From: David Given Date: Mon, 12 Dec 2016 22:38:18 +0000 (+0100) Subject: IR imports are now analysed and split so that all IRs imported from another X-Git-Url: https://git.ndcode.org/public/gitweb.cgi?a=commitdiff_plain;h=4f0011329857580b4d5de902d807213f1582cf02;p=ack.git IR imports are now analysed and split so that all IRs imported from another block come via a phi and from a direct predecessor. Add a separate pass for calculate use-def data on vregs. Add a pass for (attempting to) split vregs before and after instructions. --- diff --git a/mach/proto/mcg/mcg.h b/mach/proto/mcg/mcg.h index 6ea7c6157..b0c897eb1 100644 --- a/mach/proto/mcg/mcg.h +++ b/mach/proto/mcg/mcg.h @@ -105,8 +105,11 @@ extern void tb_fileend(void); extern void tb_procedure(void); extern void tb_regvar(struct procedure* proc, arith offset, int size, int type, int priority); +extern void pass_convert_inputs_to_phis(void); extern void pass_convert_locals_to_ssa(void); +extern void pass_convert_nonlocal_phis(void); extern void pass_convert_stack_ops(void); +extern void pass_determine_vreg_usage(void); extern void pass_eliminate_trivial_blocks(void); extern void pass_find_phi_congruence_groups(void); extern void pass_group_irs(void); @@ -119,6 +122,7 @@ extern void pass_register_allocator(void); extern void pass_remove_dead_blocks(void); extern void pass_remove_dead_phis(void); extern void pass_split_critical_edges(void); +extern void pass_split_live_ranges(void); extern void pass_wire_up_return_values(void); extern void platform_calculate_offsets(void); diff --git a/mach/proto/mcg/pass_inputstophis.c b/mach/proto/mcg/pass_inputstophis.c new file mode 100644 index 000000000..0d4443c10 --- /dev/null +++ b/mach/proto/mcg/pass_inputstophis.c @@ -0,0 +1,99 @@ +#include "mcg.h" + +/* Currently, IRs can refer to other IRs which are either in the current bb or + * else defined in a parent bb (although, we hope, in a strictly dominating + * bb). This makes life awkward in future passes, as it's not obvious where + * each IR is defined. So, we convert these non-local references into phis, + * so ensuring that all IR references are to either the current bb, or via + * a phi to a parent bb. */ + +/* Tests if this node *contains* a non-local reference. */ +static bool find_non_local_direct_reference_cb(struct ir* ir, void* user) +{ + if (ir->left && (ir->left->bb != ir->bb)) + return true; + if (ir->right && (ir->right->bb != ir->bb)) + return true; + return false; +} + +/* Returns the non-local reference itself. */ +static struct ir* find_non_local_direct_reference(struct basicblock* bb) +{ + int i; + + for (i=0; iirs.count; i++) + { + struct ir* ir = ir_walk(bb->irs.item[i], + find_non_local_direct_reference_cb, bb); + if (ir) + { + if (ir->left && (ir->left->bb != bb)) + return ir->left; + if (ir->right && (ir->right->bb != bb)) + return ir->right; + } + } + + return NULL; +} + +static bool is_defined_in_prev_block(struct basicblock* bb, struct ir* ir) +{ + int i; + + for (i=0; iprevs.count; i++) + if (bb->prevs.item[i] == ir->bb) + return true; + + return false; +} + +static struct ir* insert_phis_and_rewrite(struct basicblock* bb, struct ir* src) +{ + int i; + struct ir* dest; + + dest = new_ir0(IR_PHI, src->size); + dest->root = dest; + dest->bb = bb; + + tracef('I', "I: in %s, inserting phi $%d for $%d (from %s)\n", + bb->name, dest->id, src->id, src->bb->name); + + pmap_add(&dest->u.phivalue, src->bb, src); + array_insert(&bb->irs, dest, 0); + + ir_rewrite(bb, src, dest); + + if (is_defined_in_prev_block(bb, src)) + return dest; + + return dest; +} + +void pass_convert_inputs_to_phis(void) +{ + int i; + + /* Insert new phi nodes for any use of a non-local IR in an ordinary + * instruction. */ + + for (i=0; iid, bb->name, ir->bb->name); + insert_phis_and_rewrite(bb, ir); + } + } +} + +/* vim: set sw=4 ts=4 expandtab : */ diff --git a/mach/proto/mcg/pass_nonlocalphis.c b/mach/proto/mcg/pass_nonlocalphis.c new file mode 100644 index 000000000..ab364d4b9 --- /dev/null +++ b/mach/proto/mcg/pass_nonlocalphis.c @@ -0,0 +1,193 @@ +#include "mcg.h" + +/* Phis can import IRs defined in any IR which strictly dominates the current + * one. This makes life awkward for future passes, as it's not obvious what's + * defined where, so we convert these non-local references into phis, + * so ensuring that all IR references are to either the current bb, or via + * a phi to a parent bb. */ + +static ARRAYOF(struct basicblock) confirmed; +static ARRAYOF(struct basicblock) pending; +static PMAPOF(struct ir, struct ir) added; + +static struct basicblock* current_src; +static struct basicblock* current_dest; +static struct ir* current_ir; + +static bool is_defined_in_prev_block(struct basicblock* bb, struct ir* ir) +{ + int i; + + for (i=0; iprevs.count; i++) + if (bb->prevs.item[i] == ir->bb) + return true; + + return false; +} + +static void recursively_add_children_to_pending(struct basicblock* bb) +{ + int i; + + for (i=0; iprevs.count; i++) + { + struct basicblock* candidate = bb->prevs.item[i]; + + if ((candidate != current_src) + && (candidate != current_dest) + && !array_appendu(&pending, candidate)) + recursively_add_children_to_pending(candidate); + } +} + +static void recursively_move_children_to_confirmed(struct basicblock* bb) +{ + int i; + + for (i=0; inexts.count; i++) + { + struct basicblock* candidate = bb->nexts.item[i]; + + if (array_contains(&pending, candidate)) + { + tracef('I', "I: encompassing %s\n", candidate->name); + array_remove(&pending, candidate); + array_appendu(&confirmed, candidate); + recursively_move_children_to_confirmed(candidate); + } + } +} + +static struct ir* new_phi(struct basicblock* bb, int size, struct ir* src) +{ + struct ir* phi = new_ir0(IR_PHI, size); + phi->root = phi; + phi->bb = bb; + + if (src) + pmap_add(&phi->u.phivalue, src->bb, src); + array_insert(&bb->irs, phi, 0); + + return phi; +} + +static struct ir* insert_phi_to_prev(struct basicblock* bb, int size, struct ir* src) +{ + struct ir* phi = new_phi(bb, size, src); + tracef('I', "I: adding phi $%d for $%d in %s\n", + phi->id, src->id, bb->name); + ir_rewrite_single_block(bb, src, phi); + return phi; +} + +static bool replace_irs_cb(struct ir* ir, void* user) +{ + int i; + bool* found = user; + + if (*found) + return true; + if (ir->opcode != IR_PHI) + return true; + + for (i=0; iu.phivalue.count; i++) + { + if (pmap_contains_bi(&added, current_ir, ir->u.phivalue.item[i].right)) + { + *found = true; + return true; + } + } + + return false; +} + +static bool already_importing(struct basicblock* bb) +{ + int i; + + for (i=0; iirs.count; i++) + { + bool found = false; + ir_walk(bb->irs.item[i], replace_irs_cb, &found); + if (found) + return true; + } + + return false; +} + +static void import_ir(struct ir* phi) +{ + int i; + + confirmed.count = 0; + pending.count = 0; + + recursively_add_children_to_pending(current_dest); + array_appendu(&confirmed, current_dest); + recursively_move_children_to_confirmed(current_src); + + /* Remove the original source from the phi. */ + + ir_print('I', phi); + pmap_remove_either(&phi->u.phivalue, current_ir); + + /* For every prev which is in our confirmed list, make sure + * that prev exports an IR, and add that IR to the phi. */ + + for (i=0; iprevs.count; i++) + { + struct basicblock* bb = current_dest->prevs.item[i]; + + if (bb == current_src) + pmap_add(&phi->u.phivalue, bb, current_ir); + else if (array_contains(&confirmed, bb) && !already_importing(bb)) + { + struct ir* newphi = insert_phi_to_prev(bb, current_ir->size, current_ir); + pmap_add(&phi->u.phivalue, bb, newphi); + pmap_add(&added, current_ir, newphi); + array_remove(&confirmed, bb); + } + } + ir_print('I', phi); +} + +void pass_convert_nonlocal_phis(void) +{ + int i, j, k; + + added.count = 0; + + /* If a phi refers to an IR defined in a node which isn't a direct parent, + * insert phis upstream for it. */ + +restart: + for (i=0; iirs.count; j++) + { + struct ir* phi = current_dest->irs.item[j]; + if (phi->opcode == IR_PHI) + { + for (k=0; ku.phivalue.count; k++) + { + current_ir = phi->u.phivalue.item[k].right; + current_src = current_ir->bb; + + if (!array_contains(¤t_dest->prevs, current_src)) + { + tracef('I', "I: import of non-local IR $%d into %s from %s\n", + current_ir->id, current_dest->name, current_src->name); + import_ir(phi); + goto restart; + } + } + } + } + } +} + +/* vim: set sw=4 ts=4 expandtab : */ diff --git a/mach/proto/mcg/pass_splitliveranges.c b/mach/proto/mcg/pass_splitliveranges.c new file mode 100644 index 000000000..50417d5a8 --- /dev/null +++ b/mach/proto/mcg/pass_splitliveranges.c @@ -0,0 +1,119 @@ +#include "mcg.h" + +static struct basicblock* current_bb; + +static void rewrite_vregs(struct basicblock* bb, + int pos, struct vreg* src, struct vreg* dest) +{ + int i, j; + + while (pos < bb->hops.count) + { + struct hop* hop = bb->hops.item[pos]; + + array_replace(&hop->ins, src, dest); + array_replace(&hop->throughs, src, dest); + array_replace(&hop->outs, src, dest); + + for (i=0; iinsels.count; i++) + { + struct insel* insel = hop->insels.item[i]; + if ((insel->type == INSEL_VREG) && (insel->u.vreg == src)) + insel->u.vreg = dest; + } + + for (i=0; iconstraints.count; i++) + { + struct constraint* c = hop->constraints.item[i].right; + + if (hop->constraints.item[i].left == src) + hop->constraints.item[i].left = dest; + + if (c->equals_to == src) + c->equals_to = dest; + } + + pos++; + } + + for (i=0; inexts.count; i++) + { + struct basicblock* nextbb = bb->nexts.item[i]; + + for (j=0; jphis.count; j++) + { + struct phi* phi = nextbb->phis.item[j].right; + if (phi->ir->result == src) + phi->ir->result = dest; + } + } +} + +static void rewrite_blocks(struct basicblock* startbb, int startindex, + struct vreg* src, struct vreg* dest) +{ + int i; + + for (i=0; itype = src->type; + copy = new_copy_hop(current_bb, src, dest); + + array_insert(¤t_bb->hops, copy, index+1); + + rewrite_blocks(current_bb, index+2, src, dest); + return 1; +} + +void pass_split_live_ranges(void) +{ + int i, j, k; + + for (i=0; ihops.count; j++) + { + struct hop* hop = current_bb->hops.item[j]; + + if (!hop->is_move) + { + for (k=0; kins.count; k++) + { + struct vreg* vreg = hop->ins.item[k]; + j += insert_move_after(j-1, vreg); + } + + for (k=0; kouts.count; k++) + { + struct vreg* vreg = hop->outs.item[k]; + insert_move_after(j, vreg); + } + } + } + } +} + +/* vim: set sw=4 ts=4 expandtab : */ \ No newline at end of file diff --git a/mach/proto/mcg/pass_vregusage.c b/mach/proto/mcg/pass_vregusage.c new file mode 100644 index 000000000..b1fd6ee31 --- /dev/null +++ b/mach/proto/mcg/pass_vregusage.c @@ -0,0 +1,64 @@ +#include "mcg.h" + +static ARRAYOF(struct vreg) vregs; + +static void assign_uses_cb(struct hop* hop, void* user) +{ + int i; + + for (i=0; iins.count; i++) + array_appendu(&hop->ins.item[i]->usedhops, hop); + + for (i=0; iouts.count; i++) + { + struct vreg* vreg = hop->outs.item[i]; + assert(vreg->defined == NULL); + vreg->defined = hop; + array_appendu(&vregs, vreg); + } +} + +static bool is_spillable_vreg(struct vreg* vreg) +{ + int i; + + if (vreg->defined && !vreg->defined->is_move) + return false; + + for (i=0; iusedhops.count; i++) + if (!vreg->usedhops.item[i]->is_move) + return false; + + return true; +} + +void pass_determine_vreg_usage(void) +{ + int i, j; + + vregs.count = 0; + hop_walk(assign_uses_cb, NULL); + + for (i=0; iphis.count; j++) + { + struct vreg* dest = bb->phis.item[j].left; + struct phi* phi = bb->phis.item[j].right; + struct vreg* src = phi->ir->result; + array_appendu(&src->usedphis, bb); + array_appendu(&vregs, dest); + } + } + + for (i=0; iis_spillable = is_spillable_vreg(vreg); + } +} + +/* vim: set sw=4 ts=4 expandtab : */ + +