https://github.com/python/cpython/commit/716cbae06c7d9d641626dfdb783f3959edf470a4
commit: 716cbae06c7d9d641626dfdb783f3959edf470a4
branch: main
author: Hai Zhu <[email protected]>
committer: Fidget-Spinner <[email protected]>
date: 2026-08-13T12:23:45+01:00
summary:
gh-154701: prevent executor self-links in JIT cold exits (GH-155323)
* prevent executor self-links in JIT cold exits
* 📜🤖 Added by blurb_it.
* fix windows ci
---------
Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com>
files:
A
Misc/NEWS.d/next/Core_and_Builtins/2026-08-07-10-14-56.gh-issue-154701.zulh2S.rst
M Include/internal/pycore_optimizer.h
M Include/internal/pycore_uop_metadata.h
M Lib/test/test_capi/test_opt.py
M Python/bytecodes.c
M Python/executor_cases.c.h
diff --git a/Include/internal/pycore_optimizer.h
b/Include/internal/pycore_optimizer.h
index 3d60638649dcb5a..9f4f8918a40d1ad 100644
--- a/Include/internal/pycore_optimizer.h
+++ b/Include/internal/pycore_optimizer.h
@@ -206,7 +206,7 @@ typedef struct _PyExecutorObject {
PyAPI_FUNC(_PyExecutorObject*) _Py_GetExecutor(PyCodeObject *code, int offset);
int _Py_ExecutorInit(_PyExecutorObject *, const _PyBloomFilter *);
-void _Py_ExecutorDetach(_PyExecutorObject *);
+PyAPI_FUNC(void) _Py_ExecutorDetach(_PyExecutorObject *);
PyAPI_FUNC(void) _Py_Executor_DependsOn(_PyExecutorObject *executor, void
*obj);
/* We use a bloomfilter with k = 6, m = 256
diff --git a/Include/internal/pycore_uop_metadata.h
b/Include/internal/pycore_uop_metadata.h
index e52233b21277591..02e755330d6c209 100644
--- a/Include/internal/pycore_uop_metadata.h
+++ b/Include/internal/pycore_uop_metadata.h
@@ -408,7 +408,7 @@ const uint32_t _PyUop_Flags[MAX_UOP_ID+1] = {
[_ERROR_POP_N] = HAS_ARG_FLAG | HAS_SYNC_SP_FLAG,
[_SPILL_OR_RELOAD] = 0,
[_TIER2_RESUME_CHECK] = HAS_PERIODIC_FLAG,
- [_COLD_EXIT] = HAS_SYNC_SP_FLAG,
+ [_COLD_EXIT] = HAS_ESCAPES_FLAG | HAS_SYNC_SP_FLAG,
[_COLD_DYNAMIC_EXIT] = HAS_SYNC_SP_FLAG,
[_GUARD_CODE_VERSION__PUSH_FRAME] = HAS_EXIT_FLAG,
[_GUARD_CODE_VERSION_YIELD_VALUE] = HAS_EXIT_FLAG,
diff --git a/Lib/test/test_capi/test_opt.py b/Lib/test/test_capi/test_opt.py
index 5806216d46e7eb6..d5a94ef69c14de0 100644
--- a/Lib/test/test_capi/test_opt.py
+++ b/Lib/test/test_capi/test_opt.py
@@ -12,7 +12,7 @@
from test.support import (script_helper, requires_specialization,
import_helper, Py_GIL_DISABLED, requires_jit_enabled,
- reset_code)
+ reset_code, SHORT_TIMEOUT, isolation)
_testinternalcapi = import_helper.import_module("_testinternalcapi")
@@ -6225,6 +6225,28 @@ def __exit__(self, e, v, t): ...
f1()
"""), PYTHON_JIT="1")
+ @isolation.runInSubprocess(timeout=SHORT_TIMEOUT)
+ def test_for_iter_side_exit_does_not_self_link(self):
+ def exhaust(iterator):
+ for _ in iterator:
+ pass
+
+ values = range(TIER2_THRESHOLD)
+ # After the initial trace, MAX_CHAIN_DEPTH side exits cause the final
+ # executor to be installed at FOR_ITER.
+ warmup_iterators = (
+ iter(set(values)),
+ iter(dict.fromkeys(values)),
+ iter(values),
+ enumerate(values),
+ zip(values, values),
+ )
+ for iterator in warmup_iterators:
+ exhaust(iterator)
+
+ # A different iterator type must not link that executor to itself.
+ exhaust(map(bool, values))
+
def global_identity(x):
return x
diff --git
a/Misc/NEWS.d/next/Core_and_Builtins/2026-08-07-10-14-56.gh-issue-154701.zulh2S.rst
b/Misc/NEWS.d/next/Core_and_Builtins/2026-08-07-10-14-56.gh-issue-154701.zulh2S.rst
new file mode 100644
index 000000000000000..c32803e71cbf993
--- /dev/null
+++
b/Misc/NEWS.d/next/Core_and_Builtins/2026-08-07-10-14-56.gh-issue-154701.zulh2S.rst
@@ -0,0 +1 @@
+Fix an infinite loop in JIT when a ``FOR_ITER`` side exit links an executor
back to itself.
diff --git a/Python/bytecodes.c b/Python/bytecodes.c
index 4d7b338e2dbd4c3..d657ae579a8f986 100644
--- a/Python/bytecodes.c
+++ b/Python/bytecodes.c
@@ -6262,6 +6262,10 @@ dummy_func(
if (target->op.code == ENTER_EXECUTOR) {
PyCodeObject *code = _PyFrame_GetCode(frame);
executor = code->co_executors->executors[target->op.arg];
+ if (executor == _PyExecutor_FromExit(exit)) {
+ _Py_ExecutorDetach(executor);
+ GOTO_TIER_ONE(target);
+ }
Py_INCREF(executor);
assert(tstate->jit_exit == exit);
exit->executor = executor;
diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h
index e45bbd7cceb295f..46e721ea34b6b6e 100644
--- a/Python/executor_cases.c.h
+++ b/Python/executor_cases.c.h
@@ -23789,6 +23789,14 @@
if (target->op.code == ENTER_EXECUTOR) {
PyCodeObject *code = _PyFrame_GetCode(frame);
executor = code->co_executors->executors[target->op.arg];
+ if (executor == _PyExecutor_FromExit(exit)) {
+ _PyFrame_SetStackPointer(frame, stack_pointer);
+ _PyFrame_StackPointerValidate(frame);
+ _Py_ExecutorDetach(executor);
+ _PyFrame_StackPointerInvalidate(frame);
+ SET_CURRENT_CACHED_VALUES(0);
+ GOTO_TIER_ONE(target);
+ }
Py_INCREF(executor);
assert(tstate->jit_exit == exit);
exit->executor = executor;
_______________________________________________
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]