Skip to content

Commit 0e9ce84

Browse files
Address review
1 parent 634ed26 commit 0e9ce84

File tree

5 files changed

+36
-49
lines changed

5 files changed

+36
-49
lines changed

Include/internal/pycore_uop_metadata.h

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Lib/test/test_generated_cases.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2411,14 +2411,14 @@ def test_replace_opocode_uop_reject_array_effects(self):
24112411
"""
24122412
input2 = """
24132413
op(OP, (foo[2] -- res)) {
2414-
REPLACE_OPCODE_IF_EVALUATES_PURE(foo[2]);
2414+
REPLACE_OPCODE_IF_EVALUATES_PURE(foo);
24152415
res = sym_new_unknown(ctx);
24162416
}
24172417
"""
24182418
output = """
24192419
"""
2420-
with self.assertRaisesRegex(AssertionError,
2421-
"Unsafe to convert a symbol to an array-like StackRef."):
2420+
with self.assertRaisesRegex(SyntaxError,
2421+
"Pure evaluation cannot take array-like inputs"):
24222422
self.run_cases_test(input, input2, output)
24232423

24242424
if __name__ == "__main__":

Python/bytecodes.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -829,7 +829,7 @@ dummy_func(
829829
DEOPT_IF(!res);
830830
}
831831

832-
pure op(_BINARY_OP_EXTEND, (descr/4, left, right -- res)) {
832+
op(_BINARY_OP_EXTEND, (descr/4, left, right -- res)) {
833833
PyObject *left_o = PyStackRef_AsPyObjectBorrow(left);
834834
PyObject *right_o = PyStackRef_AsPyObjectBorrow(right);
835835
assert(INLINE_CACHE_ENTRIES_BINARY_OP == 5);

Tools/cases_generator/generators_common.py

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -86,16 +86,6 @@ def emit_to(out: CWriter, tkn_iter: TokenIterator, end: str) -> Token:
8686
out.emit(tkn)
8787
raise analysis_error(f"Expecting {end}. Reached end of file", tkn)
8888

89-
def skip_to(tkn_iter: TokenIterator, end: str) -> Token:
90-
parens = 0
91-
for tkn in tkn_iter:
92-
if tkn.kind == end and parens == 0:
93-
return tkn
94-
if tkn.kind == "LPAREN":
95-
parens += 1
96-
if tkn.kind == "RPAREN":
97-
parens -= 1
98-
raise analysis_error(f"Expecting {end}. Reached end of file", tkn)
9989

10090
ReplacementFunctionType = Callable[
10191
[Token, TokenIterator, CodeSection, Storage, Instruction | None], bool

Tools/cases_generator/optimizer_generator.py

Lines changed: 31 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
"""
55

66
import argparse
7-
import copy
87

98
from analyzer import (
109
Analysis,
@@ -22,7 +21,6 @@
2221
write_header,
2322
Emitter,
2423
TokenIterator,
25-
skip_to,
2624
always_true,
2725
)
2826
from cwriter import CWriter
@@ -81,8 +79,7 @@ def type_name(var: StackItem) -> str:
8179
return "JitOptRef "
8280

8381
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."
8683
return "_PyStackRef "
8784

8885
def declare_variables(uop: Uop, out: CWriter, skip_inputs: bool) -> None:
@@ -168,27 +165,45 @@ def replace_opcode_if_evaluates_pure(
168165
storage: Storage,
169166
inst: Instruction | None,
170167
) -> 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
172174

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)
177175
if len(input_identifiers) == 0:
178176
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",
180178
self.original_uop.body.open
181179
)
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")
185198
emitter.emit(') {\n')
186199
# Declare variables, before they are shadowed.
187-
for inp in self.original_uop.stack.inputs:
200+
for inp in used_stack_inputs:
188201
if inp.used:
189202
emitter.emit(f"{type_name(inp)}{inp.name}_sym = {inp.name};\n")
190203
# 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)
192207
if inp.used:
193208
emitter.emit(f"{stackref_type_name(inp)}{inp.name} = sym_get_const_as_stackref(ctx, {inp.name}_sym);\n")
194209
# Rename all output variables to stackref variant.
@@ -321,24 +336,6 @@ def error_if(
321336
return not unconditional
322337

323338

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-
342339
def write_uop(
343340
override: Uop | None,
344341
uop: Uop,
@@ -369,7 +366,7 @@ def write_uop(
369366
cast = f"uint{cache.size*16}_t"
370367
out.emit(f"{type}{cache.name} = ({cast})this_instr->operand0;\n")
371368
if override:
372-
emitter = OptimizerEmitter(out, {}, uop, copy.deepcopy(stack))
369+
emitter = OptimizerEmitter(out, {}, uop, stack.copy())
373370
# No reference management of inputs needed.
374371
for var in storage.inputs: # type: ignore[possibly-undefined]
375372
var.in_local = False

0 commit comments

Comments
 (0)