diff --git a/Include/internal/pycore_code.h b/Include/internal/pycore_code.h index 9d33ed71e78907..c181543722f6e0 100644 --- a/Include/internal/pycore_code.h +++ b/Include/internal/pycore_code.h @@ -65,7 +65,6 @@ typedef struct { typedef struct { _Py_CODEUNIT counter; _Py_CODEUNIT type_version[2]; - _Py_CODEUNIT dict_offset; _Py_CODEUNIT keys_version[2]; _Py_CODEUNIT descr[4]; } _PyLoadMethodCache; diff --git a/Include/internal/pycore_opcode.h b/Include/internal/pycore_opcode.h index e5d948dd696a2b..e254537da763a2 100644 --- a/Include/internal/pycore_opcode.h +++ b/Include/internal/pycore_opcode.h @@ -49,7 +49,7 @@ const uint8_t _PyOpcode_Caches[256] = { [COMPARE_OP] = 2, [LOAD_GLOBAL] = 5, [BINARY_OP] = 1, - [LOAD_METHOD] = 10, + [LOAD_METHOD] = 9, [CALL] = 4, }; diff --git a/Lib/importlib/_bootstrap_external.py b/Lib/importlib/_bootstrap_external.py index 894848a15d97d8..1b6654202f3d75 100644 --- a/Lib/importlib/_bootstrap_external.py +++ b/Lib/importlib/_bootstrap_external.py @@ -407,6 +407,7 @@ def _write_atomic(path, data, mode=0o666): # Python 3.12a1 3500 (Remove PRECALL opcode) # Python 3.12a1 3501 (YIELD_VALUE oparg == stack_depth) # Python 3.12a1 3502 (LOAD_FAST_CHECK, no NULL-check in LOAD_FAST) +# Python 3.12a1 3503 (Shrink LOAD_METHOD cache) # Python 3.13 will start with 3550 @@ -420,7 +421,7 @@ def _write_atomic(path, data, mode=0o666): # Whenever MAGIC_NUMBER is changed, the ranges in the magic_values array # in PC/launcher.c must also be updated. -MAGIC_NUMBER = (3502).to_bytes(2, 'little') + b'\r\n' +MAGIC_NUMBER = (3503).to_bytes(2, 'little') + b'\r\n' _RAW_MAGIC_NUMBER = int.from_bytes(MAGIC_NUMBER, 'little') # For import.c diff --git a/Lib/opcode.py b/Lib/opcode.py index 5cbb5c57521ae3..0996cc7eb0f6cd 100644 --- a/Lib/opcode.py +++ b/Lib/opcode.py @@ -383,7 +383,6 @@ def jabs_op(name, op): "LOAD_METHOD": { "counter": 1, "type_version": 2, - "dict_offset": 1, "keys_version": 2, "descr": 4, }, diff --git a/Lib/test/test_dis.py b/Lib/test/test_dis.py index a4f7f84995bf5f..a94cd6432e1232 100644 --- a/Lib/test/test_dis.py +++ b/Lib/test/test_dis.py @@ -1047,7 +1047,7 @@ def test_show_caches(self): for cache in caches: self.assertRegex(cache, pattern) self.assertEqual(caches.count(""), 8) - self.assertEqual(len(caches), 23) + self.assertEqual(len(caches), 22) class DisWithFileTests(DisTests): diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-06-06-14-28-24.gh-issue-93533.lnC0CC.rst b/Misc/NEWS.d/next/Core and Builtins/2022-06-06-14-28-24.gh-issue-93533.lnC0CC.rst new file mode 100644 index 00000000000000..2d8ac7068af673 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2022-06-06-14-28-24.gh-issue-93533.lnC0CC.rst @@ -0,0 +1 @@ +Reduce the size of the inline cache for ``LOAD_METHOD`` by 2 bytes. diff --git a/Python/ceval.c b/Python/ceval.c index ec86c70673ca9a..d1480ac190ea41 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -4581,12 +4581,9 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int 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 *)&cache->dict_offset; + int dictoffset = self_cls->tp_dictoffset; + assert(dictoffset > 0); 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 != read_u32(cache->keys_version), @@ -4628,9 +4625,9 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int _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); - int dictoffset = cache->dict_offset; + int dictoffset = self_cls->tp_dictoffset; + assert(dictoffset > 0); PyObject *dict = *(PyObject **)((char *)self + dictoffset); - assert(dictoffset == self_cls->tp_dictoffset && dictoffset > 0); /* This object has a __dict__, just not yet created */ DEOPT_IF(dict != NULL, LOAD_METHOD); STAT_INC(LOAD_METHOD, hit); diff --git a/Python/specialize.c b/Python/specialize.c index 4d30a2aea5f3be..1df281a19a41ea 100644 --- a/Python/specialize.c +++ b/Python/specialize.c @@ -986,7 +986,7 @@ _Py_Specialize_LoadMethod(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name) } } } - if (dictkind == MANAGED_VALUES || dictkind == MANAGED_DICT || dictkind == OFFSET_DICT) { + if (dictkind == MANAGED_VALUES || dictkind == OFFSET_DICT) { Py_ssize_t index = _PyDictKeys_StringLookup(keys, name); if (index != DKIX_EMPTY) { SPECIALIZATION_FAIL(LOAD_METHOD, SPEC_FAIL_LOAD_METHOD_IS_ATTR); @@ -1007,17 +1007,14 @@ _Py_Specialize_LoadMethod(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name) _Py_SET_OPCODE(*instr, LOAD_METHOD_WITH_VALUES); break; case MANAGED_DICT: - *(int16_t *)&cache->dict_offset = (int16_t)MANAGED_DICT_OFFSET; - _Py_SET_OPCODE(*instr, LOAD_METHOD_WITH_DICT); - break; + SPECIALIZATION_FAIL(LOAD_METHOD, SPEC_FAIL_LOAD_METHOD_HAS_MANAGED_DICT); + goto fail; case OFFSET_DICT: assert(owner_cls->tp_dictoffset > 0 && owner_cls->tp_dictoffset <= INT16_MAX); - cache->dict_offset = (uint16_t)owner_cls->tp_dictoffset; _Py_SET_OPCODE(*instr, LOAD_METHOD_WITH_DICT); break; case LAZY_DICT: assert(owner_cls->tp_dictoffset > 0 && owner_cls->tp_dictoffset <= INT16_MAX); - cache->dict_offset = (uint16_t)owner_cls->tp_dictoffset; _Py_SET_OPCODE(*instr, LOAD_METHOD_LAZY_DICT); break; }