mirror of
https://github.com/python/cpython.git
synced 2025-09-26 10:19:53 +00:00
gh-109094: replace frame->prev_instr by frame->instr_ptr (#109095)
This commit is contained in:
parent
573eff3e2e
commit
67a91f78e4
23 changed files with 249 additions and 164 deletions
|
@ -130,3 +130,28 @@ returns. This extra frame is inserted so that `RETURN_VALUE`, `YIELD_VALUE`, and
|
|||
`RETURN_GENERATOR` do not need to check whether the current frame is the entry frame.
|
||||
The shim frame points to a special code object containing the `INTERPRETER_EXIT`
|
||||
instruction which cleans up the shim frame and returns.
|
||||
|
||||
|
||||
### The Instruction Pointer
|
||||
|
||||
`_PyInterpreterFrame` has two fields which are used to maintain the instruction
|
||||
pointer: `instr_ptr` and `return_offset`.
|
||||
|
||||
When a frame is executing, `instr_ptr` points to the instruction currently being
|
||||
executed. In a suspended frame, it points to the instruction that would execute
|
||||
if the frame were to resume. After `frame.f_lineno` is set, `instr_ptr` points to
|
||||
the next instruction to be executed. During a call to a python function,
|
||||
`instr_ptr` points to the call instruction, because this is what we would expect
|
||||
to see in an exception traceback.
|
||||
|
||||
The `return_offset` field determines where a `RETURN` should go in the caller,
|
||||
relative to `instr_ptr`. It is only meaningful to the callee, so it needs to
|
||||
be set in any instruction that implements a call (to a Python function),
|
||||
including CALL, SEND and BINARY_SUBSCR_GETITEM, among others. If there is no
|
||||
callee, then return_offset is meaningless. It is necessary to have a separate
|
||||
field for the return offset because (1) if we apply this offset to `instr_ptr`
|
||||
while executing the `RETURN`, this is too early and would lose us information
|
||||
about the previous instruction which we could need for introspecting and
|
||||
debugging. (2) `SEND` needs to pass two offsets to the generator: one for
|
||||
`RETURN` and one for `YIELD`. It uses the `oparg` for one, and the
|
||||
`return_offset` for the other.
|
||||
|
|
|
@ -821,7 +821,7 @@ frame_setlineno(PyFrameObject *f, PyObject* p_new_lineno, void *Py_UNUSED(ignore
|
|||
}
|
||||
/* Finally set the new lasti and return OK. */
|
||||
f->f_lineno = 0;
|
||||
f->f_frame->prev_instr = _PyCode_CODE(code) + best_addr;
|
||||
f->f_frame->instr_ptr = _PyCode_CODE(code) + best_addr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1079,7 +1079,7 @@ PyFrame_New(PyThreadState *tstate, PyCodeObject *code,
|
|||
f->f_frame = (_PyInterpreterFrame *)f->_f_frame_data;
|
||||
f->f_frame->owner = FRAME_OWNED_BY_FRAME_OBJECT;
|
||||
// This frame needs to be "complete", so pretend that the first RESUME ran:
|
||||
f->f_frame->prev_instr = _PyCode_CODE(code) + code->_co_firsttraceable;
|
||||
f->f_frame->instr_ptr = _PyCode_CODE(code) + code->_co_firsttraceable + 1;
|
||||
assert(!_PyFrame_IsIncomplete(f->f_frame));
|
||||
Py_DECREF(func);
|
||||
_PyObject_GC_TRACK(f);
|
||||
|
@ -1093,7 +1093,7 @@ _PyFrame_OpAlreadyRan(_PyInterpreterFrame *frame, int opcode, int oparg)
|
|||
assert(_PyOpcode_Deopt[opcode] == opcode);
|
||||
int check_oparg = 0;
|
||||
for (_Py_CODEUNIT *instruction = _PyCode_CODE(_PyFrame_GetCode(frame));
|
||||
instruction < frame->prev_instr; instruction++)
|
||||
instruction < frame->instr_ptr; instruction++)
|
||||
{
|
||||
int check_opcode = _PyOpcode_Deopt[instruction->op.code];
|
||||
check_oparg |= instruction->op.arg;
|
||||
|
@ -1135,7 +1135,7 @@ frame_init_get_vars(_PyInterpreterFrame *frame)
|
|||
frame->localsplus[offset + i] = Py_NewRef(o);
|
||||
}
|
||||
// COPY_FREE_VARS doesn't have inline CACHEs, either:
|
||||
frame->prev_instr = _PyCode_CODE(_PyFrame_GetCode(frame));
|
||||
frame->instr_ptr = _PyCode_CODE(_PyFrame_GetCode(frame));
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include "pycore_genobject.h" // struct _Py_async_gen_state
|
||||
#include "pycore_modsupport.h" // _PyArg_CheckPositional()
|
||||
#include "pycore_object.h" // _PyObject_GC_UNTRACK()
|
||||
#include "pycore_opcode_metadata.h" // _PyOpcode_Caches
|
||||
#include "pycore_pyerrors.h" // _PyErr_ClearExcState()
|
||||
#include "pycore_pystate.h" // _PyThreadState_GET()
|
||||
|
||||
|
@ -362,8 +363,7 @@ _PyGen_yf(PyGenObject *gen)
|
|||
assert(_PyCode_CODE(_PyGen_GetCode(gen))[0].op.code != SEND);
|
||||
return NULL;
|
||||
}
|
||||
_Py_CODEUNIT next = frame->prev_instr[1];
|
||||
if (!is_resume(&next) || next.op.arg < 2)
|
||||
if (!is_resume(frame->instr_ptr) || frame->instr_ptr->op.arg < 2)
|
||||
{
|
||||
/* Not in a yield from */
|
||||
return NULL;
|
||||
|
@ -398,9 +398,12 @@ gen_close(PyGenObject *gen, PyObject *args)
|
|||
_PyInterpreterFrame *frame = (_PyInterpreterFrame *)gen->gi_iframe;
|
||||
/* It is possible for the previous instruction to not be a
|
||||
* YIELD_VALUE if the debugger has changed the lineno. */
|
||||
if (err == 0 && is_yield(frame->prev_instr)) {
|
||||
assert(is_resume(frame->prev_instr + 1));
|
||||
int exception_handler_depth = frame->prev_instr[0].op.arg;
|
||||
assert(_PyOpcode_Caches[YIELD_VALUE] == 0);
|
||||
assert(_PyOpcode_Caches[INSTRUMENTED_YIELD_VALUE] == 0);
|
||||
if (err == 0 && is_yield(frame->instr_ptr - 1)) {
|
||||
_Py_CODEUNIT *yield_instr = frame->instr_ptr - 1;
|
||||
assert(is_resume(frame->instr_ptr));
|
||||
int exception_handler_depth = yield_instr->op.arg;
|
||||
assert(exception_handler_depth > 0);
|
||||
/* We can safely ignore the outermost try block
|
||||
* as it automatically generated to handle
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue