From cbc3d9a608170221ad12d5b4cfefe3097104988b Mon Sep 17 00:00:00 2001 From: Ken Jin Date: Tue, 23 Dec 2025 23:03:21 +0000 Subject: [PATCH 1/4] Make CALL_LIST_APPEND a normal instruction, disable superinstructions in the JIT --- Include/internal/pycore_opcode_metadata.h | 2 +- Include/internal/pycore_tstate.h | 1 - Include/internal/pycore_uop_ids.h | 8 +-- Include/internal/pycore_uop_metadata.h | 24 ++++----- Python/bytecodes.c | 26 +++------ Python/executor_cases.c.h | 64 ++++++++++------------- Python/generated_cases.c.h | 23 +++----- Python/optimizer.c | 13 ----- Python/optimizer_bytecodes.c | 18 ++++++- Python/optimizer_cases.c.h | 57 ++++++-------------- Python/specialize.c | 6 +-- 11 files changed, 96 insertions(+), 146 deletions(-) diff --git a/Include/internal/pycore_opcode_metadata.h b/Include/internal/pycore_opcode_metadata.h index 351cf56355b7d0..f4441388fc8ba1 100644 --- a/Include/internal/pycore_opcode_metadata.h +++ b/Include/internal/pycore_opcode_metadata.h @@ -610,7 +610,7 @@ int _PyOpcode_num_pushed(int opcode, int oparg) { case CALL_LEN: return 1; case CALL_LIST_APPEND: - return 0; + return 1; case CALL_METHOD_DESCRIPTOR_FAST: return 1; case CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS: diff --git a/Include/internal/pycore_tstate.h b/Include/internal/pycore_tstate.h index a57f1f45c135a6..1a7ebb01403208 100644 --- a/Include/internal/pycore_tstate.h +++ b/Include/internal/pycore_tstate.h @@ -37,7 +37,6 @@ typedef struct _PyJitTracerInitialState { typedef struct _PyJitTracerPreviousState { bool dependencies_still_valid; - bool instr_is_super; int code_max_size; int code_curr_size; int instr_oparg; diff --git a/Include/internal/pycore_uop_ids.h b/Include/internal/pycore_uop_ids.h index 204210ff101efe..636d371bddb903 100644 --- a/Include/internal/pycore_uop_ids.h +++ b/Include/internal/pycore_uop_ids.h @@ -412,10 +412,10 @@ extern "C" { #define _CALL_ISINSTANCE_r31 605 #define _CALL_KW_NON_PY_r11 606 #define _CALL_LEN_r33 607 -#define _CALL_LIST_APPEND_r02 608 -#define _CALL_LIST_APPEND_r12 609 -#define _CALL_LIST_APPEND_r22 610 -#define _CALL_LIST_APPEND_r32 611 +#define _CALL_LIST_APPEND_r03 608 +#define _CALL_LIST_APPEND_r13 609 +#define _CALL_LIST_APPEND_r23 610 +#define _CALL_LIST_APPEND_r33 611 #define _CALL_METHOD_DESCRIPTOR_FAST_r01 612 #define _CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS_r01 613 #define _CALL_METHOD_DESCRIPTOR_NOARGS_r01 614 diff --git a/Include/internal/pycore_uop_metadata.h b/Include/internal/pycore_uop_metadata.h index f751f642b81ff8..9657cd41ef3bda 100644 --- a/Include/internal/pycore_uop_metadata.h +++ b/Include/internal/pycore_uop_metadata.h @@ -2629,10 +2629,10 @@ const _PyUopCachingInfo _PyUop_Caching[MAX_UOP_ID+1] = { [_CALL_LIST_APPEND] = { .best = { 0, 1, 2, 3 }, .entries = { - { 2, 0, _CALL_LIST_APPEND_r02 }, - { 2, 1, _CALL_LIST_APPEND_r12 }, - { 2, 2, _CALL_LIST_APPEND_r22 }, - { 2, 3, _CALL_LIST_APPEND_r32 }, + { 3, 0, _CALL_LIST_APPEND_r03 }, + { 3, 1, _CALL_LIST_APPEND_r13 }, + { 3, 2, _CALL_LIST_APPEND_r23 }, + { 3, 3, _CALL_LIST_APPEND_r33 }, }, }, [_CALL_METHOD_DESCRIPTOR_O] = { @@ -3743,10 +3743,10 @@ const uint16_t _PyUop_Uncached[MAX_UOP_REGS_ID+1] = { [_GUARD_CALLABLE_LIST_APPEND_r13] = _GUARD_CALLABLE_LIST_APPEND, [_GUARD_CALLABLE_LIST_APPEND_r23] = _GUARD_CALLABLE_LIST_APPEND, [_GUARD_CALLABLE_LIST_APPEND_r33] = _GUARD_CALLABLE_LIST_APPEND, - [_CALL_LIST_APPEND_r02] = _CALL_LIST_APPEND, - [_CALL_LIST_APPEND_r12] = _CALL_LIST_APPEND, - [_CALL_LIST_APPEND_r22] = _CALL_LIST_APPEND, - [_CALL_LIST_APPEND_r32] = _CALL_LIST_APPEND, + [_CALL_LIST_APPEND_r03] = _CALL_LIST_APPEND, + [_CALL_LIST_APPEND_r13] = _CALL_LIST_APPEND, + [_CALL_LIST_APPEND_r23] = _CALL_LIST_APPEND, + [_CALL_LIST_APPEND_r33] = _CALL_LIST_APPEND, [_CALL_METHOD_DESCRIPTOR_O_r01] = _CALL_METHOD_DESCRIPTOR_O, [_CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS_r01] = _CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS, [_CALL_METHOD_DESCRIPTOR_NOARGS_r01] = _CALL_METHOD_DESCRIPTOR_NOARGS, @@ -4015,10 +4015,10 @@ const char *const _PyOpcode_uop_name[MAX_UOP_REGS_ID+1] = { [_CALL_LEN] = "_CALL_LEN", [_CALL_LEN_r33] = "_CALL_LEN_r33", [_CALL_LIST_APPEND] = "_CALL_LIST_APPEND", - [_CALL_LIST_APPEND_r02] = "_CALL_LIST_APPEND_r02", - [_CALL_LIST_APPEND_r12] = "_CALL_LIST_APPEND_r12", - [_CALL_LIST_APPEND_r22] = "_CALL_LIST_APPEND_r22", - [_CALL_LIST_APPEND_r32] = "_CALL_LIST_APPEND_r32", + [_CALL_LIST_APPEND_r03] = "_CALL_LIST_APPEND_r03", + [_CALL_LIST_APPEND_r13] = "_CALL_LIST_APPEND_r13", + [_CALL_LIST_APPEND_r23] = "_CALL_LIST_APPEND_r23", + [_CALL_LIST_APPEND_r33] = "_CALL_LIST_APPEND_r33", [_CALL_METHOD_DESCRIPTOR_FAST] = "_CALL_METHOD_DESCRIPTOR_FAST", [_CALL_METHOD_DESCRIPTOR_FAST_r01] = "_CALL_METHOD_DESCRIPTOR_FAST_r01", [_CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS] = "_CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS", diff --git a/Python/bytecodes.c b/Python/bytecodes.c index d21c17d072c3f9..eb014981f215ef 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -4329,8 +4329,7 @@ dummy_func( DEOPT_IF(callable_o != interp->callable_cache.list_append); } - // This is secretly a super-instruction - op(_CALL_LIST_APPEND, (callable, self, arg -- c, s)) { + op(_CALL_LIST_APPEND, (callable, self, arg -- none, c, s)) { assert(oparg == 1); PyObject *self_o = PyStackRef_AsPyObjectBorrow(self); @@ -4343,13 +4342,9 @@ dummy_func( } c = callable; s = self; - INPUTS_DEAD(); - #if TIER_ONE - // Skip the following POP_TOP. This is done here in tier one, and - // during trace projection in tier two: - assert(next_instr->op.code == POP_TOP); - SKIP_OVER(1); - #endif + DEAD(callable); + DEAD(self); + none = PyStackRef_None; } op(_CALL_METHOD_DESCRIPTOR_O, (callable, self_or_null, args[oparg] -- res)) { @@ -5597,15 +5592,10 @@ dummy_func( // Super instructions. Instruction deopted. There's a mismatch in what the stack expects // in the optimizer. So we have to reflect in the trace correctly. _PyThreadStateImpl *_tstate = (_PyThreadStateImpl *)tstate; - if ((_tstate->jit_tracer_state.prev_state.instr->op.code == CALL_LIST_APPEND && - opcode == POP_TOP) || - (_tstate->jit_tracer_state.prev_state.instr->op.code == BINARY_OP_INPLACE_ADD_UNICODE && - opcode == STORE_FAST)) { - _tstate->jit_tracer_state.prev_state.instr_is_super = true; - } - else { - _tstate->jit_tracer_state.prev_state.instr = next_instr; - } + // JIT should have disabled super instructions, as we can + // do these optimizations ourselves in the JIT. + assert(opcode != BINARY_OP_INPLACE_ADD_UNICODE); + _tstate->jit_tracer_state.prev_state.instr = next_instr; PyObject *prev_code = PyStackRef_AsPyObjectBorrow(frame->f_executable); if (_tstate->jit_tracer_state.prev_state.instr_code != (PyCodeObject *)prev_code) { Py_SETREF(_tstate->jit_tracer_state.prev_state.instr_code, (PyCodeObject*)Py_NewRef((prev_code))); diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index f64747d6f27f2a..ea74d23b08fa6a 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -13910,12 +13910,13 @@ break; } - case _CALL_LIST_APPEND_r02: { + case _CALL_LIST_APPEND_r03: { CHECK_CURRENT_CACHED_VALUES(0); assert(WITHIN_STACK_BOUNDS_IGNORING_CACHE()); _PyStackRef arg; _PyStackRef self; _PyStackRef callable; + _PyStackRef none; _PyStackRef c; _PyStackRef s; oparg = CURRENT_OPARG(); @@ -13938,26 +13939,24 @@ } c = callable; s = self; - #if TIER_ONE - - assert(next_instr->op.code == POP_TOP); - SKIP_OVER(1); - #endif - _tos_cache1 = s; - _tos_cache0 = c; - SET_CURRENT_CACHED_VALUES(2); + none = PyStackRef_None; + _tos_cache2 = s; + _tos_cache1 = c; + _tos_cache0 = none; + SET_CURRENT_CACHED_VALUES(3); stack_pointer += -3; ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); assert(WITHIN_STACK_BOUNDS_IGNORING_CACHE()); break; } - case _CALL_LIST_APPEND_r12: { + case _CALL_LIST_APPEND_r13: { CHECK_CURRENT_CACHED_VALUES(1); assert(WITHIN_STACK_BOUNDS_IGNORING_CACHE()); _PyStackRef arg; _PyStackRef self; _PyStackRef callable; + _PyStackRef none; _PyStackRef c; _PyStackRef s; _PyStackRef _stack_item_0 = _tos_cache0; @@ -13984,26 +13983,24 @@ } c = callable; s = self; - #if TIER_ONE - - assert(next_instr->op.code == POP_TOP); - SKIP_OVER(1); - #endif - _tos_cache1 = s; - _tos_cache0 = c; - SET_CURRENT_CACHED_VALUES(2); + none = PyStackRef_None; + _tos_cache2 = s; + _tos_cache1 = c; + _tos_cache0 = none; + SET_CURRENT_CACHED_VALUES(3); stack_pointer += -2; ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); assert(WITHIN_STACK_BOUNDS_IGNORING_CACHE()); break; } - case _CALL_LIST_APPEND_r22: { + case _CALL_LIST_APPEND_r23: { CHECK_CURRENT_CACHED_VALUES(2); assert(WITHIN_STACK_BOUNDS_IGNORING_CACHE()); _PyStackRef arg; _PyStackRef self; _PyStackRef callable; + _PyStackRef none; _PyStackRef c; _PyStackRef s; _PyStackRef _stack_item_0 = _tos_cache0; @@ -14033,26 +14030,24 @@ } c = callable; s = self; - #if TIER_ONE - - assert(next_instr->op.code == POP_TOP); - SKIP_OVER(1); - #endif - _tos_cache1 = s; - _tos_cache0 = c; - SET_CURRENT_CACHED_VALUES(2); + none = PyStackRef_None; + _tos_cache2 = s; + _tos_cache1 = c; + _tos_cache0 = none; + SET_CURRENT_CACHED_VALUES(3); stack_pointer += -1; ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); assert(WITHIN_STACK_BOUNDS_IGNORING_CACHE()); break; } - case _CALL_LIST_APPEND_r32: { + case _CALL_LIST_APPEND_r33: { CHECK_CURRENT_CACHED_VALUES(3); assert(WITHIN_STACK_BOUNDS_IGNORING_CACHE()); _PyStackRef arg; _PyStackRef self; _PyStackRef callable; + _PyStackRef none; _PyStackRef c; _PyStackRef s; _PyStackRef _stack_item_0 = _tos_cache0; @@ -14085,14 +14080,11 @@ } c = callable; s = self; - #if TIER_ONE - - assert(next_instr->op.code == POP_TOP); - SKIP_OVER(1); - #endif - _tos_cache1 = s; - _tos_cache0 = c; - SET_CURRENT_CACHED_VALUES(2); + none = PyStackRef_None; + _tos_cache2 = s; + _tos_cache1 = c; + _tos_cache0 = none; + SET_CURRENT_CACHED_VALUES(3); assert(WITHIN_STACK_BOUNDS_IGNORING_CACHE()); break; } diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index baf199969de94e..9d689b97fafe56 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -3239,6 +3239,7 @@ _PyStackRef nos; _PyStackRef self; _PyStackRef arg; + _PyStackRef none; _PyStackRef c; _PyStackRef s; _PyStackRef value; @@ -3293,17 +3294,14 @@ } c = callable; s = self; - #if TIER_ONE - - assert(next_instr->op.code == POP_TOP); - SKIP_OVER(1); - #endif + none = PyStackRef_None; } // _POP_TOP { value = s; - stack_pointer[-3] = c; - stack_pointer += -2; + stack_pointer[-3] = none; + stack_pointer[-2] = c; + stack_pointer += -1; ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); PyStackRef_XCLOSE(value); @@ -11414,15 +11412,8 @@ DISPATCH(); } _PyThreadStateImpl *_tstate = (_PyThreadStateImpl *)tstate; - if ((_tstate->jit_tracer_state.prev_state.instr->op.code == CALL_LIST_APPEND && - opcode == POP_TOP) || - (_tstate->jit_tracer_state.prev_state.instr->op.code == BINARY_OP_INPLACE_ADD_UNICODE && - opcode == STORE_FAST)) { - _tstate->jit_tracer_state.prev_state.instr_is_super = true; - } - else { - _tstate->jit_tracer_state.prev_state.instr = next_instr; - } + assert(opcode != BINARY_OP_INPLACE_ADD_UNICODE); + _tstate->jit_tracer_state.prev_state.instr = next_instr; PyObject *prev_code = PyStackRef_AsPyObjectBorrow(frame->f_executable); if (_tstate->jit_tracer_state.prev_state.instr_code != (PyCodeObject *)prev_code) { _PyFrame_SetStackPointer(frame, stack_pointer); diff --git a/Python/optimizer.c b/Python/optimizer.c index 0f8ddb4ba558d3..463302eed6147c 100644 --- a/Python/optimizer.c +++ b/Python/optimizer.c @@ -703,12 +703,6 @@ _PyJit_translate_single_bytecode_to_trace( } #endif - // Skip over super instructions. - if (_tstate->jit_tracer_state.prev_state.instr_is_super) { - _tstate->jit_tracer_state.prev_state.instr_is_super = false; - return 1; - } - if (opcode == ENTER_EXECUTOR) { goto full; } @@ -957,12 +951,6 @@ _PyJit_translate_single_bytecode_to_trace( trace[trace_length - 1].operand1 = PyStackRef_IsNone(frame->f_executable) ? 2 : ((int)(frame->stackpointer - _PyFrame_Stackbase(frame))); break; } - if (uop == _BINARY_OP_INPLACE_ADD_UNICODE) { - assert(i + 1 == nuops); - _Py_CODEUNIT *next = target_instr + 1 + _PyOpcode_Caches[_PyOpcode_Deopt[opcode]]; - assert(next->op.code == STORE_FAST); - operand = next->op.arg; - } // All other instructions ADD_TO_TRACE(uop, oparg, operand, target); } @@ -1077,7 +1065,6 @@ _PyJit_TryInitializeTracing( _tstate->jit_tracer_state.prev_state.instr_frame = frame; _tstate->jit_tracer_state.prev_state.instr_oparg = oparg; _tstate->jit_tracer_state.prev_state.instr_stacklevel = curr_stackdepth; - _tstate->jit_tracer_state.prev_state.instr_is_super = false; assert(curr_instr->op.code == JUMP_BACKWARD_JIT || (exit != NULL)); _tstate->jit_tracer_state.initial_state.jump_backward_instr = curr_instr; diff --git a/Python/optimizer_bytecodes.c b/Python/optimizer_bytecodes.c index b40b597643dc94..7c833282974a19 100644 --- a/Python/optimizer_bytecodes.c +++ b/Python/optimizer_bytecodes.c @@ -299,8 +299,21 @@ dummy_func(void) { } op(_BINARY_OP_ADD_UNICODE, (left, right -- res, l, r)) { - REPLACE_OPCODE_IF_EVALUATES_PURE(left, right); res = sym_new_type(ctx, &PyUnicode_Type); + // Re-implement the BINARY_OP_INPLACE_ADD_UNICODE super-instruction. + if ((this_instr + 5)->opcode == _STORE_FAST) { + assert((this_instr + 1)->opcode == _POP_TOP_UNICODE); + assert((this_instr + 2)->opcode == _POP_TOP_UNICODE); + assert((this_instr + 3)->opcode == _CHECK_VALIDITY); + assert((this_instr + 4)->opcode == _SET_IP); + int local_slot = (this_instr + 5)->oparg; + if (PyJitRef_Unwrap(left) == PyJitRef_Unwrap(GETLOCAL(local_slot))) { + REPLACE_OP(this_instr, _BINARY_OP_INPLACE_ADD_UNICODE, oparg, local_slot); + REPLACE_OP(this_instr + 1, _NOP, 0, 0); + REPLACE_OP(this_instr + 2, _NOP, 0, 0); + REPLACE_OP(this_instr + 5, _NOP, 0, 0); + } + } l = left; r = right; } @@ -1041,10 +1054,11 @@ dummy_func(void) { sym_set_const(flag, Py_True); } - op(_CALL_LIST_APPEND, (callable, self, arg -- c, s)) { + op(_CALL_LIST_APPEND, (callable, self, arg -- none, c, s)) { (void)(arg); c = callable; s = self; + none = sym_new_const(ctx, Py_None); } op(_GUARD_IS_FALSE_POP, (flag -- )) { diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h index a17a5688847e07..891616bccf4c69 100644 --- a/Python/optimizer_cases.c.h +++ b/Python/optimizer_cases.c.h @@ -646,43 +646,20 @@ JitOptRef r; right = stack_pointer[-1]; left = stack_pointer[-2]; - if ( - sym_is_safe_const(ctx, left) && - sym_is_safe_const(ctx, right) - ) { - JitOptRef left_sym = left; - JitOptRef right_sym = right; - _PyStackRef left = sym_get_const_as_stackref(ctx, left_sym); - _PyStackRef right = sym_get_const_as_stackref(ctx, right_sym); - _PyStackRef res_stackref; - _PyStackRef l_stackref; - _PyStackRef r_stackref; - /* Start of uop copied from bytecodes for constant evaluation */ - PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); - PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); - assert(PyUnicode_CheckExact(left_o)); - assert(PyUnicode_CheckExact(right_o)); - STAT_INC(BINARY_OP, hit); - PyObject *res_o = PyUnicode_Concat(left_o, right_o); - res_stackref = PyStackRef_FromPyObjectSteal(res_o); - if (PyStackRef_IsNull(res)) { - JUMP_TO_LABEL(error); + res = sym_new_type(ctx, &PyUnicode_Type); + if ((this_instr + 5)->opcode == _STORE_FAST) { + assert((this_instr + 1)->opcode == _POP_TOP_UNICODE); + assert((this_instr + 2)->opcode == _POP_TOP_UNICODE); + assert((this_instr + 3)->opcode == _CHECK_VALIDITY); + assert((this_instr + 4)->opcode == _SET_IP); + int local_slot = (this_instr + 5)->oparg; + if (PyJitRef_Unwrap(left) == PyJitRef_Unwrap(GETLOCAL(local_slot))) { + REPLACE_OP(this_instr, _BINARY_OP_INPLACE_ADD_UNICODE, oparg, local_slot); + REPLACE_OP(this_instr + 1, _NOP, 0, 0); + REPLACE_OP(this_instr + 2, _NOP, 0, 0); + REPLACE_OP(this_instr + 5, _NOP, 0, 0); } - l_stackref = left; - r_stackref = right; - /* End of uop copied from bytecodes for constant evaluation */ - res = sym_new_const_steal(ctx, PyStackRef_AsPyObjectSteal(res_stackref)); - l = sym_new_const_steal(ctx, PyStackRef_AsPyObjectSteal(l_stackref)); - r = sym_new_const_steal(ctx, PyStackRef_AsPyObjectSteal(r_stackref)); - CHECK_STACK_BOUNDS(1); - stack_pointer[-2] = res; - stack_pointer[-1] = l; - stack_pointer[0] = r; - stack_pointer += 1; - ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); - break; } - res = sym_new_type(ctx, &PyUnicode_Type); l = left; r = right; CHECK_STACK_BOUNDS(1); @@ -2952,6 +2929,7 @@ JitOptRef arg; JitOptRef self; JitOptRef callable; + JitOptRef none; JitOptRef c; JitOptRef s; arg = stack_pointer[-1]; @@ -2960,11 +2938,10 @@ (void)(arg); c = callable; s = self; - CHECK_STACK_BOUNDS(-1); - stack_pointer[-3] = c; - stack_pointer[-2] = s; - stack_pointer += -1; - ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + none = sym_new_const(ctx, Py_None); + stack_pointer[-3] = none; + stack_pointer[-2] = c; + stack_pointer[-1] = s; break; } diff --git a/Python/specialize.c b/Python/specialize.c index 19433bc7a74319..135e6a7a042080 100644 --- a/Python/specialize.c +++ b/Python/specialize.c @@ -1623,10 +1623,8 @@ specialize_method_descriptor(PyMethodDescrObject *descr, PyObject *self_or_null, } PyInterpreterState *interp = _PyInterpreterState_GET(); PyObject *list_append = interp->callable_cache.list_append; - _Py_CODEUNIT next = instr[INLINE_CACHE_ENTRIES_CALL + 1]; - bool pop = (next.op.code == POP_TOP); int oparg = instr->op.arg; - if ((PyObject *)descr == list_append && oparg == 1 && pop) { + if ((PyObject *)descr == list_append && oparg == 1) { assert(self_or_null != NULL); if (PyList_CheckExact(self_or_null)) { specialize(instr, CALL_LIST_APPEND); @@ -2186,12 +2184,14 @@ _Py_Specialize_BinaryOp(_PyStackRef lhs_st, _PyStackRef rhs_st, _Py_CODEUNIT *in break; } if (PyUnicode_CheckExact(lhs)) { +#ifndef _Py_TIER2 // JIT doesn't support super instructions. _Py_CODEUNIT next = instr[INLINE_CACHE_ENTRIES_BINARY_OP + 1]; bool to_store = (next.op.code == STORE_FAST); if (to_store && PyStackRef_AsPyObjectBorrow(locals[next.op.arg]) == lhs) { specialize(instr, BINARY_OP_INPLACE_ADD_UNICODE); return; } +#endif specialize(instr, BINARY_OP_ADD_UNICODE); return; } From 9b66f09a008edfeb637584f5104e21d706a9f2c9 Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Tue, 23 Dec 2025 23:06:19 +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 --- .../2025-12-23-23-06-11.gh-issue-143092.6MISbb.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-12-23-23-06-11.gh-issue-143092.6MISbb.rst diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-12-23-23-06-11.gh-issue-143092.6MISbb.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-12-23-23-06-11.gh-issue-143092.6MISbb.rst new file mode 100644 index 00000000000000..dddfc56c8f7038 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-12-23-23-06-11.gh-issue-143092.6MISbb.rst @@ -0,0 +1 @@ +Fix a crash in the JIT when dealing with ``list.append(x)`` style code. From d60dceee6dab1d3f8e300ee1b520cbd6fa6c559e Mon Sep 17 00:00:00 2001 From: Ken Jin Date: Tue, 23 Dec 2025 23:10:38 +0000 Subject: [PATCH 3/4] add test --- Lib/test/test_capi/test_opt.py | 41 ++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/Lib/test/test_capi/test_opt.py b/Lib/test/test_capi/test_opt.py index 16288a447e20fe..b76d5e246f16fa 100644 --- a/Lib/test/test_capi/test_opt.py +++ b/Lib/test/test_capi/test_opt.py @@ -3133,6 +3133,47 @@ def f1(): """), PYTHON_JIT="1") self.assertEqual(result[0].rc, 0, result) + def test_143092(self): + def f1(): + a = "a" + for i in range(50): + x = a[i % len(a)] + + s = "" + for _ in range(10): + s += "" + + class A: ... + class B: ... + + match s: + case int(): ... + case str(): ... + case dict(): ... + + ( + u0, + *u1, + u2, + u4, + u5, + u6, + u7, + u8, + u9, u10, u11, + u12, u13, u14, u15, u16, u17, u18, u19, u20, u21, u22, u23, u24, u25, u26, u27, u28, u29, + ) = [None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, + None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, + None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, + None, None, None, None, None, None, None, None,] + + s = "" + for _ in range(10): + s += "" + s += "" + + for i in range(TIER2_THRESHOLD * 10): + f1() def global_identity(x): return x From 880b48d720a0c3eca5b9c4f51a694cd927fec0af Mon Sep 17 00:00:00 2001 From: Ken Jin Date: Wed, 24 Dec 2025 12:46:21 +0000 Subject: [PATCH 4/4] Remove _BINARY_OP_INPLACE_ADD_UNICODE super instruction --- Include/internal/pycore_opcode_metadata.h | 2 +- Include/internal/pycore_uop_ids.h | 2 +- Include/internal/pycore_uop_metadata.h | 6 ++-- Lib/_opcode_metadata.py | 2 ++ Python/bytecodes.c | 34 +++++++++-------------- Python/executor_cases.c.h | 33 ++++++++++++---------- Python/generated_cases.c.h | 27 ++++++++---------- Python/optimizer.c | 6 ++++ Python/optimizer_bytecodes.c | 20 ++----------- Python/optimizer_cases.c.h | 26 ++++++----------- Python/specialize.c | 2 -- 11 files changed, 65 insertions(+), 95 deletions(-) diff --git a/Include/internal/pycore_opcode_metadata.h b/Include/internal/pycore_opcode_metadata.h index f4441388fc8ba1..c8665996462d4c 100644 --- a/Include/internal/pycore_opcode_metadata.h +++ b/Include/internal/pycore_opcode_metadata.h @@ -534,7 +534,7 @@ int _PyOpcode_num_pushed(int opcode, int oparg) { case BINARY_OP_EXTEND: return 1; case BINARY_OP_INPLACE_ADD_UNICODE: - return 0; + return 1; case BINARY_OP_MULTIPLY_FLOAT: return 1; case BINARY_OP_MULTIPLY_INT: diff --git a/Include/internal/pycore_uop_ids.h b/Include/internal/pycore_uop_ids.h index 636d371bddb903..9e36a0efc0d20c 100644 --- a/Include/internal/pycore_uop_ids.h +++ b/Include/internal/pycore_uop_ids.h @@ -371,7 +371,7 @@ extern "C" { #define _BINARY_OP_ADD_UNICODE_r13 564 #define _BINARY_OP_ADD_UNICODE_r23 565 #define _BINARY_OP_EXTEND_r21 566 -#define _BINARY_OP_INPLACE_ADD_UNICODE_r20 567 +#define _BINARY_OP_INPLACE_ADD_UNICODE_r21 567 #define _BINARY_OP_MULTIPLY_FLOAT_r03 568 #define _BINARY_OP_MULTIPLY_FLOAT_r13 569 #define _BINARY_OP_MULTIPLY_FLOAT_r23 570 diff --git a/Include/internal/pycore_uop_metadata.h b/Include/internal/pycore_uop_metadata.h index 9657cd41ef3bda..9f1fcb402eb2d1 100644 --- a/Include/internal/pycore_uop_metadata.h +++ b/Include/internal/pycore_uop_metadata.h @@ -1065,7 +1065,7 @@ const _PyUopCachingInfo _PyUop_Caching[MAX_UOP_ID+1] = { .entries = { { -1, -1, -1 }, { -1, -1, -1 }, - { 0, 2, _BINARY_OP_INPLACE_ADD_UNICODE_r20 }, + { 1, 2, _BINARY_OP_INPLACE_ADD_UNICODE_r21 }, { -1, -1, -1 }, }, }, @@ -3437,7 +3437,7 @@ const uint16_t _PyUop_Uncached[MAX_UOP_REGS_ID+1] = { [_BINARY_OP_ADD_UNICODE_r03] = _BINARY_OP_ADD_UNICODE, [_BINARY_OP_ADD_UNICODE_r13] = _BINARY_OP_ADD_UNICODE, [_BINARY_OP_ADD_UNICODE_r23] = _BINARY_OP_ADD_UNICODE, - [_BINARY_OP_INPLACE_ADD_UNICODE_r20] = _BINARY_OP_INPLACE_ADD_UNICODE, + [_BINARY_OP_INPLACE_ADD_UNICODE_r21] = _BINARY_OP_INPLACE_ADD_UNICODE, [_GUARD_BINARY_OP_EXTEND_r22] = _GUARD_BINARY_OP_EXTEND, [_BINARY_OP_EXTEND_r21] = _BINARY_OP_EXTEND, [_BINARY_SLICE_r31] = _BINARY_SLICE, @@ -3944,7 +3944,7 @@ const char *const _PyOpcode_uop_name[MAX_UOP_REGS_ID+1] = { [_BINARY_OP_EXTEND] = "_BINARY_OP_EXTEND", [_BINARY_OP_EXTEND_r21] = "_BINARY_OP_EXTEND_r21", [_BINARY_OP_INPLACE_ADD_UNICODE] = "_BINARY_OP_INPLACE_ADD_UNICODE", - [_BINARY_OP_INPLACE_ADD_UNICODE_r20] = "_BINARY_OP_INPLACE_ADD_UNICODE_r20", + [_BINARY_OP_INPLACE_ADD_UNICODE_r21] = "_BINARY_OP_INPLACE_ADD_UNICODE_r21", [_BINARY_OP_MULTIPLY_FLOAT] = "_BINARY_OP_MULTIPLY_FLOAT", [_BINARY_OP_MULTIPLY_FLOAT_r03] = "_BINARY_OP_MULTIPLY_FLOAT_r03", [_BINARY_OP_MULTIPLY_FLOAT_r13] = "_BINARY_OP_MULTIPLY_FLOAT_r13", diff --git a/Lib/_opcode_metadata.py b/Lib/_opcode_metadata.py index e681cb17e43e04..b3dce93710a23f 100644 --- a/Lib/_opcode_metadata.py +++ b/Lib/_opcode_metadata.py @@ -28,6 +28,7 @@ "BINARY_OP_SUBSCR_STR_INT", "BINARY_OP_SUBSCR_DICT", "BINARY_OP_SUBSCR_GETITEM", + "BINARY_OP_INPLACE_ADD_UNICODE", "BINARY_OP_EXTEND", "BINARY_OP_INPLACE_ADD_UNICODE", ], @@ -125,6 +126,7 @@ 'BINARY_OP_ADD_UNICODE': 131, 'BINARY_OP_EXTEND': 132, 'BINARY_OP_INPLACE_ADD_UNICODE': 3, + 'BINARY_OP_INPLACE_ADD_UNICODE': 3, 'BINARY_OP_MULTIPLY_FLOAT': 133, 'BINARY_OP_MULTIPLY_INT': 134, 'BINARY_OP_SUBSCR_DICT': 135, diff --git a/Python/bytecodes.c b/Python/bytecodes.c index eb014981f215ef..151c3c44c4c1fc 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -588,7 +588,7 @@ dummy_func( BINARY_OP_SUBSCR_STR_INT, BINARY_OP_SUBSCR_DICT, BINARY_OP_SUBSCR_GETITEM, - // BINARY_OP_INPLACE_ADD_UNICODE, // See comments at that opcode. + BINARY_OP_INPLACE_ADD_UNICODE, BINARY_OP_EXTEND, }; @@ -762,13 +762,10 @@ dummy_func( macro(BINARY_OP_ADD_UNICODE) = _GUARD_TOS_UNICODE + _GUARD_NOS_UNICODE + unused/5 + _BINARY_OP_ADD_UNICODE + _POP_TOP_UNICODE + _POP_TOP_UNICODE; - // This is a subtle one. It's a super-instruction for - // BINARY_OP_ADD_UNICODE followed by STORE_FAST - // where the store goes into the left argument. - // So the inputs are the same as for all BINARY_OP - // specializations, but there is no output. - // At the end we just skip over the STORE_FAST. - op(_BINARY_OP_INPLACE_ADD_UNICODE, (left, right --)) { + // This is a subtle one. We write NULL to the local + // of the following STORE_FAST and leave the result for STORE_FAST + // later to store. + op(_BINARY_OP_INPLACE_ADD_UNICODE, (left, right -- res)) { PyObject *left_o = PyStackRef_AsPyObjectBorrow(left); assert(PyUnicode_CheckExact(left_o)); assert(PyUnicode_CheckExact(PyStackRef_AsPyObjectBorrow(right))); @@ -796,20 +793,16 @@ dummy_func( * that the string is safe to mutate. */ assert(Py_REFCNT(left_o) >= 2 || !PyStackRef_IsHeapSafe(left)); - PyStackRef_CLOSE_SPECIALIZED(left, _PyUnicode_ExactDealloc); - DEAD(left); PyObject *temp = PyStackRef_AsPyObjectSteal(*target_local); - PyObject *right_o = PyStackRef_AsPyObjectSteal(right); + PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); PyUnicode_Append(&temp, right_o); - *target_local = PyStackRef_FromPyObjectSteal(temp); - Py_DECREF(right_o); - ERROR_IF(PyStackRef_IsNull(*target_local)); - #if TIER_ONE - // The STORE_FAST is already done. This is done here in tier one, - // and during trace projection in tier two: - assert(next_instr->op.code == STORE_FAST); - SKIP_OVER(1); - #endif + PyStackRef_CLOSE_SPECIALIZED(right, _PyUnicode_ExactDealloc); + DEAD(right); + PyStackRef_CLOSE_SPECIALIZED(left, _PyUnicode_ExactDealloc); + DEAD(left); + ERROR_IF(temp == NULL); + res = PyStackRef_FromPyObjectSteal(temp); + *target_local = PyStackRef_NULL; } op(_GUARD_BINARY_OP_EXTEND, (descr/4, left, right -- left, right)) { @@ -5594,7 +5587,6 @@ dummy_func( _PyThreadStateImpl *_tstate = (_PyThreadStateImpl *)tstate; // JIT should have disabled super instructions, as we can // do these optimizations ourselves in the JIT. - assert(opcode != BINARY_OP_INPLACE_ADD_UNICODE); _tstate->jit_tracer_state.prev_state.instr = next_instr; PyObject *prev_code = PyStackRef_AsPyObjectBorrow(frame->f_executable); if (_tstate->jit_tracer_state.prev_state.instr_code != (PyCodeObject *)prev_code) { diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index ea74d23b08fa6a..56e84dc47d7384 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -4291,11 +4291,12 @@ break; } - case _BINARY_OP_INPLACE_ADD_UNICODE_r20: { + case _BINARY_OP_INPLACE_ADD_UNICODE_r21: { CHECK_CURRENT_CACHED_VALUES(2); assert(WITHIN_STACK_BOUNDS_IGNORING_CACHE()); _PyStackRef right; _PyStackRef left; + _PyStackRef res; _PyStackRef _stack_item_0 = _tos_cache0; _PyStackRef _stack_item_1 = _tos_cache1; right = _stack_item_1; @@ -4321,29 +4322,31 @@ } STAT_INC(BINARY_OP, hit); assert(Py_REFCNT(left_o) >= 2 || !PyStackRef_IsHeapSafe(left)); - PyStackRef_CLOSE_SPECIALIZED(left, _PyUnicode_ExactDealloc); PyObject *temp = PyStackRef_AsPyObjectSteal(*target_local); - PyObject *right_o = PyStackRef_AsPyObjectSteal(right); + PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); + stack_pointer[0] = left; + stack_pointer[1] = right; + stack_pointer += 2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); PyUnicode_Append(&temp, right_o); stack_pointer = _PyFrame_GetStackPointer(frame); - *target_local = PyStackRef_FromPyObjectSteal(temp); - _PyFrame_SetStackPointer(frame, stack_pointer); - Py_DECREF(right_o); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (PyStackRef_IsNull(*target_local)) { + PyStackRef_CLOSE_SPECIALIZED(right, _PyUnicode_ExactDealloc); + PyStackRef_CLOSE_SPECIALIZED(left, _PyUnicode_ExactDealloc); + if (temp == NULL) { + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); SET_CURRENT_CACHED_VALUES(0); JUMP_TO_ERROR(); } - #if TIER_ONE - - assert(next_instr->op.code == STORE_FAST); - SKIP_OVER(1); - #endif - _tos_cache0 = PyStackRef_ZERO_BITS; + res = PyStackRef_FromPyObjectSteal(temp); + *target_local = PyStackRef_NULL; + _tos_cache0 = res; _tos_cache1 = PyStackRef_ZERO_BITS; _tos_cache2 = PyStackRef_ZERO_BITS; - SET_CURRENT_CACHED_VALUES(0); + SET_CURRENT_CACHED_VALUES(1); + stack_pointer += -2; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); assert(WITHIN_STACK_BOUNDS_IGNORING_CACHE()); break; } diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 9d689b97fafe56..ae1b14c65f3ae4 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -382,6 +382,7 @@ _PyStackRef nos; _PyStackRef left; _PyStackRef right; + _PyStackRef res; // _GUARD_TOS_UNICODE { value = stack_pointer[-1]; @@ -426,27 +427,22 @@ } STAT_INC(BINARY_OP, hit); assert(Py_REFCNT(left_o) >= 2 || !PyStackRef_IsHeapSafe(left)); - PyStackRef_CLOSE_SPECIALIZED(left, _PyUnicode_ExactDealloc); PyObject *temp = PyStackRef_AsPyObjectSteal(*target_local); - PyObject *right_o = PyStackRef_AsPyObjectSteal(right); - stack_pointer += -2; - ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + PyObject *right_o = PyStackRef_AsPyObjectBorrow(right); _PyFrame_SetStackPointer(frame, stack_pointer); PyUnicode_Append(&temp, right_o); stack_pointer = _PyFrame_GetStackPointer(frame); - *target_local = PyStackRef_FromPyObjectSteal(temp); - _PyFrame_SetStackPointer(frame, stack_pointer); - Py_DECREF(right_o); - stack_pointer = _PyFrame_GetStackPointer(frame); - if (PyStackRef_IsNull(*target_local)) { - JUMP_TO_LABEL(error); + PyStackRef_CLOSE_SPECIALIZED(right, _PyUnicode_ExactDealloc); + PyStackRef_CLOSE_SPECIALIZED(left, _PyUnicode_ExactDealloc); + if (temp == NULL) { + JUMP_TO_LABEL(pop_2_error); } - #if TIER_ONE - - assert(next_instr->op.code == STORE_FAST); - SKIP_OVER(1); - #endif + res = PyStackRef_FromPyObjectSteal(temp); + *target_local = PyStackRef_NULL; } + stack_pointer[-2] = res; + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } @@ -11412,7 +11408,6 @@ DISPATCH(); } _PyThreadStateImpl *_tstate = (_PyThreadStateImpl *)tstate; - assert(opcode != BINARY_OP_INPLACE_ADD_UNICODE); _tstate->jit_tracer_state.prev_state.instr = next_instr; PyObject *prev_code = PyStackRef_AsPyObjectBorrow(frame->f_executable); if (_tstate->jit_tracer_state.prev_state.instr_code != (PyCodeObject *)prev_code) { diff --git a/Python/optimizer.c b/Python/optimizer.c index 463302eed6147c..5e97f20f869efd 100644 --- a/Python/optimizer.c +++ b/Python/optimizer.c @@ -951,6 +951,12 @@ _PyJit_translate_single_bytecode_to_trace( trace[trace_length - 1].operand1 = PyStackRef_IsNone(frame->f_executable) ? 2 : ((int)(frame->stackpointer - _PyFrame_Stackbase(frame))); break; } + if (uop == _BINARY_OP_INPLACE_ADD_UNICODE) { + assert(i + 1 == nuops); + _Py_CODEUNIT *next = target_instr + 1 + _PyOpcode_Caches[_PyOpcode_Deopt[opcode]]; + assert(next->op.code == STORE_FAST); + operand = next->op.arg; + } // All other instructions ADD_TO_TRACE(uop, oparg, operand, target); } diff --git a/Python/optimizer_bytecodes.c b/Python/optimizer_bytecodes.c index 7c833282974a19..7da09722ff188c 100644 --- a/Python/optimizer_bytecodes.c +++ b/Python/optimizer_bytecodes.c @@ -300,26 +300,11 @@ dummy_func(void) { op(_BINARY_OP_ADD_UNICODE, (left, right -- res, l, r)) { res = sym_new_type(ctx, &PyUnicode_Type); - // Re-implement the BINARY_OP_INPLACE_ADD_UNICODE super-instruction. - if ((this_instr + 5)->opcode == _STORE_FAST) { - assert((this_instr + 1)->opcode == _POP_TOP_UNICODE); - assert((this_instr + 2)->opcode == _POP_TOP_UNICODE); - assert((this_instr + 3)->opcode == _CHECK_VALIDITY); - assert((this_instr + 4)->opcode == _SET_IP); - int local_slot = (this_instr + 5)->oparg; - if (PyJitRef_Unwrap(left) == PyJitRef_Unwrap(GETLOCAL(local_slot))) { - REPLACE_OP(this_instr, _BINARY_OP_INPLACE_ADD_UNICODE, oparg, local_slot); - REPLACE_OP(this_instr + 1, _NOP, 0, 0); - REPLACE_OP(this_instr + 2, _NOP, 0, 0); - REPLACE_OP(this_instr + 5, _NOP, 0, 0); - } - } l = left; r = right; } - op(_BINARY_OP_INPLACE_ADD_UNICODE, (left, right --)) { - JitOptRef res; + op(_BINARY_OP_INPLACE_ADD_UNICODE, (left, right -- res)) { if (sym_is_const(ctx, left) && sym_is_const(ctx, right)) { assert(PyUnicode_CheckExact(sym_get_const(ctx, left))); assert(PyUnicode_CheckExact(sym_get_const(ctx, right))); @@ -333,8 +318,7 @@ dummy_func(void) { else { res = sym_new_type(ctx, &PyUnicode_Type); } - // _STORE_FAST: - GETLOCAL(this_instr->operand0) = res; + GETLOCAL(this_instr->operand0) = sym_new_null(ctx); } op(_BINARY_OP_SUBSCR_INIT_CALL, (container, sub, getitem -- new_frame)) { diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h index 891616bccf4c69..29d335933901b6 100644 --- a/Python/optimizer_cases.c.h +++ b/Python/optimizer_cases.c.h @@ -647,19 +647,6 @@ right = stack_pointer[-1]; left = stack_pointer[-2]; res = sym_new_type(ctx, &PyUnicode_Type); - if ((this_instr + 5)->opcode == _STORE_FAST) { - assert((this_instr + 1)->opcode == _POP_TOP_UNICODE); - assert((this_instr + 2)->opcode == _POP_TOP_UNICODE); - assert((this_instr + 3)->opcode == _CHECK_VALIDITY); - assert((this_instr + 4)->opcode == _SET_IP); - int local_slot = (this_instr + 5)->oparg; - if (PyJitRef_Unwrap(left) == PyJitRef_Unwrap(GETLOCAL(local_slot))) { - REPLACE_OP(this_instr, _BINARY_OP_INPLACE_ADD_UNICODE, oparg, local_slot); - REPLACE_OP(this_instr + 1, _NOP, 0, 0); - REPLACE_OP(this_instr + 2, _NOP, 0, 0); - REPLACE_OP(this_instr + 5, _NOP, 0, 0); - } - } l = left; r = right; CHECK_STACK_BOUNDS(1); @@ -674,9 +661,9 @@ case _BINARY_OP_INPLACE_ADD_UNICODE: { JitOptRef right; JitOptRef left; + JitOptRef res; right = stack_pointer[-1]; left = stack_pointer[-2]; - JitOptRef res; if (sym_is_const(ctx, left) && sym_is_const(ctx, right)) { assert(PyUnicode_CheckExact(sym_get_const(ctx, left))); assert(PyUnicode_CheckExact(sym_get_const(ctx, right))); @@ -685,15 +672,18 @@ goto error; } res = sym_new_const(ctx, temp); + CHECK_STACK_BOUNDS(-1); + stack_pointer[-2] = res; + stack_pointer += -1; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); Py_DECREF(temp); } else { res = sym_new_type(ctx, &PyUnicode_Type); + stack_pointer += -1; } - GETLOCAL(this_instr->operand0) = res; - CHECK_STACK_BOUNDS(-2); - stack_pointer += -2; - ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + GETLOCAL(this_instr->operand0) = sym_new_null(ctx); + stack_pointer[-1] = res; break; } diff --git a/Python/specialize.c b/Python/specialize.c index 135e6a7a042080..e67078afdd9df3 100644 --- a/Python/specialize.c +++ b/Python/specialize.c @@ -2184,14 +2184,12 @@ _Py_Specialize_BinaryOp(_PyStackRef lhs_st, _PyStackRef rhs_st, _Py_CODEUNIT *in break; } if (PyUnicode_CheckExact(lhs)) { -#ifndef _Py_TIER2 // JIT doesn't support super instructions. _Py_CODEUNIT next = instr[INLINE_CACHE_ENTRIES_BINARY_OP + 1]; bool to_store = (next.op.code == STORE_FAST); if (to_store && PyStackRef_AsPyObjectBorrow(locals[next.op.arg]) == lhs) { specialize(instr, BINARY_OP_INPLACE_ADD_UNICODE); return; } -#endif specialize(instr, BINARY_OP_ADD_UNICODE); return; }