Skip to content

Commit f012df7

Browse files
authored
Shrink the LOAD_METHOD cache by one codeunit. (#93537)
1 parent 75ceae0 commit f012df7

File tree

8 files changed

+12
-18
lines changed

8 files changed

+12
-18
lines changed

Include/internal/pycore_code.h

-1
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,6 @@ typedef struct {
6565
typedef struct {
6666
_Py_CODEUNIT counter;
6767
_Py_CODEUNIT type_version[2];
68-
_Py_CODEUNIT dict_offset;
6968
_Py_CODEUNIT keys_version[2];
7069
_Py_CODEUNIT descr[4];
7170
} _PyLoadMethodCache;

Include/internal/pycore_opcode.h

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Lib/importlib/_bootstrap_external.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -407,6 +407,7 @@ def _write_atomic(path, data, mode=0o666):
407407
# Python 3.12a1 3500 (Remove PRECALL opcode)
408408
# Python 3.12a1 3501 (YIELD_VALUE oparg == stack_depth)
409409
# Python 3.12a1 3502 (LOAD_FAST_CHECK, no NULL-check in LOAD_FAST)
410+
# Python 3.12a1 3503 (Shrink LOAD_METHOD cache)
410411

411412
# Python 3.13 will start with 3550
412413

@@ -420,7 +421,7 @@ def _write_atomic(path, data, mode=0o666):
420421
# Whenever MAGIC_NUMBER is changed, the ranges in the magic_values array
421422
# in PC/launcher.c must also be updated.
422423

423-
MAGIC_NUMBER = (3502).to_bytes(2, 'little') + b'\r\n'
424+
MAGIC_NUMBER = (3503).to_bytes(2, 'little') + b'\r\n'
424425

425426
_RAW_MAGIC_NUMBER = int.from_bytes(MAGIC_NUMBER, 'little') # For import.c
426427

Lib/opcode.py

-1
Original file line numberDiff line numberDiff line change
@@ -383,7 +383,6 @@ def jabs_op(name, op):
383383
"LOAD_METHOD": {
384384
"counter": 1,
385385
"type_version": 2,
386-
"dict_offset": 1,
387386
"keys_version": 2,
388387
"descr": 4,
389388
},

Lib/test/test_dis.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1047,7 +1047,7 @@ def test_show_caches(self):
10471047
for cache in caches:
10481048
self.assertRegex(cache, pattern)
10491049
self.assertEqual(caches.count(""), 8)
1050-
self.assertEqual(len(caches), 23)
1050+
self.assertEqual(len(caches), 22)
10511051

10521052

10531053
class DisWithFileTests(DisTests):
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Reduce the size of the inline cache for ``LOAD_METHOD`` by 2 bytes.

Python/ceval.c

+4-7
Original file line numberDiff line numberDiff line change
@@ -4581,12 +4581,9 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
45814581
DEOPT_IF(self_cls->tp_version_tag != read_u32(cache->type_version),
45824582
LOAD_METHOD);
45834583
/* Treat index as a signed 16 bit value */
4584-
int dictoffset = *(int16_t *)&cache->dict_offset;
4584+
int dictoffset = self_cls->tp_dictoffset;
4585+
assert(dictoffset > 0);
45854586
PyDictObject **dictptr = (PyDictObject**)(((char *)self)+dictoffset);
4586-
assert(
4587-
dictoffset == MANAGED_DICT_OFFSET ||
4588-
(dictoffset == self_cls->tp_dictoffset && dictoffset > 0)
4589-
);
45904587
PyDictObject *dict = *dictptr;
45914588
DEOPT_IF(dict == NULL, LOAD_METHOD);
45924589
DEOPT_IF(dict->ma_keys->dk_version != read_u32(cache->keys_version),
@@ -4628,9 +4625,9 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
46284625
_PyLoadMethodCache *cache = (_PyLoadMethodCache *)next_instr;
46294626
uint32_t type_version = read_u32(cache->type_version);
46304627
DEOPT_IF(self_cls->tp_version_tag != type_version, LOAD_METHOD);
4631-
int dictoffset = cache->dict_offset;
4628+
int dictoffset = self_cls->tp_dictoffset;
4629+
assert(dictoffset > 0);
46324630
PyObject *dict = *(PyObject **)((char *)self + dictoffset);
4633-
assert(dictoffset == self_cls->tp_dictoffset && dictoffset > 0);
46344631
/* This object has a __dict__, just not yet created */
46354632
DEOPT_IF(dict != NULL, LOAD_METHOD);
46364633
STAT_INC(LOAD_METHOD, hit);

Python/specialize.c

+3-6
Original file line numberDiff line numberDiff line change
@@ -986,7 +986,7 @@ _Py_Specialize_LoadMethod(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name)
986986
}
987987
}
988988
}
989-
if (dictkind == MANAGED_VALUES || dictkind == MANAGED_DICT || dictkind == OFFSET_DICT) {
989+
if (dictkind == MANAGED_VALUES || dictkind == OFFSET_DICT) {
990990
Py_ssize_t index = _PyDictKeys_StringLookup(keys, name);
991991
if (index != DKIX_EMPTY) {
992992
SPECIALIZATION_FAIL(LOAD_METHOD, SPEC_FAIL_LOAD_METHOD_IS_ATTR);
@@ -1007,17 +1007,14 @@ _Py_Specialize_LoadMethod(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name)
10071007
_Py_SET_OPCODE(*instr, LOAD_METHOD_WITH_VALUES);
10081008
break;
10091009
case MANAGED_DICT:
1010-
*(int16_t *)&cache->dict_offset = (int16_t)MANAGED_DICT_OFFSET;
1011-
_Py_SET_OPCODE(*instr, LOAD_METHOD_WITH_DICT);
1012-
break;
1010+
SPECIALIZATION_FAIL(LOAD_METHOD, SPEC_FAIL_LOAD_METHOD_HAS_MANAGED_DICT);
1011+
goto fail;
10131012
case OFFSET_DICT:
10141013
assert(owner_cls->tp_dictoffset > 0 && owner_cls->tp_dictoffset <= INT16_MAX);
1015-
cache->dict_offset = (uint16_t)owner_cls->tp_dictoffset;
10161014
_Py_SET_OPCODE(*instr, LOAD_METHOD_WITH_DICT);
10171015
break;
10181016
case LAZY_DICT:
10191017
assert(owner_cls->tp_dictoffset > 0 && owner_cls->tp_dictoffset <= INT16_MAX);
1020-
cache->dict_offset = (uint16_t)owner_cls->tp_dictoffset;
10211018
_Py_SET_OPCODE(*instr, LOAD_METHOD_LAZY_DICT);
10221019
break;
10231020
}

0 commit comments

Comments
 (0)