Skip to content

Commit 7840ff3

Browse files
authored
gh-98831: Modernize the LOAD_ATTR family (#101488)
1 parent 95fb0e0 commit 7840ff3

File tree

3 files changed

+205
-230
lines changed

3 files changed

+205
-230
lines changed

Python/bytecodes.c

+61-116
Original file line numberDiff line numberDiff line change
@@ -436,7 +436,7 @@ dummy_func(
436436
PREDICT(JUMP_BACKWARD);
437437
}
438438

439-
family(store_subscr) = {
439+
family(store_subscr, INLINE_CACHE_ENTRIES_STORE_SUBSCR) = {
440440
STORE_SUBSCR,
441441
STORE_SUBSCR_DICT,
442442
STORE_SUBSCR_LIST_INT,
@@ -950,7 +950,7 @@ dummy_func(
950950
Py_DECREF(seq);
951951
}
952952

953-
family(store_attr) = {
953+
family(store_attr, INLINE_CACHE_ENTRIES_STORE_ATTR) = {
954954
STORE_ATTR,
955955
STORE_ATTR_INSTANCE_VALUE,
956956
STORE_ATTR_SLOT,
@@ -1436,6 +1436,20 @@ dummy_func(
14361436
PREDICT(JUMP_BACKWARD);
14371437
}
14381438

1439+
family(load_attr, INLINE_CACHE_ENTRIES_LOAD_ATTR) = {
1440+
LOAD_ATTR,
1441+
LOAD_ATTR_INSTANCE_VALUE,
1442+
LOAD_ATTR_MODULE,
1443+
LOAD_ATTR_WITH_HINT,
1444+
LOAD_ATTR_SLOT,
1445+
LOAD_ATTR_CLASS,
1446+
LOAD_ATTR_PROPERTY,
1447+
LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN,
1448+
LOAD_ATTR_METHOD_WITH_VALUES,
1449+
LOAD_ATTR_METHOD_NO_DICT,
1450+
LOAD_ATTR_METHOD_LAZY_DICT,
1451+
};
1452+
14391453
inst(LOAD_ATTR, (unused/9, owner -- res2 if (oparg & 1), res)) {
14401454
#if ENABLE_SPECIALIZATION
14411455
_PyAttrCache *cache = (_PyAttrCache *)next_instr;
@@ -1485,64 +1499,43 @@ dummy_func(
14851499
}
14861500
}
14871501

1488-
// error: LOAD_ATTR has irregular stack effect
1489-
inst(LOAD_ATTR_INSTANCE_VALUE) {
1502+
inst(LOAD_ATTR_INSTANCE_VALUE, (unused/1, type_version/2, index/1, unused/5, owner -- res2 if (oparg & 1), res)) {
14901503
assert(cframe.use_tracing == 0);
1491-
PyObject *owner = TOP();
1492-
PyObject *res;
14931504
PyTypeObject *tp = Py_TYPE(owner);
1494-
_PyAttrCache *cache = (_PyAttrCache *)next_instr;
1495-
uint32_t type_version = read_u32(cache->version);
14961505
assert(type_version != 0);
14971506
DEOPT_IF(tp->tp_version_tag != type_version, LOAD_ATTR);
14981507
assert(tp->tp_dictoffset < 0);
14991508
assert(tp->tp_flags & Py_TPFLAGS_MANAGED_DICT);
15001509
PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner);
15011510
DEOPT_IF(!_PyDictOrValues_IsValues(dorv), LOAD_ATTR);
1502-
res = _PyDictOrValues_GetValues(dorv)->values[cache->index];
1511+
res = _PyDictOrValues_GetValues(dorv)->values[index];
15031512
DEOPT_IF(res == NULL, LOAD_ATTR);
15041513
STAT_INC(LOAD_ATTR, hit);
15051514
Py_INCREF(res);
1506-
SET_TOP(NULL);
1507-
STACK_GROW((oparg & 1));
1508-
SET_TOP(res);
1515+
res2 = NULL;
15091516
Py_DECREF(owner);
1510-
JUMPBY(INLINE_CACHE_ENTRIES_LOAD_ATTR);
15111517
}
15121518

1513-
// error: LOAD_ATTR has irregular stack effect
1514-
inst(LOAD_ATTR_MODULE) {
1519+
inst(LOAD_ATTR_MODULE, (unused/1, type_version/2, index/1, unused/5, owner -- res2 if (oparg & 1), res)) {
15151520
assert(cframe.use_tracing == 0);
1516-
PyObject *owner = TOP();
1517-
PyObject *res;
1518-
_PyAttrCache *cache = (_PyAttrCache *)next_instr;
15191521
DEOPT_IF(!PyModule_CheckExact(owner), LOAD_ATTR);
15201522
PyDictObject *dict = (PyDictObject *)((PyModuleObject *)owner)->md_dict;
15211523
assert(dict != NULL);
1522-
DEOPT_IF(dict->ma_keys->dk_version != read_u32(cache->version),
1523-
LOAD_ATTR);
1524+
DEOPT_IF(dict->ma_keys->dk_version != type_version, LOAD_ATTR);
15241525
assert(dict->ma_keys->dk_kind == DICT_KEYS_UNICODE);
1525-
assert(cache->index < dict->ma_keys->dk_nentries);
1526-
PyDictUnicodeEntry *ep = DK_UNICODE_ENTRIES(dict->ma_keys) + cache->index;
1526+
assert(index < dict->ma_keys->dk_nentries);
1527+
PyDictUnicodeEntry *ep = DK_UNICODE_ENTRIES(dict->ma_keys) + index;
15271528
res = ep->me_value;
15281529
DEOPT_IF(res == NULL, LOAD_ATTR);
15291530
STAT_INC(LOAD_ATTR, hit);
15301531
Py_INCREF(res);
1531-
SET_TOP(NULL);
1532-
STACK_GROW((oparg & 1));
1533-
SET_TOP(res);
1532+
res2 = NULL;
15341533
Py_DECREF(owner);
1535-
JUMPBY(INLINE_CACHE_ENTRIES_LOAD_ATTR);
15361534
}
15371535

1538-
// error: LOAD_ATTR has irregular stack effect
1539-
inst(LOAD_ATTR_WITH_HINT) {
1536+
inst(LOAD_ATTR_WITH_HINT, (unused/1, type_version/2, index/1, unused/5, owner -- res2 if (oparg & 1), res)) {
15401537
assert(cframe.use_tracing == 0);
1541-
PyObject *owner = TOP();
1542-
PyObject *res;
15431538
PyTypeObject *tp = Py_TYPE(owner);
1544-
_PyAttrCache *cache = (_PyAttrCache *)next_instr;
1545-
uint32_t type_version = read_u32(cache->version);
15461539
assert(type_version != 0);
15471540
DEOPT_IF(tp->tp_version_tag != type_version, LOAD_ATTR);
15481541
assert(tp->tp_flags & Py_TPFLAGS_MANAGED_DICT);
@@ -1552,7 +1545,7 @@ dummy_func(
15521545
DEOPT_IF(dict == NULL, LOAD_ATTR);
15531546
assert(PyDict_CheckExact((PyObject *)dict));
15541547
PyObject *name = GETITEM(names, oparg>>1);
1555-
uint16_t hint = cache->index;
1548+
uint16_t hint = index;
15561549
DEOPT_IF(hint >= (size_t)dict->ma_keys->dk_nentries, LOAD_ATTR);
15571550
if (DK_IS_UNICODE(dict->ma_keys)) {
15581551
PyDictUnicodeEntry *ep = DK_UNICODE_ENTRIES(dict->ma_keys) + hint;
@@ -1567,73 +1560,49 @@ dummy_func(
15671560
DEOPT_IF(res == NULL, LOAD_ATTR);
15681561
STAT_INC(LOAD_ATTR, hit);
15691562
Py_INCREF(res);
1570-
SET_TOP(NULL);
1571-
STACK_GROW((oparg & 1));
1572-
SET_TOP(res);
1563+
res2 = NULL;
15731564
Py_DECREF(owner);
1574-
JUMPBY(INLINE_CACHE_ENTRIES_LOAD_ATTR);
15751565
}
15761566

1577-
// error: LOAD_ATTR has irregular stack effect
1578-
inst(LOAD_ATTR_SLOT) {
1567+
inst(LOAD_ATTR_SLOT, (unused/1, type_version/2, index/1, unused/5, owner -- res2 if (oparg & 1), res)) {
15791568
assert(cframe.use_tracing == 0);
1580-
PyObject *owner = TOP();
1581-
PyObject *res;
15821569
PyTypeObject *tp = Py_TYPE(owner);
1583-
_PyAttrCache *cache = (_PyAttrCache *)next_instr;
1584-
uint32_t type_version = read_u32(cache->version);
15851570
assert(type_version != 0);
15861571
DEOPT_IF(tp->tp_version_tag != type_version, LOAD_ATTR);
1587-
char *addr = (char *)owner + cache->index;
1572+
char *addr = (char *)owner + index;
15881573
res = *(PyObject **)addr;
15891574
DEOPT_IF(res == NULL, LOAD_ATTR);
15901575
STAT_INC(LOAD_ATTR, hit);
15911576
Py_INCREF(res);
1592-
SET_TOP(NULL);
1593-
STACK_GROW((oparg & 1));
1594-
SET_TOP(res);
1577+
res2 = NULL;
15951578
Py_DECREF(owner);
1596-
JUMPBY(INLINE_CACHE_ENTRIES_LOAD_ATTR);
15971579
}
15981580

1599-
// error: LOAD_ATTR has irregular stack effect
1600-
inst(LOAD_ATTR_CLASS) {
1581+
inst(LOAD_ATTR_CLASS, (unused/1, type_version/2, unused/2, descr/4, cls -- res2 if (oparg & 1), res)) {
16011582
assert(cframe.use_tracing == 0);
1602-
_PyLoadMethodCache *cache = (_PyLoadMethodCache *)next_instr;
16031583

1604-
PyObject *cls = TOP();
16051584
DEOPT_IF(!PyType_Check(cls), LOAD_ATTR);
1606-
uint32_t type_version = read_u32(cache->type_version);
16071585
DEOPT_IF(((PyTypeObject *)cls)->tp_version_tag != type_version,
16081586
LOAD_ATTR);
16091587
assert(type_version != 0);
16101588

16111589
STAT_INC(LOAD_ATTR, hit);
1612-
PyObject *res = read_obj(cache->descr);
1590+
res2 = NULL;
1591+
res = descr;
16131592
assert(res != NULL);
16141593
Py_INCREF(res);
1615-
SET_TOP(NULL);
1616-
STACK_GROW((oparg & 1));
1617-
SET_TOP(res);
16181594
Py_DECREF(cls);
1619-
JUMPBY(INLINE_CACHE_ENTRIES_LOAD_ATTR);
16201595
}
16211596

1622-
// error: LOAD_ATTR has irregular stack effect
1623-
inst(LOAD_ATTR_PROPERTY) {
1597+
inst(LOAD_ATTR_PROPERTY, (unused/1, type_version/2, func_version/2, fget/4, owner -- unused if (oparg & 1), unused)) {
16241598
assert(cframe.use_tracing == 0);
16251599
DEOPT_IF(tstate->interp->eval_frame, LOAD_ATTR);
1626-
_PyLoadMethodCache *cache = (_PyLoadMethodCache *)next_instr;
16271600

1628-
PyObject *owner = TOP();
16291601
PyTypeObject *cls = Py_TYPE(owner);
1630-
uint32_t type_version = read_u32(cache->type_version);
16311602
DEOPT_IF(cls->tp_version_tag != type_version, LOAD_ATTR);
16321603
assert(type_version != 0);
1633-
PyObject *fget = read_obj(cache->descr);
16341604
assert(Py_IS_TYPE(fget, &PyFunction_Type));
16351605
PyFunctionObject *f = (PyFunctionObject *)fget;
1636-
uint32_t func_version = read_u32(cache->keys_version);
16371606
assert(func_version != 0);
16381607
DEOPT_IF(f->func_version != func_version, LOAD_ATTR);
16391608
PyCodeObject *code = (PyCodeObject *)f->func_code;
@@ -1642,6 +1611,7 @@ dummy_func(
16421611
STAT_INC(LOAD_ATTR, hit);
16431612
Py_INCREF(fget);
16441613
_PyInterpreterFrame *new_frame = _PyFrame_PushUnchecked(tstate, f, 1);
1614+
// Manipulate stack directly because we exit with DISPATCH_INLINED().
16451615
SET_TOP(NULL);
16461616
int shrink_stack = !(oparg & 1);
16471617
STACK_SHRINK(shrink_stack);
@@ -1650,20 +1620,14 @@ dummy_func(
16501620
DISPATCH_INLINED(new_frame);
16511621
}
16521622

1653-
// error: LOAD_ATTR has irregular stack effect
1654-
inst(LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN) {
1623+
inst(LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN, (unused/1, type_version/2, func_version/2, getattribute/4, owner -- unused if (oparg & 1), unused)) {
16551624
assert(cframe.use_tracing == 0);
16561625
DEOPT_IF(tstate->interp->eval_frame, LOAD_ATTR);
1657-
_PyLoadMethodCache *cache = (_PyLoadMethodCache *)next_instr;
1658-
PyObject *owner = TOP();
16591626
PyTypeObject *cls = Py_TYPE(owner);
1660-
uint32_t type_version = read_u32(cache->type_version);
16611627
DEOPT_IF(cls->tp_version_tag != type_version, LOAD_ATTR);
16621628
assert(type_version != 0);
1663-
PyObject *getattribute = read_obj(cache->descr);
16641629
assert(Py_IS_TYPE(getattribute, &PyFunction_Type));
16651630
PyFunctionObject *f = (PyFunctionObject *)getattribute;
1666-
uint32_t func_version = read_u32(cache->keys_version);
16671631
assert(func_version != 0);
16681632
DEOPT_IF(f->func_version != func_version, LOAD_ATTR);
16691633
PyCodeObject *code = (PyCodeObject *)f->func_code;
@@ -1674,6 +1638,7 @@ dummy_func(
16741638
PyObject *name = GETITEM(names, oparg >> 1);
16751639
Py_INCREF(f);
16761640
_PyInterpreterFrame *new_frame = _PyFrame_PushUnchecked(tstate, f, 2);
1641+
// Manipulate stack directly because we exit with DISPATCH_INLINED().
16771642
SET_TOP(NULL);
16781643
int shrink_stack = !(oparg & 1);
16791644
STACK_SHRINK(shrink_stack);
@@ -1768,6 +1733,7 @@ dummy_func(
17681733
ERROR_IF(res == NULL, error);
17691734
}
17701735

1736+
// No cache size here, since this is a family of super-instructions.
17711737
family(compare_and_branch) = {
17721738
COMPARE_AND_BRANCH,
17731739
COMPARE_AND_BRANCH_FLOAT,
@@ -2373,69 +2339,54 @@ dummy_func(
23732339

23742340
}
23752341

2376-
// error: LOAD_ATTR has irregular stack effect
2377-
inst(LOAD_ATTR_METHOD_WITH_VALUES) {
2342+
inst(LOAD_ATTR_METHOD_WITH_VALUES, (unused/1, type_version/2, keys_version/2, descr/4, self -- res2 if (oparg & 1), res)) {
23782343
/* Cached method object */
23792344
assert(cframe.use_tracing == 0);
2380-
PyObject *self = TOP();
23812345
PyTypeObject *self_cls = Py_TYPE(self);
2382-
_PyLoadMethodCache *cache = (_PyLoadMethodCache *)next_instr;
2383-
uint32_t type_version = read_u32(cache->type_version);
23842346
assert(type_version != 0);
23852347
DEOPT_IF(self_cls->tp_version_tag != type_version, LOAD_ATTR);
23862348
assert(self_cls->tp_flags & Py_TPFLAGS_MANAGED_DICT);
23872349
PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(self);
23882350
DEOPT_IF(!_PyDictOrValues_IsValues(dorv), LOAD_ATTR);
23892351
PyHeapTypeObject *self_heap_type = (PyHeapTypeObject *)self_cls;
23902352
DEOPT_IF(self_heap_type->ht_cached_keys->dk_version !=
2391-
read_u32(cache->keys_version), LOAD_ATTR);
2353+
keys_version, LOAD_ATTR);
23922354
STAT_INC(LOAD_ATTR, hit);
2393-
PyObject *res = read_obj(cache->descr);
2394-
assert(res != NULL);
2395-
assert(_PyType_HasFeature(Py_TYPE(res), Py_TPFLAGS_METHOD_DESCRIPTOR));
2396-
SET_TOP(Py_NewRef(res));
2397-
PUSH(self);
2398-
JUMPBY(INLINE_CACHE_ENTRIES_LOAD_ATTR);
2355+
assert(descr != NULL);
2356+
res2 = Py_NewRef(descr);
2357+
assert(_PyType_HasFeature(Py_TYPE(res2), Py_TPFLAGS_METHOD_DESCRIPTOR));
2358+
res = self;
2359+
assert(oparg & 1);
23992360
}
24002361

2401-
// error: LOAD_ATTR has irregular stack effect
2402-
inst(LOAD_ATTR_METHOD_NO_DICT) {
2362+
inst(LOAD_ATTR_METHOD_NO_DICT, (unused/1, type_version/2, unused/2, descr/4, self -- res2 if (oparg & 1), res)) {
24032363
assert(cframe.use_tracing == 0);
2404-
PyObject *self = TOP();
24052364
PyTypeObject *self_cls = Py_TYPE(self);
2406-
_PyLoadMethodCache *cache = (_PyLoadMethodCache *)next_instr;
2407-
uint32_t type_version = read_u32(cache->type_version);
24082365
DEOPT_IF(self_cls->tp_version_tag != type_version, LOAD_ATTR);
24092366
assert(self_cls->tp_dictoffset == 0);
24102367
STAT_INC(LOAD_ATTR, hit);
2411-
PyObject *res = read_obj(cache->descr);
2412-
assert(res != NULL);
2413-
assert(_PyType_HasFeature(Py_TYPE(res), Py_TPFLAGS_METHOD_DESCRIPTOR));
2414-
SET_TOP(Py_NewRef(res));
2415-
PUSH(self);
2416-
JUMPBY(INLINE_CACHE_ENTRIES_LOAD_ATTR);
2368+
assert(descr != NULL);
2369+
assert(_PyType_HasFeature(Py_TYPE(descr), Py_TPFLAGS_METHOD_DESCRIPTOR));
2370+
res2 = Py_NewRef(descr);
2371+
res = self;
2372+
assert(oparg & 1);
24172373
}
24182374

2419-
// error: LOAD_ATTR has irregular stack effect
2420-
inst(LOAD_ATTR_METHOD_LAZY_DICT) {
2375+
inst(LOAD_ATTR_METHOD_LAZY_DICT, (unused/1, type_version/2, unused/2, descr/4, self -- res2 if (oparg & 1), res)) {
24212376
assert(cframe.use_tracing == 0);
2422-
PyObject *self = TOP();
24232377
PyTypeObject *self_cls = Py_TYPE(self);
2424-
_PyLoadMethodCache *cache = (_PyLoadMethodCache *)next_instr;
2425-
uint32_t type_version = read_u32(cache->type_version);
24262378
DEOPT_IF(self_cls->tp_version_tag != type_version, LOAD_ATTR);
24272379
Py_ssize_t dictoffset = self_cls->tp_dictoffset;
24282380
assert(dictoffset > 0);
24292381
PyObject *dict = *(PyObject **)((char *)self + dictoffset);
24302382
/* This object has a __dict__, just not yet created */
24312383
DEOPT_IF(dict != NULL, LOAD_ATTR);
24322384
STAT_INC(LOAD_ATTR, hit);
2433-
PyObject *res = read_obj(cache->descr);
2434-
assert(res != NULL);
2435-
assert(_PyType_HasFeature(Py_TYPE(res), Py_TPFLAGS_METHOD_DESCRIPTOR));
2436-
SET_TOP(Py_NewRef(res));
2437-
PUSH(self);
2438-
JUMPBY(INLINE_CACHE_ENTRIES_LOAD_ATTR);
2385+
assert(descr != NULL);
2386+
assert(_PyType_HasFeature(Py_TYPE(descr), Py_TPFLAGS_METHOD_DESCRIPTOR));
2387+
res2 = Py_NewRef(descr);
2388+
res = self;
2389+
assert(oparg & 1);
24392390
}
24402391

24412392
// stack effect: (__0, __array[oparg] -- )
@@ -3265,27 +3216,21 @@ dummy_func(
32653216

32663217
// Future families go below this point //
32673218

3268-
family(call) = {
3219+
family(call, INLINE_CACHE_ENTRIES_CALL) = {
32693220
CALL, CALL_PY_EXACT_ARGS,
32703221
CALL_PY_WITH_DEFAULTS, CALL_BOUND_METHOD_EXACT_ARGS, CALL_BUILTIN_CLASS,
32713222
CALL_BUILTIN_FAST_WITH_KEYWORDS, CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS, CALL_NO_KW_BUILTIN_FAST,
32723223
CALL_NO_KW_BUILTIN_O, CALL_NO_KW_ISINSTANCE, CALL_NO_KW_LEN,
32733224
CALL_NO_KW_LIST_APPEND, CALL_NO_KW_METHOD_DESCRIPTOR_FAST, CALL_NO_KW_METHOD_DESCRIPTOR_NOARGS,
32743225
CALL_NO_KW_METHOD_DESCRIPTOR_O, CALL_NO_KW_STR_1, CALL_NO_KW_TUPLE_1,
32753226
CALL_NO_KW_TYPE_1 };
3276-
family(for_iter) = {
3227+
family(for_iter, INLINE_CACHE_ENTRIES_FOR_ITER) = {
32773228
FOR_ITER, FOR_ITER_LIST,
32783229
FOR_ITER_RANGE };
3279-
family(load_attr) = {
3280-
LOAD_ATTR, LOAD_ATTR_CLASS,
3281-
LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN, LOAD_ATTR_INSTANCE_VALUE, LOAD_ATTR_MODULE,
3282-
LOAD_ATTR_PROPERTY, LOAD_ATTR_SLOT, LOAD_ATTR_WITH_HINT,
3283-
LOAD_ATTR_METHOD_LAZY_DICT, LOAD_ATTR_METHOD_NO_DICT,
3284-
LOAD_ATTR_METHOD_WITH_VALUES };
3285-
family(load_global) = {
3230+
family(load_global, INLINE_CACHE_ENTRIES_LOAD_GLOBAL) = {
32863231
LOAD_GLOBAL, LOAD_GLOBAL_BUILTIN,
32873232
LOAD_GLOBAL_MODULE };
32883233
family(store_fast) = { STORE_FAST, STORE_FAST__LOAD_FAST, STORE_FAST__STORE_FAST };
3289-
family(unpack_sequence) = {
3234+
family(unpack_sequence, INLINE_CACHE_ENTRIES_UNPACK_SEQUENCE) = {
32903235
UNPACK_SEQUENCE, UNPACK_SEQUENCE_LIST,
32913236
UNPACK_SEQUENCE_TUPLE, UNPACK_SEQUENCE_TWO_TUPLE };

0 commit comments

Comments
 (0)