From: Stefan Behnel Date: Mon, 9 Aug 2010 17:44:39 +0000 (+0200) Subject: more None check elimination X-Git-Url: http://git.tremily.us/?a=commitdiff_plain;h=5dd72bd043029e780aa28cccce70e1ce0597719c;p=cython.git more None check elimination --- diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index 5de7deaf..d03dbb2f 100755 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -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() diff --git a/Cython/Compiler/Optimize.py b/Cython/Compiler/Optimize.py index f25f401a..cae8c982 100644 --- a/Cython/Compiler/Optimize.py +++ b/Cython/Compiler/Optimize.py @@ -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 diff --git a/Cython/Compiler/UtilNodes.py b/Cython/Compiler/UtilNodes.py index d96db1d9..3ac02cfa 100644 --- a/Cython/Compiler/UtilNodes.py +++ b/Cython/Compiler/UtilNodes.py @@ -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