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:
David Given 2016-10-15 01:15:08 +02:00
parent 886adb86d7
commit bb17aea73a
10 changed files with 115 additions and 35 deletions

View file

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

View file

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

View file

@ -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)

View file

@ -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(&current_hop->constraints, vreg); struct constraint* c = pmap_findleft(&current_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(&current_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(&current_hop->constraints, vreg); c = pmap_findleft(&current_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

View file

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

View file

@ -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

View file

@ -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(&registerattrs, 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;
} }

View file

@ -20,6 +20,7 @@ typedef struct term* Term;
enum enum
{ {
CONSTRAINT_EQUALS, CONSTRAINT_EQUALS,
CONSTRAINT_CORRUPTED_ATTR,
}; };
struct constraint struct constraint

View file

@ -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[];

View file

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