/* * graph.c: Helper routines to graph various internal states of the code generator * * Author: * Dietmar Maurer (dietmar@ximian.com) * * (C) 2003 Ximian, Inc. */ #include #ifndef DISABLE_JIT #include #include #include "mini.h" static char * convert_name (const char *str) { int i, j, len = strlen (str); char *res = g_malloc (len * 2); j = 0; for (i = 0; i < len; i++) { char c = str [i]; switch (c) { case '.': res [j++] = '_'; break; default: res [j++] = c; } } res [j] = 0; return res; } static void dtree_emit_one_loop_level (MonoCompile *cfg, FILE *fp, MonoBasicBlock *h) { MonoBasicBlock *bb; int i, level = 0; if (h) { level = h->nesting; fprintf (fp, "subgraph cluster_%d {\n", h->block_num); fprintf (fp, "label=\"loop_%d\"\n", h->block_num); } for (i = 1; i < cfg->num_bblocks; ++i) { bb = cfg->bblocks [i]; if (!h || (g_list_find (h->loop_blocks, bb) && bb != h)) { if (bb->nesting == level) { fprintf (fp, "BB%d -> BB%d;\n", bb->idom->block_num, bb->block_num); } if (bb->nesting == (level + 1) && bb->loop_blocks) { fprintf (fp, "BB%d -> BB%d;\n", bb->idom->block_num, bb->block_num); dtree_emit_one_loop_level (cfg, fp, bb); } } } if (h) { fprintf (fp, "}\n"); } } static void cfg_emit_one_loop_level (MonoCompile *cfg, FILE *fp, MonoBasicBlock *h) { MonoBasicBlock *bb; int j, level = 0; if (h) { level = h->nesting; fprintf (fp, "subgraph cluster_%d {\n", h->block_num); fprintf (fp, "label=\"loop_%d\"\n", h->block_num); } for (bb = cfg->bb_entry->next_bb; bb; bb = bb->next_bb) { if (bb->region != -1) { switch (bb->region & (MONO_REGION_FINALLY|MONO_REGION_CATCH|MONO_REGION_FAULT|MONO_REGION_FILTER)) { case MONO_REGION_CATCH: fprintf (fp, "BB%d [color=blue];\n", bb->block_num);; break; case MONO_REGION_FINALLY: fprintf (fp, "BB%d [color=green];\n", bb->block_num);; break; case MONO_REGION_FAULT: case MONO_REGION_FILTER: fprintf (fp, "BB%d [color=yellow];\n", bb->block_num);; break; default: break; } } if (!h || (g_list_find (h->loop_blocks, bb) && bb != h)) { if (bb->nesting == level) { for (j = 0; j < bb->in_count; j++) fprintf (fp, "BB%d -> BB%d;\n", bb->in_bb [j]->block_num, bb->block_num); } if (bb->nesting == (level + 1) && bb->loop_blocks) { for (j = 0; j < bb->in_count; j++) fprintf (fp, "BB%d -> BB%d;\n", bb->in_bb [j]->block_num, bb->block_num); cfg_emit_one_loop_level (cfg, fp, bb); } } } if (h) { fprintf (fp, "}\n"); } } static void mono_draw_dtree (MonoCompile *cfg, FILE *fp) { g_assert ((cfg->comp_done & MONO_COMP_IDOM)); fprintf (fp, "digraph %s {\n", convert_name (cfg->method->name)); fprintf (fp, "node [fontsize=12.0]\nedge [len=1,color=red]\n"); fprintf (fp, "label=\"Dominator tree for %s\";\n", mono_method_full_name (cfg->method, TRUE)); fprintf (fp, "BB0 [shape=doublecircle];\n"); fprintf (fp, "BB1 [color=red];\n"); dtree_emit_one_loop_level (cfg, fp, NULL); fprintf (fp, "}\n"); } static void mono_draw_cfg (MonoCompile *cfg, FILE *fp) { fprintf (fp, "digraph %s {\n", convert_name (cfg->method->name)); fprintf (fp, "node [fontsize=12.0]\nedge [len=1,color=red]\n"); fprintf (fp, "label=\"CFG for %s\";\n", mono_method_full_name (cfg->method, TRUE)); fprintf (fp, "BB0 [shape=doublecircle];\n"); fprintf (fp, "BB1 [color=red];\n"); cfg_emit_one_loop_level (cfg, fp, NULL); fprintf (fp, "}\n"); } #if 0 static void mono_print_label (FILE *fp, MonoInst *tree) { int arity; if (!tree) return; arity = mono_burg_arity [tree->opcode]; fprintf (fp, "\\ %s%s", arity? "(": "", mono_inst_name (tree->opcode)); switch (tree->opcode) { case OP_ICONST: fprintf (fp, "[%ld]", (long)tree->inst_c0); break; case OP_I8CONST: fprintf (fp, "[%lld]", (long long)tree->inst_l); break; case OP_R8CONST: fprintf (fp, "[%f]", *(double*)tree->inst_p0); break; case OP_R4CONST: fprintf (fp, "[%f]", *(float*)tree->inst_p0); break; case OP_ARG: case OP_LOCAL: fprintf (fp, "[%d]", (int)tree->inst_c0); break; case OP_REGOFFSET: fprintf (fp, "[0x%x(%s)]", (int)tree->inst_offset, mono_arch_regname (tree->inst_basereg)); break; case OP_REGVAR: fprintf (fp, "[%s]", mono_arch_regname (tree->dreg)); break; case CEE_NEWARR: fprintf (fp, "[%s]", tree->inst_newa_class->name); mono_print_label (fp, tree->inst_newa_len); break; case OP_CALL: case OP_CALL_MEMBASE: case OP_FCALL: case OP_FCALL_MEMBASE: case OP_LCALL: case OP_LCALL_MEMBASE: case OP_VCALL: case OP_VCALL_MEMBASE: case OP_VOIDCALL: case OP_VOIDCALL_MEMBASE: { MonoCallInst *call = (MonoCallInst*)tree; if (call->method) { if (mono_method_signature (call->method)->hasthis && tree->inst_left) { mono_print_label (fp, tree->inst_left); } fprintf (fp, "[%s]", call->method->name); } break; } case OP_PHI: { int i; fprintf (fp, "[%d\\ (", (int)tree->inst_c0); for (i = 0; i < tree->inst_phi_args [0]; i++) { if (i) fprintf (fp, ",\\ "); fprintf (fp, "%d", tree->inst_phi_args [i + 1]); } fprintf (fp, ")]"); break; } case OP_NOP: case OP_JMP: case OP_BREAK: break; case OP_BR: fprintf (fp, "[B%d]", tree->inst_target_bb->block_num); break; case OP_SWITCH: case CEE_ISINST: case CEE_CASTCLASS: case OP_CALL_REG: case OP_FCALL_REG: case OP_LCALL_REG: case OP_VCALL_REG: case OP_VOIDCALL_REG: mono_print_label (fp, tree->inst_left); break; case CEE_BNE_UN: case CEE_BEQ: case CEE_BLT: case CEE_BLT_UN: case CEE_BGT: case CEE_BGT_UN: case CEE_BGE: case CEE_BGE_UN: case CEE_BLE: case CEE_BLE_UN: fprintf (fp, "[B%dB%d]", tree->inst_true_bb->block_num, tree->inst_false_bb->block_num); mono_print_label (fp, tree->inst_left); break; default: if (arity) { mono_print_label (fp, tree->inst_left); if (arity > 1) mono_print_label (fp, tree->inst_right); } break; } if (arity) fprintf (fp, ")"); } #endif static void mono_draw_code_cfg (MonoCompile *cfg, FILE *fp) { MonoBasicBlock *bb; fprintf (fp, "digraph %s {\n", convert_name (cfg->method->name)); fprintf (fp, "node [fontsize=12.0]\nedge [len=1,color=red]\n"); fprintf (fp, "label=\"CFG for %s\";\n", mono_method_full_name (cfg->method, TRUE)); fprintf (fp, "BB0 [shape=doublecircle];\n"); fprintf (fp, "BB1 [color=red];\n"); for (bb = cfg->bb_entry->next_bb; bb; bb = bb->next_bb) { MonoInst *inst; const char *color; if (bb == cfg->bb_exit) continue; if ((cfg->comp_done & MONO_COMP_REACHABILITY) && (bb->flags & BB_REACHABLE)) color = "color=red,"; else color = ""; fprintf (fp, "BB%d [%sshape=record,labeljust=l,label=\"{BB%d|", bb->block_num, color, bb->block_num); MONO_BB_FOR_EACH_INS (bb, inst) { //mono_print_label (fp, inst); fprintf (fp, "\\n"); } fprintf (fp, "}\"];\n"); } cfg_emit_one_loop_level (cfg, fp, NULL); fprintf (fp, "}\n"); } void mono_draw_graph (MonoCompile *cfg, MonoGraphOptions draw_options) { char *com; const char *fn; FILE *fp; int _i G_GNUC_UNUSED; fn = "/tmp/minidtree.graph"; fp = fopen (fn, "w+"); g_assert (fp); switch (draw_options) { case MONO_GRAPH_DTREE: mono_draw_dtree (cfg, fp); break; case MONO_GRAPH_CFG: mono_draw_cfg (cfg, fp); break; case MONO_GRAPH_CFG_CODE: case MONO_GRAPH_CFG_OPTCODE: case MONO_GRAPH_CFG_SSA: mono_draw_code_cfg (cfg, fp); break; } fclose (fp); //com = g_strdup_printf ("dot %s -Tpng -o %s.png; eog %s.png", fn, fn, fn); com = g_strdup_printf ("dot %s -Tps -o %s.ps;gv %s.ps", fn, fn, fn); _i = system (com); g_free (com); } #endif /* DISABLE_JIT */