From: David Given Date: Sat, 15 Oct 2016 20:53:56 +0000 (+0200) Subject: Register spilling to the stack frame works, more or less. X-Git-Url: https://git.ndcode.org/public/gitweb.cgi?a=commitdiff_plain;h=7aa60a645136f18174ce4b8b2a87404c6d676aea;p=ack.git Register spilling to the stack frame works, more or less. --- diff --git a/mach/proto/mcg/hop.c b/mach/proto/mcg/hop.c index 3797d7b24..b5eaa0292 100644 --- a/mach/proto/mcg/hop.c +++ b/mach/proto/mcg/hop.c @@ -52,6 +52,27 @@ void hop_add_value_insel(struct hop* hop, struct ir* ir) array_append(&hop->insels, insel); } +void hop_add_st_offset_insel(struct hop* hop, struct hreg* hreg) +{ + struct insel* insel = new_insel(INSEL_ST_OFFSET); + insel->u.hreg = hreg; + array_append(&hop->insels, insel); +} + +void hop_add_ab_offset_insel(struct hop* hop, int offset) +{ + struct insel* insel = new_insel(INSEL_AB_OFFSET); + insel->u.offset = offset; + array_append(&hop->insels, insel); +} + +void hop_add_lb_offset_insel(struct hop* hop, int offset) +{ + struct insel* insel = new_insel(INSEL_LB_OFFSET); + insel->u.offset = offset; + array_append(&hop->insels, insel); +} + void hop_add_eoi_insel(struct hop* hop) { struct insel* insel = new_insel(INSEL_EOI); @@ -74,6 +95,18 @@ void hop_add_insel(struct hop* hop, const char* fmt, ...) hop_add_string_insel(hop, aprintf("%d", va_arg(ap, int))); break; + case 'S': + hop_add_st_offset_insel(hop, va_arg(ap, struct hreg*)); + break; + + case 'A': + hop_add_ab_offset_insel(hop, va_arg(ap, int)); + break; + + case 'L': + hop_add_lb_offset_insel(hop, va_arg(ap, int)); + break; + case 'H': hop_add_hreg_insel(hop, va_arg(ap, struct hreg*)); break; @@ -193,6 +226,18 @@ char* hop_render(struct hop* hop) appendf("%s", insel->u.string); break; + case INSEL_ST_OFFSET: + appendf("(st+%d)", insel->u.hreg->offset); + break; + + case INSEL_AB_OFFSET: + appendf("(ab+%d)", insel->u.offset); + break; + + case INSEL_LB_OFFSET: + appendf("(lb+%d)", insel->u.offset); + break; + case INSEL_VALUE: { struct ir* ir = insel->u.value; diff --git a/mach/proto/mcg/hop.h b/mach/proto/mcg/hop.h index dcc6fe03b..e1f2f6d92 100644 --- a/mach/proto/mcg/hop.h +++ b/mach/proto/mcg/hop.h @@ -7,6 +7,9 @@ enum insel_type INSEL_HREG, INSEL_VREG, INSEL_VALUE, + INSEL_ST_OFFSET, + INSEL_AB_OFFSET, + INSEL_LB_OFFSET, INSEL_EOI }; @@ -19,6 +22,7 @@ struct insel struct hreg* hreg; struct vreg* vreg; struct ir* value; + int offset; } u; }; @@ -53,6 +57,9 @@ extern void hop_add_string_insel(struct hop* hop, const char* string); extern void hop_add_hreg_insel(struct hop* hop, struct hreg* hreg); extern void hop_add_vreg_insel(struct hop* hop, struct vreg* vreg); extern void hop_add_value_insel(struct hop* hop, struct ir* ir); +extern void hop_add_st_offset_insel(struct hop* hop, struct hreg* hreg); +extern void hop_add_ab_offset_insel(struct hop* hop, int offset); +extern void hop_add_lb_offset_insel(struct hop* hop, int offset); extern void hop_add_eoi_insel(struct hop* hop); extern void hop_add_insel(struct hop* hop, const char* fmt, ...); diff --git a/mach/proto/mcg/mcg.h b/mach/proto/mcg/mcg.h index 5cd875ef4..005324d0d 100644 --- a/mach/proto/mcg/mcg.h +++ b/mach/proto/mcg/mcg.h @@ -112,7 +112,7 @@ extern void pass_instruction_selector(void); extern void pass_live_vreg_analysis(void); extern void pass_add_prologue_epilogue(struct procedure* proc); extern void pass_promote_float_ops(struct procedure* proc); -extern void pass_register_allocator(void); +extern void pass_register_allocator(struct procedure* proc); extern void pass_remove_dead_blocks(struct procedure* proc); extern void pass_remove_dead_phis(void); extern void pass_split_critical_edges(struct procedure* proc); @@ -120,6 +120,7 @@ extern void pass_split_critical_edges(struct procedure* proc); extern struct hop* platform_prologue(struct procedure* proc); extern struct hop* platform_epilogue(struct procedure* proc); extern struct hop* platform_move(struct basicblock* bb, struct hreg* src, struct hreg* dest); +extern void platform_calculate_offsets(struct procedure* proc); extern FILE* outputfile; extern FILE* dominance_dot_file; diff --git a/mach/proto/mcg/parse_em.c b/mach/proto/mcg/parse_em.c index 2b88eac81..eaa712a4f 100644 --- a/mach/proto/mcg/parse_em.c +++ b/mach/proto/mcg/parse_em.c @@ -308,7 +308,7 @@ static void parse_pseu(void) current_proc = calloc(sizeof(struct procedure), 1); current_proc->name = strdup(em.em_pnam); current_proc->entry = bb_get(current_proc->name); - current_proc->nlocals = em.em_nlocals; + current_proc->locals_size = em.em_nlocals; code_bb = current_proc->entry; code_bb->is_root = true; array_append(¤t_proc->blocks, code_bb); diff --git a/mach/proto/mcg/pass_instructionselection.c b/mach/proto/mcg/pass_instructionselection.c index 52adc7b34..82d7f9875 100644 --- a/mach/proto/mcg/pass_instructionselection.c +++ b/mach/proto/mcg/pass_instructionselection.c @@ -100,6 +100,26 @@ static void constrain_input_reg(int child, uint32_t attr) get_constraint(vreg)->attrs = attr; } +static uint32_t find_type_from_constraint(uint32_t attr) +{ + /* Looks through the registers and finds a concrete register implementing + * that attribute, and returns the type. We assume that all registers + * implementing an attribute (which anyone is going to ask for, 'volatile' + * doesn't count) will have the same type. TODO: mcgg should check for + * this. */ + + const struct burm_register_data* brd = burm_register_data; + while (brd->name) + { + if (brd->attrs & attr) + return brd->type; + brd++; + } + + fatal("unable to find a register matching attribute 0x%x", attr); + return 0; +} + static void constrain_output_reg(uint32_t attr) { struct vreg* vreg = current_hop->output; @@ -109,6 +129,7 @@ static void constrain_output_reg(uint32_t attr) array_appendu(¤t_hop->outs, vreg); vreg->defined = current_hop; + vreg->type = find_type_from_constraint(attr); get_constraint(vreg)->attrs = attr; } diff --git a/mach/proto/mcg/pass_phigroups.c b/mach/proto/mcg/pass_phigroups.c index c773aa5ad..41fa6e093 100644 --- a/mach/proto/mcg/pass_phigroups.c +++ b/mach/proto/mcg/pass_phigroups.c @@ -34,8 +34,9 @@ static void recursively_associate_group(struct phicongruence* c, struct vreg* vr if (vreg->defined) { struct constraint* constraint = pmap_findleft(&vreg->defined->constraints, vreg); - if ((c->attrs == 0) || (constraint->attrs < c->attrs)) - c->attrs = constraint->attrs; + if (c->type == 0) + c->type = vreg->type; + assert(c->type == vreg->type); array_appendu(&c->definitions, vreg->defined); } diff --git a/mach/proto/mcg/pass_registerallocator.c b/mach/proto/mcg/pass_registerallocator.c index f967e3136..6dadc2c76 100644 --- a/mach/proto/mcg/pass_registerallocator.c +++ b/mach/proto/mcg/pass_registerallocator.c @@ -6,6 +6,8 @@ struct assignment struct vreg* out; }; +static struct procedure* current_proc; + static ARRAYOF(struct hreg) hregs; static PMAPOF(struct vreg, struct hreg) evicted; @@ -42,7 +44,7 @@ static void wire_up_blocks_ins_outs(void) } static struct hreg* allocate_phi_hreg(register_assignment_t* regs, - struct vreg* vreg, uint32_t attrs) + struct vreg* vreg, uint32_t type) { int i; @@ -52,7 +54,7 @@ static struct hreg* allocate_phi_hreg(register_assignment_t* regs, for (i=0; iattrs & attrs)) + if (!pmap_findleft(regs, hreg) && (hreg->type == type)) { /* This one is unused. Use it. */ return hreg; @@ -99,21 +101,41 @@ static struct hreg* evict(struct vreg* vreg) assert(false); } -static bool allocatable_input(struct hreg* hreg, struct vreg* vreg) +static bool type_match(struct hreg* hreg, struct vreg* vreg) { struct constraint* c = pmap_findleft(¤t_hop->constraints, vreg); + if (c) + return (hreg->attrs & c->attrs); + if (vreg->congruence) + return (hreg->type == vreg->congruence->type); + return (hreg->type == vreg->type); +} + +static bool allocatable_stackable_input(struct hreg* hreg, struct vreg* vreg) +{ return !pmap_findleft(current_ins, hreg) && - (!c || (hreg->attrs & c->attrs)); + type_match(hreg, vreg); } -static bool allocatable_output(struct hreg* hreg, struct vreg* vreg) +static bool allocatable_stackable_output(struct hreg* hreg, struct vreg* vreg) { - struct constraint* c = pmap_findleft(¤t_hop->constraints, vreg); return !pmap_findleft(current_outs, hreg) && - (!c || (hreg->attrs & c->attrs)) && + type_match(hreg, vreg) && !(hreg->attrs & current_hop->insndata->corrupts); } +static bool allocatable_input(struct hreg* hreg, struct vreg* vreg) +{ + return allocatable_stackable_input(hreg, vreg) && + !hreg->is_stacked; +} + +static bool allocatable_output(struct hreg* hreg, struct vreg* vreg) +{ + return allocatable_stackable_output(hreg, vreg) && + !hreg->is_stacked; +} + static struct hreg* find_input_reg(struct vreg* vreg) { int i; @@ -174,7 +196,32 @@ static void add_input_register(struct vreg* vreg, struct hreg* hreg) if (hreg) { - if (pmap_findleft(current_ins, hreg) == vreg) + if (hreg->is_stacked) + { + /* This vreg is stacked; we need to put it in a register. That's + * slightly exciting because the vreg might be a through, which + * means we need an output register too... which we might not be + * able to allocate. */ + + if (array_contains(¤t_hop->throughs, vreg)) + { + struct hreg* src = hreg; + hreg = find_through_reg(vreg); + assert(hreg); + pmap_remove(current_ins, src, vreg); + pmap_remove(current_outs, src, vreg); + pmap_add(current_ins, hreg, vreg); + pmap_add(current_outs, hreg, vreg); + return; + } + else + { + /* Not a through. */ + pmap_remove(current_ins, hreg, vreg); + hreg = NULL; + } + } + else if (pmap_findleft(current_ins, hreg) == vreg) { /* Yup, already there. */ } @@ -213,7 +260,7 @@ static void add_output_register(struct vreg* vreg) c = pmap_findleft(¤t_hop->constraints, vreg); if (c->equals_to) { - tracef('R', "R: outputput equality constraint of %%%d to %%%d\n", + tracef('R', "R: output equality constraint of %%%d to %%%d\n", vreg->id, c->equals_to->id); /* This output register is constrained to be in the same hreg as an @@ -262,28 +309,31 @@ static void add_through_register(struct vreg* vreg, struct hreg* hreg) if (hreg) { - bool infree = allocatable_input(hreg, vreg); - bool outfree = allocatable_output(hreg, vreg); + bool infree = allocatable_stackable_input(hreg, vreg); + bool outfree = allocatable_stackable_output(hreg, vreg); + struct vreg* inuse = pmap_findleft(current_ins, hreg); + struct vreg* outuse = pmap_findleft(current_outs, hreg); - if (infree && outfree) + if ((infree || (inuse == vreg)) && + (outfree || (outuse == vreg))) { - /* Register unused --- use it. */ - } - if ((infree || pmap_findleft(current_ins, hreg) == vreg) && - (outfree || pmap_findleft(current_outs, hreg) == vreg)) - { - /* Input and output are either free or already assigned. */ + /* Input and output are either free or already assigned to this + * vreg. */ } else { - /* Nope, can't honour the hint. */ - hreg = NULL; + /* Nope, can't honour the hint. Mark the register as evicted; we'll + * put it in something later (probably a stack slot). */ + + tracef('R', "R: cannot place %%%d in %s, evicting\n", vreg->id, hreg->name); + pmap_put(&evicted, vreg, hreg); + pmap_remove(current_ins, hreg, vreg); + pmap_remove(current_outs, hreg, vreg); + return; } } - if (!hreg) - hreg = find_through_reg(vreg); - + assert(hreg); pmap_put(current_ins, hreg, vreg); pmap_put(current_outs, hreg, vreg); } @@ -302,8 +352,8 @@ static void find_new_home_for_evicted_register(struct vreg* vreg, struct hreg* s { hreg = hregs.item[i]; if ((hreg->type == src->type) && - allocatable_input(hreg, vreg) && - allocatable_output(hreg, vreg)) + allocatable_stackable_input(hreg, vreg) && + allocatable_stackable_output(hreg, vreg)) { goto found; } @@ -311,7 +361,8 @@ static void find_new_home_for_evicted_register(struct vreg* vreg, struct hreg* s /* No more registers --- allocate a stack slot. */ - assert(false); + hreg = new_stacked_hreg(src->type); + array_append(&hregs, hreg); found: tracef('R', "R: evicted %%%d moving to %s\n", vreg->id, hreg->name); @@ -442,7 +493,7 @@ static void assign_hregs_to_vregs(void) if (!pmap_findright(old, vreg)) { struct phicongruence* c = vreg->congruence; - struct hreg* hreg = allocate_phi_hreg(old, vreg, c->attrs); + struct hreg* hreg = allocate_phi_hreg(old, vreg, c->type); tracef('R', "R: import fallback hreg %s for phi input %%%d from %s\n", hreg->name, vreg->id, phi->prev->name); @@ -551,7 +602,8 @@ static int insert_moves(struct basicblock* bb, int index, else { /* Swap. */ - + + assert(false); src = copies.item[0].left; dest = pmap_findleft(&copies, src); hop = create_swap(bb, src, dest); @@ -626,13 +678,43 @@ static void insert_phi_copies(void) } } -void pass_register_allocator(void) +static int pack_stackframe(int stacksize, int size, uint32_t attr) { + int i; + + for (i=0; iis_stacked && (hreg->type & attr)) + { + hreg->offset = stacksize; + stacksize += size; + } + } + + return stacksize; +} + +static void layout_stack_frame(void) +{ + int stacksize = 0; + stacksize = pack_stackframe(stacksize, 8, burm_bytes8_ATTR); + stacksize = pack_stackframe(stacksize, 4, burm_bytes4_ATTR); + stacksize = pack_stackframe(stacksize, 2, burm_bytes2_ATTR); + stacksize = pack_stackframe(stacksize, 1, burm_bytes1_ATTR); + current_proc->spills_size = stacksize; +} + +void pass_register_allocator(struct procedure* proc) +{ + current_proc = proc; + populate_hregs(); wire_up_blocks_ins_outs(); assign_hregs_to_vregs(); insert_phi_copies(); + layout_stack_frame(); } /* vim: set sw=4 ts=4 expandtab : */ diff --git a/mach/proto/mcg/powerpc.c b/mach/proto/mcg/powerpc.c index b5c606110..8cd2e269f 100644 --- a/mach/proto/mcg/powerpc.c +++ b/mach/proto/mcg/powerpc.c @@ -1,17 +1,35 @@ #include "mcg.h" -struct hop* platform_prologue(struct procedure* proc) +/* mcg stack frames are laid out as: + * + * | ...params... + * | --------------- <- ap + * | saved regs + * | --------------- <- st + * | spills + * | --------------- <- fp (a.k.a. lb) + * | locals + * | --------------- <- sp + * V ...user area... + * + */ + +void platform_calculate_offsets(struct procedure* proc) { - int framesize = proc->nlocals + 8; - int retbase = proc->nlocals; + proc->fp_to_st = proc->spills_size; + proc->fp_to_ap = proc->fp_to_st + proc->saved_size + 8; + proc->fp_to_lb = 0; +} +struct hop* platform_prologue(struct procedure* proc) +{ struct hop* hop = new_hop(proc->entry, NULL); - hop_add_insel(hop, "addi sp, fp, %d", -framesize); + hop_add_insel(hop, "addi sp, sp, %d", proc->fp_to_ap + proc->locals_size); hop_add_insel(hop, "mfspr 0, lr"); - hop_add_insel(hop, "stw fp, %d(sp)", retbase); - hop_add_insel(hop, "stw 0, %d(sp)", retbase+4); - hop_add_insel(hop, "addi fp, sp, retbase"); + hop_add_insel(hop, "stw fp, %d(sp)", proc->fp_to_st + proc->locals_size); + hop_add_insel(hop, "stw 0, %d(sp)", proc->fp_to_st + proc->locals_size + 4); + hop_add_insel(hop, "addi fp, sp, %d", proc->locals_size); return hop; } @@ -30,7 +48,14 @@ struct hop* platform_move(struct basicblock* bb, struct hreg* src, struct hreg* struct hop* hop = new_hop(bb, NULL); if ((src->type & burm_int_ATTR) && (dest->type & burm_int_ATTR)) - hop_add_insel(hop, "mr %H, %H", dest, src); + { + if (src->is_stacked) + hop_add_insel(hop, "lwz %H, %S(fp) ! %H", dest, src, src); + else if (dest->is_stacked) + hop_add_insel(hop, "stw %H, %S(fp) ! %H", src, dest, dest); + else + hop_add_insel(hop, "mr %H, %H", dest, src); + } else fatal("cannot generate move from %s to %s", src->name, dest->name); diff --git a/mach/proto/mcg/procedure.c b/mach/proto/mcg/procedure.c index d6382b06d..00c1a457b 100644 --- a/mach/proto/mcg/procedure.c +++ b/mach/proto/mcg/procedure.c @@ -1,5 +1,7 @@ #include "mcg.h" +extern struct procedure* current_proc; + static void print_blocks(char k, struct procedure* proc) { int i; @@ -157,8 +159,6 @@ static void write_dominance_graph(const char* name) void procedure_compile(struct procedure* proc) { - int i; - pass_group_irs(proc); print_blocks('1', proc); @@ -189,10 +189,11 @@ void procedure_compile(struct procedure* proc) pass_find_phi_congruence_groups(); pass_live_vreg_analysis(); print_hops('8', proc); - pass_register_allocator(); + pass_register_allocator(proc); pass_add_prologue_epilogue(proc); print_hops('9', proc); + platform_calculate_offsets(proc); emit_procedure(proc); if (cfg_dot_file) diff --git a/mach/proto/mcg/procedure.h b/mach/proto/mcg/procedure.h index 9d653b3f4..f0c6b3be3 100644 --- a/mach/proto/mcg/procedure.h +++ b/mach/proto/mcg/procedure.h @@ -13,7 +13,12 @@ struct procedure const char* name; struct basicblock* entry; struct basicblock* exit; - size_t nlocals; + int locals_size; + int spills_size; + int saved_size; + int fp_to_st; + int fp_to_ap; + int fp_to_lb; ARRAYOF(struct basicblock) blocks; IMAPOF(struct local) locals; }; diff --git a/mach/proto/mcg/reg.c b/mach/proto/mcg/reg.c index 2cb523764..25ec0b607 100644 --- a/mach/proto/mcg/reg.c +++ b/mach/proto/mcg/reg.c @@ -20,15 +20,16 @@ struct hreg* new_hreg(const struct burm_register_data* brd) return hreg; } -struct hreg* new_stacked_hreg(int offset, uint32_t type) +struct hreg* new_stacked_hreg(uint32_t type) { + static int hreg_count = 1; struct hreg* hreg = calloc(1, sizeof *hreg); - hreg->name = aprintf("stacked_%d", offset); + hreg->name = aprintf("stacked_%d_id_%d", type, hreg_count++); hreg->realname = hreg->name; hreg->type = type; hreg->attrs = type; hreg->is_stacked = true; - hreg->offset = offset; + hreg->offset = -1; return hreg; } diff --git a/mach/proto/mcg/reg.h b/mach/proto/mcg/reg.h index 0f47dfd03..e71864f3a 100644 --- a/mach/proto/mcg/reg.h +++ b/mach/proto/mcg/reg.h @@ -8,7 +8,7 @@ struct phicongruence int id; ARRAYOF(struct vreg) vregs; ARRAYOF(struct hop) definitions; - uint32_t attrs; + uint32_t type; }; struct hreg @@ -24,6 +24,7 @@ struct hreg struct vreg { int id; + uint32_t type; struct phicongruence* congruence; struct hop* defined; ARRAYOF(struct hop) used; @@ -34,7 +35,7 @@ typedef PMAPOF(struct hreg, struct vreg) register_assignment_t; extern struct vreg* new_vreg(void); extern struct hreg* new_hreg(const struct burm_register_data* brd); -extern struct hreg* new_stacked_hreg(int offset, uint32_t type); +extern struct hreg* new_stacked_hreg(uint32_t type); #endif diff --git a/mach/proto/mcg/treebuilder.c b/mach/proto/mcg/treebuilder.c index 80433ec7d..f4a81752e 100644 --- a/mach/proto/mcg/treebuilder.c +++ b/mach/proto/mcg/treebuilder.c @@ -721,6 +721,7 @@ static void insn_ivalue(int opcode, arith value) /* This is actually ignored --- the entire block gets special * treatment. But a lot of the rest of the code assumes that * all basic blocks have one instruction, so we insert one. */ + array_append(¤t_proc->exit->irs, new_ir0( IR_RET, 0