extern void _Py_ClearExecutorDeletionList(PyInterpreterState *interp);
#endif
-int _PyJit_translate_single_bytecode_to_trace(PyThreadState *tstate, _PyInterpreterFrame *frame, _Py_CODEUNIT *next_instr, bool stop_tracing);
+int _PyJit_translate_single_bytecode_to_trace(PyThreadState *tstate, _PyInterpreterFrame *frame, _Py_CODEUNIT *next_instr, int stop_tracing_opcode);
int
_PyJit_TryInitializeTracing(PyThreadState *tstate, _PyInterpreterFrame *frame,
pass
return None
+def get_all_executors(func):
+ code = func.__code__
+ co_code = code.co_code
+ executors = []
+ for i in range(0, len(co_code), 2):
+ try:
+ executors.append(_opcode.get_executor(code, i))
+ except ValueError:
+ pass
+ return executors
+
def iter_opnames(ex):
for item in ex:
next(g)
""" % _testinternalcapi.SPECIALIZATION_THRESHOLD))
+ def test_executor_side_exits_create_another_executor(self):
+ def f():
+ for x in range(TIER2_THRESHOLD + 3):
+ for y in range(TIER2_THRESHOLD + 3):
+ z = x + y
+
+ f()
+ all_executors = get_all_executors(f)
+ # Inner loop warms up first.
+ # Outer loop warms up later, linking to the inner one.
+ # Therefore, we have at least two executors.
+ self.assertGreaterEqual(len(all_executors), 2)
+ for executor in all_executors:
+ opnames = list(get_opnames(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":
+ break
+ elif op == "_DEOPT":
+ self.fail(f"_DEOPT encountered first at executor"
+ f" {executor} at offset {idx} rather"
+ f" than expected _EXIT_TRACE")
+
+
def global_identity(x):
return x
bool stop_tracing = (opcode == WITH_EXCEPT_START ||
opcode == RERAISE || opcode == CLEANUP_THROW ||
opcode == PUSH_EXC_INFO || opcode == INTERPRETER_EXIT);
- int full = !_PyJit_translate_single_bytecode_to_trace(tstate, frame, next_instr, stop_tracing);
+ int full = !_PyJit_translate_single_bytecode_to_trace(tstate, frame, next_instr, stop_tracing ? _DEOPT : 0);
if (full) {
LEAVE_TRACING();
int err = stop_tracing_and_jit(tstate, frame);
#if _Py_TIER2
assert(IS_JIT_TRACING());
int opcode = next_instr->op.code;
- _PyJit_translate_single_bytecode_to_trace(tstate, frame, NULL, true);
+ _PyJit_translate_single_bytecode_to_trace(tstate, frame, NULL, _EXIT_TRACE);
LEAVE_TRACING();
int err = stop_tracing_and_jit(tstate, frame);
ERROR_IF(err < 0);
opcode == RERAISE || opcode == CLEANUP_THROW ||
opcode == PUSH_EXC_INFO || opcode == INTERPRETER_EXIT);
_PyFrame_SetStackPointer(frame, stack_pointer);
- int full = !_PyJit_translate_single_bytecode_to_trace(tstate, frame, next_instr, stop_tracing);
+ int full = !_PyJit_translate_single_bytecode_to_trace(tstate, frame, next_instr, stop_tracing ? _DEOPT : 0);
stack_pointer = _PyFrame_GetStackPointer(frame);
if (full) {
LEAVE_TRACING();
assert(IS_JIT_TRACING());
int opcode = next_instr->op.code;
_PyFrame_SetStackPointer(frame, stack_pointer);
- _PyJit_translate_single_bytecode_to_trace(tstate, frame, NULL, true);
+ _PyJit_translate_single_bytecode_to_trace(tstate, frame, NULL, _EXIT_TRACE);
stack_pointer = _PyFrame_GetStackPointer(frame);
LEAVE_TRACING();
_PyFrame_SetStackPointer(frame, stack_pointer);
PyThreadState *tstate,
_PyInterpreterFrame *frame,
_Py_CODEUNIT *next_instr,
- bool stop_tracing)
+ int stop_tracing_opcode)
{
#ifdef Py_DEBUG
goto full;
}
- if (stop_tracing) {
- ADD_TO_TRACE(_DEOPT, 0, 0, target);
+ if (stop_tracing_opcode != 0) {
+ ADD_TO_TRACE(stop_tracing_opcode, 0, 0, target);
goto done;
}