Skip to content

Commit a52d252

Browse files
authored
bpo-46823: Implement LOAD_FAST__LOAD_ATTR_INSTANCE_VALUE superinstruction (GH-31484)
1 parent 4fccf91 commit a52d252

File tree

6 files changed

+91
-4
lines changed

6 files changed

+91
-4
lines changed

Include/opcode.h

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Lib/opcode.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,7 @@ def jabs_op(name, op):
295295
"LOAD_FAST__LOAD_CONST",
296296
"LOAD_CONST__LOAD_FAST",
297297
"STORE_FAST__STORE_FAST",
298+
"LOAD_FAST__LOAD_ATTR_INSTANCE_VALUE",
298299
]
299300
_specialization_stats = [
300301
"success",
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Implement a specialized combined opcode ``LOAD_FAST__LOAD_ATTR_INSTANCE_VALUE``. Patch by Dennis Sweeney.

Python/ceval.c

Lines changed: 77 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3444,6 +3444,34 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int thr
34443444
}
34453445
}
34463446

3447+
TARGET(LOAD_FAST__LOAD_ATTR_INSTANCE_VALUE) {
3448+
assert(cframe.use_tracing == 0);
3449+
PyObject *owner = GETLOCAL(oparg); // borrowed
3450+
if (owner == NULL) {
3451+
goto unbound_local_error;
3452+
}
3453+
// GET_CACHE(), but for the following opcode
3454+
assert(_Py_OPCODE(*next_instr) == LOAD_ATTR_INSTANCE_VALUE);
3455+
SpecializedCacheEntry *caches = _GetSpecializedCacheEntryForInstruction(
3456+
first_instr, INSTR_OFFSET() + 1, _Py_OPARG(*next_instr));
3457+
_PyAdaptiveEntry *cache0 = &caches[0].adaptive;
3458+
assert(cache0->version != 0);
3459+
PyTypeObject *tp = Py_TYPE(owner);
3460+
// These DEOPT_IF miss branches do PUSH(Py_NewRef(owner)).
3461+
DEOPT_IF(tp->tp_version_tag != cache0->version,
3462+
LOAD_FAST__LOAD_ATTR_INSTANCE_VALUE);
3463+
assert(tp->tp_dictoffset < 0);
3464+
assert(tp->tp_flags & Py_TPFLAGS_MANAGED_DICT);
3465+
PyDictValues *values = *_PyObject_ValuesPointer(owner);
3466+
DEOPT_IF(values == NULL, LOAD_FAST__LOAD_ATTR_INSTANCE_VALUE);
3467+
PyObject *res = values->values[cache0->index];
3468+
DEOPT_IF(res == NULL, LOAD_FAST__LOAD_ATTR_INSTANCE_VALUE);
3469+
STAT_INC(LOAD_ATTR, hit);
3470+
PUSH(Py_NewRef(res));
3471+
next_instr++;
3472+
NOTRACE_DISPATCH();
3473+
}
3474+
34473475
TARGET(LOAD_ATTR_INSTANCE_VALUE) {
34483476
assert(cframe.use_tracing == 0);
34493477
PyObject *owner = TOP();
@@ -3452,13 +3480,13 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int thr
34523480
SpecializedCacheEntry *caches = GET_CACHE();
34533481
_PyAdaptiveEntry *cache0 = &caches[0].adaptive;
34543482
assert(cache0->version != 0);
3455-
DEOPT_IF(tp->tp_version_tag != cache0->version, LOAD_ATTR);
3483+
DEOPT_IF(tp->tp_version_tag != cache0->version, LOAD_ATTR_INSTANCE_VALUE);
34563484
assert(tp->tp_dictoffset < 0);
34573485
assert(tp->tp_flags & Py_TPFLAGS_MANAGED_DICT);
34583486
PyDictValues *values = *_PyObject_ValuesPointer(owner);
3459-
DEOPT_IF(values == NULL, LOAD_ATTR);
3487+
DEOPT_IF(values == NULL, LOAD_ATTR_INSTANCE_VALUE);
34603488
res = values->values[cache0->index];
3461-
DEOPT_IF(res == NULL, LOAD_ATTR);
3489+
DEOPT_IF(res == NULL, LOAD_ATTR_INSTANCE_VALUE);
34623490
STAT_INC(LOAD_ATTR, hit);
34633491
Py_INCREF(res);
34643492
SET_TOP(res);
@@ -5515,6 +5543,52 @@ MISS_WITH_CACHE(BINARY_SUBSCR)
55155543
MISS_WITH_CACHE(UNPACK_SEQUENCE)
55165544
MISS_WITH_OPARG_COUNTER(STORE_SUBSCR)
55175545

5546+
LOAD_ATTR_INSTANCE_VALUE_miss:
5547+
{
5548+
// Special-cased so that if LOAD_ATTR_INSTANCE_VALUE
5549+
// gets replaced, then any preceeding
5550+
// LOAD_FAST__LOAD_ATTR_INSTANCE_VALUE gets replaced as well
5551+
STAT_INC(LOAD_ATTR_INSTANCE_VALUE, miss);
5552+
STAT_INC(LOAD_ATTR, miss);
5553+
_PyAdaptiveEntry *cache = &GET_CACHE()->adaptive;
5554+
cache->counter--;
5555+
if (cache->counter == 0) {
5556+
next_instr[-1] = _Py_MAKECODEUNIT(LOAD_ATTR_ADAPTIVE, _Py_OPARG(next_instr[-1]));
5557+
if (_Py_OPCODE(next_instr[-2]) == LOAD_FAST__LOAD_ATTR_INSTANCE_VALUE) {
5558+
next_instr[-2] = _Py_MAKECODEUNIT(LOAD_FAST, _Py_OPARG(next_instr[-2]));
5559+
if (_Py_OPCODE(next_instr[-3]) == LOAD_FAST) {
5560+
next_instr[-3] = _Py_MAKECODEUNIT(LOAD_FAST__LOAD_FAST, _Py_OPARG(next_instr[-3]));
5561+
}
5562+
}
5563+
STAT_INC(LOAD_ATTR, deopt);
5564+
cache_backoff(cache);
5565+
}
5566+
oparg = cache->original_oparg;
5567+
JUMP_TO_INSTRUCTION(LOAD_ATTR);
5568+
}
5569+
5570+
LOAD_FAST__LOAD_ATTR_INSTANCE_VALUE_miss:
5571+
{
5572+
// This is special-cased because we have a superinstruction
5573+
// that includes a specialized instruction.
5574+
// If the specialized portion misses, carry out
5575+
// the first instruction, then perform a miss
5576+
// for the second instruction as usual.
5577+
5578+
// Do LOAD_FAST
5579+
{
5580+
PyObject *value = GETLOCAL(oparg);
5581+
assert(value != NULL); // Already checked if unbound
5582+
Py_INCREF(value);
5583+
PUSH(value);
5584+
NEXTOPARG();
5585+
next_instr++;
5586+
}
5587+
5588+
// Now we are in the correct state for LOAD_ATTR
5589+
goto LOAD_ATTR_INSTANCE_VALUE_miss;
5590+
}
5591+
55185592
binary_subscr_dict_error:
55195593
{
55205594
PyObject *sub = POP();

Python/opcode_targets.h

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Python/specialize.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -889,6 +889,16 @@ _Py_Specialize_LoadAttr(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name, Sp
889889
return -1;
890890
}
891891
if (err) {
892+
if (_Py_OPCODE(instr[0]) == LOAD_ATTR_INSTANCE_VALUE) {
893+
// Note: instr[-1] exists because there's something on the stack,
894+
// and instr[-2] exists because there's at least a RESUME as well.
895+
if (_Py_OPCODE(instr[-1]) == LOAD_FAST) {
896+
instr[-1] = _Py_MAKECODEUNIT(LOAD_FAST__LOAD_ATTR_INSTANCE_VALUE, _Py_OPARG(instr[-1]));
897+
if (_Py_OPCODE(instr[-2]) == LOAD_FAST__LOAD_FAST) {
898+
instr[-2] = _Py_MAKECODEUNIT(LOAD_FAST, _Py_OPARG(instr[-2]));
899+
}
900+
}
901+
}
892902
goto success;
893903
}
894904
fail:

0 commit comments

Comments
 (0)