From 7a5d356543029270b745ff7ef908152dd52ae659 Mon Sep 17 00:00:00 2001 From: Robert Bradshaw Date: Fri, 12 Feb 2010 01:56:08 -0800 Subject: [PATCH] Infer integer types when entries not used in arithmatic expressions. --- Cython/Compiler/Main.py | 6 ++- Cython/Compiler/Symtab.py | 3 ++ Cython/Compiler/TypeInference.py | 66 +++++++++++++++++++++++++++++--- 3 files changed, 69 insertions(+), 6 deletions(-) diff --git a/Cython/Compiler/Main.py b/Cython/Compiler/Main.py index 6cb2c387..ac64fbec 100644 --- a/Cython/Compiler/Main.py +++ b/Cython/Compiler/Main.py @@ -88,7 +88,7 @@ class Context(object): from ParseTreeTransforms import AnalyseDeclarationsTransform, AnalyseExpressionsTransform from ParseTreeTransforms import CreateClosureClasses, MarkClosureVisitor, DecoratorTransform from ParseTreeTransforms import InterpretCompilerDirectives, TransformBuiltinMethods - from TypeInference import MarkAssignments + from TypeInference import MarkAssignments, MarkOverflowingArithmatic from ParseTreeTransforms import AlignFunctionDefinitions, GilCheck from AnalysedTreeTransforms import AutoTestDictTransform from AutoDocTransforms import EmbedSignature @@ -135,6 +135,7 @@ class Context(object): EmbedSignature(self), EarlyReplaceBuiltinCalls(self), MarkAssignments(self), + MarkOverflowingArithmatic(self), TransformBuiltinMethods(self), IntroduceBufferAuxiliaryVars(self), _check_c_declarations, @@ -218,8 +219,11 @@ class Context(object): for phase in pipeline: if phase is not None: if DebugFlags.debug_verbose_pipeline: + t = time() print "Entering pipeline phase %r" % phase data = phase(data) + if DebugFlags.debug_verbose_pipeline: + print " %.3f seconds" % (time() - t) except CompileError, err: # err is set Errors.report_error(err) diff --git a/Cython/Compiler/Symtab.py b/Cython/Compiler/Symtab.py index 6bde21fa..70de775d 100644 --- a/Cython/Compiler/Symtab.py +++ b/Cython/Compiler/Symtab.py @@ -119,6 +119,8 @@ class Entry(object): # inline_func_in_pxd boolean Hacky special case for inline function in pxd file. # Ideally this should not be necesarry. # assignments [ExprNode] List of expressions that get assigned to this entry. + # might_overflow boolean In an arithmatic expression that could cause + # overflow (used for type inference). inline_func_in_pxd = False borrowed = 0 @@ -167,6 +169,7 @@ class Entry(object): is_overridable = 0 buffer_aux = None prev_entry = None + might_overflow = 0 def __init__(self, name, cname, type, pos = None, init = None): self.name = name diff --git a/Cython/Compiler/TypeInference.py b/Cython/Compiler/TypeInference.py index 72d389c8..e4b2f9c2 100644 --- a/Cython/Compiler/TypeInference.py +++ b/Cython/Compiler/TypeInference.py @@ -112,6 +112,60 @@ class MarkAssignments(CythonTransform): self.visitchildren(node) return node +class MarkOverflowingArithmatic(CythonTransform): + + # It may be possible to integrate this with the above for + # performance improvements (though likely not worth it). + + might_overflow = False + + def __call__(self, root): + self.env_stack = [] + self.env = root.scope + return super(MarkOverflowingArithmatic, self).__call__(root) + + def visit_safe_node(self, node): + self.might_overflow, saved = False, self.might_overflow + self.visitchildren(node) + self.might_overflow = saved + return node + + def visit_neutral_node(self, node): + self.visitchildren(node) + return node + + def visit_dangerous_node(self, node): + self.might_overflow, saved = True, self.might_overflow + self.visitchildren(node) + self.might_overflow = saved + return node + + def visit_FuncDefNode(self, node): + self.env_stack.append(self.env) + self.env = node.local_scope + self.visit_safe_node(node) + self.env = self.env_stack.pop() + return node + + def visit_NameNode(self, node): + if self.might_overflow: + entry = node.entry or self.env.lookup(node.name) + if entry: + entry.might_overflow = True + return node + + def visit_BinopNode(self, node): + if node.operator in '&|^': + return self.visit_neutral_node(node) + else: + return self.visit_dangerous_node(node) + + visit_UnopNode = visit_neutral_node + + visit_UnaryMinusNode = visit_dangerous_node + + visit_Node = visit_safe_node + class PyObjectTypeInferer: """ @@ -175,7 +229,7 @@ class SimpleAssignmentTypeInferer: entry = ready_to_infer.pop() types = [expr.infer_type(scope) for expr in entry.assignments] if types: - entry.type = spanning_type(types) + entry.type = spanning_type(types, entry.might_overflow) else: # FIXME: raise a warning? # print "No assignments", entry.pos, entry @@ -188,9 +242,9 @@ class SimpleAssignmentTypeInferer: if len(deps) == 1 and deps == set([entry]): types = [expr.infer_type(scope) for expr in entry.assignments if expr.type_dependencies(scope) == ()] if types: - entry.type = spanning_type(types) + entry.type = spanning_type(types, entry.might_overflow) types = [expr.infer_type(scope) for expr in entry.assignments] - entry.type = spanning_type(types) # might be wider... + entry.type = spanning_type(types, entry.might_overflow) # might be wider... resolve_dependancy(entry) del dependancies_by_entry[entry] if ready_to_infer: @@ -218,11 +272,11 @@ def find_spanning_type(type1, type2): return PyrexTypes.c_double_type return result_type -def aggressive_spanning_type(types): +def aggressive_spanning_type(types, might_overflow): result_type = reduce(find_spanning_type, types) return result_type -def safe_spanning_type(types): +def safe_spanning_type(types, might_overflow): result_type = reduce(find_spanning_type, types) if result_type.is_pyobject: # any specific Python type is always safe to infer @@ -249,6 +303,8 @@ def safe_spanning_type(types): return result_type # TODO: double complex should be OK as well, but we need # to make sure everything is supported. + elif result_type.is_int and not might_overflow: + return result_type return py_object_type -- 2.26.2