diff --git a/Include/internal/pycore_frame.h b/Include/internal/pycore_frame.h index 211831a6e49..49bdc6324ca 100644 --- a/Include/internal/pycore_frame.h +++ b/Include/internal/pycore_frame.h @@ -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 */ @@ -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; } @@ -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; } @@ -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) diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-03-31-01-30-03.bpo-47177.fQqaov.rst b/Misc/NEWS.d/next/Core and Builtins/2022-03-31-01-30-03.bpo-47177.fQqaov.rst new file mode 100644 index 00000000000..01e6c883924 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2022-03-31-01-30-03.bpo-47177.fQqaov.rst @@ -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. diff --git a/Modules/_tracemalloc.c b/Modules/_tracemalloc.c index 738d54530c9..ae09869deda 100644 --- a/Modules/_tracemalloc.c +++ b/Modules/_tracemalloc.c @@ -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; } diff --git a/Objects/frameobject.c b/Objects/frameobject.c index 6842e62839f..07b610717d2 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -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); } } @@ -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 * @@ -419,12 +420,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: @@ -555,7 +555,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++) { @@ -598,7 +598,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; } @@ -880,10 +880,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; } @@ -893,7 +894,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; } @@ -914,8 +915,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; @@ -926,7 +927,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); diff --git a/Objects/genobject.c b/Objects/genobject.c index cdb2a0f76b0..e58118b2694 100644 --- a/Objects/genobject.c +++ b/Objects/genobject.c @@ -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 */ @@ -490,13 +490,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); @@ -1344,9 +1342,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); diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 53e4f0781d6..64c4bbb5a93 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -8980,7 +8980,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 || diff --git a/Python/ceval.c b/Python/ceval.c index 5384aac5d6e..cb739c1572d 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -58,12 +58,10 @@ static int prtrace(PyThreadState *, PyObject *, const char *); static void lltrace_instruction(_PyInterpreterFrame *frame, int opcode, int oparg) { if (HAS_ARG(opcode)) { - printf("%d: %d, %d\n", - frame->f_lasti, opcode, oparg); + printf("%d: %d, %d\n", _PyInterpreterFrame_LASTI(frame), opcode, oparg); } else { - printf("%d: %d\n", - frame->f_lasti, opcode); + printf("%d: %d\n", _PyInterpreterFrame_LASTI(frame), opcode); } } #endif @@ -1249,14 +1247,13 @@ eval_frame_handle_pending(PyThreadState *tstate) #ifdef Py_STATS #define INSTRUCTION_START(op) \ do { \ - frame->f_lasti = INSTR_OFFSET(); \ - next_instr++; \ + frame->prev_instr = next_instr++; \ OPCODE_EXE_INC(op); \ _py_stats.opcode_stats[lastopcode].pair_count[op]++; \ lastopcode = op; \ } while (0) #else -#define INSTRUCTION_START(op) frame->f_lasti = INSTR_OFFSET(); next_instr++ +#define INSTRUCTION_START(op) (frame->prev_instr = next_instr++) #endif #if USE_COMPUTED_GOTOS @@ -1646,9 +1643,9 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int consts = co->co_consts; \ first_instr = _PyCode_CODE(co); \ } \ - assert(frame->f_lasti >= -1); \ + assert(_PyInterpreterFrame_LASTI(frame) >= -1); \ /* Jump back to the last instruction executed... */ \ - next_instr = first_instr + frame->f_lasti + 1; \ + next_instr = frame->prev_instr + 1; \ stack_pointer = _PyFrame_GetStackPointer(frame); \ /* Set stackdepth to -1. \ Update when returning or calling trace function. \ @@ -2204,7 +2201,8 @@ handle_eval_breaker: new_frame->localsplus[i] = NULL; } _PyFrame_SetStackPointer(frame, stack_pointer); - frame->f_lasti += INLINE_CACHE_ENTRIES_BINARY_SUBSCR; + JUMPBY(INLINE_CACHE_ENTRIES_BINARY_SUBSCR); + frame->prev_instr = next_instr - 1; new_frame->previous = frame; frame = cframe.current_frame = new_frame; CALL_STAT_INC(inlined_py_calls); @@ -2605,7 +2603,7 @@ handle_eval_breaker: if (oparg) { PyObject *lasti = PEEK(oparg + 1); if (PyLong_Check(lasti)) { - frame->f_lasti = PyLong_AsLong(lasti); + frame->prev_instr = first_instr + PyLong_AsLong(lasti); assert(!_PyErr_Occurred(tstate)); } else { @@ -4603,7 +4601,8 @@ handle_eval_breaker: goto error; } _PyFrame_SetStackPointer(frame, stack_pointer); - frame->f_lasti += INLINE_CACHE_ENTRIES_CALL; + JUMPBY(INLINE_CACHE_ENTRIES_CALL); + frame->prev_instr = next_instr - 1; new_frame->previous = frame; cframe.current_frame = frame = new_frame; CALL_STAT_INC(inlined_py_calls); @@ -4708,7 +4707,8 @@ handle_eval_breaker: } STACK_SHRINK(2-is_meth); _PyFrame_SetStackPointer(frame, stack_pointer); - frame->f_lasti += INLINE_CACHE_ENTRIES_CALL; + JUMPBY(INLINE_CACHE_ENTRIES_CALL); + frame->prev_instr = next_instr - 1; new_frame->previous = frame; frame = cframe.current_frame = new_frame; goto start_frame; @@ -4748,7 +4748,8 @@ handle_eval_breaker: } STACK_SHRINK(2-is_meth); _PyFrame_SetStackPointer(frame, stack_pointer); - frame->f_lasti += INLINE_CACHE_ENTRIES_CALL; + JUMPBY(INLINE_CACHE_ENTRIES_CALL); + frame->prev_instr = next_instr - 1; new_frame->previous = frame; frame = cframe.current_frame = new_frame; goto start_frame; @@ -5435,8 +5436,8 @@ handle_eval_breaker: #endif { if (tstate->tracing == 0) { - int instr_prev = frame->f_lasti; - frame->f_lasti = INSTR_OFFSET(); + int instr_prev = _PyInterpreterFrame_LASTI(frame); + frame->prev_instr = next_instr; TRACING_NEXTOPARG(); switch(opcode) { case COPY_FREE_VARS: @@ -5474,7 +5475,7 @@ handle_eval_breaker: goto error; } /* Reload possibly changed frame fields */ - JUMPTO(frame->f_lasti); + next_instr = frame->prev_instr; stack_pointer = _PyFrame_GetStackPointer(frame); frame->stacktop = -1; @@ -5491,10 +5492,8 @@ handle_eval_breaker: #else default: #endif - fprintf(stderr, - "XXX lineno: %d, opcode: %d\n", - PyCode_Addr2Line(frame->f_code, frame->f_lasti*sizeof(_Py_CODEUNIT)), - opcode); + fprintf(stderr, "XXX lineno: %d, opcode: %d\n", + _PyInterpreterFrame_GetLine(frame), opcode); _PyErr_SetString(tstate, PyExc_SystemError, "unknown opcode"); goto error; @@ -5598,7 +5597,8 @@ exception_unwind: } PyObject *exc, *val, *tb; if (lasti) { - PyObject *lasti = PyLong_FromLong(frame->f_lasti); + int frame_lasti = _PyInterpreterFrame_LASTI(frame); + PyObject *lasti = PyLong_FromLong(frame_lasti); if (lasti == NULL) { goto exception_unwind; } @@ -6690,9 +6690,10 @@ call_trace(Py_tracefunc func, PyObject *obj, int old_what = tstate->tracing_what; tstate->tracing_what = what; PyThreadState_EnterTracing(tstate); - assert(frame->f_lasti >= 0); + assert(_PyInterpreterFrame_LASTI(frame) >= 0); initialize_trace_info(&tstate->trace_info, frame); - f->f_lineno = _PyCode_CheckLineNumber(frame->f_lasti*sizeof(_Py_CODEUNIT), &tstate->trace_info.bounds); + int addr = _PyInterpreterFrame_LASTI(frame) * sizeof(_Py_CODEUNIT); + f->f_lineno = _PyCode_CheckLineNumber(addr, &tstate->trace_info.bounds); result = func(obj, f, what, arg); f->f_lineno = 0; PyThreadState_LeaveTracing(tstate); @@ -6742,7 +6743,8 @@ maybe_call_line_trace(Py_tracefunc func, PyObject *obj, else { lastline = _PyCode_CheckLineNumber(instr_prev*sizeof(_Py_CODEUNIT), &tstate->trace_info.bounds); } - int line = _PyCode_CheckLineNumber(frame->f_lasti*sizeof(_Py_CODEUNIT), &tstate->trace_info.bounds); + int addr = _PyInterpreterFrame_LASTI(frame) * sizeof(_Py_CODEUNIT); + int line = _PyCode_CheckLineNumber(addr, &tstate->trace_info.bounds); PyFrameObject *f = _PyFrame_GetFrameObject(frame); if (f == NULL) { return -1; @@ -6750,10 +6752,10 @@ maybe_call_line_trace(Py_tracefunc func, PyObject *obj, if (line != -1 && f->f_trace_lines) { /* Trace backward edges (except in 'yield from') or if line number has changed */ int trace = line != lastline || - (frame->f_lasti < instr_prev && - // SEND has no quickened forms, so no need to use _PyOpcode_Deopt - // here: - _Py_OPCODE(_PyCode_CODE(frame->f_code)[frame->f_lasti]) != SEND); + (_PyInterpreterFrame_LASTI(frame) < instr_prev && + // SEND has no quickened forms, so no need to use _PyOpcode_Deopt + // here: + _Py_OPCODE(*frame->prev_instr) != SEND); if (trace) { result = call_trace(func, obj, tstate, frame, PyTrace_LINE, Py_None); } @@ -7685,7 +7687,7 @@ dtrace_function_entry(_PyInterpreterFrame *frame) PyCodeObject *code = frame->f_code; filename = PyUnicode_AsUTF8(code->co_filename); funcname = PyUnicode_AsUTF8(code->co_name); - lineno = PyCode_Addr2Line(frame->f_code, frame->f_lasti*sizeof(_Py_CODEUNIT)); + lineno = _PyInterpreterFrame_GetLine(frame); PyDTrace_FUNCTION_ENTRY(filename, funcname, lineno); } @@ -7700,7 +7702,7 @@ dtrace_function_return(_PyInterpreterFrame *frame) PyCodeObject *code = frame->f_code; filename = PyUnicode_AsUTF8(code->co_filename); funcname = PyUnicode_AsUTF8(code->co_name); - lineno = PyCode_Addr2Line(frame->f_code, frame->f_lasti*sizeof(_Py_CODEUNIT)); + lineno = _PyInterpreterFrame_GetLine(frame); PyDTrace_FUNCTION_RETURN(filename, funcname, lineno); } @@ -7718,11 +7720,12 @@ maybe_dtrace_line(_PyInterpreterFrame *frame, */ initialize_trace_info(trace_info, frame); int lastline = _PyCode_CheckLineNumber(instr_prev*sizeof(_Py_CODEUNIT), &trace_info->bounds); - int line = _PyCode_CheckLineNumber(frame->f_lasti*sizeof(_Py_CODEUNIT), &trace_info->bounds); + int addr = _PyInterpreterFrame_LASTI(frame) * sizeof(_Py_CODEUNIT); + int line = _PyCode_CheckLineNumber(addr, &trace_info->bounds); if (line != -1) { /* Trace backward edges or first instruction of a new line */ - if (frame->f_lasti < instr_prev || - (line != lastline && frame->f_lasti*sizeof(_Py_CODEUNIT) == (unsigned int)trace_info->bounds.ar_start)) + if (_PyInterpreterFrame_LASTI(frame) < instr_prev || + (line != lastline && addr == trace_info->bounds.ar_start)) { co_filename = PyUnicode_AsUTF8(frame->f_code->co_filename); if (!co_filename) { diff --git a/Python/frame.c b/Python/frame.c index 3396ed8d2ae..c2da123a2bb 100644 --- a/Python/frame.c +++ b/Python/frame.c @@ -127,3 +127,10 @@ _PyFrame_Push(PyThreadState *tstate, PyFunctionObject *func) _PyFrame_InitializeSpecials(new_frame, func, NULL, code->co_nlocalsplus); return new_frame; } + +int +_PyInterpreterFrame_GetLine(_PyInterpreterFrame *frame) +{ + int addr = _PyInterpreterFrame_LASTI(frame) * sizeof(_Py_CODEUNIT); + return PyCode_Addr2Line(frame->f_code, addr); +} diff --git a/Python/traceback.c b/Python/traceback.c index 488c1b17cf5..3ec0618af99 100644 --- a/Python/traceback.c +++ b/Python/traceback.c @@ -238,8 +238,8 @@ _PyTraceBack_FromFrame(PyObject *tb_next, PyFrameObject *frame) { assert(tb_next == NULL || PyTraceBack_Check(tb_next)); assert(frame != NULL); - - return tb_create_raw((PyTracebackObject *)tb_next, frame, frame->f_frame->f_lasti*sizeof(_Py_CODEUNIT), + int addr = _PyInterpreterFrame_LASTI(frame->f_frame) * sizeof(_Py_CODEUNIT); + return tb_create_raw((PyTracebackObject *)tb_next, frame, addr, PyFrame_GetLineNumber(frame)); } @@ -1180,7 +1180,7 @@ dump_frame(int fd, _PyInterpreterFrame *frame) PUTS(fd, "???"); } - int lineno = PyCode_Addr2Line(code, frame->f_lasti*sizeof(_Py_CODEUNIT)); + int lineno = _PyInterpreterFrame_GetLine(frame); PUTS(fd, ", line "); if (lineno >= 0) { _Py_DumpDecimal(fd, (size_t)lineno); diff --git a/Tools/gdb/libpython.py b/Tools/gdb/libpython.py index 00cdcca084e..4f7a8bca5fd 100755 --- a/Tools/gdb/libpython.py +++ b/Tools/gdb/libpython.py @@ -1015,7 +1015,10 @@ class PyFramePtr: return self._f_special("nlocalsplus", int_from_int) def _f_lasti(self): - return self._f_special("f_lasti", int_from_int) + codeunit_p = gdb.lookup_type("_Py_CODEUNIT").pointer() + prev_instr = self._gdbval["prev_instr"] + first_instr = self._f_code().field("co_code_adaptive").cast(codeunit_p) + return int(prev_instr - first_instr) def is_entry(self): return self._f_special("is_entry", bool)