8000 bpo-47177: Replace `f_lasti` with `prev_instr` by brandtbucher · Pull Request #32208 · python/cpython · GitHub
[go: up one dir, main page]

Skip to content

bpo-47177: Replace f_lasti with prev_instr #32208

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 14 commits into from
Apr 7, 2022
17 changes: 10 additions & 7 deletions Include/internal/pycore_frame.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,6 @@ enum _frameowner {
FRAME_OWNED_BY_FRAME_OBJECT = 2
};

/*
frame->f_lasti refers to the index of the last instruction,
unless it's -1 in which case next_instr should be first_instr.
*/

typedef struct _PyInterpreterFrame {
PyFunctionObject *f_func; /* Strong reference */
PyObject *f_globals; /* Borrowed reference */
Expand All @@ -52,13 +47,20 @@ typedef struct _PyInterpreterFrame {
PyCodeObject *f_code; /* Strong reference */
PyFrameObject *frame_obj; /* Strong reference, may be NULL */
struct _PyInterpreterFrame *previous;
int f_lasti; /* Last instruction if called */
// NOTE: This is not necessarily the last instruction started in the given
// frame. Rather, it is the code unit *prior to* the *next* instruction. For
// example, it may be an inline CACHE entry, an instruction we just jumped
// over, or (in the case of a newly-created frame) a totally invalid value:
_Py_CODEUNIT *prev_instr;
int stacktop; /* Offset of TOS from localsplus */
bool is_entry; // Whether this is the "root" frame for the current _PyCFrame.
char owner;
PyObject *localsplus[1];
} _PyInterpreterFrame;

#define _PyInterpreterFrame_LASTI(IF) \
((int)((IF)->prev_instr - _PyCode_CODE((IF)->f_code)))

static inline PyObject **_PyFrame_Stackbase(_PyInterpreterFrame *f) {
return f->localsplus + f->f_code->co_nlocalsplus;
}
Expand Down Expand Up @@ -97,7 +99,7 @@ _PyFrame_InitializeSpecials(
frame->f_locals = Py_XNewRef(locals);
frame->stacktop = nlocalsplus;
frame->frame_obj = NULL;
frame->f_lasti = -1;
frame->prev_instr = _PyCode_CODE(frame->f_code) - 1;
frame->is_entry = false;
frame->owner = FRAME_OWNED_BY_THREAD;
}
Expand Down Expand Up @@ -186,6 +188,7 @@ void _PyThreadState_PopFrame(PyThreadState *tstate, _PyInterpreterFrame *frame);
_PyInterpreterFrame *
_PyFrame_Push(PyThreadState *tstate, PyFunctionObject *func);

int _PyInterpreterFrame_GetLine(_PyInterpreterFrame *frame);

static inline
PyGenObject *_PyFrame_GetGenerator(_PyInterpreterFrame *frame)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Replace the ``f_lasti`` member of the internal ``_PyInterpreterFrame`` structure with a ``prev_instr`` pointer, which reduces overhead in the main interpreter loop. The ``f_lasti`` attribute of Python-layer frame objects is preserved for backward-compatibility.
2 changes: 1 addition & 1 deletion Modules/_tracemalloc.c
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,7 @@ static void
tracemalloc_get_frame(_PyInterpreterFrame *pyframe, frame_t *frame)
{
frame->filename = &_Py_STR(anon_unknown);
int lineno = PyCode_Addr2Line(pyframe->f_code, pyframe->f_lasti*sizeof(_Py_CODEUNIT));
int lineno = _PyInterpreterFrame_GetLine(pyframe);
if (lineno < 0) {
lineno = 0;
}
Expand Down
35 changes: 18 additions & 17 deletions Objects/frameobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ PyFrame_GetLineNumber(PyFrameObject *f)
return f->f_lineno;
}
else {
return PyCode_Addr2Line(f->f_frame->f_code, f->f_frame->f_lasti*sizeof(_Py_CODEUNIT));
return _PyInterpreterFrame_GetLine(f->f_frame);
}
}

Expand All @@ -58,10 +58,11 @@ frame_getlineno(PyFrameObject *f, void *closure)
static PyObject *
frame_getlasti(PyFrameObject *f, void *closure)
{
if (f->f_frame->f_lasti < 0) {
int lasti = _PyInterpreterFrame_LASTI(f->f_frame);
if (lasti < 0) {
return PyLong_FromLong(-1);
}
return PyLong_FromLong(f->f_frame->f_lasti*sizeof(_Py_CODEUNIT));
return PyLong_FromLong(lasti * sizeof(_Py_CODEUNIT));
}

static PyObject *
Expand Down 8000 Expand Up @@ -426,12 +427,11 @@ _PyFrame_GetState(PyFrameObject *frame)
}
case FRAME_OWNED_BY_THREAD:
{
if (frame->f_frame->f_lasti < 0) {
if (_PyInterpreterFrame_LASTI(frame->f_frame) < 0) {
return FRAME_CREATED;
}
uint8_t *code = (uint8_t *)frame->f_frame->f_code->co_code_adaptive;
int opcode = code[frame->f_frame->f_lasti*sizeof(_Py_CODEUNIT)];
switch(_PyOpcode_Deopt[opcode]) {
switch (_PyOpcode_Deopt[_Py_OPCODE(*frame->f_frame->prev_instr)])
{
case COPY_FREE_VARS:
case MAKE_CELL:
case RETURN_GENERATOR:
Expand Down Expand Up @@ -562,7 +562,7 @@ frame_setlineno(PyFrameObject *f, PyObject* p_new_lineno, void *Py_UNUSED(ignore

int64_t best_stack = OVERFLOWED;
int best_addr = -1;
int64_t start_stack = stacks[f->f_frame->f_lasti];
int64_t start_stack = stacks[_PyInterpreterFrame_LASTI(f->f_frame)];
int err = -1;
const char *msg = "cannot find bytecode for specified line";
for (int i = 0; i < len; i++) {
Expand Down Expand Up @@ -605,7 +605,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->f_lasti = best_addr;
f->f_frame->prev_instr = _PyCode_CODE(f->f_frame->f_code) + best_addr;
return 0;
}

Expand Down Expand Up @@ -887,10 +887,11 @@ _PyFrame_OpAlreadyRan(_PyInterpreterFrame *frame, int opcode, int oparg)
// This only works when opcode is a non-quickened form:
assert(_PyOpcode_Deopt[opcode] == opcode);
int check_oparg = 0;
for (int i = 0; i < frame->f_lasti; i++) {
_Py_CODEUNIT instruction = _PyCode_CODE(frame->f_code)[i];
int check_opcode = _PyOpcode_Deopt[_Py_OPCODE(instruction)];
check_oparg |= _Py_OPARG(instruction);
for (_Py_CODEUNIT *instruction = _PyCode_CODE(frame->f_code);
instruction < frame->prev_instr; instruction++)
{
int check_opcode = _PyOpcode_Deopt[_Py_OPCODE(*instruction)];
check_oparg |= _Py_OPARG(*instruction);
if (check_opcode == opcode && check_oparg == oparg) {
return 1;
}
Expand All @@ -900,7 +901,7 @@ _PyFrame_OpAlreadyRan(_PyInterpreterFrame *frame, int opcode, int oparg)
else {
check_oparg = 0;
}
i += _PyOpcode_Caches[check_opcode];
instruction += _PyOpcode_Caches[check_opcode];
}
return 0;
}
Expand All @@ -921,8 +922,8 @@ _PyFrame_FastToLocalsWithError(_PyInterpreterFrame *frame) {
fast = _PyFrame_GetLocalsArray(frame);
// COPY_FREE_VARS has no quickened forms, so no need to use _PyOpcode_Deopt
// here:
if (frame->f_lasti < 0 && _Py_OPCODE(_PyCode_CODE(co)[0]) == COPY_FREE_VARS)
{
int lasti = _PyInterpreterFrame_LASTI(frame);
if (lasti < 0 && _Py_OPCODE(_PyCode_CODE(co)[0]) == COPY_FREE_VARS) {
/* Free vars have not been initialized -- Do that */
PyCodeObject *co = frame->f_code;
PyObject *closure = frame->f_func->func_closure;
Expand All @@ -933,7 +934,7 @@ _PyFrame_FastToLocalsWithError(_PyInterpreterFrame *frame) {
frame->localsplus[offset + i] = o;
}
// COPY_FREE_VARS doesn't have inline CACHEs, either:
frame->f_lasti = 0;
frame->prev_instr = _PyCode_CODE(frame->f_code);
}
for (int i = 0; i < co->co_nlocalsplus; i++) {
_PyLocals_Kind kind = _PyLocals_GetKind(co->co_localspluskinds, i);
Expand Down
19 changes: 8 additions & 11 deletions Objects/genobject.c
< E668 td class="blob-num blob-num-deletion empty-cell">
Original file line number Diff line number Diff line change
Expand Up @@ -352,14 +352,14 @@ _PyGen_yf(PyGenObject *gen)
if (gen->gi_frame_state < FRAME_CLEARED) {
_PyInterpreterFrame *frame = (_PyInterpreterFrame *)gen->gi_iframe;

if (frame->f_lasti < 1) {
if (gen->gi_frame_state == FRAME_CREATED) {
/* Return immediately if the frame didn't start yet. SEND
always come after LOAD_CONST: a code object should not start
with SEND */
assert(_Py_OPCODE(_PyCode_CODE(gen->gi_code)[0]) != SEND);
return NULL;
}
_Py_CODEUNIT next = _PyCode_CODE(gen->gi_code)[frame->f_lasti + 1];
_Py_CODEUNIT next = frame->prev_instr[1];
if (_PyOpcode_Deopt[_Py_OPCODE(next)] != RESUME || _Py_OPARG(next) < 2)
{
/* Not in a yield from */
Expand Down Expand Up @@ -487,13 +487,11 @@ _gen_throw(PyGenObject *gen, int close_on_genexit,
// XXX: Performing this jump ourselves is awkward and problematic.
// See https://github.com/python/cpython/pull/31968.
/* Termination repetition of SEND loop */
assert(frame->f_lasti >= 0);
_Py_CODEUNIT *code = _PyCode_CODE(gen->gi_code);
assert(_PyInterpreterFrame_LASTI(frame) >= 0);
/* Backup to SEND */
frame->f_lasti--;
assert(_Py_OPCODE(code[frame->f_lasti]) == SEND);
int jump = _Py_OPARG(code[frame->f_lasti]);
frame->f_lasti += jump;
assert(_Py_OPCODE(frame->prev_instr[-1]) == SEND);
int jump = _Py_OPARG(frame->prev_instr[-1]);
frame->prev_instr += jump - 1;
if (_PyGen_FetchStopIterationValue(&val) == 0) {
ret = gen_send(gen, val);
Py_DECREF(val);
Expand Down Expand Up @@ -1338,9 +1336,8 @@ compute_cr_origin(int origin_depth, _PyInterpreterFrame *current_frame)
frame = current_frame;
for (int i = 0; i < frame_count; ++i) {
PyCodeObject *code = frame->f_code;
PyObject *frameinfo = Py_BuildValue("OiO",
code->co_filename,
PyCode_Addr2Line(frame->f_code, frame->f_lasti*sizeof(_Py_CODEUNIT)),
int line = _PyInterpreterFrame_GetLine(frame);
PyObject *frameinfo = Py_BuildValue("OiO", code->co_filename, line,
code->co_name);
if (!frameinfo) {
Py_DECREF(cr_origin);
Expand Down
2 changes: 1 addition & 1 deletion Objects/typeobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -8948,7 +8948,7 @@ super_init_without_args(_PyInterpreterFrame *cframe, PyCodeObject *co,
if (firstarg != NULL && (_PyLocals_GetKind(co->co_localspluskinds, 0) & CO_FAST_CELL)) {
// "firstarg" is a cell here unless (very unlikely) super()
// was called from the C-API before the first MAKE_CELL op.
if (cframe->f_lasti >= 0) {
if (_PyInterpreterFrame_LASTI(cframe) >= 0) {
// MAKE_CELL and COPY_FREE_VARS have no quickened forms, so no need
// to use _PyOpcode_Deopt here:
assert(_Py_OPCODE(_PyCode_CODE(co)[0]) == MAKE_CELL ||
Expand Down
Loading
0