GH-99298: Clean up attribute specializations (GH-99398)

This commit is contained in:
Brandt Bucher 2022-11-17 15:09:18 -08:00 committed by GitHub
parent 8555dee5ae
commit b629fdd88a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 39 additions and 57 deletions

View file

@ -213,10 +213,10 @@ extern int _PyLineTable_PreviousAddressRange(PyCodeAddressRange *range);
/* Specialization functions */ /* Specialization functions */
extern int _Py_Specialize_LoadAttr(PyObject *owner, _Py_CODEUNIT *instr, extern void _Py_Specialize_LoadAttr(PyObject *owner, _Py_CODEUNIT *instr,
PyObject *name);
extern int _Py_Specialize_StoreAttr(PyObject *owner, _Py_CODEUNIT *instr,
PyObject *name); PyObject *name);
extern void _Py_Specialize_StoreAttr(PyObject *owner, _Py_CODEUNIT *instr,
PyObject *name);
extern void _Py_Specialize_LoadGlobal(PyObject *globals, PyObject *builtins, extern void _Py_Specialize_LoadGlobal(PyObject *globals, PyObject *builtins,
_Py_CODEUNIT *instr, PyObject *name); _Py_CODEUNIT *instr, PyObject *name);
extern void _Py_Specialize_BinarySubscr(PyObject *sub, PyObject *container, extern void _Py_Specialize_BinarySubscr(PyObject *sub, PyObject *container,

View file

@ -0,0 +1,3 @@
Remove the remaining error paths for attribute specializations, and refuse
to specialize attribute accesses on types that haven't had
:c:func:`PyType_Ready` called on them yet.

View file

@ -1137,11 +1137,7 @@ dummy_func(
PyObject *owner = TOP(); PyObject *owner = TOP();
PyObject *name = GETITEM(names, oparg); PyObject *name = GETITEM(names, oparg);
next_instr--; next_instr--;
if (_Py_Specialize_StoreAttr(owner, next_instr, name)) { _Py_Specialize_StoreAttr(owner, next_instr, name);
// "undo" the rewind so end up in the correct handler:
next_instr++;
goto error;
}
DISPATCH_SAME_OPARG(); DISPATCH_SAME_OPARG();
} }
STAT_INC(STORE_ATTR, deferred); STAT_INC(STORE_ATTR, deferred);
@ -1713,11 +1709,7 @@ dummy_func(
PyObject *owner = TOP(); PyObject *owner = TOP();
PyObject *name = GETITEM(names, oparg>>1); PyObject *name = GETITEM(names, oparg>>1);
next_instr--; next_instr--;
if (_Py_Specialize_LoadAttr(owner, next_instr, name)) { _Py_Specialize_LoadAttr(owner, next_instr, name);
// "undo" the rewind so end up in the correct handler:
next_instr++;
goto error;
}
DISPATCH_SAME_OPARG(); DISPATCH_SAME_OPARG();
} }
STAT_INC(LOAD_ATTR, deferred); STAT_INC(LOAD_ATTR, deferred);

View file

@ -1136,11 +1136,7 @@
PyObject *owner = TOP(); PyObject *owner = TOP();
PyObject *name = GETITEM(names, oparg); PyObject *name = GETITEM(names, oparg);
next_instr--; next_instr--;
if (_Py_Specialize_StoreAttr(owner, next_instr, name)) { _Py_Specialize_StoreAttr(owner, next_instr, name);
// "undo" the rewind so end up in the correct handler:
next_instr++;
goto error;
}
DISPATCH_SAME_OPARG(); DISPATCH_SAME_OPARG();
} }
STAT_INC(STORE_ATTR, deferred); STAT_INC(STORE_ATTR, deferred);
@ -1718,11 +1714,7 @@
PyObject *owner = TOP(); PyObject *owner = TOP();
PyObject *name = GETITEM(names, oparg>>1); PyObject *name = GETITEM(names, oparg>>1);
next_instr--; next_instr--;
if (_Py_Specialize_LoadAttr(owner, next_instr, name)) { _Py_Specialize_LoadAttr(owner, next_instr, name);
// "undo" the rewind so end up in the correct handler:
next_instr++;
goto error;
}
DISPATCH_SAME_OPARG(); DISPATCH_SAME_OPARG();
} }
STAT_INC(LOAD_ATTR, deferred); STAT_INC(LOAD_ATTR, deferred);

View file

