diff options
author | Brandt Bucher <brandtbucher@microsoft.com> | 2022-03-03 23:31:00 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-03-03 23:31:00 (GMT) |
commit | 05a8bc1c944709e7468f157bd1b6032f368e43bf (patch) | |
tree | 8bd069cf131d3d1f32bd388bba71eb856bdf3616 /Python | |
parent | 65b92ccdec2ee4a99e54aaf7ae2d9bbc2ebfe549 (diff) | |
download | cpython-05a8bc1c944709e7468f157bd1b6032f368e43bf.zip cpython-05a8bc1c944709e7468f157bd1b6032f368e43bf.tar.gz cpython-05a8bc1c944709e7468f157bd1b6032f368e43bf.tar.bz2 |
bpo-46841: Use inline caching for attribute accesses (GH-31640)
Diffstat (limited to 'Python')
-rw-r--r-- | Python/ceval.c | 268 | ||||
-rw-r--r-- | Python/opcode_targets.h | 2 | ||||
-rw-r--r-- | Python/specialize.c | 120 |
3 files changed, 159 insertions, 231 deletions
diff --git a/Python/ceval.c b/Python/ceval.c index c86b744..915ab93 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -1448,16 +1448,15 @@ eval_frame_handle_pending(PyThreadState *tstate) // 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 *cache = (_PyAttrCache *)next_instr; \ 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 != cache0->version, \ - LOAD_##attr_or_method); \ + DEOPT_IF(dict->ma_keys->dk_version != read_u32(cache->version), \ + LOAD_##attr_or_method); \ assert(dict->ma_keys->dk_kind == DICT_KEYS_UNICODE); \ - assert(cache0->index < dict->ma_keys->dk_nentries); \ - PyDictUnicodeEntry *ep = DK_UNICODE_ENTRIES(dict->ma_keys) + cache0->index; \ + assert(cache->index < dict->ma_keys->dk_nentries); \ + PyDictUnicodeEntry *ep = DK_UNICODE_ENTRIES(dict->ma_keys) + cache->index; \ res = ep->me_value; \ DEOPT_IF(res == NULL, LOAD_##attr_or_method); \ STAT_INC(LOAD_##attr_or_method, hit); \ @@ -2197,7 +2196,7 @@ handle_eval_breaker: PyObject *sub = TOP(); PyObject *container = SECOND(); _PyBinarySubscrCache *cache = (_PyBinarySubscrCache *)next_instr; - uint32_t type_version = read32(&cache->type_version); + uint32_t type_version = read_u32(cache->type_version); PyTypeObject *tp = Py_TYPE(container); DEOPT_IF(tp->tp_version_tag != type_version, BINARY_SUBSCR); assert(tp->tp_flags & Py_TPFLAGS_HEAPTYPE); @@ -2849,8 +2848,10 @@ handle_eval_breaker: err = PyObject_SetAttr(owner, name, v); Py_DECREF(v); Py_DECREF(owner); - if (err != 0) + if (err != 0) { goto error; + } + JUMPBY(INLINE_CACHE_ENTRIES_STORE_ATTR); DISPATCH(); } @@ -3028,7 +3029,7 @@ handle_eval_breaker: DEOPT_IF(!PyDict_CheckExact(GLOBALS()), LOAD_GLOBAL); PyDictObject *dict = (PyDictObject *)GLOBALS(); _PyLoadGlobalCache *cache = (_PyLoadGlobalCache *)next_instr; - uint32_t version = read32(&cache->module_keys_version); + uint32_t version = read_u32(cache->module_keys_version); DEOPT_IF(dict->ma_keys->dk_version != version, LOAD_GLOBAL); assert(DK_IS_UNICODE(dict->ma_keys)); PyDictUnicodeEntry *entries = DK_UNICODE_ENTRIES(dict->ma_keys); @@ -3048,7 +3049,7 @@ handle_eval_breaker: PyDictObject *mdict = (PyDictObject *)GLOBALS(); PyDictObject *bdict = (PyDictObject *)BUILTINS(); _PyLoadGlobalCache *cache = (_PyLoadGlobalCache *)next_instr; - uint32_t mod_version = read32(&cache->module_keys_version); + uint32_t mod_version = read_u32(cache->module_keys_version); uint16_t bltn_version = cache->builtin_keys_version; DEOPT_IF(mdict->ma_keys->dk_version != mod_version, LOAD_GLOBAL); DEOPT_IF(bdict->ma_keys->dk_version != bltn_version, LOAD_GLOBAL); @@ -3423,76 +3424,49 @@ handle_eval_breaker: } Py_DECREF(owner); SET_TOP(res); + JUMPBY(INLINE_CACHE_ENTRIES_LOAD_ATTR); DISPATCH(); } TARGET(LOAD_ATTR_ADAPTIVE) { assert(cframe.use_tracing == 0); - SpecializedCacheEntry *cache = GET_CACHE(); - if (cache->adaptive.counter == 0) { + _PyAttrCache *cache = (_PyAttrCache *)next_instr; + if (cache->counter == 0) { PyObject *owner = TOP(); - PyObject *name = GETITEM(names, cache->adaptive.original_oparg); + PyObject *name = GETITEM(names, oparg); next_instr--; - if (_Py_Specialize_LoadAttr(owner, next_instr, name, cache) < 0) { + if (_Py_Specialize_LoadAttr(owner, next_instr, name) < 0) { goto error; } DISPATCH(); } else { STAT_INC(LOAD_ATTR, deferred); - cache->adaptive.counter--; - oparg = cache->adaptive.original_oparg; + cache->counter--; JUMP_TO_INSTRUCTION(LOAD_ATTR); } } - TARGET(LOAD_FAST__LOAD_ATTR_INSTANCE_VALUE) { - assert(cframe.use_tracing == 0); - PyObject *owner = GETLOCAL(oparg); // borrowed - if (owner == NULL) { - goto unbound_local_error; - } - // GET_CACHE(), but for the following opcode - assert(_Py_OPCODE(*next_instr) == LOAD_ATTR_INSTANCE_VALUE); - SpecializedCacheEntry *caches = _GetSpecializedCacheEntryForInstruction( - first_instr, INSTR_OFFSET() + 1, _Py_OPARG(*next_instr)); - _PyAdaptiveEntry *cache0 = &caches[0].adaptive; - assert(cache0->version != 0); - PyTypeObject *tp = Py_TYPE(owner); - // These DEOPT_IF miss branches do PUSH(Py_NewRef(owner)). - DEOPT_IF(tp->tp_version_tag != cache0->version, - LOAD_FAST__LOAD_ATTR_INSTANCE_VALUE); - assert(tp->tp_dictoffset < 0); - assert(tp->tp_flags & Py_TPFLAGS_MANAGED_DICT); - PyDictValues *values = *_PyObject_ValuesPointer(owner); - DEOPT_IF(values == NULL, LOAD_FAST__LOAD_ATTR_INSTANCE_VALUE); - PyObject *res = values->values[cache0->index]; - DEOPT_IF(res == NULL, LOAD_FAST__LOAD_ATTR_INSTANCE_VALUE); - STAT_INC(LOAD_ATTR, hit); - PUSH(Py_NewRef(res)); - next_instr++; - NOTRACE_DISPATCH(); - } - TARGET(LOAD_ATTR_INSTANCE_VALUE) { assert(cframe.use_tracing == 0); PyObject *owner = TOP(); PyObject *res; PyTypeObject *tp = Py_TYPE(owner); - SpecializedCacheEntry *caches = GET_CACHE(); - _PyAdaptiveEntry *cache0 = &caches[0].adaptive; - assert(cache0->version != 0); - DEOPT_IF(tp->tp_version_tag != cache0->version, LOAD_ATTR_INSTANCE_VALUE); + _PyAttrCache *cache = (_PyAttrCache *)next_instr; + uint32_t type_version = read_u32(cache->version); + assert(type_version != 0); + DEOPT_IF(tp->tp_version_tag != type_version, LOAD_ATTR); assert(tp->tp_dictoffset < 0); assert(tp->tp_flags & Py_TPFLAGS_MANAGED_DICT); PyDictValues *values = *_PyObject_ValuesPointer(owner); - DEOPT_IF(values == NULL, LOAD_ATTR_INSTANCE_VALUE); - res = values->values[cache0->index]; - DEOPT_IF(res == NULL, LOAD_ATTR_INSTANCE_VALUE); + DEOPT_IF(values == NULL, LOAD_ATTR); + res = values->values[cache->index]; + DEOPT_IF(res == NULL, LOAD_ATTR); STAT_INC(LOAD_ATTR, hit); Py_INCREF(res); SET_TOP(res); Py_DECREF(owner); + JUMPBY(INLINE_CACHE_ENTRIES_LOAD_ATTR); NOTRACE_DISPATCH(); } @@ -3504,6 +3478,7 @@ handle_eval_breaker: LOAD_MODULE_ATTR_OR_METHOD(ATTR); SET_TOP(res); Py_DECREF(owner); + JUMPBY(INLINE_CACHE_ENTRIES_LOAD_ATTR); NOTRACE_DISPATCH(); } @@ -3512,16 +3487,16 @@ handle_eval_breaker: PyObject *owner = TOP(); PyObject *res; PyTypeObject *tp = Py_TYPE(owner); - SpecializedCacheEntry *caches = GET_CACHE(); - _PyAdaptiveEntry *cache0 = &caches[0].adaptive; - assert(cache0->version != 0); - DEOPT_IF(tp->tp_version_tag != cache0->version, LOAD_ATTR); + _PyAttrCache *cache = (_PyAttrCache *)next_instr; + uint32_t type_version = read_u32(cache->version); + assert(type_version != 0); + DEOPT_IF(tp->tp_version_tag != type_version, LOAD_ATTR); assert(tp->tp_flags & Py_TPFLAGS_MANAGED_DICT); PyDictObject *dict = *(PyDictObject **)_PyObject_ManagedDictPointer(owner); DEOPT_IF(dict == NULL, LOAD_ATTR); assert(PyDict_CheckExact((PyObject *)dict)); - PyObject *name = GETITEM(names, cache0->original_oparg); - uint16_t hint = cache0->index; + PyObject *name = GETITEM(names, oparg); + uint16_t hint = cache->index; DEOPT_IF(hint >= (size_t)dict->ma_keys->dk_nentries, LOAD_ATTR); if (DK_IS_UNICODE(dict->ma_keys)) { PyDictUnicodeEntry *ep = DK_UNICODE_ENTRIES(dict->ma_keys) + hint; @@ -3538,6 +3513,7 @@ handle_eval_breaker: Py_INCREF(res); SET_TOP(res); Py_DECREF(owner); + JUMPBY(INLINE_CACHE_ENTRIES_LOAD_ATTR); NOTRACE_DISPATCH(); } @@ -3546,36 +3522,36 @@ handle_eval_breaker: PyObject *owner = TOP(); PyObject *res; PyTypeObject *tp = Py_TYPE(owner); - SpecializedCacheEntry *caches = GET_CACHE(); - _PyAdaptiveEntry *cache0 = &caches[0].adaptive; - assert(cache0->version != 0); - DEOPT_IF(tp->tp_version_tag != cache0->version, LOAD_ATTR); - char *addr = (char *)owner + cache0->index; + _PyAttrCache *cache = (_PyAttrCache *)next_instr; + uint32_t type_version = read_u32(cache->version); + assert(type_version != 0); + DEOPT_IF(tp->tp_version_tag != type_version, LOAD_ATTR); + char *addr = (char *)owner + cache->index; res = *(PyObject **)addr; DEOPT_IF(res == NULL, LOAD_ATTR); STAT_INC(LOAD_ATTR, hit); Py_INCREF(res); SET_TOP(res); Py_DECREF(owner); + JUMPBY(INLINE_CACHE_ENTRIES_LOAD_ATTR); NOTRACE_DISPATCH(); } TARGET(STORE_ATTR_ADAPTIVE) { assert(cframe.use_tracing == 0); - SpecializedCacheEntry *cache = GET_CACHE(); - if (cache->adaptive.counter == 0) { + _PyAttrCache *cache = (_PyAttrCache *)next_instr; + if (cache->counter == 0) { PyObject *owner = TOP(); - PyObject *name = GETITEM(names, cache->adaptive.original_oparg); + PyObject *name = GETITEM(names, oparg); next_instr--; - if (_Py_Specialize_StoreAttr(owner, next_instr, name, cache) < 0) { + if (_Py_Specialize_StoreAttr(owner, next_instr, name) < 0) { goto error; } DISPATCH(); } else { STAT_INC(STORE_ATTR, deferred); - cache->adaptive.counter--; - oparg = cache->adaptive.original_oparg; + cache->counter--; JUMP_TO_INSTRUCTION(STORE_ATTR); } } @@ -3584,15 +3560,15 @@ handle_eval_breaker: assert(cframe.use_tracing == 0); PyObject *owner = TOP(); PyTypeObject *tp = Py_TYPE(owner); - SpecializedCacheEntry *caches = GET_CACHE(); - _PyAdaptiveEntry *cache0 = &caches[0].adaptive; - assert(cache0->version != 0); - DEOPT_IF(tp->tp_version_tag != cache0->version, STORE_ATTR); + _PyAttrCache *cache = (_PyAttrCache *)next_instr; + uint32_t type_version = read_u32(cache->version); + assert(type_version != 0); + DEOPT_IF(tp->tp_version_tag != type_version, STORE_ATTR); assert(tp->tp_flags & Py_TPFLAGS_MANAGED_DICT); PyDictValues *values = *_PyObject_ValuesPointer(owner); DEOPT_IF(values == NULL, STORE_ATTR); STAT_INC(STORE_ATTR, hit); - Py_ssize_t index = cache0->index; + Py_ssize_t index = cache->index; STACK_SHRINK(1); PyObject *value = POP(); PyObject *old_value = values->values[index]; @@ -3604,6 +3580,7 @@ handle_eval_breaker: Py_DECREF(old_value); } Py_DECREF(owner); + JUMPBY(INLINE_CACHE_ENTRIES_STORE_ATTR); NOTRACE_DISPATCH(); } @@ -3611,16 +3588,16 @@ handle_eval_breaker: assert(cframe.use_tracing == 0); PyObject *owner = TOP(); PyTypeObject *tp = Py_TYPE(owner); - SpecializedCacheEntry *caches = GET_CACHE(); - _PyAdaptiveEntry *cache0 = &caches[0].adaptive; - assert(cache0->version != 0); - DEOPT_IF(tp->tp_version_tag != cache0->version, STORE_ATTR); + _PyAttrCache *cache = (_PyAttrCache *)next_instr; + uint32_t type_version = read_u32(cache->version); + assert(type_version != 0); + DEOPT_IF(tp->tp_version_tag != type_version, STORE_ATTR); assert(tp->tp_flags & Py_TPFLAGS_MANAGED_DICT); PyDictObject *dict = *(PyDictObject **)_PyObject_ManagedDictPointer(owner); DEOPT_IF(dict == NULL, STORE_ATTR); assert(PyDict_CheckExact((PyObject *)dict)); - PyObject *name = GETITEM(names, cache0->original_oparg); - uint16_t hint = cache0->index; + PyObject *name = GETITEM(names, oparg); + uint16_t hint = cache->index; DEOPT_IF(hint >= (size_t)dict->ma_keys->dk_nentries, STORE_ATTR); PyObject *value, *old_value; if (DK_IS_UNICODE(dict->ma_keys)) { @@ -3650,6 +3627,7 @@ handle_eval_breaker: /* PEP 509 */ dict->ma_version_tag = DICT_NEXT_VERSION(); Py_DECREF(owner); + JUMPBY(INLINE_CACHE_ENTRIES_STORE_ATTR); NOTRACE_DISPATCH(); } @@ -3657,11 +3635,11 @@ handle_eval_breaker: assert(cframe.use_tracing == 0); PyObject *owner = TOP(); PyTypeObject *tp = Py_TYPE(owner); - SpecializedCacheEntry *caches = GET_CACHE(); - _PyAdaptiveEntry *cache0 = &caches[0].adaptive; - assert(cache0->version != 0); - DEOPT_IF(tp->tp_version_tag != cache0->version, STORE_ATTR); - char *addr = (char *)owner + cache0->index; + _PyAttrCache *cache = (_PyAttrCache *)next_instr; + uint32_t type_version = read_u32(cache->version); + assert(type_version != 0); + DEOPT_IF(tp->tp_version_tag != type_version, STORE_ATTR); + char *addr = (char *)owner + cache->index; STAT_INC(STORE_ATTR, hit); STACK_SHRINK(1); PyObject *value = POP(); @@ -3669,6 +3647,7 @@ handle_eval_breaker: *(PyObject **)addr = value; Py_XDECREF(old_value); Py_DECREF(owner); + JUMPBY(INLINE_CACHE_ENTRIES_STORE_ATTR); NOTRACE_DISPATCH(); } @@ -4425,25 +4404,25 @@ handle_eval_breaker: Py_DECREF(obj); PUSH(meth); } + JUMPBY(INLINE_CACHE_ENTRIES_LOAD_METHOD); DISPATCH(); } TARGET(LOAD_METHOD_ADAPTIVE) { assert(cframe.use_tracing == 0); - SpecializedCacheEntry *cache = GET_CACHE(); - if (cache->adaptive.counter == 0) { + _PyLoadMethodCache *cache = (_PyLoadMethodCache *)next_instr; + if (cache->counter == 0) { PyObject *owner = TOP(); - PyObject *name = GETITEM(names, cache->adaptive.original_oparg); + PyObject *name = GETITEM(names, oparg); next_instr--; - if (_Py_Specialize_LoadMethod(owner, next_instr, name, cache) < 0) { + if (_Py_Specialize_LoadMethod(owner, next_instr, name) < 0) { goto error; } DISPATCH(); } else { STAT_INC(LOAD_METHOD, deferred); - cache->adaptive.counter--; - oparg = cache->adaptive.original_oparg; + cache->counter--; JUMP_TO_INSTRUCTION(LOAD_METHOD); } } @@ -4453,22 +4432,24 @@ handle_eval_breaker: assert(cframe.use_tracing == 0); PyObject *self = TOP(); PyTypeObject *self_cls = Py_TYPE(self); - SpecializedCacheEntry *caches = GET_CACHE(); - _PyAttrCache *cache1 = &caches[-1].attr; - _PyObjectCache *cache2 = &caches[-2].obj; - assert(cache1->tp_version != 0); - DEOPT_IF(self_cls->tp_version_tag != cache1->tp_version, LOAD_METHOD); + _PyLoadMethodCache *cache = (_PyLoadMethodCache *)next_instr; + uint32_t type_version = read_u32(cache->type_version); + assert(type_version != 0); + DEOPT_IF(self_cls->tp_version_tag != type_version, LOAD_METHOD); assert(self_cls->tp_flags & Py_TPFLAGS_MANAGED_DICT); PyDictObject *dict = *(PyDictObject**)_PyObject_ManagedDictPointer(self); DEOPT_IF(dict != NULL, LOAD_METHOD); - DEOPT_IF(((PyHeapTypeObject *)self_cls)->ht_cached_keys->dk_version != cache1->dk_version, LOAD_METHOD); + PyHeapTypeObject *self_heap_type = (PyHeapTypeObject *)self_cls; + DEOPT_IF(self_heap_type->ht_cached_keys->dk_version != + read_u32(cache->keys_version), LOAD_METHOD); STAT_INC(LOAD_METHOD, hit); - PyObject *res = cache2->obj; + 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(); } @@ -4478,14 +4459,12 @@ handle_eval_breaker: 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; + _PyLoadMethodCache *cache = (_PyLoadMethodCache *)next_instr; - DEOPT_IF(self_cls->tp_version_tag != cache1->tp_version, LOAD_METHOD); + DEOPT_IF(self_cls->tp_version_tag != read_u32(cache->type_version), + LOAD_METHOD); /* Treat index as a signed 16 bit value */ - int dictoffset = *(int16_t *)&cache0->index; + int dictoffset = *(int16_t *)&cache->dict_offset; PyDictObject **dictptr = (PyDictObject**)(((char *)self)+dictoffset); assert( dictoffset == MANAGED_DICT_OFFSET || @@ -4493,14 +4472,16 @@ handle_eval_breaker: ); PyDictObject *dict = *dictptr; DEOPT_IF(dict == NULL, LOAD_METHOD); - DEOPT_IF(dict->ma_keys->dk_version != cache1->dk_version, LOAD_METHOD); + DEOPT_IF(dict->ma_keys->dk_version != read_u32(cache->keys_version), + LOAD_METHOD); STAT_INC(LOAD_METHOD, hit); - PyObject *res = cache2->obj; + 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(); } @@ -4508,18 +4489,18 @@ handle_eval_breaker: assert(cframe.use_tracing == 0); PyObject *self = TOP(); PyTypeObject *self_cls = Py_TYPE(self); - SpecializedCacheEntry *caches = GET_CACHE(); - _PyAttrCache *cache1 = &caches[-1].attr; - _PyObjectCache *cache2 = &caches[-2].obj; - DEOPT_IF(self_cls->tp_version_tag != cache1->tp_version, LOAD_METHOD); + _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); assert(self_cls->tp_dictoffset == 0); STAT_INC(LOAD_METHOD, hit); - PyObject *res = cache2->obj; + 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(); } @@ -4532,29 +4513,30 @@ handle_eval_breaker: SET_TOP(NULL); Py_DECREF(owner); PUSH(res); + JUMPBY(INLINE_CACHE_ENTRIES_LOAD_METHOD); NOTRACE_DISPATCH(); } TARGET(LOAD_METHOD_CLASS) { /* LOAD_METHOD, for class methods */ assert(cframe.use_tracing == 0); - SpecializedCacheEntry *caches = GET_CACHE(); - _PyAttrCache *cache1 = &caches[-1].attr; - _PyObjectCache *cache2 = &caches[-2].obj; + _PyLoadMethodCache *cache = (_PyLoadMethodCache *)next_instr; PyObject *cls = TOP(); DEOPT_IF(!PyType_Check(cls), LOAD_METHOD); - DEOPT_IF(((PyTypeObject *)cls)->tp_version_tag != cache1->tp_version, - LOAD_METHOD); - assert(cache1->tp_version != 0); + uint32_t type_version = read_u32(cache->type_version); + DEOPT_IF(((PyTypeObject *)cls)->tp_version_tag != type_version, + LOAD_METHOD); + assert(type_version != 0); STAT_INC(LOAD_METHOD, hit); - PyObject *res = cache2->obj; + PyObject *res = read_obj(cache->descr); assert(res != NULL); Py_INCREF(res); SET_TOP(NULL); Py_DECREF(cls); PUSH(res); + JUMPBY(INLINE_CACHE_ENTRIES_LOAD_METHOD); NOTRACE_DISPATCH(); } @@ -5607,10 +5589,10 @@ opname ## _miss: \ JUMP_TO_INSTRUCTION(opname); \ } -MISS_WITH_CACHE(LOAD_ATTR) -MISS_WITH_CACHE(STORE_ATTR) +MISS_WITH_INLINE_CACHE(LOAD_ATTR) +MISS_WITH_INLINE_CACHE(STORE_ATTR) MISS_WITH_INLINE_CACHE(LOAD_GLOBAL) -MISS_WITH_CACHE(LOAD_METHOD) +MISS_WITH_INLINE_CACHE(LOAD_METHOD) MISS_WITH_CACHE(PRECALL) MISS_WITH_CACHE(CALL) MISS_WITH_INLINE_CACHE(BINARY_OP) @@ -5619,52 +5601,6 @@ MISS_WITH_INLINE_CACHE(BINARY_SUBSCR) MISS_WITH_INLINE_CACHE(UNPACK_SEQUENCE) MISS_WITH_OPARG_COUNTER(STORE_SUBSCR) -LOAD_ATTR_INSTANCE_VALUE_miss: - { - // Special-cased so that if LOAD_ATTR_INSTANCE_VALUE - // gets replaced, then any preceeding - // LOAD_FAST__LOAD_ATTR_INSTANCE_VALUE gets replaced as well - STAT_INC(LOAD_ATTR_INSTANCE_VALUE, miss); - STAT_INC(LOAD_ATTR, miss); - _PyAdaptiveEntry *cache = &GET_CACHE()->adaptive; - cache->counter--; - if (cache->counter == 0) { - next_instr[-1] = _Py_MAKECODEUNIT(LOAD_ATTR_ADAPTIVE, _Py_OPARG(next_instr[-1])); - if (_Py_OPCODE(next_instr[-2]) == LOAD_FAST__LOAD_ATTR_INSTANCE_VALUE) { - next_instr[-2] = _Py_MAKECODEUNIT(LOAD_FAST, _Py_OPARG(next_instr[-2])); - if (_Py_OPCODE(next_instr[-3]) == LOAD_FAST) { - next_instr[-3] = _Py_MAKECODEUNIT(LOAD_FAST__LOAD_FAST, _Py_OPARG(next_instr[-3])); - } - } - STAT_INC(LOAD_ATTR, deopt); - cache_backoff(cache); - } - oparg = cache->original_oparg; - JUMP_TO_INSTRUCTION(LOAD_ATTR); - } - -LOAD_FAST__LOAD_ATTR_INSTANCE_VALUE_miss: - { - // This is special-cased because we have a superinstruction - // that includes a specialized instruction. - // If the specialized portion misses, carry out - // the first instruction, then perform a miss - // for the second instruction as usual. - - // Do LOAD_FAST - { - PyObject *value = GETLOCAL(oparg); - assert(value != NULL); // Already checked if unbound - Py_INCREF(value); - PUSH(value); - NEXTOPARG(); - next_instr++; - } - - // Now we are in the correct state for LOAD_ATTR - goto LOAD_ATTR_INSTANCE_VALUE_miss; - } - binary_subscr_dict_error: { PyObject *sub = POP(); diff --git a/Python/opcode_targets.h b/Python/opcode_targets.h index d463e30..7be7b16 100644 --- a/Python/opcode_targets.h +++ b/Python/opcode_targets.h @@ -175,7 +175,7 @@ static void *opcode_targets[256] = { &&TARGET_LOAD_FAST__LOAD_CONST, &&TARGET_LOAD_CONST__LOAD_FAST, &&TARGET_STORE_FAST__STORE_FAST, - &&TARGET_LOAD_FAST__LOAD_ATTR_INSTANCE_VALUE, + &&_unknown_opcode, &&_unknown_opcode, &&_unknown_opcode, &&_unknown_opcode, diff --git a/Python/specialize.c b/Python/specialize.c index 4a94aaf..66dce8c 100644 --- a/Python/specialize.c +++ b/Python/specialize.c @@ -58,12 +58,9 @@ static uint8_t adaptive_opcodes[256] = { /* The number of cache entries required for a "family" of instructions. */ static uint8_t cache_requirements[256] = { - [LOAD_ATTR] = 1, // _PyAdaptiveEntry - [LOAD_METHOD] = 3, /* _PyAdaptiveEntry, _PyAttrCache and _PyObjectCache */ [STORE_SUBSCR] = 0, [CALL] = 2, /* _PyAdaptiveEntry and _PyObjectCache/_PyCallCache */ [PRECALL] = 2, /* _PyAdaptiveEntry and _PyObjectCache/_PyCallCache */ - [STORE_ATTR] = 1, // _PyAdaptiveEntry }; Py_ssize_t _Py_QuickenedCount = 0; @@ -641,11 +638,10 @@ initial_counter_value(void) { static int -specialize_module_load_attr( - PyObject *owner, _Py_CODEUNIT *instr, PyObject *name, - _PyAdaptiveEntry *cache0, int opcode, - int opcode_module) +specialize_module_load_attr(PyObject *owner, _Py_CODEUNIT *instr, + PyObject *name, int opcode, int opcode_module) { + _PyAttrCache *cache = (_PyAttrCache *)(instr + 1); PyModuleObject *m = (PyModuleObject *)owner; PyObject *value = NULL; assert((owner->ob_type->tp_flags & Py_TPFLAGS_MANAGED_DICT) == 0); @@ -676,8 +672,8 @@ specialize_module_load_attr( SPECIALIZATION_FAIL(opcode, SPEC_FAIL_OUT_OF_VERSIONS); return -1; } - cache0->version = keys_version; - cache0->index = (uint16_t)index; + write_u32(cache->version, keys_version); + cache->index = (uint16_t)index; *instr = _Py_MAKECODEUNIT(opcode_module, _Py_OPARG(*instr)); return 0; } @@ -765,7 +761,6 @@ static int specialize_dict_access( PyObject *owner, _Py_CODEUNIT *instr, PyTypeObject *type, DescriptorClassification kind, PyObject *name, - _PyAdaptiveEntry *cache0, int base_op, int values_op, int hint_op) { assert(kind == NON_OVERRIDING || kind == NON_DESCRIPTOR || kind == ABSENT || @@ -775,6 +770,7 @@ specialize_dict_access( SPECIALIZATION_FAIL(base_op, SPEC_FAIL_ATTR_NOT_MANAGED_DICT); return 0; } + _PyAttrCache *cache = (_PyAttrCache *)(instr + 1); PyObject **dictptr = _PyObject_ManagedDictPointer(owner); PyDictObject *dict = (PyDictObject *)*dictptr; if (dict == NULL) { @@ -787,8 +783,8 @@ specialize_dict_access( SPECIALIZATION_FAIL(base_op, SPEC_FAIL_OUT_OF_RANGE); return 0; } - cache0->version = type->tp_version_tag; - cache0->index = (uint16_t)index; + write_u32(cache->version, type->tp_version_tag); + cache->index = (uint16_t)index; *instr = _Py_MAKECODEUNIT(values_op, _Py_OPARG(*instr)); } else { @@ -804,20 +800,22 @@ specialize_dict_access( SPECIALIZATION_FAIL(base_op, SPEC_FAIL_OUT_OF_RANGE); return 0; } - cache0->index = (uint16_t)hint; - cache0->version = type->tp_version_tag; + cache->index = (uint16_t)hint; + write_u32(cache->version, type->tp_version_tag); *instr = _Py_MAKECODEUNIT(hint_op, _Py_OPARG(*instr)); } return 1; } int -_Py_Specialize_LoadAttr(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name, SpecializedCacheEntry *cache) +_Py_Specialize_LoadAttr(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name) { - _PyAdaptiveEntry *cache0 = &cache->adaptive; + assert(_PyOpcode_InlineCacheEntries[LOAD_ATTR] == + INLINE_CACHE_ENTRIES_LOAD_ATTR); + _PyAttrCache *cache = (_PyAttrCache *)(instr + 1); if (PyModule_CheckExact(owner)) { - int err = specialize_module_load_attr(owner, instr, name, cache0, - LOAD_ATTR, LOAD_ATTR_MODULE); + int err = specialize_module_load_attr(owner, instr, name, LOAD_ATTR, + LOAD_ATTR_MODULE); if (err) { goto fail; } @@ -856,8 +854,8 @@ _Py_Specialize_LoadAttr(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name, Sp } assert(dmem->type == T_OBJECT_EX); assert(offset > 0); - cache0->index = (uint16_t)offset; - cache0->version = type->tp_version_tag; + cache->index = (uint16_t)offset; + write_u32(cache->version, type->tp_version_tag); *instr = _Py_MAKECODEUNIT(LOAD_ATTR_SLOT, _Py_OPARG(*instr)); goto success; } @@ -865,8 +863,8 @@ _Py_Specialize_LoadAttr(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name, Sp { Py_ssize_t offset = offsetof(PyObject, ob_type); assert(offset == (uint16_t)offset); - cache0->index = (uint16_t)offset; - cache0->version = type->tp_version_tag; + cache->index = (uint16_t)offset; + write_u32(cache->version, type->tp_version_tag); *instr = _Py_MAKECODEUNIT(LOAD_ATTR_SLOT, _Py_OPARG(*instr)); goto success; } @@ -887,41 +885,33 @@ _Py_Specialize_LoadAttr(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name, Sp break; } int err = specialize_dict_access( - owner, instr, type, kind, name, cache0, + owner, instr, type, kind, name, LOAD_ATTR, LOAD_ATTR_INSTANCE_VALUE, LOAD_ATTR_WITH_HINT ); if (err < 0) { return -1; } if (err) { - if (_Py_OPCODE(instr[0]) == LOAD_ATTR_INSTANCE_VALUE) { - // Note: instr[-1] exists because there's something on the stack, - // and instr[-2] exists because there's at least a RESUME as well. - if (_Py_OPCODE(instr[-1]) == LOAD_FAST) { - instr[-1] = _Py_MAKECODEUNIT(LOAD_FAST__LOAD_ATTR_INSTANCE_VALUE, _Py_OPARG(instr[-1])); - if (_Py_OPCODE(instr[-2]) == LOAD_FAST__LOAD_FAST) { - instr[-2] = _Py_MAKECODEUNIT(LOAD_FAST, _Py_OPARG(instr[-2])); - } - } - } goto success; } fail: STAT_INC(LOAD_ATTR, failure); assert(!PyErr_Occurred()); - cache_backoff(cache0); + cache->counter = ADAPTIVE_CACHE_BACKOFF; return 0; success: STAT_INC(LOAD_ATTR, success); assert(!PyErr_Occurred()); - cache0->counter = initial_counter_value(); + cache->counter = initial_counter_value(); return 0; } int -_Py_Specialize_StoreAttr(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name, SpecializedCacheEntry *cache) +_Py_Specialize_StoreAttr(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name) { - _PyAdaptiveEntry *cache0 = &cache->adaptive; + assert(_PyOpcode_InlineCacheEntries[STORE_ATTR] == + INLINE_CACHE_ENTRIES_STORE_ATTR); + _PyAttrCache *cache = (_PyAttrCache *)(instr + 1); PyTypeObject *type = Py_TYPE(owner); if (PyModule_CheckExact(owner)) { SPECIALIZATION_FAIL(STORE_ATTR, SPEC_FAIL_OVERRIDDEN); @@ -954,8 +944,8 @@ _Py_Specialize_StoreAttr(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name, S } assert(dmem->type == T_OBJECT_EX); assert(offset > 0); - cache0->index = (uint16_t)offset; - cache0->version = type->tp_version_tag; + cache->index = (uint16_t)offset; + write_u32(cache->version, type->tp_version_tag); *instr = _Py_MAKECODEUNIT(STORE_ATTR_SLOT, _Py_OPARG(*instr)); goto success; } @@ -978,7 +968,7 @@ _Py_Specialize_StoreAttr(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name, S } int err = specialize_dict_access( - owner, instr, type, kind, name, cache0, + owner, instr, type, kind, name, STORE_ATTR, STORE_ATTR_INSTANCE_VALUE, STORE_ATTR_WITH_HINT ); if (err < 0) { @@ -990,12 +980,12 @@ _Py_Specialize_StoreAttr(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name, S fail: STAT_INC(STORE_ATTR, failure); assert(!PyErr_Occurred()); - cache_backoff(cache0); + cache->counter = ADAPTIVE_CACHE_BACKOFF; return 0; success: STAT_INC(STORE_ATTR, success); assert(!PyErr_Occurred()); - cache0->counter = initial_counter_value(); + cache->counter = initial_counter_value(); return 0; } @@ -1037,18 +1027,18 @@ load_method_fail_kind(DescriptorClassification kind) #endif static int -specialize_class_load_method(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name, - _PyAttrCache *cache1, _PyObjectCache *cache2) +specialize_class_load_method(PyObject *owner, _Py_CODEUNIT *instr, + PyObject *name) { - + _PyLoadMethodCache *cache = (_PyLoadMethodCache *)(instr + 1); PyObject *descr = NULL; DescriptorClassification kind = 0; kind = analyze_descriptor((PyTypeObject *)owner, name, &descr, 0); switch (kind) { case METHOD: case NON_DESCRIPTOR: - cache1->tp_version = ((PyTypeObject *)owner)->tp_version_tag; - cache2->obj = descr; + write_u32(cache->type_version, ((PyTypeObject *)owner)->tp_version_tag); + write_obj(cache->descr, descr); *instr = _Py_MAKECODEUNIT(LOAD_METHOD_CLASS, _Py_OPARG(*instr)); return 0; #ifdef Py_STATS @@ -1078,16 +1068,18 @@ typedef enum { // can cause a significant drop in cache hits. A possible test is // python.exe -m test_typing test_re test_dis test_zlib. int -_Py_Specialize_LoadMethod(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name, SpecializedCacheEntry *cache) +_Py_Specialize_LoadMethod(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name) { - _PyAdaptiveEntry *cache0 = &cache->adaptive; - _PyAttrCache *cache1 = &cache[-1].attr; - _PyObjectCache *cache2 = &cache[-2].obj; + assert(_PyOpcode_InlineCacheEntries[LOAD_METHOD] == + INLINE_CACHE_ENTRIES_LOAD_METHOD); + _PyLoadMethodCache *cache = (_PyLoadMethodCache *)(instr + 1); PyTypeObject *owner_cls = Py_TYPE(owner); if (PyModule_CheckExact(owner)) { - int err = specialize_module_load_attr(owner, instr, name, cache0, - LOAD_METHOD, LOAD_METHOD_MODULE); + assert(INLINE_CACHE_ENTRIES_LOAD_ATTR <= + INLINE_CACHE_ENTRIES_LOAD_METHOD); + int err = specialize_module_load_attr(owner, instr, name, LOAD_METHOD, + LOAD_METHOD_MODULE); if (err) { goto fail; } @@ -1099,7 +1091,7 @@ _Py_Specialize_LoadMethod(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name, } } if (PyType_Check(owner)) { - int err = specialize_class_load_method(owner, instr, name, cache1, cache2); + int err = specialize_class_load_method(owner, instr, name); if (err) { goto fail; } @@ -1157,7 +1149,7 @@ _Py_Specialize_LoadMethod(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name, SPECIALIZATION_FAIL(LOAD_METHOD, SPEC_FAIL_OUT_OF_VERSIONS); goto fail; } - cache1->dk_version = keys_version; + write_u32(cache->keys_version, keys_version); } switch(dictkind) { case NO_DICT: @@ -1167,12 +1159,12 @@ _Py_Specialize_LoadMethod(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name, *instr = _Py_MAKECODEUNIT(LOAD_METHOD_WITH_VALUES, _Py_OPARG(*instr)); break; case MANAGED_DICT: - *(int16_t *)&cache0->index = (int16_t)MANAGED_DICT_OFFSET; + *(int16_t *)&cache->dict_offset = (int16_t)MANAGED_DICT_OFFSET; *instr = _Py_MAKECODEUNIT(LOAD_METHOD_WITH_DICT, _Py_OPARG(*instr)); break; case OFFSET_DICT: assert(owner_cls->tp_dictoffset > 0 && owner_cls->tp_dictoffset <= INT16_MAX); - cache0->index = (uint16_t)owner_cls->tp_dictoffset; + cache->dict_offset = (uint16_t)owner_cls->tp_dictoffset; *instr = _Py_MAKECODEUNIT(LOAD_METHOD_WITH_DICT, _Py_OPARG(*instr)); break; } @@ -1190,18 +1182,18 @@ _Py_Specialize_LoadMethod(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name, * PyType_Modified usages in typeobject.c). The MCACHE has been * working since Python 2.6 and it's battle-tested. */ - cache1->tp_version = owner_cls->tp_version_tag; - cache2->obj = descr; + write_u32(cache->type_version, owner_cls->tp_version_tag); + write_obj(cache->descr, descr); // Fall through. success: STAT_INC(LOAD_METHOD, success); assert(!PyErr_Occurred()); - cache0->counter = initial_counter_value(); + cache->counter = initial_counter_value(); return 0; fail: STAT_INC(LOAD_METHOD, failure); assert(!PyErr_Occurred()); - cache_backoff(cache0); + cache->counter = ADAPTIVE_CACHE_BACKOFF; return 0; } @@ -1238,7 +1230,7 @@ _Py_Specialize_LoadGlobal( goto fail; } cache->index = (uint16_t)index; - write32(&cache->module_keys_version, keys_version); + write_u32(cache->module_keys_version, keys_version); *instr = _Py_MAKECODEUNIT(LOAD_GLOBAL_MODULE, _Py_OPARG(*instr)); goto success; } @@ -1273,7 +1265,7 @@ _Py_Specialize_LoadGlobal( goto fail; } cache->index = (uint16_t)index; - write32(&cache->module_keys_version, globals_version); + write_u32(cache->module_keys_version, globals_version); cache->builtin_keys_version = (uint16_t)builtins_version; *instr = _Py_MAKECODEUNIT(LOAD_GLOBAL_BUILTIN, _Py_OPARG(*instr)); goto success; @@ -1393,7 +1385,7 @@ _Py_Specialize_BinarySubscr( goto fail; } assert(cls->tp_version_tag != 0); - write32(&cache->type_version, cls->tp_version_tag); + write_u32(cache->type_version, cls->tp_version_tag); int version = _PyFunction_GetVersionForCurrentState(func); if (version == 0 || version != (uint16_t)version) { SPECIALIZATION_FAIL(BINARY_SUBSCR, SPEC_FAIL_OUT_OF_VERSIONS); |