https://github.com/python/cpython/commit/979d92fefc0b6863c9783659f80d36cdfac90506
commit: 979d92fefc0b6863c9783659f80d36cdfac90506
branch: main
author: Yi Yang <[email protected]>
committer: Fidget-Spinner <[email protected]>
date: 2026-01-24T16:09:29Z
summary:
gh-144140: Optimize len for string constants in optimizer (GH-144142)
files:
M Lib/test/test_capi/test_opt.py
M Python/optimizer_bytecodes.c
M Python/optimizer_cases.c.h
diff --git a/Lib/test/test_capi/test_opt.py b/Lib/test/test_capi/test_opt.py
index ff3adb1d4542e0..0a02f29d978020 100644
--- a/Lib/test/test_capi/test_opt.py
+++ b/Lib/test/test_capi/test_opt.py
@@ -2419,6 +2419,21 @@ def testfunc(n):
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.
diff --git a/Python/optimizer_bytecodes.c b/Python/optimizer_bytecodes.c
index 38cd088d9fb030..9bc6075adc70cf 100644
--- a/Python/optimizer_bytecodes.c
+++ b/Python/optimizer_bytecodes.c
@@ -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);
diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h
index e9405473fe2e0d..2cd1f833dc34f1 100644
--- a/Python/optimizer_cases.c.h
+++ b/Python/optimizer_cases.c.h
@@ -3309,15 +3309,30 @@
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);
_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]