Skip to content

Commit 3b4f5ed

Browse files
authored
[3.11] GH-93516: Backport GH-93769 (GH-94231)
* Store offset of first traceable instruction to avoid having to recompute it all the time when tracing.
1 parent 50a2e36 commit 3b4f5ed

File tree

7 files changed

+218
-210
lines changed

7 files changed

+218
-210
lines changed

Doc/data/python3.11.abi

+160-157
Large diffs are not rendered by default.

Include/cpython/code.h

+1
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ typedef uint16_t _Py_CODEUNIT;
9191
PyObject *co_weakreflist; /* to support weakrefs to code objects */ \
9292
PyObject *_co_code; /* cached co_code object/attribute */ \
9393
char *_co_linearray; /* array of line offsets */ \
94+
int _co_firsttraceable; /* index of first traceable instruction */ \
9495
/* Scratch space for extra data relating to the code object. \
9596
Type is a void* to keep the format private in codeobject.c to force \
9697
people to go through the proper APIs. */ \

Lib/opcode.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ def jabs_op(name, op):
175175
hasfree.append(148)
176176
def_op('COPY_FREE_VARS', 149)
177177

178-
def_op('RESUME', 151)
178+
def_op('RESUME', 151) # This must be kept in sync with deepfreeze.py
179179
def_op('MATCH_CLASS', 152)
180180

181181
def_op('FORMAT_VALUE', 155)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Store offset of first traceable instruction in code object to avoid having
2+
to recompute it for each instruction when tracing.

Objects/codeobject.c

+6
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,12 @@ init_code(PyCodeObject *co, struct _PyCodeConstructor *con)
341341
co->_co_linearray = NULL;
342342
memcpy(_PyCode_CODE(co), PyBytes_AS_STRING(con->code),
343343
PyBytes_GET_SIZE(con->code));
344+
int entry_point = 0;
345+
while (entry_point < Py_SIZE(co) &&
346+
_Py_OPCODE(_PyCode_CODE(co)[entry_point]) != RESUME) {
347+
entry_point++;
348+
}
349+
co->_co_firsttraceable = entry_point;
344350
}
345351

346352
static int

Python/ceval.c

