-
-
Notifications
You must be signed in to change notification settings - Fork 33.1k
Description
Crash report
What happened?
The interpreter will abort when a JIT build patched with the diff below runs the code in the MRE. Please let me know whether this abort repros for you.
The necessary diff:
diff --git a/Include/internal/pycore_backoff.h b/Include/internal/pycore_backoff.h
index 454c8dde031..9e21c41421a 100644
--- a/Include/internal/pycore_backoff.h
+++ b/Include/internal/pycore_backoff.h
@@ -99,8 +99,8 @@ backoff_counter_triggers(_Py_BackoffCounter counter)
// Must be larger than ADAPTIVE_COOLDOWN_VALUE, otherwise when JIT code is
// invalidated we may construct a new trace before the bytecode has properly
// re-specialized:
-#define JUMP_BACKWARD_INITIAL_VALUE 4095
-#define JUMP_BACKWARD_INITIAL_BACKOFF 12
+#define JUMP_BACKWARD_INITIAL_VALUE 63
+#define JUMP_BACKWARD_INITIAL_BACKOFF 6
static inline _Py_BackoffCounter
initial_jump_backoff_counter(void)
{
@@ -112,8 +112,8 @@ initial_jump_backoff_counter(void)
* Must be larger than ADAPTIVE_COOLDOWN_VALUE,
* otherwise when a side exit warms up we may construct
* a new trace before the Tier 1 code has properly re-specialized. */
-#define SIDE_EXIT_INITIAL_VALUE 4095
-#define SIDE_EXIT_INITIAL_BACKOFF 12
+#define SIDE_EXIT_INITIAL_VALUE 63
+#define SIDE_EXIT_INITIAL_BACKOFF 6
static inline _Py_BackoffCounter
initial_temperature_backoff_counter(void)
diff --git a/Include/internal/pycore_optimizer.h b/Include/internal/pycore_optimizer.h
index 685c39dcd65..58a0ff31929 100644
--- a/Include/internal/pycore_optimizer.h
+++ b/Include/internal/pycore_optimizer.h
@@ -91,9 +91,9 @@ PyAPI_FUNC(void) _Py_Executors_InvalidateCold(PyInterpreterState *interp);
// Used as the threshold to trigger executor invalidation when
// trace_run_counter is greater than this value.
-#define JIT_CLEANUP_THRESHOLD 100000
+#define JIT_CLEANUP_THRESHOLD 10000
-#define TRACE_STACK_SIZE 5
+#define TRACE_STACK_SIZE 10
int _Py_uop_analyze_and_optimize(_PyInterpreterFrame *frame,
_PyUOpInstruction *trace, int trace_len, int curr_stackentries,
@@ -124,7 +124,7 @@ static inline uint16_t uop_get_error_target(const _PyUOpInstruction *inst)
}
// Holds locals, stack, locals, stack ... co_consts (in that order)
-#define MAX_ABSTRACT_INTERP_SIZE 4096
+#define MAX_ABSTRACT_INTERP_SIZE 8192
#define TY_ARENA_SIZE (UOP_MAX_TRACE_LENGTH * 5)
@@ -135,7 +135,7 @@ static inline uint16_t uop_get_error_target(const _PyUOpInstruction *inst)
// progress (and inserting a new ENTER_EXECUTOR instruction). In practice, this
// is the "maximum amount of polymorphism" that an isolated trace tree can
// handle before rejoining the rest of the program.
-#define MAX_CHAIN_DEPTH 4
+#define MAX_CHAIN_DEPTH 16
/* Symbols */
/* See explanation in optimizer_symbols.c */
The (rather long, sorry!) MRE:
from random import Random
fuzzer_rng = Random(238542)
def uop_harness_f1():
# Simple function call
def func_1(): pass
for _ in range(1500):
res = func_1()
# Recursive function calls
def deep_0(p): return p + 1
def deep_1(p): return deep_0(p) + 1
def deep_2(p): return deep_1(p) + 1
for i in range(3500):
deep_2(i)
# To decorate a function below, moving this block breaks repro
def decorator(func):
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
# Variables and random() calls
mv_8407_0 = 0
mv_8407_1 = 1
var_1, *var_2, var_3, var_4, var_5, var_6, var_7, var_8, var_9, var_10, var_11, var_12, var_13, var_14, var_15, var_16, var_17, var_18, var_19, var_20, var_21 = [None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None]
mv_8407_3 = mv_8407_4 = mv_8407_5 = mv_8407_6 = mv_8407_7 = mv_8407_8 = mv_8407_9 = 9
if fuzzer_rng.random() >= 0.1:
mv_8407_10 = 9
if fuzzer_rng.random() >= 0.1:
mv_8407_11 = 10
if fuzzer_rng.random() < 0.1:
mv_8407_12 = 12
if fuzzer_rng.random() < 0.1:
mv_8407_13 = 14
if fuzzer_rng.random() >= 0.1:
mv_8407_14 = 15
if fuzzer_rng.random() < 0.1:
mv_8407_15 = 16
if fuzzer_rng.random() < 0.1:
mv_8407_32 = 18
if fuzzer_rng.random() >= 0.1:
mv_8407_17 = 16
if fuzzer_rng.random() < 0.1:
mv_8407_18 = 19
if fuzzer_rng.random() >= 0.1:
for i_loop_4481 in range(730):
if fuzzer_rng.random() < 0.1:
mv_8407_19 = 21
if fuzzer_rng.random() >= 0.1:
for i_loop_7482 in range(96):
mv_8407_20 = 20
for i_loop_6618 in [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14] * 28:
for i_loop_8222 in [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]:
mv_8407_46 = 46
# Decorated function call
@decorator
def func_2(): pass
for _ in range(500):
res = func_2()
for x in range(4500): # Should abort around x == 122
print(x)
uop_harness_f1()
Backtrace for --enable-experimental-jit=interpreter
:
python: Python/optimizer.c:979: translate_bytecode_to_trace: Assertion `trace_length < max_length' failed.
Program received signal SIGABRT, Aborted.
#0 __pthread_kill_implementation (no_tid=0, signo=6, threadid=140737350580032) at ./nptl/pthread_kill.c:44
#1 __pthread_kill_internal (signo=6, threadid=140737350580032) at ./nptl/pthread_kill.c:78
#2 __GI___pthread_kill (threadid=140737350580032, signo=signo@entry=6) at ./nptl/pthread_kill.c:89
#3 0x00007ffff7ce0476 in __GI_raise (sig=sig@entry=6) at ../sysdeps/posix/raise.c:26
#4 0x00007ffff7cc67f3 in __GI_abort () at ./stdlib/abort.c:79
#5 0x00007ffff7cc671b in __assert_fail_base (fmt=0x7ffff7e7b130 "%s%s%s:%u: %s%sAssertion `%s' failed.\n%n",
assertion=0x555555aea5cd "trace_length < max_length", file=0x555555aea482 "Python/optimizer.c", line=979, function=<optimized out>)
at ./assert/assert.c:94
#6 0x00007ffff7cd7e96 in __GI___assert_fail (assertion=assertion@entry=0x555555aea5cd "trace_length < max_length",
file=file@entry=0x555555aea482 "Python/optimizer.c", line=line@entry=979,
function=function@entry=0x555555aed590 <__PRETTY_FUNCTION__.27> "translate_bytecode_to_trace") at ./assert/assert.c:103
#7 0x0000555555921cf6 in translate_bytecode_to_trace (frame=frame@entry=0x7ffff7fb0098, instr=<optimized out>, instr@entry=0x555555ef93ae,
trace=trace@entry=0x7ffff78b4000, buffer_size=buffer_size@entry=1200, dependencies=dependencies@entry=0x7fffffffd340,
progress_needed=progress_needed@entry=false) at Python/optimizer.c:979
#8 0x00005555559222ea in uop_optimize (frame=frame@entry=0x7ffff7fb0098, instr=instr@entry=0x555555ef93ae,
exec_ptr=exec_ptr@entry=0x7fffffffd498, curr_stackentries=2, progress_needed=progress_needed@entry=false) at Python/optimizer.c:1304
#9 0x000055555592290d in _PyOptimizer_Optimize (frame=frame@entry=0x7ffff7fb0098, start=start@entry=0x555555ef93ae,
executor_ptr=executor_ptr@entry=0x7fffffffd498, chain_depth=1) at Python/optimizer.c:135
#10 0x00005555558a2099 in _PyTier2Interpreter (current_executor=0x7ffff7aca690, frame=0x7ffff7fb0098, stack_pointer=<optimized out>,
tstate=0x555555d19fd8 <_PyRuntime+333720>) at Python/executor_cases.c.h:7485
#11 0x0000555555860d01 in _PyEval_EvalFrameDefault (tstate=tstate@entry=0x555555d19fd8 <_PyRuntime+333720>, frame=0x7ffff7fb0098,
frame@entry=0x7ffff7fb0020, throwflag=throwflag@entry=0) at Python/generated_cases.c.h:5496
#12 0x0000555555875638 in _PyEval_EvalFrame (throwflag=0, frame=0x7ffff7fb0020, tstate=0x555555d19fd8 <_PyRuntime+333720>)
at ./Include/internal/pycore_ceval.h:121
#13 _PyEval_Vector (tstate=tstate@entry=0x555555d19fd8 <_PyRuntime+333720>, func=func@entry=0x7ffff7a4aa50, locals=locals@entry=0x7ffff7a476b0,
args=args@entry=0x0, argcount=argcount@entry=0, kwnames=kwnames@entry=0x0) at Python/ceval.c:1990
#14 0x0000555555875737 in PyEval_EvalCode (co=co@entry=0x7ffff7c19a80, globals=globals@entry=0x7ffff7a476b0, locals=locals@entry=0x7ffff7a476b0)
at Python/ceval.c:873
#15 0x000055555594923b in run_eval_code_obj (tstate=tstate@entry=0x555555d19fd8 <_PyRuntime+333720>, co=co@entry=0x7ffff7c19a80,
globals=globals@entry=0x7ffff7a476b0, locals=locals@entry=0x7ffff7a476b0) at Python/pythonrun.c:1365
#16 0x000055555594a453 in run_mod (mod=mod@entry=0x555555ef3098, filename=filename@entry=0x7ffff7ab1a20, globals=globals@entry=0x7ffff7a476b0,
locals=locals@entry=0x7ffff7a476b0, flags=flags@entry=0x7fffffffdb18, arena=arena@entry=0x7ffff7ab7d00, interactive_src=0x0,
generate_new_source=0) at Python/pythonrun.c:1459
Here's the output of running with PYTHON_LLTRACE=4
:
lltrace_trace_length.txt
The output above is cut short, here are the last lines shown in console, including No room for _CHECK_VALIDITY (need 2, got -2)
:
493: NOT_TAKEN(0)
700 ADD_TO_TRACE: _CHECK_VALIDITY (0, target=493, operand0=0, operand1=0)
701 ADD_TO_TRACE: _SET_IP (0, target=493, operand0=0x564abd083a3a, operand1=0)
702 ADD_TO_TRACE: _NOP (0, target=493, operand0=0, operand1=0)
494: JUMP_BACKWARD_JIT(30)
703 ADD_TO_TRACE: _CHECK_VALIDITY (0, target=494, operand0=0, operand1=0)
704 ADD_TO_TRACE: _SET_IP (0, target=494, operand0=0x564abd083a3c, operand1=0)
705 ADD_TO_TRACE: _CHECK_PERIODIC (0, target=494, operand0=0, operand1=0, error_target=0)
466: FOR_ITER_RANGE(32)
No room for _CHECK_VALIDITY (need 2, got -2)
python: Python/optimizer.c:979: translate_bytecode_to_trace: Assertion `trace_length < max_length' failed.
Aborted (core dumped)
Here's the output of running with PYTHON_OPT_DEBUG=4
:
opt_debug_trace_length.txt
Interestingly, the --enable-experimental-jit=interpreter
build segfaults when running the same code under PYTHON_LLTRACE=4
, in what seems to be a completely different crash. I'll soon report this other crash so we can discuss whether it's a real bug or expected.
Found using lafleur.
CPython versions tested on:
CPython main branch
Operating systems tested on:
Linux
Output from running 'python -VV' on the command line:
Python 3.15.0a0 (heads/main-dirty:69c6b438e84, Sep 20 2025, 17:15:31) [GCC 11.4.0]