Twobit Pass 1: standardization of syntax.

Pass 1 collects information needed for optimization while converting a Scheme definition or expression into the front end's intermediate form. It performs hygienic macro expansion, eliminates internal definitions, checks syntax, and gives a unique name to each local variable (alpha conversion). Pass 1 also creates for each local variable a table containing all references, assignments, and procedure calls to that variable. If the integrate-usual-procedures compiler switch is true, then Pass 1 performs some constant folding and normalizes the number of arguments passed to procedures such as + and make-vector.

As an example, Pass 1 converts

(define reverse-map
  (lambda (f l)
    (define (loop l x)
      (if (pair? l)
          (loop (cdr l)
                (cons (f (car l))
                      x))
          x))
    (loop l '())))

into a forbiddingly large intermediate form that is equivalent to

((lambda ()
   (begin
     (set! reverse-map
       (lambda (.f|1 .l|1)
         ((lambda (.loop|2)
            (begin
              (set! .loop|2
                (lambda (.l|3 .x|3)
                  (if (pair? .l|3)
                      (.loop|2
                        ((lambda (.x|6|9)
                           (begin
                             (.check! (pair? .x|6|9) '1 .x|6|9)
                             (.cdr:pair .x|6|9)))
                         .l|3)
                        (cons (.f|1 ((lambda (.x|15|18)
                                       (begin
                                         (.check! (pair? .x|15|18) '0 .x|15|18)
                                         (.car:pair .x|15|18)))
                                     .l|3))
                              .x|3))
                      .x|3)))
              (.loop|2 .l|1 '())))
          (unspecified))))
     'reverse-map)))

This would be a legal expression of IEEE Scheme, except the renamed identifers are illegal: They have been prefixed by a period and contain a vertical bar. These illegal characters prevent any conflict with the names of legal global variables, which are not renamed. When Twobit is used as a preprocessor, legal but obscure sequences of characters are used instead of the period and vertical bar.

The internal definition of loop has been converted into a LET that binds .loop|2 to a newly allocated location whose contents are undefined, and then stores the result of a lambda expression into that location. That is a literal rendering of the official semantics for an internal definition in Scheme, but most optimizing compilers do not expand internal definitions and LETRECs to such a low level.

Calls to certain of Scheme's standard library procedures, including car and cdr, have been expanded into calls to low-level procedures that are protected by explicit run-time checks.

The output of Pass 1 has several important properties:

Since there are no internal definitions in the output of Pass 1, Pass 2 has only seven kinds of expression to optimize.