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);
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;
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;
INSEL_HREG,
INSEL_VREG,
INSEL_VALUE,
+ INSEL_ST_OFFSET,
+ INSEL_AB_OFFSET,
+ INSEL_LB_OFFSET,
INSEL_EOI
};
struct hreg* hreg;
struct vreg* vreg;
struct ir* value;
+ int offset;
}
u;
};
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, ...);
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);
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;
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);
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;
array_appendu(¤t_hop->outs, vreg);
vreg->defined = current_hop;
+ vreg->type = find_type_from_constraint(attr);
get_constraint(vreg)->attrs = attr;
}
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);
}
struct vreg* out;
};
+static struct procedure* current_proc;
+
static ARRAYOF(struct hreg) hregs;
static PMAPOF(struct vreg, struct hreg) evicted;
}
static struct hreg* allocate_phi_hreg(register_assignment_t* regs,
- struct vreg* vreg, uint32_t attrs)
+ struct vreg* vreg, uint32_t type)
{
int i;
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;
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;
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. */
}
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
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);
}
{
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;
}
/* 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);
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);
else
{
/* Swap. */
-
+
+ assert(false);
src = copies.item[0].left;
dest = pmap_findleft(&copies, src);
hop = create_swap(bb, src, dest);
}
}
-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 : */
#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;
}
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);
#include "mcg.h"
+extern struct procedure* current_proc;
+
static void print_blocks(char k, struct procedure* proc)
{
int i;
void procedure_compile(struct procedure* proc)
{
- int i;
-
pass_group_irs(proc);
print_blocks('1', 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)
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;
};
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;
}
int id;
ARRAYOF(struct vreg) vregs;
ARRAYOF(struct hop) definitions;
- uint32_t attrs;
+ uint32_t type;
};
struct hreg
struct vreg
{
int id;
+ uint32_t type;
struct phicongruence* congruence;
struct hop* defined;
ARRAYOF(struct hop) used;
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
/* 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