Skip to content

Commit

Permalink
gh-92777: Add LOAD_METHOD_LAZY_DICT (GH-92778)
Browse files Browse the repository at this point in the history
  • Loading branch information
Fidget-Spinner committed May 25, 2022
1 parent db3ef0c commit 5e6e5b9
Show file tree
Hide file tree
Showing 7 changed files with 85 additions and 49 deletions.
27 changes: 14 additions & 13 deletions Include/internal/pycore_opcode.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

37 changes: 19 additions & 18 deletions Include/opcode.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Lib/opcode.py
Expand Up @@ -303,6 +303,7 @@ def jabs_op(name, op):
"LOAD_METHOD": [
"LOAD_METHOD_ADAPTIVE",
"LOAD_METHOD_CLASS",
"LOAD_METHOD_LAZY_DICT",
"LOAD_METHOD_MODULE",
"LOAD_METHOD_NO_DICT",
"LOAD_METHOD_WITH_DICT",
Expand Down
@@ -0,0 +1 @@
Specialize ``LOAD_METHOD`` for objects with lazy dictionaries. Patch by Ken Jin.
23 changes: 23 additions & 0 deletions Python/ceval.c
Expand Up @@ -4673,6 +4673,29 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
NOTRACE_DISPATCH();
}

TARGET(LOAD_METHOD_LAZY_DICT) {
assert(cframe.use_tracing == 0);
PyObject *self = TOP();
PyTypeObject *self_cls = Py_TYPE(self);
_PyLoadMethodCache *cache = (_PyLoadMethodCache *)next_instr;
uint32_t type_version = read_u32(cache->type_version);
DEOPT_IF(self_cls->tp_version_tag != type_version, LOAD_METHOD);
int dictoffset = cache->dict_offset;
PyObject *dict = *(PyObject **)((char *)self + dictoffset);
assert(dictoffset == self_cls->tp_dictoffset && dictoffset > 0);
/* This object has a __dict__, just not yet created */
DEOPT_IF(dict != NULL, LOAD_METHOD);
STAT_INC(LOAD_METHOD, hit);
PyObject *res = read_obj(cache->descr);
assert(res != NULL);
assert(_PyType_HasFeature(Py_TYPE(res), Py_TPFLAGS_METHOD_DESCRIPTOR));
Py_INCREF(res);
SET_TOP(res);
PUSH(self);
JUMPBY(INLINE_CACHE_ENTRIES_LOAD_METHOD);
NOTRACE_DISPATCH();
}

TARGET(LOAD_METHOD_MODULE) {
/* LOAD_METHOD, for module methods */
assert(cframe.use_tracing == 0);
Expand Down
24 changes: 12 additions & 12 deletions Python/opcode_targets.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

21 changes: 15 additions & 6 deletions Python/specialize.c
Expand Up @@ -903,7 +903,8 @@ typedef enum {
MANAGED_VALUES = 1,
MANAGED_DICT = 2,
OFFSET_DICT = 3,
NO_DICT = 4
NO_DICT = 4,
LAZY_DICT = 5,
} ObjectDictKind;

// Please collect stats carefully before and after modifying. A subtle change
Expand Down Expand Up @@ -972,14 +973,17 @@ _Py_Specialize_LoadMethod(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name)
else {
PyObject *dict = *(PyObject **) ((char *)owner + dictoffset);
if (dict == NULL) {
SPECIALIZATION_FAIL(LOAD_METHOD, SPEC_FAIL_NO_DICT);
goto fail;
// This object will have a dict if user access __dict__
dictkind = LAZY_DICT;
keys = NULL;
}
else {
keys = ((PyDictObject *)dict)->ma_keys;
dictkind = OFFSET_DICT;
}
keys = ((PyDictObject *)dict)->ma_keys;
dictkind = OFFSET_DICT;
}
}
if (dictkind != NO_DICT) {
if (dictkind == MANAGED_VALUES || dictkind == MANAGED_DICT || dictkind == OFFSET_DICT) {
Py_ssize_t index = _PyDictKeys_StringLookup(keys, name);
if (index != DKIX_EMPTY) {
SPECIALIZATION_FAIL(LOAD_METHOD, SPEC_FAIL_LOAD_METHOD_IS_ATTR);
Expand Down Expand Up @@ -1008,6 +1012,11 @@ _Py_Specialize_LoadMethod(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name)
cache->dict_offset = (uint16_t)owner_cls->tp_dictoffset;
_Py_SET_OPCODE(*instr, LOAD_METHOD_WITH_DICT);
break;
case LAZY_DICT:
assert(owner_cls->tp_dictoffset > 0 && owner_cls->tp_dictoffset <= INT16_MAX);
cache->dict_offset = (uint16_t)owner_cls->tp_dictoffset;
_Py_SET_OPCODE(*instr, LOAD_METHOD_LAZY_DICT);
break;
}
/* `descr` is borrowed. This is safe for methods (even inherited ones from
* super classes!) as long as tp_version_tag is validated for two main reasons:
Expand Down

0 comments on commit 5e6e5b9

Please sign in to comment.