From 13c117d15d1835b8911a9e24a00f52231c559e3d Mon Sep 17 00:00:00 2001 From: David Given Date: Tue, 20 Sep 2016 20:37:16 +0200 Subject: [PATCH] Import iburg. --- util/mcgg/LICENSE | 20 ++ util/mcgg/LOG | 39 +++ util/mcgg/README | 58 ++++ util/mcgg/UPSTREAM | 6 + util/mcgg/custom.mk | 0 util/mcgg/gram.y | 174 ++++++++++ util/mcgg/iburg.1 | 285 ++++++++++++++++ util/mcgg/iburg.c | 746 ++++++++++++++++++++++++++++++++++++++++++ util/mcgg/iburg.h | 65 ++++ util/mcgg/makefile | 23 ++ util/mcgg/sample4.brg | 109 ++++++ util/mcgg/sample5.brg | 85 +++++ 12 files changed, 1610 insertions(+) create mode 100644 util/mcgg/LICENSE create mode 100644 util/mcgg/LOG create mode 100644 util/mcgg/README create mode 100644 util/mcgg/UPSTREAM create mode 100644 util/mcgg/custom.mk create mode 100644 util/mcgg/gram.y create mode 100644 util/mcgg/iburg.1 create mode 100644 util/mcgg/iburg.c create mode 100644 util/mcgg/iburg.h create mode 100644 util/mcgg/makefile create mode 100644 util/mcgg/sample4.brg create mode 100644 util/mcgg/sample5.brg diff --git a/util/mcgg/LICENSE b/util/mcgg/LICENSE new file mode 100644 index 000000000..b21f90758 --- /dev/null +++ b/util/mcgg/LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 1993,1994,1995,1996 David R. Hanson. + +Permission is hereby granted, free of charge, to any person obtaining a copy of this +software and associated documentation files (the "Software"), to deal in the Software +without restriction, including without limitation the rights to use, copy, modify, +merge, publish, distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be included in all copies +or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF +CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE +OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + diff --git a/util/mcgg/LOG b/util/mcgg/LOG new file mode 100644 index 000000000..3556af7cc --- /dev/null +++ b/util/mcgg/LOG @@ -0,0 +1,39 @@ +Tue Aug 12 10:03:34 PDT 1997 + +makefile: +Customized for use under NT. + +gram.y: +Fixed diagnostic formatting, handled cases when \n doesn't terminate +the input buffer; protected against versions of fgets that touch +buffer at EOF. + +sample4.brg: +Changed state value from int to long for 64-bit machines. + + +Tue May 7 14:20:11 PDT 1996 + +The distribution now includes the RCS files. +Specific changes are as follows; thanks to Francisco Arzu +(farzu@uvg.edu.gt) for these suggestions. + +gram.y: +Changed TERM to TERMINAL for bison. +Moved definition of yylineno for bison. +Changed hard-coded 32767 to maxcost, and checked +only costs against maxcost. +Replaced 0s in calls with NULL. + +iburg.1: +Document STATE_TYPE, -maxcost=ddd, and use of SHRT_MAX. + +iburg.c: +Implemented STATE_TYPE, default int. +Changed 32767 to maxcost, which is SHRT_MAX, SHRT_MAX/2 +(when sizeof(int)==sizeof(short)), or maxcost. Included limits.h. + + +Thu May 12 11:33:48 EDT 1994 + +initial release. diff --git a/util/mcgg/README b/util/mcgg/README new file mode 100644 index 000000000..339ca1553 --- /dev/null +++ b/util/mcgg/README @@ -0,0 +1,58 @@ +iburg -- A Code Generator Generator + +iburg is a code-generator generator that uses dynamic programming at +compile time. It's described in + +C. W. Fraser, D. R. Hanson and T. A. Proebsting, +Engineering a simple, efficient code generator generator, +ACM Letters on Prog. Languages and Systems 1, 3 (Sep. 1992), 213-226. +http://storage.webhop.net/documents/iburg.pdf + +iburg is written in and generates ANSI C and thus must be compiled +with an ANSI C compiler and preprocessor, e.g., gcc or lcc. To compile +iburg, type "make". There should be no warnings or errors (except +perhaps in the system-dependent YACC skeleton). If you need to +customize the makefile, edit custom.mk, which is included in makefile. +The default custom.mk is empty. + +sample.brg is from the paper in burg.ps, sample4.brg is from the paper +in iburg.ps, and sample5.brg is an example from a compilers course. +"make test" runs iburg on sample[45].brg and executes the resulting +programs. The output should be something like: + +% make test +./iburg -I sample4.brg sample4.c; cc -o test4 sample4.c; ./test4 +sample4.c +i = c + 4; +stmt: ASGNI(disp,reg) + disp: ADDRLP + reg: disp + disp: ADDI(reg,con) + reg: CVCI(INDIRC(disp)) + disp: ADDRLP + con: CNSTI +./iburg -I sample5.brg sample5.c; cc -o test5 sample5.c; ./test5 +sample5.c +stm: MOVE(MEM(loc),reg) + loc: NAME + reg: PLUS(MEM(loc),reg) + loc: PLUS(NAME,reg) + reg: MEM(loc) + loc: NAME + reg: con + con: CONST +% + +To install iburg, copy it and its man page to the appropriate local +directories, e.g., on UNIX: + +% cp iburg /usr/local +% cp iburg.1 /usr/local/man/man1 + +"make clobber" removes the executables and all derived files except +gram.c; "make clean" removes just object, core, and sample*.c files. + +Mail bug reports along with the shortest input that exposes them to +drh@drhanson.net. + +$Id$ diff --git a/util/mcgg/UPSTREAM b/util/mcgg/UPSTREAM new file mode 100644 index 000000000..9cd9ae41e --- /dev/null +++ b/util/mcgg/UPSTREAM @@ -0,0 +1,6 @@ +This is a copy of iburg from head of git here: + +https://github.com/drh/iburg + +Version: 2151dd7d126e2f804b822bb78059c870a3a15f5e + diff --git a/util/mcgg/custom.mk b/util/mcgg/custom.mk new file mode 100644 index 000000000..e69de29bb diff --git a/util/mcgg/gram.y b/util/mcgg/gram.y new file mode 100644 index 000000000..965c9e9df --- /dev/null +++ b/util/mcgg/gram.y @@ -0,0 +1,174 @@ +%{ +#include +#include +#include +#include "iburg.h" +static char rcsid[] = "$Id$"; +static int yylineno = 0; +%} +%union { + int n; + char *string; + Tree tree; +} +%term TERMINAL +%term START +%term PPERCENT + +%token ID +%token INT +%type lhs +%type tree +%type cost +%% +spec : decls PPERCENT rules { yylineno = 0; } + | decls { yylineno = 0; } + ; + +decls : /* lambda */ + | decls decl + ; + +decl : TERMINAL blist '\n' + | START lhs '\n' { + if (nonterm($2)->number != 1) + yyerror("redeclaration of the start symbol\n"); + } + | '\n' + | error '\n' { yyerrok; } + ; + +blist : /* lambda */ + | blist ID '=' INT { term($2, $4); } + ; + +rules : /* lambda */ + | rules lhs ':' tree '=' INT cost ';' '\n' { rule($2, $4, $6, $7); } + | rules '\n' + | rules error '\n' { yyerrok; } + ; + +lhs : ID { nonterm($$ = $1); } + ; + +tree : ID { $$ = tree($1, NULL, NULL); } + | ID '(' tree ')' { $$ = tree($1, $3, NULL); } + | ID '(' tree ',' tree ')' { $$ = tree($1, $3, $5); } + ; + +cost : /* lambda */ { $$ = 0; } + | '(' INT ')' { if ($2 > maxcost) { + yyerror("%d exceeds maximum cost of %d\n", $2, maxcost); + $$ = maxcost; + } else + $$ = $2; } + ; +%% +#include +#include + +int errcnt = 0; +FILE *infp = NULL; +FILE *outfp = NULL; +static char buf[BUFSIZ], *bp = buf; +static int ppercent = 0; + +static int get(void) { + if (*bp == 0) { + bp = buf; + *bp = 0; + if (fgets(buf, sizeof buf, infp) == NULL) + return EOF; + yylineno++; + while (buf[0] == '%' && buf[1] == '{' && (buf[2] == '\n' || buf[2] == '\r')) { + for (;;) { + if (fgets(buf, sizeof buf, infp) == NULL) { + yywarn("unterminated %{...%}\n"); + return EOF; + } + yylineno++; + if (strcmp(buf, "%}\n") == 0 || strcmp(buf, "%}\r\n") == 0) + break; + fputs(buf, outfp); + } + if (fgets(buf, sizeof buf, infp) == NULL) + return EOF; + yylineno++; + } + } + return *bp++; +} + +void yyerror(char *fmt, ...) { + va_list ap; + + va_start(ap, fmt); + if (yylineno > 0) + fprintf(stderr, "line %d: ", yylineno); + vfprintf(stderr, fmt, ap); + if (fmt[strlen(fmt)-1] != '\n') + fprintf(stderr, "\n"); + errcnt++; +} + +int yylex(void) { + int c; + + while ((c = get()) != EOF) { + switch (c) { + case ' ': case '\f': case '\t': case '\r': + continue; + case '\n': + case '(': case ')': case ',': + case ';': case '=': case ':': + return c; + } + if (c == '%' && *bp == '%') { + bp++; + return ppercent++ ? 0 : PPERCENT; + } else if (c == '%' && strncmp(bp, "term", 4) == 0 + && isspace(bp[4])) { + bp += 4; + return TERMINAL; + } else if (c == '%' && strncmp(bp, "start", 5) == 0 + && isspace(bp[5])) { + bp += 5; + return START; + } else if (isdigit(c)) { + int n = 0; + do { + int d = c - '0'; + if (n > (INT_MAX - d)/10) + yyerror("integer greater than %d\n", INT_MAX); + else + n = 10*n + d; + c = get(); + } while (c != EOF && isdigit(c)); + bp--; + yylval.n = n; + return INT; + } else if (isalpha(c)) { + char *p = bp - 1; + while (isalpha(*bp) || isdigit(*bp) || *bp == '_') + bp++; + yylval.string = alloc(bp - p + 1); + strncpy(yylval.string, p, bp - p); + yylval.string[bp - p] = 0; + return ID; + } else if (isprint(c)) + yyerror("invalid character `%c'\n", c); + else + yyerror("invalid character `\\%03o'\n", (unsigned char)c); + } + return 0; +} + +void yywarn(char *fmt, ...) { + va_list ap; + + va_start(ap, fmt); + if (yylineno > 0) + fprintf(stderr, "line %d: ", yylineno); + fprintf(stderr, "warning: "); + vfprintf(stderr, fmt, ap); +} diff --git a/util/mcgg/iburg.1 b/util/mcgg/iburg.1 new file mode 100644 index 000000000..582143bdb --- /dev/null +++ b/util/mcgg/iburg.1 @@ -0,0 +1,285 @@ +.TH IBURG 1 "local \- 1/26/93" +.\" $Id$ +.SH NAME +iburg \- code generator generator +.SH SYNOPSIS +.B iburg +[ +.I option +]... +[ [ +.I input +] +.I output +] +.br +.SH DESCRIPTION +.PP +.I iburg +reads BURG specification from +.I input +and writes a pattern-matching code generator to +.IR output . +If +.I input +is `\-' or is omitted, +.I iburg +reads the standard input; +If +.I output +is `\-' or is omitted, +.I iburg +writes to the standard output. +.PP +.I iburg +accepts BURG specifications that conform to the following EBNF grammar. +Terminals are enclosed in single quotes, all other symbols are nonterminals, +{X} denotes zero or more instances of X, and [X] denotes an optional X. +.PP +.nf +.RS +.ft CW +spec: { dcl } `%%' { rule } [ `%%' ] + +dcl: `%start' nonterm + `%term' { identifier `=' integer } + +rule: nonterm `:' tree `=' integer [ cost ] `;' + +cost: `(' integer ')' + +tree: term `(' tree `,' tree `)' + term `(' tree `)' + term + nonterm +.RE +.fi +.PP +Specifications are structurally similar to +.IR yacc 's. +Text between +`\f(CW%{\fP' +and +`\f(CW%}\fP' +is called the configuration section; there may be several such segments. +All are concatenated and copied verbatim into the head of the generated +parser, which is called burm. +Text after the second +`\f(CW%%\fP', +if any, is also copied verbatim into +.IR burm , +at the end. +.PP +Specifications consist of declarations, a +`\f(CW%%\fP' +separator, and rules. +Input is line-oriented; each declaration and rule must appear on a separate line, +and declarations must begin in column 1. +Declarations declare terminals \(em the operators in subject +trees \(em and associate a unique, positive external symbol +number with each one. +Non-terminals are declared by their presence +on the left side of rules. The +\f(CW%start\fP +declaration optionally declares a non-terminal as the start symbol. +In the grammar above, +\f(CWterm\fP +and +\f(CWnonterm\fP +denote identifiers that are terminals and non-terminals, respectively. +.PP +Rules define tree patterns in a fully parenthesized prefix +form. Every non-terminal denotes a tree. +Each operator has a fixed +arity, which is inferred from the rules in which it is used. +A chain rule is a rule whose pattern is another non-terminal. +If no start symbol is declared, the non-terminal defined by the first rule is used. +.PP +Each rule has a unique, positive external rule number, which +comes after the pattern and is preceded by a +`\f(CW=\fP'. +External rule numbers are used to report the +matching rule to a user-supplied semantic action routine. +Rules end with an optional non-negative, integer cost; omitted costs +default to zero. +.PP +The display below shows a fragment of a BURG specification. +This example uses upper-case for terminals and lower-case for non-terminals. +.PP +.nf +.ft CW +%{ +enum { ADDI=309, ADDRLP=295, ASGNI=53, + CNSTI=21, CVCI=85, I0I=661, INDIRC=67 }; + +typedef struct tree { + int op; + struct tree *kids[2]; + int val; + struct { int state; } x; +} *NODEPTR_TYPE, *Tree; +#define LEFT_CHILD(p) ((p)->kids[0]) +#define RIGHT_CHILD(p) ((p)->kids[1]) +#define PANIC printf +#define STATE_LABEL(p) ((p)->x.state) + +int OP_LABEL(NODEPTR_TYPE p) { + switch (p->op) { + case CNSTI: if (p->val == 0) return 661 /* I0I */; + default: return p->op; + } +} +%} +%term ADDI=309 ADDRLP=295 ASGNI=53 +%term CNSTI=21 CVCI=85 I0I=661 INDIRC=67 +%% +stmt: ASGNI(disp,reg) = 4 (1); +stmt: reg = 5; +reg: ADDI(reg,rc) = 6 (1); +reg: CVCI(INDIRC(disp)) = 7 (1); +reg: I0I = 8; +reg: disp = 9 (1); +disp: ADDI(reg,con) = 10; +disp: ADDRLP = 11; +rc: con = 12; +rc: reg = 13; +con: CNSTI = 14; +con: I0I = 15; +%% +.fi +.PP +The configuration section configures +\f(CWburm\fP +for the trees being parsed and the client's environment. +As shown, this section must define +\f(CWNODEPTR_TYPE\fP +to be a visible typedef symbol for a pointer to a +node in the subject tree. +\f(CWburm\fP +invokes +\f(CWOP_LABEL(p)\fP, +\f(CWLEFT\_CHILD(p)\fP, and +\f(CWRIGHT\_CHILD(p)\fP +to read the operator and children from the node pointed to by \f(CWp\fP. +It invokes +\f(CWPANIC\fP +when it detects an error. +If the configuration section defines these operations as macros, they are implemented in-line; +otherwise, they must be implemented as functions. +.PP +By default, +\f(CWburm\fP +computes and stores a single integral state in each node of the subject tree. +The configuration section must define a macro +\f(CWSTATE_LABEL(p)\fP +to access the state field of the node pointed to +by \f(CWp\fP. It must be large enough to hold a pointer, and +a macro is required because it is used as an lvalue. +The configuration section may define the macro +\f(CWSTATE_TYPE\fP +to specify a different type for state values; if +\f(CWSTATE_TYPE\fP +is not defined, int is used. +.PP +The configuration section may also define +\f(CWALLOC(n)\fP +for allocating +\f(CWn\fP +bytes. +If +\f(CWALLOC\fP +is not defined, +.IR malloc (3) +is used. +.SH OPTIONS +.TP +.BI \-p \ prefix +.br +.ns +.TP +.BI \-p prefix +Use +.I prefix +as the disambiquating prefix for exported names and visible fields. +The default is `\f(CWburm\fP'. +.TP +.BI \-maxcost= ddd +Use the integral value +.I ddd +as the maximum cost. +The default is \f(CWSHRT_MAX\fR +when ints are bigger than shorts, and +\f(CWSHRT_MAX\fP/2 when ints and shorts are the same size. +.TP +.B \-I +Emit code for the following data values and functions. +.sp +.nf +.ft CW + char burm_arity[]; + char *burm_opname[]; + char *burm_ntname[]; + char *burm_string[]; + short burm_cost[][4]; + int burm_op_label(NODEPTR_TYPE p); + NODEPTR_TYPE burm_child(NODEPTR_TYPE p, int index); + STATE_TYPE burm_state_label(NODEPTR_TYPE p); +.sp +.fi +.ft R +\f(CWburm_arity\fP and +\f(CWburm_opname\fP +are indexed by external symbol numbers and gives their arities. +\f(CWburm_ntname\fP +is indexed by a non-terminal number and gives its string name. +\f(CWburm_string\fP +and +\f(CWburm_cost\fP +are indexed by an external rule number and give the string +representation and cost of each rule. +The functions encapsulate the similarly named macros. +.TP +.B \-T +Arrange for +.sp +.nf +.ft CW + void burm_trace(NODEPTR_TYPE p, int eruleno, + int cost, int bestcost); +.sp +.fi +.ft R +to be called at each successful match. +\f(CWp\fP +identifies the node and +\f(CWeruleno\fP +identifies the matching rule; +\f(CWeruleno\fP +is an index into \f(CWburm_string\fP. +\f(CWcost\fP +is the cost of the match and +\f(CWbestcost\fP +is the cost of the best previous match. The current match +wins only if +\f(CWcost\fP +is less than \f(CWbestcost\fP. +SHRT_MAX represents the infinite cost of no previous match. +\f(CWburm_trace\fP must be declared in the configuration section. +.SH "SEE ALSO" +C. W. Fraser, R. R. Henry and T. A. Proebsting, +`BURG \(em Fast optimal instruction selection and tree parsing,' +.I +SIGPLAN Notices +.BR 27 , +4 (Apr. 1992), 68-76. +.PP +C. W. Fraser, D. R. Hanson and T. A. Proebsting, +`Engineering a simple, efficient code generator generator,' +.I +ACM Letters on Programming Languages and Systems +.BR 1 , +3 (Sep. 1992), 213-226. +.br +.SH BUGS +Mail bug reports along with the shortest input +that exposes them to drh@drhanson.net. diff --git a/util/mcgg/iburg.c b/util/mcgg/iburg.c new file mode 100644 index 000000000..18e80ad60 --- /dev/null +++ b/util/mcgg/iburg.c @@ -0,0 +1,746 @@ +#include +#include +#include +#include +#include +#include +#include +#include "iburg.h" + +static char rcsid[] = "$Id$"; + +int maxcost = SHRT_MAX; + +static char *prefix = "burm"; +static int Iflag = 0, Tflag = 0; +static int ntnumber = 0; +static Nonterm start = 0; +static Term terms; +static Nonterm nts; +static Rule rules; +static int nrules; + +static char *stringf(char *fmt, ...); +static void print(char *fmt, ...); +static void ckreach(Nonterm p); +static void emitclosure(Nonterm nts); +static void emitcost(Tree t, char *v); +static void emitdefs(Nonterm nts, int ntnumber); +static void emitfuncs(void); +static void emitheader(void); +static void emitkids(Rule rules, int nrules); +static void emitlabel(Nonterm start); +static void emitleaf(Term p, int ntnumber); +static void emitnts(Rule rules, int nrules); +static void emitrecord(char *pre, Rule r, int cost); +static void emitrule(Nonterm nts); +static void emitstate(Term terms, Nonterm start, int ntnumber); +static void emitstring(Rule rules); +static void emitstruct(Nonterm nts, int ntnumber); +static void emitterms(Term terms); +static void emittest(Tree t, char *v, char *suffix); + +int main(int argc, char *argv[]) { + int c, i; + Nonterm p; + + if (sizeof (short) == sizeof (int)) + maxcost = SHRT_MAX/2; + for (i = 1; i < argc; i++) + if (strcmp(argv[i], "-I") == 0) + Iflag = 1; + else if (strcmp(argv[i], "-T") == 0) + Tflag = 1; + else if (strncmp(argv[i], "-maxcost=", 9) == 0 && isdigit(argv[i][9])) + maxcost = atoi(argv[i] + 9); + else if (strncmp(argv[i], "-p", 2) == 0 && argv[i][2]) + prefix = &argv[i][2]; + else if (strncmp(argv[i], "-p", 2) == 0 && i + 1 < argc) + prefix = argv[++i]; + else if (*argv[i] == '-' && argv[i][1]) { + yyerror("usage: %s [-T | -I | -p prefix | -maxcost=ddd ]... [ [ input ] output \n", + argv[0]); + exit(1); + } else if (infp == NULL) { + if (strcmp(argv[i], "-") == 0) + infp = stdin; + else if ((infp = fopen(argv[i], "r")) == NULL) { + yyerror("%s: can't read `%s'\n", argv[0], argv[i]); + exit(1); + } + } else if (outfp == NULL) { + if (strcmp(argv[i], "-") == 0) + outfp = stdout; + if ((outfp = fopen(argv[i], "w")) == NULL) { + yyerror("%s: can't write `%s'\n", argv[0], argv[i]); + exit(1); + } + } + if (infp == NULL) + infp = stdin; + if (outfp == NULL) + outfp = stdout; + yyparse(); + if (start) + ckreach(start); + for (p = nts; p; p = p->link) + if (!p->reached) + yyerror("can't reach non-terminal `%s'\n", p->name); + emitheader(); + emitdefs(nts, ntnumber); + emitstruct(nts, ntnumber); + emitnts(rules, nrules); + emitterms(terms); + if (Iflag) + emitstring(rules); + emitrule(nts); + emitclosure(nts); + if (start) + emitstate(terms, start, ntnumber); + print("#ifdef STATE_LABEL\n"); + if (start) + emitlabel(start); + emitkids(rules, nrules); + emitfuncs(); + print("#endif\n"); + if (!feof(infp)) + while ((c = getc(infp)) != EOF) + putc(c, outfp); + return errcnt > 0; +} + +/* alloc - allocate nbytes or issue fatal error */ +void *alloc(int nbytes) { + void *p = calloc(1, nbytes); + + if (p == NULL) { + yyerror("out of memory\n"); + exit(1); + } + return p; +} + +/* stringf - format and save a string */ +static char *stringf(char *fmt, ...) { + va_list ap; + char *s, buf[512]; + + va_start(ap, fmt); + vsprintf(buf, fmt, ap); + va_end(ap); + return strcpy(alloc(strlen(buf) + 1), buf); +} + +struct entry { + union { + char *name; + struct term t; + struct nonterm nt; + } sym; + struct entry *link; +} *table[211]; +#define HASHSIZE (sizeof table/sizeof table[0]) + +/* hash - return hash number for str */ +static unsigned hash(char *str) { + unsigned h = 0; + + while (*str) + h = (h<<1) + *str++; + return h; +} + +/* lookup - lookup symbol name */ +static void *lookup(char *name) { + struct entry *p = table[hash(name)%HASHSIZE]; + + for ( ; p; p = p->link) + if (strcmp(name, p->sym.name) == 0) + return &p->sym; + return 0; +} + +/* install - install symbol name */ +static void *install(char *name) { + struct entry *p = alloc(sizeof *p); + int i = hash(name)%HASHSIZE; + + p->sym.name = name; + p->link = table[i]; + table[i] = p; + return &p->sym; +} + +/* nonterm - create a new terminal id, if necessary */ +Nonterm nonterm(char *id) { + Nonterm p = lookup(id), *q = &nts; + + if (p && p->kind == NONTERM) + return p; + if (p && p->kind == TERM) + yyerror("`%s' is a terminal\n", id); + p = install(id); + p->kind = NONTERM; + p->number = ++ntnumber; + if (p->number == 1) + start = p; + while (*q && (*q)->number < p->number) + q = &(*q)->link; + assert(*q == 0 || (*q)->number != p->number); + p->link = *q; + *q = p; + return p; +} + +/* term - create a new terminal id with external symbol number esn */ +Term term(char *id, int esn) { + Term p = lookup(id), *q = &terms; + + if (p) + yyerror("redefinition of terminal `%s'\n", id); + else + p = install(id); + p->kind = TERM; + p->esn = esn; + p->arity = -1; + while (*q && (*q)->esn < p->esn) + q = &(*q)->link; + if (*q && (*q)->esn == p->esn) + yyerror("duplicate external symbol number `%s=%d'\n", + p->name, p->esn); + p->link = *q; + *q = p; + return p; +} + +/* tree - create & initialize a tree node with the given fields */ +Tree tree(char *id, Tree left, Tree right) { + Tree t = alloc(sizeof *t); + Term p = lookup(id); + int arity = 0; + + if (left && right) + arity = 2; + else if (left) + arity = 1; + if (p == NULL && arity > 0) { + yyerror("undefined terminal `%s'\n", id); + p = term(id, -1); + } else if (p == NULL && arity == 0) + p = (Term)nonterm(id); + else if (p && p->kind == NONTERM && arity > 0) { + yyerror("`%s' is a non-terminal\n", id); + p = term(id, -1); + } + if (p->kind == TERM && p->arity == -1) + p->arity = arity; + if (p->kind == TERM && arity != p->arity) + yyerror("inconsistent arity for terminal `%s'\n", id); + t->op = p; + t->nterms = p->kind == TERM; + if (t->left = left) + t->nterms += left->nterms; + if (t->right = right) + t->nterms += right->nterms; + return t; +} + +/* rule - create & initialize a rule with the given fields */ +Rule rule(char *id, Tree pattern, int ern, int cost) { + Rule r = alloc(sizeof *r), *q; + Term p = pattern->op; + + nrules++; + r->lhs = nonterm(id); + r->packed = ++r->lhs->lhscount; + for (q = &r->lhs->rules; *q; q = &(*q)->decode) + ; + *q = r; + r->pattern = pattern; + r->ern = ern; + r->cost = cost; + if (p->kind == TERM) { + r->next = p->rules; + p->rules = r; + } else if (pattern->left == NULL && pattern->right == NULL) { + Nonterm p = pattern->op; + r->chain = p->chain; + p->chain = r; + } + for (q = &rules; *q && (*q)->ern < r->ern; q = &(*q)->link) + ; + if (*q && (*q)->ern == r->ern) + yyerror("duplicate external rule number `%d'\n", r->ern); + r->link = *q; + *q = r; + return r; +} + +/* print - formatted output */ +static void print(char *fmt, ...) { + va_list ap; + + va_start(ap, fmt); + for ( ; *fmt; fmt++) + if (*fmt == '%') + switch (*++fmt) { + case 'd': fprintf(outfp, "%d", va_arg(ap, int)); break; + case 's': fputs(va_arg(ap, char *), outfp); break; + case 'P': fprintf(outfp, "%s_", prefix); break; + case 'T': { + Tree t = va_arg(ap, Tree); + print("%S", t->op); + if (t->left && t->right) + print("(%T,%T)", t->left, t->right); + else if (t->left) + print("(%T)", t->left); + break; + } + case 'R': { + Rule r = va_arg(ap, Rule); + print("%S: %T", r->lhs, r->pattern); + break; + } + case 'S': fputs(va_arg(ap, Term)->name, outfp); break; + case '1': case '2': case '3': case '4': case '5': { + int n = *fmt - '0'; + while (n-- > 0) + putc('\t', outfp); + break; + } + default: putc(*fmt, outfp); break; + } + else + putc(*fmt, outfp); + va_end(ap); +} + +/* reach - mark all non-terminals in tree t as reachable */ +static void reach(Tree t) { + Nonterm p = t->op; + + if (p->kind == NONTERM) + if (!p->reached) + ckreach(p); + if (t->left) + reach(t->left); + if (t->right) + reach(t->right); +} + +/* ckreach - mark all non-terminals reachable from p */ +static void ckreach(Nonterm p) { + Rule r; + + p->reached = 1; + for (r = p->rules; r; r = r->decode) + reach(r->pattern); +} + +/* emitcase - emit one case in function state */ +static void emitcase(Term p, int ntnumber) { + Rule r; + + print("%1case %d: /* %S */\n", p->esn, p); + switch (p->arity) { + case 0: case -1: + if (!Tflag) { + emitleaf(p, ntnumber); + return; + } + break; + case 1: print("%2assert(l);\n"); break; + case 2: print("%2assert(l && r);\n"); break; + default: assert(0); + } + for (r = p->rules; r; r = r->next) { + switch (p->arity) { + case 0: case -1: + print("%2{%1/* %R */\n%3c = ", r); + break; + case 1: + if (r->pattern->nterms > 1) { + print("%2if (%1/* %R */\n", r); + emittest(r->pattern->left, "l", " "); + print("%2) {\n%3c = "); + } else + print("%2{%1/* %R */\n%3c = ", r); + emitcost(r->pattern->left, "l"); + break; + case 2: + if (r->pattern->nterms > 1) { + print("%2if (%1/* %R */\n", r); + emittest(r->pattern->left, "l", + r->pattern->right->nterms ? " && " : " "); + emittest(r->pattern->right, "r", " "); + print("%2) {\n%3c = "); + } else + print("%2{%1/* %R */\n%3c = ", r); + emitcost(r->pattern->left, "l"); + emitcost(r->pattern->right, "r"); + break; + default: assert(0); + } + print("%d;\n", r->cost); + emitrecord("\t\t\t", r, 0); + print("%2}\n"); + } + print("%2break;\n"); +} + +/* emitclosure - emit the closure functions */ +static void emitclosure(Nonterm nts) { + Nonterm p; + + for (p = nts; p; p = p->link) + if (p->chain) + print("static void %Pclosure_%S(struct %Pstate *, int);\n", p); + print("\n"); + for (p = nts; p; p = p->link) + if (p->chain) { + Rule r; + print("static void %Pclosure_%S(struct %Pstate *p, int c) {\n", p); + for (r = p->chain; r; r = r->chain) + emitrecord("\t", r, r->cost); + print("}\n\n"); + } +} + +/* emitcost - emit cost computation for tree t */ +static void emitcost(Tree t, char *v) { + Nonterm p = t->op; + + if (p->kind == TERM) { + if (t->left) + emitcost(t->left, stringf("%s->left", v)); + if (t->right) + emitcost(t->right, stringf("%s->right", v)); + } else + print("%s->cost[%P%S_NT] + ", v, p); +} + +/* emitdefs - emit non-terminal defines and data structures */ +static void emitdefs(Nonterm nts, int ntnumber) { + Nonterm p; + + for (p = nts; p; p = p->link) + print("#define %P%S_NT %d\n", p, p->number); + print("int %Pmax_nt = %d;\n\n", ntnumber); + if (Iflag) { + print("char *%Pntname[] = {\n%10,\n"); + for (p = nts; p; p = p->link) + print("%1\"%S\",\n", p); + print("%10\n};\n\n"); + } +} + +/* emitfuncs - emit functions to access node fields */ +static void emitfuncs(void) { + print("int %Pop_label(NODEPTR_TYPE p) {\n" +"%1%Passert(p, PANIC(\"NULL tree in %Pop_label\\n\"));\n" +"%1return OP_LABEL(p);\n}\n\n"); + print("STATE_TYPE %Pstate_label(NODEPTR_TYPE p) {\n" +"%1%Passert(p, PANIC(\"NULL tree in %Pstate_label\\n\"));\n" +"%1return STATE_LABEL(p);\n}\n\n"); + print("NODEPTR_TYPE %Pchild(NODEPTR_TYPE p, int index) {\n" +"%1%Passert(p, PANIC(\"NULL tree in %Pchild\\n\"));\n" +"%1switch (index) {\n%1case 0:%1return LEFT_CHILD(p);\n" +"%1case 1:%1return RIGHT_CHILD(p);\n%1}\n" +"%1%Passert(0, PANIC(\"Bad index %%d in %Pchild\\n\", index));\n%1return 0;\n}\n\n"); +} + +/* emitheader - emit initial definitions */ +static void emitheader(void) { + print("#include \n#include \n"); + print("#ifndef STATE_TYPE\n#define STATE_TYPE int\n#endif\n"); + print("#ifndef ALLOC\n#define ALLOC(n) malloc(n)\n#endif\n" +"#ifndef %Passert\n#define %Passert(x,y) if (!(x)) { y; abort(); }\n#endif\n\n"); + if (Tflag) + print("static NODEPTR_TYPE %Pnp;\n\n"); +} + +/* computekids - compute paths to kids in tree t */ +static char *computekids(Tree t, char *v, char *bp, int *ip) { + Term p = t->op; + + if (p->kind == NONTERM) { + sprintf(bp, "\t\tkids[%d] = %s;\n", (*ip)++, v); + bp += strlen(bp); + } else if (p->arity > 0) { + bp = computekids(t->left, stringf("LEFT_CHILD(%s)", v), bp, ip); + if (p->arity == 2) + bp = computekids(t->right, stringf("RIGHT_CHILD(%s)", v), bp, ip); + } + return bp; +} + +/* emitkids - emit burm_kids */ +static void emitkids(Rule rules, int nrules) { + int i; + Rule r, *rc = alloc((nrules + 1)*sizeof *rc); + char **str = alloc((nrules + 1)*sizeof *str); + + for (i = 0, r = rules; r; r = r->link) { + int j = 0; + char buf[1024], *bp = buf; + *computekids(r->pattern, "p", bp, &j) = 0; + for (j = 0; str[j] && strcmp(str[j], buf); j++) + ; + if (str[j] == NULL) + str[j] = strcpy(alloc(strlen(buf) + 1), buf); + r->kids = rc[j]; + rc[j] = r; + } + print("NODEPTR_TYPE *%Pkids(NODEPTR_TYPE p, int eruleno, NODEPTR_TYPE kids[]) {\n" +"%1%Passert(p, PANIC(\"NULL tree in %Pkids\\n\"));\n" +"%1%Passert(kids, PANIC(\"NULL kids in %Pkids\\n\"));\n" +"%1switch (eruleno) {\n"); + for (i = 0; r = rc[i]; i++) { + for ( ; r; r = r->kids) + print("%1case %d: /* %R */\n", r->ern, r); + print("%s%2break;\n", str[i]); + } + print("%1default:\n%2%Passert(0, PANIC(\"Bad external rule number %%d in %Pkids\\n\", eruleno));\n%1}\n%1return kids;\n}\n\n"); +} + +/* emitlabel - emit the labelling functions */ +static void emitlabel(Nonterm start) { + print("static void %Plabel1(NODEPTR_TYPE p) {\n" +"%1%Passert(p, PANIC(\"NULL tree in %Plabel\\n\"));\n" +"%1switch (%Parity[OP_LABEL(p)]) {\n" +"%1case 0:\n"); + if (Tflag) + print("%2%Pnp = p;\n"); + print("%2STATE_LABEL(p) = %Pstate(OP_LABEL(p), 0, 0);\n%2break;\n" +"%1case 1:\n%2%Plabel1(LEFT_CHILD(p));\n"); + if (Tflag) + print("%2%Pnp = p;\n"); + print("%2STATE_LABEL(p) = %Pstate(OP_LABEL(p),\n" +"%3STATE_LABEL(LEFT_CHILD(p)), 0);\n%2break;\n" +"%1case 2:\n%2%Plabel1(LEFT_CHILD(p));\n%2%Plabel1(RIGHT_CHILD(p));\n"); + if (Tflag) + print("%2%Pnp = p;\n"); + print("%2STATE_LABEL(p) = %Pstate(OP_LABEL(p),\n" +"%3STATE_LABEL(LEFT_CHILD(p)),\n%3STATE_LABEL(RIGHT_CHILD(p)));\n%2break;\n" +"%1}\n}\n\n"); + print( +"STATE_TYPE %Plabel(NODEPTR_TYPE p) {\n%1%Plabel1(p);\n" +"%1return ((struct %Pstate *)STATE_LABEL(p))->rule.%P%S ? STATE_LABEL(p) : 0;\n" +"}\n\n", start); +} + +/* closure - fill in cost & rule with results of chain rules w/p as rhs */ +static void closure(int cost[], Rule rule[], Nonterm p, int c) { + Rule r; + + for (r = p->chain; r; r = r->chain) + if (c + r->cost < cost[r->lhs->number]) { + cost[r->lhs->number] = c + r->cost; + rule[r->lhs->number] = r; + closure(cost, rule, r->lhs, c + r->cost); + } +} + +/* emitleaf - emit state code for a leaf */ +static void emitleaf(Term p, int ntnumber) { + int i; + Rule r; + static int *cost; + static Rule *rule; + + if (cost == NULL) { + cost = alloc((ntnumber + 1)*sizeof *cost); + rule = alloc((ntnumber + 1)*sizeof *rule); + } + for (i = 0; i <= ntnumber; i++) { + cost[i] = maxcost; + rule[i] = NULL; + } + for (r = p->rules; r; r = r->next) + if (r->pattern->left == NULL && r->pattern->right == NULL) { + cost[r->lhs->number] = r->cost; + rule[r->lhs->number] = r; + closure(cost, rule, r->lhs, r->cost); + } + print("%2{\n%3static struct %Pstate z = { %d, 0, 0,\n%4{%10,\n", p->esn); + for (i = 1; i <= ntnumber; i++) + if (cost[i] < maxcost) + print("%5%d,%1/* %R */\n", cost[i], rule[i]); + else + print("%5%d,\n", cost[i]); + print("%4},{\n"); + for (i = 1; i <= ntnumber; i++) + if (rule[i]) + print("%5%d,%1/* %R */\n", rule[i]->packed, rule[i]); + else + print("%50,\n"); + print("%4}\n%3};\n%3return (STATE_TYPE)&z;\n%2}\n"); +} + +/* computents - fill in bp with burm_nts vector for tree t */ +static char *computents(Tree t, char *bp) { + if (t) { + Nonterm p = t->op; + if (p->kind == NONTERM) { + sprintf(bp, "%s_%s_NT, ", prefix, p->name); + bp += strlen(bp); + } else + bp = computents(t->right, computents(t->left, bp)); + } + return bp; +} + +/* emitnts - emit burm_nts ragged array */ +static void emitnts(Rule rules, int nrules) { + Rule r; + int i, j, *nts = alloc(nrules*sizeof *nts); + char **str = alloc(nrules*sizeof *str); + + for (i = 0, r = rules; r; r = r->link) { + char buf[1024]; + *computents(r->pattern, buf) = 0; + for (j = 0; str[j] && strcmp(str[j], buf); j++) + ; + if (str[j] == NULL) { + print("static short %Pnts_%d[] = { %s0 };\n", j, buf); + str[j] = strcpy(alloc(strlen(buf) + 1), buf); + } + nts[i++] = j; + } + print("\nshort *%Pnts[] = {\n"); + for (i = j = 0, r = rules; r; r = r->link) { + for ( ; j < r->ern; j++) + print("%10,%1/* %d */\n", j); + print("%1%Pnts_%d,%1/* %d */\n", nts[i++], j++); + } + print("};\n\n"); +} + +/* emitrecord - emit code that tests for a winning match of rule r */ +static void emitrecord(char *pre, Rule r, int cost) { + print("%sif (", pre); + if (Tflag) + print("%Ptrace(%Pnp, %d, c + %d, p->cost[%P%S_NT]), ", + r->ern, cost, r->lhs); + print("c + %d < p->cost[%P%S_NT]) {\n" +"%s%1p->cost[%P%S_NT] = c + %d;\n%s%1p->rule.%P%S = %d;\n", + cost, r->lhs, pre, r->lhs, cost, pre, r->lhs, + r->packed); + if (r->lhs->chain) + print("%s%1%Pclosure_%S(p, c + %d);\n", pre, r->lhs, cost); + print("%s}\n", pre); +} + +/* emitrule - emit decoding vectors and burm_rule */ +static void emitrule(Nonterm nts) { + Nonterm p; + + for (p = nts; p; p = p->link) { + Rule r; + print("static short %Pdecode_%S[] = {\n%10,\n", p); + for (r = p->rules; r; r = r->decode) + print("%1%d,\n", r->ern); + print("};\n\n"); + } + print("int %Prule(STATE_TYPE state, int goalnt) {\n" +"%1%Passert(goalnt >= 1 && goalnt <= %d, PANIC(\"Bad goal nonterminal %%d in %Prule\\n\", goalnt));\n" +"%1if (!state)\n%2return 0;\n%1switch (goalnt) {\n", ntnumber); + for (p = nts; p; p = p->link) + print("%1case %P%S_NT:" +"%1return %Pdecode_%S[((struct %Pstate *)state)->rule.%P%S];\n", p, p, p); + print("%1default:\n%2%Passert(0, PANIC(\"Bad goal nonterminal %%d in %Prule\\n\", goalnt));\n%1}\n%1return 0;\n}\n\n"); +} + +/* emitstate - emit state function */ +static void emitstate(Term terms, Nonterm start, int ntnumber) { + int i; + Term p; + + print("STATE_TYPE %Pstate(int op, STATE_TYPE left, STATE_TYPE right) {\n%1int c;\n" +"%1struct %Pstate *p, *l = (struct %Pstate *)left,\n" +"%2*r = (struct %Pstate *)right;\n\n%1assert(sizeof (STATE_TYPE) >= sizeof (void *));\n%1"); + if (!Tflag) + print("if (%Parity[op] > 0) "); + print("{\n%2p = ALLOC(sizeof *p);\n" +"%2%Passert(p, PANIC(\"ALLOC returned NULL in %Pstate\\n\"));\n" +"%2p->op = op;\n%2p->left = l;\n%2p->right = r;\n%2p->rule.%P%S = 0;\n", start); + for (i = 1; i <= ntnumber; i++) + print("%2p->cost[%d] =\n", i); + print("%3%d;\n%1}\n%1switch (op) {\n", maxcost); + for (p = terms; p; p = p->link) + emitcase(p, ntnumber); + print("%1default:\n" +"%2%Passert(0, PANIC(\"Bad operator %%d in %Pstate\\n\", op));\n%1}\n" +"%1return (STATE_TYPE)p;\n}\n\n"); +} + +/* emitstring - emit array of rules and costs */ +static void emitstring(Rule rules) { + Rule r; + int k; + + print("short %Pcost[][4] = {\n"); + for (k = 0, r = rules; r; r = r->link) { + for ( ; k < r->ern; k++) + print("%1{ 0 },%1/* %d */\n", k); + print("%1{ %d },%1/* %d = %R */\n", r->cost, k++, r); + } + print("};\n\nchar *%Pstring[] = {\n"); + for (k = 0, r = rules; r; r = r->link) { + for ( ; k < r->ern; k++) + print("%1/* %d */%10,\n", k); + print("%1/* %d */%1\"%R\",\n", k++, r); + } + print("};\n\n"); +} + +/* emitstruct - emit the definition of the state structure */ +static void emitstruct(Nonterm nts, int ntnumber) { + print("struct %Pstate {\n%1int op;\n%1struct %Pstate *left, *right;\n" +"%1short cost[%d];\n%1struct {\n", ntnumber + 1); + for ( ; nts; nts = nts->link) { + int n = 1, m = nts->lhscount; + while (m >>= 1) + n++; + print("%2unsigned %P%S:%d;\n", nts, n); + } + print("%1} rule;\n};\n\n"); +} + +/* emitterms - emit terminal data structures */ +static void emitterms(Term terms) { + Term p; + int k; + + print("char %Parity[] = {\n"); + for (k = 0, p = terms; p; p = p->link) { + for ( ; k < p->esn; k++) + print("%10,%1/* %d */\n", k); + print("%1%d,%1/* %d=%S */\n", p->arity < 0 ? 0 : p->arity, k++, p); + } + print("};\n\n"); + if (Iflag) { + print("char *%Popname[] = {\n"); + for (k = 0, p = terms; p; p = p->link) { + for ( ; k < p->esn; k++) + print("%1/* %d */%10,\n", k); + print("%1/* %d */%1\"%S\",\n", k++, p); + } + print("};\n\n"); + } +} + +/* emittest - emit clause for testing a match */ +static void emittest(Tree t, char *v, char *suffix) { + Term p = t->op; + + if (p->kind == TERM) { + print("%3%s->op == %d%s/* %S */\n", v, p->esn, + t->nterms > 1 ? " && " : suffix, p); + if (t->left) + emittest(t->left, stringf("%s->left", v), + t->right && t->right->nterms ? " && " : suffix); + if (t->right) + emittest(t->right, stringf("%s->right", v), suffix); + } +} diff --git a/util/mcgg/iburg.h b/util/mcgg/iburg.h new file mode 100644 index 000000000..e55d5a8f9 --- /dev/null +++ b/util/mcgg/iburg.h @@ -0,0 +1,65 @@ +#ifndef BURG_INCLUDED +#define BURG_INCLUDED + +/* $Id$ */ +/* iburg.c: */ +extern void *alloc(int nbytes); + +typedef enum { TERM=1, NONTERM } Kind; +typedef struct rule *Rule; +typedef struct term *Term; +struct term { /* terminals: */ + char *name; /* terminal name */ + Kind kind; /* TERM */ + int esn; /* external symbol number */ + int arity; /* operator arity */ + Term link; /* next terminal in esn order */ + Rule rules; /* rules whose pattern starts with term */ +}; + +typedef struct nonterm *Nonterm; +struct nonterm { /* non-terminals: */ + char *name; /* non-terminal name */ + Kind kind; /* NONTERM */ + int number; /* identifying number */ + int lhscount; /* # times nt appears in a rule lhs */ + int reached; /* 1 iff reached from start non-terminal */ + Rule rules; /* rules w/non-terminal on lhs */ + Rule chain; /* chain rules w/non-terminal on rhs */ + Nonterm link; /* next terminal in number order */ +}; +extern Nonterm nonterm(char *id); +extern Term term(char *id, int esn); + +typedef struct tree *Tree; +struct tree { /* tree patterns: */ + void *op; /* a terminal or non-terminal */ + Tree left, right; /* operands */ + int nterms; /* number of terminal nodes in this tree */ +}; +extern Tree tree(char *op, Tree left, Tree right); + +struct rule { /* rules: */ + Nonterm lhs; /* lefthand side non-terminal */ + Tree pattern; /* rule pattern */ + int ern; /* external rule number */ + int packed; /* packed external rule number */ + int cost; /* associated cost */ + Rule link; /* next rule in ern order */ + Rule next; /* next rule with same pattern root */ + Rule chain; /* next chain rule with same rhs */ + Rule decode; /* next rule with same lhs */ + Rule kids; /* next rule with same burm_kids pattern */ +}; +extern Rule rule(char *id, Tree pattern, int ern, int cost); +extern int maxcost; /* maximum cost */ + +/* gram.y: */ +void yyerror(char *fmt, ...); +int yyparse(void); +void yywarn(char *fmt, ...); +extern int errcnt; +extern FILE *infp; +extern FILE *outfp; + +#endif diff --git a/util/mcgg/makefile b/util/mcgg/makefile new file mode 100644 index 000000000..bff5d9ab2 --- /dev/null +++ b/util/mcgg/makefile @@ -0,0 +1,23 @@ +# $Id$ +O=.o +E= +CFLAGS= +LDFLAGS= +YFLAGS= +OBJS=iburg$O gram$O +CUSTOM=custom.mk +include $(CUSTOM) + +iburg$E: $(OBJS); $(CC) -o $@ $(LDFLAGS) $(OBJS) + +$(OBJS): iburg.h + +test: iburg$E sample4.brg sample5.brg + ./iburg$E -I sample4.brg sample4.c; $(CC) -o test4$E sample4.c; ./test4$E + ./iburg$E -I sample5.brg sample5.c; $(CC) -o test5$E sample5.c; ./test5$E + +clean:: + rm -f *$O core sample*.c a.out test4$E test5$E + +clobber:: clean + rm -f y.tab.c gram.tab.c iburg$E diff --git a/util/mcgg/sample4.brg b/util/mcgg/sample4.brg new file mode 100644 index 000000000..0b5c6116a --- /dev/null +++ b/util/mcgg/sample4.brg @@ -0,0 +1,109 @@ +%{ +#include +#include +#include +enum { + ADDI=309, ADDRLP=295, ASGNI=53, + CNSTI=21, CVCI=85, I0I=661, INDIRC=67 +}; + +#define STATE_TYPE long +typedef struct tree { + int op; + struct tree *kids[2]; + int val; + struct { STATE_TYPE state; } x; +} *NODEPTR_TYPE, *Tree; +#define LEFT_CHILD(p) ((p)->kids[0]) +#define RIGHT_CHILD(p) ((p)->kids[1]) +#define PANIC printf +#define STATE_LABEL(p) ((p)->x.state) + +int OP_LABEL(NODEPTR_TYPE p) { + switch (p->op) { + case CNSTI: if (p->val == 0) return 661 /* I0I */; + default: return p->op; + } +} + +static void burm_trace(NODEPTR_TYPE, int, int, int); +%} +%term ADDI=309 ADDRLP=295 ASGNI=53 +%term CNSTI=21 CVCI=85 I0I=661 INDIRC=67 +%% +stmt: ASGNI(disp,reg) = 4 (1); +stmt: reg = 5; +reg: ADDI(reg,rc) = 6 (1); +reg: CVCI(INDIRC(disp)) = 7 (1); +reg: I0I = 8; +reg: disp = 9 (1); +disp: ADDI(reg,con) = 10; +disp: ADDRLP = 11; +rc: con = 12; +rc: reg = 13; +con: CNSTI = 14; +con: I0I = 15; +%% + +static int trace; + +/* burm_trace - print trace message for matching p; decrement trace */ +static void burm_trace(NODEPTR_TYPE p, int eruleno, int cost, int bestcost) { + if (trace < 0) + fprintf(stderr, "0x%p matched %s = %d with cost %d vs. %d\n", p, + burm_string[eruleno], eruleno, cost, bestcost); + else if (trace > 0 && cost < bestcost) { + --trace; + fprintf(stderr, "0x%p matched %s = %d with cost %d\n", p, + burm_string[eruleno], eruleno, cost); + } +} + +/* dumpCover - print the matched cover for p */ +static void dumpCover(Tree p, int goalnt, int indent) { + int eruleno = burm_rule(p->x.state, goalnt); + short *nts = burm_nts[eruleno]; + Tree kids[10]; + int i; + + for (i = 0; i < indent; i++) + fprintf(stderr, " "); + fprintf(stderr, "%s\n", burm_string[eruleno]); + burm_kids(p, eruleno, kids); + for (i = 0; nts[i]; i++) + dumpCover(kids[i], nts[i], indent + 1); +} + +static void gen(NODEPTR_TYPE p) { + if (burm_label(p) == 0) + fprintf(stderr, "no cover\n"); + else + dumpCover(p, 1, 0); +} + +static Tree tree(int op, Tree l, Tree r) { + Tree t = malloc(sizeof *t); + + t->op = op; + t->kids[0] = l; t->kids[1] = r; + t->val = 0; + t->x.state = 0; + return t; +} + +main(void) { + Tree t; + + if (getenv("Trace")) + trace = atoi(getenv("Trace")); + printf("i = c + 4;\n"); + t = tree(ASGNI, + tree(ADDRLP, 0, 0), + tree(ADDI, + tree(CVCI, tree(INDIRC, tree(ADDRLP, 0, 0), 0), 0), + (t = tree(CNSTI, 0, 0), t->val = 4, t) + ) + ); + gen(t); + return 0; +} diff --git a/util/mcgg/sample5.brg b/util/mcgg/sample5.brg new file mode 100644 index 000000000..a830d03c9 --- /dev/null +++ b/util/mcgg/sample5.brg @@ -0,0 +1,85 @@ +%{ +#include +#include +#include + +#define TRACE + +enum { MOVE=1, MEM=2, PLUS=3, NAME=4, CONST=6 }; + +#define STATE_TYPE void* +typedef struct tree { + int op; + struct tree *kids[2]; + STATE_TYPE state_label; +} *NODEPTR_TYPE; +#define OP_LABEL(p) ((p)->op) +#define LEFT_CHILD(p) ((p)->kids[0]) +#define RIGHT_CHILD(p) ((p)->kids[1]) +#define STATE_LABEL(p) ((p)->state_label) +#define PANIC printf + +static void burm_trace(NODEPTR_TYPE p, int eruleno, int cost, int bestcost) { +#ifdef TRACE + extern char *burm_string[]; + + fprintf(stderr, "0x%p matched %s with cost %d vs. %d\n", p, + burm_string[eruleno], cost, bestcost); +#endif +} +%} +%term MOVE=1 MEM=2 PLUS=3 NAME=4 CONST=6 +%% +stm: MOVE(MEM(loc),reg) = 1 (4); + +reg: PLUS(con,reg) = 2 (3); +reg: PLUS(reg,reg) = 3 (2); +reg: PLUS(MEM(loc),reg) = 4 (4); +reg: MEM(loc) = 5 (4); +reg: con = 6 (2); + +loc: reg = 7; +loc: NAME = 8; +loc: PLUS(NAME,reg) = 9; + +con: CONST = 10; +%% +static void dumpCover(NODEPTR_TYPE p, int goalnt, int indent) { +#ifdef TRACE + int eruleno = burm_rule(STATE_LABEL(p), goalnt); + short *nts = burm_nts[eruleno]; + NODEPTR_TYPE kids[10]; + int i; + + for (i = 0; i < indent; i++) + fprintf(stderr, " "); + fprintf(stderr, "%s\n", burm_string[eruleno]); + burm_kids(p, eruleno, kids); + for (i = 0; nts[i]; i++) + dumpCover(kids[i], nts[i], indent + 1); +#endif +} + +static NODEPTR_TYPE tree(int op, NODEPTR_TYPE l, NODEPTR_TYPE r) { + NODEPTR_TYPE p = malloc(sizeof *p); + + assert(p); + p->op = op; + p->kids[0] = l; p->kids[1] = r; + return p; +} + +main(void) { + NODEPTR_TYPE p; + + p = tree(MOVE, + tree(MEM, tree(NAME, 0, 0), 0), + tree(PLUS, + tree(MEM, tree(PLUS, + tree(NAME, 0, 0), + tree(MEM, tree(NAME, 0, 0), 0)), 0), + tree(CONST, 0, 0) ) ); + burm_label(p); + dumpCover(p, 1, 0); + return 0; +}