Register spilling to the stack frame works, more or less.
authorDavid Given <dg@cowlark.com>
Sat, 15 Oct 2016 20:53:56 +0000 (22:53 +0200)
committerDavid Given <dg@cowlark.com>
Sat, 15 Oct 2016 20:53:56 +0000 (22:53 +0200)
13 files changed:
mach/proto/mcg/hop.c
mach/proto/mcg/hop.h
mach/proto/mcg/mcg.h
mach/proto/mcg/parse_em.c
mach/proto/mcg/pass_instructionselection.c
mach/proto/mcg/pass_phigroups.c
mach/proto/mcg/pass_registerallocator.c
mach/proto/mcg/powerpc.c
mach/proto/mcg/procedure.c
mach/proto/mcg/procedure.h
mach/proto/mcg/reg.c
mach/proto/mcg/reg.h
mach/proto/mcg/treebuilder.c

index 3797d7b..b5eaa02 100644 (file)
@@ -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;
index dcc6fe0..e1f2f6d 100644 (file)
@@ -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, ...);
index 5cd875e..005324d 100644 (file)
@@ -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;
index 2b88eac..eaa712a 100644 (file)
@@ -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(&current_proc->blocks, code_bb);
index 52adc7b..82d7f98 100644 (file)
@@ -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(&current_hop->outs, vreg);
     vreg->defined = current_hop;
+    vreg->type = find_type_from_constraint(attr);
 
     get_constraint(vreg)->attrs = attr;
 }
index c773aa5..41fa6e0 100644 (file)
@@ -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);
     }
index f967e31..6dadc2c 100644 (file)
@@ -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; i<hregs.count; i++)
     {
         struct hreg* hreg = hregs.item[i];
-        if (!pmap_findleft(regs, hreg) && (hreg->attrs & 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(&current_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(&current_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(&current_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(&current_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; i<hregs.count; i++)
+    {
+        struct hreg* hreg = hregs.item[i];
+        if (hreg->is_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 : */
index b5c6061..8cd2e26 100644 (file)
@@ -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);
 
index d6382b0..00c1a45 100644 (file)
@@ -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)
index 9d653b3..f0c6b3b 100644 (file)
@@ -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;
 };
index 2cb5237..25ec0b6 100644 (file)
@@ -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;
 }
 
index 0f47dfd..e71864f 100644 (file)
@@ -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
 
index 80433ec..f4a8175 100644 (file)
@@ -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(&current_proc->exit->irs,
                     new_ir0(
                         IR_RET, 0