+41-52
Original file line numberDiff line numberDiff line change
@@ -5608,57 +5608,51 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
56085608
case DO_TRACING:
56095609
#endif
56105610
{
5611-
if (tstate->tracing == 0) {
5611+
if (tstate->tracing == 0 &&
5612+
INSTR_OFFSET() >= frame->f_code->_co_firsttraceable
5613+
) {
5614+
assert(
5615+
_PyOpcode_Deopt[first_instr[frame->f_code->_co_firsttraceable]]
5616+
== RESUME
5617+
);
56125618
int instr_prev = _PyInterpreterFrame_LASTI(frame);
56135619
frame->prev_instr = next_instr;
56145620
TRACING_NEXTOPARG();
5615-
switch(opcode) {
5616-
case COPY_FREE_VARS:
5617-
case MAKE_CELL:
5618-
case RETURN_GENERATOR:
5619-
/* Frame not fully initialized */
5620-
break;
5621-
case RESUME:
5622-
if (oparg < 2) {
5623-
CHECK_EVAL_BREAKER();
5624-
}
5625-
/* Call tracing */
5626-
TRACE_FUNCTION_ENTRY();
5627-
DTRACE_FUNCTION_ENTRY();
5628-
break;
5629-
case POP_TOP:
5630-
if (_Py_OPCODE(next_instr[-1]) == RETURN_GENERATOR) {
5631-
/* Frame not fully initialized */
5632-
break;
5633-
}
5634-
/* fall through */
5635-
default:
5636-
/* line-by-line tracing support */
5637-
if (PyDTrace_LINE_ENABLED()) {
5638-
maybe_dtrace_line(frame, &tstate->trace_info, instr_prev);
5639-
}
5640-
5641-
if (cframe.use_tracing &&
5642-
tstate->c_tracefunc != NULL && !tstate->tracing) {
5643-
int err;
5644-
/* see maybe_call_line_trace()
5645-
for expository comments */
5646-
_PyFrame_SetStackPointer(frame, stack_pointer);
5647-
5648-
err = maybe_call_line_trace(tstate->c_tracefunc,
5649-
tstate->c_traceobj,
5650-
tstate, frame, instr_prev);
5651-
if (err) {
5652-
/* trace function raised an exception */
5653-
next_instr++;
5654-
goto error;
5655-
}
5656-
/* Reload possibly changed frame fields */
5657-
next_instr = frame->prev_instr;
5621+
if (opcode == RESUME) {
5622+
if (oparg < 2) {
5623+
CHECK_EVAL_BREAKER();
5624+
}
5625+
/* Call tracing */
5626+
TRACE_FUNCTION_ENTRY();
5627+
DTRACE_FUNCTION_ENTRY();
5628+
}
5629+
else {
5630+
/* line-by-line tracing support */
5631+
if (PyDTrace_LINE_ENABLED()) {
5632+
maybe_dtrace_line(frame, &tstate->trace_info, instr_prev);
5633+
}
56585634

5659-
stack_pointer = _PyFrame_GetStackPointer(frame);
5660-
frame->stacktop = -1;
5635+
if (cframe.use_tracing &&
5636+
tstate->c_tracefunc != NULL && !tstate->tracing) {
5637+
int err;
5638+
/* see maybe_call_line_trace()
5639+
for expository comments */
5640+
_PyFrame_SetStackPointer(frame, stack_pointer);
5641+
5642+
err = maybe_call_line_trace(tstate->c_tracefunc,
5643+
tstate->c_traceobj,
5644+
tstate, frame, instr_prev);
5645+
if (err) {
5646+
/* trace function raised an exception */
5647+
next_instr++;
5648+
goto error;
56615649
}
5650+
/* Reload possibly changed frame fields */
5651+
next_instr = frame->prev_instr;
5652+
5653+
stack_pointer = _PyFrame_GetStackPointer(frame);
5654+
frame->stacktop = -1;
5655+
}
56625656
}
56635657
}
56645658
TRACING_NEXTOPARG();
@@ -6896,13 +6890,8 @@ maybe_call_line_trace(Py_tracefunc func, PyObject *obj,
68966890
if (_PyCode_InitLineArray(frame->f_code)) {
68976891
return -1;
68986892
}
6899-
int entry_point = 0;
6900-
_Py_CODEUNIT *code = _PyCode_CODE(frame->f_code);
6901-
while (_PyOpcode_Deopt[_Py_OPCODE(code[entry_point])] != RESUME) {
6902-
entry_point++;
6903-
}
69046893
int lastline;
6905-
if (instr_prev <= entry_point) {
6894+
if (instr_prev <= frame->f_code->_co_firsttraceable) {
69066895
lastline = -1;
69076896
}
69086897
else {

Tools/scripts/deepfreeze.py

+7
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@
2222
verbose = False
2323
identifiers, strings = get_identifiers_and_strings()
2424

25+
# This must be kept in sync with opcode.py
26+
RESUME = 151
27+
2528
def isprintable(b: bytes) -> bool:
2629
return all(0x20 <= c < 0x7f for c in b)
2730

@@ -284,6 +287,10 @@ def generate_code(self, name: str, code: types.CodeType) -> str:
284287
self.write(f"._co_code = NULL,")
285288
self.write("._co_linearray = NULL,")
286289
self.write(f".co_code_adaptive = {co_code_adaptive},")
290+
for i, op in enumerate(code.co_code[::2]):
291+
if op == RESUME:
292+
self.write(f"._co_firsttraceable = {i},")
293+
break
287294
name_as_code = f"(PyCodeObject *)&{name}"
288295
self.deallocs.append(f"_PyStaticCode_Dealloc({name_as_code});")
289296
self.interns.append(f"_PyStaticCode_InternStrings({name_as_code})")

0 commit comments

Comments
 (0)