|
| 1 | +#include "scope.h" |
| 2 | +#include "../ir_private.h" |
| 3 | + |
| 4 | +#include "list.h" |
| 5 | +#include "dict.h" |
| 6 | +#include "util.h" |
| 7 | + |
| 8 | +#include <assert.h> |
| 9 | + |
| 10 | +static int extra_uniqueness = 0; |
| 11 | + |
| 12 | +static CFNode* get_let_pred(const CFNode* n) { |
| 13 | + if (entries_count_list(n->pred_edges) == 1) { |
| 14 | + CFEdge pred = read_list(CFEdge, n->pred_edges)[0]; |
| 15 | + assert(pred.dst == n); |
| 16 | + if (pred.type == LetTailEdge && entries_count_list(pred.src->succ_edges) == 1) { |
| 17 | + assert(is_case(n->node)); |
| 18 | + return pred.src; |
| 19 | + } |
| 20 | + } |
| 21 | + return NULL; |
| 22 | +} |
| 23 | + |
| 24 | +static void dump_cf_node(FILE* output, const CFNode* n) { |
| 25 | + const Node* bb = n->node; |
| 26 | + const Node* body = get_abstraction_body(bb); |
| 27 | + if (!body) |
| 28 | + return; |
| 29 | + if (get_let_pred(n)) |
| 30 | + return; |
| 31 | + |
| 32 | + String color = "black"; |
| 33 | + if (is_case(bb)) |
| 34 | + color = "green"; |
| 35 | + else if (is_basic_block(bb)) |
| 36 | + color = "blue"; |
| 37 | + |
| 38 | + String label = ""; |
| 39 | + |
| 40 | + String abs_name = get_abstraction_name_unsafe(bb); |
| 41 | + if (!abs_name) |
| 42 | + abs_name = format_string_interned(bb->arena, "%%%d", bb->id); |
| 43 | + label = format_string_arena(bb->arena->arena, "%s: %s", abs_name, label); |
| 44 | + |
| 45 | + const CFNode* let_chain_end = n; |
| 46 | + while (body->tag == Let_TAG) { |
| 47 | + const Node* instr = body->payload.let.instruction; |
| 48 | + // label = ""; |
| 49 | + if (instr->tag == PrimOp_TAG) |
| 50 | + label = format_string_arena(bb->arena->arena, "%slet ... = %s (...)\n", label, get_primop_name(instr->payload.prim_op.op)); |
| 51 | + else |
| 52 | + label = format_string_arena(bb->arena->arena, "%slet ... = %s (...)\n", label, node_tags[instr->tag]); |
| 53 | + |
| 54 | + const Node* abs = body->payload.let.tail; |
| 55 | + label = format_string_arena(bb->arena->arena, "%s%%%d: ", label, abs->id); |
| 56 | + |
| 57 | + if (entries_count_list(let_chain_end->succ_edges) != 1 || read_list(CFEdge, let_chain_end->succ_edges)[0].type != LetTailEdge) |
| 58 | + break; |
| 59 | + |
| 60 | + let_chain_end = read_list(CFEdge, let_chain_end->succ_edges)[0].dst; |
| 61 | + assert(let_chain_end->node == abs); |
| 62 | + assert(is_case(abs)); |
| 63 | + body = get_abstraction_body(abs); |
| 64 | + } |
| 65 | + |
| 66 | + label = format_string_arena(bb->arena->arena, "%s%s", label, node_tags[body->tag]); |
| 67 | + |
| 68 | + fprintf(output, "bb_%zu [label=\"%s\", color=\"%s\", shape=box];\n", (size_t) n, label, color); |
| 69 | + |
| 70 | + for (size_t i = 0; i < entries_count_list(n->dominates); i++) { |
| 71 | + CFNode* d = read_list(CFNode*, n->dominates)[i]; |
| 72 | + if (!find_key_dict(const Node*, n->structurally_dominates, d->node)) |
| 73 | + dump_cf_node(output, d); |
| 74 | + } |
| 75 | +} |
| 76 | + |
| 77 | +static void dump_cfg_scope(FILE* output, Scope* scope) { |
| 78 | + extra_uniqueness++; |
| 79 | + |
| 80 | + const Node* entry = scope->entry->node; |
| 81 | + fprintf(output, "subgraph cluster_%s {\n", get_abstraction_name(entry)); |
| 82 | + fprintf(output, "label = \"%s\";\n", get_abstraction_name(entry)); |
| 83 | + for (size_t i = 0; i < entries_count_list(scope->contents); i++) { |
| 84 | + const CFNode* n = read_list(const CFNode*, scope->contents)[i]; |
| 85 | + dump_cf_node(output, n); |
| 86 | + } |
| 87 | + for (size_t i = 0; i < entries_count_list(scope->contents); i++) { |
| 88 | + const CFNode* bb_node = read_list(const CFNode*, scope->contents)[i]; |
| 89 | + const CFNode* src_node = bb_node; |
| 90 | + while (true) { |
| 91 | + const CFNode* let_parent = get_let_pred(src_node); |
| 92 | + if (let_parent) |
| 93 | + src_node = let_parent; |
| 94 | + else |
| 95 | + break; |
| 96 | + } |
| 97 | + |
| 98 | + for (size_t j = 0; j < entries_count_list(bb_node->succ_edges); j++) { |
| 99 | + CFEdge edge = read_list(CFEdge, bb_node->succ_edges)[j]; |
| 100 | + const CFNode* target_node = edge.dst; |
| 101 | + |
| 102 | + if (edge.type == LetTailEdge && get_let_pred(target_node) == bb_node) |
| 103 | + continue; |
| 104 | + |
| 105 | + String edge_color = "black"; |
| 106 | + switch (edge.type) { |
| 107 | + case LetTailEdge: edge_color = "green"; break; |
| 108 | + case StructuredEnterBodyEdge: edge_color = "blue"; break; |
| 109 | + case StructuredLeaveBodyEdge: edge_color = "red"; break; |
| 110 | + case StructuredPseudoExitEdge: edge_color = "darkred"; break; |
| 111 | + default: break; |
| 112 | + } |
| 113 | + |
| 114 | + fprintf(output, "bb_%zu -> bb_%zu [color=\"%s\"];\n", (size_t) (src_node), (size_t) (target_node), edge_color); |
| 115 | + } |
| 116 | + } |
| 117 | + fprintf(output, "}\n"); |
| 118 | +} |
| 119 | + |
| 120 | +void dump_cfg(FILE* output, Module* mod) { |
| 121 | + if (output == NULL) |
| 122 | + output = stderr; |
| 123 | + |
| 124 | + fprintf(output, "digraph G {\n"); |
| 125 | + struct List* scopes = build_scopes(mod); |
| 126 | + for (size_t i = 0; i < entries_count_list(scopes); i++) { |
| 127 | + Scope* scope = read_list(Scope*, scopes)[i]; |
| 128 | + dump_cfg_scope(output, scope); |
| 129 | + destroy_scope(scope); |
| 130 | + } |
| 131 | + destroy_list(scopes); |
| 132 | + fprintf(output, "}\n"); |
| 133 | +} |
| 134 | + |
| 135 | +void dump_cfg_auto(Module* mod) { |
| 136 | + FILE* f = fopen("cfg.dot", "wb"); |
| 137 | + dump_cfg(f, mod); |
| 138 | + fclose(f); |
| 139 | +} |
0 commit comments