From: David Given Date: Sun, 2 Oct 2016 15:50:34 +0000 (+0200) Subject: Perform SSA conversion of locals. Much, *much* better code now, at least X-Git-Url: https://git.ndcode.org/public/gitweb.cgi?a=commitdiff_plain;h=c079e974928bf9f983fe8c1ebfaae80def45a6f8;p=ack.git Perform SSA conversion of locals. Much, *much* better code now, at least inasmuch as it looks better before register allocation. Basic blocks now know their own successors and predecessors (after a certain point in the IR processing). --- diff --git a/mach/proto/mcg/basicblock.c b/mach/proto/mcg/basicblock.c index e44f553ee..5a30d9a90 100644 --- a/mach/proto/mcg/basicblock.c +++ b/mach/proto/mcg/basicblock.c @@ -24,7 +24,7 @@ struct basicblock* bb_get(const char* name) p = str2idf((char*) name, 0); if (!p->block) { - p->block = calloc(sizeof(struct basicblock), 1); + p->block = calloc(1, sizeof(*p->block)); p->block->name = name; } return p->block; @@ -39,8 +39,4 @@ void bb_alias(struct basicblock* block, const char* name) p->block = block; } -void bb_print(char k, struct basicblock* block) -{ -} - /* vim: set sw=4 ts=4 expandtab : */ diff --git a/mach/proto/mcg/basicblock.h b/mach/proto/mcg/basicblock.h new file mode 100644 index 000000000..8f36e5eee --- /dev/null +++ b/mach/proto/mcg/basicblock.h @@ -0,0 +1,25 @@ +#ifndef BASICBLOCK_H +#define BASICBLOCK_H + +struct basicblock +{ + const char* name; + ARRAYOF(struct em) ems; + ARRAYOF(struct ir) irs; + ARRAYOF(struct hop) hops; + + ARRAYOF(struct basicblock) prevs; + ARRAYOF(struct basicblock) nexts; + int order; /* used by SSA code */ + + bool is_fake : 1; + bool is_root : 1; + bool is_terminated : 1; +}; + +extern void bb_init(void); +extern struct basicblock* bb_get(const char* name); +extern void bb_alias(struct basicblock* block, const char* name); + +#endif + diff --git a/mach/proto/mcg/ir.c b/mach/proto/mcg/ir.c index 0aea3ea50..de3ec396f 100644 --- a/mach/proto/mcg/ir.c +++ b/mach/proto/mcg/ir.c @@ -107,7 +107,7 @@ static void print_expr(char k, const struct ir* ir) tracef(k, "%s", ir_data[ir->opcode].name); if (ir->size) tracef(k, "%d", ir->size); - tracef(k, ":%d(", ir->id); + tracef(k, "("); switch (ir->opcode) { diff --git a/mach/proto/mcg/main.c b/mach/proto/mcg/main.c index 5d450bdfc..bf722f4f9 100644 --- a/mach/proto/mcg/main.c +++ b/mach/proto/mcg/main.c @@ -5,10 +5,15 @@ bool tracing(char k) switch (k) { case 0: return true; + case 'S': return true; case 'E': return false; + case 'G': return true; case '0': return false; case '1': return false; case '2': return false; + case '3': return false; + case '4': return false; + case '5': return false; case 'I': return true; default: return true; } diff --git a/mach/proto/mcg/mcg.h b/mach/proto/mcg/mcg.h index 78560499b..ba23e2d4f 100644 --- a/mach/proto/mcg/mcg.h +++ b/mach/proto/mcg/mcg.h @@ -24,6 +24,7 @@ #include "ir.h" #include "mcgg.h" #include "hop.h" +#include "basicblock.h" #include "procedure.h" extern char em_pseu[][4]; @@ -73,17 +74,6 @@ struct em } u; }; -struct basicblock -{ - const char* name; - ARRAYOF(struct em) ems; - ARRAYOF(struct ir) irs; - ARRAYOF(struct hop) hops; - bool is_fake : 1; - bool is_root : 1; - bool is_terminated : 1; -}; - extern const char* aprintf(const char* fmt, ...); extern void tracef(char k, const char* fmt, ...); extern bool tracing(char k); @@ -104,11 +94,6 @@ extern void data_block(const uint8_t* data, size_t size, bool is_ro); extern void data_offset(const char* label, arith offset, bool is_ro); extern void data_bss(arith size, int init); -extern void bb_init(void); -extern struct basicblock* bb_get(const char* name); -extern void bb_alias(struct basicblock* block, const char* name); -extern void bb_print(char k, struct basicblock* block); - extern void tb_filestart(void); extern void tb_fileend(void); extern void tb_procedure(struct procedure* proc); @@ -120,6 +105,7 @@ extern void pass_eliminate_trivial_blocks(struct procedure* proc); extern void pass_instruction_selector(struct procedure* proc); extern void pass_promote_float_ops(struct procedure* proc); extern void pass_group_irs(struct procedure* proc); +extern void pass_convert_locals_to_ssa(struct procedure* proc); #endif diff --git a/mach/proto/mcg/pass_convertstackops.c b/mach/proto/mcg/pass_convertstackops.c index f5764484a..d555b58ba 100644 --- a/mach/proto/mcg/pass_convertstackops.c +++ b/mach/proto/mcg/pass_convertstackops.c @@ -41,28 +41,6 @@ static struct ir* get_first_pop(struct basicblock* bb) return NULL; } -static bool collect_outputs_cb(struct ir* ir, void* user) -{ - struct basicblock* caller = user; - - if (ir->opcode == IR_BLOCK) - pmap_add(&graph, caller, ir->u.bvalue); - return false; -} - -static void make_bb_graph(struct procedure* proc) -{ - int i, j; - - graph.count = 0; - for (i=0; iblocks.count; i++) - { - struct basicblock* bb = proc->blocks.item[i]; - for (j=0; jirs.count; j++) - ir_walk(bb->irs.item[j], collect_outputs_cb, bb); - } -} - static void convert_block(struct procedure* proc, struct basicblock* bb) { int i, j; @@ -78,32 +56,26 @@ static void convert_block(struct procedure* proc, struct basicblock* bb) /* Abort unless *every* successor block of this one starts with a pop * of the same size... */ - for (i=0; inexts.count; i++) { - if (graph.item[i].left == bb) + struct basicblock* outbb = bb->nexts.item[i]; + + ir = get_first_pop(outbb); + if (!ir || (ir->size != lastpush->size)) + return; + array_appendu(&pops, ir); + + /* Also abort unless *every* predecessor block of the one we've + * just found *also* ends in a push of the same size. */ + + for (j=0; jprevs.count; j++) { - struct basicblock* outbb = graph.item[i].right; + struct basicblock* inbb = outbb->prevs.item[j]; - ir = get_first_pop(outbb); + ir = get_last_push(inbb); if (!ir || (ir->size != lastpush->size)) return; - array_appendu(&pops, ir); - - /* Also abort unless *every* predecessor block of the one we've - * just found *also* ends in a push of the same size. */ - - for (j=0; jsize != lastpush->size)) - return; - array_appendu(&pushes, ir); - } - } + array_appendu(&pushes, ir); } } @@ -139,8 +111,6 @@ void pass_convert_stack_ops(struct procedure* proc) { int i; - make_bb_graph(proc); - for (i=0; iblocks.count; i++) convert_block(proc, proc->blocks.item[i]); } diff --git a/mach/proto/mcg/pass_ssa.c b/mach/proto/mcg/pass_ssa.c new file mode 100644 index 000000000..87a0b973e --- /dev/null +++ b/mach/proto/mcg/pass_ssa.c @@ -0,0 +1,340 @@ +#include "mcg.h" + +static struct basicblock* entry; +static ARRAYOF(struct basicblock) postorder; +static PMAPOF(struct basicblock, struct basicblock) dominators; +static PMAPOF(struct basicblock, struct basicblock) dominancefrontiers; + +static struct local* current_local; +static ARRAYOF(struct basicblock) defining; +static ARRAYOF(struct basicblock) needsphis; +static ARRAYOF(struct ir) definitions; +static ARRAYOF(struct basicblock) rewritten; + +static void recursively_walk_blocks(struct basicblock* bb); + +static void recursively_walk_graph_postorder(struct basicblock* bb) +{ + static ARRAYOF(struct basicblock) pending; + int i; + + if (array_contains(&postorder, bb) || array_contains(&pending, bb)) + return; + + array_appendu(&pending, bb); + + i = 0; + for (i=0; inexts.count; i++) + recursively_walk_graph_postorder(bb->nexts.item[i]); + + array_remove(&pending, bb); + bb->order = postorder.count; + array_appendu(&postorder, bb); +} + +static void walk_graph_postorder() +{ + int i; + + postorder.count = 0; + recursively_walk_graph_postorder(entry); + + for (i=0; iname); + } +} + +static struct basicblock* intersect(struct basicblock* p1, struct basicblock* p2) +{ + while (p1 != p2) + { + while (p1->order < p2->order) + p1 = pmap_get(&dominators, p1); + + while (p2->order < p1->order) + p2 = pmap_get(&dominators, p2); + } + + return p1; +} + +static void calculate_dominance_graph(void) +{ + /* This is the algorithm described here: + * + * Cooper, Keith D., Timothy J. Harvey, and Ken Kennedy. + * "A simple, fast dominance algorithm." + * Software Practice & Experience 4.1-10 (2001): 1-8. + * + * https://www.cs.rice.edu/~keith/EMBED/dom.pdf + */ + + int i, j; + bool changed; + + dominators.count = 0; + + /* The entry block dominates itself. */ + + pmap_put(&dominators, entry, entry); + + do + { + changed = false; + + for (i = postorder.count-2; i >= 0; i--) + { + struct basicblock* b = postorder.item[i]; + struct basicblock* new_idom = NULL; + for (j=0; jprevs.count; j++) + { + struct basicblock* p = b->prevs.item[j]; + + if (!new_idom) + new_idom = p; + else if (pmap_get(&dominators, p)) + new_idom = intersect(p, new_idom); + } + + if (pmap_get(&dominators, b) != new_idom) + { + pmap_put(&dominators, b, new_idom); + changed = true; + } + } + } + while (changed); + + for (i=0; i %s\n", + dominators.item[i].left->name, + dominators.item[i].right->name); + } +} + +static void calculate_dominance_frontier_graph(void) +{ + /* This is the algorithm described here: + * + * Cooper, Keith D., Timothy J. Harvey, and Ken Kennedy. + * "A simple, fast dominance algorithm." + * Software Practice & Experience 4.1-10 (2001): 1-8. + * + * https://www.cs.rice.edu/~keith/EMBED/dom.pdf + */ + + int i, j; + + dominancefrontiers.count = 0; + + for (i=0; iprevs.count >= 2) + { + for (j=0; jprevs.count; j++) + { + struct basicblock* runner = b->prevs.item[j]; + while (runner != dominator) + { + tracef('S', "S: %s is in %s's dominance frontier\n", + b->name, runner->name); + pmap_add(&dominancefrontiers, runner, b); + runner = pmap_get(&dominators, runner); + } + } + } + } + +} + +static bool is_local(struct ir* ir) +{ + return ((ir->opcode == IR_LOAD) && + (ir->left->opcode == IR_LOCAL) && + (ir->left->u.ivalue == current_local->offset)); +} + +static bool rewrite_loads_cb(struct ir* ir, void* user) +{ + struct ir* definition = user; + + /* Rewrite in place where possible. */ + + if (ir->left && is_local(ir->left)) + ir->left = definition; + if (ir->right && is_local(ir->right)) + ir->right = definition; + + /* Otherwise, go via a IR_REG (which should, with luck, turn into no code). */ + if (is_local(ir)) + { + ir->opcode = IR_NOP; + ir->left = definition; + ir->right = NULL; + } + + return false; +} + +/* Walks the tree, rewriting IRs to push new definitions downwards. */ + +static void recursively_rewrite_tree(struct basicblock* bb) +{ + int i; + int defcount = definitions.count; + + if (array_contains(&rewritten, bb)) + return; + array_appendu(&rewritten, bb); + + for (i=0; iirs.count; i++) + { + struct ir* ir = bb->irs.item[i]; + + if (definitions.count > 0) + { + ir_walk(ir, rewrite_loads_cb, definitions.item[definitions.count-1]); + } + + if (((ir->opcode == IR_STORE) && + (ir->left->opcode == IR_LOCAL) && + (ir->left->u.ivalue == current_local->offset) + ) || + ((i == 0) && + (ir->opcode == IR_PHI) && + array_contains(&needsphis, bb))) + { + /* This is a definition. */ + + if (ir->opcode == IR_STORE) + { + ir->opcode = IR_NOP; + ir->left = ir->right; + ir->right = NULL; + } + array_push(&definitions, ir); + } + } + + for (i=0; inexts.count; i++) + { + struct basicblock* nextbb = bb->nexts.item[i]; + struct ir* ir = nextbb->irs.item[0]; + + if ((definitions.count > 0) && + (ir->opcode == IR_PHI) && + array_contains(&needsphis, nextbb)) + { + array_appendu(&ir->u.phivalue, definitions.item[definitions.count-1]); + } + + recursively_rewrite_tree(nextbb); + } + + definitions.count = defcount; +} + +static void ssa_convert(void) +{ + int i, j; + + /* If this is a parameter, synthesise a load/store at the beginning of the + * program to force it into a register. (Unless it's written to it'll + * always be read from the frame.) */ + + if (current_local->offset >= 0) + { + struct ir* ir = new_ir2( + IR_STORE, current_local->size, + new_localir(current_local->offset), + new_ir1( + IR_LOAD, current_local->size, + new_localir(current_local->offset) + ) + ); + + ir->root = ir; + ir->left->root = ir; + ir->right->root = ir; + ir->right->left->root = ir; + array_insert(&entry->irs, ir, 0); + } + + defining.count = 0; + needsphis.count = 0; + + /* Find everwhere where the variable is *defined*. */ + + for (i=0; iirs.count; j++) + { + struct ir* ir = bb->irs.item[j]; + if ((ir->opcode == IR_STORE) && + (ir->left->opcode == IR_LOCAL) && + (ir->left->u.ivalue == current_local->offset)) + { + array_appendu(&defining, bb); + } + } + } + + /* Every block which is in one of the defining block's dominance frontiers + * requires a phi. Remember that adding a phi also adds a definition. */ + + for (i=0; ioffset, dominates->name); + } + } + + /* Add empty phi nodes. */ + + for (i=0; isize); + ir->root = ir; + array_insert(&bb->irs, ir, 0); + } + + /* Now do the rewriting by walking the tree, pushing definitions down the tree. */ + + definitions.count = 0; + rewritten.count = 0; + recursively_rewrite_tree(entry); +} + +void pass_convert_locals_to_ssa(struct procedure* proc) +{ + int i; + + entry = proc->blocks.item[0]; + walk_graph_postorder(); + assert(postorder.count == proc->blocks.count); + calculate_dominance_graph(); + calculate_dominance_frontier_graph(); + + for (i=0; ilocals.count; i++) + { + current_local = proc->locals.item[i].right; + if (current_local->is_register) + ssa_convert(); + } +} + +/* vim: set sw=4 ts=4 expandtab : */ + + diff --git a/mach/proto/mcg/procedure.c b/mach/proto/mcg/procedure.c index dc11e0a43..5ea0372b9 100644 --- a/mach/proto/mcg/procedure.c +++ b/mach/proto/mcg/procedure.c @@ -27,15 +27,67 @@ void procedure_compile(struct procedure* proc) print_blocks('1', proc); pass_group_irs(proc); + /* Passes from here on must preserve IR grouping */ + pass_eliminate_trivial_blocks(proc); pass_remove_dead_blocks(proc); + + procedure_update_bb_graph(proc); + /* Passes from here on can't alter the BB graph */ + + print_blocks('2', proc); pass_convert_stack_ops(proc); + print_blocks('3', proc); + pass_convert_locals_to_ssa(proc); + print_blocks('4', proc); pass_promote_float_ops(proc); + print_blocks('5', proc); - print_blocks('2', proc); pass_instruction_selector(proc); } +static bool collect_outputs_cb(struct ir* ir, void* user) +{ + struct basicblock* caller = user; + + if (ir->opcode == IR_BLOCK) + { + array_appendu(&caller->nexts, ir->u.bvalue); + array_appendu(&ir->u.bvalue->prevs, caller); + } + return false; +} + +void procedure_update_bb_graph(struct procedure* proc) +{ + int i, j; + + for (i=0; iblocks.count; i++) + { + struct basicblock* bb = proc->blocks.item[i]; + bb->prevs.count = bb->nexts.count = 0; + } + + for (i=0; iblocks.count; i++) + { + struct basicblock* bb = proc->blocks.item[i]; + for (j=0; jirs.count; j++) + ir_walk(bb->irs.item[j], collect_outputs_cb, bb); + } + + for (i=0; iblocks.count; i++) + { + struct basicblock* bb = proc->blocks.item[i]; + + for (j=0; jnexts.count; j++) + { + tracef('G', "G: graph %s -> %s\n", + bb->name, + bb->nexts.item[j]->name); + } + } +} + /* vim: set sw=4 ts=4 expandtab : */ diff --git a/mach/proto/mcg/table b/mach/proto/mcg/table index 6d3e231d9..d523cfb09 100644 --- a/mach/proto/mcg/table +++ b/mach/proto/mcg/table @@ -39,6 +39,7 @@ PATTERNS int; float; + any; PAIR(BLOCK4, BLOCK4); @@ -69,6 +70,13 @@ PATTERNS prefers int(in) cost 1; + int = NOP4(in:int) + emit "mov %int, %in" + cost 1; + + any = PHI4 + cost 0; + float = in:REG4 prefers float(in) cost 1; @@ -146,6 +154,11 @@ PATTERNS emit "b $false" cost 8; + CJUMPLT(value:cc, PAIR(true:BLOCK4, false:BLOCK4)) + emit "blt $true" + emit "b $false" + cost 8; + CALL(dest:LABEL4) emit "bl $dest" cost 4; diff --git a/util/mcgg/ir.dat b/util/mcgg/ir.dat index b12cf8934..baa4617a6 100644 --- a/util/mcgg/ir.dat +++ b/util/mcgg/ir.dat @@ -6,6 +6,7 @@ S CONST # must be followed by float form S CONSTF S REG +S NOP S LABEL S BLOCK V PAIR