\n", i, color, opname, inst->operand0);
#endif
}
static bool
is_stop(_PyUOpInstruction const *inst)
{
uint16_t base_opcode = _PyUop_Uncached[inst->opcode];
return (base_opcode == _EXIT_TRACE || base_opcode == _DEOPT || base_opcode == _JUMP_TO_TOP);
}
/* Writes the node and outgoing edges for a single tracelet in graphviz format.
* Each tracelet is presented as a table of the uops it contains.
* If Py_STATS is enabled, execution counts are included.
*
* https://graphviz.readthedocs.io/en/stable/manual.html
* https://graphviz.org/gallery/
*/
static void
executor_to_gv(_PyExecutorObject *executor, FILE *out)
{
PyCodeObject *code = executor->vm_data.code;
fprintf(out, "executor_%p [\n", executor);
fprintf(out, " shape = none\n");
/* Write the HTML table for the uops */
fprintf(out, " label =
\n");
fprintf(out, "]\n\n");
/* Write all the outgoing edges */
_PyExecutorObject *cold = _PyExecutor_GetColdExecutor();
_PyExecutorObject *cold_dynamic = _PyExecutor_GetColdDynamicExecutor();
for (uint32_t i = 0; i < executor->code_size; i++) {
_PyUOpInstruction const *inst = &executor->trace[i];
uint16_t base_opcode = _PyUop_Uncached[inst->opcode];
uint16_t flags = _PyUop_Flags[base_opcode];
_PyExitData *exit = NULL;
if (base_opcode == _JUMP_TO_TOP) {
fprintf(out, "executor_%p:i%d -> executor_%p:i%d [color = \"" LOOP "\"]\n", executor, i, executor, inst->jump_target);
break;
}
if (base_opcode == _EXIT_TRACE) {
exit = (_PyExitData *)inst->operand0;
}
else if (flags & HAS_EXIT_FLAG) {
assert(inst->format == UOP_FORMAT_JUMP);
_PyUOpInstruction const *exit_inst = &executor->trace[inst->jump_target];
uint16_t base_exit_opcode = _PyUop_Uncached[exit_inst->opcode];
(void)base_exit_opcode;
assert(base_exit_opcode == _EXIT_TRACE || base_exit_opcode == _DYNAMIC_EXIT);
exit = (_PyExitData *)exit_inst->operand0;
}
if (exit != NULL) {
if (exit->executor == cold || exit->executor == cold_dynamic) {
#ifdef Py_STATS
/* Only mark as have cold exit if it has actually exited */
uint64_t diff = inst->execution_count - executor->trace[i+1].execution_count;
if (diff) {
fprintf(out, "cold_%p%d [ label = \"%" PRIu64 "\" shape = ellipse color=\"" BLUE "\" ]\n", executor, i, diff);
fprintf(out, "executor_%p:i%d -> cold_%p%d\n", executor, i, executor, i);
}
#endif
}
else {
fprintf(out, "executor_%p:i%d -> executor_%p:start\n", executor, i, exit->executor);
}
}
if (is_stop(inst)) {
break;
}
}
}
/* Write the graph of all the live tracelets in graphviz format. */
int
_PyDumpExecutors(FILE *out)
{
fprintf(out, "digraph ideal {\n\n");
fprintf(out, " rankdir = \"LR\"\n\n");
fprintf(out, " node [colorscheme=greys9]\n");
PyInterpreterState *interp = PyInterpreterState_Get();
for (size_t i = 0; i < interp->executor_count; i++) {
executor_to_gv(interp->executor_ptrs[i], out);
}
fprintf(out, "}\n\n");
return 0;
}
#else
int
_PyDumpExecutors(FILE *out)
{
PyErr_SetString(PyExc_NotImplementedError, "No JIT available");
return -1;
}
void
_PyExecutor_Free(struct _PyExecutorObject *self)
{
/* This should never be called */
Py_UNREACHABLE();
}
#endif /* _Py_TIER2 */