Massive rewrite of how emitters and the instruction selector works, after I

realised that the existing approach wasn't working. Now, hopefully, tracks the
instruction trees generated during selection properly.
This commit is contained in:
David Given 2016-10-04 00:16:06 +02:00
parent 68f98cbad7
commit bd28bddb92
6 changed files with 175 additions and 160 deletions

View file

@ -20,6 +20,7 @@ struct ir
ARRAYOF(struct ir) phivalue;
} u;
struct vreg* result; /* vreg containing IR result */
IMAPOF(struct hop) hops; /* only for root IRs; by goal */
};

View file

@ -1,10 +1,22 @@
#include "mcg.h"
#define MAX_CHILDREN 10
struct insn
{
struct ir* ir;
struct hop* hop;
const struct burm_instruction_data* insndata;
int num_children;
struct insn* children[MAX_CHILDREN];
};
static struct basicblock* current_bb;
static struct hop* current_hop;
static struct ir* current_ir;
static struct insn* current_insn;
static const struct burm_emitter_data emitter_data;
static void emit(struct insn* insn);
void burm_trace(struct burm_node* p, int ruleno, int cost, int bestcost) {
const struct burm_instruction_data* insndata = &burm_instruction_data[ruleno];
@ -20,11 +32,23 @@ void burm_panic_cannot_match(struct burm_node* node)
exit(1);
}
static void emit_reg(struct burm_node* node, int goal)
static void emit_return_reg(void)
{
struct hop* hop = imap_get(&current_ir->hops, goal);
hop_add_vreg_insel(current_hop, current_hop->output);
}
hop_add_vreg_insel(current_hop, hop->output);
static void emit_reg(int child)
{
struct insn* insn = current_insn->children[child];
struct vreg* vreg;
if (insn->hop)
vreg = insn->hop->output;
else
vreg = insn->ir->result;
if (vreg)
hop_add_vreg_insel(current_hop, vreg);
}
static void emit_string(const char* data)
@ -32,17 +56,14 @@ static void emit_string(const char* data)
hop_add_string_insel(current_hop, data);
}
static void emit_fragment(struct burm_node* node, int goal)
static void emit_fragment(int child)
{
int insn_no = burm_rule(node->state_label, goal);
const struct burm_instruction_data* insndata = &burm_instruction_data[insn_no];
if (insndata->emitter)
insndata->emitter(node, &emitter_data);
emit(current_insn->children[child]);
}
static void emit_value(struct burm_node* node)
static void emit_value(int child)
{
hop_add_value_insel(current_hop, node->ir);
hop_add_value_insel(current_hop, current_insn->children[child]->ir);
}
static void emit_eoi(void)
@ -50,73 +71,75 @@ static void emit_eoi(void)
hop_add_eoi_insel(current_hop);
}
static void emit_constraint_equals(struct burm_node* node, int goal)
{
#if 0
struct hop* hop;
if (!goal)
goal = ir->goal_no;
hop = imap_get(&current_ir->hops, goal);
current_hop->output = hop->output;
#endif
}
static const struct burm_emitter_data emitter_data =
{
&emit_string,
&emit_fragment,
&emit_return_reg,
&emit_reg,
&emit_value,
&emit_eoi,
&emit_constraint_equals
};
static void walk_instructions(struct burm_node* node, int goal)
static void emit(struct insn* insn)
{
struct burm_node* children[10];
int insn_no = burm_rule(node->state_label, goal);
const struct burm_instruction_data* insndata = &burm_instruction_data[insn_no];
const short* nts = burm_nts[insn_no];
struct hop* parent_hop = NULL;
struct ir* ir = node->ir;
struct insn* old = current_insn;
current_insn = insn;
insn->insndata->emitter(&emitter_data);
current_insn = old;
}
static struct insn* walk_instructions(struct burm_node* node, int goal)
{
struct insn* insn = calloc(1, sizeof(*insn));
int i;
if (!insndata->is_fragment)
insn->ir = node->ir;
insn->num_children = 0;
if (goal)
{
parent_hop = current_hop;
current_hop = new_hop(insn_no, ir);
if (goal != 1)
int insn_no = burm_rule(node->state_label, goal);
const short* nts = burm_nts[insn_no];
struct burm_node* children[MAX_CHILDREN] = {0};
insn->insndata = &burm_instruction_data[insn_no];
burm_kids(node, insn_no, children);
i = 0;
for (;;)
{
current_hop->output = new_vreg();
imap_add(&current_ir->hops, goal, current_hop);
if (!children[i])
break;
insn->children[i] = walk_instructions(children[i], nts[i]);
insn->num_children++;
i++;
}
tracef('I', "I: $%d goal %d %s selected %d: %s\n",
node->ir->id,
goal,
insn->insndata->is_fragment ? "fragment" : "instruction",
insn_no,
insn->insndata->name);
if (!insn->insndata->is_fragment)
{
insn->hop = current_hop = new_hop(0, insn->ir);
insn->hop->output = new_vreg();
emit(insn);
hop_print('I', current_hop);
if (goal != 1)
insn->ir->result = insn->hop->output;
}
}
burm_kids(node, insn_no, children);
for (i=0; nts[i]; i++)
walk_instructions(children[i], nts[i]);
tracef('I', "I: $%d goal %d selected %s %d: %s\n",
ir->id,
goal,
insndata->is_fragment ? "fragment" : "instruction",
insn_no,
insndata->name);
if (!insndata->is_fragment)
{
/* This may cause the vregs to be reassigned for this instruction (and
* fragments contained within it). */
insndata->emitter(node, &emitter_data);
hop_print('I', current_hop);
array_append(&ir->hops, current_hop);
current_hop = parent_hop;
}
return insn;
}
static struct burm_node* build_shadow_tree(struct ir* root, struct ir* ir)

