|
4 | 4 | """ |
5 | 5 |
|
6 | 6 | import argparse |
7 | | -import copy |
8 | 7 |
|
9 | 8 | from analyzer import ( |
10 | 9 | Analysis, |
|
22 | 21 | write_header, |
23 | 22 | Emitter, |
24 | 23 | TokenIterator, |
25 | | - skip_to, |
26 | 24 | always_true, |
27 | 25 | ) |
28 | 26 | from cwriter import CWriter |
@@ -81,8 +79,7 @@ def type_name(var: StackItem) -> str: |
81 | 79 | return "JitOptRef " |
82 | 80 |
|
83 | 81 | def stackref_type_name(var: StackItem) -> str: |
84 | | - if var.is_array(): |
85 | | - assert False, "Unsafe to convert a symbol to an array-like StackRef." |
| 82 | + assert not var.is_array(), "Unsafe to convert a symbol to an array-like StackRef." |
86 | 83 | return "_PyStackRef " |
87 | 84 |
|
88 | 85 | def declare_variables(uop: Uop, out: CWriter, skip_inputs: bool) -> None: |
@@ -168,27 +165,45 @@ def replace_opcode_if_evaluates_pure( |
168 | 165 | storage: Storage, |
169 | 166 | inst: Instruction | None, |
170 | 167 | ) -> bool: |
171 | | - skip_to(tkn_iter, "SEMI") |
| 168 | + input_identifiers = [] |
| 169 | + for token in tkn_iter: |
| 170 | + if token.kind == "IDENTIFIER": |
| 171 | + input_identifiers.append(token) |
| 172 | + if token.kind == "SEMI": |
| 173 | + break |
172 | 174 |
|
173 | | - emitter = OptimizerConstantEmitter(self.out, {}, self.original_uop, copy.deepcopy(self.stack)) |
174 | | - emitter.emit("if (\n") |
175 | | - assert isinstance(uop, Uop) |
176 | | - input_identifiers = replace_opcode_if_evaluates_pure_identifiers(uop) |
177 | 175 | if len(input_identifiers) == 0: |
178 | 176 | raise analysis_error( |
179 | | - "Pure operations must have at least 1 input", |
| 177 | + "To evaluate an operation as pure, it must have at least 1 input", |
180 | 178 | self.original_uop.body.open |
181 | 179 | ) |
182 | | - for inp in input_identifiers[:-1]: |
183 | | - emitter.emit(f"sym_is_safe_const(ctx, {inp}) &&\n") |
184 | | - emitter.emit(f"sym_is_safe_const(ctx, {input_identifiers[-1]})\n") |
| 180 | + # Check that the input identifiers belong to the uop's |
| 181 | + # input stack effect |
| 182 | + uop_stack_effect_input_identifers = {inp.name for inp in uop.stack.inputs} |
| 183 | + for input_tkn in input_identifiers: |
| 184 | + if input_tkn.text not in uop_stack_effect_input_identifers: |
| 185 | + raise analysis_error(f"{input_tkn.text} referenced in " |
| 186 | + f"REPLACE_OPCODE_IF_EVALUATES_PURE but does not " |
| 187 | + f"exist in the base uop's input stack effects", |
| 188 | + input_tkn) |
| 189 | + input_identifiers_as_str = {tkn.text for tkn in input_identifiers} |
| 190 | + used_stack_inputs = [inp for inp in uop.stack.inputs if inp.name in input_identifiers_as_str] |
| 191 | + assert len(used_stack_inputs) > 0 |
| 192 | + emitter = OptimizerConstantEmitter(self.out, {}, self.original_uop, self.stack.copy()) |
| 193 | + emitter.emit("if (\n") |
| 194 | + assert isinstance(uop, Uop) |
| 195 | + for inp in used_stack_inputs[:-1]: |
| 196 | + emitter.emit(f"sym_is_safe_const(ctx, {inp.name}) &&\n") |
| 197 | + emitter.emit(f"sym_is_safe_const(ctx, {used_stack_inputs[-1].name})\n") |
185 | 198 | emitter.emit(') {\n') |
186 | 199 | # Declare variables, before they are shadowed. |
187 | | - for inp in self.original_uop.stack.inputs: |
| 200 | + for inp in used_stack_inputs: |
188 | 201 | if inp.used: |
189 | 202 | emitter.emit(f"{type_name(inp)}{inp.name}_sym = {inp.name};\n") |
190 | 203 | # Shadow the symbolic variables with stackrefs. |
191 | | - for inp in self.original_uop.stack.inputs: |
| 204 | + for inp in used_stack_inputs: |
| 205 | + if inp.is_array(): |
| 206 | + raise analysis_error("Pure evaluation cannot take array-like inputs.", tkn) |
192 | 207 | if inp.used: |
193 | 208 | emitter.emit(f"{stackref_type_name(inp)}{inp.name} = sym_get_const_as_stackref(ctx, {inp.name}_sym);\n") |
194 | 209 | # Rename all output variables to stackref variant. |
@@ -321,24 +336,6 @@ def error_if( |
321 | 336 | return not unconditional |
322 | 337 |
|
323 | 338 |
|
324 | | -def replace_opcode_if_evaluates_pure_identifiers(uop: Uop) -> list[str]: |
325 | | - token = None |
326 | | - iterator = uop.body.tokens() |
327 | | - for token in iterator: |
328 | | - if token.kind == "IDENTIFIER" and token.text == "REPLACE_OPCODE_IF_EVALUATES_PURE": |
329 | | - break |
330 | | - assert token is not None |
331 | | - assert token.kind == "IDENTIFIER" and token.text == "REPLACE_OPCODE_IF_EVALUATES_PURE", uop.name |
332 | | - assert next(iterator).kind == "LPAREN" |
333 | | - idents = [] |
334 | | - for token in iterator: |
335 | | - if token.kind == "RPAREN": |
336 | | - break |
337 | | - if token.kind == "IDENTIFIER": |
338 | | - idents.append(token.text) |
339 | | - return idents |
340 | | - |
341 | | - |
342 | 339 | def write_uop( |
343 | 340 | override: Uop | None, |
344 | 341 | uop: Uop, |
@@ -369,7 +366,7 @@ def write_uop( |
369 | 366 | cast = f"uint{cache.size*16}_t" |
370 | 367 | out.emit(f"{type}{cache.name} = ({cast})this_instr->operand0;\n") |
371 | 368 | if override: |
372 | | - emitter = OptimizerEmitter(out, {}, uop, copy.deepcopy(stack)) |
| 369 | + emitter = OptimizerEmitter(out, {}, uop, stack.copy()) |
373 | 370 | # No reference management of inputs needed. |
374 | 371 | for var in storage.inputs: # type: ignore[possibly-undefined] |
375 | 372 | var.in_local = False |
|
0 commit comments