From a0654d240be83e4e9284fc43d2f45833e56c3505 Mon Sep 17 00:00:00 2001 From: Dag Sverre Seljebotn Date: Mon, 1 Dec 2008 01:06:57 +0100 Subject: [PATCH] More fixes for new temps --- Cython/Compiler/Code.py | 3 +- Cython/Compiler/ExprNodes.py | 104 +++++++++++++++++++++++++++++------ Cython/Compiler/Nodes.py | 23 ++++++++ Cython/Compiler/Optimize.py | 8 ++- 4 files changed, 117 insertions(+), 21 deletions(-) diff --git a/Cython/Compiler/Code.py b/Cython/Compiler/Code.py index 07df8075..d173acaf 100644 --- a/Cython/Compiler/Code.py +++ b/Cython/Compiler/Code.py @@ -150,8 +150,9 @@ class FunctionState(object): freelist = self.temps_free.get((type, manage_ref)) if freelist is None: freelist = [] - self.temps_free[(type, manage_ref)] = freelist + if name in freelist: + raise RuntimeError("Temp %s freed twice!" % name) freelist.append(name) if DebugFlags.debug_temp_code_comments: self.owner.putln("/* %s released */" % name) diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index 79b991ee..a3dee717 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -439,6 +439,7 @@ class ExprNode(Node): self.generate_result_code(code) if self.is_temp: self.generate_subexpr_disposal_code(code) + self.free_subexpr_temps(code) def generate_subexpr_evaluation_code(self, code): for node in self.subexpr_nodes(): @@ -487,6 +488,10 @@ class ExprNode(Node): def free_temps(self, code): pass + def free_subexpr_temps(self, code): + for sub in self.subexpr_nodes(): + sub.free_temps(code) + # ---------------- Annotation --------------------- def annotate(self, code): @@ -582,12 +587,13 @@ class RemoveAllocateTemps(type): class NewTempExprNode(ExprNode): backwards_compatible_result = None - + temp_code = None + # Do not enable this unless you are trying to make all ExprNodes # NewTempExprNodes (child nodes reached via recursion may not have # transferred). # __metaclass__ = RemoveAllocateTemps - + def result(self): if self.is_temp: return self.temp_code @@ -617,6 +623,8 @@ class NewTempExprNode(ExprNode): self.release_subexpr_temps(env) def allocate_temp_result(self, code): + if self.temp_code: + raise RuntimeError("Temp allocated multiple times") type = self.type if not type.is_void: if type.is_pyobject: @@ -630,7 +638,11 @@ class NewTempExprNode(ExprNode): self.temp_code = None def release_temp_result(self, code): + if not self.temp_code: + raise RuntimeError("No temp (perhaps released multiple times? See self.old_temp)") code.funcstate.release_temp(self.temp_code) + self.old_temp = self.temp_code + self.temp_code = None def generate_evaluation_code(self, code): code.mark_pos(self.pos) @@ -648,6 +660,7 @@ class NewTempExprNode(ExprNode): # If we are temp we do not need to wait until this node is disposed # before disposing children. self.generate_subexpr_disposal_code(code) + self.free_subexpr_temps(code) def generate_disposal_code(self, code): if self.is_temp: @@ -658,16 +671,18 @@ class NewTempExprNode(ExprNode): self.generate_subexpr_disposal_code(code) def generate_post_assignment_code(self, code): - if self.is_temp and self.type.is_pyobject: - code.putln("%s = 0;" % self.result()) - self.generate_subexpr_disposal_code(code) + if self.is_temp: + if self.type.is_pyobject: + code.putln("%s = 0;" % self.result()) + else: + self.generate_subexpr_disposal_code(code) def free_temps(self, code): if self.is_temp: - self.release_temp_result() - for sub in self.subexpr_nodes(): - sub.free_temps(code) - + self.release_temp_result(code) + else: + self.free_subexpr_temps(code) + # ExprNode = NewTempExprNode class AtomicExprNode(ExprNode): @@ -1238,7 +1253,7 @@ class NameNode(AtomicExprNode): print("NameNode.generate_assignment_code:") print("...generating disposal code for %s" % rhs) rhs.generate_disposal_code(code) - + rhs.free_temps(code) else: if self.type.is_buffer: # Generate code for doing the buffer release/acquisition. @@ -1270,6 +1285,7 @@ class NameNode(AtomicExprNode): print("NameNode.generate_assignment_code:") print("...generating post-assignment code for %s" % rhs) rhs.generate_post_assignment_code(code) + rhs.free_temps(code) def generate_acquire_buffer(self, rhs, code): # rhstmp is only used in case the rhs is a complicated expression leading to @@ -1708,6 +1724,14 @@ class IndexNode(ExprNode): for i in self.indices: i.generate_disposal_code(code) + def free_subexpr_temps(self, code): + self.base.free_temps(code) + if not self.indices: + self.index.free_temps(code) + else: + for i in self.indices: + i.free_temps(code) + def generate_result_code(self, code): if self.is_buffer_access: if code.globalstate.directives['nonecheck']: @@ -1795,7 +1819,9 @@ class IndexNode(ExprNode): "%s = %s;" % ( self.result(), rhs.result())) self.generate_subexpr_disposal_code(code) + self.free_subexpr_temps(code) rhs.generate_disposal_code(code) + rhs.free_temps(code) def generate_deletion_code(self, code): self.generate_subexpr_evaluation_code(code) @@ -1935,6 +1961,7 @@ class SliceIndexNode(ExprNode): rhs.result(), i)) self.generate_subexpr_disposal_code(code) rhs.generate_disposal_code(code) + rhs.free_temps(code) def generate_deletion_code(self, code): if not self.type.is_pyobject: @@ -2722,6 +2749,7 @@ class AttributeNode(ExprNode): self.interned_attr_cname, rhs.py_result())) rhs.generate_disposal_code(code) + rhs.free_temps(code) else: if (self.obj.type.is_extension_type and self.needs_none_check @@ -2738,7 +2766,9 @@ class AttributeNode(ExprNode): rhs.result_as(self.ctype()))) #rhs.result())) rhs.generate_post_assignment_code(code) + rhs.free_temps(code) self.obj.generate_disposal_code(code) + self.obj.free_temps(code) def generate_deletion_code(self, code): self.obj.generate_evaluation_code(code) @@ -2750,6 +2780,7 @@ class AttributeNode(ExprNode): else: error(self.pos, "Cannot delete C attribute of extension type") self.obj.generate_disposal_code(code) + self.obj.free_temps(code) def annotate(self, code): if self.is_py_attr: @@ -2838,6 +2869,10 @@ class SequenceNode(NewTempExprNode): self.generate_operation_code(code) def generate_assignment_code(self, rhs, code): + # Need to work around the fact that generate_evaluation_code + # allocates the temps in a rather hacky way -- the assignment + # is evaluated twice, within each if-block. + code.putln( "if (PyTuple_CheckExact(%s) && PyTuple_GET_SIZE(%s) == %s) {" % ( rhs.py_result(), @@ -2855,6 +2890,10 @@ class SequenceNode(NewTempExprNode): value_node.generate_evaluation_code(code) rhs.generate_disposal_code(code) + for i in range(len(self.args)): + self.args[i].generate_assignment_code( + self.coerced_unpacked_items[i], code) + code.putln("} else {") code.putln( @@ -2881,12 +2920,13 @@ class SequenceNode(NewTempExprNode): print("UnpackNode.generate_assignment_code:") print("...generating disposal code for %s" % self.iterator) self.iterator.generate_disposal_code(code) + self.iterator.free_temps(code) - code.putln("}") - rhs.free_temps() for i in range(len(self.args)): self.args[i].generate_assignment_code( self.coerced_unpacked_items[i], code) + code.putln("}") + rhs.free_temps(code) def annotate(self, code): for arg in self.args: @@ -2948,7 +2988,9 @@ class TupleNode(SequenceNode): # of generate_disposal_code, because values were stored # in the tuple using a reference-stealing operation. for arg in self.args: - arg.generate_post_assignment_code(code) + arg.generate_post_assignment_code(code) + # Should NOT call free_temps -- this is invoked by the default + # generate_evaluation_code which will do that. class ListNode(SequenceNode): @@ -3050,7 +3092,9 @@ class ListNode(SequenceNode): # of generate_disposal_code, because values were stored # in the list using a reference-stealing operation. for arg in self.args: - arg.generate_post_assignment_code(code) + arg.generate_post_assignment_code(code) + # Should NOT call free_temps -- this is invoked by the default + # generate_evaluation_code which will do that. class ListComprehensionNode(SequenceNode): @@ -3202,6 +3246,7 @@ class DictNode(ExprNode): item.key.value, item.value.result())) item.generate_disposal_code(code) + item.free_temps(code) def annotate(self, code): for item in self.key_value_pairs: @@ -3227,6 +3272,10 @@ class DictItemNode(ExprNode): def generate_disposal_code(self, code): self.key.generate_disposal_code(code) self.value.generate_disposal_code(code) + + def free_temps(self, code): + self.key.free_temps(code) + self.value.free_temps(code) def __iter__(self): return iter([self.key, self.value]) @@ -4068,10 +4117,12 @@ class BoolBinopNode(NewTempExprNode): self.operand2.generate_evaluation_code(code) self.allocate_temp_result(code) code.putln("%s = %s;" % (self.result(), self.operand2.result())) - self.operand2.free_temps() + self.operand2.generate_post_assignment_code(code) + self.operand2.free_temps(code) code.putln("} else {") code.putln("%s = %s;" % (self.result(), self.operand1.result())) - self.operand1.free_temps() + self.operand1.generate_post_assignment_code(code) + self.operand1.free_temps(code) code.putln("}") def generate_operand1_test(self, code): @@ -4174,6 +4225,7 @@ class CondExprNode(ExprNode): self.false_val.generate_evaluation_code(code) code.putln("}") self.test.generate_disposal_code(code) + self.test.free_temps(code) richcmp_constants = { "<" : "Py_LT", @@ -4401,13 +4453,21 @@ class PrimaryCmpNode(NewTempExprNode, CmpNode): self.result(), self.operand2) self.operand1.generate_disposal_code(code) self.operand2.generate_disposal_code(code) - + self.operand1.free_temps(code) + self.operand2.free_temps(code) + def generate_subexpr_disposal_code(self, code): # If this is called, it is a non-cascaded cmp, # so only need to dispose of the two main operands. self.operand1.generate_disposal_code(code) self.operand2.generate_disposal_code(code) + def free_subexpr_temps(self, code): + # If this is called, it is a non-cascaded cmp, + # so only need to dispose of the two main operands. + self.operand1.free_temps(code) + self.operand2.free_temps(code) + def annotate(self, code): self.operand1.annotate(code) self.operand2.annotate(code) @@ -4484,6 +4544,7 @@ class CascadedCmpNode(Node, CmpNode): code, result, self.operand2) # Cascaded cmp result is always temp self.operand2.generate_disposal_code(code) + self.operand2.free_temps(code) code.putln("}") def annotate(self, code): @@ -4604,6 +4665,9 @@ class PyTypeTestNode(CoercionNode): def generate_post_assignment_code(self, code): self.arg.generate_post_assignment_code(code) + + def free_temps(self, code): + self.arg.free_temps(code) class CoerceToPyTypeNode(CoercionNode): @@ -4775,8 +4839,12 @@ class CloneNode(CoercionNode): def release_temp(self, env): pass + + def free_temps(self, code): + pass + -class PersistentNode(ExprNode): +class DISABLED_PersistentNode(ExprNode): # A PersistentNode is like a CloneNode except it handles the temporary # allocation itself by keeping track of the number of times it has been # used. diff --git a/Cython/Compiler/Nodes.py b/Cython/Compiler/Nodes.py index 01ad7b84..139432f2 100644 --- a/Cython/Compiler/Nodes.py +++ b/Cython/Compiler/Nodes.py @@ -2444,6 +2444,7 @@ class PyClassDefNode(ClassDefNode): self.body.generate_execution_code(code) self.target.generate_assignment_code(self.classobj, code) self.dict.generate_disposal_code(code) + self.dict.free_temps(code) class CClassDefNode(ClassDefNode): @@ -2659,6 +2660,7 @@ class ExprStatNode(StatNode): if not self.expr.is_temp and self.expr.result(): code.putln("%s;" % self.expr.result()) self.expr.generate_disposal_code(code) + self.expr.free_temps(code) def annotate(self, code): self.expr.annotate(code) @@ -2884,6 +2886,7 @@ class CascadedAssignmentNode(AssignmentNode): lhs.generate_assignment_code(rhs, code) # Assignment has disposed of the cloned RHS self.rhs.generate_disposal_code(code) + self.rhs.free_temps(code) def annotate(self, code): for i in range(len(self.lhs_list)): @@ -3016,7 +3019,9 @@ class InPlaceAssignmentNode(AssignmentNode): code.error_goto_if_null(self.result_value.py_result(), self.pos))) self.result_value.generate_evaluation_code(code) # May be a type check... self.rhs.generate_disposal_code(code) + self.rhs.free_temps(code) self.dup.generate_disposal_code(code) + self.dup.free_temps(code) self.lhs.generate_assignment_code(self.result_value, code) else: c_op = self.operator @@ -3034,8 +3039,10 @@ class InPlaceAssignmentNode(AssignmentNode): self.dup.generate_result_code(code) code.putln("%s %s= %s;" % (self.lhs.result(), c_op, self.rhs.result()) ) self.rhs.generate_disposal_code(code) + self.rhs.free_temps(code) if self.dup.is_temp: self.dup.generate_subexpr_disposal_code(code) + self.dup.free_subexpr_temps(code) def create_dup_node(self, env): import ExprNodes @@ -3065,6 +3072,8 @@ class InPlaceAssignmentNode(AssignmentNode): index = index, indices = indices, is_temp = self.dup.is_temp) + else: + assert False self.lhs = target_lhs return self.dup @@ -3117,6 +3126,7 @@ class PrintStatNode(StatNode): self.append_newline, code.error_goto(self.pos))) self.arg_tuple.generate_disposal_code(code) + self.arg_tuple.free_temps(code) def annotate(self, code): self.arg_tuple.annotate(code) @@ -3152,6 +3162,7 @@ class ExecStatNode(StatNode): (self.temp_result,) + args)) for arg in self.args: arg.generate_disposal_code(code) + arg.free_temps(code) code.putln( code.error_goto_if_null(self.temp_result, self.pos)) code.put_decref_clear(self.temp_result, py_object_type) @@ -3287,6 +3298,7 @@ class ReturnStatNode(StatNode): Naming.retval_cname, self.value.result_as(self.return_type))) self.value.generate_post_assignment_code(code) + self.value.free_temps(code) else: if self.return_type.is_pyobject: code.put_init_to_py_none(Naming.retval_cname, self.return_type) @@ -3372,10 +3384,13 @@ class RaiseStatNode(StatNode): "__Pyx_ReRaise();") if self.exc_type: self.exc_type.generate_disposal_code(code) + self.exc_type.free_temps(code) if self.exc_value: self.exc_value.generate_disposal_code(code) + self.exc_value.free_temps(code) if self.exc_tb: self.exc_tb.generate_disposal_code(code) + self.exc_tb.free_temps(code) code.putln( code.error_goto(self.pos)) @@ -3442,6 +3457,7 @@ class AssertStatNode(StatNode): "PyErr_SetObject(PyExc_AssertionError, %s);" % self.value.py_result()) self.value.generate_disposal_code(code) + self.value.free_temps(code) else: code.putln( "PyErr_SetNone(PyExc_AssertionError);") @@ -3450,6 +3466,7 @@ class AssertStatNode(StatNode): code.putln( "}") self.cond.generate_disposal_code(code) + self.cond.free_temps(code) code.putln("#endif") def annotate(self, code): @@ -3756,6 +3773,7 @@ class ForInStatNode(LoopNode, StatNode): code.put_label(break_label) self.iterator.release_counter_temp(code) self.iterator.generate_disposal_code(code) + self.iterator.free_temps(code) def annotate(self, code): self.target.annotate(code) @@ -3878,9 +3896,12 @@ class ForFromStatNode(LoopNode, StatNode): code.putln("}") code.put_label(break_label) self.bound1.generate_disposal_code(code) + self.bound1.free_temps(code) self.bound2.generate_disposal_code(code) + self.bound2.free_temps(code) if self.step is not None: self.step.generate_disposal_code(code) + self.step.free_temps(code) relation_table = { # {relop : (initial offset, increment op)} @@ -4103,6 +4124,7 @@ class ExceptClauseNode(Node): self.match_flag, self.pattern.py_result())) self.pattern.generate_disposal_code(code) + self.pattern.free_temps(code) code.putln( "if (%s) {" % self.match_flag) @@ -4539,6 +4561,7 @@ class FromImportStatNode(StatNode): code.error_goto_if_null(self.item.result(), self.pos))) target.generate_assignment_code(self.item, code) self.module.generate_disposal_code(code) + self.module.free_temps(code) diff --git a/Cython/Compiler/Optimize.py b/Cython/Compiler/Optimize.py index a9021662..64aef083 100644 --- a/Cython/Compiler/Optimize.py +++ b/Cython/Compiler/Optimize.py @@ -8,9 +8,13 @@ import TypeSlots import Symtab from StringEncoding import EncodedString +#def unwrap_node(node): +# while isinstance(node, ExprNodes.PersistentNode): +# node = node.arg +# return node + +# Temporary hack while PersistentNode is out of order def unwrap_node(node): - while isinstance(node, ExprNodes.PersistentNode): - node = node.arg return node def is_common_value(a, b): -- 2.26.2