Commit 5dd72bd0 authored by Stefan Behnel's avatar Stefan Behnel

more None check elimination

parent 339700e7
...@@ -2592,6 +2592,14 @@ class SliceNode(ExprNode): ...@@ -2592,6 +2592,14 @@ class SliceNode(ExprNode):
class CallNode(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): def analyse_as_type_constructor(self, env):
type = self.function.analyse_as_type(env) type = self.function.analyse_as_type(env)
if type and type.is_struct_or_union: if type and type.is_struct_or_union:
...@@ -2725,12 +2733,14 @@ class SimpleCallNode(CallNode): ...@@ -2725,12 +2733,14 @@ class SimpleCallNode(CallNode):
else: else:
self.type = Builtin.builtin_types[function.entry.name] self.type = Builtin.builtin_types[function.entry.name]
self.result_ctype = py_object_type self.result_ctype = py_object_type
self.may_return_none = False
elif function.is_name and function.type_entry: elif function.is_name and function.type_entry:
# We are calling an extension type constructor. As # We are calling an extension type constructor. As
# long as we do not support __new__(), the result type # long as we do not support __new__(), the result type
# is clear # is clear
self.type = function.type_entry.type self.type = function.type_entry.type
self.result_ctype = py_object_type self.result_ctype = py_object_type
self.may_return_none = False
else: else:
self.type = py_object_type self.type = py_object_type
self.is_temp = 1 self.is_temp = 1
...@@ -2945,6 +2955,12 @@ class PythonCapiFunctionNode(ExprNode): ...@@ -2945,6 +2955,12 @@ class PythonCapiFunctionNode(ExprNode):
class PythonCapiCallNode(SimpleCallNode): class PythonCapiCallNode(SimpleCallNode):
# Python C-API Function call (only created in transforms) # 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, def __init__(self, pos, function_name, func_type,
utility_code = None, py_name=None, **kwargs): utility_code = None, py_name=None, **kwargs):
self.type = func_type.return_type self.type = func_type.return_type
...@@ -3017,6 +3033,7 @@ class GeneralCallNode(CallNode): ...@@ -3017,6 +3033,7 @@ class GeneralCallNode(CallNode):
# as we do not support __new__(), the result type is clear # as we do not support __new__(), the result type is clear
self.type = function.type_entry.type self.type = function.type_entry.type
self.result_ctype = py_object_type self.result_ctype = py_object_type
self.may_return_none = False
else: else:
self.type = py_object_type self.type = py_object_type
self.is_temp = 1 self.is_temp = 1
...@@ -5611,6 +5628,12 @@ class BoolBinopNode(ExprNode): ...@@ -5611,6 +5628,12 @@ class BoolBinopNode(ExprNode):
type2 = self.operand2.infer_type(env) type2 = self.operand2.infer_type(env)
return PyrexTypes.independent_spanning_type(type1, type2) 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): def calculate_constant_result(self):
if self.operator == 'and': if self.operator == 'and':
self.constant_result = \ self.constant_result = \
...@@ -6398,6 +6421,9 @@ class CastNode(CoercionNode): ...@@ -6398,6 +6421,9 @@ class CastNode(CoercionNode):
CoercionNode.__init__(self, arg) CoercionNode.__init__(self, arg)
self.type = new_type self.type = new_type
def may_be_none(self):
return self.arg.may_be_none()
def calculate_result_code(self): def calculate_result_code(self):
return self.arg.result_as(self.type) return self.arg.result_as(self.type)
...@@ -6425,6 +6451,11 @@ class PyTypeTestNode(CoercionNode): ...@@ -6425,6 +6451,11 @@ class PyTypeTestNode(CoercionNode):
def analyse_types(self, env): def analyse_types(self, env):
pass pass
def may_be_none(self):
if self.notnone:
return False
return self.arg.may_be_none()
def result_in_temp(self): def result_in_temp(self):
return self.arg.result_in_temp() return self.arg.result_in_temp()
......
...@@ -1729,11 +1729,13 @@ class OptimizeBuiltinCalls(Visitor.EnvTransform): ...@@ -1729,11 +1729,13 @@ class OptimizeBuiltinCalls(Visitor.EnvTransform):
return ExprNodes.PythonCapiCallNode( return ExprNodes.PythonCapiCallNode(
node.pos, "PyObject_GetAttr", self.PyObject_GetAttr2_func_type, node.pos, "PyObject_GetAttr", self.PyObject_GetAttr2_func_type,
args = pos_args, args = pos_args,
may_return_none = True,
is_temp = node.is_temp) is_temp = node.is_temp)
elif len(pos_args) == 3: elif len(pos_args) == 3:
return ExprNodes.PythonCapiCallNode( return ExprNodes.PythonCapiCallNode(
node.pos, "__Pyx_GetAttr3", self.PyObject_GetAttr3_func_type, node.pos, "__Pyx_GetAttr3", self.PyObject_GetAttr3_func_type,
args = pos_args, args = pos_args,
may_return_none = True,
is_temp = node.is_temp, is_temp = node.is_temp,
utility_code = Builtin.getattr3_utility_code) utility_code = Builtin.getattr3_utility_code)
else: else:
...@@ -1758,6 +1760,7 @@ class OptimizeBuiltinCalls(Visitor.EnvTransform): ...@@ -1758,6 +1760,7 @@ class OptimizeBuiltinCalls(Visitor.EnvTransform):
return ExprNodes.PythonCapiCallNode( return ExprNodes.PythonCapiCallNode(
node.pos, "PyObject_GetIter", self.PyObject_GetIter_func_type, node.pos, "PyObject_GetIter", self.PyObject_GetIter_func_type,
args = pos_args, args = pos_args,
may_return_none = True,
is_temp = node.is_temp) is_temp = node.is_temp)
elif len(pos_args) == 2: elif len(pos_args) == 2:
return ExprNodes.PythonCapiCallNode( return ExprNodes.PythonCapiCallNode(
...@@ -1956,6 +1959,7 @@ class OptimizeBuiltinCalls(Visitor.EnvTransform): ...@@ -1956,6 +1959,7 @@ class OptimizeBuiltinCalls(Visitor.EnvTransform):
return ExprNodes.PythonCapiCallNode( return ExprNodes.PythonCapiCallNode(
node.pos, "__Pyx_PyObject_Append", self.PyObject_Append_func_type, node.pos, "__Pyx_PyObject_Append", self.PyObject_Append_func_type,
args = args, args = args,
may_return_none = True,
is_temp = node.is_temp, is_temp = node.is_temp,
utility_code = append_utility_code utility_code = append_utility_code
) )
...@@ -1979,6 +1983,7 @@ class OptimizeBuiltinCalls(Visitor.EnvTransform): ...@@ -1979,6 +1983,7 @@ class OptimizeBuiltinCalls(Visitor.EnvTransform):
return ExprNodes.PythonCapiCallNode( return ExprNodes.PythonCapiCallNode(
node.pos, "__Pyx_PyObject_Pop", self.PyObject_Pop_func_type, node.pos, "__Pyx_PyObject_Pop", self.PyObject_Pop_func_type,
args = args, args = args,
may_return_none = True,
is_temp = node.is_temp, is_temp = node.is_temp,
utility_code = pop_utility_code utility_code = pop_utility_code
) )
...@@ -1990,6 +1995,7 @@ class OptimizeBuiltinCalls(Visitor.EnvTransform): ...@@ -1990,6 +1995,7 @@ class OptimizeBuiltinCalls(Visitor.EnvTransform):
return ExprNodes.PythonCapiCallNode( return ExprNodes.PythonCapiCallNode(
node.pos, "__Pyx_PyObject_PopIndex", self.PyObject_PopIndex_func_type, node.pos, "__Pyx_PyObject_PopIndex", self.PyObject_PopIndex_func_type,
args = args, args = args,
may_return_none = True,
is_temp = node.is_temp, is_temp = node.is_temp,
utility_code = pop_index_utility_code utility_code = pop_index_utility_code
) )
...@@ -2059,6 +2065,7 @@ class OptimizeBuiltinCalls(Visitor.EnvTransform): ...@@ -2059,6 +2065,7 @@ class OptimizeBuiltinCalls(Visitor.EnvTransform):
return self._substitute_method_call( return self._substitute_method_call(
node, "__Pyx_PyDict_GetItemDefault", self.Pyx_PyDict_GetItem_func_type, node, "__Pyx_PyDict_GetItemDefault", self.Pyx_PyDict_GetItem_func_type,
'get', is_unbound_method, args, 'get', is_unbound_method, args,
may_return_none = True,
utility_code = dict_getitem_default_utility_code) utility_code = dict_getitem_default_utility_code)
...@@ -2559,7 +2566,8 @@ class OptimizeBuiltinCalls(Visitor.EnvTransform): ...@@ -2559,7 +2566,8 @@ class OptimizeBuiltinCalls(Visitor.EnvTransform):
def _substitute_method_call(self, node, name, func_type, def _substitute_method_call(self, node, name, func_type,
attr_name, is_unbound_method, args=(), attr_name, is_unbound_method, args=(),
utility_code=None): utility_code=None,
may_return_none=ExprNodes.PythonCapiCallNode.may_return_none):
args = list(args) args = list(args)
if args and not args[0].is_literal: if args and not args[0].is_literal:
self_arg = args[0] self_arg = args[0]
...@@ -2576,7 +2584,8 @@ class OptimizeBuiltinCalls(Visitor.EnvTransform): ...@@ -2576,7 +2584,8 @@ class OptimizeBuiltinCalls(Visitor.EnvTransform):
node.pos, name, func_type, node.pos, name, func_type,
args = args, args = args,
is_temp = node.is_temp, 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): def _inject_int_default_argument(self, node, args, arg_index, type, default_value):
...@@ -3000,8 +3009,9 @@ class FinalOptimizePhase(Visitor.CythonTransform): ...@@ -3000,8 +3009,9 @@ class FinalOptimizePhase(Visitor.CythonTransform):
just before the C code generation phase. just before the C code generation phase.
The optimizations currently implemented in this class are: 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 - isinstance -> typecheck for cdef types
- eliminate checks for None and/or types that became redundant after tree changes
""" """
def visit_SingleAssignmentNode(self, node): def visit_SingleAssignmentNode(self, node):
"""Avoid redundant initialisation of local variables before their """Avoid redundant initialisation of local variables before their
...@@ -3032,3 +3042,14 @@ class FinalOptimizePhase(Visitor.CythonTransform): ...@@ -3032,3 +3042,14 @@ class FinalOptimizePhase(Visitor.CythonTransform):
PyTypeObjectPtr = PyrexTypes.CPtrType(utility_scope.lookup('PyTypeObject').type) PyTypeObjectPtr = PyrexTypes.CPtrType(utility_scope.lookup('PyTypeObject').type)
node.args[1] = ExprNodes.CastNode(node.args[1], PyTypeObjectPtr) node.args[1] = ExprNodes.CastNode(node.args[1], PyTypeObjectPtr)
return node 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
...@@ -140,6 +140,15 @@ class ResultRefNode(AtomicExprNode): ...@@ -140,6 +140,15 @@ class ResultRefNode(AtomicExprNode):
if self.expression is not None: if self.expression is not None:
return self.expression.infer_type(env) 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): def is_simple(self):
return True return True
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment