]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-134584: Eliminate redundant refcounting from ``_CALL_LEN`` (gh-136104)
authorDonghee Na <donghee.na@python.org>
Thu, 11 Dec 2025 15:24:34 +0000 (00:24 +0900)
committerGitHub <noreply@github.com>
Thu, 11 Dec 2025 15:24:34 +0000 (15:24 +0000)
Include/internal/pycore_opcode_metadata.h
Include/internal/pycore_uop_ids.h
Include/internal/pycore_uop_metadata.h
Lib/test/test_capi/test_opt.py
Python/bytecodes.c
Python/executor_cases.c.h
Python/generated_cases.c.h
Python/optimizer_bytecodes.c
Python/optimizer_cases.c.h

index 3ec0a86341483abaa8468c369e9f419aac22cbfe..35659de22b799d5ee531a98d94c5f66a301fa3d1 100644 (file)
@@ -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 } } },
index 08e517dea326e8c829e4bc6b8af226def0e7f250..ce1ad5a4c8a5f68be1e3adcd2ce8ad1b8fed2df5 100644 (file)
@@ -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
index 9ca53329e5d9599c123cd0c940d7c4e0282a4843..1e74588d3aa62ec6b4bd0230f7a8d2af5bbfd939 100644 (file)
@@ -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",
index f16055ab84de0890863e73283db834bcd9b3b5e1..0e7fd62c28a06545c4d5ffd5fded97c230c946aa 100644 (file)
@@ -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.
index 3cc6ff842c9202bf4e729c6ed3eb897f90f1c1b6..cb39a23d9510774f33425a8e1ad08557731a95de 100644 (file)
@@ -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);
         }
 
index 248cf60b70e45527760d1ad7564a8db3cdeea984..598fea4dfbf6921fbd4c138904cfbe3c1c33ec61 100644 (file)
             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__);
                 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;
         }
index b70b7fde37a72a4ee028ca0c8d552109f5e33107..a0e6fd2d7cba27c2661fa78d688d691e1e565052 100644 (file)
             _PyStackRef callable;
             _PyStackRef arg;
             _PyStackRef res;
+            _PyStackRef a;
+            _PyStackRef c;
+            _PyStackRef value;
             /* Skip 1 cache entry */
             /* Skip 2 cache entries */
             // _GUARD_NOS_NULL
             // _CALL_LEN
             {
                 arg = stack_pointer[-1];
-                (void)null;
                 STAT_INC(CALL, hit);
                 PyObject *arg_o = PyStackRef_AsPyObjectBorrow(arg);
                 _PyFrame_SetStackPointer(frame, stack_pointer);
                 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();
         }
 
index 701f46ac66312074ca2cdcd105f2c1331c50a42e..9e767464500d459c6e37db0048215306e01ca85a 100644 (file)
@@ -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)) {
index f19ecb898a24cb76b3ba29db3549fe41d3dfa659..c3f122215ed7bd8c54a65da62efb29cb21290349 100644 (file)
 
         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) {
                 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;
         }