From: Neko Asakura Date: Mon, 13 Apr 2026 12:56:29 +0000 (-0400) Subject: gh-148285: Allow recording uops after specializing uops (GH-148482) X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=5e74d920c80f4bf4d44d84838aed81423c42b9dd;p=thirdparty%2FPython%2Fcpython.git gh-148285: Allow recording uops after specializing uops (GH-148482) --- diff --git a/Lib/test/test_generated_cases.py b/Lib/test/test_generated_cases.py index 57991b5b6b85..bb831fa984c3 100644 --- a/Lib/test/test_generated_cases.py +++ b/Lib/test/test_generated_cases.py @@ -1890,6 +1890,64 @@ class TestGeneratedCases(unittest.TestCase): """ self.run_cases_test(input, output) + def test_recording_after_specializing_with_cache(self): + input = """ + specializing op(SPEC, (counter/1 --)) { + spam; + } + + tier2 op(REC, (--)) { + RECORD_VALUE(0); + } + + op(BODY, (--)) { + ham; + } + + macro(OP) = SPEC + unused/2 + REC + BODY; + """ + output = """ + TARGET(OP) { + #if _Py_TAIL_CALL_INTERP + int opcode = OP; + (void)(opcode); + #endif + _Py_CODEUNIT* const this_instr = next_instr; + (void)this_instr; + frame->instr_ptr = next_instr; + next_instr += 4; + INSTRUCTION_STATS(OP); + // SPEC + { + uint16_t counter = read_u16(&this_instr[1].cache); + (void)counter; + spam; + } + /* Skip 2 cache entries */ + // BODY + { + ham; + } + DISPATCH(); + } + """ + self.run_cases_test(input, output) + + def test_recording_after_non_specializing(self): + input = """ + op(REGULAR, (--)) { + spam; + } + + tier2 op(REC, (--)) { + RECORD_VALUE(0); + } + + macro(OP) = REGULAR + REC; + """ + with self.assertRaisesRegex(SyntaxError, "Recording uop"): + self.run_cases_test(input, "") + class TestGeneratedAbstractCases(unittest.TestCase): def setUp(self) -> None: diff --git a/Tools/cases_generator/analyzer.py b/Tools/cases_generator/analyzer.py index 6ba9c43ef1f0..100de4c72509 100644 --- a/Tools/cases_generator/analyzer.py +++ b/Tools/cases_generator/analyzer.py @@ -1132,7 +1132,9 @@ def add_macro( macro: parser.Macro, instructions: dict[str, Instruction], uops: dict[str, Uop] ) -> None: parts: list[Part] = [] - first = True + # Track the last non-specializing uop seen, so that recording uops + # can follow specializing ones without triggering the position check. + prev_uop: Uop | None = None for part in macro.uops: match part: case parser.OpName(): @@ -1144,12 +1146,14 @@ def add_macro( f"No Uop named {part.name}", macro.tokens[0] ) uop = uops[part.name] - if uop.properties.records_value and not first: + if uop.properties.records_value and prev_uop is not None: raise analysis_error( - f"Recording uop {part.name} must be first in macro", + f"Recording uop {part.name} is not allowed " + f"after non-specializing uops in macro", macro.tokens[0]) parts.append(uop) - first = False + if "specializing" not in uop.annotations: + prev_uop = uop case parser.CacheEffect(): parts.append(Skip(part.size)) case _: