]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
GH-131498: Cases generator: Parse down to C statement level. (GH-131948)
authorMark Shannon <mark@hotpy.org>
Wed, 2 Apr 2025 15:31:59 +0000 (16:31 +0100)
committerGitHub <noreply@github.com>
Wed, 2 Apr 2025 15:31:59 +0000 (16:31 +0100)
* Parse down to statement level in the cases generator

* Add handling for #if macros, treating them much like normal ifs.

16 files changed:
Lib/test/test_generated_cases.py
Python/bytecodes.c
Python/executor_cases.c.h
Python/generated_cases.c.h
Python/optimizer_bytecodes.c
Python/optimizer_cases.c.h
Tools/cases_generator/analyzer.py
Tools/cases_generator/cwriter.py
Tools/cases_generator/generators_common.py
Tools/cases_generator/lexer.py
Tools/cases_generator/optimizer_generator.py
Tools/cases_generator/parser.py
Tools/cases_generator/parsing.py
Tools/cases_generator/plexer.py
Tools/cases_generator/tier1_generator.py
Tools/cases_generator/tier2_generator.py

index 3f396e4da3ef7d86444c504951b4ded5850b2b35..302e69de285ca86b9d004f045e2a68057c580ce4 100644 (file)
@@ -457,7 +457,6 @@ class TestGeneratedCases(unittest.TestCase):
             if (cond) {
                 JUMP_TO_LABEL(label);
             }
-            // Comment is ok
             DISPATCH();
         }
     """
@@ -586,7 +585,6 @@ class TestGeneratedCases(unittest.TestCase):
 
         LABEL(somewhere)
         {
-
         }
     """
         self.run_cases_test(input, output)
