David R. Hanson
Department of Computer Science, Princeton University,
35 Olden St.,
Princeton, NJ 08544
Extract the distribution into its own directory. All paths below are relative to this directory. The distribution holds the following subdirectories.
Installation on Unix systems involves three steps performed in the following order.
If you're installing lcc on a PC, go to Sec. 6 after skimming Secs. 2-5.
doc/install.html is the HTML file for this document. doc/install.ps and doc/install.txt are PostScript and plain ASCII versions.
Include files are in directories named include/target/system; the meaningful combinations are
For example, if the paths shown above are chosen and if include/mips/ultrix has the appropriate include files, install the man page and include files by
% cp doc/lcc.1 /usr/local/man/man1 % cp include/mips/ultrix/*.h /usr/local/include/ansi
The directory cpp contains the source code for an ANSI preprocessor written by Dennis Ritchie. It's written in ANSI C, so you must use an ANSI C compiler to compile it. Alternatively, you can install lcc with an existing preprocessor, use lcc to build and install cpp, and then reconfigure the driver to use the new cpp.
To build and install cpp, execute the commands
% cd cpp % make % cp cpp /usr/local/libwhere the destination is the location chosen for cpp in Sec. 2. Use the CC= option to specify an ANSI C compiler, if necessary; e.g., CC=gcc. The command ``make clean'' cleans up, but does not remove cpp, and ``make clobber'' cleans up and removes cpp.
This preprocessor makes no assumptions about the standard include files. You must specify the appropriate -I options in the include array defined in the host-specific part of the driver, as detailed in the next section.
Debug your version of the driver by running it with the -v -v options, which cause it to echo the commands it would execute, but not to execute them.
Here's etc/hart.c, which we'll use as an example in describing how to edit a host-specific part. This example illustrates all the important features.
/* DECStations running ULTRIX at Princeton University */ #include <string.h> char *cpp[] = { "/usr/gnu/lib/gcc-cpp", "-undef", "-DLANGUAGE_C", "-D_LANGUAGE_C", "-D__LANGUAGE_C", "-D_unix", "-D__unix", "-Dultrix", "-D_ultrix", "-D__ultrix", "-Dmips", "-D_mips", "-D__mips", "-Dhost_mips", "-D_host_mips", "-D__host_mips", "-DMIPSEL", "-D_MIPSEL", "-D__MIPSEL", "$1", "$2", "$3", 0 }; char *include[] = { "-I/usr/local/include/ansi", 0 }; char *com[] = { "/usr/local/lib/rcc", "-target=mips-ultrix", "$1", "$2", "$3", 0 }; char *as[] = { "/bin/as", "-o", "$3", "", "$1", "-nocpp", "-EL", "$2", 0 }; char *ld[] = { "/usr/bin/ld", "-o", "$3", "/usr/lib/crt0.o", "$1", "$2", "", "", "-lm", "-lc", 0 }; int option(arg) char *arg; { if (strcmp(arg, "-g") == 0) as[3] = "-g"; else if (strcmp(arg, "-p") == 0 && strcmp(ld[3], "/usr/lib/crt0.o") == 0) { ld[3] = "/usr/lib/mcrt0.o"; ld[7] = "/usr/lib/libprof1.a"; } else if (strcmp(arg, "-b") == 0 && access("/usr/local/lib/bbexit.o", 4) == 0) ld[6] = "/usr/local/lib/bbexit.o"; else return 0; return 1; }Most of the host-specific code is data that gives prototypes for the commands that invoke the preprocessor, compiler, assembler, and loader. Each command prototype is an array of pointers to strings terminated with a null pointer; the first string is the full path name of the command and the others are the arguments or argument placeholders, which are described below.
The cpp array gives the command for running the preprocessor. lcc is intended to be used with an ANSI preprocessor, like the GNU C preprocessor. If the GNU C preprocessor is used, as shown in this example, it must be named gcc-cpp in order for lcc's -N option to work correctly.
Literal arguments specified in prototypes, e.g., "-Dmips" in the cpp command above, are passed to the command as given.
The strings "$1", "$2", and "$3" in prototypes are placeholders for lists of arguments that are substituted in a copy of the prototype before the command is executed. $1 is replaced by the options specified by the user; for the preprocessor, this list always contains at least -Dunix and -D__LCC__. $2 is replaced by the input files, and $3 is replaced by the output file.
Zero-length arguments after replacement are removed from the argument list before the command is invoked. So, e.g., if the preprocessor is invoked without an output file, "$3" becomes "", which is removed from the final argument list.
For example, to specify a preprocessor command prototype to invoke the preprocessor installed in Sec. 3 with the options -Dmips and -Dultrix, the cpp array would be
char *cpp[] = { "/usr/local/lib/cpp", "-Dmips", "-Dultrix", "$1", "$2", "$3", 0 };where cpp[0] is the location chosen for cpp in Sec. 2.
The include array is a list of -I options that specify which directives should be searched to satisfy include directives. These directories are searched in the order given. The first directory should be the one to which the ANSI header files were copied in Sec. 2. The driver adds these options to cpp's arguments when it invokes the preprocessor, except when -N is specified.
Design this list carefully. Mixing ANSI and pre-ANSI headers (e.g., by listing /usr/include after the directory of ANSI headers, as shown above) may mix incompatible headers. Unless the default list holds only /usr/include or only the ANSI headers, many users may be forced to use -N and -I incessantly.
com gives the command for invoking the compiler. This prototype can appear as shown above, with two important changes. The command name should be edited to reflect the location of the compiler chosen in Sec. 2, and the option -target=mips-ultrix should be edited to the target-system for your host. lcc can generate code for all of the target-system combinations listed in the file src/bind.c. The -target option specifies the default combination. The driver's -Wf option can be used to specify other combinations; the man page elaborates.
as gives the command for invoking the assembler.
ld gives the command for invoking the loader. For the other commands, the list $2 contains a single file; for ld, $2 contains all ``.o'' files and libraries, and $3 is a.out, unless the -o option is specified. As suggested in the code above, ld must also specify the appropriate startup code and default libraries.
The option function is described below; for now, use an existing option function or one that returns 0.
After specifying the prototypes, compile the driver by
% cd etc % make HOST=hart cc -c hart.c cc -c lcc.c cc -s hart.o lcc.o; rm -f hart.o lcc.owhere hart is replaced by yourhostname. Run the resulting a.out with the options -v -v to display the commands that would be executed, e.g.,
% a.out -v -v foo.c baz.c mylib.a -lX11 a.out $ Revision: 3.2 $ $ Date: 1994/09/08 17:15:13 $ foo.c: /usr/gnu/lib/gcc-cpp -undef -DLANGUAGE_C -D_LANGUAGE_C -D__LANGUAGE_C -D_unix -D__unix -Dultrix -D_ultrix -D__ultrix -Dmips -D_mips -D__mips -Dhost_mips -D_host_mips -D__host_mips -DMIPSEL -D_MIPSEL -D__MIPSEL -Dunix -D__LCC__ -v -I/usr/local/include/ansi foo.c | /usr/local/lib/rcc -target=mips-ultrix -v - /tmp/lcc11717.s /bin/as -o foo.o -nocpp -EL /tmp/lcc11717.s baz.c: /usr/gnu/lib/gcc-cpp -undef -DLANGUAGE_C -D_LANGUAGE_C -D__LANGUAGE_C -D_unix -D__unix -Dultrix -D_ultrix -D__ultrix -Dmips -D_mips -D__mips -Dhost_mips -D_host_mips -D__host_mips -DMIPSEL -D_MIPSEL -D__MIPSEL -Dunix -D__LCC__ -v -I/usr/local/include/ansi baz.c | /usr/local/lib/rcc -target=mips-ultrix -v - /tmp/lcc11717.s /bin/as -o baz.o -nocpp -EL /tmp/lcc11717.s /usr/bin/ld -o a.out /usr/lib/crt0.o foo.o baz.o mylib.a -lX11 -lm -lc rm /tmp/lcc11717.sLeading spaces indicate lines that have been folded manually to fit this page. Note the use of a pipeline to connect the preprocessor and compiler. lcc arranges this pipeline itself; it does not call the shell. If you want lcc to use temporary files instead of a pipeline, define PIPE=0 in CFLAGS when making the driver:
% make CFLAGS='-DPIPE=0' HOST=hartThe option -pipe forces lcc to use a pipeline between the preprocessor and the compiler regardless of PIPE's value.
As the output shows, lcc places temporary files in /tmp. Alternatives can be specified by defining TEMPDIR in CFLAGS when making the driver, e.g.,
% make CFLAGS='-DTEMPDIR=\"/usr/tmp\"' HOST=hartcauses lcc to place temporary files in /usr/tmp. Once the driver is completed, install it by
% cp a.out /usr/local/bin/lccwhere the destination is the location chosen for lcc in Sec. 2.
The option function is called for the options -g, -p, -pg, and -b because these compiler options might also affect the loader's arguments. For these options, the driver calls option(arg) to give the host-specific code an opportunity to edit the ld command, if necessary. option can change ld, if necessary, and return 1 to announce its acceptance of the option. If the option is unsupported, option should return 0.
For example, in response to -g, the option function shown above changes as[3] from "" to "-g", which specifies the debugging option to the assembler. If -g is not specified, the "" argument is omitted from the as command because it's empty.
Likewise, the -p causes option to change the name of the startup code and add the name of the profiling library. Note that option has been written to support simultaneous use of -g and -p, e.g.,
% a.out -v -v -g -p foo.s baz.o -o myfoo a.out $ Revision: 3.2 $ $ Date: 1994/09/08 17:15:13 $ /bin/as -o foo.o -g -nocpp -EL foo.s /usr/bin/ld -o myfoo /usr/lib/mcrt0.o foo.o baz.o /usr/lib/libprof1.a -lm -lc rm /tmp/lcc12270.sOn Suns, the driver also recognizes -Bstatic and -Bdynamic as linker options, and recognizes but ignores Sun's ``-target name'' option.
The option -Woarg causes the driver to pass arg to option. Such options have no other effect; this mechanism is provided to support system-specific options that affect the commands executed by the driver.
To complete the driver, write an appropriate option function for your system, and make and install the driver as described above.
% make bbexit.o % cp bbexit.o /usr/local/lib/bbexit.oIf necessary, change /usr/local/lib to reflect local conventions. The exit function in etc/bbexit.c works on the systems listed in Sec. 2, but may need to be modified for other systems.
If option supports -b, you should also install etc/bprint.c, which reads prof.out and generates a listing annotated with execution counts. After lcc is installed, install bprint with the commands
% make bprint % cp bprint /usr/local/bin/bprint % cp ../doc/bprint.1 /usr/local/man/man1The makefile uses lcc to compile bprint.c; you must use lcc or another ANSI C compiler, e.g., gcc, because bprint.c is written in ANSI C. Also, bprint.c includes "../src/profio.c", so it must be compiled in etc.
The object files, rcc, and the generated code for the programs in the test suite are placed in the directory target/system where target and system are the names of your target machine and its operating system, respectively. There are directories for the supported target/system combinations, e.g., mips/ultrix.
The default target in src/makefile is rcc. lcc is built by executing make from the apppropriate target/system directory and specifying system-specific values for CFLAGS and LDFLAGS, if necessary. For example, to build rcc for a MIPS running Ultrix, execute the commands
% cd mips/ultrix % make -f ../../src/makefile cc -c -O ../../src/alloc.c ... cc -c -O ../../src/x86.c cc -o rcc alloc.o bind.o ... mips.o sparc.o x86.oThere may be a few warnings, but there should be no errors. If your host is an SGI machine running IRIX 4.0 or later, you might need CFLAGS=-cckr. If cc doesn't automatically search the directory that holds the source file, specify CFLAGS=-I../../src. If you use gcc, specify CFLAGS="-ansi -fno-builtin".
Once rcc is built with the host C compiler, run the test suite to verify that rcc is working correctly. The commands in src/makefile run the shell script src/run on each C program in the test suite, tst/*.c. It uses the driver, lcc, so you must have the driver installed before testing rcc. The target-system combination is read from the variable TARGET, which is specified when invoking make:
% make -f ../../src/makefile TARGET=mips-ultrix test ../rcc -target=mips-ultrix 8q: ../rcc -target=mips-ultrix array: ../rcc -target=mips-ultrix cf: ../rcc -target=mips-ultrix cq: ../rcc -target=mips-ultrix cvt: ../rcc -target=mips-ultrix fields: ../rcc -target=mips-ultrix front: ../rcc -target=mips-ultrix incr: ../rcc -target=mips-ultrix init: ../rcc -target=mips-ultrix limits: ../rcc -target=mips-ultrix paranoia: ../rcc -target=mips-ultrix sort: ../rcc -target=mips-ultrix spill: ../rcc -target=mips-ultrix stdarg: ../rcc -target=mips-ultrix struct: ../rcc -target=mips-ultrix switch: ../rcc -target=mips-ultrix wf1: ../rcc -target=mips-ultrix yacc:For each C program in the test suite, src/run compiles the program and uses diff to compare the generated assembly code with the expected code (the MIPS code expected for tst/8q.c is in mips/ultrix/tst/8q.sbk, etc.). If there are differences, the script executes the generated code with the input given in tst (the input for tst/8q.c is in tst/8q.0, etc.) and compares the output with the expected output (the expected output from tst/8q.c on the MIPS is in mips/ultrix/tst/8q.1bk, etc.). The script also compares the diagnostics from the compiler with the expected diagnostics.
On some systems, there may be a few differences between the generated code and the expected code. These differences occur because the expected code is generated by cross compilation on a MIPS and the least significant bits of some floating-point constants differ from those bits in constants generated on your system. There should be no differences in the output from executing the test programs.
The ../rcc and -target=mips-ultrix preceding the name of each test program in the output above indicate the compiler and the target, e.g., ``../rcc is generating code for a mips running the ultrix operating system.''
Next, build rcc again using the just-built rcc:
% make -f ../../src/makefile TARGET=mips-ultrix triple rm -f *.o make -f ../../src/makefile CC='lcc -B./ -d0.1 -A' CFLAGS='-Wf-target=mips-ultrix -I../../src/../include/`echo mips-ultrix|tr - /` -I../../src' LDFLAGS='' lcc -B./ -d0.1 -A -c -Wf-target=mips-ultrix -I../../src/../include/`echo mips-ultrix|tr - /` -I../../src ../../src/alloc.c ... lcc -B./ -d0.1 -A -c -Wf-target=mips-ultrix -I../../src/../include/`echo mips-ultrix|tr - /` -I../../src ../../src/x86.c lcc -B./ -d0.1 -A -o rcc alloc.o ... sparc.o x86.o strip rcc od rcc +8 >od2 rm -f *.o make -f ../../src/makefile CC='lcc -B./ -d0.1 -A' CFLAGS='-Wf-target=mips-ultrix -I../../src/../include/`echo mips-ultrix|tr - /` -I../../src' LDFLAGS='' lcc -B./ -d0.1 -A -c -Wf-target=mips-ultrix -I../../src/../include/`echo mips-ultrix|tr - /` -I../../src ../../src/alloc.c ... lcc -B./ -d0.1 -A -c -Wf-target=mips-ultrix -I../../src/../include/`echo mips-ultrix|tr - /` -I../../src ../../src/x86.c lcc -B./ -d0.1 -A -o rcc alloc.o ... sparc.o x86.o strip rcc od rcc +8 >od3 cmp od[23] && rm od[23]This command builds rcc twice; once using the rcc built by cc and again using the rcc built by lcc. After building each version, an octal dump of the resulting binary is made, and the two dumps are compared. They should be identical, as shown at the end of the output above. If they aren't, our compiler is generating bad code; contact us.
The final version of rcc should also pass the test suite; i.e., the output from
make -f ../../src/makefile TARGET=mips-ultrix testshould be identical to that from the previous make test.
Now install the final version of rcc:
% cp rcc /usr/local/lib/rccwhere the destination is the location chosen for rcc in Sec. 2.
On some systems, you may be able to use environment variables and make's -e option to avoid specifying TARGET on each make command, and the make commands described above can be done with a single command:
% setenv TARGET mips-ultrix % cd mips/ultrix % make -e -f ../../src/makefile test triple test cleanThe command
make -f ../../src/makefile cleancleans up, but does not remove rcc, and
make -f ../../src/makefile clobbercleans up and removes rcc.
The code generators for the other targets can be tested by running make from the appropriate target-specific directory and setting some environment variables to control what src/run does. For example, if you built mips/ultrix/rcc and installed it in /usr/local/lib/rcc, you can test the SPARC code generator for the SunOS operating system as follows.
% setenv REMOTEHOST noexecute % setenv BUILDDIR /usr/local/lib/ % cd sparc/sun % make -f ../../src/makefile RCC= TARGET=sparc-sun test /usr/local/lib/rcc -target=sparc-sun 8q: /usr/local/lib/rcc -target=sparc-sun array: /usr/local/lib/rcc -target=sparc-sun cf: /usr/local/lib/rcc -target=sparc-sun cq: /usr/local/lib/rcc -target=sparc-sun cvt: /usr/local/lib/rcc -target=sparc-sun fields: /usr/local/lib/rcc -target=sparc-sun front: /usr/local/lib/rcc -target=sparc-sun incr: /usr/local/lib/rcc -target=sparc-sun init: /usr/local/lib/rcc -target=sparc-sun limits: /usr/local/lib/rcc -target=sparc-sun paranoia: /usr/local/lib/rcc -target=sparc-sun sort: /usr/local/lib/rcc -target=sparc-sun spill: /usr/local/lib/rcc -target=sparc-sun stdarg: /usr/local/lib/rcc -target=sparc-sun struct: /usr/local/lib/rcc -target=sparc-sun switch: /usr/local/lib/rcc -target=sparc-sun wf1: /usr/local/lib/rcc -target=sparc-sun yacc:As above, src/run compares the SPARC code generated with what's expected. There should be no differences. Setting REMOTEHOST to noexecute suppresses the assembly and execution of the generated code. BUILDDIR gives the directory that holds rcc, and specifying RCC= to make insures that rcc is not rebuilt in the sparc/sun directory.
If you set REMOTEHOST to the name of a SPARC machine to which you can rlogin, src/run will rcp the generated code to that machine and execute it there, if necessary. See src/run for the details.
% lcc -Wf-target=sparc-sun -S tst/8q.cgenerates SPARC code for tst/8q.c in 8q.s.
lcc can also generate code for a ``symbolic'' target. This target is used routinely in front-end development, and its output is a printable representation of the input program, e.g., the dags constructed by the front end are printed, and other interface functions print their arguments. You can specify this target with the option -Wf-target=symbolic. For example,
% lcc -Wf-target=symbolic -S tst/8q.cgenerates symbolic output for tst/8q.c in 8q.s. Finally, the option -Wf-target=null specifies the ``null'' target for which lcc emits nothing and thus only checks the syntax and semantics of its inputs files.
% cd lburg % makeYou must use lcc or another ANSI C compiler, e.g., gcc, because lburg is written in ANSI C.
To test lburg, use it process one of the machine descriptions in src/*.md and compare the output with the corresponding src/*.c file. For example,
% lburg <../src/x86.md | diff ../src/x86.nw - 40c40 < generated at Mon Dec 5 17:49:52 1994 --- > generated at Tue Dec 6 13:35:42 1994processes src/x86.md and compares the output with what's expected. You may get 1-2 lines of differences, because lburg's output includes a timestamp and a version stamp. Move lburg to the desired installation directory. The command ``make clean'' cleans up, but does not remove lburg, and ``make clobber'' cleans up more and removes lburg.
src/makefile includes three rules for building src/mips.c, src/sparc.c, and src/x86.c from the corresponding machine descriptions in src/mips.md, src/sparc.md, and src/x86.md. These rules are commented out in the distributed src/makefile; remove the leading ``#'' to uncomment these lines, if you want to run lburg whenever these src/*.md files are changed. You may also want to remove the leading ``#'' on the rm command in the clobber rule, if you want ``make clobber'' to remove the src/*.c files generated by lburg.
Chapters 13-15 in A Retargetable C Compiler: Design and Implementation describe the overall structure of lcc's code generator, and the use of lburg. Chapters 16-18 describe the target-specific parts, including the lburg rules, for the MIPS, SPARC, and x86 architectures. The easiest way to write a code generator for a new target is to make a copy of the .md file for the architecture that's closest to your intended target, and edit the copy, using the corresponding chapter as a guide.
For example, the following steps add a code generator for the PowerPC under AIX.
power.o: $(SRC)/power.c; $(CC) -c $(CFLAGS) $(SRC)/power.c $(SRC)/power.c: $(SRC)/power.md; lburg <$(SRC)/power.md >$(SRC)/power.c
extern Interface powerIR;to the beginning of src/bind.c, and add the line
"power-aix", &powerIR,to the initialization of bindings.
% cd power/aix % make -f ../../src/makefile TARGET=power-aixbuild an rcc that includes the new code generator. It's a good idea to do all these steps changing src/power.md as little as possible to get the machinery in place first, then finish editing src/power.md into the new code generator.
On the X86, lcc emits assembler code that uses a 32-bit flat address space. It requires an external assembler, linker, library, and DOS extender. We assemble lcc's emitted code with Borland Turbo Assembler 4.0 or Microsoft's MASM 6.11, and we turn the resulting object code into a .exe file using the linker, library, and DOS extender from Borland C++ 4.02 plus Borland PowerPack for DOS. Warning: Borland's start-up code initializes the floating-point unit to convert floats and doubles to integers by rounding instead of truncating them, which the ANSI Standard specifies.
Small changes to src\x86.c have allowed it to work with the linker, library, and DOS extender from Symantec C++ 6.1.
[386Enh] device=windpmi.386to your system.ini (which is located in \windows).
lcc.exe is a prebuilt driver for use with Borland's Turbo Assembler 4.0, Borland C++ 4.02, and Borland PowerPack for DOS. To use some other combination of assembler, linker, library, and DOS extender, or to change the path names compiled into the driver, you will have to build a new driver. You'll probably have to modify the driver and include\x86\dos\*.h if you want to use them. The source code for the PC version of the driver is in etc\lcc-pc.c and etc\bc4.c. It is unlikely that you'll need to rebuild cpp.exe or rcc.exe.
lcc comes with a preprocessor and ANSI include files (include\x86\dos\*.h) that work with the library and DOS extender included with Symantec C++ 6.1. Include files are generally tailored, however, for a specific library, so use the headers that come with your library, or adapt lcc's headers to work with your library.
If you use headers that exploit language extensions, you must also use their companion preprocessor, or you must protect cpp.exe from the extensions, because cpp.exe obeys the ANSI specification. For example, when we use lcc with the library from Borland C++ 4.02, we use Borland's headers too, and they annotate many declarations with a non-ANSI attribute, __cdecl. The distribution's cpp.exe won't recognize __cdecl, so we use the cpp.exe option -D__cdecl= to eliminate this extension, or we use Borland's preprocessor.
The Unix driver for lcc invokes the process for each compiler subphase that's needed. In principle, the DOS driver could do likewise, but doing so fails under at least some DOS extenders. For maximum portability, the distributed DOS driver thus merely emits a command file, into dolcc.bat in the current directory, which must be run to compile anything; for example,
C:> lcc 8q.c C:> dolcc.batcompiles, assembles, and links 8q.c. and leaves the executable in a.exe. lcc.exe writes commands to dolcc.bat that generate and consume temporary files. These files have names like c:\tmp\lccddddd.*, so create the directory c:\tmp, if it doesn't exist.
Users running programs like doskey can easily create a macro that abbreviates this idiom, but makefiles and batch files must invoke dolcc.bat explicitly. If the library routines system or execv work with your DOS extender, you might be able to modify the routine execute in etc\lcc-pc.c to avoid this irritation.
Note that the distributed lcc.exe obviates the option -v -v decsribed in Sec. 4 because it never executes commands anyway. Also, it complicates the idiom that saves the preprocessed source in a file. That is,
C:> lcc -E 8q.c >8q.ifails to capture the preprocessed 8q.c in 8q.i because lcc.exe creates but does not execute a preprocessor command.
On MS-DOS systems, several batch files replace the Unix make and shell files described in the sections above. src\triple.bat replaces the make triple process described in Sec. 5. It calls src\lccn.bat to compile lcc's modules using lcc.exe. src\test.bat runs lcc's test suite. It calls src\run.bat once for each test program. The batch files compile the test files in the directory x86\dos\tst, but they compile the compiler itself in the src directory. lccn.bat and run.bat include dolcc.bat as a hard path name; edit them if you changed the driver to deliver its commands elsewhere.
src\triple.bat builds rcc.exe three times. It builds the first with Borland C++ 4.02, and builds the other two with the rcc.exe built just before. For the first of these three stages, triple.bat copies the generated executable to directory src\rcc1. The second and third stages do likewise with directories src\rcc2 and src\rcc3, and they move the .s files there as well, so that they can be compared. Clear these directories once all tests pass.
Each stage runs src\test.bat. Once all tests pass, go to the directory x86\dos\tst and run clean.bat, which cleans up.
Ideally, rcc2\rcc.exe and rcc3\rcc.exe or rcc2\*.obj and rcc3\*.obj would be identical, but at least some DOS assemblers and linkers emit slightly different object and executable files when rerun on some fixed assembler modules, so triple.bat can confirm that lcc consistently emits the same code for itself only by comparing rcc2\*.s with rcc3\*.s.
subscribe lccto majordomo@cs.princeton.edu. This line must appear in the message body; ``Subject:'' lines are ignored. To learn more about mailing lists served by majordomo, send a message with the 1-word body ``help'' to majordomo@cs.princeton.edu. Mail sent to lcc@cs.princeton.edu is forwarded to everyone on the mailing list.
There is also an lcc-bugs mailing list for reporting bugs; subscribe to it by sending a message with the 1-line body
subscribe lcc-bugsto majordomo@cs.princeton.edu. Mail addressed to lcc-bugs@cs.princeton.edu is forwarded to everyone on this list.