Skip to content

Commit 6d7518d

Browse files
committed
Factor out unwinding logic
1 parent 6c63afc commit 6d7518d

File tree

9 files changed

+347
-173
lines changed

9 files changed

+347
-173
lines changed

Include/cpython/ceval.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ PyAPI_FUNC(void) PyEval_SetTraceAllThreads(Py_tracefunc, PyObject *);
1212
flag was set, else return 0. */
1313
PyAPI_FUNC(int) PyEval_MergeCompilerFlags(PyCompilerFlags *cf);
1414

15-
PyAPI_FUNC(PyObject *) _PyEval_EvalFrameDefault(PyThreadState *tstate, struct _PyInterpreterFrame *f, int exc);
15+
PyAPI_FUNC(PyObject *) _PyEval_EvalFrameDefault(PyThreadState *tstate, struct _PyInterpreterFrame *f, int throwflag);
16+
PyAPI_FUNC(PyObject *) _PyEval_Interpret(PyThreadState *tstate, struct _PyInterpreterFrame *f);
1617

1718
PyAPI_FUNC(Py_ssize_t) PyUnstable_Eval_RequestCodeExtraIndex(freefunc);
1819
// Old name -- remove when this API changes:

Include/cpython/pystate.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -278,7 +278,7 @@ PyAPI_FUNC(void) PyThreadState_DeleteCurrent(void);
278278

279279
/* Frame evaluation API */
280280

281-
typedef PyObject* (*_PyFrameEvalFunction)(PyThreadState *tstate, struct _PyInterpreterFrame *, int);
281+
typedef PyObject* (*_PyFrameEvalFunction)(PyThreadState *tstate, struct _PyInterpreterFrame *, int throwflag);
282282

283283
PyAPI_FUNC(_PyFrameEvalFunction) _PyInterpreterState_GetEvalFrameFunc(
284284
PyInterpreterState *interp);

Include/internal/pycore_ceval.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,16 +108,23 @@ extern _PyPerf_Callbacks _Py_perfmap_callbacks;
108108
extern _PyPerf_Callbacks _Py_perfmap_jit_callbacks;
109109
#endif
110110

111+
_Py_CODEUNIT *
112+
_Py_UnwindFrame(PyThreadState *tstate, struct _PyInterpreterFrame *frame, _Py_CODEUNIT *location);
113+
111114
static inline PyObject*
112115
_PyEval_EvalFrame(PyThreadState *tstate, struct _PyInterpreterFrame *frame, int throwflag)
113116
{
114117
EVAL_CALL_STAT_INC(EVAL_CALL_TOTAL);
115118
if (tstate->interp->eval_frame == NULL) {
116119
return _PyEval_EvalFrameDefault(tstate, frame, throwflag);
117120
}
118-
return tstate->interp->eval_frame(tstate, frame, throwflag);
121+
return tstate->interp->eval_frame(tstate, frame, 0);
119122
}
120123

124+
void
125+
_Py_MonitorThrow(PyThreadState *tstate,
126+
_PyInterpreterFrame *frame);
127+
121128
extern PyObject*
122129
_PyEval_Vector(PyThreadState *tstate,
123130
PyFunctionObject *func, PyObject *locals,

Modules/_testinternalcapi.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -643,7 +643,7 @@ set_eval_frame_default(PyObject *self, PyObject *Py_UNUSED(args))
643643
}
644644

645645
static PyObject *
646-
record_eval(PyThreadState *tstate, struct _PyInterpreterFrame *f, int exc)
646+
record_eval(PyThreadState *tstate, struct _PyInterpreterFrame *f, int throwflag)
647647
{
648648
if (PyStackRef_FunctionCheck(f->f_funcobj)) {
649649
PyFunctionObject *func = _PyFrame_GetFunction(f);
@@ -656,7 +656,7 @@ record_eval(PyThreadState *tstate, struct _PyInterpreterFrame *f, int exc)
656656
return NULL;
657657
}
658658
}
659-
return _PyEval_EvalFrameDefault(tstate, f, exc);
659+
return _PyEval_EvalFrameDefault(tstate, f, throwflag);
660660
}
661661

662662

Python/bytecodes.c

Lines changed: 21 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,6 @@ dummy_func(
8787
unsigned int oparg,
8888
_Py_CODEUNIT *next_instr,
8989
PyObject **stack_pointer,
90-
int throwflag,
9190
PyObject *args[]
9291
)
9392
{
@@ -1381,7 +1380,6 @@ dummy_func(
13811380

13821381
tier1 inst(CLEANUP_THROW, (sub_iter_st, last_sent_val_st, exc_value_st -- none, value)) {
13831382
PyObject *exc_value = PyStackRef_AsPyObjectBorrow(exc_value_st);
1384-
assert(throwflag);
13851383
assert(exc_value && PyExceptionInstance_Check(exc_value));
13861384

13871385
int matches = PyErr_GivenExceptionMatches(exc_value, PyExc_StopIteration);
@@ -5231,60 +5229,42 @@ dummy_func(
52315229
PyTraceBack_Here(f);
52325230
}
52335231
}
5232+
/* Manually spill and reload. TO DO: The code generator should do this */
5233+
_PyFrame_SetStackPointer(frame, stack_pointer);
52345234
_PyEval_MonitorRaise(tstate, frame, next_instr-1);
5235+
stack_pointer = _PyFrame_GetStackPointer(frame);
52355236
goto exception_unwind;
52365237
}
52375238

52385239
label(exception_unwind) {
5240+
assert(_PyErr_Occurred(tstate));
52395241
/* We can't use frame->instr_ptr here, as RERAISE may have set it */
5240-
int offset = INSTR_OFFSET()-1;
5241-
int level, handler, lasti;
5242-
if (get_exception_handler(_PyFrame_GetCode(frame), offset, &level, &handler, &lasti) == 0) {
5243-
// No handlers, so exit.
5242+
/* Manually spill and reload. TO DO: The code generator should do this */
5243+
_PyFrame_SetStackPointer(frame, stack_pointer);
5244+
next_instr = _Py_UnwindFrame(tstate, frame, next_instr-1);
5245+
stack_pointer = _PyFrame_GetStackPointer(frame);
5246+
if (next_instr == NULL) {
52445247
assert(_PyErr_Occurred(tstate));
5245-
5246-
/* Pop remaining stack entries. */
5247-
_PyStackRef *stackbase = _PyFrame_Stackbase(frame);
5248-
while (stack_pointer > stackbase) {
5249-
PyStackRef_XCLOSE(POP());
5250-
}
5251-
assert(STACK_LEVEL() == 0);
5252-
_PyFrame_SetStackPointer(frame, stack_pointer);
5253-
monitor_unwind(tstate, frame, next_instr-1);
5254-
goto exit_unwind;
5255-
}
5256-
5257-
assert(STACK_LEVEL() >= level);
5258-
_PyStackRef *new_top = _PyFrame_Stackbase(frame) + level;
5259-
while (stack_pointer > new_top) {
5260-
PyStackRef_XCLOSE(POP());
5261-
}
5262-
if (lasti) {
5263-
int frame_lasti = _PyInterpreterFrame_LASTI(frame);
5264-
PyObject *lasti = PyLong_FromLong(frame_lasti);
5265-
if (lasti == NULL) {
5266-
goto exception_unwind;
5248+
_Py_LeaveRecursiveCallPy(tstate);
5249+
frame = tstate->current_frame;
5250+
frame->return_offset = 0;
5251+
if (frame->owner == FRAME_OWNED_BY_INTERPRETER) {
5252+
/* Restore previous frame and exit */
5253+
tstate->current_frame = frame->previous;
5254+
tstate->c_recursion_remaining += PY_EVAL_C_STACK_UNITS;
5255+
return NULL;
52675256
}
5268-
PUSH(PyStackRef_FromPyObjectSteal(lasti));
5269-
}
5270-
5271-
/* Make the raw exception data
5272-
available to the handler,
5273-
so a program can emulate the
5274-
Python main loop. */
5275-
PyObject *exc = _PyErr_GetRaisedException(tstate);
5276-
PUSH(PyStackRef_FromPyObjectSteal(exc));
5277-
next_instr = _PyFrame_GetBytecode(frame) + handler;
5278-
5279-
if (monitor_handled(tstate, frame, next_instr, exc) < 0) {
5280-
goto exception_unwind;
5257+
stack_pointer = _PyFrame_GetStackPointer(frame);
5258+
next_instr = frame->instr_ptr;
5259+
goto error;
52815260
}
52825261
/* Resume normal execution */
52835262
#ifdef LLTRACE
52845263
if (frame->lltrace >= 5) {
52855264
lltrace_resume_frame(frame);
52865265
}
52875266
#endif
5267+
assert(!_PyErr_Occurred(tstate));
52885268
DISPATCH();
52895269
}
52905270

Python/ceval.c

Lines changed: 94 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,7 @@ maybe_lltrace_resume_frame(_PyInterpreterFrame *frame, PyObject *globals)
250250
lltrace = *python_lltrace - '0'; // TODO: Parse an int and all that
251251
}
252252
}
253+
lltrace = 5;
253254
if (lltrace >= 5) {
254255
lltrace_resume_frame(frame);
255256
}
@@ -271,9 +272,8 @@ static void monitor_unwind(PyThreadState *tstate,
271272
static int monitor_handled(PyThreadState *tstate,
272273
_PyInterpreterFrame *frame,
273274
_Py_CODEUNIT *instr, PyObject *exc);
274-
static void monitor_throw(PyThreadState *tstate,
275-
_PyInterpreterFrame *frame,
276-
_Py_CODEUNIT *instr);
275+
void _Py_MonitorThrow(PyThreadState *tstate,
276+
_PyInterpreterFrame *frame);
277277

278278
static int get_exception_handler(PyCodeObject *, int, int*, int*, int*);
279279
static _PyInterpreterFrame *
@@ -671,14 +671,22 @@ PyEval_EvalFrame(PyFrameObject *f)
671671
{
672672
/* Function kept for backward compatibility */
673673
PyThreadState *tstate = _PyThreadState_GET();
674-
return _PyEval_EvalFrame(tstate, f->f_frame, 0);
674+
if (tstate->interp->eval_frame == NULL) {
675+
return _PyEval_Interpret(tstate, f->f_frame);
676+
}
677+
return tstate->interp->eval_frame(tstate, f->f_frame, 0);
675678
}
676679

677680
PyObject *
678681
PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
679682
{
680-
PyThreadState *tstate = _PyThreadState_GET();
681-
return _PyEval_EvalFrame(tstate, f->f_frame, throwflag);
683+
if (throwflag) {
684+
PyThreadState *tstate = _PyThreadState_GET();
685+
if (_Py_UnwindFrame(tstate, f->f_frame, f->f_frame->instr_ptr)) {
686+
return NULL;
687+
}
688+
}
689+
return PyEval_EvalFrame(f);
682690
}
683691

684692
#include "ceval_macros.h"
@@ -761,14 +769,84 @@ _PyObjectArray_Free(PyObject **array, PyObject **scratch)
761769
}
762770
}
763771

