Basic register allocation works!
authorDavid Given <dg@cowlark.com>
Sat, 8 Oct 2016 21:32:54 +0000 (23:32 +0200)
committerDavid Given <dg@cowlark.com>
Sat, 8 Oct 2016 21:32:54 +0000 (23:32 +0200)
mach/proto/mcg/basicblock.h
mach/proto/mcg/graph.c
mach/proto/mcg/hop.c
mach/proto/mcg/hop.h
mach/proto/mcg/mcg.h
mach/proto/mcg/pass_registerallocator.c
mach/proto/mcg/pass_ssa.c
mach/proto/mcg/procedure.c
mach/proto/mcg/reg.c
mach/proto/mcg/reg.h
mach/proto/mcg/table

index 4c65721..323fa4d 100644 (file)
@@ -24,6 +24,12 @@ struct basicblock
     ARRAYOF(struct vreg) liveins;
     ARRAYOF(struct vreg) liveouts;
 
+    /* Register assignments on entry and exit. These are *pointers* (because
+     * they just point to the regsin/regsout of the first and last hop
+     * respectively). */
+    register_assignment_t* regsin;
+    register_assignment_t* regsout;
+
     bool is_fake : 1;
     bool is_root : 1;
     bool is_terminated : 1;
index 9c97673..395b7bf 100644 (file)
@@ -87,10 +87,10 @@ static struct basicblock* intersect(struct basicblock* p1, struct basicblock* p2
     while (p1 != p2)
     {
         while (p1->order < p2->order)
-            p1 = pmap_get(&dominance.graph, p1);
+            p1 = pmap_findleft(&dominance.graph, p1);
 
         while (p2->order < p1->order)
-            p2 = pmap_get(&dominance.graph, p2);
+            p2 = pmap_findleft(&dominance.graph, p2);
     }
 
     return p1;
@@ -129,16 +129,16 @@ static void calculate_dominance_graph(void)
                 struct basicblock* p = b->prevs.item[j];
                 
                 /* Skip unprocessed blocks. */
-                if (!pmap_get(&dominance.graph, p))
+                if (!pmap_findleft(&dominance.graph, p))
                     continue;
 
                 if (!new_idom)
                     new_idom = p;
-                else if (pmap_get(&dominance.graph, p))
+                else if (pmap_findleft(&dominance.graph, p))
                     new_idom = intersect(p, new_idom);
             }
 
-            if (pmap_get(&dominance.graph, b) != new_idom)
+            if (pmap_findleft(&dominance.graph, b) != new_idom)
             {
                 pmap_put(&dominance.graph, b, new_idom);
                 changed = true;
index 652b9f6..d0a2dfc 100644 (file)
@@ -81,8 +81,17 @@ void hop_print(char k, struct hop* hop)
                                break;
 
                        case INSEL_VREG:
-                               tracef(k, "%%%d", insel->u.vreg->id);
+            {
+                struct vreg* vreg = insel->u.vreg;
+                struct hreg* hreg = pmap_findright(&hop->regsin, vreg);
+                if (!hreg)
+                    hreg = pmap_findright(&hop->regsout, vreg);
+                if (hreg)
+                    tracef(k, "%s", hreg->name);
+                else
+                    tracef(k, "%%%d", vreg->id);
                                break;
+            }
 
                        case INSEL_STRING:
                                tracef(k, "%s", insel->u.string);
index 18e70d3..16a39f7 100644 (file)
@@ -32,7 +32,8 @@ struct hop
        ARRAYOF(struct vreg) ins;
        ARRAYOF(struct vreg) outs;
     ARRAYOF(struct vreg) throughs;
-       PMAPOF(struct vreg, struct hreg) registers;
+    register_assignment_t regsin;
+    register_assignment_t regsout;
 };
 
 extern struct hop* new_hop(struct basicblock* bb, struct ir* ir);
index f4e33f2..a90bbb5 100644 (file)
@@ -23,8 +23,8 @@
 #include "astring.h"
 #include "ir.h"
 #include "mcgg.h"
-#include "hop.h"
 #include "reg.h"
+#include "hop.h"
 #include "basicblock.h"
 #include "procedure.h"
 #include "graph.h"
index e7cf1d7..bb22a4d 100644 (file)
@@ -1,7 +1,176 @@
 #include "mcg.h"
 