View file

@ -52,6 +52,7 @@ PATTERNS
cost 4;
reg = POP4
with int reg
emit "pop %reg"
cost 4;
@ -68,12 +69,11 @@ PATTERNS
cost 4;
reg = in:REG
with (reg == in)
emit "reg %reg"
emit "mov %reg, %in"
cost 1;
reg = NOP(in:reg)
with (reg == in)
emit "mov %reg, %in"
cost 1;
@ -90,18 +90,22 @@ PATTERNS
cost 4;
reg = LOAD4(addr:address)
with int reg
emit "ldr %reg, %addr"
cost 4;
reg = LOAD1(addr:address)
with int reg
emit "ldrb %reg, %addr"
cost 4;
reg = CIU14(LOAD1(addr:address))
with int reg
emit "ldrb %reg, %addr"
cost 4;
reg = CII14(CIU41(CIU14(LOAD1(addr:address))))
with int reg
emit "ldrsb %reg, %addr"
cost 4;
@ -109,6 +113,7 @@ PATTERNS
/* Locals */
reg = in:LOCAL4
with int reg
emit "add %reg, fp, #$in"
cost 4;
@ -161,14 +166,17 @@ PATTERNS
/* Comparisons */
cc = COMPARES4(left:reg, right:aluparam)
with cc cc
emit "cmp %left, %right"
cost 4;
cc = COMPARES4(COMPARES4(left:reg, right:aluparam), CONST4)
with cc cc
emit "cmp %left, %right"
cost 4;
reg = cc
with int reg
emit "mov %reg, #0"
emit "movlt %reg, #-1"
emit "movgt %reg, #1"
@ -178,10 +186,12 @@ PATTERNS
/* Conversions */
reg = CII14(CIU41(value:reg))
with int reg
emit "sxtb %reg, %value"
cost 4;
reg = CIU41(in:reg)
with int reg
emit "and %reg, %in, #0xff"
cost 4;
@ -189,19 +199,23 @@ PATTERNS
/* ALU operations */
reg = ADD4(left:reg, right:aluparam)
with int reg
emit "add %reg, %left, %right"
cost 4;
reg = ADD4(left:aluparam, right:reg)
with int reg
emit "add %reg, %right, %left"
cost 4;
reg = MOD4(left:reg, right:reg)
with int reg
emit "udiv %reg, %left, %right"
emit "mls %reg, %reg, %right, %left"
cost 8;
reg = DIV4(left:reg, right:aluparam)
with int reg
emit "div %reg, %left, %right"
cost 4;
@ -212,28 +226,34 @@ PATTERNS
emit "%value";
reg = value:aluparam
with int reg
emit "mov %reg, %value"
cost 4;
reg = value:LABEL4
with int reg
emit "adr %reg, $value"
cost 4;
reg = value:BLOCK4
with int reg
emit "adr %reg, $value"
cost 4;
reg = value:CONST4
with int reg
emit "ldr %reg, address-containing-$value"
cost 8;
reg = value:CONSTF4
with int reg
emit "vldr %reg, address-containing-$value"
cost 8;
/* FPU operations */
reg = ADDF4(left:reg, right:reg)
with int reg
emit "fadds %reg, %left, %right"
cost 4;

