https://github.com/python/cpython/commit/18281db0d0350ceca2b74afb2c88ed644a737369
commit: 18281db0d0350ceca2b74afb2c88ed644a737369
branch: main
author: Lukas Geiger <[email protected]>
committer: kumaraditya303 <[email protected]>
date: 2026-05-18T12:20:42+05:30
summary:
gh-144140: Optimize len for frozen dict/set constants in optimizer (#149969)
files:
M Lib/test/test_capi/test_opt.py
M Python/optimizer_bytecodes.c
M Python/optimizer_cases.c.h
M Tools/cases_generator/analyzer.py
diff --git a/Lib/test/test_capi/test_opt.py b/Lib/test/test_capi/test_opt.py
index 2f606c2c6eba2d..790e965d6e5ff2 100644
--- a/Lib/test/test_capi/test_opt.py
+++ b/Lib/test/test_capi/test_opt.py
@@ -3154,7 +3154,7 @@ def testfunc(n):
uops = get_opnames(ex)
self.assertNotIn("_CHECK_IS_NOT_PY_CALLABLE_KW", uops)
- def test_call_len_string(self):
+ def test_call_len_string_frozen_set_dict(self):
def testfunc(n):
for _ in range(n):
_ = len("abc")
@@ -3162,12 +3162,14 @@ def testfunc(n):
_ = len(d)
_ = len(b"def")
_ = len(b"")
+ _ = len(FROZEN_SET_CONST)
+ _ = len(FROZEN_DICT_CONST)
_, ex = self._run_with_optimizer(testfunc, TIER2_THRESHOLD)
self.assertIsNotNone(ex)
uops = get_opnames(ex)
self.assertNotIn("_CALL_LEN", uops)
- self.assertGreaterEqual(count_ops(ex, "_LOAD_CONST_INLINE_BORROW"), 8)
+ self.assertGreaterEqual(count_ops(ex, "_LOAD_CONST_INLINE_BORROW"), 10)
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/optimizer_bytecodes.c b/Python/optimizer_bytecodes.c
index 96dbaea5a5797e..c968185d77c331 100644
--- a/Python/optimizer_bytecodes.c
+++ b/Python/optimizer_bytecodes.c
@@ -2378,7 +2378,7 @@ dummy_func(void) {
res = sym_new_type(ctx, &PyLong_Type);
Py_ssize_t length = sym_tuple_length(arg);
- // Not a tuple, check if it's a const string
+ // Not a tuple, check if it's another immutable const with known length
if (length < 0 && sym_is_const(ctx, arg)) {
PyObject *const_val = sym_get_const(ctx, arg);
if (const_val != NULL) {
@@ -2388,6 +2388,12 @@ dummy_func(void) {
else if (PyBytes_CheckExact(const_val)) {
length = PyBytes_GET_SIZE(const_val);
}
+ else if (PyFrozenDict_CheckExact(const_val)) {
+ length = PyDict_GET_SIZE(const_val);
+ }
+ else if (PyFrozenSet_CheckExact(const_val)) {
+ length = PySet_GET_SIZE(const_val);
+ }
}
}
diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h
index f336549d2ed244..d52ebb9804197d 100644
--- a/Python/optimizer_cases.c.h
+++ b/Python/optimizer_cases.c.h
@@ -4636,12 +4636,13 @@
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;
+ }
+ else if (PyFrozenDict_CheckExact(const_val)) {
+ length = PyDict_GET_SIZE(const_val);
+ }
+ else if (PyFrozenSet_CheckExact(const_val)) {
+ length = PySet_GET_SIZE(const_val);
}
}
}
diff --git a/Tools/cases_generator/analyzer.py
b/Tools/cases_generator/analyzer.py
index 59bca201a947e3..6f0ddeaeaabf09 100644
--- a/Tools/cases_generator/analyzer.py
+++ b/Tools/cases_generator/analyzer.py
@@ -616,6 +616,9 @@ def has_error_without_pop(op: parser.CodeDef) -> bool:
"PyStackRef_RefcountOnObject",
"PyStackRef_TYPE",
"PyStackRef_True",
+ "PyBytes_GET_SIZE",
+ "PyDict_GET_SIZE",
+ "PySet_GET_SIZE",
"PyTuple_GET_ITEM",
"PyTuple_GET_SIZE",
"PyType_HasFeature",
_______________________________________________
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]