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);
print_header(k, hop);
p = strtok(buffer, "\n");
if (!p)
{
print_header(k, hop);
tracef(k, "\n");
}
while (p)
{
print_header(k, hop);
tracef(k, "%s\n", p);
tracef(k, "%s", p);
p = strtok(NULL, "\n");
if (p)
{
tracef(k, "\n");
print_header(k, hop);
}
}
tracef(k, "\n");
}
/* vim: set sw=4 ts=4 expandtab : */

View file

@ -34,6 +34,7 @@ struct hop
int id;
struct basicblock* bb;
struct ir* ir;
const struct burm_instruction_data* insndata;
ARRAYOF(struct insel) insels;
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)
{
insn->hop = current_hop = new_hop(current_bb, insn->ir);
current_hop->insndata = insn->insndata;
emit(insn);
if (!current_hop->output)

View file

@ -99,10 +99,19 @@ static struct hreg* evict(struct vreg* vreg)
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);
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)
@ -113,8 +122,7 @@ static struct hreg* find_input_reg(struct vreg* vreg)
for (i=0; i<hregs.count; i++)
{
hreg = hregs.item[i];
if (allocatable(hreg, vreg) &&
!pmap_findleft(current_ins, hreg))
if (allocatable_input(hreg, vreg))
{
return hreg;
}
@ -131,8 +139,7 @@ static struct hreg* find_output_reg(struct vreg* vreg)
for (i=0; i<hregs.count; i++)
{
hreg = hregs.item[i];
if (allocatable(hreg, vreg) &&
!pmap_findleft(current_outs, hreg))
if (allocatable_output(hreg, vreg))
{
return hreg;
}
@ -149,9 +156,8 @@ static struct hreg* find_through_reg(struct vreg* vreg)
for (i=0; i<hregs.count; i++)
{
hreg = hregs.item[i];
if (allocatable(hreg, vreg) &&
!pmap_findleft(current_ins, hreg) &&
!pmap_findleft(current_outs, hreg))
if (allocatable_input(hreg, vreg) &&
allocatable_output(hreg, vreg))
{
return hreg;
}
@ -166,19 +172,31 @@ static void add_input_register(struct vreg* vreg, struct hreg* hreg)
/* 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))
pmap_add(current_ins, hreg, vreg);
return;
if (pmap_findleft(current_ins, hreg) == vreg)
{
/* Yup, already there. */
}
else if (allocatable_input(hreg, vreg))
{
/* The register is free. */
}
else
{
/* Can't honour the hint. */
hreg = NULL;
}
}
if (!hreg)
{
/* Find an unused input register of the right class. */
hreg = find_input_reg(vreg);
if (!hreg)
hreg = evict(hreg);
hreg = evict(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);
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
* input register (most likely for a 2op instruction). */
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);
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)
{
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];
if ((hreg->type == src->type) &&
!pmap_findleft(current_ins, hreg) &&
!pmap_findleft(current_outs, hreg))
allocatable_input(hreg, vreg) &&
allocatable_output(hreg, vreg))
{
goto found;
}
@ -283,10 +336,7 @@ static void select_registers(struct hop* hop,
{
struct vreg* vreg = hop->throughs.item[i];
struct hreg* hreg = pmap_findright(old, vreg);
assert(hreg != NULL);
pmap_put(current_ins, hreg, vreg);
pmap_put(current_outs, hreg, vreg);
add_through_register(vreg, hreg);
}
/* Any registers being *read* by the instruction should also stay where

View file

@ -305,10 +305,12 @@ PATTERNS
cost 8;
CALL(dest:LABEL4)
with corrupted(volatile)
emit "bl $dest"
cost 4;
CALL(dest:(int)reg)
with corrupted(volatile)
emit "mtspr ctr, %dest"
emit "bcctrl ALWAYS, 0, 0"
cost 8;

View file

@ -26,6 +26,7 @@ extern int yylex(void);
}
%term COPY
%term CORRUPTED
%term COST
%term DECLARATIONS
%term EMIT
@ -145,6 +146,8 @@ constraint
: '(' constraint ')' { $$ = $2; }
| '%' ID EQUALS '%' ID { $$ = calloc(1, sizeof(*$$));
$$->type = CONSTRAINT_EQUALS; $$->left = $2; $$->right = $5; }
| CORRUPTED '(' ID ')' { $$ = calloc(1, sizeof(*$$));
$$->type = CONSTRAINT_CORRUPTED_ATTR; $$->left = $3; }
;
qfragments

View file

@ -1239,6 +1239,27 @@ static void emitinsndata(Rule rules)
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");
r = r->link;
}

View file

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

View file

@ -56,6 +56,7 @@ struct burm_instruction_data
const char* name;
burm_emitter_t* emitter;
bool is_fragment;
uint32_t corrupts;
};
extern const struct burm_instruction_data burm_instruction_data[];

View file

@ -38,6 +38,7 @@ static int braces = 0;
"DECLARATIONS" return DECLARATIONS;
"PATTERNS" return PATTERNS;
"REGISTERS" return REGISTERS;
"corrupted" return CORRUPTED;
"cost" return COST;
"emit" return EMIT;
"fragment" return FRAGMENT;