1515 analysis_error ,
1616 CodeSection ,
1717 Label ,
18- uop_variable_used ,
1918)
2019from generators_common import (
2120 DEFAULT_INPUT ,
@@ -148,10 +147,12 @@ def emit_default(out: CWriter, uop: Uop, stack: Stack) -> None:
148147
149148class OptimizerEmitter (Emitter ):
150149
151- def __init__ (self , out : CWriter , labels : dict [str , Label ]):
150+ def __init__ (self , out : CWriter , labels : dict [str , Label ], original_uop : Uop , stack : Stack ):
152151 super ().__init__ (out , labels )
153152 self ._replacers ["REPLACE_OPCODE_IF_EVALUATES_PURE" ] = self .replace_opcode_if_evaluates_pure
154153 self .is_abstract = True
154+ self .original_uop = original_uop
155+ self .stack = stack
155156
156157 def emit_save (self , storage : Storage ) -> None :
157158 storage .flush (self .out )
@@ -172,14 +173,55 @@ def replace_opcode_if_evaluates_pure(
172173 inst : Instruction | None ,
173174 ) -> bool :
174175 skip_to (tkn_iter , "SEMI" )
176+ emitter = OptimizerConstantEmitter (self .out , {}, self .original_uop , copy .deepcopy (self .stack ))
177+ emitter .emit ("if (\n " )
178+ input_identifiers = replace_opcode_if_evaluates_pure_identifiers (uop )
179+ assert len (input_identifiers ) > 0 , "Pure operations must have at least 1 input"
180+ for inp in input_identifiers [:- 1 ]:
181+ emitter .emit (f"sym_is_safe_const(ctx, { inp } ) &&\n " )
182+ emitter .emit (f"sym_is_safe_const(ctx, { input_identifiers [- 1 ]} )\n " )
183+ emitter .emit (') {\n ' )
184+ # Declare variables, before they are shadowed.
185+ for inp in self .original_uop .stack .inputs :
186+ if inp .used :
187+ emitter .emit (f"{ type_name (inp )} { inp .name } _sym = { inp .name } ;\n " )
188+ # Shadow the symbolic variables with stackrefs.
189+ for inp in self .original_uop .stack .inputs :
190+ if inp .used :
191+ emitter .emit (f"{ stackref_type_name (inp )} { inp .name } = sym_get_const_as_stackref(ctx, { inp .name } _sym);\n " )
192+ # Rename all output variables to stackref variant.
193+ for outp in self .original_uop .stack .outputs :
194+ assert not outp .is_array (), "Array output StackRefs not supported for pure ops."
195+ emitter .emit (f"_PyStackRef { outp .name } _stackref;\n " )
196+
197+
198+ storage = Storage .for_uop (self .stack , self .original_uop , CWriter .null (), check_liveness = False )
199+ # No reference management of outputs needed.
200+ for var in storage .outputs :
201+ var .in_local = True
202+ emitter .emit ("/* Start of uop copied from bytecodes for constant evaluation */\n " )
203+ emitter .emit_tokens (self .original_uop , storage , inst = None , emit_braces = False )
204+ self .out .start_line ()
205+ emitter .emit ("/* End of uop copied from bytecodes for constant evaluation */\n " )
206+ # Finally, assign back the output stackrefs to symbolics.
207+ for outp in self .original_uop .stack .outputs :
208+ # All new stackrefs are created from new references.
209+ # That's how the stackref contract works.
210+ if not outp .peek :
211+ emitter .emit (f"{ outp .name } = sym_new_const_steal(ctx, PyStackRef_AsPyObjectSteal({ outp .name } _stackref));\n " )
212+ else :
213+ emitter .emit (f"{ outp .name } = sym_new_const(ctx, PyStackRef_AsPyObjectBorrow({ outp .name } _stackref));\n " )
214+ storage .flush (self .out )
215+ emitter .emit ("break;\n " )
216+ emitter .emit ("}\n " )
175217 return True
176218
177219class OptimizerConstantEmitter (OptimizerEmitter ):
178- def __init__ (self , out : CWriter , labels : dict [str , Label ], uop : Uop ):
179- super ().__init__ (out , labels )
220+ def __init__ (self , out : CWriter , labels : dict [str , Label ], original_uop : Uop , stack : Stack ):
221+ super ().__init__ (out , labels , original_uop , stack )
180222 # Replace all outputs to point to their stackref versions.
181223 overrides = {
182- outp .name : self .emit_stackref_override for outp in uop .stack .outputs
224+ outp .name : self .emit_stackref_override for outp in self . original_uop .stack .outputs
183225 }
184226 self ._replacers = {** self ._replacers , ** overrides }
185227
@@ -214,59 +256,6 @@ def replace_opcode_if_evaluates_pure_identifiers(uop: Uop) -> list[str]:
214256 return idents
215257
216258
217- def write_uop_pure_evaluation_region_header (
218- uop : Uop ,
219- override : Uop ,
220- out : CWriter ,
221- stack : Stack ,
222- ) -> None :
223- emitter = OptimizerConstantEmitter (out , {}, uop )
224- emitter .emit ("if (\n " )
225- input_identifiers = replace_opcode_if_evaluates_pure_identifiers (override )
226- assert len (input_identifiers ) > 0 , "Pure operations must have at least 1 input"
227- for inp in input_identifiers [:- 1 ]:
228- emitter .emit (f"sym_is_safe_const(ctx, { inp } ) &&\n " )
229- emitter .emit (f"sym_is_safe_const(ctx, { input_identifiers [- 1 ]} )\n " )
230- emitter .emit (') {\n ' )
231- # Declare variables, before they are shadowed.
232- for inp in uop .stack .inputs :
233- if inp .used :
234- emitter .emit (f"{ type_name (inp )} { inp .name } _sym = { inp .name } ;\n " )
235- # Shadow the symbolic variables with stackrefs.
236- for inp in uop .stack .inputs :
237- if inp .used :
238- emitter .emit (f"{ stackref_type_name (inp )} { inp .name } = sym_get_const_as_stackref(ctx, { inp .name } _sym);\n " )
239- # Rename all output variables to stackref variant.
240- for outp in uop .stack .outputs :
241- assert not outp .is_array (), "Array output StackRefs not supported for pure ops."
242- emitter .emit (f"_PyStackRef { outp .name } _stackref;\n " )
243- stack = copy .deepcopy (stack )
244-
245- storage = Storage .for_uop (stack , uop , CWriter .null (), check_liveness = False )
246- # No reference management of outputs needed.
247- for var in storage .outputs :
248- var .in_local = True
249- emitter .emit ("/* Start of uop copied from bytecodes for constant evaluation */\n " )
250- emitter .emit_tokens (uop , storage , inst = None , emit_braces = False )
251- out .start_line ()
252- emitter .emit ("/* End of uop copied from bytecodes for constant evaluation */\n " )
253- # Finally, assign back the output stackrefs to symbolics.
254- for outp in uop .stack .outputs :
255- # All new stackrefs are created from new references.
256- # That's how the stackref contract works.
257- if not outp .peek :
258- emitter .emit (f"{ outp .name } = sym_new_const_steal(ctx, PyStackRef_AsPyObjectSteal({ outp .name } _stackref));\n " )
259- else :
260- emitter .emit (f"{ outp .name } = sym_new_const(ctx, PyStackRef_AsPyObjectBorrow({ outp .name } _stackref));\n " )
261- storage .flush (out )
262- emitter .emit ("}\n " )
263- emitter .emit ("else {\n " )
264-
265- def write_uop_pure_evaluation_region_footer (
266- out : CWriter ,
267- ) -> None :
268- out .emit ("}\n " )
269-
270259def write_uop (
271260 override : Uop | None ,
272261 uop : Uop ,
@@ -299,16 +288,11 @@ def write_uop(
299288 # No reference management of inputs needed.
300289 for var in storage .inputs : # type: ignore[possibly-undefined]
301290 var .in_local = False
302- replace_opcode_if_evaluates_pure = uop_variable_used (override , "REPLACE_OPCODE_IF_EVALUATES_PURE" )
303- if replace_opcode_if_evaluates_pure :
304- write_uop_pure_evaluation_region_header (uop , override , out , stack )
305291 out .start_line ()
306- emitter = OptimizerEmitter (out , {})
292+ emitter = OptimizerEmitter (out , {}, uop , copy . deepcopy ( stack ) )
307293 _ , storage = emitter .emit_tokens (override , storage , inst = None , emit_braces = False )
308294 storage .flush (out )
309295 out .start_line ()
310- if replace_opcode_if_evaluates_pure :
311- write_uop_pure_evaluation_region_footer (out )
312296 else :
313297 emit_default (out , uop , stack )
314298 out .start_line ()
0 commit comments