compiler

Unnamed Compiled Systems Language Project
git clone http://frotz.net/git/compiler.git
Log | Files | Refs

commit c3efba3073f91cd64ab40b4bf7ce1607e384f70e
parent 59b31e0490f97b652d6c3ab315c2824c434dd968
Author: Brian Swetland <swetland@frotz.net>
Date:   Sat, 18 Dec 2021 12:23:27 -0800

ir: control flow graph work

- wrap the instruction stream in BBlocks that represent basic blocks
- BBlocks form a graph traversable forward or backward
- BBlocks also form a list to allow "visit every BBlock once"
- nodes each BBlock tracks a list of instructions traversable forward or backward
- fix up the label_idx table to remove redirects during opt_func_label_cleanup()
- enable some verbose debug chatter for now:
- write IR disassembly at various stages to debug/...
- write a graphviz graph of BB/CF to debug/..
- fix incorrect disassembly of LD/ST instructions after opcode bitflags change

Diffstat:
Msrc/codegen-ir.c | 180++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------
Msrc/ir.h | 20++++++++++++++++++++
2 files changed, 186 insertions(+), 14 deletions(-)

diff --git a/src/codegen-ir.c b/src/codegen-ir.c @@ -67,6 +67,7 @@ i32 reg_get() { // table of labels i32 label_next = 0; Inst label_idx[1024]; +BBlock bb_idx[1024]; Inst _inst_make(u32 op, i32 a, i32 b, i32 c) { // add opcode-specific property bitflags to allow @@ -675,6 +676,18 @@ Inst gen_func(Ast node) { void inst_disasm(FILE* fp, Inst ins); +i32 bb_id_counter = 0; + +BBlock make_bblock(i32 pcount) { + i32 sz = sizeof(BBlockRec) + pcount * 4; + BBlock bb = malloc(sz); + memset(bb, 0, sz); + //bb->pcount = pcount; + bb->id = bb_id_counter; + bb_id_counter++; + return bb; +} + bool inst_is_label(Inst ins) { return ins->op & INF_IS_LABEL; } @@ -724,7 +737,7 @@ Inst opt_inst_destroy(Inst ins) { // - insert labels after flow control departures from straightline // if such a label does not already exist // - remove code between unconditional branches and labels -void opt_func_label_cleanup(Inst first, Inst last) { +BBlock opt_func_label_cleanup(Inst first, Inst last) { // skip the global label and start with the local label L0 Inst ins = first->next; while (ins != last) { @@ -757,7 +770,8 @@ void opt_func_label_cleanup(Inst first, Inst last) { if (inst_is_label(ins)) { if ((ins->c == 0) && !(inst_is_cond_branch(ins->prev))) { // nobody jumps here - // mark label as unused? remove from idx? + // remove label + label_idx[ins->a] = nil; ins = opt_inst_destroy(ins); continue; } @@ -781,23 +795,143 @@ void opt_func_label_cleanup(Inst first, Inst last) { ins_last_placed->next = next; next->prev = ins_last_placed; } + if (inst_is_return(ins)) { + // returns are treated as B Llast + // from a flow-control standpoint + last->c++; + } + ins = ins->next; + } + + // for each non-redirected label, create a basic block + // sized for the max possible inbound edges of that label + i32 n = 0; + BBlock bblast = nil; + while (n < label_next) { + if ((label_idx[n] != nil) && (label_idx[n]->a == n)) { + BBlock bb = make_bblock(label_idx[n]->c + 1); + bb_idx[n] = bb; + bb_idx[n]->first = label_idx[n]; + if (bblast != nil) { + bblast->list = bb; + } + bblast = bb; + } else { + bb_idx[n] = nil; + } + n++; + } + + // For each branch instruction, fixup any indirections + // so they point at their true target label. + // Hook up prev[] and next[] pointers between bblocks + ins = first->next; + BBlock bb = bb_idx[0]; + bb->flags |= BB_FIRST; + while (ins != nil) { + if (inst_is_label(ins)) { + bb->last = ins; + BBlock nextbb = bb_idx[ins->a]; + // if control flows from previous instruction + // (last in prev bb) to this one, bidirectionally + // link us + if (!inst_is_uncond_branch(ins->prev) && + !inst_is_return(ins->prev)) { + nextbb->prev[nextbb->pcount] = bb; + nextbb->pcount++; + if (bb->next[0] == nil) { + bb->next[0] = nextbb; + } else { + bb->next[1] = nextbb; + } + } + bb = nextbb; + } else if (inst_is_branch(ins)) { + Inst target = label_idx[ins->a]; + if (ins->a != target->a) { + // we've been redirected, let's fix up + ins->a = target->a; + } + BBlock tbb = bb_idx[target->a]; + + // we're branching from bb to tbb + tbb->prev[tbb->pcount] = bb; + tbb->pcount++; + if (bb->next[0] == nil) { + bb->next[0] = tbb; + } else { + bb->next[1] = tbb; + } + } else if (inst_is_return(ins)) { + BBlock tbb = bb_idx[last->a]; + + // return is like a branch to the last label/bb + tbb->prev[tbb->pcount] = bb; + tbb->pcount++; + if (bb->next[0] == nil) { + bb->next[0] = tbb; + } else { + bb->next[1] = tbb; + } + } ins = ins->next; } + + bb->flags |= BB_LAST; + + // first bb has pcount 0 but prev[0] points to last for convenience + bb_idx[0]->prev[0] = bb; + return bb_idx[0]; } -void opt_func(Ast node, Inst first, Inst last) { - opt_func_label_cleanup(first, last); +BBlock opt_func(Ast node, Inst first, Inst last) { + BBlock bblist = opt_func_label_cleanup(first, last); + return bblist; } + void dis_func(FILE* fp, Inst ins, str name) { - fprintf(stderr,"\n------------------\n"); - fprintf(stderr,"@%s:\n", name); + fprintf(fp,"@%s:\n", name); while (ins != nil) { - inst_disasm(stderr, ins); + inst_disasm(fp, ins); ins = ins->next; } } +void graph_func(FILE* fp, str fn, BBlock blocks) { + fprintf(fp, + "digraph g {\n" + "graph [ rankdir=TB; ];\n" + "node [ shape=plain; ];\n"); + BBlock bb = blocks; + while (bb != nil) { + fprintf(fp, "\"%p\" [ label=<<TABLE BORDER=\"0\" CELLBORDER=\"1\" CELLSPACING=\"0\">\n", bb); + Inst ins = bb->first; + if (bb == blocks) { + fprintf(fp,"<TR><TD BGCOLOR=\"gray\" ALIGN=\"left\">@%s:</TD></TR>\n", fn); + } + while (ins != bb->last) { + if (inst_is_label(ins)) { + fprintf(fp,"<TR><TD BGCOLOR=\"gray\" ALIGN=\"left\">"); + } else { + fprintf(fp,"<TR><TD ALIGN=\"left\">"); + } + inst_disasm(fp, ins); + fprintf(fp,"</TD></TR>\n"); + ins = ins->next; + } + fprintf(fp, "</TABLE>>; ];\n"); + if (bb->next[0]) { + fprintf(fp, "\"%p\" -> \"%p\" [ ];\n", bb, bb->next[0]); + } + if (bb->next[1]) { + fprintf(fp, "\"%p\" -> \"%p\" [ ];\n", bb, bb->next[1]); + } + bb = bb->list; + } + fprintf(fp, "}\n"); +} + void gen_program(Ast node) { gen_trace( "gen_risc5_simple()\n"); @@ -806,11 +940,29 @@ void gen_program(Ast node) { node = node->c2; while (node != nil) { if (node->kind == AST_FUNC) { + str fn = node->sym->name->text; Inst first = gen_func(node); Inst last = ins_last_placed; - dis_func(stderr, first, node->sym->name->text); - opt_func(node, first, last); - dis_func(stderr, first, node->sym->name->text); + char tmp[256]; + sprintf(tmp, "debug/%s.ast.ir", fn); + FILE* fp = fopen(tmp, "w"); + if (fp != nil) { + dis_func(fp, first, fn); + fclose(fp); + } + BBlock bblocks = opt_func(node, first, last); + sprintf(tmp, "debug/%s.bb.ir", fn); + fp = fopen(tmp, "w"); + if (fp != nil) { + dis_func(fp, first, fn); + fclose(fp); + } + sprintf(tmp, "debug/%s.bb.dot", fn); + fp = fopen(tmp, "w"); + if (fp != nil) { + graph_func(fp, fn, bblocks); + fclose(fp); + } } node = node->c2; } @@ -852,14 +1004,14 @@ void inst_disasm(FILE* fp, Inst ins) { //fprintf(fp, "(%08x %08x %08x %08x)\n", ins->op, ins->a, ins->b, ins->c); if (op == INS_LABEL) { prlabel(fp, ins->a); - fprintf(fp, ": // count=%u", ins->c); + fprintf(fp, ":"); // count=%u", ins->c); } else if (op == INS_MOV) { fprintf(fp, "\tmov "); prreg(fp, ins->a); fprintf(fp, ", "); prarg(fp, ins->c, ins->op); } else if ((op == INS_LD) || (op == INS_ST)) { - str sz = isz_name[(ins->op & INF_SZ_MASK) >> 8]; + str sz = isz_name[(ins->op & INF_SZ_MASK) >> INF_SZ_SHIFT]; fprintf(fp, "\t%s%s ", ins_name[op], sz); prreg(fp, ins->a); fprintf(fp, ", ["); @@ -885,10 +1037,10 @@ void inst_disasm(FILE* fp, Inst ins) { fprintf(fp, ", "); prarg(fp, ins->c, ins->op); } else if (op == INS_CALL) { - fprintf(stderr, "\tcall "); + fprintf(fp, "\tcall "); prlabel(fp, ins->a); } else if (op == INS_RET) { - fprintf(stderr, "\tret "); + fprintf(fp, "\tret "); prreg(fp, ins->a); } else { fprintf(fp, "\tinval 0x%x", ins->op); diff --git a/src/ir.h b/src/ir.h @@ -10,6 +10,7 @@ enum { INF_SZ_U16 = 0x0040, INF_SZ_U32 = 0x0080, INF_SZ_MASK = 0x00C0, + INF_SZ_SHIFT = 6, // property bits INF_IS_B_UC = 0x0200, // is uncondition branch @@ -85,7 +86,9 @@ str isz_name[4] = { }; typedef struct InstRec InstRec; +typedef struct BBlockRec BBlockRec; typedef struct InstRec* Inst; +typedef struct BBlockRec* BBlock; struct InstRec { Inst next; @@ -95,3 +98,20 @@ struct InstRec { i32 b; i32 c; }; + +enum { + BB_FIRST = 0x0001, + BB_LAST = 0x0002, +}; + +struct BBlockRec { + Inst first; + Inst last; + i32 flags; + i32 id; + + i32 pcount; + BBlock list; + BBlock next[2]; + BBlock prev[0]; +};