View file

@ -28,6 +28,7 @@ static int nextern = 1;
}
%term ALLOCATES
%term COPY
%term COST
%term DECLARATIONS
%term EMIT
@ -105,24 +106,24 @@ patterns
;
pattern
: ID '=' rhs { nonterm($1, false); $$ = rule($1, $3, nextern++); }
| rhs { $$ = rule("stmt", $1, nextern++); }
| pattern PREFERS predicate { $$ = $1; array_append(&$$->prefers, $3); }
| pattern WHEN predicate { $$ = $1; array_append(&$$->requires, $3); }
| pattern COST INT { $$ = $1; $$->cost = $3; }
| pattern_constraints { $$ = $1; }
| pattern_emit { $$ = $1; }
: ID '=' rhs { nonterm($1, false); $$ = rule($1, $3, nextern++); }
| rhs { $$ = rule("stmt", $1, nextern++); }
| pattern PREFERS predicate { $$ = $1; array_append(&$$->prefers, $3); }
| pattern WHEN predicate { $$ = $1; array_append(&$$->requires, $3); }
| pattern COST INT { $$ = $1; $$->cost = $3; }
| pattern_constraints { $$ = $1; }
| pattern_emit { $$ = $1; }
;
rhs
: terminfo { $$ = tree(&$1, NULL, NULL); }
| terminfo '(' rhs ')' { $$ = tree(&$1, $3, NULL); }
| terminfo '(' rhs ',' rhs ')' { $$ = tree(&$1, $3, $5); }
: terminfo { $$ = tree(&$1, NULL, NULL); }
| terminfo '(' rhs ')' { $$ = tree(&$1, $3, NULL); }
| terminfo '(' rhs ',' rhs ')' { $$ = tree(&$1, $3, $5); }
;
terminfo
: ID { $$.name = $1; }
| ID ':' ID { $$.label = $1; $$.name = $3; }
: ID { $$.name = $1; }
| ID ':' ID { $$.label = $1; $$.name = $3; }
;
pattern_emit

View file

