GH-96421: Insert shim frame on entry to interpreter (GH-96319)

* Adds EXIT_INTERPRETER instruction to exit PyEval_EvalDefault()

* Simplifies RETURN_VALUE, YIELD_VALUE and RETURN_GENERATOR instructions as they no longer need to check for entry frames.
This commit is contained in:
Mark Shannon 2022-11-10 04:34:57 -08:00 committed by GitHub
parent dbf2faf579
commit 1e197e63e2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
24 changed files with 450 additions and 346 deletions

View file

@ -97,8 +97,7 @@ dummy_func(
PyObject *consts,
_Py_CODEUNIT *next_instr,
PyObject **stack_pointer,
CallShape call_shape,
_Py_CODEUNIT *first_instr,
PyObject *kwnames,
int throwflag,
binaryfunc binary_ops[]
)
@ -617,6 +616,21 @@ dummy_func(
goto error;
}
// stack effect: (__0 -- )
inst(INTERPRETER_EXIT) {
assert(frame == &entry_frame);
assert(_PyFrame_IsIncomplete(frame));
PyObject *retval = POP();
assert(EMPTY());
/* Restore previous cframe and return. */
tstate->cframe = cframe.previous;
tstate->cframe->use_tracing = cframe.use_tracing;
assert(tstate->cframe->current_frame == frame->previous);
assert(!_PyErr_Occurred(tstate));
_Py_LeaveRecursiveCallTstate(tstate);
return retval;
}
// stack effect: (__0 -- )
inst(RETURN_VALUE) {
PyObject *retval = POP();
@ -625,23 +639,10 @@ dummy_func(
TRACE_FUNCTION_EXIT();
DTRACE_FUNCTION_EXIT();
_Py_LeaveRecursiveCallPy(tstate);
if (!frame->is_entry) {
frame = cframe.current_frame = pop_frame(tstate, frame);
_PyFrame_StackPush(frame, retval);
goto resume_frame;
}
_Py_LeaveRecursiveCallTstate(tstate);
if (frame->owner == FRAME_OWNED_BY_GENERATOR) {
PyGenObject *gen = _PyFrame_GetGenerator(frame);
tstate->exc_info = gen->gi_exc_state.previous_item;
gen->gi_exc_state.previous_item = NULL;
}
/* Restore previous cframe and return. */
tstate->cframe = cframe.previous;
tstate->cframe->use_tracing = cframe.use_tracing;
assert(tstate->cframe->current_frame == frame->previous);
assert(!_PyErr_Occurred(tstate));
return retval;
assert(frame != &entry_frame);
frame = cframe.current_frame = pop_frame(tstate, frame);
_PyFrame_StackPush(frame, retval);
goto resume_frame;
}
// stack effect: ( -- )
@ -775,6 +776,7 @@ dummy_func(
// error: SEND stack effect depends on jump flag
inst(SEND) {
assert(frame != &entry_frame);
assert(STACK_LEVEL() >= 2);
PyObject *v = POP();
PyObject *receiver = TOP();
@ -839,6 +841,7 @@ dummy_func(
// The compiler treats any exception raised here as a failed close()
// or throw() call.
assert(oparg == STACK_LEVEL());
assert(frame != &entry_frame);
PyObject *retval = POP();
PyGenObject *gen = _PyFrame_GetGenerator(frame);
gen->gi_frame_state = FRAME_SUSPENDED;
@ -848,19 +851,12 @@ dummy_func(
tstate->exc_info = gen->gi_exc_state.previous_item;
gen->gi_exc_state.previous_item = NULL;
_Py_LeaveRecursiveCallPy(tstate);
if (!frame->is_entry) {
frame = cframe.current_frame = frame->previous;
frame->prev_instr -= frame->yield_offset;
_PyFrame_StackPush(frame, retval);
goto resume_frame;
}
_Py_LeaveRecursiveCallTstate(tstate);
/* Restore previous cframe and return. */
tstate->cframe = cframe.previous;
tstate->cframe->use_tracing = cframe.use_tracing;
assert(tstate->cframe->current_frame == frame->previous);
assert(!_PyErr_Occurred(tstate));
return retval;
_PyInterpreterFrame *gen_frame = frame;
frame = cframe.current_frame = frame->previous;
gen_frame->previous = NULL;
frame->prev_instr -= frame->yield_offset;
_PyFrame_StackPush(frame, retval);
goto resume_frame;
}
// stack effect: (__0 -- )
@ -876,7 +872,7 @@ dummy_func(
if (oparg) {
PyObject *lasti = PEEK(oparg + 1);
if (PyLong_Check(lasti)) {
frame->prev_instr = first_instr + PyLong_AsLong(lasti);
frame->prev_instr = _PyCode_CODE(frame->f_code) + PyLong_AsLong(lasti);
assert(!_PyErr_Occurred(tstate));
}
else {
@ -2696,12 +2692,10 @@ dummy_func(
gen->gi_exc_state.previous_item = tstate->exc_info;
tstate->exc_info = &gen->gi_exc_state;
gen_frame->previous = frame;
gen_frame->is_entry = false;
frame = cframe.current_frame = gen_frame;
goto start_frame;
}
// stack effect: ( -- __0)
inst(BEFORE_ASYNC_WITH) {
PyObject *mgr = TOP();
@ -2929,9 +2923,9 @@ dummy_func(
// stack effect: ( -- )
inst(KW_NAMES) {
assert(call_shape.kwnames == NULL);
assert(kwnames == NULL);
assert(oparg < PyTuple_GET_SIZE(consts));
call_shape.kwnames = GETITEM(consts, oparg);
kwnames = GETITEM(consts, oparg);
}
// stack effect: (__0, __array[oparg] -- )
@ -2943,7 +2937,7 @@ dummy_func(
int nargs = oparg + is_meth;
PyObject *callable = PEEK(nargs + 1);
next_instr--;
_Py_Specialize_Call(callable, next_instr, nargs, call_shape.kwnames);
_Py_Specialize_Call(callable, next_instr, nargs, kwnames);
DISPATCH_SAME_OPARG();
}
STAT_INC(CALL, deferred);
@ -2972,9 +2966,9 @@ dummy_func(
STACK_SHRINK(total_args);
_PyInterpreterFrame *new_frame = _PyEvalFramePushAndInit(
tstate, (PyFunctionObject *)function, locals,
stack_pointer, positional_args, call_shape.kwnames
stack_pointer, positional_args, kwnames
);
call_shape.kwnames = NULL;
kwnames = NULL;
STACK_SHRINK(2-is_meth);
// The frame has stolen all the arguments from the stack,
// so there is no need to clean them up.
@ -2994,15 +2988,15 @@ dummy_func(
if (cframe.use_tracing) {
res = trace_call_function(
tstate, function, stack_pointer-total_args,
positional_args, call_shape.kwnames);
positional_args, kwnames);
}
else {
res = PyObject_Vectorcall(
function, stack_pointer-total_args,
positional_args | PY_VECTORCALL_ARGUMENTS_OFFSET,
call_shape.kwnames);
kwnames);
}
call_shape.kwnames = NULL;
kwnames = NULL;
assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL));
Py_DECREF(function);
/* Clear the stack */
@ -3021,7 +3015,7 @@ dummy_func(
// stack effect: (__0, __array[oparg] -- )
inst(CALL_PY_EXACT_ARGS) {
assert(call_shape.kwnames == NULL);
assert(kwnames == NULL);
DEOPT_IF(tstate->interp->eval_frame, CALL);
_PyCallCache *cache = (_PyCallCache *)next_instr;
int is_meth = is_method(stack_pointer, oparg);
@ -3054,7 +3048,7 @@ dummy_func(
// stack effect: (__0, __array[oparg] -- )
inst(CALL_PY_WITH_DEFAULTS) {
assert(call_shape.kwnames == NULL);
assert(kwnames == NULL);
DEOPT_IF(tstate->interp->eval_frame, CALL);
_PyCallCache *cache = (_PyCallCache *)next_instr;
int is_meth = is_method(stack_pointer, oparg);
@ -3094,7 +3088,7 @@ dummy_func(
// stack effect: (__0, __array[oparg] -- )
inst(CALL_NO_KW_TYPE_1) {
assert(call_shape.kwnames == NULL);
assert(kwnames == NULL);
assert(cframe.use_tracing == 0);
assert(oparg == 1);
DEOPT_IF(is_method(stack_pointer, 1), CALL);
@ -3112,7 +3106,7 @@ dummy_func(
// stack effect: (__0, __array[oparg] -- )
inst(CALL_NO_KW_STR_1) {
assert(call_shape.kwnames == NULL);
assert(kwnames == NULL);
assert(cframe.use_tracing == 0);
assert(oparg == 1);
DEOPT_IF(is_method(stack_pointer, 1), CALL);
@ -3134,7 +3128,7 @@ dummy_func(
// stack effect: (__0, __array[oparg] -- )
inst(CALL_NO_KW_TUPLE_1) {
assert(call_shape.kwnames == NULL);
assert(kwnames == NULL);
assert(oparg == 1);
DEOPT_IF(is_method(stack_pointer, 1), CALL);
PyObject *callable = PEEK(2);
@ -3166,8 +3160,8 @@ dummy_func(
JUMPBY(INLINE_CACHE_ENTRIES_CALL);
STACK_SHRINK(total_args);
PyObject *res = tp->tp_vectorcall((PyObject *)tp, stack_pointer,
total_args-kwnames_len, call_shape.kwnames);
call_shape.kwnames = NULL;
total_args-kwnames_len, kwnames);
kwnames = NULL;
/* Free the arguments. */
for (int i = 0; i < total_args; i++) {
Py_DECREF(stack_pointer[i]);
@ -3185,7 +3179,7 @@ dummy_func(
inst(CALL_NO_KW_BUILTIN_O) {
assert(cframe.use_tracing == 0);
/* Builtin METH_O functions */
assert(call_shape.kwnames == NULL);
assert(kwnames == NULL);
int is_meth = is_method(stack_pointer, oparg);
int total_args = oparg + is_meth;
DEOPT_IF(total_args != 1, CALL);
@ -3219,7 +3213,7 @@ dummy_func(
inst(CALL_NO_KW_BUILTIN_FAST) {
assert(cframe.use_tracing == 0);
/* Builtin METH_FASTCALL functions, without keywords */
assert(call_shape.kwnames == NULL);
assert(kwnames == NULL);
int is_meth = is_method(stack_pointer, oparg);
int total_args = oparg + is_meth;
PyObject *callable = PEEK(total_args + 1);
@ -3276,10 +3270,10 @@ dummy_func(
PyCFunction_GET_SELF(callable),
stack_pointer,
total_args - KWNAMES_LEN(),
call_shape.kwnames
kwnames
);
assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL));
call_shape.kwnames = NULL;
kwnames = NULL;
/* Free the arguments. */
for (int i = 0; i < total_args; i++) {
@ -3297,7 +3291,7 @@ dummy_func(
// stack effect: (__0, __array[oparg] -- )
inst(CALL_NO_KW_LEN) {
assert(cframe.use_tracing == 0);
assert(call_shape.kwnames == NULL);
assert(kwnames == NULL);
/* len(o) */
int is_meth = is_method(stack_pointer, oparg);
int total_args = oparg + is_meth;
@ -3327,7 +3321,7 @@ dummy_func(
// stack effect: (__0, __array[oparg] -- )
inst(CALL_NO_KW_ISINSTANCE) {
assert(cframe.use_tracing == 0);
assert(call_shape.kwnames == NULL);
assert(kwnames == NULL);
/* isinstance(o, o2) */
int is_meth = is_method(stack_pointer, oparg);
int total_args = oparg + is_meth;
@ -3360,7 +3354,7 @@ dummy_func(
// stack effect: (__0, __array[oparg] -- )
inst(CALL_NO_KW_LIST_APPEND) {
assert(cframe.use_tracing == 0);
assert(call_shape.kwnames == NULL);
assert(kwnames == NULL);
assert(oparg == 1);
PyObject *callable = PEEK(3);
PyInterpreterState *interp = _PyInterpreterState_GET();
@ -3382,7 +3376,7 @@ dummy_func(
// stack effect: (__0, __array[oparg] -- )
inst(CALL_NO_KW_METHOD_DESCRIPTOR_O) {
assert(call_shape.kwnames == NULL);
assert(kwnames == NULL);
int is_meth = is_method(stack_pointer, oparg);
int total_args = oparg + is_meth;
PyMethodDescrObject *callable =
@ -3435,9 +3429,9 @@ dummy_func(
_PyCFunctionFastWithKeywords cfunc =
(_PyCFunctionFastWithKeywords)(void(*)(void))meth->ml_meth;
PyObject *res = cfunc(self, stack_pointer, nargs - KWNAMES_LEN(),
call_shape.kwnames);
kwnames);
assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL));
call_shape.kwnames = NULL;
kwnames = NULL;
/* Free the arguments. */
for (int i = 0; i < nargs; i++) {
@ -3455,7 +3449,7 @@ dummy_func(
// stack effect: (__0, __array[oparg] -- )
inst(CALL_NO_KW_METHOD_DESCRIPTOR_NOARGS) {
assert(call_shape.kwnames == NULL);
assert(kwnames == NULL);
assert(oparg == 0 || oparg == 1);
int is_meth = is_method(stack_pointer, oparg);
int total_args = oparg + is_meth;
@ -3489,7 +3483,7 @@ dummy_func(
// stack effect: (__0, __array[oparg] -- )
inst(CALL_NO_KW_METHOD_DESCRIPTOR_FAST) {
assert(call_shape.kwnames == NULL);
assert(kwnames == NULL);
int is_meth = is_method(stack_pointer, oparg);
int total_args = oparg + is_meth;
PyMethodDescrObject *callable =
@ -3606,25 +3600,12 @@ dummy_func(
gen->gi_frame_state = FRAME_CREATED;
gen_frame->owner = FRAME_OWNED_BY_GENERATOR;
_Py_LeaveRecursiveCallPy(tstate);
if (!frame->is_entry) {
_PyInterpreterFrame *prev = frame->previous;
_PyThreadState_PopFrame(tstate, frame);
frame = cframe.current_frame = prev;
_PyFrame_StackPush(frame, (PyObject *)gen);
goto resume_frame;
}
_Py_LeaveRecursiveCallTstate(tstate);
/* Make sure that frame is in a valid state */
frame->stacktop = 0;
frame->f_locals = NULL;
Py_INCREF(frame->f_funcobj);
Py_INCREF(frame->f_code);
/* Restore previous cframe and return. */
tstate->cframe = cframe.previous;
tstate->cframe->use_tracing = cframe.use_tracing;
assert(tstate->cframe->current_frame == frame->previous);
assert(!_PyErr_Occurred(tstate));
return (PyObject *)gen;
assert(frame != &entry_frame);
_PyInterpreterFrame *prev = frame->previous;
_PyThreadState_PopFrame(tstate, frame);
frame = cframe.current_frame = prev;
_PyFrame_StackPush(frame, (PyObject *)gen);
goto resume_frame;
}
// error: BUILD_SLICE has irregular stack effect