Sort of keep track of registers and register classes. Start walking the

generated instruction tree --- holy cow, they look like instructions!
This commit is contained in:
David Given 2016-09-25 22:17:14 +02:00
parent bde5792b1a
commit 39aa672422
9 changed files with 346 additions and 116 deletions

View file

@ -35,6 +35,7 @@ struct ir
} u; } u;
void* state_label; /* used by the iburg instruction selector */ void* state_label; /* used by the iburg instruction selector */
int insn_no;
bool is_sequence : 1; bool is_sequence : 1;
bool is_generated : 1; bool is_generated : 1;

View file

@ -7,7 +7,10 @@
static int OP_LABEL(struct ir* ir) static int OP_LABEL(struct ir* ir)
{ {
if (ir->is_generated) if (ir->is_generated)
{
assert(ir->is_sequence);
return ir_to_esn(IR_REG, ir->size); return ir_to_esn(IR_REG, ir->size);
}
return ir_to_esn(ir->opcode, ir->size); return ir_to_esn(ir->opcode, ir->size);
} }

View file

@ -38,25 +38,75 @@ void burm_panic_cannot_match(struct ir* ir)
exit(1); exit(1);
} }
static void queue_instructions(struct ir* ir, int goal) static const struct burm_emitter_data emitter_data;
static void emit_string(const char* data)
{ {
struct ir* children[10]; tracef('I', "I: emit: %s\n", data);
int ruleno = burm_rule(ir->state_label, goal);
const struct burm_instruction_data* insndata = &burm_instruction_data[ruleno];
const short* nts = burm_nts[ruleno];
int i;
burm_kids(ir, ruleno, children);
for (i=0; nts[i]; i++)
queue_instructions(children[i], nts[i]);
tracef('I', "I: $%d selected %s %d: %s\n",
ir->id,
insndata->is_fragment ? "fragment" : "instruction",
ruleno,
insndata->name);
} }
static void emit_reg(struct ir* ir)
{
const struct burm_instruction_data* insndata = &burm_instruction_data[ir->insn_no];
if (insndata->is_fragment)
insndata->emitter(ir, &emitter_data);
else
tracef('I', "I: emit reg $%d\n", ir->id);
}
static void emit_value(struct ir* ir)
{
tracef('I', "I: emit value\n");
}
static void emit_resultreg(void)
{
tracef('I', "I: emit resultreg\n");
}
static void emit_eoi(void)
{
tracef('I', "I: emit eoi\n");
}
static const struct burm_emitter_data emitter_data =
{
&emit_string,
&emit_reg,
&emit_value,
&emit_resultreg,
&emit_eoi
};
static void walk_instructions(struct ir* ir, int goal)
{
struct ir* children[10];
int insn_no = burm_rule(ir->state_label, goal);
const struct burm_instruction_data* insndata = &burm_instruction_data[insn_no];
const short* nts = burm_nts[insn_no];
int i;
ir->insn_no = insn_no;
burm_kids(ir, insn_no, children);
for (i=0; nts[i]; i++)
walk_instructions(children[i], nts[i]);
tracef('I', "I: $%d %s selected %s %d: %s\n",
ir->id,
ir->is_sequence ? "S" : " ",
insndata->is_fragment ? "fragment" : "instruction",
insn_no,
insndata->name);
ir->is_generated = true;
if (insndata->allocate)
tracef('I', "I: allocate reg of class %d\n", insndata->allocate);
if (!insndata->is_fragment && insndata->emitter)
insndata->emitter(ir, &emitter_data);
}
static void select_instructions(struct basicblock* bb) static void select_instructions(struct basicblock* bb)
{ {
int i; int i;
@ -73,7 +123,7 @@ static void select_instructions(struct basicblock* bb)
if (!insnno) if (!insnno)
burm_panic_cannot_match(ir); burm_panic_cannot_match(ir);
queue_instructions(ir, 1); walk_instructions(ir, 1);
} }
} }
@ -86,7 +136,6 @@ void pass_instruction_selector(struct procedure* proc)
struct basicblock* bb = proc->blocks[i]; struct basicblock* bb = proc->blocks[i];
select_instructions(bb); select_instructions(bb);
} }
exit(1);
} }
/* vim: set sw=4 ts=4 expandtab : */ /* vim: set sw=4 ts=4 expandtab : */

View file

