Skip to content

Commit ef6a482

Browse files
authored
bpo-47177: Replace f_lasti with prev_instr (pythonGH-32208)
1 parent 87eec70 commit ef6a482

File tree

10 files changed

+90
-75
lines changed

10 files changed

+90
-75
lines changed

Include/internal/pycore_frame.h

+10-7
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,6 @@ enum _frameowner {
3939
FRAME_OWNED_BY_FRAME_OBJECT = 2
4040
};
4141

42-
/*
43-
frame->f_lasti refers to the index of the last instruction,
44-
unless it's -1 in which case next_instr should be first_instr.
45-
*/
46-
4742
typedef struct _PyInterpreterFrame {
4843
PyFunctionObject *f_func; /* Strong reference */
4944
PyObject *f_globals; /* Borrowed reference */
@@ -52,13 +47,20 @@ typedef struct _PyInterpreterFrame {
5247
PyCodeObject *f_code; /* Strong reference */
5348
PyFrameObject *frame_obj; /* Strong reference, may be NULL */
5449
struct _PyInterpreterFrame *previous;
55-
int f_lasti; /* Last instruction if called */
50+
// NOTE: This is not necessarily the last instruction started in the given
51+
// frame. Rather, it is the code unit *prior to* the *next* instruction. For
52+
// example, it may be an inline CACHE entry, an instruction we just jumped
53+
// over, or (in the case of a newly-created frame) a totally invalid value:
54+
_Py_CODEUNIT *prev_instr;
5655
int stacktop; /* Offset of TOS from localsplus */
5756
bool is_entry; // Whether this is the "root" frame for the current _PyCFrame.
5857
char owner;
5958
PyObject *localsplus[1];
6059
} _PyInterpreterFrame;
6160

61+
#define _PyInterpreterFrame_LASTI(IF) \
62+
((int)((IF)->prev_instr - _PyCode_CODE((IF)->f_code)))
63+
6264
static inline PyObject **_PyFrame_Stackbase(_PyInterpreterFrame *f) {
6365
return f->localsplus + f->f_code->co_nlocalsplus;
6466
}
@@ -97,7 +99,7 @@ _PyFrame_InitializeSpecials(
9799
frame->f_locals = Py_XNewRef(locals);
98100
frame->stacktop = nlocalsplus;
99101
frame->frame_obj = NULL;
100-
frame->f_lasti = -1;
102+
frame->prev_instr = _PyCode_CODE(frame->f_code) - 1;
101103
frame->is_entry = false;
102104
frame->owner = FRAME_OWNED_BY_THREAD;
103105
}
@@ -186,6 +188,7 @@ void _PyThreadState_PopFrame(PyThreadState *tstate, _PyInterpreterFrame *frame);
186188
_PyInterpreterFrame *
187189
_PyFrame_Push(PyThreadState *tstate, PyFunctionObject *func);
188190

191+
int _PyInterpreterFrame_GetLine(_PyInterpreterFrame *frame);
189192

190193
static inline
191194
PyGenObject *_PyFrame_GetGenerator(_PyInterpreterFrame *frame)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
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.

Modules/_tracemalloc.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -308,7 +308,7 @@ static void
308308
tracemalloc_get_frame(_PyInterpreterFrame *pyframe, frame_t *frame)
309309
{
310310
frame->filename = &_Py_STR(anon_unknown);
311-
int lineno = PyCode_Addr2Line(pyframe->f_code, pyframe->f_lasti*sizeof(_Py_CODEUNIT));
311+
int lineno = _PyInterpreterFrame_GetLine(pyframe);
312312
if (lineno < 0) {
313313
lineno = 0;
314314
}

Objects/frameobject.c

+18-17
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ PyFrame_GetLineNumber(PyFrameObject *f)
3939
return f->f_lineno;
4040
}
4141
else {
42-
return PyCode_Addr2Line(f->f_frame->f_code, f->f_frame->f_lasti*sizeof(_Py_CODEUNIT));
42+
return _PyInterpreterFrame_GetLine(f->f_frame);
4343
}
4444
}
4545

@@ -58,10 +58,11 @@ frame_getlineno(PyFrameObject *f, void *closure)
5858
static PyObject *
5959
frame_getlasti(PyFrameObject *f, void *closure)
6060
{
61-
if (f->f_frame->f_lasti < 0) {
61+
int lasti = _PyInterpreterFrame_LASTI(f->f_frame);
62+
if (lasti < 0) {
6263
return PyLong_FromLong(-1);
6364
}
64-
return PyLong_FromLong(f->f_frame->f_lasti*sizeof(_Py_CODEUNIT));
65+
return PyLong_FromLong(lasti * sizeof(_Py_CODEUNIT));
6566
}
6667

6768
static PyObject *
@@ -419,12 +420,11 @@ _PyFrame_GetState(PyFrameObject *frame)
419420
}
420421
case FRAME_OWNED_BY_THREAD:
421422
{
422-
if (frame->f_frame->f_lasti < 0) {
423+
if (_PyInterpreterFrame_LASTI(frame->f_frame) < 0) {
423424
return FRAME_CREATED;
424425
}
425-
uint8_t *code = (uint8_t *)frame->f_frame->f_code->co_code_adaptive;
426-
int opcode = code[frame->f_frame->f_lasti*sizeof(_Py_CODEUNIT)];
427-
switch(_PyOpcode_Deopt[opcode]) {
426+
switch (_PyOpcode_Deopt[_Py_OPCODE(*frame->f_frame->prev_instr)])
427+
{
428428
case COPY_FREE_VARS:
429429
case MAKE_CELL:
430430
case RETURN_GENERATOR:
@@ -555,7 +555,7 @@ frame_setlineno(PyFrameObject *f, PyObject* p_new_lineno, void *Py_UNUSED(ignore
555555

556556
int64_t best_stack = OVERFLOWED;
557557
int best_addr = -1;
558-
int64_t start_stack = stacks[f->f_frame->f_lasti];
558+
int64_t start_stack = stacks[_PyInterpreterFrame_LASTI(f->f_frame)];
559559
int err = -1;
560560
const char *msg = "cannot find bytecode for specified line";
561561
for (int i = 0; i < len; i++) {
@@ -598,7 +598,7 @@ frame_setlineno(PyFrameObject *f, PyObject* p_new_lineno, void *Py_UNUSED(ignore
598598
}
599599
/* Finally set the new lasti and return OK. */
600600
f->f_lineno = 0;
601-
f->f_frame->f_lasti = best_addr;
601+
f->f_frame->prev_instr = _PyCode_CODE(f->f_frame->f_code) + best_addr;
602602
return 0;
603603
}
604604

@@ -880,10 +880,11 @@ _PyFrame_OpAlreadyRan(_PyInterpreterFrame *frame, int opcode, int oparg)
880880
// This only works when opcode is a non-quickened form:
881881
assert(_PyOpcode_Deopt[opcode] == opcode);
882882
int check_oparg = 0;
883-
for (int i = 0; i < frame->f_lasti; i++) {
884-
_Py_CODEUNIT instruction = _PyCode_CODE(frame->f_code)[i];
885-
int check_opcode = _PyOpcode_Deopt[_Py_OPCODE(instruction)];
886-
check_oparg |= _Py_OPARG(instruction);
883+
for (_Py_CODEUNIT *instruction = _PyCode_CODE(frame->f_code);
884+
instruction < frame->prev_instr; instruction++)
885+
{
886+
int check_opcode = _PyOpcode_Deopt[_Py_OPCODE(*instruction)];
887+
check_oparg |= _Py_OPARG(*instruction);
887888
if (check_opcode == opcode && check_oparg == oparg) {
888889
return 1;
889890
}
@@ -893,7 +894,7 @@ _PyFrame_OpAlreadyRan(_PyInterpreterFrame *frame, int opcode, int oparg)
893894
else {
894895
check_oparg = 0;
895896
}
896-
i += _PyOpcode_Caches[check_opcode];
897+
instruction += _PyOpcode_Caches[check_opcode];
897898
}
898899
return 0;
899900
}
@@ -914,8 +915,8 @@ _PyFrame_FastToLocalsWithError(_PyInterpreterFrame *frame) {
914915
fast = _PyFrame_GetLocalsArray(frame);
915916
// COPY_FREE_VARS has no quickened forms, so no need to use _PyOpcode_Deopt
916917
// here:
917-
if (frame->f_lasti < 0 && _Py_OPCODE(_PyCode_CODE(co)[0]) == COPY_FREE_VARS)
918-
{
918+
int lasti = _PyInterpreterFrame_LASTI(frame);
919+
if (lasti < 0 && _Py_OPCODE(_PyCode_CODE(co)[0]) == COPY_FREE_VARS) {
919920
/* Free vars have not been initialized -- Do that */
920921
PyCodeObject *co = frame->f_code;
921922
PyObject *closure = frame->f_func->func_closure;
@@ -926,7 +927,7 @@ _PyFrame_FastToLocalsWithError(_PyInterpreterFrame *frame) {
926927
frame->localsplus[offset + i] = o;
927928
}
928929
// COPY_FREE_VARS doesn't have inline CACHEs, either:
929-
frame->f_lasti = 0;
930+
frame->prev_instr = _PyCode_CODE(frame->f_code);
930931
}
931932
for (int i = 0; i < co->co_nlocalsplus; i++) {
932933
_PyLocals_Kind kind = _PyLocals_GetKind(co->co_localspluskinds, i);

Objects/genobject.c

+8-11
Original file line numberDiff line numberDiff line change
@@ -352,14 +352,14 @@ _PyGen_yf(PyGenObject *gen)
352352
if (gen->gi_frame_state < FRAME_CLEARED) {
353353
_PyInterpreterFrame *frame = (_PyInterpreterFrame *)gen->gi_iframe;
354354

355-
if (frame->f_lasti < 1) {
355+
if (gen->gi_frame_state == FRAME_CREATED) {
356356
/* Return immediately if the frame didn't start yet. SEND
357357
always come after LOAD_CONST: a code object should not start
358358
with SEND */
359359
assert(_Py_OPCODE(_PyCode_CODE(gen->gi_code)[0]) != SEND);
360360
return NULL;
361361
}
362-
_Py_CODEUNIT next = _PyCode_CODE(gen->gi_code)[frame->f_lasti + 1];
362+
_Py_CODEUNIT next = frame->prev_instr[1];
363363
if (_PyOpcode_Deopt[_Py_OPCODE(next)] != RESUME || _Py_OPARG(next) < 2)
364364
{
365365
/* Not in a yield from */
@@ -490,13 +490,11 @@ _gen_throw(PyGenObject *gen, int close_on_genexit,
490490
// XXX: Performing this jump ourselves is awkward and problematic.
491491
// See https://github.com/python/cpython/pull/31968.
492492
/* Termination repetition of SEND loop */
493-
assert(frame->f_lasti >= 0);
494-
_Py_CODEUNIT *code = _PyCode_CODE(gen->gi_code);
493+
assert(_PyInterpreterFrame_LASTI(frame) >= 0);
495494
/* Backup to SEND */
496-
frame->f_lasti--;
497-
assert(_Py_OPCODE(code[frame->f_lasti]) == SEND);
498-
int jump = _Py_OPARG(code[frame->f_lasti]);
499-
frame->f_lasti += jump;
495+
assert(_Py_OPCODE(frame->prev_instr[-1]) == SEND);
496+
int jump = _Py_OPARG(frame->prev_instr[-1]);
497+
frame->prev_instr += jump - 1;
500498
if (_PyGen_FetchStopIterationValue(&val) == 0) {
501499
ret = gen_send(gen, val);
502500
Py_DECREF(val);
@@ -1344,9 +1342,8 @@ compute_cr_origin(int origin_depth, _PyInterpreterFrame *current_frame)
13441342
frame = current_frame;
13451343
for (int i = 0; i < frame_count; ++i) {
13461344
PyCodeObject *code = frame->f_code;
1347-
PyObject *frameinfo = Py_BuildValue("OiO",
1348-
code->co_filename,
1349-
PyCode_Addr2Line(frame->f_code, frame->f_lasti*sizeof(_Py_CODEUNIT)),
1345+
int line = _PyInterpreterFrame_GetLine(frame);
1346+
PyObject *frameinfo = Py_BuildValue("OiO", code->co_filename, line,
13501347
code->co_name);
13511348
if (!frameinfo) {
13521349
Py_DECREF(cr_origin);

Objects/typeobject.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -8980,7 +8980,7 @@ super_init_without_args(_PyInterpreterFrame *cframe, PyCodeObject *co,
89808980
if (firstarg != NULL && (_PyLocals_GetKind(co->co_localspluskinds, 0) & CO_FAST_CELL)) {
89818981
// "firstarg" is a cell here unless (very unlikely) super()
89828982
// was called from the C-API before the first MAKE_CELL op.
8983-
if (cframe->f_lasti >= 0) {
8983+
if (_PyInterpreterFrame_LASTI(cframe) >= 0) {
89848984
// MAKE_CELL and COPY_FREE_VARS have no quickened forms, so no need
89858985
// to use _PyOpcode_Deopt here:
89868986
assert(_Py_OPCODE(_PyCode_CODE(co)[0]) == MAKE_CELL ||

0 commit comments

Comments
 (0)