gh-98831: Support conditional effects; use for LOAD_ATTR (#101333)

This commit is contained in:
Guido van Rossum 2023-01-29 17:28:39 -08:00 committed by GitHub
parent c4170c36b0
commit f5a3d91b6c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 194 additions and 120 deletions

View file

@ -51,7 +51,7 @@
// Dummy variables for stack effects.
static PyObject *value, *value1, *value2, *left, *right, *res, *sum, *prod, *sub;
static PyObject *container, *start, *stop, *v, *lhs, *rhs;
static PyObject *container, *start, *stop, *v, *lhs, *rhs, *res2;
static PyObject *list, *tuple, *dict, *owner, *set, *str, *tup, *map, *keys;
static PyObject *exit_func, *lasti, *val, *retval, *obj, *iter;
static PyObject *aiter, *awaitable, *iterable, *w, *exc_value, *bc;
@ -1438,13 +1438,11 @@ dummy_func(
PREDICT(JUMP_BACKWARD);
}
// error: LOAD_ATTR has irregular stack effect
inst(LOAD_ATTR) {
inst(LOAD_ATTR, (unused/9, owner -- res2 if (oparg & 1), res)) {
#if ENABLE_SPECIALIZATION
_PyAttrCache *cache = (_PyAttrCache *)next_instr;
if (ADAPTIVE_COUNTER_IS_ZERO(cache->counter)) {
assert(cframe.use_tracing == 0);
PyObject *owner = TOP();
PyObject *name = GETITEM(names, oparg>>1);
next_instr--;
_Py_Specialize_LoadAttr(owner, next_instr, name);
@ -1454,26 +1452,18 @@ dummy_func(
DECREMENT_ADAPTIVE_COUNTER(cache->counter);
#endif /* ENABLE_SPECIALIZATION */
PyObject *name = GETITEM(names, oparg >> 1);
PyObject *owner = TOP();
if (oparg & 1) {
/* Designed to work in tandem with CALL. */
/* Designed to work in tandem with CALL, pushes two values. */
PyObject* meth = NULL;
int meth_found = _PyObject_GetMethod(owner, name, &meth);
if (meth == NULL) {
/* Most likely attribute wasn't found. */
goto error;
}
if (meth_found) {
if (_PyObject_GetMethod(owner, name, &meth)) {
/* We can bypass temporary bound method object.
meth is unbound method and obj is self.
meth | self | arg1 | ... | argN
*/
SET_TOP(meth);
PUSH(owner); // self
assert(meth != NULL); // No errors on this branch
res2 = meth;
res = owner; // Transfer ownership
}
else {
/* meth is not an unbound method (but a regular attr, or
@ -1483,20 +1473,18 @@ dummy_func(
NULL | meth | arg1 | ... | argN
*/
SET_TOP(NULL);
Py_DECREF(owner);
PUSH(meth);
ERROR_IF(meth == NULL, error);
res2 = NULL;
res = meth;
}
}
else {
PyObject *res = PyObject_GetAttr(owner, name);
if (res == NULL) {
goto error;
}
/* Classic, pushes one value. */
res = PyObject_GetAttr(owner, name);
Py_DECREF(owner);
SET_TOP(res);
ERROR_IF(res == NULL, error);
}
JUMPBY(INLINE_CACHE_ENTRIES_LOAD_ATTR);
}
// error: LOAD_ATTR has irregular stack effect

View file

@ -1745,11 +1745,13 @@
TARGET(LOAD_ATTR) {
PREDICTED(LOAD_ATTR);
PyObject *owner = PEEK(1);
PyObject *res2 = NULL;
PyObject *res;
#if ENABLE_SPECIALIZATION
_PyAttrCache *cache = (_PyAttrCache *)next_instr;
if (ADAPTIVE_COUNTER_IS_ZERO(cache->counter)) {
assert(cframe.use_tracing == 0);
PyObject *owner = TOP();
PyObject *name = GETITEM(names, oparg>>1);
next_instr--;
_Py_Specialize_LoadAttr(owner, next_instr, name);
@ -1759,26 +1761,18 @@
DECREMENT_ADAPTIVE_COUNTER(cache->counter);
#endif /* ENABLE_SPECIALIZATION */
PyObject *name = GETITEM(names, oparg >> 1);
PyObject *owner = TOP();
if (oparg & 1) {
/* Designed to work in tandem with CALL. */
/* Designed to work in tandem with CALL, pushes two values. */
PyObject* meth = NULL;
int meth_found = _PyObject_GetMethod(owner, name, &meth);
if (meth == NULL) {
/* Most likely attribute wasn't found. */
goto error;
}
if (meth_found) {
if (_PyObject_GetMethod(owner, name, &meth)) {
/* We can bypass temporary bound method object.
meth is unbound method and obj is self.
meth | self | arg1 | ... | argN
*/
SET_TOP(meth);
PUSH(owner); // self
assert(meth != NULL); // No errors on this branch
res2 = meth;
res = owner; // Transfer ownership
}
else {
/* meth is not an unbound method (but a regular attr, or
@ -1788,20 +1782,22 @@
NULL | meth | arg1 | ... | argN
*/
SET_TOP(NULL);
Py_DECREF(owner);
PUSH(meth);
if (meth == NULL) goto pop_1_error;
res2 = NULL;
res = meth;
}
}
else {
PyObject *res = PyObject_GetAttr(owner, name);
if (res == NULL) {
goto error;
}
/* Classic, pushes one value. */
res = PyObject_GetAttr(owner, name);
Py_DECREF(owner);
SET_TOP(res);
if (res == NULL) goto pop_1_error;
}
JUMPBY(INLINE_CACHE_ENTRIES_LOAD_ATTR);
STACK_GROW(((oparg & 1) ? 1 : 0));
POKE(1, res);
if (oparg & 1) { POKE(1 + ((oparg & 1) ? 1 : 0), res2); }
JUMPBY(9);
DISPATCH();
}

View file

@ -185,7 +185,7 @@ _PyOpcode_num_popped(int opcode, int oparg) {
case MAP_ADD:
return 2;
case LOAD_ATTR:
return -1;
return 1;
case LOAD_ATTR_INSTANCE_VALUE:
return -1;
case LOAD_ATTR_MODULE:
@ -531,7 +531,7 @@ _PyOpcode_num_pushed(int opcode, int oparg) {
case MAP_ADD:
return 0;
case LOAD_ATTR:
return -1;
return ((oparg & 1) ? 1 : 0) + 1;
case LOAD_ATTR_INSTANCE_VALUE:
return -1;
case LOAD_ATTR_MODULE:
@ -694,7 +694,7 @@ _PyOpcode_num_pushed(int opcode, int oparg) {
}
#endif
enum Direction { DIR_NONE, DIR_READ, DIR_WRITE };
enum InstructionFormat { INSTR_FMT_IB, INSTR_FMT_IBC, INSTR_FMT_IBC0, INSTR_FMT_IBC000, INSTR_FMT_IBIB, INSTR_FMT_IX, INSTR_FMT_IXC, INSTR_FMT_IXC000 };
enum InstructionFormat { INSTR_FMT_IB, INSTR_FMT_IBC, INSTR_FMT_IBC0, INSTR_FMT_IBC000, INSTR_FMT_IBC00000000, INSTR_FMT_IBIB, INSTR_FMT_IX, INSTR_FMT_IXC, INSTR_FMT_IXC000 };
struct opcode_metadata {
enum Direction dir_op1;
enum Direction dir_op2;
@ -791,7 +791,7 @@ struct opcode_metadata {
[DICT_UPDATE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
[DICT_MERGE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
[MAP_ADD] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
[LOAD_ATTR] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
[LOAD_ATTR] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC00000000 },
[LOAD_ATTR_INSTANCE_VALUE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
[LOAD_ATTR_MODULE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
[LOAD_ATTR_WITH_HINT] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },