Basic register allocation works!

This commit is contained in:
David Given 2016-10-08 23:32:54 +02:00
parent 4261744537
commit d75cc0a663
11 changed files with 205 additions and 16 deletions

View 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;

View 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;

View 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);

View 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);

View 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"

View 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 : */

View 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);

View 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);

View 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;

View 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

View 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