]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-144140: Optimize len for string constants in optimizer (GH-144142)
authorYi Yang <1948638989@qq.com>
Sat, 24 Jan 2026 16:09:29 +0000 (00:09 +0800)
committerGitHub <noreply@github.com>
Sat, 24 Jan 2026 16:09:29 +0000 (16:09 +0000)
Lib/test/test_capi/test_opt.py
Python/optimizer_bytecodes.c
Python/optimizer_cases.c.h

index ff3adb1d4542e068142eba7c2547360d8aa7a7bc..0a02f29d978020c5b032db6cbd5683fc5807d923 100644 (file)
@@ -2419,6 +2419,21 @@ class TestUopsOptimization(unittest.TestCase):
         self.assertNotIn("_GUARD_TOS_INT", uops)
         self.assertIn("_POP_TOP_NOP", uops)
 
+    def test_call_len_string(self):
+        def testfunc(n):
+            for _ in range(n):
+                _ = len("abc")
+                d = ''
+                _ = len(d)
+                _ = len(b"def")
+                _ = len(b"")
+
+        _, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD)
+        self.assertIsNotNone(ex)
+        uops = get_opnames(ex)
+        self.assertNotIn("_CALL_LEN", uops)
+        self.assertEqual(count_ops(ex, "_SHUFFLE_3_LOAD_CONST_INLINE_BORROW"), 4)
+
     def test_call_len_known_length_small_int(self):
         # Make sure that len(t) is optimized for a tuple of length 5.
         # See https://github.com/python/cpython/issues/139393.
index 38cd088d9fb030d2f23c2d87dbfd8eeed9030d8f..9bc6075adc70cfd370e3643e25ef214f94e49c54 100644 (file)
@@ -1455,15 +1455,28 @@ dummy_func(void) {
 
     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) {
-            PyObject *temp = PyLong_FromSsize_t(tuple_length);
+        Py_ssize_t length = sym_tuple_length(arg);
+
+        // Not a tuple, check if it's a const string
+        if (length < 0 && sym_is_const(ctx, arg)) {
+            PyObject *const_val = sym_get_const(ctx, arg);
+            if (const_val != NULL) {
+                if (PyUnicode_CheckExact(const_val)) {
+                    length = PyUnicode_GET_LENGTH(const_val);
+                }
+                else if (PyBytes_CheckExact(const_val)) {
+                    length = PyBytes_GET_SIZE(const_val);
+                }
+            }
+        }
+
+        if (length >= 0) {
+            PyObject *temp = PyLong_FromSsize_t(length);
             if (temp == NULL) {
                 goto error;
             }
             if (_Py_IsImmortal(temp)) {
-                ADD_OP(_SHUFFLE_3_LOAD_CONST_INLINE_BORROW,
-                           0, (uintptr_t)temp);
+                ADD_OP(_SHUFFLE_3_LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)temp);
             }
             res = sym_new_const(ctx, temp);
             Py_DECREF(temp);
index e9405473fe2e0d06f64edd1a89fe150a7bcc9f10..2cd1f833dc34f17025c6454da1e1c3e792d3ec72 100644 (file)
             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) {
-                PyObject *temp = PyLong_FromSsize_t(tuple_length);
+            Py_ssize_t length = sym_tuple_length(arg);
+            if (length < 0 && sym_is_const(ctx, arg)) {
+                PyObject *const_val = sym_get_const(ctx, arg);
+                if (const_val != NULL) {
+                    if (PyUnicode_CheckExact(const_val)) {
+                        length = PyUnicode_GET_LENGTH(const_val);
+                    }
+                    else if (PyBytes_CheckExact(const_val)) {
+                        CHECK_STACK_BOUNDS(-2);
+                        stack_pointer[-3] = res;
+                        stack_pointer += -2;
+                        ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__);
+                        length = PyBytes_GET_SIZE(const_val);
+                        stack_pointer += 2;
+                    }
+                }
+            }
+            if (length >= 0) {
+                PyObject *temp = PyLong_FromSsize_t(length);
                 if (temp == NULL) {
                     goto error;
                 }
                 if (_Py_IsImmortal(temp)) {
-                    ADD_OP(_SHUFFLE_3_LOAD_CONST_INLINE_BORROW,
-                       0, (uintptr_t)temp);
+                    ADD_OP(_SHUFFLE_3_LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)temp);
                 }
                 res = sym_new_const(ctx, temp);
                 CHECK_STACK_BOUNDS(-2);