Skip to content

Commit 29faba9

Browse files
committed
gh-145064: fix JIT tracing for non-function frames
1 parent 3a2a686 commit 29faba9

File tree

4 files changed

+30
-4
lines changed

4 files changed

+30
-4
lines changed

Lib/test/test_capi/test_opt.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4065,6 +4065,24 @@ def hot_loop():
40654065
self.assertNotIn('_PyJit_TryInitializeTracing', stderr,
40664066
f"JIT tracer memory leak detected:\n{stderr}")
40674067

4068+
def test_145064(self):
4069+
# https://github.com/python/cpython/issues/145064
4070+
result = script_helper.run_python_until_end('-c', textwrap.dedent(f"""
4071+
SRC = '''
4072+
class A:
4073+
def __init__(self, x):
4074+
self.x = x
4075+
4076+
for i in range(8):
4077+
A(i)
4078+
'''
4079+
4080+
for _ in range({TIER2_THRESHOLD * 6}):
4081+
ns = {{}}
4082+
exec(SRC, ns, ns)
4083+
"""), PYTHON_JIT="1", PYTHON_JIT_STRESS="1")
4084+
self.assertEqual(result[0].rc, 0, result)
4085+
40684086
def global_identity(x):
40694087
return x
40704088

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix a JIT crash in ``_Py_uop_frame_new()`` when tracing starts from non-function frames (for example ``_Py_InitCleanup`` shims) by using the traced code object directly and only storing function metadata when available.

Python/optimizer.c

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ _PyOptimizer_Optimize(
146146
assert(!interp->compiling);
147147
assert(_tstate->jit_tracer_state->initial_state.stack_depth >= 0);
148148
#ifndef Py_GIL_DISABLED
149-
assert(_tstate->jit_tracer_state->initial_state.func != NULL);
149+
assert(_tstate->jit_tracer_state->initial_state.code != NULL);
150150
interp->compiling = true;
151151
// The first executor in a chain and the MAX_CHAIN_DEPTH'th executor *must*
152152
// make progress in order to avoid infinite loops or excessively-long
@@ -1020,7 +1020,10 @@ _PyJit_TryInitializeTracing(
10201020
tracer->initial_state.start_instr = start_instr;
10211021
tracer->initial_state.close_loop_instr = close_loop_instr;
10221022
tracer->initial_state.code = (PyCodeObject *)Py_NewRef(code);
1023-
tracer->initial_state.func = (PyFunctionObject *)Py_NewRef(func);
1023+
// Tracing can start in shim frames (e.g. _Py_InitCleanup), where
1024+
// frame->f_funcobj is not a PyFunctionObject.
1025+
tracer->initial_state.func = PyFunction_Check(func) ?
1026+
(PyFunctionObject *)Py_NewRef(func) : NULL;
10241027
tracer->initial_state.executor = (_PyExecutorObject *)Py_XNewRef(current_executor);
10251028
tracer->initial_state.exit = exit;
10261029
tracer->initial_state.stack_depth = (int)(stack_pointer - _PyFrame_Stackbase(frame));

Python/optimizer_analysis.c

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -458,6 +458,8 @@ optimize_uops(
458458
{
459459
assert(!PyErr_Occurred());
460460
assert(tstate->jit_tracer_state != NULL);
461+
PyCodeObject *co = tstate->jit_tracer_state->initial_state.code;
462+
assert(co != NULL);
461463
PyFunctionObject *func = tstate->jit_tracer_state->initial_state.func;
462464

463465
JitOptContext *ctx = &tstate->jit_tracer_state->opt_context;
@@ -473,11 +475,13 @@ optimize_uops(
473475
}
474476

475477
_Py_uop_abstractcontext_init(ctx, dependencies);
476-
_Py_UOpsAbstractFrame *frame = _Py_uop_frame_new(ctx, (PyCodeObject *)func->func_code, NULL, 0);
478+
_Py_UOpsAbstractFrame *frame = _Py_uop_frame_new(ctx, co, NULL, 0);
477479
if (frame == NULL) {
478480
return 0;
479481
}
480-
frame->func = func;
482+
if (func != NULL) {
483+
frame->func = func;
484+
}
481485
ctx->curr_frame_depth++;
482486
ctx->frame = frame;
483487
_Py_uop_sym_set_stack_depth(ctx, curr_stacklen, frame->stack_pointer);

0 commit comments

Comments
 (0)