@ -1,9 +1,35 @@
REGISTERS
r0 GPR RET0;
r1 GPR RET1;
r2 GPR;
r3 GPR;
r4 GPR;
r5 GPR;
r6 GPR;
r7 GPR;
r8 GPR;
r9 GPR;
r10 GPR;
r11 GPR;
cc CC;
DECLARATIONS
address fragment;
aluparam fragment;
reg allocates(GPR);
tristate allocates(CC);
PATTERNS PATTERNS
/* Special */ /* Special */
reg; reg;
reg = REG4;
PAIR(BLOCK4, BLOCK4); PAIR(BLOCK4, BLOCK4);
@ -25,6 +51,7 @@ PATTERNS
emit "mov r0, %in" emit "mov r0, %in"
cost 4; cost 4;
/* Memory operations */ /* Memory operations */
STORE4(addr:address, value:reg) STORE4(addr:address, value:reg)
@ -55,16 +82,16 @@ PATTERNS
cost 4; cost 4;
address = in:LOCAL4 address = in:LOCAL4
fragment "[fp, #$in]"; emit "[fp, #$in]";
/* Memory addressing modes */ /* Memory addressing modes */
address = ADD4(addr:reg, offset:CONST) address = ADD4(addr:reg, offset:CONST4)
fragment "[%addr, #$offset]"; emit "[%addr, #$offset]";
address = addr:reg address = addr:reg
fragment "[%addr]"; emit "[%addr]";
/* Branches */ /* Branches */
@ -82,7 +109,6 @@ PATTERNS
/* Comparisons */ /* Comparisons */
tristate = COMPARES4(left:reg, right:aluparam) tristate = COMPARES4(left:reg, right:aluparam)
outs CC
emit "cmp %left, %right" emit "cmp %left, %right"
cost 4; cost 4;
@ -115,7 +141,7 @@ PATTERNS
cost 4; cost 4;
aluparam = value:CONST4 aluparam = value:CONST4
fragment "#$value"; emit "#$value";
aluparam = reg; aluparam = reg;

View file

@ -16,40 +16,80 @@ static int nextern = 1;
%union { %union {
int n; int n;
char* string; char* string;
Nonterm nonterm;
Tree tree; Tree tree;
Rule rule; Rule rule;
struct reg* reg;
struct stringlist* stringlist; struct stringlist* stringlist;
char* stringpair[2]; char* stringpair[2];
} }
%term TERMINAL
%term START
%term PPERCENT
%term PATTERNS %term ALLOCATES
%term WHEN %term COST
%term DECLARATIONS
%term EMIT %term EMIT
%term FRAGMENT %term FRAGMENT
%term COST
%term INS %term INS
%term OUTS %term OUTS
%term PATTERNS
%term REGISTERS
%term WHEN
%token <n> INT %token <n> INT
%token <string> ID %token <string> ID
%token <string> CFRAGMENT %token <string> CFRAGMENT
%token <string> QFRAGMENT %token <string> QFRAGMENT
%type <rule> pattern %type <nonterm> allocates
%type <nonterm> declaration
%type <reg> register
%type <rule> emit %type <rule> emit
%type <rule> pattern
%type <stringlist> cfragments %type <stringlist> cfragments
%type <stringlist> qfragments %type <stringlist> qfragments
%type <tree> rhs
%type <stringpair> labelledid %type <stringpair> labelledid
%type <tree> rhs
%% %%
spec spec
: PATTERNS patterns : REGISTERS registers
DECLARATIONS declarations
PATTERNS patterns
; ;
registers
: /* nothing */
| registers register ';'
| register ';'
;
register
: ID { $$ = makereg($1); }
| register ID { $$ = $1; addregclass($1, $2); }
;
declarations
: /* nothing */
| declarations declaration ';'
| declaration ';'
;
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 = getregclass($4);
}
;
patterns patterns
: /* nothing */ : /* nothing */
| patterns pattern ';' | patterns pattern ';'
@ -57,11 +97,9 @@ patterns
; ;
pattern pattern
: ID '=' rhs { nonterm($1); $$ = rule($1, $3, nextern++); } : ID '=' rhs { nonterm($1, false); $$ = rule($1, $3, nextern++); }
| rhs { $$ = rule("stmt", $1, nextern++); } | rhs { $$ = rule("stmt", $1, nextern++); }
| pattern WHEN cfragments { $$ = $1; stringlist_addall(&$$->when, $3); } | pattern WHEN cfragments { $$ = $1; stringlist_addall(&$$->when, $3); }
| pattern INS ins { $$ = $1; }
| pattern OUTS outs { $$ = $1; }
| emit { $$ = $1; } | emit { $$ = $1; }
| pattern COST INT { $$ = $1; $$->cost = $3; } | pattern COST INT { $$ = $1; $$->cost = $3; }
; ;
@ -82,42 +120,18 @@ cfragments
| cfragments CFRAGMENT { $$ = $1; stringlist_add($$, $2); } | cfragments CFRAGMENT { $$ = $1; stringlist_add($$, $2); }
; ;
ins
: ins ',' in
| in
;
in
: ID ':' ID
;
outs
: outs ',' out
| out
;
out
: ID
| ID ':' ID
;
emit emit
: pattern EMIT qfragments { : pattern EMIT qfragments {
$$ = $1; $$ = $1;
stringlist_add($3, "\n"); if (!$$->lhs->is_fragment)
stringlist_add($3, "\n");
stringlist_addall(&$$->code, $3); stringlist_addall(&$$->code, $3);
$$->is_fragment = false;
}
| pattern FRAGMENT qfragments {
$$ = $1;
stringlist_addall(&$$->code, $3);
$$->is_fragment = true;
} }
; ;
qfragments qfragments
: /* nothing */ { $$ = calloc(1, sizeof *$$); } : /* nothing */ { $$ = calloc(1, sizeof *$$); }
| qfragments QFRAGMENT { $$ = $1; stringlist_add($$, $2); } | qfragments QFRAGMENT { $$ = $1; stringlist_add($$, $2); }
; ;
%% %%