772+
_Py_CODEUNIT *
773+
unwind_frame(PyThreadState *tstate, _PyInterpreterFrame *frame, _Py_CODEUNIT *location)
774+
{
775+
int offset, level, handler, lasti;
776+
start:
777+
/* We can't use frame->instr_ptr here, as RERAISE may have set it */
778+
offset = (int)(location - _PyCode_CODE(_PyFrame_GetCode(frame)));
779+
if (get_exception_handler(_PyFrame_GetCode(frame), offset, &level, &handler, &lasti) == 0) {
780+
// No handlers, so exit.
781+
assert(PyErr_Occurred());
782+
783+
/* Pop remaining stack entries. */
784+
_PyStackRef *stackbase = _PyFrame_Stackbase(frame);
785+
while (frame->stackpointer > stackbase) {
786+
PyStackRef_XCLOSE(_PyFrame_StackPop(frame));
787+
}
788+
monitor_unwind(tstate, frame, location);
789+
// GH-99729: We need to unlink the frame *before* clearing it:
790+
_PyInterpreterFrame *dying = frame;
791+
tstate->current_frame = dying->previous;
792+
_PyEval_FrameClearAndPop(tstate, dying);
793+
return NULL;
794+
}
795+
796+
_PyStackRef *new_top = _PyFrame_Stackbase(frame) + level;
797+
assert(frame->stackpointer >= new_top);
798+
while (frame->stackpointer > new_top) {
799+
PyStackRef_XCLOSE(_PyFrame_StackPop(frame));
800+
}
801+
if (lasti) {
802+
int frame_lasti = _PyInterpreterFrame_LASTI(frame);
803+
PyObject *lasti = PyLong_FromLong(frame_lasti);
804+
if (lasti == NULL) {
805+
return NULL;
806+
}
807+
_PyFrame_StackPush(frame, PyStackRef_FromPyObjectSteal(lasti));
808+
}
809+
810+
/* Make the raw exception data
811+
available to the handler,
812+
so a program can emulate the
813+
Python main loop. */
814+
PyObject *exc = _PyErr_GetRaisedException(tstate);
815+
_PyFrame_StackPush(frame, PyStackRef_FromPyObjectSteal(exc));
816+
location = _PyCode_CODE(_PyFrame_GetCode(frame)) + handler;
817+
frame->instr_ptr = location;
818+
if (monitor_handled(tstate, frame, location, exc) < 0) {
819+
goto start;
820+
}
821+
return location;
822+
}
823+
824+
_Py_CODEUNIT *
825+
_Py_UnwindFrame(PyThreadState *tstate, _PyInterpreterFrame *frame, _Py_CODEUNIT *location)
826+
{
827+
assert(frame->stackpointer != NULL);
828+
return unwind_frame(tstate, frame, location);
829+
}
830+
764831

