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

more None check elimination

parent 339700e7
......@@ -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 = \
......@@ -6398,6 +6421,9 @@ class CastNode(CoercionNode):
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)
......@@ -6425,6 +6451,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()
......
......@@ -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
......@@ -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
......
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