GH-93911: Specialize `LOAD_ATTR` for custom `__getattribute__` (GH-93988)

This commit is contained in:
Ken Jin 2022-08-17 19:37:07 +08:00 committed by GitHub
parent 36517101dd
commit 7276ca25f5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 219 additions and 86 deletions

View File

@ -118,6 +118,7 @@ struct callable_cache {
PyObject *isinstance;
PyObject *len;
PyObject *list_append;
PyObject *object__getattribute__;
};
/* "Locals plus" for a code object is the set of locals + cell vars +

View File

@ -152,6 +152,7 @@ const uint8_t _PyOpcode_Deopt[256] = {
[LOAD_ATTR] = LOAD_ATTR,
[LOAD_ATTR_ADAPTIVE] = LOAD_ATTR,
[LOAD_ATTR_CLASS] = LOAD_ATTR,
[LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN] = LOAD_ATTR,
[LOAD_ATTR_INSTANCE_VALUE] = LOAD_ATTR,
[LOAD_ATTR_METHOD_LAZY_DICT] = LOAD_ATTR,
[LOAD_ATTR_METHOD_NO_DICT] = LOAD_ATTR,
@ -315,20 +316,20 @@ static const char *const _PyOpcode_OpName[267] = {
[PRINT_EXPR] = "PRINT_EXPR",
[LOAD_BUILD_CLASS] = "LOAD_BUILD_CLASS",
[LOAD_ATTR_CLASS] = "LOAD_ATTR_CLASS",
[LOAD_ATTR_INSTANCE_VALUE] = "LOAD_ATTR_INSTANCE_VALUE",
[LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN] = "LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN",
[LOAD_ASSERTION_ERROR] = "LOAD_ASSERTION_ERROR",
[RETURN_GENERATOR] = "RETURN_GENERATOR",
[LOAD_ATTR_INSTANCE_VALUE] = "LOAD_ATTR_INSTANCE_VALUE",
[LOAD_ATTR_MODULE] = "LOAD_ATTR_MODULE",
[LOAD_ATTR_PROPERTY] = "LOAD_ATTR_PROPERTY",
[LOAD_ATTR_SLOT] = "LOAD_ATTR_SLOT",
[LOAD_ATTR_WITH_HINT] = "LOAD_ATTR_WITH_HINT",
[LOAD_ATTR_METHOD_LAZY_DICT] = "LOAD_ATTR_METHOD_LAZY_DICT",
[LOAD_ATTR_METHOD_NO_DICT] = "LOAD_ATTR_METHOD_NO_DICT",
[LIST_TO_TUPLE] = "LIST_TO_TUPLE",
[RETURN_VALUE] = "RETURN_VALUE",
[IMPORT_STAR] = "IMPORT_STAR",
[SETUP_ANNOTATIONS] = "SETUP_ANNOTATIONS",
[LOAD_ATTR_METHOD_WITH_DICT] = "LOAD_ATTR_METHOD_WITH_DICT",
[LOAD_ATTR_METHOD_NO_DICT] = "LOAD_ATTR_METHOD_NO_DICT",
[ASYNC_GEN_WRAP] = "ASYNC_GEN_WRAP",
[PREP_RERAISE_STAR] = "PREP_RERAISE_STAR",
[POP_EXCEPT] = "POP_EXCEPT",
@ -355,7 +356,7 @@ static const char *const _PyOpcode_OpName[267] = {
[JUMP_FORWARD] = "JUMP_FORWARD",
[JUMP_IF_FALSE_OR_POP] = "JUMP_IF_FALSE_OR_POP",
[JUMP_IF_TRUE_OR_POP] = "JUMP_IF_TRUE_OR_POP",
[LOAD_ATTR_METHOD_WITH_VALUES] = "LOAD_ATTR_METHOD_WITH_VALUES",
[LOAD_ATTR_METHOD_WITH_DICT] = "LOAD_ATTR_METHOD_WITH_DICT",
[POP_JUMP_FORWARD_IF_FALSE] = "POP_JUMP_FORWARD_IF_FALSE",
[POP_JUMP_FORWARD_IF_TRUE] = "POP_JUMP_FORWARD_IF_TRUE",
[LOAD_GLOBAL] = "LOAD_GLOBAL",
@ -363,7 +364,7 @@ static const char *const _PyOpcode_OpName[267] = {
[CONTAINS_OP] = "CONTAINS_OP",
[RERAISE] = "RERAISE",
[COPY] = "COPY",
[LOAD_CONST__LOAD_FAST] = "LOAD_CONST__LOAD_FAST",
[LOAD_ATTR_METHOD_WITH_VALUES] = "LOAD_ATTR_METHOD_WITH_VALUES",
[BINARY_OP] = "BINARY_OP",
[SEND] = "SEND",
[LOAD_FAST] = "LOAD_FAST",
@ -383,9 +384,9 @@ static const char *const _PyOpcode_OpName[267] = {
[STORE_DEREF] = "STORE_DEREF",
[DELETE_DEREF] = "DELETE_DEREF",
[JUMP_BACKWARD] = "JUMP_BACKWARD",
[LOAD_FAST__LOAD_CONST] = "LOAD_FAST__LOAD_CONST",
[LOAD_CONST__LOAD_FAST] = "LOAD_CONST__LOAD_FAST",
[CALL_FUNCTION_EX] = "CALL_FUNCTION_EX",
[LOAD_FAST__LOAD_FAST] = "LOAD_FAST__LOAD_FAST",
[LOAD_FAST__LOAD_CONST] = "LOAD_FAST__LOAD_CONST",
[EXTENDED_ARG] = "EXTENDED_ARG",
[LIST_APPEND] = "LIST_APPEND",
[SET_ADD] = "SET_ADD",
@ -395,37 +396,37 @@ static const char *const _PyOpcode_OpName[267] = {
[YIELD_VALUE] = "YIELD_VALUE",
[RESUME] = "RESUME",
[MATCH_CLASS] = "MATCH_CLASS",
[LOAD_FAST__LOAD_FAST] = "LOAD_FAST__LOAD_FAST",
[LOAD_GLOBAL_ADAPTIVE] = "LOAD_GLOBAL_ADAPTIVE",
[LOAD_GLOBAL_BUILTIN] = "LOAD_GLOBAL_BUILTIN",
[FORMAT_VALUE] = "FORMAT_VALUE",
[BUILD_CONST_KEY_MAP] = "BUILD_CONST_KEY_MAP",
[BUILD_STRING] = "BUILD_STRING",
[LOAD_GLOBAL_BUILTIN] = "LOAD_GLOBAL_BUILTIN",
[LOAD_GLOBAL_MODULE] = "LOAD_GLOBAL_MODULE",
[RESUME_QUICK] = "RESUME_QUICK",
[STORE_ATTR_ADAPTIVE] = "STORE_ATTR_ADAPTIVE",
[STORE_ATTR_INSTANCE_VALUE] = "STORE_ATTR_INSTANCE_VALUE",
[LIST_EXTEND] = "LIST_EXTEND",
[SET_UPDATE] = "SET_UPDATE",
[DICT_MERGE] = "DICT_MERGE",
[DICT_UPDATE] = "DICT_UPDATE",
[STORE_ATTR_INSTANCE_VALUE] = "STORE_ATTR_INSTANCE_VALUE",
[STORE_ATTR_SLOT] = "STORE_ATTR_SLOT",
[STORE_ATTR_WITH_HINT] = "STORE_ATTR_WITH_HINT",
[STORE_FAST__LOAD_FAST] = "STORE_FAST__LOAD_FAST",
[STORE_FAST__STORE_FAST] = "STORE_FAST__STORE_FAST",
[STORE_SUBSCR_ADAPTIVE] = "STORE_SUBSCR_ADAPTIVE",
[CALL] = "CALL",
[KW_NAMES] = "KW_NAMES",
[POP_JUMP_BACKWARD_IF_NOT_NONE] = "POP_JUMP_BACKWARD_IF_NOT_NONE",
[POP_JUMP_BACKWARD_IF_NONE] = "POP_JUMP_BACKWARD_IF_NONE",
[POP_JUMP_BACKWARD_IF_FALSE] = "POP_JUMP_BACKWARD_IF_FALSE",
[POP_JUMP_BACKWARD_IF_TRUE] = "POP_JUMP_BACKWARD_IF_TRUE",
[STORE_SUBSCR_ADAPTIVE] = "STORE_SUBSCR_ADAPTIVE",
[STORE_SUBSCR_DICT] = "STORE_SUBSCR_DICT",
[STORE_SUBSCR_LIST_INT] = "STORE_SUBSCR_LIST_INT",
[UNPACK_SEQUENCE_ADAPTIVE] = "UNPACK_SEQUENCE_ADAPTIVE",
[UNPACK_SEQUENCE_LIST] = "UNPACK_SEQUENCE_LIST",
[UNPACK_SEQUENCE_TUPLE] = "UNPACK_SEQUENCE_TUPLE",
[UNPACK_SEQUENCE_TWO_TUPLE] = "UNPACK_SEQUENCE_TWO_TUPLE",
[183] = "<183>",
[184] = "<184>",
[185] = "<185>",
[186] = "<186>",
@ -513,7 +514,6 @@ static const char *const _PyOpcode_OpName[267] = {
#endif
#define EXTRA_CASES \
case 183: \
case 184: \
case 185: \
case 186: \

View File

@ -75,6 +75,9 @@ extern void _PyStaticType_ClearWeakRefs(PyTypeObject *type);
extern void _PyStaticType_Dealloc(PyTypeObject *type);
PyObject *_Py_slot_tp_getattro(PyObject *self, PyObject *name);
PyObject *_Py_slot_tp_getattr_hook(PyObject *self, PyObject *name);
#ifdef __cplusplus
}
#endif

59
Include/opcode.h generated
View File

@ -176,35 +176,36 @@ extern "C" {
#define JUMP_BACKWARD_QUICK 66
#define LOAD_ATTR_ADAPTIVE 67
#define LOAD_ATTR_CLASS 72
#define LOAD_ATTR_INSTANCE_VALUE 73
#define LOAD_ATTR_MODULE 76
#define LOAD_ATTR_PROPERTY 77
#define LOAD_ATTR_SLOT 78
#define LOAD_ATTR_WITH_HINT 79
#define LOAD_ATTR_METHOD_LAZY_DICT 80
#define LOAD_ATTR_METHOD_NO_DICT 81
#define LOAD_ATTR_METHOD_WITH_DICT 86
#define LOAD_ATTR_METHOD_WITH_VALUES 113
#define LOAD_CONST__LOAD_FAST 121
#define LOAD_FAST__LOAD_CONST 141
#define LOAD_FAST__LOAD_FAST 143
#define LOAD_GLOBAL_ADAPTIVE 153
#define LOAD_GLOBAL_BUILTIN 154
#define LOAD_GLOBAL_MODULE 158
#define RESUME_QUICK 159
#define STORE_ATTR_ADAPTIVE 160
#define STORE_ATTR_INSTANCE_VALUE 161
#define STORE_ATTR_SLOT 166
#define STORE_ATTR_WITH_HINT 167
#define STORE_FAST__LOAD_FAST 168
#define STORE_FAST__STORE_FAST 169
#define STORE_SUBSCR_ADAPTIVE 170
#define STORE_SUBSCR_DICT 177
#define STORE_SUBSCR_LIST_INT 178
#define UNPACK_SEQUENCE_ADAPTIVE 179
#define UNPACK_SEQUENCE_LIST 180
#define UNPACK_SEQUENCE_TUPLE 181
#define UNPACK_SEQUENCE_TWO_TUPLE 182
#define LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN 73
#define LOAD_ATTR_INSTANCE_VALUE 76
#define LOAD_ATTR_MODULE 77
#define LOAD_ATTR_PROPERTY 78
#define LOAD_ATTR_SLOT 79
#define LOAD_ATTR_WITH_HINT 80
#define LOAD_ATTR_METHOD_LAZY_DICT 81
#define LOAD_ATTR_METHOD_NO_DICT 86
#define LOAD_ATTR_METHOD_WITH_DICT 113
#define LOAD_ATTR_METHOD_WITH_VALUES 121
#define LOAD_CONST__LOAD_FAST 141
#define LOAD_FAST__LOAD_CONST 143
#define LOAD_FAST__LOAD_FAST 153
#define LOAD_GLOBAL_ADAPTIVE 154
#define LOAD_GLOBAL_BUILTIN 158
#define LOAD_GLOBAL_MODULE 159
#define RESUME_QUICK 160
#define STORE_ATTR_ADAPTIVE 161
#define STORE_ATTR_INSTANCE_VALUE 166
#define STORE_ATTR_SLOT 167
#define STORE_ATTR_WITH_HINT 168
#define STORE_FAST__LOAD_FAST 169
#define STORE_FAST__STORE_FAST 170
#define STORE_SUBSCR_ADAPTIVE 177
#define STORE_SUBSCR_DICT 178
#define STORE_SUBSCR_LIST_INT 179
#define UNPACK_SEQUENCE_ADAPTIVE 180
#define UNPACK_SEQUENCE_LIST 181
#define UNPACK_SEQUENCE_TUPLE 182
#define UNPACK_SEQUENCE_TWO_TUPLE 183
#define DO_TRACING 255
#define HAS_ARG(op) ((((op) >= HAVE_ARGUMENT) && (!IS_PSEUDO_OPCODE(op)))\

View File

@ -338,6 +338,7 @@ _specializations = {
"LOAD_ATTR_ADAPTIVE",
# These potentially push [NULL, bound method] onto the stack.
"LOAD_ATTR_CLASS",
"LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN",
"LOAD_ATTR_INSTANCE_VALUE",
"LOAD_ATTR_MODULE",
"LOAD_ATTR_PROPERTY",

View File

@ -0,0 +1 @@
Specialize ``LOAD_ATTR`` for objects with custom ``__getattr__`` and ``__getattribute__``.

View File

@ -8102,17 +8102,17 @@ slot_tp_call(PyObject *self, PyObject *args, PyObject *kwds)
/* There are two slot dispatch functions for tp_getattro.
- slot_tp_getattro() is used when __getattribute__ is overridden
- _Py_slot_tp_getattro() is used when __getattribute__ is overridden
but no __getattr__ hook is present;
- slot_tp_getattr_hook() is used when a __getattr__ hook is present.
- _Py_slot_tp_getattr_hook() is used when a __getattr__ hook is present.
The code in update_one_slot() always installs slot_tp_getattr_hook(); this
detects the absence of __getattr__ and then installs the simpler slot if
necessary. */
The code in update_one_slot() always installs _Py_slot_tp_getattr_hook();
this detects the absence of __getattr__ and then installs the simpler
slot if necessary. */
static PyObject *
slot_tp_getattro(PyObject *self, PyObject *name)
PyObject *
_Py_slot_tp_getattro(PyObject *self, PyObject *name)
{
PyObject *stack[2] = {self, name};
return vectorcall_method(&_Py_ID(__getattribute__), stack, 2);
@ -8143,8 +8143,8 @@ call_attribute(PyObject *self, PyObject *attr, PyObject *name)
return res;
}
static PyObject *
slot_tp_getattr_hook(PyObject *self, PyObject *name)
PyObject *
_Py_slot_tp_getattr_hook(PyObject *self, PyObject *name)
{
PyTypeObject *tp = Py_TYPE(self);
PyObject *getattr, *getattribute, *res;
@ -8157,8 +8157,8 @@ slot_tp_getattr_hook(PyObject *self, PyObject *name)
getattr = _PyType_Lookup(tp, &_Py_ID(__getattr__));
if (getattr == NULL) {
/* No __getattr__ hook: use a simpler dispatcher */
tp->tp_getattro = slot_tp_getattro;
return slot_tp_getattro(self, name);
tp->tp_getattro = _Py_slot_tp_getattro;
return _Py_slot_tp_getattro(self, name);
}
Py_INCREF(getattr);
/* speed hack: we could use lookup_maybe, but that would resolve the
@ -8519,10 +8519,10 @@ static slotdef slotdefs[] = {
PyWrapperFlag_KEYWORDS),
TPSLOT("__str__", tp_str, slot_tp_str, wrap_unaryfunc,
"__str__($self, /)\n--\n\nReturn str(self)."),
TPSLOT("__getattribute__", tp_getattro, slot_tp_getattr_hook,
TPSLOT("__getattribute__", tp_getattro, _Py_slot_tp_getattr_hook,
wrap_binaryfunc,
"__getattribute__($self, name, /)\n--\n\nReturn getattr(self, name)."),
TPSLOT("__getattr__", tp_getattro, slot_tp_getattr_hook, NULL, ""),
TPSLOT("__getattr__", tp_getattro, _Py_slot_tp_getattr_hook, NULL, ""),
TPSLOT("__setattr__", tp_setattro, slot_tp_setattro, wrap_setattr,
"__setattr__($self, name, value, /)\n--\n\nImplement setattr(self, name, value)."),
TPSLOT("__delattr__", tp_setattro, slot_tp_setattro, wrap_delattr,

View File

@ -3698,6 +3698,7 @@ handle_eval_breaker:
DEOPT_IF(cls->tp_version_tag != type_version, LOAD_ATTR);
assert(type_version != 0);
PyObject *fget = read_obj(cache->descr);
assert(Py_IS_TYPE(fget, &PyFunction_Type));
PyFunctionObject *f = (PyFunctionObject *)fget;
uint32_t func_version = read_u32(cache->keys_version);
assert(func_version != 0);
@ -3709,8 +3710,8 @@ handle_eval_breaker:
Py_INCREF(fget);
_PyInterpreterFrame *new_frame = _PyFrame_PushUnchecked(tstate, f);
SET_TOP(NULL);
int push_null = !(oparg & 1);
STACK_SHRINK(push_null);
int shrink_stack = !(oparg & 1);
STACK_SHRINK(shrink_stack);
new_frame->localsplus[0] = owner;
for (int i = 1; i < code->co_nlocalsplus; i++) {
new_frame->localsplus[i] = NULL;
@ -3724,6 +3725,44 @@ handle_eval_breaker:
goto start_frame;
}
TARGET(LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN) {
assert(cframe.use_tracing == 0);
DEOPT_IF(tstate->interp->eval_frame, LOAD_ATTR);
_PyLoadMethodCache *cache = (_PyLoadMethodCache *)next_instr;
PyObject *owner = TOP();
PyTypeObject *cls = Py_TYPE(owner);
uint32_t type_version = read_u32(cache->type_version);
DEOPT_IF(cls->tp_version_tag != type_version, LOAD_ATTR);
assert(type_version != 0);
PyObject *getattribute = read_obj(cache->descr);
assert(Py_IS_TYPE(getattribute, &PyFunction_Type));
PyFunctionObject *f = (PyFunctionObject *)getattribute;
PyCodeObject *code = (PyCodeObject *)f->func_code;
DEOPT_IF(((PyCodeObject *)f->func_code)->co_argcount != 2, LOAD_ATTR);
DEOPT_IF(!_PyThreadState_HasStackSpace(tstate, code->co_framesize), CALL);
STAT_INC(LOAD_ATTR, hit);
PyObject *name = GETITEM(names, oparg >> 1);
Py_INCREF(f);
_PyInterpreterFrame *new_frame = _PyFrame_PushUnchecked(tstate, f);
SET_TOP(NULL);
int shrink_stack = !(oparg & 1);
STACK_SHRINK(shrink_stack);
Py_INCREF(name);
new_frame->localsplus[0] = owner;
new_frame->localsplus[1] = name;
for (int i = 2; i < code->co_nlocalsplus; i++) {
new_frame->localsplus[i] = NULL;
}
_PyFrame_SetStackPointer(frame, stack_pointer);
JUMPBY(INLINE_CACHE_ENTRIES_LOAD_ATTR);
frame->prev_instr = next_instr - 1;
new_frame->previous = frame;
frame = cframe.current_frame = new_frame;
CALL_STAT_INC(inlined_py_calls);
goto start_frame;
}
TARGET(STORE_ATTR_ADAPTIVE) {
assert(cframe.use_tracing == 0);
_PyAttrCache *cache = (_PyAttrCache *)next_instr;

View File

@ -72,20 +72,20 @@ static void *opcode_targets[256] = {
&&TARGET_PRINT_EXPR,
&&TARGET_LOAD_BUILD_CLASS,
&&TARGET_LOAD_ATTR_CLASS,
&&TARGET_LOAD_ATTR_INSTANCE_VALUE,
&&TARGET_LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN,
&&TARGET_LOAD_ASSERTION_ERROR,
&&TARGET_RETURN_GENERATOR,
&&TARGET_LOAD_ATTR_INSTANCE_VALUE,
&&TARGET_LOAD_ATTR_MODULE,
&&TARGET_LOAD_ATTR_PROPERTY,
&&TARGET_LOAD_ATTR_SLOT,
&&TARGET_LOAD_ATTR_WITH_HINT,
&&TARGET_LOAD_ATTR_METHOD_LAZY_DICT,
&&TARGET_LOAD_ATTR_METHOD_NO_DICT,
&&TARGET_LIST_TO_TUPLE,
&&TARGET_RETURN_VALUE,
&&TARGET_IMPORT_STAR,
&&TARGET_SETUP_ANNOTATIONS,
&&TARGET_LOAD_ATTR_METHOD_WITH_DICT,
&&TARGET_LOAD_ATTR_METHOD_NO_DICT,
&&TARGET_ASYNC_GEN_WRAP,
&&TARGET_PREP_RERAISE_STAR,
&&TARGET_POP_EXCEPT,
@ -112,7 +112,7 @@ static void *opcode_targets[256] = {
&&TARGET_JUMP_FORWARD,
&&TARGET_JUMP_IF_FALSE_OR_POP,
&&TARGET_JUMP_IF_TRUE_OR_POP,
&&TARGET_LOAD_ATTR_METHOD_WITH_VALUES,
&&TARGET_LOAD_ATTR_METHOD_WITH_DICT,
&&TARGET_POP_JUMP_FORWARD_IF_FALSE,
&&TARGET_POP_JUMP_FORWARD_IF_TRUE,
&&TARGET_LOAD_GLOBAL,
@ -120,7 +120,7 @@ static void *opcode_targets[256] = {
&&TARGET_CONTAINS_OP,
&&TARGET_RERAISE,
&&TARGET_COPY,
&&TARGET_LOAD_CONST__LOAD_FAST,
&&TARGET_LOAD_ATTR_METHOD_WITH_VALUES,
&&TARGET_BINARY_OP,
&&TARGET_SEND,
&&TARGET_LOAD_FAST,
@ -140,9 +140,9 @@ static void *opcode_targets[256] = {
&&TARGET_STORE_DEREF,
&&TARGET_DELETE_DEREF,
&&TARGET_JUMP_BACKWARD,
&&TARGET_LOAD_FAST__LOAD_CONST,
&&TARGET_LOAD_CONST__LOAD_FAST,
&&TARGET_CALL_FUNCTION_EX,
&&TARGET_LOAD_FAST__LOAD_FAST,
&&TARGET_LOAD_FAST__LOAD_CONST,
&&TARGET_EXTENDED_ARG,
&&TARGET_LIST_APPEND,
&&TARGET_SET_ADD,
@ -152,30 +152,31 @@ static void *opcode_targets[256] = {
&&TARGET_YIELD_VALUE,
&&TARGET_RESUME,
&&TARGET_MATCH_CLASS,
&&TARGET_LOAD_FAST__LOAD_FAST,
&&TARGET_LOAD_GLOBAL_ADAPTIVE,
&&TARGET_LOAD_GLOBAL_BUILTIN,
&&TARGET_FORMAT_VALUE,
&&TARGET_BUILD_CONST_KEY_MAP,
&&TARGET_BUILD_STRING,
&&TARGET_LOAD_GLOBAL_BUILTIN,
&&TARGET_LOAD_GLOBAL_MODULE,
&&TARGET_RESUME_QUICK,
&&TARGET_STORE_ATTR_ADAPTIVE,
&&TARGET_STORE_ATTR_INSTANCE_VALUE,
&&TARGET_LIST_EXTEND,
&&TARGET_SET_UPDATE,
&&TARGET_DICT_MERGE,
&&TARGET_DICT_UPDATE,
&&TARGET_STORE_ATTR_INSTANCE_VALUE,
&&TARGET_STORE_ATTR_SLOT,
&&TARGET_STORE_ATTR_WITH_HINT,
&&TARGET_STORE_FAST__LOAD_FAST,
&&TARGET_STORE_FAST__STORE_FAST,
&&TARGET_STORE_SUBSCR_ADAPTIVE,
&&TARGET_CALL,
&&TARGET_KW_NAMES,
&&TARGET_POP_JUMP_BACKWARD_IF_NOT_NONE,
&&TARGET_POP_JUMP_BACKWARD_IF_NONE,
&&TARGET_POP_JUMP_BACKWARD_IF_FALSE,
&&TARGET_POP_JUMP_BACKWARD_IF_TRUE,
&&TARGET_STORE_SUBSCR_ADAPTIVE,
&&TARGET_STORE_SUBSCR_DICT,
&&TARGET_STORE_SUBSCR_LIST_INT,
&&TARGET_UNPACK_SEQUENCE_ADAPTIVE,
@ -253,6 +254,5 @@ static void *opcode_targets[256] = {
&&_unknown_opcode,
&&_unknown_opcode,
&&_unknown_opcode,
&&_unknown_opcode,
&&TARGET_DO_TRACING
};

View File

@ -787,6 +787,9 @@ pycore_init_builtins(PyThreadState *tstate)
PyObject *list_append = _PyType_Lookup(&PyList_Type, &_Py_ID(append));
assert(list_append);
interp->callable_cache.list_append = list_append;
PyObject *object__getattribute__ = _PyType_Lookup(&PyBaseObject_Type, &_Py_ID(__getattribute__));
assert(object__getattribute__);
interp->callable_cache.object__getattribute__ = object__getattribute__;
if (_PyBuiltins_AddExceptions(bimod) < 0) {
return _PyStatus_ERR("failed to add exceptions to builtins");

View File

@ -362,6 +362,8 @@ miss_counter_start(void) {
#define SPEC_FAIL_OUT_OF_RANGE 4
#define SPEC_FAIL_EXPECTED_ERROR 5
#define SPEC_FAIL_WRONG_NUMBER_ARGUMENTS 6
#define SPEC_FAIL_NOT_PY_FUNCTION 7
#define SPEC_FAIL_LOAD_GLOBAL_NON_STRING_OR_SPLIT 18
@ -387,6 +389,7 @@ miss_counter_start(void) {
#define SPEC_FAIL_ATTR_HAS_MANAGED_DICT 25
#define SPEC_FAIL_ATTR_INSTANCE_ATTRIBUTE 26
#define SPEC_FAIL_ATTR_METACLASS_ATTRIBUTE 27
#define SPEC_FAIL_ATTR_PROPERTY_NOT_PY_FUNCTION 28
/* Binary subscr and store subscr */
@ -498,6 +501,9 @@ miss_counter_start(void) {
#define SPEC_FAIL_UNPACK_SEQUENCE_ITERATOR 8
#define SPEC_FAIL_UNPACK_SEQUENCE_SEQUENCE 9
static int function_kind(PyCodeObject *code);
static bool function_check_args(PyObject *o, int expected_argcount, int opcode);
static uint32_t function_get_version(PyObject *o, int opcode);
static int
specialize_module_load_attr(PyObject *owner, _Py_CODEUNIT *instr,
@ -557,13 +563,15 @@ typedef enum {
MUTABLE, /* Instance of a mutable class; might, or might not, be a descriptor */
ABSENT, /* Attribute is not present on the class */
DUNDER_CLASS, /* __class__ attribute */
GETSET_OVERRIDDEN /* __getattribute__ or __setattr__ has been overridden */
GETSET_OVERRIDDEN, /* __getattribute__ or __setattr__ has been overridden */
GETATTRIBUTE_IS_PYTHON_FUNCTION /* Descriptor requires calling a Python __getattribute__ */
} DescriptorClassification;
static DescriptorClassification
analyze_descriptor(PyTypeObject *type, PyObject *name, PyObject **descr, int store)
{
bool has_getattr = false;
if (store) {
if (type->tp_setattro != PyObject_GenericSetAttr) {
*descr = NULL;
@ -571,7 +579,42 @@ analyze_descriptor(PyTypeObject *type, PyObject *name, PyObject **descr, int sto
}
}
else {
if (type->tp_getattro != PyObject_GenericGetAttr) {
getattrofunc getattro_slot = type->tp_getattro;
if (getattro_slot == PyObject_GenericGetAttr) {
/* Normal attribute lookup; */
has_getattr = false;
}
else if (getattro_slot == _Py_slot_tp_getattr_hook ||
getattro_slot == _Py_slot_tp_getattro) {
/* One or both of __getattribute__ or __getattr__ may have been
overridden See typeobject.c for why these functions are special. */
PyObject *getattribute = _PyType_Lookup(type,
&_Py_ID(__getattribute__));
PyInterpreterState *interp = _PyInterpreterState_GET();
bool has_custom_getattribute = getattribute != NULL &&
getattribute != interp->callable_cache.object__getattribute__;
has_getattr = _PyType_Lookup(type, &_Py_ID(__getattr__)) != NULL;
if (has_custom_getattribute) {
if (getattro_slot == _Py_slot_tp_getattro &&
!has_getattr &&
Py_IS_TYPE(getattribute, &PyFunction_Type)) {
*descr = getattribute;
return GETATTRIBUTE_IS_PYTHON_FUNCTION;
}
/* Potentially both __getattr__ and __getattribute__ are set.
Too complicated */
*descr = NULL;
return GETSET_OVERRIDDEN;
}
/* Potentially has __getattr__ but no custom __getattribute__.
Fall through to usual descriptor analysis.
Usual attribute lookup should only be allowed at runtime
if we can guarantee that there is no way an exception can be
raised. This means some specializations, e.g. specializing
for property() isn't safe.
*/
}
else {
*descr = NULL;
return GETSET_OVERRIDDEN;
}
@ -595,7 +638,10 @@ analyze_descriptor(PyTypeObject *type, PyObject *name, PyObject **descr, int sto
return OTHER_SLOT;
}
if (desc_cls == &PyProperty_Type) {
return PROPERTY;
/* We can't detect at runtime whether an attribute exists
with property. So that means we may have to call
__getattr__. */
return has_getattr ? GETSET_OVERRIDDEN : PROPERTY;
}
if (PyUnicode_CompareWithASCIIString(name, "__class__") == 0) {
if (descriptor == _PyType_Lookup(&PyBaseObject_Type, name)) {
@ -674,7 +720,6 @@ specialize_dict_access(
static int specialize_attr_loadmethod(PyObject* owner, _Py_CODEUNIT* instr, PyObject* name,
PyObject* descr, DescriptorClassification kind);
static int specialize_class_load_attr(PyObject* owner, _Py_CODEUNIT* instr, PyObject* name);
static int function_kind(PyCodeObject *code);
int
_Py_Specialize_LoadAttr(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name)
@ -729,24 +774,13 @@ _Py_Specialize_LoadAttr(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name)
SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_EXPECTED_ERROR);
goto fail;
}
if (Py_TYPE(fget) != &PyFunction_Type) {
SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_ATTR_PROPERTY);
if (!Py_IS_TYPE(fget, &PyFunction_Type)) {
SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_ATTR_PROPERTY_NOT_PY_FUNCTION);
goto fail;
}
PyFunctionObject *func = (PyFunctionObject *)fget;
PyCodeObject *fcode = (PyCodeObject *)func->func_code;
int kind = function_kind(fcode);
if (kind != SIMPLE_FUNCTION) {
SPECIALIZATION_FAIL(LOAD_ATTR, kind);
goto fail;
}
if (fcode->co_argcount != 1) {
SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_WRONG_NUMBER_ARGUMENTS);
goto fail;
}
int version = _PyFunction_GetVersionForCurrentState(func);
uint32_t version = function_check_args(fget, 1, LOAD_ATTR) &&
function_get_version(fget, LOAD_ATTR);
if (version == 0) {
SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_OUT_OF_VERSIONS);
goto fail;
}
write_u32(lm_cache->keys_version, version);
@ -795,6 +829,22 @@ _Py_Specialize_LoadAttr(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name)
case GETSET_OVERRIDDEN:
SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_OVERRIDDEN);
goto fail;
case GETATTRIBUTE_IS_PYTHON_FUNCTION:
{
assert(type->tp_getattro == _Py_slot_tp_getattro);
assert(Py_IS_TYPE(descr, &PyFunction_Type));
_PyLoadMethodCache *lm_cache = (_PyLoadMethodCache *)(instr + 1);
uint32_t func_version = function_check_args(descr, 2, LOAD_ATTR) &&
function_get_version(descr, LOAD_ATTR);
if (func_version == 0) {
goto fail;
}
/* borrowed */
write_obj(lm_cache->descr, descr);
write_u32(lm_cache->type_version, type->tp_version_tag);
_Py_SET_OPCODE(*instr, LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN);
goto success;
}
case BUILTIN_CLASSMETHOD:
case PYTHON_CLASSMETHOD:
case NON_OVERRIDING:
@ -873,6 +923,7 @@ _Py_Specialize_StoreAttr(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name)
case MUTABLE:
SPECIALIZATION_FAIL(STORE_ATTR, SPEC_FAIL_ATTR_MUTABLE_CLASS);
goto fail;
case GETATTRIBUTE_IS_PYTHON_FUNCTION:
case GETSET_OVERRIDDEN:
SPECIALIZATION_FAIL(STORE_ATTR, SPEC_FAIL_OVERRIDDEN);
goto fail;
@ -1215,6 +1266,39 @@ function_kind(PyCodeObject *code) {
return SIMPLE_FUNCTION;
}
/* Returning false indicates a failure. */
static bool
function_check_args(PyObject *o, int expected_argcount, int opcode)
{
assert(Py_IS_TYPE(o, &PyFunction_Type));
PyFunctionObject *func = (PyFunctionObject *)o;
PyCodeObject *fcode = (PyCodeObject *)func->func_code;
int kind = function_kind(fcode);
if (kind != SIMPLE_FUNCTION) {
SPECIALIZATION_FAIL(opcode, kind);
return false;
}
if (fcode->co_argcount != expected_argcount) {
SPECIALIZATION_FAIL(opcode, SPEC_FAIL_WRONG_NUMBER_ARGUMENTS);
return false;
}
return true;
}
/* Returning 0 indicates a failure. */
static uint32_t
function_get_version(PyObject *o, int opcode)
{
assert(Py_IS_TYPE(o, &PyFunction_Type));
PyFunctionObject *func = (PyFunctionObject *)o;
uint32_t version = _PyFunction_GetVersionForCurrentState(func);
if (version == 0) {
SPECIALIZATION_FAIL(opcode, SPEC_FAIL_OUT_OF_VERSIONS);
return 0;
}
return version;
}
int
_Py_Specialize_BinarySubscr(
PyObject *container, PyObject *sub, _Py_CODEUNIT *instr)