diff --git a/Lib/test/test_generated_cases.py b/Lib/test/test_generated_cases.py index 3f396e4da3ef7d..302e69de285ca8 100644 --- a/Lib/test/test_generated_cases.py +++ b/Lib/test/test_generated_cases.py @@ -457,7 +457,6 @@ def test_error_if_plain_with_comment(self): if (cond) { JUMP_TO_LABEL(label); } - // Comment is ok DISPATCH(); } """ @@ -586,7 +585,6 @@ def test_suppress_dispatch(self): LABEL(somewhere) { - } """ self.run_cases_test(input, output) @@ -1351,7 +1349,6 @@ def test_pop_on_error_peeks(self): } // THIRD { - // Mark j and k as used if (cond) { JUMP_TO_LABEL(pop_2_error); } @@ -1757,17 +1754,14 @@ def test_complex_label(self): 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 @@ def test_spilled_label(self): output = """ LABEL(one) { - /* STACK SPILLED */ stack_pointer = _PyFrame_GetStackPointer(frame); JUMP_TO_LABEL(two); } @@ -1851,7 +1844,6 @@ def test_multiple_labels(self): output = """ LABEL(my_label_1) { - // Comment _PyFrame_SetStackPointer(frame, stack_pointer); do_thing1(); stack_pointer = _PyFrame_GetStackPointer(frame); @@ -1860,7 +1852,6 @@ def test_multiple_labels(self): LABEL(my_label_2) { - // Comment _PyFrame_SetStackPointer(frame, stack_pointer); do_thing2(); stack_pointer = _PyFrame_GetStackPointer(frame); diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 8d7e137c82943a..b2900ba951a52f 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -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 diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index ca64d7557e33d5..ccdf74a575baa2 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -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); @@ -601,11 +601,6 @@ 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); @@ -749,7 +744,6 @@ _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(); @@ -1111,17 +1105,6 @@ 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); @@ -1139,8 +1122,7 @@ 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 @@ -1213,8 +1195,6 @@ 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; } @@ -1303,7 +1283,6 @@ 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(); @@ -1364,7 +1343,6 @@ 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); @@ -1398,7 +1376,6 @@ 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(); @@ -1461,7 +1438,6 @@ if (rc <= 0) { JUMP_TO_ERROR(); } - // not found or error res = PyStackRef_FromPyObjectSteal(res_o); stack_pointer[0] = res; stack_pointer += 1; @@ -1567,7 +1543,6 @@ 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; @@ -1605,7 +1580,6 @@ 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(); @@ -1615,7 +1589,6 @@ UOP_STAT_INC(uopcode, miss); JUMP_TO_JUMP_TARGET(); } - // Ensure index < len(list) if (index >= PyList_GET_SIZE(list)) { UNLOCK_OBJECT(list); if (true) { @@ -1628,7 +1601,7 @@ 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()); @@ -1673,7 +1646,6 @@ _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)); @@ -1762,7 +1734,6 @@ _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); @@ -1906,9 +1877,6 @@ _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); @@ -1925,7 +1893,6 @@ _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 || @@ -1962,7 +1929,6 @@ 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; @@ -2049,7 +2015,6 @@ _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, @@ -2268,7 +2233,6 @@ _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(); } @@ -2459,8 +2423,6 @@ 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) { @@ -2477,8 +2439,6 @@ 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); @@ -2568,7 +2528,6 @@ 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); @@ -2825,7 +2784,6 @@ 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); @@ -2936,8 +2894,6 @@ 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, @@ -3039,7 +2995,7 @@ 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()); @@ -3082,26 +3038,15 @@ 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); @@ -3116,7 +3061,6 @@ } } else { - /* Classic, pushes one value. */ _PyFrame_SetStackPointer(frame, stack_pointer); attr_o = PyObject_GetAttr(PyStackRef_AsPyObjectBorrow(owner), name); stack_pointer = _PyFrame_GetStackPointer(frame); @@ -3198,7 +3142,8 @@ 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(); @@ -3311,7 +3256,8 @@ } 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(); @@ -3529,8 +3475,6 @@ 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()); @@ -3623,12 +3567,10 @@ 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()); @@ -3657,12 +3599,10 @@ _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()); @@ -3687,7 +3627,6 @@ 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()); @@ -3767,7 +3706,6 @@ 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; @@ -4002,7 +3940,6 @@ _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); @@ -4029,8 +3966,6 @@ 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, @@ -4053,15 +3988,14 @@ 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; @@ -4099,7 +4033,6 @@ _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)); @@ -4118,7 +4051,6 @@ _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); @@ -4141,13 +4073,9 @@ _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 " @@ -4157,26 +4085,23 @@ } 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; @@ -4188,7 +4113,6 @@ _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); @@ -4206,15 +4130,12 @@ _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()); @@ -4290,8 +4211,6 @@ _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(); @@ -4440,10 +4359,7 @@ 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(); @@ -4460,7 +4376,6 @@ 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; @@ -4513,15 +4428,6 @@ 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); @@ -4532,7 +4438,7 @@ 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); @@ -4607,7 +4513,6 @@ 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)); @@ -4690,7 +4595,6 @@ 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(); @@ -4755,7 +4659,6 @@ 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--; @@ -4770,7 +4673,6 @@ 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) { @@ -4895,7 +4797,6 @@ 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); @@ -5174,8 +5075,6 @@ 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; @@ -5365,7 +5264,6 @@ 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( @@ -5381,9 +5279,6 @@ } 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; @@ -5493,7 +5388,6 @@ 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])) { @@ -5512,7 +5406,6 @@ 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(); @@ -5552,7 +5445,6 @@ 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; @@ -5570,7 +5462,6 @@ } 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); @@ -5634,7 +5525,6 @@ 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; @@ -5651,7 +5541,6 @@ JUMP_TO_JUMP_TARGET(); } STAT_INC(CALL, hit); - /* res = func(self, arguments, nargs, kwnames) */ _PyFrame_SetStackPointer(frame, stack_pointer); PyCFunctionFastWithKeywords cfunc = (PyCFunctionFastWithKeywords)(void(*)(void)) @@ -5717,7 +5606,6 @@ 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])) { @@ -5771,7 +5659,6 @@ 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; @@ -5860,8 +5747,7 @@ 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 @@ -5898,7 +5784,6 @@ 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(); @@ -6068,7 +5953,6 @@ 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(); @@ -6115,7 +5999,6 @@ 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(); @@ -6229,7 +6112,6 @@ 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])) { @@ -6252,8 +6134,6 @@ _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) { @@ -6368,7 +6248,6 @@ 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); @@ -6617,8 +6496,6 @@ _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); @@ -7012,7 +6889,6 @@ 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); } diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 41ea054d3f5eea..c75371d12b0ba1 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -389,17 +389,6 @@ 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); @@ -417,8 +406,7 @@ 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 @@ -595,7 +583,6 @@ if (rc <= 0) { JUMP_TO_LABEL(error); } - // not found or error res = PyStackRef_FromPyObjectSteal(res_o); stack_pointer[0] = res; stack_pointer += 1; @@ -670,8 +657,6 @@ } // _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; @@ -726,7 +711,6 @@ 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)); @@ -831,7 +815,6 @@ 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); @@ -892,7 +875,6 @@ 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)); @@ -1055,7 +1037,6 @@ _PyStackRef res; // _SPECIALIZE_BINARY_SLICE { - // Placeholder until we implement BINARY_SLICE specialization #if ENABLE_SPECIALIZATION OPCODE_DEFERRED_INC(BINARY_SLICE); #endif /* ENABLE_SPECIALIZATION */ @@ -1070,8 +1051,6 @@ 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; } @@ -1406,14 +1385,12 @@ { 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) @@ -1426,18 +1403,14 @@ 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); @@ -1620,7 +1593,6 @@ 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( @@ -1636,16 +1608,11 @@ } 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); @@ -1781,8 +1748,6 @@ } // _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; @@ -1871,7 +1836,6 @@ { 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--; @@ -1886,7 +1850,6 @@ 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) { @@ -1905,8 +1868,6 @@ } // _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); @@ -2053,7 +2014,6 @@ 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; @@ -2073,7 +2033,6 @@ } 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); @@ -2169,7 +2128,6 @@ 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; @@ -2188,7 +2146,6 @@ JUMP_TO_PREDICTED(CALL); } STAT_INC(CALL, hit); - /* res = func(self, arguments, nargs, kwnames) */ _PyFrame_SetStackPointer(frame, stack_pointer); PyCFunctionFastWithKeywords cfunc = (PyCFunctionFastWithKeywords)(void(*)(void)) @@ -2286,7 +2243,6 @@ 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])) { @@ -2308,7 +2264,6 @@ 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)); @@ -2422,8 +2377,6 @@ 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)); @@ -2487,7 +2440,6 @@ 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) { @@ -2640,7 +2592,6 @@ 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; @@ -2751,7 +2702,6 @@ 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])) { @@ -2759,7 +2709,6 @@ 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) @@ -2778,9 +2727,6 @@ _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); } @@ -2788,7 +2734,6 @@ 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; @@ -2948,7 +2893,6 @@ 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])) { @@ -2971,8 +2915,6 @@ _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) { @@ -2991,8 +2933,6 @@ } // _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); @@ -3056,7 +2996,6 @@ 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); @@ -3188,7 +3127,6 @@ 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])) { @@ -3211,8 +3149,6 @@ _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) { @@ -3231,8 +3167,6 @@ } // _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); @@ -3267,7 +3201,6 @@ 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])) { @@ -3370,8 +3303,7 @@ 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 @@ -3408,7 +3340,6 @@ 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)); @@ -3675,7 +3606,6 @@ 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)); @@ -3771,7 +3701,6 @@ 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)); @@ -3886,7 +3815,6 @@ 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); @@ -4054,8 +3982,6 @@ } // _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; @@ -4118,7 +4044,6 @@ 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--; @@ -4133,7 +4058,6 @@ 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) { @@ -4152,8 +4076,6 @@ } // _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); @@ -4643,12 +4565,10 @@ 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; @@ -4713,12 +4633,10 @@ _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; @@ -4781,7 +4699,6 @@ 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; @@ -4922,7 +4839,6 @@ 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; @@ -5005,7 +4921,6 @@ 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); @@ -5053,8 +4968,6 @@ 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); @@ -5106,7 +5019,6 @@ _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); } @@ -5141,7 +5053,6 @@ _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, @@ -5165,7 +5076,6 @@ _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)); @@ -5282,7 +5192,7 @@ _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)); @@ -5322,11 +5232,6 @@ 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); @@ -5377,9 +5282,6 @@ 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; @@ -5450,8 +5352,6 @@ _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); @@ -5545,7 +5445,6 @@ } // _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); @@ -5563,15 +5462,12 @@ _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; @@ -5612,10 +5508,6 @@ 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)); @@ -5634,14 +5526,11 @@ 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); @@ -5698,10 +5587,6 @@ { 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; @@ -5717,7 +5602,6 @@ Py_DECREF(seq); stack_pointer = _PyFrame_GetStackPointer(frame); } - /* Jump forward oparg, then skip following END_FOR instruction */ JUMPBY(oparg + 1); DISPATCH(); } @@ -5738,8 +5622,6 @@ _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)); @@ -5747,7 +5629,6 @@ } if (result == 0) { it->it_index = -1; - /* Jump forward oparg, then skip following END_FOR instruction */ JUMPBY(oparg + 1); DISPATCH(); } @@ -5803,7 +5684,6 @@ #endif STAT_INC(FOR_ITER, hit); if (r->len <= 0) { - // Jump over END_FOR instruction. JUMPBY(oparg + 1); DISPATCH(); } @@ -5882,7 +5762,7 @@ stack_pointer = _PyFrame_GetStackPointer(frame); } #endif - /* Jump forward oparg, then skip following END_FOR instruction */ + JUMPBY(oparg + 1); DISPATCH(); } @@ -6031,7 +5911,6 @@ _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); @@ -6061,7 +5940,6 @@ _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); @@ -6090,13 +5968,9 @@ _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 " @@ -6106,26 +5980,23 @@ } 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(); @@ -6241,13 +6112,11 @@ 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( @@ -6264,14 +6133,12 @@ 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) @@ -6284,18 +6151,14 @@ 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); @@ -6455,8 +6318,6 @@ 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)); @@ -6520,7 +6381,6 @@ 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) { @@ -6631,13 +6491,11 @@ 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; @@ -6655,7 +6513,6 @@ 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])) { @@ -6663,7 +6520,6 @@ 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) @@ -6681,9 +6537,6 @@ _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); } @@ -6691,7 +6544,6 @@ 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); @@ -6796,7 +6648,7 @@ { 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)); @@ -6841,8 +6693,6 @@ _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)); @@ -6926,10 +6776,8 @@ _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(); @@ -7041,8 +6889,6 @@ } 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; @@ -7101,8 +6947,6 @@ 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); @@ -7182,7 +7026,7 @@ 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(); } @@ -7324,8 +7168,6 @@ 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(); } @@ -7352,7 +7194,7 @@ { 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); @@ -7373,7 +7215,6 @@ JUMP_TO_LABEL(error); } if (frame->instr_ptr != this_instr) { - /* Instrumentation has jumped */ next_instr = frame->instr_ptr; } } @@ -7415,7 +7256,6 @@ _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); @@ -7462,9 +7302,6 @@ // _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); @@ -7481,7 +7318,6 @@ _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 || @@ -7514,13 +7350,11 @@ 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; } @@ -7574,7 +7408,6 @@ #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(); } @@ -7595,11 +7428,6 @@ } // _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); } @@ -7633,11 +7461,6 @@ } // _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); } @@ -7647,7 +7470,6 @@ _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--; @@ -7687,11 +7509,6 @@ 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(); @@ -7722,11 +7539,6 @@ } // _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); } @@ -7853,26 +7665,15 @@ 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); @@ -7887,7 +7688,6 @@ } } else { - /* Classic, pushes one value. */ _PyFrame_SetStackPointer(frame, stack_pointer); attr_o = PyObject_GetAttr(PyStackRef_AsPyObjectBorrow(owner), name); stack_pointer = _PyFrame_GetStackPointer(frame); @@ -8086,7 +7886,6 @@ 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); @@ -8144,7 +7943,8 @@ 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)); @@ -8205,7 +8005,6 @@ 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)); @@ -8330,7 +8129,6 @@ { 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)); @@ -8610,8 +8408,6 @@ } // _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; @@ -8778,7 +8574,8 @@ } 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)); @@ -8845,7 +8642,6 @@ 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; @@ -8866,8 +8662,6 @@ _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 @@ -8876,7 +8670,6 @@ 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 @@ -9155,8 +8948,6 @@ 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); @@ -9166,8 +8957,6 @@ } } 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); @@ -9175,7 +8964,6 @@ 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); @@ -9578,8 +9366,6 @@ 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); @@ -9756,7 +9542,7 @@ 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()); @@ -9797,8 +9583,6 @@ 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) { @@ -9861,8 +9645,6 @@ 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, @@ -9893,8 +9675,6 @@ 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, @@ -9917,15 +9697,14 @@ 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; @@ -9946,7 +9725,6 @@ _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)); @@ -10362,8 +10140,6 @@ 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(); } @@ -10398,7 +10174,7 @@ { 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); @@ -10507,7 +10283,6 @@ _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); @@ -10677,8 +10452,6 @@ // _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; @@ -10711,7 +10484,6 @@ 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); @@ -11092,8 +10864,6 @@ 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()); @@ -11299,7 +11069,6 @@ _PyStackRef stop; // _SPECIALIZE_STORE_SLICE { - // Placeholder until we implement STORE_SLICE specialization #if ENABLE_SPECIALIZATION OPCODE_DEFERRED_INC(STORE_SLICE); #endif /* ENABLE_SPECIALIZATION */ @@ -11384,7 +11153,6 @@ // _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; @@ -11488,7 +11256,6 @@ 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)); @@ -11500,7 +11267,6 @@ 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) { @@ -11514,7 +11280,7 @@ 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()); @@ -11754,7 +11520,6 @@ /* 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)); @@ -12117,15 +11882,6 @@ 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); @@ -12136,7 +11892,7 @@ 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); @@ -12165,9 +11921,6 @@ _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); @@ -12184,7 +11937,6 @@ _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); diff --git a/Python/optimizer_bytecodes.c b/Python/optimizer_bytecodes.c index a948cccbf858ac..da36704d91e4b3 100644 --- a/Python/optimizer_bytecodes.c +++ b/Python/optimizer_bytecodes.c @@ -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); } } diff --git a/Python/optimizer_cases.c.h b/Python/optimizer_cases.c.h index e306567c81253b..ea25b8224a459b 100644 --- a/Python/optimizer_cases.c.h +++ b/Python/optimizer_cases.c.h @@ -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; } @@ -162,7 +161,8 @@ 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; @@ -173,7 +173,8 @@ 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); } @@ -185,7 +186,8 @@ 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); } @@ -197,7 +199,8 @@ 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); } @@ -209,7 +212,8 @@ 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); } @@ -241,7 +245,8 @@ 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; @@ -301,8 +306,6 @@ 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); @@ -332,8 +335,6 @@ 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); @@ -363,8 +364,6 @@ 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); @@ -415,8 +414,6 @@ 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); @@ -447,8 +444,6 @@ 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); @@ -479,8 +474,6 @@ 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); @@ -538,7 +531,6 @@ else { res = sym_new_type(ctx, &PyUnicode_Type); } - // _STORE_FAST: GETLOCAL(this_instr->operand0) = res; stack_pointer += -2; assert(WITHIN_STACK_BOUNDS()); @@ -690,7 +682,6 @@ 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; @@ -699,7 +690,6 @@ curr_space -= framesize; co = get_code(this_instr); if (co == NULL) { - // might be impossible, but bailing is still safe ctx->done = true; } res = temp; @@ -735,7 +725,6 @@ /* _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; } @@ -784,7 +773,6 @@ 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); } @@ -834,7 +822,6 @@ 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); @@ -1097,15 +1084,8 @@ 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); @@ -1156,7 +1136,6 @@ } } if (attr == NULL) { - /* No conversion made. We don't know what `attr` is. */ attr = sym_new_not_null(ctx); } stack_pointer[-1] = attr; @@ -1507,7 +1486,6 @@ } case _FOR_ITER_GEN_FRAME: { - /* We are about to hit the end of the trace */ ctx->done = true; break; } @@ -1712,8 +1690,6 @@ } 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); } @@ -1761,7 +1737,6 @@ 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++; } @@ -1787,16 +1762,13 @@ 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; } @@ -1804,11 +1776,8 @@ 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; @@ -2049,7 +2018,6 @@ 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; @@ -2061,7 +2029,6 @@ 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; @@ -2123,64 +2090,34 @@ 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()); @@ -2253,11 +2190,9 @@ 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; @@ -2273,11 +2208,9 @@ 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()); @@ -2296,8 +2229,6 @@ 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; } diff --git a/Tools/cases_generator/analyzer.py b/Tools/cases_generator/analyzer.py index 5d75b9f036b524..a217d7136a5401 100644 --- a/Tools/cases_generator/analyzer.py +++ b/Tools/cases_generator/analyzer.py @@ -3,18 +3,19 @@ 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 @@ def dump(self, indent: str) -> None: @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 @@ def is_viable(self) -> bool: 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 @@ def is_super(self) -> bool: 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 @@ def has_error_without_pop(op: parser.CodeDef) -> bool: "_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 diff --git a/Tools/cases_generator/cwriter.py b/Tools/cases_generator/cwriter.py index 2bda9fe73df83e..6636755db55eec 100644 --- a/Tools/cases_generator/cwriter.py +++ b/Tools/cases_generator/cwriter.py @@ -99,7 +99,7 @@ def emit_token(self, tkn: Token) -> None: 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) diff --git a/Tools/cases_generator/generators_common.py b/Tools/cases_generator/generators_common.py index b192738849d18d..4571811bcdff3c 100644 --- a/Tools/cases_generator/generators_common.py +++ b/Tools/cases_generator/generators_common.py @@ -12,6 +12,7 @@ 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 @@ def deopt_if( 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 @@ def _print_storage(self, storage: Storage) -> None: 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 @@ def _emit_block( 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: diff --git a/Tools/cases_generator/lexer.py b/Tools/cases_generator/lexer.py index b4bcd73fdbfe52..79bb9c27a55601 100644 --- a/Tools/cases_generator/lexer.py +++ b/Tools/cases_generator/lexer.py @@ -80,7 +80,10 @@ def choice(*opts: str) -> str: # 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": diff --git a/Tools/cases_generator/optimizer_generator.py b/Tools/cases_generator/optimizer_generator.py index 182933e9fc4af4..f515bb6fdc9213 100644 --- a/Tools/cases_generator/optimizer_generator.py +++ b/Tools/cases_generator/optimizer_generator.py @@ -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",) diff --git a/Tools/cases_generator/parser.py b/Tools/cases_generator/parser.py index 696c5c16432990..4ec46d8cac6e4b 100644 --- a/Tools/cases_generator/parser.py +++ b/Tools/cases_generator/parser.py @@ -11,8 +11,17 @@ 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) diff --git a/Tools/cases_generator/parsing.py b/Tools/cases_generator/parsing.py index 84aed49d491e01..9c9b0053a5928b 100644 --- a/Tools/cases_generator/parsing.py +++ b/Tools/cases_generator/parsing.py @@ -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 @@ def first_token(self) -> lx.Token: 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 @@ def label_def(self) -> LabelDef | None: 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 @@ def members(self, allow_sequence : bool=False) -> list[str] | None: 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 @@ def c_blob(self) -> list[lx.Token]: filename = "" src = "if (x) { x.foo; // comment\n}" parser = Parser(src, filename) - x = parser.definition() - print(x) + while node := parser.definition(): + pprint.pprint(node) diff --git a/Tools/cases_generator/plexer.py b/Tools/cases_generator/plexer.py index cb6c5375866490..95a68cf1562111 100644 --- a/Tools/cases_generator/plexer.py +++ b/Tools/cases_generator/plexer.py @@ -69,6 +69,20 @@ def require(self, kind: str) -> Token: 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() diff --git a/Tools/cases_generator/tier1_generator.py b/Tools/cases_generator/tier1_generator.py index f784b6b3371908..ccf2dfe2d2e684 100644 --- a/Tools/cases_generator/tier1_generator.py +++ b/Tools/cases_generator/tier1_generator.py @@ -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( diff --git a/Tools/cases_generator/tier2_generator.py b/Tools/cases_generator/tier2_generator.py index 8c78388ba53481..75b0d5cb51072c 100644 --- a/Tools/cases_generator/tier2_generator.py +++ b/Tools/cases_generator/tier2_generator.py @@ -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",)