@ -663,32 +663,33 @@ static int specialize_attr_loadmethod(PyObject* owner, _Py_CODEUNIT* instr, PyOb
PyObject* descr, DescriptorClassification kind); PyObject* descr, DescriptorClassification kind);
static int specialize_class_load_attr(PyObject* owner, _Py_CODEUNIT* instr, PyObject* name); static int specialize_class_load_attr(PyObject* owner, _Py_CODEUNIT* instr, PyObject* name);
int void
_Py_Specialize_LoadAttr(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name) _Py_Specialize_LoadAttr(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name)
{ {
assert(_PyOpcode_Caches[LOAD_ATTR] == INLINE_CACHE_ENTRIES_LOAD_ATTR); assert(_PyOpcode_Caches[LOAD_ATTR] == INLINE_CACHE_ENTRIES_LOAD_ATTR);
_PyAttrCache *cache = (_PyAttrCache *)(instr + 1); _PyAttrCache *cache = (_PyAttrCache *)(instr + 1);
PyTypeObject *type = Py_TYPE(owner);
if (!_PyType_IsReady(type)) {
// We *might* not really need this check, but we inherited it from
// PyObject_GenericGetAttr and friends... and this way we still do the
// right thing if someone forgets to call PyType_Ready(type):
SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_OTHER);
goto fail;
}
if (PyModule_CheckExact(owner)) { if (PyModule_CheckExact(owner)) {
int err = specialize_module_load_attr(owner, instr, name, LOAD_ATTR, if (specialize_module_load_attr(owner, instr, name, LOAD_ATTR,
LOAD_ATTR_MODULE); LOAD_ATTR_MODULE))
if (err) { {
goto fail; goto fail;
} }
goto success; goto success;
} }
if (PyType_Check(owner)) { if (PyType_Check(owner)) {
int err = specialize_class_load_attr(owner, instr, name); if (specialize_class_load_attr(owner, instr, name)) {
if (err) {
goto fail; goto fail;
} }
goto success; goto success;
} }
PyTypeObject *type = Py_TYPE(owner);
if (type->tp_dict == NULL) {
if (PyType_Ready(type) < 0) {
return -1;
}
}
PyObject *descr = NULL; PyObject *descr = NULL;
DescriptorClassification kind = analyze_descriptor(type, name, &descr, 0); DescriptorClassification kind = analyze_descriptor(type, name, &descr, 0);
assert(descr != NULL || kind == ABSENT || kind == GETSET_OVERRIDDEN); assert(descr != NULL || kind == ABSENT || kind == GETSET_OVERRIDDEN);
@ -803,14 +804,9 @@ _Py_Specialize_LoadAttr(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name)
case ABSENT: case ABSENT:
break; break;
} }
int err = specialize_dict_access( if (specialize_dict_access(owner, instr, type, kind, name, LOAD_ATTR,
owner, instr, type, kind, name, LOAD_ATTR_INSTANCE_VALUE, LOAD_ATTR_WITH_HINT))
LOAD_ATTR, LOAD_ATTR_INSTANCE_VALUE, LOAD_ATTR_WITH_HINT {
);
if (err < 0) {
return -1;
}
if (err) {
goto success; goto success;
} }
fail: fail:
@ -818,20 +814,26 @@ fail:
assert(!PyErr_Occurred()); assert(!PyErr_Occurred());
_Py_SET_OPCODE(*instr, LOAD_ATTR); _Py_SET_OPCODE(*instr, LOAD_ATTR);
cache->counter = adaptive_counter_backoff(cache->counter); cache->counter = adaptive_counter_backoff(cache->counter);
return 0; return;
success: success:
STAT_INC(LOAD_ATTR, success); STAT_INC(LOAD_ATTR, success);
assert(!PyErr_Occurred()); assert(!PyErr_Occurred());
cache->counter = adaptive_counter_cooldown(); cache->counter = adaptive_counter_cooldown();
return 0;
} }
int void
_Py_Specialize_StoreAttr(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name) _Py_Specialize_StoreAttr(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name)
{ {
assert(_PyOpcode_Caches[STORE_ATTR] == INLINE_CACHE_ENTRIES_STORE_ATTR); assert(_PyOpcode_Caches[STORE_ATTR] == INLINE_CACHE_ENTRIES_STORE_ATTR);
_PyAttrCache *cache = (_PyAttrCache *)(instr + 1); _PyAttrCache *cache = (_PyAttrCache *)(instr + 1);
PyTypeObject *type = Py_TYPE(owner); PyTypeObject *type = Py_TYPE(owner);
if (!_PyType_IsReady(type)) {
// We *might* not really need this check, but we inherited it from
// PyObject_GenericSetAttr and friends... and this way we still do the
// right thing if someone forgets to call PyType_Ready(type):
SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_OTHER);
goto fail;
}
if (PyModule_CheckExact(owner)) { if (PyModule_CheckExact(owner)) {
SPECIALIZATION_FAIL(STORE_ATTR, SPEC_FAIL_OVERRIDDEN); SPECIALIZATION_FAIL(STORE_ATTR, SPEC_FAIL_OVERRIDDEN);
goto fail; goto fail;
@ -890,15 +892,9 @@ _Py_Specialize_StoreAttr(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name)
case ABSENT: case ABSENT:
break; break;
} }
if (specialize_dict_access(owner, instr, type, kind, name, STORE_ATTR,
int err = specialize_dict_access( STORE_ATTR_INSTANCE_VALUE, STORE_ATTR_WITH_HINT))
owner, instr, type, kind, name, {
STORE_ATTR, STORE_ATTR_INSTANCE_VALUE, STORE_ATTR_WITH_HINT
);
if (err < 0) {
return -1;
}
if (err) {
goto success; goto success;
} }
fail: fail:
@ -906,12 +902,11 @@ fail:
assert(!PyErr_Occurred()); assert(!PyErr_Occurred());
_Py_SET_OPCODE(*instr, STORE_ATTR); _Py_SET_OPCODE(*instr, STORE_ATTR);
cache->counter = adaptive_counter_backoff(cache->counter); cache->counter = adaptive_counter_backoff(cache->counter);
return 0; return;
success: success:
STAT_INC(STORE_ATTR, success); STAT_INC(STORE_ATTR, success);
assert(!PyErr_Occurred()); assert(!PyErr_Occurred());
cache->counter = adaptive_counter_cooldown(); cache->counter = adaptive_counter_cooldown();
return 0;
} }