Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
DAVID R. HANSON Princeton and TODD A. PROEBSTING The University of Arizona University
Many code-generator generators use tree pattern matching and dynamic programming. This paper describes a simple program that generates matchers that are fast, compact, and easy to understand. It is simpler than common alternatives: 200700 lines of Icon or 950 lines of C versus 3000 lines of C for Twig and 5000 for burg. Its matchers run up to 25 times faster than Twigs, They are necessarily slower than burgs BURS (bottom-up rewrite system) matchers, but they are more flexible and still practical. Categories
ation;
D.3.4 [Programming
systems and compiler
Languages]:
generators
Processorscode
gener-
compilers;
General
Terms: Languages Key Words and Phrases: Code generation, code-generator Icon programming language, tree pattern matching generator, dynamic pro-
Additional gramming,
1. INTRODUCTION
Many code-generator generators programming (DP) [2, 4, 8]. They and semantic actions produce that, tree for code. They with with matchers use tree pattern matching and dynamic accept tree patterns and associated costs, allocate make two registers passes and over that emit each object subject that
example,
actions
minimum-cost based
generators
Authors addresses: C. W. Fraser, AT&T Bell Laboratories, 600 Mountain Avenue 2C-464, Murray Hill, NJ 07974-0636; D. R. Hanson, Department of Computer Science, Princeton University, Princeton, NJ 08544; T. A. Proebsting, Department of Computer Science, The University of Arizona, Tucson, AZ 85721. Permission to copy without fee all or part of this material is granted provided that the copies are not made or distributed for direct commercial advantage, the ACM copyright notice and the title of the publication and its date appear, and notice is given that copying is by permission of the Association for Computing Machinery. To copy otherwise, or to republish, requires a fee and/or specific permission. @ 1992 ACM 1057-4514/92/0900-0213 $01.50 ACM Letters on Programming Languages and Systems, Vol. 1, No. 3, September 1992, Pages 213-226,
214
C.
W. Fraser et al.
are hard-coded parsers time to identify and mirror the their tree input cover. of string matching [ 1, 15] that, in is but patterns grammars. in the They same use
matchers
recursive-descent
mirror variant
a minimum-cost
use a table-driven
possible matches at the same time. This algorithm than trying each possible match one at a time, BEG matchers, cover. rewrite Twig system) matchers theory use DP [5,6,
Like
at compile
the DP to compile-compile time. BURS but BURS matchers generate optimal main disadvantage of BURS is that delay DP until compile This paper describes tion and writes hard-coded, generators a technique [9, 12]. iburg
be constants;
time permit costs to involve a program called iburg that that that has does DP at compile effective proved
arbitrary computations. reads a burg specificatime. The matcher evolved is into with other of what types of code
a matcher
was built
to test early
versions
burgs specification language and interface, but it is useful in its own right because it is simpler and thus easier for novices to understand, because it allows dynamic cost computation, grammars [16]. iburg has been and because it admits used with good results a larger class of tree in a first course on
compilers. burg and iburg have MIPS, and SPARC code generators
been used also to produce robust VAX, for lCC, a retargetable compiler for ANSI matchers, reference programs but this paper describes them in the
[11]. iburg
similar paid
more several
detail
standard
BEG
it describes programs
and it quantifies
of such
2. SPECIFICATIONS
Figure tions. 1 shows Grammar an extended symbols BNF grammar in for italic burg type, and and iburg terminal specificasymbols of X, a %% in are displayed
are displayed in typewriter and [X] denotes an optional separator, and rules. The
operators
subject treesand associate a unique, positive external symbol number with each one. Nonterminals are declared by their presence on the left side of rules. The %start declaration optionally declares a nonterminal as the start symbol. In Figure 1 term and nonterm denote identifiers that are terminals and nonterminals, respectively. Rules define tree patterns in a fully parenthesized prefix form. Every nonterminal denotes a tree. Each operator has a fixed arity, which is inferred rule is a rule whose pattern is from the rules in which it is used. A chain another nonterminal. If no start symbol is declared, the nonterminal defined by the first rule is used.
Languages and Systems, Vol. 1, No. 3, September 1992.
Generator
215
grammar
dcl
+
-+
{dcl}~~ %start
I
rule cost tree + + + I \
%terrn { identifier=
nonterm ( integer term term : tree ) , tree )
= integer
( tree ( tree)
\ Fig. 1. Extended
BNF grammar
1.
%term %term TL stint: stint: reg: reg: reg: reg: disp: disp: rc: rc: con: con:
ADDI=309 CNSTI=21
2. 3. 4, 5. 6. 7. 8. 9.
10. 11. 12. 13. 14. 15.
ASGNI(disp,reg) reg = 5; ADDI(reg,rc) 101 = 8; disp = 9 (l); ADDI(reg,con) ADDRLP = 11; con = 12; reg = 13; CNSTI = 14; 101 = 15: Fig.2. Sample burg
CVCI(INDIRC(disp))
= 10;
specification.
Each
rule
has aunique,
positive
external
rule
number,
which
comes
after
the pattern andis preceded by an equal sign.As described below, rule numbers are used to report the matching ruleto auser-supplied tic action routine. Rules end omitted costs defaultto zero. Figure example 1-2 2 shows a fragment uses uppercase the operators with an optional nonnegative, for the
integer VAX.
of a burg external
specification symbol
and lowercase
declare
numbers figures.
usually generated by a preprocessor (e.g., including YACC-style semantic tion [13]. Only the rules on lines on lines 5, 9, 12, and 13 are chain
that accepts a richer form of specification actions) and that emits a burg specificanonzero costs. The rules rules.
Languages and Systems, Vol. 1, No. 3, September 1992.
4, 6, 7, and 9 have
C. W. Fraser et al, in Figure The with operator and pointer integer integer 2 are some names of the operators like in lCCS intermediate a generic denote used in of a local constant
formed suffix
a one-character addition
C, I, or P, which
integer, (ADDRLP),
operations, assignment
(CNSTI), widening a character to an integer (CVCI), and fetching a character (INDIRC). The rules show that are binary; CVCI and INDIRC are unary; and ADDRLP, are leaves. 3. MATCHING Both reduce tree versions subject the of burg trees. minimum pass over generate The the labeling subject cost, functions function, tree is such that the label(p), the a cover. client
calls rules
and the M
left-to-right with
if there
with (M, C) to indicate that the matches the node with cost C. Figure 3 shows the intermediate C fragment: +4;} node which Figure sion in the following {inti; The left computes charc; child i=c
pattern language
external
for the
assignment
expres-
of the ASGNI
computes
the
address widens
child and C)
the address
of C, fetches
the character,
(M,
denote labels from matches, and [M, C] denote labels from chain rules. The rule from Figure 2 denoted by each M is also shown. Each C sums the costs of the pattern nonterminals or chain rule. on the i with right-hand side and the cost with(11, of the relevant 2 matches For example, the pattern in line 11 of Figure
pattern denotes a disp, the chain rule in line 9 applies with a cost of O for matching a disp plus 1 for the chain rule itself. Likewise, the chain rules in lines 5 and 13 apply can specify in line because subtrees the chain beyond 2 refers rule in line 9 denotes children. a reg. For example, node. No Patterns the pattern the immediate to the grandchild
7 of Figure
of the CVCI
separate pattern matches the INDIRC node, but node. The cost is the cost of matching the ADDRLP 11, plus 1. Nodes are annotated with (M, C) only if C
line 7s pattern covers that i as a disp, which is rule is less than all previous
matches for the nonterminal on the left-hand side of rule M. For example, the ADDI node matches the disp pattern in line 10 of Figure 2, which means that it also matches all rules with disp alone on the right-hand side, namely, line 9. By transitivity, it also matches the chain rules in lines 5 and 13. But all three of these chain rules yield cost 2, which is not better than previous matches for those nonterminals. Once labeled, a subject tree is reduced by traversing it from the top down
ACM Letters on Programming Languages and Systems, Vol. 1, No 3, September 1992.
Generator
217
disp:
(4, 0+2+1=3)
reg)
resf:
stint: rc:
ASGNI
ADDI(re9,
con)
reg:
(7, 0+1=1)
A
and that by performing in these identifies appropriate traversals, subtrees emitting another burg code. Reducers that are supplied assist
ADDRLP C
actions,
such
and M and a
by clients,
but burg
functions
Reference
single, integral state number, ing matches and costs. iburg nodes with data equivalent
which encodes all of the information does the DP at compile time and C). Its state numbers are really that
to (M,
an implementation or functions
of label
node fields via client-supplied macros function state to identify matches: int
label(NODEPTR_TYPE p) { if (p) { int 1 = label(LEFT_CHILD(p)); int r = label(RIGHT.CHILD(p)); return STATE_LABEL(p) = state(OP_LABEL(p), } else return O;
1, r);
} NODEPTR.TYPE nodes. OP_LABEL, functions child, state that return, field. and its right number is a typedef or macro that defines the data type LEFT. CHILD, and RIGHT_ CHILD are macros respectively, child. a nodes external is a macro symbol that number, accesses STATE_LABEL of or
state accepts an external symbol number for a node and the state numbers for the nodes left and right children. It returns the state number to assign to that node. For unary operators and leaves, it ignores the last one or two arguments, respectively.
ACM Letters on Programming Langnages and Systems, Vol. 1, No. 3, September 1992.
218
C. W. Fraser et al
4. IMPLEMENTATION
iburg of tree state Figure generates pattern numbers for a state matching successful function [7]. It that uses a straightforward hard code instead hold for the records, state which record implementation of tables. specification Its in vectors of the (M, generates The
are pointers
to state
C) values
matches.
2 is the following: struct state { int op; struct state * left, short cost[6]; short rule[6]; };
* right;
iburg cost
also
generates vectors:
integer
codes
for
the
nonterminals,
which
index
the
stmt_NT 1 disp_NT 2 rc_NT 3 reg_NT 4 con_NT 5 the start a nonzero that defines the nonterminal when value for has the value allocated, X. of state on the and gives symbol the cases that are allocates and initializes to begin test for a by itself is called that switch and p + rule[ 1. rule numbers that ps are node
By convention, State positive. matched Figure contributed a new state records Thus, a rule
are cleared
external
X ] indicates
nonterminal
4 shows record
by Figure
and
number
matching. Each nonleaf case is one or more if statements match by consulting the state records of descendants. The does all the necessary If a match with succeeds, to the the pointer testing state for leaves. cost is computed, the rule number: cost, int eruleno) the resulting record, external
and record
nonterminal,
void record(struct state *p, int if (cost < p ~ cost[ntl) { p ~ cost[ntl = cost; p ~ rule[ntl = eruleno; 1
nt, int
The
match
is recorded
only
if its
cost
is less with
than
previous
matches.
The
32767
to represent
infinite
The first call to record is for the match itselfi the other calls are for chain rules. For example, the second if statement in the ADDI case tests whether ps node matches the pattern records that the node matches
ACM Letters on Programming
in line 10. If it does, the first call to record a disp. The chain rule in line 9 says that a
Generator
219
int
op,
int state
left, *1 *r = =
int
(struct (struct
p = malloc(sizeof p->op p->rule[l] switch case if (op) ADDI: (1->rule[reg-lJTl = op; = { p->left
. . . = O;
p->cost[l]
r->rule[rc_NT)
c = l->cost[reg_NT]
+ r->cost[rc_NT]
C, 6) ; 13); 5); c + O,
+ 1;
record(p,
record(p, record(p, } if (1->rule[reg-IiT]
reg_NT,
rc_NT, stmt_NT,
c + O,
{ + O;
c + 1,
c + 1 + O, c + i
+ O,
3
break; case ADDRLP: C=o; record(p, record(p, record(p, record(p, break; case if CVCI: (1->op == INDIRC &k l->left->rule[disp-NT]) + 1; { disp_NT, reg_NT, rc_NT, stmt_NT, c, c + 11); 1, 9); 13); 5);
c + 1 + O, c + 1 + 0,
c = l->left>cost record(p, record(p, record(p, 1 break; . . . 3 return } Fig. 4. (int)p; reg_NT, rc_NT, stmt_NT,
c + 0,
c + 0,
5);
Implementation
of state.
220
C. W. Fraser et al.
a disp rules also matches in lines and a reg with an additional say that closure cost of 1, which are a a node matching cost rules
13, which
of O. In
general, there is a call to record for the transitive that reach the nonterminal defined by the match.
of all chain
5. IMPROVEMENTS
The generated code-generation Students a couple that make matcher described and in the previous the generator emits however, smaller 642 lines vector that section itself several and faster. is practical for many 4 in imrule exam-
elements
of the rule
can accommodate
nonterminals
are defined
ple, only lines 10 and 11 in Figure to record one of the two positive compact bit fields, range of integers and as in the following
2 define disp, so only two bits are needed values. Definitions can be mapped into a in minimum space in state records as
stored
example:
struct state { int op; struct state * left, *right; short cost [6]; struct { unsigned int stmt:2; unsigned int disp:2; unsigned int rc:2; unsigned int reg3; unsigned int com.2; } rule; }; External number rule that external short short short short short rule numbers for matches to map the are retrieved iburg integers in the example: by calling compact rule with a state of to
[13].
generates
an implementation representation
as in the following [1= = [ 1= [1 = [1 = {O, {o, {O, {O, {O, 10,11 ]; 12, 131; 4,5 }; 6,7,8,9 }; 14,15];
int rule(int state, struct state * p switch (goalnt) case disp_NT case rc_NT.
ACM Letters on Programming
int goalnt) { = (struct state * )state; { return decode. disp[p + rule. disp]; return decode_rc[p -+ rule.rc];
Languages and Systems, Vol. 1, No. 3, September 1992
A Simple, Efficient Code-Generator case stmt_NT case reg_NE case con_ NT: } return return return decode_ decode_ decode_ stmt[p ~ rule. stint]; reg[p - rule.reg]; con[p ~ rule. con];
Generator
221
1
Packed
rule
numbers
cannot
be subscripted,
so record
and
the
code that
tests for a match large specifications. and the encoding bytes. Packing and decode much ized With faster, rule packed rule
must be changed. This scheme can save much space for For example, the VAX specification has 47 nonterminals, scheme reduces the size of its rule vector from 96 to 16 can also numbers, structure assignments original packed start fields. further needs still: All costs much The be set, but rule fields symbol initialization. save time: It takes longer VAX yet, to read, matcher been but that write, initialslower. swamps
47 assignments; fields,
a 16-byte
copy beats
47 assignments
by a margin
are read in only match. The rule always fields tion then will rule begins should at all. the prevent numbers that initializers zero They
two places: the rule function above, and the tests for a function is called during a top-down tree traversal, which the start symbol then fields match. as the the tree goal nonterminal. to match, require if they If it finds and read no rule the rule read number, the failed a false rule failed tests no more initializagarbage, which packing cost in
with
be examined
anyway.
The match
to match,
be infinite, initializer,
saves time,
but it still
is so small record
it could
not be measured. If the cost test implement its chain rules must fail too, because These calls can be avoided if the cost test both this improvement and packed rules. in the ADDI case in Figure 4 becomes costs fails. For the
the calls to record that increase monotonically. Inlining example, following: record the second
accommodates if statement
if (1 -+ rule.reg && r -+ rule. con) { c = 1- cost[reg_NT] + r ~ cost[con_NT] + O; if (c < p -+ cost[disp_NT]) { / * disp: ADDI(reg, con) * / p - cost[disp_NT] = c; p s rule.disp = 1; closure_ disp(p, c); }
1
P ~ ~le.disp rule 10. This each nonterminal
k set to 1 because
a more that
above
maps
compact
chain iburg
can be reached
generates
222
C.
closure_X, which records the chain rule match if its previous matches and, if applicable, calls another closure ple, the closure function for disp is the following: void closure_ disp(struct state *p, int c) { if (c + 1 < p - cost[reg_NT]) { / * reg disp p ~ cost[reg_NTl = c + 1; p ~ rule.reg = 4; closure_ reg(p, c + 1); } 1 The incoming rule. reg This disp, cost, c, is the cost of matching the cost of the chain cost of this application function. rule is the
*\
side of the chain 1 for line this sum rules chain 9s is for and
cost plus
closure_reg
void closure_reg(struct state *p, int c) { if (c + O < p ~ cost[rc_NT]) { \ * rc: reg * / p ~ cost[rc_NTl = c + O; p ~ rule.rc = 2; 1 if (c + O < p ~ cost[stmt_NT]) { / * stmti reg * / p - cost[stmt_NT] = c + O; p ~ rule.stmt = 2; } } The final improvement saves times for leaves, which abound in subject
trees from code generators. record data about matches Leaves, however, always
The computation and encoding at compile-compile time are match, and the contents of the
of all of the state complicated [18]. state record are and closure be allocated case in
easily computed by simulating the effect of the assignments function calls shown above. The state records for leaves can thus and initialized at compile-compile Figure 4 becomes the following: case ADDRLP. static struct { state time; for example, the
ADDRLP
z = { 295,0,0,
{ o,
1,/ * stint: O,/ * disp: rcireg 1,/. 1,/ * reg 32767, },{2, / * stint: 2,/ * disp: rc:reg 2,/. 4,/ * reg }};O return } reg * / ADDRLP
./
*/
disp
*/
reg */ ADDRLP
*/
*/
disp
*/
(int) &z;
Languages and Systems, Vol. 1, No. 3, September 1992
Generator
223
Version Original untuned version Inline record; add closure routines Initialize only one element of rule Precompute leaf states Pack rule numbers
The
first
three
values
initialize
the
op,
left,
and
right
fields
of the
state
structure. The two brace-enclosed initializers given the cost respectively. The code at the beginning of state (see Figure and initializes test that Table shows tion resulting the state machines Closure one place ment must could I traces the number cost. The matcher. a state the record addition column processor. is not needed of each of Icon column shows for leaves, excludes leaf ops. improvement the number lcc column above. of
in iburg times
and helps
quantify object
implementa-
second
in a typical shows
cross-compilation the time for RISC rules improveand have state must in
on a MIPS
The fourth
and rule routines. All times are in seconds. would show smaller improvements. routines rather save so much than in multiple in this other On the the half. space because record trial, hand, small, but they runs. packing The
it is trivial rule
cost appears
and it cuts
Two proposed improvements proved uneconomical. First, the closure routines were inlined; measurements showed that the matcher was faster, but it was larger than even the initial version above. Independently, the closure routines were recoded to avoid tail recursion, but no speedup was measured. The recoding replaced ment, and the switch possible that though large Much parsing the respectively, each closure routine bound check added with a case in a switch stateunnecessary overhead; so it is of tail recursion could For do better, example,
a compiler implementation speedups seem unlikely. processing done by iburg input and writing the output in the 642-line final version. written in C is 950 lines,
of the
is straightforward.
account for 181 and 159 lines, By way of comparison, a new and a burg processor is 5100 lines
6. DISCUSSION
iburg was built to test early versions of what evolved into burgs specification language and interface. Initial tests used Twig and a Twig preprocessor, but Twig produced incorrect matchers for large CISC grammars. The error proved hard to find, so Twig was abandoned and iburg was written. The
224
C.
W. Fraser et al.
Table 11, Times for Compiling C Programs the SPEC Benchmarks Benchmark iburg 90.2 iburg 77.9 in
initial
version
was
in two
days
with
of Icon.
The
final, is 642
version generated
support burg.
by iburg
those
from
Table
II
shows the times for compiling the C programs in the SPEC benchmarks [19] with two versions of lCC. These times are for running only the compiler proper; preprocessing, assembly, and linking time are not included. The compilations were done on an IRIS 4D/220GTX 3.3.2, and the times are elapsed time in seconds times over at several least runs 96 on a lightly utilization times loaded [i.e., achieved percent with 32MB and are the machine. the ratio All running IRIX lowest elapsed reported runs (user +
of times to differences
Profiling to burgs
the time,
On these
accounts
of the execution
accounts for only 1. 12.0 percent, making burg Comparable figures for Twig are unavailable process large grammars, were but before Using work with measurements taken. a nearly
roughly 6 12 times because it did not Twig complete was abandoned, VAX
grammar,
and 0.85 s in the iburg matcher. Using a partial MIPS grammar, ICC compiled the module in 9.19 s using a Twig matcher and 4.54 s using a matcher from the initial iburg; it spent 4.04 s in the Twig matcher and 0.16 s in the slowed slower. iburg, iburg matcher. Both versions which of Icc is why used a naive emitter that was with with by complex grammars, the VAX compiler was so much
are useful for comparing Twig them useless for comparisons is that the costs must
of BURS
be constant
because the DP is done at compile-compile however, can involve arbitrary computation example, ASGNI(disp, the pattern CNSTI)
specifies a clear instruction if the constant is O. Twigs cost computations can inspect the subject tree and return a cost of, say, 1 if the constant is O and infinity otherwise.
ACM Letters on Programming Languages and Systems, Vol. 1, No, 3, September 1992,
A Simple, EfFicient Code-Generator BURS tional state, specifications that can handle identify the pass changes this CNSTI kind of context
Generator sensitivity
. with
225 addicalling
operators
special
cases. For
example,
before
is O. Thus,
101)
specifies a clear instruction. Most context-sensitive cases that machines, can be handled similarly, example, rules could rml
L(J.
arise in code generation, even for CISC perhaps with a few additional rules. For indexed addressing much like mode BEGs state their parser prefer takes predicates conditions and mirrors label its 12 easily be extended so that
recognizing
can be useful
development.
the code for a recursive-descent ideal used Twig, mistakes numbers students
attribute previously
for teaching.
used in a course
iburg.
students make their inevitable Twigs or burgs, only inscrutable the debugger. When records the matching easily compare
they make mistakes with iburg, each rules and costs for each nonterminal, actual operation with their
the matchers
expectations.
ACKNOWLEDGMENTS
2 borrows in pub.
from
[13],
parts
of which
were
written ftp
by Robert from
Henry.
C version
of iburg
is available
for anonymous
ftp.cs.prince-
AUTHORS
NOTE
Section fields.
5 notes that
BEG
make
[7] carried
observation
case in Section
saves something,
and it is easier
REFERENCES
1. AHO, A. V., AND COHASICK, M. J. Efficient string matching An aid to bibliographic search. Commun. ACM 18, 6 (June 1975), 333-340. 2. AHO, A. V., AND JOHNSON, S. C. Optimal code generation for expression trees. J. ACM 23, 3 (July 1976), 488-501. 3. AHO, A. V., GANAPATHI, M., AND TJIANG, S. W. K. Code generation using tree matching and ACM Trans. Program. Lang. Syst. 11, 4 (Oct. 1989), 491-516. dynamic programming. Principles, Techniques, and Tools. 4. AHO, A. V., SETHI, R., AND ULLMAN, J. D. Compilers:
226
C. W. Fraser et al
Efficient retargetable code generaJ. Corrqmt. Lang. 15, 3 (1990), 127-140. tree pattern matching. In Conference Record of the ACM Symposium on Principles of Programming Languages (Munich, Germany, Jan. 21-23, 1987). ACM, New York, 168-177. 7. EMMELMANN, H., SCHROER, F.-W., AND LANDWEHR, R. BEGA generator for efficient back of the SIGPLAN 89 Conference on Programming Language Destgn and ends. In Proceedings In
Code
Implementation. SIGPLAN Not. (ACM) 24, 7 (July 1989), 227-237. 8. FERDINAND, C., SEIDL, H., AND WILHELM, R. Tree automata for code selection. GenerationConcepts, Tools. Techniques, Proceedings of the International Workshop
on Code
Generation (Dagstuhl, Germany), R. Giegerich and S. L. Graham, Eds. Springer-Verlag, New York, 1991, 30-50. of the SIGPLAN 89 9. FRASER, C. W. A language for writing code generators. In Proceedings Conference on Programming Language Design and Implementation. SIGPLAN Not.
(ACM)
Pratt. Not.
17.
10 (Oct. 1991), 29-43. FRASER, C. W., AND HENRY, R. R. Hard-coding bottom-up code generation tables to save time and space. Softw. Pratt. Exper. 21, 1 (Jan. 1991), 112. FRASER, C. W., HENRY, R. R., AND PROEBSTING, T. A. BURGFast optimal instruction Not. (ACM) 27, 4 (Apr. 1992), 6876. selection and tree parsing. SIGPLAN GRISWOLD,R. E., AND GRISWOLD, M. T. The Icon Programming Language. 2nd ed. PrenticeHall, Englewood Cliffs, N.J., 1990. HOFFMAN, C. M., AND ODONNELL, M. J. Pattern matching in trees. J. ACM 29, 1 (Jan. 1982), 68-95. PELEGRf-LLOPART, E. Tree transformation in compiler systems. Ph.D. thesis, Computer Science Division, Dept. of Electrical Engineering and Computer Science, Univ. of California, Berkeley, Calif., Dec. 1987. PELEGRf-LLOPART, E., AND GRAHAM, S. L. Optimal code generation for expression trees: An application
Programming SIGPLAN Not.
Record of the ACM S.wwsiu m on princwles of of BURS theory. In Conference Languages (San Diego, Calif., Jan. 13-15, 1988). ACM, New York, pp. 294-308. of the 18. PROEBSTING, T. A. Simple and efficient BURS table generation. In Proceedings 92 Conference on Programming Language SPEC Design Benchmark and Implementation. Suite Release SIGPLAN 1.0. Standards