https://github.com/python/cpython/commit/3cb7eaec857503e4e343ced1e82c0864e16da729
commit: 3cb7eaec857503e4e343ced1e82c0864e16da729
branch: main
author: Kumar Aditya <[email protected]>
committer: kumaraditya303 <[email protected]>
date: 2026-04-14T16:02:23Z
summary:
gh-131798: constant fold special method lookups in JIT (#148432)
files:
M Include/internal/pycore_opcode_metadata.h
M Lib/test/test_capi/test_opt.py
M Python/bytecodes.c
M Python/optimizer_bytecodes.c
M Python/optimizer_cases.c.h
M Python/record_functions.c.h
diff --git a/Include/internal/pycore_opcode_metadata.h
b/Include/internal/pycore_opcode_metadata.h
index be7105cef5310f..da12e2f087d808 100644
--- a/Include/internal/pycore_opcode_metadata.h
+++ b/Include/internal/pycore_opcode_metadata.h
@@ -1252,7 +1252,7 @@ const struct opcode_metadata
_PyOpcode_opcode_metadata[267] = {
[LOAD_LOCALS] = { true, INSTR_FMT_IX, HAS_ERROR_FLAG },
[LOAD_NAME] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_NAME_FLAG |
HAS_ERROR_FLAG | HAS_ESCAPES_FLAG },
[LOAD_SMALL_INT] = { true, INSTR_FMT_IB, HAS_ARG_FLAG },
- [LOAD_SPECIAL] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_ERROR_FLAG |
HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG },
+ [LOAD_SPECIAL] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_ERROR_FLAG |
HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG | HAS_RECORDS_VALUE_FLAG },
[LOAD_SUPER_ATTR] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_NAME_FLAG |
HAS_ERROR_FLAG | HAS_ESCAPES_FLAG },
[LOAD_SUPER_ATTR_ATTR] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG |
HAS_NAME_FLAG | HAS_EXIT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG },
[LOAD_SUPER_ATTR_METHOD] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG |
HAS_NAME_FLAG | HAS_EXIT_FLAG | HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG |
HAS_ESCAPES_FLAG | HAS_RECORDS_VALUE_FLAG },
@@ -1472,7 +1472,7 @@ _PyOpcode_macro_expansion[256] = {
[LOAD_LOCALS] = { .nuops = 1, .uops = { { _LOAD_LOCALS, OPARG_SIMPLE, 0 }
} },
[LOAD_NAME] = { .nuops = 1, .uops = { { _LOAD_NAME, OPARG_SIMPLE, 0 } } },
[LOAD_SMALL_INT] = { .nuops = 1, .uops = { { _LOAD_SMALL_INT,
OPARG_SIMPLE, 0 } } },
- [LOAD_SPECIAL] = { .nuops = 2, .uops = { { _INSERT_NULL, OPARG_SIMPLE, 0
}, { _LOAD_SPECIAL, OPARG_SIMPLE, 0 } } },
+ [LOAD_SPECIAL] = { .nuops = 3, .uops = { { _RECORD_TOS_TYPE, OPARG_SIMPLE,
0 }, { _INSERT_NULL, OPARG_SIMPLE, 0 }, { _LOAD_SPECIAL, OPARG_SIMPLE, 0 } } },
[LOAD_SUPER_ATTR_ATTR] = { .nuops = 1, .uops = { { _LOAD_SUPER_ATTR_ATTR,
OPARG_SIMPLE, 1 } } },
[LOAD_SUPER_ATTR_METHOD] = { .nuops = 3, .uops = { { _RECORD_NOS,
OPARG_SIMPLE, 0 }, { _GUARD_LOAD_SUPER_ATTR_METHOD, OPARG_SIMPLE, 1 }, {
_LOAD_SUPER_ATTR_METHOD, OPARG_SIMPLE, 1 } } },
[MAKE_CELL] = { .nuops = 1, .uops = { { _MAKE_CELL, OPARG_SIMPLE, 0 } } },
diff --git a/Lib/test/test_capi/test_opt.py b/Lib/test/test_capi/test_opt.py
index c144ab1106ccea..9c85f036b39ff4 100644
--- a/Lib/test/test_capi/test_opt.py
+++ b/Lib/test/test_capi/test_opt.py
@@ -3435,6 +3435,25 @@ def f(n):
self.assertNotIn("_LOAD_ATTR_METHOD_NO_DICT", uops)
self.assertIn("_LOAD_CONST_INLINE_BORROW", uops)
+ def test_cached_load_special(self):
+ class CM:
+ def __enter__(self):
+ return self
+ def __exit__(self, *args):
+ pass
+ def f(n):
+ cm = CM()
+ x = 0
+ for _ in range(n):
+ with cm:
+ x += 1
+ return x
+ res, ex = self._run_with_optimizer(f, TIER2_THRESHOLD)
+ self.assertIsNotNone(ex)
+ self.assertEqual(res, TIER2_THRESHOLD)
+ uops = get_opnames(ex)
+ self.assertNotIn("_LOAD_SPECIAL", uops)
+
def test_store_fast_refcount_elimination(self):
def foo(x):
# Since x is known to be
diff --git a/Python/bytecodes.c b/Python/bytecodes.c
index dc21f084f74c49..6d51679884d57e 100644
--- a/Python/bytecodes.c
+++ b/Python/bytecodes.c
@@ -3883,6 +3883,7 @@ dummy_func(
}
macro(LOAD_SPECIAL) =
+ _RECORD_TOS_TYPE +
_INSERT_NULL +
_LOAD_SPECIAL;
diff --git a/Python/optimizer_bytecodes.c b/Python/optimizer_bytecodes.c
index 055e79ed9a3d14..76ce5cffb573e1 100644
--- a/Python/optimizer_bytecodes.c
+++ b/Python/optimizer_bytecodes.c
@@ -1845,8 +1845,30 @@ dummy_func(void) {
}
op(_LOAD_SPECIAL, (method_and_self[2] -- method_and_self[2])) {
- method_and_self[0] = sym_new_not_null(ctx);
- method_and_self[1] = sym_new_unknown(ctx);
+ bool optimized = false;
+ PyTypeObject *type = sym_get_probable_type(method_and_self[1]);
+ if (type != NULL) {
+ PyObject *name = _Py_SpecialMethods[oparg].name;
+ PyObject *descr = _PyType_Lookup(type, name);
+ if (descr != NULL && (Py_TYPE(descr)->tp_flags &
Py_TPFLAGS_METHOD_DESCRIPTOR)) {
+ ADD_OP(_GUARD_TYPE_VERSION, 0, type->tp_version_tag);
+ bool immortal = _Py_IsImmortal(descr) || (type->tp_flags &
Py_TPFLAGS_IMMUTABLETYPE);
+ ADD_OP(immortal ? _LOAD_CONST_INLINE_BORROW :
_LOAD_CONST_INLINE,
+ 0, (uintptr_t)descr);
+ ADD_OP(_SWAP, 3, 0);
+ ADD_OP(_POP_TOP, 0, 0);
+ if ((type->tp_flags & Py_TPFLAGS_IMMUTABLETYPE) == 0) {
+ PyType_Watch(TYPE_WATCHER_ID, (PyObject *)type);
+ _Py_BloomFilter_Add(dependencies, type);
+ }
+ method_and_self[0] = sym_new_const(ctx, descr);
+ optimized = true;
+ }
+ }
+ if (!optimized) {
+ method_and_self[0] = sym_new_not_null(ctx);
+ method_and_self[1] = sym_new_unknown(ctx);
+ }
}
op(_JUMP_TO_TOP, (--)) {
diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h
index 620ac604950c0b..547460e1a75581 100644
--- a/Python/optimizer_cases.c.h
+++ b/Python/optimizer_cases.c.h
@@ -3555,8 +3555,30 @@
case _LOAD_SPECIAL: {
JitOptRef *method_and_self;
method_and_self = &stack_pointer[-2];
- method_and_self[0] = sym_new_not_null(ctx);
- method_and_self[1] = sym_new_unknown(ctx);
+ bool optimized = false;
+ PyTypeObject *type = sym_get_probable_type(method_and_self[1]);
+ if (type != NULL) {
+ PyObject *name = _Py_SpecialMethods[oparg].name;
+ PyObject *descr = _PyType_Lookup(type, name);
+ if (descr != NULL && (Py_TYPE(descr)->tp_flags &
Py_TPFLAGS_METHOD_DESCRIPTOR)) {
+ ADD_OP(_GUARD_TYPE_VERSION, 0, type->tp_version_tag);
+ bool immortal = _Py_IsImmortal(descr) || (type->tp_flags &
Py_TPFLAGS_IMMUTABLETYPE);
+ ADD_OP(immortal ? _LOAD_CONST_INLINE_BORROW :
_LOAD_CONST_INLINE,
+ 0, (uintptr_t)descr);
+ ADD_OP(_SWAP, 3, 0);
+ ADD_OP(_POP_TOP, 0, 0);
+ if ((type->tp_flags & Py_TPFLAGS_IMMUTABLETYPE) == 0) {
+ PyType_Watch(TYPE_WATCHER_ID, (PyObject *)type);
+ _Py_BloomFilter_Add(dependencies, type);
+ }
+ method_and_self[0] = sym_new_const(ctx, descr);
+ optimized = true;
+ }
+ }
+ if (!optimized) {
+ method_and_self[0] = sym_new_not_null(ctx);
+ method_and_self[1] = sym_new_unknown(ctx);
+ }
break;
}
diff --git a/Python/record_functions.c.h b/Python/record_functions.c.h
index 48181961aac582..376dac501fe622 100644
--- a/Python/record_functions.c.h
+++ b/Python/record_functions.c.h
@@ -115,6 +115,7 @@ const _PyOpcodeRecordEntry _PyOpcode_RecordEntries[256] = {
[STORE_ATTR_WITH_HINT] = {1, {_RECORD_TOS_TYPE_INDEX}},
[STORE_ATTR_SLOT] = {1, {_RECORD_TOS_TYPE_INDEX}},
[FOR_ITER_GEN] = {1, {_RECORD_NOS_GEN_FUNC_INDEX}},
+ [LOAD_SPECIAL] = {1, {_RECORD_TOS_TYPE_INDEX}},
[LOAD_ATTR_METHOD_WITH_VALUES] = {1, {_RECORD_TOS_TYPE_INDEX}},
[LOAD_ATTR_METHOD_NO_DICT] = {1, {_RECORD_TOS_TYPE_INDEX}},
[LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES] = {1, {_RECORD_TOS_TYPE_INDEX}},
_______________________________________________
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]