@@ -1351,7 +1349,6 @@ class TestGeneratedCases(unittest.TestCase):
             }
             // THIRD
             {
-                // Mark j and k as used
                 if (cond) {
                     JUMP_TO_LABEL(pop_2_error);
                 }
@@ -1757,17 +1754,14 @@ class TestGeneratedCases(unittest.TestCase):
         output = """
         LABEL(other_label)
         {
-
         }
 
         LABEL(other_label2)
         {
-
         }
 
         LABEL(my_label)
         {
-            // Comment
             _PyFrame_SetStackPointer(frame, stack_pointer);
             do_thing();
             stack_pointer = _PyFrame_GetStackPointer(frame);
@@ -1795,7 +1789,6 @@ class TestGeneratedCases(unittest.TestCase):
         output = """
         LABEL(one)
         {
-            /* STACK SPILLED */
             stack_pointer = _PyFrame_GetStackPointer(frame);
             JUMP_TO_LABEL(two);
         }
@@ -1851,7 +1844,6 @@ class TestGeneratedCases(unittest.TestCase):
         output = """
         LABEL(my_label_1)
         {
-            // Comment
             _PyFrame_SetStackPointer(frame, stack_pointer);
             do_thing1();
             stack_pointer = _PyFrame_GetStackPointer(frame);
@@ -1860,7 +1852,6 @@ class TestGeneratedCases(unittest.TestCase):
 
         LABEL(my_label_2)
         {
-            // Comment
             _PyFrame_SetStackPointer(frame, stack_pointer);
             do_thing2();
             stack_pointer = _PyFrame_GetStackPointer(frame);
index 8d7e137c82943a5062a6b46aef03b35852929d83..b2900ba951a52fb141574c45aeb1a50e71b653ac 100644 (file)
@@ -163,7 +163,7 @@ dummy_func(
         op(_CHECK_PERIODIC_IF_NOT_YIELD_FROM, (--)) {
             if ((oparg & RESUME_OPARG_LOCATION_MASK) < RESUME_AFTER_YIELD_FROM) {
                 _Py_CHECK_EMSCRIPTEN_SIGNALS_PERIODICALLY();
-                QSBR_QUIESCENT_STATE(tstate); \
+                QSBR_QUIESCENT_STATE(tstate);
                 if (_Py_atomic_load_uintptr_relaxed(&tstate->eval_breaker) & _PY_EVAL_EVENTS_MASK) {
                     int err = _Py_HandlePending(tstate);
                     ERROR_IF(err != 0, error);
@@ -2245,7 +2245,8 @@ dummy_func(
             PyObject *attr_o = FT_ATOMIC_LOAD_PTR_ACQUIRE(*value_ptr);
             DEOPT_IF(attr_o == NULL);
             #ifdef Py_GIL_DISABLED
-            if (!_Py_TryIncrefCompareStackRef(value_ptr, attr_o, &attr)) {
+            int increfed = _Py_TryIncrefCompareStackRef(value_ptr, attr_o, &attr);
+            if (!increfed) {
                 DEOPT_IF(true);
             }
             #else
@@ -2322,7 +2323,8 @@ dummy_func(
             }
             STAT_INC(LOAD_ATTR, hit);
 #ifdef Py_GIL_DISABLED
-            if (!_Py_TryIncrefCompareStackRef(&ep->me_value, attr_o, &attr)) {
+            int increfed = _Py_TryIncrefCompareStackRef(&ep->me_value, attr_o, &attr);
+            if (!increfed) {
                 DEOPT_IF(true);
             }
 #else
index ca64d7557e33d554a73a8395329e0d7d4a189a76..ccdf74a575baa25de2b39417dee4dd4b8e203601 100644 (file)
@@ -30,7 +30,7 @@
             oparg = CURRENT_OPARG();
             if ((oparg & RESUME_OPARG_LOCATION_MASK) < RESUME_AFTER_YIELD_FROM) {
                 _Py_CHECK_EMSCRIPTEN_SIGNALS_PERIODICALLY();
-                QSBR_QUIESCENT_STATE(tstate); \
+                QSBR_QUIESCENT_STATE(tstate);
                 if (_Py_atomic_load_uintptr_relaxed(&tstate->eval_breaker) & _PY_EVAL_EVENTS_MASK) {
                     _PyFrame_SetStackPointer(frame, stack_pointer);
                     int err = _Py_HandlePending(tstate);
         case _END_FOR: {
             _PyStackRef value;
             value = stack_pointer[-1];
-            /* Don't update instr_ptr, so that POP_ITER sees
-             * the FOR_ITER as the previous instruction.
-             * This has the benign side effect that if value is
-             * finalized it will see the location as the FOR_ITER's.
-             */
             stack_pointer += -1;
             assert(WITHIN_STACK_BOUNDS());
             _PyFrame_SetStackPointer(frame, stack_pointer);
             _PyStackRef value;
             _PyStackRef res;
             value = stack_pointer[-1];
-            // This one is a bit weird, because we expect *some* failures:
             if (!PyStackRef_IsNone(value)) {
                 UOP_STAT_INC(uopcode, miss);
                 JUMP_TO_JUMP_TARGET();
                 JUMP_TO_JUMP_TARGET();
             }
             STAT_INC(BINARY_OP, hit);
-            /* Handle `left = left + right` or `left += right` for str.
-             *
-             * When possible, extend `left` in place rather than
-             * allocating a new PyUnicodeObject. This attempts to avoid
-             * quadratic behavior when one neglects to use str.join().
-             *
-             * If `left` has only two references remaining (one from
-             * the stack, one in the locals), DECREFing `left` leaves
-             * only the locals reference, so PyUnicode_Append knows
-             * that the string is safe to mutate.
-             */
             assert(Py_REFCNT(left_o) >= 2 || !PyStackRef_IsHeapSafe(left));
             PyStackRef_CLOSE_SPECIALIZED(left, _PyUnicode_ExactDealloc);
             PyObject *temp = PyStackRef_AsPyObjectSteal(*target_local);
                 JUMP_TO_ERROR();
             }
             #if TIER_ONE
-            // The STORE_FAST is already done. This is done here in tier one,
-            // and during trace projection in tier two:
+
             assert(next_instr->op.code == STORE_FAST);
             SKIP_OVER(1);
             #endif
                 PyStackRef_AsPyObjectSteal(stop));
             stack_pointer = _PyFrame_GetStackPointer(frame);
             PyObject *res_o;
-            // Can't use ERROR_IF() here, because we haven't
-            // DECREF'ed container yet, and we still own slice.
             if (slice == NULL) {
                 res_o = NULL;
             }
                 UOP_STAT_INC(uopcode, miss);
                 JUMP_TO_JUMP_TARGET();
             }
-            // Deopt unless 0 <= sub < PyList_Size(list)
             if (!_PyLong_IsNonNegativeCompact((PyLongObject *)sub)) {
                 UOP_STAT_INC(uopcode, miss);
                 JUMP_TO_JUMP_TARGET();
                 UOP_STAT_INC(uopcode, miss);
                 JUMP_TO_JUMP_TARGET();
             }
-            // Specialize for reading an ASCII character from any string:
             Py_UCS4 c = PyUnicode_READ_CHAR(str, index);
             if (Py_ARRAY_LENGTH(_Py_SINGLETON(strings).ascii) <= c) {
                 UOP_STAT_INC(uopcode, miss);
                 UOP_STAT_INC(uopcode, miss);
                 JUMP_TO_JUMP_TARGET();
             }
-            // Deopt unless 0 <= sub < PyTuple_Size(list)
             if (!_PyLong_IsNonNegativeCompact((PyLongObject *)sub)) {
                 UOP_STAT_INC(uopcode, miss);
                 JUMP_TO_JUMP_TARGET();
             if (rc <= 0) {
                 JUMP_TO_ERROR();
             }
-            // not found or error
             res = PyStackRef_FromPyObjectSteal(res_o);
             stack_pointer[0] = res;
             stack_pointer += 1;
             sub = stack_pointer[-1];
             container = stack_pointer[-2];
             v = stack_pointer[-3];
-            /* container[sub] = v */
             _PyFrame_SetStackPointer(frame, stack_pointer);
             int err = PyObject_SetItem(PyStackRef_AsPyObjectBorrow(container), PyStackRef_AsPyObjectBorrow(sub), PyStackRef_AsPyObjectBorrow(v));
             _PyStackRef tmp = sub;
                 UOP_STAT_INC(uopcode, miss);
                 JUMP_TO_JUMP_TARGET();
             }
-            // Ensure nonnegative, zero-or-one-digit ints.
             if (!_PyLong_IsNonNegativeCompact((PyLongObject *)sub)) {
                 UOP_STAT_INC(uopcode, miss);
                 JUMP_TO_JUMP_TARGET();
                 UOP_STAT_INC(uopcode, miss);
                 JUMP_TO_JUMP_TARGET();
             }
-            // Ensure index < len(list)
             if (index >= PyList_GET_SIZE(list)) {
                 UNLOCK_OBJECT(list);
                 if (true) {
             FT_ATOMIC_STORE_PTR_RELEASE(_PyList_ITEMS(list)[index],
                                         PyStackRef_AsPyObjectSteal(value));
             assert(old_value != NULL);
-            UNLOCK_OBJECT(list);  // unlock before decrefs!
+            UNLOCK_OBJECT(list);
             PyStackRef_CLOSE_SPECIALIZED(sub_st, _PyLong_ExactDealloc);
             stack_pointer += -3;
             assert(WITHIN_STACK_BOUNDS());
             _PyStackRef container;
             sub = stack_pointer[-1];
             container = stack_pointer[-2];
-            /* del container[sub] */
             _PyFrame_SetStackPointer(frame, stack_pointer);
             int err = PyObject_DelItem(PyStackRef_AsPyObjectBorrow(container),
                                        PyStackRef_AsPyObjectBorrow(sub));
             _PyFrame_SetStackPointer(frame, stack_pointer);
             assert(EMPTY());
             _Py_LeaveRecursiveCallPy(tstate);
-            // GH-99729: We need to unlink the frame *before* clearing it:
             _PyInterpreterFrame *dying = frame;
             frame = tstate->current_frame = dying->previous;
             _PyEval_FrameClearAndPop(tstate, dying);
             _PyStackRef value;
             oparg = CURRENT_OPARG();
             retval = stack_pointer[-1];
-            // NOTE: It's important that YIELD_VALUE never raises an exception!
-            // The compiler treats any exception raised here as a failed close()
-            // or throw() call.
             assert(frame->owner != FRAME_OWNED_BY_INTERPRETER);
             frame->instr_ptr++;
             PyGenObject *gen = _PyGen_GetGeneratorFromFrame(frame);
             _PyInterpreterFrame *gen_frame = frame;
             frame = tstate->current_frame = frame->previous;
             gen_frame->previous = NULL;
-            /* We don't know which of these is relevant here, so keep them equal */
             assert(INLINE_CACHE_ENTRIES_SEND == INLINE_CACHE_ENTRIES_FOR_ITER);
             #if TIER_ONE
             assert(frame->instr_ptr->op.code == INSTRUMENTED_LINE ||
         case _LOAD_COMMON_CONSTANT: {
             _PyStackRef value;
             oparg = CURRENT_OPARG();
-            // Keep in sync with _common_constants in opcode.py
             assert(oparg < NUM_COMMON_CONSTANTS);
             value = PyStackRef_FromPyObjectNew(tstate->interp->common_consts[oparg]);
             stack_pointer[0] = value;
             _PyFrame_SetStackPointer(frame, stack_pointer);
             err = PyObject_DelItem(ns, name);
             stack_pointer = _PyFrame_GetStackPointer(frame);
-            // Can't use ERROR_IF here.
             if (err != 0) {
                 _PyFrame_SetStackPointer(frame, stack_pointer);
                 _PyEval_FormatExcCheckArg(tstate, PyExc_NameError,
             _PyFrame_SetStackPointer(frame, stack_pointer);
             int err = PyDict_Pop(GLOBALS(), name, NULL);
             stack_pointer = _PyFrame_GetStackPointer(frame);
-            // Can't use ERROR_IF here.
             if (err < 0) {
                 JUMP_TO_ERROR();
             }
 
         case _MAKE_CELL: {
             oparg = CURRENT_OPARG();
-            // "initial" is probably NULL but not if it's an arg (or set
-            // via the f_locals proxy before MAKE_CELL has run).
             PyObject *initial = PyStackRef_AsPyObjectBorrow(GETLOCAL(oparg));
             PyObject *cell = PyCell_New(initial);
             if (cell == NULL) {
         case _DELETE_DEREF: {
             oparg = CURRENT_OPARG();
             PyObject *cell = PyStackRef_AsPyObjectBorrow(GETLOCAL(oparg));
-            // Can't use ERROR_IF here.
-            // Fortunately we don't need its superpower.
             PyObject *oldobj = PyCell_SwapTakeRef((PyCellObject *)cell, NULL);
             if (oldobj == NULL) {
                 _PyFrame_SetStackPointer(frame, stack_pointer);
 
         case _COPY_FREE_VARS: {
             oparg = CURRENT_OPARG();
-            /* Copy closure variables to free variables */
             PyCodeObject *co = _PyFrame_GetCode(frame);
             assert(PyStackRef_FunctionCheck(frame->f_funcobj));
             PyFunctionObject *func = (PyFunctionObject *)PyStackRef_AsPyObjectBorrow(frame->f_funcobj);
                 stack_pointer = _PyFrame_GetStackPointer(frame);
                 JUMP_TO_ERROR();
             }
-            /* check if __annotations__ in locals()... */
             _PyFrame_SetStackPointer(frame, stack_pointer);
             int err = PyMapping_GetOptionalItem(LOCALS(), &_Py_ID(__annotations__), &ann_dict);
             stack_pointer = _PyFrame_GetStackPointer(frame);
             dict_st = stack_pointer[-3 - (oparg - 1)];
             PyObject *dict = PyStackRef_AsPyObjectBorrow(dict_st);
             assert(PyDict_CheckExact(dict));
-            /* dict[key] = value */
-            // Do not DECREF INPUTS because the function steals the references
             _PyFrame_SetStackPointer(frame, stack_pointer);
             int err = _PyDict_SetItem_Take2(
                 (PyDictObject *)dict,
                 JUMP_TO_ERROR();
             }
             if (method_found) {
-                self_or_null = self_st; // transfer ownership
+                self_or_null = self_st;
             } else {
                 stack_pointer += -1;
                 assert(WITHIN_STACK_BOUNDS());
             PyObject *name = GETITEM(FRAME_CO_NAMES, oparg >> 1);
             PyObject *attr_o;
             if (oparg & 1) {
-                /* Designed to work in tandem with CALL, pushes two values. */
                 attr_o = NULL;
                 _PyFrame_SetStackPointer(frame, stack_pointer);
                 int is_meth = _PyObject_GetMethod(PyStackRef_AsPyObjectBorrow(owner), name, &attr_o);
                 stack_pointer = _PyFrame_GetStackPointer(frame);
                 if (is_meth) {
-                    /* We can bypass temporary bound method object.
-                       meth is unbound method and obj is self.
-                       meth | self | arg1 | ... | argN
-                     */
-                    assert(attr_o != NULL);  // No errors on this branch
-                    self_or_null[0] = owner;  // Transfer ownership
+                    assert(attr_o != NULL);
+                    self_or_null[0] = owner;
                 }
                 else {
-                    /* meth is not an unbound method (but a regular attr, or
-                       something was returned by a descriptor protocol).  Set
-                       the second element of the stack to NULL, to signal
-                       CALL that it's not a method call.
-                       meth | NULL | arg1 | ... | argN
-                     */
                     stack_pointer += -1;
                     assert(WITHIN_STACK_BOUNDS());
                     _PyFrame_SetStackPointer(frame, stack_pointer);
                 }
             }
             else {
-                /* Classic, pushes one value. */
                 _PyFrame_SetStackPointer(frame, stack_pointer);
                 attr_o = PyObject_GetAttr(PyStackRef_AsPyObjectBorrow(owner), name);
                 stack_pointer = _PyFrame_GetStackPointer(frame);
                 JUMP_TO_JUMP_TARGET();
             }
             #ifdef Py_GIL_DISABLED
-            if (!_Py_TryIncrefCompareStackRef(value_ptr, attr_o, &attr)) {
+            int increfed = _Py_TryIncrefCompareStackRef(value_ptr, attr_o, &attr);
+            if (!increfed) {
                 if (true) {
                     UOP_STAT_INC(uopcode, miss);
                     JUMP_TO_JUMP_TARGET();
             }
             STAT_INC(LOAD_ATTR, hit);
             #ifdef Py_GIL_DISABLED
-            if (!_Py_TryIncrefCompareStackRef(&ep->me_value, attr_o, &attr)) {
+            int increfed = _Py_TryIncrefCompareStackRef(&ep->me_value, attr_o, &attr);
+            if (!increfed) {
                 if (true) {
                     UOP_STAT_INC(uopcode, miss);
                     JUMP_TO_JUMP_TARGET();
             stack_pointer = _PyFrame_GetStackPointer(frame);
             FT_ATOMIC_STORE_PTR_RELEASE(ep->me_value, PyStackRef_AsPyObjectSteal(value));
             UNLOCK_OBJECT(dict);
-            // old_value should be DECREFed after GC track checking is done, if not, it could raise a segmentation fault,
-            // when dict only holds the strong reference to value in ep->me_value.
             STAT_INC(STORE_ATTR, hit);
             stack_pointer += -2;
             assert(WITHIN_STACK_BOUNDS());
             STAT_INC(COMPARE_OP, hit);
             double dleft = PyFloat_AS_DOUBLE(left_o);
             double dright = PyFloat_AS_DOUBLE(right_o);
-            // 1 if NaN, 2 if <, 4 if >, 8 if ==; this matches low four bits of the oparg
             int sign_ish = COMPARISON_BIT(dleft, dright);
             PyStackRef_CLOSE_SPECIALIZED(left, _PyFloat_ExactDealloc);
             PyStackRef_CLOSE_SPECIALIZED(right, _PyFloat_ExactDealloc);
             res = (sign_ish & oparg) ? PyStackRef_True : PyStackRef_False;
-            // It's always a bool, so we don't care about oparg & 16.
             stack_pointer[-2] = res;
             stack_pointer += -1;
             assert(WITHIN_STACK_BOUNDS());
                    _PyLong_DigitCount((PyLongObject *)right_o) <= 1);
             Py_ssize_t ileft = _PyLong_CompactValue((PyLongObject *)left_o);
             Py_ssize_t iright = _PyLong_CompactValue((PyLongObject *)right_o);
-            // 2 if <, 4 if >, 8 if ==; this matches the low 4 bits of the oparg
             int sign_ish = COMPARISON_BIT(ileft, iright);
             PyStackRef_CLOSE_SPECIALIZED(left, _PyLong_ExactDealloc);
             PyStackRef_CLOSE_SPECIALIZED(right, _PyLong_ExactDealloc);
             res =  (sign_ish & oparg) ? PyStackRef_True : PyStackRef_False;
-            // It's always a bool, so we don't care about oparg & 16.
             stack_pointer[-2] = res;
             stack_pointer += -1;
             assert(WITHIN_STACK_BOUNDS());
             assert((oparg & 0xf) == COMPARISON_NOT_EQUALS || (oparg & 0xf) == COMPARISON_EQUALS);
             assert(COMPARISON_NOT_EQUALS + 1 == COMPARISON_EQUALS);
             res = ((COMPARISON_NOT_EQUALS + eq) & oparg) ? PyStackRef_True : PyStackRef_False;
-            // It's always a bool, so we don't care about oparg & 16.
             stack_pointer[-2] = res;
             stack_pointer += -1;
             assert(WITHIN_STACK_BOUNDS());
                 JUMP_TO_JUMP_TARGET();
             }
             STAT_INC(CONTAINS_OP, hit);
-            // Note: both set and frozenset use the same seq_contains method!
             _PyFrame_SetStackPointer(frame, stack_pointer);
             int res = _PySet_Contains((PySetObject *)right_o, left_o);
             _PyStackRef tmp = right;
             _PyStackRef obj;
             _PyStackRef len;
             obj = stack_pointer[-1];
-            // PUSH(len(TOS))
             _PyFrame_SetStackPointer(frame, stack_pointer);
             Py_ssize_t len_i = PyObject_Length(PyStackRef_AsPyObjectBorrow(obj));
             stack_pointer = _PyFrame_GetStackPointer(frame);
             names = stack_pointer[-1];
             type = stack_pointer[-2];
             subject = stack_pointer[-3];
-            // Pop TOS and TOS1. Set TOS to a tuple of attributes on success, or
-            // None on failure.
             assert(PyTuple_CheckExact(PyStackRef_AsPyObjectBorrow(names)));
             _PyFrame_SetStackPointer(frame, stack_pointer);
             PyObject *attrs_o = _PyEval_MatchClass(tstate,
             stack_pointer += -3;
             assert(WITHIN_STACK_BOUNDS());
             if (attrs_o) {
-                assert(PyTuple_CheckExact(attrs_o));  // Success!
+                assert(PyTuple_CheckExact(attrs_o));
                 attrs = PyStackRef_FromPyObjectSteal(attrs_o);
             }
             else {
                 if (_PyErr_Occurred(tstate)) {
                     JUMP_TO_ERROR();
                 }
-                // Error!
-                attrs = PyStackRef_None;  // Failure!
+                attrs = PyStackRef_None;
             }
             stack_pointer[0] = attrs;
             stack_pointer += 1;
             _PyStackRef values_or_none;
             keys = stack_pointer[-1];
             subject = stack_pointer[-2];
-            // On successful match, PUSH(values). Otherwise, PUSH(None).
             _PyFrame_SetStackPointer(frame, stack_pointer);
             PyObject *values_or_none_o = _PyEval_MatchKeys(tstate,
                 PyStackRef_AsPyObjectBorrow(subject), PyStackRef_AsPyObjectBorrow(keys));
             _PyStackRef iterable;
             _PyStackRef iter;
             iterable = stack_pointer[-1];
-            /* before: [obj]; after [getiter(obj)] */
             _PyFrame_SetStackPointer(frame, stack_pointer);
             PyObject *iter_o = PyObject_GetIter(PyStackRef_AsPyObjectBorrow(iterable));
             stack_pointer = _PyFrame_GetStackPointer(frame);
             _PyStackRef iterable;
             _PyStackRef iter;
             iterable = stack_pointer[-1];
-            /* before: [obj]; after [getiter(obj)] */
             PyObject *iterable_o = PyStackRef_AsPyObjectBorrow(iterable);
             if (PyCoro_CheckExact(iterable_o)) {
-                /* `iterable` is a coroutine */
                 if (!(_PyFrame_GetCode(frame)->co_flags & (CO_COROUTINE | CO_ITERABLE_COROUTINE))) {
-                    /* and it is used in a 'yield from' expression of a
-                       regular generator. */
                     _PyFrame_SetStackPointer(frame, stack_pointer);
                     _PyErr_SetString(tstate, PyExc_TypeError,
                                      "cannot 'yield from' a coroutine object "
                 }
                 iter = iterable;
             }
+            else if (PyGen_CheckExact(iterable_o)) {
+                iter = iterable;
+            }
             else {
-                if (PyGen_CheckExact(iterable_o)) {
-                    iter = iterable;
-                }
-                else {
-                    /* `iterable` is not a generator. */
-                    _PyFrame_SetStackPointer(frame, stack_pointer);
-                    PyObject *iter_o = PyObject_GetIter(iterable_o);
-                    stack_pointer = _PyFrame_GetStackPointer(frame);
-                    if (iter_o == NULL) {
-                        JUMP_TO_ERROR();
-                    }
-                    iter = PyStackRef_FromPyObjectSteal(iter_o);
-                    _PyFrame_SetStackPointer(frame, stack_pointer);
-                    _PyStackRef tmp = iterable;
-                    iterable = iter;
-                    stack_pointer[-1] = iterable;
-                    PyStackRef_CLOSE(tmp);
-                    stack_pointer = _PyFrame_GetStackPointer(frame);
+                _PyFrame_SetStackPointer(frame, stack_pointer);
+                PyObject *iter_o = PyObject_GetIter(iterable_o);
+                stack_pointer = _PyFrame_GetStackPointer(frame);
+                if (iter_o == NULL) {
+                    JUMP_TO_ERROR();
                 }
+                iter = PyStackRef_FromPyObjectSteal(iter_o);
+                _PyFrame_SetStackPointer(frame, stack_pointer);
+                _PyStackRef tmp = iterable;
+                iterable = iter;
+                stack_pointer[-1] = iterable;
+                PyStackRef_CLOSE(tmp);
+                stack_pointer = _PyFrame_GetStackPointer(frame);
             }
             stack_pointer[-1] = iter;
             break;
             _PyStackRef iter;
             _PyStackRef next;
             iter = stack_pointer[-1];
-            /* before: [iter]; after: [iter, iter()] *or* [] (and jump over END_FOR.) */
             PyObject *iter_o = PyStackRef_AsPyObjectBorrow(iter);
             _PyFrame_SetStackPointer(frame, stack_pointer);
             PyObject *next_o = (*Py_TYPE(iter_o)->tp_iternext)(iter_o);
                     _PyErr_Clear(tstate);
                     stack_pointer = _PyFrame_GetStackPointer(frame);
                 }
-                /* iterator ended normally */
-                /* The translator sets the deopt target just past the matching END_FOR */
                 if (true) {
                     UOP_STAT_INC(uopcode, miss);
                     JUMP_TO_JUMP_TARGET();
                 }
             }
             next = PyStackRef_FromPyObjectSteal(next_o);
-            // Common case: no jump, leave it to the code generator
             stack_pointer[0] = next;
             stack_pointer += 1;
             assert(WITHIN_STACK_BOUNDS());
             _PyFrame_SetStackPointer(frame, stack_pointer);
             int result = _PyList_GetItemRefNoLock(seq, it->it_index, &next);
             stack_pointer = _PyFrame_GetStackPointer(frame);
-            // A negative result means we lost a race with another thread
-            // and we need to take the slow path.
             if (result < 0) {
                 UOP_STAT_INC(uopcode, miss);
                 JUMP_TO_JUMP_TARGET();
                 JUMP_TO_JUMP_TARGET();
             }
             #ifdef Py_GIL_DISABLED
-            // Since generators can't be used by multiple threads anyway we
-            // don't need to deopt here, but this lets us work on making
-            // generators thread-safe without necessarily having to
-            // specialize them thread-safely as well.
+
             if (!_PyObject_IsUniquelyReferenced((PyObject *)gen)) {
                 UOP_STAT_INC(uopcode, miss);
                 JUMP_TO_JUMP_TARGET();
             gen->gi_exc_state.previous_item = tstate->exc_info;
             tstate->exc_info = &gen->gi_exc_state;
             gen_frame->previous = frame;
-            // oparg is the return offset from the next instruction.
             frame->return_offset = (uint16_t)( 2 + oparg);
             stack_pointer[0].bits = (uintptr_t)gen_frame;
             stack_pointer += 1;
             lasti = stack_pointer[-3];
             exit_self = stack_pointer[-4];
             exit_func = stack_pointer[-5];
-            /* At the top of the stack are 4 values:
-               - val: TOP = exc_info()
-               - unused: SECOND = previous exception
-               - lasti: THIRD = lasti of exception in exc_info()
-               - exit_self: FOURTH = the context or NULL
-               - exit_func: FIFTH = the context.__exit__ function or context.__exit__ bound method
-               We call FOURTH(type(TOP), TOP, GetTraceback(TOP)).
-               Then we push the __exit__ return value.
-             */
             PyObject *exc, *tb;
             PyObject *val_o = PyStackRef_AsPyObjectBorrow(val);
             PyObject *exit_func_o = PyStackRef_AsPyObjectBorrow(exit_func);
                 tb = Py_None;
             }
             assert(PyStackRef_LongCheck(lasti));
-            (void)lasti; // Shut up compiler warning if asserts are off
+            (void)lasti;
             PyObject *stack[5] = {NULL, PyStackRef_AsPyObjectBorrow(exit_self), exc, val_o, tb};
             int has_self = !PyStackRef_IsNull(exit_self);
             _PyFrame_SetStackPointer(frame, stack_pointer);
             owner = stack_pointer[-1];
             PyObject *descr = (PyObject *)CURRENT_OPERAND0();
             assert(oparg & 1);
-            /* Cached method object */
             STAT_INC(LOAD_ATTR, hit);
             assert(descr != NULL);
             assert(_PyType_HasFeature(Py_TYPE(descr), Py_TPFLAGS_METHOD_DESCRIPTOR));
             uint16_t dictoffset = (uint16_t)CURRENT_OPERAND0();
             char *ptr = ((char *)PyStackRef_AsPyObjectBorrow(owner)) + MANAGED_DICT_OFFSET + dictoffset;
             PyObject *dict = FT_ATOMIC_LOAD_PTR_ACQUIRE(*(PyObject **)ptr);
-            /* This object has a __dict__, just not yet created */
             if (dict != NULL) {
                 UOP_STAT_INC(uopcode, miss);
                 JUMP_TO_JUMP_TARGET();
             self_or_null = &stack_pointer[-1 - oparg];
             callable = &stack_pointer[-2 - oparg];
             PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]);
-            // oparg counts all of the args, but *not* self:
             int total_args = oparg;
             if (!PyStackRef_IsNull(self_or_null[0])) {
                 args--;
                 args, total_args, NULL, frame
             );
             stack_pointer = _PyFrame_GetStackPointer(frame);
-            // The frame has stolen all the arguments from the stack.
             stack_pointer += -2 - oparg;
             assert(WITHIN_STACK_BOUNDS());
             if (temp == NULL) {
                 arguments--;
                 total_args++;
             }
-            /* Callable is not a normal Python function */
             STACKREFS_TO_PYOBJECTS(arguments, total_args, args_o);
             if (CONVERSION_FAILED(args_o)) {
                 _PyFrame_SetStackPointer(frame, stack_pointer);
         case _PUSH_FRAME: {
             _PyInterpreterFrame *new_frame;
             new_frame = (_PyInterpreterFrame *)stack_pointer[-1].bits;
-            // Write it out explicitly because it's subtly different.
-            // Eventually this should be the only occurrence of this code.
             assert(tstate->interp->eval_frame == NULL);
             _PyInterpreterFrame *temp = new_frame;
             stack_pointer += -1;
             stack_pointer = _PyFrame_GetStackPointer(frame);
             assert(_PyFrame_GetBytecode(shim)[0].op.code == EXIT_INIT_CHECK);
             assert(_PyFrame_GetBytecode(shim)[1].op.code == RETURN_VALUE);
-            /* Push self onto stack of shim */
             shim->localsplus[0] = PyStackRef_DUP(self[0]);
             _PyFrame_SetStackPointer(frame, stack_pointer);
             _PyInterpreterFrame *temp = _PyEvalFramePushAndInit(
             }
             init_frame = temp;
             frame->return_offset = 1 + INLINE_CACHE_ENTRIES_CALL;
-            /* Account for pushing the extra frame.
-             * We don't check recursion depth here,
-             * as it will be checked after start_frame */
             tstate->py_recursion_remaining--;
             stack_pointer[0].bits = (uintptr_t)init_frame;
             stack_pointer += 1;
             args = &stack_pointer[-oparg];
             self_or_null = &stack_pointer[-1 - oparg];
             callable = &stack_pointer[-2 - oparg];
-            /* Builtin METH_O functions */
             PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]);
             int total_args = oparg;
             if (!PyStackRef_IsNull(self_or_null[0])) {
                 UOP_STAT_INC(uopcode, miss);
                 JUMP_TO_JUMP_TARGET();
             }
-            // CPython promises to check all non-vectorcall function calls.
             if (_Py_ReachedRecursionLimit(tstate)) {
                 UOP_STAT_INC(uopcode, miss);
                 JUMP_TO_JUMP_TARGET();
             args = &stack_pointer[-oparg];
             self_or_null = &stack_pointer[-1 - oparg];
             callable = &stack_pointer[-2 - oparg];
-            /* Builtin METH_FASTCALL functions, without keywords */
             PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]);
             int total_args = oparg;
             _PyStackRef *arguments = args;
             }
             STAT_INC(CALL, hit);
             PyCFunction cfunc = PyCFunction_GET_FUNCTION(callable_o);
-            /* res = func(self, args, nargs) */
             STACKREFS_TO_PYOBJECTS(arguments, total_args, args_o);
             if (CONVERSION_FAILED(args_o)) {
                 _PyFrame_SetStackPointer(frame, stack_pointer);
             args = &stack_pointer[-oparg];
             self_or_null = &stack_pointer[-1 - oparg];
             callable = &stack_pointer[-2 - oparg];
-            /* Builtin METH_FASTCALL | METH_KEYWORDS functions */
             PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]);
             int total_args = oparg;
             _PyStackRef *arguments = args;
                 JUMP_TO_JUMP_TARGET();
             }
             STAT_INC(CALL, hit);
-            /* res = func(self, arguments, nargs, kwnames) */
             _PyFrame_SetStackPointer(frame, stack_pointer);
             PyCFunctionFastWithKeywords cfunc =
             (PyCFunctionFastWithKeywords)(void(*)(void))
             args = &stack_pointer[-oparg];
             self_or_null = &stack_pointer[-1 - oparg];
             callable = &stack_pointer[-2 - oparg];
-            /* len(o) */
             PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]);
             int total_args = oparg;
             if (!PyStackRef_IsNull(self_or_null[0])) {
             args = &stack_pointer[-oparg];
             self_or_null = &stack_pointer[-1 - oparg];
             callable = stack_pointer[-2 - oparg];
-            /* isinstance(o, o2) */
             PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable);
             int total_args = oparg;
             _PyStackRef *arguments = args;
                 JUMP_TO_ERROR();
             }
             #if TIER_ONE
-            // Skip the following POP_TOP. This is done here in tier one, and
-            // during trace projection in tier two:
+
             assert(next_instr->op.code == POP_TOP);
             SKIP_OVER(1);
             #endif
                 UOP_STAT_INC(uopcode, miss);
                 JUMP_TO_JUMP_TARGET();
             }
-            // CPython promises to check all non-vectorcall function calls.
             if (_Py_ReachedRecursionLimit(tstate)) {
                 UOP_STAT_INC(uopcode, miss);
                 JUMP_TO_JUMP_TARGET();
                 UOP_STAT_INC(uopcode, miss);
                 JUMP_TO_JUMP_TARGET();
             }
-            // CPython promises to check all non-vectorcall function calls.
             if (_Py_ReachedRecursionLimit(tstate)) {
                 UOP_STAT_INC(uopcode, miss);
                 JUMP_TO_JUMP_TARGET();
                 total_args++;
             }
             PyMethodDescrObject *method = (PyMethodDescrObject *)callable_o;
-            /* Builtin METH_FASTCALL methods, without keywords */
             if (!Py_IS_TYPE(method, &PyMethodDescr_Type)) {
                 UOP_STAT_INC(uopcode, miss);
                 JUMP_TO_JUMP_TARGET();
             self_or_null = &stack_pointer[-2 - oparg];
             callable = &stack_pointer[-3 - oparg];
             PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]);
-            // oparg counts all of the args, but *not* self:
             int total_args = oparg;
             _PyStackRef *arguments = args;
             if (!PyStackRef_IsNull(self_or_null[0])) {
             _PyFrame_SetStackPointer(frame, stack_pointer);
             PyStackRef_CLOSE(kwnames);
             stack_pointer = _PyFrame_GetStackPointer(frame);
-            // The frame has stolen all the arguments from the stack,
-            // so there is no need to clean them up.
             stack_pointer += -2 - oparg;
             assert(WITHIN_STACK_BOUNDS());
             if (temp == NULL) {
                 arguments--;
                 total_args++;
             }
-            /* Callable is not a normal Python function */
             STACKREFS_TO_PYOBJECTS(arguments, total_args, args_o);
             if (CONVERSION_FAILED(args_o)) {
                 _PyFrame_SetStackPointer(frame, stack_pointer);
             _PyStackRef res;
             value = stack_pointer[-1];
             PyObject *value_o = PyStackRef_AsPyObjectBorrow(value);
-            /* If value is a unicode object, then we know the result
-             * of format(value) is value itself. */
             if (!PyUnicode_CheckExact(value_o)) {
                 _PyFrame_SetStackPointer(frame, stack_pointer);
                 PyObject *res_o = PyObject_Format(value_o, NULL);
 
         case _MAKE_WARM: {
             current_executor->vm_data.warm = true;
-            // It's okay if this ends up going negative.
             if (--tstate->interp->trace_run_counter == 0) {
                 _Py_set_eval_breaker_bit(tstate, _PY_EVAL_JIT_INVALIDATE_COLD_BIT);
             }
index 41ea054d3f5eea5a3e20cedbe14d5be4eba3f604..c75371d12b0ba1bf9620cc7fb585afcf90836057 100644 (file)
                     JUMP_TO_PREDICTED(BINARY_OP);
                 }
                 STAT_INC(BINARY_OP, hit);
-                /* Handle `left = left + right` or `left += right` for str.
-                 *
-                 * When possible, extend `left` in place rather than
-                 * allocating a new PyUnicodeObject. This attempts to avoid
-                 * quadratic behavior when one neglects to use str.join().
-                 *
-                 * If `left` has only two references remaining (one from
-                 * the stack, one in the locals), DECREFing `left` leaves
-                 * only the locals reference, so PyUnicode_Append knows
-                 * that the string is safe to mutate.
-                 */
                 assert(Py_REFCNT(left_o) >= 2 || !PyStackRef_IsHeapSafe(left));
                 PyStackRef_CLOSE_SPECIALIZED(left, _PyUnicode_ExactDealloc);
                 PyObject *temp = PyStackRef_AsPyObjectSteal(*target_local);
                     JUMP_TO_LABEL(error);
                 }
                 #if TIER_ONE
-                // The STORE_FAST is already done. This is done here in tier one,
-                // and during trace projection in tier two:
+
                 assert(next_instr->op.code == STORE_FAST);
                 SKIP_OVER(1);
                 #endif
             if (rc <= 0) {
                 JUMP_TO_LABEL(error);
             }
-            // not found or error
             res = PyStackRef_FromPyObjectSteal(res_o);
             stack_pointer[0] = res;
             stack_pointer += 1;
             }
             // _PUSH_FRAME
             {
-                // Write it out explicitly because it's subtly different.
-                // Eventually this should be the only occurrence of this code.
                 assert(tstate->interp->eval_frame == NULL);
                 _PyInterpreterFrame *temp = new_frame;
                 stack_pointer += -2;
                     assert(_PyOpcode_Deopt[opcode] == (BINARY_OP));
                     JUMP_TO_PREDICTED(BINARY_OP);
                 }
-                // Deopt unless 0 <= sub < PyList_Size(list)
                 if (!_PyLong_IsNonNegativeCompact((PyLongObject *)sub)) {
                     UPDATE_MISS_STATS(BINARY_OP);
                     assert(_PyOpcode_Deopt[opcode] == (BINARY_OP));
                     assert(_PyOpcode_Deopt[opcode] == (BINARY_OP));
                     JUMP_TO_PREDICTED(BINARY_OP);
                 }
-                // Specialize for reading an ASCII character from any string:
                 Py_UCS4 c = PyUnicode_READ_CHAR(str, index);
                 if (Py_ARRAY_LENGTH(_Py_SINGLETON(strings).ascii) <= c) {
                     UPDATE_MISS_STATS(BINARY_OP);
                     assert(_PyOpcode_Deopt[opcode] == (BINARY_OP));
                     JUMP_TO_PREDICTED(BINARY_OP);
                 }
-                // Deopt unless 0 <= sub < PyTuple_Size(list)
                 if (!_PyLong_IsNonNegativeCompact((PyLongObject *)sub)) {
                     UPDATE_MISS_STATS(BINARY_OP);
                     assert(_PyOpcode_Deopt[opcode] == (BINARY_OP));
             _PyStackRef res;
             // _SPECIALIZE_BINARY_SLICE
             {
-                // Placeholder until we implement BINARY_SLICE specialization
                 #if ENABLE_SPECIALIZATION
                 OPCODE_DEFERRED_INC(BINARY_SLICE);
                 #endif  /* ENABLE_SPECIALIZATION */
                     PyStackRef_AsPyObjectSteal(stop));
                 stack_pointer = _PyFrame_GetStackPointer(frame);
                 PyObject *res_o;
-                // Can't use ERROR_IF() here, because we haven't
-                // DECREF'ed container yet, and we still own slice.
                 if (slice == NULL) {
                     res_o = NULL;
                 }
             {
                 args = &stack_pointer[-oparg];
                 PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]);
-                // oparg counts all of the args, but *not* self:
                 int total_args = oparg;
                 _PyStackRef *arguments = args;
                 if (!PyStackRef_IsNull(self_or_null[0])) {
                     arguments--;
                     total_args++;
                 }
-                // Check if the call can be inlined or not
                 if (Py_TYPE(callable_o) == &PyFunction_Type &&
                     tstate->interp->eval_frame == NULL &&
                     ((PyFunctionObject *)callable_o)->vectorcall == _PyFunction_Vectorcall)
                         arguments, total_args, NULL, frame
                     );
                     stack_pointer = _PyFrame_GetStackPointer(frame);
-                    // Manipulate stack directly since we leave using DISPATCH_INLINED().
                     stack_pointer += -2 - oparg;
                     assert(WITHIN_STACK_BOUNDS());
-                    // The frame has stolen all the arguments from the stack,
-                    // so there is no need to clean them up.
                     if (new_frame == NULL) {
                         JUMP_TO_LABEL(error);
                     }
                     frame->return_offset = 4 ;
                     DISPATCH_INLINED(new_frame);
                 }
-                /* Callable is not a normal Python function */
                 STACKREFS_TO_PYOBJECTS(arguments, total_args, args_o);
                 if (CONVERSION_FAILED(args_o)) {
                     _PyFrame_SetStackPointer(frame, stack_pointer);
                 stack_pointer = _PyFrame_GetStackPointer(frame);
                 assert(_PyFrame_GetBytecode(shim)[0].op.code == EXIT_INIT_CHECK);
                 assert(_PyFrame_GetBytecode(shim)[1].op.code == RETURN_VALUE);
-                /* Push self onto stack of shim */
                 shim->localsplus[0] = PyStackRef_DUP(self[0]);
                 _PyFrame_SetStackPointer(frame, stack_pointer);
                 _PyInterpreterFrame *temp = _PyEvalFramePushAndInit(
                 }
                 init_frame = temp;
                 frame->return_offset = 1 + INLINE_CACHE_ENTRIES_CALL;
-                /* Account for pushing the extra frame.
-                 * We don't check recursion depth here,
-                 * as it will be checked after start_frame */
                 tstate->py_recursion_remaining--;
             }
             // _PUSH_FRAME
             {
                 new_frame = init_frame;
-                // Write it out explicitly because it's subtly different.
-                // Eventually this should be the only occurrence of this code.
                 assert(tstate->interp->eval_frame == NULL);
                 _PyInterpreterFrame *temp = new_frame;
                 _PyFrame_SetStackPointer(frame, stack_pointer);
             }
             // _PUSH_FRAME
             {
-                // Write it out explicitly because it's subtly different.
-                // Eventually this should be the only occurrence of this code.
                 assert(tstate->interp->eval_frame == NULL);
                 _PyInterpreterFrame *temp = new_frame;
                 stack_pointer += -2 - oparg;
             {
                 args = &stack_pointer[-oparg];
                 PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]);
-                // oparg counts all of the args, but *not* self:
                 int total_args = oparg;
                 if (!PyStackRef_IsNull(self_or_null[0])) {
                     args--;
                     args, total_args, NULL, frame
                 );
                 stack_pointer = _PyFrame_GetStackPointer(frame);
-                // The frame has stolen all the arguments from the stack.
                 stack_pointer += -2 - oparg;
                 assert(WITHIN_STACK_BOUNDS());
                 if (temp == NULL) {
             }
             // _PUSH_FRAME
             {
-                // Write it out explicitly because it's subtly different.
-                // Eventually this should be the only occurrence of this code.
                 assert(tstate->interp->eval_frame == NULL);
                 _PyInterpreterFrame *temp = new_frame;
                 _PyFrame_SetStackPointer(frame, stack_pointer);
                 args = &stack_pointer[-oparg];
                 self_or_null = &stack_pointer[-1 - oparg];
                 callable = &stack_pointer[-2 - oparg];
-                /* Builtin METH_FASTCALL functions, without keywords */
                 PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]);
                 int total_args = oparg;
                 _PyStackRef *arguments = args;
                 }
                 STAT_INC(CALL, hit);
                 PyCFunction cfunc = PyCFunction_GET_FUNCTION(callable_o);
-                /* res = func(self, args, nargs) */
                 STACKREFS_TO_PYOBJECTS(arguments, total_args, args_o);
                 if (CONVERSION_FAILED(args_o)) {
                     _PyFrame_SetStackPointer(frame, stack_pointer);
                 args = &stack_pointer[-oparg];
                 self_or_null = &stack_pointer[-1 - oparg];
                 callable = &stack_pointer[-2 - oparg];
-                /* Builtin METH_FASTCALL | METH_KEYWORDS functions */
                 PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]);
                 int total_args = oparg;
                 _PyStackRef *arguments = args;
                     JUMP_TO_PREDICTED(CALL);
                 }
                 STAT_INC(CALL, hit);
-                /* res = func(self, arguments, nargs, kwnames) */
                 _PyFrame_SetStackPointer(frame, stack_pointer);
                 PyCFunctionFastWithKeywords cfunc =
                 (PyCFunctionFastWithKeywords)(void(*)(void))
                 args = &stack_pointer[-oparg];
                 self_or_null = &stack_pointer[-1 - oparg];
                 callable = &stack_pointer[-2 - oparg];
-                /* Builtin METH_O functions */
                 PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]);
                 int total_args = oparg;
                 if (!PyStackRef_IsNull(self_or_null[0])) {
                     assert(_PyOpcode_Deopt[opcode] == (CALL));
                     JUMP_TO_PREDICTED(CALL);
                 }
-                // CPython promises to check all non-vectorcall function calls.
                 if (_Py_ReachedRecursionLimit(tstate)) {
                     UPDATE_MISS_STATS(CALL);
                     assert(_PyOpcode_Deopt[opcode] == (CALL));
                 func_st = func;
                 (void)null;
                 PyObject *func = PyStackRef_AsPyObjectBorrow(func_st);
-                // DICT_MERGE is called before this opcode if there are kwargs.
-                // It converts all dict subtypes in kwargs into regular dicts.
                 EVAL_CALL_STAT_INC_IF_FUNCTION(EVAL_CALL_FUNCTION_EX, func);
                 PyObject *result_o;
                 assert(!_PyErr_Occurred(tstate));
                             tstate, func_st, locals,
                             nargs, callargs, kwargs, frame);
                         stack_pointer = _PyFrame_GetStackPointer(frame);
-                        // Need to sync the stack since we exit with DISPATCH_INLINED.
                         stack_pointer += -2;
                         assert(WITHIN_STACK_BOUNDS());
                         if (new_frame == NULL) {
             args = &stack_pointer[-oparg];
             self_or_null = &stack_pointer[-1 - oparg];
             callable = stack_pointer[-2 - oparg];
-            /* isinstance(o, o2) */
             PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable);
             int total_args = oparg;
             _PyStackRef *arguments = args;
                 args = &stack_pointer[-1 - oparg];
                 PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]);
                 PyObject *kwnames_o = PyStackRef_AsPyObjectBorrow(kwnames);
-                // oparg counts all of the args, but *not* self:
                 int total_args = oparg;
                 _PyStackRef *arguments = args;
                 if (!PyStackRef_IsNull(self_or_null[0])) {
                     total_args++;
                 }
                 int positional_args = total_args - (int)PyTuple_GET_SIZE(kwnames_o);
-                // Check if the call can be inlined or not
                 if (Py_TYPE(callable_o) == &PyFunction_Type &&
                     tstate->interp->eval_frame == NULL &&
                     ((PyFunctionObject *)callable_o)->vectorcall == _PyFunction_Vectorcall)
                     _PyFrame_SetStackPointer(frame, stack_pointer);
                     PyStackRef_CLOSE(kwnames);
                     stack_pointer = _PyFrame_GetStackPointer(frame);
-                    // Sync stack explicitly since we leave using DISPATCH_INLINED().
-                    // The frame has stolen all the arguments from the stack,
-                    // so there is no need to clean them up.
                     if (new_frame == NULL) {
                         JUMP_TO_LABEL(error);
                     }
                     frame->return_offset = 4 ;
                     DISPATCH_INLINED(new_frame);
                 }
-                /* Callable is not a normal Python function */
                 STACKREFS_TO_PYOBJECTS(arguments, total_args, args_o);
                 if (CONVERSION_FAILED(args_o)) {
                     stack_pointer[-1] = kwnames;
                 kwnames = stack_pointer[-1];
                 args = &stack_pointer[-1 - oparg];
                 PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]);
-                // oparg counts all of the args, but *not* self:
                 int total_args = oparg;
                 _PyStackRef *arguments = args;
                 if (!PyStackRef_IsNull(self_or_null[0])) {
                 _PyFrame_SetStackPointer(frame, stack_pointer);
                 PyStackRef_CLOSE(kwnames);
                 stack_pointer = _PyFrame_GetStackPointer(frame);
-                // The frame has stolen all the arguments from the stack,
-                // so there is no need to clean them up.
                 stack_pointer += -2 - oparg;
                 assert(WITHIN_STACK_BOUNDS());
                 if (temp == NULL) {
             }
             // _PUSH_FRAME
             {
-                // Write it out explicitly because it's subtly different.
-                // Eventually this should be the only occurrence of this code.
                 assert(tstate->interp->eval_frame == NULL);
                 _PyInterpreterFrame *temp = new_frame;
                 _PyFrame_SetStackPointer(frame, stack_pointer);
                     arguments--;
                     total_args++;
                 }
-                /* Callable is not a normal Python function */
                 STACKREFS_TO_PYOBJECTS(arguments, total_args, args_o);
                 if (CONVERSION_FAILED(args_o)) {
                     _PyFrame_SetStackPointer(frame, stack_pointer);
                 args = &stack_pointer[-1 - oparg];
                 self_or_null = &stack_pointer[-2 - oparg];
                 PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]);
-                // oparg counts all of the args, but *not* self:
                 int total_args = oparg;
                 _PyStackRef *arguments = args;
                 if (!PyStackRef_IsNull(self_or_null[0])) {
                 _PyFrame_SetStackPointer(frame, stack_pointer);
                 PyStackRef_CLOSE(kwnames);
                 stack_pointer = _PyFrame_GetStackPointer(frame);
-                // The frame has stolen all the arguments from the stack,
-                // so there is no need to clean them up.
                 stack_pointer += -2 - oparg;
                 assert(WITHIN_STACK_BOUNDS());
                 if (temp == NULL) {
             }
             // _PUSH_FRAME
             {
-                // Write it out explicitly because it's subtly different.
-                // Eventually this should be the only occurrence of this code.
                 assert(tstate->interp->eval_frame == NULL);
                 _PyInterpreterFrame *temp = new_frame;
                 _PyFrame_SetStackPointer(frame, stack_pointer);
             args = &stack_pointer[-oparg];
             self_or_null = &stack_pointer[-1 - oparg];
             callable = &stack_pointer[-2 - oparg];
-            /* len(o) */
             PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]);
             int total_args = oparg;
             if (!PyStackRef_IsNull(self_or_null[0])) {
                 JUMP_TO_LABEL(error);
             }
             #if TIER_ONE
-            // Skip the following POP_TOP. This is done here in tier one, and
-            // during trace projection in tier two:
+
             assert(next_instr->op.code == POP_TOP);
             SKIP_OVER(1);
             #endif
                     total_args++;
                 }
                 PyMethodDescrObject *method = (PyMethodDescrObject *)callable_o;
-                /* Builtin METH_FASTCALL methods, without keywords */
                 if (!Py_IS_TYPE(method, &PyMethodDescr_Type)) {
                     UPDATE_MISS_STATS(CALL);
                     assert(_PyOpcode_Deopt[opcode] == (CALL));
                     assert(_PyOpcode_Deopt[opcode] == (CALL));
                     JUMP_TO_PREDICTED(CALL);
                 }
-                // CPython promises to check all non-vectorcall function calls.
                 if (_Py_ReachedRecursionLimit(tstate)) {
                     UPDATE_MISS_STATS(CALL);
                     assert(_PyOpcode_Deopt[opcode] == (CALL));
                     assert(_PyOpcode_Deopt[opcode] == (CALL));
                     JUMP_TO_PREDICTED(CALL);
                 }
-                // CPython promises to check all non-vectorcall function calls.
                 if (_Py_ReachedRecursionLimit(tstate)) {
                     UPDATE_MISS_STATS(CALL);
                     assert(_PyOpcode_Deopt[opcode] == (CALL));
                     arguments--;
                     total_args++;
                 }
-                /* Callable is not a normal Python function */
                 STACKREFS_TO_PYOBJECTS(arguments, total_args, args_o);
                 if (CONVERSION_FAILED(args_o)) {
                     _PyFrame_SetStackPointer(frame, stack_pointer);
             }
             // _PUSH_FRAME
             {
-                // Write it out explicitly because it's subtly different.
-                // Eventually this should be the only occurrence of this code.
                 assert(tstate->interp->eval_frame == NULL);
                 _PyInterpreterFrame *temp = new_frame;
                 stack_pointer += -2 - oparg;
                 args = &stack_pointer[-oparg];
                 self_or_null = &stack_pointer[-1 - oparg];
                 PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]);
-                // oparg counts all of the args, but *not* self:
                 int total_args = oparg;
                 if (!PyStackRef_IsNull(self_or_null[0])) {
                     args--;
                     args, total_args, NULL, frame
                 );
                 stack_pointer = _PyFrame_GetStackPointer(frame);
-                // The frame has stolen all the arguments from the stack.
                 stack_pointer += -2 - oparg;
                 assert(WITHIN_STACK_BOUNDS());
                 if (temp == NULL) {
             }
             // _PUSH_FRAME
             {
-                // Write it out explicitly because it's subtly different.
-                // Eventually this should be the only occurrence of this code.
                 assert(tstate->interp->eval_frame == NULL);
                 _PyInterpreterFrame *temp = new_frame;
                 _PyFrame_SetStackPointer(frame, stack_pointer);
                 STAT_INC(COMPARE_OP, hit);
                 double dleft = PyFloat_AS_DOUBLE(left_o);
                 double dright = PyFloat_AS_DOUBLE(right_o);
-                // 1 if NaN, 2 if <, 4 if >, 8 if ==; this matches low four bits of the oparg
                 int sign_ish = COMPARISON_BIT(dleft, dright);
                 PyStackRef_CLOSE_SPECIALIZED(left, _PyFloat_ExactDealloc);
                 PyStackRef_CLOSE_SPECIALIZED(right, _PyFloat_ExactDealloc);
                 res = (sign_ish & oparg) ? PyStackRef_True : PyStackRef_False;
-                // It's always a bool, so we don't care about oparg & 16.
             }
             stack_pointer[-2] = res;
             stack_pointer += -1;
                    _PyLong_DigitCount((PyLongObject *)right_o) <= 1);
                 Py_ssize_t ileft = _PyLong_CompactValue((PyLongObject *)left_o);
                 Py_ssize_t iright = _PyLong_CompactValue((PyLongObject *)right_o);
-                // 2 if <, 4 if >, 8 if ==; this matches the low 4 bits of the oparg
                 int sign_ish = COMPARISON_BIT(ileft, iright);
                 PyStackRef_CLOSE_SPECIALIZED(left, _PyLong_ExactDealloc);
                 PyStackRef_CLOSE_SPECIALIZED(right, _PyLong_ExactDealloc);
                 res =  (sign_ish & oparg) ? PyStackRef_True : PyStackRef_False;
-                // It's always a bool, so we don't care about oparg & 16.
             }
             stack_pointer[-2] = res;
             stack_pointer += -1;
                 assert((oparg & 0xf) == COMPARISON_NOT_EQUALS || (oparg & 0xf) == COMPARISON_EQUALS);
                 assert(COMPARISON_NOT_EQUALS + 1 == COMPARISON_EQUALS);
                 res = ((COMPARISON_NOT_EQUALS + eq) & oparg) ? PyStackRef_True : PyStackRef_False;
-                // It's always a bool, so we don't care about oparg & 16.
             }
             stack_pointer[-2] = res;
             stack_pointer += -1;
                 JUMP_TO_PREDICTED(CONTAINS_OP);
             }
             STAT_INC(CONTAINS_OP, hit);
-            // Note: both set and frozenset use the same seq_contains method!
             _PyFrame_SetStackPointer(frame, stack_pointer);
             int res = _PySet_Contains((PySetObject *)right_o, left_o);
             _PyStackRef tmp = right;
             frame->instr_ptr = next_instr;
             next_instr += 1;
             INSTRUCTION_STATS(COPY_FREE_VARS);
-            /* Copy closure variables to free variables */
             PyCodeObject *co = _PyFrame_GetCode(frame);
             assert(PyStackRef_FunctionCheck(frame->f_funcobj));
             PyFunctionObject *func = (PyFunctionObject *)PyStackRef_AsPyObjectBorrow(frame->f_funcobj);
             next_instr += 1;
             INSTRUCTION_STATS(DELETE_DEREF);
             PyObject *cell = PyStackRef_AsPyObjectBorrow(GETLOCAL(oparg));
-            // Can't use ERROR_IF here.
-            // Fortunately we don't need its superpower.
             PyObject *oldobj = PyCell_SwapTakeRef((PyCellObject *)cell, NULL);
             if (oldobj == NULL) {
                 _PyFrame_SetStackPointer(frame, stack_pointer);
             _PyFrame_SetStackPointer(frame, stack_pointer);
             int err = PyDict_Pop(GLOBALS(), name, NULL);
             stack_pointer = _PyFrame_GetStackPointer(frame);
-            // Can't use ERROR_IF here.
             if (err < 0) {
                 JUMP_TO_LABEL(error);
             }
             _PyFrame_SetStackPointer(frame, stack_pointer);
             err = PyObject_DelItem(ns, name);
             stack_pointer = _PyFrame_GetStackPointer(frame);
-            // Can't use ERROR_IF here.
             if (err != 0) {
                 _PyFrame_SetStackPointer(frame, stack_pointer);
                 _PyEval_FormatExcCheckArg(tstate, PyExc_NameError,
             _PyStackRef sub;
             sub = stack_pointer[-1];
             container = stack_pointer[-2];
-            /* del container[sub] */
             _PyFrame_SetStackPointer(frame, stack_pointer);
             int err = PyObject_DelItem(PyStackRef_AsPyObjectBorrow(container),
                                        PyStackRef_AsPyObjectBorrow(sub));
             _PyStackRef exc_st;
             exc_st = stack_pointer[-1];
             awaitable_st = stack_pointer[-2];
-            JUMPBY(0); // Pretend jump as we need source offset for monitoring
+            JUMPBY(0);
             (void)oparg;
             PyObject *exc = PyStackRef_AsPyObjectBorrow(exc_st);
             assert(exc && PyExceptionInstance_Check(exc));
             INSTRUCTION_STATS(END_FOR);
             _PyStackRef value;
             value = stack_pointer[-1];
-            /* Don't update instr_ptr, so that POP_ITER sees
-             * the FOR_ITER as the previous instruction.
-             * This has the benign side effect that if value is
-             * finalized it will see the location as the FOR_ITER's.
-             */
             stack_pointer += -1;
             assert(WITHIN_STACK_BOUNDS());
             _PyFrame_SetStackPointer(frame, stack_pointer);
             assert(executor->vm_data.code == code);
             assert(executor->vm_data.valid);
             assert(tstate->previous_executor == NULL);
-            /* If the eval breaker is set then stay in tier 1.
-             * This avoids any potentially infinite loops
-             * involving _RESUME_CHECK */
             if (_Py_atomic_load_uintptr_relaxed(&tstate->eval_breaker) & _PY_EVAL_EVENTS_MASK) {
                 opcode = executor->vm_data.opcode;
                 oparg = (oparg & ~255) | executor->vm_data.oparg;
             _PyStackRef res;
             value = stack_pointer[-1];
             PyObject *value_o = PyStackRef_AsPyObjectBorrow(value);
-            /* If value is a unicode object, then we know the result
-             * of format(value) is value itself. */
             if (!PyUnicode_CheckExact(value_o)) {
                 _PyFrame_SetStackPointer(frame, stack_pointer);
                 PyObject *res_o = PyObject_Format(value_o, NULL);
             }
             // _FOR_ITER
             {
-                /* before: [iter]; after: [iter, iter()] *or* [] (and jump over END_FOR.) */
                 PyObject *iter_o = PyStackRef_AsPyObjectBorrow(iter);
                 _PyFrame_SetStackPointer(frame, stack_pointer);
                 PyObject *next_o = (*Py_TYPE(iter_o)->tp_iternext)(iter_o);
                         _PyErr_Clear(tstate);
                         stack_pointer = _PyFrame_GetStackPointer(frame);
                     }
-                    /* iterator ended normally */
                     assert(next_instr[oparg].op.code == END_FOR ||
                        next_instr[oparg].op.code == INSTRUMENTED_END_FOR);
-                    /* Jump forward oparg, then skip following END_FOR */
                     JUMPBY(oparg + 1);
                     DISPATCH();
                 }
                 next = PyStackRef_FromPyObjectSteal(next_o);
-                // Common case: no jump, leave it to the code generator
             }
             stack_pointer[0] = next;
             stack_pointer += 1;
                     JUMP_TO_PREDICTED(FOR_ITER);
                 }
                 #ifdef Py_GIL_DISABLED
-                // Since generators can't be used by multiple threads anyway we
-                // don't need to deopt here, but this lets us work on making
-                // generators thread-safe without necessarily having to
-                // specialize them thread-safely as well.
                 if (!_PyObject_IsUniquelyReferenced((PyObject *)gen)) {
                     UPDATE_MISS_STATS(FOR_ITER);
                     assert(_PyOpcode_Deopt[opcode] == (FOR_ITER));
                 gen->gi_exc_state.previous_item = tstate->exc_info;
                 tstate->exc_info = &gen->gi_exc_state;
                 gen_frame->previous = frame;
-                // oparg is the return offset from the next instruction.
                 frame->return_offset = (uint16_t)( 2 + oparg);
             }
             // _PUSH_FRAME
             {
                 new_frame = gen_frame;
-                // Write it out explicitly because it's subtly different.
-                // Eventually this should be the only occurrence of this code.
                 assert(tstate->interp->eval_frame == NULL);
                 _PyInterpreterFrame *temp = new_frame;
                 _PyFrame_SetStackPointer(frame, stack_pointer);
             {
                 PyObject *iter_o = PyStackRef_AsPyObjectBorrow(iter);
                 assert(Py_TYPE(iter_o) == &PyListIter_Type);
-                // For free-threaded Python, the loop exit can happen at any point during
-                // item retrieval, so it doesn't make much sense to check and jump
-                // separately before item retrieval. Any length check we do here can be
-                // invalid by the time we actually try to fetch the item.
                 #ifdef Py_GIL_DISABLED
                 assert(_PyObject_IsUniquelyReferenced(iter_o));
                 (void)iter_o;
                         Py_DECREF(seq);
                         stack_pointer = _PyFrame_GetStackPointer(frame);
                     }
-                    /* Jump forward oparg, then skip following END_FOR instruction */
                     JUMPBY(oparg + 1);
                     DISPATCH();
                 }
                 _PyFrame_SetStackPointer(frame, stack_pointer);
                 int result = _PyList_GetItemRefNoLock(seq, it->it_index, &next);
                 stack_pointer = _PyFrame_GetStackPointer(frame);
-                // A negative result means we lost a race with another thread
-                // and we need to take the slow path.
                 if (result < 0) {
                     UPDATE_MISS_STATS(FOR_ITER);
                     assert(_PyOpcode_Deopt[opcode] == (FOR_ITER));
                 }
                 if (result == 0) {
                     it->it_index = -1;
-                    /* Jump forward oparg, then skip following END_FOR instruction */
                     JUMPBY(oparg + 1);
                     DISPATCH();
                 }
                 #endif
                 STAT_INC(FOR_ITER, hit);
                 if (r->len <= 0) {
-                    // Jump over END_FOR instruction.
                     JUMPBY(oparg + 1);
                     DISPATCH();
                 }
                         stack_pointer = _PyFrame_GetStackPointer(frame);
                     }
                     #endif
-                    /* Jump forward oparg, then skip following END_FOR instruction */
+
                     JUMPBY(oparg + 1);
                     DISPATCH();
                 }
             _PyStackRef iterable;
             _PyStackRef iter;
             iterable = stack_pointer[-1];
-            /* before: [obj]; after [getiter(obj)] */
             _PyFrame_SetStackPointer(frame, stack_pointer);
             PyObject *iter_o = PyObject_GetIter(PyStackRef_AsPyObjectBorrow(iterable));
             stack_pointer = _PyFrame_GetStackPointer(frame);
             _PyStackRef obj;
             _PyStackRef len;
             obj = stack_pointer[-1];
-            // PUSH(len(TOS))
             _PyFrame_SetStackPointer(frame, stack_pointer);
             Py_ssize_t len_i = PyObject_Length(PyStackRef_AsPyObjectBorrow(obj));
             stack_pointer = _PyFrame_GetStackPointer(frame);
             _PyStackRef iterable;
             _PyStackRef iter;
             iterable = stack_pointer[-1];
-            /* before: [obj]; after [getiter(obj)] */
             PyObject *iterable_o = PyStackRef_AsPyObjectBorrow(iterable);
             if (PyCoro_CheckExact(iterable_o)) {
-                /* `iterable` is a coroutine */
                 if (!(_PyFrame_GetCode(frame)->co_flags & (CO_COROUTINE | CO_ITERABLE_COROUTINE))) {
-                    /* and it is used in a 'yield from' expression of a
-                       regular generator. */
                     _PyFrame_SetStackPointer(frame, stack_pointer);
                     _PyErr_SetString(tstate, PyExc_TypeError,
                                      "cannot 'yield from' a coroutine object "
                 }
                 iter = iterable;
             }
+            else if (PyGen_CheckExact(iterable_o)) {
+                iter = iterable;
+            }
             else {
-                if (PyGen_CheckExact(iterable_o)) {
-                    iter = iterable;
-                }
-                else {
-                    /* `iterable` is not a generator. */
-                    _PyFrame_SetStackPointer(frame, stack_pointer);
-                    PyObject *iter_o = PyObject_GetIter(iterable_o);
-                    stack_pointer = _PyFrame_GetStackPointer(frame);
-                    if (iter_o == NULL) {
-                        JUMP_TO_LABEL(error);
-                    }
-                    iter = PyStackRef_FromPyObjectSteal(iter_o);
-                    _PyFrame_SetStackPointer(frame, stack_pointer);
-                    _PyStackRef tmp = iterable;
-                    iterable = iter;
-                    stack_pointer[-1] = iterable;
-                    PyStackRef_CLOSE(tmp);
-                    stack_pointer = _PyFrame_GetStackPointer(frame);
+                _PyFrame_SetStackPointer(frame, stack_pointer);
+                PyObject *iter_o = PyObject_GetIter(iterable_o);
+                stack_pointer = _PyFrame_GetStackPointer(frame);
+                if (iter_o == NULL) {
+                    JUMP_TO_LABEL(error);
                 }
+                iter = PyStackRef_FromPyObjectSteal(iter_o);
+                _PyFrame_SetStackPointer(frame, stack_pointer);
+                _PyStackRef tmp = iterable;
+                iterable = iter;
+                stack_pointer[-1] = iterable;
+                PyStackRef_CLOSE(tmp);
+                stack_pointer = _PyFrame_GetStackPointer(frame);
             }
             stack_pointer[-1] = iter;
             DISPATCH();
                 if (is_meth) {
                     arg0 = PyStackRef_AsPyObjectBorrow(maybe_self[0]);
                 }
+                else if (oparg) {
+                    arg0 = PyStackRef_AsPyObjectBorrow(args[0]);
+                }
                 else {
-                    if (oparg) {
-                        arg0 = PyStackRef_AsPyObjectBorrow(args[0]);
-                    }
-                    else {
-                        arg0 = &_PyInstrumentation_MISSING;
-                    }
+                    arg0 = &_PyInstrumentation_MISSING;
                 }
                 _PyFrame_SetStackPointer(frame, stack_pointer);
                 int err = _Py_call_instrumentation_2args(
                 self_or_null = maybe_self;
                 callable = func;
                 PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]);
-                // oparg counts all of the args, but *not* self:
                 int total_args = oparg;
                 _PyStackRef *arguments = args;
                 if (!PyStackRef_IsNull(self_or_null[0])) {
                     arguments--;
                     total_args++;
                 }
-                // Check if the call can be inlined or not
                 if (Py_TYPE(callable_o) == &PyFunction_Type &&
                     tstate->interp->eval_frame == NULL &&
                     ((PyFunctionObject *)callable_o)->vectorcall == _PyFunction_Vectorcall)
                         arguments, total_args, NULL, frame
                     );
                     stack_pointer = _PyFrame_GetStackPointer(frame);
-                    // Manipulate stack directly since we leave using DISPATCH_INLINED().
                     stack_pointer += -2 - oparg;
                     assert(WITHIN_STACK_BOUNDS());
-                    // The frame has stolen all the arguments from the stack,
-                    // so there is no need to clean them up.
                     if (new_frame == NULL) {
                         JUMP_TO_LABEL(error);
                     }
                     frame->return_offset = 4 ;
                     DISPATCH_INLINED(new_frame);
                 }
-                /* Callable is not a normal Python function */
                 STACKREFS_TO_PYOBJECTS(arguments, total_args, args_o);
                 if (CONVERSION_FAILED(args_o)) {
                     _PyFrame_SetStackPointer(frame, stack_pointer);
                 func_st = func;
                 (void)null;
                 PyObject *func = PyStackRef_AsPyObjectBorrow(func_st);
-                // DICT_MERGE is called before this opcode if there are kwargs.
-                // It converts all dict subtypes in kwargs into regular dicts.
                 EVAL_CALL_STAT_INC_IF_FUNCTION(EVAL_CALL_FUNCTION_EX, func);
                 PyObject *result_o;
                 assert(!_PyErr_Occurred(tstate));
                             tstate, func_st, locals,
                             nargs, callargs, kwargs, frame);
                         stack_pointer = _PyFrame_GetStackPointer(frame);
-                        // Need to sync the stack since we exit with DISPATCH_INLINED.
                         stack_pointer += -2;
                         assert(WITHIN_STACK_BOUNDS());
                         if (new_frame == NULL) {
                 if (is_meth) {
                     arg = PyStackRef_AsPyObjectBorrow(self_or_null[0]);
                 }
+                else if (args) {
+                    arg = PyStackRef_AsPyObjectBorrow(args[0]);
+                }
                 else {
-                    if (args) {
-                        arg = PyStackRef_AsPyObjectBorrow(args[0]);
-                    }
-                    else {
-                        arg = &_PyInstrumentation_MISSING;
-                    }
+                    arg = &_PyInstrumentation_MISSING;
                 }
                 PyObject *function = PyStackRef_AsPyObjectBorrow(callable[0]);
                 stack_pointer[-1] = kwnames_out;
                 kwnames = kwnames_out;
                 PyObject *callable_o = PyStackRef_AsPyObjectBorrow(callable[0]);
                 PyObject *kwnames_o = PyStackRef_AsPyObjectBorrow(kwnames);
-                // oparg counts all of the args, but *not* self:
                 int total_args = oparg;
                 _PyStackRef *arguments = args;
                 if (!PyStackRef_IsNull(self_or_null[0])) {
                     total_args++;
                 }
                 int positional_args = total_args - (int)PyTuple_GET_SIZE(kwnames_o);
-                // Check if the call can be inlined or not
                 if (Py_TYPE(callable_o) == &PyFunction_Type &&
                     tstate->interp->eval_frame == NULL &&
                     ((PyFunctionObject *)callable_o)->vectorcall == _PyFunction_Vectorcall)
                     _PyFrame_SetStackPointer(frame, stack_pointer);
                     PyStackRef_CLOSE(kwnames);
                     stack_pointer = _PyFrame_GetStackPointer(frame);
-                    // Sync stack explicitly since we leave using DISPATCH_INLINED().
-                    // The frame has stolen all the arguments from the stack,
-                    // so there is no need to clean them up.
                     if (new_frame == NULL) {
                         JUMP_TO_LABEL(error);
                     }
                     frame->return_offset = 4 ;
                     DISPATCH_INLINED(new_frame);
                 }
-                /* Callable is not a normal Python function */
                 STACKREFS_TO_PYOBJECTS(arguments, total_args, args_o);
                 if (CONVERSION_FAILED(args_o)) {
                     _PyFrame_SetStackPointer(frame, stack_pointer);
             {
                 exc_st = stack_pointer[-1];
                 awaitable_st = stack_pointer[-2];
-                JUMPBY(0); // Pretend jump as we need source offset for monitoring
+                JUMPBY(0);
                 (void)oparg;
                 PyObject *exc = PyStackRef_AsPyObjectBorrow(exc_st);
                 assert(exc && PyExceptionInstance_Check(exc));
             _PyStackRef value;
             value = stack_pointer[-1];
             receiver = stack_pointer[-2];
-            /* Need to create a fake StopIteration error here,
-             * to conform to PEP 380 */
             if (PyStackRef_GenCheck(receiver)) {
                 _PyFrame_SetStackPointer(frame, stack_pointer);
                 int err = monitor_stop_iteration(tstate, frame, this_instr, PyStackRef_AsPyObjectBorrow(value));
                     _PyErr_Clear(tstate);
                     stack_pointer = _PyFrame_GetStackPointer(frame);
                 }
-                /* iterator ended normally */
                 assert(next_instr[oparg].op.code == END_FOR ||
                        next_instr[oparg].op.code == INSTRUMENTED_END_FOR);
-                /* Skip END_FOR */
                 JUMPBY(oparg + 1);
             }
             DISPATCH();
             }
             if (_PyOpcode_Caches[original_opcode]) {
                 _PyBinaryOpCache *cache = (_PyBinaryOpCache *)(next_instr+1);
-                /* Prevent the underlying instruction from specializing
-                 * and overwriting the instrumentation. */
                 PAUSE_ADAPTIVE_COUNTER(cache->counter);
             }
             opcode = original_opcode;
                         JUMP_TO_LABEL(error);
                     }
                 }
-                // we make no attempt to optimize here; specializations should
-                // handle any case whose performance we care about
                 PyObject *stack[] = {class, self};
                 _PyFrame_SetStackPointer(frame, stack_pointer);
                 PyObject *super = PyObject_Vectorcall(global_super, stack, oparg & 2, NULL);
             frame->instr_ptr = next_instr;
             next_instr += 1;
             INSTRUCTION_STATS(INSTRUMENTED_NOT_TAKEN);
-            (void)this_instr; // INSTRUMENTED_JUMP requires this_instr
+            (void)this_instr;
             INSTRUMENTED_JUMP(prev_instr, next_instr, PY_MONITORING_EVENT_BRANCH_LEFT);
             DISPATCH();
         }
                     ptrdiff_t off = this_instr - _PyFrame_GetBytecode(frame);
                     frame->tlbc_index = ((_PyThreadStateImpl *)tstate)->tlbc_index;
                     frame->instr_ptr = bytecode + off;
-                    // Make sure this_instr gets reset correctley for any uops that
-                    // follow
                     next_instr = frame->instr_ptr;
                     DISPATCH();
                 }
             {
                 if ((oparg & RESUME_OPARG_LOCATION_MASK) < RESUME_AFTER_YIELD_FROM) {
                     _Py_CHECK_EMSCRIPTEN_SIGNALS_PERIODICALLY();
-                    QSBR_QUIESCENT_STATE(tstate); \
+                    QSBR_QUIESCENT_STATE(tstate);
                     if (_Py_atomic_load_uintptr_relaxed(&tstate->eval_breaker) & _PY_EVAL_EVENTS_MASK) {
                         _PyFrame_SetStackPointer(frame, stack_pointer);
                         int err = _Py_HandlePending(tstate);
                     JUMP_TO_LABEL(error);
                 }
                 if (frame->instr_ptr != this_instr) {
-                    /* Instrumentation has jumped */
                     next_instr = frame->instr_ptr;
                 }
             }
                 _PyFrame_SetStackPointer(frame, stack_pointer);
                 assert(EMPTY());
                 _Py_LeaveRecursiveCallPy(tstate);
-                // GH-99729: We need to unlink the frame *before* clearing it:
                 _PyInterpreterFrame *dying = frame;
                 frame = tstate->current_frame = dying->previous;
                 _PyEval_FrameClearAndPop(tstate, dying);
             // _YIELD_VALUE
             {
                 retval = val;
-                // NOTE: It's important that YIELD_VALUE never raises an exception!
-                // The compiler treats any exception raised here as a failed close()
-                // or throw() call.
                 assert(frame->owner != FRAME_OWNED_BY_INTERPRETER);
                 frame->instr_ptr++;
                 PyGenObject *gen = _PyGen_GetGeneratorFromFrame(frame);
                 _PyInterpreterFrame *gen_frame = frame;
                 frame = tstate->current_frame = frame->previous;
                 gen_frame->previous = NULL;
-                /* We don't know which of these is relevant here, so keep them equal */
                 assert(INLINE_CACHE_ENTRIES_SEND == INLINE_CACHE_ENTRIES_FOR_ITER);
                 #if TIER_ONE
                 assert(frame->instr_ptr->op.code == INSTRUMENTED_LINE ||
             retval = stack_pointer[-1];
             assert(frame->owner == FRAME_OWNED_BY_INTERPRETER);
             assert(_PyFrame_IsIncomplete(frame));
-            /* Restore previous frame and return. */
             tstate->current_frame = frame->previous;
             assert(!_PyErr_Occurred(tstate));
             PyObject *result = PyStackRef_AsPyObjectSteal(retval);
             stack_pointer += -1;
             assert(WITHIN_STACK_BOUNDS());
-            /* Not strictly necessary, but prevents warnings */
             return result;
         }
 
                 #if ENABLE_SPECIALIZATION
                 if (this_instr->op.code == JUMP_BACKWARD) {
                     this_instr->op.code = tstate->interp->jit ? JUMP_BACKWARD_JIT : JUMP_BACKWARD_NO_JIT;
-                    // Need to re-dispatch so the warmup counter isn't off by one:
                     next_instr = this_instr;
                     DISPATCH_SAME_OPARG();
                 }
             }
             // _JUMP_BACKWARD_NO_INTERRUPT
             {
-                /* This bytecode is used in the `yield from` or `await` loop.
-                 * If there is an interrupt, we want it handled in the innermost
-                 * generator or coroutine, so we deliberately do not check it here.
-                 * (see bpo-30039).
-                 */
                 assert(oparg <= INSTR_OFFSET());
                 JUMPBY(-oparg);
             }
             }
             // _JUMP_BACKWARD_NO_INTERRUPT
             {
-                /* This bytecode is used in the `yield from` or `await` loop.
-                 * If there is an interrupt, we want it handled in the innermost
-                 * generator or coroutine, so we deliberately do not check it here.
-                 * (see bpo-30039).
-                 */
                 assert(oparg <= INSTR_OFFSET());
                 JUMPBY(-oparg);
             }
                 _Py_BackoffCounter counter = this_instr[1].counter;
                 if (backoff_counter_triggers(counter) && this_instr->op.code == JUMP_BACKWARD_JIT) {
                     _Py_CODEUNIT *start = this_instr;
-                    /* Back up over EXTENDED_ARGs so optimizer sees the whole instruction */
                     while (oparg > 255) {
                         oparg >>= 8;
                         start--;
             frame->instr_ptr = next_instr;
             next_instr += 1;
             INSTRUCTION_STATS(JUMP_BACKWARD_NO_INTERRUPT);
-            /* This bytecode is used in the `yield from` or `await` loop.
-             * If there is an interrupt, we want it handled in the innermost
-             * generator or coroutine, so we deliberately do not check it here.
-             * (see bpo-30039).
-             */
             assert(oparg <= INSTR_OFFSET());
             JUMPBY(-oparg);
             DISPATCH();
             }
             // _JUMP_BACKWARD_NO_INTERRUPT
             {
-                /* This bytecode is used in the `yield from` or `await` loop.
-                 * If there is an interrupt, we want it handled in the innermost
-                 * generator or coroutine, so we deliberately do not check it here.
-                 * (see bpo-30039).
-                 */
                 assert(oparg <= INSTR_OFFSET());
                 JUMPBY(-oparg);
             }
                 PyObject *name = GETITEM(FRAME_CO_NAMES, oparg >> 1);
                 PyObject *attr_o;
                 if (oparg & 1) {
-                    /* Designed to work in tandem with CALL, pushes two values. */
                     attr_o = NULL;
                     _PyFrame_SetStackPointer(frame, stack_pointer);
                     int is_meth = _PyObject_GetMethod(PyStackRef_AsPyObjectBorrow(owner), name, &attr_o);
                     stack_pointer = _PyFrame_GetStackPointer(frame);
                     if (is_meth) {
-                        /* We can bypass temporary bound method object.
-                           meth is unbound method and obj is self.
-                           meth | self | arg1 | ... | argN
-                         */
-                        assert(attr_o != NULL);  // No errors on this branch
-                        self_or_null[0] = owner;  // Transfer ownership
+                        assert(attr_o != NULL);
+                        self_or_null[0] = owner;
                     }
                     else {
-                        /* meth is not an unbound method (but a regular attr, or
-                           something was returned by a descriptor protocol).  Set
-                           the second element of the stack to NULL, to signal
-                           CALL that it's not a method call.
-                           meth | NULL | arg1 | ... | argN
-                         */
                         stack_pointer += -1;
                         assert(WITHIN_STACK_BOUNDS());
                         _PyFrame_SetStackPointer(frame, stack_pointer);
                     }
                 }
                 else {
-                    /* Classic, pushes one value. */
                     _PyFrame_SetStackPointer(frame, stack_pointer);
                     attr_o = PyObject_GetAttr(PyStackRef_AsPyObjectBorrow(owner), name);
                     stack_pointer = _PyFrame_GetStackPointer(frame);
             PyObject *name = GETITEM(FRAME_CO_NAMES, oparg >> 1);
             _PyInterpreterFrame *new_frame = _PyFrame_PushUnchecked(
                 tstate, PyStackRef_FromPyObjectNew(f), 2, frame);
-            // Manipulate stack directly because we exit with DISPATCH_INLINED().
             STACK_SHRINK(1);
             new_frame->localsplus[0] = owner;
             new_frame->localsplus[1] = PyStackRef_FromPyObjectNew(name);
                     JUMP_TO_PREDICTED(LOAD_ATTR);
                 }
                 #ifdef Py_GIL_DISABLED
-                if (!_Py_TryIncrefCompareStackRef(value_ptr, attr_o, &attr)) {
+                int increfed = _Py_TryIncrefCompareStackRef(value_ptr, attr_o, &attr);
+                if (!increfed) {
                     if (true) {
                         UPDATE_MISS_STATS(LOAD_ATTR);
                         assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR));
                 uint16_t dictoffset = read_u16(&this_instr[4].cache);
                 char *ptr = ((char *)PyStackRef_AsPyObjectBorrow(owner)) + MANAGED_DICT_OFFSET + dictoffset;
                 PyObject *dict = FT_ATOMIC_LOAD_PTR_ACQUIRE(*(PyObject **)ptr);
-                /* This object has a __dict__, just not yet created */
                 if (dict != NULL) {
                     UPDATE_MISS_STATS(LOAD_ATTR);
                     assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR));
             {
                 PyObject *descr = read_obj(&this_instr[6].cache);
                 assert(oparg & 1);
-                /* Cached method object */
                 STAT_INC(LOAD_ATTR, hit);
                 assert(descr != NULL);
                 assert(_PyType_HasFeature(Py_TYPE(descr), Py_TPFLAGS_METHOD_DESCRIPTOR));
             }
             // _PUSH_FRAME
             {
-                // Write it out explicitly because it's subtly different.
-                // Eventually this should be the only occurrence of this code.
                 assert(tstate->interp->eval_frame == NULL);
                 _PyInterpreterFrame *temp = new_frame;
                 stack_pointer += -1;
                 }
                 STAT_INC(LOAD_ATTR, hit);
                 #ifdef Py_GIL_DISABLED
-                if (!_Py_TryIncrefCompareStackRef(&ep->me_value, attr_o, &attr)) {
+                int increfed = _Py_TryIncrefCompareStackRef(&ep->me_value, attr_o, &attr);
+                if (!increfed) {
                     if (true) {
                         UPDATE_MISS_STATS(LOAD_ATTR);
                         assert(_PyOpcode_Deopt[opcode] == (LOAD_ATTR));
             next_instr += 1;
             INSTRUCTION_STATS(LOAD_COMMON_CONSTANT);
             _PyStackRef value;
-            // Keep in sync with _common_constants in opcode.py
             assert(oparg < NUM_COMMON_CONSTANTS);
             value = PyStackRef_FromPyObjectNew(tstate->interp->common_consts[oparg]);
             stack_pointer[0] = value;
             _Py_CODEUNIT* const this_instr = next_instr - 1;
             (void)this_instr;
             _PyStackRef value;
-            /* We can't do this in the bytecode compiler as
-             * marshalling can intern strings and make them immortal. */
             PyObject *obj = GETITEM(FRAME_CO_CONSTS, oparg);
             value = PyStackRef_FromPyObjectNew(obj);
             #if ENABLE_SPECIALIZATION_FT
             if (!_Py_atomic_compare_exchange_uint8(
                     &this_instr->op.code, &expected,
                     _Py_IsImmortal(obj) ? LOAD_CONST_IMMORTAL : LOAD_CONST_MORTAL)) {
-                // We might lose a race with instrumentation, which we don't care about.
                 assert(expected >= MIN_INSTRUMENTED_OPCODE);
             }
             #else
                     stack_pointer = _PyFrame_GetStackPointer(frame);
                     if (v_o == NULL) {
                         if (!_PyErr_Occurred(tstate)) {
-                            /* _PyDict_LoadGlobal() returns NULL without raising
-                             * an exception if the key doesn't exist */
                             _PyFrame_SetStackPointer(frame, stack_pointer);
                             _PyEval_FormatExcCheckArg(tstate, PyExc_NameError,
                                 NAME_ERROR_MSG, name);
                     }
                 }
                 else {
-                    /* Slow-path if globals or builtins is not a dict */
-                    /* namespace 1: globals */
                     _PyFrame_SetStackPointer(frame, stack_pointer);
                     int err = PyMapping_GetOptionalItem(GLOBALS(), name, &v_o);
                     stack_pointer = _PyFrame_GetStackPointer(frame);
                         JUMP_TO_LABEL(error);
                     }
                     if (v_o == NULL) {
-                        /* namespace 2: builtins */
                         _PyFrame_SetStackPointer(frame, stack_pointer);
                         int err = PyMapping_GetOptionalItem(BUILTINS(), name, &v_o);
                         stack_pointer = _PyFrame_GetStackPointer(frame);
                         JUMP_TO_LABEL(error);
                     }
                 }
-                // we make no attempt to optimize here; specializations should
-                // handle any case whose performance we care about
                 PyObject *stack[] = {class, self};
                 _PyFrame_SetStackPointer(frame, stack_pointer);
                 PyObject *super = PyObject_Vectorcall(global_super, stack, oparg & 2, NULL);
                 JUMP_TO_LABEL(error);
             }
             if (method_found) {
-                self_or_null = self_st; // transfer ownership
+                self_or_null = self_st;
             } else {
                 stack_pointer += -1;
                 assert(WITHIN_STACK_BOUNDS());
             frame->instr_ptr = next_instr;
             next_instr += 1;
             INSTRUCTION_STATS(MAKE_CELL);
-            // "initial" is probably NULL but not if it's an arg (or set
-            // via the f_locals proxy before MAKE_CELL has run).
             PyObject *initial = PyStackRef_AsPyObjectBorrow(GETLOCAL(oparg));
             PyObject *cell = PyCell_New(initial);
             if (cell == NULL) {
             dict_st = stack_pointer[-3 - (oparg - 1)];
             PyObject *dict = PyStackRef_AsPyObjectBorrow(dict_st);
             assert(PyDict_CheckExact(dict));
-            /* dict[key] = value */
-            // Do not DECREF INPUTS because the function steals the references
             _PyFrame_SetStackPointer(frame, stack_pointer);
             int err = _PyDict_SetItem_Take2(
                 (PyDictObject *)dict,
             names = stack_pointer[-1];
             type = stack_pointer[-2];
             subject = stack_pointer[-3];
-            // Pop TOS and TOS1. Set TOS to a tuple of attributes on success, or
-            // None on failure.
             assert(PyTuple_CheckExact(PyStackRef_AsPyObjectBorrow(names)));
             _PyFrame_SetStackPointer(frame, stack_pointer);
             PyObject *attrs_o = _PyEval_MatchClass(tstate,
             stack_pointer += -3;
             assert(WITHIN_STACK_BOUNDS());
             if (attrs_o) {
-                assert(PyTuple_CheckExact(attrs_o));  // Success!
+                assert(PyTuple_CheckExact(attrs_o));
                 attrs = PyStackRef_FromPyObjectSteal(attrs_o);
             }
             else {
                 if (_PyErr_Occurred(tstate)) {
                     JUMP_TO_LABEL(error);
                 }
-                // Error!
-                attrs = PyStackRef_None;  // Failure!
+                attrs = PyStackRef_None;
             }
             stack_pointer[0] = attrs;
             stack_pointer += 1;
             _PyStackRef values_or_none;
             keys = stack_pointer[-1];
             subject = stack_pointer[-2];
-            // On successful match, PUSH(values). Otherwise, PUSH(None).
             _PyFrame_SetStackPointer(frame, stack_pointer);
             PyObject *values_or_none_o = _PyEval_MatchKeys(tstate,
                 PyStackRef_AsPyObjectBorrow(subject), PyStackRef_AsPyObjectBorrow(keys));
                     ptrdiff_t off = this_instr - _PyFrame_GetBytecode(frame);
                     frame->tlbc_index = ((_PyThreadStateImpl *)tstate)->tlbc_index;
                     frame->instr_ptr = bytecode + off;
-                    // Make sure this_instr gets reset correctley for any uops that
-                    // follow
                     next_instr = frame->instr_ptr;
                     DISPATCH();
                 }
             {
                 if ((oparg & RESUME_OPARG_LOCATION_MASK) < RESUME_AFTER_YIELD_FROM) {
                     _Py_CHECK_EMSCRIPTEN_SIGNALS_PERIODICALLY();
-                    QSBR_QUIESCENT_STATE(tstate); \
+                    QSBR_QUIESCENT_STATE(tstate);
                     if (_Py_atomic_load_uintptr_relaxed(&tstate->eval_breaker) & _PY_EVAL_EVENTS_MASK) {
                         _PyFrame_SetStackPointer(frame, stack_pointer);
                         int err = _Py_HandlePending(tstate);
             _PyFrame_SetStackPointer(frame, stack_pointer);
             assert(EMPTY());
             _Py_LeaveRecursiveCallPy(tstate);
-            // GH-99729: We need to unlink the frame *before* clearing it:
             _PyInterpreterFrame *dying = frame;
             frame = tstate->current_frame = dying->previous;
             _PyEval_FrameClearAndPop(tstate, dying);
             // _PUSH_FRAME
             {
                 new_frame = gen_frame;
-                // Write it out explicitly because it's subtly different.
-                // Eventually this should be the only occurrence of this code.
                 assert(tstate->interp->eval_frame == NULL);
                 _PyInterpreterFrame *temp = new_frame;
                 stack_pointer += -1;
                 stack_pointer = _PyFrame_GetStackPointer(frame);
                 JUMP_TO_LABEL(error);
             }
-            /* check if __annotations__ in locals()... */
             _PyFrame_SetStackPointer(frame, stack_pointer);
             int err = PyMapping_GetOptionalItem(LOCALS(), &_Py_ID(__annotations__), &ann_dict);
             stack_pointer = _PyFrame_GetStackPointer(frame);
                 stack_pointer = _PyFrame_GetStackPointer(frame);
                 FT_ATOMIC_STORE_PTR_RELEASE(ep->me_value, PyStackRef_AsPyObjectSteal(value));
                 UNLOCK_OBJECT(dict);
-                // old_value should be DECREFed after GC track checking is done, if not, it could raise a segmentation fault,
-                // when dict only holds the strong reference to value in ep->me_value.
                 STAT_INC(STORE_ATTR, hit);
                 stack_pointer += -2;
                 assert(WITHIN_STACK_BOUNDS());
             _PyStackRef stop;
             // _SPECIALIZE_STORE_SLICE
             {
-                // Placeholder until we implement STORE_SLICE specialization
                 #if ENABLE_SPECIALIZATION
                 OPCODE_DEFERRED_INC(STORE_SLICE);
                 #endif  /* ENABLE_SPECIALIZATION */
             // _STORE_SUBSCR
             {
                 v = stack_pointer[-3];
-                /* container[sub] = v */
                 _PyFrame_SetStackPointer(frame, stack_pointer);
                 int err = PyObject_SetItem(PyStackRef_AsPyObjectBorrow(container), PyStackRef_AsPyObjectBorrow(sub), PyStackRef_AsPyObjectBorrow(v));
                 _PyStackRef tmp = sub;
                     assert(_PyOpcode_Deopt[opcode] == (STORE_SUBSCR));
                     JUMP_TO_PREDICTED(STORE_SUBSCR);
                 }
-                // Ensure nonnegative, zero-or-one-digit ints.
                 if (!_PyLong_IsNonNegativeCompact((PyLongObject *)sub)) {
                     UPDATE_MISS_STATS(STORE_SUBSCR);
                     assert(_PyOpcode_Deopt[opcode] == (STORE_SUBSCR));
                     assert(_PyOpcode_Deopt[opcode] == (STORE_SUBSCR));
                     JUMP_TO_PREDICTED(STORE_SUBSCR);
                 }
-                // Ensure index < len(list)
                 if (index >= PyList_GET_SIZE(list)) {
                     UNLOCK_OBJECT(list);
                     if (true) {
                 FT_ATOMIC_STORE_PTR_RELEASE(_PyList_ITEMS(list)[index],
                                         PyStackRef_AsPyObjectSteal(value));
                 assert(old_value != NULL);
-                UNLOCK_OBJECT(list);  // unlock before decrefs!
+                UNLOCK_OBJECT(list);
                 PyStackRef_CLOSE_SPECIALIZED(sub_st, _PyLong_ExactDealloc);
                 stack_pointer += -3;
                 assert(WITHIN_STACK_BOUNDS());
             /* Skip 1 cache entry */
             /* Skip 2 cache entries */
             value = stack_pointer[-1];
-            // This one is a bit weird, because we expect *some* failures:
             if (!PyStackRef_IsNone(value)) {
                 UPDATE_MISS_STATS(TO_BOOL);
                 assert(_PyOpcode_Deopt[opcode] == (TO_BOOL));
             lasti = stack_pointer[-3];
             exit_self = stack_pointer[-4];
             exit_func = stack_pointer[-5];
-            /* At the top of the stack are 4 values:
-               - val: TOP = exc_info()
-               - unused: SECOND = previous exception
-               - lasti: THIRD = lasti of exception in exc_info()
-               - exit_self: FOURTH = the context or NULL
-               - exit_func: FIFTH = the context.__exit__ function or context.__exit__ bound method
-               We call FOURTH(type(TOP), TOP, GetTraceback(TOP)).
-               Then we push the __exit__ return value.
-             */
             PyObject *exc, *tb;
             PyObject *val_o = PyStackRef_AsPyObjectBorrow(val);
             PyObject *exit_func_o = PyStackRef_AsPyObjectBorrow(exit_func);
                 tb = Py_None;
             }
             assert(PyStackRef_LongCheck(lasti));
-            (void)lasti; // Shut up compiler warning if asserts are off
+            (void)lasti;
             PyObject *stack[5] = {NULL, PyStackRef_AsPyObjectBorrow(exit_self), exc, val_o, tb};
             int has_self = !PyStackRef_IsNull(exit_self);
             _PyFrame_SetStackPointer(frame, stack_pointer);
             _PyStackRef retval;
             _PyStackRef value;
             retval = stack_pointer[-1];
-            // NOTE: It's important that YIELD_VALUE never raises an exception!
-            // The compiler treats any exception raised here as a failed close()
-            // or throw() call.
             assert(frame->owner != FRAME_OWNED_BY_INTERPRETER);
             frame->instr_ptr++;
             PyGenObject *gen = _PyGen_GetGeneratorFromFrame(frame);
             _PyInterpreterFrame *gen_frame = frame;
             frame = tstate->current_frame = frame->previous;
             gen_frame->previous = NULL;
-            /* We don't know which of these is relevant here, so keep them equal */
             assert(INLINE_CACHE_ENTRIES_SEND == INLINE_CACHE_ENTRIES_FOR_ITER);
             #if TIER_ONE
             assert(frame->instr_ptr->op.code == INSTRUMENTED_LINE ||
@@ -12256,7 +12008,6 @@ JUMP_TO_LABEL(error);
 
         LABEL(error)
         {
-            /* Double-check exception status. */
             #ifdef NDEBUG
             if (!_PyErr_Occurred(tstate)) {
                 _PyFrame_SetStackPointer(frame, stack_pointer);
@@ -12268,7 +12019,6 @@ JUMP_TO_LABEL(error);
             assert(_PyErr_Occurred(tstate));
             #endif
 
-            /* Log traceback info. */
             assert(frame->owner != FRAME_OWNED_BY_INTERPRETER);
             if (!_PyFrame_IsIncomplete(frame)) {
                 _PyFrame_SetStackPointer(frame, stack_pointer);
@@ -12287,15 +12037,11 @@ JUMP_TO_LABEL(error);
 
         LABEL(exception_unwind)
         {
-            /* STACK SPILLED */
-            /* We can't use frame->instr_ptr here, as RERAISE may have set it */
             int offset = INSTR_OFFSET()-1;
             int level, handler, lasti;
             int handled = get_exception_handler(_PyFrame_GetCode(frame), offset, &level, &handler, &lasti);
             if (handled == 0) {
-                // No handlers, so exit.
                 assert(_PyErr_Occurred(tstate));
-                /* Pop remaining stack entries. */
                 _PyStackRef *stackbase = _PyFrame_Stackbase(frame);
                 while (frame->stackpointer > stackbase) {
                     _PyStackRef ref = _PyFrame_StackPop(frame);
@@ -12319,10 +12065,6 @@ JUMP_TO_LABEL(error);
                 }
                 _PyFrame_StackPush(frame, PyStackRef_FromPyObjectSteal(lasti));
             }
-            /* Make the raw exception data
-               available to the handler,
-               so a program can emulate the
-               Python main loop. */
             PyObject *exc = _PyErr_GetRaisedException(tstate);
             _PyFrame_StackPush(frame, PyStackRef_FromPyObjectSteal(exc));
             next_instr = _PyFrame_GetBytecode(frame) + handler;
@@ -12330,7 +12072,6 @@ JUMP_TO_LABEL(error);
             if (err < 0) {
                 JUMP_TO_LABEL(exception_unwind);
             }
-            /* Resume normal execution */
             #ifdef Py_DEBUG
             if (frame->lltrace >= 5) {
                 lltrace_resume_frame(frame);
@@ -12345,17 +12086,14 @@ JUMP_TO_LABEL(error);
 
         LABEL(exit_unwind)
         {
-            /* STACK SPILLED */
             assert(_PyErr_Occurred(tstate));
             _Py_LeaveRecursiveCallPy(tstate);
             assert(frame->owner != FRAME_OWNED_BY_INTERPRETER);
-            // GH-99729: We need to unlink the frame *before* clearing it:
             _PyInterpreterFrame *dying = frame;
             frame = tstate->current_frame = dying->previous;
             _PyEval_FrameClearAndPop(tstate, dying);
             frame->return_offset = 0;
             if (frame->owner == FRAME_OWNED_BY_INTERPRETER) {
-                /* Restore previous frame and exit */
                 tstate->current_frame = frame->previous;
                 return NULL;
             }
@@ -12366,7 +12104,6 @@ JUMP_TO_LABEL(error);
 
         LABEL(start_frame)
         {
-            /* STACK SPILLED */
             int too_deep = _Py_EnterRecursivePy(tstate);
             if (too_deep) {
                 JUMP_TO_LABEL(exit_unwind);
@@ -12378,9 +12115,6 @@ JUMP_TO_LABEL(error);
                 JUMP_TO_LABEL(exit_unwind);
             }
             frame->lltrace = lltrace;
-            /* _PyEval_EvalFrameDefault() must not be called with an exception set,
-               because it can clear it (directly or indirectly) and so the
-               caller loses its exception */
             assert(!_PyErr_Occurred(tstate));
             #endif
             stack_pointer = _PyFrame_GetStackPointer(frame);
index a948cccbf858ac75593c6bee1a2af389af1a2c1e..da36704d91e4b327c4cafaa7bba17c4e6bb78008 100644 (file)
@@ -367,34 +367,39 @@ dummy_func(void) {
     }
 
     op(_TO_BOOL, (value -- res)) {
-        if (!optimize_to_bool(this_instr, ctx, value, &res)) {
+        int already_bool = optimize_to_bool(this_instr, ctx, value, &res);
+        if (!already_bool) {
             res = sym_new_truthiness(ctx, value, true);
         }
     }
 
     op(_TO_BOOL_BOOL, (value -- res)) {
-        if (!optimize_to_bool(this_instr, ctx, value, &res)) {
+        int already_bool = optimize_to_bool(this_instr, ctx, value, &res);
+        if (!already_bool) {
             sym_set_type(value, &PyBool_Type);
             res = sym_new_truthiness(ctx, value, true);
         }
     }
 
     op(_TO_BOOL_INT, (value -- res)) {
-        if (!optimize_to_bool(this_instr, ctx, value, &res)) {
+        int already_bool = optimize_to_bool(this_instr, ctx, value, &res);
+        if (!already_bool) {
             sym_set_type(value, &PyLong_Type);
             res = sym_new_truthiness(ctx, value, true);
         }
     }
 
     op(_TO_BOOL_LIST, (value -- res)) {
-        if (!optimize_to_bool(this_instr, ctx, value, &res)) {
+        int already_bool = optimize_to_bool(this_instr, ctx, value, &res);
+        if (!already_bool) {
             sym_set_type(value, &PyList_Type);
             res = sym_new_type(ctx, &PyBool_Type);
         }
     }
 
     op(_TO_BOOL_NONE, (value -- res)) {
-        if (!optimize_to_bool(this_instr, ctx, value, &res)) {
+        int already_bool = optimize_to_bool(this_instr, ctx, value, &res);
+        if (!already_bool) {
             sym_set_const(value, Py_None);
             res = sym_new_const(ctx, Py_False);
         }
@@ -415,7 +420,8 @@ dummy_func(void) {
     }
 
     op(_TO_BOOL_STR, (value -- res)) {
-        if (!optimize_to_bool(this_instr, ctx, value, &res)) {
+        int already_bool = optimize_to_bool(this_instr, ctx, value, &res);
+        if (!already_bool) {
             res = sym_new_truthiness(ctx, value, true);
         }
     }
index e306567c81253b78dafd41c283e5110896411dc0..ea25b8224a459b037d3d50c46528c7fc0a8b5a39 100644 (file)
@@ -28,7 +28,6 @@
         case _LOAD_FAST_CHECK: {
             JitOptSymbol *value;
             value = GETLOCAL(oparg);
-            // We guarantee this will error - just bail and don't optimize it.
             if (sym_is_null(value)) {
                 ctx->done = true;
             }
             JitOptSymbol *value;
             JitOptSymbol *res;
             value = stack_pointer[-1];
-            if (!optimize_to_bool(this_instr, ctx, value, &res)) {
+            int already_bool = optimize_to_bool(this_instr, ctx, value, &res);
+            if (!already_bool) {
                 res = sym_new_truthiness(ctx, value, true);
             }
             stack_pointer[-1] = res;
             JitOptSymbol *value;
             JitOptSymbol *res;
             value = stack_pointer[-1];
-            if (!optimize_to_bool(this_instr, ctx, value, &res)) {
+            int already_bool = optimize_to_bool(this_instr, ctx, value, &res);
+            if (!already_bool) {
                 sym_set_type(value, &PyBool_Type);
                 res = sym_new_truthiness(ctx, value, true);
             }
             JitOptSymbol *value;
             JitOptSymbol *res;
             value = stack_pointer[-1];
-            if (!optimize_to_bool(this_instr, ctx, value, &res)) {
+            int already_bool = optimize_to_bool(this_instr, ctx, value, &res);
+            if (!already_bool) {
                 sym_set_type(value, &PyLong_Type);
                 res = sym_new_truthiness(ctx, value, true);
             }
             JitOptSymbol *value;
             JitOptSymbol *res;
             value = stack_pointer[-1];
-            if (!optimize_to_bool(this_instr, ctx, value, &res)) {
+            int already_bool = optimize_to_bool(this_instr, ctx, value, &res);
+            if (!already_bool) {
                 sym_set_type(value, &PyList_Type);
                 res = sym_new_type(ctx, &PyBool_Type);
             }
             JitOptSymbol *value;
             JitOptSymbol *res;
             value = stack_pointer[-1];
-            if (!optimize_to_bool(this_instr, ctx, value, &res)) {
+            int already_bool = optimize_to_bool(this_instr, ctx, value, &res);
+            if (!already_bool) {
                 sym_set_const(value, Py_None);
                 res = sym_new_const(ctx, Py_False);
             }
             JitOptSymbol *value;
             JitOptSymbol *res;
             value = stack_pointer[-1];
-            if (!optimize_to_bool(this_instr, ctx, value, &res)) {
+            int already_bool = optimize_to_bool(this_instr, ctx, value, &res);
+            if (!already_bool) {
                 res = sym_new_truthiness(ctx, value, true);
             }
             stack_pointer[-1] = res;
                 stack_pointer += -1;
                 assert(WITHIN_STACK_BOUNDS());
                 Py_DECREF(temp);
-                // TODO gh-115506:
-                // replace opcode with constant propagated one and add tests!
             }
             else {
                 res = sym_new_type(ctx, &PyLong_Type);
                 stack_pointer += -1;
                 assert(WITHIN_STACK_BOUNDS());
                 Py_DECREF(temp);
-                // TODO gh-115506:
-                // replace opcode with constant propagated one and add tests!
             }
             else {
                 res = sym_new_type(ctx, &PyLong_Type);
                 stack_pointer += -1;
                 assert(WITHIN_STACK_BOUNDS());
                 Py_DECREF(temp);
-                // TODO gh-115506:
-                // replace opcode with constant propagated one and add tests!
             }
             else {
                 res = sym_new_type(ctx, &PyLong_Type);
                 stack_pointer += -1;
                 assert(WITHIN_STACK_BOUNDS());
                 Py_DECREF(temp);
-                // TODO gh-115506:
-                // replace opcode with constant propagated one and update tests!
             }
             else {
                 res = sym_new_type(ctx, &PyFloat_Type);
                 stack_pointer += -1;
                 assert(WITHIN_STACK_BOUNDS());
                 Py_DECREF(temp);
-                // TODO gh-115506:
-                // replace opcode with constant propagated one and update tests!
             }
             else {
                 res = sym_new_type(ctx, &PyFloat_Type);
                 stack_pointer += -1;
                 assert(WITHIN_STACK_BOUNDS());
                 Py_DECREF(temp);
-                // TODO gh-115506:
-                // replace opcode with constant propagated one and update tests!
             }
             else {
                 res = sym_new_type(ctx, &PyFloat_Type);
             else {
                 res = sym_new_type(ctx, &PyUnicode_Type);
             }
-            // _STORE_FAST:
             GETLOCAL(this_instr->operand0) = res;
             stack_pointer += -2;
             assert(WITHIN_STACK_BOUNDS());
             ctx->frame->stack_pointer = stack_pointer;
             frame_pop(ctx);
             stack_pointer = ctx->frame->stack_pointer;
-            /* Stack space handling */
             assert(corresponding_check_stack == NULL);
             assert(co != NULL);
             int framesize = co->co_framesize;
             curr_space -= framesize;
             co = get_code(this_instr);
             if (co == NULL) {
-                // might be impossible, but bailing is still safe
                 ctx->done = true;
             }
             res = temp;
         /* _SEND is not a viable micro-op for tier 2 */
 
         case _SEND_GEN_FRAME: {
-            // We are about to hit the end of the trace:
             ctx->done = true;
             break;
         }
         case _UNPACK_SEQUENCE: {
             JitOptSymbol **values;
             values = &stack_pointer[-1];
-            /* This has to be done manually */
             for (int i = 0; i < oparg; i++) {
                 values[i] = sym_new_unknown(ctx);
             }
         case _UNPACK_EX: {
             JitOptSymbol **values;
             values = &stack_pointer[-1];
-            /* This has to be done manually */
             int totalargs = (oparg & 0xFF) + (oparg >> 8) + 1;
             for (int i = 0; i < totalargs; i++) {
                 values[i] = sym_new_unknown(ctx);
             if (sym_matches_type_version(owner, type_version)) {
                 REPLACE_OP(this_instr, _NOP, 0, 0);
             } else {
-                // add watcher so that whenever the type changes we invalidate this
                 PyTypeObject *type = _PyType_LookupByVersion(type_version);
-                // if the type is null, it was not found in the cache (there was a conflict)
-                // with the key, in which case we can't trust the version
                 if (type) {
-                    // if the type version was set properly, then add a watcher
-                    // if it wasn't this means that the type version was previously set to something else
-                    // and we set the owner to bottom, so we don't need to add a watcher because we must have
-                    // already added one earlier.
                     if (sym_set_type_version(owner, type_version)) {
                         PyType_Watch(TYPE_WATCHER_ID, (PyObject *)type);
                         _Py_BloomFilter_Add(dependencies, type);
                 }
             }
             if (attr == NULL) {
-                /* No conversion made. We don't know what `attr` is. */
                 attr = sym_new_not_null(ctx);
             }
             stack_pointer[-1] = attr;
         }
 
         case _FOR_ITER_GEN_FRAME: {
-            /* We are about to hit the end of the trace */
             ctx->done = true;
             break;
         }
         }
 
         case _CHECK_PEP_523: {
-            /* Setting the eval frame function invalidates
-             * all executors, so no need to check dynamically */
             if (_PyInterpreterState_GET()->eval_frame == NULL) {
                 REPLACE_OP(this_instr, _NOP, 0 ,0);
             }
             assert(self_or_null != NULL);
             assert(args != NULL);
             if (sym_is_not_null(self_or_null)) {
-                // Bound method fiddling, same as _INIT_CALL_PY_EXACT_ARGS in VM
                 args--;
                 argcount++;
             }
             stack_pointer = new_frame->stack_pointer;
             co = get_code(this_instr);
             if (co == NULL) {
-                // should be about to _EXIT_TRACE anyway
                 ctx->done = true;
                 break;
             }
-            /* Stack space handling */
             int framesize = co->co_framesize;
             assert(framesize > 0);
             curr_space += framesize;
             if (curr_space < 0 || curr_space > INT32_MAX) {
-                // won't fit in signed 32-bit int
                 ctx->done = true;
                 break;
             }
             if (first_valid_check_stack == NULL) {
                 first_valid_check_stack = corresponding_check_stack;
             }
-            else {
-                if (corresponding_check_stack) {
-                    // delete all but the first valid _CHECK_STACK_SPACE
-                    corresponding_check_stack->opcode = _NOP;
-                }
+            else if (corresponding_check_stack) {
+                corresponding_check_stack->opcode = _NOP;
             }
             corresponding_check_stack = NULL;
             break;
             frame_pop(ctx);
             stack_pointer = ctx->frame->stack_pointer;
             res = sym_new_unknown(ctx);
-            /* Stack space handling */
             assert(corresponding_check_stack == NULL);
             assert(co != NULL);
             int framesize = co->co_framesize;
             assert(WITHIN_STACK_BOUNDS());
             co = get_code(this_instr);
             if (co == NULL) {
-                // might be impossible, but bailing is still safe
                 ctx->done = true;
             }
             stack_pointer[-1] = res;
             bool lhs_float = sym_matches_type(left, &PyFloat_Type);
             bool rhs_float = sym_matches_type(right, &PyFloat_Type);
             if (!((lhs_int || lhs_float) && (rhs_int || rhs_float))) {
-                // There's something other than an int or float involved:
                 res = sym_new_unknown(ctx);
             }
-            else {
-                if (oparg == NB_POWER || oparg == NB_INPLACE_POWER) {
-                    // This one's fun... the *type* of the result depends on the
-                    // *values* being exponentiated. However, exponents with one
-                    // constant part are reasonably common, so it's probably worth
-                    // trying to infer some simple cases:
-                    // - A: 1 ** 1 -> 1 (int ** int -> int)
-                    // - B: 1 ** -1 -> 1.0 (int ** int -> float)
-                    // - C: 1.0 ** 1 -> 1.0 (float ** int -> float)
-                    // - D: 1 ** 1.0 -> 1.0 (int ** float -> float)
-                    // - E: -1 ** 0.5 ~> 1j (int ** float -> complex)
-                    // - F: 1.0 ** 1.0 -> 1.0 (float ** float -> float)
-                    // - G: -1.0 ** 0.5 ~> 1j (float ** float -> complex)
-                    if (rhs_float) {
-                        // Case D, E, F, or G... can't know without the sign of the LHS
-                        // or whether the RHS is whole, which isn't worth the effort:
-                        res = sym_new_unknown(ctx);
-                    }
-                    else {
-                        if (lhs_float) {
-                            // Case C:
-                            res = sym_new_type(ctx, &PyFloat_Type);
-                        }
-                        else {
-                            if (!sym_is_const(ctx, right)) {
-                                // Case A or B... can't know without the sign of the RHS:
-                                res = sym_new_unknown(ctx);
-                            }
-                            else {
-                                if (_PyLong_IsNegative((PyLongObject *)sym_get_const(ctx, right))) {
-                                    // Case B:
-                                    res = sym_new_type(ctx, &PyFloat_Type);
-                                }
-                                else {
-                                    // Case A:
-                                    res = sym_new_type(ctx, &PyLong_Type);
-                                }
-                            }
-                        }
-                    }
+            else if (oparg == NB_POWER || oparg == NB_INPLACE_POWER) {
+                if (rhs_float) {
+                    res = sym_new_unknown(ctx);
+                }
+                else if (lhs_float) {
+                    res = sym_new_type(ctx, &PyFloat_Type);
+                }
+                else if (!sym_is_const(ctx, right)) {
+                    res = sym_new_unknown(ctx);
+                }
+                else if (_PyLong_IsNegative((PyLongObject *)sym_get_const(ctx, right))) {
+                    res = sym_new_type(ctx, &PyFloat_Type);
                 }
                 else {
-                    if (oparg == NB_TRUE_DIVIDE || oparg == NB_INPLACE_TRUE_DIVIDE) {
-                        res = sym_new_type(ctx, &PyFloat_Type);
-                    }
-                    else {
-                        if (lhs_int && rhs_int) {
-                            res = sym_new_type(ctx, &PyLong_Type);
-                        }
-                        else {
-                            res = sym_new_type(ctx, &PyFloat_Type);
-                        }
-                    }
+                    res = sym_new_type(ctx, &PyLong_Type);
                 }
             }
+            else if (oparg == NB_TRUE_DIVIDE || oparg == NB_INPLACE_TRUE_DIVIDE) {
+                res = sym_new_type(ctx, &PyFloat_Type);
+            }
+            else if (lhs_int && rhs_int) {
+                res = sym_new_type(ctx, &PyLong_Type);
+            }
+            else {
+                res = sym_new_type(ctx, &PyFloat_Type);
+            }
             stack_pointer[-2] = res;
             stack_pointer += -1;
             assert(WITHIN_STACK_BOUNDS());
                 assert(value != NULL);
                 eliminate_pop_guard(this_instr, !Py_IsNone(value));
             }
-            else {
-                if (sym_has_type(flag)) {
-                    assert(!sym_matches_type(flag, &_PyNone_Type));
-                    eliminate_pop_guard(this_instr, true);
-                }
+            else if (sym_has_type(flag)) {
+                assert(!sym_matches_type(flag, &_PyNone_Type));
+                eliminate_pop_guard(this_instr, true);
             }
             sym_set_const(flag, Py_None);
             stack_pointer += -1;
                 assert(value != NULL);
                 eliminate_pop_guard(this_instr, Py_IsNone(value));
             }
-            else {
-                if (sym_has_type(flag)) {
-                    assert(!sym_matches_type(flag, &_PyNone_Type));
-                    eliminate_pop_guard(this_instr, false);
-                }
+            else if (sym_has_type(flag)) {
+                assert(!sym_matches_type(flag, &_PyNone_Type));
+                eliminate_pop_guard(this_instr, false);
             }
             stack_pointer += -1;
             assert(WITHIN_STACK_BOUNDS());
         case _CHECK_STACK_SPACE_OPERAND: {
             uint32_t framesize = (uint32_t)this_instr->operand0;
             (void)framesize;
-            /* We should never see _CHECK_STACK_SPACE_OPERANDs.
-             * They are only created at the end of this pass. */
             Py_UNREACHABLE();
             break;
         }
index 5d75b9f036b52427d14f6842f44c3d0bc9419467..a217d7136a540196acb032aa22a90b74e540575b 100644 (file)
@@ -3,18 +3,19 @@ import itertools
 import lexer
 import parser
 import re
-from typing import Optional
+from typing import Optional, Callable
+
+from parser import Stmt, SimpleStmt, BlockStmt, IfStmt, WhileStmt
 
 @dataclass
 class EscapingCall:
-    start: lexer.Token
+    stmt: SimpleStmt
     call: lexer.Token
-    end: lexer.Token
     kills: lexer.Token | None
 
 @dataclass
 class Properties:
-    escaping_calls: dict[lexer.Token, EscapingCall]
+    escaping_calls: dict[SimpleStmt, EscapingCall]
     escapes: bool
     error_with_pop: bool
     error_without_pop: bool
@@ -48,7 +49,7 @@ class Properties:
 
     @staticmethod
     def from_list(properties: list["Properties"]) -> "Properties":
-        escaping_calls: dict[lexer.Token, EscapingCall] = {}
+        escaping_calls: dict[SimpleStmt, EscapingCall] = {}
         for p in properties:
             escaping_calls.update(p.escaping_calls)
         return Properties(
@@ -176,9 +177,8 @@ class Uop:
     annotations: list[str]
     stack: StackEffect
     caches: list[CacheEntry]
-    deferred_refs: dict[lexer.Token, str | None]
     local_stores: list[lexer.Token]
-    body: list[lexer.Token]
+    body: BlockStmt
     properties: Properties
     _size: int = -1
     implicitly_created: bool = False
@@ -221,7 +221,7 @@ class Uop:
         return self.why_not_viable() is None
 
     def is_super(self) -> bool:
-        for tkn in self.body:
+        for tkn in self.body.tokens():
             if tkn.kind == "IDENTIFIER" and tkn.text == "oparg1":
                 return True
         return False
@@ -229,7 +229,7 @@ class Uop:
 
 class Label:
 
-    def __init__(self, name: str, spilled: bool, body: list[lexer.Token], properties: Properties):
+    def __init__(self, name: str, spilled: bool, body: BlockStmt, properties: Properties):
         self.name = name
         self.spilled = spilled
         self.body = body
@@ -421,100 +421,102 @@ def analyze_caches(inputs: list[parser.InputEffect]) -> list[CacheEntry]:
     return [CacheEntry(i.name, int(i.size)) for i in caches]
 
 
-def find_assignment_target(node: parser.InstDef, idx: int) -> list[lexer.Token]:
-    """Find the tokens that make up the left-hand side of an assignment"""
-    offset = 0
-    for tkn in reversed(node.block.tokens[: idx]):
-        if tkn.kind in {"SEMI", "LBRACE", "RBRACE", "CMACRO"}:
-            return node.block.tokens[idx - offset : idx]
-        offset += 1
-    return []
-
-
 def find_variable_stores(node: parser.InstDef) -> list[lexer.Token]:
     res: list[lexer.Token] = []
     outnames = { out.name for out in node.outputs }
     innames = { out.name for out in node.inputs }
-    for idx, tkn in enumerate(node.block.tokens):
-        if tkn.kind == "AND":
-            name = node.block.tokens[idx+1]
-            if name.text in outnames:
-                res.append(name)
-        if tkn.kind != "EQUALS":
-            continue
-        lhs = find_assignment_target(node, idx)
-        assert lhs
-        while lhs and lhs[0].kind == "COMMENT":
-            lhs = lhs[1:]
-        if len(lhs) != 1 or lhs[0].kind != "IDENTIFIER":
-            continue
-        name = lhs[0]
-        if name.text in outnames or name.text in innames:
-            res.append(name)
-    return res
-
-def analyze_deferred_refs(node: parser.InstDef) -> dict[lexer.Token, str | None]:
-    """Look for PyStackRef_FromPyObjectNew() calls"""
-
-    def in_frame_push(idx: int) -> bool:
-        for tkn in reversed(node.block.tokens[: idx - 1]):
-            if tkn.kind in {"SEMI", "LBRACE", "RBRACE"}:
-                return False
-            if tkn.kind == "IDENTIFIER" and tkn.text == "_PyFrame_PushUnchecked":
-                return True
-        return False
-
-    refs: dict[lexer.Token, str | None] = {}
-    for idx, tkn in enumerate(node.block.tokens):
-        if tkn.kind != "IDENTIFIER" or tkn.text != "PyStackRef_FromPyObjectNew":
-            continue
-
-        if idx == 0 or node.block.tokens[idx - 1].kind != "EQUALS":
-            if in_frame_push(idx):
-                # PyStackRef_FromPyObjectNew() is called in _PyFrame_PushUnchecked()
-                refs[tkn] = None
-                continue
-            raise analysis_error("Expected '=' before PyStackRef_FromPyObjectNew", tkn)
-
-        lhs = find_assignment_target(node, idx - 1)
-        if len(lhs) == 0:
-            raise analysis_error(
-                "PyStackRef_FromPyObjectNew() must be assigned to an output", tkn
-            )
-
-        if lhs[0].kind == "TIMES" or any(
-            t.kind == "ARROW" or t.kind == "LBRACKET" for t in lhs[1:]
-        ):
-            # Don't handle: *ptr = ..., ptr->field = ..., or ptr[field] = ...
-            # Assume that they are visible to the GC.
-            refs[tkn] = None
-            continue
 
-        if len(lhs) != 1 or lhs[0].kind != "IDENTIFIER":
-            raise analysis_error(
-                "PyStackRef_FromPyObjectNew() must be assigned to an output", tkn
-            )
-
-        name = lhs[0].text
-        match = (
-            any(var.name == name for var in node.inputs)
-            or any(var.name == name for var in node.outputs)
-        )
-        if not match:
-            raise analysis_error(
-                f"PyStackRef_FromPyObjectNew() must be assigned to an input or output, not '{name}'",
-                tkn,
-            )
+    def find_stores_in_tokens(tokens: list[lexer.Token], callback: Callable[[lexer.Token], None]) -> None:
+        while tokens and tokens[0].kind == "COMMENT":
+            tokens = tokens[1:]
+        if len(tokens) < 4:
+            return
+        if tokens[1].kind == "EQUALS":
+            if tokens[0].kind == "IDENTIFIER":
+                name = tokens[0].text
+                if name in outnames or name in innames:
+                    callback(tokens[0])
+        #Passing the address of a local is also a definition
+        for idx, tkn in enumerate(tokens):
+            if tkn.kind == "AND":
+                name_tkn = tokens[idx+1]
+                if name_tkn.text in outnames:
+                    callback(name_tkn)
+
+    def visit(stmt: Stmt) -> None:
+        if isinstance(stmt, IfStmt):
+            def error(tkn: lexer.Token) -> None:
+                raise analysis_error("Cannot define variable in 'if' condition", tkn)
+            find_stores_in_tokens(stmt.condition, error)
+        elif isinstance(stmt, SimpleStmt):
+            find_stores_in_tokens(stmt.contents, res.append)
+
+    node.block.accept(visit)
+    return res
 
-        refs[tkn] = name
 
-    return refs
+#def analyze_deferred_refs(node: parser.InstDef) -> dict[lexer.Token, str | None]:
+    #"""Look for PyStackRef_FromPyObjectNew() calls"""
+
+    #def in_frame_push(idx: int) -> bool:
+        #for tkn in reversed(node.block.tokens[: idx - 1]):
+            #if tkn.kind in {"SEMI", "LBRACE", "RBRACE"}:
+                #return False
+            #if tkn.kind == "IDENTIFIER" and tkn.text == "_PyFrame_PushUnchecked":
+                #return True
+        #return False
+
+    #refs: dict[lexer.Token, str | None] = {}
+    #for idx, tkn in enumerate(node.block.tokens):
+        #if tkn.kind != "IDENTIFIER" or tkn.text != "PyStackRef_FromPyObjectNew":
+            #continue
+
+        #if idx == 0 or node.block.tokens[idx - 1].kind != "EQUALS":
+            #if in_frame_push(idx):
+                ## PyStackRef_FromPyObjectNew() is called in _PyFrame_PushUnchecked()
+                #refs[tkn] = None
+                #continue
+            #raise analysis_error("Expected '=' before PyStackRef_FromPyObjectNew", tkn)
+
+        #lhs = find_assignment_target(node, idx - 1)
+        #if len(lhs) == 0:
+            #raise analysis_error(
+                #"PyStackRef_FromPyObjectNew() must be assigned to an output", tkn
+            #)
+
+        #if lhs[0].kind == "TIMES" or any(
+            #t.kind == "ARROW" or t.kind == "LBRACKET" for t in lhs[1:]
+        #):
+            ## Don't handle: *ptr = ..., ptr->field = ..., or ptr[field] = ...
+            ## Assume that they are visible to the GC.
+            #refs[tkn] = None
+            #continue
+
+        #if len(lhs) != 1 or lhs[0].kind != "IDENTIFIER":
+            #raise analysis_error(
+                #"PyStackRef_FromPyObjectNew() must be assigned to an output", tkn
+            #)
+
+        #name = lhs[0].text
+        #match = (
+            #any(var.name == name for var in node.inputs)
+            #or any(var.name == name for var in node.outputs)
+        #)
+        #if not match:
+            #raise analysis_error(
+                #f"PyStackRef_FromPyObjectNew() must be assigned to an input or output, not '{name}'",
+                #tkn,
+            #)
+
+        #refs[tkn] = name
+
+    #return refs
 
 
 def variable_used(node: parser.CodeDef, name: str) -> bool:
     """Determine whether a variable with a given name is used in a node."""
     return any(
-        token.kind == "IDENTIFIER" and token.text == name for token in node.block.tokens
+        token.kind == "IDENTIFIER" and token.text == name for token in node.block.tokens()
     )
 
 
@@ -678,93 +680,86 @@ NON_ESCAPING_FUNCTIONS = (
     "_Py_ReachedRecursionLimit",
 )
 
-def find_stmt_start(node: parser.CodeDef, idx: int) -> lexer.Token:
-    assert idx < len(node.block.tokens)
-    while True:
-        tkn = node.block.tokens[idx-1]
-        if tkn.kind in {"SEMI", "LBRACE", "RBRACE", "CMACRO"}:
-            break
-        idx -= 1
-        assert idx > 0
-    while node.block.tokens[idx].kind == "COMMENT":
-        idx += 1
-    return node.block.tokens[idx]
-
-
-def find_stmt_end(node: parser.CodeDef, idx: int) -> lexer.Token:
-    assert idx < len(node.block.tokens)
-    while True:
-        idx += 1
-        tkn = node.block.tokens[idx]
-        if tkn.kind == "SEMI":
-            return node.block.tokens[idx+1]
-
-def check_escaping_calls(instr: parser.CodeDef, escapes: dict[lexer.Token, EscapingCall]) -> None:
+def check_escaping_calls(instr: parser.CodeDef, escapes: dict[SimpleStmt, EscapingCall]) -> None:
+    error: lexer.Token | None = None
     calls = {e.call for e in escapes.values()}
-    in_if = 0
-    tkn_iter = iter(instr.block.tokens)
-    for tkn in tkn_iter:
-        if tkn.kind == "IF":
-            next(tkn_iter)
-            in_if = 1
-        if tkn.kind == "IDENTIFIER" and tkn.text in ("DEOPT_IF", "ERROR_IF", "EXIT_IF"):
-            next(tkn_iter)
-            in_if = 1
-        elif tkn.kind == "LPAREN" and in_if:
-            in_if += 1
-        elif tkn.kind == "RPAREN":
-            if in_if:
-                in_if -= 1
-        elif tkn in calls and in_if:
-            raise analysis_error(f"Escaping call '{tkn.text} in condition", tkn)
-
-def find_escaping_api_calls(instr: parser.CodeDef) -> dict[lexer.Token, EscapingCall]:
-    result: dict[lexer.Token, EscapingCall] = {}
-    tokens = instr.block.tokens
-    for idx, tkn in enumerate(tokens):
-        try:
-            next_tkn = tokens[idx+1]
-        except IndexError:
-            break
-        if tkn.kind == "SWITCH":
-            raise analysis_error(f"switch statements are not supported due to their complex flow control. Sorry.", tkn)
-        if next_tkn.kind != lexer.LPAREN:
-            continue
-        if tkn.kind == lexer.IDENTIFIER:
-            if tkn.text.upper() == tkn.text:
-                # simple macro
-                continue
-            #if not tkn.text.startswith(("Py", "_Py", "monitor")):
-            #    continue
-            if tkn.text.startswith(("sym_", "optimize_")):
-                # Optimize functions
-                continue
-            if tkn.text.endswith("Check"):
-                continue
-            if tkn.text.startswith("Py_Is"):
-                continue
-            if tkn.text.endswith("CheckExact"):
-                continue
-            if tkn.text in NON_ESCAPING_FUNCTIONS:
+
+    def visit(stmt: Stmt) -> None:
+        nonlocal error
+        if isinstance(stmt, IfStmt) or isinstance(stmt, WhileStmt):
+            for tkn in stmt.condition:
+                if tkn in calls:
+                    error = tkn
+        elif isinstance(stmt, SimpleStmt):
+            in_if = 0
+            tkn_iter = iter(stmt.contents)
+            for tkn in tkn_iter:
+                if tkn.kind == "IDENTIFIER" and tkn.text in ("DEOPT_IF", "ERROR_IF", "EXIT_IF"):
+                    in_if = 1
+                    next(tkn_iter)
+                elif tkn.kind == "LPAREN":
+                    if in_if:
+                        in_if += 1
+                elif tkn.kind == "RPAREN":
+                    if in_if:
+                        in_if -= 1
+                elif tkn in calls and in_if:
+                    error = tkn
+
+
+    instr.block.accept(visit)
+    if error is not None:
+        raise analysis_error(f"Escaping call '{error.text} in condition", error)
+
+def find_escaping_api_calls(instr: parser.CodeDef) -> dict[SimpleStmt, EscapingCall]:
+    result: dict[SimpleStmt, EscapingCall] = {}
+
+    def visit(stmt: Stmt) -> None:
+        if not isinstance(stmt, SimpleStmt):
+            return
+        tokens = stmt.contents
+        for idx, tkn in enumerate(tokens):
+            try:
+                next_tkn = tokens[idx+1]
+            except IndexError:
+                break
+            if next_tkn.kind != lexer.LPAREN:
                 continue
-        elif tkn.kind == "RPAREN":
-            prev = tokens[idx-1]
-            if prev.text.endswith("_t") or prev.text == "*" or prev.text == "int":
-                #cast
+            if tkn.kind == lexer.IDENTIFIER:
+                if tkn.text.upper() == tkn.text:
+                    # simple macro
+                    continue
+                #if not tkn.text.startswith(("Py", "_Py", "monitor")):
+                #    continue
+                if tkn.text.startswith(("sym_", "optimize_")):
+                    # Optimize functions
+                    continue
+                if tkn.text.endswith("Check"):
+                    continue
+                if tkn.text.startswith("Py_Is"):
+                    continue
+                if tkn.text.endswith("CheckExact"):
+                    continue
+                if tkn.text in NON_ESCAPING_FUNCTIONS:
+                    continue
+            elif tkn.kind == "RPAREN":
+                prev = tokens[idx-1]
+                if prev.text.endswith("_t") or prev.text == "*" or prev.text == "int":
+                    #cast
+                    continue
+            elif tkn.kind != "RBRACKET":
                 continue
-        elif tkn.kind != "RBRACKET":
-            continue
-        if tkn.text in ("PyStackRef_CLOSE", "PyStackRef_XCLOSE"):
-            if len(tokens) <= idx+2:
-                raise analysis_error("Unexpected end of file", next_tkn)
-            kills = tokens[idx+2]
-            if kills.kind != "IDENTIFIER":
-                raise analysis_error(f"Expected identifier, got '{kills.text}'", kills)
-        else:
-            kills = None
-        start = find_stmt_start(instr, idx)
-        end = find_stmt_end(instr, idx)
-        result[start] = EscapingCall(start, tkn, end, kills)
+            if tkn.text in ("PyStackRef_CLOSE", "PyStackRef_XCLOSE"):
+                if len(tokens) <= idx+2:
+                    raise analysis_error("Unexpected end of file", next_tkn)
+                kills = tokens[idx+2]
+                if kills.kind != "IDENTIFIER":
+                    raise analysis_error(f"Expected identifier, got '{kills.text}'", kills)
+            else:
+                kills = None
+            result[stmt] = EscapingCall(stmt, tkn, kills)
+
+    instr.block.accept(visit)
     check_escaping_calls(instr, result)
     return result
 
@@ -876,9 +871,8 @@ def make_uop(
         annotations=op.annotations,
         stack=analyze_stack(op),
         caches=analyze_caches(inputs),
-        deferred_refs=analyze_deferred_refs(op),
         local_stores=find_variable_stores(op),
-        body=op.block.tokens,
+        body=op.block,
         properties=compute_properties(op),
     )
     for anno in op.annotations:
@@ -898,9 +892,8 @@ def make_uop(
             annotations=op.annotations,
             stack=analyze_stack(op),
             caches=analyze_caches(inputs),
-            deferred_refs=analyze_deferred_refs(op),
             local_stores=find_variable_stores(op),
-            body=op.block.tokens,
+            body=op.block,
             properties=properties,
         )
         rep.replicates = result
@@ -1015,7 +1008,7 @@ def add_label(
     labels: dict[str, Label],
 ) -> None:
     properties = compute_properties(label)
-    labels[label.name] = Label(label.name, label.spilled, label.block.tokens, properties)
+    labels[label.name] = Label(label.name, label.spilled, label.block, properties)
 
 
 def assign_opcodes(
@@ -1109,9 +1102,9 @@ def get_instruction_size_for_uop(instructions: dict[str, Instruction], uop: Uop)
     If there is more than one instruction that contains the uop,
     ensure that they all have the same size.
     """
-    for tkn in uop.body:
-          if tkn.text == "INSTRUCTION_SIZE":
-               break
+    for tkn in uop.body.tokens():
+        if tkn.text == "INSTRUCTION_SIZE":
+            break
     else:
         return None
 
index 2bda9fe73df83eda7bdbb08c06f41191576ebeff..6636755db55eecb2f31375ea7b3f8d69b9984541 100644 (file)
@@ -99,7 +99,7 @@ class CWriter:
         self.maybe_dedent(tkn.text)
         self.set_position(tkn)
         self.emit_text(tkn.text)
-        if tkn.kind == "CMACRO":
+        if tkn.kind.startswith("CMACRO"):
             self.newline = True
         self.maybe_indent(tkn.text)
 
index b192738849d18dce36e2b9d6a634323273068baa..4571811bcdff3c9c143f127a13d5d5bf36ce9283 100644 (file)
@@ -12,6 +12,7 @@ from cwriter import CWriter
 from typing import Callable, TextIO, Iterator, Iterable
 from lexer import Token
 from stack import Storage, StackError
+from parser import Stmt, SimpleStmt, BlockStmt, IfStmt, ForStmt, WhileStmt, MacroIfStmt
 
 # Set this to true for voluminous output showing state of stack and locals
 PRINT_STACKS = False
@@ -160,7 +161,7 @@ class Emitter:
         self.emit(") {\n")
         next(tkn_iter)  # Semi colon
         assert inst is not None
-        assert inst.family is not None
+        assert inst.family is not None, inst
         family_name = inst.family.name
         self.emit(f"UPDATE_MISS_STATS({family_name});\n")
         self.emit(f"assert(_PyOpcode_Deopt[opcode] == ({family_name}));\n")
@@ -458,119 +459,38 @@ class Emitter:
             self.emit(storage.as_comment())
             self.out.start_line()
 
-    def _emit_if(
+    def _emit_stmt(
         self,
-        tkn_iter: TokenIterator,
+        stmt: Stmt,
         uop: CodeSection,
         storage: Storage,
         inst: Instruction | None,
-    ) -> tuple[bool, Token, Storage]:
-        """Returns (reachable?, closing '}', stack)."""
-        tkn = next(tkn_iter)
-        assert tkn.kind == "LPAREN"
-        self.out.emit(tkn)
-        rparen = emit_to(self.out, tkn_iter, "RPAREN")
-        self.emit(rparen)
-        if_storage = storage.copy()
-        reachable, rbrace, if_storage = self._emit_block(tkn_iter, uop, if_storage, inst, True)
-        try:
-            maybe_else = tkn_iter.peek()
-            if maybe_else and maybe_else.kind == "ELSE":
-                self._print_storage(storage)
-                self.emit(rbrace)
-                self.emit(next(tkn_iter))
-                maybe_if = tkn_iter.peek()
-                if maybe_if and maybe_if.kind == "IF":
-                    # Emit extra braces around the if to get scoping right
-                    self.emit(" {\n")
-                    self.emit(next(tkn_iter))
-                    else_reachable, rbrace, else_storage = self._emit_if(tkn_iter, uop, storage, inst)
-                    self.out.start_line()
-                    self.emit("}\n")
-                else:
-                    else_reachable, rbrace, else_storage = self._emit_block(tkn_iter, uop, storage, inst, True)
-                if not reachable:
-                    # Discard the if storage
-                    reachable = else_reachable
-                    storage = else_storage
-                elif not else_reachable:
-                    # Discard the else storage
-                    storage = if_storage
-                    reachable = True
-                else:
-                    if PRINT_STACKS:
-                        self.emit("/* Merge */\n")
-                        self.out.emit(if_storage.as_comment())
-                        self.out.emit("\n")
-                        self.out.emit(else_storage.as_comment())
-                    else_storage.merge(if_storage, self.out)
-                    storage = else_storage
-                    self._print_storage(storage)
-            else:
-                if reachable:
-                    if PRINT_STACKS:
-                        self.emit("/* Merge */\n")
-                    if_storage.merge(storage, self.out)
-                    storage = if_storage
-                    self._print_storage(storage)
-                else:
-                    # Discard the if storage
-                    reachable = True
-        except StackError as ex:
-            self._print_storage(if_storage)
-            raise analysis_error(ex.args[0], rbrace) from None
-        return reachable, rbrace, storage
-
-    def _emit_block(
+    ) -> tuple[bool, Token | None, Storage]:
+        method_name = "emit_" + stmt.__class__.__name__
+        method = getattr(self, method_name, None)
+        if method is None:
+            raise NotImplementedError
+        return method(stmt, uop, storage, inst) # type: ignore[no-any-return]
+
+    def emit_SimpleStmt(
         self,
-        tkn_iter: TokenIterator,
+        stmt: SimpleStmt,
         uop: CodeSection,
         storage: Storage,
         inst: Instruction | None,
-        emit_first_brace: bool
-    ) -> tuple[bool, Token, Storage]:
-        """ Returns (reachable?, closing '}', stack)."""
-        braces = 1
+    ) -> tuple[bool, Token | None, Storage]:
         local_stores = set(uop.local_stores)
-        tkn = next(tkn_iter)
-        reload: Token | None = None
+        reachable = True
+        tkn = stmt.contents[-1]
         try:
-            reachable = True
-            line : int = -1
-            if tkn.kind != "LBRACE":
-                raise analysis_error(f"PEP 7: expected '{{', found: {tkn.text}", tkn)
-            escaping_calls = uop.properties.escaping_calls
-            if emit_first_brace:
-                self.emit(tkn)
-            self._print_storage(storage)
+            if stmt in uop.properties.escaping_calls:
+                escape = uop.properties.escaping_calls[stmt]
+                if escape.kills is not None:
+                    self.stackref_kill(escape.kills, storage, True)
+                self.emit_save(storage)
+            tkn_iter = TokenIterator(stmt.contents)
             for tkn in tkn_iter:
-                if PRINT_STACKS and tkn.line != line:
-                    self.out.start_line()
-                    self.emit(storage.as_comment())
-                    self.out.start_line()
-                    line = tkn.line
-                if tkn in escaping_calls:
-                    escape = escaping_calls[tkn]
-                    if escape.kills is not None:
-                        if tkn == reload:
-                            self.emit_reload(storage)
-                        self.stackref_kill(escape.kills, storage, True)
-                        self.emit_save(storage)
-                    elif tkn != reload:
-                        self.emit_save(storage)
-                    reload = escape.end
-                elif tkn == reload:
-                    self.emit_reload(storage)
-                if tkn.kind == "LBRACE":
-                    self.out.emit(tkn)
-                    braces += 1
-                elif tkn.kind == "RBRACE":
-                    self._print_storage(storage)
-                    braces -= 1
-                    if braces == 0:
-                        return reachable, tkn, storage
-                    self.out.emit(tkn)
-                elif tkn.kind == "GOTO":
+                if tkn.kind == "GOTO":
                     label_tkn = next(tkn_iter)
                     self.goto_label(tkn, label_tkn, storage)
                     reachable = False
@@ -597,34 +517,161 @@ class Emitter:
                             self._print_storage(storage)
                             reachable = False
                         self.out.emit(tkn)
-                elif tkn.kind == "IF":
+                else:
+                    self.out.emit(tkn)
+            if stmt in uop.properties.escaping_calls:
+                self.emit_reload(storage)
+            return reachable, None, storage
+        except StackError as ex:
+            raise analysis_error(ex.args[0], tkn) #from None
+
+
+    def emit_MacroIfStmt(
+        self,
+        stmt: MacroIfStmt,
+        uop: CodeSection,
+        storage: Storage,
+        inst: Instruction | None,
+    ) -> tuple[bool, Token | None, Storage]:
+        self.out.emit(stmt.condition)
+        branch = stmt.else_ is not None
+        reachable = True
+        for s in stmt.body:
+            r, tkn, storage = self._emit_stmt(s, uop, storage, inst)
+            if tkn is not None:
+                self.out.emit(tkn)
+            if not r:
+                reachable = False
+        if branch:
+            else_storage = storage.copy()
+            assert stmt.else_ is not None
+            self.out.emit(stmt.else_)
+            assert stmt.else_body is not None
+            for s in stmt.else_body:
+                r, tkn, else_storage = self._emit_stmt(s, uop, else_storage, inst)
+                if tkn is not None:
                     self.out.emit(tkn)
-                    if_reachable, rbrace, storage = self._emit_if(tkn_iter, uop, storage, inst)
-                    if reachable:
-                        reachable = if_reachable
-                    self.out.emit(rbrace)
+                if not r:
+                    reachable = False
+            storage.merge(else_storage, self.out)
+        self.out.emit(stmt.endif)
+        return reachable, None, storage
+
+
+    def emit_IfStmt(
+        self,
+        stmt: IfStmt,
+        uop: CodeSection,
+        storage: Storage,
+        inst: Instruction | None,
+    ) -> tuple[bool, Token | None, Storage]:
+        self.out.emit(stmt.if_)
+        for tkn in stmt.condition:
+            self.out.emit(tkn)
+        if_storage = storage.copy()
+        rbrace: Token | None = stmt.if_
+        try:
+            reachable, rbrace, if_storage = self._emit_stmt(stmt.body, uop, if_storage, inst)
+            if stmt.else_ is not None:
+                assert rbrace is not None
+                self.out.emit(rbrace)
+                self.out.emit(stmt.else_)
+            if stmt.else_body is not None:
+                else_reachable, rbrace, else_storage = self._emit_stmt(stmt.else_body, uop, storage, inst)
+                if not reachable:
+                    reachable, storage = else_reachable, else_storage
+                elif not else_reachable:
+                    # Discard the else storage
+                    storage = if_storage
                 else:
+                    #Both reachable
+                    else_storage.merge(if_storage, self.out)
+                    storage = else_storage
+            else:
+                if reachable:
+                    if_storage.merge(storage, self.out)
+                    storage = if_storage
+                else:
+                    # Discard the if storage
+                    reachable = True
+            return reachable, rbrace, storage
+        except StackError as ex:
+            self._print_storage(if_storage)
+            assert rbrace is not None
+            raise analysis_error(ex.args[0], rbrace) from None
+
+    def emit_BlockStmt(
+        self,
+        stmt: BlockStmt,
+        uop: CodeSection,
+        storage: Storage,
+        inst: Instruction | None,
+        emit_braces: bool = True,
+    ) -> tuple[bool, Token | None, Storage]:
+        """ Returns (reachable?, closing '}', stack)."""
+        tkn: Token | None = None
+        try:
+            if emit_braces:
+                self.out.emit(stmt.open)
+            reachable = True
+            for s in stmt.body:
+                reachable, tkn, storage = self._emit_stmt(s, uop, storage, inst)
+                if tkn is not None:
                     self.out.emit(tkn)
+                if not reachable:
+                    break
+            return reachable, stmt.close, storage
         except StackError as ex:
+            if tkn is None:
+                tkn = stmt.close
             raise analysis_error(ex.args[0], tkn) from None
-        raise analysis_error("Expecting closing brace. Reached end of file", tkn)
+
+    def emit_ForStmt(
+        self,
+        stmt: ForStmt,
+        uop: CodeSection,
+        storage: Storage,
+        inst: Instruction | None,
+    ) -> tuple[bool, Token | None, Storage]:
+        """ Returns (reachable?, closing '}', stack)."""
+        self.out.emit(stmt.for_)
+        for tkn in stmt.header:
+            self.out.emit(tkn)
+        return self._emit_stmt(stmt.body, uop, storage, inst)
+
+    def emit_WhileStmt(
+        self,
+        stmt: WhileStmt,
+        uop: CodeSection,
+        storage: Storage,
+        inst: Instruction | None,
+    ) -> tuple[bool, Token | None, Storage]:
+        """ Returns (reachable?, closing '}', stack)."""
+        self.out.emit(stmt.while_)
+        for tkn in stmt.condition:
+            self.out.emit(tkn)
+        return self._emit_stmt(stmt.body, uop, storage, inst)
+
 
     def emit_tokens(
         self,
         code: CodeSection,
         storage: Storage,
         inst: Instruction | None,
+        emit_braces: bool = True
     ) -> Storage:
-        tkn_iter = TokenIterator(code.body)
         self.out.start_line()
-        reachable, rbrace, storage = self._emit_block(tkn_iter, code, storage, inst, False)
+        reachable, tkn, storage = self.emit_BlockStmt(code.body, code, storage, inst, emit_braces)
+        assert tkn is not None
         try:
             if reachable:
                 self._print_storage(storage)
                 storage.push_outputs()
                 self._print_storage(storage)
+            if emit_braces:
+                self.out.emit(tkn)
         except StackError as ex:
-            raise analysis_error(ex.args[0], rbrace) from None
+            raise analysis_error(ex.args[0], tkn) from None
         return storage
 
     def emit(self, txt: str | Token) -> None:
index b4bcd73fdbfe520beee0d44db2e14d76ebf92fc4..79bb9c27a5560108dae1f651c435646e7e9614dd 100644 (file)
@@ -80,7 +80,10 @@ opmap = {pattern.replace("\\", "") or "\\": op for op, pattern in operators.item
 
 # Macros
 macro = r"#.*\n"
-CMACRO = "CMACRO"
+CMACRO_IF = "CMACRO_IF"
+CMACRO_ELSE = "CMACRO_ELSE"
+CMACRO_ENDIF = "CMACRO_ENDIF"
+CMACRO_OTHER = "CMACRO_OTHER"
 
 id_re = r"[a-zA-Z_][0-9a-zA-Z_]*"
 IDENTIFIER = "IDENTIFIER"
@@ -292,6 +295,7 @@ def tokenize(src: str, line: int = 1, filename: str = "") -> Iterator[Token]:
     linestart = -1
     for m in matcher.finditer(src):
         start, end = m.span()
+        macro_body = ""
         text = m.group(0)
         if text in keywords:
             kind = keywords[text]
@@ -316,7 +320,15 @@ def tokenize(src: str, line: int = 1, filename: str = "") -> Iterator[Token]:
         elif text[0] == "'":
             kind = CHARACTER
         elif text[0] == "#":
-            kind = CMACRO
+            macro_body = text[1:].strip()
+            if macro_body.startswith("if"):
+                kind = CMACRO_IF
+            elif macro_body.startswith("else"):
+                kind = CMACRO_ELSE
+            elif macro_body.startswith("endif"):
+                kind = CMACRO_ENDIF
+            else:
+                kind = CMACRO_OTHER
         elif text[0] == "/" and text[1] in "/*":
             kind = COMMENT
         else:
@@ -338,7 +350,7 @@ def tokenize(src: str, line: int = 1, filename: str = "") -> Iterator[Token]:
                 line += newlines
         else:
             begin = line, start - linestart
-            if kind == CMACRO:
+            if macro_body:
                 linestart = end
                 line += 1
         if kind != "\n":
index 182933e9fc4af42ca5d063ebd8f1c1185d5f0708..f515bb6fdc921370c18f8fec9c467e4f8f8df3f2 100644 (file)
@@ -145,7 +145,7 @@ def write_uop(
             # No reference management of inputs needed.
             for var in storage.inputs:  # type: ignore[possibly-undefined]
                 var.in_local = False
-            storage = emitter.emit_tokens(override, storage, None)
+            storage = emitter.emit_tokens(override, storage, None, False)
             out.start_line()
             storage.flush(out)
         else:
@@ -153,7 +153,7 @@ def write_uop(
             out.start_line()
             stack.flush(out)
     except StackError as ex:
-        raise analysis_error(ex.args[0], prototype.body[0]) # from None
+        raise analysis_error(ex.args[0], prototype.body.open) # from None
 
 
 SKIPS = ("_EXTENDED_ARG",)
index 696c5c16432990e10bd81c64c4c4bb67d72ccca4..4ec46d8cac6e4b5304e3118831b30d8fec0769b0 100644 (file)
@@ -11,8 +11,17 @@ from parsing import (  # noqa: F401
     InputEffect,
     OpName,
     AstNode,
+    Stmt,
+    SimpleStmt,
+    IfStmt,
+    ForStmt,
+    WhileStmt,
+    BlockStmt,
+    MacroIfStmt,
 )
 
+import pprint
+
 CodeDef = InstDef | LabelDef
 
 def prettify_filename(filename: str) -> str:
@@ -61,6 +70,7 @@ def parse_files(filenames: list[str]) -> list[AstNode]:
             assert node is not None
             result.append(node)  # type: ignore[arg-type]
         if not psr.eof():
+            pprint.pprint(result)
             psr.backup()
             raise psr.make_syntax_error(
                 f"Extra stuff at the end of {filename}", psr.next(True)
index 84aed49d491e015ee254b9f0a7ba610cbe488dc4..9c9b0053a5928b9e533e6eb4e1ac26ca57e72fc5 100644 (file)
@@ -1,10 +1,12 @@
 """Parser for bytecodes.inst."""
 
 from dataclasses import dataclass, field
-from typing import NamedTuple, Callable, TypeVar, Literal, cast
+from typing import NamedTuple, Callable, TypeVar, Literal, cast, Iterator
+from io import StringIO
 
 import lexer as lx
 from plexer import PLexer
+from cwriter import CWriter
 
 
 P = TypeVar("P", bound="Parser")
@@ -66,12 +68,181 @@ class Node:
         assert context is not None
         return context.owner.tokens[context.begin]
 
+# Statements
+
+Visitor = Callable[["Stmt"], None]
+
+class Stmt:
+
+    def __repr__(self) -> str:
+        io = StringIO()
+        out = CWriter(io, 0, False)
+        self.print(out)
+        return io.getvalue()
+
+    def print(self, out:CWriter) -> None:
+        raise NotImplementedError
+
+    def accept(self, visitor: Visitor) -> None:
+        raise NotImplementedError
+
+    def tokens(self) -> Iterator[lx.Token]:
+        raise NotImplementedError
+
+
+@dataclass
+class IfStmt(Stmt):
+    if_: lx.Token
+    condition: list[lx.Token]
+    body: Stmt
+    else_: lx.Token | None
+    else_body: Stmt | None
+
+    def print(self, out:CWriter) -> None:
+        out.emit(self.if_)
+        for tkn in self.condition:
+            out.emit(tkn)
+        self.body.print(out)
+        if self.else_ is not None:
+            out.emit(self.else_)
+        self.body.print(out)
+        if self.else_body is not None:
+            self.else_body.print(out)
+
+    def accept(self, visitor: Visitor) -> None:
+        visitor(self)
+        self.body.accept(visitor)
+        if self.else_body is not None:
+            self.else_body.accept(visitor)
+
+    def tokens(self) -> Iterator[lx.Token]:
+        yield self.if_
+        yield from self.condition
+        yield from self.body.tokens()
+        if self.else_ is not None:
+            yield self.else_
+        if self.else_body is not None:
+            yield from self.else_body.tokens()
+
+
+@dataclass
+class ForStmt(Stmt):
+    for_: lx.Token
+    header: list[lx.Token]
+    body: Stmt
+
+    def print(self, out:CWriter) -> None:
+        out.emit(self.for_)
+        for tkn in self.header:
+            out.emit(tkn)
+        self.body.print(out)
+
+    def accept(self, visitor: Visitor) -> None:
+        visitor(self)
+        self.body.accept(visitor)
+
+    def tokens(self) -> Iterator[lx.Token]:
+        yield self.for_
+        yield from self.header
+        yield from self.body.tokens()
+
+
+@dataclass
+class WhileStmt(Stmt):
+    while_: lx.Token
+    condition: list[lx.Token]
+    body: Stmt
+
+    def print(self, out:CWriter) -> None:
+        out.emit(self.while_)
+        for tkn in self.condition:
+            out.emit(tkn)
+        self.body.print(out)
+
+    def accept(self, visitor: Visitor) -> None:
+        visitor(self)
+        self.body.accept(visitor)
+
+    def tokens(self) -> Iterator[lx.Token]:
+        yield self.while_
+        yield from self.condition
+        yield from self.body.tokens()
+
+
+@dataclass
+class MacroIfStmt(Stmt):
+    condition: lx.Token
+    body: list[Stmt]
+    else_: lx.Token | None
+    else_body: list[Stmt] | None
+    endif: lx.Token
+
+    def print(self, out:CWriter) -> None:
+        out.emit(self.condition)
+        for stmt in self.body:
+            stmt.print(out)
+        if self.else_body is not None:
+            out.emit("#else\n")
+            for stmt in self.else_body:
+                stmt.print(out)
+
+    def accept(self, visitor: Visitor) -> None:
+        visitor(self)
+        for stmt in self.body:
+            stmt.accept(visitor)
+        if self.else_body is not None:
+            for stmt in self.else_body:
+                stmt.accept(visitor)
+
+    def tokens(self) -> Iterator[lx.Token]:
+        yield self.condition
+        for stmt in self.body:
+            yield from stmt.tokens()
+        if self.else_body is not None:
+            for stmt in self.else_body:
+                yield from stmt.tokens()
+
 
 @dataclass
-class Block(Node):
-    # This just holds a context which has the list of tokens.
-    pass
+class BlockStmt(Stmt):
+    open: lx.Token
+    body: list[Stmt]
+    close: lx.Token
+
+    def print(self, out:CWriter) -> None:
+        out.emit(self.open)
+        for stmt in self.body:
+            stmt.print(out)
+        out.start_line()
+        out.emit(self.close)
+
+    def accept(self, visitor: Visitor) -> None:
+        visitor(self)
+        for stmt in self.body:
+            stmt.accept(visitor)
+
+    def tokens(self) -> Iterator[lx.Token]:
+        yield self.open
+        for stmt in self.body:
+            yield from stmt.tokens()
+        yield self.close
+
+
+@dataclass
+class SimpleStmt(Stmt):
+    contents: list[lx.Token]
+
+    def print(self, out:CWriter) -> None:
+        for tkn in self.contents:
+            out.emit(tkn)
+
+    def tokens(self) -> Iterator[lx.Token]:
+        yield from self.contents
+
+    def accept(self, visitor: Visitor) -> None:
+        visitor(self)
 
+    __hash__ = object.__hash__
 
 @dataclass
 class StackEffect(Node):
@@ -124,7 +295,7 @@ class InstDef(Node):
     name: str
     inputs: list[InputEffect]
     outputs: list[OutputEffect]
-    block: Block
+    block: BlockStmt
 
 
 @dataclass
@@ -153,7 +324,7 @@ class Pseudo(Node):
 class LabelDef(Node):
     name: str
     spilled: bool
-    block: Block
+    block: BlockStmt
 
 
 AstNode = InstDef | Macro | Pseudo | Family | LabelDef
@@ -183,23 +354,22 @@ class Parser(PLexer):
             if self.expect(lx.LPAREN):
                 if tkn := self.expect(lx.IDENTIFIER):
                     if self.expect(lx.RPAREN):
-                        if block := self.block():
-                            return LabelDef(tkn.text, spilled, block)
+                        block = self.block()
+                        return LabelDef(tkn.text, spilled, block)
         return None
 
     @contextual
     def inst_def(self) -> InstDef | None:
         if hdr := self.inst_header():
-            if block := self.block():
-                return InstDef(
-                    hdr.annotations,
-                    hdr.kind,
-                    hdr.name,
-                    hdr.inputs,
-                    hdr.outputs,
-                    block,
-                )
-            raise self.make_syntax_error("Expected block")
+            block = self.block()
+            return InstDef(
+                hdr.annotations,
+                hdr.kind,
+                hdr.name,
+                hdr.inputs,
+                hdr.outputs,
+                block,
+            )
         return None
 
     @contextual
@@ -473,28 +643,85 @@ class Parser(PLexer):
         self.setpos(here)
         return None
 
-    @contextual
-    def block(self) -> Block | None:
-        if self.c_blob():
-            return Block()
-        return None
+    def block(self) -> BlockStmt:
+        open = self.require(lx.LBRACE)
+        stmts: list[Stmt] = []
+        while not (close := self.expect(lx.RBRACE)):
+            stmts.append(self.stmt())
+        return BlockStmt(open, stmts, close)
+
+    def stmt(self) -> Stmt:
+        if tkn := self.expect(lx.IF):
+            return self.if_stmt(tkn)
+        elif self.expect(lx.LBRACE):
+            self.backup()
+            return self.block()
+        elif tkn := self.expect(lx.FOR):
+            return self.for_stmt(tkn)
+        elif tkn := self.expect(lx.WHILE):
+            return self.while_stmt(tkn)
+        elif tkn := self.expect(lx.CMACRO_IF):
+            return self.macro_if(tkn)
+        elif tkn := self.expect(lx.CMACRO_ELSE):
+            msg = "Unexpected #else"
+            raise self.make_syntax_error(msg)
+        elif tkn := self.expect(lx.CMACRO_ENDIF):
+            msg = "Unexpected #endif"
+            raise self.make_syntax_error(msg)
+        elif tkn := self.expect(lx.CMACRO_OTHER):
+            return SimpleStmt([tkn])
+        elif tkn := self.expect(lx.SWITCH):
+            msg = "switch statements are not supported due to their complex flow control. Sorry."
+            raise self.make_syntax_error(msg)
+        tokens = self.consume_to(lx.SEMI)
+        return SimpleStmt(tokens)
+
+    def if_stmt(self, if_: lx.Token) -> IfStmt:
+        lparen = self.require(lx.LPAREN)
+        condition = [lparen] + self.consume_to(lx.RPAREN)
+        body = self.block()
+        else_body: Stmt | None = None
+        else_: lx.Token | None = None
+        if else_ := self.expect(lx.ELSE):
+            if inner := self.expect(lx.IF):
+                else_body = self.if_stmt(inner)
+            else:
+                else_body = self.block()
+        return IfStmt(if_, condition, body, else_, else_body)
+
+    def macro_if(self, cond: lx.Token) -> MacroIfStmt:
+        else_ = None
+        body: list[Stmt] = []
+        else_body: list[Stmt] | None = None
+        part = body
+        while True:
+            if tkn := self.expect(lx.CMACRO_ENDIF):
+                return MacroIfStmt(cond, body, else_, else_body, tkn)
+            elif tkn := self.expect(lx.CMACRO_ELSE):
+                if part is else_body:
+                    raise self.make_syntax_error("Multiple #else")
+                else_ = tkn
+                else_body = []
+                part = else_body
+            else:
+                part.append(self.stmt())
 
-    def c_blob(self) -> list[lx.Token]:
-        tokens: list[lx.Token] = []
-        level = 0
-        while tkn := self.next(raw=True):
-            tokens.append(tkn)
-            if tkn.kind in (lx.LBRACE, lx.LPAREN, lx.LBRACKET):
-                level += 1
-            elif tkn.kind in (lx.RBRACE, lx.RPAREN, lx.RBRACKET):
-                level -= 1
-                if level <= 0:
-                    break
-        return tokens
+    def for_stmt(self, for_: lx.Token) -> ForStmt:
+        lparen = self.require(lx.LPAREN)
+        header = [lparen] + self.consume_to(lx.RPAREN)
+        body = self.block()
+        return ForStmt(for_, header, body)
+
+    def while_stmt(self, while_: lx.Token) -> WhileStmt:
+        lparen = self.require(lx.LPAREN)
+        cond = [lparen] + self.consume_to(lx.RPAREN)
+        body = self.block()
+        return WhileStmt(while_, cond, body)
 
 
 if __name__ == "__main__":
     import sys
+    import pprint
 
     if sys.argv[1:]:
         filename = sys.argv[1]
@@ -512,5 +739,5 @@ if __name__ == "__main__":
         filename = "<default>"
         src = "if (x) { x.foo; // comment\n}"
     parser = Parser(src, filename)
-    x = parser.definition()
-    print(x)
+    while node := parser.definition():
+        pprint.pprint(node)
index cb6c5375866490dd25295f9f6f71a0e7fa581991..95a68cf156211181ebc366e3a7cb76dc27593a66 100644 (file)
@@ -69,6 +69,20 @@ class PLexer:
             f"Expected {kind!r} but got {tkn and tkn.text!r}", tkn
         )
 
+    def consume_to(self, end: str) -> list[Token]:
+        res: list[Token] = []
+        parens = 0
+        while tkn := self.next(raw=True):
+            res.append(tkn)
+            if tkn.kind == end and parens == 0:
+                return res
+            if tkn.kind == "LPAREN":
+                parens += 1
+            if tkn.kind == "RPAREN":
+                parens -= 1
+        raise self.make_syntax_error(
+            f"Expected {end!r} but reached EOF", tkn)
+
     def extract_line(self, lineno: int) -> str:
         # Return source line `lineno` (1-based)
         lines = self.src.splitlines()
index f784b6b3371908ff9091c2b7e823c7a883efa6cc..ccf2dfe2d2e68440f66c4f50a742ca7175b978e0 100644 (file)
@@ -79,38 +79,35 @@ def write_uop(
         emitter.emit(f"// flush\n")
         stack.flush(emitter.out)
         return offset, stack
-    try:
-        locals: dict[str, Local] = {}
-        emitter.out.start_line()
-        if braces:
-            emitter.out.emit(f"// {uop.name}\n")
-            emitter.emit("{\n")
-        storage = Storage.for_uop(stack, uop, emitter.out)
-        emitter._print_storage(storage)
+    locals: dict[str, Local] = {}
+    emitter.out.start_line()
+    if braces:
+        emitter.out.emit(f"// {uop.name}\n")
+        emitter.emit("{\n")
+    storage = Storage.for_uop(stack, uop, emitter.out)
+    emitter._print_storage(storage)
+
+    for cache in uop.caches:
+        if cache.name != "unused":
+            if cache.size == 4:
+                type = "PyObject *"
+                reader = "read_obj"
+            else:
+                type = f"uint{cache.size*16}_t "
+                reader = f"read_u{cache.size*16}"
+            emitter.emit(
+                f"{type}{cache.name} = {reader}(&this_instr[{offset}].cache);\n"
+            )
+            if inst.family is None:
+                emitter.emit(f"(void){cache.name};\n")
+        offset += cache.size
 
-        for cache in uop.caches:
-            if cache.name != "unused":
-                if cache.size == 4:
-                    type = "PyObject *"
-                    reader = "read_obj"
-                else:
-                    type = f"uint{cache.size*16}_t "
-                    reader = f"read_u{cache.size*16}"
-                emitter.emit(
-                    f"{type}{cache.name} = {reader}(&this_instr[{offset}].cache);\n"
-                )
-                if inst.family is None:
-                    emitter.emit(f"(void){cache.name};\n")
-            offset += cache.size
-
-        storage = emitter.emit_tokens(uop, storage, inst)
-        if braces:
-            emitter.out.start_line()
-            emitter.emit("}\n")
-        # emitter.emit(stack.as_comment() + "\n")
-        return offset, storage.stack
-    except StackError as ex:
-        raise analysis_error(ex.args[0], uop.body[0])
+    storage = emitter.emit_tokens(uop, storage, inst, False)
+    if braces:
+        emitter.out.start_line()
+        emitter.emit("}\n")
+    # emitter.emit(stack.as_comment() + "\n")
+    return offset, storage.stack
 
 
 def uses_this(inst: Instruction) -> bool:
@@ -127,7 +124,7 @@ def uses_this(inst: Instruction) -> bool:
     for uop in inst.parts:
         if not isinstance(uop, Uop):
             continue
-        for tkn in uop.body:
+        for tkn in uop.body.tokens():
             if (tkn.kind == "IDENTIFIER"
                     and (tkn.text in {"DEOPT_IF", "EXIT_IF"})):
                 return True
@@ -201,15 +198,11 @@ def generate_tier1_labels(
     # Emit tail-callable labels as function defintions
     for name, label in analysis.labels.items():
         emitter.emit(f"LABEL({name})\n")
-        emitter.emit("{\n")
         storage = Storage(Stack(), [], [], False)
         if label.spilled:
             storage.spilled = 1
-            emitter.emit("/* STACK SPILLED */\n")
         emitter.emit_tokens(label, storage, None)
-        emitter.emit("\n")
-        emitter.emit("}\n")
-        emitter.emit("\n")
+        emitter.emit("\n\n")
 
 
 def generate_tier1_cases(
index 8c78388ba534814f6baf61b9453a9c07ef038fab..75b0d5cb51072cef9e0402ff4870e0778ecf74f9 100644 (file)
@@ -154,10 +154,10 @@ def write_uop(uop: Uop, emitter: Emitter, stack: Stack) -> Stack:
                     cast = f"uint{cache.size*16}_t"
                 emitter.emit(f"{type}{cache.name} = ({cast})CURRENT_OPERAND{idx}();\n")
                 idx += 1
-        storage = emitter.emit_tokens(uop, storage, None)
+        storage = emitter.emit_tokens(uop, storage, None, False)
         storage.flush(emitter.out)
     except StackError as ex:
-        raise analysis_error(ex.args[0], uop.body[0]) from None
+        raise analysis_error(ex.args[0], uop.body.open) from None
     return storage.stack
 
 SKIPS = ("_EXTENDED_ARG",)