diff options
author | Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com> | 2021-08-17 14:55:55 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-08-17 14:55:55 (GMT) |
commit | 96346cb6d0593ef9ec122614347ccb053cd63433 (patch) | |
tree | 9f2b952134803d5de5229ece709b19e29ef0e7ab /Python/ceval.c | |
parent | fcd651d16fc5ac3d07dd3f57f1001a861a2e7d23 (diff) | |
download | cpython-96346cb6d0593ef9ec122614347ccb053cd63433.zip cpython-96346cb6d0593ef9ec122614347ccb053cd63433.tar.gz cpython-96346cb6d0593ef9ec122614347ccb053cd63433.tar.bz2 |
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
Diffstat (limited to 'Python/ceval.c')
-rw-r--r-- | Python/ceval.c | 142 |
1 files changed, 127 insertions, 15 deletions
diff --git a/Python/ceval.c b/Python/ceval.c index 4878749..333c54f 100644 --- a/Python/ceval.c +++ b/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: |