more None check elimination
authorStefan Behnel <scoder@users.berlios.de>
Mon, 9 Aug 2010 17:44:39 +0000 (19:44 +0200)
committerStefan Behnel <scoder@users.berlios.de>
Mon, 9 Aug 2010 17:44:39 +0000 (19:44 +0200)
Cython/Compiler/ExprNodes.py
Cython/Compiler/Optimize.py
Cython/Compiler/UtilNodes.py

index 5de7deaf414d4d70c5bc17bd0ec5f15d94137d34..d03dbb2fc21866aa8ebd62b7b9f1d8e43708434d 100755 (executable)
@@ -2592,6 +2592,14 @@ class SliceNode(ExprNode):
 
 class CallNode(ExprNode):
 
+    # allow overriding the default 'may_be_none' behaviour
+    may_return_none = None
+
+    def may_be_none(self):
+        if self.may_return_none is not None:
+            return self.may_return_none
+        return ExprNode.may_be_none(self)
+
     def analyse_as_type_constructor(self, env):
         type = self.function.analyse_as_type(env)
         if type and type.is_struct_or_union:
@@ -2725,12 +2733,14 @@ class SimpleCallNode(CallNode):
                 else:
                     self.type = Builtin.builtin_types[function.entry.name]
                     self.result_ctype = py_object_type
+                self.may_return_none = False
             elif function.is_name and function.type_entry:
                 # We are calling an extension type constructor.  As
                 # long as we do not support __new__(), the result type
                 # is clear
                 self.type = function.type_entry.type
                 self.result_ctype = py_object_type
+                self.may_return_none = False
             else:
                 self.type = py_object_type
             self.is_temp = 1
@@ -2945,6 +2955,12 @@ class PythonCapiFunctionNode(ExprNode):
 class PythonCapiCallNode(SimpleCallNode):
     # Python C-API Function call (only created in transforms)
 
+    # By default, we assume that the call never returns None, as this
+    # is true for most C-API functions in CPython.  If this does not
+    # apply to a call, set the following to True (or None to inherit
+    # the default behaviour).
+    may_return_none = False
+
     def __init__(self, pos, function_name, func_type,
                  utility_code = None, py_name=None, **kwargs):
         self.type = func_type.return_type
@@ -3017,6 +3033,7 @@ class GeneralCallNode(CallNode):
             # as we do not support __new__(), the result type is clear
             self.type = function.type_entry.type
             self.result_ctype = py_object_type
+            self.may_return_none = False
         else:
             self.type = py_object_type
         self.is_temp = 1
@@ -5611,6 +5628,12 @@ class BoolBinopNode(ExprNode):
         type2 = self.operand2.infer_type(env)
         return PyrexTypes.independent_spanning_type(type1, type2)
 
+    def may_be_none(self):
+        if self.operator == 'or':
+            return self.operand2.may_be_none()
+        else:
+            return self.operand1.may_be_none() or self.operand2.may_be_none()
+
     def calculate_constant_result(self):
         if self.operator == 'and':
             self.constant_result = \
@@ -6397,6 +6420,9 @@ class CastNode(CoercionNode):
     def __init__(self, arg, new_type):
         CoercionNode.__init__(self, arg)
         self.type = new_type
+
+    def may_be_none(self):
+        return self.arg.may_be_none()
     
     def calculate_result_code(self):
         return self.arg.result_as(self.type)
@@ -6424,6 +6450,11 @@ class PyTypeTestNode(CoercionNode):
     
     def analyse_types(self, env):
         pass
+
+    def may_be_none(self):
+        if self.notnone:
+            return False
+        return self.arg.may_be_none()
     
     def result_in_temp(self):
         return self.arg.result_in_temp()
index f25f401a7707235c9ab950aa174a9594cbc47320..cae8c9821472a11881de3916f3efe0c7e2bc05e6 100644 (file)
@@ -1729,11 +1729,13 @@ class OptimizeBuiltinCalls(Visitor.EnvTransform):
             return ExprNodes.PythonCapiCallNode(
                 node.pos, "PyObject_GetAttr", self.PyObject_GetAttr2_func_type,
                 args = pos_args,
+                may_return_none = True,
                 is_temp = node.is_temp)
         elif len(pos_args) == 3:
             return ExprNodes.PythonCapiCallNode(
                 node.pos, "__Pyx_GetAttr3", self.PyObject_GetAttr3_func_type,
                 args = pos_args,
+                may_return_none = True,
                 is_temp = node.is_temp,
                 utility_code = Builtin.getattr3_utility_code)
         else:
