Skip to content

Commit e9a08b0

Browse files
constant fold classmethod and staticmethod in JIT
1 parent ee2775c commit e9a08b0

File tree

7 files changed

+98
-16
lines changed

7 files changed

+98
-16
lines changed

Include/internal/pycore_opcode_metadata.h

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Lib/test/test_capi/test_opt.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3173,24 +3173,36 @@ def m(self):
31733173
class E(Exception):
31743174
def m(self):
31753175
return 1
3176+
class F:
3177+
@classmethod
3178+
def class_method(cls):
3179+
return 1
3180+
@staticmethod
3181+
def static_method():
3182+
return 1
3183+
31763184
def f(n):
31773185
x = 0
31783186
c = C()
31793187
d = D()
31803188
e = E()
3189+
f = F()
31813190
for _ in range(n):
31823191
x += C.A # _LOAD_ATTR_CLASS
31833192
x += c.A # _LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES
31843193
x += d.A # _LOAD_ATTR_NONDESCRIPTOR_NO_DICT
31853194
x += c.m() # _LOAD_ATTR_METHOD_WITH_VALUES
31863195
x += d.m() # _LOAD_ATTR_METHOD_NO_DICT
31873196
x += e.m() # _LOAD_ATTR_METHOD_LAZY_DICT
3197+
x += f.class_method() # _LOAD_ATTR
3198+
x += f.static_method() # _LOAD_ATTR
31883199
return x
31893200

31903201
res, ex = self._run_with_optimizer(f, TIER2_THRESHOLD)
3191-
self.assertEqual(res, 6 * TIER2_THRESHOLD)
3202+
self.assertEqual(res, 8 * TIER2_THRESHOLD)
31923203
self.assertIsNotNone(ex)
31933204
uops = get_opnames(ex)
3205+
self.assertNotIn("_LOAD_ATTR", uops)
31943206
self.assertNotIn("_LOAD_ATTR_CLASS", uops)
31953207
self.assertNotIn("_LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES", uops)
31963208
self.assertNotIn("_LOAD_ATTR_NONDESCRIPTOR_NO_DICT", uops)

Python/bytecodes.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2728,6 +2728,7 @@ dummy_func(
27282728

27292729
macro(LOAD_ATTR) =
27302730
_SPECIALIZE_LOAD_ATTR +
2731+
_RECORD_TOS_TYPE +
27312732
unused/8 +
27322733
_LOAD_ATTR;
27332734

Python/optimizer_bytecodes.c

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -873,10 +873,42 @@ dummy_func(void) {
873873
}
874874

875875
op(_LOAD_ATTR, (owner -- attr, self_or_null[oparg&1])) {
876-
(void)owner;
877-
attr = sym_new_not_null(ctx);
878-
if (oparg & 1) {
879-
self_or_null[0] = sym_new_unknown(ctx);
876+
PyTypeObject *type = sym_get_probable_type(owner);
877+
if (oparg & 1 && type != NULL) {
878+
PyObject *name = get_co_name(ctx, oparg >> 1);
879+
PyObject *descr = _PyType_Lookup(type, name);
880+
bool class_method = descr && Py_IS_TYPE(descr, &PyClassMethod_Type);
881+
bool static_method = descr && Py_IS_TYPE(descr, &PyStaticMethod_Type);
882+
if (class_method || static_method) {
883+
PyObject *callable = NULL;
884+
if (class_method) {
885+
callable = _PyClassMethod_GetFunc(descr);
886+
}
887+
else {
888+
assert(static_method);
889+
callable = _PyStaticMethod_GetFunc(descr);
890+
}
891+
assert(callable);
892+
bool immortal = _Py_IsImmortal(callable) || (type->tp_flags & Py_TPFLAGS_IMMUTABLETYPE);
893+
ADD_OP(_GUARD_TYPE_VERSION, 0, type->tp_version_tag);
894+
ADD_OP(_POP_TOP, 0, 0);
895+
ADD_OP(immortal ? _LOAD_CONST_INLINE_BORROW : _LOAD_CONST_INLINE, 0, (uintptr_t)callable);
896+
if (class_method) {
897+
ADD_OP(_LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)type);
898+
self_or_null[0] = sym_new_const(ctx, (PyObject *)type);
899+
} else if (static_method) {
900+
ADD_OP(_PUSH_NULL, 0, 0);
901+
self_or_null[0] = sym_new_null(ctx);
902+
}
903+
attr = sym_new_const(ctx, callable);
904+
PyType_Watch(TYPE_WATCHER_ID, (PyObject *)type);
905+
_Py_BloomFilter_Add(dependencies, (PyTypeObject *)type);
906+
} else {
907+
attr = sym_new_not_null(ctx);
908+
self_or_null[0] = sym_new_unknown(ctx);
909+
}
910+
} else {
911+
attr = sym_new_not_null(ctx);
880912
}
881913
}
882914

Python/optimizer_cases.c.h

Lines changed: 43 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Python/record_functions.c.h

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Tools/cases_generator/analyzer.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1149,7 +1149,8 @@ def add_macro(
11491149
f"Recording uop {part.name} must be first in macro",
11501150
macro.tokens[0])
11511151
parts.append(uop)
1152-
first = False
1152+
if uop.properties.tier != 1:
1153+
first = False
11531154
case parser.CacheEffect():
11541155
parts.append(Skip(part.size))
11551156
case _:

0 commit comments

Comments
 (0)