]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-139109: JIT _EXIT_TRACE to ENTER_EXECUTOR rather than _DEOPT (GH-141573)
authorKen Jin <kenjin@python.org>
Sat, 15 Nov 2025 20:19:41 +0000 (04:19 +0800)
committerGitHub <noreply@github.com>
Sat, 15 Nov 2025 20:19:41 +0000 (20:19 +0000)
Include/internal/pycore_optimizer.h
Lib/test/test_capi/test_opt.py
Python/bytecodes.c
Python/generated_cases.c.h
Python/optimizer.c

index 653285a2c6b79b0cf935a814f5ba393e619cbb29..0307a174e77346ef4338cd64da82a1822b70d444 100644 (file)
@@ -362,7 +362,7 @@ PyAPI_FUNC(int) _PyDumpExecutors(FILE *out);
 extern void _Py_ClearExecutorDeletionList(PyInterpreterState *interp);
 #endif
 
-int _PyJit_translate_single_bytecode_to_trace(PyThreadState *tstate, _PyInterpreterFrame *frame, _Py_CODEUNIT *next_instr, bool stop_tracing);
+int _PyJit_translate_single_bytecode_to_trace(PyThreadState *tstate, _PyInterpreterFrame *frame, _Py_CODEUNIT *next_instr, int stop_tracing_opcode);
 
 int
 _PyJit_TryInitializeTracing(PyThreadState *tstate, _PyInterpreterFrame *frame,
index f06c6cbda2976ca48582831049d80116d3825751..25372fee58e0d7417b4a45a49447cf4a36b57f3b 100644 (file)
@@ -40,6 +40,17 @@ def get_first_executor(func):
             pass
     return None
 
+def get_all_executors(func):
+    code = func.__code__
+    co_code = code.co_code
+    executors = []
+    for i in range(0, len(co_code), 2):
+        try:
+            executors.append(_opcode.get_executor(code, i))
+        except ValueError:
+            pass
+    return executors
+
 
 def iter_opnames(ex):
     for item in ex:
@@ -2629,6 +2640,31 @@ class TestUopsOptimization(unittest.TestCase):
             next(g)
         """ % _testinternalcapi.SPECIALIZATION_THRESHOLD))
 
+    def test_executor_side_exits_create_another_executor(self):
+        def f():
+            for x in range(TIER2_THRESHOLD + 3):
+                for y in range(TIER2_THRESHOLD + 3):
+                    z = x + y
+
+        f()
+        all_executors = get_all_executors(f)
+        # Inner loop warms up first.
+        # Outer loop warms up later, linking to the inner one.
+        # Therefore, we have at least two executors.
+        self.assertGreaterEqual(len(all_executors), 2)
+        for executor in all_executors:
+            opnames = list(get_opnames(executor))
+            # Assert all executors first terminator ends in
+            # _EXIT_TRACE or _JUMP_TO_TOP, not _DEOPT
+            for idx, op in enumerate(opnames):
+                if op == "_EXIT_TRACE" or op == "_JUMP_TO_TOP":
+                    break
+                elif op == "_DEOPT":
+                    self.fail(f"_DEOPT encountered first at executor"
+                              f" {executor} at offset {idx} rather"
+                              f" than expected _EXIT_TRACE")
+
+
 
 def global_identity(x):
     return x
index 8a7b784bb9eec2e787d6b383d1b46d0dd375afa2..565eaa7a59917578a818ebdd33ff61e7fe9b1e3b 100644 (file)
@@ -5643,7 +5643,7 @@ dummy_func(
             bool stop_tracing = (opcode == WITH_EXCEPT_START ||
                 opcode == RERAISE || opcode == CLEANUP_THROW ||
                 opcode == PUSH_EXC_INFO || opcode == INTERPRETER_EXIT);
-            int full = !_PyJit_translate_single_bytecode_to_trace(tstate, frame, next_instr, stop_tracing);
+            int full = !_PyJit_translate_single_bytecode_to_trace(tstate, frame, next_instr, stop_tracing ? _DEOPT : 0);
             if (full) {
                 LEAVE_TRACING();
                 int err = stop_tracing_and_jit(tstate, frame);
@@ -5683,7 +5683,7 @@ dummy_func(
 #if _Py_TIER2
             assert(IS_JIT_TRACING());
             int opcode = next_instr->op.code;
-            _PyJit_translate_single_bytecode_to_trace(tstate, frame, NULL, true);
+            _PyJit_translate_single_bytecode_to_trace(tstate, frame, NULL, _EXIT_TRACE);
             LEAVE_TRACING();
             int err = stop_tracing_and_jit(tstate, frame);
             ERROR_IF(err < 0);
index 01f65d9dd375f7d831c12f14a582a7dca0bb17f7..0d4678df68ce2dd235c6941ac4b0f7092867bd52 100644 (file)
@@ -12263,7 +12263,7 @@ JUMP_TO_LABEL(error);
                                  opcode == RERAISE || opcode == CLEANUP_THROW ||
                                  opcode == PUSH_EXC_INFO || opcode == INTERPRETER_EXIT);
             _PyFrame_SetStackPointer(frame, stack_pointer);
-            int full = !_PyJit_translate_single_bytecode_to_trace(tstate, frame, next_instr, stop_tracing);
+            int full = !_PyJit_translate_single_bytecode_to_trace(tstate, frame, next_instr, stop_tracing ? _DEOPT : 0);
             stack_pointer = _PyFrame_GetStackPointer(frame);
             if (full) {
                 LEAVE_TRACING();
@@ -12309,7 +12309,7 @@ JUMP_TO_LABEL(error);
             assert(IS_JIT_TRACING());
             int opcode = next_instr->op.code;
             _PyFrame_SetStackPointer(frame, stack_pointer);
-            _PyJit_translate_single_bytecode_to_trace(tstate, frame, NULL, true);
+            _PyJit_translate_single_bytecode_to_trace(tstate, frame, NULL, _EXIT_TRACE);
             stack_pointer = _PyFrame_GetStackPointer(frame);
             LEAVE_TRACING();
             _PyFrame_SetStackPointer(frame, stack_pointer);
index 65007a256d0c3b055982dc15c68a416d2adbd5da..9db894f0bf054a127c9b8d3477f76830f5ffef05 100644 (file)
@@ -574,7 +574,7 @@ _PyJit_translate_single_bytecode_to_trace(
     PyThreadState *tstate,
     _PyInterpreterFrame *frame,
     _Py_CODEUNIT *next_instr,
-    bool stop_tracing)
+    int stop_tracing_opcode)
 {
 
 #ifdef Py_DEBUG
@@ -637,8 +637,8 @@ _PyJit_translate_single_bytecode_to_trace(
         goto full;
     }
 
-    if (stop_tracing) {
-        ADD_TO_TRACE(_DEOPT, 0, 0, target);
+    if (stop_tracing_opcode != 0) {
+        ADD_TO_TRACE(stop_tracing_opcode, 0, 0, target);
         goto done;
     }