void hop_print(char k, struct hop* hop)
{
- int i;
+ int i, j;
bool soi = true;
i = 0;
if (soi)
{
- tracef(k, "%c: %d from $%d: ", k, hop->id, hop->ir->id);
+ tracef(k, "%c: %d from $%d:", k, hop->id, hop->ir->id);
+
+ for (j=0; j<hop->ins.count; j++)
+ tracef(k, " <%%%d", hop->ins.item[j]->id);
+ for (j=0; j<hop->outs.count; j++)
+ tracef(k, " >%%%d", hop->outs.item[j]->id);
+ tracef(k, " ");
+
soi = false;
}
struct ir* ir;
ARRAYOF(struct insel) insels;
struct vreg* output;
+
+ ARRAYOF(struct vreg) ins;
+ ARRAYOF(struct vreg) outs;
PMAPOF(struct vreg, struct hreg) registers;
};
extern void pass_remove_dead_blocks(struct procedure* proc);
extern void pass_split_critical_edges(struct procedure* proc);
+extern void register_allocator(struct procedure* proc);
+
#endif
/* vim: set sw=4 ts=4 expandtab : */
hop_add_vreg_insel(current_hop, current_hop->output);
}
-static void emit_reg(int child)
+static struct vreg* find_vreg_of_child(int child)
{
struct insn* insn = current_insn->children[child];
- struct vreg* vreg;
if (insn->hop)
- vreg = insn->hop->output;
+ return insn->hop->output;
else
- vreg = insn->ir->result;
+ return insn->ir->result;
+}
+
+static void emit_reg(int child)
+{
+ struct vreg* vreg = find_vreg_of_child(child);
if (vreg)
hop_add_vreg_insel(current_hop, vreg);
hop_add_eoi_insel(current_hop);
}
+static void constrain_input_reg(int child, int attr)
+{
+ struct vreg* vreg = find_vreg_of_child(child);
+
+ if (vreg)
+ array_appendu(¤t_hop->ins, vreg);
+}
+
+static void constrain_output_reg(int attr)
+{
+}
+
static const struct burm_emitter_data emitter_data =
{
&emit_string,
&emit_reg,
&emit_value,
&emit_eoi,
+ &constrain_input_reg,
+ &constrain_output_reg
};
static void emit(struct insn* insn)
break;
default:
+ /* FIXME: some instructions don't emit anything, so
+ * allocating a register for them is a waste of time. */
vreg = new_vreg();
}
insn->hop = current_hop = new_hop(0, insn->ir);
insn->hop->output = vreg;
+ if (vreg)
+ array_appendu(¤t_hop->outs, vreg);
+
emit(insn);
hop_print('I', current_hop);
pass_promote_float_ops(proc);
print_blocks('6', proc);
-
pass_instruction_selector(proc);
+
+ register_allocator(proc);
}
static bool collect_outputs_cb(struct ir* ir, void* user)
+#include "mcg.h"
+
+static ARRAYOF(struct basicblock) blocks;
+
+static void recursively_walk_blocks(struct basicblock* bb)
+{
+ int i;
+
+ if (array_appendu(&blocks, bb))
+ return;
+ tracef('R', "R: considering block %s\n", bb->name);
+
+ for (i=0; i<bb->nexts.count; i++)
+ recursively_walk_blocks(bb->nexts.item[i]);
+}
+
+static void order_blocks(struct procedure* proc)
+{
+ /* Put them into preorder; this ensures that when we do the allocation,
+ * we do all of a block's predecessors before the block (except for
+ * backward edges). */
+
+ blocks.count = 0;
+ recursively_walk_blocks(proc->blocks.item[0]);
+ assert(blocks.count == proc->blocks.count);
+}
+
+void register_allocator(struct procedure* proc)
+{
+ order_blocks(proc);
+}
+
/* vim: set sw=4 ts=4 expandtab : */
DECLARATIONS
- reg;
cc;
address fragment;
/* Special */
- reg;
-
PAIR(BLOCK4, BLOCK4);
/* Miscellaneous special things */
- PUSH4(in:reg)
+ PUSH4(in:(int)reg)
emit "push %in"
cost 4;
- reg = POP4
- with int reg
- emit "pop %reg"
+ out:(int)reg = POP4
+ emit "pop %out"
cost 4;
RET
emit "ret"
cost 4;
- SETRET4(in:reg)
- with ret reg
+ SETRET4(in:(ret)reg)
emit "mov r0, %in"
cost 4;
emit "add sp, sp, %delta"
cost 4;
- reg = in:REG
- cost 1;
-
- reg = NOP(in:reg)
- cost 1;
-
/* Memory operations */
- STORE4(addr:address, value:reg)
- with int value
+ STORE4(addr:address, value:(int)reg)
emit "str %value, %addr"
cost 4;
- STORE1(addr:address, value:reg)
- with int value
+ STORE1(addr:address, value:(int)reg)
emit "strb %value, %addr"
cost 4;
- reg = LOAD4(addr:address)
- with int reg
- emit "ldr %reg, %addr"
+ out:(int)reg = LOAD4(addr:address)
+ emit "ldr %out, %addr"
cost 4;
- reg = LOAD1(addr:address)
- with int reg
- emit "ldrb %reg, %addr"
+ out:(int)reg = LOAD1(addr:address)
+ emit "ldrb %out, %addr"
cost 4;
- reg = CIU14(LOAD1(addr:address))
- with int reg
- emit "ldrb %reg, %addr"
+ out:(int)reg = CIU14(LOAD1(addr:address))
+ emit "ldrb %out, %addr"
cost 4;
- reg = CII14(CIU41(CIU14(LOAD1(addr:address))))
- with int reg
- emit "ldrsb %reg, %addr"
+ out:(int)reg = CII14(CIU41(CIU14(LOAD1(addr:address))))
+ emit "ldrsb %out, %addr"
cost 4;
/* Locals */
- reg = in:LOCAL4
- with int reg
- emit "add %reg, fp, #$in"
+ out:(int)reg = in:LOCAL4
+ emit "add %out, fp, #$in"
cost 4;
address = in:LOCAL4
/* Memory addressing modes */
- address = ADD4(addr:reg, offset:CONST4)
- with int addr
+ address = ADD4(addr:(int)reg, offset:CONST4)
emit "[%addr, #$offset]";
- address = ADD4(addr1:reg, addr2:reg)
- with int addr1, int addr2
+ address = ADD4(addr1:(int)reg, addr2:(int)reg)
emit "[%addr1, %addr2]";
- address = addr:reg
- with int addr
+ address = addr:(int)reg
emit "[%addr]";
emit "b $addr"
cost 4;
- CJUMPEQ(value:cc, PAIR(true:BLOCK4, false:BLOCK4))
+ CJUMPEQ(value:(cc)cc, PAIR(true:BLOCK4, false:BLOCK4))
emit "beq $true"
emit "b $false"
cost 8;
- CJUMPLE(value:cc, PAIR(true:BLOCK4, false:BLOCK4))
+ CJUMPLE(value:(cc)cc, PAIR(true:BLOCK4, false:BLOCK4))
emit "ble $true"
emit "b $false"
cost 8;
- CJUMPLT(value:cc, PAIR(true:BLOCK4, false:BLOCK4))
+ CJUMPLT(value:(cc)cc, PAIR(true:BLOCK4, false:BLOCK4))
emit "blt $true"
emit "b $false"
cost 8;
/* Comparisons */
- cc = COMPARES4(left:reg, right:aluparam)
- with cc cc
+ (cc)cc = COMPARES4(left:(int)reg, right:aluparam)
emit "cmp %left, %right"
cost 4;
- cc = COMPARES4(COMPARES4(left:reg, right:aluparam), CONST4)
- with cc cc
+ (cc)cc = COMPARES4(COMPARES4(left:(int)reg, right:aluparam), CONST4)
emit "cmp %left, %right"
cost 4;
- reg = cc
- with int reg
- emit "mov %reg, #0"
- emit "movlt %reg, #-1"
- emit "movgt %reg, #1"
+ out:(int)reg = (cc)cc
+ emit "mov %out, #0"
+ emit "movlt %out, #-1"
+ emit "movgt %out, #1"
cost 12;
/* Conversions */
- reg = CII14(CIU41(value:reg))
- with int reg
- emit "sxtb %reg, %value"
+ out:(int)reg = CII14(CIU41(value:(int)reg))
+ emit "sxtb %out, %value"
cost 4;
- reg = CIU41(in:reg)
- with int reg
- emit "and %reg, %in, #0xff"
+ out:(int)reg = CIU41(in:(int)reg)
+ emit "and %out, %in, #0xff"
cost 4;
/* ALU operations */
- reg = ADD4(left:reg, right:aluparam)
- with int reg
- emit "add %reg, %left, %right"
+ out:(int)reg = ADD4(left:(int)reg, right:aluparam)
+ emit "add %out, %left, %right"
cost 4;
- reg = ADD4(left:aluparam, right:reg)
- with int reg
- emit "add %reg, %right, %left"
+ out:(int)reg = ADD4(left:aluparam, right:(int)reg)
+ emit "add %out, %right, %left"
cost 4;
- reg = MOD4(left:reg, right:reg)
- with int reg
- emit "udiv %reg, %left, %right"
- emit "mls %reg, %reg, %right, %left"
+ out:(int)reg = MOD4(left:(int)reg, right:(int)reg)
+ emit "udiv %out, %left, %right"
+ emit "mls %out, %out, %right, %left"
cost 8;
- reg = DIV4(left:reg, right:aluparam)
- with int reg
- emit "div %reg, %left, %right"
+ out:(int)reg = DIV4(left:(int)reg, right:aluparam)
+ emit "div %out, %left, %right"
cost 4;
aluparam = value:CONST4
emit "#$value";
- aluparam = value:reg
+ aluparam = value:(int)reg
emit "%value";
- reg = value:aluparam
- with int reg
- emit "mov %reg, %value"
+ out:(int)reg = value:aluparam
+ emit "mov %out, %value"
cost 4;
- reg = value:LABEL4
- with int reg
- emit "adr %reg, $value"
+ out:(int)reg = value:LABEL4
+ emit "adr %out, $value"
cost 4;
- reg = value:BLOCK4
- with int reg
- emit "adr %reg, $value"
+ out:(int)reg = value:BLOCK4
+ emit "adr %out, $value"
cost 4;
- reg = value:CONST4
- with int reg
- emit "ldr %reg, address-containing-$value"
+ out:(int)reg = value:CONST4
+ emit "ldr %out, address-containing-$value"
cost 8;
- reg = value:CONSTF4
- with int reg
- emit "vldr %reg, address-containing-$value"
+ out:(int)reg = value:CONSTF4
+ emit "vldr %out, address-containing-$value"
cost 8;
/* FPU operations */
- reg = ADDF4(left:reg, right:reg)
- with int reg
- emit "fadds %reg, %left, %right"
+ out:(float)reg = ADDF4(left:(float)reg, right:(float)reg)
+ emit "fadds %out, %left, %right"
cost 4;
/* vim: set sw=4 ts=4 expandtab : */
extern int yylex(void);
-static int nextern = 1;
-
%}
%union {
int n;
declaration
: ID { $$ = nonterm($1, true); }
| declaration FRAGMENT { $$ = $1; $$->is_fragment = true; }
- | allocates { $$ = $1; }
- ;
-
-allocates
- : declaration ALLOCATES '(' ID ')'
- {
- $$ = $1;
- if ($$->allocate)
- yyerror("pattern type is defined to already allocate a register");
- $$->allocate = getregattr($4);
- }
;
patterns
;
pattern
- : ID '=' rhs { nonterm($1, false); $$ = rule($1, $3, nextern++); }
- | rhs { $$ = rule("stmt", $1, nextern++); }
+ : terminfo '=' rhs { nonterm($1.name, false); $$ = rule(&$1, $3); }
+ | rhs { $$ = rule(NULL, $1); }
| pattern PREFERS predicate { $$ = $1; array_append(&$$->prefers, $3); }
| pattern WHEN predicate { $$ = $1; array_append(&$$->requires, $3); }
| pattern COST INT { $$ = $1; $$->cost = $3; }
terminfo
: ID { $$.name = $1; }
+ | '(' ID ')' ID { $$.attr = $2; $$.name = $4; }
| ID ':' ID { $$.label = $1; $$.name = $3; }
+ | ID ':' '(' ID ')' ID { $$.label = $1; $$.attr = $4; $$.name = $6; }
;
pattern_emit
#include "ircodes.h"
#include "astring.h"
#include "smap.h"
+#include "mcgg.h"
static char rcsid[] = "$Id$";
makeregattr("bytes4");
makeregattr("bytes8");
+ /* Define some standard terms. */
+
+ {
+ const static struct terminfo reg = { "reg", NULL, "" };
+ const static struct terminfo REG = { "REG", NULL, NULL };
+ const static struct terminfo NOP = { "NOP", NULL, NULL };
+
+ nonterm("reg", true);
+
+ rule(NULL, tree(®, NULL, NULL))->cost = 1;
+ rule(®, tree(®, NULL, NULL))->cost = 1;
+ rule(®, tree(&NOP, tree(®, NULL, NULL), NULL))->cost = 1;
+ }
+
yyin = infp;
yyparse();
}
/* tree - create & initialize a tree node with the given fields */
-Tree tree(struct terminfo* ti, Tree left, Tree right)
+Tree tree(const struct terminfo* ti, Tree left, Tree right)
{
Tree t = calloc(1, sizeof *t);
Term p = lookup(ti->name);
if (p->kind == TERM && arity != p->arity)
yyerror("inconsistent arity for terminal `%s'\n", ti->name);
t->op = p;
- t->label = ti->label;
t->nterms = p->kind == TERM;
if (t->left = left)
t->nterms += left->nterms;
if (t->right = right)
t->nterms += right->nterms;
+
+ /* Special rules that have no output register attribute use "" as the
+ * attribute name; these can't be made by the grammar. */
+
+ t->label = ti->label;
+ if ((p->kind == TERM) && (ti->attr))
+ yyerror("can't specify an input register attribute for terminal '%s'", ti->name);
+ if (p->kind == NONTERM)
+ {
+ Nonterm nt = (Nonterm)p;
+ if (nt->is_fragment && ti->attr)
+ yyerror("can't specify an input register attribute for fragment '%s'", ti->name);
+ if (!nt->is_fragment && !ti->attr)
+ yyerror("must specify an input register attribute for non-fragment '%s'", ti->name);
+
+ if (ti->attr && ti->attr[0])
+ {
+ nt->attr = smap_get(®isterattrs, ti->attr);
+ if (!nt->attr)
+ yyerror("'%s' doesn't seem to be a known register attribute", ti->attr);
+ }
+ }
return t;
}
/* rule - create & initialize a rule with the given fields */
-Rule rule(char* id, Tree pattern, int ern)
+Rule rule(const struct terminfo* ti, Tree pattern)
{
+ static int number = 1;
+ static const struct terminfo stmt = { "stmt", NULL, NULL };
Rule r = calloc(1, sizeof *r);
Rule *q;
Term p = pattern->op;
+ if (!ti)
+ ti = &stmt;
+
nrules++;
r->lineno = yylineno;
- r->lhs = nonterm(id, false);
+ r->lhs = nonterm(ti->name, false);
r->packed = ++r->lhs->lhscount;
for (q = &r->lhs->rules; *q; q = &(*q)->decode)
;
*q = r;
r->pattern = pattern;
- r->ern = ern;
+ r->ern = number++;
if (p->kind == TERM)
{
r->next = p->rules;
yyerror("duplicate external rule number `%d'\n", r->ern);
r->link = *q;
*q = r;
+
+ r->label = ti->label;
+ if (r->lhs->is_fragment && ti->attr)
+ yyerror("can't specify an output register attribute for a fragment");
+ if (!r->lhs->is_fragment && !ti->attr && (r->lhs->number != NONTERM_STMT))
+ yyerror("must specify an output register attribute for non-fragments");
+
+ /* Special rules that have no output register attribute use "" as the
+ * attribute name; these can't be made by the grammar. */
+
+ if (ti->attr && ti->attr[0])
+ {
+ r->attr = smap_get(®isterattrs, ti->attr);
+ if (!r->attr)
+ yyerror("'%s' doesn't seem to be a known register attribute", ti->attr);
+ }
+
return r;
}
}
}
+static void emit_input_regs(Tree node, int* index)
+{
+ /* This must return the same ordering as the burm_kids() function uses. */
+
+ Nonterm nt = node->op;
+ if ((nt->kind == NONTERM) && !nt->is_fragment && !node->left && !node->right)
+ {
+ uint32_t attr = 0;
+ if (nt->attr->number)
+ attr = 1<<nt->attr->number;
+ print("%1data->constrain_input_reg(%d, 0x%x);\n", *index, attr);
+ }
+
+ if (!node->left && !node->right)
+ (*index)++;
+
+ if (node->left)
+ emit_input_regs(node->left, index);
+ if (node->right)
+ emit_input_regs(node->right, index);
+}
+
/* emitinsndata - emit the code generation data */
static void emitinsndata(Rule rules)
{
print("/* %R */\n", r);
print("static void %Pemitter_%d(const struct %Pemitter_data* data) {\n", r->ern);
+ if (r->attr)
+ print("%1data->constrain_output_reg(0x%x);\n", 1<<r->attr->number);
+
+ {
+ int index = 0;
+ emit_input_regs(r->pattern, &index);
+ }
+
while (f)
{
switch (f->data[0])
{
const char* label = f->data + 1;
- if (strcmp(label, r->lhs->name) == 0)
+ if (r->label && (strcmp(label, r->label) == 0))
print("%1data->emit_return_reg();\n", label);
else
{
print("%2&%Pemitter_%d,\n", r->ern);
- if (r->lhs->allocate)
- print("%2%d,\n", r->lhs->allocate->number);
- else
- print("%20,\n");
-
print("%2%s,\n", r->lhs->is_fragment ? "true" : "false");
print("%1},\n");
{
const char* name;
const char* label;
+ const char* attr;
};
struct reg
Rule chain; /* chain rules w/non-terminal on rhs */
Nonterm link; /* next terminal in number order */
bool is_fragment; /* these instructions are all fragments */
- struct regattr* allocate; /* allocate this kind of register */
+ struct regattr* attr; /* input register attribute */
};
extern void* lookup(const char* name);
extern Nonterm nonterm(const char* id, bool allocate);
Tree left, right; /* operands */
int nterms; /* number of terminal nodes in this tree */
};
-extern Tree tree(struct terminfo* ti, Tree left, Tree right);
+extern Tree tree(const struct terminfo* ti, Tree left, Tree right);
struct rule
{ /* rules: */
int ern; /* external rule number */
int packed; /* packed external rule number */
int cost; /* associated cost */
+ const char* label; /* label for LHS */
+ struct regattr* attr; /* register attribute of result */
Rule link; /* next rule in ern order */
Rule next; /* next rule with same pattern root */
Rule chain; /* next chain rule with same rhs */
ARRAYOF(struct constraint) constraints; /* register constraints */
struct stringlist code; /* compiler output code strings */
};
-extern Rule rule(char* id, Tree pattern, int ern);
+extern Rule rule(const struct terminfo* ti, Tree pattern);
extern int maxcost; /* maximum cost */
/* gram.y: */
void (*emit_reg)(int child);
void (*emit_value)(int child);
void (*emit_eoi)(void);
+ void (*constrain_input_reg)(int child, int attr);
+ void (*constrain_output_reg)(int attr);
};
typedef void burm_emitter_t(const struct burm_emitter_data* data);
{
const char* name;
burm_emitter_t* emitter;
- int allocate;
bool is_fragment;
};
REGATTR_BYTES8
};
+enum
+{
+ NONTERM_STMT = 1
+};
+
#endif
/* vim: set sw=4 ts=4 expandtab : */