View file

@ -20,6 +20,8 @@ static char* prefix = "burm";
static int Tflag = 1; /* tracing */ static int Tflag = 1; /* tracing */
static int ntnumber = 0; static int ntnumber = 0;
static Nonterm start = 0; static Nonterm start = 0;
static struct reg* regs = NULL;
static struct regclass* regclasses = NULL;
static Term terms; static Term terms;
static Nonterm nts; static Nonterm nts;
static Rule rules; static Rule rules;
@ -34,14 +36,16 @@ static void emitcostcalc(Rule r);
static void emitdefs(Nonterm nts, int ntnumber); static void emitdefs(Nonterm nts, int ntnumber);
static void emitfuncs(void); static void emitfuncs(void);
static void emitheader(void); static void emitheader(void);
static void emitinsndata(Rule rules);
static void emitkids(Rule rules, int nrules); static void emitkids(Rule rules, int nrules);
static void emitlabel(Nonterm start); static void emitlabel(Nonterm start);
static void emitleaf(Term p, int ntnumber); static void emitleaf(Term p, int ntnumber);
static void emitnts(Rule rules, int nrules); static void emitnts(Rule rules, int nrules);
static void emitrecord(char* pre, Rule r, int cost);
static void emitrule(Nonterm nts);
static void emitpredicatedefinitions(Rule rules); static void emitpredicatedefinitions(Rule rules);
static void emitinsndata(Rule rules); static void emitrecord(char* pre, Rule r, int cost);
static void emitregisterclasses(struct regclass* rc);
static void emitregisters(struct reg* regs);
static void emitrule(Nonterm nts);
static void emitstate(Term terms, Nonterm start, int ntnumber); static void emitstate(Term terms, Nonterm start, int ntnumber);
static void emitstring(Rule rules); static void emitstring(Rule rules);
static void emitstruct(Nonterm nts, int ntnumber); static void emitstruct(Nonterm nts, int ntnumber);
@ -116,7 +120,7 @@ int main(int argc, char* argv[])
emitheader(); emitheader();
registerterminals(); registerterminals();
start = nonterm("stmt"); start = nonterm("stmt", true);
yyin = infp; yyin = infp;
yyparse(); yyparse();
@ -127,6 +131,8 @@ int main(int argc, char* argv[])
if (!p->reached) if (!p->reached)
yyerror("can't reach non-terminal `%s'\n", p->name); yyerror("can't reach non-terminal `%s'\n", p->name);
emitregisterclasses(regclasses);
emitregisters(regs);
emitdefs(nts, ntnumber); emitdefs(nts, ntnumber);
emitstruct(nts, ntnumber); emitstruct(nts, ntnumber);
emitnts(rules, nrules); emitnts(rules, nrules);
@ -203,6 +209,8 @@ struct entry
const char* name; const char* name;
struct term t; struct term t;
struct nonterm nt; struct nonterm nt;
struct reg r;
struct regclass rc;
} sym; } sym;
struct entry* link; struct entry* link;
} * table[211]; } * table[211];
@ -219,7 +227,7 @@ static unsigned hash(const char* str)
} }
/* lookup - lookup symbol name */ /* lookup - lookup symbol name */
static void* lookup(const char* name) void* lookup(const char* name)
{ {
struct entry* p = table[hash(name) % HASHSIZE]; struct entry* p = table[hash(name) % HASHSIZE];
@ -241,15 +249,69 @@ static void* install(const char* name)
return &p->sym; return &p->sym;
} }
/* nonterm - create a new terminal id, if necessary */ struct reg* makereg(const char* id)
Nonterm nonterm(const char* id)
{ {
Nonterm p = lookup(id), * q = &nts; struct reg* p = lookup(id);
struct reg** q = &regs;
static int number = 1;
if (p)
yyerror("redefinition of '%s'", id);
p = install(id);
p->kind = REG;
p->number = number++;
while (*q && (*q)->number < p->number)
q = &(*q)->link;
assert(*q == 0 || (*q)->number != p->number);
p->link = *q;
*q = p;
return p;
}
void addregclass(struct reg* reg, const char* id)
{
struct regclass* p = lookup(id);
struct regclass** q = &regclasses;
static int number = 1;
if (p && (p->kind != REGCLASS))
yyerror("redefinition of '%s' as something else\n", id);
if (!p)
{
p = install(id);
p->kind = REGCLASS;
p->number = number++;
while (*q && (*q)->number < p->number)
q = &(*q)->link;
assert(*q == 0 || (*q)->number != p->number);
p->link = *q;
*q = p;
}
}
struct regclass* getregclass(const char* id)
{
struct regclass* p = lookup(id);
if (!p || (p->kind != REGCLASS))
yyerror("'%p' is not the name of a register class");
return p;
}
/* nonterm - create a new terminal id, if necessary */
Nonterm nonterm(const char* id, bool allocate)
{
Nonterm p = lookup(id);
Nonterm* q = &nts;
if (p && p->kind == NONTERM) if (p && p->kind == NONTERM)
return p; return p;
if (p && p->kind == TERM) if (p)
yyerror("`%s' is a terminal\n", id); yyerror("redefinition of '%s' as something else\n", id);
if (!allocate)
yyerror("'%s' has not been declared\n", id);
p = install(id); p = install(id);
p->kind = NONTERM; p->kind = NONTERM;
p->number = ++ntnumber; p->number = ++ntnumber;
@ -270,12 +332,9 @@ Term term(const char* id, int esn)
Term* q = &terms; Term* q = &terms;
if (p) if (p)
{ yyerror("redefinition of '%s'\n", id);
yyerror("redefinition of terminal `%s'\n", id);
exit(1); p = install(id);
}
else
p = install(id);
p->kind = TERM; p->kind = TERM;
p->esn = esn; p->esn = esn;
p->arity = -1; p->arity = -1;
@ -309,7 +368,7 @@ Tree tree(const char* id, const char* label, Tree left, Tree right)
p = term(id, -1); p = term(id, -1);
} }
else if (p == NULL && arity == 0) else if (p == NULL && arity == 0)
p = (Term)nonterm(id); p = (Term)nonterm(id, false);
else if (p && p->kind == NONTERM && arity > 0) else if (p && p->kind == NONTERM && arity > 0)
{ {
yyerror("`%s' is a non-terminal\n", id); yyerror("`%s' is a non-terminal\n", id);
@ -338,7 +397,7 @@ Rule rule(char* id, Tree pattern, int ern)
nrules++; nrules++;
r->lineno = yylineno; r->lineno = yylineno;
r->lhs = nonterm(id); r->lhs = nonterm(id, false);
r->packed = ++r->lhs->lhscount; r->packed = ++r->lhs->lhscount;
for (q = &r->lhs->rules; *q; q = &(*q)->decode) for (q = &r->lhs->rules; *q; q = &(*q)->decode)
; ;
@ -452,6 +511,38 @@ static void ckreach(Nonterm p)
reach(r->pattern); reach(r->pattern);
} }
static void emitregisterclasses(struct regclass* rc)
{
int k = 0;
print("const char* %Pregister_class_names[] = {\n");
while (rc)
{
for (; k < rc->number; k++)
print("%1NULL,\n");
k++;
print("%1\"%s\",\n", rc->name);
rc = rc->link;
}
print("};\n\n");
}
static void emitregisters(struct reg* r)
{
int k = 0;
print("const char* %Pregister_names[] = {\n");
while (r)
{
for (; k < r->number; k++)
print("%1NULL,\n");
k++;
print("%1\"%s\",\n", r->name);
r = r->link;
}
print("};\n\n");
}
/* emitcase - emit one case in function state */ /* emitcase - emit one case in function state */
static void emitcase(Term p, int ntnumber) static void emitcase(Term p, int ntnumber)
{ {
@ -841,6 +932,13 @@ static uint32_t find_label(Tree root, const char* name, uint32_t path)
return p; return p;
} }
static void label_not_found(Rule rule, const char* label)
{
yylineno = rule->lineno;
yyerror("label '%s' not found", label);
exit(1);
}
/* emitinsndata - emit the code generation data */ /* emitinsndata - emit the code generation data */
static void emitinsndata(Rule rules) static void emitinsndata(Rule rules)
{ {
@ -854,7 +952,7 @@ static void emitinsndata(Rule rules)
if (f) if (f)
{ {
print("/* %R */\n", r); print("/* %R */\n", r);
print("static void %Pemitter_%d(NODEPTR_TYPE node, struct %Pemitter_data* data) {\n", r->ern); print("static void %Pemitter_%d(NODEPTR_TYPE node, const struct %Pemitter_data* data) {\n", r->ern);
while (f) while (f)
{ {
@ -864,20 +962,28 @@ static void emitinsndata(Rule rules)
{ {
const char* label = f->data + 1; const char* label = f->data + 1;
print("%1data->emit_ir(");
if (strcmp(label, r->lhs->name) == 0) if (strcmp(label, r->lhs->name) == 0)
print("node"); print("%1data->emit_resultreg();\n");
else else
{ {
uint32_t path = find_label(r->pattern, label, 0); uint32_t path = find_label(r->pattern, label, 0);
print("%1data->emit_reg(");
if (path == PATH_MISSING) if (path == PATH_MISSING)
{ label_not_found(r, label);
yylineno = r->lineno;
yyerror("label '%s' not found", label);
exit(1);
}
print_path(path); print_path(path);
print(");\n");
} }
break;
}
case '$':
{
const char* label = f->data + 1;
uint32_t path = find_label(r->pattern, label, 0);
print("%1data->emit_value(");
if (path == PATH_MISSING)
label_not_found(r, label);
print_path(path);
print(");\n"); print(");\n");
break; break;
} }
@ -919,7 +1025,12 @@ static void emitinsndata(Rule rules)
else else
print("NULL,\n"); print("NULL,\n");
print("%2%s,\n", r->is_fragment ? "true" : "false"); 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"); print("%1},\n");
r = r->link; r = r->link;

View file

@ -8,10 +8,33 @@ extern char* stringf(char* fmt, ...);
typedef enum typedef enum
{ {
TERM = 1, TERM = 1,
NONTERM NONTERM,
REG,
REGCLASS
} Kind; } Kind;
typedef struct rule* Rule; typedef struct rule* Rule;
typedef struct term* Term; typedef struct term* Term;
struct reg
{
const char* name; /* register name */
Kind kind; /* REG */
int number; /* identifying number */
struct reg* link; /* next in list */
};
struct regclass
{
const char* name; /* class name */
Kind kind; /* REGCLASS */
int number; /* identifying number */
struct regclass* link; /* next in list */
};
extern struct reg* makereg(const char* name);
extern void addregclass(struct reg* reg, const char* regclass);
extern struct regclass* getregclass(const char* name);
struct term struct term
{ /* terminals: */ { /* terminals: */
char* name; /* terminal name */ char* name; /* terminal name */
@ -25,16 +48,19 @@ struct term
typedef struct nonterm* Nonterm; typedef struct nonterm* Nonterm;
struct nonterm struct nonterm
{ /* non-terminals: */ { /* non-terminals: */
char* name; /* non-terminal name */ char* name; /* non-terminal name */
Kind kind; /* NONTERM */ Kind kind; /* NONTERM */
int number; /* identifying number */ int number; /* identifying number */
int lhscount; /* # times nt appears in a rule lhs */ int lhscount; /* # times nt appears in a rule lhs */
int reached; /* 1 iff reached from start non-terminal */ int reached; /* 1 iff reached from start non-terminal */
Rule rules; /* rules w/non-terminal on lhs */ Rule rules; /* rules w/non-terminal on lhs */
Rule chain; /* chain rules w/non-terminal on rhs */ Rule chain; /* chain rules w/non-terminal on rhs */
Nonterm link; /* next terminal in number order */ Nonterm link; /* next terminal in number order */
bool is_fragment; /* these instructions are all fragments */
struct regclass* allocate; /* allocate this kind of register */
}; };
extern Nonterm nonterm(const char* id); extern void* lookup(const char* name);
extern Nonterm nonterm(const char* id, bool allocate);
extern Term term(const char* id, int esn); extern Term term(const char* id, int esn);
typedef struct tree* Tree; typedef struct tree* Tree;
@ -62,7 +88,6 @@ struct rule
Rule kids; /* next rule with same burm_kids pattern */ Rule kids; /* next rule with same burm_kids pattern */
struct stringlist when; /* C predicate string */ struct stringlist when; /* C predicate string */
struct stringlist code; /* compiler output code strings */ struct stringlist code; /* compiler output code strings */
bool is_fragment; /* does this rule generate an instruction fragment? */
}; };
extern Rule rule(char* id, Tree pattern, int ern); extern Rule rule(char* id, Tree pattern, int ern);
extern int maxcost; /* maximum cost */ extern int maxcost; /* maximum cost */

View file

@ -26,18 +26,20 @@ extern void burm_trace(struct ir* p, int ruleno, int cost, int bestcost);
struct burm_emitter_data struct burm_emitter_data
{ {
void* user;
void (*emit_string)(const char* data); void (*emit_string)(const char* data);
void (*emit_ir)(struct ir* ir); void (*emit_reg)(struct ir* ir);
void (*emit_value)(struct ir* ir);
void (*emit_resultreg)(void);
void (*emit_eoi)(void); void (*emit_eoi)(void);
}; };
typedef void burm_emitter_t(struct ir* ir, struct burm_emitter_data* data); typedef void burm_emitter_t(struct ir* ir, const struct burm_emitter_data* data);
struct burm_instruction_data struct burm_instruction_data
{ {
const char* name; const char* name;
burm_emitter_t* emitter; burm_emitter_t* emitter;
int allocate;
bool is_fragment; bool is_fragment;
}; };

View file

@ -50,12 +50,12 @@ static int braces = 0;
<INITIAL>"\"" BEGIN(QSTRING); <INITIAL>"\"" BEGIN(QSTRING);
<QSTRING>"\"" BEGIN(INITIAL); <QSTRING>"\"" BEGIN(INITIAL);
<QSTRING>%[a-zA-Z_][a-zA_Z_0-9]+ { <QSTRING>[%$][a-zA-Z_][a-zA_Z_0-9]+ {
yylval.string = strdup(yytext); yylval.string = strdup(yytext);
return QFRAGMENT; return QFRAGMENT;
} }
<QSTRING>[^\r\n%"]+ { <QSTRING>[^\r\n%$"]+ {
yylval.string = strdup(yytext); yylval.string = strdup(yytext);
return QFRAGMENT; return QFRAGMENT;
} }
@ -65,17 +65,16 @@ static int braces = 0;
<COMMENT>[^*]* ; <COMMENT>[^*]* ;
<COMMENT>"*" ; <COMMENT>"*" ;
"%%" return PPERCENT; "DECLARATIONS" return DECLARATIONS;
"%term" return TERMINAL;
"%start" return START;
"PATTERNS" return PATTERNS; "PATTERNS" return PATTERNS;
"when" return WHEN; "REGISTERS" return REGISTERS;
"ins" return INS; "allocates" return ALLOCATES;
"outs" return OUTS; "cost" return COST;
"emit" return EMIT; "emit" return EMIT;
"fragment" return FRAGMENT; "fragment" return FRAGMENT;
"cost" return COST; "ins" return INS;
"outs" return OUTS;
"when" return WHEN;
"//"[^\n]*\n ; "//"[^\n]*\n ;