@@ -1758,6 +1760,7 @@ class OptimizeBuiltinCalls(Visitor.EnvTransform):
             return ExprNodes.PythonCapiCallNode(
                 node.pos, "PyObject_GetIter", self.PyObject_GetIter_func_type,
                 args = pos_args,
+                may_return_none = True,
                 is_temp = node.is_temp)
         elif len(pos_args) == 2:
             return ExprNodes.PythonCapiCallNode(
@@ -1956,6 +1959,7 @@ class OptimizeBuiltinCalls(Visitor.EnvTransform):
         return ExprNodes.PythonCapiCallNode(
             node.pos, "__Pyx_PyObject_Append", self.PyObject_Append_func_type,
             args = args,
+            may_return_none = True,
             is_temp = node.is_temp,
             utility_code = append_utility_code
             )
@@ -1979,6 +1983,7 @@ class OptimizeBuiltinCalls(Visitor.EnvTransform):
             return ExprNodes.PythonCapiCallNode(
                 node.pos, "__Pyx_PyObject_Pop", self.PyObject_Pop_func_type,
                 args = args,
+                may_return_none = True,
                 is_temp = node.is_temp,
                 utility_code = pop_utility_code
                 )
@@ -1990,6 +1995,7 @@ class OptimizeBuiltinCalls(Visitor.EnvTransform):
                     return ExprNodes.PythonCapiCallNode(
                         node.pos, "__Pyx_PyObject_PopIndex", self.PyObject_PopIndex_func_type,
                         args = args,
+                        may_return_none = True,
                         is_temp = node.is_temp,
                         utility_code = pop_index_utility_code
                         )
@@ -2059,6 +2065,7 @@ class OptimizeBuiltinCalls(Visitor.EnvTransform):
         return self._substitute_method_call(
             node, "__Pyx_PyDict_GetItemDefault", self.Pyx_PyDict_GetItem_func_type,
             'get', is_unbound_method, args,
+            may_return_none = True,
             utility_code = dict_getitem_default_utility_code)
 
 
@@ -2559,7 +2566,8 @@ class OptimizeBuiltinCalls(Visitor.EnvTransform):
 
     def _substitute_method_call(self, node, name, func_type,
                                 attr_name, is_unbound_method, args=(),
-                                utility_code=None):
+                                utility_code=None,
+                                may_return_none=ExprNodes.PythonCapiCallNode.may_return_none):
         args = list(args)
         if args and not args[0].is_literal:
             self_arg = args[0]
@@ -2576,7 +2584,8 @@ class OptimizeBuiltinCalls(Visitor.EnvTransform):
             node.pos, name, func_type,
             args = args,
             is_temp = node.is_temp,
-            utility_code = utility_code
+            utility_code = utility_code,
+            may_return_none = may_return_none,
             )
 
     def _inject_int_default_argument(self, node, args, arg_index, type, default_value):
@@ -3000,8 +3009,9 @@ class FinalOptimizePhase(Visitor.CythonTransform):
     just before the C code generation phase. 
     
     The optimizations currently implemented in this class are: 
-        - Eliminate None assignment and refcounting for first assignment. 
+        - eliminate None assignment and refcounting for first assignment. 
         - isinstance -> typecheck for cdef types
+        - eliminate checks for None and/or types that became redundant after tree changes
     """
     def visit_SingleAssignmentNode(self, node):
         """Avoid redundant initialisation of local variables before their
@@ -3032,3 +3042,14 @@ class FinalOptimizePhase(Visitor.CythonTransform):
                     PyTypeObjectPtr = PyrexTypes.CPtrType(utility_scope.lookup('PyTypeObject').type)
                     node.args[1] = ExprNodes.CastNode(node.args[1], PyTypeObjectPtr)
         return node
+
+    def visit_PyTypeTestNode(self, node):
+        """Remove tests for alternatively allowed None values from
+        type tests when we know that the argument cannot be None
+        anyway.
+        """
+        self.visitchildren(node)
+        if not node.notnone:
+            if not node.arg.may_be_none():
+                node.notnone = True
+        return node
index d96db1d92ffacdaf7ebd986a41a5ac4a0feaa944..3ac02cfac37c09f31ad1b30f366169cfdd4e0708 100644 (file)
@@ -140,6 +140,15 @@ class ResultRefNode(AtomicExprNode):
         if self.expression is not None:
             return self.expression.infer_type(env)
 
+    def _DISABLED_may_be_none(self):
+        # not sure if this is safe - the expression may not be the
+        # only value that gets assigned
+        if self.expression is not None:
+            return self.expression.may_be_none()
+        if self.type is not None:
+            return self.type.is_pyobject
+        return True # play safe
+
     def is_simple(self):
         return True