From 3443edbcf49857020b50201e333e1e7be46e941b Mon Sep 17 00:00:00 2001 From: cocolato Date: Fri, 7 Aug 2026 18:00:23 +0800 Subject: [PATCH 1/3] prevent executor self-links in JIT cold exits --- Include/internal/pycore_uop_metadata.h | 2 +- Lib/test/test_capi/test_opt.py | 24 +++++++++++++++++++++++- Python/bytecodes.c | 4 ++++ Python/executor_cases.c.h | 8 ++++++++ 4 files changed, 36 insertions(+), 2 deletions(-) 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/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; From 8fe3fe51bdb230813a113d525886ccfcc3c4a767 Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Fri, 7 Aug 2026 10:14:59 +0000 Subject: [PATCH 2/3] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20blu?= =?UTF-8?q?rb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../2026-08-07-10-14-56.gh-issue-154701.zulh2S.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2026-08-07-10-14-56.gh-issue-154701.zulh2S.rst 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. From 842fce12deae72c0159977f3dca2f3b8d3a8b200 Mon Sep 17 00:00:00 2001 From: cocolato Date: Fri, 7 Aug 2026 18:35:51 +0800 Subject: [PATCH 3/3] fix windows ci --- Include/internal/pycore_optimizer.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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