+static ARRAYOF(struct hreg) hregs;
+static int stacksize;
+
+static void populate_hregs(void)
+{
+    const struct burm_register_data* brd = burm_register_data;
+
+    stacksize = 0;
+    while (brd->name)
+    {
+        array_append(&hregs, new_hreg(brd));
+        brd++;
+    }
+}
+
+static void wire_up_blocks_ins_outs(void)
+{
+    int i, j;
+
+    for (i=0; i<dominance.preorder.count; i++)
+    {
+        struct basicblock* bb = dominance.preorder.item[i];
+        assert(bb->hops.count >= 1);
+        bb->regsin = &bb->hops.item[0]->regsin;
+        bb->regsout = &bb->hops.item[bb->hops.count-1]->regsout;
+    }
+}
+
+static struct hreg* allocate_hreg(register_assignment_t* regs, struct vreg* vreg)
+{
+    int i;
+
+    for (i=0; i<hregs.count; i++)
+    {
+        struct hreg* hreg = hregs.item[i];
+        if (!pmap_findleft(regs, hreg))
+        {
+            pmap_put(regs, hreg, vreg);
+            return hreg;
+        }
+    }
+
+    fatal("ran out of registers");
+}
+
+static void select_registers(struct hop* hop,
+    register_assignment_t* old, register_assignment_t* in, register_assignment_t* out)
+{
+    int i;
+
+    /* First, any vregs passing through the instruction stay in the same
+     * registers they are currently in. */
+
+    for (i=0; i<hop->throughs.count; i++)
+    {
+        struct vreg* vreg = hop->throughs.item[i];
+        struct hreg* hreg = pmap_findright(old, vreg);
+        assert(hreg != NULL);
+
+        pmap_put(in, hreg, vreg);
+        pmap_put(out, hreg, vreg);
+    }
+
+    /* Any registers being *read* by the instruction should also stay where
+     * they are. (This is likely to duplicate some throughs.) */
+
+    for (i=0; i<hop->ins.count; i++)
+    {
+        struct vreg* vreg = hop->ins.item[i];
+        struct hreg* hreg = pmap_findright(old, vreg);
+        assert(hreg != NULL);
+
+        pmap_put(in, hreg, vreg);
+    }
+
+    /* Any output registers will be *new* vregs (because SSA). So, allocate
+     * new hregs for them. */
+
+    for (i=0; i<hop->outs.count; i++)
+    {
+        struct vreg* vreg = hop->outs.item[i];
+        allocate_hreg(out, vreg);
+    }
+}
+
 void pass_register_allocator(void)
 {
+    int i, j, k;
+
+    populate_hregs();
+    wire_up_blocks_ins_outs();
+
+    for (i=0; i<dominance.preorder.count; i++)
+    {
+        struct basicblock* bb = dominance.preorder.item[i];
+        register_assignment_t* old = bb->regsin;
+        
+        tracef('R', "R: allocating block %s\n", bb->name);
+
+        /* Attempt to import any block input registers from a predecessor. At
+         * least one predecessor should export it; our graph traversal order
+         * guarantees it. */
+
+        for (j=0; j<bb->liveins.count; j++)
+        {
+            struct vreg* vreg = bb->liveins.item[j];
+            for (k=0; k<bb->prevs.count; k++)
+            {
+                struct basicblock* prevbb = bb->prevs.item[k];
+                struct hreg* hreg = pmap_findright(prevbb->regsout, vreg);
+                if (hreg)
+                {
+                    tracef('R', "R: import hreg %s for input %%%d from %s\n",
+                        hreg->name, vreg->id, prevbb->name);
+                    assert(!pmap_findleft(old, hreg));
+                    pmap_put(old, hreg, vreg);
+                    goto nextvreg;
+                }
+            }
+
+            fatal("couldn't find a register assignment for $%d", vreg->id);
+            nextvreg:;
+        }
+
+        /* And now do the same for the phis. Unlike input vregs, a phi can
+         * import a vreg from multiple locations. However, we don't care which
+         * one we find, as this is just a hint. Picking the same register as a
+         * predecessor helps reduce the number of copies the SSA deconstruction
+         * pass will need to insert. */
+
+        for (j=0; j<bb->phis.count; j++)
+        {
+            struct vreg* vreg = bb->phis.item[j].left;
+            struct phi* phi = bb->phis.item[j].right;
+            if (!pmap_findright(old, vreg))
+            {
+                /* This variable isn't allocated yet. */
+
+                struct hreg* hreg = pmap_findright(
+                    phi->prev->regsout, phi->ir->result);
+                if (hreg && !pmap_findleft(old, hreg))
+                {
+                    tracef('R', "R: import hreg %s for phi input %%%d from %s\n",
+                        hreg->name, vreg->id, phi->prev->name);
+                    pmap_put(old, hreg, vreg);
+                }
+            }
+        }
+
+        /* It's possible for the previous stage to fail because in in has
+         * clobbered the physical register we were wanting. So we need to
+         * allocate a new register for that phi value. */
+
+        for (j=0; j<bb->phis.count; j++)
+        {
+            struct vreg* vreg = bb->phis.item[j].left;
+            if (!pmap_findright(old, vreg))
+                allocate_hreg(old, vreg);
+        }
+            
+        for (j=0; j<bb->hops.count; j++)
+        {
+            struct hop* hop = bb->hops.item[j];
+            register_assignment_t* in = &hop->regsin;
+            register_assignment_t* out = &hop->regsout;;
+
+            select_registers(hop, old, in, out);
+
+            old = out;
+        }
+    }
 }
 
 /* vim: set sw=4 ts=4 expandtab : */
