https://github.com/python/cpython/commit/ed73c909f278a1eb558b120ef8ed2c0f8528bf58
commit: ed73c909f278a1eb558b120ef8ed2c0f8528bf58
branch: main
author: Ken Jin <[email protected]>
committer: Fidget-Spinner <[email protected]>
date: 2025-11-15T20:19:41Z
summary:

gh-139109: JIT _EXIT_TRACE to ENTER_EXECUTOR rather than _DEOPT (GH-141573)

files:
M Include/internal/pycore_optimizer.h
M Lib/test/test_capi/test_opt.py
M Python/bytecodes.c
M Python/generated_cases.c.h
M Python/optimizer.c

diff --git a/Include/internal/pycore_optimizer.h 
b/Include/internal/pycore_optimizer.h
index 653285a2c6b79b..0307a174e77346 100644
--- a/Include/internal/pycore_optimizer.h
+++ b/Include/internal/pycore_optimizer.h
@@ -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,
diff --git a/Lib/test/test_capi/test_opt.py b/Lib/test/test_capi/test_opt.py
index f06c6cbda2976c..25372fee58e0d7 100644
--- a/Lib/test/test_capi/test_opt.py
+++ b/Lib/test/test_capi/test_opt.py
@@ -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 @@ def gen():
             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
diff --git a/Python/bytecodes.c b/Python/bytecodes.c
index 8a7b784bb9eec2..565eaa7a599175 100644
--- a/Python/bytecodes.c
+++ b/Python/bytecodes.c
@@ -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);
diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h
index 01f65d9dd375f7..0d4678df68ce2d 100644
--- a/Python/generated_cases.c.h
+++ b/Python/generated_cases.c.h
@@ -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);
diff --git a/Python/optimizer.c b/Python/optimizer.c
index 65007a256d0c3b..9db894f0bf054a 100644
--- a/Python/optimizer.c
+++ b/Python/optimizer.c
@@ -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;
     }
 

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to