mirror of
https://github.com/python/cpython.git
synced 2024-11-23 18:04:37 +08:00
GH-98831: Typed stack effects, and more instructions converted (#99764)
Stack effects can now have a type, e.g. `inst(X, (left, right -- jump/uint64_t)) { ... }`. Instructions converted to the non-legacy format: * COMPARE_OP * COMPARE_OP_FLOAT_JUMP * COMPARE_OP_INT_JUMP * COMPARE_OP_STR_JUMP * STORE_ATTR * DELETE_ATTR * STORE_GLOBAL * STORE_ATTR_INSTANCE_VALUE * STORE_ATTR_WITH_HINT * STORE_ATTR_SLOT, and complete the store_attr family * Complete the store_subscr family: STORE_SUBSCR{,DICT,LIST_INT} (STORE_SUBSCR was alread half converted, but wasn't using cache effects yet.) * DELETE_SUBSCR * PRINT_EXPR * INTERPRETER_EXIT (a bit weird, ends in return) * RETURN_VALUE * GET_AITER (had to restructure it some) The original had mysterious `SET_TOP(NULL)` before `goto error`. I assume those just account for `obj` having been decref'ed, so I got rid of them in favor of the cleanup implied by `ERROR_IF()`. * LIST_APPEND (a bit unhappy with it) * SET_ADD (also a bit unhappy with it) Various other improvements/refactorings as well.
This commit is contained in:
parent
35cc0ea736
commit
c85be734d1
@ -81,8 +81,17 @@ do { \
|
||||
// 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 *list, *tuple, *dict;
|
||||
static PyObject *exit_func, *lasti, *val;
|
||||
static PyObject *list, *tuple, *dict, *owner;
|
||||
static PyObject *exit_func, *lasti, *val, *retval, *obj, *iter;
|
||||
static size_t jump;
|
||||
// Dummy variables for cache effects
|
||||
static _Py_CODEUNIT when_to_jump_mask, invert, counter, index, hint;
|
||||
static uint32_t type_version;
|
||||
// Dummy opcode names for 'op' opcodes
|
||||
#define _COMPARE_OP_FLOAT 1003
|
||||
#define _COMPARE_OP_INT 1004
|
||||
#define _COMPARE_OP_STR 1005
|
||||
#define _JUMP_IF 1006
|
||||
|
||||
static PyObject *
|
||||
dummy_func(
|
||||
@ -205,7 +214,7 @@ dummy_func(
|
||||
};
|
||||
|
||||
|
||||
inst(BINARY_OP_MULTIPLY_INT, (left, right, unused/1 -- prod)) {
|
||||
inst(BINARY_OP_MULTIPLY_INT, (unused/1, left, right -- prod)) {
|
||||
assert(cframe.use_tracing == 0);
|
||||
DEOPT_IF(!PyLong_CheckExact(left), BINARY_OP);
|
||||
DEOPT_IF(!PyLong_CheckExact(right), BINARY_OP);
|
||||
@ -216,7 +225,7 @@ dummy_func(
|
||||
ERROR_IF(prod == NULL, error);
|
||||
}
|
||||
|
||||
inst(BINARY_OP_MULTIPLY_FLOAT, (left, right, unused/1 -- prod)) {
|
||||
inst(BINARY_OP_MULTIPLY_FLOAT, (unused/1, left, right -- prod)) {
|
||||
assert(cframe.use_tracing == 0);
|
||||
DEOPT_IF(!PyFloat_CheckExact(left), BINARY_OP);
|
||||
DEOPT_IF(!PyFloat_CheckExact(right), BINARY_OP);
|
||||
@ -229,7 +238,7 @@ dummy_func(
|
||||
ERROR_IF(prod == NULL, error);
|
||||
}
|
||||
|
||||
inst(BINARY_OP_SUBTRACT_INT, (left, right, unused/1 -- sub)) {
|
||||
inst(BINARY_OP_SUBTRACT_INT, (unused/1, left, right -- sub)) {
|
||||
assert(cframe.use_tracing == 0);
|
||||
DEOPT_IF(!PyLong_CheckExact(left), BINARY_OP);
|
||||
DEOPT_IF(!PyLong_CheckExact(right), BINARY_OP);
|
||||
@ -240,7 +249,7 @@ dummy_func(
|
||||
ERROR_IF(sub == NULL, error);
|
||||
}
|
||||
|
||||
inst(BINARY_OP_SUBTRACT_FLOAT, (left, right, unused/1 -- sub)) {
|
||||
inst(BINARY_OP_SUBTRACT_FLOAT, (unused/1, left, right -- sub)) {
|
||||
assert(cframe.use_tracing == 0);
|
||||
DEOPT_IF(!PyFloat_CheckExact(left), BINARY_OP);
|
||||
DEOPT_IF(!PyFloat_CheckExact(right), BINARY_OP);
|
||||
@ -252,7 +261,7 @@ dummy_func(
|
||||
ERROR_IF(sub == NULL, error);
|
||||
}
|
||||
|
||||
inst(BINARY_OP_ADD_UNICODE, (left, right, unused/1 -- res)) {
|
||||
inst(BINARY_OP_ADD_UNICODE, (unused/1, left, right -- res)) {
|
||||
assert(cframe.use_tracing == 0);
|
||||
DEOPT_IF(!PyUnicode_CheckExact(left), BINARY_OP);
|
||||
DEOPT_IF(Py_TYPE(right) != Py_TYPE(left), BINARY_OP);
|
||||
@ -299,7 +308,7 @@ dummy_func(
|
||||
JUMPBY(INLINE_CACHE_ENTRIES_BINARY_OP + 1);
|
||||
}
|
||||
|
||||
inst(BINARY_OP_ADD_FLOAT, (left, right, unused/1 -- sum)) {
|
||||
inst(BINARY_OP_ADD_FLOAT, (unused/1, left, right -- sum)) {
|
||||
assert(cframe.use_tracing == 0);
|
||||
DEOPT_IF(!PyFloat_CheckExact(left), BINARY_OP);
|
||||
DEOPT_IF(Py_TYPE(right) != Py_TYPE(left), BINARY_OP);
|
||||
@ -312,7 +321,7 @@ dummy_func(
|
||||
ERROR_IF(sum == NULL, error);
|
||||
}
|
||||
|
||||
inst(BINARY_OP_ADD_INT, (left, right, unused/1 -- sum)) {
|
||||
inst(BINARY_OP_ADD_INT, (unused/1, left, right -- sum)) {
|
||||
assert(cframe.use_tracing == 0);
|
||||
DEOPT_IF(!PyLong_CheckExact(left), BINARY_OP);
|
||||
DEOPT_IF(Py_TYPE(right) != Py_TYPE(left), BINARY_OP);
|
||||
@ -331,7 +340,7 @@ dummy_func(
|
||||
BINARY_SUBSCR_TUPLE_INT,
|
||||
};
|
||||
|
||||
inst(BINARY_SUBSCR, (container, sub, unused/4 -- res)) {
|
||||
inst(BINARY_SUBSCR, (unused/4, container, sub -- res)) {
|
||||
_PyBinarySubscrCache *cache = (_PyBinarySubscrCache *)next_instr;
|
||||
if (ADAPTIVE_COUNTER_IS_ZERO(cache->counter)) {
|
||||
assert(cframe.use_tracing == 0);
|
||||
@ -377,7 +386,7 @@ dummy_func(
|
||||
ERROR_IF(err, error);
|
||||
}
|
||||
|
||||
inst(BINARY_SUBSCR_LIST_INT, (list, sub, unused/4 -- res)) {
|
||||
inst(BINARY_SUBSCR_LIST_INT, (unused/4, list, sub -- res)) {
|
||||
assert(cframe.use_tracing == 0);
|
||||
DEOPT_IF(!PyLong_CheckExact(sub), BINARY_SUBSCR);
|
||||
DEOPT_IF(!PyList_CheckExact(list), BINARY_SUBSCR);
|
||||
@ -396,7 +405,7 @@ dummy_func(
|
||||
Py_DECREF(list);
|
||||
}
|
||||
|
||||
inst(BINARY_SUBSCR_TUPLE_INT, (tuple, sub, unused/4 -- res)) {
|
||||
inst(BINARY_SUBSCR_TUPLE_INT, (unused/4, tuple, sub -- res)) {
|
||||
assert(cframe.use_tracing == 0);
|
||||
DEOPT_IF(!PyLong_CheckExact(sub), BINARY_SUBSCR);
|
||||
DEOPT_IF(!PyTuple_CheckExact(tuple), BINARY_SUBSCR);
|
||||
@ -415,7 +424,7 @@ dummy_func(
|
||||
Py_DECREF(tuple);
|
||||
}
|
||||
|
||||
inst(BINARY_SUBSCR_DICT, (dict, sub, unused/4 -- res)) {
|
||||
inst(BINARY_SUBSCR_DICT, (unused/4, dict, sub -- res)) {
|
||||
assert(cframe.use_tracing == 0);
|
||||
DEOPT_IF(!PyDict_CheckExact(dict), BINARY_SUBSCR);
|
||||
STAT_INC(BINARY_SUBSCR, hit);
|
||||
@ -426,14 +435,14 @@ dummy_func(
|
||||
}
|
||||
Py_DECREF(dict);
|
||||
Py_DECREF(sub);
|
||||
ERROR_IF(1, error);
|
||||
ERROR_IF(true, error);
|
||||
}
|
||||
Py_INCREF(res); // Do this before DECREF'ing dict, sub
|
||||
Py_DECREF(dict);
|
||||
Py_DECREF(sub);
|
||||
}
|
||||
|
||||
inst(BINARY_SUBSCR_GETITEM, (container, sub, unused/1, type_version/2, func_version/1 -- unused)) {
|
||||
inst(BINARY_SUBSCR_GETITEM, (unused/1, type_version/2, func_version/1, container, sub -- unused)) {
|
||||
PyTypeObject *tp = Py_TYPE(container);
|
||||
DEOPT_IF(tp->tp_version_tag != type_version, BINARY_SUBSCR);
|
||||
assert(tp->tp_flags & Py_TPFLAGS_HEAPTYPE);
|
||||
@ -457,52 +466,48 @@ dummy_func(
|
||||
DISPATCH_INLINED(new_frame);
|
||||
}
|
||||
|
||||
// stack effect: (__0 -- )
|
||||
inst(LIST_APPEND) {
|
||||
PyObject *v = POP();
|
||||
PyObject *list = PEEK(oparg);
|
||||
if (_PyList_AppendTakeRef((PyListObject *)list, v) < 0)
|
||||
goto error;
|
||||
// Alternative: (list, unused[oparg], v -- list, unused[oparg])
|
||||
inst(LIST_APPEND, (v --)) {
|
||||
PyObject *list = PEEK(oparg + 1); // +1 to account for v staying on stack
|
||||
ERROR_IF(_PyList_AppendTakeRef((PyListObject *)list, v) < 0, error);
|
||||
PREDICT(JUMP_BACKWARD);
|
||||
}
|
||||
|
||||
// stack effect: (__0 -- )
|
||||
inst(SET_ADD) {
|
||||
PyObject *v = POP();
|
||||
PyObject *set = PEEK(oparg);
|
||||
int err;
|
||||
err = PySet_Add(set, v);
|
||||
// Alternative: (set, unused[oparg], v -- set, unused[oparg])
|
||||
inst(SET_ADD, (v --)) {
|
||||
PyObject *set = PEEK(oparg + 1); // +1 to account for v staying on stack
|
||||
int err = PySet_Add(set, v);
|
||||
Py_DECREF(v);
|
||||
if (err != 0)
|
||||
goto error;
|
||||
ERROR_IF(err, error);
|
||||
PREDICT(JUMP_BACKWARD);
|
||||
}
|
||||
|
||||
inst(STORE_SUBSCR, (v, container, sub -- )) {
|
||||
_PyStoreSubscrCache *cache = (_PyStoreSubscrCache *)next_instr;
|
||||
if (ADAPTIVE_COUNTER_IS_ZERO(cache->counter)) {
|
||||
family(store_subscr) = {
|
||||
STORE_SUBSCR,
|
||||
STORE_SUBSCR_DICT,
|
||||
STORE_SUBSCR_LIST_INT,
|
||||
};
|
||||
|
||||
inst(STORE_SUBSCR, (counter/1, v, container, sub -- )) {
|
||||
if (ADAPTIVE_COUNTER_IS_ZERO(counter)) {
|
||||
assert(cframe.use_tracing == 0);
|
||||
next_instr--;
|
||||
_Py_Specialize_StoreSubscr(container, sub, next_instr);
|
||||
DISPATCH_SAME_OPARG();
|
||||
}
|
||||
STAT_INC(STORE_SUBSCR, deferred);
|
||||
_PyStoreSubscrCache *cache = (_PyStoreSubscrCache *)next_instr;
|
||||
DECREMENT_ADAPTIVE_COUNTER(cache->counter);
|
||||
/* container[sub] = v */
|
||||
int err = PyObject_SetItem(container, sub, v);
|
||||
Py_DECREF(v);
|
||||
Py_DECREF(container);
|
||||
Py_DECREF(sub);
|
||||
ERROR_IF(err != 0, error);
|
||||
JUMPBY(INLINE_CACHE_ENTRIES_STORE_SUBSCR);
|
||||
ERROR_IF(err, error);
|
||||
}
|
||||
|
||||
// stack effect: (__0, __1, __2 -- )
|
||||
inst(STORE_SUBSCR_LIST_INT) {
|
||||
inst(STORE_SUBSCR_LIST_INT, (unused/1, value, list, sub -- )) {
|
||||
assert(cframe.use_tracing == 0);
|
||||
PyObject *sub = TOP();
|
||||
PyObject *list = SECOND();
|
||||
PyObject *value = THIRD();
|
||||
DEOPT_IF(!PyLong_CheckExact(sub), STORE_SUBSCR);
|
||||
DEOPT_IF(!PyList_CheckExact(list), STORE_SUBSCR);
|
||||
|
||||
@ -515,60 +520,42 @@ dummy_func(
|
||||
|
||||
PyObject *old_value = PyList_GET_ITEM(list, index);
|
||||
PyList_SET_ITEM(list, index, value);
|
||||
STACK_SHRINK(3);
|
||||
assert(old_value != NULL);
|
||||
Py_DECREF(old_value);
|
||||
_Py_DECREF_SPECIALIZED(sub, (destructor)PyObject_Free);
|
||||
Py_DECREF(list);
|
||||
JUMPBY(INLINE_CACHE_ENTRIES_STORE_SUBSCR);
|
||||
}
|
||||
|
||||
// stack effect: (__0, __1, __2 -- )
|
||||
inst(STORE_SUBSCR_DICT) {
|
||||
inst(STORE_SUBSCR_DICT, (unused/1, value, dict, sub -- )) {
|
||||
assert(cframe.use_tracing == 0);
|
||||
PyObject *sub = TOP();
|
||||
PyObject *dict = SECOND();
|
||||
PyObject *value = THIRD();
|
||||
DEOPT_IF(!PyDict_CheckExact(dict), STORE_SUBSCR);
|
||||
STACK_SHRINK(3);
|
||||
STAT_INC(STORE_SUBSCR, hit);
|
||||
int err = _PyDict_SetItem_Take2((PyDictObject *)dict, sub, value);
|
||||
Py_DECREF(dict);
|
||||
if (err != 0) {
|
||||
goto error;
|
||||
}
|
||||
JUMPBY(INLINE_CACHE_ENTRIES_STORE_SUBSCR);
|
||||
ERROR_IF(err, error);
|
||||
}
|
||||
|
||||
// stack effect: (__0, __1 -- )
|
||||
inst(DELETE_SUBSCR) {
|
||||
PyObject *sub = TOP();
|
||||
PyObject *container = SECOND();
|
||||
int err;
|
||||
STACK_SHRINK(2);
|
||||
inst(DELETE_SUBSCR, (container, sub --)) {
|
||||
/* del container[sub] */
|
||||
err = PyObject_DelItem(container, sub);
|
||||
int err = PyObject_DelItem(container, sub);
|
||||
Py_DECREF(container);
|
||||
Py_DECREF(sub);
|
||||
if (err != 0)
|
||||
goto error;
|
||||
ERROR_IF(err, error);
|
||||
}
|
||||
|
||||
// stack effect: (__0 -- )
|
||||
inst(PRINT_EXPR) {
|
||||
PyObject *value = POP();
|
||||
inst(PRINT_EXPR, (value --)) {
|
||||
PyObject *hook = _PySys_GetAttr(tstate, &_Py_ID(displayhook));
|
||||
PyObject *res;
|
||||
// Can't use ERROR_IF here.
|
||||
if (hook == NULL) {
|
||||
_PyErr_SetString(tstate, PyExc_RuntimeError,
|
||||
"lost sys.displayhook");
|
||||
Py_DECREF(value);
|
||||
goto error;
|
||||
ERROR_IF(true, error);
|
||||
}
|
||||
res = PyObject_CallOneArg(hook, value);
|
||||
Py_DECREF(value);
|
||||
if (res == NULL)
|
||||
goto error;
|
||||
ERROR_IF(res == NULL, error);
|
||||
Py_DECREF(res);
|
||||
}
|
||||
|
||||
@ -595,11 +582,10 @@ dummy_func(
|
||||
goto error;
|
||||
}
|
||||
|
||||
// stack effect: (__0 -- )
|
||||
inst(INTERPRETER_EXIT) {
|
||||
inst(INTERPRETER_EXIT, (retval --)) {
|
||||
assert(frame == &entry_frame);
|
||||
assert(_PyFrame_IsIncomplete(frame));
|
||||
PyObject *retval = POP();
|
||||
STACK_SHRINK(1); // Since we're not going to DISPATCH()
|
||||
assert(EMPTY());
|
||||
/* Restore previous cframe and return. */
|
||||
tstate->cframe = cframe.previous;
|
||||
@ -610,9 +596,8 @@ dummy_func(
|
||||
return retval;
|
||||
}
|
||||
|
||||
// stack effect: (__0 -- )
|
||||
inst(RETURN_VALUE) {
|
||||
PyObject *retval = POP();
|
||||
inst(RETURN_VALUE, (retval --)) {
|
||||
STACK_SHRINK(1);
|
||||
assert(EMPTY());
|
||||
_PyFrame_SetStackPointer(frame, stack_pointer);
|
||||
TRACE_FUNCTION_EXIT();
|
||||
@ -627,48 +612,37 @@ dummy_func(
|
||||
goto resume_frame;
|
||||
}
|
||||
|
||||
// stack effect: ( -- )
|
||||
inst(GET_AITER) {
|
||||
inst(GET_AITER, (obj -- iter)) {
|
||||
unaryfunc getter = NULL;
|
||||
PyObject *iter = NULL;
|
||||
PyObject *obj = TOP();
|
||||
PyTypeObject *type = Py_TYPE(obj);
|
||||
|
||||
if (type->tp_as_async != NULL) {
|
||||
getter = type->tp_as_async->am_aiter;
|
||||
}
|
||||
|
||||
if (getter != NULL) {
|
||||
iter = (*getter)(obj);
|
||||
Py_DECREF(obj);
|
||||
if (iter == NULL) {
|
||||
SET_TOP(NULL);
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
else {
|
||||
SET_TOP(NULL);
|
||||
if (getter == NULL) {
|
||||
_PyErr_Format(tstate, PyExc_TypeError,
|
||||
"'async for' requires an object with "
|
||||
"__aiter__ method, got %.100s",
|
||||
type->tp_name);
|
||||
Py_DECREF(obj);
|
||||
goto error;
|
||||
ERROR_IF(true, error);
|
||||
}
|
||||
|
||||
iter = (*getter)(obj);
|
||||
Py_DECREF(obj);
|
||||
ERROR_IF(iter == NULL, error);
|
||||
|
||||
if (Py_TYPE(iter)->tp_as_async == NULL ||
|
||||
Py_TYPE(iter)->tp_as_async->am_anext == NULL) {
|
||||
|
||||
SET_TOP(NULL);
|
||||
_PyErr_Format(tstate, PyExc_TypeError,
|
||||
"'async for' received an object from __aiter__ "
|
||||
"that does not implement __anext__: %.100s",
|
||||
Py_TYPE(iter)->tp_name);
|
||||
Py_DECREF(iter);
|
||||
goto error;
|
||||
ERROR_IF(true, error);
|
||||
}
|
||||
|
||||
SET_TOP(iter);
|
||||
}
|
||||
|
||||
// stack effect: ( -- __0)
|
||||
@ -1119,53 +1093,43 @@ dummy_func(
|
||||
Py_DECREF(seq);
|
||||
}
|
||||
|
||||
// stack effect: (__0, __1 -- )
|
||||
inst(STORE_ATTR) {
|
||||
_PyAttrCache *cache = (_PyAttrCache *)next_instr;
|
||||
if (ADAPTIVE_COUNTER_IS_ZERO(cache->counter)) {
|
||||
family(store_attr) = {
|
||||
STORE_ATTR,
|
||||
STORE_ATTR_INSTANCE_VALUE,
|
||||
STORE_ATTR_SLOT,
|
||||
STORE_ATTR_WITH_HINT,
|
||||
};
|
||||
|
||||
inst(STORE_ATTR, (counter/1, unused/3, v, owner --)) {
|
||||
if (ADAPTIVE_COUNTER_IS_ZERO(counter)) {
|
||||
assert(cframe.use_tracing == 0);
|
||||
PyObject *owner = TOP();
|
||||
PyObject *name = GETITEM(names, oparg);
|
||||
next_instr--;
|
||||
_Py_Specialize_StoreAttr(owner, next_instr, name);
|
||||
DISPATCH_SAME_OPARG();
|
||||
}
|
||||
STAT_INC(STORE_ATTR, deferred);
|
||||
_PyAttrCache *cache = (_PyAttrCache *)next_instr;
|
||||
DECREMENT_ADAPTIVE_COUNTER(cache->counter);
|
||||
PyObject *name = GETITEM(names, oparg);
|
||||
PyObject *owner = TOP();
|
||||
PyObject *v = SECOND();
|
||||
int err;
|
||||
STACK_SHRINK(2);
|
||||
err = PyObject_SetAttr(owner, name, v);
|
||||
int err = PyObject_SetAttr(owner, name, v);
|
||||
Py_DECREF(v);
|
||||
Py_DECREF(owner);
|
||||
if (err != 0) {
|
||||
goto error;
|
||||
}
|
||||
JUMPBY(INLINE_CACHE_ENTRIES_STORE_ATTR);
|
||||
ERROR_IF(err, error);
|
||||
}
|
||||
|
||||
// stack effect: (__0 -- )
|
||||
inst(DELETE_ATTR) {
|
||||
inst(DELETE_ATTR, (owner --)) {
|
||||
PyObject *name = GETITEM(names, oparg);
|
||||
PyObject *owner = POP();
|
||||
int err;
|
||||
err = PyObject_SetAttr(owner, name, (PyObject *)NULL);
|
||||
int err = PyObject_SetAttr(owner, name, (PyObject *)NULL);
|
||||
Py_DECREF(owner);
|
||||
if (err != 0)
|
||||
goto error;
|
||||
ERROR_IF(err, error);
|
||||
}
|
||||
|
||||
// stack effect: (__0 -- )
|
||||
inst(STORE_GLOBAL) {
|
||||
inst(STORE_GLOBAL, (v --)) {
|
||||
PyObject *name = GETITEM(names, oparg);
|
||||
PyObject *v = POP();
|
||||
int err;
|
||||
err = PyDict_SetItem(GLOBALS(), name, v);
|
||||
int err = PyDict_SetItem(GLOBALS(), name, v);
|
||||
Py_DECREF(v);
|
||||
if (err != 0)
|
||||
goto error;
|
||||
ERROR_IF(err, error);
|
||||
}
|
||||
|
||||
inst(DELETE_GLOBAL, (--)) {
|
||||
@ -1954,22 +1918,15 @@ dummy_func(
|
||||
DISPATCH_INLINED(new_frame);
|
||||
}
|
||||
|
||||
// stack effect: (__0, __1 -- )
|
||||
inst(STORE_ATTR_INSTANCE_VALUE) {
|
||||
inst(STORE_ATTR_INSTANCE_VALUE, (unused/1, type_version/2, index/1, value, owner --)) {
|
||||
assert(cframe.use_tracing == 0);
|
||||
PyObject *owner = TOP();
|
||||
PyTypeObject *tp = Py_TYPE(owner);
|
||||
_PyAttrCache *cache = (_PyAttrCache *)next_instr;
|
||||
uint32_t type_version = read_u32(cache->version);
|
||||
assert(type_version != 0);
|
||||
DEOPT_IF(tp->tp_version_tag != type_version, STORE_ATTR);
|
||||
assert(tp->tp_flags & Py_TPFLAGS_MANAGED_DICT);
|
||||
PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner);
|
||||
DEOPT_IF(!_PyDictOrValues_IsValues(dorv), STORE_ATTR);
|
||||
STAT_INC(STORE_ATTR, hit);
|
||||
Py_ssize_t index = cache->index;
|
||||
STACK_SHRINK(1);
|
||||
PyObject *value = POP();
|
||||
PyDictValues *values = _PyDictOrValues_GetValues(dorv);
|
||||
PyObject *old_value = values->values[index];
|
||||
values->values[index] = value;
|
||||
@ -1980,16 +1937,11 @@ dummy_func(
|
||||
Py_DECREF(old_value);
|
||||
}
|
||||
Py_DECREF(owner);
|
||||
JUMPBY(INLINE_CACHE_ENTRIES_STORE_ATTR);
|
||||
}
|
||||
|
||||
// stack effect: (__0, __1 -- )
|
||||
inst(STORE_ATTR_WITH_HINT) {
|
||||
inst(STORE_ATTR_WITH_HINT, (unused/1, type_version/2, hint/1, value, owner --)) {
|
||||
assert(cframe.use_tracing == 0);
|
||||
PyObject *owner = TOP();
|
||||
PyTypeObject *tp = Py_TYPE(owner);
|
||||
_PyAttrCache *cache = (_PyAttrCache *)next_instr;
|
||||
uint32_t type_version = read_u32(cache->version);
|
||||
assert(type_version != 0);
|
||||
DEOPT_IF(tp->tp_version_tag != type_version, STORE_ATTR);
|
||||
assert(tp->tp_flags & Py_TPFLAGS_MANAGED_DICT);
|
||||
@ -1999,17 +1951,14 @@ dummy_func(
|
||||
DEOPT_IF(dict == NULL, STORE_ATTR);
|
||||
assert(PyDict_CheckExact((PyObject *)dict));
|
||||
PyObject *name = GETITEM(names, oparg);
|
||||
uint16_t hint = cache->index;
|
||||
DEOPT_IF(hint >= (size_t)dict->ma_keys->dk_nentries, STORE_ATTR);
|
||||
PyObject *value, *old_value;
|
||||
PyObject *old_value;
|
||||
uint64_t new_version;
|
||||
if (DK_IS_UNICODE(dict->ma_keys)) {
|
||||
PyDictUnicodeEntry *ep = DK_UNICODE_ENTRIES(dict->ma_keys) + hint;
|
||||
DEOPT_IF(ep->me_key != name, STORE_ATTR);
|
||||
old_value = ep->me_value;
|
||||
DEOPT_IF(old_value == NULL, STORE_ATTR);
|
||||
STACK_SHRINK(1);
|
||||
value = POP();
|
||||
new_version = _PyDict_NotifyEvent(PyDict_EVENT_MODIFIED, dict, name, value);
|
||||
ep->me_value = value;
|
||||
}
|
||||
@ -2018,8 +1967,6 @@ dummy_func(
|
||||
DEOPT_IF(ep->me_key != name, STORE_ATTR);
|
||||
old_value = ep->me_value;
|
||||
DEOPT_IF(old_value == NULL, STORE_ATTR);
|
||||
STACK_SHRINK(1);
|
||||
value = POP();
|
||||
new_version = _PyDict_NotifyEvent(PyDict_EVENT_MODIFIED, dict, name, value);
|
||||
ep->me_value = value;
|
||||
}
|
||||
@ -2032,36 +1979,32 @@ dummy_func(
|
||||
/* PEP 509 */
|
||||
dict->ma_version_tag = new_version;
|
||||
Py_DECREF(owner);
|
||||
JUMPBY(INLINE_CACHE_ENTRIES_STORE_ATTR);
|
||||
}
|
||||
|
||||
// stack effect: (__0, __1 -- )
|
||||
inst(STORE_ATTR_SLOT) {
|
||||
inst(STORE_ATTR_SLOT, (unused/1, type_version/2, index/1, value, owner --)) {
|
||||
assert(cframe.use_tracing == 0);
|
||||
PyObject *owner = TOP();
|
||||
PyTypeObject *tp = Py_TYPE(owner);
|
||||
_PyAttrCache *cache = (_PyAttrCache *)next_instr;
|
||||
uint32_t type_version = read_u32(cache->version);
|
||||
assert(type_version != 0);
|
||||
DEOPT_IF(tp->tp_version_tag != type_version, STORE_ATTR);
|
||||
char *addr = (char *)owner + cache->index;
|
||||
char *addr = (char *)owner + index;
|
||||
STAT_INC(STORE_ATTR, hit);
|
||||
STACK_SHRINK(1);
|
||||
PyObject *value = POP();
|
||||
PyObject *old_value = *(PyObject **)addr;
|
||||
*(PyObject **)addr = value;
|
||||
Py_XDECREF(old_value);
|
||||
Py_DECREF(owner);
|
||||
JUMPBY(INLINE_CACHE_ENTRIES_STORE_ATTR);
|
||||
}
|
||||
|
||||
// stack effect: (__0 -- )
|
||||
inst(COMPARE_OP) {
|
||||
family(compare_op) = {
|
||||
COMPARE_OP,
|
||||
_COMPARE_OP_FLOAT,
|
||||
_COMPARE_OP_INT,
|
||||
_COMPARE_OP_STR,
|
||||
};
|
||||
|
||||
inst(COMPARE_OP, (unused/2, left, right -- res)) {
|
||||
_PyCompareOpCache *cache = (_PyCompareOpCache *)next_instr;
|
||||
if (ADAPTIVE_COUNTER_IS_ZERO(cache->counter)) {
|
||||
assert(cframe.use_tracing == 0);
|
||||
PyObject *right = TOP();
|
||||
PyObject *left = SECOND();
|
||||
next_instr--;
|
||||
_Py_Specialize_CompareOp(left, right, next_instr, oparg);
|
||||
DISPATCH_SAME_OPARG();
|
||||
@ -2069,57 +2012,43 @@ dummy_func(
|
||||
STAT_INC(COMPARE_OP, deferred);
|
||||
DECREMENT_ADAPTIVE_COUNTER(cache->counter);
|
||||
assert(oparg <= Py_GE);
|
||||
PyObject *right = POP();
|
||||
PyObject *left = TOP();
|
||||
PyObject *res = PyObject_RichCompare(left, right, oparg);
|
||||
SET_TOP(res);
|
||||
res = PyObject_RichCompare(left, right, oparg);
|
||||
Py_DECREF(left);
|
||||
Py_DECREF(right);
|
||||
if (res == NULL) {
|
||||
goto error;
|
||||
}
|
||||
JUMPBY(INLINE_CACHE_ENTRIES_COMPARE_OP);
|
||||
ERROR_IF(res == NULL, error);
|
||||
}
|
||||
|
||||
// stack effect: (__0 -- )
|
||||
inst(COMPARE_OP_FLOAT_JUMP) {
|
||||
// The result is an int disguised as an object pointer.
|
||||
op(_COMPARE_OP_FLOAT, (unused/1, when_to_jump_mask/1, left, right -- jump: size_t)) {
|
||||
assert(cframe.use_tracing == 0);
|
||||
// Combined: COMPARE_OP (float ? float) + POP_JUMP_IF_(true/false)
|
||||
_PyCompareOpCache *cache = (_PyCompareOpCache *)next_instr;
|
||||
int when_to_jump_mask = cache->mask;
|
||||
PyObject *right = TOP();
|
||||
PyObject *left = SECOND();
|
||||
DEOPT_IF(!PyFloat_CheckExact(left), COMPARE_OP);
|
||||
DEOPT_IF(!PyFloat_CheckExact(right), COMPARE_OP);
|
||||
double dleft = PyFloat_AS_DOUBLE(left);
|
||||
double dright = PyFloat_AS_DOUBLE(right);
|
||||
int sign = (dleft > dright) - (dleft < dright);
|
||||
// 1 if <, 2 if ==, 4 if >; this matches when _to_jump_mask
|
||||
int sign_ish = 2*(dleft > dright) + 2 - (dleft < dright);
|
||||
DEOPT_IF(isnan(dleft), COMPARE_OP);
|
||||
DEOPT_IF(isnan(dright), COMPARE_OP);
|
||||
STAT_INC(COMPARE_OP, hit);
|
||||
JUMPBY(INLINE_CACHE_ENTRIES_COMPARE_OP);
|
||||
NEXTOPARG();
|
||||
STACK_SHRINK(2);
|
||||
_Py_DECREF_SPECIALIZED(left, _PyFloat_ExactDealloc);
|
||||
_Py_DECREF_SPECIALIZED(right, _PyFloat_ExactDealloc);
|
||||
jump = sign_ish & when_to_jump_mask;
|
||||
}
|
||||
// The input is an int disguised as an object pointer!
|
||||
op(_JUMP_IF, (jump: size_t --)) {
|
||||
assert(opcode == POP_JUMP_IF_FALSE || opcode == POP_JUMP_IF_TRUE);
|
||||
int jump = (1 << (sign + 1)) & when_to_jump_mask;
|
||||
if (!jump) {
|
||||
next_instr++;
|
||||
}
|
||||
else {
|
||||
JUMPBY(1 + oparg);
|
||||
if (jump) {
|
||||
JUMPBY(oparg);
|
||||
}
|
||||
}
|
||||
// We're praying that the compiler optimizes the flags manipuations.
|
||||
super(COMPARE_OP_FLOAT_JUMP) = _COMPARE_OP_FLOAT + _JUMP_IF;
|
||||
|
||||
// stack effect: (__0 -- )
|
||||
inst(COMPARE_OP_INT_JUMP) {
|
||||
// Similar to COMPARE_OP_FLOAT
|
||||
op(_COMPARE_OP_INT, (unused/1, when_to_jump_mask/1, left, right -- jump: size_t)) {
|
||||
assert(cframe.use_tracing == 0);
|
||||
// Combined: COMPARE_OP (int ? int) + POP_JUMP_IF_(true/false)
|
||||
_PyCompareOpCache *cache = (_PyCompareOpCache *)next_instr;
|
||||
int when_to_jump_mask = cache->mask;
|
||||
PyObject *right = TOP();
|
||||
PyObject *left = SECOND();
|
||||
DEOPT_IF(!PyLong_CheckExact(left), COMPARE_OP);
|
||||
DEOPT_IF(!PyLong_CheckExact(right), COMPARE_OP);
|
||||
DEOPT_IF((size_t)(Py_SIZE(left) + 1) > 2, COMPARE_OP);
|
||||
@ -2128,51 +2057,30 @@ dummy_func(
|
||||
assert(Py_ABS(Py_SIZE(left)) <= 1 && Py_ABS(Py_SIZE(right)) <= 1);
|
||||
Py_ssize_t ileft = Py_SIZE(left) * ((PyLongObject *)left)->ob_digit[0];
|
||||
Py_ssize_t iright = Py_SIZE(right) * ((PyLongObject *)right)->ob_digit[0];
|
||||
int sign = (ileft > iright) - (ileft < iright);
|
||||
JUMPBY(INLINE_CACHE_ENTRIES_COMPARE_OP);
|
||||
NEXTOPARG();
|
||||
STACK_SHRINK(2);
|
||||
// 1 if <, 2 if ==, 4 if >; this matches when _to_jump_mask
|
||||
int sign_ish = 2*(ileft > iright) + 2 - (ileft < iright);
|
||||
_Py_DECREF_SPECIALIZED(left, (destructor)PyObject_Free);
|
||||
_Py_DECREF_SPECIALIZED(right, (destructor)PyObject_Free);
|
||||
assert(opcode == POP_JUMP_IF_FALSE || opcode == POP_JUMP_IF_TRUE);
|
||||
int jump = (1 << (sign + 1)) & when_to_jump_mask;
|
||||
if (!jump) {
|
||||
next_instr++;
|
||||
}
|
||||
else {
|
||||
JUMPBY(1 + oparg);
|
||||
}
|
||||
jump = sign_ish & when_to_jump_mask;
|
||||
}
|
||||
super(COMPARE_OP_INT_JUMP) = _COMPARE_OP_INT + _JUMP_IF;
|
||||
|
||||
// stack effect: (__0 -- )
|
||||
inst(COMPARE_OP_STR_JUMP) {
|
||||
// Similar to COMPARE_OP_FLOAT, but for ==, != only
|
||||
op(_COMPARE_OP_STR, (unused/1, invert/1, left, right -- jump: size_t)) {
|
||||
assert(cframe.use_tracing == 0);
|
||||
// Combined: COMPARE_OP (str == str or str != str) + POP_JUMP_IF_(true/false)
|
||||
_PyCompareOpCache *cache = (_PyCompareOpCache *)next_instr;
|
||||
int invert = cache->mask;
|
||||
PyObject *right = TOP();
|
||||
PyObject *left = SECOND();
|
||||
DEOPT_IF(!PyUnicode_CheckExact(left), COMPARE_OP);
|
||||
DEOPT_IF(!PyUnicode_CheckExact(right), COMPARE_OP);
|
||||
STAT_INC(COMPARE_OP, hit);
|
||||
int res = _PyUnicode_Equal(left, right);
|
||||
assert(oparg == Py_EQ || oparg == Py_NE);
|
||||
JUMPBY(INLINE_CACHE_ENTRIES_COMPARE_OP);
|
||||
NEXTOPARG();
|
||||
assert(opcode == POP_JUMP_IF_FALSE || opcode == POP_JUMP_IF_TRUE);
|
||||
STACK_SHRINK(2);
|
||||
_Py_DECREF_SPECIALIZED(left, _PyUnicode_ExactDealloc);
|
||||
_Py_DECREF_SPECIALIZED(right, _PyUnicode_ExactDealloc);
|
||||
assert(res == 0 || res == 1);
|
||||
assert(invert == 0 || invert == 1);
|
||||
int jump = res ^ invert;
|
||||
if (!jump) {
|
||||
next_instr++;
|
||||
}
|
||||
else {
|
||||
JUMPBY(1 + oparg);
|
||||
}
|
||||
jump = res ^ invert;
|
||||
}
|
||||
super(COMPARE_OP_STR_JUMP) = _COMPARE_OP_STR + _JUMP_IF;
|
||||
|
||||
// stack effect: (__0 -- )
|
||||
inst(IS_OP) {
|
||||
@ -3633,7 +3541,7 @@ dummy_func(
|
||||
PUSH(Py_NewRef(peek));
|
||||
}
|
||||
|
||||
inst(BINARY_OP, (lhs, rhs, unused/1 -- res)) {
|
||||
inst(BINARY_OP, (unused/1, lhs, rhs -- res)) {
|
||||
_PyBinaryOpCache *cache = (_PyBinaryOpCache *)next_instr;
|
||||
if (ADAPTIVE_COUNTER_IS_ZERO(cache->counter)) {
|
||||
assert(cframe.use_tracing == 0);
|
||||
@ -3691,9 +3599,6 @@ dummy_func(
|
||||
|
||||
// Future families go below this point //
|
||||
|
||||
family(binary_subscr) = {
|
||||
BINARY_SUBSCR, BINARY_SUBSCR_DICT,
|
||||
BINARY_SUBSCR_GETITEM, BINARY_SUBSCR_LIST_INT, BINARY_SUBSCR_TUPLE_INT };
|
||||
family(call) = {
|
||||
CALL, CALL_PY_EXACT_ARGS,
|
||||
CALL_PY_WITH_DEFAULTS, CALL_BOUND_METHOD_EXACT_ARGS, CALL_BUILTIN_CLASS,
|
||||
@ -3702,9 +3607,6 @@ family(call) = {
|
||||
CALL_NO_KW_LIST_APPEND, CALL_NO_KW_METHOD_DESCRIPTOR_FAST, CALL_NO_KW_METHOD_DESCRIPTOR_NOARGS,
|
||||
CALL_NO_KW_METHOD_DESCRIPTOR_O, CALL_NO_KW_STR_1, CALL_NO_KW_TUPLE_1,
|
||||
CALL_NO_KW_TYPE_1 };
|
||||
family(compare_op) = {
|
||||
COMPARE_OP, COMPARE_OP_FLOAT_JUMP,
|
||||
COMPARE_OP_INT_JUMP, COMPARE_OP_STR_JUMP };
|
||||
family(for_iter) = {
|
||||
FOR_ITER, FOR_ITER_LIST,
|
||||
FOR_ITER_RANGE };
|
||||
@ -3719,13 +3621,7 @@ family(load_fast) = { LOAD_FAST, LOAD_FAST__LOAD_CONST, LOAD_FAST__LOAD_FAST };
|
||||
family(load_global) = {
|
||||
LOAD_GLOBAL, LOAD_GLOBAL_BUILTIN,
|
||||
LOAD_GLOBAL_MODULE };
|
||||
family(store_attr) = {
|
||||
STORE_ATTR, STORE_ATTR_INSTANCE_VALUE,
|
||||
STORE_ATTR_SLOT, STORE_ATTR_WITH_HINT };
|
||||
family(store_fast) = { STORE_FAST, STORE_FAST__LOAD_FAST, STORE_FAST__STORE_FAST };
|
||||
family(store_subscr) = {
|
||||
STORE_SUBSCR, STORE_SUBSCR_DICT,
|
||||
STORE_SUBSCR_LIST_INT };
|
||||
family(unpack_sequence) = {
|
||||
UNPACK_SEQUENCE, UNPACK_SEQUENCE_LIST,
|
||||
UNPACK_SEQUENCE_TUPLE, UNPACK_SEQUENCE_TWO_TUPLE };
|
||||
|
489
Python/generated_cases.c.h
generated
489
Python/generated_cases.c.h
generated
@ -139,7 +139,7 @@
|
||||
if (prod == NULL) goto pop_2_error;
|
||||
STACK_SHRINK(1);
|
||||
POKE(1, prod);
|
||||
next_instr += 1;
|
||||
JUMPBY(1);
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
@ -159,7 +159,7 @@
|
||||
if (prod == NULL) goto pop_2_error;
|
||||
STACK_SHRINK(1);
|
||||
POKE(1, prod);
|
||||
next_instr += 1;
|
||||
JUMPBY(1);
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
@ -177,7 +177,7 @@
|
||||
if (sub == NULL) goto pop_2_error;
|
||||
STACK_SHRINK(1);
|
||||
POKE(1, sub);
|
||||
next_instr += 1;
|
||||
JUMPBY(1);
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
@ -196,7 +196,7 @@
|
||||
if (sub == NULL) goto pop_2_error;
|
||||
STACK_SHRINK(1);
|
||||
POKE(1, sub);
|
||||
next_instr += 1;
|
||||
JUMPBY(1);
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
@ -214,7 +214,7 @@
|
||||
if (res == NULL) goto pop_2_error;
|
||||
STACK_SHRINK(1);
|
||||
POKE(1, res);
|
||||
next_instr += 1;
|
||||
JUMPBY(1);
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
@ -268,7 +268,7 @@
|
||||
if (sum == NULL) goto pop_2_error;
|
||||
STACK_SHRINK(1);
|
||||
POKE(1, sum);
|
||||
next_instr += 1;
|
||||
JUMPBY(1);
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
@ -286,7 +286,7 @@
|
||||
if (sum == NULL) goto pop_2_error;
|
||||
STACK_SHRINK(1);
|
||||
POKE(1, sum);
|
||||
next_instr += 1;
|
||||
JUMPBY(1);
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
@ -311,7 +311,7 @@
|
||||
if (res == NULL) goto pop_2_error;
|
||||
STACK_SHRINK(1);
|
||||
POKE(1, res);
|
||||
next_instr += 4;
|
||||
JUMPBY(4);
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
@ -380,7 +380,7 @@
|
||||
Py_DECREF(list);
|
||||
STACK_SHRINK(1);
|
||||
POKE(1, res);
|
||||
next_instr += 4;
|
||||
JUMPBY(4);
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
@ -406,7 +406,7 @@
|
||||
Py_DECREF(tuple);
|
||||
STACK_SHRINK(1);
|
||||
POKE(1, res);
|
||||
next_instr += 4;
|
||||
JUMPBY(4);
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
@ -424,14 +424,14 @@
|
||||
}
|
||||
Py_DECREF(dict);
|
||||
Py_DECREF(sub);
|
||||
if (1) goto pop_2_error;
|
||||
if (true) goto pop_2_error;
|
||||
}
|
||||
Py_INCREF(res); // Do this before DECREF'ing dict, sub
|
||||
Py_DECREF(dict);
|
||||
Py_DECREF(sub);
|
||||
STACK_SHRINK(1);
|
||||
POKE(1, res);
|
||||
next_instr += 4;
|
||||
JUMPBY(4);
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
@ -464,22 +464,21 @@
|
||||
}
|
||||
|
||||
TARGET(LIST_APPEND) {
|
||||
PyObject *v = POP();
|
||||
PyObject *list = PEEK(oparg);
|
||||
if (_PyList_AppendTakeRef((PyListObject *)list, v) < 0)
|
||||
goto error;
|
||||
PyObject *v = PEEK(1);
|
||||
PyObject *list = PEEK(oparg + 1); // +1 to account for v staying on stack
|
||||
if (_PyList_AppendTakeRef((PyListObject *)list, v) < 0) goto pop_1_error;
|
||||
STACK_SHRINK(1);
|
||||
PREDICT(JUMP_BACKWARD);
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
TARGET(SET_ADD) {
|
||||
PyObject *v = POP();
|
||||
PyObject *set = PEEK(oparg);
|
||||
int err;
|
||||
err = PySet_Add(set, v);
|
||||
PyObject *v = PEEK(1);
|
||||
PyObject *set = PEEK(oparg + 1); // +1 to account for v staying on stack
|
||||
int err = PySet_Add(set, v);
|
||||
Py_DECREF(v);
|
||||
if (err != 0)
|
||||
goto error;
|
||||
if (err) goto pop_1_error;
|
||||
STACK_SHRINK(1);
|
||||
PREDICT(JUMP_BACKWARD);
|
||||
DISPATCH();
|
||||
}
|
||||
@ -489,31 +488,32 @@
|
||||
PyObject *sub = PEEK(1);
|
||||
PyObject *container = PEEK(2);
|
||||
PyObject *v = PEEK(3);
|
||||
_PyStoreSubscrCache *cache = (_PyStoreSubscrCache *)next_instr;
|
||||
if (ADAPTIVE_COUNTER_IS_ZERO(cache->counter)) {
|
||||
uint16_t counter = read_u16(next_instr + 0);
|
||||
if (ADAPTIVE_COUNTER_IS_ZERO(counter)) {
|
||||
assert(cframe.use_tracing == 0);
|
||||
next_instr--;
|
||||
_Py_Specialize_StoreSubscr(container, sub, next_instr);
|
||||
DISPATCH_SAME_OPARG();
|
||||
}
|
||||
STAT_INC(STORE_SUBSCR, deferred);
|
||||
_PyStoreSubscrCache *cache = (_PyStoreSubscrCache *)next_instr;
|
||||
DECREMENT_ADAPTIVE_COUNTER(cache->counter);
|
||||
/* container[sub] = v */
|
||||
int err = PyObject_SetItem(container, sub, v);
|
||||
Py_DECREF(v);
|
||||
Py_DECREF(container);
|
||||
Py_DECREF(sub);
|
||||
if (err != 0) goto pop_3_error;
|
||||
JUMPBY(INLINE_CACHE_ENTRIES_STORE_SUBSCR);
|
||||
if (err) goto pop_3_error;
|
||||
STACK_SHRINK(3);
|
||||
JUMPBY(1);
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
TARGET(STORE_SUBSCR_LIST_INT) {
|
||||
PyObject *sub = PEEK(1);
|
||||
PyObject *list = PEEK(2);
|
||||
PyObject *value = PEEK(3);
|
||||
assert(cframe.use_tracing == 0);
|
||||
PyObject *sub = TOP();
|
||||
PyObject *list = SECOND();
|
||||
PyObject *value = THIRD();
|
||||
DEOPT_IF(!PyLong_CheckExact(sub), STORE_SUBSCR);
|
||||
DEOPT_IF(!PyList_CheckExact(list), STORE_SUBSCR);
|
||||
|
||||
@ -526,61 +526,58 @@
|
||||
|
||||
PyObject *old_value = PyList_GET_ITEM(list, index);
|
||||
PyList_SET_ITEM(list, index, value);
|
||||
STACK_SHRINK(3);
|
||||
assert(old_value != NULL);
|
||||
Py_DECREF(old_value);
|
||||
_Py_DECREF_SPECIALIZED(sub, (destructor)PyObject_Free);
|
||||
Py_DECREF(list);
|
||||
JUMPBY(INLINE_CACHE_ENTRIES_STORE_SUBSCR);
|
||||
STACK_SHRINK(3);
|
||||
JUMPBY(1);
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
TARGET(STORE_SUBSCR_DICT) {
|
||||
PyObject *sub = PEEK(1);
|
||||
PyObject *dict = PEEK(2);
|
||||
PyObject *value = PEEK(3);
|
||||
assert(cframe.use_tracing == 0);
|
||||
PyObject *sub = TOP();
|
||||
PyObject *dict = SECOND();
|
||||
PyObject *value = THIRD();
|
||||
DEOPT_IF(!PyDict_CheckExact(dict), STORE_SUBSCR);
|
||||
STACK_SHRINK(3);
|
||||
STAT_INC(STORE_SUBSCR, hit);
|
||||
int err = _PyDict_SetItem_Take2((PyDictObject *)dict, sub, value);
|
||||
Py_DECREF(dict);
|
||||
if (err != 0) {
|
||||
goto error;
|
||||
}
|
||||
JUMPBY(INLINE_CACHE_ENTRIES_STORE_SUBSCR);
|
||||
if (err) goto pop_3_error;
|
||||
STACK_SHRINK(3);
|
||||
JUMPBY(1);
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
TARGET(DELETE_SUBSCR) {
|
||||
PyObject *sub = TOP();
|
||||
PyObject *container = SECOND();
|
||||
int err;
|
||||
STACK_SHRINK(2);
|
||||
PyObject *sub = PEEK(1);
|
||||
PyObject *container = PEEK(2);
|
||||
/* del container[sub] */
|
||||
err = PyObject_DelItem(container, sub);
|
||||
int err = PyObject_DelItem(container, sub);
|
||||
Py_DECREF(container);
|
||||
Py_DECREF(sub);
|
||||
if (err != 0)
|
||||
goto error;
|
||||
if (err) goto pop_2_error;
|
||||
STACK_SHRINK(2);
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
TARGET(PRINT_EXPR) {
|
||||
PyObject *value = POP();
|
||||
PyObject *value = PEEK(1);
|
||||
PyObject *hook = _PySys_GetAttr(tstate, &_Py_ID(displayhook));
|
||||
PyObject *res;
|
||||
// Can't use ERROR_IF here.
|
||||
if (hook == NULL) {
|
||||
_PyErr_SetString(tstate, PyExc_RuntimeError,
|
||||
"lost sys.displayhook");
|
||||
Py_DECREF(value);
|
||||
goto error;
|
||||
if (true) goto pop_1_error;
|
||||
}
|
||||
res = PyObject_CallOneArg(hook, value);
|
||||
Py_DECREF(value);
|
||||
if (res == NULL)
|
||||
goto error;
|
||||
if (res == NULL) goto pop_1_error;
|
||||
Py_DECREF(res);
|
||||
STACK_SHRINK(1);
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
@ -607,9 +604,10 @@
|
||||
}
|
||||
|
||||
TARGET(INTERPRETER_EXIT) {
|
||||
PyObject *retval = PEEK(1);
|
||||
assert(frame == &entry_frame);
|
||||
assert(_PyFrame_IsIncomplete(frame));
|
||||
PyObject *retval = POP();
|
||||
STACK_SHRINK(1); // Since we're not going to DISPATCH()
|
||||
assert(EMPTY());
|
||||
/* Restore previous cframe and return. */
|
||||
tstate->cframe = cframe.previous;
|
||||
@ -621,7 +619,8 @@
|
||||
}
|
||||
|
||||
TARGET(RETURN_VALUE) {
|
||||
PyObject *retval = POP();
|
||||
PyObject *retval = PEEK(1);
|
||||
STACK_SHRINK(1);
|
||||
assert(EMPTY());
|
||||
_PyFrame_SetStackPointer(frame, stack_pointer);
|
||||
TRACE_FUNCTION_EXIT();
|
||||
@ -637,46 +636,39 @@
|
||||
}
|
||||
|
||||
TARGET(GET_AITER) {
|
||||
PyObject *obj = PEEK(1);
|
||||
PyObject *iter;
|
||||
unaryfunc getter = NULL;
|
||||
PyObject *iter = NULL;
|
||||
PyObject *obj = TOP();
|
||||
PyTypeObject *type = Py_TYPE(obj);
|
||||
|
||||
if (type->tp_as_async != NULL) {
|
||||
getter = type->tp_as_async->am_aiter;
|
||||
}
|
||||
|
||||
if (getter != NULL) {
|
||||
iter = (*getter)(obj);
|
||||
Py_DECREF(obj);
|
||||
if (iter == NULL) {
|
||||
SET_TOP(NULL);
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
else {
|
||||
SET_TOP(NULL);
|
||||
if (getter == NULL) {
|
||||
_PyErr_Format(tstate, PyExc_TypeError,
|
||||
"'async for' requires an object with "
|
||||
"__aiter__ method, got %.100s",
|
||||
type->tp_name);
|
||||
Py_DECREF(obj);
|
||||
goto error;
|
||||
if (true) goto pop_1_error;
|
||||
}
|
||||
|
||||
iter = (*getter)(obj);
|
||||
Py_DECREF(obj);
|
||||
if (iter == NULL) goto pop_1_error;
|
||||
|
||||
if (Py_TYPE(iter)->tp_as_async == NULL ||
|
||||
Py_TYPE(iter)->tp_as_async->am_anext == NULL) {
|
||||
|
||||
SET_TOP(NULL);
|
||||
_PyErr_Format(tstate, PyExc_TypeError,
|
||||
"'async for' received an object from __aiter__ "
|
||||
"that does not implement __anext__: %.100s",
|
||||
Py_TYPE(iter)->tp_name);
|
||||
Py_DECREF(iter);
|
||||
goto error;
|
||||
if (true) goto pop_1_error;
|
||||
}
|
||||
|
||||
SET_TOP(iter);
|
||||
POKE(1, iter);
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
@ -1131,51 +1123,46 @@
|
||||
|
||||
TARGET(STORE_ATTR) {
|
||||
PREDICTED(STORE_ATTR);
|
||||
_PyAttrCache *cache = (_PyAttrCache *)next_instr;
|
||||
if (ADAPTIVE_COUNTER_IS_ZERO(cache->counter)) {
|
||||
PyObject *owner = PEEK(1);
|
||||
PyObject *v = PEEK(2);
|
||||
uint16_t counter = read_u16(next_instr + 0);
|
||||
if (ADAPTIVE_COUNTER_IS_ZERO(counter)) {
|
||||
assert(cframe.use_tracing == 0);
|
||||
PyObject *owner = TOP();
|
||||
PyObject *name = GETITEM(names, oparg);
|
||||
next_instr--;
|
||||
_Py_Specialize_StoreAttr(owner, next_instr, name);
|
||||
DISPATCH_SAME_OPARG();
|
||||
}
|
||||
STAT_INC(STORE_ATTR, deferred);
|
||||
_PyAttrCache *cache = (_PyAttrCache *)next_instr;
|
||||
DECREMENT_ADAPTIVE_COUNTER(cache->counter);
|
||||
PyObject *name = GETITEM(names, oparg);
|
||||
PyObject *owner = TOP();
|
||||
PyObject *v = SECOND();
|
||||
int err;
|
||||
STACK_SHRINK(2);
|
||||
err = PyObject_SetAttr(owner, name, v);
|
||||
int err = PyObject_SetAttr(owner, name, v);
|
||||
Py_DECREF(v);
|
||||
Py_DECREF(owner);
|
||||
if (err != 0) {
|
||||
goto error;
|
||||
}
|
||||
JUMPBY(INLINE_CACHE_ENTRIES_STORE_ATTR);
|
||||
if (err) goto pop_2_error;
|
||||
STACK_SHRINK(2);
|
||||
JUMPBY(4);
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
TARGET(DELETE_ATTR) {
|
||||
PyObject *owner = PEEK(1);
|
||||
PyObject *name = GETITEM(names, oparg);
|
||||
PyObject *owner = POP();
|
||||
int err;
|
||||
err = PyObject_SetAttr(owner, name, (PyObject *)NULL);
|
||||
int err = PyObject_SetAttr(owner, name, (PyObject *)NULL);
|
||||
Py_DECREF(owner);
|
||||
if (err != 0)
|
||||
goto error;
|
||||
if (err) goto pop_1_error;
|
||||
STACK_SHRINK(1);
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
TARGET(STORE_GLOBAL) {
|
||||
PyObject *v = PEEK(1);
|
||||
PyObject *name = GETITEM(names, oparg);
|
||||
PyObject *v = POP();
|
||||
int err;
|
||||
err = PyDict_SetItem(GLOBALS(), name, v);
|
||||
int err = PyDict_SetItem(GLOBALS(), name, v);
|
||||
Py_DECREF(v);
|
||||
if (err != 0)
|
||||
goto error;
|
||||
if (err) goto pop_1_error;
|
||||
STACK_SHRINK(1);
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
@ -1970,20 +1957,18 @@
|
||||
}
|
||||
|
||||
TARGET(STORE_ATTR_INSTANCE_VALUE) {
|
||||
PyObject *owner = PEEK(1);
|
||||
PyObject *value = PEEK(2);
|
||||
uint32_t type_version = read_u32(next_instr + 1);
|
||||
uint16_t index = read_u16(next_instr + 3);
|
||||
assert(cframe.use_tracing == 0);
|
||||
PyObject *owner = TOP();
|
||||
PyTypeObject *tp = Py_TYPE(owner);
|
||||
_PyAttrCache *cache = (_PyAttrCache *)next_instr;
|
||||
uint32_t type_version = read_u32(cache->version);
|
||||
assert(type_version != 0);
|
||||
DEOPT_IF(tp->tp_version_tag != type_version, STORE_ATTR);
|
||||
assert(tp->tp_flags & Py_TPFLAGS_MANAGED_DICT);
|
||||
PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner);
|
||||
DEOPT_IF(!_PyDictOrValues_IsValues(dorv), STORE_ATTR);
|
||||
STAT_INC(STORE_ATTR, hit);
|
||||
Py_ssize_t index = cache->index;
|
||||
STACK_SHRINK(1);
|
||||
PyObject *value = POP();
|
||||
PyDictValues *values = _PyDictOrValues_GetValues(dorv);
|
||||
PyObject *old_value = values->values[index];
|
||||
values->values[index] = value;
|
||||
@ -1994,16 +1979,18 @@
|
||||
Py_DECREF(old_value);
|
||||
}
|
||||
Py_DECREF(owner);
|
||||
JUMPBY(INLINE_CACHE_ENTRIES_STORE_ATTR);
|
||||
STACK_SHRINK(2);
|
||||
JUMPBY(4);
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
TARGET(STORE_ATTR_WITH_HINT) {
|
||||
PyObject *owner = PEEK(1);
|
||||
PyObject *value = PEEK(2);
|
||||
uint32_t type_version = read_u32(next_instr + 1);
|
||||
uint16_t hint = read_u16(next_instr + 3);
|
||||
assert(cframe.use_tracing == 0);
|
||||
PyObject *owner = TOP();
|
||||
PyTypeObject *tp = Py_TYPE(owner);
|
||||
_PyAttrCache *cache = (_PyAttrCache *)next_instr;
|
||||
uint32_t type_version = read_u32(cache->version);
|
||||
assert(type_version != 0);
|
||||
DEOPT_IF(tp->tp_version_tag != type_version, STORE_ATTR);
|
||||
assert(tp->tp_flags & Py_TPFLAGS_MANAGED_DICT);
|
||||
@ -2013,17 +2000,14 @@
|
||||
DEOPT_IF(dict == NULL, STORE_ATTR);
|
||||
assert(PyDict_CheckExact((PyObject *)dict));
|
||||
PyObject *name = GETITEM(names, oparg);
|
||||
uint16_t hint = cache->index;
|
||||
DEOPT_IF(hint >= (size_t)dict->ma_keys->dk_nentries, STORE_ATTR);
|
||||
PyObject *value, *old_value;
|
||||
PyObject *old_value;
|
||||
uint64_t new_version;
|
||||
if (DK_IS_UNICODE(dict->ma_keys)) {
|
||||
PyDictUnicodeEntry *ep = DK_UNICODE_ENTRIES(dict->ma_keys) + hint;
|
||||
DEOPT_IF(ep->me_key != name, STORE_ATTR);
|
||||
old_value = ep->me_value;
|
||||
DEOPT_IF(old_value == NULL, STORE_ATTR);
|
||||
STACK_SHRINK(1);
|
||||
value = POP();
|
||||
new_version = _PyDict_NotifyEvent(PyDict_EVENT_MODIFIED, dict, name, value);
|
||||
ep->me_value = value;
|
||||
}
|
||||
@ -2032,8 +2016,6 @@
|
||||
DEOPT_IF(ep->me_key != name, STORE_ATTR);
|
||||
old_value = ep->me_value;
|
||||
DEOPT_IF(old_value == NULL, STORE_ATTR);
|
||||
STACK_SHRINK(1);
|
||||
value = POP();
|
||||
new_version = _PyDict_NotifyEvent(PyDict_EVENT_MODIFIED, dict, name, value);
|
||||
ep->me_value = value;
|
||||
}
|
||||
@ -2046,37 +2028,39 @@
|
||||
/* PEP 509 */
|
||||
dict->ma_version_tag = new_version;
|
||||
Py_DECREF(owner);
|
||||
JUMPBY(INLINE_CACHE_ENTRIES_STORE_ATTR);
|
||||
STACK_SHRINK(2);
|
||||
JUMPBY(4);
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
TARGET(STORE_ATTR_SLOT) {
|
||||
PyObject *owner = PEEK(1);
|
||||
PyObject *value = PEEK(2);
|
||||
uint32_t type_version = read_u32(next_instr + 1);
|
||||
uint16_t index = read_u16(next_instr + 3);
|
||||
assert(cframe.use_tracing == 0);
|
||||
PyObject *owner = TOP();
|
||||
PyTypeObject *tp = Py_TYPE(owner);
|
||||
_PyAttrCache *cache = (_PyAttrCache *)next_instr;
|
||||
uint32_t type_version = read_u32(cache->version);
|
||||
assert(type_version != 0);
|
||||
DEOPT_IF(tp->tp_version_tag != type_version, STORE_ATTR);
|
||||
char *addr = (char *)owner + cache->index;
|
||||
char *addr = (char *)owner + index;
|
||||
STAT_INC(STORE_ATTR, hit);
|
||||
STACK_SHRINK(1);
|
||||
PyObject *value = POP();
|
||||
PyObject *old_value = *(PyObject **)addr;
|
||||
*(PyObject **)addr = value;
|
||||
Py_XDECREF(old_value);
|
||||
Py_DECREF(owner);
|
||||
JUMPBY(INLINE_CACHE_ENTRIES_STORE_ATTR);
|
||||
STACK_SHRINK(2);
|
||||
JUMPBY(4);
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
TARGET(COMPARE_OP) {
|
||||
PREDICTED(COMPARE_OP);
|
||||
PyObject *right = PEEK(1);
|
||||
PyObject *left = PEEK(2);
|
||||
PyObject *res;
|
||||
_PyCompareOpCache *cache = (_PyCompareOpCache *)next_instr;
|
||||
if (ADAPTIVE_COUNTER_IS_ZERO(cache->counter)) {
|
||||
assert(cframe.use_tracing == 0);
|
||||
PyObject *right = TOP();
|
||||
PyObject *left = SECOND();
|
||||
next_instr--;
|
||||
_Py_Specialize_CompareOp(left, right, next_instr, oparg);
|
||||
DISPATCH_SAME_OPARG();
|
||||
@ -2084,109 +2068,13 @@
|
||||
STAT_INC(COMPARE_OP, deferred);
|
||||
DECREMENT_ADAPTIVE_COUNTER(cache->counter);
|
||||
assert(oparg <= Py_GE);
|
||||
PyObject *right = POP();
|
||||
PyObject *left = TOP();
|
||||
PyObject *res = PyObject_RichCompare(left, right, oparg);
|
||||
SET_TOP(res);
|
||||
res = PyObject_RichCompare(left, right, oparg);
|
||||
Py_DECREF(left);
|
||||
Py_DECREF(right);
|
||||
if (res == NULL) {
|
||||
goto error;
|
||||
}
|
||||
JUMPBY(INLINE_CACHE_ENTRIES_COMPARE_OP);
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
TARGET(COMPARE_OP_FLOAT_JUMP) {
|
||||
assert(cframe.use_tracing == 0);
|
||||
// Combined: COMPARE_OP (float ? float) + POP_JUMP_IF_(true/false)
|
||||
_PyCompareOpCache *cache = (_PyCompareOpCache *)next_instr;
|
||||
int when_to_jump_mask = cache->mask;
|
||||
PyObject *right = TOP();
|
||||
PyObject *left = SECOND();
|
||||
DEOPT_IF(!PyFloat_CheckExact(left), COMPARE_OP);
|
||||
DEOPT_IF(!PyFloat_CheckExact(right), COMPARE_OP);
|
||||
double dleft = PyFloat_AS_DOUBLE(left);
|
||||
double dright = PyFloat_AS_DOUBLE(right);
|
||||
int sign = (dleft > dright) - (dleft < dright);
|
||||
DEOPT_IF(isnan(dleft), COMPARE_OP);
|
||||
DEOPT_IF(isnan(dright), COMPARE_OP);
|
||||
STAT_INC(COMPARE_OP, hit);
|
||||
JUMPBY(INLINE_CACHE_ENTRIES_COMPARE_OP);
|
||||
NEXTOPARG();
|
||||
STACK_SHRINK(2);
|
||||
_Py_DECREF_SPECIALIZED(left, _PyFloat_ExactDealloc);
|
||||
_Py_DECREF_SPECIALIZED(right, _PyFloat_ExactDealloc);
|
||||
assert(opcode == POP_JUMP_IF_FALSE || opcode == POP_JUMP_IF_TRUE);
|
||||
int jump = (1 << (sign + 1)) & when_to_jump_mask;
|
||||
if (!jump) {
|
||||
next_instr++;
|
||||
}
|
||||
else {
|
||||
JUMPBY(1 + oparg);
|
||||
}
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
TARGET(COMPARE_OP_INT_JUMP) {
|
||||
assert(cframe.use_tracing == 0);
|
||||
// Combined: COMPARE_OP (int ? int) + POP_JUMP_IF_(true/false)
|
||||
_PyCompareOpCache *cache = (_PyCompareOpCache *)next_instr;
|
||||
int when_to_jump_mask = cache->mask;
|
||||
PyObject *right = TOP();
|
||||
PyObject *left = SECOND();
|
||||
DEOPT_IF(!PyLong_CheckExact(left), COMPARE_OP);
|
||||
DEOPT_IF(!PyLong_CheckExact(right), COMPARE_OP);
|
||||
DEOPT_IF((size_t)(Py_SIZE(left) + 1) > 2, COMPARE_OP);
|
||||
DEOPT_IF((size_t)(Py_SIZE(right) + 1) > 2, COMPARE_OP);
|
||||
STAT_INC(COMPARE_OP, hit);
|
||||
assert(Py_ABS(Py_SIZE(left)) <= 1 && Py_ABS(Py_SIZE(right)) <= 1);
|
||||
Py_ssize_t ileft = Py_SIZE(left) * ((PyLongObject *)left)->ob_digit[0];
|
||||
Py_ssize_t iright = Py_SIZE(right) * ((PyLongObject *)right)->ob_digit[0];
|
||||
int sign = (ileft > iright) - (ileft < iright);
|
||||
JUMPBY(INLINE_CACHE_ENTRIES_COMPARE_OP);
|
||||
NEXTOPARG();
|
||||
STACK_SHRINK(2);
|
||||
_Py_DECREF_SPECIALIZED(left, (destructor)PyObject_Free);
|
||||
_Py_DECREF_SPECIALIZED(right, (destructor)PyObject_Free);
|
||||
assert(opcode == POP_JUMP_IF_FALSE || opcode == POP_JUMP_IF_TRUE);
|
||||
int jump = (1 << (sign + 1)) & when_to_jump_mask;
|
||||
if (!jump) {
|
||||
next_instr++;
|
||||
}
|
||||
else {
|
||||
JUMPBY(1 + oparg);
|
||||
}
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
TARGET(COMPARE_OP_STR_JUMP) {
|
||||
assert(cframe.use_tracing == 0);
|
||||
// Combined: COMPARE_OP (str == str or str != str) + POP_JUMP_IF_(true/false)
|
||||
_PyCompareOpCache *cache = (_PyCompareOpCache *)next_instr;
|
||||
int invert = cache->mask;
|
||||
PyObject *right = TOP();
|
||||
PyObject *left = SECOND();
|
||||
DEOPT_IF(!PyUnicode_CheckExact(left), COMPARE_OP);
|
||||
DEOPT_IF(!PyUnicode_CheckExact(right), COMPARE_OP);
|
||||
STAT_INC(COMPARE_OP, hit);
|
||||
int res = _PyUnicode_Equal(left, right);
|
||||
assert(oparg == Py_EQ || oparg == Py_NE);
|
||||
JUMPBY(INLINE_CACHE_ENTRIES_COMPARE_OP);
|
||||
NEXTOPARG();
|
||||
assert(opcode == POP_JUMP_IF_FALSE || opcode == POP_JUMP_IF_TRUE);
|
||||
STACK_SHRINK(2);
|
||||
_Py_DECREF_SPECIALIZED(left, _PyUnicode_ExactDealloc);
|
||||
_Py_DECREF_SPECIALIZED(right, _PyUnicode_ExactDealloc);
|
||||
assert(res == 0 || res == 1);
|
||||
assert(invert == 0 || invert == 1);
|
||||
int jump = res ^ invert;
|
||||
if (!jump) {
|
||||
next_instr++;
|
||||
}
|
||||
else {
|
||||
JUMPBY(1 + oparg);
|
||||
}
|
||||
if (res == NULL) goto pop_2_error;
|
||||
STACK_SHRINK(1);
|
||||
POKE(1, res);
|
||||
JUMPBY(2);
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
@ -3681,7 +3569,7 @@
|
||||
if (res == NULL) goto pop_2_error;
|
||||
STACK_SHRINK(1);
|
||||
POKE(1, res);
|
||||
next_instr += 1;
|
||||
JUMPBY(1);
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
@ -3714,20 +3602,20 @@
|
||||
value = GETLOCAL(oparg);
|
||||
assert(value != NULL);
|
||||
Py_INCREF(value);
|
||||
_tmp_1 = value;
|
||||
_tmp_2 = value;
|
||||
}
|
||||
NEXTOPARG();
|
||||
next_instr++;
|
||||
JUMPBY(1);
|
||||
{
|
||||
PyObject *value;
|
||||
value = GETLOCAL(oparg);
|
||||
assert(value != NULL);
|
||||
Py_INCREF(value);
|
||||
_tmp_2 = value;
|
||||
_tmp_1 = value;
|
||||
}
|
||||
STACK_GROW(2);
|
||||
POKE(1, _tmp_2);
|
||||
POKE(2, _tmp_1);
|
||||
POKE(1, _tmp_1);
|
||||
POKE(2, _tmp_2);
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
@ -3739,19 +3627,19 @@
|
||||
value = GETLOCAL(oparg);
|
||||
assert(value != NULL);
|
||||
Py_INCREF(value);
|
||||
_tmp_1 = value;
|
||||
_tmp_2 = value;
|
||||
}
|
||||
NEXTOPARG();
|
||||
next_instr++;
|
||||
JUMPBY(1);
|
||||
{
|
||||
PyObject *value;
|
||||
value = GETITEM(consts, oparg);
|
||||
Py_INCREF(value);
|
||||
_tmp_2 = value;
|
||||
_tmp_1 = value;
|
||||
}
|
||||
STACK_GROW(2);
|
||||
POKE(1, _tmp_2);
|
||||
POKE(2, _tmp_1);
|
||||
POKE(1, _tmp_1);
|
||||
POKE(2, _tmp_2);
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
@ -3762,7 +3650,7 @@
|
||||
SETLOCAL(oparg, value);
|
||||
}
|
||||
NEXTOPARG();
|
||||
next_instr++;
|
||||
JUMPBY(1);
|
||||
{
|
||||
PyObject *value;
|
||||
value = GETLOCAL(oparg);
|
||||
@ -3775,16 +3663,16 @@
|
||||
}
|
||||
|
||||
TARGET(STORE_FAST__STORE_FAST) {
|
||||
PyObject *_tmp_1 = PEEK(2);
|
||||
PyObject *_tmp_2 = PEEK(1);
|
||||
PyObject *_tmp_1 = PEEK(1);
|
||||
PyObject *_tmp_2 = PEEK(2);
|
||||
{
|
||||
PyObject *value = _tmp_2;
|
||||
PyObject *value = _tmp_1;
|
||||
SETLOCAL(oparg, value);
|
||||
}
|
||||
NEXTOPARG();
|
||||
next_instr++;
|
||||
JUMPBY(1);
|
||||
{
|
||||
PyObject *value = _tmp_1;
|
||||
PyObject *value = _tmp_2;
|
||||
SETLOCAL(oparg, value);
|
||||
}
|
||||
STACK_SHRINK(2);
|
||||
@ -3798,32 +3686,145 @@
|
||||
PyObject *value;
|
||||
value = GETITEM(consts, oparg);
|
||||
Py_INCREF(value);
|
||||
_tmp_1 = value;
|
||||
_tmp_2 = value;
|
||||
}
|
||||
NEXTOPARG();
|
||||
next_instr++;
|
||||
JUMPBY(1);
|
||||
{
|
||||
PyObject *value;
|
||||
value = GETLOCAL(oparg);
|
||||
assert(value != NULL);
|
||||
Py_INCREF(value);
|
||||
_tmp_2 = value;
|
||||
_tmp_1 = value;
|
||||
}
|
||||
STACK_GROW(2);
|
||||
POKE(1, _tmp_2);
|
||||
POKE(2, _tmp_1);
|
||||
POKE(1, _tmp_1);
|
||||
POKE(2, _tmp_2);
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
TARGET(COMPARE_OP_FLOAT_JUMP) {
|
||||
PyObject *_tmp_1 = PEEK(1);
|
||||
PyObject *_tmp_2 = PEEK(2);
|
||||
{
|
||||
PyObject *right = _tmp_1;
|
||||
PyObject *left = _tmp_2;
|
||||
size_t jump;
|
||||
uint16_t when_to_jump_mask = read_u16(next_instr + 1);
|
||||
assert(cframe.use_tracing == 0);
|
||||
// Combined: COMPARE_OP (float ? float) + POP_JUMP_IF_(true/false)
|
||||
DEOPT_IF(!PyFloat_CheckExact(left), COMPARE_OP);
|
||||
DEOPT_IF(!PyFloat_CheckExact(right), COMPARE_OP);
|
||||
double dleft = PyFloat_AS_DOUBLE(left);
|
||||
double dright = PyFloat_AS_DOUBLE(right);
|
||||
// 1 if <, 2 if ==, 4 if >; this matches when _to_jump_mask
|
||||
int sign_ish = 2*(dleft > dright) + 2 - (dleft < dright);
|
||||
DEOPT_IF(isnan(dleft), COMPARE_OP);
|
||||
DEOPT_IF(isnan(dright), COMPARE_OP);
|
||||
STAT_INC(COMPARE_OP, hit);
|
||||
_Py_DECREF_SPECIALIZED(left, _PyFloat_ExactDealloc);
|
||||
_Py_DECREF_SPECIALIZED(right, _PyFloat_ExactDealloc);
|
||||
jump = sign_ish & when_to_jump_mask;
|
||||
_tmp_2 = (PyObject *)jump;
|
||||
}
|
||||
JUMPBY(2);
|
||||
NEXTOPARG();
|
||||
JUMPBY(1);
|
||||
{
|
||||
size_t jump = (size_t)_tmp_2;
|
||||
assert(opcode == POP_JUMP_IF_FALSE || opcode == POP_JUMP_IF_TRUE);
|
||||
if (jump) {
|
||||
JUMPBY(oparg);
|
||||
}
|
||||
}
|
||||
STACK_SHRINK(2);
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
TARGET(COMPARE_OP_INT_JUMP) {
|
||||
PyObject *_tmp_1 = PEEK(1);
|
||||
PyObject *_tmp_2 = PEEK(2);
|
||||
{
|
||||
PyObject *right = _tmp_1;
|
||||
PyObject *left = _tmp_2;
|
||||
size_t jump;
|
||||
uint16_t when_to_jump_mask = read_u16(next_instr + 1);
|
||||
assert(cframe.use_tracing == 0);
|
||||
// Combined: COMPARE_OP (int ? int) + POP_JUMP_IF_(true/false)
|
||||
DEOPT_IF(!PyLong_CheckExact(left), COMPARE_OP);
|
||||
DEOPT_IF(!PyLong_CheckExact(right), COMPARE_OP);
|
||||
DEOPT_IF((size_t)(Py_SIZE(left) + 1) > 2, COMPARE_OP);
|
||||
DEOPT_IF((size_t)(Py_SIZE(right) + 1) > 2, COMPARE_OP);
|
||||
STAT_INC(COMPARE_OP, hit);
|
||||
assert(Py_ABS(Py_SIZE(left)) <= 1 && Py_ABS(Py_SIZE(right)) <= 1);
|
||||
Py_ssize_t ileft = Py_SIZE(left) * ((PyLongObject *)left)->ob_digit[0];
|
||||
Py_ssize_t iright = Py_SIZE(right) * ((PyLongObject *)right)->ob_digit[0];
|
||||
// 1 if <, 2 if ==, 4 if >; this matches when _to_jump_mask
|
||||
int sign_ish = 2*(ileft > iright) + 2 - (ileft < iright);
|
||||
_Py_DECREF_SPECIALIZED(left, (destructor)PyObject_Free);
|
||||
_Py_DECREF_SPECIALIZED(right, (destructor)PyObject_Free);
|
||||
jump = sign_ish & when_to_jump_mask;
|
||||
_tmp_2 = (PyObject *)jump;
|
||||
}
|
||||
JUMPBY(2);
|
||||
NEXTOPARG();
|
||||
JUMPBY(1);
|
||||
{
|
||||
size_t jump = (size_t)_tmp_2;
|
||||
assert(opcode == POP_JUMP_IF_FALSE || opcode == POP_JUMP_IF_TRUE);
|
||||
if (jump) {
|
||||
JUMPBY(oparg);
|
||||
}
|
||||
}
|
||||
STACK_SHRINK(2);
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
TARGET(COMPARE_OP_STR_JUMP) {
|
||||
PyObject *_tmp_1 = PEEK(1);
|
||||
PyObject *_tmp_2 = PEEK(2);
|
||||
{
|
||||
PyObject *right = _tmp_1;
|
||||
PyObject *left = _tmp_2;
|
||||
size_t jump;
|
||||
uint16_t invert = read_u16(next_instr + 1);
|
||||
assert(cframe.use_tracing == 0);
|
||||
// Combined: COMPARE_OP (str == str or str != str) + POP_JUMP_IF_(true/false)
|
||||
DEOPT_IF(!PyUnicode_CheckExact(left), COMPARE_OP);
|
||||
DEOPT_IF(!PyUnicode_CheckExact(right), COMPARE_OP);
|
||||
STAT_INC(COMPARE_OP, hit);
|
||||
int res = _PyUnicode_Equal(left, right);
|
||||
assert(oparg == Py_EQ || oparg == Py_NE);
|
||||
_Py_DECREF_SPECIALIZED(left, _PyUnicode_ExactDealloc);
|
||||
_Py_DECREF_SPECIALIZED(right, _PyUnicode_ExactDealloc);
|
||||
assert(res == 0 || res == 1);
|
||||
assert(invert == 0 || invert == 1);
|
||||
jump = res ^ invert;
|
||||
_tmp_2 = (PyObject *)jump;
|
||||
}
|
||||
JUMPBY(2);
|
||||
NEXTOPARG();
|
||||
JUMPBY(1);
|
||||
{
|
||||
size_t jump = (size_t)_tmp_2;
|
||||
assert(opcode == POP_JUMP_IF_FALSE || opcode == POP_JUMP_IF_TRUE);
|
||||
if (jump) {
|
||||
JUMPBY(oparg);
|
||||
}
|
||||
}
|
||||
STACK_SHRINK(2);
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
TARGET(END_FOR) {
|
||||
PyObject *_tmp_1 = PEEK(2);
|
||||
PyObject *_tmp_2 = PEEK(1);
|
||||
PyObject *_tmp_1 = PEEK(1);
|
||||
PyObject *_tmp_2 = PEEK(2);
|
||||
{
|
||||
PyObject *value = _tmp_2;
|
||||
PyObject *value = _tmp_1;
|
||||
Py_DECREF(value);
|
||||
}
|
||||
{
|
||||
PyObject *value = _tmp_1;
|
||||
PyObject *value = _tmp_2;
|
||||
Py_DECREF(value);
|
||||
}
|
||||
STACK_SHRINK(2);
|
||||
|
@ -13,6 +13,7 @@ import sys
|
||||
import typing
|
||||
|
||||
import parser
|
||||
from parser import StackEffect
|
||||
|
||||
DEFAULT_INPUT = os.path.relpath(
|
||||
os.path.join(os.path.dirname(__file__), "../../Python/bytecodes.c")
|
||||
@ -22,7 +23,7 @@ DEFAULT_OUTPUT = os.path.relpath(
|
||||
)
|
||||
BEGIN_MARKER = "// BEGIN BYTECODES //"
|
||||
END_MARKER = "// END BYTECODES //"
|
||||
RE_PREDICTED = r"(?s)(?:PREDICT\(|GO_TO_INSTRUCTION\(|DEOPT_IF\(.*?,\s*)(\w+)\);"
|
||||
RE_PREDICTED = r"^\s*(?:PREDICT\(|GO_TO_INSTRUCTION\(|DEOPT_IF\(.*?,\s*)(\w+)\);\s*$"
|
||||
UNUSED = "unused"
|
||||
BITS_PER_CODE_UNIT = 16
|
||||
|
||||
@ -73,6 +74,34 @@ class Formatter:
|
||||
yield
|
||||
self.emit("}")
|
||||
|
||||
def stack_adjust(self, diff: int):
|
||||
if diff > 0:
|
||||
self.emit(f"STACK_GROW({diff});")
|
||||
elif diff < 0:
|
||||
self.emit(f"STACK_SHRINK({-diff});")
|
||||
|
||||
def declare(self, dst: StackEffect, src: StackEffect | None):
|
||||
if dst.name == UNUSED:
|
||||
return
|
||||
typ = f"{dst.type} " if dst.type else "PyObject *"
|
||||
init = ""
|
||||
if src:
|
||||
cast = self.cast(dst, src)
|
||||
init = f" = {cast}{src.name}"
|
||||
self.emit(f"{typ}{dst.name}{init};")
|
||||
|
||||
def assign(self, dst: StackEffect, src: StackEffect):
|
||||
if src.name == UNUSED:
|
||||
return
|
||||
cast = self.cast(dst, src)
|
||||
if m := re.match(r"^PEEK\((\d+)\)$", dst.name):
|
||||
self.emit(f"POKE({m.group(1)}, {cast}{src.name});")
|
||||
else:
|
||||
self.emit(f"{dst.name} = {cast}{src.name};")
|
||||
|
||||
def cast(self, dst: StackEffect, src: StackEffect) -> str:
|
||||
return f"({dst.type or 'PyObject *'})" if src.type != dst.type else ""
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
class Instruction:
|
||||
@ -83,13 +112,15 @@ class Instruction:
|
||||
kind: typing.Literal["inst", "op"]
|
||||
name: str
|
||||
block: parser.Block
|
||||
block_text: list[str] # Block.text, less curlies, less PREDICT() calls
|
||||
predictions: list[str] # Prediction targets (instruction names)
|
||||
|
||||
# Computed by constructor
|
||||
always_exits: bool
|
||||
cache_offset: int
|
||||
cache_effects: list[parser.CacheEffect]
|
||||
input_effects: list[parser.StackEffect]
|
||||
output_effects: list[parser.StackEffect]
|
||||
input_effects: list[StackEffect]
|
||||
output_effects: list[StackEffect]
|
||||
|
||||
# Set later
|
||||
family: parser.Family | None = None
|
||||
@ -100,13 +131,14 @@ class Instruction:
|
||||
self.kind = inst.kind
|
||||
self.name = inst.name
|
||||
self.block = inst.block
|
||||
self.always_exits = always_exits(self.block)
|
||||
self.block_text, self.predictions = extract_block_text(self.block)
|
||||
self.always_exits = always_exits(self.block_text)
|
||||
self.cache_effects = [
|
||||
effect for effect in inst.inputs if isinstance(effect, parser.CacheEffect)
|
||||
]
|
||||
self.cache_offset = sum(c.size for c in self.cache_effects)
|
||||
self.input_effects = [
|
||||
effect for effect in inst.inputs if isinstance(effect, parser.StackEffect)
|
||||
effect for effect in inst.inputs if isinstance(effect, StackEffect)
|
||||
]
|
||||
self.output_effects = inst.outputs # For consistency/completeness
|
||||
|
||||
@ -122,42 +154,39 @@ class Instruction:
|
||||
)
|
||||
|
||||
# Write input stack effect variable declarations and initializations
|
||||
for i, seffect in enumerate(reversed(self.input_effects), 1):
|
||||
if seffect.name != UNUSED:
|
||||
out.emit(f"PyObject *{seffect.name} = PEEK({i});")
|
||||
for i, ieffect in enumerate(reversed(self.input_effects), 1):
|
||||
src = StackEffect(f"PEEK({i})", "")
|
||||
out.declare(ieffect, src)
|
||||
|
||||
# Write output stack effect variable declarations
|
||||
input_names = {seffect.name for seffect in self.input_effects}
|
||||
input_names.add(UNUSED)
|
||||
for seffect in self.output_effects:
|
||||
if seffect.name not in input_names:
|
||||
out.emit(f"PyObject *{seffect.name};")
|
||||
input_names = {ieffect.name for ieffect in self.input_effects}
|
||||
for oeffect in self.output_effects:
|
||||
if oeffect.name not in input_names:
|
||||
out.declare(oeffect, None)
|
||||
|
||||
self.write_body(out, 0)
|
||||
|
||||
# Skip the rest if the block always exits
|
||||
if always_exits(self.block):
|
||||
if self.always_exits:
|
||||
return
|
||||
|
||||
# Write net stack growth/shrinkage
|
||||
diff = len(self.output_effects) - len(self.input_effects)
|
||||
if diff > 0:
|
||||
out.emit(f"STACK_GROW({diff});")
|
||||
elif diff < 0:
|
||||
out.emit(f"STACK_SHRINK({-diff});")
|
||||
out.stack_adjust(diff)
|
||||
|
||||
# Write output stack effect assignments
|
||||
unmoved_names = {UNUSED}
|
||||
unmoved_names: set[str] = set()
|
||||
for ieffect, oeffect in zip(self.input_effects, self.output_effects):
|
||||
if ieffect.name == oeffect.name:
|
||||
unmoved_names.add(ieffect.name)
|
||||
for i, seffect in enumerate(reversed(self.output_effects)):
|
||||
if seffect.name not in unmoved_names:
|
||||
out.emit(f"POKE({i+1}, {seffect.name});")
|
||||
for i, oeffect in enumerate(reversed(self.output_effects), 1):
|
||||
if oeffect.name not in unmoved_names:
|
||||
dst = StackEffect(f"PEEK({i})", "")
|
||||
out.assign(dst, oeffect)
|
||||
|
||||
# Write cache effect
|
||||
if self.cache_offset:
|
||||
out.emit(f"next_instr += {self.cache_offset};")
|
||||
out.emit(f"JUMPBY({self.cache_offset});")
|
||||
|
||||
def write_body(self, out: Formatter, dedent: int, cache_adjust: int = 0) -> None:
|
||||
"""Write the instruction body."""
|
||||
@ -171,36 +200,19 @@ class Instruction:
|
||||
# is always an object pointer.
|
||||
# If this becomes false, we need a way to specify
|
||||
# syntactically what type the cache data is.
|
||||
type = "PyObject *"
|
||||
typ = "PyObject *"
|
||||
func = "read_obj"
|
||||
else:
|
||||
type = f"uint{bits}_t "
|
||||
typ = f"uint{bits}_t "
|
||||
func = f"read_u{bits}"
|
||||
out.emit(f"{type}{ceffect.name} = {func}(next_instr + {cache_offset});")
|
||||
out.emit(f"{typ}{ceffect.name} = {func}(next_instr + {cache_offset});")
|
||||
cache_offset += ceffect.size
|
||||
assert cache_offset == self.cache_offset + cache_adjust
|
||||
|
||||
# Get lines of text with proper dedent
|
||||
blocklines = self.block.to_text(dedent=dedent).splitlines(True)
|
||||
|
||||
# Remove blank lines from both ends
|
||||
while blocklines and not blocklines[0].strip():
|
||||
blocklines.pop(0)
|
||||
while blocklines and not blocklines[-1].strip():
|
||||
blocklines.pop()
|
||||
|
||||
# Remove leading and trailing braces
|
||||
assert blocklines and blocklines[0].strip() == "{"
|
||||
assert blocklines and blocklines[-1].strip() == "}"
|
||||
blocklines.pop()
|
||||
blocklines.pop(0)
|
||||
|
||||
# Remove trailing blank lines
|
||||
while blocklines and not blocklines[-1].strip():
|
||||
blocklines.pop()
|
||||
|
||||
# Write the body, substituting a goto for ERROR_IF()
|
||||
for line in blocklines:
|
||||
assert dedent <= 0
|
||||
extra = " " * -dedent
|
||||
for line in self.block_text:
|
||||
if m := re.match(r"(\s*)ERROR_IF\((.+), (\w+)\);\s*$", line):
|
||||
space, cond, label = m.groups()
|
||||
# ERROR_IF() must pop the inputs from the stack.
|
||||
@ -215,34 +227,36 @@ class Instruction:
|
||||
else:
|
||||
break
|
||||
if ninputs:
|
||||
out.write_raw(f"{space}if ({cond}) goto pop_{ninputs}_{label};\n")
|
||||
out.write_raw(
|
||||
f"{extra}{space}if ({cond}) goto pop_{ninputs}_{label};\n"
|
||||
)
|
||||
else:
|
||||
out.write_raw(f"{space}if ({cond}) goto {label};\n")
|
||||
out.write_raw(f"{extra}{space}if ({cond}) goto {label};\n")
|
||||
else:
|
||||
out.write_raw(line)
|
||||
out.write_raw(extra + line)
|
||||
|
||||
|
||||
InstructionOrCacheEffect = Instruction | parser.CacheEffect
|
||||
StackEffectMapping = list[tuple[StackEffect, StackEffect]]
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
class Component:
|
||||
instr: Instruction
|
||||
input_mapping: dict[str, parser.StackEffect]
|
||||
output_mapping: dict[str, parser.StackEffect]
|
||||
input_mapping: StackEffectMapping
|
||||
output_mapping: StackEffectMapping
|
||||
|
||||
def write_body(self, out: Formatter, cache_adjust: int) -> None:
|
||||
with out.block(""):
|
||||
for var, ieffect in self.input_mapping.items():
|
||||
out.emit(f"PyObject *{ieffect.name} = {var};")
|
||||
for oeffect in self.output_mapping.values():
|
||||
out.emit(f"PyObject *{oeffect.name};")
|
||||
for var, ieffect in self.input_mapping:
|
||||
out.declare(ieffect, var)
|
||||
for _, oeffect in self.output_mapping:
|
||||
out.declare(oeffect, None)
|
||||
|
||||
self.instr.write_body(out, dedent=-4, cache_adjust=cache_adjust)
|
||||
for var, oeffect in self.output_mapping.items():
|
||||
out.emit(f"{var} = {oeffect.name};")
|
||||
|
||||
|
||||
# TODO: Use a common base class for {Super,Macro}Instruction
|
||||
for var, oeffect in self.output_mapping:
|
||||
out.assign(var, oeffect)
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
@ -250,7 +264,7 @@ class SuperOrMacroInstruction:
|
||||
"""Common fields for super- and macro instructions."""
|
||||
|
||||
name: str
|
||||
stack: list[str]
|
||||
stack: list[StackEffect]
|
||||
initial_sp: int
|
||||
final_sp: int
|
||||
|
||||
@ -369,7 +383,11 @@ class Analyzer:
|
||||
def find_predictions(self) -> None:
|
||||
"""Find the instructions that need PREDICTED() labels."""
|
||||
for instr in self.instrs.values():
|
||||
for target in re.findall(RE_PREDICTED, instr.block.text):
|
||||
targets = set(instr.predictions)
|
||||
for line in instr.block_text:
|
||||
if m := re.match(RE_PREDICTED, line):
|
||||
targets.add(m.group(1))
|
||||
for target in targets:
|
||||
if target_instr := self.instrs.get(target):
|
||||
target_instr.predicted = True
|
||||
else:
|
||||
@ -440,24 +458,9 @@ class Analyzer:
|
||||
stack, initial_sp = self.stack_analysis(components)
|
||||
sp = initial_sp
|
||||
parts: list[Component] = []
|
||||
for component in components:
|
||||
match component:
|
||||
case parser.CacheEffect() as ceffect:
|
||||
parts.append(ceffect)
|
||||
case Instruction() as instr:
|
||||
input_mapping = {}
|
||||
for ieffect in reversed(instr.input_effects):
|
||||
sp -= 1
|
||||
if ieffect.name != UNUSED:
|
||||
input_mapping[stack[sp]] = ieffect
|
||||
output_mapping = {}
|
||||
for oeffect in instr.output_effects:
|
||||
if oeffect.name != UNUSED:
|
||||
output_mapping[stack[sp]] = oeffect
|
||||
sp += 1
|
||||
parts.append(Component(instr, input_mapping, output_mapping))
|
||||
case _:
|
||||
typing.assert_never(component)
|
||||
for instr in components:
|
||||
part, sp = self.analyze_instruction(instr, stack, sp)
|
||||
parts.append(part)
|
||||
final_sp = sp
|
||||
return SuperInstruction(super.name, stack, initial_sp, final_sp, super, parts)
|
||||
|
||||
@ -471,22 +474,26 @@ class Analyzer:
|
||||
case parser.CacheEffect() as ceffect:
|
||||
parts.append(ceffect)
|
||||
case Instruction() as instr:
|
||||
input_mapping = {}
|
||||
for ieffect in reversed(instr.input_effects):
|
||||
sp -= 1
|
||||
if ieffect.name != UNUSED:
|
||||
input_mapping[stack[sp]] = ieffect
|
||||
output_mapping = {}
|
||||
for oeffect in instr.output_effects:
|
||||
if oeffect.name != UNUSED:
|
||||
output_mapping[stack[sp]] = oeffect
|
||||
sp += 1
|
||||
parts.append(Component(instr, input_mapping, output_mapping))
|
||||
part, sp = self.analyze_instruction(instr, stack, sp)
|
||||
parts.append(part)
|
||||
case _:
|
||||
typing.assert_never(component)
|
||||
final_sp = sp
|
||||
return MacroInstruction(macro.name, stack, initial_sp, final_sp, macro, parts)
|
||||
|
||||
def analyze_instruction(
|
||||
self, instr: Instruction, stack: list[StackEffect], sp: int
|
||||
) -> tuple[Component, int]:
|
||||
input_mapping: StackEffectMapping = []
|
||||
for ieffect in reversed(instr.input_effects):
|
||||
sp -= 1
|
||||
input_mapping.append((stack[sp], ieffect))
|
||||
output_mapping: StackEffectMapping = []
|
||||
for oeffect in instr.output_effects:
|
||||
output_mapping.append((stack[sp], oeffect))
|
||||
sp += 1
|
||||
return Component(instr, input_mapping, output_mapping), sp
|
||||
|
||||
def check_super_components(self, super: parser.Super) -> list[Instruction]:
|
||||
components: list[Instruction] = []
|
||||
for op in super.ops:
|
||||
@ -514,7 +521,7 @@ class Analyzer:
|
||||
|
||||
def stack_analysis(
|
||||
self, components: typing.Iterable[InstructionOrCacheEffect]
|
||||
) -> tuple[list[str], int]:
|
||||
) -> tuple[list[StackEffect], int]:
|
||||
"""Analyze a super-instruction or macro.
|
||||
|
||||
Print an error if there's a cache effect (which we don't support yet).
|
||||
@ -536,7 +543,10 @@ class Analyzer:
|
||||
# At this point, 'current' is the net stack effect,
|
||||
# and 'lowest' and 'highest' are the extremes.
|
||||
# Note that 'lowest' may be negative.
|
||||
stack = [f"_tmp_{i+1}" for i in range(highest - lowest)]
|
||||
# TODO: Reverse the numbering.
|
||||
stack = [
|
||||
StackEffect(f"_tmp_{i+1}", "") for i in reversed(range(highest - lowest))
|
||||
]
|
||||
return stack, -lowest
|
||||
|
||||
def write_instructions(self) -> None:
|
||||
@ -561,7 +571,9 @@ class Analyzer:
|
||||
if instr.predicted:
|
||||
self.out.emit(f"PREDICTED({name});")
|
||||
instr.write(self.out)
|
||||
if not always_exits(instr.block):
|
||||
if not instr.always_exits:
|
||||
for prediction in instr.predictions:
|
||||
self.out.emit(f"PREDICT({prediction});")
|
||||
self.out.emit(f"DISPATCH();")
|
||||
|
||||
# Write and count super-instructions
|
||||
@ -589,11 +601,11 @@ class Analyzer:
|
||||
for comp in sup.parts:
|
||||
if not first:
|
||||
self.out.emit("NEXTOPARG();")
|
||||
self.out.emit("next_instr++;")
|
||||
self.out.emit("JUMPBY(1);")
|
||||
first = False
|
||||
comp.write_body(self.out, 0)
|
||||
if comp.instr.cache_offset:
|
||||
self.out.emit(f"next_instr += {comp.instr.cache_offset};")
|
||||
self.out.emit(f"JUMPBY({comp.instr.cache_offset});")
|
||||
|
||||
def write_macro(self, mac: MacroInstruction) -> None:
|
||||
"""Write code for a macro instruction."""
|
||||
@ -608,43 +620,68 @@ class Analyzer:
|
||||
cache_adjust += comp.instr.cache_offset
|
||||
|
||||
if cache_adjust:
|
||||
self.out.emit(f"next_instr += {cache_adjust};")
|
||||
self.out.emit(f"JUMPBY({cache_adjust});")
|
||||
|
||||
@contextlib.contextmanager
|
||||
def wrap_super_or_macro(self, up: SuperOrMacroInstruction):
|
||||
"""Shared boilerplate for super- and macro instructions."""
|
||||
# TODO: Somewhere (where?) make it so that if one instruction
|
||||
# has an output that is input to another, and the variable names
|
||||
# and types match and don't conflict with other instructions,
|
||||
# that variable is declared with the right name and type in the
|
||||
# outer block, rather than trusting the compiler to optimize it.
|
||||
self.out.emit("")
|
||||
with self.out.block(f"TARGET({up.name})"):
|
||||
for i, var in enumerate(up.stack):
|
||||
for i, var in reversed(list(enumerate(up.stack))):
|
||||
src = None
|
||||
if i < up.initial_sp:
|
||||
self.out.emit(f"PyObject *{var} = PEEK({up.initial_sp - i});")
|
||||
else:
|
||||
self.out.emit(f"PyObject *{var};")
|
||||
src = StackEffect(f"PEEK({up.initial_sp - i})", "")
|
||||
self.out.declare(var, src)
|
||||
|
||||
yield
|
||||
|
||||
if up.final_sp > up.initial_sp:
|
||||
self.out.emit(f"STACK_GROW({up.final_sp - up.initial_sp});")
|
||||
elif up.final_sp < up.initial_sp:
|
||||
self.out.emit(f"STACK_SHRINK({up.initial_sp - up.final_sp});")
|
||||
self.out.stack_adjust(up.final_sp - up.initial_sp)
|
||||
for i, var in enumerate(reversed(up.stack[: up.final_sp]), 1):
|
||||
self.out.emit(f"POKE({i}, {var});")
|
||||
dst = StackEffect(f"PEEK({i})", "")
|
||||
self.out.assign(dst, var)
|
||||
|
||||
self.out.emit(f"DISPATCH();")
|
||||
|
||||
|
||||
def always_exits(block: parser.Block) -> bool:
|
||||
def extract_block_text(block: parser.Block) -> tuple[list[str], list[str]]:
|
||||
# Get lines of text with proper dedent
|
||||
blocklines = block.text.splitlines(True)
|
||||
|
||||
# Remove blank lines from both ends
|
||||
while blocklines and not blocklines[0].strip():
|
||||
blocklines.pop(0)
|
||||
while blocklines and not blocklines[-1].strip():
|
||||
blocklines.pop()
|
||||
|
||||
# Remove leading and trailing braces
|
||||
assert blocklines and blocklines[0].strip() == "{"
|
||||
assert blocklines and blocklines[-1].strip() == "}"
|
||||
blocklines.pop()
|
||||
blocklines.pop(0)
|
||||
|
||||
# Remove trailing blank lines
|
||||
while blocklines and not blocklines[-1].strip():
|
||||
blocklines.pop()
|
||||
|
||||
# Separate PREDICT(...) macros from end
|
||||
predictions: list[str] = []
|
||||
while blocklines and (m := re.match(r"^\s*PREDICT\((\w+)\);\s*$", blocklines[-1])):
|
||||
predictions.insert(0, m.group(1))
|
||||
blocklines.pop()
|
||||
|
||||
return blocklines, predictions
|
||||
|
||||
|
||||
def always_exits(lines: list[str]) -> bool:
|
||||
"""Determine whether a block always ends in a return/goto/etc."""
|
||||
text = block.text
|
||||
lines = text.splitlines()
|
||||
while lines and not lines[-1].strip():
|
||||
lines.pop()
|
||||
if not lines or lines[-1].strip() != "}":
|
||||
return False
|
||||
lines.pop()
|
||||
if not lines:
|
||||
return False
|
||||
line = lines.pop().rstrip()
|
||||
line = lines[-1].rstrip()
|
||||
# Indent must match exactly (TODO: Do something better)
|
||||
if line[:12] != " " * 12:
|
||||
return False
|
||||
|
@ -62,7 +62,8 @@ class Block(Node):
|
||||
@dataclass
|
||||
class StackEffect(Node):
|
||||
name: str
|
||||
# TODO: type, condition
|
||||
type: str = ""
|
||||
# TODO: array, condition
|
||||
|
||||
|
||||
@dataclass
|
||||
@ -147,7 +148,7 @@ class Parser(PLexer):
|
||||
if self.expect(lx.LPAREN) and (tkn := self.expect(lx.IDENTIFIER)):
|
||||
name = tkn.text
|
||||
if self.expect(lx.COMMA):
|
||||
inp, outp = self.stack_effect()
|
||||
inp, outp = self.io_effect()
|
||||
if self.expect(lx.RPAREN):
|
||||
if (tkn := self.peek()) and tkn.kind == lx.LBRACE:
|
||||
return InstHeader(kind, name, inp, outp)
|
||||
@ -156,7 +157,7 @@ class Parser(PLexer):
|
||||
return InstHeader(kind, name, [], [])
|
||||
return None
|
||||
|
||||
def stack_effect(self) -> tuple[list[InputEffect], list[OutputEffect]]:
|
||||
def io_effect(self) -> tuple[list[InputEffect], list[OutputEffect]]:
|
||||
# '(' [inputs] '--' [outputs] ')'
|
||||
if self.expect(lx.LPAREN):
|
||||
inputs = self.inputs() or []
|
||||
@ -181,23 +182,7 @@ class Parser(PLexer):
|
||||
|
||||
@contextual
|
||||
def input(self) -> InputEffect | None:
|
||||
# IDENTIFIER '/' INTEGER (CacheEffect)
|
||||
# IDENTIFIER (StackEffect)
|
||||
if tkn := self.expect(lx.IDENTIFIER):
|
||||
if self.expect(lx.DIVIDE):
|
||||
if num := self.expect(lx.NUMBER):
|
||||
try:
|
||||
size = int(num.text)
|
||||
except ValueError:
|
||||
raise self.make_syntax_error(
|
||||
f"Expected integer, got {num.text!r}"
|
||||
)
|
||||
else:
|
||||
return CacheEffect(tkn.text, size)
|
||||
raise self.make_syntax_error("Expected integer")
|
||||
else:
|
||||
# TODO: Arrays, conditions
|
||||
return StackEffect(tkn.text)
|
||||
return self.cache_effect() or self.stack_effect()
|
||||
|
||||
def outputs(self) -> list[OutputEffect] | None:
|
||||
# output (, output)*
|
||||
@ -214,8 +199,30 @@ class Parser(PLexer):
|
||||
|
||||
@contextual
|
||||
def output(self) -> OutputEffect | None:
|
||||
return self.stack_effect()
|
||||
|
||||
@contextual
|
||||
def cache_effect(self) -> CacheEffect | None:
|
||||
# IDENTIFIER '/' NUMBER
|
||||
if tkn := self.expect(lx.IDENTIFIER):
|
||||
return StackEffect(tkn.text)
|
||||
if self.expect(lx.DIVIDE):
|
||||
num = self.require(lx.NUMBER).text
|
||||
try:
|
||||
size = int(num)
|
||||
except ValueError:
|
||||
raise self.make_syntax_error(f"Expected integer, got {num!r}")
|
||||
else:
|
||||
return CacheEffect(tkn.text, size)
|
||||
|
||||
@contextual
|
||||
def stack_effect(self) -> StackEffect | None:
|
||||
# IDENTIFIER [':' IDENTIFIER]
|
||||
# TODO: Arrays, conditions
|
||||
if tkn := self.expect(lx.IDENTIFIER):
|
||||
type = ""
|
||||
if self.expect(lx.COLON):
|
||||
type = self.require(lx.IDENTIFIER).text
|
||||
return StackEffect(tkn.text, type)
|
||||
|
||||
@contextual
|
||||
def super_def(self) -> Super | None:
|
||||
|
Loading…
Reference in New Issue
Block a user