index 64f6769..9bf2d77 100644 (file)
@@ -26,7 +26,7 @@ static void calculate_dominance_frontier_graph(void)
     for (i=0; i<cfg.postorder.count; i++)
     {
         struct basicblock* b = cfg.postorder.item[i];
-        struct basicblock* dominator = pmap_get(&dominance.graph, b);
+        struct basicblock* dominator = pmap_findleft(&dominance.graph, b);
         if (b->prevs.count >= 2)
         {
             for (j=0; j<b->prevs.count; j++)
@@ -37,7 +37,7 @@ static void calculate_dominance_frontier_graph(void)
                     tracef('S', "S: %s is in %s's dominance frontier\n",
                         b->name, runner->name);
                     pmap_add(&dominancefrontiers, runner, b);
-                    runner = pmap_get(&dominance.graph, runner);
+                    runner = pmap_findleft(&dominance.graph, runner);
                 }
             }
         }
@@ -187,7 +187,7 @@ static void ssa_convert(void)
     for (i=0; i<defining.count; i++)
     {
         struct basicblock* bb = defining.item[i];
-        struct basicblock* dominates = pmap_get(&dominancefrontiers, bb);
+        struct basicblock* dominates = pmap_findleft(&dominancefrontiers, bb);
         if (dominates)
         {
             array_appendu(&needsphis, dominates);
index b621654..b2970b6 100644 (file)
@@ -167,9 +167,11 @@ void procedure_compile(struct procedure* proc)
     print_blocks('6', proc);
 
     pass_instruction_selector();
-    pass_live_vreg_analysis();
     print_hops('7', proc);
-    //pass_register_allocator();
+    pass_live_vreg_analysis();
+    print_hops('8', proc);
+    pass_register_allocator();
+    print_hops('9', proc);
 
     if (cfg_dot_file)
         write_cfg_graph(proc->name);
index c4cf710..848c1cd 100644 (file)
@@ -9,7 +9,7 @@ struct vreg* new_vreg(void)
        return vreg;
 }
 
-struct hreg* new_hreg(struct burm_register_data* brd)
+struct hreg* new_hreg(const struct burm_register_data* brd)
 {
        struct hreg* hreg = calloc(1, sizeof *hreg);
        hreg->name = brd->name;
index b901eb6..743b9c3 100644 (file)
@@ -18,9 +18,11 @@ struct vreg
     ARRAYOF(struct hop) used;
 };
 
+typedef PMAPOF(struct hreg, struct vreg) register_assignment_t;
+
 extern struct vreg* new_vreg(void);
 
-extern struct hreg* new_hreg(struct burm_register_data* brd);
+extern struct hreg* new_hreg(const struct burm_register_data* brd);
 extern struct hreg* new_stacked_hreg(int offset, uint32_t attrs);
 
 #endif
index c589101..9321724 100644 (file)
@@ -104,7 +104,7 @@ PATTERNS
                cost 4;
 
        SETRET4(in:(ret)reg)
-               emit "mov r0, %in"
+               emit "mr r3, %in"
                cost 4;
 
     (ret)reg = GETRET4