More Dirty Tricks in SPITBOL
This post continues the discussion (, ) about dirty tricks in the SPITBOL compiler. This time I'd like to talk about a technique that I mentioned briefly in passing: Looking at the generated code during execution to find where statements begin. It turns out that SPITBOL's runtime support examined the program's code while it was running in several contexts. In fact, it even changed the program's code from time to time.
The most important reason for the program to change its own code was the unusual way in which SNOBOL4 does I/O. Instead of having I/O statements or library functions, it allows the programmer to designate specific variables as being attached to files. For example, the variable named
INPUT reads a line from the standard input stream each time its value is accessed, and the variable named
OUTPUT writes its contents, followed by a newline, to the standard output stream each time it is given a new value. Accordingly, the statement
OUTPUT = INPUT
reads a line from the standard input and copies that line to the standard output.
If you want to read from a file, rather than from the standard input stream, you execute a statement such as
Here, we are calling the library function named
INPUT (which is distinct from the variable named
INPUT) and giving it two arguments. The first argument is the name of a variable that we wish to associate with an input file, the name of which we supply as the second argument. There is a corresponding library function named
OUTPUT for output files.
Because we can use a string to name the variable(s) that we intend to use for input/output, it is not possible in general to know until the
OUTPUT function is executed what the name of that variable will be. Accordingly, when the compiler generates a load or store instruction that accesses any variable at all, it is possible that that instruction will wind up doing input or output. It should be obvious that the overhead involved in testing every variable access for possible I/O operations would slow down programs substantially.
SPITBOL avoided this overhead by a technique that was as effective as it was sneaky: When a program calls
OUTPUT, the runtime library now knows the name of the variable that is to be used for the purpose. It has a symbol table sitting around at runtime, so it can figure out that variable's address. It then looks through the machine code that it generated for the entire program, finding every instruction that accessed that variable. It replaces each such instruction by a call to the library.
In other words, the call to
INPUT above replaces every instruction that accesses the variable
in by a call to the library that asks it to read from
Obviously, this technique is not one that we would recommend in general. In the case of SPITBOL, however, it was yet one more technique that users of the compiler did not have to worry about, and that contributed to the blazing speed of programs that the compiler compiled.