diff --git a/.distr b/.distr new file mode 100644 index 00000000..24a21cea --- /dev/null +++ b/.distr @@ -0,0 +1,19 @@ +Action +Copyright +NEW +README +TakeAction +bin +doc +emtest +etc +first +h +include +modules +lang +lib +mach +man +mkun +util diff --git a/Action b/Action index 7caae0ba..8f2e6503 100644 --- a/Action +++ b/Action @@ -7,12 +7,25 @@ end name "EM definition" dir etc end -name "C preprocessor" -dir util/cpp +name "LL(1) Parser generator" +dir util/LLgen end name "EM definition library" dir util/data end +name "C utilities" +dir util/cmisc +end +name "Modules" +dir modules/src +indir +end +name "C preprocessor" +dir util/cpp +end +name "ACK object utilities" +dir util/amisc +end name "Encode/Decode" dir util/misc end @@ -25,6 +38,10 @@ end name "EM Peephole optimizer" dir util/opt end +name "EM Global optimizer" +dir util/ego +indir +end name "ACK archiver" dir util/arch end @@ -34,18 +51,24 @@ end name "Bootstrap for backend tables" dir util/cgg end -name "LL(1) Parser generator" -dir util/LLgen -end name "Bootstrap for newest form of backend tables" dir util/ncgg end +name "LED link editor" +dir util/led +end +name "TOPGEN target optimizer generator" +dir util/topgen +end name "C frontend" -dir lang/cem/comp +dir lang/cem/cemcom end name "Basic frontend" dir lang/basic/src end +name "Occam frontend" +dir lang/occam/comp +end name "Intel 8086 support" dir mach/i86 indir @@ -82,10 +105,6 @@ name "4-4 Interpreter support" dir mach/int44 indir end -name "IBM PC/IX support" -dir mach/ix -indir -end name "Motorola 68000 2-4 support" dir mach/m68k2 indir @@ -114,14 +133,26 @@ name "Signetics 2650 support" dir mach/s2650 indir end -name "Vax 2-4 support" -dir mach/vax2 -indir -end name "Vax 4-4 support" dir mach/vax4 indir end +name "M68020 System V/68 support" +dir mach/m68020 +indir +end +name "Sun 3 M68020 support" +dir mach/sun3 +indir +end +name "Sun 2 M68000 support" +dir mach/sun2 +indir +end +name "Mantra M68000 System V.0 support" +dir mach/mantra +indir +end name "Z80 support" dir mach/z80 indir diff --git a/Copyright b/Copyright index 2c794d37..ba4dd0a0 100644 --- a/Copyright +++ b/Copyright @@ -1,7 +1,7 @@ /* - * (c) copyright 1983 by the Vrije Universiteit, Amsterdam, The Netherlands. + * A M S T E R D A M C O M P I L E R K I T * - * This product is part of the Amsterdam Compiler Kit. + * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. * * Permission to use, sell, duplicate or disclose this software must be * obtained in writing. Requests for such permissions may be sent to @@ -14,4 +14,3 @@ * The Netherlands * */ - diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..bf4572b2 --- /dev/null +++ b/Makefile @@ -0,0 +1,35 @@ +cmp: # compile everything and compare + (cd etc ; make cmp ) + (cd util ; make cmp ) + (cd lang ; make cmp ) + (cd mach ; make cmp ) + +install: # compile everything to machine code + (cd etc ; make install ) + (cd util ; make install ) + (cd lang/cem ; make install ) + (cd mach ; make install ) + (cd lang/pc ; make install ) + +clean: # remove all non-sources, except boot-files + (cd doc ; make clean ) + (cd man ; make clean ) + (cd h ; make clean ) + (cd etc ; make clean ) + (cd util ; make clean ) + (cd lang ; make clean ) + (cd mach ; make clean ) + +opr: # print all sources + make pr | opr + +pr: # print all sources + @( pr Makefile ; \ + (cd doc ; make pr ) ; \ + (cd man ; make pr ) ; \ + (cd h ; make pr ) ; \ + (cd etc ; make pr ) ; \ + (cd lang ; make pr ) ; \ + (cd util ; make pr ) ; \ + (cd mach ; make pr ) \ + ) diff --git a/NEW b/NEW index 9648505b..bfb63c09 100644 --- a/NEW +++ b/NEW @@ -1,17 +1,27 @@ What's new: - A lot of things have changed since that previous distribution. + A lot of things have changed since the previous distribution. It is not wise to mix files created by the previous version of the Kit with files belonging to this version, although that might sometimes work. -The major additions are: - - Basic frontend - - New codegenerator - - LL(1) parser generator - - Vax backend with 4-byte wordsize - - Motorola 68000 backend with 4-byte wordsize - - Motorola 68000 interpreter for 2- and 4-byte wordsize - - Z8000 assembler and backend. - - 6805 assembler - - NatSem 16032 assembler - - Intel 8080 backend - - Zilog Z80 backend - - Signetics 2650 assembler +The major changes are: + - a new C-compiler and runtime system + - a new C preprocessor + - new assembler framework, allowing the generation of relocatable + object code for most processors + - new versions of all assemblers, using the new assembler framework + - a new link-editor, linking is now a separate and fast phase for most + machines + - improved Pascal compiler, now also handles 4-byte wordsize + - Motorola M68020 backend and assembler + - Support for (some) SUN systems + - improved version of LL(1) parser generator, producing faster code + - a new language: Occam + - better System V support, the Kit should now just compile and run + + Ceriel J.H. Jacobs + Dept. of Math. and Computer Science + Vrije Universiteit + Postbus 7161 + 1007 MC Amsterdam + The Netherlands + + (UseNet: ceriel@cs.vu.nl) diff --git a/TakeAction b/TakeAction index 082a8083..a2f56f73 100755 --- a/TakeAction +++ b/TakeAction @@ -19,7 +19,7 @@ RETC=0 do eval set $LINE case x"$1" in - x#*) ;; + x!*) ;; xname) SYS="$2" ACTION='make $PAR' DIR=. @@ -44,12 +44,18 @@ do FAIL="$2" ;; xsuccess) SUCC="$2" ;; xdir) DIR="$2" ;; - xsystem) case `ack_sys` in - $2) ;; - *) echo "Sorry, $SYS can only be made on $2 systems" + xsystem) PAT="$2" + oIFS=$IFS + IFS="|" + eval set $2 + case x`ack_sys` in + x$1|x$2|x$3|x$4|x$5|x$6|x$7) ;; + *) echo "Sorry, $SYS can only be made on $PAT systems" DOIT=no ;; - esac ;; + esac + IFS=$oIFS + ;; xend) case $DOIT in no) continue ;; esac diff --git a/bin/.distr b/bin/.distr new file mode 100644 index 00000000..353cf133 --- /dev/null +++ b/bin/.distr @@ -0,0 +1 @@ +em.pascal diff --git a/bin/em.pascal b/bin/em.pascal index c77dc1e7..2e03475b 100755 --- a/bin/em.pascal +++ b/bin/em.pascal @@ -1 +1 @@ -exec /usr/em/doc/em.doc/int/em /usr/em/doc/em.doc/int/tables ${1-e.out} core +exec /usr/em/doc/em/int/em /usr/em/doc/em/int/tables ${1-e.out} core diff --git a/distr/Action b/distr/Action index 0eddf7fe..c50f46f9 100644 --- a/distr/Action +++ b/distr/Action @@ -1,15 +1,12 @@ name "Installation manual" dir doc end -name "EM documentation" -dir doc/em.doc -end name "Pascal bootstrap files" dir lang/pc/pem end name "LLgen bootstrap files" dir util/LLgen end -name "MSC6500 vend_library" -dir mach/6500/libem +name "ego share pop_push file" +dir util/ego/share end diff --git a/distr/Action1 b/distr/Action1 index b342c2ff..d1add76b 100644 --- a/distr/Action1 +++ b/distr/Action1 @@ -1,6 +1,9 @@ -name "vax2/cg bootstrap files" -dir mach/vax2/cg +name "m68k2/cg bootstrap files" +dir mach/m68k2/cg end name "vax4/cg bootstrap files" dir mach/vax4/cg end +name "m68020/ncg bootstrap files" +dir mach/m68020/ncg +end diff --git a/distr/Exceptions b/distr/Exceptions index c0139dbb..a95572a5 100644 --- a/distr/Exceptions +++ b/distr/Exceptions @@ -1,5 +1,3 @@ --- ./bin/em.pascal no RCS file --- ./doc/em.doc/doc.pr no RCS file -- ./doc/install.pr no RCS file -- ./h/em_mnem.h no RCS file -- ./h/em_pseu.h no RCS file @@ -8,29 +6,15 @@ -- ./lang/basic/src/y.tab.h no RCS file -- ./lang/pc/pem/pem22.m no RCS file -- ./lang/pc/pem/pem24.m no RCS file +-- ./lang/pc/pem/pem44.m no RCS file -- ./lib/LLgen/incl no RCS file -- ./lib/LLgen/rec no RCS file --- ./lib/ix/head_em no RCS file --- ./lib/ix/head_i no RCS file --- ./lib/ix/tail_em no RCS file --- ./lib/ix/tail_em.vend no RCS file --- ./lib/ix/tail_mon no RCS file --- ./mach/6500/libem/tail_em.ve.s.a no RCS file --- ./mach/vax2/cg/tables1.c no RCS file --- ./mach/vax2/cg/tables1.h no RCS file +-- ./mach/m68k2/cg/tables1.c no RCS file +-- ./mach/m68k2/cg/tables1.h no RCS file +-- ./mach/m68020/ncg/tables1.c no RCS file +-- ./mach/m68020/ncg/tables1.h no RCS file -- ./mach/vax4/cg/tables1.c no RCS file -- ./mach/vax4/cg/tables1.h no RCS file --- ./mach/z80/int/libpc/pc_tail.c.a no RCS file --- ./mkun/pubmac no distr2 yet --- ./mkun/tmac.q no distr2 yet --- ./mkun/tmac.q1 no distr2 yet --- ./mkun/tmac.q2 no distr2 yet --- ./mkun/tmac.q3 no distr2 yet --- ./mkun/tmac.q4 no distr2 yet --- ./mkun/tmac.q5 no distr2 yet --- ./mkun/tmac.q6 no distr2 yet --- ./mkun/tmac.q7 no distr2 yet --- ./mkun/tmac.q8 no distr2 yet -- ./util/LLgen/src/parser no RCS file -- ./util/LLgen/src/LLgen.c no RCS file -- ./util/LLgen/src/Lpars.c no RCS file @@ -39,4 +23,4 @@ -- ./util/data/em_flag.c no RCS file -- ./util/data/em_mnem.c no RCS file -- ./util/data/em_pseu.c no RCS file --- ./util/data/em_ptyp.c no RCS file +-- ./util/ego/share/pop_push.h no RCS file diff --git a/distr/How_To b/distr/How_To index 7a130ced..d4091190 100644 --- a/distr/How_To +++ b/distr/How_To @@ -4,7 +4,7 @@ The EM home directory contains a file called ".distr". It contains the names of all the files and directories you want to have in the distribution. The directories should contain .distr files, the other files should be placed under RCS. -The current RCS revision name is "distr2". +The current RCS revision name is "distr3". The are files that derive from other files and yet should be placed in the distribution. These files should not be placed under RCS. @@ -25,12 +25,12 @@ destination tree. For each file mentioned there it performes certain actions: 1- Directory Change to that directory and call yourself recursively. 2- File - a- Try to do "co -rdistr2 destination_tree/path/destination_file" + a- Try to do "co -rdistr3 destination_tree/path/destination_file" on succes "chmod +w destination_file" else b- Try to do "co destination_tree/destination_file" on succes "chmod +w destination_file" and - give message that says "Missing distr2 entry" (or some such). + give message that says "Missing distr3 entry" (or some such). else c- I Does a file LIST exist in this directory AND is the first line of LIST equal to the name of the @@ -52,23 +52,23 @@ Some files derive from other files in the tree, those derivations should be done with the use of an already installed distribution. The files Action and Action1 in this directory contain the actions we now take. (Confession: most of the time we use /usr/em) -One warning, to re-nroff the IR-81 report it takes more then just nroff -because most nroff's can't stand that report and stop half-way. -The ntroff program does the trick, but only on the 11's. - tbl sources | ntroff -Tlp | ntlp After running these re-derivation programs the distrubtion tree starts to look like the tree you need. There are too many files there though, especially the files created by the derivation process. That is why we now give the command: - dtar cdf distr2 . -The file distr2 is the one you should put on tape! + dtar cdf distr3 . +The file distr3 is the one you should put on tape! But,.... before doing that: Try it out! Repeat the process described in the installation manual. Only if that succeeds you are sure that you included the files needed, -and gave all other files the correct "distr2" RCS id. -After you sent the tape away, forbid ANYBODY to touch the distr2 id +and gave all other files the correct "distr3" RCS id. +After you sent the tape away, forbid ANYBODY to touch the distr3 id in your RCS files. Good Luck, Ed Keizer, 85/4/15. + +Updated for 3rd distribution by Ceriel Jacobs, 87/3/11. +And again, + Good Luck! diff --git a/distr/dwalk b/distr/dwalk index 9835e3df..ec9cc3af 100755 --- a/distr/dwalk +++ b/distr/dwalk @@ -14,7 +14,7 @@ do ${DD-:} $CDIR $i CDIR=$CDIR/$i export CDIR - exec /usr/em/distr/dwalk + exec /proj/em/distr/dwalk else echo ++ Could not access $CDIR/$i fi diff --git a/distr/f.attf b/distr/f.attf index c0139dbb..a95572a5 100644 --- a/distr/f.attf +++ b/distr/f.attf @@ -1,5 +1,3 @@ --- ./bin/em.pascal no RCS file --- ./doc/em.doc/doc.pr no RCS file -- ./doc/install.pr no RCS file -- ./h/em_mnem.h no RCS file -- ./h/em_pseu.h no RCS file @@ -8,29 +6,15 @@ -- ./lang/basic/src/y.tab.h no RCS file -- ./lang/pc/pem/pem22.m no RCS file -- ./lang/pc/pem/pem24.m no RCS file +-- ./lang/pc/pem/pem44.m no RCS file -- ./lib/LLgen/incl no RCS file -- ./lib/LLgen/rec no RCS file --- ./lib/ix/head_em no RCS file --- ./lib/ix/head_i no RCS file --- ./lib/ix/tail_em no RCS file --- ./lib/ix/tail_em.vend no RCS file --- ./lib/ix/tail_mon no RCS file --- ./mach/6500/libem/tail_em.ve.s.a no RCS file --- ./mach/vax2/cg/tables1.c no RCS file --- ./mach/vax2/cg/tables1.h no RCS file +-- ./mach/m68k2/cg/tables1.c no RCS file +-- ./mach/m68k2/cg/tables1.h no RCS file +-- ./mach/m68020/ncg/tables1.c no RCS file +-- ./mach/m68020/ncg/tables1.h no RCS file -- ./mach/vax4/cg/tables1.c no RCS file -- ./mach/vax4/cg/tables1.h no RCS file --- ./mach/z80/int/libpc/pc_tail.c.a no RCS file --- ./mkun/pubmac no distr2 yet --- ./mkun/tmac.q no distr2 yet --- ./mkun/tmac.q1 no distr2 yet --- ./mkun/tmac.q2 no distr2 yet --- ./mkun/tmac.q3 no distr2 yet --- ./mkun/tmac.q4 no distr2 yet --- ./mkun/tmac.q5 no distr2 yet --- ./mkun/tmac.q6 no distr2 yet --- ./mkun/tmac.q7 no distr2 yet --- ./mkun/tmac.q8 no distr2 yet -- ./util/LLgen/src/parser no RCS file -- ./util/LLgen/src/LLgen.c no RCS file -- ./util/LLgen/src/Lpars.c no RCS file @@ -39,4 +23,4 @@ -- ./util/data/em_flag.c no RCS file -- ./util/data/em_mnem.c no RCS file -- ./util/data/em_pseu.c no RCS file --- ./util/data/em_ptyp.c no RCS file +-- ./util/ego/share/pop_push.h no RCS file diff --git a/distr/mkf b/distr/mkf index 145a2e32..2d8faace 100755 --- a/distr/mkf +++ b/distr/mkf @@ -1,10 +1,10 @@ -if co -q -rdistr2 $DESTDIR/$1/$2 >/dev/null 2>&1 +if co -q -rdistr3 $DESTDIR/$1/$2 >/dev/null 2>&1 then chmod +w $DESTDIR/$1/$2 elif co -q $DESTDIR/$1/$2 >/dev/null 2>&1 then chmod +w $DESTDIR/$1/$2 - echo -- $1/$2 no distr2 yet + echo -- $1/$2 no distr3 yet elif grep LIST .distr >/dev/null 2>&1 && (test "$2" = "`head -1 $DESTDIR/$1/LIST`") >/dev/null 2>&1 && ${DA-false} "$1" "$2" diff --git a/distr/mktree b/distr/mktree index df22298a..56fbcffa 100644 --- a/distr/mktree +++ b/distr/mktree @@ -2,7 +2,7 @@ case $# in 1) ;; *) echo $0 directory ; exit 1 ;; esac -DDIR=/usr/em/distr +DDIR=/proj/em/distr case $1 in /*) DESTDIR=$1 ;; *) DESTDIR=`pwd`/$1 ;; diff --git a/distr/todistr b/distr/todistr index 968ec954..e1bfb5d6 100644 --- a/distr/todistr +++ b/distr/todistr @@ -23,4 +23,4 @@ esac case x$REV in x) exit 2 ;; esac -rcs -ndistr2:$REV $FLAGS $FILE +rcs -ndistr3:$REV $FLAGS $FILE diff --git a/doc/.distr b/doc/.distr new file mode 100644 index 00000000..6bd944ba --- /dev/null +++ b/doc/.distr @@ -0,0 +1,23 @@ +Makefile +ack.doc +basic.doc +cg.doc +crefman.doc +em +install.doc +install.pr +ncg.doc +pcref.doc +peep.doc +regadd.doc +toolkit.doc +v7bugs.doc +val.doc +LLgen +6500.doc +i80.doc +z80.doc +m68020.doc +top +ego +occam diff --git a/doc/6500.doc b/doc/6500.doc index aeef24a9..cdbcb990 100644 --- a/doc/6500.doc +++ b/doc/6500.doc @@ -1,6 +1,6 @@ . \" $Header$" -.po +10 -.ND +.RP +.ND Dec 1984 .TL .B A backend table for the 6500 microprocessor @@ -12,212 +12,6 @@ The backend table is part of the Amsterdam Compiler Kit (ACK). It translates the intermediate language family EM to a machine code for the MCS6500 microprocessor family. .AE -.PP -.bp -.NH -Introduction. -.PP -As more and more organizations aquire many micro and minicomputers, -the need for portable compilers is becoming more and more acute. -The present situation, in which each harware vendor provides its -own compilers -- each with its own deficiencies and extensions, and -none of them compatible -- leaves much to be desired. -The ideal situation would be an integrated system containing -a family of (cross) compilers, each compiler accepting a standard -source language and, producing code for a wide variety of target -machines. Furthermore, the compilers should be compatible, so programs -written in one language can call procedures written in another -language. Finally, the system should be designed so as to make -adding new languages and, new machines easy. Such an integerated -system is being built at the Vrije Universiteit. -.PP -The compiler building system, which is called the "Amsterdam Compiler -Kit" (ACK), can be thought of as a "tool kit." It consists of -a number of parts that can be combined to form compilers (and -interpreters) with various properties. The tool kit is based -on an idea (UNCOL) that was first suggested in 1960 [5], -but which never really caught on then. The problem which UNCOL -attemps to solve is how to make a compiler for each of -.B -N -.R -languages on -.B -M -.R -different machines without having to write -.B -N -.R -x -.B -M -.R -programs. -.PP -As shown in Fig. 1, the UNCOL approach is to write -.B -N -.R -"front ends," each of which translates -one source language to a common -intermediate language, UNCOL (UNiversal Computer Oriented -Language), and -.B -M -.R -"back ends," each of which translates programs -in UNCOL to a specific machine language. Under these conditions, -only -.B -N -.R -+ -.B -M -.R -programs must be written to provide all -.B -N -.R -languages on all -.B -M -.R -machines, instead of -.B -N -.R -x -.B -M -.R -programs. -.PP -Various reseachers have attempted to design a suitable UNCOL [1,6], -but none of these have become popular. It is the believe of the -designers of the Amsterdam Compiler Kit that previous attemps -have failed because they have been too ambitious, that is, they have -tried to cover all languages and all machines using a single UNCOL. -The approach of the designers is more modest: -they cater only to algebraic languages and machines whose memory -consist of 8-bit bytes, each with its own address. -Typical languages that could be handled include Ada, ALGOL 60, -ALGOL 68, BASIC, C, FORTRAN, Modula, Pascal, PL/I, PL/M, PLAIN and -RATFOR, where COBOL, LISP and SNOBOL would be less efficient. -Examples of machines that could be included are the Intel 8080 and -8086, Motorola 6800, 6809 and 68000, Zilog Z80 and Z8000, DEC PDP-11 -and Vax, MOS Technology MCS6500 family and IBM but not the Burroughs -6700, CDC Cyber or Univac 1108 (because they are not byte_oriented). -With these restrictions the designers believe that the old UNCOL -idea can be used as the basis of a practical compiler-building -system. -.sp 10 -.bp -.NH -An overview of the Amsterdam Compiler kit -.PP -The tool kit consists of eight components: -.IP 1. -The preprocessor. -.IP 2. -The front ends. -.IP 3. -The peephole optimizer. -.IP 4. -The global optimizer. -.IP 5. -The back end. -.IP 6. -The target machine optimizer. -.IP 7. -The universal assembler/linker. -.IP 8. -The utility package. -.PP -A fully optimizing compiler, depicted in Fig. 2, has seven cascaded -phases. Conceptually, each component reads an input file and writes -a transformed output file to be used as input to the next component. -In practice, some components may use temporary files to allow -multiple passes over the input or internal intermediate files. -.sp 20 -.PP -In the following paragraphs a brief decription of each component -is given. -A more detailed description of the back end will be given in the -rest of this document. For a more detailed descripiton on the rest -of the components see [7]. A program to be compiled is first fed -into the (language independed) preprocessor, which provides a -simple macro facility and similar textual facilities. -The preprocessor's ouput is a legal program in one of the programming -languages supported, whereas the input is a program possibly -augmented with macro's, etc. -.PP -This output goes into the appropriate front end, whose job it is to -produce intermediate cade. -This intermediate code (the UNCOL of ACK) is the machine language -for a simple stack machine EM (Encoding Machine). -A typical front end might build a parse tree from the input -and then use the parse tree to generate EM cade, -which is similar to reverse Polish. -In order to perform this work, the front end has to maintain tables of declare -tables of declared variables, labels, etc., determine where -to place the data structures in memory and so on. -.PP -The EM code generated by the front end is fed into the peephole -optimizer, which scans it with a window of a view instructions, -replacing certain inefficient code sequences by better ones. -Such a search is important because EM contains instructions to -handle numerous important special cases efficiently -(e.g. incrementing a variable by 1). -It is our strategy to relieve the front ends of the burden -of hunting for special cases because there are many front ends -and just one peephole optimizer. -By handeling the special cases in the peephole optimizer, -the front ends become simpler, easier to write and easier to maintain. -.PP -Following the peephole optimizer is a global optimizer [2], -which unlike the peephole optimizer, examines the program as a whole. -It builts a data flow graph to make possible a variety of global -optimizations, among them, moving invariant code out of loops, -avoiding redundant computations, live/dead analysis and -eliminating tail recursion. -Note that the output of the global optimizer is still EM code. -.PP -Next comes the back end, which differs from the front ends in a -fundamental way. -Each front end is a separate program, whereas the back end is a -single program that is driven by a machine dependent driving table. -The driving table for a specific machine tells how EM code is -mapped onto the machine's assembly language. -Although a simple driving table just might macro expand each -EM instruction into a sequence of target machine instructions, -a much more sophisticated translation strategy is normaly used, -as described later. -For speech, the back end does not actually read in the driving -table at run time. -Instead, the tables are compiled along with the back end in advance, -resulting in one binairy program per machine. -.PP -The output of the back end is a program in the assembly language -of some particular machine. -The next component in the pipeline reads this program and performs -peephole optimization on it. -The optimizations performed here involve idiosyncrasies of the -target machine that cannot be performed by the machine-independent -EM-to-EM peephole optimizer. -Typically these optimizations take advantage of the special -instructions or special addressing modes. -.PP -The optimized target machine assembly code then goes into the final -component in the pipeline, the universal assembler/linker. -This program assembles the input to object format, extracting -routines from libraries and including them as needed. -.PP -The final component of the tool kit is the utility package, -which contains various test programs, interpreters for EM code, -EM libraries, conversion programs and other aids for the -implementer and user. .bp .DS C .B @@ -264,7 +58,7 @@ manufactured by Acorn Computer Ltd.. The MOS Technology MCS6500 .PP The MCS6500 is as a family of CPU devices developed by MOS -Technology. +Technology [1]. The members of the MCS6500 family are the same chips in a different housing. The MCS6502, the big brother in the family, can handle 64k @@ -861,7 +655,7 @@ The above description of the machine table is a description of the table for the MCS6500. It uses only a part of the possibilities which the code generator generator offers. -For a more precise and detailed description see [4]. +For a more precise and detailed description see [2]. .DS C .B THE BACK END TABLE. @@ -1141,7 +935,7 @@ This subroutine expects the multiplicand in zero page at locations ARTH, ARTH+1, while the multiplier is in zero page locations ARTH+2, ARTH+3. For a description of the algorithms used for multiplication and -division, see [9]. +division, see [3]. A table content is: .sp 1 .br @@ -2071,34 +1865,6 @@ if it is to be used on a MCS6500. REFERENCES. .R .IP 1. -Haddon. B.K., and Waite, W.M. -Experience with the Universal Intermediate Language Janus. -.B -Software Practice & Experience 8 -.R -, -5 (Sept.-Oct. 1978), 601-616. -.RS -.PP -An intermediate language for use with Algol 68, Pascal, etc. -is described. -The paper discusses some problems encountered and how they were -dealt with. -.RE -.IP 2. -Lowry, E.S., and Medlock, C.W. Object Code Optimization. -.B -Commun. ACM 12 -.R -, -(Jan. 1969), 13-22. -.RS -.PP -A classical paper on global object code optimization. -It covers data flow analysis, common subexpressions, code motion, -register allocation and other techniques. -.RE -.IP 3. Osborn, A., Jacobson, S., and Kane, J. The Mos Technology MCS6500. .B An Introduction to Microcomputers , @@ -2109,7 +1875,7 @@ Volume II, Some Real Products (june 1977) chap. 9. A hardware description of some real existing CPU's, such as the Intel Z80, MCS6500, etc. is given in this book. .RE -.IP 4. +.IP 2. van Staveren, H. The table driven code generator from the Amsterdam Compiler Kit. Vrije Universiteit, Amsterdam, (July 11, 1983). @@ -2117,43 +1883,7 @@ Vrije Universiteit, Amsterdam, (July 11, 1983). .PP The defining document for writing a back end table. .RE -.IP 5. -Steel, T.B., Jr. UNCOL: The Myth and the Fact. in -.B -Ann. Rev. Auto. Prog. -.R -Goodman, R. (ed.), vol 2., (1960), 325-344. -.RS -.PP -An introduction to the UNCOL idea by its originator. -.RE -.IP 6. -Steel. T.B., Jr. A first Version of UNCOL. -.B -Proc. Western Joint Comp. Conf. -.R -, -(1961), 371-377. -.IP 7. -Tanenbaum, A.S., Stevenson, J.W., Keizer, E.G., and van Staveren, -H. -A Practical Tool Kit for Making Portable Compilers. -Informatica Rapport 74, Vrije Universiteit, Amsterdam, 1983. -.RS -.PP -An overview on the Amsterdam Compiler Kit. -.RE -.IP 8. -Tanenbaum, A.S., Stevenson, J.W., Keizer, E.G., and van Staveren, -H. -Description of an Experimental Machine Architecture for use with -Block Structured Languages. -Informatica Rapport 81, Vrije Universiteit, Amsterdam, 1983. -.RS -.PP -The defining document for EM. -.RE -.IP 9. +.IP 3. Tanenbaum, A.S. Structured Computer Organization. Prentice Hall. (1976). .RS diff --git a/doc/LLgen/.distr b/doc/LLgen/.distr new file mode 100644 index 00000000..7e288211 --- /dev/null +++ b/doc/LLgen/.distr @@ -0,0 +1,3 @@ +LLgen.n +LLgen.refs +Makefile diff --git a/doc/LLgen/LLgen.n b/doc/LLgen/LLgen.n new file mode 100644 index 00000000..c1550d8a --- /dev/null +++ b/doc/LLgen/LLgen.n @@ -0,0 +1,1046 @@ +.\" $Header$ +.\" Run this paper off with +.\" refer [options] -p LLgen.refs LLgen.doc | [n]eqn | tbl | (nt)roff -ms +.if '\*(>.'' \{\ +. if '\*(<.'' \{\ +. if n .ds >. . +. if n .ds >, , +. if t .ds <. . +. if t .ds <, ,\ +\}\ +\} +.cs 5 22u +.ND +.EQ +delim @@ +.EN +.TL +LLgen, an extended LL(1) parser generator +.AU +Ceriel J. H. Jacobs +.AI +Dept. of Mathematics and Computer Science +Vrije Universiteit +Amsterdam, The Netherlands +.AB +\fILLgen\fR provides a +tool for generating an efficient recursive descent parser +with no backtrack from +an Extended Context Free syntax. +The \fILLgen\fR +user specifies the syntax, together with code +describing actions associated with the parsing process. +\fILLgen\fR +turns this specification into a number of subroutines that handle the +parsing process. +.PP +The grammar may be ambiguous. +\fILLgen\fR contains both static and dynamic facilities +to resolve these ambiguities. +.PP +The specification can be split into several files, for each of +which \fILLgen\fR generates an output file containing the +corresponding part of the parser. +Furthermore, only output files that differ from their previous +version are updated. +Other output files are not affected in any +way. +This allows the user to recompile only those output files that have +changed. +.PP +The subroutine produced by \fILLgen\fR calls a user supplied routine +that must return the next token. This way, the input to the +parser can be split into single characters or higher level +tokens. +.PP +An error recovery mechanism is generated almost completely +automatically. +It is based on so called \fBdefault choices\fR, which are +implicitly or explicitly specified by the user. +.PP +\fILLgen\fR has succesfully been used to create recognizers for +Pascal and C. +.AE +.NH +Introduction +.PP +\fILLgen\fR +provides a tool for generating an efficient recursive +descent parser with no backtrack from an Extended Context Free +syntax. +A parser generated by +\fILLgen\fR +will be called +\fILLparse\fR +for the rest of this document. +It is assumed that the reader has some knowledge of LL(1) grammars and +recursive descent parsers. +For a survey on the subject, see reference +.[ ( +griffiths +.]). +.PP +Extended LL(1) parsers are an extension of LL(1) parsers. They are +derived from an Extended Context-Free (ECF) syntax instead of a Context-Free +(CF) syntax. +ECF syntax is described in section 2. +Section 3 provides an outline of a +specification as accepted by +\fILLgen\fR and also discusses the lexical conventions of +grammar specification files. +Section 4 provides a description of the way the +\fILLgen\fR +user can associate +actions with the syntax. These actions must be written in the programming +language C, +.[ +kernighan ritchie +.] +which also is the target language of \fILLgen\fR. +The error recovery technique is discussed in section 5. +This section also discusses what the user can do about it. +Section 6 discusses +the facilities \fILLgen\fR offers +to resolve ambiguities and conflicts. +\fILLgen\fR offers facilities to resolve them both at parser +generation time and during the execution of \fILLparse\fR. +Section 7 discusses the +\fILLgen\fR +working environment. +It also discusses the lexical analyzer that must be supplied by the +user. +This lexical analyzer must read the input stream and break it +up into basic input items, called \fBtokens\fR for the rest of +this document. +Appendix A gives a summary of the +\fILLgen\fR +input syntax. +Appendix B gives an example. +It is very instructive to compare this example with the one +given in reference +.[ ( +yacc +.]). +It demonstrates the struggle \fILLparse\fR and other LL(1) +parsers have with expressions. +Appendix C gives an example of the \fILLgen\fR features +allowing the user to recompile only those output files that +have changed, using the \fImake\fR program. +.[ +make +.] +.NH +The Extended Context-Free Syntax +.PP +The extensions of an ECF syntax with respect to an ordinary CF syntax are: +.IP 1. 10 +An ECF syntax contains the repetition operator: "N" (N represents a positive +integer). +.IP 2. 10 +An ECF syntax contains the closure set operator without and with +upperbound: "*" and "*N". +.IP 3. 10 +An ECF syntax contains the positive closure set operator without and with +upperbound: "+" and "+N". +.IP 4. 10 +An ECF syntax contains the optional operator: "?", which is a +shorthand for "*1". +.IP 5. 10 +An ECF syntax contains parentheses "[" and "]" which can be +used for grouping. +.PP +We can describe the syntax of an ECF syntax with an ECF syntax : +.DS +.ft 5 +grammar : rule + + ; +.ft R +.DE +This grammar rule states that a grammar consists of one or more +rules. +.DS +.ft 5 +rule : nonterminal ':' productionrule ';' + ; +.ft R +.DE +A rule consists of a left hand side, the nonterminal, +followed by ":", +the \fBproduce symbol\fR, followed by a production rule, followed by a +";", in\%di\%ca\%ting the end of the rule. +.DS +.ft 5 +productionrule : production [ '|' production ]* + ; +.ft R +.DE +A production rule consists of one or +more alternative productions separated by "|". This symbol is called the +\fBalternation symbol\fR. +.DS +.ft 5 +production : term * + ; +.ft R +.DE +A production consists of a possibly empty list of terms. +So, empty productions are allowed. +.DS +.ft 5 +term : element repeats + ; +.ft R +.DE +A term is an element, possibly with a repeat specification. +.DS +.ft 5 +element : LITERAL + | IDENTIFIER + | '[' productionrule ']' + ; +.ft R +.DE +An element can be a LITERAL, which basically is a single character +between apostrophes, it can be an IDENTIFIER, which is either a +nonterminal or a token, and it can be a production rule +between square parentheses. +.DS +.ft 5 +repeats : '?' + | [ '*' | '+' ] NUMBER ? + | NUMBER ? + ; +.ft R +.DE +These are the repeat specifications discussed above. Notice that +this specification may be empty. +.PP +The class of ECF languages +is identical with the class of CF languages. However, in many +cases recursive definitions of language features can now be +replaced by iterative ones. This tends to reduce the number of +nonterminals and gives rise to very efficient recursive descent +parsers. +.NH +Grammar Specifications +.PP +The major part of a +\fILLgen\fR +grammar specification consists of an +ECF syntax specification. +Names in this syntax specification refer to either tokens or nonterminal +symbols. +\fILLgen\fR +requires token names to be declared as such. This way it +can be avoided that a typing error in a nonterminal name causes it to +be accepted as a token name. The token declarations will be +discussed later. +A name will be regarded as a nonterminal symbol, unless it is declared +as a token name. +If there is no production rule for a nonterminal symbol, \fILLgen\fR +will complain. +.PP +A grammar specification may also include some C routines, +for instance the lexical analyzer and an error reporting +routine. +Thus, a grammar specification file can contain declarations, +grammar rules and C-code. +.PP +Blanks, tabs and newlines are ignored, but may not appear in names or +keywords. +Comments may appear wherever a name is legal (which is almost +everywhere). +They are enclosed in +/* ... */, as in C. Comments do not nest. +.PP +Names may be of arbitrary length, and can be made up of letters, underscore +"\_" and non-initial digits. Upper and lower case letters are distinct. +Only the first 50 characters are significant. +Notice however, that the names for the tokens will be used by the +C-preprocessor. +The number of significant characters therefore depends on the +underlying C-implementation. +A safe rule is to make the identifiers distinct in the first six +characters, case ignored. +.PP +There are two kinds of tokens: +those that are declared and are denoted by a name, +and literals. +.PP +A literal consists of a character enclosed in apostrophes "'". +The "\e" is an escape character within literals. The following escapes +are recognized : +.TS +center; +l l. +\&'\en' newline +\&'\er' return +\&'\e'' apostrophe "'" +\&'\e\e' backslash "\e" +\&'\et' tab +\&'\eb' backspace +\&'\ef' form feed +\&'\exxx' "xxx" in octal +.TE +.PP +Names representing tokens must be declared before they are used. +This can be done using the "\fB%token\fR" keyword, +by writing +.nf +.ft 5 +.sp 1 +%token name1, name2, . . . ; +.ft R +.fi +.PP +\fILLparse\fR is designed to recognize special nonterminal +symbols called \fBstart symbols\fR. +\fILLgen\fR allows for more than one start symbol. +Thus, grammars with more than one entry point are accepted. +The start symbols must be declared explicitly using the +"\fB%start\fR" keyword. It can be used whenever a declaration is +legal, f.i.: +.nf +.ft 5 +.sp 1 +%start LLparse, specification ; +.ft R +.fi +.sp 1 +declares "specification" as a start symbol and associates the +identifier "LLparse" with it. +"LLparse" will now be the name of the C-function that must be +called to recognize "specification". +.NH +Actions +.PP +\fILLgen\fR +allows arbitrary insertions of actions within the right hand side +of a production rule in the ECF syntax. An action consists of a number of C +statements, enclosed in the brackets "{" and "}". +.PP +\fILLgen\fR +generates a parsing routine for each rule in the grammar. The actions +supplied by the user are just inserted in the proper place. +There may also be declarations before the statements in the +action, as +the "{" and "}" are copied into the target code along with the +action. The scope of these declarations terminates with the +closing bracket "}" of the action. +.PP +In addition to actions, it is also possible to declare local variables +in the parsing routine, which can then be used in the actions. +Such a declaration consists of a number of C variable declarations, +enclosed in the brackets "{" and "}". It must be placed +right in front of the ":" in the grammar rule. +The scope of these local variables consists of the complete +grammar rule. +.PP +In order to facilitate communication between the actions and +\fILLparse\fR, +the parsing routines can be given C-like parameters. So, for example +.nf +.ft 5 +.sp 1 +expr(int *pval;) { int fact; } : + /* + * Rule with one parameter, a pointer to an int. + * Parameter specifications are ordinary C declarations. + * One local variable, of type int. + */ + factor (&fact) { *pval = fact; } + /* + * factor is another nonterminal symbol. + * One actual parameter is supplied. + * Notice that the parameter passing mechanism is that + * of C. + */ + [ '+' factor (&fact) { *pval += fact; } ]* + /* + * remember the '*' means zero or more times + */ + ; +.sp 1 +.ft R +.fi +is a rule to recognize a number of factors, separated by "+", and +to compute their sum. +.PP +\fILLgen\fR +generates C code, so the parameter passing mechanism is that of +C, as is shown in the example above. +.PP +Actions often manipulate attributes of the token just read. +For instance, when an identifier is read, its name must be +looked up in a symbol table. +Therefore, \fILLgen\fR generates code +such that at a number of places in the grammar rule +it is defined which token has last been read. +After a token, the last token read is this token. +After a "[" or a "|", the last token read is the next token to +be accepted by \fILLparse\fR. +At all other places, it is undefined which token has last been +read. +The last token read is available in the global integer variable +\fILLsymb\fR. +.PP +The user may also specify C-code wherever a \fILLgen\fR-declaration is +legal. +Again, this code must be enclosed in the brackets "{" and "}". +This way, the user can define global declarations and +C-functions. +To avoid name-conflicts with identifiers generated by +\fILLgen\fR, \fILLparse\fR only uses names beginning with +"LL"; the user should avoid such names. +.NH +Error Recovery +.PP +The error recovery technique used by \fILLgen\fR is a +modification of the one presented in reference +.[ ( +automatic construction error correcting +.]). +It is based on \fBdefault choices\fR, which just are +what the word says, default choices at +every point in the grammar where there is a +choice. +Thus, in an alternation, one of the productions is marked as a +default choice, and in a term with a non-fixed repetition +specification there will also be a default choice (between +doing the term (once more) and continuing with the rest of the +production in which the term appears). +.PP +When \fILLparse\fR detects an error after having parsed the +string @s@, the default choices enable it to compute one +syntactically correct continuation, +consisting of the tokens @t sub 1~...~t sub n@, +such that @s~t sub 1~...~t sub n@ is a string of tokens that +is a member of the language defined by the grammar. +Notice, that the computation of this continuation must +terminate, which implies that the default choices may not +invoke recursive rules. +.PP +At each point in this continuation, a certain number of other +tokens could also be syntactically correct, f.i. the token +@t@ is syntactically correct at point @t sub i@ in this +continuation, if the string @s~t sub 1~...~t sub i~t~s sub 1@ +is a string of the language defined by the grammar for some +string @s sub 1@ and i >= 0. +.PP +The set @T@ +containing all these tokens (including @t sub 1 ,~...,~t sub n@) is computed. +Next, \fILLparse\fR discards zero +or more tokens from its input, until a token +@t@ \(mo @T@ is found. +The error is then corrected by inserting i (i >= 0) tokens +@t sub 1~...~t sub i@, such that the string +@s~t sub 1~...~t sub i~t~s sub 1@ is a string of the language +defined by the grammar, for some @s sub 1@. +Then, normal parsing is resumed. +.PP +The above is difficult to implement in a recursive decent +parser, and is not the way \fILLparse\fR does it, but the +effect is the same. In fact, \fILLparse\fR maintains a list +of tokens that may not be discarded, which is adjusted as +\fILLparse\fR proceeds. This list is just a representation +of the set @T@ mentioned +above. When an error occurs, \fILLparse\fR discards tokens until +a token @t@ that is a member of this list is found. +Then, it continues parsing, following the default choices, +inserting tokens along the way, until this token @t@ is legal. +The selection of +the default choices must guarantee that this will always +happen. +.PP +The default choices are explicitly or implicitly +specified by the user. +By default, the default choice in an alternation is the +alternative with the shortest possible terminal production. +The user can select one of the other productions in the +alternation as the default choice by putting the keyword +"\fB%default\fR" in front of it. +.PP +By default, for terms with a repetition count containing "*" or +"?" the default choice is to continue with the rest of the rule +in which the term appears, and +.sp 1 +.ft 5 +.nf + term+ +.fi +.ft R +.sp 1 +is treated as +.sp 1 +.nf +.ft 5 + term term* . +.ft R +.fi +.PP +It is also clear, that it can never be the default choice to do +the term (once more), because this could cause the parser to +loop, inserting tokens forever. +However, when the user does not want the parser to skip +tokens that would not have been skipped if the term +would have been the default choice, +the skipping of such a term can be prevented by +using the keyword "\fB%persistent\fR". +For instance, the rule +.sp 1 +.ft 5 +.nf +commandlist : command* ; +.fi +.ft R +.sp 1 +could be changed to +.sp 1 +.ft 5 +.nf +commandlist : [ %persistent command ]* ; +.fi +.ft R +.sp 1 +The effects of this in case of a syntax error are twofold: +The set @T@ mentioned above will be extended as if "command" were +in the default production, so that fewer tokens will be +skipped. +Also, if the first token that is not skipped is a member of the +subset of @T@ arising from the grammar rule for "command", +\fILLparse\fR will enter that rule. +So, in fact the default choice +is determined dynamically (by \fILLparse\fR). +Again, \fILLgen\fR checks (statically) +that \fILLparse\fR will always terminate, and if not, +\fILLgen\fR will complain. +.PP +An important property of this error recovery method is that, +once a rule is started, it will be finished. +This means that all actions in the rule will be executed +normally, so that the user can be sure that there will be no +inconsistencies in his data structures because of syntax +errors. +Also, as the method is in fact error correcting, the +actions in a rule only have to deal with syntactically correct +input. +.NH +Ambiguities and conflicts +.PP +As \fILLgen\fR generates a recursive descent parser with no backtrack, +it must at all times be able to determine what to do, +based on the current input symbol. +Unfortunately, this cannot be done for all grammars. +Two kinds of conflicts can arise : +.IP 1) 10 +the grammar rule is of the form "production1 | production2", +and \fILLparse\fR cannot decide which production to chose. +This we call an \fBalternation conflict\fR. +.IP 2) 10 +the grammar rule is of the form "[ productionrule ]...", +where ... specifies a non-fixed repetition count, +and \fILLparse\fR cannot decide whether to +choose "productionrule" once more, or to continue. +This we call a \fBrepetition conflict\fR. +.PP +There can be several causes for conflicts: the grammar may be +ambiguous, or the grammar may require a more complex parser +than \fILLgen\fR can construct. +The conflicts can be examined by inspecting the verbose +(-\fBv\fR) option output file. +The conflicts can be resolved by rewriting the grammar +or by using \fBconflict resolvers\fR. +The mechanism described here is based on the attributed parsing +of reference +.[ ( +milton +.]). +.PP +An alternation conflict can be resolved by putting an \fBif condition\fR +in front of the first conflicting production. +It consists of a "\fB%if\fR" followed by a +C-expression between parentheses. +\fILLparse\fR will then evaluate this expression whenever a +token is met at this point on which there is a conflict, so +the conflict will be resolved dynamically. +If the expression evaluates to +non-zero, the first conflicting production is chosen, +otherwise one of the remaining ones is chosen. +.PP +An alternation conflict can also be resolved using the keywords +"\fB%prefer\fR" or "\fB%avoid\fR". "\fB%prefer\fR" +is equivalent in behaviour to +"\fB%if\fR (1)". "\fB%avoid\fR" is equivalent to "\fB%if\fR (0)". +In these cases however, "\fB%prefer\fR" and "\fB%avoid\fR" should be used, +as they resolve the conflict statically and thus +give rise to better C-code. +.PP +A repetition conflict can be resolved by putting a \fBwhile condition\fR +right after the opening parentheses. This while condition +consists of a "\fB%while\fR" followed by a C-expression between +parentheses. Again, \fILLparse\fR will then +evaluate this expression whenever a token is met +at this point on which there is a conflict. +If the expression evaluates to non-zero, the +repeating part is chosen, otherwise the parser continues with +the rest of the rule. +Appendix B will give an example of these features. +.PP +A useful aid in writing conflict resolvers is the "\fB%first\fR" keyword. +It is used to declare a C-macro that forms an expression +returning 1 if the parameter supplied can start a specified +nonterminal, f.i.: +.sp 1 +.nf +.ft 5 +%first fmac, nonterm ; +.ft R +.sp 1 +.fi +declares "fmac" as a macro with one parameter, whose value +is a token number. If the parameter +X can start the nonterminal "nonterm", "fmac(X)" is true, +otherwise it is false. +.NH +The LLgen working environment +.PP +\fILLgen\fR generates a number of files: one for each input +file, and two other files: \fILpars.c\fR and \fILpars.h\fR. +\fILpars.h\fR contains "#-define"s for the tokennames. +\fILpars.c\fR contains the error recovery routines and tables. +Only those output files that differ from their previous version +are updated. See appendix C for a possible application of this +feature. +.PP +The names of the output files are constructed as +follows: +in the input file name, the suffix after the last point is +replaced by a "c". If no point is present in the input file +name, ".c" is appended to it. \fILLgen\fR checks that the +filename constructed this way in fact represents a previous +version, or does not exist already. +.PP +The user must provide some environment to obtain a complete +program. +Routines called \fImain\fR and \fILLmessage\fR must be defined. +Also, a lexical analyzer must be provided. +.PP +The routine \fImain\fR must be defined, as it must be in every +C-program. It should eventually call one of the startsymbol +routines. +.PP +The routine \fILLmessage\fR must accept one +parameter, whose value is a token number, zero or -1. +.br +A zero parameter indicates that the current token (the one in +the external variable \fILLsymb\fR) is deleted. +.br +A -1 parameter indicates that the parser expected end of file, but did'nt get +it. +The parser will then skip tokens until end of file is detected. +.br +A parameter that is a token number (a positive parameter) +indicates that this +token is to be inserted in front of the token currently in +\fILLsymb\fR. +The user can give the token the proper attributes. +Also, the user must take care, that the token currently in +\fILLsymb\fR is again returned by the \fBnext\fR call to the +lexical analyzer, with the proper attributes. +So, the lexical analyzer must have a facility to push back one +token. +.PP +The user may also supply his own error recovery routines, or handle +errors differently. For this purpose, the name of a routine to be called +when an error occurs may be declared using the keyword \fB%onerror\fR. +This routine takes two parameters. +The first one is either the token number of the +token expected, or 0. In the last case, the error occurred at a choice. +In both cases, the routine must ensure that the next call to the lexical +analyser returns the token that replaces the current one. Of course, +that could well be the current one, in which case +.I LLparse +recovers from the error. +The second parameter contains a list of tokens that are not skipped at the +error point. The list is in the form of a null-terminated array of integers, +whose address is passed. +.PP +The user must supply a lexical analyzer to read the input stream and +break it up into tokens, which are passed to +.I LLparse. +It should be an integer valued function, returning the token number. +The name of this function can be declared using the +"\fB%lexical\fR" keyword. +This keyword can be used wherever a declaration is legal and may appear +only once in the grammar specification, f.i.: +.sp 1 +.nf +.ft 5 +%lexical scanner ; +.ft R +.fi +.sp 1 +declares "scanner" as the name of the lexical analyzer. +The default name for the lexical analyzer is "yylex". +The reason for this funny name is that a useful tool for constructing +lexical analyzers is the +.I Lex +program, +.[ +lex +.] +which generates a routine of that name. +.PP +The token numbers are chosen by \fILLgen\fR. +The token number for a literal +is the numerical value of the character in the local character set. +If the tokens have a name, +the "#\ define" mechanism of C is used to give them a value and +to allow the lexical analyzer to return their token numbers symbolically. +These "#\ define"s are collected in the file \fILpars.h\fR which +can be "#\ include"d in any file that needs the token-names. +.PP +The lexical analyzer must signal the end +of input to \fILLparse\fR +by returning a number less than or equal to zero. +.bp +.SH +References +.[ +$LIST$ +.] +.bp +.SH +Appendix A : LLgen Input Syntax +.PP +This appendix has a description of the \fILLgen\fR input syntax, +as a \fILLgen\fR specification. As a matter of fact, the current +version of \fILLgen\fR is written with \fILLgen\fR. +.nf +.ft 5 +.sp 2 +/* + * First the declarations of the terminals + * The order is not important + */ + +%token IDENTIFIER; /* terminal or nonterminal name */ +%token NUMBER; +%token LITERAL; + +/* + * Reserved words + */ + +%token TOKEN; /* %token */ +%token START; /* %start */ +%token PERSISTENT; /* %persistent */ +%token IF; /* %if */ +%token WHILE; /* %while */ +%token AVOID; /* %avoid */ +%token PREFER; /* %prefer */ +%token DEFAULT; /* %default */ +%token LEXICAL; /* %lexical */ +%token ONERROR; /* %onerror */ +%token FIRST; /* %first */ + +/* + * Declare LLparse to be a C-routine that recognizes "specification" + */ + +%start LLparse, specification; + +specification + : declaration* + ; + +declaration + : START + IDENTIFIER ',' IDENTIFIER + ';' + | '{' + /* Read C-declaration here */ + '}' + | TOKEN + IDENTIFIER + [ ',' IDENTIFIER ]* + ';' + | FIRST + IDENTIFIER ',' IDENTIFIER + ';' + | LEXICAL + IDENTIFIER + ';' + | ONERROR + IDENTIFIER + ';' + | rule + ; + +rule : IDENTIFIER parameters? ldecl? + ':' productions + ';' + ; + +ldecl : '{' + /* Read C-declaration here */ + '}' + ; + +productions + : simpleproduction + [ '|' DEFAULT? simpleproduction ]* + ; + +simpleproduction + : [ IF '(' /* Read C-expression here */ ')' + | PREFER + | AVOID + ]? + [ element repeats ]* + ; + +element : '{' + /* Read action here */ + '}' + | '[' [ WHILE '(' /* Read C-expression here */ ')' ]? + PERSISTENT? + productions + ']' + | LITERAL + | IDENTIFIER parameters? + ; + +parameters + : '(' /* Read C-parameters here */ ')' + ; + +repeats : /* empty */ + | [ '*' | '+' ] NUMBER? + | NUMBER + | '?' + ; + +.fi +.ft R +.bp +.SH +Appendix B : An example +.PP +This example gives the complete \fILLgen\fR specification of a simple +desk calculator. It has 26 registers, labeled "a" through "z", +and accepts arithmetic expressions made up of the C operators ++, -, *, /, %, &, and |, with their usual priorities. +The value of the expression is +printed. As in C, an integer that begins with 0 is assumed to +be octal; otherwise it is assumed to be decimal. +.PP +Although the example is short and not very complicated, it +demonstrates the use of if and while conditions. In +the example they are in fact used to reduce the number of +nonterminals, and to reduce the overhead due to the recursion +that would be involved in parsing an expression with an +ordinary recursive descent parser. In an ordinary LL(1) +grammar there would be one nonterminal for each operator +priority. The example shows how we can do it all with one +nonterminal, no matter how many priority levels there are. +.sp 1 +.nf +.ft 5 +{ +#include +#include +#define MAXPRIO 5 +#define prio(op) (ptab[op]) + +struct token { + int t_tokno; /* token number */ + int t_tval; /* Its attribute */ +} stok = { 0,0 }, tok; + +int nerrors = 0; +int regs[26]; /* Space for the registers */ +int ptab[128]; /* Attribute table */ + +struct token +nexttok() { /* Read next token and return it */ + register c; + struct token new; + + while ((c = getchar()) == ' ' || c == '\et') { /* nothing */ } + if (isdigit(c)) new.t_tokno = DIGIT; + else if (islower(c)) new.t_tokno = IDENT; + else new.t_tokno = c; + if (c >= 0) new.t_tval = ptab[c]; + return new; +} } + +%token DIGIT, IDENT; +%start parse, list; + +list : stat* ; + +stat { int ident, val; } : + %if (stok = nexttok(), + stok.t_tokno == '=') + /* The conflict is resolved by looking one further + * token ahead. The grammar is LL(2) + */ + IDENT + { ident = tok.t_tval; } + '=' expr(1,&val) '\en' + { if (!nerrors) regs[ident] = val; } + | expr(1,&val) '\en' + { if (!nerrors) printf("%d\en",val); } + | '\en' + ; + +expr(int level, *val;) { int expr; } : + %if (level <= MAXPRIO) + /* The grammar is ambiguous here. If level > MAXPRIO, + * this invocation will only scan one factor + */ + expr(MAXPRIO+1,val) + [ %while (prio(tok.t_tokno) >= level) + /* Swallow operators as long as their priority is + * larger than or equal to the level of this invocation + */ + '+' expr(prio('+')+1,&expr) + { *val += expr; } + /* This states that '+' groups left to right. If it + * should group right to left, the rule should read: + * '+' expr(prio('+'),&expr) + */ + | '-' expr(prio('-'),&expr) + { *val -= expr; } + | '*' expr(prio('*'),&expr) + { *val *= expr; } + | '/' expr(prio('/'),&expr) + { *val /= expr; } + | '%' expr(prio('%'),&expr) + { *val %= expr; } + | '&' expr(prio('&'),&expr) + { *val &= expr; } + | '|' expr(prio('|'),&expr) + { *val |= expr; } + ]* + /* Notice the "*" here. It is important. + */ + | '(' expr(1,val) ')' + | '-' expr(MAXPRIO+1,val) + { *val = -*val; } + | number(val) + | IDENT + { *val = regs[tok.t_tval]; } + ; + +number(int *val;) { int base; } + : DIGIT + { base = (*val=tok.t_tval)==0?8:10; } + [ DIGIT + { *val = base * *val + tok.t_tval; } + ]* ; + +%lexical scanner ; +{ +scanner() { + if (stok.t_tokno) { /* a token has been inserted or read ahead */ + tok = stok; + stok.t_tokno = 0; + return tok.t_tokno; + } + if (nerrors && tok.t_tokno == '\en') { + printf("ERROR\en"); + nerrors = 0; + } + tok = nexttok(); + return tok.t_tokno; +} + +LLmessage(insertedtok) { + nerrors++; + if (insertedtok) { /* token inserted, save old token */ + stok = tok; + tok.t_tval = 0; + if (insertedtok < 128) tok.t_tval = ptab[insertedtok]; + } +} + +main() { + register *p; + + for (p = ptab; p < &ptab[128]; p++) *p = 0; + /* for letters, their attribute is their index in the regs array */ + for (p = &ptab['a']; p <= &ptab['z']; p++) *p = p - &ptab['a']; + /* for digits, their attribute is their value */ + for (p = &ptab['0']; p <= &ptab['9']; p++) *p = p - &ptab['0']; + /* for operators, their attribute is their priority */ + ptab['*'] = 4; + ptab['/'] = 4; + ptab['%'] = 4; + ptab['+'] = 3; + ptab['-'] = 3; + ptab['&'] = 2; + ptab['|'] = 1; + return parse(); +} } +.fi +.ft R +.bp +.SH +Appendix C. How to use \fILLgen\fR. +.PP +This appendix demonstrates how \fILLgen\fR can be used in +combination with the \fImake\fR program, to make effective use +of the \fILLgen\fR-feature that it only changes output files +when neccessary. \fIMake\fR uses a "makefile", which +is a file containing dependencies and associated commands. +A dependency usually indicates that some files depend on other +files. When a file depends on another file and is older than +that other file, the commands associated with the dependency +are executed. +.PP +So, \fImake\fR seems just the program that we always wanted. +However, it +is not very good in handling programs that generate more than +one file. +As usual, there is a way around this problem. +A sample makefile follows: +.sp 1 +.ft 5 +.nf +# The grammar exists of the files decl.g, stat.g and expr.g. +# The ".o"-files are the result of a C-compilation. + +GFILES = decl.g stat.g expr.g +OFILES = decl.o stat.o expr.o Lpars.o +LLOPT = + +# As make does'nt handle programs that generate more than one +# file well, we just don't tell make about it. +# We just create a dummy file, and touch it whenever LLgen is +# executed. This way, the dummy in fact depends on the grammar +# files. +# Then, we execute make again, to do the C-compilations and +# such. + +all: dummy + make parser + +dummy: $(GFILES) + LLgen $(LLOPT) $(GFILES) + touch dummy + +parser: $(OFILES) + $(CC) -o parser $(LDFLAGS) $(OFILES) + +# Some dependencies without actions : +# make already knows what to do about them + +Lpars.o: Lpars.h +stat.o: Lpars.h +decl.o: Lpars.h +expr.o: Lpars.h + +.fi +.ft R diff --git a/doc/LLgen/LLgen.refs b/doc/LLgen/LLgen.refs new file mode 100644 index 00000000..102e8459 --- /dev/null +++ b/doc/LLgen/LLgen.refs @@ -0,0 +1,54 @@ +%T An ALL(1) Compiler Generator +%A D. R. Milton +%A L. W. Kirchhoff +%A B. R. Rowland +%B Proc. of the SIGPLAN '79 Symposium on Compiler Construction +%D August 1979 +%J SIGPLAN Notices +%N 8 +%P 152-157 +%V 14 + +%T Lex - A Lexical Analyser Generator +%A M. E. Lesk +%I Bell Laboratories +%D October 1975 +%C Murrey Hill, New Jersey +%R Comp. Sci. Tech. Rep. No. 39 + +%T Yacc: Yet Another Compiler Compiler +%A S. C. Johnson +%I Bell Laboratories +%D 1975 +%C Murray Hill, New Jersey +%R Comp. Sci. Tech. Rep. No. 32 + +%T The C Programming Language +%A B. W. Kernighan +%A D. M. Ritchie +%I Prentice-Hall, Inc. +%C Englewood Cliffs, New Jersey +%D 1978 + +%A M. Griffiths +%T LL(1) Grammars and Analysers +%E F. L. Bauer and J. Eickel +%B Compiler Construction, An Advanced Course +%I Springer-Verlag +%C New York, N.Y. +%D 1974 + +%T Make - A Program for Maintaining Computer Programs +%A S. I. Feldman +%J Software - Practice and Experience +%V 10 +%N 8 +%P 255-265 +%D August 1979 + +%T Methods for the Automatic Construction of Error Correcting Parsers +%A J. R\*:ohrich +%J Acta Informatica +%V 13 +%P 115-139 +%D 1980 diff --git a/doc/LLgen/Makefile b/doc/LLgen/Makefile new file mode 100644 index 00000000..8aee6815 --- /dev/null +++ b/doc/LLgen/Makefile @@ -0,0 +1,8 @@ +# $Header$ + +EQN=eqn +REFER=refer +TBL=tbl + +../LLgen.doc: LLgen.n LLgen.refs + $(REFER) -sA+T -p LLgen.refs LLgen.n | $(EQN) | $(TBL) > $@ diff --git a/doc/Makefile b/doc/Makefile index 18989353..ab14cc96 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -2,52 +2,67 @@ SUF=pr PRINT=cat -RESFILES=cref.$(SUF) pcref.$(SUF) val.$(SUF) v7bugs.$(SUF) install.$(SUF)\ -ack.$(SUF) cg.$(SUF) regadd.$(SUF) peep.$(SUF) toolkit.$(SUF) LLgen.$(SUF)\ -basic.$(SUF) 6500.$(SUF) ncg.$(SUF) NROFF=nroff +TBL=tbl +EQN=eqn +PIC=pic +REFER=refer MS=-ms -cref.$(SUF): cref.doc - tbl $? | $(NROFF) >$@ -v7bugs.$(SUF): v7bugs.doc - $(NROFF) $(MS) $? >$@ -ack.$(SUF): ack.doc - $(NROFF) $(MS) $? >$@ -cg.$(SUF): cg.doc - $(NROFF) $(MS) $? >$@ -ncg.$(SUF): ncg.doc - $(NROFF) $(MS) $? >$@ -regadd.$(SUF): regadd.doc - $(NROFF) $(MS) $? >$@ -install.$(SUF): install.doc - $(NROFF) $(MS) $? >$@ -pcref.$(SUF): pcref.doc - $(NROFF) $? >$@ -basic.$(SUF): basic.doc - $(NROFF) $(MS) $? >$@ -peep.$(SUF): peep.doc - $(NROFF) $(MS) $? >$@ -val.$(SUF): val.doc - $(NROFF) $? >$@ -toolkit.$(SUF): toolkit.doc - $(NROFF) $(MS) $? >$@ -LLgen.$(SUF): LLgen.doc - eqn $? | $(NROFF) $(MS) >$@ +RESFILES= \ + toolkit.$(SUF) install.$(SUF) em.$(SUF) ack.$(SUF) v7bugs.$(SUF) \ + peep.$(SUF) cg.$(SUF) ncg.$(SUF) regadd.$(SUF) LLgen.$(SUF) \ + basic.$(SUF) crefman.$(SUF) pcref.$(SUF) val.$(SUF) \ + 6500.$(SUF) i80.$(SUF) z80.$(SUF) top.$(SUF) ego.$(SUF) \ + m68020.$(SUF) occam.$(SUF) nopt.$(SUF) + +.SUFFIXES: .doc .$(SUF) + +.doc.$(SUF): + $(NROFF) $(MS) $< > $@ + +crefman.$(SUF): crefman.doc + $(EQN) crefman.doc | $(NROFF) $(MS) >$@ +v7bugs.$(SUF): v7bugs.doc + $(NROFF) v7bugs.doc >$@ +install.$(SUF): install.doc + $(TBL) install.doc | $(NROFF) $(MS) >$@ +pcref.$(SUF): pcref.doc + $(NROFF) pcref.doc >$@ +val.$(SUF): val.doc + $(NROFF) val.doc >$@ 6500.$(SUF): 6500.doc - $(NROFF) $(MS) $? >$@ + $(TBL) 6500.doc | $(NROFF) $(MS) >$@ +LLgen.doc: LLgen.X +LLgen.X: + cd LLgen; make "EQN="$(EQN) "TBL="$(TBL) "REFER="$(REFER) +top.doc: top.X +top.X: + cd top; make "EQN="$(EQN) "TBL="$(TBL) "REFER="$(REFER) +occam.doc: occam.X +occam.X: + cd occam; make "PIC="$(PIC) "TBL="$(TBL) "EQN="$(EQN) +ego.doc: ego.X +ego.X: + cd ego; make "REFER="$(REFER) +em.$(SUF): em.X +em.X: + cd em; make "TBL="$(TBL) "NROFF="$(NROFF) "SUF="$(SUF) install cmp: distr: install.doc - nroff -Tlp install.doc >install.pr + tbl install.doc | nroff -Tlp $(MS) >install.pr + pr: - @make "SUF="$SUF "NROFF="$NROFF "PRINT="$PRINT $(RESFILES) \ - >make.pr.out 2>&1 + @make "SUF="$(SUF) "NROFF="$(NROFF) "EQN="$(EQN) "TBL="$(TBL) \ + "PIC="$(PIC) "MS="$(MS) \ + $(RESFILES) >make.pr.out 2>&1 @$(PRINT) $(RESFILES) opr: make pr | opr clean: - -rm -f *.old $(RESFILES) *.t + -rm -f *.old $(RESFILES) *.t *.out LLgen.doc top.doc \ + occam.doc ego.doc diff --git a/doc/ack.doc b/doc/ack.doc index 3067ac96..bfa7d281 100644 --- a/doc/ack.doc +++ b/doc/ack.doc @@ -1,7 +1,6 @@ .\" $Header$ -.nr LL 7.5i -.tr ~ .nr PD 1v +.tr ~ .TL Ack Description File .br @@ -9,7 +8,7 @@ Reference Manual .AU Ed Keizer .AI -Wiskundig Seminarium +Vakgroep Informatica Vrije Universiteit Amsterdam .NH @@ -24,16 +23,16 @@ source file. Each transformation table entry tells which input suffixes are allowed and what suffix/name the output file has. When the output file does not already satisfy the request of the -user, with the flag \fB-c.suffix\fP, the table is scanned +user, with the flag \fB\-c.suffix\fP, the table is scanned starting with the next transformation in the table for another transformation that has as input suffix the output suffix of the previous transformation. A few special transformations are recognized, among them is the -combiner. -A program combining several files into one. -When no stop suffix was specified (flag \fB-c.suffix\fP) \fIack\fP -stops after executing the combiner with as arguments the - -possibly transformed - input files and libraries. +combiner, which is +a program combining several files into one. +When no stop suffix was specified (flag \fB\-c.suffix\fP) \fIack\fP +stops after executing the combiner with as arguments the \- +possibly transformed \- input files and libraries. \fIAck\fP will only perform the transformations in the order in which they are presented in the table. .LP @@ -60,7 +59,7 @@ convoluted. First, when the last filename in the program call name is not one of \fIack\fP, \fIcc\fP, \fIacc\fP, \fIpc\fP or \fIapc\fP, this filename is used as the backend description name. -Second, when the \fB-m\fP is present the \fB-m\fP is chopped of this +Second, when the \fB\-m\fP is present the \fB\-m\fP is chopped of this flag and the rest is used as the backend description name. Third, when both failed the shell environment variable ACKM is used. @@ -75,7 +74,8 @@ This descriptions are simply files read in at compile time. At the moment of writing this document, the descriptions included are: pdp, fe, i86, m68k2, vax2 and int. The name of a description is first searched for internally, -then in the directory lib/ack and finally in the current +then in lib/descr/\fIname\fP, then in +lib/\fIname\fP/descr, band finally in the current directory of the user. .NH Using the description file @@ -119,8 +119,8 @@ Syntax: (\fIsuffix sequence\fP:\fIsuffix sequence\fP=\fItext\fP) .br Example: (.c.p.e:.e=tail_em) .br -If the two suffix sequences have a common member -~\&.e in this -case~- the text is produced. +If the two suffix sequences have a common member \-~\&.e in this +case~\- the text is produced. When no common member is present the empty string is produced. Thus the example given is a constant expression. Normally, one of the suffix sequences is produced by variable @@ -134,17 +134,17 @@ the text following the \fIneed\fP is appended to both the HEAD and TAIL variable. The value of the variable RTS is determined by the first transformation used with a \fIrts\fP property. -.LP +.IP Two runtime flags have effect on the value of one or more of these variables. -The flag \fB-.suffix\fP has the same effect on these three variables +The flag \fB\-.suffix\fP has the same effect on these three variables as if a file with that \fBsuffix\fP was included in the argument list and had to be translated. -The flag \fB-r.suffix\fP only has that effect on the TAIL +The flag \fB\-r.suffix\fP only has that effect on the TAIL variable. The program call names \fIacc\fP and \fIcc\fP have the effect -of an automatic \fB-.c\fB flag. -\fIApc\fP and \fIpc\fP have the effect of an automatic \fB-.p\fP flag. +of an automatic \fB\-.c\fP flag. +\fIApc\fP and \fIpc\fP have the effect of an automatic \fB\-.p\fP flag. .IP "Line splitting" .br The string is transformed into a sequence of strings by replacing @@ -168,7 +168,7 @@ of the line. Three special two-characters sequences exist: \e#, \e\e and \e. Their effect is described under 'backslashing' above. -Each - nonempty - line starts with a keyword, possibly +Each \- nonempty \- line starts with a keyword, possibly preceded by blank space. The keyword can be followed by a further specification. The two are separated by blank space. @@ -193,7 +193,7 @@ The lines in between associate properties to a transformation and may be presented in any order. The identifier after the \fIname\fP keyword determines the name of the transformation. -This name is used for debugging and by the \fB-R\fP flag. +This name is used for debugging and by the \fB\-R\fP flag. The keywords are used to specify which input suffices are recognized by that transformation, the program to run, the arguments to be handed to that program @@ -205,14 +205,13 @@ The possible keywords are: .br followed by a sequence of suffices. Each file with one of these suffices is allowed as input file. -Preprocessor transformations, those with the \fBP\fP property -after the \fIprop\fP keyword, do not need the \fIfrom\fP +Preprocessor transformations do not need the \fIfrom\fP keyword. All other transformations do. .nr PD 0 .IP \fIto\fP .br followed by the suffix of the output file name or in the case of a -linker -~indicated by C option after the \fIprop\fP keyword~- +linker the output file name. .IP \fIprogram\fP .br @@ -235,9 +234,9 @@ assignment separated by blank space. As soon as both description files are read, \fIack\fP looks at all transformations in these files to find a match for the flags given to \fIack\fP. -The flags \fB-m\fP, \fB-o\fP, -\fI-O\fP, \fB-r\fP, \fB-v\fP, \fB-g\fP, -\fB-c\fP, \fB-t\fP, -\fB-k\fP, \fB-R\fP and -\f-.\fP are specific to \fIack\fP and +The flags \fB\-m\fP, \fB\-o\fP, +\fB\-O\fP, \fB\-r\fP, \fB\-v\fP, \fB\-g\fP, \-\fB\-c\fP, \fB\-t\fP, +\fB\-k\fP, \fB\-R\fP and \-\fB\-.\fP are specific to \fIack\fP and not handed down to any transformation. The matching is performed in the order in which the entries appear in the definition. @@ -249,11 +248,11 @@ replaced by the characters matched by the * in the expression. The right hand part is also subject to variable replacement. The variable will probably be used in the program arguments. -The \fB-l\fP flags are special, +The \fB\-l\fP flags are special, the order in which they are presented to \fIack\fP must be preserved. The identifier LNAME is used in conjunction with the scanning of -\fB-l\fP flags. +\fB\-l\fP flags. The value assigned to LNAME is used to replace the flag. The example further on shows the use all this. .IP \fIargs\fP @@ -261,39 +260,51 @@ The example further on shows the use all this. The keyword is followed by the program call arguments. It is subject to backslashing, variable replacement, expression replacement, line splitting and IO replacement. -The variables assigned to by \fImapflags\P will probably be +The variables assigned to by \fImapflags\fP will probably be used here. The flags not recognized by \fIack\fP or any of the transformations are passed to the linker and inserted before all other arguments. -.IP \fIprop\fB +.IP \fIstdin\fP .br -This -~optional~- keyword is followed by a sequence of options, -each option is indicated by one character -signifying a special property of the transformation. +This keyword indicates that the transformation reads from standard input. +.IP \fIstdout\fP +.br +This keyword indicates that the transformation writes on standard output. +.IP \fIoptimizer\fP +.br +This keyword indicates that this transformation is an optimizer. +.IP \fIlinker\fP +.br +This keyword indicates that this transformation is the linker. +.IP \fIcombiner\fP +.br +This keyword indicates that this transformation is a combiner. A combiner +is a program combining several files into one, but is not a linker. +An example of a combiner is the global optimizer. +.IP \fIprep\fP +.br +This \-~optional~\- keyword is followed an option indicating its relation +to the preprocessor. The possible options are: .DS X - < the input file will be read from standard input - > the output file will be written on standard output - p the input files must be preprocessed - m the input files must be preprocessed when starting with # - O this transformation is an optimizer and may be skipped - P this transformation is the preprocessor - C this transformation is the linker + always the input files must be preprocessed + cond the input files must be preprocessed when starting with # + is this transformation is the preprocessor .DE .IP \fIrts\fP .br -This -~optional~- keyword indicates that the rest of the line must be +This \-~optional~\- keyword indicates that the rest of the line must be used to set the variable RTS, if it was not already set. Thus the variable RTS is set by the first transformation executed which such a property or as a result from \fIack\fP's program -call name (acc, cc, apc or pc) or by the \fB-.suffix\fP flag. +call name (acc, cc, apc or pc) or by the \fB\-.suffix\fP flag. .IP \fIneed\fP .br -This -~optional~- keyword indicates that the rest of the line must be +This \-~optional~\- keyword indicates that the rest of the line must be concatenated to the NEEDS variable. This is done once for every transformation used or indicated by one of the program call names mentioned above or indicated -by the \fB-.suffix\fP flag. +by the \fB\-.suffix\fP flag. .br .nr PD 1v .NH @@ -302,119 +313,118 @@ Conventions used in description files \fIAck\fP reads two description files. A few of the variables defined in the machine specific file are used by the descriptions of the front-ends. -Other variables, set by \fack\fB, are of use to all +Other variables, set by \fIack\fP, are of use to all transformations. .PP \fIAck\fP sets the variable EM to the home directory of the Amsterdam Compiler Kit. The variable SOURCE is set to the name of the argument that is currently being massaged, this is usefull for debugging. +The variable SUFFIX is set to the suffix of the argument that is +currently being massaged. .br The variable M indicates the -directory in mach/{M}/lib/tail_..... and NAME is the string to -be defined by the preprocessor with -D{NAME}. +directory in lib/{M}/tail_..... and NAME is the string to +be defined by the preprocessor with \-D{NAME}. The definitions of {w}, {s}, {l}, {d}, {f} and {p} indicate EM_WSIZE, EM_SSIZE, EM_LSIZE, EM_DSIZE, EM_FSIZE and EM_PSIZE respectively. .br -The variable INCLUDES is used as the last argument to \fIcpp\fP, -it is currently used to add the directory {EM}/include to +The variable INCLUDES is used as the last argument to \fIcpp\fP. +It is used to add directories to the list of directories containing #include files. -{EM}/include contains a few files used by the library routines -for part III from the -.UX -manual. -These routines are included in the kit. .PP The variables HEAD, TAIL and RTS are set by \fIack\fP and used to compose the arguments for the linker. .NH Example -.sp 1 -description for front-end +.PP +Description for front-end .DS X -name cpp # the C-preprocessor - # no from, it's governed by the P property - to .i # result files have suffix i - program {EM}/lib/cpp # pathname of loadfile - mapflag -I* CPP_F={CPP_F?} -I* # grab -I.. -U.. and - mapflag -U* CPP_F={CPP_F?} -U* # -D.. to use as arguments - mapflag -D* CPP_F={CPP_F?} -D* # in the variable CPP_F - args {CPP_F?} {INCLUDES?} -D{NAME} -DEM_WSIZE={w} -DEM_PSIZE={p} \ --DEM_SSIZE={s} -DEM_LSIZE={l} -DEM_FSIZE={f} -DEM_DSIZE={d} < - # The arguments are: first the -[IUD]... - # then the include dir's for this machine - # then the NAME and size valeus finally - # followed by the input file name - prop >P # Output on stdout, is preprocessor +.ta 4n 40n +name cpp # the C-preprocessor + # no from, it's governed by the P property + to .i # result files have suffix i + program {EM}/lib/cpp # pathname of loadfile + mapflag \-I* CPP_F={CPP_F?} \-I* # grab \-I.. \-U.. and + mapflag \-U* CPP_F={CPP_F?} \-U* # \-D.. to use as arguments + mapflag \-D* CPP_F={CPP_F?} \-D* # in the variable CPP_F + args {CPP_F?} {INCLUDES?} \-D{NAME} \-DEM_WSIZE={w} \-DEM_PSIZE={p} \e + \-DEM_SSIZE={s} \-DEM_LSIZE={l} \-DEM_FSIZE={f} \-DEM_DSIZE={d} < + # The arguments are: first the \-[IUD]... + # then the include dir's for this machine + # then the NAME and size valeus finally + # followed by the input file name + stdout # Output on stdout + prep is # Is preprocessor end -name cem # the C-compiler proper - from .c # used for files with suffix .c - to .k # produces compact code files - program {EM}/lib/em_cem # pathname of loadfile - mapflag -p CEM_F={CEM_F?} -Xp # pass -p as -Xp to cem - mapflag -L CEM_F={CEM_F?} -l # pass -L as -l to cem - args -Vw{w}i{w}p{p}f{f}s{s}l{l}d{d} {CEM_F?} - # the arguments are the object sizes in - # the -V... flag and possibly -l and -Xp - prop <>p # input on stdin, output on stdout, use cpp - rts .c # use the C run-time system - need .c # use the C libraries +name cem # the C-compiler proper + from .c # used for files with suffix .c + to .k # produces compact code files + program {EM}/lib/em_cem # pathname of loadfile + mapflag \-p CEM_F={CEM_F?} \-Xp # pass \-p as \-Xp to cem + mapflag \-L CEM_F={CEM_F?} \-l # pass \-L as \-l to cem + args \-Vw{w}i{w}p{p}f{f}s{s}l{l}d{d} {CEM_F?} + # the arguments are the object sizes in + # the \-V... flag and possibly \-l and \-Xp + stdin # input from stdin + stdout # output on stdout + prep always # use cpp + rts .c # use the C run-time system + need .c # use the C libraries end -name decode # make human readable files from compact code - from .k.m # accept files with suffix .k or .m - to .e # produce .e files - program {EM}/lib/em_decode # pathname of loadfile - args < # the input file name is the only argument - prop > # the output comes on stdout +name decode # make human readable files from compact code + from .k.m # accept files with suffix .k or .m + to .e # produce .e files + program {EM}/lib/em_decode # pathname of loadfile + args < # the input file name is the only argument + stdout # the output comes on stdout end .DE .DS X +.ta 4n 40n Example of a backend, in this case the EM assembler/loader. -var w=2 # wordsize 2 -var p=2 # pointersize 2 -var s=2 # short size 2 -var l=4 # long size 4 -var f=4 # float size 4 -var d=8 # double size 8 -var M=int # Unused in this example -var NAME=int22 # for cpp (NAME=int results in #define int 1) -var LIB=mach/int/lib/tail_ # part of file name for libraries -var RT=mach/int/lib/head_ # part of file name for run-time startoff -var SIZE_FLAG=-sm # default internal table size flag -var INCLUDES=-I{EM}/include # use {EM}/include for #include files -name asld # Assembler/loader - from .k.m.a # accepts compact code and archives - to e.out # output file name - program {EM}/lib/em_ass # load file pathname - mapflag -l* LNAME={EM}/{LIB}* # e.g. -ly becomes - # {EM}/mach/int/lib/tail_y - mapflag -+* ASS_F={ASS_F?} -+* # recognize -+ and -- - mapflag --* ASS_F={ASS_F?} --* - mapflag -s* SIZE_FLAG=-s* # overwrite old value of SIZE_FLAG - args {SIZE_FLAG} \ - ({RTS}:.c={EM}/{RT}cc) ({RTS}:.p={EM}/{RT}pc) -o > < \ - (.p:{TAIL}={EM}/{LIB}pc) \ - (.c:{TAIL}={EM}/{LIB}cc.1s {EM}/{LIB}cc.2g) \ - (.c.p:{TAIL}={EM}/{LIB}mon) - # -s[sml] must be first argument - # the next line contains the choice for head_cc or head_pc - # and the specification of in- and output. - # the last three args lines choose libraries - prop C # This is the final stage +var w=2 # wordsize 2 +var p=2 # pointersize 2 +var s=2 # short size 2 +var l=4 # long size 4 +var f=4 # float size 4 +var d=8 # dou‹ÚXYÂHÈ\Ùˆš[H˜e startoff +var SIZE_FLAG=\-sm # default internal table size flag +var INCLUDES=\-I{EM}/include # use {EM}/include for #include files +name asld # Assembler/loader + from .k.m.a # accepts compact code and archives + to e.out # output file name + program {EM}/lib/em_ass # load file pathname + mapflag \-l* LNAME={EM}/{LIB}* # e.g. \-ly becomes + # {EM}/mach/int/lib/tail_y + mapflag \-+* ASS_F={ASS_F?} \-+* # recognize \-+ and \-\- + mapflag \-\-* ASS_F={ASS_F?} \-\-* + mapflag \-s* SIZE_FLAG=\-s* # overwrite old value of SIZE_FLAG + args {SIZE_FLAG} \e + ({RTS}:.c={EM}/{RT}cc) ({RTS}:.p={EM}/{RT}pc) \-o > < \e + (.p:{TAIL}={EM}/{LIB}pc) \e + (.c:{TAIL}={EM}/{LIB}cc.1s {EM}/{LIB}cc.2g) \e + (.c.p:{TAIL}={EM}/{LIB}mon) + # \-s[sml] must be first argument + # the next line contains the choice for head_cc or head_pc + # and the specification of in- and output. + # the last three args lines choose libraries + linker end .DE -The command "ack -mint -v -v -I../h -L -ly prog.c" - would result in the following +The command \fIack \-mint \-v \-v \-I../h \-L \-ly prog.c\fP +would result in the following calls (with exec(II)): .DS X -1) /lib/cpp -I../h -I/usr/em/include -Dint22 -DEM_WSIZE=2 -DEM_PSIZE=2 - -DEM_SSIZE=2 -DEM_LSIZE=4 -DEM_FSIZE=4 -DEM_DSIZE=8 prog.c -2) /usr/em/lib/em_cem -Vw2i2p2f4s2l4d8 -l -3) /usr/em/lib/em_ass -sm /usr/em/mach/int/lib/head_cc -o e.out prog.k - /usr/em/mach/int/lib/tail_y /usr/em/mach/int/lib/tail_cc.1s - /usr/em/mach/int/lib/tail_cc.2g /usr/em/mach/int/lib/tail_mon +.ta 4n +1) /lib/cpp \-I../h \-I/usr/em/include \-Dint22 \-DEM_WSIZE=2 \-DEM_PSIZE=2 \e + \-DEM_SSIZE=2 \-DEM_LSIZE=4 \-DEM_FSIZE=4 \-DEM_DSIZE=8 prog.c +2) /usr/em/lib/em_cem \-Vw2i2p2f4s2l4d8 \-l +3) /usr/em/lib/em_ass \-sm /usr/em/mach/int/lib/head_cc \-o e.out prog.k + /usr/em/mach/int/lib/tail_y /usr/em/mach/int/lib/tail_cc.1s + /usr/em/mach/int/lib/tail_cc.2g /usr/em/mach/int/lib/tail_mon .DE diff --git a/doc/cg.doc b/doc/cg.doc index 9fe39b17..391a8608 100644 --- a/doc/cg.doc +++ b/doc/cg.doc @@ -1,5 +1,6 @@ .\" $Header$ .RP +.ND Nov 1984 .TL The table driven code generator from .br @@ -17,6 +18,11 @@ The Amsterdam Compiler Kit is such a collection of tools. This document provides a description of the internal workings of the table driven code generator in the Amsterdam Compiler Kit, and a description of syntax and semantics of the driving table. +.PP +>>> NOTE <<< +.br +This document pertains to the \fBold\fP code generator. Refer to the +"Second Revised Edition" for the new code generator. .AE .NH 1 Introduction @@ -197,10 +203,10 @@ This is given as .DS FORMAT = string .DE -The default for string is "%d" or "%ld" depending on the wordsize of -the machine. For example on the PDP 11 one can use +The default for string is "%ld". +For example on the PDP 11 one can use .DS -FORMAT= "0%o" +FORMAT= "0%lo" .DE to satisfy the old UNIX assembler that reads octal unless followed by a period, and the ACK assembler that follows C conventions. @@ -974,7 +980,7 @@ and their range depends on the machine at hand. The type 'int' is used for things like labelcounters that won't require more than 16 bits precision. The type 'word' is used among others to assemble datawords and -is of type 'long' if EM_WSIZE>2. +is of type 'long'. The type 'full' is used for addresses and is of type 'long' if EM_WSIZE>2 or EM_PSIZE>2. .PP @@ -1115,13 +1121,13 @@ Example mach.h for the PDP-11 #define cst_fmt "$%d." #define off_fmt "%d." -#define ilb_fmt "I%02x%x" +#define ilb_fmt "I%x_%x" #define dlb_fmt "_%d" #define hol_fmt "hol%d" -#define hol_off "%d.+hol%d" +#define hol_off "%ld.+hol%d" -#define con_cst(x) fprintf(codefile,"%d.\en",x) +#define con_cst(x) fprintf(codefile,"%ld.\en",x) #define con_ilb(x) fprintf(codefile,"%s\en",x) #define con_dlb(x) fprintf(codefile,"%s\en",x) diff --git a/doc/cref.doc b/doc/cref.doc index ccbb4f80..76c4f84d 100644 --- a/doc/cref.doc +++ b/doc/cref.doc @@ -1,5 +1,4 @@ .\" $Header$ -.ll 72 .nr ID 4 .de hd 'sp 2 diff --git a/doc/crefman.doc b/doc/crefman.doc new file mode 100644 index 00000000..10e59a37 --- /dev/null +++ b/doc/crefman.doc @@ -0,0 +1,627 @@ +.EQ +delim $$ +.EN +.RP +.TL +ACK/CEM Compiler +.br +Reference Manual +.AU +Erik H. Baalbergen +.AI +Department of Mathematics and Computer Science +Vrije Universiteit +Amsterdam +The Netherlands +.AB no +.AE +.NH +C Language +.PP +This section discusses the extensions to and deviations from the C language, +as described in [1]. +The issues are numbered according to the reference manual. +.SH +2.2 Identifiers +.PP +Upper and lower case letters are different. +The number of significant letters +is 32 by default, but may be set to another value using the \fB\-M\fP option. +The identifier length should be set according to the rest of the compilation +programs. +.SH +2.3 Keywords +.SH +\f5asm\fP +.PP +The keyword \f5asm\fP +is recognized. +However, the statement +.DS +.ft 5 +asm(string); +.ft R +.DE +is skipped, while a warning is given. +.SH +\f5enum\fP +.PP +The \f5enum\fP keyword is recognized and interpreted. +.SH +\f5entry\fP, \f5fortran\fP +.PP +The words \f5entry\fP and \f5fortran\fP +are reserved under the restricted option. +The words are not interpreted by the compiler. +.SH +2.4.1 Integer Constants +.PP +An octal or hex constant which is less than or equal to the largest unsigned +(target) machine integer is taken to be \f5unsigned\fP. +An octal or hex constant which exceeds the largest unsigned (target) machine +integer is taken to be \f5long\fP. +.SH +2.4.3 Character Constants +.PP +A character constant is a sequence of 1 up to \f5sizeof(int)\fP characters +enclosed in single quotes. +The value of a character constant '$c sub 1 c sub 2 ... c sub n$' +is $d sub n + M \(mu d sub {n - 1} + ... + M sup {n - 1} \(mu d sub 2 + M sup n \(mu d sub 1$, +where M is 1 + maximum unsigned number representable in an \f5unsigned char\fP, +and $d sub i$ is the signed value (ASCII) +of character $c sub i$. +.SH +2.4.4 Floating Constants +.PP +The compiler does not support compile-time floating point arithmetic. +.SH +2.6 Hardware characteristics +.PP +The compiler is capable of producing EM code for machines with the following +properties +.IP \(bu +a \f5char\fP is 8 bits +.IP \(bu +the size of \f5int\fP is equal to the word size +.IP \(bu +the size of \f5short\fP may not exceed the size of \f5int\fP +.IP \(bu +the size of \f5int\fP may not exceed the size of \f5long\fP +.IP \(bu +the size of pointers is equal to the size of either \f5short\fP, \f5int\fP +or \f5long\fP +.LP +.SH +4 What's in a name? +.SH +\f5char\fP +.PP +Objects of type \f5char\fP are taken to be signed. +The combination \f5unsigned char\fP is legal. +.SH +\f5unsigned\fP +.PP +The type combinations \f5unsigned char\fP, \f5unsigned short\fP and +\f5unsigned long\fP are supported. +.SH +\f5enum\fP +.PP +The data type \f5enum\fP is implemented as described +in \fIRecent Changes to C\fP (see appendix A). +.I Cem +treats enumeration variables as if they were \f5int\fP. +.SH +\f5void\fP +.PP +Type \f5void\fP is implemented. +The type specifies an empty set of values, which takes no storage space. +.SH +\fRFundamental types\fP +.PP +The names of the fundamental types can be redefined by the user, using +\f5typedef\fP. +.SH +7 Expressions +.PP +The order of evaluation of expressions depends on the complexity of the +subexpressions. +In case of commutative operations, the most complex subexpression is +evaluated first. +Parameter lists are evaluated from right to left. +.SH +7.2 Unary operators +.PP +The type of a \f5sizeof\fP expression is \f5unsigned int\fP. +.SH +7.13 Conditional operator +.PP +Both the second and the third expression in a conditional expression may +include assignment operators. +They may be structs or unions. +.SH +7.14 Assignment operators +.PP +Structures may be assigned, passed as arguments to functions, and returned +by functions. +The types of operands taking part must be the same. +.SH +8.2 Type specifiers +.PP +The combinations \f5unsigned char\fP, \f5unsigned short\fP +and \f5unsigned long\fP are implemented. +.SH +8.5 Structure and union declarations +.PP +Fields of any integral type, either signed or unsigned, +are supported, as long as the type fits in a word on the target machine. +.PP +Fields are left adjusted by default; the first field is put into the left +part of a word, the next one on the right side of the first one, etc. +The \f5-Vr\fP option in the call of the compiler +causes fields to be right adjusted within a machine word. +.PP +The tags of structs and unions occupy a different name space from that of +variables and that of member names. +.SH +9.7 Switch statement +.PP +The type of \fIexpression\fP in +.DS +.ft 5 +\f5switch (\fP\fIexpression\fP\f5)\fP \fIstatement\fP +.ft +.DE +must be integral. +A warning is given under the restricted option if the type is \f5long\fP. +.SH +10 External definitions +.PP +See [4] for a discussion on this complicated issue. +.SH +10.1 External function definitions +.PP +Structures may be passed as arguments to functions, and returned +by functions. +.SH +11.1 Lexical scope +.PP +Typedef names may be redeclared like any other variable name; the ice mentioned +in \(sc11.1 is walked correctly. +.SH +12 Compiler control lines +.PP +Lines which do not occur within comment, and with \f5#\fP as first +character, are interpreted as compiler control line. +There may be an arbitrary number of spaces, tabs and comments (collectively +referred as \fIwhite space\fP) following the \f5#\fP. +Comments may contain newline characters. +Control lines with only white space between the \f5#\fP and the line separator +are skipped. +.PP +The #\f5include\fP, #\f5ifdef\fP, #\f5ifndef\fP, #\f5undef\fP, #\f5else\fP and +#\f5endif\fP control lines and line directives consist of a fixed number of +arguments. +The list of arguments may be followed an arbitrary sequence of characters, +in which comment is interpreted as such. +(I.e., the text between \f5/*\fP and \f5*/\fP is skipped, regardless of +newlines; note that commented-out lines beginning with \f5#\fP are not +considered to be control lines.) +.SH +12.1 Token replacement +.PP +The replacement text of macros is taken to be a string of characters, in which +an identifier may stand for a formal parameter, and in which comment is +interpreted as such. +Comments and newline characters, preceeded by a backslash, in the replacement +text are replaced by a space character. +.PP +The actual parameters of a macro are considered tokens and are +balanced with regard to \f5()\fP, \f5{}\fP and \f5[]\fP. +This prevents the use of macros like +.DS +.ft 5 +CTL([) +.ft +.DE +.PP +Formal parameters of a macro must have unique names within the formal-parameter +list of that macro. +.PP +A message is given at the definition of a macro if the macro has +already been #\f5defined\fP, while the number of formal parameters differ or +the replacement texts are not equal (apart from leading and trailing +white space). +.PP +Recursive use of macros is detected by the compiler. +.PP +Standard #\f5defined\fP macros are +.DS +\f5__FILE__\fP name of current input file as string constant +\f5__DATE__\fP curent date as string constant; e.g. \f5"Tue Wed 2 14:45:23 1986"\fP +\f5__LINE__\fP current line number as an integer +.DE +.PP +No message is given if \fIidentifier\fP is not known in +.DS +.ft 5 +#undef \fIidentifier\fP +.ft +.DE +.SH +12.2 File inclusion +.PP +A newline character is appended to each file which is included. +.SH +12.3 Conditional compilation +.PP +The #\f5if\fP, #\f5ifdef\fP and #\f5ifndef\fP control lines may be followed +by an arbitrary number of +.DS +.ft 5 +#elif \fIconstant-expression\fP +.ft +.DE +control lines, before the corresponding #\f5else\fP or #\f5endif\fP +is encountered. +The construct +.DS +.ft 5 +#elif \fIconstant-expression\fP +some text +#endif /* corresponding to #elif */ +.ft +.DE +is equivalent to +.DS +.ft 5 +#else +#if \fIconstant-expression\fP +some text +#endif /* corresponding to #if */ +#endif /* corresponding to #else */ +.ft +.DE +.PP +The \fIconstant-expression\fP in #\f5if\fP and #\f5elif\fP control lines +may contain the construction +.DS +.ft 5 +defined(\fIidentifier\fP) +.ft +.DE +which is replaced by \f51\fP, if \fIidentifier\fP has been #\f5defined\fP, +and by \f50\fP, if not. +.PP +Comments in skipped lines are interpreted as such. +.SH +12.4 Line control +.PP +Line directives may occur in the following forms: +.DS +.ft 5 +#line \fIconstant\fP +#line \fIconstant\fP "\fIfilename\fP" +#\fIconstant\fP +#\fIconstant\fP "\fIfilename\fP" +.ft +.DE +Note that \fIfilename\fP is enclosed in double quotes. +.SH +14.2 Functions +.PP +If a pointer to a function is called, the function the pointer points to +is called instead. +.SH +15 Constant expressions +.PP +The compiler distinguishes the following types of integral constant expressions +.IP \(bu +field-width specifier +.IP \(bu +case-entry specifier +.IP \(bu +array-size specifier +.IP \(bu +global variable initialization value +.IP \(bu +enum-value specifier +.IP \(bu +truth value in \f5#if\fP control line +.LP +.PP +Constant integral expressions are compile-time evaluated while an effort +is made to report overflow. +Constant floating expressions are not compile-time evaluated. +.NH +Compiler flags +.IP \fB\-C\fR +Run the preprocessor stand-alone while maintaining the comments. +Line directives are produced whenever needed. +.IP \fB\-D\fP\fIname\fP=\fIstring-of-characters\fP +.br +Define \fIname\fR as macro with \fIstring-of-characters\fR as +replacement text. +.IP \fB\-D\fP\fIname\fP +.br +Equal to \fB\-D\fP\fIname\fP\fB=1\fP. +.IP \fB\-E\fP +Run the preprocessor stand alone, i.e., +list the sequence of input tokens and delete any comments. +Line directives are produced whenever needed. +.IP \fB\-I\fIpath\fR +.br +Prepend \fIpath\fR to the list of include directories. +To put the directories "include", "sys/h" and "util/h" into the +include directory list in that order, the user has to specify +.DS +.ft 5 +-Iinclude -Isys/h -Iutil/h +.ft R +.DE +An empty \fIpath\fP causes the standard include +directory (usually \f5/usr/include\fP) to be forgotten. +.IP \fB\-M\fP\fIn\fP +.br +Set maximum significant identifier length to \fIn\fP. +.IP \fB\-n\fP +Suppress EM register messages. +The user-declared variables are not stored into registers on the target +machine. +.IP \fB\-p\fP +Generate the EM \fBfil\fP and \fBlin\fP instructions in order to enable +an interpreter to keep track of the current location in the source code. +.IP \fB\-P\fP +Equivalent with \fB\-E\fP, but without line directives. +.IP \fB\-R\fP +Interpret the input as restricted C (according to the language as +described in [1]). +.IP \fB\-T\fP\fIpath\fP +.br +Create temporary files, if necessary, in directory \fIpath\fP. +.IP \fB\-U\fP\fIname\fP +.br +Get rid of the compiler-predefined macro \fIname\fP, i.e., +consider +.DS +.ft 5 +#undef \fIname\fP +.ft R +.DE +to appear in the beginning of the file. +.IP \fB\-V\fIcm\fR.\fIn\fR,\ \fB\-V\fIcm\fR.\fIncm\fR.\fIn\fR\ ... +.br +Set the size and alignment requirements. +The letter \fIc\fR indicates the simple type, which is one of +\fBs\fR(short), \fBi\fR(int), \fBl\fR(long), \fBf\fR(float), \fBd\fR(double) +or \fBp\fR(pointer). +If \fIc\fR is \fBS\fP or \fBU\fP, then \fIn\fP is taken to be the initial +alignment of structs or unions, respectively. +The effective alignment of a struct or union is the least common multiple +of the initial struct/union alignment and the alignments of its members. +The \fIm\fR parameter can be used to specify the length of the type (in bytes) +and the \fIn\fR parameter for the alignment of that type. +Absence of \fIm\fR or \fIn\fR causes the default value to be retained. +To specify that the bitfields should be right adjusted instead of the +default left adjustment, specify \fBr\fR as \fIc\fR parameter. +.IP \fB\-w\fR +Suppress warning messages +.IP \fB\-\-\fIcharacter\fR +.br +Set debug-flag \fIcharacter\fP. +This enables some special features offered by a debug and develop version of +the compiler. +Some particular flags may be recognized, others may have surprising effects. +.RS +.IP \fBd\fP +Generate a dependency graph, reflecting the calling structure of functions. +Lines of the form +.DS +.ft 5 +DFA: \fIcalling-function\fP: \fIcalled-function\fP +.ft +.DE +are generated whenever a function call is encountered. +.IP \fBf\fP +Dump whole identifier table, including macros and reserved words. +.IP \fBh\fP +Supply hash-table statistics. +.IP \fBi\fP +Print names of included files. +.IP \fBm\fP +Supply statistics concerning the memory allocation. +.IP \fBt\fP +Dump table of identifiers. +.IP \fBu\fP +Generate extra statistics concerning the predefined types and identifiers. +Works in combination with \fBf\fP or \fBt\fP. +.IP \fBx\fP +Print expression trees in human-readable format. +.RE +.LP +.SH +References +.IP [1] +Brian W. Kernighan, Dennis M. Ritchie, +.I +The C Programming Language +.R +.IP [2] +L. Rosler, +.I +Draft Proposed Standard - Programming Language C, +.R +ANSI X3J11 Language Subcommittee +.IP [3] +Erik H. Baalbergen, Dick Grune, Maarten Waage, +.I +The CEM Compiler, +.R +Informatica Manual IM-4, Dept. of Mathematics and Computer Science, Vrije +Universiteit, Amsterdam, The Netherlands +.IP [4] +Erik H. Baalbergen, +.I +Modeling global declarations in C, +.R +internal paper +.LP +.bp +.SH +Appendix A - Enumeration Type +.PP +The syntax is +.sp +.RS +.I enum-specifier : +.RS +\&\f5enum\fP { \fIenum-list\fP } +.br +\&\f5enum\fP \fIidentifier\fP { \fIenum-list\fP } +.br +\&\f5enum\fP \fIidentifier\fP +.RE +.sp +\&\fIenum-list\fP : +.RS +\&\fIenumerator\fP +.br +\&\fIenum-list\fP , \fIenumerator\fP +.RE +.sp +\&\fIenumerator\fP : +.RS +\&\fIidentifier\fP +.br +\&\fIidentifier\fP = \fIconstant-expression\fP +.RE +.sp +.RE +The identifier has the same role as the structure tag in a struct specification. +It names a particular enumeration type. +.PP +The identifiers in the enum-list are declared as constants, and may appear +whenever constants are required. +If no enumerators with +.B = +appear, then the values of the constants begin at 0 and increase by 1 as the +declaration is read from left to right. +An enumerator with +.B = +gives the associated identifier the value indicated; subsequent identifiers +continue the progression from the assigned value. +.PP +Enumeration tags and constants must all be distinct, and, unlike structure +tags and members, are drawn from the same set as ordinary identifiers. +.PP +Objects of a given enumeration type are regarded as having a type distinct +from objects of all other types. +.bp +.SH +Appendix B: C grammar in LL(1) form +.PP +The \fBbold-faced\fP and \fIitalicized\fP tokens represent terminal symbols. +.vs 16 +.nf +\fBexternal definitions\fP +program: external-definition* +external-definition: ext-decl-specifiers [declarator [function | non-function] | '\fB;\fP'] | asm-statement +ext-decl-specifiers: decl-specifiers? +non-function: initializer? ['\fB,\fP' init-declarator]* '\fB;\fP' +function: declaration* compound-statement +.sp 1 +\fBdeclarations\fP +declaration: decl-specifiers init-declarator-list? '\fB;\fP' +decl-specifiers: other-specifier+ [single-type-specifier other-specifier*]? | single-type-specifier other-specifier* +other-specifier: \fBauto\fP | \fBstatic\fP | \fBextern\fP | \fBtypedef\fP | \fBregister\fP | \fBshort\fP | \fBlong\fP | \fBunsigned\fP +type-specifier: decl-specifiers +single-type-specifier: \fItype-identifier\fP | struct-or-union-specifier | enum-specifier +init-declarator-list: init-declarator ['\fB,\fP' init-declarator]* +init-declarator: declarator initializer? +declarator: primary-declarator ['\fB(\fP' formal-list ? '\fB)\fP' | arrayer]* | '\fB*\fP' declarator +primary-declarator: identifier | '\fB(\fP' declarator '\fB)\fP' +arrayer: '\fB[\fP' constant-expression? '\fB]\fP' +formal-list: formal ['\fB,\fP' formal]* +formal: identifier +enum-specifier: \fBenum\fP [enumerator-pack | identifier enumerator-pack?] +enumerator-pack: '\fB{\fP' enumerator ['\fB,\fP' enumerator]* '\fB,\fP'? '\fB}\fP' +enumerator: identifier ['\fB=\fP' constant-expression]? +struct-or-union-specifier: [ \fBstruct\fP | \fBunion\fP] [ struct-declaration-pack | identifier struct-declaration-pack?] +struct-declaration-pack: '\fB{\fP' struct-declaration+ '\fB}\fP' +struct-declaration: type-specifier struct-declarator-list '\fB;\fP'? +struct-declarator-list: struct-declarator ['\fB,\fP' struct-declarator]* +struct-declarator: declarator bit-expression? | bit-expression +bit-expression: '\fB:\fP' constant-expression +initializer: '\fB=\fP'? initial-value +cast: '\fB(\fP' type-specifier abstract-declarator '\fB)\fP' +abstract-declarator: primary-abstract-declarator ['\fB(\fP' '\fB)\fP' | arrayer]* | '\fB*\fP' abstract-declarator +primary-abstract-declarator: ['\fB(\fP' abstract-declarator '\fB)\fP']? +.sp 1 +\fBstatements\fP +statement: + expression-statement + | label '\fB:\fP' statement + | compound-statement + | if-statement + | while-statement + | do-statement + | for-statement + | switch-statement + | case-statement + | default-statement + | break-statement + | continue-statement + | return-statement + | jump + | '\fB;\fP' + | asm-statement + ; +expression-statement: expression '\fB;\fP' +label: identifier +if-statement: \fBif\fP '\fB(\fP' expression '\fB)\fP' statement [\fBelse\fP statement]? +while-statement: \fBwhile\fP '\fB(\fP' expression '\fB)\fP' statement +do-statement: \fBdo\fP statement \fBwhile\fP '\fB(\fP' expression '\fB)\fP' '\fB;\fP' +for-statement: \fBfor\fP '\fB(\fP' expression? '\fB;\fP' expression? '\fB;\fP' expression? '\fB)\fP' statement +switch-statement: \fBswitch\fP '\fB(\fP' expression '\fB)\fP' statement +case-statement: \fBcase\fP constant-expression '\fB:\fP' statement +default-statement: \fBdefault\fP '\fB:\fP' statement +break-statement: \fBbreak\fP '\fB;\fP' +continue-statement: \fBcontinue\fP '\fB;\fP' +return-statement: \fBreturn\fP expression? '\fB;\fP' +jump: \fBgoto\fP identifier '\fB;\fP' +compound-statement: '\fB{\fP' declaration* statement* '\fB}\fP' +asm-statement: \fBasm\fP '\fB(\fP' \fIstring\fP '\fB)\fP' '\fB;\fP' +.sp 1 +\fBexpressions\fP +initial-value: assignment-expression | initial-value-pack +initial-value-pack: '\fB{\fP' initial-value-list '\fB}\fP' +initial-value-list: initial-value ['\fB,\fP' initial-value]* '\fB,\fP'? +primary: \fIidentifier\fP | constant | \fIstring\fP | '\fB(\fP' expression '\fB)\fP' +secundary: primary [index-pack | parameter-pack | selection]* +index-pack: '\fB[\fP' expression '\fB]\fP' +parameter-pack: '\fB(\fP' parameter-list? '\fB)\fP' +selection: ['\fB.\fP' | '\fB\->\fP'] identifier +parameter-list: assignment-expression ['\fB,\fP' assignment-expression]* +postfixed: secundary postop? +unary: cast unary | postfixed | unop unary | size-of +size-of: \fBsizeof\fP [cast | unary] +binary-expression: unary [binop binary-expression]* +conditional-expression: binary-expression ['\fB?\fP' expression '\fB:\fP' assignment-expression]? +assignment-expression: conditional-expression [asgnop assignment-expression]? +expression: assignment-expression ['\fB,\fP' assignment-expression]* +unop: '\fB*\fP' | '\fB&\fP' | '\fB\-\fP' | '\fB!\fP' | '\fB~ \fP' | '\fB++\fP' | '\fB\-\-\fP' +postop: '\fB++\fP' | '\fB\-\-\fP' +multop: '\fB*\fP' | '\fB/\fP' | '\fB%\fP' +addop: '\fB+\fP' | '\fB\-\fP' +shiftop: '\fB<<\fP' | '\fB>>\fP' +relop: '\fB<\fP' | '\fB>\fP' | '\fB<=\fP' | '\fB>=\fP' +eqop: '\fB==\fP' | '\fB!=\fP' +arithop: multop | addop | shiftop | '\fB&\fP' | '\fB^ \fP' | '\fB|\fP' +binop: arithop | relop | eqop | '\fB&&\fP' | '\fB||\fP' +asgnop: '\fB=\fP' | '\fB+\fP' '\fB=\fP' | '\fB\-\fP' '\fB=\fP' | '\fB*\fP' '\fB=\fP' | '\fB/\fP' '\fB=\fP' | '\fB%\fP' '\fB=\fP' + | '\fB<<\fP' '\fB=\fP' | '\fB>>\fP' '\fB=\fP' | '\fB&\fP' '\fB=\fP' | '\fB^ \fP' '\fB=\fP' | '\fB|\fP' '\fB=\fP' + | '\fB+=\fP' | '\fB\-=\fP' | '\fB*=\fP' | '\fB/=\fP' | '\fB%=\fP' + | '\fB<<=\fP' | '\fB>>=\fP' | '\fB&=\fP' | '\fB^=\fP' | '\fB|=\fP' +constant: \fIinteger\fP | \fIfloating\fP +constant-expression: assignment-expression +identifier: \fIidentifier\fP | \fItype-identifier\fP +.fi diff --git a/doc/ego/.distr b/doc/ego/.distr new file mode 100644 index 00000000..f41bff47 --- /dev/null +++ b/doc/ego/.distr @@ -0,0 +1,18 @@ +Makefile +bo +ca +cf +cj +cs +ic +il +intro +lv +ov +ra +refs.gen +refs.opt +refs.stat +sp +sr +ud diff --git a/doc/ego/Makefile b/doc/ego/Makefile new file mode 100644 index 00000000..7b5b067c --- /dev/null +++ b/doc/ego/Makefile @@ -0,0 +1,52 @@ +REFS=-p refs.opt -p refs.stat -p refs.gen +INTRO=intro/intro? +OV=ov/ov? +IC=ic/ic? +CF=cf/cf? +IL=il/il? +SR=sr/sr? +CS=cs/cs? +SP=sp/sp? +UD=ud/ud? +LV=lv/lv? +CJ=cj/cj? +BO=bo/bo? +RA=ra/ra? +CA=ca/ca? +EGO=$(INTRO) $(OV) $(IC) $(CF) $(IL) $(SR) $(CS) $(SP) $(CJ) $(BO) \ + $(UD) $(LV) $(RA) $(CA) +REFER=refer + +../ego.doc: $(EGO) + $(REFER) -sA+T -l4,2 $(REFS) intro/head $(EGO) intro/tail > ../ego.doc + +ego.f: $(EGO) + $(REFER) -sA+T -l4,2 $(REFS) intro/head $(EGO) intro/tail | nroff -ms > ego.f +intro.f: $(INTRO) + $(REFER) -sA+T -l4,2 $(REFS) ov/head $(INTRO) intro/tail | nroff -ms > intro.f +ov.f: $(OV) + $(REFER) -sA+T -l4,2 $(REFS) ov/head $(OV) intro/tail | nroff -ms > ov.f +ic.f: $(IC) + $(REFER) -sA+T -l4,2 $(REFS) ic/head $(IC) intro/tail | nroff -ms > ic.f +cf.f: $(CF) + $(REFER) -sA+T -l4,2 $(REFS) cf/head $(CF) intro/tail | nroff -ms > cf.f +il.f: $(IL) + $(REFER) -sA+T -l4,2 $(REFS) il/head $(IL) intro/tail | nroff -ms > il.f +sr.f: $(SR) + $(REFER) -sA+T -l4,2 $(REFS) sr/head $(SR) intro/tail | nroff -ms > sr.f +cs.f: $(CS) + $(REFER) -sA+T -l4,2 $(REFS) cs/head $(CS) intro/tail | nroff -ms > cs.f +sp.f: $(SP) + $(REFER) -sA+T -l4,2 $(REFS) sp/head $(SP) intro/tail | nroff -ms > sp.f +cj.f: $(CJ) + $(REFER) -sA+T -l4,2 $(REFS) cj/head $(CJ) intro/tail | nroff -ms > cj.f +bo.f: $(BO) + $(REFER) -sA+T -l4,2 $(REFS) bo/head $(BO) intro/tail | nroff -ms > bo.f +ud.f: $(UD) + $(REFER) -sA+T -l4,2 $(REFS) ud/head $(UD) intro/tail | nroff -ms > ud.f +lv.f: $(LV) + $(REFER) -sA+T -l4,2 $(REFS) lv/head $(LV) intro/tail | nroff -ms > lv.f +ra.f: $(RA) + $(REFER) -sA+T -l4,2 $(REFS) ra/head $(RA) intro/tail | nroff -ms > ra.f +ca.f: $(CA) + $(REFER) -sA+T -l4,2 $(REFS) ca/head $(CA) intro/tail | nroff -ms > ca.f diff --git a/doc/ego/bo/.distr b/doc/ego/bo/.distr new file mode 100644 index 00000000..8f41f100 --- /dev/null +++ b/doc/ego/bo/.distr @@ -0,0 +1 @@ +bo1 diff --git a/doc/ego/bo/bo1 b/doc/ego/bo/bo1 new file mode 100644 index 00000000..23019605 --- /dev/null +++ b/doc/ego/bo/bo1 @@ -0,0 +1,151 @@ +.bp +.NH 1 +Branch Optimization +.NH 2 +Introduction +.PP +The Branch Optimization phase (BO) performs two related +(branch) optimizations. +.NH 3 +Fusion of basic blocks +.PP +If two basic blocks B1 and B2 have the following properties: +.DS +SUCC(B1) = {B2} +PRED(B2) = {B1} +.DE +then B1 and B2 can be combined into one basic block. +If B1 ends in an unconditional jump to the beginning of B2, this +jump can be eliminated, +hence saving a little execution time and object code size. +This technique can be used to eliminate some deficiencies +introduced by the front ends (for example, the "C" front end +translates switch statements inefficiently due to its one pass nature). +.NH 3 +While-loop optimization +.PP +The straightforward way to translate a while loop is to +put the test for loop termination at the beginning of the loop. +.DS +while cond loop LAB1: Test cond + body of the loop ---> Branch On False To LAB2 +end loop code for body of loop + Branch To LAB1 + LAB2: + +Fig. 10.1 Example of Branch Optimization +.DE +If the condition fails at the Nth iteration, the following code +gets executed (dynamically): +.DS +N * conditional branch (which fails N-1 times) +N-1 * unconditional branch +N-1 * body of the loop +.DE +An alternative translation is: +.DS + Branch To LAB2 +LAB1: + code for body of loop +LAB2: + Test cond + Branch On True To LAB1 +.DE +This translation results in the following profile: +.DS +N * conditional branch (which succeeds N-1 times) +1 * unconditional branch +N-1 * body of the loop +.DE +So the second translation will be significantly faster if N >> 2. +If N=2, execution time will be slightly increased. +On the average, the program will be speeded up. +Note that the code sizes of the two translations will be the same. +.NH 2 +Implementation +.PP +The basic block fusion technique is implemented +by traversing the control flow graph of a procedure, +looking for basic blocks B with only one successor (S). +If one is found, it is checked if S has only one predecessor +(which has to be B). +If so, the two basic blocks can in principle be combined. +However, as one basic block will have to be moved, +the textual order of the basic blocks will be altered. +This reordering causes severe problems in the presence +of conditional jumps. +For example, if S ends in a conditional branch, +the basic block that comes textually next to S must stay +in that position. +So the transformation in Fig. 10.2 is illegal. +.DS +LAB1: S1 LAB1: S1 + BRA LAB2 S2 + ... --> BEQ LAB3 +LAB2: S2 ... + BEQ LAB3 S3 + S3 + +Fig. 10.2 An illegal transformation of Branch Optimization +.DE +If B is moved towards S the same problem occurs if the block before B +ends in a conditional jump. +The problem could be solved by adding one extra branch, +but this would reduce the gains of the optimization to zero. +Hence the optimization will only be done if the block that +follows S (in the textual order) is not a successor of S. +This condition assures that S does not end in a conditional branch. +The condition always holds for the code generated by the "C" +front end for a switch statement. +.PP +After the transformation has been performed, +some attributes of the basic blocks involved (such as successor and +predecessor sets and immediate dominator) must be recomputed. +.PP +The while-loop technique is applied to one loop at a time. +The list of basic blocks of the loop is traversed to find +a block B that satisfies the following conditions: +.IP 1. +the textually next block to B is not part of the loop +.IP 2. +the last instruction of B is an unconditional branch; +hence B has only one successor, say S +.IP 3. +the textually next block of B is a successor of S +.IP 4. +the last instruction of S is a conditional branch +.LP +If such a block B is found, the control flow graph is changed +as depicted in Fig. 10.3. +.DS + | | + | v + v | + |-----<------| ----->-----| + ____|____ | | + | | | |-------| | + | S1 | | | v | + | Bcc | | | .... | +|--| | | | | +| --------- | | ----|---- | +| | | | | | +| .... ^ | | S2 | | +| | | | | | +| --------- | | | | | +v | | | ^ --------- | +| | S2 | | | | | +| | BRA | | | |-----<----- +| | | | | v +| --------- | | ____|____ +| | | | | | +| ------>------ | | S1 | +| | | Bnn | +|-------| | | | + | | ----|---- + v | | + |----<--| + | + v + +Fig. 10.3 Transformation of the CFG by Branch Optimization +.DE diff --git a/doc/ego/ca/.distr b/doc/ego/ca/.distr new file mode 100644 index 00000000..4723880e --- /dev/null +++ b/doc/ego/ca/.distr @@ -0,0 +1 @@ +ca1 diff --git a/doc/ego/ca/ca1 b/doc/ego/ca/ca1 new file mode 100644 index 00000000..781710ea --- /dev/null +++ b/doc/ego/ca/ca1 @@ -0,0 +1,75 @@ +.bp +.NH 1 +Compact assembly generation +.NH 2 +Introduction +.PP +The "Compact Assembly generation phase" (CA) transforms the +intermediate code of the optimizer into EM code in +Compact Assembly Language (CAL) format. +In the intermediate code, all program entities +(such as procedures, labels, global variables) +are denoted by a unique identifying number (see 3.5). +In the CAL output of the optimizer these numbers have to +be replaced by normal identifiers (strings). +The original identifiers of the input program are used whenever possible. +Recall that the IC phase generates two files that can be +used to map unique identifying numbers to procedure names and +global variable names. +For instruction labels CA always generates new names. +The reasons for doing so are: +.IP - +instruction labels are only visible inside one procedure, so they can +not be referenced in other modules +.IP - +the names are not very suggestive anyway, as they must be integer numbers +.IP - +the optimizer considerably changes the control structure of the program, +so there is really no one to one mapping of instruction labels in +the input and the output program. +.LP +As the optimizer combines all input modules into one module, +visibility problems may occur. +Two modules M1 and M2 can both define an identifier X (provided that +X is not externally visible in any of these modules). +If M1 and M2 are combined into one module M, two distinct +entities with the same name would exist in M, which +is not allowed. +.[~[ +tanenbaum machine architecture +.], section 11.1.4.3] +In these cases, CA invents a new unique name for one of the entities. +.NH 2 +Implementation +.PP +CA first reads the files containing the procedure and global variable names +and stores the names in two tables. +It scans these tables to make sure that all names are different. +Subsequently it reads the EM text, one procedure at a time, +and outputs it in CAL format. +The major part of the code that does the latter transformation +is adapted from the EM Peephole Optimizer. +.PP +The main problem of the implementation of CA is to +assure that the visibility rules are obeyed. +If an identifier must be externally visible (i.e. +it was externally visible in the input program) +and the identifier is defined (in the output program) before +being referenced, +an EXA or EXP pseudo must be generated for it. +(Note that the optimizer may change the order of definitions and +references, so some pseudos may be needed that were not +present in the input program). +On the other hand, an identifier may be only internally visible. +If such an identifier is referenced before being defined, +an INA or INP pseudo must be emitted prior to its first reference. +.UH +Acknowledgements +.PP +The author would like to thank Andy Tanenbaum for his guidance, +Duk Bekema for implementing the Common Subexpression Elimination phase +and writing the initial documentation of that phase, +Dick Grune for reading the manuscript of this report +and Ceriel Jacobs, Ed Keizer, Martin Kersten, Hans van Staveren +and the members of the S.T.W. user's group for their +interest and assistance. diff --git a/doc/ego/cf/.distr b/doc/ego/cf/.distr new file mode 100644 index 00000000..6aff9ca7 --- /dev/null +++ b/doc/ego/cf/.distr @@ -0,0 +1,6 @@ +cf1 +cf2 +cf3 +cf4 +cf5 +cf6 diff --git a/doc/ego/cf/cf1 b/doc/ego/cf/cf1 new file mode 100644 index 00000000..e6554745 --- /dev/null +++ b/doc/ego/cf/cf1 @@ -0,0 +1,94 @@ +.bp +.NH +The Control Flow Phase +.PP +In the previous chapter we described the intermediate +code of the global optimizer. +We also specified which part of this code +was constructed by the IC phase of the optimizer. +The Control Flow Phase (\fICF\fR) does +the remainder of the job, +i.e. it determines: +.IP - +the control flow graphs +.IP - +the loop tables +.IP - +the calling, change and use attributes of +the procedure table entries +.LP +CF operates on one procedure at a time. +For every procedure it first reads the EM instructions +from the EM-text file and groups them into basic blocks. +For every basic block, its successors and +predecessors are determined, +resulting in the control flow graph. +Next, the immediate dominator of every basic block +is computed. +Using these dominators, any loop in the +procedure is detected. +Finally, interprocedural analysis is done, +after which we will know the global effects of +every procedure call on its environment. +.sp +CF uses the same internal data structures +for the procedure table and object table as IC. +.NH 2 +Partitioning into basic blocks +.PP +With regard to flow of control, we distinguish +three kinds of EM instructions: +jump instructions, instruction label definitions and +normal instructions. +Jump instructions are all conditional or unconditional +branch instructions, +the case instructions (CSA/CSB) +and the RET (return) instruction. +A procedure call (CAL) is not considered to be a jump. +A defining occurrence of an instruction label +is regarded as an EM instruction. +.PP +An instruction starts +a new basic block, in any of the following cases: +.IP 1. +It is the first instruction of a procedure +.IP 2. +It is the first of a list of instruction label +defining occurrences +.IP 3. +It follows a jump +.LP +If there are several consecutive instruction labels +(which is highly unusual), +all of them are put in the same basic block. +Note that several cases may overlap, +e.g. a label definition at the beginning of a procedure +or a label following a jump. +.PP +A simple Finite State Machine is used to model +the above rules. +It also recognizes the end of a procedure, +marked by an END pseudo. +The basic blocks are stored internally as a doubly linked +linear list. +The blocks are linked in textual order. +Every node of this list has the attributes described +in the previous chapter (see syntax rule for +basic_block). +Furthermore, every node contains a pointer to its +EM instructions, +which are represented internally +as a linear, doubly linked list, +just as in the IC phase. +However, instead of one list per procedure (as in IC) +there is now one list per basic block. +.PP +On the fly, a table is build that maps +every label identifier to the label definition +instruction. +This table is used for computing the control flow. +The table is stored as a dynamically allocated array. +The length of the array is the number of labels +of the current procedure; +this value can be found in the procedure table, +where it was stored by IC. diff --git a/doc/ego/cf/cf2 b/doc/ego/cf/cf2 new file mode 100644 index 00000000..c4dd95d5 --- /dev/null +++ b/doc/ego/cf/cf2 @@ -0,0 +1,50 @@ +.NH 2 +Control Flow +.PP +A \fIsuccessor\fR of a basic block B is a block C +that can be executed immediately after B. +C is said to be a \fIpredecessor\fR of B. +A block ending with a RET instruction +has no successors. +Such a block is called a \fIreturn block\fR. +Any block that has no predecessors cannot be +executed at all (i.e. it is unreachable), +unless it is the first block of a procedure, +called the \fIprocedure entry block\fR. +.PP +Internally, the successor and predecessor +attributes of a basic block are stored as \fIsets\fR. +Alternatively, one may regard all these +sets of all basic blocks as a conceptual \fIgraph\fR, +in which there is an edge from B to C if C +is in the successor set of B. +We call this conceptual graph +the \fIControl Flow Graph\fR. +.PP +The only successor of a basic block ending on an +unconditional branch instruction is the block that +contains the label definition of the target of the jump. +The target instruction can be found via the LAB_ID +that is the operand of the jump instruction, +by using the label-map table mentioned +above. +If the last instruction of a block is a +conditional jump, +the successors are the target block and the textually +next block. +The last instruction can also be a case jump +instruction (CSA or CSB). +We then analyze the case descriptor, +to find all possible target instructions +and their associated blocks. +We require the case descriptor to be allocated in +a ROM, so it cannot be changed dynamically. +A case jump via an alterable descriptor could in principle +go to any label in the program. +In the presence of such an uncontrolled jump, +hardly any optimization can be done. +We do not expect any front end to generate such a descriptor, +however, because of the controlled nature +of case statements in high level languages. +If the basic block does not end in a jump instruction, +its only successor is the textually next block. diff --git a/doc/ego/cf/cf3 b/doc/ego/cf/cf3 new file mode 100644 index 00000000..42e8827b --- /dev/null +++ b/doc/ego/cf/cf3 @@ -0,0 +1,53 @@ +.NH 2 +Immediate dominators +.PP +A basic block B dominates a block C if every path +in the control flow graph from the procedure entry block +to C goes through B. +The immediate dominator of C is the closest dominator +of C on any path from the entry block. +See also +.[~[ +aho compiler design +.], section 13.1.] +.PP +There are a number of algorithms to compute +the immediate dominator relation. +.IP 1. +Purdom and Moore give an algorithm that is +easy to program and easy to describe (although the +description they give is unreadable; +it is given in a very messy Algol60 program full of gotos). +.[ +predominators +.] +.IP 2. +Aho and Ullman present a bitvector algorithm, which is also +easy to program and to understand. +(See +.[~[ +aho compiler design +.], section 13.1.]). +.IP 3 +Lengauer and Tarjan introduce a fast algorithm that is +hard to understand, yet remarkably easy to implement. +.[ +lengauer dominators +.] +.LP +The Purdom-Moore algorithm is very slow if the +number of basic blocks in the flow graph is large. +The Aho-Ullman algorithm in fact computes the +dominator relation, +from which the immediate dominator relation can be computed +in time quadratic to the number of basic blocks, worst case. +The storage requirement is also quadratic to the number +of blocks. +The running time of the third algorithm is proportional +to: +.DS +(number of edges in the graph) * log(number of blocks). +.DE +We have chosen this algorithm because it is fast +(as shown by experiments done by Lengauer and Tarjan), +it is easy to program and requires little data space. diff --git a/doc/ego/cf/cf4 b/doc/ego/cf/cf4 new file mode 100644 index 00000000..ca19394d --- /dev/null +++ b/doc/ego/cf/cf4 @@ -0,0 +1,93 @@ +.NH 2 +Loop detection +.PP +Loops are detected by using the loop construction +algorithm of. +.[~[ +aho compiler design +.], section 13.1.] +This algorithm uses \fIback edges\fR. +A back edge is an edge from B to C in the CFG, +whose head (C) dominates its tail (B). +The loop associated with this back edge +consists of C plus all nodes in the CFG +that can reach B without going through C. +.PP +As an example of how the algorithm works, +consider the piece of program of Fig. 4.1. +First just look at the program and think for +yourself what part of the code constitutes the loop. +.DS +loop + if cond then 1 + -- lots of simple + -- assignment + -- statements 2 3 + exit; -- exit loop + else + S; -- one statement + end if; +end loop; + +Fig. 4.1 A misleading loop +.DE +Although a human being may be easily deceived +by the brackets "loop" and "end loop", +the loop detection algorithm will correctly +reply that only the test for "cond" and +the single statement in the false-part +of the if statement are part of the loop! +The statements in the true-part only get +executed once, so there really is no reason at all +to say they're part of the loop too. +The CFG contains one back edge, "3->1". +As node 3 cannot be reached from node 2, +the latter node is not part of the loop. +.PP +A source of problems with the algorithm is the fact +that different back edges may result in +the same loop. +Such an ill-structured loop is +called a \fImessy\fR loop. +After a loop has been constructed, it is checked +if it is really a new loop. +.PP +Loops can partly overlap, without one being nested +inside the other. +This is the case in the program of Fig. 4.2. +.DS +1: 1 + S1; +2: + S2; 2 + if cond then + goto 4; + S3; 3 4 + goto 1; +4: + S4; + goto 1; + +Fig. 4.2 Partly overlapping loops +.DE +There are two back edges "3->1" and "4->1", +resulting in the loops {1,2,3} and {1,2,4}. +With every basic block we associate a set of +all loops it is part of. +It is not sufficient just to record its +most enclosing loop. +.PP +After all loops of a procedure are detected, we determine +the nesting level of every loop. +Finally, we find all strong and firm blocks of the loop. +If the loop has only one back edge (i.e. it is not messy), +the set of firm blocks consists of the +head of this back edge and its dominators +in the loop (including the loop entry block). +A firm block is also strong if it is not a +successor of a block that may exit the loop; +a block may exit a loop if it has an (immediate) successor +that is not part of the loop. +For messy loops we do not determine the strong +and firm blocks. These loops are expected +to occur very rarely. diff --git a/doc/ego/cf/cf5 b/doc/ego/cf/cf5 new file mode 100644 index 00000000..8ef4d11c --- /dev/null +++ b/doc/ego/cf/cf5 @@ -0,0 +1,79 @@ +.NH 2 +Interprocedural analysis +.PP +It is often desirable to know the effects +a procedure call may have. +The optimization below is only possible if +we know for sure that the call to P cannot +change A. +.DS +A := 10; A:= 10; +P; -- procedure call --> P; +B := A + 2; B := 12; +.DE +Although it is not possible to predict exactly +all the effects a procedure call has, we may +determine a kind of upper bound for it. +So we compute all variables that may be +changed by P, although they need not be +changed at every invocation of P. +We can get hold of this set by just looking +at all assignment (store) instructions +in the body of P. +EM also has a set of \fIindirect\fR assignment +instructions, +i.e. assignment through a pointer variable. +In general, it is not possible to determine +which variable is affected by such an assignment. +In these cases, we just record the fact that P +does an indirect assignment. +Note that this does not mean that all variables +are potentially affected, as the front ends +may generate messages telling that certain +variables can never be accessed indirectly. +We also set a flag if P does a use (load) indirect. +Note that we only have to look at \fIglobal\fR +variables. +If P changes or uses any of its locals, +this has no effect on its environment. +Local variables of a lexically enclosing +procedure can only be accessed indirectly. +.PP +A procedure P may of course call another procedure. +To determine the effects of a call to P, +we also must know the effects of a call to the second procedure. +This second one may call a third one, and so on. +Effectively, we need to compute the \fItransitive closure\fR +of the effects. +To do this, we determine for every procedure +which other procedures it calls. +This set is the "calling" attribute of a procedure. +One may regard all these sets as a conceptual graph, +in which there is an edge from P to Q +if Q is in the calling set of P. This graph will +be referred to as the \fIcall graph\fR. +(Note the resemblance with the control flow graph). +.PP +We can detect which procedures are called by P +by looking at all CAL instructions in its body. +Unfortunately, a procedure may also be +called indirectly, via a CAI instruction. +Yet, only procedures that are used as operand of an LPI +instruction can be called indirect, +because this is the only way to take the address of a procedure. +We determine for every procedure whether it does +a CAI instruction. +We also build a set of all procedures used as +operand of an LPI. +.sp +After all procedures have been processed (i.e. all CFGs +are constructed, all loops are detected, +all procedures are analyzed to see which variables +they may change, which procedures they call, +whether they do a CAI or are used in an LPI) the +transitive closure of all interprocedural +information is computed. +During the same process, +the calling set of every procedure that uses a CAI +is extended with the above mentioned set of all +procedures that can be called indirect. diff --git a/doc/ego/cf/cf6 b/doc/ego/cf/cf6 new file mode 100644 index 00000000..a560b48e --- /dev/null +++ b/doc/ego/cf/cf6 @@ -0,0 +1,21 @@ +.NH 2 +Source files +.PP +The sources of CF are in the following files and packages: +.IP cf.h: 14 +declarations of global variables and data structures +.IP cf.c: +the routine main; interprocedural analysis; +transitive closure +.IP succ: +control flow (successor and predecessor) +.IP idom: +immediate dominators +.IP loop: +loop detection +.IP get: +read object and procedure table; +read EM text and partition it into basic blocks +.IP put: +write tables, CFGs and EM text +.LP diff --git a/doc/ego/cj/.distr b/doc/ego/cj/.distr new file mode 100644 index 00000000..a92acc3b --- /dev/null +++ b/doc/ego/cj/.distr @@ -0,0 +1 @@ +cj1 diff --git a/doc/ego/cj/cj1 b/doc/ego/cj/cj1 new file mode 100644 index 00000000..c725a786 --- /dev/null +++ b/doc/ego/cj/cj1 @@ -0,0 +1,136 @@ +.bp +.NH 1 +Cross jumping +.NH 2 +Introduction +.PP +The "Cross Jumping" optimization technique (CJ) +.[ +wulf design optimizing compiler +.] +is basically a space optimization technique. It looks for pairs of +basic blocks (B1,B2), for which: +.DS +SUCC(B1) = SUCC(B2) = {S} +.DE +(So B1 and B2 both have one and the same successor). +If the last few non-branch instructions are the same for B1 and B2, +one such sequence can be eliminated. +.DS +Pascal: + +if cond then + S1 + S3 +else + S2 + S3 + +(pseudo) EM: + +TEST COND TEST COND +BNE *1 BNE *1 +S1 S1 +S3 ---> BRA *2 +BRA *2 1: +1: S2 +S2 2: +S3 S3 +2: + +Fig. 9.1 An example of Cross Jumping +.DE +As the basic blocks have the same successor, +at least one of them ends in an unconditional branch instruction (BRA). +Hence no extra branch instruction is ever needed, just the target +of an existing branch needs to be changed; neither the program size +nor the execution time will ever increase. +In general, the execution time will remain the same, unless +further optimizations can be applied because of this optimization. +.PP +This optimization is particularly effective, +because it cannot always be done by the programmer at the source level, +as demonstrated by the Fig. 8.2. +.DS + Pascal: + + if cond then + x := f(4) + else + x := g(5) + + + EM: + + ... ... + LOC 4 LOC 5 + CAL F CAL G + ASP 2 ASP 2 + LFR 2 LFR 2 + STL X STL X + +Fig. 9.2 Effectiveness of Cross Jumping +.DE +At the source level there is no common tail, +but at the EM level there is a common tail. +.NH 2 +Implementation +.PP +The implementation of cross jumping is rather straightforward. +The technique is applied to one procedure at a time. +The control flow graph of the procedure +is scanned for pairs of basic blocks +with the same (single) successor and with common tails. +Note that there may be more than two such blocks (e.g. as the result +of a case statement). +This is dealt with by repeating the entire process until no +further optimizations can de done for the current procedure. +.sp +If a suitable pair of basic blocks has been found, the control flow +graph must be altered. One of the basic +blocks must be split into two. +The control flow graphs before and after the optimization are shown +in Fig. 9.3 and Fig. 9.4. +.DS + + -------- -------- + | | | | + | S1 | | S2 | + | S3 | | S3 | + | | | | + -------- -------- + | | + |------------------|--------------------| + | + v + +Fig. 9.3 CFG before optimization +.DE +.DS + + -------- -------- + | | | | + | S1 | | S2 | + | | | | + -------- -------- + | | + |--------------------<------------------| + v + -------- + | | + | S3 | + | | + -------- + | + v + +Fig. 9.4 CFG after optimization +.DE +Some attributes of the three resulting blocks (such as immediate dominator) +are updated. +.PP +In some cases, cross jumping might split the computation of an expression +into two, by inserting a branch somewhere in the middle. +Most code generators will generate very poor assembly code when +presented with such EM code. +Therefor, cross jumping is not performed in these cases. diff --git a/doc/ego/cs/.distr b/doc/ego/cs/.distr new file mode 100644 index 00000000..99b8c779 --- /dev/null +++ b/doc/ego/cs/.distr @@ -0,0 +1,5 @@ +cs1 +cs2 +cs3 +cs4 +cs5 diff --git a/doc/ego/cs/cs1 b/doc/ego/cs/cs1 new file mode 100644 index 00000000..d1cc8ceb --- /dev/null +++ b/doc/ego/cs/cs1 @@ -0,0 +1,42 @@ +.bp +.NH 1 +Common subexpression elimination +.NH 2 +Introduction +.PP +The Common Subexpression Elimination optimization technique (CS) +tries to eliminate multiple computations of EM expressions +that yield the same result. +It places the result of one such computation +in a temporary variable, +and replaces the other computations by a reference +to this temporary variable. +The primary goal of this technique is to decrease +the execution time of the program, +but in general it will save space too. +.PP +As an example of the application of Common Subexpression Elimination, +consider the piece of program in Fig. 7.1(a). +.DS +x := a * b; TMP := a * b; x := a * b; +CODE; x := TMP; CODE +y := c + a * b; CODE y := x; + y := c + TMP; + + (a) (b) (c) + +Fig. 7.1 Examples of Common Subexpression Elimination +.DE +If neither a nor b is changed in CODE, +the instructions can be replaced by those of Fig. 7.1(b), +which saves one multiplication, +but costs an extra store instruction. +If the value of x is not changed in CODE either, +the instructions can be replaced by those of Fig. 7.1(c). +In this case +the extra store is not needed. +.PP +In the following sections we will describe +which transformations are done +by CS and how this phase +was implemented. diff --git a/doc/ego/cs/cs2 b/doc/ego/cs/cs2 new file mode 100644 index 00000000..90877aa9 --- /dev/null +++ b/doc/ego/cs/cs2 @@ -0,0 +1,83 @@ +.NH 2 +Specification of the Common Subexpression Elimination phase +.PP +In this section we will describe +the window +through which CS examines the code, +the expressions recognized by CS, +and finally the changes made to the code. +.NH 3 +The working window +.PP +The CS algorithm is applied to the +largest sequence of textually adjacent basic blocks +B1,..,Bn, for which +.DS +PRED(Bj) = {Bj-1}, j = 2,..,n. +.DE +Intuitively, this window consists of straight line code, +with only one entry point (at the beginning); it may +contain jumps, which should all have their targets outside the window. +This is illustrated in Fig. 7.2. +.DS +x := a * b; (1) +if x < 10 then (2) + y := a * b; (3) + +Fig. 7.2 The working window of CS +.DE +Line (2) can only be executed after line (1). +Likewise, line (3) can only be executed after +line (2). +Both a and b have the same values at line (1) and at line (3). +.PP +Larger windows were avoided. +In Fig. 7.3, the value of a at line (4) may have been obtained +at more than one point. +.DS +x := a * b; (1) +if x < 10 then (2) + a := 100; (3) +y := a * b; (4) + +Fig. 7.3 Several working windows +.DE +.NH 3 +Recognized expressions. +.PP +The computations eliminated by CS need not be normal expressions +(like "a * b"), +but can even consist of a single operand that is expensive to access, +such as an array element or a record field. +If an array element is used, +its address is computed implicitly. +CS is able to eliminate either the element itself or its +address, whichever one is most profitable. +A variable of a textually enclosing procedure may also be +expensive to access, depending on the lexical level difference. +.NH 3 +Transformations +.PP +CS creates a new temporary local variable (TMP) +for every eliminated expression, +unless it is able to use an existing local variable. +It emits code to initialize this variable with the +result of the expression. +Most recurrences of the expression +can simply be replaced by a reference to TMP. +If the address of an array element is recognized as +a common subexpression, +references to the element itself are replaced by +indirect references through TMP (see Fig. 7.4). +.DS +x := A[i]; TMP := &A[i]; + . . . --> x := *TMP; +A[i] := y; . . . + *TMP := y; + +Fig. 7.4 Elimination of an array address computation +.DE +Here, '&' is the 'address of' operator, +and unary '*' is the indirection operator. +(Note that EM actually has different instructions to do +a use-indirect or an assign-indirect.) diff --git a/doc/ego/cs/cs3 b/doc/ego/cs/cs3 new file mode 100644 index 00000000..2d84a83e --- /dev/null +++ b/doc/ego/cs/cs3 @@ -0,0 +1,243 @@ +.NH 2 +Implementation +.PP +.NH 3 +The value number method +.PP +To determine whether two expressions have the same result, +there must be some way to determine whether their operands have +the same values. +We use a system of \fIvalue numbers\fP +.[ +kennedy data flow analysis +.] +in which each distinct value of whatever type, +created or used within the working window, +receives a unique identifying number, its value number. +Two items have the same value number if and only if, +based only upon information from the instructions in the window, +their values are provably identical. +For example, after processing the statement +.DS +a := 4; +.DE +the variable a and the constant 4 have the same value number. +.PP +The value number of the result of an expression depends only +on the kind of operator and the value number(s) of the operand(s). +The expressions need not be textually equal, as shown in Fig. 7.5. +.DS +a := c; (1) +use(a * b); (2) +d := b; (3) +use(c * d); (4) + +Fig. 7.5 Different expressions with the same value number +.DE +At line (1) a receives the same value number as c. +At line (2) d receives the same value number as b. +At line (4) the expression "c * d" receives the same value number +as the expression "a * b" at line (2), +because the value numbers of their left and right operands are the same, +and the operator (*) is the same. +.PP +As another example of the value number method, consider Fig. 7.6. +.DS +use(a * b); (1) +a := 123; (2) +use(a * b); (3) + +Fig. 7.6 Identical expressions with the different value numbers +.DE +Although textually the expressions "a * b" in line 1 and line 3 are equal, +a will have different value numbers at line 3 and line 1. +The two expressions will not mistakenly be recognized as equivalent. +.NH 3 +Entities +.PP +The Value Number Method distinguishes between operators and operands. +The value numbers of operands are stored in a table, +called the \fIsymbol table\fR. +The value number of a subexpression depends on the +(root) operator of the expression and on the value numbers +of its operands. +A table of "available expressions" is used to do this mapping. +.PP +CS recognizes the following kinds of EM operands, called \fIentities\fR: +.IP +- constant +- local variable +- external variable +- indirectly accessed entity +- offsetted entity +- address of local variable +- address of external variable +- address of offsetted entity +- address of local base +- address of argument base +- array element +- procedure identifier +- floating zero +- local base +- heap pointer +- ignore mask +.LP +Whenever a new entity is encountered in the working window, +it is entered in the symbol table and given a brand new value number. +Most entities have attributes (e.g. the offset in +the current stackframe for local variables), +which are also stored in the symbol table. +.PP +An entity is called static if its value cannot be changed +(e.g. a constant or an address). +.NH 3 +Parsing expressions +.PP +Common subexpressions are recognized by simulating the behaviour +of the EM machine. +The EM code is parsed from left to right; +as EM is postfix code, this is a bottom up parse. +At any point the current state of the EM runtime stack is +reflected by a simulated "fake stack", +containing descriptions of the parsed operands and expressions. +A descriptor consists of: +.DS +(1) the value number of the operand or expression +(2) the size of the operand or expression +(3) a pointer to the first line of EM-code + that constitutes the operand or expression +.DE +Note that operands may consist of several EM instructions. +Whenever an operator is encountered, the +descriptors of its operands are on top of the fake stack. +The operator and the value numbers of the operands +are used as indices in the table of available expressions, +to determine the value number of the expression. +.PP +During the parsing process, +we keep track of the first line of each expression; +we need this information when we decide to eliminate the expression. +.NH 3 +Updating entities +.PP +An entity is assigned a value number when it is +used for the first time +in the working window. +If the entity is used as left hand side of an assignment, +it gets the value number of the right hand side. +Sometimes the effects of an instruction on an entity cannot +be determined exactly; +the current value and value number of the entity may become +inconsistent. +Hence the current value number must be forgotten. +This is achieved by giving the entity a new value number +that was not used before. +The entity is said to be \fIkilled\fR. +.PP +As information is lost when an entity is killed, +CS tries to save as many entities as possible. +In case of an indirect assignment through a pointer, +some analysis is done to see which variables cannot be altered. +For a procedure call, the interprocedural information contained +in the procedure table is used to restrict the set of entities that may +be changed by the call. +Local variables for which the front end generated +a register message can never be changed by an indirect assignment +or a procedure call. +.NH 3 +Changing the EM text +.PP +When a new expression comes available, +it is checked whether its result is saved in a local +that may go in a register. +The last line of the expression must be followed +by a STL or SDL instruction +(depending on the size of the result) +and a register message must be present for +this local. +If there is such a local, +it is recorded in the available expressions table. +Each time a new occurrence of this expression +is found, +the value number of the local is compared against +the value number of the result. +If they are different the local cannot be used and is forgotten. +.PP +The available expressions are linked in a list. +New expressions are linked at the head of the list. +In this way expressions that are contained within other +expressions appear later in the list, +because EM-expressions are postfix. +The elimination process walks through the list, +starting at the head, to find the largest expressions first. +If an expression is eliminated, +any expression later on in the list, contained in the former expression, +is removed from the list, +as expressions can only be eliminated once. +.PP +A STL or SDL is emitted after the first occurrence of the expression, +unless there was an existing local variable that could hold the result. +.NH 3 +Desirability analysis +.PP +Although the global optimizer works on EM code, +the goal is to improve the quality of the object code. +Therefore some machine-dependent information is needed +to decide whether it is desirable to +eliminate a given expression. +Because it is impossible for the CS phase to know +exactly what code will be generated, +some heuristics are used. +CS essentially looks for some special cases +that should not be eliminated. +These special cases can be turned on or off for a given machine, +as indicated in a machine descriptor file. +.PP +Some operators can sometimes be translated +into an addressing mode for the machine at hand. +Such an operator is only eliminated +if its operand is itself expensive, +i.e. it is not just a simple load. +The machine descriptor file contains a set of such operators. +.PP +Eliminating the loading of the Local Base or +the Argument Base by the LXL resp. LXA instruction +is only beneficial if the difference in lexical levels +exceeds a certain threshold. +The machine descriptor file contains this threshold. +.PP +Replacing a SAR or a LAR by an AAR followed by a LOI +may possibly increase the size of the object code. +We assume that this is only possible when the +size of the array element is greater than some limit. +.PP +There are back ends that can very efficiently translate +the index computing instruction sequence LOC SLI ADS. +If this is the case, +the SLI instruction between a LOC +and an ADS is not eliminated. +.PP +To handle unforseen cases, the descriptor file may also contain +a set of operators that should never be eliminated. +.NH 3 +The algorithm +.PP +After these preparatory explanations, +the algorithm itself is easy to understand. +For each instruction within the current window, +the following steps are performed in the given order : +.IP 1. +Check if this instruction defines an entity. +If so, the set of entities is updated accordingly. +.IP 2. +Kill all entities that might be affected by this instruction. +.IP 3. +Simulate the instruction on the fake-stack. +If this instruction is an operator, +update the list of available expressions accordingly. +.PP +The result of this process is +a list of available expressions plus the information +needed to eliminate them. +Expressions that are desirable to eliminate are eliminated. +Next, the window is shifted and the process is repeated. diff --git a/doc/ego/cs/cs4 b/doc/ego/cs/cs4 new file mode 100644 index 00000000..54362183 --- /dev/null +++ b/doc/ego/cs/cs4 @@ -0,0 +1,305 @@ +.NH 2 +Implementation. +.PP +In this section we will discuss the implementation of the CS phase. +We will first describe the basic actions that are undertaken +by the algorithm, than the algorithm itself. +.NH 3 +Partioning the EM instructions +.PP +There are over 100 EM instructions. +For our purpose we partition this huge set into groups of +instructions which can be more or less conveniently handled together. +.PP +There are groups for all sorts of load instructions: +simple loads, expensive loads, loads of an array element. +A load is considered \fIexpensive\fP when more than one EM instructions +are involved in loading it. +The load of a lexical entity is also considered expensive. +For instance: LOF is expensive, LAL is not. +LAR forms a group on its own, +because it is not only an expensive load, +but also implicitly includes the ternary operator AAR, +which computes the address of the array element. +.PP +There are groups for all sorts of operators: +unary, binary, and ternary. +The groups of operators are further partitioned according to the size +of their operand(s) and result. +\" .PP +\" The distinction between operators and expensive loads is not always clear. +\" The ADP instruction for example, +\" might seem a unary operator because it pops one item +\" (a pointer) from the stack. +\" However, two ADP-instructions which pop an item with the same value number +\" need not have the same result, +\" because the attributes (an offset, to be added to the pointer) +\" can be different. +\" Is it then a binary operator? +\" That would give rise to the strange, and undesirable, +\" situation that some binary operators pop two operands +\" and others pop one. +\" The conclusion is inevitable: +\" we have been fooled by the name (ADd Pointer). +\" The ADP-instruction is an expensive load. +\" In this context LAF, meaning Load Address of oFfsetted, +\" would have been a better name, +\" corresponding to LOF, like LAL, +\" Load Address of Local, corresponds to LOL. +.PP +There are groups for all sorts of stores: +direct, indirect, array element. +The SAR forms a group on its own for the same reason +as appeared with LAR. +.PP +The effect of the remaining instructions is less clear. +They do not help very much in parsing expressions or +in constructing our pseudo symboltable. +They are partitioned according to the following criteria: +.RS +.IP "-" +They change the value of an entity without using the stack +(e.g. ZRL, DEE). +.IP "-" +They are subroutine calls (CAI, CAL). +.IP "-" +They change the stack in some irreproduceable way (e.g. ASP, LFR, DUP). +.IP "-" +They have no effect whatever on the stack or on the entities. +This does not mean they can be deleted, +but they can be ignored for the moment +(e.g. MES, LIN, NOP). +.IP "-" +Their effect is too complicate too compute, +so we just assume worst case behaviour. +Hopefully, they do not occur very often. +(e.g. MON, STR, BLM). +.IP "-" +They signal the end of the basic block (e.g. BLT, RET, TRP). +.RE +.NH 3 +Parsing expressions +.PP +To recognize expressions, +we simulate the behaviour of the EM machine, +by means of a fake-stack. +When we scan the instructions in sequential order, +we first encounter the instructions that load +the operands on the stack, +and then the instruction that indicates the operator, +because EM expressions are postfix. +When we find an instruction to load an operand, +we load on the fake-stack a struct with the following information: +.DS +(1) the value number of the operand +(2) the size of the operand +(3) a pointer to the first line of EM-code + that constitutes the operand +.DE +In most cases, (3) will point to the line +that loaded the operand (e.g. LOL, LOC), +i.e. there is only one line that refers to this operand, +but sometimes some information must be popped +to load the operand (e.g. LOI, LAR). +This information must have been pushed before, +so we also pop a pointer to the first line that pushed +the information. +This line is now the first line that defines the operand. +.PP +When we find the operator instruction, +we pop its operand(s) from the fake-stack. +The first line that defines the first operand is +now the first line of the expression. +We now have all information to determine +whether the just parsed expression has occurred before. +We also know the first and last line of the expression; +we need this when we decide to eliminate it. +Associated with each available expression is a set of +which the elements contains the first and last line of +a recurrence of this expression. +.PP +Not only will the operand(s) be popped from the fake-stack, +but the following will be pushed: +.DS +(1) the value number of the result +(2) the size of the result +(3) a pointer to the first line of the expression +.DE +In this way an item on the fake-stack always contains +the necessary information. +As you see, EM expressions are parsed bottum up. +.NH 3 +Updating entities +.PP +As said before, +we build our private "symboltable", +while scanning the EM-instructions. +The behaviour of the EM-machine is not only reflected +in the fake-stack, +but also in the entities. +When an entity is created, +we do not yet know its value, +so we assign a brand new value number to it. +Each time a store-instruction is encountered, +we change the value number of the target entity of this store +to the value number of the token that was popped +from the fake-stack. +Because entities may overlap, +we must also "forget" the value numbers of entities +that might be affected by this store. +Each such entity will be \fIkilled\fP, +i.e. assigned a brand new valuenumber. +.PP +Because we lose information when we forget +the value number of an entity, +we try to save as much entities as possible. +When we store into an external, +we don't have to kill locals and vice versa. +Furthermore, we can see whether two locals or +two externals overlap, +because we know the offset from the local base, +resp. the offset within the data block, +and the size. +The situation becomes more complicated when we have +to consider indirection. +The worst case is that we store through an unknown pointer. +In that case we kill all entities except those locals +for which a so-called \fIregister message\fP has been generated; +this register message indicates that this local can never be +accessed indirectly. +If we know this pointer we can be more careful. +If it points to a local then the entity that is accessed through +this pointer can never overlap with an external. +If it points to an external this entity can never overlap with a local. +Furthermore, in the latter case, +we can find the data block this entity belongs to. +Since pointer arithmetic is only defined within a data block, +this entity can never overlap with entities that are known to +belong to another data block. +.PP +Not only after a store-instruction but also after a +subroutine-call it may be necessary to kill entities; +the subroutine may affect global variables or store +through a pointer. +If a subroutine is called that is not available as EM-text, +we assume worst case behaviour, +i.e. we kill all entities without register message. +.NH 3 +Additions and replacements. +.PP +When a new expression comes available, +we check whether the result is saved in a local +that may go in a register. +The last line of the expression must be followed +by a STL or SDL instruction, +depending on the size of the result +(resp. WS and 2*WS), +and a register message must be present for +this local. +If we have found such a local, +we store a pointer to it with the available expression. +Each time a new occurrence of this expression +is found, +we compare the value number of the local against +the value number of the result. +When they are different we remove the pointer to it, +because we cannot use it. +.PP +The available expressions are singly linked in a list. +When a new expression comes available, +we link it at the head of the list. +In this way expressions that are contained within other +expressions appear later in the list, +because EM-expressions are postfix. +When we are going to eliminate expressions, +we walk through the list, +starting at the head, to find the largest expressions first. +When we decide to eliminate an expression, +we look at the expressions in the tail of the list, +starting from where we are now, +to delete expressions that are contained within +the chosen one because +we cannot eliminate an expression more than once. +.PP +When we are going to eliminate expressions, +and we do not have a local that holds the result, +we emit a STL or SDL after the line where the expression +was first found. +The other occurrences are simply removed, +unless they contain instructions that not only have +effect on the stack; e.g. messages, stores, calls. +Before each instruction that needs the result on the stack, +we emit a LOL or LDL. +When the expression was an AAR, +but the instruction was a LAR or a SAR, +we append a LOI resp. a STI of the number of bytes +in an array-element after each LOL/LDL. +.NH 3 +Desirability analysis +.PP +Although the global optimizer works on EM code, +the goal is to improve the quality of the object code. +Therefore we need some machine dependent information +to decide whether it is desirable to +eliminate a given expression. +Because it is impossible for the CS phase to know +exactly what code will be generated, +we use some heuristics. +In most cases it will save time when we eliminate an +operator, so we just do it. +We only look for some special cases. +.PP +Some operators can in some cases be translated +into an addressing mode for the machine at hand. +We only eliminate such an operator, +when its operand is itself "expensive", +i.e. not just a simple load. +The user of the CS phase has to supply +a set of such operators. +.PP +Eliminating the loading of the Local Base or +the Argument Base by the LXL resp. LXA instruction +is only beneficial when the number of lexical levels +we have to go back exceeds a certain threshold. +This threshold will be different when registers +are saved by the back end. +The user must supply this threshold. +.PP +Replacing a SAR or a LAR by an AAR followed by a LOI +may possibly increase the size of the object code. +We assume that this is only possible when the +size of the array element is greater than some +(user-supplied) limit. +.PP +There are back ends that can very efficiently translate +the index computing instruction sequence LOC SLI ADS. +If this is the case, +we do not eliminate the SLI instruction between a LOC +and an ADS. +.PP +To handle unforeseen cases, the user may also supply +a set of operators that should never be eliminated. +.NH 3 +The algorithm +.PP +After these preparatory explanations, +we can be short about the algorithm itself. +For each instruction within our window, +the following steps are performed in the order given: +.IP 1. +We check if this instructin defines an entity. +If this is the case the set of entities is updated accordingly. +.IP 2. +We kill all entities that might be affected by this instruction. +.IP 3. +The instruction is simulated on the fake-stack. +Copy propagation is done. +If this instruction is an operator, +we update the list of available expressions accordingly. +.PP +When we have processed all instructions this way, +we have built a list of available expressions plus the information we +need to eliminate them. +Those expressions of which desirability analysis tells us so, +we eliminate. +The we shift our window and continue. diff --git a/doc/ego/cs/cs5 b/doc/ego/cs/cs5 new file mode 100644 index 00000000..eaf88402 --- /dev/null +++ b/doc/ego/cs/cs5 @@ -0,0 +1,46 @@ +.NH 2 +Source files of CS +.PP +The sources of CS are in the following files and packages: +.IP cs.h 14 +declarations of global variables and data structures +.IP cs.c +the routine main; +a driving routine to process +the basic blocks in the right order +.IP vnm +implements a procedure that performs +the value numbering on one basic block +.IP eliminate +implements a procedure that does the +transformations, if desirable +.IP avail +implements a procedure that manipulates the list of available expressions +.IP entity +implements a procedure that manipulates the set of entities +.IP getentity +implements a procedure that extracts the +pseudo symboltable information from EM-instructions; +uses a small table +.IP kill +implements several routines that find the entities +that might be changed by EM-instructions +and kill them +.IP partition +implements several routines that partition the huge set +of EM-instructions into more or less manageable, +more or less logical chunks +.IP profit +implements a procedure that decides whether it +is advantageous to eliminate an expression; +also removes expressions with side-effects +.IP stack +implements the fake-stack and operations on it +.IP alloc +implements several allocation routines +.IP aux +implements several auxiliary routines +.IP debug +implements several routines to provide debugging +and verbose output +.LP diff --git a/doc/ego/ic/.distr b/doc/ego/ic/.distr new file mode 100644 index 00000000..eabb4147 --- /dev/null +++ b/doc/ego/ic/.distr @@ -0,0 +1,5 @@ +ic1 +ic2 +ic3 +ic4 +ic5 diff --git a/doc/ego/ic/ic1 b/doc/ego/ic/ic1 new file mode 100644 index 00000000..6347bc73 --- /dev/null +++ b/doc/ego/ic/ic1 @@ -0,0 +1,57 @@ +.bp +.NH +The Intermediate Code and the IC phase +.PP +In this chapter the intermediate code of the EM global optimizer +will be defined. +The 'Intermediate Code construction' phase (IC), +which builds the initial intermediate code from +EM Compact Assembly Language, +will be described. +.NH 2 +Introduction +.PP +The EM global optimizer is a multi pass program, +hence there is a need for an intermediate code. +Usually, programs in the Amsterdam Compiler Kit use the +Compact Assembly Language format +.[~[ +keizer architecture +.], section 11.2] +for this purpose. +Although this code has some convenient features, +such as being compact, +it is quite unsuitable in our case, +because of a number of reasons. +At first, the code lacks global information +about whole procedures or whole basic blocks. +Second, it uses identifiers ('names') to bind +defining and applied occurrences of +procedures, data labels and instruction labels. +Although this is usual in high level programming +languages, it is awkward in an intermediate code +that must be read many times. +Each pass of the optimizer would have +to incorporate an identifier look-up mechanism +to associate a defining occurrence with each +applied occurrence of an identifier. +Finally, EM programs are used to declare blocks of bytes, +rather than variables. A 'hol 6' instruction may be used to +declare three 2-byte variables. +Clearly, the optimizer wants to deal with variables, and +not with rows of bytes. +.PP +To overcome these problems, we have developed a new +intermediate code. +This code does not merely consist of the EM instructions, +but also contains global information in the +form of tables and graphs. +Before describing the intermediate code we will +first leap aside to outline +the problems one generally encounters +when trying to store complex data structures such as +graphs outside the program, i.e. in a file. +We trust this will enhance the +comprehensibility of the +intermediate code definition and the design and implementation +of the IC phase. diff --git a/doc/ego/ic/ic2 b/doc/ego/ic/ic2 new file mode 100644 index 00000000..13715626 --- /dev/null +++ b/doc/ego/ic/ic2 @@ -0,0 +1,146 @@ +.NH 2 +Representation of complex data structures in a sequential file +.PP +Most programmers are quite used to deal with +complex data structures, such as +arrays, graphs and trees. +There are some particular problems that occur +when storing such a data structure +in a sequential file. +We call data that is kept in +main memory +.UL internal +,as opposed to +.UL external +data +that is kept in a file outside the program. +.sp +We assume a simple data structure of a +scalar type (integer, floating point number) +has some known external representation. +An +.UL array +having elements of a scalar type can be represented +externally easily, by successively +representing its elements. +The external representation may be preceded by a +number, giving the length of the array. +Now, consider a linear, singly linked list, +the elements of which look like: +.DS +record + data: scalar_type; + next: pointer_type; +end; +.DE +It is significant to note that the "next" +fields of the elements only have a meaning within +main memory. +The field contains the address of some location in +main memory. +If a list element is written to a file in +some program, +and read by another program, +the element will be allocated at a different +address in main memory. +Hence this address value is completely +useless outside the program. +.sp +One may represent the list by ignoring these "next" fields +and storing the data items in the order they are linked. +The "next" fields are represented \fIimplicitly\fR. +When the file is read again, +the same list can be reconstructed. +In order to know where the external representation of the +list ends, +it may be useful to put the length of +the list in front of it. +.sp +Note that arrays and linear lists have the +same external representation. +.PP +A doubly linked, linear list, +with elements of the type: +.DS +record + data: scalar_type; + next, + previous: pointer_type; +end +.DE +can be represented in precisely the same way. +Both the "next" and the "previous" fields are represented +implicitly. +.PP +Next, consider a binary tree, +the nodes of which have type: +.DS +record + data: scalar_type; + left, + right: pointer_type; +end +.DE +Such a tree can be represented sequentially, +by storing its nodes in some fixed order, e.g. prefix order. +A special null data item may be used to +denote a missing left or right son. +For example, let the scalar type be integer, +and let the null item be 0. +Then the tree of fig. 3.1(a) +can be represented as in fig. 3.1(b). +.DS + 4 + + 9 12 + + 12 3 4 6 + + 8 1 5 1 + +Fig. 3.1(a) A binary tree + + +4 9 12 0 0 3 8 0 0 1 0 0 12 4 0 5 0 0 6 1 0 0 0 + +Fig. 3.1(b) Its sequential representation +.DE +We are still able to represent the pointer fields ("left" +and "right") implicitly. +.PP +Finally, consider a general +.UL graph +, where each node has a "data" field and +pointer fields, +with no restriction on where they may point to. +Now we're at the end of our tale. +There is no way to represent the pointers implicitly, +like we did with lists and trees. +In order to represent them explicitly, +we use the following scheme. +Every node gets an extra field, +containing some unique number that identifies the node. +We call this number its +.UL id. +A pointer is represented externally as the id of the node +it points to. +When reading the file we use a table that maps +an id to the address of its node. +In general this table will not be completely filled in +until we have read the entire external representation of +the graph and allocated internal memory locations for +every node. +Hence we cannot reconstruct the graph in one scan. +That is, there may be some pointers from node A to B, +where B is placed after A in the sequential file than A. +When we read the node of A we cannot map the id of B +to the address of node B, +as we have not yet allocated node B. +We can overcome this problem if the size +of every node is known in advance. +In this case we can allocate memory for a node +on first reference. +Else, the mapping from id to pointer +cannot be done while reading nodes. +The mapping can be done either in an extra scan +or at every reference to the node. diff --git a/doc/ego/ic/ic3 b/doc/ego/ic/ic3 new file mode 100644 index 00000000..d98e8233 --- /dev/null +++ b/doc/ego/ic/ic3 @@ -0,0 +1,414 @@ +.NH 2 +Definition of the intermediate code +.PP +The intermediate code of the optimizer consists +of several components: +.IP - +the object table +.IP - +the procedure table +.IP - +the em code +.IP - +the control flow graphs +.IP - +the loop table +.LP - +.PP +These components are described in +the next sections. +The syntactic structure of every component +is described by a set of context free syntax rules, +with the following conventions: +.DS +x a non-terminal symbol +A a terminal symbol (in capitals) +x: a b c; a grammar rule +a | b a or b +(a)+ 1 or more occurrences of a +{a} 0 or more occurrences of a +.DE +.NH 3 +The object table +.PP +EM programs declare blocks of bytes rather than (global) variables. +A typical program may declare 'HOL 7780' +to allocate space for 8 I/O buffers, +2 large arrays and 10 scalar variables. +The optimizer wants to deal with +.UL objects +like variables, buffers and arrays +and certainly not with huge numbers of bytes. +Therefore the intermediate code contains information +about which global objects are used. +This information can be obtained from an EM program +by just looking at the operands of instruction +such as LOE, LAE, LDE, STE, SDE, INE, DEE and ZRE. +.PP +The object table consists of a list of +.UL datablock +entries. +Each such entry represents a declaration like HOL, BSS, +CON or ROM. +There are five kinds of datablock entries. +The fifth kind, +UNKNOWN, denotes a declaration in a +separately compiled file that is not made +available to the optimizer. +Each datablock entry contains the type of the block, +its size, and a description of the objects that +belong to it. +If it is a rom, +it also contains a list of values given +as arguments to the rom instruction, +provided that this list contains only integer numbers. +An object has an offset (within its datablock) +and a size. +The size need not always be determinable. +Both datablock and object contain a unique +identifying number +(see previous section for their use). +.DS +.UL syntax + object_table: + {datablock} ; + datablock: + D_ID -- unique identifying number + PSEUDO -- one of ROM,CON,BSS,HOL,UNKNOWN + SIZE -- # bytes declared + FLAGS + {value} -- contents of rom + {object} ; -- objects of the datablock + object: + O_ID -- unique identifying number + OFFSET -- offset within the datablock + SIZE ; -- size of the object in bytes + value: + argument ; +.DE +A data block has only one flag: "external", indicating +whether the data label is externally visible. +The syntax for "argument" will be given later on +(see em_text). +.NH 3 +The procedure table +.PP +The procedure table contains global information +about all procedures that are made available +to the optimizer +and that are needed by the EM program. +(Library units may not be needed, see section 3.5). +The table has one entry for +every procedure. +.DS +.UL syntax + procedure_table: + {procedure} + procedure: + P_ID -- unique identifying number + #LABELS -- number of instruction labels + #LOCALS -- number of bytes for locals + #FORMALS -- number of bytes for formals + FLAGS -- flag bits + calling -- procedures called by this one + change -- info about global variables changed + use ; -- info about global variables used + calling: + {P_ID} ; -- procedures called + change: + ext -- external variables changed + FLAGS ; + use: + FLAGS ; + ext: + {O_ID} ; -- a set of objects +.DE +.PP +The number of bytes of formal parameters accessed by +a procedure is determined by the front ends and +passed via a message (parameter message) to the optimizer. +If the front end is not able to determine this number +(e.g. the parameter may be an array of dynamic size or +the procedure may have a variable number of arguments) the attribute +contains the value 'UNKNOWN_SIZE'. +.sp 0 +A procedure has the following flags: +.IP - +external: true if the proc. is externally visible +.IP - +bodyseen: true if its code is available as EM text +.IP - +calunknown: true if it calls a procedure that has its bodyseen +flag not set +.IP - +environ: true if it uses or changes a (non-global) variable in +a lexically enclosing procedure +.IP - +lpi: true if is used as operand of an lpi instruction, so +it may be called indirect +.LP +The change and use attributes both have one flag: "indirect", +indicating whether the procedure does a 'use indirect' +or a 'store indirect' (indirect means through a pointer). +.NH 3 +The EM text +.PP +The EM text contains the EM instructions. +Every EM instruction has an operation code (opcode) +and 0 or 1 operands. +EM pseudo instructions can have more than +1 operand. +The opcode is just a small (8 bit) integer. +.sp +There are several kinds of operands, which we will +refer to as +.UL types. +Many EM instructions can have more than one type of operand. +The types and their encodings in Compact Assembly Language +are discussed extensively in. +.[~[ +keizer architecture +.], section 11.2] +Of special interest is the way numeric values +are represented. +Of prime importance is the machine independency of +the representation. +Ultimately, one could store every integer +just as a string of the characters '0' to '9'. +As doing arithmetic on strings is awkward, +Compact Assembly Language allows several alternatives. +The main idea is to look at the value of the integer. +Integers that fit in 16, 32 or 64 bits are +represented as a row of resp. 2, 4 and 8 bytes, +preceded by an indication of how many bytes are used. +Longer integers are represented as strings; +this is only allowed within pseudo instructions, however. +This concept works very well for target machines +with reasonable word sizes. +At present, most ACK software cannot be used for word sizes +higher than 32 bits, +although the handles for using larger word sizes are +present in the design of the EM code. +In the intermediate code we essentially use the +same ideas. +We allow three representations of integers. +.IP - +integers that fit in a short are represented as a short +.IP - +integers that fit in a long but not in a short are represented +as longs +.IP - +all remaining integers are represented as strings +(only allowed in pseudos). +.LP +The terms short and long are defined in +.[~[ +ritchie reference manual programming language +.], section 4] +and depend only on the source machine +(i.e. the machine on which ACK runs), +not on the target machines. +For historical reasons a long will often be called an +.UL offset. +.PP +Operands can also be instruction labels, +objects or procedures. +Instruction labels are denoted by a +.UL label +.UL identifier, +which can be distinguished from a normal identifier. +.sp +The operand of a pseudo instruction can be a list of +.UL arguments. +Arguments can have the same type as operands, except +for the type short, which is not used for arguments. +Furthermore, an argument can be a string or +a string representation of a signed integer, unsigned integer +or floating point number. +If the number of arguments is not fully determined by +the pseudo instruction (e.g. a ROM pseudo can have any number +of arguments), then the list is terminated by a special +argument of type CEND. +.DS +.UL syntax + em_text: + {line} ; + line: + INSTR -- opcode + OPTYPE -- operand type + operand ; + operand: + empty | -- OPTYPE = NO + SHORT | -- OPTYPE = SHORT + OFFSET | -- OPTYPE = OFFSET + LAB_ID | -- OPTYPE = INSTRLAB + O_ID | -- OPTYPE = OBJECT + P_ID | -- OPTYPE = PROCEDURE + {argument} ; -- OPTYPE = LIST + argument: + ARGTYPE + arg ; + arg: + empty | -- ARGTYPE = CEND + OFFSET | + LAB_ID | + O_ID | + P_ID | + string | -- ARGTYPE = STRING + const ; -- ARGTYPE = ICON,UCON or FCON + string: + LENGTH -- number of characters + {CHARACTER} ; + const: + SIZE -- number of bytes + string ; -- string representation of (un)signed + -- or floating point constant +.DE +.NH 3 +The control flow graphs +.PP +Each procedure can be divided +into a number of basic blocks. +A basic block is a piece of code with +no jumps in, except at the beginning, +and no jumps out, except at the end. +.PP +Every basic block has a set of +.UL successors, +which are basic blocks that can follow it immediately in +the dynamic execution sequence. +The +.UL predecessors +are the basic blocks of which this one +is a successor. +The successor and predecessor attributes +of all basic blocks of a single procedure +are said to form the +.UL control +.UL flow +.UL graph +of that procedure. +.PP +Another important attribute is the +.UL immediate +.UL dominator. +A basic block B dominates a block C if +every path in the graph from the procedure entry block +to C goes through B. +The immediate dominator of C is the closest dominator +of C on any path from the entry block. +(Note that the dominator relation is transitive, +so the immediate dominator is well defined.) +.PP +A basic block also has an attribute containing +the identifiers of every +.UL loop +that the block belongs to (see next section for loops). +.DS +.UL syntax + control_flow_graph: + {basic_block} ; + basic_block: + B_ID -- unique identifying number + #INSTR -- number of EM instructions + succ + pred + idom -- immediate dominator + loops -- set of loops + FLAGS ; -- flag bits + succ: + {B_ID} ; + pred: + {B_ID} ; + idom: + B_ID ; + loops: + {LP_ID} ; +.DE +The flag bits can have the values 'firm' and 'strong', +which are explained below. +.NH 3 +The loop tables +.PP +Every procedure has an associated +.UL loop +.UL table +containing information about all the loops +in the procedure. +Loops can be detected by a close inspection of +the control flow graph. +The main idea is to look for two basic blocks, +B and C, for which the following holds: +.IP - +B is a successor of C +.IP - +B is a dominator of C +.LP +B is called the loop +.UL entry +and C is called the loop +.UL end. +Intuitively, C contains a jump backwards to +the beginning of the loop (B). +.PP +A loop L1 is said to be +.UL nested +within loop L2 if all basic blocks of L1 +are also part of L2. +It is important to note that loops could +originally be written as a well structured for -or +while loop or as a messy goto loop. +Hence loops may partly overlap without one +being nested inside the other. +The +.UL nesting +.UL level +of a loop is the number of loops in +which it is nested (so it is 0 for +an outermost loop). +The details of loop detection will be discussed later. +.PP +It is often desirable to know whether a +basic block gets executed during every iteration +of a loop. +This leads to the following definitions: +.IP - +A basic block B of a loop L is said to be a \fIfirm\fR block +of L if B is executed on all successive iterations of L, +with the only possible exception of the last iteration. +.IP - +A basic block B of a loop L is said to be a \fIstrong\fR block +of L if B is executed on all successive iterations of L. +.LP +Note that a strong block is also a firm block. +If a block is part of a conditional statement, it is neither +strong nor firm, as it may be skipped during some iterations +(see Fig. 3.2). +.DS +loop + if cond1 then + ... -- this code will not + -- result in a firm or strong block + end if; + ... -- strong (always executed) + exit when cond2; + ... -- firm (not executed on + -- last iteration). +end loop; + +Fig. 3.2 Example of firm and strong block +.DE +.DS +.UL syntax + looptable: + {loop} ; + loop: + LP_ID -- unique identifying number + LEVEL -- loop nesting level + entry -- loop entry block + end ; + entry: + B_ID ; + end: + B_ID ; +.DE diff --git a/doc/ego/ic/ic4 b/doc/ego/ic/ic4 new file mode 100644 index 00000000..950a660e --- /dev/null +++ b/doc/ego/ic/ic4 @@ -0,0 +1,80 @@ +.NH 2 +External representation of the intermediate code +.PP +The syntax of the intermediate code was given +in the previous section. +In this section we will make some remarks about +the representation of the code in sequential files. +.sp +We use sequential files in order to avoid +the bookkeeping of complex file indices. +As a consequence of this decision +we can't store all components +of the intermediate code +in one file. +If a phase wishes to change some attribute +of a procedure, +or wants to add or delete entire procedures +(inline substitution may do the latter), +the procedure table will only be fully updated +after the entire EM text has been scanned. +Yet, the next phase undoubtedly wants +to read the procedure table before it +starts working on the EM text. +Hence there is an ordering problem, which +can be solved easily by putting the +procedure table in a separate file. +Similarly, the data block table is kept +in a file of its own. +.PP +The control flow graphs (CFGs) could be mixed +with the EM text. +Rather, we have chosen to put them +in a separate file too. +The control flow graph file should be regarded as a +file that imposes some structure on the EM-text file, +just as an overhead sheet containing a picture +of a Flow Chart may be put on an overhead sheet +containing statements. +The loop tables are also put in the CFG file. +A loop imposes an extra structure on the +CFGs and hence on the EM text. +So there are four files: +.IP - +the EM-text file +.IP - +the procedure table file +.IP - +the object table file +.IP - +the CFG and loop tables file +.LP +Every table is preceded by its length, in order to +tell where it ends. +The CFG file also contains the number of instructions of +every basic block, +indicating which part of the EM text belongs +to that block. +.DS +.UL syntax + intermediate_code: + object_table_file + proctable_file + em_text_file + cfg_file ; + object_table_file: + LENGTH -- number of objects + object_table ; + proctable_file: + LENGTH -- number of procedures + procedure_table ; + em_text_file: + em_text ; + cfg_file: + {per_proc} ; -- one for every procedure + per_proc: + BLENGTH -- number of basic blocks + LLENGTH -- number of loops + control_flow_graph + looptable ; +.DE diff --git a/doc/ego/ic/ic5 b/doc/ego/ic/ic5 new file mode 100644 index 00000000..9dd5daae --- /dev/null +++ b/doc/ego/ic/ic5 @@ -0,0 +1,163 @@ +.NH 2 +The Intermediate Code construction phase +.PP +The first phase of the global optimizer, +called +.UL IC, +constructs a major part of the intermediate code. +To be specific, it produces: +.IP - +the EM text +.IP - +the object table +.IP - +part of the procedure table +.LP +The calling, change and use attributes of a procedure +and all its flags except the external and bodyseen flags +are computed by the next phase (Control Flow phase). +.PP +As explained before, +the intermediate code does not contain +any names of variables or procedures. +The normal identifiers are replaced by identifying +numbers. +Yet, the output of the global optimizer must +contain normal identifiers, as this +output is in Compact Assembly Language format. +We certainly want all externally visible names +to be the same in the input as in the output, +because the optimized EM module may be a library unit, +used by other modules. +IC dumps the names of all procedures and data labels +on two files: +.IP - +the procedure dump file, containing tuples (P_ID, procedure name) +.IP - +the data dump file, containing tuples (D_ID, data label name) +.LP +The names of instruction labels are not dumped, +as they are not visible outside the procedure +in which they are defined. +.PP +The input to IC consists of one or more files. +Each file is either an EM module in Compact Assembly Language +format, or a Unix archive file (library) containing such modules. +IC only extracts those modules from a library that are +needed somehow, just as a linker does. +It is advisable to present as much code +of the EM program as possible to the optimizer, +although it is not required to present the whole program. +If a procedure is called somewhere in the EM text, +but its body (text) is not included in the input, +its bodyseen flag in the procedure table will still +be off. +Whenever such a procedure is called, +we assume the worst case for everything; +it will change and use all variables it has access to, +it will call every procedure etc. +.sp +Similarly, if a data label is used +but not defined, the PSEUDO attribute in its data block +will be set to UNKNOWN. +.NH 3 +Implementation +.PP +Part of the code for the EM Peephole Optimizer +.[ +staveren peephole toplass +.] +has been used for IC. +Especially the routines that read and unravel +Compact Assembly Language and the identifier +lookup mechanism have been used. +New code was added to recognize objects, +build the object and procedure tables and to +output the intermediate code. +.PP +IC uses singly linked linear lists for both the +procedure and object table. +Hence there are no limits on the size of such +a table (except for the trivial fact that it must fit +in main memory). +Both tables are outputted after all EM code has +been processed. +IC reads the EM text of one entire procedure +at a time, +processes it and appends the modified code to +the EM text file. +EM code is represented internally as a doubly linked linear +list of EM instructions. +.PP +Objects are recognized by looking at the operands +of instructions that reference global data. +If we come across the instructions: +.DS +LDE X+6 -- Load Double External +LAE X+20 -- Load Address External +.DE +we conclude that the data block +preceded by the data label X contains an object +at offset 6 of size twice the word size, +and an object at offset 20 of unknown size. +.sp +A data block entry of the object table is allocated +at the first reference to a data label. +If this reference is a defining occurrence +or a INA pseudo instruction, +the label is not externally visible +.[~[ +keizer architecture +.], section 11.1.4.3] +In this case, the external flag of the data block +is turned off. +If the first reference is an applied occurrence +or a EXA pseudo instruction, the flag is set. +We record this information, because the +optimizer may change the order of defining and +applied occurrences. +The INA and EXA pseudos are removed from the EM text. +They may be regenerated by the last phase +of the optimizer. +.sp +Similar rules hold for the procedure table +and the INP and EXP pseudos. +.NH 3 +Source files of IC +.PP +The source files of IC consist +of the files ic.c, ic.h and several packages. +.UL ic.h +contains type definitions, macros and +variable declarations that may be used by +ic.c and by every package. +.UL ic.c +contains the definitions of these variables, +the procedure +.UL main +and some high level I/O routines used by main. +.sp +Every package xxx consists of two files. +ic_xxx.h contains type definitions, +macros, variable declarations and +procedure declarations that may be used by +every .c file that includes this .h file. +The file ic_xxx.c provides the +definitions of these variables and +the implementation of the declared procedures. +IC uses the following packages: +.IP lookup: 18 +procedures that loop up procedure, data label +and instruction label names; procedures to dump +the procedure and data label names. +.IP lib: +one procedure that gets the next useful input module; +while scanning archives, it skips unnecessary modules. +.IP aux: +several auxiliary routines. +.IP io: +low-level I/O routines that unravel the Compact +Assembly Language. +.IP put: +routines that output the intermediate code +.LP diff --git a/doc/ego/il/.distr b/doc/ego/il/.distr new file mode 100644 index 00000000..2aac5686 --- /dev/null +++ b/doc/ego/il/.distr @@ -0,0 +1,6 @@ +il1 +il2 +il3 +il4 +il5 +il6 diff --git a/doc/ego/il/il1 b/doc/ego/il/il1 new file mode 100644 index 00000000..5bc33e6a --- /dev/null +++ b/doc/ego/il/il1 @@ -0,0 +1,112 @@ +.bp +.NH 1 +Inline substitution +.NH 2 +Introduction +.PP +The Inline Substitution technique (IL) +tries to decrease the overhead associated +with procedure calls (invocations). +During a procedure call, several actions +must be undertaken to set up the right +environment for the called procedure. +.[ +johnson calling sequence +.] +On return from the procedure, most of these +effects must be undone. +This entire process introduces significant +costs in execution time as well as +in object code size. +.PP +The inline substitution technique replaces +some of the calls by the modified body of +the called procedure, hence eliminating +the overhead. +Furthermore, as the calling and called procedure +are now integrated, they can be optimized +together, using other techniques of the optimizer. +This often leads to extra opportunities for +optimization +.[ +ball predicting effects +.] +.[ +carter code generation cacm +.] +.[ +scheifler inline cacm +.] +.PP +An inline substitution of a call to a procedure P increases +the size of the program, unless P is very small or P is +called only once. +In the latter case, P can be eliminated. +In practice, procedures that are called only once occur +quite frequently, due to the +introduction of structured programming. +(Carter +.[ +carter umi ann arbor +.] +states that almost 50% of the Pascal procedures +he analyzed were called just once). +.PP +Scheifler +.[ +scheifler inline cacm +.] +has a more general view of inline substitution. +In his model, the program under consideration is +allowed to grow by a certain amount, +i.e. code size is sacrificed to speed up the program. +The above two cases are just special cases of +his model, obtained by setting the size-change to +(approximately) zero. +He formulates the substitution problem as follows: +.IP +"Given a program, a subset of all invocations, +a maximum program size, and a maximum procedure size, +find a sequence of substitutions that minimizes +the expected execution time." +.LP +Scheifler shows that this problem is NP-complete +.[~[ +aho hopcroft ullman analysis algorithms +.], chapter 10] +by reduction to the Knapsack Problem. +Heuristics will have to be used to find a near-optimal +solution. +.PP +In the following chapters we will extend +Scheifler's view and adapt it to the EM Global Optimizer. +We will first describe the transformations that have +to be applied to the EM text when a call is substituted +in line. +Next we will examine in which cases inline substitution +is not possible or desirable. +Heuristics will be developed for +chosing a good sequence of substitutions. +These heuristics make no demand on the user +(such as making profiles +.[ +scheifler inline cacm +.] +or giving pragmats +.[~[ +ichbiah ada military standard +.], section 6.3.2]), +although the model could easily be extended +to use such information. +Finally, we will discuss the implementation +of the IL phase of the optimizer. +.PP +We will often use the term inline expansion +as a synonym of inline substitution. +.sp 0 +The inverse technique of procedure abstraction +(automatic subroutine generation) +.[ +shaffer subroutine generation +.] +will not be discussed in this report. diff --git a/doc/ego/il/il2 b/doc/ego/il/il2 new file mode 100644 index 00000000..ea69b35d --- /dev/null +++ b/doc/ego/il/il2 @@ -0,0 +1,93 @@ +.NH 2 +Parameters and local variables. +.PP +In the EM calling sequence, the calling procedure +pushes its parameters on the stack +before doing the CAL. +The called routine first saves some +status information on the stack and then +allocates space for its own locals +(also on the stack). +Usually, one special purpose register, +the Local Base (LB) register, +is used to access both the locals and the +parameters. +If memory is highly segmented, +the stack frames of the caller and the callee +may be allocated in different fragments; +an extra Argument Base (AB) register is used +in this case to access the actual parameters. +See 4.2 of +.[ +keizer architecture +.] +for further details. +.PP +If a procedure call is expanded in line, +there are two problems: +.IP 1. 3 +No stack frame will be allocated for the called procedure; +we must find another place to put its locals. +.IP 2. +The LB register cannot be used to access the actual +parameters; +as the CAL instruction is deleted, the LB will +still point to the local base of the \fIcalling\fR procedure. +.LP +The local variables of the called procedure will +be put in the stack frame of the calling procedure, +just after its own locals. +The size of the stack frame of the +calling procedure will be increased +during its entire lifetime. +Therefore our model will allow a +limit to be set on the number of bytes +for locals that the called procedure may have +(see next section). +.PP +There are several alternatives to access the parameters. +An actual parameter may be any auxiliary expression, +which we will refer to as +the \fIactual parameter expression\fR. +The value of this expression is stored +in a location on the stack (see above), +the \fIparameter location\fR. +.sp 0 +The alternatives for accessing parameters are: +.IP - +save the value of the stackpointer at the point of the CAL +in a temporary variable X; +this variable can be used to simulate the AB register, i.e. +parameter locations are accessed via an offset to +the value of X. +.IP - +create a new temporary local variable T for +the parameter (in the stack frame of the caller); +every access to the parameter location must be changed +into an access to T. +.IP - +do not evaluate the actual parameter expression before the call; +instead, substitute this expression for every use of the +parameter location. +.LP +The first method may be expensive if X is not +put in a register. +We will not use this method. +The time required to evaluate and access the +parameters when the second method is used +will not differ much from the normal +calling sequence (i.e. not in line call). +It is not expensive, but there are no +extra savings either. +The third method is essentially the 'by name' +parameter mechanism of Algol60. +If the actual parameter is just a numeric constant, +it is advantageous to use it. +Yet, there are several circumstances +under which it cannot or should not be used. +We will deal with this in the next section. +.sp 0 +In general we will use the third method, +if it is possible and desirable. +Such parameters will be called \fIin line parameters\fR. +In all other cases we will use the second method. diff --git a/doc/ego/il/il3 b/doc/ego/il/il3 new file mode 100644 index 00000000..e8ec7ee8 --- /dev/null +++ b/doc/ego/il/il3 @@ -0,0 +1,164 @@ +.NH 2 +Feasibility and desirability analysis +.PP +Feasibility and desirability analysis +of in line substitution differ +somewhat from most other techniques. +Usually, much effort is needed to find +a feasible opportunity for optimization +(e.g. a redundant subexpression). +Desirability analysis then checks +if it is really advantageous to do +the optimization. +For IL, opportunities are easy to find. +To see if an in line expansion is +desirable will not be hard either. +Yet, the main problem is to find the most +desirable ones. +We will deal with this problem later and +we will first attend feasibility and +desirability analysis. +.PP +There are several reasons why a procedure invocation +cannot or should not be expanded in line. +.sp +A call to a procedure P cannot be expanded in line +in any of the following cases: +.IP 1. 3 +The body of P is not available as EM text. +Clearly, there is no way to do the substitution. +.IP 2. +P, or any procedure called by P (transitively), +follows the chain of statically enclosing +procedures (via a LXL or LXA instruction) +or follows the chain of dynamically enclosing +procedures (via a DCH). +If the call were expanded in line, +one level would be removed from the chains, +leading to total chaos. +This chaos could be solved by patching up +every LXL, LXA or DCH in all procedures +that could be part of the chains, +but this is hard to implement. +.IP 3. +P, or any procedure called by P (transitively), +calls a procedure whose body is not +available as EM text. +The unknown procedure may use an LXL, LXA or DCH. +However, in several languages a separately +compiled procedure has no access to the +static or dynamic chain. +In this case +this point does not apply. +.IP 4. +P, or any procedure called by P (transitively), +uses the LPB instruction, which converts a +local base to an argument base; +as the locals and parameters are stored +in a non-standard way (differing from the +normal EM calling sequence) this instruction +would yield incorrect results. +.IP 5. +The total number of bytes of the parameters +of P is not known. +P may be a procedure with a variable number +of parameters or may have an array of dynamic size +as value parameter. +.LP +It is undesirable to expand a call to a procedure P in line +in any of the following cases: +.IP 1. 3 +P is large, i.e. the number of EM instructions +of P exceeds some threshold. +The expanded code would be large too. +Furthermore, several programs in ACK, +including the global optimizer itself, +may run out of memory if they they have to run +in a small address space and are provided +very large procedures. +The threshold may be set to infinite, +in which case this point does not apply. +.IP 2. +P has many local variables. +All these variables would have to be allocated +in the stack frame of the calling procedure. +.PP +If a call may be expanded in line, we have to +decide how to access its parameters. +In the previous section we stated that we would +use in line parameters whenever possible and desirable. +There are several reasons why a parameter +cannot or should not be expanded in line. +.sp +No parameter of a procedure P can be expanded in line, +in any of the following cases: +.IP 1. 3 +P, or any procedure called by P (transitively), +does a store-indirect or a use-indirect (i.e. through +a pointer). +However, if the front-end has generated messages +telling that certain parameters can not be accessed +indirectly, those parameters may be expanded in line. +.IP 2. +P, or any procedure called by P (transitively), +calls a procedure whose body is not available as EM text. +The unknown procedure may do a store-indirect +or a use-indirect. +However, the same remark about front-end messages +as for 1. holds here. +.IP 3. +The address of a parameter location is taken (via a LAL). +In the normal calling sequence, all parameters +are stored sequentially. If the address of one +parameter location is taken, the address of any +other parameter location can be computed from it. +Hence we must put every parameter in a temporary location; +furthermore, all these locations must be in +the same order as for the normal calling sequence. +.IP 4. +P has overlapping parameters; for example, it uses +the parameter at offset 10 both as a 2 byte and as a 4 byte +parameter. +Such code may be produced by the front ends if +the formal parameter is of some record type +with variants. +.PP +Sometimes a specific parameter must not be expanded in line. +.sp 0 +An actual parameter expression cannot be expanded in line +in any of the following cases: +.IP 1. 3 +P stores into the parameter location. +Even if the actual parameter expression is a simple +variable, it is incorrect to change the 'store into +formal' into a 'store into actual', because of +the parameter mechanism used. +In Pascal, the following expansion is incorrect: +.DS +procedure p (x:integer); +begin + x := 20; +end; +... +a := 10; a := 10; +p(a); ---> a := 20; +write(a); write(a); +.DE +.IP 2. +P changes any of the operands of the +actual parameter expression. +If the expression is expanded and evaluated +after the operand has been changed, +the wrong value will be used. +.IP 3. +The actual parameter expression has side effects. +It must be evaluated only once, +at the place of the call. +.LP +It is undesirable to expand an actual parameter in line +in the following case: +.IP 1. 3 +The parameter is used more than once +(dynamically) and the actual parameter expression +is not just a simple variable or constant. +.LP diff --git a/doc/ego/il/il4 b/doc/ego/il/il4 new file mode 100644 index 00000000..fdc664b1 --- /dev/null +++ b/doc/ego/il/il4 @@ -0,0 +1,132 @@ +.NH 2 +Heuristic rules +.PP +Using the information described +in the previous section, +we can find all calls that can +be expanded in line, and for which +this expansion is desirable. +In general, we cannot expand all these calls, +so we have to choose the 'best' ones. +With every CAL instruction +that may be expanded, we associate +a \fIpay off\fR, +which expresses how desirable it is +to expand this specific CAL. +.sp +Let Tc denote the portion of EM text involved +in a specific call, i.e. the pushing of the actual +parameter expressions, the CAL itself, +the popping of the parameters and the +pushing of the result (if any, via an LFR). +Let Te denote the EM text that would be obtained +by expanding the call in line. +Let Pc be the original program and Pe the program +with Te substituted for Tc. +The pay off of the CAL depends on two factors: +.IP - +T = execution_time(Pe) - execution_time(Pc) +.IP - +S = code_size(Pe) - code_size(Pc) +.LP +The change in execution time (T) depends on: +.IP - +T1 = execution_time(Te) - execution_time(Tc) +.IP - +N = number of times Te or Tc get executed. +.LP +We assume that T1 will be the same every +time the code gets executed. +This is a reasonable assumption. +(Note that we are talking about one CAL, +not about different calls to the same procedure). +Hence +.DS +T = N * T1 +.DE +T1 can be estimated by a careful analysis +of the transformations that are performed. +Below, we list everything that will be +different when a call is expanded in line: +.IP - +The CAL instruction is not executed. +This saves a subroutine jump. +.IP - +The instructions in the procedure prolog +are not executed. +These instructions, generated from the PRO pseudo, +save some machine registers +(including the old LB), set the new LB and allocate space +for the locals of the called routine. +The savings may be less if there are no +locals to allocate. +.IP - +In line parameters are not evaluated before the call +and are not pushed on the stack. +.IP - +All remaining parameters are stored in local variables, +instead of being pushed on the stack. +.IP - +If the number of parameters is nonzero, +the ASP instruction after the CAL is not executed. +.IP - +Every reference to an in line parameter is +substituted by the parameter expression. +.IP - +RET (return) instructions are replaced by +BRA (branch) instructions. +If the called procedure 'falls through' +(i.e. it has only one RET, at the end of its code), +even the BRA is not needed. +.IP - +The LFR (fetch function result) is not executed +.PP +Besides these changes, which are caused directly by IL, +other changes may occur as IL influences other optimization +techniques, such as Register Allocation and Constant Propagation. +Our heuristic rules do not take into account the quite +inpredictable effects on Register Allocation. +It does, however, favour calls that have numeric \fIconstants\fR +as parameter; especially the constant "0" as an inline +parameter gets high scores, +as further optimizations may often be possible. +.PP +It cannot be determined statically how often a CAL instruction gets +executed. +We will use \fIloop nesting\fR information here. +The nesting level of the loop in which +the CAL appears (if any) will be used as an +indication for the number of times it gets executed. +.PP +Based on all these facts, +the pay off of a call will be computed. +The following model was developed empirically. +Assume procedure P calls procedure Q. +The call takes place in basic block B. +.DS +ZP = # zero parameters +CP = # constant parameters - ZP +LN = Loop Nesting level (0 if outside any loop) +F = \fIif\fR # formal parameters of Q > 0 \fIthen\fR 1 \fIelse\fR 0 +FT = \fIif\fR Q falls through \fIthen\fR 1 \fIelse\fR 0 +S = size(Q) - 1 - # inline_parameters - F +L = \fIif\fR # local variables of P > 0 \fIthen\fR 0 \fIelse\fR -1 +A = CP + 2 * ZP +N = \fIif\fR LN=0 and P is never called from a loop \fIthen\fR 0 \fIelse\fR (LN+1)**2 +FM = \fIif\fR B is a firm block \fIthen\fR 2 \fIelse\fR 1 + +pay_off = (100/S + FT + F + L + A) * N * FM +.DE +S stands for the size increase of the program, +which is slightly less than the size of Q. +The size of a procedure is taken to be its number +of (non-pseudo) EM instructions. +The terms "loop nesting level" and "firm" were defined +in the chapter on the Intermediate Code (section "loop tables"). +If a call is not inside a loop and the calling procedure +is itself never called from a loop (transitively), +then the call will probably be executed at most once. +Such a call is never expanded in line (its pay off is zero). +If the calling procedure doesn't have local variables, a penalty (L) +is introduced, as it will most likely get local variables if the +call gets expanded. diff --git a/doc/ego/il/il5 b/doc/ego/il/il5 new file mode 100644 index 00000000..6445ba7d --- /dev/null +++ b/doc/ego/il/il5 @@ -0,0 +1,440 @@ +.NH 2 +Implementation +.PP +A major factor in the implementation +of Inline Substitution is the requirement +not to use an excessive amount of memory. +IL essentially analyzes the entire program; +it makes decisions based on which procedure calls +appear in the whole program. +Yet, because of the memory restriction, it is +not feasible to read the entire program +in main memory. +To solve this problem, the IL phase has been +split up into three subphases that are executed sequentially: +.IP 1. +analyze every procedure; see how it accesses its parameters; +simultaneously collect all calls +appearing in the whole program an put them +in a \fIcall-list\fR. +.IP 2. +use the call-list and decide which calls will be substituted +in line. +.IP 3. +take the decisions of subphase 2 and modify the +program accordingly. +.LP +Subphases 1 and 3 scan the input program; only +subphase 3 modifies it. +It is essential that the decisions can be made +in subphase 2 +without using the input program, +provided that subphase 1 puts enough information +in the call-list. +Subphase 2 keeps the entire call-list in main memory +and repeatedly scans it, to +find the next best candidate for expansion. +.PP +We will specify the +data structures used by IL before +describing the subphases. +.NH 3 +Data structures +.NH 4 +The procedure table +.PP +In subphase 1 information is gathered about every procedure +and added to the procedure table. +This information is used by the heuristic rules. +A proctable entry for procedure p has +the following extra information: +.IP - +is it allowed to substitute an invocation of p in line? +.IP - +is it allowed to put any parameter of such a call in line? +.IP - +the size of p (number of EM instructions) +.IP - +does p 'fall through'? +.IP - +a description of the formal parameters that p accesses; this information +is obtained by looking at the code of p. For every parameter f, +we record: +.RS +.IP - +the offset of f +.IP - +the type of f (word, double word, pointer) +.IP - +may the corresponding actual parameter be put in line? +.IP - +is f ever accessed indirectly? +.IP - +if f used: never, once or more than once? +.RE +.IP - +the number of times p is called (see below) +.IP - +the file address of its call-count information (see below). +.LP +.NH 4 +Call-count information +.PP +As a result of Inline Substitution, some procedures may +become useless, because all their invocations have been +substituted in line. +One of the tasks of IL is to keep track which +procedures are no longer called. +Note that IL is especially keen on procedures that are +called only once +(possibly as a result of expanding all other calls to it). +So we want to know how many times a procedure +is called \fIduring\fR Inline Substitution. +It is not good enough to compute this +information afterwards. +The task is rather complex, because +the number of times a procedure is called +varies during the entire process: +.IP 1. +If a call to p is substituted in line, +the number of calls to p gets decremented by 1. +.IP 2. +If a call to p is substituted in line, +and p contains n calls to q, then the number of calls to q +gets incremented by n. +.IP 3. +If a procedure p is removed (because it is no +longer called) and p contains n calls to q, +then the number of calls to q gets decremented by n. +.LP +(Note that p may be the same as q, if p is recursive). +.sp 0 +So we actually want to have the following information: +.DS +NRCALL(p,q) = number of call to q appearing in p, + +for all procedures p and q that may be put in line. +.DE +This information, called \fIcall-count information\fR is +computed by the first subphase. +It is stored in a file. +It is represented as a number of lists, rather than as +a (very sparse) matrix. +Every procedure has a list of (proc,count) pairs, +telling which procedures it calls, and how many times. +The file address of its call-count list is stored +in its proctable entry. +Whenever this information is needed, it is fetched from +the file, using direct access. +The proctable entry also contains the number of times +a procedure is called, at any moment. +.NH 4 +The call-list +.PP +The call-list is the major data structure use by IL. +Every item of the list describes one procedure call. +It contains the following attributes: +.IP - +the calling procedure (caller) +.IP - +the called procedure (callee) +.IP - +identification of the CAL instruction (sequence number) +.IP - +the loop nesting level; our heuristic rules appreciate +calls inside a loop (or even inside a loop nested inside +another loop, etc.) more than other calls +.IP - +the actual parameter expressions involved in the call; +for every actual, we record: +.RS +.IP - +the EM code of the expression +.IP - +the number of bytes of its result (size) +.IP - +an indication if the actual may be put in line +.RE +.LP +The structure of the call-list is rather complex. +Whenever a call is expanded in line, new calls +will suddenly appear in the program, +that were not contained in the original body +of the calling subroutine. +These calls are inherited from the called procedure. +We will refer to these invocations as \fInested calls\fR +(see Fig. 5.1). +.DS +procedure p is +begin . + a(); . + b(); . +end; + +procedure r is procedure r is +begin begin + x(); x(); + p(); -- in line a(); -- nested call + y(); b(); -- nested call +end; y(); + end; + +Fig. 5.1 Example of nested procedure calls +.DE +Nested calls may subsequently be put in line too +(probably resulting in a yet deeper nesting level, etc.). +So the call-list does not always reflect the source program, +but changes dynamically, as decisions are made. +If a call to p is expanded, all calls appearing in p +will be added to the call-list. +.sp 0 +A convenient and elegant way to represent +the call-list is to use a LISP-like list. +.[ +poel lisp trac +.] +Calls that appear at the same level +are linked in the CDR direction. If a call C +to a procedure p is expanded, +all calls appearing in p are put in a sub-list +of C, i.e. in its CAR. +In the example above, before the decision +to expand the call to p is made, the +call-list of procedure r looks like: +.DS +(call-to-x, call-to-p, call-to-y) +.DE +After the decision, it looks like: +.DS +(call-to-x, (call-to-p*, call-to-a, call-to-b), call-to-y) +.DE +The call to p is marked, because it has been +substituted. +Whenever IL wants to traverse the call-list of some procedure, +it uses the well-known LISP technique of +recursion in the CAR direction and +iteration in the CDR direction +(see page 1.19-2 of +.[ +poel lisp trac +.] +). +All list traversals look like: +.DS +traverse(list) +{ + for (c = first(list); c != 0; c = CDR(c)) { + if (c is marked) { + traverse(CAR(c)); + } else { + do something with c + } + } +} +.DE +The entire call-list consists of a number of LISP-like lists, +one for every procedure. +The proctable entry of a procedure contains a pointer +to the beginning of the list. +.NH 3 +The first subphase: procedure analysis +.PP +The tasks of the first subphase are to determine +several attributes of every procedure +and to construct the basic call-list, +i.e. without nested calls. +The size of a procedure is determined +by simply counting its EM instructions. +Pseudo instructions are skipped. +A procedure does not 'fall through' if its CFG +contains a basic block +that is not the last block of the CFG and +that ends on a RET instruction. +The formal parameters of a procedure are determined +by inspection of +its code. +.PP +The call-list in constructed by looking at all CAL instructions +appearing in the program. +The call-list should only contain calls to procedures +that may be put in line. +This fact is only known if the procedure was +analyzed earlier. +If a call to a procedure p appears in the program +before the body of p, +the call will always be put in the call-list. +If p is later found to be unsuitable, +the call will be removed from the list by the +second subphase. +.PP +An important issue is the recognition +of the actual parameter expressions of the call. +The front ends produces messages telling how many +bytes of formal parameters every procedure accesses. +(If there is no such message for a procedure, it +cannot be put in line). +The actual parameters together must account for +the same number of bytes.A recursive descent parser is used +to parse side-effect free EM expressions. +It uses a table and some +auxiliary routines to determine +how many bytes every EM instruction pops from the stack +and how many bytes it pushes onto the stack. +These numbers depend on the EM instruction, its argument, +and the wordsize and pointersize of the target machine. +Initially, the parser has to recognize the +number of bytes specified in the formals-message, +say N. +Assume the first instruction before the CAL pops S bytes +and pushes R bytes. +If R > N, too many bytes are recognized +and the parser fails. +Else, it calls itself recursively to recognize the +S bytes used as operand of the instruction. +If it succeeds in doing so, it continues with the next instruction, +i.e. the first instruction before the code recognized by +the recursive call, to recognize N-R more bytes. +The result is a number of EM instructions that collectively push N bytes. +If an instruction is come across that has side-effects +(e.g. a store or a procedure call) or of which R and S cannot +be computed statically (e.g. a LOS), it fails. +.sp 0 +Note that the parser traverses the code backwards. +As EM code is essentially postfix code, the parser works top down. +.PP +If the parser fails to recognize the parameters, the call will not +be substituted in line. +If the parameters can be determined, they still have to +match the formal parameters of the called procedure. +This check is performed by the second subphase; it cannot be +done here, because it is possible that the called +procedure has not been analyzed yet. +.PP +The entire call-list is written to a file, +to be processed by the second subphase. +.NH 3 +The second subphase: making decisions +.PP +The task of the second subphase is quite easy +to understand. +It reads the call-list file, +builds an incore call-list and deletes every +call that may not be expanded in line (either because the called +procedure may not be put in line, or because the actual parameters +of the call do not match the formal parameters of the called procedure). +It assigns a \fIpay-off\fR to every call, +indicating how desirable it is to expand it. +.PP +The subphase repeatedly scans the call-list and takes +the call with the highest ratio. +The chosen one gets marked, +and the call-list is extended with the nested calls, +as described above. +These nested calls are also assigned a ratio, +and will be considered too during the next scans. +.sp 0 +After every decision the number of times +every procedure is called is updated, using +the call-count information. +Meanwhile, the subphase keeps track of the amount of space left +available. +If all space is used, or if there are no more calls left to +be expanded, it exits this loop. +Finally, calls to procedures that are called only +once are also chosen. +.PP +The actual parameters of a call are only needed by +this subphase to assign a ratio to a call. +To save some space, these actuals are not kept in main memory. +They are removed after the call has been read and a ratio +has been assigned to it. +So this subphase works with \fIabstracts\fR of calls. +After all work has been done, +the actual parameters of the chosen calls are retrieved +from a file, +as they are needed by the transformation subphase. +.NH 3 +The third subphase: doing transformations +.PP +The third subphase makes the actual modifications to +the EM text. +It is directed by the decisions made in the previous subphase, +as expressed via the call-list. +The call-list read by this subphase contains +only calls that were selected for expansion. +The list is ordered in the same way as the EM text, +i.e. if a call C1 appears before a call C2 in the call-list, +C1 also appears before C2 in the EM text. +So the EM text is traversed linearly, +the calls that have to be substituted are determined +and the modifications are made. +If a procedure is come across that is no longer needed, +it is simply not written to the output EM file. +The substitution of a call takes place in distinct steps: +.IP "change the calling sequence" 7 +.sp 0 +The actual parameter expressions are changed. +Parameters that are put in line are removed. +All remaining ones must store their result in a +temporary local variable, rather than +push it on the stack. +The CAL instruction and any ASP (to pop actual parameters) +or LFR (to fetch the result of a function) +are deleted. +.IP "fetch the text of the called procedure" +.sp 0 +Direct disk access is used to to read the text of the +called procedure. +The file offset is obtained from the proctable entry. +.IP "allocate bytes for locals and temporaries" +.sp 0 +The local variables of the called procedure will be put in the +stack frame of the calling procedure. +The same applies to any temporary variables +that hold the result of parameters +that were not put in line. +The proctable entry of the caller is updated. +.IP "put a label after the CAL" +.sp 0 +If the called procedure contains a RET (return) instruction +somewhere in the middle of its text (i.e. it does +not fall through), the RET must be changed into +a BRA (branch), to jump over the +remainder of the text. +This label is not needed if the called +procedure falls through. +.IP "copy the text of the called procedure and modify it" +.sp 0 +References to local variables of the called routine +and to parameters that are not put in line +are changed to refer to the +new local of the caller. +References to in line parameters are replaced +by the actual parameter expression. +Returns (RETs) are either deleted or +replaced by a BRA. +Messages containing information about local +variables or parameters are changed. +Global data declarations and the PRO and END pseudos +are removed. +Instruction labels and references to them are +changed to make sure they do not have the +same identifying number as +labels in the calling procedure. +.IP "insert the modified text" +.sp 0 +The pseudos of the called procedure are put after the pseudos +of the calling procedure. +The real text of the callee is put at +the place where the CAL was. +.IP "take care of nested substitutions" +.sp 0 +The expanded procedure may contain calls that +have to be expanded too (nested calls). +If the descriptor of this call contains actual +parameter expressions, +the code of the expressions has to be changed +the same way as the code of the callee was changed. +Next, the entire process of finding CALs and doing +the substitutions is repeated recursively. +.LP diff --git a/doc/ego/il/il6 b/doc/ego/il/il6 new file mode 100644 index 00000000..bf61cad5 --- /dev/null +++ b/doc/ego/il/il6 @@ -0,0 +1,27 @@ +.NH 2 +Source files of IL +.PP +The sources of IL are in the following files +and packages (the prefixes 1_, 2_ and 3_ refer to the three subphases): +.IP il.h: 14 +declarations of global variables and +data structures +.IP il.c: +the routine main; the driving routines of the three subphases +.IP 1_anal: +contains a subroutine that analyzes a procedure +.IP 1_cal: +contains a subroutine that analyzes a call +.IP 1_aux: +implements auxiliary procedures used by subphase 1 +.IP 2_aux: +implements auxiliary procedures used by subphase 2 +.IP 3_subst: +the driving routine for doing the substitution +.IP 3_change: +lower level routines that do certain modifications +.IP 3_aux: +implements auxiliary procedures used by subphase 3 +.IP aux +implements auxiliary procedures used by several subphases. +.LP diff --git a/doc/ego/intro/.distr b/doc/ego/intro/.distr new file mode 100644 index 00000000..45de4077 --- /dev/null +++ b/doc/ego/intro/.distr @@ -0,0 +1,3 @@ +head +intro1 +tail diff --git a/doc/ego/intro/head b/doc/ego/intro/head new file mode 100644 index 00000000..0d015a9d --- /dev/null +++ b/doc/ego/intro/head @@ -0,0 +1,7 @@ +.ND +.ll 80m +.nr LL 80m +.nr tl 78m +.tr ~ +.ds >. . +.ds [. " \[ diff --git a/doc/ego/intro/intro1 b/doc/ego/intro/intro1 new file mode 100644 index 00000000..de7a5ae8 --- /dev/null +++ b/doc/ego/intro/intro1 @@ -0,0 +1,79 @@ +.TL +The design and implementation of +the EM Global Optimizer +.AU +H.E. Bal +.AI +Vrije Universiteit +Wiskundig Seminarium, Amsterdam +.AB +The EM Global Optimizer is part of the Amsterdam Compiler Kit, +a toolkit for making retargetable compilers. +It optimizes the intermediate code common to all compilers of +the toolkit (EM), +so it can be used for all programming languages and +all processors supported by the kit. +.PP +The optimizer is based on well-understood concepts like +control flow analysis and data flow analysis. +It performs the following optimizations: +Inline Substitution, Strength Reduction, Common Subexpression Elimination, +Stack Pollution, Cross Jumping, Branch Optimization, Copy Propagation, +Constant Propagation, Dead Code Elimination and Register Allocation. +.PP +This report describes the design of the optimizer and several +of its implementation issues. +.AE +.bp +.NH 1 +Introduction +.PP +.FS +This work was supported by the +Stichting Technische Wetenschappen (STW) +under grant VWI00.0001. +.FE +The EM Global Optimizer is part of a software toolkit +for making production-quality retargetable compilers. +This toolkit, +called the Amsterdam Compiler Kit +.[ +tanenbaum toolkit rapport +.] +.[ +tanenbaum toolkit cacm +.] +runs under the Unix* +.FS +*Unix is a Trademark of Bell Laboratories +.FE +operating system. +.sp 0 +The main design philosophy of the toolkit is to use +a language- and machine-independent +intermediate code, called EM. +.[ +keizer architecture +.] +The basic compilation process can be split up into +two parts. +A language-specific front end translates the source program into EM. +A machine-specific back end transforms EM to assembly code +of the target machine. +.PP +The global optimizer is an optional phase of the +compilation process, and can be used to obtain +machine code of a higher quality. +The optimizer transforms EM-code to better EM-code, +so it comes between the front end and the back end. +It can be used with any combination of languages +and machines, as far as they are supported by +the compiler kit. +.PP +This report describes the design of the +global optimizer and several of its +implementation issues. +Measurements can be found in. +.[ +bal tanenbaum global +.] diff --git a/doc/ego/intro/tail b/doc/ego/intro/tail new file mode 100644 index 00000000..6cd2d486 --- /dev/null +++ b/doc/ego/intro/tail @@ -0,0 +1,3 @@ +.[ +$LIST$ +.] diff --git a/doc/ego/lv/.distr b/doc/ego/lv/.distr new file mode 100644 index 00000000..b82f3da5 --- /dev/null +++ b/doc/ego/lv/.distr @@ -0,0 +1 @@ +lv1 diff --git a/doc/ego/lv/lv1 b/doc/ego/lv/lv1 new file mode 100644 index 00000000..7574ca6f --- /dev/null +++ b/doc/ego/lv/lv1 @@ -0,0 +1,95 @@ +.bp +.NH 1 +Live-Variable analysis +.NH 2 +Introduction +.PP +The "Live-Variable analysis" optimization technique (LV) +performs some code improvements and computes information that may be +used by subsequent optimizations. +The main task of this phase is the +computation of \fIlive-variable information\fR. +.[~[ +aho compiler design +.] section 14.4] +A variable A is said to be \fIdead\fR at some point p of the +program text, if on no path in the control flow graph +from p to a RET (return), A can be used before being changed; +else A is said to be \fIlive\fR. +.PP +A statement of the form +.DS +VARIABLE := EXPRESSION +.DE +is said to be dead if the left hand side variable is dead just after +the statement and the right hand side expression has no +side effects (i.e. it doesn't change any variable). +Such a statement can be eliminated entirely. +Dead code will seldom be present in the original program, +but it may be the result of earlier optimizations, +such as copy propagation. +.PP +Live-variable information is passed to other phases via +messages in the EM code. +Live/dead messages are generated at points in the EM text where +variables become dead or live. +This information is especially useful for the Register +Allocation phase. +.NH 2 +Implementation +.PP +The implementation uses algorithm 14.6 of. +.[ +aho compiler design +.] +First two sets DEF and USE are computed for every basic block b: +.IP DEF(b) 9 +the set of all variables that are assigned a value in b before +being used +.IP USE(b) 9 +the set of all variables that may be used in b before being changed. +.LP +(So variables that may, but need not, be used resp. changed via a procedure +call or through a pointer are included in USE but not in DEF). +The next step is to compute the sets IN and OUT : +.IP IN[b] 9 +the set of all variables that are live at the beginning of b +.IP OUT[b] 9 +the set of all variables that are live at the end of b +.LP +IN and OUT can be computed for all blocks simultaneously by solving the +data flow equations: +.DS +(1) IN[b] = OUT[b] - DEF[b] + USE[b] +[2] OUT[b] = IN[s1] + ... + IN[sn] ; + where SUCC[b] = {s1, ... , sn} +.DE +The equations are solved by a similar algorithm as for +the Use Definition equations (see previous chapter). +.PP +Finally, each basic block is visited in turn to remove its dead code +and to emit the live/dead messages. +Every basic block b is traversed from its last +instruction backwards to the beginning of b. +Initially, all variables that are dead at the end +of b are marked dead. All others are marked live. +If we come across an assignment to a variable X that +was marked live, a live-message is put after the +assignment and X is marked dead; +if X was marked dead, the assignment may be removed, provided that +the right hand side expression contains no side effects. +If we come across a use of a variable X that +was marked dead, a dead-message is put after the +use and X is marked live. +So at any point, the mark of X tells whether X is +live or dead immediately before that point. +A message is also generated at the start of a basic block +for every variable that was live at the end of the (textually) +previous block, but dead at the entry of this block, or v.v. +.PP +Only local variables are considered. +This significantly reduces the memory needed by this phase, +eases the implementation and is hardly less efficient than +considering all variables. +(Note that it is very hard to prove that an assignment to +a global variable is dead). diff --git a/doc/ego/ov/.distr b/doc/ego/ov/.distr new file mode 100644 index 00000000..9170d50d --- /dev/null +++ b/doc/ego/ov/.distr @@ -0,0 +1 @@ +ov1 diff --git a/doc/ego/ov/ov1 b/doc/ego/ov/ov1 new file mode 100644 index 00000000..5ab3d5d0 --- /dev/null +++ b/doc/ego/ov/ov1 @@ -0,0 +1,371 @@ +.bp +.NH 1 +Overview of the global optimizer +.NH 2 +The ACK compilation process +.PP +The EM Global Optimizer is one of three optimizers that are +part of the Amsterdam Compiler Kit (ACK). +The phases of ACK are: +.IP 1. +A Front End translates a source program to EM +.IP 2. +The Peephole Optimizer +.[ +tanenbaum staveren peephole toplass +.] +reads EM code and produces 'better' EM code. +It performs a number of optimizations (mostly peephole +optimizations) +such as constant folding, strength reduction and unreachable code +elimination. +.IP 3. +The Global Optimizer further improves the EM code. +.IP 4. +The Code Generator transforms EM to assembly code +of the target computer. +.IP 5. +The Target Optimizer improves the assembly code. +.IP 6. +An Assembler/Loader generates an executable file. +.LP +For a more extensive overview of the ACK compilation process, +we refer to. +.[ +tanenbaum toolkit rapport +.] +.[ +tanenbaum toolkit cacm +.] +.PP +The input of the Global Optimizer may consist of files and +libraries. +Every file or module in the library must contain EM code in +Compact Assembly Language format. +.[~[ +tanenbaum machine architecture +.], section 11.2] +The output consists of one such EM file. +The input files and libraries together need not +constitute an entire program, +although as much of the program as possible should be supplied. +The more information about the program the optimizer +gets, the better its output code will be. +.PP +The Global Optimizer is language- and machine-independent, +i.e. it can be used for all languages and machines supported by ACK. +Yet, it puts some unavoidable restrictions on the EM code +produced by the Front End (see below). +It must have some knowledge of the target machine. +This knowledge is expressed in a machine description table +which is passed as argument to the optimizer. +This table does not contain very detailed information about the +target (such as its instruction set and addressing modes). +.NH 2 +The EM code +.PP +The definition of EM, the intermediate code of all ACK compilers, +is given in a separate document. +.[ +tanenbaum machine architecture +.] +We will only discuss some features of EM that are most relevant +to the Global Optimizer. +.PP +EM is the assembly code of a virtual \fIstack machine\fR. +All operations are performed on the top of the stack. +For example, the statement "A := B + 3" may be expressed in EM as: +.DS +LOL -4 -- push local variable B +LOC 3 -- push constant 3 +ADI 2 -- add two 2-byte items on top of + -- the stack and push the result +STL -2 -- pop A +.DE +So EM is essentially a \fIpostfix\fR code. +.PP +EM has a rich instruction set, containing several arithmetic +and logical operators. +It also contains special-case instructions (such as INCrement). +.PP +EM has \fIglobal\fR (\fIexternal\fR) variables, accessible +by all procedures and \fIlocal\fR variables, accessible by a few +(nested) procedures. +The local variables of a lexically enclosing procedure may +be accessed via a \fIstatic link\fR. +EM has instructions to follow the static chain. +There are EM instruction to allow a procedure +to access its local variables directly (such as LOL and STL above). +Local variables are referenced via an offset in the stack frame +of the procedure, rather than by their names (e.g. -2 and -4 above). +The EM code does not contain the (source language) type +of the variables. +.PP +All structured statements in the source program are expressed in +low level jump instructions. +Besides conditional and unconditional branch instructions, there are +two case instructions (CSA and CSB), +to allow efficient translation of case statements. +.NH 2 +Requirements on the EM input +.PP +As the optimizer should be useful for all languages, +it clearly should not put severe restrictions on the EM code +of the input. +There is, however, one immovable requirement: +it must be possible to determine the \fIflow of control\fR of the +input program. +As virtually all global optimizations are based on control flow information, +the optimizer would be totally powerless without it. +For this reason we restrict the usage of the case jump instructions (CSA/CSB) +of EM. +Such an instruction is always called with the address of a case descriptor +on top the the stack. +.[~[ +tanenbaum machine architecture +.] section 7.4] +This descriptor contains the labels of all possible +destinations of the jump. +We demand that all case descriptors are allocated in a global +data fragment of type ROM, i.e. the case descriptors +may not be modifyable. +Furthermore, any case instruction should be immediately preceded by +a LAE (Load Address External) instruction, that loads the +address of the descriptor, +so the descriptor can be uniquely identified. +.PP +The optimizer will work improperly if the user deceives the control flow. +We will give two methods to do this. +.PP +In "C" the notorious library routines "setjmp" and "longjmp" +.[ +unix programmer's manual McIlroy +.] +may be used to jump out of a procedure, +but can also be used for a number of other stuffy purposes, +for example, to create an extra entry point in a loop. +.DS + while (condition) { + .... + setjmp(buf); + ... + } + ... + longjmp(buf); +.DE +The invocation to longjmp actually is a jump to the place of +the last call to setjmp with the same argument (buf). +As the calls to setjmp and longjmp are indistinguishable from +normal procedure calls, the optimizer will not see the danger. +No need to say that several loop optimizations will behave +unexpectedly when presented with such pathological input. +.PP +Another way to deceive the flow of control is +by using exception handling routines. +Ada* +.FS +* Ada is a registered trademark of the U.S. Government +(Ada Joint Program Office). +.FE +has clearly recognized the dangers of exception handling, +but other languages (such as PL/I) have not. +.[ +ada rationale +.] +.PP +The optimizer will be more effective if the EM input contains +some extra information about the source program. +Especially the \fIregister message\fR is very important. +These messages indicate which local variables may never be +accessed indirectly. +Most optimizations benefit significantly by this information. +.PP +The Inline Substitution technique needs to know how many bytes +of formal parameters every procedure accesses. +Only calls to procedures for which the EM code contains this information +will be substituted in line. +.NH 2 +Structure of the optimizer +.PP +The Global Optimizer is organized as a number of \fIphases\fR, +each one performing some task. +The main structure is as follows: +.IP IC 6 +the Intermediate Code construction phase transforms EM into the +intermediate code (ic) of the optimizer +.IP CF +the Control Flow phase extends the ic with control flow +information and interprocedural information +.IP OPTs +zero or more optimization phases, each one performing one or +more related optimizations +.IP CA +the Compact Assembly phase generates Compact Assembly Language EM code +out of ic. +.LP +.PP +An important issue in the design of a global optimizer is the +interaction between optimization techniques. +It is often advantageous to combine several techniques in +one algorithm that takes into account all interactions between them. +Ideally, one single algorithm should be developed that does +all optimizations simultaneously and deals with all possible interactions. +In practice, such an algorithm is still far out of reach. +Instead some rather ad hoc (albeit important) combinations are chosen, +such as Common Subexpression Elimination and Register Allocation. +.[ +prabhala sethi common subexpressions +.] +.[ +sethi ullman optimal code +.] +.PP +In the Em Global Optimizer there is one separate algorithm for +every technique. +Note that this does not mean that all techniques are independent +of each other. +.PP +In principle, the optimization phases can be run in any order; +a phase may even be run more than once. +However, the following rules should be obeyed: +.IP - +the Live Variable analysis phase (LV) must be run prior to +Register Allocation (RA), as RA uses information outputted by LV. +.IP - +RA should be the last phase; this is a consequence of the way +the interface between RA and the Code Generator is defined. +.LP +The ordering of the phases has significant impact on +the quality of the produced code. +In +.[ +wulf overview production quality carnegie-mellon +.] +two kinds of phase ordering problems are distinguished. +If two techniques A and B both take away opportunities of each other, +there is a "negative" ordering problem. +If, on the other hand, both A and B introduce new optimization +opportunities for each other, the problem is called "positive". +In the Global Optimizer the following interactions must be +taken into account: +.IP - +Inline Substitution (IL) may create new opportunities for most +other techniques, so it should be run as early as possible +.IP - +Use Definition analysis (UD) may introduce opportunities for LV. +.IP - +Strength Reduction may create opportunities for UD +.LP +The optimizer has a default phase ordering, which can +be changed by the user. +.NH 2 +Structure of this document +.PP +The remaining chapters of this document each describe one +phase of the optimizer. +For every phase, we describe its task, its design, +its implementation, and its source files. +The latter two sections are intended to aid the +maintenance of the optimizer and +can be skipped by the initial reader. +.NH 2 +References +.PP +There are very +few modern textbooks on optimization. +Chapters 12, 13, and 14 of +.[ +aho compiler design +.] +are a good introduction to the subject. +Wulf et. al. +.[ +wulf optimizing compiler +.] +describe one specific optimizing (Bliss) compiler. +Anklam et. al. +.[ +anklam vax-11 +.] +discuss code generation and optimization in +compilers for one specific machine (a Vax-11). +Kirchgaesner et. al. +.[ +optimizing ada compiler +.] +present a brief description of many +optimizations; the report also contains a lengthy (over 60 pages) +bibliography. +.PP +The number of articles on optimization is quite impressive. +The Lowry and Medlock paper on the Fortran H compiler +.[ +object code optimization Lowry Medlock +.] +is a classical one. +Other papers on global optimization are. +.[ +faiman optimizing pascal +.] +.[ +perkins sites +.] +.[ +harrison general purpose optimizing +.] +.[ +morel partial redundancies +.] +.[ +Mintz global optimizer +.] +Freudenberger +.[ +freudenberger setl optimizer +.] +describes an optimizer for a Very High Level Language (SETL). +The Production-Quality Compiler-Compiler (PQCC) project uses +very sophisticated compiler techniques, as described in. +.[ +wulf overview ieee +.] +.[ +wulf overview carnegie-mellon +.] +.[ +wulf machine-relative +.] +.PP +Several Ph.D. theses are dedicated to optimization. +Davidson +.[ +davidson simplifying +.] +outlines a machine-independent peephole optimizer that +improves assembly code. +Katkus +.[ +katkus +.] +describes how efficient programs can be obtained at little cost by +optimizing only a small part of a program. +Photopoulos +.[ +photopoulos mixed code +.] +discusses the idea of generating interpreted intermediate code as well +as assembly code, to obtain programs that are both small and fast. +Shaffer +.[ +shaffer automatic +.] +describes the theory of automatic subroutine generation. +.] +Leverett +.[ +leverett register allocation compilers +.] +deals with register allocation in the PQCC compilers. +.PP +References to articles about specific optimization techniques +will be given in later chapters. diff --git a/doc/ego/ra/.distr b/doc/ego/ra/.distr new file mode 100644 index 00000000..d9cbc6df --- /dev/null +++ b/doc/ego/ra/.distr @@ -0,0 +1,4 @@ +ra1 +ra2 +ra3 +ra4 diff --git a/doc/ego/ra/ra1 b/doc/ego/ra/ra1 new file mode 100644 index 00000000..fb5343f9 --- /dev/null +++ b/doc/ego/ra/ra1 @@ -0,0 +1,33 @@ +.bp +.NH 1 +Register Allocation +.NH 2 +Introduction +.PP +The efficient usage of the general purpose registers +of the target machine plays a key role in any optimizing compiler. +This subject, often referred to as \fIRegister Allocation\fR, +has great impact on both the code generator and the +optimizing part of such a compiler. +The code generator needs registers for at least the evaluation of +arithmetic expressions; +the optimizer uses the registers to decrease the access costs +of frequently used entities (such as variables). +The design of an optimizing compiler must pay great +attention to the cooperation of optimization, register allocation +and code generation. +.PP +Register allocation has received much attention in literature (see +.[ +leverett register allocation compilers +.] +.[ +chaitin register coloring +.] +.[ +freiburghouse usage counts +.] +and +.[~[ +sites register +.]]). diff --git a/doc/ego/ra/ra2 b/doc/ego/ra/ra2 new file mode 100644 index 00000000..e6dfc138 --- /dev/null +++ b/doc/ego/ra/ra2 @@ -0,0 +1,139 @@ +.NH 2 +Usage of registers in ACK compilers +.PP +We will first describe the major design decisions +of the Amsterdam Compiler Kit, +as far as they concern register allocation. +Subsequently we will outline +the role of the Global Optimizer in the register +allocation process and the interface +between the code generator and the optimizer. +.NH 3 +Usage of registers without the intervention of the Global Optimizer +.PP +Registers are used for two purposes: +.IP 1. +for the evaluation of arithmetic expressions +.IP 2. +to hold local variables, for the duration of the procedure they +are local to. +.LP +It is essential to note that no translation part of the compilers, +except for the code generator, knows anything at all +about the register set of the target computer. +Hence all decisions about registers are ultimately made by +the code generator. +Earlier phases of a compiler can only \fIadvise\fR the code generator. +.PP +The code generator splits the register set into two: +a fixed part for the evaluation of expressions (called \fIscratch\fR +registers) and a fixed part to store local variables. +This partitioning, which depends only on the target computer, significantly +reduces the complexity of register allocation, at the penalty +of some loss of code quality. +.PP +The code generator has some (machine-dependent) knowledge of the access costs +of memory locations and registers and of the costs of saving and +restoring registers. (Registers are always saved by the \fIcalled\fR +procedure). +This knowledge is expressed in a set of procedures for each target machine. +The code generator also knows how many registers there are and of +which type they are. +A register can be of type \fIpointer\fR, \fIfloating point\fR +or \fIgeneral\fR. +.PP +The front ends of the compilers determine which local variables may +be put in a register; +such a variable may never be accessed indirectly (i.e. through a pointer). +The front end also determines the types and sizes of these variables. +The type can be any of the register types or the type \fIloop variable\fR, +which denotes a general-typed variable that is used as loop variable +in a for-statement. +All this information is collected in a \fIregister message\fR in +the EM code. +Such a message is a pseudo EM instruction. +This message also contains a \fIscore\fR field, +indicating how desirable it is to put this variable in a register. +A front end may assign a high score to a variable if it +was declared as a register variable (which is only possible in +some languages, such as "C"). +Any compiler phase before the code generator may change this score field, +if it has reason to do so. +The code generator bases its decisions on the information contained +in the register message, most notably on the score. +.PP +If the global optimizer is not used, +the score fields are set by the Peephole Optimizer. +This optimizer simply counts the number of occurrences +of every local (register) variable and adds this count +to the score provided by the front end. +In this way a simple, yet quite effective +register allocation scheme is achieved. +.NH 3 +The role of the Global Optimizer +.PP +The Global Optimizer essentially tries to improve the scheme +outlined above. +It uses the following principles for this purpose: +.IP - +Entities are not always assigned a register for the duration +of an entire procedure; smaller regions of the program text +may be considered too. +.IP - +several variables may be put in the same register simultaneously, +provided at most one of them is live at any point. +.IP - +besides local variables, other entities (such as constants and addresses of +variables and procedures) may be put in a register. +.IP - +more accurate cost estimates are used. +.LP +To perform its task, the optimizer must have some +knowledge of the target machine. +.NH 3 +The interface between the register allocator and the code generator +.PP +The RA phase of the optimizer must somehow be able to express its +decisions. +Such decisions may look like: 'put constant 1283 in a register from +line 12 to line 40'. +To be precise, RA must be able to tell the code generator to: +.IP - +initialize a register with some value +.IP - +update an entity from a register +.IP - +replace all occurrences of an entity in a certain region +of text by a reference to the register. +.LP +At least three problems occur here: the code generator is only used to +put local variables in registers, +it only assigns a register to a variable for the duration of an entire +procedure and it is not used to have some earlier compiler phase +make all the decisions. +.PP +All problems are solved by one mechanism, that involves no changes +to the code generator. +With every (non-scratch) register R that will be used in +a procedure P, we associate a new variable T, local to P. +The size of T is the same as the size of R. +A register message is generated for T with an exceptionally high score. +The scores of all original register messages are set to zero. +Consequently, the code generator will always assign precisely those new +variables to a register. +If the optimizer wants to put some entity, say the constant 1283, in +a register, it emits the code "T := 1283" and replaces all occurrences +of '1283' by T. +Similarly, it can put the address of a procedure in T and replace all +calls to that procedure by indirect calls. +Furthermore, it can put several different entities in T (and thus in R) +during the lifetime of P. +.PP +In principle, the code generated by the optimizer in this way would +always be valid EM code, even if the optimizer would be presented +a totally wrong description of the target computer register set. +In practice, it would be a waste of data as well as text space to +allocate memory for these new variables, as they will always be assigned +a register (in the correct order of events). +Hence, no memory locations are allocated for them. +For this reason they are called pseudo local variables. diff --git a/doc/ego/ra/ra3 b/doc/ego/ra/ra3 new file mode 100644 index 00000000..6ba296bd --- /dev/null +++ b/doc/ego/ra/ra3 @@ -0,0 +1,383 @@ +.NH 2 +The register allocation phase +.PP +.NH 3 +Overview +.PP +The RA phase deals with one procedure at a time. +For every procedure, it first determines which entities +may be put in a register. Such an entity +is called an \fIitem\fR. +For every item it decides during which parts of the procedure it +might be assigned a register. +Such a region is called a \fItimespan\fR. +For any item, several (possibly overlapping) timespans may +be considered. +A pair (item,timespan) is called an \fIallocation\fR. +If the items of two allocations are both live at some +point of time in the intersections of their timespans, +these allocations are said to be \fIrivals\fR of each other, +as they cannot be assigned the same register. +The rivals-set of every allocation is computed. +Next, the gains of assigning a register to an allocation are estimated, +for every allocation. +With all this information, decisions are made which allocations +to store in which registers (\fIpacking\fR). +Finally, the EM text is transformed to reflect these decisions. +.NH 3 +The item recognition subphase +.PP +RA tries to put the following entities in a register: +.IP - +a local variable for which a register message was found +.IP - +the address of a local variable for which no +register message was found +.IP - +the address of a global variable +.IP - +the address of a procedure +.IP - +a numeric constant. +.LP +Only the \fIaddress\fR of a global variable +may be put in a register, not the variable itself. +This approach avoids the very complex problems that would be +caused by procedure calls and indirect pointer references (see +.[~[ +aho design compiler +.] sections 14.7 and 14.8] +and +.[~[ +spillman side-effects +.]]). +Still, on most machines accessing a global variable using indirect +addressing through a register is much cheaper than +accessing it via its address. +Similarly, if the address of a procedure is put in a register, the +procedure can be called via an indirect call. +.PP +With every item we associate a register type. +This type is +.DS +for local variables: the type contained in the register message +for addresses of variables and procedures: the pointer type +for constants: the general type +.DE +An entity other than a local variable is not taken to be an item +if it is used only once within the current procedure. +.PP +An item is said to be \fIlive\fR at some point of the program text +if its value may be used before it is changed. +As addresses and constants are never changed, all items but local +variables are always live. +The region of text during which a local variable is live is +determined via the live/dead messages generated by the +Live Variable analysis phase of the Global Optimizer. +.NH 3 +The allocation determination subphase +.PP +If a procedure has more items than registers, +it may be advantageous to put an item in a register +only during those parts of the procedure where it is most +heavily used. +Such a part will be called a timespan. +With every item we may associate a set of timespans. +If two timespans of an item overlap, +at most one of them may be granted a register, +as there is no use in putting the same item in two +registers simultaneously. +If two timespans of an item are distinct, +both may be chosen; +the item will possibly be put in two +different registers during different parts of the procedure. +The timespan may also consist +of the whole procedure. +.PP +A list of (item,timespan) pairs (allocations) +is build, which will be the input to the decision making +subphase of RA (packing subphase). +This allocation list is the main data structure of RA. +The description of the remainder of RA will be in terms +of allocations rather than items. +The phrase "to assign a register to an allocation" means "to assign +a register to the item of the allocation for the duration of +the timespan of the allocation". +Subsequent subphases will add more information +to this list. +.PP +Several factors must be taken into account when a +timespan for an item is constructed: +.IP 1. +At any \fIentry point\fR of the timespan where the +item is live, +the register must be initialized with the item +.IP 2. +At any exit point of the timespan where the item is live, +the item must be updated. +.LP +In order to decrease these costs, we will only consider timespans with +one entry point +and no live exit points. +.NH 3 +The rivals computation subphase +.PP +As stated before, several different items may be put in the +same register, provided they are not live simultaneously. +For every allocation we determine the intersection +of its timespan and the lifetime of its item (i.e. the part of the +procedure during which the item is live). +The allocation is said to be busy during this intersection. +If two allocations are ever busy simultaneously they are +said to be rivals of each other. +The rivals information is added to the allocation list. +.NH 3 +The profits computation subphase +.PP +To make good decisions, the packing subphase needs to +know which allocations can be assigned the same register +(rivals information) and how much is gained by +granting an allocation a register. +.PP +Besides the gains of using a register instead of an +item, +two kinds of overhead costs must be +taken into account: +.IP - +the register must be initialized with the item +.IP - +the register must be saved at procedure entry +and restored at procedure exit. +.LP +The latter costs should not be due to a single +allocation, as several allocations can be assigned the same register. +These costs are dealt with after packing has been done. +They do not influence the decisions of the packing algorithm, +they may only undo them. +.PP +The actual profits consist of improvements +of execution time and code size. +As the former is far more difficult to estimate , we will +discuss code size improvements first. +.PP +The gains of putting a certain item in a register +depends on how the item is used. +Suppose the item is +a pointer variable. +On machines that do not have a +double-indirect addressing mode, +two instructions are needed to dereference the variable +if it is not in a register, but only one if it is put in a register. +If the variable is not dereferenced, but simply copied, one instruction +may be sufficient in both cases. +So the gains of putting a pointer variable in a register are higher +if the variable is dereferenced often. +.PP +To make accurate estimates, detailed knowledge of +the target machine and of the code generator +would be needed. +Therefore, a simplification has been made that substantially limits +the amount of target machine information that is needed. +The estimation of the number of bytes saved does +not take into account how an item is used. +Rather, an average number is used. +So these gains are computed as follows: +.DS +#bytes_saved = #occurrences * gains_per_occurrence +.DE +The number of occurrences is derived from +the EM code. +Note that this is not exact either, +as there is no one-to-one correspondence between occurrences in +the EM code and in the assembler code. +.PP +The gains of one occurrence depend on: +.IP 1. +the type of the item +.IP 2. +the size of the item +.IP 3. +the type of the register +.LP +and for local variables and addresses of local variables: +.IP 4. +the type of the local variable +.IP 5. +the offset of the variable in the stackframe +.LP +For every allocation we try two types of registers: the register type +of the item and the general register type. +Only the type with the highest profits will subsequently be used. +This type is added to the allocation information. +.PP +To compute the gains, RA uses a machine-dependent table +that is read from a machine descriptor file. +By means of this table the number of bytes saved can be computed +as a function of the five properties. +.PP +The costs of initializing a register with an item +is determined in a similar way. +The cost of one initialization is also +obtained from the descriptor file. +Note that there can be at most one initialization for any +allocation. +.PP +To summarize, the number of bytes a certain allocation would +save is computed as follows: +.DS +net_bytes_saved = bytes_saved - init_cost +bytes_saved = #occurrences * gains_per_occ +init_cost = #initializations * costs_per_init +.DE +.PP +It is inherently more difficult to estimate the execution +time saved by putting an item in a register, +because it is impossible to predict how +many times an item will be used dynamically. +If an occurrence is part of a loop, +it may be executed many times. +If it is part of a conditional statement, +it may never be executed at all. +In the latter case, the speed of the program may even get +worse if an initialization is needed. +As a clear example, consider the piece of "C" code in Fig. 13.1. +.DS +switch(expr) { + case 1: p(); break; + case 2: p(); p(); break; + case 3: p(); break; + default: break; +} + +Fig. 13.1 A "C" switch statement +.DE +Lots of bytes may be saved by putting the address of procedure p +in a register, as p is called four times (statically). +Dynamically, p will be called zero, one or two times, +depending on the value of the expression. +.PP +The optimizer uses the following strategy for optimizing +execution time: +.IP 1. +try to put items in registers during \fIloops\fR first +.IP 2. +always keep the initializing code outside the loop +.IP 3. +if an item is not used in a loop, do not put it in a register if +the initialization costs may be higher than the gains +.LP +The latter condition can be checked by determining the +minimal number of usages (dynamically) of the item during the procedure, +via a shortest path algorithm. +In the example above, this minimal number is zero, so the address of +p is not put in a register. +.PP +The costs of one occurrence is estimated as described above for the +code size. +The number of dynamic occurrences is guessed by looking at the +loop nesting level of every occurrence. +If the item is never used in a loop, +the minimal number of occurrences is used. +From these facts, the execution time improvement is assessed +for every allocation. +.NH 3 +The packing subphase +.PP +The packing subphase takes as input the allocation +list and outputs a +description of which allocations should be put +in which registers. +So it is essentially the decision making part of RA. +.PP +The packing system tries to assign a register to allocations one +at a time, in some yet to be defined order. +For every allocation A, it first checks if there is a register +(of the right type) +that is already assigned to one or more allocations, +none of which are rivals of A. +In this case A is assigned the same register. +Else, A is assigned a new register, if one exists. +A table containing the number of free registers for every type +is maintained. +It is initialized with the number of non-scratch registers of +the target computer and updated whenever a +new register is handed out. +The packing algorithm stops when no more allocations can +or need be assigned a register. +.PP +After an allocation A has been packed, +all allocations with non-disjunct timespans (including +A itself) are removed from the allocation list. +.PP +In case the number of items exceeds the number of registers, it +is important to choose the most profitable allocations. +Due to the possibility of having several allocations +occupying the same register, +this problem is quite complex. +Our packing algorithm uses simple heuristic rules +and avoids any combinatorial search. +It has distinct rules for different costs measures. +.PP +If object code size is the most important factor, +the algorithm is greedy and chooses allocations in +decreasing order of their profits attribute. +It does not take into account the fact that +other allocations may be passed over because of +this decision. +.PP +If execution time is at prime stake, the algorithm +first considers allocations whose timespans consist of loops. +After all these have been packed, it considers the remaining +allocations. +Within the two subclasses, it considers allocations +with the highest profits first. +When assigning a register to an allocation with a loop +as timespan, the algorithm checks if the item has +already been put in a register during another loop. +If so, it tries to use the same register for the +new allocation. +After all packing has been done, +it checks if the item has always been assigned the same +register (although not necessarily during all loops). +If so, it tries to put the item in that register during +the entire procedure. This is possible +if the allocation (item,whole_procedure) is not a rival +of any allocation with a different item that has been +assigned to the same register. +Note that this approach is essentially 'bottom up', +as registers are first assigned over small regions +of text which are later collapsed into larger regions. +The advantage of this approach is the fact that +the decisions for one loop can be made independently +of all other loops. +.PP +After the entire packing process has been completed, +we compute for each register how much is gained in using +this register, by simply adding the net profits +of all allocations assigned to it. +This total yield should outweigh the costs of +saving/restoring the register at procedure entry/exit. +As most modern processors (e.g. 68000, Vax) have special +instructions to save/restore several registers, +the differential costs of saving one extra register are by +no means constant. +The costs are read from the machine descriptor file and +compared to the total yields of the registers. +As a consequence of this analysis, some allocations +may have their registers taken away. +.NH 3 +The transformation subphase +.PP +The final subphase of RA transforms the EM text according to the +decisions made by the packing system. +It traverses the text of the currently optimized procedure and +changes all occurrences of items at points where +they are assigned a register. +It also clears the score field of the register messages for +normal local variables and emits register messages with a very +high score for the pseudo locals. +At points where registers have to be initialized with items, +it generates EM code to do so. +Finally it tries to decrease the size of the stackframe +of the procedure by looking at which local variables need not +be given memory locations. diff --git a/doc/ego/ra/ra4 b/doc/ego/ra/ra4 new file mode 100644 index 00000000..4bfeef74 --- /dev/null +++ b/doc/ego/ra/ra4 @@ -0,0 +1,28 @@ +.NH 2 +Source files of RA +.PP +The sources of RA are in the following files and packages: +.IP ra.h: 14 +declarations of global variables and data structures +.IP ra.c: +the routine main; initialization of target machine-dependent tables +.IP items: +a routine to build the list of items of one procedure; +routines to manipulate items +.IP lifetime: +contains a subroutine that determines when items are live/dead +.IP alloclist: +contains subroutines that build the initial allocations list +and that compute the rivals sets. +.IP profits: +contains a subroutine that computes the profits of the allocations +and a routine that determines the costs of saving/restoring registers +.IP pack: +contains the packing subphase +.IP xform: +contains the transformation subphase +.IP interval: +contains routines to manipulate intervals of time +.IP aux: +contains auxiliary routines +.LP diff --git a/doc/ego/refs.gen b/doc/ego/refs.gen new file mode 100644 index 00000000..408fc50d --- /dev/null +++ b/doc/ego/refs.gen @@ -0,0 +1,120 @@ +%T A Practical Toolkit for Making Portable Compilers +%A A.S. Tanenbaum +%A H. van Staveren +%A E.G. Keizer +%A J.W. Stevenson +%I Vrije Universiteit, Amsterdam +%R Rapport nr IR-74 +%D October 1981 + +%T A Practical Toolkit for Making Portable Compilers +%A A.S. Tanenbaum +%A H. van Staveren +%A E.G. Keizer +%A J.W. Stevenson +%J CACM +%V 26 +%N 9 +%P 654-660 +%D September 1983 + +%T A Unix Toolkit for Making Portable Compilers +%A A.S. Tanenbaum +%A H. van Staveren +%A E.G. Keizer +%A J.W. Stevenson +%J Proceedings USENIX conf. +%C Toronto, Canada +%V 26 +%D July 1983 +%P 255-261 + +%T Using Peephole Optimization on Intermediate Code +%A A.S. Tanenbaum +%A H. van Staveren +%A J.W. Stevenson +%J TOPLAS +%V 4 +%N 1 +%P 21-36 +%D January 1982 + +%T Language- and Machine-independent Global Optimization on Intermediate Code +%A H.E. Bal +%A A.S. Tanenbaum +%J Computer Languages +%V 11 +%N 2 +%P 105-121 +%D April 1986 + +%T Description of a machine architecture for use with +block structured languages +%A A.S. Tanenbaum +%A H. van Staveren +%A E.G. Keizer +%A J.W. Stevenson +%I Vrije Universiteit, Amsterdam +%R Rapport nr IR-81 +%D August 1983 + +%T Amsterdam Compiler Kit documentation +%A A.S. Tanenbaum et. al. +%I Vrije Universiteit, Amsterdam +%R Rapport nr IR-90 +%D June 1984 + +%T The C Programming Language - Reference Manual +%A D.M. Ritchie +%I Bell Laboratories +%C Murray Hill, New Jersey +%D 1978 + +%T Unix programmer's manual, Seventh Edition +%A B.W. Kernighan +%A M.D. McIlroy +%I Bell Laboratories +%C Murray Hill, New Jersey +%V 1 +%D January 1979 + +%T A Tour Through the Portable C Compiler +%A S.C. Johnson +%I Bell Laboratories +%B Unix programmer's manual, Seventh Edition +%C Murray Hill, New Jersey +%D January 1979 + + +%T Ada Programming Language - MILITARY STANDARD +%A J.D. Ichbiah +%I U.S. Department of Defense +%R ANSI/MIL-STD-1815A +%D 22 January 1983 + +%T Rationale for the Design of the Ada Programming Language +%A J.D. Ichbiah +%J SIGPLAN Notices +%V 14 +%N 6 +%D June 1979 + +%T The Programming Languages LISP and TRAC +%A W.L. van der Poel +%I Technische Hogeschool Delft +%C Delft +%D 1972 + +%T Compiler construction +%A W.M. Waite +%A G. Goos +%I Springer-Verlag +%C New York +%D 1984 + +%T The C Programming Language +%A B.W. Kernighan +%A D.M. Ritchie +%I Prentice-Hall, Inc +%C Englewood Cliffs,NJ +%D 1978 diff --git a/doc/ego/refs.opt b/doc/ego/refs.opt new file mode 100644 index 00000000..6029c7bc --- /dev/null +++ b/doc/ego/refs.opt @@ -0,0 +1,546 @@ +%T Principles of compiler design +%A A.V. Aho +%A J.D. Ullman +%I Addison-Wesley +%C Reading, Massachusetts +%D 1978 + +%T The Design and Analysis of Computer Algorithms +%A A.V. Aho +%A J.E. Hopcroft +%A J.D. Ullman +%I Addison-Wesley +%C Reading, Massachusetts +%D 1974 + +%T Code generation in a machine-independent compiler +%A R.G.G. Cattell +%A J.M. Newcomer +%A B.W. Leverett +%J SIGPLAN Notices +%V 14 +%N 8 +%P 65-75 +%D August 1979 + +%T An algorithm for Reduction of Operator Strength +%A J. Cocke +%A K. Kennedy +%J CACM +%V 20 +%N 11 +%P 850-856 +%D November 1977 + +%T Reduction of Operator Strength +%A F.E. Allen +%A J. Cocke +%A K. Kennedy +%B Program Flow Analysis +%E S.S. Muchnick and D. Jones +%I Prentice-Hall +%C Englewood Cliffs, N.J. +%D 1981 + +%T Simplifying Code Generation Through Peephole Optimization +%A J.W. Davidson +%R Ph.D. thesis +%I Dept. of Computer Science +%C Univ. of Arizona +%D December 1981 + +%T A study of selective optimization techniques +%A G.R. Katkus +%R Ph.D. Thesis +%C University of Southern California +%D 1973 + +%T Automatic subroutine generation in an optimizing compiler +%A J.B. Shaffer +%R Ph.D. Thesis +%C University of Maryland +%D 1978 + +%T Optimal mixed code generation for microcomputers +%A D.S. Photopoulos +%R Ph.D. Thesis +%C Northeastern University +%D 1981 + +%T The Design of an Optimizing Compiler +%A W.A. Wulf +%A R.K. Johnsson +%A C.B. Weinstock +%A S.O. Hobbs +%A C.M. Geschke +%I American Elsevier Publishing Company +%C New York +%D 1975 + +%T Retargetable Compiler Code Generation +%A M. Ganapathi +%A C.N. Fischer +%A J.L. Hennessy +%J ACM Computing Surveys +%V 14 +%N 4 +%P 573-592 +%D December 1982 + +%T An Optimizing Pascal Compiler +%A R.N. Faiman +%A A.A. Kortesoja +%J IEEE Trans. on Softw. Eng. +%V 6 +%N 6 +%P 512-518 +%D November 1980 + +%T Experience with the SETL Optimizer +%A S.M. Freudenberger +%A J.T. Schwartz +%J TOPLAS +%V 5 +%N 1 +%P 26-45 +%D Januari 1983 + +%T An Optimizing Ada Compiler +%A W. Kirchgaesner +%A J. Uhl +%A G. Winterstein +%A G. Goos +%A M. Dausmann +%A S. Drossopoulou +%I Institut fur Informatik II, Universitat Karlsruhe +%D February 1983 + +%T A Fast Algorithm for Finding Dominators +in a Flowgraph +%A T. Lengauer +%A R.E. Tarjan +%J TOPLAS +%V 1 +%N 1 +%P 121-141 +%D July 1979 + +%T Optimization of hierarchical directed graphs +%A M.T. Lepage +%A D.T. Barnard +%A A. Rudmik +%J Computer Languages +%V 6 +%N 1 +%P 19-34 +%D Januari 1981 + +%T Object Code Optimization +%A E.S. Lowry +%A C.W. Medlock +%J CACM +%V 12 +%N 1 +%P 13-22 +%D Januari 1969 + +%T Automatic Program Improvement: +Variable Usage Transformations +%A B. Maher +%A D.H. Sleeman +%J TOPLAS +%V 5 +%N 2 +%P 236-264 +%D April 1983 + +%T The design of a global optimizer +%A R.J. Mintz +%A G.A. Fisher +%A M. Sharir +%J SIGPLAN Notices +%V 14 +%N 9 +%P 226-234 +%D September 1979 + +%T Global Optimization by Suppression of Partial Redundancies +%A E. Morel +%A C. Renvoise +%J CACM +%V 22 +%N 2 +%P 96-103 +%D February 1979 + +%T Efficient Computation of Expressions with Common Subexpressions +%A B. Prabhala +%A R. Sethi +%J JACM +%V 27 +%N 1 +%P 146-163 +%D Januari 1980 + +%T An Analysis of Inline Substitution for a Structured +Programming Language +%A R.W. Scheifler +%J CACM +%V 20 +%N 9 +%P 647-654 +%D September 1977 + +%T Immediate Predominators in a Directed Graph +%A P.W. Purdom +%A E.F. Moore +%J CACM +%V 15 +%N 8 +%P 777-778 +%D August 1972 + +%T The Generation of Optimal Code for Arithmetic Expressions +%A R. Sethi +%A J.D. Ullman +%J JACM +%V 17 +%N 4 +%P 715-728 +%D October 1970 + +%T Exposing side-effects in a PL/I optimizing compiler +%A T.C. Spillman +%B Information Processing 1971 +%I North-Holland Publishing Company +%C Amsterdam +%P 376-381 +%D 1971 + +%T Inner Loops in Flowgraphs and Code Optimization +%A S. Vasudevan +%J Acta Informatica +%N 17 +%P 143-155 +%D 1982 + +%T A New Strategy for Code Generation - the General-Purpose +Optimizing Compiler +%A W.H. Harrison +%J IEEE Trans. on Softw. Eng. +%V 5 +%N 4 +%P 367-373 +%D July 1979 + +%T PQCC: A Machine-Relative Compiler Technology +%A W.M. Wulf +%R CMU-CS-80-144 +%I Carnegie-Mellon University +%C Pittsburgh +%D 25 september 1980 + +%T Machine-independent Pascal code optimization +%A D.R. Perkins +%A R.L. Sites +%J SIGPLAN Notices +%V 14 +%N 8 +%P 201-207 +%D August 1979 + +%T A Case Study of a New Code Generation Technique for Compilers +%A J.L. Carter +%J CACM +%V 20 +%N 12 +%P 914-920 +%D December 1977 + +%T Table-driven Code Generation +%A S.L. Graham +%J IEEE Computer +%V 13 +%N 8 +%P 25-33 +%D August 1980 + +%T Register Allocation in Optimizing Compilers +%A B.W. Leverett +%R Ph.D. Thesis, CMU-CS-81-103 +%I Carnegie-Mellon University +%C Pittsburgh +%D February 1981 + +%T Register Allocation via Coloring +%A G.J. Chaitin +%A M.A. Auslander +%A A.K. Chandra +%A J. Cocke +%A M.E. Hopkins +%A P.W. Markstein +%J Computer Languages +%V 6 +%N 1 +%P 47-57 +%D January 1981 + +%T How to Call Procedures, or Second Thoughts on +Ackermann's Function +%A B.A. Wichmann +%J Software - Practice and Experience +%V 7 +%P 317-329 +%D 1977 + +%T Register Allocation Via Usage Counts +%A R.A. Freiburghouse +%J CACM +%V 17 +%N 11 +%P 638-642 +%D November 1974 + +%T Machine-independent register allocation +%A R.L. Sites +%J SIGPLAN Notices +%V 14 +%N 8 +%P 221-225 +%D August 1979 + +%T An Overview of the Production-Quality Compiler-Compiler Project +%A B.W. Leverett +%A R.G.G Cattell +%A S.O. Hobbs +%A J.M. Newcomer +%A A.H. Reiner +%A B.R. Schatz +%A W.A. Wulf +%J IEEE Computer +%V 13 +%N 8 +%P 38-49 +%D August 1980 + +%T An Overview of the Production-Quality Compiler-Compiler Project +%A B.W. Leverett +%A R.G.G Cattell +%A S.O. Hobbs +%A J.M. Newcomer +%A A.H. Reiner +%A B.R. Schatz +%A W.A. Wulf +%R CMU-CS-79-105 +%I Carnegie-Mellon University +%C Pittsburgh +%D 1979 + +%T Topics in Code Generation and Register Allocation +%A B.W. Leverett +%R CMU-CS-82-130 +%I Carnegie-Mellon University +%C Pittsburgh +%D 28 July 1982 + +%T Predicting the Effects of Optimization on a Procedure Body +%A J.E. Ball +%J SIGPLAN Notices +%V 14 +%N 8 +%P 214-220 +%D August 1979 + +%T The C Language Calling Sequence +%A S.C. Johnson +%A D.M. Ritchie +%I Bell Laboratories +%C Murray Hill, New Jersey +%D September 1981 + +%T A Generalization of Two Code Ordering Optimizations +%A C.W. Fraser +%R TR 82-11 +%I Department of Computer Science +%C The University of Arizona, Tucson +%D October 1982 + +%T A Survey of Data Flow Analysis Techniques +%A K. Kennedy +%B Program Flow Analysis +%E S.S. Muchnick and D. Jones +%I Prentice-Hall +%C Englewood Cliffs +%D 1981 + +%T Delayed Binding in PQCC Generated Compilers +%A W.A. Wulf +%A K.V. Nori +%R CMU-CS-82-138 +%I Carnegie-Mellon University +%C Pittsburgh +%D 1982 + +%T Interprocedural Data Flow Analysis in the presence +of Pointers, Procedure Variables, and Label Variables +%A W.E. Weihl +%J Conf. Rec. of the 7th ACM Symp. on Principles of +Programming Languages +%C Las Vegas, Nevada +%P 83-94 +%D 1980 + +%T Low-Cost, High-Yield Code Optimization +%A D.R. Hanson +%R TR 82-17 +%I Department of Computer Science +%C The University of Arizona, Tucson +%D November 1982 + +%T Program Flow Analysis +%E S.S. Muchnick and D. Jones +%I Prentice-Hall +%C Englewood Cliffs +%D 1981 + +%T A machine independent algorithm for code generation and its +use in retargetable compilers +%A R. Glanville +%R Ph.D. thesis +%C University of California, Berkeley +%D December 1977 + +%T A formal framework for the derivation of machine-specific optimizers +%A R. Giegerich +%J TOPLAS +%V 5 +%N 3 +%P 478-498 +%D July 1983 + +%T Engineering a compiler: Vax-11 code generation and optimization +%A P. Anklam +%A D. Cutler +%A R. Heinen +%A M. MacLaren +%I Digital Equipment Corporation +%D 1982 + +%T Analyzing exotic instructions for a retargetable code generator +%A T.M. Morgan +%A L.A. Rowe +%J SIGPLAN Notices +%V 17 +%N 6 +%P 197-204 +%D June 1982 + +%T TCOLAda and the Middle End of the PQCC Ada Compiler +%A B.M. Brosgol +%J SIGPLAN Notices +%V 15 +%N 11 +%P 101-112 +%D November 1980 + +%T Implementation Implications of Ada Generics +%A G. Bray +%J Ada Letters +%V III +%N 2 +%P 62-71 +%D September 1983 + +%T Attributed Linear Intermediate Representations for Retargetable +Code Generators +%A M. Ganapathi +%A C.N. Fischer +%J Software-Practice and Experience +%V 14 +%N 4 +%P 347-364 +%D April 1984 + +%T UNCOL: The myth and the fact +%A T.B. Steel +%J Annu. Rev. Autom. Program. +%V 2 +%D 1960 +%P 325-344 + +%T Experience with a Graham-Glanville Style Code Generator +%A P. Aigrain +%A S.L. Graham +%A R.R. Henry +%A M.K. McKusick +%A E.P. Llopart +%J SIGPLAN Notices +%V 19 +%N 6 +%D June 1984 +%P 13-24 + +%T Using Dynamic Programming to generate Optimized Code in a +Graham-Glanville Style Code Generator +%A T.W. Christopher +%A P.J. Hatcher +%A R.C. Kukuk +%J SIGPLAN Notices +%V 19 +%N 6 +%D June 1984 +%P 25-36 + +%T Peep - An Architectural Description Driven Peephole Optimizer +%A R.R. Kessler +%J SIGPLAN Notices +%V 19 +%N 6 +%D June 1984 +%P 106-110 + +%T Automatic Generation of Peephole Optimizations +%A J.W. Davidson +%A C.W. Fraser +%J SIGPLAN Notices +%V 19 +%N 6 +%D June 1984 +%P 111-116 + +%T Analysing and Compressing Assembly Code +%A C.W. Fraser +%A E.W. Myers +%A A.L. Wendt +%J SIGPLAN Notices +%V 19 +%N 6 +%D June 1984 +%P 117-121 + +%T Register Allocation by Priority-based Coloring +%A F. Chow +%A J. Hennessy +%J SIGPLAN Notices +%V 19 +%N 6 +%D June 1984 +%P 222-232 +%V 19 +%N 6 +%D June 1984 +%P 117-121 + +%T Code Selection through Object Code Optimization +%A J.W. Davidson +%A C.W. Fraser +%I Dept. of Computer Science +%C Univ. of Arizona +%D November 1981 + +%T A Portable Machine-Independent Global Optimizer - Design +and Measurements +%A F.C. Chow +%I Computer Systems Laboratory +%C Stanford University +%D December 1983 diff --git a/doc/ego/refs.stat b/doc/ego/refs.stat new file mode 100644 index 00000000..56fcd7fd --- /dev/null +++ b/doc/ego/refs.stat @@ -0,0 +1,29 @@ +%T An analysis of Pascal Programs +%A L.R. Carter +%I UMI Research Press +%C Ann Arbor, Michigan +%D 1982 + +%T An Emperical Study of FORTRAN Programs +%A D.E. Knuth +%J Software - Practice and Experience +%V 1 +%P 105-133 +%D 1971 + +%T F77 Performance +%A D.A. Mosher +%A R.P. Corbett +%J ;login: +%V 7 +%N 3 +%D June 1982 + +%T Ada Language Statistics for the iMAX 432 Operating System +%A S.F. Zeigler +%A R.P. Weicker +%J Ada LETTERS +%V 2 +%N 6 +%P 63-67 +%D May 1983 diff --git a/doc/ego/sp/.distr b/doc/ego/sp/.distr new file mode 100644 index 00000000..fb3527e1 --- /dev/null +++ b/doc/ego/sp/.distr @@ -0,0 +1 @@ +sp1 diff --git a/doc/ego/sp/sp1 b/doc/ego/sp/sp1 new file mode 100644 index 00000000..20c633f8 --- /dev/null +++ b/doc/ego/sp/sp1 @@ -0,0 +1,171 @@ +.bp +.NH 1 +Stack pollution +.NH 2 +Introduction +.PP +The "Stack Pollution" optimization technique (SP) decreases the costs +(time as well as space) of procedure calls. +In the EM calling sequence, the actual parameters are popped from +the stack by the \fIcalling\fR procedure. +The ASP (Adjust Stack Pointer) instruction is used for this purpose. +A call in EM is shown in Fig. 8.1 +.DS +Pascal: EM: + +f(a,2) LOC 2 + LOE A + CAL F + ASP 4 -- pop 4 bytes + +Fig. 8.1 An example procedure call in Pascal and EM +.DE +As procedure calls occur often in most programs, +the ASP is one of the most frequently used EM instructions. +.PP +The main intention of removing the actual parameters after a procedure call +is to avoid the stack size to increase rapidly. +Yet, in some cases, it is possible to \fIdelay\fR or even \fIavoid\fR the +removal of the parameters without letting the stack grow +significantly. +In this way, considerable savings in code size and execution time may +be achieved, at the cost of a slightly increased stack size. +.PP +A stack adjustment may be delayed if there is some other stack adjustment +later on in the same basic block. +The two ASPs can be combined into one. +.DS +Pascal: EM: optimized EM: + +f(a,2) LOC 2 LOC 2 +g(3,b,c) LOE A LOE A + CAL F CAL F + ASP 4 LOE C + LOE C LOE B + LOE B LOC 3 + LOC 3 CAL G + CAL G ASP 10 + ASP 6 + +Fig. 8.2 An example of local Stack Pollution +.DE +The stacksize will be increased only temporarily. +If the basic block contains another ASP, the ASP 10 may subsequently be +combined with that next ASP, and so on. +.PP +For some back ends, a stack adjustment also takes place +at the point of a procedure return. +There is no need to specify the number of bytes to be popped at a +return. +This provides an opportunity to remove ASPs more globally. +If all ASPs outside any loop are removed, the increase of the +stack size will still only be small, as no such ASP is executed more +than once without an intervening return from the procedure it is part of. +.PP +This second approach is not generally applicable to all target machines, +as some back ends require the stack to be cleaned up at the point of +a procedure return. +.NH 2 +Implementation +.PP +There is one main problem the implementation has to solve. +In EM, the stack is not only used for passing parameters, +but also for evaluating expressions. +Hence, ASP instructions can only be combined or removed +if certain conditions are satisfied. +.PP +Two consecutive ASPs of one basic block can only be combined +(as described above) if: +.IP 1. +On no point of text in between the two ASPs, any item is popped from +the stack that was pushed onto it before the first ASP. +.IP 2. +The number of bytes popped from the stack by the second ASP must equal +the number of bytes pushed since the first ASP. +.LP +Condition 1. is not satisfied in Fig. 8.3. +.DS +Pascal: EM: + +5 + f(10) + g(30) LOC 5 + LOC 10 + CAL F + ASP 2 -- cannot be removed + LFR 2 -- push function result + ADI 2 + LOC 30 + CAL G + ASP 2 + LFR 2 + ADI 2 +Fig. 8.3 An illegal transformation +.DE +If the first ASP were removed (delayed), the first ADI would add +10 and f(10), instead of 5 and f(10). +.sp +Condition 2. is not satisfied in Fig. 8.4. +.DS +Pascal: EM: + +f(10) + 5 * g(30) LOC 10 + CAL F + ASP 2 + LFR 2 + LOC 5 + LOC 30 + CAL G + ASP 2 + LFR 2 + MLI 2 -- 5 * g(30) + ADI 2 + +Fig. 8.4 A second illegal transformation +.DE +If the two ASPs were combined into one 'ASP 4', the constant 5 would +have been popped, rather than the parameter 10 (so '10 + f(10)*g(30)' +would have been computed). +.PP +The second approach to deleting ASPs (i.e. let the procedure return +do the stack clean-up) +is only applied to the last ASP of every basic block. +Any preceding ASPs are dealt with by the first approach. +The last ASP of a basic block B will only be removed if: +.IP - +on no path in the control flow graph from B to any block containing a +RET (return) there is a basic block that, at some point of its text, pops +items from the stack that it has not itself pushed earlier. +.LP +Clearly, if this condition is satisfied, no harm can be done; no +other basic block will ever access items that were pushed +on the stack before the ASP. +.PP +The number of bytes pushed onto or popped from the stack can be +easily encoded in a so called "pop-push table". +The numbers in general depend on the target machine word- and pointer +size and on the argument given to the instruction. +For example, an ADS instruction is described by: +.DS + -a-p+p +.DE +which means: an 'ADS n' first pops an n-byte value (n being the argument), +next pops a pointer-size value and finally pushes a pointer-size value. +For some infrequently used EM instructions the pop-push numbers +cannot be computed statically. +.PP +The stack pollution algorithm first performs a depth first search over +the control flow graph and marks all blocks that do not satisfy +the global condition. +Next it visits all basic blocks in turn. +For every pair of adjacent ASPs, it checks conditions 1. and 2. and +combines the ASPs if they are satisfied. +The new ASP may be used as first ASP in the next pair. +If a condition fails, it simply continues with the next ASP. +Finally, the last ASP is removed if: +.IP - +nothing has been popped from the stack after the last ASP that was +pushed before it +.IP - +the block was not marked by the depth first search +.IP - +the block is not in a loop +.LP diff --git a/doc/ego/sr/.distr b/doc/ego/sr/.distr new file mode 100644 index 00000000..6de85411 --- /dev/null +++ b/doc/ego/sr/.distr @@ -0,0 +1,4 @@ +sr1 +sr2 +sr3 +sr4 diff --git a/doc/ego/sr/sr1 b/doc/ego/sr/sr1 new file mode 100644 index 00000000..cc8f660e --- /dev/null +++ b/doc/ego/sr/sr1 @@ -0,0 +1,44 @@ +.bp +.NH 1 +Strength reduction +.NH 2 +Introduction +.PP +The Strength Reduction optimization technique (SR) +tries to replace expensive operators +by cheaper ones, +in order to decrease the execution time +of the program. +A classical example is replacing a 'multiplication by 2' +by an addition or a shift instruction. +These kinds of local transformations are already +done by the EM Peephole Optimizer. +Strength reduction can also be applied +more generally to operators used in a loop. +.DS +i := 1; i := 1; +while i < 100 loop --> TMP := i * 118; + put(i * 118); while i < 100 loop + i := i + 1; put(TMP); +end loop; i := i + 1; + TMP := TMP + 118; + end loop; + +Fig. 6.1 An example of Strenght Reduction +.DE +In Fig. 6.1, a multiplication inside a loop is +replaced by an addition inside the loop and a multiplication +outside the loop. +Clearly, this is a global optimization; it cannot +be done by a peephole optimizer. +.PP +In some cases a related technique, \fItest replacement\fR, +can be used to eliminate the +loop variable i. +This technique will not be discussed in this report. +.sp 0 +In the example above, the resulting code +can be further optimized by using +constant propagation. +Obviously, this is not the task of the +Strength Reduction phase. diff --git a/doc/ego/sr/sr2 b/doc/ego/sr/sr2 new file mode 100644 index 00000000..c3000f93 --- /dev/null +++ b/doc/ego/sr/sr2 @@ -0,0 +1,217 @@ +.NH 2 +The model of strength reduction +.PP +In this section we will describe +the transformations performed by +Strength Reduction (SR). +Before doing so, we will introduce the +central notion of an induction variable. +.NH 3 +Induction variables +.PP +SR looks for variables whose +values form an arithmetic progression +at the beginning of a loop. +These variables are called induction variables. +The most frequently occurring example of such +a variable is a loop-variable in a high-order +programming language. +Several quite sophisticated models of strength +reduction can be found in the literature. +.[ +cocke reduction strength cacm +.] +.[ +allen cocke kennedy reduction strength +.] +.[ +lowry medlock cacm +.] +.[ +aho compiler design +.] +In these models the notion of an induction variable +is far more general than the intuitive notion +of a loop-variable. +The definition of an induction variable we present here +is more restricted, +yielding a simpler model and simpler transformations. +We think the principle source for strength reduction lies in +expressions using a loop-variable, +i.e. a variable that is incremented or decremented +by the same amount after every loop iteration, +and that cannot be changed in any other way. +.PP +Of course, the EM code does not contain high level constructs +such as for-statements. +We will define an induction variable in terms +of the Intermediate Code of the optimizer. +Note that the notions of a loop in the +EM text and of a firm basic block +were defined in section 3.3.5. +.sp +.UL definition +.sp 0 +An induction variable i of a loop L is a local variable +that is never accessed indirectly, +whose size is the word size of the target machine, and +that is assigned exactly once within L, +the assignment: +.IP - +being of the form i := i + c or i := c +i, +c is a constant +called the \fIstep value\fR of i. +.IP - +occurring in a firm block of L. +.LP +(Note that the first restriction on the assignment +is not described in terms of the Intermediate Code; +we will give such a description later; the current +definition is easier to understand however). +.NH 3 +Recognized expressions +.PP +SR recognizes certain expressions using +an induction variable and replaces +them by cheaper ones. +Two kinds of expensive operations are recognized: +multiplication and array address computations. +The expressions that are simplified must +use an induction variable +as an operand of +a multiplication or as index in an array expression. +.PP +Often a linear function of an induction variable is used, +rather than the variable itself. +In these cases optimization is still possible. +We call such expressions \fIiv-expressions\fR. +.sp +.UL definition: +.sp 0 +An iv-expression of an induction variable i of a loop L is +an expression that: +.IP - +uses only the operators + and - (unary as well as binary) +.IP - +uses i as operand exactly once +.IP - +uses (besides i) only constants or variables that are +never changed in L as operands. +.LP +.PP +The expressions recognized by SR are of the following forms: +.IP (1) +iv_expression * constant +.IP (2) +constant * iv_expression +.IP (3) +A[iv-expression] := (assign to array element) +.IP (4) +A[iv-expression] (use array element) +.IP (5) +& A[iv-expression] (take address of array element) +.LP +(Note that EM has different instructions to use an array element, +store into one, or take the address of one, resp. LAR, SAR, and AAR). +.sp 0 +The size of the elements of A must +be known statically. +In cases (3) and (4) this size +must equal the word size of the +target machine. +.NH 3 +Transformations +.PP +With every recognized expression we associate +a new temporary local variable TMP, +allocated in the stack frame of the +procedure containing the expression. +At any program point within the loop, TMP will +contain the following value: +.IP multiplication: 18 +the current value of iv-expression * constant +.IP arrays: +the current value of &A[iv-expression]. +.LP +In the second case, TMP essentially is a pointer variable, +pointing to the element of A that is currently in use. +.sp 0 +If the same expression occurs several times in the loop, +the same temporary local is used each time. +.PP +Three transformations are applied to the EM text: +.IP (1) +TMP is initialized with the right value. +This initialization takes place just +before the loop. +.IP (2) +The recognized expression is simplified. +.IP (3) +TMP is incremented; this takes place just +after the induction variable is incremented. +.LP +For multiplication, the initial value of TMP +is the value of the recognized expression at +the program point immediately before the loop. +For arrays, TMP is initialized with the address +of the first array element that is accessed. +So the initialization code is: +.DS +TMP := iv-expression * constant; or +TMP := &A[iv-expression] +.DE +At the point immediately before the loop, +the induction variable will already have been +initialized, +so the value used in the code above will be the +value it has during the first iteration. +.PP +For multiplication, the recognized expression can simply be +replaced by TMP. +For array optimizations, the replacement +depends on the form: +.DS +\fIform\fR \fIreplacement\fR +(3) A[iv-expr] := *TMP := (assign indirect) +(4) A[iv-expr] *TMP (use indirect) +(5) &A[iv-expr] TMP +.DE +The '*' denotes the indirect operator. (Note that +EM has different instructions to do +an assign-indirect and a use-indirect). +As the size of the array elements is restricted +to be the word size in case (3) and (4), +only one EM instruction needs to +be generated in all cases. +.PP +The amount by which TMP is incremented is: +.IP multiplication: 18 +step value * constant +.IP arrays: +step value * element size +.LP +Note that the step value (see definition of induction variable above), +the constant, and the element size (see previous section) can all +be determined statically. +If the sign of the induction variable in the +iv-expression is negative, the amount +must be negated. +.PP +The transformations are demonstrated by an example. +.DS +i := 100; i := 100; +while i > 1 loop TMP := (6-i) * 5; + X := (6-i) * 5 + 2; while i > 1 loop + Y := (6-i) * 5 - 8; --> X := TMP + 2; + i := i - 3; Y := TMP - 8; +end loop; i := i - 3; + TMP := TMP + 15; + end loop; + +Fig. 6.2 Example of complex Strength Reduction transformations +.DE +The expression '(6-i)*5' is recognized twice. The constant +is 5. +The step value is -3. +The sign of i in the recognized expression is '-'. +So the increment value of TMP is -(-3*5) = +15. diff --git a/doc/ego/sr/sr3 b/doc/ego/sr/sr3 new file mode 100644 index 00000000..12dbcff7 --- /dev/null +++ b/doc/ego/sr/sr3 @@ -0,0 +1,232 @@ +.NH 2 +Implementation +.PP +Like most phases, SR deals with one procedure +at a time. +Within a procedure, SR works on one loop at a time. +Loops are processed in textual order. +If loops are nested inside each other, +SR starts with the outermost loop and proceeds in the +inwards direction. +This order is chosen, +because it enables the optimization +of multi-dimensional array address computations, +if the elements are accessed in the usual way +(i.e. row after row, rather than column after column). +For every loop, SR first detects all induction variables +and then tries to recognize +expressions that can be optimized. +.NH 3 +Finding induction variables +.PP +The process of finding induction variables +can conveniently be split up +into two parts. +First, the EM text of the loop is scanned to find +all \fIcandidate\fR induction variables, +which are word-sized local variables +that are assigned precisely once +in the loop, within a firm block. +Second, for every candidate, the single assignment +is inspected, to see if it has the form +required by the definition of an induction variable. +.PP +Candidates are found by scanning the EM code of the loop. +During this scan, two sets are maintained. +The set "cand" contains all variables that were +assigned exactly once so far, within a firm block. +The set "dismiss" contains all variables that +should not be made a candidate. +Initially, both sets are empty. +If a variable is assigned to, it is put +in the cand set, if three conditions are met: +.IP 1. +the variable was not in cand or dismiss already +.IP 2. +the assignment takes place in a firm block +.IP 3. +the assignment is not a ZRL instruction (assignment by zero) +or a SDL instruction (store double local). +.LP +If any condition fails, the variable is dismissed from cand +(if it was there already) and put in dismiss +(if it was not there already). +.sp 0 +All variables for which no register message was generated (i.e. those +variables that may be accessed indirectly) are assumed +to be changed in the loop. +.sp 0 +All variables that remain in cand are candidate induction variables. +.PP +From the set of candidates, the induction variables can +be determined, by inspecting the single assignment. +The assignment must match one of the EM patterns below. +('x' is the candidate. 'ws' is the word size of the target machine. +'n' is any number.) +.DS +\fIpattern\fR \fIstep size\fR +INL x | +1 +DEL x | -1 +LOL x ; (INC | DEC) ; STL x | +1 | -1 +LOL x ; LOC n ; (ADI ws | SBI ws) ; STL x | +n | -n +LOC n ; LOL x ; ADI ws ; STL x. +n +.DE +From the patterns the step size of the induction variable +can also be determined. +These step sizes are displayed on the right hand side. +.sp +For every induction variable we maintain the following information: +.IP - +the offset of the variable in the stackframe of its procedure +.IP - +a pointer to the EM text of the assignment statement +.IP - +the step value +.LP +.NH 3 +Optimizing expressions +.PP +If any induction variables of the loop were found, +the EM text of the loop is scanned again, +to detect expressions that can be optimized. +SR scans for multiplication and array instructions. +Whenever it finds such an instruction, it analyses the +code in front of it. +If an expression is to be optimized, it must +be generated by the following syntax rules. +.DS + optimizable_expr: + iv_expr const mult | + const iv_expr mult | + address iv_expr address array_instr; + mult: + MLI ws | + MLU ws ; + array_instr: + LAR ws | + SAR ws | + AAR ws ; + const: + LOC n ; +.DE +An 'address' is an EM instruction that loads an +address on the stack. +An instruction like LOL may be an 'address', if +the size of an address (pointer size, =ps) is +the same as the word size. +If the pointer size is twice the word size, +instructions like LDL are an 'address'. +(The addresses in the third grammar rule +denote resp. the array address and the +array descriptor address). +.DS + address: + LAE | + LAL | + LOL if ps=ws | + LOE ,, | + LIL ,, | + LDL if ps=2*ws | + LDE ,, ; +.DE +The notion of an iv-expression was introduced earlier. +.DS + iv_expr: + iv_expr unair_op | + iv_expr iv_expr binary_op | + loopconst | + iv ; + unair_op: + NGI ws | + INC | + DEC ; + binary_op: + ADI ws | + ADU ws | + SBI ws | + SBU ws ; + loopconst: + const | + LOL x if x is not changed in loop ; + iv: + LOL x if x is an induction variable ; +.DE +An iv_expression must satisfy one additional constraint: +it must use exactly one operand that is an induction +variable. +A simple, hand written, top-down parser is used +to recognize an iv-expression. +It scans the EM code from right to left +(recall that EM is essentially postfix). +It uses semantic attributes (inherited as well as +derived) to check the additional constraint. +.PP +All information assembled during the recognition +process is put in a 'code_info' structure. +This structure contains the following information: +.IP - +the optimizable code itself +.IP - +the loop and basic block the code is part of +.IP - +the induction variable +.IP - +the iv-expression +.IP - +the sign of the induction variable in the +iv-expression +.IP - +the offset and size of the temporary local variable +.IP - +the expensive operator (MLI, LAR etc.) +.IP - +the instruction that loads the constant +(for multiplication) or the array descriptor +(for arrays). +.LP +The entire transformation process is driven +by this information. +As the EM text is represented internally +as a list, this process consists +mainly of straightforward list manipulations. +.sp 0 +The initialization code must be put +immediately before the loop entry. +For this purpose a \fIheader block\fR is +created that has the loop entry block as +its only successor and that dominates the +entry block. +The CFG and all relations (SUCC,PRED, IDOM, LOOPS etc.) +are updated. +.sp 0 +An EM instruction that will +replace the optimizable code +is created and put at the place of the old code. +The list representing the old optimizable code +is used to create a list for the initializing code, +as they are similar. +Only two modifications are required: +.IP - +if the expensive operator is a LAR or SAR, +it must be replaced by an AAR, as the initial value +of TMP is the \fIaddress\fR of the first +array element that is accessed. +.IP - +code must be appended to store the result of the +expression in TMP. +.LP +Finally, code to increment TMP is created and put after +the code of the single assignment to the +induction variable. +The generated code uses either an integer addition +(ADI) or an integer-to-pointer addition (ADS) +to do the increment. +.PP +SR maintains a set of all expressions that have already +been recognized in the present loop. +Such expressions are said to be \fIavailable\fR. +If an expression is recognized that is +already available, +no new temporary local variable is allocated for it, +and the code to initialize and increment the local +is not generated. diff --git a/doc/ego/sr/sr4 b/doc/ego/sr/sr4 new file mode 100644 index 00000000..ae876437 --- /dev/null +++ b/doc/ego/sr/sr4 @@ -0,0 +1,28 @@ +.NH 2 +Source files of SR +.PP +The sources of SR are in the following files +and packages: +.IP sr.h: 14 +declarations of global variables and +data structures +.IP sr.c: +the routine main; a driving routine to process +(possibly nested) loops in the right order +.IP iv +implements a procedure that finds the induction variables +of a loop +.IP reduce +implements a procedure that finds optimizable expressions +and that does the transformations +.IP cand +implements a procedure that finds the candidate induction +variables; used to implement iv +.IP xform +implements several useful routines that transform +lists of EM text or a CFG; used to implement reduce +.IP expr +implements a procedure that parses iv-expressions +.IP aux +implements several auxiliary procedures. +.LP diff --git a/doc/ego/ud/.distr b/doc/ego/ud/.distr new file mode 100644 index 00000000..f64dc114 --- /dev/null +++ b/doc/ego/ud/.distr @@ -0,0 +1,5 @@ +ud1 +ud2 +ud3 +ud4 +ud5 diff --git a/doc/ego/ud/ud1 b/doc/ego/ud/ud1 new file mode 100644 index 00000000..8f2a12f5 --- /dev/null +++ b/doc/ego/ud/ud1 @@ -0,0 +1,58 @@ +.bp +.NH 1 +Use-Definition analysis +.NH 2 +Introduction +.PP +The "Use-Definition analysis" phase (UD) consists of two related optimization +techniques that both depend on "Use-Definition" information. +The techniques are Copy Propagation and Constant Propagation. +They are best explained via an example (see Figs. 11.1 and 11.2). +.DS + (1) A := B A := B + ... --> ... + (2) use(A) use(B) + +Fig. 11.1 An example of Copy Propagation +.DE +.DS + (1) A := 12 A := 12 + ... --> ... + (2) use(A) use(12) + +Fig. 11.2 An example of Constant Propagation +.DE +Both optimizations have to check that the value of A at line (2) +can only be obtained at line (1). +Copy Propagation also has to assure that the value of B is +the same at line (1) as at line (2). +.PP +One purpose of both transformations is to introduce +opportunities for the Dead Code Elimination optimization. +If the variable A is used nowhere else, the assignment A := B +becomes useless and can be eliminated. +.sp 0 +If B is less expensive to access than A (e.g. this is sometimes the case +if A is a local variable and B is a global variable), +Copy Propagation directly improves the code itself. +If A is cheaper to access the transformation will not be performed. +Likewise, a constant as operand may be cheeper than a variable. +Having a constant as operand may also facilitate other optimizations. +.PP +The design of UD is based on the theory described in section +14.1 and 14.3 of. +.[ +aho compiler design +.] +As a main departure from that theory, +we do not demand the statement A := B to become redundant after +Copy Propagation. +If B is cheaper to access than A, the optimization is always performed; +if B is more expensive than A, we never do the transformation. +If A and B are equally expensive UD uses the heuristic rule to +replace infrequently used variables by frequently used ones. +This rule increases the chances of the assignment to become useless. +.PP +In the next section we will give a brief outline of the data +flow theory used +for the implementation of UD. diff --git a/doc/ego/ud/ud2 b/doc/ego/ud/ud2 new file mode 100644 index 00000000..21174f45 --- /dev/null +++ b/doc/ego/ud/ud2 @@ -0,0 +1,64 @@ +.NH 2 +Data flow information +.PP +.NH 3 +Use-Definition information +.PP +A \fIdefinition\fR of a variable A is an assignment to A. +A definition is said to \fIreach\fR a point p if there is a +path in the control flow graph from the definition to p, such that +A is not redefined on that path. +.PP +For every basic block B, we define the following sets: +.IP GEN[b] 9 +the set of definitions in b that reach the end of b. +.IP KILL[b] +the set of definitions outside b that define a variable that +is changed in b. +.IP IN[b] +the set of all definitions reaching the beginning of b. +.IP OUT[b] +the set of all definitions reaching the end of b. +.LP +GEN and KILL can be determined by inspecting the code of the procedure. +IN and OUT are computed by solving the following data flow equations: +.DS +(1) OUT[b] = IN[b] - KILL[b] + GEN[b] +(2) IN[b] = OUT[p1] + ... + OUT[pn], + where PRED(b) = {p1, ... , pn} +.DE +.NH 3 +Copy information +.PP +A \fIcopy\fR is a definition of the form "A := B". +A copy is said to be \fIgenerated\fR in a basic block n if +it occurs in n and there is no subsequent assignment to B in n. +A copy is said to be \fIkilled\fR in n if: +.IP (i) +it occurs in n and there is a subsequent assignment to B within n, or +.IP (ii) +it occurs outside n, the definition A := B reaches the beginning of n +and B is changed in n (note that a copy also is a definition). +.LP +A copy \fIreaches\fR a point p, if there are no assignments to B +on any path in the control flow graph from the copy to p. +.PP +We define the following sets: +.IP C_GEN[b] 11 +the set of all copies in b generated in b. +.IP C_KILL[b] +the set of all copies killed in b. +.IP C_IN[b] +the set of all copies reaching the beginning of b. +.IP C_OUT[b] +the set of all copies reaching the end of b. +.LP +C_IN and C_OUT are computed by solving the following equations: +(root is the entry node of the current procedure; '*' denotes +set intersection) +.DS +(1) C_OUT[b] = C_IN[b] - C_KILL[b] + C_GEN[b] +(2) C_IN[b] = C_OUT[p1] * ... * C_OUT[pn], + where PRED(b) = {p1, ... , pn} and b /= root + C_IN[root] = {all copies} +.DE diff --git a/doc/ego/ud/ud3 b/doc/ego/ud/ud3 new file mode 100644 index 00000000..99bf2a03 --- /dev/null +++ b/doc/ego/ud/ud3 @@ -0,0 +1,26 @@ +.NH 2 +Pointers and subroutine calls +.PP +The theory outlined above assumes that variables can +only be changed by a direct assignment. +This condition does not hold for EM. +In case of an assignment through a pointer variable, +it is in general impossible to see which variable is affected +by the assignment. +Similar problems occur in the presence of procedure calls. +Therefore we distinguish two kinds of definitions: +.IP - +an \fIexplicit\fR definition is a direct assignment to one +specific variable +.IP - +an \fIimplicit\fR definition is the potential alteration of +a variable as a result of a procedure call or an indirect assignment. +.LP +An indirect assignment causes implicit definitions to +all variables that may be accessed indirectly, i.e. +all local variables for which no register message was generated +and all global variables. +If a procedure contains an indirect assignment it may change the +same set of variables, else it may change some global variables directly. +The KILL, GEN, IN and OUT sets contain explicit as well +as implicit definitions. diff --git a/doc/ego/ud/ud4 b/doc/ego/ud/ud4 new file mode 100644 index 00000000..c31ad64b --- /dev/null +++ b/doc/ego/ud/ud4 @@ -0,0 +1,78 @@ +.NH 2 +Implementation +.PP +UD first builds a number of tables: +.IP locals: 9 +contains information about the local variables of the +current procedure (offset,size,whether a register message was found +for it and, if so, the score field of that message) +.IP defs: +a table of all explicit definitions appearing in the +current procedure. +.IP copies: +a table of all copies appearing in the +current procedure. +.LP +Every variable (local as well as global), definition and copy +is identified by a unique number, which is the index +in the table. +All tables are constructed by traversing the EM code. +A fourth table, "vardefs" is used, indexed by a 'variable number', +which contains for every variable the set of explicit definitions of it. +Also, for each basic block b, the set CHGVARS containing all variables +changed by it is computed. +.PP +The GEN sets are obtained in one scan over the EM text, +by analyzing every EM instruction. +The KILL set of a basic block b is computed by looking at the +set of variables +changed by b (i.e. CHGVARS[b]). +For every such variable v, all explicit definitions to v +(i.e. vardefs[v]) that are not in GEN[b] are added to KILL[b]. +Also, the implicit defininition of v is added to KILL[b]. +Next, the data flow equations for use-definition information +are solved, +using a straight forward, iterative algorithm. +All sets are represented as bitvectors, so the operations +on sets (union, difference) can be implemented efficiently. +.PP +The C_GEN and C_KILL sets are computed simultaneously in one scan +over the EM text. +For every copy A := B appearing in basic block b we do +the following: +.IP 1. +for every basic block n /= b that changes B, see if the definition A := B +reaches the beginning of n (i.e. check if the index number of A := B in +the "defs" table is an element of IN[n]); +if so, add the copy to C_KILL[n] +.IP 2. +if B is redefined later on in b, add the copy to C_KILL[b], else +add it to C_GEN[b] +.LP +C_IN and C_OUT are computed from C_GEN and C_KILL via the second set of +data flow equations. +.PP +Finally, in one last scan all opportunities for optimization are +detected. +For every use u of a variable A, we check if +there is a unique explicit definition d reaching u. +.sp +If the definition is a copy A := B and B has the same value at d as +at u, then the use of A at u may be changed into B. +The latter condition can be verified as follows: +.IP - +if u and d are in the same basic block, see if there is +any assignment to B in between d and u +.IP - +if u and d are in different basic blocks, the condition is +satisfied if there is no assignment to B in the block of u prior to u +and d is in C_IN[b]. +.LP +Before the transformation is actually done, UD first makes sure the +alteration is really desirable, as described before. +The information needed for this purpose (access costs of local and +global variables) is read from a machine descriptor file. +.sp +If the only definition reaching u has the form "A := constant", the use +of A at u is replaced by the constant. + diff --git a/doc/ego/ud/ud5 b/doc/ego/ud/ud5 new file mode 100644 index 00000000..1d617e12 --- /dev/null +++ b/doc/ego/ud/ud5 @@ -0,0 +1,19 @@ + +.NH 2 +Source files of UD +.PP +The sources of UD are in the following files and packages: +.IP ud.h: 14 +declarations of global variables and data structures +.IP ud.c: +the routine main; initialization of target machine dependent tables +.IP defs: +routines to compute the GEN and KILL sets and routines to analyse +EM instructions +.IP const: +routines involved in constant propagation +.IP copy: +routines involved in copy propagation +.IP aux: +contains auxiliary routines +.LP diff --git a/doc/em/.distr b/doc/em/.distr new file mode 100644 index 00000000..322dc503 --- /dev/null +++ b/doc/em/.distr @@ -0,0 +1,32 @@ +Makefile +READ_ME +addend.n +app.codes.nr +app.exam.nr +app.int.nr +assem.nr +cont.nr +descr.nr +dspace.nr +em.i +env.nr +even.c +exam.e +exam.p +int +intro.nr +ip.awk +ispace.nr +mach.nr +macr.nr +mapping.nr +mem.nr +print +show +title.nr +traps.nr +types.nr +mkdispatch.c +dispat1.sed +dispat2.sed +dispat3.sed diff --git a/doc/em/Makefile b/doc/em/Makefile index 81cc36dc..37fcc657 100644 --- a/doc/em/Makefile +++ b/doc/em/Makefile @@ -1,31 +1,36 @@ -head: doc.pr +HOME=../.. +TBL=tbl NROFF=nroff -FILES = macr.nr title.nr intro.nr mem.nr ispace.nr dspace.nr mapping.nr types.nr descr.nr iotrap.nr mach.nr assem.nr app.nr -IOP=../../util/ass/ip_spec.t +SUF=pr -doc.pr: $(FILES) itables em.i - tbl $(FILES) | $(NROFF) >doc.pr +head: ../em.$(SUF) -distr: $(FILES) itables em.i - tbl $(FILES) | nroff -Tlp >doc.pr +FILES = macr.nr title.nr intro.nr mem.nr ispace.nr dspace.nr mapping.nr \ + types.nr descr.nr env.nr traps.nr mach.nr assem.nr \ + app.int.nr app.codes.nr app.exam.nr cont.nr -opr: doc.pr - make pr | opr +IOP=$(HOME)/util/ass/ip_spec.t# # to construct itables from -pr: - @make "NROFF="$NROFF doc.pr >makepr.out 2>&1 - @cat doc.pr +../em.$(SUF): $(FILES) itables dispatdummy em.i Makefile + $(TBL) $(FILES) | $(NROFF) > ../em.$(SUF) -app.t: itables em.i +app.codes.pr: app.codes.nr itables dispatdummy -em.i: int/em.p - @echo Sorry, this copy was edited by hand from int/em.p +itables: $(IOP) ip.awk + awk -f ip.awk $(IOP) | sed 's/-/\\-/g' | $(TBL) >itables -itables: $(IOP) - awk -f ip.awk $(IOP) | tbl >itables +dispatdummy: $(IOP) mkdispatch + mkdispatch < $(IOP) > dispatdummy + sed -f dispat1.sed < dispatdummy | $(TBL) > dispat1 + sed -f dispat2.sed < dispatdummy | $(TBL) > dispat2 + sed -f dispat3.sed < dispatdummy | $(TBL) > dispat3 + +mkdispatch: mkdispatch.c + cc -I$(HOME)/util/ass -I$(HOME)/h -o mkdispatch mkdispatch.c $(HOME)/lib/em_data.a .SUFFIXES : .pr .nr -.nr.pr: ; tbl macr.nr $*.nr | $(NROFF) >$@ +.nr.pr: ; $(TBL) macr.nr $*.nr | $(NROFF) >$@ -cont.t intro.t mem.t ispace.t dspace.t mapping.t succ.t descr.t iotrap.t mach.t assem.t kern.t app.t: macr.nr +clean: + rm -f *.pr itables *.out dispatdummy dispat? *.o mkdispatch diff --git a/doc/em/READ_ME b/doc/em/READ_ME index 1511d1dd..d41e5c97 100644 --- a/doc/em/READ_ME +++ b/doc/em/READ_ME @@ -1 +1,9 @@ -Sorry, the kun macro package is not ours to distribute. +This it the text of IR-81, +DESCRIPTION OF A MACHINE ARCHITECTURE FOR USE WITH BLOCK STRUCTURED LANGUAGES + +The file em.i (text of the defining interpreter) was hand-edited from int/em.p + +To print, set NROFF and TBL in the Makefile and call make. +It uses the kun macro package which is also distributed. + +The directory int contains the interpreter. diff --git a/doc/em/app.codes.nr b/doc/em/app.codes.nr new file mode 100644 index 00000000..8aa2bb6f --- /dev/null +++ b/doc/em/app.codes.nr @@ -0,0 +1,153 @@ +.BP +.AP "EM CODE TABLES" +The following table is used by the assembler for EM machine +language. +It specifies the opcodes used for each instruction and +how arguments are mapped to machine language arguments. +The table is presented in three columns, +each line in each column contains three or four fields. +Each line describes a range of interpreter opcodes by +specifying for which instruction the range is used, the type of the +opcodes (mini, shortie, etc..) and range for the instruction +argument. +.A +The first field on each line gives the EM instruction mnemonic, +the second field gives some flags. +If the opcodes are minis or shorties the third field specifies +how many minis/shorties are used. +The last field gives the number of the (first) interpreter +opcode. +.N 1 +Flags : +.IS 3 +.N 1 +Opcode type, only one of the following may be specified. +.PS - 5 " " +.PT \- +opcode without argument +.PT m +mini +.PT s +shortie +.PT 2 +opcode with 2-byte signed argument +.PT 4 +opcode with 4-byte signed argument +.PT 8 +opcode with 8-byte signed argument +.PE +Secondary (escaped) opcodes. +.PS - 5 " " +.PT e +The opcode thus marked is in the secondary opcode group instead +of the primary +.PE +restrictions on arguments +.PS - 5 " " +.PT N +Negative arguments only +.PT P +Positive and zero arguments only +.PE +mapping of arguments +.PS - 5 " " +.PT w +argument must be divisible by the wordsize and is divided by the +wordsize before use as opcode argument. +.PT o +argument ( possibly after division ) must be >= 1 and is +decremented before use as opcode argument +.PE +.IE +If the opcode type is 2,4 or 8 the resulting argument is used as +opcode argument (least significant byte first). +.N +If the opcode type is mini, the argument is added +to the first opcode \- if in range \- . +If the argument is negative, the absolute value minus one is +used in the algorithm above. +.N +For shorties with positive arguments the first opcode is used +for arguments in the range 0..255, the second for the range +256..511, etc.. +For shorties with negative arguments the first opcode is used +for arguments in the range \-1..\-256, the second for the range +\-257..\-512, etc.. +The byte following the opcode contains the least significant +byte of the argument. +First some examples of these specifications. +.PS - 5 +.PT "aar mwPo 1 34" +Indicates that opcode 34 is used as a mini for Positive +instruction arguments only. +The w and o indicate division and decrementing of the +instruction argument. +Because the resulting argument must be zero ( only opcode 34 may be used +), this mini can only be used for instruction argument 2. +Conclusion: opcode 34 is for "AAR 2". +.PT "adp sP 1 41" +Opcode 41 is used as shortie for ADP with arguments in the range +0..255. +.PT "bra sN 2 60" +Opcode 60 is used as shortie for BRA with arguments \-1..\-256, +61 is used for arguments \-257..\-512. +.PT "zer e\- 145" +Escaped opcode 145 is used for ZER. +.PE +The interpreter opcode table: +.N 1 +.IS 3 +.so itables +.IE +.P +The table above results in the following dispatch tables. +Dispatch tables are used by interpreters to jump to the +routines implementing the EM instructions, indexed by the next opcode. +Each line of the dispatch tables gives the routine names +of eight consecutive opcodes, preceded by the first opcode number +on that line. +Routine names consist of an EM mnemonic followed by a suffix. +The suffices show the encoding used for each opcode. +.N +The following suffices exist: +.N 1 +.VS 1 0 +.IS 4 +.PS - 11 +.PT .z +no arguments +.PT .l +16-bit argument +.PT .lw +16-bit argument divided by the wordsize +.PT .p +positive 16-bit argument +.PT .pw +positive 16-bit argument divided by the wordsize +.PT .n +negative 16-bit argument +.PT .nw +negative 16-bit argument divided by the wordsize +.PT .s +shortie with as high order argument byte +.PT .w +shortie with argument divided by the wordsize +.PT . +mini with as argument +.PT .W +mini with *wordsize as argument +.PE 1 + is a possibly negative integer. +.VS 1 1 +.IE +The dispatch table for the 256 primary opcodes: +.N 1 +.so dispat1 +.N 2 +The list of secondary opcodes (escape1): +.N 1 +.so dispat2 +.N 2 +Finally, the list of opcodes with four byte arguments (escape2). +.N 1 +.so dispat3 diff --git a/doc/em/app.exam.nr b/doc/em/app.exam.nr new file mode 100644 index 00000000..2121e08a --- /dev/null +++ b/doc/em/app.exam.nr @@ -0,0 +1,277 @@ +.BP +.AP "AN EXAMPLE PROGRAM" +.A 1 0 +.NA +.ta 4n 8n 12n 16n 20n +.nf + 1 program example(output); + 2 {This program just demonstrates typical EM code.} + 3 type rec = record r1: integer; r2:real; r3: boolean end; + 4 var mi: integer; mx:real; r:rec; + 5 + 6 function sum(a,b:integer):integer; + 7 begin + 8 sum := a + b + 9 end; +10 +11 procedure test(var r: rec); +12 label 1; +13 var i,j: integer; +14 x,y: real; +15 b: boolean; +16 c: char; +17 a: array[1..100] of integer; +18 +19 begin +20 j := 1; +21 i := 3 * j + 6; +22 x := 4.8; +23 y := x/0.5; +24 b := true; +25 c := 'z'; +26 for i:= 1 to 100 do a[i] := i * i; +27 r.r1 := j+27; +28 r.r3 := b; +29 r.r2 := x+y; +30 i := sum(r.r1, a[j]); +31 while i > 0 do begin j := j + r.r1; i := i - 1 end; +32 with r do begin r3 := b; r2 := x+y; r1 := 0 end; +33 goto 1; +34 1: writeln(j, i:6, x:9:3, b) +35 end; {test} +36 begin {main program} +37 mx := 15.96; +38 mi := 99; +39 test(r) +40 end. +.fi +.AD +.BP +The EM code as produced by the Pascal-VU compiler is given below. Comments +have been added manually. Note that this code has already been optimized. +.A 1 0 +.NA +.nf +.ta 1n 24n + mes 2,2,2 ; wordsize 2, pointersize 2 +\&.1 + rom 't.p\e000' ; the name of the source file + hol 552,\-32768,0 ; externals and buf occupy 552 bytes + exp $sum ; sum can be called from other modules + pro $sum,2 ; procedure sum ; 2 bytes local storage + lin 8 ; code from source line 8 + ldl 0 ; load two locals ( a and b ) + adi 2 ; add them + ret 2 ; return the result + end 2 ; end of procedure ( still two bytes local storage ) +\&.2 + rom 1,99,2 ; descriptor of array a[] + exp $test ; the compiler exports all level 0 procedures + pro $test,226 ; procedure test, 226 bytes local storage +\&.3 + rom 4.8F8 ; assemble Floating point 4.8 (8 bytes) in +\&.4 ; global storage + rom 0.5F8 ; same for 0.5 + mes 3,\-226,2,2 ; compiler temporary not referenced by address + mes 3,\-24,2,0 ; the same is true for i, j, b and c in test + mes 3,\-22,2,0 + mes 3,\-4,2,0 + mes 3,\-2,2,0 + mes 3,\-20,8,0 ; and for x and y + mes 3,\-12,8,0 + lin 20 ; maintain source line number + loc 1 + stl \-4 ; j := 1 + lni ; lin 21 prior to optimization + lol \-4 + loc 3 + mli 2 + loc 6 + adi 2 + stl \-2 ; i := 3 * j + 6 + lni ; lin 22 prior to optimization + lae .3 + loi 8 + lal \-12 + sti 8 ; x := 4.8 + lni ; lin 23 prior to optimization + lal \-12 + loi 8 + lae .4 + loi 8 + dvf 8 + lal \-20 + sti 8 ; y := x / 0.5 + lni ; lin 24 prior to optimization + loc 1 + stl \-22 ; b := true + lni ; lin 25 prior to optimization + loc 122 + stl \-24 ; c := 'z' + lni ; lin 26 prior to optimization + loc 1 + stl \-2 ; for i:= 1 +2 + lol \-2 + dup 2 + mli 2 ; i*i + lal \-224 + lol \-2 + lae .2 + sar 2 ; a[i] := + lol \-2 + loc 100 + beq *3 ; to 100 do + inl \-2 ; increment i and loop + bra *2 +3 + lin 27 + lol \-4 + loc 27 + adi 2 ; j + 27 + sil 0 ; r.r1 := + lni ; lin 28 prior to optimization + lol \-22 ; b + lol 0 + stf 10 ; r.r3 := + lni ; lin 29 prior to optimization + lal \-20 + loi 16 + adf 8 ; x + y + lol 0 + adp 2 + sti 8 ; r.r2 := + lni ; lin 23 prior to optimization + lal \-224 + lol \-4 + lae .2 + lar 2 ; a[j] + lil 0 ; r.r1 + cal $sum ; call now + asp 4 ; remove parameters from stack + lfr 2 ; get function result + stl \-2 ; i := +4 + lin 31 + lol \-2 + zle *5 ; while i > 0 do + lol \-4 + lil 0 + adi 2 + stl \-4 ; j := j + r.r1 + del \-2 ; i := i - 1 + bra *4 ; loop +5 + lin 32 + lol 0 + stl \-226 ; make copy of address of r + lol \-22 + lol \-226 + stf 10 ; r3 := b + lal \-20 + loi 16 + adf 8 + lol \-226 + adp 2 + sti 8 ; r2 := x + y + loc 0 + sil \-226 ; r1 := 0 + lin 34 ; note the abscence of the unnecesary jump + lae 22 ; address of output structure + lol \-4 + cal $_wri ; write integer with default width + asp 4 ; pop parameters + lae 22 + lol \-2 + loc 6 + cal $_wsi ; write integer width 6 + asp 6 + lae 22 + lal \-12 + loi 8 + loc 9 + loc 3 + cal $_wrf ; write fixed format real, width 9, precision 3 + asp 14 + lae 22 + lol \-22 + cal $_wrb ; write boolean, default width + asp 4 + lae 22 + cal $_wln ; writeln + asp 2 + ret 0 ; return, no result + end 226 + exp $_main + pro $_main,0 ; main program +\&.6 + con 2,\-1,22 ; description of external files +\&.5 + rom 15.96F8 + fil .1 ; maintain source file name + lae .6 ; description of external files + lae 0 ; base of hol area to relocate buffer addresses + cal $_ini ; initialize files, etc... + asp 4 + lin 37 + lae .5 + loi 8 + lae 2 + sti 8 ; mx := 15.96 + lni ; lin 38 prior to optimization + loc 99 + ste 0 ; mi := 99 + lni ; lin 39 prior to optimization + lae 10 ; address of r + cal $test + asp 2 + loc 0 ; normal exit + cal $_hlt ; cleanup and finish + asp 2 + end 0 + mes 5 ; reals were used +.fi +.AD +.A 1 0 +The compact code corresponding to the above program is listed below. +Read it horizontally, line by line, not column by column. +Each number represents a byte of compact code, printed in decimal. +The first two bytes form the magic word. +.N 1 +.IS 3 +.Dr 33 + 173 0 159 122 122 122 255 242 1 161 250 124 116 46 112 0 + 255 156 245 40 2 245 0 128 120 155 249 123 115 117 109 160 + 249 123 115 117 109 122 67 128 63 120 3 122 88 122 152 122 + 242 2 161 121 219 122 255 155 249 124 116 101 115 116 160 249 + 124 116 101 115 116 245 226 0 242 3 161 253 128 123 52 46 + 56 255 242 4 161 253 128 123 48 46 53 255 159 123 245 30 + 255 122 122 255 159 123 96 122 120 255 159 123 98 122 120 255 + 159 123 116 122 120 255 159 123 118 122 120 255 159 123 100 128 + 120 255 159 123 108 128 120 255 67 140 69 121 113 116 68 73 + 116 69 123 81 122 69 126 3 122 113 118 68 57 242 3 72 + 128 58 108 112 128 68 58 108 72 128 57 242 4 72 128 44 + 128 58 100 112 128 68 69 121 113 98 68 69 245 122 0 113 + 96 68 69 121 113 118 182 73 118 42 122 81 122 58 245 32 + 255 73 118 57 242 2 94 122 73 118 69 220 10 123 54 118 + 18 122 183 67 147 73 116 69 147 3 122 104 120 68 73 98 + 73 120 111 130 68 58 100 72 136 2 128 73 120 4 122 112 + 128 68 58 245 32 255 73 116 57 242 2 59 122 65 120 20 + 249 123 115 117 109 8 124 64 122 113 118 184 67 151 73 118 + 128 125 73 116 65 120 3 122 113 116 41 118 18 124 185 67 + 152 73 120 113 245 30 255 73 98 73 245 30 255 111 130 58 + 100 72 136 2 128 73 245 30 255 4 122 112 128 69 120 104 + 245 30 255 67 154 57 142 73 116 20 249 124 95 119 114 105 + 8 124 57 142 73 118 69 126 20 249 124 95 119 115 105 8 + 126 57 142 58 108 72 128 69 129 69 123 20 249 124 95 119 + 114 102 8 134 57 142 73 98 20 249 124 95 119 114 98 8 + 124 57 142 20 249 124 95 119 108 110 8 122 88 120 152 245 + 226 0 155 249 125 95 109 97 105 110 160 249 125 95 109 97 + 105 110 120 242 6 151 122 119 142 255 242 5 161 253 128 125 + 49 53 46 57 54 255 50 242 1 57 242 6 57 120 20 249 + 124 95 105 110 105 8 124 67 157 57 242 5 72 128 57 122 + 112 128 68 69 219 110 120 68 57 130 20 249 124 116 101 115 + 116 8 122 69 120 20 249 124 95 104 108 116 8 122 152 120 + 159 124 160 255 159 125 255 +.De +.IE diff --git a/doc/em/app.int.nr b/doc/em/app.int.nr new file mode 100644 index 00000000..d77adde1 --- /dev/null +++ b/doc/em/app.int.nr @@ -0,0 +1,8 @@ +.BP +.AP "EM INTERPRETER" +.nf +.ft CW +.ta 8 16 24 32 40 48 56 64 72 80 +.so em.i +.ft P +.fi diff --git a/doc/em/assem.nr b/doc/em/assem.nr index c1987f6e..38b46fa6 100644 --- a/doc/em/assem.nr +++ b/doc/em/assem.nr @@ -34,7 +34,7 @@ The scope of an instruction label is its procedure. .A The pseudoinstructions CON, ROM and BSS may be preceded by a line containing a -1-8 character data label, the first character of which is a +1\-8 character data label, the first character of which is a letter, period or underscore. The period may only be followed by digits, the others may be followed by letters, digits and underscores. @@ -66,7 +66,7 @@ They do not belong to a specific procedure. All constants in EM are interpreted in the decimal base. The ASCII assembly language accepts constant expressions wherever constants are allowed. -The operators recognized are: +, -, *, % and / with the usual +The operators recognized are: +, \-, *, % and / with the usual precedence order. Use of the parentheses ( and ) to alter the precedence order is allowed. .S3 "Instruction arguments" @@ -109,16 +109,16 @@ integers on top of the stack are to be compared. on top of the stack that specifies the size of the integers to be compared. Thus the following two sequences are equivalent: -.N 2 +.N 1 .TS center, tab(:) ; l r 30 l r. -LDL:-10:LDL:-10 -LDL:-14:LDL:-14 +LDL:\-10:LDL:\-10 +LDL:\-14:LDL:\-14 ::LOC:4 CMI:4:CMI: ZEQ:*1:ZEQ:*1 -.TE 2 +.TE 1 Section 11.1.6 shows the arguments allowed for each instruction. .S3 "Pseudoinstruction arguments" Pseudoinstruction arguments can be divided in two classes: @@ -139,7 +139,7 @@ initializer's size. This integer is governed by the same restrictions as for transfer of objects to/from memory. As in instruction arguments, initializers include expressions of the form: -\&"LABEL+offset" and "LABEL-offset". +\&"LABEL+offset" and "LABEL\-offset". The offset must be an unsigned decimal constant. The 'IUF' indicators cannot be used in the offsets. .P @@ -167,7 +167,7 @@ double quote:":\e" bit pattern:\fBddd\fP:\e\fBddd\fP .TE .DE -The escape \fBddd\fP consists of the backslash followed by 1, +The escape \fB\eddd\fP consists of the backslash followed by 1, 2, or 3 octal digits specifing the value of the desired character. If the character following a backslash is not one of those @@ -190,9 +190,9 @@ instructions and pseudoinstructions. .TS tab(:); l l l. -:\&=:integer constant (current range -2**31..2**31-1) +:\&=:integer constant (current range \-2**31..2**31\-1) :\&=:data label -:\&=: or or + or - +:\&=: or or + or \- :\&=:integer constant, unsigned constant, floating-point constant :\&=:string constant (surrounded by double quotes), :\&=:instruction label @@ -425,13 +425,13 @@ etc. represent the succeeding bytes. tab(:) ; rw17 4 l. 0:Reserved for future use -1-129:Machine instructions, see Appendix A, alphabetical list -130-149:Reserved for future use -150-161:BSS,CON,END,EXA,EXC,EXP,HOL,INA,INP,MES,PRO,ROM -162-179:Reserved for future pseudoinstructions -180-239:Instruction labels 0 - 59 (180 is local label 0 etc.) -240-244:See the Common Table below -245-255:Not used +1\-129:Machine instructions, see Appendix A, alphabetical list +130\-149:Reserved for future use +150\-161:BSS,CON,END,EXA,EXC,EXP,HOL,INA,INP,MES,PRO,ROM +162\-179:Reserved for future pseudoinstructions +180\-239:Instruction labels 0 \- 59 (180 is local label 0 etc.) +240\-244:See the Common Table below +245\-255:Not used .TE 1 .DE 0 After a label, the assembler is back in neutral state; it can immediately @@ -449,9 +449,9 @@ encoded as follows: .TS tab(:); r l. -0-239:Offsets from -120 to 119 +0\-239:Offsets from \-120 to 119 -240-255:See the Common Table below +240\-255:See the Common Table below .TE 1 Absence of an optional argument is indicated by a special byte. @@ -467,8 +467,8 @@ class:bytes:description :240:b1:Instruction label b1 (Not used for branches) :241:b1 b2:16 bit instruction label (256*b2 + b1) -:242:b1:Global label .0-.255, with b1 being the label -:243:b1 b2:Global label .0-.32767 +:242:b1:Global label .0\-.255, with b1 being the label +:243:b1 b2:Global label .0\-.32767 :::with 256*b2+b1 being the label :244::Global symbol not of the form .nnn :245:b1 b2:16 bit constant @@ -488,7 +488,7 @@ class:bytes:description The bytes specifying the value of a 16, 32 or 64 bit constant are presented in two's complement notation, with the least significant byte first. For example: the value of a 32 bit -constant is ((s4*256+b3)*256+b2)*256+b1, where s4 is b4-256 if +constant is ((s4*256+b3)*256+b2)*256+b1, where s4 is b4\-256 if b4 is greater than 128 else s4 takes the value of b4. A consists of a inmediatly followed by a sequence of bytes with length . @@ -498,10 +498,10 @@ The pseudoinstructions fall into several categories, depending on their arguments: .N 1 .DS - Group 1 -- EXC, BSS, HOL have a known number of arguments - Group 2 -- EXA, EXP, INA, INP have a string as argument - Group 3 -- CON, MES, ROM have a variable number of various things - Group 4 -- END, PRO have a trailing optional argument. + Group 1 \- EXC, BSS, HOL have a known number of arguments + Group 2 \- EXA, EXP, INA, INP have a string as argument + Group 3 \- CON, MES, ROM have a variable number of various things + Group 4 \- END, PRO have a trailing optional argument. .DE 1 Groups 1 and 2 use the encoding described above. @@ -522,7 +522,7 @@ Example ASCII|Example compact 2||182 1||181 LOC|10|69 130 - LOC|-10|69 110 + LOC|\-10|69 110 LOC|300|69 245 44 1 BRA|*19|18 139 300||241 44 1 @@ -531,7 +531,6 @@ Example ASCII|Example compact CON|.35|151 242 35 255 .TE 0 .IE 0 -.BP .S2 "Assembly language instruction list" .P For each instruction in the list the range of argument values @@ -556,7 +555,7 @@ are indicated by letters: .ds s \fBs\fP .ds z \fBz\fP .ds o \fBo\fP -.ds - \fB-\fP +.ds - \fB\-\fP .N 1 .TS tab(:); @@ -589,185 +588,214 @@ Instructions that check for undefined integer or floating-point values and underflow or overflow are indicated below by (*). .N 1 -.DS B -GROUP 1 - LOAD +.DS +.ta 12n +GROUP 1 \- LOAD - LOC \*c : Load constant (i.e. push one word onto the stack) - LDC \*d : Load double constant ( push two words ) - LOL \*l : Load word at \*l-th local (\*l<0) or parameter (\*l>=0) - LOE \*g : Load external word \*g - LIL \*l : Load word pointed to by \*l-th local or parameter - LOF \*f : Load offsetted (top of stack + \*f yield address) - LAL \*l : Load address of local or parameter - LAE \*g : Load address of external - LXL \*n : Load lexical (address of LB \*n static levels back) - LXA \*n : Load lexical (address of AB \*n static levels back) - LOI \*o : Load indirect \*o bytes (address is popped from the stack) - LOS \*w : Load indirect, \*w-byte integer on top of stack gives object size - LDL \*l : Load double local or parameter (two consecutive words are stacked) - LDE \*g : Load double external (two consecutive externals are stacked) - LDF \*f : Load double offsetted (top of stack + \*f yield address) - LPI \*p : Load procedure identifier + LOC \*c : Load constant (i.e. push one word onto the stack) + LDC \*d : Load double constant ( push two words ) + LOL \*l : Load word at \*l-th local (\*l<0) or parameter (\*l>=0) + LOE \*g : Load external word \*g + LIL \*l : Load word pointed to by \*l-th local or parameter + LOF \*f : Load offsetted (top of stack + \*f yield address) + LAL \*l : Load address of local or parameter + LAE \*g : Load address of external + LXL \*n : Load lexical (address of LB \*n static levels back) + LXA \*n : Load lexical (address of AB \*n static levels back) + LOI \*o : Load indirect \*o bytes (address is popped from the stack) + LOS \*w : Load indirect, \*w-byte integer on top of stack gives object size + LDL \*l : Load double local or parameter (two consecutive words are stacked) + LDE \*g : Load double external (two consecutive externals are stacked) + LDF \*f : Load double offsetted (top of stack + \*f yield address) + LPI \*p : Load procedure identifier +.DE -GROUP 2 - STORE +.DS +GROUP 2 \- STORE - STL \*l : Store local or parameter - STE \*g : Store external - SIL \*l : Store into word pointed to by \*l-th local or parameter - STF \*f : Store offsetted - STI \*o : Store indirect \*o bytes (pop address, then data) - STS \*w : Store indirect, \*w-byte integer on top of stack gives object size - SDL \*l : Store double local or parameter - SDE \*g : Store double external - SDF \*f : Store double offsetted + STL \*l : Store local or parameter + STE \*g : Store external + SIL \*l : Store into word pointed to by \*l-th local or parameter + STF \*f : Store offsetted + STI \*o : Store indirect \*o bytes (pop address, then data) + STS \*w : Store indirect, \*w-byte integer on top of stack gives object size + SDL \*l : Store double local or parameter + SDE \*g : Store double external + SDF \*f : Store double offsetted +.DE -GROUP 3 - INTEGER ARITHMETIC +.DS +GROUP 3 \- INTEGER ARITHMETIC - ADI \*w : Addition (*) - SBI \*w : Subtraction (*) - MLI \*w : Multiplication (*) - DVI \*w : Division (*) - RMI \*w : Remainder (*) - NGI \*w : Negate (two's complement) (*) - SLI \*w : Shift left (*) - SRI \*w : Shift right (*) + ADI \*w : Addition (*) + SBI \*w : Subtraction (*) + MLI \*w : Multiplication (*) + DVI \*w : Division (*) + RMI \*w : Remainder (*) + NGI \*w : Negate (two's complement) (*) + SLI \*w : Shift left (*) + SRI \*w : Shift right (*) +.DE -GROUP 4 - UNSIGNED ARITHMETIC +.DS +GROUP 4 \- UNSIGNED ARITHMETIC - ADU \*w : Addition - SBU \*w : Subtraction - MLU \*w : Multiplication - DVU \*w : Division - RMU \*w : Remainder - SLU \*w : Shift left - SRU \*w : Shift right + ADU \*w : Addition + SBU \*w : Subtraction + MLU \*w : Multiplication + DVU \*w : Division + RMU \*w : Remainder + SLU \*w : Shift left + SRU \*w : Shift right +.DE -GROUP 5 - FLOATING POINT ARITHMETIC +.DS +GROUP 5 \- FLOATING POINT ARITHMETIC - ADF \*w : Floating add (*) - SBF \*w : Floating subtract (*) - MLF \*w : Floating multiply (*) - DVF \*w : Floating divide (*) - NGF \*w : Floating negate (*) - FIF \*w : Floating multiply and split integer and fraction part (*) - FEF \*w : Split floating number in exponent and fraction part (*) + ADF \*w : Floating add (*) + SBF \*w : Floating subtract (*) + MLF \*w : Floating multiply (*) + DVF \*w : Floating divide (*) + NGF \*w : Floating negate (*) + FIF \*w : Floating multiply and split integer and fraction part (*) + FEF \*w : Split floating number in exponent and fraction part (*) +.DE -GROUP 6 - POINTER ARITHMETIC +.DS +GROUP 6 \- POINTER ARITHMETIC - ADP \*f : Add \*f to pointer on top of stack - ADS \*w : Add \*w-byte value and pointer - SBS \*w : Subtract pointers in same fragment and push diff as size \*w integer + ADP \*f : Add \*f to pointer on top of stack + ADS \*w : Add \*w-byte value and pointer + SBS \*w : Subtract pointers in same fragment and push diff as size \*w integer +.DE -GROUP 7 - INCREMENT/DECREMENT/ZERO +.DS +GROUP 7 \- INCREMENT/DECREMENT/ZERO - INC \*- : Increment word on top of stack by 1 (*) - INL \*l : Increment local or parameter (*) - INE \*g : Increment external (*) - DEC \*- : Decrement word on top of stack by 1 (*) - DEL \*l : Decrement local or parameter (*) - DEE \*g : Decrement external (*) - ZRL \*l : Zero local or parameter - ZRE \*g : Zero external - ZRF \*w : Load a floating zero of size \*w - ZER \*w : Load \*w zero bytes + INC \*- : Increment word on top of stack by 1 (*) + INL \*l : Increment local or parameter (*) + INE \*g : Increment external (*) + DEC \*- : Decrement word on top of stack by 1 (*) + DEL \*l : Decrement local or parameter (*) + DEE \*g : Decrement external (*) + ZRL \*l : Zero local or parameter + ZRE \*g : Zero external + ZRF \*w : Load a floating zero of size \*w + ZER \*w : Load \*w zero bytes +.DE -GROUP 8 - CONVERT (stack: source, source size, dest. size (top)) +.DS \" ??? +GROUP 8 \- CONVERT (stack: source, source size, dest. size (top)) - CII \*- : Convert integer to integer (*) - CUI \*- : Convert unsigned to integer (*) - CFI \*- : Convert floating to integer (*) - CIF \*- : Convert integer to floating (*) - CUF \*- : Convert unsigned to floating (*) - CFF \*- : Convert floating to floating (*) - CIU \*- : Convert integer to unsigned - CUU \*- : Convert unsigned to unsigned - CFU \*- : Convert floating to unsigned + CII \*- : Convert integer to integer (*) + CUI \*- : Convert unsigned to integer (*) + CFI \*- : Convert floating to integer (*) + CIF \*- : Convert integer to floating (*) + CUF \*- : Convert unsigned to floating (*) + CFF \*- : Convert floating to floating (*) + CIU \*- : Convert integer to unsigned + CUU \*- : Convert unsigned to unsigned + CFU \*- : Convert floating to unsigned +.DE -GROUP 9 - LOGICAL +.DS +GROUP 9 \- LOGICAL - AND \*w : Boolean and on two groups of \*w bytes - IOR \*w : Boolean inclusive or on two groups of \*w bytes - XOR \*w : Boolean exclusive or on two groups of \*w bytes - COM \*w : Complement (one's complement of top \*w bytes) - ROL \*w : Rotate left a group of \*w bytes - ROR \*w : Rotate right a group of \*w bytes + AND \*w : Boolean and on two groups of \*w bytes + IOR \*w : Boolean inclusive or on two groups of \*w bytes + XOR \*w : Boolean exclusive or on two groups of \*w bytes + COM \*w : Complement (one's complement of top \*w bytes) + ROL \*w : Rotate left a group of \*w bytes + ROR \*w : Rotate right a group of \*w bytes +.DE -GROUP 10 - SETS +.DS +GROUP 10 \- SETS - INN \*w : Bit test on \*w byte set (bit number on top of stack) - SET \*w : Create singleton \*w byte set with bit n on (n is top of stack) + INN \*w : Bit test on \*w byte set (bit number on top of stack) + SET \*w : Create singleton \*w byte set with bit n on (n is top of stack) +.DE -GROUP 11 - ARRAY +.DS +GROUP 11 \- ARRAY - LAR \*w : Load array element, descriptor contains integers of size \*w - SAR \*w : Store array element - AAR \*w : Load address of array element + LAR \*w : Load array element, descriptor contains integers of size \*w + SAR \*w : Store array element + AAR \*w : Load address of array element +.DE -GROUP 12 - COMPARE +.DS +GROUP 12 \- COMPARE - CMI \*w : Compare \*w byte integers, Push negative, zero, positive for <, = or > - CMF \*w : Compare \*w byte reals - CMU \*w : Compare \*w byte unsigneds - CMS \*w : Compare \*w byte values, can only be used for bit for bit equality test - CMP \*- : Compare pointers + CMI \*w : Compare \*w byte integers, Push negative, zero, positive for <, = or > + CMF \*w : Compare \*w byte reals + CMU \*w : Compare \*w byte unsigneds + CMS \*w : Compare \*w byte values, can only be used for bit for bit equality test + CMP \*- : Compare pointers - TLT \*- : True if less, i.e. iff top of stack < 0 - TLE \*- : True if less or equal, i.e. iff top of stack <= 0 - TEQ \*- : True if equal, i.e. iff top of stack = 0 - TNE \*- : True if not equal, i.e. iff top of stack non zero - TGE \*- : True if greater or equal, i.e. iff top of stack >= 0 - TGT \*- : True if greater, i.e. iff top of stack > 0 + TLT \*- : True if less, i.e. iff top of stack < 0 + TLE \*- : True if less or equal, i.e. iff top of stack <= 0 + TEQ \*- : True if equal, i.e. iff top of stack = 0 + TNE \*- : True if not equal, i.e. iff top of stack non zero + TGE \*- : True if greater or equal, i.e. iff top of stack >= 0 + TGT \*- : True if greater, i.e. iff top of stack > 0 +.DE -GROUP 13 - BRANCH +.DS \" ??? +GROUP 13 \- BRANCH - BRA \*b : Branch unconditionally to label \*b + BRA \*b : Branch unconditionally to label \*b - BLT \*b : Branch less (pop 2 words, branch if top > second) - BLE \*b : Branch less or equal - BEQ \*b : Branch equal - BNE \*b : Branch not equal - BGE \*b : Branch greater or equal - BGT \*b : Branch greater + BLT \*b : Branch less (pop 2 words, branch if top > second) + BLE \*b : Branch less or equal + BEQ \*b : Branch equal + BNE \*b : Branch not equal + BGE \*b : Branch greater or equal + BGT \*b : Branch greater - ZLT \*b : Branch less than zero (pop 1 word, branch negative) - ZLE \*b : Branch less or equal to zero - ZEQ \*b : Branch equal zero - ZNE \*b : Branch not zero - ZGE \*b : Branch greater or equal zero - ZGT \*b : Branch greater than zero + ZLT \*b : Branch less than zero (pop 1 word, branch negative) + ZLE \*b : Branch less or equal to zero + ZEQ \*b : Branch equal zero + ZNE \*b : Branch not zero + ZGE \*b : Branch greater or equal zero + ZGT \*b : Branch greater than zero +.DE -GROUP 14 - PROCEDURE CALL +.DS +GROUP 14 \- PROCEDURE CALL - CAI \*- : Call procedure (procedure identifier on stack) - CAL \*p : Call procedure (with identifier \*p) - LFR \*s : Load function result - RET \*z : Return (function result consists of top \*z bytes) + CAI \*- : Call procedure (procedure identifier on stack) + CAL \*p : Call procedure (with identifier \*p) + LFR \*s : Load function result + RET \*z : Return (function result consists of top \*z bytes) +.DE -GROUP 15 - MISCELLANEOUS +.DS +GROUP 15 \- MISCELLANEOUS - ASP \*f : Adjust the stack pointer by \*f - ASS \*w : Adjust the stack pointer by \*w-byte integer - BLM \*z : Block move \*z bytes; first pop destination addr, then source addr - BLS \*w : Block move, size is in \*w-byte integer on top of stack - CSA \*w : Case jump; address of jump table at top of stack - CSB \*w : Table lookup jump; address of jump table at top of stack - DCH \*- : Follow dynamic chain, convert LB to LB of caller - DUP \*s : Duplicate top \*s bytes - DUS \*w : Duplicate top \*w bytes - EXG \*w : Exchange top \*w bytes - FIL \*g : File name (external 4 := \*g) - GTO \*g : Non-local goto, descriptor at \*g - LIM \*- : Load 16 bit ignore mask - LIN \*n : Line number (external 0 := \*n) - LNI \*- : Line number increment - LOR \*r : Load register (0=LB, 1=SP, 2=HP) - LPB \*- : Convert local base to argument base - MON \*- : Monitor call - NOP \*- : No operation - RCK \*w : Range check; trap on error - RTT \*- : Return from trap - SIG \*- : Trap errors to proc identifier on top of stack, -2 resets default - SIM \*- : Store 16 bit ignore mask - STR \*r : Store register (0=LB, 1=SP, 2=HP) - TRP \*- : Cause trap to occur (Error number on stack) + ASP \*f : Adjust the stack pointer by \*f + ASS \*w : Adjust the stack pointer by \*w-byte integer + BLM \*z : Block move \*z bytes; first pop destination addr, then source addr + BLS \*w : Block move, size is in \*w-byte integer on top of stack + CSA \*w : Case jump; address of jump table at top of stack + CSB \*w : Table lookup jump; address of jump table at top of stack + DCH \*- : Follow dynamic chain, convert LB to LB of caller + DUP \*s : Duplicate top \*s bytes + DUS \*w : Duplicate top \*w bytes + EXG \*w : Exchange top \*w bytes + FIL \*g : File name (external 4 := \*g) + GTO \*g : Non-local goto, descriptor at \*g + LIM \*- : Load 16 bit ignore mask + LIN \*n : Line number (external 0 := \*n) + LNI \*- : Line number increment + LOR \*r : Load register (0=LB, 1=SP, 2=HP) + LPB \*- : Convert local base to argument base + MON \*- : Monitor call + NOP \*- : No operation + RCK \*w : Range check; trap on error + RTT \*- : Return from trap + SIG \*- : Trap errors to proc identifier on top of stack, \-2 resets default + SIM \*- : Store 16 bit ignore mask + STR \*r : Store register (0=LB, 1=SP, 2=HP) + TRP \*- : Cause trap to occur (Error number on stack) .DE 0 diff --git a/doc/em/cont.nr b/doc/em/cont.nr new file mode 100644 index 00000000..b020dca0 --- /dev/null +++ b/doc/em/cont.nr @@ -0,0 +1,6 @@ +.MS T A 0 +.ME +.BP +.MS B A 0 +.ME +.CT diff --git a/doc/em/descr.nr b/doc/em/descr.nr index a377e55d..ed79ba85 100644 --- a/doc/em/descr.nr +++ b/doc/em/descr.nr @@ -36,7 +36,7 @@ Array descriptors contain the following three integers: .PT lower bound~~~~~~~~~~~~~~~~~~~~~signed .PT -upper bound - lower bound~~~~~~~unsigned +upper bound \- lower bound~~~~~~~unsigned .PT number of bytes per element~~~~~unsigned .PE @@ -60,7 +60,7 @@ LAR n (n is the size of the integers in the descriptor and I) All array instructions first pop the address of the descriptor and the index. If the index is not within the bounds specified, a trap occurs. -If ok, (I~-~lower bound) is multiplied +If ok, (I~\-~lower bound) is multiplied by the number of bytes per element (the third word). The result is added to the address of A and replaces A on the stack. .A @@ -128,12 +128,12 @@ each source language case statement is up to the front end. If the range of the index value is dense, i.e .DS -(highest value - lowest value) / number of cases +(highest value \- lowest value) / number of cases .DE 1 is less than some threshold, then CSA is the obvious choice. If the range is sparse, CSB is better. .N 2 -.DS +.Dr 30 |--------------------| |--------------------| high address | pointer for upb | | pointer n-1 | |--------------------| |- - - - - - - | @@ -157,7 +157,6 @@ If the range is sparse, CSB is better. |--------------------| |--------------------| CSA descriptor CSB descriptor - - - Figure 4. Descriptor layout for CSA and CSB -.DE +.Df +Figure 4. Descriptor layout for CSA and CSB +.De diff --git a/doc/em/dispat1.sed b/doc/em/dispat1.sed new file mode 100644 index 00000000..c459211c --- /dev/null +++ b/doc/em/dispat1.sed @@ -0,0 +1,6 @@ +1c\ +.TS\ +r l l l l l l l l. +s/-/\\-/g +/DISPATCH2/,$c\ +.TE diff --git a/doc/em/dispat2.sed b/doc/em/dispat2.sed new file mode 100644 index 00000000..8955df58 --- /dev/null +++ b/doc/em/dispat2.sed @@ -0,0 +1,6 @@ +1,/DISPATCH2/c\ +.TS\ +r l l l l l l l l. +s/-/\\-/g +/DISPATCH3/,$c\ +.TE diff --git a/doc/em/dispat3.sed b/doc/em/dispat3.sed new file mode 100644 index 00000000..881881ef --- /dev/null +++ b/doc/em/dispat3.sed @@ -0,0 +1,6 @@ +1,/DISPATCH3/c\ +.TS\ +r l l l l l l l l. +s/-/\\-/g +$a\ +.TE diff --git a/doc/em/dspace.nr b/doc/em/dspace.nr index 7d58dea1..4fa5447d 100644 --- a/doc/em/dspace.nr +++ b/doc/em/dspace.nr @@ -23,7 +23,7 @@ Examples are LOE, LAE and STE. Part of the global data area is initialized by the compiler, the rest is not initialized at all or is initialized -with a value, typically -32768 or 0. +with a value, typically \-32768 or 0. Part of the initialized global data may be made read-only if the implementation supports protection. .P @@ -47,7 +47,7 @@ addressed with the use of the DCH instruction. .A Many instructions have offsets to LB as argument, for instance LOL, LAL and STL. -The arguments of these instructions range from -1 to some +The arguments of these instructions range from \-1 to some (negative) minimum for the access of local storage and from 0 to some (positive) maximum for parameter access. @@ -246,7 +246,7 @@ reserved. The parameters and local storage are accessed by the same instructions. Negative offsets are used for access to local variables. The highest byte, that is the byte nearest -to LB, has to be accessed with offset -1. +to LB, has to be accessed with offset \-1. The pseudoinstruction specifying the entry point of a procedure, has an argument that specifies the amount of local storage needed. @@ -255,7 +255,7 @@ are the only ones that can be accessed with a fixed negative offset. The initial value of the allocated words is not defined, but implementations that check for undefined values will probably initialize them with a -special 'undefined' pattern, typically -32768. +special 'undefined' pattern, typically \-32768. .A Fourth, any EM implementation is allowed to reserve a variable size block beneath the local variables. @@ -297,7 +297,7 @@ This can be done with the aforementioned ASP instruction. Each procedure frame is a separate fragment. Because any fragment may be placed anywhere in memory, procedure frames need not be contiguous. -.DS +.Dr 47 |===============================| | actual parameter n-1 | |-------------------------------| @@ -305,14 +305,14 @@ procedure frames need not be contiguous. | . | | . | |-------------------------------| - | actual parameter 0 | ( <- AB ) + | actual parameter 0 | ( <\- AB ) |===============================| |===============================| |///////////////////////////////| |///// return status block /////| - |///////////////////////////////| <- LB + |///////////////////////////////| <\- LB |===============================| | | | local variables | @@ -340,11 +340,11 @@ procedure frames need not be contiguous. | . | | . | |-------------------------------| - | parameter 0 | <- SP + | parameter 0 | <\- SP |===============================| - - Figure 1. A sample procedure frame and parameters. -.DE +.Df +Figure 1. A sample procedure frame and parameters. +.De .S2 "Heap data area" The heap area starts empty, with HP pointing to the low end of it. diff --git a/doc/em/em.i b/doc/em/em.i index 23d821c0..04ab3728 100644 --- a/doc/em/em.i +++ b/doc/em/em.i @@ -1,3 +1,4 @@ + { This is an interpreter for EM. It serves as the official machine definition. This interpreter must run on a machine which supports arithmetic with words and memory offsets. @@ -30,7 +31,7 @@ to object size in bits - 1. The effect of a count not in this range is undefined. } -.BP +.bp {$i256} {$d+} program em(tables,prog,input,output); diff --git a/doc/em/env.nr b/doc/em/env.nr new file mode 100644 index 00000000..8aafaeac --- /dev/null +++ b/doc/em/env.nr @@ -0,0 +1,210 @@ +.SN 8 +.VS 1 0 +.BP +.S1 "ENVIRONMENT INTERACTIONS" +EM programs can interact with their environment in three ways. +Two, starting/stopping and monitor calls, are dealt with in this chapter. +The remaining way to interact, interrupts, will be treated +together with traps in chapter 9. +.S2 "Program starting and stopping" +EM user programs start with a call to a procedure called +m_a_i_n. +The assembler and backends look for the definition of a procedure +with this name in their input. +The call passes three parameters to the procedure. +The parameters are similar to the parameters supplied by the +UNIX +.FS +UNIX is a Trademark of Bell Laboratories. +.FE +operating system to C programs. +These parameters are often called +.BW argc , +.B argv +and +.BW envp . +Argc is the parameter nearest to LB and is a wordsized integer. +The other two are pointers to the first element of an array of +string pointers. +.N +The +.B argv +array contains +.B argc +strings, the first of which contains the program call name. +The other strings in the +.B argv +array are the program parameters. +.P +The +.B envp +array contains strings in the form "name=string", where 'name' +is the name of an environment variable and string its value. +The +.B envp +is terminated by a zero pointer. +.P +An EM user program stops if the program returns from the first +invocation of m_a_i_n. +The contents of the function return area are used to procure a +wordsized program return code. +EM programs also stop when traps and interrupts occur that are +not caught and when the exit monitor call is executed. +.S2 "Input/Output and other monitor calls" +EM differs from most conventional machines in that it has high level i/o +instructions. +Typical instructions are OPEN FILE and READ FROM FILE instead +of low level instructions such as setting and clearing +bits in device registers. +By providing such high level i/o primitives, the task of implementing +EM on various non EM machines is made considerably easier. +.P +I/O is initiated by the MON instruction, which expects an iocode on top +of the stack. +Often there are also parameters which are pushed on the +stack in reverse order, that is: last +parameter first. +Some i/o functions also provide results, which are returned on the stack. +In the list of monitor calls we use several types of parameters and results, +these types consist of integers and unsigneds of varying sizes, but never +smaller than the wordsize, and the two pointer types. +.N 1 +The names of the types used are: +.IS 4 +.PS - 10 +.PT int +an integer of wordsize +.PT int2 +an integer whose size is the maximum of the wordsize and 2 +bytes +.PT int4 +an integer whose size is the maximum of the wordsize and 4 +bytes +.PT intp +an integer with the size of a pointer +.PT uns2 +an unsigned integer whose size is the maximum of the wordsize and 2 +.PT unsp +an unsigned integer with the size of a pointer +.PT ptr +a pointer into data space +.PE 1 +.IE 0 +The table below lists the i/o codes with their results and +parameters. +This list is similar to the system calls of the UNIX Version 7 +operating system. +.A +To execute a monitor call, proceed as follows: +.IS 2 +.N 1 +.PS a 4 "" ) +.PT +Stack the parameters, in reverse order, last parameter first. +.PT +Push the monitor call number (iocode) onto the stack. +.PT +Execute the MON instruction. +.PE 1 +.IE +An error code is present on the top of the stack after +execution of most monitor calls. +If this error code is zero, the call performed the action +requested and the results are available on top of the stack. +Non-zero error codes indicate a failure, in this case no +results are available and the error code has been pushed twice. +This construction enables programs to test for failure with a +single instruction (~TEQ or TNE~) and still find out the cause of +the failure. +The result name 'e' is reserved for the error code. +.N 1 +List of monitor calls. +.DS B +.ta 2n 8n 16n 32n 48n +number name parameters results function + + 1 Exit status:int Terminate this process + 2 Fork e,flag,pid:int Spawn new process + 3 Read fildes:int;buf:ptr;nbytes:unsp + e:int;rbytes:unsp Read from file + 4 Write fildes:int;buf:ptr;nbytes:unsp + e:int;wbytes:unsp Write on a file + 5 Open string:ptr;flag:int + e,fildes:int Open file for read and/or write + 6 Close fildes:int e:int Close a file + 7 Wait e:int;status,pid:int2 + Wait for child + 8 Creat string:ptr;mode:int + e,fildes:int Create a new file + 9 Link string1,string2:ptr + e:int Link to a file + 10 Unlink string:ptr e:int Remove directory entry + 12 Chdir string:ptr e:int Change default directory + 14 Mknod string:ptr;mode,addr:int2 + e:int Make a special file + 15 Chmod string:ptr;mode:int2 + e:int Change mode of file + 16 Chown string:ptr;owner,group:int2 + e:int Change owner/group of a file + 18 Stat string,statbuf:ptr + e:int Get file status + 19 Lseek fildes:int;off:int4;whence:int + e:int;oldoff:int4 Move read/write pointer + 20 Getpid pid:int2 Get process identification + 21 Mount special,string:ptr;rwflag:int + e:int Mount file system + 22 Umount special:ptr e:int Unmount file system + 23 Setuid userid:int2 e:int Set user ID + 24 Getuid e_uid,r_uid:int2 Get user ID + 25 Stime time:int4 e:int Set time and date + 26 Ptrace request:int;pid:int2;addr:ptr;data:int + e,value:int Process trace + 27 Alarm seconds:uns2 previous:uns2 Schedule signal + 28 Fstat fildes:int;statbuf:ptr + e:int Get file status + 29 Pause Stop until signal + 30 Utime string,timep:ptr + e:int Set file times + 33 Access string:ptr;mode:int + e:int Determine file accessibility + 34 Nice incr:int Set program priority + 35 Ftime bufp:ptr e:int Get date and time + 36 Sync Update filesystem + 37 Kill pid:int2;sig:int + e:int Send signal to a process + 41 Dup fildes,newfildes:int + e,fildes:int Duplicate a file descriptor + 42 Pipe e,w_des,r_des:int Create a pipe + 43 Times buffer:ptr Get process times + 44 Profil buff:ptr;bufsiz,offset,scale:intp Execution time profile + 46 Setgid gid:int2 e:int Set group ID + 47 Getgid e_gid,r_gid:int Get group ID + 48 Sigtrp trapno,signo:int + e,prevtrap:int See below + 51 Acct file:ptr e:int Turn accounting on or off + 53 Lock flag:int e:int Lock a process + 54 Ioctl fildes,request:int;argp:ptr + e:int Control device + 56 Mpxcall cmd:int;vec:ptr e:int Multiplexed file handling + 59 Exece name,argv,envp:ptr + e:int Execute a file + 60 Umask complmode:int2 oldmask:int2 Set file creation mode mask + 61 Chroot string:ptr e:int Change root directory +.DE 1 +Codes 0, 11, 13, 17, 31, 32, 38, 39, 40, 45, 49, 50, 52, +55, 57, 58, 62, and 63 are +not used. +.P +All monitor calls, except fork and sigtrp +are the same as the UNIX version 7 system calls. +.P +The sigtrp entry maps UNIX signals onto EM interrupts. +Normally, trapno is in the range 0 to 252. +In that case it requests that signal signo +will cause trap trapno to occur. +When given trap number \-2, default signal handling is reset, and when given +trap number \-3, the signal is ignored. +.P +The flag returned by fork is 1 in the child process and 0 in +the parent. +The pid returned is the process-id of the other process. diff --git a/doc/em/int/.distr b/doc/em/int/.distr new file mode 100644 index 00000000..bc114b36 --- /dev/null +++ b/doc/em/int/.distr @@ -0,0 +1,5 @@ +Makefile +READ_ME +em.p +emdmp.c +mktables.c diff --git a/doc/em/int/Makefile b/doc/em/int/Makefile index 54f79298..ab19fd0f 100644 --- a/doc/em/int/Makefile +++ b/doc/em/int/Makefile @@ -9,7 +9,7 @@ tables: mktables $(HOME)/util/ass/ip_spec.t mktables: mktables.c $(HOME)/h/em_spec.h $(HOME)/h/em_flag.h \ $(HOME)/util/data/em_data.a $(HOME)/util/ass/ip_spec.h - cc -O -o mktables mktables.c $(HOME)/util/data/em_data.a + cc -I$(HOME) -O -o mktables mktables.c $(HOME)/util/data/em_data.a em.out: em.p apc -mint -O em.p >emerrs ; mv e.out em.out @@ -24,7 +24,7 @@ nem: nem.p apc -O -i nem.p >emerrs ; mv a.out nem emdmp: emdmp.c - cc -o emdmp -O emdmp.c + cc -I$(HOME)/h -o emdmp -O emdmp.c cmp: diff --git a/doc/em/int/emdmp.c b/doc/em/int/emdmp.c index 60588668..b6e4b5e6 100644 --- a/doc/em/int/emdmp.c +++ b/doc/em/int/emdmp.c @@ -1,17 +1,7 @@ +/* $Header$ */ /* - * (c) copyright 1983 by the Vrije Universiteit, Amsterdam, The Netherlands. - * - * This product is part of the Amsterdam Compiler Kit. - * - * Permission to use, sell, duplicate or disclose this software must be - * obtained in writing. Requests for such permissions may be sent to - * - * Dr. Andrew S. Tanenbaum - * Wiskundig Seminarium - * Vrije Universiteit - * Postbox 7161 - * 1007 MC Amsterdam - * The Netherlands + * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". * */ @@ -20,7 +10,7 @@ /* Print a readable version of the data in the post mortem dump */ /* dmpc [-s] [-dn,m] [file] */ -#include "/usr/em/h/local.h" +#include #include #include @@ -109,7 +99,7 @@ main(argc,argv) char **argv; printf(" "); } if ( line ) { - printf("line %D",line) ; + printf("line %ld",line) ; } if ( fileaddr || line ) printf(", "); fseek(fcore,512L,0); @@ -128,9 +118,9 @@ main(argc,argv) char **argv; if ( tsize ) printn("Text size",tsize) ; if ( dsize ) printn("Data size",dsize) ; } - if ( dflag==0 ) return 0; + if ( dflag==0 ) exit(0); fatal("d-flag not implemeted (yet)"); - return 1 ; + exit(1) ; } scanargs(argc,argv) char **argv ; { diff --git a/doc/em/int/mktables.c b/doc/em/int/mktables.c index 70f4365f..62b2a613 100644 --- a/doc/em/int/mktables.c +++ b/doc/em/int/mktables.c @@ -1,26 +1,16 @@ +/* $Header$ */ /* - * (c) copyright 1983 by the Vrije Universiteit, Amsterdam, The Netherlands. - * - * This product is part of the Amsterdam Compiler Kit. - * - * Permission to use, sell, duplicate or disclose this software must be - * obtained in writing. Requests for such permissions may be sent to - * - * Dr. Andrew S. Tanenbaum - * Wiskundig Seminarium - * Vrije Universiteit - * Postbox 7161 - * 1007 MC Amsterdam - * The Netherlands + * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". * */ /* Author: E.G. Keizer */ #include -#include "/usr/em/util/ass/ip_spec.h" -#include "/usr/em/h/em_spec.h" -#include "/usr/em/h/em_flag.h" +#include +#include +#include /* This program reads the human readable interpreter specification and produces a efficient machine representation that can be @@ -57,7 +47,7 @@ main(argc,argv) char **argv ; { atend=0 ; readin(); atend=1 ; - return nerror ; + exit(nerror) ; } readin() { diff --git a/doc/em/intro.nr b/doc/em/intro.nr index 4b9c2667..e9c4d7ad 100644 --- a/doc/em/intro.nr +++ b/doc/em/intro.nr @@ -81,7 +81,6 @@ toward structured programs consisting of many small procedures greatly reduces the value of registers to hold local variables because the large number of procedure calls implies a large overhead in saving and restoring the registers at every call. -.BP .P Although there are no general purpose registers, there are a few internal registers with specific functions as follows: @@ -89,12 +88,12 @@ few internal registers with specific functions as follows: .N 1 .TS tab(:); -l 1 l l. -PC:-:Program Counter:Pointer to next instruction -LB:-:Local Base:Points to base of the local variables \ -in the current procedure. -SP:-:Stack Pointer:Points to the highest occupied word on the stack. -HP:-:Heap Pointer:Points to the top of the heap area. +l 1 l l l. +PC:\-:Program Counter:Pointer to next instruction +LB:\-:Local Base:Points to base of the local variables +:::in the current procedure. +SP:\-:Stack Pointer:Points to the highest occupied word on the stack. +HP:\-:Heap Pointer:Points to the top of the heap area. .TE 1 .IE .A diff --git a/doc/em/iotrap.nr b/doc/em/iotrap.nr index c5a5fa2d..716f363b 100644 --- a/doc/em/iotrap.nr +++ b/doc/em/iotrap.nr @@ -338,7 +338,7 @@ msave lim ; get current ignore mask ste msave ; save it lim - loc 4 ; bit for EFOVFL + loc 16 ; bit for EFOVFL ior 2 ; set in mask sim ; ignore EFOVFL from now on lpi $catch ; load procedure identifier diff --git a/doc/em/ip.awk b/doc/em/ip.awk index 53839457..6c365869 100644 --- a/doc/em/ip.awk +++ b/doc/em/ip.awk @@ -1,6 +1,11 @@ -BEGIN { printf ".TS\nlw(6) lw(8) rw(3) rw(6) 14 lw(6) lw(8) rw(3) rw(6) 14 lw(6) lw(8) rw(3) rw(6).\n" } -NF == 4 { printf "%s\t%s\t%d\t%d",$1,$2,$3,$4 } -NF == 3 { printf "%s\t%s\t\t%d",$1,$2,$3 } - { if ( NR%3 == 0 ) printf("\n") ; else printf("\t"); } -END { if ( NR%3 != 0 ) printf("\n") - printf ".TE\n" } +BEGIN { printf(".TS\n"); + for (i = 0; i < 3; i++) + printf("lw(4) 0 lw(6) 0 rw(2) 0 rw(5) 8 "); + printf(".\n"); + } +NF == 4 { printf "%s\t%s\t%d\t%d",$1,$2,$3,$4 } +NF == 3 { printf "%s\t%s\t\t%d",$1,$2,$3 } + { if ( NR%3 == 0 ) printf("\n") ; else printf("\t"); } +END { if ( NR%3 != 0 ) printf("\n"); + printf(".TE\n"); + } diff --git a/doc/em/itables b/doc/em/itables index 27d9c41c..a4825dc9 100644 --- a/doc/em/itables +++ b/doc/em/itables @@ -10,2105 +10,2430 @@ .. .nf .nr #~ 0 -.if n .nr #~ 0.6n +.if \n(.T .if n .nr #~ 0.6n .ds #d .d .if \(ts\n(.z\(ts\(ts .ds #d nl .fc .nr 33 \n(.s -.rm 80 81 82 83 84 85 86 87 88 89 90 91 -.nr 80 0 +.rm 66 67 68 69 70 71 72 73 74 75 76 77 +.nr 66 0 .nr 38 \waar -.if \n(80<\n(38 .nr 80 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wadp -.if \n(80<\n(38 .nr 80 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wadp -.if \n(80<\n(38 .nr 80 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wasp -.if \n(80<\n(38 .nr 80 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wbeq -.if \n(80<\n(38 .nr 80 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wble -.if \n(80<\n(38 .nr 80 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wbne -.if \n(80<\n(38 .nr 80 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wbra -.if \n(80<\n(38 .nr 80 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wcff -.if \n(80<\n(38 .nr 80 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wcmf -.if \n(80<\n(38 .nr 80 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wcms -.if \n(80<\n(38 .nr 80 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wdec -.if \n(80<\n(38 .nr 80 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wdup -.if \n(80<\n(38 .nr 80 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wfil -.if \n(80<\n(38 .nr 80 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wine -.if \n(80<\n(38 .nr 80 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \winn -.if \n(80<\n(38 .nr 80 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wlae -.if \n(80<\n(38 .nr 80 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wlal -.if \n(80<\n(38 .nr 80 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wlal -.if \n(80<\n(38 .nr 80 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wldc -.if \n(80<\n(38 .nr 80 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wldl -.if \n(80<\n(38 .nr 80 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wlfr -.if \n(80<\n(38 .nr 80 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wlil -.if \n(80<\n(38 .nr 80 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wlni -.if \n(80<\n(38 .nr 80 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wloc -.if \n(80<\n(38 .nr 80 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wloe -.if \n(80<\n(38 .nr 80 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wlof -.if \n(80<\n(38 .nr 80 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wloi -.if \n(80<\n(38 .nr 80 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wlol -.if \n(80<\n(38 .nr 80 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wlol -.if \n(80<\n(38 .nr 80 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wlxa -.if \n(80<\n(38 .nr 80 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wmli -.if \n(80<\n(38 .nr 80 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wret -.if \n(80<\n(38 .nr 80 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wsbf -.if \n(80<\n(38 .nr 80 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wset -.if \n(80<\n(38 .nr 80 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wsli -.if \n(80<\n(38 .nr 80 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wstf -.if \n(80<\n(38 .nr 80 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wsti -.if \n(80<\n(38 .nr 80 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wstl -.if \n(80<\n(38 .nr 80 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wstl -.if \n(80<\n(38 .nr 80 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wtgt -.if \n(80<\n(38 .nr 80 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wzeq -.if \n(80<\n(38 .nr 80 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wzge -.if \n(80<\n(38 .nr 80 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wzlt -.if \n(80<\n(38 .nr 80 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wzre -.if \n(80<\n(38 .nr 80 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wzrl -.if \n(80<\n(38 .nr 80 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \waar -.if \n(80<\n(38 .nr 80 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wadi -.if \n(80<\n(38 .nr 80 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wads -.if \n(80<\n(38 .nr 80 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wand -.if \n(80<\n(38 .nr 80 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wass -.if \n(80<\n(38 .nr 80 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wbgt -.if \n(80<\n(38 .nr 80 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wbls -.if \n(80<\n(38 .nr 80 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wbne -.if \n(80<\n(38 .nr 80 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wcfi -.if \n(80<\n(38 .nr 80 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wcmf -.if \n(80<\n(38 .nr 80 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wcmi -.if \n(80<\n(38 .nr 80 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wcmu -.if \n(80<\n(38 .nr 80 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wcom -.if \n(80<\n(38 .nr 80 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wcsb -.if \n(80<\n(38 .nr 80 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wcui -.if \n(80<\n(38 .nr 80 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wdel -.if \n(80<\n(38 .nr 80 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wdus -.if \n(80<\n(38 .nr 80 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wdvf -.if \n(80<\n(38 .nr 80 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wdvu -.if \n(80<\n(38 .nr 80 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wfef -.if \n(80<\n(38 .nr 80 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \winl -.if \n(80<\n(38 .nr 80 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \winn -.if \n(80<\n(38 .nr 80 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wlar -.if \n(80<\n(38 .nr 80 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wldf -.if \n(80<\n(38 .nr 80 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wlfr -.if \n(80<\n(38 .nr 80 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wlim -.if \n(80<\n(38 .nr 80 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wlor -.if \n(80<\n(38 .nr 80 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wlxl -.if \n(80<\n(38 .nr 80 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wmli -.if \n(80<\n(38 .nr 80 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wmlu -.if \n(80<\n(38 .nr 80 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wngf -.if \n(80<\n(38 .nr 80 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wnop -.if \n(80<\n(38 .nr 80 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wret -.if \n(80<\n(38 .nr 80 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wrmu -.if \n(80<\n(38 .nr 80 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wrol -.if \n(80<\n(38 .nr 80 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wrtt -.if \n(80<\n(38 .nr 80 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wsbf -.if \n(80<\n(38 .nr 80 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wsbi -.if \n(80<\n(38 .nr 80 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wsbu -.if \n(80<\n(38 .nr 80 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wsdf -.if \n(80<\n(38 .nr 80 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wset -.if \n(80<\n(38 .nr 80 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wsil -.if \n(80<\n(38 .nr 80 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wsli -.if \n(80<\n(38 .nr 80 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wslu -.if \n(80<\n(38 .nr 80 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wsru -.if \n(80<\n(38 .nr 80 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wsts -.if \n(80<\n(38 .nr 80 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wtge -.if \n(80<\n(38 .nr 80 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wxor -.if \n(80<\n(38 .nr 80 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wzer -.if \n(80<\n(38 .nr 80 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wzle -.if \n(80<\n(38 .nr 80 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wzrf -.if \n(80<\n(38 .nr 80 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wdch -.if \n(80<\n(38 .nr 80 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wexg -.if \n(80<\n(38 .nr 80 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wldc -.if \n(80<\n(38 .nr 80 \n(38 -.80 -.rm 80 -.nr 38 6n -.if \n(80<\n(38 .nr 80 \n(38 -.nr 81 0 -.nr 38 \wmwPo -.if \n(81<\n(38 .nr 81 \n(38 -.nr 38 \w2 -.if \n(81<\n(38 .nr 81 \n(38 -.nr 38 \wsN -.if \n(81<\n(38 .nr 81 \n(38 -.nr 38 \wmwPo -.if \n(81<\n(38 .nr 81 \n(38 -.nr 38 \wsP -.if \n(81<\n(38 .nr 81 \n(38 -.nr 38 \wsP -.if \n(81<\n(38 .nr 81 \n(38 -.nr 38 \wsP -.if \n(81<\n(38 .nr 81 \n(38 -.nr 38 \wsP -.if \n(81<\n(38 .nr 81 \n(38 -.nr 38 \w- -.if \n(81<\n(38 .nr 81 \n(38 -.nr 38 \wsP -.if \n(81<\n(38 .nr 81 \n(38 -.nr 38 \wsP -.if \n(81<\n(38 .nr 81 \n(38 -.nr 38 \w- -.if \n(81<\n(38 .nr 81 \n(38 -.nr 38 \wmwPo -.if \n(81<\n(38 .nr 81 \n(38 -.nr 38 \w2 -.if \n(81<\n(38 .nr 81 \n(38 -.nr 38 \wsw -.if \n(81<\n(38 .nr 81 \n(38 -.nr 38 \wsP -.if \n(81<\n(38 .nr 81 \n(38 -.nr 38 \w2 -.if \n(81<\n(38 .nr 81 \n(38 -.nr 38 \wN2 -.if \n(81<\n(38 .nr 81 \n(38 -.nr 38 \wswP -.if \n(81<\n(38 .nr 81 \n(38 -.nr 38 \wmP -.if \n(81<\n(38 .nr 81 \n(38 -.nr 38 \wmP -.if \n(81<\n(38 .nr 81 \n(38 -.nr 38 \wsP -.if \n(81<\n(38 .nr 81 \n(38 -.nr 38 \wmwP -.if \n(81<\n(38 .nr 81 \n(38 -.nr 38 \w- -.if \n(81<\n(38 .nr 81 \n(38 -.nr 38 \wmN -.if \n(81<\n(38 .nr 81 \n(38 -.nr 38 \ww2 -.if \n(81<\n(38 .nr 81 \n(38 -.nr 38 \wmwPo -.if \n(81<\n(38 .nr 81 \n(38 -.nr 38 \wmPo -.if \n(81<\n(38 .nr 81 \n(38 -.nr 38 \wwP2 -.if \n(81<\n(38 .nr 81 \n(38 -.nr 38 \wmwN -.if \n(81<\n(38 .nr 81 \n(38 -.nr 38 \wmPo -.if \n(81<\n(38 .nr 81 \n(38 -.nr 38 \wmwPo -.if \n(81<\n(38 .nr 81 \n(38 -.nr 38 \wsP -.if \n(81<\n(38 .nr 81 \n(38 -.nr 38 \wsP -.if \n(81<\n(38 .nr 81 \n(38 -.nr 38 \wsP -.if \n(81<\n(38 .nr 81 \n(38 -.nr 38 \wmwPo -.if \n(81<\n(38 .nr 81 \n(38 -.nr 38 \w2 -.if \n(81<\n(38 .nr 81 \n(38 -.nr 38 \wmPo -.if \n(81<\n(38 .nr 81 \n(38 -.nr 38 \wwP2 -.if \n(81<\n(38 .nr 81 \n(38 -.nr 38 \wmwN -.if \n(81<\n(38 .nr 81 \n(38 -.nr 38 \w- -.if \n(81<\n(38 .nr 81 \n(38 -.nr 38 \w2 -.if \n(81<\n(38 .nr 81 \n(38 -.nr 38 \wsP -.if \n(81<\n(38 .nr 81 \n(38 -.nr 38 \wsP -.if \n(81<\n(38 .nr 81 \n(38 -.nr 38 \ww2 -.if \n(81<\n(38 .nr 81 \n(38 -.nr 38 \wswN -.if \n(81<\n(38 .nr 81 \n(38 -.nr 38 \we- -.if \n(81<\n(38 .nr 81 \n(38 -.nr 38 \we2 -.if \n(81<\n(38 .nr 81 \n(38 -.nr 38 \we- -.if \n(81<\n(38 .nr 81 \n(38 -.nr 38 \we2 -.if \n(81<\n(38 .nr 81 \n(38 -.nr 38 \we2 -.if \n(81<\n(38 .nr 81 \n(38 -.nr 38 \we2 -.if \n(81<\n(38 .nr 81 \n(38 -.nr 38 \we2 -.if \n(81<\n(38 .nr 81 \n(38 -.nr 38 \we2 -.if \n(81<\n(38 .nr 81 \n(38 -.nr 38 \we- -.if \n(81<\n(38 .nr 81 \n(38 -.nr 38 \we2 -.if \n(81<\n(38 .nr 81 \n(38 -.nr 38 \we- -.if \n(81<\n(38 .nr 81 \n(38 -.nr 38 \we2 -.if \n(81<\n(38 .nr 81 \n(38 -.nr 38 \we- -.if \n(81<\n(38 .nr 81 \n(38 -.nr 38 \we2 -.if \n(81<\n(38 .nr 81 \n(38 -.nr 38 \we- -.if \n(81<\n(38 .nr 81 \n(38 -.nr 38 \wewP2 -.if \n(81<\n(38 .nr 81 \n(38 -.nr 38 \we2 -.if \n(81<\n(38 .nr 81 \n(38 -.nr 38 \we- -.if \n(81<\n(38 .nr 81 \n(38 -.nr 38 \we2 -.if \n(81<\n(38 .nr 81 \n(38 -.nr 38 \we- -.if \n(81<\n(38 .nr 81 \n(38 -.nr 38 \wewP2 -.if \n(81<\n(38 .nr 81 \n(38 -.nr 38 \we- -.if \n(81<\n(38 .nr 81 \n(38 -.nr 38 \we2 -.if \n(81<\n(38 .nr 81 \n(38 -.nr 38 \we2 -.if \n(81<\n(38 .nr 81 \n(38 -.nr 38 \we2 -.if \n(81<\n(38 .nr 81 \n(38 -.nr 38 \we- -.if \n(81<\n(38 .nr 81 \n(38 -.nr 38 \wesP -.if \n(81<\n(38 .nr 81 \n(38 -.nr 38 \we2 -.if \n(81<\n(38 .nr 81 \n(38 -.nr 38 \we2 -.if \n(81<\n(38 .nr 81 \n(38 -.nr 38 \we- -.if \n(81<\n(38 .nr 81 \n(38 -.nr 38 \we- -.if \n(81<\n(38 .nr 81 \n(38 -.nr 38 \we- -.if \n(81<\n(38 .nr 81 \n(38 -.nr 38 \we2 -.if \n(81<\n(38 .nr 81 \n(38 -.nr 38 \we2 -.if \n(81<\n(38 .nr 81 \n(38 -.nr 38 \we- -.if \n(81<\n(38 .nr 81 \n(38 -.nr 38 \we- -.if \n(81<\n(38 .nr 81 \n(38 -.nr 38 \we2 -.if \n(81<\n(38 .nr 81 \n(38 -.nr 38 \we- -.if \n(81<\n(38 .nr 81 \n(38 -.nr 38 \we2 -.if \n(81<\n(38 .nr 81 \n(38 -.nr 38 \we2 -.if \n(81<\n(38 .nr 81 \n(38 -.nr 38 \we2 -.if \n(81<\n(38 .nr 81 \n(38 -.nr 38 \wewP2 -.if \n(81<\n(38 .nr 81 \n(38 -.nr 38 \we2 -.if \n(81<\n(38 .nr 81 \n(38 -.nr 38 \we- -.if \n(81<\n(38 .nr 81 \n(38 -.nr 38 \we2 -.if \n(81<\n(38 .nr 81 \n(38 -.nr 38 \we2 -.if \n(81<\n(38 .nr 81 \n(38 -.nr 38 \we- -.if \n(81<\n(38 .nr 81 \n(38 -.nr 38 \we2 -.if \n(81<\n(38 .nr 81 \n(38 -.nr 38 \we- -.if \n(81<\n(38 .nr 81 \n(38 -.nr 38 \we2 -.if \n(81<\n(38 .nr 81 \n(38 -.nr 38 \we2 -.if \n(81<\n(38 .nr 81 \n(38 -.nr 38 \we- -.if \n(81<\n(38 .nr 81 \n(38 -.nr 38 \we- -.if \n(81<\n(38 .nr 81 \n(38 -.nr 38 \w4 -.if \n(81<\n(38 .nr 81 \n(38 -.81 -.rm 81 -.nr 38 8n -.if \n(81<\n(38 .nr 81 \n(38 -.nr 82 0 -.nr 38 \w1 -.if \n(82<\n(38 .nr 82 \n(38 -.nr 38 \w1 -.if \n(82<\n(38 .nr 82 \n(38 -.nr 38 \w5 -.if \n(82<\n(38 .nr 82 \n(38 -.nr 38 \w1 -.if \n(82<\n(38 .nr 82 \n(38 -.nr 38 \w1 -.if \n(82<\n(38 .nr 82 \n(38 -.nr 38 \w1 -.if \n(82<\n(38 .nr 82 \n(38 -.nr 38 \w2 -.if \n(82<\n(38 .nr 82 \n(38 -.nr 38 \w1 -.if \n(82<\n(38 .nr 82 \n(38 -.nr 38 \w1 -.if \n(82<\n(38 .nr 82 \n(38 -.nr 38 \w1 -.if \n(82<\n(38 .nr 82 \n(38 -.nr 38 \w1 -.if \n(82<\n(38 .nr 82 \n(38 -.nr 38 \w1 -.if \n(82<\n(38 .nr 82 \n(38 -.nr 38 \w1 -.if \n(82<\n(38 .nr 82 \n(38 -.nr 38 \w1 -.if \n(82<\n(38 .nr 82 \n(38 -.nr 38 \w1 -.if \n(82<\n(38 .nr 82 \n(38 -.nr 38 \w1 -.if \n(82<\n(38 .nr 82 \n(38 -.nr 38 \w2 -.if \n(82<\n(38 .nr 82 \n(38 -.nr 38 \w1 -.if \n(82<\n(38 .nr 82 \n(38 -.nr 38 \w4 -.if \n(82<\n(38 .nr 82 \n(38 -.nr 38 \w1 -.if \n(82<\n(38 .nr 82 \n(38 -.nr 38 \w8 -.if \n(82<\n(38 .nr 82 \n(38 -.nr 38 \w1 -.if \n(82<\n(38 .nr 82 \n(38 -.nr 38 \w2 -.if \n(82<\n(38 .nr 82 \n(38 -.nr 38 \w1 -.if \n(82<\n(38 .nr 82 \n(38 -.nr 38 \w1 -.if \n(82<\n(38 .nr 82 \n(38 -.nr 38 \w1 -.if \n(82<\n(38 .nr 82 \n(38 -.nr 38 \w1 -.if \n(82<\n(38 .nr 82 \n(38 -.nr 38 \w1 -.if \n(82<\n(38 .nr 82 \n(38 -.nr 38 \w5 -.if \n(82<\n(38 .nr 82 \n(38 -.nr 38 \w1 -.if \n(82<\n(38 .nr 82 \n(38 -.nr 38 \w1 -.if \n(82<\n(38 .nr 82 \n(38 -.nr 38 \w1 -.if \n(82<\n(38 .nr 82 \n(38 -.nr 38 \w1 -.if \n(82<\n(38 .nr 82 \n(38 -.82 -.rm 82 -.nr 38 3n -.if \n(82<\n(38 .nr 82 \n(38 -.nr 83 0 -.nr 38 \w34 -.if \n(83<\n(38 .nr 83 \n(38 -.nr 38 \w38 -.if \n(83<\n(38 .nr 83 \n(38 -.nr 38 \w42 -.if \n(83<\n(38 .nr 83 \n(38 -.nr 38 \w45 -.if \n(83<\n(38 .nr 83 \n(38 -.nr 38 \w52 -.if \n(83<\n(38 .nr 83 \n(38 -.nr 38 \w55 -.if \n(83<\n(38 .nr 83 \n(38 -.nr 38 \w58 -.if \n(83<\n(38 .nr 83 \n(38 -.nr 38 \w62 -.if \n(83<\n(38 .nr 83 \n(38 -.nr 38 \w93 -.if \n(83<\n(38 .nr 83 \n(38 -.nr 38 \w96 -.if \n(83<\n(38 .nr 83 \n(38 -.nr 38 \w100 -.if \n(83<\n(38 .nr 83 \n(38 -.nr 38 \w103 -.if \n(83<\n(38 .nr 83 \n(38 -.nr 38 \w106 -.if \n(83<\n(38 .nr 83 \n(38 -.nr 38 \w109 -.if \n(83<\n(38 .nr 83 \n(38 -.nr 38 \w112 -.if \n(83<\n(38 .nr 83 \n(38 -.nr 38 \w117 -.if \n(83<\n(38 .nr 83 \n(38 -.nr 38 \w120 -.if \n(83<\n(38 .nr 83 \n(38 -.nr 38 \w129 -.if \n(83<\n(38 .nr 83 \n(38 -.nr 38 \w132 -.if \n(83<\n(38 .nr 83 \n(38 -.nr 38 \w136 -.if \n(83<\n(38 .nr 83 \n(38 -.nr 38 \w139 -.if \n(83<\n(38 .nr 83 \n(38 -.nr 38 \w143 -.if \n(83<\n(38 .nr 83 \n(38 -.nr 38 \w146 -.if \n(83<\n(38 .nr 83 \n(38 -.nr 38 \w150 -.if \n(83<\n(38 .nr 83 \n(38 -.nr 38 \w152 -.if \n(83<\n(38 .nr 83 \n(38 -.nr 38 \w155 -.if \n(83<\n(38 .nr 83 \n(38 -.nr 38 \w162 -.if \n(83<\n(38 .nr 83 \n(38 -.nr 38 \w168 -.if \n(83<\n(38 .nr 83 \n(38 -.nr 38 \w174 -.if \n(83<\n(38 .nr 83 \n(38 -.nr 38 \w180 -.if \n(83<\n(38 .nr 83 \n(38 -.nr 38 \w190 -.if \n(83<\n(38 .nr 83 \n(38 -.nr 38 \w194 -.if \n(83<\n(38 .nr 83 \n(38 -.nr 38 \w199 -.if \n(83<\n(38 .nr 83 \n(38 -.nr 38 \w202 -.if \n(83<\n(38 .nr 83 \n(38 -.nr 38 \w206 -.if \n(83<\n(38 .nr 83 \n(38 -.nr 38 \w209 -.if \n(83<\n(38 .nr 83 \n(38 -.nr 38 \w214 -.if \n(83<\n(38 .nr 83 \n(38 -.nr 38 \w218 -.if \n(83<\n(38 .nr 83 \n(38 -.nr 38 \w224 -.if \n(83<\n(38 .nr 83 \n(38 -.nr 38 \w228 -.if \n(83<\n(38 .nr 83 \n(38 -.nr 38 \w235 -.if \n(83<\n(38 .nr 83 \n(38 -.nr 38 \w238 -.if \n(83<\n(38 .nr 83 \n(38 -.nr 38 \w242 -.if \n(83<\n(38 .nr 83 \n(38 -.nr 38 \w245 -.if \n(83<\n(38 .nr 83 \n(38 -.nr 38 \w248 -.if \n(83<\n(38 .nr 83 \n(38 -.nr 38 \w252 -.if \n(83<\n(38 .nr 83 \n(38 -.nr 38 \w1 -.if \n(83<\n(38 .nr 83 \n(38 -.nr 38 \w4 -.if \n(83<\n(38 .nr 83 \n(38 -.nr 38 \w7 -.if \n(83<\n(38 .nr 83 \n(38 -.nr 38 \w10 -.if \n(83<\n(38 .nr 83 \n(38 -.nr 38 \w13 -.if \n(83<\n(38 .nr 83 \n(38 -.nr 38 \w16 -.if \n(83<\n(38 .nr 83 \n(38 -.nr 38 \w19 -.if \n(83<\n(38 .nr 83 \n(38 -.nr 38 \w22 -.if \n(83<\n(38 .nr 83 \n(38 -.nr 38 \w25 -.if \n(83<\n(38 .nr 83 \n(38 -.nr 38 \w28 -.if \n(83<\n(38 .nr 83 \n(38 -.nr 38 \w31 -.if \n(83<\n(38 .nr 83 \n(38 -.nr 38 \w34 -.if \n(83<\n(38 .nr 83 \n(38 -.nr 38 \w37 -.if \n(83<\n(38 .nr 83 \n(38 -.nr 38 \w40 -.if \n(83<\n(38 .nr 83 \n(38 -.nr 38 \w43 -.if \n(83<\n(38 .nr 83 \n(38 -.nr 38 \w46 -.if \n(83<\n(38 .nr 83 \n(38 -.nr 38 \w49 -.if \n(83<\n(38 .nr 83 \n(38 -.nr 38 \w52 -.if \n(83<\n(38 .nr 83 \n(38 -.nr 38 \w55 -.if \n(83<\n(38 .nr 83 \n(38 -.nr 38 \w58 -.if \n(83<\n(38 .nr 83 \n(38 -.nr 38 \w61 -.if \n(83<\n(38 .nr 83 \n(38 -.nr 38 \w64 -.if \n(83<\n(38 .nr 83 \n(38 -.nr 38 \w67 -.if \n(83<\n(38 .nr 83 \n(38 -.nr 38 \w70 -.if \n(83<\n(38 .nr 83 \n(38 -.nr 38 \w73 -.if \n(83<\n(38 .nr 83 \n(38 -.nr 38 \w76 -.if \n(83<\n(38 .nr 83 \n(38 -.nr 38 \w79 -.if \n(83<\n(38 .nr 83 \n(38 -.nr 38 \w82 -.if \n(83<\n(38 .nr 83 \n(38 -.nr 38 \w85 -.if \n(83<\n(38 .nr 83 \n(38 -.nr 38 \w88 -.if \n(83<\n(38 .nr 83 \n(38 -.nr 38 \w91 -.if \n(83<\n(38 .nr 83 \n(38 -.nr 38 \w94 -.if \n(83<\n(38 .nr 83 \n(38 -.nr 38 \w97 -.if \n(83<\n(38 .nr 83 \n(38 -.nr 38 \w100 -.if \n(83<\n(38 .nr 83 \n(38 -.nr 38 \w103 -.if \n(83<\n(38 .nr 83 \n(38 -.nr 38 \w106 -.if \n(83<\n(38 .nr 83 \n(38 -.nr 38 \w109 -.if \n(83<\n(38 .nr 83 \n(38 -.nr 38 \w112 -.if \n(83<\n(38 .nr 83 \n(38 -.nr 38 \w115 -.if \n(83<\n(38 .nr 83 \n(38 -.nr 38 \w118 -.if \n(83<\n(38 .nr 83 \n(38 -.nr 38 \w121 -.if \n(83<\n(38 .nr 83 \n(38 -.nr 38 \w124 -.if \n(83<\n(38 .nr 83 \n(38 -.nr 38 \w127 -.if \n(83<\n(38 .nr 83 \n(38 -.nr 38 \w130 -.if \n(83<\n(38 .nr 83 \n(38 -.nr 38 \w133 -.if \n(83<\n(38 .nr 83 \n(38 -.nr 38 \w136 -.if \n(83<\n(38 .nr 83 \n(38 -.nr 38 \w139 -.if \n(83<\n(38 .nr 83 \n(38 -.nr 38 \w142 -.if \n(83<\n(38 .nr 83 \n(38 -.nr 38 \w145 -.if \n(83<\n(38 .nr 83 \n(38 -.nr 38 \w148 -.if \n(83<\n(38 .nr 83 \n(38 -.nr 38 \w151 -.if \n(83<\n(38 .nr 83 \n(38 -.nr 38 \w154 -.if \n(83<\n(38 .nr 83 \n(38 -.nr 38 \w157 -.if \n(83<\n(38 .nr 83 \n(38 -.nr 38 \w0 -.if \n(83<\n(38 .nr 83 \n(38 -.83 -.rm 83 -.nr 38 6n -.if \n(83<\n(38 .nr 83 \n(38 -.nr 84 0 -.nr 38 \wadf -.if \n(84<\n(38 .nr 84 \n(38 -.nr 38 \wadp -.if \n(84<\n(38 .nr 84 \n(38 -.nr 38 \wads -.if \n(84<\n(38 .nr 84 \n(38 -.nr 38 \wasp -.if \n(84<\n(38 .nr 84 \n(38 -.nr 38 \wbge -.if \n(84<\n(38 .nr 84 \n(38 -.nr 38 \wblm -.if \n(84<\n(38 .nr 84 \n(38 -.nr 38 \wbra -.if \n(84<\n(38 .nr 84 \n(38 -.nr 38 \wcal -.if \n(84<\n(38 .nr 84 \n(38 -.nr 38 \wcif -.if \n(84<\n(38 .nr 84 \n(38 -.nr 38 \wcmi -.if \n(84<\n(38 .nr 84 \n(38 -.nr 38 \wcsa -.if \n(84<\n(38 .nr 84 \n(38 -.nr 38 \wdee -.if \n(84<\n(38 .nr 84 \n(38 -.nr 38 \wdvf -.if \n(84<\n(38 .nr 84 \n(38 -.nr 38 \winc -.if \n(84<\n(38 .nr 84 \n(38 -.nr 38 \winl -.if \n(84<\n(38 .nr 84 \n(38 -.nr 38 \wior -.if \n(84<\n(38 .nr 84 \n(38 -.nr 38 \wlae -.if \n(84<\n(38 .nr 84 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wlal -.if \n(84<\n(38 .nr 84 \n(38 -.nr 38 \wlal -.if \n(84<\n(38 .nr 84 \n(38 -.nr 38 \wlde -.if \n(84<\n(38 .nr 84 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wldl -.if \n(84<\n(38 .nr 84 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wlil -.if \n(84<\n(38 .nr 84 \n(38 -.nr 38 \wlin -.if \n(84<\n(38 .nr 84 \n(38 -.nr 38 \wloc -.if \n(84<\n(38 .nr 84 \n(38 -.nr 38 \wloc -.if \n(84<\n(38 .nr 84 \n(38 -.nr 38 \wloe -.if \n(84<\n(38 .nr 84 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wlof -.if \n(84<\n(38 .nr 84 \n(38 -.nr 38 \wloi -.if \n(84<\n(38 .nr 84 \n(38 -.nr 38 \wlol -.if \n(84<\n(38 .nr 84 \n(38 -.nr 38 \wlol -.if \n(84<\n(38 .nr 84 \n(38 -.nr 38 \wlxl -.if \n(84<\n(38 .nr 84 \n(38 -.nr 38 \wrck -.if \n(84<\n(38 .nr 84 \n(38 -.nr 38 \wrmi -.if \n(84<\n(38 .nr 84 \n(38 -.nr 38 \wsbi -.if \n(84<\n(38 .nr 84 \n(38 -.nr 38 \wsil -.if \n(84<\n(38 .nr 84 \n(38 -.nr 38 \wste -.if \n(84<\n(38 .nr 84 \n(38 -.nr 38 \wstf -.if \n(84<\n(38 .nr 84 \n(38 -.nr 38 \wsti -.if \n(84<\n(38 .nr 84 \n(38 -.nr 38 \wstl -.if \n(84<\n(38 .nr 84 \n(38 -.nr 38 \wstl -.if \n(84<\n(38 .nr 84 \n(38 -.nr 38 \wtlt -.if \n(84<\n(38 .nr 84 \n(38 -.nr 38 \wzeq -.if \n(84<\n(38 .nr 84 \n(38 -.nr 38 \wzgt -.if \n(84<\n(38 .nr 84 \n(38 -.nr 38 \wzne -.if \n(84<\n(38 .nr 84 \n(38 -.nr 38 \wzre -.if \n(84<\n(38 .nr 84 \n(38 -.nr 38 \wzrl -.if \n(84<\n(38 .nr 84 \n(38 -.nr 38 \wadf -.if \n(84<\n(38 .nr 84 \n(38 -.nr 38 \wadi -.if \n(84<\n(38 .nr 84 \n(38 -.nr 38 \wadu -.if \n(84<\n(38 .nr 84 \n(38 -.nr 38 \wand -.if \n(84<\n(38 .nr 84 \n(38 -.nr 38 \wass -.if \n(84<\n(38 .nr 84 \n(38 -.nr 38 \wble -.if \n(84<\n(38 .nr 84 \n(38 -.nr 38 \wbls -.if \n(84<\n(38 .nr 84 \n(38 -.nr 38 \wcai -.if \n(84<\n(38 .nr 84 \n(38 -.nr 38 \wcfu -.if \n(84<\n(38 .nr 84 \n(38 -.nr 38 \wcmf -.if \n(84<\n(38 .nr 84 \n(38 -.nr 38 \wcms -.if \n(84<\n(38 .nr 84 \n(38 -.nr 38 \wcmu -.if \n(84<\n(38 .nr 84 \n(38 -.nr 38 \wcsa -.if \n(84<\n(38 .nr 84 \n(38 -.nr 38 \wcsb -.if \n(84<\n(38 .nr 84 \n(38 -.nr 38 \wcuu -.if \n(84<\n(38 .nr 84 \n(38 -.nr 38 \wdel -.if \n(84<\n(38 .nr 84 \n(38 -.nr 38 \wdus -.if \n(84<\n(38 .nr 84 \n(38 -.nr 38 \wdvi -.if \n(84<\n(38 .nr 84 \n(38 -.nr 38 \wdvu -.if \n(84<\n(38 .nr 84 \n(38 -.nr 38 \wfif -.if \n(84<\n(38 .nr 84 \n(38 -.nr 38 \winl -.if \n(84<\n(38 .nr 84 \n(38 -.nr 38 \wior -.if \n(84<\n(38 .nr 84 \n(38 -.nr 38 \wlar -.if \n(84<\n(38 .nr 84 \n(38 -.nr 38 \wldl -.if \n(84<\n(38 .nr 84 \n(38 -.nr 38 \wlil -.if \n(84<\n(38 .nr 84 \n(38 -.nr 38 \wlos -.if \n(84<\n(38 .nr 84 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wlpi -.if \n(84<\n(38 .nr 84 \n(38 -.nr 38 \wmlf -.if \n(84<\n(38 .nr 84 \n(38 -.nr 38 \wmli -.if \n(84<\n(38 .nr 84 \n(38 -.nr 38 \wmon -.if \n(84<\n(38 .nr 84 \n(38 -.nr 38 \wngi -.if \n(84<\n(38 .nr 84 \n(38 -.nr 38 \wrck -.if \n(84<\n(38 .nr 84 \n(38 -.nr 38 \wrmi -.if \n(84<\n(38 .nr 84 \n(38 -.nr 38 \wrmu -.if \n(84<\n(38 .nr 84 \n(38 -.nr 38 \wror -.if \n(84<\n(38 .nr 84 \n(38 -.nr 38 \wsar -.if \n(84<\n(38 .nr 84 \n(38 -.nr 38 \wsbf -.if \n(84<\n(38 .nr 84 \n(38 -.nr 38 \wsbs -.if \n(84<\n(38 .nr 84 \n(38 -.nr 38 \wsbu -.if \n(84<\n(38 .nr 84 \n(38 -.nr 38 \wsdl -.if \n(84<\n(38 .nr 84 \n(38 -.nr 38 \wset -.if \n(84<\n(38 .nr 84 \n(38 -.nr 38 \wsil -.if \n(84<\n(38 .nr 84 \n(38 -.nr 38 \wsli -.if \n(84<\n(38 .nr 84 \n(38 -.nr 38 \wsri -.if \n(84<\n(38 .nr 84 \n(38 -.nr 38 \wsru -.if \n(84<\n(38 .nr 84 \n(38 -.nr 38 \wsts -.if \n(84<\n(38 .nr 84 \n(38 -.nr 38 \wtle -.if \n(84<\n(38 .nr 84 \n(38 -.nr 38 \wxor -.if \n(84<\n(38 .nr 84 \n(38 -.nr 38 \wzge -.if \n(84<\n(38 .nr 84 \n(38 -.nr 38 \wzlt -.if \n(84<\n(38 .nr 84 \n(38 -.nr 38 \wzrf -.if \n(84<\n(38 .nr 84 \n(38 -.nr 38 \wexg -.if \n(84<\n(38 .nr 84 \n(38 -.nr 38 \wlpb -.if \n(84<\n(38 .nr 84 \n(38 -.84 -.rm 84 -.nr 38 6n -.if \n(84<\n(38 .nr 84 \n(38 -.nr 85 0 -.nr 38 \wsP -.if \n(85<\n(38 .nr 85 \n(38 -.nr 38 \wmPo -.if \n(85<\n(38 .nr 85 \n(38 -.nr 38 \wmwPo -.if \n(85<\n(38 .nr 85 \n(38 -.nr 38 \wswP -.if \n(85<\n(38 .nr 85 \n(38 -.nr 38 \wsP -.if \n(85<\n(38 .nr 85 \n(38 -.nr 38 \wsP -.if \n(85<\n(38 .nr 85 \n(38 -.nr 38 \w2 -.if \n(85<\n(38 .nr 85 \n(38 -.nr 38 \wmPo -.if \n(85<\n(38 .nr 85 \n(38 -.nr 38 \w- -.if \n(85<\n(38 .nr 85 \n(38 -.nr 38 \wmwPo -.if \n(85<\n(38 .nr 85 \n(38 -.nr 38 \wmwPo -.if \n(85<\n(38 .nr 85 \n(38 -.nr 38 \wsw -.if \n(85<\n(38 .nr 85 \n(38 -.nr 38 \wsP -.if \n(85<\n(38 .nr 85 \n(38 -.nr 38 \w- -.if \n(85<\n(38 .nr 85 \n(38 -.nr 38 \wmwN -.if \n(85<\n(38 .nr 85 \n(38 -.nr 38 \wmwPo -.if \n(85<\n(38 .nr 85 \n(38 -.nr 38 \wsw -.if \n(85<\n(38 .nr 85 \n(38 -.nr 38 \wm -.if \n(85<\n(38 .nr 85 \n(38 -.nr 38 \wswN -.if \n(85<\n(38 .nr 85 \n(38 -.nr 38 \ww2 -.if \n(85<\n(38 .nr 85 \n(38 -.nr 38 \wswN -.if \n(85<\n(38 .nr 85 \n(38 -.nr 38 \wswN -.if \n(85<\n(38 .nr 85 \n(38 -.nr 38 \w2 -.if \n(85<\n(38 .nr 85 \n(38 -.nr 38 \w2 -.if \n(85<\n(38 .nr 85 \n(38 -.nr 38 \wsP -.if \n(85<\n(38 .nr 85 \n(38 -.nr 38 \wsw -.if \n(85<\n(38 .nr 85 \n(38 -.nr 38 \wsP -.if \n(85<\n(38 .nr 85 \n(38 -.nr 38 \wmwPo -.if \n(85<\n(38 .nr 85 \n(38 -.nr 38 \wwN2 -.if \n(85<\n(38 .nr 85 \n(38 -.nr 38 \wswP -.if \n(85<\n(38 .nr 85 \n(38 -.nr 38 \wmPo -.if \n(85<\n(38 .nr 85 \n(38 -.nr 38 \wmwPo -.if \n(85<\n(38 .nr 85 \n(38 -.nr 38 \wmwPo -.if \n(85<\n(38 .nr 85 \n(38 -.nr 38 \wmwPo -.if \n(85<\n(38 .nr 85 \n(38 -.nr 38 \wswN -.if \n(85<\n(38 .nr 85 \n(38 -.nr 38 \ww2 -.if \n(85<\n(38 .nr 85 \n(38 -.nr 38 \wmwPo -.if \n(85<\n(38 .nr 85 \n(38 -.nr 38 \wmwPo -.if \n(85<\n(38 .nr 85 \n(38 -.nr 38 \wwN2 -.if \n(85<\n(38 .nr 85 \n(38 -.nr 38 \wswN -.if \n(85<\n(38 .nr 85 \n(38 -.nr 38 \w- -.if \n(85<\n(38 .nr 85 \n(38 -.nr 38 \wsP -.if \n(85<\n(38 .nr 85 \n(38 -.nr 38 \wsP -.if \n(85<\n(38 .nr 85 \n(38 -.nr 38 \wsP -.if \n(85<\n(38 .nr 85 \n(38 -.nr 38 \wsw -.if \n(85<\n(38 .nr 85 \n(38 -.nr 38 \wwN2 -.if \n(85<\n(38 .nr 85 \n(38 -.nr 38 \we2 -.if \n(85<\n(38 .nr 85 \n(38 -.nr 38 \we- -.if \n(85<\n(38 .nr 85 \n(38 -.nr 38 \we2 -.if \n(85<\n(38 .nr 85 \n(38 -.nr 38 \we- -.if \n(85<\n(38 .nr 85 \n(38 -.nr 38 \we- -.if \n(85<\n(38 .nr 85 \n(38 -.nr 38 \we2 -.if \n(85<\n(38 .nr 85 \n(38 -.nr 38 \we- -.if \n(85<\n(38 .nr 85 \n(38 -.nr 38 \we- -.if \n(85<\n(38 .nr 85 \n(38 -.nr 38 \we- -.if \n(85<\n(38 .nr 85 \n(38 -.nr 38 \we- -.if \n(85<\n(38 .nr 85 \n(38 -.nr 38 \we2 -.if \n(85<\n(38 .nr 85 \n(38 -.nr 38 \we- -.if \n(85<\n(38 .nr 85 \n(38 -.nr 38 \we2 -.if \n(85<\n(38 .nr 85 \n(38 -.nr 38 \we- -.if \n(85<\n(38 .nr 85 \n(38 -.nr 38 \we- -.if \n(85<\n(38 .nr 85 \n(38 -.nr 38 \wewN2 -.if \n(85<\n(38 .nr 85 \n(38 -.nr 38 \we- -.if \n(85<\n(38 .nr 85 \n(38 -.nr 38 \we2 -.if \n(85<\n(38 .nr 85 \n(38 -.nr 38 \we- -.if \n(85<\n(38 .nr 85 \n(38 -.nr 38 \we2 -.if \n(85<\n(38 .nr 85 \n(38 -.nr 38 \wewN2 -.if \n(85<\n(38 .nr 85 \n(38 -.nr 38 \we2 -.if \n(85<\n(38 .nr 85 \n(38 -.nr 38 \we- -.if \n(85<\n(38 .nr 85 \n(38 -.nr 38 \wewP2 -.if \n(85<\n(38 .nr 85 \n(38 -.nr 38 \wewP2 -.if \n(85<\n(38 .nr 85 \n(38 -.nr 38 \we2 -.if \n(85<\n(38 .nr 85 \n(38 -.nr 38 \we2 -.if \n(85<\n(38 .nr 85 \n(38 -.nr 38 \we2 -.if \n(85<\n(38 .nr 85 \n(38 -.nr 38 \we- -.if \n(85<\n(38 .nr 85 \n(38 -.nr 38 \we- -.if \n(85<\n(38 .nr 85 \n(38 -.nr 38 \we2 -.if \n(85<\n(38 .nr 85 \n(38 -.nr 38 \we2 -.if \n(85<\n(38 .nr 85 \n(38 -.nr 38 \we2 -.if \n(85<\n(38 .nr 85 \n(38 -.nr 38 \we- -.if \n(85<\n(38 .nr 85 \n(38 -.nr 38 \we2 -.if \n(85<\n(38 .nr 85 \n(38 -.nr 38 \we2 -.if \n(85<\n(38 .nr 85 \n(38 -.nr 38 \we- -.if \n(85<\n(38 .nr 85 \n(38 -.nr 38 \we2 -.if \n(85<\n(38 .nr 85 \n(38 -.nr 38 \we- -.if \n(85<\n(38 .nr 85 \n(38 -.nr 38 \wewP2 -.if \n(85<\n(38 .nr 85 \n(38 -.nr 38 \we- -.if \n(85<\n(38 .nr 85 \n(38 -.nr 38 \wewN2 -.if \n(85<\n(38 .nr 85 \n(38 -.nr 38 \we- -.if \n(85<\n(38 .nr 85 \n(38 -.nr 38 \we2 -.if \n(85<\n(38 .nr 85 \n(38 -.nr 38 \we- -.if \n(85<\n(38 .nr 85 \n(38 -.nr 38 \we- -.if \n(85<\n(38 .nr 85 \n(38 -.nr 38 \we- -.if \n(85<\n(38 .nr 85 \n(38 -.nr 38 \we- -.if \n(85<\n(38 .nr 85 \n(38 -.nr 38 \we2 -.if \n(85<\n(38 .nr 85 \n(38 -.nr 38 \we2 -.if \n(85<\n(38 .nr 85 \n(38 -.nr 38 \we- -.if \n(85<\n(38 .nr 85 \n(38 -.nr 38 \wesP -.if \n(85<\n(38 .nr 85 \n(38 -.nr 38 \we- -.if \n(85<\n(38 .nr 85 \n(38 -.85 -.rm 85 -.nr 38 8n -.if \n(85<\n(38 .nr 85 \n(38 -.nr 86 0 -.nr 38 \w1 -.if \n(86<\n(38 .nr 86 \n(38 -.nr 38 \w2 -.if \n(86<\n(38 .nr 86 \n(38 -.nr 38 \w1 -.if \n(86<\n(38 .nr 86 \n(38 -.nr 38 \w1 -.if \n(86<\n(38 .nr 86 \n(38 -.nr 38 \w1 -.if \n(86<\n(38 .nr 86 \n(38 -.nr 38 \w1 -.if \n(86<\n(38 .nr 86 \n(38 -.nr 38 \w28 -.if \n(86<\n(38 .nr 86 \n(38 -.nr 38 \w2 -.if \n(86<\n(38 .nr 86 \n(38 -.nr 38 \w1 -.if \n(86<\n(38 .nr 86 \n(38 -.nr 38 \w1 -.if \n(86<\n(38 .nr 86 \n(38 -.nr 38 \w1 -.if \n(86<\n(38 .nr 86 \n(38 -.nr 38 \w3 -.if \n(86<\n(38 .nr 86 \n(38 -.nr 38 \w1 -.if \n(86<\n(38 .nr 86 \n(38 -.nr 38 \w7 -.if \n(86<\n(38 .nr 86 \n(38 -.nr 38 \w1 -.if \n(86<\n(38 .nr 86 \n(38 -.nr 38 \w2 -.if \n(86<\n(38 .nr 86 \n(38 -.nr 38 \w1 -.if \n(86<\n(38 .nr 86 \n(38 -.nr 38 \w1 -.if \n(86<\n(38 .nr 86 \n(38 -.nr 38 \w1 -.if \n(86<\n(38 .nr 86 \n(38 -.nr 38 \w5 -.if \n(86<\n(38 .nr 86 \n(38 -.nr 38 \w1 -.if \n(86<\n(38 .nr 86 \n(38 -.nr 38 \w4 -.if \n(86<\n(38 .nr 86 \n(38 -.nr 38 \w1 -.if \n(86<\n(38 .nr 86 \n(38 -.nr 38 \w2 -.if \n(86<\n(38 .nr 86 \n(38 -.nr 38 \w1 -.if \n(86<\n(38 .nr 86 \n(38 -.nr 38 \w1 -.if \n(86<\n(38 .nr 86 \n(38 -.nr 38 \w2 -.if \n(86<\n(38 .nr 86 \n(38 -.nr 38 \w1 -.if \n(86<\n(38 .nr 86 \n(38 -.nr 38 \w2 -.if \n(86<\n(38 .nr 86 \n(38 -.nr 38 \w4 -.if \n(86<\n(38 .nr 86 \n(38 -.nr 38 \w1 -.if \n(86<\n(38 .nr 86 \n(38 -.nr 38 \w2 -.if \n(86<\n(38 .nr 86 \n(38 -.nr 38 \w1 -.if \n(86<\n(38 .nr 86 \n(38 -.nr 38 \w1 -.if \n(86<\n(38 .nr 86 \n(38 -.nr 38 \w1 -.if \n(86<\n(38 .nr 86 \n(38 -.nr 38 \w1 -.if \n(86<\n(38 .nr 86 \n(38 -.86 -.rm 86 -.nr 38 3n -.if \n(86<\n(38 .nr 86 \n(38 -.nr 87 0 -.nr 38 \w35 -.if \n(87<\n(38 .nr 87 \n(38 -.nr 38 \w39 -.if \n(87<\n(38 .nr 87 \n(38 -.nr 38 \w43 -.if \n(87<\n(38 .nr 87 \n(38 -.nr 38 \w50 -.if \n(87<\n(38 .nr 87 \n(38 -.nr 38 \w53 -.if \n(87<\n(38 .nr 87 \n(38 -.nr 38 \w56 -.if \n(87<\n(38 .nr 87 \n(38 -.nr 38 \w59 -.if \n(87<\n(38 .nr 87 \n(38 -.nr 38 \w64 -.if \n(87<\n(38 .nr 87 \n(38 -.nr 38 \w94 -.if \n(87<\n(38 .nr 87 \n(38 -.nr 38 \w97 -.if \n(87<\n(38 .nr 87 \n(38 -.nr 38 \w101 -.if \n(87<\n(38 .nr 87 \n(38 -.nr 38 \w104 -.if \n(87<\n(38 .nr 87 \n(38 -.nr 38 \w107 -.if \n(87<\n(38 .nr 87 \n(38 -.nr 38 \w110 -.if \n(87<\n(38 .nr 87 \n(38 -.nr 38 \w113 -.if \n(87<\n(38 .nr 87 \n(38 -.nr 38 \w118 -.if \n(87<\n(38 .nr 87 \n(38 -.nr 38 \w121 -.if \n(87<\n(38 .nr 87 \n(38 -.nr 38 \w130 -.if \n(87<\n(38 .nr 87 \n(38 -.nr 38 \w133 -.if \n(87<\n(38 .nr 87 \n(38 -.nr 38 \w137 -.if \n(87<\n(38 .nr 87 \n(38 -.nr 38 \w140 -.if \n(87<\n(38 .nr 87 \n(38 -.nr 38 \w144 -.if \n(87<\n(38 .nr 87 \n(38 -.nr 38 \w148 -.if \n(87<\n(38 .nr 87 \n(38 -.nr 38 \w151 -.if \n(87<\n(38 .nr 87 \n(38 -.nr 38 \w153 -.if \n(87<\n(38 .nr 87 \n(38 -.nr 38 \w156 -.if \n(87<\n(38 .nr 87 \n(38 -.nr 38 \w166 -.if \n(87<\n(38 .nr 87 \n(38 -.nr 38 \w169 -.if \n(87<\n(38 .nr 87 \n(38 -.nr 38 \w175 -.if \n(87<\n(38 .nr 87 \n(38 -.nr 38 \w188 -.if \n(87<\n(38 .nr 87 \n(38 -.nr 38 \w191 -.if \n(87<\n(38 .nr 87 \n(38 -.nr 38 \w196 -.if \n(87<\n(38 .nr 87 \n(38 -.nr 38 \w200 -.if \n(87<\n(38 .nr 87 \n(38 -.nr 38 \w203 -.if \n(87<\n(38 .nr 87 \n(38 -.nr 38 \w207 -.if \n(87<\n(38 .nr 87 \n(38 -.nr 38 \w210 -.if \n(87<\n(38 .nr 87 \n(38 -.nr 38 \w215 -.if \n(87<\n(38 .nr 87 \n(38 -.nr 38 \w219 -.if \n(87<\n(38 .nr 87 \n(38 -.nr 38 \w225 -.if \n(87<\n(38 .nr 87 \n(38 -.nr 38 \w233 -.if \n(87<\n(38 .nr 87 \n(38 -.nr 38 \w236 -.if \n(87<\n(38 .nr 87 \n(38 -.nr 38 \w239 -.if \n(87<\n(38 .nr 87 \n(38 -.nr 38 \w243 -.if \n(87<\n(38 .nr 87 \n(38 -.nr 38 \w246 -.if \n(87<\n(38 .nr 87 \n(38 -.nr 38 \w249 -.if \n(87<\n(38 .nr 87 \n(38 -.nr 38 \w253 -.if \n(87<\n(38 .nr 87 \n(38 -.nr 38 \w2 -.if \n(87<\n(38 .nr 87 \n(38 -.nr 38 \w5 -.if \n(87<\n(38 .nr 87 \n(38 -.nr 38 \w8 -.if \n(87<\n(38 .nr 87 \n(38 -.nr 38 \w11 -.if \n(87<\n(38 .nr 87 \n(38 -.nr 38 \w14 -.if \n(87<\n(38 .nr 87 \n(38 -.nr 38 \w17 -.if \n(87<\n(38 .nr 87 \n(38 -.nr 38 \w20 -.if \n(87<\n(38 .nr 87 \n(38 -.nr 38 \w23 -.if \n(87<\n(38 .nr 87 \n(38 -.nr 38 \w26 -.if \n(87<\n(38 .nr 87 \n(38 -.nr 38 \w29 -.if \n(87<\n(38 .nr 87 \n(38 -.nr 38 \w32 -.if \n(87<\n(38 .nr 87 \n(38 -.nr 38 \w35 -.if \n(87<\n(38 .nr 87 \n(38 -.nr 38 \w38 -.if \n(87<\n(38 .nr 87 \n(38 -.nr 38 \w41 -.if \n(87<\n(38 .nr 87 \n(38 -.nr 38 \w44 -.if \n(87<\n(38 .nr 87 \n(38 -.nr 38 \w47 -.if \n(87<\n(38 .nr 87 \n(38 -.nr 38 \w50 -.if \n(87<\n(38 .nr 87 \n(38 -.nr 38 \w53 -.if \n(87<\n(38 .nr 87 \n(38 -.nr 38 \w56 -.if \n(87<\n(38 .nr 87 \n(38 -.nr 38 \w59 -.if \n(87<\n(38 .nr 87 \n(38 -.nr 38 \w62 -.if \n(87<\n(38 .nr 87 \n(38 -.nr 38 \w65 -.if \n(87<\n(38 .nr 87 \n(38 -.nr 38 \w68 -.if \n(87<\n(38 .nr 87 \n(38 -.nr 38 \w71 -.if \n(87<\n(38 .nr 87 \n(38 -.nr 38 \w74 -.if \n(87<\n(38 .nr 87 \n(38 -.nr 38 \w77 -.if \n(87<\n(38 .nr 87 \n(38 -.nr 38 \w80 -.if \n(87<\n(38 .nr 87 \n(38 -.nr 38 \w83 -.if \n(87<\n(38 .nr 87 \n(38 -.nr 38 \w86 -.if \n(87<\n(38 .nr 87 \n(38 -.nr 38 \w89 -.if \n(87<\n(38 .nr 87 \n(38 -.nr 38 \w92 -.if \n(87<\n(38 .nr 87 \n(38 -.nr 38 \w95 -.if \n(87<\n(38 .nr 87 \n(38 -.nr 38 \w98 -.if \n(87<\n(38 .nr 87 \n(38 -.nr 38 \w101 -.if \n(87<\n(38 .nr 87 \n(38 -.nr 38 \w104 -.if \n(87<\n(38 .nr 87 \n(38 -.nr 38 \w107 -.if \n(87<\n(38 .nr 87 \n(38 -.nr 38 \w110 -.if \n(87<\n(38 .nr 87 \n(38 -.nr 38 \w113 -.if \n(87<\n(38 .nr 87 \n(38 -.nr 38 \w116 -.if \n(87<\n(38 .nr 87 \n(38 -.nr 38 \w119 -.if \n(87<\n(38 .nr 87 \n(38 -.nr 38 \w122 -.if \n(87<\n(38 .nr 87 \n(38 -.nr 38 \w125 -.if \n(87<\n(38 .nr 87 \n(38 -.nr 38 \w128 -.if \n(87<\n(38 .nr 87 \n(38 -.nr 38 \w131 -.if \n(87<\n(38 .nr 87 \n(38 -.nr 38 \w134 -.if \n(87<\n(38 .nr 87 \n(38 -.nr 38 \w137 -.if \n(87<\n(38 .nr 87 \n(38 -.nr 38 \w140 -.if \n(87<\n(38 .nr 87 \n(38 -.nr 38 \w143 -.if \n(87<\n(38 .nr 87 \n(38 -.nr 38 \w146 -.if \n(87<\n(38 .nr 87 \n(38 -.nr 38 \w149 -.if \n(87<\n(38 .nr 87 \n(38 -.nr 38 \w152 -.if \n(87<\n(38 .nr 87 \n(38 -.nr 38 \w155 -.if \n(87<\n(38 .nr 87 \n(38 -.nr 38 \w158 -.if \n(87<\n(38 .nr 87 \n(38 -.87 -.rm 87 -.nr 38 6n -.if \n(87<\n(38 .nr 87 \n(38 -.nr 88 0 -.nr 38 \wadi -.if \n(88<\n(38 .nr 88 \n(38 -.nr 38 \wadp -.if \n(88<\n(38 .nr 88 \n(38 -.nr 38 \wand -.if \n(88<\n(38 .nr 88 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wbeq -.if \n(88<\n(38 .nr 88 \n(38 -.nr 38 \wbgt -.if \n(88<\n(38 .nr 88 \n(38 -.nr 38 \wblt -.if \n(88<\n(38 .nr 88 \n(38 -.nr 38 \wbra -.if \n(88<\n(38 .nr 88 \n(38 -.nr 38 \wcal -.if \n(88<\n(38 .nr 88 \n(38 -.nr 38 \wcii -.if \n(88<\n(38 .nr 88 \n(38 -.nr 38 \wcmp -.if \n(88<\n(38 .nr 88 \n(38 -.nr 38 \wcsb -.if \n(88<\n(38 .nr 88 \n(38 -.nr 38 \wdel -.if \n(88<\n(38 .nr 88 \n(38 -.nr 38 \wdvi -.if \n(88<\n(38 .nr 88 \n(38 -.nr 38 \wine -.if \n(88<\n(38 .nr 88 \n(38 -.nr 38 \winl -.if \n(88<\n(38 .nr 88 \n(38 -.nr 38 \wior -.if \n(88<\n(38 .nr 88 \n(38 -.nr 38 \wlal -.if \n(88<\n(38 .nr 88 \n(38 -.nr 38 \wlal -.if \n(88<\n(38 .nr 88 \n(38 -.nr 38 \wlar -.if \n(88<\n(38 .nr 88 \n(38 -.nr 38 \wlde -.if \n(88<\n(38 .nr 88 \n(38 -.nr 38 \wlfr -.if \n(88<\n(38 .nr 88 \n(38 -.nr 38 \wlil -.if \n(88<\n(38 .nr 88 \n(38 -.nr 38 \wlin -.if \n(88<\n(38 .nr 88 \n(38 -.nr 38 \wloc -.if \n(88<\n(38 .nr 88 \n(38 -.nr 38 \wloc -.if \n(88<\n(38 .nr 88 \n(38 -.nr 38 \wlof -.if \n(88<\n(38 .nr 88 \n(38 -.nr 38 \wloi -.if \n(88<\n(38 .nr 88 \n(38 -.nr 38 \wloi -.if \n(88<\n(38 .nr 88 \n(38 -.nr 38 \wlol -.if \n(88<\n(38 .nr 88 \n(38 -.nr 38 \wlol -.if \n(88<\n(38 .nr 88 \n(38 -.nr 38 \wmlf -.if \n(88<\n(38 .nr 88 \n(38 -.nr 38 \wret -.if \n(88<\n(38 .nr 88 \n(38 -.nr 38 \wsar -.if \n(88<\n(38 .nr 88 \n(38 -.nr 38 \wsdl -.if \n(88<\n(38 .nr 88 \n(38 -.nr 38 \wsil -.if \n(88<\n(38 .nr 88 \n(38 -.nr 38 \wste -.if \n(88<\n(38 .nr 88 \n(38 -.nr 38 \wstf -.if \n(88<\n(38 .nr 88 \n(38 -.nr 38 \wsti -.if \n(88<\n(38 .nr 88 \n(38 -.nr 38 \wstl -.if \n(88<\n(38 .nr 88 \n(38 -.nr 38 \wteq -.if \n(88<\n(38 .nr 88 \n(38 -.nr 38 \wtne -.if \n(88<\n(38 .nr 88 \n(38 -.nr 38 \wzer -.if \n(88<\n(38 .nr 88 \n(38 -.nr 38 \wzle -.if \n(88<\n(38 .nr 88 \n(38 -.nr 38 \wzne -.if \n(88<\n(38 .nr 88 \n(38 -.nr 38 \wzrl -.if \n(88<\n(38 .nr 88 \n(38 -.nr 38 \waar -.if \n(88<\n(38 .nr 88 \n(38 -.nr 38 \wadf -.if \n(88<\n(38 .nr 88 \n(38 -.nr 38 \wads -.if \n(88<\n(38 .nr 88 \n(38 -.nr 38 \wadu -.if \n(88<\n(38 .nr 88 \n(38 -.nr 38 \wasp -.if \n(88<\n(38 .nr 88 \n(38 -.nr 38 \wbge -.if \n(88<\n(38 .nr 88 \n(38 -.nr 38 \wblm -.if \n(88<\n(38 .nr 88 \n(38 -.nr 38 \wblt -.if \n(88<\n(38 .nr 88 \n(38 -.nr 38 \wcal -.if \n(88<\n(38 .nr 88 \n(38 -.nr 38 \wciu -.if \n(88<\n(38 .nr 88 \n(38 -.nr 38 \wcmi -.if \n(88<\n(38 .nr 88 \n(38 -.nr 38 \wcms -.if \n(88<\n(38 .nr 88 \n(38 -.nr 38 \wcom -.if \n(88<\n(38 .nr 88 \n(38 -.nr 38 \wcsa -.if \n(88<\n(38 .nr 88 \n(38 -.nr 38 \wcuf -.if \n(88<\n(38 .nr 88 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 +.nr 38 \wble +.if \n(66<\n(38 .nr 66 \n(38 +.nr 38 \wbne +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wdee -.if \n(88<\n(38 .nr 88 \n(38 -.nr 38 \wdup -.if \n(88<\n(38 .nr 88 \n(38 -.nr 38 \wdvf -.if \n(88<\n(38 .nr 88 \n(38 -.nr 38 \wdvi -.if \n(88<\n(38 .nr 88 \n(38 -.nr 38 \wfef -.if \n(88<\n(38 .nr 88 \n(38 -.nr 38 \wfif -.if \n(88<\n(38 .nr 88 \n(38 -.nr 38 \winn -.if \n(88<\n(38 .nr 88 \n(38 -.nr 38 \wior -.if \n(88<\n(38 .nr 88 \n(38 -.nr 38 \wldc -.if \n(88<\n(38 .nr 88 \n(38 -.nr 38 \wldl -.if \n(88<\n(38 .nr 88 \n(38 -.nr 38 \wlil -.if \n(88<\n(38 .nr 88 \n(38 -.nr 38 \wlos -.if \n(88<\n(38 .nr 88 \n(38 -.nr 38 \wlxa -.if \n(88<\n(38 .nr 88 \n(38 -.nr 38 \wmlf -.if \n(88<\n(38 .nr 88 \n(38 -.nr 38 \wmlu -.if \n(88<\n(38 .nr 88 \n(38 -.nr 38 \wngf -.if \n(88<\n(38 .nr 88 \n(38 -.nr 38 \wngi -.if \n(88<\n(38 .nr 88 \n(38 -.nr 38 \wrck -.if \n(88<\n(38 .nr 88 \n(38 -.nr 38 \wrmi -.if \n(88<\n(38 .nr 88 \n(38 -.nr 38 \wrol -.if \n(88<\n(38 .nr 88 \n(38 -.nr 38 \wror -.if \n(88<\n(38 .nr 88 \n(38 -.nr 38 \wsar -.if \n(88<\n(38 .nr 88 \n(38 -.nr 38 \wsbi -.if \n(88<\n(38 .nr 88 \n(38 -.nr 38 \wsbs -.if \n(88<\n(38 .nr 88 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 +.nr 38 \wfil +.if \n(66<\n(38 .nr 66 \n(38 +.nr 38 \winl +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wsde -.if \n(88<\n(38 .nr 88 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wsdl -.if \n(88<\n(38 .nr 88 \n(38 -.nr 38 \wsig -.if \n(88<\n(38 .nr 88 \n(38 -.nr 38 \wsim -.if \n(88<\n(38 .nr 88 \n(38 -.nr 38 \wslu -.if \n(88<\n(38 .nr 88 \n(38 -.nr 38 \wsri -.if \n(88<\n(38 .nr 88 \n(38 -.nr 38 \wsti -.if \n(88<\n(38 .nr 88 \n(38 -.nr 38 \wstr -.if \n(88<\n(38 .nr 88 \n(38 -.nr 38 \wtrp -.if \n(88<\n(38 .nr 88 \n(38 -.nr 38 \wzer -.if \n(88<\n(38 .nr 88 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 +.nr 38 \wste +.if \n(66<\n(38 .nr 66 \n(38 +.nr 38 \wstl +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wzgt -.if \n(88<\n(38 .nr 88 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wzne -.if \n(88<\n(38 .nr 88 \n(38 +.if \n(66<\n(38 .nr 66 \n(38 .nr 38 \wzrl -.if \n(88<\n(38 .nr 88 \n(38 -.nr 38 \wexg -.if \n(88<\n(38 .nr 88 \n(38 -.nr 38 \wgto -.if \n(88<\n(38 .nr 88 \n(38 -.88 -.rm 88 -.nr 38 6n -.if \n(88<\n(38 .nr 88 \n(38 -.nr 89 0 +.if \n(66<\n(38 .nr 66 \n(38 +.66 +.rm 66 +.nr 38 4n +.if \n(66<\n(38 .nr 66 \n(38 +.nr 67 0 .nr 38 \wmwPo -.if \n(89<\n(38 .nr 89 \n(38 -.nr 38 \wsP -.if \n(89<\n(38 .nr 89 \n(38 -.nr 38 \wmwPo -.if \n(89<\n(38 .nr 89 \n(38 +.if \n(67<\n(38 .nr 67 \n(38 .nr 38 \w2 -.if \n(89<\n(38 .nr 89 \n(38 -.nr 38 \wsP -.if \n(89<\n(38 .nr 89 \n(38 -.nr 38 \wsP -.if \n(89<\n(38 .nr 89 \n(38 +.if \n(67<\n(38 .nr 67 \n(38 .nr 38 \wsN -.if \n(89<\n(38 .nr 89 \n(38 +.if \n(67<\n(38 .nr 67 \n(38 +.nr 38 \wmwPo +.if \n(67<\n(38 .nr 67 \n(38 .nr 38 \wsP -.if \n(89<\n(38 .nr 89 \n(38 -.nr 38 \w- -.if \n(89<\n(38 .nr 89 \n(38 -.nr 38 \w- -.if \n(89<\n(38 .nr 89 \n(38 -.nr 38 \wmwPo -.if \n(89<\n(38 .nr 89 \n(38 -.nr 38 \wswN -.if \n(89<\n(38 .nr 89 \n(38 -.nr 38 \wmwPo -.if \n(89<\n(38 .nr 89 \n(38 -.nr 38 \ww2 -.if \n(89<\n(38 .nr 89 \n(38 -.nr 38 \wswN -.if \n(89<\n(38 .nr 89 \n(38 +.if \n(67<\n(38 .nr 67 \n(38 .nr 38 \wsP -.if \n(89<\n(38 .nr 89 \n(38 -.nr 38 \wP2 -.if \n(89<\n(38 .nr 89 \n(38 -.nr 38 \wmN -.if \n(89<\n(38 .nr 89 \n(38 +.if \n(67<\n(38 .nr 67 \n(38 +.nr 38 \wsP +.if \n(67<\n(38 .nr 67 \n(38 +.nr 38 \wsP +.if \n(67<\n(38 .nr 67 \n(38 +.nr 38 \w- +.if \n(67<\n(38 .nr 67 \n(38 +.nr 38 \wsP +.if \n(67<\n(38 .nr 67 \n(38 +.nr 38 \wsP +.if \n(67<\n(38 .nr 67 \n(38 +.nr 38 \w- +.if \n(67<\n(38 .nr 67 \n(38 .nr 38 \wmwPo -.if \n(89<\n(38 .nr 89 \n(38 +.if \n(67<\n(38 .nr 67 \n(38 +.nr 38 \w2 +.if \n(67<\n(38 .nr 67 \n(38 .nr 38 \wsw -.if \n(89<\n(38 .nr 89 \n(38 -.nr 38 \wmwPo -.if \n(89<\n(38 .nr 89 \n(38 -.nr 38 \wswP -.if \n(89<\n(38 .nr 89 \n(38 +.if \n(67<\n(38 .nr 67 \n(38 .nr 38 \wsP -.if \n(89<\n(38 .nr 89 \n(38 +.if \n(67<\n(38 .nr 67 \n(38 +.nr 38 \w2 +.if \n(67<\n(38 .nr 67 \n(38 +.nr 38 \wN2 +.if \n(67<\n(38 .nr 67 \n(38 +.nr 38 \wswP +.if \n(67<\n(38 .nr 67 \n(38 .nr 38 \wmP -.if \n(89<\n(38 .nr 89 \n(38 -.nr 38 \wsN -.if \n(89<\n(38 .nr 89 \n(38 -.nr 38 \w2 -.if \n(89<\n(38 .nr 89 \n(38 -.nr 38 \w2 -.if \n(89<\n(38 .nr 89 \n(38 +.if \n(67<\n(38 .nr 67 \n(38 +.nr 38 \wmP +.if \n(67<\n(38 .nr 67 \n(38 .nr 38 \wsP -.if \n(89<\n(38 .nr 89 \n(38 +.if \n(67<\n(38 .nr 67 \n(38 .nr 38 \wmwP -.if \n(89<\n(38 .nr 89 \n(38 -.nr 38 \wswN -.if \n(89<\n(38 .nr 89 \n(38 -.nr 38 \wsP -.if \n(89<\n(38 .nr 89 \n(38 -.nr 38 \wmwP -.if \n(89<\n(38 .nr 89 \n(38 +.if \n(67<\n(38 .nr 67 \n(38 +.nr 38 \w- +.if \n(67<\n(38 .nr 67 \n(38 +.nr 38 \wmN +.if \n(67<\n(38 .nr 67 \n(38 +.nr 38 \ww2 +.if \n(67<\n(38 .nr 67 \n(38 .nr 38 \wmwPo -.if \n(89<\n(38 .nr 89 \n(38 -.nr 38 \wswN -.if \n(89<\n(38 .nr 89 \n(38 -.nr 38 \wswP -.if \n(89<\n(38 .nr 89 \n(38 -.nr 38 \wsw -.if \n(89<\n(38 .nr 89 \n(38 -.nr 38 \wsP -.if \n(89<\n(38 .nr 89 \n(38 -.nr 38 \wsP -.if \n(89<\n(38 .nr 89 \n(38 -.nr 38 \wmwP -.if \n(89<\n(38 .nr 89 \n(38 -.nr 38 \w- -.if \n(89<\n(38 .nr 89 \n(38 -.nr 38 \w- -.if \n(89<\n(38 .nr 89 \n(38 -.nr 38 \wsP -.if \n(89<\n(38 .nr 89 \n(38 -.nr 38 \wsP -.if \n(89<\n(38 .nr 89 \n(38 -.nr 38 \wsN -.if \n(89<\n(38 .nr 89 \n(38 +.if \n(67<\n(38 .nr 67 \n(38 +.nr 38 \wmPo +.if \n(67<\n(38 .nr 67 \n(38 +.nr 38 \wwP2 +.if \n(67<\n(38 .nr 67 \n(38 .nr 38 \wmwN -.if \n(89<\n(38 .nr 89 \n(38 -.nr 38 \we2 -.if \n(89<\n(38 .nr 89 \n(38 +.if \n(67<\n(38 .nr 67 \n(38 +.nr 38 \wmPo +.if \n(67<\n(38 .nr 67 \n(38 +.nr 38 \wmwPo +.if \n(67<\n(38 .nr 67 \n(38 +.nr 38 \wsP +.if \n(67<\n(38 .nr 67 \n(38 +.nr 38 \wsP +.if \n(67<\n(38 .nr 67 \n(38 +.nr 38 \wsP +.if \n(67<\n(38 .nr 67 \n(38 +.nr 38 \wmwPo +.if \n(67<\n(38 .nr 67 \n(38 +.nr 38 \w2 +.if \n(67<\n(38 .nr 67 \n(38 +.nr 38 \wmPo +.if \n(67<\n(38 .nr 67 \n(38 +.nr 38 \wwP2 +.if \n(67<\n(38 .nr 67 \n(38 +.nr 38 \wmwN +.if \n(67<\n(38 .nr 67 \n(38 +.nr 38 \w- +.if \n(67<\n(38 .nr 67 \n(38 +.nr 38 \w2 +.if \n(67<\n(38 .nr 67 \n(38 +.nr 38 \wsP +.if \n(67<\n(38 .nr 67 \n(38 +.nr 38 \wsP +.if \n(67<\n(38 .nr 67 \n(38 +.nr 38 \ww2 +.if \n(67<\n(38 .nr 67 \n(38 +.nr 38 \wswN +.if \n(67<\n(38 .nr 67 \n(38 .nr 38 \we- -.if \n(89<\n(38 .nr 89 \n(38 +.if \n(67<\n(38 .nr 67 \n(38 .nr 38 \we2 -.if \n(89<\n(38 .nr 89 \n(38 +.if \n(67<\n(38 .nr 67 \n(38 .nr 38 \we- -.if \n(89<\n(38 .nr 89 \n(38 -.nr 38 \wew2 -.if \n(89<\n(38 .nr 89 \n(38 +.if \n(67<\n(38 .nr 67 \n(38 .nr 38 \we2 -.if \n(89<\n(38 .nr 89 \n(38 +.if \n(67<\n(38 .nr 67 \n(38 .nr 38 \we2 -.if \n(89<\n(38 .nr 89 \n(38 +.if \n(67<\n(38 .nr 67 \n(38 .nr 38 \we2 -.if \n(89<\n(38 .nr 89 \n(38 +.if \n(67<\n(38 .nr 67 \n(38 .nr 38 \we2 -.if \n(89<\n(38 .nr 89 \n(38 +.if \n(67<\n(38 .nr 67 \n(38 +.nr 38 \we2 +.if \n(67<\n(38 .nr 67 \n(38 .nr 38 \we- -.if \n(89<\n(38 .nr 89 \n(38 +.if \n(67<\n(38 .nr 67 \n(38 .nr 38 \we2 -.if \n(89<\n(38 .nr 89 \n(38 +.if \n(67<\n(38 .nr 67 \n(38 .nr 38 \we- -.if \n(89<\n(38 .nr 89 \n(38 +.if \n(67<\n(38 .nr 67 \n(38 .nr 38 \we2 -.if \n(89<\n(38 .nr 89 \n(38 +.if \n(67<\n(38 .nr 67 \n(38 .nr 38 \we- -.if \n(89<\n(38 .nr 89 \n(38 -.nr 38 \we- -.if \n(89<\n(38 .nr 89 \n(38 -.nr 38 \wew2 -.if \n(89<\n(38 .nr 89 \n(38 +.if \n(67<\n(38 .nr 67 \n(38 .nr 38 \we2 -.if \n(89<\n(38 .nr 89 \n(38 -.nr 38 \we2 -.if \n(89<\n(38 .nr 89 \n(38 +.if \n(67<\n(38 .nr 67 \n(38 .nr 38 \we- -.if \n(89<\n(38 .nr 89 \n(38 -.nr 38 \we2 -.if \n(89<\n(38 .nr 89 \n(38 -.nr 38 \we- -.if \n(89<\n(38 .nr 89 \n(38 -.nr 38 \we2 -.if \n(89<\n(38 .nr 89 \n(38 -.nr 38 \we- -.if \n(89<\n(38 .nr 89 \n(38 -.nr 38 \we2 -.if \n(89<\n(38 .nr 89 \n(38 -.nr 38 \wewN2 -.if \n(89<\n(38 .nr 89 \n(38 -.nr 38 \wewN2 -.if \n(89<\n(38 .nr 89 \n(38 -.nr 38 \we- -.if \n(89<\n(38 .nr 89 \n(38 -.nr 38 \we2 -.if \n(89<\n(38 .nr 89 \n(38 -.nr 38 \we- -.if \n(89<\n(38 .nr 89 \n(38 -.nr 38 \we2 -.if \n(89<\n(38 .nr 89 \n(38 -.nr 38 \we2 -.if \n(89<\n(38 .nr 89 \n(38 -.nr 38 \we- -.if \n(89<\n(38 .nr 89 \n(38 -.nr 38 \we- -.if \n(89<\n(38 .nr 89 \n(38 -.nr 38 \we- -.if \n(89<\n(38 .nr 89 \n(38 -.nr 38 \we2 -.if \n(89<\n(38 .nr 89 \n(38 -.nr 38 \we- -.if \n(89<\n(38 .nr 89 \n(38 -.nr 38 \we- -.if \n(89<\n(38 .nr 89 \n(38 -.nr 38 \we2 -.if \n(89<\n(38 .nr 89 \n(38 -.nr 38 \we- -.if \n(89<\n(38 .nr 89 \n(38 -.nr 38 \we2 -.if \n(89<\n(38 .nr 89 \n(38 -.nr 38 \wewN2 -.if \n(89<\n(38 .nr 89 \n(38 -.nr 38 \we- -.if \n(89<\n(38 .nr 89 \n(38 -.nr 38 \we- -.if \n(89<\n(38 .nr 89 \n(38 -.nr 38 \we2 -.if \n(89<\n(38 .nr 89 \n(38 -.nr 38 \we- -.if \n(89<\n(38 .nr 89 \n(38 -.nr 38 \we2 -.if \n(89<\n(38 .nr 89 \n(38 -.nr 38 \wesP -.if \n(89<\n(38 .nr 89 \n(38 -.nr 38 \we- -.if \n(89<\n(38 .nr 89 \n(38 -.nr 38 \we2 -.if \n(89<\n(38 .nr 89 \n(38 -.nr 38 \we2 -.if \n(89<\n(38 .nr 89 \n(38 -.nr 38 \we2 -.if \n(89<\n(38 .nr 89 \n(38 +.if \n(67<\n(38 .nr 67 \n(38 .nr 38 \wewP2 -.if \n(89<\n(38 .nr 89 \n(38 +.if \n(67<\n(38 .nr 67 \n(38 .nr 38 \we2 -.if \n(89<\n(38 .nr 89 \n(38 +.if \n(67<\n(38 .nr 67 \n(38 +.nr 38 \we- +.if \n(67<\n(38 .nr 67 \n(38 .nr 38 \we2 -.if \n(89<\n(38 .nr 89 \n(38 -.89 -.rm 89 -.nr 38 8n -.if \n(89<\n(38 .nr 89 \n(38 -.nr 90 0 -.nr 38 \w2 -.if \n(90<\n(38 .nr 90 \n(38 -.nr 38 \w1 -.if \n(90<\n(38 .nr 90 \n(38 -.nr 38 \w1 -.if \n(90<\n(38 .nr 90 \n(38 -.nr 38 \w1 -.if \n(90<\n(38 .nr 90 \n(38 -.nr 38 \w1 -.if \n(90<\n(38 .nr 90 \n(38 -.nr 38 \w2 -.if \n(90<\n(38 .nr 90 \n(38 -.nr 38 \w1 -.if \n(90<\n(38 .nr 90 \n(38 -.nr 38 \w1 -.if \n(90<\n(38 .nr 90 \n(38 -.nr 38 \w1 -.if \n(90<\n(38 .nr 90 \n(38 -.nr 38 \w1 -.if \n(90<\n(38 .nr 90 \n(38 -.nr 38 \w1 -.if \n(90<\n(38 .nr 90 \n(38 -.nr 38 \w1 -.if \n(90<\n(38 .nr 90 \n(38 -.nr 38 \w1 -.if \n(90<\n(38 .nr 90 \n(38 -.nr 38 \w1 -.if \n(90<\n(38 .nr 90 \n(38 -.nr 38 \w1 -.if \n(90<\n(38 .nr 90 \n(38 -.nr 38 \w2 -.if \n(90<\n(38 .nr 90 \n(38 -.nr 38 \w1 -.if \n(90<\n(38 .nr 90 \n(38 -.nr 38 \w1 -.if \n(90<\n(38 .nr 90 \n(38 -.nr 38 \w34 -.if \n(90<\n(38 .nr 90 \n(38 -.nr 38 \w1 -.if \n(90<\n(38 .nr 90 \n(38 -.nr 38 \w1 -.if \n(90<\n(38 .nr 90 \n(38 +.if \n(67<\n(38 .nr 67 \n(38 +.nr 38 \we- +.if \n(67<\n(38 .nr 67 \n(38 +.nr 38 \wewP2 +.if \n(67<\n(38 .nr 67 \n(38 +.nr 38 \we- +.if \n(67<\n(38 .nr 67 \n(38 +.nr 38 \we2 +.if \n(67<\n(38 .nr 67 \n(38 +.nr 38 \we2 +.if \n(67<\n(38 .nr 67 \n(38 +.nr 38 \we2 +.if \n(67<\n(38 .nr 67 \n(38 +.nr 38 \we- +.if \n(67<\n(38 .nr 67 \n(38 +.nr 38 \wesP +.if \n(67<\n(38 .nr 67 \n(38 +.nr 38 \we2 +.if \n(67<\n(38 .nr 67 \n(38 +.nr 38 \we2 +.if \n(67<\n(38 .nr 67 \n(38 +.nr 38 \we- +.if \n(67<\n(38 .nr 67 \n(38 +.nr 38 \we- +.if \n(67<\n(38 .nr 67 \n(38 +.nr 38 \we- +.if \n(67<\n(38 .nr 67 \n(38 +.nr 38 \we2 +.if \n(67<\n(38 .nr 67 \n(38 +.nr 38 \we2 +.if \n(67<\n(38 .nr 67 \n(38 +.nr 38 \we- +.if \n(67<\n(38 .nr 67 \n(38 +.nr 38 \we- +.if \n(67<\n(38 .nr 67 \n(38 +.nr 38 \we2 +.if \n(67<\n(38 .nr 67 \n(38 +.nr 38 \we- +.if \n(67<\n(38 .nr 67 \n(38 +.nr 38 \we2 +.if \n(67<\n(38 .nr 67 \n(38 +.nr 38 \we2 +.if \n(67<\n(38 .nr 67 \n(38 +.nr 38 \we2 +.if \n(67<\n(38 .nr 67 \n(38 +.nr 38 \wewP2 +.if \n(67<\n(38 .nr 67 \n(38 +.nr 38 \we2 +.if \n(67<\n(38 .nr 67 \n(38 +.nr 38 \we- +.if \n(67<\n(38 .nr 67 \n(38 +.nr 38 \we2 +.if \n(67<\n(38 .nr 67 \n(38 +.nr 38 \we2 +.if \n(67<\n(38 .nr 67 \n(38 +.nr 38 \we- +.if \n(67<\n(38 .nr 67 \n(38 +.nr 38 \we2 +.if \n(67<\n(38 .nr 67 \n(38 +.nr 38 \we- +.if \n(67<\n(38 .nr 67 \n(38 +.nr 38 \we2 +.if \n(67<\n(38 .nr 67 \n(38 +.nr 38 \we2 +.if \n(67<\n(38 .nr 67 \n(38 +.nr 38 \we- +.if \n(67<\n(38 .nr 67 \n(38 +.nr 38 \we- +.if \n(67<\n(38 .nr 67 \n(38 .nr 38 \w4 -.if \n(90<\n(38 .nr 90 \n(38 -.nr 38 \w1 -.if \n(90<\n(38 .nr 90 \n(38 -.nr 38 \w1 -.if \n(90<\n(38 .nr 90 \n(38 -.nr 38 \w2 -.if \n(90<\n(38 .nr 90 \n(38 -.nr 38 \w1 -.if \n(90<\n(38 .nr 90 \n(38 -.nr 38 \w1 -.if \n(90<\n(38 .nr 90 \n(38 -.nr 38 \w1 -.if \n(90<\n(38 .nr 90 \n(38 -.nr 38 \w3 -.if \n(90<\n(38 .nr 90 \n(38 -.nr 38 \w1 -.if \n(90<\n(38 .nr 90 \n(38 -.nr 38 \w1 -.if \n(90<\n(38 .nr 90 \n(38 -.nr 38 \w2 -.if \n(90<\n(38 .nr 90 \n(38 -.nr 38 \w1 -.if \n(90<\n(38 .nr 90 \n(38 -.nr 38 \w1 -.if \n(90<\n(38 .nr 90 \n(38 -.nr 38 \w1 -.if \n(90<\n(38 .nr 90 \n(38 -.nr 38 \w2 -.if \n(90<\n(38 .nr 90 \n(38 -.nr 38 \w1 -.if \n(90<\n(38 .nr 90 \n(38 -.90 -.rm 90 -.nr 38 3n -.if \n(90<\n(38 .nr 90 \n(38 -.nr 91 0 -.nr 38 \w36 -.if \n(91<\n(38 .nr 91 \n(38 -.nr 38 \w41 -.if \n(91<\n(38 .nr 91 \n(38 -.nr 38 \w44 -.if \n(91<\n(38 .nr 91 \n(38 -.nr 38 \w51 -.if \n(91<\n(38 .nr 91 \n(38 -.nr 38 \w54 -.if \n(91<\n(38 .nr 91 \n(38 -.nr 38 \w57 -.if \n(91<\n(38 .nr 91 \n(38 -.nr 38 \w60 -.if \n(91<\n(38 .nr 91 \n(38 -.nr 38 \w92 -.if \n(91<\n(38 .nr 91 \n(38 -.nr 38 \w95 -.if \n(91<\n(38 .nr 91 \n(38 -.nr 38 \w99 -.if \n(91<\n(38 .nr 91 \n(38 -.nr 38 \w102 -.if \n(91<\n(38 .nr 91 \n(38 -.nr 38 \w105 -.if \n(91<\n(38 .nr 91 \n(38 -.nr 38 \w108 -.if \n(91<\n(38 .nr 91 \n(38 -.nr 38 \w111 -.if \n(91<\n(38 .nr 91 \n(38 -.nr 38 \w116 -.if \n(91<\n(38 .nr 91 \n(38 -.nr 38 \w119 -.if \n(91<\n(38 .nr 91 \n(38 -.nr 38 \w128 -.if \n(91<\n(38 .nr 91 \n(38 -.nr 38 \w131 -.if \n(91<\n(38 .nr 91 \n(38 -.nr 38 \w135 -.if \n(91<\n(38 .nr 91 \n(38 -.nr 38 \w138 -.if \n(91<\n(38 .nr 91 \n(38 -.nr 38 \w141 -.if \n(91<\n(38 .nr 91 \n(38 -.nr 38 \w145 -.if \n(91<\n(38 .nr 91 \n(38 -.nr 38 \w149 -.if \n(91<\n(38 .nr 91 \n(38 -.nr 38 \w0 -.if \n(91<\n(38 .nr 91 \n(38 -.nr 38 \w154 -.if \n(91<\n(38 .nr 91 \n(38 -.nr 38 \w161 -.if \n(91<\n(38 .nr 91 \n(38 -.nr 38 \w167 -.if \n(91<\n(38 .nr 91 \n(38 -.nr 38 \w173 -.if \n(91<\n(38 .nr 91 \n(38 -.nr 38 \w176 -.if \n(91<\n(38 .nr 91 \n(38 -.nr 38 \w189 -.if \n(91<\n(38 .nr 91 \n(38 -.nr 38 \w193 -.if \n(91<\n(38 .nr 91 \n(38 -.nr 38 \w197 -.if \n(91<\n(38 .nr 91 \n(38 -.nr 38 \w201 -.if \n(91<\n(38 .nr 91 \n(38 -.nr 38 \w205 -.if \n(91<\n(38 .nr 91 \n(38 -.nr 38 \w208 -.if \n(91<\n(38 .nr 91 \n(38 -.nr 38 \w211 -.if \n(91<\n(38 .nr 91 \n(38 -.nr 38 \w217 -.if \n(91<\n(38 .nr 91 \n(38 -.nr 38 \w223 -.if \n(91<\n(38 .nr 91 \n(38 -.nr 38 \w226 -.if \n(91<\n(38 .nr 91 \n(38 -.nr 38 \w234 -.if \n(91<\n(38 .nr 91 \n(38 -.nr 38 \w237 -.if \n(91<\n(38 .nr 91 \n(38 -.nr 38 \w241 -.if \n(91<\n(38 .nr 91 \n(38 -.nr 38 \w244 -.if \n(91<\n(38 .nr 91 \n(38 -.nr 38 \w247 -.if \n(91<\n(38 .nr 91 \n(38 -.nr 38 \w250 -.if \n(91<\n(38 .nr 91 \n(38 -.nr 38 \w0 -.if \n(91<\n(38 .nr 91 \n(38 -.nr 38 \w3 -.if \n(91<\n(38 .nr 91 \n(38 -.nr 38 \w6 -.if \n(91<\n(38 .nr 91 \n(38 -.nr 38 \w9 -.if \n(91<\n(38 .nr 91 \n(38 -.nr 38 \w12 -.if \n(91<\n(38 .nr 91 \n(38 -.nr 38 \w15 -.if \n(91<\n(38 .nr 91 \n(38 -.nr 38 \w18 -.if \n(91<\n(38 .nr 91 \n(38 -.nr 38 \w21 -.if \n(91<\n(38 .nr 91 \n(38 -.nr 38 \w24 -.if \n(91<\n(38 .nr 91 \n(38 -.nr 38 \w27 -.if \n(91<\n(38 .nr 91 \n(38 -.nr 38 \w30 -.if \n(91<\n(38 .nr 91 \n(38 -.nr 38 \w33 -.if \n(91<\n(38 .nr 91 \n(38 -.nr 38 \w36 -.if \n(91<\n(38 .nr 91 \n(38 -.nr 38 \w39 -.if \n(91<\n(38 .nr 91 \n(38 -.nr 38 \w42 -.if \n(91<\n(38 .nr 91 \n(38 -.nr 38 \w45 -.if \n(91<\n(38 .nr 91 \n(38 -.nr 38 \w48 -.if \n(91<\n(38 .nr 91 \n(38 -.nr 38 \w51 -.if \n(91<\n(38 .nr 91 \n(38 -.nr 38 \w54 -.if \n(91<\n(38 .nr 91 \n(38 -.nr 38 \w57 -.if \n(91<\n(38 .nr 91 \n(38 -.nr 38 \w60 -.if \n(91<\n(38 .nr 91 \n(38 -.nr 38 \w63 -.if \n(91<\n(38 .nr 91 \n(38 -.nr 38 \w66 -.if \n(91<\n(38 .nr 91 \n(38 -.nr 38 \w69 -.if \n(91<\n(38 .nr 91 \n(38 -.nr 38 \w72 -.if \n(91<\n(38 .nr 91 \n(38 -.nr 38 \w75 -.if \n(91<\n(38 .nr 91 \n(38 -.nr 38 \w78 -.if \n(91<\n(38 .nr 91 \n(38 -.nr 38 \w81 -.if \n(91<\n(38 .nr 91 \n(38 -.nr 38 \w84 -.if \n(91<\n(38 .nr 91 \n(38 -.nr 38 \w87 -.if \n(91<\n(38 .nr 91 \n(38 -.nr 38 \w90 -.if \n(91<\n(38 .nr 91 \n(38 -.nr 38 \w93 -.if \n(91<\n(38 .nr 91 \n(38 -.nr 38 \w96 -.if \n(91<\n(38 .nr 91 \n(38 -.nr 38 \w99 -.if \n(91<\n(38 .nr 91 \n(38 -.nr 38 \w102 -.if \n(91<\n(38 .nr 91 \n(38 -.nr 38 \w105 -.if \n(91<\n(38 .nr 91 \n(38 -.nr 38 \w108 -.if \n(91<\n(38 .nr 91 \n(38 -.nr 38 \w111 -.if \n(91<\n(38 .nr 91 \n(38 -.nr 38 \w114 -.if \n(91<\n(38 .nr 91 \n(38 -.nr 38 \w117 -.if \n(91<\n(38 .nr 91 \n(38 -.nr 38 \w120 -.if \n(91<\n(38 .nr 91 \n(38 -.nr 38 \w123 -.if \n(91<\n(38 .nr 91 \n(38 -.nr 38 \w126 -.if \n(91<\n(38 .nr 91 \n(38 -.nr 38 \w129 -.if \n(91<\n(38 .nr 91 \n(38 -.nr 38 \w132 -.if \n(91<\n(38 .nr 91 \n(38 -.nr 38 \w135 -.if \n(91<\n(38 .nr 91 \n(38 -.nr 38 \w138 -.if \n(91<\n(38 .nr 91 \n(38 -.nr 38 \w141 -.if \n(91<\n(38 .nr 91 \n(38 -.nr 38 \w144 -.if \n(91<\n(38 .nr 91 \n(38 -.nr 38 \w147 -.if \n(91<\n(38 .nr 91 \n(38 -.nr 38 \w150 -.if \n(91<\n(38 .nr 91 \n(38 -.nr 38 \w153 -.if \n(91<\n(38 .nr 91 \n(38 -.nr 38 \w156 -.if \n(91<\n(38 .nr 91 \n(38 -.nr 38 \w159 -.if \n(91<\n(38 .nr 91 \n(38 -.91 -.rm 91 +.if \n(67<\n(38 .nr 67 \n(38 +.nr 38 \wN4 +.if \n(67<\n(38 .nr 67 \n(38 +.nr 38 \wwP4 +.if \n(67<\n(38 .nr 67 \n(38 +.nr 38 \wwN4 +.if \n(67<\n(38 .nr 67 \n(38 +.nr 38 \w4 +.if \n(67<\n(38 .nr 67 \n(38 +.nr 38 \w4 +.if \n(67<\n(38 .nr 67 \n(38 +.nr 38 \w4 +.if \n(67<\n(38 .nr 67 \n(38 +.nr 38 \w4 +.if \n(67<\n(38 .nr 67 \n(38 +.nr 38 \w4 +.if \n(67<\n(38 .nr 67 \n(38 +.nr 38 \ww4 +.if \n(67<\n(38 .nr 67 \n(38 +.nr 38 \w4 +.if \n(67<\n(38 .nr 67 \n(38 +.nr 38 \wwP4 +.if \n(67<\n(38 .nr 67 \n(38 +.nr 38 \w4 +.if \n(67<\n(38 .nr 67 \n(38 +.nr 38 \wwN4 +.if \n(67<\n(38 .nr 67 \n(38 +.nr 38 \ww4 +.if \n(67<\n(38 .nr 67 \n(38 +.nr 38 \wwN4 +.if \n(67<\n(38 .nr 67 \n(38 +.nr 38 \w4 +.if \n(67<\n(38 .nr 67 \n(38 +.nr 38 \w4 +.if \n(67<\n(38 .nr 67 \n(38 +.nr 38 \wwN4 +.if \n(67<\n(38 .nr 67 \n(38 +.67 +.rm 67 .nr 38 6n -.if \n(91<\n(38 .nr 91 \n(38 +.if \n(67<\n(38 .nr 67 \n(38 +.nr 68 0 +.nr 38 \w1 +.if \n(68<\n(38 .nr 68 \n(38 +.nr 38 \w1 +.if \n(68<\n(38 .nr 68 \n(38 +.nr 38 \w5 +.if \n(68<\n(38 .nr 68 \n(38 +.nr 38 \w1 +.if \n(68<\n(38 .nr 68 \n(38 +.nr 38 \w1 +.if \n(68<\n(38 .nr 68 \n(38 +.nr 38 \w1 +.if \n(68<\n(38 .nr 68 \n(38 +.nr 38 \w2 +.if \n(68<\n(38 .nr 68 \n(38 +.nr 38 \w1 +.if \n(68<\n(38 .nr 68 \n(38 +.nr 38 \w1 +.if \n(68<\n(38 .nr 68 \n(38 +.nr 38 \w1 +.if \n(68<\n(38 .nr 68 \n(38 +.nr 38 \w1 +.if \n(68<\n(38 .nr 68 \n(38 +.nr 38 \w1 +.if \n(68<\n(38 .nr 68 \n(38 +.nr 38 \w1 +.if \n(68<\n(38 .nr 68 \n(38 +.nr 38 \w1 +.if \n(68<\n(38 .nr 68 \n(38 +.nr 38 \w1 +.if \n(68<\n(38 .nr 68 \n(38 +.nr 38 \w1 +.if \n(68<\n(38 .nr 68 \n(38 +.nr 38 \w2 +.if \n(68<\n(38 .nr 68 \n(38 +.nr 38 \w1 +.if \n(68<\n(38 .nr 68 \n(38 +.nr 38 \w4 +.if \n(68<\n(38 .nr 68 \n(38 +.nr 38 \w1 +.if \n(68<\n(38 .nr 68 \n(38 +.nr 38 \w8 +.if \n(68<\n(38 .nr 68 \n(38 +.nr 38 \w1 +.if \n(68<\n(38 .nr 68 \n(38 +.nr 38 \w2 +.if \n(68<\n(38 .nr 68 \n(38 +.nr 38 \w1 +.if \n(68<\n(38 .nr 68 \n(38 +.nr 38 \w1 +.if \n(68<\n(38 .nr 68 \n(38 +.nr 38 \w1 +.if \n(68<\n(38 .nr 68 \n(38 +.nr 38 \w1 +.if \n(68<\n(38 .nr 68 \n(38 +.nr 38 \w1 +.if \n(68<\n(38 .nr 68 \n(38 +.nr 38 \w5 +.if \n(68<\n(38 .nr 68 \n(38 +.nr 38 \w1 +.if \n(68<\n(38 .nr 68 \n(38 +.nr 38 \w1 +.if \n(68<\n(38 .nr 68 \n(38 +.nr 38 \w1 +.if \n(68<\n(38 .nr 68 \n(38 +.nr 38 \w1 +.if \n(68<\n(38 .nr 68 \n(38 +.68 +.rm 68 +.nr 38 2n +.if \n(68<\n(38 .nr 68 \n(38 +.nr 69 0 +.nr 38 \w34 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w38 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w42 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w45 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w52 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w55 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w58 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w62 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w93 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w96 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w100 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w103 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w106 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w109 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w112 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w117 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w120 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w129 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w132 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w136 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w139 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w143 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w146 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w150 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w152 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w155 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w162 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w168 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w174 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w180 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w190 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w194 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w199 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w202 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w206 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w209 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w214 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w218 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w224 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w228 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w235 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w238 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w242 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w245 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w248 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w252 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w1 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w4 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w7 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w10 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w13 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w16 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w19 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w22 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w25 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w28 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w31 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w34 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w37 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w40 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w43 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w46 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w49 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w52 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w55 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w58 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w61 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w64 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w67 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w70 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w73 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w76 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w79 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w82 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w85 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w88 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w91 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w94 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w97 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w100 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w103 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w106 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w109 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w112 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w115 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w118 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w121 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w124 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w127 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w130 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w133 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w136 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w139 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w142 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w145 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w148 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w151 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w154 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w157 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w0 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w3 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w6 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w9 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w12 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w15 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w18 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w21 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w24 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w27 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w30 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w33 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w36 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w39 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w42 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w45 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w48 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w51 +.if \n(69<\n(38 .nr 69 \n(38 +.nr 38 \w54 +.if \n(69<\n(38 .nr 69 \n(38 +.69 +.rm 69 +.nr 38 5n +.if \n(69<\n(38 .nr 69 \n(38 +.nr 70 0 +.nr 38 \wadf +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wadp +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wads +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wasp +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wbge +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wblm +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wbra +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wcal +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wcif +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wcmi +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wcsa +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wdee +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wdvf +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \winc +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \winl +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wior +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wlae +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wlal +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wlal +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wlde +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wldl +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wlil +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wlin +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wloc +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wloc +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wloe +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wlof +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wloi +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wlol +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wlol +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wlxl +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wrck +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wrmi +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wsbi +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wsil +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wste +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wstf +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wsti +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wstl +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wstl +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wtlt +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wzeq +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wzgt +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wzne +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wzre +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wzrl +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wadf +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wadi +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wadu +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wand +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wass +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wble +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wbls +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wcai +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wcfu +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wcmf +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wcms +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wcmu +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wcsa +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wcsb +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wcuu +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wdel +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wdus +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wdvi +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wdvu +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wfif +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \winl +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wior +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wlar +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wldl +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wlil +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wlos +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wlpi +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wmlf +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wmli +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wmon +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wngi +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wrck +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wrmi +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wrmu +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wror +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wsar +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wsbf +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wsbs +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wsbu +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wsdl +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wset +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wsil +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wsli +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wsri +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wsru +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wsts +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wtle +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wxor +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wzge +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wzlt +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wzrf +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wexg +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wlpb +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wlae +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wlde +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wldl +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wloc +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wlol +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wadp +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wbge +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wblm +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wbra +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wdel +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wgto +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \winl +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wsdf +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wsil +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wstf +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wzeq +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wzle +.if \n(70<\n(38 .nr 70 \n(38 +.nr 38 \wzre +.if \n(70<\n(38 .nr 70 \n(38 +.70 +.rm 70 +.nr 38 4n +.if \n(70<\n(38 .nr 70 \n(38 +.nr 71 0 +.nr 38 \wsP +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \wmPo +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \wmwPo +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \wswP +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \wsP +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \wsP +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \w2 +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \wmPo +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \w- +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \wmwPo +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \wmwPo +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \wsw +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \wsP +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \w- +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \wmwN +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \wmwPo +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \wsw +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \wmP +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \wswN +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \ww2 +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \wswN +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \wswN +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \w2 +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \w2 +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \wsP +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \wsw +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \wsP +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \wmwPo +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \wwN2 +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \wswP +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \wmPo +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \wmwPo +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \wmwPo +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \wmwPo +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \wswN +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \ww2 +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \wmwPo +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \wmwPo +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \wwN2 +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \wswN +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \w- +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \wsP +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \wsP +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \wsP +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \wsw +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \wwN2 +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \we2 +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \we- +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \we2 +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \we- +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \we- +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \we2 +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \we- +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \we- +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \we- +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \we- +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \we2 +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \we- +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \we2 +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \we- +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \we- +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \wewN2 +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \we- +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \we2 +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \we- +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \we2 +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \wewN2 +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \we2 +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \we- +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \wewP2 +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \wewP2 +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \we2 +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \we2 +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \we2 +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \we- +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \we- +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \we2 +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \we2 +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \we2 +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \we- +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \we2 +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \we2 +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \we- +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \we2 +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \we- +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \wewP2 +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \we- +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \wewN2 +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \we- +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \we2 +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \we- +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \we- +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \we- +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \we- +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \we2 +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \we2 +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \we- +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \wesP +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \we- +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \w4 +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \ww4 +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \wwN4 +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \w4 +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \wwP4 +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \w4 +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \w4 +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \w4 +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \w4 +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \wwP4 +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \w4 +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \wwN4 +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \w4 +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \wwP4 +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \w4 +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \w4 +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \w4 +.if \n(71<\n(38 .nr 71 \n(38 +.nr 38 \ww4 +.if \n(71<\n(38 .nr 71 \n(38 +.71 +.rm 71 +.nr 38 6n +.if \n(71<\n(38 .nr 71 \n(38 +.nr 72 0 +.nr 38 \w1 +.if \n(72<\n(38 .nr 72 \n(38 +.nr 38 \w2 +.if \n(72<\n(38 .nr 72 \n(38 +.nr 38 \w1 +.if \n(72<\n(38 .nr 72 \n(38 +.nr 38 \w1 +.if \n(72<\n(38 .nr 72 \n(38 +.nr 38 \w1 +.if \n(72<\n(38 .nr 72 \n(38 +.nr 38 \w1 +.if \n(72<\n(38 .nr 72 \n(38 +.nr 38 \w28 +.if \n(72<\n(38 .nr 72 \n(38 +.nr 38 \w2 +.if \n(72<\n(38 .nr 72 \n(38 +.nr 38 \w1 +.if \n(72<\n(38 .nr 72 \n(38 +.nr 38 \w1 +.if \n(72<\n(38 .nr 72 \n(38 +.nr 38 \w1 +.if \n(72<\n(38 .nr 72 \n(38 +.nr 38 \w3 +.if \n(72<\n(38 .nr 72 \n(38 +.nr 38 \w1 +.if \n(72<\n(38 .nr 72 \n(38 +.nr 38 \w7 +.if \n(72<\n(38 .nr 72 \n(38 +.nr 38 \w1 +.if \n(72<\n(38 .nr 72 \n(38 +.nr 38 \w2 +.if \n(72<\n(38 .nr 72 \n(38 +.nr 38 \w1 +.if \n(72<\n(38 .nr 72 \n(38 +.nr 38 \w1 +.if \n(72<\n(38 .nr 72 \n(38 +.nr 38 \w1 +.if \n(72<\n(38 .nr 72 \n(38 +.nr 38 \w5 +.if \n(72<\n(38 .nr 72 \n(38 +.nr 38 \w1 +.if \n(72<\n(38 .nr 72 \n(38 +.nr 38 \w4 +.if \n(72<\n(38 .nr 72 \n(38 +.nr 38 \w1 +.if \n(72<\n(38 .nr 72 \n(38 +.nr 38 \w2 +.if \n(72<\n(38 .nr 72 \n(38 +.nr 38 \w1 +.if \n(72<\n(38 .nr 72 \n(38 +.nr 38 \w1 +.if \n(72<\n(38 .nr 72 \n(38 +.nr 38 \w2 +.if \n(72<\n(38 .nr 72 \n(38 +.nr 38 \w1 +.if \n(72<\n(38 .nr 72 \n(38 +.nr 38 \w2 +.if \n(72<\n(38 .nr 72 \n(38 +.nr 38 \w4 +.if \n(72<\n(38 .nr 72 \n(38 +.nr 38 \w1 +.if \n(72<\n(38 .nr 72 \n(38 +.nr 38 \w2 +.if \n(72<\n(38 .nr 72 \n(38 +.nr 38 \w1 +.if \n(72<\n(38 .nr 72 \n(38 +.nr 38 \w1 +.if \n(72<\n(38 .nr 72 \n(38 +.nr 38 \w1 +.if \n(72<\n(38 .nr 72 \n(38 +.nr 38 \w1 +.if \n(72<\n(38 .nr 72 \n(38 +.72 +.rm 72 +.nr 38 2n +.if \n(72<\n(38 .nr 72 \n(38 +.nr 73 0 +.nr 38 \w35 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w39 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w43 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w50 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w53 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w56 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w59 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w64 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w94 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w97 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w101 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w104 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w107 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w110 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w113 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w118 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w121 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w130 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w133 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w137 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w140 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w144 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w148 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w151 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w153 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w156 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w166 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w169 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w175 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w188 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w191 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w196 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w200 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w203 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w207 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w210 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w215 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w219 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w225 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w233 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w236 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w239 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w243 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w246 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w249 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w253 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w2 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w5 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w8 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w11 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w14 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w17 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w20 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w23 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w26 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w29 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w32 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w35 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w38 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w41 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w44 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w47 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w50 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w53 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w56 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w59 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w62 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w65 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w68 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w71 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w74 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w77 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w80 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w83 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w86 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w89 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w92 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w95 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w98 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w101 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w104 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w107 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w110 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w113 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w116 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w119 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w122 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w125 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w128 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w131 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w134 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w137 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w140 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w143 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w146 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w149 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w152 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w155 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w158 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w1 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w4 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w7 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w10 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w13 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w16 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w19 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w22 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w25 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w28 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w31 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w34 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w37 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w40 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w43 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w46 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w49 +.if \n(73<\n(38 .nr 73 \n(38 +.nr 38 \w52 +.if \n(73<\n(38 .nr 73 \n(38 +.73 +.rm 73 +.nr 38 5n +.if \n(73<\n(38 .nr 73 \n(38 +.nr 74 0 +.nr 38 \wadi +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wadp +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wand +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wbeq +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wbgt +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wblt +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wbra +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wcal +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wcii +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wcmp +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wcsb +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wdel +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wdvi +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wine +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \winl +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wior +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wlal +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wlal +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wlar +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wlde +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wlfr +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wlil +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wlin +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wloc +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wloc +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wlof +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wloi +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wloi +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wlol +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wlol +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wmlf +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wret +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wsar +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wsdl +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wsil +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wste +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wstf +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wsti +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wstl +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wteq +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wtne +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wzer +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wzle +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wzne +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wzrl +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \waar +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wadf +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wads +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wadu +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wasp +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wbge +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wblm +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wblt +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wcal +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wciu +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wcmi +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wcms +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wcom +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wcsa +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wcuf +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wdee +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wdup +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wdvf +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wdvi +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wfef +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wfif +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \winn +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wior +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wldc +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wldl +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wlil +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wlos +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wlxa +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wmlf +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wmlu +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wngf +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wngi +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wrck +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wrmi +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wrol +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wror +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wsar +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wsbi +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wsbs +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wsde +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wsdl +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wsig +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wsim +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wslu +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wsri +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wsti +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wstr +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wtrp +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wzer +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wzgt +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wzne +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wzrl +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wexg +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wgto +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wlal +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wldf +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wlil +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wloe +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wlol +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wasp +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wbgt +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wblt +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wcal +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wdel +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wine +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wlin +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wsdl +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wsil +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wstl +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wzge +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wzlt +.if \n(74<\n(38 .nr 74 \n(38 +.nr 38 \wzrl +.if \n(74<\n(38 .nr 74 \n(38 +.74 +.rm 74 +.nr 38 4n +.if \n(74<\n(38 .nr 74 \n(38 +.nr 75 0 +.nr 38 \wmwPo +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \wsP +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \wmwPo +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \w2 +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \wsP +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \wsP +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \wsN +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \wsP +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \w- +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \w- +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \wmwPo +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \wswN +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \wmwPo +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \ww2 +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \wswN +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \wsP +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \wP2 +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \wmN +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \wmwPo +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \wsw +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \wmwPo +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \wswP +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \wsP +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \wmP +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \wsN +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \w2 +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \w2 +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \wsP +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \wmwP +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \wswN +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \wsP +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \wmwP +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \wmwPo +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \wswN +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \wswP +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \wsw +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \wsP +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \wsP +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \wmwP +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \w- +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \w- +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \wsP +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \wsP +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \wsN +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \wmwN +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \we2 +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \we- +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \we2 +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \we- +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \wew2 +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \we2 +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \we2 +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \we2 +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \we2 +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \we- +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \we2 +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \we- +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \we2 +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \we- +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \we- +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \wew2 +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \we2 +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \we2 +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \we- +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \we2 +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \we- +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \we2 +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \we- +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \we2 +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \wewN2 +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \wewN2 +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \we- +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \we2 +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \we- +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \we2 +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \we2 +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \we- +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \we- +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \we- +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \we2 +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \we- +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \we- +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \we2 +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \we- +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \we2 +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \wewN2 +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \we- +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \we- +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \we2 +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \we- +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \we2 +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \wesP +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \we- +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \we2 +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \we2 +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \we2 +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \wewP2 +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \we2 +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \we2 +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \wP4 +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \w4 +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \wwP4 +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \ww4 +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \wwN4 +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \ww4 +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \w4 +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \w4 +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \w4 +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \wwN4 +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \ww4 +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \w4 +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \wwP4 +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \wwN4 +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \wwP4 +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \w4 +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \w4 +.if \n(75<\n(38 .nr 75 \n(38 +.nr 38 \wwP4 +.if \n(75<\n(38 .nr 75 \n(38 +.75 +.rm 75 +.nr 38 6n +.if \n(75<\n(38 .nr 75 \n(38 +.nr 76 0 +.nr 38 \w2 +.if \n(76<\n(38 .nr 76 \n(38 +.nr 38 \w1 +.if \n(76<\n(38 .nr 76 \n(38 +.nr 38 \w1 +.if \n(76<\n(38 .nr 76 \n(38 +.nr 38 \w1 +.if \n(76<\n(38 .nr 76 \n(38 +.nr 38 \w1 +.if \n(76<\n(38 .nr 76 \n(38 +.nr 38 \w2 +.if \n(76<\n(38 .nr 76 \n(38 +.nr 38 \w1 +.if \n(76<\n(38 .nr 76 \n(38 +.nr 38 \w1 +.if \n(76<\n(38 .nr 76 \n(38 +.nr 38 \w1 +.if \n(76<\n(38 .nr 76 \n(38 +.nr 38 \w1 +.if \n(76<\n(38 .nr 76 \n(38 +.nr 38 \w1 +.if \n(76<\n(38 .nr 76 \n(38 +.nr 38 \w1 +.if \n(76<\n(38 .nr 76 \n(38 +.nr 38 \w1 +.if \n(76<\n(38 .nr 76 \n(38 +.nr 38 \w1 +.if \n(76<\n(38 .nr 76 \n(38 +.nr 38 \w1 +.if \n(76<\n(38 .nr 76 \n(38 +.nr 38 \w2 +.if \n(76<\n(38 .nr 76 \n(38 +.nr 38 \w1 +.if \n(76<\n(38 .nr 76 \n(38 +.nr 38 \w1 +.if \n(76<\n(38 .nr 76 \n(38 +.nr 38 \w34 +.if \n(76<\n(38 .nr 76 \n(38 +.nr 38 \w1 +.if \n(76<\n(38 .nr 76 \n(38 +.nr 38 \w1 +.if \n(76<\n(38 .nr 76 \n(38 +.nr 38 \w4 +.if \n(76<\n(38 .nr 76 \n(38 +.nr 38 \w1 +.if \n(76<\n(38 .nr 76 \n(38 +.nr 38 \w1 +.if \n(76<\n(38 .nr 76 \n(38 +.nr 38 \w2 +.if \n(76<\n(38 .nr 76 \n(38 +.nr 38 \w1 +.if \n(76<\n(38 .nr 76 \n(38 +.nr 38 \w1 +.if \n(76<\n(38 .nr 76 \n(38 +.nr 38 \w1 +.if \n(76<\n(38 .nr 76 \n(38 +.nr 38 \w3 +.if \n(76<\n(38 .nr 76 \n(38 +.nr 38 \w1 +.if \n(76<\n(38 .nr 76 \n(38 +.nr 38 \w1 +.if \n(76<\n(38 .nr 76 \n(38 +.nr 38 \w2 +.if \n(76<\n(38 .nr 76 \n(38 +.nr 38 \w1 +.if \n(76<\n(38 .nr 76 \n(38 +.nr 38 \w1 +.if \n(76<\n(38 .nr 76 \n(38 +.nr 38 \w1 +.if \n(76<\n(38 .nr 76 \n(38 +.nr 38 \w2 +.if \n(76<\n(38 .nr 76 \n(38 +.nr 38 \w1 +.if \n(76<\n(38 .nr 76 \n(38 +.76 +.rm 76 +.nr 38 2n +.if \n(76<\n(38 .nr 76 \n(38 +.nr 77 0 +.nr 38 \w36 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w41 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w44 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w51 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w54 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w57 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w60 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w92 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w95 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w99 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w102 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w105 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w108 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w111 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w116 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w119 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w128 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w131 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w135 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w138 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w141 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w145 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w149 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w0 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w154 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w161 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w167 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w173 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w176 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w189 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w193 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w197 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w201 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w205 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w208 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w211 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w217 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w223 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w226 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w234 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w237 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w241 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w244 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w247 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w250 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w0 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w3 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w6 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w9 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w12 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w15 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w18 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w21 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w24 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w27 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w30 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w33 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w36 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w39 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w42 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w45 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w48 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w51 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w54 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w57 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w60 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w63 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w66 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w69 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w72 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w75 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w78 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w81 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w84 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w87 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w90 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w93 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w96 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w99 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w102 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w105 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w108 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w111 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w114 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w117 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w120 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w123 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w126 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w129 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w132 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w135 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w138 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w141 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w144 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w147 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w150 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w153 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w156 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w159 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w2 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w5 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w8 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w11 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w14 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w17 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w20 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w23 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w26 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w29 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w32 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w35 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w38 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w41 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w44 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w47 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w50 +.if \n(77<\n(38 .nr 77 \n(38 +.nr 38 \w53 +.if \n(77<\n(38 .nr 77 \n(38 +.77 +.rm 77 +.nr 38 5n +.if \n(77<\n(38 .nr 77 \n(38 .nr 38 1n -.nr 79 0 -.nr 40 \n(79+(0*\n(38) -.nr 80 +\n(40 -.nr 41 \n(80+(3*\n(38) -.nr 81 +\n(41 -.nr 42 \n(81+(3*\n(38) -.nr 82 +\n(42 -.nr 43 \n(82+(3*\n(38) -.nr 83 +\n(43 -.nr 44 \n(83+(14*\n(38) -.nr 84 +\n(44 -.nr 45 \n(84+(3*\n(38) -.nr 85 +\n(45 -.nr 46 \n(85+(3*\n(38) -.nr 86 +\n(46 -.nr 47 \n(86+(3*\n(38) -.nr 87 +\n(47 -.nr 48 \n(87+(14*\n(38) -.nr 88 +\n(48 -.nr 49 \n(88+(3*\n(38) -.nr 89 +\n(49 -.nr 50 \n(89+(3*\n(38) -.nr 90 +\n(50 -.nr 51 \n(90+(3*\n(38) -.nr 91 +\n(51 -.nr TW \n(91 -.if t .if (\n(TW+\n(.o)>7.65i .tm Table at line 103 file Input is too wide - \n(TW units +.nr 65 0 +.nr 40 \n(65+((0*\n(38)/2) +.nr 66 +\n(40 +.nr 41 \n(66+((0*\n(38)/2) +.nr 67 +\n(41 +.nr 42 \n(67+((0*\n(38)/2) +.nr 68 +\n(42 +.nr 43 \n(68+((0*\n(38)/2) +.nr 69 +\n(43 +.nr 44 \n(69+((16*\n(38)/2) +.nr 70 +\n(44 +.nr 45 \n(70+((0*\n(38)/2) +.nr 71 +\n(45 +.nr 46 \n(71+((0*\n(38)/2) +.nr 72 +\n(46 +.nr 47 \n(72+((0*\n(38)/2) +.nr 73 +\n(47 +.nr 48 \n(73+((16*\n(38)/2) +.nr 74 +\n(48 +.nr 49 \n(74+((0*\n(38)/2) +.nr 75 +\n(49 +.nr 50 \n(75+((0*\n(38)/2) +.nr 76 +\n(50 +.nr 51 \n(76+((0*\n(38)/2) +.nr 77 +\n(51 +.nr TW \n(77 +.if t .if (\n(TW+\n(.o)>7.65i .tm Table at line 121 file Input is too wide - \n(TW units .fc   .nr #T 0-1 .nr #a 0-1 .eo .de T# +.nr 35 1m .ds #d .d .if \(ts\n(.z\(ts\(ts .ds #d nl .mk ## @@ -2117,409 +2442,481 @@ .ls .. .ec -.ta \n(80u \n(81u \n(82u \n(83u \n(84u \n(85u \n(86u \n(87u \n(88u \n(89u \n(90u \n(91u -.nr 31 \n(.f +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u .nr 35 1m +.nr 31 \n(.f \&\h'|\n(40u'aar\h'|\n(41u'mwPo\h'|\n(42u'1\h'|\n(43u'34\h'|\n(44u'adf\h'|\n(45u'sP\h'|\n(46u'1\h'|\n(47u'35\h'|\n(48u'adi\h'|\n(49u'mwPo\h'|\n(50u'2\h'|\n(51u'36 -.ta \n(80u \n(81u \n(82u \n(83u \n(84u \n(85u \n(86u \n(87u \n(88u \n(89u \n(90u \n(91u -.nr 31 \n(.f +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u .nr 35 1m +.nr 31 \n(.f \&\h'|\n(40u'adp\h'|\n(41u'2\h'|\n(42u'\h'|\n(43u'38\h'|\n(44u'adp\h'|\n(45u'mPo\h'|\n(46u'2\h'|\n(47u'39\h'|\n(48u'adp\h'|\n(49u'sP\h'|\n(50u'1\h'|\n(51u'41 -.ta \n(80u \n(81u \n(82u \n(83u \n(84u \n(85u \n(86u \n(87u \n(88u \n(89u \n(90u \n(91u -.nr 31 \n(.f +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u .nr 35 1m +.nr 31 \n(.f \&\h'|\n(40u'adp\h'|\n(41u'sN\h'|\n(42u'1\h'|\n(43u'42\h'|\n(44u'ads\h'|\n(45u'mwPo\h'|\n(46u'1\h'|\n(47u'43\h'|\n(48u'and\h'|\n(49u'mwPo\h'|\n(50u'1\h'|\n(51u'44 -.ta \n(80u \n(81u \n(82u \n(83u \n(84u \n(85u \n(86u \n(87u \n(88u \n(89u \n(90u \n(91u -.nr 31 \n(.f +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u .nr 35 1m +.nr 31 \n(.f \&\h'|\n(40u'asp\h'|\n(41u'mwPo\h'|\n(42u'5\h'|\n(43u'45\h'|\n(44u'asp\h'|\n(45u'swP\h'|\n(46u'1\h'|\n(47u'50\h'|\n(48u'beq\h'|\n(49u'2\h'|\n(50u'\h'|\n(51u'51 -.ta \n(80u \n(81u \n(82u \n(83u \n(84u \n(85u \n(86u \n(87u \n(88u \n(89u \n(90u \n(91u -.nr 31 \n(.f +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u .nr 35 1m +.nr 31 \n(.f \&\h'|\n(40u'beq\h'|\n(41u'sP\h'|\n(42u'1\h'|\n(43u'52\h'|\n(44u'bge\h'|\n(45u'sP\h'|\n(46u'1\h'|\n(47u'53\h'|\n(48u'bgt\h'|\n(49u'sP\h'|\n(50u'1\h'|\n(51u'54 -.ta \n(80u \n(81u \n(82u \n(83u \n(84u \n(85u \n(86u \n(87u \n(88u \n(89u \n(90u \n(91u -.nr 31 \n(.f +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u .nr 35 1m +.nr 31 \n(.f \&\h'|\n(40u'ble\h'|\n(41u'sP\h'|\n(42u'1\h'|\n(43u'55\h'|\n(44u'blm\h'|\n(45u'sP\h'|\n(46u'1\h'|\n(47u'56\h'|\n(48u'blt\h'|\n(49u'sP\h'|\n(50u'1\h'|\n(51u'57 -.ta \n(80u \n(81u \n(82u \n(83u \n(84u \n(85u \n(86u \n(87u \n(88u \n(89u \n(90u \n(91u -.nr 31 \n(.f +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u .nr 35 1m +.nr 31 \n(.f \&\h'|\n(40u'bne\h'|\n(41u'sP\h'|\n(42u'1\h'|\n(43u'58\h'|\n(44u'bra\h'|\n(45u'2\h'|\n(46u'\h'|\n(47u'59\h'|\n(48u'bra\h'|\n(49u'sN\h'|\n(50u'2\h'|\n(51u'60 -.ta \n(80u \n(81u \n(82u \n(83u \n(84u \n(85u \n(86u \n(87u \n(88u \n(89u \n(90u \n(91u -.nr 31 \n(.f +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u .nr 35 1m +.nr 31 \n(.f \&\h'|\n(40u'bra\h'|\n(41u'sP\h'|\n(42u'2\h'|\n(43u'62\h'|\n(44u'cal\h'|\n(45u'mPo\h'|\n(46u'28\h'|\n(47u'64\h'|\n(48u'cal\h'|\n(49u'sP\h'|\n(50u'1\h'|\n(51u'92 -.ta \n(80u \n(81u \n(82u \n(83u \n(84u \n(85u \n(86u \n(87u \n(88u \n(89u \n(90u \n(91u -.nr 31 \n(.f +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u .nr 35 1m +.nr 31 \n(.f \&\h'|\n(40u'cff\h'|\n(41u'-\h'|\n(42u'\h'|\n(43u'93\h'|\n(44u'cif\h'|\n(45u'-\h'|\n(46u'\h'|\n(47u'94\h'|\n(48u'cii\h'|\n(49u'-\h'|\n(50u'\h'|\n(51u'95 -.ta \n(80u \n(81u \n(82u \n(83u \n(84u \n(85u \n(86u \n(87u \n(88u \n(89u \n(90u \n(91u -.nr 31 \n(.f +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u .nr 35 1m +.nr 31 \n(.f \&\h'|\n(40u'cmf\h'|\n(41u'sP\h'|\n(42u'1\h'|\n(43u'96\h'|\n(44u'cmi\h'|\n(45u'mwPo\h'|\n(46u'2\h'|\n(47u'97\h'|\n(48u'cmp\h'|\n(49u'-\h'|\n(50u'\h'|\n(51u'99 -.ta \n(80u \n(81u \n(82u \n(83u \n(84u \n(85u \n(86u \n(87u \n(88u \n(89u \n(90u \n(91u -.nr 31 \n(.f +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u .nr 35 1m +.nr 31 \n(.f \&\h'|\n(40u'cms\h'|\n(41u'sP\h'|\n(42u'1\h'|\n(43u'100\h'|\n(44u'csa\h'|\n(45u'mwPo\h'|\n(46u'1\h'|\n(47u'101\h'|\n(48u'csb\h'|\n(49u'mwPo\h'|\n(50u'1\h'|\n(51u'102 -.ta \n(80u \n(81u \n(82u \n(83u \n(84u \n(85u \n(86u \n(87u \n(88u \n(89u \n(90u \n(91u -.nr 31 \n(.f +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u .nr 35 1m +.nr 31 \n(.f \&\h'|\n(40u'dec\h'|\n(41u'-\h'|\n(42u'\h'|\n(43u'103\h'|\n(44u'dee\h'|\n(45u'sw\h'|\n(46u'1\h'|\n(47u'104\h'|\n(48u'del\h'|\n(49u'swN\h'|\n(50u'1\h'|\n(51u'105 -.ta \n(80u \n(81u \n(82u \n(83u \n(84u \n(85u \n(86u \n(87u \n(88u \n(89u \n(90u \n(91u -.nr 31 \n(.f +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u .nr 35 1m +.nr 31 \n(.f \&\h'|\n(40u'dup\h'|\n(41u'mwPo\h'|\n(42u'1\h'|\n(43u'106\h'|\n(44u'dvf\h'|\n(45u'sP\h'|\n(46u'1\h'|\n(47u'107\h'|\n(48u'dvi\h'|\n(49u'mwPo\h'|\n(50u'1\h'|\n(51u'108 -.ta \n(80u \n(81u \n(82u \n(83u \n(84u \n(85u \n(86u \n(87u \n(88u \n(89u \n(90u \n(91u -.nr 31 \n(.f +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u .nr 35 1m +.nr 31 \n(.f \&\h'|\n(40u'fil\h'|\n(41u'2\h'|\n(42u'\h'|\n(43u'109\h'|\n(44u'inc\h'|\n(45u'-\h'|\n(46u'\h'|\n(47u'110\h'|\n(48u'ine\h'|\n(49u'w2\h'|\n(50u'\h'|\n(51u'111 -.ta \n(80u \n(81u \n(82u \n(83u \n(84u \n(85u \n(86u \n(87u \n(88u \n(89u \n(90u \n(91u -.nr 31 \n(.f +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u .nr 35 1m +.nr 31 \n(.f \&\h'|\n(40u'ine\h'|\n(41u'sw\h'|\n(42u'1\h'|\n(43u'112\h'|\n(44u'inl\h'|\n(45u'mwN\h'|\n(46u'3\h'|\n(47u'113\h'|\n(48u'inl\h'|\n(49u'swN\h'|\n(50u'1\h'|\n(51u'116 -.ta \n(80u \n(81u \n(82u \n(83u \n(84u \n(85u \n(86u \n(87u \n(88u \n(89u \n(90u \n(91u -.nr 31 \n(.f +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u .nr 35 1m +.nr 31 \n(.f \&\h'|\n(40u'inn\h'|\n(41u'sP\h'|\n(42u'1\h'|\n(43u'117\h'|\n(44u'ior\h'|\n(45u'mwPo\h'|\n(46u'1\h'|\n(47u'118\h'|\n(48u'ior\h'|\n(49u'sP\h'|\n(50u'1\h'|\n(51u'119 -.ta \n(80u \n(81u \n(82u \n(83u \n(84u \n(85u \n(86u \n(87u \n(88u \n(89u \n(90u \n(91u -.nr 31 \n(.f +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u .nr 35 1m +.nr 31 \n(.f \&\h'|\n(40u'lae\h'|\n(41u'2\h'|\n(42u'\h'|\n(43u'120\h'|\n(44u'lae\h'|\n(45u'sw\h'|\n(46u'7\h'|\n(47u'121\h'|\n(48u'lal\h'|\n(49u'P2\h'|\n(50u'\h'|\n(51u'128 -.ta \n(80u \n(81u \n(82u \n(83u \n(84u \n(85u \n(86u \n(87u \n(88u \n(89u \n(90u \n(91u -.nr 31 \n(.f +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u .nr 35 1m -\&\h'|\n(40u'lal\h'|\n(41u'N2\h'|\n(42u'\h'|\n(43u'129\h'|\n(44u'lal\h'|\n(45u'm\h'|\n(46u'1\h'|\n(47u'130\h'|\n(48u'lal\h'|\n(49u'mN\h'|\n(50u'1\h'|\n(51u'131 -.ta \n(80u \n(81u \n(82u \n(83u \n(84u \n(85u \n(86u \n(87u \n(88u \n(89u \n(90u \n(91u .nr 31 \n(.f +\&\h'|\n(40u'lal\h'|\n(41u'N2\h'|\n(42u'\h'|\n(43u'129\h'|\n(44u'lal\h'|\n(45u'mP\h'|\n(46u'1\h'|\n(47u'130\h'|\n(48u'lal\h'|\n(49u'mN\h'|\n(50u'1\h'|\n(51u'131 +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u .nr 35 1m +.nr 31 \n(.f \&\h'|\n(40u'lal\h'|\n(41u'swP\h'|\n(42u'1\h'|\n(43u'132\h'|\n(44u'lal\h'|\n(45u'swN\h'|\n(46u'2\h'|\n(47u'133\h'|\n(48u'lar\h'|\n(49u'mwPo\h'|\n(50u'1\h'|\n(51u'135 -.ta \n(80u \n(81u \n(82u \n(83u \n(84u \n(85u \n(86u \n(87u \n(88u \n(89u \n(90u \n(91u -.nr 31 \n(.f +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u .nr 35 1m +.nr 31 \n(.f \&\h'|\n(40u'ldc\h'|\n(41u'mP\h'|\n(42u'1\h'|\n(43u'136\h'|\n(44u'lde\h'|\n(45u'w2\h'|\n(46u'\h'|\n(47u'137\h'|\n(48u'lde\h'|\n(49u'sw\h'|\n(50u'1\h'|\n(51u'138 -.ta \n(80u \n(81u \n(82u \n(83u \n(84u \n(85u \n(86u \n(87u \n(88u \n(89u \n(90u \n(91u -.nr 31 \n(.f +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u .nr 35 1m +.nr 31 \n(.f \&\h'|\n(40u'ldl\h'|\n(41u'mP\h'|\n(42u'1\h'|\n(43u'139\h'|\n(44u'ldl\h'|\n(45u'swN\h'|\n(46u'1\h'|\n(47u'140\h'|\n(48u'lfr\h'|\n(49u'mwPo\h'|\n(50u'2\h'|\n(51u'141 -.ta \n(80u \n(81u \n(82u \n(83u \n(84u \n(85u \n(86u \n(87u \n(88u \n(89u \n(90u \n(91u -.nr 31 \n(.f +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u .nr 35 1m +.nr 31 \n(.f \&\h'|\n(40u'lfr\h'|\n(41u'sP\h'|\n(42u'1\h'|\n(43u'143\h'|\n(44u'lil\h'|\n(45u'swN\h'|\n(46u'1\h'|\n(47u'144\h'|\n(48u'lil\h'|\n(49u'swP\h'|\n(50u'1\h'|\n(51u'145 -.ta \n(80u \n(81u \n(82u \n(83u \n(84u \n(85u \n(86u \n(87u \n(88u \n(89u \n(90u \n(91u -.nr 31 \n(.f +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u .nr 35 1m +.nr 31 \n(.f \&\h'|\n(40u'lil\h'|\n(41u'mwP\h'|\n(42u'2\h'|\n(43u'146\h'|\n(44u'lin\h'|\n(45u'2\h'|\n(46u'\h'|\n(47u'148\h'|\n(48u'lin\h'|\n(49u'sP\h'|\n(50u'1\h'|\n(51u'149 -.ta \n(80u \n(81u \n(82u \n(83u \n(84u \n(85u \n(86u \n(87u \n(88u \n(89u \n(90u \n(91u -.nr 31 \n(.f +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u .nr 35 1m +.nr 31 \n(.f \&\h'|\n(40u'lni\h'|\n(41u'-\h'|\n(42u'\h'|\n(43u'150\h'|\n(44u'loc\h'|\n(45u'2\h'|\n(46u'\h'|\n(47u'151\h'|\n(48u'loc\h'|\n(49u'mP\h'|\n(50u'34\h'|\n(51u'0 -.ta \n(80u \n(81u \n(82u \n(83u \n(84u \n(85u \n(86u \n(87u \n(88u \n(89u \n(90u \n(91u -.nr 31 \n(.f +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u .nr 35 1m +.nr 31 \n(.f \&\h'|\n(40u'loc\h'|\n(41u'mN\h'|\n(42u'1\h'|\n(43u'152\h'|\n(44u'loc\h'|\n(45u'sP\h'|\n(46u'1\h'|\n(47u'153\h'|\n(48u'loc\h'|\n(49u'sN\h'|\n(50u'1\h'|\n(51u'154 -.ta \n(80u \n(81u \n(82u \n(83u \n(84u \n(85u \n(86u \n(87u \n(88u \n(89u \n(90u \n(91u -.nr 31 \n(.f +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u .nr 35 1m +.nr 31 \n(.f \&\h'|\n(40u'loe\h'|\n(41u'w2\h'|\n(42u'\h'|\n(43u'155\h'|\n(44u'loe\h'|\n(45u'sw\h'|\n(46u'5\h'|\n(47u'156\h'|\n(48u'lof\h'|\n(49u'2\h'|\n(50u'\h'|\n(51u'161 -.ta \n(80u \n(81u \n(82u \n(83u \n(84u \n(85u \n(86u \n(87u \n(88u \n(89u \n(90u \n(91u -.nr 31 \n(.f +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u .nr 35 1m +.nr 31 \n(.f \&\h'|\n(40u'lof\h'|\n(41u'mwPo\h'|\n(42u'4\h'|\n(43u'162\h'|\n(44u'lof\h'|\n(45u'sP\h'|\n(46u'1\h'|\n(47u'166\h'|\n(48u'loi\h'|\n(49u'2\h'|\n(50u'\h'|\n(51u'167 -.ta \n(80u \n(81u \n(82u \n(83u \n(84u \n(85u \n(86u \n(87u \n(88u \n(89u \n(90u \n(91u -.nr 31 \n(.f +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u .nr 35 1m +.nr 31 \n(.f \&\h'|\n(40u'loi\h'|\n(41u'mPo\h'|\n(42u'1\h'|\n(43u'168\h'|\n(44u'loi\h'|\n(45u'mwPo\h'|\n(46u'4\h'|\n(47u'169\h'|\n(48u'loi\h'|\n(49u'sP\h'|\n(50u'1\h'|\n(51u'173 -.ta \n(80u \n(81u \n(82u \n(83u \n(84u \n(85u \n(86u \n(87u \n(88u \n(89u \n(90u \n(91u -.nr 31 \n(.f +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u .nr 35 1m +.nr 31 \n(.f \&\h'|\n(40u'lol\h'|\n(41u'wP2\h'|\n(42u'\h'|\n(43u'174\h'|\n(44u'lol\h'|\n(45u'wN2\h'|\n(46u'\h'|\n(47u'175\h'|\n(48u'lol\h'|\n(49u'mwP\h'|\n(50u'4\h'|\n(51u'176 -.ta \n(80u \n(81u \n(82u \n(83u \n(84u \n(85u \n(86u \n(87u \n(88u \n(89u \n(90u \n(91u -.nr 31 \n(.f +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u .nr 35 1m +.nr 31 \n(.f \&\h'|\n(40u'lol\h'|\n(41u'mwN\h'|\n(42u'8\h'|\n(43u'180\h'|\n(44u'lol\h'|\n(45u'swP\h'|\n(46u'1\h'|\n(47u'188\h'|\n(48u'lol\h'|\n(49u'swN\h'|\n(50u'1\h'|\n(51u'189 -.ta \n(80u \n(81u \n(82u \n(83u \n(84u \n(85u \n(86u \n(87u \n(88u \n(89u \n(90u \n(91u -.nr 31 \n(.f +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u .nr 35 1m +.nr 31 \n(.f \&\h'|\n(40u'lxa\h'|\n(41u'mPo\h'|\n(42u'1\h'|\n(43u'190\h'|\n(44u'lxl\h'|\n(45u'mPo\h'|\n(46u'2\h'|\n(47u'191\h'|\n(48u'mlf\h'|\n(49u'sP\h'|\n(50u'1\h'|\n(51u'193 -.ta \n(80u \n(81u \n(82u \n(83u \n(84u \n(85u \n(86u \n(87u \n(88u \n(89u \n(90u \n(91u -.nr 31 \n(.f +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u .nr 35 1m +.nr 31 \n(.f \&\h'|\n(40u'mli\h'|\n(41u'mwPo\h'|\n(42u'2\h'|\n(43u'194\h'|\n(44u'rck\h'|\n(45u'mwPo\h'|\n(46u'1\h'|\n(47u'196\h'|\n(48u'ret\h'|\n(49u'mwP\h'|\n(50u'2\h'|\n(51u'197 -.ta \n(80u \n(81u \n(82u \n(83u \n(84u \n(85u \n(86u \n(87u \n(88u \n(89u \n(90u \n(91u -.nr 31 \n(.f +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u .nr 35 1m +.nr 31 \n(.f \&\h'|\n(40u'ret\h'|\n(41u'sP\h'|\n(42u'1\h'|\n(43u'199\h'|\n(44u'rmi\h'|\n(45u'mwPo\h'|\n(46u'1\h'|\n(47u'200\h'|\n(48u'sar\h'|\n(49u'mwPo\h'|\n(50u'1\h'|\n(51u'201 -.ta \n(80u \n(81u \n(82u \n(83u \n(84u \n(85u \n(86u \n(87u \n(88u \n(89u \n(90u \n(91u -.nr 31 \n(.f +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u .nr 35 1m +.nr 31 \n(.f \&\h'|\n(40u'sbf\h'|\n(41u'sP\h'|\n(42u'1\h'|\n(43u'202\h'|\n(44u'sbi\h'|\n(45u'mwPo\h'|\n(46u'2\h'|\n(47u'203\h'|\n(48u'sdl\h'|\n(49u'swN\h'|\n(50u'1\h'|\n(51u'205 -.ta \n(80u \n(81u \n(82u \n(83u \n(84u \n(85u \n(86u \n(87u \n(88u \n(89u \n(90u \n(91u -.nr 31 \n(.f +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u .nr 35 1m +.nr 31 \n(.f \&\h'|\n(40u'set\h'|\n(41u'sP\h'|\n(42u'1\h'|\n(43u'206\h'|\n(44u'sil\h'|\n(45u'swN\h'|\n(46u'1\h'|\n(47u'207\h'|\n(48u'sil\h'|\n(49u'swP\h'|\n(50u'1\h'|\n(51u'208 -.ta \n(80u \n(81u \n(82u \n(83u \n(84u \n(85u \n(86u \n(87u \n(88u \n(89u \n(90u \n(91u -.nr 31 \n(.f +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u .nr 35 1m +.nr 31 \n(.f \&\h'|\n(40u'sli\h'|\n(41u'mwPo\h'|\n(42u'1\h'|\n(43u'209\h'|\n(44u'ste\h'|\n(45u'w2\h'|\n(46u'\h'|\n(47u'210\h'|\n(48u'ste\h'|\n(49u'sw\h'|\n(50u'3\h'|\n(51u'211 -.ta \n(80u \n(81u \n(82u \n(83u \n(84u \n(85u \n(86u \n(87u \n(88u \n(89u \n(90u \n(91u -.nr 31 \n(.f +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u .nr 35 1m +.nr 31 \n(.f \&\h'|\n(40u'stf\h'|\n(41u'2\h'|\n(42u'\h'|\n(43u'214\h'|\n(44u'stf\h'|\n(45u'mwPo\h'|\n(46u'2\h'|\n(47u'215\h'|\n(48u'stf\h'|\n(49u'sP\h'|\n(50u'1\h'|\n(51u'217 -.ta \n(80u \n(81u \n(82u \n(83u \n(84u \n(85u \n(86u \n(87u \n(88u \n(89u \n(90u \n(91u -.nr 31 \n(.f +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u .nr 35 1m +.nr 31 \n(.f \&\h'|\n(40u'sti\h'|\n(41u'mPo\h'|\n(42u'1\h'|\n(43u'218\h'|\n(44u'sti\h'|\n(45u'mwPo\h'|\n(46u'4\h'|\n(47u'219\h'|\n(48u'sti\h'|\n(49u'sP\h'|\n(50u'1\h'|\n(51u'223 -.ta \n(80u \n(81u \n(82u \n(83u \n(84u \n(85u \n(86u \n(87u \n(88u \n(89u \n(90u \n(91u -.nr 31 \n(.f +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u .nr 35 1m +.nr 31 \n(.f \&\h'|\n(40u'stl\h'|\n(41u'wP2\h'|\n(42u'\h'|\n(43u'224\h'|\n(44u'stl\h'|\n(45u'wN2\h'|\n(46u'\h'|\n(47u'225\h'|\n(48u'stl\h'|\n(49u'mwP\h'|\n(50u'2\h'|\n(51u'226 -.ta \n(80u \n(81u \n(82u \n(83u \n(84u \n(85u \n(86u \n(87u \n(88u \n(89u \n(90u \n(91u -.nr 31 \n(.f +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u .nr 35 1m +.nr 31 \n(.f \&\h'|\n(40u'stl\h'|\n(41u'mwN\h'|\n(42u'5\h'|\n(43u'228\h'|\n(44u'stl\h'|\n(45u'swN\h'|\n(46u'1\h'|\n(47u'233\h'|\n(48u'teq\h'|\n(49u'-\h'|\n(50u'\h'|\n(51u'234 -.ta \n(80u \n(81u \n(82u \n(83u \n(84u \n(85u \n(86u \n(87u \n(88u \n(89u \n(90u \n(91u -.nr 31 \n(.f +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u .nr 35 1m +.nr 31 \n(.f \&\h'|\n(40u'tgt\h'|\n(41u'-\h'|\n(42u'\h'|\n(43u'235\h'|\n(44u'tlt\h'|\n(45u'-\h'|\n(46u'\h'|\n(47u'236\h'|\n(48u'tne\h'|\n(49u'-\h'|\n(50u'\h'|\n(51u'237 -.ta \n(80u \n(81u \n(82u \n(83u \n(84u \n(85u \n(86u \n(87u \n(88u \n(89u \n(90u \n(91u -.nr 31 \n(.f +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u .nr 35 1m +.nr 31 \n(.f \&\h'|\n(40u'zeq\h'|\n(41u'2\h'|\n(42u'\h'|\n(43u'238\h'|\n(44u'zeq\h'|\n(45u'sP\h'|\n(46u'2\h'|\n(47u'239\h'|\n(48u'zer\h'|\n(49u'sP\h'|\n(50u'1\h'|\n(51u'241 -.ta \n(80u \n(81u \n(82u \n(83u \n(84u \n(85u \n(86u \n(87u \n(88u \n(89u \n(90u \n(91u -.nr 31 \n(.f +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u .nr 35 1m +.nr 31 \n(.f \&\h'|\n(40u'zge\h'|\n(41u'sP\h'|\n(42u'1\h'|\n(43u'242\h'|\n(44u'zgt\h'|\n(45u'sP\h'|\n(46u'1\h'|\n(47u'243\h'|\n(48u'zle\h'|\n(49u'sP\h'|\n(50u'1\h'|\n(51u'244 -.ta \n(80u \n(81u \n(82u \n(83u \n(84u \n(85u \n(86u \n(87u \n(88u \n(89u \n(90u \n(91u -.nr 31 \n(.f +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u .nr 35 1m +.nr 31 \n(.f \&\h'|\n(40u'zlt\h'|\n(41u'sP\h'|\n(42u'1\h'|\n(43u'245\h'|\n(44u'zne\h'|\n(45u'sP\h'|\n(46u'1\h'|\n(47u'246\h'|\n(48u'zne\h'|\n(49u'sN\h'|\n(50u'1\h'|\n(51u'247 -.ta \n(80u \n(81u \n(82u \n(83u \n(84u \n(85u \n(86u \n(87u \n(88u \n(89u \n(90u \n(91u -.nr 31 \n(.f +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u .nr 35 1m +.nr 31 \n(.f \&\h'|\n(40u'zre\h'|\n(41u'w2\h'|\n(42u'\h'|\n(43u'248\h'|\n(44u'zre\h'|\n(45u'sw\h'|\n(46u'1\h'|\n(47u'249\h'|\n(48u'zrl\h'|\n(49u'mwN\h'|\n(50u'2\h'|\n(51u'250 -.ta \n(80u \n(81u \n(82u \n(83u \n(84u \n(85u \n(86u \n(87u \n(88u \n(89u \n(90u \n(91u -.nr 31 \n(.f +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u .nr 35 1m +.nr 31 \n(.f \&\h'|\n(40u'zrl\h'|\n(41u'swN\h'|\n(42u'1\h'|\n(43u'252\h'|\n(44u'zrl\h'|\n(45u'wN2\h'|\n(46u'\h'|\n(47u'253\h'|\n(48u'aar\h'|\n(49u'e2\h'|\n(50u'\h'|\n(51u'0 -.ta \n(80u \n(81u \n(82u \n(83u \n(84u \n(85u \n(86u \n(87u \n(88u \n(89u \n(90u \n(91u -.nr 31 \n(.f +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u .nr 35 1m +.nr 31 \n(.f \&\h'|\n(40u'aar\h'|\n(41u'e-\h'|\n(42u'\h'|\n(43u'1\h'|\n(44u'adf\h'|\n(45u'e2\h'|\n(46u'\h'|\n(47u'2\h'|\n(48u'adf\h'|\n(49u'e-\h'|\n(50u'\h'|\n(51u'3 -.ta \n(80u \n(81u \n(82u \n(83u \n(84u \n(85u \n(86u \n(87u \n(88u \n(89u \n(90u \n(91u -.nr 31 \n(.f +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u .nr 35 1m +.nr 31 \n(.f \&\h'|\n(40u'adi\h'|\n(41u'e2\h'|\n(42u'\h'|\n(43u'4\h'|\n(44u'adi\h'|\n(45u'e-\h'|\n(46u'\h'|\n(47u'5\h'|\n(48u'ads\h'|\n(49u'e2\h'|\n(50u'\h'|\n(51u'6 -.ta \n(80u \n(81u \n(82u \n(83u \n(84u \n(85u \n(86u \n(87u \n(88u \n(89u \n(90u \n(91u -.nr 31 \n(.f +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u .nr 35 1m +.nr 31 \n(.f \&\h'|\n(40u'ads\h'|\n(41u'e-\h'|\n(42u'\h'|\n(43u'7\h'|\n(44u'adu\h'|\n(45u'e2\h'|\n(46u'\h'|\n(47u'8\h'|\n(48u'adu\h'|\n(49u'e-\h'|\n(50u'\h'|\n(51u'9 -.ta \n(80u \n(81u \n(82u \n(83u \n(84u \n(85u \n(86u \n(87u \n(88u \n(89u \n(90u \n(91u -.nr 31 \n(.f +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u .nr 35 1m +.nr 31 \n(.f \&\h'|\n(40u'and\h'|\n(41u'e2\h'|\n(42u'\h'|\n(43u'10\h'|\n(44u'and\h'|\n(45u'e-\h'|\n(46u'\h'|\n(47u'11\h'|\n(48u'asp\h'|\n(49u'ew2\h'|\n(50u'\h'|\n(51u'12 -.ta \n(80u \n(81u \n(82u \n(83u \n(84u \n(85u \n(86u \n(87u \n(88u \n(89u \n(90u \n(91u -.nr 31 \n(.f +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u .nr 35 1m +.nr 31 \n(.f \&\h'|\n(40u'ass\h'|\n(41u'e2\h'|\n(42u'\h'|\n(43u'13\h'|\n(44u'ass\h'|\n(45u'e-\h'|\n(46u'\h'|\n(47u'14\h'|\n(48u'bge\h'|\n(49u'e2\h'|\n(50u'\h'|\n(51u'15 -.ta \n(80u \n(81u \n(82u \n(83u \n(84u \n(85u \n(86u \n(87u \n(88u \n(89u \n(90u \n(91u -.nr 31 \n(.f +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u .nr 35 1m +.nr 31 \n(.f \&\h'|\n(40u'bgt\h'|\n(41u'e2\h'|\n(42u'\h'|\n(43u'16\h'|\n(44u'ble\h'|\n(45u'e2\h'|\n(46u'\h'|\n(47u'17\h'|\n(48u'blm\h'|\n(49u'e2\h'|\n(50u'\h'|\n(51u'18 -.ta \n(80u \n(81u \n(82u \n(83u \n(84u \n(85u \n(86u \n(87u \n(88u \n(89u \n(90u \n(91u -.nr 31 \n(.f +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u .nr 35 1m +.nr 31 \n(.f \&\h'|\n(40u'bls\h'|\n(41u'e2\h'|\n(42u'\h'|\n(43u'19\h'|\n(44u'bls\h'|\n(45u'e-\h'|\n(46u'\h'|\n(47u'20\h'|\n(48u'blt\h'|\n(49u'e2\h'|\n(50u'\h'|\n(51u'21 -.ta \n(80u \n(81u \n(82u \n(83u \n(84u \n(85u \n(86u \n(87u \n(88u \n(89u \n(90u \n(91u -.nr 31 \n(.f +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u .nr 35 1m +.nr 31 \n(.f \&\h'|\n(40u'bne\h'|\n(41u'e2\h'|\n(42u'\h'|\n(43u'22\h'|\n(44u'cai\h'|\n(45u'e-\h'|\n(46u'\h'|\n(47u'23\h'|\n(48u'cal\h'|\n(49u'e2\h'|\n(50u'\h'|\n(51u'24 -.ta \n(80u \n(81u \n(82u \n(83u \n(84u \n(85u \n(86u \n(87u \n(88u \n(89u \n(90u \n(91u -.nr 31 \n(.f +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u .nr 35 1m +.nr 31 \n(.f \&\h'|\n(40u'cfi\h'|\n(41u'e-\h'|\n(42u'\h'|\n(43u'25\h'|\n(44u'cfu\h'|\n(45u'e-\h'|\n(46u'\h'|\n(47u'26\h'|\n(48u'ciu\h'|\n(49u'e-\h'|\n(50u'\h'|\n(51u'27 -.ta \n(80u \n(81u \n(82u \n(83u \n(84u \n(85u \n(86u \n(87u \n(88u \n(89u \n(90u \n(91u -.nr 31 \n(.f +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u .nr 35 1m +.nr 31 \n(.f \&\h'|\n(40u'cmf\h'|\n(41u'e2\h'|\n(42u'\h'|\n(43u'28\h'|\n(44u'cmf\h'|\n(45u'e-\h'|\n(46u'\h'|\n(47u'29\h'|\n(48u'cmi\h'|\n(49u'e2\h'|\n(50u'\h'|\n(51u'30 -.ta \n(80u \n(81u \n(82u \n(83u \n(84u \n(85u \n(86u \n(87u \n(88u \n(89u \n(90u \n(91u -.nr 31 \n(.f +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u .nr 35 1m +.nr 31 \n(.f \&\h'|\n(40u'cmi\h'|\n(41u'e-\h'|\n(42u'\h'|\n(43u'31\h'|\n(44u'cms\h'|\n(45u'e2\h'|\n(46u'\h'|\n(47u'32\h'|\n(48u'cms\h'|\n(49u'e-\h'|\n(50u'\h'|\n(51u'33 -.ta \n(80u \n(81u \n(82u \n(83u \n(84u \n(85u \n(86u \n(87u \n(88u \n(89u \n(90u \n(91u -.nr 31 \n(.f +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u .nr 35 1m +.nr 31 \n(.f \&\h'|\n(40u'cmu\h'|\n(41u'e2\h'|\n(42u'\h'|\n(43u'34\h'|\n(44u'cmu\h'|\n(45u'e-\h'|\n(46u'\h'|\n(47u'35\h'|\n(48u'com\h'|\n(49u'e2\h'|\n(50u'\h'|\n(51u'36 -.ta \n(80u \n(81u \n(82u \n(83u \n(84u \n(85u \n(86u \n(87u \n(88u \n(89u \n(90u \n(91u -.nr 31 \n(.f +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u .nr 35 1m +.nr 31 \n(.f \&\h'|\n(40u'com\h'|\n(41u'e-\h'|\n(42u'\h'|\n(43u'37\h'|\n(44u'csa\h'|\n(45u'e2\h'|\n(46u'\h'|\n(47u'38\h'|\n(48u'csa\h'|\n(49u'e-\h'|\n(50u'\h'|\n(51u'39 -.ta \n(80u \n(81u \n(82u \n(83u \n(84u \n(85u \n(86u \n(87u \n(88u \n(89u \n(90u \n(91u -.nr 31 \n(.f +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u .nr 35 1m +.nr 31 \n(.f \&\h'|\n(40u'csb\h'|\n(41u'e2\h'|\n(42u'\h'|\n(43u'40\h'|\n(44u'csb\h'|\n(45u'e-\h'|\n(46u'\h'|\n(47u'41\h'|\n(48u'cuf\h'|\n(49u'e-\h'|\n(50u'\h'|\n(51u'42 -.ta \n(80u \n(81u \n(82u \n(83u \n(84u \n(85u \n(86u \n(87u \n(88u \n(89u \n(90u \n(91u -.nr 31 \n(.f +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u .nr 35 1m +.nr 31 \n(.f \&\h'|\n(40u'cui\h'|\n(41u'e-\h'|\n(42u'\h'|\n(43u'43\h'|\n(44u'cuu\h'|\n(45u'e-\h'|\n(46u'\h'|\n(47u'44\h'|\n(48u'dee\h'|\n(49u'ew2\h'|\n(50u'\h'|\n(51u'45 -.ta \n(80u \n(81u \n(82u \n(83u \n(84u \n(85u \n(86u \n(87u \n(88u \n(89u \n(90u \n(91u -.nr 31 \n(.f +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u .nr 35 1m +.nr 31 \n(.f \&\h'|\n(40u'del\h'|\n(41u'ewP2\h'|\n(42u'\h'|\n(43u'46\h'|\n(44u'del\h'|\n(45u'ewN2\h'|\n(46u'\h'|\n(47u'47\h'|\n(48u'dup\h'|\n(49u'e2\h'|\n(50u'\h'|\n(51u'48 -.ta \n(80u \n(81u \n(82u \n(83u \n(84u \n(85u \n(86u \n(87u \n(88u \n(89u \n(90u \n(91u -.nr 31 \n(.f +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u .nr 35 1m +.nr 31 \n(.f \&\h'|\n(40u'dus\h'|\n(41u'e2\h'|\n(42u'\h'|\n(43u'49\h'|\n(44u'dus\h'|\n(45u'e-\h'|\n(46u'\h'|\n(47u'50\h'|\n(48u'dvf\h'|\n(49u'e2\h'|\n(50u'\h'|\n(51u'51 -.ta \n(80u \n(81u \n(82u \n(83u \n(84u \n(85u \n(86u \n(87u \n(88u \n(89u \n(90u \n(91u -.nr 31 \n(.f +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u .nr 35 1m +.nr 31 \n(.f \&\h'|\n(40u'dvf\h'|\n(41u'e-\h'|\n(42u'\h'|\n(43u'52\h'|\n(44u'dvi\h'|\n(45u'e2\h'|\n(46u'\h'|\n(47u'53\h'|\n(48u'dvi\h'|\n(49u'e-\h'|\n(50u'\h'|\n(51u'54 -.ta \n(80u \n(81u \n(82u \n(83u \n(84u \n(85u \n(86u \n(87u \n(88u \n(89u \n(90u \n(91u -.nr 31 \n(.f +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u .nr 35 1m +.nr 31 \n(.f \&\h'|\n(40u'dvu\h'|\n(41u'e2\h'|\n(42u'\h'|\n(43u'55\h'|\n(44u'dvu\h'|\n(45u'e-\h'|\n(46u'\h'|\n(47u'56\h'|\n(48u'fef\h'|\n(49u'e2\h'|\n(50u'\h'|\n(51u'57 -.ta \n(80u \n(81u \n(82u \n(83u \n(84u \n(85u \n(86u \n(87u \n(88u \n(89u \n(90u \n(91u -.nr 31 \n(.f +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u .nr 35 1m +.nr 31 \n(.f \&\h'|\n(40u'fef\h'|\n(41u'e-\h'|\n(42u'\h'|\n(43u'58\h'|\n(44u'fif\h'|\n(45u'e2\h'|\n(46u'\h'|\n(47u'59\h'|\n(48u'fif\h'|\n(49u'e-\h'|\n(50u'\h'|\n(51u'60 -.ta \n(80u \n(81u \n(82u \n(83u \n(84u \n(85u \n(86u \n(87u \n(88u \n(89u \n(90u \n(91u -.nr 31 \n(.f +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u .nr 35 1m +.nr 31 \n(.f \&\h'|\n(40u'inl\h'|\n(41u'ewP2\h'|\n(42u'\h'|\n(43u'61\h'|\n(44u'inl\h'|\n(45u'ewN2\h'|\n(46u'\h'|\n(47u'62\h'|\n(48u'inn\h'|\n(49u'e2\h'|\n(50u'\h'|\n(51u'63 -.ta \n(80u \n(81u \n(82u \n(83u \n(84u \n(85u \n(86u \n(87u \n(88u \n(89u \n(90u \n(91u -.nr 31 \n(.f +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u .nr 35 1m +.nr 31 \n(.f \&\h'|\n(40u'inn\h'|\n(41u'e-\h'|\n(42u'\h'|\n(43u'64\h'|\n(44u'ior\h'|\n(45u'e2\h'|\n(46u'\h'|\n(47u'65\h'|\n(48u'ior\h'|\n(49u'e-\h'|\n(50u'\h'|\n(51u'66 -.ta \n(80u \n(81u \n(82u \n(83u \n(84u \n(85u \n(86u \n(87u \n(88u \n(89u \n(90u \n(91u -.nr 31 \n(.f +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u .nr 35 1m +.nr 31 \n(.f \&\h'|\n(40u'lar\h'|\n(41u'e2\h'|\n(42u'\h'|\n(43u'67\h'|\n(44u'lar\h'|\n(45u'e-\h'|\n(46u'\h'|\n(47u'68\h'|\n(48u'ldc\h'|\n(49u'e2\h'|\n(50u'\h'|\n(51u'69 -.ta \n(80u \n(81u \n(82u \n(83u \n(84u \n(85u \n(86u \n(87u \n(88u \n(89u \n(90u \n(91u -.nr 31 \n(.f +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u .nr 35 1m +.nr 31 \n(.f \&\h'|\n(40u'ldf\h'|\n(41u'e2\h'|\n(42u'\h'|\n(43u'70\h'|\n(44u'ldl\h'|\n(45u'ewP2\h'|\n(46u'\h'|\n(47u'71\h'|\n(48u'ldl\h'|\n(49u'ewN2\h'|\n(50u'\h'|\n(51u'72 -.ta \n(80u \n(81u \n(82u \n(83u \n(84u \n(85u \n(86u \n(87u \n(88u \n(89u \n(90u \n(91u -.nr 31 \n(.f +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u .nr 35 1m +.nr 31 \n(.f \&\h'|\n(40u'lfr\h'|\n(41u'e2\h'|\n(42u'\h'|\n(43u'73\h'|\n(44u'lil\h'|\n(45u'ewP2\h'|\n(46u'\h'|\n(47u'74\h'|\n(48u'lil\h'|\n(49u'ewN2\h'|\n(50u'\h'|\n(51u'75 -.ta \n(80u \n(81u \n(82u \n(83u \n(84u \n(85u \n(86u \n(87u \n(88u \n(89u \n(90u \n(91u -.nr 31 \n(.f +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u .nr 35 1m +.nr 31 \n(.f \&\h'|\n(40u'lim\h'|\n(41u'e-\h'|\n(42u'\h'|\n(43u'76\h'|\n(44u'los\h'|\n(45u'e2\h'|\n(46u'\h'|\n(47u'77\h'|\n(48u'los\h'|\n(49u'e-\h'|\n(50u'\h'|\n(51u'78 -.ta \n(80u \n(81u \n(82u \n(83u \n(84u \n(85u \n(86u \n(87u \n(88u \n(89u \n(90u \n(91u -.nr 31 \n(.f +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u .nr 35 1m +.nr 31 \n(.f \&\h'|\n(40u'lor\h'|\n(41u'esP\h'|\n(42u'1\h'|\n(43u'79\h'|\n(44u'lpi\h'|\n(45u'e2\h'|\n(46u'\h'|\n(47u'80\h'|\n(48u'lxa\h'|\n(49u'e2\h'|\n(50u'\h'|\n(51u'81 -.ta \n(80u \n(81u \n(82u \n(83u \n(84u \n(85u \n(86u \n(87u \n(88u \n(89u \n(90u \n(91u -.nr 31 \n(.f +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u .nr 35 1m +.nr 31 \n(.f \&\h'|\n(40u'lxl\h'|\n(41u'e2\h'|\n(42u'\h'|\n(43u'82\h'|\n(44u'mlf\h'|\n(45u'e2\h'|\n(46u'\h'|\n(47u'83\h'|\n(48u'mlf\h'|\n(49u'e-\h'|\n(50u'\h'|\n(51u'84 -.ta \n(80u \n(81u \n(82u \n(83u \n(84u \n(85u \n(86u \n(87u \n(88u \n(89u \n(90u \n(91u -.nr 31 \n(.f +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u .nr 35 1m +.nr 31 \n(.f \&\h'|\n(40u'mli\h'|\n(41u'e2\h'|\n(42u'\h'|\n(43u'85\h'|\n(44u'mli\h'|\n(45u'e-\h'|\n(46u'\h'|\n(47u'86\h'|\n(48u'mlu\h'|\n(49u'e2\h'|\n(50u'\h'|\n(51u'87 -.ta \n(80u \n(81u \n(82u \n(83u \n(84u \n(85u \n(86u \n(87u \n(88u \n(89u \n(90u \n(91u -.nr 31 \n(.f +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u .nr 35 1m +.nr 31 \n(.f \&\h'|\n(40u'mlu\h'|\n(41u'e-\h'|\n(42u'\h'|\n(43u'88\h'|\n(44u'mon\h'|\n(45u'e-\h'|\n(46u'\h'|\n(47u'89\h'|\n(48u'ngf\h'|\n(49u'e2\h'|\n(50u'\h'|\n(51u'90 -.ta \n(80u \n(81u \n(82u \n(83u \n(84u \n(85u \n(86u \n(87u \n(88u \n(89u \n(90u \n(91u -.nr 31 \n(.f +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u .nr 35 1m +.nr 31 \n(.f \&\h'|\n(40u'ngf\h'|\n(41u'e-\h'|\n(42u'\h'|\n(43u'91\h'|\n(44u'ngi\h'|\n(45u'e2\h'|\n(46u'\h'|\n(47u'92\h'|\n(48u'ngi\h'|\n(49u'e-\h'|\n(50u'\h'|\n(51u'93 -.ta \n(80u \n(81u \n(82u \n(83u \n(84u \n(85u \n(86u \n(87u \n(88u \n(89u \n(90u \n(91u -.nr 31 \n(.f +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u .nr 35 1m +.nr 31 \n(.f \&\h'|\n(40u'nop\h'|\n(41u'e-\h'|\n(42u'\h'|\n(43u'94\h'|\n(44u'rck\h'|\n(45u'e2\h'|\n(46u'\h'|\n(47u'95\h'|\n(48u'rck\h'|\n(49u'e-\h'|\n(50u'\h'|\n(51u'96 -.ta \n(80u \n(81u \n(82u \n(83u \n(84u \n(85u \n(86u \n(87u \n(88u \n(89u \n(90u \n(91u -.nr 31 \n(.f +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u .nr 35 1m +.nr 31 \n(.f \&\h'|\n(40u'ret\h'|\n(41u'e2\h'|\n(42u'\h'|\n(43u'97\h'|\n(44u'rmi\h'|\n(45u'e2\h'|\n(46u'\h'|\n(47u'98\h'|\n(48u'rmi\h'|\n(49u'e-\h'|\n(50u'\h'|\n(51u'99 -.ta \n(80u \n(81u \n(82u \n(83u \n(84u \n(85u \n(86u \n(87u \n(88u \n(89u \n(90u \n(91u -.nr 31 \n(.f +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u .nr 35 1m +.nr 31 \n(.f \&\h'|\n(40u'rmu\h'|\n(41u'e2\h'|\n(42u'\h'|\n(43u'100\h'|\n(44u'rmu\h'|\n(45u'e-\h'|\n(46u'\h'|\n(47u'101\h'|\n(48u'rol\h'|\n(49u'e2\h'|\n(50u'\h'|\n(51u'102 -.ta \n(80u \n(81u \n(82u \n(83u \n(84u \n(85u \n(86u \n(87u \n(88u \n(89u \n(90u \n(91u -.nr 31 \n(.f +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u .nr 35 1m +.nr 31 \n(.f \&\h'|\n(40u'rol\h'|\n(41u'e-\h'|\n(42u'\h'|\n(43u'103\h'|\n(44u'ror\h'|\n(45u'e2\h'|\n(46u'\h'|\n(47u'104\h'|\n(48u'ror\h'|\n(49u'e-\h'|\n(50u'\h'|\n(51u'105 -.ta \n(80u \n(81u \n(82u \n(83u \n(84u \n(85u \n(86u \n(87u \n(88u \n(89u \n(90u \n(91u -.nr 31 \n(.f +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u .nr 35 1m +.nr 31 \n(.f \&\h'|\n(40u'rtt\h'|\n(41u'e-\h'|\n(42u'\h'|\n(43u'106\h'|\n(44u'sar\h'|\n(45u'e2\h'|\n(46u'\h'|\n(47u'107\h'|\n(48u'sar\h'|\n(49u'e-\h'|\n(50u'\h'|\n(51u'108 -.ta \n(80u \n(81u \n(82u \n(83u \n(84u \n(85u \n(86u \n(87u \n(88u \n(89u \n(90u \n(91u -.nr 31 \n(.f +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u .nr 35 1m +.nr 31 \n(.f \&\h'|\n(40u'sbf\h'|\n(41u'e2\h'|\n(42u'\h'|\n(43u'109\h'|\n(44u'sbf\h'|\n(45u'e-\h'|\n(46u'\h'|\n(47u'110\h'|\n(48u'sbi\h'|\n(49u'e2\h'|\n(50u'\h'|\n(51u'111 -.ta \n(80u \n(81u \n(82u \n(83u \n(84u \n(85u \n(86u \n(87u \n(88u \n(89u \n(90u \n(91u -.nr 31 \n(.f +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u .nr 35 1m +.nr 31 \n(.f \&\h'|\n(40u'sbi\h'|\n(41u'e-\h'|\n(42u'\h'|\n(43u'112\h'|\n(44u'sbs\h'|\n(45u'e2\h'|\n(46u'\h'|\n(47u'113\h'|\n(48u'sbs\h'|\n(49u'e-\h'|\n(50u'\h'|\n(51u'114 -.ta \n(80u \n(81u \n(82u \n(83u \n(84u \n(85u \n(86u \n(87u \n(88u \n(89u \n(90u \n(91u -.nr 31 \n(.f +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u .nr 35 1m +.nr 31 \n(.f \&\h'|\n(40u'sbu\h'|\n(41u'e2\h'|\n(42u'\h'|\n(43u'115\h'|\n(44u'sbu\h'|\n(45u'e-\h'|\n(46u'\h'|\n(47u'116\h'|\n(48u'sde\h'|\n(49u'e2\h'|\n(50u'\h'|\n(51u'117 -.ta \n(80u \n(81u \n(82u \n(83u \n(84u \n(85u \n(86u \n(87u \n(88u \n(89u \n(90u \n(91u -.nr 31 \n(.f +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u .nr 35 1m +.nr 31 \n(.f \&\h'|\n(40u'sdf\h'|\n(41u'e2\h'|\n(42u'\h'|\n(43u'118\h'|\n(44u'sdl\h'|\n(45u'ewP2\h'|\n(46u'\h'|\n(47u'119\h'|\n(48u'sdl\h'|\n(49u'ewN2\h'|\n(50u'\h'|\n(51u'120 -.ta \n(80u \n(81u \n(82u \n(83u \n(84u \n(85u \n(86u \n(87u \n(88u \n(89u \n(90u \n(91u -.nr 31 \n(.f +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u .nr 35 1m +.nr 31 \n(.f \&\h'|\n(40u'set\h'|\n(41u'e2\h'|\n(42u'\h'|\n(43u'121\h'|\n(44u'set\h'|\n(45u'e-\h'|\n(46u'\h'|\n(47u'122\h'|\n(48u'sig\h'|\n(49u'e-\h'|\n(50u'\h'|\n(51u'123 -.ta \n(80u \n(81u \n(82u \n(83u \n(84u \n(85u \n(86u \n(87u \n(88u \n(89u \n(90u \n(91u -.nr 31 \n(.f +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u .nr 35 1m +.nr 31 \n(.f \&\h'|\n(40u'sil\h'|\n(41u'ewP2\h'|\n(42u'\h'|\n(43u'124\h'|\n(44u'sil\h'|\n(45u'ewN2\h'|\n(46u'\h'|\n(47u'125\h'|\n(48u'sim\h'|\n(49u'e-\h'|\n(50u'\h'|\n(51u'126 -.ta \n(80u \n(81u \n(82u \n(83u \n(84u \n(85u \n(86u \n(87u \n(88u \n(89u \n(90u \n(91u -.nr 31 \n(.f +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u .nr 35 1m +.nr 31 \n(.f \&\h'|\n(40u'sli\h'|\n(41u'e2\h'|\n(42u'\h'|\n(43u'127\h'|\n(44u'sli\h'|\n(45u'e-\h'|\n(46u'\h'|\n(47u'128\h'|\n(48u'slu\h'|\n(49u'e2\h'|\n(50u'\h'|\n(51u'129 -.ta \n(80u \n(81u \n(82u \n(83u \n(84u \n(85u \n(86u \n(87u \n(88u \n(89u \n(90u \n(91u -.nr 31 \n(.f +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u .nr 35 1m +.nr 31 \n(.f \&\h'|\n(40u'slu\h'|\n(41u'e-\h'|\n(42u'\h'|\n(43u'130\h'|\n(44u'sri\h'|\n(45u'e2\h'|\n(46u'\h'|\n(47u'131\h'|\n(48u'sri\h'|\n(49u'e-\h'|\n(50u'\h'|\n(51u'132 -.ta \n(80u \n(81u \n(82u \n(83u \n(84u \n(85u \n(86u \n(87u \n(88u \n(89u \n(90u \n(91u -.nr 31 \n(.f +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u .nr 35 1m +.nr 31 \n(.f \&\h'|\n(40u'sru\h'|\n(41u'e2\h'|\n(42u'\h'|\n(43u'133\h'|\n(44u'sru\h'|\n(45u'e-\h'|\n(46u'\h'|\n(47u'134\h'|\n(48u'sti\h'|\n(49u'e2\h'|\n(50u'\h'|\n(51u'135 -.ta \n(80u \n(81u \n(82u \n(83u \n(84u \n(85u \n(86u \n(87u \n(88u \n(89u \n(90u \n(91u -.nr 31 \n(.f +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u .nr 35 1m +.nr 31 \n(.f \&\h'|\n(40u'sts\h'|\n(41u'e2\h'|\n(42u'\h'|\n(43u'136\h'|\n(44u'sts\h'|\n(45u'e-\h'|\n(46u'\h'|\n(47u'137\h'|\n(48u'str\h'|\n(49u'esP\h'|\n(50u'1\h'|\n(51u'138 -.ta \n(80u \n(81u \n(82u \n(83u \n(84u \n(85u \n(86u \n(87u \n(88u \n(89u \n(90u \n(91u -.nr 31 \n(.f +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u .nr 35 1m +.nr 31 \n(.f \&\h'|\n(40u'tge\h'|\n(41u'e-\h'|\n(42u'\h'|\n(43u'139\h'|\n(44u'tle\h'|\n(45u'e-\h'|\n(46u'\h'|\n(47u'140\h'|\n(48u'trp\h'|\n(49u'e-\h'|\n(50u'\h'|\n(51u'141 -.ta \n(80u \n(81u \n(82u \n(83u \n(84u \n(85u \n(86u \n(87u \n(88u \n(89u \n(90u \n(91u -.nr 31 \n(.f +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u .nr 35 1m +.nr 31 \n(.f \&\h'|\n(40u'xor\h'|\n(41u'e2\h'|\n(42u'\h'|\n(43u'142\h'|\n(44u'xor\h'|\n(45u'e-\h'|\n(46u'\h'|\n(47u'143\h'|\n(48u'zer\h'|\n(49u'e2\h'|\n(50u'\h'|\n(51u'144 -.ta \n(80u \n(81u \n(82u \n(83u \n(84u \n(85u \n(86u \n(87u \n(88u \n(89u \n(90u \n(91u -.nr 31 \n(.f +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u .nr 35 1m +.nr 31 \n(.f \&\h'|\n(40u'zer\h'|\n(41u'e-\h'|\n(42u'\h'|\n(43u'145\h'|\n(44u'zge\h'|\n(45u'e2\h'|\n(46u'\h'|\n(47u'146\h'|\n(48u'zgt\h'|\n(49u'e2\h'|\n(50u'\h'|\n(51u'147 -.ta \n(80u \n(81u \n(82u \n(83u \n(84u \n(85u \n(86u \n(87u \n(88u \n(89u \n(90u \n(91u -.nr 31 \n(.f +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u .nr 35 1m +.nr 31 \n(.f \&\h'|\n(40u'zle\h'|\n(41u'e2\h'|\n(42u'\h'|\n(43u'148\h'|\n(44u'zlt\h'|\n(45u'e2\h'|\n(46u'\h'|\n(47u'149\h'|\n(48u'zne\h'|\n(49u'e2\h'|\n(50u'\h'|\n(51u'150 -.ta \n(80u \n(81u \n(82u \n(83u \n(84u \n(85u \n(86u \n(87u \n(88u \n(89u \n(90u \n(91u -.nr 31 \n(.f +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u .nr 35 1m +.nr 31 \n(.f \&\h'|\n(40u'zrf\h'|\n(41u'e2\h'|\n(42u'\h'|\n(43u'151\h'|\n(44u'zrf\h'|\n(45u'e-\h'|\n(46u'\h'|\n(47u'152\h'|\n(48u'zrl\h'|\n(49u'ewP2\h'|\n(50u'\h'|\n(51u'153 -.ta \n(80u \n(81u \n(82u \n(83u \n(84u \n(85u \n(86u \n(87u \n(88u \n(89u \n(90u \n(91u -.nr 31 \n(.f +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u .nr 35 1m +.nr 31 \n(.f \&\h'|\n(40u'dch\h'|\n(41u'e-\h'|\n(42u'\h'|\n(43u'154\h'|\n(44u'exg\h'|\n(45u'esP\h'|\n(46u'1\h'|\n(47u'155\h'|\n(48u'exg\h'|\n(49u'e2\h'|\n(50u'\h'|\n(51u'156 -.ta \n(80u \n(81u \n(82u \n(83u \n(84u \n(85u \n(86u \n(87u \n(88u \n(89u \n(90u \n(91u -.nr 31 \n(.f +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u .nr 35 1m +.nr 31 \n(.f \&\h'|\n(40u'exg\h'|\n(41u'e-\h'|\n(42u'\h'|\n(43u'157\h'|\n(44u'lpb\h'|\n(45u'e-\h'|\n(46u'\h'|\n(47u'158\h'|\n(48u'gto\h'|\n(49u'e2\h'|\n(50u'\h'|\n(51u'159 -.ta \n(80u \n(81u \n(82u \n(83u \n(84u \n(85u \n(86u \n(87u \n(88u \n(89u \n(90u \n(91u -.nr 31 \n(.f +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u .nr 35 1m -\&\h'|\n(40u'ldc\h'|\n(41u'4\h'|\n(42u'\h'|\n(43u'0\h'|\n(44u'\h'|\n(45u'\h'|\n(46u'\h'|\n(47u'\h'|\n(48u'\h'|\n(49u'\h'|\n(50u'\h'|\n(51u' +.nr 31 \n(.f +\&\h'|\n(40u'ldc\h'|\n(41u'4\h'|\n(42u'\h'|\n(43u'0\h'|\n(44u'lae\h'|\n(45u'4\h'|\n(46u'\h'|\n(47u'1\h'|\n(48u'lal\h'|\n(49u'P4\h'|\n(50u'\h'|\n(51u'2 +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u +.nr 35 1m +.nr 31 \n(.f +\&\h'|\n(40u'lal\h'|\n(41u'N4\h'|\n(42u'\h'|\n(43u'3\h'|\n(44u'lde\h'|\n(45u'w4\h'|\n(46u'\h'|\n(47u'4\h'|\n(48u'ldf\h'|\n(49u'4\h'|\n(50u'\h'|\n(51u'5 +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u +.nr 35 1m +.nr 31 \n(.f +\&\h'|\n(40u'ldl\h'|\n(41u'wP4\h'|\n(42u'\h'|\n(43u'6\h'|\n(44u'ldl\h'|\n(45u'wN4\h'|\n(46u'\h'|\n(47u'7\h'|\n(48u'lil\h'|\n(49u'wP4\h'|\n(50u'\h'|\n(51u'8 +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u +.nr 35 1m +.nr 31 \n(.f +\&\h'|\n(40u'lil\h'|\n(41u'wN4\h'|\n(42u'\h'|\n(43u'9\h'|\n(44u'loc\h'|\n(45u'4\h'|\n(46u'\h'|\n(47u'10\h'|\n(48u'loe\h'|\n(49u'w4\h'|\n(50u'\h'|\n(51u'11 +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u +.nr 35 1m +.nr 31 \n(.f +\&\h'|\n(40u'lof\h'|\n(41u'4\h'|\n(42u'\h'|\n(43u'12\h'|\n(44u'lol\h'|\n(45u'wP4\h'|\n(46u'\h'|\n(47u'13\h'|\n(48u'lol\h'|\n(49u'wN4\h'|\n(50u'\h'|\n(51u'14 +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u +.nr 35 1m +.nr 31 \n(.f +\&\h'|\n(40u'lpi\h'|\n(41u'4\h'|\n(42u'\h'|\n(43u'15\h'|\n(44u'adp\h'|\n(45u'4\h'|\n(46u'\h'|\n(47u'16\h'|\n(48u'asp\h'|\n(49u'w4\h'|\n(50u'\h'|\n(51u'17 +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u +.nr 35 1m +.nr 31 \n(.f +\&\h'|\n(40u'beq\h'|\n(41u'4\h'|\n(42u'\h'|\n(43u'18\h'|\n(44u'bge\h'|\n(45u'4\h'|\n(46u'\h'|\n(47u'19\h'|\n(48u'bgt\h'|\n(49u'4\h'|\n(50u'\h'|\n(51u'20 +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u +.nr 35 1m +.nr 31 \n(.f +\&\h'|\n(40u'ble\h'|\n(41u'4\h'|\n(42u'\h'|\n(43u'21\h'|\n(44u'blm\h'|\n(45u'4\h'|\n(46u'\h'|\n(47u'22\h'|\n(48u'blt\h'|\n(49u'4\h'|\n(50u'\h'|\n(51u'23 +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u +.nr 35 1m +.nr 31 \n(.f +\&\h'|\n(40u'bne\h'|\n(41u'4\h'|\n(42u'\h'|\n(43u'24\h'|\n(44u'bra\h'|\n(45u'4\h'|\n(46u'\h'|\n(47u'25\h'|\n(48u'cal\h'|\n(49u'4\h'|\n(50u'\h'|\n(51u'26 +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u +.nr 35 1m +.nr 31 \n(.f +\&\h'|\n(40u'dee\h'|\n(41u'w4\h'|\n(42u'\h'|\n(43u'27\h'|\n(44u'del\h'|\n(45u'wP4\h'|\n(46u'\h'|\n(47u'28\h'|\n(48u'del\h'|\n(49u'wN4\h'|\n(50u'\h'|\n(51u'29 +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u +.nr 35 1m +.nr 31 \n(.f +\&\h'|\n(40u'fil\h'|\n(41u'4\h'|\n(42u'\h'|\n(43u'30\h'|\n(44u'gto\h'|\n(45u'4\h'|\n(46u'\h'|\n(47u'31\h'|\n(48u'ine\h'|\n(49u'w4\h'|\n(50u'\h'|\n(51u'32 +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u +.nr 35 1m +.nr 31 \n(.f +\&\h'|\n(40u'inl\h'|\n(41u'wP4\h'|\n(42u'\h'|\n(43u'33\h'|\n(44u'inl\h'|\n(45u'wN4\h'|\n(46u'\h'|\n(47u'34\h'|\n(48u'lin\h'|\n(49u'4\h'|\n(50u'\h'|\n(51u'35 +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u +.nr 35 1m +.nr 31 \n(.f +\&\h'|\n(40u'sde\h'|\n(41u'4\h'|\n(42u'\h'|\n(43u'36\h'|\n(44u'sdf\h'|\n(45u'4\h'|\n(46u'\h'|\n(47u'37\h'|\n(48u'sdl\h'|\n(49u'wP4\h'|\n(50u'\h'|\n(51u'38 +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u +.nr 35 1m +.nr 31 \n(.f +\&\h'|\n(40u'sdl\h'|\n(41u'wN4\h'|\n(42u'\h'|\n(43u'39\h'|\n(44u'sil\h'|\n(45u'wP4\h'|\n(46u'\h'|\n(47u'40\h'|\n(48u'sil\h'|\n(49u'wN4\h'|\n(50u'\h'|\n(51u'41 +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u +.nr 35 1m +.nr 31 \n(.f +\&\h'|\n(40u'ste\h'|\n(41u'w4\h'|\n(42u'\h'|\n(43u'42\h'|\n(44u'stf\h'|\n(45u'4\h'|\n(46u'\h'|\n(47u'43\h'|\n(48u'stl\h'|\n(49u'wP4\h'|\n(50u'\h'|\n(51u'44 +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u +.nr 35 1m +.nr 31 \n(.f +\&\h'|\n(40u'stl\h'|\n(41u'wN4\h'|\n(42u'\h'|\n(43u'45\h'|\n(44u'zeq\h'|\n(45u'4\h'|\n(46u'\h'|\n(47u'46\h'|\n(48u'zge\h'|\n(49u'4\h'|\n(50u'\h'|\n(51u'47 +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u +.nr 35 1m +.nr 31 \n(.f +\&\h'|\n(40u'zgt\h'|\n(41u'4\h'|\n(42u'\h'|\n(43u'48\h'|\n(44u'zle\h'|\n(45u'4\h'|\n(46u'\h'|\n(47u'49\h'|\n(48u'zlt\h'|\n(49u'4\h'|\n(50u'\h'|\n(51u'50 +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u +.nr 35 1m +.nr 31 \n(.f +\&\h'|\n(40u'zne\h'|\n(41u'4\h'|\n(42u'\h'|\n(43u'51\h'|\n(44u'zre\h'|\n(45u'w4\h'|\n(46u'\h'|\n(47u'52\h'|\n(48u'zrl\h'|\n(49u'wP4\h'|\n(50u'\h'|\n(51u'53 +.ta \n(66u \n(67u \n(68u \n(69u \n(70u \n(71u \n(72u \n(73u \n(74u \n(75u \n(76u \n(77u +.nr 35 1m +.nr 31 \n(.f +\&\h'|\n(40u'zrl\h'|\n(41u'wN4\h'|\n(42u'\h'|\n(43u'54\h'|\n(44u'\h'|\n(45u'\h'|\n(46u'\h'|\n(47u'\h'|\n(48u'\h'|\n(49u'\h'|\n(50u'\h'|\n(51u' .fc .nr T. 1 .T# 1 .35 .TE -.if \n-(b.=0 .nr c. \n(.c-\n(d.-102 +.if \n-(b.=0 .nr c. \n(.c-\n(d.-120 diff --git a/doc/em/mach.nr b/doc/em/mach.nr index 1374eff3..6cb6b14f 100644 --- a/doc/em/mach.nr +++ b/doc/em/mach.nr @@ -21,19 +21,21 @@ two groups of 256 secondary opcodes each. .A EM instructions without arguments have a single opcode assigned, possibly escaped: -.DS +.Dr 6 |--------------| | opcode | |--------------| - or +.De + or +.Dr 6 |--------------|--------------| | escape | opcode | |--------------|--------------| -.DE +.De The encoding for instructions with an argument is more complex. Several instructions have an address from the global data area as argument. @@ -42,37 +44,39 @@ and negative arguments. .N 1 There is always an opcode that takes the next two bytes as argument, high byte first: -.DS +.Dr 6 |--------------|--------------|--------------| | opcode | hibyte | lobyte | |--------------|--------------|--------------| - or +.De + or +.Dr 6 |--------------|--------------|--------------|--------------| | escape | opcode | hibyte | lobyte | |--------------|--------------|--------------|--------------| -.DE -.DS +.De An extra escape is provided for instructions with four or eight byte arguments. +.Dr 6 |--------------|--------------|--------------| |--------------| | ESCAPE | opcode | hibyte |...| lobyte | |--------------|--------------|--------------| |--------------| -.DE +.De For most instructions some argument values predominate. The most frequent combinations of instruction and argument will be encoded in a single byte, called a mini: -.DS +.Dr 6 |---------------| |opcode+argument| (mini) |---------------| -.DE +.De The number of minis is restricted, because only 254 primary opcodes are available. Many instructions have the bulk of their arguments @@ -85,19 +89,21 @@ that combines the instruction and the high byte of the argument into a single opcode. These opcodes are called shorties. Shorties may be escaped. -.DS +.Dr 6 |--------------|--------------| | opcode+high | lobyte | (shortie) |--------------|--------------| - or +.De + or +.Dr 6 |--------------|--------------|--------------| | escape | opcode+high | lobyte | |--------------|--------------|--------------| -.DE +.De Escaped shorties are useless if the normal encoding has a primary opcode. Note that for some instruction-argument combinations several different encodings are available. @@ -266,66 +272,66 @@ contain a null terminated ASCII string .PE 1 .DE 0 .VS 1 1 -.DS +.Dr 6 ------------------- | 0 | n | repeat last initialization n times ------------------- -.DE -.DS +.De +.Dr 4 --------- | 1 | m | m uninitialized words --------- -.DE -.DS +.De +.Dr 6 ____________ / bytes \e ----------------- ----- | 2 | m | b | b |...| b | m initialized bytes ----------------- ----- -.DE -.DS +.De +.Dr 6 _________ / word \e ----------------------- | 3 | m | w |... m initialized wordsized integers ----------------------- -.DE -.DS +.De +.Dr 6 _________ / pointer \e ----------------------- | 4 | m | p |... m initialized data pointers ----------------------- -.DE -.DS +.De +.Dr 6 _________ / pointer \e ----------------------- | 5 | m | p |... m initialized instruction pointers ----------------------- -.DE -.DS +.De +.Dr 6 ____________ / bytes \e ------------------------- | 6 | m | b | b |...| b | initialized integer of size m ------------------------- -.DE -.DS +.De +.Dr 6 ____________ / bytes \e ------------------------- | 7 | m | b | b |...| b | initialized unsigned of size m ------------------------- -.DE -.DS +.De +.Dr 6 ____________ / string \e ------------------------- | 8 | m | s | initialized float of size m ------------------------- -.DE 3 +.De 3 .PS - 8 .PT type~0: If the last initialization initialized k bytes starting diff --git a/doc/em/macr.nr b/doc/em/macr.nr index 14c628c4..e97acc3f 100644 --- a/doc/em/macr.nr +++ b/doc/em/macr.nr @@ -1,8 +1,7 @@ .so /usr/lib/tmac/tmac.kun -.SS 6 +.SS 10 +.if n .LL 78 .RP -.PL 12i 11i -.LL 89 .MS T E \!.TL '%''' .ME @@ -14,3 +13,24 @@ .ME .SM S1 B .SM S2 B +.\" below are three simple macros to get the drawings right +.\" added by Dick Grune +.de Dr \" Drawing $1 (size) +.N 1 +.NE \\$1 +.NA +.ft CW \" constant spacing +.lg 0 \" no ligatures +.. +.de Df \" Drawing Footer +.N 1 +.FT P +.CS +.lg 1 +.. +.de De \" Drawing End $1 (lines) +.Df \" if it hasn't happened yet +.CE +.AD +.N \\$1 +.. diff --git a/doc/em/mapping.nr b/doc/em/mapping.nr index fbd0ff11..f940c44f 100644 --- a/doc/em/mapping.nr +++ b/doc/em/mapping.nr @@ -14,46 +14,46 @@ with 64K bytes of address space. Here we use a member of the EM family with 2-byte word and pointer size. The most straightforward layout is shown in figure 2. -.N 1 -.DS - 65534 -> |-------------------------------| +.Dr 40 + 65534 \-> |-------------------------------| |///////////////////////////////| |//// unimplemented memory /////| |///////////////////////////////| - ML -> |-------------------------------| + ML \-> |-------------------------------| | | - | | <- LB + | | <\- LB | stack and local area | | | - |-------------------------------| <- SP + |-------------------------------| <\- SP |///////////////////////////////| |//////// inaccessible /////////| |///////////////////////////////| - |-------------------------------| <- HP + |-------------------------------| <\- HP | | | heap area | | | | | - HB -> |-------------------------------| + HB \-> |-------------------------------| | | | global data area | | | - EB -> |-------------------------------| + EB \-> |-------------------------------| | | - | program text | <- PC + | program text | <\- PC | | | ( and tables ) | | | | | - PB -> |-------------------------------| + PB \-> |-------------------------------| |///////////////////////////////| |////////// undefined //////////| |///////////////////////////////| - 0 -> |-------------------------------| - - Figure 2. Memory layout showing typical register - positions during execution of an EM program. -.DE 2 + 0 \-> |-------------------------------| +.Df +Figure 2. Memory layout showing typical register +positions during execution of an EM program. +.De +.N 1 The base registers for the various memory pieces can be stored in target machine registers or memory. .IS @@ -123,8 +123,7 @@ upside down, as shown in figure 3. This is possible because the pointer format is explicitly undefined. The first element of a word array will have a lower physical address than the second element. -.N 2 -.DS +.Dr 18 | | | | | EB=60 | | ^ | | | | | | @@ -140,18 +139,18 @@ lower physical address than the second element. | | | | Type A Type B -.sp 2 - Figure 3. Two possible memory implementations. - Numbers within the boxes are EM addresses. - The other numbers are physical addresses. -.DE 2 -.A 0 0 +.Df +Figure 3. Two possible memory implementations. +Numbers within the boxes are EM addresses. +The other numbers are physical addresses. +.De +.A 1 0 So, we have two different EM memory implementations: .IS .PS - 4 -.PT A~- +.PT A~\- stack downwards -.PT B~- +.PT B~\- stack upwards .PE .IE @@ -188,7 +187,7 @@ ADP:3:pop:r0:pop:r0 ::push:r0:push:r0 LOI:1:pop:r0:pop:r0 -::-::neg:r0 +::\-::neg:r0 ::clr:r1:clr:r1 ::bisb:eb(r0),r1:bisb:eb(r0),r1 ::push:r1:push:r1 diff --git a/doc/em/mkdispatch.c b/doc/em/mkdispatch.c new file mode 100644 index 00000000..b9db3434 --- /dev/null +++ b/doc/em/mkdispatch.c @@ -0,0 +1,470 @@ +/* + * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + * + */ + +#include "ip_spec.h" +#include +#include "em_spec.h" +#include "em_flag.h" + +#ifndef NORCSID +static char rcs_id[] = "$Header$" ; +#endif + +/* This program reads the human readable interpreter specification + and produces a efficient machine representation that can be + translated by a C-compiler. +*/ + +#define NOTAB 600 /* The max no of interpreter specs */ +#define ESCAP1 256 +#define ESCAP2 257 + +struct opform intable[NOTAB] ; +struct opform *lastform = intable-1 ; + +int nerror = 0 ; +int atend = 0 ; +int line = 1 ; + +extern char em_mnem[][4] ; +char esca1[] = "escape1" ; +char esca2[] = "escape2" ; +#define ename(no) ((no)==ESCAP1?esca1:(no)==ESCAP2?esca2:em_mnem[(no)]) + +extern char em_flag[] ; + +main(argc,argv) char **argv ; { + if ( argc>1 ) { + if ( freopen(argv[1],"r",stdin)==NULL) { + fatal("Cannot open %s",argv[1]) ; + } + } + if ( argc>2 ) { + if ( freopen(argv[2],"w",stdout)==NULL) { + fatal("Cannot create %s",argv[2]) ; + } + } + if ( argc>3 ) { + fatal("%s [ file [ file ] ]",argv[0]) ; + } + atend=0 ; + readin(); + atend=1 ; + checkall(); + if ( nerror==0 ) { + writeout(); + } + exit(nerror) ; +} + +readin() { + register struct opform *nextform ; + char *ident(); + char *firstid ; + + for ( nextform=intable ; + !feof(stdin) && nextform<&intable[NOTAB] ; ) { + firstid=ident() ; + if ( *firstid=='\n' || feof(stdin) ) continue ; + lastform=nextform ; + nextform->i_opcode = getmnem(firstid) ; + nextform->i_flag = decflag(ident()) ; + switch ( nextform->i_flag&OPTYPE ) { + case OPMINI: + case OPSHORT: + nextform->i_num = atoi(ident()) ; + break ; + } + nextform->i_low = atoi(ident()) ; + if ( *ident()!='\n' ) { + int c ; + error("End of line expected"); + while ( (c=readchar())!='\n' && c!=EOF ) ; + } + nextform++ ; + } + if ( !feof(stdin) ) fatal("Internal table too small") ; +} + +char *ident() { + /* skip spaces and tabs, anything up to space,tab or eof is + a identifier. + Anything from # to end-of-line is an end-of-line. + End-of-line is an identifier all by itself. + */ + + static char array[200] ; + register int c ; + register char *cc ; + + do { + c=readchar() ; + } while ( c==' ' || c=='\t' ) ; + for ( cc=array ; cc<&array[(sizeof array) - 1] ; cc++ ) { + if ( c=='#' ) { + do { + c=readchar(); + } while ( c!='\n' && c!=EOF ) ; + } + *cc = c ; + if ( c=='\n' && cc==array ) break ; + c=readchar() ; + if ( c=='\n' ) { + pushback(c) ; + break ; + } + if ( c==' ' || c=='\t' || c==EOF ) break ; + } + *++cc=0 ; + return array ; +} + +int getmnem(str) char *str ; { + char (*ptr)[4] ; + + for ( ptr = em_mnem ; *ptr<= &em_mnem[sp_lmnem-sp_fmnem][0] ; ptr++ ) { + if ( strcmp(*ptr,str)==0 ) return (ptr-em_mnem) ; + } + error("Illegal mnemonic") ; + return 0 ; +} + +error(str,a1,a2,a3,a4,a5,a6) /* VARARGS1 */ char *str ; { + if ( !atend ) fprintf(stderr,"line %d: ",line) ; + fprintf(stderr,str,a1,a2,a3,a4,a5,a6) ; + fprintf(stderr,"\n"); + nerror++ ; +} + +mess(str,a1,a2,a3,a4,a5,a6) /* VARARGS1 */ char *str ; { + if ( !atend ) fprintf(stderr,"line %d: ",line) ; + fprintf(stderr,str,a1,a2,a3,a4,a5,a6) ; + fprintf(stderr,"\n"); +} + +fatal(str,a1,a2,a3,a4,a5,a6) /* VARARGS1 */ char *str ; { + error(str,a1,a2,a3,a4,a5,a6) ; + exit(1) ; +} + +#define ILLGL -1 + +check(val) int val ; { + if ( val!=ILLGL ) error("Illegal flag combination") ; +} + +int decflag(str) char *str ; { + int type ; + int escape ; + int range ; + int wordm ; + int notzero ; + + type=escape=range=wordm=notzero= ILLGL ; + while ( *str ) switch ( *str++ ) { + case 'm' : + check(type) ; type=OPMINI ; break ; + case 's' : + check(type) ; type=OPSHORT ; break ; + case '-' : + check(type) ; type=OPNO ; break ; + case '1' : + check(type) ; type=OP8 ; break ; + case '2' : + check(type) ; type=OP16 ; break ; + case '4' : + check(type) ; type=OP32 ; break ; + case '8' : + check(type) ; type=OP64 ; break ; + case 'e' : + check(escape) ; escape=0 ; break ; + case 'N' : + check(range) ; range= 2 ; break ; + case 'P' : + check(range) ; range= 1 ; break ; + case 'w' : + check(wordm) ; wordm=0 ; break ; + case 'o' : + check(notzero) ; notzero=0 ; break ; + default : + error("Unknown flag") ; + } + if ( type==ILLGL ) error("Type must be specified") ; + switch ( type ) { + case OP64 : + case OP32 : + if ( escape!=ILLGL ) error("Conflicting escapes") ; + escape=ILLGL ; + case OP16 : + case OP8 : + case OPSHORT : + case OPNO : + if ( notzero!=ILLGL ) mess("Improbable OPNZ") ; + if ( type==OPNO && range!=ILLGL ) { + mess("No operand in range") ; + } + } + if ( escape!=ILLGL ) type|=OPESC ; + if ( wordm!=ILLGL ) type|=OPWORD ; + switch ( range) { + case ILLGL : type|=OP_BOTH ; + if ( type==OPMINI || type==OPSHORT ) + error("Minies and shorties must have P or N") ; + break ; + case 1 : type|=OP_POS ; break ; + case 2 : type|=OP_NEG ; break ; + } + if ( notzero!=ILLGL ) type|=OPNZ ; + return type ; +} + +/* ----------- checking --------------*/ + +int ecodes[256],codes[256],lcodes[256] ; +char eflags[256], flags[256]; +int elows[256], lows[256]; + +#define NMNEM (sp_lmnem-sp_fmnem+1) +#define MUST 1 +#define MAY 2 +#define FORB 3 + +char negc[NMNEM], zc[NMNEM], posc[NMNEM] ; + +checkall() { + register i,flag ; + register struct opform *next ; + int opc,low ; + + for ( i=0 ; ii_flag&0377 ; + opc = next->i_opcode&0377 ; + low = next->i_low&0377 ; + chkc(flag,low,opc,low) ; + switch(flag&OPTYPE) { + case OPNO : zc[opc]++ ; break ; + case OPMINI : + case OPSHORT : + for ( i=1 ; i<((next->i_num)&0377) ; i++ ) { + chkc(flag,low+i,opc,low) ; + } + if ( !(em_flag[opc]&PAR_G) && + (flag&OPRANGE)==OP_BOTH) { + mess("Mini's and shorties should have P or N"); + } + break ; + case OP8 : + error("OP8 is removed") ; + break ; + case OP16 : + if ( flag&OP_NEG ) + negc[opc]++ ; + else if ( flag&OP_POS ) + posc[opc]++ ; + break ; + case OP32 : + case OP64 : + break ; + default : + error("Illegal type") ; + break ; + } + } + atend=1 ; + for ( i=0 ; i<256 ; i++ ) if ( codes[i]== -1 ) { + mess("interpreter opcode %d not used",i) ; + } + for ( opc=0 ; opc1 ) mess("More then one OPNO for %s",ename(emc)) ; + if ( posc[emc]>1 ) mess("More then one OP16(pos) for %s",ename(emc)) ; + if ( negc[emc]>1 ) mess("More then one OP16(neg) for %s",ename(emc)) ; + switch(zf) { + case MUST: + if ( zc[emc]==0 ) mess("No OPNO for %s",ename(emc)) ; + break ; + case FORB: + if ( zc[emc]==1 ) mess("Forbidden OPNO for %s",ename(emc)) ; + break ; + } + switch(pf) { + case MUST: + if ( posc[emc]==0 ) mess("No OP16(pos) for %s",ename(emc)) ; + break ; + case FORB: + if ( posc[emc]==1 ) + mess("Forbidden OP16(pos) for %s",ename(emc)) ; + break ; + } + switch(nf) { + case MUST: + if ( negc[emc]==0 ) mess("No OP16(neg) for %s",ename(emc)) ; + break ; + case FORB: + if ( negc[emc]==1 ) + mess("Forbidden OP16(neg) for %s",ename(emc)) ; + break ; + } +} + +static int pushchar ; +static int pushf ; + +int readchar() { + int c ; + + if ( pushf ) { + pushf=0 ; + c = pushchar ; + } else { + if ( feof(stdin) ) return EOF ; + c=getc(stdin) ; + } + if ( c=='\n' ) line++ ; + return c ; +} + +pushback(c) { + if ( pushf ) { + fatal("Double pushback") ; + } + pushf++ ; + pushchar=c ; + if ( c=='\n' ) line-- ; +} + +writeout() { + register int i; + + printf("DISPATCH1"); + for (i = 0; i < 256;) { + if (!(i % 8)) printf("\n%d", i); + printf("\t%s", ename(codes[i])); + if (i < 254) { + prx(flags[i],lows[i],i); + } + i++; + } + + printf("\nDISPATCH2"); + for (i = 0; i < 256;) { + if (ecodes[i] != -1) { + if (!(i % 8)) printf("\n%d", i); + printf("\t%s", ename(ecodes[i])); + prx(eflags[i],elows[i],i); + } + else break; + i++; + } + + printf("\nDISPATCH3"); + i = 0; + while (lcodes[i] != -1) { + if (!(i % 8)) printf("\n%d", i); + printf("\t%s", ename(ecodes[i])); + i++; + } + while (i++ % 8) putchar('\t'); + putchar('\n'); +} + +prx(flg,low,opc) + register int flg; +{ + int arg = opc - low; + + putchar('.'); + switch(flg&OPTYPE) { + case OPNO: + putchar('z'); + break; + case OP16: + if (flg&OP_POS) putchar('p'); + else if (flg&OP_NEG) putchar('n'); + else putchar('l'); + if (flg&OPWORD) putchar('w'); + break; + case OPSHORT: + if (flg & OPWORD) putchar('w'); + else putchar('s'); + /* fall through */ + case OPMINI: + if (flg & OPNZ) arg++; + if (flg & OP_NEG) arg = -arg - 1; + printf("%d",arg); + if((flg&OPTYPE) == OPMINI && (flg & OPWORD)) putchar('W'); + } +} diff --git a/doc/em/title.nr b/doc/em/title.nr index 348d55db..20f5a891 100644 --- a/doc/em/title.nr +++ b/doc/em/title.nr @@ -1,6 +1,6 @@ .po 0 .TP 1 -.ll 79 +.ll 79n .sp 15 .ce 4 DESCRIPTION OF A MACHINE diff --git a/doc/em/traps.nr b/doc/em/traps.nr new file mode 100644 index 00000000..b6d41261 --- /dev/null +++ b/doc/em/traps.nr @@ -0,0 +1,171 @@ +.SN 9 +.VS 1 0 +.BP +.S1 "TRAPS AND INTERRUPTS" +EM provides a means for the user program to catch all traps +generated by the program itself, the hardware, or external conditions. +This mechanism uses five instructions: LIM, SIM, SIG, TRP and RTT. +This section of the manual may be omitted on the first reading since it +presupposes knowledge of the EM instruction set. +.P +The action taken when a trap occures is determined by the value +of an internal EM trap register. +This register contains a pointer to a procedure. +Initially the pointer used is zero and all traps halt the +program with, hopefully, a useful message to the outside world. +The SIG instruction can be used to alter the trap register, +it pops a procedure pointer from the +stack into the trap register. +When a trap occurs after storing a nonzero value in the trap +register, the procedure pointed to by the trap register +is called with the trap number +as the only parameter (see below). +SIG returns the previous value of the trap register on the +stack. +Two consecutive SIGs are a no-op. +When a trap occurs, the trap register is reset to its initial +condition, to prevent recursive traps from hanging the machine up, +e.g. stack overflow in the stack overflow handling procedure. +.P +The runtime systems for some languages need to ignore some EM +traps. +EM offers a feature called the ignore mask. +It contains one bit for each of the lowest 16 trap numbers. +The bits are numbered 0 to 15, with the least significant bit +having number 0. +If a certain bit is 1 the corresponding trap never +occurs and processing simply continues. +The actions performed by the offending instruction are +described by the Pascal program in appendix A. +.N +If the bit is 0, traps are not ignored. +The instructions LIM and SIM allow copying and replacement of +the ignore mask.~ +.P +The TRP instruction generates a trap, the trap number being found on the +stack. +This is, among other things, +useful for library procedures and runtime systems. +It can also be used by a low level trap procedure to pass the trap to a +higher level one (see example below). +.P +The RTT instruction returns from the trap procedure and continues after the +trap. +In the list below all traps marked with an asterisk ('*') are +considered to be fatal and it is explicitly undefined what happens if +you try to restart after the trap. +.P +The way a trap procedure is called is completely compatible +with normal calling conventions. The only way a trap procedure +differs from normal procedures is the return. It has to use RTT instead +of RET. This is necessary because the complete runtime status is saved on the +stack before calling the procedure and all this status has to be reloaded. +Error numbers are in the range 0 to 252. +The trap numbers are divided into three categories: +.IS 4 +.N 1 +.PS - 10 +.PT ~~0\-~63 +EM machine errors, e.g. illegal instruction. +.PS - 8 +.PT ~0\-15 +maskable +.PT 16\-63 +not maskable +.PE +.PT ~64\-127 +Reserved for use by compilers, run time systems, etc. +.PT 128\-252 +Available for user programs. +.PE 1 +.IE +EM machine errors are numbered as follows: +.DS I 5 +.TS +tab(@); +n l l. +0@EARRAY@Array bound error +1@ERANGE@Range bound error +2@ESET@Set bound error +3@EIOVFL@Integer overflow +4@EFOVFL@Floating overflow +5@EFUNFL@Floating underflow +6@EIDIVZ@Divide by 0 +7@EFDIVZ@Divide by 0.0 +8@EIUND@Undefined integer +9@EFUND@Undefined float +10@ECONV@Conversion error +16*@ESTACK@Stack overflow +17*@EHEAP@Heap overflow +18*@EILLINS@Illegal instruction +19*@EODDZ@Illegal size argument +20*@ECASE@Case error +21*@EMEMFLT@Addressing non existent memory +22*@EBADPTR@Bad pointer used +23*@EBADPC@Program counter out of range +24@EBADLAE@Bad argument of LAE +25@EBADMON@Bad monitor call +26@EBADLIN@Argument of LIN too high +27@EBADGTO@GTO descriptor error +.TE +.DE 0 +.P +As an example, +suppose a subprocedure has to be written to do a numeric +calculation. +When an overflow occurs the computation has to be stopped and +the higher level procedure must be resumed. +This can be programmed as follows using the mechanism described above: +.DS B +.ta 1n 24n + mes 2,2,2 ; set sizes +ersave + bss 2,0,0 ; Room to save previous value of trap procedure +msave + bss 2,0,0 ; Room to save previous value of trap mask + + pro calcule,0 ; entry point + lxl 0 ; fill in non-local goto descriptor with LB + ste jmpbuf+4 + lor 1 ; and SP + ste jmpbuf+2 + lim ; get current ignore mask + ste msave ; save it + lim + loc 16 ; bit for EFOVFL + ior 2 ; set in mask + sim ; ignore EFOVFL from now on + lpi $catch ; load procedure identifier + sig ; catch wil get all traps now + ste ersave ; save previous trap procedure identifier + ; perform calculation now, possibly generating overflow +1 ; label jumped to by catch procedure + loe ersave ; get old trap procedure + sig ; refer all following trap to old procedure + asp 2 ; remove result of sig + loe msave ; restore previous mask + sim ; done now + ; load result of calculation + ret 2 ; return result +jmpbuf + con *1,0,0 + end +.DE 0 +.VS 1 1 +.DS +Example of catch procedure +.ta 1n 24n + pro catch,0 ; Local procedure that must catch the overflow trap + lol 2 ; Load trap number + loc 4 ; check for overflow + bne *1 ; if other trap, call higher trap procedure + gto jmpbuf ; return to procedure calcule +1 ; other trap has occurred + loe ersave ; previous trap procedure + sig ; other procedure will get the traps now + asp 2 ; remove the result of sig + lol 2 ; stack trap number + trp ; call other trap procedure + rtt ; if other procedure returns, do the same + end +.DE diff --git a/doc/em/types.nr b/doc/em/types.nr index c014a78a..27cb2c9d 100644 --- a/doc/em/types.nr +++ b/doc/em/types.nr @@ -33,7 +33,7 @@ restrictions imposed on the representation of the types used. A number \fBn\fP used in these paragraphs indicates the size of the object in \fIbits\fP. .S2 "Unsigned integers" -The range of unsigned integers is 0..2\v'-0.5m'\fBn\fP\v'0.5m'-1. +The range of unsigned integers is 0..2\v'-0.5m'\fBn\fP\v'0.5m'\-1. A binary representation is assumed. The order of the bits within an object is knowingly left unspecified. @@ -60,21 +60,22 @@ and expect a correct result. We assume existence of at least single word unsigned arithmetic in any implementation. .S2 "Signed Integers" -The range of signed integers is -2\v'-0.5m'\fBn\fP-1\v'0.5m'~..~2\v'-0.5m'\fBn\fP-1\v'0.5m'-1, +The range of signed integers is +\-2\v'-0.5m'\fBn\fP\-1\v'0.5m'~..~2\v'-0.5m'\fBn\fP\-1\v'0.5m'\-1, in other words the range of signed integers of \fBn\fP bits using two's complement arithmetic. -The representation is the same as for unsigned integers except -the range 2\v'-0.5m'\fBn\fP-1\v'0.5m'~..~2\v'-0.5m'\fBn\fP\v'0.5m'-1 is mapped on the -range -2\v'-0.5m'\fBn\fP-1\v'0.5m'~..~-1. +The representation is the same as for unsigned integers except the range +2\v'-0.5m'\fBn\fP\-1\v'0.5m'~..~2\v'-0.5m'\fBn\fP\v'0.5m'\-1 is mapped on the +range \-2\v'-0.5m'\fBn\fP\-1\v'0.5m'~..~\-1. In other words, the most significant bit is used as sign bit. The convert instructions between signed and unsigned integers of the same size can be used to catch errors. .A -The value -2\v'-0.5m'\fBn\fP-1\v'0.5m' is used for undefined +The value \-2\v'-0.5m'\fBn\fP\-1\v'0.5m' is used for undefined signed integers. EM implementations should trap when this value is used in an operation on signed integers. -The instruction mask, accessed with SIM and LIM -~see chapter 9~- , +The instruction mask, accessed with SIM and LIM \-~see chapter 9~\- , can be used to disable such traps. .A We assume existence of at least single word signed arithmetic @@ -126,5 +127,5 @@ the value of the unsigned integer is the summation of the Example: a 2-word bit set (wordsize 2) containing the elements 1, 6, 8, 15, 18, 21, 27 and 28 is composed of two integers, e.g. at addresses 40 and 42. -The word at 40 contains the value 33090 (or~-32446), +The word at 40 contains the value 33090 (or~\-32446), the word at 42 contains the value 6180. diff --git a/doc/i80.doc b/doc/i80.doc index 232e5aec..8de34e9c 100644 --- a/doc/i80.doc +++ b/doc/i80.doc @@ -1,5 +1,6 @@ -." $Header$ +. \" $Header$ .RP +.ND April 1985 .TL Back end table for the Intel 8080 micro-processor .AU @@ -97,15 +98,11 @@ The 8080 microprocessor provides five flip-flops used as condition flags .br The sign bit S is set (cleared) by certain instructions when the most significant bit of the result of an operation equals one (zero). -.br The zero bit Z is set (cleared) by certain operations when the 8-bit result of an operation equals (does not equal) zero. -.br The parity bit P is set (cleared) if the 8-bit result of an operation includes an even (odd) number of ones. -.br C is the normal carry bit. -.br AC is an auxiliary carry that indicates whether there has been a carry out of bit 3 of the accumulator. This auxiliary carry is used only by the DAA instruction, which @@ -130,14 +127,12 @@ of register-pair HL. .NH 3 Register addressing .PP - -With each intruction using register addressing, +With each instruction using register addressing, only one register is specified (except for the MOV instruction), although in many of them the accumulator is implied as second operand. Examples are CMP E, which compares register E with the accumulator, and DCR B, which decrements register B. -.br A few instructions deal with 16 bit register-pairs: examples are DCX B, which decrements register-pair BC and the PUSH and POP instructions. @@ -173,7 +168,7 @@ The high order byte is stored at the highest address. THE 8080 BACK END TABLE .PP The back end table is designed as described in [5]. -So for an overall design of a back end table I refer to this document. +For an overall design of a back end table I refer to this document. .br This section deals with problems encountered in writing the 8080 back-end table. @@ -185,30 +180,30 @@ Constant definitions Word size (EM_WSIZE) and pointer size (EM_PSIZE) are both defined as two bytes. The hole between AB and LB (EM_BSIZE) is four bytes: only the -return address and the localbase are saved. +return address and the local base are saved. .NH 2 Registers and their properties .PP All properties have the default size of two bytes, because one-byte registers also cover two bytes when put on the real stack. .sp 1 -The next considerations led to the choise of register-pair BC -as localbase. -Though saving the localbase in memory would leave one more register-pair +The next considerations led to the choice of register-pair BC +as local base. +Though saving the local base in memory would leave one more register-pair available as scratch register, it would slow down instructions as 'lol' and 'stl' too much. -So a register-pair should be sacrificed as localbase. +So a register-pair should be sacrificed as local base. Because a back-end without a free register-pair HL is completely -broken-winged, the only reasonable choises are BC and DE. -Though the choise between them might seem arbitrary at first sight, +broken-winged, the only reasonable choices are BC and DE. +Though the choice between them might seem arbitrary at first sight, there is a difference between register-pairs BC and DE: the instruction XCHG exchanges the contents of register-pairs DE and HL. When DE and HL are both heavily used on the fake-stack, this instruction -is very usefull. -Since it won't be usefull too often to exchange HL with the localbase +is very useful. +Since it won't be useful too often to exchange HL with the local base and since an instruction exchanging BC and HL does not exist, BC is -chosen as localbase. +chosen as local base. .sp 1 Many of the register properties are never mentioned in the PATTERNS part of the table. @@ -219,7 +214,7 @@ The properties really used in the PATTERNS part are: the accumulator only .IP reg: any of the registers A, D, E, H or L. Of course the registers B and C which are -used as localbase don't possess this property. +used as local base don't possess this property. When there is a single register on the fake-stack, its value is always considered non-negative. .IP dereg: @@ -228,12 +223,12 @@ register-pair DE only register-pair HL only .IP hl_or_de: register-pairs HL and DE both have this property -.IP localbase: +.IP local base: used only once (i.e. in the EM-instruction 'str 0') .PP .sp 1 The stackpointer SP and the processor status word PSW have to be -defined explicitely because they are needed in some instructions +defined explicitly because they are needed in some instructions (i.e. SP in LXI, DCX and INX and PSW in PUSH and POP). .br It doesn't matter that the processor status word is not just register A @@ -252,7 +247,6 @@ Compared with many other back-end tables, there are only a small number of different tokens (four). Reasons are the limited addressing modes of the 8080 microprocessor, no index registers etc. -.br For example to translate the EM-instruction .DS lol 10 @@ -260,16 +254,15 @@ lol 10 the next 8080 instructions are generated: .DS L LXI H,10 /* load registers pair HL with value 10 */ -DAD B /* add localbase (BC) to HL */ +DAD B /* add local base (BC) to HL */ MOV E,M /* load E with byte pointed to by HL */ INX H /* increment HL */ MOV D,M /* load D with next byte */ .DE -Of course, instead of emitting code immmediately, it could be postponed +Of course, instead of emitting code immediately, it could be postponed by placing something like a {LOCAL,10} on the fake-stack, but some day the above mentioned code will have to be generated, so a LOCAL-token is -hardly usefull. -.br +hardly useful. See also the comment on the load instructions. .NH 2 Sets @@ -295,13 +288,13 @@ In fact it usually takes 3 extra time periods when this register indirect mode is used instead of register mode, but since the costs are not completely orthogonal this results in small deficiencies for the DCR, INR and MOV instructions. -Although it is not particularly usefull these deficiencies are +Although it is not particularly useful these deficiencies are corrected in the INSTRUCTIONS part, by treating the register indirect -mode seperately. +mode separately. .sp 1 The costs of the conditional call and return instructions really depend on whether or not the call resp. return is actually made. -Unimportant. +However, this is not important to the behaviour of the back end. .sp 1 Instructions not used in this table have been commented out. Of course many of them are used in the library routines. @@ -316,7 +309,7 @@ The TESTS section is only included to refrain .B cgg from complaining. .NH 2 -Stackingrules +Stacking rules .PP When, for example, the token {const2,10} has to be stacked while no free register-pair is available, the next code is generated: @@ -341,12 +334,11 @@ this instruction in fact exchanges the contents of these register-pairs. Before the coercion is carried out other appearances of DE and HL on the fake-stack will be moved to the real stack, because in -the INSTRUCTION-part is told that XCHG destroyes the contents +the INSTRUCTION-part is told that XCHG destroys the contents of both DE and HL. -.br The coercion transposing one register-pair to another one by emitting two MOV-instructions, will be used only if -one of the register-pairs is the localbase. +one of the register-pairs is the local base. .NH 2 Patterns .PP @@ -361,7 +353,7 @@ gen lhld {label,$1} yields hl .DE the 'uses'-clause could have been omitted because .B cgg -knows that LHLD destroyes register-pair HL. +knows that LHLD destroys register-pair HL. .sp 1 Since there is only one register with property 'hlreg', there is no difference between 'uses hlreg' (allocate a @@ -369,7 +361,7 @@ register with property 'hlreg') and 'kills hlreg' (remove all registers with property 'hlreg' from the fake-stack). The same applies for the property 'dereg'. .br -As a consequence 'kills' is rarely used in this back-end table. +Consequently 'kills' is rarely used in this back-end table. .NH 3 Group 1: Load instructions .PP @@ -385,7 +377,6 @@ To refrain .B cgg from emitting the code for 'lol 10' again, an extra pattern is included in the table for cases like this. -.br The same applies for two consecutive 'loe'-s or 'lil'-s. .sp 1 A bit tricky is 'lof'. @@ -401,7 +392,7 @@ knows that HL is destroyed). .sp 1 By lookahead, .B cgg -can make a clever choise between the first and +can make a clever choice between the first and second code rule of 'loi 4'. The same applies for several other instructions. .NH 3 @@ -415,13 +406,12 @@ Groups 3 and 4: Signed and unsigned integer arithmetic Since the 8080 instruction set doesn't provide multiply and divide instructions, special routines are made to accomplish these tasks. .sp 1 -Instead of providing four slighty differing routines for 16 bit signed or +Instead of providing four slightly differing routines for 16 bit signed or unsigned division, yielding the quotient or the remainder, the routines are merged. This saves space and assembly time when several variants are used in a particular program, at the cost of a little speed. -.br When the routine is called, bit 7 of register A indicates whether the operands should be considered as signed or as unsigned integers, and bit 0 of register A indicates whether the quotient or the @@ -436,14 +426,15 @@ provided, because this one will usually be much faster. .NH 3 Group 5: Floating point arithmetic .PP -Floating points are not implemented. -.br +Floating point is not implemented. Whenever an EM-instruction involving floating points is offered -to the code-generator, it generates the code 'call eunimpl', -which traps with trap number 63. +to the code-generator, it calls the corresponding +library routine with the proper parameters. +Each floating point library routine calls 'eunimpl', +trapping with trap number 63. Some of the Pascal and C library routines output floating point EM-instructions, so code has to be generated for them. -Of course this doesn't imply the code will ever be executed. +Of course this does not imply the code will ever be executed. .NH 3 Group 12: Compare instructions .PP @@ -468,7 +459,6 @@ gen mov a,%1.2 but the current version of .B cgg doesn't approve this. -.br In any case .B cgg chooses either DE or HL to store the result, using lookahead. @@ -481,7 +471,7 @@ If only 2 bytes have to be returned, register-pair DE is used. LIBRARY ROUTINES .PP Most of the library routines start with saving the return address -and the localbase, so that the parameters are on the top of the stack +and the local base, so that the parameters are on the top of the stack and the registers B and C are available as scratch registers. Since register-pair HL is needed to accomplish these tasks, and also to restore everything just before the routine returns, @@ -493,7 +483,6 @@ When a routine returns 2 bytes, they are usually returned in registers-pair DE. When it returns more than 2 bytes they are pushed onto the stack. .br - It would have been possible to let the 32 bit arithmetic routines return 2 bytes in DE and the remaining 2 bytes on the stack (this often would have saved some space and execution time), @@ -504,7 +493,6 @@ TRAPS Whenever a trap, for example trying to divide by zero, occurs in a program that originally was written in C or Pascal, a special trap handler is called. -.br This trap handler wants to write an appropriate error message on the monitor. It tries to read the message from a file (e.g. etc/pc_rt_errors in the @@ -549,11 +537,11 @@ for example by downloading or by storing it in ROM (Read Only Memory). .sp 1 Depending on the characteristics of the particular 8080 based system, some -adaptions have to be made: +adaptations have to be made: .IP 1) 10 In 'head_em': the base address, which is the address where the first 8080 instruction will be stored, and the initial value of the -stackpointer are set to 0x1000 and 0x8000 respectivally. +stackpointer are set to 0x1000 and 0x8000 respectively. .br Other systems require other values. .IP 2) @@ -574,12 +562,12 @@ If this is not the right way on your system, change it. .IP 5) In 'tail_em': the current version of the 8080 back-end has very limited I/O capabilities, because it was tested on a system that -had no knowlegde of files. +had no knowledge of files. So the implementation of the EM-instruction 'mon' is very simple; it can only do the following things: .RS .IP Monitor\ call\ 1: 40 -Exit +exit .IP Monitor\ call\ 3: read, always reads from the monitor. .br @@ -607,7 +595,7 @@ INTEL 8080 VERSUS ZILOG Z80 AND INTEL 8086 .NH 2 Introduction .PP -At about the same time I develloped the back end +At about the same time I developed the back end for the Intel 8080 and Intel 8085, Frans van Haarlem did the same job for the Zilog z80 microprocessor. Since the z80 processor is an extension of the 8080, @@ -615,8 +603,8 @@ any machine code offered to a 8080 processor can be offered to a z80 too. The assembly languages are quite different however. .br -During the devellopments of the back ends we have used -two micro-computers, both equiped with a z80 microprocessor. +During the developments of the back ends we have used +two micro-computers, both equipped with a z80 microprocessor. Of course the output of the 8080 back end is assembled by an 8080 assembler. This should assure I have never used any of the features that are potentially available in the z80 processor, @@ -632,7 +620,7 @@ I have also involved the 8086 micro-processor in this measurements. Differences between the 8080 and z80 processors .PP Except for some features that are less important concerning back ends, -there are two points where the z80 improves the 8080: +there are two points where the z80 improves upon the 8080: .IP First, 18 the z80 has two additional index registers, IX and IY. They are used as in @@ -662,11 +650,10 @@ Special routines are included to jump to near locations, saving 1 byte. Consequences for the 8080 and z80 back end .PP The most striking difference between the 8080 and z80 back ends -is the choise of the localbase. -The writer of the z80 back end chose index register IY as localbase, +is the choice of the local base. +The writer of the z80 back end chose index register IY as local base, because this results in the cheapest coding of EM-instructions like 'lol' and 'stl'. -.br The z80 instructions that load local 10, for example .DS LD E,(IY+10) @@ -679,7 +666,7 @@ Although the profit of the z80 might be not world-shocking, it should be noted that as a side effect it may save some pushing and popping since register pair HL is not used. .sp 1 -The choise of IY as localbase has its drawbacks too. +The choice of IY as local base has its drawbacks too. The root of the problem is that it is not possible to add IY to HL. For the EM-instruction @@ -699,7 +686,7 @@ This annoying push and pop instructions are also needed in some other instructions, for instance in 'lol' when the offset doesn't fit in one byte. .sp 1 -Beside the choise of the localbase, I think there is no +Beside the choice of the local base, I think there is no fundamental difference between the 8080 and z80 back ends, except of course that the z80 back end has register pair BC and, less important, index register IX available as scratch registers. @@ -715,7 +702,6 @@ some C programs and some Pascal programs. Then I produced 8080, z80 and 8086 code for them. Investigating the assembler listing I found the lengths of the different parts of the generated code. -.br I have checked two areas: .IP 1) 8 the entire text part @@ -742,7 +728,7 @@ The table below should be read as follows. For all programs I have computed the ratio of the code-lengths of the 8080, z80 and 8086. The averages of all Pascal/C programs are listed in the table, -standarized to '100' for the 8080. +standardized to '100' for the 8080. So the listed '107' indicates that the lengths of the text parts of the z80 programs that originally were Pascal programs, averaged 7 percent larger than in the corresponding 8080 programs. @@ -760,7 +746,7 @@ averaged 7 percent larger than in the corresponding 8080 programs. The most striking thing in this table is that the z80 back end appears to produce larger code than the 8080 back end. The reason is that the current z80 back end table is -not very elaborate yet. +not very sophisticated yet. For instance it doesn't look for any EM-pattern longer than one. So the table shows that the preparations in the 8080 back end table to produce faster code (like recognizing special EM-patterns @@ -768,7 +754,7 @@ and permitting one byte registers on the fake-stack) was not just for fun, but really improved the generated code significantly. .sp 1 -The table shows that the 8080 table is relativelly better +The table shows that the 8080 table is relatively better when only the plain user program is considered instead of the entire text part. This is not very surprising since the 8080 back end sometimes uses library routines where the z80 and especially the 8086 don't. @@ -797,7 +783,7 @@ An overview on the Amsterdam Compiler Kit. .IP [3] Tanenbaum, A.S., Stevenson, J.W., Keizer, E.G., and van Staveren, H. .br -Desciption of an experimental machine architecture for use with block +Description of an experimental machine architecture for use with block structured languages, .br Informatica report 81, Vrije Universiteit, Amsterdam, 1983. diff --git a/doc/install.doc b/doc/install.doc index 81754201..7f9c6533 100644 --- a/doc/install.doc +++ b/doc/install.doc @@ -1,12 +1,13 @@ .\" $Header$ -.nr LL 7.5i -.nr PD 1v +.if n .nr PD 1v +.if n .nr LL 78m +.if n .ll 78m .TL -Amsterdam Compiler Kit installation guide +Amsterdam Compiler Kit Installation Guide .AU Ed Keizer .AI -Wiskundig Seminarium +Vakgroep Informatica Vrije Universiteit Amsterdam .NH @@ -16,15 +17,20 @@ This document describes the process of installing Amsterdam Compiler Kit. It depends on your combination of hard- and software how hard it will be to install the Kit. -This description is intended for a PDP 11/44 running +This description is intended for a VAX running BSD 4.1 .UX -Version 7. -Installation on other PDP 11's should be easy, as long +\. +Installation on VAX BSD4.2/4.3 systems, +Sun-2 or Sun-3 systems running Release 3.0 or newer, and some System V systems +should be easy. +Installation on PDP 11's running +.UX +Version 7, BSD 2.8, or 2.9 +should also be easy, as long as they have separate instruction and data space. Installation on machine's without this feature, like PDP 11/34, PDP 11/60 requires extensive surgery on some programs and is thought of as impossible. -Installation on VAX'en running BSD4.1 should also be easy. See section 7 for installation on other systems. .NH Restoring tree @@ -33,16 +39,16 @@ The process of installing Amsterdam Compiler Kit is quite simple. It is important that the original Amsterdam Compiler Kit distribution tree structure is restored. Proceed as follows -.IP " -" 10 +.IP " \-" 10 Create a directory, for example /usr/em, on a device -with at least 15 Megabytes left. -.IP " -" +with at least 25 Megabytes left. +.IP " \-" Change to that directory (cd ...); it will be the working directory. -.IP " -" +.IP " \-" Extract all files from the distribution medium, for instance magtape: \fBtar x\fP. -.IP " -" +.IP " \-" Keep a copy of the original distribution to be able to repeat the process of installation in case of disasters. This copy is also useful as a reference point for diff-listings. @@ -61,7 +67,7 @@ root of a tree containing almost all binaries and libraries used by commands. All files specific to a certain machine are collected in one subtree per machine. E.g. "lib/pdp", "lib/z8000". -The names used here are the same names as used for subdirectories +The names used here are the same names as used for subtrees of "mach". .IP "lib/descr" .br @@ -70,6 +76,10 @@ Command descriptor files used by the program ack. .br Files used by the LL(1) Parser-generator. .br +.IP "lib/ego" +.br +Files used by the global optimizer. +.br .IP "etc" .br The main description of EM sits here. @@ -80,11 +90,15 @@ Make in this directory creates most of the files in "h" and "util/data". This make should only be called when the EM definition is changed. -.IP "include" +.IP "include/_tail_cc" .br -More or less system independent include files needed by modules +Include files needed by modules in the C library from lang/cem/libcc. Especially needed for "stdio". +.IP "inlude/_tail_mon" +.br +More or less system independent include files needed by modules +in the library lang/cem/libcc/mon. .IP "h" .br The #include files for: @@ -114,24 +128,45 @@ pc_file.h Macro's used in file handling in Pascal pc_size.h Sizes of objects used by Pascal compiler and run-time system. em_reg.h Definition of names for register types. +ocm_chan.h Used by the occam run-time system +ocm_parco.h Used by the occam run-time system +ocm_proc.h Used by the occam run-time system .TE +.IP "modules" +.br +root of a tree containing modules for compiler writers. +.IP "modules/man" +.br +manual pages for all modules. +.IP "modules/lib" +.br +contains module objects. +.IP "modules/src" +.br +contains sources of the modules, each module in its own directory. +.IP "modules/h" +.br +include files for some of the modules. +.IP "modules/pkg" +.br +include files for some of the modules. .IP "doc" .br -This directory contains the unformatted documents for the kit. +This directory contains the unformatted documents for the Kit. A list of the available documents can be found in the last section. -.IP "doc/em.doc" +.IP "doc/em" .br -The EM-manual IR-81 -.IP "doc/em.doc/int" +The EM-manual IR-81. +.IP "doc/em/int" .br -The EM interpreter written in Pascal +The EM interpreter written in Pascal. .IP "mkun" .br The PUBMAC macro package for nroff/troff from the Katholieke Universiteit at Nijmegen. -It is used for the EM reference manual, -the Makefile installs the macro package in -/usr/lib/tmac/tmac.mkun*. +It is used for the EM reference manual. +The Makefile installs the macro package in +/usr/lib/tmac. This package is in the public domain. .IP "mach" .br @@ -143,29 +178,35 @@ These directories have subdirectories named: .in +3n .TS l l. -as the assembler ( *.s + libraries => a.out ) -cg the backend ( *.m => *.s ) -ncg the new backend ( *.m => *.s ) +cg the backend (*.m => *.s) +ncg the new backend (*.m => *.s) +as the assembler (*.s => *.o) or + assembler/linker (*.s + libraries => a.out) +cv Conversion programs for a.out files. +dl Down-load programs +top the target optimizer + +libem Sources for EM runtime system, only depending on CPU type libbc Used to create Basic run-time system and libraries libcc Used to create C run-time system and libraries libpc Used to create Pascal run-time system and libraries -libem Sources for EM runtime system, intended to depend only on CPU type +liboc Used to create Occam run-time system and libraries libsys Sources for system-dependent EM library test Various tests -dl Down-load programs -cv Conversion programs for a.out files. + int Source for an interpreter .TE .in -3n -The directory proto contains files used by most machines +The directory proto contains files used by most machines, like machine-independent sources and Makefiles. .in +3n .TS l l. -mach/proto/libg Makefile for compiling libraries. -mach/proto/as Assembler sources. mach/proto/cg Current backend sources. mach/proto/ncg New backend sources. +mach/proto/as Assembler sources. +mach/proto/top Target optimizer sources. +mach/proto/libg Makefile for compiling libraries. .TE .IP "emtest" .br @@ -181,7 +222,7 @@ Just there to group the directories for all front-ends Pascal front-end .IP "lang/pc/libpc" .br -Source of Pascal run-time system ( in EM or C ) +Source of Pascal run-time system (in EM or C) .IP "lang/pc/test" .br Some test programs written in Pascal @@ -204,7 +245,7 @@ Stdio sources .IP "lang/cem/libcc/mon" .br Sources for routines in chapter II, written in EM -.IP "lang/cem/comp" +.IP "lang/cem/cemcom" .br The compiler proper .IP "lang/cem/ctest" @@ -225,6 +266,18 @@ Basic run-time library source. .IP "lang/basic/test" .br Various Basic programs. +.IP "lang/occam" +.br +Occam front-end. +.IP "lang/occam/comp" +.br +The compiler proper. +.IP "lang/occam/lib" +.br +Source of Occam run-time system (in EM or C). +.IP "lang/occam/test" +.br +Some Occam programs. .IP "util" .br Contains directories with sources for various utilities @@ -233,20 +286,26 @@ Contains directories with sources for various utilities The program used for translation with the Kit. .IP "util/opt" .br -EM peephole optimizer (*.k => *.m) +EM peephole optimizer (*.k => *.m). +.IP "util/ego" +.br +The global optimizer. +.IP "util/topgen" +.br +The target optimizer generator.. .IP "util/misc" .br -Decode (*.[km] => *.e) + encode (*.e => *.k) +Decode (*.[km] => *.e) + encode (*.e => *.k). .IP "util/data" .br -The C-code for `lib/em_data.a` -These sources are created by the Makefile in `etc` +The C-code for `lib/em_data.a`. +These sources are created by the Makefile in `etc`. .IP "util/ass" .br -The EM assembler ( *.[km] + libraries => e.out ) +The EM assembler (*.[km] + libraries => e.out). .IP "util/arch" .br -The archiver to be used for ALL EM utilities +The archivers to be used for ALL EM utilities. .IP "util/cgg" .br A program needed for compiling backends. @@ -255,13 +314,24 @@ A program needed for compiling backends. A program needed for compiling the newest backends. .IP "util/cpp" .br -The V7 C preprocessor. +The C preprocessor. .IP "util/shf" .br Various shell files. .IP "util/LLgen" .br The extended LL(1) parser generator. +.IP "util/amisc" +.br +Contains some programs handling ACK a.out format, such as anm, asize. +.IP "util/cmisc" +.br +Contains some programs to help in resolving name conflicts, and +a dependency generator for makefiles. +.IP "util/led" +.br +The ACK link-editor, reading ACK relocatable a.out format, and writing +ACK a.out format. .ne 4 .LP All pathnames mentioned in the text of this document are relative to the @@ -273,18 +343,25 @@ Adapting ACK to your system Before compiling the sources in the Kit some installation dependent actions have to be taken. Most of these are performed by an interactive shell script in the file -first in a directory of the same name. +.I first +in a directory of the same name. .LP These actions are: .sp 1 -.IP - +.IP \- Automatically checking whether you included the ACK bin directory in your shell PATH. See also the section on "commands". -.IP - +.IP \- Automatically setting the pathname of the parent directory in ../h/em_path.h. See also the section on "pathnames". -.IP - +.IP \- +Discovering how to call +.I cc +to get it to include the +.I lex +library. +.IP \- Asking you for the type of system you have and creating the shell script "ack_sys" in the Kit's bin directory. Several utilities make use of "ack_sys" to determine the type of @@ -296,79 +373,93 @@ c c c l l l. answer system type default machine pdp_v7 PDP11 with sep I/D and version 7 pdp -vax_bsd4_1a VAX11 with BSD4.1a vax2 -vax_bsd4_1c VAX11 with BSD4.1c vax2 -vax_bsd4_2 VAX11 with BSD4.2 vax2 -pc_ix IBM PC with PC/IX ix +vax_bsd4_1a VAX11 with BSD4.1a vax4 +vax_bsd4_2 VAX11 with BSD4.2 vax4 +vax_sysV_2 VAX11 with System V.2 vax4 +pc_ix IBM PC with PC/IX i86 m68_unisoft Motorola 68000 with Unisoft UNIX m68k2 m68_pmds Philips PMDS pmds +m68_sysV_0 68000 with Uniplus UNIX System V.0 mantra +sun3 Sun 3 Motorola 68020 workstation sun3 +sun2 Sun 2 Motorola 68010 workstation sun2 ANY Neither of the above m68k2 .TE .sp 1 -As mentioned before the installation on VAX'en and PDP11's should -be easy. -The pc_ix and m68 systems are also known to behave reasonably, -but the installation procedure has not been extensively tested. +For some of these, the installation procedure has not been tested, as +we don't have them. However, +the pdp_v7, vax_bsd4_1a, pc_ix, sun3 and m68 systems are known to behave +reasonably. +The Sun systems should run Release 3.0 or newer. For ANY you can use any name you fancy, but the Kit will not be able to compile programs for your system. If you want to do that you have to read the section about "compilation on a different machine". -.IP - +.IP \- Automatically setting the default machine for which code is produced to your own type of system according to the table above. This in done in the file "h/local.h". See also the section 8.2. -.IP - +.IP \- Automatically editing a few description files that tell ACK to use your system's assembler. On both the PDP and the VAX the Kit uses the native assembler and linker. -The description files in lib/pdp/descr, lib/vax2/descr and +The description files in lib/pdp/descr and lib/vax4/descr have to be altered to prevent attempts to assemble programs with unsuitable assemblers. The original descr files are copied to descr.orig. -.IP - -Automatically installing the special include directory for vax2. -This will only be done on VAX systems. -The shell scripts needed by ACK for the vax2 backend differ slightly -from the one issued by Berkeley. -.br -Note: this has only been tested under BSD4.1a. -.IP - -Automatically editing the system.h file in mach/vax[24]/libem. -Again, only on VAXen. -These files reflect whether you have BSD4.1a, BSD4.1c or BSD4.2. -.LP -.sp 1 -Some actions still have to be done by hand. -.sp 1 -.IP - -The VAX backends cannot be booted on systems +.IP \- +The VAX backend cannot be booted on systems with a 16-bit address space systems. The program lib/cgg needs more memory than available to transform the table into files suitable for the C-compiler. Therefore files tables1.h and tables1.c have been provided in -the directories mach/vax[24]/cg. -These must be copied to tables.h and tables.c on their respective -directories to get working code-generators for the VAX on PDP11's. +the directory mach/vax4/cg. +These must be copied to tables.h and tables.c +to get working code-generators for the VAX on PDP11's. You will hardly be able to use these, because the code generated by these programs cannot be assembled and loaded without a native VAX assembler, but its nice to be able to look at the code produced. -.IP - +The same problem occurs for the m68k2 backend and the m68020 backend, +and the same solution is chosen. +.IP \- +On machines with a 16-bit address space, the C-compiler has no +builtin preprocessor. Arangements are made to this effect. +.IP \- +On the PDP, +.I ranlib +is not used, because it does not work properly, +at least, on our 2.9 BSD system it does'nt. +This is done by creating a dummy shell script +.I ranlib +in the ACK bin directory. +If you are sure that your +.I ranlib +does work properly, you can just remove the shell script before +installing the Kit. +.LP +.sp 1 +Some actions still have to be done by hand. +.sp 1 +.IP \- The installation of the PUBMAC macro package is not done automatically because you needs super-user privileges to do that on most systems. This macro package is used with several of the documents provided in the Kit. -.IP - +.IP \- UNIX V7 as originally distributed contains a few bugs that prevent correct execution of some of the larger programs. See the section named "Fixes for the UNIX V7 system" about what to do. -.IP - +Berkeley 2.8 or 2.9 may also have some of these bugs. +.IP \- The manual files for the Kit can be copied to their appropriate place in the system by giving the command "make install" -in the man directory. +in the man directory, but only +.B after +running the installation of the +Kit itself. .NH Compiling the Kit .PP @@ -434,111 +525,117 @@ The backends for the other machines are known to run our own test programs, but might reveal errors when more heavily used. .NH 2 -An example output of TakaAction. +An example output of TakeAction. .PP .DS System definition -- done EM definition -- done -C preprocessor -- done +LL(1) Parser generator -- done EM definition library -- done +C utilities -- done +system-call interface module -- done +string routines module -- done +formatted print module -- done +assertion module -- done +memory allocation module -- done +fast, linear time malloc -- done +EM messages generation module -- done +identifier table module -- done +input module -- done +ACK-object reading and writing module -- done +EM-code reading module -- done +EM code generation module -- done +Modules -- done +C preprocessor -- done +ACK object utilities -- done Encode/Decode -- done Shell files in bin -- done EM assembler -- done EM Peephole optimizer -- done + . + . + . +EM Global optimizer -- done ACK archiver -- done Program 'ack' -- done Bootstrap for backend tables -- done -LL(1) Parser generator -- done Bootstrap for newest form of backend tables -- done +LED link editor -- done +TOPGEN target optimizer generator -- done C frontend -- done Basic frontend -- done +Occam frontend -- done Intel 8086 assembler -- done Intel 8086 backend -- done -Intel 8086 download program(s) -- done Intel 8086 C libraries -- done Intel 8086 EM library -- done Intel 8086 Pascal library -- done -Intel 8086 Stand-alone io library -- done +Intel 8086 PC/IX systemcall library -- done Intel 8086 Basic library -- done +Intel 8086 Occam library -- done +Intel 8086 conversion program from ack.out --> PC/IX a.out -- done Intel 8086 support -- done -MSC6500 assembler -- done -MSC6500 backend -- done -MSC6500 download program(s) -- done -MSC6500 C libraries -- done -MSC6500 EM library -- done -MSC6500 Pascal library -- done -MSC6500 Basic library -- done -MSC6500 support -- done -Motorola 6800 assembler -- done -Motorola 6800 support -- done -Motorola 6805 assembler -- done -Motorola 6805 support -- done -Motorola 6809 assembler -- done -Motorola 6809 support -- done -Intel 8080 assembler -- done -Intel 8080 support -- done -2-2 Interpreter C libraries -- done -2-2 Interpreter Pascal library -- done -2-2 Interpreter Basic library -- done -2-2 Interpreter support -- done -2-4 Interpreter C libraries -- done -2-4 Interpreter Pascal library -- done -2-4 Interpreter Basic library -- done -2-4 Interpreter support -- done -4-4 Interpreter C libraries -- done -4-4 Interpreter Pascal library -- done -4-4 Interpreter Basic library -- done -4-4 Interpreter support -- done -Sorry, IBM PC/IX conversion program(s) can only be made on pc_ix systems -IBM PC/IX support -- done -Motorola 68000 2-4 assembler -- done + . + . + . +Motorola 68000 assembler -- done Motorola 68000 2-4 backend -- done -Sorry, Motorola 68000 interpreters can only be made on m68* systems -Sorry, the m68k? conversion program has to be translated on the target machine +Motorola 68000 2-4 conversion program -- done +Motorola 68000 target optimizer -- done Motorola 68000 2-4 C libraries -- done Motorola 68000 2-4 EM library -- done Motorola 68000 2-4 Pascal library -- done Motorola 68000 2-4 System library -- done Motorola 68000 2-4 Basic library -- done -Motorola 68000 2-4 support, see mach/m68k2/Out -NS16032 assembler -- done -NS16032 support -- done +Motorola 68000 2-4 Occam library -- done +Sorry, Motorola 68000 interpreters can only be made on m68* systems +Motorola 68000 2-4 support -- done + . + . + . PDP 11 assembler -- done PDP 11 backend -- done -PDP 11 interpreter -- done -PDP 11 C libraries -- done -PDP 11 EM library -- done -PDP 11 Pascal library -- done -PDP 11 Basic library -- done +PDP 11 target optimizer -- done +Sorry, PDP 11 interpreter can only be made on pdp* systems +Sorry, PDP 11 C libraries can only be made on pdp* systems +Sorry, PDP 11 EM library can only be made on pdp* systems +Sorry, PDP 11 systemcall library can only be made on pdp* systems +Sorry, PDP 11 Pascal library can only be made on pdp* systems +Sorry, PDP 11 Basic library can only be made on pdp* systems +Sorry, PDP 11 Occam library can only be made on pdp* systems PDP 11 support -- done -PMDS download program(s) -- done -PMDS EM library -- done -PMDS support -- done -Signetics 6502 assembler -- done -Signetics 2650 support -- done -Vax 2-4 backend -- done -Sorry, Vax 2-4 Basic library can only be made on vax* systems -Sorry, Vax 2-4 C libraries can only be made on vax* systems -Sorry, Vax 2-4 EM library can only be made on vax* systems -Sorry, Vax 2-4 Pascal library can only be made on vax* systems -Vax 2-4 support -- done + . + . + . Vax 4-4 backend -- done Sorry, Vax 4-4 C libraries can only be made on vax* systems Sorry, Vax 4-4 EM library can only be made on vax* systems -Sorry, Vax 4-4 Pascal library can only be made on vax* systems +Sorry, Vax 4-4 Occam library can only be made on vax* systems Sorry, Vax 4-4 Basic library can only be made on vax* systems -Vax 4-4 support, see mach/vax4/Out -Z80 assembler -- done -Z80 support -- done -Zilog Z8000 assembler -- done -Zilog Z8000 backend -- done -Zilog Z8000 C libraries -- done -Zilog Z8000 EM library -- done -Zilog Z8000 Pascal library -- done -Zilog Z8000 Basic library -- done -Zilog Z8000 support -- done -Nascon download program(s) -- done -Nascom support -- done +Sorry, Vax 4-4 systemcall interface can only be made on vax* systems +Vax target optimizer -- done +Vax 4-4 support -- done +M68020 assembler -- done +M68020 backend -- done +M68020 EM library -- done +M68020 system call library -- done +M68020 C libraries -- done +M68020 PC library -- done +M68020 Basic library -- done +M68020 Occam library -- done +Sorry, M68020 VME131 System V/68 R2V2.1 conversion can only be made on m68020 systems +M68020 System V/68 support -- done +Ack.out --> Sun 3 M68020 a.out format conversion program -- done +Sun 3 M68020 systemcall library -- done +Sun 3 M68020 C libraries -- done +Sun 3 M68020 support -- done +Ack.out --> Sun 2 M68000 a.out format conversion program -- done +Sun 2 M68000 systemcall library -- done +Sun 2 M68000 C libraries -- done +Sun 2 M68000 support -- done + . + . + . Failed for Pascal frontend, see lang/pc/pem/Out .DE .PP @@ -551,14 +648,14 @@ Only the Pascal frontend failed to compile in this example. If you want to repeat a certain part of the installation, look in the Action file for the directory in which that part is to be found. If that directory contains an Action file issue the command -"sh EMHOME/TakeAction", otherwise type "make install". +"sh EM_DIR/TakeAction", otherwise type "make install". .NH Commands .PP The following commands are available in the bin directory after compilation -of the kit: +of the Kit: .sp 1 -.IP "\fIack\fP, \fIacc\fP, \fIabc\fP, \fIapc\fP and their links" +.IP "\fIack\fP, \fIacc\fP, \fIabc\fP, \fIapc\fP, \fIocm\fP and their links" .br The names mentioned here can be used to compile Pascal, C, etc... programs. Most of the links can be used to generate code for a particular @@ -566,7 +663,10 @@ machine. See also the section about "Machines". .IP \fIarch\fP .br -The archiver used for the EM- and universal assembler. +The archiver used for the EM- and universal assembler/loader. +.IP \fIaal\fP +.br +The archiver used for ACK objects. .IP \fIem\fP .br This program selects a interpreter to execute an e.out file. @@ -586,6 +686,17 @@ not feasible on your system. (Like translating PDP assembly). .IP \fImarch\fP .br A shell script used while compiling libraries. +.IP "\fIasize\fP, \fIanm\fP, \fIastrip\fP" +.br +Do the same as \fIsize\fP, \fInm\fP and \fIstrip\fP, but for ACK object format. +.IP \fImkdep\fP +.br +A dependency generator for makefiles. +.IP "\fIcid\fP, \fIprid\fP, \fIcclash\fP" +.br +Some utilities for handling name clashes in C programs. Some +systems have C-compilers with only 7 or 8 characters significant in +identifiers. .sp 1 .LP We currently make the Kit available to our users by telling @@ -624,52 +735,83 @@ command system i/p languages fp dir remarks pdp PDP/UNIX V7 2/2 C * pdp needs sep. I/D Pascal No assembler Basic - -vax2 VAX/BSD 4.? 2/4 C * vax2 No assembler - Pascal - Basic + Occam vax4 VAX/BSD 4.? 4/4 C * vax4 No assembler + System V.2 Pascal Basic + Occam m68k2 M68000/Unisoft 2/4 C m68k2 Pascal Basic + Occam -m68k4 M68000/PMDS 4/4 C m68k2 - Basic m68k4 +m68k4 M68000/Unisoft 4/4 C m68k4 + Pascal m68k2 + Basic + Occam -pmds M68000/PMDS 2/2 C pmds Philips Micro +pmds M68000/PMDS 2/4 C pmds Philips Micro Pascal m68k2 Devel. System Basic + Occam -i86 Bare 8086 2/2 C i86 For ISBC 86/12A - Pascal - Basic +pmds4 M68000/PMDS 4/4 C pmds4 Philips Micro + Pascal m68k2 Devel. System + Basic m68k4 + Occam -ix IBM PC/IX 2/2 C ix IBM PC with PC/IX - Pascal i86 Causes kernel crashes +mantra M68000/SysV.0 4/4 C mantra + Pascal m68k2 + Basic m68k4 + Occam + +m68020 M68020/V/68 4/4 C m68020 + R2V2.1 Pascal Basic + Occam + +sun3 SUN 3 R3.0 4/4 C sun3 + Pascal m68020 + Basic + Occam + +sun2 SUN 2 R3.0 4/4 C sun2 + Pascal m68k4 + Basic m68k2 + Occam + +i86 IBM PC/IX 2/2 C i86 IBM PC with PC/IX + Pascal Causes kernel crashes + Basic + Occam z8000 Zilog 8000 2/2 C z8000 Central Data Pascal CPU board - Basic + Basic Uses assembler/loader + Occam int Same as int22 int22 EM machine 2/2 C * int22 Needs interpreter Pascal Basic + Occam int24 EM machine 2/4 C * int24 Needs interpreter Pascal Basic + Occam int44 EM machine 4/4 C * int44 Needs interpreter - Basic - -6500 6502/BBC 2/2 C 6500 Pascal Basic + Occam + +6500 6502/BBC 2/2 C 6500 Uses assembler/loader + Pascal + Basic + Occam 6800 Bare 6800 6800 Assembler only @@ -677,23 +819,29 @@ int44 EM machine 4/4 C * int44 Needs interpreter 6809 Bare 6809 6809 Assembler only -ns Bare NS16032 ns Assembler only +ns Bare NS16032 4/4 C ns + Pascal + Basic + Occam i80 Hermac/z80 2/2 C i80 Pascal Basic + Occam z80 Hermac/z80 2/2 C z80 \fIi80\fP is faster Pascal Basic + Occam s2650 Signetics 2650 s2650 Assembler only .TE .PP The commands \fBint\fP, \fBint22\fP, \fBint24\fP and \fBint44\fP produce e.out files with EM machine code which must be interpreted. -The Kit contains two interpreters one running under PDP 11/V7 UNIX -and the PMDS system. +The Kit contains two interpreters: one running under PDP 11/V7 UNIX, +and one for the M68000, running under the PMDS system, SUN systems, +the Mantra system, etc. The first one can only interpret 2/2 e.out files, the other takes 2/4 and 4/4 files. The PDP 11 interpreter executes floating point instructions. @@ -711,15 +859,8 @@ for C and Basic programs on any UNIX machine. The presence of most UNIX utilities is essential for compilation. A few of the programs you certainly need are: C-compiler, Yacc, sed, make and lex. -Except the Pascal compiler proper all programs +Except for the Pascal compiler proper all programs can be translated on a normal UNIX system, like V7, BSD4.1. -.PP -We know there are certain problems with System V. -Some of the character/string routines are named differently, -ctype(3) seems to have a different naming scheme. -The most annoying thing is that in printf format strings the -role of the format %03x is taken by something like %.3x. -Several programs suffer from this format change. .NH 2 Backend .PP @@ -736,63 +877,41 @@ Pascal When you can produce executable code it is also possible to boot the Pascal Compiler, which is written in Pascal itself. -The Kit contains the compact code files for the 2/2 and 2/4 +The Kit contains the compact code files for the 2/2, 4/4 and 2/4 versions of the Pascal compiler. -The current version of this compiler can only be used on machines -with a 16-bit word size and 16- or 32-bit pointers. The Makefile automatically tries to boot the Pascal compiler from one of these compact code files, if the compiler proves unable to compile itself. .NH 2 -Universal assembler +Universal assembler/loader, link editor .PP The native assemblers and loaders are used on PDP-11 and VAX. The description files in lib/*/descr for other systems use our -universal assembler. -The load file produced by this assembler is not directly +universal assembler and for most machines our link editor. +The load file produced is not directly usable in any system known to us, but has to be converted before it can be put to use. The \fIcv\fP programs convert our a.out format into executable files. The \fIdl\fP programs present for some machines unravel -these our a.out files and transmit commands to load memory +our a.out files and transmit commands to load memory to a microprocessor over a serial line. -The file man/a.out.5 contains a description of the format of -the universal assembler load file, -it might be useful to those who wish or need to write their +The file man/ack.out.5 contains a description of the format of +the universal assembler load file. +It might be useful to those who wish or need to write their own conversion programs. -.LP -One warning has to be issued here. -The universal assembler a.out files contain integers and/or -longs with the bytes in the order your own system uses. -Copying these files to machines with a different byte order -will not always produce the desired results. +Also, a module is included to read and write our a.out format. +See modules/man/object.3. .NH 2 Compiling libraries .PP -The Kit contains sources for part II and III of the C-library, -they have been grabbed from our V7 system and sometimes -altered in a EM dependent way or replaced altogether when the original -was in assembly. +The Kit contains sources for part II and III of the C-library. These files can be used to make libraries for the Ack C-compiler. The recompilation process uses a few include files. -The include directory in the EM home directory contains a few more -or less system independent include files. -Some backends, like the vax2 backend also need a few include files -of their own, replacing ones in /usr/include. -The include files are sought in lib/*/include. -The system dependent include files are fetched from /usr/include -on the system you use to recompile. -This may lead to several problems. -Sometimes the system differs so much from V7 that certain manifest constants -do not exist any more. -At other times these include files were written for a compiler without -a restriction on name length. -In that case - I've seen it happen - people tend to use differing -identifiers that are identical in the first eight characters. -All these problems you have to solve yourself, -the libraries are only included as an extra and too much system -dependent to give any guarantees. +The include directory in the EM home directory contains the include files +it needs. +An effort has been made to make the part III stuff as system independent as +possible. .NH Options .NH 2 @@ -800,10 +919,10 @@ Default machine .PP There is one important option in h/local.h. The utility \fIack\fP uses a default machine name when called -as \fIacc\fP, \fIcc\fP, \fIabc\fP, \fIapc\fP, \fIpc\fP or \fIack\fP. +as \fIacc\fP, \fIcc\fP, \fIabc\fP, \fIapc\fP, \fIpc\fP, \fIocm\fP, or \fIack\fP. The machine name used for default is determined by the definition of ACKM in h/local.h. -The Kit is distributed with "pdp" as the default machine, +The Kit is distributed with "vax4" as the default machine, but the shell script "first" in the directory "first" alters this to suit your own system. There is nothing against using the Kit as a cross-compiler @@ -818,7 +937,7 @@ needs. Pathnames .PP Absolute pathnames are concentrated in "h/em_path.h". -Only the Pascal runtime system and the utility \fIack\fP use +Only the Pascal runtime system and the utilities \fIack\fP and \fILLgen\fP use absolute pathnames to access files in the Kit. The tree is distributed with /usr/em as the working directory. @@ -888,9 +1007,10 @@ to the bin and lib directories. Fixes for the UNIX V7 system .PP UNIX System V7 has a few bugs that prevent a part of or the whole Kit -from working properly. +from working properly. Berkeley 2.8 and/or 2.9 may also suffer from this +problem. To be honest, we do not know which of the following changes are -essential to the functioning of our Kit. +essential to the functioning of our Kit. The change to "ld" is. .PP The file "doc/v7bugs.doc" gives for each of the following bugs a small test program and a diff listing of the source files that have to be @@ -908,6 +1028,18 @@ Floating point registers are not copied to child in fork(). .LP Use the test programs to see if the errors are present in your system and to check if the modifications are effective. +.PP +You may also have to change /usr/src/cmd/cc.c (/bin/cc) to pass the +.B -i +flag to +.I ld. +Also, /usr/src/cmd/ld.c (/bin/ld) may have to be changed to increase the +number of library positions it can hold. +On our (2.9 BSD) version, this is the constant +.I NROUT. +It must be at least 400. +This may require separate I&D. +Also, our version does not check that the table does not overflow. .NH Testing .PP @@ -960,7 +1092,7 @@ process. The differences will be presented on standard output. The contents of the result files depend on the wordsize, the xx.cem.g files on the distribution are intended for a -16-bit machine. +32-bit machine. .IP Basic .br The directory lang/basic/test contains some forty basic programs. @@ -972,7 +1104,7 @@ If it compiles its output is compared to a file with suffix .g which contains the output to be expected. The make should be started with its standard input diverted to /dev/null. -An example of the output od a make is present in the file Out.std. +An example of the output of a make is present in the file Out.std. .NH Documentation .PP @@ -983,18 +1115,19 @@ following commands: cd man make install .DE +but do this \fBafter\fR compiling the Kit. .LP Several documents are provided: .TS l l. -doc/toolkit.doc general overview -doc/em.doc description of the EM machine architecture +doc/toolkit.doc general overview (CACM article) +doc/em description of the EM machine architecture doc/ack.doc format of machine description files (lib/*/descr) doc/basic.doc Basic reference manual doc/pcref.doc Pascal-frontend reference manual doc/val.doc results of running the Pascal Validation Suite -doc/cref.doc C-frontend manual -doc/LLgen.doc description of the LL(1) parser generator. +doc/crefman.doc C-frontend description +doc/LLgen description of the LL(1) parser generator. doc/peep.doc internal documentation for the peephole optimizer doc/cg.doc documentation for backend writers and maintainers doc/regadd.doc addendum to previous document describing register variables @@ -1003,8 +1136,15 @@ doc/v7bugs.doc bugs in the V7 system and how to fix them doc/6500.doc MSC 6500 backend description. doc/i80.doc Intel 8080 backend description. doc/z80.doc Zilog Z80 backend description. +doc/m68020.doc Motorola M68000/M68020 backend description +doc/occam Occam-frontend description +doc/ego Global Optimizer description +doc/top Target Optimizer description doc/install.doc this document doc/install.pr this document (formatted) .TE +.PP +The names in this list without a suffix are in fact a subdirectory. +Use the Makefile to get readable copies. .LP Good luck. diff --git a/doc/m68020.doc b/doc/m68020.doc new file mode 100644 index 00000000..8bb6b7f2 --- /dev/null +++ b/doc/m68020.doc @@ -0,0 +1,1408 @@ +.nr PS 11 +.nr VS 13p +.EQ +delim @@ +.EN +.EQ +gfont R +.EN +.ND +.RP +.TL +A back end table for the Motorola MC68000, MC68010 and MC68020 microprocessors +.AU +Frank Doodeman +.AB +A back end table is part of the Amsterdam Compiler Kit (ACK). It is used +to produce the actual back end, a program that translates the intermediate +language family EM to assembly language for some target machine. The table +discussed here can be used for two back ends, suitable for in total three +machines: the MC68000 and MC68010 (the difference between these two is +so small that one back end table can be used for either one), or +for the MC68020. +.AE +.NH +Introduction +.PP +To simplify the task of producing portable (cross) compilers and interpreters +the Vrije Universiteit designed an integrated collection of programs, the +Amsterdam Compiler Kit (ACK) [2]. It is based on the old UNCOL idea [1] which +attempts to solve the problem of how to make a compiler for each of @ N @ +languages on @ M @ different machines without having to write @ N times M @ +programs. +.PP +The UNCOL approach is to write @ N @ +.I +front ends, +.R +which translate the +source language into a common intermediate language UNCOL (Universal Computer +Oriented Language), and @ M @ +.I +back ends, +.R +each of which translates programs in +UNCOL into a specific machine language. Under these conditions only @ M + N @ +programs must be written to provide all @ N @ languages on all @ M @ +machines, instead of @ M times N @ programs. +.PP +The intermediate language for the Amsterdam Compiler Kit is the machine language +for a simple stack machine called EM (Encoding Machine) [3]. So a back end for +the MC68020 translates EM code into MC68020 assembly language. Writing such a +table [4] suffices to get the back end. +.PP +The back end is a single program that is driven by a machine dependent driving +table. This table, the back end table, defines the mapping of EM code to +the MC68000, MC68010 or MC68020 assembly language. +.NH +The MC68000 and MC68020 micro processors +.PP +In this document the name MC68000 will be used for both the MC68000 and the +MC68010 micro processors, because as far as the back end table is concerned +there is no difference between them. For a complete and detailed description +of the MC68020 one is referred to [5]; for the MC68000 one might also use [6]. +In this section some relevant parts will be handled. +.NH 2 +Registers +.PP +Both the MC68000 and the MC68020 have eight 32-bit data registers (@ D sub 0 @-@ D sub 7 @) that can +be used for byte (8-bit), word (16-bit) and long word (32-bit) data operations. +They also have seven 32-bit address registers (@ A sub 0 @-@ A sub 6 @) that may be used as +software stack pointers and base address registers; address register @ A sub 7 @ is +used as the system stack pointer. Address registers may also be used for +word and long word address operations. +.NH 2 +Addressing modes +.PP +First the MC68000 addressing modes will be discussed. Since the MC68020's +set of addressing modes is an extension of the MC68000's set, of course this +section also applies to the MC68020. +.PP +In the description we use: +.IP @ A sub n @ +for address register; +.IP @ D sub n @ +for data register; +.IP @ R sub n @ +for address or data register; +.IP @ X sub n @ +for index register (either data or address register); +.IP @ PC @ +for program counter; +.IP @ d sub 8 @ +for 8 bit displacement integer; +.IP @ d sub 16 @ +for 16 bit displacement integer; +.IP @ bd @ +for base displacement (may be null, word or long); +.IP @ od @ +for outer displacement (may be null, word or long). +.NH 3 +General addressing modes +.NH 4 +Register Direct Addressing +.IP Syntax: 8 +@ R sub n @ +.PP +This addressing mode (it can be used with either a data register or an address +register) specifies that the operand is in one of +the 16 multifunction registers. +.NH 4 +Address Register Indirect +.IP Syntax: 8 +@ ( A sub n ) @ +.PP +The address of the operand is in the address register specified. +.NH 4 +Address Register Indirect With Postincrement +.IP Syntax: 8 +@ ( A sub n )+ @ +.PP +The address of the operand is in the address register specified. After the +operand address is used, the address register is incremented by one, two or +four depending upon whether the size of the operand is byte, word or long. +If the address register is the stack pointer and the operand size is byte, the +address register is incremented by two rather than one to keep the stack pointer +on a word boundary. +.NH 4 +Address Register Indirect With Predecrement +.IP Syntax: 8 +@ -( A sub n ) @ +.PP +The address of the operand is in the address register specified. Before the +operand address is used, the address register is decremented by one, two or +four depending upon whether the size of the operand is byte, word or long. +If the address register is the stack pointer and the operand size is byte, the +address register is decremented by two rather than one to keep the stack pointer +on a word boundary. +.NH 4 +Address Register Indirect With Displacement +.IP Syntax: 8 +@ d sub 16 ( A sub n ) @ for the MC68000, @ ( d sub 16 , A sub n ) @ for the MC68020 +.PP +This address mode requires one word of extension. The address of the operand is +the sum of the contents of the address register and the sign extended 16-bit +integer in the extension word. +.NH 4 +Address Register Indirect With Index +.IP Syntax: 8 +@ d sub 8 ( A sub n , X sub n .size) @ for the MC68000, @ ( d sub 8 , A sub n , X sub n .size) @ for the MC68020 +.PP +This address mode requires one word of extension according to a certain format, +which specifies +.IP 1. +which register to use as index register; +.IP 2. +a flag that indicates whether the index register is a data register or an +address register; +.IP 3. +a flag that indicates the index size; this is +.I word +when the low order part of the index register is to be used, and +.I long +when the whole long value in the register is to be used as index; +.IP 4. +an 8-bit displacement integer (the low order byte of the extension word). +.PP +The address of the operand is the sum of the contents of the address register, +the possibly sign extended contents of index register and the sign +extended 8-bit displacement. +.NH 4 +Absolute Data Addressing +.IP Syntax: 8 +@ address @ for the MC68000, @ ( address ) @ for the MC68020 +.PP +Two different kinds of this mode are available: +.IP 1. +Absolute Short Address; this mode requires one word of extension. The address of +the operand is the sign extended 16-bit extension word. +.IP 2. +Absolute Long Address; this mode requires two words of extension. The address of +the operand is developed by concatenation of the two extension words; the high +order part of the address is the first extension word, the low order part is +the second. +.NH 4 +Program Counter With Displacement. +.IP Syntax: 8 +@ d sub 16 ( PC ) @ for the MC68000, @ ( d sub 16 , PC ) @ for the MC68020 +.PP +This mode requires one word of extension. The address of the operand is the sum +of the address in the program counter and the sign extended 16-bit displacement +integer in the extension word. The value in the program counter is the +address of the extension word. +.NH 4 +Program Counter With Index +.IP Syntax: 8 +@ d sub 8 ( PC , X sub n .size ) @ for the MC68000, @ ( d sub 8 , PC, X sub n .size ) @ for the MC68020 +.PP +This mode requires one word of extension as described under +.I +Address Register Indirect With Index. +.R +The address of the operand is the sum of the value in the +program counter, the possibly sign extended index register and the sign +extended 8-bit displacement integer in the extension word. +The value in the program counter is the address of the extension word. +.NH 4 +Immediate Data +.IP Syntax: 8 +@ \#data @ +.PP +This addressing mode requires either one or two words of extension, depending +on the size of the operation; +.IP +byte operation - the operand is in the low order byte of extension word; +.IP +word operation - the operand is in the extension word; +.IP +long operation - the operand is in the two extension words, the high order +16-bits are in the first extension word, the low order 16-bits in the second. +.NH 3 +Extra MC68020 addressing modes +.PP +The MC68020 has three more addressing modes. These modes all use a displacement +(some even two), an address register and an index register. Instead of the +address register one may also use the program counter. Any of these +may be omitted. If all addends are omitted the processor creates an +effective address of zero. All of these three modes require at least one +extension word, the +.I +Full Format Extension Word, +.R +which specifies: +.IP 1. +the index register number (0-7); +.IP 2. +the index register type (address or data register); +.IP 3. +the size of the index (only low order part or the whole register) +.IP 4. +a scale factor. This is a number from 0 to 3 which specifies how many bits +the contents of the index register is to be shifted to the left before being +used as an index; +.IP 5. +a flag that specifies whether the base (address) register is to be added or +to be suppressed; +.IP 6. +a flag that specifies whether to add or suppress the index operand; +.IP 7. +two bits that specify the size of the base displacement (null, word or long); +.IP 8. +three bits that in combination with (6) above specify which of the three +addressing modes (described below) to use and, if used, the size of the +outer displacement (null, word or long). +.IP N.B. +All modes mentioned above for the MC68000 +that use an index register may have this register +scaled (only when using the MC68020). +.PP +The three extra addressing modes are: +.NH 4 +Address Register Indirect With Index (Base Displacement) +.IP Syntax: 8 +@ ( bd , A sub n , X sub n .size*scale ) @ (MC68020 only) +.PP +The address of the operand is the sum of the contents of the address register, +the scaled contents of the possibly scaled index register and the possibly +sign extended base displacement. When the program counter is used instead +of the address register, the value in the program counter is the address +of the full format extension word. This mode requires one or two more extension +words when the size of the base displacement is word or long respectively. +.PP +Note that without the index operand, this mode is an extension of the +.I +Address Register Indirect With Displacement +.R +mode; when using the MC68020 one is no longer limited to a 16-bit displacement. +Also note that with the index operand added, this mode is an extension +of the +.I +Address Register Indirect With Index +.R +mode; when using the MC68020 one is no longer limited to an 8-bit displacement. +.NH 4 +Memory Indirect Post-Indexed +.IP Syntax: 8 +@ ( [ bd , A sub n ] , X sub n .size*scale , od ) @ (MC68020 only) +.PP +This mode may use an outer displacement. First an intermediate memory +address is calculated by adding the contents of the address register and +the possibly sign extended base displacement. This address is used +for in indirect memory access of a long word, followed by adding +the index operand (scaled and possibly signed extended). Finally the +outer displacement is added to yield the address of the operand. +When the program counter is used, the value in the program counter is the +address of the full format extension word. +.NH 4 +Memory Indirect Pre-Indexed +.IP Syntax: 8 +@ ( [ bd , A sub n , X sub n .size*scale ] , od ) @ (MC68020 only) +.PP +This mode may use an outer displacement. First an intermediate memory +address is calculated by adding the contents of the address register, +the scaled contents of the possibly sign extended index register and +the possibly sign extended base displacement. This address is used +for an indirect memory access of a long word, followed by adding +the outer displacement to yield the address of the operand. +When the program counter is used, the value in the program counter is the +address of the full format extension word. +.NH 3 +Addressing modes used in the table +.PP +Not all addressing modes mentioned above are used in code generation. It is +clear that none of the modes that use the program counter PC can be used, +since at code generation time nothing is known about the value in PC. +Also some of the possibilities of the three MC68020 addressing modes are not +used; e.g. it is possible to use a +.I +Data Register Indirect +.R +mode, which actually is the +.I +Address Register Indirect With Index +.R +mode, with the address register and the displacement left out. However +such a mode would require two extra bytes for the full format extension word, +and it would also be much slower than using +.I +Address Register Indirect. +.R +For this kind of reasons several possible addressing modes are not used in the +generation of code. +In the table address registers are only used for holding addresses, and +for index registers only data registers are used. +.NH +The M68000 and MC68020 back end table +.PP +The table itself has to be run through the C preprocessor +before it can be used to generate +the back end (called +.I +code generator +.R +or +.I cg +for short). When no flags are given to +the preprocessor an MC68020 code generator is produced; for the MC68000 +code generator one has to run the table through the preprocessor using the +.I -Dm68k4 +flag. +.PP +The table is designed as described in [4]. For the overall design of a back +end table one is referred to this document. This section only deals +with problems encountered in writing the table and other things worth noting. +.NH 2 +Constant Definitions +.PP +Wordsize and pointersize (EM_WSIZE and EM_PSIZE respectively) are defined +as four (bytes). EM_BSIZE, the hole between AB (the parameter base) and +LB (the local base), is eight bytes: only +the return address and the localbase are saved. +.NH 2 +Properties +.PP +Since Hans van Staveren in his document [4] clearly states that +.I cg +execution time is negatively influenced by the number of properties, only +four different properties have been defined. Besides, since the registers +really are multifunctional, these four are really all that are needed. +.NH 2 +Registers +.PP +The table uses register variables: @ D sub 3 @ - @ D sub 7 @ are used as general register +variables, and address registers @ A sub 2 @ - @ A sub 5 @ are used as pointer register +variables. @ A sub 6 @ is reserved for the localbase. +.NH 2 +Tokens +.PP +At first glance one might wonder about the amount of tokens, especially +for the MC68020, considering the small amount of different addressing modes. +However, the last three addressing modes mentioned for the MC68020 may +omit any of the addends, and this leads to a large amount of different tokens. +I did consider the possibility of enlarging the number of tokens and sets +even further, because there might be assemblers that don't handle displacements +of zero optimally (they might generate a 2 byte extension word holding zero). +The small profit in bytes in the generated code +however does not justify the increase +in size of the token section, the set section and the patterns section, +so this idea was not developed any further. +.PP +The timing cost of the tokens may be incorrect for some MC68000 tokens. +This is because the MC68000 uses a 16-bit data bus which causes the need +of two separate memory accesses for getting 32-bit operands. +.NH 3 +Token names +.PP +The amount of tokens and the limited capability of the authors imagination +might have caused the names of some tokens not to be very clarifying. +Some information about the names may be in place here. +.PP +Whenever part of a token name is in capitals that part is memory indirected +(i.e. in square brackets). In token names +.I OFF +and +.I off +mean an offsetted address register, so an address register with a displacement +(either base displacement or outer displacement). +.I +IND, ind +.R +and +.I index +stand for indexed, or index register. +.I ABS +and +.I abs +stand for absolute, which actually is just a displacement (base or outer). +These `rules' only apply to names of tokens that represent actual operands. +There are also tokens that represent addresses of operands. These +(with a few exceptions) contain +.I +regA, regX +.R +and +.I con +as parts of there names, which stand for address register, index register and +displacement (always base displacement) respectively. If the address to which +the token refers uses memory indirection, that part of the name comes first +(in small letters), followed by an underscore. The memory indirection part +follows the `rules' for operand token names. +.PP +Of course there are exceptions to these `rules' but in those cases the names +are self explanatory. +.PP +Two special cases: +.I ext_regX +is the name of the token that represents the +address of an absolute indexed operand, syntax @ ( bd , X sub n .size*scale ) @; +.I regX +does not represent any real mode, but is used with EM array instructions and +pointer arithmetic. +.NH 3 +Special tokens for the MC68000 +.PP +The MC68000 requires two extra tokens, which are called +.I t_regAcon +and +.I +t_regAregXcon. +.R +They are necessary because +.I regAcon +can only have a 16-bit displacement on the MC68000, and +.I regAregXcon +uses only 8 bits for its displacement. To prevent these addressing modes to +be used with displacements that are too large, the extra tokens are needed. +Whenever the displacements become too large and they need +to be used in the generation +of assembly code, these tokens are transformed into other tokens. +To prevent the table from becoming too messy I defined +.I t_regAcon +and +.I t_regAregXcon +to be identical to +.I regAcon +and +.I regAregXcon +respectively for the MC68020. +.NH 2 +Sets +.PP +Most set names used in the table are self explanatory, especially to the reader +who is familiar with the four addressing categories as mentioned in [5]: +.I +data, memory, alterable +.R +and +.I +control. +.R +In the sets definition part some sets are defined that are not used elsewhere in +the table, but are only used to be part of the definition of +some other set. This keeps the +set definition part from getting too unreadable. +.PP +The sets called +.I imm_cmp +consist of all tokens that can be used to compare with a constant. +.NH 2 +Instructions +.PP +Only the instructions that are used in code generation are listed here. +The first few instructions are meant especially for the use with register +variables. The operand LOCAL used here refers to a register variable. +The reader may not conclude that these operations are also allowed on +ordinary locals. The space and timing cost of these instructions have been +adapted, but the use of the word LOCAL for register variables causes these cost +to be inaccurate anyway. +.PP +The +.I killreg +instruction, which generates a comment in the assembly language output and +which is meant to let +.I cg +know that the data register operand has its contents destroyed, +needs some explaining but this explanation is better in place +in the discussion of groups 3 and 4 of the section about patterns. +.PP +The timing cost of the instructions are probably not very accurate for the +MC68020 because the MC68020 uses an instruction cache and prefetch. The +cost used in the table are the `worst case cost' as mentioned in section 9 +of [5]. +.NH 2 +Moves +.PP +These are all pretty straightforward, except perhaps when +.I t_regAcon +and +.I t_regAregXcon +are used. In these cases the size of the displacement has to be checked +before moving. This also applies to the stacking rules and the coercions. +.NH 2 +Tests +.PP +These three tests (one fore each operation size) could not be more +straightforward than they are now. +.NH 2 +Stackingrules +.PP +The only peculiar stackingrule is the one for +.I +regX. +.R +This token is only used with EM array instructions and +with pointer arithmetic. Whenever it is put +on the fake stack, some EM instructions are left in the instruction stream +to remove this token. Consequently it should never have to be stacked. However +the +.I +code generator generator +.R +(or +.I cgg +for short) +complained about not having a stackingrule for this token, so it had to +be added nevertheless. +.NH 2 +Coercions +.PP +These are all straightforward. There are no splitting coercions since +the fake stack never contains any tokens that can be split. +There are only two unstacking coercions. +The rest are all transforming coercions. Almost all coercions transform +tokens into either a data register or an address register, except in the +MC68000 part of the table the +.I t_regAcon +and +.I t_regAregXcon +tokens are transformed into real +.I regAcon +and +.I regAregXcon +tokens with displacements that are properly sized. +.NH 2 +Patterns +.PP +This is the largest part of the table. It is subdivided into 17 groups. +We will take a closer look at the more interesting groups. +.NH 3 +Group 0: rules for register variables +.PP +This group makes sure that EM instructions using register variables are +handled efficiently. This group includes: local loads and +stores; arithmetic, shifts and logical operations on locals and indirect locals +and pointer handling, where C expressions like +.I +*cp++ +.R +are handled. For such an expression there are several EM instruction +sequences the front end might generate. For an integer pointer e.g.: +.DS +.B +lol lol adp stl loi $1==$2 && $1==$4 && $3==4 && $5==4 +.I +.DE +or +.DS +.B +lol loi lol adp stl $1==$3 && $3==$5 && $2==4 && $5==4 +.I +.DE +or perhaps even +.DS +.B +lil lol adp stl $1==$2 && $2==$4 && $3==4 +.I +.DE +Each of these is included, since which one is generated is is up to the front +end. If the front end is consistent this will mean that some of these patterns +will never be used in code generation. This might seem a waist, but anyone +who thinks that will certainly change his mind when his new C front end +generates a different EM instruction sequence. +.NH 3 +Groups 1 and 2: load and store instructions +.PP +In these groups +.B lof +and +.B stf +, +.B loi +and +.B sti +, +.B ldf +and +.B sdf +are the important instructions. +These are the large parts in this group, especially the +.B loi +and +.B sti +instructions, because they come in three basic sizes (byte, word and long). +Note that with these instructions in the MC68000 part the +.I exact +is omitted in front of +.I regAcon +and +.I +regAregXcon. +.R +This makes sure that +.I t_regAcon +and +.I t_regAregXcon +are transformed into proper tokens before they are used as addresses. +.PP +Also note that the +.I regAregXcon +token is completely left out from the +\fBlof\fR, \fBstf\fR, \fBldf\fR and \fBsdf\fR +instruction handling. This is because the sum of the token displacement +and the offset provided in the instruction cannot be checked and is likely +to exceed 8 bits. Unfortunately +.I cgg +does not allow the inspection of subregisters of tokens that are on the +fake stack. This same problem might also occur with the +.I regAcon +token, but this is less likely because it +uses 16-bit displacements. Besides if it would have been left out the +\fBlof\fR, \fBstf\fR, \fBldf\fR and \fBsdf\fR +instructions would have been handled considerably less efficient. +.NH 3 +Groups 3 and 4: integer and unsigned arithmetic +.PP +EM instruction +.B sbi +also works with address registers, because the +.B cmp +instruction in group 12 is replaced by \fBsbi 4\fR. +.PP +For the MC68000 \fBmli\fR, \fBmlu\fR, \fBdvi\fR, \fBdvu\fR, \fBrmi\fR +and \fBrmu\fR are handled +by library routines. This is because the MC68000 has only 16-bit multiplications +and divisions. +.PP +The MC68020 does have 32-bit multiplications and divisions, but for the +.B rmi +and +.B rmu +EM instructions peculiar things happen anyway: they generate the +.I killreg +instruction. This is necessary because the data register that +first held the dividend now holds the quotient; the original contents are +destroyed without +.I cg +knowing about it (the destruction of the two registers that make up the +.I DREG_pair +token couldn't be noted in the instructions part of the table). +To let +.I cg +know that these contents are destroyed, we have to use this `pseudo instruction' +from lack of a better solution. +.NH 3 +Group 5: floating point arithmetic +.PP +Since floating point arithmetic is not implemented traps will be generated here. +.NH 3 +Group 6: pointer arithmetic +.PP +This also is a very important group, along with groups 1 and 2. The MC68020 +has many different addressing modes and if possible they should be used in +the generation of assembly language. +.PP +The +.I regX +token is generated here too. It is meant to make efficient use of the +MC68020 possibility of scaling index registers. +.PP +Note that I would have liked one extra pattern to handle C-statements +like +.DS +.I +pointer += expr ? constant1 : constant2; +.R +.DE +efficiently. This pattern would have looked like: +.DS +pat ads +with const +leaving adp %1.num +.DE +but when +.I cg +is coming to the EM replacement part, the constant has already been removed +from the fake stack, causing +.I %1.num +to have a wrong value. +.NH 3 +Group 9: logical instructions +.PP +The EM instructions \fBand\fR, +.B ior +and +.B xor +are so much alike that procedures can be used here, except for the +.B +xor $1==4 +.R +instruction, because the MC68000 +.I eor +instruction does not allow as many kinds of operands as +.I and +and +.I +or. +.R +.NH 3 +Group 11: arrays +.PP +This group also tries to make efficient use of the available addressing modes, +but it leaves the actual work to group 6 mentioned above. +.PP +The +.I regX +token is also generated here. In this group this token is very useful for +handling array instructions for arrays with one, two, four or eight byte +elements; the array index goes into the index register, which can then +be scaled appropriately. An offset is used when the +first array element has an index other than zero. +.PP +I would have liked some extra patterns here too but they won't work +for the same reasons as explained in the discussion of group 6. +.NH 3 +Group 14: procedure calls instructions +.PP +The function return area consists of registers @ D sub 0 @ and @ D sub 1 @. +.NH 3 +Group 15: miscellaneous instructions +.PP +In many cases here library routines are called. These will be discussed +later. +.PP +Two special EM instructions are included here: \fBdch\fR, and \fBlpb\fR. +I don't know when they are generated by a front end, but these +instructions were also in the back end table for the PDP. In the PDP table +these instructions were replaced by +.B +loi 4 +.R +and +.B +adp 8 +.R +respectively. I included them both, since they couldn't do any harm. +.NH 3 +Extra group: optimalization +.PP +This group is handling EM patterns with more than one instruction. This group +is not absolutely necessary but it makes the generation of code +more efficient. Among the things that are handled here are: arithmetic and +logical operations on locals, externals and indirect locals; shifting +of locals, externals and indirect locals by one; some pointer arithmetic; tests +in combination with logical and's and or's or with branches. Finally +there are sixteen patterns about divisions that could be handled more +efficiently by right shifts and which I think should be handled by the +peephole optimizer (since it also handles +the same patterns with multiplication). +.NH +The library routines +.PP +The table is supplied with two separate libraries: one for the MC68000 and one +for the MC68020. The MC68000 uses a couple more routines than the MC68020 +because it doesn't have 32-bit division and multiplication. +.PP +The routines that need to pop their operands first store their return address. +Routines that need other register besides @ D sub 0 @-@ D sub 2 @ and @ A sub 0 @-@ A sub 1 @ first store +the original contents of those registers. @ D sub 0 @-@ D sub 2 @ and @ A sub 0 @-@ A sub 1 @ do not have +to be saved because if they contain anything useful, their contents +are pushed on the stack before the routine is called. +.PP +The +.I .trp +routine just prints a message stating the trap number and exits (except +of course when that particular trap number is masked). Usually higher +level languages use their own trap handling routines. +.PP +The +.I .mon +routine doesn't do anything useful at all. It just prints a message stating that +the specified system call is not implemented and then exits. Front ends +usually generate calls to special routines rather than the EM +instruction \fBmon\fR. +These routines have to be supplied in another library. They +may be system dependent (e.g. the MC68000 machine this table was tested on +first moves the parameters to registers, then moves the system call number +to @ D sub 0 @ and then executes +.I +trap #0, +.R +whereas the MC68020 machine this table was tested on required the parameters +to be on the stack rather than in registers). Therefor this library is not +discussed here. +.PP +The +.I .printf +routine is included for EM diagnostic messages. It can print strings using %s, +16-bit decimal numbers using %d and 32-bit hexadecimal numbers using %x. +.PP +The +.I .strhp +routine stores a new EM heap pointer, and sometimes it needs to allocate more +heap space. This is done by calling the system call routine \fI_brk\fR. +Chunks of 1K bytes are allocated, but this can easily be changed into +larger or smaller chunks. +.PP +The MC68000 library also contains a routine to handle the EM instruction \fBrck\fR. +The MC68020 has an instruction +.I cmp2 +that is specially meant for range checking so the MC68020 library can do without +that routine. +.PP +The MC68000 library has two multiplication routines, one for unsigned and the other +for signed multiplication. The one for signed multiplication +first tests the sizes of the operands, to see if it can perform +the 16 bit machine instruction instead of the routine. If not, it considers +it's two operands being two digit numbers in a 65535-radix system. It +uses the 16-bit unsigned multiply instruction +.I mulu +three times (it does not calculate the high order result), +and adds up the intermediary results the proper way. The signed +multiplication routine calculates the sign of the result, calculates +the result as it it were an unsigned multiplication, and +adjusts the sign of the result. Here testing +the operands for there sizes would be less simple, because the operands +are signeds; so that is not done here. +.PP +The MC68000 library also has two division routines. The routine for unsigned +division uses the popular algorithm, where the divisor is shifted out and +the quotient shifted in. The signed division routine calculates the sign of +both the quotient and the remainder, calls the unsigned division routine +and adjusts the signs for the quotient and the remainder. +.PP +The +.I .nop +routine is included for testing purposes. This routine prints the line +number and the value in the stack pointer. Calls to this routine +are generated by the EM instruction \fBnop\fR, which is ordinarily +left out by the peephole optimizer. +.NH +Testing the table +.PP +There are special test programs available for testing back end tables. +First there is the EM test set, which tests most EM instructions, making +good use of the +.B nop +instruction. Then there are the Pascal and C test programs. The Pascal +test programs report errors, which makes it relatively easy +to find out what was wrong in the table. The C test programs just +generate some output, which then has to be compared to the expected +output. Differences are +not only caused by errors but also e.g. by the use of four +byte integers and unsigneds (which this table does), +the use of signed characters +instead of unsigned characters (the C front end I used generated signed +characters) or because the back end +does not support floating point. +These differences have to be `filtered out' to reveal +the differences caused by actual errors in the back end table. +These errors then have to be found out by examining the assembly code, for +no proper diagnostic messages are generated. +.PP +After these three basic tests there still remain a number of patterns that +haven't been tested yet. Fortunately +.I cgg +offers the possibility of generating a special +.I cg +that can print a list of patterns that haven't been used in +code generation yet. +For these patterns the table writer has to write his own test programs. +This may complicate things a bit because errors may now be caused by +errors in the back end table as well as errors in the test programs. +The latter happened quite often to me, because I found EM +to be an uncomfortable programming language (of course it isn't meant to +be a programming language, but an intermediary language). +.PP +There still remain a couple of patterns in this table that haven't been tested +yet. However these patterns all have very similar cases that have been +tested (an example of this is mentioned in the section on group 0 +of the patterns section of the table). Some patterns have to +do with floating point numbers. These EM instructions all generate +traps, so they didn't all have to be tested. The two instructions +.B dch +and +.B lpb +haven't been tested in this table, but since they only use EM replacement +and they have been tested in the PDP back end table, these two should +be all right. +.NH +Performance of the back end +.PP +To test the performance of the back end I gathered a couple of +C programs and compiled them on the machines I used to test the back ends on. +I compiled them using the C compiler that was available there and +I also compiled them using the back end. I then compared the sizes +of the text segments in the object files. +The final results of these comparisons are in fig. 1 and fig. 2. +.KF +.TS +center box; +cfI s s s s s +c s s s s s +c c | c s | c s +c c | c s | c s +c | c | c c | c c +l | n | n n | n n. +Differences in text segment sizes for the MC68000 +parts of the back end compiled by itself +_ +original old m68k4 new MC68000 +compiler (100%) back end back end +_ +name size size perc. size perc. +_ +codegen.c 13892 16224 116.7% 12860 92.5% +compute.c 4340 4502 103.7% 4530 104.3% +equiv.c 680 662 97.3% 598 87.9% +fillem.c 8016 7304 91.1% 6880 85.8% +gencode.c 1356 1194 88.0% 1130 83.3% +glosym.c 224 202 90.1% 190 84.8% +main.c 732 672 91.8% 634 86.6% +move.c 1876 1526 81.3% 1410 75.1% +nextem.c 1288 1594 123.7% 1192 92.5% +reg.c 1076 1014 94.2% 916 85.1% +regvar.c 1352 1188 87.8% 1150 85.0% +salloc.c 1240 1100 88.7% 1024 82.5% +state.c 628 600 95.5% 532 84.7% +subr.c 6948 6382 91.8% 5680 81.7% += +averages 2939 3155 95.8% 2766 86.6% +.TE +.DS C +fig 1. +.DE +.KE +.KF +.TS +center box; +cfI s s s +cfI s s s +c s s s +c s s s +c c | c s +c c | c s +c | c | c c +l | n | n n. +Differences in text segment sizes +for the MC68020 +parts of the back end +compiled by itself +_ +original MC68020 +compiler (100%) back end +_ +name size size perc. +_ +codegen.c 12608 12134 96.2% +compute.c 4624 4416 95.5% +equiv.c 572 504 88.1% +fillem.c 7780 6976 89.6% +gencode.c 1320 1086 82.2% +glosym.c 228 182 79.8% +main.c 736 596 80.9% +move.c 1392 1280 91.9% +nextem.c 1176 1066 90.6% +reg.c 1052 836 79.4% +regvar.c 1196 968 80.9% +salloc.c 1200 932 77.6% +state.c 580 528 91.0% +subr.c 6136 5268 85.8% += +averages 2900 2627 86.4% +.TE +.DS C +fig 2. +.DE +.KE +Fig. 1 also includes results of an old m68k4 back end (a back end +for the MC68000 with four byte word and pointersize). The table for +this back end was given to me as an example, but I thought it didn't make +good use of the MC68000's addressing capabilities, it hardly did any +optimalization, and it sometimes even +generated code that the assembler would not swallow. +This was sufficient reason for me to write a completely new table. +.PP +The results from the table may not be taken too seriously. The sizes measured +are the sizes of the text segments of the user programs, i.e. without the +inclusion of library routines. Of course these segments do contain calls +to these routines. Another thing is that the +.I rom +segment may be included in the text segment (this is why the +results for the MC68000 for +.I compute.c +look so bad). +.PP +Some other things must be said about these results. +The quality of EM code +generated by the C front end is certainly not optimal. The front end +uses temporary locals (extra locals that are used to evaluate expressions) +far too quickly: for a simple C expression like +.DS +.I +*(pointer) += constant +.R +.DE +where +.I pointer +is a register variable, the C front end generates (for obscure reasons) +a temporary local that holds the contents of \fIpointer\fR. This way +the pattern for +.DS +.B +loc lil adi sil $2==$4 && $3==4 +.R +.DE +for register variables is not used and longer, less efficient +code is generated. But even in spite of this, the back end seems to +generate rather compact code. +.NH +Some timing results +.PP +In order to measure the performance of the code generated by the back end +some timing tests were done. The reason I chose these particular tests is +that they were also done for many other back ends; the reader can compare +the results if he so wishes (of course comparing the results only +show a global difference in speed of the various machines; it doesn't +show whether some back end generates relatively better code than another). +.PP +On the MC68000 machine the statements were executed one million times. +On the MC68020 machine the statements had to be executed four million times +because this machine was so fast that timing results would be very +unreliable if the statements were executed only one million times. +.PP +For testing I used the following C test program: +.DS +.I +main() +{ + int i, j, ... + ... + for (i=0; i<1000; i++) + for (j=0; j<1000; j++) + STATEMENT; +} +.R +.DE +where +.I STATEMENT +is any of the test statements or the empty statement. For the MC68020 +tests I used 2000 instead of 1000. +The results of the test with the empty statement were used to calculate +the execution times of the other test statements. +.PP +Figures 3 and 4 show many results. For each machine actually two tests were +done: one with register variables, and the other without them. +I noticed that the original C compilers on both machines did not generate +the use of register variables, unless specifically requested. The +back end uses register variables when and where they are profitable, even +if the user did not ask for them. +.KF +.TS +center box; +cfI s s s s +c s s s s +c | c s | c s +cw(1.5i) | c c | c c +c | c c | c c +lp-2fI | n n | n n. +timing results for the MC68000 +times in @ mu @seconds +_ +test statement without register variables with register variables +_ + original new MC68000 original new MC68000 + C compiler back end C compiler back end +_ +int1=0; 2.8 2.7 0.5 0.5 +int1=int2-1; 4.1 4.1 1.3 1.3 +int1=int1+1; 4.1 4.1 1.3 1.3 +int1=int2*int3; 40.0 40.5 36.2 36.8 +T{ +int1=(int2<0); +\/*true*/ +T} 5.5 7.3 2.0 4.5 +T{ +int1=(int2<0); +\/*false*/ +T} 4.7 8.5 2.8 5.6 +T{ +int1=(int2<3); +\/*true*/ +T} 6.2 7.7 2.6 5.4 +T{ +int1=(int2<3); +\/*false*/ +T} 5.4 8.9 3.6 6.5 +T{ +.na +int1=((int2>3)||(int2<3)); +\/* true || false */ +T} 6.0 7.8 3.4 5.4 +T{ +.na +int1=((int2>3)||(int2<3)); +\/* false || true */ +T} 9.1 10.2 5.7 7.1 +T{ +.na +switch (int1) { +case 1: int1=0; break; +case 2: int1=1; break; +} +T} 6.3 17.8 5.3 14.0 +T{ +.na +if (int1=0) int2=3; +\/*true*/ +T} 5.1 4.7 1.3 1.3 +T{ +.na +if (int1=0) int2=3; +\/*false*/ +T} 2.2 2.1 1.9 1.1 +while (int1>0) int1=int1-1; 2.2 2.1 1.1 1.1 +int1=a[int2]; 6.8 6.7 4.0 3.1 +p3(int1); 14.3 11.1 13.4 10.0 +int1=f(int2); 17.7 14.5 14.8 11.7 +s.overhead=5400; 2.8 2.7 2.9 2.7 +.TE +.DS C +Fig. 3 +.DE +.KE +.KF +.TS +center box; +cfI s s s s +c s s s s +c | c s | c s +cw(1.5i) | c c | c c +c | c c | c c +lp-2fI | n n | n n. +timing results for the MC68020 +times in @ mu @seconds +_ +test statement without register variables with register variables +_ + original new MC68020 original new MC68020 + C compiler back end C compiler back end +_ +int1=0; .25 .25 .15 .15 +int1=int2-1; 1.3 1.3 .38 .38 +int1=int1+1; 1.2 .90 .38 .15 +int1=int2*int3; 4.4 4.2 3.0 3.1 +T{ +int1=(int2<0); +\/*true*/ +T} 1.6 2.7 1.1 2.3 +T{ +int1=(int2<0); +\/*false*/ +T} 1.9 2.9 .80 2.1 +T{ +int1=(int2<3); +\/*true*/ +T} 1.7 2.8 1.2 2.6 +T{ +int1=(int2<3); +\/*false*/ +T} 2.1 3.0 .85 2.3 +T{ +.na +int1=((int2>3)||(int2<3)); +\/* true || false */ +T} 2.1 3.1 1.2 2.5 +T{ +.na +int1=((int2>3)||(int2<3)); +\/* false || true */ +T} 3.4 4.2 1.8 3.2 +T{ +.na +switch (int1) { +case 1: int1=0; break; +case 2: int1=1; break; +} +T} 2.7 8.0 2.0 6.9 +T{ +.na +if (int1=0) int2=3; +\/*true*/ +T} 1.2 1.3 .63 .63 +T{ +.na +if (int1=0) int2=3; +\/*false*/ +T} 1.7 1.6 .50 .53 +while (int1>0) int1=int1-1; 1.2 1.3 .55 .53 +int1=a[int2]; 1.8 1.8 1.0 1.0 +p3(int1); 14.8 5.5 14.1 5.0 +int1=f(int2); 16.3 6.6 15.2 5.9 +s.overhead=5400; .48 .48 .50 .50 +.TE +.DS C +Fig. 4 +.DE +.KE +.PP +The reader may have noticed that on both machines the back end seems +to generate considerably slower code for tests where a `condition' is +used in the rhs of an assignment statement. This is in fact not true: it is +the front end that generates bad code. Two examples: for the C statement +.DS +.I +int1 = (int2 < 0); +.R +.DE +the front end generates the following code for the rhs (I +used arbitrary labels): +.DS +.B +lol -16 +zlt *10 +loc 0 +bra *11 +10 +loc 1 +11 +.R +.DE +while in this case (to my opinion) it should have generated +.DS +.B +lol -16 +tlt +.R +.DE +which is much shorter. Another example: for the C statement +.DS +.I +int1 = (int2 < 3); +.B +.DE +the front end generates for the rhs +.DS +.B +lol -16 +loc 3 +blt *10 +loc 0 +bra *11 +10 +loc 1 +11 +.R +.DE +while a much better translation would be +.DS +.B +lol -16 +loc 3 +cmi 4 +tlt +.R +.DE +.PP +Another statement that the back end seems to generate slower code for is +the C switch statement. This is true, but it is also caused by +the way these things are done in EM. EM uses the +.B csa +or +.B csb +instruction, and for these two I had to use library routines. On larger +switch statements the +.I .csa +routine will perform relatively better. +.PP +The back end generates considerably faster code for procedure and function +calls, especially in the MC68020 case, and also for the C statement +.DS +.I +int1 = int1 + 1; +.R +.DE +The original C compilers use the same method for this instruction +as for +.DS +.I +int1 = int2 - 1; +.R +.DE +they perform the addition in a scratch register, and then store the +result. For the former C statement this is not necessary, because +the MC68000 and MC68020 have an instruction that can add constants +to almost anything (in this case: to locals). The MC68000 and MC68020 +back ends do use this instruction. +.NH +Some final remarks +.PP +As mentioned a few times before, the C front end compiler does not +generate optimal code and as a consequence of this the +back end does not always generate optimal code. This is especially +the case with temporary locals, which the front end generates much +too quickly, and also with conditional expressions that are +used in the rhs of an assignment statement (fortunately this is not +needed so much). +.PP +If +.I cgg +would have been able to accept operands separated by any character +instead of just by commas (in the instruction definitions part), +I wouldn't have had the need of the +.I killreg +pseudo instruction. It would also be handy to have +.I cgg +accept all normal C operators. At the moment +.I cgg +does not accept binary ands, ors and exors, even though in [4] +it is stated that +.I cgg +does accept all normal C operators. As it happens I did not need the +binary operators, but at some time in developing the table I thought +I did. +.PP +I would also like +.I cg +to do more with the condition codes information that is supplied with +each instruction in the instruction definitions section of the table. +Sometimes +.I cg +generates test instructions which actually were not necessary. This +of course causes the generated +programs to be slightly larger and slightly slower. +.PP +In spite of the few minor shortcomings mentioned above I found +.I cgg +a very comfortable tool to use. +.SH +References +.PP +.IP [1] +T. B. Steel Jr., +.I +UNCOL: The myth and the Fact, +.R +in Ann. Rev. Auto. Prog., +R. Goodman (ed.), Vol. 2 (1969), pp 325 - 344 +.IP [2] +A. S. Tanenbaum, H. van Staveren, E. G. Keizer, J. W. Stevenson, +.I +A practical toolkit for making portable compilers, +.R +Informatica Report 74, Vrije Universiteit, Amsterdam, 1983 +.IP [3] +A. S. Tanenbaum, H. van Staveren, E. G. Keizer, J. W. Stevenson, +.I +Description of an experimental machine architecture for use with +block structured languages, +.R +Informatica Report 81, Vrije Universiteit, Amsterdam, 1983 +.IP [4] +H. van Staveren +.I +The table driven code generator from the Amsterdam Compiler Kit, +Second Revised Edition, +.R +Vrije Universiteit, Amsterdam +.IP [5] +.I +MC68020 32-bit Microprocessor User's Manual, +.R +Second Edition, +Motorola Inc., 1985, 1984 +.IP [6] +.I +MC68000 16-bit Microprocessor User's Manual, +Preliminary, +.R +Motorola Inc., 1979 diff --git a/doc/nopt.doc b/doc/nopt.doc new file mode 100644 index 00000000..00e7795d --- /dev/null +++ b/doc/nopt.doc @@ -0,0 +1,586 @@ +.\" $Header$ +.TL +A Tour of the New Peephole Optimizer +.AU +B. J. McKenzie +.NH +Introduction +.LP +The peephole optimizer consists of four major parts: +.IP a) +the table describing the optimization to be performed +.IP b) +a program to parse these tables and build input and output routines to +interface to the library and a dfa based routine to recognize patterns and +make the requested replacements. +.IP c) +common routines for the library that are independent of the table of a) +.IP d) +a stand alone version of the optimizer. +.LP +The library conforms to the +.I EM_CODE(3) +module interface but with routine names of the form +.BI C_ xxx +replaced by names like +.BI O_ xxx. +Furthermore there is also no routine +.I O_getid +and no variable +.I O_tmpdir +in the module. +The library module results in calls to the usual +.I EM_CODE(3) +module. It is possible to write a front end so that it can call either the +normal +.I EM_CODE(3) +module or this new module by adding +.B +#define PEEPHOLE +.R +before the line +.B +#include +.R +This will map all calls to the routine +.BI C_ xxx +into a call to the routine +.BI O_ xxx. + +.LP +We shall now describe each of these major parts in some detail. + +.NH +The optimization table +.LP +The file +.I patterns +contains the patterns of EM instructions to be recognized by the optimizer +and the EM instructions to replace them. Each pattern may have an +optional restriction that must be satisfied before the replacement is made. +The syntax of the table will be described using extended BNF notation +used by +.I LLGen +where: +.DS +.I + [...] - are used to group items + | - is used to separate alternatives + ; - terminates a rule + ? - indicates item is optional + * - indicates item is repeated zero or more times + + - indicates item is repeated one or more times +.R +.DE +The format of each rule in the table is: +.DS +.I + rule : pattern global_restriction? ':' replacement + ; +.R +.DE +Each rule must be on a single line except that it may be broken after the +colon if the next line begins with a tab character. +The pattern has the syntax: +.DS +.I + pattern : [ EM_mnem [ local_restriction ]? ]+ + ; + EM-mnem : "An EM instruction mnemonic" + | 'lab' + ; +.R +.DE +and consists of a sequence of one or more EM instructions or +.I lab +which stands for a defined instruction label. Each EM-mnem may optionally be +followed by a local restriction on the argument of the mnemonic and take +one of the following forms depending on the type of the EM instruction it +follows: +.DS +.I + local_restriction : normal_restriction + | opt_arg_restriction + | ext_arg_restriction + ; +.R +.DE +A normal restriction is used after all types of EM instruction except for +those that allow an optional argument, (such as +.I adi +) or those involving external names, (such as +.I lae +) +and takes the form: +.DS +.I + normal_restriction : [ rel_op ]? expression + ; + rel_op : '==' + | '!=' + | '<=' + | '<' + | '>=' + | '>' + ; +.R +.DE +If the rel_op is missing, the equality +.I == +operator is assumed. The general form of expression is defined later but +basically it involves simple constants, references to EM_mnem arguments +that appear earlier in the pattern and expressions similar to those used +in C expressions. + +The form of the restriction after those EM instructions like +.I adi +whose arguments are optional takes the form: +.DS +.I + opt_arg_restriction : normal_restriction + | 'defined' + | 'undefined' + ; +.R +.DE +The +.I defined +and +.I undefined +indicate that the argument is present +or absent respectively. The normal restriction form implies that the +argument is present and satisfies the restriction. + +The form of the restriction after those EM instructions like +.I lae +whose arguments refer to external object take the form: +.DS +.I + ext_arg_restriction : patarg offset_part? + ; + offset_part : [ '+' | '-' ] expression + ; +.R +.DE +Such an argument has one of three forms: a offset with no name, an +offset form a name or an offset from a label. With no offset part +the restriction requires the argument to be identical to a previous +external argument. With an offset part it requires an identical name +part, (either empty, same name or same label) and supplies a relationship +among the offset parts. It is possible to refer to test for the same +external argument, the same name or to obtain the offset part of an external +argument using the +.I sameext +, +.I samenam +and +.I offset +functions given below. +.LP +The general form of an expression is: +.DS +.I + expression : expression binop expression + | unaryop expression + | '(' expression ')' + | bin_function '(' expression ',' expression ')' + | ext_function '(' patarg ',' patarg ')' + | 'offset' '(' patarg ')' + | patarg + | 'p' + | 'w' + | INTEGER + ; +.R +.DE +.DS +.I + bin_function : 'sfit' + | 'ufit' + | 'samesign' + | 'rotate' + ; +.R +.DE +.DS +.I + ext_function : 'samenam' + | 'sameext' + ; + patarg : '$' INTEGER + ; + binop : "As for C language" + unaryop : "As for C language" +.R +.DE +The INTEGER in the +.I patarg +refers to the first, second, etc. argument in the pattern and it is +required to refer to a pattern that appears earlier in the pattern +The +.I w +and +.I p +refer to the word size and pointer size (in bytes) respectively. The +various function test for: +.IP sfit 10 +the first argument fits as a signed value of +the number of bit specified by the second argument. +.IP ufit 10 +as for sfit but for unsigned values. +.IP samesign 10 +the first argument has the same sign as the second. +.IP rotate 10 +the value of the first argument rotated by the number of bit specified +by the second argument. +.IP samenam 10 +both arguments refer to externals and have either no name, the same name +or same label. +.IP sameext 10 +both arguments refer to the same external. +.IP offset 10 +the argument is an external and this yields it offset part. + +.LP +The global restriction takes the form: +.DS +.I + global_restriction : '?' expression + ; +.R +.DE +and is used to express restrictions that cannot be expressed as simple +restrictions on a single argument or are can be expressed in a more +readable fashion as a global restriction. An example of such a rule is: +.DS +.I + dup w ldl stf ? p==2*w : ldl $2 stf $3 ldl $2 lof $3 +.R +.DE +which says that this rule only applies if the pointer size is twice the +word size. + +.NH +Incompatibilities with Previous Optimizer +.LP +The current table format is not compatible with previous versions of the +peephole optimizer tables. In particular the previous table had no provision +for local restrictions and only the equivalent of the global restriction. +This meant that our +.I '?' +character that announces the presence of the optional global restriction was +not required. The previous optimizer performed a number of other tasks that +were unrelated to optimization that were possible because the old optimizer +read the EM code for a complete procedure at a time. This included tasks such +as register variable reference counting and moving the information regarding +the number of bytes of local storage required by a procedure from it +.I end +pseudo instruction to it's +.I pro +pseudo instruction. These tasks are no longer done by this module but have +been moved to other modules or programs in the pipeline. The register variable +reference counting is now performed by the front end. The reordering of +code, such as the moving of mes instructions and the local storage +requirements from the end to beginning of procedures, is now performed using +the insertpart mechanism in the +.I EM_CODE +(or +.I EM_OPT +) module. +The removal of dead code is performed by the global optimizer. +Various +.I ext_functions +available in the old tables are no longer available as they rely on +information that is not available to the current program. +These are the +.I notreg +and the +.I rom +functions. +The previous optimizer allowed the use of +.I LLP, +.I LEP, +.I SLP +and +.I SEP +in patterns. For example +.I LLP +stood for either +.I lol +if the pointer size was the same as the word size, or for +.I ldl +if the pointer size was twice the word size. +In the current optimizer it is necessary to include two patterns for each +such single pattern in the old table. For example for a pattern containing +.I LLP +there would be one pattern with +.I lol +and with a global restriction of the form +.I p=w +and another pattern with ldl and a global restriction of the form +.I p=2*w. + +.NH +The Parser +.LP +The program to parse the tables and build the pattern table dependent dfa +routines is built from the files: +.IP parser.h 15 +header file +.IP parser.g 15 +LLGen source file defining syntax of table +.IP syntax.l 15 +Lex sources file defining form of tokens in table. +.IP initlex.c 15 +Uses the data in the library +.I em_data.a +to initialize the lexical analyzer to recognize EM instruction mnemonics. +.IP outputdfa.c 15 +Routines to output the dfa when it has been constructed. It outputs the files +.I dfa.c +and +.I trans.c +.IP outcalls.c 15 +Routines to output the file +.I incalls.r +defined in the next section. +.IP findworst.c 15 +Routines to analyze patterns to find how to continue matching after a +successful replacement or failed match. + +.LP +The parser checks that the tables conform to the syntax outlined in the +previous section and also makes a number of semantic checks on their +validity. Further versions could make further checks such as looking for +cycles in the rules or checking that each replacement leaves the same +number of bytes on the stack as the pattern it replaces. The parser +builds an internal dfa representation of the rules by combining rules with +common prefixes. All local and global restrictions are combined into a single +test to be performed are a complete pattern has been detected in the input. +The idea is to build a structure so that each of the patterns can be matched +and then the corresponding tests made and the first that succeeds is replaced. +If two rules have the same pattern and both their tests also succeed the one +that appears first in the tables file will be done. Somewhat less obvious +is that if one pattern is a proper prefix of a longer pattern and its test +succeeds then the second pattern will not be checked for. + +A major task of the parser if to decide on the action to take when a rule has +been partially matched or when a pattern has been completely matched but its +test does not succeed. This requires a search of all patterns to see if any +part of the part matched could be part of some other pattern. for example +given the two patterns: +.DS +.I + loc adi w loc adi w : loc $1+$3 adi w + loc adi w loc sbi w : loc $1-$3 adi w +.R +.DE +If the first pattern fails after seeing the input: +.DS +.I + loc adi loc +.R +.DE +the parser will still need to check whether the second pattern matches. +This requires a decision on how to fix up any internal data structures in +the dfa matcher, such as moving some instructions from the pattern to the +output queue and moving the pattern along and then deciding what state +it should continue from. Similar decisions are requires after a pattern +has been replaced. For example if the replacement is empty it is necessary +to backup +.I n-1 +instructions where +.I n +is the length of the longest pattern in the tables. + +.NH +Structure of the Resulting Library + +.LP +The major data structures maintained by the library consist of three queues; +an +.I output +queue of instructions awaiting output, a +.I pattern +queue containing instructions that match the current prefix, and a +.I backup +queue of instructions that have been backed up over and need to be reparsed +for further pattern matches. +These three queues are maintained in a single fixed size buffer as explained +in more detail in the next section. +Also, after a successful match, a replacement queue is constructed. + + +.LP +If no errors are detected by the parser in the tables it output the following +files if they have changed from the existing version of the file: +.IP dfa.c 10 +this contains the dfa encoded into a number of arrays using the technique +of row displacement for compacted sparse matricies. Given an opcode and +the current state, the value of +.I OO_base[OO_state] +is consulted to obtain a pointer into the array +.I OO_checknext. +If this pointer in zero or the +.I check +field of the addressed structure does +not correspond to the curerent state then it is known there is no entry for +this opcode/state pair and the +.I OO_default +array is consulted instead. +If the check field does match then the +.I next +field contains the new state. +After each transition the array +.I OO_ftrans +is consulted to see if this state corresponds to a final state +(i.e. a complete pattern) and if so the corresponding function is called. +.IP trans.c 10 +this contains external declarations of transition routines with names like +.B OO_xxxdotrans +(where +.I xxx +is a small integer). +These are called when there a transition to state +.I xxx +that corresponds to a +complete pattern. Any tests are performed if necessary to confirm that the +pattern matches and then the replacement instructions are placed on the +output queue and the routine +.I OO_mkrepl +is called to make the replacement and if backup the amount required. +If there are a number of patterns with the same instructions but different +tests, these will all appear in the same routine and the tests performed in +the order they appear in the original +.I patterns +file. +.IP incalls.r 10 +this contains an entry for every EM instruction (plus +.I lab +) giving information on how to build a routine with the name +.BI O_ xxx +for the library version of the module. +If the EM instruction does not appear in the tables +patterns at all then the dfa routine is called to flush any current queued +output and the the output +.BI C_ xxx +routine is called. If the EM instruction does appear in a pattern then the +instruction data structure fields are +initialized and it is added onto the end of the pattern queue. +The dfa routines are then called to attempted to make a transition. +This file is input to the +.I awk +program +.I makefuns.awk. + +.LP +The following files contain code that is independent of the pattern tables: +.IP main.c 10 +this is used only in the stand alone version of the optimizer and consists +of code to open the input file, read the input using the +.I READ_EM(3) +module and call the dfa routines. This version does not require the routines +constructed from the incalls.r file described above. +.IP nopt.c 10 +general routines to initialize, and maintain the data structures. The file +handling routines +.I O_open +etc are defined here. Also defined are routines for flushing the output queue +by calling the +.I EM_mkcalls +routine from the +.I READ_EM(3) +module and moving instructions from the output to the backup queue. +Routines to free the strings stored in instructions +with types of +.I sof_ptyp, +.I pro_ptyp, +.I str_ptyp, +.I ico_ptyp, +.I uco_ptyp, +and +.I fco_ptyp are also defined. These strings are copied to a large array that +is extended by +.I Realloc +if it overflows. The strings can be thrown away on any flush that occurs when +the backup queue is empty. +.IP mkstrct.c 10 +contains routines to build the data structure from the input +.BI C_ xxx +routines and place the structure on the pattern queue. These routines are also +used to build the data structures when a replacement is constructed. +.IP aux.c 10 +routines to implement the external functions used in the pattern table. + +.LP +The following files are also used in building the module library: +.IP makefuns.awk 10 +this +.I awk +program is used to produce individual C files with names like +.BI O_ xxx.c +each containing a single function definition and then call the +.I cc +compiler to produce a single output file. +This enables the loader to only load those routines that are actually +needed when the library is loaded. +.IP pseudo.r 10 +this file is like the +.I incalls.r +file produced by the parser but is built by hand and handles the pseudo +EM instructions. It is also processed by +.I makefuns.awk. + +.NH +Miscellaneous Issues +.LP +The output, pattern and backup queues are maintained in fixed length array, +.I OO_buffer +allocated of size +.I MAXBUFFER +(a constant declared in nopt.h) at run time. +It consists of an array of the +.I e_instr +data structure used by the +.I READ_EM(3) +module. +At any time the pointers +.I OO_patternqueue +and +.I OO_nxtpatt +point to the beginning and end of the current pattern prefix that corresponds +to the current state. Any instructions on the backup queue are between +.I OO_nxtpatt +and +.I OO_endbackup. +If there are no instructions on the backup queue then +.I OO_endbackup +will be 0 (zero). +The size of the replacement queue is set to the length of the maximum +replacement length by the tables output by the parser. + +.LP +The fixed size of the buffer causes no difficulty in +practice and can only result in some potential optimizations being missed. +When space for a new instruction is required and the buffer is full the +routine +.I OO_halfflush +is called to flush half the buffer and move all the data structures left. +It should be noted that it is not possible to statically determine the +maximum possible size for these queues as they need to be unbounded in +the worst case. +A study of the rule +.DS +.I + inc dec : +.R +.DE +with the input consisting of +.I N +.I inc +and then +.I N +.I dec +instructions requires an output queue length of +.I N-1 +to find all possible replacements. diff --git a/doc/occam/.distr b/doc/occam/.distr new file mode 100644 index 00000000..7c37d90b --- /dev/null +++ b/doc/occam/.distr @@ -0,0 +1,12 @@ +Makefile +ctot +p0 +p1 +p2 +p3 +p4 +p5 +p6 +p7 +p8 +p9 diff --git a/doc/occam/Makefile b/doc/occam/Makefile new file mode 100644 index 00000000..71583865 --- /dev/null +++ b/doc/occam/Makefile @@ -0,0 +1,17 @@ +EMHOME=../.. +FILES= p0 p1 p2 p3 p4 p5 p6 p7 p8 p9 + +PIC=pic +EQN=eqn +TBL=tbl +../occam.doc: p0 p1 p2 p3 p4 p5 p6 p7 p8 p9 channel.h.t channel.c.t + cat $(FILES) | $(PIC) | $(TBL) | $(EQN) > $@ + +channel.h.t: $(EMHOME)/h/ocm_chan.h + ctot <$(EMHOME)/h/ocm_chan.h >channel.h.t + +channel.c.t: channel.c + ctot channel.c.t + +channel.c: $(EMHOME)/lang/occam/lib/tail_ocm.a + arch x $(EMHOME)/lang/occam/lib/tail_ocm.a channel.c diff --git a/doc/occam/ctot b/doc/occam/ctot new file mode 100755 index 00000000..302c6910 --- /dev/null +++ b/doc/occam/ctot @@ -0,0 +1,8 @@ +sed 's/^$/.sp 0.5/ +s/\\/\\e/g +s/^ $/.ft\ +.DE\ +.bp\ +.DS\ +.ft 5\ +.ta 0.65i 1.3i 1.95i 2.6i 3.25i 3.9i 4.55i 5.2i 5.85i 6.5i/' diff --git a/doc/occam/p0 b/doc/occam/p0 new file mode 100644 index 00000000..1055ec1c --- /dev/null +++ b/doc/occam/p0 @@ -0,0 +1,21 @@ +.pl 11.7i +.ND +.de PT +.if \\n%>0 .if e .tl '\fB%\fP''' +.if \\n%>1 .if o .tl '''\fB%\fP' +.. +.TL +An Occam Compiler +.AU +Kees Bot +Edwin Scheffer +.AI +Vrije Universiteit +Amsterdam, The Netherlands +.AB +This document describes the implementation of an \fBOccam\fP to \fBEM\fP +compiler. The lexical analysis is done using \fBLex\fP. +For the semantic analysis the extended LL(1) parser generator \fBLLgen\fP is +used. To handle the Occam-specific features as channels and parallelism some +library routines are required. +.AE diff --git a/doc/occam/p1 b/doc/occam/p1 new file mode 100644 index 00000000..d1e2aa49 --- /dev/null +++ b/doc/occam/p1 @@ -0,0 +1,87 @@ +.NH +Introduction +.PP +Occam [1] is a programming language which is based on the concepts of +concurrency and communication. These concepts enable today's applications of +microprocessors and computers to be implemented more effectively. +.PP +An Occam program consists of a (dynamically determined) number +of processes communicating through channels. +To communicate with the outside world some predefined channels are needed. +A channel has only one writer and one reader; it carries machine words and +bytes, at the reader/writer's discretion. The process with its communication +in Occam replaces the procedure with parameters in other languages (there are +no procedures in Occam). +.PP +In addition to the normal assignment statement, Occam has two more +information-transfer statements, the input and the output: +.DS +.ft 5 + chan1 ? x -- reads a value from chan1 into x + chan2 ! x -- writes the value of x onto chan2 +.ft +.DE +Both the outputting and the inputting processes wait until the other is there. +Channels are declared and given names. Arrays of channels are possible. +.PP +Processes come in 5 varieties: sequential, parallel, alternative, +conditional and repetitive. A process starts with a reserved word telling +its nature, followed by an indented list of other processes. (Indentation +is used to indicate block structure.) It may be preceded by declarations. +The processes in a sequential/parallel process are executed sequentially/in +parallel. The processes in an alternative process have guards based on the +availability of input; the first to be ready is executed (this is waiting +for multiple input). The conditional and repetitive processes are normal +\fBIF\fPs and \fBWHILE\fPs. +.PP +\fIProducer-consumer example:\fP +.DS +.ft 5 +.nf +CHAN buffer: -- declares the channel buffer +PAR + WHILE TRUE -- the producer + VAR x: -- a local variable + SEQ + produce(x) -- in some way + buffer ! x -- and send it + WHILE TRUE -- the consumer + VAR x: + SEQ + buffer ? x -- get a value + consume(x) -- in some way +.ft +.fi +.DE +.bp +.PP +Processes can be replicated from a given template; this combines +with arrays of variables and/or channels. +.PP +\fIExample: 20 window-sorters in series:\fP +.DS +.ft 5 +.nf +CHAN s[20]: -- 20 channels +PAR i = [ 0 FOR 19 ] -- 19 processes + WHILE TRUE + VAR v1, v2: + SEQ + s[i] ? v1; v2 -- wait for 2 variables from s[i] + IF + v1 <= v2 -- ok + s[i+1] ! v1; v2 + v1 > v2 -- reorder + s[i+1] ! v2; v1 +.fi +.ft +.DE +.PP +A process may wait for a condition, which must include a comparison +with \fBNOW\fP, the present clock value. +.PP +Processes may be distributed over several processors; all processes +under a \fBVAR\fP declaration must run on the same processor. Concurrency can be +improved by avoiding \fBVAR\fP declarations, and replacing them by \fBCHAN\fP +declarations. Processes can be allocated explicitly on named processors and +channels can be connected to physical ports. diff --git a/doc/occam/p2 b/doc/occam/p2 new file mode 100644 index 00000000..771428c8 --- /dev/null +++ b/doc/occam/p2 @@ -0,0 +1,151 @@ +.NH +The Compiler +.PP +The compiler is written in \fBC\fP using LLgen and Lex and compiles +Occam programs to EM code, using the procedural interface as defined for EM. +In the following sub-sections we describe the LLgen parser generator and +the aspect of indentation. +.NH 2 +The LLgen Parser Generator +.PP +LLgen accepts a Context Free syntax extended with the operators `\f5*\fP', `\f5?\fP' and `\f5+\fP' +that have effects similar to those in regular expressions. +The `\f5*\fP' is the closure set operator without an upperbound; `\f5+\fP' is the positive +closure operator without an upperbound; `\f5?\fP' is the optional operator; +`\f5[\fP' and `\f5]\fP' can be used for grouping. +For example, a comma-separated list of expressions can be described as: +.DS +.ft 5 + expression_list: + expression [ ',' expression ]* + ; +.ft +.DE +.LP +Alternatives must be separated by `\f5|\fP'. +C code (``actions'') can be inserted at all points between the colon and the +semicolon. +Variables global to the complete rule can be declared just in front of the +colon enclosed in the brackets `\f5{\fP' and `\f5}\fP'. All other declarations are local to +their actions. +Nonterminals can have parameters to pass information. +A more mature version of the above example would be: +.DS +.ft 5 + expression_list(expr *e;) { expr e1, e2; } : + expression(&e1) + [ ',' expression(&e2) + { e1=append(e1, e2); } + ]* + { *e=e1; } + ; +.ft +.DE +As LLgen generates a recursive-descent parser with no backtrack, it must at all +times be able to determine what to do, based on the current input symbol. +Unfortunately, this cannot be done for all grammars. Two kinds of conflicts +are possible, viz. the \fBalternation\fP and \fBrepetition\fP conflict. +An alternation confict arises if two sides of an alternation can start with the +same symbol. E.g. +.DS +.ft 5 + plus: '+' | '+' ; +.ft +.DE +The parser doesn't know which `\f5+\fP' to choose (neither do we). +Such a conflict can be resolved by putting an \fBif-condition\fP in front of +the first conflicting production. It consists of a \fB``%if''\fP followed by a +C-expression between parentheses. +If a conflict occurs (and only if it does) the C-expression is evaluated and +parsing continues along this path if non-zero. Example: +.DS +.ft 5 + plus: + %if (some_plusses_are_more_equal_than_others()) + '+' + | + '+' + ; +.ft +.DE +A repetition conflict arises when the parser cannot decide whether +``\f5productionrule\fP'' in e.g. ``\f5[ productionrule ]*\fP'' must be chosen +once more, or that it should continue. +This kind of conflicts can be resolved by putting a \fBwhile-condition\fP right +after the opening parentheses. It consists of a \fB``%while''\fP +followed by a C-expression between parentheses. As an example, we can look at +the \fBcomma-expression\fP in C. The comma may only be used for the +comma-expression if the total expression is not part of another comma-separated +list: +.DS +.nf +.ft 5 + comma_expression: + sub_expression + [ %while (not_part_of_comma_separated_list()) + ',' sub_expression + ]* + ; +.ft +.fi +.DE +Again, the \fB``%while''\fP is only used in case of a conflict. +.LP +Error recovery is done almost completely automatically. All you have to do +is to write a routine called \fILLmessage\fP to give the necessary error +messages and supply information about terminals found missing. +.NH 2 +Indentation +.PP +The way conflicts can be resolved are of great use to Occam. The use of +indentation, to group statements, leads to many conflicts because the spaces +used for indentation are just token separators to the lexical analyzer, i.e. +``white space''. The lexical analyzer can be instructed to generate `BEGIN' and +`END' tokens at each indentation change, but that leads to great difficulties +as expressions may occupy several lines, thus leading to indentation changes +at the strangest moments. So we decided to resolve the conflicts by looking +at the indentation ourselves. The lexical analyzer puts the current indentation +level in the global variable \fIind\fP for use by the parser. The best example +is the \fBSEQ\fP construct, which exists in two flavors, one with a replicator +and one process: +.DS +.nf +.ft 5 + seq i = [ 1 for str[byte 0] ] + out ! str[byte i] +.ft +.fi +.DE +and one without a replicator and several processes: +.DS +.nf +.ft 5 + seq + in ? c + out ! c +.ft +.fi +.DE +The LLgen skeleton grammar to handle these two is: +.DS +.nf +.ft 5 + SEQ { line=yylineno; oind=ind; } + [ %if (line==yylineno) + replicator + process + | + [ %while (ind>oind) process ]* + ] +.ft +.fi +.DE +This shows clearly that, a replicator must be on the same line as the \fBSEQ\fP, +and new processes are collected as long as the indentation level of each process +is greater than the indentation level of \fBSEQ\fP (with appropriate checks on this +identation). +.PP +Different indentation styles are accepted, as long as the same amount of spaces +is used for each indentation shift. The ascii tab character sets the indentation +level to an eight space boundary. The first indentation level found in a file +is used to compare all other indentation levels to. diff --git a/doc/occam/p3 b/doc/occam/p3 new file mode 100644 index 00000000..143df659 --- /dev/null +++ b/doc/occam/p3 @@ -0,0 +1,337 @@ +.NH +Implementation +.PP +It is now time to describe the implementation of some of the occam-specific +features such as channels and \fBNOW\fP. Also the way communication with +UNIX\(dg is performed must be described. +.FS +\(dg UNIX is a trademark of Bell Laboratories +.FE +For a thorough description of the library routines to simulate parallelism, +which are e.g. used by the channel routines and by the \fBPAR\fP construct +in Appendix B, see [6]. +.NH 2 +Channels +.PP +There are currently two types of channels (see Figure 1.) indicated by the type +field of a channel variable: +.IP - +An interprocess communication channel with two additional fields: +.RS +.IP - +A synchronization field to hold the state of an interprocess communication +channel. +.IP - +An integer variable to hold the value to be send. +.RE +.IP - +An outside world communication channel. This is a member of an array of +channels connected to UNIX files. Its additional fields are: +.RS +.IP - +A flags field holding a readahead flag and a flag that tells if this channel +variable is currently connected to a file. +.IP - +A preread character, if readahead is done. +.IP - +An index field to find the corresponding UNIX file. +.RE +.LP +.PS +box ht 3.0 wid 3.0 +box ht 0.75 wid 0.75 with .nw at 1st box.nw + (0.5, -0.5) "Process 1" +box ht 0.75 wid 0.75 with .ne at 1st box.ne + (-0.5, -0.5) "Process 2" +box ht 0.75 wid 0.75 with .sw at 1st box.sw + (0.5, 0.5) "Process 3" +box ht 0.75 wid 0.75 with .se at 1st box.se + (-0.5, 0.5) "Process 4" +line right from 5/12 <2nd box.ne, 2nd box.se> to 3rd box +line right from 7/12 <2nd box.ne, 2nd box.se> to 3rd box +line right from 5/12 <4th box.ne, 4th box.se> to 5th box +line right from 7/12 <4th box.ne, 4th box.se> to 5th box +line down from 5/12 <2nd box.sw, 2nd box.se> to 4th box +line down from 7/12 <2nd box.sw, 2nd box.se> to 4th box +line down from 5/12 <3rd box.sw, 3rd box.se> to 5th box +line down from 7/12 <3rd box.sw, 3rd box.se> to 5th box +line right 1.0 from 5/12 <5th box.ne, 5th box.se> +line right 1.0 from 7/12 <5th box.ne, 5th box.se> +line left 1.0 from 5/12 <2nd box.nw, 2nd box.sw> +line left 1.0 from 7/12 <2nd box.nw, 2nd box.sw> +.PE +.DS C +\fIFigure 1. Interprocess and outside world communication channels\fP +.DE +The basic channel handling is done by \f5chan_in\fP and \f5chan_out\fP. All +other routines are based on them. The routine \f5chan_any\fP only checks if +there's a value available on a given channel. (It does not read this value!) +\f5C_init\fP initializes an array of interprocess communication channels. +.LP +The following table shows Occam statements paired with the routines used to +execute them. +.TS H +center, box; +c | c | c +lf5 | lf5 | lf5. +Occam statement Channel handling routine Called as += +.sp 0.5 +.TH +T{ +.nf +CHAN c: +CHAN c[z]: +.fi +T} T{ +.nf +c_init(c, z) +chan *c; unsigned z; +.fi +T} T{ +.nf +c_init(&c, 1); +c_init(&c, z); +.fi +T} +.sp 0.5 +_ +.sp 0.5 +T{ +.nf +c ? v +.fi +T} T{ +.nf +chan_in(v, c) +long *v; chan *c; +.fi +T} T{ +.nf +chan_in(&v, &c); +.fi +T} +.sp 0.5 +T{ +.nf +c ? b[byte i] +.fi +T} T{ +.nf +cbyte_in(b, c) +char *b; chan *c; +.fi +T} T{ +.nf +cbyte_in(&b[i], &c); +.fi +T} +.sp 0.5 +T{ +.nf +c ? a[i for z] +.fi +T} T{ +.nf +c_wa_in(a, z, c) +long *a; unsigned z; chan *c; +.fi +T} T{ +.nf +c_wa_in(&a[i], z, &c); +.fi +T} +.sp 0.5 +T{ +.nf +c ? a[byte i for z] +.fi +T} T{ +.nf +c_ba_in(a, z, c) +long *a; unsigned z; chan *c; +.fi +T} T{ +.nf +c_ba_in(&a[i], z, &c); +.fi +T} +.sp 0.5 +_ +.sp 0.5 +T{ +.nf +c ! v +.fi +T} T{ +.nf +chan_out(v, c) +long *v; chan *c; +.fi +T} T{ +.nf +chan_out(&v, &c); +.fi +T} +.sp 0.5 +T{ +.nf +c ! a[i for z] +.fi +T} T{ +.nf +c_wa_out(a, z, c) +long *a; unsigned z; chan *c; +.fi +T} T{ +.nf +c_wa_out(&a[i], z, &c); +.fi +T} +.sp 0.5 +T{ +.nf +c ! a[byte i for z] +.fi +T} T{ +.nf +c_ba_out(a, z, c) +long *a; unsigned z; chan *c; +.fi +T} T{ +.nf +c_ba_out(&a[i], z, &c); +.fi +T} +.sp 0.5 +_ +.sp 0.5 +T{ +.nf +alt + c ? .... + .... +.fi +T} T{ +.nf +int chan_any(c) +chan *c; +.fi +T} T{ +.nf +deadlock=0; +for(;;) { + if (chan_any(&c)) { + .... + .... +.fi +T} +.sp 0.5 +.TE +The code of \f5c_init\fP, \f5chan_in\fP, \f5chan_out\fP and \f5chan_any\fP +can be found in Appendix A. +.NH 3 +Synchronization on interprocess communication channels +.PP +The synchronization field can hold three different values indicating the +state the channel is in: +.IP "- \fBC\(ulS\(ulFREE\fP:" 15 +Ground state, channel not in use. +.IP "- \fBC\(ulS\(ulANY\fP:" 15 +Channel holds a value, the sending process is waiting for an acknowledgement +about its receipt. +.IP "- \fBC\(ulS\(ulACK\fP:" 15 +Channel data has been removed by a receiving process, the sending process can +set the channel free now. +.LP +A sending process cannot simply wait until the channel changes state C\(ulS\(ulANY +to state C\(ulS\(ulFREE before it continues. There is a third state needed to prevent +a third process from using the channel before our sending process is +acknowledged. Note, however that it is not allowed to use a channel for input +or output in more than one parallel process. This is too difficult to check +in practice, so we tried to smooth it a little. +.NH 2 +NOW +.PP +\fBNOW\fP evaluates to the current time returned by the time(2) system call. +The code is simply: +.DS +.ft 5 +.nf + long now() + { + deadlock=0; + return time((long *) 0); + } +.fi +.ft +.DE +The ``deadlock=0'' prevents deadlocks while using the clock. +.NH 2 +UNIX interface +.PP +To handle the communication with the outside world the following channels are +defined: +.IP - +\fBinput\fP, that corresponds with the standard input file, +.IP - +\fBoutput\fP, that corresponds with the standard output file, +.IP - +\fBerror\fP, that corresponds with the standard error file. +.IP - +\fBfile\fP, an array of channels that can be subscripted with an index +obtained by the builtin named process ``\f5open\fP''. Note that +\fBinput\fP=\fBfile\fP[0], \fBoutput\fP=\fBfile\fP[1] and +\fBerror\fP=\fBfile\fP[2]. +.LP +Builtin named processes to open and close files are defined as +.DS +.nf +.ft 5 +proc open(var index, value name[], mode[]) = ..... : +proc close(value index) = ..... : +.fi +.ft +.DE +To open a file `junk', write nonsense onto it, and close it, goes as follows: +.DS +.ft 5 +.nf + var i: + seq + open(i, "junk", "w") + file[i] ! nonsense + close(i) +.fi +.ft +.DE +Errors opening a file are reported by a negative index, which is the +negative value of the error number (called \fIerrno\fP in UNIX). +.LP +Bytes read from or written onto these channels are taken from occam variables. +As these variables can hold more than 256 values, some negative values are used +to control channels. These values are: +.IP "- \fBEOF\fP" 9 +(-1): Eof from file channel is read as -1. +.IP "- \fBTEXT\fP" 9 +(-2): A -2 written onto any channel connected to a terminal puts this +terminal in the normal line oriented mode (i.e. characters typed are echoed +and lines are buffered before they are read). +.IP "- \fBRAW\fP" 9 +(-3): A -3 written onto any channel connected to a terminal puts it in raw mode +(i.e. no echoing of typed characters and no line buffering). +.LP +To exit an Occam program, e.g. after an error, a builtin named process +\f5exit\fP is available that takes an exit code as its argument. +.NH 2 +Replicators and slices +.PP +Both the base and the count of replicators like in +.DS +.ft 5 + par i = [ base for count ] +.ft +.DE +may be arbitrary expressions. The count in array slices like in +.DS +.ft 5 + c ? A[ base for count ] +.ft +.DE +must be a constant expression however, the base is again free. diff --git a/doc/occam/p4 b/doc/occam/p4 new file mode 100644 index 00000000..e6da5169 --- /dev/null +++ b/doc/occam/p4 @@ -0,0 +1,42 @@ +.NH +Particular details +.NH 2 +Lower case/Upper case +.PP +Keywords must be either fully written in lower case or in upper case, thus +\fBPAR\fP is equivalent to \fBpar\fP but \fBPar\fP is not a keyword. Identifiers +may be of mixed case. Different styles are used in our examples just to indicate +what's accepted by the compiler. +.NH 2 +File inclusion +.PP +The C preprocessor is applied to the input file before +compilation, so that files containing useful \fBPROC\fP and \fBDEF\fP +declarations can be used in your program by using the \fB#include\fP-directive +of the preprocessor. +.NH 2 +Substitution +.PP +Named processes are not textually substituted. A procedure call is used instead. +The semantics of occam substitution imply this by letting a global variable +(i.e. not declared inside the named process' body) be found where the named +process is defined and not where it is substituted. +.NH 2 +ANY +.PP +According to the occam syntax the \fBANY\fP keyword may be the only argument of +an input or output process. Thus, +.DS +.ft 5 + c ? ANY; x +.ft +.DE +is not allowed. Because it was easy to add, and it was used by some programs, +our compiler allows it. (If you prefer portability you are advised not to make +use of it.) +.NH 2 +Configuration +.PP +The special configuration keywords like \fBPLACED\fP, \fBALLOCATE\fP, \fBPORT\fP +and \fBLOAD\fP are not implemented. Only \fBPRI\fP works because \fBPAR\fP and +\fBALT\fP work the same without it. diff --git a/doc/occam/p5 b/doc/occam/p5 new file mode 100644 index 00000000..1dc98e02 --- /dev/null +++ b/doc/occam/p5 @@ -0,0 +1,18 @@ +.NH +Conclusions +.PP +Writing the compiler was very straightforward using the LLgen parser generator. +Its extended grammar and its way of conflict resolving were of great use to us, +especially +the indentation handling could be implemented quite easily. The automatic +error recovery given by LLgen took a great weight of our shoulders. +.PP +A set of parallelism simulation routines makes implementing \fBPAR\fP constructs +very simple. And we consider it a necessity to have such a layer to shield the +compiler writer from these details. +.PP +The translation to EM code was fairly direct, no great tricks were needed to +make things work. Only the different sizes of words and pointers that are given +as parameters to the compiler must be carefully watched. Variables or pointers +must sometimes be handled with double word instructions for different word or +pointer sizes. diff --git a/doc/occam/p6 b/doc/occam/p6 new file mode 100644 index 00000000..2ce3d9da --- /dev/null +++ b/doc/occam/p6 @@ -0,0 +1,5 @@ +.NH +Acknowledgement +.PP +We want to thank Dick Grune for his description of Occam which is used +in the introduction. diff --git a/doc/occam/p7 b/doc/occam/p7 new file mode 100644 index 00000000..c9397d15 --- /dev/null +++ b/doc/occam/p7 @@ -0,0 +1,23 @@ +.bp +.NH +References +.LP +.IP [1] +INMOS limited, \fIOCCAM Programming manual\fP, Prentice-Hall, 1984. +.IP [2] +C. J. H. Jacobs, \fISome Topics in Parser Generation\fP, +Informatica Rapport IR-105, Vrije Universiteit, Amsterdam, October 1985. +.IP [3] +B. W. Kernighan and D. M. Ritchie, \fIThe C Programming Language\fP, +Prentice-Hall, 1978. +.IP [4] +M. E. Lesk, \fILex - A Lexical Analyser Generator\fP, Comp. Sci. Tech. Rep. +No. 39, Bell Laboratories, Murrey Hill, New Jersey, October 1975. +.IP [5] +A. S. Tanenbaum, H. van Staveren, E. G. Keizer, J. W. Stevenson, +\fIDescription of a Machine Architecture for use with Block Structured +Languages\fP, Informatica Rapport IR-81, Vrije Universiteit, Amsterdam, 1983. +.IP [6] +K. Bot and E. Scheffer, \fIA set of multi-process primitives for stack based +machines\fP, Vrije Universiteit, Amsterdam, 1986. +.LP diff --git a/doc/occam/p8 b/doc/occam/p8 new file mode 100644 index 00000000..88c0b58f --- /dev/null +++ b/doc/occam/p8 @@ -0,0 +1,16 @@ +.bp +.NH +Appendix A: Implementation of the channel routines +.DS L +.ft 5 +.ta 0.65i 1.3i 1.95i 2.6i 3.25i 3.9i 4.55i 5.2i 5.85i 6.5i +.so channel.h.t +.ft +.DE +.bp +.DS L +.ft 5 +.ta 0.65i 1.3i 1.95i 2.6i 3.25i 3.9i 4.55i 5.2i 5.85i 6.5i +.so channel.c.t +.ft +.DE diff --git a/doc/occam/p9 b/doc/occam/p9 new file mode 100644 index 00000000..dd8292f2 --- /dev/null +++ b/doc/occam/p9 @@ -0,0 +1,60 @@ +.bp +.NH +Appendix B: Translation of a \fBPAR\fP construct to EM code using the library +routines to simulate parallelism +.PP +Translation of the parallel construct: +.DS +.ft 5 + par + P0 + par i = [ 1 for n ] + P(i) +.DE +is +.TS +center; +lf5 lf5. + lal -20 ; Assume 20 bytes of local variables at this moment + cal $parbegin ; Set up a process group + asp 4 ; Assume pointersize = 4 + cal $parfork ; Split stack in two from local -20 + lfr 4 ; Assume wordsize = 4 + zne *23 ; One end jumps to second process, other continues here + lor 0 ; Static link + cal $P0 + asp 4 + bra *24 ; Jump to the outer parend +23 + cal $parfork ; Fork off `par i = ...' process + lfr 4 + zne *25 ; One end jumps to end of outer par + lal -20 ; Place break just above i + cal $parbegin ; Set up another process group for the P(i) + loc 1 + stl -24 ; i:=1 + lol n ; Assume n can be addressed this simply + stl -28 ; A nameless counter + bra *26 ; Branch to counter test +27 + cal $parfork ; Fork off one P(i) + lfr 4 + zne *28 ; One jumps away to increment i, the other calls P(i) + lol -24 + lor 0 + cal $P + asp 8 + bra *29 +28 + inl -24 ; i:=i+1 + del -28 ; counter:=counter-1 +26 + lol -28 + zgt *27 ; while counter>0 repeat loop +29 + cal $parend ; Wait for the P(i) to finish, then delete group + bra *24 ; Jump to the higher up meeting place with P0 +25 ; Note that the bra will be optimized away +24 + cal $parend ; Wait for both processes to end, then delete group +.TE diff --git a/doc/pcref.doc b/doc/pcref.doc index 0cf42031..db979f4a 100644 --- a/doc/pcref.doc +++ b/doc/pcref.doc @@ -2,7 +2,7 @@ .ds OF \\fBtest~off:~\\fR .ds ON \\fBtest~on:~~\\fR .ds AL \\fBtest~all:~\\fR -.ll 72 +.ll 72n .wh 0 hd .wh 60 fo .de hd @@ -37,7 +37,7 @@ by .de VU .sp 3 .ce 4 -Wiskundig Seminarium +Vakgroep Informatica Vrije Universiteit De Boelelaan 1081 Amsterdam diff --git a/doc/toolkit.doc b/doc/toolkit.doc index d6e7b5d5..3596ecfa 100644 --- a/doc/toolkit.doc +++ b/doc/toolkit.doc @@ -1,7 +1,6 @@ .\" $Header$ .RP -.ND -.nr LL 78m +.ND July 1984 .tr ~ .ds as * .TL diff --git a/doc/top/.distr b/doc/top/.distr new file mode 100644 index 00000000..4cdc7ab7 --- /dev/null +++ b/doc/top/.distr @@ -0,0 +1,3 @@ +Makefile +refs.top +top.n diff --git a/doc/top/Makefile b/doc/top/Makefile new file mode 100644 index 00000000..acd50d8b --- /dev/null +++ b/doc/top/Makefile @@ -0,0 +1,7 @@ +# $Header$ + +REFER=refer +TBL=tbl + +../top.doc: top.n refs.top + $(REFER) -sA+T -l4,2 -p refs.top top.n | $(TBL) > $@ diff --git a/doc/top/refs.top b/doc/top/refs.top new file mode 100644 index 00000000..c4438987 --- /dev/null +++ b/doc/top/refs.top @@ -0,0 +1,84 @@ +%T A Practical Toolkit for Making Portable Compilers +%A A.S. Tanenbaum +%A J.M. van Staveren +%A E.G. Keizer +%A J.W. Stevenson +%I Vrije Universiteit, Amsterdam +%R Rapport nr IR-74 +%D October 1981 + +%T A Practical Toolkit for Making Portable Compilers +%A A.S. Tanenbaum +%A J.M. van Staveren +%A E.G. Keizer +%A J.W. Stevenson +%J CACM +%V 26 +%N 9 +%P 654-660 +%D September 1983 + +%T A Unix Toolkit for Making Portable Compilers +%A A.S. Tanenbaum +%A J.M. van Staveren +%A E.G. Keizer +%A J.W. Stevenson +%J Proceedings USENIX conf. +%C Toronto, Canada +%V 26 +%D July 1983 +%P 255-261 + +%T Using Peephole Optimization on Intermediate Code +%A A.S. Tanenbaum +%A J.M. van Staveren +%A J.W. Stevenson +%J TOPLAS +%V 4 +%N 1 +%P 21-36 +%D January 1982 + +%T Amsterdam Compiler Kit documentation +%A A.S. Tanenbaum +%A E.G. Keizer +%A J.M. van Staveren +%A J.W. Stevenson +%I Vrije Universiteit, Amsterdam +%R Rapport nr IR-90 +%D June 1984 + +%T Language- and Machine-independant Global Optimization on +Intermediate Code +%A H.E. Bal +%A A.S. Tanenbaum +%I Vrije Universiteit, Amsterdam +%R Rapport IR-98 +%D March 1985 + +%T The Design and Implementation of the EM Global Optimizer +%A H.E. Bal +%I Vrije Universiteit, Amsterdam +%R Rapport IR-99 +%D March 1985 + + +%T The C Programming Language +%A B.W. Kernighan +%A D.M. Ritchie +%I Prentice-Hall, Inc +%C Englewood Cliffs,NJ +%D 1978 + +%T Principles of compiler design +%A A.V. Aho +%A J.D. Ullman +%I Addison-Wesley +%C Reading, Massachusetts +%D 1978 + +%T Some Topics in Parser Generation +%A C.J.H. Jacobs +%R Rapport IR-105 +%D October 1985 +%I Vrije Universiteit, Amsterdam diff --git a/doc/top/top.n b/doc/top/top.n new file mode 100644 index 00000000..7067bf82 --- /dev/null +++ b/doc/top/top.n @@ -0,0 +1,862 @@ +.ND +.tr ~ +.ds >. . +.ds [. [ +.ds .] ] +.TL +The ACK Target Optimizer +.AU +H.E. Bal +.AI +Vrije Universiteit +Wiskundig Seminarium, Amsterdam +.AB +The Target Optimizer is one of several optimizers that are part of +the Amsterdam Compiler Kit. +It operates directly on assembly code, +rather than on a higher level intermediate code, +as the Peephole Optimizer and Global Optimizer do. +Consequently, the Target Optimizer can do optimizations +that are highly machine-dependent. +.PP +Each target machine has its own Target Optimizer. +New optimizers are generated by the Target Optimizer Generator, +which uses a machine-dependent table as input. +This document contains full information on how to +write such a table for a new machine. +It also discusses the implementation of the +Target Optimizer and its generator. +.AE +.bp +.NH 1 +Introduction +.PP +.FS +This work was supported by the +Stichting Technische Wetenschappen (STW) +under grant VWI03.0001. +.FE +This document describes the target optimizer component +of the Amsterdam Compiler Kit (ACK) . +.[ +tanenbaum staveren amsterdam toolkit +.] +.[ +tanenbaum staveren cacm +.] +.[ +tanenbaum staveren toronto +.] +Optimization takes place in several parts of ACK compilers, +most notably in the Peephole Optimizer +.[ +staveren peephole toplas +.] +and +the Global Optimizer, +.[ +bal tanenbaum global optimization +.] +.[ +bal implementation global optimizer +.] +which are both language- and machine-independent, +and in the machine-specific code generators. +.[ +documentation amsterdam compiler kit +.] +The target optimizer is the finishing touch in this sequence of +optimizers. +It can be used to capture those optimizations that are hard +to express in the other parts of ACK. +These optimizations will typically be very machine-specific. +.PP +The target optimizer operates on the assembly code of some target machine. +Hence there is one target optimizer per machine. +However, just as for the ACK code generators and assemblers, +a framework has been build that allows easy generation of +target optimizers out of machine-independent parts and a +machine-dependent description table (see figure 1.). +So the major part of the code of a target optimizer is +shared among all target optimizers. +.DS +.ft 5 + + + |-------------------------| + | machine-independent | + | code | + | | + |-----------------| |-------------------------| +descrip- |target optimizer | | machine-dependent code | + tion --> |generator | ----> | + tables | +table | | | | + |-----------------| |-------------------------| + + target optimizer +.ft R + + Figure 1: Generation of a target optimizer. + +.DE +.PP +This document focusses on the description of the machine-dependent table. +In chapter 2 we give an informal introduction to the optimization +algorithm and to the definition of the table format. +Chapters 3 and 4 discuss the implementation of the target optimizer +and the target optimizer generator. +Appendix A gives full information for writing a description table. +.bp +.NH 1 +Global structure of the target optimizer +.PP +The target optimizer is based on the well understood model +of a \fIpeephole optimizer\fR. +.[ +aho ullman compiler +.] +It contains a machine-dependent table +of (pattern,replacement) pairs. +Each pattern describes +a sequence of one or more assembler instructions +that can be replaced by zero or more equivalent, yet cheaper, +instructions (the 'replacement'). +The optimizer maintains a \fIwindow\fR that moves over the input. +At any moment, the window contains some contiguous part of the input. +If the instructions in the current window match some pattern +in the table, +they are replaced by the corresponding replacement; +else, the window moves one instruction to the right. +.PP +In the remainder of this section we will give an informal +description of the machine-dependent table. +A more precise definition is given in appendix A. +We will first discuss the restrictions put on the +format of the assembly code. +.NH 2 +Assumptions about the assembly code format +.PP +We assume that a line of assembly code begins with an +instruction \fImnemonic\fR (opcode), +followed by zero or more \fIoperands\fR. +The mnemonic and the first operand must be separated by a special +character (e.g. a space or a tab). +Likewise, the operands must be separated by a special +character (e.g. a comma). +These separators need not be the same for all machines. +.NH 2 +Informal description of the machine-dependent tables +.PP +The major part of the table consists of (pattern,replacement) pairs +called \fIentries\fR. +.PP +A pattern is a list of instruction descriptions. +Each instruction description describes the instruction mnemonic and +the operands. +.PP +A mnemonic is described either by a string constant or by the +keyword ANY. +As all entities dealt with by the target optimizer are strings, +string constants do not contain quotes. +A string constant matches only itself. +ANY matches every instruction mnemonic. +.nf + +Examples of mnemonic descriptions: +.ft 5 + + add + sub.l + mulw3 + ANY +.ft R +.fi +.PP +An operand can also be described by a string constant. +.nf + +Examples: +.ft 5 + + (sp)+ + r5 + -4(r6) + +.ft R +.fi +Alternatively, it can be described by means of a \fIvariable name\fR. +Variables have values which are strings. +They have to be declared in the table before the patterns. +Each such declaration defines the name of a variable and +a \fIrestriction\fR to which its value is subjected. +.nf +Example of variable declarations: +.ft 5 + + CONST { VAL[0] == '$' }; + REG { VAL[0] == 'r' && VAL[1] >= '0' && VAL[1] <= '3' && + VAL[2] == '\\0' }; + X { TRUE }; + +.ft R +.fi +The keyword VAL denotes the value of the variable, which is +a null-terminated string. +An operand description given via a variable name matches an +actual operand if the actual operand obeys the associated restriction. +.nf +.ft 5 + + CONST matches $1, $-5, $foo etc. + REG matches r0, r1, r2 and r3 + X matches anything +.ft R + +.fi +The restriction (between curly braces) may be any legal "C" +.[ +kernighan ritchie c programming +.] +expression. +It may also contain calls to user-defined procedures. +These procedures must be added to the table after the patterns. +.nf + +Example: +.ft 5 + + FERMAT_NUMBER { VAL[0] == '$' && is_fermat_number(&VAL[1]) }; + +.ft R +.fi +An operand can also be described by a mixture of a string constant +and a variable name. +The most general form allowed is: +.nf + + string_constant1 variable_name string_constant2 + +Example: +.ft 5 + + (REG)+ matches (r0)+, (r1)+, (r2)+ and (r3)+ + +.ft R +.fi +Any of the three components may be omitted, +so the first two forms are just special cases of the general form. +The name of a variable can not be used as a string constant. +In the above context, it is impossible to define an operand that +matches the string "REG". +This limitation is of little consequence, +as the table writer is free to choose the names of variables. +This approach, however, avoids the need for awkward escape sequences. +.PP +A pattern consists of one or more instruction descriptions +(separated by a colon) +followed by an optional constraint. +A pattern "P1 : P2 : .. : Pn C" matches the sequence of +instructions "I1 I2 .. In" if: +.IP (i) 7 +for each i, 1 <= i <= n, Pi matches Ii, as described above; +.IP (ii) +multiple occurrences of the same variable name or of +the keyword ANY stand for the same values throughout the pattern; +.IP (iii) +the optional constraint C is satisfied, i.e. it evaluates to TRUE. +.LP +.nf +The pattern: +.ft 5 + + dec REG : move.b CONST,(REG) + +.ft R +matches: +.ft 5 + + dec r0 : move.b $4,(r0) + +.ft R +but not: +.ft 5 + + dec r0 : move.b $4,(r1) + +.ft R +(as the variable REG matches two different strings). +.fi +If a pattern containing different registers must be described, +extra names for a register should be declared, all sharing +the same restriction. +.nf +Example: +.ft 5 + + REG1,REG2 { VAL[0] == 'r' && ..... }; + + addl3 REG1,REG1,REG2 : subl2 REG2,REG1 +.ft R +.fi +.PP +The optional constraint is an auxiliary "C" expression (just like +the parameter restrictions). +The expression may refer to the variables and to ANY. +.nf +Example: +.ft 5 + + move REG1,REG2 { REG1[1] == REG2[1] + 1 } + +.ft R +matches +.ft 5 + + move r1,r0 + move r2,r1 + move r3,r2 +.ft R +.fi +.PP +The replacement part of a (pattern,replacement) table entry +has the same structure as a pattern, except that: +.IP (i) +it may not contain an additional constraint; +.IP (ii) +it may be empty. +.LP +A replacement may also refer to the values of variables and ANY. +.NH 2 +Examples +.PP +This section contains some realistic examples for +optimization on PDP-11 and Vax assembly code. +.NH 3 +Vax examples +.PP +Suppose the table contains the following declarations: +.nf + +.ft 5 + X, LOG { TRUE }; + LAB { VAL[0] == 'L' }; /* e.g. L0017 */ + A { no_side_effects(VAL) }; + NUM { is_number(VAL) }; +.ft R + +.fi +The procedure "no_side_effects" checks if its argument +contains any side effects, i.e. auto increment or auto decrement. +The procedure "is_number" checks if its argument contains only digits. +These procedures must be supplied by the table-writer and must be +included in the table. +.PP +.nf +.ft 5 +\fIentry:\fP addl3 X,A,A -> addl2 X,A; +.ft R + +.fi +This entry changes a 3-operand instruction into a cheaper 2-operand +instruction. +An optimization like: +.nf +.ft 5 + + addl3 r0,(r2)+,(r2)+ -> addl2 r0,(r2)+ + +.ft R +.fi +is illegal, as r2 should be incremented twice. +Hence the second argument is required to +be side-effect free. +.PP +.nf +.ft 5 +\fIentry:\fP addw2 $-NUM,X -> subw2 $NUM,X; +.ft R + +.fi +An instruction like "subw2 $5,r0" is cheaper +than "addw2 $-5,r0", +because constants in the range 0 to 63 are represented +very efficiently on the Vax. +.PP +.nf +.ft 5 +\fIentry:\fP bitw $NUM,A : jneq LAB + { is_poweroftwo(NUM,LOG) } -> jbs $LOG,A,LAB; + +.ft R +.fi +A "bitw x,y" sets the condition codes to the bitwise "and" of +x and y. +A "jbs n,x,l" branches to l if bit n of x is set. +So, for example, the following transformation is possible: +.nf +.ft 5 + + bitw $32,r0 : jneq L0017 -> jbs $5,r0,L0017 + +.ft R +.fi +The user-defined procedure "is_poweroftwo" checks if its first argument is +a power of 2 and, if so, sets its second argument to the logarithm +of the first argument. (Both arguments are strings). +Note that the variable LOG is not used in the pattern itself. +It is assigned a (string) value by "is_poweroftwo" and is used +in the replacement. +.NH 3 +PDP-11 examples +.PP +Suppose we have the following declarations: +.nf + +.ft 5 + X { TRUE }; + A { no_side_effects(VAL) }; + L1, L2 { VAL[0] == 'I' }; + REG { VAL[0] == 'r' && VAL[1] >= '0' && VAL[1] <= '5' && + VAL[2] == '\\0' }; + +.ft P +.fi +The implementation of "no_side_effects" may of course +differ for the PDP-11 and the Vax. +.PP +.nf +.ft 5 +\fIentry:\fP mov REG,A : ANY A,X -> mov REG,A : ANY REG,X ; +.ft R + +.fi +This entry implements register subsumption. +If A and REG hold the same value (which is true after "mov REG,A") +and A is used as source (first) operand, it is cheaper to use REG instead. +.PP +.nf +.ft 5 +\fIentry:\fP jeq L1 : jbr L2 : labdef L1 -> jne L2 : labdef L1; +.ft R + +.fi +The "jeq L1" is a "skip over an unconditional jump". "labdef L1" +denotes the definition (i.e. defining occurrence) of label L1. +As the target optimizer has to know how such a definition +looks like, this must be expressed in the table (see Appendix A). +.PP +.nf +.ft 5 +\fIentry:\fP add $01,X { carry_dead(REST) } -> inc X; +.ft R + +.fi +On the PDP-11, an add-one is not equivalent to an increment. +The latter does not set the carry-bit of the condition codes, +while the former does. +So a look-ahead is needed to see if the rest of the input uses +the carry-bit before changing the condition codes. +A look-ahead of one instruction is provided by +the target optimizer. +This will normally be sufficient for compiler-generated code. +The keyword REST contains the mnemonic of the first instruction of +the rest of the input. +If this instruction uses the carry-bit (e.g. an adc, subc, bhis) +the transformation is not allowed. +.bp +.NH 1 +Implementation of the target optimizer +.PP +The target optimizer reads one input file of assembler instructions, +processes it, and writes the optimized code +to the output file. +So it performs one pass over the input. +.NH 2 +The window mechanism +.PP +The optimizer uses a \fIwindow\fR that moves over the input. +It repeatedly tries to match the instructions in the window +with the patterns in the table. +If no match is possible, the window moves +one instruction forwards (to the right). +After a successful match the matched instructions are +removed from the window and are replaced by the +replacement part of the table entry. +Furthermore, the window is moved a few instructions +backwards, +as it is possible that instructions that were rejected earlier now do match. +For example, consider the following patterns: +.DS +.ft 5 +cmp $0, X -> tst X ; +mov REG,X : tst X -> move REG.X ; /* redundant test */ +.ft R +.DE +If the input is: +.DS +.ft 5 +mov r0,foo : cmp $0,foo +.ft R +.DE +then the first instruction is initially rejected. +However, after the transformation +.DS +.ft 5 +cmp $0,foo -> tst foo +.ft R +.DE +the following optimization is possible: +.DS +.ft 5 +mov r0,foo : tst foo -> mov r0,foo +.ft R +.DE +.PP +The window is implemented as a \fIqueue\fR. +Matching takes place at the head of the queue. +New instructions are added at the tail. +If the window is moved forwards, the instruction at the head +is not yet written to the output, +as it may be needed later on. +Instead it is added to a second queue, +the \fIbackup queue\fR. +After a successful match, the entire backup queue is +inserted at the front of the window queue, +which effectively implements the shift backwards. +.PP +Both queues have the length of the longest pattern in the table. +If, as a result of a forward window move, +the backup queue gets full, +the instruction at its head is outputted and removed. +Instructions are read from the input whenever the +window queue contains fewer elements than the length +of the longest pattern. +.NH 2 +Pattern matching +.PP +Pattern matching is done in three steps: +.IP (i) 7 +find patterns in the table whose instruction mnemonics +match the mnemonics of the instructions in the +current window; +.IP (ii) +check if the operands of the pattern match the operands of the +instructions in the current window; +.IP (iii) +check if the optional constraint is satisfied. +.LP +For step (i) hashing is used. +The mnemonic of the first instruction of the window +is used to determine a list of possible patterns. +Patterns starting with ANY are always tried. +.PP +Matching of operand descriptions against actual operands +takes place as follows. +The general form of an operand description is: +.DS +string_constant1 variable_name string_constant2 +.DE +The actual operand should begin with string_constant1 and end +on string_constant2. +If so, these strings are stripped from it and the remaining string is +matched against the variable. +Matching a string against a variable is +defined as follows: +.IP 1. +initially (before the entire pattern match) +all variables are uninstantiated; +.IP 2. +matching a string against an uninstantiated variable +succeeds if the restriction associated with the variable is +satisfied. +As a side effect, it causes the variable to be instantiated to +the string; +.IP 3. +matching a string against an instantiated variable succeeds +only if the variable was instantiated to the same string. +.LP +Matching an actual mnemonic against the keyword ANY is defined likewise. +.PP +The matching scheme implements the requirement that multiple occurrences +of the same variable name or of the keyword ANY should +stand for the same values throughout the entire pattern +(see section 2.). +.PP +Both the parameter restriction of 2. and the constraint of step (iii) +are checked by executing the "C" expression. +.NH 2 +Data structures +.PP +The most important data structure is the representation +of the input instructions. +For every instruction we use two representations: +.IP (i) +the textual representation, +i.e. the exact code as it appeared in the input; +.IP (ii) +a structural representation, +containing the opcode and the operands. +.LP +The opcode of an instruction is determined as soon as it is read. +If the line contains a label definition, the opcode is set +to "labdef", so a label definition is treated like a normal +instruction. +.PP +The operands of an instruction are not determined until +they are needed, i.e. until step (i) of the pattern matching +process has succeeded. +For every instruction we keep track of a \fIstate\fR. +After the opcode has successfully been determined, +the state is OPC_ONLY. +Once the operands have been recognized, the state is set to DONE. +If the opcode or operands can not be determined, +or if the instruction cannot be optimized for any other +reason (see Appendix A), the state is set to JUNK +and any attempt to match it will fail. +.PP +For each table entry we record the following information: +.IP (i) 7 +the length of the pattern (i.e. the number of instruction descriptions) +.IP (ii) +a description of the instructions of the pattern +.IP (iii) +the length of the replacement +.IP (iv) +a description of the instructions of the replacement. +.LP +The description of an instruction consists of: +.IP (i) +the opcode +.IP (ii) +for each operand, a description of the operand. +.LP +The description of an operand of the form: +.DS +string_constant1 variable_name string_constant2 +.DE +contains: +.IP (i) +both string constants +.IP (ii) +the number of the variable. +.LP +Each declared variable is assigned a unique number. +For every variable we maintain: +.IP (i) +its state (instantiated or not instantiated) +.IP (ii) +its current value (a string). +.LP +The restrictions on variables and the constraints are stored +in a switch-statement, +indexed by variable number and entry number respectively. +.bp +.NH 1 +Implementation of the target optimizer generator +.PP +The target optimizer generator (\fItopgen\fR) +reads a target machine description table and produces +two files: +.IP gen.h: 9 +contains macro definitions for +machine parameters that were changed +in the parameter section of the table (see appendix A) +and for some attributes derived from the table +(longest pattern, number of patterns, number +of variables). +.IP gen.c: +contains the entry description tables, +code for checking the parameter restrictions and constraints +(switch statements) +and the user-defined procedures. +.LP +These two files are compiled together with some machine-independent +files to produce a target optimizer. +.PP +Topgen is implemented using +the LL(1) parser generator system LLgen , +.[ +jacobs topics parser generation +.] +a powerful tool of the Amsterdam Compiler Kit. +This system provides a flexible way of describing the syntax of the tables. +The syntactical description of the table format included +in Appendix A was derived from the LLgen syntax rules. +.PP +The parser uses a simple, hand-written, lexical analyzer (scanner). +The scanner returns a single character in most cases. +The recognition of identifiers is left to the parser, as +this eases the analysis of operand descriptions. +Comments are removed from the input by the scanner, +but white space is passed to the parser, +as it is meaningful in some contexts (it separates the +opcode description from the description of the first operand). +.PP +Topgen maintains two symbol tables, one for variable names and one +for tunable parameters. +The symbol tables are organized as binary trees. +.bp +.SH +Appendix A +.PP +In this appendix we present a complete definition of the target +optimizer description table format. +This appendix is intended for table-writers. +We use syntax rules for the description of the table format. +The following notation is used: +.TS +center; +l l. +{ a } zero or more of a +[ a ] zero or one of a +a b a followed by b +a | b a or b +.TE +Terminals are given in quotes, as in ';'. +.PP +The table may contain white space and comment at all reasonable places. +Comments are as in "C", so they begin with /* and end on */. +Identifiers are sequences of letters, digits and the underscore ('_'), +beginning with a letter. +.PP +.DS +.ft 5 +table -> {parameter_line} '%%;' {variable_declaration} '%%;' + {entry} '%%;' user_routines. +.ft R +.DE +A table consists of four sections, containing machine-dependent +constants, variable declarations, pattern rules and +user-supplied subroutines. +.PP +.DS +.ft 5 +parameter_line -> identifier value ';' . +.ft R +.DE +A parameter line defines some attributes of the target machines +assembly code. +For unspecified parameters default values apply. +The names of the parameters and the corresponding defaults +are shown in table 1. +.TS +center; +l l. +OPC_TERMINATOR ' ' +OP_SEPARATOR ',' +LABEL_STARTER 'I' +LABEL_TERMINATOR ':' +MAXOP 2 +MAXOPLEN 25 +MAX_OPC_LEN 10 +MAXVARLEN 25 +MAXLINELEN 100 +.TE +.ce 1 +table 1: parameter names and defaults +.DE +The OPC_TERMINATOR is the character that separates the instruction +mnemonic from the first operand (if any). +The OP_SEPARATOR separates adjacent operands. +A LABEL_STARTER is the first character of an instruction label. +(Instruction labels are assumed to start with the same character). +The LABEL_TERMINATOR is the last character of a label definition. +It is assumed that this character is not used in an applied +occurrence of the label identifier. +For example, the defining occurrence may be "I0017:" +and the applied occurrence may be "I0017" +as in "jmp I0017". +MAXOP defines the maximum number of operands an instruction can have. +MAXOPLEN is the maximum length (in characters) of an operand. +MAX_OPC_LEN is the maximum length of an instruction opcode. +MAXVARLEN is the maximum length of a declared string variable. +As variables may be set by user routines (see "bitw" example for +the Vax) the table-writer must have access to this length and +must be able to change it. +MAXLINELEN denotes the maximum length of a line of assembly code. +.PP +If a line of assembly code violates any of the assumptions or +exceeds some limit, +the line is not optimized. +Optimization does, however, proceed with the rest of the input. +.PP +.DS +.ft 5 +variable_declaration -> identifier {',' identifier} restriction ';' . + +restriction -> '{' anything '}' . +.ft R +.DE +A variable declaration declares one or more string variables +that may be used in the patterns and in the replacements. +If a variable is used as part of an operand description in +a pattern, the entire pattern can only match if the +restriction evaluates to TRUE. +If the pattern does match, the variable is assigned the matching +part of the actual operand. +Variables that are not used in a pattern are initialized to +null-strings and may be assigned a value in the constraint-part of +the pattern. +.PP +The restriction must be a legal "C" expression. +It may not contain a closing bracket ('}'). +Inside the expression, the name VAL stands for the part of the actual +(matching) operand. +The expression may contain calls to procedures that are defined in the +user-routines section. +.DS +.ft 5 +entry -> pattern '->' replacement ';' . + +pattern -> instruction_descr + { ':' instruction_descr } + constraint . + +replacement -> [ instruction_descr { ':' instruction_descr } ] . + +instruction_descr -> opcode + white + [ operand_descr { ',' operand_descr } ] . + +constraint -> '{' anything '}' . + +operand_descr -> [ string_constant ] + [ variable_name ] + [ string_constant ] . + +variable_name -> identifier . + +opcode -> anything . +.ft R +.DE +The symbol 'white' stands for white space (space or tab). +An opcode can be any string not containing the special +symbols ';', '{', '}', ':', ',', '->' or white space. +To be recognized, it must begin with a letter. +The opcode should either be a mnemonic of a target machine +instruction or it should be one of the keywords ANY and labdef. +ANY matches any actual opcode. labdef matches only label definitions. +.PP +If an operand description contains an identifier (as defined earlier), +it is checked if the identifier is the name of a declared variable. +This effects the semantics of the matching rules for the operand, +as described in section 2. +An operand may contain at most one such variable name. +.PP +The constraint must be a legal "C" expression, just as the operand restriction. +It may call user-defined procedures and use or change the value of +declared variables. +It may also use the string variable REST, +which contains the mnemonic of the first instruction of the +rest of the input. (REST is a null-string if this mnemonic can +not be determined). +.DS +.ft 5 +user_routines -> anything . +.ft R +.DE +The remainder of the table consists of user-defined subroutines. +.bp +.[ +$LIST$ +.] diff --git a/doc/v7bugs.doc b/doc/v7bugs.doc index 5a5f0a6a..b6618830 100644 --- a/doc/v7bugs.doc +++ b/doc/v7bugs.doc @@ -12,19 +12,19 @@ .br .ne 20 .sp 2 -.in 5 -.ti -5 +.in 5n +.ti -5n ERROR \\n+e: .. .de PS .sp .nf -.in +5 +.in +5n .. .de PE .sp .fi -.in -5 +.in -5n .. .sp 3 .ce diff --git a/doc/val.doc b/doc/val.doc index 2be0c093..958de128 100644 --- a/doc/val.doc +++ b/doc/val.doc @@ -1,5 +1,5 @@ .\" $Header$ -.ll 72 +.ll 72n .wh 0 hd .wh 60 fo .de hd diff --git a/doc/z80.doc b/doc/z80.doc index e8a15a08..1976ca26 100644 --- a/doc/z80.doc +++ b/doc/z80.doc @@ -1,16 +1,21 @@ +. \" $Header$ +.ND April 1985 +.TL THE Z80 BACK END TABLE - +.AU +Frans van Haarlem +.NH 1 INTRODUCTION - +.PP This table was written to make it run, not to make it clever! The effect is, that the table written for the intel 8080, which was made very clever runs faster and requiers less space!! So, for anyone to run programs on a z80 machine: You could try to make the table as clever as the one for the i80, or you could run the i80 table, for that can run on every z80 too. - +.NH IMPLEMENTATION - +.PP It will not be possible to run the entire Amsterdam Compiler Kit on a Z80-based computer system. One has to write a program on another @@ -24,31 +29,37 @@ for example by downloading or by storing it in ROM (Read Only Memory). Depending on the characteristics of the particular z80 based system, some adaptions have to be made: -1) In 'head_em': the base address, which is the address where the first - z80 instruction will be stored, and the initial value of the - stackpointer are set to 0x1000 and 0x7ffe respectivally. - The latter because it could run on a 32K machine as well. - Other systems require other values. -2) In 'head_em': before calling "_m_a_i_n", the environment - pointer, argument vector and argument count will have to be pushed - onto the stack. - Since this back-end is tested on a system without any knowledge - of these things, dummies are pushed now. -3) In 'tail_em': proper routines "putchar" and "getchar" should - be provided. - They should write resp. read a character on/from the monitor. - Maybe some conversions will have to be made. - The ones for the Nascom and Hermac z80 micro's are to be found - in the EM-library. -4) In 'head_em': an application program returns control to the monitor by - jumping to address 0x20. - If this is not the right way on your system, change it. - For an CPM-machine for example this should be 0x5, to provide a warm boot. -5) In 'tail_em': the current version of the z80 back-end has very limited I/O - capabilities, because it was tested on a system that - had no knowlegde of files. - So the implementation of the EM-instruction 'mon' is very simple; - it can only do the following things: +.IP 1) +In \fIhead_em\fP: the base address, which is the address where the first +z80 instruction will be stored, and the initial value of the +stackpointer are set to 0x1000 and 0x7ffe respectivally. +The latter because it could run on a 32K machine as well. +Other systems require other values. +.IP 2) +In \fIhead_em\fP: before calling "_m_a_i_n", the environment +pointer, argument vector and argument count will have to be pushed +onto the stack. +Since this back-end is tested on a system without any knowledge +of these things, dummies are pushed now. +.IP 3) +In \fItail_em\fP: proper routines "putchar" and "getchar" should +be provided. +They should write resp. read a character on/from the monitor. +Maybe some conversions will have to be made. +The ones for the Nascom and Hermac z80 micro's are to be found +in the EM-library. +.IP 4) +In \fIhead_em\fP: an application program returns control to the monitor by +jumping to address 0x20. +If this is not the right way on your system, change it. +For an CPM-machine for example this should be 0x5, to provide a warm boot. +.IP 5) +In \fItail_em\fP: the current version of the z80 back-end has very limited I/O +capabilities, because it was tested on a system that +had no knowlegde of files. +So the implementation of the EM-instruction \fImon\fP is very simple; +it can only do the following things: +.DS Monitor call 1: Exit Monitor call 3: @@ -64,5 +75,6 @@ adaptions have to be made: close file, returns error code = 0. Monitor call 54: io-control, returns error code = 0. +.DE If the system should do file-handling the routine ".mon" should be extended thoroughly. diff --git a/emtest/.distr b/emtest/.distr new file mode 100644 index 00000000..3d60866b --- /dev/null +++ b/emtest/.distr @@ -0,0 +1,6 @@ +Makefile +READ_ME +ok +select.c +test.h +tests diff --git a/emtest/Makefile b/emtest/Makefile index 77ce23fe..b20ae215 100644 --- a/emtest/Makefile +++ b/emtest/Makefile @@ -16,4 +16,4 @@ last: tests test.h select echo 0 >last select: select.c - cc -O -n -o select select.c + cc -O -i -o select select.c diff --git a/emtest/select.c b/emtest/select.c index 9be038ad..9b2624ab 100644 --- a/emtest/select.c +++ b/emtest/select.c @@ -1,17 +1,7 @@ +/* $Header$ */ /* - * (c) copyright 1983 by the Vrije Universiteit, Amsterdam, The Netherlands. - * - * This product is part of the Amsterdam Compiler Kit. - * - * Permission to use, sell, duplicate or disclose this software must be - * obtained in writing. Requests for such permissions may be sent to - * - * Dr. Andrew S. Tanenbaum - * Wiskundig Seminarium - * Vrije Universiteit - * Postbox 7161 - * 1007 MC Amsterdam - * The Netherlands + * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". * */ @@ -46,6 +36,8 @@ char name1[] = "/usr/tmp/f1XXXXXX"; char name2[] = "/usr/tmp/f2XXXXXX"; char name3[] = "/usr/tmp/f3XXXXXX"; +char *to3dig(); + stop() { unlink(name1); unlink(name2); @@ -130,9 +122,10 @@ select() { } fprintf(file2, "; %s\n", line); if (fflag) { - fprintf(file1, ".%03d\n", i); - fprintf(file1, " con \"tst%03d\"\n", i); - fprintf(file2, " fil .%03d\n", i); + char *s = to3dig(i); + fprintf(file1, ".%s\n", s); + fprintf(file1, " con \"tst%s\"\n", s); + fprintf(file2, " fil .%s\n", s); } f = file1; while (getline()) { @@ -247,3 +240,17 @@ usage() { nerrors++; stop(); } + +char * +to3dig(i) + register int i; +{ + static char buf[4]; + register char *s = buf; + + *s++ = (i % 1000) / 100 + '0'; + *s++ = (i % 100) / 10 + '0'; + *s++ = (i % 10) + '0'; + *s = '\0'; + return buf; +} diff --git a/emtest/test.h b/emtest/test.h index e69de29b..ff93f109 100644 --- a/emtest/test.h +++ b/emtest/test.h @@ -0,0 +1,8 @@ +/* $Header$ */ +/* + * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + */ +#undef W2S +#define FS 4 +#define F2S 8 diff --git a/emtest/tests b/emtest/tests index 0479430f..692acab7 100644 --- a/emtest/tests +++ b/emtest/tests @@ -149,8 +149,8 @@ TEST 005: test loe, ste HOL 8 MAIN 0 loc 95 - ste 4 - loe 4 + ste WS + loe WS loc 95 bne *1 OK @@ -162,7 +162,7 @@ OK OK loc 95 ste 0 - loe 4 + loe WS loe 0 bne *1 OK @@ -408,8 +408,8 @@ TEST 020: test ldf MAIN 20 loc 123 loc 77 - sdl -4 - lal -9 + sdl -8 + lal -13 ldf 5 loc 77 bne *1 @@ -442,7 +442,7 @@ MAIN 20 bne *1 OK TEST 023: test sde -HOL 20 +HOL 24 MAIN 0 loc 40 loc 41 @@ -455,7 +455,7 @@ MAIN 0 bne *1 OK TEST 024: test sdf -HOL 20 +HOL 24 MAIN 0 loc 51 loc 50 @@ -486,7 +486,7 @@ MAIN 20 bne *1 OK TEST 026: test sti 1 and lol -MAIN 0 +MAIN 20 loc 257 stl -12+WS loc 514 @@ -546,9 +546,9 @@ MAIN 20 loc 102 #endif loc 103 - lal -14 + lal -16 sti 4 - lol -14 + lol -16 loc 103 bne *1 OK @@ -597,10 +597,10 @@ MAIN 20 loc 102 #endif loc 103 - lal -14 + lal -16 loc 4 sts WS - lol -14 + lol -16 loc 103 bne *1 OK @@ -1707,7 +1707,7 @@ MAIN 12 bne *1 OK TEST 077: test cal -HOL 12 +HOL 8 MAIN 0 cal $p077 loe 4 @@ -1721,7 +1721,7 @@ PROC ret 0 end TEST 078: test cai -HOL 12 +HOL 8 MAIN 0 lpi $p078 cai @@ -1833,7 +1833,7 @@ OK bne *1 OK TEST 083: test blm -HOL 40 +HOL 28 MAIN 32 loc 61 ste 12 @@ -1869,8 +1869,18 @@ OK bne *1 OK TEST 084: test bls WS -HOL 40 +HOL 28 MAIN 32 + loc 20 + stl -8 + lal -8 + lae 20 + loc WS + bls WS + loe 20 + loc 20 + bne *1 +OK loc 55 stl -8+WS loc 56 @@ -3283,7 +3293,7 @@ OK OK #endif TEST 120: test cal -HOL 12 +HOL 8 MAIN 0 loc 0 ste 4 @@ -3573,19 +3583,6 @@ OK cmp zne *1 OK -; same as above but with ASS instead of ASP - lxl 0 - cal $retp2 - loc PS - ass WS - lfr 2*PS - lpi $retp2 - cmp - zne *1 - lxl 0 - cmp - zne *1 -OK PROC pro $retw0,0 ret 0 diff --git a/etc/.distr b/etc/.distr new file mode 100644 index 00000000..d1d6e82f --- /dev/null +++ b/etc/.distr @@ -0,0 +1,7 @@ +Makefile +em_table +new_table +pc_errors +pc_rt_errors +pop_push +traps diff --git a/etc/em_table b/etc/em_table index 8dd963bb..38ca26d1 100644 --- a/etc/em_table +++ b/etc/em_table @@ -26,18 +26,18 @@ ucon 252 fcon 253 cend 255 -bss 0 -con 1 -end 2 -exa 3 -exc 4 -exp 5 -hol 6 -ina 7 -inp 8 -mes 9 -pro 10 -rom 11 +bss 0 nvt +con 1 a+ +end 2 n? +exa 3 e +exc 4 nn +exp 5 p +hol 6 nvt +ina 7 e +inp 8 p +mes 9 na* +pro 10 pn? +rom 11 a+ aar w- -p-a-p+p adf w- -a-a+a diff --git a/etc/new_table b/etc/new_table index ef83ccb0..accc7d14 100755 --- a/etc/new_table +++ b/etc/new_table @@ -10,7 +10,7 @@ ed - em_table <<'A' > X 1,/^$/g/ /s// /gp A -ed - em_table <<'A' | awk '{print $1,$2+'$p'}' > Y +ed - em_table <<'A' | awk '{$2=$2+'$p'; print}' > Y 1,/^$/d 1,/^$/g/ /s// /gp A @@ -31,6 +31,7 @@ g/^/s//#define sp_/p A ed - Y <<'A' > $h/em_pseu.h +g/ \(.*\) .*/s// \1/ g/\(.*\) \(.*\)/s//#define ps_\1 \2/p A @@ -58,7 +59,7 @@ echo '};' ( echo '#include char em_flag[] = {' -ed - Z <<'A' | tr a-z A-Z +ed - Z <<'A' | tr abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ g/^... /s/// g/ .*/s/// g/\(.\)\(.\)/s//PAR_\1 | FLO_\2/ diff --git a/first/.distr b/first/.distr new file mode 100644 index 00000000..9325d2af --- /dev/null +++ b/first/.distr @@ -0,0 +1,6 @@ +hash +ckpath +first +did_first +myecho.c +fixlexlib diff --git a/first/ckpath b/first/ckpath index 2829749c..2809601d 100644 --- a/first/ckpath +++ b/first/ckpath @@ -12,6 +12,26 @@ x) *) echo "Sorry, there is something wrong with your PATH ($PATH)" ;; esac +echo "echo t_$$" > X_Y_Z_ +chmod +x X_Y_Z_ +case x`X_Y_Z_` +in +xt_$$) + ;; +x) + (cd ../bin ; echo Sorry, . is not in your shell PATH" ($PATH)") + STAT=2 ;; +*) + echo "Sorry, there is something wrong with your PATH ($PATH)" ;; +esac +rm -f X_Y_Z_ +case $STAT +in +2) + ;; +*) + hash -r ;; +esac echo "echo l_$$" >x_tpath chmod +x x_tpath case x`(x_tpath) 2>/dev/null` diff --git a/first/em_path.h.src b/first/em_path.h.src index d1a3bab6..d3497568 100644 --- a/first/em_path.h.src +++ b/first/em_path.h.src @@ -1,3 +1,8 @@ +/* $Header$ */ +/* + * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + */ /* Intended as a common directory for ALL temporary files */ #define TMP_DIR "/usr/tmp" diff --git a/first/first b/first/first index 68b13e53..1dd27acb 100755 --- a/first/first +++ b/first/first @@ -34,18 +34,36 @@ else exit 7 fi fi +: translate myecho +if cc -o myecho myecho.c > /dev/null 2>&1 +then + : +else + echo "Sorry, cc does not seem to work" + exit 8 +fi +: find cc option for lex library +if fixlexlib +then + : +else + exit 9 +fi : remove non-system as and ld from descr files if (ack_sys) >/dev/null 2>&1 then : echo Your system is: `ack_sys`. else - echo -n "Give me the type of your system, the current choice is: -pdp_v7 PDP11 with sep I/D and version 7 + myecho -n "Give me the type of your system, the current choice is: +pdp_v7 PDP11 with sep I/D and version 7 (or BSD 2.8, 2.9) vax_bsd4_1a VAX11 with BSD4.1a -vax_bsd4_1c VAX11 with BSD4.1c vax_bsd4_2 VAX11 with BSD4.2 +vax_sysV_2 VAX11 with System V.2 pc_ix IBM PC with PC/IX m68_unisoft Motorola 68000 with Unisoft UNIX +sun3 Sun 3 M68020 workstation +sun2 Sun 2 M68000 workstation +m68_sysV_0 Motorola 68000 with Uniplus System V.0 Unix ANY Neither of the above system type: " @@ -54,15 +72,15 @@ system type: " echo echo "$SYSNAME" >../bin/ack_sys chmod +x ../bin/ack_sys case `ack_sys` in - pdp_v7|vax_bsd4_1[ac]|vax_bsd4_2|pc_ix|m68_unisoft) ;; + pdp_v7|vax_bsd4_1a|vax_bsd4_2|vax_sysV_2|pc_ix|m68_unisoft|sun3|sun2|m68_sysV_0) ;; *) echo None of the software especially intended for the named systems will work ;; esac else echo Sorry, got EOF when reading system name. - exit 8 + exit 10 fi fi -echo -n "Your system is `ack_sys`, are you satisfied with that? (y/n) " +myecho -n "Your system is `ack_sys`, are you satisfied with that? (y/n) " if read YESNO then case $YESNO in @@ -72,26 +90,38 @@ then exec sh $0 ;; *) echo "I do not understand your answer ($YESNO). Bye" - exit 9 + exit 11 ;; esac else echo Sorry, got EOF when reading your answer. - exit 9 + exit 12 fi : "Take action according to the system used" : 'Prevent the use of the system assembler on for certain systems' +: 'prevent the use of ranlib on pdp 11s' case `ack_sys` in vax_bsd*) RMD=pdp ;; -pdp_*) RMD="vax2 vax4" ;; -*) RMD="pdp vax2 vax4" ;; +vax_sys*) RMD=pdp ;; +pdp_*) RMD="vax4" + echo 'echo "no ranlib on this system"; exit 93' > ../bin/ranlib + chmod +x ../bin/ranlib + ranlib > /dev/null 2>&1 + case X$? in + X93) ;; + *) echo "Sorry, there still is an error in your PATH." + echo "It finds the system ranlib before ours, which is in the ACK bin directory." + exit 13 + esac + ;; +*) RMD="pdp vax4" ;; esac for i in $RMD do ( cd ../lib/$i if grep '^name as$' descr >/dev/null 2>&1 - then -cp descr descr.orig + then + cp descr descr.orig ed - descr <<'ABC' /^name as$/;/^end$/d /^name ld$/;/^end$/d @@ -101,17 +131,80 @@ ABC fi ) done -: 'Set the default machine in ../h/local.h' case `ack_sys` in -pdp_v7) ACM=pdp ;; -vax_bsd4_1[ac]) ACM=vax2 ;; -vax_bsd4_2) ACM=vax2 ;; -pc_ix) ACM=ix ;; +vax_bsd4_2) ( cd ../lib/vax4 + cp descr descr.orig + ed - descr <<'ABC' +/CPP_F/s/$/ -D__BSD4_2/ +w +q +ABC + ) + ;; +vax_sysV_2) ( cd ../lib/vax4 + cp descr descr.orig + ed - descr <<'ABC' +/CPP_F/s/$/ -D__USG/ +w +q +ABC + ) + ;; +esac +case `ack_sys` in +m68_sysV_0) + ( cd ../lib/int24 + cp descr descr.orig + ed - descr <<'ABC' +/CPP_F/s/$/ -D__USG/ +w +q +ABC + ) + ( cd ../lib/int44 + cp descr descr.orig + ed - descr <<'ABC' +/CPP_F/s/$/ -D__USG/ +w +q +ABC + ) + ;; +sun2|sun3) + ( cd ../lib/int24 + cp descr descr.orig + ed - descr <<'ABC' +/CPP_F/s/$/ -D__BSD4_2/ +w +q +ABC + ) + ( cd ../lib/int44 + cp descr descr.orig + ed - descr <<'ABC' +/CPP_F/s/$/ -D__BSD4_2/ +w +q +ABC + ) + ;; +esac +: 'Set the default machine in ../h/local.h' +BM=1 +case `ack_sys` in +pdp_v7) ACM=pdp ; BM=0 ;; +vax_bsd4_1a) ACM=vax4 ;; +vax_bsd4_2) ACM=vax4 ;; +vax_sysV_2) ACM=vax4 ;; +pc_ix) ACM=i86 ; BM=0;; +sun3) ACM=sun3 ;; +sun2) ACM=sun2 ;; m68_unisoft) ACM=m68k2 ;; +m68_sysV_0) ACM=mantra ;; *) ACM=m68k2 ;; esac rm -f local.h -sed /ACKM/s/'".*"'/'"'$ACM'"'/ <../h/local.h >local.h +sed -e /ACKM/s/'".*"'/'"'$ACM'"'/ -e /BIGMACH/s/'[01]'/$BM/ < ../h/local.h >local.h if cmp -s ../h/local.h local.h then : else @@ -120,27 +213,64 @@ else fi echo "Your default machine to compile for is $ACM" case `ack_sys` in -vax_bsd4_*) - echo 'Installing the include directory in lib/vax2' - ( cd ../lib/vax2 ; sh fetch_inc ) - echo Done - case `ack_sys` in - vax_bsd4_1a) VERS=BSD41a ;; - vax_bsd4_1c) VERS=BSD41c ;; - vax_bsd4_2) VERS=BSD42 ;; - *) echo "Unknown VAX BSD version, look at mach/vax[24]/libem" - break ;; - esac - for i in vax2 vax4 - do ( - cd ../mach/$i/libem - ed - system.h < /dev/null 2>&1 + rm -f libsys/* + cp libbsd4_2/* libsys + ) + ;; +vax_bsd4_1a) + ( cd ../mach/vax4 + echo "Copying mach/vax4/libbsd4_1a to mach/vax4/libsys" + mkdir libsys > /dev/null 2>&1 + rm -f libsys/* + cp libbsd4_1a/* libsys + ) + ;; +vax_sysV_2) + ( cd ../mach/vax4 + echo "Copying mach/vax4/libsysV_2 to mach/vax4/libsys" + mkdir libsys > /dev/null 2>&1 + rm -f libsys/* + cp libsysV_2/* libsys + ) + ;; +esac +case X$BM in +X0) + ( cd ../lang/cem/cemcom + cp SmallPars Parameters + cd ../../../lib/descr + cp fe fe.orig + ed - fe << ABC +/em_cemcom/i + prep always +. +/CPP_F/;.+2d +/CPP_F/;.+2d w q ABC - ) done - echo 'mach/vax[24]/libem/system.h reflects your BSD version.' + ) + for i in ../mach/vax4/cg ../mach/m68k2/cg ../mach/m68020/ncg + do + ( cd $i + cp tables1.c tables.c + cp tables1.h tables.h + ) + done + ( cd ../modules/src/malloc + ed - param.h < /dev/null 2>&1 + then + TRY=`.Xlex` + else TRY=-lln + fi + myecho "trying to find your lex library ..." + cat > x.l <<'EOF' +%% +[A-Z] putchar(yytext[0]+'a'-'A'); +EOF + if lex x.l > /dev/null 2>&1 && cc -c lex.yy.c > /dev/null 2>&1 + then : + else myecho "Sorry, your lex does not seem to work" + exit 2 + fi + cat > trylib <<'EOF' +if cc lex.yy.o $1 > /dev/null 2>&1 +then + rm -f lex.yy.* a.out + exit 0 +else + exit 1 +fi +EOF + if sh trylib $TRY + then + LEX=$TRY + else + exec $0 -ll $TRY + fi + ;; +*) if sh trylib $1 + then + LEX=$1 + else + TRIES="$2 and $1" + FL=fail + fi + ;; +esac +case X$FL in +Xfail) myecho 'What option do I have to give to cc to get the LEX library?' + myecho "I tried " $TRIES "but these don't seem to work." + myecho -n 'LEX library option: ' + if read ANSWER + then : + else myecho "Sorry, got EOF while reading your answer" + exit 9 + fi + exec $0 $ANSWER "$TRIES" + ;; +Xsucces) + for i in ../util/opt ../util/cgg ../util/ncgg ../lang/occam/comp + do + ( cd $i + ed - Makefile << EOF +/^LEXLIB/c +LEXLIB = $LEX +. +w +q +EOF + ) + done + ;; +esac +rm -f x.l trylib lex.yy.* +echo echo "$LEX" > .Xlex +chmod +x .Xlex +echo "apparently, \"cc ... $LEX\" works" diff --git a/first/hash b/first/hash new file mode 100755 index 00000000..e69de29b diff --git a/first/local.h.src b/first/local.h.src index 9839d9c8..1fdb12b1 100644 --- a/first/local.h.src +++ b/first/local.h.src @@ -1,7 +1,15 @@ +/* $Header$ */ +/* + * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + */ /* collection of options, selected by including or excluding 'defines' */ /* Version number of the EM object code */ # define VERSION 3 /* 16 bits number */ /* The default machine used by ack, acc, apc */ -# define ACKM "pdp" +# define ACKM "sun3" + +/* size of local machine, either 0 (for 16 bit address space), or 1 */ +# define BIGMACHINE 1 diff --git a/first/myecho.c b/first/myecho.c new file mode 100644 index 00000000..d74c09a3 --- /dev/null +++ b/first/myecho.c @@ -0,0 +1,21 @@ +#include + +main(argc, argv) + int argc; + char *argv[]; +{ + int nflag = 0; + + if(argc > 1 && ! strncmp(argv[1], "-n", 2)) { + nflag++; + argc--; + argv++; + } + while (--argc > 0) { + fputs(argv[1], stdout); + argv++; + if (argc > 1) putchar(' '); + } + if (!nflag) putchar('\n'); + exit(0); +} diff --git a/h/.distr b/h/.distr new file mode 100644 index 00000000..685a45e3 --- /dev/null +++ b/h/.distr @@ -0,0 +1,26 @@ +Makefile +arch.h +bc_io.h +bc_string.h +as_spec.h +cg_pattern.h +cgg_cg.h +em_abs.h +em_ego.h +em_flag.h +em_mes.h +em_mnem.h +em_path.h +em_pseu.h +em_ptyp.h +em_reg.h +em_spec.h +local.h +out.h +pc_err.h +pc_file.h +pc_size.h +ranlib.h +ocm_chan.h +ocm_parco.h +ocm_proc.h diff --git a/h/arch.h b/h/arch.h index 0c8e9bff..78a0bc3b 100644 --- a/h/arch.h +++ b/h/arch.h @@ -1,4 +1,11 @@ +/* $Header$ */ +/* + * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + */ #define ARMAG 0177545 +#define AALMAG 0177454 + struct ar_hdr { char ar_name[14]; long ar_date; diff --git a/h/as_spec.h b/h/as_spec.h index b309ffa0..4909cbc0 100644 --- a/h/as_spec.h +++ b/h/as_spec.h @@ -1 +1,6 @@ +/* $Header$ */ +/* + * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + */ #define as_magic (sp_magic|(14<<8)) diff --git a/h/bc_io.h b/h/bc_io.h index fe0df882..933a7487 100644 --- a/h/bc_io.h +++ b/h/bc_io.h @@ -1,3 +1,7 @@ +/* + * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + */ #include /* $Header$ */ diff --git a/h/bc_string.h b/h/bc_string.h index 527b6fb1..7dc31c54 100644 --- a/h/bc_string.h +++ b/h/bc_string.h @@ -1,3 +1,7 @@ +/* + * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + */ # /* $Header$ */ diff --git a/h/cg_pattern.h b/h/cg_pattern.h index fc42c2f8..46dac7c2 100644 --- a/h/cg_pattern.h +++ b/h/cg_pattern.h @@ -1,3 +1,8 @@ +/* $Header$ */ +/* + * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + */ /* offsets of interesting fields in EM-pattern */ #define PO_HASH 0 diff --git a/h/cgg_cg.h b/h/cgg_cg.h index ea8c4a16..c0e7d751 100644 --- a/h/cgg_cg.h +++ b/h/cgg_cg.h @@ -1,3 +1,7 @@ +/* + * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + */ /* $Header$ */ /* offsets of interesting fields in EM-pattern */ @@ -120,6 +124,9 @@ typedef struct exprnode *node_p; #define EX_HIGHW 38 #define EX_INREG 39 #define EX_REGVAR 40 +#define EX_OR 41 +#define EX_XOR 42 +#define EX_AND 43 typedef struct { /* to stack coercions */ diff --git a/h/em_abs.h b/h/em_abs.h index 2daf5480..9855cff5 100644 --- a/h/em_abs.h +++ b/h/em_abs.h @@ -1,3 +1,8 @@ +/* $Header$ */ +/* + * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + */ #define LINO_AD 0 #define FILN_AD 4 diff --git a/h/em_ego.h b/h/em_ego.h index 61457e26..958cbdc7 100644 --- a/h/em_ego.h +++ b/h/em_ego.h @@ -1,3 +1,8 @@ +/* $Header$ */ +/* + * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + */ /* * The various different hints as given after a mes ms_ego * diff --git a/h/em_flag.h b/h/em_flag.h index 0425b18e..fb0c9ef9 100644 --- a/h/em_flag.h +++ b/h/em_flag.h @@ -1,3 +1,8 @@ +/* $Header$ */ +/* + * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + */ /* flags */ #define EM_PAR 0017 /* parameter type */ #define EM_FLO 0060 /* flow information */ diff --git a/h/em_mes.h b/h/em_mes.h index 8eec56b7..9849f601 100644 --- a/h/em_mes.h +++ b/h/em_mes.h @@ -1,3 +1,8 @@ +/* $Header$ */ +/* + * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + */ /* * mnemonics for the message numbers in EM */ diff --git a/h/em_ptyp.h b/h/em_ptyp.h index 6cb3da2c..5b1cf095 100644 --- a/h/em_ptyp.h +++ b/h/em_ptyp.h @@ -1,8 +1,25 @@ +/* $Header$ */ +/* + * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + */ #define ptyp(x) (1<<(x-sp_fspec)) -#define cst_ptyp 0000140 -#define sym_ptyp 0000034 -#define arg_ptyp 0000574 -#define con_ptyp 0036000 -#define val_ptyp 0037777 -#define any_ptyp 0137777 +#define cst_ptyp (ptyp(sp_cst2)|ptyp(sp_cst4)) +#define nof_ptyp (ptyp(sp_dlb1)|ptyp(sp_dlb2)|ptyp(sp_doff)) +#define sof_ptyp (ptyp(sp_dnam)|ptyp(sp_doff)) +#define lab_ptyp (ptyp(sp_dlb1)|ptyp(sp_dlb2)|ptyp(sp_dnam)) +#define ico_ptyp (ptyp(sp_icon)) +#define uco_ptyp (ptyp(sp_ucon)) +#define fco_ptyp (ptyp(sp_fcon)) +#define str_ptyp (ptyp(sp_scon)) +#define con_ptyp (str_ptyp|ico_ptyp|uco_ptyp|fco_ptyp) +#define ilb_ptyp (ptyp(sp_ilb1)|ptyp(sp_ilb2)) +#define pro_ptyp (ptyp(sp_pnam)) +#define off_ptyp (ptyp(sp_doff)) +#define end_ptyp (ptyp(sp_cend)) +#define sym_ptyp (lab_ptyp) +#define arg_ptyp (nof_ptyp|cst_ptyp|sof_ptyp) +#define par_ptyp (arg_ptyp|ico_ptyp|uco_ptyp|fco_ptyp|pro_ptyp|ilb_ptyp) +#define val_ptyp (par_ptyp|str_ptyp) +#define any_ptyp (val_ptyp|end_ptyp) diff --git a/h/em_reg.h b/h/em_reg.h index 760d7cfd..fc8034fd 100644 --- a/h/em_reg.h +++ b/h/em_reg.h @@ -1,3 +1,8 @@ +/* $Header$ */ +/* + * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + */ /* * mes ms_reg,offset,size,type,priority * diff --git a/h/m2_traps.h b/h/m2_traps.h new file mode 100644 index 00000000..39c1b6b7 --- /dev/null +++ b/h/m2_traps.h @@ -0,0 +1,3 @@ +#define M2_TOOLARGE 64 +#define M2_TOOMANY 65 +#define M2_NORESULT 66 diff --git a/h/ocm_chan.h b/h/ocm_chan.h new file mode 100644 index 00000000..18e9a196 --- /dev/null +++ b/h/ocm_chan.h @@ -0,0 +1,52 @@ +/* $Header$ */ +/* + * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + */ +/* ocm_chan.h - channel definitions */ +#include +#include "ocm_parco.h" + +typedef union channel { + struct { /* Interprocess channel */ + char _type; /* Channel type, see note */ + char synch; /* State in channel synchronization */ + long val; /* Transmitted value */ + } c; + struct { /* File channel */ + char _type; /* Dummy field, see note */ + char index; /* Index in the file array */ + char flgs; /* Status flags: in use & readahead */ + char preread; /* Possible preread character */ + } f; +} chan; +#define type c._type /* Channel type */ +/* Note: The channel type should not be part of each structure in chan. But + * the C alignment rules would make chan about 50% bigger if we had done it + * the right way. Note that the order of fields in a struct cannot be a problem + * as long as struct c is the largest within the union. + */ + +#define C_T_CHAN 0 /* Type of a interprocess channel */ +#define C_T_FILE 1 /* Type of a file channel */ + +#define C_S_FREE 0 /* IP channel is free */ +#define C_S_ANY 1 /* IP channel contains data */ +#define C_S_ACK 2 /* IP channel data is removed */ + +#define C_F_EOF (-1L) /* File channel returns EOF */ +#define C_F_TEXT (-2L) /* File channel becomes line oriented */ +#define C_F_RAW (-3L) /* File channel becomes character oriented */ + +#define C_F_INUSE 0x01 /* File channel is connected to a UNIX file */ +#define C_F_READAHEAD 0x02 /* File channel has a preread character */ + +extern chan file[20]; /* Array of file channels */ +extern FILE *unix_file[20]; /* Pointers to buffered UNIX files */ + +void c_init(); + +void chan_in(), cbyte_in(), c_wa_in(), c_ba_in(); +void chan_out(), c_wa_out(), c_ba_out(); + +int chan_any(); diff --git a/h/ocm_parco.h b/h/ocm_parco.h new file mode 100644 index 00000000..35826d70 --- /dev/null +++ b/h/ocm_parco.h @@ -0,0 +1,23 @@ +/* $Header$ */ +/* + * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + */ +/* parco.h - Define names for simulation routines + * + * This file is to be included by users of the higher-level routines + * + */ + +void pc_begin(), resumenext(), parend(), resume(), coend(); +int pc_fork(); + +#define nullid ((int *) 0 - (int *) 0) + /* I.e. a 0 of type "pointer difference" */ + +#define parbegin(sbrk) pc_begin(sbrk, nullid) +#define parfork() pc_fork(nullid) +#define cobegin(sbrk, id) pc_begin(sbrk, id) +#define cofork(id) pc_fork(id) + +extern int deadlock; diff --git a/h/ocm_proc.h b/h/ocm_proc.h new file mode 100644 index 00000000..7ffd41e9 --- /dev/null +++ b/h/ocm_proc.h @@ -0,0 +1,61 @@ +/* $Header$ */ +/* + * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + */ +/* process.h - Define administration types and functions + * + * This file is to be included by implementors of the higher + * level routines + * + */ +#include "ocm_parco.h" + +#ifndef ptrdiff /* This type must be able to hold a pointer difference */ +#if EM_WSIZE