From ad90e7683112c6ab5c5662d0a37226da6a5dca87 Mon Sep 17 00:00:00 2001 From: Ken Jin Date: Wed, 14 Jan 2026 18:35:45 +0000 Subject: [PATCH 1/4] ENTER_EXECUTOR can be inserted after BINARY_OP_INPLACE_ADD_UNICODE --- Python/bytecodes.c | 5 +++++ Python/executor_cases.c.h | 10 ++++++++++ Python/generated_cases.c.h | 7 +++++++ 3 files changed, 22 insertions(+) diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 66f322c2ef7757..d16a35bb5349fc 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -783,6 +783,11 @@ dummy_func( int next_oparg; #if TIER_ONE + #if _Py_TIER2 + // In JIT builds, we might have inserted an executor after this instruction, + // which breaks this super instruction. + DEOPT_IF(next_instr->op.code != STORE_FAST); + #endif assert(next_instr->op.code == STORE_FAST); next_oparg = next_instr->op.arg; #else diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index 42cca042022fc1..6d98e5e3f4c93f 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -5065,6 +5065,16 @@ assert(PyUnicode_CheckExact(PyStackRef_AsPyObjectBorrow(right))); int next_oparg; #if TIER_ONE + #if _Py_TIER2 + + if (next_instr->op.code != STORE_FAST) { + UOP_STAT_INC(uopcode, miss); + _tos_cache1 = right; + _tos_cache0 = left; + SET_CURRENT_CACHED_VALUES(2); + JUMP_TO_JUMP_TARGET(); + } + #endif assert(next_instr->op.code == STORE_FAST); next_oparg = next_instr->op.arg; #else diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index b9b493130d732f..43f4db88ec3107 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -413,6 +413,13 @@ assert(PyUnicode_CheckExact(PyStackRef_AsPyObjectBorrow(right))); int next_oparg; #if TIER_ONE + #if _Py_TIER2 + if (next_instr->op.code != STORE_FAST) { + UPDATE_MISS_STATS(BINARY_OP); + assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); + JUMP_TO_PREDICTED(BINARY_OP); + } + #endif assert(next_instr->op.code == STORE_FAST); next_oparg = next_instr->op.arg; #else From 7f38ff5017af71469f28e0cd0b06121154e11227 Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Thu, 15 Jan 2026 22:18:26 +0000 Subject: [PATCH 2/4] =?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-01-15-22-18-19.gh-issue-143820.eRhozc.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2026-01-15-22-18-19.gh-issue-143820.eRhozc.rst diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-01-15-22-18-19.gh-issue-143820.eRhozc.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-01-15-22-18-19.gh-issue-143820.eRhozc.rst new file mode 100644 index 00000000000000..e8755c73233abf --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-01-15-22-18-19.gh-issue-143820.eRhozc.rst @@ -0,0 +1 @@ +Fix a bug in JIT builds where executors can be inserted after certain super instructions. From 8ff98964cec1e00d6acdb850e44f7516d550d28e Mon Sep 17 00:00:00 2001 From: Ken Jin Date: Tue, 20 Jan 2026 18:30:11 +0000 Subject: [PATCH 3/4] Address review --- Include/internal/pycore_opcode_metadata.h | 2 +- Include/internal/pycore_uop_metadata.h | 2 +- Lib/test/test_capi/test_opt.py | 61 +++++++++++++++++++++++ Modules/_testinternalcapi/test_cases.c.h | 9 +++- Python/bytecodes.c | 13 +++-- Python/executor_cases.c.h | 14 +++--- Python/generated_cases.c.h | 12 ++--- 7 files changed, 91 insertions(+), 22 deletions(-) diff --git a/Include/internal/pycore_opcode_metadata.h b/Include/internal/pycore_opcode_metadata.h index ce6324d0a8e0b9..77b2dc6200653d 100644 --- a/Include/internal/pycore_opcode_metadata.h +++ b/Include/internal/pycore_opcode_metadata.h @@ -1095,7 +1095,7 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[267] = { [BINARY_OP_ADD_INT] = { true, INSTR_FMT_IXC0000, HAS_EXIT_FLAG }, [BINARY_OP_ADD_UNICODE] = { true, INSTR_FMT_IXC0000, HAS_EXIT_FLAG | HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG }, [BINARY_OP_EXTEND] = { true, INSTR_FMT_IXC0000, HAS_DEOPT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, - [BINARY_OP_INPLACE_ADD_UNICODE] = { true, INSTR_FMT_IXC0000, HAS_LOCAL_FLAG | HAS_DEOPT_FLAG | HAS_EXIT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, + [BINARY_OP_INPLACE_ADD_UNICODE] = { true, INSTR_FMT_IBC0000, HAS_ARG_FLAG | HAS_LOCAL_FLAG | HAS_DEOPT_FLAG | HAS_EXIT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [BINARY_OP_MULTIPLY_FLOAT] = { true, INSTR_FMT_IXC0000, HAS_EXIT_FLAG | HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG }, [BINARY_OP_MULTIPLY_INT] = { true, INSTR_FMT_IXC0000, HAS_EXIT_FLAG }, [BINARY_OP_SUBSCR_DICT] = { true, INSTR_FMT_IXC0000, HAS_EXIT_FLAG | HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG }, diff --git a/Include/internal/pycore_uop_metadata.h b/Include/internal/pycore_uop_metadata.h index 6398448d5faece..ca64c4284043bb 100644 --- a/Include/internal/pycore_uop_metadata.h +++ b/Include/internal/pycore_uop_metadata.h @@ -113,7 +113,7 @@ const uint32_t _PyUop_Flags[MAX_UOP_ID+1] = { [_BINARY_OP_ADD_FLOAT] = HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_PURE_FLAG, [_BINARY_OP_SUBTRACT_FLOAT] = HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_PURE_FLAG, [_BINARY_OP_ADD_UNICODE] = HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_PURE_FLAG, - [_BINARY_OP_INPLACE_ADD_UNICODE] = HAS_LOCAL_FLAG | HAS_DEOPT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, + [_BINARY_OP_INPLACE_ADD_UNICODE] = HAS_ARG_FLAG | HAS_LOCAL_FLAG | HAS_DEOPT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, [_GUARD_BINARY_OP_EXTEND] = HAS_DEOPT_FLAG | HAS_ESCAPES_FLAG, [_BINARY_OP_EXTEND] = HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, [_BINARY_SLICE] = HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, diff --git a/Lib/test/test_capi/test_opt.py b/Lib/test/test_capi/test_opt.py index 79c7f530b8ae89..d79c0ca818f1d8 100644 --- a/Lib/test/test_capi/test_opt.py +++ b/Lib/test/test_capi/test_opt.py @@ -3792,6 +3792,67 @@ def __next__(self): """), PYTHON_JIT="1", PYTHON_JIT_STRESS="1") self.assertEqual(result[0].rc, 0, result) + def test_143820(self): + # https://github.com/python/cpython/issues/143358 + + result = script_helper.run_python_until_end('-c', textwrap.dedent(f""" + import sys + import random + + int_v1 = 981679 + int_v2 = -3791744241805517 + any_v3 = 939.217 + + def f1(): int_v1 ^ int_v1 + + for i_f1 in range(300): + f1() + + def f2(): + class Int(int): + def __index__(self):... + + inf = float('inf') + nzero = -0 + zero = 0.0 + dummy = 0 + print('', file=sys.stderr) + + def f_0_dc_6103(p): return p + 1 + def f_1_dc_6103(p): return f_0_dc_6103(p) + 1 + def f_2_dc_6103(p): return f_1_dc_6103(p) + 1 + def f_3_dc_6103(p): return f_2_dc_6103(p) + 1 + def f_4_dc_6103(p): return f_3_dc_6103(p) + 1 + def f_5_dc_6103(p): return f_4_dc_6103(p) + 1 + def f_6_dc_6103(p): return f_5_dc_6103(p) + 1 + def f_7_dc_6103(p): return f_6_dc_6103(p) + 1 + def f_8_dc_6103(p): return f_7_dc_6103(p) + 1 + def f_9_dc_6103(p): return f_8_dc_6103(p) + 1 + + if inf == inf: dummy += 1 + s = '' + try: + for _ in range(10): + s += '' + s += 'y' + except Exception: pass + int_v1 ^ int_v1 + int_v1 ^ int_v1 + int_v1 ^ int_v1 + int_v2 - int_v1 + int_v2 - int_v1 + int_v2 - int_v1 + int_v2 - int_v1 + int_v2 - int_v1 + not any_v3 + not any_v3 + not any_v3 + + for i_f2 in range(300): + f2() + """), PYTHON_JIT="1", PYTHON_JIT_STRESS="1") + self.assertEqual(result[0].rc, 0, result) + def global_identity(x): return x diff --git a/Modules/_testinternalcapi/test_cases.c.h b/Modules/_testinternalcapi/test_cases.c.h index c02d236fc3e8ac..78814a7830d7b3 100644 --- a/Modules/_testinternalcapi/test_cases.c.h +++ b/Modules/_testinternalcapi/test_cases.c.h @@ -416,8 +416,15 @@ assert(PyUnicode_CheckExact(PyStackRef_AsPyObjectBorrow(right))); int next_oparg; #if TIER_ONE - assert(next_instr->op.code == STORE_FAST); next_oparg = next_instr->op.arg; + #if _Py_TIER2 + + if (next_instr->op.code == ENTER_EXECUTOR) { + _PyExecutorObject *exec = _PyFrame_GetCode(frame)->co_executors->executors[next_oparg]; + next_oparg = exec->vm_data.oparg; + } + #endif + assert(next_instr->op.code == STORE_FAST || next_instr->op.code == ENTER_EXECUTOR); #else next_oparg = (int)CURRENT_OPERAND0_16(); #endif diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 60810de5786790..24e63972a1bd12 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -783,13 +783,16 @@ dummy_func( int next_oparg; #if TIER_ONE - #if _Py_TIER2 + next_oparg = next_instr->op.arg; +#if _Py_TIER2 // In JIT builds, we might have inserted an executor after this instruction, // which breaks this super instruction. - DEOPT_IF(next_instr->op.code != STORE_FAST); - #endif - assert(next_instr->op.code == STORE_FAST); - next_oparg = next_instr->op.arg; + if (next_instr->op.code == ENTER_EXECUTOR) { + _PyExecutorObject *exec = _PyFrame_GetCode(frame)->co_executors->executors[next_oparg]; + next_oparg = exec->vm_data.oparg; + } +#endif + assert(next_instr->op.code == STORE_FAST || next_instr->op.code == ENTER_EXECUTOR); #else next_oparg = (int)CURRENT_OPERAND0_16(); #endif diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index 4066edb6a80773..37efa24f0877da 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -5058,6 +5058,7 @@ _PyStackRef res; _PyStackRef _stack_item_0 = _tos_cache0; _PyStackRef _stack_item_1 = _tos_cache1; + oparg = CURRENT_OPARG(); right = _stack_item_1; left = _stack_item_0; PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); @@ -5065,18 +5066,15 @@ assert(PyUnicode_CheckExact(PyStackRef_AsPyObjectBorrow(right))); int next_oparg; #if TIER_ONE + next_oparg = next_instr->op.arg; #if _Py_TIER2 - if (next_instr->op.code != STORE_FAST) { - UOP_STAT_INC(uopcode, miss); - _tos_cache1 = right; - _tos_cache0 = left; - SET_CURRENT_CACHED_VALUES(2); - JUMP_TO_JUMP_TARGET(); + if (next_instr->op.code == ENTER_EXECUTOR) { + _PyExecutorObject *exec = _PyFrame_GetCode(frame)->co_executors->executors[next_oparg]; + next_oparg = exec->vm_data.oparg; } #endif - assert(next_instr->op.code == STORE_FAST); - next_oparg = next_instr->op.arg; + assert(next_instr->op.code == STORE_FAST || next_instr->op.code == ENTER_EXECUTOR); #else next_oparg = (int)CURRENT_OPERAND0_16(); #endif diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index ec68e676bfcb15..610d19145e3f19 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -416,15 +416,15 @@ assert(PyUnicode_CheckExact(PyStackRef_AsPyObjectBorrow(right))); int next_oparg; #if TIER_ONE + next_oparg = next_instr->op.arg; #if _Py_TIER2 - if (next_instr->op.code != STORE_FAST) { - UPDATE_MISS_STATS(BINARY_OP); - assert(_PyOpcode_Deopt[opcode] == (BINARY_OP)); - JUMP_TO_PREDICTED(BINARY_OP); + + if (next_instr->op.code == ENTER_EXECUTOR) { + _PyExecutorObject *exec = _PyFrame_GetCode(frame)->co_executors->executors[next_oparg]; + next_oparg = exec->vm_data.oparg; } #endif - assert(next_instr->op.code == STORE_FAST); - next_oparg = next_instr->op.arg; + assert(next_instr->op.code == STORE_FAST || next_instr->op.code == ENTER_EXECUTOR); #else next_oparg = (int)CURRENT_OPERAND0_16(); #endif From 400ee18b83c38c5267d0615e8de0dda24c93b9ea Mon Sep 17 00:00:00 2001 From: Ken Jin Date: Sat, 24 Jan 2026 09:50:21 +0000 Subject: [PATCH 4/4] Address review partially --- Include/internal/pycore_code.h | 2 +- Modules/_testinternalcapi/test_cases.c.h | 5 ++++- Python/bytecodes.c | 3 ++- Python/executor_cases.c.h | 14 +++++++++++++- Python/generated_cases.c.h | 5 ++++- 5 files changed, 24 insertions(+), 5 deletions(-) diff --git a/Include/internal/pycore_code.h b/Include/internal/pycore_code.h index bd3f4f38e5a1bd..81cbcaa8637072 100644 --- a/Include/internal/pycore_code.h +++ b/Include/internal/pycore_code.h @@ -529,7 +529,7 @@ typedef struct { PyAPI_FUNC(int) _Py_Instrument(PyCodeObject *co, PyInterpreterState *interp); -extern _Py_CODEUNIT _Py_GetBaseCodeUnit(PyCodeObject *code, int offset); +PyAPI_FUNC(_Py_CODEUNIT) _Py_GetBaseCodeUnit(PyCodeObject *code, int offset); extern int _PyInstruction_GetLength(PyCodeObject *code, int offset); diff --git a/Modules/_testinternalcapi/test_cases.c.h b/Modules/_testinternalcapi/test_cases.c.h index 78814a7830d7b3..0f9096bb08bb85 100644 --- a/Modules/_testinternalcapi/test_cases.c.h +++ b/Modules/_testinternalcapi/test_cases.c.h @@ -424,7 +424,10 @@ next_oparg = exec->vm_data.oparg; } #endif - assert(next_instr->op.code == STORE_FAST || next_instr->op.code == ENTER_EXECUTOR); + _PyFrame_SetStackPointer(frame, stack_pointer); + assert(_Py_GetBaseCodeUnit(_PyFrame_GetCode(frame), + next_instr - _PyFrame_GetBytecode(frame)).op.code == STORE_FAST); + stack_pointer = _PyFrame_GetStackPointer(frame); #else next_oparg = (int)CURRENT_OPERAND0_16(); #endif diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 24e63972a1bd12..f99e6f22b18fda 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -792,7 +792,8 @@ dummy_func( next_oparg = exec->vm_data.oparg; } #endif - assert(next_instr->op.code == STORE_FAST || next_instr->op.code == ENTER_EXECUTOR); + assert(_Py_GetBaseCodeUnit(_PyFrame_GetCode(frame), + next_instr - _PyFrame_GetBytecode(frame)).op.code == STORE_FAST); #else next_oparg = (int)CURRENT_OPERAND0_16(); #endif diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index 37efa24f0877da..ba2b2c69faf228 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -5074,9 +5074,17 @@ next_oparg = exec->vm_data.oparg; } #endif - assert(next_instr->op.code == STORE_FAST || next_instr->op.code == ENTER_EXECUTOR); + stack_pointer[0] = left; + stack_pointer[1] = right; + stack_pointer += 2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + _PyFrame_SetStackPointer(frame, stack_pointer); + assert(_Py_GetBaseCodeUnit(_PyFrame_GetCode(frame), + next_instr - _PyFrame_GetBytecode(frame)).op.code == STORE_FAST); + stack_pointer = _PyFrame_GetStackPointer(frame); #else next_oparg = (int)CURRENT_OPERAND0_16(); + stack_pointer += 2; #endif _PyStackRef *target_local = &GETLOCAL(next_oparg); assert(PyUnicode_CheckExact(left_o)); @@ -5085,6 +5093,8 @@ _tos_cache1 = right; _tos_cache0 = left; SET_CURRENT_CACHED_VALUES(2); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); JUMP_TO_JUMP_TARGET(); } STAT_INC(BINARY_OP, hit); @@ -5092,6 +5102,8 @@ PyObject *temp = PyStackRef_AsPyObjectSteal(*target_local); PyObject *right_o = PyStackRef_AsPyObjectSteal(right); PyStackRef_CLOSE_SPECIALIZED(left, _PyUnicode_ExactDealloc); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); PyUnicode_Append(&temp, right_o); _Py_DECREF_SPECIALIZED(right_o, _PyUnicode_ExactDealloc); diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 610d19145e3f19..884a3fd6fea5da 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -424,7 +424,10 @@ next_oparg = exec->vm_data.oparg; } #endif - assert(next_instr->op.code == STORE_FAST || next_instr->op.code == ENTER_EXECUTOR); + _PyFrame_SetStackPointer(frame, stack_pointer); + assert(_Py_GetBaseCodeUnit(_PyFrame_GetCode(frame), + next_instr - _PyFrame_GetBytecode(frame)).op.code == STORE_FAST); + stack_pointer = _PyFrame_GetStackPointer(frame); #else next_oparg = (int)CURRENT_OPERAND0_16(); #endif