From 97984d367d521700fd5130b7b62456914c415191 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Fri, 27 Oct 2023 13:21:42 -0700 Subject: [PATCH 01/24] Make all labels in _PyUopExecute end in _tier_two Use `GOTO_ERROR(error)` instead of `goto error`. This macro is defined differently in the tier two interpreter. --- Python/bytecodes.c | 102 +++++------ Python/ceval_macros.h | 7 +- Python/executor.c | 20 ++- Python/executor_cases.c.h | 244 +++++++++++++------------- Python/generated_cases.c.h | 102 +++++------ Tools/cases_generator/instructions.py | 2 + 6 files changed, 242 insertions(+), 235 deletions(-) diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 5b1d70b303060d..4098e891e5faee 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -170,7 +170,7 @@ dummy_func( uintptr_t code_version = _PyFrame_GetCode(frame)->_co_instrumentation_version; if (code_version != global_version) { if (_Py_Instrument(_PyFrame_GetCode(frame), tstate->interp)) { - goto error; + GOTO_ERROR(error); } next_instr--; } @@ -266,7 +266,7 @@ dummy_func( if (PyGen_Check(receiver)) { PyErr_SetObject(PyExc_StopIteration, value); if (monitor_stop_iteration(tstate, frame, next_instr-1)) { - goto error; + GOTO_ERROR(error); } PyErr_SetRaisedException(NULL); } @@ -281,7 +281,7 @@ dummy_func( if (PyGen_Check(receiver) || PyCoro_CheckExact(receiver)) { PyErr_SetObject(PyExc_StopIteration, value); if (monitor_stop_iteration(tstate, frame, next_instr-1)) { - goto error; + GOTO_ERROR(error); } PyErr_SetRaisedException(NULL); } @@ -809,7 +809,7 @@ dummy_func( int err = _Py_call_instrumentation_arg( tstate, PY_MONITORING_EVENT_PY_RETURN, frame, next_instr-1, retval); - if (err) goto error; + if (err) GOTO_ERROR(error); STACK_SHRINK(1); assert(EMPTY()); _PyFrame_SetStackPointer(frame, stack_pointer); @@ -833,7 +833,7 @@ dummy_func( int err = _Py_call_instrumentation_arg( tstate, PY_MONITORING_EVENT_PY_RETURN, frame, next_instr-1, retval); - if (err) goto error; + if (err) GOTO_ERROR(error); Py_INCREF(retval); assert(EMPTY()); _PyFrame_SetStackPointer(frame, stack_pointer); @@ -889,7 +889,7 @@ dummy_func( if (PyAsyncGen_CheckExact(aiter)) { awaitable = type->tp_as_async->am_anext(aiter); if (awaitable == NULL) { - goto error; + GOTO_ERROR(error); } } else { if (type->tp_as_async != NULL){ @@ -899,7 +899,7 @@ dummy_func( if (getter != NULL) { next_iter = (*getter)(aiter); if (next_iter == NULL) { - goto error; + GOTO_ERROR(error); } } else { @@ -907,7 +907,7 @@ dummy_func( "'async for' requires an iterator with " "__anext__ method, got %.100s", type->tp_name); - goto error; + GOTO_ERROR(error); } awaitable = _PyCoro_GetAwaitableIter(next_iter); @@ -919,7 +919,7 @@ dummy_func( Py_TYPE(next_iter)->tp_name); Py_DECREF(next_iter); - goto error; + GOTO_ERROR(error); } else { Py_DECREF(next_iter); } @@ -1000,7 +1000,7 @@ dummy_func( JUMPBY(oparg); } else { - goto error; + GOTO_ERROR(error); } } Py_DECREF(v); @@ -1034,7 +1034,7 @@ dummy_func( int err = _Py_call_instrumentation_arg( tstate, PY_MONITORING_EVENT_PY_YIELD, frame, next_instr-1, retval); - if (err) goto error; + if (err) GOTO_ERROR(error); tstate->exc_info = gen->gi_exc_state.previous_item; gen->gi_exc_state.previous_item = NULL; _Py_LeaveRecursiveCallPy(tstate); @@ -1087,7 +1087,7 @@ dummy_func( else { assert(PyLong_Check(lasti)); _PyErr_SetString(tstate, PyExc_SystemError, "lasti is not an int"); - goto error; + GOTO_ERROR(error); } } assert(exc && PyExceptionInstance_Check(exc)); @@ -1164,7 +1164,7 @@ dummy_func( if (ns == NULL) { _PyErr_Format(tstate, PyExc_SystemError, "no locals when deleting %R", name); - goto error; + GOTO_ERROR(error); } err = PyObject_DelItem(ns, name); // Can't use ERROR_IF here. @@ -1172,7 +1172,7 @@ dummy_func( _PyEval_FormatExcCheckArg(tstate, PyExc_NameError, NAME_ERROR_MSG, name); - goto error; + GOTO_ERROR(error); } } @@ -1287,7 +1287,7 @@ dummy_func( _PyEval_FormatExcCheckArg(tstate, PyExc_NameError, NAME_ERROR_MSG, name); } - goto error; + GOTO_ERROR(error); } } @@ -1304,7 +1304,7 @@ dummy_func( inst(LOAD_FROM_DICT_OR_GLOBALS, (mod_or_class_dict -- v)) { PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); if (PyMapping_GetOptionalItem(mod_or_class_dict, name, &v) < 0) { - goto error; + GOTO_ERROR(error); } if (v == NULL) { v = PyDict_GetItemWithError(GLOBALS(), name); @@ -1312,17 +1312,17 @@ dummy_func( Py_INCREF(v); } else if (_PyErr_Occurred(tstate)) { - goto error; + GOTO_ERROR(error); } else { if (PyMapping_GetOptionalItem(BUILTINS(), name, &v) < 0) { - goto error; + GOTO_ERROR(error); } if (v == NULL) { _PyEval_FormatExcCheckArg( tstate, PyExc_NameError, NAME_ERROR_MSG, name); - goto error; + GOTO_ERROR(error); } } } @@ -1338,7 +1338,7 @@ dummy_func( } PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); if (PyMapping_GetOptionalItem(mod_or_class_dict, name, &v) < 0) { - goto error; + GOTO_ERROR(error); } if (v == NULL) { v = PyDict_GetItemWithError(GLOBALS(), name); @@ -1346,17 +1346,17 @@ dummy_func( Py_INCREF(v); } else if (_PyErr_Occurred(tstate)) { - goto error; + GOTO_ERROR(error); } else { if (PyMapping_GetOptionalItem(BUILTINS(), name, &v) < 0) { - goto error; + GOTO_ERROR(error); } if (v == NULL) { _PyEval_FormatExcCheckArg( tstate, PyExc_NameError, NAME_ERROR_MSG, name); - goto error; + GOTO_ERROR(error); } } } @@ -1486,7 +1486,7 @@ dummy_func( // Fortunately we don't need its superpower. if (oldobj == NULL) { _PyEval_FormatExcUnbound(tstate, _PyFrame_GetCode(frame), oparg); - goto error; + GOTO_ERROR(error); } PyCell_SET(cell, NULL); Py_DECREF(oldobj); @@ -1499,7 +1499,7 @@ dummy_func( name = PyTuple_GET_ITEM(_PyFrame_GetCode(frame)->co_localsplusnames, oparg); if (PyMapping_GetOptionalItem(class_dict, name, &value) < 0) { Py_DECREF(class_dict); - goto error; + GOTO_ERROR(error); } Py_DECREF(class_dict); if (!value) { @@ -1507,7 +1507,7 @@ dummy_func( value = PyCell_GET(cell); if (value == NULL) { _PyEval_FormatExcUnbound(tstate, _PyFrame_GetCode(frame), oparg); - goto error; + GOTO_ERROR(error); } Py_INCREF(value); } @@ -1586,7 +1586,7 @@ dummy_func( inst(BUILD_SET, (values[oparg] -- set)) { set = PySet_New(NULL); if (set == NULL) - goto error; + GOTO_ERROR(error); int err = 0; for (int i = 0; i < oparg; i++) { PyObject *item = values[i]; @@ -1654,7 +1654,7 @@ dummy_func( PyTuple_GET_SIZE(keys) != (Py_ssize_t)oparg) { _PyErr_SetString(tstate, PyExc_SystemError, "bad BUILD_CONST_KEY_MAP keys argument"); - goto error; // Pop the keys and values. + GOTO_ERROR(error); // Pop the keys and values. } map = _PyDict_FromItems( &PyTuple_GET_ITEM(keys, 0), 1, @@ -2424,7 +2424,7 @@ dummy_func( _PyErr_SetString(tstate, PyExc_TypeError, "cannot 'yield from' a coroutine object " "in a non-coroutine generator"); - goto error; + GOTO_ERROR(error); } iter = iterable; } @@ -2435,7 +2435,7 @@ dummy_func( /* `iterable` is not a generator. */ iter = PyObject_GetIter(iterable); if (iter == NULL) { - goto error; + GOTO_ERROR(error); } DECREF_INPUTS(); } @@ -2470,7 +2470,7 @@ dummy_func( if (next == NULL) { if (_PyErr_Occurred(tstate)) { if (!_PyErr_ExceptionMatches(tstate, PyExc_StopIteration)) { - goto error; + GOTO_ERROR(error); } monitor_raise(tstate, frame, next_instr-1); _PyErr_Clear(tstate); @@ -2500,7 +2500,7 @@ dummy_func( else { if (_PyErr_Occurred(tstate)) { if (!_PyErr_ExceptionMatches(tstate, PyExc_StopIteration)) { - goto error; + GOTO_ERROR(error); } monitor_raise(tstate, frame, here); _PyErr_Clear(tstate); @@ -2699,7 +2699,7 @@ dummy_func( "asynchronous context manager protocol", Py_TYPE(mgr)->tp_name); } - goto error; + GOTO_ERROR(error); } exit = _PyObject_LookupSpecial(mgr, &_Py_ID(__aexit__)); if (exit == NULL) { @@ -2711,7 +2711,7 @@ dummy_func( Py_TYPE(mgr)->tp_name); } Py_DECREF(enter); - goto error; + GOTO_ERROR(error); } DECREF_INPUTS(); res = _PyObject_CallNoArgs(enter); @@ -2734,7 +2734,7 @@ dummy_func( "context manager protocol", Py_TYPE(mgr)->tp_name); } - goto error; + GOTO_ERROR(error); } exit = _PyObject_LookupSpecial(mgr, &_Py_ID(__exit__)); if (exit == NULL) { @@ -2746,7 +2746,7 @@ dummy_func( Py_TYPE(mgr)->tp_name); } Py_DECREF(enter); - goto error; + GOTO_ERROR(error); } DECREF_INPUTS(); res = _PyObject_CallNoArgs(enter); @@ -2994,7 +2994,7 @@ dummy_func( // The frame has stolen all the arguments from the stack, // so there is no need to clean them up. if (new_frame == NULL) { - goto error; + GOTO_ERROR(error); } SKIP_OVER(INLINE_CACHE_ENTRIES_CALL); assert(1 + INLINE_CACHE_ENTRIES_CALL == next_instr - frame->instr_ptr); @@ -3213,7 +3213,7 @@ dummy_func( STAT_INC(CALL, hit); PyObject *self = _PyType_NewManagedObject(tp); if (self == NULL) { - goto error; + GOTO_ERROR(error); } Py_DECREF(tp); _PyInterpreterFrame *shim = _PyFrame_PushTrampolineUnchecked( @@ -3252,7 +3252,7 @@ dummy_func( PyErr_Format(PyExc_TypeError, "__init__() should return None, not '%.200s'", Py_TYPE(should_be_none)->tp_name); - goto error; + GOTO_ERROR(error); } } @@ -3291,7 +3291,7 @@ dummy_func( // This is slower but CPython promises to check all non-vectorcall // function calls. if (_Py_EnterRecursiveCallTstate(tstate, " while calling a Python object")) { - goto error; + GOTO_ERROR(error); } PyObject *arg = args[0]; res = _PyCFunction_TrampolineCall(cfunc, PyCFunction_GET_SELF(callable), arg); @@ -3376,7 +3376,7 @@ dummy_func( PyObject *arg = args[0]; Py_ssize_t len_i = PyObject_Length(arg); if (len_i < 0) { - goto error; + GOTO_ERROR(error); } res = PyLong_FromSsize_t(len_i); assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); @@ -3401,7 +3401,7 @@ dummy_func( PyObject *inst = args[0]; int retval = PyObject_IsInstance(inst, cls); if (retval < 0) { - goto error; + GOTO_ERROR(error); } res = PyBool_FromLong(retval); assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); @@ -3451,7 +3451,7 @@ dummy_func( // This is slower but CPython promises to check all non-vectorcall // function calls. if (_Py_EnterRecursiveCallTstate(tstate, " while calling a Python object")) { - goto error; + GOTO_ERROR(error); } res = _PyCFunction_TrampolineCall(cfunc, self, arg); _Py_LeaveRecursiveCallTstate(tstate); @@ -3511,7 +3511,7 @@ dummy_func( // This is slower but CPython promises to check all non-vectorcall // function calls. if (_Py_EnterRecursiveCallTstate(tstate, " while calling a Python object")) { - goto error; + GOTO_ERROR(error); } res = _PyCFunction_TrampolineCall(cfunc, self, NULL); _Py_LeaveRecursiveCallTstate(tstate); @@ -3598,7 +3598,7 @@ dummy_func( // The frame has stolen all the arguments from the stack, // so there is no need to clean them up. if (new_frame == NULL) { - goto error; + GOTO_ERROR(error); } assert(next_instr - frame->instr_ptr == 1); frame->return_offset = 1; @@ -3646,11 +3646,11 @@ dummy_func( assert(kwargs == NULL || PyDict_CheckExact(kwargs)); if (!PyTuple_CheckExact(callargs)) { if (check_args_iterable(tstate, func, callargs) < 0) { - goto error; + GOTO_ERROR(error); } PyObject *tuple = PySequence_Tuple(callargs); if (tuple == NULL) { - goto error; + GOTO_ERROR(error); } Py_SETREF(callargs, tuple); } @@ -3664,7 +3664,7 @@ dummy_func( int err = _Py_call_instrumentation_2args( tstate, PY_MONITORING_EVENT_CALL, frame, next_instr-1, func, arg); - if (err) goto error; + if (err) GOTO_ERROR(error); result = PyObject_Call(func, callargs, kwargs); if (result == NULL) { _Py_call_instrumentation_exc2( @@ -3695,7 +3695,7 @@ dummy_func( // Need to manually shrink the stack since we exit with DISPATCH_INLINED. STACK_SHRINK(oparg + 3); if (new_frame == NULL) { - goto error; + GOTO_ERROR(error); } assert(next_instr - frame->instr_ptr == 1); frame->return_offset = 1; @@ -3716,7 +3716,7 @@ dummy_func( Py_DECREF(codeobj); if (func_obj == NULL) { - goto error; + GOTO_ERROR(error); } _PyFunction_SetVersion( @@ -3756,7 +3756,7 @@ dummy_func( PyFunctionObject *func = (PyFunctionObject *)frame->f_funcobj; PyGenObject *gen = (PyGenObject *)_Py_MakeCoro(func); if (gen == NULL) { - goto error; + GOTO_ERROR(error); } assert(EMPTY()); _PyFrame_SetStackPointer(frame, stack_pointer); diff --git a/Python/ceval_macros.h b/Python/ceval_macros.h index 544e8ef8fa8c0a..01b2422f6f6e31 100644 --- a/Python/ceval_macros.h +++ b/Python/ceval_macros.h @@ -116,11 +116,14 @@ goto start_frame; \ } while (0) +// Use this instead of 'goto error' so Tier 2 can go to a different label +#define GOTO_ERROR(LABEL) goto LABEL + #define CHECK_EVAL_BREAKER() \ _Py_CHECK_EMSCRIPTEN_SIGNALS_PERIODICALLY(); \ if (_Py_atomic_load_uintptr_relaxed(&tstate->interp->ceval.eval_breaker) & _PY_EVAL_EVENTS_MASK) { \ if (_Py_HandlePending(tstate) != 0) { \ - goto error; \ + GOTO_ERROR(error); \ } \ } @@ -326,7 +329,7 @@ do { \ }\ else { \ result = PyFloat_FromDouble(dval); \ - if ((result) == NULL) goto error; \ + if ((result) == NULL) GOTO_ERROR(error); \ _Py_DECREF_NO_DEALLOC(left); \ _Py_DECREF_NO_DEALLOC(right); \ } \ diff --git a/Python/executor.c b/Python/executor.c index bfa7f7e1c3d84e..f512492e80f1dc 100644 --- a/Python/executor.c +++ b/Python/executor.c @@ -21,11 +21,13 @@ #define TIER_TWO 2 #include "ceval_macros.h" +#undef GOTO_ERROR +#define GOTO_ERROR(LABEL) goto LABEL ## _tier_two #undef DEOPT_IF #define DEOPT_IF(COND, INSTNAME) \ if ((COND)) { \ - goto deoptimize; \ + goto deoptimize_tier_two;\ } #ifdef Py_STATS @@ -111,22 +113,22 @@ _PyUopExecute(_PyExecutorObject *executor, _PyInterpreterFrame *frame, PyObject } } -unbound_local_error: +unbound_local_error_tier_two: _PyEval_FormatExcCheckArg(tstate, PyExc_UnboundLocalError, UNBOUNDLOCAL_ERROR_MSG, PyTuple_GetItem(_PyFrame_GetCode(frame)->co_localsplusnames, oparg) ); - goto error; + goto error_tier_two; -pop_4_error: +pop_4_error_tier_two: STACK_SHRINK(1); -pop_3_error: +pop_3_error_tier_two: STACK_SHRINK(1); -pop_2_error: +pop_2_error_tier_two: STACK_SHRINK(1); -pop_1_error: +pop_1_error_tier_two: STACK_SHRINK(1); -error: +error_tier_two: // On ERROR_IF we return NULL as the frame. // The caller recovers the frame from tstate->current_frame. DPRINTF(2, "Error: [Opcode %d, operand %" PRIu64 "]\n", opcode, operand); @@ -136,7 +138,7 @@ _PyUopExecute(_PyExecutorObject *executor, _PyInterpreterFrame *frame, PyObject Py_DECREF(self); return NULL; -deoptimize: +deoptimize_tier_two: // On DEOPT_IF we just repeat the last instruction. // This presumes nothing was popped from the stack (nor pushed). DPRINTF(2, "DEOPT: [Opcode %d, operand %" PRIu64 "]\n", opcode, operand); diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index 9f37cd75b47efb..5616c47e58df1b 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -22,7 +22,7 @@ case LOAD_FAST_CHECK: { PyObject *value; value = GETLOCAL(oparg); - if (value == NULL) goto unbound_local_error; + if (value == NULL) goto unbound_local_error_tier_two; Py_INCREF(value); STACK_GROW(1); stack_pointer[-1] = value; @@ -99,7 +99,7 @@ value = stack_pointer[-1]; res = PyNumber_Negative(value); Py_DECREF(value); - if (res == NULL) goto pop_1_error; + if (res == NULL) goto pop_1_error_tier_two; stack_pointer[-1] = res; break; } @@ -130,7 +130,7 @@ #endif /* ENABLE_SPECIALIZATION */ int err = PyObject_IsTrue(value); Py_DECREF(value); - if (err < 0) goto pop_1_error; + if (err < 0) goto pop_1_error_tier_two; res = err ? Py_True : Py_False; stack_pointer[-1] = res; break; @@ -226,7 +226,7 @@ value = stack_pointer[-1]; res = PyNumber_Invert(value); Py_DECREF(value); - if (res == NULL) goto pop_1_error; + if (res == NULL) goto pop_1_error_tier_two; stack_pointer[-1] = res; break; } @@ -251,7 +251,7 @@ res = _PyLong_Multiply((PyLongObject *)left, (PyLongObject *)right); _Py_DECREF_SPECIALIZED(right, (destructor)PyObject_Free); _Py_DECREF_SPECIALIZED(left, (destructor)PyObject_Free); - if (res == NULL) goto pop_2_error; + if (res == NULL) goto pop_2_error_tier_two; STACK_SHRINK(1); stack_pointer[-1] = res; break; @@ -267,7 +267,7 @@ res = _PyLong_Add((PyLongObject *)left, (PyLongObject *)right); _Py_DECREF_SPECIALIZED(right, (destructor)PyObject_Free); _Py_DECREF_SPECIALIZED(left, (destructor)PyObject_Free); - if (res == NULL) goto pop_2_error; + if (res == NULL) goto pop_2_error_tier_two; STACK_SHRINK(1); stack_pointer[-1] = res; break; @@ -283,7 +283,7 @@ res = _PyLong_Subtract((PyLongObject *)left, (PyLongObject *)right); _Py_DECREF_SPECIALIZED(right, (destructor)PyObject_Free); _Py_DECREF_SPECIALIZED(left, (destructor)PyObject_Free); - if (res == NULL) goto pop_2_error; + if (res == NULL) goto pop_2_error_tier_two; STACK_SHRINK(1); stack_pointer[-1] = res; break; @@ -367,7 +367,7 @@ res = PyUnicode_Concat(left, right); _Py_DECREF_SPECIALIZED(left, _PyUnicode_ExactDealloc); _Py_DECREF_SPECIALIZED(right, _PyUnicode_ExactDealloc); - if (res == NULL) goto pop_2_error; + if (res == NULL) goto pop_2_error_tier_two; STACK_SHRINK(1); stack_pointer[-1] = res; break; @@ -392,7 +392,7 @@ res = PyObject_GetItem(container, sub); Py_DECREF(container); Py_DECREF(sub); - if (res == NULL) goto pop_2_error; + if (res == NULL) goto pop_2_error_tier_two; STACK_SHRINK(1); stack_pointer[-1] = res; break; @@ -417,7 +417,7 @@ Py_DECREF(slice); } Py_DECREF(container); - if (res == NULL) goto pop_3_error; + if (res == NULL) goto pop_3_error_tier_two; STACK_SHRINK(2); stack_pointer[-1] = res; break; @@ -443,7 +443,7 @@ } Py_DECREF(v); Py_DECREF(container); - if (err) goto pop_4_error; + if (err) goto pop_4_error_tier_two; STACK_SHRINK(4); break; } @@ -534,7 +534,7 @@ } Py_DECREF(dict); Py_DECREF(sub); - if (true) goto pop_2_error; + if (true) goto pop_2_error_tier_two; } Py_INCREF(res); // Do this before DECREF'ing dict, sub Py_DECREF(dict); @@ -549,7 +549,7 @@ PyObject *list; v = stack_pointer[-1]; list = stack_pointer[-2 - (oparg-1)]; - if (_PyList_AppendTakeRef((PyListObject *)list, v) < 0) goto pop_1_error; + if (_PyList_AppendTakeRef((PyListObject *)list, v) < 0) goto pop_1_error_tier_two; STACK_SHRINK(1); break; } @@ -561,7 +561,7 @@ set = stack_pointer[-2 - (oparg-1)]; int err = PySet_Add(set, v); Py_DECREF(v); - if (err) goto pop_1_error; + if (err) goto pop_1_error_tier_two; STACK_SHRINK(1); break; } @@ -588,7 +588,7 @@ Py_DECREF(v); Py_DECREF(container); Py_DECREF(sub); - if (err) goto pop_3_error; + if (err) goto pop_3_error_tier_two; STACK_SHRINK(3); break; } @@ -631,7 +631,7 @@ STAT_INC(STORE_SUBSCR, hit); int err = _PyDict_SetItem_Take2((PyDictObject *)dict, sub, value); Py_DECREF(dict); - if (err) goto pop_3_error; + if (err) goto pop_3_error_tier_two; STACK_SHRINK(3); break; } @@ -645,7 +645,7 @@ int err = PyObject_DelItem(container, sub); Py_DECREF(container); Py_DECREF(sub); - if (err) goto pop_2_error; + if (err) goto pop_2_error_tier_two; STACK_SHRINK(2); break; } @@ -657,7 +657,7 @@ assert(oparg <= MAX_INTRINSIC_1); res = _PyIntrinsics_UnaryFunctions[oparg].func(tstate, value); Py_DECREF(value); - if (res == NULL) goto pop_1_error; + if (res == NULL) goto pop_1_error_tier_two; stack_pointer[-1] = res; break; } @@ -672,7 +672,7 @@ res = _PyIntrinsics_BinaryFunctions[oparg].func(tstate, value2, value1); Py_DECREF(value2); Py_DECREF(value1); - if (res == NULL) goto pop_2_error; + if (res == NULL) goto pop_2_error_tier_two; STACK_SHRINK(1); stack_pointer[-1] = res; break; @@ -721,12 +721,12 @@ "__aiter__ method, got %.100s", type->tp_name); Py_DECREF(obj); - if (true) goto pop_1_error; + if (true) goto pop_1_error_tier_two; } iter = (*getter)(obj); Py_DECREF(obj); - if (iter == NULL) goto pop_1_error; + if (iter == NULL) goto pop_1_error_tier_two; if (Py_TYPE(iter)->tp_as_async == NULL || Py_TYPE(iter)->tp_as_async->am_anext == NULL) { @@ -736,7 +736,7 @@ "that does not implement __anext__: %.100s", Py_TYPE(iter)->tp_name); Py_DECREF(iter); - if (true) goto pop_1_error; + if (true) goto pop_1_error_tier_two; } stack_pointer[-1] = iter; break; @@ -753,7 +753,7 @@ if (PyAsyncGen_CheckExact(aiter)) { awaitable = type->tp_as_async->am_anext(aiter); if (awaitable == NULL) { - goto error; + GOTO_ERROR(error); } } else { if (type->tp_as_async != NULL){ @@ -763,7 +763,7 @@ if (getter != NULL) { next_iter = (*getter)(aiter); if (next_iter == NULL) { - goto error; + GOTO_ERROR(error); } } else { @@ -771,7 +771,7 @@ "'async for' requires an iterator with " "__anext__ method, got %.100s", type->tp_name); - goto error; + GOTO_ERROR(error); } awaitable = _PyCoro_GetAwaitableIter(next_iter); @@ -783,7 +783,7 @@ Py_TYPE(next_iter)->tp_name); Py_DECREF(next_iter); - goto error; + GOTO_ERROR(error); } else { Py_DECREF(next_iter); } @@ -819,7 +819,7 @@ } } - if (iter == NULL) goto pop_1_error; + if (iter == NULL) goto pop_1_error_tier_two; stack_pointer[-1] = iter; break; } @@ -843,11 +843,11 @@ case LOAD_BUILD_CLASS: { PyObject *bc; - if (PyMapping_GetOptionalItem(BUILTINS(), &_Py_ID(__build_class__), &bc) < 0) goto error; + if (PyMapping_GetOptionalItem(BUILTINS(), &_Py_ID(__build_class__), &bc) < 0) goto error_tier_two; if (bc == NULL) { _PyErr_SetString(tstate, PyExc_NameError, "__build_class__ not found"); - if (true) goto error; + if (true) goto error_tier_two; } STACK_GROW(1); stack_pointer[-1] = bc; @@ -864,14 +864,14 @@ _PyErr_Format(tstate, PyExc_SystemError, "no locals found when storing %R", name); Py_DECREF(v); - if (true) goto pop_1_error; + if (true) goto pop_1_error_tier_two; } if (PyDict_CheckExact(ns)) err = PyDict_SetItem(ns, name, v); else err = PyObject_SetItem(ns, name, v); Py_DECREF(v); - if (err) goto pop_1_error; + if (err) goto pop_1_error_tier_two; STACK_SHRINK(1); break; } @@ -883,7 +883,7 @@ if (ns == NULL) { _PyErr_Format(tstate, PyExc_SystemError, "no locals when deleting %R", name); - goto error; + GOTO_ERROR(error); } err = PyObject_DelItem(ns, name); // Can't use ERROR_IF here. @@ -891,7 +891,7 @@ _PyEval_FormatExcCheckArg(tstate, PyExc_NameError, NAME_ERROR_MSG, name); - goto error; + GOTO_ERROR(error); } break; } @@ -912,7 +912,7 @@ PyObject **top = stack_pointer + oparg - 1; int res = _PyEval_UnpackIterable(tstate, seq, oparg, -1, top); Py_DECREF(seq); - if (res == 0) goto pop_1_error; + if (res == 0) goto pop_1_error_tier_two; STACK_SHRINK(1); STACK_GROW(oparg); break; @@ -978,7 +978,7 @@ PyObject **top = stack_pointer + totalargs - 1; int res = _PyEval_UnpackIterable(tstate, seq, oparg & 0xFF, oparg >> 8, top); Py_DECREF(seq); - if (res == 0) goto pop_1_error; + if (res == 0) goto pop_1_error_tier_two; STACK_GROW((oparg & 0xFF) + (oparg >> 8)); break; } @@ -1003,7 +1003,7 @@ int err = PyObject_SetAttr(owner, name, v); Py_DECREF(v); Py_DECREF(owner); - if (err) goto pop_2_error; + if (err) goto pop_2_error_tier_two; STACK_SHRINK(2); break; } @@ -1014,7 +1014,7 @@ PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); int err = PyObject_DelAttr(owner, name); Py_DECREF(owner); - if (err) goto pop_1_error; + if (err) goto pop_1_error_tier_two; STACK_SHRINK(1); break; } @@ -1025,7 +1025,7 @@ PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); int err = PyDict_SetItem(GLOBALS(), name, v); Py_DECREF(v); - if (err) goto pop_1_error; + if (err) goto pop_1_error_tier_two; STACK_SHRINK(1); break; } @@ -1040,7 +1040,7 @@ _PyEval_FormatExcCheckArg(tstate, PyExc_NameError, NAME_ERROR_MSG, name); } - goto error; + GOTO_ERROR(error); } break; } @@ -1051,7 +1051,7 @@ if (locals == NULL) { _PyErr_SetString(tstate, PyExc_SystemError, "no locals found"); - if (true) goto error; + if (true) goto error_tier_two; } Py_INCREF(locals); STACK_GROW(1); @@ -1065,7 +1065,7 @@ mod_or_class_dict = stack_pointer[-1]; PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); if (PyMapping_GetOptionalItem(mod_or_class_dict, name, &v) < 0) { - goto error; + GOTO_ERROR(error); } if (v == NULL) { v = PyDict_GetItemWithError(GLOBALS(), name); @@ -1073,17 +1073,17 @@ Py_INCREF(v); } else if (_PyErr_Occurred(tstate)) { - goto error; + GOTO_ERROR(error); } else { if (PyMapping_GetOptionalItem(BUILTINS(), name, &v) < 0) { - goto error; + GOTO_ERROR(error); } if (v == NULL) { _PyEval_FormatExcCheckArg( tstate, PyExc_NameError, NAME_ERROR_MSG, name); - goto error; + GOTO_ERROR(error); } } } @@ -1098,11 +1098,11 @@ if (mod_or_class_dict == NULL) { _PyErr_SetString(tstate, PyExc_SystemError, "no locals found"); - if (true) goto error; + if (true) goto error_tier_two; } PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); if (PyMapping_GetOptionalItem(mod_or_class_dict, name, &v) < 0) { - goto error; + GOTO_ERROR(error); } if (v == NULL) { v = PyDict_GetItemWithError(GLOBALS(), name); @@ -1110,17 +1110,17 @@ Py_INCREF(v); } else if (_PyErr_Occurred(tstate)) { - goto error; + GOTO_ERROR(error); } else { if (PyMapping_GetOptionalItem(BUILTINS(), name, &v) < 0) { - goto error; + GOTO_ERROR(error); } if (v == NULL) { _PyEval_FormatExcCheckArg( tstate, PyExc_NameError, NAME_ERROR_MSG, name); - goto error; + GOTO_ERROR(error); } } } @@ -1157,7 +1157,7 @@ _PyEval_FormatExcCheckArg(tstate, PyExc_NameError, NAME_ERROR_MSG, name); } - if (true) goto error; + if (true) goto error_tier_two; } Py_INCREF(res); } @@ -1165,15 +1165,15 @@ /* Slow-path if globals or builtins is not a dict */ /* namespace 1: globals */ - if (PyMapping_GetOptionalItem(GLOBALS(), name, &res) < 0) goto error; + if (PyMapping_GetOptionalItem(GLOBALS(), name, &res) < 0) goto error_tier_two; if (res == NULL) { /* namespace 2: builtins */ - if (PyMapping_GetOptionalItem(BUILTINS(), name, &res) < 0) goto error; + if (PyMapping_GetOptionalItem(BUILTINS(), name, &res) < 0) goto error_tier_two; if (res == NULL) { _PyEval_FormatExcCheckArg( tstate, PyExc_NameError, NAME_ERROR_MSG, name); - if (true) goto error; + if (true) goto error_tier_two; } } } @@ -1241,7 +1241,7 @@ case DELETE_FAST: { PyObject *v = GETLOCAL(oparg); - if (v == NULL) goto unbound_local_error; + if (v == NULL) goto unbound_local_error_tier_two; SETLOCAL(oparg, NULL); break; } @@ -1253,7 +1253,7 @@ // Fortunately we don't need its superpower. if (oldobj == NULL) { _PyEval_FormatExcUnbound(tstate, _PyFrame_GetCode(frame), oparg); - goto error; + GOTO_ERROR(error); } PyCell_SET(cell, NULL); Py_DECREF(oldobj); @@ -1270,7 +1270,7 @@ name = PyTuple_GET_ITEM(_PyFrame_GetCode(frame)->co_localsplusnames, oparg); if (PyMapping_GetOptionalItem(class_dict, name, &value) < 0) { Py_DECREF(class_dict); - goto error; + GOTO_ERROR(error); } Py_DECREF(class_dict); if (!value) { @@ -1278,7 +1278,7 @@ value = PyCell_GET(cell); if (value == NULL) { _PyEval_FormatExcUnbound(tstate, _PyFrame_GetCode(frame), oparg); - goto error; + GOTO_ERROR(error); } Py_INCREF(value); } @@ -1292,7 +1292,7 @@ value = PyCell_GET(cell); if (value == NULL) { _PyEval_FormatExcUnbound(tstate, _PyFrame_GetCode(frame), oparg); - if (true) goto error; + if (true) goto error_tier_two; } Py_INCREF(value); STACK_GROW(1); @@ -1333,7 +1333,7 @@ for (int _i = oparg; --_i >= 0;) { Py_DECREF(pieces[_i]); } - if (str == NULL) { STACK_SHRINK(oparg); goto error; } + if (str == NULL) { STACK_SHRINK(oparg); goto error_tier_two; } STACK_SHRINK(oparg); STACK_GROW(1); stack_pointer[-1] = str; @@ -1345,7 +1345,7 @@ PyObject *tup; values = stack_pointer - oparg; tup = _PyTuple_FromArraySteal(values, oparg); - if (tup == NULL) { STACK_SHRINK(oparg); goto error; } + if (tup == NULL) { STACK_SHRINK(oparg); goto error_tier_two; } STACK_SHRINK(oparg); STACK_GROW(1); stack_pointer[-1] = tup; @@ -1357,7 +1357,7 @@ PyObject *list; values = stack_pointer - oparg; list = _PyList_FromArraySteal(values, oparg); - if (list == NULL) { STACK_SHRINK(oparg); goto error; } + if (list == NULL) { STACK_SHRINK(oparg); goto error_tier_two; } STACK_SHRINK(oparg); STACK_GROW(1); stack_pointer[-1] = list; @@ -1380,7 +1380,7 @@ Py_TYPE(iterable)->tp_name); } Py_DECREF(iterable); - if (true) goto pop_1_error; + if (true) goto pop_1_error_tier_two; } assert(Py_IsNone(none_val)); Py_DECREF(iterable); @@ -1395,7 +1395,7 @@ set = stack_pointer[-2 - (oparg-1)]; int err = _PySet_Update(set, iterable); Py_DECREF(iterable); - if (err < 0) goto pop_1_error; + if (err < 0) goto pop_1_error_tier_two; STACK_SHRINK(1); break; } @@ -1406,7 +1406,7 @@ values = stack_pointer - oparg; set = PySet_New(NULL); if (set == NULL) - goto error; + GOTO_ERROR(error); int err = 0; for (int i = 0; i < oparg; i++) { PyObject *item = values[i]; @@ -1416,7 +1416,7 @@ } if (err != 0) { Py_DECREF(set); - if (true) { STACK_SHRINK(oparg); goto error; } + if (true) { STACK_SHRINK(oparg); goto error_tier_two; } } STACK_SHRINK(oparg); STACK_GROW(1); @@ -1435,7 +1435,7 @@ for (int _i = oparg*2; --_i >= 0;) { Py_DECREF(values[_i]); } - if (map == NULL) { STACK_SHRINK(oparg*2); goto error; } + if (map == NULL) { STACK_SHRINK(oparg*2); goto error_tier_two; } STACK_SHRINK(oparg*2); STACK_GROW(1); stack_pointer[-1] = map; @@ -1448,33 +1448,33 @@ if (LOCALS() == NULL) { _PyErr_Format(tstate, PyExc_SystemError, "no locals found when setting up annotations"); - if (true) goto error; + if (true) goto error_tier_two; } /* check if __annotations__ in locals()... */ if (PyDict_CheckExact(LOCALS())) { ann_dict = _PyDict_GetItemWithError(LOCALS(), &_Py_ID(__annotations__)); if (ann_dict == NULL) { - if (_PyErr_Occurred(tstate)) goto error; + if (_PyErr_Occurred(tstate)) goto error_tier_two; /* ...if not, create a new one */ ann_dict = PyDict_New(); - if (ann_dict == NULL) goto error; + if (ann_dict == NULL) goto error_tier_two; err = PyDict_SetItem(LOCALS(), &_Py_ID(__annotations__), ann_dict); Py_DECREF(ann_dict); - if (err) goto error; + if (err) goto error_tier_two; } } else { /* do the same if locals() is not a dict */ - if (PyMapping_GetOptionalItem(LOCALS(), &_Py_ID(__annotations__), &ann_dict) < 0) goto error; + if (PyMapping_GetOptionalItem(LOCALS(), &_Py_ID(__annotations__), &ann_dict) < 0) goto error_tier_two; if (ann_dict == NULL) { ann_dict = PyDict_New(); - if (ann_dict == NULL) goto error; + if (ann_dict == NULL) goto error_tier_two; err = PyObject_SetItem(LOCALS(), &_Py_ID(__annotations__), ann_dict); Py_DECREF(ann_dict); - if (err) goto error; + if (err) goto error_tier_two; } else { Py_DECREF(ann_dict); @@ -1493,7 +1493,7 @@ PyTuple_GET_SIZE(keys) != (Py_ssize_t)oparg) { _PyErr_SetString(tstate, PyExc_SystemError, "bad BUILD_CONST_KEY_MAP keys argument"); - goto error; // Pop the keys and values. + GOTO_ERROR(error); // Pop the keys and values. } map = _PyDict_FromItems( &PyTuple_GET_ITEM(keys, 0), 1, @@ -1502,7 +1502,7 @@ Py_DECREF(values[_i]); } Py_DECREF(keys); - if (map == NULL) { STACK_SHRINK(oparg); goto pop_1_error; } + if (map == NULL) { STACK_SHRINK(oparg); goto pop_1_error_tier_two; } STACK_SHRINK(oparg); stack_pointer[-1] = map; break; @@ -1520,7 +1520,7 @@ Py_TYPE(update)->tp_name); } Py_DECREF(update); - if (true) goto pop_1_error; + if (true) goto pop_1_error_tier_two; } Py_DECREF(update); STACK_SHRINK(1); @@ -1537,7 +1537,7 @@ if (_PyDict_MergeEx(dict, update, 2) < 0) { _PyEval_FormatKwargsError(tstate, callable, update); Py_DECREF(update); - if (true) goto pop_1_error; + if (true) goto pop_1_error_tier_two; } Py_DECREF(update); STACK_SHRINK(1); @@ -1554,7 +1554,7 @@ assert(PyDict_CheckExact(dict)); /* dict[key] = value */ // Do not DECREF INPUTS because the function steals the references - if (_PyDict_SetItem_Take2((PyDictObject *)dict, key, value) != 0) goto pop_2_error; + if (_PyDict_SetItem_Take2((PyDictObject *)dict, key, value) != 0) goto pop_2_error_tier_two; STACK_SHRINK(2); break; } @@ -1576,7 +1576,7 @@ Py_DECREF(global_super); Py_DECREF(class); Py_DECREF(self); - if (attr == NULL) goto pop_3_error; + if (attr == NULL) goto pop_3_error_tier_two; STACK_SHRINK(2); stack_pointer[-1] = attr; break; @@ -1604,7 +1604,7 @@ Py_DECREF(class); if (attr == NULL) { Py_DECREF(self); - if (true) goto pop_3_error; + if (true) goto pop_3_error_tier_two; } if (method_found) { self_or_null = self; // transfer ownership @@ -1656,7 +1656,7 @@ NULL | meth | arg1 | ... | argN */ Py_DECREF(owner); - if (attr == NULL) goto pop_1_error; + if (attr == NULL) goto pop_1_error_tier_two; self_or_null = NULL; } } @@ -1664,7 +1664,7 @@ /* Classic, pushes one value. */ attr = PyObject_GetAttr(owner, name); Py_DECREF(owner); - if (attr == NULL) goto pop_1_error; + if (attr == NULL) goto pop_1_error_tier_two; } STACK_GROW(((oparg & 1) ? 1 : 0)); stack_pointer[-1 - (oparg & 1 ? 1 : 0)] = attr; @@ -1900,11 +1900,11 @@ res = PyObject_RichCompare(left, right, oparg >> 5); Py_DECREF(left); Py_DECREF(right); - if (res == NULL) goto pop_2_error; + if (res == NULL) goto pop_2_error_tier_two; if (oparg & 16) { int res_bool = PyObject_IsTrue(res); Py_DECREF(res); - if (res_bool < 0) goto pop_2_error; + if (res_bool < 0) goto pop_2_error_tier_two; res = res_bool ? Py_True : Py_False; } STACK_SHRINK(1); @@ -2007,7 +2007,7 @@ int res = PySequence_Contains(right, left); Py_DECREF(left); Py_DECREF(right); - if (res < 0) goto pop_2_error; + if (res < 0) goto pop_2_error_tier_two; b = (res ^ oparg) ? Py_True : Py_False; STACK_SHRINK(1); stack_pointer[-1] = b; @@ -2024,7 +2024,7 @@ if (_PyEval_CheckExceptStarTypeValid(tstate, match_type) < 0) { Py_DECREF(exc_value); Py_DECREF(match_type); - if (true) goto pop_2_error; + if (true) goto pop_2_error_tier_two; } match = NULL; @@ -2033,10 +2033,10 @@ &match, &rest); Py_DECREF(exc_value); Py_DECREF(match_type); - if (res < 0) goto pop_2_error; + if (res < 0) goto pop_2_error_tier_two; assert((match == NULL) == (rest == NULL)); - if (match == NULL) goto pop_2_error; + if (match == NULL) goto pop_2_error_tier_two; if (!Py_IsNone(match)) { PyErr_SetHandledException(match); @@ -2055,7 +2055,7 @@ assert(PyExceptionInstance_Check(left)); if (_PyEval_CheckExceptTypeValid(tstate, right) < 0) { Py_DECREF(right); - if (true) goto pop_1_error; + if (true) goto pop_1_error_tier_two; } int res = PyErr_GivenExceptionMatches(left, right); @@ -2086,9 +2086,9 @@ obj = stack_pointer[-1]; // PUSH(len(TOS)) Py_ssize_t len_i = PyObject_Length(obj); - if (len_i < 0) goto error; + if (len_i < 0) goto error_tier_two; len_o = PyLong_FromSsize_t(len_i); - if (len_o == NULL) goto error; + if (len_o == NULL) goto error_tier_two; STACK_GROW(1); stack_pointer[-1] = len_o; break; @@ -2113,7 +2113,7 @@ assert(PyTuple_CheckExact(attrs)); // Success! } else { - if (_PyErr_Occurred(tstate)) goto pop_3_error; + if (_PyErr_Occurred(tstate)) goto pop_3_error_tier_two; attrs = Py_None; // Failure! } STACK_SHRINK(2); @@ -2151,7 +2151,7 @@ subject = stack_pointer[-2]; // On successful match, PUSH(values). Otherwise, PUSH(None). values_or_none = _PyEval_MatchKeys(tstate, subject, keys); - if (values_or_none == NULL) goto error; + if (values_or_none == NULL) goto error_tier_two; STACK_GROW(1); stack_pointer[-1] = values_or_none; break; @@ -2164,7 +2164,7 @@ /* before: [obj]; after [getiter(obj)] */ iter = PyObject_GetIter(iterable); Py_DECREF(iterable); - if (iter == NULL) goto pop_1_error; + if (iter == NULL) goto pop_1_error_tier_two; stack_pointer[-1] = iter; break; } @@ -2182,7 +2182,7 @@ _PyErr_SetString(tstate, PyExc_TypeError, "cannot 'yield from' a coroutine object " "in a non-coroutine generator"); - goto error; + GOTO_ERROR(error); } iter = iterable; } @@ -2193,7 +2193,7 @@ /* `iterable` is not a generator. */ iter = PyObject_GetIter(iterable); if (iter == NULL) { - goto error; + GOTO_ERROR(error); } Py_DECREF(iterable); } @@ -2322,7 +2322,7 @@ r->start = value + r->step; r->len--; next = PyLong_FromLong(value); - if (next == NULL) goto error; + if (next == NULL) goto error_tier_two; STACK_GROW(1); stack_pointer[-1] = next; break; @@ -2360,7 +2360,7 @@ PyObject *stack[4] = {NULL, exc, val, tb}; res = PyObject_Vectorcall(exit_func, stack + 1, 3 | PY_VECTORCALL_ARGUMENTS_OFFSET, NULL); - if (res == NULL) goto error; + if (res == NULL) goto error_tier_two; STACK_GROW(1); stack_pointer[-1] = res; break; @@ -2641,7 +2641,7 @@ res = PyObject_Str(arg); Py_DECREF(arg); Py_DECREF(&PyUnicode_Type); // I.e., callable - if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } + if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error_tier_two; } STACK_SHRINK(oparg); STACK_SHRINK(1); stack_pointer[-1] = res; @@ -2665,7 +2665,7 @@ res = PySequence_Tuple(arg); Py_DECREF(arg); Py_DECREF(&PyTuple_Type); // I.e., tuple - if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } + if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error_tier_two; } STACK_SHRINK(oparg); STACK_SHRINK(1); stack_pointer[-1] = res; @@ -2681,7 +2681,7 @@ PyErr_Format(PyExc_TypeError, "__init__() should return None, not '%.200s'", Py_TYPE(should_be_none)->tp_name); - goto error; + GOTO_ERROR(error); } STACK_SHRINK(1); break; @@ -2710,7 +2710,7 @@ Py_DECREF(args[i]); } Py_DECREF(tp); - if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } + if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error_tier_two; } STACK_SHRINK(oparg); STACK_SHRINK(1); stack_pointer[-1] = res; @@ -2740,7 +2740,7 @@ // This is slower but CPython promises to check all non-vectorcall // function calls. if (_Py_EnterRecursiveCallTstate(tstate, " while calling a Python object")) { - goto error; + GOTO_ERROR(error); } PyObject *arg = args[0]; res = _PyCFunction_TrampolineCall(cfunc, PyCFunction_GET_SELF(callable), arg); @@ -2749,7 +2749,7 @@ Py_DECREF(arg); Py_DECREF(callable); - if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } + if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error_tier_two; } STACK_SHRINK(oparg); STACK_SHRINK(1); stack_pointer[-1] = res; @@ -2787,7 +2787,7 @@ Py_DECREF(args[i]); } Py_DECREF(callable); - if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } + if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error_tier_two; } /* Not deopting because this doesn't mean our optimization was wrong. `res` can be NULL for valid reasons. Eg. getattr(x, 'invalid'). In those cases an exception is set, so we must @@ -2829,7 +2829,7 @@ Py_DECREF(args[i]); } Py_DECREF(callable); - if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } + if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error_tier_two; } STACK_SHRINK(oparg); STACK_SHRINK(1); stack_pointer[-1] = res; @@ -2858,14 +2858,14 @@ PyObject *arg = args[0]; Py_ssize_t len_i = PyObject_Length(arg); if (len_i < 0) { - goto error; + GOTO_ERROR(error); } res = PyLong_FromSsize_t(len_i); assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); Py_DECREF(callable); Py_DECREF(arg); - if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } + if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error_tier_two; } STACK_SHRINK(oparg); STACK_SHRINK(1); stack_pointer[-1] = res; @@ -2894,7 +2894,7 @@ PyObject *inst = args[0]; int retval = PyObject_IsInstance(inst, cls); if (retval < 0) { - goto error; + GOTO_ERROR(error); } res = PyBool_FromLong(retval); assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); @@ -2902,7 +2902,7 @@ Py_DECREF(inst); Py_DECREF(cls); Py_DECREF(callable); - if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } + if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error_tier_two; } STACK_SHRINK(oparg); STACK_SHRINK(1); stack_pointer[-1] = res; @@ -2935,7 +2935,7 @@ // This is slower but CPython promises to check all non-vectorcall // function calls. if (_Py_EnterRecursiveCallTstate(tstate, " while calling a Python object")) { - goto error; + GOTO_ERROR(error); } res = _PyCFunction_TrampolineCall(cfunc, self, arg); _Py_LeaveRecursiveCallTstate(tstate); @@ -2943,7 +2943,7 @@ Py_DECREF(self); Py_DECREF(arg); Py_DECREF(callable); - if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } + if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error_tier_two; } STACK_SHRINK(oparg); STACK_SHRINK(1); stack_pointer[-1] = res; @@ -2983,7 +2983,7 @@ Py_DECREF(args[i]); } Py_DECREF(callable); - if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } + if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error_tier_two; } STACK_SHRINK(oparg); STACK_SHRINK(1); stack_pointer[-1] = res; @@ -3017,14 +3017,14 @@ // This is slower but CPython promises to check all non-vectorcall // function calls. if (_Py_EnterRecursiveCallTstate(tstate, " while calling a Python object")) { - goto error; + GOTO_ERROR(error); } res = _PyCFunction_TrampolineCall(cfunc, self, NULL); _Py_LeaveRecursiveCallTstate(tstate); assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); Py_DECREF(self); Py_DECREF(callable); - if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } + if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error_tier_two; } STACK_SHRINK(oparg); STACK_SHRINK(1); stack_pointer[-1] = res; @@ -3063,7 +3063,7 @@ Py_DECREF(args[i]); } Py_DECREF(callable); - if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error; } + if (res == NULL) { STACK_SHRINK(oparg); goto pop_2_error_tier_two; } STACK_SHRINK(oparg); STACK_SHRINK(1); stack_pointer[-1] = res; @@ -3081,7 +3081,7 @@ Py_DECREF(codeobj); if (func_obj == NULL) { - goto error; + GOTO_ERROR(error); } _PyFunction_SetVersion( @@ -3137,7 +3137,7 @@ Py_DECREF(start); Py_DECREF(stop); Py_XDECREF(step); - if (slice == NULL) { STACK_SHRINK(((oparg == 3) ? 1 : 0)); goto pop_2_error; } + if (slice == NULL) { STACK_SHRINK(((oparg == 3) ? 1 : 0)); goto pop_2_error_tier_two; } STACK_SHRINK(((oparg == 3) ? 1 : 0)); STACK_SHRINK(1); stack_pointer[-1] = slice; @@ -3153,7 +3153,7 @@ conv_fn = CONVERSION_FUNCTIONS[oparg]; result = conv_fn(value); Py_DECREF(value); - if (result == NULL) goto pop_1_error; + if (result == NULL) goto pop_1_error_tier_two; stack_pointer[-1] = result; break; } @@ -3167,7 +3167,7 @@ if (!PyUnicode_CheckExact(value)) { res = PyObject_Format(value, NULL); Py_DECREF(value); - if (res == NULL) goto pop_1_error; + if (res == NULL) goto pop_1_error_tier_two; } else { res = value; @@ -3185,7 +3185,7 @@ res = PyObject_Format(value, fmt_spec); Py_DECREF(value); Py_DECREF(fmt_spec); - if (res == NULL) goto pop_2_error; + if (res == NULL) goto pop_2_error_tier_two; STACK_SHRINK(1); stack_pointer[-1] = res; break; @@ -3224,7 +3224,7 @@ res = _PyEval_BinaryOps[oparg](lhs, rhs); Py_DECREF(lhs); Py_DECREF(rhs); - if (res == NULL) goto pop_2_error; + if (res == NULL) goto pop_2_error_tier_two; STACK_SHRINK(1); stack_pointer[-1] = res; break; diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index fb4506e68765ec..b5e9d42a7aac03 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -48,7 +48,7 @@ uintptr_t code_version = _PyFrame_GetCode(frame)->_co_instrumentation_version; if (code_version != global_version) { if (_Py_Instrument(_PyFrame_GetCode(frame), tstate->interp)) { - goto error; + GOTO_ERROR(error); } next_instr--; } @@ -200,7 +200,7 @@ if (PyGen_Check(receiver)) { PyErr_SetObject(PyExc_StopIteration, value); if (monitor_stop_iteration(tstate, frame, next_instr-1)) { - goto error; + GOTO_ERROR(error); } PyErr_SetRaisedException(NULL); } @@ -229,7 +229,7 @@ if (PyGen_Check(receiver) || PyCoro_CheckExact(receiver)) { PyErr_SetObject(PyExc_StopIteration, value); if (monitor_stop_iteration(tstate, frame, next_instr-1)) { - goto error; + GOTO_ERROR(error); } PyErr_SetRaisedException(NULL); } @@ -1018,7 +1018,7 @@ int err = _Py_call_instrumentation_arg( tstate, PY_MONITORING_EVENT_PY_RETURN, frame, next_instr-1, retval); - if (err) goto error; + if (err) GOTO_ERROR(error); STACK_SHRINK(1); assert(EMPTY()); _PyFrame_SetStackPointer(frame, stack_pointer); @@ -1072,7 +1072,7 @@ int err = _Py_call_instrumentation_arg( tstate, PY_MONITORING_EVENT_PY_RETURN, frame, next_instr-1, retval); - if (err) goto error; + if (err) GOTO_ERROR(error); Py_INCREF(retval); assert(EMPTY()); _PyFrame_SetStackPointer(frame, stack_pointer); @@ -1136,7 +1136,7 @@ if (PyAsyncGen_CheckExact(aiter)) { awaitable = type->tp_as_async->am_anext(aiter); if (awaitable == NULL) { - goto error; + GOTO_ERROR(error); } } else { if (type->tp_as_async != NULL){ @@ -1146,7 +1146,7 @@ if (getter != NULL) { next_iter = (*getter)(aiter); if (next_iter == NULL) { - goto error; + GOTO_ERROR(error); } } else { @@ -1154,7 +1154,7 @@ "'async for' requires an iterator with " "__anext__ method, got %.100s", type->tp_name); - goto error; + GOTO_ERROR(error); } awaitable = _PyCoro_GetAwaitableIter(next_iter); @@ -1166,7 +1166,7 @@ Py_TYPE(next_iter)->tp_name); Py_DECREF(next_iter); - goto error; + GOTO_ERROR(error); } else { Py_DECREF(next_iter); } @@ -1258,7 +1258,7 @@ JUMPBY(oparg); } else { - goto error; + GOTO_ERROR(error); } } Py_DECREF(v); @@ -1301,7 +1301,7 @@ int err = _Py_call_instrumentation_arg( tstate, PY_MONITORING_EVENT_PY_YIELD, frame, next_instr-1, retval); - if (err) goto error; + if (err) GOTO_ERROR(error); tstate->exc_info = gen->gi_exc_state.previous_item; gen->gi_exc_state.previous_item = NULL; _Py_LeaveRecursiveCallPy(tstate); @@ -1364,7 +1364,7 @@ else { assert(PyLong_Check(lasti)); _PyErr_SetString(tstate, PyExc_SystemError, "lasti is not an int"); - goto error; + GOTO_ERROR(error); } } assert(exc && PyExceptionInstance_Check(exc)); @@ -1473,7 +1473,7 @@ if (ns == NULL) { _PyErr_Format(tstate, PyExc_SystemError, "no locals when deleting %R", name); - goto error; + GOTO_ERROR(error); } err = PyObject_DelItem(ns, name); // Can't use ERROR_IF here. @@ -1481,7 +1481,7 @@ _PyEval_FormatExcCheckArg(tstate, PyExc_NameError, NAME_ERROR_MSG, name); - goto error; + GOTO_ERROR(error); } DISPATCH(); } @@ -1639,7 +1639,7 @@ _PyEval_FormatExcCheckArg(tstate, PyExc_NameError, NAME_ERROR_MSG, name); } - goto error; + GOTO_ERROR(error); } DISPATCH(); } @@ -1664,7 +1664,7 @@ mod_or_class_dict = stack_pointer[-1]; PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); if (PyMapping_GetOptionalItem(mod_or_class_dict, name, &v) < 0) { - goto error; + GOTO_ERROR(error); } if (v == NULL) { v = PyDict_GetItemWithError(GLOBALS(), name); @@ -1672,17 +1672,17 @@ Py_INCREF(v); } else if (_PyErr_Occurred(tstate)) { - goto error; + GOTO_ERROR(error); } else { if (PyMapping_GetOptionalItem(BUILTINS(), name, &v) < 0) { - goto error; + GOTO_ERROR(error); } if (v == NULL) { _PyEval_FormatExcCheckArg( tstate, PyExc_NameError, NAME_ERROR_MSG, name); - goto error; + GOTO_ERROR(error); } } } @@ -1701,7 +1701,7 @@ } PyObject *name = GETITEM(FRAME_CO_NAMES, oparg); if (PyMapping_GetOptionalItem(mod_or_class_dict, name, &v) < 0) { - goto error; + GOTO_ERROR(error); } if (v == NULL) { v = PyDict_GetItemWithError(GLOBALS(), name); @@ -1709,17 +1709,17 @@ Py_INCREF(v); } else if (_PyErr_Occurred(tstate)) { - goto error; + GOTO_ERROR(error); } else { if (PyMapping_GetOptionalItem(BUILTINS(), name, &v) < 0) { - goto error; + GOTO_ERROR(error); } if (v == NULL) { _PyEval_FormatExcCheckArg( tstate, PyExc_NameError, NAME_ERROR_MSG, name); - goto error; + GOTO_ERROR(error); } } } @@ -1881,7 +1881,7 @@ // Fortunately we don't need its superpower. if (oldobj == NULL) { _PyEval_FormatExcUnbound(tstate, _PyFrame_GetCode(frame), oparg); - goto error; + GOTO_ERROR(error); } PyCell_SET(cell, NULL); Py_DECREF(oldobj); @@ -1898,7 +1898,7 @@ name = PyTuple_GET_ITEM(_PyFrame_GetCode(frame)->co_localsplusnames, oparg); if (PyMapping_GetOptionalItem(class_dict, name, &value) < 0) { Py_DECREF(class_dict); - goto error; + GOTO_ERROR(error); } Py_DECREF(class_dict); if (!value) { @@ -1906,7 +1906,7 @@ value = PyCell_GET(cell); if (value == NULL) { _PyEval_FormatExcUnbound(tstate, _PyFrame_GetCode(frame), oparg); - goto error; + GOTO_ERROR(error); } Py_INCREF(value); } @@ -2034,7 +2034,7 @@ values = stack_pointer - oparg; set = PySet_New(NULL); if (set == NULL) - goto error; + GOTO_ERROR(error); int err = 0; for (int i = 0; i < oparg; i++) { PyObject *item = values[i]; @@ -2121,7 +2121,7 @@ PyTuple_GET_SIZE(keys) != (Py_ssize_t)oparg) { _PyErr_SetString(tstate, PyExc_SystemError, "bad BUILD_CONST_KEY_MAP keys argument"); - goto error; // Pop the keys and values. + GOTO_ERROR(error); // Pop the keys and values. } map = _PyDict_FromItems( &PyTuple_GET_ITEM(keys, 0), 1, @@ -3208,7 +3208,7 @@ _PyErr_SetString(tstate, PyExc_TypeError, "cannot 'yield from' a coroutine object " "in a non-coroutine generator"); - goto error; + GOTO_ERROR(error); } iter = iterable; } @@ -3219,7 +3219,7 @@ /* `iterable` is not a generator. */ iter = PyObject_GetIter(iterable); if (iter == NULL) { - goto error; + GOTO_ERROR(error); } Py_DECREF(iterable); } @@ -3248,7 +3248,7 @@ if (next == NULL) { if (_PyErr_Occurred(tstate)) { if (!_PyErr_ExceptionMatches(tstate, PyExc_StopIteration)) { - goto error; + GOTO_ERROR(error); } monitor_raise(tstate, frame, next_instr-1); _PyErr_Clear(tstate); @@ -3282,7 +3282,7 @@ else { if (_PyErr_Occurred(tstate)) { if (!_PyErr_ExceptionMatches(tstate, PyExc_StopIteration)) { - goto error; + GOTO_ERROR(error); } monitor_raise(tstate, frame, here); _PyErr_Clear(tstate); @@ -3457,7 +3457,7 @@ "asynchronous context manager protocol", Py_TYPE(mgr)->tp_name); } - goto error; + GOTO_ERROR(error); } exit = _PyObject_LookupSpecial(mgr, &_Py_ID(__aexit__)); if (exit == NULL) { @@ -3469,7 +3469,7 @@ Py_TYPE(mgr)->tp_name); } Py_DECREF(enter); - goto error; + GOTO_ERROR(error); } Py_DECREF(mgr); res = _PyObject_CallNoArgs(enter); @@ -3500,7 +3500,7 @@ "context manager protocol", Py_TYPE(mgr)->tp_name); } - goto error; + GOTO_ERROR(error); } exit = _PyObject_LookupSpecial(mgr, &_Py_ID(__exit__)); if (exit == NULL) { @@ -3512,7 +3512,7 @@ Py_TYPE(mgr)->tp_name); } Py_DECREF(enter); - goto error; + GOTO_ERROR(error); } Py_DECREF(mgr); res = _PyObject_CallNoArgs(enter); @@ -3825,7 +3825,7 @@ // The frame has stolen all the arguments from the stack, // so there is no need to clean them up. if (new_frame == NULL) { - goto error; + GOTO_ERROR(error); } SKIP_OVER(INLINE_CACHE_ENTRIES_CALL); assert(1 + INLINE_CACHE_ENTRIES_CALL == next_instr - frame->instr_ptr); @@ -4180,7 +4180,7 @@ STAT_INC(CALL, hit); PyObject *self = _PyType_NewManagedObject(tp); if (self == NULL) { - goto error; + GOTO_ERROR(error); } Py_DECREF(tp); _PyInterpreterFrame *shim = _PyFrame_PushTrampolineUnchecked( @@ -4221,7 +4221,7 @@ PyErr_Format(PyExc_TypeError, "__init__() should return None, not '%.200s'", Py_TYPE(should_be_none)->tp_name); - goto error; + GOTO_ERROR(error); } STACK_SHRINK(1); DISPATCH(); @@ -4281,7 +4281,7 @@ // This is slower but CPython promises to check all non-vectorcall // function calls. if (_Py_EnterRecursiveCallTstate(tstate, " while calling a Python object")) { - goto error; + GOTO_ERROR(error); } PyObject *arg = args[0]; res = _PyCFunction_TrampolineCall(cfunc, PyCFunction_GET_SELF(callable), arg); @@ -4402,7 +4402,7 @@ PyObject *arg = args[0]; Py_ssize_t len_i = PyObject_Length(arg); if (len_i < 0) { - goto error; + GOTO_ERROR(error); } res = PyLong_FromSsize_t(len_i); assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); @@ -4439,7 +4439,7 @@ PyObject *inst = args[0]; int retval = PyObject_IsInstance(inst, cls); if (retval < 0) { - goto error; + GOTO_ERROR(error); } res = PyBool_FromLong(retval); assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); @@ -4506,7 +4506,7 @@ // This is slower but CPython promises to check all non-vectorcall // function calls. if (_Py_EnterRecursiveCallTstate(tstate, " while calling a Python object")) { - goto error; + GOTO_ERROR(error); } res = _PyCFunction_TrampolineCall(cfunc, self, arg); _Py_LeaveRecursiveCallTstate(tstate); @@ -4590,7 +4590,7 @@ // This is slower but CPython promises to check all non-vectorcall // function calls. if (_Py_EnterRecursiveCallTstate(tstate, " while calling a Python object")) { - goto error; + GOTO_ERROR(error); } res = _PyCFunction_TrampolineCall(cfunc, self, NULL); _Py_LeaveRecursiveCallTstate(tstate); @@ -4704,7 +4704,7 @@ // The frame has stolen all the arguments from the stack, // so there is no need to clean them up. if (new_frame == NULL) { - goto error; + GOTO_ERROR(error); } assert(next_instr - frame->instr_ptr == 1); frame->return_offset = 1; @@ -4764,11 +4764,11 @@ assert(kwargs == NULL || PyDict_CheckExact(kwargs)); if (!PyTuple_CheckExact(callargs)) { if (check_args_iterable(tstate, func, callargs) < 0) { - goto error; + GOTO_ERROR(error); } PyObject *tuple = PySequence_Tuple(callargs); if (tuple == NULL) { - goto error; + GOTO_ERROR(error); } Py_SETREF(callargs, tuple); } @@ -4782,7 +4782,7 @@ int err = _Py_call_instrumentation_2args( tstate, PY_MONITORING_EVENT_CALL, frame, next_instr-1, func, arg); - if (err) goto error; + if (err) GOTO_ERROR(error); result = PyObject_Call(func, callargs, kwargs); if (result == NULL) { _Py_call_instrumentation_exc2( @@ -4813,7 +4813,7 @@ // Need to manually shrink the stack since we exit with DISPATCH_INLINED. STACK_SHRINK(oparg + 3); if (new_frame == NULL) { - goto error; + GOTO_ERROR(error); } assert(next_instr - frame->instr_ptr == 1); frame->return_offset = 1; @@ -4843,7 +4843,7 @@ Py_DECREF(codeobj); if (func_obj == NULL) { - goto error; + GOTO_ERROR(error); } _PyFunction_SetVersion( @@ -4892,7 +4892,7 @@ PyFunctionObject *func = (PyFunctionObject *)frame->f_funcobj; PyGenObject *gen = (PyGenObject *)_Py_MakeCoro(func); if (gen == NULL) { - goto error; + GOTO_ERROR(error); } assert(EMPTY()); _PyFrame_SetStackPointer(frame, stack_pointer); diff --git a/Tools/cases_generator/instructions.py b/Tools/cases_generator/instructions.py index c6b551675e3e7e..e985f176608afb 100644 --- a/Tools/cases_generator/instructions.py +++ b/Tools/cases_generator/instructions.py @@ -202,6 +202,8 @@ def write_body( ninputs, symbolic = list_effect_size(ieffs) if ninputs: label = f"pop_{ninputs}_{label}" + if tier == TIER_TWO: + label = label + "_tier_two" if symbolic: out.write_raw( f"{space}if ({cond}) {{ STACK_SHRINK({symbolic}); goto {label}; }}\n" From 157a4504673a2beff3de266acbc4a5f9beaf29b7 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Fri, 27 Oct 2023 14:00:01 -0700 Subject: [PATCH 02/24] Control lltrace via PYTHON_LLTRACE=N This replaces PYTHONUOPSDEBUG=N. The meaning of N is the same (for now): 0: no tracing 1: print when tier 2 trace created 2: print contents of tier 2 trace 3: print every uop executed 4: print optimization attempts and details 5: print tier 1 instructions and stack --- Python/ceval.c | 14 +++++++------- Python/ceval_macros.h | 2 +- Python/executor.c | 6 +++--- Python/optimizer.c | 12 ++++++------ 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/Python/ceval.c b/Python/ceval.c index e3a7c5f38403a7..c89e272258429e 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -201,15 +201,15 @@ maybe_lltrace_resume_frame(_PyInterpreterFrame *frame, _PyInterpreterFrame *skip if (r < 0) { return -1; } - int lltrace = r; + int lltrace = r * 5; // Levels 1-4 only trace uops if (!lltrace) { - // When tracing executed uops, also trace bytecode - char *uop_debug = Py_GETENV("PYTHONUOPSDEBUG"); - if (uop_debug != NULL && *uop_debug >= '0') { - lltrace = (*uop_debug - '0') >= 5; // TODO: Parse an int and all that + // Can also be controlled by environment variable + char *python_lltrace = Py_GETENV("PYTHON_LLTRACE"); + if (python_lltrace != NULL && *python_lltrace >= '0') { + lltrace = *python_lltrace - '0'; // TODO: Parse an int and all that } } - if (lltrace) { + if (lltrace >= 5) { lltrace_resume_frame(frame); } return lltrace; @@ -913,7 +913,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } /* Resume normal execution */ #ifdef LLTRACE - if (lltrace) { + if (lltrace >= 5) { lltrace_resume_frame(frame); } #endif diff --git a/Python/ceval_macros.h b/Python/ceval_macros.h index 01b2422f6f6e31..418cb70ddf04cc 100644 --- a/Python/ceval_macros.h +++ b/Python/ceval_macros.h @@ -84,7 +84,7 @@ /* PRE_DISPATCH_GOTO() does lltrace if enabled. Normally a no-op */ #ifdef LLTRACE -#define PRE_DISPATCH_GOTO() if (lltrace) { \ +#define PRE_DISPATCH_GOTO() if (lltrace >= 5) { \ lltrace_instruction(frame, stack_pointer, next_instr); } #else #define PRE_DISPATCH_GOTO() ((void)0) diff --git a/Python/executor.c b/Python/executor.c index f512492e80f1dc..b0a20a1a1b3604 100644 --- a/Python/executor.c +++ b/Python/executor.c @@ -48,10 +48,10 @@ _PyInterpreterFrame * _PyUopExecute(_PyExecutorObject *executor, _PyInterpreterFrame *frame, PyObject **stack_pointer) { #ifdef Py_DEBUG - char *uop_debug = Py_GETENV("PYTHONUOPSDEBUG"); + char *python_lltrace = Py_GETENV("PYTHON_LLTRACE"); int lltrace = 0; - if (uop_debug != NULL && *uop_debug >= '0') { - lltrace = *uop_debug - '0'; // TODO: Parse an int and all that + if (python_lltrace != NULL && *python_lltrace >= '0') { + lltrace = *python_lltrace - '0'; // TODO: Parse an int and all that } #define DPRINTF(level, ...) \ if (lltrace >= (level)) { printf(__VA_ARGS__); } diff --git a/Python/optimizer.c b/Python/optimizer.c index 6402287a412a35..d094af45774bfc 100644 --- a/Python/optimizer.c +++ b/Python/optimizer.c @@ -435,10 +435,10 @@ translate_bytecode_to_trace( int trace_stack_depth = 0; #ifdef Py_DEBUG - char *uop_debug = Py_GETENV("PYTHONUOPSDEBUG"); + char *python_lltrace = Py_GETENV("PYTHON_LLTRACE"); int lltrace = 0; - if (uop_debug != NULL && *uop_debug >= '0') { - lltrace = *uop_debug - '0'; // TODO: Parse an int and all that + if (python_lltrace != NULL && *python_lltrace >= '0') { + lltrace = *python_lltrace - '0'; // TODO: Parse an int and all that } #endif @@ -883,10 +883,10 @@ remove_unneeded_uops(_PyUOpInstruction *trace, int trace_length) if (dest < last_instr) { int new_trace_length = move_stubs(trace, dest, last_instr, trace_length); #ifdef Py_DEBUG - char *uop_debug = Py_GETENV("PYTHONUOPSDEBUG"); + char *python_lltrace = Py_GETENV("PYTHON_LLTRACE"); int lltrace = 0; - if (uop_debug != NULL && *uop_debug >= '0') { - lltrace = *uop_debug - '0'; // TODO: Parse an int and all that + if (python_lltrace != NULL && *python_lltrace >= '0') { + lltrace = *python_lltrace - '0'; // TODO: Parse an int and all that } if (lltrace >= 2) { printf("Optimized trace (length %d+%d = %d, saved %d):\n", From d1b9c1b2a8f960281b2305500e7fc1343be21a81 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Fri, 27 Oct 2023 14:05:30 -0700 Subject: [PATCH 03/24] Rename PYTHONUOPS to PYTHON_UOPS for consistency --- Python/pylifecycle.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index 7b56034541756a..3c57056fb81e81 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -1229,7 +1229,7 @@ init_interp_main(PyThreadState *tstate) // Turn on experimental tier 2 (uops-based) optimizer if (is_main_interp) { - char *envvar = Py_GETENV("PYTHONUOPS"); + char *envvar = Py_GETENV("PYTHON_UOPS"); int enabled = envvar != NULL && *envvar > '0'; if (_Py_get_xoption(&config->xoptions, L"uops") != NULL) { enabled = 1; From d805312af802cbe9e2a02f47a4930d2c702507ca Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Fri, 27 Oct 2023 15:50:56 -0700 Subject: [PATCH 04/24] Integrate Tier 2 into _PyEval_EvalFrameDefault --- Python/bytecodes.c | 15 +++-- Python/ceval.c | 128 +++++++++++++++++++++++++++++++++++-- Python/executor.c | 14 ++-- Python/executor_cases.c.h | 8 +-- Python/generated_cases.c.h | 7 +- 5 files changed, 150 insertions(+), 22 deletions(-) diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 4098e891e5faee..1a65acf08959f6 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -2318,13 +2318,16 @@ dummy_func( JUMPBY(1-original_oparg); frame->instr_ptr = next_instr; Py_INCREF(executor); + if (executor->execute == _PyUopExecute) { + self = (_PyUOpExecutorObject *)executor; + goto enter_tier_two; + } frame = executor->execute(executor, frame, stack_pointer); if (frame == NULL) { frame = tstate->current_frame; goto resume_with_error; } - next_instr = frame->instr_ptr; - goto resume_frame; + goto enter_tier_one; } inst(POP_JUMP_IF_FALSE, (unused/1, cond -- )) { @@ -3947,18 +3950,18 @@ dummy_func( op(_POP_JUMP_IF_FALSE, (flag -- )) { if (Py_IsFalse(flag)) { - pc = oparg; + next_uop = self->trace + oparg; } } op(_POP_JUMP_IF_TRUE, (flag -- )) { if (Py_IsTrue(flag)) { - pc = oparg; + next_uop = self->trace + oparg; } } op(_JUMP_TO_TOP, (--)) { - pc = 0; + next_uop = self->trace; CHECK_EVAL_BREAKER(); } @@ -3981,7 +3984,7 @@ dummy_func( _PyFrame_SetStackPointer(frame, stack_pointer); Py_DECREF(self); OPT_HIST(trace_uop_execution_counter, trace_run_length_hist); - return frame; + goto enter_tier_one; } op(_INSERT, (unused[oparg], top -- top, unused[oparg])) { diff --git a/Python/ceval.c b/Python/ceval.c index c89e272258429e..43d0c0a370f31e 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -679,9 +679,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int #ifdef Py_STATS int lastopcode = 0; #endif - // opcode is an 8-bit value to improve the code generated by MSVC - // for the big switch below (in combination with the EXTRA_CASES macro). - uint8_t opcode; /* Current opcode */ + int opcode; /* Current opcode */ int oparg; /* Current opcode argument, if any */ #ifdef LLTRACE int lltrace = 0; @@ -729,6 +727,9 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int goto resume_with_error; } + /* State shared between Tier 1 and Tier 2 interpreter */ + _PyUOpExecutorObject *self = NULL; + /* Local "register" variables. * These are cached values from the frame and code object. */ @@ -765,7 +766,9 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int /* Start instructions */ #if !USE_COMPUTED_GOTOS dispatch_opcode: - switch (opcode) + // Cast to an 8-bit value to improve the code generated by MSVC + // (in combination with the EXTRA_CASES macro). + switch ((uint8_t)opcode) #endif { @@ -942,6 +945,123 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int stack_pointer = _PyFrame_GetStackPointer(frame); goto error; + + +// The Tier 2 interpreter is also here! +enter_tier_two: + +#undef LOAD_IP +#define LOAD_IP(UNUSED) \ +do { ip_offset = (_Py_CODEUNIT *)_PyFrame_GetCode(frame)->co_code_adaptive; } while (0) + +#undef GOTO_ERROR +#define GOTO_ERROR(LABEL) goto LABEL ## _tier_two + +#undef DEOPT_IF +#define DEOPT_IF(COND, INSTNAME) \ + if ((COND)) { \ + goto deoptimize_tier_two;\ + } + +#ifdef Py_STATS +// Disable these macros that apply to Tier 1 stats when we are in Tier 2 +#undef STAT_INC +#define STAT_INC(opname, name) ((void)0) +#undef STAT_DEC +#define STAT_DEC(opname, name) ((void)0) +#undef CALL_STAT_INC +#define CALL_STAT_INC(name) ((void)0) +#endif + +#undef ENABLE_SPECIALIZATION +#define ENABLE_SPECIALIZATION 0 + +#ifdef Py_DEBUG + #define DPRINTF(level, ...) \ + if (lltrace >= (level)) { printf(__VA_ARGS__); } +#else + #define DPRINTF(level, ...) +#endif + + CHECK_EVAL_BREAKER(); + + OPT_STAT_INC(traces_executed); + _Py_CODEUNIT *ip_offset = (_Py_CODEUNIT *)_PyFrame_GetCode(frame)->co_code_adaptive; + _PyUOpInstruction *next_uop = self->trace; + uint64_t operand; +#ifdef Py_STATS + uint64_t trace_uop_execution_counter = 0; +#endif + + for (;;) { + opcode = next_uop->opcode; + oparg = next_uop->oparg; + operand = next_uop->operand; + DPRINTF(3, + "%4d: uop %s, oparg %d, operand %" PRIu64 ", stack_level %d\n", + (int)(next_uop - self->trace), + opcode < 256 ? _PyOpcode_OpName[opcode] : _PyOpcode_uop_name[opcode], + oparg, + operand, + (int)(stack_pointer - _PyFrame_Stackbase(frame))); + next_uop++; + OPT_STAT_INC(uops_executed); + UOP_EXE_INC(opcode); +#ifdef Py_STATS + trace_uop_execution_counter++; +#endif + + switch (opcode) { + +#undef TIER_ONE +#define TIER_TWO 2 +#include "executor_cases.c.h" + + default: + { + fprintf(stderr, "Unknown uop %d, oparg %d, operand %" PRIu64 "\n", + opcode, oparg, operand); + Py_FatalError("Unknown uop"); + } + + } + } + +unbound_local_error_tier_two: + _PyEval_FormatExcCheckArg(tstate, PyExc_UnboundLocalError, + UNBOUNDLOCAL_ERROR_MSG, + PyTuple_GetItem(_PyFrame_GetCode(frame)->co_localsplusnames, oparg) + ); + goto error_tier_two; + +pop_4_error_tier_two: + STACK_SHRINK(1); +pop_3_error_tier_two: + STACK_SHRINK(1); +pop_2_error_tier_two: + STACK_SHRINK(1); +pop_1_error_tier_two: + STACK_SHRINK(1); +error_tier_two: + DPRINTF(2, "Error: [Opcode %d, operand %" PRIu64 "]\n", opcode, operand); + OPT_HIST(trace_uop_execution_counter, trace_run_length_hist); + frame->return_offset = 0; // Don't leave this random + _PyFrame_SetStackPointer(frame, stack_pointer); + Py_DECREF(self); + goto resume_with_error; + +deoptimize_tier_two: + // On DEOPT_IF we just repeat the last instruction. + // This presumes nothing was popped from the stack (nor pushed). + DPRINTF(2, "DEOPT: [Opcode %d, operand %" PRIu64 "]\n", opcode, operand); + OPT_HIST(trace_uop_execution_counter, trace_run_length_hist); + frame->return_offset = 0; // Dispatch to frame->instr_ptr + _PyFrame_SetStackPointer(frame, stack_pointer); + Py_DECREF(self); +enter_tier_one: + next_instr = frame->instr_ptr; + goto resume_frame; + } #if defined(__GNUC__) # pragma GCC diagnostic pop diff --git a/Python/executor.c b/Python/executor.c index b0a20a1a1b3604..581c77f3480c88 100644 --- a/Python/executor.c +++ b/Python/executor.c @@ -47,6 +47,7 @@ _PyInterpreterFrame * _PyUopExecute(_PyExecutorObject *executor, _PyInterpreterFrame *frame, PyObject **stack_pointer) { + Py_FatalError("Tier 2 is now inlined into Tier 1"); #ifdef Py_DEBUG char *python_lltrace = Py_GETENV("PYTHON_LLTRACE"); int lltrace = 0; @@ -74,7 +75,7 @@ _PyUopExecute(_PyExecutorObject *executor, _PyInterpreterFrame *frame, PyObject OPT_STAT_INC(traces_executed); _Py_CODEUNIT *ip_offset = (_Py_CODEUNIT *)_PyFrame_GetCode(frame)->co_code_adaptive; - int pc = 0; + _PyUOpInstruction *next_uop = self->trace; int opcode; int oparg; uint64_t operand; @@ -83,17 +84,17 @@ _PyUopExecute(_PyExecutorObject *executor, _PyInterpreterFrame *frame, PyObject #endif for (;;) { - opcode = self->trace[pc].opcode; - oparg = self->trace[pc].oparg; - operand = self->trace[pc].operand; + opcode = next_uop->opcode; + oparg = next_uop->oparg; + operand = next_uop->operand; DPRINTF(3, "%4d: uop %s, oparg %d, operand %" PRIu64 ", stack_level %d\n", - pc, + (int)(next_uop - self->trace), opcode < 256 ? _PyOpcode_OpName[opcode] : _PyOpcode_uop_name[opcode], oparg, operand, (int)(stack_pointer - _PyFrame_Stackbase(frame))); - pc++; + next_uop++; OPT_STAT_INC(uops_executed); UOP_EXE_INC(opcode); #ifdef Py_STATS @@ -146,5 +147,6 @@ _PyUopExecute(_PyExecutorObject *executor, _PyInterpreterFrame *frame, PyObject frame->return_offset = 0; // Dispatch to frame->instr_ptr _PyFrame_SetStackPointer(frame, stack_pointer); Py_DECREF(self); +enter_tier_one: return frame; } diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index 5616c47e58df1b..0815ebf3507ee5 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -3245,7 +3245,7 @@ PyObject *flag; flag = stack_pointer[-1]; if (Py_IsFalse(flag)) { - pc = oparg; + next_uop = self->trace + oparg; } STACK_SHRINK(1); break; @@ -3255,14 +3255,14 @@ PyObject *flag; flag = stack_pointer[-1]; if (Py_IsTrue(flag)) { - pc = oparg; + next_uop = self->trace + oparg; } STACK_SHRINK(1); break; } case _JUMP_TO_TOP: { - pc = 0; + next_uop = self->trace; CHECK_EVAL_BREAKER(); break; } @@ -3288,7 +3288,7 @@ _PyFrame_SetStackPointer(frame, stack_pointer); Py_DECREF(self); OPT_HIST(trace_uop_execution_counter, trace_run_length_hist); - return frame; + goto enter_tier_one; break; } diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index b5e9d42a7aac03..388d6373d1a999 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -2999,13 +2999,16 @@ JUMPBY(1-original_oparg); frame->instr_ptr = next_instr; Py_INCREF(executor); + if (executor->execute == _PyUopExecute) { + self = (_PyUOpExecutorObject *)executor; + goto enter_tier_two; + } frame = executor->execute(executor, frame, stack_pointer); if (frame == NULL) { frame = tstate->current_frame; goto resume_with_error; } - next_instr = frame->instr_ptr; - goto resume_frame; + goto enter_tier_one; } TARGET(POP_JUMP_IF_FALSE) { From e0e60ced06794dba547a03bf61729484eb361f53 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Wed, 9 Aug 2023 12:53:14 -0700 Subject: [PATCH 05/24] DO NOT MERGE: Always use -Xuops --- Python/pylifecycle.c | 1 + 1 file changed, 1 insertion(+) diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index 3c57056fb81e81..cb7e4d028d8efa 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -1234,6 +1234,7 @@ init_interp_main(PyThreadState *tstate) if (_Py_get_xoption(&config->xoptions, L"uops") != NULL) { enabled = 1; } + enabled = 1; // TEMPORARY: always enable if (enabled) { PyObject *opt = PyUnstable_Optimizer_NewUOpOptimizer(); if (opt == NULL) { From a0aed59fb35d93bd957d79d058bbb0b45c53f57a Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Tue, 31 Oct 2023 15:44:59 -0700 Subject: [PATCH 06/24] Get rid of separate executor.c file --- Makefile.pre.in | 7 --- Python/executor.c | 153 --------------------------------------------- Python/optimizer.c | 9 +++ 3 files changed, 9 insertions(+), 160 deletions(-) delete mode 100644 Python/executor.c diff --git a/Makefile.pre.in b/Makefile.pre.in index f2b252cce9775c..2950d87700ed5e 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -412,7 +412,6 @@ PYTHON_OBJS= \ Python/crossinterp.o \ Python/dynamic_annotations.o \ Python/errors.o \ - Python/executor.o \ Python/flowgraph.o \ Python/frame.o \ Python/frozenmain.o \ @@ -1603,12 +1602,6 @@ Python/ceval.o: \ $(srcdir)/Python/generated_cases.c.h \ $(srcdir)/Python/opcode_targets.h -Python/executor.o: \ - $(srcdir)/Include/internal/pycore_opcode_metadata.h \ - $(srcdir)/Include/internal/pycore_optimizer.h \ - $(srcdir)/Python/ceval_macros.h \ - $(srcdir)/Python/executor_cases.c.h - Python/flowgraph.o: \ $(srcdir)/Include/internal/pycore_opcode_metadata.h diff --git a/Python/executor.c b/Python/executor.c deleted file mode 100644 index 85983a95a72dd9..00000000000000 --- a/Python/executor.c +++ /dev/null @@ -1,153 +0,0 @@ -#include "Python.h" - -#include "opcode.h" - -#include "pycore_bitutils.h" -#include "pycore_call.h" -#include "pycore_ceval.h" -#include "pycore_dict.h" -#include "pycore_emscripten_signal.h" -#include "pycore_intrinsics.h" -#include "pycore_long.h" -#include "pycore_object.h" -#include "pycore_opcode_metadata.h" -#include "pycore_opcode_utils.h" -#include "pycore_pyerrors.h" -#include "pycore_range.h" -#include "pycore_setobject.h" // _PySet_Update() -#include "pycore_sliceobject.h" -#include "pycore_uops.h" - -#define TIER_TWO 2 -#include "ceval_macros.h" - -#undef GOTO_ERROR -#define GOTO_ERROR(LABEL) goto LABEL ## _tier_two - -#undef DEOPT_IF -#define DEOPT_IF(COND, INSTNAME) \ - if ((COND)) { \ - UOP_STAT_INC(INSTNAME, miss); \ - goto deoptimize_tier_two;\ - } - -#ifdef Py_STATS -// Disable these macros that apply to Tier 1 stats when we are in Tier 2 -#undef STAT_INC -#define STAT_INC(opname, name) ((void)0) -#undef STAT_DEC -#define STAT_DEC(opname, name) ((void)0) -#undef CALL_STAT_INC -#define CALL_STAT_INC(name) ((void)0) -#endif - -#undef ENABLE_SPECIALIZATION -#define ENABLE_SPECIALIZATION 0 - - -_PyInterpreterFrame * -_PyUopExecute(_PyExecutorObject *executor, _PyInterpreterFrame *frame, PyObject **stack_pointer) -{ - Py_FatalError("Tier 2 is now inlined into Tier 1"); -#ifdef Py_DEBUG - char *python_lltrace = Py_GETENV("PYTHON_LLTRACE"); - int lltrace = 0; - if (python_lltrace != NULL && *python_lltrace >= '0') { - lltrace = *python_lltrace - '0'; // TODO: Parse an int and all that - } - #define DPRINTF(level, ...) \ - if (lltrace >= (level)) { printf(__VA_ARGS__); } -#else - #define DPRINTF(level, ...) -#endif - - DPRINTF(3, - "Entering _PyUopExecute for %s (%s:%d) at byte offset %ld\n", - PyUnicode_AsUTF8(_PyFrame_GetCode(frame)->co_qualname), - PyUnicode_AsUTF8(_PyFrame_GetCode(frame)->co_filename), - _PyFrame_GetCode(frame)->co_firstlineno, - 2 * (long)(frame->instr_ptr - - (_Py_CODEUNIT *)_PyFrame_GetCode(frame)->co_code_adaptive)); - - PyThreadState *tstate = _PyThreadState_GET(); - _PyUOpExecutorObject *self = (_PyUOpExecutorObject *)executor; - - CHECK_EVAL_BREAKER(); - - OPT_STAT_INC(traces_executed); - _Py_CODEUNIT *ip_offset = (_Py_CODEUNIT *)_PyFrame_GetCode(frame)->co_code_adaptive; - _PyUOpInstruction *next_uop = self->trace; - int opcode; - int oparg; - uint64_t operand; -#ifdef Py_STATS - uint64_t trace_uop_execution_counter = 0; -#endif - - for (;;) { - opcode = next_uop->opcode; - oparg = next_uop->oparg; - operand = next_uop->operand; - DPRINTF(3, - "%4d: uop %s, oparg %d, operand %" PRIu64 ", stack_level %d\n", - (int)(next_uop - self->trace), - opcode < 256 ? _PyOpcode_OpName[opcode] : _PyOpcode_uop_name[opcode], - oparg, - operand, - (int)(stack_pointer - _PyFrame_Stackbase(frame))); - next_uop++; - OPT_STAT_INC(uops_executed); - UOP_STAT_INC(opcode, execution_count); -#ifdef Py_STATS - trace_uop_execution_counter++; -#endif - - switch (opcode) { - -#include "executor_cases.c.h" - - default: - { - fprintf(stderr, "Unknown uop %d, operand %" PRIu64 "\n", opcode, operand); - Py_FatalError("Unknown uop"); - } - - } - } - -unbound_local_error_tier_two: - _PyEval_FormatExcCheckArg(tstate, PyExc_UnboundLocalError, - UNBOUNDLOCAL_ERROR_MSG, - PyTuple_GetItem(_PyFrame_GetCode(frame)->co_localsplusnames, oparg) - ); - goto error_tier_two; - -pop_4_error_tier_two: - STACK_SHRINK(1); -pop_3_error_tier_two: - STACK_SHRINK(1); -pop_2_error_tier_two: - STACK_SHRINK(1); -pop_1_error_tier_two: - STACK_SHRINK(1); -error_tier_two: - // On ERROR_IF we return NULL as the frame. - // The caller recovers the frame from tstate->current_frame. - DPRINTF(2, "Error: [Opcode %d, operand %" PRIu64 "]\n", opcode, operand); - OPT_HIST(trace_uop_execution_counter, trace_run_length_hist); - frame->return_offset = 0; // Don't leave this random - _PyFrame_SetStackPointer(frame, stack_pointer); - Py_DECREF(self); - return NULL; - -deoptimize_tier_two: - // On DEOPT_IF we just repeat the last instruction. - // This presumes nothing was popped from the stack (nor pushed). - DPRINTF(2, "DEOPT: [Opcode %d, operand %" PRIu64 "]\n", opcode, operand); - OPT_HIST(trace_uop_execution_counter, trace_run_length_hist); - frame->return_offset = 0; // Dispatch to frame->instr_ptr - _PyFrame_SetStackPointer(frame, stack_pointer); - Py_DECREF(self); -enter_tier_one: - return frame; -} diff --git a/Python/optimizer.c b/Python/optimizer.c index a0b17c755d9e41..0e5b4370ccb946 100644 --- a/Python/optimizer.c +++ b/Python/optimizer.c @@ -939,6 +939,15 @@ uop_optimize( return 1; } +/* Dummy execute() function for Uop Executor. + * The actual implementation is inlined in ceval.c, + * in _PyEval_EvalFrameDefault(). */ +_PyInterpreterFrame * +_PyUopExecute(_PyExecutorObject *executor, _PyInterpreterFrame *frame, PyObject **stack_pointer) +{ + Py_FatalError("Tier 2 is now inlined into Tier 1"); +} + static void uop_opt_dealloc(PyObject *self) { PyObject_Free(self); From 75605c7077da1e697615f4126daecbd0494b8f72 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Tue, 31 Oct 2023 16:17:40 -0700 Subject: [PATCH 07/24] Most suggestions from Mark's code review - Remove CHECK_EVAL_BREAKER() from top of Tier 2 loop - Make Tier 2 default case Py_UNREACHABLE() in non-debug mode - GOTO_TIER_TWO() macro instead of `goto enter_tier_two` - Inline ip_offset (Tier 2 LOAD_IP() is now a no-op) - Move #define/#under TIER_ONE/TIER_TWO into generated .c.h files - ceval_macros.h defines Tier 1 macros, Tier 2 is inlined in ceval.c --- Python/bytecodes.c | 5 +++-- Python/ceval.c | 14 +++++--------- Python/ceval_macros.h | 25 +++++-------------------- Python/executor_cases.c.h | 10 +++++++++- Python/generated_cases.c.h | 9 ++++++++- Tools/cases_generator/generate_cases.py | 20 ++++++++++++++++++++ 6 files changed, 50 insertions(+), 33 deletions(-) diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 0a0f9299973bbd..3c84450b3ea39b 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -2314,7 +2314,7 @@ dummy_func( Py_INCREF(executor); if (executor->execute == _PyUopExecute) { self = (_PyUOpExecutorObject *)executor; - goto enter_tier_two; + GOTO_TIER_TWO(); } frame = executor->execute(executor, frame, stack_pointer); if (frame == NULL) { @@ -3942,7 +3942,8 @@ dummy_func( op(_SET_IP, (--)) { TIER_TWO_ONLY - frame->instr_ptr = ip_offset + oparg; + // TODO: Put the code pointer in `operand` to avoid indirection via `frame` + frame->instr_ptr = _PyCode_CODE(_PyFrame_GetCode(frame)) + oparg; } op(_SAVE_RETURN_OFFSET, (--)) { diff --git a/Python/ceval.c b/Python/ceval.c index 73073514960395..413fcc06cbdb04 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -611,10 +611,8 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) return _PyEval_EvalFrame(tstate, f->f_frame, throwflag); } -#define TIER_ONE 1 #include "ceval_macros.h" - int _Py_CheckRecursiveCallPy( PyThreadState *tstate) { @@ -952,8 +950,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int enter_tier_two: #undef LOAD_IP -#define LOAD_IP(UNUSED) \ -do { ip_offset = (_Py_CODEUNIT *)_PyFrame_GetCode(frame)->co_code_adaptive; } while (0) +#define LOAD_IP(UNUSED) (void)0 #undef GOTO_ERROR #define GOTO_ERROR(LABEL) goto LABEL ## _tier_two @@ -984,10 +981,7 @@ do { ip_offset = (_Py_CODEUNIT *)_PyFrame_GetCode(frame)->co_code_adaptive; } wh #define DPRINTF(level, ...) #endif - CHECK_EVAL_BREAKER(); - OPT_STAT_INC(traces_executed); - _Py_CODEUNIT *ip_offset = (_Py_CODEUNIT *)_PyFrame_GetCode(frame)->co_code_adaptive; _PyUOpInstruction *next_uop = self->trace; uint64_t operand; #ifdef Py_STATS @@ -1013,16 +1007,18 @@ do { ip_offset = (_Py_CODEUNIT *)_PyFrame_GetCode(frame)->co_code_adaptive; } wh switch (opcode) { -#undef TIER_ONE -#define TIER_TWO 2 #include "executor_cases.c.h" default: +#ifdef Py_DEBUG { fprintf(stderr, "Unknown uop %d, oparg %d, operand %" PRIu64 "\n", opcode, oparg, operand); Py_FatalError("Unknown uop"); } +#else + Py_UNREACHABLE(); +#endif } } diff --git a/Python/ceval_macros.h b/Python/ceval_macros.h index ab405b3306fc3d..d9560f5e131c98 100644 --- a/Python/ceval_macros.h +++ b/Python/ceval_macros.h @@ -377,27 +377,14 @@ static inline void _Py_LeaveRecursiveCallPy(PyThreadState *tstate) { /* Implementation of "macros" that modify the instruction pointer, * stack pointer, or frame pointer. - * These need to treated differently by tier 1 and 2. */ - -#if TIER_ONE + * These need to treated differently by tier 1 and 2. + * The Tier 1 version is here; Tier 2 is inlined in ceval.c. */ #define LOAD_IP(OFFSET) do { \ next_instr = frame->instr_ptr + (OFFSET); \ } while (0) -#define STORE_SP() \ -_PyFrame_SetStackPointer(frame, stack_pointer) - -#define LOAD_SP() \ -stack_pointer = _PyFrame_GetStackPointer(frame); - -#endif - - -#if TIER_TWO - -#define LOAD_IP(UNUSED) \ -do { ip_offset = (_Py_CODEUNIT *)_PyFrame_GetCode(frame)->co_code_adaptive; } while (0) +/* There's no STORE_IP(), it's inlined by the code generator. */ #define STORE_SP() \ _PyFrame_SetStackPointer(frame, stack_pointer) @@ -405,8 +392,6 @@ _PyFrame_SetStackPointer(frame, stack_pointer) #define LOAD_SP() \ stack_pointer = _PyFrame_GetStackPointer(frame); -#endif - - - +/* Tier-switching macros. */ +#define GOTO_TIER_TWO() goto enter_tier_two; diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index 15709cc50a6e9a..d95ec7c9a822a5 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -3,6 +3,11 @@ // Python/bytecodes.c // Do not edit! +#ifdef TIER_ONE + #error "This file is for Tier 2 only" +#endif +#define TIER_TWO 2 + case NOP: { break; } @@ -3312,7 +3317,8 @@ case _SET_IP: { TIER_TWO_ONLY - frame->instr_ptr = ip_offset + oparg; + // TODO: Put the code pointer in `operand` to avoid indirection via `frame` + frame->instr_ptr = _PyCode_CODE(_PyFrame_GetCode(frame)) + oparg; break; } @@ -3343,3 +3349,5 @@ stack_pointer[-1 - oparg] = top; break; } + +#undef TIER_TWO diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index fe67834560f6ce..09984fa8745b6b 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -3,6 +3,11 @@ // Python/bytecodes.c // Do not edit! +#ifdef TIER_TWO + #error "This file is for Tier 1 only" +#endif +#define TIER_ONE 1 + TARGET(NOP) { frame->instr_ptr = next_instr; next_instr += 1; @@ -3363,7 +3368,7 @@ Py_INCREF(executor); if (executor->execute == _PyUopExecute) { self = (_PyUOpExecutorObject *)executor; - goto enter_tier_two; + GOTO_TIER_TWO(); } frame = executor->execute(executor, frame, stack_pointer); if (frame == NULL) { @@ -5676,3 +5681,5 @@ assert(0 && "Executing RESERVED instruction."); Py_UNREACHABLE(); } + +#undef TIER_ONE diff --git a/Tools/cases_generator/generate_cases.py b/Tools/cases_generator/generate_cases.py index ed4fc8d6130cec..92d44eb3e1f264 100644 --- a/Tools/cases_generator/generate_cases.py +++ b/Tools/cases_generator/generate_cases.py @@ -758,6 +758,12 @@ def write_instructions( self.write_provenance_header() + self.out.write_raw("\n") + self.out.write_raw("#ifdef TIER_TWO\n") + self.out.write_raw(" #error \"This file is for Tier 1 only\"\n") + self.out.write_raw("#endif\n") + self.out.write_raw("#define TIER_ONE 1\n") + # Write and count instructions of all kinds n_macros = 0 for thing in self.everything: @@ -773,6 +779,9 @@ def write_instructions( case _: assert_never(thing) + self.out.write_raw("\n") + self.out.write_raw("#undef TIER_ONE\n") + print( f"Wrote {n_macros} cases to {output_filename}", file=sys.stderr, @@ -786,6 +795,13 @@ def write_executor_instructions( with open(executor_filename, "w") as f: self.out = Formatter(f, 8, emit_line_directives) self.write_provenance_header() + + self.out.write_raw("\n") + self.out.write_raw("#ifdef TIER_ONE\n") + self.out.write_raw(" #error \"This file is for Tier 2 only\"\n") + self.out.write_raw("#endif\n") + self.out.write_raw("#define TIER_TWO 2\n") + for instr in self.instrs.values(): if instr.is_viable_uop(): n_uops += 1 @@ -795,6 +811,10 @@ def write_executor_instructions( if instr.check_eval_breaker: self.out.emit("CHECK_EVAL_BREAKER();") self.out.emit("break;") + + self.out.write_raw("\n") + self.out.write_raw("#undef TIER_TWO\n") + print( f"Wrote {n_uops} cases to {executor_filename}", file=sys.stderr, From 5e844763252820546b62bea0c89f42759ff4eaa4 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Tue, 31 Oct 2023 16:41:49 -0700 Subject: [PATCH 08/24] Fix test_generated_cases.py by stripping preprocessor prefix/suffix --- Lib/test/test_generated_cases.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Lib/test/test_generated_cases.py b/Lib/test/test_generated_cases.py index 475d7492040b7b..ea0c11621a2ca9 100644 --- a/Lib/test/test_generated_cases.py +++ b/Lib/test/test_generated_cases.py @@ -118,17 +118,19 @@ def run_cases_test(self, input: str, expected: str): with open(self.temp_output_filename) as temp_output: lines = temp_output.readlines() - while lines and lines[0].startswith("// "): + while lines and lines[0].startswith(("// ", "#", " #", "\n")): lines.pop(0) + while lines and lines[-1].startswith(("#", "\n")): + lines.pop(-1) actual = "".join(lines) - # if actual.rstrip() != expected.rstrip(): + # if actual.strip() != expected.strip(): # print("Actual:") # print(actual) # print("Expected:") # print(expected) # print("End") - self.assertEqual(actual.rstrip(), expected.rstrip()) + self.assertEqual(actual.strip(), expected.strip()) def test_inst_no_args(self): input = """ From 917b7a272d37ae2afc079210037233661d14bdf1 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Tue, 31 Oct 2023 17:00:47 -0700 Subject: [PATCH 09/24] Eradicate executors.c from Windows build files --- PCbuild/_freeze_module.vcxproj | 1 - PCbuild/_freeze_module.vcxproj.filters | 3 --- PCbuild/pythoncore.vcxproj | 1 - PCbuild/pythoncore.vcxproj.filters | 3 --- Python/ceval_macros.h | 2 +- 5 files changed, 1 insertion(+), 9 deletions(-) diff --git a/PCbuild/_freeze_module.vcxproj b/PCbuild/_freeze_module.vcxproj index 20d800a6959552..e4ce3fefc91008 100644 --- a/PCbuild/_freeze_module.vcxproj +++ b/PCbuild/_freeze_module.vcxproj @@ -200,7 +200,6 @@ - diff --git a/PCbuild/_freeze_module.vcxproj.filters b/PCbuild/_freeze_module.vcxproj.filters index 40256a22fb5562..8e987cdf2b86e0 100644 --- a/PCbuild/_freeze_module.vcxproj.filters +++ b/PCbuild/_freeze_module.vcxproj.filters @@ -127,9 +127,6 @@ Source Files - - Source Files - Source Files diff --git a/PCbuild/pythoncore.vcxproj b/PCbuild/pythoncore.vcxproj index 4d3621a6146807..eca2671b0cc090 100644 --- a/PCbuild/pythoncore.vcxproj +++ b/PCbuild/pythoncore.vcxproj @@ -552,7 +552,6 @@ - diff --git a/PCbuild/pythoncore.vcxproj.filters b/PCbuild/pythoncore.vcxproj.filters index 69d8e0312e0169..447bb266d7587e 100644 --- a/PCbuild/pythoncore.vcxproj.filters +++ b/PCbuild/pythoncore.vcxproj.filters @@ -1253,9 +1253,6 @@ Python - - Python - Python diff --git a/Python/ceval_macros.h b/Python/ceval_macros.h index d9560f5e131c98..ed4b9effa0cb34 100644 --- a/Python/ceval_macros.h +++ b/Python/ceval_macros.h @@ -1,4 +1,4 @@ -// Macros and other things needed by ceval.c, executor.c, and bytecodes.c +// Macros and other things needed by ceval.c, and bytecodes.c /* Computed GOTOs, or the-optimization-commonly-but-improperly-known-as-"threaded code" From 9067eb064b09bcdbed3e2f9da13096cc911d64c0 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Tue, 31 Oct 2023 17:25:26 -0700 Subject: [PATCH 10/24] Rename deoptimize_tier_two back to deoptimize (for Justin) --- Python/ceval.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Python/ceval.c b/Python/ceval.c index 413fcc06cbdb04..423fc34e99e7fe 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -958,7 +958,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int #undef DEOPT_IF #define DEOPT_IF(COND, INSTNAME) \ if ((COND)) { \ - goto deoptimize_tier_two;\ + goto deoptimize;\ } #ifdef Py_STATS @@ -1046,7 +1046,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int Py_DECREF(self); goto resume_with_error; -deoptimize_tier_two: +deoptimize: // On DEOPT_IF we just repeat the last instruction. // This presumes nothing was popped from the stack (nor pushed). DPRINTF(2, "DEOPT: [Opcode %d, operand %" PRIu64 "]\n", opcode, operand); From 6a4e495a9aad979456a04fd3bcd9e58e637031db Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Tue, 31 Oct 2023 17:43:59 -0700 Subject: [PATCH 11/24] Fix whitespace --- Python/ceval_macros.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Python/ceval_macros.h b/Python/ceval_macros.h index ed4b9effa0cb34..d22075169b8199 100644 --- a/Python/ceval_macros.h +++ b/Python/ceval_macros.h @@ -377,7 +377,7 @@ static inline void _Py_LeaveRecursiveCallPy(PyThreadState *tstate) { /* Implementation of "macros" that modify the instruction pointer, * stack pointer, or frame pointer. - * These need to treated differently by tier 1 and 2. + * These need to treated differently by tier 1 and 2. * The Tier 1 version is here; Tier 2 is inlined in ceval.c. */ #define LOAD_IP(OFFSET) do { \ From 81f1883c6f84e0e5fb74327fa098b75008f96210 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Tue, 31 Oct 2023 21:27:56 -0700 Subject: [PATCH 12/24] Revert "DO NOT MERGE: Always use -Xuops" (I accidentally kept this commit after pushing it experimentally. This means I've been testing with uops on all the time, which is actually pretty amazing.) This reverts commit e0e60ced06794dba547a03bf61729484eb361f53. --- Python/pylifecycle.c | 1 - 1 file changed, 1 deletion(-) diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index cb7e4d028d8efa..3c57056fb81e81 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -1234,7 +1234,6 @@ init_interp_main(PyThreadState *tstate) if (_Py_get_xoption(&config->xoptions, L"uops") != NULL) { enabled = 1; } - enabled = 1; // TEMPORARY: always enable if (enabled) { PyObject *opt = PyUnstable_Optimizer_NewUOpOptimizer(); if (opt == NULL) { From ee27e73080bcaee999bad0c4705e89439d469ec3 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Tue, 31 Oct 2023 21:33:43 -0700 Subject: [PATCH 13/24] Add blurb --- .../2023-10-31-21-33-35.gh-issue-111520.vw-rxJ.rst | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-10-31-21-33-35.gh-issue-111520.vw-rxJ.rst diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-10-31-21-33-35.gh-issue-111520.vw-rxJ.rst b/Misc/NEWS.d/next/Core and Builtins/2023-10-31-21-33-35.gh-issue-111520.vw-rxJ.rst new file mode 100644 index 00000000000000..2f203f6a2f9156 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-10-31-21-33-35.gh-issue-111520.vw-rxJ.rst @@ -0,0 +1,6 @@ +Merge the Tier 1 (bytecode) and Tier 2 (micro-ops) interpreters together, +moving the Tier 2 interpreter loop and switch into +``_PyEval_EvalFrameDefault()`` in ``ceval.c``. + +**Beware!** This changes the environment variables to enable micro-ops and +their debugging to ``PYTHON_UOPS`` and ``PYTHON_LLTRACE``. From 7ebc228bb1c05ebd02955c7f656f7f7d370b1d2b Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Tue, 31 Oct 2023 21:47:16 -0700 Subject: [PATCH 14/24] Add more color to the news blurb --- .../2023-10-31-21-33-35.gh-issue-111520.vw-rxJ.rst | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-10-31-21-33-35.gh-issue-111520.vw-rxJ.rst b/Misc/NEWS.d/next/Core and Builtins/2023-10-31-21-33-35.gh-issue-111520.vw-rxJ.rst index 2f203f6a2f9156..67ca9c6671f99b 100644 --- a/Misc/NEWS.d/next/Core and Builtins/2023-10-31-21-33-35.gh-issue-111520.vw-rxJ.rst +++ b/Misc/NEWS.d/next/Core and Builtins/2023-10-31-21-33-35.gh-issue-111520.vw-rxJ.rst @@ -1,6 +1,9 @@ Merge the Tier 1 (bytecode) and Tier 2 (micro-ops) interpreters together, moving the Tier 2 interpreter loop and switch into -``_PyEval_EvalFrameDefault()`` in ``ceval.c``. +``_PyEval_EvalFrameDefault()`` in ``Python/ceval.c``. +The ``Python/executor.c`` file is gone. +Also the ``TIER_ONE`` and ``TIER_TWO`` macros are now handled +by the code generator. **Beware!** This changes the environment variables to enable micro-ops and their debugging to ``PYTHON_UOPS`` and ``PYTHON_LLTRACE``. From a96ac7fc4bc71efe94edc4a6e9c608bac0304a0f Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Wed, 1 Nov 2023 08:04:54 -0700 Subject: [PATCH 15/24] Eliminate 'operand' local variable --- Python/ceval.c | 6 +++- Python/executor_cases.c.h | 46 +++++++++++++-------------- Tools/cases_generator/instructions.py | 2 +- 3 files changed, 29 insertions(+), 25 deletions(-) diff --git a/Python/ceval.c b/Python/ceval.c index 423fc34e99e7fe..b7299727493dc3 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -983,7 +983,9 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int OPT_STAT_INC(traces_executed); _PyUOpInstruction *next_uop = self->trace; - uint64_t operand; +#ifdef Py_DEBUG + uint64_t operand; // Used by several DPRINTF() calls +#endif #ifdef Py_STATS uint64_t trace_uop_execution_counter = 0; #endif @@ -991,7 +993,9 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int for (;;) { opcode = next_uop->opcode; oparg = next_uop->oparg; +#ifdef Py_DEBUG operand = next_uop->operand; +#endif DPRINTF(3, "%4d: uop %s, oparg %d, operand %" PRIu64 ", stack_level %d\n", (int)(next_uop - self->trace), diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index eece2eb2aa4143..913abacca29112 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -204,7 +204,7 @@ PyObject *value; PyObject *res; value = stack_pointer[-1]; - uint32_t version = (uint32_t)operand; + uint32_t version = (uint32_t)next_uop[-1].operand; // This one is a bit weird, because we expect *some* failures: assert(version); DEOPT_IF(Py_TYPE(value)->tp_version_tag != version, TO_BOOL); @@ -874,7 +874,7 @@ case _SPECIALIZE_UNPACK_SEQUENCE: { PyObject *seq; seq = stack_pointer[-1]; - uint16_t counter = (uint16_t)operand; + uint16_t counter = (uint16_t)next_uop[-1].operand; #if ENABLE_SPECIALIZATION if (ADAPTIVE_COUNTER_IS_ZERO(counter)) { next_instr = this_instr; @@ -1144,7 +1144,7 @@ } case _GUARD_GLOBALS_VERSION: { - uint16_t version = (uint16_t)operand; + uint16_t version = (uint16_t)next_uop[-1].operand; PyDictObject *dict = (PyDictObject *)GLOBALS(); DEOPT_IF(!PyDict_CheckExact(dict), _GUARD_GLOBALS_VERSION); DEOPT_IF(dict->ma_keys->dk_version != version, _GUARD_GLOBALS_VERSION); @@ -1153,7 +1153,7 @@ } case _GUARD_BUILTINS_VERSION: { - uint16_t version = (uint16_t)operand; + uint16_t version = (uint16_t)next_uop[-1].operand; PyDictObject *dict = (PyDictObject *)BUILTINS(); DEOPT_IF(!PyDict_CheckExact(dict), _GUARD_BUILTINS_VERSION); DEOPT_IF(dict->ma_keys->dk_version != version, _GUARD_BUILTINS_VERSION); @@ -1164,7 +1164,7 @@ case _LOAD_GLOBAL_MODULE: { PyObject *res; PyObject *null = NULL; - uint16_t index = (uint16_t)operand; + uint16_t index = (uint16_t)next_uop[-1].operand; PyDictObject *dict = (PyDictObject *)GLOBALS(); PyDictUnicodeEntry *entries = DK_UNICODE_ENTRIES(dict->ma_keys); res = entries[index].me_value; @@ -1182,7 +1182,7 @@ case _LOAD_GLOBAL_BUILTINS: { PyObject *res; PyObject *null = NULL; - uint16_t index = (uint16_t)operand; + uint16_t index = (uint16_t)next_uop[-1].operand; PyDictObject *bdict = (PyDictObject *)BUILTINS(); PyDictUnicodeEntry *entries = DK_UNICODE_ENTRIES(bdict->ma_keys); res = entries[index].me_value; @@ -1632,7 +1632,7 @@ case _GUARD_TYPE_VERSION: { PyObject *owner; owner = stack_pointer[-1]; - uint32_t type_version = (uint32_t)operand; + uint32_t type_version = (uint32_t)next_uop[-1].operand; PyTypeObject *tp = Py_TYPE(owner); assert(type_version != 0); DEOPT_IF(tp->tp_version_tag != type_version, _GUARD_TYPE_VERSION); @@ -1654,7 +1654,7 @@ PyObject *attr; PyObject *null = NULL; owner = stack_pointer[-1]; - uint16_t index = (uint16_t)operand; + uint16_t index = (uint16_t)next_uop[-1].operand; PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner); attr = _PyDictOrValues_GetValues(dorv)->values[index]; DEOPT_IF(attr == NULL, _LOAD_ATTR_INSTANCE_VALUE); @@ -1671,7 +1671,7 @@ case _CHECK_ATTR_MODULE: { PyObject *owner; owner = stack_pointer[-1]; - uint32_t type_version = (uint32_t)operand; + uint32_t type_version = (uint32_t)next_uop[-1].operand; DEOPT_IF(!PyModule_CheckExact(owner), _CHECK_ATTR_MODULE); PyDictObject *dict = (PyDictObject *)((PyModuleObject *)owner)->md_dict; assert(dict != NULL); @@ -1684,7 +1684,7 @@ PyObject *attr; PyObject *null = NULL; owner = stack_pointer[-1]; - uint16_t index = (uint16_t)operand; + uint16_t index = (uint16_t)next_uop[-1].operand; PyDictObject *dict = (PyDictObject *)((PyModuleObject *)owner)->md_dict; assert(dict->ma_keys->dk_kind == DICT_KEYS_UNICODE); assert(index < dict->ma_keys->dk_nentries); @@ -1718,7 +1718,7 @@ PyObject *attr; PyObject *null = NULL; owner = stack_pointer[-1]; - uint16_t hint = (uint16_t)operand; + uint16_t hint = (uint16_t)next_uop[-1].operand; PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner); PyDictObject *dict = (PyDictObject *)_PyDictOrValues_GetDict(dorv); DEOPT_IF(hint >= (size_t)dict->ma_keys->dk_nentries, _LOAD_ATTR_WITH_HINT); @@ -1749,7 +1749,7 @@ PyObject *attr; PyObject *null = NULL; owner = stack_pointer[-1]; - uint16_t index = (uint16_t)operand; + uint16_t index = (uint16_t)next_uop[-1].operand; char *addr = (char *)owner + index; attr = *(PyObject **)addr; DEOPT_IF(attr == NULL, _LOAD_ATTR_SLOT); @@ -1766,7 +1766,7 @@ case _CHECK_ATTR_CLASS: { PyObject *owner; owner = stack_pointer[-1]; - uint32_t type_version = (uint32_t)operand; + uint32_t type_version = (uint32_t)next_uop[-1].operand; DEOPT_IF(!PyType_Check(owner), _CHECK_ATTR_CLASS); assert(type_version != 0); DEOPT_IF(((PyTypeObject *)owner)->tp_version_tag != type_version, _CHECK_ATTR_CLASS); @@ -1778,7 +1778,7 @@ PyObject *attr; PyObject *null = NULL; owner = stack_pointer[-1]; - PyObject *descr = (PyObject *)operand; + PyObject *descr = (PyObject *)next_uop[-1].operand; STAT_INC(LOAD_ATTR, hit); assert(descr != NULL); attr = Py_NewRef(descr); @@ -1804,7 +1804,7 @@ PyObject *value; owner = stack_pointer[-1]; value = stack_pointer[-2]; - uint16_t index = (uint16_t)operand; + uint16_t index = (uint16_t)next_uop[-1].operand; PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner); STAT_INC(STORE_ATTR, hit); PyDictValues *values = _PyDictOrValues_GetValues(dorv); @@ -1826,7 +1826,7 @@ PyObject *value; owner = stack_pointer[-1]; value = stack_pointer[-2]; - uint16_t index = (uint16_t)operand; + uint16_t index = (uint16_t)next_uop[-1].operand; char *addr = (char *)owner + index; STAT_INC(STORE_ATTR, hit); PyObject *old_value = *(PyObject **)addr; @@ -2384,7 +2384,7 @@ case _GUARD_KEYS_VERSION: { PyObject *owner; owner = stack_pointer[-1]; - uint32_t keys_version = (uint32_t)operand; + uint32_t keys_version = (uint32_t)next_uop[-1].operand; PyTypeObject *owner_cls = Py_TYPE(owner); PyHeapTypeObject *owner_heap_type = (PyHeapTypeObject *)owner_cls; DEOPT_IF(owner_heap_type->ht_cached_keys->dk_version != keys_version, _GUARD_KEYS_VERSION); @@ -2396,7 +2396,7 @@ PyObject *attr; PyObject *self; owner = stack_pointer[-1]; - PyObject *descr = (PyObject *)operand; + PyObject *descr = (PyObject *)next_uop[-1].operand; assert(oparg & 1); /* Cached method object */ STAT_INC(LOAD_ATTR, hit); @@ -2415,7 +2415,7 @@ PyObject *attr; PyObject *self; owner = stack_pointer[-1]; - PyObject *descr = (PyObject *)operand; + PyObject *descr = (PyObject *)next_uop[-1].operand; assert(oparg & 1); assert(Py_TYPE(owner)->tp_dictoffset == 0); STAT_INC(LOAD_ATTR, hit); @@ -2433,7 +2433,7 @@ PyObject *owner; PyObject *attr; owner = stack_pointer[-1]; - PyObject *descr = (PyObject *)operand; + PyObject *descr = (PyObject *)next_uop[-1].operand; assert((oparg & 1) == 0); STAT_INC(LOAD_ATTR, hit); assert(descr != NULL); @@ -2447,7 +2447,7 @@ PyObject *owner; PyObject *attr; owner = stack_pointer[-1]; - PyObject *descr = (PyObject *)operand; + PyObject *descr = (PyObject *)next_uop[-1].operand; assert((oparg & 1) == 0); assert(Py_TYPE(owner)->tp_dictoffset == 0); STAT_INC(LOAD_ATTR, hit); @@ -2474,7 +2474,7 @@ PyObject *attr; PyObject *self; owner = stack_pointer[-1]; - PyObject *descr = (PyObject *)operand; + PyObject *descr = (PyObject *)next_uop[-1].operand; assert(oparg & 1); STAT_INC(LOAD_ATTR, hit); assert(descr != NULL); @@ -2523,7 +2523,7 @@ PyObject *callable; self_or_null = stack_pointer[-1 - oparg]; callable = stack_pointer[-2 - oparg]; - uint32_t func_version = (uint32_t)operand; + uint32_t func_version = (uint32_t)next_uop[-1].operand; DEOPT_IF(!PyFunction_Check(callable), _CHECK_FUNCTION_EXACT_ARGS); PyFunctionObject *func = (PyFunctionObject *)callable; DEOPT_IF(func->func_version != func_version, _CHECK_FUNCTION_EXACT_ARGS); diff --git a/Tools/cases_generator/instructions.py b/Tools/cases_generator/instructions.py index 521e0892f28185..f3e4f76697853b 100644 --- a/Tools/cases_generator/instructions.py +++ b/Tools/cases_generator/instructions.py @@ -162,7 +162,7 @@ def write_body( f"{func}(&this_instr[{active.offset + 1}].cache);" ) else: - out.emit(f"{typ}{ceffect.name} = ({typ.strip()})operand;") + out.emit(f"{typ}{ceffect.name} = ({typ.strip()})next_uop[-1].operand;") # Write the body, substituting a goto for ERROR_IF() and other stuff assert dedent <= 0 From e02409d0814673d09724ed37548818d871f64d46 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Wed, 1 Nov 2023 08:11:21 -0700 Subject: [PATCH 16/24] Rename self -> current_executor (TODO: eliminate it?) --- Python/bytecodes.c | 11 ++++++----- Python/ceval.c | 10 +++++----- Python/executor_cases.c.h | 8 ++++---- Python/generated_cases.c.h | 2 +- 4 files changed, 16 insertions(+), 15 deletions(-) diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 242a7ed6479271..6c668c36166bd7 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -63,6 +63,7 @@ static size_t jump; static uint16_t invert, counter, index, hint; #define unused 0 // Used in a macro def, can't be static static uint32_t type_version; +static _PyUOpExecutorObject *current_executor; static PyObject * dummy_func( @@ -2364,7 +2365,7 @@ dummy_func( frame->instr_ptr = next_instr; Py_INCREF(executor); if (executor->execute == _PyUopExecute) { - self = (_PyUOpExecutorObject *)executor; + current_executor = (_PyUOpExecutorObject *)executor; GOTO_TIER_TWO(); } frame = executor->execute(executor, frame, stack_pointer); @@ -3991,18 +3992,18 @@ dummy_func( op(_POP_JUMP_IF_FALSE, (flag -- )) { if (Py_IsFalse(flag)) { - next_uop = self->trace + oparg; + next_uop = current_executor->trace + oparg; } } op(_POP_JUMP_IF_TRUE, (flag -- )) { if (Py_IsTrue(flag)) { - next_uop = self->trace + oparg; + next_uop = current_executor->trace + oparg; } } op(_JUMP_TO_TOP, (--)) { - next_uop = self->trace; + next_uop = current_executor->trace; CHECK_EVAL_BREAKER(); } @@ -4024,7 +4025,7 @@ dummy_func( op(_EXIT_TRACE, (--)) { TIER_TWO_ONLY _PyFrame_SetStackPointer(frame, stack_pointer); - Py_DECREF(self); + Py_DECREF(current_executor); OPT_HIST(trace_uop_execution_counter, trace_run_length_hist); goto enter_tier_one; } diff --git a/Python/ceval.c b/Python/ceval.c index b7299727493dc3..e1c9a2e0b03ef0 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -727,7 +727,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } /* State shared between Tier 1 and Tier 2 interpreter */ - _PyUOpExecutorObject *self = NULL; + _PyUOpExecutorObject *current_executor = NULL; /* Local "register" variables. * These are cached values from the frame and code object. */ @@ -982,7 +982,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int #endif OPT_STAT_INC(traces_executed); - _PyUOpInstruction *next_uop = self->trace; + _PyUOpInstruction *next_uop = current_executor->trace; #ifdef Py_DEBUG uint64_t operand; // Used by several DPRINTF() calls #endif @@ -998,7 +998,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int #endif DPRINTF(3, "%4d: uop %s, oparg %d, operand %" PRIu64 ", stack_level %d\n", - (int)(next_uop - self->trace), + (int)(next_uop - current_executor->trace), opcode < 256 ? _PyOpcode_OpName[opcode] : _PyOpcode_uop_name[opcode], oparg, operand, @@ -1047,7 +1047,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int OPT_HIST(trace_uop_execution_counter, trace_run_length_hist); frame->return_offset = 0; // Don't leave this random _PyFrame_SetStackPointer(frame, stack_pointer); - Py_DECREF(self); + Py_DECREF(current_executor); goto resume_with_error; deoptimize: @@ -1057,7 +1057,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int OPT_HIST(trace_uop_execution_counter, trace_run_length_hist); frame->return_offset = 0; // Dispatch to frame->instr_ptr _PyFrame_SetStackPointer(frame, stack_pointer); - Py_DECREF(self); + Py_DECREF(current_executor); enter_tier_one: next_instr = frame->instr_ptr; goto resume_frame; diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index 913abacca29112..94005285ec7ee8 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -3220,7 +3220,7 @@ PyObject *flag; flag = stack_pointer[-1]; if (Py_IsFalse(flag)) { - next_uop = self->trace + oparg; + next_uop = current_executor->trace + oparg; } STACK_SHRINK(1); break; @@ -3230,14 +3230,14 @@ PyObject *flag; flag = stack_pointer[-1]; if (Py_IsTrue(flag)) { - next_uop = self->trace + oparg; + next_uop = current_executor->trace + oparg; } STACK_SHRINK(1); break; } case _JUMP_TO_TOP: { - next_uop = self->trace; + next_uop = current_executor->trace; CHECK_EVAL_BREAKER(); break; } @@ -3262,7 +3262,7 @@ case _EXIT_TRACE: { TIER_TWO_ONLY _PyFrame_SetStackPointer(frame, stack_pointer); - Py_DECREF(self); + Py_DECREF(current_executor); OPT_HIST(trace_uop_execution_counter, trace_run_length_hist); goto enter_tier_one; break; diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 5c85c185459210..e4b328fee99738 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -3438,7 +3438,7 @@ frame->instr_ptr = next_instr; Py_INCREF(executor); if (executor->execute == _PyUopExecute) { - self = (_PyUOpExecutorObject *)executor; + current_executor = (_PyUOpExecutorObject *)executor; GOTO_TIER_TWO(); } frame = executor->execute(executor, frame, stack_pointer); From fdf1a2fc6d628a3a21df4b9be4ffb84cca46185f Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Wed, 1 Nov 2023 08:17:58 -0700 Subject: [PATCH 17/24] Move `_EXIT_TRACE` logic to a separate label Using `GOTO_TIER_ONE()` macro. This should make things simpler for Justin. --- Python/bytecodes.c | 5 +---- Python/ceval.c | 11 +++++++++++ Python/ceval_macros.h | 2 ++ Python/executor_cases.c.h | 5 +---- 4 files changed, 15 insertions(+), 8 deletions(-) diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 6c668c36166bd7..98af114a72fa60 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -4024,10 +4024,7 @@ dummy_func( op(_EXIT_TRACE, (--)) { TIER_TWO_ONLY - _PyFrame_SetStackPointer(frame, stack_pointer); - Py_DECREF(current_executor); - OPT_HIST(trace_uop_execution_counter, trace_run_length_hist); - goto enter_tier_one; + GOTO_TIER_ONE(); } op(_INSERT, (unused[oparg], top -- top, unused[oparg])) { diff --git a/Python/ceval.c b/Python/ceval.c index e1c9a2e0b03ef0..6e156ab2009468 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -1027,6 +1027,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } } +// Jump here from ERROR_IF(..., unbound_local_error) unbound_local_error_tier_two: _PyEval_FormatExcCheckArg(tstate, PyExc_UnboundLocalError, UNBOUNDLOCAL_ERROR_MSG, @@ -1034,6 +1035,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int ); goto error_tier_two; +// JUMP to any of these from ERROR_IF(..., error) pop_4_error_tier_two: STACK_SHRINK(1); pop_3_error_tier_two: @@ -1050,6 +1052,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int Py_DECREF(current_executor); goto resume_with_error; +// Jump here from DEOPT_IF() deoptimize: // On DEOPT_IF we just repeat the last instruction. // This presumes nothing was popped from the stack (nor pushed). @@ -1058,10 +1061,18 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int frame->return_offset = 0; // Dispatch to frame->instr_ptr _PyFrame_SetStackPointer(frame, stack_pointer); Py_DECREF(current_executor); + // Fall through +// Jump here from ENTER_EXECUTOR enter_tier_one: next_instr = frame->instr_ptr; goto resume_frame; +// Jump here from _EXIT_TRACE +exit_trace: + _PyFrame_SetStackPointer(frame, stack_pointer); + Py_DECREF(current_executor); + OPT_HIST(trace_uop_execution_counter, trace_run_length_hist); + goto enter_tier_one; } #if defined(__GNUC__) # pragma GCC diagnostic pop diff --git a/Python/ceval_macros.h b/Python/ceval_macros.h index d22075169b8199..546adbe5f438d1 100644 --- a/Python/ceval_macros.h +++ b/Python/ceval_macros.h @@ -395,3 +395,5 @@ stack_pointer = _PyFrame_GetStackPointer(frame); /* Tier-switching macros. */ #define GOTO_TIER_TWO() goto enter_tier_two; + +#define GOTO_TIER_ONE() goto exit_trace; diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index 94005285ec7ee8..fdf3e4b01a88b3 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -3261,10 +3261,7 @@ case _EXIT_TRACE: { TIER_TWO_ONLY - _PyFrame_SetStackPointer(frame, stack_pointer); - Py_DECREF(current_executor); - OPT_HIST(trace_uop_execution_counter, trace_run_length_hist); - goto enter_tier_one; + GOTO_TIER_ONE(); break; } From 2a6450c10c217882322dbb53f46e2f5c27ff824b Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Wed, 1 Nov 2023 11:06:16 -0700 Subject: [PATCH 18/24] Limit infinite recursion in test_typing --- Lib/test/test_typing.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 7f60bf4bbc1e75..11078b9919ddd5 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -45,7 +45,7 @@ import weakref import types -from test.support import import_helper, captured_stderr, cpython_only +from test.support import import_helper, captured_stderr, cpython_only, infinite_recursion from test import mod_generics_cache from test import _typed_dict_helper @@ -5626,7 +5626,8 @@ def cmp(o1, o2): r1 = namespace1() r2 = namespace2() self.assertIsNot(r1, r2) - self.assertRaises(RecursionError, cmp, r1, r2) + with infinite_recursion(): + self.assertRaises(RecursionError, cmp, r1, r2) def test_union_forward_recursion(self): ValueList = List['Value'] From 4783de3b115133c8951bb05a7cb169de787250a6 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Wed, 1 Nov 2023 11:19:02 -0700 Subject: [PATCH 19/24] Limit infinite recursion in test_fileio --- Lib/test/test_fileio.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_fileio.py b/Lib/test/test_fileio.py index ebfcffd1829174..5d07e506df11ab 100644 --- a/Lib/test/test_fileio.py +++ b/Lib/test/test_fileio.py @@ -10,7 +10,7 @@ from functools import wraps from test.support import ( - cpython_only, swap_attr, gc_collect, is_emscripten, is_wasi + cpython_only, swap_attr, gc_collect, is_emscripten, is_wasi, infinite_recursion ) from test.support.os_helper import ( TESTFN, TESTFN_ASCII, TESTFN_UNICODE, make_bad_fd, @@ -187,7 +187,8 @@ def testRecursiveRepr(self): # Issue #25455 with swap_attr(self.f, 'name', self.f): with self.assertRaises(RuntimeError): - repr(self.f) # Should not crash + with infinite_recursion(): + repr(self.f) # Should not crash def testErrors(self): f = self.f From b9516a1b041d7a61659eddc1f89d00acce5e0e37 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Wed, 1 Nov 2023 11:21:36 -0700 Subject: [PATCH 20/24] Limit infinite recursion in test_xml_etree --- Lib/test/test_xml_etree.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_xml_etree.py b/Lib/test/test_xml_etree.py index 6d413aa68a338d..66a1d54ae9c2d8 100644 --- a/Lib/test/test_xml_etree.py +++ b/Lib/test/test_xml_etree.py @@ -25,7 +25,7 @@ from test import support from test.support import os_helper from test.support import warnings_helper -from test.support import findfile, gc_collect, swap_attr, swap_item +from test.support import findfile, gc_collect, swap_attr, swap_item, infinite_recursion from test.support.import_helper import import_fresh_module from test.support.os_helper import TESTFN @@ -2540,7 +2540,8 @@ def test_recursive_repr(self): e = ET.Element('foo') with swap_attr(e, 'tag', e): with self.assertRaises(RuntimeError): - repr(e) # Should not crash + with infinite_recursion(): + repr(e) # Should not crash def test_element_get_text(self): # Issue #27863 From 33c3faeebfc843778f459ffc884a8e28373f1f4a Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Wed, 1 Nov 2023 11:28:20 -0700 Subject: [PATCH 21/24] Limit infinite recursion in test_call --- Lib/test/test_call.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_call.py b/Lib/test/test_call.py index b1c78d7136fc9b..36d7e918afeee1 100644 --- a/Lib/test/test_call.py +++ b/Lib/test/test_call.py @@ -1017,8 +1017,10 @@ def c_py_recurse(m): with self.assertRaises(RecursionError): recurse(101_000) c_recurse(100) - with self.assertRaises(RecursionError): - c_recurse(90_000) + if sys.platform != "win32": + # This crashes CI on Windows + with self.assertRaises(RecursionError): + c_recurse(90_000) c_py_recurse(90) with self.assertRaises(RecursionError): c_py_recurse(100_000) From 998e054b745466aafd96ab28310700a4a0815e61 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Wed, 1 Nov 2023 11:57:02 -0700 Subject: [PATCH 22/24] Fix test_call better: adjust Py_C_RECURSION_LIMIT in pystate.h --- Include/cpython/pystate.h | 7 ++++++- Lib/test/support/__init__.py | 11 +++++++++-- Lib/test/test_call.py | 6 ++---- 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/Include/cpython/pystate.h b/Include/cpython/pystate.h index ec99f90d669d12..f94b6fe0753616 100644 --- a/Include/cpython/pystate.h +++ b/Include/cpython/pystate.h @@ -214,7 +214,12 @@ struct _ts { # define Py_C_RECURSION_LIMIT 500 #else // This value is duplicated in Lib/test/support/__init__.py -# define Py_C_RECURSION_LIMIT 1500 +# if defined(MS_WINDOWS) && defined(Py_DEBUG) + // On Windows in debug mode the interpreter uses more stack space +# define Py_C_RECURSION_LIMIT 1000 +# else +# define Py_C_RECURSION_LIMIT 1500 +# endif #endif diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index de7db70275441a..f64f88dcd2c6f3 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -2355,8 +2355,15 @@ def adjust_int_max_str_digits(max_digits): #For recursion tests, easily exceeds default recursion limit EXCEEDS_RECURSION_LIMIT = 5000 -# The default C recursion limit (from Include/cpython/pystate.h). -Py_C_RECURSION_LIMIT = 1500 +def _get_c_recursion_limit(): + try: + import _testcapi + return _testcapi.Py_C_RECURSION_LIMIT + except (ImportError, AttributeError): + return 1500 # (from Include/cpython/pystate.h) + +# The default C recursion limit. +Py_C_RECURSION_LIMIT = _get_c_recursion_limit() #Windows doesn't have os.uname() but it doesn't support s390x. skip_on_s390x = unittest.skipIf(hasattr(os, 'uname') and os.uname().machine == 's390x', diff --git a/Lib/test/test_call.py b/Lib/test/test_call.py index 36d7e918afeee1..b1c78d7136fc9b 100644 --- a/Lib/test/test_call.py +++ b/Lib/test/test_call.py @@ -1017,10 +1017,8 @@ def c_py_recurse(m): with self.assertRaises(RecursionError): recurse(101_000) c_recurse(100) - if sys.platform != "win32": - # This crashes CI on Windows - with self.assertRaises(RecursionError): - c_recurse(90_000) + with self.assertRaises(RecursionError): + c_recurse(90_000) c_py_recurse(90) with self.assertRaises(RecursionError): c_py_recurse(100_000) From 19d9d401e84c01a3f8226810f5aa4ecef93ede2e Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Wed, 1 Nov 2023 12:01:02 -0700 Subject: [PATCH 23/24] Revert unnecessary fixes to recursive tests --- Lib/test/test_fileio.py | 5 ++--- Lib/test/test_typing.py | 5 ++--- Lib/test/test_xml_etree.py | 5 ++--- 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/Lib/test/test_fileio.py b/Lib/test/test_fileio.py index 5d07e506df11ab..ebfcffd1829174 100644 --- a/Lib/test/test_fileio.py +++ b/Lib/test/test_fileio.py @@ -10,7 +10,7 @@ from functools import wraps from test.support import ( - cpython_only, swap_attr, gc_collect, is_emscripten, is_wasi, infinite_recursion + cpython_only, swap_attr, gc_collect, is_emscripten, is_wasi ) from test.support.os_helper import ( TESTFN, TESTFN_ASCII, TESTFN_UNICODE, make_bad_fd, @@ -187,8 +187,7 @@ def testRecursiveRepr(self): # Issue #25455 with swap_attr(self.f, 'name', self.f): with self.assertRaises(RuntimeError): - with infinite_recursion(): - repr(self.f) # Should not crash + repr(self.f) # Should not crash def testErrors(self): f = self.f diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 11078b9919ddd5..7f60bf4bbc1e75 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -45,7 +45,7 @@ import weakref import types -from test.support import import_helper, captured_stderr, cpython_only, infinite_recursion +from test.support import import_helper, captured_stderr, cpython_only from test import mod_generics_cache from test import _typed_dict_helper @@ -5626,8 +5626,7 @@ def cmp(o1, o2): r1 = namespace1() r2 = namespace2() self.assertIsNot(r1, r2) - with infinite_recursion(): - self.assertRaises(RecursionError, cmp, r1, r2) + self.assertRaises(RecursionError, cmp, r1, r2) def test_union_forward_recursion(self): ValueList = List['Value'] diff --git a/Lib/test/test_xml_etree.py b/Lib/test/test_xml_etree.py index 66a1d54ae9c2d8..6d413aa68a338d 100644 --- a/Lib/test/test_xml_etree.py +++ b/Lib/test/test_xml_etree.py @@ -25,7 +25,7 @@ from test import support from test.support import os_helper from test.support import warnings_helper -from test.support import findfile, gc_collect, swap_attr, swap_item, infinite_recursion +from test.support import findfile, gc_collect, swap_attr, swap_item from test.support.import_helper import import_fresh_module from test.support.os_helper import TESTFN @@ -2540,8 +2540,7 @@ def test_recursive_repr(self): e = ET.Element('foo') with swap_attr(e, 'tag', e): with self.assertRaises(RuntimeError): - with infinite_recursion(): - repr(e) # Should not crash + repr(e) # Should not crash def test_element_get_text(self): # Issue #27863 From 03de1bfd69bd13efae82e10530aca87cd1c29869 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Wed, 1 Nov 2023 12:31:53 -0700 Subject: [PATCH 24/24] Even better fix -- increase stack space on Windows in debug mode --- Include/cpython/pystate.h | 7 +------ PCbuild/python.vcxproj | 2 +- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/Include/cpython/pystate.h b/Include/cpython/pystate.h index f94b6fe0753616..ec99f90d669d12 100644 --- a/Include/cpython/pystate.h +++ b/Include/cpython/pystate.h @@ -214,12 +214,7 @@ struct _ts { # define Py_C_RECURSION_LIMIT 500 #else // This value is duplicated in Lib/test/support/__init__.py -# if defined(MS_WINDOWS) && defined(Py_DEBUG) - // On Windows in debug mode the interpreter uses more stack space -# define Py_C_RECURSION_LIMIT 1000 -# else -# define Py_C_RECURSION_LIMIT 1500 -# endif +# define Py_C_RECURSION_LIMIT 1500 #endif diff --git a/PCbuild/python.vcxproj b/PCbuild/python.vcxproj index f4640454a73070..8b733865962373 100644 --- a/PCbuild/python.vcxproj +++ b/PCbuild/python.vcxproj @@ -95,7 +95,7 @@ Console 2000000 - 8000000 + 12000000