8000 gh-92777: Add LOAD_METHOD_LAZY_DICT (GH-92778) · python/cpython@5e6e5b9 · GitHub
[go: up one dir, main page]

Skip to content

Commit 5e6e5b9

Browse files
gh-92777: Add LOAD_METHOD_LAZY_DICT (GH-92778)
1 parent db3ef0c commit 5e6e5b9

File tree

7 files changed

+85
-49
lines changed

7 files changed

+85
-49
lines changed

Include/internal/pycore_opcode.h

Lines changed: 14 additions & 13 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Include/opcode.h

Lines changed: 19 additions & 18 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Lib/opcode.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,7 @@ def jabs_op(name, op):
303303
"LOAD_METHOD": [
304304
"LOAD_METHOD_ADAPTIVE",
305305
"LOAD_METHOD_CLASS",
306+
"LOAD_METHOD_LAZY_DICT",
306307
"LOAD_METHOD_MODULE",
307308
"LOAD_METHOD_NO_DICT",
308309
"LOAD_METHOD_WITH_DICT",
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Specialize ``LOAD_METHOD`` for objects with lazy dictionaries. Patch by Ken Jin.

Python/ceval.c

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4673,6 +4673,29 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
46734673
NOTRACE_DISPATCH();
46744674
}
46754675

4676+
TARGET(LOAD_METHOD_LAZY_DICT) {
4677+
assert(cframe.use_tracing == 0);
4678+
PyObject *self = TOP();
4679+
PyTypeObject *self_cls = Py_TYPE(self);
4680+
_PyLoadMethodCache *cache = (_PyLoadMethodCache *)next_instr;
4681+
uint32_t type_version = read_u32(cache->type_version);
4682+
DEOPT_IF(self_cls->tp_version_tag != type_version, LOAD_METHOD);
4683+
int dictoffset = cache->dict_offset;
4684+
PyObject *dict = *(PyObject **)((char *)self + dictoffset);
4685+
assert(dictoffset == self_cls->tp_dictoffset && dictoffset > 0);
4686+
/* This object has a __dict__, just not yet created */
4687+
DEOPT_IF(dict != NULL, LOAD_METHOD);
4688+
STAT_INC(LOAD_METHOD, hit);
4689+
PyObject *res = read_obj(cache->descr);
4690+
assert(res != NULL);
4691+
assert(_PyType_HasFeature(Py_TYPE(res), Py_TPFLAGS_METHOD_DESCRIPTOR));
4692+
Py_INCREF(res);
4693+
SET_TOP(res);
4694+
PUSH(self);
4695+
JUMPBY(INLINE_CACHE_ENTRIES_LOAD_METHOD);
4696+
NOTRACE_DISPATCH();
4697+
}
4698+
46764699
TARGET(LOAD_METHOD_MODULE) {
46774700
/* LOAD_METHOD, for module methods */
46784701
assert(cframe.use_tracing == 0);

Python/opcode_targets.h

Lines changed: 12 additions & 12 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Python/specialize.c

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -903,7 +903,8 @@ typedef enum {
903903
MANAGED_VALUES = 1,
904904
MANAGED_DICT = 2,
905905
OFFSET_DICT = 3,
906-
NO_DICT = 4
906+
NO_DICT = 4,
907+
LAZY_DICT = 5,
907908
} ObjectDictKind;
908909

909910
// Please collect stats carefully before and after modifying. A subtle change
@@ -972,14 +973,17 @@ _Py_Specialize_LoadMethod(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name)
972973
else {
973974
PyObject *dict = *(PyObject **) ((char *)owner + dictoffset);
974975
if (dict == NULL) {
975-
SPECIALIZATION_FAIL(LOAD_METHOD, SPEC_FAIL_NO_DICT);
976-
goto fail;
976+
// This object will have a dict if user access __dict__
977+
dictkind = LAZY_DICT;
978+
keys = NULL;
979+
}
980+
else {
981+
keys = ((PyDictObject *)dict)->ma_keys;
982+
dictkind = OFFSET_DICT;
977983
}
978-
keys = ((PyDictObject *)dict)->ma_keys;
979-
dictkind = OFFSET_DICT;
980984
}
981985
}
982-
if (dictkind != NO_DICT) {
986+
if (dictkind == MANAGED_VALUES || dictkind == MANAGED_DICT || dictkind == OFFSET_DICT) {
983987
Py_ssize_t index = _PyDictKeys_StringLookup(keys, name);
984988
if (index != DKIX_EMPTY) {
985989
SPECIALIZATION_FAIL(LOAD_METHOD, SPEC_FAIL_LOAD_METHOD_IS_ATTR);
@@ -1008,6 +1012,11 @@ _Py_Specialize_LoadMethod(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name)
10081012
cache->dict_offset = (uint16_t)owner_cls->tp_dictoffset;
10091013
_Py_SET_OPCODE(*instr, LOAD_METHOD_WITH_DICT);
10101014
break;
1015+
case LAZY_DICT:
1016+
assert(owner_cls->tp_dictoffset > 0 && owner_cls->tp_dictoffset <= INT16_MAX);
1017+
cache->dict_offset = (uint16_t)owner_cls->tp_dictoffset;
1018+
_Py_SET_OPCODE(*instr, LOAD_METHOD_LAZY_DICT);
1019+
break;
10111020
}
10121021
/* `descr` is borrowed. This is safe for methods (even inherited ones from
10131022
* super classes!) as long as tp_version_tag is validated for two main reasons:

0 commit comments

Comments
 (0)
0