From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com> Date: Mon, 27 Oct 2025 14:22:05 +0000 (+0000) Subject: Stop recursive traces X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=bdd2123ea3139945d115a63b29e51790e291403d;p=thirdparty%2FPython%2Fcpython.git Stop recursive traces --- diff --git a/Include/internal/pycore_opcode_metadata.h b/Include/internal/pycore_opcode_metadata.h index 2bb915ad62b1..f72c0412bc6f 100644 --- a/Include/internal/pycore_opcode_metadata.h +++ b/Include/internal/pycore_opcode_metadata.h @@ -1146,7 +1146,7 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[267] = { [END_ASYNC_FOR] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_JUMP_FLAG | HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG }, [END_FOR] = { true, INSTR_FMT_IX, HAS_ESCAPES_FLAG | HAS_NO_SAVE_IP_FLAG }, [END_SEND] = { true, INSTR_FMT_IX, HAS_ESCAPES_FLAG | HAS_PURE_FLAG }, - [ENTER_EXECUTOR] = { true, INSTR_FMT_IB, HAS_ARG_FLAG }, + [ENTER_EXECUTOR] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [EXIT_INIT_CHECK] = { true, INSTR_FMT_IX, HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG }, [EXTENDED_ARG] = { true, INSTR_FMT_IB, HAS_ARG_FLAG }, [FORMAT_SIMPLE] = { true, INSTR_FMT_IX, HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, diff --git a/Include/internal/pycore_optimizer.h b/Include/internal/pycore_optimizer.h index 7c542e322ecb..ff829adbc2ee 100644 --- a/Include/internal/pycore_optimizer.h +++ b/Include/internal/pycore_optimizer.h @@ -361,8 +361,8 @@ extern void _Py_ClearExecutorDeletionList(PyInterpreterState *interp); int _PyJit_translate_single_bytecode_to_trace(PyThreadState *tstate, _PyInterpreterFrame *frame, _Py_CODEUNIT *next_instr); -void -_PyJit_InitializeTracing(PyThreadState *tstate, _PyInterpreterFrame *frame, +int +_PyJit_TryInitializeTracing(PyThreadState *tstate, _PyInterpreterFrame *frame, _Py_CODEUNIT *curr_instr, _Py_CODEUNIT *insert_exec_instr, _Py_CODEUNIT *close_loop_instr, int curr_stackdepth, int chain_depth, _PyExitData *exit, int oparg); diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 0328cd3ef969..9cd32bf63e75 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -2972,8 +2972,10 @@ dummy_func( oparg >>= 8; insert_exec_at--; } - _PyJit_InitializeTracing(tstate, frame, this_instr, insert_exec_at, next_instr, STACK_LEVEL(), 0, NULL, oparg); - ENTER_TRACING(); + int succ = _PyJit_TryInitializeTracing(tstate, frame, this_instr, insert_exec_at, next_instr, STACK_LEVEL(), 0, NULL, oparg); + if (succ) { + ENTER_TRACING(); + } } } else { @@ -3019,6 +3021,15 @@ dummy_func( tier1 inst(ENTER_EXECUTOR, (--)) { #ifdef _Py_TIER2 + // We want to end any current trace here, before we possibly need + // to start tracing new ones due to recursive traces in any inner C functions + // in tier2 code. + if (IS_JIT_TRACING()) { + _PyJit_translate_single_bytecode_to_trace(tstate, frame, next_instr); + LEAVE_TRACING(); + int err = bail_tracing_and_jit(tstate, frame); + ERROR_IF(err < 0); + } PyCodeObject *code = _PyFrame_GetCode(frame); _PyExecutorObject *executor = code->co_executors->executors[oparg & 255]; assert(executor->vm_data.index == INSTR_OFFSET() - 1); @@ -5428,9 +5439,12 @@ dummy_func( // Note: it's safe to use target->op.arg here instead of the oparg given by EXTENDED_ARG. // The invariant in the optimizer is the deopt target always points back to the first EXTENDED_ARG. // So setting it to anything else is wrong. - _PyJit_InitializeTracing(tstate, frame, target, target, target, STACK_LEVEL(), chain_depth, exit, target->op.arg); + int succ = _PyJit_TryInitializeTracing(tstate, frame, target, target, target, STACK_LEVEL(), chain_depth, exit, target->op.arg); exit->temperature = initial_temperature_backoff_counter(); - GOTO_TIER_ONE_CONTINUE_TRACING(target); + if (succ) { + GOTO_TIER_ONE_CONTINUE_TRACING(target); + } + GOTO_TIER_ONE(target); } assert(tstate->jit_exit == exit); exit->executor = executor; @@ -5636,7 +5650,9 @@ dummy_func( } tstate->interp->jit_state.specialize_counter = 0; PyCodeObject *prev_code = (PyCodeObject *)Py_NewRef(_PyFrame_GetCode(frame)); - Py_SETREF(tstate->interp->jit_state.prev_instr_code, prev_code); + if (tstate->interp->jit_state.prev_instr_code != prev_code) { + Py_SETREF(tstate->interp->jit_state.prev_instr_code, prev_code); + } tstate->interp->jit_state.prev_instr_frame = frame; tstate->interp->jit_state.prev_instr_oparg = oparg; diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index fafaa7bd12bb..b81b0300da05 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -7485,9 +7485,12 @@ _PyExecutorObject *previous_executor = _PyExecutor_FromExit(exit); assert(tstate->current_executor == (PyObject *)previous_executor); int chain_depth = previous_executor->vm_data.chain_depth + 1; - _PyJit_InitializeTracing(tstate, frame, target, target, target, STACK_LEVEL(), chain_depth, exit, target->op.arg); + int succ = _PyJit_TryInitializeTracing(tstate, frame, target, target, target, STACK_LEVEL(), chain_depth, exit, target->op.arg); exit->temperature = initial_temperature_backoff_counter(); - GOTO_TIER_ONE_CONTINUE_TRACING(target); + if (succ) { + GOTO_TIER_ONE_CONTINUE_TRACING(target); + } + GOTO_TIER_ONE(target); } assert(tstate->jit_exit == exit); exit->executor = executor; diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index d9fea0e4ae57..e9d5a611ccd6 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -5491,6 +5491,19 @@ INSTRUCTION_STATS(ENTER_EXECUTOR); opcode = ENTER_EXECUTOR; #ifdef _Py_TIER2 + + if (IS_JIT_TRACING()) { + _PyFrame_SetStackPointer(frame, stack_pointer); + _PyJit_translate_single_bytecode_to_trace(tstate, frame, next_instr); + stack_pointer = _PyFrame_GetStackPointer(frame); + LEAVE_TRACING(); + _PyFrame_SetStackPointer(frame, stack_pointer); + int err = bail_tracing_and_jit(tstate, frame); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (err < 0) { + JUMP_TO_LABEL(error); + } + } PyCodeObject *code = _PyFrame_GetCode(frame); _PyExecutorObject *executor = code->co_executors->executors[oparg & 255]; assert(executor->vm_data.index == INSTR_OFFSET() - 1); @@ -7690,8 +7703,10 @@ oparg >>= 8; insert_exec_at--; } - _PyJit_InitializeTracing(tstate, frame, this_instr, insert_exec_at, next_instr, STACK_LEVEL(), 0, NULL, oparg); - ENTER_TRACING(); + int succ = _PyJit_TryInitializeTracing(tstate, frame, this_instr, insert_exec_at, next_instr, STACK_LEVEL(), 0, NULL, oparg); + if (succ) { + ENTER_TRACING(); + } } } else { @@ -12342,9 +12357,11 @@ JUMP_TO_LABEL(error); } tstate->interp->jit_state.specialize_counter = 0; PyCodeObject *prev_code = (PyCodeObject *)Py_NewRef(_PyFrame_GetCode(frame)); - _PyFrame_SetStackPointer(frame, stack_pointer); - Py_SETREF(tstate->interp->jit_state.prev_instr_code, prev_code); - stack_pointer = _PyFrame_GetStackPointer(frame); + if (tstate->interp->jit_state.prev_instr_code != prev_code) { + _PyFrame_SetStackPointer(frame, stack_pointer); + Py_SETREF(tstate->interp->jit_state.prev_instr_code, prev_code); + stack_pointer = _PyFrame_GetStackPointer(frame); + } tstate->interp->jit_state.prev_instr_frame = frame; tstate->interp->jit_state.prev_instr_oparg = oparg; tstate->interp->jit_state.prev_instr_stacklevel = STACK_LEVEL(); diff --git a/Python/optimizer.c b/Python/optimizer.c index da9247cd139c..05ab91fd5435 100644 --- a/Python/optimizer.c +++ b/Python/optimizer.c @@ -619,6 +619,11 @@ _PyJit_translate_single_bytecode_to_trace( } if (opcode == ENTER_EXECUTOR) { + int is_first_instr = tstate->interp->jit_state.close_loop_instr == next_instr || tstate->interp->jit_state.insert_exec_instr == next_instr; + if (is_first_instr && tstate->interp->jit_state.code_curr_size > 5) { + ADD_TO_TRACE(_JUMP_TO_TOP, 0, 0, 0); + goto done; + } goto full; } @@ -925,9 +930,15 @@ full: return 0; } -void -_PyJit_InitializeTracing(PyThreadState *tstate, _PyInterpreterFrame *frame, _Py_CODEUNIT *curr_instr, _Py_CODEUNIT *insert_exec_instr, _Py_CODEUNIT *close_loop_instr, int curr_stackdepth, int chain_depth, _PyExitData *exit, int oparg) +// Returns 0 for do not enter tracing, 1 on enter tracing. +int +_PyJit_TryInitializeTracing(PyThreadState *tstate, _PyInterpreterFrame *frame, _Py_CODEUNIT *curr_instr, _Py_CODEUNIT *insert_exec_instr, _Py_CODEUNIT *close_loop_instr, int curr_stackdepth, int chain_depth, _PyExitData *exit, int oparg) { + // A recursive trace. + // Don't trace into the inner call because it will stomp on the previous trace, causing endless retraces. + if (tstate->interp->jit_state.code_curr_size > 2) { + return 0; + } PyCodeObject *code = _PyFrame_GetCode(frame); #ifdef Py_DEBUG char *python_lltrace = Py_GETENV("PYTHON_LLTRACE"); @@ -965,6 +976,7 @@ _PyJit_InitializeTracing(PyThreadState *tstate, _PyInterpreterFrame *frame, _Py_ tstate->interp->jit_state.dynamic_jump_taken = false; tstate->interp->jit_state.prev_instr_is_super = false; _Py_BloomFilter_Init(&tstate->interp->jit_state.dependencies); + return 1; } void diff --git a/Tools/cases_generator/analyzer.py b/Tools/cases_generator/analyzer.py index b206a0a4b985..b66e2f0ab0d2 100644 --- a/Tools/cases_generator/analyzer.py +++ b/Tools/cases_generator/analyzer.py @@ -699,7 +699,7 @@ NON_ESCAPING_FUNCTIONS = ( "PyStackRef_Unwrap", "_PyLong_CheckExactAndCompact", "_PyExecutor_FromExit", - "_PyJit_InitializeTracing", + "_PyJit_TryInitializeTracing", )