You can now mark a register as corrupting a certain register class; calls work,
or at least look like they work. The bad news is that the register allocator has a rare talent for putting things in the wrong register.
This commit is contained in:
parent
886adb86d7
commit
bb17aea73a
|
@ -178,20 +178,19 @@ void hop_print(char k, struct hop* hop)
|
||||||
|
|
||||||
hop_render(hop);
|
hop_render(hop);
|
||||||
|
|
||||||
print_header(k, hop);
|
|
||||||
|
|
||||||
p = strtok(buffer, "\n");
|
p = strtok(buffer, "\n");
|
||||||
if (!p)
|
print_header(k, hop);
|
||||||
{
|
|
||||||
print_header(k, hop);
|
|
||||||
tracef(k, "\n");
|
|
||||||
}
|
|
||||||
while (p)
|
while (p)
|
||||||
{
|
{
|
||||||
print_header(k, hop);
|
tracef(k, "%s", p);
|
||||||
tracef(k, "%s\n", p);
|
|
||||||
p = strtok(NULL, "\n");
|
p = strtok(NULL, "\n");
|
||||||
|
if (p)
|
||||||
|
{
|
||||||
|
tracef(k, "\n");
|
||||||
|
print_header(k, hop);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
tracef(k, "\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* vim: set sw=4 ts=4 expandtab : */
|
/* vim: set sw=4 ts=4 expandtab : */
|
||||||
|
|
|
@ -34,6 +34,7 @@ struct hop
|
||||||
int id;
|
int id;
|
||||||
struct basicblock* bb;
|
struct basicblock* bb;
|
||||||
struct ir* ir;
|
struct ir* ir;
|
||||||
|
const struct burm_instruction_data* insndata;
|
||||||
ARRAYOF(struct insel) insels;
|
ARRAYOF(struct insel) insels;
|
||||||
struct vreg* output;
|
struct vreg* output;
|
||||||
|
|
||||||
|
|
|
@ -182,6 +182,7 @@ static struct insn* walk_instructions(struct burm_node* node, int goal)
|
||||||
if (!insn->insndata->is_fragment)
|
if (!insn->insndata->is_fragment)
|
||||||
{
|
{
|
||||||
insn->hop = current_hop = new_hop(current_bb, insn->ir);
|
insn->hop = current_hop = new_hop(current_bb, insn->ir);
|
||||||
|
current_hop->insndata = insn->insndata;
|
||||||
emit(insn);
|
emit(insn);
|
||||||
|
|
||||||
if (!current_hop->output)
|
if (!current_hop->output)
|
||||||
|
|
|
@ -99,10 +99,19 @@ static struct hreg* evict(struct vreg* vreg)
|
||||||
assert(false);
|
assert(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool allocatable(struct hreg* hreg, struct vreg* vreg)
|
static bool allocatable_input(struct hreg* hreg, struct vreg* vreg)
|
||||||
{
|
{
|
||||||
struct constraint* c = pmap_findleft(¤t_hop->constraints, vreg);
|
struct constraint* c = pmap_findleft(¤t_hop->constraints, vreg);
|
||||||
return (hreg->attrs & c->attrs);
|
return !pmap_findleft(current_ins, hreg) &&
|
||||||
|
(!c || (hreg->attrs & c->attrs));
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool allocatable_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)) &&
|
||||||
|
!(hreg->attrs & current_hop->insndata->corrupts);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct hreg* find_input_reg(struct vreg* vreg)
|
static struct hreg* find_input_reg(struct vreg* vreg)
|
||||||
|
@ -113,8 +122,7 @@ static struct hreg* find_input_reg(struct vreg* vreg)
|
||||||
for (i=0; i<hregs.count; i++)
|
for (i=0; i<hregs.count; i++)
|
||||||
{
|
{
|
||||||
hreg = hregs.item[i];
|
hreg = hregs.item[i];
|
||||||
if (allocatable(hreg, vreg) &&
|
if (allocatable_input(hreg, vreg))
|
||||||
!pmap_findleft(current_ins, hreg))
|
|
||||||
{
|
{
|
||||||
return hreg;
|
return hreg;
|
||||||
}
|
}
|
||||||
|
@ -131,8 +139,7 @@ static struct hreg* find_output_reg(struct vreg* vreg)
|
||||||
for (i=0; i<hregs.count; i++)
|
for (i=0; i<hregs.count; i++)
|
||||||
{
|
{
|
||||||
hreg = hregs.item[i];
|
hreg = hregs.item[i];
|
||||||
if (allocatable(hreg, vreg) &&
|
if (allocatable_output(hreg, vreg))
|
||||||
!pmap_findleft(current_outs, hreg))
|
|
||||||
{
|
{
|
||||||
return hreg;
|
return hreg;
|
||||||
}
|
}
|
||||||
|
@ -149,9 +156,8 @@ static struct hreg* find_through_reg(struct vreg* vreg)
|
||||||
for (i=0; i<hregs.count; i++)
|
for (i=0; i<hregs.count; i++)
|
||||||
{
|
{
|
||||||
hreg = hregs.item[i];
|
hreg = hregs.item[i];
|
||||||
if (allocatable(hreg, vreg) &&
|
if (allocatable_input(hreg, vreg) &&
|
||||||
!pmap_findleft(current_ins, hreg) &&
|
allocatable_output(hreg, vreg))
|
||||||
!pmap_findleft(current_outs, hreg))
|
|
||||||
{
|
{
|
||||||
return hreg;
|
return hreg;
|
||||||
}
|
}
|
||||||
|
@ -166,19 +172,31 @@ static void add_input_register(struct vreg* vreg, struct hreg* hreg)
|
||||||
|
|
||||||
/* Register hint for an input? */
|
/* Register hint for an input? */
|
||||||
|
|
||||||
if (hreg && allocatable(hreg, vreg))
|
if (hreg)
|
||||||
{
|
{
|
||||||
/* If it's already assigned, it's most likely a through. */
|
if (pmap_findleft(current_ins, hreg) == vreg)
|
||||||
if (!pmap_findleft(current_ins, hreg))
|
{
|
||||||
pmap_add(current_ins, hreg, vreg);
|
/* Yup, already there. */
|
||||||
return;
|
}
|
||||||
|
else if (allocatable_input(hreg, vreg))
|
||||||
|
{
|
||||||
|
/* The register is free. */
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Can't honour the hint. */
|
||||||
|
hreg = NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Find an unused input register of the right class. */
|
|
||||||
|
|
||||||
hreg = find_input_reg(vreg);
|
|
||||||
if (!hreg)
|
if (!hreg)
|
||||||
hreg = evict(hreg);
|
{
|
||||||
|
/* Find an unused input register of the right class. */
|
||||||
|
|
||||||
|
hreg = find_input_reg(vreg);
|
||||||
|
if (!hreg)
|
||||||
|
hreg = evict(vreg);
|
||||||
|
}
|
||||||
|
|
||||||
pmap_add(current_ins, hreg, vreg);
|
pmap_add(current_ins, hreg, vreg);
|
||||||
}
|
}
|
||||||
|
@ -195,14 +213,17 @@ static void add_output_register(struct vreg* vreg)
|
||||||
c = pmap_findleft(¤t_hop->constraints, vreg);
|
c = pmap_findleft(¤t_hop->constraints, vreg);
|
||||||
if (c->equals_to)
|
if (c->equals_to)
|
||||||
{
|
{
|
||||||
|
tracef('R', "R: outputput 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
|
/* This output register is constrained to be in the same hreg as an
|
||||||
* input register (most likely for a 2op instruction). */
|
* input register (most likely for a 2op instruction). */
|
||||||
|
|
||||||
hreg = pmap_findright(current_ins, c->equals_to);
|
hreg = pmap_findright(current_ins, c->equals_to);
|
||||||
|
|
||||||
/* If this register is current unused as an output, use it. */
|
/* If this register is currently unused as an output, use it. */
|
||||||
|
|
||||||
if (!pmap_findleft(current_outs, hreg))
|
if (allocatable_output(hreg, c->equals_to))
|
||||||
{
|
{
|
||||||
pmap_add(current_outs, hreg, vreg);
|
pmap_add(current_outs, hreg, vreg);
|
||||||
return;
|
return;
|
||||||
|
@ -235,6 +256,38 @@ static void add_output_register(struct vreg* vreg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void add_through_register(struct vreg* vreg, struct hreg* hreg)
|
||||||
|
{
|
||||||
|
/* Register hint for an input? */
|
||||||
|
|
||||||
|
if (hreg)
|
||||||
|
{
|
||||||
|
bool infree = allocatable_input(hreg, vreg);
|
||||||
|
bool outfree = allocatable_output(hreg, vreg);
|
||||||
|
|
||||||
|
if (infree && outfree)
|
||||||
|
{
|
||||||
|
/* 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. */
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Nope, can't honour the hint. */
|
||||||
|
hreg = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hreg)
|
||||||
|
hreg = find_through_reg(vreg);
|
||||||
|
|
||||||
|
pmap_put(current_ins, hreg, vreg);
|
||||||
|
pmap_put(current_outs, hreg, vreg);
|
||||||
|
}
|
||||||
|
|
||||||
static void find_new_home_for_evicted_register(struct vreg* vreg, struct hreg* src)
|
static void find_new_home_for_evicted_register(struct vreg* vreg, struct hreg* src)
|
||||||
{
|
{
|
||||||
uint32_t srctype = src->type;
|
uint32_t srctype = src->type;
|
||||||
|
@ -249,8 +302,8 @@ static void find_new_home_for_evicted_register(struct vreg* vreg, struct hreg* s
|
||||||
{
|
{
|
||||||
hreg = hregs.item[i];
|
hreg = hregs.item[i];
|
||||||
if ((hreg->type == src->type) &&
|
if ((hreg->type == src->type) &&
|
||||||
!pmap_findleft(current_ins, hreg) &&
|
allocatable_input(hreg, vreg) &&
|
||||||
!pmap_findleft(current_outs, hreg))
|
allocatable_output(hreg, vreg))
|
||||||
{
|
{
|
||||||
goto found;
|
goto found;
|
||||||
}
|
}
|
||||||
|
@ -283,10 +336,7 @@ static void select_registers(struct hop* hop,
|
||||||
{
|
{
|
||||||
struct vreg* vreg = hop->throughs.item[i];
|
struct vreg* vreg = hop->throughs.item[i];
|
||||||
struct hreg* hreg = pmap_findright(old, vreg);
|
struct hreg* hreg = pmap_findright(old, vreg);
|
||||||
assert(hreg != NULL);
|
add_through_register(vreg, hreg);
|
||||||
|
|
||||||
pmap_put(current_ins, hreg, vreg);
|
|
||||||
pmap_put(current_outs, hreg, vreg);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Any registers being *read* by the instruction should also stay where
|
/* Any registers being *read* by the instruction should also stay where
|
||||||
|
|
|
@ -305,10 +305,12 @@ PATTERNS
|
||||||
cost 8;
|
cost 8;
|
||||||
|
|
||||||
CALL(dest:LABEL4)
|
CALL(dest:LABEL4)
|
||||||
|
with corrupted(volatile)
|
||||||
emit "bl $dest"
|
emit "bl $dest"
|
||||||
cost 4;
|
cost 4;
|
||||||
|
|
||||||
CALL(dest:(int)reg)
|
CALL(dest:(int)reg)
|
||||||
|
with corrupted(volatile)
|
||||||
emit "mtspr ctr, %dest"
|
emit "mtspr ctr, %dest"
|
||||||
emit "bcctrl ALWAYS, 0, 0"
|
emit "bcctrl ALWAYS, 0, 0"
|
||||||
cost 8;
|
cost 8;
|
||||||
|
|
|
@ -26,6 +26,7 @@ extern int yylex(void);
|
||||||
}
|
}
|
||||||
|
|
||||||
%term COPY
|
%term COPY
|
||||||
|
%term CORRUPTED
|
||||||
%term COST
|
%term COST
|
||||||
%term DECLARATIONS
|
%term DECLARATIONS
|
||||||
%term EMIT
|
%term EMIT
|
||||||
|
@ -145,6 +146,8 @@ constraint
|
||||||
: '(' constraint ')' { $$ = $2; }
|
: '(' constraint ')' { $$ = $2; }
|
||||||
| '%' ID EQUALS '%' ID { $$ = calloc(1, sizeof(*$$));
|
| '%' ID EQUALS '%' ID { $$ = calloc(1, sizeof(*$$));
|
||||||
$$->type = CONSTRAINT_EQUALS; $$->left = $2; $$->right = $5; }
|
$$->type = CONSTRAINT_EQUALS; $$->left = $2; $$->right = $5; }
|
||||||
|
| CORRUPTED '(' ID ')' { $$ = calloc(1, sizeof(*$$));
|
||||||
|
$$->type = CONSTRAINT_CORRUPTED_ATTR; $$->left = $3; }
|
||||||
;
|
;
|
||||||
|
|
||||||
qfragments
|
qfragments
|
||||||
|
|
|
@ -1239,6 +1239,27 @@ static void emitinsndata(Rule rules)
|
||||||
|
|
||||||
print("%2%s,\n", r->lhs->is_fragment ? "true" : "false");
|
print("%2%s,\n", r->lhs->is_fragment ? "true" : "false");
|
||||||
|
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
uint32_t attrs = 0;
|
||||||
|
|
||||||
|
for (i=0; i<r->constraints.count; i++)
|
||||||
|
{
|
||||||
|
struct constraint* c = r->constraints.item[i];
|
||||||
|
|
||||||
|
if (c->type == CONSTRAINT_CORRUPTED_ATTR)
|
||||||
|
{
|
||||||
|
struct regattr* p = smap_get(®isterattrs, c->left);
|
||||||
|
if (!p)
|
||||||
|
yyerror("no such register attribute '%s'", c->left);
|
||||||
|
|
||||||
|
attrs |= 1<<(p->number);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
print("%2%d, /* corruption attrs */\n", attrs);
|
||||||
|
}
|
||||||
|
|
||||||
print("%1},\n");
|
print("%1},\n");
|
||||||
r = r->link;
|
r = r->link;
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@ typedef struct term* Term;
|
||||||
enum
|
enum
|
||||||
{
|
{
|
||||||
CONSTRAINT_EQUALS,
|
CONSTRAINT_EQUALS,
|
||||||
|
CONSTRAINT_CORRUPTED_ATTR,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct constraint
|
struct constraint
|
||||||
|
|
|
@ -56,6 +56,7 @@ struct burm_instruction_data
|
||||||
const char* name;
|
const char* name;
|
||||||
burm_emitter_t* emitter;
|
burm_emitter_t* emitter;
|
||||||
bool is_fragment;
|
bool is_fragment;
|
||||||
|
uint32_t corrupts;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern const struct burm_instruction_data burm_instruction_data[];
|
extern const struct burm_instruction_data burm_instruction_data[];
|
||||||
|
|
|
@ -38,6 +38,7 @@ static int braces = 0;
|
||||||
"DECLARATIONS" return DECLARATIONS;
|
"DECLARATIONS" return DECLARATIONS;
|
||||||
"PATTERNS" return PATTERNS;
|
"PATTERNS" return PATTERNS;
|
||||||
"REGISTERS" return REGISTERS;
|
"REGISTERS" return REGISTERS;
|
||||||
|
"corrupted" return CORRUPTED;
|
||||||
"cost" return COST;
|
"cost" return COST;
|
||||||
"emit" return EMIT;
|
"emit" return EMIT;
|
||||||
"fragment" return FRAGMENT;
|
"fragment" return FRAGMENT;
|
||||||
|
|
Loading…
Reference in a new issue