summaryrefslogtreecommitdiffstats
path: root/Python
diff options
context:
space:
mode:
authorDennis Sweeney <36520290+sweeneyde@users.noreply.github.com>2022-02-24 14:55:59 (GMT)
committerGitHub <noreply@github.com>2022-02-24 14:55:59 (GMT)
commita52d2528a405c1e2bfeb6470cb3313a5338dc45f (patch)
treec7a8d9c23869de114ba3c5f09a7582e1185b30c4 /Python
parent4fccf910738d1442852cb900747e6dccb8fe03ef (diff)
downloadcpython-a52d2528a405c1e2bfeb6470cb3313a5338dc45f.zip
cpython-a52d2528a405c1e2bfeb6470cb3313a5338dc45f.tar.gz
cpython-a52d2528a405c1e2bfeb6470cb3313a5338dc45f.tar.bz2
bpo-46823: Implement LOAD_FAST__LOAD_ATTR_INSTANCE_VALUE superinstruction (GH-31484)
Diffstat (limited to 'Python')
-rw-r--r--Python/ceval.c80
-rw-r--r--Python/opcode_targets.h2
-rw-r--r--Python/specialize.c10
3 files changed, 88 insertions, 4 deletions
diff --git a/Python/ceval.c b/Python/ceval.c
index 8077570..f3bdaf1 100644
--- a/Python/ceval.c
+++ b/Python/ceval.c
@@ -3444,6 +3444,34 @@ handle_eval_breaker:
}
}
+ TARGET(LOAD_FAST__LOAD_ATTR_INSTANCE_VALUE) {
+ assert(cframe.use_tracing == 0);
+ PyObject *owner = GETLOCAL(oparg); // borrowed
+ if (owner == NULL) {
+ goto unbound_local_error;
+ }
+ // GET_CACHE(), but for the following opcode
+ assert(_Py_OPCODE(*next_instr) == LOAD_ATTR_INSTANCE_VALUE);
+ SpecializedCacheEntry *caches = _GetSpecializedCacheEntryForInstruction(
+ first_instr, INSTR_OFFSET() + 1, _Py_OPARG(*next_instr));
+ _PyAdaptiveEntry *cache0 = &caches[0].adaptive;
+ assert(cache0->version != 0);
+ PyTypeObject *tp = Py_TYPE(owner);
+ // These DEOPT_IF miss branches do PUSH(Py_NewRef(owner)).
+ DEOPT_IF(tp->tp_version_tag != cache0->version,
+ LOAD_FAST__LOAD_ATTR_INSTANCE_VALUE);
+ assert(tp->tp_dictoffset < 0);
+ assert(tp->tp_flags & Py_TPFLAGS_MANAGED_DICT);
+ PyDictValues *values = *_PyObject_ValuesPointer(owner);
+ DEOPT_IF(values == NULL, LOAD_FAST__LOAD_ATTR_INSTANCE_VALUE);
+ PyObject *res = values->values[cache0->index];
+ DEOPT_IF(res == NULL, LOAD_FAST__LOAD_ATTR_INSTANCE_VALUE);
+ STAT_INC(LOAD_ATTR, hit);
+ PUSH(Py_NewRef(res));
+ next_instr++;
+ NOTRACE_DISPATCH();
+ }
+
TARGET(LOAD_ATTR_INSTANCE_VALUE) {
assert(cframe.use_tracing == 0);
PyObject *owner = TOP();
@@ -3452,13 +3480,13 @@ handle_eval_breaker:
SpecializedCacheEntry *caches = GET_CACHE();
_PyAdaptiveEntry *cache0 = &caches[0].adaptive;
assert(cache0->version != 0);
- DEOPT_IF(tp->tp_version_tag != cache0->version, LOAD_ATTR);
+ DEOPT_IF(tp->tp_version_tag != cache0->version, LOAD_ATTR_INSTANCE_VALUE);
assert(tp->tp_dictoffset < 0);
assert(tp->tp_flags & Py_TPFLAGS_MANAGED_DICT);
PyDictValues *values = *_PyObject_ValuesPointer(owner);
- DEOPT_IF(values == NULL, LOAD_ATTR);
+ DEOPT_IF(values == NULL, LOAD_ATTR_INSTANCE_VALUE);
res = values->values[cache0->index];
- DEOPT_IF(res == NULL, LOAD_ATTR);
+ DEOPT_IF(res == NULL, LOAD_ATTR_INSTANCE_VALUE);
STAT_INC(LOAD_ATTR, hit);
Py_INCREF(res);
SET_TOP(res);
@@ -5515,6 +5543,52 @@ MISS_WITH_CACHE(BINARY_SUBSCR)
MISS_WITH_CACHE(UNPACK_SEQUENCE)
MISS_WITH_OPARG_COUNTER(STORE_SUBSCR)
+LOAD_ATTR_INSTANCE_VALUE_miss:
+ {
+ // Special-cased so that if LOAD_ATTR_INSTANCE_VALUE
+ // gets replaced, then any preceeding
+ // LOAD_FAST__LOAD_ATTR_INSTANCE_VALUE gets replaced as well
+ STAT_INC(LOAD_ATTR_INSTANCE_VALUE, miss);
+ STAT_INC(LOAD_ATTR, miss);
+ _PyAdaptiveEntry *cache = &GET_CACHE()->adaptive;
+ cache->counter--;
+ if (cache->counter == 0) {
+ next_instr[-1] = _Py_MAKECODEUNIT(LOAD_ATTR_ADAPTIVE, _Py_OPARG(next_instr[-1]));
+ if (_Py_OPCODE(next_instr[-2]) == LOAD_FAST__LOAD_ATTR_INSTANCE_VALUE) {
+ next_instr[-2] = _Py_MAKECODEUNIT(LOAD_FAST, _Py_OPARG(next_instr[-2]));
+ if (_Py_OPCODE(next_instr[-3]) == LOAD_FAST) {
+ next_instr[-3] = _Py_MAKECODEUNIT(LOAD_FAST__LOAD_FAST, _Py_OPARG(next_instr[-3]));
+ }
+ }
+ STAT_INC(LOAD_ATTR, deopt);
+ cache_backoff(cache);
+ }
+ oparg = cache->original_oparg;
+ JUMP_TO_INSTRUCTION(LOAD_ATTR);
+ }
+
+LOAD_FAST__LOAD_ATTR_INSTANCE_VALUE_miss:
+ {
+ // This is special-cased because we have a superinstruction
+ // that includes a specialized instruction.
+ // If the specialized portion misses, carry out
+ // the first instruction, then perform a miss
+ // for the second instruction as usual.
+
+ // Do LOAD_FAST
+ {
+ PyObject *value = GETLOCAL(oparg);
+ assert(value != NULL); // Already checked if unbound
+ Py_INCREF(value);
+ PUSH(value);
+ NEXTOPARG();
+ next_instr++;
+ }
+
+ // Now we are in the correct state for LOAD_ATTR
+ goto LOAD_ATTR_INSTANCE_VALUE_miss;
+ }
+
binary_subscr_dict_error:
{
PyObject *sub = POP();
diff --git a/Python/opcode_targets.h b/Python/opcode_targets.h
index cf5bdc7..fac671e 100644
--- a/Python/opcode_targets.h
+++ b/Python/opcode_targets.h
@@ -173,7 +173,7 @@ static void *opcode_targets[256] = {
&&TARGET_CALL,
&&TARGET_KW_NAMES,
&&TARGET_STORE_FAST__STORE_FAST,
- &&_unknown_opcode,
+ &&TARGET_LOAD_FAST__LOAD_ATTR_INSTANCE_VALUE,
&&_unknown_opcode,
&&_unknown_opcode,
&&_unknown_opcode,
diff --git a/Python/specialize.c b/Python/specialize.c
index 816cca1..1641766 100644
--- a/Python/specialize.c
+++ b/Python/specialize.c
@@ -889,6 +889,16 @@ _Py_Specialize_LoadAttr(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name, Sp
return -1;
}
if (err) {
+ if (_Py_OPCODE(instr[0]) == LOAD_ATTR_INSTANCE_VALUE) {
+ // Note: instr[-1] exists because there's something on the stack,
+ // and instr[-2] exists because there's at least a RESUME as well.
+ if (_Py_OPCODE(instr[-1]) == LOAD_FAST) {
+ instr[-1] = _Py_MAKECODEUNIT(LOAD_FAST__LOAD_ATTR_INSTANCE_VALUE, _Py_OPARG(instr[-1]));
+ if (_Py_OPCODE(instr[-2]) == LOAD_FAST__LOAD_FAST) {
+ instr[-2] = _Py_MAKECODEUNIT(LOAD_FAST, _Py_OPARG(instr[-2]));
+ }
+ }
+ }
goto success;
}
fail: