From: Donghee Na Date: Thu, 11 Dec 2025 15:24:34 +0000 (+0900) Subject: gh-134584: Eliminate redundant refcounting from ``_CALL_LEN`` (gh-136104) X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=a27538540ed12c91f75361ae78d1117c74667af7;p=thirdparty%2FPython%2Fcpython.git gh-134584: Eliminate redundant refcounting from ``_CALL_LEN`` (gh-136104) --- diff --git a/Include/internal/pycore_opcode_metadata.h b/Include/internal/pycore_opcode_metadata.h index 3ec0a8634148..35659de22b79 100644 --- a/Include/internal/pycore_opcode_metadata.h +++ b/Include/internal/pycore_opcode_metadata.h @@ -1366,7 +1366,7 @@ _PyOpcode_macro_expansion[256] = { [CALL_KW_BOUND_METHOD] = { .nuops = 6, .uops = { { _CHECK_PEP_523, OPARG_SIMPLE, 1 }, { _CHECK_METHOD_VERSION_KW, 2, 1 }, { _EXPAND_METHOD_KW, OPARG_SIMPLE, 3 }, { _PY_FRAME_KW, OPARG_SIMPLE, 3 }, { _SAVE_RETURN_OFFSET, OPARG_SAVE_RETURN_OFFSET, 3 }, { _PUSH_FRAME, OPARG_SIMPLE, 3 } } }, [CALL_KW_NON_PY] = { .nuops = 3, .uops = { { _CHECK_IS_NOT_PY_CALLABLE_KW, OPARG_SIMPLE, 3 }, { _CALL_KW_NON_PY, OPARG_SIMPLE, 3 }, { _CHECK_PERIODIC_AT_END, OPARG_REPLACED, 3 } } }, [CALL_KW_PY] = { .nuops = 6, .uops = { { _CHECK_PEP_523, OPARG_SIMPLE, 1 }, { _CHECK_FUNCTION_VERSION_KW, 2, 1 }, { _CHECK_RECURSION_REMAINING, OPARG_SIMPLE, 3 }, { _PY_FRAME_KW, OPARG_SIMPLE, 3 }, { _SAVE_RETURN_OFFSET, OPARG_SAVE_RETURN_OFFSET, 3 }, { _PUSH_FRAME, OPARG_SIMPLE, 3 } } }, - [CALL_LEN] = { .nuops = 3, .uops = { { _GUARD_NOS_NULL, OPARG_SIMPLE, 3 }, { _GUARD_CALLABLE_LEN, OPARG_SIMPLE, 3 }, { _CALL_LEN, OPARG_SIMPLE, 3 } } }, + [CALL_LEN] = { .nuops = 5, .uops = { { _GUARD_NOS_NULL, OPARG_SIMPLE, 3 }, { _GUARD_CALLABLE_LEN, OPARG_SIMPLE, 3 }, { _CALL_LEN, OPARG_SIMPLE, 3 }, { _POP_TOP, OPARG_SIMPLE, 3 }, { _POP_TOP, OPARG_SIMPLE, 3 } } }, [CALL_LIST_APPEND] = { .nuops = 4, .uops = { { _GUARD_CALLABLE_LIST_APPEND, OPARG_SIMPLE, 3 }, { _GUARD_NOS_NOT_NULL, OPARG_SIMPLE, 3 }, { _GUARD_NOS_LIST, OPARG_SIMPLE, 3 }, { _CALL_LIST_APPEND, OPARG_SIMPLE, 3 } } }, [CALL_METHOD_DESCRIPTOR_FAST] = { .nuops = 2, .uops = { { _CALL_METHOD_DESCRIPTOR_FAST, OPARG_SIMPLE, 3 }, { _CHECK_PERIODIC_AT_END, OPARG_REPLACED, 3 } } }, [CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS] = { .nuops = 2, .uops = { { _CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS, OPARG_SIMPLE, 3 }, { _CHECK_PERIODIC_AT_END, OPARG_REPLACED, 3 } } }, diff --git a/Include/internal/pycore_uop_ids.h b/Include/internal/pycore_uop_ids.h index 08e517dea326..ce1ad5a4c8a5 100644 --- a/Include/internal/pycore_uop_ids.h +++ b/Include/internal/pycore_uop_ids.h @@ -431,7 +431,7 @@ extern "C" { #define _CALL_INTRINSIC_2_r21 624 #define _CALL_ISINSTANCE_r31 625 #define _CALL_KW_NON_PY_r11 626 -#define _CALL_LEN_r31 627 +#define _CALL_LEN_r33 627 #define _CALL_LIST_APPEND_r30 628 #define _CALL_METHOD_DESCRIPTOR_FAST_r01 629 #define _CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS_r01 630 diff --git a/Include/internal/pycore_uop_metadata.h b/Include/internal/pycore_uop_metadata.h index 9ca53329e5d9..1e74588d3aa6 100644 --- a/Include/internal/pycore_uop_metadata.h +++ b/Include/internal/pycore_uop_metadata.h @@ -2624,7 +2624,7 @@ const _PyUopCachingInfo _PyUop_Caching[MAX_UOP_ID+1] = { { -1, -1, -1 }, { -1, -1, -1 }, { -1, -1, -1 }, - { 1, 0, _CALL_LEN_r31 }, + { 3, 0, _CALL_LEN_r33 }, }, }, [_GUARD_CALLABLE_ISINSTANCE] = { @@ -3751,7 +3751,7 @@ const uint16_t _PyUop_Uncached[MAX_UOP_REGS_ID+1] = { [_GUARD_CALLABLE_LEN_r13] = _GUARD_CALLABLE_LEN, [_GUARD_CALLABLE_LEN_r23] = _GUARD_CALLABLE_LEN, [_GUARD_CALLABLE_LEN_r33] = _GUARD_CALLABLE_LEN, - [_CALL_LEN_r31] = _CALL_LEN, + [_CALL_LEN_r33] = _CALL_LEN, [_GUARD_CALLABLE_ISINSTANCE_r03] = _GUARD_CALLABLE_ISINSTANCE, [_GUARD_CALLABLE_ISINSTANCE_r13] = _GUARD_CALLABLE_ISINSTANCE, [_GUARD_CALLABLE_ISINSTANCE_r23] = _GUARD_CALLABLE_ISINSTANCE, @@ -4042,7 +4042,7 @@ const char *const _PyOpcode_uop_name[MAX_UOP_REGS_ID+1] = { [_CALL_KW_NON_PY] = "_CALL_KW_NON_PY", [_CALL_KW_NON_PY_r11] = "_CALL_KW_NON_PY_r11", [_CALL_LEN] = "_CALL_LEN", - [_CALL_LEN_r31] = "_CALL_LEN_r31", + [_CALL_LEN_r33] = "_CALL_LEN_r33", [_CALL_LIST_APPEND] = "_CALL_LIST_APPEND", [_CALL_LIST_APPEND_r30] = "_CALL_LIST_APPEND_r30", [_CALL_METHOD_DESCRIPTOR_FAST] = "_CALL_METHOD_DESCRIPTOR_FAST", diff --git a/Lib/test/test_capi/test_opt.py b/Lib/test/test_capi/test_opt.py index f16055ab84de..0e7fd62c28a0 100644 --- a/Lib/test/test_capi/test_opt.py +++ b/Lib/test/test_capi/test_opt.py @@ -2066,6 +2066,7 @@ class TestUopsOptimization(unittest.TestCase): self.assertIn("_CALL_LEN", uops) self.assertNotIn("_GUARD_NOS_INT", uops) self.assertNotIn("_GUARD_TOS_INT", uops) + self.assertIn("_POP_TOP_NOP", uops) def test_call_len_known_length_small_int(self): # Make sure that len(t) is optimized for a tuple of length 5. diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 3cc6ff842c92..cb39a23d9510 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -4280,7 +4280,9 @@ dummy_func( unused/2 + _GUARD_NOS_NULL + _GUARD_CALLABLE_LEN + - _CALL_LEN; + _CALL_LEN + + POP_TOP + + POP_TOP; op(_GUARD_CALLABLE_LEN, (callable, unused, unused -- callable, unused, unused)){ PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable); @@ -4288,9 +4290,8 @@ dummy_func( DEOPT_IF(callable_o != interp->callable_cache.len); } - op(_CALL_LEN, (callable, null, arg -- res)) { + op(_CALL_LEN, (callable, null, arg -- res, a, c)) { /* len(o) */ - (void)null; STAT_INC(CALL, hit); PyObject *arg_o = PyStackRef_AsPyObjectBorrow(arg); Py_ssize_t len_i = PyObject_Length(arg_o); @@ -4302,9 +4303,9 @@ dummy_func( if (res_o == NULL) { ERROR_NO_POP(); } - PyStackRef_CLOSE(arg); - DEAD(null); - PyStackRef_CLOSE(callable); + a = arg; + c = callable; + INPUTS_DEAD(); res = PyStackRef_FromPyObjectSteal(res_o); } diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index 248cf60b70e4..598fea4dfbf6 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -13370,24 +13370,23 @@ break; } - case _CALL_LEN_r31: { + case _CALL_LEN_r33: { CHECK_CURRENT_CACHED_VALUES(3); assert(WITHIN_STACK_BOUNDS_WITH_CACHE()); _PyStackRef arg; - _PyStackRef null; _PyStackRef callable; _PyStackRef res; + _PyStackRef a; + _PyStackRef c; _PyStackRef _stack_item_0 = _tos_cache0; _PyStackRef _stack_item_1 = _tos_cache1; _PyStackRef _stack_item_2 = _tos_cache2; arg = _stack_item_2; - null = _stack_item_1; callable = _stack_item_0; - (void)null; STAT_INC(CALL, hit); PyObject *arg_o = PyStackRef_AsPyObjectBorrow(arg); stack_pointer[0] = callable; - stack_pointer[1] = null; + stack_pointer[1] = _stack_item_1; stack_pointer[2] = arg; stack_pointer += 3; ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); @@ -13404,21 +13403,15 @@ SET_CURRENT_CACHED_VALUES(0); JUMP_TO_ERROR(); } - stack_pointer += -1; - ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(arg); - stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -2; - ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); - _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(callable); - stack_pointer = _PyFrame_GetStackPointer(frame); + a = arg; + c = callable; res = PyStackRef_FromPyObjectSteal(res_o); + _tos_cache2 = c; + _tos_cache1 = a; _tos_cache0 = res; - _tos_cache1 = PyStackRef_ZERO_BITS; - _tos_cache2 = PyStackRef_ZERO_BITS; - SET_CURRENT_CACHED_VALUES(1); + SET_CURRENT_CACHED_VALUES(3); + stack_pointer += -3; + ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); assert(WITHIN_STACK_BOUNDS_WITH_CACHE()); break; } diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index b70b7fde37a7..a0e6fd2d7cba 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -3144,6 +3144,9 @@ _PyStackRef callable; _PyStackRef arg; _PyStackRef res; + _PyStackRef a; + _PyStackRef c; + _PyStackRef value; /* Skip 1 cache entry */ /* Skip 2 cache entries */ // _GUARD_NOS_NULL @@ -3169,7 +3172,6 @@ // _CALL_LEN { arg = stack_pointer[-1]; - (void)null; STAT_INC(CALL, hit); PyObject *arg_o = PyStackRef_AsPyObjectBorrow(arg); _PyFrame_SetStackPointer(frame, stack_pointer); @@ -3183,21 +3185,30 @@ if (res_o == NULL) { JUMP_TO_LABEL(error); } + a = arg; + c = callable; + res = PyStackRef_FromPyObjectSteal(res_o); + } + // _POP_TOP + { + value = c; + stack_pointer[-3] = res; + stack_pointer[-2] = a; stack_pointer += -1; ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(arg); + PyStackRef_XCLOSE(value); stack_pointer = _PyFrame_GetStackPointer(frame); - stack_pointer += -2; + } + // _POP_TOP + { + value = a; + stack_pointer += -1; ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); _PyFrame_SetStackPointer(frame, stack_pointer); - PyStackRef_CLOSE(callable); + PyStackRef_XCLOSE(value); stack_pointer = _PyFrame_GetStackPointer(frame); - res = PyStackRef_FromPyObjectSteal(res_o); } - stack_pointer[0] = res; - stack_pointer += 1; - ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); DISPATCH(); } diff --git a/Python/optimizer_bytecodes.c b/Python/optimizer_bytecodes.c index 701f46ac6631..9e767464500d 100644 --- a/Python/optimizer_bytecodes.c +++ b/Python/optimizer_bytecodes.c @@ -1202,7 +1202,7 @@ dummy_func(void) { sym_set_const(callable, (PyObject *)&PyUnicode_Type); } - op(_CALL_LEN, (callable, null, arg -- res)) { + op(_CALL_LEN, (callable, null, arg -- res, a, c)) { res = sym_new_type(ctx, &PyLong_Type); Py_ssize_t tuple_length = sym_tuple_length(arg); if (tuple_length >= 0) { @@ -1217,6 +1217,8 @@ dummy_func(void) { res = sym_new_const(ctx, temp); Py_DECREF(temp); } + a = arg; + c = callable; } op(_GET_LEN, (obj -- obj, len)) { diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h index f19ecb898a24..c3f122215ed7 100644 --- a/Python/optimizer_cases.c.h +++ b/Python/optimizer_cases.c.h @@ -3002,8 +3002,12 @@ case _CALL_LEN: { JitOptRef arg; + JitOptRef callable; JitOptRef res; + JitOptRef a; + JitOptRef c; arg = stack_pointer[-1]; + callable = stack_pointer[-3]; res = sym_new_type(ctx, &PyLong_Type); Py_ssize_t tuple_length = sym_tuple_length(arg); if (tuple_length >= 0) { @@ -3023,10 +3027,11 @@ Py_DECREF(temp); stack_pointer += 2; } - CHECK_STACK_BOUNDS(-2); + a = arg; + c = callable; stack_pointer[-3] = res; - stack_pointer += -2; - ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__); + stack_pointer[-2] = a; + stack_pointer[-1] = c; break; }