[1] Of course, compiled procedures functions as well as interpreted procedures functions are compound (nonprimitive). For compatibility with the terminology used in the explicit-control evaluator, in this section we will use compound to mean interpreted (as opposed to compiled).
[2] Now that the evaluator machine starts with a branch, we must always initialize the flag register before starting the evaluator machine. To start the machine at its ordinary read-eval-print read-evaluate-print loop, we could use
Original JavaScript
(define (start-eceval) (set! the-global-environment (setup-environment)) (set-register-contents! eceval 'flag false) (start eceval)) function start_eceval() { set_register_contents(eceval, "flag", false); return start(eceval); }
[3] Since a compiled procedure function is an object that the system may try to print, we also modify the system print operation user-print user_print (from section 4.1.4) so that it will not attempt to print the components of a compiled procedure: function:
Original JavaScript
(define (user-print object) (cond ((compound-procedure? object) (display (list 'compound-procedure (procedure-parameters object) (procedure-body object) '))) ((compiled-procedure? object) (display ')) (else (display object)))) function user_print(string, object) { function prepare(object) { return is_compound_function(object) ? "< compound function >" : is_primitive_function(object) ? "< primitive function >" : is_compiled_function(object) ? "< compiled function >" : is_pair(object) ? pair(prepare(head(object)), prepare(tail(object))) : object; } display(string + " " + stringify(prepare(object))); }
[4] We can do even better by extending the compiler to allow compiled code to call interpreted procedures. functions. See exercise 5.53.
[5] Independent of the strategy of execution, we incur significant overhead if we insist that errors encountered in execution of a user program be detected and signaled, rather than being allowed to kill the system or produce wrong answers. For example, an out-of-bounds array reference can be detected by checking the validity of the reference before performing it. The overhead of checking, however, can be many times the cost of the array reference itself, and a programmer should weigh speed against safety in determining whether such a check is desirable. A good compiler should be able to produce code with such checks, should avoid redundant checks, and should allow programmers to control the extent and type of error checking in the compiled code.

Compilers for popular languages, such as C and C++, put hardly any error-checking operations into running code, so as to make things run as fast as possible. As a result, it falls to programmers to explicitly provide error checking. Unfortunately, people often neglect to do this, even in critical applications where speed is not a constraint. Their programs lead fast and dangerous lives. For example, the notorious Worm that paralyzed the Internet in 1988 exploited the UNIX$^{\textrm{TM}}$ operating system's failure to check whether the input buffer has overflowed in the finger daemon. (See Spafford 1989.)
[6] Of course, with either the interpretation or the compilation strategy we must also implement for the new machine storage allocation, input and output, and all the various operations that we took as primitive in our discussion of the evaluator and compiler. One strategy for minimizing work here is to write as many of these operations as possible in Lisp JavaScript and then compile them for the new machine. Ultimately, everything reduces to a small kernel (such as garbage collection and the mechanism for applying actual machine primitives) that is hand-coded for the new machine.
[7] This strategy leads to amusing tests of correctness of the compiler, such as checking whether the compilation of a program on the new machine, using the compiled compiler, is identical with the compilation of the program on the original Lisp JavaScript system. Tracking down the source of differences is fun but often frustrating, because the results are extremely sensitive to minuscule details.
5.5.7   Interfacing Compiled Code to the Evaluator