◐ Shell
reader mode source ↗
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
File filter
Conversations
Jump to
Diff view
Apply and reload
Show whitespace
Diff view
Apply and reload
1 change: 1 addition & 0 deletions Include/internal/pycore_code.h
Original file line number Diff line number Diff line change
@@ -336,6 +336,7 @@ typedef struct _stats {
uint64_t deferred;
uint64_t miss;
uint64_t deopt;
#if SPECIALIZATION_STATS_DETAILED
PyObject *miss_types;
#endif
2 changes: 2 additions & 0 deletions Python/ceval.c
Original file line number Diff line number Diff line change
Expand Up @@ -2805,6 +2805,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag)

case TARGET(LOAD_GLOBAL): {
PREDICTED(LOAD_GLOBAL);
PyObject *name = GETITEM(names, oparg);
PyObject *v;
if (PyDict_CheckExact(GLOBALS())
Expand Down Expand Up @@ -3289,6 +3290,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag)

case TARGET(LOAD_ATTR): {
PREDICTED(LOAD_ATTR);
PyObject *name = GETITEM(names, oparg);
PyObject *owner = TOP();
PyObject *res = PyObject_GetAttr(owner, name);
Expand Down
248 changes: 176 additions & 72 deletions Python/specialize.c
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ print_stats(SpecializationStats *stats, const char *name)
PRINT_STAT(name, deferred);
PRINT_STAT(name, miss);
PRINT_STAT(name, deopt);
#if SPECIALIZATION_STATS_DETAILED
if (stats->miss_types == NULL) {
return;
Expand Down @@ -302,6 +303,8 @@ _Py_Quicken(PyCodeObject *code) {
return 0;
}

static int
specialize_module_load_attr(
PyObject *owner, _Py_CODEUNIT *instr, PyObject *name,
Expand Down Expand Up @@ -349,6 +352,68 @@ specialize_module_load_attr(
return 0;
}

int
_Py_Specialize_LoadAttr(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name, SpecializedCacheEntry *cache)
{
Expand All @@ -362,94 +427,134 @@ _Py_Specialize_LoadAttr(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name, Sp
goto success;
}
PyTypeObject *type = Py_TYPE(owner);
if (type->tp_getattro != PyObject_GenericGetAttr) {
SPECIALIZATION_FAIL(LOAD_ATTR, Py_TYPE(owner), name, "__getattribute__ overridden");
goto fail;
}
if (type->tp_dict == NULL) {
if (PyType_Ready(type) < 0) {
return -1;
}
}
PyObject *descr = _PyType_Lookup(type, name);
if (descr != NULL) {
// We found an attribute with a data-like descriptor.
PyTypeObject *dtype = Py_TYPE(descr);
if (dtype != &PyMemberDescr_Type) {
SPECIALIZATION_FAIL(LOAD_ATTR, Py_TYPE(owner), name, "not a member descriptor");
goto fail;
}
// It's a slot
PyMemberDescrObject *member = (PyMemberDescrObject *)descr;
struct PyMemberDef *dmem = member->d_member;
if (dmem->type != T_OBJECT_EX) {
SPECIALIZATION_FAIL(LOAD_ATTR, Py_TYPE(owner), name, "non-object slot");
goto fail;
}
Py_ssize_t offset = dmem->offset;
if (offset != (uint16_t)offset) {
SPECIALIZATION_FAIL(LOAD_ATTR, Py_TYPE(owner), name, "offset out of range");
goto fail;
}
assert(offset > 0);
cache0->index = (uint16_t)offset;
cache1->tp_version = type->tp_version_tag;
*instr = _Py_MAKECODEUNIT(LOAD_ATTR_SLOT, _Py_OPARG(*instr));
goto success;
}
// No desciptor
if (type->tp_dictoffset <= 0) {
SPECIALIZATION_FAIL(LOAD_ATTR, Py_TYPE(owner), name, "no dict or negative offset");
goto fail;
}
PyObject **dictptr = (PyObject **) ((char *)owner + type->tp_dictoffset);
if (*dictptr == NULL || !PyDict_CheckExact(*dictptr)) {
SPECIALIZATION_FAIL(LOAD_ATTR, Py_TYPE(owner), name, "no dict or not a dict");
goto fail;
}
// We found an instance with a __dict__.
PyDictObject *dict = (PyDictObject *)*dictptr;
if ((type->tp_flags & Py_TPFLAGS_HEAPTYPE)
&& dict->ma_keys == ((PyHeapTypeObject*)type)->ht_cached_keys
) {
// Keys are shared
assert(PyUnicode_CheckExact(name));
Py_hash_t hash = PyObject_Hash(name);
if (hash == -1) {
return -1;
}
PyObject *value;
Py_ssize_t index = _Py_dict_lookup(dict, name, hash, &value);
assert (index != DKIX_ERROR);
if (index != (uint16_t)index) {
SPECIALIZATION_FAIL(LOAD_ATTR, Py_TYPE(owner), name, "index out of range");
goto fail;
}
uint32_t keys_version = _PyDictKeys_GetVersionForCurrentState(dict);
if (keys_version == 0) {
SPECIALIZATION_FAIL(LOAD_ATTR, Py_TYPE(owner), name, "no more key versions");
goto fail;
}
cache1->dk_version_or_hint = keys_version;
cache1->tp_version = type->tp_version_tag;
cache0->index = (uint16_t)index;
*instr = _Py_MAKECODEUNIT(LOAD_ATTR_SPLIT_KEYS, _Py_OPARG(*instr));
goto success;
}
else {
PyObject *value = NULL;
Py_ssize_t hint =
_PyDict_GetItemHint(dict, name, -1, &value);
if (hint != (uint32_t)hint) {
SPECIALIZATION_FAIL(LOAD_ATTR, Py_TYPE(owner), name, "hint out of range");
goto fail;
}
cache1->dk_version_or_hint = (uint32_t)hint;
cache1->tp_version = type->tp_version_tag;
*instr = _Py_MAKECODEUNIT(LOAD_ATTR_WITH_HINT, _Py_OPARG(*instr));
goto success;
}

fail:
STAT_INC(LOAD_ATTR, specialization_failure);
assert(!PyErr_Occurred());
Expand All @@ -462,7 +567,6 @@ _Py_Specialize_LoadAttr(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name, Sp
return 0;
}


int
_Py_Specialize_LoadGlobal(
PyObject *globals, PyObject *builtins,
Toggle all file notes Toggle all file annotations