]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-148285: Allow recording uops after specializing uops (GH-148482)
authorNeko Asakura <neko.asakura@outlook.com>
Mon, 13 Apr 2026 12:56:29 +0000 (08:56 -0400)
committerGitHub <noreply@github.com>
Mon, 13 Apr 2026 12:56:29 +0000 (20:56 +0800)
Lib/test/test_generated_cases.py
Tools/cases_generator/analyzer.py

index 57991b5b6b859cd4f26b2b7eab393cbc712cdfed..bb831fa984c34bde476ae0b624b12ac8651b7b2a 100644 (file)
@@ -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:
index 6ba9c43ef1f0c38cbdeee18aa3120434c17e88f8..100de4c7250907c566b1d2a90f91c64872c62c0a 100644 (file)
@@ -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 _: