Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 22 additions & 4 deletions Lib/test/test_capi/test_opt.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,13 @@ def iter_opnames(ex):
def get_opnames(ex):
return list(iter_opnames(ex))

def iter_ops(ex):
for item in ex:
yield item

def get_ops(ex):
return list(iter_ops(ex))


@requires_specialization
@unittest.skipIf(Py_GIL_DISABLED, "optimizer not yet supported in free-threaded builds")
Expand Down Expand Up @@ -3003,14 +3010,25 @@ def f():
# Outer loop warms up later, linking to the inner one.
# Therefore, we have at least two executors.
self.assertGreaterEqual(len(all_executors), 2)
executor_ids = [id(e) for e in all_executors]
for executor in all_executors:
opnames = list(get_opnames(executor))
ops = get_ops(executor)
# Assert all executors first terminator ends in
# _EXIT_TRACE or _JUMP_TO_TOP, not _DEOPT
for idx, op in enumerate(opnames):
if op == "_EXIT_TRACE" or op == "_JUMP_TO_TOP":
for idx, op in enumerate(ops):
opname = op[0]
if opname == "_EXIT_TRACE":
# As this is a link outer executor to inner
# executor problem, all executors exits should point to
# another valid executor. In this case, none of them
# should be the cold executor.
exit = op[3]
link_to = _testinternalcapi.get_exit_executor(exit)
self.assertIn(id(link_to), executor_ids)
break
elif opname == "_JUMP_TO_TOP":
break
elif op == "_DEOPT":
elif opname == "_DEOPT":
self.fail(f"_DEOPT encountered first at executor"
f" {executor} at offset {idx} rather"
f" than expected _EXIT_TRACE")
Expand Down
17 changes: 17 additions & 0 deletions Modules/_testinternalcapi.c
Original file line number Diff line number Diff line change
Expand Up @@ -1245,6 +1245,22 @@ invalidate_executors(PyObject *self, PyObject *obj)
Py_RETURN_NONE;
}

static PyObject *
get_exit_executor(PyObject *self, PyObject *arg)
{
if (!PyLong_CheckExact(arg)) {
PyErr_SetString(PyExc_TypeError, "argument must be an ID to an _PyExitData");
return NULL;
}
uint64_t ptr;
if (PyLong_AsUInt64(arg, &ptr) < 0) {
// Error set by PyLong API
return NULL;
}
_PyExitData *exit = (_PyExitData *)ptr;
return Py_NewRef(exit->executor);
}

#endif

static int _pending_callback(void *arg)
Expand Down Expand Up @@ -2546,6 +2562,7 @@ static PyMethodDef module_functions[] = {
#ifdef _Py_TIER2
{"add_executor_dependency", add_executor_dependency, METH_VARARGS, NULL},
{"invalidate_executors", invalidate_executors, METH_O, NULL},
{"get_exit_executor", get_exit_executor, METH_O, NULL},
#endif
{"pending_threadfunc", _PyCFunction_CAST(pending_threadfunc),
METH_VARARGS | METH_KEYWORDS},
Expand Down
10 changes: 7 additions & 3 deletions Python/optimizer.c
Original file line number Diff line number Diff line change
Expand Up @@ -625,7 +625,6 @@ _PyJit_translate_single_bytecode_to_trace(
int trace_length = _tstate->jit_tracer_state.prev_state.code_curr_size;
_PyUOpInstruction *trace = _tstate->jit_tracer_state.code_buffer;
int max_length = _tstate->jit_tracer_state.prev_state.code_max_size;
int exit_op = stop_tracing_opcode == 0 ? _EXIT_TRACE : stop_tracing_opcode;

_Py_CODEUNIT *this_instr = _tstate->jit_tracer_state.prev_state.instr;
_Py_CODEUNIT *target_instr = this_instr;
Expand Down Expand Up @@ -691,13 +690,18 @@ _PyJit_translate_single_bytecode_to_trace(
goto full;
}

if (stop_tracing_opcode != 0) {
if (stop_tracing_opcode == _DEOPT) {
// gh-143183: It's important we rewind to the last known proper target.
// The current target might be garbage as stop tracing usually indicates
// we are in something that we can't trace.
DPRINTF(2, "Told to stop tracing\n");
goto unsupported;
}
else if (stop_tracing_opcode != 0) {
assert(stop_tracing_opcode == _EXIT_TRACE);
ADD_TO_TRACE(stop_tracing_opcode, 0, 0, target);
goto done;
}

DPRINTF(2, "%p %d: %s(%d) %d %d\n", old_code, target, _PyOpcode_OpName[opcode], oparg, needs_guard_ip, old_stack_level);

Expand Down Expand Up @@ -733,7 +737,7 @@ _PyJit_translate_single_bytecode_to_trace(
int32_t old_target = (int32_t)uop_get_target(curr);
curr++;
trace_length++;
curr->opcode = exit_op;
curr->opcode = _DEOPT;
curr->format = UOP_FORMAT_TARGET;
curr->target = old_target;
}
Expand Down
Loading