Skip to content

Commit 54f74b8

Browse files
authored
GH-128563: Move some labels, to simplify implementing tailcalling interpreter. (GH-129525)
1 parent 9ba281d commit 54f74b8

File tree

5 files changed

+86
-51
lines changed

5 files changed

+86
-51
lines changed

Python/bytecodes.c

+29-4
Original file line numberDiff line numberDiff line change
@@ -5303,14 +5303,40 @@ dummy_func(
53035303
tstate->c_recursion_remaining += PY_EVAL_C_STACK_UNITS;
53045304
return NULL;
53055305
}
5306-
goto resume_with_error;
5306+
next_instr = frame->instr_ptr;
5307+
stack_pointer = _PyFrame_GetStackPointer(frame);
5308+
goto error;
53075309
}
53085310

5309-
label(resume_with_error) {
5311+
label(start_frame) {
5312+
if (_Py_EnterRecursivePy(tstate)) {
5313+
goto exit_unwind;
5314+
}
53105315
next_instr = frame->instr_ptr;
53115316
stack_pointer = _PyFrame_GetStackPointer(frame);
5312-
goto error;
5317+
5318+
#ifdef LLTRACE
5319+
{
5320+
int lltrace = maybe_lltrace_resume_frame(frame, GLOBALS());
5321+
frame->lltrace = lltrace;
5322+
if (lltrace < 0) {
5323+
goto exit_unwind;
5324+
}
5325+
}
5326+
#endif
5327+
5328+
#ifdef Py_DEBUG
5329+
/* _PyEval_EvalFrameDefault() must not be called with an exception set,
5330+
because it can clear it (directly or indirectly) and so the
5331+
caller loses its exception */
5332+
assert(!_PyErr_Occurred(tstate));
5333+
#endif
5334+
5335+
DISPATCH();
53135336
}
5337+
5338+
5339+
53145340
// END BYTECODES //
53155341

53165342
}
@@ -5320,7 +5346,6 @@ dummy_func(
53205346
exit_unwind:
53215347
handle_eval_breaker:
53225348
resume_frame:
5323-
resume_with_error:
53245349
start_frame:
53255350
unbound_local_error:
53265351
;

Python/ceval.c

+29-41
Original file line numberDiff line numberDiff line change
@@ -792,6 +792,11 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
792792
return NULL;
793793
}
794794

795+
/* Local "register" variables.
796+
* These are cached values from the frame and code object. */
797+
_Py_CODEUNIT *next_instr;
798+
_PyStackRef *stack_pointer;
799+
795800
#if defined(Py_DEBUG) && !defined(Py_STACKREF_DEBUG)
796801
/* Set these to invalid but identifiable values for debugging. */
797802
entry_frame.f_funcobj = (_PyStackRef){.bits = 0xaaa0};
@@ -819,67 +824,36 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
819824
/* support for generator.throw() */
820825
if (throwflag) {
821826
if (_Py_EnterRecursivePy(tstate)) {
822-
goto exit_unwind;
827+
goto early_exit;
823828
}
824-
/* Because this avoids the RESUME,
825-
* we need to update instrumentation */
826829
#ifdef Py_GIL_DISABLED
827830
/* Load thread-local bytecode */
828831
if (frame->tlbc_index != ((_PyThreadStateImpl *)tstate)->tlbc_index) {
829832
_Py_CODEUNIT *bytecode =
830833
_PyEval_GetExecutableCode(tstate, _PyFrame_GetCode(frame));
831834
if (bytecode == NULL) {
832-
goto exit_unwind;
835+
goto early_exit;
833836
}
834837
ptrdiff_t off = frame->instr_ptr - _PyFrame_GetBytecode(frame);
835838
frame->tlbc_index = ((_PyThreadStateImpl *)tstate)->tlbc_index;
836839
frame->instr_ptr = bytecode + off;
837840
}
838841
#endif
842+
/* Because this avoids the RESUME, we need to update instrumentation */
839843
_Py_Instrument(_PyFrame_GetCode(frame), tstate->interp);
840-
monitor_throw(tstate, frame, frame->instr_ptr);
841-
/* TO DO -- Monitor throw entry. */
842-
goto resume_with_error;
844+
next_instr = frame->instr_ptr;
845+
stack_pointer = _PyFrame_GetStackPointer(frame);
846+
monitor_throw(tstate, frame, next_instr);
847+
goto error;
843848
}
844849

