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]

Reply via email to