typedef _Py_CODEUNIT *(*_PyJitEntryFuncPtr)(struct _PyExecutorObject *exec, _PyInterpreterFrame *frame, _PyStackRef *stack_pointer, PyThreadState *tstate);
-typedef struct _PyJitState {
+typedef struct _PyJitTracerState {
int jit_tracer_code_max_size;
int jit_tracer_code_curr_size;
_PyBloomFilter jit_tracer_dependencies;
PyFunctionObject *jit_tracer_initial_func; // Strong
struct _PyExitData *jit_tracer_previous_exit;
_PyInterpreterFrame *jit_tracer_current_frame;
-} _PyJitState;
+} _PyJitTracerState;
/* PyInterpreterState holds the global state for one of the runtime's
interpreters. Typically the initial (main) interpreter is the only one.
struct types_state types;
struct callable_cache callable_cache;
PyObject *common_consts[NUM_COMMON_CONSTANTS];
- _PyJitState jit_state;
+ _PyJitTracerState jit_state;
bool jit;
bool compiling;
struct _PyExecutorObject *executor_list_head;
[INTERPRETER_EXIT] = 1,
[RETURN_VALUE] = 1,
[YIELD_VALUE] = 1,
- [JUMP_FORWARD] = 1,
- [JUMP_BACKWARD_NO_INTERRUPT] = 1,
- [INSTRUMENTED_FOR_ITER] = 1,
+ [LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN] = 1,
[RETURN_GENERATOR] = 1,
[BINARY_OP_SUBSCR_GETITEM] = 1,
[INSTRUMENTED_RETURN_VALUE] = 1,
[SEND] = 1,
[SEND_GEN] = 1,
[INSTRUMENTED_YIELD_VALUE] = 1,
- [INSTRUMENTED_END_ASYNC_FOR] = 1,
- [END_ASYNC_FOR] = 1,
[LOAD_ATTR_PROPERTY] = 1,
- [JUMP_BACKWARD] = 1,
- [JUMP_BACKWARD_NO_JIT] = 1,
- [JUMP_BACKWARD_JIT] = 1,
- [POP_JUMP_IF_TRUE] = 1,
- [POP_JUMP_IF_FALSE] = 1,
- [POP_JUMP_IF_NONE] = 1,
- [POP_JUMP_IF_NOT_NONE] = 1,
- [FOR_ITER] = 1,
- [FOR_ITER_LIST] = 1,
- [FOR_ITER_TUPLE] = 1,
- [FOR_ITER_RANGE] = 1,
[FOR_ITER_GEN] = 1,
+ [CALL] = 1,
+ [INSTRUMENTED_CALL] = 1,
[CALL_PY_GENERAL] = 1,
[CALL_BOUND_METHOD_GENERAL] = 1,
[CALL_BOUND_METHOD_EXACT_ARGS] = 1,
[CALL_ALLOC_AND_ENTER_INIT] = 1,
[CALL_KW_PY] = 1,
[CALL_KW_BOUND_METHOD] = 1,
+ [CALL_KW] = 1,
+ [INSTRUMENTED_CALL_KW] = 1,
+ [CALL_FUNCTION_EX] = 1,
+ [INSTRUMENTED_CALL_FUNCTION_EX] = 1,
};
#endif
typedef struct _PyExitData {
uint32_t target;
uint16_t index;
- char is_dynamic;
_Py_BackoffCounter temperature;
struct _PyExecutorObject *executor;
} _PyExitData;
if (err == 0) {
assert(retval_o != NULL);
JUMPBY(oparg);
- RECORD_JUMP_TAKEN();
+ RECORD_DYNAMIC_JUMP_TAKEN();
}
else {
PyStackRef_CLOSE(v);
}
// Jump forward by oparg and skip the following END_FOR
JUMPBY(oparg + 1);
- RECORD_JUMP_TAKEN();
+ RECORD_DYNAMIC_JUMP_TAKEN();
DISPATCH();
}
next = item;
null_or_index = PyStackRef_TagInt(-1);
/* Jump forward oparg, then skip following END_FOR instruction */
JUMPBY(oparg + 1);
- RECORD_JUMP_TAKEN();
+ RECORD_DYNAMIC_JUMP_TAKEN();
DISPATCH();
}
#endif
null_or_index = PyStackRef_TagInt(-1);
/* Jump forward oparg, then skip following END_FOR instruction */
JUMPBY(oparg + 1);
- RECORD_JUMP_TAKEN();
+ RECORD_DYNAMIC_JUMP_TAKEN();
DISPATCH();
}
}
if (r->len <= 0) {
// Jump over END_FOR instruction.
JUMPBY(oparg + 1);
- RECORD_JUMP_TAKEN();
+ RECORD_DYNAMIC_JUMP_TAKEN();
DISPATCH();
}
}
LOAD_IP(frame->return_offset);
#endif
#if TIER_TWO
- frame->instr_ptr += (frame->return_offset);
+ TIER2_STORE_IP(frame->return_offset);
#endif
RELOAD_STACK();
res = PyStackRef_FromPyObjectStealMortal((PyObject *)gen);
tier2 op(_EXIT_TRACE, (exit_p/4 --)) {
_PyExitData *exit = (_PyExitData *)exit_p;
- assert(!exit->is_dynamic);
#if defined(Py_DEBUG) && !defined(_Py_JIT)
_Py_CODEUNIT *target = _PyFrame_GetBytecode(frame) + exit->target;
OPT_HIST(trace_uop_execution_counter, trace_run_length_hist);
#endif
#define TRACING_JUMP_TO_LABEL(label) \
- RECORD_JUMP_TAKEN() \
+ RECORD_DYNAMIC_JUMP_TAKEN() \
RECORD_TRACE_NO_DISPATCH() \
assert(!IS_JIT_TRACING()); \
JUMP_TO_LABEL(label);
#define RECORD_BRANCH_TAKEN(bitset, flag)
#endif
-#define RECORD_JUMP_TAKEN() _jump_taken = 1;
+#define RECORD_DYNAMIC_JUMP_TAKEN() _jump_taken = 1;
#define UNBOUNDLOCAL_ERROR_MSG \
"cannot access local variable '%s' where it is not associated with a value"
LOAD_IP(frame->return_offset);
#endif
#if TIER_TWO
- frame->instr_ptr += (frame->return_offset);
+ TIER2_STORE_IP(frame->return_offset);
#endif
stack_pointer = _PyFrame_GetStackPointer(frame);
res = PyStackRef_FromPyObjectStealMortal((PyObject *)gen);
case _EXIT_TRACE: {
PyObject *exit_p = (PyObject *)CURRENT_OPERAND0();
_PyExitData *exit = (_PyExitData *)exit_p;
- assert(!exit->is_dynamic);
#if defined(Py_DEBUG) && !defined(_Py_JIT)
_Py_CODEUNIT *target = _PyFrame_GetBytecode(frame) + exit->target;
OPT_HIST(trace_uop_execution_counter, trace_run_length_hist);
LOAD_IP(frame->return_offset);
#endif
#if TIER_TWO
- frame->instr_ptr += (frame->return_offset);
+ TIER2_STORE_IP(frame->return_offset);
#endif
stack_pointer = _PyFrame_GetStackPointer(frame);
res = PyStackRef_FromPyObjectStealMortal((PyObject *)gen);
TRACING_JUMP_TO_LABEL(error);
}
JUMPBY(oparg + 1);
- RECORD_JUMP_TAKEN();
+ RECORD_DYNAMIC_JUMP_TAKEN();
stack_pointer[-1] = null_or_index;
TRACING_DISPATCH();
}
if ((size_t)PyStackRef_UntagInt(null_or_index) >= (size_t)PyList_GET_SIZE(list_o)) {
null_or_index = PyStackRef_TagInt(-1);
JUMPBY(oparg + 1);
- RECORD_JUMP_TAKEN();
+ RECORD_DYNAMIC_JUMP_TAKEN();
stack_pointer[-1] = null_or_index;
TRACING_DISPATCH();
}
STAT_INC(FOR_ITER, hit);
if (r->len <= 0) {
JUMPBY(oparg + 1);
- RECORD_JUMP_TAKEN();
+ RECORD_DYNAMIC_JUMP_TAKEN();
TRACING_DISPATCH();
}
}
if ((size_t)PyStackRef_UntagInt(null_or_index) >= (size_t)PyTuple_GET_SIZE(tuple_o)) {
null_or_index = PyStackRef_TagInt(-1);
JUMPBY(oparg + 1);
- RECORD_JUMP_TAKEN();
+ RECORD_DYNAMIC_JUMP_TAKEN();
stack_pointer[-1] = null_or_index;
TRACING_DISPATCH();
}
LOAD_IP(frame->return_offset);
#endif
#if TIER_TWO
- frame->instr_ptr += (frame->return_offset);
+ TIER2_STORE_IP(frame->return_offset);
#endif
stack_pointer = _PyFrame_GetStackPointer(frame);
res = PyStackRef_FromPyObjectStealMortal((PyObject *)gen);
if (err == 0) {
assert(retval_o != NULL);
JUMPBY(oparg);
- RECORD_JUMP_TAKEN();
+ RECORD_DYNAMIC_JUMP_TAKEN();
}
else {
stack_pointer += -1;
interp->compiling = false;
return 0;
}
- // We are the only one still holding a reference to this code object that
- // is practically dead.
- if (_PyObject_IsUniquelyReferenced((PyObject *)code) || _PyObject_IsUniquelyReferenced((PyObject *)tstate->interp->jit_state.jit_tracer_initial_func)) {
- interp->compiling = false;
- return 0;
- }
- // One of our depencies while tracing was invalidated. Not worth compiling.
+ // One of our dependencies while tracing was invalidated. Not worth compiling.
if (!tstate->interp->jit_state.jit_tracer_dependencies_still_valid) {
interp->compiling = false;
return 0;
}
#endif
+ if (!tstate->interp->jit_state.jit_tracer_dependencies_still_valid) {
+ goto done;
+ }
+
DPRINTF(2, "%p %d: %s(%d) %d\n", old_code, target, _PyOpcode_OpName[opcode], oparg, progress_needed);
- bool needs_guard_ip = _PyOpcode_NeedsGuardIp[opcode] &&
- !(opcode == FOR_ITER_RANGE || opcode == FOR_ITER_LIST || opcode == FOR_ITER_TUPLE) &&
- !(opcode == JUMP_BACKWARD_NO_INTERRUPT || opcode == JUMP_BACKWARD || opcode == JUMP_BACKWARD_JIT) &&
- !(opcode == POP_JUMP_IF_TRUE || opcode == POP_JUMP_IF_FALSE || opcode == POP_JUMP_IF_NONE || opcode == POP_JUMP_IF_NOT_NONE);
+ bool needs_guard_ip = _PyOpcode_NeedsGuardIp[opcode];
// Strange control-flow, unsupported opcode, etc.
if (jump_taken ||
// If we haven't guarded the IP, then it's untraceable.
(frame != tstate->interp->jit_state.jit_tracer_current_frame && !needs_guard_ip) ||
(oparg > 0xFFFF) ||
- // TODO (gh-140277): The constituent use one extra stack slot. So we need to check for heaedroom.
+ // TODO (gh-140277): The constituent use one extra stack slot. So we need to check for headroom.
(opcode == BINARY_OP_SUBSCR_GETITEM && old_stack_level + 1 > old_code->co_stacksize)||
// Exception stuff, could be handled in the future maybe?
opcode == WITH_EXCEPT_START || opcode == RERAISE || opcode == CLEANUP_THROW || opcode == PUSH_EXC_INFO ||
_PyExitData *exit = &executor->exits[next_exit];
exit->target = buffer[i].target;
dest->operand0 = (uint64_t)exit;
- exit->is_dynamic = (char)(opcode == _DYNAMIC_EXIT);
next_exit--;
}
}
}
void
-_Py_JITTracer_InvalidateDependency(PyThreadState *old_tstate, void *obj)
+_Py_JITTracer_InvalidateDependency(PyThreadState *tstate, void *obj)
{
_PyBloomFilter obj_filter;
_Py_BloomFilter_Init(&obj_filter);
_Py_BloomFilter_Add(&obj_filter, obj);
- PyInterpreterState *interp = old_tstate->interp;
-
- _Py_FOR_EACH_TSTATE_UNLOCKED(interp, tstate) {
- if (bloom_filter_may_contain(&tstate->interp->jit_state.jit_tracer_dependencies, &obj_filter))
- {
- tstate->interp->jit_state.jit_tracer_dependencies_still_valid = false;
- }
-
+ if (bloom_filter_may_contain(&tstate->interp->jit_state.jit_tracer_dependencies, &obj_filter))
+ {
+ tstate->interp->jit_state.jit_tracer_dependencies_still_valid = false;
}
}
/* Invalidate all executors */
{
OPT_STAT_INC(optimizer_attempts);
- optimize_uops(
+ length = optimize_uops(
initial_func, buffer,
length, curr_stacklen, dependencies);
+ if (length == 0) {
+ return length;
+ }
+
assert(length > 0);
length = remove_unneeded_uops(buffer, length);
no_save_ip=no_save_ip,
tier=tier_variable(op),
needs_prev=variable_used(op, "prev_instr"),
- needs_guard_ip=variable_used(op, "JUMPBY") or variable_used(op, "LLTRACE_RESUME_FRAME"),
+ needs_guard_ip=variable_used(op, "TIER2_STORE_IP") or variable_used(op, "LLTRACE_RESUME_FRAME") or variable_used(op, "DISPATCH_INLINED"),
)
def expand(items: list[StackItem], oparg: int) -> list[StackItem]:
"DISPATCH": self.dispatch,
"INSTRUCTION_SIZE": self.instruction_size,
"stack_pointer": self.stack_pointer,
- "RECORD_JUMP_TAKEN": self.record_jump_taken,
+ "RECORD_DYNAMIC_JUMP_TAKEN": self.record_dynamic_jump_taken,
}
self.out = out
self.labels = labels
self.out.emit(f" {uop.instruction_size}u ")
return True
- def record_jump_taken(
+ def record_dynamic_jump_taken(
self,
tkn: Token,
tkn_iter: TokenIterator,
emit_to(self.out, tkn_iter, "SEMI")
return False
- def record_jump_taken(
+ def record_dynamic_jump_taken(
self,
tkn: Token,
tkn_iter: TokenIterator,