From b57948ed49b0b88a68438861405e11b312e3dc5d Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Fri, 31 Jan 2025 15:32:25 +0000 Subject: [PATCH 1/2] Remove resume_with_error label --- Python/bytecodes.c | 6 +----- Python/ceval.c | 19 ++++++++++--------- Python/ceval_macros.h | 4 +++- Python/generated_cases.c.h | 5 ----- Tools/cases_generator/analyzer.py | 2 -- 5 files changed, 14 insertions(+), 22 deletions(-) diff --git a/Python/bytecodes.c b/Python/bytecodes.c index f659a5e5c920a7..b98d256e2a832d 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -5303,14 +5303,11 @@ dummy_func( tstate->c_recursion_remaining += PY_EVAL_C_STACK_UNITS; return NULL; } - goto resume_with_error; - } - - label(resume_with_error) { next_instr = frame->instr_ptr; stack_pointer = _PyFrame_GetStackPointer(frame); goto error; } + // END BYTECODES // } @@ -5320,7 +5317,6 @@ dummy_func( exit_unwind: handle_eval_breaker: resume_frame: - resume_with_error: start_frame: unbound_local_error: ; diff --git a/Python/ceval.c b/Python/ceval.c index e3b87441f8088d..99eac16b823c67 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -792,6 +792,11 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int return NULL; } + /* Local "register" variables. + * These are cached values from the frame and code object. */ + _Py_CODEUNIT *next_instr; + _PyStackRef *stack_pointer; + #if defined(Py_DEBUG) && !defined(Py_STACKREF_DEBUG) /* Set these to invalid but identifiable values for debugging. */ entry_frame.f_funcobj = (_PyStackRef){.bits = 0xaaa0}; @@ -838,15 +843,11 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int #endif _Py_Instrument(_PyFrame_GetCode(frame), tstate->interp); monitor_throw(tstate, frame, frame->instr_ptr); - /* TO DO -- Monitor throw entry. */ - goto resume_with_error; + next_instr = frame->instr_ptr; + stack_pointer = _PyFrame_GetStackPointer(frame); + goto error; } - /* Local "register" variables. - * These are cached values from the frame and code object. */ - _Py_CODEUNIT *next_instr; - _PyStackRef *stack_pointer; - #if defined(_Py_TIER2) && !defined(_Py_JIT) /* Tier 2 interpreter state */ _PyExecutorObject *current_executor = NULL; @@ -983,10 +984,10 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int OPT_HIST(trace_uop_execution_counter, trace_run_length_hist); assert(next_uop[-1].format == UOP_FORMAT_TARGET); frame->return_offset = 0; // Don't leave this random - _PyFrame_SetStackPointer(frame, stack_pointer); Py_DECREF(current_executor); tstate->previous_executor = NULL; - goto resume_with_error; + next_instr = frame->instr_ptr; + goto error; jump_to_jump_target: assert(next_uop[-1].format == UOP_FORMAT_JUMP); diff --git a/Python/ceval_macros.h b/Python/ceval_macros.h index 62c80c96e422fd..c2fc38f3c18e53 100644 --- a/Python/ceval_macros.h +++ b/Python/ceval_macros.h @@ -381,7 +381,9 @@ do { \ tstate->previous_executor = NULL; \ frame = tstate->current_frame; \ if (next_instr == NULL) { \ - goto resume_with_error; \ + next_instr = frame->instr_ptr; \ + stack_pointer = _PyFrame_GetStackPointer(frame); \ + goto error; \ } \ stack_pointer = _PyFrame_GetStackPointer(frame); \ DISPATCH(); \ diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index ffdad70815caef..2d1e79363c2f68 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -8703,11 +8703,6 @@ tstate->c_recursion_remaining += PY_EVAL_C_STACK_UNITS; return NULL; } - goto resume_with_error; - } - - resume_with_error: - { next_instr = frame->instr_ptr; stack_pointer = _PyFrame_GetStackPointer(frame); goto error; diff --git a/Tools/cases_generator/analyzer.py b/Tools/cases_generator/analyzer.py index b9293ff4b19951..acf9458019fb4b 100644 --- a/Tools/cases_generator/analyzer.py +++ b/Tools/cases_generator/analyzer.py @@ -511,7 +511,6 @@ def has_error_with_pop(op: parser.InstDef) -> bool: variable_used(op, "ERROR_IF") or variable_used(op, "pop_1_error") or variable_used(op, "exception_unwind") - or variable_used(op, "resume_with_error") ) @@ -520,7 +519,6 @@ def has_error_without_pop(op: parser.InstDef) -> bool: variable_used(op, "ERROR_NO_POP") or variable_used(op, "pop_1_error") or variable_used(op, "exception_unwind") - or variable_used(op, "resume_with_error") ) From 80b3c364a6bbd6efce23b1f8e7918769b1313545 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Fri, 31 Jan 2025 15:56:08 +0000 Subject: [PATCH 2/2] Reduce entry points to interpreter loop down to two. --- Python/bytecodes.c | 29 ++++++++++++++++++++++ Python/ceval.c | 51 ++++++++++++++------------------------ Python/generated_cases.c.h | 27 ++++++++++++++++++++ 3 files changed, 75 insertions(+), 32 deletions(-) diff --git a/Python/bytecodes.c b/Python/bytecodes.c index b98d256e2a832d..effc8e0b6f6578 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -5308,6 +5308,35 @@ dummy_func( goto error; } + label(start_frame) { + if (_Py_EnterRecursivePy(tstate)) { + goto exit_unwind; + } + next_instr = frame->instr_ptr; + stack_pointer = _PyFrame_GetStackPointer(frame); + + #ifdef LLTRACE + { + int lltrace = maybe_lltrace_resume_frame(frame, GLOBALS()); + frame->lltrace = lltrace; + if (lltrace < 0) { + goto exit_unwind; + } + } + #endif + + #ifdef Py_DEBUG + /* _PyEval_EvalFrameDefault() must not be called with an exception set, + because it can clear it (directly or indirectly) and so the + caller loses its exception */ + assert(!_PyErr_Occurred(tstate)); + #endif + + DISPATCH(); + } + + + // END BYTECODES // } diff --git a/Python/ceval.c b/Python/ceval.c index 99eac16b823c67..11518684c136bd 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -824,27 +824,26 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int /* support for generator.throw() */ if (throwflag) { if (_Py_EnterRecursivePy(tstate)) { - goto exit_unwind; + goto early_exit; } - /* Because this avoids the RESUME, - * we need to update instrumentation */ #ifdef Py_GIL_DISABLED /* Load thread-local bytecode */ if (frame->tlbc_index != ((_PyThreadStateImpl *)tstate)->tlbc_index) { _Py_CODEUNIT *bytecode = _PyEval_GetExecutableCode(tstate, _PyFrame_GetCode(frame)); if (bytecode == NULL) { - goto exit_unwind; + goto early_exit; } ptrdiff_t off = frame->instr_ptr - _PyFrame_GetBytecode(frame); frame->tlbc_index = ((_PyThreadStateImpl *)tstate)->tlbc_index; frame->instr_ptr = bytecode + off; } #endif + /* Because this avoids the RESUME, we need to update instrumentation */ _Py_Instrument(_PyFrame_GetCode(frame), tstate->interp); - monitor_throw(tstate, frame, frame->instr_ptr); next_instr = frame->instr_ptr; stack_pointer = _PyFrame_GetStackPointer(frame); + monitor_throw(tstate, frame, next_instr); goto error; } @@ -854,33 +853,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int const _PyUOpInstruction *next_uop = NULL; #endif -start_frame: - if (_Py_EnterRecursivePy(tstate)) { - goto exit_unwind; - } - - next_instr = frame->instr_ptr; -resume_frame: - stack_pointer = _PyFrame_GetStackPointer(frame); - -#ifdef LLTRACE - { - int lltrace = maybe_lltrace_resume_frame(frame, GLOBALS()); - frame->lltrace = lltrace; - if (lltrace < 0) { - goto exit_unwind; - } - } -#endif - -#ifdef Py_DEBUG - /* _PyEval_EvalFrameDefault() must not be called with an exception set, - because it can clear it (directly or indirectly) and so the - caller loses its exception */ - assert(!_PyErr_Occurred(tstate)); -#endif - - DISPATCH(); + goto start_frame; #include "generated_cases.c.h" @@ -1019,6 +992,20 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int #endif // _Py_TIER2 +early_exit: + assert(_PyErr_Occurred(tstate)); + _Py_LeaveRecursiveCallPy(tstate); + assert(frame->owner != FRAME_OWNED_BY_INTERPRETER); + // GH-99729: We need to unlink the frame *before* clearing it: + _PyInterpreterFrame *dying = frame; + frame = tstate->current_frame = dying->previous; + _PyEval_FrameClearAndPop(tstate, dying); + frame->return_offset = 0; + assert(frame->owner == FRAME_OWNED_BY_INTERPRETER); + /* Restore previous frame and exit */ + tstate->current_frame = frame->previous; + tstate->c_recursion_remaining += PY_EVAL_C_STACK_UNITS; + return NULL; } #if defined(__GNUC__) diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 2d1e79363c2f68..38ea63d71ab044 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -8708,5 +8708,32 @@ goto error; } + start_frame: + { + if (_Py_EnterRecursivePy(tstate)) { + goto exit_unwind; + } + next_instr = frame->instr_ptr; + stack_pointer = _PyFrame_GetStackPointer(frame); + #ifdef LLTRACE + { + int lltrace = maybe_lltrace_resume_frame(frame, GLOBALS()); + frame->lltrace = lltrace; + if (lltrace < 0) { + goto exit_unwind; + } + } + #endif + + #ifdef Py_DEBUG + /* _PyEval_EvalFrameDefault() must not be called with an exception set, + because it can clear it (directly or indirectly) and so the + caller loses its exception */ + assert(!_PyErr_Occurred(tstate)); + #endif + + DISPATCH(); + } + /* END LABELS */ #undef TIER_ONE