845-
/* Local "register" variables.
846-
* These are cached values from the frame and code object. */
847-
_Py_CODEUNIT *next_instr;
848-
_PyStackRef *stack_pointer;
849-
850850
#if defined(_Py_TIER2) && !defined(_Py_JIT)
851851
/* Tier 2 interpreter state */
852852
_PyExecutorObject *current_executor = NULL;
853853
const _PyUOpInstruction *next_uop = NULL;
854854
#endif
855855

856-
start_frame:
857-
if (_Py_EnterRecursivePy(tstate)) {
858-
goto exit_unwind;
859-
}
860-
861-
next_instr = frame->instr_ptr;
862-
resume_frame:
863-
stack_pointer = _PyFrame_GetStackPointer(frame);
864-
865-
#ifdef LLTRACE
866-
{
867-
int lltrace = maybe_lltrace_resume_frame(frame, GLOBALS());
868-
frame->lltrace = lltrace;
869-
if (lltrace < 0) {
870-
goto exit_unwind;
871-
}
872-
}
873-
#endif
874-
875-
#ifdef Py_DEBUG
876-
/* _PyEval_EvalFrameDefault() must not be called with an exception set,
877-
because it can clear it (directly or indirectly) and so the
878-
caller loses its exception */
879-
assert(!_PyErr_Occurred(tstate));
880-
#endif
881-
882-
DISPATCH();
856+
goto start_frame;
883857

884858
#include "generated_cases.c.h"
885859

@@ -983,10 +957,10 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
983957
OPT_HIST(trace_uop_execution_counter, trace_run_length_hist);
984958
assert(next_uop[-1].format == UOP_FORMAT_TARGET);
985959
frame->return_offset = 0; // Don't leave this random
986-
_PyFrame_SetStackPointer(frame, stack_pointer);
987960
Py_DECREF(current_executor);
988961
tstate->previous_executor = NULL;
989-
goto resume_with_error;
962+
next_instr = frame->instr_ptr;
963+
goto error;
990964

991965
jump_to_jump_target:
992966
assert(next_uop[-1].format == UOP_FORMAT_JUMP);
@@ -1018,6 +992,20 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
1018992

1019993
#endif // _Py_TIER2
1020994

995+
early_exit:
996+
assert(_PyErr_Occurred(tstate));
997+
_Py_LeaveRecursiveCallPy(tstate);
998+
assert(frame->owner != FRAME_OWNED_BY_INTERPRETER);
999+
// GH-99729: We need to unlink the frame *before* clearing it:
1000+
_PyInterpreterFrame *dying = frame;
1001+
frame = tstate->current_frame = dying->previous;
1002+
_PyEval_FrameClearAndPop(tstate, dying);
1003+
frame->return_offset = 0;
1004+
assert(frame->owner == FRAME_OWNED_BY_INTERPRETER);
1005+
/* Restore previous frame and exit */
1006+
tstate->current_frame = frame->previous;
1007+
tstate->c_recursion_remaining += PY_EVAL_C_STACK_UNITS;
1008+
return NULL;
10211009
}
10221010

10231011
#if defined(__GNUC__)

Python/ceval_macros.h

+3-1
Original file line numberDiff line numberDiff line change
@@ -381,7 +381,9 @@ do { \
381381
tstate->previous_executor = NULL; \
382382
frame = tstate->current_frame; \
383383
if (next_instr == NULL) { \
384-
goto resume_with_error; \
384+
next_instr = frame->instr_ptr; \
385+
stack_pointer = _PyFrame_GetStackPointer(frame); \
386+
goto error; \
385387
} \
386388
stack_pointer = _PyFrame_GetStackPointer(frame); \
387389
DISPATCH(); \

Python/generated_cases.c.h

+25-3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Tools/cases_generator/analyzer.py

-2
Original file line numberDiff line numberDiff line change
@@ -511,7 +511,6 @@ def has_error_with_pop(op: parser.InstDef) -> bool:
511511
variable_used(op, "ERROR_IF")
512512
or variable_used(op, "pop_1_error")
513513
or variable_used(op, "exception_unwind")
514-
or variable_used(op, "resume_with_error")
515514
)
516515

517516

@@ -520,7 +519,6 @@ def has_error_without_pop(op: parser.InstDef) -> bool:
520519
variable_used(op, "ERROR_NO_POP")
521520
or variable_used(op, "pop_1_error")
522521
or variable_used(op, "exception_unwind")
523-
or variable_used(op, "resume_with_error")
524522
)
525523

526524

0 commit comments

Comments
 (0)