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]