int prev_instr_stacklevel;
int specialize_counter;
_PyUOpInstruction *code_buffer;
- _Py_CODEUNIT *insert_exec_instr;
+ _Py_CODEUNIT *start_instr;
_Py_CODEUNIT *close_loop_instr;
+ _Py_CODEUNIT *jump_backward_instr;
PyCodeObject *initial_code; // Strong
PyFunctionObject *initial_func; // Strong
_Py_CODEUNIT *prev_instr;
PyCodeObject *prev_instr_code; // Strong
struct _PyExitData *prev_exit;
- struct _PyExecutorObject *prev_executor; // Strong
- _Py_CODEUNIT *jump_backward_instr;
_PyInterpreterFrame *prev_instr_frame;
_PyBloomFilter dependencies;
} _PyJitTracerState;
typedef struct _PyExitData {
uint32_t target;
- uint16_t index;
- char is_dynamic:4;
- char is_control_flow:4;
+ uint16_t index:14;
+ uint16_t is_dynamic:1;
+ uint16_t is_control_flow:1;
_Py_BackoffCounter temperature;
struct _PyExecutorObject *executor;
} _PyExitData;
int opcode = uop->opcode;
return (
opcode == _EXIT_TRACE ||
+ opcode == _DEOPT ||
opcode == _JUMP_TO_TOP ||
opcode == _DYNAMIC_EXIT
);
int
_PyJit_TryInitializeTracing(PyThreadState *tstate, _PyInterpreterFrame *frame,
- _Py_CODEUNIT *curr_instr, _Py_CODEUNIT *insert_exec_instr,
+ _Py_CODEUNIT *curr_instr, _Py_CODEUNIT *start_instr,
_Py_CODEUNIT *close_loop_instr, int curr_stackdepth, int chain_depth, _PyExitData *exit,
- _PyExecutorObject *prev_exec, int oparg);
+ int oparg);
void _PyJit_FinalizeTracing(PyThreadState *tstate);
PyObject *result = PyStackRef_AsPyObjectSteal(retval);
if (IS_JIT_TRACING()) {
#if _Py_TIER2
- _PyJit_translate_single_bytecode_to_trace(tstate, frame, next_instr);
+ _PyJit_translate_single_bytecode_to_trace(tstate, frame, NULL);
LEAVE_TRACING();
int err = bail_tracing_and_jit(tstate, frame);
if (err < 0) {
oparg >>= 8;
insert_exec_at--;
}
- int succ = _PyJit_TryInitializeTracing(tstate, frame, this_instr, insert_exec_at, next_instr, STACK_LEVEL(), 0, NULL, NULL, oparg);
+ int succ = _PyJit_TryInitializeTracing(tstate, frame, this_instr, insert_exec_at, next_instr, STACK_LEVEL(), 0, NULL, oparg);
if (succ) {
ENTER_TRACING();
}
tier2 op(_EXIT_TRACE, (exit_p/4 --)) {
_PyExitData *exit = (_PyExitData *)exit_p;
#if defined(Py_DEBUG) && !defined(_Py_JIT)
- const _Py_CODEUNIT *target = ((frame->owner >= FRAME_OWNED_BY_INTERPRETER)
+ const _Py_CODEUNIT *target = ((frame->owner == FRAME_OWNED_BY_INTERPRETER)
? _Py_INTERPRETER_TRAMPOLINE_INSTRUCTIONS_PTR : _PyFrame_GetBytecode(frame))
+ exit->target;
OPT_HIST(trace_uop_execution_counter, trace_run_length_hist);
TIER2_TO_TIER2(exit->executor);
}
- // Note: this is different than _COLD_EXIT/_EXIT_TRACE, as it may lead to multiple executors
- // from a single exit!
tier2 op(_DYNAMIC_EXIT, (exit_p/4 --)) {
#if defined(Py_DEBUG) && !defined(_Py_JIT)
_PyExitData *exit = (_PyExitData *)exit_p;
}
tier2 op(_DEOPT, (--)) {
- GOTO_TIER_ONE(_PyFrame_GetBytecode(frame) + CURRENT_TARGET());
+ GOTO_TIER_ONE((frame->owner == FRAME_OWNED_BY_INTERPRETER)
+ ? _Py_INTERPRETER_TRAMPOLINE_INSTRUCTIONS_PTR : _PyFrame_GetBytecode(frame) + CURRENT_TARGET());
}
tier2 op(_HANDLE_PENDING_AND_DEOPT, (--)) {
tier2 op(_COLD_EXIT, ( -- )) {
_PyExitData *exit = tstate->jit_exit;
assert(exit != NULL);
- _Py_CODEUNIT *target = ((frame->owner >= FRAME_OWNED_BY_INTERPRETER)
- ? (_Py_CODEUNIT *)_Py_INTERPRETER_TRAMPOLINE_INSTRUCTIONS_PTR : _PyFrame_GetBytecode(frame)) + exit->target;
+ assert(frame->owner < FRAME_OWNED_BY_INTERPRETER);
+ _Py_CODEUNIT *target = _PyFrame_GetBytecode(frame) + exit->target;
_Py_BackoffCounter temperature = exit->temperature;
_PyExecutorObject *executor;
if (target->op.code == ENTER_EXECUTOR) {
TIER2_TO_TIER2(exit->executor);
}
else {
- if (frame->owner >= FRAME_OWNED_BY_INTERPRETER) {
- GOTO_TIER_ONE(target);
- }
if (!backoff_counter_triggers(temperature)) {
exit->temperature = advance_backoff_counter(temperature);
GOTO_TIER_ONE(target);
// 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.
- int succ = _PyJit_TryInitializeTracing(tstate, frame, target, target, target, STACK_LEVEL(), chain_depth, exit, previous_executor, target->op.arg);
+ int succ = _PyJit_TryInitializeTracing(tstate, frame, target, target, target, STACK_LEVEL(), chain_depth, exit, target->op.arg);
exit->temperature = restart_backoff_counter(exit->temperature);
if (succ) {
GOTO_TIER_ONE_CONTINUE_TRACING(target);
}
tier2 op(_COLD_DYNAMIC_EXIT, ( -- )) {
+ // TODO (gh-139109): This should be similar to _COLD_EXIT in the future.
_Py_CODEUNIT *target = frame->instr_ptr;
GOTO_TIER_ONE(target);
}
_Py_CODEUNIT *next_instr;
_PyStackRef *stack_pointer;
entry.stack[0] = PyStackRef_NULL;
- entry.frame.f_funcobj = PyStackRef_NULL;
-#if defined(Py_DEBUG)
+#ifdef Py_STACKREF_DEBUG
+ entry.frame.f_funcobj = PyStackRef_None;
+#elif defined(Py_DEBUG)
/* Set these to invalid but identifiable values for debugging. */
+ entry.frame.f_funcobj = (_PyStackRef){.bits = 0xaaa0};
entry.frame.f_locals = (PyObject*)0xaaa1;
entry.frame.frame_obj = (PyFrameObject*)0xaaa2;
entry.frame.f_globals = (PyObject*)0xaaa3;
PyObject *exit_p = (PyObject *)CURRENT_OPERAND0();
_PyExitData *exit = (_PyExitData *)exit_p;
#if defined(Py_DEBUG) && !defined(_Py_JIT)
- const _Py_CODEUNIT *target = ((frame->owner >= FRAME_OWNED_BY_INTERPRETER)
+ const _Py_CODEUNIT *target = ((frame->owner == FRAME_OWNED_BY_INTERPRETER)
? _Py_INTERPRETER_TRAMPOLINE_INSTRUCTIONS_PTR : _PyFrame_GetBytecode(frame))
+ exit->target;
OPT_HIST(trace_uop_execution_counter, trace_run_length_hist);
}
case _DEOPT: {
- GOTO_TIER_ONE(_PyFrame_GetBytecode(frame) + CURRENT_TARGET());
+ GOTO_TIER_ONE((frame->owner == FRAME_OWNED_BY_INTERPRETER)
+ ? _Py_INTERPRETER_TRAMPOLINE_INSTRUCTIONS_PTR : _PyFrame_GetBytecode(frame) + CURRENT_TARGET());
break;
}
case _COLD_EXIT: {
_PyExitData *exit = tstate->jit_exit;
assert(exit != NULL);
- _Py_CODEUNIT *target = ((frame->owner >= FRAME_OWNED_BY_INTERPRETER)
- ? (_Py_CODEUNIT *)_Py_INTERPRETER_TRAMPOLINE_INSTRUCTIONS_PTR : _PyFrame_GetBytecode(frame)) + exit->target;
+ assert(frame->owner < FRAME_OWNED_BY_INTERPRETER);
+ _Py_CODEUNIT *target = _PyFrame_GetBytecode(frame) + exit->target;
_Py_BackoffCounter temperature = exit->temperature;
_PyExecutorObject *executor;
if (target->op.code == ENTER_EXECUTOR) {
TIER2_TO_TIER2(exit->executor);
}
else {
- if (frame->owner >= FRAME_OWNED_BY_INTERPRETER) {
- GOTO_TIER_ONE(target);
- }
if (!backoff_counter_triggers(temperature)) {
exit->temperature = advance_backoff_counter(temperature);
GOTO_TIER_ONE(target);
_PyExecutorObject *previous_executor = _PyExecutor_FromExit(exit);
assert(tstate->current_executor == (PyObject *)previous_executor);
int chain_depth = previous_executor->vm_data.chain_depth + !exit->is_control_flow;
- int succ = _PyJit_TryInitializeTracing(tstate, frame, target, target, target, STACK_LEVEL(), chain_depth, exit, previous_executor, target->op.arg);
+ int succ = _PyJit_TryInitializeTracing(tstate, frame, target, target, target, STACK_LEVEL(), chain_depth, exit, target->op.arg);
exit->temperature = restart_backoff_counter(exit->temperature);
if (succ) {
GOTO_TIER_ONE_CONTINUE_TRACING(target);
stack_pointer += -1;
assert(WITHIN_STACK_BOUNDS());
_PyFrame_SetStackPointer(frame, stack_pointer);
- _PyJit_translate_single_bytecode_to_trace(tstate, frame, next_instr);
+ _PyJit_translate_single_bytecode_to_trace(tstate, frame, NULL);
stack_pointer = _PyFrame_GetStackPointer(frame);
LEAVE_TRACING();
_PyFrame_SetStackPointer(frame, stack_pointer);
oparg >>= 8;
insert_exec_at--;
}
- int succ = _PyJit_TryInitializeTracing(tstate, frame, this_instr, insert_exec_at, next_instr, STACK_LEVEL(), 0, NULL, NULL, oparg);
+ int succ = _PyJit_TryInitializeTracing(tstate, frame, this_instr, insert_exec_at, next_instr, STACK_LEVEL(), 0, NULL, oparg);
if (succ) {
ENTER_TRACING();
}
chain_depth %= MAX_CHAIN_DEPTH;
bool progress_needed = chain_depth == 0;
PyCodeObject *code = (PyCodeObject *)tstate->interp->jit_state.initial_code;
- _Py_CODEUNIT *start = tstate->interp->jit_state.insert_exec_instr;
+ _Py_CODEUNIT *start = tstate->interp->jit_state.start_instr;
if (progress_needed && !has_space_for_executor(code, start)) {
interp->compiling = false;
return 0;
// Strange control-flow
bool has_dynamic_jump_taken = OPCODE_HAS_UNPREDICTABLE_JUMP(opcode) &&
(next_instr != this_instr + 1 + _PyOpcode_Caches[_PyOpcode_Deopt[opcode]]);
- if (has_dynamic_jump_taken) {
- DPRINTF(2, "Unsupported: dynamic jump taken\n");
- goto unsupported;
- }
/* Special case the first instruction,
* so that we can guarantee forward progress */
}
bool needs_guard_ip = _PyOpcode_NeedsGuardIp[opcode];
+ if (has_dynamic_jump_taken && !needs_guard_ip) {
+ DPRINTF(2, "Unsupported: dynamic jump taken\n");
+ goto unsupported;
+ }
DPRINTF(2, "%p %d: %s(%d) %d %d\n", old_code, target, _PyOpcode_OpName[opcode], oparg, needs_guard_ip, old_stack_level);
#ifdef Py_DEBUG
case JUMP_BACKWARD_NO_INTERRUPT:
{
if ((next_instr != tstate->interp->jit_state.close_loop_instr) &&
- (next_instr != tstate->interp->jit_state.insert_exec_instr) &&
+ (next_instr != tstate->interp->jit_state.start_instr) &&
tstate->interp->jit_state.code_curr_size > 5 &&
// These are coroutines, and we want to unroll those usually.
opcode != JUMP_BACKWARD_NO_INTERRUPT) {
OPT_STAT_INC(inner_loop);
ADD_TO_TRACE(_EXIT_TRACE, 0, 0, target);
trace[trace_length-1].operand1 = true; // is_control_flow
- DPRINTF(2, "JUMP_BACKWARD not to top ends trace %p %p %p\n", next_instr, tstate->interp->jit_state.close_loop_instr, tstate->interp->jit_state.insert_exec_instr);
+ DPRINTF(2, "JUMP_BACKWARD not to top ends trace %p %p %p\n", next_instr, tstate->interp->jit_state.close_loop_instr, tstate->interp->jit_state.start_instr);
goto done;
}
break;
* start with RESUME_CHECK */
ADD_TO_TRACE(_TIER2_RESUME_CHECK, 0, 0, target);
break;
-
+ case INTERPRETER_EXIT:
+ ADD_TO_TRACE(_DEOPT, 0, 0, target);
+ goto done;;
default:
{
const struct opcode_macro_expansion *expansion = &_PyOpcode_macro_expansion[opcode];
PyCodeObject *new_code = (PyCodeObject *)PyStackRef_AsPyObjectBorrow(frame->f_executable);
PyFunctionObject *new_func = (PyFunctionObject *)PyStackRef_AsPyObjectBorrow(frame->f_funcobj);
- if (new_func != NULL) {
- operand = (uintptr_t)new_func;
- DPRINTF(2, "Adding %p func to op\n", (void *)operand);
- _Py_BloomFilter_Add(dependencies, new_func);
- }
- else if (new_code != NULL && !Py_IsNone((PyObject*)new_code)) {
- operand = (uintptr_t)new_code | 1;
- DPRINTF(2, "Adding %p code to op\n", (void *)operand);
- _Py_BloomFilter_Add(dependencies, new_code);
- }
- else {
- operand = 0;
+ operand = 0;
+ if (frame->owner < FRAME_OWNED_BY_INTERPRETER) {
+ if (new_func != NULL) {
+ operand = (uintptr_t)new_func;
+ DPRINTF(2, "Adding %p func to op\n", (void *)operand);
+ _Py_BloomFilter_Add(dependencies, new_func);
+ }
+ else if (new_code != NULL && !Py_IsNone((PyObject*)new_code)) {
+ operand = (uintptr_t)new_code | 1;
+ DPRINTF(2, "Adding %p code to op\n", (void *)operand);
+ _Py_BloomFilter_Add(dependencies, new_code);
+ }
}
ADD_TO_TRACE(uop, oparg, operand, target);
trace[trace_length - 1].operand1 = PyStackRef_IsNone(frame->f_executable) ? 2 : ((int)(frame->stackpointer - _PyFrame_Stackbase(frame)));
}
}
// Loop back to the start
- int is_first_instr = tstate->interp->jit_state.close_loop_instr == next_instr || tstate->interp->jit_state.insert_exec_instr == next_instr;
+ int is_first_instr = tstate->interp->jit_state.close_loop_instr == next_instr || tstate->interp->jit_state.start_instr == next_instr;
if (is_first_instr && tstate->interp->jit_state.code_curr_size > 5) {
+ if (needs_guard_ip) {
+ ADD_TO_TRACE(_SET_IP, 0, (uintptr_t)next_instr, 0);
+ }
ADD_TO_TRACE(_JUMP_TO_TOP, 0, 0, 0);
goto done;
}
// 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, _PyExecutorObject *prev_exec, int oparg)
+_PyJit_TryInitializeTracing(
+ PyThreadState *tstate, _PyInterpreterFrame *frame, _Py_CODEUNIT *curr_instr,
+ _Py_CODEUNIT *start_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.
chain_depth);
#endif
- add_to_trace(tstate->interp->jit_state.code_buffer, 0, _START_EXECUTOR, 0, (uintptr_t)insert_exec_instr, INSTR_IP(insert_exec_instr, code));
+ add_to_trace(tstate->interp->jit_state.code_buffer, 0, _START_EXECUTOR, 0, (uintptr_t)start_instr, INSTR_IP(start_instr, code));
add_to_trace(tstate->interp->jit_state.code_buffer, 1, _MAKE_WARM, 0, 0, 0);
tstate->interp->jit_state.code_curr_size = 2;
tstate->interp->jit_state.code_max_size = UOP_MAX_TRACE_LENGTH;
- tstate->interp->jit_state.insert_exec_instr = insert_exec_instr;
+ tstate->interp->jit_state.start_instr = start_instr;
tstate->interp->jit_state.close_loop_instr = close_loop_instr;
tstate->interp->jit_state.initial_code = (PyCodeObject *)Py_NewRef(code);
tstate->interp->jit_state.initial_func = (PyFunctionObject *)Py_XNewRef(PyStackRef_AsPyObjectBorrow(frame->f_funcobj));
tstate->interp->jit_state.prev_instr_oparg = oparg;
tstate->interp->jit_state.prev_instr_stacklevel = curr_stackdepth;
tstate->interp->jit_state.prev_instr_is_super = false;
- assert(curr_instr->op.code == JUMP_BACKWARD_JIT || (prev_exec != NULL && exit != NULL));
+ assert(curr_instr->op.code == JUMP_BACKWARD_JIT || (exit != NULL));
tstate->interp->jit_state.jump_backward_instr = curr_instr;
- tstate->interp->jit_state.prev_executor = (_PyExecutorObject *)Py_XNewRef(prev_exec);
+ assert(curr_instr->op.code == JUMP_BACKWARD_JIT || (exit != NULL));
_Py_BloomFilter_Init(&tstate->interp->jit_state.dependencies);
return 1;
}
Py_CLEAR(tstate->interp->jit_state.initial_code);
Py_CLEAR(tstate->interp->jit_state.initial_func);
Py_CLEAR(tstate->interp->jit_state.prev_instr_code);
- Py_CLEAR(tstate->interp->jit_state.prev_executor);
tstate->interp->jit_state.code_curr_size = 2;
tstate->interp->jit_state.code_max_size = UOP_MAX_TRACE_LENGTH - 1;
}
for (int i = 0; i < length; i++) {
_PyUOpInstruction *inst = &buffer[i];
int opcode = inst->opcode;
- if (inst->format != UOP_FORMAT_TARGET) {
- fprintf(stdout, "I: %d\n", i);
- }
int32_t target = (int32_t)uop_get_target(inst);
uint16_t exit_flags = _PyUop_Flags[opcode] & (HAS_EXIT_FLAG | HAS_DEOPT_FLAG | HAS_PERIODIC_FLAG);
if (exit_flags) {
}
case _JUMP_TO_TOP:
case _DYNAMIC_EXIT:
+ case _DEOPT:
return pc + 1;
}
}
ctx->done = true;
}
+ op(_DEOPT, (--)) {
+ ctx->done = true;
+ }
+
op(_REPLACE_WITH_TRUE, (value -- res)) {
REPLACE_OP(this_instr, _POP_TOP_LOAD_CONST_INLINE_BORROW, 0, (uintptr_t)Py_True);
res = sym_new_const(ctx, Py_True);
}
case _DEOPT: {
+ ctx->done = true;
break;
}