Cython has moved to github.

cython-devel

view Cython/Compiler/UtilNodes.py @ 3042:9756a762a5c8

fix ticket 467: restore eval-once semantics for all rhs items in parallel assignments by extracting common subexpressions into temps
author Stefan Behnel <scoder@users.berlios.de>
date Sat Mar 06 15:30:38 2010 +0100 (2 years ago)
parents 6866d26ea6ef
children dd72144b697c
line source
1 #
2 # Nodes used as utilities and support for transforms etc.
3 # These often make up sets including both Nodes and ExprNodes
4 # so it is convenient to have them in a seperate module.
5 #
7 import Nodes
8 import ExprNodes
9 from Nodes import Node
10 from ExprNodes import AtomicExprNode
12 class TempHandle(object):
13 # THIS IS DEPRECATED, USE LetRefNode instead
14 temp = None
15 needs_xdecref = False
16 def __init__(self, type):
17 self.type = type
18 self.needs_cleanup = type.is_pyobject
20 def ref(self, pos):
21 return TempRefNode(pos, handle=self, type=self.type)
23 def cleanup_ref(self, pos):
24 return CleanupTempRefNode(pos, handle=self, type=self.type)
26 class TempRefNode(AtomicExprNode):
27 # THIS IS DEPRECATED, USE LetRefNode instead
28 # handle TempHandle
30 def analyse_types(self, env):
31 assert self.type == self.handle.type
33 def analyse_target_types(self, env):
34 assert self.type == self.handle.type
36 def analyse_target_declaration(self, env):
37 pass
39 def calculate_result_code(self):
40 result = self.handle.temp
41 if result is None: result = "<error>" # might be called and overwritten
42 return result
44 def generate_result_code(self, code):
45 pass
47 def generate_assignment_code(self, rhs, code):
48 if self.type.is_pyobject:
49 rhs.make_owned_reference(code)
50 # TODO: analyse control flow to see if this is necessary
51 code.put_xdecref(self.result(), self.ctype())
52 code.putln('%s = %s;' % (self.result(), rhs.result_as(self.ctype())))
53 rhs.generate_post_assignment_code(code)
54 rhs.free_temps(code)
56 class CleanupTempRefNode(TempRefNode):
57 # THIS IS DEPRECATED, USE LetRefNode instead
58 # handle TempHandle
60 def generate_assignment_code(self, rhs, code):
61 pass
63 def generate_execution_code(self, code):
64 if self.type.is_pyobject:
65 code.put_decref_clear(self.result(), self.type)
66 self.handle.needs_cleanup = False
68 class TempsBlockNode(Node):
69 # THIS IS DEPRECATED, USE LetNode instead
71 """
72 Creates a block which allocates temporary variables.
73 This is used by transforms to output constructs that need
74 to make use of a temporary variable. Simply pass the types
75 of the needed temporaries to the constructor.
77 The variables can be referred to using a TempRefNode
78 (which can be constructed by calling get_ref_node).
79 """
81 # temps [TempHandle]
82 # body StatNode
84 child_attrs = ["body"]
86 def generate_execution_code(self, code):
87 for handle in self.temps:
88 handle.temp = code.funcstate.allocate_temp(
89 handle.type, manage_ref=handle.needs_cleanup)
90 self.body.generate_execution_code(code)
91 for handle in self.temps:
92 if handle.needs_cleanup:
93 if handle.needs_xdecref:
94 code.put_xdecref_clear(handle.temp, handle.type)
95 else:
96 code.put_decref_clear(handle.temp, handle.type)
97 code.funcstate.release_temp(handle.temp)
99 def analyse_control_flow(self, env):
100 self.body.analyse_control_flow(env)
102 def analyse_declarations(self, env):
103 self.body.analyse_declarations(env)
105 def analyse_expressions(self, env):
106 self.body.analyse_expressions(env)
108 def generate_function_definitions(self, env, code):
109 self.body.generate_function_definitions(env, code)
111 def annotate(self, code):
112 self.body.annotate(code)
115 class ResultRefNode(AtomicExprNode):
116 # A reference to the result of an expression. The result_code
117 # must be set externally (usually a temp name).
119 subexprs = []
121 def __init__(self, expression):
122 self.pos = expression.pos
123 self.expression = expression
124 if hasattr(expression, "type"):
125 self.type = expression.type
127 def analyse_types(self, env):
128 self.type = self.expression.type
130 def infer_type(self, env):
131 return self.expression.infer_type(env)
133 def is_simple(self):
134 return True
136 def result(self):
137 return self.result_code
139 def generate_evaluation_code(self, code):
140 pass
142 def generate_result_code(self, code):
143 pass
145 def generate_disposal_code(self, code):
146 pass
148 def generate_assignment_code(self, rhs, code):
149 if self.type.is_pyobject:
150 rhs.make_owned_reference(code)
151 code.put_decref(self.result(), self.ctype())
152 code.putln('%s = %s;' % (self.result(), rhs.result_as(self.ctype())))
153 rhs.generate_post_assignment_code(code)
154 rhs.free_temps(code)
156 def allocate_temps(self, env):
157 pass
159 def release_temp(self, env):
160 pass
162 def free_temps(self, code):
163 pass
166 class LetNodeMixin:
167 def set_temp_expr(self, lazy_temp):
168 self.lazy_temp = lazy_temp
169 self.temp_expression = lazy_temp.expression
171 def setup_temp_expr(self, code):
172 self.temp_expression.generate_evaluation_code(code)
173 self._result_in_temp = self.temp_expression.result_in_temp()
174 self.temp_type = self.temp_expression.type
175 if self._result_in_temp:
176 self.temp = self.temp_expression.result()
177 else:
178 self.temp_expression.make_owned_reference(code)
179 self.temp = code.funcstate.allocate_temp(
180 self.temp_type, manage_ref=True)
181 code.putln("%s = %s;" % (self.temp, self.temp_expression.result()))
182 self.lazy_temp.result_code = self.temp
184 def teardown_temp_expr(self, code):
185 if not self._result_in_temp:
186 if self.temp_type.is_pyobject:
187 code.put_decref_clear(self.temp, self.temp_type)
188 code.funcstate.release_temp(self.temp)
190 class EvalWithTempExprNode(ExprNodes.ExprNode, LetNodeMixin):
191 # A wrapper around a subexpression that moves an expression into a
192 # temp variable and provides it to the subexpression.
194 subexprs = ['temp_expression', 'subexpression']
196 def __init__(self, lazy_temp, subexpression):
197 self.set_temp_expr(lazy_temp)
198 self.pos = subexpression.pos
199 self.subexpression = subexpression
200 # if called after type analysis, we already know the type here
201 self.type = self.subexpression.type
203 def infer_type(self, env):
204 return self.subexpression.infer_type(env)
206 def result(self):
207 return self.subexpression.result()
209 def analyse_types(self, env):
210 self.temp_expression.analyse_types(env)
211 self.subexpression.analyse_types(env)
212 self.type = self.subexpression.type
214 def generate_evaluation_code(self, code):
215 self.setup_temp_expr(code)
216 self.subexpression.generate_evaluation_code(code)
217 self.teardown_temp_expr(code)
219 LetRefNode = ResultRefNode
221 class LetNode(Nodes.StatNode, LetNodeMixin):
222 # Implements a local temporary variable scope. Imagine this
223 # syntax being present:
224 # let temp = VALUE:
225 # BLOCK (can modify temp)
226 # if temp is an object, decref
227 #
228 # Usually used after analysis phase, but forwards analysis methods
229 # to its children
231 child_attrs = ['temp_expression', 'body']
233 def __init__(self, lazy_temp, body):
234 self.set_temp_expr(lazy_temp)
235 self.pos = body.pos
236 self.body = body
238 def analyse_control_flow(self, env):
239 self.body.analyse_control_flow(env)
241 def analyse_declarations(self, env):
242 self.temp_expression.analyse_declarations(env)
243 self.body.analyse_declarations(env)
245 def analyse_expressions(self, env):
246 self.temp_expression.analyse_expressions(env)
247 self.body.analyse_expressions(env)
249 def generate_execution_code(self, code):
250 self.setup_temp_expr(code)
251 self.body.generate_execution_code(code)
252 self.teardown_temp_expr(code)