The register allocator now makes a spirited attempt to honour register
attributes when allocating. Unfortunately, backward edges don't work (because the limited def-use chain stuff doesn't work across basic blocks). Needs more thought.
This commit is contained in:
parent
38de688c5a
commit
23c3575f0f
|
@ -21,6 +21,11 @@ struct insel
|
||||||
u;
|
u;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct constraint
|
||||||
|
{
|
||||||
|
uint32_t attrs;
|
||||||
|
};
|
||||||
|
|
||||||
struct hop
|
struct hop
|
||||||
{
|
{
|
||||||
int id;
|
int id;
|
||||||
|
@ -29,6 +34,8 @@ struct hop
|
||||||
ARRAYOF(struct insel) insels;
|
ARRAYOF(struct insel) insels;
|
||||||
struct vreg* output;
|
struct vreg* output;
|
||||||
|
|
||||||
|
PMAPOF(struct vreg, struct constraint) constraints;
|
||||||
|
|
||||||
ARRAYOF(struct vreg) ins;
|
ARRAYOF(struct vreg) ins;
|
||||||
ARRAYOF(struct vreg) outs;
|
ARRAYOF(struct vreg) outs;
|
||||||
ARRAYOF(struct vreg) throughs;
|
ARRAYOF(struct vreg) throughs;
|
||||||
|
|
|
@ -78,15 +78,29 @@ static void emit_eoi(void)
|
||||||
hop_add_eoi_insel(current_hop);
|
hop_add_eoi_insel(current_hop);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void constrain_input_reg(int child, int attr)
|
static struct constraint* get_constraint(struct vreg* vreg)
|
||||||
|
{
|
||||||
|
struct constraint* c = pmap_findleft(¤t_hop->constraints, vreg);
|
||||||
|
if (!c)
|
||||||
|
{
|
||||||
|
c = calloc(1, sizeof(*c));
|
||||||
|
pmap_put(¤t_hop->constraints, vreg, c);
|
||||||
|
}
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void constrain_input_reg(int child, uint32_t attr)
|
||||||
{
|
{
|
||||||
struct vreg* vreg = find_vreg_of_child(child);
|
struct vreg* vreg = find_vreg_of_child(child);
|
||||||
|
struct constraint* c;
|
||||||
|
|
||||||
if (vreg)
|
if (vreg)
|
||||||
array_appendu(¤t_hop->ins, vreg);
|
array_appendu(¤t_hop->ins, vreg);
|
||||||
|
|
||||||
|
get_constraint(vreg)->attrs = attr;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void constrain_output_reg(int attr)
|
static void constrain_output_reg(uint32_t attr)
|
||||||
{
|
{
|
||||||
struct vreg* vreg = current_hop->output;
|
struct vreg* vreg = current_hop->output;
|
||||||
|
|
||||||
|
@ -95,6 +109,8 @@ static void constrain_output_reg(int attr)
|
||||||
|
|
||||||
array_appendu(¤t_hop->outs, vreg);
|
array_appendu(¤t_hop->outs, vreg);
|
||||||
vreg->defined = current_hop;
|
vreg->defined = current_hop;
|
||||||
|
|
||||||
|
get_constraint(vreg)->attrs = attr;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct burm_emitter_data emitter_data =
|
static const struct burm_emitter_data emitter_data =
|
||||||
|
|
|
@ -28,7 +28,7 @@ static void wire_up_blocks_ins_outs(void)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct hreg* allocate_hreg(register_assignment_t* regs, struct vreg* vreg)
|
static struct hreg* allocate_hreg(register_assignment_t* regs, struct vreg* vreg, uint32_t attr)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
@ -36,11 +36,14 @@ static struct hreg* allocate_hreg(register_assignment_t* regs, struct vreg* vreg
|
||||||
{
|
{
|
||||||
struct hreg* hreg = hregs.item[i];
|
struct hreg* hreg = hregs.item[i];
|
||||||
if (!pmap_findleft(regs, hreg))
|
if (!pmap_findleft(regs, hreg))
|
||||||
|
{
|
||||||
|
if (hreg->attrs & attr)
|
||||||
{
|
{
|
||||||
pmap_put(regs, hreg, vreg);
|
pmap_put(regs, hreg, vreg);
|
||||||
return hreg;
|
return hreg;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fatal("ran out of registers");
|
fatal("ran out of registers");
|
||||||
}
|
}
|
||||||
|
@ -81,7 +84,8 @@ static void select_registers(struct hop* hop,
|
||||||
for (i=0; i<hop->outs.count; i++)
|
for (i=0; i<hop->outs.count; i++)
|
||||||
{
|
{
|
||||||
struct vreg* vreg = hop->outs.item[i];
|
struct vreg* vreg = hop->outs.item[i];
|
||||||
allocate_hreg(out, vreg);
|
struct constraint* c = pmap_findleft(&hop->constraints, vreg);
|
||||||
|
allocate_hreg(out, vreg, c->attrs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -97,7 +101,7 @@ void pass_register_allocator(void)
|
||||||
struct basicblock* bb = dominance.preorder.item[i];
|
struct basicblock* bb = dominance.preorder.item[i];
|
||||||
register_assignment_t* old = bb->regsin;
|
register_assignment_t* old = bb->regsin;
|
||||||
|
|
||||||
tracef('R', "R: allocating block %s\n", bb->name);
|
tracef('R', "R: considering block %s\n", bb->name);
|
||||||
|
|
||||||
/* Attempt to import any block input registers from a predecessor. At
|
/* Attempt to import any block input registers from a predecessor. At
|
||||||
* least one predecessor should export it; our graph traversal order
|
* least one predecessor should export it; our graph traversal order
|
||||||
|
@ -151,21 +155,42 @@ void pass_register_allocator(void)
|
||||||
|
|
||||||
/* It's possible for the previous stage to fail because in in has
|
/* It's possible for the previous stage to fail because in in has
|
||||||
* clobbered the physical register we were wanting. So we need to
|
* clobbered the physical register we were wanting. So we need to
|
||||||
* allocate a new register for that phi value. */
|
* allocate a new register for that phi value.
|
||||||
|
*
|
||||||
|
* We don't bother allocating anything if the vreg is never used.
|
||||||
|
* */
|
||||||
|
|
||||||
for (j=0; j<bb->phis.count; j++)
|
for (j=0; j<bb->phis.count; j++)
|
||||||
{
|
{
|
||||||
struct vreg* vreg = bb->phis.item[j].left;
|
struct vreg* vreg = bb->phis.item[j].left;
|
||||||
if (!pmap_findright(old, vreg))
|
struct phi* phi = bb->phis.item[j].right;
|
||||||
allocate_hreg(old, vreg);
|
if (!pmap_findright(old, vreg) && (vreg->used.count > 0))
|
||||||
|
{
|
||||||
|
struct hop* used = vreg->used.item[0];
|
||||||
|
struct constraint* c = pmap_findleft(&used->constraints, vreg);
|
||||||
|
struct hreg* hreg = allocate_hreg(old, vreg, c->attrs);
|
||||||
|
|
||||||
|
tracef('R', "R: import fallback hreg %s for phi input %%%d from %s\n",
|
||||||
|
hreg->name, vreg->id, phi->prev->name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (j=0; j<bb->hops.count; j++)
|
for (j=0; j<bb->hops.count; j++)
|
||||||
{
|
{
|
||||||
|
int k;
|
||||||
struct hop* hop = bb->hops.item[j];
|
struct hop* hop = bb->hops.item[j];
|
||||||
register_assignment_t* in = &hop->regsin;
|
register_assignment_t* in = &hop->regsin;
|
||||||
register_assignment_t* out = &hop->regsout;;
|
register_assignment_t* out = &hop->regsout;;
|
||||||
|
|
||||||
|
tracef('R', "R: %d from $%d:", hop->id, hop->ir->id);
|
||||||
|
for (k=0; k<hop->ins.count; k++)
|
||||||
|
tracef('R', " r%%%d", hop->ins.item[k]->id);
|
||||||
|
for (k=0; k<hop->throughs.count; k++)
|
||||||
|
tracef('R', " =%%%d", hop->throughs.item[k]->id);
|
||||||
|
for (k=0; k<hop->outs.count; k++)
|
||||||
|
tracef('R', " w%%%d", hop->outs.item[k]->id);
|
||||||
|
tracef('R', "\n");
|
||||||
|
|
||||||
select_registers(hop, old, in, out);
|
select_registers(hop, old, in, out);
|
||||||
|
|
||||||
old = out;
|
old = out;
|
||||||
|
|
|
@ -400,8 +400,8 @@ Tree tree(const struct terminfo* ti, Tree left, Tree right)
|
||||||
|
|
||||||
if (ti->attr && ti->attr[0])
|
if (ti->attr && ti->attr[0])
|
||||||
{
|
{
|
||||||
nt->attr = smap_get(®isterattrs, ti->attr);
|
t->attr = smap_get(®isterattrs, ti->attr);
|
||||||
if (!nt->attr)
|
if (!t->attr)
|
||||||
yyerror("'%s' doesn't seem to be a known register attribute", ti->attr);
|
yyerror("'%s' doesn't seem to be a known register attribute", ti->attr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1077,10 +1077,12 @@ static void emit_input_regs(Tree node, int* index)
|
||||||
Nonterm nt = node->op;
|
Nonterm nt = node->op;
|
||||||
if ((nt->kind == NONTERM) && !nt->is_fragment && !node->left && !node->right)
|
if ((nt->kind == NONTERM) && !nt->is_fragment && !node->left && !node->right)
|
||||||
{
|
{
|
||||||
uint32_t attr = 0;
|
if (node->attr)
|
||||||
if (nt->attr->number)
|
{
|
||||||
attr = 1<<nt->attr->number;
|
uint32_t attr = 1<<node->attr->number;
|
||||||
print("%1data->constrain_input_reg(%d, 0x%x);\n", *index, attr);
|
print("%1data->constrain_input_reg(%d, 0x%x /* %s */);\n",
|
||||||
|
*index, attr, node->attr->name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!node->left && !node->right)
|
if (!node->left && !node->right)
|
||||||
|
@ -1118,7 +1120,8 @@ static void emitinsndata(Rule rules)
|
||||||
print("static void %Pemitter_%d(const struct %Pemitter_data* data) {\n", r->ern);
|
print("static void %Pemitter_%d(const struct %Pemitter_data* data) {\n", r->ern);
|
||||||
|
|
||||||
if (r->attr)
|
if (r->attr)
|
||||||
print("%1data->constrain_output_reg(0x%x);\n", 1<<r->attr->number);
|
print("%1data->constrain_output_reg(0x%x /* %s */);\n",
|
||||||
|
1<<r->attr->number, r->attr->name);
|
||||||
|
|
||||||
{
|
{
|
||||||
int index = 0;
|
int index = 0;
|
||||||
|
|
|
@ -96,7 +96,6 @@ struct nonterm
|
||||||
Rule chain; /* chain rules w/non-terminal on rhs */
|
Rule chain; /* chain rules w/non-terminal on rhs */
|
||||||
Nonterm link; /* next terminal in number order */
|
Nonterm link; /* next terminal in number order */
|
||||||
bool is_fragment; /* these instructions are all fragments */
|
bool is_fragment; /* these instructions are all fragments */
|
||||||
struct regattr* attr; /* input register attribute */
|
|
||||||
};
|
};
|
||||||
extern void* lookup(const char* name);
|
extern void* lookup(const char* name);
|
||||||
extern Nonterm nonterm(const char* id, bool allocate);
|
extern Nonterm nonterm(const char* id, bool allocate);
|
||||||
|
@ -109,6 +108,7 @@ struct tree
|
||||||
const char* label; /* user label for this node */
|
const char* label; /* user label for this node */
|
||||||
Tree left, right; /* operands */
|
Tree left, right; /* operands */
|
||||||
int nterms; /* number of terminal nodes in this tree */
|
int nterms; /* number of terminal nodes in this tree */
|
||||||
|
struct regattr* attr; /* input register attribute */
|
||||||
};
|
};
|
||||||
extern Tree tree(const struct terminfo* ti, Tree left, Tree right);
|
extern Tree tree(const struct terminfo* ti, Tree left, Tree right);
|
||||||
|
|
||||||
|
|
|
@ -42,6 +42,10 @@ S AND
|
||||||
S OR
|
S OR
|
||||||
S EOR
|
S EOR
|
||||||
S NOT
|
S NOT
|
||||||
|
S ASL
|
||||||
|
S ASR
|
||||||
|
S LSL
|
||||||
|
S LSR
|
||||||
|
|
||||||
# Conversions
|
# Conversions
|
||||||
S CII1
|
S CII1
|
||||||
|
|
|
@ -44,8 +44,8 @@ struct burm_emitter_data
|
||||||
void (*emit_reg)(int child);
|
void (*emit_reg)(int child);
|
||||||
void (*emit_value)(int child);
|
void (*emit_value)(int child);
|
||||||
void (*emit_eoi)(void);
|
void (*emit_eoi)(void);
|
||||||
void (*constrain_input_reg)(int child, int attr);
|
void (*constrain_input_reg)(int child, uint32_t attr);
|
||||||
void (*constrain_output_reg)(int attr);
|
void (*constrain_output_reg)(uint32_t attr);
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef void burm_emitter_t(const struct burm_emitter_data* data);
|
typedef void burm_emitter_t(const struct burm_emitter_data* data);
|
||||||
|
|
Loading…
Reference in a new issue