@ -679,20 +679,22 @@ static void emitheader(void)
}
/* computekids - compute paths to kids in tree t */
static char* computekids(Tree t, const char* v, char* bp, int* ip)
static char* computekids(Tree node, const char* v, char* bp, int* ip)
{
Term p = t->op;
Term t = node->op;
if (p->kind == NONTERM)
if (!node->left && !node->right)
{
sprintf(bp, "\t\tkids[%d] = %s;\n", (*ip)++, v);
bp += strlen(bp);
}
else if (p->arity > 0)
if (t->kind == TERM)
{
bp = computekids(t->left, aprintf("LEFT_CHILD(%s)", v), bp, ip);
if (p->arity == 2)
bp = computekids(t->right, aprintf("RIGHT_CHILD(%s)", v), bp, ip);
if (t->arity >= 1)
bp = computekids(node->left, aprintf("LEFT_CHILD(%s)", v), bp, ip);
if (t->arity == 2)
bp = computekids(node->right, aprintf("RIGHT_CHILD(%s)", v), bp, ip);
}
return bp;
}
@ -910,6 +912,27 @@ static void label_not_found(Rule rule, const char* label)
exit(1);
}
static bool find_child_index(Tree node, const char* name, int* index, Tree* found)
{
/* This must return the same ordering as the burm_kids() function uses. */
if (node->label && strcmp(node->label, name) == 0)
{
if (found)
*found = node;
return true;
}
if (!node->left && !node->right)
(*index)++;
if (node->left && find_child_index(node->left, name, index, found))
return true;
if (node->right && find_child_index(node->right, name, index, found))
return true;
return false;
}
/* emitpredicates - emit predicates for rules */
static void emitpredicatedefinitions(Rule r)
{
@ -954,49 +977,6 @@ static void emitpredicatedefinitions(Rule r)
}
}
static void invalid_equals_constraint(Rule rule)
{
yylineno = rule->lineno;
yyerror("left hand side of an equality constraint must be the output register");
exit(1);
}
static void emit_ir_expr(Rule rule, const char* label)
{
if (strcmp(label, rule->lhs->name) == 0)
print("node, %P%s_NT", label);
else
{
Tree node;
uint32_t path = find_label(rule->pattern, label, 0, &node);
Nonterm nt = node->op;
if (path == PATH_MISSING)
label_not_found(rule, label);
print_path(path);
if (nt->kind == NONTERM)
print(", %P%s_NT", ((Nonterm)node->op)->name);
else
print(", 0");
}
}
static void emit_constraint(Rule rule, struct constraint* c)
{
switch (c->type)
{
case CONSTRAINT_EQUALS:
if (strcmp(c->left, rule->lhs->name) != 0)
invalid_equals_constraint(rule);
print("%1data->emit_constraint_equals(");
emit_ir_expr(rule, c->right);
print(");\n");
break;
}
}
/* emitinsndata - emit the code generation data */
static void emitinsndata(Rule rules)
{
@ -1007,6 +987,7 @@ static void emitinsndata(Rule rules)
while (r)
{
struct stringfragment* f = r->code.first;
yylineno = r->lineno;
if (!f)
{
@ -1019,20 +1000,7 @@ static void emitinsndata(Rule rules)
}
print("/* %R */\n", r);
print("static void %Pemitter_%d(NODEPTR_TYPE node, const struct %Pemitter_data* data) {\n", r->ern);
/* Constraints come first, because they may cause vreg reassignment, which
* we want to happen before the instruction emission. */
{
int i;
for (i=0; i<r->constraints.count; i++)
{
struct constraint* c = r->constraints.item[i];
emit_constraint(r, c);
}
}
print("static void %Pemitter_%d(const struct %Pemitter_data* data) {\n", r->ern);
while (f)
{
@ -1043,23 +1011,26 @@ static void emitinsndata(Rule rules)
const char* label = f->data + 1;
if (strcmp(label, r->lhs->name) == 0)
print("%1data->emit_reg(node, %P%s_NT);\n", label);
print("%1data->emit_return_reg();\n", label);
else
{
Tree node;
uint32_t path = find_label(r->pattern, label, 0, &node);
int index = 0;
if (!find_child_index(r->pattern, label, &index, &node))
label_not_found(r, label);
Nonterm nt = node->op;
if (path == PATH_MISSING)
label_not_found(r, label);
if (nt->is_fragment)
print("%1data->emit_fragment(");
if (nt->kind == NONTERM)
{
if (nt->is_fragment)
print("%1data->emit_fragment(");
else
print("%1data->emit_reg(");
}
else
print("%1data->emit_reg(");
print_path(path);
print(", %P%s_NT);\n", ((Nonterm)node->op)->name);
print("%d);\n", index);
}
break;
}
@ -1067,12 +1038,11 @@ static void emitinsndata(Rule rules)
case '$':
{
const char* label = f->data + 1;
uint32_t path = find_label(r->pattern, label, 0, NULL);
print("%1data->emit_value(");
if (path == PATH_MISSING)
int index = 0;
if (!find_child_index(r->pattern, label, &index, NULL))
label_not_found(r, label);
print_path(path);
print(");\n");
print("%1data->emit_value(%d);\n", index);
break;
}

View file

@ -39,14 +39,14 @@ extern void burm_trace(NODEPTR_TYPE p, int ruleno, int cost, int bestcost);
struct burm_emitter_data
{
void (*emit_string)(const char* data);
void (*emit_fragment)(NODEPTR_TYPE node, int goal);
void (*emit_reg)(NODEPTR_TYPE node, int goal);
void (*emit_value)(NODEPTR_TYPE node);
void (*emit_fragment)(int child);
void (*emit_return_reg)(void);
void (*emit_reg)(int child);
void (*emit_value)(int child);
void (*emit_eoi)(void);
void (*emit_constraint_equals)(NODEPTR_TYPE node, int goal);
};
typedef void burm_emitter_t(NODEPTR_TYPE node, const struct burm_emitter_data* data);
typedef void burm_emitter_t(const struct burm_emitter_data* data);
struct burm_instruction_data
{