diff options
Diffstat (limited to 'Python')
-rw-r--r-- | Python/ceval.c | 36 | ||||
-rw-r--r-- | Python/opcode_targets.h | 30 | ||||
-rw-r--r-- | Python/specialize.c | 66 |
3 files changed, 102 insertions, 30 deletions
diff --git a/Python/ceval.c b/Python/ceval.c index f3bdaf1..a64a24f 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -4424,7 +4424,7 @@ handle_eval_breaker: } } - TARGET(LOAD_METHOD_CACHED) { + TARGET(LOAD_METHOD_WITH_VALUES) { /* LOAD_METHOD, with cached method object */ assert(cframe.use_tracing == 0); PyObject *self = TOP(); @@ -4432,7 +4432,7 @@ handle_eval_breaker: 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); assert(self_cls->tp_flags & Py_TPFLAGS_MANAGED_DICT); PyDictObject *dict = *(PyDictObject**)_PyObject_ManagedDictPointer(self); @@ -4448,6 +4448,38 @@ handle_eval_breaker: NOTRACE_DISPATCH(); } + TARGET(LOAD_METHOD_WITH_DICT) { + /* LOAD_METHOD, with a dict + Can be either a managed dict, or a tp_dictoffset offset.*/ + 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); + /* Treat index as a signed 16 bit value */ + int dictoffset = *(int16_t *)&cache0->index; + PyDictObject **dictptr = (PyDictObject**)(((char *)self)+dictoffset); + assert( + dictoffset == MANAGED_DICT_OFFSET || + (dictoffset == self_cls->tp_dictoffset && dictoffset > 0) + ); + PyDictObject *dict = *dictptr; + DEOPT_IF(dict == NULL, LOAD_METHOD); + DEOPT_IF(dict->ma_keys->dk_version != cache1->dk_version, LOAD_METHOD); + STAT_INC(LOAD_METHOD, hit); + 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); + NOTRACE_DISPATCH(); + } + TARGET(LOAD_METHOD_NO_DICT) { assert(cframe.use_tracing == 0); PyObject *self = TOP(); diff --git a/Python/opcode_targets.h b/Python/opcode_targets.h index fac671e..3339ab1 100644 --- a/Python/opcode_targets.h +++ b/Python/opcode_targets.h @@ -47,40 +47,40 @@ static void *opcode_targets[256] = { &&TARGET_LOAD_GLOBAL_MODULE, &&TARGET_LOAD_GLOBAL_BUILTIN, &&TARGET_LOAD_METHOD_ADAPTIVE, - &&TARGET_LOAD_METHOD_CACHED, + &&TARGET_LOAD_METHOD_CLASS, &&TARGET_WITH_EXCEPT_START, &&TARGET_GET_AITER, &&TARGET_GET_ANEXT, &&TARGET_BEFORE_ASYNC_WITH, &&TARGET_BEFORE_WITH, &&TARGET_END_ASYNC_FOR, - &&TARGET_LOAD_METHOD_CLASS, &&TARGET_LOAD_METHOD_MODULE, &&TARGET_LOAD_METHOD_NO_DICT, + &&TARGET_LOAD_METHOD_WITH_DICT, + &&TARGET_LOAD_METHOD_WITH_VALUES, &&TARGET_PRECALL_ADAPTIVE, - &&TARGET_PRECALL_BUILTIN_CLASS, &&TARGET_STORE_SUBSCR, &&TARGET_DELETE_SUBSCR, + &&TARGET_PRECALL_BUILTIN_CLASS, &&TARGET_PRECALL_NO_KW_BUILTIN_O, &&TARGET_PRECALL_NO_KW_BUILTIN_FAST, &&TARGET_PRECALL_BUILTIN_FAST_WITH_KEYWORDS, &&TARGET_PRECALL_NO_KW_LEN, &&TARGET_PRECALL_NO_KW_ISINSTANCE, - &&TARGET_PRECALL_NO_KW_LIST_APPEND, &&TARGET_GET_ITER, &&TARGET_GET_YIELD_FROM_ITER, &&TARGET_PRINT_EXPR, &&TARGET_LOAD_BUILD_CLASS, - &&TARGET_PRECALL_NO_KW_METHOD_DESCRIPTOR_O, + &&TARGET_PRECALL_NO_KW_LIST_APPEND, &&TARGET_GET_AWAITABLE, &&TARGET_LOAD_ASSERTION_ERROR, &&TARGET_RETURN_GENERATOR, + &&TARGET_PRECALL_NO_KW_METHOD_DESCRIPTOR_O, &&TARGET_PRECALL_NO_KW_METHOD_DESCRIPTOR_NOARGS, &&TARGET_PRECALL_NO_KW_STR_1, &&TARGET_PRECALL_NO_KW_TUPLE_1, &&TARGET_PRECALL_NO_KW_TYPE_1, &&TARGET_PRECALL_NO_KW_METHOD_DESCRIPTOR_FAST, - &&TARGET_PRECALL_BOUND_METHOD, &&TARGET_LIST_TO_TUPLE, &&TARGET_RETURN_VALUE, &&TARGET_IMPORT_STAR, @@ -130,7 +130,7 @@ static void *opcode_targets[256] = { &&TARGET_POP_JUMP_IF_NOT_NONE, &&TARGET_POP_JUMP_IF_NONE, &&TARGET_RAISE_VARARGS, - &&TARGET_PRECALL_PYFUNC, + &&TARGET_PRECALL_BOUND_METHOD, &&TARGET_MAKE_FUNCTION, &&TARGET_BUILD_SLICE, &&TARGET_JUMP_NO_INTERRUPT, @@ -139,39 +139,40 @@ static void *opcode_targets[256] = { &&TARGET_LOAD_DEREF, &&TARGET_STORE_DEREF, &&TARGET_DELETE_DEREF, + &&TARGET_PRECALL_PYFUNC, &&TARGET_RESUME_QUICK, - &&TARGET_STORE_ATTR_ADAPTIVE, &&TARGET_CALL_FUNCTION_EX, - &&TARGET_STORE_ATTR_INSTANCE_VALUE, + &&TARGET_STORE_ATTR_ADAPTIVE, &&TARGET_EXTENDED_ARG, &&TARGET_LIST_APPEND, &&TARGET_SET_ADD, &&TARGET_MAP_ADD, &&TARGET_LOAD_CLASSDEREF, &&TARGET_COPY_FREE_VARS, - &&TARGET_STORE_ATTR_SLOT, + &&TARGET_STORE_ATTR_INSTANCE_VALUE, &&TARGET_RESUME, &&TARGET_MATCH_CLASS, + &&TARGET_STORE_ATTR_SLOT, &&TARGET_STORE_ATTR_WITH_HINT, - &&TARGET_UNPACK_SEQUENCE_ADAPTIVE, &&TARGET_FORMAT_VALUE, &&TARGET_BUILD_CONST_KEY_MAP, &&TARGET_BUILD_STRING, + &&TARGET_UNPACK_SEQUENCE_ADAPTIVE, &&TARGET_UNPACK_SEQUENCE_LIST, - &&TARGET_UNPACK_SEQUENCE_TUPLE, &&TARGET_LOAD_METHOD, - &&TARGET_UNPACK_SEQUENCE_TWO_TUPLE, + &&TARGET_UNPACK_SEQUENCE_TUPLE, &&TARGET_LIST_EXTEND, &&TARGET_SET_UPDATE, &&TARGET_DICT_MERGE, &&TARGET_DICT_UPDATE, &&TARGET_PRECALL, + &&TARGET_UNPACK_SEQUENCE_TWO_TUPLE, &&TARGET_LOAD_FAST__LOAD_FAST, &&TARGET_STORE_FAST__LOAD_FAST, &&TARGET_LOAD_FAST__LOAD_CONST, - &&TARGET_LOAD_CONST__LOAD_FAST, &&TARGET_CALL, &&TARGET_KW_NAMES, + &&TARGET_LOAD_CONST__LOAD_FAST, &&TARGET_STORE_FAST__STORE_FAST, &&TARGET_LOAD_FAST__LOAD_ATTR_INSTANCE_VALUE, &&_unknown_opcode, @@ -253,6 +254,5 @@ static void *opcode_targets[256] = { &&_unknown_opcode, &&_unknown_opcode, &&_unknown_opcode, - &&_unknown_opcode, &&TARGET_DO_TRACING }; diff --git a/Python/specialize.c b/Python/specialize.c index 1641766..6e4eb46 100644 --- a/Python/specialize.c +++ b/Python/specialize.c @@ -1062,6 +1062,13 @@ specialize_class_load_method(PyObject *owner, _Py_CODEUNIT *instr, PyObject *nam } } +typedef enum { + MANAGED_VALUES = 1, + MANAGED_DICT = 2, + OFFSET_DICT = 3, + NO_DICT = 4 +} ObjectDictKind; + // Please collect stats carefully before and after modifying. A subtle change // can cause a significant drop in cache hits. A possible test is // python.exe -m test_typing test_re test_dis test_zlib. @@ -1071,8 +1078,8 @@ _Py_Specialize_LoadMethod(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name, _PyAdaptiveEntry *cache0 = &cache->adaptive; _PyAttrCache *cache1 = &cache[-1].attr; _PyObjectCache *cache2 = &cache[-2].obj; - 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); @@ -1102,13 +1109,39 @@ _Py_Specialize_LoadMethod(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name, SPECIALIZATION_FAIL(LOAD_METHOD, load_method_fail_kind(kind)); goto fail; } + ObjectDictKind dictkind; + PyDictKeysObject *keys; if (owner_cls->tp_flags & Py_TPFLAGS_MANAGED_DICT) { - PyObject **owner_dictptr = _PyObject_ManagedDictPointer(owner); - if (*owner_dictptr) { - SPECIALIZATION_FAIL(LOAD_METHOD, SPEC_FAIL_LOAD_METHOD_HAS_MANAGED_DICT); + PyObject *dict = *_PyObject_ManagedDictPointer(owner); + keys = ((PyHeapTypeObject *)owner_cls)->ht_cached_keys; + if (dict == NULL) { + dictkind = MANAGED_VALUES; + } + else { + dictkind = MANAGED_DICT; + } + } + else { + Py_ssize_t dictoffset = owner_cls->tp_dictoffset; + if (dictoffset < 0 || dictoffset > INT16_MAX) { + SPECIALIZATION_FAIL(LOAD_METHOD, SPEC_FAIL_OUT_OF_RANGE); goto fail; } - PyDictKeysObject *keys = ((PyHeapTypeObject *)owner_cls)->ht_cached_keys; + if (dictoffset == 0) { + dictkind = NO_DICT; + keys = NULL; + } + else { + PyObject *dict = *(PyObject **) ((char *)owner + dictoffset); + if (dict == NULL) { + SPECIALIZATION_FAIL(LOAD_METHOD, SPEC_FAIL_NO_DICT); + goto fail; + } + keys = ((PyDictObject *)dict)->ma_keys; + dictkind = OFFSET_DICT; + } + } + if (dictkind != NO_DICT) { Py_ssize_t index = _PyDictKeys_StringLookup(keys, name); if (index != DKIX_EMPTY) { SPECIALIZATION_FAIL(LOAD_METHOD, SPEC_FAIL_LOAD_METHOD_IS_ATTR); @@ -1120,16 +1153,23 @@ _Py_Specialize_LoadMethod(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name, goto fail; } cache1->dk_version = keys_version; - *instr = _Py_MAKECODEUNIT(LOAD_METHOD_CACHED, _Py_OPARG(*instr)); } - else { - if (owner_cls->tp_dictoffset == 0) { + switch(dictkind) { + case NO_DICT: *instr = _Py_MAKECODEUNIT(LOAD_METHOD_NO_DICT, _Py_OPARG(*instr)); - } - else { - SPECIALIZATION_FAIL(LOAD_METHOD, SPEC_FAIL_LOAD_METHOD_HAS_DICT); - goto fail; - } + break; + case MANAGED_VALUES: + *instr = _Py_MAKECODEUNIT(LOAD_METHOD_WITH_VALUES, _Py_OPARG(*instr)); + break; + case MANAGED_DICT: + *(int16_t *)&cache0->index = (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; + *instr = _Py_MAKECODEUNIT(LOAD_METHOD_WITH_DICT, _Py_OPARG(*instr)); + 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: |