[Python-checkins] GH-98831: Typed stack effects, and more instructions converted (#99764)

gvanrossum webhook-mailer at python.org
Thu Dec 8 16:31:33 EST 2022


https://github.com/python/cpython/commit/c85be734d1823f02a3600d7cd9195cecbf51afe8
commit: c85be734d1823f02a3600d7cd9195cecbf51afe8
branch: main
author: Guido van Rossum <guido at python.org>
committer: gvanrossum <gvanrossum at gmail.com>
date: 2022-12-08T13:31:27-08:00
summary:

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.

files:
M Python/bytecodes.c
M Python/generated_cases.c.h
M Tools/cases_generator/generate_cases.py
M Tools/cases_generator/parser.py

diff --git a/Python/bytecodes.c b/Python/bytecodes.c
index d0480ac01eb6..7a1bfdcc9e26 100644
--- a/Python/bytecodes.c
+++ b/Python/bytecodes.c
@@ -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 };
diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h
index 0805386866b3..510b6c4a75b8 100644
--- a/Python/generated_cases.c.h
+++ b/Python/generated_cases.c.h
@@ -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);
diff --git a/Tools/cases_generator/generate_cases.py b/Tools/cases_generator/generate_cases.py
index 2952634a3cda..844272291245 100644
--- a/Tools/cases_generator/generate_cases.py
+++ b/Tools/cases_generator/generate_cases.py
@@ -13,6 +13,7 @@
 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 @@
 )
 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 @@ def block(self, head: str):
             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 @@ def __init__(self, inst: parser.InstDef):
         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 @@ def write(self, out: Formatter) -> None:
                     )
 
         # 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 @@ def write_body(self, out: Formatter, dedent: int, cache_adjust: int = 0) -> None
                     # 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 @@ def write_body(self, out: Formatter, dedent: int, cache_adjust: int = 0) -> None
                     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};")
-            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};")
+            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)
 
-# 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 @@ def analyze(self) -> None:
     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 @@ def analyze_super(self, super: parser.Super) -> SuperInstruction:
         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 @@ def analyze_macro(self, macro: parser.Macro) -> MacroInstruction:
                 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 @@ def check_macro_components(
 
     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 @@ def stack_analysis(
         # 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 @@ def write_instructions(self) -> None:
                     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 @@ def write_super(self, sup: SuperInstruction) -> None:
             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 @@ def write_macro(self, mac: MacroInstruction) -> None:
                         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
diff --git a/Tools/cases_generator/parser.py b/Tools/cases_generator/parser.py
index 02a7834d2215..d802c733dfd1 100644
--- a/Tools/cases_generator/parser.py
+++ b/Tools/cases_generator/parser.py
@@ -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 @@ def inst_header(self) -> InstHeader | None:
             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 @@ def inst_header(self) -> InstHeader | None:
                     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 @@ def inputs(self) -> list[InputEffect] | None:
 
     @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 @@ def outputs(self) -> list[OutputEffect] | None:
 
     @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):
+            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):
-            return StackEffect(tkn.text)
+            type = ""
+            if self.expect(lx.COLON):
+                type = self.require(lx.IDENTIFIER).text
+            return StackEffect(tkn.text, type)
 
     @contextual
     def super_def(self) -> Super | None:



More information about the Python-checkins mailing list