mirror of
https://github.com/python/cpython.git
synced 2025-10-21 22:22:48 +00:00
bpo-44889: Specialize LOAD_METHOD with PEP 659 adaptive interpreter (GH-27722)
Adds four new instructions: * LOAD_METHOD_ADAPTIVE * LOAD_METHOD_CACHED * LOAD_METHOD_MODULE * LOAD_METHOD_CLASS
This commit is contained in:
parent
fcd651d16f
commit
96346cb6d0
7 changed files with 351 additions and 46 deletions
142
Python/ceval.c
142
Python/ceval.c
|
@ -1439,6 +1439,27 @@ eval_frame_handle_pending(PyThreadState *tstate)
|
|||
#define BUILTINS() frame->f_builtins
|
||||
#define LOCALS() frame->f_locals
|
||||
|
||||
/* Shared opcode macros */
|
||||
|
||||
// shared by LOAD_ATTR_MODULE and LOAD_METHOD_MODULE
|
||||
#define LOAD_MODULE_ATTR_OR_METHOD(attr_or_method) \
|
||||
SpecializedCacheEntry *caches = GET_CACHE(); \
|
||||
_PyAdaptiveEntry *cache0 = &caches[0].adaptive; \
|
||||
_PyAttrCache *cache1 = &caches[-1].attr; \
|
||||
DEOPT_IF(!PyModule_CheckExact(owner), LOAD_##attr_or_method); \
|
||||
PyDictObject *dict = (PyDictObject *)((PyModuleObject *)owner)->md_dict; \
|
||||
assert(dict != NULL); \
|
||||
DEOPT_IF(dict->ma_keys->dk_version != cache1->dk_version_or_hint, \
|
||||
LOAD_##attr_or_method); \
|
||||
assert(dict->ma_keys->dk_kind == DICT_KEYS_UNICODE); \
|
||||
assert(cache0->index < dict->ma_keys->dk_nentries); \
|
||||
PyDictKeyEntry *ep = DK_ENTRIES(dict->ma_keys) + cache0->index; \
|
||||
res = ep->me_value; \
|
||||
DEOPT_IF(res == NULL, LOAD_##attr_or_method); \
|
||||
STAT_INC(LOAD_##attr_or_method, hit); \
|
||||
record_cache_hit(cache0); \
|
||||
Py_INCREF(res);
|
||||
|
||||
static int
|
||||
trace_function_entry(PyThreadState *tstate, InterpreterFrame *frame)
|
||||
{
|
||||
|
@ -3511,23 +3532,10 @@ check_eval_breaker:
|
|||
|
||||
TARGET(LOAD_ATTR_MODULE): {
|
||||
assert(cframe.use_tracing == 0);
|
||||
// shared with LOAD_METHOD_MODULE
|
||||
PyObject *owner = TOP();
|
||||
PyObject *res;
|
||||
SpecializedCacheEntry *caches = GET_CACHE();
|
||||
_PyAdaptiveEntry *cache0 = &caches[0].adaptive;
|
||||
_PyAttrCache *cache1 = &caches[-1].attr;
|
||||
DEOPT_IF(!PyModule_CheckExact(owner), LOAD_ATTR);
|
||||
PyDictObject *dict = (PyDictObject *)((PyModuleObject *)owner)->md_dict;
|
||||
assert(dict != NULL);
|
||||
DEOPT_IF(dict->ma_keys->dk_version != cache1->dk_version_or_hint, LOAD_ATTR);
|
||||
assert(dict->ma_keys->dk_kind == DICT_KEYS_UNICODE);
|
||||
assert(cache0->index < dict->ma_keys->dk_nentries);
|
||||
PyDictKeyEntry *ep = DK_ENTRIES(dict->ma_keys) + cache0->index;
|
||||
res = ep->me_value;
|
||||
DEOPT_IF(res == NULL, LOAD_ATTR);
|
||||
STAT_INC(LOAD_ATTR, hit);
|
||||
record_cache_hit(cache0);
|
||||
Py_INCREF(res);
|
||||
LOAD_MODULE_ATTR_OR_METHOD(ATTR);
|
||||
SET_TOP(res);
|
||||
Py_DECREF(owner);
|
||||
DISPATCH();
|
||||
|
@ -4282,6 +4290,8 @@ check_eval_breaker:
|
|||
}
|
||||
|
||||
TARGET(LOAD_METHOD): {
|
||||
PREDICTED(LOAD_METHOD);
|
||||
STAT_INC(LOAD_METHOD, unquickened);
|
||||
/* Designed to work in tandem with CALL_METHOD. */
|
||||
PyObject *name = GETITEM(names, oparg);
|
||||
PyObject *obj = TOP();
|
||||
|
@ -4318,6 +4328,107 @@ check_eval_breaker:
|
|||
DISPATCH();
|
||||
}
|
||||
|
||||
TARGET(LOAD_METHOD_ADAPTIVE): {
|
||||
assert(cframe.use_tracing == 0);
|
||||
SpecializedCacheEntry *cache = GET_CACHE();
|
||||
if (cache->adaptive.counter == 0) {
|
||||
PyObject *owner = TOP();
|
||||
PyObject *name = GETITEM(names, cache->adaptive.original_oparg);
|
||||
next_instr--;
|
||||
if (_Py_Specialize_LoadMethod(owner, next_instr, name, cache) < 0) {
|
||||
goto error;
|
||||
}
|
||||
DISPATCH();
|
||||
}
|
||||
else {
|
||||
STAT_INC(LOAD_METHOD, deferred);
|
||||
cache->adaptive.counter--;
|
||||
oparg = cache->adaptive.original_oparg;
|
||||
STAT_DEC(LOAD_METHOD, unquickened);
|
||||
JUMP_TO_INSTRUCTION(LOAD_METHOD);
|
||||
}
|
||||
}
|
||||
|
||||
TARGET(LOAD_METHOD_CACHED): {
|
||||
/* LOAD_METHOD, with cached method object */
|
||||
assert(cframe.use_tracing == 0);
|
||||
PyObject *self = TOP();
|
||||
PyTypeObject *self_cls = Py_TYPE(self);
|
||||
SpecializedCacheEntry *caches = GET_CACHE();
|
||||
_PyAdaptiveEntry *cache0 = &caches[0].adaptive;
|
||||
_PyAttrCache *cache1 = &caches[-1].attr;
|
||||
_PyObjectCache *cache2 = &caches[-2].obj;
|
||||
|
||||
DEOPT_IF(self_cls->tp_version_tag != cache1->tp_version, LOAD_METHOD);
|
||||
assert(cache1->dk_version_or_hint != 0);
|
||||
assert(cache1->tp_version != 0);
|
||||
assert(self_cls->tp_dictoffset >= 0);
|
||||
assert(Py_TYPE(self_cls)->tp_dictoffset > 0);
|
||||
|
||||
// inline version of _PyObject_GetDictPtr for offset >= 0
|
||||
PyObject *dict = self_cls->tp_dictoffset != 0 ?
|
||||
*(PyObject **) ((char *)self + self_cls->tp_dictoffset) : NULL;
|
||||
|
||||
// Ensure self.__dict__ didn't modify keys.
|
||||
// Don't care if self has no dict, it could be builtin or __slots__.
|
||||
DEOPT_IF(dict != NULL &&
|
||||
((PyDictObject *)dict)->ma_keys->dk_version !=
|
||||
cache1->dk_version_or_hint, LOAD_METHOD);
|
||||
|
||||
STAT_INC(LOAD_METHOD, hit);
|
||||
record_cache_hit(cache0);
|
||||
PyObject *res = cache2->obj;
|
||||
assert(res != NULL);
|
||||
assert(_PyType_HasFeature(Py_TYPE(res), Py_TPFLAGS_METHOD_DESCRIPTOR));
|
||||
Py_INCREF(res);
|
||||
SET_TOP(res);
|
||||
PUSH(self);
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
TARGET(LOAD_METHOD_MODULE): {
|
||||
assert(cframe.use_tracing == 0);
|
||||
PyObject *owner = TOP();
|
||||
PyObject *res;
|
||||
LOAD_MODULE_ATTR_OR_METHOD(METHOD);
|
||||
SET_TOP(NULL);
|
||||
Py_DECREF(owner);
|
||||
PUSH(res);
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
TARGET(LOAD_METHOD_CLASS): {
|
||||
/* LOAD_METHOD, for class methods */
|
||||
assert(cframe.use_tracing == 0);
|
||||
SpecializedCacheEntry *caches = GET_CACHE();
|
||||
_PyAdaptiveEntry *cache0 = &caches[0].adaptive;
|
||||
_PyAttrCache *cache1 = &caches[-1].attr;
|
||||
_PyObjectCache *cache2 = &caches[-2].obj;
|
||||
|
||||
PyObject *cls = TOP();
|
||||
PyTypeObject *cls_type = Py_TYPE(cls);
|
||||
assert(cls_type->tp_dictoffset > 0);
|
||||
PyObject *dict = *(PyObject **) ((char *)cls + cls_type->tp_dictoffset);
|
||||
// Don't care if no dict -- tp_version_tag should catch anything wrong.
|
||||
DEOPT_IF(dict != NULL && ((PyDictObject *)dict)->ma_keys->dk_version !=
|
||||
cache1->dk_version_or_hint, LOAD_METHOD);
|
||||
DEOPT_IF(((PyTypeObject *)cls)->tp_version_tag != cache1->tp_version,
|
||||
LOAD_METHOD);
|
||||
assert(cache1->dk_version_or_hint != 0);
|
||||
assert(cache1->tp_version != 0);
|
||||
|
||||
STAT_INC(LOAD_METHOD, hit);
|
||||
record_cache_hit(cache0);
|
||||
PyObject *res = cache2->obj;
|
||||
assert(res != NULL);
|
||||
assert(_PyType_HasFeature(Py_TYPE(res), Py_TPFLAGS_METHOD_DESCRIPTOR));
|
||||
Py_INCREF(res);
|
||||
SET_TOP(NULL);
|
||||
Py_DECREF(cls);
|
||||
PUSH(res);
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
TARGET(CALL_METHOD): {
|
||||
/* Designed to work in tamdem with LOAD_METHOD. */
|
||||
PyObject **sp, *res;
|
||||
|
@ -4648,6 +4759,7 @@ opname ## _miss: \
|
|||
MISS_WITH_CACHE(LOAD_ATTR)
|
||||
MISS_WITH_CACHE(STORE_ATTR)
|
||||
MISS_WITH_CACHE(LOAD_GLOBAL)
|
||||
MISS_WITH_CACHE(LOAD_METHOD)
|
||||
MISS_WITH_OPARG_COUNTER(BINARY_SUBSCR)
|
||||
|
||||
binary_subscr_dict_error:
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue