gh-87729: specialize LOAD_SUPER_ATTR_METHOD (#103809)

This commit is contained in:
Carl Meyer 2023-04-25 11:45:51 -06:00 committed by GitHub
parent cef542ca57
commit ef25febcf2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 539 additions and 357 deletions

View File

@ -51,6 +51,15 @@ typedef struct {
#define INLINE_CACHE_ENTRIES_BINARY_SUBSCR CACHE_ENTRIES(_PyBinarySubscrCache) #define INLINE_CACHE_ENTRIES_BINARY_SUBSCR CACHE_ENTRIES(_PyBinarySubscrCache)
typedef struct {
uint16_t counter;
uint16_t class_version[2];
uint16_t self_type_version[2];
uint16_t method[4];
} _PySuperAttrCache;
#define INLINE_CACHE_ENTRIES_LOAD_SUPER_ATTR CACHE_ENTRIES(_PySuperAttrCache)
typedef struct { typedef struct {
uint16_t counter; uint16_t counter;
uint16_t version[2]; uint16_t version[2];
@ -217,6 +226,8 @@ extern int _PyLineTable_PreviousAddressRange(PyCodeAddressRange *range);
/* Specialization functions */ /* Specialization functions */
extern void _Py_Specialize_LoadSuperAttr(PyObject *global_super, PyObject *class, PyObject *self,
_Py_CODEUNIT *instr, PyObject *name, int load_method);
extern void _Py_Specialize_LoadAttr(PyObject *owner, _Py_CODEUNIT *instr, extern void _Py_Specialize_LoadAttr(PyObject *owner, _Py_CODEUNIT *instr,
PyObject *name); PyObject *name);
extern void _Py_Specialize_StoreAttr(PyObject *owner, _Py_CODEUNIT *instr, extern void _Py_Specialize_StoreAttr(PyObject *owner, _Py_CODEUNIT *instr,

View File

@ -42,6 +42,7 @@ const uint8_t _PyOpcode_Caches[256] = {
[LOAD_GLOBAL] = 4, [LOAD_GLOBAL] = 4,
[BINARY_OP] = 1, [BINARY_OP] = 1,
[SEND] = 1, [SEND] = 1,
[LOAD_SUPER_ATTR] = 9,
[CALL] = 3, [CALL] = 3,
}; };
@ -180,6 +181,7 @@ const uint8_t _PyOpcode_Deopt[256] = {
[LOAD_GLOBAL_MODULE] = LOAD_GLOBAL, [LOAD_GLOBAL_MODULE] = LOAD_GLOBAL,
[LOAD_NAME] = LOAD_NAME, [LOAD_NAME] = LOAD_NAME,
[LOAD_SUPER_ATTR] = LOAD_SUPER_ATTR, [LOAD_SUPER_ATTR] = LOAD_SUPER_ATTR,
[LOAD_SUPER_ATTR_METHOD] = LOAD_SUPER_ATTR,
[MAKE_CELL] = MAKE_CELL, [MAKE_CELL] = MAKE_CELL,
[MAKE_FUNCTION] = MAKE_FUNCTION, [MAKE_FUNCTION] = MAKE_FUNCTION,
[MAP_ADD] = MAP_ADD, [MAP_ADD] = MAP_ADD,
@ -304,29 +306,29 @@ static const char *const _PyOpcode_OpName[266] = {
[FOR_ITER_TUPLE] = "FOR_ITER_TUPLE", [FOR_ITER_TUPLE] = "FOR_ITER_TUPLE",
[FOR_ITER_RANGE] = "FOR_ITER_RANGE", [FOR_ITER_RANGE] = "FOR_ITER_RANGE",
[FOR_ITER_GEN] = "FOR_ITER_GEN", [FOR_ITER_GEN] = "FOR_ITER_GEN",
[LOAD_SUPER_ATTR_METHOD] = "LOAD_SUPER_ATTR_METHOD",
[LOAD_ATTR_CLASS] = "LOAD_ATTR_CLASS", [LOAD_ATTR_CLASS] = "LOAD_ATTR_CLASS",
[LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN] = "LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN",
[GET_ITER] = "GET_ITER", [GET_ITER] = "GET_ITER",
[GET_YIELD_FROM_ITER] = "GET_YIELD_FROM_ITER", [GET_YIELD_FROM_ITER] = "GET_YIELD_FROM_ITER",
[LOAD_ATTR_INSTANCE_VALUE] = "LOAD_ATTR_INSTANCE_VALUE", [LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN] = "LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN",
[LOAD_BUILD_CLASS] = "LOAD_BUILD_CLASS", [LOAD_BUILD_CLASS] = "LOAD_BUILD_CLASS",
[LOAD_ATTR_INSTANCE_VALUE] = "LOAD_ATTR_INSTANCE_VALUE",
[LOAD_ATTR_MODULE] = "LOAD_ATTR_MODULE", [LOAD_ATTR_MODULE] = "LOAD_ATTR_MODULE",
[LOAD_ATTR_PROPERTY] = "LOAD_ATTR_PROPERTY",
[LOAD_ASSERTION_ERROR] = "LOAD_ASSERTION_ERROR", [LOAD_ASSERTION_ERROR] = "LOAD_ASSERTION_ERROR",
[RETURN_GENERATOR] = "RETURN_GENERATOR", [RETURN_GENERATOR] = "RETURN_GENERATOR",
[LOAD_ATTR_PROPERTY] = "LOAD_ATTR_PROPERTY",
[LOAD_ATTR_SLOT] = "LOAD_ATTR_SLOT", [LOAD_ATTR_SLOT] = "LOAD_ATTR_SLOT",
[LOAD_ATTR_WITH_HINT] = "LOAD_ATTR_WITH_HINT", [LOAD_ATTR_WITH_HINT] = "LOAD_ATTR_WITH_HINT",
[LOAD_ATTR_METHOD_LAZY_DICT] = "LOAD_ATTR_METHOD_LAZY_DICT", [LOAD_ATTR_METHOD_LAZY_DICT] = "LOAD_ATTR_METHOD_LAZY_DICT",
[LOAD_ATTR_METHOD_NO_DICT] = "LOAD_ATTR_METHOD_NO_DICT", [LOAD_ATTR_METHOD_NO_DICT] = "LOAD_ATTR_METHOD_NO_DICT",
[LOAD_ATTR_METHOD_WITH_VALUES] = "LOAD_ATTR_METHOD_WITH_VALUES", [LOAD_ATTR_METHOD_WITH_VALUES] = "LOAD_ATTR_METHOD_WITH_VALUES",
[LOAD_CONST__LOAD_FAST] = "LOAD_CONST__LOAD_FAST", [LOAD_CONST__LOAD_FAST] = "LOAD_CONST__LOAD_FAST",
[LOAD_FAST__LOAD_CONST] = "LOAD_FAST__LOAD_CONST",
[RETURN_VALUE] = "RETURN_VALUE", [RETURN_VALUE] = "RETURN_VALUE",
[LOAD_FAST__LOAD_FAST] = "LOAD_FAST__LOAD_FAST", [LOAD_FAST__LOAD_CONST] = "LOAD_FAST__LOAD_CONST",
[SETUP_ANNOTATIONS] = "SETUP_ANNOTATIONS", [SETUP_ANNOTATIONS] = "SETUP_ANNOTATIONS",
[LOAD_FAST__LOAD_FAST] = "LOAD_FAST__LOAD_FAST",
[LOAD_GLOBAL_BUILTIN] = "LOAD_GLOBAL_BUILTIN", [LOAD_GLOBAL_BUILTIN] = "LOAD_GLOBAL_BUILTIN",
[LOAD_GLOBAL_MODULE] = "LOAD_GLOBAL_MODULE", [LOAD_GLOBAL_MODULE] = "LOAD_GLOBAL_MODULE",
[STORE_ATTR_INSTANCE_VALUE] = "STORE_ATTR_INSTANCE_VALUE",
[POP_EXCEPT] = "POP_EXCEPT", [POP_EXCEPT] = "POP_EXCEPT",
[STORE_NAME] = "STORE_NAME", [STORE_NAME] = "STORE_NAME",
[DELETE_NAME] = "DELETE_NAME", [DELETE_NAME] = "DELETE_NAME",
@ -349,9 +351,9 @@ static const char *const _PyOpcode_OpName[266] = {
[IMPORT_NAME] = "IMPORT_NAME", [IMPORT_NAME] = "IMPORT_NAME",
[IMPORT_FROM] = "IMPORT_FROM", [IMPORT_FROM] = "IMPORT_FROM",
[JUMP_FORWARD] = "JUMP_FORWARD", [JUMP_FORWARD] = "JUMP_FORWARD",
[STORE_ATTR_INSTANCE_VALUE] = "STORE_ATTR_INSTANCE_VALUE",
[STORE_ATTR_SLOT] = "STORE_ATTR_SLOT", [STORE_ATTR_SLOT] = "STORE_ATTR_SLOT",
[STORE_ATTR_WITH_HINT] = "STORE_ATTR_WITH_HINT", [STORE_ATTR_WITH_HINT] = "STORE_ATTR_WITH_HINT",
[STORE_FAST__LOAD_FAST] = "STORE_FAST__LOAD_FAST",
[POP_JUMP_IF_FALSE] = "POP_JUMP_IF_FALSE", [POP_JUMP_IF_FALSE] = "POP_JUMP_IF_FALSE",
[POP_JUMP_IF_TRUE] = "POP_JUMP_IF_TRUE", [POP_JUMP_IF_TRUE] = "POP_JUMP_IF_TRUE",
[LOAD_GLOBAL] = "LOAD_GLOBAL", [LOAD_GLOBAL] = "LOAD_GLOBAL",
@ -381,7 +383,7 @@ static const char *const _PyOpcode_OpName[266] = {
[JUMP_BACKWARD] = "JUMP_BACKWARD", [JUMP_BACKWARD] = "JUMP_BACKWARD",
[LOAD_SUPER_ATTR] = "LOAD_SUPER_ATTR", [LOAD_SUPER_ATTR] = "LOAD_SUPER_ATTR",
[CALL_FUNCTION_EX] = "CALL_FUNCTION_EX", [CALL_FUNCTION_EX] = "CALL_FUNCTION_EX",
[STORE_FAST__STORE_FAST] = "STORE_FAST__STORE_FAST", [STORE_FAST__LOAD_FAST] = "STORE_FAST__LOAD_FAST",
[EXTENDED_ARG] = "EXTENDED_ARG", [EXTENDED_ARG] = "EXTENDED_ARG",
[LIST_APPEND] = "LIST_APPEND", [LIST_APPEND] = "LIST_APPEND",
[SET_ADD] = "SET_ADD", [SET_ADD] = "SET_ADD",
@ -391,20 +393,20 @@ static const char *const _PyOpcode_OpName[266] = {
[YIELD_VALUE] = "YIELD_VALUE", [YIELD_VALUE] = "YIELD_VALUE",
[RESUME] = "RESUME", [RESUME] = "RESUME",
[MATCH_CLASS] = "MATCH_CLASS", [MATCH_CLASS] = "MATCH_CLASS",
[STORE_FAST__STORE_FAST] = "STORE_FAST__STORE_FAST",
[STORE_SUBSCR_DICT] = "STORE_SUBSCR_DICT", [STORE_SUBSCR_DICT] = "STORE_SUBSCR_DICT",
[STORE_SUBSCR_LIST_INT] = "STORE_SUBSCR_LIST_INT",
[FORMAT_VALUE] = "FORMAT_VALUE", [FORMAT_VALUE] = "FORMAT_VALUE",
[BUILD_CONST_KEY_MAP] = "BUILD_CONST_KEY_MAP", [BUILD_CONST_KEY_MAP] = "BUILD_CONST_KEY_MAP",
[BUILD_STRING] = "BUILD_STRING", [BUILD_STRING] = "BUILD_STRING",
[STORE_SUBSCR_LIST_INT] = "STORE_SUBSCR_LIST_INT",
[UNPACK_SEQUENCE_LIST] = "UNPACK_SEQUENCE_LIST", [UNPACK_SEQUENCE_LIST] = "UNPACK_SEQUENCE_LIST",
[UNPACK_SEQUENCE_TUPLE] = "UNPACK_SEQUENCE_TUPLE", [UNPACK_SEQUENCE_TUPLE] = "UNPACK_SEQUENCE_TUPLE",
[UNPACK_SEQUENCE_TWO_TUPLE] = "UNPACK_SEQUENCE_TWO_TUPLE", [UNPACK_SEQUENCE_TWO_TUPLE] = "UNPACK_SEQUENCE_TWO_TUPLE",
[SEND_GEN] = "SEND_GEN",
[LIST_EXTEND] = "LIST_EXTEND", [LIST_EXTEND] = "LIST_EXTEND",
[SET_UPDATE] = "SET_UPDATE", [SET_UPDATE] = "SET_UPDATE",
[DICT_MERGE] = "DICT_MERGE", [DICT_MERGE] = "DICT_MERGE",
[DICT_UPDATE] = "DICT_UPDATE", [DICT_UPDATE] = "DICT_UPDATE",
[166] = "<166>", [SEND_GEN] = "SEND_GEN",
[167] = "<167>", [167] = "<167>",
[168] = "<168>", [168] = "<168>",
[169] = "<169>", [169] = "<169>",
@ -508,7 +510,6 @@ static const char *const _PyOpcode_OpName[266] = {
#endif #endif
#define EXTRA_CASES \ #define EXTRA_CASES \
case 166: \
case 167: \ case 167: \
case 168: \ case 168: \
case 169: \ case 169: \

View File

@ -119,6 +119,8 @@ PyObject *_Py_slot_tp_getattr_hook(PyObject *self, PyObject *name);
PyObject * PyObject *
_PySuper_Lookup(PyTypeObject *su_type, PyObject *su_obj, PyObject *name, int *meth_found); _PySuper_Lookup(PyTypeObject *su_type, PyObject *su_obj, PyObject *name, int *meth_found);
PyObject *
_PySuper_LookupDescr(PyTypeObject *su_type, PyObject *su_obj, PyObject *name);
#ifdef __cplusplus #ifdef __cplusplus
} }

53
Include/opcode.h generated
View File

@ -183,32 +183,33 @@ extern "C" {
#define FOR_ITER_TUPLE 63 #define FOR_ITER_TUPLE 63
#define FOR_ITER_RANGE 64 #define FOR_ITER_RANGE 64
#define FOR_ITER_GEN 65 #define FOR_ITER_GEN 65
#define LOAD_ATTR_CLASS 66 #define LOAD_SUPER_ATTR_METHOD 66
#define LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN 67 #define LOAD_ATTR_CLASS 67
#define LOAD_ATTR_INSTANCE_VALUE 70 #define LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN 70
#define LOAD_ATTR_MODULE 72 #define LOAD_ATTR_INSTANCE_VALUE 72
#define LOAD_ATTR_PROPERTY 73 #define LOAD_ATTR_MODULE 73
#define LOAD_ATTR_SLOT 76 #define LOAD_ATTR_PROPERTY 76
#define LOAD_ATTR_WITH_HINT 77 #define LOAD_ATTR_SLOT 77
#define LOAD_ATTR_METHOD_LAZY_DICT 78 #define LOAD_ATTR_WITH_HINT 78
#define LOAD_ATTR_METHOD_NO_DICT 79 #define LOAD_ATTR_METHOD_LAZY_DICT 79
#define LOAD_ATTR_METHOD_WITH_VALUES 80 #define LOAD_ATTR_METHOD_NO_DICT 80
#define LOAD_CONST__LOAD_FAST 81 #define LOAD_ATTR_METHOD_WITH_VALUES 81
#define LOAD_FAST__LOAD_CONST 82 #define LOAD_CONST__LOAD_FAST 82
#define LOAD_FAST__LOAD_FAST 84 #define LOAD_FAST__LOAD_CONST 84
#define LOAD_GLOBAL_BUILTIN 86 #define LOAD_FAST__LOAD_FAST 86
#define LOAD_GLOBAL_MODULE 87 #define LOAD_GLOBAL_BUILTIN 87
#define STORE_ATTR_INSTANCE_VALUE 88 #define LOAD_GLOBAL_MODULE 88
#define STORE_ATTR_SLOT 111 #define STORE_ATTR_INSTANCE_VALUE 111
#define STORE_ATTR_WITH_HINT 112 #define STORE_ATTR_SLOT 112
#define STORE_FAST__LOAD_FAST 113 #define STORE_ATTR_WITH_HINT 113
#define STORE_FAST__STORE_FAST 143 #define STORE_FAST__LOAD_FAST 143
#define STORE_SUBSCR_DICT 153 #define STORE_FAST__STORE_FAST 153
#define STORE_SUBSCR_LIST_INT 154 #define STORE_SUBSCR_DICT 154
#define UNPACK_SEQUENCE_LIST 158 #define STORE_SUBSCR_LIST_INT 158
#define UNPACK_SEQUENCE_TUPLE 159 #define UNPACK_SEQUENCE_LIST 159
#define UNPACK_SEQUENCE_TWO_TUPLE 160 #define UNPACK_SEQUENCE_TUPLE 160
#define SEND_GEN 161 #define UNPACK_SEQUENCE_TWO_TUPLE 161
#define SEND_GEN 166
#define HAS_ARG(op) ((((op) >= HAVE_ARGUMENT) && (!IS_PSEUDO_OPCODE(op)))\ #define HAS_ARG(op) ((((op) >= HAVE_ARGUMENT) && (!IS_PSEUDO_OPCODE(op)))\
|| ((op) == JUMP) \ || ((op) == JUMP) \

View File

@ -440,7 +440,8 @@ _code_type = type(_write_atomic.__code__)
# Python 3.12a7 3524 (Shrink the BINARY_SUBSCR caches) # Python 3.12a7 3524 (Shrink the BINARY_SUBSCR caches)
# Python 3.12b1 3525 (Shrink the CALL caches) # Python 3.12b1 3525 (Shrink the CALL caches)
# Python 3.12b1 3526 (Add instrumentation support) # Python 3.12b1 3526 (Add instrumentation support)
# Python 3.12b1 3527 (Optimize super() calls) # Python 3.12b1 3527 (Add LOAD_SUPER_ATTR)
# Python 3.12b1 3528 (Add LOAD_SUPER_ATTR_METHOD specialization)
# Python 3.13 will start with 3550 # Python 3.13 will start with 3550
@ -457,7 +458,7 @@ _code_type = type(_write_atomic.__code__)
# Whenever MAGIC_NUMBER is changed, the ranges in the magic_values array # Whenever MAGIC_NUMBER is changed, the ranges in the magic_values array
# in PC/launcher.c must also be updated. # in PC/launcher.c must also be updated.
MAGIC_NUMBER = (3527).to_bytes(2, 'little') + b'\r\n' MAGIC_NUMBER = (3528).to_bytes(2, 'little') + b'\r\n'
_RAW_MAGIC_NUMBER = int.from_bytes(MAGIC_NUMBER, 'little') # For import.c _RAW_MAGIC_NUMBER = int.from_bytes(MAGIC_NUMBER, 'little') # For import.c

View File

@ -353,6 +353,9 @@ _specializations = {
"FOR_ITER_RANGE", "FOR_ITER_RANGE",
"FOR_ITER_GEN", "FOR_ITER_GEN",
], ],
"LOAD_SUPER_ATTR": [
"LOAD_SUPER_ATTR_METHOD",
],
"LOAD_ATTR": [ "LOAD_ATTR": [
# These potentially push [NULL, bound method] onto the stack. # These potentially push [NULL, bound method] onto the stack.
"LOAD_ATTR_CLASS", "LOAD_ATTR_CLASS",
@ -426,6 +429,12 @@ _cache_format = {
"FOR_ITER": { "FOR_ITER": {
"counter": 1, "counter": 1,
}, },
"LOAD_SUPER_ATTR": {
"counter": 1,
"class_version": 2,
"self_type_version": 2,
"method": 4,
},
"LOAD_ATTR": { "LOAD_ATTR": {
"counter": 1, "counter": 1,
"version": 2, "version": 2,

View File

@ -1,6 +1,29 @@
import unittest import unittest
class TestLoadSuperAttrCache(unittest.TestCase):
def test_descriptor_not_double_executed_on_spec_fail(self):
calls = []
class Descriptor:
def __get__(self, instance, owner):
calls.append((instance, owner))
return lambda: 1
class C:
d = Descriptor()
class D(C):
def f(self):
return super().d()
d = D()
self.assertEqual(d.f(), 1) # warmup
calls.clear()
self.assertEqual(d.f(), 1) # try to specialize
self.assertEqual(calls, [(d, D)])
class TestLoadAttrCache(unittest.TestCase): class TestLoadAttrCache(unittest.TestCase):
def test_descriptor_added_after_optimization(self): def test_descriptor_added_after_optimization(self):
class Descriptor: class Descriptor:

View File

@ -1 +1,4 @@
Add :opcode:`LOAD_SUPER_ATTR` to speed up ``super().meth()`` and ``super().attr`` calls. Add :opcode:`LOAD_SUPER_ATTR` (and a specialization for ``super().method()``) to
speed up ``super().method()`` and ``super().attr``. This makes
``super().method()`` roughly 2.3x faster and brings it within 20% of the
performance of a simple method call. Patch by Vladimir Matveev and Carl Meyer.

View File

@ -9380,22 +9380,19 @@ super_repr(PyObject *self)
su->type ? su->type->tp_name : "NULL"); su->type ? su->type->tp_name : "NULL");
} }
// if `method` is non-NULL, we are looking for a method descriptor, /* Do a super lookup without executing descriptors or falling back to getattr
// and setting `*method` to 1 means we found one. on the super object itself.
May return NULL with or without an exception set, like PyDict_GetItemWithError. */
static PyObject * static PyObject *
do_super_lookup(superobject *su, PyTypeObject *su_type, PyObject *su_obj, _super_lookup_descr(PyTypeObject *su_type, PyTypeObject *su_obj_type, PyObject *name)
PyTypeObject *su_obj_type, PyObject *name, int *method)
{ {
PyObject *mro, *res; PyObject *mro, *res;
Py_ssize_t i, n; Py_ssize_t i, n;
int temp_su = 0;
if (su_obj_type == NULL)
goto skip;
mro = su_obj_type->tp_mro; mro = su_obj_type->tp_mro;
if (mro == NULL) if (mro == NULL)
goto skip; return NULL;
assert(PyTuple_Check(mro)); assert(PyTuple_Check(mro));
n = PyTuple_GET_SIZE(mro); n = PyTuple_GET_SIZE(mro);
@ -9407,7 +9404,7 @@ do_super_lookup(superobject *su, PyTypeObject *su_type, PyObject *su_obj,
} }
i++; /* skip su->type (if any) */ i++; /* skip su->type (if any) */
if (i >= n) if (i >= n)
goto skip; return NULL;
/* keep a strong reference to mro because su_obj_type->tp_mro can be /* keep a strong reference to mro because su_obj_type->tp_mro can be
replaced during PyDict_GetItemWithError(dict, name) */ replaced during PyDict_GetItemWithError(dict, name) */
@ -9420,6 +9417,35 @@ do_super_lookup(superobject *su, PyTypeObject *su_type, PyObject *su_obj,
res = PyDict_GetItemWithError(dict, name); res = PyDict_GetItemWithError(dict, name);
if (res != NULL) { if (res != NULL) {
Py_INCREF(res); Py_INCREF(res);
Py_DECREF(mro);
return res;
}
else if (PyErr_Occurred()) {
Py_DECREF(mro);
return NULL;
}
i++;
} while (i < n);
Py_DECREF(mro);
return NULL;
}
// if `method` is non-NULL, we are looking for a method descriptor,
// and setting `*method = 1` means we found one.
static PyObject *
do_super_lookup(superobject *su, PyTypeObject *su_type, PyObject *su_obj,
PyTypeObject *su_obj_type, PyObject *name, int *method)
{
PyObject *res;
int temp_su = 0;
if (su_obj_type == NULL) {
goto skip;
}
res = _super_lookup_descr(su_type, su_obj_type, name);
if (res != NULL) {
if (method && _PyType_HasFeature(Py_TYPE(res), Py_TPFLAGS_METHOD_DESCRIPTOR)) { if (method && _PyType_HasFeature(Py_TYPE(res), Py_TPFLAGS_METHOD_DESCRIPTOR)) {
*method = 1; *method = 1;
} }
@ -9436,18 +9462,12 @@ do_super_lookup(superobject *su, PyTypeObject *su_type, PyObject *su_obj,
} }
} }
Py_DECREF(mro);
return res; return res;
} }
else if (PyErr_Occurred()) { else if (PyErr_Occurred()) {
Py_DECREF(mro);
return NULL; return NULL;
} }
i++;
} while (i < n);
Py_DECREF(mro);
skip: skip:
if (su == NULL) { if (su == NULL) {
PyObject *args[] = {(PyObject *)su_type, su_obj}; PyObject *args[] = {(PyObject *)su_type, su_obj};
@ -9544,6 +9564,18 @@ _PySuper_Lookup(PyTypeObject *su_type, PyObject *su_obj, PyObject *name, int *me
return res; return res;
} }
PyObject *
_PySuper_LookupDescr(PyTypeObject *su_type, PyObject *su_obj, PyObject *name)
{
PyTypeObject *su_obj_type = supercheck(su_type, su_obj);
if (su_obj_type == NULL) {
return NULL;
}
PyObject *res = _super_lookup_descr(su_type, su_obj_type, name);
Py_DECREF(su_obj_type);
return res;
}
static PyObject * static PyObject *
super_descr_get(PyObject *self, PyObject *obj, PyObject *type) super_descr_get(PyObject *self, PyObject *obj, PyObject *type)
{ {

View File

@ -1554,26 +1554,27 @@ dummy_func(
PREDICT(JUMP_BACKWARD); PREDICT(JUMP_BACKWARD);
} }
inst(LOAD_SUPER_ATTR, (global_super, class, self -- res2 if (oparg & 1), res)) { family(load_super_attr, INLINE_CACHE_ENTRIES_LOAD_SUPER_ATTR) = {
LOAD_SUPER_ATTR,
LOAD_SUPER_ATTR_METHOD,
};
inst(LOAD_SUPER_ATTR, (unused/9, global_super, class, self -- res2 if (oparg & 1), res)) {
PyObject *name = GETITEM(frame->f_code->co_names, oparg >> 2); PyObject *name = GETITEM(frame->f_code->co_names, oparg >> 2);
if (global_super == (PyObject *)&PySuper_Type && PyType_Check(class)) { int load_method = oparg & 1;
int method = 0; #if ENABLE_SPECIALIZATION
Py_DECREF(global_super); _PySuperAttrCache *cache = (_PySuperAttrCache *)next_instr;
res = _PySuper_Lookup((PyTypeObject *)class, self, name, oparg & 1 ? &method : NULL); if (ADAPTIVE_COUNTER_IS_ZERO(cache->counter)) {
Py_DECREF(class); next_instr--;
if (res == NULL) { _Py_Specialize_LoadSuperAttr(global_super, class, self, next_instr, name, load_method);
Py_DECREF(self); DISPATCH_SAME_OPARG();
ERROR_IF(true, error);
} }
// Works with CALL, pushes two values: either `meth | self` or `NULL | meth`. STAT_INC(LOAD_SUPER_ATTR, deferred);
if (method) { DECREMENT_ADAPTIVE_COUNTER(cache->counter);
res2 = res; #endif /* ENABLE_SPECIALIZATION */
res = self; // transfer ownership
} else { // we make no attempt to optimize here; specializations should
res2 = NULL; // handle any case whose performance we care about
Py_DECREF(self);
}
} else {
PyObject *stack[] = {class, self}; PyObject *stack[] = {class, self};
PyObject *super = PyObject_Vectorcall(global_super, stack, oparg & 2, NULL); PyObject *super = PyObject_Vectorcall(global_super, stack, oparg & 2, NULL);
DECREF_INPUTS(); DECREF_INPUTS();
@ -1582,6 +1583,18 @@ dummy_func(
Py_DECREF(super); Py_DECREF(super);
ERROR_IF(res == NULL, error); ERROR_IF(res == NULL, error);
} }
inst(LOAD_SUPER_ATTR_METHOD, (unused/1, class_version/2, self_type_version/2, method/4, global_super, class, self -- res2, res)) {
DEOPT_IF(global_super != (PyObject *)&PySuper_Type, LOAD_SUPER_ATTR);
DEOPT_IF(!PyType_Check(class), LOAD_SUPER_ATTR);
DEOPT_IF(((PyTypeObject *)class)->tp_version_tag != class_version, LOAD_SUPER_ATTR);
PyTypeObject *self_type = Py_TYPE(self);
DEOPT_IF(self_type->tp_version_tag != self_type_version, LOAD_SUPER_ATTR);
res2 = method;
res = self; // transfer ownership
Py_INCREF(res2);
Py_DECREF(global_super);
Py_DECREF(class);
} }
family(load_attr, INLINE_CACHE_ENTRIES_LOAD_ATTR) = { family(load_attr, INLINE_CACHE_ENTRIES_LOAD_ATTR) = {

File diff suppressed because it is too large Load Diff

View File

@ -207,6 +207,8 @@ _PyOpcode_num_popped(int opcode, int oparg, bool jump) {
return 2; return 2;
case LOAD_SUPER_ATTR: case LOAD_SUPER_ATTR:
return 3; return 3;
case LOAD_SUPER_ATTR_METHOD:
return 3;
case LOAD_ATTR: case LOAD_ATTR:
return 1; return 1;
case LOAD_ATTR_INSTANCE_VALUE: case LOAD_ATTR_INSTANCE_VALUE:
@ -593,6 +595,8 @@ _PyOpcode_num_pushed(int opcode, int oparg, bool jump) {
return 0; return 0;
case LOAD_SUPER_ATTR: case LOAD_SUPER_ATTR:
return ((oparg & 1) ? 1 : 0) + 1; return ((oparg & 1) ? 1 : 0) + 1;
case LOAD_SUPER_ATTR_METHOD:
return 2;
case LOAD_ATTR: case LOAD_ATTR:
return ((oparg & 1) ? 1 : 0) + 1; return ((oparg & 1) ? 1 : 0) + 1;
case LOAD_ATTR_INSTANCE_VALUE: case LOAD_ATTR_INSTANCE_VALUE:
@ -775,7 +779,7 @@ _PyOpcode_num_pushed(int opcode, int oparg, bool jump) {
} }
#endif #endif
enum InstructionFormat { INSTR_FMT_IB, INSTR_FMT_IBC, INSTR_FMT_IBC00, INSTR_FMT_IBC000, INSTR_FMT_IBC00000000, INSTR_FMT_IBIB, INSTR_FMT_IX, INSTR_FMT_IXC, INSTR_FMT_IXC000 }; enum InstructionFormat { INSTR_FMT_IB, INSTR_FMT_IBC, INSTR_FMT_IBC00, INSTR_FMT_IBC000, INSTR_FMT_IBC00000000, INSTR_FMT_IBIB, INSTR_FMT_IX, INSTR_FMT_IXC, INSTR_FMT_IXC000, INSTR_FMT_IXC00000000 };
struct opcode_metadata { struct opcode_metadata {
bool valid_entry; bool valid_entry;
enum InstructionFormat instr_format; enum InstructionFormat instr_format;
@ -883,7 +887,8 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[256] = {
[DICT_UPDATE] = { true, INSTR_FMT_IB }, [DICT_UPDATE] = { true, INSTR_FMT_IB },
[DICT_MERGE] = { true, INSTR_FMT_IB }, [DICT_MERGE] = { true, INSTR_FMT_IB },
[MAP_ADD] = { true, INSTR_FMT_IB }, [MAP_ADD] = { true, INSTR_FMT_IB },
[LOAD_SUPER_ATTR] = { true, INSTR_FMT_IB }, [LOAD_SUPER_ATTR] = { true, INSTR_FMT_IBC00000000 },
[LOAD_SUPER_ATTR_METHOD] = { true, INSTR_FMT_IXC00000000 },
[LOAD_ATTR] = { true, INSTR_FMT_IBC00000000 }, [LOAD_ATTR] = { true, INSTR_FMT_IBC00000000 },
[LOAD_ATTR_INSTANCE_VALUE] = { true, INSTR_FMT_IBC00000000 }, [LOAD_ATTR_INSTANCE_VALUE] = { true, INSTR_FMT_IBC00000000 },
[LOAD_ATTR_MODULE] = { true, INSTR_FMT_IBC00000000 }, [LOAD_ATTR_MODULE] = { true, INSTR_FMT_IBC00000000 },

View File

@ -65,29 +65,29 @@ static void *opcode_targets[256] = {
&&TARGET_FOR_ITER_TUPLE, &&TARGET_FOR_ITER_TUPLE,
&&TARGET_FOR_ITER_RANGE, &&TARGET_FOR_ITER_RANGE,
&&TARGET_FOR_ITER_GEN, &&TARGET_FOR_ITER_GEN,
&&TARGET_LOAD_SUPER_ATTR_METHOD,
&&TARGET_LOAD_ATTR_CLASS, &&TARGET_LOAD_ATTR_CLASS,
&&TARGET_LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN,
&&TARGET_GET_ITER, &&TARGET_GET_ITER,
&&TARGET_GET_YIELD_FROM_ITER, &&TARGET_GET_YIELD_FROM_ITER,
&&TARGET_LOAD_ATTR_INSTANCE_VALUE, &&TARGET_LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN,
&&TARGET_LOAD_BUILD_CLASS, &&TARGET_LOAD_BUILD_CLASS,
&&TARGET_LOAD_ATTR_INSTANCE_VALUE,
&&TARGET_LOAD_ATTR_MODULE, &&TARGET_LOAD_ATTR_MODULE,
&&TARGET_LOAD_ATTR_PROPERTY,
&&TARGET_LOAD_ASSERTION_ERROR, &&TARGET_LOAD_ASSERTION_ERROR,
&&TARGET_RETURN_GENERATOR, &&TARGET_RETURN_GENERATOR,
&&TARGET_LOAD_ATTR_PROPERTY,
&&TARGET_LOAD_ATTR_SLOT, &&TARGET_LOAD_ATTR_SLOT,
&&TARGET_LOAD_ATTR_WITH_HINT, &&TARGET_LOAD_ATTR_WITH_HINT,
&&TARGET_LOAD_ATTR_METHOD_LAZY_DICT, &&TARGET_LOAD_ATTR_METHOD_LAZY_DICT,
&&TARGET_LOAD_ATTR_METHOD_NO_DICT, &&TARGET_LOAD_ATTR_METHOD_NO_DICT,
&&TARGET_LOAD_ATTR_METHOD_WITH_VALUES, &&TARGET_LOAD_ATTR_METHOD_WITH_VALUES,
&&TARGET_LOAD_CONST__LOAD_FAST, &&TARGET_LOAD_CONST__LOAD_FAST,
&&TARGET_LOAD_FAST__LOAD_CONST,
&&TARGET_RETURN_VALUE, &&TARGET_RETURN_VALUE,
&&TARGET_LOAD_FAST__LOAD_FAST, &&TARGET_LOAD_FAST__LOAD_CONST,
&&TARGET_SETUP_ANNOTATIONS, &&TARGET_SETUP_ANNOTATIONS,
&&TARGET_LOAD_FAST__LOAD_FAST,
&&TARGET_LOAD_GLOBAL_BUILTIN, &&TARGET_LOAD_GLOBAL_BUILTIN,
&&TARGET_LOAD_GLOBAL_MODULE, &&TARGET_LOAD_GLOBAL_MODULE,
&&TARGET_STORE_ATTR_INSTANCE_VALUE,
&&TARGET_POP_EXCEPT, &&TARGET_POP_EXCEPT,
&&TARGET_STORE_NAME, &&TARGET_STORE_NAME,
&&TARGET_DELETE_NAME, &&TARGET_DELETE_NAME,
@ -110,9 +110,9 @@ static void *opcode_targets[256] = {
&&TARGET_IMPORT_NAME, &&TARGET_IMPORT_NAME,
&&TARGET_IMPORT_FROM, &&TARGET_IMPORT_FROM,
&&TARGET_JUMP_FORWARD, &&TARGET_JUMP_FORWARD,
&&TARGET_STORE_ATTR_INSTANCE_VALUE,
&&TARGET_STORE_ATTR_SLOT, &&TARGET_STORE_ATTR_SLOT,
&&TARGET_STORE_ATTR_WITH_HINT, &&TARGET_STORE_ATTR_WITH_HINT,
&&TARGET_STORE_FAST__LOAD_FAST,
&&TARGET_POP_JUMP_IF_FALSE, &&TARGET_POP_JUMP_IF_FALSE,
&&TARGET_POP_JUMP_IF_TRUE, &&TARGET_POP_JUMP_IF_TRUE,
&&TARGET_LOAD_GLOBAL, &&TARGET_LOAD_GLOBAL,
@ -142,7 +142,7 @@ static void *opcode_targets[256] = {
&&TARGET_JUMP_BACKWARD, &&TARGET_JUMP_BACKWARD,
&&TARGET_LOAD_SUPER_ATTR, &&TARGET_LOAD_SUPER_ATTR,
&&TARGET_CALL_FUNCTION_EX, &&TARGET_CALL_FUNCTION_EX,
&&TARGET_STORE_FAST__STORE_FAST, &&TARGET_STORE_FAST__LOAD_FAST,
&&TARGET_EXTENDED_ARG, &&TARGET_EXTENDED_ARG,
&&TARGET_LIST_APPEND, &&TARGET_LIST_APPEND,
&&TARGET_SET_ADD, &&TARGET_SET_ADD,
@ -152,20 +152,20 @@ static void *opcode_targets[256] = {
&&TARGET_YIELD_VALUE, &&TARGET_YIELD_VALUE,
&&TARGET_RESUME, &&TARGET_RESUME,
&&TARGET_MATCH_CLASS, &&TARGET_MATCH_CLASS,
&&TARGET_STORE_FAST__STORE_FAST,
&&TARGET_STORE_SUBSCR_DICT, &&TARGET_STORE_SUBSCR_DICT,
&&TARGET_STORE_SUBSCR_LIST_INT,
&&TARGET_FORMAT_VALUE, &&TARGET_FORMAT_VALUE,
&&TARGET_BUILD_CONST_KEY_MAP, &&TARGET_BUILD_CONST_KEY_MAP,
&&TARGET_BUILD_STRING, &&TARGET_BUILD_STRING,
&&TARGET_STORE_SUBSCR_LIST_INT,
&&TARGET_UNPACK_SEQUENCE_LIST, &&TARGET_UNPACK_SEQUENCE_LIST,
&&TARGET_UNPACK_SEQUENCE_TUPLE, &&TARGET_UNPACK_SEQUENCE_TUPLE,
&&TARGET_UNPACK_SEQUENCE_TWO_TUPLE, &&TARGET_UNPACK_SEQUENCE_TWO_TUPLE,
&&TARGET_SEND_GEN,
&&TARGET_LIST_EXTEND, &&TARGET_LIST_EXTEND,
&&TARGET_SET_UPDATE, &&TARGET_SET_UPDATE,
&&TARGET_DICT_MERGE, &&TARGET_DICT_MERGE,
&&TARGET_DICT_UPDATE, &&TARGET_DICT_UPDATE,
&&_unknown_opcode, &&TARGET_SEND_GEN,
&&_unknown_opcode, &&_unknown_opcode,
&&_unknown_opcode, &&_unknown_opcode,
&&_unknown_opcode, &&_unknown_opcode,

View File

@ -96,6 +96,7 @@ _Py_GetSpecializationStats(void) {
return NULL; return NULL;
} }
int err = 0; int err = 0;
err += add_stat_dict(stats, LOAD_SUPER_ATTR, "load_super_attr");
err += add_stat_dict(stats, LOAD_ATTR, "load_attr"); err += add_stat_dict(stats, LOAD_ATTR, "load_attr");
err += add_stat_dict(stats, LOAD_GLOBAL, "load_global"); err += add_stat_dict(stats, LOAD_GLOBAL, "load_global");
err += add_stat_dict(stats, BINARY_SUBSCR, "binary_subscr"); err += add_stat_dict(stats, BINARY_SUBSCR, "binary_subscr");
@ -320,6 +321,14 @@ _PyCode_Quicken(PyCodeObject *code)
#define SPEC_FAIL_LOAD_GLOBAL_NON_DICT 17 #define SPEC_FAIL_LOAD_GLOBAL_NON_DICT 17
#define SPEC_FAIL_LOAD_GLOBAL_NON_STRING_OR_SPLIT 18 #define SPEC_FAIL_LOAD_GLOBAL_NON_STRING_OR_SPLIT 18
/* Super */
#define SPEC_FAIL_SUPER_NOT_LOAD_METHOD 9
#define SPEC_FAIL_SUPER_BAD_CLASS 10
#define SPEC_FAIL_SUPER_SHADOWED 11
#define SPEC_FAIL_SUPER_NOT_METHOD 12
#define SPEC_FAIL_SUPER_ERROR_OR_NOT_FOUND 13
/* Attributes */ /* Attributes */
#define SPEC_FAIL_ATTR_OVERRIDING_DESCRIPTOR 9 #define SPEC_FAIL_ATTR_OVERRIDING_DESCRIPTOR 9
@ -505,6 +514,52 @@ specialize_module_load_attr(
/* Attribute specialization */ /* Attribute specialization */
void
_Py_Specialize_LoadSuperAttr(PyObject *global_super, PyObject *class, PyObject *self,
_Py_CODEUNIT *instr, PyObject *name, int load_method) {
assert(ENABLE_SPECIALIZATION);
assert(_PyOpcode_Caches[LOAD_SUPER_ATTR] == INLINE_CACHE_ENTRIES_LOAD_SUPER_ATTR);
_PySuperAttrCache *cache = (_PySuperAttrCache *)(instr + 1);
if (!load_method) {
SPECIALIZATION_FAIL(LOAD_SUPER_ATTR, SPEC_FAIL_SUPER_NOT_LOAD_METHOD);
goto fail;
}
if (global_super != (PyObject *)&PySuper_Type) {
SPECIALIZATION_FAIL(LOAD_SUPER_ATTR, SPEC_FAIL_SUPER_SHADOWED);
goto fail;
}
if (!PyType_Check(class)) {
SPECIALIZATION_FAIL(LOAD_SUPER_ATTR, SPEC_FAIL_SUPER_BAD_CLASS);
goto fail;
}
PyTypeObject *tp = (PyTypeObject *)class;
PyObject *res = _PySuper_LookupDescr(tp, self, name);
if (res == NULL) {
SPECIALIZATION_FAIL(LOAD_SUPER_ATTR, SPEC_FAIL_SUPER_ERROR_OR_NOT_FOUND);
PyErr_Clear();
goto fail;
}
if (_PyType_HasFeature(Py_TYPE(res), Py_TPFLAGS_METHOD_DESCRIPTOR)) {
write_u32(cache->class_version, tp->tp_version_tag);
write_u32(cache->self_type_version, Py_TYPE(self)->tp_version_tag);
write_obj(cache->method, res); // borrowed
instr->op.code = LOAD_SUPER_ATTR_METHOD;
goto success;
}
SPECIALIZATION_FAIL(LOAD_SUPER_ATTR, SPEC_FAIL_SUPER_NOT_METHOD);
fail:
STAT_INC(LOAD_SUPER_ATTR, failure);
assert(!PyErr_Occurred());
instr->op.code = LOAD_SUPER_ATTR;
cache->counter = adaptive_counter_backoff(cache->counter);
return;
success:
STAT_INC(LOAD_SUPER_ATTR, success);
assert(!PyErr_Occurred());
cache->counter = adaptive_counter_cooldown();
}
typedef enum { typedef enum {
OVERRIDING, /* Is an overriding descriptor, and will remain so. */ OVERRIDING, /* Is an overriding descriptor, and will remain so. */
METHOD, /* Attribute has Py_TPFLAGS_METHOD_DESCRIPTOR set */ METHOD, /* Attribute has Py_TPFLAGS_METHOD_DESCRIPTOR set */