765832
/* _PyEval_EvalFrameDefault() is a *big* function,
766833
* so consume 3 units of C stack */
767834
#define PY_EVAL_C_STACK_UNITS 2
768835

769836

837+
PyObject*
838+
_PyEval_EvalFrameDefault(PyThreadState *tstate, struct _PyInterpreterFrame *frame, int throwflag)
839+
{
840+
if (throwflag) {
841+
if (_Py_UnwindFrame(tstate, frame, frame->instr_ptr)) {
842+
return NULL;
843+
}
844+
}
845+
return _PyEval_Interpret(tstate, frame);
846+
}
847+
770848
PyObject* _Py_HOT_FUNCTION
771-
_PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int throwflag)
849+
_PyEval_Interpret(PyThreadState *tstate, _PyInterpreterFrame *frame)
772850
{
773851
_Py_EnsureTstateNotNULL(tstate);
774852
CALL_STAT_INC(pyeval_calls);
@@ -817,13 +895,6 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
817895
goto exit_unwind;
818896
}
819897

820-
/* support for generator.throw() */
821-
if (throwflag) {
822-
if (_Py_EnterRecursivePy(tstate)) {
823-
goto exit_unwind;
824-
}
825-
/* Because this avoids the RESUME,
826-
* we need to update instrumentation */
827898
#ifdef Py_GIL_DISABLED
828899
/* Load thread-local bytecode */
829900
if (frame->tlbc_index != ((_PyThreadStateImpl *)tstate)->tlbc_index) {
@@ -837,12 +908,6 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
837908
frame->instr_ptr = bytecode + off;
838909
}
839910
#endif
840-
_Py_Instrument(_PyFrame_GetCode(frame), tstate->interp);
841-
monitor_throw(tstate, frame, frame->instr_ptr);
842-
/* TO DO -- Monitor throw entry. */
843-
goto resume_with_error;
844-
}
845-
846911
/* Local "register" variables.
847912
* These are cached values from the frame and code object. */
848913
_Py_CODEUNIT *next_instr;
@@ -893,6 +958,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
893958
#ifdef _Py_JIT
894959
assert(0);
895960
#else
961+
stack_pointer = _PyFrame_GetStackPointer(frame);
896962

897963
#undef LOAD_IP
898964
#define LOAD_IP(UNUSED) (void)0
@@ -1757,7 +1823,10 @@ _PyEval_Vector(PyThreadState *tstate, PyFunctionObject *func,
17571823
return NULL;
17581824
}
17591825
EVAL_CALL_STAT_INC(EVAL_CALL_VECTOR);
1760-
return _PyEval_EvalFrame(tstate, frame, 0);
1826+
if (tstate->interp->eval_frame != NULL) {
1827+
return tstate->interp->eval_frame(tstate, frame, 0);
1828+
}
1829+
return _PyEval_Interpret(tstate, frame);
17611830
}
17621831

17631832
/* Legacy API */
@@ -2235,15 +2304,14 @@ monitor_handled(PyThreadState *tstate,
22352304
return _Py_call_instrumentation_arg(tstate, PY_MONITORING_EVENT_EXCEPTION_HANDLED, frame, instr, exc);
22362305
}
22372306

2238-
static void
2239-
monitor_throw(PyThreadState *tstate,
2240-
_PyInterpreterFrame *frame,
2241-
_Py_CODEUNIT *instr)
2307+
void
2308+
_Py_MonitorThrow(PyThreadState *tstate,
2309+
_PyInterpreterFrame *frame)
22422310
{
22432311
if (no_tools_for_global_event(tstate, PY_MONITORING_EVENT_PY_THROW)) {
22442312
return;
22452313
}
2246-
do_monitor_exc(tstate, frame, instr, PY_MONITORING_EVENT_PY_THROW);
2314+
do_monitor_exc(tstate, frame, frame->instr_ptr, PY_MONITORING_EVENT_PY_THROW);
22472315
}
22482316

22492317
void

0 commit comments

Comments
 (0)