Import GC benchmarks from Larceny, by Hansen, Clinger, et al.
[bpt/guile.git] / gc-benchmarks / larceny / twobit-input-long.sch
diff --git a/gc-benchmarks/larceny/twobit-input-long.sch b/gc-benchmarks/larceny/twobit-input-long.sch
new file mode 100644 (file)
index 0000000..5727ad7
--- /dev/null
@@ -0,0 +1,23798 @@
+; Complete source for Twobit and Sparc assembler in one file.
+; $Id: twobit.sch,v 1.3 1999/08/23 19:14:26 lth Exp $
+;
+; See 'twobit-benchmark', at end.
+
+; Copyright 1998 Lars T Hansen.
+;
+; $Id: twobit.sch,v 1.3 1999/08/23 19:14:26 lth Exp $
+;
+; Completely fundamental pathname manipulation.
+
+; This takes zero or more directory components and a file name and
+; constructs a filename relative to the current directory.
+
+(define (make-relative-filename . components)
+
+  (define (construct l)
+    (if (null? (cdr l))
+       l
+       (cons (car l)
+             (cons "/" (construct (cdr l))))))
+
+  (if (null? (cdr components))
+      (car components)
+      (apply string-append (construct components))))
+
+; This takes one or more directory components and constructs a 
+; directory name with proper termination (a crock -- we can finess 
+; this later).
+
+(define (pathname-append . components)
+
+  (define (construct l)
+    (cond ((null? (cdr l))
+          l)
+         ((string=? (car l) "")
+          (construct (cdr l)))
+          ((char=? #\/ (string-ref (car l) (- (string-length (car l)) 1)))
+           (cons (car l) (construct (cdr l))))
+         (else
+          (cons (car l)
+                (cons "/" (construct (cdr l)))))))
+
+  (let ((n (if (null? (cdr components))
+              (car components)
+              (apply string-append (construct components)))))
+    (if (not (char=? #\/ (string-ref n (- (string-length n) 1))))
+       (string-append n "/")
+       n)))
+
+; eof
+; Copyright 1998 Lars T Hansen.
+;
+; $Id: twobit.sch,v 1.3 1999/08/23 19:14:26 lth Exp $
+;
+; Nbuild parameters for SPARC Larceny.
+
+(define (make-nbuild-parameter dir source? verbose? hostdir hostname)
+  (let ((parameters 
+        `((compiler       . ,(pathname-append dir "Compiler"))
+          (util           . ,(pathname-append dir "Util"))
+          (build          . ,(pathname-append dir "Rts" "Build"))
+          (source         . ,(pathname-append dir "Lib"))
+           (common-source  . ,(pathname-append dir "Lib" "Common"))
+           (repl-source    . ,(pathname-append dir "Repl"))
+           (interp-source  . ,(pathname-append dir "Eval"))
+           (machine-source . ,(pathname-append dir "Lib" "Sparc"))
+          (common-asm     . ,(pathname-append dir "Asm" "Common"))
+          (sparc-asm      . ,(pathname-append dir "Asm" "Sparc"))
+          (target-machine . SPARC)
+          (endianness     . big)
+          (word-size      . 32)
+          (always-source? . ,source?)
+          (verbose-load?  . ,verbose?)
+          (compatibility  . ,(pathname-append dir "Compat" hostdir))
+          (host-system    . ,hostname)
+          )))
+    (lambda (key)
+      (let ((probe (assq key parameters)))
+       (if probe 
+           (cdr probe)
+           #f)))))
+
+(define nbuild-parameter
+  (make-nbuild-parameter "" #f #f "Larceny" "Larceny"))
+
+; eof
+; Copyright 1998 Lars T Hansen.
+;
+; $Id: twobit.sch,v 1.3 1999/08/23 19:14:26 lth Exp $
+;
+; Useful list functions.
+;
+; Notes:
+; * Reduce, reduce-right, fold-right, fold-left are compatible with MIT Scheme.
+; * Make-list is compatible with MIT Scheme and Chez Scheme.
+; * These are not (yet) compatible with Shivers's proposed list functions.
+; * remq, remv, remove, remq!, remv!, remov!, every?, and some? are in the 
+;   basic library.
+
+; Destructively remove all associations whose key matches `key' from `alist'.
+
+(define (aremq! key alist)
+  (cond ((null? alist) alist)
+       ((eq? key (caar alist))
+        (aremq! key (cdr alist)))
+       (else
+        (set-cdr! alist (aremq! key (cdr alist)))
+        alist)))
+
+(define (aremv! key alist)
+  (cond ((null? alist) alist)
+       ((eqv? key (caar alist))
+        (aremv! key (cdr alist)))
+       (else
+        (set-cdr! alist (aremv! key (cdr alist)))
+        alist)))
+
+(define (aremove! key alist)
+  (cond ((null? alist) alist)
+       ((equal? key (caar alist))
+        (aremove! key (cdr alist)))
+       (else
+        (set-cdr! alist (aremove! key (cdr alist)))
+        alist)))
+
+; Return a list of elements of `list' selected by the predicate.
+
+(define (filter select? list)
+  (cond ((null? list) list)
+       ((select? (car list))
+        (cons (car list) (filter select? (cdr list))))
+       (else
+        (filter select? (cdr list)))))
+
+; Return the first element of `list' selected by the predicate.
+
+(define (find selected? list)
+  (cond ((null? list) #f)
+       ((selected? (car list)) (car list))
+       (else (find selected? (cdr list)))))
+
+; Return a list with all duplicates (according to predicate) removed.
+
+(define (remove-duplicates list same?)
+
+  (define (member? x list)
+    (cond ((null? list) #f)
+          ((same? x (car list)) #t)
+          (else (member? x (cdr list)))))
+
+  (cond ((null? list) list)
+        ((member? (car list) (cdr list))
+         (remove-duplicates (cdr list) same?))
+        (else
+         (cons (car list) (remove-duplicates (cdr list) same?)))))
+
+; Return the least element of `list' according to some total order.
+
+(define (least less? list)
+  (reduce (lambda (a b) (if (less? a b) a b)) #f list))
+
+; Return the greatest element of `list' according to some total order.
+
+(define (greatest greater? list)
+  (reduce (lambda (a b) (if (greater? a b) a b)) #f list))
+  
+; (mappend p l) = (apply append (map p l))
+
+(define (mappend proc l)
+  (apply append (map proc l)))
+
+; (make-list n)   => (a1 ... an) for some ai
+; (make-list n x) => (a1 ... an) where ai = x
+
+(define (make-list nelem . rest)
+  (let ((val (if (null? rest) #f (car rest))))
+    (define (loop n l)
+      (if (zero? n)
+         l
+         (loop (- n 1) (cons val l))))
+    (loop nelem '())))
+
+; (reduce p x ()) => x
+; (reduce p x (a)) => a
+; (reduce p x (a b ...)) => (p (p a b) ...))
+
+(define (reduce proc initial l)
+
+  (define (loop val l)
+    (if (null? l)
+        val
+        (loop (proc val (car l)) (cdr l))))
+
+  (cond ((null? l) initial)
+       ((null? (cdr l)) (car l))
+       (else (loop (car l) (cdr l)))))
+
+; (reduce-right p x ()) => x
+; (reduce-right p x (a)) => a
+; (reduce-right p x (a b ...)) => (p a (p b ...))
+
+(define (reduce-right proc initial l)
+
+  (define (loop l)
+    (if (null? (cdr l))
+       (car l)
+       (proc (car l) (loop (cdr l)))))
+
+  (cond ((null? l) initial)
+       ((null? (cdr l)) (car l))
+       (else (loop l))))
+
+; (fold-left p x (a b ...)) => (p (p (p x a) b) ...)
+
+(define (fold-left proc initial l)
+  (if (null? l)
+      initial
+      (fold-left proc (proc initial (car l)) (cdr l))))
+
+; (fold-right p x (a b ...)) => (p a (p b (p ... x)))
+
+(define (fold-right proc initial l)
+  (if (null? l)
+      initial
+      (proc (car l) (fold-right proc initial (cdr l)))))
+
+; (iota n) => (0 1 2 ... n-1)
+
+(define (iota n)
+  (let loop ((n (- n 1)) (r '()))
+    (let ((r (cons n r)))
+      (if (= n 0)
+         r
+         (loop (- n 1) r)))))
+
+; (list-head (a1 ... an) m) => (a1 ... am)   for m <= n
+
+(define (list-head l n)
+  (if (zero? n)
+      '()
+      (cons (car l) (list-head (cdr l) (- n 1)))))
+
+       
+; eof
+; Copyright 1998 Lars T Hansen.
+;
+; $Id: twobit.sch,v 1.3 1999/08/23 19:14:26 lth Exp $
+;
+; Larceny -- compatibility library for Twobit running under Larceny.
+
+(define ($$trace x) #t)
+
+(define host-system 'larceny)
+
+; Temporary?
+
+(define (.check! flag exn . args)
+  (if (not flag)
+      (apply error "Runtime check exception: " exn args)))
+
+; The compatibility library loads Auxlib if compat:initialize is called
+; without arguments.  Compat:load will load fasl files when appropriate.
+
+(define (compat:initialize . rest)
+  (if (null? rest)
+      (let ((dir (nbuild-parameter 'compatibility)))
+       (compat:load (string-append dir "compat2.sch"))
+       (compat:load (string-append dir "../../Auxlib/list.sch"))
+       (compat:load (string-append dir "../../Auxlib/pp.sch")))))
+
+(define (with-optimization level thunk) 
+  (thunk))
+
+; Calls thunk1, and if thunk1 causes an error to be signalled, calls thunk2.
+
+(define (call-with-error-control thunk1 thunk2) 
+  (let ((eh (error-handler)))
+    (error-handler (lambda args
+                    (error-handler eh)
+                    (thunk2)
+                    (apply eh args)))
+    (thunk1)
+    (error-handler eh)))
+
+(define (larc-new-extension fn ext)
+  (let* ((l (string-length fn))
+        (x (let loop ((i (- l 1)))
+             (cond ((< i 0) #f)
+                   ((char=? (string-ref fn i) #\.) (+ i 1))
+                   (else (loop (- i 1)))))))
+    (if (not x)
+       (string-append fn "." ext)
+       (string-append (substring fn 0 x) ext))))
+
+(define (compat:load filename)
+  (define (loadit fn)
+    (if (nbuild-parameter 'verbose-load?)
+       (format #t "~a~%" fn))
+    (load fn))
+  (if (nbuild-parameter 'always-source?)
+      (loadit filename)
+      (let ((fn (larc-new-extension filename "fasl")))
+       (if (and (file-exists? fn)
+                (compat:file-newer? fn filename))
+           (loadit fn)
+           (loadit filename)))))
+
+(define (compat:file-newer? a b)
+  (let* ((ta    (file-modification-time a))
+        (tb    (file-modification-time b))
+        (limit (vector-length ta)))
+    (let loop ((i 0))
+      (cond ((= i limit)
+            #f)
+           ((= (vector-ref ta i) (vector-ref tb i))
+            (loop (+ i 1)))
+           (else
+            (> (vector-ref ta i) (vector-ref tb i)))))))
+
+; eof
+; Copyright 1998 Lars T Hansen.
+;
+; $Id: twobit.sch,v 1.3 1999/08/23 19:14:26 lth Exp $
+;
+; Larceny -- second part of compatibility code
+; This file ought to be compiled, but doesn't have to be.
+;
+; 12 April 1999
+
+(define host-system 'larceny)          ; Don't remove this!
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;
+; A well-defined sorting procedure.
+
+(define compat:sort (lambda (list less?) (sort list less?)))
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;
+; Well-defined character codes.
+; Returns the UCS-2 code for a character.
+
+(define compat:char->integer char->integer)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;
+; Input and output
+
+(define (write-lop item port)
+  (lowlevel-write item port)
+  (newline port)
+  (newline port))
+
+(define write-fasl-datum lowlevel-write)
+
+; The power of self-hosting ;-)
+
+(define (misc->bytevector x)
+  (let ((bv (bytevector-like-copy x)))
+    (typetag-set! bv $tag.bytevector-typetag)
+    bv))
+
+(define string->bytevector misc->bytevector)
+
+(define bignum->bytevector misc->bytevector)
+
+(define (flonum->bytevector x)
+  (clear-first-word (misc->bytevector x)))
+
+(define (compnum->bytevector x)
+  (clear-first-word (misc->bytevector x)))
+
+; Clears garbage word of compnum/flonum; makes regression testing much
+; easier.
+
+(define (clear-first-word bv)
+  (bytevector-like-set! bv 0 0)
+  (bytevector-like-set! bv 1 0)
+  (bytevector-like-set! bv 2 0)
+  (bytevector-like-set! bv 3 0)
+  bv)
+
+(define (list->bytevector l)
+  (let ((b (make-bytevector (length l))))
+    (do ((i 0 (+ i 1))
+        (l l (cdr l)))
+       ((null? l) b)
+      (bytevector-set! b i (car l)))))
+
+(define bytevector-word-ref 
+  (let ((two^8  (expt 2 8))
+       (two^16 (expt 2 16))
+       (two^24 (expt 2 24)))
+    (lambda (bv i)
+      (+ (* (bytevector-ref bv i) two^24)
+        (* (bytevector-ref bv (+ i 1)) two^16)
+        (* (bytevector-ref bv (+ i 2)) two^8)
+        (bytevector-ref bv (+ i 3))))))
+
+(define (twobit-format fmt . rest)
+  (let ((out (open-output-string)))
+    (apply format out fmt rest)
+    (get-output-string out)))
+
+; This needs to be a random number in both a weaker and stronger sense
+; than `random': it doesn't need to be a truly random number, so a sequence
+; of calls can return a non-random sequence, but if two processes generate
+; two sequences, then those sequences should not be the same.
+;
+; Gross, huh?
+
+(define (an-arbitrary-number)
+  (system "echo \\\"`date`\\\" > a-random-number")
+  (let ((x (string-hash (call-with-input-file "a-random-number" read))))
+    (delete-file "a-random-number")
+    x))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;
+; Miscellaneous
+
+(define cerror error)
+
+; eof
+; Copyright 1991 Wiliam Clinger.
+;
+; $Id: twobit.sch,v 1.3 1999/08/23 19:14:26 lth Exp $
+;
+; Sets represented as lists.
+;
+; 5 April 1999.
+
+(define (empty-set) '())
+
+(define (empty-set? x) (null? x))
+
+(define (make-set x)
+  (define (loop x y)
+    (cond ((null? x) y)
+          ((member (car x) y) (loop (cdr x) y))
+          (else (loop (cdr x) (cons (car x) y)))))
+  (loop x '()))
+
+(define (set-equal? x y)
+  (and (subset? x y) (subset? y x)))
+
+(define (subset? x y)
+  (every? (lambda (x) (member x y))
+          x))
+
+; To get around MacScheme's limit on the number of arguments.
+
+(define apply-union)
+
+(define union
+  (letrec ((union2
+            (lambda (x y)
+              (cond ((null? x) y)
+                    ((member (car x) y)
+                     (union2 (cdr x) y))
+                    (else (union2 (cdr x) (cons (car x) y)))))))
+    
+    (set! apply-union
+          (lambda (sets)
+            (do ((sets sets (cdr sets))
+                 (result '() (union2 (car sets) result)))
+                ((null? sets)
+                 result))))
+    
+    (lambda args
+      (cond ((null? args) '())
+            ((null? (cdr args)) (car args))
+            ((null? (cddr args)) (union2 (car args) (cadr args)))
+            (else (union2 (union2 (car args)
+                                  (cadr args))
+                          (apply union (cddr args))))))))
+
+(define intersection
+  (letrec ((intersection2
+            (lambda (x y)
+              (cond ((null? x) '())
+                    ((member (car x) y)
+                     (cons (car x) (intersection2 (cdr x) y)))
+                    (else (intersection2 (cdr x) y))))))
+    (lambda args
+      (cond ((null? args) '())
+            ((null? (cdr args)) (car args))
+            ((null? (cddr args)) (intersection2 (car args) (cadr args)))
+            (else (intersection2 (intersection2 (car args)
+                                                (cadr args))
+                                 (apply intersection (cddr args))))))))
+
+(define (difference x y)
+  (cond ((null? x) '())
+        ((member (car x) y)
+         (difference (cdr x) y))
+        (else (cons (car x) (difference (cdr x) y)))))
+; Reasonably portable hashing on EQ?, EQV?, EQUAL?.
+; Requires bignums, SYMBOL-HASH.
+;
+; Given any Scheme object, returns a non-negative exact integer
+; less than 2^24.
+
+(define object-hash (lambda (x) 0))    ; hash on EQ?, EQV?
+(define equal-hash (lambda (x) 0))     ; hash on EQUAL?
+
+(let ((n 16777216)
+      (n-1 16777215)
+      (adj:fixnum   9000000)
+      (adj:negative 8000000)
+      (adj:large    7900000)
+      (adj:ratnum   7800000)
+      (adj:complex  7700000)
+      (adj:flonum   7000000)
+      (adj:compnum  6900000)
+      (adj:char     6111000)
+      (adj:string   5022200)
+      (adj:vector   4003330)
+      (adj:misc     3000444)
+      (adj:pair     2555000)
+      (adj:proc     2321001)
+      (adj:iport    2321002)
+      (adj:oport    2321003)
+      (adj:weird    2321004)
+      (budget0      32))
+  
+  (define (combine hash adjustment)
+    (modulo (+ hash hash hash adjustment) 16777216))
+  
+  (define (hash-on-equal x budget)
+    (if (> budget 0)
+        (cond ((string? x)
+               (string-hash x))
+              ((pair? x)
+               (let ((budget (quotient budget 2)))
+                 (combine (hash-on-equal (car x) budget)
+                          (hash-on-equal (cdr x) budget))))
+              ((vector? x)
+               (let ((n (vector-length x))
+                     (budget (quotient budget 4)))
+                 (if (> n 0)
+                     (combine
+                      (combine (hash-on-equal (vector-ref x 0) budget)
+                               (hash-on-equal (vector-ref x (- n 1)) budget))
+                      (hash-on-equal (vector-ref x (quotient n 2))
+                                     (+ budget budget)))
+                     adj:vector)))
+              (else
+               (object-hash x)))
+        adj:weird))
+  
+  (set! object-hash
+        (lambda (x)
+          (cond ((symbol? x)
+                 (symbol-hash x))
+                ((number? x)
+                 (if (exact? x)
+                     (cond ((integer? x)
+                            (cond ((negative? x)
+                                   (combine (object-hash (- x)) adj:negative))
+                                  ((< x n)
+                                   (combine x adj:fixnum))
+                                  (else
+                                   (combine (modulo x n) adj:large))))
+                           ((rational? x)
+                            (combine (combine (object-hash (numerator x))
+                                              adj:ratnum)
+                                     (object-hash (denominator x))))
+                           ((real? x)
+                            adj:weird)
+                           ((complex? x)
+                            (combine (combine (object-hash (real-part x))
+                                              adj:complex)
+                                     (object-hash (imag-part x))))
+                           (else
+                            adj:weird))
+                     (cond (#t
+                            ; We can't really do anything with inexact numbers
+                            ; unless infinities and NaNs behave reasonably.
+                            adj:flonum)
+                           ((rational? x)
+                            (combine
+                             (combine (object-hash
+                                       (inexact->exact (numerator x)))
+                                      adj:flonum)
+                             (object-hash (inexact->exact (denominator x)))))
+                           ((real? x)
+                            adj:weird)
+                           ((complex? x)
+                            (combine (combine (object-hash (real-part x))
+                                              adj:compnum)
+                                     (object-hash (imag-part x))))
+                           (else adj:weird))))
+                ((char? x)
+                 (combine (char->integer x) adj:char))
+                ((string? x)
+                 (combine (string-length x) adj:string))
+                ((vector? x)
+                 (combine (vector-length x) adj:vector))
+                ((eq? x #t)
+                 (combine 1 adj:misc))
+                ((eq? x #f)
+                 (combine 2 adj:misc))
+                ((null? x)
+                 (combine 3 adj:misc))
+                ((pair? x)
+                 adj:pair)
+                ((procedure? x)
+                 adj:proc)
+                ((input-port? x)
+                 adj:iport)
+                ((output-port? x)
+                 adj:oport)
+                (else
+                 adj:weird))))
+  
+  (set! equal-hash
+        (lambda (x)
+          (hash-on-equal x budget0)))); Hash tables.
+; Requires CALL-WITHOUT-INTERRUPTS.
+; This code should be thread-safe provided VECTOR-REF is atomic.
+;
+; (make-hashtable <hash-function> <bucket-searcher> <size>)
+;
+;     Returns a newly allocated mutable hash table
+;     using <hash-function> as the hash function
+;     and <bucket-searcher>, e.g. ASSQ, ASSV, ASSOC, to search a bucket
+;     with <size> buckets at first, expanding the number of buckets as needed.
+;     The <hash-function> must accept a key and return a non-negative exact
+;     integer.
+;
+; (make-hashtable <hash-function> <bucket-searcher>)
+;
+;     Equivalent to (make-hashtable <hash-function> <bucket-searcher> n)
+;     for some value of n chosen by the implementation.
+;
+; (make-hashtable <hash-function>)
+;
+;     Equivalent to (make-hashtable <hash-function> assv).
+;
+; (make-hashtable)
+;
+;     Equivalent to (make-hashtable object-hash assv).
+;
+; (hashtable-contains? <hashtable> <key>)
+;
+;     Returns true iff the <hashtable> contains an entry for <key>.
+;
+; (hashtable-fetch <hashtable> <key> <flag>)
+;
+;     Returns the value associated with <key> in the <hashtable> if the
+;     <hashtable> contains <key>; otherwise returns <flag>.
+;
+; (hashtable-get <hashtable> <key>)
+;
+;     Equivalent to (hashtable-fetch <hashtable> <key> #f)
+;
+; (hashtable-put! <hashtable> <key> <value>)
+;
+;     Changes the <hashtable> to associate <key> with <value>, replacing
+;     any existing association for <key>.
+;
+; (hashtable-remove! <hashtable> <key>)
+;
+;     Removes any association for <key> within the <hashtable>.
+;
+; (hashtable-clear! <hashtable>)
+;
+;     Removes all associations from the <hashtable>.
+;
+; (hashtable-size <hashtable>)
+;
+;     Returns the number of keys contained within the <hashtable>.
+;
+; (hashtable-for-each <procedure> <hashtable>)
+;
+;     The <procedure> must accept two arguments, a key and the value
+;     associated with that key.  Calls the <procedure> once for each
+;     key-value association.  The order of these calls is indeterminate.
+;
+; (hashtable-map <procedure> <hashtable>)
+;
+;     The <procedure> must accept two arguments, a key and the value
+;     associated with that key.  Calls the <procedure> once for each
+;     key-value association, and returns a list of the results.  The
+;     order of the calls is indeterminate.
+;
+; (hashtable-copy <hashtable>)
+;
+;     Returns a copy of the <hashtable>.
+
+; These global variables are assigned new values later.
+
+(define make-hashtable      (lambda args '*))
+(define hashtable-contains? (lambda (ht key) #f))
+(define hashtable-fetch     (lambda (ht key flag) flag))
+(define hashtable-get       (lambda (ht key) (hashtable-fetch ht key #f)))
+(define hashtable-put!      (lambda (ht key val) '*))
+(define hashtable-remove!   (lambda (ht key) '*))
+(define hashtable-clear!    (lambda (ht) '*))
+(define hashtable-size      (lambda (ht) 0))
+(define hashtable-for-each  (lambda (ht proc) '*))
+(define hashtable-map       (lambda (ht proc) '()))
+(define hashtable-copy      (lambda (ht) ht))
+
+; Implementation.
+; A hashtable is represented as a vector of the form
+;
+;     #(("HASHTABLE") <count> <hasher> <searcher> <buckets>)
+;
+; where <count> is the number of associations within the hashtable,
+; <hasher> is the hash function, <searcher> is the bucket searcher,
+; and <buckets> is a vector of buckets.
+;
+; The <hasher> and <searcher> fields are constant, but
+; the <count> and <buckets> fields are mutable.
+;
+; For thread-safe operation, the mutators must modify both
+; as an atomic operation.  Other operations do not require
+; critical sections provided VECTOR-REF is an atomic operation
+; and the operation does not modify the hashtable, does not
+; reference the <count> field, and fetches the <buckets>
+; field exactly once.
+
+(let ((doc      (list "HASHTABLE"))
+      (count    (lambda (ht)   (vector-ref ht 1)))
+      (count!   (lambda (ht n) (vector-set! ht 1 n)))
+      (hasher   (lambda (ht)   (vector-ref ht 2)))
+      (searcher (lambda (ht)   (vector-ref ht 3)))
+      (buckets  (lambda (ht)   (vector-ref ht 4)))
+      (buckets! (lambda (ht v) (vector-set! ht 4 v)))
+      (defaultn 10))
+  (let ((hashtable? (lambda (ht)
+                      (and (vector? ht)
+                           (= 5 (vector-length ht))
+                           (eq? doc (vector-ref ht 0)))))
+        (hashtable-error (lambda (x)
+                           (display "ERROR: Bad hash table: ")
+                           (newline)
+                           (write x)
+                           (newline))))
+    
+    ; Internal operations.
+    
+    (define (make-ht hashfun searcher size)
+      (vector doc 0 hashfun searcher (make-vector size '())))
+    
+    ; Substitute x for the first occurrence of y within the list z.
+    ; y is known to occur within z.
+    
+    (define (substitute1 x y z)
+      (cond ((eq? y (car z))
+             (cons x (cdr z)))
+            (else
+             (cons (car z)
+                   (substitute1 x y (cdr z))))))
+    
+    ; Remove the first occurrence of x from y.
+    ; x is known to occur within y.
+    
+    (define (remq1 x y)
+      (cond ((eq? x (car y))
+             (cdr y))
+            (else
+             (cons (car y)
+                   (remq1 x (cdr y))))))
+    
+    (define (resize ht0)
+      (call-without-interrupts
+       (lambda ()
+         (let ((ht (make-ht (hasher ht0)
+                            (searcher ht0)
+                            (+ 1 (* 2 (count ht0))))))
+           (ht-for-each (lambda (key val)
+                          (put! ht key val))
+                        ht0)
+           (buckets! ht0 (buckets ht))))))
+    
+    ; Returns the contents of the hashtable as a vector of pairs.
+    
+    (define (contents ht)
+      (let* ((v (buckets ht))
+             (n (vector-length v))
+             (z (make-vector (count ht) '())))
+        (define (loop i bucket j)
+          (if (null? bucket)
+              (if (= i n)
+                  (if (= j (vector-length z))
+                      z
+                      (begin (display "BUG in hashtable")
+                             (newline)
+                             '#()))
+                  (loop (+ i 1)
+                        (vector-ref v i)
+                        j))
+              (let ((entry (car bucket)))
+                (vector-set! z j (cons (car entry) (cdr entry)))
+                (loop i
+                      (cdr bucket)
+                      (+ j 1)))))
+        (loop 0 '() 0)))
+    
+    (define (contains? ht key)
+      (if (hashtable? ht)
+          (let* ((v (buckets ht))
+                 (n (vector-length v))
+                 (h (modulo ((hasher ht) key) n))
+                 (b (vector-ref v h)))
+            (if ((searcher ht) key b)
+                #t
+                #f))
+          (hashtable-error ht)))
+    
+    (define (fetch ht key flag)
+      (if (hashtable? ht)
+          (let* ((v (buckets ht))
+                 (n (vector-length v))
+                 (h (modulo ((hasher ht) key) n))
+                 (b (vector-ref v h))
+                 (probe ((searcher ht) key b)))
+            (if probe
+                (cdr probe)
+                flag))
+          (hashtable-error ht)))
+    
+    (define (put! ht key val)
+      (if (hashtable? ht)
+          (call-without-interrupts
+           (lambda ()
+             (let* ((v (buckets ht))
+                    (n (vector-length v))
+                    (h (modulo ((hasher ht) key) n))
+                    (b (vector-ref v h))
+                    (probe ((searcher ht) key b)))
+               (if probe
+                   ; Using SET-CDR! on the probe would make it necessary
+                   ; to synchronize the CONTENTS routine.
+                   (vector-set! v h (substitute1 (cons key val) probe b))
+                   (begin (count! ht (+ (count ht) 1))
+                          (vector-set! v h (cons (cons key val) b))
+                          (if (> (count ht) n)
+                              (resize ht)))))
+             #f))
+          (hashtable-error ht)))
+    
+    (define (remove! ht key)
+      (if (hashtable? ht)
+          (call-without-interrupts
+           (lambda ()
+             (let* ((v (buckets ht))
+                    (n (vector-length v))
+                    (h (modulo ((hasher ht) key) n))
+                    (b (vector-ref v h))
+                    (probe ((searcher ht) key b)))
+               (if probe
+                   (begin (count! ht (- (count ht) 1))
+                          (vector-set! v h (remq1 probe b))
+                          (if (< (* 2 (+ defaultn (count ht))) n)
+                              (resize ht))))
+               #f)))
+          (hashtable-error ht)))
+    
+    (define (clear! ht)
+      (if (hashtable? ht)
+          (call-without-interrupts
+           (lambda ()
+             (begin (count! ht 0)
+                    (buckets! ht (make-vector defaultn '()))
+                    #f)))
+          (hashtable-error ht)))
+    
+    (define (size ht)
+      (if (hashtable? ht)
+          (count ht)
+          (hashtable-error ht)))
+    
+    ; This code must be written so that the procedure can modify the
+    ; hashtable without breaking any invariants.
+    
+    (define (ht-for-each f ht)
+      (if (hashtable? ht)
+          (let* ((v (contents ht))
+                 (n (vector-length v)))
+            (do ((j 0 (+ j 1)))
+                ((= j n))
+                (let ((x (vector-ref v j)))
+                  (f (car x) (cdr x)))))
+          (hashtable-error ht)))
+    
+    (define (ht-map f ht)
+      (if (hashtable? ht)
+          (let* ((v (contents ht))
+                 (n (vector-length v)))
+            (do ((j 0 (+ j 1))
+                 (results '() (let ((x (vector-ref v j)))
+                                (cons (f (car x) (cdr x))
+                                      results))))
+                ((= j n)
+                 (reverse results))))
+          (hashtable-error ht)))
+    
+    (define (ht-copy ht)
+      (if (hashtable? ht)
+          (let* ((newtable (make-hashtable (hasher ht) (searcher ht) 0))
+                 (v (buckets ht))
+                 (n (vector-length v))
+                 (newvector (make-vector n '())))
+            (count! newtable (count ht))
+            (buckets! newtable newvector)
+            (do ((i 0 (+ i 1)))
+                ((= i n))
+                (vector-set! newvector i (append (vector-ref v i) '())))
+            newtable)
+          (hashtable-error ht)))
+    
+    ; External entry points.
+    
+    (set! make-hashtable
+          (lambda args
+            (let* ((hashfun (if (null? args) object-hash (car args)))
+                   (searcher (if (or (null? args) (null? (cdr args)))
+                                 assv
+                                 (cadr args)))
+                   (size (if (or (null? args) (null? (cdr args)) (null? (cddr args)))
+                             defaultn
+                             (caddr args))))
+              (make-ht hashfun searcher size))))
+    
+    (set! hashtable-contains? (lambda (ht key)      (contains? ht key)))
+    (set! hashtable-fetch     (lambda (ht key flag) (fetch ht key flag)))
+    (set! hashtable-get       (lambda (ht key)      (fetch ht key #f)))
+    (set! hashtable-put!      (lambda (ht key val)  (put! ht key val)))
+    (set! hashtable-remove!   (lambda (ht key)      (remove! ht key)))
+    (set! hashtable-clear!    (lambda (ht)          (clear! ht)))
+    (set! hashtable-size      (lambda (ht)          (size ht)))
+    (set! hashtable-for-each  (lambda (ht proc)     (ht-for-each ht proc)))
+    (set! hashtable-map       (lambda (ht proc)     (ht-map ht proc)))
+    (set! hashtable-copy      (lambda (ht)          (ht-copy ht)))
+    #f))
+; Hash trees: a functional data structure analogous to hash tables.
+;
+; (make-hashtree <hash-function> <bucket-searcher>)
+;
+;     Returns a newly allocated mutable hash table
+;     using <hash-function> as the hash function
+;     and <bucket-searcher>, e.g. ASSQ, ASSV, ASSOC, to search a bucket.
+;     The <hash-function> must accept a key and return a non-negative exact
+;     integer.
+;
+; (make-hashtree <hash-function>)
+;
+;     Equivalent to (make-hashtree <hash-function> assv).
+;
+; (make-hashtree)
+;
+;     Equivalent to (make-hashtree object-hash assv).
+;
+; (hashtree-contains? <hashtree> <key>)
+;
+;     Returns true iff the <hashtree> contains an entry for <key>.
+;
+; (hashtree-fetch <hashtree> <key> <flag>)
+;
+;     Returns the value associated with <key> in the <hashtree> if the
+;     <hashtree> contains <key>; otherwise returns <flag>.
+;
+; (hashtree-get <hashtree> <key>)
+;
+;     Equivalent to (hashtree-fetch <hashtree> <key> #f)
+;
+; (hashtree-put <hashtree> <key> <value>)
+;
+;     Returns a new hashtree that is like <hashtree> except that
+;     <key> is associated with <value>.
+;
+; (hashtree-remove <hashtree> <key>)
+;
+;     Returns a new hashtree that is like <hashtree> except that
+;     <key> is not associated with any value.
+;
+; (hashtree-size <hashtree>)
+;
+;     Returns the number of keys contained within the <hashtree>.
+;
+; (hashtree-for-each <procedure> <hashtree>)
+;
+;     The <procedure> must accept two arguments, a key and the value
+;     associated with that key.  Calls the <procedure> once for each
+;     key-value association.  The order of these calls is indeterminate.
+;
+; (hashtree-map <procedure> <hashtree>)
+;
+;     The <procedure> must accept two arguments, a key and the value
+;     associated with that key.  Calls the <procedure> once for each
+;     key-value association, and returns a list of the results.  The
+;     order of the calls is indeterminate.
+
+; These global variables are assigned new values later.
+
+(define make-hashtree      (lambda args '*))
+(define hashtree-contains? (lambda (ht key) #f))
+(define hashtree-fetch     (lambda (ht key flag) flag))
+(define hashtree-get       (lambda (ht key) (hashtree-fetch ht key #f)))
+(define hashtree-put       (lambda (ht key val) '*))
+(define hashtree-remove    (lambda (ht key) '*))
+(define hashtree-size      (lambda (ht) 0))
+(define hashtree-for-each  (lambda (ht proc) '*))
+(define hashtree-map       (lambda (ht proc) '()))
+
+; Implementation.
+; A hashtree is represented as a vector of the form
+;
+;     #(("hashtree") <count> <hasher> <searcher> <buckets>)
+;
+; where <count> is the number of associations within the hashtree,
+; <hasher> is the hash function, <searcher> is the bucket searcher,
+; and <buckets> is generated by the following grammar:
+;
+; <buckets>       ::=  ()
+;                   |  (<fixnum> <associations> <buckets> <buckets>)
+; <alist>         ::=  (<associations>)
+; <associations>  ::=  
+;                   |  <association> <associations>
+; <association>   ::=  (<key> . <value>)
+;
+; If <buckets> is of the form (n alist buckets1 buckets2),
+; then n is the hash code of all keys in alist, all keys in buckets1
+; have a hash code less than n, and all keys in buckets2 have a hash
+; code greater than n.
+
+(let ((doc      (list "hashtree"))
+      (count    (lambda (ht)   (vector-ref ht 1)))
+      (hasher   (lambda (ht)   (vector-ref ht 2)))
+      (searcher (lambda (ht)   (vector-ref ht 3)))
+      (buckets  (lambda (ht)   (vector-ref ht 4)))
+      
+      (make-empty-buckets (lambda () '()))
+      
+      (make-buckets
+       (lambda (h alist buckets1 buckets2)
+         (list h alist buckets1 buckets2)))
+      
+      (buckets-empty? (lambda (buckets) (null? buckets)))
+      
+      (buckets-n      (lambda (buckets) (car buckets)))
+      (buckets-alist  (lambda (buckets) (cadr buckets)))
+      (buckets-left   (lambda (buckets) (caddr buckets)))
+      (buckets-right  (lambda (buckets) (cadddr buckets))))
+  
+  (let ((hashtree? (lambda (ht)
+                      (and (vector? ht)
+                           (= 5 (vector-length ht))
+                           (eq? doc (vector-ref ht 0)))))
+        (hashtree-error (lambda (x)
+                           (display "ERROR: Bad hash tree: ")
+                           (newline)
+                           (write x)
+                           (newline))))
+    
+    ; Internal operations.
+    
+    (define (make-ht count hashfun searcher buckets)
+      (vector doc count hashfun searcher buckets))
+    
+    ; Substitute x for the first occurrence of y within the list z.
+    ; y is known to occur within z.
+    
+    (define (substitute1 x y z)
+      (cond ((eq? y (car z))
+             (cons x (cdr z)))
+            (else
+             (cons (car z)
+                   (substitute1 x y (cdr z))))))
+    
+    ; Remove the first occurrence of x from y.
+    ; x is known to occur within y.
+    
+    (define (remq1 x y)
+      (cond ((eq? x (car y))
+             (cdr y))
+            (else
+             (cons (car y)
+                   (remq1 x (cdr y))))))
+    
+    ; Returns the contents of the hashtree as a list of pairs.
+    
+    (define (contents ht)
+      (let* ((t (buckets ht)))
+        
+        (define (contents t alist)
+          (if (buckets-empty? t)
+              alist
+              (contents (buckets-left t)
+                        (contents (buckets-right t)
+                                  (append-reverse (buckets-alist t)
+                                                  alist)))))
+        
+        (define (append-reverse x y)
+          (if (null? x)
+              y
+              (append-reverse (cdr x)
+                              (cons (car x) y))))
+        
+        ; Creating a new hashtree from a list that is almost sorted
+        ; in hash code order would create an extremely unbalanced
+        ; hashtree, so this routine randomizes the order a bit.
+        
+        (define (randomize1 alist alist1 alist2 alist3)
+          (if (null? alist)
+              (randomize-combine alist1 alist2 alist3)
+              (randomize2 (cdr alist)
+                          (cons (car alist) alist1)
+                          alist2
+                          alist3)))
+        
+        (define (randomize2 alist alist1 alist2 alist3)
+          (if (null? alist)
+              (randomize-combine alist1 alist2 alist3)
+              (randomize3 (cdr alist)
+                          alist1
+                          (cons (car alist) alist2)
+                          alist3)))
+        
+        (define (randomize3 alist alist1 alist2 alist3)
+          (if (null? alist)
+              (randomize-combine alist1 alist2 alist3)
+              (randomize1 (cdr alist)
+                          alist1
+                          alist2
+                          (cons (car alist) alist3))))
+        
+        (define (randomize-combine alist1 alist2 alist3)
+          (cond ((null? alist2)
+                 alist1)
+                ((null? alist3)
+                 (append-reverse alist2 alist1))
+                (else
+                 (append-reverse
+                  (randomize1 alist3 '() '() '())
+                  (append-reverse
+                   (randomize1 alist1 '() '() '())
+                   (randomize1 alist2 '() '() '()))))))
+        
+        (randomize1 (contents t '()) '() '() '())))
+    
+    (define (contains? ht key)
+      (if (hashtree? ht)
+          (let* ((t (buckets ht))
+                 (h ((hasher ht) key)))
+            (if ((searcher ht) key (find-bucket t h))
+                #t
+                #f))
+          (hashtree-error ht)))
+    
+    (define (fetch ht key flag)
+      (if (hashtree? ht)
+          (let* ((t (buckets ht))
+                 (h ((hasher ht) key))
+                 (probe ((searcher ht) key (find-bucket t h))))
+            (if probe
+                (cdr probe)
+                flag))
+          (hashtree-error ht)))
+    
+    ; Given a <buckets> t and a hash code h, returns the alist for h.
+    
+    (define (find-bucket t h)
+      (if (buckets-empty? t)
+          '()
+          (let ((n (buckets-n t)))
+            (cond ((< h n)
+                   (find-bucket (buckets-left t) h))
+                  ((< n h)
+                   (find-bucket (buckets-right t) h))
+                  (else
+                   (buckets-alist t))))))
+    
+    (define (put ht key val)
+      (if (hashtree? ht)
+          (let ((t (buckets ht))
+                (h ((hasher ht) key))
+                (association (cons key val))
+                (c (count ht)))
+            (define (put t h)
+              (if (buckets-empty? t)
+                  (begin (set! c (+ c 1))
+                         (make-buckets h (list association) t t))
+                  (let ((n     (buckets-n t))
+                        (alist (buckets-alist t))
+                        (left  (buckets-left t))
+                        (right (buckets-right t)))
+                    (cond ((< h n)
+                           (make-buckets n
+                                         alist
+                                         (put (buckets-left t) h)
+                                         right))
+                          ((< n h)
+                           (make-buckets n
+                                         alist
+                                         left
+                                         (put (buckets-right t) h)))
+                          (else
+                           (let ((probe ((searcher ht) key alist)))
+                             (if probe
+                                 (make-buckets n
+                                               (substitute1 association
+                                                            probe
+                                                            alist)
+                                               left
+                                               right)
+                                 (begin
+                                  (set! c (+ c 1))
+                                  (make-buckets n
+                                                (cons association alist)
+                                                left
+                                                right)))))))))
+            (let ((buckets (put t h)))
+              (make-ht c (hasher ht) (searcher ht) buckets)))
+          (hashtree-error ht)))
+    
+    (define (remove ht key)
+      (if (hashtree? ht)
+          (let ((t (buckets ht))
+                (h ((hasher ht) key))
+                (c (count ht)))
+            (define (remove t h)
+              (if (buckets-empty? t)
+                  t
+                  (let ((n     (buckets-n t))
+                        (alist (buckets-alist t))
+                        (left  (buckets-left t))
+                        (right (buckets-right t)))
+                    (cond ((< h n)
+                           (make-buckets n
+                                         alist
+                                         (remove left h)
+                                         right))
+                          ((< n h)
+                           (make-buckets n
+                                         alist
+                                         left
+                                         (remove right h)))
+                          (else
+                           (let ((probe ((searcher ht) key alist)))
+                             (if probe
+                                 (begin (set! c (- c 1))
+                                        (make-buckets n
+                                                      (remq1 probe alist)
+                                                      left
+                                                      right))
+                                 t)))))))
+            (let ((buckets (remove t h)))
+              (make-ht c (hasher ht) (searcher ht) buckets)))
+          (hashtree-error ht)))
+    
+    (define (size ht)
+      (if (hashtree? ht)
+          (count ht)
+          (hashtree-error ht)))
+    
+    (define (ht-for-each f ht)
+      (if (hashtree? ht)
+          (for-each (lambda (association)
+                      (f (car association)
+                         (cdr association)))
+                    (contents ht))
+          (hashtree-error ht)))
+    
+    (define (ht-map f ht)
+      (if (hashtree? ht)
+          (map (lambda (association)
+                 (f (car association)
+                    (cdr association)))
+               (contents ht))
+          (hashtree-error ht)))
+    
+    ; External entry points.
+    
+    (set! make-hashtree
+          (lambda args
+            (let* ((hashfun (if (null? args) object-hash (car args)))
+                   (searcher (if (or (null? args) (null? (cdr args)))
+                                 assv
+                                 (cadr args))))
+              (make-ht 0 hashfun searcher (make-empty-buckets)))))
+    
+    (set! hashtree-contains? (lambda (ht key)      (contains? ht key)))
+    (set! hashtree-fetch     (lambda (ht key flag) (fetch ht key flag)))
+    (set! hashtree-get       (lambda (ht key)      (fetch ht key #f)))
+    (set! hashtree-put       (lambda (ht key val)  (put ht key val)))
+    (set! hashtree-remove    (lambda (ht key)      (remove ht key)))
+    (set! hashtree-size      (lambda (ht)          (size ht)))
+    (set! hashtree-for-each  (lambda (ht proc)     (ht-for-each ht proc)))
+    (set! hashtree-map       (lambda (ht proc)     (ht-map ht proc)))
+    #f))
+; Copyright 1994 William Clinger
+;
+; $Id: twobit.sch,v 1.3 1999/08/23 19:14:26 lth Exp $
+;
+; 24 April 1999
+;
+; Compiler switches needed by Twobit.
+
+(define make-twobit-flag)
+(define display-twobit-flag)
+
+(define make-twobit-flag
+  (lambda (name)
+
+    (define (twobit-warning)
+      (display "Error: incorrect arguments to ")
+      (write name)
+      (newline)
+      (reset))
+
+    (define (display-flag state)
+      (display (if state "  + " "  - "))
+      (display name)
+      (display " is ")
+      (display (if state "on" "off"))
+      (newline))
+
+    (let ((state #t))
+      (lambda args
+        (cond ((null? args) state)
+              ((and (null? (cdr args))
+                    (boolean? (car args)))
+               (set! state (car args))
+               state)
+              ((and (null? (cdr args))
+                    (eq? (car args) 'display))
+               (display-flag state))
+              (else (twobit-warning)))))))
+
+(define (display-twobit-flag flag)
+  (flag 'display))
+  
+; Debugging and convenience.
+
+(define issue-warnings
+  (make-twobit-flag 'issue-warnings))
+
+(define include-source-code
+  (make-twobit-flag 'include-source-code))
+
+(define include-variable-names
+  (make-twobit-flag 'include-variable-names))
+
+(define include-procedure-names
+  (make-twobit-flag 'include-procedure-names))
+
+; Space efficiency.
+; This switch isn't fully implemented yet.  If it is true, then
+; Twobit will generate flat closures and will go to some trouble
+; to zero stale registers and stack slots.
+; Don't turn this switch off unless space is more important than speed.
+
+(define avoid-space-leaks
+  (make-twobit-flag 'avoid-space-leaks))
+
+; Major optimizations.
+
+(define integrate-usual-procedures
+  (make-twobit-flag 'integrate-usual-procedures))
+
+(define control-optimization
+  (make-twobit-flag 'control-optimization))
+
+(define parallel-assignment-optimization
+  (make-twobit-flag 'parallel-assignment-optimization))
+
+(define lambda-optimization
+  (make-twobit-flag 'lambda-optimization))
+
+(define benchmark-mode
+  (make-twobit-flag 'benchmark-mode))
+
+(define benchmark-block-mode
+  (make-twobit-flag 'benchmark-block-mode))
+
+(define global-optimization
+  (make-twobit-flag 'global-optimization))
+
+(define interprocedural-inlining
+  (make-twobit-flag 'interprocedural-inlining))
+
+(define interprocedural-constant-propagation
+  (make-twobit-flag 'interprocedural-constant-propagation))
+
+(define common-subexpression-elimination
+  (make-twobit-flag 'common-subexpression-elimination))
+
+(define representation-inference
+  (make-twobit-flag 'representation-inference))
+
+(define local-optimization
+  (make-twobit-flag 'local-optimization))
+
+; For backwards compatibility, until I can change the code.
+
+(define (ignore-space-leaks . args)
+  (if (null? args)
+      (not (avoid-space-leaks))
+      (avoid-space-leaks (not (car args)))))
+
+(define lambda-optimizations lambda-optimization)
+(define local-optimizations local-optimization)
+
+(define (set-compiler-flags! how)
+  (case how
+    ((no-optimization)
+     (set-compiler-flags! 'standard)
+     (avoid-space-leaks #t)
+     (integrate-usual-procedures #f)
+     (control-optimization #f)
+     (parallel-assignment-optimization #f)
+     (lambda-optimization #f)
+     (benchmark-mode #f)
+     (benchmark-block-mode #f)
+     (global-optimization #f)
+     (interprocedural-inlining #f)
+     (interprocedural-constant-propagation #f)
+     (common-subexpression-elimination #f)
+     (representation-inference #f)
+     (local-optimization #f))
+    ((standard) 
+     (issue-warnings #t)
+     (include-source-code #f)
+     (include-procedure-names #t)
+     (include-variable-names #t)
+     (avoid-space-leaks #f)
+     (runtime-safety-checking #t)
+     (integrate-usual-procedures #f)
+     (control-optimization #t)
+     (parallel-assignment-optimization #t)
+     (lambda-optimization #t)
+     (benchmark-mode #f)
+     (benchmark-block-mode #f)
+     (global-optimization #t)
+     (interprocedural-inlining #t)
+     (interprocedural-constant-propagation #t)
+     (common-subexpression-elimination #t)
+     (representation-inference #t)
+     (local-optimization #t))
+    ((fast-safe)
+     (let ((bbmode (benchmark-block-mode)))
+       (set-compiler-flags! 'standard)
+       (integrate-usual-procedures #t)
+       (benchmark-mode #t)
+       (benchmark-block-mode bbmode)))
+    ((fast-unsafe) 
+     (set-compiler-flags! 'fast-safe)
+     (runtime-safety-checking #f))
+    (else 
+     (error "set-compiler-flags!: unknown mode " how))))
+
+(define (display-twobit-flags which)
+  (case which
+    ((debugging)
+     (display-twobit-flag issue-warnings)
+     (display-twobit-flag include-procedure-names)
+     (display-twobit-flag include-variable-names)
+     (display-twobit-flag include-source-code))
+    ((safety)
+     (display-twobit-flag avoid-space-leaks))
+    ((optimization)
+     (display-twobit-flag integrate-usual-procedures)
+     (display-twobit-flag control-optimization)
+     (display-twobit-flag parallel-assignment-optimization)
+     (display-twobit-flag lambda-optimization)
+     (display-twobit-flag benchmark-mode)
+     (display-twobit-flag benchmark-block-mode)
+     (display-twobit-flag global-optimization)
+     (if (global-optimization)
+         (begin (display "  ")
+                (display-twobit-flag interprocedural-inlining)
+                (display "  ")
+                (display-twobit-flag interprocedural-constant-propagation)
+                (display "  ")
+                (display-twobit-flag common-subexpression-elimination)
+                (display "  ")
+                (display-twobit-flag representation-inference)))
+     (display-twobit-flag local-optimization))
+    (else
+     ; The switch might mean something to the assembler, but not to Twobit
+     #t)))
+
+; eof
+; Copyright 1991 William Clinger
+;
+; $Id: twobit.sch,v 1.3 1999/08/23 19:14:26 lth Exp $
+;
+; 14 April 1999 / wdc
+
+($$trace "pass1.aux")
+
+;***************************************************************
+;
+; Each definition in this section should be overridden by an assignment
+; in a target-specific file.
+;
+; If a lambda expression has more than @maxargs-with-rest-arg@ required
+; arguments followed by a rest argument, then the macro expander will
+; rewrite the lambda expression as a lambda expression with only one
+; argument (a rest argument) whose body is a LET that binds the arguments
+; of the original lambda expression.
+
+(define @maxargs-with-rest-arg@
+  1000000)                              ; infinity
+
+(define (prim-entry name) #f)           ; no integrable procedures
+(define (prim-arity name) 0)            ; all of which take 0 arguments
+(define (prim-opcodename name) name)    ; and go by their source names
+
+; End of definitions to be overridden by target-specific assignments.
+;
+;***************************************************************
+
+; Miscellaneous routines.
+
+(define (m-warn msg . more)
+  (if (issue-warnings)
+      (begin
+       (display "WARNING from macro expander:")
+       (newline)
+       (display msg)
+       (newline)
+       (for-each (lambda (x) (write x) (newline))
+                 more))))
+
+(define (m-error msg . more)
+  (display "ERROR detected during macro expansion:")
+  (newline)
+  (display msg)
+  (newline)
+  (for-each (lambda (x) (write x) (newline))
+            more)
+  (m-quit (make-constant #f)))
+
+(define (m-bug msg . more)
+  (display "BUG in macro expander: ")
+  (newline)
+  (display msg)
+  (newline)
+  (for-each (lambda (x) (write x) (newline))
+            more)
+  (m-quit (make-constant #f)))
+
+; Given a <formals>, returns a list of bound variables.
+
+'
+(define (make-null-terminated x)
+  (cond ((null? x) '())
+        ((pair? x)
+         (cons (car x) (make-null-terminated (cdr x))))
+        (else (list x))))
+
+; Returns the length of the given list, or -1 if the argument
+; is not a list.  Does not check for circular lists.
+
+(define (safe-length x)
+  (define (loop x n)
+    (cond ((null? x) n)
+          ((pair? x) (loop (cdr x) (+ n 1)))
+          (else -1)))
+  (loop x 0))
+
+; Given a unary predicate and a list, returns a list of those
+; elements for which the predicate is true.
+
+(define (filter1 p x)
+  (cond ((null? x) '())
+        ((p (car x)) (cons (car x) (filter1 p (cdr x))))
+        (else (filter1 p (cdr x)))))
+
+; Given a unary predicate and a list, returns #t if the
+; predicate is true of every element of the list.
+
+(define (every1? p x)
+  (cond ((null? x) #t)
+        ((p (car x)) (every1? p (cdr x)))
+        (else #f)))
+
+; Binary union of two sets represented as lists, using equal?.
+
+(define (union2 x y)
+  (cond ((null? x) y)
+        ((member (car x) y)
+         (union2 (cdr x) y))
+        (else (union2 (cdr x) (cons (car x) y)))))
+
+; Given an association list, copies the association pairs.
+
+(define (copy-alist alist)
+  (map (lambda (x) (cons (car x) (cdr x)))
+       alist))
+
+; Removes a value from a list.  May destroy the list.
+
+'
+(define remq!
+  (letrec ((loop (lambda (x y prev)
+                   (cond ((null? y) #t)
+                         ((eq? x (car y))
+                          (set-cdr! prev (cdr y))
+                          (loop x (cdr prev) prev))
+                         (else
+                          (loop x (cdr y) y))))))
+    (lambda (x y)
+      (cond ((null? y) '())
+            ((eq? x (car y))
+             (remq! x (cdr y)))
+            (else
+             (loop x (cdr y) y)
+             y)))))
+
+; Procedure-specific source code transformations.
+; The transformer is passed a source code expression and a predicate
+; and returns one of:
+;
+;    the original source code expression
+;    a new source code expression to use in place of the original
+;    #f to indicate that the procedure is being called
+;      with an incorrect number of arguments or
+;      with an incorrect operand
+;
+; The original source code expression is guaranteed to be a list whose
+; car is the name associated with the transformer.
+; The predicate takes an identifier (a symbol) and returns true iff
+; that identifier is bound to something other than its global binding.
+;
+; Since the procedures and their transformations are target-specific,
+; they are defined in another file, in the Target subdirectory.
+
+; FIXME:
+; I think this is now used in only one place, in simplify-if.
+
+(define (integrable? name)
+  (and (integrate-usual-procedures)
+       (prim-entry name)))
+
+; MAKE-READABLE strips the referencing information
+; and replaces (begin I) by I.
+; If the optional argument is true, then it also reconstructs LET.
+
+(define (make-readable exp . rest)
+  (let ((fancy? (and (not (null? rest))
+                     (car rest))))
+    (define (make-readable exp)
+      (case (car exp)
+        ((quote)    (make-readable-quote exp))
+        ((lambda)   `(lambda ,(lambda.args exp)
+                             ,@(map (lambda (def)
+                                      `(define ,(def.lhs def)
+                                               ,(make-readable (def.rhs def))))
+                                    (lambda.defs exp))
+                               ,(make-readable (lambda.body exp))))
+        ((set!)     `(set! ,(assignment.lhs exp)
+                           ,(make-readable (assignment.rhs exp))))
+        ((if)       `(if ,(make-readable (if.test exp))
+                         ,(make-readable (if.then exp))
+                         ,(make-readable (if.else exp))))
+        ((begin)    (if (variable? exp)
+                        (variable.name exp)
+                        `(begin ,@(map make-readable (begin.exprs exp)))))
+        (else       (make-readable-call exp))))
+    (define (make-readable-quote exp)
+      (let ((x (constant.value exp)))
+        (if (and fancy?
+                 (or (boolean? x)
+                     (number? x)
+                     (char? x)
+                     (string? x)))
+            x
+            exp)))
+    (define (make-readable-call exp)
+      (let ((proc (call.proc exp)))
+        (if (and fancy?
+                 (lambda? proc)
+                 (list? (lambda.args proc)))
+            ;(make-readable-let* exp '() '() '())
+            (make-readable-let exp)
+            `(,(make-readable (call.proc exp))
+              ,@(map make-readable (call.args exp))))))
+    (define (make-readable-let exp)
+      (let* ((L (call.proc exp))
+             (formals (lambda.args L))
+             (args (map make-readable (call.args exp)))
+             (body (make-readable (lambda.body L))))
+        (if (and (null? (lambda.defs L))
+                 (= (length args) 1)
+                 (pair? body)
+                 (or (and (eq? (car body) 'let)
+                          (= (length (cadr body)) 1))
+                     (eq? (car body) 'let*)))
+            `(let* ((,(car formals) ,(car args))
+                    ,@(cadr body))
+                   ,@(cddr body))
+            `(let ,(map list
+                        (lambda.args L)
+                        args)
+                  ,@(map (lambda (def)
+                           `(define ,(def.lhs def)
+                                    ,(make-readable (def.rhs def))))
+                         (lambda.defs L))
+                    ,body))))
+    (define (make-readable-let* exp vars inits defs)
+      (if (and (null? defs)
+               (call? exp)
+               (lambda? (call.proc exp))
+               (= 1 (length (lambda.args (call.proc exp)))))
+          (let ((proc (call.proc exp))
+                (arg (car (call.args exp))))
+            (if (and (call? arg)
+                     (lambda? (call.proc arg))
+                     (= 1 (length (lambda.args (call.proc arg))))
+                     (null? (lambda.defs (call.proc arg))))
+                (make-readable-let*
+                 (make-call proc (list (lambda.body (call.proc arg))))
+                 (cons (car (lambda.args (call.proc arg))) vars)
+                 (cons (make-readable (car (call.args arg))) inits)
+                 '())
+                (make-readable-let* (lambda.body proc)
+                                    (cons (car (lambda.args proc)) vars)
+                                    (cons (make-readable (car (call.args exp)))
+                                          inits)
+                                    (map (lambda (def)
+                                           `(define ,(def.lhs def)
+                                                    ,(make-readable (def.rhs def))))
+                                         (reverse (lambda.defs proc))))))
+          (cond ((or (not (null? vars))
+                     (not (null? defs)))
+                 `(let* ,(map list
+                              (reverse vars)
+                              (reverse inits))
+                        ,@defs
+                         ,(make-readable exp)))
+                ((and (call? exp)
+                      (lambda? (call.proc exp)))
+                 (let ((proc (call.proc exp)))
+                   `(let ,(map list
+                               (lambda.args proc)
+                               (map make-readable (call.args exp)))
+                         ,@(map (lambda (def)
+                                  `(define ,(def.lhs def)
+                                           ,(make-readable (def.rhs def))))
+                                (lambda.defs proc))
+                          ,(make-readable (lambda.body proc)))))
+                (else
+                 (make-readable exp)))))
+    (make-readable exp)))
+
+; For testing.
+
+; MAKE-UNREADABLE does the reverse.
+; It assumes there are no internal definitions.
+
+(define (make-unreadable exp)
+  (cond ((symbol? exp) (list 'begin exp))
+        ((pair? exp)
+         (case (car exp)
+           ((quote) exp)
+           ((lambda) (list 'lambda
+                           (cadr exp)
+                           '(begin)
+                           (list '() '() '() '())
+                           (make-unreadable (cons 'begin (cddr exp)))))
+           ((set!) (list 'set! (cadr exp) (make-unreadable (caddr exp))))
+           ((if) (list 'if
+                       (make-unreadable (cadr exp))
+                       (make-unreadable (caddr exp))
+                       (if (= (length exp) 3)
+                           '(unspecified)
+                           (make-unreadable (cadddr exp)))))
+           ((begin) (if (= (length exp) 2)
+                        (make-unreadable (cadr exp))
+                        (cons 'begin (map make-unreadable (cdr exp)))))
+           (else (map make-unreadable exp))))
+        (else (list 'quote exp))))
+; Copyright 1991 William D Clinger.
+;
+; $Id: twobit.sch,v 1.3 1999/08/23 19:14:26 lth Exp $
+;
+; 12 April 1999.
+;
+; Procedures for fetching and clobbering parts of expressions.
+
+($$trace "pass2.aux")
+
+(define (constant? exp) (eq? (car exp) 'quote))
+(define (variable? exp)
+  (and (eq? (car exp) 'begin)
+       (null? (cddr exp))))
+(define (lambda? exp) (eq? (car exp) 'lambda))
+(define (call? exp) (pair? (car exp)))
+(define (assignment? exp) (eq? (car exp) 'set!))
+(define (conditional? exp) (eq? (car exp) 'if))
+(define (begin? exp)
+  (and (eq? (car exp) 'begin)
+       (not (null? (cddr exp)))))
+
+(define (make-constant value) (list 'quote value))
+(define (make-variable name) (list 'begin name))
+(define (make-lambda formals defs R F G decls doc body)
+  (list 'lambda
+        formals
+        (cons 'begin defs)
+        (list 'quote (list R F G decls doc))
+        body))
+(define (make-call proc args) (cons proc (append args '())))
+(define (make-assignment lhs rhs) (list 'set! lhs rhs))
+(define (make-conditional e0 e1 e2) (list 'if e0 e1 e2))
+(define (make-begin exprs)
+  (if (null? (cdr exprs))
+      (car exprs)
+      (cons 'begin (append exprs '()))))
+(define (make-definition lhs rhs) (list 'define lhs rhs))
+
+(define (constant.value exp) (cadr exp))
+(define (variable.name exp) (cadr exp))
+(define (lambda.args exp) (cadr exp))
+(define (lambda.defs exp) (cdr (caddr exp)))
+(define (lambda.R exp) (car (cadr (cadddr exp))))
+(define (lambda.F exp) (cadr (cadr (cadddr exp))))
+(define (lambda.G exp) (caddr (cadr (cadddr exp))))
+(define (lambda.decls exp) (cadddr (cadr (cadddr exp))))
+(define (lambda.doc exp) (car (cddddr (cadr (cadddr exp)))))
+(define (lambda.body exp) (car (cddddr exp)))
+(define (call.proc exp) (car exp))
+(define (call.args exp) (cdr exp))
+(define (assignment.lhs exp) (cadr exp))
+(define (assignment.rhs exp) (caddr exp))
+(define (if.test exp) (cadr exp))
+(define (if.then exp) (caddr exp))
+(define (if.else exp) (cadddr exp))
+(define (begin.exprs exp) (cdr exp))
+(define (def.lhs exp) (cadr exp))
+(define (def.rhs exp) (caddr exp))
+
+(define (variable-set! exp newexp)
+  (set-car! exp (car newexp))
+  (set-cdr! exp (append (cdr newexp) '())))
+(define (lambda.args-set! exp args) (set-car! (cdr exp) args))
+(define (lambda.defs-set! exp defs) (set-cdr! (caddr exp) defs))
+(define (lambda.R-set! exp R) (set-car! (cadr (cadddr exp)) R))
+(define (lambda.F-set! exp F) (set-car! (cdr (cadr (cadddr exp))) F))
+(define (lambda.G-set! exp G) (set-car! (cddr (cadr (cadddr exp))) G))
+(define (lambda.decls-set! exp decls) (set-car! (cdddr (cadr (cadddr exp))) decls))
+(define (lambda.doc-set! exp doc) (set-car! (cddddr (cadr (cadddr exp))) doc))
+(define (lambda.body-set! exp exp0) (set-car! (cddddr exp) exp0))
+(define (call.proc-set! exp exp0) (set-car! exp exp0))
+(define (call.args-set! exp exprs) (set-cdr! exp exprs))
+(define (assignment.rhs-set! exp exp0) (set-car! (cddr exp) exp0))
+(define (if.test-set! exp exp0) (set-car! (cdr exp) exp0))
+(define (if.then-set! exp exp0) (set-car! (cddr exp) exp0))
+(define (if.else-set! exp exp0) (set-car! (cdddr exp) exp0))
+(define (begin.exprs-set! exp exprs) (set-cdr! exp exprs))
+
+(define expression-set! variable-set!)  ; used only by pass 3
+
+; FIXME:  This duplicates information in Lib/procinfo.sch.
+
+(define (make-doc name arity formals source-code filename filepos)
+  (vector name source-code arity filename filepos formals))
+(define (doc.name d)    (vector-ref d 0))
+(define (doc.code d)    (vector-ref d 1))
+(define (doc.arity d)   (vector-ref d 2))
+(define (doc.file d)    (vector-ref d 3))
+(define (doc.filepos d) (vector-ref d 4))
+(define (doc.formals d) (vector-ref d 5))
+(define (doc.name-set! d x)    (if d (vector-set! d 0 x)))
+(define (doc.code-set! d x)    (if d (vector-set! d 1 x)))
+(define (doc.arity-set! d x)   (if d (vector-set! d 2 x)))
+(define (doc.file-set! d x)    (if d (vector-set! d 3 x)))
+(define (doc.filepos-set! d x) (if d (vector-set! d 4 x)))
+(define (doc.formals-set! d x) (if d (vector-set! d 5 x)))
+(define (doc-copy d) (list->vector (vector->list d)))
+
+(define (ignored? name) (eq? name name:IGNORED))
+
+; Fairly harmless bug: rest arguments aren't getting flagged.
+
+(define (flag-as-ignored name L)
+  (define (loop name formals)
+    (cond ((null? formals)
+           ;(pass2-error p2error:violation-of-invariant name formals)
+           #t)
+          ((symbol? formals) #t)
+          ((eq? name (car formals))
+           (set-car! formals name:IGNORED)
+           (if (not (local? (lambda.R L) name:IGNORED))
+               (lambda.R-set! L
+                 (cons (make-R-entry name:IGNORED '() '() '())
+                       (lambda.R L)))))
+          (else (loop name (cdr formals)))))
+  (loop name (lambda.args L)))
+
+(define (make-null-terminated formals)
+  (cond ((null? formals) '())
+        ((symbol? formals) (list formals))
+        (else (cons (car formals)
+                    (make-null-terminated (cdr formals))))))
+
+(define (list-head x n)
+  (cond ((zero? n) '())
+        (else (cons (car x) (list-head (cdr x) (- n 1))))))
+
+(define (remq x y)
+  (cond ((null? y) '())
+        ((eq? x (car y)) (remq x (cdr y)))
+        (else (cons (car y) (remq x (cdr y))))))
+
+(define (make-call-to-LIST args)
+  (cond ((null? args) (make-constant '()))
+        ((null? (cdr args))
+         (make-call (make-variable name:CONS)
+                    (list (car args) (make-constant '()))))
+        (else (make-call (make-variable name:LIST) args))))
+
+(define (pass2-error i . etc)
+  (apply cerror (cons (vector-ref pass2-error-messages i) etc)))
+
+(define pass2-error-messages
+  '#("System error: violation of an invariant in pass 2"
+     "Wrong number of arguments to known procedure"))
+
+(define p2error:violation-of-invariant 0)
+(define p2error:wna 1)
+
+; Procedures for fetching referencing information from R-tables.
+
+(define (make-R-entry name refs assigns calls)
+  (list name refs assigns calls))
+
+(define (R-entry.name x) (car x))
+(define (R-entry.references x) (cadr x))
+(define (R-entry.assignments x) (caddr x))
+(define (R-entry.calls x) (cadddr x))
+
+(define (R-entry.references-set! x refs) (set-car! (cdr x) refs))
+(define (R-entry.assignments-set! x assignments) (set-car! (cddr x) assignments))
+(define (R-entry.calls-set! x calls) (set-car! (cdddr x) calls))
+
+(define (local? R I)
+  (assq I R))
+
+(define (R-entry R I)
+  (assq I R))
+
+(define (R-lookup R I)
+  (or (assq I R)
+      (pass2-error p2error:violation-of-invariant R I)))
+
+(define (references R I)
+  (cadr (R-lookup R I)))
+
+(define (assignments R I)
+  (caddr (R-lookup R I)))
+
+(define (calls R I)
+  (cadddr (R-lookup R I)))
+
+(define (references-set! R I X)
+  (set-car! (cdr (R-lookup R I)) X))
+
+(define (assignments-set! R I X)
+  (set-car! (cddr (R-lookup R I)) X))
+
+(define (calls-set! R I X)
+  (set-car! (cdddr (R-lookup R I)) X))
+
+; A notepad is a vector of the form #(L0 (L1 ...) (L2 ...) (I ...)),
+; where the components are:
+;    element 0: a parent lambda expression (or #f if there is no enclosing
+;               parent, or we want to pretend that there isn't).
+;    element 1: a list of lambda expressions that the parent lambda
+;               expression encloses immediately.
+;    element 2: a subset of that list that does not escape.
+;    element 3: a list of free variables.
+
+(define (make-notepad L)
+  (vector L '() '() '()))
+
+(define (notepad.parent np)      (vector-ref np 0))
+(define (notepad.lambdas np)     (vector-ref np 1))
+(define (notepad.nonescaping np) (vector-ref np 2))
+(define (notepad.vars np)        (vector-ref np 3))
+
+(define (notepad.lambdas-set! np x)     (vector-set! np 1 x))
+(define (notepad.nonescaping-set! np x) (vector-set! np 2 x))
+(define (notepad.vars-set! np x)        (vector-set! np 3 x))
+
+(define (notepad-lambda-add! np L)
+  (notepad.lambdas-set! np (cons L (notepad.lambdas np))))
+
+(define (notepad-nonescaping-add! np L)
+  (notepad.nonescaping-set! np (cons L (notepad.nonescaping np))))
+
+(define (notepad-var-add! np I)
+  (let ((vars (notepad.vars np)))
+    (if (not (memq I vars))
+        (notepad.vars-set! np (cons I vars)))))
+
+; Given a notepad, returns the list of variables that are closed
+; over by some nested lambda expression that escapes.
+
+(define (notepad-captured-variables np)
+  (let ((nonescaping (notepad.nonescaping np)))
+    (apply-union
+     (map (lambda (L)
+            (if (memq L nonescaping)
+                (lambda.G L)
+                (lambda.F L)))
+          (notepad.lambdas np)))))
+
+; Given a notepad, returns a list of free variables computed
+; as the union of the immediate free variables with the free
+; variables of nested lambda expressions.
+
+(define (notepad-free-variables np)
+  (do ((lambdas (notepad.lambdas np) (cdr lambdas))
+       (fv (notepad.vars np)
+           (let ((L (car lambdas)))
+             (union (difference (lambda.F L)
+                                (make-null-terminated (lambda.args L)))
+                    fv))))
+      ((null? lambdas) fv)))
+; Copyright 1992 William Clinger
+;
+; $Id: twobit.sch,v 1.3 1999/08/23 19:14:26 lth Exp $
+;
+; 13 December 1998
+\f; Implementation-dependent parameters and preferences that determine
+; how identifiers are represented in the output of the macro expander.
+;
+; The basic problem is that there are no reserved words, so the
+; syntactic keywords of core Scheme that are used to express the
+; output need to be represented by data that cannot appear in the
+; input.  This file defines those data.
+
+($$trace "prefs")
+
+; FIXME: The following definitions are currently ignored.
+
+; The following definitions assume that identifiers of mixed case
+; cannot appear in the input.
+
+(define begin1  (string->symbol "Begin"))
+(define define1 (string->symbol "Define"))
+(define quote1  (string->symbol "Quote"))
+(define lambda1 (string->symbol "Lambda"))
+(define if1     (string->symbol "If"))
+(define set!1   (string->symbol "Set!"))
+
+; The following defines an implementation-dependent expression
+; that evaluates to an undefined (not unspecified!) value, for
+; use in expanding the (define x) syntax.
+
+(define undefined1 (list (string->symbol "Undefined")))
+
+; End of FIXME.
+
+; A variable is renamed by suffixing a vertical bar followed by a unique
+; integer.  In IEEE and R4RS Scheme, a vertical bar cannot appear as part
+; of an identifier, but presumably this is enforced by the reader and not
+; by the compiler.  Any other character that cannot appear as part of an
+; identifier may be used instead of the vertical bar.
+
+(define renaming-prefix-character #\.)
+(define renaming-suffix-character #\|)
+
+(define renaming-prefix (string renaming-prefix-character))
+(define renaming-suffix (string renaming-suffix-character))
+
+; Patches for Twobit.  Here temporarily.
+
+(define (make-toplevel-definition id exp)
+  (if (lambda? exp)
+      (doc.name-set! (lambda.doc exp) id))
+  (make-begin
+   (list (make-assignment id exp)
+         (make-constant id))))
+        
+(define (make-undefined)
+  (make-call (make-variable 'undefined) '()))
+
+(define (make-unspecified)
+  (make-call (make-variable 'unspecified) '()))
+; Copyright 1992 William Clinger
+;
+; $Id: twobit.sch,v 1.3 1999/08/23 19:14:26 lth Exp $
+;
+; 9 December 1998
+\f; Syntactic environments.
+;
+; A syntactic environment maps identifiers to denotations,
+; where a denotation is one of
+;
+;    (special <special>)
+;    (macro <rules> <env>)
+;    (inline <rules> <env>)
+;    (identifier <id> <references> <assignments> <calls>)
+;
+; and where <special> is one of
+;
+;    quote
+;    lambda
+;    if
+;    set!
+;    begin
+;    define
+;    define-syntax
+;    let-syntax
+;    letrec-syntax
+;    syntax-rules
+;
+; and where <rules> is a compiled <transformer spec> (see R4RS),
+; <env> is a syntactic environment, and <id> is an identifier.
+;
+; An inline denotation is like a macro denotation, except that it
+; is not an error when none of the rules match the use.  Inline
+; denotations are created by DEFINE-INLINE.
+; The standard syntactic environment should not include any
+; identifier denotations; space leaks will result if it does.
+
+($$trace "syntaxenv")
+
+(define standard-syntactic-environment
+  `((quote         . (special quote))
+    (lambda        . (special lambda))
+    (if            . (special if))
+    (set!          . (special set!))
+    (begin         . (special begin))
+    (define        . (special define))
+    (define-inline . (special define-inline))
+    (define-syntax . (special define-syntax))
+    (let-syntax    . (special let-syntax))
+    (letrec-syntax . (special letrec-syntax))
+    (syntax-rules  . (special syntax-rules))
+    ))
+
+; Unforgeable synonyms for lambda and set!, used to expand definitions.
+
+(define lambda0 (string->symbol " lambda "))
+(define set!0 (string->symbol " set! "))
+
+(define (syntactic-copy env)
+  (copy-alist env))
+
+(define (make-basic-syntactic-environment)
+  (cons (cons lambda0
+              (cdr (assq 'lambda standard-syntactic-environment)))
+        (cons (cons set!0
+                    (cdr (assq 'set! standard-syntactic-environment)))
+              (syntactic-copy standard-syntactic-environment))))
+
+; The global-syntactic-environment will always be a nonempty
+; association list since there is no way to remove the entry
+; for lambda0.  That entry is used as a header by destructive
+; operations.
+
+(define global-syntactic-environment
+  (make-basic-syntactic-environment))
+
+(define (global-syntactic-environment-set! env)
+  (set-cdr! global-syntactic-environment env)
+  #t)
+
+(define (syntactic-bind-globally! id denotation)
+  (if (and (identifier-denotation? denotation)
+           (eq? id (identifier-name denotation)))
+      (letrec ((remove-bindings-for-id
+                (lambda (bindings)
+                  (cond ((null? bindings) '())
+                        ((eq? (caar bindings) id)
+                         (remove-bindings-for-id (cdr bindings)))
+                        (else (cons (car bindings)
+                                    (remove-bindings-for-id (cdr bindings))))))))
+        (global-syntactic-environment-set!
+         (remove-bindings-for-id (cdr global-syntactic-environment))))
+      (let ((x (assq id global-syntactic-environment)))
+        (if x
+            (begin (set-cdr! x denotation) #t)
+            (global-syntactic-environment-set!
+             (cons (cons id denotation)
+                   (cdr global-syntactic-environment)))))))
+
+(define (syntactic-divert env1 env2)
+  (append env2 env1))
+
+(define (syntactic-extend env ids denotations)
+  (syntactic-divert env (map cons ids denotations)))
+
+(define (syntactic-lookup env id)
+  (let ((entry (assq id env)))
+    (if entry
+        (cdr entry)
+        (make-identifier-denotation id))))
+
+(define (syntactic-assign! env id denotation)
+  (let ((entry (assq id env)))
+    (if entry
+        (set-cdr! entry denotation)
+        (m-bug "Bug detected in syntactic-assign!" env id denotation))))
+
+; Denotations.
+
+(define denotation-class car)
+
+(define (special-denotation? denotation)
+  (eq? (denotation-class denotation) 'special))
+
+(define (macro-denotation? denotation)
+  (eq? (denotation-class denotation) 'macro))
+
+(define (inline-denotation? denotation)
+  (eq? (denotation-class denotation) 'inline))
+
+(define (identifier-denotation? denotation)
+  (eq? (denotation-class denotation) 'identifier))
+
+(define (make-macro-denotation rules env)
+  (list 'macro rules env))
+
+(define (make-inline-denotation id rules env)
+  (list 'inline rules env id))
+
+(define (make-identifier-denotation id)
+  (list 'identifier id '() '() '()))
+
+(define macro-rules        cadr)
+(define macro-env          caddr)
+
+(define inline-rules       macro-rules)
+(define inline-env         macro-env)
+(define inline-name        cadddr)
+
+(define identifier-name    cadr)
+(define identifier-R-entry cdr)
+
+(define (same-denotation? d1 d2)
+  (or (eq? d1 d2)
+      (and (identifier-denotation? d1)
+           (identifier-denotation? d2)
+           (eq? (identifier-name d1)
+                (identifier-name d2)))))
+
+(define denotation-of-quote
+  (syntactic-lookup standard-syntactic-environment 'quote))
+
+(define denotation-of-lambda
+  (syntactic-lookup standard-syntactic-environment 'lambda))
+
+(define denotation-of-if
+  (syntactic-lookup standard-syntactic-environment 'if))
+
+(define denotation-of-set!
+  (syntactic-lookup standard-syntactic-environment 'set!))
+
+(define denotation-of-begin
+  (syntactic-lookup standard-syntactic-environment 'begin))
+
+(define denotation-of-define
+  (syntactic-lookup standard-syntactic-environment 'define))
+
+(define denotation-of-define-inline
+  (syntactic-lookup standard-syntactic-environment 'define-inline))
+
+(define denotation-of-define-syntax
+  (syntactic-lookup standard-syntactic-environment 'define-syntax))
+
+(define denotation-of-let-syntax
+  (syntactic-lookup standard-syntactic-environment 'let-syntax))
+
+(define denotation-of-letrec-syntax
+  (syntactic-lookup standard-syntactic-environment 'letrec-syntax))
+
+(define denotation-of-syntax-rules
+  (syntactic-lookup standard-syntactic-environment 'syntax-rules))
+
+(define denotation-of-...
+  (syntactic-lookup standard-syntactic-environment '...))
+
+(define denotation-of-transformer
+  (syntactic-lookup standard-syntactic-environment 'transformer))
+
+; Given a syntactic environment env to be extended, an alist returned
+; by rename-vars, and a syntactic environment env2, extends env by
+; binding the fresh identifiers to the denotations of the original
+; identifiers in env2.
+
+(define (syntactic-alias env alist env2)
+  (syntactic-divert
+   env
+   (map (lambda (name-pair)
+          (let ((old-name (car name-pair))
+                (new-name (cdr name-pair)))
+            (cons new-name
+                  (syntactic-lookup env2 old-name))))
+        alist)))
+
+; Given a syntactic environment and an alist returned by rename-vars,
+; extends the environment by binding the old identifiers to the fresh
+; identifiers.
+; For Twobit, it also binds the fresh identifiers to their denotations.
+; This is ok so long as the fresh identifiers are not legal Scheme
+; identifiers.
+
+(define (syntactic-rename env alist)
+  (if (null? alist)
+      env
+      (let* ((old (caar alist))
+             (new (cdar alist))
+             (denotation (make-identifier-denotation new)))
+        (syntactic-rename
+         (cons (cons old denotation)
+               (cons (cons new denotation)
+                     env))
+         (cdr alist)))))
+
+; Renaming of variables.
+
+(define renaming-counter 0)
+
+(define (make-rename-procedure)
+  (set! renaming-counter (+ renaming-counter 1))
+  (let ((suffix (string-append renaming-suffix (number->string renaming-counter))))
+    (lambda (sym)
+      (if (symbol? sym)
+          (let ((s (symbol->string sym)))
+            (if (and (positive? (string-length s))
+                     (char=? (string-ref s 0) renaming-prefix-character))
+                (string->symbol (string-append s suffix))
+                (string->symbol (string-append renaming-prefix s suffix))))
+          (m-warn "Illegal use of rename procedure" 'ok:FIXME sym)))))
+
+; Given a datum, strips the suffixes from any symbols that appear within
+; the datum, trying not to copy any more of the datum than necessary.
+
+(define (m-strip x)
+  (define (original-symbol x)
+    (define (loop sym s i n)
+      (cond ((= i n) sym)
+            ((char=? (string-ref s i)
+                     renaming-suffix-character)
+             (string->symbol (substring s 1 i)))
+            (else
+             (loop sym s (+ i 1) n))))
+    (let ((s (symbol->string x)))
+      (if (and (positive? (string-length s))
+               (char=? (string-ref s 0) renaming-prefix-character))
+          (loop x s 0 (string-length s))
+          x)))
+  (cond ((symbol? x)
+         (original-symbol x))
+        ((pair? x)
+         (let ((a (m-strip (car x)))
+               (b (m-strip (cdr x))))
+           (if (and (eq? a (car x))
+                    (eq? b (cdr x)))
+               x
+               (cons a b))))
+        ((vector? x)
+         (let* ((v (vector->list x))
+                (v2 (map m-strip v)))
+           (if (equal? v v2)
+               x
+               (list->vector v2))))
+        (else x)))
+
+; Given a list of identifiers, or a formal parameter "list",
+; returns an alist that associates each identifier with a fresh identifier.
+
+(define (rename-vars original-vars)
+  (let ((rename (make-rename-procedure)))
+    (define (loop vars newvars)
+      (cond ((null? vars) (reverse newvars))
+            ((pair? vars)
+             (let ((var (car vars)))
+               (if (symbol? var)
+                   (loop (cdr vars)
+                         (cons (cons var (rename var))
+                               newvars))
+                   (m-error "Illegal variable" var))))
+            ((symbol? vars)
+             (loop (list vars) newvars))
+            (else (m-error "Malformed parameter list" original-vars))))
+    (loop original-vars '())))
+
+; Given a <formals> and an alist returned by rename-vars that contains
+; a new name for each formal identifier in <formals>, renames the
+; formal identifiers.
+
+(define (rename-formals formals alist)
+  (cond ((null? formals) '())
+        ((pair? formals)
+         (cons (cdr (assq (car formals) alist))
+               (rename-formals (cdr formals) alist)))
+        (else (cdr (assq formals alist)))))
+; Copyright 1992 William Clinger
+;
+; Permission to copy this software, in whole or in part, to use this
+; software for any lawful purpose, and to redistribute this software
+; is granted subject to the restriction that all copies made of this
+; software must include this copyright notice in full.
+;
+; I also request that you send me a copy of any improvements that you
+; make to this software so that they may be incorporated within it to
+; the benefit of the Scheme community.
+;
+; 23 November 1998
+\f; Compiler for a <transformer spec>.
+;
+; References:
+;
+;    The Revised^4 Report on the Algorithmic Language Scheme.
+;    Clinger and Rees [editors].  To appear in Lisp Pointers.
+;    Also available as a technical report from U of Oregon,
+;    MIT AI Lab, and Cornell.
+;
+;    Macros That Work.  Clinger and Rees.  POPL '91.
+;
+; The input is a <transformer spec> and a syntactic environment.
+; Syntactic environments are described in another file.
+;
+; The supported syntax differs from the R4RS in that vectors are
+; allowed as patterns and as templates and are not allowed as
+; pattern or template data.
+;
+;    <transformer spec>  -->  (syntax-rules <literals> <rules>)
+;    <rules>  -->  ()  |  (<rule> . <rules>)
+;    <rule> --> (<pattern> <template>)
+;    <pattern> --> <pattern_var>      ; a <symbol> not in <literals>
+;                | <symbol>           ; a <symbol> in <literals>
+;                | ()
+;                | (<pattern> . <pattern>)
+;                | (<ellipsis_pattern>)
+;                | #(<pattern>*)                     ; extends R4RS
+;                | #(<pattern>* <ellipsis_pattern>)  ; extends R4RS
+;                | <pattern_datum>
+;    <template> --> <pattern_var>
+;                |  <symbol>
+;                |  ()
+;                |  (<template2> . <template2>)
+;                |  #(<template>*)                   ; extends R4RS
+;                |  <pattern_datum>
+;    <template2> --> <template>  |  <ellipsis_template>
+;    <pattern_datum> --> <string>                    ; no <vector>
+;                     |  <character>
+;                     |  <boolean>
+;                     |  <number>
+;    <ellipsis_pattern>  --> <pattern> ...
+;    <ellipsis_template> --> <template> ...
+;    <pattern_var>       --> <symbol> ; not in <literals>
+;    <literals>  -->  ()  |  (<symbol> . <literals>)
+;
+; Definitions.
+;
+; scope of an ellipsis
+;
+;    Within a pattern or template, the scope of an ellipsis
+;    (...) is the pattern or template that appears to its left.
+;
+; rank of a pattern variable
+;
+;    The rank of a pattern variable is the number of ellipses
+;    within whose scope it appears in the pattern.
+;
+; rank of a subtemplate
+;
+;    The rank of a subtemplate is the number of ellipses within
+;    whose scope it appears in the template.
+;
+; template rank of an occurrence of a pattern variable
+;
+;    The template rank of an occurrence of a pattern variable
+;    within a template is the rank of that occurrence, viewed
+;    as a subtemplate.
+;
+; variables bound by a pattern
+;
+;    The variables bound by a pattern are the pattern variables
+;    that appear within it.
+;
+; referenced variables of a subtemplate
+;
+;    The referenced variables of a subtemplate are the pattern
+;    variables that appear within it.
+;
+; variables opened by an ellipsis template
+;
+;    The variables opened by an ellipsis template are the
+;    referenced pattern variables whose rank is greater than
+;    the rank of the ellipsis template.
+;    
+;
+; Restrictions.
+;
+;    No pattern variable appears more than once within a pattern.
+;
+;    For every occurrence of a pattern variable within a template,
+;    the template rank of the occurrence must be greater than or
+;    equal to the pattern variable's rank.
+;
+;    Every ellipsis template must open at least one variable.
+;    
+;    For every ellipsis template, the variables opened by an
+;    ellipsis template must all be bound to sequences of the
+;    same length.
+;
+;
+; The compiled form of a <rule> is
+;
+;    <rule> --> (<pattern> <template> <inserted>)
+;    <pattern> --> <pattern_var>
+;                | <symbol>
+;                | ()
+;                | (<pattern> . <pattern>)
+;                | <ellipsis_pattern>
+;                | #(<pattern>)
+;                | <pattern_datum>
+;    <template> --> <pattern_var>
+;                |  <symbol>
+;                |  ()
+;                |  (<template2> . <template2>)
+;                |  #(<pattern>)
+;                |  <pattern_datum>
+;    <template2> --> <template>  |  <ellipsis_template>
+;    <pattern_datum> --> <string>
+;                     |  <character>
+;                     |  <boolean>
+;                     |  <number>
+;    <pattern_var>       --> #(<V> <symbol> <rank>)
+;    <ellipsis_pattern>  --> #(<E> <pattern> <pattern_vars>)
+;    <ellipsis_template> --> #(<E> <template> <pattern_vars>)
+;    <inserted> -->     ()  |  (<symbol> . <inserted>)
+;    <pattern_vars> --> ()  |  (<pattern_var> . <pattern_vars>)
+;    <rank>  -->  <exact non-negative integer>
+;
+; where <V> and <E> are unforgeable values.
+; The pattern variables associated with an ellipsis pattern
+; are the variables bound by the pattern, and the pattern
+; variables associated with an ellipsis template are the
+; variables opened by the ellipsis template.
+;
+;
+; What's wrong with the above?
+; If the template contains a big chunk that contains no pattern variables
+; or inserted identifiers, then the big chunk will be copied unnecessarily.
+; That shouldn't matter very often.
+
+($$trace "syntaxrules")
+
+(define pattern-variable-flag (list 'v))
+(define ellipsis-pattern-flag (list 'e))
+(define ellipsis-template-flag ellipsis-pattern-flag)
+
+(define (make-patternvar v rank)
+  (vector pattern-variable-flag v rank))
+(define (make-ellipsis-pattern P vars)
+  (vector ellipsis-pattern-flag P vars))
+(define (make-ellipsis-template T vars)
+  (vector ellipsis-template-flag T vars))
+
+(define (patternvar? x)
+  (and (vector? x)
+       (= (vector-length x) 3)
+       (eq? (vector-ref x 0) pattern-variable-flag)))
+
+(define (ellipsis-pattern? x)
+  (and (vector? x)
+       (= (vector-length x) 3)
+       (eq? (vector-ref x 0) ellipsis-pattern-flag)))
+
+(define (ellipsis-template? x)
+  (and (vector? x)
+       (= (vector-length x) 3)
+       (eq? (vector-ref x 0) ellipsis-template-flag)))
+
+(define (patternvar-name V) (vector-ref V 1))
+(define (patternvar-rank V) (vector-ref V 2))
+(define (ellipsis-pattern P) (vector-ref P 1))
+(define (ellipsis-pattern-vars P) (vector-ref P 2))
+(define (ellipsis-template T) (vector-ref T 1))
+(define (ellipsis-template-vars T) (vector-ref T 2))
+
+(define (pattern-variable v vars)
+  (cond ((null? vars) #f)
+        ((eq? v (patternvar-name (car vars)))
+         (car vars))
+        (else (pattern-variable v (cdr vars)))))
+
+; Given a <transformer spec> and a syntactic environment,
+; returns a macro denotation.
+;
+; A macro denotation is of the form
+;
+;    (macro (<rule> ...) env)
+;
+; where each <rule> has been compiled as described above.
+
+(define (m-compile-transformer-spec spec env)
+  (if (and (> (safe-length spec) 1)
+           (eq? (syntactic-lookup env (car spec))
+                denotation-of-syntax-rules))
+      (let ((literals (cadr spec))
+            (rules (cddr spec)))
+        (if (or (not (list? literals))
+                (not (every1? (lambda (rule)
+                                (and (= (safe-length rule) 2)
+                                     (pair? (car rule))))
+                              rules)))
+            (m-error "Malformed syntax-rules" spec))
+        (list 'macro
+              (map (lambda (rule)
+                     (m-compile-rule rule literals env))
+                   rules)
+              env))
+      (m-error "Malformed syntax-rules" spec)))
+
+(define (m-compile-rule rule literals env)
+  (m-compile-pattern (cdr (car rule))
+                     literals
+                     env
+                     (lambda (compiled-rule patternvars)
+                       ; FIXME
+                       ; should check uniqueness of pattern variables here
+                       (cons compiled-rule
+                             (m-compile-template
+                              (cadr rule)
+                              patternvars
+                              env)))))
+
+(define (m-compile-pattern P literals env k)
+  (define (loop P vars rank k)
+    (cond ((symbol? P)
+           (if (memq P literals)
+               (k P vars)
+               (let ((var (make-patternvar P rank)))
+                 (k var (cons var vars)))))
+          ((null? P) (k '() vars))
+          ((pair? P)
+           (if (and (pair? (cdr P))
+                    (symbol? (cadr P))
+                    (same-denotation? (syntactic-lookup env (cadr P))
+                                      denotation-of-...))
+               (if (null? (cddr P))
+                   (loop (car P)
+                         '()
+                         (+ rank 1)
+                         (lambda (P vars1)
+                           (k (make-ellipsis-pattern P vars1)
+                              (union2 vars1 vars))))
+                   (m-error "Malformed pattern" P))
+               (loop (car P)
+                     vars
+                     rank
+                     (lambda (P1 vars)
+                       (loop (cdr P)
+                             vars
+                             rank
+                             (lambda (P2 vars)
+                               (k (cons P1 P2) vars)))))))
+          ((vector? P)
+           (loop (vector->list P)
+                 vars
+                 rank
+                 (lambda (P vars)
+                   (k (vector P) vars))))
+          (else (k P vars))))
+  (loop P '() 0 k))
+
+(define (m-compile-template T vars env)
+  
+  (define (loop T inserted referenced rank escaped? k)
+    (cond ((symbol? T)
+           (let ((x (pattern-variable T vars)))
+             (if x
+                 (if (>= rank (patternvar-rank x))
+                     (k x inserted (cons x referenced))
+                     (m-error
+                      "Too few ellipses follow pattern variable in template"
+                      (patternvar-name x)))
+                 (k T (cons T inserted) referenced))))
+          ((null? T) (k '() inserted referenced))
+          ((pair? T)
+           (cond ((and (not escaped?)
+                       (symbol? (car T))
+                       (same-denotation? (syntactic-lookup env (car T))
+                                         denotation-of-...)
+                       (pair? (cdr T))
+                       (null? (cddr T)))
+                  (loop (cadr T) inserted referenced rank #t k))
+                 ((and (not escaped?)
+                       (pair? (cdr T))
+                       (symbol? (cadr T))
+                       (same-denotation? (syntactic-lookup env (cadr T))
+                                         denotation-of-...))
+                  (loop1 T inserted referenced rank escaped? k))
+                 (else
+                  (loop (car T)
+                        inserted
+                        referenced
+                        rank
+                        escaped?
+                        (lambda (T1 inserted referenced)
+                          (loop (cdr T)
+                                inserted
+                                referenced
+                                rank
+                                escaped?
+                                (lambda (T2 inserted referenced)
+                                  (k (cons T1 T2) inserted referenced))))))))
+          ((vector? T)
+           (loop (vector->list T)
+                 inserted
+                 referenced
+                 rank
+                 escaped?
+                 (lambda (T inserted referenced)
+                   (k (vector T) inserted referenced))))
+          (else (k T inserted referenced))))
+  
+  (define (loop1 T inserted referenced rank escaped? k)
+    (loop (car T)
+          inserted
+          '()
+          (+ rank 1)
+          escaped?
+          (lambda (T1 inserted referenced1)
+            (loop (cddr T)
+                  inserted
+                  (append referenced1 referenced)
+                  rank
+                  escaped?
+                  (lambda (T2 inserted referenced)
+                    (k (cons (make-ellipsis-template
+                              T1
+                              (filter1 (lambda (var)
+                                         (> (patternvar-rank var)
+                                            rank))
+                                       referenced1))
+                             T2)
+                       inserted
+                       referenced))))))
+  
+  (loop T
+        '()
+        '()
+        0
+        #f
+        (lambda (T inserted referenced)
+          (list T inserted))))
+
+; The pattern matcher.
+;
+; Given an input, a pattern, and two syntactic environments,
+; returns a pattern variable environment (represented as an alist)
+; if the input matches the pattern, otherwise returns #f.
+
+(define empty-pattern-variable-environment
+  (list (make-patternvar (string->symbol "") 0)))
+
+(define (m-match F P env-def env-use)
+  
+  (define (match F P answer rank)
+    (cond ((null? P)
+           (and (null? F) answer))
+          ((pair? P)
+           (and (pair? F)
+                (let ((answer (match (car F) (car P) answer rank)))
+                  (and answer (match (cdr F) (cdr P) answer rank)))))
+          ((symbol? P)
+           (and (symbol? F)
+                (same-denotation? (syntactic-lookup env-def P)
+                                  (syntactic-lookup env-use F))
+                answer))
+          ((patternvar? P)
+           (cons (cons P F) answer))
+          ((ellipsis-pattern? P)
+           (match1 F P answer (+ rank 1)))
+          ((vector? P)
+           (and (vector? F)
+                (match (vector->list F) (vector-ref P 0) answer rank)))
+          (else (and (equal? F P) answer))))
+  
+  (define (match1 F P answer rank)
+    (cond ((not (list? F)) #f)
+          ((null? F)
+           (append (map (lambda (var) (cons var '()))
+                        (ellipsis-pattern-vars P))
+                   answer))
+          (else
+           (let* ((P1 (ellipsis-pattern P))
+                  (answers (map (lambda (F) (match F P1 answer rank))
+                                F)))
+             (if (every1? (lambda (answer) answer) answers)
+                 (append (map (lambda (var)
+                                (cons var
+                                      (map (lambda (answer)
+                                             (cdr (assq var answer)))
+                                           answers)))
+                              (ellipsis-pattern-vars P))
+                         answer)
+                 #f)))))
+  
+  (match F P empty-pattern-variable-environment 0))
+
+(define (m-rewrite T alist)
+  
+  (define (rewrite T alist rank)
+    (cond ((null? T) '())
+          ((pair? T)
+           ((if (ellipsis-pattern? (car T))
+                append
+                cons)
+            (rewrite (car T) alist rank)
+            (rewrite (cdr T) alist rank)))
+          ((symbol? T) (cdr (assq T alist)))
+          ((patternvar? T) (cdr (assq T alist)))
+          ((ellipsis-template? T)
+           (rewrite1 T alist (+ rank 1)))
+          ((vector? T)
+           (list->vector (rewrite (vector-ref T 0) alist rank)))
+          (else T)))
+  
+  (define (rewrite1 T alist rank)
+    (let* ((T1 (ellipsis-template T))
+           (vars (ellipsis-template-vars T))
+           (rows (map (lambda (var) (cdr (assq var alist)))
+                      vars)))
+      (map (lambda (alist) (rewrite T1 alist rank))
+           (make-columns vars rows alist))))
+  
+  (define (make-columns vars rows alist)
+    (define (loop rows)
+      (if (null? (car rows))
+          '()
+          (cons (append (map (lambda (var row)
+                               (cons var (car row)))
+                             vars
+                             rows)
+                        alist)
+                (loop (map cdr rows)))))
+    (if (or (null? (cdr rows))
+            (apply = (map length rows)))
+        (loop rows)
+        (m-error "Use of macro is not consistent with definition"
+                 vars
+                 rows)))
+  
+  (rewrite T alist 0))
+
+; Given a use of a macro, the syntactic environment of the use,
+; a continuation that expects a transcribed expression and
+; a new environment in which to continue expansion, and a boolean
+; that is true if this transcription is for an inline procedure,
+; does the right thing.
+
+(define (m-transcribe0 exp env-use k inline?)
+  (let* ((m (syntactic-lookup env-use (car exp)))
+         (rules (macro-rules m))
+         (env-def (macro-env m))
+         (F (cdr exp)))
+    (define (loop rules)
+      (if (null? rules)
+          (if inline?
+              (k exp env-use)
+              (m-error "Use of macro does not match definition" exp))
+          (let* ((rule (car rules))
+                 (pattern (car rule))
+                 (alist (m-match F pattern env-def env-use)))
+            (if alist
+                (let* ((template (cadr rule))
+                       (inserted (caddr rule))
+                       (alist2 (rename-vars inserted))
+                       (newexp (m-rewrite template (append alist2 alist))))
+                  (k newexp
+                     (syntactic-alias env-use alist2 env-def)))
+                (loop (cdr rules))))))
+    (if (procedure? rules)
+        (m-transcribe-low-level exp env-use k rules env-def)
+        (loop rules))))
+
+(define (m-transcribe exp env-use k)
+  (m-transcribe0 exp env-use k #f))
+
+(define (m-transcribe-inline exp env-use k)
+  (m-transcribe0 exp env-use k #t))
+
+; Copyright 1998 William Clinger
+;
+; $Id: twobit.sch,v 1.3 1999/08/23 19:14:26 lth Exp $
+;
+; Low-level macro facility based on explicit renaming.  See
+; William D Clinger. Hygienic macros through explicit renaming.
+; In Lisp Pointers IV(4), 25-28, December 1991.
+
+($$trace "lowlevel")
+
+(define (m-transcribe-low-level exp env-use k transformer env-def)
+  (let ((rename0 (make-rename-procedure))
+        (renamed '())
+        (ok #t))
+    (define (lookup sym)
+      (let loop ((alist renamed))
+        (cond ((null? alist)
+               (syntactic-lookup env-use sym))
+              ((eq? sym (cdr (car alist)))
+               (syntactic-lookup env-def (car (car alist))))
+              (else
+               (loop (cdr alist))))))
+    (let ((rename
+           (lambda (sym)
+             (if ok
+                 (let ((probe (assq sym renamed)))
+                   (if probe
+                       (cdr probe)
+                       (let ((sym2 (rename0 sym)))
+                         (set! renamed (cons (cons sym sym2) renamed))
+                         sym2)))
+                 (m-error "Illegal use of a rename procedure" sym))))
+          (compare
+           (lambda (sym1 sym2)
+             (same-denotation? (lookup sym1) (lookup sym2)))))
+      (let ((exp2 (transformer exp rename compare)))
+        (set! ok #f)
+        (k exp2
+           (syntactic-alias env-use renamed env-def))))))
+
+(define identifier? symbol?)
+
+(define (identifier->symbol id)
+  (m-strip id))
+; Copyright 1992 William Clinger
+;
+; $Id: twobit.sch,v 1.3 1999/08/23 19:14:26 lth Exp $
+;
+; 22 April 1999
+
+($$trace "expand")
+
+; This procedure sets the default scope of global macro definitions.
+
+(define define-syntax-scope
+  (let ((flag 'letrec))
+    (lambda args
+      (cond ((null? args) flag)
+            ((not (null? (cdr args)))
+             (apply m-warn
+                    "Too many arguments passed to define-syntax-scope"
+                    args))
+            ((memq (car args) '(letrec letrec* let*))
+             (set! flag (car args)))
+            (else (m-warn "Unrecognized argument to define-syntax-scope"
+                          (car args)))))))
+
+; The main entry point.
+; The outermost lambda allows known procedures to be lifted outside
+; all local variables.
+
+(define (macro-expand def-or-exp)
+  (call-with-current-continuation
+   (lambda (k)
+     (set! m-quit k)
+     (set! renaming-counter 0)
+     (make-call
+      (make-lambda '() ; formals
+                   '() ; definitions
+                   '() ; R
+                   '() ; F
+                   '() ; G
+                   '() ; declarations
+                   #f  ; documentation
+                   (desugar-definitions def-or-exp
+                                        global-syntactic-environment
+                                        make-toplevel-definition))
+      '()))))
+
+(define (desugar-definitions exp env make-toplevel-definition)
+  (letrec
+    
+    ((define-loop 
+       (lambda (exp rest first env)
+         (cond ((and (pair? exp)
+                     (symbol? (car exp))
+                     (eq? (syntactic-lookup env (car exp))
+                          denotation-of-begin)
+                     (pair? (cdr exp)))
+                (define-loop (cadr exp) (append (cddr exp) rest) first env))
+               ((and (pair? exp)
+                     (symbol? (car exp))
+                     (eq? (syntactic-lookup env (car exp))
+                          denotation-of-define))
+                (let ((exp (desugar-define exp env)))
+                  (cond ((and (null? first) (null? rest))
+                         exp)
+                        ((null? rest)
+                         (make-begin (reverse (cons exp first))))
+                        (else (define-loop (car rest)
+                                (cdr rest)
+                                (cons exp first)
+                                env)))))
+               ((and (pair? exp)
+                     (symbol? (car exp))
+                     (or (eq? (syntactic-lookup env (car exp))
+                              denotation-of-define-syntax)
+                         (eq? (syntactic-lookup env (car exp))
+                              denotation-of-define-inline))
+                     (null? first))
+                (define-syntax-loop exp rest env))
+               ((and (pair? exp)
+                     (symbol? (car exp))
+                     (macro-denotation? (syntactic-lookup env (car exp))))
+                (m-transcribe exp
+                              env
+                              (lambda (exp env)
+                                (define-loop exp rest first env))))
+               ((and (null? first) (null? rest))
+                (m-expand exp env))
+               ((null? rest)
+                (make-begin (reverse (cons (m-expand exp env) first))))
+               (else (make-begin
+                      (append (reverse first)
+                              (map (lambda (exp) (m-expand exp env))
+                                   (cons exp rest))))))))
+     
+     (define-syntax-loop 
+       (lambda (exp rest env)
+         (cond ((and (pair? exp)
+                     (symbol? (car exp))
+                     (eq? (syntactic-lookup env (car exp))
+                          denotation-of-begin)
+                     (pair? (cdr exp)))
+                (define-syntax-loop (cadr exp) (append (cddr exp) rest) env))
+               ((and (pair? exp)
+                     (symbol? (car exp))
+                     (eq? (syntactic-lookup env (car exp))
+                          denotation-of-define-syntax))
+                (if (pair? (cdr exp))
+                    (redefinition (cadr exp)))
+                (if (null? rest)
+                    (m-define-syntax exp env)
+                    (begin (m-define-syntax exp env)
+                           (define-syntax-loop (car rest) (cdr rest) env))))
+               ((and (pair? exp)
+                     (symbol? (car exp))
+                     (eq? (syntactic-lookup env (car exp))
+                          denotation-of-define-inline))
+                (if (pair? (cdr exp))
+                    (redefinition (cadr exp)))
+                (if (null? rest)
+                    (m-define-inline exp env)
+                    (begin (m-define-inline exp env)
+                           (define-syntax-loop (car rest) (cdr rest) env))))
+               ((and (pair? exp)
+                     (symbol? (car exp))
+                     (macro-denotation? (syntactic-lookup env (car exp))))
+                (m-transcribe exp
+                              env
+                              (lambda (exp env)
+                                (define-syntax-loop exp rest env))))
+               ((and (pair? exp)
+                     (symbol? (car exp))
+                     (eq? (syntactic-lookup env (car exp))
+                          denotation-of-define))
+                (define-loop exp rest '() env))
+               ((null? rest)
+                (m-expand exp env))
+               (else (make-begin
+                      (map (lambda (exp) (m-expand exp env))
+                           (cons exp rest)))))))
+     
+     (desugar-define
+      (lambda (exp env)
+        (cond 
+         ((null? (cdr exp)) (m-error "Malformed definition" exp))
+         ; (define foo) syntax is transformed into (define foo (undefined)).
+         ((null? (cddr exp))
+          (let ((id (cadr exp)))
+            (if (or (null? pass1-block-inlines)
+                    (not (memq id pass1-block-inlines)))
+                (begin
+                 (redefinition id)
+                 (syntactic-bind-globally! id (make-identifier-denotation id))))
+            (make-toplevel-definition id (make-undefined))))
+         ((pair? (cadr exp))              
+          (desugar-define
+           (let* ((def (car exp))
+                  (pattern (cadr exp))
+                  (f (car pattern))
+                  (args (cdr pattern))
+                  (body (cddr exp)))
+             (if (and (symbol? (car (cadr exp)))
+                      (benchmark-mode)
+                      (list? (cadr exp)))
+                 `(,def ,f
+                        (,lambda0 ,args
+                           ((,lambda0 (,f)
+                               (,set!0 ,f (,lambda0 ,args ,@body))
+                               ,pattern)
+                            0)))
+                 `(,def ,f (,lambda0 ,args ,@body))))
+           env))
+         ((> (length exp) 3) (m-error "Malformed definition" exp))
+         (else (let ((id (cadr exp)))
+                 (if (or (null? pass1-block-inlines)
+                         (not (memq id pass1-block-inlines)))
+                     (begin
+                      (redefinition id)
+                      (syntactic-bind-globally! id (make-identifier-denotation id))))
+                 (make-toplevel-definition id (m-expand (caddr exp) env)))))))
+     
+     (redefinition
+      (lambda (id)
+        (if (symbol? id)
+            (if (not (identifier-denotation?
+                      (syntactic-lookup global-syntactic-environment id)))
+                (if (issue-warnings)
+                    (m-warn "Redefining " id)))
+            (m-error "Malformed variable or keyword" id)))))
+    
+    ; body of letrec
+    
+    (define-loop exp '() '() env)))
+
+; Given an expression and a syntactic environment,
+; returns an expression in core Scheme.
+
+(define (m-expand exp env)
+  (cond ((not (pair? exp))
+         (m-atom exp env))
+        ((not (symbol? (car exp)))
+         (m-application exp env))
+        (else
+         (let ((keyword (syntactic-lookup env (car exp))))
+           (case (denotation-class keyword)
+             ((special)
+              (cond
+               ((eq? keyword denotation-of-quote)         (m-quote exp))
+               ((eq? keyword denotation-of-lambda)        (m-lambda exp env))
+               ((eq? keyword denotation-of-if)            (m-if exp env))
+               ((eq? keyword denotation-of-set!)          (m-set exp env))
+               ((eq? keyword denotation-of-begin)         (m-begin exp env))
+               ((eq? keyword denotation-of-let-syntax)
+               (m-let-syntax exp env))
+               ((eq? keyword denotation-of-letrec-syntax)
+               (m-letrec-syntax exp env))
+               ((or (eq? keyword denotation-of-define)
+                    (eq? keyword denotation-of-define-syntax)
+                    (eq? keyword denotation-of-define-inline))
+                (m-error "Definition out of context" exp))
+               (else (m-bug "Bug detected in m-expand" exp env))))
+             ((macro) (m-macro exp env))
+             ((inline) (m-inline exp env))
+             ((identifier) (m-application exp env))
+             (else (m-bug "Bug detected in m-expand" exp env)))))))
+
+(define (m-atom exp env)
+  (cond ((not (symbol? exp))
+         ; Here exp ought to be a boolean, number, character, or string.
+         ; I'll warn about other things but treat them as if quoted.
+        ;
+        ; I'm turning off some of the warnings because notably procedures
+        ; and #!unspecified can occur in loaded files and it's a major
+        ; pain if a warning is printed for each. --lars
+         (if (and (not (boolean? exp))
+                  (not (number? exp))
+                  (not (char? exp))
+                  (not (string? exp))
+                 (not (procedure? exp))
+                 (not (eq? exp (unspecified))))
+             (m-warn "Malformed constant -- should be quoted" exp))
+         (make-constant exp))
+        (else (let ((denotation (syntactic-lookup env exp)))
+                (case (denotation-class denotation)
+                  ((special macro)
+                   (m-warn "Syntactic keyword used as a variable" exp)
+                   ; Syntactic keywords used as variables are treated as #t.
+                   (make-constant #t))
+                  ((inline)
+                   (make-variable (inline-name denotation)))
+                  ((identifier)
+                   (let ((var (make-variable (identifier-name denotation)))
+                         (R-entry (identifier-R-entry denotation)))
+                     (R-entry.references-set!
+                      R-entry
+                      (cons var (R-entry.references R-entry)))
+                     var))
+                  (else (m-bug "Bug detected by m-atom" exp env)))))))
+
+(define (m-quote exp)
+  (if (and (pair? (cdr exp))
+           (null? (cddr exp)))
+      (make-constant (m-strip (cadr exp)))
+      (m-error "Malformed quoted constant" exp)))
+
+(define (m-lambda exp env)
+  (if (> (safe-length exp) 2)
+      
+      (let* ((formals (cadr exp))
+             (alist (rename-vars formals))
+             (env (syntactic-rename env alist))
+             (body (cddr exp)))
+        
+        (do ((alist alist (cdr alist)))
+            ((null? alist))
+            (if (assq (caar alist) (cdr alist))
+                (m-error "Malformed parameter list" formals)))
+        
+        ; To simplify the run-time system, there's a limit on how many
+        ; fixed arguments can be followed by a rest argument.
+        ; That limit is removed here.
+        ; Bug: documentation slot isn't right when this happens.
+        ; Bug: this generates extremely inefficient code.
+        
+        (if (and (not (list? formals))
+                 (> (length alist) @maxargs-with-rest-arg@))
+            (let ((TEMP (car (rename-vars '(temp)))))
+              (m-lambda
+               `(,lambda0 ,TEMP
+                           ((,lambda0 ,(map car alist)
+                                      ,@(cddr exp))
+                            ,@(do ((actuals '() (cons (list name:CAR path)
+                                                      actuals))
+                                   (path TEMP (list name:CDR path))
+                                   (formals formals (cdr formals)))
+                                  ((symbol? formals)
+                                   (append (reverse actuals) (list path))))))
+               env))
+            (make-lambda (rename-formals formals alist)
+                         '() ; no definitions yet
+                         (map (lambda (entry)
+                                (cdr (syntactic-lookup env (cdr entry))))
+                              alist) ; R
+                         '() ; F
+                         '() ; G
+                         '() ; decls
+                         (make-doc #f
+                                   (if (list? formals)
+                                       (length alist)
+                                       (exact->inexact (- (length alist) 1)))
+                                   (if (include-variable-names)
+                                       formals
+                                       #f)
+                                   (if (include-source-code)
+                                       exp
+                                       #f)
+                                   source-file-name
+                                   source-file-position)
+                         (m-body body env))))
+      
+      (m-error "Malformed lambda expression" exp)))
+
+(define (m-body body env)
+  (define (loop body env defs)
+    (if (null? body)
+        (m-error "Empty body"))
+    (let ((exp (car body)))
+      (if (and (pair? exp)
+               (symbol? (car exp)))
+          (let ((denotation (syntactic-lookup env (car exp))))
+            (case (denotation-class denotation)
+              ((special)
+               (cond ((eq? denotation denotation-of-begin)
+                      (loop (append (cdr exp) (cdr body)) env defs))
+                     ((eq? denotation denotation-of-define)
+                      (loop (cdr body) env (cons exp defs)))
+                     (else (finalize-body body env defs))))
+              ((macro)
+               (m-transcribe exp
+                             env
+                             (lambda (exp env)
+                               (loop (cons exp (cdr body))
+                                     env
+                                     defs))))
+              ((inline identifier)
+               (finalize-body body env defs))
+              (else (m-bug "Bug detected in m-body" body env))))
+          (finalize-body body env defs))))
+  (loop body env '()))
+
+(define (finalize-body body env defs)
+  (if (null? defs)
+      (let ((body (map (lambda (exp) (m-expand exp env))
+                       body)))
+        (if (null? (cdr body))
+            (car body)
+            (make-begin body)))
+      (let ()
+        (define (sort-defs defs)
+          (let* ((augmented
+                  (map (lambda (def)
+                         (let ((rhs (cadr def)))
+                           (if (not (pair? rhs))
+                               (cons 'trivial def)
+                               (let ((denotation
+                                      (syntactic-lookup env (car rhs))))
+                                 (cond ((eq? denotation
+                                             denotation-of-lambda)
+                                        (cons 'procedure def))
+                                       ((eq? denotation
+                                             denotation-of-quote)
+                                        (cons 'trivial def))
+                                       (else
+                                        (cons 'miscellaneous def)))))))
+                       defs))
+                 (sorted (twobit-sort (lambda (x y)
+                                        (or (eq? (car x) 'procedure)
+                                            (eq? (car y) 'miscellaneous)))
+                                      augmented)))
+            (map cdr sorted)))
+        (define (desugar-definition def)
+          (if (> (safe-length def) 2)
+              (cond ((pair? (cadr def))
+                     (desugar-definition
+                      `(,(car def)
+                        ,(car (cadr def))
+                        (,lambda0
+                          ,(cdr (cadr def))
+                          ,@(cddr def)))))
+                    ((and (= (length def) 3)
+                          (symbol? (cadr def)))
+                     (cdr def))
+                    (else (m-error "Malformed definition" def)))
+              (m-error "Malformed definition" def)))
+        (define (expand-letrec bindings body)
+          (make-call
+           (m-expand
+            `(,lambda0 ,(map car bindings)
+                       ,@(map (lambda (binding)
+                                `(,set!0 ,(car binding)
+                                         ,(cadr binding)))
+                              bindings)
+                         ,@body)
+            env)
+           (map (lambda (binding) (make-unspecified)) bindings)))
+        (expand-letrec (sort-defs (map desugar-definition
+                                       (reverse defs)))
+                       body))))
+
+(define (m-if exp env)
+  (let ((n (safe-length exp)))
+    (if (or (= n 3) (= n 4))
+        (make-conditional (m-expand (cadr exp) env)
+                          (m-expand (caddr exp) env)
+                          (if (= n 3)
+                              (make-unspecified)
+                              (m-expand (cadddr exp) env)))
+        (m-error "Malformed if expression" exp))))
+
+(define (m-set exp env)
+  (if (= (safe-length exp) 3)
+      (let ((lhs (m-expand (cadr exp) env))
+            (rhs (m-expand (caddr exp) env)))
+        (if (variable? lhs)
+            (let* ((x (variable.name lhs))
+                   (assignment (make-assignment x rhs))
+                   (denotation (syntactic-lookup env x)))
+              (if (identifier-denotation? denotation)
+                  (let ((R-entry (identifier-R-entry denotation)))
+                    (R-entry.references-set!
+                     R-entry
+                     (remq lhs (R-entry.references R-entry)))
+                    (R-entry.assignments-set!
+                     R-entry
+                     (cons assignment (R-entry.assignments R-entry)))))
+              (if (and (lambda? rhs)
+                       (include-procedure-names))
+                  (let ((doc (lambda.doc rhs)))
+                    (doc.name-set! doc x)))
+              (if pass1-block-compiling?
+                  (set! pass1-block-assignments
+                        (cons x pass1-block-assignments)))
+              assignment)
+            (m-error "Malformed assignment" exp)))
+      (m-error "Malformed assignment" exp)))
+
+(define (m-begin exp env)
+  (cond ((> (safe-length exp) 1)
+         (make-begin (map (lambda (exp) (m-expand exp env)) (cdr exp))))
+        ((= (safe-length exp) 1)
+         (m-warn "Non-standard begin expression" exp)
+         (make-unspecified))
+        (else
+         (m-error "Malformed begin expression" exp))))
+
+(define (m-application exp env)
+  (if (> (safe-length exp) 0)
+      (let* ((proc (m-expand (car exp) env))
+             (args (map (lambda (exp) (m-expand exp env))
+                        (cdr exp)))
+             (call (make-call proc args)))
+        (if (variable? proc)
+            (let* ((procname (variable.name proc))
+                   (entry
+                    (and (not (null? args))
+                         (constant? (car args))
+                         (integrate-usual-procedures)
+                         (every1? constant? args)
+                         (let ((entry (constant-folding-entry procname)))
+                           (and entry
+                                (let ((predicates
+                                       (constant-folding-predicates entry)))
+                                  (and (= (length args)
+                                          (length predicates))
+                                       (let loop ((args args)
+                                                  (predicates predicates))
+                                         (cond ((null? args) entry)
+                                               (((car predicates)
+                                                 (constant.value (car args)))
+                                                (loop (cdr args)
+                                                      (cdr predicates)))
+                                               (else #f))))))))))
+              (if entry
+                  (make-constant (apply (constant-folding-folder entry)
+                                        (map constant.value args)))
+                  (let ((denotation (syntactic-lookup env procname)))
+                    (if (identifier-denotation? denotation)
+                        (let ((R-entry (identifier-R-entry denotation)))
+                          (R-entry.calls-set!
+                           R-entry
+                           (cons call (R-entry.calls R-entry)))))
+                    call)))
+            call))
+      (m-error "Malformed application" exp)))
+
+; The environment argument should always be global here.
+
+(define (m-define-inline exp env)
+  (cond ((and (= (safe-length exp) 3)
+              (symbol? (cadr exp)))
+         (let ((name (cadr exp)))
+           (m-define-syntax1 name
+                             (caddr exp)
+                             env
+                             (define-syntax-scope))
+           (let ((denotation
+                  (syntactic-lookup global-syntactic-environment name)))
+             (syntactic-bind-globally!
+              name
+              (make-inline-denotation name
+                                      (macro-rules denotation)
+                                      (macro-env denotation))))
+           (make-constant name)))
+        (else
+         (m-error "Malformed define-inline" exp))))
+
+; The environment argument should always be global here.
+
+(define (m-define-syntax exp env)
+  (cond ((and (= (safe-length exp) 3)
+              (symbol? (cadr exp)))
+         (m-define-syntax1 (cadr exp)
+                           (caddr exp)
+                           env
+                           (define-syntax-scope)))
+        ((and (= (safe-length exp) 4)
+              (symbol? (cadr exp))
+              ; FIXME: should use denotations here
+              (memq (caddr exp) '(letrec letrec* let*)))
+         (m-define-syntax1 (cadr exp)
+                           (cadddr exp)
+                           env
+                           (caddr exp)))
+        (else (m-error "Malformed define-syntax" exp))))
+
+(define (m-define-syntax1 keyword spec env scope)
+  (if (and (pair? spec)
+           (symbol? (car spec)))
+      (let* ((transformer-keyword (car spec))
+             (denotation (syntactic-lookup env transformer-keyword)))
+        (cond ((eq? denotation denotation-of-syntax-rules)
+               (case scope
+                 ((letrec)  (m-define-syntax-letrec keyword spec env))
+                 ((letrec*) (m-define-syntax-letrec* keyword spec env))
+                 ((let*)    (m-define-syntax-let* keyword spec env))
+                 (else      (m-bug "Weird scope" scope))))
+              ((same-denotation? denotation denotation-of-transformer)
+               ; FIXME: no error checking here
+               (syntactic-bind-globally!
+                keyword
+                (make-macro-denotation (eval (cadr spec)) env)))
+              (else
+               (m-error "Malformed syntax transformer" spec))))
+      (m-error "Malformed syntax transformer" spec))
+  (make-constant keyword))
+
+(define (m-define-syntax-letrec keyword spec env)
+  (syntactic-bind-globally!
+   keyword
+   (m-compile-transformer-spec spec env)))
+
+(define (m-define-syntax-letrec* keyword spec env)
+  (let* ((env (syntactic-extend (syntactic-copy env)
+                                (list keyword)
+                                '((fake denotation))))
+         (transformer (m-compile-transformer-spec spec env)))
+    (syntactic-assign! env keyword transformer)
+    (syntactic-bind-globally! keyword transformer)))
+
+(define (m-define-syntax-let* keyword spec env)
+  (syntactic-bind-globally!
+   keyword
+   (m-compile-transformer-spec spec (syntactic-copy env))))
+
+(define (m-let-syntax exp env)
+  (if (and (> (safe-length exp) 2)
+           (every1? (lambda (binding)
+                      (and (pair? binding)
+                           (symbol? (car binding))
+                           (pair? (cdr binding))
+                           (null? (cddr binding))))
+                    (cadr exp)))
+      (m-body (cddr exp)
+              (syntactic-extend env
+                                (map car (cadr exp))
+                                (map (lambda (spec)
+                                       (m-compile-transformer-spec
+                                        spec
+                                        env))
+                                     (map cadr (cadr exp)))))
+      (m-error "Malformed let-syntax" exp)))
+
+(define (m-letrec-syntax exp env)
+  (if (and (> (safe-length exp) 2)
+           (every1? (lambda (binding)
+                      (and (pair? binding)
+                           (symbol? (car binding))
+                           (pair? (cdr binding))
+                           (null? (cddr binding))))
+                    (cadr exp)))
+      (let ((env (syntactic-extend env
+                                   (map car (cadr exp))
+                                   (map (lambda (id)
+                                          '(fake denotation))
+                                        (cadr exp)))))
+        (for-each (lambda (id spec)
+                    (syntactic-assign!
+                     env
+                     id
+                     (m-compile-transformer-spec spec env)))
+                  (map car (cadr exp))
+                  (map cadr (cadr exp)))
+        (m-body (cddr exp) env))
+      (m-error "Malformed let-syntax" exp)))
+
+(define (m-macro exp env)
+  (m-transcribe exp
+                env
+                (lambda (exp env)
+                  (m-expand exp env))))
+
+(define (m-inline exp env)
+  (if (integrate-usual-procedures)
+      (m-transcribe-inline exp
+                           env
+                           (lambda (newexp env)
+                             (if (eq? exp newexp)
+                                 (m-application exp env)
+                                 (m-expand newexp env))))
+      (m-application exp env)))
+
+(define m-quit             ; assigned by macro-expand
+  (lambda (v) v))
+
+; To do:
+; Clean up alist hacking et cetera.
+; Declarations.
+; Integrable procedures.
+; New semantics for body of LET-SYNTAX and LETREC-SYNTAX.
+; Copyright 1992 William Clinger
+;
+; $Id: twobit.sch,v 1.3 1999/08/23 19:14:26 lth Exp $
+;
+; 5 April 1999.
+
+($$trace "usual")
+
+; The usual macros, adapted from Jonathan's Version 2 implementation.
+; DEFINE is handled primitively, since top-level DEFINE has a side
+; effect on the global syntactic environment, and internal definitions
+; have to be handled specially anyway.
+;
+; Some extensions are noted, as are some optimizations.
+;
+; The LETREC* scope rule is used here to protect these macros against
+; redefinition of LAMBDA etc.  The scope rule is changed to LETREC at
+; the end of this file.
+
+(define-syntax-scope 'letrec*)
+
+(for-each (lambda (form)
+            (macro-expand form))
+          '(
+
+; Named LET is defined later, after LETREC has been defined.
+
+(define-syntax let
+  (syntax-rules ()
+    ((let ((?name ?val) ...) ?body ?body1 ...)
+     ((lambda (?name ...) ?body ?body1 ...) ?val ...))))
+
+(define-syntax let*
+  (syntax-rules ()
+    ((let* () ?body ?body1 ...)
+     (let () ?body ?body1 ...))
+    ((let* ((?name1 ?val1) (?name ?val) ...) ?body ?body1 ...)
+     (let ((?name1 ?val1)) (let* ((?name ?val) ...) ?body ?body1 ...)))))
+
+; Internal definitions have to be handled specially anyway,
+; so we might as well rely on them here.
+
+(define-syntax letrec
+  (syntax-rules (lambda quote)
+   ((letrec ((?name ?val) ...) ?body ?body2 ...)
+    ((lambda ()
+       (define ?name ?val) ...
+       ?body ?body2 ...)))))
+
+; This definition of named LET extends the prior definition of LET.
+; The first rule is non-circular, thanks to the LET* scope that is
+; specified for this use of DEFINE-SYNTAX.
+
+(define-syntax let let*
+  (syntax-rules ()
+    ((let (?bindings ...) . ?body)
+     (let (?bindings ...) . ?body))
+    ((let ?tag ((?name ?val) ...) ?body ?body1 ...)
+     (let ((?name ?val) ...)
+       (letrec ((?tag (lambda (?name ...) ?body ?body1 ...)))
+         (?tag ?name ...))))))
+
+(define-syntax and
+  (syntax-rules ()
+    ((and) #t)
+    ((and ?e) ?e)
+    ((and ?e1 ?e2 ?e3 ...)
+     (if ?e1 (and ?e2 ?e3 ...) #f))))
+
+(define-syntax or
+  (syntax-rules ()
+    ((or) #f)
+    ((or ?e) ?e)
+    ((or ?e1 ?e2 ?e3 ...)
+     (let ((temp ?e1))
+       (if temp temp (or ?e2 ?e3 ...))))))
+
+(define-syntax cond
+  (syntax-rules (else =>)
+    ((cond (else ?result ?result2 ...))
+     (begin ?result ?result2 ...))
+    
+    ((cond (?test => ?result))
+     (let ((temp ?test))
+       (if temp (?result temp))))
+    
+    ((cond (?test)) ?test)
+    
+    ((cond (?test ?result ?result2 ...))
+     (if ?test (begin ?result ?result2 ...)))
+    
+    ((cond (?test => ?result) ?clause ?clause2 ...)
+     (let ((temp ?test))
+       (if temp (?result temp) (cond ?clause ?clause2 ...))))
+    
+    ((cond (?test) ?clause ?clause2 ...)
+     (or ?test (cond ?clause ?clause2 ...)))
+    
+    ((cond (?test ?result ?result2 ...)
+           ?clause ?clause2 ...)
+     (if ?test
+         (begin ?result ?result2 ...)
+         (cond ?clause ?clause2 ...)))))
+
+; The R4RS says a <step> may be omitted.
+; That's a good excuse for a macro-defining macro that uses LETREC-SYNTAX
+; and the ... escape.
+
+(define-syntax do
+  (syntax-rules ()
+    ((do (?bindings0 ...) (?test) ?body0 ...)
+     (do (?bindings0 ...) (?test (if #f #f)) ?body0 ...))
+    ((do (?bindings0 ...) ?clause0 ?body0 ...)
+     (letrec-syntax
+       ((do-aux
+         (... (syntax-rules ()
+                ((do-aux () ((?name ?init ?step) ...) ?clause ?body ...)
+                 (letrec ((loop (lambda (?name ...)
+                                  (cond ?clause
+                                        (else
+                                         (begin #t ?body ...)
+                                         (loop ?step ...))))))
+                   (loop ?init ...)))
+                ((do-aux ((?name ?init ?step) ?todo ...)
+                         (?bindings ...)
+                         ?clause
+                         ?body ...)
+                 (do-aux (?todo ...)
+                         (?bindings ... (?name ?init ?step))
+                         ?clause
+                         ?body ...))
+                ((do-aux ((?name ?init) ?todo ...)
+                         (?bindings ...)
+                         ?clause
+                         ?body ...)
+                 (do-aux (?todo ...)
+                         (?bindings ... (?name ?init ?name))
+                         ?clause
+                         ?body ...))))))
+       (do-aux (?bindings0 ...) () ?clause0 ?body0 ...)))))
+
+(define-syntax delay
+  (syntax-rules ()
+    ((delay ?e) (.make-promise (lambda () ?e)))))
+
+; Another use of LETREC-SYNTAX and the escape extension.
+
+(define-syntax case
+  (syntax-rules (else)
+    ((case ?e1 (else ?body ?body2 ...))
+     (begin ?e1 ?body ?body2 ...))
+    ((case ?e1 (?z ?body ?body2 ...))
+     (if (memv ?e1 '?z) (begin ?body ?body2 ...)))
+    ((case ?e1 ?clause1 ?clause2 ?clause3 ...)
+     (letrec-syntax
+       ((case-aux
+          (... (syntax-rules (else)
+                ((case-aux ?temp (else ?body ?body2 ...))
+                 (begin ?body ?body2 ...))
+                ((case-aux ?temp ((?z ...) ?body ?body2 ...))
+                 (if (memv ?temp '(?z ...)) (begin ?body ?body2 ...)))
+                ((case-aux ?temp ((?z ...) ?body ?body2 ...) ?c1 ?c2 ...)
+                 (if (memv ?temp '(?z ...))
+                     (begin ?body ?body2 ...)
+                     (case-aux ?temp ?c1 ?c2 ...)))
+                ; a popular extension
+                ((case-aux ?temp (?z ?body ...) ?c1 ...)
+                 (case-aux ?temp ((?z) ?body ...) ?c1 ...))))))
+       (let ((temp ?e1))
+         (case-aux temp ?clause1 ?clause2 ?clause3 ...))))))
+
+; A complete implementation of quasiquote, obtained by translating
+; Jonathan Rees's implementation that was posted to RRRS-AUTHORS
+; on 22 December 1986.
+; Unfortunately, the use of LETREC scope means that it is vulnerable
+; to top-level redefinitions of QUOTE etc.  That could be fixed, but
+; it has hair enough already.
+
+(begin
+ (define-syntax .finalize-quasiquote letrec
+   (syntax-rules (quote unquote unquote-splicing)
+    ((.finalize-quasiquote quote ?arg ?return)
+     (.interpret-continuation ?return (quote ?arg)))
+    ((.finalize-quasiquote unquote ?arg ?return)
+     (.interpret-continuation ?return ?arg))
+    ((.finalize-quasiquote unquote-splicing ?arg ?return)
+     (syntax-error ",@ in illegal context" ?arg))
+    ((.finalize-quasiquote ?mode ?arg ?return)
+     (.interpret-continuation ?return (?mode . ?arg)))))
+ ; The first two "arguments" to .descend-quasiquote and to
+ ; .descend-quasiquote-pair are always identical.
+ (define-syntax .descend-quasiquote letrec
+   (syntax-rules (quasiquote unquote unquote-splicing)
+    ((.descend-quasiquote `?y ?x ?level ?return)
+     (.descend-quasiquote-pair ?x ?x (?level) ?return))
+    ((.descend-quasiquote ,?y ?x () ?return)
+     (.interpret-continuation ?return unquote ?y))
+    ((.descend-quasiquote ,?y ?x (?level) ?return)
+     (.descend-quasiquote-pair ?x ?x ?level ?return))
+    ((.descend-quasiquote ,@?y ?x () ?return)
+     (.interpret-continuation ?return unquote-splicing ?y))
+    ((.descend-quasiquote ,@?y ?x (?level) ?return)
+     (.descend-quasiquote-pair ?x ?x ?level ?return))
+    ((.descend-quasiquote (?y . ?z) ?x ?level ?return)
+     (.descend-quasiquote-pair ?x ?x ?level ?return))
+    ((.descend-quasiquote #(?y ...) ?x ?level ?return)
+     (.descend-quasiquote-vector ?x ?x ?level ?return))
+    ((.descend-quasiquote ?y ?x ?level ?return)
+     (.interpret-continuation ?return quote ?x))))
+ (define-syntax .descend-quasiquote-pair letrec
+   (syntax-rules (quote unquote unquote-splicing)
+    ((.descend-quasiquote-pair (?carx . ?cdrx) ?x ?level ?return)
+     (.descend-quasiquote ?carx ?carx ?level (1 ?cdrx ?x ?level ?return)))))
+ (define-syntax .descend-quasiquote-vector letrec
+   (syntax-rules (quote)
+    ((.descend-quasiquote-vector #(?y ...) ?x ?level ?return)
+     (.descend-quasiquote (?y ...) (?y ...) ?level (6 ?x ?return)))))
+ ; Representations for continuations used here.
+ ; Continuation types 0, 1, 2, and 6 take a mode and an expression.
+ ; Continuation types -1, 3, 4, 5, and 7 take just an expression.
+ ;
+ ; (-1)
+ ;     means no continuation
+ ; (0)
+ ;     means to call .finalize-quasiquote with no further continuation
+ ; (1 ?cdrx ?x ?level ?return)
+ ;     means a return from the call to .descend-quasiquote from
+ ;     .descend-quasiquote-pair
+ ; (2 ?car-mode ?car-arg ?x ?return)
+ ;     means a return from the second call to .descend-quasiquote in
+ ;     in Jonathan's code for .descend-quasiquote-pair
+ ; (3 ?car-arg ?return)
+ ;     means take the result and return an append of ?car-arg with it
+ ; (4 ?cdr-mode ?cdr-arg ?return)
+ ;     means take the result and call .finalize-quasiquote on ?cdr-mode
+ ;     and ?cdr-arg with a continuation of type 5
+ ; (5 ?car-result ?return)
+ ;     means take the result and return a cons of ?car-result onto it
+ ; (6 ?x ?return)
+ ;     means a return from the call to .descend-quasiquote from
+ ;     .descend-quasiquote-vector
+ ; (7 ?return)
+ ;     means take the result and return a call of list->vector on it
+ (define-syntax .interpret-continuation letrec
+   (syntax-rules (quote unquote unquote-splicing)
+    ((.interpret-continuation (-1) ?e) ?e)
+    ((.interpret-continuation (0) ?mode ?arg)
+     (.finalize-quasiquote ?mode ?arg (-1)))    
+    ((.interpret-continuation (1 ?cdrx ?x ?level ?return) ?car-mode ?car-arg)
+     (.descend-quasiquote ?cdrx
+                          ?cdrx
+                          ?level
+                          (2 ?car-mode ?car-arg ?x ?return)))    
+    ((.interpret-continuation (2 quote ?car-arg ?x ?return) quote ?cdr-arg)
+     (.interpret-continuation ?return quote ?x))    
+    ((.interpret-continuation (2 unquote-splicing ?car-arg ?x ?return) quote ())
+     (.interpret-continuation ?return unquote ?car-arg))
+    ((.interpret-continuation (2 unquote-splicing ?car-arg ?x ?return)
+                              ?cdr-mode ?cdr-arg)
+     (.finalize-quasiquote ?cdr-mode ?cdr-arg (3 ?car-arg ?return)))  
+    ((.interpret-continuation (2 ?car-mode ?car-arg ?x ?return)
+                              ?cdr-mode ?cdr-arg)
+     (.finalize-quasiquote ?car-mode ?car-arg (4 ?cdr-mode ?cdr-arg ?return)))
+      
+    ((.interpret-continuation (3 ?car-arg ?return) ?e)
+     (.interpret-continuation ?return append (?car-arg ?e)))
+    ((.interpret-continuation (4 ?cdr-mode ?cdr-arg ?return) ?e1)
+     (.finalize-quasiquote ?cdr-mode ?cdr-arg (5 ?e1 ?return)))
+    ((.interpret-continuation (5 ?e1 ?return) ?e2)
+     (.interpret-continuation ?return .cons (?e1 ?e2)))
+    ((.interpret-continuation (6 ?x ?return) quote ?arg)
+     (.interpret-continuation ?return quote ?x))
+    ((.interpret-continuation (6 ?x ?return) ?mode ?arg)
+     (.finalize-quasiquote ?mode ?arg (7 ?return)))
+    ((.interpret-continuation (7 ?return) ?e)
+     (.interpret-continuation ?return .list->vector (?e)))))
+ (define-syntax quasiquote letrec
+   (syntax-rules ()
+    ((quasiquote ?x)
+     (.descend-quasiquote ?x ?x () (0)))))
+ )
+
+(define-syntax let*-syntax
+  (syntax-rules ()
+    ((let*-syntax () ?body)
+     (let-syntax () ?body))
+    ((let*-syntax ((?name1 ?val1) (?name ?val) ...) ?body)
+     (let-syntax ((?name1 ?val1)) (let*-syntax ((?name ?val) ...) ?body)))))
+
+
+            ))
+
+(define-syntax-scope 'letrec)
+
+(define standard-syntactic-environment
+  (syntactic-copy global-syntactic-environment))
+
+(define (make-standard-syntactic-environment)
+  (syntactic-copy standard-syntactic-environment))
+; Copyright 1998 William Clinger.
+;
+; $Id: twobit.sch,v 1.3 1999/08/23 19:14:26 lth Exp $
+;
+; 25 April 1999
+;
+; Given an expression in the subset of Scheme used as an intermediate language
+; by Twobit, returns a newly allocated copy of the expression in which the
+; local variables have been renamed and the referencing information has been
+; recomputed.
+
+(define (copy-exp exp)
+  
+  (define special-names (cons name:IGNORED argument-registers))
+  
+  (define original-names (make-hashtable symbol-hash assq))
+  
+  (define renaming-counter 0)
+  
+  (define (rename-vars vars)
+    (let ((rename (make-rename-procedure)))
+      (map (lambda (var)
+             (cond ((memq var special-names)
+                    var)
+                   ((hashtable-get original-names var)
+                    (rename var))
+                   (else
+                    (hashtable-put! original-names var #t)
+                    var)))
+           vars)))
+  
+  (define (rename-formals formals newnames)
+    (cond ((null? formals) '())
+          ((symbol? formals) (car newnames))
+          ((memq (car formals) special-names)
+           (cons (car formals)
+                 (rename-formals (cdr formals)
+                                 (cdr newnames))))
+          (else (cons (car newnames)
+                      (rename-formals (cdr formals)
+                                      (cdr newnames))))))
+  
+  ; Environments that map symbols to arbitrary information.
+  ; This data type is mutable, and uses the shallow binding technique.
+  
+  (define (make-env) (make-hashtable symbol-hash assq))
+  
+  (define (env-bind! env sym info)
+    (let ((stack (hashtable-get env sym)))
+      (hashtable-put! env sym (cons info stack))))
+  
+  (define (env-unbind! env sym)
+    (let ((stack (hashtable-get env sym)))
+      (hashtable-put! env sym (cdr stack))))
+  
+  (define (env-lookup env sym default)
+    (let ((stack (hashtable-get env sym)))
+      (if stack
+          (car stack)
+          default)))
+  
+  (define (env-bind-multiple! env symbols infos)
+    (for-each (lambda (sym info) (env-bind! env sym info))
+              symbols
+              infos))
+  
+  (define (env-unbind-multiple! env symbols)
+    (for-each (lambda (sym) (env-unbind! env sym))
+              symbols))
+  
+  ;
+  
+  (define (lexical-lookup R-table name)
+    (assq name R-table))
+  
+  (define (copy exp env notepad R-table)
+    (cond ((constant? exp) exp)
+          ((lambda? exp)
+           (let* ((bvl (make-null-terminated (lambda.args exp)))
+                  (newnames (rename-vars bvl))
+                  (procnames (map def.lhs (lambda.defs exp)))
+                  (newprocnames (rename-vars procnames))
+                  (refinfo (map (lambda (var)
+                                  (make-R-entry var '() '() '()))
+                                (append newnames newprocnames)))
+                  (newexp
+                   (make-lambda
+                    (rename-formals (lambda.args exp) newnames)
+                    '()
+                    refinfo
+                    '()
+                    '()
+                    (lambda.decls exp)
+                    (lambda.doc exp)
+                    (lambda.body exp))))
+             (env-bind-multiple! env procnames newprocnames)
+             (env-bind-multiple! env bvl newnames)
+             (for-each (lambda (entry)
+                         (env-bind! R-table (R-entry.name entry) entry))
+                       refinfo)
+             (notepad-lambda-add! notepad newexp)
+             (let ((newnotepad (make-notepad notepad)))
+               (for-each (lambda (name rhs)
+                           (lambda.defs-set!
+                             newexp
+                             (cons (make-definition
+                                    name
+                                    (copy rhs env newnotepad R-table))
+                                   (lambda.defs newexp))))
+                         (reverse newprocnames)
+                         (map def.rhs
+                              (reverse (lambda.defs exp))))
+               (lambda.body-set!
+                 newexp
+                 (copy (lambda.body exp) env newnotepad R-table))
+               (lambda.F-set! newexp (notepad-free-variables newnotepad))
+               (lambda.G-set! newexp (notepad-captured-variables newnotepad)))
+             (env-unbind-multiple! env procnames)
+             (env-unbind-multiple! env bvl)
+             (for-each (lambda (entry)
+                         (env-unbind! R-table (R-entry.name entry)))
+                       refinfo)
+             newexp))
+          ((assignment? exp)
+           (let* ((oldname (assignment.lhs exp))
+                  (name (env-lookup env oldname oldname))
+                  (varinfo (env-lookup R-table name #f))
+                  (newexp
+                   (make-assignment name
+                                    (copy (assignment.rhs exp) env notepad R-table))))
+             (notepad-var-add! notepad name)
+             (if varinfo
+                 (R-entry.assignments-set!
+                  varinfo
+                  (cons newexp (R-entry.assignments varinfo))))
+             newexp))
+          ((conditional? exp)
+           (make-conditional (copy (if.test exp) env notepad R-table)
+                             (copy (if.then exp) env notepad R-table)
+                             (copy (if.else exp) env notepad R-table)))
+          ((begin? exp)
+           (make-begin (map (lambda (exp) (copy exp env notepad R-table))
+                            (begin.exprs exp))))
+          ((variable? exp)
+           (let* ((oldname (variable.name exp))
+                  (name (env-lookup env oldname oldname))
+                  (varinfo (env-lookup R-table name #f))
+                  (newexp (make-variable name)))
+             (notepad-var-add! notepad name)
+             (if varinfo
+                 (R-entry.references-set!
+                  varinfo
+                  (cons newexp (R-entry.references varinfo))))
+             newexp))
+          ((call? exp)
+           (let ((newexp (make-call (copy (call.proc exp) env notepad R-table)
+                                    (map (lambda (exp)
+                                           (copy exp env notepad R-table))
+                                         (call.args exp)))))
+             (if (variable? (call.proc newexp))
+                 (let ((varinfo
+                        (env-lookup R-table
+                                    (variable.name
+                                     (call.proc newexp))
+                                    #f)))
+                   (if varinfo
+                       (R-entry.calls-set!
+                        varinfo
+                        (cons newexp (R-entry.calls varinfo))))))
+             (if (lambda? (call.proc newexp))
+                 (notepad-nonescaping-add! notepad (call.proc newexp)))
+             newexp))
+          (else ???)))
+  
+  (copy exp (make-env) (make-notepad #f) (make-env)))
+
+; For debugging.
+; Given an expression, traverses the expression to confirm
+; that the referencing invariants are correct.
+
+(define (check-referencing-invariants exp . flags)
+  
+  (let ((check-free-variables? (memq 'free flags))
+        (check-referencing? (memq 'reference flags))
+        (first-violation? #t))
+    
+    ; env is the list of enclosing lambda expressions,
+    ; beginning with the innermost.
+    
+    (define (check exp env)
+      (cond ((constant? exp) (return exp #t))
+            ((lambda? exp)
+             (let ((env (cons exp env)))
+               (return exp
+                       (and (every? (lambda (exp)
+                                      (check exp env))
+                                    (map def.rhs (lambda.defs exp)))
+                            (check (lambda.body exp) env)
+                            (if (and check-free-variables?
+                                     (not (null? env)))
+                                 (subset? (difference
+                                           (lambda.F exp)
+                                           (make-null-terminated
+                                            (lambda.args exp)))
+                                          (lambda.F (car env)))
+                                #t)
+                            (if check-referencing?
+                                (let ((env (cons exp env))
+                                      (R (lambda.R exp)))
+                                  (every? (lambda (formal)
+                                            (or (ignored? formal)
+                                                (R-entry R formal)))
+                                          (make-null-terminated
+                                           (lambda.args exp))))
+                                #t)))))
+            ((variable? exp)
+             (return exp
+                     (and (if (and check-free-variables?
+                                   (not (null? env)))
+                              (memq (variable.name exp)
+                                    (lambda.F (car env)))
+                              #t)
+                          (if check-referencing?
+                              (let ((Rinfo (lookup env (variable.name exp))))
+                                (if Rinfo
+                                    (memq exp (R-entry.references Rinfo))
+                                    #t))
+                              #t))))
+            ((assignment? exp)
+             (return exp
+                     (and (check (assignment.rhs exp) env)
+                          (if (and check-free-variables?
+                                   (not (null? env)))
+                              (memq (assignment.lhs exp)
+                                    (lambda.F (car env)))
+                              #t)
+                          (if check-referencing?
+                              (let ((Rinfo (lookup env (assignment.lhs exp))))
+                                (if Rinfo
+                                    (memq exp (R-entry.assignments Rinfo))
+                                    #t))
+                              #t))))
+            ((conditional? exp)
+             (return exp
+                     (and (check (if.test exp) env)
+                          (check (if.then exp) env)
+                          (check (if.else exp) env))))
+            ((begin? exp)
+             (return exp
+                     (every? (lambda (exp) (check exp env))
+                             (begin.exprs exp))))
+            ((call? exp)
+             (return exp
+                     (and (check (call.proc exp) env)
+                          (every? (lambda (exp) (check exp env))
+                                  (call.args exp))
+                          (if (and check-referencing?
+                                   (variable? (call.proc exp)))
+                              (let ((Rinfo (lookup env
+                                                   (variable.name 
+                                                    (call.proc exp)))))
+                                (if Rinfo
+                                    (memq exp (R-entry.calls Rinfo))
+                                    #t))
+                              #t))))
+            (else ???)))
+    
+    (define (return exp flag)
+      (cond (flag
+             #t)
+            (first-violation?
+             (set! first-violation? #f)
+             (display "Violation of referencing invariants")
+             (newline)
+             (pretty-print (make-readable exp))
+             #f)
+            (else (pretty-print (make-readable exp))
+                  #f)))
+    
+    (define (lookup env I)
+      (if (null? env)
+          #f
+          (let ((Rinfo (R-entry (lambda.R (car env)) I)))
+            (or Rinfo
+                (lookup (cdr env) I)))))
+    
+    (if (null? flags)
+        (begin (set! check-free-variables? #t)
+               (set! check-referencing? #t)))
+    
+    (check exp '())))
+
+
+; Calculating the free variable information for an expression
+; as output by pass 2.  This should be faster than computing both
+; the free variables and the referencing information.
+
+(define (compute-free-variables! exp)
+  
+  (define empty-set (make-set '()))
+  
+  (define (singleton x) (list x))
+  
+  (define (union2 x y) (union x y))
+  (define (union3 x y z) (union x y z))
+  
+  (define (set->list set) set)
+  
+  (define (free exp)
+    (cond ((constant? exp) empty-set)
+          ((lambda? exp)
+           (let* ((defs (lambda.defs exp))
+                  (formals (make-set
+                            (make-null-terminated (lambda.args exp))))
+                  (defined (make-set (map def.lhs defs)))
+                  (Fdefs
+                   (apply-union
+                    (map (lambda (def)
+                           (free (def.rhs def)))
+                         defs)))
+                  (Fbody (free (lambda.body exp)))
+                  (F (union2 Fdefs Fbody)))
+             (lambda.F-set! exp (set->list F))
+             (lambda.G-set! exp (set->list F))
+             (difference F (union2 formals defined))))
+          ((assignment? exp)
+           (union2 (make-set (list (assignment.lhs exp)))
+                   (free (assignment.rhs exp))))
+          ((conditional? exp)
+           (union3 (free (if.test exp))
+                   (free (if.then exp))
+                   (free (if.else exp))))
+          ((begin? exp)
+           (apply-union
+            (map (lambda (exp) (free exp))
+                 (begin.exprs exp))))
+          ((variable? exp)
+           (singleton (variable.name exp)))
+          ((call? exp)
+           (union2 (free (call.proc exp))
+                   (apply-union
+                    (map (lambda (exp) (free exp))
+                         (call.args exp)))))
+          (else ???)))
+  
+  (free exp))
+
+; As above, but representing sets as hashtrees.
+; This is commented out because it is much slower than the implementation
+; above.  Because the set of free variables is represented as a list
+; within a lambda expression, this implementation must convert the
+; representation for every lambda expression, which is quite expensive
+; for A-normal form.
+
+(begin
+'
+(define (compute-free-variables! exp)
+  
+  (define empty-set (make-hashtree symbol-hash assq))
+  
+  (define (singleton x)
+    (hashtree-put empty-set x #t))
+  
+  (define (make-set values)
+    (if (null? values)
+        empty-set
+        (hashtree-put (make-set (cdr values))
+                      (car values)
+                      #t)))
+  
+  (define (union2 x y)
+    (hashtree-for-each (lambda (key val)
+                         (set! x (hashtree-put x key #t)))
+                       y)
+    x)
+  
+  (define (union3 x y z)
+    (union2 (union2 x y) z))
+  
+  (define (apply-union sets)
+    (cond ((null? sets)
+           (make-set '()))
+          ((null? (cdr sets))
+           (car sets))
+          (else
+           (union2 (car sets)
+                   (apply-union (cdr sets))))))
+  
+  (define (difference x y)
+    (hashtree-for-each (lambda (key val)
+                         (set! x (hashtree-remove x key)))
+                       y)
+    x)
+  
+  (define (set->list set)
+    (hashtree-map (lambda (sym val) sym) set))
+  
+  (define (free exp)
+    (cond ((constant? exp) empty-set)
+          ((lambda? exp)
+           (let* ((defs (lambda.defs exp))
+                  (formals (make-set
+                            (make-null-terminated (lambda.args exp))))
+                  (defined (make-set (map def.lhs defs)))
+                  (Fdefs
+                   (apply-union
+                    (map (lambda (def)
+                           (free (def.rhs def)))
+                         defs)))
+                  (Fbody (free (lambda.body exp)))
+                  (F (union2 Fdefs Fbody)))
+             (lambda.F-set! exp (set->list F))
+             (lambda.G-set! exp (set->list F))
+             (difference F (union2 formals defined))))
+          ((assignment? exp)
+           (union2 (make-set (list (assignment.lhs exp)))
+                   (free (assignment.rhs exp))))
+          ((conditional? exp)
+           (union3 (free (if.test exp))
+                   (free (if.then exp))
+                   (free (if.else exp))))
+          ((begin? exp)
+           (apply-union
+            (map (lambda (exp) (free exp))
+                 (begin.exprs exp))))
+          ((variable? exp)
+           (singleton (variable.name exp)))
+          ((call? exp)
+           (union2 (free (call.proc exp))
+                   (apply-union
+                    (map (lambda (exp) (free exp))
+                         (call.args exp)))))
+          (else ???)))
+  
+  (hashtree-map (lambda (sym val) sym)
+                (free exp)))
+#t); Copyright 1991 William Clinger
+;
+; $Id: twobit.sch,v 1.3 1999/08/23 19:14:26 lth Exp $
+;
+; 24 April 1999
+;
+; First pass of the Twobit compiler:
+;   macro expansion, syntax checking, alpha conversion,
+;   preliminary annotation.
+;
+; The input to this pass is a Scheme definition or expression.
+; The output is an expression in the subset of Scheme described
+; by the following grammar, where the output satisfies certain
+; additional invariants described below.
+;
+; "X ..." means zero or more occurrences of X.
+;
+; L  -->  (lambda (I_1 ...)
+;           (begin D ...)
+;           (quote (R F G <decls> <doc>)
+;           E)
+;      |  (lambda (I_1 ... . I_rest)
+;           (begin D ...)
+;           (quote (R F <decls> <doc>))
+;           E)
+; D  -->  (define I L)
+; E  -->  (quote K)                        ; constants
+;      |  (begin I)                        ; variable references
+;      |  L                                ; lambda expressions
+;      |  (E0 E1 ...)                      ; calls
+;      |  (set! I E)                       ; assignments
+;      |  (if E0 E1 E2)                    ; conditionals
+;      |  (begin E0 E1 E2 ...)             ; sequential expressions
+; I  -->  <identifier>
+;
+; R  -->  ((I <references> <assignments> <calls>) ...)
+; F  -->  (I ...)
+; G  -->  (I ...)
+;
+; Invariants that hold for the output:
+;   *  There are no internal definitions.
+;   *  No identifier containing an upper case letter is bound anywhere.
+;      (Change the "name:..." variables if upper case is preferred.)
+;   *  No identifier is bound in more than one place.
+;   *  Each R contains one entry for every identifier bound in the
+;      formal argument list and the internal definition list that
+;      precede it.  Each entry contains a list of pointers to all
+;      references to the identifier, a list of pointers to all
+;      assignments to the identifier, and a list of pointers to all
+;      calls to the identifier.
+;   *  Except for constants, the expression does not share structure
+;      with the original input or itself, except that the references
+;      and assignments in R are guaranteed to share structure with
+;      the expression.  Thus the expression may be side effected, and
+;      side effects to references or assignments obtained through R
+;      are guaranteed to change the references or assignments pointed
+;      to by R.
+;   *  F and G are garbage.
+
+($$trace "pass1")
+
+(define source-file-name #f)
+(define source-file-position #f)
+
+(define pass1-block-compiling? #f)
+(define pass1-block-assignments '())
+(define pass1-block-inlines '())
+
+(define (pass1 def-or-exp . rest)
+  (set! source-file-name #f)
+  (set! source-file-position #f)
+  (set! pass1-block-compiling? #f)
+  (set! pass1-block-assignments '())
+  (set! pass1-block-inlines '())
+  (if (not (null? rest))
+      (begin (set! source-file-name (car rest))
+             (if (not (null? (cdr rest)))
+                 (set! source-file-position (cadr rest)))))
+  (set! renaming-counter 0)
+  (macro-expand def-or-exp))
+
+; Compiles a whole sequence of top-level forms on the assumption
+; that no variable that is defined by a form in the sequence is
+; ever defined or assigned outside of the sequence.
+;
+; This is a crock in three parts:
+;
+;    1.  Macro-expand each form and record assignments.
+;    2.  Find the top-level variables that are defined but not
+;        assigned, give them local names, generate a DEFINE-INLINE
+;        for each of the top-level procedures, and macro-expand
+;        each form again.
+;    3.  Wrap the whole mess in an appropriate LET and recompute
+;        the referencing information by copying it.
+;
+; Note that macros get expanded twice, and that all DEFINE-SYNTAX
+; macros are considered local to the forms.
+
+; FIXME: Need to turn off warning messages.
+
+(define (pass1-block forms . rest)
+  
+  (define (part1)
+    (set! pass1-block-compiling? #t)
+    (set! pass1-block-assignments '())
+    (set! pass1-block-inlines '())
+    (set! renaming-counter 0)
+    (let ((env0 (syntactic-copy global-syntactic-environment))
+          (bmode (benchmark-mode))
+          (wmode (issue-warnings))
+          (defined '()))
+      (define (make-toplevel-definition id exp)
+        (cond ((memq id defined)
+               (set! pass1-block-assignments
+                     (cons id pass1-block-assignments)))
+              ((or (constant? exp)
+                   (and (lambda? exp)
+                        (list? (lambda.args exp))))
+               (set! defined (cons id defined))))
+        (make-begin
+         (list (make-assignment id exp)
+               (make-constant id))))
+      (benchmark-mode #f)
+      (issue-warnings #f)
+      (for-each (lambda (form)
+                  (desugar-definitions form
+                                       global-syntactic-environment
+                                       make-toplevel-definition))
+                forms)
+      (set! global-syntactic-environment env0)
+      (benchmark-mode bmode)
+      (issue-warnings wmode)
+      (part2 (filter (lambda (id)
+                       (not (memq id pass1-block-assignments)))
+                     (reverse defined)))))
+  
+  (define (part2 defined)
+    (set! pass1-block-compiling? #f)
+    (set! pass1-block-assignments '())
+    (set! pass1-block-inlines '())
+    (set! renaming-counter 0)
+    (let* ((rename (make-rename-procedure))
+           (alist (map (lambda (id)
+                         (cons id (rename id)))
+                       defined))
+           (definitions0 '())    ; for constants
+           (definitions1 '()))   ; for lambda expressions
+      (define (make-toplevel-definition id exp)
+        (if (lambda? exp)
+            (doc.name-set! (lambda.doc exp) id))
+        (let ((probe (assq id alist)))
+          (if probe
+              (let ((id1 (cdr probe)))
+                (cond ((constant? exp)
+                       (set! definitions0
+                             (cons (make-assignment id exp)
+                                   definitions0))
+                       (make-constant id))
+                      ((lambda? exp)
+                       (set! definitions1
+                             (cons (make-assignment id1 exp)
+                                   definitions1))
+                       (make-assignment
+                        id
+                        (make-lambda (lambda.args exp)
+                                     '() ; no definitions
+                                     '() ; R
+                                     '() ; F
+                                     '() ; G
+                                     '() ; decls
+                                     (lambda.doc exp)
+                                     (make-call
+                                      (make-variable id1)
+                                      (map make-variable
+                                           (lambda.args exp))))))
+                      (else
+                       (m-error "Inconsistent macro expansion"
+                                (make-readable exp)))))
+              (make-assignment id exp))))
+      (let ((env0 (syntactic-copy global-syntactic-environment))
+            (bmode (benchmark-mode))
+            (wmode (issue-warnings)))
+        (issue-warnings #f)
+        (for-each (lambda (pair)
+                    (let ((id0 (car pair))
+                          (id1 (cdr pair)))
+                      (syntactic-bind-globally!
+                       id0
+                       (make-inline-denotation
+                        id0
+                        (lambda (exp rename compare)
+                          ; Deliberately non-hygienic!
+                          (cons id1 (cdr exp)))
+                        global-syntactic-environment))
+                      (set! pass1-block-inlines
+                            (cons id0 pass1-block-inlines))))
+                  alist)
+        (benchmark-mode #f)
+        (issue-warnings wmode)
+        (let ((forms
+               (do ((forms forms (cdr forms))
+                    (newforms '()
+                              (cons (desugar-definitions
+                                     (car forms)
+                                     global-syntactic-environment
+                                     make-toplevel-definition)
+                                    newforms)))
+                   ((null? forms)
+                    (reverse newforms)))))
+          (benchmark-mode bmode)
+          (set! global-syntactic-environment env0)
+          (part3 alist definitions0 definitions1 forms)))))
+  
+  (define (part3 alist definitions0 definitions1 forms)
+    (set! pass1-block-compiling? #f)
+    (set! pass1-block-assignments '())
+    (set! pass1-block-inlines '())
+    (let* ((constnames0 (map assignment.lhs definitions0))
+           (constnames1 (map (lambda (id0)
+                               (cdr (assq id0 alist)))
+                             constnames0))
+           (procnames1 (map assignment.lhs definitions1)))
+      (copy-exp
+       (make-call
+        (make-lambda
+         constnames1
+         '() ; no definitions
+         '() ; R
+         '() ; F
+         '() ; G
+         '() ; decls
+         #f  ; doc
+         (make-begin
+          (list
+           (make-begin
+            (cons (make-constant #f)
+                  (reverse
+                   (map (lambda (id)
+                          (make-assignment id (make-variable (cdr (assq id alist)))))
+                        constnames0))))
+           (make-call
+            (make-lambda
+             constnames0
+             '() ; no definitions
+             '() ; R
+             '() ; F
+             '() ; G
+             '() ; decls
+             #f  ; doc
+             (make-call
+              (make-lambda
+               (map assignment.lhs definitions1)
+               '() ; no definitions
+               '() ; R
+               '() ; F
+               '() ; G
+               '() ; decls
+               #f  ; doc
+               (make-begin (cons (make-constant #f)
+                                 (append definitions1 forms))))
+              (map (lambda (ignored) (make-unspecified))
+                   definitions1)))
+            (map make-variable constnames1))
+           )))
+        (map assignment.rhs definitions0)))))
+  
+  (set! source-file-name #f)
+  (set! source-file-position #f)
+  (if (not (null? rest))
+      (begin (set! source-file-name (car rest))
+             (if (not (null? (cdr rest)))
+                 (set! source-file-position (cadr rest)))))
+  (part1))
+; Copyright 1999 William D Clinger.
+;
+; Permission to copy this software, in whole or in part, to use this
+; software for any lawful noncommercial purpose, and to redistribute
+; this software is granted subject to the restriction that all copies
+; made of this software must include this copyright notice in full.
+;
+; I also request that you send me a copy of any improvements that you
+; make to this software so that they may be incorporated within it to
+; the benefit of the Scheme community.
+;
+; 7 June 1999.
+;
+; Support for intraprocedural value numbering:
+;     set of available expressions
+;     miscellaneous
+;
+; The set of available expressions is represented as a
+; mutable abstract data type Available with these operations:
+;
+; make-available-table:                                    -> Available
+; copy-available-table: Available                          -> Available
+; available-expression: Available x Expr                   -> (symbol + {#f})
+; available-variable:   Available x symbol                 -> Expr
+; available-extend!:    Available x symbol x Expr x Killer ->
+; available-kill!:      Available x Killer                 ->
+;
+; where Expr is of the form
+;
+; Expr  -->  W
+;         |  (W_0 W_1 ...)
+;
+; W  -->  (quote K)
+;      |  (begin I)
+;
+; and Killer is a fixnum, as defined later in this file.
+;
+; (make-available-table)
+;     returns an empty table of available expressions.
+; (copy-available-table available)
+;     copies the given table.
+; (available-expression available E)
+;     returns the name of E if it is available in the table, else #f.
+; (available-variable available T)
+;     returns a constant or variable to use in place of T, else #f.
+; (available-extend! available T E K)
+;     adds the binding (T E) to the table, with Killer K.
+;     If E is a variable and this binding is never killed, then copy
+;         propagation will replace uses of T by uses of E; otherwise
+;         commoning will replace uses of E by uses of T, until the
+;         binding is killed.
+; (available-kill! available K)
+;     removes all bindings whose Killer intersects K.
+;
+; (available-extend! available T E K) is very fast if the previous
+; operation on the table was (available-expression available E).
+
+; Implementation.
+;
+; Quick and dirty.
+; The available expressions are represented as a vector of 2 association
+; lists.  The first list is used for common subexpression elimination,
+; and the second is used for copy and constant propagation.
+;
+; Each element of the first list is a binding of
+; a symbol T to an expression E, with killer K,
+; represented by the list (E T K).
+;
+; Each element of the second list is a binding of
+; a symbol T to an expression E, with killer K,
+; represented by the list (T E K).
+; The expression E will be a constant or variable.
+
+(define (make-available-table)
+  (vector '() '()))
+
+(define (copy-available-table available)
+  (vector (vector-ref available 0)
+          (vector-ref available 1)))
+
+(define (available-expression available E)
+  (let ((binding (assoc E (vector-ref available 0))))
+    (if binding
+        (cadr binding)
+        #f)))
+
+(define (available-variable available T)
+  (let ((binding (assq T (vector-ref available 1))))
+    (if binding
+        (cadr binding)
+        #f)))
+
+(define (available-extend! available T E K)
+  (cond ((constant? E)
+         (vector-set! available
+                      1
+                      (cons (list T E K)
+                            (vector-ref available 1))))
+        ((and (variable? E)
+              (eq? K available:killer:none))
+         (vector-set! available
+                      1
+                      (cons (list T E K)
+                            (vector-ref available 1))))
+        (else
+         (vector-set! available
+                      0
+                      (cons (list E T K)
+                            (vector-ref available 0))))))
+
+(define (available-kill! available K)
+  (vector-set! available
+               0
+               (filter (lambda (binding)
+                         (zero?
+                          (logand K
+                                  (caddr binding))))
+                       (vector-ref available 0)))
+  (vector-set! available
+               1
+               (filter (lambda (binding)
+                         (zero?
+                          (logand K
+                                  (caddr binding))))
+                       (vector-ref available 1))))
+
+(define (available-intersect! available0 available1 available2)
+  (vector-set! available0
+               0
+               (intersection (vector-ref available1 0)
+                             (vector-ref available2 0)))
+  (vector-set! available0
+               1
+               (intersection (vector-ref available1 1)
+                             (vector-ref available2 1))))
+
+; The Killer concrete data type, represented as a fixnum.
+;
+; The set of side effects that can kill an available expression
+; are a subset of
+;
+; assignments to global variables
+; uses of SET-CAR!
+; uses of SET-CDR!
+; uses of STRING-SET!
+; uses of VECTOR-SET!
+;
+; This list is not complete.  If we were trying to perform common
+; subexpression elimination on calls to PEEK-CHAR, for example,
+; then those calls would be killed by reads.
+
+(define available:killer:globals   2)
+(define available:killer:car       4)
+(define available:killer:cdr       8)
+(define available:killer:string   16) ; also bytevectors etc
+(define available:killer:vector   32) ; also structures etc
+(define available:killer:cell     64)
+(define available:killer:io      128)
+(define available:killer:none      0) ; none of the above
+(define available:killer:all    1022) ; all of the above
+
+(define available:killer:immortal  0) ; never killed
+(define available:killer:dead   1023) ; never available
+
+
+
+(define (available:killer-combine k1 k2)
+  (logior k1 k2))
+
+; Miscellaneous.
+
+; A simple lambda expression has no internal definitions at its head
+; and no declarations aside from A-normal form.
+
+(define (simple-lambda? L)
+  (and (null? (lambda.defs L))
+       (every? (lambda (decl)
+                 (eq? decl A-normal-form-declaration))
+               (lambda.decls L))))
+
+; A real call is a call whose procedure expression is
+; neither a lambda expression nor a primop.
+
+(define (real-call? E)
+  (and (call? E)
+       (let ((proc (call.proc E)))
+         (and (not (lambda? proc))
+              (or (not (variable? proc))
+                  (let ((f (variable.name proc)))
+                    (or (not (integrate-usual-procedures))
+                        (not (prim-entry f)))))))))
+
+(define (prim-call E)
+  (and (call? E)
+       (let ((proc (call.proc E)))
+         (and (variable? proc)
+              (integrate-usual-procedures)
+              (prim-entry (variable.name proc))))))
+
+(define (no-side-effects? E)
+  (or (constant? E)
+      (variable? E)
+      (lambda? E)
+      (and (conditional? E)
+           (no-side-effects? (if.test E))
+           (no-side-effects? (if.then E))
+           (no-side-effects? (if.else E)))
+      (and (call? E)
+           (let ((proc (call.proc E)))
+             (and (variable? proc)
+                  (integrate-usual-procedures)
+                  (let ((entry (prim-entry (variable.name proc))))
+                    (and entry
+                         (not (eq? available:killer:dead
+                                   (prim-lives-until entry))))))))))
+
+; Given a local variable, the expression within its scope, and
+; a list of local variables that are known to be used only once,
+; returns #t if the variable is used only once.
+;
+; The purpose of this routine is to recognize temporaries that
+; may once have had two or more uses because of CSE, but now have
+; only one use because of further CSE followed by dead code elimination.
+
+(define (temporary-used-once? T E used-once)
+  (cond ((call? E)
+         (let ((proc (call.proc E))
+               (args (call.args E)))
+           (or (and (lambda? proc)
+                    (not (memq T (lambda.F proc)))
+                    (and (pair? args)
+                         (null? (cdr args))
+                         (temporary-used-once? T (car args) used-once)))
+               (do ((exprs (cons proc (call.args E))
+                           (cdr exprs))
+                    (n     0
+                           (let ((exp (car exprs)))
+                             (cond ((constant? exp)
+                                    n)
+                                   ((variable? exp)
+                                    (if (eq? T (variable.name exp))
+                                        (+ n 1)
+                                        n))
+                                   (else
+                                    ; Terminate the loop and return #f.
+                                    2)))))
+                   ((or (null? exprs)
+                        (> n 1))
+                    (= n 1))))))
+        (else
+         (memq T used-once))))
+
+; Register bindings.
+
+(define (make-regbinding lhs rhs use)
+  (list lhs rhs use))
+
+(define (regbinding.lhs x) (car x))
+(define (regbinding.rhs x) (cadr x))
+(define (regbinding.use x) (caddr x))
+
+; Given a list of register bindings, an expression E and its free variables F,
+; returns two values:
+;     E with the register bindings wrapped around it
+;     the free variables of the wrapped expression
+
+(define (wrap-with-register-bindings regbindings E F)
+  (if (null? regbindings)
+      (values E F)
+      (let* ((regbinding (car regbindings))
+             (R (regbinding.lhs regbinding))
+             (x (regbinding.rhs regbinding)))
+        (wrap-with-register-bindings
+         (cdr regbindings)
+         (make-call (make-lambda (list R)
+                                 '()
+                                 '()
+                                 F
+                                 F
+                                 (list A-normal-form-declaration)
+                                 #f
+                                 E)
+                    (list (make-variable x)))
+         (union (list x)
+                (difference F (list R)))))))
+
+; Returns two values:
+;   the subset of regbindings that have x as their right hand side
+;   the rest of regbindings
+
+(define (register-bindings regbindings x)
+  (define (loop regbindings to-x others)
+    (cond ((null? regbindings)
+           (values to-x others))
+          ((eq? x (regbinding.rhs (car regbindings)))
+           (loop (cdr regbindings)
+                 (cons (car regbindings) to-x)
+                 others))
+          (else
+           (loop (cdr regbindings)
+                 to-x
+                 (cons (car regbindings) others)))))
+  (loop regbindings '() '()))
+
+; This procedure is called when the compiler can tell that an assertion
+; is never true.
+
+(define (declaration-error E)
+  (if (issue-warnings)
+      (begin (display "WARNING: Assertion is false: ")
+             (write (make-readable E #t))
+             (newline))))
+; Representations, which form a subtype hierarchy.
+;
+; <rep>  ::=  <fixnum>  |  (<fixnum> <datum> ...)
+;
+; (<rep> <datum> ...) is a subtype of <rep>, but the non-fixnum
+; representations are otherwise interpreted by arbitrary code.
+
+(define *nreps* 0)
+(define *rep-encodings* '())
+(define *rep-decodings* '())
+(define *rep-subtypes* '())
+(define *rep-joins* (make-bytevector 0))
+(define *rep-meets* (make-bytevector 0))
+(define *rep-joins-special* '#())
+(define *rep-meets-special* '#())
+
+(define (representation-error msg . stuff)
+  (apply error
+         (if (string? msg)
+             (string-append "Bug in flow analysis: " msg)
+             msg)
+         stuff))
+
+(define (symbol->rep sym)
+  (let ((probe (assq sym *rep-encodings*)))
+    (if probe
+        (cdr probe)
+        (let ((rep *nreps*))
+          (set! *nreps* (+ *nreps* 1))
+          (if (> *nreps* 255)
+              (representation-error "Too many representation types"))
+          (set! *rep-encodings*
+                (cons (cons sym rep)
+                      *rep-encodings*))
+          (set! *rep-decodings*
+                (cons (cons rep sym)
+                      *rep-decodings*))
+          rep))))
+
+(define (rep->symbol rep)
+  (if (pair? rep)
+      (cons (rep->symbol (car rep)) (cdr rep))
+      (let ((probe (assv rep *rep-decodings*)))
+        (if probe
+            (cdr probe)
+            'unknown))))
+
+(define (representation-table table)
+  (map (lambda (row)
+         (map (lambda (x)
+                (if (list? x)
+                    (map symbol->rep x)
+                    x))
+              row))
+       table))
+
+; DEFINE-SUBTYPE is how representation types are defined.
+
+(define (define-subtype sym1 sym2)
+  (let* ((rep2 (symbol->rep sym2))
+         (rep1 (symbol->rep sym1)))
+    (set! *rep-subtypes*
+          (cons (cons rep1 rep2)
+                *rep-subtypes*))
+    sym1))
+
+; COMPUTE-TYPE-STRUCTURE! must be called before DEFINE-INTERSECTION.
+
+(define (define-intersection sym1 sym2 sym3)
+  (let ((rep1 (symbol->rep sym1))
+        (rep2 (symbol->rep sym2))
+        (rep3 (symbol->rep sym3)))
+    (representation-aset! *rep-meets* rep1 rep2 rep3)
+    (representation-aset! *rep-meets* rep2 rep1 rep3)))
+
+;
+
+(define (representation-aref bv i j)
+  (bytevector-ref bv (+ (* *nreps* i) j)))
+
+(define (representation-aset! bv i j x)
+  (bytevector-set! bv (+ (* *nreps* i) j) x))
+
+(define (compute-unions!)
+  
+  ; Always define a bottom element.
+  
+  (for-each (lambda (sym)
+              (define-subtype 'bottom sym))
+            (map car *rep-encodings*))
+  
+  (let* ((debugging? #f)
+         (n *nreps*)
+         (n^2 (* n n))
+         (matrix (make-bytevector n^2)))
+    
+    ; This code assumes there will always be a top element.
+    
+    (define (lub rep1 rep2 subtype?)
+      (do ((i 0 (+ i 1))
+           (bounds '()
+                   (if (and (subtype? rep1 i)
+                            (subtype? rep2 i))
+                       (cons i bounds)
+                       bounds)))
+          ((= i n)
+           (car (twobit-sort subtype? bounds)))))
+    
+    (define (join i j)
+      (lub i j (lambda (rep1 rep2)
+                 (= 1 (representation-aref matrix rep1 rep2)))))
+    
+    (define (compute-transitive-closure!)
+      (let ((changed? #f))
+        (define (loop)
+          (do ((i 0 (+ i 1)))
+              ((= i n))
+              (do ((k 0 (+ k 1)))
+                  ((= k n))
+                  (do ((j 0 (+ j 1))
+                       (sum 0
+                            (logior sum
+                                    (logand
+                                     (representation-aref matrix i j)
+                                     (representation-aref matrix j k)))))
+                      ((= j n)
+                       (if (> sum 0)
+                           (let ((x (representation-aref matrix i k)))
+                             (if (zero? x)
+                                 (begin
+                                  (set! changed? #t)
+                                  (representation-aset! matrix i k 1)))))))))
+          (if changed?
+              (begin (set! changed? #f)
+                     (loop))))
+        (loop)))
+    
+    (define (compute-joins!)
+      (let ((default (lambda (x y)
+                       (error "Compiler bug: special meet or join" x y))))
+        (set! *rep-joins-special* (make-vector n default))
+        (set! *rep-meets-special* (make-vector n default)))
+      (set! *rep-joins* (make-bytevector n^2))
+      (set! *rep-meets* (make-bytevector n^2))
+      (do ((i 0 (+ i 1)))
+          ((= i n))
+          (do ((j 0 (+ j 1)))
+              ((= j n))
+              (representation-aset! *rep-joins*
+                                    i
+                                    j
+                                    (join i j)))))
+    
+    (do ((i 0 (+ i 1)))
+        ((= i n))
+        (do ((j 0 (+ j 1)))
+            ((= j n))
+            (representation-aset! matrix i j 0))
+        (representation-aset! matrix i i 1))
+    (for-each (lambda (subtype)
+                (let ((rep1 (car subtype))
+                      (rep2 (cdr subtype)))
+                  (representation-aset! matrix rep1 rep2 1)))
+              *rep-subtypes*)
+    (compute-transitive-closure!)
+    (if debugging?
+        (do ((i 0 (+ i 1)))
+            ((= i n))
+            (do ((j 0 (+ j 1)))
+                ((= j n))
+                (write-char #\space)
+                (write (representation-aref matrix i j)))
+            (newline)))
+    (compute-joins!)
+    (set! *rep-subtypes* '())))
+
+; Intersections are not dual to unions because a conservative analysis
+; must always err on the side of the larger subtype.
+; COMPUTE-UNIONS! must be called before COMPUTE-INTERSECTIONS!.
+
+(define (compute-intersections!)
+  (let ((n *nreps*))
+    
+    (define (meet i j)
+      (let ((k (representation-union i j)))
+        (if (= i k)
+            j
+            i)))
+    
+    (do ((i 0 (+ i 1)))
+        ((= i n))
+        (do ((j 0 (+ j 1)))
+            ((= j n))
+            (representation-aset! *rep-meets*
+                                  i
+                                  j
+                                  (meet i j))))))
+
+(define (compute-type-structure!)
+  (compute-unions!)
+  (compute-intersections!))
+
+(define (representation-subtype? rep1 rep2)
+  (equal? rep2 (representation-union rep1 rep2)))
+
+(define (representation-union rep1 rep2)
+  (if (fixnum? rep1)
+      (if (fixnum? rep2)
+          (representation-aref *rep-joins* rep1 rep2)
+          (representation-union rep1 (car rep2)))
+      (if (fixnum? rep2)
+          (representation-union (car rep1) rep2)
+          (let ((r1 (car rep1))
+                (r2 (car rep2)))
+            (if (= r1 r2)
+                ((vector-ref *rep-joins-special* r1) rep1 rep2)
+                (representation-union r1 r2))))))
+
+(define (representation-intersection rep1 rep2)
+  (if (fixnum? rep1)
+      (if (fixnum? rep2)
+          (representation-aref *rep-meets* rep1 rep2)
+          (representation-intersection rep1 (car rep2)))
+      (if (fixnum? rep2)
+          (representation-intersection (car rep1) rep2)
+          (let ((r1 (car rep1))
+                (r2 (car rep2)))
+            (if (= r1 r2)
+                ((vector-ref *rep-meets-special* r1) rep1 rep2)
+                (representation-intersection r1 r2))))))
+
+; For debugging.
+
+(define (display-unions-and-intersections)
+  (let* ((column-width 10)
+         (columns/row (quotient 80 column-width)))
+    
+    (define (display-symbol sym)
+      (let* ((s (symbol->string sym))
+             (n (string-length s)))
+        (if (< n column-width)
+            (begin (display s)
+                   (display (make-string (- column-width n) #\space)))
+            (begin (display (substring s 0 (- column-width 1)))
+                   (write-char #\space)))))
+    
+    ; Display columns i to n.
+    
+    (define (display-matrix f i n)
+      (display (make-string column-width #\space))
+      (do ((i i (+ i 1)))
+          ((= i n))
+          (display-symbol (rep->symbol i)))
+      (newline)
+      (newline)
+      (do ((k 0 (+ k 1)))
+          ((= k *nreps*))
+          (display-symbol (rep->symbol k))
+          (do ((i i (+ i 1)))
+              ((= i n))
+              (display-symbol (rep->symbol (f k i))))
+          (newline))
+      (newline)
+      (newline))
+    
+    (display "Unions:")
+    (newline)
+    (newline)
+    
+    (do ((i 0 (+ i columns/row)))
+        ((>= i *nreps*))
+        (display-matrix representation-union
+                        i
+                        (min *nreps* (+ i columns/row))))
+    
+    (display "Intersections:")
+    (newline)
+    (newline)
+    
+    (do ((i 0 (+ i columns/row)))
+        ((>= i *nreps*))
+        (display-matrix representation-intersection
+                        i
+                        (min *nreps* (+ i columns/row))))))
+
+; Operations that can be specialized.
+;
+; Format: (<name> (<arg-rep> ...) <specific-name>)
+
+(define (rep-specific? f rs)
+  (rep-match f rs rep-specific caddr))
+
+; Operations whose result has some specific representation.
+;
+; Format: (<name> (<arg-rep> ...) (<result-rep>))
+
+(define (rep-result? f rs)
+  (rep-match f rs rep-result caaddr))
+
+; Unary predicates that give information about representation.
+;
+; Format: (<name> <rep-if-true> <rep-if-false>)
+
+(define (rep-if-true f rs)
+  (rep-match f rs rep-informing caddr))
+
+(define (rep-if-false f rs)
+  (rep-match f rs rep-informing cadddr))
+
+; Given the name of an integrable primitive,
+; the representations of its arguments,
+; a representation table, and a selector function
+; finds the most type-specific row of the table that matches both
+; the name of the primitive and the representations of its arguments,
+; and returns the result of applying the selector to that row.
+; If no row matches, then REP-MATCH returns #f.
+;
+; FIXME:  This should be more efficient, and should prefer the most
+; specific matches.
+
+(define (rep-match f rs table selector)
+  (let ((n (length rs)))
+    (let loop ((entries table))
+      (cond ((null? entries)
+             #f)
+            ((eq? f (car (car entries)))
+             (let ((rs0 (cadr (car entries))))
+               (if (and (= n (length rs0))
+                        (every? (lambda (r1+r2)
+                                  (let ((r1 (car r1+r2))
+                                        (r2 (cdr r1+r2)))
+                                    (representation-subtype? r1 r2)))
+                                (map cons rs rs0)))
+                   (selector (car entries))
+                   (loop (cdr entries)))))
+            (else
+             (loop (cdr entries)))))))
+
+; Abstract interpretation with respect to types and constraints.
+; Returns a representation type.
+
+(define (aeval E types constraints)
+  (cond ((call? E)
+         (let ((proc (call.proc E)))
+           (if (variable? proc)
+               (let* ((op (variable.name proc))
+                      (argtypes (map (lambda (E)
+                                       (aeval E types constraints))
+                                     (call.args E)))
+                      (type (rep-result? op argtypes)))
+                 (if type
+                     type
+                     rep:object))
+               rep:object)))
+        ((variable? E)
+         (representation-typeof (variable.name E) types constraints))
+        ((constant? E)
+         (representation-of-value (constant.value E)))
+        (else
+         rep:object)))
+
+; If x has representation type t0 in the hash table,
+; and some further constraints
+;
+;     x = (op y1 ... yn)
+;     x : t1
+;      ...
+;     x : tk
+;
+; then
+;
+;     typeof (x) = op (typeof (y1), ..., typeof (yn))
+;                  &  t0  &  t1  &  ...  &  tk
+;
+; where & means intersection and op is the abstraction of op.
+;
+; Also if T : true and T = E then E may give information about
+; the types of other variables.  Similarly for T : false.
+
+(define (representation-typeof name types constraints)
+  (let ((t0 (hashtable-fetch types name rep:object))
+        (cs (hashtable-fetch (constraints.table constraints) name '())))
+    (define (loop type cs)
+      (if (null? cs)
+          type
+          (let* ((c (car cs))
+                 (cs (cdr cs))
+                 (E (constraint.rhs c)))
+            (cond ((constant? E)
+                   (loop (representation-intersection type
+                                                      (constant.value E))
+                         cs))
+                  ((call? E)
+                   (loop (representation-intersection
+                          type (aeval E types constraints))
+                         cs))
+                  (else
+                   (loop type cs))))))
+    (loop t0 cs)))
+
+; Constraints.
+;
+; The constraints used by this analysis consist of type constraints
+; together with the available expressions used for commoning.
+;
+; (T E      K)   T = E     until killed by an effect in K
+; (T '<rep> K)   T : <rep> until killed by an effect in K
+
+(define (make-constraint T E K)
+  (list T E K))
+
+(define (constraint.lhs c)
+  (car c))
+
+(define (constraint.rhs c)
+  (cadr c))
+
+(define (constraint.killer c)
+  (caddr c))
+
+(define (make-type-constraint T type K)
+  (make-constraint T
+                   (make-constant type)
+                   K))
+
+; If the new constraint is of the form T = E until killed by K,
+; then there shouldn't be any prior constraints.
+;
+; Otherwise the new constraint is of the form T : t until killed by K.
+; Suppose the prior constraints are
+;     T = E  until killed by K
+;     T : t1 until killed by K1
+;      ...
+;     T : tn until killed by Kn
+;
+; If there exists i such that ti is a subtype of t and Ki a subset of K,
+; then the new constraint adds no new information and should be ignored.
+; Otherwise compute t' = t1 & ... & tn and K' = K1 | ... | Kn, where
+; & indicates intersection and | indicates union.
+; If K = K' then add the new constraint T : t' until killed by K;
+; otherwise add two new constraints:
+;     T : t' until killed by K'
+;     T : t  until killed by K
+
+(define (constraints-add! types constraints new)
+  (let* ((debugging? #f)
+         (T (constraint.lhs new))
+         (E (constraint.rhs new))
+         (K (constraint.killer new))
+         (cs (constraints-for-variable constraints T)))
+    
+    (define (loop type K cs newcs)
+      (if (null? cs)
+          (cons (make-type-constraint T type K) newcs)
+          (let* ((c2 (car cs))
+                 (cs (cdr cs))
+                 (E2 (constraint.rhs c2))
+                 (K2 (constraint.killer c2)))
+            (if (constant? E2)
+                (let* ((type2 (constant.value E2))
+                       (type3 (representation-intersection type type2)))
+                  (cond ((eq? type2 type3)
+                         (if (= K2 (logand K K2))
+                             (append newcs cs)
+                             (loop (representation-intersection type type2)
+                                   (available:killer-combine K K2)
+                                   cs
+                                   (cons c2 newcs))))
+                        ((representation-subtype? type type3)
+                         (if (= K (logand K K2))
+                             (loop type K cs newcs)
+                             (loop type K cs (cons c2 newcs))))
+                        (else
+                         (loop type3
+                               (available:killer-combine K K2)
+                               cs
+                               (cons c2 newcs)))))
+                (let* ((op (variable.name (call.proc E2)))
+                       (args (call.args E2))
+                       (argtypes (map (lambda (exp)
+                                        (aeval exp types constraints))
+                                      args)))
+                  (cond ((representation-subtype? type rep:true)
+                         (let ((reps (rep-if-true op argtypes)))
+                           (if reps
+                               (record-new-reps! args argtypes reps K2))))
+                        ((representation-subtype? type rep:false)
+                         (let ((reps (rep-if-false op argtypes)))
+                           (if reps
+                               (record-new-reps! args argtypes reps K2)))))
+                  (loop type K cs (cons c2 newcs)))))))
+    
+    (define (record-new-reps! args argtypes reps K2)
+      (if debugging?
+          (begin (write (list (map make-readable args)
+                              (map rep->symbol argtypes)
+                              (map rep->symbol reps)))
+                 (newline)))
+      (for-each (lambda (arg type0 type1)
+                  (if (not (representation-subtype? type0 type1))
+                      (if (variable? arg)
+                          (let ((name (variable.name arg)))
+                            ; FIXME:  In this context, a variable
+                            ; should always be local so the hashtable
+                            ; operation isn't necessary.
+                            (if (hashtable-get types name)
+                                (constraints-add!
+                                 types
+                                 constraints
+                                 (make-type-constraint
+                                  name
+                                  type1 
+                                  (available:killer-combine K K2)))
+                                (cerror
+                                 "Compiler bug: unexpected global: "
+                                 name))))))
+                args argtypes reps))
+    
+    (if (not (zero? K))
+        (constraints-add-killedby! constraints T K))
+    
+    (let* ((table (constraints.table constraints))
+           (cs (hashtable-fetch table T '())))
+      (cond ((constant? E)
+             ; It's a type constraint.
+             (let ((type (constant.value E)))
+               (if debugging?
+                   (begin (display T)
+                          (display " : ")
+                          (display (rep->symbol type))
+                          (newline)))
+               (let ((cs (loop type K cs '())))
+                 (hashtable-put! table T cs)
+                 constraints)))
+            (else
+             (if debugging?
+                 (begin (display T)
+                        (display " = ")
+                        (display (make-readable E #t))
+                        (newline)))
+             (if (not (null? cs))
+                 (begin
+                  (display "Compiler bug: ")
+                  (write T)
+                  (display " has unexpectedly nonempty constraints")
+                  (newline)))
+             (hashtable-put! table T (list (list T E K)))
+             constraints)))))
+
+; Sets of constraints.
+;
+; The set of constraints is represented as (<hashtable> <killedby>),
+; where <hashtable> is a hashtable mapping variables to lists of
+; constraints as above, and <killedby> is a vector mapping basic killers
+; to lists of variables that need to be examined for constraints that
+; are killed by that basic killer.
+
+(define number-of-basic-killers
+  (do ((i 0 (+ i 1))
+       (k 1 (+ k k)))
+      ((> k available:killer:dead)
+       i)))
+
+(define (constraints.table  constraints) (car constraints))
+(define (constraints.killed constraints) (cadr constraints))
+
+(define (make-constraints-table)
+  (list (make-hashtable symbol-hash assq)
+        (make-vector number-of-basic-killers '())))
+
+(define (copy-constraints-table constraints)
+  (list (hashtable-copy (constraints.table constraints))
+        (list->vector (vector->list (constraints.killed constraints)))))
+
+(define (constraints-for-variable constraints T)
+  (hashtable-fetch (constraints.table constraints) T '()))
+
+(define (constraints-add-killedby! constraints T K0)
+  (if (not (zero? K0))
+      (let ((v (constraints.killed constraints)))
+        (do ((i 0 (+ i 1))
+             (k 1 (+ k k)))
+            ((= i number-of-basic-killers))
+            (if (not (zero? (logand k K0)))
+                (vector-set! v i (cons T (vector-ref v i))))))))
+
+(define (constraints-kill! constraints K)
+  (if (not (zero? K))
+      (let ((table (constraints.table constraints))
+            (killed (constraints.killed constraints)))
+        (define (examine! T)
+          (let ((cs (filter (lambda (c)
+                              (zero? (logand (constraint.killer c) K)))
+                            (hashtable-fetch table T '()))))
+            (if (null? cs)
+                (hashtable-remove! table T)
+                (hashtable-put! table T cs))))
+        (do ((i 0 (+ i 1))
+             (j 1 (+ j j)))
+            ((= i number-of-basic-killers))
+            (if (not (zero? (logand j K)))
+                (begin (for-each examine! (vector-ref killed i))
+                       (vector-set! killed i '())))))))
+
+(define (constraints-intersect! constraints0 constraints1 constraints2)
+  (let ((table0 (constraints.table constraints0))
+        (table1 (constraints.table constraints1))
+        (table2 (constraints.table constraints2)))
+    (if (eq? table0 table1)
+        ; FIXME:  Which is more efficient: to update the killed vector,
+        ; or not to update it?  Both are safe.
+        (hashtable-for-each (lambda (T cs)
+                              (if (not (null? cs))
+                                  (hashtable-put!
+                                   table0
+                                   T
+                                   (cs-intersect
+                                    (hashtable-fetch table2 T '())
+                                    cs))))
+                            table1)
+        ; This case shouldn't ever happen, so it can be slow.
+        (begin
+         (constraints-intersect! constraints0 constraints0 constraints1)
+         (constraints-intersect! constraints0 constraints0 constraints2)))))
+
+(define (cs-intersect cs1 cs2)
+  (define (loop cs init rep Krep)
+    (if (null? cs)
+        (values init rep Krep)
+        (let* ((c (car cs))
+               (cs (cdr cs))
+               (E2 (constraint.rhs c))
+               (K2 (constraint.killer c)))
+          (cond ((constant? E2)
+                 (loop cs
+                       init
+                       (representation-intersection rep (constant.value E2))
+                       (available:killer-combine Krep K2)))
+                ((call? E2)
+                 (if init
+                     (begin (display "Compiler bug in cs-intersect")
+                            (break))
+                     (loop cs c rep Krep)))
+                (else
+                 (error "Compiler bug in cs-intersect"))))))
+  (call-with-values
+   (lambda ()
+     (loop cs1 #f rep:object available:killer:none))
+   (lambda (c1 rep1 Krep1)
+     (call-with-values
+      (lambda ()
+        (loop cs2 #f rep:object available:killer:none))
+      (lambda (c2 rep2 Krep2)
+        (let ((c (if (equal? c1 c2) c1 #f))
+              (rep (representation-union rep1 rep2))
+              (Krep (available:killer-combine Krep1 Krep2)))
+          (if (eq? rep rep:object)
+              (if c (list c) '())
+              (let ((T (constraint.lhs (car cs1))))
+                (if c
+                    (list c (make-type-constraint T rep Krep))
+                    (list (make-type-constraint T rep Krep)))))))))))
+; DO NOT EDIT THIS FILE. Edit the config file and rerun "config".
+
+(define $gc.ephemeral 0)
+(define $gc.tenuring 1)
+(define $gc.full 2)
+(define $mstat.wallocated-hi 0)
+(define $mstat.wallocated-lo 1)
+(define $mstat.wcollected-hi 2)
+(define $mstat.wcollected-lo 3)
+(define $mstat.wcopied-hi 4)
+(define $mstat.wcopied-lo 5)
+(define $mstat.gctime 6)
+(define $mstat.wlive 7)
+(define $mstat.gc-last-gen 8)
+(define $mstat.gc-last-type 9)
+(define $mstat.generations 10)
+(define $mstat.g-gc-count 0)
+(define $mstat.g-prom-count 1)
+(define $mstat.g-gctime 2)
+(define $mstat.g-wlive 3)
+(define $mstat.g-np-youngp 4)
+(define $mstat.g-np-oldp 5)
+(define $mstat.g-np-j 6)
+(define $mstat.g-np-k 7)
+(define $mstat.g-alloc 8)
+(define $mstat.g-target 9)
+(define $mstat.g-promtime 10)
+(define $mstat.remsets 11)
+(define $mstat.r-apool 0)
+(define $mstat.r-upool 1)
+(define $mstat.r-ahash 2)
+(define $mstat.r-uhash 3)
+(define $mstat.r-hrec-hi 4)
+(define $mstat.r-hrec-lo 5)
+(define $mstat.r-hrem-hi 6)
+(define $mstat.r-hrem-lo 7)
+(define $mstat.r-hscan-hi 8)
+(define $mstat.r-hscan-lo 9)
+(define $mstat.r-wscan-hi 10)
+(define $mstat.r-wscan-lo 11)
+(define $mstat.r-ssbrec-hi 12)
+(define $mstat.r-ssbrec-lo 13)
+(define $mstat.r-np-p 14)
+(define $mstat.fflushed-hi 12)
+(define $mstat.fflushed-lo 13)
+(define $mstat.wflushed-hi 14)
+(define $mstat.wflushed-lo 15)
+(define $mstat.stk-created 16)
+(define $mstat.frestored-hi 17)
+(define $mstat.frestored-lo 18)
+(define $mstat.words-heap 19)
+(define $mstat.words-remset 20)
+(define $mstat.words-rts 21)
+(define $mstat.swb-assign 22)
+(define $mstat.swb-lhs-ok 23)
+(define $mstat.swb-rhs-const 24)
+(define $mstat.swb-not-xgen 25)
+(define $mstat.swb-trans 26)
+(define $mstat.rtime 27)
+(define $mstat.stime 28)
+(define $mstat.utime 29)
+(define $mstat.minfaults 30)
+(define $mstat.majfaults 31)
+(define $mstat.np-remsetp 32)
+(define $mstat.max-heap 33)
+(define $mstat.promtime 34)
+(define $mstat.wmoved-hi 35)
+(define $mstat.wmoved-lo 36)
+(define $mstat.vsize 37)
+(define $g.reg0 12)
+(define $r.reg8 44)
+(define $r.reg9 48)
+(define $r.reg10 52)
+(define $r.reg11 56)
+(define $r.reg12 60)
+(define $r.reg13 64)
+(define $r.reg14 68)
+(define $r.reg15 72)
+(define $r.reg16 76)
+(define $r.reg17 80)
+(define $r.reg18 84)
+(define $r.reg19 88)
+(define $r.reg20 92)
+(define $r.reg21 96)
+(define $r.reg22 100)
+(define $r.reg23 104)
+(define $r.reg24 108)
+(define $r.reg25 112)
+(define $r.reg26 116)
+(define $r.reg27 120)
+(define $r.reg28 124)
+(define $r.reg29 128)
+(define $r.reg30 132)
+(define $r.reg31 136)
+(define $g.stkbot 180)
+(define $g.gccnt 420)
+(define $m.alloc 1024)
+(define $m.alloci 1032)
+(define $m.gc 1040)
+(define $m.addtrans 1048)
+(define $m.stkoflow 1056)
+(define $m.stkuflow 1072)
+(define $m.creg 1080)
+(define $m.creg-set! 1088)
+(define $m.add 1096)
+(define $m.subtract 1104)
+(define $m.multiply 1112)
+(define $m.quotient 1120)
+(define $m.remainder 1128)
+(define $m.divide 1136)
+(define $m.modulo 1144)
+(define $m.negate 1152)
+(define $m.numeq 1160)
+(define $m.numlt 1168)
+(define $m.numle 1176)
+(define $m.numgt 1184)
+(define $m.numge 1192)
+(define $m.zerop 1200)
+(define $m.complexp 1208)
+(define $m.realp 1216)
+(define $m.rationalp 1224)
+(define $m.integerp 1232)
+(define $m.exactp 1240)
+(define $m.inexactp 1248)
+(define $m.exact->inexact 1256)
+(define $m.inexact->exact 1264)
+(define $m.make-rectangular 1272)
+(define $m.real-part 1280)
+(define $m.imag-part 1288)
+(define $m.sqrt 1296)
+(define $m.round 1304)
+(define $m.truncate 1312)
+(define $m.apply 1320)
+(define $m.varargs 1328)
+(define $m.typetag 1336)
+(define $m.typetag-set 1344)
+(define $m.break 1352)
+(define $m.eqv 1360)
+(define $m.partial-list->vector 1368)
+(define $m.timer-exception 1376)
+(define $m.exception 1384)
+(define $m.singlestep 1392)
+(define $m.syscall 1400)
+(define $m.bvlcmp 1408)
+(define $m.enable-interrupts 1416)
+(define $m.disable-interrupts 1424)
+(define $m.alloc-bv 1432)
+(define $m.global-ex 1440)
+(define $m.invoke-ex 1448)
+(define $m.global-invoke-ex 1456)
+(define $m.argc-ex 1464)
+; DO NOT EDIT THIS FILE. Edit the config file and rerun "config".
+
+(define $r.g0 0)
+(define $r.g1 1)
+(define $r.g2 2)
+(define $r.g3 3)
+(define $r.g4 4)
+(define $r.g5 5)
+(define $r.g6 6)
+(define $r.g7 7)
+(define $r.o0 8)
+(define $r.o1 9)
+(define $r.o2 10)
+(define $r.o3 11)
+(define $r.o4 12)
+(define $r.o5 13)
+(define $r.o6 14)
+(define $r.o7 15)
+(define $r.l0 16)
+(define $r.l1 17)
+(define $r.l2 18)
+(define $r.l3 19)
+(define $r.l4 20)
+(define $r.l5 21)
+(define $r.l6 22)
+(define $r.l7 23)
+(define $r.i0 24)
+(define $r.i1 25)
+(define $r.i2 26)
+(define $r.i3 27)
+(define $r.i4 28)
+(define $r.i5 29)
+(define $r.i6 30)
+(define $r.i7 31)
+(define $r.result $r.o0)
+(define $r.argreg2 $r.o1)
+(define $r.argreg3 $r.o2)
+(define $r.stkp $r.o3)
+(define $r.stklim $r.i0)
+(define $r.tmp1 $r.o4)
+(define $r.tmp2 $r.o5)
+(define $r.tmp0 $r.g1)
+(define $r.e-top $r.i0)
+(define $r.e-limit $r.o3)
+(define $r.timer $r.i4)
+(define $r.millicode $r.i7)
+(define $r.globals $r.i7)
+(define $r.reg0 $r.l0)
+(define $r.reg1 $r.l1)
+(define $r.reg2 $r.l2)
+(define $r.reg3 $r.l3)
+(define $r.reg4 $r.l4)
+(define $r.reg5 $r.l5)
+(define $r.reg6 $r.l6)
+(define $r.reg7 $r.l7)
+; DO NOT EDIT THIS FILE. Edit the config file and rerun "config".
+
+(define $ex.car 0)
+(define $ex.cdr 1)
+(define $ex.setcar 2)
+(define $ex.setcdr 3)
+(define $ex.add 10)
+(define $ex.sub 11)
+(define $ex.mul 12)
+(define $ex.div 13)
+(define $ex.lessp 14)
+(define $ex.lesseqp 15)
+(define $ex.equalp 16)
+(define $ex.greatereqp 17)
+(define $ex.greaterp 18)
+(define $ex.quotient 19)
+(define $ex.remainder 20)
+(define $ex.modulo 21)
+(define $ex.logior 22)
+(define $ex.logand 23)
+(define $ex.logxor 24)
+(define $ex.lognot 25)
+(define $ex.lsh 26)
+(define $ex.rsha 27)
+(define $ex.rshl 28)
+(define $ex.e2i 29)
+(define $ex.i2e 30)
+(define $ex.exactp 31)
+(define $ex.inexactp 32)
+(define $ex.round 33)
+(define $ex.trunc 34)
+(define $ex.zerop 35)
+(define $ex.neg 36)
+(define $ex.abs 37)
+(define $ex.realpart 38)
+(define $ex.imagpart 39)
+(define $ex.vref 40)
+(define $ex.vset 41)
+(define $ex.vlen 42)
+(define $ex.pref 50)
+(define $ex.pset 51)
+(define $ex.plen 52)
+(define $ex.sref 60)
+(define $ex.sset 61)
+(define $ex.slen 62)
+(define $ex.bvref 70)
+(define $ex.bvset 71)
+(define $ex.bvlen 72)
+(define $ex.bvlref 80)
+(define $ex.bvlset 81)
+(define $ex.bvllen 82)
+(define $ex.vlref 90)
+(define $ex.vlset 91)
+(define $ex.vllen 92)
+(define $ex.typetag 100)
+(define $ex.typetagset 101)
+(define $ex.apply 102)
+(define $ex.argc 103)
+(define $ex.vargc 104)
+(define $ex.nonproc 105)
+(define $ex.undef-global 106)
+(define $ex.dump 107)
+(define $ex.dumpfail 108)
+(define $ex.timer 109)
+(define $ex.unsupported 110)
+(define $ex.int2char 111)
+(define $ex.char2int 112)
+(define $ex.mkbvl 113)
+(define $ex.mkvl 114)
+(define $ex.char<? 115)
+(define $ex.char<=? 116)
+(define $ex.char=? 117)
+(define $ex.char>? 118)
+(define $ex.char>=? 119)
+(define $ex.bvfill 120)
+(define $ex.enable-interrupts 121)
+(define $ex.keyboard-interrupt 122)
+(define $ex.arithmetic-exception 123)
+(define $ex.global-invoke 124)
+(define $ex.fx+ 140)
+(define $ex.fx- 141)
+(define $ex.fx-- 142)
+(define $ex.fx= 143)
+(define $ex.fx< 144)
+(define $ex.fx<= 145)
+(define $ex.fx> 146)
+(define $ex.fx>= 147)
+(define $ex.fxpositive? 148)
+(define $ex.fxnegative? 149)
+(define $ex.fxzero? 150)
+(define $ex.fx* 151)
+; DO NOT EDIT THIS FILE. Edit the config file and rerun "config".
+
+(define $tag.tagmask 7)
+(define $tag.pair-tag 1)
+(define $tag.vector-tag 3)
+(define $tag.bytevector-tag 5)
+(define $tag.procedure-tag 7)
+(define $imm.vector-header 162)
+(define $imm.bytevector-header 194)
+(define $imm.procedure-header 254)
+(define $imm.true 6)
+(define $imm.false 2)
+(define $imm.null 10)
+(define $imm.unspecified 278)
+(define $imm.eof 534)
+(define $imm.undefined 790)
+(define $imm.character 38)
+(define $tag.vector-typetag 0)
+(define $tag.rectnum-typetag 4)
+(define $tag.ratnum-typetag 8)
+(define $tag.symbol-typetag 12)
+(define $tag.port-typetag 16)
+(define $tag.structure-typetag 20)
+(define $tag.bytevector-typetag 0)
+(define $tag.string-typetag 4)
+(define $tag.flonum-typetag 8)
+(define $tag.compnum-typetag 12)
+(define $tag.bignum-typetag 16)
+(define $hdr.port 178)
+(define $hdr.struct 182)
+(define $p.codevector -3)
+(define $p.constvector 1)
+(define $p.linkoffset 5)
+(define $p.reg0 5)
+(define $p.codeoffset -1)
+; Copyright 1991 William Clinger
+;
+; Relatively target-independent information for Twobit's backend.
+;
+; 24 April 1999 / wdc
+;
+; Most of the definitions in this file can be extended or overridden by
+; target-specific definitions.
+
+(define twobit-sort
+  (lambda (less? list) (compat:sort list less?)))
+
+(define renaming-prefix ".")
+
+; The prefix used for cells introduced by the compiler.
+
+(define cell-prefix (string-append renaming-prefix "CELL:"))
+
+; Names of global procedures that cannot be redefined or assigned
+; by ordinary code.
+; The expansion of quasiquote uses .cons and .list directly, so these
+; should not be changed willy-nilly.
+; Others may be used directly by a DEFINE-INLINE.
+
+(define name:CHECK!  '.check!)
+(define name:CONS '.cons)
+(define name:LIST '.list)
+(define name:MAKE-CELL '.make-cell)
+(define name:CELL-REF '.cell-ref)
+(define name:CELL-SET! '.cell-set!)
+(define name:IGNORED (string->symbol "IGNORED"))
+(define name:CAR '.car)
+(define name:CDR '.cdr)
+
+;(begin (eval `(define ,name:CONS cons))
+;       (eval `(define ,name:LIST list))
+;       (eval `(define ,name:MAKE-CELL list))
+;       (eval `(define ,name:CELL-REF car))
+;       (eval `(define ,name:CELL-SET! set-car!)))
+
+; If (INTEGRATE-USUAL-PROCEDURES) is true, then control optimization
+; recognizes calls to these procedures.
+
+(define name:NOT 'not)
+(define name:MEMQ 'memq)
+(define name:MEMV 'memv)
+
+; If (INTEGRATE-USUAL-PROCEDURES) is true, then control optimization
+; recognizes calls to these procedures and also creates calls to them.
+
+(define name:EQ? 'eq?)
+(define name:EQV? 'eqv?)
+
+; Control optimization creates calls to these procedures,
+; which do not need to check their arguments.
+
+(define name:FIXNUM?       'fixnum?)
+(define name:CHAR?         'char?)
+(define name:SYMBOL?       'symbol?)
+(define name:FX<           '<:fix:fix)
+(define name:FX-           'fx-)                   ; non-checking version
+(define name:CHAR->INTEGER 'char->integer)         ; non-checking version
+(define name:VECTOR-REF    'vector-ref:trusted)
+
+
+; Constant folding.
+; Prototype, will probably change in the future.
+
+(define (constant-folding-entry name)
+  (assq name $usual-constant-folding-procedures$))
+
+(define constant-folding-predicates cadr)
+(define constant-folding-folder caddr)
+
+(define $usual-constant-folding-procedures$
+  (let ((always? (lambda (x) #t))
+        (charcode? (lambda (n)
+                     (and (number? n)
+                          (exact? n)
+                          (<= 0 n)
+                          (< n 128))))
+        (ratnum? (lambda (n)
+                   (and (number? n)
+                        (exact? n)
+                        (rational? n))))
+        ; smallint? is defined later.
+        (smallint? (lambda (n) (smallint? n))))
+    `(
+      ; This makes some assumptions about the host system.
+      
+      (integer->char (,charcode?) ,integer->char)
+      (char->integer (,char?) ,char->integer)
+      (zero? (,ratnum?) ,zero?)
+      (< (,ratnum? ,ratnum?) ,<)
+      (<= (,ratnum? ,ratnum?) ,<=)
+      (= (,ratnum? ,ratnum?) ,=)
+      (>= (,ratnum? ,ratnum?) ,>=)
+      (> (,ratnum? ,ratnum?) ,>)
+      (+ (,ratnum? ,ratnum?) ,+)
+      (- (,ratnum? ,ratnum?) ,-)
+      (* (,ratnum? ,ratnum?) ,*)
+      (-- (,ratnum?) ,(lambda (x) (- 0 x)))
+      (eq? (,always? ,always?) ,eq?)
+      (eqv? (,always? ,always?) ,eqv?)
+      (equal? (,always? ,always?) ,equal?)
+      (memq (,always? ,list?) ,memq)
+      (memv (,always? ,list?) ,memv)
+      (member (,always? ,list?) ,member)
+      (assq (,always? ,list?) ,assq)
+      (assv (,always? ,list?) ,assv)
+      (assoc (,always? ,list?) ,assoc)
+      (length (,list?) ,length)
+      (fixnum? (,smallint?) ,smallint?)
+      (=:fix:fix  (,smallint? ,smallint?) ,=)
+      (<:fix:fix  (,smallint? ,smallint?) ,<)
+      (<=:fix:fix (,smallint? ,smallint?) ,<=)
+      (>:fix:fix  (,smallint? ,smallint?) ,>)
+      (>=:fix:fix (,smallint? ,smallint?) ,>=)
+      )))
+
+(begin '
+       (define (.check! flag exn . args)
+         (if (not flag)
+             (apply error "Runtime check exception: " exn args)))
+       #t)
+
+; Order matters.  If f and g are both inlined, and the definition of g
+; uses f, then f should be defined before g.
+
+(for-each pass1
+          `(
+
+(define-inline car
+  (syntax-rules ()
+   ((car x0)
+    (let ((x x0))
+      (.check! (pair? x) ,$ex.car x)
+      (car:pair x)))))
+   
+(define-inline cdr
+  (syntax-rules ()
+   ((car x0)
+    (let ((x x0))
+      (.check! (pair? x) ,$ex.cdr x)
+      (cdr:pair x)))))
+
+(define-inline vector-length
+  (syntax-rules ()
+   ((vector-length v0)
+    (let ((v v0))
+      (.check! (vector? v) ,$ex.vlen v)
+      (vector-length:vec v)))))
+   
+(define-inline vector-ref
+  (syntax-rules ()
+   ((vector-ref v0 i0)
+    (let ((v v0)
+          (i i0))
+      (.check! (fixnum? i) ,$ex.vref v i)
+      (.check! (vector? v) ,$ex.vref v i)
+      (.check! (<:fix:fix i (vector-length:vec v)) ,$ex.vref v i)
+      (.check! (>=:fix:fix i 0) ,$ex.vref  v i)
+      (vector-ref:trusted v i)))))
+   
+(define-inline vector-set!
+  (syntax-rules ()
+   ((vector-set! v0 i0 x0)
+    (let ((v v0)
+          (i i0)
+          (x x0))
+      (.check! (fixnum? i) ,$ex.vset v i x)
+      (.check! (vector? v) ,$ex.vset v i x)
+      (.check! (<:fix:fix i (vector-length:vec v)) ,$ex.vset v i x)
+      (.check! (>=:fix:fix i 0) ,$ex.vset v i x)
+      (vector-set!:trusted v i x)))))
+   
+; This transformation must make sure the entire list is freshly
+; allocated when an argument to LIST returns more than once.
+
+(define-inline list
+  (syntax-rules ()
+   ((list)
+    '())
+   ((list ?e)
+    (cons ?e '()))
+   ((list ?e1 ?e2 ...)
+    (let* ((t1 ?e1)
+           (t2 (list ?e2 ...)))
+      (cons t1 t2)))))
+
+; This transformation must make sure the entire list is freshly
+; allocated when an argument to VECTOR returns more than once.
+
+(define-inline vector
+  (syntax-rules ()
+   ((vector)
+    '#())
+   ((vector ?e)
+    (make-vector 1 ?e))
+   ((vector ?e1 ?e2 ...)
+    (letrec-syntax
+      ((vector-aux1
+        (... (syntax-rules ()
+              ((vector-aux1 () ?n ?exps ?indexes ?temps)
+               (vector-aux2 ?n ?exps ?indexes ?temps))
+              ((vector-aux1 (?exp1 ?exp2 ...) ?n ?exps ?indexes ?temps)
+               (vector-aux1 (?exp2 ...)
+                            (+ ?n 1)
+                            (?exp1 . ?exps)
+                            (?n . ?indexes)
+                            (t . ?temps))))))
+       (vector-aux2
+        (... (syntax-rules ()
+              ((vector-aux2 ?n (?exp1 ?exp2 ...) (?n1 ?n2 ...) (?t1 ?t2 ...))
+               (let* ((?t1 ?exp1)
+                      (?t2 ?exp2)
+                      ...
+                      (v (make-vector ?n ?t1)))
+                 (vector-set! v ?n2 ?t2)
+                 ...
+                 v))))))
+      (vector-aux1 (?e1 ?e2 ...) 0 () () ())))))
+
+(define-inline cadddr
+  (syntax-rules ()
+   ((cadddr ?e)
+    (car (cdr (cdr (cdr ?e)))))))
+
+(define-inline cddddr
+  (syntax-rules ()
+   ((cddddr ?e)
+    (cdr (cdr (cdr (cdr ?e)))))))
+
+(define-inline cdddr
+  (syntax-rules ()
+   ((cdddr ?e)
+    (cdr (cdr (cdr ?e))))))
+
+(define-inline caddr
+  (syntax-rules ()
+   ((caddr ?e)
+    (car (cdr (cdr ?e))))))
+
+(define-inline cddr
+  (syntax-rules ()
+   ((cddr ?e)
+    (cdr (cdr ?e)))))
+
+(define-inline cdar
+  (syntax-rules ()
+   ((cdar ?e)
+    (cdr (car ?e)))))
+
+(define-inline cadr
+  (syntax-rules ()
+   ((cadr ?e)
+    (car (cdr ?e)))))
+
+(define-inline caar
+  (syntax-rules ()
+   ((caar ?e)
+    (car (car ?e)))))
+
+(define-inline make-vector
+  (syntax-rules ()
+   ((make-vector ?n)
+    (make-vector ?n '()))))
+
+(define-inline make-string
+  (syntax-rules ()
+   ((make-string ?n)
+    (make-string ?n #\space))))
+
+(define-inline =
+  (syntax-rules ()
+   ((= ?e1 ?e2 ?e3 ?e4 ...)
+    (let ((t ?e2))
+      (and (= ?e1 t)
+           (= t ?e3 ?e4 ...))))))
+
+(define-inline <
+  (syntax-rules ()
+   ((< ?e1 ?e2 ?e3 ?e4 ...)
+    (let ((t ?e2))
+      (and (< ?e1 t)
+           (< t ?e3 ?e4 ...))))))
+
+(define-inline >
+  (syntax-rules ()
+   ((> ?e1 ?e2 ?e3 ?e4 ...)
+    (let ((t ?e2))
+      (and (> ?e1 t)
+           (> t ?e3 ?e4 ...))))))
+
+(define-inline <=
+  (syntax-rules ()
+   ((<= ?e1 ?e2 ?e3 ?e4 ...)
+    (let ((t ?e2))
+      (and (<= ?e1 t)
+           (<= t ?e3 ?e4 ...))))))
+
+(define-inline >=
+  (syntax-rules ()
+   ((>= ?e1 ?e2 ?e3 ?e4 ...)
+    (let ((t ?e2))
+      (and (>= ?e1 t)
+           (>= t ?e3 ?e4 ...))))))
+
+(define-inline +
+  (syntax-rules ()
+   ((+)
+    0)
+   ((+ ?e)
+    ?e)
+   ((+ ?e1 ?e2 ?e3 ?e4 ...)
+    (+ (+ ?e1 ?e2) ?e3 ?e4 ...))))
+
+(define-inline *
+  (syntax-rules ()
+   ((*)
+    1)
+   ((* ?e)
+    ?e)
+   ((* ?e1 ?e2 ?e3 ?e4 ...)
+    (* (* ?e1 ?e2) ?e3 ?e4 ...))))
+
+(define-inline -
+  (syntax-rules ()
+   ((- ?e)
+    (- 0 ?e))
+   ((- ?e1 ?e2 ?e3 ?e4 ...)
+    (- (- ?e1 ?e2) ?e3 ?e4 ...))))
+
+(define-inline /
+  (syntax-rules ()
+   ((/ ?e)
+    (/ 1 ?e))
+   ((/ ?e1 ?e2 ?e3 ?e4 ...)
+    (/ (/ ?e1 ?e2) ?e3 ?e4 ...))))
+
+(define-inline abs
+  (syntax-rules ()
+   ((abs ?z)
+    (let ((temp ?z))
+      (if (< temp 0)
+          (-- temp)
+          temp)))))
+
+(define-inline negative?
+  (syntax-rules ()
+   ((negative? ?x)
+    (< ?x 0))))
+
+(define-inline positive?
+  (syntax-rules ()
+   ((positive? ?x)
+    (> ?x 0))))
+
+(define-inline eqv?
+  (transformer
+   (lambda (exp rename compare)
+     (let ((arg1 (cadr exp))
+           (arg2 (caddr exp)))
+       (define (constant? exp)
+         (or (boolean? exp)
+             (char? exp)
+             (and (pair? exp)
+                  (= (length exp) 2)
+                  (identifier? (car exp))
+                  (compare (car exp) (rename 'quote))
+                  (symbol? (cadr exp)))))
+       (if (or (constant? arg1)
+               (constant? arg2))
+           (cons (rename 'eq?) (cdr exp))
+           exp)))))
+
+(define-inline memq
+  (syntax-rules (quote)
+   ((memq ?expr '(?datum ...))
+    (letrec-syntax
+      ((memq0
+        (... (syntax-rules (quote)
+              ((memq0 '?xx '(?d ...))
+               (let ((t1 '(?d ...)))
+                 (memq1 '?xx t1 (?d ...))))
+              ((memq0 ?e '(?d ...))
+               (let ((t0 ?e)
+                     (t1 '(?d ...)))
+                 (memq1 t0 t1 (?d ...)))))))
+       (memq1
+        (... (syntax-rules ()
+              ((memq1 ?t0 ?t1 ())
+               #f)
+              ((memq1 ?t0 ?t1 (?d1 ?d2 ...))
+               (if (eq? ?t0 '?d1)
+                   ?t1
+                   (let ((?t1 (cdr ?t1)))
+                     (memq1 ?t0 ?t1 (?d2 ...)))))))))
+      (memq0 ?expr '(?datum ...))))))
+
+(define-inline memv
+  (transformer
+   (lambda (exp rename compare)
+     (let ((arg1 (cadr exp))
+           (arg2 (caddr exp)))
+       (if (or (boolean? arg1)
+               (fixnum? arg1)
+               (char? arg1)
+               (and (pair? arg1)
+                    (= (length arg1) 2)
+                    (identifier? (car arg1))
+                    (compare (car arg1) (rename 'quote))
+                    (symbol? (cadr arg1)))
+               (and (pair? arg2)
+                    (= (length arg2) 2)
+                    (identifier? (car arg2))
+                    (compare (car arg2) (rename 'quote))
+                    (every1? (lambda (x)
+                               (or (boolean? x)
+                                   (fixnum? x)
+                                   (char? x)
+                                   (symbol? x)))
+                             (cadr arg2))))
+           (cons (rename 'memq) (cdr exp))
+           exp)))))
+
+(define-inline assv
+  (transformer
+   (lambda (exp rename compare)
+     (let ((arg1 (cadr exp))
+           (arg2 (caddr exp)))
+       (if (or (boolean? arg1)
+               (char? arg1)
+               (and (pair? arg1)
+                    (= (length arg1) 2)
+                    (identifier? (car arg1))
+                    (compare (car arg1) (rename 'quote))
+                    (symbol? (cadr arg1)))
+               (and (pair? arg2)
+                    (= (length arg2) 2)
+                    (identifier? (car arg2))
+                    (compare (car arg2) (rename 'quote))
+                    (every1? (lambda (y)
+                               (and (pair? y)
+                                    (let ((x (car y)))
+                                      (or (boolean? x)
+                                          (char? x)
+                                          (symbol? x)))))
+                             (cadr arg2))))
+           (cons (rename 'assq) (cdr exp))
+           exp)))))
+
+(define-inline map
+  (syntax-rules (lambda)
+   ((map ?proc ?exp1 ?exp2 ...)
+    (letrec-syntax
+      ((loop
+        (... (syntax-rules (lambda)
+              ((loop 1 () (?y1 ?y2 ...) ?f ?exprs)
+               (loop 2 (?y1 ?y2 ...) ?f ?exprs))
+              ((loop 1 (?a1 ?a2 ...) (?y2 ...) ?f ?exprs)
+               (loop 1 (?a2 ...) (y1 ?y2 ...) ?f ?exprs))
+              
+              ((loop 2 ?ys (lambda ?formals ?body) ?exprs)
+               (loop 3 ?ys (lambda ?formals ?body) ?exprs))
+              ((loop 2 ?ys (?f1 . ?f2) ?exprs)
+               (let ((f (?f1 . ?f2)))
+                 (loop 3 ?ys f ?exprs)))
+              ; ?f must be a constant or variable.
+              ((loop 2 ?ys ?f ?exprs)
+               (loop 3 ?ys ?f ?exprs))
+              
+              ((loop 3 (?y1 ?y2 ...) ?f (?e1 ?e2 ...))
+               (do ((?y1 ?e1 (cdr ?y1))
+                    (?y2 ?e2 (cdr ?y2))
+                    ...
+                    (results '() (cons (?f (car ?y1) (car ?y2) ...)
+                                       results)))
+                   ((or (null? ?y1) (null? ?y2) ...)
+                    (reverse results))))))))
+      
+      (loop 1 (?exp1 ?exp2 ...) () ?proc (?exp1 ?exp2 ...))))))
+
+(define-inline for-each
+  (syntax-rules (lambda)
+   ((for-each ?proc ?exp1 ?exp2 ...)
+    (letrec-syntax
+      ((loop
+        (... (syntax-rules (lambda)
+              ((loop 1 () (?y1 ?y2 ...) ?f ?exprs)
+               (loop 2 (?y1 ?y2 ...) ?f ?exprs))
+              ((loop 1 (?a1 ?a2 ...) (?y2 ...) ?f ?exprs)
+               (loop 1 (?a2 ...) (y1 ?y2 ...) ?f ?exprs))
+              
+              ((loop 2 ?ys (lambda ?formals ?body) ?exprs)
+               (loop 3 ?ys (lambda ?formals ?body) ?exprs))
+              ((loop 2 ?ys (?f1 . ?f2) ?exprs)
+               (let ((f (?f1 . ?f2)))
+                 (loop 3 ?ys f ?exprs)))
+              ; ?f must be a constant or variable.
+              ((loop 2 ?ys ?f ?exprs)
+               (loop 3 ?ys ?f ?exprs))
+              
+              ((loop 3 (?y1 ?y2 ...) ?f (?e1 ?e2 ...))
+               (do ((?y1 ?e1 (cdr ?y1))
+                    (?y2 ?e2 (cdr ?y2))
+                    ...)
+                   ((or (null? ?y1) (null? ?y2) ...)
+                    (if #f #f))
+                   (?f (car ?y1) (car ?y2) ...)))))))
+      
+      (loop 1 (?exp1 ?exp2 ...) () ?proc (?exp1 ?exp2 ...))))))
+
+))
+
+(define extended-syntactic-environment
+  (syntactic-copy global-syntactic-environment))
+
+(define (make-extended-syntactic-environment)
+  (syntactic-copy extended-syntactic-environment))
+
+; MacScheme machine assembly instructions.
+
+(define instruction.op car)
+(define instruction.arg1 cadr)
+(define instruction.arg2 caddr)
+(define instruction.arg3 cadddr)
+
+; Opcode table.
+
+(define *mnemonic-names* '())           ; For readify-lap
+(begin
+ '
+ (define *last-reserved-mnemonic* 32767)       ; For consistency check
+ '
+ (define make-mnemonic
+   (let ((count 0))
+     (lambda (name)
+       (set! count (+ count 1))
+       (if (= count *last-reserved-mnemonic*)
+           (error "Error in make-mnemonic: conflict: " name))
+       (set! *mnemonic-names* (cons (cons count name) *mnemonic-names*))
+       count)))
+ '
+ (define (reserved-mnemonic name value)
+   (if (and (> value 0) (< value *last-reserved-mnemonic*))
+       (set! *last-reserved-mnemonic* value))
+   (set! *mnemonic-names* (cons (cons value name) *mnemonic-names*))
+   value)
+ #t)
+
+(define make-mnemonic
+   (let ((count 0))
+     (lambda (name)
+       (set! count (+ count 1))
+       (set! *mnemonic-names* (cons (cons count name) *mnemonic-names*))
+       count)))
+
+(define (reserved-mnemonic name ignored)
+  (make-mnemonic name))
+
+(define $.linearize (reserved-mnemonic '.linearize -1))  ; unused?
+(define $.label (reserved-mnemonic '.label 63))
+(define $.proc (reserved-mnemonic '.proc 62))    ; proc entry point
+(define $.cont (reserved-mnemonic '.cont 61))    ; return point
+(define $.align (reserved-mnemonic '.align 60))  ; align code stream
+(define $.asm (reserved-mnemonic '.asm 59))      ; in-line native code
+(define $.proc-doc                               ; internal def proc info
+  (reserved-mnemonic '.proc-doc 58))
+(define $.end                                    ; end of code vector
+  (reserved-mnemonic '.end 57))                  ; (asm internal)
+(define $.singlestep                             ; insert singlestep point
+  (reserved-mnemonic '.singlestep 56))           ; (asm internal)
+(define $.entry (reserved-mnemonic '.entry 55))  ; procedure entry point 
+                                                 ; (asm internal)
+
+(define $op1 (make-mnemonic 'op1))               ; op      prim
+(define $op2 (make-mnemonic 'op2))               ; op2     prim,k
+(define $op3 (make-mnemonic 'op3))               ; op3     prim,k1,k2
+(define $op2imm (make-mnemonic 'op2imm))         ; op2imm  prim,x
+(define $const (make-mnemonic 'const))           ; const   x
+(define $global (make-mnemonic 'global))         ; global  x
+(define $setglbl (make-mnemonic 'setglbl))       ; setglbl x
+(define $lexical (make-mnemonic 'lexical))       ; lexical m,n
+(define $setlex (make-mnemonic 'setlex))         ; setlex  m,n
+(define $stack (make-mnemonic 'stack))           ; stack   n
+(define $setstk (make-mnemonic 'setstk))         ; setstk  n
+(define $load (make-mnemonic 'load))             ; load    k,n
+(define $store (make-mnemonic 'store))           ; store   k,n
+(define $reg (make-mnemonic 'reg))               ; reg     k
+(define $setreg (make-mnemonic 'setreg))         ; setreg  k
+(define $movereg (make-mnemonic 'movereg))       ; movereg k1,k2
+(define $lambda (make-mnemonic 'lambda))         ; lambda  x,n,doc
+(define $lexes (make-mnemonic 'lexes))           ; lexes   n,doc
+(define $args= (make-mnemonic 'args=))           ; args=   k
+(define $args>= (make-mnemonic 'args>=))         ; args>=  k
+(define $invoke (make-mnemonic 'invoke))         ; invoke  k
+(define $save (make-mnemonic 'save))             ; save    L,k
+(define $setrtn (make-mnemonic 'setrtn))         ; setrtn  L
+(define $restore (make-mnemonic 'restore))       ; restore n    ; deprecated
+(define $pop (make-mnemonic 'pop))               ; pop     k
+(define $popstk (make-mnemonic 'popstk))         ; popstk       ; for students
+(define $return (make-mnemonic 'return))         ; return
+(define $mvrtn (make-mnemonic 'mvrtn))           ; mvrtn        ; NYI
+(define $apply (make-mnemonic 'apply))           ; apply
+(define $nop (make-mnemonic 'nop))               ; nop
+(define $jump (make-mnemonic 'jump))             ; jump    m,o
+(define $skip (make-mnemonic 'skip))             ; skip    L    ; forward
+(define $branch (make-mnemonic 'branch))         ; branch  L
+(define $branchf (make-mnemonic 'branchf))       ; branchf L
+(define $check (make-mnemonic 'check))           ; check   k1,k2,k3,L
+(define $trap (make-mnemonic 'trap))             ; trap    k1,k2,k3,exn
+
+; A peephole optimizer may define more instructions in some
+; target-specific file.
+
+; eof
+; Copyright 1991 William Clinger
+;
+; $Id: twobit.sch,v 1.3 1999/08/23 19:14:26 lth Exp $
+;
+; Larceny -- target-specific information for Twobit's SPARC backend.
+;
+; 11 June 1999 / wdc
+
+; The maximum number of fixed arguments that may be followed by a rest
+; argument.  This limitation is removed by the macro expander.
+
+(define @maxargs-with-rest-arg@ 30)
+
+; The number of MacScheme machine registers.
+; (They do not necessarily correspond to hardware registers.)
+
+(define *nregs* 32)
+(define *lastreg* (- *nregs* 1))
+(define *fullregs* (quotient *nregs* 2))
+
+; The number of argument registers that are represented by hardware
+; registers.
+
+(define *nhwregs* 8)
+
+; Variable names that indicate register targets.
+
+(define *regnames*
+  (do ((alist '() (cons (cons (string->symbol
+                               (string-append ".REG" (number->string r)))
+                              r)
+                        alist))
+       (r (- *nhwregs* 1) (- r 1)))
+      ((<= r 0)
+       alist)))
+
+; A non-inclusive upper bound for the instruction encodings.
+
+(define *number-of-mnemonics* 72)
+
+; Integrable procedures and procedure-specific source code transformations.
+; Every integrable procedure that takes a varying number of arguments must
+; supply a transformation procedure to map calls into the fixed arity
+; required by the MacScheme machine instructions.
+
+; The table of integrable procedures.
+; Each entry is a list of the following items:
+;
+;    procedure name
+;    arity (or -1 for special primops like .check!)
+;    procedure name to be used by the disassembler
+;    predicate for immediate operands (or #f)
+;    primop code in the MacScheme machine (not used by Larceny)
+;    the effects that kill this primop's result
+;    the effects of this primop that kill available expressions
+
+(define (prim-entry name)
+  (assq name $usual-integrable-procedures$))
+
+(define prim-arity cadr)
+(define prim-opcodename caddr)
+(define prim-immediate? cadddr)
+(define (prim-primcode entry)
+  (car (cddddr entry)))
+
+; This predicate returns #t iff its argument will be represented
+; as a fixnum on the target machine.
+
+(define smallint?
+  (let* ((least (- (expt 2 29)))
+         (greatest (- (- least) 1)))
+    (lambda (x)
+      (and (number? x)
+           (exact? x)
+           (integer? x)
+           (<= least x greatest)))))
+
+(define (sparc-imm? x)
+  (and (fixnum? x)
+       (<= -1024 x 1023)))
+
+(define (sparc-eq-imm? x)
+  (or (sparc-imm? x)
+      (eq? x #t)
+      (eq? x #f)
+      (eq? x '())))
+
+(define (valid-typetag? x)
+  (and (fixnum? x)
+       (<= 0 x 7)))
+
+(define (fixnum-primitives) #t)
+(define (flonum-primitives) #t)
+
+; The table of primitives has been extended with
+; kill information used for commoning.
+
+(define (prim-lives-until entry)
+  (list-ref entry 5))
+
+(define (prim-kills entry)
+  (list-ref entry 6))
+
+(define $usual-integrable-procedures$
+  (let ((:globals  available:killer:globals)
+        (:car      available:killer:car)
+        (:cdr      available:killer:cdr)
+        (:string   available:killer:string)
+        (:vector   available:killer:vector)
+        (:cell     available:killer:cell)
+        (:io       available:killer:io)
+        (:none     available:killer:none)     ; none of the above
+        (:all      available:killer:all)      ; all of the above
+        (:immortal available:killer:immortal) ; never killed
+        (:dead     available:killer:dead)     ; never available
+        )
+
+;    external     arity  internal    immediate    ignored  killed     kills
+;    name                name        predicate             by what
+;                                                          kind of
+;                                                          effect
+
+  `((break            0 break            #f             3 ,:dead     ,:all)
+    (creg             0 creg             #f             7 ,:dead     ,:all)
+    (unspecified      0 unspecified      #f            -1 ,:dead     ,:none)
+    (undefined        0 undefined        #f             8 ,:dead     ,:none)
+    (eof-object       0 eof-object       #f            -1 ,:dead     ,:none)
+    (enable-interrupts 1 enable-interrupts #f          -1 ,:dead     ,:all)
+    (disable-interrupts 0 disable-interrupts #f        -1 ,:dead     ,:all)
+
+    (typetag          1 typetag          #f          #x11 ,:dead     ,:none)
+    (not              1 not              #f          #x18 ,:immortal ,:none)
+    (null?            1 null?            #f          #x19 ,:immortal ,:none)
+    (pair?            1 pair?            #f          #x1a ,:immortal ,:none)
+    (eof-object?      1 eof-object?      #f            -1 ,:immortal ,:none)
+    (port?            1 port?            #f            -1 ,:dead     ,:none)
+    (structure?       1 structure?       #f            -1 ,:dead     ,:none)
+    (car              1 car              #f          #x1b ,:car      ,:none)
+    (,name:CAR        1 car              #f          #x1b ,:car      ,:none)
+    (cdr              1 cdr              #f          #x1c ,:cdr      ,:none)
+    (,name:CDR        1 cdr              #f          #x1c ,:cdr      ,:none)
+    (symbol?          1 symbol?          #f          #x1f ,:immortal ,:none)
+    (number?          1 complex?         #f          #x20 ,:immortal ,:none)
+    (complex?         1 complex?         #f          #x20 ,:immortal ,:none)
+    (real?            1 rational?        #f          #x21 ,:immortal ,:none)
+    (rational?        1 rational?        #f          #x21 ,:immortal ,:none)
+    (integer?         1 integer?         #f          #x22 ,:immortal ,:none)
+    (fixnum?          1 fixnum?          #f          #x23 ,:immortal ,:none)
+    (flonum?          1 flonum?          #f            -1 ,:immortal ,:none)
+    (compnum?         1 compnum?         #f            -1 ,:immortal ,:none)
+    (exact?           1 exact?           #f          #x24 ,:immortal ,:none)
+    (inexact?         1 inexact?         #f          #x25 ,:immortal ,:none)
+    (exact->inexact   1 exact->inexact   #f          #x26 ,:immortal ,:none)
+    (inexact->exact   1 inexact->exact   #f          #x27 ,:immortal ,:none)
+    (round            1 round            #f          #x28 ,:immortal ,:none)
+    (truncate         1 truncate         #f          #x29 ,:immortal ,:none)
+    (zero?            1 zero?            #f          #x2c ,:immortal ,:none)
+    (--               1 --               #f          #x2d ,:immortal ,:none)
+    (lognot           1 lognot           #f          #x2f ,:immortal ,:none)
+    (real-part        1 real-part        #f          #x3e ,:immortal ,:none)
+    (imag-part        1 imag-part        #f          #x3f ,:immortal ,:none)
+    (char?            1 char?            #f          #x40 ,:immortal ,:none)
+    (char->integer    1 char->integer    #f          #x41 ,:immortal ,:none)
+    (integer->char    1 integer->char    #f          #x42 ,:immortal ,:none)
+    (string?          1 string?          #f          #x50 ,:immortal ,:none)
+    (string-length    1 string-length    #f          #x51 ,:immortal ,:none)
+    (vector?          1 vector?          #f          #x52 ,:immortal ,:none)
+    (vector-length    1 vector-length    #f          #x53 ,:immortal ,:none)
+    (bytevector?      1 bytevector?      #f          #x54 ,:immortal ,:none)
+    (bytevector-length 1 bytevector-length #f        #x55 ,:immortal ,:none)
+    (bytevector-fill! 2 bytevector-fill! #f            -1 ,:dead     ,:string)
+    (make-bytevector  1 make-bytevector  #f          #x56 ,:dead     ,:none)
+    (procedure?       1 procedure?       #f          #x58 ,:immortal ,:none)
+    (procedure-length 1 procedure-length #f          #x59 ,:dead     ,:none)
+    (make-procedure   1 make-procedure   #f          #x5a ,:dead     ,:none)
+    (creg-set!        1 creg-set!        #f          #x71 ,:dead     ,:none)
+    (,name:MAKE-CELL  1 make-cell        #f          #x7e ,:dead     ,:none)
+    (,name:CELL-REF   1 cell-ref         #f          #x7f ,:cell     ,:none)
+    (,name:CELL-SET!  2 cell-set!        #f          #xdf ,:dead     ,:cell)
+    (typetag-set!     2 typetag-set! ,valid-typetag? #xa0 ,:dead     ,:all)
+    (eq?              2 eq?           ,sparc-eq-imm? #xa1 ,:immortal ,:none)
+    (eqv?             2 eqv?             #f          #xa2 ,:immortal ,:none)
+    (cons             2 cons             #f          #xa8 ,:dead     ,:none)
+    (,name:CONS       2 cons             #f          #xa8 ,:dead     ,:none)
+    (set-car!         2 set-car!         #f          #xa9 ,:dead     ,:car)
+    (set-cdr!         2 set-cdr!         #f          #xaa ,:dead     ,:cdr)
+    (+                2 +                ,sparc-imm? #xb0 ,:immortal ,:none)
+    (-                2 -                ,sparc-imm? #xb1 ,:immortal ,:none)
+    (*                2 *                ,sparc-imm? #xb2 ,:immortal ,:none)
+    (/                2 /                #f          #xb3 ,:immortal ,:none)
+    (quotient         2 quotient         #f          #xb4 ,:immortal ,:none)
+    (<                2 <                ,sparc-imm? #xb5 ,:immortal ,:none)
+    (<=               2 <=               ,sparc-imm? #xb6 ,:immortal ,:none)
+    (=                2 =                ,sparc-imm? #xb7 ,:immortal ,:none)
+    (>                2 >                ,sparc-imm? #xb8 ,:immortal ,:none)
+    (>=               2 >=               ,sparc-imm? #xb9 ,:immortal ,:none)
+    (logand           2 logand           #f          #xc0 ,:immortal ,:none)
+    (logior           2 logior           #f          #xc1 ,:immortal ,:none)
+    (logxor           2 logxor           #f          #xc2 ,:immortal ,:none)
+    (lsh              2 lsh              #f          #xc3 ,:immortal ,:none)
+    (rsha             2 rsha             #f            -1 ,:immortal ,:none)
+    (rshl             2 rshl             #f            -1 ,:immortal ,:none)
+    (rot              2 rot              #f          #xc4 ,:immortal ,:none)
+    (make-string      2 make-string      #f            -1 ,:dead     ,:none)
+    (string-ref       2 string-ref       ,sparc-imm? #xd1 ,:string   ,:none)
+    (string-set!      3 string-set!      ,sparc-imm?   -1 ,:dead     ,:string)
+    (make-vector      2 make-vector      #f          #xd2 ,:dead     ,:none)
+    (vector-ref       2 vector-ref       ,sparc-imm? #xd3 ,:vector   ,:none)
+    (bytevector-ref   2 bytevector-ref   ,sparc-imm? #xd5 ,:string   ,:none)
+    (procedure-ref    2 procedure-ref    #f          #xd7 ,:dead     ,:none)
+    (char<?           2 char<?           ,char?      #xe0 ,:immortal ,:none)
+    (char<=?          2 char<=?          ,char?      #xe1 ,:immortal ,:none)
+    (char=?           2 char=?           ,char?      #xe2 ,:immortal ,:none)
+    (char>?           2 char>?           ,char?      #xe3 ,:immortal ,:none)
+    (char>=?          2 char>=?          ,char?      #xe4 ,:immortal ,:none)
+    
+    (sys$partial-list->vector 2 sys$partial-list->vector #f -1 ,:dead ,:all)
+    (vector-set!      3 vector-set!      #f          #xf1 ,:dead     ,:vector)
+    (bytevector-set!  3 bytevector-set!  #f          #xf2 ,:dead     ,:string)
+    (procedure-set!   3 procedure-set!   #f          #xf3 ,:dead     ,:all)
+    (bytevector-like? 1 bytevector-like? #f            -1 ,:immortal ,:none)
+    (vector-like?     1 vector-like?     #f            -1 ,:immortal ,:none)
+    (bytevector-like-ref 2 bytevector-like-ref #f      -1 ,:string   ,:none)
+    (bytevector-like-set! 3 bytevector-like-set! #f    -1 ,:dead     ,:string)
+    (sys$bvlcmp       2 sys$bvlcmp       #f            -1 ,:dead     ,:all)
+    (vector-like-ref  2 vector-like-ref  #f            -1 ,:vector   ,:none)
+    (vector-like-set! 3 vector-like-set! #f            -1 ,:dead     ,:vector)
+    (vector-like-length 1 vector-like-length #f        -1 ,:immortal ,:none)
+    (bytevector-like-length 1 bytevector-like-length #f -1 ,:immortal ,:none)
+    (remainder        2 remainder        #f            -1 ,:immortal ,:none)
+    (sys$read-char    1 sys$read-char    #f            -1 ,:dead     ,:io)
+    (gc-counter       0 gc-counter       #f            -1 ,:dead     ,:none)
+    ,@(if (fixnum-primitives)
+         `((most-positive-fixnum
+                          0 most-positive-fixnum
+                                         #f            -1 ,:immortal ,:none)
+           (most-negative-fixnum
+                          0 most-negative-fixnum
+                                         #f            -1 ,:immortal ,:none)
+           (fx+          2 fx+          ,sparc-imm?   -1 ,:immortal ,:none)
+           (fx-          2 fx-          ,sparc-imm?   -1 ,:immortal ,:none)
+           (fx--         1 fx--         #f            -1 ,:immortal ,:none)
+           (fx*          2 fx*          #f            -1 ,:immortal ,:none)
+           (fx=          2 fx=          ,sparc-imm?   -1 ,:immortal ,:none)
+           (fx<          2 fx<          ,sparc-imm?   -1 ,:immortal ,:none)
+           (fx<=         2 fx<=         ,sparc-imm?   -1 ,:immortal ,:none)
+           (fx>          2 fx>          ,sparc-imm?   -1 ,:immortal ,:none)
+           (fx>=         2 fx>=         ,sparc-imm?   -1 ,:immortal ,:none)
+           (fxzero?      1 fxzero?      #f            -1 ,:immortal ,:none)
+           (fxpositive?  1 fxpositive?  #f            -1 ,:immortal ,:none)
+           (fxnegative?  1 fxnegative?  #f            -1 ,:immortal ,:none))
+         '())
+    ,@(if (flonum-primitives)
+          `((fl+          2 +            #f            -1 ,:immortal ,:none)
+           (fl-          2 -            #f            -1 ,:immortal ,:none)
+           (fl--         1 --           #f            -1 ,:immortal ,:none)
+           (fl*          2 *            #f            -1 ,:immortal ,:none)
+           (fl=          2 =            #f            -1 ,:immortal ,:none)
+           (fl<          2 <            #f            -1 ,:immortal ,:none)
+           (fl<=         2 <=           #f            -1 ,:immortal ,:none)
+           (fl>          2 >            #f            -1 ,:immortal ,:none)
+           (fl>=         2 >=           #f            -1 ,:immortal ,:none))
+          '())
+
+    ; Added for CSE, representation analysis.
+
+    (,name:CHECK!    -1 check!           #f            -1 ,:dead     ,:none)
+    (vector-length:vec 1 vector-length:vec #f          -1 ,:immortal ,:none)
+    (vector-ref:trusted 2 vector-ref:trusted ,sparc-imm? -1 ,:vector   ,:none)
+    (vector-set!:trusted 3 vector-set!:trusted #f      -1 ,:dead     ,:vector)
+    (car:pair         1 car:pair         #f            -1 ,:car      ,:none)
+    (cdr:pair         1 cdr:pair         #f            -1 ,:cdr      ,:none)
+    (=:fix:fix        2 =:fix:fix        ,sparc-imm?   -1 ,:immortal ,:none)
+    (<:fix:fix        2 <:fix:fix        ,sparc-imm?   -1 ,:immortal ,:none)
+    (<=:fix:fix       2 <=:fix:fix       ,sparc-imm?   -1 ,:immortal ,:none)
+    (>=:fix:fix       2 >=:fix:fix       ,sparc-imm?   -1 ,:immortal ,:none)
+    (>:fix:fix        2 >:fix:fix        ,sparc-imm?   -1 ,:immortal ,:none)
+    
+    ; Not yet implemented.
+
+    (+:idx:idx        2 +:idx:idx        #f            -1 ,:immortal ,:none)
+    (+:fix:fix        2 +:idx:idx        #f            -1 ,:immortal ,:none)
+    (+:exi:exi        2 +:idx:idx        #f            -1 ,:immortal ,:none)
+    (+:flo:flo        2 +:idx:idx        #f            -1 ,:immortal ,:none)
+    (=:flo:flo        2 =:flo:flo        #f            -1 ,:immortal ,:none)
+    (=:obj:flo        2 =:obj:flo        #f            -1 ,:immortal ,:none)
+    (=:flo:obj        2 =:flo:obj        #f            -1 ,:immortal ,:none)
+    )))
+
+; Not used by the Sparc assembler; for information only.
+
+(define $immediate-primops$
+  '((typetag-set! #x80)
+    (eq? #x81)
+    (+ #x82)
+    (- #x83)
+    (< #x84)
+    (<= #x85)
+    (= #x86)
+    (> #x87)
+    (>= #x88)
+    (char<? #x89)
+    (char<=? #x8a)
+    (char=? #x8b)
+    (char>? #x8c)
+    (char>=? #x8d)
+    (string-ref #x90)
+    (vector-ref #x91)
+    (bytevector-ref #x92)
+    (bytevector-like-ref -1)
+    (vector-like-ref -1)
+    (fx+ -1)
+    (fx- -1)
+    (fx-- -1)
+    (fx= -1)
+    (fx< -1)
+    (fx<= -1)
+    (fx> -1)
+    (fx>= -1)))
+
+; Operations introduced by peephole optimizer.
+
+(define $reg/op1/branchf                  ; reg/op1/branchf    prim,k1,L
+  (make-mnemonic 'reg/op1/branchf))
+(define $reg/op2/branchf                  ; reg/op2/branchf    prim,k1,k2,L
+  (make-mnemonic 'reg/op2/branchf))
+(define $reg/op2imm/branchf               ; reg/op2imm/branchf prim,k1,x,L
+  (make-mnemonic 'reg/op2imm/branchf))
+(define $reg/op1/check             ; reg/op1/check      prim,k1,k2,k3,k4,exn
+  (make-mnemonic 'reg/op1/check))
+(define $reg/op2/check             ; reg/op2/check      prim,k1,k2,k3,k4,k5,exn
+  (make-mnemonic 'reg/op2/check))
+(define $reg/op2imm/check          ; reg/op2imm/check   prim,k1,x,k2,k3,k4,exn
+  (make-mnemonic 'reg/op2imm/check))
+(define $reg/op1/setreg                   ; reg/op1/setreg     prim,k1,kr
+  (make-mnemonic 'reg/op1/setreg))
+(define $reg/op2/setreg                   ; reg/op2/setreg     prim,k1,k2,kr
+  (make-mnemonic 'reg/op2/setreg))
+(define $reg/op2imm/setreg                ; reg/op2imm/setreg  prim,k1,x,kr
+  (make-mnemonic 'reg/op2imm/setreg))
+(define $reg/branchf                      ; reg/branchf        k, L
+  (make-mnemonic 'reg/branchf))
+(define $reg/return                       ; reg/return         k
+  (make-mnemonic 'reg/return))
+(define $reg/setglbl                      ; reg/setglbl        k,x
+  (make-mnemonic 'reg/setglbl))
+(define $reg/op3                          ; reg/op3            prim,k1,k2,k3
+  (make-mnemonic 'reg/op3))
+(define $const/setreg                     ; const/setreg       const,k
+  (make-mnemonic 'const/setreg))
+(define $const/return                     ; const/return       const
+  (make-mnemonic 'const/return))
+(define $global/setreg                    ; global/setreg      x,k
+  (make-mnemonic 'global/setreg))
+(define $setrtn/branch                    ; setrtn/branch      L,doc
+  (make-mnemonic 'setrtn/branch))
+(define $setrtn/invoke                    ; setrtn/invoke      L
+  (make-mnemonic 'setrtn/invoke))
+(define $global/invoke                    ; global/invoke      global,n
+  (make-mnemonic 'global/invoke))
+
+; misc
+
+(define $cons     'cons)
+(define $car:pair 'car)
+(define $cdr:pair 'cdr)
+
+; eof
+; Target-specific representations.
+;
+; A few of these representation types must be specified for every target:
+;     rep:object
+;     rep:procedure
+;     rep:true
+;     rep:false
+;     rep:bottom
+
+(define-subtype 'true       'object)      ; values that count as true
+(define-subtype 'eqtype     'object)      ; can use EQ? instead of EQV?
+(define-subtype 'nonpointer 'eqtype)      ; can omit write barrier
+(define-subtype 'eqtype1    'eqtype)      ; eqtypes excluding #f
+(define-subtype 'boolean    'nonpointer)
+(define-subtype 'truth      'eqtype1)     ; { #t }
+(define-subtype 'truth      'boolean)
+(define-subtype 'false      'boolean)     ; { #f }
+(define-subtype 'eqtype1    'true)  
+(define-subtype 'procedure  'true)
+(define-subtype 'vector     'true)
+(define-subtype 'bytevector 'true)
+(define-subtype 'string     'true)
+(define-subtype 'pair       'true)
+(define-subtype 'emptylist  'eqtype1)
+(define-subtype 'emptylist  'nonpointer)
+(define-subtype 'symbol     'eqtype1)
+(define-subtype 'char       'eqtype1)
+(define-subtype 'char       'nonpointer)
+(define-subtype 'number     'true)
+(define-subtype 'inexact    'number)
+(define-subtype 'flonum     'inexact)
+(define-subtype 'integer    'number)
+(define-subtype 'exact      'number)
+(define-subtype 'exactint   'integer)
+(define-subtype 'exactint   'exact)
+(define-subtype 'fixnum     'exactint)
+(define-subtype '!fixnum    'fixnum)      ; 0 <= n
+(define-subtype 'fixnum!    'fixnum)      ; n <= largest index
+(define-subtype 'index      '!fixnum)
+(define-subtype 'index      'fixnum!)
+(define-subtype 'zero       'index)
+(define-subtype 'fixnum     'eqtype1)
+(define-subtype 'fixnum     'nonpointer)
+
+(compute-type-structure!)
+
+; If the intersection of rep1 and rep2 is known precisely,
+; but neither is a subtype of the other, then their intersection
+; should be declared explicitly.
+; Otherwise a conservative approximation will be used.
+
+(define-intersection 'true 'eqtype 'eqtype1)
+(define-intersection 'true 'boolean 'truth)
+(define-intersection 'exact 'integer 'exactint)
+(define-intersection '!fixnum 'fixnum! 'index)
+
+;(display-unions-and-intersections)
+
+; Parameters.
+
+(define rep:min_fixnum (- (expt 2 29)))
+(define rep:max_fixnum (- (expt 2 29) 1))
+(define rep:max_index  (- (expt 2 24) 1))
+
+; The representations we'll recognize for now.
+
+(define rep:object       (symbol->rep 'object))
+(define rep:true         (symbol->rep 'true))
+(define rep:truth        (symbol->rep 'truth))
+(define rep:false        (symbol->rep 'false))
+(define rep:boolean      (symbol->rep 'boolean))
+(define rep:pair         (symbol->rep 'pair))
+(define rep:symbol       (symbol->rep 'symbol))
+(define rep:number       (symbol->rep 'number))
+(define rep:zero         (symbol->rep 'zero))
+(define rep:index        (symbol->rep 'index))
+(define rep:fixnum       (symbol->rep 'fixnum))
+(define rep:exactint     (symbol->rep 'exactint))
+(define rep:flonum       (symbol->rep 'flonum))
+(define rep:exact        (symbol->rep 'exact))
+(define rep:inexact      (symbol->rep 'inexact))
+(define rep:integer      (symbol->rep 'integer))
+;(define rep:real         (symbol->rep 'real))
+(define rep:char         (symbol->rep 'char))
+(define rep:string       (symbol->rep 'string))
+(define rep:vector       (symbol->rep 'vector))
+(define rep:procedure    (symbol->rep 'procedure))
+(define rep:bottom       (symbol->rep 'bottom))
+
+; Given the value of a quoted constant, return its representation.
+
+(define (representation-of-value x)
+  (cond ((boolean? x)
+         (if x
+             rep:truth
+             rep:false))
+        ((pair? x)
+         rep:pair)
+        ((symbol? x)
+         rep:symbol)
+        ((number? x)
+         (cond ((and (exact? x)
+                     (integer? x))
+                (cond ((zero? x)
+                       rep:zero)
+                      ((<= 0 x rep:max_index)
+                       rep:index)
+                      ((<= rep:min_fixnum
+                           x
+                           rep:max_fixnum)
+                       rep:fixnum)
+                      (else
+                       rep:exactint)))
+               ((and (inexact? x)
+                     (real? x))
+                rep:flonum)
+               (else
+                ; We're not tracking other numbers yet.
+                rep:number)))
+        ((char? x)
+         rep:char)
+        ((string? x)
+         rep:string)
+        ((vector? x)
+         rep:vector)
+        ; Everything counts as true except for #f.
+        (else
+         rep:true)))
+
+; Tables that express the representation-specific operations,
+; and the information about representations that are implied
+; by certain operations.
+; FIXME:  Currently way incomplete, but good enough for testing.
+
+(define rep-specific
+  
+  (representation-table
+   
+   ; When the procedure in the first column is called with
+   ; arguments described in the middle column, then the procedure
+   ; in the last column can be called instead.
+   
+   '(
+    ;(+                  (index index)               +:idx:idx)
+    ;(+                  (fixnum fixnum)             +:fix:fix)
+    ;(-                  (index index)               -:idx:idx)
+    ;(-                  (fixnum fixnum)             -:fix:fix)
+     
+     (=                  (fixnum fixnum)             =:fix:fix)
+     (<                  (fixnum fixnum)             <:fix:fix)
+     (<=                 (fixnum fixnum)             <=:fix:fix)
+     (>                  (fixnum fixnum)             >:fix:fix)
+     (>=                 (fixnum fixnum)             >=:fix:fix)
+     
+    ;(+                  (flonum flonum)             +:flo:flo)
+    ;(-                  (flonum flonum)             -:flo:flo)
+    ;(=                  (flonum flonum)             =:flo:flo)
+    ;(<                  (flonum flonum)             <:flo:flo)
+    ;(<=                 (flonum flonum)             <=:flo:flo)
+    ;(>                  (flonum flonum)             >:flo:flo)
+    ;(>=                 (flonum flonum)             >=:flo:flo)
+     
+    ;(vector-set!:trusted (vector fixnum nonpointer) vector-set!:trusted:imm)
+     )))
+
+(define rep-result
+  
+  (representation-table
+   
+   ; When the procedure in the first column is called with
+   ; arguments described in the middle column, then the result
+   ; is described by the last column.
+   
+   '((fixnum?           (fixnum)                    (truth))
+     (vector?           (vector)                    (truth))
+     (<=                (zero !fixnum)              (truth))
+     (>=                (!fixnum zero)              (truth))
+     (<=:fix:fix        (zero !fixnum)              (truth))
+     (>=:fix:fix        (!fixnum zero)              (truth))
+     
+     (+                 (index index)               (!fixnum))
+     (+                 (fixnum fixnum)             (exactint))
+     (-                 (index index)               (fixnum!))
+     (-                 (fixnum fixnum)             (exactint))
+     
+     (+                 (flonum flonum)             (flonum))
+     (-                 (flonum flonum)             (flonum))
+     
+    ;(+:idx:idx         (index index)               (!fixnum))
+    ;(-:idx:idx         (index index)               (fixnum!))
+    ;(+:fix:fix         (index index)               (exactint))
+    ;(+:fix:fix         (fixnum fixnum)             (exactint))
+    ;(-:idx:idx         (index index)               (fixnum))
+    ;(-:fix:fix         (fixnum fixnum)             (exactint))
+     
+     (make-vector       (object object)             (vector))
+     (vector-length:vec (vector)                    (index))
+     (cons              (object object)             (pair))
+     
+     ; Is it really all that useful to know that the result
+     ; of these comparisons is a boolean?
+     
+     (=                 (number number)             (boolean))
+     (<                 (number number)             (boolean))
+     (<=                (number number)             (boolean))
+     (>                 (number number)             (boolean))
+     (>=                (number number)             (boolean))
+     
+     (=:fix:fix         (fixnum fixnum)             (boolean))
+     (<:fix:fix         (fixnum fixnum)             (boolean))
+     (<=:fix:fix        (fixnum fixnum)             (boolean))
+     (>:fix:fix         (fixnum fixnum)             (boolean))
+     (>=:fix:fix        (fixnum fixnum)             (boolean))
+     )))
+
+(define rep-informing
+  
+  (representation-table
+   
+   ; When the predicate in the first column is called in the test position
+   ; of a conditional expression, on arguments described by the second
+   ; column, then the arguments are described by the third column if the
+   ; predicate returns true, and by the fourth column if the predicate
+   ; returns false.
+   
+   '(
+     (fixnum?     (object)           (fixnum)          (object))
+     (flonum?     (object)           (flonum)          (object))
+     (vector?     (object)           (vector)          (object))
+     (pair?       (object)           (pair)            (object))
+     
+     (=           (exactint index)   (index index)     (exactint index))
+     (=           (index exactint)   (index index)     (index exactint))
+     (=           (exactint !fixnum) (!fixnum !fixnum) (exactint !fixnum))
+     (=           (!fixnum exactint) (!fixnum !fixnum) (!fixnum exactint))
+     (=           (exactint fixnum!) (fixnum! fixnum!) (exactint fixnum!))
+     (=           (fixnum! exactint) (fixnum! fixnum!) (fixnum! exactint))
+     
+     (<           (!fixnum fixnum!)  (index index)     (!fixnum fixnum!))
+     (<           (fixnum fixnum!)   (fixnum! fixnum!) (fixnum fixnum!))
+     (<           (!fixnum fixnum)   (!fixnum !fixnum) (!fixnum fixnum))
+     (<           (fixnum! !fixnum)  (fixnum! !fixnum) (index index))
+     
+     (<=          (!fixnum fixnum!)  (index index)     (!fixnum fixnum!))
+     (<=          (fixnum! !fixnum)  (fixnum! !fixnum) (index index))
+     (<=          (fixnum fixnum!)   (fixnum! fixnum!) (fixnum fixnum!))
+     (<=          (!fixnum fixnum)   (!fixnum !fixnum) (!fixnum fixnum))
+     
+     (>           (!fixnum fixnum!)  (!fixnum fixnum!) (index index))
+     (>           (fixnum! !fixnum)  (index index)     (fixnum! !fixnum))
+     (>           (fixnum fixnum!)   (fixnum fixnum!)  (fixnum! fixnum!))
+     (>           (!fixnum fixnum)   (!fixnum fixnum)  (!fixnum !fixnum))
+     
+     (>=          (!fixnum fixnum!)  (!fixnum fixnum!) (index index))
+     (>=          (fixnum! !fixnum)  (index index)     (fixnum! !fixnum))
+     (>=          (fixnum fixnum!)   (fixnum fixnum!)  (fixnum! fixnum!))
+     (>=          (!fixnum fixnum)   (!fixnum fixnum)  (!fixnum !fixnum))
+     
+     (=:fix:fix   (exactint index)   (index index)     (exactint index))
+     (=:fix:fix   (index exactint)   (index index)     (index exactint))
+     (=:fix:fix   (exactint !fixnum) (!fixnum !fixnum) (exactint !fixnum))
+     (=:fix:fix   (!fixnum exactint) (!fixnum !fixnum) (!fixnum exactint))
+     (=:fix:fix   (exactint fixnum!) (fixnum! fixnum!) (exactint fixnum!))
+     (=:fix:fix   (fixnum! exactint) (fixnum! fixnum!) (fixnum! exactint))
+     
+     (<:fix:fix   (!fixnum fixnum!)  (index index)     (!fixnum fixnum!))
+     (<:fix:fix   (fixnum! !fixnum)  (fixnum! !fixnum) (index index))
+     (<:fix:fix   (fixnum fixnum!)   (fixnum! fixnum!) (fixnum fixnum!))
+     (<:fix:fix   (!fixnum fixnum)   (!fixnum !fixnum) (!fixnum fixnum))
+     
+     (<=:fix:fix  (!fixnum fixnum!)  (index index)     (!fixnum fixnum!))
+     (<=:fix:fix  (fixnum! !fixnum)  (fixnum! !fixnum) (index index))
+     (<=:fix:fix  (fixnum fixnum!)   (fixnum! fixnum!) (fixnum fixnum!))
+     (<=:fix:fix  (!fixnum fixnum)   (!fixnum !fixnum) (!fixnum fixnum))
+     
+     (>:fix:fix   (!fixnum fixnum!)  (!fixnum fixnum!) (index index))
+     (>:fix:fix   (fixnum! !fixnum)  (index index)     (fixnum! !fixnum))
+     (>:fix:fix   (fixnum fixnum!)   (fixnum fixnum!)  (fixnum! fixnum!))
+     (>:fix:fix   (!fixnum fixnum)   (!fixnum fixnum)  (!fixnum !fixnum))
+     
+     (>=:fix:fix  (!fixnum fixnum!)  (!fixnum fixnum!) (index index))
+     (>=:fix:fix  (fixnum! !fixnum)  (index index)     (fixnum! !fixnum))
+     (>=:fix:fix  (fixnum fixnum!)   (fixnum fixnum!)  (fixnum! fixnum!))
+     (>=:fix:fix  (!fixnum fixnum)   (!fixnum fixnum)  (!fixnum !fixnum))
+     )))
+; Copyright 1991 William D Clinger.
+;
+; Permission to copy this software, in whole or in part, to use this
+; software for any lawful noncommercial purpose, and to redistribute
+; this software is granted subject to the restriction that all copies
+; made of this software must include this copyright notice in full.
+; 
+; I also request that you send me a copy of any improvements that you
+; make to this software so that they may be incorporated within it to
+; the benefit of the Scheme community.
+;
+; 25 April 1999.
+;
+; Second pass of the Twobit compiler:
+;   single assignment analysis, local source transformations,
+;   assignment elimination, and lambda lifting.
+; The code for assignment elimination and lambda lifting
+; are in a separate file.
+;
+; This pass operates as a source-to-source transformation on
+; expressions written in the subset of Scheme described by the
+; following grammar, where the input and output expressions
+; satisfy certain additional invariants described below.
+;
+; "X ..." means zero or more occurrences of X.
+;
+; L  -->  (lambda (I_1 ...)
+;           (begin D ...)
+;           (quote (R F G <decls> <doc>)
+;           E)
+;      |  (lambda (I_1 ... . I_rest)
+;           (begin D ...)
+;           (quote (R F G <decls> <doc>))
+;           E)
+; D  -->  (define I L)
+; E  -->  (quote K)                        ; constants
+;      |  (begin I)                        ; variable references
+;      |  L                                ; lambda expressions
+;      |  (E0 E1 ...)                      ; calls
+;      |  (set! I E)                       ; assignments
+;      |  (if E0 E1 E2)                    ; conditionals
+;      |  (begin E0 E1 E2 ...)             ; sequential expressions
+; I  -->  <identifier>
+;
+; R  -->  ((I <references> <assignments> <calls>) ...)
+; F  -->  (I ...)
+; G  -->  (I ...)
+;
+; Invariants that hold for the input only:
+;   *  There are no internal definitions.
+;   *  No identifier containing an upper case letter is bound anywhere.
+;      (Change the "name:..." variables if upper case is preferred.)
+;   *  No identifier is bound in more than one place.
+;   *  Each R contains one entry for every identifier bound in the
+;      formal argument list and the internal definition list that
+;      precede it.  Each entry contains a list of pointers to all
+;      references to the identifier, a list of pointers to all
+;      assignments to the identifier, and a list of pointers to all
+;      calls to the identifier.
+;   *  Except for constants, the expression does not share structure
+;      with the original input or itself, except that the references
+;      and assignments in R are guaranteed to share structure with
+;      the expression.  Thus the expression may be side effected, and
+;      side effects to references or assignments obtained through R
+;      are guaranteed to change the references or assignments pointed
+;      to by R.
+;
+; Invariants that hold for the output only:
+;   *  There are no assignments except to global variables.
+;   *  If I is declared by an internal definition, then the right hand
+;      side of the internal definition is a lambda expression and I
+;      is referenced only in the procedure position of a call.
+;   *  Each R contains one entry for every identifier bound in the
+;      formal argument list and the internal definition list that
+;      precede it.  Each entry contains a list of pointers to all
+;      references to the identifier, a list of pointers to all
+;      assignments to the identifier, and a list of pointers to all
+;      calls to the identifier.
+;   *  For each lambda expression, the associated F is a list of all
+;      the identifiers that occur free in the body of that lambda
+;      expression, and possibly a few extra identifiers that were
+;      once free but have been removed by optimization.
+;   *  For each lambda expression, the associated G is a subset of F
+;      that contains every identifier that occurs free within some
+;      inner lambda expression that escapes, and possibly a few that
+;      don't.  (Assignment-elimination does not calculate G exactly.)
+;   *  Variables named IGNORED are neither referenced nor assigned.
+;   *  Except for constants, the expression does not share structure
+;      with the original input or itself, except that the references
+;      and assignments in R are guaranteed to share structure with
+;      the expression.  Thus the expression may be side effected, and
+;      side effects to references or assignments obtained through R
+;      are guaranteed to change the references or assignments pointed
+;      to by R.
+
+(define (pass2 exp)
+  (simplify exp (make-notepad #f)))
+
+; Given an expression and a "notepad" data structure that conveys
+; inherited attributes, performs the appropriate optimizations and
+; destructively modifies the notepad to record various attributes
+; that it synthesizes while traversing the expression.  In particular,
+; any nested lambda expressions and any variable references will be
+; noted in the notepad.
+
+(define (simplify exp notepad)
+  (case (car exp)
+    ((quote)    exp)
+    ((lambda)   (simplify-lambda exp notepad))
+    ((set!)     (simplify-assignment exp notepad))
+    ((if)       (simplify-conditional exp notepad))
+    ((begin)    (if (variable? exp)
+                    (begin (notepad-var-add! notepad (variable.name exp))
+                           exp)
+                    (simplify-sequential exp notepad)))
+    (else       (simplify-call exp notepad))))
+
+; Most optimization occurs here.
+; The  right hand sides of internal definitions are simplified,
+; as is the body.
+; Internal definitions of enclosed lambda expressions may
+; then be lifted to this one.
+; Single assignment analysis creates internal definitions.
+; Single assignment elimination converts single assignments
+; to bindings where possible, and renames arguments whose value
+; is ignored.
+; Assignment elimination then replaces all remaining assigned
+; variables by heap-allocated cells.
+
+(define (simplify-lambda exp notepad)
+  (notepad-lambda-add! notepad exp)
+  (let ((defs (lambda.defs exp))
+        (body (lambda.body exp))
+        (newnotepad (make-notepad exp)))
+    (for-each (lambda (def)
+                (simplify-lambda (def.rhs def) newnotepad))
+              defs)
+    (lambda.body-set! exp (simplify body newnotepad))
+    (lambda.F-set! exp (notepad-free-variables newnotepad))
+    (lambda.G-set! exp (notepad-captured-variables newnotepad))
+    (single-assignment-analysis exp newnotepad)
+    (let ((known-lambdas (notepad.nonescaping newnotepad)))
+      (for-each (lambda (L)
+                  (if (memq L known-lambdas)
+                      (lambda-lifting L exp)
+                      (lambda-lifting L L)))
+                (notepad.lambdas newnotepad))))
+  (single-assignment-elimination exp notepad)
+  (assignment-elimination exp)
+  (if (not (notepad.parent notepad))
+      ; This is an outermost lambda expression.
+      (lambda-lifting exp exp))
+  exp)
+
+; SIMPLIFY-ASSIGNMENT performs this transformation:
+;
+;    (set! I (begin ... E))
+; -> (begin ... (set! I E))
+
+(define (simplify-assignment exp notepad)
+  (notepad-var-add! notepad (assignment.lhs exp))
+  (let ((rhs (simplify (assignment.rhs exp) notepad)))
+    (cond ((begin? rhs)
+           (let ((exprs (reverse (begin.exprs rhs))))
+             (assignment.rhs-set! exp (car exprs))
+             (post-simplify-begin
+              (make-begin (reverse (cons exp (cdr exprs))))
+              notepad)))
+          (else (assignment.rhs-set! exp rhs) exp))))
+
+(define (simplify-sequential exp notepad)
+  (let ((exprs (map (lambda (exp) (simplify exp notepad))
+                    (begin.exprs exp))))
+    (begin.exprs-set! exp exprs)
+    (post-simplify-begin exp notepad)))
+
+; Given (BEGIN E0 E1 E2 ...) where the E_i are simplified expressions,
+; flattens any nested BEGINs and removes trivial expressions that
+; don't appear in the last position.  The second argument is used only
+; if a lambda expression is removed.
+; This procedure is careful to return E instead of (BEGIN E).
+; Fairly harmless bug: a variable reference removed by this procedure
+; may remain on the notepad when it shouldn't.
+
+(define (post-simplify-begin exp notepad)
+  (let ((unspecified-expression (make-unspecified)))
+    ; (flatten exprs '()) returns the flattened exprs in reverse order.
+    (define (flatten exprs flattened)
+      (cond ((null? exprs) flattened)
+            ((begin? (car exprs))
+             (flatten (cdr exprs)
+                      (flatten (begin.exprs (car exprs)) flattened)))
+            (else (flatten (cdr exprs) (cons (car exprs) flattened)))))
+    (define (filter exprs filtered)
+      (if (null? exprs)
+          filtered
+          (let ((exp (car exprs)))
+            (cond ((constant? exp) (filter (cdr exprs) filtered))
+                  ((variable? exp) (filter (cdr exprs) filtered))
+                  ((lambda? exp)
+                   (notepad.lambdas-set!
+                    notepad
+                    (remq exp (notepad.lambdas notepad)))
+                   (filter (cdr exprs) filtered))
+                  ((equal? exp unspecified-expression)
+                   (filter (cdr exprs) filtered))
+                  (else (filter (cdr exprs) (cons exp filtered)))))))
+    (let ((exprs (flatten (begin.exprs exp) '())))
+      (begin.exprs-set! exp (filter (cdr exprs) (list (car exprs))))
+      (if (null? (cdr (begin.exprs exp)))
+          (car (begin.exprs exp))
+          exp))))
+
+; SIMPLIFY-CALL performs this transformation:
+;
+;    (... (begin ... E) ...)
+; -> (begin ... (... E ...))
+;
+; It also takes care of LET transformations.
+
+(define (simplify-call exp notepad)
+  (define (loop args newargs exprs)
+    (cond ((null? args)
+           (finish newargs exprs))
+          ((begin? (car args))
+           (let ((newexprs (reverse (begin.exprs (car args)))))
+             (loop (cdr args)
+                   (cons (car newexprs) newargs)
+                   (append (cdr newexprs) exprs))))
+          (else (loop (cdr args) (cons (car args) newargs) exprs))))
+  (define (finish newargs exprs)
+    (call.args-set! exp (reverse newargs))
+    (let* ((newexp
+            (if (lambda? (call.proc exp))
+                (simplify-let exp notepad)
+                (begin
+                 (call.proc-set! exp
+                                 (simplify (call.proc exp) notepad))
+                 exp)))
+           (newexp
+            (if (and (call? newexp)
+                     (variable? (call.proc newexp)))
+                (let* ((procname (variable.name (call.proc newexp)))
+                       (args (call.args newexp))
+                       (entry
+                        (and (not (null? args))
+                             (constant? (car args))
+                             (integrate-usual-procedures)
+                             (every? constant? args)
+                             (let ((entry (constant-folding-entry procname)))
+                               (and entry
+                                    (let ((predicates
+                                           (constant-folding-predicates entry)))
+                                      (and (= (length args)
+                                              (length predicates))
+                                           (let loop ((args args)
+                                                      (predicates predicates))
+                                             (cond ((null? args) entry)
+                                                   (((car predicates)
+                                                     (constant.value
+                                                      (car args)))
+                                                    (loop (cdr args)
+                                                          (cdr predicates)))
+                                                   (else #f))))))))))
+                  (if entry
+                      (make-constant (apply (constant-folding-folder entry)
+                                            (map constant.value args)))
+                      newexp))
+                newexp)))
+      (cond ((and (call? newexp)
+                  (begin? (call.proc newexp)))
+             (let ((exprs0 (reverse (begin.exprs (call.proc newexp)))))
+               (call.proc-set! newexp (car exprs0))
+               (post-simplify-begin
+                (make-begin (reverse
+                             (cons newexp
+                                   (append (cdr exprs0) exprs))))
+                notepad)))
+            ((null? exprs)
+             newexp)
+            (else
+             (post-simplify-begin
+              (make-begin (reverse (cons newexp exprs)))
+              notepad)))))
+  (call.args-set! exp (map (lambda (arg) (simplify arg notepad))
+                           (call.args exp)))
+  (loop (call.args exp) '() '()))
+
+; SIMPLIFY-LET performs these transformations:
+;
+;    ((lambda (I_1 ... I_k . I_rest) ---) E1 ... Ek Ek+1 ...)
+; -> ((lambda (I_1 ... I_k I_rest) ---) E1 ... Ek (LIST Ek+1 ...))
+;
+;    ((lambda (I1 I2 ...) (begin D ...) (quote ...) E) L ...)
+; -> ((lambda (I2 ...) (begin (define I1 L) D ...) (quote ...) E) ...)
+;
+; provided I1 is not assigned and each reference to I1 is in call position.
+;
+;    ((lambda (I1)
+;       (begin)
+;       (quote ((I1 ((begin I1)) () ())))
+;       (begin I1))
+;     E1)
+;
+; -> E1
+;
+;    ((lambda (I1)
+;       (begin)
+;       (quote ((I1 ((begin I1)) () ())))
+;       (if (begin I1) E2 E3))
+;     E1)
+;
+; -> (if E1 E2 E3)
+;
+; (Together with SIMPLIFY-CONDITIONAL, this cleans up the output of the OR
+; macro and enables certain control optimizations.)
+;
+;    ((lambda (I1 I2 ...)
+;       (begin D ...)
+;       (quote (... (I <references> () <calls>) ...) ...)
+;       E)
+;     K ...)
+; -> ((lambda (I2 ...)
+;       (begin D' ...)
+;       (quote (... ...) ...)
+;       E')
+;     ...)
+;
+; where D' ... and E' ... are obtained from D ... and E ...
+; by replacing all references to I1 by K.  This transformation
+; applies if K is a constant that can be duplicated without changing
+; its EQV? behavior.
+;
+;    ((lambda () (begin) (quote ...) E)) -> E
+;
+;    ((lambda (IGNORED I2 ...) ---) E1 E2 ...)
+; -> (begin E1 ((lambda (I2 ...) ---) E2 ...))
+;
+; (Single assignment analysis, performed by the simplifier for lambda
+; expressions, detects unused arguments and replaces them in the argument
+; list by the special identifier IGNORED.)
+
+(define (simplify-let exp notepad)
+  (define proc (call.proc exp))
+  
+  ; Loop1 operates before simplification of the lambda body.
+  
+  (define (loop1 formals actuals processed-formals processed-actuals)
+    (cond ((null? formals)
+           (if (not (null? actuals))
+               (pass2-error p2error:wna exp))
+           (return1 processed-formals processed-actuals))
+          ((symbol? formals)
+           (return1 (cons formals processed-formals)
+                    (cons (make-call-to-LIST actuals) processed-actuals)))
+          ((null? actuals)
+           (pass2-error p2error:wna exp)
+           (return1 processed-formals
+                    processed-actuals))
+          ((and (lambda? (car actuals))
+                (let ((Rinfo (R-lookup (lambda.R proc) (car formals))))
+                  (and (null? (R-entry.assignments Rinfo))
+                       (= (length (R-entry.references Rinfo))
+                          (length (R-entry.calls Rinfo))))))
+           (let ((I (car formals))
+                 (L (car actuals)))
+             (notepad-nonescaping-add! notepad L)
+             (lambda.defs-set! proc
+               (cons (make-definition I L)
+                     (lambda.defs proc)))
+             (standardize-known-calls L
+                                      (R-entry.calls
+                                       (R-lookup (lambda.R proc) I)))
+             (lambda.F-set! proc (union (lambda.F proc)
+                                        (free-variables L)))
+             (lambda.G-set! proc (union (lambda.G proc) (lambda.G L))))
+           (loop1 (cdr formals)
+                  (cdr actuals)
+                  processed-formals
+                  processed-actuals))
+          ((and (constant? (car actuals))
+                (let ((x (constant.value (car actuals))))
+                  (or (boolean? x)
+                      (number? x)
+                      (symbol? x)
+                      (char? x))))
+           (let* ((I (car formals))
+                  (Rinfo (R-lookup (lambda.R proc) I)))
+             (if (null? (R-entry.assignments Rinfo))
+                 (begin
+                  (for-each (lambda (ref)
+                              (variable-set! ref (car actuals)))
+                            (R-entry.references Rinfo))
+                  (lambda.R-set! proc (remq Rinfo (lambda.R proc)))
+                  (lambda.F-set! proc (remq I (lambda.F proc)))
+                  (lambda.G-set! proc (remq I (lambda.G proc)))
+                  (loop1 (cdr formals)
+                         (cdr actuals)
+                         processed-formals
+                         processed-actuals))
+                 (loop1 (cdr formals)
+                        (cdr actuals)
+                        (cons (car formals) processed-formals)
+                        (cons (car actuals) processed-actuals)))))
+          (else (if (null? actuals)
+                    (pass2-error p2error:wna exp))
+                (loop1 (cdr formals)
+                       (cdr actuals)
+                       (cons (car formals) processed-formals)
+                       (cons (car actuals) processed-actuals)))))
+  
+  (define (return1 rev-formals rev-actuals)
+    (let ((formals (reverse rev-formals))
+          (actuals (reverse rev-actuals)))
+      (lambda.args-set! proc formals)
+      (if (and (not (null? formals))
+               (null? (cdr formals))
+               (let* ((x (car formals))
+                      (R (lambda.R proc))
+                      (refs (references R x)))
+                 (and (= 1 (length refs))
+                      (null? (assignments R x)))))
+          (let ((x (car formals))
+                (body (lambda.body proc)))
+            (cond ((and (variable? body)
+                        (eq? x (variable.name body)))
+                   (simplify (car actuals) notepad))
+                  ((and (conditional? body)
+                        (let ((B0 (if.test body)))
+                          (variable? B0)
+                          (eq? x (variable.name B0))))
+                   (if.test-set! body (car actuals))
+                   (simplify body notepad))
+                  (else
+                   (return1-finish formals actuals))))
+          (return1-finish formals actuals))))
+  
+  (define (return1-finish formals actuals)
+    (simplify-lambda proc notepad)
+    (loop2 formals actuals '() '() '()))
+  
+  ; Loop2 operates after simplification of the lambda body.
+  
+  (define (loop2 formals actuals processed-formals processed-actuals for-effect)
+    (cond ((null? formals)
+           (return2 processed-formals processed-actuals for-effect))
+          ((ignored? (car formals))
+           (loop2 (cdr formals)
+                  (cdr actuals)
+                  processed-formals
+                  processed-actuals
+                  (cons (car actuals) for-effect)))
+          (else (loop2 (cdr formals)
+                       (cdr actuals)
+                       (cons (car formals) processed-formals)
+                       (cons (car actuals) processed-actuals)
+                       for-effect))))
+  
+  (define (return2 rev-formals rev-actuals rev-for-effect)
+    (let ((formals (reverse rev-formals))
+          (actuals (reverse rev-actuals))
+          (for-effect (reverse rev-for-effect)))
+      (lambda.args-set! proc formals)
+      (call.args-set! exp actuals)
+      (let ((exp (if (and (null? actuals)
+                          (or (null? (lambda.defs proc))
+                              (and (notepad.parent notepad)
+                                   (POLICY:LIFT? proc
+                                                 (notepad.parent notepad)
+                                                 (map (lambda (def) '())
+                                                      (lambda.defs proc))))))
+                     (begin (for-each (lambda (I)
+                                        (notepad-var-add! notepad I))
+                                      (lambda.F proc))
+                            (if (not (null? (lambda.defs proc)))
+                                (let ((parent (notepad.parent notepad))
+                                      (defs (lambda.defs proc))
+                                      (R (lambda.R proc)))
+                                  (lambda.defs-set!
+                                    parent
+                                    (append defs (lambda.defs parent)))
+                                  (lambda.defs-set! proc '())
+                                  (lambda.R-set!
+                                    parent
+                                    (append (map (lambda (def)
+                                                   (R-lookup R (def.lhs def)))
+                                                 defs)
+                                            (lambda.R parent)))))
+                            (lambda.body proc))
+                     exp)))
+        (if (null? for-effect)
+            exp
+            (post-simplify-begin (make-begin (append for-effect (list exp)))
+                                 notepad)))))
+  
+  (notepad-nonescaping-add! notepad proc)
+  (loop1 (lambda.args proc) (call.args exp) '() '()))
+
+; Single assignment analysis performs the transformation
+;
+;    (lambda (... I ...)
+;      (begin D ...)
+;      (quote (... (I <references> ((set! I L)) <calls>) ...) ...)
+;      (begin (set! I L) E1 ...))
+; -> (lambda (... IGNORED ...)
+;      (begin (define I L) D ...)
+;      (quote (... (I <references> () <calls>) ...) ...)
+;      (begin E1 ...))
+;
+; For best results, pass 1 should sort internal definitions and LETRECs so
+; that procedure definitions/bindings come first.
+;
+; This procedure operates by side effect.
+
+(define (single-assignment-analysis L notepad)
+  (let ((formals (lambda.args L))
+        (defs (lambda.defs L))
+        (R (lambda.R L))
+        (body (lambda.body L)))
+    (define (finish! exprs escapees)
+      (begin.exprs-set! body
+                        (append (reverse escapees)
+                                exprs))
+      (lambda.body-set! L (post-simplify-begin body '())))
+    (if (begin? body)
+        (let loop ((exprs (begin.exprs body))
+                   (escapees '()))
+          (let ((first (car exprs)))
+            (if (and (assignment? first)
+                     (not (null? (cdr exprs))))
+                (let ((I (assignment.lhs first))
+                      (rhs (assignment.rhs first)))
+                  (if (and (lambda? rhs)
+                           (local? R I)
+                           (= 1 (length (assignments R I))))
+                      (if (= (length (calls R I))
+                             (length (references R I)))
+                          (begin (notepad-nonescaping-add! notepad rhs)
+                                 (flag-as-ignored I L)
+                                 (lambda.defs-set! L
+                                   (cons (make-definition I rhs)
+                                         (lambda.defs L)))
+                                 (assignments-set! R I '())
+                                 (standardize-known-calls
+                                  rhs
+                                  (R-entry.calls (R-lookup R I)))
+                                 (loop (cdr exprs) escapees))
+                          (loop (cdr exprs)
+                                (cons (car exprs) escapees)))
+                      (finish! exprs escapees)))
+                (finish! exprs escapees)))))))
+
+(define (standardize-known-calls L calls)
+  (let ((formals (lambda.args L)))
+    (cond ((not (list? formals))
+           (let* ((newformals (make-null-terminated formals))
+                  (n (- (length newformals) 1)))
+             (lambda.args-set! L newformals)
+             (for-each (lambda (call)
+                         (if (>= (length (call.args call)) n)
+                             (call.args-set!
+                              call
+                              (append (list-head (call.args call) n)
+                                      (list
+                                       (make-call-to-LIST
+                                        (list-tail (call.args call) n)))))
+                             (pass2-error p2error:wna call)))
+                       calls)))
+          (else (let ((n (length formals)))
+                  (for-each (lambda (call)
+                              (if (not (= (length (call.args call)) n))
+                                  (pass2-error p2error:wna call)))
+                            calls))))))
+; Copyright 1991 William D Clinger.
+;
+; $Id: twobit.sch,v 1.3 1999/08/23 19:14:26 lth Exp $
+;
+; 13 November 1998
+;
+; Second pass of the Twobit compiler, part 2:
+;   single assignment elimination, assignment elimination,
+;   and lambda lifting.
+;
+; See part 1 for further documentation.
+
+; Single assignment elimination performs the transformation
+;
+;    (lambda (... I1 ... In ...)
+;      (begin D ...)
+;      (begin (set! I1 E1)
+;             ...
+;             (set! In En)
+;             E ...))
+; -> (lambda (... IGNORED ... IGNORED ...)
+;      (let* ((I1 E1) ... (In En))
+;        (begin D ...)
+;        (begin E ...)))
+;
+; provided for each k:
+;
+;    1.  Ik does not occur in E1, ..., Ek.
+;    2.  Either E1 through Ek contain no procedure calls
+;        or Ik is not referenced by an escaping lambda expression.
+;    3.  Ik is assigned only once.
+;
+; I doubt whether the third condition is really necessary, but
+; dropping it would involve a more complex calculation of the
+; revised referencing information.
+;
+; A more precise description of the transformation:
+;
+;    (lambda (... I1 ... In ...)
+;      (begin (define F1 L1) ...)
+;      (quote (... (I1 <references> ((set! I1 E1)) <calls>) ...
+;                  (In <references> ((set! In En)) <calls>)
+;                  (F1 <references> () <calls>) ...) ...)
+;      (begin (set! I1 E1) ... (set! In En) E ...))
+; -> (lambda (... IGNORED ... IGNORED ...)
+;      (begin)
+;      (quote (...) ...)
+;      ((lambda (I1)
+;         (begin)
+;         (quote ((I1 <references> () <calls>)) ...)
+;         ...
+;           ((lambda (In)
+;              (begin (define F1 L1) ...)
+;              (quote (... (In <references> () <calls>)
+;                          (F1 <references> () <calls>) ...) ...)
+;              (begin E ...))
+;            En)
+;         ...)
+;       E1))
+;
+; For best results, pass 1 should sort internal definitions and LETRECs
+; so that procedure definitions/bindings come first, followed by
+; definitions/bindings whose right hand side contains no calls,
+; followed by definitions/bindings of variables that do not escape,
+; followed by all other definitions/bindings.
+;
+; Pass 1 can't tell which variables escape, however.  Pass 2 can't tell
+; which variables escape either until all enclosed lambda expressions
+; have been simplified and the first transformation above has been
+; performed.  That is why single assignment analysis precedes single
+; assignment elimination.  As implemented here, an assignment that does
+; not satisfy the conditions above will prevent the transformation from
+; being applied to any subsequent assignments.
+;
+; This procedure operates by side effect.
+
+(define (single-assignment-elimination L notepad)
+  
+  (if (begin? (lambda.body L))
+      
+      (let* ((formals (make-null-terminated (lambda.args L)))
+             (defined (map def.lhs (lambda.defs L)))
+             (escaping (intersection formals
+                                     (notepad-captured-variables notepad)))
+             (R (lambda.R L)))
+        
+        ; Given:
+        ;    exprs that remain in the body;
+        ;    assigns that will be replaced by let* variables;
+        ;    call-has-occurred?, a boolean;
+        ;    free variables of the assigns;
+        ; Performs the transformation described above.
+        
+        (define (loop exprs assigns call-has-occurred? free)
+          (cond ((null? (cdr exprs))
+                 (return exprs assigns))
+                ((assignment? (car exprs))
+                 (let ((I1 (assignment.lhs (car exprs)))
+                       (E1 (assignment.rhs (car exprs))))
+                   (if (and (memq I1 formals)
+                            (= (length (assignments R I1)) 1)
+                            (not (and call-has-occurred?
+                                      (memq I1 escaping))))
+                       (let* ((free-in-E1 (free-variables E1))
+                              (newfree (union free-in-E1 free)))
+                         (if (or (memq I1 newfree)
+                                 (not
+                                  (empty-set?
+                                   (intersection free-in-E1 defined))))
+                             (return exprs assigns)
+                             (loop (cdr exprs)
+                                   (cons (car exprs) assigns)
+                                   (or call-has-occurred?
+                                       (might-return-twice? E1))
+                                   newfree)))
+                       (return exprs assigns))))
+                (else (return exprs assigns))))
+        
+        (define (return exprs assigns)
+          (if (not (null? assigns))
+              (let ((I (assignment.lhs (car assigns)))
+                    (E (assignment.rhs (car assigns)))
+                    (defs (lambda.defs L))
+                    (F (lambda.F L))
+                    (G (lambda.G L)))
+                (flag-as-ignored I L)
+                (assignments-set! R I '())
+                (let ((L2 (make-lambda (list I)
+                                       defs
+                                       (cons (R-entry R I)
+                                             (map (lambda (def)
+                                                    (R-entry R (def.lhs def)))
+                                                  defs))
+                                       F
+                                       G
+                                       (lambda.decls L)
+                                       (lambda.doc L)
+                                       (make-begin exprs))))
+                  (lambda.defs-set! L '())
+                  (for-each (lambda (entry)
+                              (lambda.R-set! L (remq entry R)))
+                            (lambda.R L2))
+                  (return-loop (cdr assigns) (make-call L2 (list E)))))))
+        
+        (define (return-loop assigns body)
+          (if (null? assigns)
+              (let ((L3 (call.proc body)))
+                (lambda.body-set! L body)
+                (lambda-lifting L3 L))
+              (let* ((I (assignment.lhs (car assigns)))
+                     (E (assignment.rhs (car assigns)))
+                     (L3 (call.proc body))
+                     (F (remq I (lambda.F L3)))
+                     (G (remq I (lambda.G L3))))
+                (flag-as-ignored I L)
+                (assignments-set! R I '())
+                (let ((L2 (make-lambda (list I)
+                                       '()
+                                       (list (R-entry R I))
+                                       F
+                                       G
+                                       (lambda.decls L)
+                                       (lambda.doc L)
+                                       body)))
+                  (lambda.R-set! L (remq (R-entry R I) R))
+                  (lambda-lifting L3 L2)
+                  (return-loop (cdr assigns) (make-call L2 (list E)))))))
+        
+        (loop (begin.exprs (lambda.body L)) '() #f '())))
+  
+  L)
+
+; Temporary definitions.
+
+(define (free-variables exp)
+  (case (car exp)
+    ((quote)    '())
+    ((lambda)   (difference (lambda.F exp)
+                            (make-null-terminated (lambda.args exp))))
+    ((set!)     (union (list (assignment.lhs exp))
+                       (free-variables (assignment.rhs exp))))
+    ((if)       (union (free-variables (if.test exp))
+                       (free-variables (if.then exp))
+                       (free-variables (if.else exp))))
+    ((begin)    (if (variable? exp)
+                    (list (variable.name exp))
+                    (apply union (map free-variables (begin.exprs exp)))))
+    (else       (apply union (map free-variables exp)))))
+
+(define (might-return-twice? exp)
+  (case (car exp)
+    ((quote)    #f)
+    ((lambda)   #f)
+    ((set!)     (might-return-twice? (assignment.rhs exp)))
+    ((if)       (or (might-return-twice? (if.test exp))
+                    (might-return-twice? (if.then exp))
+                    (might-return-twice? (if.else exp))))
+    ((begin)    (if (variable? exp)
+                    #f
+                    (some? might-return-twice? (begin.exprs exp))))
+    (else       #t)))
+
+
+; Assignment elimination replaces variables that appear on the left
+; hand side of an assignment by data structures.  This is necessary
+; to avoid some nasty complications with lambda lifting.
+;
+; This procedure operates by side effect.
+
+(define (assignment-elimination L)
+  (let ((R (lambda.R L)))
+    
+    ; Given a list of entries, return those for assigned variables.
+    
+    (define (loop entries assigned)
+      (cond ((null? entries)
+             (if (not (null? assigned))
+                 (eliminate assigned)))
+            ((not (null? (R-entry.assignments (car entries))))
+             (loop (cdr entries) (cons (car entries) assigned)))
+            ((null? (R-entry.references (car entries)))
+             (flag-as-ignored (R-entry.name (car entries)) L)
+             (loop (cdr entries) assigned))
+            (else (loop (cdr entries) assigned))))
+    
+    ; Given a list of entries for assigned variables I1 ...,
+    ; remove the assignments by replacing the body by a LET of the form
+    ; ((LAMBDA (V1 ...) ...) (MAKE-CELL I1) ...), by replacing references
+    ; by calls to CELL-REF, and by replacing assignments by calls to
+    ; CELL-SET!.
+    
+    (define (eliminate assigned)
+      (let* ((oldnames (map R-entry.name assigned))
+             (newnames (map generate-new-name oldnames)))
+        (let ((augmented-entries (map list newnames assigned))
+              (renaming-alist (map cons oldnames newnames))
+              (defs (lambda.defs L)))
+          (for-each cellify! augmented-entries)
+          (for-each (lambda (def)
+                      (do ((free (lambda.F (def.rhs def)) (cdr free)))
+                          ((null? free))
+                          (let ((z (assq (car free) renaming-alist)))
+                            (if z
+                                (set-car! free (cdr z))))))
+                    defs)
+          (let ((newbody
+                 (make-call
+                  (make-lambda (map car augmented-entries)
+                               defs
+                               (union (map (lambda (def)
+                                             (R-entry R (def.lhs def)))
+                                           defs)
+                                      (map new-reference-info augmented-entries))
+                               (union (list name:CELL-REF name:CELL-SET!)
+                                      newnames
+                                      (difference (lambda.F L) oldnames))
+                               (union (list name:CELL-REF name:CELL-SET!)
+                                      newnames
+                                      (difference (lambda.G L) oldnames))
+                               (lambda.decls L)
+                               (lambda.doc L)
+                               (lambda.body L))
+                  (map (lambda (name)
+                         (make-call (make-variable name:MAKE-CELL)
+                                    (list (make-variable name))))
+                       (map R-entry.name assigned)))))
+            (lambda.F-set! L (union (list name:MAKE-CELL name:CELL-REF name:CELL-SET!)
+                                    (difference (lambda.F L)
+                                                (map def.lhs (lambda.defs L)))))
+            (lambda.defs-set! L '())
+            (for-each update-old-reference-info!
+                      (map (lambda (arg)
+                             (car (call.args arg)))
+                           (call.args newbody)))
+            (lambda.body-set! L newbody)
+            (lambda-lifting (call.proc newbody) L)))))
+    
+    (define (generate-new-name name)
+      (string->symbol (string-append cell-prefix (symbol->string name))))
+    
+    ; In addition to replacing references and assignments involving the
+    ; old variable by calls to CELL-REF and CELL-SET! on the new, CELLIFY!
+    ; uses the old entry to collect the referencing information for the
+    ; new variable.
+    
+    (define (cellify! augmented-entry)
+      (let ((newname (car augmented-entry))
+            (entry (cadr augmented-entry)))
+        (do ((refs (R-entry.references entry)
+                   (cdr refs)))
+            ((null? refs))
+            (let* ((reference (car refs))
+                   (newref (make-variable newname)))
+              (set-car! reference (make-variable name:CELL-REF))
+              (set-car! (cdr reference) newref)
+              (set-car! refs newref)))
+        (do ((assigns (R-entry.assignments entry)
+                      (cdr assigns)))
+            ((null? assigns))
+            (let* ((assignment (car assigns))
+                   (newref (make-variable newname)))
+              (set-car! assignment (make-variable name:CELL-SET!))
+              (set-car! (cdr assignment) newref)
+              (R-entry.references-set! entry
+                                       (cons newref
+                                             (R-entry.references entry)))))
+        (R-entry.assignments-set! entry '())))
+    
+    ; This procedure creates a brand new entry for a new variable, extracting
+    ; the references stored in the old entry by CELLIFY!.
+    
+    (define (new-reference-info augmented-entry)
+      (make-R-entry (car augmented-entry)
+                    (R-entry.references (cadr augmented-entry))
+                    '()
+                    '()))
+    
+    ; This procedure updates the old entry to reflect the fact that it is
+    ; now referenced once and never assigned.
+    
+    (define (update-old-reference-info! ref)
+      (references-set! R (variable.name ref) (list ref))
+      (assignments-set! R (variable.name ref) '())
+      (calls-set! R (variable.name ref) '()))
+    
+    (loop R '())))
+
+; Lambda lifting raises internal definitions to outer scopes to avoid
+; having to choose between creating a closure or losing tail recursion.
+; If L is not #f, then L2 is a lambda expression nested within L.
+; Any internal definitions that occur within L2 may be lifted to L
+; by adding extra arguments to the defined procedure and to all calls to it.
+; Lambda lifting is not a clear win, because the extra arguments could
+; easily become more expensive than creating a closure and referring
+; to the non-local arguments through the closure.  The heuristics used
+; to decide whether to lift a group of internal definitions are isolated
+; within the POLICY:LIFT? procedure.
+
+; L2 can be the same as L, so the order of side effects is critical.
+
+(define (lambda-lifting L2 L)
+  
+  ; The call to sort is optional.  It gets the added arguments into
+  ; the same order they appear in the formals list, which is an
+  ; advantage for register targeting.
+  
+  (define (lift L2 L args-to-add)
+    (let ((formals (make-null-terminated (lambda.args L2))))
+      (do ((defs (lambda.defs L2) (cdr defs))
+           (args-to-add args-to-add (cdr args-to-add)))
+          ((null? defs))
+          (let* ((def (car defs))
+                 (entry (R-lookup (lambda.R L2) (def.lhs def)))
+                 (calls (R-entry.calls entry))
+                 (added (twobit-sort (lambda (x y)
+                                       (let ((xx (memq x formals))
+                                             (yy (memq y formals)))
+                                         (if (and xx yy)
+                                             (> (length xx) (length yy))
+                                             #t)))
+                                     (car args-to-add)))
+                 (L3 (def.rhs def)))
+            ; The flow equation guarantees that these added arguments
+            ; will occur free by the time this round of lifting is done.
+            (lambda.F-set! L3 (union added (lambda.F L3)))
+            (lambda.args-set! L3 (append added (lambda.args L3)))
+            (for-each (lambda (call)
+                        (let ((newargs (map make-variable added)))
+                          ; The referencing information is made obsolete here!
+                          (call.args-set! call
+                                          (append newargs (call.args call)))))
+                      calls)
+            (lambda.R-set! L2 (remq entry (lambda.R L2)))
+            (lambda.R-set! L (cons entry (lambda.R L)))
+            ))
+      (if (not (eq? L2 L))
+          (begin
+           (lambda.defs-set! L (append (lambda.defs L2) (lambda.defs L)))
+           (lambda.defs-set! L2 '())))))
+  
+  (if L
+      (if (not (null? (lambda.defs L2)))
+          (let ((args-to-add (compute-added-arguments
+                              (lambda.defs L2)
+                              (make-null-terminated (lambda.args L2)))))
+            (if (POLICY:LIFT? L2 L args-to-add)
+                (lift L2 L args-to-add))))))
+
+; Given a list of definitions ((define f1 ...) ...) and a set of formals
+; N over which the definitions may be lifted, returns a list of the
+; subsets of N that need to be added to each procedure definition
+; as new arguments.
+;
+; Algorithm: Let F_i be the variables that occur free in the body of
+; the lambda expression associated with f_i.  Construct the call graph.
+; Solve the flow equations
+;
+;     A_i = (F_i /\ N) \/ (\/ {A_j | A_i calls A_j})
+;
+; where /\ is intersection and \/ is union.
+
+(define (compute-added-arguments defs formals)
+  (let ((procs (map def.lhs defs))
+        (freevars (map lambda.F (map def.rhs defs))))
+    (let ((callgraph (map (lambda (names)
+                            (map (lambda (name)
+                                   (position name procs))
+                                 (intersection names procs)))
+                          freevars))
+          (added_0 (map (lambda (names)
+                          (intersection names formals))
+                        freevars)))
+      (vector->list
+       (compute-fixedpoint
+        (make-vector (length procs) '())
+        (list->vector (map (lambda (term0 indexes)
+                             (lambda (approximations)
+                               (union term0
+                                      (apply union
+                                             (map (lambda (i)
+                                                    (vector-ref approximations i))
+                                                  indexes)))))
+                           added_0
+                           callgraph))
+        set-equal?)))))
+
+(define (position x l)
+  (cond ((eq? x (car l)) 0)
+        (else (+ 1 (position x (cdr l))))))
+
+; Given a vector of starting approximations,
+; a vector of functions that compute a next approximation
+; as a function of the vector of approximations,
+; and an equality predicate,
+; returns a vector of fixed points.
+
+(define (compute-fixedpoint v functions equiv?)
+  (define (loop i flag)
+    (if (negative? i)
+        (if flag
+            (loop (- (vector-length v) 1) #f)
+            v)
+        (let ((next_i ((vector-ref functions i) v)))
+          (if (equiv? next_i (vector-ref v i))
+              (loop (- i 1) flag)
+              (begin (vector-set! v i next_i)
+                     (loop (- i 1) #t))))))
+  (loop (- (vector-length v) 1) #f))
+
+
+; Given a lambda expression L2, its parent lambda expression
+; L (which may be the same as L2, or #f), and a list of the
+; lists of arguments that would need to be added to known
+; local procedures, returns #t iff lambda lifting should be done.
+;
+; Here are some heuristics:
+;
+;   Don't lift if it means adding too many arguments.
+;   Don't lift large groups of definitions.
+;   In questionable cases it is better to lift to an outer
+;     lambda expression that already contains internal
+;     definitions than to one that doesn't.
+;   It is better not to lift if the body contains a lambda
+;     expression that has to be closed anyway.
+
+(define (POLICY:LIFT? L2 L args-to-add)
+  (and (lambda-optimizations)
+       (not (lambda? (lambda.body L2)))
+       (every? (lambda (addlist)
+                 (< (length addlist) 6))
+               args-to-add)))
+; Copyright 1991 William D Clinger (for SIMPLIFY-CONDITIONAL)
+; Copyright 1999 William D Clinger (for everything else)
+;
+; Permission to copy this software, in whole or in part, to use this
+; software for any lawful noncommercial purpose, and to redistribute
+; this software is granted subject to the restriction that all copies
+; made of this software must include this copyright notice in full.
+; 
+; I also request that you send me a copy of any improvements that you
+; make to this software so that they may be incorporated within it to
+; the benefit of the Scheme community.
+;
+; 11 April 1999.
+;
+; Some source transformations on IF expressions:
+;
+; (if '#f E1 E2)                      E2
+; (if 'K  E1 E2)                      E1                    K != #f
+; (if (if B0 '#f '#f) E1 E2)          (begin B0 E2)
+; (if (if B0 '#f 'K ) E1 E2)          (if B0 E2 E1)         K != #f
+; (if (if B0 'K  '#f) E1 E2)          (if B0 E1 E2)         K != #f
+; (if (if B0 'K1 'K2) E1 E2)          (begin B0 E1)         K1, K2 != #f
+; (if (if B0 (if B1 #t #f) B2) E1 E2) (if (if B0 B1 B2) E1 E2)
+; (if (if B0 B1 (if B2 #t #f)) E1 E2) (if (if B0 B1 B2) E1 E2)
+; (if (if X  X   B0 ) E1 E2)          (if (if X #t B0) E1 E2)   X a variable
+; (if (if X  B0  X  ) E1 E2)          (if (if X B0 #f) E1 E2)   X a variable
+; (if ((lambda (X)                    (if ((lambda (X)
+;        (if X X B2)) B0)                    (if X #t (if B2 #t #f))) B0)
+;     E1 E2)                              E1 E2)
+; (if (begin ... B0) E1 E2)           (begin ... (if B0 E1 E2))
+; (if (not E0) E1 E2)                 (if E0 E2 E1)         not is integrable
+;
+; FIXME:  Three of the transformations above are intended to clean up
+; the output of the OR macro.  It isn't yet clear how well this works.
+
+(define (simplify-conditional exp notepad)
+  (define (coercion-to-boolean? exp)
+    (and (conditional? exp)
+         (let ((E1 (if.then exp))
+               (E2 (if.else exp)))
+           (and (constant? E1)
+                (eq? #t (constant.value E1))
+                (constant? E2)
+                (eq? #f (constant.value E2))))))
+  (if (not (control-optimization))
+      (begin (if.test-set! exp (simplify (if.test exp) notepad))
+             (if.then-set! exp (simplify (if.then exp) notepad))
+             (if.else-set! exp (simplify (if.else exp) notepad))
+             exp)
+      (let* ((test (if.test exp)))
+        (if (and (call? test)
+                 (lambda? (call.proc test))
+                 (let* ((L (call.proc test))
+                        (body (lambda.body L)))
+                   (and (conditional? body)
+                        (let ((R (lambda.R L))
+                              (B0 (if.test body))
+                              (B1 (if.then body)))
+                          (and (variable? B0)
+                               (variable? B1)
+                               (let ((x (variable.name B0)))
+                                 (and (eq? x (variable.name B1))
+                                      (local? R x)
+                                      (= 1 (length R))
+                                      (= 1 (length (call.args test))))))))))
+            (let* ((L (call.proc test))
+                   (R (lambda.R L))
+                   (body (lambda.body L))
+                   (ref (if.then body))
+                   (x (variable.name ref))
+                   (entry (R-entry R x)))
+              (if.then-set! body (make-constant #t))
+              (if.else-set! body
+                            (make-conditional (if.else body)
+                                              (make-constant #t)
+                                              (make-constant #f)))
+              (R-entry.references-set! entry
+                                       (remq ref
+                                             (R-entry.references entry)))
+              (simplify-conditional exp notepad))
+            (let loop ((test (simplify (if.test exp) notepad)))
+              (if.test-set! exp test)
+              (cond ((constant? test)
+                     (simplify (if (constant.value test)
+                                   (if.then exp)
+                                   (if.else exp))
+                               notepad))
+                    ((and (conditional? test)
+                          (constant? (if.then test))
+                          (constant? (if.else test)))
+                     (cond ((and (constant.value (if.then test))
+                                 (constant.value (if.else test)))
+                            (post-simplify-begin
+                             (make-begin (list (if.test test)
+                                               (simplify (if.then exp)
+                                                         notepad)))
+                             notepad))
+                           ((and (not (constant.value (if.then test)))
+                                 (not (constant.value (if.else test))))
+                            (post-simplify-begin
+                             (make-begin (list (if.test test)
+                                               (simplify (if.else exp)
+                                                         notepad)))
+                             notepad))
+                           (else (if (not (constant.value (if.then test)))
+                                     (let ((temp (if.then exp)))
+                                       (if.then-set! exp (if.else exp))
+                                       (if.else-set! exp temp)))
+                                 (if.test-set! exp (if.test test))
+                                 (loop (if.test exp)))))
+                    ((and (conditional? test)
+                          (or (coercion-to-boolean? (if.then test))
+                              (coercion-to-boolean? (if.else test))))
+                     (if (coercion-to-boolean? (if.then test))
+                         (if.then-set! test (if.test (if.then test)))
+                         (if.else-set! test (if.test (if.else test))))
+                     (loop test))
+                    ((and (conditional? test)
+                          (variable? (if.test test))
+                          (let ((x (variable.name (if.test test))))
+                            (or (and (variable? (if.then test))
+                                     (eq? x (variable.name (if.then test)))
+                                     1)
+                                (and (variable? (if.else test))
+                                     (eq? x (variable.name (if.else test)))
+                                     2))))
+                     =>
+                     (lambda (n)
+                       (case n
+                         ((1) (if.then-set! test (make-constant #t)))
+                         ((2) (if.else-set! test (make-constant #f))))
+                       (loop test)))
+                    ((begin? test)
+                     (let ((exprs (reverse (begin.exprs test))))
+                       (if.test-set! exp (car exprs))
+                       (post-simplify-begin
+                        (make-begin (reverse (cons (loop (car exprs))
+                                                   (cdr exprs))))
+                        notepad)))
+                    ((and (call? test)
+                          (variable? (call.proc test))
+                          (eq? (variable.name (call.proc test)) name:NOT)
+                          (integrable? name:NOT)
+                          (integrate-usual-procedures)
+                          (= (length (call.args test)) 1))
+                     (let ((temp (if.then exp)))
+                       (if.then-set! exp (if.else exp))
+                       (if.else-set! exp temp))
+                     (loop (car (call.args test))))
+                    (else
+                     (simplify-case exp notepad))))))))
+
+; Given a conditional expression whose test has been simplified,
+; simplifies the then and else parts while applying optimizations
+; for CASE expressions.
+; Precondition: (control-optimization) is true.
+
+(define (simplify-case exp notepad)
+  (let ((E0 (if.test exp)))
+    (if (and (call? E0)
+             (variable? (call.proc E0))
+             (let ((name (variable.name (call.proc E0))))
+               ; FIXME: Should ensure that the name is integrable,
+               ; but MEMQ and MEMV probably aren't according to the
+               ; INTEGRABLE? predicate.
+               (or (eq? name name:EQ?)
+                   (eq? name name:EQV?)
+                   (eq? name name:MEMQ)
+                   (eq? name name:MEMV)))
+             (integrate-usual-procedures)
+             (= (length (call.args E0)) 2)
+             (variable? (car (call.args E0)))
+             (constant? (cadr (call.args E0))))
+        (simplify-case-clauses (variable.name (car (call.args E0)))
+                               exp
+                               notepad)
+        (begin (if.then-set! exp (simplify (if.then exp) notepad))
+               (if.else-set! exp (simplify (if.else exp) notepad))
+               exp))))
+
+; Code generation for case expressions.
+;
+; A case expression turns into a conditional expression
+; of the form
+;
+; CASE{I}  ::=  E  |  (if (PRED I K) E CASE{I})
+; PRED  ::=  memv  |  memq  |  eqv?  |  eq?
+;
+; The memq and eq? predicates are used when the constant
+; is a (list of) boolean, fixnum, char, empty list, or symbol.
+; The constants will almost always be of these types.
+;
+; The first step is to remove duplicated constants and to
+; collect all the case clauses, sorting them into the following
+; categories based on their simplified list of constants:
+;     constants are fixnums
+;     constants are characters
+;     constants are symbols
+;     constants are of mixed or other type
+; After duplicated constants have been removed, the predicates
+; for these clauses can be tested in any order.
+
+; Given the name of an arbitrary variable, an expression that
+; has not yet been simplified or can safely be simplified again,
+; and a notepad, returns the expression after simplification.
+; If the expression is equivalent to a case expression that dispatches
+; on the given variable, then case-optimization will be applied.
+
+(define (simplify-case-clauses var0 E notepad)
+  
+  (define notepad2 (make-notepad (notepad.parent notepad)))
+  
+  (define (collect-clauses E fix chr sym other constants)
+    (if (not (conditional? E))
+        (analyze (simplify E notepad2)
+                 fix chr sym other constants)
+        (let ((test (simplify (if.test E) notepad2))
+              (code (simplify (if.then E) notepad2)))
+          (if.test-set! E test)
+          (if.then-set! E code)
+          (if (not (call? test))
+              (finish E fix chr sym other constants)
+              (let ((proc (call.proc test))
+                    (args (call.args test)))
+                (if (not (and (variable? proc)
+                              (let ((name (variable.name proc)))
+                                ; FIXME: See note above.
+                                (or (eq? name name:EQ?)
+                                    (eq? name name:EQV?)
+                                    (eq? name name:MEMQ)
+                                    (eq? name name:MEMV)))
+                              (= (length args) 2)
+                              (variable? (car args))
+                              (eq? (variable.name (car args)) var0)
+                              (constant? (cadr args))))
+                    (finish E fix chr sym other constants)
+                    (let ((pred (variable.name proc))
+                          (datum (constant.value (cadr args))))
+                      ; FIXME
+                      (if (or (and (or (eq? pred name:MEMV)
+                                       (eq? pred name:MEMQ))
+                                   (not (list? datum)))
+                              (and (eq? pred name:EQ?)
+                                   (not (eqv-is-ok? datum)))
+                              (and (eq? pred name:MEMQ)
+                                   (not (every? (lambda (datum)
+                                                  (eqv-is-ok? datum))
+                                                datum))))
+                          (finish E fix chr sym other constants)
+                          (call-with-values
+                           (lambda ()
+                             (remove-duplicates (if (or (eq? pred name:EQV?)
+                                                        (eq? pred name:EQ?))
+                                                    (list datum)
+                                                    datum)
+                                                constants))
+                           (lambda (data constants)
+                             (let ((clause (list data code))
+                                   (E2 (if.else E)))
+                               (cond ((every? smallint? data)
+                                      (collect-clauses E2
+                                                       (cons clause fix)
+                                                       chr
+                                                       sym
+                                                       other
+                                                       constants))
+                                     ((every? char? data)
+                                      (collect-clauses E2
+                                                       fix
+                                                       (cons clause chr)
+                                                       sym
+                                                       other
+                                                       constants))
+                                     ((every? symbol? data)
+                                      (collect-clauses E2
+                                                       fix
+                                                       chr
+                                                       (cons clause sym)
+                                                       other
+                                                       constants))
+                                     (else
+                                      (collect-clauses E2
+                                                       fix
+                                                       chr
+                                                       sym
+                                                       (cons clause other)
+                                                       constants))))))))))))))
+  
+  (define (remove-duplicates data set)
+    (let loop ((originals data)
+               (data '())
+               (set set))
+      (if (null? originals)
+          (values data set)
+          (let ((x (car originals))
+                (originals (cdr originals)))
+            (if (memv x set)
+                (loop originals data set)
+                (loop originals (cons x data) (cons x set)))))))
+  
+  (define (finish E fix chr sym other constants)
+    (if.else-set! E (simplify (if.else E) notepad2))
+    (analyze E fix chr sym other constants))
+  
+  (define (analyze default fix chr sym other constants)
+    (notepad-var-add! notepad2 var0)
+    (for-each (lambda (L)
+                (notepad-lambda-add! notepad L))
+              (notepad.lambdas notepad2))
+    (for-each (lambda (L)
+                (notepad-nonescaping-add! notepad L))
+              (notepad.nonescaping notepad2))
+    (for-each (lambda (var)
+                (notepad-var-add! notepad var))
+              (append (list name:FIXNUM?
+                            name:CHAR?
+                            name:SYMBOL?
+                            name:FX<
+                            name:FX-
+                            name:CHAR->INTEGER
+                            name:VECTOR-REF)
+                      (notepad.vars notepad2)))
+    (analyze-clauses (notepad.vars notepad2)
+                     var0
+                     default
+                     (reverse fix)
+                     (reverse chr)
+                     (reverse sym)
+                     (reverse other)
+                     constants))
+  
+  (collect-clauses E '() '() '() '() '()))
+
+; Returns true if EQ? and EQV? behave the same on x.
+
+(define (eqv-is-ok? x)
+  (or (smallint? x)
+      (char? x)
+      (symbol? x)
+      (boolean? x)))
+
+; Returns true if EQ? and EQV? behave the same on x.
+
+(define (eq-is-ok? x)
+  (eqv-is-ok? x))
+
+; Any case expression that dispatches on a variable var0 and whose
+; constants are disjoint can be compiled as
+;
+; (let ((n (cond ((eq? var0 'K1) ...)   ; miscellaneous constants
+;                ...
+;                ((fixnum? var0)
+;                 <dispatch-on-fixnum>)
+;                ((char? var0)
+;                 <dispatch-on-char>)
+;                ((symbol? var0)
+;                 <dispatch-on-symbols>)
+;                (else 0))))
+;   <dispatch-on-case-number>)
+;
+; where the <dispatch-on-case-number> uses binary search within
+; the interval [0, p+1), where p is the number of non-default cases.
+;
+; On the SPARC, sequential search is faster if there are fewer than
+; 8 constants, and sequential search uses less than half the space
+; if there are fewer than 10 constants.  Most target machines should
+; similar, so I'm hard-wiring this constant.
+; FIXME:  The hardwired constant is annoying.
+
+(define (analyze-clauses F var0 default fix chr sym other constants)
+  (cond ((or (and (null? fix)
+                  (null? chr))
+             (< (length constants) 12))
+         (implement-clauses-by-sequential-search var0
+                                                 default
+                                                 (append fix chr sym other)))
+        (else
+         (implement-clauses F var0 default fix chr sym other constants))))
+
+; Implements the general technique described above.
+
+(define (implement-clauses F var0 default fix chr sym other constants)
+  (let* ((name:n ((make-rename-procedure) 'n))
+         ; Referencing information is destroyed by pass 2.
+         (entry (make-R-entry name:n '() '() '()))
+         (F (union (make-set (list name:n)) F))
+         (L (make-lambda
+             (list name:n)
+             '()
+             '()  ; entry
+             F
+             '()
+             '()
+             #f
+             (implement-case-dispatch
+              name:n
+              (cons default
+                    (map cadr
+                         ; The order here must match the order
+                         ; used by IMPLEMENT-DISPATCH.
+                         (append other fix chr sym)))))))
+    (make-call L
+               (list (implement-dispatch 0
+                                         var0
+                                         (map car other)
+                                         (map car fix)
+                                         (map car chr)
+                                         (map car sym))))))
+
+(define (implement-case-dispatch var0 exprs)
+  (implement-intervals var0
+                       (map (lambda (n code)
+                              (list n (+ n 1) code))
+                            (iota (length exprs))
+                            exprs)))
+
+; Given the number of prior clauses,
+; the variable on which to dispatch,
+; a list of constant lists for mixed or miscellaneous clauses,
+; a list of constant lists for the fixnum clauses,
+; a list of constant lists for the character clauses, and
+; a list of constant lists for the symbol clauses,
+; returns code that computes the index of the selected clause.
+; The mixed/miscellaneous clauses must be tested first because
+; Twobit's SMALLINT? predicate might not be true of all fixnums
+; on the target machine, which means that Twobit might classify
+; some fixnums as miscellaneous.
+
+(define (implement-dispatch prior var0 other fix chr sym)
+  (cond ((not (null? other))
+         (implement-dispatch-other
+          (implement-dispatch (+ prior (length other))
+                              var0 fix chr sym '())
+          prior var other))
+        ((not (null? fix))
+         (make-conditional (make-call (make-variable name:FIXNUM?)
+                                      (list (make-variable var0)))
+                           (implement-dispatch-fixnum prior var0 fix)
+                           (implement-dispatch (+ prior (length fix))
+                                               var0 '() chr sym other)))
+        ((not (null? chr))
+         (make-conditional (make-call (make-variable name:CHAR?)
+                                      (list (make-variable var0)))
+                           (implement-dispatch-char prior var0 chr)
+                           (implement-dispatch (+ prior (length chr))
+                                               var0 fix '() sym other)))
+        ((not (null? sym))
+         (make-conditional (make-call (make-variable name:SYMBOL?)
+                                      (list (make-variable var0)))
+                           (implement-dispatch-symbol prior var0 sym)
+                           (implement-dispatch (+ prior (length sym))
+                                               var0 fix chr '() other)))
+        (else
+         (make-constant 0))))
+
+; The value of var0 will be known to be a fixnum.
+; Can use table lookup, binary search, or sequential search.
+; FIXME: Never uses sequential search, which is best when
+; there are only a few constants, with gaps between them.
+
+(define (implement-dispatch-fixnum prior var0 lists)
+  
+  (define (calculate-intervals n lists)
+    (define (loop n lists intervals)
+      (if (null? lists)
+          (twobit-sort (lambda (interval1 interval2)
+                         (< (car interval1) (car interval2)))
+                       intervals)
+          (let ((constants (twobit-sort < (car lists))))
+            (loop (+ n 1)
+                  (cdr lists)
+                  (append (extract-intervals n constants)
+                          intervals)))))
+    (loop n lists '()))
+  
+  (define (extract-intervals n constants)
+    (if (null? constants)
+        '()
+        (let ((k0 (car constants)))
+          (do ((constants (cdr constants) (cdr constants))
+               (k1 (+ k0 1) (+ k1 1)))
+              ((or (null? constants)
+                   (not (= k1 (car constants))))
+               (cons (list k0 k1 (make-constant n))
+                     (extract-intervals n constants)))))))
+  
+  (define (complete-intervals intervals)
+    (cond ((null? intervals)
+           intervals)
+          ((null? (cdr intervals))
+           intervals)
+          (else
+           (let* ((i1 (car intervals))
+                  (i2 (cadr intervals))
+                  (end1 (cadr i1))
+                  (start2 (car i2))
+                  (intervals (complete-intervals (cdr intervals))))
+             (if (= end1 start2)
+                 (cons i1 intervals)
+                 (cons i1
+                       (cons (list end1 start2 (make-constant 0))
+                             intervals)))))))
+  
+  (let* ((intervals (complete-intervals
+                     (calculate-intervals (+ prior 1) lists)))
+         (lo (car (car intervals)))
+         (hi (car (car (reverse intervals))))
+         (p (length intervals)))
+    (make-conditional
+     (make-call (make-variable name:FX<)
+                (list (make-variable var0)
+                      (make-constant lo)))
+     (make-constant 0)
+     (make-conditional
+      (make-call (make-variable name:FX<)
+                 (list (make-variable var0)
+                       (make-constant (+ hi 1))))
+      ; The static cost of table lookup is about hi - lo words.
+      ; The static cost of binary search is about 5 SPARC instructions
+      ; per interval.
+      (if (< (- hi lo) (* 5 p))
+          (implement-table-lookup var0 (+ prior 1) lists lo hi)
+          (implement-intervals var0 intervals))
+      (make-constant 0)))))
+
+(define (implement-dispatch-char prior var0 lists)
+  (let* ((lists (map (lambda (constants)
+                       (map compat:char->integer constants))
+                     lists))
+         (name:n ((make-rename-procedure) 'n))
+         ; Referencing information is destroyed by pass 2.
+         ;(entry (make-R-entry name:n '() '() '()))
+         (F (list name:n name:EQ? name:FX< name:FX- name:VECTOR-REF))
+         (L (make-lambda
+             (list name:n)
+             '()
+             '()  ; entry
+             F
+             '()
+             '()
+             #f
+             (implement-dispatch-fixnum prior name:n lists))))
+    (make-call L
+               (make-call (make-variable name:CHAR->INTEGER)
+                          (list (make-variable var0))))))
+
+(define (implement-dispatch-symbol prior var0 lists)
+  (implement-dispatch-other (make-constant 0) prior var0 lists))
+
+(define (implement-dispatch-other default prior var0 lists)
+  (if (null? lists)
+      default
+      (let* ((constants (car lists))
+             (lists (cdr lists))
+             (n (+ prior 1)))
+      (make-conditional (make-call-to-memv var0 constants)
+                        (make-constant n)
+                        (implement-dispatch-other default n var0 lists)))))
+
+(define (make-call-to-memv var0 constants)
+  (cond ((null? constants)
+         (make-constant #f))
+        ((null? (cdr constants))
+         (make-call-to-eqv var0 (car constants)))
+        (else
+         (make-conditional (make-call-to-eqv var0 (car constants))
+                           (make-constant #t)
+                           (make-call-to-memv var0 (cdr constants))))))
+
+(define (make-call-to-eqv var0 constant)
+  (make-call (make-variable
+              (if (eq-is-ok? constant)
+                  name:EQ?
+                  name:EQV?))
+             (list (make-variable var0)
+                   (make-constant constant))))
+
+; Given a variable whose value is known to be a fixnum,
+; the clause index for the first fixnum clause,
+; an ordered list of lists of constants for fixnum-only clauses,
+; and the least and greatest constants in those lists,
+; returns code for a table lookup.
+
+(define (implement-table-lookup var0 index lists lo hi)
+  (let ((v (make-vector (+ 1 (- hi lo)) 0)))
+    (do ((index index (+ index 1))
+         (lists lists (cdr lists)))
+        ((null? lists))
+        (for-each (lambda (k)
+                    (vector-set! v (- k lo) index))
+                  (car lists)))
+    (make-call (make-variable name:VECTOR-REF)
+               (list (make-constant v)
+                     (make-call (make-variable name:FX-)
+                                (list (make-variable var0)
+                                      (make-constant lo)))))))
+
+; Given a variable whose value is known to lie within the
+; half-open interval [m0, mk), and an ordered complete
+; list of intervals of the form
+;
+;     ((m0 m1 code0)
+;      (m1 m2 code1)
+;      ...
+;      (m{k-1} mk code{k-1})
+;     )
+;
+; returns an expression that finds the unique i such that
+; var0 lies within [mi, m{i+1}), and then executes code{i}.
+
+(define (implement-intervals var0 intervals)
+  (if (null? (cdr intervals))
+      (caddr (car intervals))
+      (let ((n (quotient (length intervals) 2)))
+        (do ((n n (- n 1))
+             (intervals1 '() (cons (car intervals2) intervals1))
+             (intervals2 intervals (cdr intervals2)))
+            ((zero? n)
+             (let ((intervals1 (reverse intervals1))
+                   (m (car (car intervals2))))
+               (make-conditional (make-call (make-variable name:FX<)
+                                            (list
+                                             (make-variable var0)
+                                             (make-constant m)))
+                                 (implement-intervals var0 intervals1)
+                                 (implement-intervals var0 intervals2))))))))
+
+; The brute force approach.
+; Given the variable on which the dispatch is being performed, and
+; actual (simplified) code for the default clause and
+; for all other clauses,
+; returns code to perform the dispatch by sequential search.
+
+(define *memq-threshold* 20)
+(define *memv-threshold* 4)
+
+(define (implement-clauses-by-sequential-search var0 default clauses)
+  (if (null? clauses)
+      default
+      (let* ((case1 (car clauses))
+             (clauses (cdr clauses))
+             (constants1 (car case1))
+             (code1 (cadr case1)))
+        (make-conditional (make-call-to-memv var0 constants1)
+                          code1
+                          (implement-clauses-by-sequential-search
+                           var0 default clauses)))))
+; Copyright 1999 William D Clinger.
+;
+; Permission to copy this software, in whole or in part, to use this
+; software for any lawful noncommercial purpose, and to redistribute
+; this software is granted subject to the restriction that all copies
+; made of this software must include this copyright notice in full.
+;
+; I also request that you send me a copy of any improvements that you
+; make to this software so that they may be incorporated within it to
+; the benefit of the Scheme community.
+;
+; 13 April 1999.
+;
+; The tail and non-tail call graphs of known and unknown procedures.
+;
+; Given an expression E returned by pass 2 of Twobit,
+; returns a list of the following form:
+;
+; ((#t     L ()     <tailcalls> <nontailcalls> <size> #f)
+;  (<name> L <vars> <tailcalls> <nontailcalls> <size> #f)
+;  ...)
+;
+; where
+;
+; Each L is a lambda expression that occurs within E
+; as either an escaping lambda expression or as a known
+; procedure.  If L is a known procedure, then <name> is
+; its name; otherwise <name> is #f.
+;
+; <vars> is a list of the non-global variables within whose
+; scope L occurs.
+;
+; <tailcalls> is a complete list of names of known local procedures
+; that L calls tail-recursively, disregarding calls from other known
+; procedures or escaping lambda expressions that occur within L.
+;
+; <nontailcalls> is a complete list of names of known local procedures
+; that L calls non-tail-recursively, disregarding calls from other
+; known procedures or escaping lambda expressions that occur within L.
+;
+; <size> is a measure of the size of L, including known procedures
+; and escaping lambda expressions that occur within L.
+
+(define (callgraphnode.name x) (car x))
+(define (callgraphnode.code x) (cadr x))
+(define (callgraphnode.vars x) (caddr x))
+(define (callgraphnode.tailcalls x) (cadddr x))
+(define (callgraphnode.nontailcalls x) (car (cddddr x)))
+(define (callgraphnode.size x) (cadr (cddddr x)))
+(define (callgraphnode.info x) (caddr (cddddr x)))
+
+(define (callgraphnode.size! x v) (set-car! (cdr (cddddr x)) v) #f)
+(define (callgraphnode.info! x v) (set-car! (cddr (cddddr x)) v) #f)
+
+(define (callgraph exp)
+  
+  ; Returns (union (list x) z).
+  
+  (define (adjoin x z)
+    (if (memq x z)
+        z
+        (cons x z)))
+  
+  (let ((result '()))
+    
+    ; Given a <name> as described above, a lambda expression, a list
+    ; of variables that are in scope, and a list of names of known
+    ; local procedure that are in scope, computes an entry for L and
+    ; entries for any nested known procedures or escaping lambda
+    ; expressions, and adds them to the result.
+    
+    (define (add-vertex! name L vars known)
+      
+      (let ((tailcalls '())
+            (nontailcalls '())
+            (size 0))
+        
+        ; Given an expression, a list of variables that are in scope,
+        ; a list of names of known local procedures that are in scope,
+        ; and a boolean indicating whether the expression occurs in a
+        ; tail context, adds any tail or non-tail calls to known
+        ; procedures that occur within the expression to the list
+        ; variables declared above.
+        
+        (define (graph! exp vars known tail?)
+          (set! size (+ size 1))
+          (case (car exp)
+            
+            ((quote)    #f)
+            
+            ((lambda)   (add-vertex! #f exp vars known)
+                        (set! size
+                              (+ size
+                                 (callgraphnode.size (car result)))))
+            
+            ((set!)     (graph! (assignment.rhs exp) vars known #f))
+            
+            ((if)       (graph! (if.test exp) vars known #f)
+                        (graph! (if.then exp) vars known tail?)
+                        (graph! (if.else exp) vars known tail?))
+            
+            ((begin)    (if (not (variable? exp))
+                            (do ((exprs (begin.exprs exp) (cdr exprs)))
+                                ((null? (cdr exprs))
+                                 (graph! (car exprs) vars known tail?))
+                                (graph! (car exprs) vars known #f))))
+            
+            (else       (let ((proc (call.proc exp)))
+                          (cond ((variable? proc)
+                                 (let ((name (variable.name proc)))
+                                   (if (memq name known)
+                                       (if tail?
+                                           (set! tailcalls
+                                                 (adjoin name tailcalls))
+                                           (set! nontailcalls
+                                                 (adjoin name nontailcalls))))))
+                                 ((lambda? proc)
+                                  (graph-lambda! proc vars known tail?))
+                                 (else
+                                  (graph! proc vars known #f)))
+                          (for-each (lambda (exp)
+                                      (graph! exp vars known #f))
+                                    (call.args exp))))))
+        
+        (define (graph-lambda! L vars known tail?)
+          (let* ((defs (lambda.defs L))
+                 (newknown (map def.lhs defs))
+                 (vars (append newknown
+                               (make-null-terminated
+                                (lambda.args L))
+                               vars))
+                 (known (append newknown known)))
+            (for-each (lambda (def)
+                        (add-vertex! (def.lhs def)
+                                     (def.rhs def)
+                                     vars
+                                     known)
+                        (set! size
+                              (+ size
+                                 (callgraphnode.size (car result)))))
+                      defs)
+            (graph! (lambda.body L) vars known tail?)))
+        
+        (graph-lambda! L vars known #t)
+        
+        (set! result
+              (cons (list name L vars tailcalls nontailcalls size #f)
+                    result))))
+    
+    (add-vertex! #t
+                 (make-lambda '() '() '() '() '() '() '() exp)
+                 '()
+                 '())
+    result))
+
+; Displays the callgraph, for debugging.
+
+(define (view-callgraph g)
+  (for-each (lambda (entry)
+              (let ((name (callgraphnode.name entry))
+                    (exp  (callgraphnode.code entry))
+                    (vars (callgraphnode.vars entry))
+                    (tail (callgraphnode.tailcalls entry))
+                    (nt   (callgraphnode.nontailcalls entry))
+                    (size (callgraphnode.size entry)))
+                (cond ((symbol? name)
+                       (write name))
+                      (name
+                       (display "TOP LEVEL EXPRESSION"))
+                      (else
+                       (display "ESCAPING LAMBDA EXPRESSION")))
+                (display ":")
+                (newline)
+                (display "Size: ")
+                (write size)
+                (newline)
+                ;(newline)
+                ;(display "Variables in scope: ")
+                ;(write vars)
+                ;(newline)
+                (display "Tail calls:     ")
+                (write tail)
+                (newline)
+                (display "Non-tail calls: ")
+                (write nt)
+                (newline)
+                ;(newline)
+                ;(pretty-print (make-readable exp))
+                ;(newline)
+                ;(newline)
+                (newline)))
+            g))
+; Copyright 1999 William D Clinger.
+;
+; Permission to copy this software, in whole or in part, to use this
+; software for any lawful noncommercial purpose, and to redistribute
+; this software is granted subject to the restriction that all copies
+; made of this software must include this copyright notice in full.
+;
+; I also request that you send me a copy of any improvements that you
+; make to this software so that they may be incorporated within it to
+; the benefit of the Scheme community.
+;
+; 14 April 1999.
+;
+; Inlining of known local procedures.
+;
+; First find the known and escaping procedures and compute the call graph.
+;
+; If a known local procedure is not called at all, then delete its code.
+;
+; If a known local procedure is called exactly once,
+; then inline its code at the call site and delete the
+; known local procedure.  Change the size of the code
+; at the call site by adding the size of the inlined code.
+;
+; Divide the remaining known and escaping procedures into categories:
+;     1.  makes no calls to known local procedures
+;     2.  known procedures that call known procedures;
+;         within this category, try to sort so that procedures do not
+;         call procedures that come later in the sequence; or sort by
+;         number of calls and/or size
+;     3.  escaping procedures that call known procedures
+;
+; Approve each procedure in category 1 for inlining if its code size
+; is less than some threshold.
+;
+; For each procedure in categories 2 and 3, traverse its code, inlining
+; where it seems like a good idea.  The compiler should be more aggressive
+; about inlining non-tail calls than tail calls because:
+;
+;     Inlining a non-tail call can eliminate a stack frame
+;     or expose the inlined code to loop optimizations.
+;
+;     The main reason for inlining a tail call is to enable
+;     intraprocedural optimizations or to unroll a loop.
+;
+; After inlining has been performed on a known local procedure,
+; then approve it for inlining if its size is less than some threshold.
+;
+; FIXME:
+; This strategy avoids infinite unrolling, but it also avoids finite
+; unrolling of loops.
+
+; Parameters to control inlining.
+; These can be tuned later.
+
+(define *tail-threshold* 10)
+(define *nontail-threshold* 20)
+(define *multiplier* 300)
+
+; Given a callgraph, performs inlining of known local procedures
+; by side effect.  The original expression must then be copied to
+; reinstate Twobit's invariants.
+
+; FIXME:  This code doesn't yet do the right thing with known local
+; procedures that aren't called or are called in exactly one place.
+
+(define (inline-using-callgraph! g)
+  (let ((known (make-hashtable))
+        (category2 '())
+        (category3 '()))
+    (for-each (lambda (node)
+                (let ((name (callgraphnode.name node))
+                      (tcalls (callgraphnode.tailcalls node))
+                      (ncalls (callgraphnode.nontailcalls node)))
+                  (if (symbol? name)
+                      (hashtable-put! known name node))
+                  (if (and (null? tcalls)
+                           (null? ncalls))
+                      (if (< (callgraphnode.size node)
+                             *nontail-threshold*)
+                          (callgraphnode.info! node #t))
+                      (if (symbol? name)
+                          (set! category2 (cons node category2))
+                          (set! category3 (cons node category3))))))
+              g)
+    (set! category2 (twobit-sort (lambda (x y)
+                                   (< (callgraphnode.size x)
+                                      (callgraphnode.size y)))
+                                 category2))
+    (for-each (lambda (node)
+                (inline-node! node known))
+              category2)
+    (for-each (lambda (node)
+                (inline-node! node known))
+              category3)
+    ; FIXME:
+    ; Inlining destroys the callgraph, so maybe this cleanup is useless.
+    (hashtable-for-each (lambda (name node) (callgraphnode.info! node #f))
+                        known)))
+
+; Given a node of the callgraph and a hash table of nodes for
+; known local procedures, performs inlining by side effect.
+
+(define (inline-node! node known)
+  (let* ((debugging? #f)
+         (name (callgraphnode.name node))
+         (exp (callgraphnode.code node))
+         (size0 (callgraphnode.size node))
+         (budget (quotient (* (- *multiplier* 100) size0) 100))
+         (tail-threshold *tail-threshold*)
+         (nontail-threshold *nontail-threshold*))
+    
+    ; Given an expression,
+    ; a boolean indicating whether the expression is in a tail context,
+    ; a list of procedures that should not be inlined,
+    ; and a size budget,
+    ; performs inlining by side effect and returns the unused budget.
+    
+    (define (inline exp tail? budget)
+        (if (positive? budget)
+            
+            (case (car exp)
+              
+              ((quote lambda)
+               budget)
+              
+              ((set!)
+               (inline (assignment.rhs exp) #f budget))
+              
+              ((if)
+               (let* ((budget (inline (if.test exp) #f budget))
+                      (budget (inline (if.then exp) tail? budget))
+                      (budget (inline (if.else exp) tail? budget)))
+                 budget))
+              
+              ((begin)
+               (if (variable? exp)
+                   budget
+                   (do ((exprs (begin.exprs exp) (cdr exprs))
+                        (budget budget
+                                (inline (car exprs) #f budget)))
+                       ((null? (cdr exprs))
+                        (inline (car exprs) tail? budget)))))
+              
+              (else
+               (let ((budget (do ((exprs (call.args exp) (cdr exprs))
+                                  (budget budget
+                                          (inline (car exprs) #f budget)))
+                                 ((null? exprs)
+                                  budget))))
+                 (let ((proc (call.proc exp)))
+                   (cond ((variable? proc)
+                          (let* ((procname (variable.name proc))
+                                 (procnode (hashtable-get known procname)))
+                            (if procnode
+                                (let ((size (callgraphnode.size procnode))
+                                      (info (callgraphnode.info procnode)))
+                                  (if (and info
+                                           (<= size budget)
+                                           (<= size
+                                               (if tail?
+                                                   tail-threshold
+                                                   nontail-threshold)))
+                                      (begin
+                                       (if debugging?
+                                           (begin
+                                            (display "    Inlining ")
+                                            (write (variable.name proc))
+                                            (newline)))
+                                       (call.proc-set!
+                                        exp
+                                        (copy-exp
+                                         (callgraphnode.code procnode)))
+                                       (callgraphnode.size!
+                                        node
+                                        (+ (callgraphnode.size node) size))
+                                       (- budget size))
+                                      (begin
+                                       (if (and #f debugging?)
+                                           (begin
+                                            (display "    Declining to inline ")
+                                            (write (variable.name proc))
+                                            (newline)))
+                                       budget)))
+                                budget)))
+                         ((lambda? proc)
+                          (inline (lambda.body proc) tail? budget))
+                         (else
+                          (inline proc #f budget)))))))
+            -1))
+    
+    (if (and #f debugging?)
+        (begin
+         (display "Processing ")
+         (write name)
+         (newline)))
+    
+    (let ((budget (inline (if (lambda? exp)
+                              (lambda.body exp)
+                              exp)
+                          #t
+                          budget)))
+      (if (and (negative? budget)
+               debugging?)
+          ; This shouldn't happen very often.
+          (begin (display "Ran out of inlining budget for ")
+                 (write (callgraphnode.name node))
+                 (newline)))
+      (if (<= (callgraphnode.size node) nontail-threshold)
+          (callgraphnode.info! node #t))
+      #f)))
+
+; For testing.
+
+(define (test-inlining test0)
+  (begin (define exp0 (begin (display "Compiling...")
+                             (newline)
+                             (pass2 (pass1 test0))))
+         (define g0 (begin (display "Computing call graph...")
+                           (newline)
+                           (callgraph exp0))))
+  (display "Inlining...")
+  (newline)
+  (inline-using-callgraph! g0)
+  (pretty-print (make-readable (copy-exp exp0))))
+; Copyright 1999 William D Clinger.
+;
+; Permission to copy this software, in whole or in part, to use this
+; software for any lawful noncommercial purpose, and to redistribute
+; this software is granted subject to the restriction that all copies
+; made of this software must include this copyright notice in full.
+;
+; I also request that you send me a copy of any improvements that you
+; make to this software so that they may be incorporated within it to
+; the benefit of the Scheme community.
+;
+; 14 April 1999.
+;
+; Interprocedural constant propagation and folding.
+;
+; Constant propagation must converge before constant folding can be
+; performed.  Constant folding creates more constants that can be
+; propagated, so these two optimizations must be iterated, but it
+; is safe to stop at any time.
+;
+; Abstract interpretation for constant folding.
+;
+; The abstract values are
+;     bottom    (represented here by #f)
+;     constants (represented by quoted literals)
+;     top       (represented here by #t)
+;
+; Let [[ E ]] be the abstract interpretation of E over that domain
+; of abstract values, with respect to some arbitrary set of abstract
+; values for local variables.
+;
+; If a is a global variable or a formal parameter of an escaping
+; lambda expression, then [[ a ]] = #t.
+;
+; If x is the ith formal parameter of a known local procedure f,
+; then [[ x ]] = \join_{(f E1 ... En)} [[ Ei ]].
+;
+; [[ K ]] = K
+; [[ L ]] = #t
+; [[ (begin E1 ... En) ]] = [[ En ]]
+; [[ (set! I E) ]] = #f
+;
+; If [[ E0 ]] = #t, then [[ (if E0 E1 E2) ]] = [[ E1 ]] \join [[ E2 ]]
+; else if [[ E0 ]] = K, then [[ (if E0 E1 E2) ]] = [[ E1 ]]
+;                         or [[ (if E0 E1 E2) ]] = [[ E2 ]]
+;                       depending upon K
+; else [[ (if E0 E1 E2) ]] = #f
+;
+; If f is a known local procedure with body E,
+;     then [[ (f E1 ... En) ]] = [[ E ]]
+;
+; If g is a foldable integrable procedure, then:
+; if there is some i for which [[ Ei ]] = #t,
+;     then [[ (g E1 ... En) ]] = #t
+; else if [[ E1 ]] = K1, ..., [[ En ]] = Kn,
+;     then [[ (g E1 ... En) ]] = (g K1 ... Kn)
+; else [[ (g E1 ... En) ]] = #f
+;
+; Symbolic representations of abstract values.
+; (Can be thought of as mappings from abstract environments to
+; abstract values.)
+;
+; <symbolic>     ::=  #t  |  ( <expressions> )
+; <expressions>  ::=  <empty>  |  <expression> <expressions>
+
+; Parameter to limit constant propagation and folding.
+; This parameter can be tuned later.
+
+(define *constant-propagation-limit* 5)
+
+; Given an expression as output by pass 2, performs constant
+; propagation and folding.
+
+(define (constant-propagation exp)
+  (define (constant-propagation exp i)
+    (if (< i *constant-propagation-limit*)
+        (begin
+         ;(display "Performing constant propagation and folding...")
+         ;(newline)
+         (let* ((g (callgraph exp))
+                (L (callgraphnode.code (car g)))
+                (variables (constant-propagation-using-callgraph g))
+                (changed? (constant-folding! L variables)))
+           (if changed?
+               (constant-propagation (lambda.body L) (+ i 1))
+               (lambda.body L))))))
+  (constant-propagation exp 0))
+
+; Given a callgraph, returns a hashtable of abstract values for
+; all local variables.
+
+(define (constant-propagation-using-callgraph g)
+  (let ((debugging? #f)
+        (folding? (integrate-usual-procedures))
+        (known (make-hashtable))
+        (variables (make-hashtable))
+        (counter 0))
+    
+    ; Computes joins of abstract values.
+    
+    (define (join x y)
+      (cond ((boolean? x)
+             (if x #t y))
+            ((boolean? y)
+             (join y x))
+            ((equal? x y)
+             x)
+            (else #t)))
+    
+    ; Given a <symbolic> and a vector of abstract values,
+    ; evaluates the <symbolic> and returns its abstract value.
+    
+    (define (aeval rep env)
+      (cond ((eq? rep #t)
+             #t)
+            ((null? rep)
+             #f)
+            ((null? (cdr rep))
+             (aeval1 (car rep) env))
+            (else
+             (join (aeval1 (car rep) env)
+                   (aeval (cdr rep) env)))))
+    
+    (define (aeval1 exp env)
+      
+      (case (car exp)
+        
+        ((quote)
+         exp)
+        
+        ((lambda)
+         #t)
+        
+        ((set!)
+         #f)
+        
+        ((begin)
+         (if (variable? exp)
+             (let* ((name (variable.name exp))
+                    (i (hashtable-get variables name)))
+               (if i
+                   (vector-ref env i)
+                   #t))
+             (aeval1-error)))
+        
+        ((if)
+         (let* ((val0 (aeval1 (if.test exp) env))
+                (val1 (aeval1 (if.then exp) env))
+                (val2 (aeval1 (if.else exp) env)))
+           (cond ((eq? val0 #t)
+                  (join val1 val2))
+                 ((pair? val0)
+                  (if (constant.value val0)
+                      val1
+                      val2))
+                 (else
+                  #f))))
+        
+        (else
+         (do ((exprs (reverse (call.args exp)) (cdr exprs))
+              (vals '() (cons (aeval1 (car exprs) env) vals)))
+             ((null? exprs)
+              (let ((proc (call.proc exp)))
+                (cond ((variable? proc)
+                       (let* ((procname (variable.name proc))
+                              (procnode (hashtable-get known procname))
+                              (entry (if folding?
+                                         (constant-folding-entry procname)
+                                         #f)))
+                         (cond (procnode
+                                (vector-ref env
+                                            (hashtable-get variables
+                                                           procname)))
+                               (entry
+                                ; FIXME: No constant folding
+                                #t)
+                               (else (aeval1-error)))))
+                      (else
+                       (aeval1-error)))))))))
+    
+    (define (aeval1-error)
+      (error "Compiler bug: constant propagation (aeval1)"))
+    
+    ; Combines two <symbolic>s.
+    
+    (define (combine-symbolic rep1 rep2)
+      (cond ((eq? rep1 #t) #t)
+            ((eq? rep2 #t) #t)
+            (else
+             (append rep1 rep2))))
+    
+    ; Given an expression, returns a <symbolic> that represents
+    ; a list of expressions whose abstract values can be joined
+    ; to obtain the abstract value of the given expression.
+    ; As a side effect, enters local variables into variables.
+    
+    (define (collect! exp)
+      
+      (case (car exp)
+        
+        ((quote)
+         (list exp))
+        
+        ((lambda)
+         #t)
+        
+        ((set!)
+         (collect! (assignment.rhs exp))
+         '())
+        
+        ((begin)
+         (if (variable? exp)
+             (list exp)
+             (do ((exprs (begin.exprs exp) (cdr exprs)))
+                 ((null? (cdr exprs))
+                  (collect! (car exprs)))
+                 (collect! (car exprs)))))
+        
+        ((if)
+         (collect! (if.test exp))
+         (collect! (if.then exp))
+         (collect! (if.else exp))
+         #t)
+        
+        (else
+         (do ((exprs (reverse (call.args exp)) (cdr exprs))
+              (reps '() (cons (collect! (car exprs)) reps)))
+             ((null? exprs)
+              (let ((proc (call.proc exp)))
+                (define (put-args! args reps)
+                  (cond ((pair? args)
+                         (let ((v (car args))
+                               (rep (car reps)))
+                           (hashtable-put! variables v rep)
+                           (put-args! (cdr args) (cdr reps))))
+                        ((symbol? args)
+                         (hashtable-put! variables args #t))
+                        (else #f)))
+                (cond ((variable? proc)
+                       (let* ((procname (variable.name proc))
+                              (procnode (hashtable-get known procname))
+                              (entry (if folding?
+                                         (constant-folding-entry procname)
+                                         #f)))
+                         (cond (procnode
+                                (for-each (lambda (v rep)
+                                            (hashtable-put!
+                                             variables
+                                             v
+                                             (combine-symbolic
+                                              rep (hashtable-get variables v))))
+                                          (lambda.args
+                                            (callgraphnode.code procnode))
+                                          reps)
+                                (list (make-variable procname)))
+                               (entry
+                                ; FIXME: No constant folding
+                                #t)
+                               (else #t))))
+                      ((lambda? proc)
+                       (put-args! (lambda.args proc) reps)
+                       (collect! (lambda.body proc)))
+                      (else
+                       (collect! proc)
+                       #t))))))))
+    
+    (for-each (lambda (node)
+                (let* ((name (callgraphnode.name node))
+                       (code (callgraphnode.code node))
+                       (known? (symbol? name))
+                       (rep (if known? '() #t)))
+                  (if known?
+                      (hashtable-put! known name node))
+                  (if (lambda? code)
+                      (for-each (lambda (var)
+                                  (hashtable-put! variables var rep))
+                                (make-null-terminated (lambda.args code))))))
+              g)
+    
+    (for-each (lambda (node)
+                (let ((name (callgraphnode.name node))
+                      (code (callgraphnode.code node)))
+                  (cond ((symbol? name)
+                         (hashtable-put! variables
+                                         name
+                                         (collect! (lambda.body code))))
+                        (else
+                         (collect! (lambda.body code))))))
+              g)
+    
+    (if (and #f debugging?)
+        (begin
+         (hashtable-for-each (lambda (v rep)
+                               (write v)
+                               (display ": ")
+                               (write rep)
+                               (newline))
+                             variables)
+         
+         (display "----------------------------------------")
+         (newline)))
+    
+    ;(trace aeval aeval1)
+    
+    (let* ((n (hashtable-size variables))
+           (vars (hashtable-map (lambda (v rep) v) variables))
+           (reps (map (lambda (v) (hashtable-get variables v)) vars))
+           (init (make-vector n #f))
+           (next (make-vector n)))
+      (do ((i 0 (+ i 1))
+           (vars vars (cdr vars))
+           (reps reps (cdr reps)))
+          ((= i n))
+          (hashtable-put! variables (car vars) i)
+          (vector-set! next
+                       i
+                       (let ((rep (car reps)))
+                         (lambda (env)
+                           (aeval rep env)))))
+      (compute-fixedpoint init next equal?)
+      (for-each (lambda (v)
+                  (let* ((i (hashtable-get variables v))
+                         (aval (vector-ref init i)))
+                    (hashtable-put! variables v aval)
+                    (if (and debugging?
+                             (not (eq? aval #t)))
+                        (begin (write v)
+                               (display ": ")
+                               (write aval)
+                               (newline)))))
+                vars)
+      variables)))
+
+; Given a lambda expression, performs constant propagation, folding,
+; and simplifications by side effect, using the abstract values in the
+; hash table of variables.
+; Returns #t if any new constants were created by constant folding,
+; otherwise returns #f.
+
+(define (constant-folding! L variables)
+  (let ((debugging? #f)
+        (msg1 "    Propagating constant value for ")
+        (msg2 "    Folding: ")
+        (msg3 " ==> ")
+        (folding? (integrate-usual-procedures))
+        (changed? #f))
+    
+    ; Given a known lambda expression L, its original formal parameters,
+    ; and a list of all calls to L, deletes arguments that are now
+    ; ignored because of constant propagation.
+    
+    (define (delete-ignored-args! L formals0 calls)
+      (let ((formals1 (lambda.args L)))
+        (for-each (lambda (call)
+                    (do ((formals0 formals0 (cdr formals0))
+                         (formals1 formals1 (cdr formals1))
+                         (args (call.args call)
+                               (cdr args))
+                         (newargs '()
+                                  (if (and (eq? (car formals1) name:IGNORED)
+                                           (pair?
+                                            (hashtable-get variables
+                                                           (car formals0))))
+                                      newargs
+                                      (cons (car args) newargs))))
+                        ((null? formals0)
+                         (call.args-set! call (reverse newargs)))))
+                  calls)
+        (do ((formals0 formals0 (cdr formals0))
+             (formals1 formals1 (cdr formals1))
+             (formals2 '()
+                       (if (and (not (eq? (car formals0)
+                                          (car formals1)))
+                                (eq? (car formals1) name:IGNORED)
+                                (pair?
+                                 (hashtable-get variables
+                                                (car formals0))))
+                           formals2
+                           (cons (car formals1) formals2))))
+            ((null? formals0)
+             (lambda.args-set! L (reverse formals2))))))
+    
+    (define (fold! exp)
+      
+      (case (car exp)
+        
+        ((quote) exp)
+        
+        ((lambda)
+         (let ((Rinfo (lambda.R exp))
+               (known (map def.lhs (lambda.defs exp))))
+           (for-each (lambda (entry)
+                       (let* ((v (R-entry.name entry))
+                              (aval (hashtable-fetch variables v #t)))
+                         (if (and (pair? aval)
+                                  (not (memq v known)))
+                             (let ((x (constant.value aval)))
+                               (if (or (boolean? x)
+                                       (null? x)
+                                       (symbol? x)
+                                       (number? x)
+                                       (char? x)
+                                       (and (vector? x)
+                                            (zero? (vector-length x))))
+                                   (let ((refs (R-entry.references entry)))
+                                     (for-each (lambda (ref)
+                                                 (variable-set! ref aval))
+                                               refs)
+                                     ; Do not try to use Rinfo in place of
+                                     ; (lambda.R exp) below!
+                                     (lambda.R-set!
+                                       exp
+                                       (remq entry (lambda.R exp)))
+                                     (flag-as-ignored v exp)
+                                     (if debugging?
+                                         (begin (display msg1)
+                                                (write v)
+                                                (display ": ")
+                                                (write aval)
+                                                (newline)))))))))
+                     Rinfo)
+           (for-each (lambda (def)
+                       (let* ((name (def.lhs def))
+                              (rhs (def.rhs def))
+                              (entry (R-lookup Rinfo name))
+                              (calls (R-entry.calls entry)))
+                         (if (null? calls)
+                             (begin (lambda.defs-set!
+                                      exp
+                                      (remq def (lambda.defs exp)))
+                                    ; Do not try to use Rinfo in place of
+                                    ; (lambda.R exp) below!
+                                    (lambda.R-set!
+                                      exp
+                                      (remq entry (lambda.R exp))))
+                             (let* ((formals0 (append (lambda.args rhs) '()))
+                                    (L (fold! rhs))
+                                    (formals1 (lambda.args L)))
+                               (if (not (equal? formals0 formals1))
+                                   (delete-ignored-args! L formals0 calls))))))
+                     (lambda.defs exp))
+           (lambda.body-set!
+             exp
+             (fold! (lambda.body exp)))
+           exp))
+        
+        ((set!)
+         (assignment.rhs-set! exp (fold! (assignment.rhs exp)))
+         exp)
+        
+        ((begin)
+         (if (variable? exp)
+             exp
+             (post-simplify-begin (make-begin (map fold! (begin.exprs exp)))
+                                  (make-notepad #f))))
+        
+        ((if)
+         (let ((exp0 (fold! (if.test exp)))
+               (exp1 (fold! (if.then exp)))
+               (exp2 (fold! (if.else exp))))
+           (if (constant? exp0)
+               (let ((newexp (if (constant.value exp0)
+                                 exp1
+                                 exp2)))
+                 (if debugging?
+                     (begin (display msg2)
+                            (write (make-readable exp))
+                            (display msg3)
+                            (write (make-readable newexp))
+                            (newline)))
+                 (set! changed? #t)
+                 newexp)
+               (make-conditional exp0 exp1 exp2))))
+        
+        (else
+         (let ((args (map fold! (call.args exp)))
+               (proc (fold! (call.proc exp))))
+           (cond ((and folding?
+                       (variable? proc)
+                       (every? constant? args)
+                       (let ((entry
+                              (constant-folding-entry (variable.name proc))))
+                         (and entry
+                              (let ((preds
+                                     (constant-folding-predicates entry)))
+                                (and (= (length args) (length preds))
+                                     (every?
+                                      (lambda (x) x)
+                                      (map (lambda (f v) (f v))
+                                           (constant-folding-predicates entry)
+                                           (map constant.value args))))))))
+                  (set! changed? #t)
+                  (let ((result
+                         (make-constant
+                          (apply (constant-folding-folder
+                                  (constant-folding-entry
+                                   (variable.name proc)))
+                                 (map constant.value args)))))
+                    (if debugging?
+                        (begin (display msg2)
+                               (write (make-readable (make-call proc args)))
+                               (display msg3)
+                               (write result)
+                               (newline)))
+                    result))
+                 ((and (lambda? proc)
+                       (list? (lambda.args proc)))
+                  ; FIXME: Folding should be done even if there is
+                  ; a rest argument.
+                  (let loop ((formals (reverse (lambda.args proc)))
+                             (actuals (reverse args))
+                             (processed-formals '())
+                             (processed-actuals '())
+                             (for-effect '()))
+                    (cond ((null? formals)
+                           (lambda.args-set! proc processed-formals)
+                           (call.args-set! exp processed-actuals)
+                           (let ((call (if (and (null? processed-formals)
+                                                (null? (lambda.defs proc)))
+                                           (lambda.body proc)
+                                           exp)))
+                             (if (null? for-effect)
+                                 call
+                                 (post-simplify-begin
+                                  (make-begin
+                                   (reverse (cons call for-effect)))
+                                  (make-notepad #f)))))
+                          ((ignored? (car formals))
+                           (loop (cdr formals)
+                                 (cdr actuals)
+                                 processed-formals
+                                 processed-actuals
+                                 (cons (car actuals) for-effect)))
+                          (else
+                           (loop (cdr formals)
+                                 (cdr actuals)
+                                 (cons (car formals) processed-formals)
+                                 (cons (car actuals) processed-actuals)
+                                 for-effect)))))
+                 (else
+                  (call.proc-set! exp proc)
+                  (call.args-set! exp args)
+                  exp))))))
+    
+    (fold! L)
+    changed?))
+; Copyright 1998 William D Clinger.
+;
+; Permission to copy this software, in whole or in part, to use this
+; software for any lawful noncommercial purpose, and to redistribute
+; this software is granted subject to the restriction that all copies
+; made of this software must include this copyright notice in full.
+; 
+; I also request that you send me a copy of any improvements that you
+; make to this software so that they may be incorporated within it to
+; the benefit of the Scheme community.
+;
+; 7 June 1999.
+;
+; Conversion to A-normal form, with heuristics for
+; choosing a good order of evaluation.
+;
+; This pass operates as a source-to-source transformation on
+; expressions written in the subset of Scheme described by the
+; following grammar, where the input and output expressions
+; satisfy certain additional invariants described below.
+;
+; "X ..." means zero or more occurrences of X.
+;
+; L  -->  (lambda (I_1 ...)
+;           (begin D ...)
+;           (quote (R F G <decls> <doc>)
+;           E)
+;      |  (lambda (I_1 ... . I_rest)
+;           (begin D ...)
+;           (quote (R F G <decls> <doc>))
+;           E)
+; D  -->  (define I L)
+; E  -->  (quote K)                        ; constants
+;      |  (begin I)                        ; variable references
+;      |  L                                ; lambda expressions
+;      |  (E0 E1 ...)                      ; calls
+;      |  (set! I E)                       ; assignments
+;      |  (if E0 E1 E2)                    ; conditionals
+;      |  (begin E0 E1 E2 ...)             ; sequential expressions
+; I  -->  <identifier>
+;
+; R  -->  ((I <references> <assignments> <calls>) ...)
+; F  -->  (I ...)
+; G  -->  (I ...)
+;
+; Invariants that hold for the input only:
+;   *  There are no assignments except to global variables.
+;   *  If I is declared by an internal definition, then the right hand
+;      side of the internal definition is a lambda expression and I
+;      is referenced only in the procedure position of a call.
+;   *  For each lambda expression, the associated F is a list of all
+;      the identifiers that occur free in the body of that lambda
+;      expression, and possibly a few extra identifiers that were
+;      once free but have been removed by optimization.
+;   *  For each lambda expression, the associated G is a subset of F
+;      that contains every identifier that occurs free within some
+;      inner lambda expression that escapes, and possibly a few that
+;      don't.  (Assignment-elimination does not calculate G exactly.)
+;   *  Variables named IGNORED are neither referenced nor assigned.
+;
+; Invariants that hold for the output only:
+;   *  There are no assignments except to global variables.
+;   *  If I is declared by an internal definition, then the right hand
+;      side of the internal definition is a lambda expression and I
+;      is referenced only in the procedure position of a call.
+;   *  R, F, and G are garbage.
+;   *  There are no sequential expressions.
+;   *  The output is an expression E with syntax
+;
+; E  -->  A
+;      |  (L)
+;      |  (L A)
+;
+; A  -->  W
+;      |  L
+;      |  (W_0 W_1 ...)
+;      |  (set! I W)
+;      |  (if W E1 E2)
+;
+; W  -->  (quote K)
+;      |  (begin I)
+;
+; In other words:
+; An expression is a LET* such that the rhs of every binding is
+;     a conditional with the test already evaluated, or
+;     an expression that can be evaluated in one step
+;         (treating function calls as a single step)
+;
+; A-normal form corresponds to the control flow graph for a lambda
+; expression.
+
+; Algorithm: repeated use of these rules:
+;
+; (E0 E1 ...)                              ((lambda (T0 T1 ...) (T0 T1 ...))
+;                                           E0 E1 ...)
+; (set! I E)                               ((lambda (T) (set! I T)) E)
+; (if E0 E1 E2)                            ((lambda (T) (if T E1 E2)) E0)
+; (begin E0 E1 E2 ...)                     ((lambda (T) (begin E1 E2 ...)) E0)
+;
+; ((lambda (I1 I2 I3 ...) E)               ((lambda (I1)
+;  E1 E2 E3)                                  ((lambda (I2 I3 ...) E)
+;                                              E2 E3))
+;                                           E1)
+;
+; ((lambda (I2) E)                         ((lambda (I1)
+;  ((lambda (I1) E2)                          ((lambda (I2) E)
+;   E1))                                       E2)
+;                                           E1)
+;
+; In other words:
+; Introduce a temporary name for every expression except:
+;     tail expressions
+;     the alternatives of a non-tail conditional
+; Convert every LET into a LET*.
+; Get rid of LET* on the right hand side of a binding.
+
+; Given an expression E in the representation output by pass 2,
+; returns an A-normal form for E in that representation.
+; Except for quoted values, the A-normal form does not share
+; mutable structure with the original expression E.
+;
+; KNOWN BUG:
+;
+; If you call A-normal on a form that has already been converted
+; to A-normal form, then the same temporaries will be generated
+; twice.  An optional argument lets you specify a different prefix
+; for temporaries the second time around.  Example:
+;
+; (A-normal-form (A-normal-form E ".T")
+;                ".U")
+
+; This is the declaration that is used to indicate A-normal form.
+
+(define A-normal-form-declaration (list 'anf))
+
+(define (A-normal-form E . rest)
+  
+  (define (A-normal-form E)
+    (anf-make-let* (anf E '() '())))
+  
+  ; New temporaries.
+  
+  (define temp-counter 0)
+  
+  (define temp-prefix
+    (if (or (null? rest)
+            (not (string? (car rest))))
+        (string-append renaming-prefix "T")
+        (car rest)))
+  
+  (define (newtemp)
+    (set! temp-counter (+ temp-counter 1))
+    (string->symbol
+     (string-append temp-prefix
+                    (number->string temp-counter))))
+  
+  ; Given an expression E as output by pass 2,
+  ; a list of surrounding LET* bindings,
+  ; and an ordered list of likely register variables,
+  ; return a non-empty list of LET* bindings
+  ; whose first binding associates a dummy variable
+  ; with an A-expression giving the value for E.
+  
+  (define (anf E bindings regvars)
+    (case (car E)
+      ((quote)    (anf-bind-dummy E bindings))
+      ((begin)    (if (variable? E)
+                      (anf-bind-dummy E bindings)
+                      (anf-sequential E bindings regvars)))
+      ((lambda)   (anf-lambda E bindings regvars))
+      ((set!)     (anf-assignment E bindings regvars))
+      ((if)       (anf-conditional E bindings regvars))
+      (else       (anf-call E bindings regvars))))
+  
+  (define anf:dummy (string->symbol "RESULT"))
+  
+  (define (anf-bind-dummy E bindings)
+    (cons (list anf:dummy E)
+          bindings))
+  
+  ; Unlike anf-bind-dummy, anf-bind-name and anf-bind convert
+  ; their expression argument to A-normal form.
+  ; Don't change anf-bind to call anf-bind-name, because that
+  ; would name the temporaries in an aesthetically bad order.
+  
+  (define (anf-bind-name name E bindings regvars)
+    (let ((bindings (anf E bindings regvars)))
+      (cons (list name (cadr (car bindings)))
+            (cdr bindings))))
+  
+  (define (anf-bind E bindings regvars)
+    (let ((bindings (anf E bindings regvars)))
+      (cons (list (newtemp) (cadr (car bindings)))
+            (cdr bindings))))
+  
+  (define (anf-result bindings)
+    (make-variable (car (car bindings))))
+  
+  (define (anf-make-let* bindings)
+    (define (loop bindings body)
+      (if (null? bindings)
+          body
+          (let ((T1 (car (car bindings)))
+                (E1 (cadr (car bindings))))
+            (loop (cdr bindings)
+                  (make-call (make-lambda (list T1)
+                                          '()
+                                          '()
+                                          '()
+                                          '()
+                                          (list A-normal-form-declaration)
+                                          '()
+                                          body)
+                             (list E1))))))
+    (loop (cdr bindings)
+          (cadr (car bindings))))                                  
+  
+  (define (anf-sequential E bindings regvars)
+    (do ((bindings bindings
+                   (anf-bind (car exprs) bindings regvars))
+         (exprs (begin.exprs E)
+                (cdr exprs)))
+        ((null? (cdr exprs))
+         (anf (car exprs) bindings regvars))))
+  
+  ; Heuristic: the formal parameters of an escaping lambda or
+  ; known local procedure are kept in REG1, REG2, et cetera.
+  
+  (define (anf-lambda L bindings regvars)
+    (anf-bind-dummy
+     (make-lambda (lambda.args L)
+                  (map (lambda (def)
+                         (make-definition
+                          (def.lhs def)
+                          (A-normal-form (def.rhs def))))
+                       (lambda.defs L))
+                  '()
+                  '()
+                  '()
+                  (cons A-normal-form-declaration
+                        (lambda.decls L))
+                  (lambda.doc L)
+                  (anf-make-let*
+                   (anf (lambda.body L)
+                        '()
+                        (make-null-terminated (lambda.args L)))))
+     bindings))
+  
+  (define (anf-assignment E bindings regvars)
+    (let ((I (assignment.lhs E))
+          (E1 (assignment.rhs E)))
+      (if (variable? E1)
+          (anf-bind-dummy E bindings)
+          (let* ((bindings (anf-bind E1 bindings regvars))
+                 (T1 (anf-result bindings)))
+            (anf-bind-dummy (make-assignment I T1) bindings)))))
+  
+  (define (anf-conditional E bindings regvars)
+    (let ((E0 (if.test E))
+          (E1 (if.then E))
+          (E2 (if.else E)))
+      (if (variable? E0)
+          (let ((E1 (anf-make-let* (anf E1 '() regvars)))
+                (E2 (anf-make-let* (anf E2 '() regvars))))
+            (anf-bind-dummy
+             (make-conditional E0 E1 E2)
+             bindings))
+          (let* ((bindings (anf-bind E0 bindings regvars))
+                 (E1 (anf-make-let* (anf E1 '() regvars)))
+                 (E2 (anf-make-let* (anf E2 '() regvars))))
+            (anf-bind-dummy
+             (make-conditional (anf-result bindings) E1 E2)
+             bindings)))))
+  
+  (define (anf-call E bindings regvars)
+    (let* ((proc (call.proc E))
+           (args (call.args E)))
+      
+      ; Evaluates the exprs and returns both a list of bindings and
+      ; a list of the temporaries that name the results of the exprs.
+      ; If rename-always? is true, then temporaries are generated even
+      ; for constants and temporaries.
+      
+      (define (loop exprs bindings names rename-always?)
+        (if (null? exprs)
+            (values bindings (reverse names))
+            (let ((E (car exprs)))
+              (if (or rename-always?
+                      (not (or (constant? E)
+                               (variable? E))))
+                  (let* ((bindings
+                          (anf-bind (car exprs) bindings regvars)))
+                    (loop (cdr exprs)
+                          bindings
+                          (cons (anf-result bindings) names)
+                          rename-always?))
+                  (loop (cdr exprs)
+                        bindings
+                        (cons E names)
+                        rename-always?)))))
+      
+      ; Evaluates the exprs, binding them to the vars, and returns
+      ; a list of bindings.
+      ;
+      ; Although LET variables are likely to be kept in registers,
+      ; trying to guess which register will be allocated is likely
+      ; to do more harm than good.
+      
+      (define (let-loop exprs bindings regvars vars)
+        (if (null? exprs)
+            (if (null? (lambda.defs proc))
+                (anf (lambda.body proc)
+                     bindings
+                     regvars)
+                (let ((bindings
+                       (anf-bind
+                        (make-lambda '()
+                                     (lambda.defs proc)
+                                     '()
+                                     '()
+                                     '()
+                                     (cons A-normal-form-declaration
+                                           (lambda.decls proc))
+                                     (lambda.doc proc)
+                                     (lambda.body proc))
+                        bindings
+                        '())))
+                  (anf-bind-dummy
+                   (make-call (anf-result bindings) '())
+                   bindings)))
+            (let-loop (cdr exprs)
+              (anf-bind-name (car vars)
+                             (car exprs)
+                             bindings
+                             regvars)
+              regvars
+              (cdr vars))))
+      
+      (cond ((lambda? proc)
+             (let ((formals (lambda.args proc)))
+               (if (list? formals)
+                   (let* ((pi (anf-order-of-evaluation args regvars #f))
+                          (exprs (permute args pi))
+                          (names (permute (lambda.args proc) pi)))
+                     (let-loop (reverse exprs) bindings regvars (reverse names)))
+                   (anf-call (normalize-let E) bindings regvars))))
+            
+            ((not (variable? proc))
+             (let ((pi (anf-order-of-evaluation args regvars #f)))
+               (call-with-values
+                (lambda () (loop (permute args pi) bindings '() #t))
+                (lambda (bindings names)
+                  (let ((bindings (anf-bind proc bindings regvars)))
+                    (anf-bind-dummy
+                     (make-call (anf-result bindings)
+                                (unpermute names pi))
+                     bindings))))))
+            
+            ((and (integrate-usual-procedures)
+                  (prim-entry (variable.name proc)))
+             (let ((pi (anf-order-of-evaluation args regvars #t)))
+               (call-with-values
+                (lambda () (loop (permute args pi) bindings '() #t))
+                (lambda (bindings names)
+                  (anf-bind-dummy
+                   (make-call proc (unpermute names pi))
+                   bindings)))))
+            
+            ((memq (variable.name proc) regvars)
+             (let* ((exprs (cons proc args))
+                    (pi (anf-order-of-evaluation
+                         exprs
+                         (cons name:IGNORED regvars)
+                         #f)))
+               (call-with-values
+                (lambda () (loop (permute exprs pi) bindings '() #t))
+                (lambda (bindings names)
+                  (let ((names (unpermute names pi)))
+                    (anf-bind-dummy
+                     (make-call (car names) (cdr names))
+                     bindings))))))
+            
+            (else
+             (let ((pi (anf-order-of-evaluation args regvars #f)))
+               (call-with-values
+                (lambda () (loop (permute args pi) bindings '() #t))
+                (lambda (bindings names)
+                  (anf-bind-dummy
+                   (make-call proc (unpermute names pi))
+                   bindings))))))))
+  
+  ; Given a list of expressions, a list of likely register contents,
+  ; and a switch telling whether these are arguments for a primop
+  ; or something else (such as the arguments for a real call),
+  ; try to choose a good order in which to evaluate the expressions.
+  ;
+  ; Heuristic:  If none of the expressions is a call to a non-primop,
+  ; then parallel assignment optimization gives a good order if the
+  ; regvars are right, and should do no worse than a random order if
+  ; the regvars are wrong.
+  ;
+  ; Heuristic:  If the expressions are arguments to a primop, and
+  ; none are a call to a non-primop, then the register contents
+  ; are irrelevant, and the first argument should be evaluated last.
+  ;
+  ; Heuristic:  If one or more of the expressions is a call to a
+  ; non-primop, then the following should be a good order:
+  ;
+  ;     expressions that are neither a constant, variable, or a call
+  ;     calls to non-primops
+  ;     constants and variables
+  
+  (define (anf-order-of-evaluation exprs regvars for-primop?)
+    (define (ordering targets exprs alist)
+      (let ((para
+             (parallel-assignment targets alist exprs)))
+        (or para
+            ; Evaluate left to right until a parallel assignment is found.
+            (cons (car targets)
+                  (ordering (cdr targets)
+                            (cdr exprs)
+                            alist)))))
+    (if (parallel-assignment-optimization)
+        (cond ((null? exprs) '())
+              ((null? (cdr exprs)) '(0))
+              (else
+               (let* ((contains-call? #f)
+                      (vexprs (list->vector exprs))
+                      (vindexes (list->vector
+                                 (iota (vector-length vexprs))))
+                      (contains-call? #f)
+                      (categories
+                       (list->vector
+                        (map (lambda (E)
+                               (cond ((constant? E)
+                                      2)
+                                     ((variable? E)
+                                      2)
+                                     ((complicated? E)
+                                      (set! contains-call? #t)
+                                      1)
+                                     (else
+                                      0)))
+                             exprs))))
+                 (cond (contains-call?
+                        (twobit-sort (lambda (i j)
+                                       (< (vector-ref categories i)
+                                          (vector-ref categories j)))
+                                     (iota (length exprs))))
+                       (for-primop?
+                        (reverse (iota (length exprs))))
+                       (else
+                        (let ((targets (iota (length exprs))))
+                          (define (pairup regvars targets)
+                            (if (or (null? targets)
+                                    (null? regvars))
+                                '()
+                                (cons (cons (car regvars)
+                                            (car targets))
+                                      (pairup (cdr regvars)
+                                              (cdr targets)))))
+                          (ordering targets
+                                    exprs
+                                    (pairup regvars targets))))))))
+        (iota (length exprs))))
+  
+  (define (permute things pi)
+    (let ((v (list->vector things)))
+      (map (lambda (i) (vector-ref v i))
+           pi)))
+  
+  (define (unpermute things pi)
+    (let* ((v0 (list->vector things))
+           (v1 (make-vector (vector-length v0))))
+      (do ((pi pi (cdr pi))
+           (k 0 (+ k 1)))
+          ((null? pi)
+           (vector->list v1))
+          (vector-set! v1 (car pi) (vector-ref v0 k)))))
+  
+  ; Given a call whose procedure is a lambda expression that has
+  ; a rest argument, return a genuine let expression.
+  
+  (define (normalize-let-error exp)
+    (if (issue-warnings)
+        (begin (display "WARNING from compiler: ")
+               (display "Wrong number of arguments ")
+               (display "to lambda expression")
+               (newline)
+               (pretty-print (make-readable exp) #t)
+               (newline))))
+  
+  (define (normalize-let exp)
+    (let* ((L (call.proc exp)))
+      (let loop ((formals (lambda.args L))
+                 (args (call.args exp))
+                 (newformals '())
+                 (newargs '()))
+        (cond ((null? formals)
+               (if (null? args)
+                   (begin (lambda.args-set! L (reverse newformals))
+                          (call.args-set! exp (reverse newargs)))
+                   (begin (normalize-let-error exp)
+                          (loop (list (newtemp))
+                                args
+                                newformals
+                                newargs))))
+              ((pair? formals)
+               (if (pair? args)
+                   (loop (cdr formals)
+                         (cdr args)
+                         (cons (car formals) newformals)
+                         (cons (car args) newargs))
+                   (begin (normalize-let-error exp)
+                          (loop formals
+                                (cons (make-constant 0)
+                                      args)
+                                newformals
+                                newargs))))
+              (else
+               (loop (list formals)
+                     (list (make-call-to-list args))
+                     newformals
+                     newargs))))))
+  
+  ; For heuristic use only.
+  ; An expression is complicated unless it can probably be evaluated
+  ; without saving and restoring any registers, even if it occurs in
+  ; a non-tail position.
+  
+  (define (complicated? exp)
+    ; Let's not spend all day on this.
+    (let ((budget 10))
+      (define (complicated? exp)
+        (set! budget (- budget 1))
+        (if (zero? budget)
+            #t
+            (case (car exp)
+              ((quote)    #f)
+              ((lambda)   #f)
+              ((set!)     (complicated? (assignment.rhs exp)))
+              ((if)       (or (complicated? (if.test exp))
+                              (complicated? (if.then exp))
+                              (complicated? (if.else exp))))
+              ((begin)    (if (variable? exp)
+                              #f
+                              (some? complicated?
+                                     (begin.exprs exp))))
+              (else       (let ((proc (call.proc exp)))
+                            (if (and (variable? proc)
+                                     (integrate-usual-procedures)
+                                     (prim-entry (variable.name proc)))
+                                (some? complicated?
+                                       (call.args exp))
+                                #t))))))
+      (complicated? exp)))
+  
+  (A-normal-form E))
+(define (post-simplify-anf L0 T1 E0 E1 free regbindings L2)
+  
+  (define (return-normally)
+    (values (make-call L0 (list E1))
+            free
+            regbindings))
+  
+  (return-normally))
+; Copyright 1999 William D Clinger.
+;
+; Permission to copy this software, in whole or in part, to use this
+; software for any lawful noncommercial purpose, and to redistribute
+; this software is granted subject to the restriction that all copies
+; made of this software must include this copyright notice in full.
+;
+; I also request that you send me a copy of any improvements that you
+; make to this software so that they may be incorporated within it to
+; the benefit of the Scheme community.
+;
+; 7 June 1999.
+;
+; Intraprocedural common subexpression elimination, constant propagation,
+; copy propagation, dead code elimination, and register targeting.
+;
+; (intraprocedural-commoning E 'commoning)
+;
+;     Given an A-normal form E (alpha-converted, with correct free
+;     variables and referencing information), returns an optimized
+;     A-normal form with correct free variables but incorrect referencing
+;     information.
+;
+; (intraprocedural-commoning E 'target-registers)
+;
+;     Given an A-normal form E (alpha-converted, with correct free
+;     variables and referencing information), returns an A-normal form
+;     with correct free variables but incorrect referencing information,
+;     and in which MacScheme machine register names are used as temporary
+;     variables.  The result is alpha-converted except for register names.
+;
+; (intraprocedural-commoning E 'commoning 'target-registers)
+; (intraprocedural-commoning E)
+;
+;     Given an A-normal form as described above, returns an optimized
+;     form in which register names are used as temporary variables.
+
+; Semantics of .check!:
+;
+; (.check! b exn x ...) faults with code exn and arguments x ...
+; if b is #f.
+
+; The list of argument registers.
+; This can't go in pass3commoning.aux.sch because that file must be
+; loaded before the target-specific file that defines *nregs*.
+
+(define argument-registers
+  (do ((n (- *nregs* 2) (- n 1))
+       (regs '()
+             (cons (string->symbol
+                    (string-append ".REG" (number->string n)))
+                   regs)))
+      ((zero? n)
+       regs)))
+
+(define (intraprocedural-commoning E . flags)
+  
+  (define target-registers? (or (null? flags) (memq 'target-registers flags)))
+  (define commoning? (or (null? flags) (memq 'commoning flags)))
+  
+  (define debugging? #f)
+  
+  (call-with-current-continuation
+   (lambda (return)
+     
+     (define (error . stuff)
+       (display "Bug detected during intraprocedural optimization")
+       (newline)
+       (for-each (lambda (s)
+                   (display s) (newline))
+                 stuff)
+       (return (make-constant #f)))
+     
+     ; Given an expression, an environment, the available expressions,
+     ; and an ordered list of likely register variables (used heuristically),
+     ; returns the transformed expression and its set of free variables.
+     
+     (define (scan-body E env available regvars)
+       
+       ; The local variables are those that are bound by a LET within
+       ; this procedure.  The formals of a lambda expression and the
+       ; known local procedures are counted as non-global, not local,
+       ; because there is no let-binding for a formal that can be
+       ; renamed during register targeting.
+       ; For each local variable, we keep track of how many times it
+       ; is referenced.  This information is not accurate until we
+       ; are backing out of the recursion, and does not have to be.
+       
+       (define local-variables (make-hashtable symbol-hash assq))
+       
+       (define (local-variable? sym)
+         (hashtable-get local-variables sym))
+       
+       (define (local-variable-not-used? sym)
+         (= 0 (hashtable-fetch local-variables sym -1)))
+       
+       (define (local-variable-used-once? sym)
+         (= 1 (hashtable-fetch local-variables sym 0)))
+       
+       (define (record-local-variable! sym)
+         (hashtable-put! local-variables sym 0))
+       
+       (define (used-local-variable! sym)
+         (adjust-local-variable! sym 1))
+       
+       (define (adjust-local-variable! sym n)
+         (let ((m (hashtable-get local-variables sym)))
+           (if debugging?
+               (if (and m (> m 0))
+                   (begin (write (list sym (+ m n)))
+                          (newline))))
+           (if m
+               (hashtable-put! local-variables
+                               sym
+                               (+ m n)))))
+       
+       (define (closed-over-local-variable! sym)
+         ; Set its reference count to infinity so it won't be optimized away.
+         ; FIXME:  One million isn't infinity.
+         (hashtable-put! local-variables sym 1000000))
+       
+       (define (used-variable! sym)
+         (used-local-variable! sym))
+       
+       (define (abandon-expression! E)
+         (cond ((variable? E)
+                (adjust-local-variable! (variable.name E) -1))
+               ((conditional? E)
+                (abandon-expression! (if.test E))
+                (abandon-expression! (if.then E))
+                (abandon-expression! (if.else E)))
+               ((call? E)
+                (for-each (lambda (exp)
+                            (if (variable? exp)
+                                (let ((name (variable.name exp)))
+                                  (if (local-variable? name)
+                                      (adjust-local-variable! name -1)))))
+                          (cons (call.proc E)
+                                (call.args E))))))
+       
+       ; Environments are represented as hashtrees.
+       
+       (define (make-empty-environment)
+         (make-hashtree symbol-hash assq))
+       
+       (define (environment-extend env sym)
+         (hashtree-put env sym #t))
+       
+       (define (environment-extend* env symbols)
+         (if (null? symbols)
+             env
+             (environment-extend* (hashtree-put env (car symbols) #t)
+                                  (cdr symbols))))
+       
+       (define (environment-lookup env sym)
+         (hashtree-get env sym))
+       
+       (define (global? x)
+         (cond ((local-variable? x)
+                #f)
+               ((environment-lookup env x)
+                #f)
+               (else
+                #t)))
+       
+       ;
+       
+       (define (available-add! available T E)
+         (cond ((constant? E)
+                (available-extend! available T E available:killer:immortal))
+               ((variable? E)
+                (available-extend! available
+                                   T
+                                   E
+                                   (if (global? (variable.name E))
+                                       available:killer:globals
+                                       available:killer:immortal)))
+               (else
+                (let ((entry (prim-call E)))
+                  (if entry
+                      (let ((killer (prim-lives-until entry)))
+                        (if (not (eq? killer available:killer:dead))
+                            (do ((args (call.args E) (cdr args))
+                                 (k killer
+                                    (let ((arg (car args)))
+                                      (if (and (variable? arg)
+                                               (global? (variable.name arg)))
+                                          available:killer:globals
+                                          k))))
+                                ((null? args)
+                                 (available-extend!
+                                  available
+                                  T
+                                  E
+                                  (logior killer k)))))))))))
+       
+       ; Given an expression E,
+       ; an environment containing all variables that are in scope,
+       ; and a table of available expressions,
+       ; returns multiple values:
+       ;   the transformed E
+       ;   the free variables of E
+       ;   the register bindings to be inserted; each binding has the form
+       ;     (R x (begin R)), where (begin R) is a reference to R.
+       ; 
+       ; Side effects E.
+       
+       (define (scan E env available)
+         (if (not (call? E))
+             (scan-rhs E env available)
+             (let ((proc (call.proc E)))
+               (if (not (lambda? proc))
+                   (scan-rhs E env available)
+                   (let ((vars (lambda.args proc)))
+                     (cond ((null? vars)
+                            (scan-let0 E env available))
+                           ((null? (cdr vars))
+                            (scan-binding E env available))
+                           (else
+                            (error (make-readable E)))))))))
+       
+       ; E has the form of (let ((T1 E1)) E0).
+       
+       (define (scan-binding E env available)
+         (let* ((L (call.proc E))
+                (T1 (car (lambda.args L)))
+                (E1 (car (call.args E)))
+                (E0 (lambda.body L)))
+           (record-local-variable! T1)
+           (call-with-values
+            (lambda () (scan-rhs E1 env available))
+            (lambda (E1 F1 regbindings1)
+              (available-add! available T1 E1)
+              (let* ((env (let ((formals
+                                 (make-null-terminated (lambda.args L))))
+                            (environment-extend*
+                             (environment-extend* env formals)
+                             (map def.lhs (lambda.defs L)))))
+                     (Fdefs (scan-defs L env available)))
+                (call-with-values
+                 (lambda () (scan E0 env available))
+                 (lambda (E0 F0 regbindings0)
+                   (lambda.body-set! L E0)
+                   (if target-registers?
+                       (scan-binding-phase2
+                        L T1 E0 E1 F0 F1 Fdefs regbindings0 regbindings1)
+                       (scan-binding-phase3
+                        L E0 E1 (union F0 Fdefs)
+                                F1 regbindings0 regbindings1)))))))))
+       
+       ; Given the lambda expression for a let expression that binds
+       ; a single variable T1, the transformed body E0 and right hand side E1,
+       ; their sets of free variables F0 and F1, the set of free variables
+       ; for the internal definitions of L, and the sets of register
+       ; bindings that need to be wrapped around E0 and E1, returns the
+       ; transformed let expression, its free variables, and register
+       ; bindings.
+       ;
+       ; This phase is concerned exclusively with register bindings,
+       ; and is bypassed unless the target-registers flag is specified.
+       
+       (define (scan-binding-phase2
+                L T1 E0 E1 F0 F1 Fdefs regbindings0 regbindings1)
+         
+         ; T1 can't be a register because we haven't
+         ; yet inserted register bindings that high up.
+         
+         ; Classify the register bindings that need to wrapped around E0:
+         ;     1.  those that have T1 as their rhs
+         ;     2.  those whose lhs is a register that is likely to hold
+         ;         a variable that occurs free in E1
+         ;     3.  all others
+         
+         (define (phase2a)
+           (do ((rvars regvars (cdr rvars))
+                (regs argument-registers (cdr regs))
+                (regs1 '() (if (memq (car rvars) F1)
+                               (cons (car regs) regs1)
+                               regs1)))
+               ((or (null? rvars)
+                    (null? regs))
+                ; regs1 is the set of registers that are live for E1
+                
+                (let loop ((regbindings regbindings0)
+                           (rb1 '())
+                           (rb2 '())
+                           (rb3 '()))
+                  (if (null? regbindings)
+                      (phase2b rb1 rb2 rb3)
+                      (let* ((binding (car regbindings))
+                             (regbindings (cdr regbindings))
+                             (lhs (regbinding.lhs binding))
+                             (rhs (regbinding.rhs binding)))
+                        (cond ((eq? rhs T1)
+                               (loop regbindings
+                                     (cons binding rb1)
+                                     rb2
+                                     rb3))
+                              ((memq lhs regs1)
+                               (loop regbindings
+                                     rb1
+                                     (cons binding rb2)
+                                     rb3))
+                              (else
+                               (loop regbindings
+                                     rb1
+                                     rb2
+                                     (cons binding rb3))))))))))
+         
+         ; Determine which categories of register bindings should be
+         ; wrapped around E0.
+         ; Always wrap the register bindings in category 2.
+         ; If E1 is a conditional or a real call, then wrap category 3.
+         ; If T1 might be used more than once, then wrap category 1.
+         
+         (define (phase2b rb1 rb2 rb3)
+           (if (or (conditional? E1)
+                   (real-call? E1))
+               (phase2c (append rb2 rb3) rb1 '())
+               (phase2c rb2 rb1 rb3)))
+         
+         (define (phase2c towrap rb1 regbindings0)
+           (cond ((and (not (null? rb1))
+                       (local-variable-used-once? T1))
+                  (phase2d towrap rb1 regbindings0))
+                 (else
+                  (phase2e (append rb1 towrap) regbindings0))))
+         
+         ; T1 is used only once, and there is a register binding (R T1).
+         ; Change T1 to R.
+         
+         (define (phase2d towrap regbindings-T1 regbindings0)
+           (if (not (null? (cdr regbindings-T1)))
+               (error "incorrect number of uses" T1))
+           (let* ((regbinding (car regbindings-T1))
+                  (R (regbinding.lhs regbinding)))
+             (lambda.args-set! L (list R))
+             (phase2e towrap regbindings0)))
+         
+         ; Wrap the selected register bindings around E0.
+         
+         (define (phase2e towrap regbindings0)
+           (call-with-values
+            (lambda ()
+              (wrap-with-register-bindings towrap E0 F0))
+            (lambda (E0 F0)
+              (let ((F (union Fdefs F0)))
+                (scan-binding-phase3
+                 L E0 E1 F F1 regbindings0 regbindings1)))))
+         
+         (phase2a))
+       
+       ; This phase, with arguments as above, constructs the result.
+       
+       (define (scan-binding-phase3 L E0 E1 F F1 regbindings0 regbindings1)
+         (let* ((args (lambda.args L))
+                (T1 (car args))
+                (free (union F1 (difference F args)))
+                (simple-let? (simple-lambda? L))
+                (regbindings 
+                 
+                 ; At least one of regbindings0 and regbindings1
+                 ; is the empty list.
+                 
+                 (cond ((null? regbindings0)
+                        regbindings1)
+                       ((null? regbindings1)
+                        regbindings0)
+                       (else
+                        (error 'scan-binding 'regbindings)))))
+           (lambda.body-set! L E0)
+           (lambda.F-set! L F)
+           (lambda.G-set! L F)
+           (cond ((and simple-let?
+                       (not (memq T1 F))
+                       (no-side-effects? E1))
+                  (abandon-expression! E1)
+                  (values E0 F regbindings0))
+                 ((and target-registers?
+                       simple-let?
+                       (local-variable-used-once? T1))
+                  (post-simplify-anf L T1 E0 E1 free regbindings #f))
+                 (else
+                  (values (make-call L (list E1))
+                          free
+                          regbindings)))))
+       
+       (define (scan-let0 E env available)
+         (let ((L (call.proc E)))
+           (if (simple-lambda? L)
+               (scan (lambda.body L) env available)
+               (let ((T1 (make-variable name:IGNORED)))
+                 (lambda.args-set! L (list T1))
+                 (call-with-values
+                  (lambda () (scan (make-call L (list (make-constant 0)))
+                                   env
+                                   available))
+                  (lambda (E F regbindings)
+                    (lambda.args-set! L '())
+                    (values (make-call L '())
+                            F
+                            regbindings)))))))
+       
+       ; Optimizes the internal definitions of L and returns their
+       ; free variables.
+       
+       (define (scan-defs L env available)
+         (let loop ((defs (lambda.defs L))
+                    (newdefs '())
+                    (Fdefs '()))
+           (if (null? defs)
+               (begin (lambda.defs-set! L (reverse newdefs))
+                      Fdefs)
+               (let ((def (car defs)))
+                 (call-with-values
+                  (lambda ()
+                    (let* ((Ldef (def.rhs def))
+                           (Lformals (make-null-terminated (lambda.args Ldef)))
+                           (Lenv (environment-extend*
+                                  (environment-extend* env Lformals)
+                                  (map def.lhs (lambda.defs Ldef)))))
+                      (scan Ldef Lenv available)))
+                  (lambda (rhs Frhs empty)
+                    (if (not (null? empty))
+                        (error 'scan-binding 'def))
+                    (loop (cdr defs)
+                          (cons (make-definition (def.lhs def) rhs)
+                                newdefs)
+                          (union Frhs Fdefs))))))))
+       
+       ; Given the right-hand side of a let-binding, an environment,
+       ; and a table of available expressions, returns the transformed
+       ; expression, its free variables, and the register bindings that
+       ; need to be wrapped around it.
+       
+       (define (scan-rhs E env available)
+         
+         (cond
+          ((constant? E)
+           (values E (empty-set) '()))
+          
+          ((variable? E)
+           (let* ((name (variable.name E))
+                  (Enew (and commoning?
+                             (if (global? name)
+                                 (let ((T (available-expression
+                                           available E)))
+                                   (if T
+                                       (make-variable T)
+                                       #f))
+                                 (available-variable available name)))))
+             (if Enew
+                 (scan-rhs Enew env available)
+                 (begin (used-variable! name)
+                        (values E (list name) '())))))
+          
+          ((lambda? E)
+           (let* ((formals (make-null-terminated (lambda.args E)))
+                  (env (environment-extend*
+                        (environment-extend* env formals)
+                        (map def.lhs (lambda.defs E))))
+                  (Fdefs (scan-defs E env available)))
+             (call-with-values
+              (lambda ()
+                (let ((available (copy-available-table available)))
+                  (available-kill! available available:killer:all)
+                  (scan-body (lambda.body E)
+                             env
+                             available
+                             formals)))
+              (lambda (E0 F0 regbindings0)
+                (call-with-values
+                 (lambda ()
+                   (wrap-with-register-bindings regbindings0 E0 F0))
+                 (lambda (E0 F0)
+                   (lambda.body-set! E E0)
+                   (let ((F (union Fdefs F0)))
+                     (for-each (lambda (x)
+                                 (closed-over-local-variable! x))
+                               F)
+                     (lambda.F-set! E F)
+                     (lambda.G-set! E F)
+                     (values E
+                             (difference F
+                                         (make-null-terminated
+                                          (lambda.args E)))
+                             '()))))))))
+          
+          ((conditional? E)
+           (let ((E0 (if.test E))
+                 (E1 (if.then E))
+                 (E2 (if.else E)))
+             (if (constant? E0)
+                 ; FIXME: E1 and E2 might not be a legal rhs,
+                 ; so we can't just return the simplified E1 or E2.
+                 (let ((E1 (if (constant.value E0) E1 E2)))
+                   (call-with-values
+                    (lambda () (scan E1 env available))
+                    (lambda (E1 F1 regbindings1)
+                      (cond ((or (not (call? E1))
+                                 (not (lambda? (call.proc E1))))
+                             (values E1 F1 regbindings1))
+                            (else
+                             ; FIXME: Must return a valid rhs.
+                             (values (make-conditional
+                                      (make-constant #t)
+                                      E1
+                                      (make-constant 0))
+                                     F1
+                                     regbindings1))))))
+                 (call-with-values
+                  (lambda () (scan E0 env available))
+                  (lambda (E0 F0 regbindings0)
+                    (if (not (null? regbindings0))
+                        (error 'scan-rhs 'if))
+                    (if (not (eq? E0 (if.test E)))
+                        (scan-rhs (make-conditional E0 E1 E2)
+                                  env available)
+                        (let ((available1
+                               (copy-available-table available))
+                              (available2
+                               (copy-available-table available)))
+                          (if (variable? E0)
+                              (let ((T0 (variable.name E0)))
+                                (available-add!
+                                 available2 T0 (make-constant #f)))
+                              (error (make-readable E #t)))
+                          (call-with-values
+                           (lambda () (scan E1 env available1))
+                           (lambda (E1 F1 regbindings1)
+                             (call-with-values
+                              (lambda ()
+                                (wrap-with-register-bindings
+                                 regbindings1 E1 F1))
+                              (lambda (E1 F1)
+                                (call-with-values
+                                 (lambda () (scan E2 env available2))
+                                 (lambda (E2 F2 regbindings2)
+                                   (call-with-values
+                                    (lambda ()
+                                      (wrap-with-register-bindings
+                                       regbindings2 E2 F2))
+                                    (lambda (E2 F2)
+                                      (let ((E (make-conditional
+                                                E0 E1 E2))
+                                            (F (union F0 F1 F2)))
+                                        (available-intersect!
+                                         available
+                                         available1
+                                         available2)
+                                        (values E F '())))))))))))))))))
+          
+          
+          ((assignment? E)
+           (call-with-values
+            (lambda () (scan-rhs (assignment.rhs E) env available))
+            (lambda (E1 F1 regbindings1)
+              (if (not (null? regbindings1))
+                  (error 'scan-rhs 'set!))
+              (available-kill! available available:killer:globals)
+              (values (make-assignment (assignment.lhs E) E1)
+                      (union (list (assignment.lhs E)) F1)
+                      '()))))
+          
+          ((begin? E)
+           ; Shouldn't occur in A-normal form.
+           (error 'scan-rhs 'begin))
+          
+          ((real-call? E)
+           (let* ((E0 (call.proc E))
+                  (args (call.args E))
+                  (regcontents (append regvars
+                                       (map (lambda (x) #f) args))))
+             (let loop ((args args)
+                        (regs argument-registers)
+                        (regcontents regcontents)
+                        (newargs '())
+                        (regbindings '())
+                        (F (if (variable? E0)
+                               (let ((f (variable.name E0)))
+                                 (used-variable! f)
+                                 (list f))
+                               (empty-set))))
+               (cond ((null? args)
+                      (available-kill! available available:killer:all)
+                      (values (make-call E0 (reverse newargs))
+                              F
+                              regbindings))
+                     ((null? regs)
+                      (let ((arg (car args)))
+                        (loop (cdr args)
+                              '()
+                              (cdr regcontents)
+                              (cons arg newargs)
+                              regbindings
+                              (if (variable? arg)
+                                  (let ((name (variable.name arg)))
+                                    (used-variable! name)
+                                    (union (list name) F))
+                                  F))))
+                     ((and commoning?
+                           (variable? (car args))
+                           (available-variable
+                            available
+                            (variable.name (car args))))
+                      (let* ((name (variable.name (car args)))
+                             (Enew (available-variable available name)))
+                        (loop (cons Enew (cdr args))
+                              regs regcontents newargs regbindings F)))
+                     ((and target-registers?
+                           (variable? (car args))
+                           (let ((x (variable.name (car args))))
+                             ; We haven't yet recorded this use.
+                             (or (local-variable-not-used? x)
+                                 (and (memq x regvars)
+                                      (not (eq? x (car regcontents)))))))
+                      (let* ((x (variable.name (car args)))
+                             (R (car regs))
+                             (newarg (make-variable R)))
+                        (used-variable! x)
+                        (loop (cdr args)
+                              (cdr regs)
+                              (cdr regcontents)
+                              (cons newarg newargs)
+                              (cons (make-regbinding R x newarg)
+                                    regbindings)
+                              (union (list R) F))))
+                     (else
+                      (let ((E1 (car args)))
+                        (loop (cdr args)
+                              (cdr regs)
+                              (cdr regcontents)
+                              (cons E1 newargs)
+                              regbindings
+                              (if (variable? E1)
+                                  (let ((name (variable.name E1)))
+                                    (used-variable! name)
+                                    (union (list name) F))
+                                  F))))))))
+          
+          ((call? E)
+           ; Must be a call to a primop.
+           (let* ((E0 (call.proc E))
+                  (f0 (variable.name E0)))
+             (let loop ((args (call.args E))
+                        (newargs '())
+                        (F (list f0)))
+               (cond ((null? args)
+                      (let* ((E (make-call E0 (reverse newargs)))
+                             (T (and commoning?
+                                     (available-expression
+                                      available E))))
+                        (if T
+                            (begin (abandon-expression! E)
+                                   (scan-rhs (make-variable T) env available))
+                            (begin
+                             (available-kill!
+                              available
+                              (prim-kills (prim-entry f0)))
+                             (cond ((eq? f0 name:check!)
+                                    (let ((x (car (call.args E))))
+                                      (cond ((not (runtime-safety-checking))
+                                             (abandon-expression! E)
+                                             ;(values x '() '())
+                                             (scan-rhs x env available))
+                                            ((variable? x)
+                                             (available-add!
+                                              available
+                                              (variable.name x)
+                                              (make-constant #t))
+                                             (values E F '()))
+                                            ((constant.value x)
+                                             (abandon-expression! E)
+                                             (values x '() '()))
+                                            (else
+                                             (declaration-error E)
+                                             (values E F '())))))
+                                   (else
+                                    (values E F '())))))))
+                     ((variable? (car args))
+                      (let* ((E1 (car args))
+                             (x (variable.name E1))
+                             (Enew
+                              (and commoning?
+                                   (available-variable available x))))
+                        (if Enew
+                            ; All of the arguments are constants or
+                            ; variables, so if the variable is replaced
+                            ; here it will be replaced throughout the call.
+                            (loop (cons Enew (cdr args))
+                                  newargs
+                                  (remq x F))
+                            (begin
+                             (used-variable! x)
+                             (loop (cdr args)
+                                   (cons (car args) newargs)
+                                   (union (list x) F))))))
+                     (else
+                      (loop (cdr args)
+                            (cons (car args) newargs)
+                            F))))))
+          
+          (else
+           (error 'scan-rhs (make-readable E)))))
+       
+       (call-with-values
+        (lambda () (scan E env available))
+        (lambda (E F regbindings)
+          (call-with-values
+           (lambda () (wrap-with-register-bindings regbindings E F))
+           (lambda (E F)
+             (values E F '()))))))
+     
+     (call-with-values
+      (lambda ()
+        (scan-body E
+                   (make-hashtree symbol-hash assq)
+                   (make-available-table)
+                   '()))
+      (lambda (E F regbindings)
+        (if (not (null? regbindings))
+            (error 'scan-body))
+        E)))))
+; Copyright 1999 William D Clinger.
+;
+; Permission to copy this software, in whole or in part, to use this
+; software for any lawful noncommercial purpose, and to redistribute
+; this software is granted subject to the restriction that all copies
+; made of this software must include this copyright notice in full.
+;
+; I also request that you send me a copy of any improvements that you
+; make to this software so that they may be incorporated within it to
+; the benefit of the Scheme community.
+;
+; 16 June 1999.
+;
+; Intraprocedural representation inference.
+
+(define (representation-analysis exp)
+  (let* ((debugging? #f)
+         (integrate-usual? (integrate-usual-procedures))
+         (known (make-hashtable symbol-hash assq))
+         (types (make-hashtable symbol-hash assq))
+         (g (callgraph exp))
+         (schedule (list (callgraphnode.code (car g))))
+         (changed? #f)
+         (mutate? #f))
+    
+    ; known is a hashtable that maps the name of a known local procedure
+    ; to a list of the form (tv1 ... tvN), where tv1, ..., tvN
+    ; are type variables that stand for the representation types of its
+    ; arguments.  The type variable that stands for the representation
+    ; type of the result of the procedure has the same name as the
+    ; procedure itself.
+    
+    ; types is a hashtable that maps local variables and the names
+    ; of known local procedures to an approximation of their
+    ; representation type.
+    ; For a known local procedure, the representation type is for the
+    ; result of the procedure, not the procedure itself.
+    
+    ; schedule is a stack of work that needs to be done.
+    ; Each entry in the stack is either an escaping lambda expression
+    ; or the name of a known local procedure.
+    
+    (define (schedule! job)
+      (if (not (memq job schedule))
+          (begin (set! schedule (cons job schedule))
+                 (if (not (symbol? job))
+                     (callgraphnode.info! (lookup-node job) #t)))))
+    
+    ; Schedules a known local procedure.
+    
+    (define (schedule-known-procedure! name)
+      ; Mark every known procedure that can actually be called.
+      (callgraphnode.info! (assq name g) #t)
+      (schedule! name))
+    
+    ; Schedule all code that calls the given known local procedure.
+    
+    (define (schedule-callers! name)
+      (for-each (lambda (node)
+                  (if (and (callgraphnode.info node)
+                           (or (memq name (callgraphnode.tailcalls node))
+                               (memq name (callgraphnode.nontailcalls node))))
+                      (let ((caller (callgraphnode.name node)))
+                        (if caller
+                            (schedule! caller)
+                            (schedule! (callgraphnode.code node))))))
+                g))
+    
+    ; Schedules local procedures of a lambda expression.
+    
+    (define (schedule-local-procedures! L)
+      (for-each (lambda (def)
+                  (let ((name (def.lhs def)))
+                    (if (known-procedure-is-callable? name)
+                        (schedule! name))))
+                (lambda.defs L)))
+    
+    ; Returns true iff the given known procedure is known to be callable.
+    
+    (define (known-procedure-is-callable? name)
+      (callgraphnode.info (assq name g)))
+    
+    ; Sets CHANGED? to #t and returns #t if the type variable's
+    ; approximation has changed; otherwise returns #f.
+    
+    (define (update-typevar! tv type)
+      (let* ((type0 (hashtable-get types tv))
+             (type0 (or type0
+                        (begin (hashtable-put! types tv rep:bottom)
+                               rep:bottom)))
+             (type1 (representation-union type0 type)))
+        (if (eq? type0 type1)
+            #f
+            (begin (hashtable-put! types tv type1)
+                   (set! changed? #t)
+                   (if (and debugging? mutate?)
+                       (begin (display "******** Changing type of ")
+                              (display tv)
+                              (display " from ")
+                              (display (rep->symbol type0))
+                              (display " to ")
+                              (display (rep->symbol type1))
+                              (newline)))
+                   #t))))
+    
+    ; GIven the name of a known local procedure, returns its code.
+    
+    (define (lookup-code name)
+      (callgraphnode.code (assq name g)))
+    
+    ; Given a lambda expression, either escaping or the code for
+    ; a known local procedure, returns its node in the call graph.
+    
+    (define (lookup-node L)
+      (let loop ((g g))
+        (cond ((null? g)
+               (error "Unknown lambda expression" (make-readable L #t)))
+              ((eq? L (callgraphnode.code (car g)))
+               (car g))
+              (else
+               (loop (cdr g))))))
+    
+    ; Given: a type variable, expression, and a set of constraints.
+    ; Side effects:
+    ;     Update the representation types of all variables that are
+    ;         bound within the expression.
+    ;     Update the representation types of all arguments to known
+    ;         local procedures that are called within the expression.
+    ;     If the representation type of an argument to a known local
+    ;         procedure changes, then schedule that procedure's code
+    ;         for analysis.
+    ;     Update the constraint set to reflect the constraints that
+    ;         hold following execution of the expression.
+    ;     If mutate? is true, then transform the expression to rely
+    ;         on the representation types that have been inferred.
+    ; Return: type of the expression under the current assumptions
+    ;     and constraints.
+    
+    (define (analyze exp constraints)
+      
+      (if (and #f debugging?)
+          (begin (display "Analyzing: ")
+                 (newline)
+                 (pretty-print (make-readable exp #t))
+                 (newline)))
+      
+      (case (car exp)
+        
+        ((quote)
+         (representation-of-value (constant.value exp)))
+        
+        ((begin)
+         (let* ((name (variable.name exp)))
+           (representation-typeof name types constraints)))
+        
+        ((lambda)
+         (schedule! exp)
+         rep:procedure)
+        
+        ((set!)
+         (analyze (assignment.rhs exp) constraints)
+         (constraints-kill! constraints available:killer:globals)
+         rep:object)
+        
+        ((if)
+         (let* ((E0 (if.test exp))
+                (E1 (if.then exp))
+                (E2 (if.else exp))
+                (type0 (analyze E0 constraints)))
+           (if mutate?
+               (cond ((representation-subtype? type0 rep:true)
+                      (if.test-set! exp (make-constant #t)))
+                     ((representation-subtype? type0 rep:false)
+                      (if.test-set! exp (make-constant #f)))))
+           (cond ((representation-subtype? type0 rep:true)
+                  (analyze E1 constraints))
+                 ((representation-subtype? type0 rep:false)
+                  (analyze E2 constraints))
+                 ((variable? E0)
+                  (let* ((T0 (variable.name E0))
+                         (ignored (analyze E0 constraints))
+                         (constraints1 (copy-constraints-table constraints))
+                         (constraints2 (copy-constraints-table constraints)))
+                    (constraints-add! types
+                                      constraints1
+                                      (make-type-constraint
+                                       T0 rep:true available:killer:immortal))
+                    (constraints-add! types
+                                      constraints2
+                                      (make-type-constraint
+                                       T0 rep:false available:killer:immortal))
+                    (let* ((type1 (analyze E1 constraints1))
+                           (type2 (analyze E2 constraints2))
+                           (type (representation-union type1 type2)))
+                      (constraints-intersect! constraints
+                                              constraints1
+                                              constraints2)
+                      type)))
+                 (else
+                  (representation-error "Bad ANF" (make-readable exp #t))))))
+        
+        (else
+         (let ((proc (call.proc exp))
+               (args (call.args exp)))
+           (cond ((lambda? proc)
+                  (cond ((null? args)
+                         (analyze-let0 exp constraints))
+                        ((null? (cdr args))
+                         (analyze-let1 exp constraints))
+                        (else
+                         (error "Compiler bug: pass3rep"))))
+                 ((variable? proc)
+                  (let* ((procname (variable.name proc)))
+                    (cond ((hashtable-get known procname)
+                           =>
+                           (lambda (vars)
+                             (analyze-known-call exp constraints vars)))
+                          (integrate-usual?
+                           (let ((entry (prim-entry procname)))
+                             (if entry
+                                 (analyze-primop-call exp constraints entry)
+                                 (analyze-unknown-call exp constraints))))
+                          (else
+                           (analyze-unknown-call exp constraints)))))
+                 (else
+                  (analyze-unknown-call exp constraints)))))))
+    
+    (define (analyze-let0 exp constraints)
+      (let ((proc (call.proc exp)))
+        (schedule-local-procedures! proc)
+        (if (null? (lambda.args proc))
+            (analyze (lambda.body exp) constraints)
+            (analyze-unknown-call exp constraints))))
+    
+    (define (analyze-let1 exp constraints)
+      (let* ((proc (call.proc exp))
+             (vars (lambda.args proc)))
+        (schedule-local-procedures! proc)
+        (if (and (pair? vars)
+                 (null? (cdr vars)))
+            (let* ((T1 (car vars))
+                   (E1 (car (call.args exp))))
+              (if (and integrate-usual? (call? E1))
+                  (let ((proc (call.proc E1))
+                        (args (call.args E1)))
+                    (if (variable? proc)
+                        (let* ((op (variable.name proc))
+                               (entry (prim-entry op))
+                               (K1 (if entry
+                                       (prim-lives-until entry)
+                                       available:killer:dead)))
+                          (if (not (= K1 available:killer:dead))
+                              ; Must copy the call to avoid problems
+                              ; with side effects when mutate? is true.
+                              (constraints-add!
+                               types
+                               constraints
+                               (make-constraint T1
+                                                (make-call proc args)
+                                                K1)))))))
+              (update-typevar! T1 (analyze E1 constraints))
+              (analyze (lambda.body proc) constraints))
+            (analyze-unknown-call exp constraints))))
+    
+    (define (analyze-primop-call exp constraints entry)
+      (let* ((op (prim-opcodename entry))
+             (args (call.args exp))
+             (argtypes (map (lambda (arg) (analyze arg constraints))
+                            args))
+             (type (rep-result? op argtypes)))
+        (constraints-kill! constraints (prim-kills entry))
+        (cond ((and (eq? op 'check!)
+                    (variable? (car args)))
+               (let ((varname (variable.name (car args))))
+                 (if (and mutate?
+                          (representation-subtype? (car argtypes) rep:true))
+                     (call.args-set! exp
+                                     (cons (make-constant #t) (cdr args))))
+                 (constraints-add! types
+                                   constraints
+                                   (make-type-constraint
+                                    varname
+                                    rep:true
+                                    available:killer:immortal))))
+              ((and mutate? (rep-specific? op argtypes))
+               =>
+               (lambda (newop)
+                 (call.proc-set! exp (make-variable newop)))))
+        (or type rep:object)))
+    
+    (define (analyze-known-call exp constraints vars)
+      (let* ((procname (variable.name (call.proc exp)))
+             (args (call.args exp))
+             (argtypes (map (lambda (arg) (analyze arg constraints))
+                            args)))
+        (if (not (known-procedure-is-callable? procname))
+            (schedule-known-procedure! procname))
+        (for-each (lambda (var type)
+                    (if (update-typevar! var type)
+                        (schedule-known-procedure! procname)))
+                  vars
+                  argtypes)
+        ; FIXME: We aren't analyzing the effects of known local procedures.
+        (constraints-kill! constraints available:killer:all)
+        (hashtable-get types procname)))
+    
+    (define (analyze-unknown-call exp constraints)
+      (analyze (call.proc exp) constraints)
+      (for-each (lambda (arg) (analyze arg constraints))
+                (call.args exp))
+      (constraints-kill! constraints available:killer:all)
+      rep:object)
+    
+    (define (analyze-known-local-procedure name)
+      (if debugging?
+          (begin (display "Analyzing ")
+                 (display name)
+                 (newline)))
+      (let ((L (lookup-code name))
+            (constraints (make-constraints-table)))
+        (schedule-local-procedures! L)
+        (let ((type (analyze (lambda.body L) constraints)))
+          (if (update-typevar! name type)
+              (schedule-callers! name))
+          type)))
+    
+    (define (analyze-unknown-lambda L)
+      (if debugging?
+          (begin (display "Analyzing escaping lambda expression")
+                 (newline)))
+      (schedule-local-procedures! L)
+      (let ((vars (make-null-terminated (lambda.args L))))
+        (for-each (lambda (var)
+                    (hashtable-put! types var rep:object))
+                  vars)
+        (analyze (lambda.body L)
+                 (make-constraints-table))))
+    
+    ; For debugging.
+    
+    (define (display-types)
+      (hashtable-for-each (lambda (f vars)
+                            (write f)
+                            (display " : returns ")
+                            (write (rep->symbol (hashtable-get types f)))
+                            (newline)
+                            (for-each (lambda (x)
+                                        (display "  ")
+                                        (write x)
+                                        (display ": ")
+                                        (write (rep->symbol
+                                                (hashtable-get types x)))
+                                        (newline))
+                                      vars))
+                          known))
+    
+    (define (display-all-types)
+      (let* ((vars (hashtable-map (lambda (x type) x) types))
+             (vars (twobit-sort (lambda (var1 var2)
+                                  (string<=? (symbol->string var1)
+                                             (symbol->string var2)))
+                                vars)))
+        (for-each (lambda (x)
+                    (write x)
+                    (display ": ")
+                    (write (rep->symbol
+                            (hashtable-get types x)))
+                    (newline))
+                  vars)))
+    '
+    (if debugging?
+        (begin (pretty-print (make-readable (car schedule) #t))
+               (newline)))
+    (if debugging?
+        (view-callgraph g))
+    
+    (for-each (lambda (node)
+                (let* ((name (callgraphnode.name node))
+                       (code (callgraphnode.code node))
+                       (vars (make-null-terminated (lambda.args code)))
+                       (known? (symbol? name))
+                       (rep (if known? rep:bottom rep:object)))
+                  (callgraphnode.info! node #f)
+                  (if known?
+                      (begin (hashtable-put! known name vars)
+                             (hashtable-put! types name rep)))
+                  (for-each (lambda (var)
+                              (hashtable-put! types var rep))
+                            vars)))
+              g)
+    
+    (let loop ()
+      (cond ((not (null? schedule))
+             (let ((job (car schedule)))
+               (set! schedule (cdr schedule))
+               (if (symbol? job)
+                   (analyze-known-local-procedure job)
+                   (analyze-unknown-lambda job))
+               (loop)))
+            (changed?
+             (set! changed? #f)
+             (set! schedule (list (callgraphnode.code (car g))))
+             (if debugging?
+                 (begin (display-all-types) (newline)))
+             (loop))))
+    
+    (if debugging?
+        (display-types))
+    
+    (set! mutate? #t)
+    
+    ; We don't want to analyze known procedures that are never called.
+    
+    (set! schedule
+          (cons (callgraphnode.code (car g))
+                (map callgraphnode.name
+                     (filter (lambda (node)
+                               (let* ((name (callgraphnode.name node))
+                                      (known? (symbol? name))
+                                      (marked?
+                                       (known-procedure-is-callable? name)))
+                                 (callgraphnode.info! node #f)
+                                 (and known? marked?)))
+                             g))))
+    (let loop ()
+      (if (not (null? schedule))
+          (let ((job (car schedule)))
+            (set! schedule (cdr schedule))
+            (if (symbol? job)
+                (analyze-known-local-procedure job)
+                (analyze-unknown-lambda job))
+            (loop))))
+    
+    (if changed?
+        (error "Compiler bug in representation inference"))
+    
+    (if debugging?
+        (pretty-print (make-readable (callgraphnode.code (car g)) #t)))
+    
+    exp))
+; Copyright 1999 William D Clinger.
+;
+; Permission to copy this software, in whole or in part, to use this
+; software for any lawful noncommercial purpose, and to redistribute
+; this software is granted subject to the restriction that all copies
+; made of this software must include this copyright notice in full.
+; 
+; I also request that you send me a copy of any improvements that you
+; make to this software so that they may be incorporated within it to
+; the benefit of the Scheme community.
+;
+; 11 June 1999.
+;
+; The third "pass" of the Twobit compiler actually consists of several
+; passes, which are related by the common theme of flow analysis:
+;   interprocedural inlining of known local procedures
+;   interprocedural constant propagation and folding
+;   intraprocedural commoning, copy propagation, and dead code elimination
+;   representation inference (not yet implemented)
+;   register targeting
+;
+; This pass operates as source-to-source transformations on
+; expressions written in the subset of Scheme described by the
+; following grammar:
+;
+; "X ..." means zero or more occurrences of X.
+;
+; L  -->  (lambda (I_1 ...)
+;           (begin D ...)
+;           (quote (R F G <decls> <doc>)
+;           E)
+;      |  (lambda (I_1 ... . I_rest)
+;           (begin D ...)
+;           (quote (R F G <decls> <doc>))
+;           E)
+; D  -->  (define I L)
+; E  -->  (quote K)                        ; constants
+;      |  (begin I)                        ; variable references
+;      |  L                                ; lambda expressions
+;      |  (E0 E1 ...)                      ; calls
+;      |  (set! I E)                       ; assignments
+;      |  (if E0 E1 E2)                    ; conditionals
+;      |  (begin E0 E1 E2 ...)             ; sequential expressions
+; I  -->  <identifier>
+;
+; R  -->  ((I <references> <assignments> <calls>) ...)
+; F  -->  (I ...)
+; G  -->  (I ...)
+;
+; Invariants that hold for the input only:
+;   *  There are no assignments except to global variables.
+;   *  If I is declared by an internal definition, then the right hand
+;      side of the internal definition is a lambda expression and I
+;      is referenced only in the procedure position of a call.
+;   *  R, F, and G are garbage.
+;   *  Variables named IGNORED are neither referenced nor assigned.
+;   *  The expression does not share structure with the original input,
+;      but might share structure with itself.
+;
+; Invariants that hold for the output only:
+;   *  There are no assignments except to global variables.
+;   *  If I is declared by an internal definition, then the right hand
+;      side of the internal definition is a lambda expression and I
+;      is referenced only in the procedure position of a call.
+;   *  R is garbage.
+;   *  For each lambda expression, the associated F is a list of all
+;      the identifiers that occur free in the body of that lambda
+;      expression, and possibly a few extra identifiers that were
+;      once free but have been removed by optimization.
+;   *  If a lambda expression is declared to be in A-normal form (see
+;      pass3anormal.sch), then it really is in A-normal form.
+;
+; The phases of pass 3 interact with the referencing information R
+; and the free variables F as follows:
+;
+; Inlining               ignores R,   ignores F,  destroys R,  destroys F.
+; Constant propagation      uses R,   ignores F, preserves R, preserves F.
+; Conversion to ANF      ignores R,   ignores F,  destroys R,  destroys F.
+; Commoning              ignores R,   ignores F,  destroys R,  computes F.
+; Register targeting     ignores R,   ignores F,  destroys R,  computes F.
+
+(define (pass3 exp)
+  
+  (define (phase1 exp)
+    (if (interprocedural-inlining)
+        (let ((g (callgraph exp)))
+          (inline-using-callgraph! g)
+          exp)
+        exp))
+  
+  (define (phase2 exp)
+    (if (interprocedural-constant-propagation)
+        (constant-propagation (copy-exp exp))
+        exp))
+  
+  (define (phase3 exp)
+    (if (common-subexpression-elimination)
+        (let* ((exp (if (interprocedural-constant-propagation)
+                        exp
+                        ; alpha-conversion
+                        (copy-exp exp)))
+               (exp (a-normal-form exp)))
+          (if (representation-inference)
+              (intraprocedural-commoning exp 'commoning)
+              (intraprocedural-commoning exp)))
+        exp))
+  
+  (define (phase4 exp)
+    (if (representation-inference)
+        (let ((exp (cond ((common-subexpression-elimination)
+                          exp)
+                         ((interprocedural-constant-propagation)
+                          (a-normal-form exp))
+                         (else
+                          ; alpha-conversion
+                          (a-normal-form (copy-exp exp))))))
+          (intraprocedural-commoning
+           (representation-analysis exp)))
+        exp))
+  
+  (define (finish exp)
+    (if (and (not (interprocedural-constant-propagation))
+             (not (common-subexpression-elimination)))
+        (begin (compute-free-variables! exp)
+               exp)
+        ;(make-begin (list (make-constant 'anf) exp))))
+        exp))
+  
+  (define (verify exp)
+    (check-referencing-invariants exp 'free)
+    exp)
+  
+  (if (global-optimization)
+      (verify (finish (phase4 (phase3 (phase2 (phase1 exp))))))
+      (begin (compute-free-variables! exp)
+             (verify exp))))
+; Copyright 1991 Lightship Software, Incorporated.
+;
+; $Id: twobit.sch,v 1.3 1999/08/23 19:14:26 lth Exp $
+;
+; 4 June 1999
+
+; Implements the following abstract data types.
+;
+; labels
+;     (init-labels)
+;     (make-label)
+;     cg-label-counter
+;
+; assembly streams
+;     (make-assembly-stream)
+;     (assembly-stream-code as)
+;     (gen! as . instruction)
+;     (gen-instruction! as instruction)
+;     (gen-save! as frame)
+;     (gen-restore! as frame)
+;     (gen-pop! as frame)
+;     (gen-setstk! as frame v)
+;     (gen-store! as frame r v)
+;     (gen-load! as frame r v)
+;     (gen-stack! as frame v)
+;
+; temporaries
+;     (init-temps)
+;     (newtemp)
+;     (newtemps)
+;     newtemp-counter
+;
+; register environments
+;     (cgreg-initial)
+;     (cgreg-copy regs)
+;     (cgreg-tos regs)
+;     (cgreg-liveregs regs)
+;     (cgreg-live regs r)
+;     (cgreg-vars regs)
+;     (cgreg-bind! regs r v)
+;     (cgreg-bindregs! regs vars)
+;     (cgreg-rename! regs alist)
+;     (cgreg-release! regs r)
+;     (cgreg-clear! regs)
+;     (cgreg-lookup regs var)
+;     (cgreg-lookup-reg regs r)
+;     (cgreg-join! regs1 regs2)
+;
+; stack frame environments
+;     (cgframe-initial)
+;     (cgframe-size-cell frame)
+;     (cgframe-size frame)
+;     (cgframe-copy frame)
+;     (cgframe-join! frame1 frame2)
+;     (cgframe-update-stale! frame)
+;     (cgframe-used! frame)
+;     (cgframe-bind! frame n v instruction)
+;     (cgframe-touch! frame v)
+;     (cgframe-rename! frame alist)
+;     (cgframe-release! frame v)
+;     (cgframe-lookup frame v)
+;     (cgframe-spilled? frame v)
+;
+; environments
+;     (entry.name entry)
+;     (entry.kind entry)
+;     (entry.rib entry)
+;     (entry.offset entry)
+;     (entry.label entry)
+;     (entry.regnum entry)
+;     (entry.arity entry)
+;     (entry.op entry)
+;     (entry.imm entry)
+;     (cgenv-initial)
+;     (cgenv-lookup env id)
+;     (cgenv-extend env vars procs)
+;     (cgenv-bindprocs env procs)
+;     (var-lookup var regs frame env)
+
+; Labels.
+
+(define (init-labels)
+  (set! cg-label-counter 1000))
+
+(define (make-label)
+  (set! cg-label-counter (+ cg-label-counter 1))
+  cg-label-counter)
+
+(define cg-label-counter 1000)
+
+;    an assembly stream into which instructions should be emitted
+;    an expression
+;    the desired target register ('result, a register number, or '#f)
+;    a register environment [cgreg]
+;    a stack-frame environment [cgframe]
+;      contains size of frame, current top of frame
+;    a compile-time environment [cgenv]
+;    a flag indicating whether the expression is in tail position
+
+; Assembly streams, into which instructions are emitted by side effect.
+; Represented as a list of two things:
+;
+;     Assembly code, represented as a pair whose car is a nonempty list
+;     whose cdr is a possibly empty list of MacScheme machine assembly
+;     instructions, and whose cdr is the last pair of the car.
+;
+;     Any Scheme object that the code generator wants to associate with
+;     this code.
+
+(define (make-assembly-stream)
+  (let ((code (list (list 0))))
+    (set-cdr! code (car code))
+    (list code #f)))
+
+(define (assembly-stream-code output)
+  (if (local-optimizations)
+      (filter-basic-blocks (cdar (car output)))
+      (cdar (car output))))
+
+(define (assembly-stream-info output)
+  (cadr output))
+
+(define (assembly-stream-info! output x)
+  (set-car! (cdr output) x)
+  #f)
+
+(define (gen-instruction! output instruction)
+  (let ((pair (list instruction))
+        (code (car output)))
+    (set-cdr! (cdr code) pair)
+    (set-cdr! code pair)
+    output))
+
+;
+
+(define (gen! output . instruction)
+  (gen-instruction! output instruction))
+
+(define (gen-save! output frame t0)
+  (let ((size (cgframe-size-cell frame)))
+    (gen-instruction! output (cons $save size))
+    (gen-store! output frame 0 t0)
+    (cgframe:stale-set! frame '())))
+
+(define (gen-restore! output frame)
+  (let ((size (cgframe-size-cell frame)))
+    (gen-instruction! output (cons $restore size))))
+
+(define (gen-pop! output frame)
+  (let ((size (cgframe-size-cell frame)))
+    (gen-instruction! output (cons $pop size))))
+
+(define (gen-setstk! output frame tempname)
+  (let ((instruction (list $nop $setstk -1)))
+    (cgframe-bind! frame tempname instruction)
+    (gen-instruction! output instruction)))
+
+(define (gen-store! output frame r tempname)
+  (let ((instruction (list $nop $store r -1)))
+    (cgframe-bind! frame tempname instruction)
+    (gen-instruction! output instruction)))
+
+(define (gen-load! output frame r tempname)
+  (cgframe-touch! frame tempname)
+  (let ((n (entry.slotnum (cgframe-lookup frame tempname))))
+    (gen! output $load r n)))
+
+(define (gen-stack! output frame tempname)
+  (cgframe-touch! frame tempname)
+  (let ((n (entry.slotnum (cgframe-lookup frame tempname))))
+    (gen! output $stack n)))
+
+; Returns a temporary name.
+; Temporaries are compared using EQ?, so the use of small
+; exact integers as temporary names is implementation-dependent.
+
+(define (init-temps)
+  (set! newtemp-counter 5000))
+
+(define (newtemp)
+  (set! newtemp-counter
+        (+ newtemp-counter 1))
+  newtemp-counter)
+
+(define newtemp-counter 5000)
+
+(define (newtemps n)
+  (if (zero? n)
+      '()
+      (cons (newtemp)
+            (newtemps (- n 1)))))
+
+; New representation of
+; Register environments.
+; Represented as a list of three items:
+;     an exact integer, one more than the highest index of a live register
+;     a mutable vector with *nregs* elements of the form
+;         #f        (the register is dead)
+;         #t        (the register is live)
+;         v         (the register contains variable v)
+;         t         (the register contains temporary variable t)
+;     a mutable vector of booleans: true if the register might be stale
+
+(define (cgreg-makeregs n v1 v2) (list n v1 v2))
+
+(define (cgreg-liveregs regs)
+  (car regs))
+
+(define (cgreg-contents regs)
+  (cadr regs))
+
+(define (cgreg-stale regs)
+  (caddr regs))
+
+(define (cgreg-liveregs-set! regs n)
+  (set-car! regs n)
+  regs)
+
+(define (cgreg-initial)
+  (let ((v1 (make-vector *nregs* #f))
+        (v2 (make-vector *nregs* #f)))
+    (cgreg-makeregs 0 v1 v2)))
+
+(define (cgreg-copy regs)
+  (let* ((newregs (cgreg-initial))
+         (v1a (cgreg-contents regs))
+         (v2a (cgreg-stale regs))
+         (v1 (cgreg-contents newregs))
+         (v2 (cgreg-stale newregs))
+         (n (vector-length v1a)))
+    (cgreg-liveregs-set! newregs (cgreg-liveregs regs))
+    (do ((i 0 (+ i 1)))
+        ((= i n)
+         newregs)
+        (vector-set! v1 i (vector-ref v1a i))
+        (vector-set! v2 i (vector-ref v2a i)))))
+
+(define (cgreg-tos regs)
+  (- (cgreg-liveregs regs) 1))
+
+(define (cgreg-live regs r)
+  (if (eq? r 'result)
+      (cgreg-tos regs)
+      (max r (cgreg-tos regs))))
+
+(define (cgreg-vars regs)
+  (let ((m (cgreg-liveregs regs))
+        (v (cgreg-contents regs)))
+    (do ((i (- m 1) (- i 1))
+         (vars '()
+               (cons (vector-ref v i)
+                     vars)))
+        ((< i 0)
+         vars))))
+
+(define (cgreg-bind! regs r t)
+  (let ((m (cgreg-liveregs regs))
+        (v (cgreg-contents regs)))
+    (vector-set! v r t)
+    (if (>= r m)
+        (cgreg-liveregs-set! regs (+ r 1)))))
+
+(define (cgreg-bindregs! regs vars)
+  (do ((m (cgreg-liveregs regs) (+ m 1))
+       (v (cgreg-contents regs))
+       (vars vars (cdr vars)))
+      ((null? vars)
+       (cgreg-liveregs-set! regs m)
+       regs)
+      (vector-set! v m (car vars))))
+
+(define (cgreg-rename! regs alist)
+  (do ((i (- (cgreg-liveregs regs) 1) (- i 1))
+       (v (cgreg-contents regs)))
+      ((negative? i))
+      (let ((var (vector-ref v i)))
+        (if var
+            (let ((probe (assv var alist)))
+              (if probe
+                  (vector-set! v i (cdr probe))))))))
+
+(define (cgreg-release! regs r)
+  (let ((m (cgreg-liveregs regs))
+        (v (cgreg-contents regs)))
+    (vector-set! v r #f)
+    (vector-set! (cgreg-stale regs) r #t)
+    (if (= r (- m 1))
+        (do ((m r (- m 1)))
+            ((or (negative? m)
+                 (vector-ref v m))
+             (cgreg-liveregs-set! regs (+ m 1)))))))
+
+(define (cgreg-release-except! regs vars)
+  (do ((i (- (cgreg-liveregs regs) 1) (- i 1))
+       (v (cgreg-contents regs)))
+      ((negative? i))
+      (let ((var (vector-ref v i)))
+        (if (and var (not (memq var vars)))
+            (cgreg-release! regs i)))))
+
+(define (cgreg-clear! regs)
+  (let ((m (cgreg-liveregs regs))
+        (v1 (cgreg-contents regs))
+        (v2 (cgreg-stale regs)))
+    (do ((r 0 (+ r 1)))
+        ((= r m)
+         (cgreg-liveregs-set! regs 0))
+        (vector-set! v1 r #f)
+        (vector-set! v2 r #t))))
+
+(define (cgreg-lookup regs var)
+  (let ((m (cgreg-liveregs regs))
+        (v (cgreg-contents regs)))
+    (define (loop i)
+      (cond ((< i 0)
+             #f)
+            ((eq? var (vector-ref v i))
+             (list var 'register i '(object)))
+            (else
+             (loop (- i 1)))))
+    (loop (- m 1))))
+
+(define (cgreg-lookup-reg regs r)
+  (let ((m (cgreg-liveregs regs))
+        (v (cgreg-contents regs)))
+    (if (<= m r)
+        #f
+        (vector-ref v r))))
+
+(define (cgreg-join! regs1 regs2)
+  (let ((m1 (cgreg-liveregs regs1))
+        (m2 (cgreg-liveregs regs2))
+        (v1 (cgreg-contents regs1))
+        (v2 (cgreg-contents regs2))
+        (stale1 (cgreg-stale regs1)))
+    (do ((i (- (max m1 m2) 1) (- i 1)))
+        ((< i 0)
+         (cgreg-liveregs-set! regs1 (min m1 m2)))
+        (let ((x1 (vector-ref v1 i))
+              (x2 (vector-ref v2 i)))
+          (cond ((eq? x1 x2)
+                 #t)
+                ((not x1)
+                 (if x2
+                     (vector-set! stale1 i #t)))
+                (else
+                 (vector-set! v1 i #f)
+                 (vector-set! stale1 i #t)))))))
+
+; New representation of
+; Stack-frame environments.
+; Represented as a three-element list.
+;
+; Its car is a list whose car is a list of slot entries, each
+; of the form
+;    (v n instruction stale)
+; where
+;    v is the name of a variable or temporary,
+;    n is #f or a slot number,
+;    instruction is a possibly phantom store or setstk instruction
+;       that stores v into slot n, and
+;    stale is a list of stale slot entries, each of the form
+;          (#t . n)
+;       or (#f . -1)
+;       where slot n had been allocated, initialized, and released
+;       before the store or setstk instruction was generated.
+; Slot entries are updated by side effect.
+;
+; Its cadr is the list of currently stale slots.
+;
+; Its caddr is a list of variables that are free in the continuation,
+; or #f if that information is unknown.
+; This information allows a direct-style code generator to know when
+; a slot becomes stale.
+;
+; Its cadddr is the size of the stack frame, which can be
+; increased but not decreased.  The cdddr of the stack frame
+; environment is shared with the save instruction that
+; created the frame.  What a horrible crock!
+
+; This stuff is private to the implementation of stack-frame
+; environments.
+
+(define cgframe:slots car)
+(define cgframe:stale cadr)
+(define cgframe:livevars caddr)
+(define cgframe:slot.name car)
+(define cgframe:slot.offset cadr)
+(define cgframe:slot.instruction caddr)
+(define cgframe:slot.stale cadddr)
+
+(define cgframe:slots-set! set-car!)
+(define (cgframe:stale-set! frame stale)
+  (set-car! (cdr frame) stale))
+(define (cgframe:livevars-set! frame vars)
+  (set-car! (cddr frame) vars))
+
+(define cgframe:slot.name-set! set-car!)
+
+(define (cgframe:slot.offset-set! entry n)
+  (let ((instruction (caddr entry)))
+    (if (or (not (eq? #f (cadr entry)))
+            (not (eq? $nop (car instruction))))
+        (error "Compiler bug: cgframe" entry)
+        (begin
+         (set-car! (cdr entry) n)
+         (set-car! instruction (cadr instruction))
+         (set-cdr! instruction (cddr instruction))
+         (if (eq? $setstk (car instruction))
+             (set-car! (cdr instruction) n)
+             (set-car! (cddr instruction) n))))))
+
+; Reserves a slot offset that was unused where the instruction
+; of the slot entry was generated, and returns that offset.
+
+(define (cgframe:unused-slot frame entry)
+  (let* ((stale (cgframe:slot.stale entry))
+         (probe (assq #t stale)))
+    (if probe
+        (let ((n (cdr probe)))
+          (if (zero? n)
+              (cgframe-used! frame))
+          (set-car! probe #f)
+          n)
+        (let* ((cell (cgframe-size-cell frame))
+               (n (+ 1 (car cell))))
+          (set-car! cell n)
+          (if (zero? n)
+              (cgframe:unused-slot frame entry)
+              n)))))
+
+; Public entry points.
+
+; The runtime system requires slot 0 of a frame to contain
+; a closure whose code pointer contains the return address
+; of the frame.
+; To prevent slot 0 from being used for some other purpose,
+; we rely on a complex trick:  Slot 0 is initially stale.
+; Gen-save! generates a store instruction for register 0,
+; with slot 0 as the only stale slot for that instruction;
+; then gen-save! clears the frame's set of stale slots, which
+; prevents other store instructions from using slot 0.
+
+(define (cgframe-initial)
+  (list '()
+        (list (cons #t 0))
+        '#f
+        -1))
+
+(define cgframe-livevars cgframe:livevars)
+(define cgframe-livevars-set! cgframe:livevars-set!)
+
+(define (cgframe-size-cell frame)
+  (cdddr frame))
+
+(define (cgframe-size frame)
+  (car (cgframe-size-cell frame)))
+
+(define (cgframe-used! frame)
+  (if (negative? (cgframe-size frame))
+      (set-car! (cgframe-size-cell frame) 0)))
+
+; Called only by gen-store!, gen-setstk!
+
+(define (cgframe-bind! frame var instruction)
+  (cgframe:slots-set! frame
+                      (cons (list var #f instruction (cgframe:stale frame))
+                            (cgframe:slots frame))))
+
+; Called only by gen-load!, gen-stack!
+
+(define (cgframe-touch! frame var)
+  (let ((entry (assq var (cgframe:slots frame))))
+    (if entry
+        (let ((n (cgframe:slot.offset entry)))
+          (if (eq? #f n)
+              (let ((n (cgframe:unused-slot frame entry)))
+                (cgframe:slot.offset-set! entry n))))
+        (error "Compiler bug: cgframe-touch!" frame var))))
+
+(define (cgframe-rename! frame alist)
+  (for-each (lambda (entry)
+              (let ((probe (assq (cgframe:slot.name entry) alist)))
+                (if probe
+                    (cgframe:slot.name-set! entry (cdr probe)))))
+            (cgframe:slots frame)))
+
+(define (cgframe-release! frame var)
+  (let* ((slots (cgframe:slots frame))
+         (entry (assq var slots)))
+    (if entry
+        (begin (cgframe:slots-set! frame (remq entry slots))
+               (let ((n (cgframe:slot.offset entry)))
+                 (if (and (not (eq? #f n))
+                          (not (zero? n)))
+                     (cgframe:stale-set!
+                      frame
+                      (cons (cons #t n)
+                            (cgframe:stale frame)))))))))
+
+(define (cgframe-release-except! frame vars)
+  (let loop ((slots (reverse (cgframe:slots frame)))
+             (newslots '())
+             (stale (cgframe:stale frame)))
+    (if (null? slots)
+        (begin (cgframe:slots-set! frame newslots)
+               (cgframe:stale-set! frame stale))
+        (let ((slot (car slots)))
+          (if (memq (cgframe:slot.name slot) vars)
+              (loop (cdr slots)
+                    (cons slot newslots)
+                    stale)
+              (let ((n (cgframe:slot.offset slot)))
+                (cond ((eq? n #f)
+                       (loop (cdr slots)
+                             newslots
+                             stale))
+                      ((zero? n)
+                       (loop (cdr slots)
+                             (cons slot newslots)
+                             stale))
+                      (else
+                       (loop (cdr slots)
+                             newslots
+                             (cons (cons #t n) stale))))))))))
+
+(define (cgframe-lookup frame var)
+  (let ((entry (assq var (cgframe:slots frame))))
+    (if entry
+        (let ((n (cgframe:slot.offset entry)))
+          (if (eq? #f n)
+              (cgframe-touch! frame var))
+          (list var 'frame (cgframe:slot.offset entry) '(object)))
+        #f)))
+
+(define (cgframe-spilled? frame var)
+  (let ((entry (assq var (cgframe:slots frame))))
+    (if entry
+        (let ((n (cgframe:slot.offset entry)))
+          (not (eq? #f n)))
+        #f)))
+
+; For a conditional expression, the then and else parts must be
+; evaluated using separate copies of the frame environment,
+; and those copies must be resolved at the join point.  The
+; nature of the resolution depends upon whether the conditional
+; expression is in a tail position.
+;
+; Critical invariant:
+; Any store instructions that are generated within either arm of the
+; conditional involve variables and temporaries that are local to the
+; conditional.
+;
+; If the conditional expression is in a tail position, then a slot
+; that is stale after the test can be allocated independently by the
+; two arms of the conditional.  If the conditional expression is in a
+; non-tail position, then the slot can be allocated independently
+; provided it is not a candidate destination for any previous emitted
+; store instruction.
+
+(define (cgframe-copy frame)
+  (cons (car frame)
+        (cons (cadr frame)
+              (cons (caddr frame)
+                    (cdddr frame)))))
+
+(define (cgframe-update-stale! frame)
+  (let* ((n (cgframe-size frame))
+         (v (make-vector (+ 1 n) #t))
+         (stale (cgframe:stale frame)))
+    (for-each (lambda (x)
+                (if (car x)
+                    (let ((i (cdr x)))
+                      (if (<= i n)
+                          (vector-set! v i #f)))))
+              stale)
+    (for-each (lambda (slot)
+                (let ((offset (cgframe:slot.offset slot)))
+                  (if offset
+                      (vector-set! v offset #f)
+                      (for-each (lambda (stale)
+                                  (if (car stale)
+                                      (let ((i (cdr stale)))
+                                        (if (< i n)
+                                            (vector-set! v i #f)))))
+                                (cgframe:slot.stale slot)))))
+              (cgframe:slots frame))
+    (do ((i n (- i 1))
+         (stale (filter car stale)
+                (if (vector-ref v i)
+                    (cons (cons #t i) stale)
+                    stale)))
+        ((<= i 0)
+         (cgframe:stale-set! frame stale)))))
+
+(define (cgframe-join! frame1 frame2)
+  (let* ((slots1 (cgframe:slots frame1))
+         (slots2 (cgframe:slots frame2))
+         (slots (intersection slots1 slots2))
+         (deadslots (append (difference slots1 slots)
+                            (difference slots2 slots)))
+         (deadoffsets (make-set
+                       (filter (lambda (x) (not (eq? x #f)))
+                               (map cgframe:slot.offset deadslots))))
+         (stale1 (cgframe:stale frame1))
+         (stale2 (cgframe:stale frame2))
+         (stale (intersection stale1 stale2))
+         (stale (append (map (lambda (n) (cons #t n))
+                             deadoffsets)
+                        stale)))
+    (cgframe:slots-set! frame1 slots)
+    (cgframe:stale-set! frame1 stale)))
+
+; Environments.
+;
+; Each identifier has one of the following kinds of entry.
+;
+;    (<name> register   <number>                (object))
+;    (<name> frame      <slot>                  (object))
+;    (<name> lexical    <rib>    <offset>       (object))
+;    (<name> procedure  <rib>    <label>        (object))
+;    (<name> integrable <arity>  <op>     <imm> (object))
+;    (<name> global                             (object))
+;
+; Implementation.
+;
+; An environment is represented as a list of the form
+;
+;    ((<entry> ...)                          ; lexical rib
+;     ...)
+;
+; where each <entry> has one of the forms
+;
+;    (<name> lexical <offset> (object))
+;    (<name> procedure <rib> <label> (object))
+;    (<name> integrable <arity> <op> <imm> (object))
+
+(define entry.name car)
+(define entry.kind cadr)
+(define entry.rib caddr)
+(define entry.offset cadddr)
+(define entry.label cadddr)
+(define entry.regnum caddr)
+(define entry.slotnum caddr)
+(define entry.arity caddr)
+(define entry.op cadddr)
+(define (entry.imm entry) (car (cddddr entry)))
+
+(define (cgenv-initial integrable)
+  (list (map (lambda (x)
+               (list (car x)
+                     'integrable
+                     (cadr x)
+                     (caddr x)
+                     (cadddr x)
+                     '(object)))
+             integrable)))
+
+(define (cgenv-lookup env id)
+  (define (loop ribs m)
+    (if (null? ribs)
+        (cons id '(global (object)))
+        (let ((x (assq id (car ribs))))
+          (if x
+              (case (cadr x)
+                ((lexical)
+                 (cons id
+                       (cons (cadr x)
+                             (cons m (cddr x)))))
+                ((procedure)
+                 (cons id
+                       (cons (cadr x)
+                             (cons m (cddr x)))))
+                ((integrable)
+                 (if (integrate-usual-procedures)
+                     x
+                     (loop '() m)))
+                (else ???))
+              (loop (cdr ribs) (+ m 1))))))
+  (loop env 0))
+
+(define (cgenv-extend env vars procs)
+  (cons (do ((n 0 (+ n 1))
+             (vars vars (cdr vars))
+             (rib (map (lambda (id)
+                         (list id 'procedure (make-label) '(object)))
+                       procs)
+                  (cons (list (car vars) 'lexical n '(object)) rib)))
+            ((null? vars) rib))
+        env))
+
+(define (cgenv-bindprocs env procs)
+  (cons (append (map (lambda (id)
+                       (list id 'procedure (make-label) '(object)))
+                     procs)
+                (car env))
+        (cdr env)))
+
+(define (var-lookup var regs frame env)
+  (or (cgreg-lookup regs var)
+      (cgframe-lookup frame var)
+      (cgenv-lookup env var)))
+
+; Compositions.
+
+(define compile
+  (lambda (x)
+    (pass4 (pass3 (pass2 (pass1 x))) $usual-integrable-procedures$)))
+
+(define compile-block
+  (lambda (x)
+    (pass4 (pass3 (pass2 (pass1-block x))) $usual-integrable-procedures$)))
+
+; For testing.
+
+(define foo
+  (lambda (x)
+    (pretty-print (compile x))))
+
+; Find the smallest number of registers such that
+; adding more registers does not affect the code
+; generated for x (from 4 to 32 registers).
+
+(define (minregs x)
+  (define (defregs R)
+    (set! *nregs* R)
+    (set! *lastreg* (- *nregs* 1))
+    (set! *fullregs* (quotient *nregs* 2)))
+  (defregs 32)
+  (let ((code (assemble (compile x))))
+    (define (binary-search m1 m2)
+      (if (= (+ m1 1) m2)
+          m2
+          (let ((midpt (quotient (+ m1 m2) 2)))
+            (defregs midpt)
+            (if (equal? code (assemble (compile x)))
+                (binary-search m1 midpt)
+                (binary-search midpt m2)))))
+    (defregs 4)
+    (let ((newcode (assemble (compile x))))
+      (if (equal? code newcode)
+          4
+          (binary-search 4 32)))))
+
+; Minimums:
+;  browse     10
+;  triangle    5
+;  traverse   10
+;  destruct    6
+;  puzzle      8,8,10,7
+;  tak         6
+;  fft        28   (changing the named lets to macros didn't matter)
+; Copyright 1991 William Clinger
+;
+; $Id: twobit.sch,v 1.3 1999/08/23 19:14:26 lth Exp $
+;
+; 7 June 1999.
+;
+; Fourth pass of the Twobit compiler:
+;   code generation for the MacScheme machine.
+;
+; This pass operates on input expressions described by the
+; following grammar and the invariants that follow it.
+;
+; "X ..." means zero or more occurrences of X.
+;
+; L  -->  (lambda (I_1 ...)
+;           (begin D ...)
+;           (quote (R F G <decls> <doc>)
+;           E)
+;      |  (lambda (I_1 ... . I_rest)
+;           (begin D ...)
+;           (quote (R F G <decls> <doc>))
+;           E)
+; D  -->  (define I L)
+; E  -->  (quote K)                        ; constants
+;      |  (begin I)                        ; variable references
+;      |  L                                ; lambda expressions
+;      |  (E0 E1 ...)                      ; calls
+;      |  (set! I E)                       ; assignments
+;      |  (if E0 E1 E2)                    ; conditionals
+;      |  (begin E0 E1 E2 ...)             ; sequential expressions
+; I  -->  <identifier>
+;
+; R  -->  ((I <references> <assignments> <calls>) ...)
+; F  -->  (I ...)
+; G  -->  (I ...)
+;
+; Invariants that hold for the input
+;   *  There are no assignments except to global variables.
+;   *  If I is declared by an internal definition, then the right hand
+;      side of the internal definition is a lambda expression and I
+;      is referenced only in the procedure position of a call.
+;   *  Every procedure defined by an internal definition takes a
+;      fixed number of arguments.
+;   *  Every call to a procedure defined by an internal definition
+;      passes the correct number of arguments.
+;   *  For each lambda expression, the associated F is a list of all
+;      the identifiers that occur free in the body of that lambda
+;      expression, and possibly a few extra identifiers that were
+;      once free but have been removed by optimization.
+;   *  For each lambda expression, the associated G is a subset of F
+;      that contains every identifier that occurs free within some
+;      inner lambda expression that escapes, and possibly a few that
+;      don't.  (Assignment-elimination does not calculate G exactly.)
+;   *  Variables named IGNORED are neither referenced nor assigned.
+;   *  Any lambda expression that is declared to be in A-normal form
+;      really is in A-normal form.
+;
+; 
+; Stack frames are created by "save" instructions.
+; A save instruction is generated
+; 
+;     *  at the beginning of each lambda body
+;     *  at the beginning of the code for each arm of a conditional,
+;        provided:
+;          the conditional is in a tail position
+;          the frames that were allocated by the save instructions
+;            that dominate the arms of the conditional have not been
+;            used (those save instructions will be eliminated during
+;            assembly)
+;
+; The operand of a save instruction, and of its matching pop instructions,
+; increases automatically as frame slots are allocated.
+; 
+; The code generated to return from a procedure is
+; 
+;         pop     n
+;         return
+; 
+; The code generated for a tail call is
+; 
+;         pop     n
+;         invoke  ...
+;
+; Invariant:  When the code generator reserves an argument register
+; to hold a value, that value is named, and is stored into the current
+; stack frame.  These store instructions are eliminated during assembly
+; unless there is a matching load instruction.  If all of the instructions
+; that store into a stack frame are eliminated, then the stack frame
+; itself is eliminated.
+; Exception:  An argument register may be used without naming or storing
+; its value provided the register is not in use and no expressions are
+; evaluated while it contains the unnamed and unstored value.
+
+
+(define (pass4 exp integrable)
+  (init-labels)
+  (init-temps)
+  (let ((output (make-assembly-stream))
+        (frame (cgframe-initial))
+        (regs (cgreg-initial))
+        (t0 (newtemp)))
+    (assembly-stream-info! output (make-hashtable equal-hash assoc))
+    (cgreg-bind! regs 0 t0)
+    (gen-save! output frame t0)
+    (cg0 output
+         exp
+         'result
+         regs
+         frame
+         (cgenv-initial integrable)
+         #t)
+    (pass4-code output)))
+
+(define (pass4-code output)
+  (hashtable-for-each (lambda (situation label)
+                        (cg-trap output situation label))
+                      (assembly-stream-info output))
+  (assembly-stream-code output))
+
+; Given:
+;    an assembly stream into which instructions should be emitted
+;    an expression
+;    the target register
+;      ('result, a register number, or '#f; tail position implies 'result)
+;    a register environment [cgreg]
+;    a stack-frame environment [cgframe]
+;    a compile-time environment [cgenv]
+;    a flag indicating whether the expression is in tail position
+; Returns:
+;    the target register ('result or a register number)
+; Side effects:
+;    may change the register and stack-frame environments
+;    may increase the size of the stack frame, which changes previously
+;       emitted instructions
+;    writes instructions to the assembly stream
+
+(define (cg0 output exp target regs frame env tail?)
+  (case (car exp)
+    ((quote)    (gen! output $const (constant.value exp))
+                (if tail?
+                    (begin (gen-pop! output frame)
+                           (gen! output $return)
+                           'result)
+                    (cg-move output frame regs 'result target)))
+    ((lambda)   (cg-lambda output exp regs frame env)
+                (if tail?
+                    (begin (gen-pop! output frame)
+                           (gen! output $return)
+                           'result)
+                    (cg-move output frame regs 'result target)))
+    ((set!)     (cg0 output (assignment.rhs exp) 'result regs frame env #f)
+                (cg-assignment-result output exp target regs frame env tail?))
+    ((if)       (cg-if output exp target regs frame env tail?))
+    ((begin)    (if (variable? exp)
+                    (cg-variable output exp target regs frame env tail?)
+                    (cg-sequential output exp target regs frame env tail?)))
+    (else       (cg-call output exp target regs frame env tail?))))
+
+; Lambda expressions that evaluate to closures.
+; This is hard because the MacScheme machine's lambda instruction
+; closes over the values that are in argument registers 0 through r
+; (where r can be larger than *nregs*).
+; The set of free variables is calculated and then sorted to minimize
+; register shuffling.
+;
+; Returns: nothing.
+
+(define (cg-lambda output exp regs frame env)
+  (let* ((args (lambda.args exp))
+         (vars (make-null-terminated args))
+         (free (difference (lambda.F exp) vars))
+         (free (cg-sort-vars free regs frame env))
+         (newenv (cgenv-extend env (cons #t free) '()))
+         (newoutput (make-assembly-stream)))
+    (assembly-stream-info! newoutput (make-hashtable equal-hash assoc))
+    (gen! newoutput $.proc)
+    (if (list? args)
+        (gen! newoutput $args= (length args))
+        (gen! newoutput $args>= (- (length vars) 1)))
+    (cg-known-lambda newoutput exp newenv)
+    (cg-eval-vars output free regs frame env)
+    ; FIXME
+    '
+    (if (not (ignore-space-leaks))
+        ; FIXME: Is this the right constant?
+        (begin (gen! output $const #f)
+               (gen! output $setreg 0)))
+    (gen! output
+          $lambda
+          (pass4-code newoutput)
+          (length free)
+          (lambda.doc exp))
+    ; FIXME
+    '
+    (if (not (ignore-space-leaks))
+        ; FIXME: This load forces a stack frame to be allocated.
+        (gen-load! output frame 0 (cgreg-lookup-reg regs 0)))))
+
+; Given a list of free variables, filters out the ones that
+; need to be copied into a closure, and sorts them into an order
+; that reduces register shuffling.  Returns a sorted version of
+; the list in which the first element (element 0) should go
+; into register 1, the second into register 2, and so on.
+
+(define (cg-sort-vars free regs frame env)
+  (let* ((free (filter (lambda (var)
+                         (case (entry.kind
+                                (var-lookup var regs frame env))
+                           ((register frame)
+                            #t)
+                           ((lexical)
+                            (not (ignore-space-leaks)))
+                           (else #f)))
+                       free))
+         (n (length free))
+         (m (min n (- *nregs* 1)))
+         (vec (make-vector m #f)))
+    (define (loop1 free free-notregister)
+      (if (null? free)
+          (loop2 0 free-notregister)
+          (let* ((var (car free))
+                 (entry (cgreg-lookup regs var)))
+            (if entry
+                (let ((r (entry.regnum entry)))
+                  (if (<= r n)
+                      (begin (vector-set! vec (- r 1) var)
+                             (loop1 (cdr free)
+                                    free-notregister))
+                      (loop1 (cdr free)
+                             (cons var free-notregister))))
+                (loop1 (cdr free)
+                       (cons var free-notregister))))))
+    (define (loop2 i free)
+      (cond ((null? free)
+             (vector->list vec))
+            ((= i m)
+             (append (vector->list vec) free))
+            ((vector-ref vec i)
+             (loop2 (+ i 1) free))
+            (else
+             (vector-set! vec i (car free))
+             (loop2 (+ i 1) (cdr free)))))
+    (loop1 free '())))
+
+; Fetches the given list of free variables into the corresponding
+; registers in preparation for a $lambda or $lexes instruction.
+
+(define (cg-eval-vars output free regs frame env)
+  (let ((n (length free))
+        (R-1 (- *nregs* 1)))
+    (if (>= n R-1)
+        (begin (gen! output $const '())
+               (gen! output $setreg R-1)
+               (cgreg-release! regs R-1)))
+    (do ((r n (- r 1))
+         (vars (reverse free) (cdr vars)))
+        ((zero? r))
+        (let* ((v (car vars))
+               (entry (var-lookup v regs frame env)))
+          (case (entry.kind entry)
+            ((register)
+             (let ((r1 (entry.regnum entry)))
+               (if (not (eqv? r r1))
+                   (if (< r R-1)
+                       (begin (gen! output $movereg r1 r)
+                              (cgreg-bind! regs r v))
+                       (gen! output $reg r1 v)))))
+            ((frame)
+             (if (< r R-1)
+                 (begin (gen-load! output frame r v)
+                        (cgreg-bind! regs r v))
+                 (gen-stack! output frame v)))
+            ((lexical)
+             (gen! output $lexical
+                          (entry.rib entry)
+                          (entry.offset entry)
+                          v)
+             (if (< r R-1)
+                 (begin (gen! output $setreg r)
+                        (cgreg-bind! regs r v)
+                        (gen-store! output frame r v))))
+            (else
+             (error "Bug in cg-close-lambda")))
+          (if (>= r R-1)
+              (begin (gen! output $op2 $cons R-1)
+                     (gen! output $setreg R-1)))))))
+
+; Lambda expressions that appear on the rhs of a definition are
+; compiled here.  They don't need an args= instruction at their head.
+;
+; Returns: nothing.
+
+(define (cg-known-lambda output exp env)
+  (let* ((vars (make-null-terminated (lambda.args exp)))
+         (regs (cgreg-initial))
+         (frame (cgframe-initial))
+         (t0 (newtemp)))
+    (if (member A-normal-form-declaration (lambda.decls exp))
+        (cgframe-livevars-set! frame '()))
+    (cgreg-bind! regs 0 t0)
+    (gen-save! output frame t0)
+    (do ((r 1 (+ r 1))
+         (vars vars (cdr vars)))
+        ((or (null? vars)
+             (= r *lastreg*))
+         (if (not (null? vars))
+             (begin (gen! output $movereg *lastreg* 1)
+                    (cgreg-release! regs 1)
+                    (do ((vars vars (cdr vars)))
+                        ((null? vars))
+                        (gen! output $reg 1)
+                        (gen! output $op1 $car:pair)
+                        (gen-setstk! output frame (car vars))
+                        (gen! output $reg 1)
+                        (gen! output $op1 $cdr:pair)
+                        (gen! output $setreg 1)))))
+        (cgreg-bind! regs r (car vars))
+        (gen-store! output frame r (car vars)))
+    (cg-body output
+             exp
+             'result
+             regs
+             frame
+             env
+             #t)))
+
+; Compiles a let or lambda body.
+; The arguments of the lambda expression L are already in
+; registers or the stack frame, as specified by regs and frame.
+;
+; The problem here is that the free variables of an internal
+; definition must be in a heap-allocated environment, so any
+; such variables in registers must be copied to the heap.
+;
+; Returns: destination register.
+
+(define (cg-body output L target regs frame env tail?)
+  (let* ((exp (lambda.body L))
+         (defs (lambda.defs L))
+         (free (apply-union
+                      (map (lambda (def)
+                             (let ((L (def.rhs def)))
+                               (difference (lambda.F L)
+                                           (lambda.args L))))
+                           defs))))
+    (cond ((or (null? defs) (constant? exp) (variable? exp))
+           (cg0 output exp target regs frame env tail?))
+          ((lambda? exp)
+           (let* ((free (cg-sort-vars
+                         (union free
+                                (difference
+                                 (lambda.F exp)
+                                 (make-null-terminated (lambda.args exp))))
+                         regs frame env))
+                  (newenv1 (cgenv-extend env
+                                         (cons #t free)
+                                         (map def.lhs defs)))
+                  (args (lambda.args exp))
+                  (vars (make-null-terminated args))
+                  (newoutput (make-assembly-stream)))
+             (assembly-stream-info! newoutput (make-hashtable equal-hash assoc))
+             (gen! newoutput $.proc)
+             (if (list? args)
+                 (gen! newoutput $args= (length args))
+                 (gen! newoutput $args>= (- (length vars) 1)))
+             (cg-known-lambda newoutput exp newenv1)
+             (cg-defs newoutput defs newenv1)
+             (cg-eval-vars output free regs frame env)
+             (gen! output
+                   $lambda
+                   (pass4-code newoutput)
+                   (length free)
+                   (lambda.doc exp))
+             (if tail?
+                 (begin (gen-pop! output frame)
+                        (gen! output $return)
+                        'result)
+                 (cg-move output frame regs 'result target))))
+          ((every? (lambda (def)
+                     (every? (lambda (v)
+                               (case (entry.kind
+                                      (var-lookup v regs frame env))
+                                 ((register frame) #f)
+                                 (else #t)))
+                             (let ((Ldef (def.rhs def)))
+                               (difference (lambda.F Ldef)
+                                           (lambda.args Ldef)))))
+                   defs)
+           (let* ((newenv (cgenv-bindprocs env (map def.lhs defs)))
+                  (L (make-label))
+                  (r (cg0 output exp target regs frame newenv tail?)))
+             (if (not tail?)
+                 (gen! output $skip L (cgreg-live regs r)))
+             (cg-defs output defs newenv)
+             (if (not tail?)
+                 (gen! output $.label L))
+             r))
+          (else
+           (let ((free (cg-sort-vars free regs frame env)))
+             (cg-eval-vars output free regs frame env)
+             ; FIXME: Have to restore it too!
+             '
+             (if (not (ignore-space-leaks))
+                 ; FIXME: Is this constant the right one?
+                 (begin (gen! output $const #f)
+                        (gen! output $setreg 0)))
+             (let ((t0 (cgreg-lookup-reg regs 0))
+                   (t1 (newtemp))
+                   (newenv (cgenv-extend env
+                                         (cons #t free)
+                                         (map def.lhs defs)))
+                   (L (make-label)))
+               (gen! output $lexes (length free) free)
+               (gen! output $setreg 0)
+               (cgreg-bind! regs 0 t1)
+               (if tail?
+                   (begin (cgframe-release! frame t0)
+                          (gen-store! output frame 0 t1)
+                          (cg0 output exp 'result regs frame newenv #t)
+                          (cg-defs output defs newenv)
+                          'result)
+                   (begin (gen-store! output frame 0 t1)
+                          (cg0 output exp 'result regs frame newenv #f)
+                          (gen! output $skip L (cgreg-tos regs))
+                          (cg-defs output defs newenv)
+                          (gen! output $.label L)
+                          (gen-load! output frame 0 t0)
+                          (cgreg-bind! regs 0 t0)
+                          (cgframe-release! frame t1)
+                          (cg-move output frame regs 'result target)))))))))
+
+(define (cg-defs output defs env)
+  (for-each (lambda (def)
+              (gen! output $.align 4)
+              (gen! output $.label
+                           (entry.label
+                            (cgenv-lookup env (def.lhs def))))
+              (gen! output $.proc)
+              (gen! output $.proc-doc (lambda.doc (def.rhs def)))
+              (cg-known-lambda output
+                               (def.rhs def)
+                               env))
+            defs))
+
+; The right hand side has already been evaluated into the result register.
+
+(define (cg-assignment-result output exp target regs frame env tail?)
+  (gen! output $setglbl (assignment.lhs exp))
+  (if tail?
+      (begin (gen-pop! output frame)
+             (gen! output $return)
+             'result)
+      (cg-move output frame regs 'result target)))
+
+(define (cg-if output exp target regs frame env tail?)
+  ; The test can be a constant, because it is awkward
+  ; to remove constant tests from an A-normal form.
+  (if (constant? (if.test exp))
+      (cg0 output
+           (if (constant.value (if.test exp))
+               (if.then exp)
+               (if.else exp))
+           target regs frame env tail?)
+      (begin
+       (cg0 output (if.test exp) 'result regs frame env #f)
+       (cg-if-result output exp target regs frame env tail?))))
+
+; The test expression has already been evaluated into the result register.
+
+(define (cg-if-result output exp target regs frame env tail?)
+  (let ((L1 (make-label))
+        (L2 (make-label)))
+    (gen! output $branchf L1 (cgreg-tos regs))
+    (let* ((regs2 (cgreg-copy regs))
+           (frame1 (if (and tail?
+                            (negative? (cgframe-size frame)))
+                       (cgframe-initial)
+                       frame))
+           (frame2 (if (eq? frame frame1)
+                       (cgframe-copy frame1)
+                       (cgframe-initial)))
+           (t0 (cgreg-lookup-reg regs 0)))
+      (if (not (eq? frame frame1))
+          (let ((live (cgframe-livevars frame)))
+            (cgframe-livevars-set! frame1 live)
+            (cgframe-livevars-set! frame2 live)
+            (gen-save! output frame1 t0)
+            (cg-saveregs output regs frame1)))
+      (let ((r (cg0 output (if.then exp) target regs frame1 env tail?)))
+        (if (not tail?)
+            (gen! output $skip L2 (cgreg-live regs r)))
+        (gen! output $.label L1)
+        (if (not (eq? frame frame1))
+            (begin (gen-save! output frame2 t0)
+                   (cg-saveregs output regs2 frame2))
+            (cgframe-update-stale! frame2))
+        (cg0 output (if.else exp) r regs2 frame2 env tail?)
+        (if (not tail?)
+            (begin (gen! output $.label L2)
+                   (cgreg-join! regs regs2)
+                   (cgframe-join! frame1 frame2)))
+        (if (and (not target)
+                 (not (eq? r 'result))
+                 (not (cgreg-lookup-reg regs r)))
+            (cg-move output frame regs r 'result)
+            r)))))
+
+(define (cg-variable output exp target regs frame env tail?)
+  (define (return id)
+    (if tail?
+        (begin (gen-pop! output frame)
+               (gen! output $return)
+               'result)
+        (if (and target
+                 (not (eq? 'result target)))
+            (begin (gen! output $setreg target)
+                   (cgreg-bind! regs target id)
+                   (gen-store! output frame target id)
+                   target)
+            'result)))
+  ; Same as return, but doesn't emit a store instruction.
+  (define (return-nostore id)
+    (if tail?
+        (begin (gen-pop! output frame)
+               (gen! output $return)
+               'result)
+        (if (and target
+                 (not (eq? 'result target)))
+            (begin (gen! output $setreg target)
+                   (cgreg-bind! regs target id)
+                   target)
+            'result)))
+  (let* ((id (variable.name exp))
+         (entry (var-lookup id regs frame env)))
+    (case (entry.kind entry)
+      ((global integrable)
+       (gen! output $global id)
+       (return (newtemp)))
+      ((lexical)
+       (let ((m (entry.rib entry))
+             (n (entry.offset entry)))
+         (gen! output $lexical m n id)
+         (if (or (zero? m)
+                 (negative? (cgframe-size frame)))
+             (return-nostore id)
+             (return id))))
+      ((procedure) (error "Bug in cg-variable" exp))
+      ((register)
+       (let ((r (entry.regnum entry)))
+         (if (or tail?
+                 (and target (not (eqv? target r))))
+             (begin (gen! output $reg (entry.regnum entry) id)
+                    (return-nostore id))
+             r)))
+      ((frame)
+       (cond ((eq? target 'result)
+              (gen-stack! output frame id)
+              (return id))
+             (target
+              ; Must be non-tail.
+              (gen-load! output frame target id)
+              (cgreg-bind! regs target id)
+              target)
+             (else
+              ; Must be non-tail.
+              (let ((r (choose-register regs frame)))
+                (gen-load! output frame r id)
+                (cgreg-bind! regs r id)
+                r))))
+      (else (error "Bug in cg-variable" exp)))))
+
+(define (cg-sequential output exp target regs frame env tail?)
+  (cg-sequential-loop output (begin.exprs exp) target regs frame env tail?))
+
+(define (cg-sequential-loop output exprs target regs frame env tail?)
+  (cond ((null? exprs)
+         (gen! output $const unspecified)
+         (if tail?
+             (begin (gen-pop! output frame)
+                    (gen! output $return)
+                    'result)
+             (cg-move output frame regs 'result target)))
+        ((null? (cdr exprs))
+         (cg0 output (car exprs) target regs frame env tail?))
+        (else (cg0 output (car exprs) #f regs frame env #f)
+              (cg-sequential-loop output
+                                  (cdr exprs)
+                                  target regs frame env tail?))))
+
+(define (cg-saveregs output regs frame)
+  (do ((i 1 (+ i 1))
+       (vars (cdr (cgreg-vars regs)) (cdr vars)))
+      ((null? vars))
+      (let ((t (car vars)))
+        (if t
+            (gen-store! output frame i t)))))
+
+(define (cg-move output frame regs src dst)
+  (define (bind dst)
+    (let ((temp (newtemp)))
+      (cgreg-bind! regs dst temp)
+      (gen-store! output frame dst temp)
+      dst))
+  (cond ((not dst)
+         src)
+        ((eqv? src dst)
+         dst)
+        ((eq? dst 'result)
+         (gen! output $reg src)
+         dst)
+        ((eq? src 'result)
+         (gen! output $setreg dst)
+         (bind dst))
+        ((and (not (zero? src))
+              (not (zero? dst)))
+         (gen! output $movereg src dst)
+         (bind dst))
+        (else
+         (gen! output $reg src)
+         (gen! output $setreg dst)
+         (bind dst))))
+
+; On-the-fly register allocator.
+; Tries to allocate:
+;    a hardware register that isn't being used
+;    a hardware register whose contents have already been spilled
+;    a software register that isn't being used, unless a stack
+;       frame has already been created, in which case it is better to use
+;    a hardware register that is in use and hasn't yet been spilled
+;
+; All else equal, it is better to allocate a higher-numbered register
+; because the lower-numbered registers are targets when arguments
+; are being evaluated.
+;
+; Invariant:  Every register that is returned by this allocator
+; is either not in use or has been spilled.
+
+(define (choose-register regs frame)
+  (car (choose-registers regs frame 1)))
+
+(define (choose-registers regs frame n)
+  
+  ; Find unused hardware registers.
+  (define (loop1 i n good)
+    (cond ((zero? n)
+           good)
+          ((zero? i)
+           (if (negative? (cgframe-size frame))
+               (hardcase)
+               (loop2 (- *nhwregs* 1) n good)))
+          (else
+           (if (cgreg-lookup-reg regs i)
+               (loop1 (- i 1) n good)
+               (loop1 (- i 1)
+                      (- n 1)
+                      (cons i good))))))
+  
+  ; Find already spilled hardware registers.
+  (define (loop2 i n good)
+    (cond ((zero? n)
+           good)
+          ((zero? i)
+           (hardcase))
+          (else
+           (let ((t (cgreg-lookup-reg regs i)))
+             (if (and t (cgframe-spilled? frame t))
+                 (loop2 (- i 1)
+                        (- n 1)
+                        (cons i good))
+                 (loop2 (- i 1) n good))))))
+  
+  ; This is ridiculous.
+  ; Fortunately the correctness of the compiler is independent
+  ; of the predicate used for this sort.
+  
+  (define (hardcase)
+    (let* ((frame-exists? (not (negative? (cgframe-size frame))))
+           (stufftosort
+            (map (lambda (r)
+                   (let* ((t (cgreg-lookup-reg regs r))
+                          (spilled?
+                           (and t
+                                (cgframe-spilled? frame t))))
+                     (list r t spilled?)))
+                 (cdr (iota *nregs*))))
+           (registers
+            (twobit-sort
+             (lambda (x1 x2)
+               (let ((r1 (car x1))
+                     (r2 (car x2))
+                     (t1 (cadr x1))
+                     (t2 (cadr x2)))
+                 (cond ((< r1 *nhwregs*)
+                        (cond ((not t1)                     #t)
+                              ((< r2 *nhwregs*)
+                               (cond ((not t2)              #f)
+                                     ((caddr x1)            #t)
+                                     ((caddr x2)            #f)
+                                     (else                  #t)))
+                              (frame-exists?                #t)
+                              (t2                           #t)
+                              (else                         #f)))
+                       ((< r2 *nhwregs*)
+                        (cond (frame-exists?                #f)
+                              (t1                           #f)
+                              (t2                           #t)
+                              (else                         #f)))
+                       (t1
+                        (if (and (caddr x1)
+                                 t2
+                                 (not (caddr x2)))
+                            #t
+                            #f))
+                       (else #t))))
+             stufftosort)))
+      ; FIXME: What was this for?
+      '
+      (for-each (lambda (register)
+                  (let ((t (cadr register))
+                        (spilled? (caddr register)))
+                    (if (and t (not spilled?))
+                        (cgframe-touch! frame t))))
+                registers)
+      (do ((sorted (map car registers) (cdr sorted))
+           (rs '() (cons (car sorted) rs))
+           (n n (- n 1)))
+          ((zero? n)
+           (reverse rs)))))
+  
+  (if (< n *nregs*)
+      (loop1 (- *nhwregs* 1) n '())
+      (error (string-append "Compiler bug: can't allocate "
+                            (number->string n)
+                            " registers on this target."))))
+; Copyright 1991 William Clinger
+;
+; $Id: twobit.sch,v 1.3 1999/08/23 19:14:26 lth Exp $
+;
+; 21 May 1999.
+
+; Procedure calls.
+
+(define (cg-call output exp target regs frame env tail?)
+  (let ((proc (call.proc exp)))
+    (cond ((and (lambda? proc)
+                (list? (lambda.args proc)))
+           (cg-let output exp target regs frame env tail?))
+          ((not (variable? proc))
+           (cg-unknown-call output exp target regs frame env tail?))
+          (else (let ((entry
+                       (var-lookup (variable.name proc) regs frame env)))
+                  (case (entry.kind entry)
+                    ((global lexical frame register)
+                     (cg-unknown-call output
+                                      exp
+                                      target regs frame env tail?))
+                    ((integrable)
+                     (cg-integrable-call output
+                                         exp
+                                         target regs frame env tail?))
+                    ((procedure)
+                     (cg-known-call output
+                                    exp
+                                    target regs frame env tail?))
+                    (else (error "Bug in cg-call" exp))))))))
+
+(define (cg-unknown-call output exp target regs frame env tail?)
+  (let* ((proc (call.proc exp))
+         (args (call.args exp))
+         (n (length args))
+         (L (make-label)))
+    (cond ((>= (+ n 1) *lastreg*)
+           (cg-big-call output exp target regs frame env tail?))
+          (else
+           (let ((r0 (cgreg-lookup-reg regs 0)))
+             (if (variable? proc)
+                 (let ((entry (cgreg-lookup regs (variable.name proc))))
+                   (if (and entry
+                            (<= (entry.regnum entry) n))
+                       (begin (cg-arguments output
+                                            (iota1 (+ n 1))
+                                            (append args (list proc))
+                                            regs frame env)
+                              (gen! output $reg (+ n 1)))
+                       (begin (cg-arguments output
+                                            (iota1 n)
+                                            args
+                                            regs frame env)
+                              (cg0 output proc 'result regs frame env #f)))
+                   (if tail?
+                       (gen-pop! output frame)
+                       (begin (cgframe-used! frame)
+                              (gen! output $setrtn L)))
+                   (gen! output $invoke n))
+                 (begin (cg-arguments output
+                                      (iota1 (+ n 1))
+                                      (append args (list proc))
+                                      regs frame env)
+                        (gen! output $reg (+ n 1))
+                        (if tail?
+                            (gen-pop! output frame)
+                            (begin (cgframe-used! frame)
+                                   (gen! output $setrtn L)))
+                        (gen! output $invoke n)))
+             (if tail?
+                 'result
+                 (begin (gen! output $.align 4)
+                        (gen! output $.label L)
+                        (gen! output $.cont)
+                        (cgreg-clear! regs)
+                        (cgreg-bind! regs 0 r0)
+                        (gen-load! output frame 0 r0)
+                        (cg-move output frame regs 'result target))))))))
+
+(define (cg-known-call output exp target regs frame env tail?)
+  (let* ((args (call.args exp))
+         (n (length args))
+         (L (make-label)))
+    (cond ((>= (+ n 1) *lastreg*)
+           (cg-big-call output exp target regs frame env tail?))
+          (else
+           (let ((r0 (cgreg-lookup-reg regs 0)))
+             (cg-arguments output (iota1 n) args regs frame env)
+             (if tail?
+                 (gen-pop! output frame)
+                 (begin (cgframe-used! frame)
+                        (gen! output $setrtn L)))
+             (let* ((entry (cgenv-lookup env (variable.name (call.proc exp))))
+                    (label (entry.label entry))
+                    (m (entry.rib entry)))
+               (if (zero? m)
+                   (gen! output $branch label n)
+                   (gen! output $jump m label n)))
+             (if tail?
+                 'result
+                 (begin (gen! output $.align 4)
+                        (gen! output $.label L)
+                        (gen! output $.cont)
+                        (cgreg-clear! regs)
+                        (cgreg-bind! regs 0 r0)
+                        (gen-load! output frame 0 r0)
+                        (cg-move output frame regs 'result target))))))))
+
+; Any call can be compiled as follows, even if there are no free registers.
+;
+; Let T0, T1, ..., Tn be newly allocated stack temporaries.
+;
+;     <arg0>
+;     setstk  T0
+;     <arg1>             -|
+;     setstk  T1          |
+;     ...                 |- evaluate args into stack frame
+;     <argn>              |
+;     setstk  Tn         -|
+;     const   ()
+;     setreg  R-1
+;     stack   Tn         -|
+;     op2     cons,R-1    |
+;     setreg  R-1         |
+;     ...                 |- cons up overflow args
+;     stack   T_{R-1}     |
+;     op2     cons,R-1    |
+;     setreg  R-1        -|
+;     stack   T_{R-2}      -|
+;     setreg  R-2           |
+;     ...                   |- pop remaining args into registers
+;     stack   T1            |
+;     setreg  1            -|
+;     stack   T0
+;     invoke  n
+
+(define (cg-big-call output exp target regs frame env tail?)
+  (let* ((proc (call.proc exp))
+         (args (call.args exp))
+         (n (length args))
+         (argslots (newtemps n))
+         (procslot (newtemp))
+         (r0 (cgreg-lookup-reg regs 0))
+         (R-1 (- *nregs* 1))
+         (entry (if (variable? proc)
+                    (let ((entry
+                           (var-lookup (variable.name proc)
+                                       regs frame env)))
+                      (if (eq? (entry.kind entry) 'procedure)
+                          entry
+                          #f))
+                    #f))
+         (L (make-label)))
+    (if (not entry)
+        (begin
+         (cg0 output proc 'result regs frame env #f)
+         (gen-setstk! output frame procslot)))
+    (for-each (lambda (arg argslot)
+                (cg0 output arg 'result regs frame env #f)
+                (gen-setstk! output frame argslot))
+              args
+              argslots)
+    (cgreg-clear! regs)
+    (gen! output $const '())
+    (gen! output $setreg R-1)
+    (do ((i n (- i 1))
+         (slots (reverse argslots) (cdr slots)))
+        ((zero? i))
+        (if (< i R-1)
+            (gen-load! output frame i (car slots))
+            (begin (gen-stack! output frame (car slots))
+                   (gen! output $op2 $cons R-1)
+                   (gen! output $setreg R-1))))
+    (if (not entry)
+        (gen-stack! output frame procslot))
+    (if tail?
+        (gen-pop! output frame)
+        (begin (cgframe-used! frame)
+               (gen! output $setrtn L)))
+    (if entry
+        (let ((label (entry.label entry))
+              (m (entry.rib entry)))
+          (if (zero? m)
+              (gen! output $branch label n)
+              (gen! output $jump m label n)))
+        (gen! output $invoke n))
+    (if tail?
+        'result
+        (begin (gen! output $.align 4)
+               (gen! output $.label L)
+               (gen! output $.cont)
+               (cgreg-clear! regs) ; redundant, see above
+               (cgreg-bind! regs 0 r0)
+               (gen-load! output frame 0 r0)
+               (cg-move output frame regs 'result target)))))
+
+(define (cg-integrable-call output exp target regs frame env tail?)
+  (let ((args (call.args exp))
+        (entry (var-lookup (variable.name (call.proc exp)) regs frame env)))
+    (if (= (entry.arity entry) (length args))
+        (begin (case (entry.arity entry)
+                 ((0) (gen! output $op1 (entry.op entry)))
+                 ((1) (cg0 output (car args) 'result regs frame env #f)
+                      (gen! output $op1 (entry.op entry)))
+                 ((2) (cg-integrable-call2 output
+                                           entry
+                                           args
+                                           regs frame env))
+                 ((3) (cg-integrable-call3 output
+                                           entry
+                                           args
+                                           regs frame env))
+                 (else (error "Bug detected by cg-integrable-call"
+                              (make-readable exp))))
+               (if tail?
+                   (begin (gen-pop! output frame)
+                          (gen! output $return)
+                          'result)
+                   (cg-move output frame regs 'result target)))
+        (if (negative? (entry.arity entry))
+            (cg-special output exp target regs frame env tail?)
+            (error "Wrong number of arguments to integrable procedure"
+                   (make-readable exp))))))
+
+(define (cg-integrable-call2 output entry args regs frame env)
+  (let ((op (entry.op entry)))
+    (if (and (entry.imm entry)
+             (constant? (cadr args))
+             ((entry.imm entry) (constant.value (cadr args))))
+        (begin (cg0 output (car args) 'result regs frame env #f)
+               (gen! output $op2imm
+                            op
+                            (constant.value (cadr args))))
+        (let* ((reg2 (cg0 output (cadr args) #f regs frame env #f))
+               (r2 (choose-register regs frame))
+               (t2 (if (eq? reg2 'result)
+                       (let ((t2 (newtemp)))
+                         (gen! output $setreg r2)
+                         (cgreg-bind! regs r2 t2)
+                         (gen-store! output frame r2 t2)
+                         t2)
+                       (cgreg-lookup-reg regs reg2))))
+          (cg0 output (car args) 'result regs frame env #f)
+          (let* ((r2 (or (let ((entry (cgreg-lookup regs t2)))
+                           (if entry
+                               (entry.regnum entry)
+                               #f))
+                         (let ((r2 (choose-register regs frame)))
+                           (cgreg-bind! regs r2 t2)
+                           (gen-load! output frame r2 t2)
+                           r2))))
+            (gen! output $op2 (entry.op entry) r2)
+            (if (eq? reg2 'result)
+                (begin (cgreg-release! regs r2)
+                       (cgframe-release! frame t2)))))))
+  'result)
+
+(define (cg-integrable-call3 output entry args regs frame env)
+  (let* ((reg2 (cg0 output (cadr args) #f regs frame env #f))
+         (r2 (choose-register regs frame))
+         (t2 (if (eq? reg2 'result)
+                 (let ((t2 (newtemp)))
+                   (gen! output $setreg r2)
+                   (cgreg-bind! regs r2 t2)
+                   (gen-store! output frame r2 t2)
+                   t2)
+                 (cgreg-lookup-reg regs reg2)))
+         (reg3 (cg0 output (caddr args) #f regs frame env #f))
+         (spillregs (choose-registers regs frame 2))
+         (t3 (if (eq? reg3 'result)
+                 (let ((t3 (newtemp))
+                       (r3 (if (eq? t2 (cgreg-lookup-reg
+                                        regs (car spillregs)))
+                               (cadr spillregs)
+                               (car spillregs))))
+                   (gen! output $setreg r3)
+                   (cgreg-bind! regs r3 t3)
+                   (gen-store! output frame r3 t3)
+                   t3)
+                 (cgreg-lookup-reg regs reg3))))
+    (cg0 output (car args) 'result regs frame env #f)
+    (let* ((spillregs (choose-registers regs frame 2))
+           (r2 (or (let ((entry (cgreg-lookup regs t2)))
+                           (if entry
+                               (entry.regnum entry)
+                               #f))
+                   (let ((r2 (car spillregs)))
+                     (cgreg-bind! regs r2 t2)
+                     (gen-load! output frame r2 t2)
+                     r2)))
+           (r3 (or (let ((entry (cgreg-lookup regs t3)))
+                           (if entry
+                               (entry.regnum entry)
+                               #f))
+                   (let ((r3 (if (eq? r2 (car spillregs))
+                                 (cadr spillregs)
+                                 (car spillregs))))
+                     (cgreg-bind! regs r3 t3)
+                     (gen-load! output frame r3 t3)
+                     r3))))
+      (gen! output $op3 (entry.op entry) r2 r3)
+      (if (eq? reg2 'result)
+          (begin (cgreg-release! regs r2)
+                 (cgframe-release! frame t2)))
+      (if (eq? reg3 'result)
+          (begin (cgreg-release! regs r3)
+                 (cgframe-release! frame t3)))))
+  'result)
+
+; Given a short list of expressions that can be evaluated in any order,
+; evaluates the first into the result register and the others into any
+; register, and returns an ordered list of the registers that contain
+; the arguments that follow the first.
+; The number of expressions must be less than the number of argument
+; registers.
+
+(define (cg-primop-args output args regs frame env)
+  
+  ; Given a list of expressions to evaluate, a list of variables
+  ; and temporary names for arguments that have already been
+  ; evaluated, in reverse order, and a mask of booleans that
+  ; indicate which temporaries should be released before returning,
+  ; returns the correct result.
+  
+  (define (eval-loop args temps mask)
+    (if (null? args)
+        (eval-first-into-result temps mask)
+        (let ((reg (cg0 output (car args) #f regs frame env #f)))
+          (if (eq? reg 'result)
+              (let* ((r (choose-register regs frame))
+                     (t (newtemp)))
+                (gen! output $setreg r)
+                (cgreg-bind! regs r t)
+                (gen-store! output frame r t)
+                (eval-loop (cdr args)
+                           (cons t temps)
+                           (cons #t mask)))
+              (eval-loop (cdr args)
+                         (cons (cgreg-lookup-reg regs reg) temps)
+                         (cons #f mask))))))
+  
+  (define (eval-first-into-result temps mask)
+    (cg0 output (car args) 'result regs frame env #f)
+    (finish-loop (choose-registers regs frame (length temps))
+                 temps
+                 mask
+                 '()))
+  
+  ; Given a sufficient number of disjoint registers, a list of
+  ; variable and temporary names that may need to be loaded into
+  ; registers, a mask of booleans that indicates which temporaries
+  ; should be released, and a list of registers in forward order,
+  ; returns the correct result.
+  
+  (define (finish-loop disjoint temps mask registers)
+    (if (null? temps)
+        registers
+        (let* ((t (car temps))
+               (entry (cgreg-lookup regs t)))
+          (if entry
+              (let ((r (entry.regnum entry)))
+                (if (car mask)
+                    (begin (cgreg-release! regs r)
+                           (cgframe-release! frame t)))
+                (finish-loop disjoint
+                             (cdr temps)
+                             (cdr mask)
+                             (cons r registers)))
+              (let ((r (car disjoint)))
+                (if (memv r registers)
+                    (finish-loop (cdr disjoint) temps mask registers)
+                    (begin (gen-load! output frame r t)
+                           (cgreg-bind! regs r t)
+                           (if (car mask)
+                               (begin (cgreg-release! regs r)
+                                      (cgframe-release! frame t)))
+                           (finish-loop disjoint
+                                        (cdr temps)
+                                        (cdr mask)
+                                        (cons r registers)))))))))
+  
+  (if (< (length args) *nregs*)
+      (eval-loop (cdr args) '() '())
+      (error "Bug detected by cg-primop-args" args)))
+
+
+; Parallel assignment.
+
+; Given a list of target registers, a list of expressions, and a
+; compile-time environment, generates code to evaluate the expressions
+; into the registers.
+;
+; Argument evaluation proceeds as follows:
+;
+; 1.  Evaluate all but one of the complicated arguments.
+; 2.  Evaluate remaining arguments.
+; 3.  Load spilled arguments from stack.
+
+(define (cg-arguments output targets args regs frame env)
+  
+  ; Sorts the args and their targets into complicated and
+  ; uncomplicated args and targets.
+  ; Then it calls evalargs.
+  
+  (define (sortargs targets args targets1 args1 targets2 args2)
+    (if (null? args)
+        (evalargs targets1 args1 targets2 args2)
+        (let ((target (car targets))
+              (arg (car args))
+              (targets (cdr targets))
+              (args (cdr args)))
+          (if (complicated? arg env)
+              (sortargs targets
+                        args
+                        (cons target targets1)
+                        (cons arg args1)
+                        targets2
+                        args2)
+              (sortargs targets
+                        args
+                        targets1
+                        args1
+                        (cons target targets2)
+                        (cons arg args2))))))
+  
+  ; Given the complicated args1 and their targets1,
+  ; and the uncomplicated args2 and their targets2,
+  ; evaluates all the arguments into their target registers.
+  
+  (define (evalargs targets1 args1 targets2 args2)
+    (let* ((temps1 (newtemps (length targets1)))
+           (temps2 (newtemps (length targets2))))
+      (if (not (null? args1))
+          (for-each (lambda (arg temp)
+                      (cg0 output arg 'result regs frame env #f)
+                      (gen-setstk! output frame temp))
+                    (cdr args1)
+                    (cdr temps1)))
+      (if (not (null? args1))
+          (evalargs0 (cons (car targets1) targets2)
+                     (cons (car args1) args2)
+                     (cons (car temps1) temps2))
+          (evalargs0 targets2 args2 temps2))
+      (for-each (lambda (r t)
+                  (let ((temp (cgreg-lookup-reg regs r)))
+                    (if (not (eq? temp t))
+                        (let ((entry (var-lookup t regs frame env)))
+                          (case (entry.kind entry)
+                            ((register)
+                             (gen! output $movereg (entry.regnum entry) r))
+                            ((frame)
+                             (gen-load! output frame r t)))
+                          (cgreg-bind! regs r t)))
+                    (cgframe-release! frame t)))
+                (append targets1 targets2)
+                (append temps1 temps2))))
+  
+  (define (evalargs0 targets args temps)
+    (if (not (null? targets))
+        (let ((para (let* ((regvars (map (lambda (reg)
+                                           (cgreg-lookup-reg regs reg))
+                                         targets)))
+                      (parallel-assignment targets
+                                           (map cons regvars targets)
+                                           args))))
+          (if para
+              (let ((targets para)
+                    (args (cg-permute args targets para))
+                    (temps (cg-permute temps targets para)))
+                (for-each (lambda (arg r t)
+                            (cg0 output arg r regs frame env #f)
+                            (cgreg-bind! regs r t)
+                            (gen-store! output frame r t))
+                          args
+                          para
+                          temps))
+              (let ((r (choose-register regs frame))
+                    (t (car temps)))
+                (cg0 output (car args) r regs frame env #f)
+                (cgreg-bind! regs r t)
+                (gen-store! output frame r t)
+                (evalargs0 (cdr targets)
+                           (cdr args)
+                           (cdr temps)))))))
+  
+  (if (parallel-assignment-optimization)
+      (sortargs (reverse targets) (reverse args) '() '() '() '())
+      (cg-evalargs output targets args regs frame env)))
+
+; Left-to-right evaluation of arguments directly into targets.
+
+(define (cg-evalargs output targets args regs frame env)
+  (let ((temps (newtemps (length targets))))
+    (for-each (lambda (arg r t)
+                (cg0 output arg r regs frame env #f)
+                (cgreg-bind! regs r t)
+                (gen-store! output frame r t))
+              args
+              targets
+              temps)
+    (for-each (lambda (r t)
+                (let ((temp (cgreg-lookup-reg regs r)))
+                  (if (not (eq? temp t))
+                      (begin (gen-load! output frame r t)
+                             (cgreg-bind! regs r t)))
+                  (cgframe-release! frame t)))
+              targets
+              temps)))
+
+; For heuristic use only.
+; An expression is complicated unless it can probably be evaluated
+; without saving and restoring any registers, even if it occurs in
+; a non-tail position.
+
+(define (complicated? exp env)
+  (case (car exp)
+    ((quote)    #f)
+    ((lambda)   #t)
+    ((set!)     (complicated? (assignment.rhs exp) env))
+    ((if)       (or (complicated? (if.test exp) env)
+                    (complicated? (if.then exp) env)
+                    (complicated? (if.else exp) env)))
+    ((begin)    (if (variable? exp)
+                    #f
+                    (some? (lambda (exp)
+                             (complicated? exp env))
+                           (begin.exprs exp))))
+    (else       (let ((proc (call.proc exp)))
+                  (if (and (variable? proc)
+                           (let ((entry
+                                  (cgenv-lookup env (variable.name proc))))
+                             (eq? (entry.kind entry) 'integrable)))
+                      (some? (lambda (exp)
+                               (complicated? exp env))
+                             (call.args exp))
+                      #t)))))
+
+; Returns a permutation of the src list, permuted the same way the
+; key list was permuted to obtain newkey.
+
+(define (cg-permute src key newkey)
+  (let ((alist (map cons key (iota (length key)))))
+    (do ((newkey newkey (cdr newkey))
+         (dest '()
+               (cons (list-ref src (cdr (assq (car newkey) alist)))
+                     dest)))
+        ((null? newkey) (reverse dest)))))
+
+; Given a list of register numbers,
+; an association list with entries of the form (name . regnum) giving
+; the variable names by which those registers are known in code,
+; and a list of expressions giving new values for those registers,
+; returns an ordering of the register assignments that implements a
+; parallel assignment if one can be found, otherwise returns #f.
+
+(define parallel-assignment
+ (lambda (regnums alist exps)
+   (if (null? regnums)
+       #t
+       (let ((x (toposort (dependency-graph regnums alist exps))))
+         (if x (reverse x) #f)))))
+
+(define dependency-graph
+ (lambda (regnums alist exps)
+   (let ((names (map car alist)))
+     (do ((regnums regnums (cdr regnums))
+          (exps exps (cdr exps))
+          (l '() (cons (cons (car regnums)
+                             (map (lambda (var) (cdr (assq var alist)))
+                                  (intersection (freevariables (car exps))
+                                                names)))
+                       l)))
+         ((null? regnums) l)))))
+
+; Given a nonempty graph represented as a list of the form
+;     ((node1 . <list of nodes that node1 is less than or equal to>)
+;      (node2 . <list of nodes that node2 is less than or equal to>)
+;      ...)
+; returns a topological sort of the nodes if one can be found,
+; otherwise returns #f.
+
+(define toposort
+ (lambda (graph)
+   (cond ((null? (cdr graph)) (list (caar graph)))
+         (else (toposort2 graph '())))))
+
+(define toposort2
+ (lambda (totry tried)
+   (cond ((null? totry) #f)
+         ((or (null? (cdr (car totry)))
+              (and (null? (cddr (car totry)))
+                   (eq? (cadr (car totry))
+                        (car (car totry)))))
+          (if (and (null? (cdr totry)) (null? tried))
+              (list (caar totry))
+              (let* ((node (caar totry))
+                     (x (toposort2 (map (lambda (y)
+                                          (cons (car y) (remove node (cdr y))))
+                                        (append (cdr totry) tried))
+                                   '())))
+                (if x
+                    (cons node x)
+                    #f))))
+         (else (toposort2 (cdr totry) (cons (car totry) tried))))))
+
+(define iota (lambda (n) (iota2 n '())))
+
+(define iota1 (lambda (n) (cdr (iota2 (+ n 1) '()))))
+
+(define iota2
+ (lambda (n l)
+   (if (zero? n)
+       l
+       (let ((n (- n 1)))
+         (iota2 n (cons n l))))))
+
+(define (freevariables exp)
+  (freevars2 exp '()))
+
+(define (freevars2 exp env)
+  (cond ((symbol? exp)
+         (if (memq exp env) '() (list exp)))
+        ((not (pair? exp)) '())
+        (else (let ((keyword (car exp)))
+                (cond ((eq? keyword 'quote) '())
+                      ((eq? keyword 'lambda)
+                       (let ((env (append (make-null-terminated (cadr exp))
+                                          env)))
+                         (apply-union
+                          (map (lambda (x) (freevars2 x env))
+                               (cddr exp)))))
+                      ((memq keyword '(if set! begin))
+                       (apply-union
+                        (map (lambda (x) (freevars2 x env))
+                             (cdr exp))))
+                      (else (apply-union
+                             (map (lambda (x) (freevars2 x env))
+                                  exp))))))))
+; Copyright 1991 William Clinger (cg-let and cg-let-body)
+; Copyright 1999 William Clinger (everything else)
+;
+; 10 June 1999.
+
+; Generates code for a let expression.
+
+(define (cg-let output exp target regs frame env tail?)
+  (let* ((proc (call.proc exp))
+         (vars (lambda.args proc))
+         (n (length vars))
+         (free (lambda.F proc))
+         (live (cgframe-livevars frame)))
+    (if (and (null? (lambda.defs proc))
+             (= n 1))
+        (cg-let1 output exp target regs frame env tail?)
+        (let* ((args (call.args exp))
+               (temps (newtemps n))
+               (alist (map cons temps vars)))
+          (for-each (lambda (arg t)
+                      (let ((r (choose-register regs frame)))
+                        (cg0 output arg r regs frame env #f)
+                        (cgreg-bind! regs r t)
+                        (gen-store! output frame r t)))
+                    args
+                    temps)
+          (cgreg-rename! regs alist)
+          (cgframe-rename! frame alist)
+          (cg-let-release! free live regs frame tail?)
+          (cg-let-body output proc target regs frame env tail?)))))
+
+; Given the free variables of a let body, and the variables that are
+; live after the let expression, and the usual regs, frame, and tail?
+; arguments, releases any registers and frame slots that don't need
+; to be preserved across the body of the let.
+
+(define (cg-let-release! free live regs frame tail?)
+  ; The tail case is easy because there are no live temporaries,
+  ; and there are no free variables in the context.
+  ; The non-tail case assumes A-normal form.
+  (cond (tail?
+         (let ((keepers (cons (cgreg-lookup-reg regs 0) free)))
+           (cgreg-release-except! regs keepers)
+           (cgframe-release-except! frame keepers)))
+        (live
+         (let ((keepers (cons (cgreg-lookup-reg regs 0)
+                              (union live free))))
+           (cgreg-release-except! regs keepers)
+           (cgframe-release-except! frame keepers)))))
+
+; Generates code for the body of a let.
+
+(define (cg-let-body output L target regs frame env tail?)
+  (let ((vars (lambda.args L))
+        (free (lambda.F L))
+        (live (cgframe-livevars frame)))
+    (let ((r (cg-body output L target regs frame env tail?)))
+      (for-each (lambda (v)
+                  (let ((entry (cgreg-lookup regs v)))
+                    (if entry
+                        (cgreg-release! regs (entry.regnum entry)))
+                    (cgframe-release! frame v)))
+                vars)
+      (if (and (not target)
+               (not (eq? r 'result))
+               (not (cgreg-lookup-reg regs r)))
+          (cg-move output frame regs r 'result)
+          r))))
+
+; Generates code for a let expression that binds exactly one variable
+; and has no internal definitions.  These let expressions are very
+; common in A-normal form, and there are many special cases with
+; respect to register allocation and order of evaluation.
+
+(define (cg-let1 output exp target regs frame env tail?)
+  (let* ((proc (call.proc exp))
+         (v (car (lambda.args proc)))
+         (arg (car (call.args exp)))
+         (free (lambda.F proc))
+         (live (cgframe-livevars frame))
+         (body (lambda.body proc)))
+    
+    (define (evaluate-into-register r)
+      (cg0 output arg r regs frame env #f)
+      (cgreg-bind! regs r v)
+      (gen-store! output frame r v)
+      r)
+    
+    (define (release-registers!)
+      (cgframe-livevars-set! frame live)
+      (cg-let-release! free live regs frame tail?))
+    
+    (define (finish)
+      (release-registers!)
+      (cg-let-body output proc target regs frame env tail?))
+    
+    (if live
+        (cgframe-livevars-set! frame (union live free)))
+    
+    (cond ((assq v *regnames*)
+           (evaluate-into-register (cdr (assq v *regnames*)))
+           (finish))
+          ((not (memq v free))
+           (cg0 output arg #f regs frame env #f)
+           (finish))
+          (live
+           (cg0 output arg 'result regs frame env #f)
+           (release-registers!)
+           (cg-let1-result output exp target regs frame env tail?))
+          (else
+           (evaluate-into-register (choose-register regs frame))
+           (finish)))))
+
+; Given a let expression that binds one variable whose value has already
+; been evaluated into the result register, generates code for the rest
+; of the let expression.
+; The main difficulty is an unfortunate interaction between A-normal
+; form and the MacScheme machine architecture:  We don't want to move
+; a value from the result register into a general register if it has
+; only one use and can remain in the result register until that use.
+
+(define (cg-let1-result output exp target regs frame env tail?)
+  (let* ((proc (call.proc exp))
+         (v (car (lambda.args proc)))
+         (free (lambda.F proc))
+         (live (cgframe-livevars frame))
+         (body (lambda.body proc))
+         (pattern (cg-let-used-once v body)))
+    
+    (define (move-to-register r)
+      (gen! output $setreg r)
+      (cgreg-bind! regs r v)
+      (gen-store! output frame r v)
+      r)
+    
+    (define (release-registers!)
+      (cgframe-livevars-set! frame live)
+      (cg-let-release! free live regs frame tail?))
+    
+    ; FIXME: The live variables must be correct in the frame.
+    
+    (case pattern
+      ((if)
+       (cg-if-result output body target regs frame env tail?))
+      ((let-if)
+       (if live
+           (cgframe-livevars-set! frame (union live free)))
+       (cg-if-result output
+                     (car (call.args body))
+                     'result regs frame env #f)
+       (release-registers!)
+       (cg-let1-result output body target regs frame env tail?))
+      ((set!)
+       (cg-assignment-result output
+                             body target regs frame env tail?))
+      ((let-set!)
+       (cg-assignment-result output
+                             (car (call.args body))
+                             'result regs frame env #f)
+       (cg-let1-result output body target regs frame env tail?))
+      ((primop)
+       (cg-primop-result output body target regs frame env tail?))
+      ((let-primop)
+       (cg-primop-result output
+                         (car (call.args body))
+                         'result regs frame env #f)
+       (cg-let1-result output body target regs frame env tail?))
+      ; FIXME
+      ((_called)
+       (cg-call-result output body target regs frame env tail?))
+      ; FIXME
+      ((_let-called)
+       (cg-call-result output
+                       (car (call.args body))
+                       'result regs frame env #f)
+       (cg-let1-result output body target regs frame env tail?))
+      (else
+       ; FIXME:  The first case was handled by cg-let1.
+       (cond ((assq v *regnames*)
+              (move-to-register (cdr (assq v *regnames*))))
+             ((memq v free)
+              (move-to-register (choose-register regs frame))))
+       (cg-let-body output proc target regs frame env tail?)))))
+
+; Given a call to a primop whose first argument has already been
+; evaluated into the result register and whose remaining arguments
+; consist of constants and variable references, generates code for
+; the call.
+
+(define (cg-primop-result output exp target regs frame env tail?)
+  (let ((args (call.args exp))
+        (entry (var-lookup (variable.name (call.proc exp)) regs frame env)))
+    (if (= (entry.arity entry) (length args))
+        (begin (case (entry.arity entry)
+                 ((0) (gen! output $op1 (entry.op entry)))
+                 ((1) (gen! output $op1 (entry.op entry)))
+                 ((2) (cg-primop2-result! output entry args regs frame env))
+                 ((3) (let ((rs (cg-result-args output args regs frame env)))
+                        (gen! output
+                              $op3 (entry.op entry) (car rs) (cadr rs))))
+                 (else (error "Bug detected by cg-primop-result"
+                              (make-readable exp))))
+               (if tail?
+                   (begin (gen-pop! output frame)
+                          (gen! output $return)
+                          'result)
+                   (cg-move output frame regs 'result target)))
+        (if (negative? (entry.arity entry))
+            (cg-special-result output exp target regs frame env tail?)
+            (error "Wrong number of arguments to integrable procedure"
+                   (make-readable exp))))))
+
+(define (cg-primop2-result! output entry args regs frame env)
+  (let ((op (entry.op entry))
+        (arg2 (cadr args)))
+    (if (and (constant? arg2)
+             (entry.imm entry)
+             ((entry.imm entry) (constant.value arg2)))
+        (gen! output $op2imm op (constant.value arg2))
+        (let ((rs (cg-result-args output args regs frame env)))
+          (gen! output $op2 op (car rs))))))
+
+; Given a short list of constants and variable references to be evaluated
+; into arbitrary general registers, evaluates them into registers without
+; disturbing the result register and returns a list of the registers into
+; which they are evaluated.  Before returning, any registers that were
+; allocated by this routine are released.
+
+(define (cg-result-args output args regs frame env)
+  
+  ; Given a list of unevaluated arguments,
+  ; a longer list of disjoint general registers,
+  ; the register that holds the first evaluated argument,
+  ; a list of registers in reverse order that hold other arguments,
+  ; and a list of registers to be released afterwards,
+  ; generates code to evaluate the arguments,
+  ; deallocates any registers that were evaluated to hold the arguments,
+  ; and returns the list of registers that contain the arguments.
+  
+  (define (loop args registers rr rs temps)
+    (if (null? args)
+        (begin (if (not (eq? rr 'result))
+                   (gen! output $reg rr))
+               (for-each (lambda (r) (cgreg-release! regs r))
+                         temps)
+               (reverse rs))
+        (let ((arg (car args)))
+          (cond ((constant? arg)
+                 (let ((r (car registers)))
+                   (gen! output $const/setreg (constant.value arg) r)
+                   (cgreg-bind! regs r #t)
+                   (loop (cdr args)
+                         (cdr registers)
+                         rr
+                         (cons r rs)
+                         (cons r temps))))
+                ((variable? arg)
+                 (let* ((id (variable.name arg))
+                        (entry (var-lookup id regs frame env)))
+                   (case (entry.kind entry)
+                     ((global integrable)
+                      (if (eq? rr 'result)
+                          (save-result! args registers rr rs temps)
+                          (let ((r (car registers)))
+                            (gen! output $global id)
+                            (gen! output $setreg r)
+                            (cgreg-bind! regs r id)
+                            (loop (cdr args)
+                                  (cdr registers)
+                                  rr
+                                  (cons r rs)
+                                  (cons r temps)))))
+                     ((lexical)
+                      (if (eq? rr 'result)
+                          (save-result! args registers rr rs temps)
+                          (let ((m (entry.rib entry))
+                                (n (entry.offset entry))
+                                (r (car registers)))
+                            (gen! output $lexical m n id)
+                            (gen! output $setreg r)
+                            (cgreg-bind! regs r id)
+                            (loop (cdr args)
+                                  (cdr registers)
+                                  rr
+                                  (cons r rs)
+                                  (cons r temps)))))
+                     ((procedure) (error "Bug in cg-variable" arg))
+                     ((register)
+                      (let ((r (entry.regnum entry)))
+                        (loop (cdr args)
+                              registers
+                              rr
+                              (cons r rs)
+                              temps)))
+                     ((frame)
+                      (let ((r (car registers)))
+                        (gen-load! output frame r id)
+                        (cgreg-bind! regs r id)
+                        (loop (cdr args)
+                              (cdr registers)
+                              rr
+                              (cons r rs)
+                              (cons r temps))))
+                     (else (error "Bug in cg-result-args" arg)))))
+                (else
+                 (error "Bug in cg-result-args"))))))
+  
+  (define (save-result! args registers rr rs temps)
+    (let ((r (car registers)))
+      (gen! output $setreg r)
+      (loop args
+            (cdr registers)
+            r
+            rs
+            temps)))
+  
+  (loop (cdr args)
+        (choose-registers regs frame (length args))
+        'result '() '()))
+
+; Given a local variable T1 and an expression in A-normal form,
+; cg-let-used-once returns a symbol if the local variable is used
+; exactly once in the expression and the expression matches one of
+; the patterns below.  Otherwise returns #f.  The symbol that is
+; returned is the name of the pattern that is matched.
+;
+;     pattern                         symbol returned
+; 
+;     (if T1 ... ...)                 if
+; 
+;     (<primop> T1 ...)               primop
+; 
+;     (T1 ...)                        called
+; 
+;     (set! ... T1)                   set!
+; 
+;     (let ((T2 (if T1 ... ...)))     let-if
+;       E3)
+; 
+;     (let ((T2 (<primop> T1 ...)))   let-primop
+;       E3)
+; 
+;     (let ((T2 (T1 ...)))            let-called
+;       E3)
+; 
+;     (let ((T2 (set! ... T1)))       let-set!
+;       E3)
+;
+; This implementation sometimes returns #f incorrectly, but it always
+; returns an answer in constant time (assuming A-normal form).
+
+(define (cg-let-used-once T1 exp)
+  (define budget 20)
+  (define (cg-let-used-once T1 exp)
+    (define (used? T1 exp)
+      (set! budget (- budget 1))
+      (cond ((negative? budget) #t)
+            ((constant? exp) #f)
+            ((variable? exp)
+             (eq? T1 (variable.name exp)))
+            ((lambda? exp)
+             (memq T1 (lambda.F exp)))
+            ((assignment? exp)
+             (used? T1 (assignment.rhs exp)))
+            ((call? exp)
+             (or (used? T1 (call.proc exp))
+                 (used-in-args? T1 (call.args exp))))
+            ((conditional? exp)
+             (or (used? T1 (if.test exp))
+                 (used? T1 (if.then exp))
+                 (used? T1 (if.else exp))))
+            (else #t)))
+    (define (used-in-args? T1 args)
+      (if (null? args)
+          #f
+          (or (used? T1 (car args))
+              (used-in-args? T1 (cdr args)))))
+    (set! budget (- budget 1))
+    (cond ((negative? budget) #f)
+          ((call? exp)
+           (let ((proc (call.proc exp))
+                 (args (call.args exp)))
+             (cond ((variable? proc)
+                    (let ((f (variable.name proc)))
+                      (cond ((eq? f T1)
+                             (and (not (used-in-args? T1 args))
+                                  'called))
+                            ((and (integrable? f)
+                                  (not (null? args))
+                                  (variable? (car args))
+                                  (eq? T1 (variable.name (car args))))
+                             (and (not (used-in-args? T1 (cdr args)))
+                                  'primop))
+                            (else #f))))
+                   ((lambda? proc)
+                    (and (not (memq T1 (lambda.F proc)))
+                         (not (null? args))
+                         (null? (cdr args))
+                         (case (cg-let-used-once T1 (car args))
+                           ((if)       'let-if)
+                           ((primop)   'let-primop)
+                           ((called)   'let-called)
+                           ((set!)     'let-set!)
+                           (else       #f))))
+                   (else #f))))
+          ((conditional? exp)
+           (let ((E0 (if.test exp)))
+             (and (variable? E0)
+                  (eq? T1 (variable.name E0))
+                  (not (used? T1 (if.then exp)))
+                  (not (used? T1 (if.else exp)))
+                  'if)))
+          ((assignment? exp)
+           (let ((rhs (assignment.rhs exp)))
+             (and (variable? rhs)
+                  (eq? T1 (variable.name rhs))
+                  'set!)))
+          (else #f)))
+  (cg-let-used-once T1 exp))
+
+; Given the name of a let-body pattern, an expression that matches that
+; pattern, and an expression to be substituted for the let variable,
+; returns the transformed expression.
+
+; FIXME: No longer used.
+
+(define (cg-let-transform pattern exp E1)
+  (case pattern
+    ((if)
+     (make-conditional E1 (if.then exp) (if.else exp)))
+    ((primop)
+     (make-call (call.proc exp)
+                (cons E1 (cdr (call.args exp)))))
+    ((called)
+     (make-call E1 (call.args exp)))
+    ((set!)
+     (make-assignment (assignment.lhs exp) E1))
+    ((let-if let-primop let-called let-set!)
+     (make-call (call.proc exp)
+                (list (cg-let-transform (case pattern
+                                          ((let-if)     'if)
+                                          ((let-primop) 'primop)
+                                          ((let-called) 'called)
+                                          ((let-set!)   'set!))
+                                        (car (call.args exp))
+                                        E1))))
+    (else
+     (error "Unrecognized pattern in cg-let-transform" pattern)))); Copyright 1999 William Clinger
+;
+; Code for special primitives, used to generate runtime safety checks,
+; efficient code for call-with-values, and other weird things.
+;
+; 4 June 1999.
+
+(define (cg-special output exp target regs frame env tail?)
+  (let ((name (variable.name (call.proc exp))))
+    (cond ((eq? name name:CHECK!)
+           (if (runtime-safety-checking)
+               (cg-check output exp target regs frame env tail?)))
+          (else
+           (error "Compiler bug: cg-special" (make-readable exp))))))
+
+(define (cg-special-result output exp target regs frame env tail?)
+  (let ((name (variable.name (call.proc exp))))
+    (cond ((eq? name name:CHECK!)
+           (if (runtime-safety-checking)
+               (cg-check-result output exp target regs frame env tail?)))
+          (else
+           (error "Compiler bug: cg-special" (make-readable exp))))))
+
+(define (cg-check output exp target regs frame env tail?)
+  (cg0 output (car (call.args exp)) 'result regs frame env #f)
+  (cg-check-result output exp target regs frame env tail?))
+
+(define (cg-check-result output exp target regs frame env tail?)
+  (let* ((args (call.args exp))
+         (nargs (length args))
+         (valexps (cddr args)))
+    (if (and (<= 2 nargs 5)
+             (constant? (cadr args))
+             (every? (lambda (exp)
+                       (or (constant? exp)
+                           (variable? exp)))
+                     valexps))
+        (let* ((exn (constant.value (cadr args)))
+               (vars (filter variable? valexps))
+               (rs (cg-result-args output
+                                   (cons (car args) vars)
+                                   regs frame env)))
+          
+          ; Construct the trap situation:
+          ; the exception number followed by an ordered list of
+          ; register numbers and constant expressions.
+          
+          (let loop ((registers rs)
+                     (exps valexps)
+                     (operands '()))
+            (cond ((null? exps)
+                   (let* ((situation (cons exn (reverse operands)))
+                          (ht (assembly-stream-info output))
+                          (L1 (or (hashtable-get ht situation)
+                                  (let ((L1 (make-label)))
+                                    (hashtable-put! ht situation L1)
+                                    L1))))
+                     (define (translate r)
+                       (if (number? r) r 0))
+                     (case (length operands)
+                       ((0) (gen! output $check 0 0 0 L1))
+                       ((1) (gen! output $check
+                                         (translate (car operands))
+                                         0 0 L1))
+                       ((2) (gen! output $check
+                                         (translate (car operands))
+                                         (translate (cadr operands))
+                                         0 L1))
+                       ((3) (gen! output $check
+                                         (translate (car operands))
+                                         (translate (cadr operands))
+                                         (translate (caddr operands))
+                                         L1)))))
+                  ((constant? (car exps))
+                   (loop registers
+                         (cdr exps)
+                         (cons (car exps) operands)))
+                  (else
+                   (loop (cdr registers)
+                         (cdr exps)
+                         (cons (car registers) operands))))))
+        (error "Compiler bug: runtime check" (make-readable exp)))))
+
+; Given an assembly stream and the description of a trap as recorded
+; by cg-check above, generates a non-continuable trap at that label for
+; that trap, passing the operands to the exception handler.
+
+(define (cg-trap output situation L1)
+  (let* ((exn (car situation))
+         (operands (cdr situation)))
+    (gen! output $.label L1)
+    (let ((liveregs (filter number? operands)))
+      (define (loop operands registers r)
+        (cond ((null? operands)
+               (case (length registers)
+                 ((0) (gen! output $trap 0 0 0 exn))
+                 ((1) (gen! output $trap (car registers) 0 0 exn))
+                 ((2) (gen! output $trap
+                                   (car registers)
+                                   (cadr registers)
+                                   0
+                                   exn))
+                 ((3) (gen! output $trap
+                                   (car registers)
+                                   (cadr registers)
+                                   (caddr registers)
+                                   exn))
+                 (else "Compiler bug: trap")))
+              ((number? (car operands))
+               (loop (cdr operands)
+                     (cons (car operands) registers)
+                     r))
+              ((memv r liveregs)
+               (loop operands registers (+ r 1)))
+              (else
+               (gen! output $const (constant.value (car operands)))
+               (gen! output $setreg r)
+               (loop (cdr operands)
+                     (cons r registers)
+                     (+ r 1)))))
+      (loop (reverse operands) '() 1))))
+
+; Given a short list of expressions that can be evaluated in any order,
+; evaluates the first into the result register and the others into any
+; register, and returns an ordered list of the registers that contain
+; the arguments that follow the first.
+; The number of expressions must be less than the number of argument
+; registers.
+
+; FIXME: No longer used.
+
+(define (cg-check-args output args regs frame env)
+  
+  ; Given a list of expressions to evaluate, a list of variables
+  ; and temporary names for arguments that have already been
+  ; evaluated, in reverse order, and a mask of booleans that
+  ; indicate which temporaries should be released before returning,
+  ; returns the correct result.
+  
+  (define (eval-loop args temps mask)
+    (if (null? args)
+        (eval-first-into-result temps mask)
+        (let ((reg (cg0 output (car args) #f regs frame env #f)))
+          (if (eq? reg 'result)
+              (let* ((r (choose-register regs frame))
+                     (t (newtemp)))
+                (gen! output $setreg r)
+                (cgreg-bind! regs r t)
+                (gen-store! output frame r t)
+                (eval-loop (cdr args)
+                           (cons t temps)
+                           (cons #t mask)))
+              (eval-loop (cdr args)
+                         (cons (cgreg-lookup-reg regs reg) temps)
+                         (cons #f mask))))))
+  
+  (define (eval-first-into-result temps mask)
+    (cg0 output (car args) 'result regs frame env #f)
+    (finish-loop (choose-registers regs frame (length temps))
+                 temps
+                 mask
+                 '()))
+  
+  ; Given a sufficient number of disjoint registers, a list of
+  ; variable and temporary names that may need to be loaded into
+  ; registers, a mask of booleans that indicates which temporaries
+  ; should be released, and a list of registers in forward order,
+  ; returns the correct result.
+  
+  (define (finish-loop disjoint temps mask registers)
+    (if (null? temps)
+        registers
+        (let* ((t (car temps))
+               (entry (cgreg-lookup regs t)))
+          (if entry
+              (let ((r (entry.regnum entry)))
+                (if (car mask)
+                    (begin (cgreg-release! regs r)
+                           (cgframe-release! frame t)))
+                (finish-loop disjoint
+                             (cdr temps)
+                             (cdr mask)
+                             (cons r registers)))
+              (let ((r (car disjoint)))
+                (if (memv r registers)
+                    (finish-loop (cdr disjoint) temps mask registers)
+                    (begin (gen-load! output frame r t)
+                           (cgreg-bind! regs r t)
+                           (if (car mask)
+                               (begin (cgreg-release! regs r)
+                                      (cgframe-release! frame t)))
+                           (finish-loop disjoint
+                                        (cdr temps)
+                                        (cdr mask)
+                                        (cons r registers)))))))))
+  
+  (if (< (length args) *nregs*)
+      (eval-loop (cdr args) '() '())
+      (error "Bug detected by cg-primop-args" args)))
+; Copyright 1998 William Clinger.
+;
+; $Id: twobit.sch,v 1.3 1999/08/23 19:14:26 lth Exp $
+;
+; 5 June 1999.
+;
+; Local optimizations for MacScheme machine assembly code.
+;
+; Branch tensioning.
+; Suppress nop instructions.
+; Suppress save, restore, and pop instructions whose operand is -1.
+; Suppress redundant stores.
+; Suppress definitions (primarily loads) of dead registers.
+;
+; Note:  Twobit never generates a locally redundant load or store,
+; so this code must be tested with a different code generator.
+;
+; To perform these optimizations, the basic block must be traversed
+; both forwards and backwards.
+; The forward traversal keeps track of registers that were defined
+; by a load.
+; The backward traversal keeps track of live registers.
+
+(define filter-basic-blocks
+  
+  (let* ((suppression-message
+          "Local optimization detected a useless instruction.")
+         
+         ; Each instruction is mapping to an encoding of the actions
+         ; to be performed when it is encountered during the forward
+         ; or backward traversal.
+         
+         (forward:normal                   0)
+         (forward:nop                      1)
+         (forward:ends-block               2)
+         (forward:interesting              3)
+         (forward:kills-all-registers      4)
+         (forward:nop-if-arg1-is-negative  5)
+         
+         (backward:normal                  0)
+         (backward:ends-block              1)
+         (backward:begins-block            2)
+         (backward:uses-arg1               4)
+         (backward:uses-arg2               8)
+         (backward:uses-arg3              16)
+         (backward:kills-arg1             32)
+         (backward:kills-arg2             64)
+         (backward:uses-many             128)
+         
+         ; largest mnemonic + 1
+         
+         (dispatch-table-size *number-of-mnemonics*)
+         
+         ; Dispatch table for the forwards traversal.
+         
+         (forward-table (make-bytevector dispatch-table-size))
+         
+         ; Dispatch table for the backwards traversal.
+         
+         (backward-table (make-bytevector dispatch-table-size)))
+    
+    (do ((i 0 (+ i 1)))
+        ((= i dispatch-table-size))
+        (bytevector-set! forward-table i forward:normal)
+        (bytevector-set! backward-table i backward:normal))
+    
+    (bytevector-set! forward-table $nop     forward:nop)
+    
+    (bytevector-set! forward-table $invoke  forward:ends-block)
+    (bytevector-set! forward-table $return  forward:ends-block)
+    (bytevector-set! forward-table $skip    forward:ends-block)
+    (bytevector-set! forward-table $branch  forward:ends-block)
+    (bytevector-set! forward-table $branchf forward:ends-block)
+    (bytevector-set! forward-table $jump    forward:ends-block)
+    (bytevector-set! forward-table $.align  forward:ends-block)
+    (bytevector-set! forward-table $.proc   forward:ends-block)
+    (bytevector-set! forward-table $.cont   forward:ends-block)
+    (bytevector-set! forward-table $.label  forward:ends-block)
+    
+    (bytevector-set! forward-table $store   forward:interesting)
+    (bytevector-set! forward-table $load    forward:interesting)
+    (bytevector-set! forward-table $setstk  forward:interesting)
+    (bytevector-set! forward-table $setreg  forward:interesting)
+    (bytevector-set! forward-table $movereg forward:interesting)
+    (bytevector-set! forward-table $const/setreg
+                                            forward:interesting)
+    
+    (bytevector-set! forward-table $args>=  forward:kills-all-registers)
+    (bytevector-set! forward-table $popstk  forward:kills-all-registers)
+    
+    ; These instructions also kill all registers.
+    
+    (bytevector-set! forward-table $save    forward:nop-if-arg1-is-negative)
+    (bytevector-set! forward-table $restore forward:nop-if-arg1-is-negative)
+    (bytevector-set! forward-table $pop     forward:nop-if-arg1-is-negative)
+  
+    (bytevector-set! backward-table $invoke  backward:ends-block)
+    (bytevector-set! backward-table $return  backward:ends-block)
+    (bytevector-set! backward-table $skip    backward:ends-block)
+    (bytevector-set! backward-table $branch  backward:ends-block)
+    (bytevector-set! backward-table $branchf backward:ends-block)
+    
+    (bytevector-set! backward-table $jump    backward:begins-block) ; [sic]
+    (bytevector-set! backward-table $.align  backward:begins-block)
+    (bytevector-set! backward-table $.proc   backward:begins-block)
+    (bytevector-set! backward-table $.cont   backward:begins-block)
+    (bytevector-set! backward-table $.label  backward:begins-block)
+    
+    (bytevector-set! backward-table $op2     backward:uses-arg2)
+    (bytevector-set! backward-table $op3     (logior backward:uses-arg2
+                                                     backward:uses-arg3))
+    (bytevector-set! backward-table $check   (logior
+                                              backward:uses-arg1
+                                              (logior backward:uses-arg2
+                                                      backward:uses-arg3)))
+    (bytevector-set! backward-table $trap    (logior
+                                              backward:uses-arg1
+                                              (logior backward:uses-arg2
+                                                      backward:uses-arg3)))
+    (bytevector-set! backward-table $store   backward:uses-arg1)
+    (bytevector-set! backward-table $reg     backward:uses-arg1)
+    (bytevector-set! backward-table $load    backward:kills-arg1)
+    (bytevector-set! backward-table $setreg  backward:kills-arg1)
+    (bytevector-set! backward-table $movereg (logior backward:uses-arg1
+                                                     backward:kills-arg2))
+    (bytevector-set! backward-table $const/setreg
+                                             backward:kills-arg2)
+    (bytevector-set! backward-table $lambda  backward:uses-many)
+    (bytevector-set! backward-table $lexes   backward:uses-many)
+    (bytevector-set! backward-table $args>=  backward:uses-many)
+    
+    (lambda (instructions)
+      
+      (let* ((*nregs* *nregs*) ; locals might be faster than globals
+             
+             ; During the forwards traversal:
+             ;    (vector-ref registers i) = #f
+             ;        means the content of register i is unknown
+             ;    (vector-ref registers i) = j
+             ;        means register was defined by load i,j
+             ;
+             ; During the backwards traversal:
+             ;    (vector-ref registers i) = #f means register i is dead
+             ;    (vector-ref registers i) = #t means register i is live
+             
+             (registers (make-vector *nregs* #f))
+             
+             ; During the forwards traversal, the label of a block that
+             ; falls through into another block or consists of a skip
+             ; to another block is mapped to another label.
+             ; This mapping is implemented by a hash table.
+             ; Before the backwards traversal, the transitive closure
+             ; is computed.  The graph has no cycles, and the maximum
+             ; out-degree is 1, so this is easy.
+             
+             (label-table (make-hashtable (lambda (n) n) assv)))
+        
+        (define (compute-transitive-closure!)
+          (define (lookup x)
+            (let ((y (hashtable-get label-table x)))
+              (if y
+                  (lookup y)
+                  x)))
+          (hashtable-for-each (lambda (x y)
+                                (hashtable-put! label-table x (lookup y)))
+                              label-table))
+        
+        ; Don't use this procedure until the preceding procedure
+        ; has been called.
+        
+        (define (lookup-label x)
+          (hashtable-fetch label-table x x))
+        
+        (define (vector-fill! v x)
+          (subvector-fill! v 0 (vector-length v) x))
+        
+        (define (subvector-fill! v i j x)
+          (if (< i j)
+              (begin (vector-set! v i x)
+                     (subvector-fill! v (+ i 1) j x))))
+        
+        (define (kill-stack! j)
+          (do ((i 0 (+ i 1)))
+              ((= i *nregs*))
+              (let ((x (vector-ref registers i)))
+                (if (and x (= x j))
+                    (vector-set! registers i #f)))))
+        
+        ; Dispatch procedure for the forwards traversal.
+        
+        (define (forwards instructions filtered)
+          (if (null? instructions)
+              (begin (vector-fill! registers #f)
+                     (vector-set! registers 0 #t)
+                     (compute-transitive-closure!)
+                     (backwards0 filtered '()))
+              (let* ((instruction (car instructions))
+                     (instructions (cdr instructions))
+                     (op (instruction.op instruction))
+                     (flags (bytevector-ref forward-table op)))
+                (cond ((eqv? flags forward:normal)
+                       (forwards instructions (cons instruction filtered)))
+                      ((eqv? flags forward:nop)
+                       (forwards instructions filtered))
+                      ((eqv? flags forward:nop-if-arg1-is-negative)
+                       (if (negative? (instruction.arg1 instruction))
+                           (forwards instructions filtered)
+                           (begin (vector-fill! registers #f)
+                                  (forwards instructions
+                                            (cons instruction filtered)))))
+                      ((eqv? flags forward:kills-all-registers)
+                       (vector-fill! registers #f)
+                       (forwards instructions
+                                 (cons instruction filtered)))
+                      ((eqv? flags forward:ends-block)
+                       (vector-fill! registers #f)
+                       (if (eqv? op $.label)
+                           (forwards-label instruction
+                                           instructions
+                                           filtered)
+                           (forwards instructions
+                                     (cons instruction filtered))))
+                      ((eqv? flags forward:interesting)
+                       (cond ((eqv? op $setreg)
+                              (vector-set! registers
+                                           (instruction.arg1 instruction)
+                                           #f)
+                              (forwards instructions
+                                        (cons instruction filtered)))
+                             ((eqv? op $const/setreg)
+                              (vector-set! registers
+                                           (instruction.arg2 instruction)
+                                           #f)
+                              (forwards instructions
+                                        (cons instruction filtered)))
+                             ((eqv? op $movereg)
+                              (vector-set! registers
+                                           (instruction.arg2 instruction)
+                                           #f)
+                              (forwards instructions
+                                        (cons instruction filtered)))
+                             ((eqv? op $setstk)
+                              (kill-stack! (instruction.arg1 instruction))
+                              (forwards instructions
+                                        (cons instruction filtered)))
+                             ((eqv? op $load)
+                              (let ((i (instruction.arg1 instruction))
+                                    (j (instruction.arg2 instruction)))
+                                (if (eqv? (vector-ref registers i) j)
+                                    ; Suppress redundant load.
+                                    ; Should never happen with Twobit.
+                                    (suppress-forwards instruction
+                                                       instructions
+                                                       filtered)
+                                    (begin (vector-set! registers i j)
+                                           (forwards instructions
+                                                     (cons instruction
+                                                           filtered))))))
+                             ((eqv? op $store)
+                              (let ((i (instruction.arg1 instruction))
+                                    (j (instruction.arg2 instruction)))
+                                (if (eqv? (vector-ref registers i) j)
+                                    ; Suppress redundant store.
+                                    ; Should never happen with Twobit.
+                                    (suppress-forwards instruction
+                                                       instructions
+                                                       filtered)
+                                    (begin (kill-stack! j)
+                                           (forwards instructions
+                                                     (cons instruction
+                                                           filtered))))))
+                             (else
+                              (local-optimization-error op))))
+                      (else
+                       (local-optimization-error op))))))
+        
+        ; Enters labels into a table for branch tensioning.
+        
+        (define (forwards-label instruction1 instructions filtered)
+          (let ((label1 (instruction.arg1 instruction1)))
+            (if (null? instructions)
+                ; This is ok provided the label is unreachable.
+                (forwards instructions (cdr filtered))
+                (let loop ((instructions instructions)
+                           (filtered (cons instruction1 filtered)))
+                  (let* ((instruction (car instructions))
+                         (op (instruction.op instruction))
+                         (flags (bytevector-ref forward-table op)))
+                    (cond ((eqv? flags forward:nop)
+                           (loop (cdr instructions) filtered))
+                          ((and (eqv? flags forward:nop-if-arg1-is-negative)
+                                (negative? (instruction.arg1 instruction)))
+                           (loop (cdr instructions) filtered))
+                          ((eqv? op $.label)
+                           (let ((label2 (instruction.arg1 instruction)))
+                             (hashtable-put! label-table label1 label2)
+                             (forwards-label instruction
+                                             (cdr instructions)
+                                             (cdr filtered))))
+                          ((eqv? op $skip)
+                           (let ((label2 (instruction.arg1 instruction)))
+                             (hashtable-put! label-table label1 label2)
+                             ; We can't get rid of the skip instruction
+                             ; because control might fall into this block,
+                             ; but we can get rid of the label.
+                             (forwards instructions (cdr filtered))))
+                          (else
+                           (forwards instructions filtered))))))))
+        
+        ; Dispatch procedure for the backwards traversal.
+        
+        (define (backwards instructions filtered)
+          (if (null? instructions)
+              filtered
+              (let* ((instruction (car instructions))
+                     (instructions (cdr instructions))
+                     (op (instruction.op instruction))
+                     (flags (bytevector-ref backward-table op)))
+                (cond ((eqv? flags backward:normal)
+                       (backwards instructions (cons instruction filtered)))
+                      ((eqv? flags backward:ends-block)
+                       (backwards0 (cons instruction instructions)
+                                   filtered))
+                      ((eqv? flags backward:begins-block)
+                       (backwards0 instructions
+                                   (cons instruction filtered)))
+                      ((eqv? flags backward:uses-many)
+                       (cond ((or (eqv? op $lambda)
+                                  (eqv? op $lexes))
+                              (let ((live
+                                     (if (eqv? op $lexes)
+                                         (instruction.arg1 instruction)
+                                         (instruction.arg2 instruction))))
+                                (subvector-fill! registers
+                                                 0
+                                                 (min *nregs* (+ 1 live))
+                                                 #t)
+                                (backwards instructions
+                                           (cons instruction filtered))))
+                             ((eqv? op $args>=)
+                              (vector-fill! registers #t)
+                              (backwards instructions
+                                         (cons instruction filtered)))
+                             (else
+                              (local-optimization-error op))))
+                      ((and (eqv? (logand flags backward:kills-arg1)
+                                  backward:kills-arg1)
+                            (not (vector-ref registers
+                                             (instruction.arg1 instruction))))
+                       ; Suppress initialization of dead register.
+                       (suppress-backwards instruction
+                                           instructions
+                                           filtered))
+                      ((and (eqv? (logand flags backward:kills-arg2)
+                                  backward:kills-arg2)
+                            (not (vector-ref registers
+                                             (instruction.arg2 instruction))))
+                       ; Suppress initialization of dead register.
+                       (suppress-backwards instruction
+                                           instructions
+                                           filtered))
+                      ((and (eqv? op $movereg)
+                            (= (instruction.arg1 instruction)
+                               (instruction.arg2 instruction)))
+                       (backwards instructions filtered))
+                      (else
+                       (let ((filtered (cons instruction filtered)))
+                         (if (eqv? (logand flags backward:kills-arg1)
+                                   backward:kills-arg1)
+                             (vector-set! registers
+                                          (instruction.arg1 instruction)
+                                          #f))
+                         (if (eqv? (logand flags backward:kills-arg2)
+                                   backward:kills-arg2)
+                             (vector-set! registers
+                                          (instruction.arg2 instruction)
+                                          #f))
+                         (if (eqv? (logand flags backward:uses-arg1)
+                                   backward:uses-arg1)
+                             (vector-set! registers
+                                          (instruction.arg1 instruction)
+                                          #t))
+                         (if (eqv? (logand flags backward:uses-arg2)
+                                   backward:uses-arg2)
+                             (vector-set! registers
+                                          (instruction.arg2 instruction)
+                                          #t))
+                         (if (eqv? (logand flags backward:uses-arg3)
+                                   backward:uses-arg3)
+                             (vector-set! registers
+                                          (instruction.arg3 instruction)
+                                          #t))
+                         (backwards instructions filtered)))))))
+        
+        ; Given a list of instructions in reverse order, whose first
+        ; element is the last instruction of a basic block,
+        ; and a filtered list of instructions in forward order,
+        ; returns a filtered list of instructions in the correct order.
+        
+        (define (backwards0 instructions filtered)
+          (if (null? instructions)
+              filtered
+              (let* ((instruction (car instructions))
+                     (mnemonic (instruction.op instruction)))
+                (cond ((or (eqv? mnemonic $.label)
+                           (eqv? mnemonic $.proc)
+                           (eqv? mnemonic $.cont)
+                           (eqv? mnemonic $.align))
+                       (backwards0 (cdr instructions)
+                                   (cons instruction filtered)))
+                      ; all registers are dead at a $return
+                      ((eqv? mnemonic $return)
+                       (vector-fill! registers #f)
+                       (vector-set! registers 0 #t)
+                       (backwards (cdr instructions)
+                                  (cons instruction filtered)))
+                      ; all but the argument registers are dead at an $invoke
+                      ((eqv? mnemonic $invoke)
+                       (let ((n+1 (min *nregs*
+                                       (+ (instruction.arg1 instruction) 1))))
+                         (subvector-fill! registers 0 n+1 #t)
+                         (subvector-fill! registers n+1 *nregs* #f)
+                         (backwards (cdr instructions)
+                                    (cons instruction filtered))))
+                      ; the compiler says which registers are live at the
+                      ; target of $skip, $branch, $branchf, or $jump
+                      ((or (eqv? mnemonic $skip)
+                           (eqv? mnemonic $branch))
+                       (let* ((live (instruction.arg2 instruction))
+                              (n+1 (min *nregs* (+ live 1))))
+                         (subvector-fill! registers 0 n+1 #t)
+                         (subvector-fill! registers n+1 *nregs* #f)
+                         (let ((instruction
+                                ; FIXME
+                                (list mnemonic
+                                      (lookup-label
+                                       (instruction.arg1 instruction))
+                                      live)))
+                           (backwards (cdr instructions)
+                                      (cons instruction filtered)))))
+                      ((eqv? mnemonic $jump)
+                       (let ((n+1 (min *nregs*
+                                       (+ (instruction.arg3 instruction) 1))))
+                         (subvector-fill! registers 0 n+1 #t)
+                         (subvector-fill! registers n+1 *nregs* #f)
+                         (backwards (cdr instructions)
+                                    (cons instruction filtered))))
+                      ; the live registers at the target of a $branchf must be
+                      ; combined with the live registers at the $branchf
+                      ((eqv? mnemonic $branchf)
+                       (let* ((live (instruction.arg2 instruction))
+                              (n+1 (min *nregs* (+ live 1))))
+                         (subvector-fill! registers 0 n+1 #t)
+                         (let ((instruction
+                                ; FIXME
+                                (list mnemonic
+                                      (lookup-label
+                                       (instruction.arg1 instruction))
+                                      live)))
+                           (backwards (cdr instructions)
+                                      (cons instruction filtered)))))
+                      (else (backwards instructions filtered))))))
+        
+        (define (suppress-forwards instruction instructions filtered)
+          (if (issue-warnings)
+              '(begin (display suppression-message)
+                      (newline)))
+          (forwards instructions filtered))
+        
+        (define (suppress-backwards instruction instructions filtered)
+          (if (issue-warnings)
+              '(begin (display suppression-message)
+                      (newline)))
+          (backwards instructions filtered))
+        
+        (define (local-optimization-error op)
+          (error "Compiler bug: local optimization" op))
+        
+        (vector-fill! registers #f)
+        (forwards instructions '())))))
+; Copyright 1998 Lars T Hansen.
+; 
+; $Id: twobit.sch,v 1.3 1999/08/23 19:14:26 lth Exp $
+;
+; 28 April 1999
+;
+; compile313 -- compilation parameters and driver procedures.
+
+
+; File types -- these may differ between operating systems.
+
+(define *scheme-file-types* '(".sch" ".scm"))
+(define *lap-file-type*     ".lap")
+(define *mal-file-type*     ".mal")
+(define *lop-file-type*     ".lop")
+(define *fasl-file-type*    ".fasl")
+
+; Compile and assemble a scheme source file and produce a fastload file.
+
+(define (compile-file infilename . rest)
+
+  (define (doit)
+    (let ((outfilename
+           (if (not (null? rest))
+               (car rest)
+               (rewrite-file-type infilename
+                                  *scheme-file-types*
+                                  *fasl-file-type*)))
+          (user
+           (assembly-user-data)))
+      (if (and (not (integrate-usual-procedures))
+               (issue-warnings))
+          (begin 
+            (display "WARNING from compiler: ")
+            (display "integrate-usual-procedures is turned off")
+            (newline)
+            (display "Performance is likely to be poor.")
+            (newline)))
+      (if (benchmark-block-mode)
+          (process-file-block infilename
+                              outfilename
+                              dump-fasl-segment-to-port
+                              (lambda (forms)
+                                (assemble (compile-block forms) user)))
+          (process-file infilename
+                        outfilename
+                        dump-fasl-segment-to-port
+                        (lambda (expr)
+                          (assemble (compile expr) user))))
+      (unspecified)))
+
+  (if (eq? (nbuild-parameter 'target-machine) 'standard-c)
+      (error "Compile-file not supported on this target architecture.")
+      (doit)))
+
+
+; Assemble a MAL or LOP file and produce a FASL file.
+
+(define (assemble-file infilename . rest)
+  (define (doit)
+    (let ((outfilename
+           (if (not (null? rest))
+               (car rest)
+               (rewrite-file-type infilename 
+                                  (list *lap-file-type* *mal-file-type*)
+                                  *fasl-file-type*)))
+          (malfile?
+           (file-type=? infilename *mal-file-type*))
+          (user
+           (assembly-user-data)))
+      (process-file infilename
+                    outfilename
+                    dump-fasl-segment-to-port
+                    (lambda (x) (assemble (if malfile? (eval x) x) user)))
+      (unspecified)))
+  
+  (if (eq? (nbuild-parameter 'target-machine) 'standard-c)
+      (error "Assemble-file not supported on this target architecture.")
+      (doit)))
+
+
+; Compile and assemble a single expression; return the LOP segment.
+
+(define compile-expression
+  (let ()
+    
+    (define (compile-expression expr env)
+      (let ((syntax-env
+             (case (environment-tag env)
+               ((0 1) (make-standard-syntactic-environment))
+               ((2)   global-syntactic-environment)
+               (else  
+                (error "Invalid environment for compile-expression: " env)
+                #t))))
+        (let ((current-env global-syntactic-environment))
+          (dynamic-wind
+           (lambda ()
+             (set! global-syntactic-environment syntax-env))
+           (lambda ()
+             (assemble (compile expr)))
+           (lambda ()
+             (set! global-syntactic-environment current-env))))))
+    
+    compile-expression))
+
+
+(define macro-expand-expression
+  (let ()
+    
+    (define (macro-expand-expression expr env)
+      (let ((syntax-env
+             (case (environment-tag env)
+               ((0 1) (make-standard-syntactic-environment))
+               ((2)   global-syntactic-environment)
+               (else  
+                (error "Invalid environment for compile-expression: " env)
+                #t))))
+        (let ((current-env global-syntactic-environment))
+          (dynamic-wind
+           (lambda ()
+             (set! global-syntactic-environment syntax-env))
+           (lambda ()
+             (make-readable
+              (macro-expand expr)))
+           (lambda ()
+             (set! global-syntactic-environment current-env))))))
+    
+    macro-expand-expression))
+
+
+; Compile a scheme source file to a LAP file.
+
+(define (compile313 infilename . rest)
+  (let ((outfilename
+         (if (not (null? rest))
+             (car rest)
+             (rewrite-file-type infilename
+                                *scheme-file-types* 
+                                *lap-file-type*)))
+        (write-lap
+         (lambda (item port)
+           (write item port)
+           (newline port)
+           (newline port))))
+    (if (benchmark-block-mode)
+        (process-file-block infilename outfilename write-lap compile-block)
+        (process-file infilename outfilename write-lap compile))
+    (unspecified)))
+
+
+; Assemble a LAP or MAL file to a LOP file.
+
+(define (assemble313 file . rest)
+  (let ((outputfile
+         (if (not (null? rest))
+             (car rest)
+             (rewrite-file-type file 
+                                (list *lap-file-type* *mal-file-type*)
+                                *lop-file-type*)))
+        (malfile?
+         (file-type=? file *mal-file-type*))
+        (user
+         (assembly-user-data)))
+    (process-file file
+                  outputfile
+                  write-lop
+                  (lambda (x) (assemble (if malfile? (eval x) x) user)))
+    (unspecified)))
+
+
+; Compile and assemble a Scheme source file to a LOP file.
+
+(define (compile-and-assemble313 input-file . rest)
+  (let ((output-file
+         (if (not (null? rest))
+             (car rest)
+             (rewrite-file-type input-file 
+                                *scheme-file-types*
+                                *lop-file-type*)))
+        (user
+         (assembly-user-data)))
+    (if (benchmark-block-mode)
+        (process-file-block input-file
+                            output-file
+                            write-lop
+                            (lambda (x) (assemble (compile-block x) user)))
+        (process-file input-file
+                      output-file
+                      write-lop
+                      (lambda (x) (assemble (compile x) user))))
+    (unspecified)))
+
+
+; Convert a LOP file to a FASL file.
+
+(define (make-fasl infilename . rest)
+  (define (doit)
+    (let ((outfilename
+           (if (not (null? rest))
+               (car rest)
+               (rewrite-file-type infilename
+                                  *lop-file-type*
+                                  *fasl-file-type*))))
+      (process-file infilename
+                    outfilename
+                    dump-fasl-segment-to-port
+                    (lambda (x) x))
+      (unspecified)))
+
+  (if (eq? (nbuild-parameter 'target-machine) 'standard-c)
+      (error "Make-fasl not supported on this target architecture.")
+      (doit)))
+
+
+; Disassemble a procedure's code vector.
+
+(define (disassemble item . rest)
+  (let ((output-port (if (null? rest)
+                         (current-output-port)
+                         (car rest))))
+    (disassemble-item item #f output-port)
+    (unspecified)))
+
+
+; The item can be either a procedure or a pair (assumed to be a segment).
+
+(define (disassemble-item item segment-no port)
+  
+  (define (print . rest)
+    (for-each (lambda (x) (display x port)) rest)
+    (newline port))
+  
+  (define (print-constvector cv)
+    (do ((i 0 (+ i 1)))
+        ((= i (vector-length cv)))
+        (print "------------------------------------------")
+        (print "Constant vector element # " i)
+        (case (car (vector-ref cv i))
+          ((codevector)
+           (print "Code vector")
+           (print-instructions (disassemble-codevector
+                                (cadr (vector-ref cv i)))
+                               port))
+          ((constantvector)    
+           (print "Constant vector")
+           (print-constvector (cadr (vector-ref cv i))))
+          ((global)
+           (print "Global: " (cadr (vector-ref cv i))))
+          ((data)
+           (print "Data: " (cadr (vector-ref cv i)))))))
+  
+  (define (print-segment segment)
+    (print "Segment # " segment-no)
+    (print-instructions (disassemble-codevector (car segment)) port)
+    (print-constvector (cdr segment))
+    (print "========================================"))
+  
+  (cond ((procedure? item)
+         (print-instructions (disassemble-codevector (procedure-ref item 0))
+                             port))
+        ((and (pair? item)
+              (bytevector? (car item))
+              (vector? (cdr item)))
+         (print-segment item))
+        (else
+         (error "disassemble-item: " item " is not disassemblable."))))
+
+
+; Disassemble a ".lop" or ".fasl" file; dump output to screen or 
+; other (optional) file.
+
+(define (disassemble-file file . rest)
+  
+  (define (doit input-port output-port)
+    (display "; From " output-port)
+    (display file output-port)
+    (newline output-port)
+    (do ((segment-no 0 (+ segment-no 1))
+         (segment (read input-port) (read input-port)))
+        ((eof-object? segment))
+        (disassemble-item segment segment-no output-port)))
+
+  ; disassemble313
+
+  (call-with-input-file
+   file
+   (lambda (input-port)
+     (if (null? rest)
+         (doit input-port (current-output-port))
+         (begin
+          (delete-file (car rest))
+          (call-with-output-file
+           (car rest)
+           (lambda (output-port) (doit input-port output-port)))))))
+  (unspecified))
+
+
+; Display and manipulate the compiler switches.
+
+(define (compiler-switches . rest)
+
+  (define (slow-code)
+    (set-compiler-flags! 'no-optimization)
+    (set-assembler-flags! 'no-optimization))
+
+  (define (standard-code)
+    (set-compiler-flags! 'standard)
+    (set-assembler-flags! 'standard))
+
+  (define (fast-safe-code)
+    (set-compiler-flags! 'fast-safe)
+    (set-assembler-flags! 'fast-safe))
+
+  (define (fast-unsafe-code)
+    (set-compiler-flags! 'fast-unsafe)
+    (set-assembler-flags! 'fast-unsafe))
+
+  (cond ((null? rest)
+         (display "Debugging:")
+         (newline)
+         (display-twobit-flags 'debugging)
+         (display-assembler-flags 'debugging)
+         (newline)
+         (display "Safety:")
+         (newline)
+         (display-twobit-flags 'safety)
+         (display-assembler-flags 'safety)
+         (newline)
+         (display "Speed:")
+         (newline)
+         (display-twobit-flags 'optimization)
+         (display-assembler-flags 'optimization)
+         (if #f #f))
+        ((null? (cdr rest))
+         (case (car rest)
+           ((0 slow)             (slow-code))
+           ((1 standard)         (standard-code))
+           ((2 fast-safe)        (fast-safe-code))
+           ((3 fast-unsafe)      (fast-unsafe-code))
+           ((default
+             factory-settings)   (fast-safe-code)
+                                 (include-source-code #t)
+                                 (benchmark-mode #f)
+                                 (benchmark-block-mode #f)
+                                 (common-subexpression-elimination #f)
+                                 (representation-inference #f))
+           (else 
+            (error "Unrecognized flag " (car rest) " to compiler-switches.")))
+         (unspecified))
+        (else
+         (error "Too many arguments to compiler-switches."))))
+
+; Read and process one file, producing another.
+; Preserves the global syntactic environment.
+
+(define (process-file infilename outfilename writer processer)
+  (define (doit)
+    (delete-file outfilename)
+    (call-with-output-file
+     outfilename
+     (lambda (outport)
+       (call-with-input-file
+        infilename
+        (lambda (inport)
+          (let loop ((x (read inport)))
+            (if (eof-object? x)
+                #t
+                (begin (writer (processer x) outport)
+                       (loop (read inport))))))))))
+  (let ((current-syntactic-environment
+         (syntactic-copy global-syntactic-environment)))
+    (dynamic-wind
+     (lambda () #t)
+     (lambda () (doit))
+     (lambda ()
+       (set! global-syntactic-environment
+             current-syntactic-environment)))))
+
+; Same as above, but passes a list of the entire file's contents
+; to the processer.
+; FIXME:  Both versions of PROCESS-FILE always delete the output file.
+; Shouldn't it be left alone if the input file can't be opened?
+
+(define (process-file-block infilename outfilename writer processer)
+  (define (doit)
+    (delete-file outfilename)
+    (call-with-output-file
+     outfilename
+     (lambda (outport)
+       (call-with-input-file
+        infilename
+        (lambda (inport)
+          (do ((x (read inport) (read inport))
+               (forms '() (cons x forms)))
+              ((eof-object? x)
+               (writer (processer (reverse forms)) outport))))))))
+  (let ((current-syntactic-environment
+         (syntactic-copy global-syntactic-environment)))
+    (dynamic-wind
+     (lambda () #t)
+     (lambda () (doit))
+     (lambda ()
+       (set! global-syntactic-environment
+             current-syntactic-environment)))))
+
+
+; Given a file name with some type, produce another with some other type.
+
+(define (rewrite-file-type filename matches new)
+  (if (not (pair? matches))
+      (rewrite-file-type filename (list matches) new)
+      (let ((j (string-length filename)))
+        (let loop ((m matches))
+          (cond ((null? m)
+                 (string-append filename new))
+                (else
+                 (let* ((n (car m))
+                        (l (string-length n)))
+                   (if (file-type=? filename n)
+                       (string-append (substring filename 0 (- j l)) new)
+                       (loop (cdr m))))))))))
+
+(define (file-type=? file-name type-name)
+  (let ((fl (string-length file-name))
+        (tl (string-length type-name)))
+    (and (>= fl tl)
+         (string-ci=? type-name
+                      (substring file-name (- fl tl) fl)))))
+
+; eof
+; Copyright 1998 William Clinger.
+;
+; $Id: twobit.sch,v 1.3 1999/08/23 19:14:26 lth Exp $
+;
+; Procedures that make .LAP structures human-readable
+
+(define (readify-lap code)
+  (map (lambda (x)
+        (let ((iname (cdr (assv (car x) *mnemonic-names*))))
+          (if (not (= (car x) $lambda))
+              (cons iname (cdr x))
+              (list iname (readify-lap (cadr x)) (caddr x)))))
+       code))
+
+(define (readify-file f . o)
+
+  (define (doit)
+    (let ((i (open-input-file f)))
+      (let loop ((x (read i)))
+       (if (not (eof-object? x))
+           (begin (pretty-print (readify-lap x))
+                  (loop (read i)))))))
+
+  (if (null? o)
+      (doit)
+      (begin (delete-file (car o))
+            (with-output-to-file (car o) doit))))
+
+; eof
+; Copyright 1991 Lightship Software, Incorporated.
+; 
+; $Id: twobit.sch,v 1.3 1999/08/23 19:14:26 lth Exp $
+;
+; Target-independent part of the assembler.
+;
+; This is a simple, table-driven, one-pass assembler.
+; Part of it assumes a big-endian target machine.
+;
+; The input to this pass is a list of symbolic MacScheme machine
+; instructions and pseudo-instructions.  Each symbolic MacScheme 
+; machine instruction or pseudo-instruction is a list whose car
+; is a small non-negative fixnum that acts as the mnemonic for the
+; instruction.  The rest of the list is interpreted as indicated
+; by the mnemonic.
+;
+; The output is a pair consisting of machine code (a bytevector or 
+; string) and a constant vector.
+;
+; This assembler is table-driven, and may be customized to emit
+; machine code for different target machines.  The table consists
+; of a vector of procedures indexed by mnemonics.  Each procedure
+; in the table should take two arguments: an assembly structure
+; and a source instruction.  The procedure should just assemble
+; the instruction using the operations defined below.
+;
+; The table and target can be changed by redefining the following 
+; five procedures.
+
+(define (assembly-table) (error "No assembly table defined."))
+(define (assembly-start as) #t)
+(define (assembly-end as segment) segment)
+(define (assembly-user-data) #f)
+
+; The main entry point.
+
+(define (assemble source . rest)
+  (let* ((user (if (null? rest) (assembly-user-data) (car rest)))
+        (as   (make-assembly-structure source (assembly-table) user)))
+    (assembly-start as)
+    (assemble1 as
+              (lambda (as)
+                (let ((segment (assemble-pasteup as)))
+                  (assemble-finalize! as)
+                  (assembly-end as segment)))
+              #f)))
+
+; The following procedures are to be called by table routines.
+;
+; The assembly source for nested lambda expressions should be
+; assembled by calling this procedure.  This allows an inner
+; lambda to refer to labels defined by outer lambdas.
+;
+; We delay the assembly of the nested lambda until after the outer lambda
+; has been finalized so that all labels in the outer lambda are known
+; to the inner lambda.
+;
+; The continuation procedure k is called to backpatch the constant
+; vector of the outer lambda after the inner lambda has been
+; finalized.  This is necessary because of the delayed evaluation: the
+; outer lambda holds code and constants for the inner lambda in its
+; constant vector.
+
+(define (assemble-nested-lambda as source doc k . rest)
+  (let* ((user (if (null? rest) #f (car rest)))
+        (nested-as (make-assembly-structure source (as-table as) user)))
+    (as-parent! nested-as as)
+    (as-nested! as (cons (lambda ()
+                          (assemble1 nested-as 
+                                     (lambda (nested-as)
+                                       (let ((segment
+                                              (assemble-pasteup nested-as)))
+                                         (assemble-finalize! nested-as)
+                                         (k nested-as segment)))
+                                     doc))
+                        (as-nested as)))))
+
+(define operand0 car)      ; the mnemonic
+(define operand1 cadr)
+(define operand2 caddr)
+(define operand3 cadddr)
+(define (operand4 i) (car (cddddr i)))
+
+; Emits the bits contained in the bytevector bv.
+
+(define (emit! as bv)
+  (as-code! as (cons bv (as-code as)))
+  (as-lc! as (+ (as-lc as) (bytevector-length bv))))
+
+; Emits the characters contained in the string s as code (for C generation).
+
+(define (emit-string! as s)
+  (as-code! as (cons s (as-code as)))
+  (as-lc! as (+ (as-lc as) (string-length s))))
+
+; Given any Scheme object that may legally be quoted, returns an
+; index into the constant vector for that constant.
+
+(define (emit-constant as x)
+  (do ((i 0 (+ i 1))
+       (y (as-constants as) (cdr y)))
+      ((or (null? y) (equal? x (car y)))
+       (if (null? y)
+          (as-constants! as (append! (as-constants as) (list x))))
+       i)))
+
+(define (emit-datum as x)
+  (emit-constant as (list 'data x)))
+
+(define (emit-global as x)
+  (emit-constant as (list 'global x)))
+
+(define (emit-codevector as x)
+  (emit-constants as (list 'codevector x)))
+
+(define (emit-constantvector as x)
+  (emit-constants as (list 'constantvector x)))
+
+; Set-constant changes the datum stored, without affecting the tag.
+; It can operate on the list form because the pair stored in the list
+; is shared between the list and any vector created from the list.
+
+(define (set-constant! as n datum)
+  (let ((pair (list-ref (as-constants as) n)))
+    (set-car! (cdr pair) datum)))
+
+; Guarantees that the constants will not share structure
+; with any others, and will occupy consecutive positions
+; in the constant vector.  Returns the index of the first
+; constant.
+
+(define (emit-constants as x . rest)
+  (let* ((constants (as-constants as))
+         (i         (length constants)))
+    (as-constants! as (append! constants (cons x rest)))
+    i))
+
+; Defines the given label using the current location counter.
+
+(define (emit-label! as L)
+  (set-cdr! L (as-lc as)))
+
+; Adds the integer n to the size code bytes beginning at the
+; given byte offset from the current value of the location counter.
+
+(define (emit-fixup! as offset size n)
+  (as-fixups! as (cons (list (+ offset (as-lc as)) size n)
+                      (as-fixups as))))
+
+; Adds the value of the label L to the size code bytes beginning
+; at the given byte offset from the current location counter.
+
+(define (emit-fixup-label! as offset size L)
+  (as-fixups! as (cons (list (+ offset (as-lc as)) size (list L))
+                      (as-fixups as))))
+
+; Allows the procedure proc of two arguments (code vector and current
+; location counter) to modify the code vector at will, at fixup time.
+
+(define (emit-fixup-proc! as proc)
+  (as-fixups! as (cons (list (as-lc as) 0 proc)
+                      (as-fixups as))))
+
+; Labels.
+
+; The current value of the location counter.
+
+(define (here as) (as-lc as))
+
+; Given a MAL label (a number), create an assembler label.
+
+(define (make-asm-label as label)
+  (let ((probe (find-label as label)))
+    (if probe
+       probe
+       (let ((l (cons label #f)))
+         (as-labels! as (cons l (as-labels as)))
+         l))))
+
+; This can use hashed lookup.
+
+(define (find-label as L)
+
+  (define (lookup-label-loop x labels parent)
+    (let ((entry (assq x labels)))
+      (cond (entry)
+           ((not parent) #f)
+           (else 
+            (lookup-label-loop x (as-labels parent) (as-parent parent))))))
+    
+  (lookup-label-loop L (as-labels as) (as-parent as)))
+
+; Create a new assembler label, distinguishable from a MAL label.
+
+(define new-label
+  (let ((n 0))
+    (lambda ()
+      (set! n (- n 1))
+      (cons n #f))))
+
+; Given a value name (a number), return the label value or #f.
+
+(define (label-value as L) (cdr L))
+
+; For peephole optimization.
+
+(define (next-instruction as)
+  (let ((source (as-source as)))
+    (if (null? source)
+        '(-1)
+        (car source))))
+
+(define (consume-next-instruction! as)
+  (as-source! as (cdr (as-source as))))
+
+(define (push-instruction as instruction)
+  (as-source! as (cons instruction (as-source as))))
+
+; For use by the machine assembler: assoc lists connected to as structure.
+
+(define (assembler-value as key)
+  (let ((probe (assq key (as-values as))))
+    (if probe
+       (cdr probe)
+       #f)))
+
+(define (assembler-value! as key value)
+  (let ((probe (assq key (as-values as))))
+    (if probe
+       (set-cdr! probe value)
+       (as-values! as (cons (cons key value) (as-values as))))))
+
+; For documentation.
+;
+; The value must be a documentation structure (a vector).
+
+(define (add-documentation as doc)
+  (let* ((existing-constants (cadr (car (as-constants as))))
+        (new-constants 
+         (twobit-sort (lambda (a b)
+                        (< (car a) (car b)))
+                      (cond ((not existing-constants)
+                             (list (cons (here as) doc)))
+                            ((pair? existing-constants)
+                             (cons (cons (here as) doc)
+                                   existing-constants))
+                            (else
+                             (list (cons (here as) doc)
+                                   (cons 0 existing-constants)))))))
+    (set-car! (cdar (as-constants as)) new-constants)))
+
+; This is called when a value is too large to be handled by the assembler.
+; Info is a string, expr an assembler expression, and val the resulting
+; value.  The default behavior is to signal an error.
+
+(define (asm-value-too-large as info expr val)
+  (if (as-retry as)
+      ((as-retry as))
+      (asm-error info ": Value too large: " expr " = " val)))
+
+; The implementations of asm-error and disasm-error depend on the host
+; system. Sigh.
+
+(define (asm-error msg . rest)
+  (cond ((eq? host-system 'chez)
+        (error 'assembler "~a" (list msg rest)))
+       (else
+        (apply error msg rest))))
+
+(define (disasm-error msg . rest)
+  (cond ((eq? host-system 'chez)
+        (error 'disassembler "~a" (list msg rest)))
+       (else
+        (apply error msg rest))))
+
+\f; The remaining procedures in this file are local to the assembler.
+
+; An assembly structure is a vector consisting of
+;
+;    table          (a table of assembly routines)
+;    source         (a list of symbolic instructions)
+;    lc             (location counter; an integer)
+;    code           (a list of bytevectors)
+;    constants      (a list)
+;    labels         (an alist of labels and values)
+;    fixups         (an alist of locations, sizes, and labels or fixnums)
+;    nested         (a list of assembly procedures for nested lambdas)
+;    values         (an assoc list)
+;    parent         (an assembly structure or #f)
+;    retry          (a thunk or #f)
+;    user-data      (anything)
+;
+; In fixups, labels are of the form (<L>) to distinguish them from fixnums.
+
+(define (label? x) (and (pair? x) (fixnum? (car x))))
+(define label.ident car)
+
+(define (make-assembly-structure source table user-data)
+  (vector table
+          source
+          0
+          '()
+          '()
+          '()
+          '()
+          '()
+         '()
+         #f
+         #f
+         user-data))
+
+(define (as-reset! as source)
+  (as-source! as source)
+  (as-lc! as 0)
+  (as-code! as '())
+  (as-constants! as '())
+  (as-labels! as '())
+  (as-fixups! as '())
+  (as-nested! as '())
+  (as-values! as '())
+  (as-retry! as #f))
+
+(define (as-table as)     (vector-ref as 0))
+(define (as-source as)    (vector-ref as 1))
+(define (as-lc as)        (vector-ref as 2))
+(define (as-code as)      (vector-ref as 3))
+(define (as-constants as) (vector-ref as 4))
+(define (as-labels as)    (vector-ref as 5))
+(define (as-fixups as)    (vector-ref as 6))
+(define (as-nested as)    (vector-ref as 7))
+(define (as-values as)    (vector-ref as 8))
+(define (as-parent as)    (vector-ref as 9))
+(define (as-retry as)     (vector-ref as 10))
+(define (as-user as)      (vector-ref as 11))
+
+(define (as-source! as x)    (vector-set! as 1 x))
+(define (as-lc! as x)        (vector-set! as 2 x))
+(define (as-code! as x)      (vector-set! as 3 x))
+(define (as-constants! as x) (vector-set! as 4 x))
+(define (as-labels! as x)    (vector-set! as 5 x))
+(define (as-fixups! as x)    (vector-set! as 6 x))
+(define (as-nested! as x)    (vector-set! as 7 x))
+(define (as-values! as x)    (vector-set! as 8 x))
+(define (as-parent! as x)    (vector-set! as 9 x))
+(define (as-retry! as x)     (vector-set! as 10 x))
+(define (as-user! as x)      (vector-set! as 11 x))
+
+; The guts of the assembler.
+
+(define (assemble1 as finalize doc)
+  (let ((assembly-table (as-table as))
+       (peep? (peephole-optimization))
+       (step? (single-stepping))
+       (step-instr (list $.singlestep))
+       (end-instr (list $.end)))
+
+    (define (loop)
+      (let ((source (as-source as)))
+        (if (null? source)
+           (begin ((vector-ref assembly-table $.end) end-instr as)
+                  (finalize as))
+            (begin (if step?
+                      ((vector-ref assembly-table $.singlestep)
+                       step-instr
+                       as))
+                  (if peep?
+                      (let peeploop ((src1 source))
+                        (peep as)
+                        (let ((src2 (as-source as)))
+                          (if (not (eq? src1 src2))
+                              (peeploop src2)))))
+                  (let ((source (as-source as)))
+                    (as-source! as (cdr source))
+                    ((vector-ref assembly-table (caar source))
+                     (car source)
+                     as)
+                    (loop))))))
+
+    (define (doit)
+      (emit-datum as doc)
+      (loop))
+
+    (let* ((source (as-source as))
+          (r (call-with-current-continuation
+              (lambda (k)
+                (as-retry! as (lambda () (k 'retry)))
+                (doit)))))
+      (if (eq? r 'retry)
+         (let ((old (short-effective-addresses)))
+           (as-reset! as source)
+           (dynamic-wind
+            (lambda ()
+              (short-effective-addresses #f))
+            doit
+            (lambda ()
+              (short-effective-addresses old))))
+         r))))
+
+(define (assemble-pasteup as)
+
+  (define (pasteup-code)
+    (let ((code      (make-bytevector (as-lc as)))
+         (constants (list->vector (as-constants as))))
+    
+      ; The bytevectors: byte 0 is most significant.
+
+      (define (paste-code! bvs i)
+       (if (not (null? bvs))
+           (let* ((bv (car bvs))
+                  (n  (bytevector-length bv)))
+             (do ((i i (- i 1))
+                  (j (- n 1) (- j 1))) ; (j 0 (+ j 1))
+                 ((< j 0)              ; (= j n)
+                  (paste-code! (cdr bvs) i))
+                (bytevector-set! code i (bytevector-ref bv j))))))
+    
+      (paste-code! (as-code as) (- (as-lc as) 1))
+      (as-code! as (list code))
+      (cons code constants)))
+
+  (define (pasteup-strings)
+    (let ((code      (make-string (as-lc as)))
+         (constants (list->vector (as-constants as))))
+
+      (define (paste-code! strs i)
+       (if (not (null? strs))
+           (let* ((s (car strs))
+                  (n (string-length s)))
+             (do ((i i (- i 1))
+                  (j (- n 1) (- j 1))) ; (j 0 (+ j 1))
+                 ((< j 0)              ; (= j n)
+                  (paste-code! (cdr strs) i))
+                (string-set! code i (string-ref s j))))))
+
+      (paste-code! (as-code as) (- (as-lc as) 1))
+      (as-code! as (list code))
+      (cons code constants)))
+
+  (if (bytevector? (car (as-code as)))
+      (pasteup-code)
+      (pasteup-strings)))
+
+(define (assemble-finalize! as)
+  (let ((code (car (as-code as))))
+
+    (define (apply-fixups! fixups)
+      (if (not (null? fixups))
+          (let* ((fixup      (car fixups))
+                 (i          (car fixup))
+                 (size       (cadr fixup))
+                 (adjustment (caddr fixup))  ; may be procedure
+                 (n          (if (label? adjustment)
+                                (lookup-label adjustment)
+                                adjustment)))
+            (case size
+             ((0) (fixup-proc code i n))
+              ((1) (fixup1 code i n))
+              ((2) (fixup2 code i n))
+              ((3) (fixup3 code i n))
+              ((4) (fixup4 code i n))
+              (else ???))
+            (apply-fixups! (cdr fixups)))))
+
+    (define (lookup-label L)
+      (or (label-value as (label.ident L))
+         (asm-error "Assembler error -- undefined label " L)))
+
+    (apply-fixups! (reverse! (as-fixups as)))
+
+    (for-each (lambda (nested-as-proc)
+               (nested-as-proc))
+             (as-nested as))))
+
+
+; These fixup routines assume a big-endian target machine.
+
+(define (fixup1 code i n)
+  (bytevector-set! code i (+ n (bytevector-ref code i))))
+
+(define (fixup2 code i n)
+  (let* ((x  (+ (* 256 (bytevector-ref code i))
+                (bytevector-ref code (+ i 1))))
+         (y  (+ x n))
+         (y0 (modulo y 256))
+         (y1 (modulo (quotient (- y y0) 256) 256)))
+    (bytevector-set! code i y1)
+    (bytevector-set! code (+ i 1) y0)))
+
+(define (fixup3 code i n)
+  (let* ((x  (+ (* 65536 (bytevector-ref code i))
+               (* 256 (bytevector-ref code (+ i 1)))
+                (bytevector-ref code (+ i 2))))
+         (y  (+ x n))
+         (y0 (modulo y 256))
+         (y1 (modulo (quotient (- y y0) 256) 256))
+         (y2 (modulo (quotient (- y (* 256 y1) y0) 256) 256)))
+    (bytevector-set! code i y2)
+    (bytevector-set! code (+ i 1) y1)
+    (bytevector-set! code (+ i 2) y0)))
+
+(define (fixup4 code i n)
+  (let* ((x  (+ (* 16777216 (bytevector-ref code i))
+               (* 65536 (bytevector-ref code (+ i 1)))
+               (* 256 (bytevector-ref code (+ i 2)))
+               (bytevector-ref code (+ i 3))))
+         (y  (+ x n))
+         (y0 (modulo y 256))
+         (y1 (modulo (quotient (- y y0) 256) 256))
+         (y2 (modulo (quotient (- y (* 256 y1) y0) 256) 256))
+         (y3 (modulo (quotient (- y (* 65536 y2)
+                                    (* 256 y1)
+                                    y0)
+                               256)
+                     256)))
+    (bytevector-set! code i y3)
+    (bytevector-set! code (+ i 1) y2)
+    (bytevector-set! code (+ i 2) y1)
+    (bytevector-set! code (+ i 3) y0)))
+
+(define (fixup-proc code i p)
+  (p code i))
+
+\f; For testing.
+
+(define (view-segment segment)
+  (define (display-bytevector bv)
+    (let ((n (bytevector-length bv)))
+      (do ((i 0 (+ i 1)))
+          ((= i n))
+          (if (zero? (remainder i 4))
+              (write-char #\space))
+          (if (zero? (remainder i 8))
+              (write-char #\space))
+          (if (zero? (remainder i 32))
+              (newline))
+          (let ((byte (bytevector-ref bv i)))
+            (write-char
+            (string-ref (number->string (quotient byte 16) 16) 0))
+            (write-char
+            (string-ref (number->string (remainder byte 16) 16) 0))))))
+  (if (and (pair? segment)
+           (bytevector? (car segment))
+           (vector? (cdr segment)))
+      (begin (display-bytevector (car segment))
+             (newline)
+             (write (cdr segment))
+             (newline)
+             (do ((constants (vector->list (cdr segment))
+                             (cdr constants)))
+                 ((or (null? constants)
+                      (null? (cdr constants))))
+                 (if (and (bytevector? (car constants))
+                          (vector? (cadr constants)))
+                     (view-segment (cons (car constants)
+                                         (cadr constants))))))))
+
+; emit is a procedure that takes an as and emits instructions into it.
+
+(define (test-asm emit)
+  (let ((as (make-assembly-structure #f #f #f)))
+    (emit as)
+    (let ((segment (assemble-pasteup as)))
+      (assemble-finalize! as)
+      (disassemble segment))))
+
+(define (compile&assemble x)
+  (view-segment (assemble (compile x))))
+
+; eof
+; Copyright 1998 Lars T Hansen.
+;
+; $Id: twobit.sch,v 1.3 1999/08/23 19:14:26 lth Exp $
+;
+; Common assembler -- miscellaneous utility procedures.
+
+; Given any Scheme object, return its printable representation as a string.
+; This code is largely portable (see comments).
+
+(define (format-object x)
+
+  (define (format-list x)
+    (define (loop x)
+      (cond ((null? x)
+            '(")"))
+           ((null? (cdr x))
+            (list (format-object (car x)) ")"))
+           (else
+            (cons (format-object (car x))
+                  (cons " " 
+                        (loop (cdr x)))))))
+    (apply string-append (cons "(" (loop x))))
+
+  (define (format-improper-list x)
+    (define (loop x)
+      (if (pair? (cdr x))
+         (cons (format-object (car x))
+               (cons " "
+                     (loop (cdr x))))
+         (list (format-object (car x))
+               " . "
+               (format-object (cdr x))
+               ")")))
+    (apply string-append (cons "(" (loop x))))
+
+  (cond ((null? x)             "()")
+       ((not x)               "#f")
+       ((eq? x #t)            "#t")
+       ((symbol? x)           (symbol->string x))
+       ((number? x)           (number->string x))
+       ((char? x)             (string x))
+       ((string? x)           x)
+       ((procedure? x)        "#<procedure>")
+       ((bytevector? x)       "#<bytevector>")     ; Larceny
+       ((eof-object? x)       "#<eof>")
+       ((port? x)             "#<port>")
+       ((eq? x (unspecified)) "#!unspecified")     ; Larceny
+       ((eq? x (undefined))   "#!undefined")       ; Larceny
+       ((vector? x)
+        (string-append "#" (format-list (vector->list x))))
+       ((list? x)
+        (format-list x))
+       ((pair? x)
+        (format-improper-list x))
+       (else                  "#<weird>")))
+
+; eof
+; Copyright 1998 Lars T Hansen.
+;
+; $Id: twobit.sch,v 1.3 1999/08/23 19:14:26 lth Exp $
+;
+; Larceny assembler -- 32-bit big-endian utility procedures.
+;
+; 32-bit numbers are represented as 4-byte bytevectors where byte 3
+; is the least significant and byte 0 is the most significant.
+;
+; Logically, the 'big' end is on the left and the 'little' end
+; is on the right, so a left shift shifts towards the 'big' end.
+;
+; Performance: poor, for good reasons.  See asmutil32.sch.
+
+; Identifies the code loaded.
+
+(define asm:endianness 'big)
+
+
+; Given four bytes, create a length-4 bytevector. 
+; N1 is the most significant byte, n4 the least significant.
+
+(define (asm:bv n1 n2 n3 n4)
+  (let ((bv (make-bytevector 4)))
+    (bytevector-set! bv 0 n1)
+    (bytevector-set! bv 1 n2)
+    (bytevector-set! bv 2 n3)
+    (bytevector-set! bv 3 n4)
+    bv))
+
+
+; Given a length-4 bytevector, convert it to an integer.
+
+(define (asm:bv->int bv)
+  (let ((i (+ (* (+ (* (+ (* (bytevector-ref bv 0) 256)
+                         (bytevector-ref bv 1))
+                      256)
+                   (bytevector-ref bv 2))
+                256)
+             (bytevector-ref bv 3))))
+    (if (> (bytevector-ref bv 0) 127)
+       (- i)
+       i)))
+
+
+; Shift the bits of m left by n bits, shifting in zeroes at the right end.
+; Returns a length-4 bytevector.
+;
+; M may be an exact integer or a length-4 bytevector.
+; N must be an exact nonnegative integer; it's interpreted modulo 33.
+
+(define (asm:lsh m n)
+  (if (not (bytevector? m))
+      (asm:lsh (asm:int->bv m) n)
+      (let ((m (bytevector-copy m))
+           (n (remainder n 33)))
+       (if (>= n 8)
+           (let ((k (quotient n 8)))
+             (do ((i 0 (+ i 1)))
+                 ((= (+ i k) 4)
+                  (do ((i i (+ i 1)))
+                      ((= i 4))
+                    (bytevector-set! m i 0)))
+               (bytevector-set! m i (bytevector-ref m (+ i k))))))
+       (let* ((d0 (bytevector-ref m 0))
+              (d1 (bytevector-ref m 1))
+              (d2 (bytevector-ref m 2))
+              (d3 (bytevector-ref m 3))
+              (n  (remainder n 8))
+              (n- (- 8 n)))
+         (asm:bv (logand (logior (lsh d0 n) (rshl d1 n-)) 255)
+                 (logand (logior (lsh d1 n) (rshl d2 n-)) 255)
+                 (logand (logior (lsh d2 n) (rshl d3 n-)) 255)
+                 (logand (lsh d3 n) 255))))))
+
+
+; Shift the bits of m right by n bits, shifting in zeroes at the high end.
+; Returns a length-4 bytevector.
+;
+; M may be an exact integer or a length-4 bytevector.
+; N must be an exact nonnegative integer; it's interpreted modulo 33.
+
+(define (asm:rshl m n)
+  (if (not (bytevector? m))
+      (asm:rshl (asm:int->bv m) n)
+      (let ((m (bytevector-copy m))
+           (n (remainder n 33)))
+       (if (>= n 8)
+           (let ((k (quotient n 8)))
+             (do ((i 3 (- i 1)))
+                 ((< (- i k) 0)
+                  (do ((i i (- i 1)))
+                      ((< i 0))
+                    (bytevector-set! m i 0)))
+               (bytevector-set! m i (bytevector-ref m (- i k))))))
+       (let* ((d0 (bytevector-ref m 0))
+              (d1 (bytevector-ref m 1))
+              (d2 (bytevector-ref m 2))
+              (d3 (bytevector-ref m 3))
+              (n  (remainder n 8))
+              (n- (- 8 n)))
+         (asm:bv (rshl d0 n)
+                 (logand (logior (rshl d1 n) (lsh d0 n-)) 255)
+                 (logand (logior (rshl d2 n) (lsh d1 n-)) 255)
+                 (logand (logior (rshl d3 n) (lsh d2 n-)) 255))))))
+
+
+; Shift the bits of m right by n bits, shifting in the sign bit at the
+; high end.  Returns a length-4 bytevector.
+;
+; M may be an exact integer or a length-4 bytevector.
+; N must be an exact nonnegative integer; it's interpreted modulo 33.
+
+(define asm:rsha
+  (let ((ones (asm:bv #xff #xff #xff #xff)))
+    (lambda (m n)
+      (let* ((m (if (bytevector? m) m (asm:int->bv m)))
+            (n (remainder n 33))
+            (h (rshl (bytevector-ref m 0) 7))
+            (k (asm:rshl m n)))
+;      (format #t "~a ~a ~a~%" h (bytevector-ref m 0) n)
+;      (prnx (asm:lsh ones (- 32 n))) (newline)
+       (if (zero? h)
+           k
+           (asm:logior k (asm:lsh ones (- 32 n))))))))
+
+; eof
+; Copyright 1998 Lars T Hansen.
+;
+; $Id: twobit.sch,v 1.3 1999/08/23 19:14:26 lth Exp $
+;
+; Larceny assembler -- 32-bit endianness-independent utility procedures.
+;
+; 32-bit numbers are represented as 4-byte bytevectors where the
+; exact layout depends on whether the little-endian or big-endian
+; module has been loaded.  One of them must be loaded prior to loading
+; this module.
+;
+; Logically, the 'big' end is on the left and the 'little' end
+; is on the right, so a left shift shifts towards the big end.
+;
+; Generally, performance is not a major issue in this module.  The 
+; assemblers should use more specialized code for truly good performance.
+; These procedures are mainly suitable for one-time construction of 
+; instruction templates, and during development.
+;
+; Endian-ness specific operations are in asmutil32be.sch and asmutil32le.sch:
+;
+;   (asm:bv n0 n1 n2 n3)    ; Construct bytevector
+;   (asm:bv->int bv)        ; Convert bytevector to integer
+;   (asm:lsh m k)           ; Shift left logical k bits
+;   (asm:rshl m k)          ; Shift right logical k bits
+;   (asm:rsha m k)          ; Shirt right arithmetic k bits
+
+
+; Convert an integer to a length-4 bytevector using two's complement 
+; representation for negative numbers.
+; Returns length-4 bytevector.
+;
+; The procedure handles numbers in the range -2^31..2^32-1 [sic].
+; It is an error for the number to be outside this range.
+;
+; FIXME: quotient/remainder may be slow; we could have special fixnum
+;        case that uses shifts (that could be in-lined as macro).  It could
+;        work for negative numbers too.
+; FIXME: should probably check that the number is within range.
+
+(define asm:int->bv
+  (let ((two^32 (expt 2 32)))
+    (lambda (m)
+      (let* ((m  (if (< m 0) (+ two^32 m) m))
+            (b0 (remainder m 256))
+            (m  (quotient m 256))
+            (b1 (remainder m 256))
+            (m  (quotient m 256))
+            (b2 (remainder m 256))
+            (m  (quotient m 256))
+            (b3 (remainder m 256)))
+       (asm:bv b3 b2 b1 b0)))))
+
+
+; `Or' the bits of multiple operands together. 
+; Each operand may be an exact integer or a length-4 bytevector.
+; Returns a length-4 bytevector.
+
+(define (asm:logior . ops)
+  (let ((r (asm:bv 0 0 0 0)))
+    (do ((ops ops (cdr ops)))
+       ((null? ops) r)
+      (let* ((op (car ops))
+            (op (if (bytevector? op) op (asm:int->bv op))))
+       (bytevector-set! r 0 (logior (bytevector-ref r 0)
+                                    (bytevector-ref op 0)))
+       (bytevector-set! r 1 (logior (bytevector-ref r 1)
+                                    (bytevector-ref op 1)))
+       (bytevector-set! r 2 (logior (bytevector-ref r 2)
+                                    (bytevector-ref op 2)))
+       (bytevector-set! r 3 (logior (bytevector-ref r 3)
+                                    (bytevector-ref op 3)))))))
+
+
+; `And' the bits of two operands together.
+; Either may be an exact integer or length-4 bytevector.
+; Returns length-4 bytevector.
+
+(define (asm:logand op1 op2)
+  (let ((op1 (if (bytevector? op1) op1 (asm:int->bv op1)))
+       (op2 (if (bytevector? op2) op2 (asm:int->bv op2)))
+       (bv  (make-bytevector 4)))
+    (bytevector-set! bv 0 (logand (bytevector-ref op1 0)
+                                 (bytevector-ref op2 0)))
+    (bytevector-set! bv 1 (logand (bytevector-ref op1 1)
+                                 (bytevector-ref op2 1)))
+    (bytevector-set! bv 2 (logand (bytevector-ref op1 2)
+                                 (bytevector-ref op2 2)))
+    (bytevector-set! bv 3 (logand (bytevector-ref op1 3)
+                                 (bytevector-ref op2 3)))
+    bv))
+
+
+; Extract the n low-order bits of m.
+; m may be an exact integer or a length-4 bytevector.
+; n must be an exact nonnegative integer, interpreted modulo 32.
+; Returns length-4 bytevector.
+;
+; Does not depend on endian-ness.
+
+(define asm:lobits 
+  (let ((v (make-vector 33)))
+    (do ((i 0 (+ i 1)))
+       ((= i 33))
+      (vector-set! v i (asm:int->bv (- (expt 2 i) 1))))
+    (lambda (m n)
+      (asm:logand m (vector-ref v (remainder n 33))))))
+
+; Extract the n high-order bits of m.
+; m may be an exact integer or a length-4 bytevector.
+; n must be an exact nonnegative integer, interpreted modulo 33.
+; Returns length-4 bytevector with the high-order bits of m at low end.
+;
+; Does not depend on endian-ness.
+
+(define (asm:hibits m n)
+  (asm:rshl m (- 32 (remainder n 33))))
+
+; Test that the given number (not! bytevector) m fits in an n-bit 
+; signed slot.
+;
+; Does not depend on endian-ness.
+
+(define asm:fits?
+  (let ((v (make-vector 33)))
+    (do ((i 0 (+ i 1)))
+       ((= i 33))
+      (vector-set! v i (expt 2 i)))
+    (lambda (m n)
+      (<= (- (vector-ref v (- n 1))) m (- (vector-ref v (- n 1)) 1)))))
+
+; Test that the given number (not! bytevector) m fits in an n-bit 
+; unsigned slot.
+;
+; Does not depend on endian-ness.
+
+(define asm:fits-unsigned?
+  (let ((v (make-vector 33)))
+    (do ((i 0 (+ i 1)))
+       ((= i 33))
+      (vector-set! v i (expt 2 i)))
+    (lambda (m n)
+      (<= 0 m (- (vector-ref v n) 1)))))
+
+; Add two operands (numbers or bytevectors).
+;
+; Does not depend on endian-ness.
+
+(define (asm:add a b)
+  (asm:int->bv (+ (if (bytevector? a) (asm:bv->int a) a)
+                 (if (bytevector? b) (asm:bv->int b) b))))
+
+; Given an unsigned 32-bit number, return it as a signed number
+; as appropriate.
+;
+; Does not depend on endian-ness.
+
+(define (asm:signed n)
+  (if (< n 2147483647)
+      n
+      (- n 4294967296)))
+
+
+(define (asm:print-bv bv)
+
+  (define hex "0123456789abcdef")
+
+  (define (pdig k)
+    (display (string-ref hex (quotient k 16)))
+    (display (string-ref hex (remainder k 16)))
+    (display " "))
+  
+  (if (eq? asm:endianness 'little)
+      (do ((i 3 (- i 1)))
+         ((< i 0))
+       (pdig (bytevector-ref bv i)))
+      (do ((i 0 (+ i 1)))
+         ((= i 4))
+       (pdig (bytevector-ref bv i)))))
+
+
+; eof
+; Copyright 1998 Lars T Hansen.
+;
+; $Id: twobit.sch,v 1.3 1999/08/23 19:14:26 lth Exp $
+;
+; Procedure that writes fastload segment.
+;
+; The procedure 'dump-fasl-segment-to-port' takes a segment and an output
+; port as arguments and dumps the segment in fastload format on that port.
+; The port must be a binary (untranslated) port.
+;
+; A fastload segment looks like a Scheme expression, and in fact, 
+; fastload files can mix compiled and uncompiled expressions.  A compiled
+; expression (as created by dump-fasl-segment-to-port) is a list with
+; a literal procedure in the operator position and no arguments.
+;
+; A literal procedure is a three-element list prefixed by #^P.  The three
+; elements are code (a bytevector), constants (a regular vector), and
+; R0/static link slot (always #f).  
+;
+; A bytevector is a string prefixed by #^B. The string may contain 
+; control characters; \ and " must be quoted as usual.
+;
+; A global variable reference in the constant vector is a symbol prefixed
+; by #^G.  On reading, the reference is replaced by (a pointer to) the 
+; actual cell.
+;
+; This code is highly bummed.  The procedure write-bytevector-like has the
+; same meaning as display, but in Larceny, the former is currently much
+; faster than the latter.
+
+(define (dump-fasl-segment-to-port segment outp . rest)
+  (let* ((omit-code? (not (null? rest)))
+         (controllify
+         (lambda (char)
+           (integer->char (- (char->integer char) (char->integer #\@)))))
+        (CTRLP       (controllify #\P))
+        (CTRLB       (controllify #\B))
+        (CTRLG       (controllify #\G))
+        (DOUBLEQUOTE (char->integer #\"))
+        (BACKSLASH   (char->integer #\\))
+        (len         1024))
+
+    (define buffer (make-string len #\&))
+    (define ptr 0)
+
+    (define (flush)
+      (if (< ptr len)
+         (write-bytevector-like (substring buffer 0 ptr) outp)
+         (write-bytevector-like buffer outp))
+      (set! ptr 0))
+
+    (define (putc c)
+      (if (= ptr len) (flush))
+      (string-set! buffer ptr c)
+      (set! ptr (+ ptr 1)))
+
+    (define (putb b)
+      (if (= ptr len) (flush))
+      (string-set! buffer ptr (integer->char b))
+      (set! ptr (+ ptr 1)))
+
+    (define (puts s)
+      (let ((ls (string-length s)))
+       (if (>= (+ ptr ls) len)
+           (begin (flush)
+                  (write-bytevector-like s outp))
+           (do ((i (- ls 1) (- i 1))
+                (p (+ ptr ls -1) (- p 1)))
+               ((< i 0)
+                (set! ptr (+ ptr ls)))
+             (string-set! buffer p (string-ref s i))))))
+
+    (define (putd d)
+      (flush)
+      (write-fasl-datum d outp))
+
+    (define (dump-codevec bv)
+      (if omit-code?
+          (puts "#f")
+          (begin
+            (putc #\#)
+            (putc CTRLB)
+            (putc #\")
+            (let ((limit (bytevector-length bv)))
+              (do ((i 0 (+ i 1)))
+                  ((= i limit) (putc #\")
+                               (putc #\newline))
+                (let ((c (bytevector-ref bv i)))
+                  (cond ((= c DOUBLEQUOTE) (putc #\\))
+                        ((= c BACKSLASH)   (putc #\\)))
+                  (putb c)))))))
+
+    (define (dump-constvec cv)
+      (puts "#(")
+      (for-each (lambda (const)
+                 (putc #\space)
+                 (case (car const)
+                   ((data)
+                    (putd (cadr const)))
+                   ((constantvector)
+                    (dump-constvec (cadr const)))
+                   ((codevector)
+                    (dump-codevec (cadr const)))
+                   ((global)
+                    (putc #\#)
+                    (putc CTRLG)
+                    (putd (cadr const)))
+                   ((bits)
+                    (error "BITS attribute is not supported in fasl files."))
+                   (else
+                    (error "Faulty .lop file."))))
+               (vector->list cv))
+      (puts ")")
+      (putc #\newline))
+
+    (define (dump-fasl-segment segment)
+      (if (not omit-code?) (putc #\())
+      (putc #\#)
+      (putc CTRLP)
+      (putc #\()
+      (dump-codevec (car segment))
+      (putc #\space)
+      (dump-constvec (cdr segment))
+      (puts " #f)")
+      (if (not omit-code?) (putc #\)))
+      (putc #\newline))
+
+    (dump-fasl-segment segment)
+    (flush)))
+
+; eof
+; Copyright 1998 Lars T Hansen.
+;
+; $Id: twobit.sch,v 1.3 1999/08/23 19:14:26 lth Exp $
+;
+; Bootstrap heap dumper.
+;
+; Usage: (build-heap-image outputfile inputfile-list)
+;
+; Each input file is a sequence of segments, the structure of which 
+; depends on the target architecture, but at least segment.code and 
+; segment.constants exist as accessors.
+;
+; The code is a bytevector.  The constant vector contains tagged 
+; entries (represented using length-2 lists), where the tags are
+; `data', `codevector', `constantvector', `global', or `bits'.
+;
+; `build-heap-image' reads its file arguments into the heap, creates 
+; thunks from the segments, and creates a list of the thunks.  It also 
+; creates a list of all symbols present in the loaded files.  Finally, 
+; it generates an initialization procedure (the LAP of which is hardcoded
+; into this file; see below).  A pointer to this procedure is installed 
+; in the SCHEME_ENTRY root pointer; hence, this procedure (a thunk, as 
+; it were) is called when the heap image is loaded.
+;
+; The initialization procedure calls each procedure in the thunk list in 
+; order.  It then invokes the procedure `go', which takes one argument:
+; the list of symbols.  Typically, `go' will initialize the symbol table
+; and other system tables and then call `main', but this is by no means
+; required.
+;
+; The Scheme assembler must be co-resident, since it is used by 
+; `build-heap-image' procedure to assemble the final startup code.  This
+; could be avoided by pre-assembling the code and patching it here, but 
+; the way it is now, this procedure is entirely portable -- no target
+; dependencies.
+;
+; The code is structured to allow most procedures to be overridden for
+; target architectures with more complex needs (notably the C backend).
+
+(define generate-global-symbols
+  (make-twobit-flag 'generate-global-symbols))
+(generate-global-symbols #t)
+
+(define heap.version-number 9)         ; Heap version number
+
+(define heap.root-names                        ; Roots in heap version 9
+  '(result argreg2 argreg3 
+    reg0 reg1 reg2 reg3 reg3 reg5 reg6 reg7 reg8 reg9 reg10 reg11 reg12
+    reg13 reg14 reg15 reg16 reg17 reg18 reg19 reg20 reg21 reg22 reg23
+    reg24 reg25 reg26 reg27 reg28 reg29 reg30 reg31 
+    cont startup callouts schcall-arg4 alloci-tmp))
+    
+(define (build-heap-image output-file input-files)
+
+  (define tmp-file "HEAPDATA.dat")
+
+  (define (process-input-files heap)
+    (let loop ((files input-files) (inits '()))
+      (cond ((null? files)
+            (heap.thunks! heap (apply append inits)))
+           (else
+            (let ((filename (car files)))
+              (display "Loading ")
+              (display filename)
+              (newline)
+              (loop (cdr files)
+                    (append inits (list (dump-file! heap filename)))))))))
+
+  (delete-file tmp-file)
+  (let ((heap  (make-heap #f (open-output-file tmp-file))))
+    (before-all-files heap output-file input-files)
+    (process-input-files heap)
+    (heap.set-root! heap
+                   'startup
+                   (dump-startup-procedure! heap))
+    (heap.set-root! heap
+                   'callouts
+                   (dump-global! heap 'millicode-support))
+    (write-header heap output-file)
+    (after-all-files heap output-file input-files)
+    (close-output-port (heap.output-port heap))
+    (append-file-shell-command tmp-file output-file)
+    (load-map heap)
+    (unspecified)))
+
+(define (before-all-files heap output-file-name input-file-names) #t)
+(define (after-all-files heap output-file-name input-file-names) #t)
+
+; Public
+;
+; A 'heap' is a data structure with the following public fields; none
+; of them are constant unless so annotated:
+;
+;  version          a fixnum (constant) - heap type version number
+;  roots            an assoc list that maps root names to values
+;  top              an exact nonnegative integer: the address of the 
+;                   next byte to be emitted
+;  symbol-table     a symbol table abstract data type
+;  extra            any value - a client-extension field
+;  output-port      an output port (for the data stream)
+;  thunks           a list of codevector addresses
+;
+; Bytes are emitted with the heap.byte! and heap.word! procedures,
+; which emit a byte and a 4-byte word respectively.  These update
+; the top field.
+
+(define (make-heap extra output-port)
+  (vector heap.version-number        ; version
+         '()                        ; roots
+         0                          ; top
+         (make-heap-symbol-table)   ; symtab
+         extra                      ; extra
+         output-port                ; output port
+         '()                        ; thunks
+         ))
+
+(define (heap.version h) (vector-ref h 0))
+(define (heap.roots h) (vector-ref h 1))
+(define (heap.top h) (vector-ref h 2))
+(define (heap.symbol-table h) (vector-ref h 3))
+(define (heap.extra h) (vector-ref h 4))
+(define (heap.output-port h) (vector-ref h 5))
+(define (heap.thunks h) (vector-ref h 6))
+
+(define (heap.roots! h x) (vector-set! h 1 x))
+(define (heap.top! h x) (vector-set! h 2 x))
+(define (heap.thunks! h x) (vector-set! h 6 x))
+
+
+; Symbol table.
+;
+; The symbol table maps names to symbol structures, and a symbol 
+; structure contains information about that symbol.
+;
+; The structure has four fields:
+;   name      a symbol - the print name
+;   symloc    a fixnum or null - if fixnum, the location in the
+;             heap of the symbol structure.
+;   valloc    a fixnum or null - if fixnum, the location in the
+;             heap of the global variable cell that has this
+;             symbol for its name.
+;   valno     a fixnum or null - if fixnum, the serial number of
+;             the global variable cell (largely obsolete).
+;
+; Note therefore that the symbol table maintains information about
+; whether the symbol is used as a symbol (in a datum), as a global
+; variable, or both.
+
+(define (make-heap-symbol-table)
+  (vector '() 0))
+
+(define (symtab.symbols st) (vector-ref st 0))
+(define (symtab.cell-no st) (vector-ref st 1))
+
+(define (symtab.symbols! st x) (vector-set! st 0 x))
+(define (symtab.cell-no! st x) (vector-set! st 1 x))
+
+(define (make-symcell name)
+  (vector name '() '() '()))
+
+(define (symcell.name sc) (vector-ref sc 0))    ; name
+(define (symcell.symloc sc) (vector-ref sc 1))  ; symbol location (if any)
+(define (symcell.valloc sc) (vector-ref sc 2))  ; value cell location (ditto)
+(define (symcell.valno sc) (vector-ref sc 3))   ; value cell number (ditto)
+
+(define (symcell.symloc! sc x) (vector-set! sc 1 x))
+(define (symcell.valloc! sc x) (vector-set! sc 2 x))
+(define (symcell.valno! sc x) (vector-set! sc 3 x))
+
+; Find a symcell in the table, or make a new one if there's none.
+
+(define (symbol-cell h name)
+  (let ((symtab (heap.symbol-table h)))
+    (let loop ((symbols (symtab.symbols symtab)))
+      (cond ((null? symbols)
+            (let ((new-sym (make-symcell name)))
+              (symtab.symbols! symtab (cons new-sym
+                                            (symtab.symbols symtab)))
+              new-sym))
+           ((eq? name (symcell.name (car symbols)))
+            (car symbols))
+           (else
+            (loop (cdr symbols)))))))
+
+
+; Fundamental data emitters
+
+(define twofiftysix^3 (* 256 256 256))
+(define twofiftysix^2 (* 256 256))
+(define twofiftysix   256)
+
+(define (heap.word-be! h w)
+  (heap.byte! h (quotient w twofiftysix^3))
+  (heap.byte! h (quotient (remainder w twofiftysix^3) twofiftysix^2))
+  (heap.byte! h (quotient (remainder w twofiftysix^2) twofiftysix))
+  (heap.byte! h (remainder w twofiftysix)))
+
+(define (heap.word-el! h w)
+  (heap.byte! h (remainder w twofiftysix))
+  (heap.byte! h (quotient (remainder w twofiftysix^2) twofiftysix))
+  (heap.byte! h (quotient (remainder w twofiftysix^3) twofiftysix^2))
+  (heap.byte! h (quotient w twofiftysix^3)))
+
+(define heap.word! heap.word-be!)
+
+(define (dumpheap.set-endianness! which)
+  (case which
+    ((big) (set! heap.word! heap.word-be!))
+    ((little) (set! heap.word! heap.word-el!))
+    (else ???)))
+
+(define (heap.byte! h b)
+  (write-char (integer->char b) (heap.output-port h))
+  (heap.top! h (+ 1 (heap.top h))))
+
+
+; Useful abstractions and constants.
+
+(define (heap.header-word! h immediate length)
+  (heap.word! h (+ (* length 256) immediate)))
+
+(define (heap.adjust! h)
+  (let ((p (heap.top h)))
+    (let loop ((i (- (* 8 (quotient (+ p 7) 8)) p)))
+      (if (zero? i)
+         '()
+         (begin (heap.byte! h 0)
+                (loop (- i 1)))))))
+  
+(define heap.largest-fixnum (- (expt 2 29) 1))
+(define heap.smallest-fixnum (- (expt 2 29)))
+
+(define (heap.set-root! h name value)
+  (heap.roots! h (cons (cons name value) (heap.roots h))))
+
+
+;;; The segment.* procedures may be overridden by custom code.
+
+(define segment.code car)
+(define segment.constants cdr)
+
+;;; The dump-*! procedures may be overridden by custom code.
+
+; Load a LOP file into the heap, create a thunk in the heap to hold the
+; code and constant vector, and return the list of thunk addresses in
+; the order dumped.
+
+(define (dump-file! h filename)
+  (before-dump-file h filename)
+  (call-with-input-file filename
+    (lambda (in)
+      (do ((segment (read in) (read in))
+          (thunks  '() (cons (dump-segment! h segment) thunks)))
+         ((eof-object? segment)
+          (after-dump-file h filename)
+          (reverse thunks))))))
+
+(define (before-dump-file h filename) #t)
+(define (after-dump-file h filename) #t)
+
+; Dump a segment and return the heap address of the resulting thunk.
+
+(define (dump-segment! h segment)
+  (let* ((the-code   (dump-codevector! h (segment.code segment)))
+        (the-consts (dump-constantvector! h (segment.constants segment))))
+    (dump-thunk! h the-code the-consts)))
+
+(define (dump-tagged-item! h item)
+  (case (car item)
+    ((codevector)
+     (dump-codevector! h (cadr item)))
+    ((constantvector)
+     (dump-constantvector! h (cadr item)))
+    ((data)
+     (dump-datum! h (cadr item)))
+    ((global)
+     (dump-global! h (cadr item)))
+    ((bits)
+     (cadr item))
+    (else
+     (error 'dump-tagged-item! "Unknown item ~a" item))))
+
+(define (dump-datum! h datum)
+
+  (define (fixnum? x)
+    (and (integer? x)
+        (exact? x)
+        (<= heap.smallest-fixnum x heap.largest-fixnum)))
+
+  (define (bignum? x)
+    (and (integer? x)
+        (exact? x)
+        (or (> x heap.largest-fixnum)
+            (< x heap.smallest-fixnum))))
+
+  (define (ratnum? x)
+    (and (rational? x) (exact? x) (not (integer? x))))
+
+  (define (flonum? x)
+    (and (real? x) (inexact? x)))
+
+  (define (compnum? x)
+    (and (complex? x) (inexact? x) (not (real? x))))
+
+  (define (rectnum? x)
+    (and (complex? x) (exact? x) (not (real? x))))
+
+  (cond ((fixnum? datum)
+        (dump-fixnum! h datum))
+       ((bignum? datum)
+        (dump-bignum! h datum))
+       ((ratnum? datum)
+        (dump-ratnum! h datum))
+       ((flonum? datum)
+        (dump-flonum! h datum))
+       ((compnum? datum)
+        (dump-compnum! h datum))
+       ((rectnum? datum)
+        (dump-rectnum! h datum))
+       ((char? datum)
+        (dump-char! h datum))
+       ((null? datum)
+        $imm.null)
+       ((eq? datum #t)
+        $imm.true)
+       ((eq? datum #f)
+        $imm.false)
+       ((equal? datum (unspecified))
+        $imm.unspecified)
+       ((equal? datum (undefined))
+        $imm.undefined)
+       ((vector? datum)
+        (dump-vector! h datum $tag.vector-typetag))
+       ((bytevector? datum)
+        (dump-bytevector! h datum $tag.bytevector-typetag))
+       ((pair? datum)
+        (dump-pair! h datum))
+       ((string? datum)
+        (dump-string! h datum))
+       ((symbol? datum)
+        (dump-symbol! h datum))
+       (else
+        (error 'dump-datum! "Unsupported type of datum ~a" datum))))
+
+; Returns the two's complement representation as a positive number.
+
+(define (dump-fixnum! h f)
+  (if (negative? f)
+      (- #x100000000 (* (abs f) 4))
+      (* 4 f)))
+
+(define (dump-char! h c)
+  (+ (* (char->integer c) twofiftysix^2) $imm.character))
+
+(define (dump-bignum! h b)
+  (dump-bytevector! h (bignum->bytevector b) $tag.bignum-typetag))
+
+(define (dump-ratnum! h r)
+  (dump-vector! h 
+               (vector (numerator r) (denominator r)) 
+               $tag.ratnum-typetag))
+
+(define (dump-flonum! h f)
+  (dump-bytevector! h (flonum->bytevector f) $tag.flonum-typetag))
+
+(define (dump-compnum! h c)
+  (dump-bytevector! h (compnum->bytevector c) $tag.compnum-typetag))
+
+(define (dump-rectnum! h r)
+  (dump-vector! h
+               (vector (real-part r) (imag-part r))
+               $tag.rectnum-typetag))
+
+(define (dump-string! h s)
+  (dump-bytevector! h (string->bytevector s) $tag.string-typetag))
+
+(define (dump-pair! h p)
+  (let ((the-car (dump-datum! h (car p)))
+       (the-cdr (dump-datum! h (cdr p))))
+    (let ((base (heap.top h)))
+      (heap.word! h the-car)
+      (heap.word! h the-cdr)
+      (+ base $tag.pair-tag))))
+
+(define (dump-bytevector! h bv variation)
+  (let ((base (heap.top h))
+       (l    (bytevector-length bv)))
+    (heap.header-word! h (+ $imm.bytevector-header variation) l)
+    (let loop ((i 0))
+      (if (< i l)
+         (begin (heap.byte! h (bytevector-ref bv i))
+                (loop (+ i 1)))
+         (begin (heap.adjust! h)
+                (+ base $tag.bytevector-tag))))))
+
+(define (dump-vector! h v variation)
+  (dump-vector-like! h v dump-datum! variation))
+
+(define (dump-vector-like! h cv recur! variation)
+  (let* ((l (vector-length cv))
+        (v (make-vector l '())))
+    (let loop ((i 0))
+      (if (< i l)
+         (begin (vector-set! v i (recur! h (vector-ref cv i)))
+                (loop (+ i 1)))
+         (let ((base (heap.top h)))
+           (heap.header-word! h (+ $imm.vector-header variation) (* l 4))
+           (let loop ((i 0))
+             (if (< i l)
+                 (begin (heap.word! h (vector-ref v i))
+                        (loop (+ i 1)))
+                 (begin (heap.adjust! h)
+                        (+ base $tag.vector-tag)))))))))
+
+(define (dump-codevector! h cv)
+  (dump-bytevector! h cv $tag.bytevector-typetag))
+
+(define (dump-constantvector! h cv)
+  (dump-vector-like! h cv dump-tagged-item! $tag.vector-typetag))
+
+(define (dump-symbol! h s)
+  (let ((x (symbol-cell h s)))
+    (if (null? (symcell.symloc x))
+       (symcell.symloc! x (create-symbol! h s)))
+    (symcell.symloc x)))
+
+(define (dump-global! h g)
+  (let ((x (symbol-cell h g)))
+    (if (null? (symcell.valloc x))
+       (let ((cell (create-cell! h g)))
+         (symcell.valloc! x (car cell))
+         (symcell.valno! x (cdr cell))))
+    (symcell.valloc x)))
+
+(define (dump-thunk! h code constants)
+  (let ((base (heap.top h)))
+    (heap.header-word! h $imm.procedure-header 8)
+    (heap.word! h code)
+    (heap.word! h constants)
+    (heap.adjust! h)
+    (+ base $tag.procedure-tag)))
+
+; The car's are all heap pointers, so they should not be messed with.
+; The cdr must be dumped, and then the pair.
+
+(define (dump-list-spine! h l)
+  (if (null? l)
+      $imm.null
+      (let ((the-car (car l))
+           (the-cdr (dump-list-spine! h (cdr l))))
+       (let ((base (heap.top h)))
+         (heap.word! h the-car)
+         (heap.word! h the-cdr)
+         (+ base $tag.pair-tag)))))
+
+(define (dump-startup-procedure! h)
+  (let ((thunks  (dump-list-spine! h (heap.thunks h)))
+       (symbols (dump-list-spine! h (symbol-locations h))))
+    (dump-segment! h (construct-startup-procedure symbols thunks))))
+
+; The initialization procedure. The lists are magically patched into
+; the constant vector after the procedure has been assembled but before
+; it is dumped into the heap. See below.
+;
+; (define (init-proc argv)
+;   (let loop ((l <list-of-thunks>))
+;     (if (null? l)
+;         (go <list-of-symbols> argv)
+;         (begin ((car l))
+;                (loop (cdr l))))))
+
+(define init-proc
+  `((,$.proc)
+    (,$args= 1)
+    (,$reg 1)                          ; argv into
+    (,$setreg 2)                       ;   register 2
+    (,$const (thunks))                 ; dummy list of thunks.
+    (,$setreg 1)
+    (,$.label 0)
+    (,$reg 1)
+    (,$op1 null?)                      ; (null? l)
+    (,$branchf 2)
+    (,$const (symbols))                        ; dummy list of symbols
+    (,$setreg 1)
+    (,$global go)
+    ;(,$op1 break)
+    (,$invoke 2)                       ; (go <list of symbols> argv)
+    (,$.label 2)
+    (,$save 2)
+    (,$store 0 0)
+    (,$store 1 1)
+    (,$store 2 2)
+    (,$setrtn 3)
+    (,$reg 1)
+    (,$op1 car)
+    (,$invoke 0)                       ; ((car l))
+    (,$.label 3)
+    (,$.cont)
+    (,$restore 2)
+    (,$pop 2)
+    (,$reg 1)
+    (,$op1 cdr)
+    (,$setreg 1)
+    (,$branch 0)))                     ; (loop (cdr l))
+
+
+;;; Non-overridable code beyond this point
+
+; Stuff a new symbol into the heap, return its location.
+
+(define (create-symbol! h s)
+  (dump-vector-like!
+   h 
+   (vector `(bits ,(dump-string! h (symbol->string s)))
+          '(data 0)
+          '(data ()))
+   dump-tagged-item!
+   $tag.symbol-typetag))
+
+
+; Stuff a value cell into the heap, return a pair of its location
+; and its cell number.
+
+(define (create-cell! h s)
+  (let* ((symtab (heap.symbol-table h))
+        (n (symtab.cell-no symtab))
+        (p (dump-pair! h (cons (undefined)
+                               (if (generate-global-symbols)
+                                   s
+                                   n)))))
+    (symtab.cell-no! symtab (+ n 1))
+    (cons p n)))
+
+
+(define (construct-startup-procedure symbol-list-addr init-list-addr)
+
+  ; Given some value which might appear in the constant vector, 
+  ; replace the entries matching that value with a new value.
+
+  (define (patch-constant-vector! v old new)
+    (let loop ((i (- (vector-length v) 1)))
+      (if (>= i 0)
+         (begin (if (equal? (vector-ref v i) old)
+                    (vector-set! v i new))
+                (loop (- i 1))))))
+
+  ; Assemble the startup thunk, patch it, and return it.
+
+  (display "Assembling final procedure") (newline)
+  (let ((e (single-stepping)))
+    (single-stepping #f)
+    (let ((segment (assemble init-proc)))
+      (single-stepping e)
+      (patch-constant-vector! (segment.constants segment)
+                             '(data (thunks))
+                             `(bits ,init-list-addr))
+      (patch-constant-vector! (segment.constants segment)
+                             '(data (symbols))
+                             `(bits ,symbol-list-addr))
+      segment)))
+
+
+; Return a list of symbol locations for symbols in the heap, in order.
+
+(define (symbol-locations h)
+  (let loop ((symbols (symtab.symbols (heap.symbol-table h))) (res '()))
+    (cond ((null? symbols)
+          (reverse res))
+         ((not (null? (symcell.symloc (car symbols))))
+          (loop (cdr symbols)
+                (cons (symcell.symloc (car symbols)) res)))
+         (else
+          (loop (cdr symbols) res)))))
+
+; Return list of variable name to cell number mappings for global vars.
+
+(define (load-map h)
+  (let loop ((symbols (symtab.symbols (heap.symbol-table h))) (res '()))
+    (cond ((null? symbols)
+          (reverse res))
+         ((not (null? (symcell.valloc (car symbols))))
+          (loop (cdr symbols)
+                (cons (cons (symcell.name (car symbols))
+                            (symcell.valno (car symbols)))
+                      res)))
+         (else
+          (loop (cdr symbols) res)))))
+
+
+(define (write-header h output-file)
+  (delete-file output-file)
+  (call-with-output-file output-file
+    (lambda (out)
+
+      (define (write-word w)
+       (display (integer->char (quotient w twofiftysix^3)) out)
+       (display (integer->char (quotient (remainder w twofiftysix^3) 
+                                         twofiftysix^2))
+                out)
+       (display (integer->char (quotient (remainder w twofiftysix^2) 
+                                         twofiftysix))
+                out)
+       (display (integer->char (remainder w twofiftysix)) out))
+
+      (define (write-roots)
+       (let ((assigned-roots (heap.roots h)))
+         (for-each (lambda (root-name)
+                     (let ((probe (assq root-name assigned-roots)))
+                       (if probe
+                           (write-word (cdr probe))
+                           (write-word $imm.false))))
+                   heap.root-names)))
+
+      (write-word heap.version-number)
+      (write-roots)
+      (write-word (quotient (heap.top h) 4)))))
+
+
+; This is a gross hack that happens to work very well.
+
+(define (append-file-shell-command file-to-append file-to-append-to)
+
+  (define (message)
+    (display "You must execute the command") (newline)
+    (display "   cat ") (display file-to-append) 
+    (display " >> ") (display file-to-append-to) (newline)
+    (display "to create the final heap image.") (newline))
+
+  (case host-system
+    ((chez larceny)
+     (display "Creating final image in \"")
+     (display file-to-append-to) (display "\"...") (newline)
+     (if (zero? (system (string-append "cat " file-to-append " >> " 
+                                      file-to-append-to)))
+        (delete-file file-to-append)
+        (begin (display "Failed to create image!")
+               (newline))))
+    (else
+     (message))))
+
+; eof
+; Copyright 1991 Lightship Software, Incorporated.
+;
+; $Id: twobit.sch,v 1.3 1999/08/23 19:14:26 lth Exp $
+;
+; 11 June 1999 / wdc
+;
+; Asm/Sparc/pass5p2.sch -- Sparc machine assembler, top level
+
+; Overrides the procedure of the same name in Asm/Common/pass5p1.sch.
+
+(define (assembly-table) $sparc-assembly-table$)
+
+; Controls listing of instructions during assembly.
+
+(define listify? #f)
+
+; Table of assembler procedures.
+
+(define $sparc-assembly-table$
+  (make-vector
+   *number-of-mnemonics*
+   (lambda (instruction as)
+     (asm-error "Unrecognized mnemonic " instruction))))
+
+(define (define-instruction i proc)
+  (vector-set! $sparc-assembly-table$ i proc)
+  #t)
+
+(define (list-instruction name instruction)
+  (if listify?
+      (begin (display list-indentation)
+             (display "        ")
+             (display name)
+             (display (make-string (max (- 12 (string-length name)) 1)
+                                   #\space))
+             (if (not (null? (cdr instruction)))
+                 (begin (write (cadr instruction))
+                        (do ((operands (cddr instruction)
+                                       (cdr operands)))
+                            ((null? operands))
+                            (write-char #\,)
+                            (write (car operands)))))
+             (newline)
+             (flush-output-port))))
+
+(define (list-label instruction)
+  (if listify?
+      (begin (display list-indentation)
+             (write-char #\L)
+             (write (cadr instruction))
+             (newline))))
+
+(define (list-lambda-start instruction)
+  (list-instruction "lambda" (list $lambda '* (operand2 instruction)))
+  (set! list-indentation (string-append list-indentation "|   ")))
+
+(define (list-lambda-end)
+  (set! list-indentation
+        (substring list-indentation
+                   0
+                   (- (string-length list-indentation) 4))))
+
+(define list-indentation "")
+
+; Utilities
+
+; Pseudo-instructions.
+
+(define-instruction $.label
+  (lambda (instruction as)
+    (list-label instruction)
+    (sparc.label as (make-asm-label as (operand1 instruction)))))
+
+(define-instruction $.proc
+  (lambda (instruction as)
+    (list-instruction ".proc" instruction)
+    #t))
+
+(define-instruction $.proc-doc
+  (lambda (instruction as)
+    (list-instruction ".proc-doc" instruction)
+    (add-documentation as (operand1 instruction))
+    #t))
+
+(define-instruction $.cont
+  (lambda (instruction as)
+    (list-instruction ".cont" instruction)
+    #t))
+
+(define-instruction $.align
+  (lambda (instruction as)
+    (list-instruction ".align" instruction)
+    #t))
+
+(define-instruction $.end
+  (lambda (instruction as)
+    #t))
+
+(define-instruction $.singlestep
+  (lambda (instruction as)
+    (let ((instr (car (as-source as))))
+      
+      (define (special?)
+        (let ((op (operand0 instr)))
+          (or (= op $.label)
+              (= op $.proc)
+              (= op $.cont)
+              (= op $.align)
+              (and (= op $load) (= 0 (operand1 instr))))))
+      
+      (define (readify-instr)
+        (if (= (operand0 instr) $lambda)
+            (list 'lambda '(...) (caddr instr) (cadddr instr))
+            (car (readify-lap (list instr)))))
+      
+      (if (not (special?))
+          (let ((repr   (format-object (readify-instr)))
+                (funky? (= (operand0 instr) $restore)))
+            (let ((o (emit-datum as repr)))
+              (emit-singlestep-instr! as funky? 0 o)))))))
+
+
+; Instructions.
+
+(define-instruction $op1
+  (lambda (instruction as)
+    (list-instruction "op1" instruction)
+    (emit-primop.1arg! as (operand1 instruction))))
+
+(define-instruction $op2
+  (lambda (instruction as)
+    (list-instruction "op2" instruction)
+    (emit-primop.2arg! as
+                       (operand1 instruction)
+                       (regname (operand2 instruction)))))
+
+(define-instruction $op3
+  (lambda (instruction as)
+    (list-instruction "op3" instruction)
+    (emit-primop.3arg! as
+                       (operand1 instruction)
+                       (regname (operand2 instruction))
+                       (regname (operand3 instruction)))))
+
+(define-instruction $op2imm
+  (lambda (instruction as)
+    (list-instruction "op2imm" instruction)
+    (let ((op (case (operand1 instruction)
+                ((+)    'internal:+/imm)
+                ((-)    'internal:-/imm)
+                ((fx+)  'internal:fx+/imm)
+                ((fx-)  'internal:fx-/imm)
+                ((fx=)  'internal:fx=/imm)
+                ((fx<)  'internal:fx</imm)
+                ((fx<=) 'internal:fx<=/imm)
+                ((fx>)  'internal:fx>/imm)
+                ((fx>=) 'internal:fx>=/imm)
+                ((=:fix:fix)  'internal:=:fix:fix/imm)
+                ((<:fix:fix)  'internal:<:fix:fix/imm)
+                ((<=:fix:fix) 'internal:<=:fix:fix/imm)
+                ((>:fix:fix)  'internal:>:fix:fix/imm)
+                ((>=:fix:fix) 'internal:>=:fix:fix/imm)
+                (else #f))))
+      (if op
+          (emit-primop.4arg! as op $r.result (operand2 instruction) $r.result)
+          (begin
+           (emit-constant->register as (operand2 instruction) $r.argreg2)
+           (emit-primop.2arg! as
+                              (operand1 instruction)
+                              $r.argreg2))))))
+
+(define-instruction $const
+  (lambda (instruction as)
+    (list-instruction "const" instruction)
+    (emit-constant->register as (operand1 instruction) $r.result)))
+
+(define-instruction $global
+  (lambda (instruction as)
+    (list-instruction "global" instruction)
+    (emit-global->register! as
+                            (emit-global as (operand1 instruction))
+                            $r.result)))
+
+(define-instruction $setglbl
+  (lambda (instruction as)
+    (list-instruction "setglbl" instruction)
+    (emit-register->global! as
+                            $r.result
+                            (emit-global as (operand1 instruction)))))
+
+; FIXME: A problem is that the listing is messed up because of the delayed
+; assembly; somehow we should fix this by putting an identifying label
+; in the listing and emitting this label later, with the code.
+
+(define-instruction $lambda
+  (lambda (instruction as)
+    (let ((code-offset  #f)
+          (const-offset #f))
+      (list-lambda-start instruction)
+      (assemble-nested-lambda as
+                              (operand1 instruction)
+                              (operand3 instruction)   ; documentation
+                              (lambda (nested-as segment)
+                                (set-constant! as code-offset (car segment))
+                                (set-constant! as const-offset (cdr segment))))
+      (list-lambda-end)
+      (set! code-offset  (emit-codevector as 0))
+      (set! const-offset (emit-constantvector as 0))
+      (emit-lambda! as
+                    code-offset
+                    const-offset
+                    (operand2 instruction)))))
+
+(define-instruction $lexes
+  (lambda (instruction as)
+    (list-instruction "lexes" instruction)
+    (emit-lexes! as (operand1 instruction))))
+
+(define-instruction $args=
+  (lambda (instruction as)
+    (list-instruction "args=" instruction)
+    (emit-args=! as (operand1 instruction))))
+
+(define-instruction $args>=
+  (lambda (instruction as)
+    (list-instruction "args>=" instruction)
+    (emit-args>=! as (operand1 instruction))))
+
+(define-instruction $invoke
+  (lambda (instruction as)
+    (list-instruction "invoke" instruction)
+    (emit-invoke as (operand1 instruction) #f $m.invoke-ex)))
+
+(define-instruction $restore
+  (lambda (instruction as)
+    (if (not (negative? (operand1 instruction)))
+        (begin
+         (list-instruction "restore" instruction)
+         (emit-restore! as (operand1 instruction))))))
+
+(define-instruction $pop
+  (lambda (instruction as)
+    (if (not (negative? (operand1 instruction)))
+        (begin
+         (list-instruction "pop" instruction)
+         (let ((next (next-instruction as)))
+           (if (and (peephole-optimization)
+                    (eqv? $return (operand0 next)))
+               (begin (list-instruction "return" next)
+                      (consume-next-instruction! as)
+                      (emit-pop! as (operand1 instruction) #t))
+               (emit-pop! as (operand1 instruction) #f)))))))
+
+(define-instruction $stack
+  (lambda (instruction as)
+    (list-instruction "stack" instruction)
+    (emit-load! as (operand1 instruction) $r.result)))
+
+(define-instruction $setstk
+  (lambda (instruction as)
+    (list-instruction "setstk" instruction)
+    (emit-store! as $r.result (operand1 instruction))))
+
+(define-instruction $load
+  (lambda (instruction as)
+    (list-instruction "load" instruction)
+    (emit-load! as (operand2 instruction) (regname (operand1 instruction)))))
+
+(define-instruction $store
+  (lambda (instruction as)
+    (list-instruction "store" instruction)
+    (emit-store! as (regname (operand1 instruction)) (operand2 instruction))))
+
+(define-instruction $lexical
+  (lambda (instruction as)
+    (list-instruction "lexical" instruction)
+    (emit-lexical! as (operand1 instruction) (operand2 instruction))))
+
+(define-instruction $setlex
+  (lambda (instruction as)
+    (list-instruction "setlex" instruction)
+    (emit-setlex! as (operand1 instruction) (operand2 instruction))))
+
+(define-instruction $reg
+  (lambda (instruction as)
+    (list-instruction "reg" instruction)
+    (emit-register->register! as (regname (operand1 instruction)) $r.result)))
+
+(define-instruction $setreg
+  (lambda (instruction as)
+    (list-instruction "setreg" instruction)
+    (emit-register->register! as $r.result (regname (operand1 instruction)))))
+
+(define-instruction $movereg
+  (lambda (instruction as)
+    (list-instruction "movereg" instruction)
+    (emit-register->register! as 
+                              (regname (operand1 instruction))
+                              (regname (operand2 instruction)))))
+
+(define-instruction $return
+  (lambda (instruction as)
+    (list-instruction "return" instruction)
+    (emit-return! as)))
+
+(define-instruction $reg/return
+  (lambda (instruction as)
+    (list-instruction "reg/return" instruction)
+    (emit-return-reg! as (regname (operand1 instruction)))))
+
+(define-instruction $const/return
+  (lambda (instruction as)
+    (list-instruction "const/return" instruction)
+    (emit-return-const! as (operand1 instruction))))
+
+(define-instruction $nop
+  (lambda (instruction as)
+    (list-instruction "nop" instruction)))
+
+(define-instruction $save
+  (lambda (instruction as)
+    (if (not (negative? (operand1 instruction)))
+        (begin
+         (list-instruction "save" instruction)
+         (let* ((n (operand1 instruction))
+                (v (make-vector (+ n 1) #t)))
+           (emit-save0! as n)
+           (if (peephole-optimization)
+               (let loop ((instruction (next-instruction as)))
+                 (if (eqv? $store (operand0 instruction))
+                     (begin (list-instruction "store" instruction)
+                            (emit-store! as
+                                         (regname (operand1 instruction))
+                                         (operand2 instruction))
+                            (consume-next-instruction! as)
+                            (vector-set! v (operand2 instruction) #f)
+                            (loop (next-instruction as))))))
+           (emit-save1! as v))))))
+
+(define-instruction $setrtn
+  (lambda (instruction as)
+    (list-instruction "setrtn" instruction)
+    (emit-setrtn! as (make-asm-label as (operand1 instruction)))))
+
+(define-instruction $apply
+  (lambda (instruction as)
+    (list-instruction "apply" instruction)
+    (emit-apply! as
+                 (regname (operand1 instruction))
+                 (regname (operand2 instruction)))))
+
+(define-instruction $jump
+  (lambda (instruction as)
+    (list-instruction "jump" instruction)
+    (emit-jump! as
+                (operand1 instruction)
+                (make-asm-label as (operand2 instruction)))))
+
+(define-instruction $skip
+  (lambda (instruction as)
+    (list-instruction "skip" instruction)
+    (emit-branch! as #f (make-asm-label as (operand1 instruction)))))
+
+(define-instruction $branch
+  (lambda (instruction as)
+    (list-instruction "branch" instruction)
+    (emit-branch! as #t (make-asm-label as (operand1 instruction)))))
+
+(define-instruction $branchf
+  (lambda (instruction as)
+    (list-instruction "branchf" instruction)
+    (emit-branchf! as (make-asm-label as (operand1 instruction)))))
+
+(define-instruction $check
+  (lambda (instruction as)
+    (list-instruction "check" instruction)
+    (if (not (unsafe-code))
+        (emit-check! as $r.result
+                        (make-asm-label as (operand4 instruction))
+                        (list (regname (operand1 instruction))
+                              (regname (operand2 instruction))
+                              (regname (operand3 instruction)))))))
+
+(define-instruction $trap
+  (lambda (instruction as)
+    (list-instruction "trap" instruction)
+    (emit-trap! as
+                (regname (operand1 instruction))
+                (regname (operand2 instruction))
+                (regname (operand3 instruction))
+                (operand4 instruction))))
+
+(define-instruction $const/setreg
+  (lambda (instruction as)
+    (list-instruction "const/setreg" instruction)
+    (let ((x (operand1 instruction))
+          (r (operand2 instruction)))
+      (if (hwreg? r)
+          (emit-constant->register as x (regname r))
+          (begin (emit-constant->register as x $r.tmp0)
+                 (emit-register->register! as $r.tmp0 (regname r)))))))
+
+; Operations introduced by the peephole optimizer.
+
+(define (peep-regname r)
+  (if (eq? r 'RESULT) $r.result (regname r)))
+
+(define-instruction $reg/op1/branchf
+  (lambda (instruction as)
+    (list-instruction "reg/op1/branchf" instruction)
+    (emit-primop.3arg! as
+                       (operand1 instruction)
+                       (peep-regname (operand2 instruction))
+                       (make-asm-label as (operand3 instruction)))))
+
+(define-instruction $reg/op2/branchf
+  (lambda (instruction as)
+    (list-instruction "reg/op2/branchf" instruction)
+    (emit-primop.4arg! as
+                       (operand1 instruction)
+                       (peep-regname (operand2 instruction))
+                       (peep-regname (operand3 instruction))
+                       (make-asm-label as (operand4 instruction)))))
+
+(define-instruction $reg/op2imm/branchf
+  (lambda (instruction as)
+    (list-instruction "reg/op2imm/branchf" instruction)
+    (emit-primop.4arg! as
+                       (operand1 instruction)
+                       (peep-regname (operand2 instruction))
+                       (operand3 instruction)
+                       (make-asm-label as (operand4 instruction)))))
+
+; These three are like the corresponding branchf sequences except that
+; there is a strong prediction that the branch will not be taken.
+
+(define-instruction $reg/op1/check
+  (lambda (instruction as)
+    (list-instruction "reg/op1/check" instruction)
+    (emit-primop.4arg! as
+                       (operand1 instruction)
+                       (peep-regname (operand2 instruction))
+                       (make-asm-label as (operand3 instruction))
+                       (map peep-regname (operand4 instruction)))))
+
+(define-instruction $reg/op2/check
+  (lambda (instruction as)
+    (list-instruction "reg/op2/check" instruction)
+    (emit-primop.5arg! as
+                       (operand1 instruction)
+                       (peep-regname (operand2 instruction))
+                       (peep-regname (operand3 instruction))
+                       (make-asm-label as (operand4 instruction))
+                       (map peep-regname (operand5 instruction)))))
+
+(define-instruction $reg/op2imm/check
+  (lambda (instruction as)
+    (list-instruction "reg/op2imm/check" instruction)
+    (emit-primop.5arg! as
+                       (operand1 instruction)
+                       (peep-regname (operand2 instruction))
+                       (operand3 instruction)
+                       (make-asm-label as (operand4 instruction))
+                       (map peep-regname (operand5 instruction)))))
+
+;
+
+(define-instruction $reg/op1/setreg
+  (lambda (instruction as)
+    (list-instruction "reg/op1/setreg" instruction)
+    (emit-primop.3arg! as
+                       (operand1 instruction)
+                       (peep-regname (operand2 instruction))
+                       (peep-regname (operand3 instruction)))))
+
+(define-instruction $reg/op2/setreg
+  (lambda (instruction as)
+    (list-instruction "reg/op2/setreg" instruction)
+    (emit-primop.4arg! as
+                       (operand1 instruction)
+                       (peep-regname (operand2 instruction))
+                       (peep-regname (operand3 instruction))
+                       (peep-regname (operand4 instruction)))))
+
+(define-instruction $reg/op2imm/setreg
+  (lambda (instruction as)
+    (list-instruction "reg/op2imm/setreg" instruction)
+    (emit-primop.4arg! as
+                       (operand1 instruction)
+                       (peep-regname (operand2 instruction))
+                       (operand3 instruction)
+                       (peep-regname (operand4 instruction)))))
+
+(define-instruction $reg/op3 
+  (lambda (instruction as)
+    (list-instruction "reg/op3" instruction)
+    (emit-primop.4arg! as
+                       (operand1 instruction)
+                       (peep-regname (operand2 instruction))
+                       (peep-regname (operand3 instruction))
+                       (peep-regname (operand4 instruction)))))
+
+(define-instruction $reg/branchf
+  (lambda (instruction as)
+    (list-instruction "reg/branchf" instruction)
+    (emit-branchfreg! as 
+                      (regname (operand1 instruction))
+                      (make-asm-label as (operand2 instruction)))))
+
+(define-instruction $setrtn/branch
+  (lambda (instruction as)
+    (list-instruction "setrtn/branch" instruction)
+    (emit-branch-with-setrtn! as (make-asm-label as (operand1 instruction)))))
+
+(define-instruction $setrtn/invoke
+  (lambda (instruction as)
+    (list-instruction "setrtn/invoke" instruction)
+    (emit-invoke as (operand1 instruction) #t $m.invoke-ex)))
+
+(define-instruction $global/setreg
+  (lambda (instruction as)
+    (list-instruction "global/setreg" instruction)
+    (emit-global->register! as
+                            (emit-global as (operand1 instruction))
+                            (regname (operand2 instruction)))))
+
+(define-instruction $global/invoke
+  (lambda (instruction as)
+    (list-instruction "global/invoke" instruction)
+    (emit-load-global as
+                      (emit-global as (operand1 instruction))
+                      $r.result
+                      #f)
+    (emit-invoke as (operand2 instruction) #f $m.global-invoke-ex)))
+
+(define-instruction $reg/setglbl
+  (lambda (instruction as)
+    (list-instruction "reg/setglbl" instruction)
+    (emit-register->global! as
+                            (regname (operand1 instruction))
+                            (emit-global as (operand2 instruction)))))
+
+; eof
+; Copyright 1998 Lars T Hansen.
+;
+; $Id: twobit.sch,v 1.3 1999/08/23 19:14:26 lth Exp $
+;
+; 9 May 1999.
+;
+; Asm/Sparc/peepopt.sch -- MAL peephole optimizer, for the SPARC assembler.
+;
+; The procedure `peep' is called on the as structure before every
+; instruction is assembled.  It may replace the prefix of the instruction
+; stream by some other instruction sequence.
+;
+; Invariant: if the peephole optimizer doesn't change anything, then 
+;
+;  (let ((x (as-source as)))
+;    (peep as)
+;    (eq? x (as-source as)))     => #t
+;
+; Note this still isn't right -- it should be integrated with pass5p2 --
+; but it's a step in the right direction.
+
+(define *peephole-table* (make-vector *number-of-mnemonics* #f))
+
+(define (define-peephole n p)
+  (vector-set! *peephole-table* n p)
+  (unspecified))
+
+(define (peep as)
+  (let ((t0 (as-source as)))
+    (if (not (null? t0))
+        (let ((i1 (car t0)))
+          (let ((p (vector-ref *peephole-table* (car i1))))
+            (if p
+                (let* ((t1 (if (null? t0) t0 (cdr t0)))
+                       (i2 (if (null? t1) '(-1 0 0 0) (car t1)))
+                       (t2 (if (null? t1) t1 (cdr t1)))
+                       (i3 (if (null? t2) '(-1 0 0 0) (car t2)))
+                       (t3 (if (null? t2) t2 (cdr t2))))
+                  (p as i1 i2 i3 t1 t2 t3))))))))
+
+(define-peephole $reg
+  (lambda (as i1 i2 i3 t1 t2 t3)
+    (cond ((= (car i2) $return)
+           (reg-return as i1 i2 t2))
+          ((= (car i2) $setglbl)
+           (reg-setglbl as i1 i2 t2))
+          ((= (car i2) $op1)
+           (cond ((= (car i3) $setreg)
+                  (reg-op1-setreg as i1 i2 i3 t2 t3))
+                 ((= (car i3) $branchf)
+                  (reg-op1-branchf as i1 i2 i3 t3))
+                 ((= (car i3) $check)
+                  (reg-op1-check as i1 i2 i3 t3))
+                 (else
+                  (reg-op1 as i1 i2 t2))))
+          ((= (car i2) $op2)
+           (cond ((= (car i3) $setreg)
+                  (reg-op2-setreg as i1 i2 i3 t2 t3))
+                 ((= (car i3) $branchf)
+                  (reg-op2-branchf as i1 i2 i3 t3))
+                 ((= (car i3) $check)
+                  (reg-op2-check as i1 i2 i3 t3))
+                 (else
+                  (reg-op2 as i1 i2 t2))))
+          ((= (car i2) $op2imm)
+           (cond ((= (car i3) $setreg)
+                  (reg-op2imm-setreg as i1 i2 i3 t2 t3))
+                 ((= (car i3) $branchf)
+                  (reg-op2imm-branchf as i1 i2 i3 t3))
+                 ((= (car i3) $check)
+                  (reg-op2imm-check as i1 i2 i3 t3))
+                 (else
+                  (reg-op2imm as i1 i2 t2))))
+          ((= (car i2) $op3)
+           (reg-op3 as i1 i2 t2))
+          ((= (car i2) $setreg)
+           (reg-setreg as i1 i2 t2))
+          ((= (car i2) $branchf)
+           (reg-branchf as i1 i2 t2)))))
+
+(define-peephole $op1
+  (lambda (as i1 i2 i3 t1 t2 t3)
+    (cond ((= (car i2) $branchf)
+           (op1-branchf as i1 i2 t2))
+          ((= (car i2) $setreg)
+           (op1-setreg as i1 i2 t2))
+          ((= (car i2) $check)
+           (op1-check as i1 i2 t2)))))
+
+(define-peephole $op2
+  (lambda (as i1 i2 i3 t1 t2 t3)
+    (cond ((= (car i2) $branchf)
+           (op2-branchf as i1 i2 t2))
+          ((= (car i2) $setreg)
+           (op2-setreg as i1 i2 t2))
+          ((= (car i2) $check)
+           (op2-check as i1 i2 t2)))))
+
+(define-peephole $op2imm
+  (lambda (as i1 i2 i3 t1 t2 t3)
+    (cond ((= (car i2) $branchf)
+           (op2imm-branchf as i1 i2 t2))
+          ((= (car i2) $setreg)
+           (op2imm-setreg as i1 i2 t2))
+          ((= (car i2) $check)
+           (op2imm-check as i1 i2 t2)))))
+
+(define-peephole $const
+  (lambda (as i1 i2 i3 t1 t2 t3)
+    (cond ((= (car i2) $setreg)
+           (const-setreg as i1 i2 t2))
+          ((= (car i2) $op2)
+           (const-op2 as i1 i2 t2))
+          ((= (car i2) $return)
+           (const-return as i1 i2 t2)))))
+
+(define-peephole $setrtn
+  (lambda (as i1 i2 i3 t1 t2 t3)
+    (cond ((= (car i2) $branch)
+           (cond ((= (car i3) $.align)
+                  (if (not (null? t3))
+                      (let ((i4 (car t3))
+                            (t4 (cdr t3)))
+                        (cond ((= (car i4) $.label)
+                               (setrtn-branch as i1 i2 i3 i4 t4))))))))
+          ((= (car i2) $invoke)
+           (cond ((= (car i3) $.align)
+                  (if (not (null? t3))
+                      (let ((i4 (car t3))
+                            (t4 (cdr t3)))
+                        (cond ((= (car i4) $.label)
+                               (setrtn-invoke as i1 i2 i3 i4 t4)))))))))))
+
+(define-peephole $branch
+  (lambda (as i1 i2 i3 t1 t2 t3)
+    (cond ((= (car i2) $.align)
+           (cond ((= (car i3) $.label)
+                  (branch-and-label as i1 i2 i3 t3)))))))
+
+(define-peephole $global
+  (lambda (as i1 i2 i3 t1 t2 t3)
+    (cond ((= (car i2) $setreg)
+           (global-setreg as i1 i2 t2))
+          ((= (car i2) $invoke)
+           (global-invoke as i1 i2 t2))
+          ((= (car i2) $setrtn)
+           (cond ((= (car i3) $invoke)
+                  (global-setrtn-invoke as i1 i2 i3 t3)))))))
+
+(define-peephole $reg/op1/check
+  (lambda (as i1 i2 i3 t1 t2 t3)
+    (cond ((= (car i2) $reg)
+           (cond ((= (car i3) $op1)
+                  (if (not (null? t3))
+                      (let ((i4 (car t3))
+                            (t4 (cdr t3)))
+                        (cond ((= (car i4) $setreg)
+                               (reg/op1/check-reg-op1-setreg
+                                as i1 i2 i3 i4 t4)))))))))))
+
+(define-peephole $reg/op2/check
+  (lambda (as i1 i2 i3 t1 t2 t3)
+    (cond ((= (car i2) $reg)
+           (cond ((= (car i3) $op2imm)
+                  (if (not (null? t3))
+                      (let ((i4 (car t3))
+                            (t4 (cdr t3)))
+                        (cond ((= (car i4) $check)
+                               (reg/op2/check-reg-op2imm-check
+                                as i1 i2 i3 i4 t4)))))))))))
+
+; Worker procedures.
+
+(define (reg-return as i:reg i:return tail)
+  (let ((rs (operand1 i:reg)))
+    (if (hwreg? rs)
+        (as-source! as (cons (list $reg/return rs) tail)))))
+
+(define (reg-op1-setreg as i:reg i:op1 i:setreg tail-1 tail)
+  (let ((rs (operand1 i:reg))
+        (rd (operand1 i:setreg))
+        (op (operand1 i:op1)))
+    (if (hwreg? rs)
+        (if (hwreg? rd)
+            (peep-reg/op1/setreg as op rs rd tail)
+            (peep-reg/op1/setreg as op rs 'RESULT tail-1)))))
+
+(define (reg-op1 as i:reg i:op1 tail)
+  (let ((rs (operand1 i:reg))
+        (op (operand1 i:op1)))
+    (if (hwreg? rs)
+        (peep-reg/op1/setreg as op rs 'RESULT tail))))
+
+(define (op1-setreg as i:op1 i:setreg tail)
+  (let ((op (operand1 i:op1))
+        (rd (operand1 i:setreg)))
+    (if (hwreg? rd)
+        (peep-reg/op1/setreg as op 'RESULT rd tail))))
+
+(define (peep-reg/op1/setreg as op rs rd tail)
+  (let ((op (case op
+              ((car)               'internal:car)
+              ((cdr)               'internal:cdr)
+              ((car:pair)          'internal:car:pair)
+              ((cdr:pair)          'internal:cdr:pair)
+              ((cell-ref)          'internal:cell-ref)
+              ((vector-length)     'internal:vector-length)
+              ((vector-length:vec) 'internal:vector-length:vec)
+              ((string-length)     'internal:string-length)
+              ((--)                'internal:--)
+              ((fx--)              'internal:fx--)
+              ((fxpositive?)       'internal:fxpositive?)
+              ((fxnegative?)       'internal:fxnegative?)
+              ((fxzero?)           'internal:fxzero?)
+              (else #f))))
+    (if op
+        (as-source! as (cons (list $reg/op1/setreg op rs rd) tail)))))
+
+(define (reg-op2-setreg as i:reg i:op2 i:setreg tail-1 tail)
+  (let ((rs1 (operand1 i:reg))
+        (rs2 (operand2 i:op2))
+        (op  (operand1 i:op2))
+        (rd  (operand1 i:setreg)))
+    (if (hwreg? rs1)
+        (if (hwreg? rd)
+            (peep-reg/op2/setreg as op rs1 rs2 rd tail)
+            (peep-reg/op2/setreg as op rs1 rs2 'RESULT tail-1)))))
+
+(define (reg-op2 as i:reg i:op2 tail)
+  (let ((rs1 (operand1 i:reg))
+        (rs2 (operand2 i:op2))
+        (op  (operand1 i:op2)))
+    (if (hwreg? rs1)
+        (peep-reg/op2/setreg as op rs1 rs2 'RESULT tail))))
+
+(define (op2-setreg as i:op2 i:setreg tail)
+  (let ((op  (operand1 i:op2))
+        (rs2 (operand2 i:op2))
+        (rd  (operand1 i:setreg)))
+    (if (hwreg? rd)
+        (peep-reg/op2/setreg as op 'RESULT rs2 rd tail))))
+
+(define (peep-reg/op2/setreg as op rs1 rs2 rd tail)
+  (let ((op (case op
+              ((+)                  'internal:+)
+              ((-)                  'internal:-)
+              ((fx+)                'internal:fx+)
+              ((fx-)                'internal:fx-)
+              ((fx=)                'internal:fx=)
+              ((fx>)                'internal:fx>)
+              ((fx>=)               'internal:fx>=)
+              ((fx<)                'internal:fx<)
+              ((fx<=)               'internal:fx<=)
+              ((eq?)                'internal:eq?)
+              ((cons)               'internal:cons)
+              ((vector-ref)         'internal:vector-ref)
+              ((vector-ref:trusted) 'internal:vector-ref:trusted)
+              ((string-ref)         'internal:string-ref)
+              ((set-car!)           'internal:set-car!)
+              ((set-cdr!)           'internal:set-cdr!)
+              ((cell-set!)          'internal:cell-set!)
+              (else #f))))
+    (if op
+        (as-source! as (cons (list $reg/op2/setreg op rs1 rs2 rd) tail)))))
+
+(define (reg-op2imm-setreg as i:reg i:op2imm i:setreg tail-1 tail)
+  (let ((rs  (operand1 i:reg))
+        (imm (operand2 i:op2imm))
+        (op  (operand1 i:op2imm))
+        (rd  (operand1 i:setreg)))
+    (if (hwreg? rs)
+        (if (hwreg? rd)
+            (peep-reg/op2imm/setreg as op rs imm rd tail)
+            (peep-reg/op2imm/setreg as op rs imm 'RESULT tail-1)))))
+
+(define (reg-op2imm as i:reg i:op2imm tail)
+  (let ((rs  (operand1 i:reg))
+        (imm (operand2 i:op2imm))
+        (op  (operand1 i:op2imm)))
+    (if (hwreg? rs)
+        (peep-reg/op2imm/setreg as op rs imm 'RESULT tail))))
+
+(define (op2imm-setreg as i:op2imm i:setreg tail)
+  (let ((op  (operand1 i:op2imm))
+        (imm (operand2 i:op2imm))
+        (rd  (operand1 i:setreg)))
+    (if (hwreg? rd)
+        (peep-reg/op2imm/setreg as op 'RESULT imm rd tail))))
+
+(define (peep-reg/op2imm/setreg as op rs imm rd tail)
+  (let ((op (case op
+              ((+)          'internal:+/imm)
+              ((-)          'internal:-/imm)
+              ((fx+)        'internal:fx+/imm)
+              ((fx-)        'internal:fx-/imm)
+              ((fx=)        'internal:fx=/imm)
+              ((fx<)        'internal:fx</imm)
+              ((fx<=)       'internal:fx<=/imm)
+              ((fx>)        'internal:fx>/imm)
+              ((fx>=)       'internal:fx>=/imm)
+              ((eq?)        'internal:eq?/imm)
+              ((vector-ref) 'internal:vector-ref/imm)
+              ((string-ref) 'internal:string-ref/imm)
+              (else #f))))
+    (if op
+        (as-source! as (cons (list $reg/op2imm/setreg op rs imm rd) tail)))))
+
+(define (reg-op1-branchf as i:reg i:op1 i:branchf tail)
+  (let ((rs (operand1 i:reg))
+        (op (operand1 i:op1))
+        (L  (operand1 i:branchf)))
+    (if (hwreg? rs)
+        (peep-reg/op1/branchf as op rs L tail))))
+
+(define (op1-branchf as i:op1 i:branchf tail)
+  (let ((op (operand1 i:op1))
+        (L  (operand1 i:branchf)))
+    (peep-reg/op1/branchf as op 'RESULT L tail)))
+
+(define (peep-reg/op1/branchf as op rs L tail)
+  (let ((op (case op
+              ((null?)       'internal:branchf-null?)
+              ((pair?)       'internal:branchf-pair?)
+              ((zero?)       'internal:branchf-zero?)
+              ((eof-object?) 'internal:branchf-eof-object?)
+              ((fixnum?)     'internal:branchf-fixnum?)
+              ((char?)       'internal:branchf-char?)
+              ((fxzero?)     'internal:branchf-fxzero?)
+              ((fxnegative?) 'internal:branchf-fxnegative?)
+              ((fxpositive?) 'internal:branchf-fxpositive?)
+              (else #f))))
+    (if op
+        (as-source! as (cons (list $reg/op1/branchf op rs L) tail)))))
+
+(define (reg-op2-branchf as i:reg i:op2 i:branchf tail)
+  (let ((rs1 (operand1 i:reg))
+        (rs2 (operand2 i:op2))
+        (op  (operand1 i:op2))
+        (L   (operand1 i:branchf)))
+    (if (hwreg? rs1)
+        (peep-reg/op2/branchf as op rs1 rs2 L tail))))
+
+(define (op2-branchf as i:op2 i:branchf tail)
+  (let ((op  (operand1 i:op2))
+        (rs2 (operand2 i:op2))
+        (L   (operand1 i:branchf)))
+    (peep-reg/op2/branchf as op 'RESULT rs2 L tail)))
+
+(define (peep-reg/op2/branchf as op rs1 rs2 L tail)
+  (let ((op (case op
+              ((<)       'internal:branchf-<)
+              ((>)       'internal:branchf->)
+              ((>=)      'internal:branchf->=)
+              ((<=)      'internal:branchf-<=)
+              ((=)       'internal:branchf-=)
+              ((eq?)     'internal:branchf-eq?)
+              ((char=?)  'internal:branchf-char=?)
+              ((char>=?) 'internal:branchf-char>=?)
+              ((char>?)  'internal:branchf-char>?)
+              ((char<=?) 'internal:branchf-char<=?)
+              ((char<?)  'internal:branchf-char<?)
+              ((fx=)     'internal:branchf-fx=)
+              ((fx>)     'internal:branchf-fx>)
+              ((fx>=)    'internal:branchf-fx>=)
+              ((fx<)     'internal:branchf-fx<)
+              ((fx<=)    'internal:branchf-fx<=)
+              (else #f))))
+    (if op
+        (as-source! as
+                    (cons (list $reg/op2/branchf op rs1 rs2 L)
+                          tail)))))
+
+(define (reg-op2imm-branchf as i:reg i:op2imm i:branchf tail)
+  (let ((rs  (operand1 i:reg))
+        (imm (operand2 i:op2imm))
+        (op  (operand1 i:op2imm))
+        (L   (operand1 i:branchf)))
+    (if (hwreg? rs)
+        (peep-reg/op2imm/branchf as op rs imm L tail))))
+
+(define (op2imm-branchf as i:op2imm i:branchf tail)
+  (let ((op  (operand1 i:op2imm))
+        (imm (operand2 i:op2imm))
+        (L   (operand1 i:branchf)))
+    (peep-reg/op2imm/branchf as op 'RESULT imm L tail)))
+
+(define (peep-reg/op2imm/branchf as op rs imm L tail)
+  (let ((op (case op
+              ((<)       'internal:branchf-</imm)
+              ((>)       'internal:branchf->/imm)
+              ((>=)      'internal:branchf->=/imm)
+              ((<=)      'internal:branchf-<=/imm)
+              ((=)       'internal:branchf-=/imm)
+              ((eq?)     'internal:branchf-eq?/imm)
+              ((char=?)  'internal:branchf-char=?/imm)
+              ((char>=?) 'internal:branchf-char>=?/imm)
+              ((char>?)  'internal:branchf-char>?/imm)
+              ((char<=?) 'internal:branchf-char<=?/imm)
+              ((char<?)  'internal:branchf-char<?/imm)
+              ((fx=)     'internal:branchf-fx=/imm)
+              ((fx>)     'internal:branchf-fx>/imm)
+              ((fx>=)    'internal:branchf-fx>=/imm)
+              ((fx<)     'internal:branchf-fx</imm)
+              ((fx<=)    'internal:branchf-fx<=/imm)
+              (else #f))))
+    (if op
+        (as-source! as
+                    (cons (list $reg/op2imm/branchf op rs imm L)
+                          tail)))))
+
+; Check optimization.
+
+(define (reg-op1-check as i:reg i:op1 i:check tail)
+  (let ((rs (operand1 i:reg))
+        (op (operand1 i:op1)))
+    (if (hwreg? rs)
+        (peep-reg/op1/check as
+                            op
+                            rs
+                            (operand4 i:check)
+                            (list (operand1 i:check)
+                                  (operand2 i:check)
+                                  (operand3 i:check))
+                            tail))))
+
+(define (op1-check as i:op1 i:check tail)
+  (let ((op (operand1 i:op1)))
+    (peep-reg/op1/check as
+                        op
+                        'RESULT
+                        (operand4 i:check)
+                        (list (operand1 i:check)
+                              (operand2 i:check)
+                              (operand3 i:check))
+                        tail)))
+
+(define (peep-reg/op1/check as op rs L1 liveregs tail)
+  (let ((op (case op
+              ((fixnum?)      'internal:check-fixnum?)
+              ((pair?)        'internal:check-pair?)
+              ((vector?)      'internal:check-vector?)
+              (else #f))))
+    (if op
+        (as-source! as
+                    (cons (list $reg/op1/check op rs L1 liveregs)
+                          tail)))))
+
+(define (reg-op2-check as i:reg i:op2 i:check tail)
+  (let ((rs1 (operand1 i:reg))
+        (rs2 (operand2 i:op2))
+        (op (operand1 i:op2)))
+    (if (hwreg? rs1)
+        (peep-reg/op2/check as
+                            op
+                            rs1
+                            rs2
+                            (operand4 i:check)
+                            (list (operand1 i:check)
+                                  (operand2 i:check)
+                                  (operand3 i:check))
+                            tail))))
+
+(define (op2-check as i:op2 i:check tail)
+  (let ((rs2 (operand2 i:op2))
+        (op (operand1 i:op2)))
+    (peep-reg/op2/check as
+                        op
+                        'RESULT
+                        rs2
+                        (operand4 i:check)
+                        (list (operand1 i:check)
+                              (operand2 i:check)
+                              (operand3 i:check))
+                        tail)))
+
+(define (peep-reg/op2/check as op rs1 rs2 L1 liveregs tail)
+  (let ((op (case op
+              ((<:fix:fix)   'internal:check-<:fix:fix)
+              ((<=:fix:fix)  'internal:check-<=:fix:fix)
+              ((>=:fix:fix)  'internal:check->=:fix:fix)
+              (else #f))))
+    (if op
+        (as-source! as
+                    (cons (list $reg/op2/check op rs1 rs2 L1 liveregs)
+                          tail)))))
+
+(define (reg-op2imm-check as i:reg i:op2imm i:check tail)
+  (let ((rs1 (operand1 i:reg))
+        (op (operand1 i:op2imm))
+        (imm (operand2 i:op2imm)))
+    (if (hwreg? rs1)
+        (peep-reg/op2imm/check as
+                               op
+                               rs1
+                               imm
+                               (operand4 i:check)
+                               (list (operand1 i:check)
+                                     (operand2 i:check)
+                                     (operand3 i:check))
+                               tail))))
+
+(define (op2imm-check as i:op2imm i:check tail)
+  (let ((op (operand1 i:op2imm))
+        (imm (operand2 i:op2imm)))
+    (peep-reg/op2imm/check as
+                           op
+                           'RESULT
+                           imm
+                           (operand4 i:check)
+                           (list (operand1 i:check)
+                                 (operand2 i:check)
+                                 (operand3 i:check))
+                           tail)))
+
+(define (peep-reg/op2imm/check as op rs1 imm L1 liveregs tail)
+  (let ((op (case op
+              ((<:fix:fix)   'internal:check-<:fix:fix/imm)
+              ((<=:fix:fix)  'internal:check-<=:fix:fix/imm)
+              ((>=:fix:fix)  'internal:check->=:fix:fix/imm)
+              (else #f))))
+    (if op
+        (as-source! as
+                    (cons (list $reg/op2imm/check op rs1 imm L1 liveregs)
+                          tail)))))
+
+(define (reg/op1/check-reg-op1-setreg as i:ro1check i:reg i:op1 i:setreg tail)
+  (let ((o1 (operand1 i:ro1check))
+        (r1 (operand2 i:ro1check))
+        (r2 (operand1 i:reg))
+        (o2 (operand1 i:op1))
+        (r3 (operand1 i:setreg)))
+    (if (and (eq? o1 'internal:check-vector?)
+             (eq? r1 r2)
+             (eq? o2 'vector-length:vec)
+             (hwreg? r1)
+             (hwreg? r3))
+        (as-source! as
+                    (cons (list $reg/op2/check
+                                'internal:check-vector?/vector-length:vec
+                                r1
+                                r3
+                                (operand3 i:ro1check)
+                                (operand4 i:ro1check))
+                          tail)))))
+
+; Range checks of the form 0 <= i < n can be performed by a single check.
+; This peephole optimization recognizes
+;         reg     rs1
+;         op2     <:fix:fix,rs2
+;         check   r1,r2,r3,L
+;         reg     rs1                     ; must match earlier reg
+;         op2imm  >=:fix:fix,0
+;         check   r1,r2,r3,L              ; label must match earlier check
+
+(define (reg/op2/check-reg-op2imm-check
+         as i:ro2check i:reg i:op2imm i:check tail)
+  (let ((o1   (operand1 i:ro2check))
+        (rs1  (operand2 i:ro2check))
+        (rs2  (operand3 i:ro2check))
+        (L1   (operand4 i:ro2check))
+        (live (operand5 i:ro2check))
+        (rs3  (operand1 i:reg))
+        (o2   (operand1 i:op2imm))
+        (x    (operand2 i:op2imm))
+        (L2   (operand4 i:check)))
+    (if (and (eq? o1 'internal:check-<:fix:fix)
+             (eq? o2 '>=:fix:fix)
+             (eq? rs1 rs3)
+             (eq? x 0)
+             (eq? L1 L2))
+        (as-source! as
+                    (cons (list $reg/op2/check 'internal:check-range
+                                                rs1 rs2 L1 live)
+                          tail)))))
+
+; End of check optimization.
+
+(define (reg-op3 as i:reg i:op3 tail)
+  (let ((rs1 (operand1 i:reg))
+        (rs2 (operand2 i:op3))
+        (rs3 (operand3 i:op3))
+        (op  (operand1 i:op3)))
+    (if (hwreg? rs1)
+        (let ((op (case op
+                    ((vector-set!) 'internal:vector-set!)
+                    ((string-set!) 'internal:string-set!)
+                    (else #f))))
+          (if op
+              (as-source! as (cons (list $reg/op3 op rs1 rs2 rs3) tail)))))))
+
+; Reg-setreg is not restricted to hardware registers, as $movereg is 
+; a standard instruction.
+
+(define (reg-setreg as i:reg i:setreg tail)
+  (let ((rs (operand1 i:reg))
+        (rd (operand1 i:setreg)))
+    (if (= rs rd)
+        (as-source! as tail)
+        (as-source! as (cons (list $movereg rs rd) tail)))))
+
+(define (reg-branchf as i:reg i:branchf tail)
+  (let ((rs (operand1 i:reg))
+        (L  (operand1 i:branchf)))
+    (if (hwreg? rs)
+        (as-source! as (cons (list $reg/branchf rs L) tail)))))
+
+(define (const-setreg as i:const i:setreg tail)
+  (let ((c  (operand1 i:const))
+        (rd (operand1 i:setreg)))
+    (if (hwreg? rd)
+        (as-source! as (cons (list $const/setreg c rd) tail)))))
+
+; Make-vector on vectors of known short length.
+
+(define (const-op2 as i:const i:op2 tail)
+  (let ((vn '#(make-vector:0 make-vector:1 make-vector:2 make-vector:3
+               make-vector:4 make-vector:5 make-vector:6 make-vector:7
+               make-vector:8 make-vector:9))
+        (c  (operand1 i:const))
+        (op (operand1 i:op2))
+        (r  (operand2 i:op2)))
+    (if (and (eq? op 'make-vector)
+             (fixnum? c)
+             (<= 0 c 9))
+        (as-source! as (cons (list $op2 (vector-ref vn c) r) tail)))))
+
+; Constants that can be synthesized in a single instruction can be
+; moved into RESULT in the delay slot of the return instruction.
+
+(define (const-return as i:const i:return tail)
+  (let ((c (operand1 i:const)))
+    (if (or (and (number? c) (immediate-int? c))
+            (null? c)
+            (boolean? c))
+        (as-source! as (cons (list $const/return c) tail)))))
+
+; This allows the use of hardware 'call' instructions.
+;    (setrtn Lx)
+;    (branch Ly k)
+;    (.align k)            Ignored on SPARC
+;    (.label Lx)
+; => (setrtn/branch Ly k)
+;    (.label Lx)
+
+(define (setrtn-branch as i:setrtn i:branch i:align i:label tail)
+  (let ((return-label (operand1 i:setrtn))
+        (branch-ops   (cdr i:branch))
+        (label        (operand1 i:label)))
+    (if (= return-label label)
+        (as-source! as (cons (cons $setrtn/branch branch-ops)
+                             (cons i:label
+                                   tail))))))
+
+; Ditto for 'invoke'.
+;
+; Disabled because it does _not_ pay off on the SPARC currently -- 
+; probably, the dependency created between 'jmpl' and 'st' is not 
+; handled well on the test machine (an Ultrasparc).  Might work 
+; better if the return address were to be kept in a register always.
+
+(define (setrtn-invoke as i:setrtn i:invoke i:align i:label tail)
+  (let ((return-label (operand1 i:setrtn))
+        (invoke-ops   (operand1 i:invoke))
+        (label        (operand1 i:label)))
+    (if (and #f                                ; DISABLED
+             (= return-label label))
+        (as-source! as (cons (cons $setrtn/invoke invoke-ops)
+                             (cons i:label
+                                   tail))))))
+
+; Gets rid of spurious branch-to-next-instruction
+;    (branch Lx k)
+;    (.align y)
+;    (.label Lx)
+; => (.align y)
+;    (.label Lx)
+
+(define (branch-and-label as i:branch i:align i:label tail)
+  (let ((branch-label (operand1 i:branch))
+        (label        (operand1 i:label)))
+    (if (= branch-label label)
+        (as-source! as (cons i:align (cons i:label tail))))))
+
+(define (global-setreg as i:global i:setreg tail)
+  (let ((global (operand1 i:global))
+        (rd     (operand1 i:setreg)))
+    (if (hwreg? rd)
+        (as-source! as (cons (list $global/setreg global rd) tail)))))
+
+; Obscure guard: unsafe-code = #t implies that global/invoke will not
+; check the value of the global variable, yet unsafe-code and
+; catch-undefined-globals are supposed to be independent.
+
+(define (global-invoke as i:global i:invoke tail)
+  (let ((global (operand1 i:global))
+        (argc   (operand1 i:invoke)))
+    (if (not (and (unsafe-code) (catch-undefined-globals)))
+        (as-source! as (cons (list $global/invoke global argc) tail)))))
+
+; Obscure guard: see comment for previous procedure.
+; FIXME!  This implementation is temporary until setrtn-invoke is enabled.
+
+(define (global-setrtn-invoke as i:global i:setrtn i:invoke tail)
+  (let ((global (operand1 i:global))
+        (argc   (operand1 i:invoke)))
+    (if (not (and (unsafe-code) (catch-undefined-globals)))
+        (as-source! as (cons i:setrtn 
+                             (cons (list $global/invoke global argc)
+                                   tail))))))
+
+(define (reg-setglbl as i:reg i:setglbl tail)
+  (let ((rs     (operand1 i:reg))
+        (global (operand1 i:setglbl)))
+    (if (hwreg? rs)
+        (as-source! as (cons (list $reg/setglbl rs global) tail)))))
+
+
+
+; Test code
+
+(define (peeptest istream)
+  (let ((as (make-assembly-structure istream)))
+    (let loop ((l '()))
+      (if (null? (as-source as))
+          (reverse l)
+          (begin (peep as)
+                 (let ((a (car (as-source as))))
+                   (as-source! as (cdr (as-source as)))
+                   (loop (cons a l))))))))
+
+
+; eof
+; Copyright 1998 Lars T Hansen.
+;
+; $Id: twobit.sch,v 1.3 1999/08/23 19:14:26 lth Exp $
+;
+; SPARC assembler machine parameters & utility procedures.
+;
+; 13 May 1999 / wdc
+
+; Round up to nearest 8.
+
+(define (roundup8 n)
+  (* (quotient (+ n 7) 8) 8))
+
+; Given an integer code for a register, return its register label.
+; This register label is the register number for a h.w. register and the
+; offsets from GLOBALS[ r0 ] for a s.w. register.
+
+(define regname
+  (let ((v (vector $r.reg0  $r.reg1  $r.reg2  $r.reg3  $r.reg4  $r.reg5
+                   $r.reg6  $r.reg7  $r.reg8  $r.reg9  $r.reg10 $r.reg11
+                   $r.reg12 $r.reg13 $r.reg14 $r.reg15 $r.reg16 $r.reg17
+                   $r.reg18 $r.reg19 $r.reg20 $r.reg21 $r.reg22 $r.reg23
+                   $r.reg24 $r.reg25 $r.reg26 $r.reg27 $r.reg28 $r.reg29
+                   $r.reg30 $r.reg31)))
+    (lambda (r)
+      (vector-ref v r))))
+
+; Is a general-purpose register mapped to a hardware register?
+; This is fragile! FIXME.
+
+(define (hardware-mapped? r)
+  (or (and (>= r $r.reg0) (<= r $r.reg7))
+      (= r $r.argreg2)
+      (= r $r.argreg3)
+      (= r $r.result)
+      (= r $r.g0)
+      (= r $r.tmp0)
+      (= r $r.tmp1)
+      (= r $r.tmp2)))
+
+; Used by peephole optimizer
+
+(define (hwreg? x)
+  (<= 0 x 7))
+
+(define (immediate-int? x)
+  (and (exact? x)
+       (integer? x)
+       (<= -1024 x 1023)))
+
+; Given an exact integer, can it be represented as a fixnum?
+
+(define fixnum-range?
+  (let ((-two^29  (- (expt 2 29)))
+        (two^29-1 (- (expt 2 29) 1)))
+    (lambda (x)
+      (<= -two^29 x two^29-1))))
+
+; Does the integer x fit in the immediate field of an instruction?
+
+(define (immediate-literal? x)
+  (<= -4096 x 4095))
+
+; Return the offset in the %GLOBALS table of the given memory-mapped 
+; register. A memory-mapped register is represented by an integer which 
+; is its offet, so just return the value.
+
+(define (swreg-global-offset r) r)
+
+; Return a bit representation of a character constant.
+
+(define (char->immediate c)
+  (+ (* (char->integer c) 65536) $imm.character))
+
+; Convert an integer to a fixnum.
+
+(define (thefixnum x) (* x 4))
+
+; The offset of data slot 'n' within a procedure structure, not adjusting 
+; for tag. The proc is a header followed by code, const, and then data.
+
+(define (procedure-slot-offset n)
+  (+ 12 (* n 4)))
+
+; Src is a register, hwreg is a hardware register. If src is a
+; hardware register, return src. Otherwise, emit an instruction to load
+; src into hwreg and return hwreg.
+
+(define (force-hwreg! as src hwreg)
+  (if (hardware-mapped? src)
+      src
+      (emit-load-reg! as src hwreg)))
+
+; Given an arbitrary constant opd, generate code to load it into a
+; register r.
+
+(define (emit-constant->register as opd r)
+  (cond ((and (integer? opd) (exact? opd))
+         (if (fixnum-range? opd)       
+             (emit-immediate->register! as (thefixnum opd) r)
+             (emit-const->register! as (emit-datum as opd) r)))
+        ((boolean? opd)
+         (emit-immediate->register! as
+                                    (if (eq? opd #t)
+                                        $imm.true
+                                        $imm.false)
+                                    r))
+        ((equal? opd (eof-object))
+         (emit-immediate->register! as $imm.eof r))
+        ((equal? opd (unspecified))
+         (emit-immediate->register! as $imm.unspecified r))
+        ((equal? opd (undefined))
+         (emit-immediate->register! as $imm.undefined r))
+        ((null? opd)
+         (emit-immediate->register! as $imm.null r))
+        ((char? opd)
+         (emit-immediate->register! as (char->immediate opd) r))
+        (else
+         (emit-const->register! as (emit-datum as opd) r))))
+
+
+; Stuff a bitpattern or symbolic expression into a register.
+; (CONST, for immediate constants.)
+;
+; FIXME(?): if this had access to eval-expr (currently hidden inside the
+; sparc assembler) it could attempt to evaluate symbolic expressions,
+; thereby selecting better code sequences when possible.
+
+(define (emit-immediate->register! as i r)
+  (let ((dest (if (not (hardware-mapped? r)) $r.tmp0 r)))
+    (cond ((and (number? i) (immediate-literal? i))
+           (sparc.set as i dest))
+          ((and (number? i) (zero? (remainder (abs i) 1024)))
+           (sparc.sethi as `(hi ,i) dest))
+          (else
+           (sparc.sethi as `(hi ,i) dest)
+           (sparc.ori as dest `(lo ,i) dest)))
+    (if (not (hardware-mapped? r))
+        (emit-store-reg! as r dest))))
+
+
+; Reference the constants vector and put the constant reference in a register.
+; `offset' is an integer offset into the constants vector (a constant) for
+; the current procedure.
+; Destroys $r.tmp0 and $r.tmp1, but either can be the destination register.
+; (CONST, for structured constants, GLOBAL, SETGLBL, LAMBDA).
+
+(define (emit-const->register! as offset r)
+  (let ((cvlabel (+ 4 (- (* offset 4) $tag.vector-tag))))
+    (cond ((hardware-mapped? r)
+           (sparc.ldi as $r.reg0 $p.constvector $r.tmp0)
+           (if (asm:fits? cvlabel 13)
+               (sparc.ldi as $r.tmp0 cvlabel r)
+               (begin (sparc.sethi as `(hi ,cvlabel) $r.tmp1)
+                      (sparc.addr  as $r.tmp0 $r.tmp1 $r.tmp0)
+                      (sparc.ldi   as $r.tmp0 `(lo ,cvlabel) r))))
+          (else
+           (emit-const->register! as offset $r.tmp0)
+           (emit-store-reg! as $r.tmp0 r)))))
+
+
+
+; Emit single instruction to load sw-mapped reg into another reg, and return
+; the destination reg.
+
+(define (emit-load-reg! as from to)
+  (if (or (hardware-mapped? from) (not (hardware-mapped? to)))
+      (asm-error "emit-load-reg: " from to)
+      (begin (sparc.ldi as $r.globals (swreg-global-offset from) to)
+             to)))
+
+(define (emit-store-reg! as from to)
+  (if (or (not (hardware-mapped? from)) (hardware-mapped? to))
+      (asm-error "emit-store-reg: " from to)
+      (begin (sparc.sti as from (swreg-global-offset to) $r.globals)
+             to)))
+
+; Generic move-reg-to-HW-reg
+
+(define (emit-move2hwreg! as from to)
+  (if (hardware-mapped? from)
+      (sparc.move as from to)
+      (emit-load-reg! as from to))
+  to)
+
+; Evaluation of condition code for value or control.
+;
+; branchf.a is an annulled conditional branch that tests the condition codes
+;     and branches if some condition is false.
+; rd is #f or a hardware register.
+; target is #f or a label.
+; Exactly one of rd and target must be #f.
+;
+; (Why isn't this split into two separate procedures?  Because dozens of
+; this procedure's callers have the value/control duality, and it saves
+; space to put the test here instead of putting it in each caller.)
+
+(define (emit-evaluate-cc! as branchf.a rd target)
+  (if target
+      (begin (branchf.a   as target)
+             (sparc.slot  as))
+      (let ((target (new-label)))
+        (branchf.a   as target)
+        (sparc.set   as $imm.false rd)
+        (sparc.set   as $imm.true rd)
+        (sparc.label as target))))
+
+; Code for runtime safety checking.
+
+(define (emit-check! as rs0 L1 liveregs)
+  (sparc.cmpi as rs0 $imm.false)
+  (emit-checkcc! as sparc.be L1 liveregs))
+
+; FIXME:  This should call the exception handler for non-continuable exceptions.
+
+(define (emit-trap! as rs1 rs2 rs3 exn)
+  (if (not (= rs3 $r.reg0))
+      (emit-move2hwreg! as rs3 $r.argreg3))
+  (if (not (= rs2 $r.reg0))
+      (emit-move2hwreg! as rs2 $r.argreg2))
+  (if (not (= rs1 $r.reg0))
+      (emit-move2hwreg! as rs1 $r.result))
+  (millicode-call/numarg-in-reg as $m.exception (thefixnum exn) $r.tmp0))
+
+; Given:
+;     an annulled conditional branch that branches
+;         if the check is ok
+;     a non-annulled conditional branch that branches
+;         if the check is not ok
+;     #f, or a procedure that takes an assembly segment as
+;         argument and emits an instruction that goes into
+;         the delay slot of either branch
+;     three registers whose contents should be passed to the
+;         exception handler if the check is not ok
+;     the exception code
+; Emits code to call the millicode exception routine with
+; the given exception code if the condition is false.
+;
+; FIXME:  The nop can often be replaced by the instruction that
+; follows it.
+
+(begin
+ '
+(define (emit-checkcc-and-fill-slot!
+         as branch-ok.a branch-bad slot-filler L1)
+  (let* ((situation (list exn rs1 rs2 rs3))
+         (L1 (exception-label as situation)))
+    (if L1
+        (begin (branch-bad as L1)
+               (if slot-filler
+                   (slot-filler as)
+                   (sparc.nop as)))
+        (let* ((L1 (new-label))
+               (L2 (new-label)))
+          (exception-label-set! as situation L1)
+          (branch-ok.a as L2)
+          (if slot-filler
+              (slot-filler as)
+              (sparc.slot  as))
+          (sparc.label as L1)
+          (cond ((= rs3 $r.reg0)
+                 #f)
+                ((hardware-mapped? $r.argreg3)
+                 (emit-move2hwreg! as rs3 $r.argreg3))
+                ((hardware-mapped? rs3)
+                 (emit-store-reg! as rs3 $r.argreg3))
+                (else
+                 (emit-move2hwreg! as rs3 $r.tmp0)
+                 (emit-store-reg! as $r.tmp0 $r.argreg3)))
+          (if (not (= rs2 $r.reg0))
+              (emit-move2hwreg! as rs2 $r.argreg2))
+          (if (not (= rs1 $r.reg0))
+              (emit-move2hwreg! as rs1 $r.result))
+          ; FIXME:  This should be a non-continuable exception.
+          (sparc.jmpli as $r.millicode $m.exception $r.o7)
+          (emit-immediate->register! as (thefixnum exn) $r.tmp0)
+          (sparc.label as L2)))))
+#f
+)
+
+(define (emit-checkcc! as branch-bad L1 liveregs)
+  (branch-bad as L1)
+  (apply sparc.slot2 as liveregs))
+
+; Generation of millicode calls for non-continuable exceptions.
+
+(begin
+ '
+; To create only one millicode call per code segment per non-continuable
+; exception situation, we use the "as-user" feature of assembly segments.
+; Could use a hash table here.
+
+(define (exception-label as situation)
+  (let ((user-data (as-user as)))
+    (if user-data
+        (let ((exception-labels (assq 'exception-labels user-data)))
+          (if exception-labels
+              (let ((probe (assoc situation (cdr exception-labels))))
+                (if probe
+                    (cdr probe)
+                    #f))
+              #f))
+        #f)))
+'
+(define (exception-label-set! as situation label)
+  (let ((user-data (as-user as)))
+    (if user-data
+        (let ((exception-labels (assq 'exception-labels user-data)))
+          (if exception-labels
+              (let ((probe (assoc situation (cdr exception-labels))))
+                (if probe
+                    (error "COMPILER BUG: Exception situation defined twice")
+                    (set-cdr! exception-labels
+                              (cons (cons situation label)
+                                    (cdr exception-labels)))))
+              (begin (as-user! as
+                               (cons (list 'exception-labels)
+                                     user-data))
+                     (exception-label-set! as situation label))))
+        (begin (as-user! as '())
+               (exception-label-set! as situation label)))))
+#f
+)
+
+; Millicode calling
+
+(define (millicode-call/0arg as mproc)
+  (sparc.jmpli as $r.millicode mproc $r.o7)
+  (sparc.nop   as))
+
+(define (millicode-call/1arg as mproc r)
+  (sparc.jmpli as $r.millicode mproc $r.o7)
+  (emit-move2hwreg! as r $r.argreg2))
+
+(define (millicode-call/1arg-in-result as mproc r)
+  (millicode-call/1arg-in-reg as mproc r $r.result))
+
+(define (millicode-call/1arg-in-reg as mproc rs rd)
+  (sparc.jmpli as $r.millicode mproc $r.o7)
+  (emit-move2hwreg! as rs rd))
+
+(define (millicode-call/numarg-in-result as mproc num)
+  (sparc.jmpli as $r.millicode mproc $r.o7)
+  (sparc.set   as num $r.result))
+
+(define (millicode-call/numarg-in-reg as mproc num reg)
+  (if (not (hardware-mapped? reg))
+      (asm-error "millicode-call/numarg-in-reg requires HW register: " reg))
+  (sparc.jmpli as $r.millicode mproc $r.o7)
+  (sparc.set   as num reg))
+
+(define (millicode-call/2arg as mproc r1 r2)
+  (emit-move2hwreg! as r1 $r.argreg2)
+  (sparc.jmpli      as $r.millicode mproc $r.o7)
+  (emit-move2hwreg! as r2 $r.argreg3))
+
+; NOTE: Don't use TMP0 since TMP0 is sometimes a millicode argument
+; register (for example to m_exception).
+;
+; NOTE: Don't use sparc.set rather than sethi/ori; we need to know that
+; two instructions get generated.
+;
+; FIXME: Should calculate the value if possible to get better precision
+; and to avoid generating a fixup.  See emit-return-address! in gen-msi.sch.
+
+(define (millicode-call/ret as mproc label)
+  (cond ((short-effective-addresses)
+         (sparc.jmpli as $r.millicode mproc $r.o7)
+         (sparc.addi  as $r.o7 `(- ,label (- ,(here as) 4) 8) $r.o7))
+        (else
+         (let ((val `(- ,label (+ ,(here as) 8) 8)))
+           (sparc.sethi as `(hi ,val) $r.tmp1)
+           (sparc.ori   as $r.tmp1 `(lo ,val) $r.tmp1)
+           (sparc.jmpli as $r.millicode mproc $r.o7)
+           (sparc.addr  as $r.o7 $r.tmp1 $r.o7)))))
+
+(define (check-timer as DESTINATION RETRY)
+  (sparc.subicc as $r.timer 1 $r.timer)
+  (sparc.bne.a  as DESTINATION)
+  (sparc.slot   as)
+  (millicode-call/ret as $m.timer-exception RETRY))
+
+; When the destination and retry labels are the same, and follow the
+; timer check immediately, then this code saves two static instructions.
+
+(define (check-timer0 as)
+  (sparc.subicc as $r.timer 1 $r.timer)
+  (sparc.bne.a  as (+ (here as) 16))
+  (sparc.slot   as)
+  (sparc.jmpli as $r.millicode $m.timer-exception $r.o7)
+  (sparc.nop as))
+
+; eof
+; Copyright 1998 Lars T Hansen.
+;
+; $Id: twobit.sch,v 1.3 1999/08/23 19:14:26 lth Exp $
+;
+; 9 May 1999 / wdc
+;
+; SPARC machine assembler.
+;
+; The procedure `sparc-instruction' takes an instruction class keyword and
+; some operands and returns an assembler procedure for the instruction
+; denoted by the class and the operands.
+;
+; All assembler procedures for SPARC mnemonics are defined in sparcasm2.sch.
+;
+; The SPARC has 32-bit, big-endian words.  All instructions are 1 word.
+; This assembler currently accepts a subset of the SPARC v8 instruction set.
+;
+; Each assembler procedure takes an `as' assembly structure (see 
+; Asm/Common/pass5p1.sch) and operands relevant to the instruction, and
+; side-effects the assembly structure by emitting bits for the instruction
+; and any necessary fixups.  There are separate instruction mnemonics and
+; assembler procedures for instructions which in the SPARC instruction set 
+; are normally considered the "same".  For example, the `add' instruction is
+; split into two operations here: `sparc.addr' takes a register as operand2,
+; and `sparc.addi' takes an immediate.  We could remove this restriction
+; by using objects with identity rather than numbers for registers, but it
+; does not seem to be an important problem.
+;
+; Operands that denote values (addresses, immediates, offsets) may be
+; expressed using symbolic expressions. These expressions must conform
+; to the following grammar:
+;
+;   <expr> --> ( <number> . <obj> )        ; label
+;            | <number>                    ; literal value (exact integer)
+;            | (+ <expr> ... )             ; sum
+;            | (- <expr> ... )             ; difference
+;            | (hi <expr>)                 ; high 22 bits
+;            | (lo <expr>)                 ; low 10 bits
+;
+; Each assembler procedure will check that its value operand(s) fit in 
+; their instruction fields.  It is a fatal error for an operand not 
+; to fit, and the assembler calls `asm-error' to signal this error.  
+; However, in some cases the assembler will instead call the error 
+; procedure `asm-value-too-large', which allows the higher-level assembler 
+; to retry the assembly with different settings (typically, by splitting 
+; a jump instruction into an offset calculation and a jump).
+;
+; Note: the idiom that is seen in this file,
+;   (emit-fixup-proc! as (lambda (b l) (fixup b l)))
+; when `fixup' is a local procedure, avoids allocation of the closure
+; except in the cases where the fixup is in fact needed, for gains in
+; speed and reduction in allocation.  (Ask me if you want numbers.)
+;
+; If FILL-DELAY-SLOTS returns true, then this assembler supports two
+; distinct mechanisms for filling branch delay slots.
+;
+; An annulled conditional branch or an un-annulled unconditional branch
+; may be followed by the strange instruction SPARC.SLOT, which turns into
+; a nop in the delay slot that may be replaced by copying the instruction
+; at the target of the branch into the delay slot and increasing the branch
+; offset by 4.
+;
+; An un-annulled conditional branch whose target depends upon a known set
+; of general registers, and does not depend upon the condition codes, may
+; be followed by the strange instruction SPARC.SLOT2, which takes any
+; number of registers as operands.  This strange instruction turns into
+; nothing at all if the following instruction has no side effects except
+; to the condition codes and/or to a destination register that is distinct
+; from the specified registers plus the stack pointer and %o7; otherwise
+; the SPARC.SLOT2 instruction becomes a nop in the delay slot.  The
+; implementation of this uses a buffer that must be cleared when a label
+; is emitted or when the current offset is obtained.
+
+(define sparc-instruction)
+
+(let ((original-emit-label! emit-label!)
+      (original-here here))
+  (set! emit-label!
+        (lambda (as L)
+          (assembler-value! as 'slot2-info #f)
+          (original-emit-label! as L)))
+  (set! here
+        (lambda (as)
+          (assembler-value! as 'slot2-info #f)
+          (original-here as)))
+  'emit-label!)
+
+(let ((emit! (lambda (as bits)
+               (assembler-value! as 'slot2-info #f)
+               (emit! as bits)))
+      (emit-fixup-proc! (lambda (as proc)
+                          (assembler-value! as 'slot2-info #f)
+                          (emit-fixup-proc! as proc)))
+      (goes-in-delay-slot2? (lambda (as rd)
+                              (let ((regs (assembler-value as 'slot2-info)))
+                                (and regs
+                                     (fill-delay-slots)
+                                     (not (= rd $r.stkp))
+                                     (not (= rd $r.o7))
+                                     (not (memv rd regs)))))))
+  
+  (define ibit (asm:bv 0 0 #x20 0))     ; immediate bit: 2^13
+  (define abit (asm:bv #x20 0 0 0))     ; annul bit: 2^29
+  (define zero (asm:bv 0 0 0 0))        ; all zero bits
+  
+  (define two^32 (expt 2 32))
+  
+  ; Constant expression evaluation. If the expression cannot be 
+  ; evaluated, eval-expr returns #f, otherwise a number.
+  ; The symbol table lookup must fail by returning #f.
+  
+  (define (eval-expr as e)
+    
+    (define (complement x)
+      (modulo (+ two^32 x) two^32))
+    
+    (define (hibits e)
+      (cond ((not e) e)
+            ((< e 0)
+             (complement (quotient (complement e) 1024)))
+            (else
+             (quotient e 1024))))
+    
+    (define (lobits e)
+      (cond ((not e) e)
+            ((< e 0)
+             (remainder (complement e) 1024))
+            (else
+             (remainder e 1024))))
+    
+    (define (evaluate e)
+      (cond ((integer? e)      e)
+            ((label? e)        (label-value as e))
+            ((eq? 'hi (car e)) (hibits (evaluate (cadr e))))
+            ((eq? 'lo (car e)) (lobits (evaluate (cadr e))))
+            ((eq? '+ (car e))
+             (let loop ((e (cdr e)) (s 0))
+               (if (null? e) s
+                             (let ((op (evaluate (car e))))
+                               (if (not op) op
+                                            (loop (cdr e) (+ s op)))))))
+            ((eq? '- (car e))  
+             (let loop ((e (cdr e)) (d #f))
+               (if (null? e) d
+                             (let ((op (evaluate (car e))))
+                               (if (not op) op
+                                            (loop (cdr e) (if d (- d op) op)))))))
+            (else
+             (signal-error 'badexpr e))))
+    
+    (evaluate e))
+  
+  ; Common error handling.
+  
+  (define (signal-error code . rest)
+    (define msg "SPARC assembler: ")
+    (case code
+      ((badexpr)
+       (asm-error msg "invalid expression " (car rest)))
+      ((toolarge)
+       (asm-error msg "value too large in " (car rest) ": "
+                  (cadr rest) " = " (caddr rest)))
+      ((fixup)
+       (asm-error msg "fixup failed in " (car rest) " for " (cadr rest)))
+      ((unaligned)
+       (asm-error msg "unaligned target in " (car rest) ": " (cadr rest)))
+      (else 
+       (error "Invalid error code in assembler: " code))))
+  
+  ; The following procedures construct instructions by depositing field
+  ; values directly into bytevectors; the location parameter in the dep-*!
+  ; procedures is the address in the bytevector of the most significant byte.
+  
+  (define (copy! bv k bits)
+    (bytevector-set! bv k (bytevector-ref bits 0))
+    (bytevector-set! bv (+ k 1) (bytevector-ref bits 1))
+    (bytevector-set! bv (+ k 2) (bytevector-ref bits 2))
+    (bytevector-set! bv (+ k 3) (bytevector-ref bits 3))
+    bv)
+  
+  (define (copy bits)
+    (let ((bv (make-bytevector 4)))
+      (bytevector-set! bv 0 (bytevector-ref bits 0))
+      (bytevector-set! bv 1 (bytevector-ref bits 1))
+      (bytevector-set! bv 2 (bytevector-ref bits 2))
+      (bytevector-set! bv 3 (bytevector-ref bits 3))
+      bv))
+  
+  (define (copy-instr bv from to)
+    (bytevector-set! bv to (bytevector-ref bv from))
+    (bytevector-set! bv (+ to 1) (bytevector-ref bv (+ from 1)))
+    (bytevector-set! bv (+ to 2) (bytevector-ref bv (+ from 2)))
+    (bytevector-set! bv (+ to 3) (bytevector-ref bv (+ from 3))))
+  
+  (define (dep-rs1! bits k rs1)
+    (bytevector-set! bits (+ k 1)
+                          (logior (bytevector-ref bits (+ k 1))
+                                  (rshl rs1 2)))
+    (bytevector-set! bits (+ k 2)
+                          (logior (bytevector-ref bits (+ k 2))
+                                  (lsh (logand rs1 3) 6))))
+  
+  (define (dep-rs2! bits k rs2)
+    (bytevector-set! bits (+ k 3)
+                          (logior (bytevector-ref bits (+ k 3)) rs2)))
+  
+  (define (dep-rd! bits k rd)
+    (bytevector-set! bits k
+                          (logior (bytevector-ref bits k) (lsh rd 1))))
+  
+  (define (dep-imm! bits k imm)
+    (cond ((fixnum? imm)
+           (bytevector-set! bits (+ k 3) (logand imm 255))
+           (bytevector-set! bits (+ k 2)
+                                 (logior (bytevector-ref bits (+ k 2))
+                                         (logand (rsha imm 8) 31))))
+          ((bytevector? imm)
+           (bytevector-set! bits (+ k 3) (bytevector-ref imm 0))
+           (bytevector-set! bits (+ k 2)
+                                 (logior (bytevector-ref bits (+ k 2))
+                                         (logand (bytevector-ref imm 1)
+                                                 31))))
+          (else
+           (dep-imm! bits k (asm:int->bv imm)))))
+  
+  (define (dep-branch-offset! bits k offs)
+    (cond ((fixnum? offs)
+           (if (not (= (logand offs 3) 0))
+               (signal-error 'unaligned "branch" offs))
+           (dep-imm22! bits k (rsha offs 2)))
+          ((bytevector? offs)
+           (if (not (= (logand (bytevector-ref offs 3) 3) 0))
+               (signal-error 'unaligned "branch" (asm:bv->int offs)))
+           (dep-imm22! bits k (asm:rsha offs 2)))
+          (else
+           (dep-branch-offset! bits k (asm:int->bv offs)))))
+  
+  (define (dep-imm22! bits k imm)
+    (cond ((fixnum? imm)
+           (bytevector-set! bits (+ k 3) (logand imm 255))
+           (bytevector-set! bits (+ k 2)
+                                 (logand (rsha imm 8) 255))
+           (bytevector-set! bits (+ k 1)
+                                 (logior (bytevector-ref bits (+ k 1))
+                                         (logand (rsha imm 16) 63))))
+          ((bytevector? imm)
+           (bytevector-set! bits (+ k 3) (bytevector-ref imm 3))
+           (bytevector-set! bits (+ k 2) (bytevector-ref imm 2))
+           (bytevector-set! bits (+ k 1)
+                                 (logior (bytevector-ref bits (+ k 1))
+                                         (logand (bytevector-ref imm 1)
+                                                 63))))
+          (else
+           (dep-imm22! bits k (asm:int->bv imm)))))
+  
+  (define (dep-call-offset! bits k offs)
+    (cond ((fixnum? offs)
+           (if (not (= (logand offs 3) 0))
+               (signal-error 'unaligned "call" offs))
+           (bytevector-set! bits (+ k 3) (logand (rsha offs 2) 255))
+           (bytevector-set! bits (+ k 2) (logand (rsha offs 10) 255))
+           (bytevector-set! bits (+ k 1) (logand (rsha offs 18) 255))
+           (bytevector-set! bits k (logior (bytevector-ref bits k)
+                                           (logand (rsha offs 26) 63))))
+          ((bytevector? offs)
+           (if (not (= (logand (bytevector-ref offs 3) 3) 0))
+               (signal-error 'unaligned "call" (asm:bv->int offs)))
+           (let ((offs (asm:rsha offs 2)))
+             (bytevector-set! bits (+ k 3) (bytevector-ref offs 3))
+             (bytevector-set! bits (+ k 2) (bytevector-ref offs 2))
+             (bytevector-set! bits (+ k 1) (bytevector-ref offs 1))
+             (bytevector-set! bits k (logior (bytevector-ref bits k)
+                                             (logand (bytevector-ref offs 0)
+                                                     63)))))
+          (else
+           (dep-call-offset! bits k (asm:int->bv offs)))))
+  
+  ; Add 1 to an instruction (to bump a branch offset by 4).
+  ; FIXME: should check for field overflow.
+  
+  (define (add1 bv loc)
+    (let* ((r0 (+ (bytevector-ref bv (+ loc 3)) 1))
+           (d0 (logand r0 255))
+           (c0 (rshl r0 8)))
+      (bytevector-set! bv (+ loc 3) d0)
+      (let* ((r1 (+ (bytevector-ref bv (+ loc 2)) c0))
+             (d1 (logand r1 255))
+             (c1 (rshl r1 8)))
+        (bytevector-set! bv (+ loc 2) d1)
+        (let* ((r2 (+ (bytevector-ref bv (+ loc 1)) c1))
+               (d2 (logand r2 255)))
+          (bytevector-set! bv (+ loc 1) d2)))))
+  
+  ; For delay slot filling -- uses the assembler value scratchpad in
+  ; the as structure.  Delay slot filling is discussed in the comments
+  ; for `branch' and `class-slot', below.
+  
+  (define (remember-branch-target as obj)
+    (assembler-value! as 'branch-target obj))
+  
+  (define (recover-branch-target as)
+    (assembler-value as 'branch-target))
+  
+  ; Mark the instruction at the current address as not being eligible 
+  ; for being lifted into a branch delay slot.
+  ;
+  ; FIXME: should perhaps be a hash table; see BOOT-STATUS file for details.
+  
+  (define (not-a-delay-slot-instruction as)
+    (assembler-value! as 'not-dsi
+                         (cons (here as)
+                               (or (assembler-value as 'not-dsi) '()))))
+  
+  (define (is-a-delay-slot-instruction? as bv addr)
+    (and (not (memv addr (or (assembler-value as 'not-dsi) '())))
+         (< addr (bytevector-length bv))))
+  
+  ; SETHI, etc.
+  
+  (define (class-sethi bits)
+    (let ((bits (asm:lsh bits 22)))
+      (lambda (as val rd)
+        
+        (define (fixup bv loc)
+          (dep-imm22! bv loc
+                         (or (eval-expr as val)
+                             (signal-error 'fixup "sethi" val))))
+        
+        (define (fixup2 bv loc)
+          (copy! bv loc bits)
+          (dep-rd! bv loc rd)
+          (fixup bv loc))
+        
+        (if (goes-in-delay-slot2? as rd)
+            (emit-fixup-proc! as
+                              (lambda (b l)
+                                (fixup2 b (- l 4))))
+            
+            (let ((bits (copy bits))
+                  (e    (eval-expr as val)))
+              (if e
+                  (dep-imm22! bits 0 e)
+                  (emit-fixup-proc! as (lambda (b l) (fixup b l))))
+              (dep-rd! bits 0 rd)
+              (emit! as bits))))))
+  
+  ; NOP is a peculiar sethi
+  
+  (define (class-nop i)
+    (let ((instr (class-sethi i)))
+      (lambda (as)
+        (instr as 0 $r.g0))))
+  
+  
+  ; Branches
+  
+  (define (class00b i) (branch #b010 i zero))    ; Un-annulled IU branches.
+  (define (class00a i) (branch #b010 i abit))    ; Annulled IU branches.
+  (define (classf00b i) (branch #b110 i zero))   ; Un-annulled FP branches.
+  (define (classf00a i) (branch #b110 i abit))   ; Annulled FP branches.
+  
+  ; The `type' parameter is #b010 for IU branches, #b110 for FP branches.
+  ; The `bits' parameter is the bits for the cond field.
+  ; The `annul' parameter is either `zero' or `abit' (see top of file).
+  ;
+  ; Annuled branches require special treatement for delay slot
+  ; filling based on the `slot' pseudo-instruction.
+  ;
+  ; Strategy: when a branch with the annul bit set is assembled, remember 
+  ; its target in a one-element cache in the AS structure. When a slot
+  ; instruction is found (it has its own class) then the cached
+  ; value (possibly a delayed expression) is gotten, and a fixup for the
+  ; slot is registered.  When the fixup is later evaluated, the branch
+  ; target instruction can be found, examined, and evaluated. 
+  ; 
+  ; The cached value is always valid when the slot instruction is assembled,
+  ; because a slot instruction is always directly preceded by an annulled
+  ; branch (which will always set the cache).
+  
+  (define (branch type bits annul)
+    ; The delay slot should be filled if this is an annulled branch
+    ; or an unconditional branch.
+    (let ((fill-delay-slot? (or (not (eq? annul zero))
+                                (eq? bits #b1000)))
+          (bits (asm:logior (asm:lsh bits 25) (asm:lsh type 22) annul)))
+      (lambda (as target0)
+        (let ((target `(- ,target0 ,(here as))))
+          
+          (define (expr)
+            (let ((e (eval-expr as target)))
+              (cond ((not e)
+                     e)
+                    ((not (zero? (logand e 3)))
+                     (signal-error 'unaligned "branch" target0))
+                    ((asm:fits? e 24)
+                     e)
+                    (else
+                     (asm-value-too-large as "branch" target e)))))
+          
+          (define (fixup bv loc)
+            (let ((e (expr)))
+              (if e
+                  (dep-branch-offset! bv loc e)
+                  (signal-error 'fixup "branch" target0))))
+          
+          (if fill-delay-slot?
+              (remember-branch-target as target0)
+              (remember-branch-target as #f)) ; Clears the cache.
+          (not-a-delay-slot-instruction as)
+          (let ((bits (copy bits))
+                (e    (expr)))
+            (if e
+                (dep-branch-offset! bits 0 e)
+                (emit-fixup-proc! as (lambda (b l) (fixup b l))))
+            (emit! as bits))))))
+  
+  ; Branch delay slot pseudo-instruction.
+  ;
+  ; Get the branch target expression from the cache in the AS structure,
+  ; and if it is not #f, register a fixup procedure for the delay slot that 
+  ; will copy the target instruction to the slot and add 4 to the branch
+  ; offset (unless that will overflow the offset or the instruction at the
+  ; target is not suitable for lifting).
+  ;
+  ; It's important that this fixup run _after_ any fixups for the branch
+  ; instruction itself!
+  
+  (define (class-slot)
+    (let ((nop-instr (class-nop #b100)))
+      (lambda (as)
+        
+        ; The branch target is the expression denoting the target location.
+        
+        (define branch-target (recover-branch-target as))
+        
+        (define (fixup bv loc)
+          (let ((bt (or (eval-expr as branch-target)
+                        (asm-error "Branch fixup: can't happen: " 
+                                   branch-target))))
+            (if (is-a-delay-slot-instruction? as bv bt)
+                (begin
+                 (copy-instr bv bt loc)
+                 (add1 bv (- loc 4))))))
+        
+        (if (and branch-target (fill-delay-slots))
+            (emit-fixup-proc! as (lambda (b l) (fixup b l))))
+        (nop-instr as))))
+  
+  ; Branch delay slot pseudo-instruction 2.
+  ;
+  ; Emit a nop, but record the information that will allow this nop to be
+  ; replaced by a sufficiently harmless ALU instruction.
+  
+  (define (class-slot2)
+    (let ((nop-instr (class-nop #b100)))
+      (lambda (as . regs)
+        (nop-instr as)
+        (assembler-value! as 'slot2-info regs))))
+  
+  ; ALU stuff, register operand, rdy, wryr. Also: jump.
+  
+  (define (class10r bits . extra)
+    (cond ((and (not (null? extra)) (eq? (car extra) 'rdy))
+           (let ((op (class10r bits)))
+             (lambda (as rd)
+               (op as 0 0 rd))))
+          ((and (not (null? extra)) (eq? (car extra) 'wry))
+           (let ((op (class10r bits)))
+             (lambda (as rs)
+               (op as rs 0 0))))
+          (else
+           (let ((bits  (asm:logior (asm:lsh #b10 30) (asm:lsh bits 19)))
+                 (jump? (and (not (null? extra)) (eq? (car extra) 'jump))))
+             (lambda (as rs1 rs2 rd)
+               (let ((bits (copy bits)))
+                 (dep-rs1! bits 0 rs1)
+                 (dep-rs2! bits 0 rs2)
+                 (dep-rd! bits 0 rd)
+                 (cond (jump?
+                        (not-a-delay-slot-instruction as)
+                        (emit! as bits))
+                       ((goes-in-delay-slot2? as rd)
+                        (emit-fixup-proc!
+                         as
+                         (lambda (bv loc)
+                           (copy! bv (- loc 4) bits))))
+                       (else
+                        (emit! as bits)))))))))
+  
+  
+  ; ALU stuff, immediate operand, wryi. Also: jump.
+  
+  (define (class10i bits  . extra)
+    (if (and (not (null? extra)) (eq? (car extra) 'wry))
+        (let ((op (class10i bits)))
+          (lambda (as src)
+            (op as 0 src 0)))
+        (let ((bits  (asm:logior (asm:lsh #b10 30) (asm:lsh bits 19) ibit))
+              (jump? (and (not (null? extra)) (eq? (car extra) 'jump))))
+          (lambda (as rs1 e rd)
+            
+            (define (expr)
+              (let ((imm (eval-expr as e)))
+                (cond ((not imm)
+                       imm)
+                      ((asm:fits? imm 13)
+                       imm)
+                      (jump?
+                       (asm-value-too-large as "`jmpli'" e imm))
+                      (else
+                       (asm-value-too-large as "ALU instruction" e imm)))))
+            
+            (define (fixup bv loc)
+              (let ((e (expr)))
+                (if e
+                    (dep-imm! bv loc e)
+                    (signal-error 'fixup "ALU instruction" e))))
+            
+            (let ((bits (copy bits))
+                  (e    (expr)))
+              (if e
+                  (dep-imm! bits 0 e)
+                  (emit-fixup-proc! as (lambda (b l) (fixup b l))))
+              (dep-rs1! bits 0 rs1)
+              (dep-rd! bits 0 rd)
+              (cond (jump?
+                     (not-a-delay-slot-instruction as)
+                     (emit! as bits))
+                    ((goes-in-delay-slot2? as rd)
+                     (emit-fixup-proc!
+                      as
+                      (lambda (bv loc)
+                        (copy! bv (- loc 4) bits))))
+                    (else
+                     (emit! as bits))))))))
+  
+  ; Memory stuff, register operand.
+  
+  (define (class11r bits)
+    (let ((bits (asm:logior (asm:lsh #b11 30) (asm:lsh bits 19))))
+      (lambda (as rs1 rs2 rd)
+        (let ((bits (copy bits)))
+          (dep-rs1! bits 0 rs1)
+          (dep-rs2! bits 0 rs2)
+          (dep-rd! bits 0 rd)
+          (emit! as bits)))))
+  
+  ; Memory stuff, immediate operand.
+  
+  (define (class11i bits)
+    (let ((bits (asm:logior (asm:lsh #b11 30) (asm:lsh bits 19) ibit)))
+      (lambda (as rs1 e rd)
+        
+        (define (expr)
+          (let ((imm (eval-expr as e)))
+            (cond ((not imm) imm)
+                  ((asm:fits? imm 13) imm)
+                  (else 
+                   (signal-error 'toolarge "Memory instruction" e imm)))))
+        
+        (define (fixup bv loc)
+          (let ((e (expr)))
+            (if e
+                (dep-imm! bv loc e)
+                (signal-error 'fixup "Memory instruction" e))))
+        
+        (let ((bits (copy bits))
+              (e    (expr)))
+          (dep-rs1! bits 0 rs1)
+          (dep-rd! bits 0 rd)
+          (if e
+              (dep-imm! bits 0 e)
+              (emit-fixup-proc! as (lambda (b l) (fixup b l))))
+          (emit! as bits)))))
+  
+  ; For store instructions.  The syntax is (st a b c) meaning m[ b+c ] <- a.
+  ; However, on the Sparc, the destination (rd) field is  the source of
+  ; a store, so we transform the instruction into (st c b a) and pass it
+  ; to the real store procedure.
+  
+  (define (class11sr bits)
+    (let ((store-instr (class11r bits)))
+      (lambda (as a b c)
+        (store-instr as c b a))))
+  
+  (define (class11si bits)
+    (let ((store-instr (class11i bits)))
+      (lambda (as a b c)
+        (store-instr as c b a))))
+  
+  ; Call is a class all by itself.
+  
+  (define (class-call)
+    (let ((code (asm:lsh #b01 30)))
+      (lambda (as target0)
+        (let ((target `(- ,target0 ,(here as))))
+          
+          (define (fixup bv loc)
+            (let ((e (eval-expr as target)))
+              (if e
+                  (dep-call-offset! bv loc e)
+                  (signal-error 'fixup "call" target0))))
+          
+          (let ((bits (copy code))
+                (e    (eval-expr as target)))
+            (not-a-delay-slot-instruction as)
+            (if e
+                (dep-call-offset! bits 0 e)
+                (emit-fixup-proc! as (lambda (b l) (fixup b l))))
+            (emit! as bits))))))
+  
+  (define (class-label)
+    (lambda (as label)
+      (emit-label! as label)))
+  
+  ; FP operation, don't set CC.
+  
+  (define (class-fpop1 i) (fpop #b110100 i))
+  
+  ; FP operation, set CC
+  
+  (define (class-fpop2 i) (fpop #b110101 i))
+  
+  (define (fpop type opf)
+    (let ((bits (asm:logior (asm:lsh #b10 30)
+                            (asm:lsh type 19)
+                            (asm:lsh opf 5))))
+      (lambda (as rs1 rs2 rd)
+        (let ((bits (copy bits)))
+          (dep-rs1! bits 0 rs1)
+          (dep-rs2! bits 0 rs2)
+          (dep-rd! bits 0 rd)
+          (emit! as bits)))))
+  
+  (set! sparc-instruction
+        (lambda (kwd . ops)
+          (case kwd
+            ((i11)   (apply class11i ops))
+            ((r11)   (apply class11r ops))
+            ((si11)  (apply class11si ops))
+            ((sr11)  (apply class11sr ops))
+            ((sethi) (apply class-sethi ops))
+            ((r10)   (apply class10r ops))
+            ((i10)   (apply class10i ops))
+            ((b00)   (apply class00b ops))
+            ((a00)   (apply class00a ops))
+            ((call)  (apply class-call ops))
+            ((label) (apply class-label ops))
+            ((nop)   (apply class-nop ops))
+            ((slot)  (apply class-slot ops))
+            ((slot2) (apply class-slot2 ops))
+            ((fb00)  (apply classf00b ops))
+            ((fa00)  (apply classf00a ops))
+            ((fp)    (apply class-fpop1 ops))
+            ((fpcc)  (apply class-fpop2 ops))
+            (else
+             (asm-error "sparc-instruction: unrecognized class: " kwd)))))
+  'sparc-instruction)
+
+; eof
+; Instruction mnemonics
+
+(define sparc.lddi    (sparc-instruction 'i11 #b000011))
+(define sparc.lddr    (sparc-instruction 'r11 #b000011))
+(define sparc.ldi     (sparc-instruction 'i11 #b000000))
+(define sparc.ldr     (sparc-instruction 'r11 #b000000))
+(define sparc.ldhi    (sparc-instruction 'i11 #b000010))
+(define sparc.ldhr    (sparc-instruction 'r11 #b000010))
+(define sparc.ldbi    (sparc-instruction 'i11 #b000001))
+(define sparc.ldbr    (sparc-instruction 'r11 #b000001))
+(define sparc.lddfi   (sparc-instruction 'i11 #b100011))
+(define sparc.lddfr   (sparc-instruction 'r11 #b100011))
+(define sparc.stdi    (sparc-instruction 'si11 #b000111))
+(define sparc.stdr    (sparc-instruction 'sr11 #b000111))
+(define sparc.sti     (sparc-instruction 'si11 #b000100))
+(define sparc.str     (sparc-instruction 'sr11 #b000100))
+(define sparc.sthi    (sparc-instruction 'si11 #b000110))
+(define sparc.sthr    (sparc-instruction 'sr11 #b000110))
+(define sparc.stbi    (sparc-instruction 'si11 #b000101))
+(define sparc.stbr    (sparc-instruction 'sr11 #b000101))
+(define sparc.stdfi   (sparc-instruction 'si11 #b100111))
+(define sparc.stdfr   (sparc-instruction 'sr11 #b100111))
+(define sparc.sethi   (sparc-instruction 'sethi #b100))
+(define sparc.andr    (sparc-instruction 'r10 #b000001))
+(define sparc.andrcc  (sparc-instruction 'r10 #b010001))
+(define sparc.andi    (sparc-instruction 'i10 #b000001))
+(define sparc.andicc  (sparc-instruction 'i10 #b010001))
+(define sparc.orr     (sparc-instruction 'r10 #b000010))
+(define sparc.orrcc   (sparc-instruction 'r10 #b010010))
+(define sparc.ori     (sparc-instruction 'i10 #b000010))
+(define sparc.oricc   (sparc-instruction 'i10 #b010010))
+(define sparc.xorr    (sparc-instruction 'r10 #b000011))
+(define sparc.xorrcc  (sparc-instruction 'r10 #b010011))
+(define sparc.xori    (sparc-instruction 'i10 #b000011))
+(define sparc.xoricc  (sparc-instruction 'i10 #b010011))
+(define sparc.sllr    (sparc-instruction 'r10 #b100101))
+(define sparc.slli    (sparc-instruction 'i10 #b100101))
+(define sparc.srlr    (sparc-instruction 'r10 #b100110))
+(define sparc.srli    (sparc-instruction 'i10 #b100110))
+(define sparc.srar    (sparc-instruction 'r10 #b100111))
+(define sparc.srai    (sparc-instruction 'i10 #b100111))
+(define sparc.addr    (sparc-instruction 'r10 #b000000))
+(define sparc.addrcc  (sparc-instruction 'r10 #b010000))
+(define sparc.addi    (sparc-instruction 'i10 #b000000))
+(define sparc.addicc  (sparc-instruction 'i10 #b010000))
+(define sparc.taddrcc (sparc-instruction 'r10 #b100000))
+(define sparc.taddicc (sparc-instruction 'i10 #b100000))
+(define sparc.subr    (sparc-instruction 'r10 #b000100))
+(define sparc.subrcc  (sparc-instruction 'r10 #b010100))
+(define sparc.subi    (sparc-instruction 'i10 #b000100))
+(define sparc.subicc  (sparc-instruction 'i10 #b010100))
+(define sparc.tsubrcc (sparc-instruction 'r10 #b100001))
+(define sparc.tsubicc (sparc-instruction 'i10 #b100001))
+(define sparc.smulr   (sparc-instruction 'r10 #b001011))
+(define sparc.smulrcc (sparc-instruction 'r10 #b011011))
+(define sparc.smuli   (sparc-instruction 'i10 #b001011))
+(define sparc.smulicc (sparc-instruction 'i10 #b011011))
+(define sparc.sdivr   (sparc-instruction 'r10 #b001111))
+(define sparc.sdivrcc (sparc-instruction 'r10 #b011111))
+(define sparc.sdivi   (sparc-instruction 'i10 #b001111))
+(define sparc.sdivicc (sparc-instruction 'i10 #b011111))
+(define sparc.b       (sparc-instruction 'b00 #b1000))
+(define sparc.b.a     (sparc-instruction 'a00 #b1000))
+(define sparc.bne     (sparc-instruction 'b00 #b1001))
+(define sparc.bne.a   (sparc-instruction 'a00 #b1001))
+(define sparc.be      (sparc-instruction 'b00 #b0001))
+(define sparc.be.a    (sparc-instruction 'a00 #b0001))
+(define sparc.bg      (sparc-instruction 'b00 #b1010))
+(define sparc.bg.a    (sparc-instruction 'a00 #b1010))
+(define sparc.ble     (sparc-instruction 'b00 #b0010))
+(define sparc.ble.a   (sparc-instruction 'a00 #b0010))
+(define sparc.bge     (sparc-instruction 'b00 #b1011))
+(define sparc.bge.a   (sparc-instruction 'a00 #b1011))
+(define sparc.bl      (sparc-instruction 'b00 #b0011))
+(define sparc.bl.a    (sparc-instruction 'a00 #b0011))
+(define sparc.bgu     (sparc-instruction 'b00 #b1100))
+(define sparc.bgu.a   (sparc-instruction 'a00 #b1100))
+(define sparc.bleu    (sparc-instruction 'b00 #b0100))
+(define sparc.bleu.a  (sparc-instruction 'a00 #b0100))
+(define sparc.bcc     (sparc-instruction 'b00 #b1101))
+(define sparc.bcc.a   (sparc-instruction 'a00 #b1101))
+(define sparc.bcs     (sparc-instruction 'b00 #b0101))
+(define sparc.bcs.a   (sparc-instruction 'a00 #b0101))
+(define sparc.bpos    (sparc-instruction 'b00 #b1110))
+(define sparc.bpos.a  (sparc-instruction 'a00 #b1110))
+(define sparc.bneg    (sparc-instruction 'b00 #b0110))
+(define sparc.bneg.a  (sparc-instruction 'a00 #b0110))
+(define sparc.bvc     (sparc-instruction 'b00 #b1111))
+(define sparc.bvc.a   (sparc-instruction 'a00 #b1111))
+(define sparc.bvs     (sparc-instruction 'b00 #b0111))
+(define sparc.bvs.a   (sparc-instruction 'a00 #b0111))
+(define sparc.call    (sparc-instruction 'call))
+(define sparc.jmplr   (sparc-instruction 'r10 #b111000 'jump))
+(define sparc.jmpli   (sparc-instruction 'i10 #b111000 'jump))
+(define sparc.nop     (sparc-instruction 'nop #b100))
+(define sparc.ornr    (sparc-instruction 'r10 #b000110))
+(define sparc.orni    (sparc-instruction 'i10 #b000110))
+(define sparc.ornrcc  (sparc-instruction 'r10 #b010110))
+(define sparc.ornicc  (sparc-instruction 'i10 #b010110))
+(define sparc.andni   (sparc-instruction 'i10 #b000101))
+(define sparc.andnr   (sparc-instruction 'r10 #b000101))
+(define sparc.andnicc (sparc-instruction 'i10 #b010101))
+(define sparc.andnrcc (sparc-instruction 'r10 #b010101))
+(define sparc.rdy     (sparc-instruction 'r10 #b101000 'rdy))
+(define sparc.wryr    (sparc-instruction 'r10 #b110000 'wry))
+(define sparc.wryi    (sparc-instruction 'i10 #b110000 'wry))
+(define sparc.fb      (sparc-instruction 'fb00 #b1000))
+(define sparc.fb.a    (sparc-instruction 'fa00 #b1000))
+(define sparc.fbn     (sparc-instruction 'fb00 #b0000))
+(define sparc.fbn.a   (sparc-instruction 'fa00 #b0000))
+(define sparc.fbu     (sparc-instruction 'fb00 #b0111))
+(define sparc.fbu.a   (sparc-instruction 'fa00 #b0111))
+(define sparc.fbg     (sparc-instruction 'fb00 #b0110))
+(define sparc.fbg.a   (sparc-instruction 'fa00 #b0110))
+(define sparc.fbug    (sparc-instruction 'fb00 #b0101))
+(define sparc.fbug.a  (sparc-instruction 'fa00 #b0101))
+(define sparc.fbl     (sparc-instruction 'fb00 #b0100))
+(define sparc.fbl.a   (sparc-instruction 'fa00 #b0100))
+(define sparc.fbul    (sparc-instruction 'fb00 #b0011))
+(define sparc.fbul.a  (sparc-instruction 'fa00 #b0011))
+(define sparc.fblg    (sparc-instruction 'fb00 #b0010))
+(define sparc.fblg.a  (sparc-instruction 'fa00 #b0010))
+(define sparc.fbne    (sparc-instruction 'fb00 #b0001))
+(define sparc.fbne.a  (sparc-instruction 'fa00 #b0001))
+(define sparc.fbe     (sparc-instruction 'fb00 #b1001))
+(define sparc.fbe.a   (sparc-instruction 'fa00 #b1001))
+(define sparc.fbue    (sparc-instruction 'fb00 #b1010))
+(define sparc.fbue.a  (sparc-instruction 'fa00 #b1010))
+(define sparc.fbge    (sparc-instruction 'fb00 #b1011))
+(define sparc.fbge.a  (sparc-instruction 'fa00 #b1011))
+(define sparc.fbuge   (sparc-instruction 'fb00 #b1100))
+(define sparc.fbuge.a (sparc-instruction 'fa00 #b1100))
+(define sparc.fble    (sparc-instruction 'fb00 #b1101))
+(define sparc.fble.a  (sparc-instruction 'fa00 #b1101))
+(define sparc.fbule   (sparc-instruction 'fb00 #b1110))
+(define sparc.fbule.a (sparc-instruction 'fa00 #b1110))
+(define sparc.fbo     (sparc-instruction 'fb00 #b1111))
+(define sparc.fbo.a   (sparc-instruction 'fa00 #b1111))
+(define sparc.faddd   (sparc-instruction 'fp   #b001000010))
+(define sparc.fsubd   (sparc-instruction 'fp   #b001000110))
+(define sparc.fmuld   (sparc-instruction 'fp   #b001001010))
+(define sparc.fdivd   (sparc-instruction 'fp   #b001001110))
+(define sparc%fnegs   (sparc-instruction 'fp   #b000000101)) ; See below
+(define sparc%fmovs   (sparc-instruction 'fp   #b000000001)) ; See below
+(define sparc%fabss   (sparc-instruction 'fp   #b000001001)) ; See below
+(define sparc%fcmpdcc (sparc-instruction 'fpcc #b001010010)) ; See below
+
+; Strange instructions.
+
+(define sparc.slot    (sparc-instruction 'slot))
+(define sparc.slot2   (sparc-instruction 'slot2))
+(define sparc.label   (sparc-instruction 'label))
+
+; Aliases.
+
+(define sparc.bnz     sparc.bne)
+(define sparc.bnz.a   sparc.bne.a)
+(define sparc.bz      sparc.be)
+(define sparc.bz.a    sparc.be.a)
+(define sparc.bgeu    sparc.bcc)
+(define sparc.bgeu.a  sparc.bcc.a)
+(define sparc.blu     sparc.bcs)
+(define sparc.blu.a   sparc.bcs.a)
+
+; Abstractions.
+
+(define (sparc.cmpr as r1 r2) (sparc.subrcc as r1 r2 $r.g0))
+(define (sparc.cmpi as r imm) (sparc.subicc as r imm $r.g0))
+(define (sparc.move as rs rd) (sparc.orr as $r.g0 rs rd))
+(define (sparc.set as imm rd) (sparc.ori as $r.g0 imm rd))
+(define (sparc.btsti as rs imm) (sparc.andicc as rs imm $r.g0))
+(define (sparc.clr as rd) (sparc.move as $r.g0 rd))
+
+(define (sparc.deccc as rs . rest)
+  (let ((k (cond ((null? rest) 1)
+                 ((null? (cdr rest)) (car rest))
+                 (else (asm-error "sparc.deccc: too many operands: " rest)))))
+    (sparc.subicc as rs k rs)))
+
+; Floating-point abstractions
+;
+; For fmovd, fnegd, and fabsd, we must synthesize the instruction from
+; fmovs, fnegs, and fabss -- SPARC V8 has only the latter.  (SPARC V9 add
+; the former.)
+
+(define (sparc.fmovd as rs rd)
+  (sparc%fmovs as rs 0 rd)
+  (sparc%fmovs as (+ rs 1) 0 (+ rd 1)))
+
+(define (sparc.fnegd as rs rd)
+  (sparc%fnegs as rs 0 rd)
+  (if (not (= rs rd))
+      (sparc%fmovs as (+ rs 1) 0 (+ rd 1))))
+
+(define (sparc.fabsd as rs rd)
+  (sparc%fabss as rs 0 rd)
+  (if (not (= rs rd))
+      (sparc%fmovs as (+ rs 1) 0 (+ rd 1))))
+
+(define (sparc.fcmpd as rs1 rs2)
+  (sparc%fcmpdcc as rs1 rs2 0))
+
+; eof
+; Copyright 1998 Lars T Hansen.
+;
+; $Id: twobit.sch,v 1.3 1999/08/23 19:14:26 lth Exp $
+;
+; Asm/Sparc/gen-msi.sch -- SPARC assembler code emitters for 
+;    core MacScheme instructions
+;
+; 9 May 1999 / wdc
+
+
+; SETGLBL
+;
+; RS must be a hardware register.
+;
+; A global cell is a pair, where the car holds the value.
+
+(define (emit-register->global! as rs offset)
+  (cond ((= rs $r.result)
+        (sparc.move as $r.result $r.argreg2)
+        (emit-const->register! as offset $r.result)
+        (if (write-barrier)
+            (sparc.jmpli as $r.millicode $m.addtrans $r.o7))
+        (sparc.sti as $r.argreg2 (- $tag.pair-tag) $r.result))
+       (else
+        (emit-const->register! as offset $r.result)
+        (sparc.sti as rs (- $tag.pair-tag) $r.result)
+        (if (write-barrier)
+            (millicode-call/1arg as $m.addtrans rs)))))
+
+
+; GLOBAL
+;
+; A global cell is a pair, where the car holds the value.
+; If (catch-undefined-globals) is true, then code will be emitted to
+; check whether the global is #!undefined when loaded. If it is, 
+; an exception will be taken, with the global in question in $r.result.
+
+(define (emit-global->register! as offset r)
+  (emit-load-global as offset r (catch-undefined-globals)))
+
+; This leaves the cell in ARGREG2.  That fact is utilized by global/invoke
+; to signal an appropriate error message.
+
+(define (emit-load-global as offset r check?)
+  
+  (define (emit-undef-check! as r)
+    (if check?
+       (let ((GLOBAL-OK (new-label)))
+         (sparc.cmpi   as r $imm.undefined)
+         (sparc.bne.a  as GLOBAL-OK)
+         (sparc.slot   as)
+         (millicode-call/0arg as $m.global-ex)            ; Cell in ARGREG2.
+         (sparc.label  as GLOBAL-OK))))
+
+  (emit-const->register! as offset $r.argreg2)             ; Load cell.
+  (if (hardware-mapped? r)
+      (begin (sparc.ldi as $r.argreg2 (- $tag.pair-tag) r)
+            (emit-undef-check! as r))
+      (begin (sparc.ldi as $r.argreg2 (- $tag.pair-tag) $r.tmp0)
+            (emit-store-reg! as $r.tmp0 r)
+            (emit-undef-check! as $r.tmp0))))
+
+
+; MOVEREG
+
+(define (emit-register->register! as from to)
+  (if (not (= from to))
+      (cond ((and (hardware-mapped? from) (hardware-mapped? to))
+            (sparc.move as from to))
+           ((hardware-mapped? from)
+            (emit-store-reg! as from to))
+           ((hardware-mapped? to)
+            (emit-load-reg! as from to))
+           (else
+            (emit-load-reg! as from $r.tmp0)
+            (emit-store-reg! as $r.tmp0 to)))))
+
+
+; ARGS=
+
+(define (emit-args=! as n)
+  (if (not (unsafe-code))
+      (let ((L2 (new-label)))
+       (sparc.cmpi   as $r.result (thefixnum n))  ; FIXME: limit 1023 args
+       (sparc.be.a   as L2)
+       (sparc.slot   as)
+       (millicode-call/numarg-in-reg as $m.argc-ex (thefixnum n) $r.argreg2)
+       (sparc.label  as L2))))
+
+
+; ARGS>=
+;
+; The cases for 0 and 1 rest arguments are handled in-line; all other
+; cases, including too few, are handled in millicode (really: a C call-out).
+;
+; The fast path only applies when we don't have to mess with the last
+; register, hence the test.
+
+(define (emit-args>=! as n)
+  (let ((L0  (new-label))
+       (L99 (new-label))
+       (L98 (new-label)))
+    (if (< n (- *lastreg* 1))
+       (let ((dest (regname (+ n 1))))
+         (sparc.cmpi   as $r.result (thefixnum n)) ; n args
+         (if (hardware-mapped? dest)
+             (begin
+               (sparc.be.a as L99)
+               (sparc.set  as $imm.null dest))
+             (begin
+               (sparc.set  as $imm.null $r.tmp0)
+               (sparc.be.a as L99)
+               (sparc.sti  as $r.tmp0 (swreg-global-offset dest) $r.globals)))
+         (sparc.cmpi   as $r.result (thefixnum (+ n 1))) ; n+1 args
+         (sparc.bne.a  as L98)
+         (sparc.nop    as)
+         (millicode-call/numarg-in-result as $m.alloc 8)
+         (let ((src1 (force-hwreg! as dest $r.tmp1)))
+           (sparc.set as $imm.null $r.tmp0)
+           (sparc.sti as src1 0 $r.result)
+           (sparc.sti as $r.tmp0 4 $r.result)
+           (sparc.addi as $r.result $tag.pair-tag $r.result)
+           (sparc.b as L99)
+           (if (hardware-mapped? dest)
+               (sparc.move as $r.result dest)
+               (sparc.sti  as $r.result (swreg-global-offset dest)
+                           $r.globals)))))
+    ; General case
+    (sparc.label  as L98)
+    (sparc.move   as $r.reg0 $r.argreg3)  ; FIXME in Sparc/mcode.s
+    (millicode-call/numarg-in-reg as $m.varargs (thefixnum n) $r.argreg2)
+    (sparc.label  as L99)))
+
+
+; INVOKE
+; SETRTN/INVOKE
+;
+; Bummed.  Can still do better when the procedure to call is in a general
+; register (avoids the redundant move to RESULT preceding INVOKE).
+;
+; Note we must set up the argument count even in unsafe mode, because we 
+; may be calling code that was not compiled unsafe.
+
+(define (emit-invoke as n setrtn? mc-exception)
+  (let ((START    (new-label))
+        (TIMER-OK (new-label))
+        (PROC-OK  (new-label)))
+    (cond ((not (unsafe-code))
+           (sparc.label        as START)
+           (sparc.subicc       as $r.timer 1 $r.timer)
+           (sparc.bne          as TIMER-OK)
+           (sparc.andi         as $r.result $tag.tagmask $r.tmp0)
+           (millicode-call/ret as $m.timer-exception START)
+           (sparc.label        as TIMER-OK)
+           (sparc.cmpi         as $r.tmp0 $tag.procedure-tag)
+           (sparc.be.a         as PROC-OK)
+           (sparc.ldi          as $r.result $p.codevector $r.tmp0)
+           (millicode-call/ret as mc-exception START)
+           (sparc.label        as PROC-OK))
+          (else
+           (sparc.label        as START)
+           (sparc.subicc       as $r.timer 1 $r.timer)
+           (sparc.bne.a        as TIMER-OK)
+           (sparc.ldi          as $r.result $p.codevector $r.tmp0)
+           (millicode-call/ret as $m.timer-exception START)
+           (sparc.label        as TIMER-OK)))
+    (sparc.move                as $r.result $r.reg0)
+    ;; FIXME: limit 1023 args
+    (cond (setrtn?
+           (sparc.set          as (thefixnum n) $r.result)
+           (sparc.jmpli        as $r.tmp0 $p.codeoffset $r.o7)
+           (sparc.sti          as $r.o7 4 $r.stkp))
+          (else
+           (sparc.jmpli        as $r.tmp0 $p.codeoffset $r.g0)
+           (sparc.set          as (thefixnum n) $r.result)))))
+
+; SAVE -- for new compiler
+;
+; Create stack frame.  To avoid confusing the garbage collector, the
+; slots must be initialized to something definite unless they will
+; immediately be initialized by a MacScheme machine store instruction.
+; The creation is done by emit-save0!, and the initialization is done
+; by emit-save1!.
+
+(define (emit-save0! as n)
+  (let* ((L1        (new-label))
+        (L0        (new-label))
+        (framesize (+ 8 (* (+ n 1) 4)))
+        (realsize  (roundup8 (+ framesize 4))))
+    (sparc.label  as L0)
+    (sparc.subi   as $r.stkp realsize $r.stkp)
+    (sparc.cmpr   as $r.stklim $r.stkp)
+    (sparc.ble.a  as L1)
+    (sparc.set    as framesize $r.tmp0)
+    (sparc.addi   as $r.stkp realsize $r.stkp)
+    (millicode-call/ret as $m.stkoflow L0)
+    (sparc.label  as L1)
+    ; initialize size and return fields of stack frame
+    (sparc.sti    as $r.tmp0 0 $r.stkp)
+    (sparc.sti    as $r.g0 4 $r.stkp)))
+
+; Given a vector v of booleans, initializes slot i of the stack frame
+; if and only if (vector-ref v i).
+
+(define (emit-save1! as v)
+  (let ((n (vector-length v)))
+    (let loop ((i 0) (offset 12))
+      (cond ((= i n)
+             #t)
+            ((vector-ref v i)
+            (sparc.sti as $r.g0 offset $r.stkp)
+            (loop (+ i 1) (+ offset 4)))
+           (else
+            (loop (+ i 1) (+ offset 4)))))))
+
+
+; RESTORE
+;
+; Restore registers from stack frame
+; FIXME: Use ldd/std here; see comments for emit-save!, above.
+; We pop only actual registers.
+
+(define (emit-restore! as n)
+  (let ((n (min n 31)))
+    (do ((i      0  (+ i 1))
+        (offset 12 (+ offset 4)))
+       ((> i n))
+      (let ((r (regname i)))
+       (if (hardware-mapped? r)
+           (sparc.ldi as $r.stkp offset r)
+           (begin (sparc.ldi as $r.stkp offset $r.tmp0)
+                  (emit-store-reg! as $r.tmp0 r)))))))
+
+; POP -- for new compiler
+;
+; Pop frame.
+; If returning?, then emit the return as well and put the pop
+; in its delay slot.
+
+(define (emit-pop! as n returning?)
+  (let* ((framesize (+ 8 (* (+ n 1) 4)))
+        (realsize  (roundup8 (+ framesize 4))))
+    (if returning?
+        (begin (sparc.ldi   as $r.stkp (+ realsize 4) $r.o7)
+              (sparc.jmpli as $r.o7 8 $r.g0)
+              (sparc.addi  as $r.stkp realsize $r.stkp))
+        (sparc.addi as $r.stkp realsize $r.stkp))))
+
+
+; SETRTN
+;
+; Change the return address in the stack frame.
+
+(define (emit-setrtn! as label)
+  (emit-return-address! as label)
+  (sparc.sti as $r.o7 4 $r.stkp))
+
+
+; APPLY
+;
+; `apply' falls into millicode.
+;
+; The timer check is performed here because it is not very easy for the
+; millicode to do this.
+
+(define (emit-apply! as r1 r2)
+  (let ((L0 (new-label)))
+    (check-timer0        as)
+    (sparc.label         as L0)
+    (emit-move2hwreg!    as r1 $r.argreg2)
+    (emit-move2hwreg!    as r2 $r.argreg3)
+    (millicode-call/0arg as $m.apply)))
+
+
+; LOAD
+
+(define (emit-load! as slot dest-reg)
+  (if (hardware-mapped? dest-reg)
+      (sparc.ldi as $r.stkp (+ 12 (* slot 4)) dest-reg)
+      (begin (sparc.ldi as $r.stkp (+ 12 (* slot 4)) $r.tmp0)
+            (emit-store-reg! as $r.tmp0 dest-reg))))
+
+
+; STORE
+
+(define (emit-store! as k n)
+  (if (hardware-mapped? k)
+      (sparc.sti as k (+ 12 (* n 4)) $r.stkp)
+      (begin (emit-load-reg! as k $r.tmp0)
+            (sparc.sti as $r.tmp0 (+ 12 (* n 4)) $r.stkp))))
+
+
+; LEXICAL
+
+(define (emit-lexical! as m n)
+  (let ((base (emit-follow-chain! as m)))
+    (sparc.ldi as base (- (procedure-slot-offset n) $tag.procedure-tag)
+              $r.result)))
+
+
+; SETLEX
+; FIXME: should allow an in-line barrier
+
+(define (emit-setlex! as m n)
+  (let ((base (emit-follow-chain! as m)))
+    (sparc.sti as $r.result (- (procedure-slot-offset n) $tag.procedure-tag)
+              base)
+    (if (write-barrier)
+       (begin
+         (sparc.move as $r.result $r.argreg2)
+         (millicode-call/1arg-in-result as $m.addtrans base)))))
+
+
+; Follow static links.
+;
+; By using and leaving the result in ARGREG3 rather than in RESULT, 
+; we save a temporary register.
+
+(define (emit-follow-chain! as m)
+  (let loop ((q m))
+    (cond ((not (zero? q))
+          (sparc.ldi as
+                     (if (= q m) $r.reg0 $r.argreg3)
+                     $p.linkoffset
+                     $r.argreg3)
+          (loop (- q 1)))
+         ((zero? m) 
+          $r.reg0)
+         (else 
+          $r.argreg3))))
+
+; RETURN
+
+(define (emit-return! as)
+  (sparc.ldi   as $r.stkp 4 $r.o7)
+  (sparc.jmpli as $r.o7 8 $r.g0)
+  (sparc.nop   as))
+
+
+; RETURN-REG k
+
+(define (emit-return-reg! as r)
+  (sparc.ldi   as $r.stkp 4 $r.o7)
+  (sparc.jmpli as $r.o7 8 $r.g0)
+  (sparc.move  as r $r.result))
+
+
+; RETURN-CONST k
+;
+; The constant c must be synthesizable in a single instruction.
+
+(define (emit-return-const! as c)
+  (sparc.ldi   as $r.stkp 4 $r.o7)
+  (sparc.jmpli as $r.o7 8 $r.g0)
+  (emit-constant->register as c $r.result))
+
+
+; MVRTN
+
+(define (emit-mvrtn! as)
+  (asm-error "multiple-value return has not been implemented (yet)."))
+
+
+; LEXES
+
+(define (emit-lexes! as n-slots)
+  (emit-alloc-proc! as n-slots)
+  (sparc.ldi as $r.reg0 $p.codevector $r.tmp0)
+  (sparc.ldi as $r.reg0 $p.constvector $r.tmp1)
+  (sparc.sti as $r.tmp0 $p.codevector $r.result)
+  (sparc.sti as $r.tmp1 $p.constvector $r.result)
+  (emit-init-proc-slots! as n-slots))
+
+
+; LAMBDA
+
+(define (emit-lambda! as code-offs0 const-offs0 n-slots)
+  (let* ((code-offs  (+ 4 (- (* 4 code-offs0) $tag.vector-tag)))
+         (const-offs (+ 4 (- (* 4 const-offs0) $tag.vector-tag)))
+         (fits? (asm:fits? const-offs 13)))
+    (emit-alloc-proc! as n-slots)
+    (if fits?
+        (begin (sparc.ldi as $r.reg0 $p.constvector $r.tmp0)
+               (sparc.ldi as $r.tmp0 code-offs $r.tmp1))
+        (emit-const->register! as code-offs0 $r.tmp1))
+    (sparc.sti as $r.tmp1 $p.codevector $r.result)
+    (if fits?
+        (begin (sparc.ldi as $r.reg0 $p.constvector $r.tmp0)
+               (sparc.ldi as $r.tmp0 const-offs $r.tmp1))
+        (emit-const->register! as const-offs0 $r.tmp1))
+    (sparc.sti as $r.tmp1 $p.constvector $r.result)
+    (emit-init-proc-slots! as n-slots)))
+; Allocate procedure with room for n register slots; return tagged pointer.
+
+(define emit-alloc-proc!
+  (let ((two^12 (expt 2 12)))
+    (lambda (as n)
+      (millicode-call/numarg-in-result as $m.alloc (* (+ n 4) 4))
+      (let ((header (+ (* (* (+ n 3) 4) 256) $imm.procedure-header)))
+       (emit-immediate->register! as header $r.tmp0)
+       (sparc.sti  as $r.tmp0 0 $r.result)
+       (sparc.addi as $r.result $tag.procedure-tag $r.result)))))
+
+; Initialize data slots in procedure from current registers as specified for
+; `lamba' and `lexes'. If there are more data slots than registers, then
+; we must generate code to cdr down the list in the last register to obtain
+; the rest of the data. The list is expected to have at least the minimal
+; length.
+;
+; The tagged pointer to the procedure is in $r.result.
+
+(define (emit-init-proc-slots! as n)
+
+  (define (save-registers lo hi offset)
+    (do ((lo     lo     (+ lo 1))
+        (offset offset (+ offset 4)))
+       ((> lo hi))
+      (let ((r (force-hwreg! as (regname lo) $r.tmp0)))
+       (sparc.sti as r offset $r.result))))
+
+  (define (save-list lo hi offset)
+    (emit-load-reg! as $r.reg31 $r.tmp0)
+    (do ((lo     lo      (+ lo 1))
+        (offset offset (+ offset 4)))
+       ((> lo hi))
+      (sparc.ldi as $r.tmp0 (- $tag.pair-tag) $r.tmp1)
+      (sparc.sti as $r.tmp1 offset $r.result)
+      (if (< lo hi)
+         (begin 
+           (sparc.ldi as $r.tmp0 (+ (- $tag.pair-tag) 4) $r.tmp0)))))
+      
+  (cond ((< n *lastreg*)
+        (save-registers 0 n $p.reg0))
+       (else
+        (save-registers 0 (- *lastreg* 1) $p.reg0)
+        (save-list      *lastreg* n (+ $p.reg0 (* *lastreg* 4))))))
+
+; BRANCH
+
+(define (emit-branch! as check-timer? label)
+  (if check-timer?
+      (check-timer as label label)
+      (begin (sparc.b    as label)
+             (sparc.slot as))))
+
+
+; BRANCHF
+
+(define (emit-branchf! as label)
+  (emit-branchfreg! as $r.result label))
+
+
+; BRANCHFREG -- introduced by peephole optimization.
+
+(define (emit-branchfreg! as hwreg label)
+  (sparc.cmpi as hwreg $imm.false)
+  (sparc.be.a as label)
+  (sparc.slot as))
+
+
+; BRANCH-WITH-SETRTN -- introduced by peephole optimization
+
+(define (emit-branch-with-setrtn! as label)
+  (check-timer0 as)
+  (sparc.call   as label)
+  (sparc.sti    as $r.o7 4 $r.stkp))
+
+; JUMP
+;
+; Given the finalization order (outer is finalized before inner is assembled)
+; the label value will always be available when a jump is assembled.  The
+; only exception is when m = 0, but does this ever happen?  This code handles
+; the case anyway.
+
+(define (emit-jump! as m label)
+  (let* ((r      (emit-follow-chain! as m))
+        (labelv (label-value as label))
+        (v      (if (number? labelv)
+                    (+ labelv $p.codeoffset)
+                    (list '+ label $p.codeoffset))))
+    (sparc.ldi as r $p.codevector $r.tmp0)
+    (if (and (number? v) (immediate-literal? v))
+       (sparc.jmpli as $r.tmp0 v $r.g0)
+       (begin (emit-immediate->register! as v $r.tmp1)
+              (sparc.jmplr as $r.tmp0 $r.tmp1 $r.g0)))
+    (sparc.move  as r $r.reg0)))
+
+
+; .SINGLESTEP
+;
+; Single step: jump to millicode; pass index of documentation string in
+; %TMP0. Some instructions execute when reg0 is not a valid pointer to
+; the current procedure (because this is just after returning); in this
+; case we restore reg0 from the stack location given by 'funkyloc'.
+
+(define (emit-singlestep-instr! as funky? funkyloc cvlabel)
+  (if funky?
+      (sparc.ldi as $r.stkp (+ (thefixnum funkyloc) 12) $r.reg0))
+  (millicode-call/numarg-in-reg as $m.singlestep
+                                  (thefixnum cvlabel)
+                                  $r.argreg2))
+
+
+; Emit the effective address of a label-8 into %o7.
+;
+; There are multiple ways to do this.  If the call causes an expensive
+; bubble in the pipeline it is probably much less expensive to grub
+; the code vector address out of the procedure in REG0 and calculate it
+; that way.  FIXME: We need to benchmark these options.
+;
+; In general the point is moot as the common-case sequence
+;       setrtn L1
+;       invoke n
+;   L1:
+; should be peephole-optimized into the obvious fast code.
+
+(define (emit-return-address! as label)
+  (let* ((loc  (here as))
+        (lloc (label-value as label)))
+
+    (define (emit-short val)
+      (sparc.call as (+ loc 8))
+      (sparc.addi as $r.o7 val $r.o7))
+
+    (define (emit-long val)
+      ; Don't use sparc.set: we need to know that two instructions get
+      ; generated.
+      (sparc.sethi as `(hi ,val) $r.tmp0)
+      (sparc.ori   as $r.tmp0 `(lo ,val) $r.tmp0)
+      (sparc.call  as (+ loc 16))
+      (sparc.addr  as $r.o7 $r.tmp0 $r.o7))
+
+    (cond (lloc
+          (let ((target-rel-addr (- lloc loc 8)))
+            (if (immediate-literal? target-rel-addr)
+                (emit-short target-rel-addr)
+                (emit-long (- target-rel-addr 8)))))
+         ((short-effective-addresses)
+          (emit-short `(- ,label ,loc 8)))
+         (else
+          (emit-long `(- ,label ,loc 16))))))
+
+; eof
+; Copyright 1998 Lars T Hansen.
+; 
+; $Id: twobit.sch,v 1.3 1999/08/23 19:14:26 lth Exp $
+;
+; 22 April 1999 / wdc
+;
+; SPARC code generation macros for primitives, part 1:
+;   primitives defined in Compiler/sparc.imp.sch.
+
+; These extend Asm/Common/pass5p1.sch.
+
+(define (operand5 instruction)
+  (car (cddddr (cdr instruction))))
+
+(define (operand6 instruction)
+  (cadr (cddddr (cdr instruction))))
+
+(define (operand7 instruction)
+  (caddr (cddddr (cdr instruction))))
+
+
+; Primop emitters.
+
+(define (emit-primop.1arg! as op)
+  ((find-primop op) as))
+
+(define (emit-primop.2arg! as op r)
+  ((find-primop op) as r))
+
+(define (emit-primop.3arg! as a1 a2 a3)
+  ((find-primop a1) as a2 a3))
+
+(define (emit-primop.4arg! as a1 a2 a3 a4)
+  ((find-primop a1) as a2 a3 a4))
+
+(define (emit-primop.5arg! as a1 a2 a3 a4 a5)
+  ((find-primop a1) as a2 a3 a4 a5))
+
+(define (emit-primop.6arg! as a1 a2 a3 a4 a5 a6)
+  ((find-primop a1) as a2 a3 a4 a5 a6))
+
+(define (emit-primop.7arg! as a1 a2 a3 a4 a5 a6 a7)
+  ((find-primop a1) as a2 a3 a4 a5 a6 a7))
+
+
+; Hash table of primops
+
+(define primop-vector (make-vector 256 '()))
+
+(define (define-primop name proc)
+  (let ((h (logand (symbol-hash name) 255)))
+    (vector-set! primop-vector h (cons (cons name proc)
+                                      (vector-ref primop-vector h)))
+    name))
+
+(define (find-primop name)
+  (let ((h (logand (symbol-hash name) 255)))
+    (cdr (assq name (vector-ref primop-vector h)))))
+
+(define (for-each-primop proc)
+  (do ((i 0 (+ i 1)))
+      ((= i (vector-length primop-vector)))
+    (for-each (lambda (p)
+                (proc (cdr p)))
+              (vector-ref primop-vector i))))
+
+; Primops
+
+(define-primop 'unspecified
+  (lambda (as)
+    (emit-immediate->register! as $imm.unspecified $r.result)))
+
+(define-primop 'undefined
+  (lambda (as)
+    (emit-immediate->register! as $imm.undefined $r.result)))
+
+(define-primop 'eof-object
+  (lambda (as)
+    (emit-immediate->register! as $imm.eof $r.result)))
+
+(define-primop 'enable-interrupts
+  (lambda (as)
+    (millicode-call/0arg as $m.enable-interrupts)))
+
+(define-primop 'disable-interrupts
+  (lambda (as)
+    (millicode-call/0arg as $m.disable-interrupts)))
+
+(define-primop 'gc-counter
+  (lambda (as)
+    (sparc.ldi as $r.globals $g.gccnt $r.result)))
+
+(define-primop 'zero?
+  (lambda (as)
+    (emit-cmp-primop! as sparc.be.a $m.zerop $r.g0)))
+
+(define-primop '=
+  (lambda (as r)
+    (emit-cmp-primop! as sparc.be.a $m.numeq r)))
+
+(define-primop '<
+  (lambda (as r)
+    (emit-cmp-primop! as sparc.bl.a $m.numlt r)))
+
+(define-primop '<=
+  (lambda (as r)
+    (emit-cmp-primop! as sparc.ble.a $m.numle r)))
+
+(define-primop '>
+  (lambda (as r)
+    (emit-cmp-primop! as sparc.bg.a $m.numgt r)))
+
+(define-primop '>=
+  (lambda (as r)
+    (emit-cmp-primop! as sparc.bge.a $m.numge r)))
+
+(define-primop 'complex?
+  (lambda (as)
+    (millicode-call/0arg as $m.complexp)))
+
+(define-primop 'real?
+  (lambda (as)
+    (millicode-call/0arg as $m.realp)))
+
+(define-primop 'rational?
+  (lambda (as)
+    (millicode-call/0arg as $m.rationalp)))
+
+(define-primop 'integer?
+  (lambda (as)
+    (millicode-call/0arg as $m.integerp)))
+
+(define-primop 'exact?
+  (lambda (as)
+    (millicode-call/0arg as $m.exactp)))
+
+(define-primop 'inexact?
+  (lambda (as)
+    (millicode-call/0arg as $m.inexactp)))
+
+(define-primop 'fixnum?
+  (lambda (as)
+    (sparc.btsti as $r.result 3)
+    (emit-set-boolean! as)))
+
+(define-primop '+
+  (lambda (as r)
+    (emit-primop.4arg! as 'internal:+ $r.result r $r.result)))
+
+(define-primop '-
+  (lambda (as r)
+    (emit-primop.4arg! as 'internal:- $r.result r $r.result)))
+
+(define-primop '*
+  (lambda (as rs2)
+    (emit-multiply-code as rs2 #f)))
+
+(define (emit-multiply-code as rs2 fixnum-arithmetic?)
+  (if (and (unsafe-code) fixnum-arithmetic?)
+      (begin
+       (sparc.srai    as $r.result 2 $r.tmp0)
+       (sparc.smulr   as $r.tmp0 rs2 $r.result))
+      (let ((rs2    (force-hwreg! as rs2 $r.argreg2))
+           (Lstart (new-label))
+           (Ltagok (new-label))
+           (Loflo  (new-label))
+           (Ldone  (new-label)))
+       (sparc.label   as Lstart)
+       (sparc.orr     as $r.result rs2 $r.tmp0)
+       (sparc.btsti   as $r.tmp0 3)
+       (sparc.be.a    as Ltagok)
+       (sparc.srai    as $r.result 2 $r.tmp0)
+       (sparc.label   as Loflo)
+       (if (not (= rs2 $r.argreg2)) (sparc.move as rs2 $r.argreg2))
+       (if (not fixnum-arithmetic?)
+           (begin
+             (millicode-call/ret as $m.multiply Ldone))
+           (begin
+             (sparc.set as (thefixnum $ex.fx*) $r.tmp0)
+             (millicode-call/ret as $m.exception Lstart)))
+       (sparc.label   as Ltagok)
+       (sparc.smulr   as $r.tmp0 rs2 $r.tmp0)
+       (sparc.rdy     as $r.tmp1)
+       (sparc.srai    as $r.tmp0 31 $r.tmp2)
+       (sparc.cmpr    as $r.tmp1 $r.tmp2)
+       (sparc.bne.a   as Loflo)
+       (sparc.slot    as)
+       (sparc.move    as $r.tmp0 $r.result)
+       (sparc.label   as Ldone))))
+
+(define-primop '/
+  (lambda (as r)
+    (millicode-call/1arg as $m.divide r)))
+
+(define-primop 'quotient
+  (lambda (as r)
+    (millicode-call/1arg as $m.quotient r)))
+
+(define-primop 'remainder
+  (lambda (as r)
+    (millicode-call/1arg as $m.remainder r)))
+
+(define-primop '--
+  (lambda (as)
+    (emit-negate as $r.result $r.result)))
+
+(define-primop 'round
+  (lambda (as)
+    (millicode-call/0arg as $m.round)))
+
+(define-primop 'truncate
+  (lambda (as)
+    (millicode-call/0arg as $m.truncate)))
+
+(define-primop 'lognot
+  (lambda (as)
+    (if (not (unsafe-code))
+       (emit-assert-fixnum! as $r.result $ex.lognot))
+    (sparc.ornr as $r.g0 $r.result $r.result)  ; argument order matters
+    (sparc.xori as $r.result 3 $r.result)))
+
+(define-primop 'logand
+  (lambda (as x)
+    (logical-op as $r.result x $r.result sparc.andr $ex.logand)))
+
+(define-primop 'logior
+  (lambda (as x)
+    (logical-op as $r.result x $r.result sparc.orr $ex.logior)))
+
+(define-primop 'logxor
+  (lambda (as x)
+    (logical-op as $r.result x $r.result sparc.xorr $ex.logxor)))
+
+; Fixnum shifts.
+;
+; Only positive shifts are meaningful.
+; FIXME: These are incompatible with MacScheme and MIT Scheme.
+; FIXME: need to return to start of sequence after fault.
+
+(define-primop 'lsh
+  (lambda (as x)
+    (emit-shift-operation as $ex.lsh $r.result x $r.result)))
+
+(define-primop 'rshl
+  (lambda (as x)
+    (emit-shift-operation as $ex.rshl $r.result x $r.result)))
+
+(define-primop 'rsha
+  (lambda (as x)
+    (emit-shift-operation as $ex.rsha $r.result x $r.result)))
+
+
+; fixnums only.
+; FIXME: for symmetry with shifts there should be rotl and rotr (?)
+;        or perhaps rot should only ever rotate one way.
+; FIXME: implement.
+
+(define-primop 'rot
+  (lambda (as x)
+    (asm-error "Sparcasm: ROT primop is not implemented.")))
+
+(define-primop 'null?
+  (lambda (as)
+    (sparc.cmpi as $r.result $imm.null)
+    (emit-set-boolean! as)))
+
+(define-primop 'pair?
+  (lambda (as)
+    (emit-single-tagcheck->bool! as $tag.pair-tag)))
+
+(define-primop 'eof-object?
+  (lambda (as)
+    (sparc.cmpi as $r.result $imm.eof)
+    (emit-set-boolean! as)))
+
+; Tests the specific representation, not 'flonum or compnum with 0i'.
+
+(define-primop 'flonum?
+  (lambda (as)
+    (emit-double-tagcheck->bool! as $tag.bytevector-tag
+                                (+ $imm.bytevector-header
+                                   $tag.flonum-typetag))))
+
+(define-primop 'compnum?
+  (lambda (as)
+    (emit-double-tagcheck->bool! as $tag.bytevector-tag
+                                (+ $imm.bytevector-header
+                                   $tag.compnum-typetag))))
+
+(define-primop 'symbol?
+  (lambda (as)
+    (emit-double-tagcheck->bool! as $tag.vector-tag
+                                (+ $imm.vector-header
+                                   $tag.symbol-typetag))))
+
+(define-primop 'port?
+  (lambda (as)
+    (emit-double-tagcheck->bool! as $tag.vector-tag
+                                (+ $imm.vector-header
+                                   $tag.port-typetag))))
+
+(define-primop 'structure?
+  (lambda (as)
+    (emit-double-tagcheck->bool! as $tag.vector-tag
+                                (+ $imm.vector-header
+                                   $tag.structure-typetag))))
+
+(define-primop 'char?
+  (lambda (as)
+    (sparc.andi as $r.result #xFF $r.tmp0)
+    (sparc.cmpi as $r.tmp0 $imm.character)
+    (emit-set-boolean! as)))
+
+(define-primop 'string?
+  (lambda (as)
+    (emit-double-tagcheck->bool! as
+                                $tag.bytevector-tag
+                                (+ $imm.bytevector-header
+                                   $tag.string-typetag))))
+
+(define-primop 'bytevector?
+  (lambda (as)
+    (emit-double-tagcheck->bool! as
+                                $tag.bytevector-tag
+                                (+ $imm.bytevector-header
+                                   $tag.bytevector-typetag))))
+
+(define-primop 'bytevector-like?
+  (lambda (as)
+    (emit-single-tagcheck->bool! as $tag.bytevector-tag)))
+
+(define-primop 'vector?
+  (lambda (as)
+    (emit-double-tagcheck->bool! as
+                                $tag.vector-tag
+                                (+ $imm.vector-header
+                                   $tag.vector-typetag))))
+
+(define-primop 'vector-like?
+  (lambda (as)
+    (emit-single-tagcheck->bool! as $tag.vector-tag)))
+
+(define-primop 'procedure?
+  (lambda (as)
+    (emit-single-tagcheck->bool! as $tag.procedure-tag)))
+
+(define-primop 'cons
+  (lambda (as r)
+    (emit-primop.4arg! as 'internal:cons $r.result r $r.result)))
+
+(define-primop 'car
+  (lambda (as)
+    (emit-primop.3arg! as 'internal:car $r.result $r.result)))
+
+(define-primop 'cdr
+  (lambda (as)
+    (emit-primop.3arg! as 'internal:cdr $r.result $r.result)))
+
+(define-primop 'car:pair
+  (lambda (as)
+    (sparc.ldi as $r.result (- $tag.pair-tag) $r.result)))
+
+(define-primop 'cdr:pair
+  (lambda (as)
+    (sparc.ldi as $r.result (- 4 $tag.pair-tag) $r.result)))
+
+(define-primop 'set-car!
+  (lambda (as x)
+    (if (not (unsafe-code))
+       (emit-single-tagcheck-assert! as $tag.pair-tag $ex.car #f))
+    (emit-setcar/setcdr! as $r.result x 0)))
+
+(define-primop 'set-cdr!
+  (lambda (as x)
+    (if (not (unsafe-code))
+       (emit-single-tagcheck-assert! as $tag.pair-tag $ex.cdr #f))
+    (emit-setcar/setcdr! as $r.result x 4)))
+
+; Cells are internal data structures, represented using pairs.
+; No error checking is done on cell references.
+
+(define-primop 'make-cell
+  (lambda (as)
+    (emit-primop.4arg! as 'internal:cons $r.result $r.g0 $r.result)))
+
+(define-primop 'cell-ref
+  (lambda (as)
+    (emit-primop.3arg! as 'internal:cell-ref $r.result $r.result)))
+
+(define-primop 'cell-set!
+  (lambda (as r)
+    (emit-setcar/setcdr! as $r.result r 0)))
+
+(define-primop 'syscall
+  (lambda (as)
+    (millicode-call/0arg as $m.syscall)))
+
+(define-primop 'break
+  (lambda (as)
+    (millicode-call/0arg as $m.break)))
+
+(define-primop 'creg
+  (lambda (as)
+    (millicode-call/0arg as $m.creg)))
+
+(define-primop 'creg-set!
+  (lambda (as)
+    (millicode-call/0arg as $m.creg-set!)))
+
+(define-primop 'typetag
+  (lambda (as)
+    (millicode-call/0arg as $m.typetag)))
+
+(define-primop 'typetag-set!
+  (lambda (as r)
+    (millicode-call/1arg as $m.typetag-set r)))
+
+(define-primop 'exact->inexact
+  (lambda (as)
+    (millicode-call/0arg as $m.exact->inexact)))
+
+(define-primop 'inexact->exact
+  (lambda (as)
+    (millicode-call/0arg as $m.inexact->exact)))
+
+(define-primop 'real-part
+  (lambda (as)
+    (millicode-call/0arg as $m.real-part)))
+
+(define-primop 'imag-part
+  (lambda (as)
+    (millicode-call/0arg as $m.imag-part)))
+
+(define-primop 'char->integer
+  (lambda (as)
+    (if (not (unsafe-code))
+       (emit-assert-char! as $ex.char2int #f))
+    (sparc.srli as $r.result 14 $r.result)))
+
+(define-primop 'integer->char
+  (lambda (as)
+    (if (not (unsafe-code))
+       (emit-assert-fixnum! as $r.result $ex.int2char))
+    (sparc.andi as $r.result #x3FF $r.result)
+    (sparc.slli as $r.result 14 $r.result)
+    (sparc.ori  as $r.result $imm.character $r.result)))
+
+(define-primop 'not
+  (lambda (as)
+    (sparc.cmpi as $r.result $imm.false)
+    (emit-set-boolean! as)))
+
+(define-primop 'eq?
+  (lambda (as x)
+    (emit-primop.4arg! as 'internal:eq? $r.result x $r.result)))
+
+(define-primop 'eqv?
+  (lambda (as x)
+    (let ((tmp (force-hwreg! as x $r.tmp0))
+         (L1  (new-label)))
+      (sparc.cmpr as $r.result tmp)
+      (sparc.be.a as L1)
+      (sparc.set  as $imm.true $r.result)
+      (millicode-call/1arg as $m.eqv tmp)
+      (sparc.label as L1))))
+
+(define-primop 'make-bytevector
+  (lambda (as)
+    (if (not (unsafe-code))
+       (emit-assert-positive-fixnum! as $r.result $ex.mkbvl))
+    (emit-allocate-bytevector as
+                             (+ $imm.bytevector-header
+                                $tag.bytevector-typetag)
+                             #f)
+    (sparc.addi as $r.result $tag.bytevector-tag $r.result)))
+
+(define-primop 'bytevector-fill!
+  (lambda (as rs2)
+    (let* ((fault (emit-double-tagcheck-assert! as
+                                               $tag.bytevector-tag
+                                               (+ $imm.bytevector-header
+                                                  $tag.bytevector-typetag)
+                                               $ex.bvfill
+                                               rs2))
+          (rs2 (force-hwreg! as rs2 $r.argreg2)))
+      (sparc.btsti  as rs2 3)
+      (sparc.bne    as fault)
+      (sparc.srai   as rs2 2 $r.tmp2)
+      (sparc.ldi    as $r.result (- $tag.bytevector-tag) $r.tmp0)
+      (sparc.addi   as $r.result (- 4 $tag.bytevector-tag) $r.tmp1)
+      (sparc.srai   as $r.tmp0 8 $r.tmp0)
+      (emit-bytevector-fill as $r.tmp0 $r.tmp1 $r.tmp2))))
+
+(define-primop 'bytevector-length
+  (lambda (as)
+    (emit-get-length! as 
+                     $tag.bytevector-tag
+                     (+ $imm.bytevector-header $tag.bytevector-typetag)
+                     $ex.bvlen
+                     $r.result
+                     $r.result)))
+
+(define-primop 'bytevector-like-length
+  (lambda (as)
+    (emit-get-length! as
+                     $tag.bytevector-tag
+                     #f
+                     $ex.bvllen
+                     $r.result
+                     $r.result)))
+
+(define-primop 'bytevector-ref
+  (lambda (as r)
+    (let ((fault (if (not (unsafe-code))
+                    (emit-double-tagcheck-assert!
+                     as
+                     $tag.bytevector-tag
+                     (+ $imm.bytevector-header $tag.bytevector-typetag)
+                     $ex.bvref
+                     r)
+                    #f)))
+      (emit-bytevector-like-ref! as $r.result r $r.result fault #f #t))))
+
+(define-primop 'bytevector-like-ref
+  (lambda (as r)
+    (let ((fault (if (not (unsafe-code))
+                    (emit-single-tagcheck-assert! as
+                                                  $tag.bytevector-tag
+                                                  $ex.bvlref
+                                                  r)
+                    #f)))
+      (emit-bytevector-like-ref! as $r.result r $r.result fault #f #f))))
+
+(define-primop 'bytevector-set!
+  (lambda (as r1 r2)
+    (let ((fault (if (not (unsafe-code))
+                    (emit-double-tagcheck-assert!
+                     as
+                     $tag.bytevector-tag
+                     (+ $imm.bytevector-header $tag.bytevector-typetag)
+                     $ex.bvset
+                     r1)
+                    #f)))
+      (emit-bytevector-like-set! as r1 r2 fault #t))))
+
+(define-primop 'bytevector-like-set!
+  (lambda (as r1 r2)
+    (let ((fault (if (not (unsafe-code))
+                    (emit-single-tagcheck-assert! as
+                                                  $tag.bytevector-tag
+                                                  $ex.bvlset
+                                                  r1)
+                    #f)))
+      (emit-bytevector-like-set! as r1 r2 fault #f))))
+
+(define-primop 'sys$bvlcmp
+  (lambda (as x)
+    (millicode-call/1arg as $m.bvlcmp x)))
+
+; Strings
+
+; RESULT must have nonnegative fixnum.
+; RS2 must have character.
+
+(define-primop 'make-string
+  (lambda (as rs2)
+    (let ((FAULT (new-label))
+         (START (new-label)))
+      (sparc.label as START)
+      (let ((rs2 (force-hwreg! as rs2 $r.argreg2)))
+       (if (not (unsafe-code))
+           (let ((L1 (new-label))
+                 (L2 (new-label)))
+             (sparc.tsubrcc as $r.result $r.g0 $r.g0)
+             (sparc.bvc.a   as L1)
+             (sparc.andi    as rs2 255 $r.tmp0)
+             (sparc.label   as FAULT)
+             (if (not (= rs2 $r.argreg2))
+                 (sparc.move as rs2 $r.argreg2))
+             (sparc.set     as (thefixnum $ex.mkbvl) $r.tmp0) ; Wrong code.
+             (millicode-call/ret as $m.exception START)
+             (sparc.label   as L1)
+             (sparc.bl      as FAULT)
+             (sparc.cmpi    as $r.tmp0 $imm.character)
+             (sparc.bne     as FAULT)
+             (sparc.move as $r.result $r.argreg3))
+           (begin
+             (sparc.move as $r.result $r.argreg3)))
+       (emit-allocate-bytevector as
+                                 (+ $imm.bytevector-header
+                                    $tag.string-typetag)
+                                 $r.argreg3)
+       (sparc.srai   as rs2 16 $r.tmp1)
+       (sparc.addi   as $r.result 4 $r.result)
+       (sparc.srai   as $r.argreg3 2 $r.tmp0)
+       (emit-bytevector-fill as $r.tmp0 $r.result $r.tmp1)
+       (sparc.addi as $r.result (- $tag.bytevector-tag 4) $r.result)))))
+
+(define-primop 'string-length
+  (lambda (as)
+    (emit-primop.3arg! as 'internal:string-length $r.result $r.result)))
+
+(define-primop 'string-ref
+  (lambda (as r)
+    (emit-primop.4arg! as 'internal:string-ref $r.result r $r.result)))
+
+(define-primop 'string-set!
+  (lambda (as r1 r2)
+    (emit-string-set! as $r.result r1 r2)))
+
+(define-primop 'sys$partial-list->vector
+  (lambda (as r)
+    (millicode-call/1arg as $m.partial-list->vector r)))
+
+(define-primop 'make-procedure
+  (lambda (as)
+    (emit-make-vector-like! as
+                           '()
+                           $imm.procedure-header
+                           $tag.procedure-tag)))
+
+(define-primop 'make-vector
+  (lambda (as r)
+    (emit-make-vector-like! as
+                           r
+                           (+ $imm.vector-header $tag.vector-typetag)
+                           $tag.vector-tag)))
+
+(define-primop 'make-vector:0
+  (lambda (as r) (make-vector-n as 0 r)))
+
+(define-primop 'make-vector:1
+  (lambda (as r) (make-vector-n as 1 r)))
+
+(define-primop 'make-vector:2
+  (lambda (as r) (make-vector-n as 2 r)))
+
+(define-primop 'make-vector:3
+  (lambda (as r) (make-vector-n as 3 r)))
+
+(define-primop 'make-vector:4
+  (lambda (as r) (make-vector-n as 4 r)))
+
+(define-primop 'make-vector:5
+  (lambda (as r) (make-vector-n as 5 r)))
+
+(define-primop 'make-vector:6
+  (lambda (as r) (make-vector-n as 6 r)))
+
+(define-primop 'make-vector:7
+  (lambda (as r) (make-vector-n as 7 r)))
+
+(define-primop 'make-vector:8
+  (lambda (as r) (make-vector-n as 8 r)))
+
+(define-primop 'make-vector:9
+  (lambda (as r) (make-vector-n as 9 r)))
+
+(define-primop 'vector-length
+  (lambda (as)
+    (emit-primop.3arg! as 'internal:vector-length $r.result $r.result)))
+
+(define-primop 'vector-like-length
+  (lambda (as)
+    (emit-get-length! as $tag.vector-tag #f $ex.vllen $r.result $r.result)))
+
+(define-primop 'vector-length:vec
+  (lambda (as)
+    (emit-get-length-trusted! as $tag.vector-tag $r.result $r.result)))
+
+(define-primop 'procedure-length
+  (lambda (as)
+    (emit-get-length! as $tag.procedure-tag #f $ex.plen $r.result $r.result)))
+
+(define-primop 'vector-ref
+  (lambda (as r)
+    (emit-primop.4arg! as 'internal:vector-ref $r.result r $r.result)))
+
+(define-primop 'vector-like-ref
+  (lambda (as r)
+    (let ((fault (if (not (unsafe-code))
+                    (emit-single-tagcheck-assert! as
+                                                  $tag.vector-tag
+                                                  $ex.vlref
+                                                  r)
+                    #f)))
+      (emit-vector-like-ref!
+       as $r.result r $r.result fault $tag.vector-tag #f))))
+
+(define-primop 'vector-ref:trusted
+  (lambda (as rs2)
+    (emit-vector-like-ref-trusted!
+     as $r.result rs2 $r.result $tag.vector-tag)))
+
+(define-primop 'procedure-ref
+  (lambda (as r)
+    (let ((fault (if (not (unsafe-code))
+                    (emit-single-tagcheck-assert! as
+                                                  $tag.procedure-tag
+                                                  $ex.pref
+                                                  r)
+                    #f)))
+      (emit-vector-like-ref!
+       as $r.result r $r.result fault $tag.procedure-tag #f))))
+
+(define-primop 'vector-set!
+  (lambda (as r1 r2)
+    (emit-primop.4arg! as 'internal:vector-set! $r.result r1 r2)))
+
+(define-primop 'vector-like-set!
+  (lambda (as r1 r2)
+    (let ((fault (if (not (unsafe-code))
+                    (emit-single-tagcheck-assert! as
+                                                  $tag.vector-tag
+                                                  $ex.vlset
+                                                  r1)
+                    #f)))
+      (emit-vector-like-set! as $r.result r1 r2 fault $tag.vector-tag #f))))
+
+(define-primop 'vector-set!:trusted
+  (lambda (as rs2 rs3)
+    (emit-vector-like-set-trusted! as $r.result rs2 rs3 $tag.vector-tag)))
+
+(define-primop 'procedure-set!
+  (lambda (as r1 r2)
+    (let ((fault (if (not (unsafe-code))
+                    (emit-single-tagcheck-assert! as
+                                                  $tag.procedure-tag
+                                                  $ex.pset
+                                                  r1)
+                    #f)))
+      (emit-vector-like-set! as $r.result r1 r2 fault $tag.procedure-tag #f))))
+
+(define-primop 'char<?
+  (lambda (as x)
+    (emit-char-cmp as x sparc.bl.a $ex.char<?)))
+
+(define-primop 'char<=?
+  (lambda (as x)
+    (emit-char-cmp as x sparc.ble.a $ex.char<=?)))
+
+(define-primop 'char=?
+  (lambda (as x)
+    (emit-char-cmp as x sparc.be.a $ex.char=?)))
+
+(define-primop 'char>?
+  (lambda (as x)
+    (emit-char-cmp as x sparc.bg.a $ex.char>?)))
+
+(define-primop 'char>=?
+  (lambda (as x)
+    (emit-char-cmp as x sparc.bge.a $ex.char>=?)))
+
+; Experimental (for performance).
+; This makes massive assumptions about the layout of the port structure:
+; A port is a vector-like where
+;   #0 = port.input?
+;   #4 = port.buffer
+;   #7 = port.rd-lim
+;   #8 = port.rd-ptr
+; See Lib/iosys.sch for more information.
+
+(define-primop 'sys$read-char
+  (lambda (as)
+    (let ((Lfinish (new-label))
+         (Lend    (new-label)))
+      (if (not (unsafe-code))
+         (begin
+           (sparc.andi as $r.result $tag.tagmask $r.tmp0) ; mask argument tag
+           (sparc.cmpi as $r.tmp0 $tag.vector-tag); vector-like? 
+           (sparc.bne as Lfinish)                 ; skip if not vector-like
+           (sparc.nop as)
+           (sparc.ldbi as $r.RESULT 0 $r.tmp1)))   ; header byte
+      (sparc.ldi  as $r.RESULT 1 $r.tmp2)          ; port.input? or garbage
+      (if (not (unsafe-code))
+         (begin
+           (sparc.cmpi as $r.tmp1 $hdr.port)       ; port?
+           (sparc.bne as Lfinish)))                ; skip if not port
+      (sparc.cmpi as $r.tmp2 $imm.false)           ; [slot] input port?
+      (sparc.be as Lfinish)                        ; skip if not active port
+      (sparc.ldi as $r.RESULT (+ 1 32) $r.tmp1)            ; [slot] port.rd-ptr 
+      (sparc.ldi as $r.RESULT (+ 1 28) $r.tmp2)            ; port.rd-lim
+      (sparc.ldi as $r.RESULT (+ 1 16) $r.tmp0)            ; port.buffer
+      (sparc.cmpr as $r.tmp1 $r.tmp2)              ; rd-ptr < rd-lim?
+      (sparc.bge as Lfinish)                       ; skip if rd-ptr >= rd-lim
+      (sparc.subi as $r.tmp0 1 $r.tmp0)                    ; [slot] addr of string@0
+      (sparc.srai as $r.tmp1 2 $r.tmp2)                    ; rd-ptr as native int
+      (sparc.ldbr as $r.tmp0 $r.tmp2 $r.tmp2)      ; get byte from string
+      (sparc.addi as $r.tmp1 4 $r.tmp1)                    ; bump rd-ptr
+      (sparc.sti as $r.tmp1 (+ 1 32) $r.RESULT)            ; store rd-ptr in port
+      (sparc.slli as $r.tmp2 16 $r.tmp2)           ; convert to char #1
+      (sparc.b as Lend)
+      (sparc.ori as $r.tmp2 $imm.character $r.RESULT) ; [slot] convert to char
+      (sparc.label as Lfinish)
+      (sparc.set as $imm.false $r.RESULT)          ; failed
+      (sparc.label as Lend))))
+
+
+; eof
+; Copyright 1998 Lars T Hansen.
+; 
+; $Id: twobit.sch,v 1.3 1999/08/23 19:14:26 lth Exp $
+;
+; 9 May 1999 / wdc
+;
+; SPARC code generation macros for primitives, part 2:
+;   primitives introduced by peephole optimization.
+
+(define-primop 'internal:car
+  (lambda (as src1 dest)
+    (internal-primop-invariant2 'internal:car src1 dest)
+    (if (not (unsafe-code))
+       (emit-single-tagcheck-assert-reg! as
+                                         $tag.pair-tag src1 #f $ex.car))
+    (sparc.ldi as src1 (- $tag.pair-tag) dest)))
+
+(define-primop 'internal:cdr
+  (lambda (as src1 dest)
+    (internal-primop-invariant2 'internal:cdr src1 dest)
+    (if (not (unsafe-code))
+       (emit-single-tagcheck-assert-reg! as
+                                         $tag.pair-tag src1 #f $ex.cdr))
+    (sparc.ldi as src1 (- 4 $tag.pair-tag) dest)))
+
+(define-primop 'internal:cell-ref
+  (lambda (as src1 dest)
+    (internal-primop-invariant2 'internal:cell-ref src1 dest)
+    (sparc.ldi as src1 (- $tag.pair-tag) dest)))
+
+(define-primop 'internal:set-car!
+  (lambda (as rs1 rs2 dest-ignored)
+    (internal-primop-invariant2 'internal:set-car! rs1 dest-ignored)
+    (if (not (unsafe-code))
+       (emit-single-tagcheck-assert-reg! as $tag.pair-tag rs1 rs2 $ex.car))
+    (emit-setcar/setcdr! as rs1 rs2 0)))
+
+(define-primop 'internal:set-cdr!
+  (lambda (as rs1 rs2 dest-ignored)
+    (internal-primop-invariant2 'internal:set-cdr! rs1 dest-ignored)
+    (if (not (unsafe-code))
+       (emit-single-tagcheck-assert-reg! as $tag.pair-tag rs1 rs2 $ex.cdr))
+    (emit-setcar/setcdr! as rs1 rs2 4)))
+
+(define-primop 'internal:cell-set!
+  (lambda (as rs1 rs2 dest-ignored)
+    (internal-primop-invariant2 'internal:cell-set! rs1 dest-ignored)
+    (emit-setcar/setcdr! as rs1 rs2 0)))
+
+; CONS
+;
+; One instruction reduced here translates into about 2.5KB reduction in the
+; size of the basic heap image. :-)
+;
+; In the out-of-line case, if rd != RESULT then a garbage value is left 
+; in RESULT, but it always looks like a fixnum, so it's OK.
+
+(define-primop 'internal:cons
+  (lambda (as rs1 rs2 rd)
+    (if (inline-allocation)
+       (let ((ENOUGH-MEMORY (new-label))
+             (START (new-label)))
+         (sparc.label   as START)
+         (sparc.addi    as $r.e-top 8 $r.e-top)
+         (sparc.cmpr    as $r.e-top $r.e-limit)
+         (sparc.ble.a   as ENOUGH-MEMORY)
+         (sparc.sti     as rs1 -8 $r.e-top)
+         (millicode-call/ret as $m.gc START)
+         (sparc.label   as ENOUGH-MEMORY)
+         (sparc.sti     as (force-hwreg! as rs2 $r.tmp0) -4 $r.e-top)
+         (sparc.subi    as $r.e-top (- 8 $tag.pair-tag) rd))
+       (begin
+         (if (= rs1 $r.result)
+             (sparc.move as $r.result $r.argreg2))
+         (millicode-call/numarg-in-result as $m.alloc 8)
+         (if (= rs1 $r.result)
+             (sparc.sti as $r.argreg2 0 $r.result)
+             (sparc.sti as rs1 0 $r.result))
+         (sparc.sti as (force-hwreg! as rs2 $r.tmp1) 4 $r.result)
+         (sparc.addi as $r.result $tag.pair-tag rd)))))
+
+(define-primop 'internal:car:pair
+  (lambda (as src1 dest)
+    (internal-primop-invariant2 'internal:car src1 dest)
+    (sparc.ldi as src1 (- $tag.pair-tag) dest)))
+
+(define-primop 'internal:cdr:pair
+  (lambda (as src1 dest)
+    (internal-primop-invariant2 'internal:cdr src1 dest)
+    (sparc.ldi as src1 (- 4 $tag.pair-tag) dest)))
+
+; Vector operations.
+
+(define-primop 'internal:vector-length
+  (lambda (as rs rd)
+    (internal-primop-invariant2 'internal:vector-length rs rd)
+    (emit-get-length! as
+                     $tag.vector-tag
+                     (+ $imm.vector-header $tag.vector-typetag)
+                     $ex.vlen
+                     rs
+                     rd)))
+
+(define-primop 'internal:vector-ref
+  (lambda (as rs1 rs2 rd)
+    (internal-primop-invariant2 'internal:vector-ref rs1 rd)
+    (let ((fault (if (not (unsafe-code))
+                    (emit-double-tagcheck-assert-reg/reg!
+                     as
+                     $tag.vector-tag
+                     (+ $imm.vector-header $tag.vector-typetag)
+                     rs1 
+                     rs2
+                     $ex.vref))))
+      (emit-vector-like-ref! as rs1 rs2 rd fault $tag.vector-tag #t))))
+
+(define-primop 'internal:vector-ref/imm
+  (lambda (as rs1 imm rd)
+    (internal-primop-invariant2 'internal:vector-ref/imm rs1 rd)
+    (let ((fault (if (not (unsafe-code))
+                    (emit-double-tagcheck-assert-reg/imm!
+                     as
+                     $tag.vector-tag
+                     (+ $imm.vector-header $tag.vector-typetag)
+                     rs1 
+                     imm
+                     $ex.vref))))
+      (emit-vector-like-ref/imm! as rs1 imm rd fault $tag.vector-tag #t))))
+
+(define-primop 'internal:vector-set!
+  (lambda (as rs1 rs2 rs3)
+    (internal-primop-invariant1 'internal:vector-set! rs1)
+    (let ((fault (if (not (unsafe-code))
+                    (emit-double-tagcheck-assert-reg/reg!
+                     as
+                     $tag.vector-tag
+                     (+ $imm.vector-header $tag.vector-typetag)
+                     rs1
+                     rs2
+                     $ex.vset))))
+      (emit-vector-like-set! as rs1 rs2 rs3 fault $tag.vector-tag #t))))
+
+(define-primop 'internal:vector-length:vec
+  (lambda (as rs1 dst)
+    (internal-primop-invariant2 'internal:vector-length:vec rs1 dst)
+    (emit-get-length-trusted! as $tag.vector-tag rs1 dst)))
+
+(define-primop 'internal:vector-ref:trusted
+  (lambda (as rs1 rs2 dst)
+    (emit-vector-like-ref-trusted! as rs1 rs2 dst $tag.vector-tag)))
+
+(define-primop 'internal:vector-set!:trusted
+  (lambda (as rs1 rs2 rs3)
+    (emit-vector-like-ref-trusted! as rs1 rs2 rs3 $tag.vector-tag)))
+
+; Strings.
+
+(define-primop 'internal:string-length
+  (lambda (as rs rd)
+    (internal-primop-invariant2 'internal:string-length rs rd)
+    (emit-get-length! as
+                     $tag.bytevector-tag
+                     (+ $imm.bytevector-header $tag.string-typetag)
+                     $ex.slen
+                     rs
+                     rd)))
+
+(define-primop 'internal:string-ref
+  (lambda (as rs1 rs2 rd)
+    (internal-primop-invariant2 'internal:string-ref rs1 rd)
+    (let ((fault (if (not (unsafe-code))
+                    (emit-double-tagcheck-assert-reg/reg!
+                     as
+                     $tag.bytevector-tag
+                     (+ $imm.bytevector-header $tag.string-typetag)
+                     rs1 
+                     rs2
+                     $ex.sref))))
+      (emit-bytevector-like-ref! as rs1 rs2 rd fault #t #t))))
+
+(define-primop 'internal:string-ref/imm
+  (lambda (as rs1 imm rd)
+    (internal-primop-invariant2 'internal:string-ref/imm rs1 rd)
+    (let ((fault (if (not (unsafe-code))
+                    (emit-double-tagcheck-assert-reg/imm!
+                     as
+                     $tag.bytevector-tag
+                     (+ $imm.bytevector-header $tag.string-typetag)
+                     rs1 
+                     imm
+                     $ex.sref))))
+      (emit-bytevector-like-ref/imm! as rs1 imm rd fault #t #t))))
+
+(define-primop 'internal:string-set!
+  (lambda (as rs1 rs2 rs3)
+    (internal-primop-invariant1 'internal:string-set! rs1)
+      (emit-string-set! as rs1 rs2 rs3)))
+
+(define-primop 'internal:+
+  (lambda (as src1 src2 dest)
+    (internal-primop-invariant2 'internal:+ src1 dest)
+    (emit-arith-primop! as sparc.taddrcc sparc.subr $m.add src1 src2 dest #t)))
+
+(define-primop 'internal:+/imm
+  (lambda (as src1 imm dest)
+    (internal-primop-invariant2 'internal:+/imm src1 dest)
+    (emit-arith-primop! as sparc.taddicc sparc.subi $m.add src1 imm dest #f)))
+
+(define-primop 'internal:-
+  (lambda (as src1 src2 dest)
+    (internal-primop-invariant2 'internal:- src1 dest)
+    (emit-arith-primop! as sparc.tsubrcc sparc.addr $m.subtract 
+                       src1 src2 dest #t)))
+
+(define-primop 'internal:-/imm
+  (lambda (as src1 imm dest)
+    (internal-primop-invariant2 'internal:-/imm src1 dest)
+    (emit-arith-primop! as sparc.tsubicc sparc.addi $m.subtract
+                       src1 imm dest #f)))
+
+(define-primop 'internal:--
+  (lambda (as rs rd)
+    (internal-primop-invariant2 'internal:-- rs rd)
+    (emit-negate as rs rd)))
+
+(define-primop 'internal:branchf-null?
+  (lambda (as reg label)
+    (internal-primop-invariant1 'internal:branchf-null? reg)
+    (sparc.cmpi  as reg $imm.null)
+    (sparc.bne.a as label)
+    (sparc.slot  as)))
+
+(define-primop 'internal:branchf-pair?
+  (lambda (as reg label)
+    (internal-primop-invariant1 'internal:branchf-pair? reg)
+    (sparc.andi  as reg $tag.tagmask $r.tmp0)
+    (sparc.cmpi  as $r.tmp0 $tag.pair-tag)
+    (sparc.bne.a as label)
+    (sparc.slot  as)))
+
+(define-primop 'internal:branchf-zero?
+  (lambda (as reg label)
+    (internal-primop-invariant1 'internal:brancf-zero? reg)
+    (emit-bcmp-primop! as sparc.bne.a reg $r.g0 label $m.zerop #t)))
+
+(define-primop 'internal:branchf-eof-object?
+  (lambda (as rs label)
+    (internal-primop-invariant1 'internal:branchf-eof-object? rs)
+    (sparc.cmpi  as rs $imm.eof)
+    (sparc.bne.a as label)
+    (sparc.slot  as)))
+
+(define-primop 'internal:branchf-fixnum?
+  (lambda (as rs label)
+    (internal-primop-invariant1 'internal:branchf-fixnum? rs)
+    (sparc.btsti as rs 3)
+    (sparc.bne.a as label)
+    (sparc.slot  as)))
+
+(define-primop 'internal:branchf-char?
+  (lambda (as rs label)
+    (internal-primop-invariant1 'internal:branchf-char? rs)
+    (sparc.andi  as rs 255 $r.tmp0)
+    (sparc.cmpi  as $r.tmp0 $imm.character)
+    (sparc.bne.a as label)
+    (sparc.slot  as)))
+
+(define-primop 'internal:branchf-=
+  (lambda (as src1 src2 label)
+    (internal-primop-invariant1 'internal:branchf-= src1)
+    (emit-bcmp-primop! as sparc.bne.a src1 src2 label $m.numeq #t)))
+
+(define-primop 'internal:branchf-<
+  (lambda (as src1 src2 label)
+    (internal-primop-invariant1 'internal:branchf-< src1)
+    (emit-bcmp-primop! as sparc.bge.a src1 src2 label $m.numlt #t)))
+
+(define-primop 'internal:branchf-<=
+  (lambda (as src1 src2 label)
+    (internal-primop-invariant1 'internal:branchf-<= src1)
+    (emit-bcmp-primop! as sparc.bg.a src1 src2 label $m.numle #t)))
+
+(define-primop 'internal:branchf->
+  (lambda (as src1 src2 label)
+    (internal-primop-invariant1 'internal:branchf-> src1)
+    (emit-bcmp-primop! as sparc.ble.a src1 src2 label $m.numgt #t)))
+
+(define-primop 'internal:branchf->=
+  (lambda (as src1 src2 label)
+    (internal-primop-invariant1 'internal:branchf->= src1)
+    (emit-bcmp-primop! as sparc.bl.a src1 src2 label $m.numge #t)))
+
+(define-primop 'internal:branchf-=/imm
+  (lambda (as src1 imm label)
+    (internal-primop-invariant1 'internal:branchf-=/imm src1)
+    (emit-bcmp-primop! as sparc.bne.a src1 imm label $m.numeq #f)))
+
+(define-primop 'internal:branchf-</imm
+  (lambda (as src1 imm label)
+    (internal-primop-invariant1 'internal:branchf-</imm src1)
+    (emit-bcmp-primop! as sparc.bge.a src1 imm label $m.numlt #f)))
+
+(define-primop 'internal:branchf-<=/imm
+  (lambda (as src1 imm label)
+    (internal-primop-invariant1 'internal:branchf-<=/imm src1)
+    (emit-bcmp-primop! as sparc.bg.a src1 imm label $m.numle #f)))
+
+(define-primop 'internal:branchf->/imm
+  (lambda (as src1 imm label)
+    (internal-primop-invariant1 'internal:branchf->/imm src1)
+    (emit-bcmp-primop! as sparc.ble.a src1 imm label $m.numgt #f)))
+
+(define-primop 'internal:branchf->=/imm
+  (lambda (as src1 imm label)
+    (internal-primop-invariant1 'internal:branchf->=/imm src1)
+    (emit-bcmp-primop! as sparc.bl.a src1 imm label $m.numge #f)))
+
+(define-primop 'internal:branchf-char=?
+  (lambda (as src1 src2 label)
+    (internal-primop-invariant1 'internal:branchf-char=? src1)
+    (emit-char-bcmp-primop! as sparc.bne.a src1 src2 label $ex.char=?)))
+
+(define-primop 'internal:branchf-char<=?
+  (lambda (as src1 src2 label)
+    (internal-primop-invariant1 'internal:branchf-char<=? src1)
+    (emit-char-bcmp-primop! as sparc.bg.a src1 src2 label $ex.char<=?)))
+
+(define-primop 'internal:branchf-char<?
+  (lambda (as src1 src2 label)
+    (internal-primop-invariant1 'internal:branchf-char<? src1)
+    (emit-char-bcmp-primop! as sparc.bge.a src1 src2 label $ex.char<?)))
+
+(define-primop 'internal:branchf-char>=?
+  (lambda (as src1 src2 label)
+    (internal-primop-invariant1 'internal:branchf-char>=? src1)
+    (emit-char-bcmp-primop! as sparc.bl.a src1 src2 label $ex.char>=?)))
+
+(define-primop 'internal:branchf-char>?
+  (lambda (as src1 src2 label)
+    (internal-primop-invariant1 'internal:branchf-char>=? src1)
+    (emit-char-bcmp-primop! as sparc.ble.a src1 src2 label $ex.char>?)))
+
+(define-primop 'internal:branchf-char=?/imm
+  (lambda (as src imm label)
+    (internal-primop-invariant1 'internal:branchf-char=?/imm src)
+    (emit-char-bcmp-primop! as sparc.bne.a src imm label $ex.char=?)))
+
+(define-primop 'internal:branchf-char>=?/imm
+  (lambda (as src imm label)
+    (internal-primop-invariant1 'internal:branchf-char>=?/imm src)
+    (emit-char-bcmp-primop! as sparc.bl.a src imm label $ex.char>=?)))
+
+(define-primop 'internal:branchf-char>?/imm
+  (lambda (as src imm label)
+    (internal-primop-invariant1 'internal:branchf-char>?/imm src)
+    (emit-char-bcmp-primop! as sparc.ble.a src imm label $ex.char>?)))
+
+(define-primop 'internal:branchf-char<=?/imm
+  (lambda (as src imm label)
+    (internal-primop-invariant1 'internal:branchf-char<=?/imm src)
+    (emit-char-bcmp-primop! as sparc.bg.a src imm label $ex.char<=?)))
+
+(define-primop 'internal:branchf-char<?/imm
+  (lambda (as src imm label)
+    (internal-primop-invariant1 'internal:branchf-char<?/imm src)
+    (emit-char-bcmp-primop! as sparc.bge.a src imm label $ex.char<?)))
+
+(define-primop 'internal:eq?
+  (lambda (as src1 src2 dest)
+    (internal-primop-invariant2 'internal:eq? src1 dest)
+    (let ((tmp (force-hwreg! as src2 $r.tmp0)))
+      (sparc.cmpr as src1 tmp)
+      (emit-set-boolean-reg! as dest))))
+
+(define-primop 'internal:eq?/imm
+  (lambda (as rs imm rd)
+    (internal-primop-invariant2 'internal:eq?/imm rs rd)
+    (cond ((fixnum? imm) (sparc.cmpi as rs (thefixnum imm)))
+         ((eq? imm #t)  (sparc.cmpi as rs $imm.true))
+         ((eq? imm #f)  (sparc.cmpi as rs $imm.false))
+         ((null? imm)   (sparc.cmpi as rs $imm.null))
+         (else ???))
+    (emit-set-boolean-reg! as rd)))
+
+(define-primop 'internal:branchf-eq?
+  (lambda (as src1 src2 label)
+    (internal-primop-invariant1 'internal:branchf-eq? src1)
+    (let ((src2 (force-hwreg! as src2 $r.tmp0)))
+      (sparc.cmpr  as src1 src2)
+      (sparc.bne.a as label)
+      (sparc.slot  as))))
+
+(define-primop 'internal:branchf-eq?/imm
+  (lambda (as rs imm label)
+    (internal-primop-invariant1 'internal:branchf-eq?/imm rs)
+    (cond ((fixnum? imm) (sparc.cmpi as rs (thefixnum imm)))
+         ((eq? imm #t)  (sparc.cmpi as rs $imm.true))
+         ((eq? imm #f)  (sparc.cmpi as rs $imm.false))
+         ((null? imm)   (sparc.cmpi as rs $imm.null))
+         (else ???))
+    (sparc.bne.a as label)
+    (sparc.slot  as)))
+
+; Unary predicates followed by a check.
+
+(define-primop 'internal:check-fixnum?
+  (lambda (as src L1 liveregs)
+    (sparc.btsti   as src 3)
+    (emit-checkcc! as sparc.bne L1 liveregs)))
+
+(define-primop 'internal:check-pair?
+  (lambda (as src L1 liveregs)
+    (sparc.andi    as src $tag.tagmask $r.tmp0)
+    (sparc.cmpi    as $r.tmp0 $tag.pair-tag)
+    (emit-checkcc! as sparc.bne L1 liveregs)))
+
+(define-primop 'internal:check-vector?
+  (lambda (as src L1 liveregs)
+    (sparc.andi    as src $tag.tagmask $r.tmp0)
+    (sparc.cmpi    as $r.tmp0 $tag.vector-tag)
+    (sparc.bne     as L1)
+    (sparc.nop     as)
+    (sparc.ldi     as src (- $tag.vector-tag) $r.tmp0)
+    (sparc.andi    as $r.tmp0 255 $r.tmp1)
+    (sparc.cmpi    as $r.tmp1 $imm.vector-header)
+    (emit-checkcc! as sparc.bne L1 liveregs)))
+
+(define-primop 'internal:check-vector?/vector-length:vec
+  (lambda (as src dst L1 liveregs)
+    (sparc.andi    as src     $tag.tagmask        $r.tmp0)
+    (sparc.cmpi    as $r.tmp0 $tag.vector-tag)
+    (sparc.bne     as L1)
+    (sparc.nop     as)
+    (sparc.ldi     as src     (- $tag.vector-tag) $r.tmp0)
+    (sparc.andi    as $r.tmp0 255                 $r.tmp1)
+    (sparc.cmpi    as $r.tmp1 $imm.vector-header)
+    (sparc.bne     as L1)
+    (apply sparc.slot2 as liveregs)
+    (sparc.srli    as $r.tmp0 8 dst)))
+
+(define (internal-primop-invariant2 name a b)
+    (if (not (and (hardware-mapped? a) (hardware-mapped? b)))
+       (asm-error "SPARC assembler internal invariant violated by " name
+                  " on operands " a " and " b)))
+
+(define (internal-primop-invariant1 name a)
+    (if (not (hardware-mapped? a))
+       (asm-error "SPARC assembler internal invariant violated by " name
+                  " on operand " a)))
+
+; eof
+; Copyright 1998 Lars T Hansen.
+;
+; $Id: twobit.sch,v 1.3 1999/08/23 19:14:26 lth Exp $
+;
+; SPARC code generation macros for primitives, part 3a:
+;   helper procedures for scalars.
+
+
+; LOGAND, LOGIOR, LOGXOR: logical operations on fixnums.
+;
+; Input:  Registers rs1 and rs2, both of which can be general registers.
+;         In addition, rs1 can be RESULT, and rs2 can be ARGREG2.
+; Output: Register dest, which can be a general register or RESULT.
+
+(define (logical-op as rs1 rs2 dest op excode)
+
+  (define (fail rs1 rs2 L0)
+    (if (not (= rs1 $r.result))  (sparc.move as rs1 $r.result))
+    (if (not (= rs2 $r.argreg2)) (sparc.move as rs2 $r.argreg2))
+    (sparc.set as (thefixnum excode) $r.tmp0)
+    (millicode-call/ret as $m.exception L0))
+
+  (let ((L0  (new-label))
+        (L1  (new-label)))
+    (sparc.label     as L0)
+    (let ((rs1 (force-hwreg! as rs1 $r.result))
+          (rs2 (force-hwreg! as rs2 $r.argreg2))
+          (u   (unsafe-code))
+          (d   (hardware-mapped? dest)))
+      (cond ((and u d)
+             (op as rs1 rs2 dest))
+            ((and u (not d))
+             (op as rs1 rs2 $r.tmp0)
+             (emit-store-reg! as $r.tmp0 dest))
+            ((and (not u) d)
+             (sparc.orr     as rs1 rs2 $r.tmp0)
+             (sparc.btsti   as $r.tmp0 3)
+             (sparc.bz.a    as L1)
+             (op            as rs1 rs2 dest)
+             (fail rs1 rs2 L0)
+             (sparc.label   as L1))
+            (else
+             (sparc.orr     as rs1 rs2 $r.tmp0)
+             (sparc.btsti   as $r.tmp0 3)
+             (sparc.bz.a    as L1)
+             (op            as rs1 rs2 $r.tmp0)
+             (fail rs1 rs2 L0)
+             (sparc.label   as L1)
+             (emit-store-reg! as $r.tmp0 dest))))))
+
+
+; LSH, RSHA, RSHL: Bitwise shifts on fixnums.
+;
+; Notes for future contemplation:
+;   - The semantics do not match those of MIT Scheme or MacScheme: only 
+;     positive shifts are allowed.
+;   - The names do not match the fixnum-specific procedures of Chez Scheme
+;     that have the same semantics: fxsll, fxsra, fxsrl.
+;   - This code checks that the second argument is in range; if it did
+;     not, then we could get a MOD for free.  Probably too hardware-dependent
+;     to worry about.
+;   - The range 0..31 for the shift count is curious given that the fixnum
+;     is 30-bit.
+
+(define (emit-shift-operation as exn rs1 rs2 rd)
+  (let ((rs2 (force-hwreg! as rs2 $r.argreg2)))
+    (if (not (unsafe-code))
+        (let ((L0 (new-label))
+              (FAULT (new-label))
+              (START (new-label)))
+          (sparc.label as START)
+          (sparc.btsti as rs1 3)          ; RS1 fixnum?
+          (sparc.be.a  as L0)
+          (sparc.andi  as rs2 #x7c $r.g0) ; RS2 fixnum and 0 <= RS2 < 32?
+          (sparc.label as FAULT)
+          (if (not (= rs1 $r.result))
+              (sparc.move as rs1 $r.result))
+          (if (not (= rs2 $r.argreg2))
+              (emit-move2hwreg! as rs2 $r.argreg2))
+          (sparc.set   as (thefixnum exn) $r.tmp0)
+          (millicode-call/ret as $m.exception START)
+          (sparc.label as L0)
+          (sparc.bne   as FAULT)
+          (sparc.srai  as rs2 2 $r.tmp1))
+        (begin
+          (sparc.srai  as rs2 2 $r.tmp1)))
+    (cond ((= exn $ex.lsh)
+           (sparc.sllr as rs1 $r.tmp1 rd))
+          ((= exn $ex.rshl)
+           (sparc.srlr  as rs1 $r.tmp1 rd)
+           (sparc.andni as rd 3 rd))
+          ((= exn $ex.rsha)
+           (sparc.srar  as rs1 $r.tmp1 rd)
+           (sparc.andni as rd 3 rd))
+          (else ???))))
+
+
+; Set result on condition code.
+;
+; The processor's zero bit has been affected by a previous instruction.
+; If the bit is set, store #t in RESULT, otherwise store #f in RESULT.
+
+(define (emit-set-boolean! as)
+  (emit-set-boolean-reg! as $r.result))
+
+
+; Set on condition code.
+;
+; The processor's zero bit has been affected by a previous instruction.
+; If the bit is set, store #t in the processor register 'dest', otherwise
+; store #f in 'dest'.
+
+(define (emit-set-boolean-reg! as dest)
+  (let ((L1 (new-label)))
+    (sparc.set   as $imm.true dest)
+    (sparc.bne.a as L1)
+    (sparc.set   as $imm.false dest)
+    (sparc.label as L1)))
+
+
+; Representation predicate.
+
+(define (emit-single-tagcheck->bool! as tag)
+  (sparc.andi as $r.result $tag.tagmask $r.tmp0)
+  (sparc.cmpi as $r.tmp0 tag)
+  (emit-set-boolean! as))
+
+(define (emit-single-tagcheck-assert! as tag1 excode reg2)
+  (emit-single-tagcheck-assert-reg! as tag1 $r.result reg2 excode))
+
+(define (emit-single-tagcheck-assert-reg! as tag1 reg reg2 excode)
+  (let ((L0    (new-label))
+        (L1    (new-label))
+        (FAULT (new-label)))
+    (sparc.label as L0)
+    (sparc.andi  as reg $tag.tagmask $r.tmp0)
+    (sparc.cmpi  as $r.tmp0 tag1)
+    (fault-if-ne as excode #f #f reg reg2 L0)))
+
+; Assert that a machine register has a fixnum in it.
+; Returns the label of the fault code.
+
+(define (emit-assert-fixnum! as reg excode)
+  (let ((L0    (new-label))
+        (L1    (new-label))
+        (FAULT (new-label)))
+    (sparc.label  as L0)
+    (sparc.btsti  as reg 3)
+    (fault-if-ne as excode #f #f reg #f L0)))
+
+; Assert that RESULT has a character in it.
+; Returns the label of the fault code.
+
+(define (emit-assert-char! as excode fault-label)
+  (let ((L0    (new-label))
+        (L1    (new-label))
+        (FAULT (new-label)))
+    (sparc.label as L0)
+    (sparc.andi  as $r.result #xFF $r.tmp0)
+    (sparc.cmpi  as $r.tmp0 $imm.character)
+    (fault-if-ne as excode #f fault-label #f #f L0)))
+
+; Generate code for fault handling if the zero flag is not set.
+; - excode is the nativeint exception code.
+; - cont-label, if not #f, is the label to go to if there is no fault.
+; - fault-label, if not #f, is the label of an existing fault handler.
+; - reg1, if not #f, is the number of a register which must be
+;   moved into RESULT before the fault handler is called.
+; - reg2, if not #f, is the number of a register which must be moved
+;   into ARGREG2 before the fault handler is called.
+; - ret-label, if not #f, is the return address to be set up before calling
+;   the fault handler.
+;
+; Ret-label and fault-label cannot simultaneously be non-#f; in this case
+; the ret-label is ignored (since the existing fault handler most likely
+; sets up the return in the desired manner).
+
+(define (fault-if-ne as excode cont-label fault-label reg1 reg2 ret-label)
+  (if fault-label
+      (begin 
+        (if (and reg2 (not (= reg2 $r.argreg2)))
+            (emit-move2hwreg! as reg2 $r.argreg2))
+        (sparc.bne as fault-label)
+        (if (and reg1 (not (= reg1 $r.result)))
+            (sparc.move as reg1 $r.result)
+            (sparc.nop as))
+        fault-label)
+      (let ((FAULT (new-label))
+            (L1    (new-label)))
+        (sparc.be.a  as (or cont-label L1))
+        (sparc.slot  as)
+        (sparc.label as FAULT)
+        (if (and reg1 (not (= reg1 $r.result)))
+            (sparc.move as reg1 $r.result))
+        (if (and reg2 (not (= reg2 $r.argreg2)))
+            (emit-move2hwreg! as reg2 $r.argreg2))
+        (sparc.set   as (thefixnum excode) $r.tmp0)
+        (millicode-call/ret as $m.exception (or ret-label L1))
+        (if (or (not cont-label) (not ret-label))
+            (sparc.label as L1))
+        FAULT)))
+
+; This is more expensive than what is good for it (5 cycles in the usual case),
+; but there does not seem to be a better way.
+
+(define (emit-assert-positive-fixnum! as reg excode)
+  (let ((L1 (new-label))
+        (L2 (new-label))
+        (L3 (new-label))) 
+    (sparc.label   as L2)
+    (sparc.tsubrcc as reg $r.g0 $r.g0)
+    (sparc.bvc     as L1)
+    (sparc.nop     as)
+    (sparc.label   as L3)
+    (if (not (= reg $r.result))
+        (sparc.move as reg $r.result))
+    (sparc.set     as (thefixnum excode) $r.tmp0)
+    (millicode-call/ret as $m.exception l2)
+    (sparc.label   as L1)
+    (sparc.bl      as L3)
+    (sparc.nop     as)
+    L3))
+
+
+; Arithmetic comparison with boolean result.
+
+(define (emit-cmp-primop! as branch_t.a generic r)
+  (let ((Ltagok (new-label))
+        (Lcont  (new-label))
+        (r      (force-hwreg! as r $r.argreg2)))
+    (sparc.tsubrcc as $r.result r $r.g0)
+    (sparc.bvc.a   as Ltagok)
+    (sparc.set     as $imm.false $r.result)
+    (if (not (= r $r.argreg2))
+        (sparc.move    as r $r.argreg2))
+    (millicode-call/ret as generic Lcont)
+    (sparc.label   as Ltagok)
+    (branch_t.a    as Lcont)
+    (sparc.set     as $imm.true $r.result)
+    (sparc.label   as Lcont)))
+
+
+; Arithmetic comparison and branch.
+;
+; This code does not use the chained branch trick (DCTI) that was documented
+; in the Sparc v8 manual and deprecated in the v9 manual.  This code executes
+; _much_ faster on the Ultra than the code using DCTI, even though it executes
+; the same instructions.
+;
+; Parameters and preconditions.
+;   Src1 is a general register, RESULT, ARGREG2, or ARGREG3.
+;   Src2 is a general register, RESULT, ARGREG2, ARGREG3, or an immediate.
+;   Src2 is an immediate iff src2isreg = #f.
+;   Branch_f.a is a branch on condition code that branches if the condition
+;     is not true.
+;   Generic is the millicode table offset of the generic procedure.
+
+(define (emit-bcmp-primop! as branch_f.a src1 src2 Lfalse generic src2isreg)
+  (let ((Ltagok (new-label))
+        (Ltrue  (new-label))
+        (op2    (if src2isreg
+                    (force-hwreg! as src2 $r.tmp1)
+                    (thefixnum src2)))
+        (sub   (if src2isreg sparc.tsubrcc sparc.tsubicc))
+        (mov   (if src2isreg sparc.move sparc.set)))
+    (sub         as src1 op2 $r.g0)
+    (sparc.bvc.a as Ltagok)
+    (sparc.slot  as)
+
+    ; Not both fixnums.
+    ; Must move src1 to result if src1 is not result.
+    ; Must move src2 to argreg2 if src2 is not argreg2.
+
+    (let ((move-res  (not (= src1 $r.result)))
+          (move-arg2 (or (not src2isreg) (not (= op2 $r.argreg2)))))
+      (if (and move-arg2 move-res)
+          (mov     as op2 $r.argreg2))
+      (sparc.jmpli as $r.millicode generic $r.o7)
+      (cond (move-res   (sparc.move as src1 $r.result))
+            (move-arg2  (mov        as op2 $r.argreg2))
+            (else       (sparc.nop  as)))
+      (sparc.cmpi  as $r.result $imm.false)
+      (sparc.bne.a as Ltrue)
+      (sparc.slot  as)
+      (sparc.b     as Lfalse)
+      (sparc.slot  as))
+
+    (sparc.label as Ltagok)
+    (branch_f.a   as Lfalse)
+    (sparc.slot  as)
+    (sparc.label as Ltrue)))
+
+
+; Generic arithmetic for + and -.
+; Some rules:
+;   We have two HW registers src1 and dest.
+;   If src2isreg is #t then src2 may be a HW reg or a SW reg
+;   If src2isreg is #f then src2 is an immediate fixnum, not shifted.
+;   Src1 and dest may be RESULT, but src2 may not.
+;   Src2 may be ARGREG2, the others may not.
+;
+; FIXME! This is incomprehensible.
+
+; New code below.
+
+'(define (emit-arith-primop! as op invop generic src1 src2 dest src2isreg)
+  (let ((L1  (new-label))
+        (op2 (if src2isreg
+                 (force-hwreg! as src2 $r.tmp1)
+                 (thefixnum src2))))
+    (if (and src2isreg (= op2 dest))
+        (begin (op          as src1 op2 $r.tmp0)
+               (sparc.bvc.a as L1)
+               (sparc.move  as $r.tmp0 dest))
+        (begin (op          as src1 op2 dest)
+               (sparc.bvc.a as L1)
+               (sparc.slot  as)
+               (invop       as dest op2 dest)))
+    (let ((n    (+ (if (not (= src1 $r.result)) 1 0)
+                   (if (or (not src2isreg) (not (= op2 $r.argreg2))) 1 0)))
+          (mov2 (if src2isreg sparc.move sparc.set)))
+      (if (= n 2)
+          (mov2 as op2 $r.argreg2))
+      (sparc.jmpli as $r.millicode generic $r.o7)
+      (cond ((= n 0) (sparc.nop  as))
+            ((= n 1) (mov2       as op2 $r.argreg2))
+            (else    (sparc.move as src1 $r.result)))
+      ; Generic arithmetic leaves stuff in RESULT, must move to dest if
+      ; dest is not RESULT.
+      (if (not (= dest $r.result))
+          (sparc.move as $r.result dest))
+      (sparc.label as L1))))
+
+; Comprehensible, but longer.
+;
+; Important to be careful not to clobber arguments, and not to leave garbage
+; in rd, if millicode is called.
+;
+; op is the appropriate operation.
+; invop is the appropriate inverse operation.
+; RS1 can be any general hw register or RESULT.
+; RS2/IMM can be any general register or ARGREG2 (op2isreg=#t), or 
+;         an immediate (op2isreg=#f)
+; RD can be any general hw register or RESULT.
+;
+; FIXME: split this into two procedures.
+
+(define (emit-arith-primop! as op invop generic rs1 rs2/imm rd op2isreg)
+  (let ((L1 (new-label)))
+    (if op2isreg
+        (let ((rs2 (force-hwreg! as rs2/imm $r.argreg2)))
+          (cond ((or (= rs1 rs2 rd)
+                     (and (= rs2 rd)
+                          (= generic $m.subtract)))
+                 (op          as rs1 rs2 $r.tmp0)
+                 (sparc.bvc.a as L1)
+                 (sparc.move  as $r.tmp0 rd))
+                ((= rs1 rd)
+                 (op          as rs1 rs2 rs1)
+                 (sparc.bvc.a as L1)
+                 (sparc.slot  as)
+                 (invop       as rs1 rs2 rs1))
+                ((= rs2 rd)
+                 (op          as rs1 rs2 rs2)
+                 (sparc.bvc.a as L1)
+                 (sparc.slot  as)
+                 (invop       as rs2 rs1 rs2))
+                (else
+                 (op          as rs1 rs2 rd)
+                 (sparc.bvc.a as L1)
+                 (sparc.slot  as)
+                 (if (and (not (= rd $r.result)) (not (= rd $r.argreg2)))
+                     (sparc.clr as rd))))
+          (cond ((and (= rs1 $r.result) (= rs2 $r.argreg2))
+                 ;; Could peephole the INVOP or CLR into the slot here.
+                 (millicode-call/0arg as generic))
+                ((= rs1 $r.result)
+                 (millicode-call/1arg as generic rs2))
+                ((= rs2 $r.argreg2)
+                 (millicode-call/1arg-in-result as generic rs1))
+                (else
+                 (sparc.move as rs2 $r.argreg2)
+                 (millicode-call/1arg-in-result as generic rs1))))
+        (let ((imm (thefixnum rs2/imm)))
+          (op          as rs1 imm rd)
+          (sparc.bvc.a as L1)
+          (sparc.slot  as)
+          (invop       as rd imm rd)
+          (if (not (= rs1 $r.result))
+              (sparc.move as rs1 $r.result))
+          (millicode-call/numarg-in-reg as generic imm $r.argreg2)))
+    (if (not (= rd $r.result))
+        (sparc.move as $r.result rd))
+    (sparc.label as L1)))
+
+
+; Important to be careful not to leave garbage in rd if millicode is called.
+
+(define (emit-negate as rs rd)
+  (let ((L1 (new-label)))
+    (cond ((= rs rd)
+           (sparc.tsubrcc as $r.g0 rs rs)
+           (sparc.bvc.a   as L1)
+           (sparc.slot    as)
+           (if (= rs $r.result)
+               (begin 
+                 (sparc.jmpli as $r.millicode $m.negate $r.o7)
+                 (sparc.subr  as $r.g0 $r.result $r.result))
+               (begin
+                 (sparc.subr  as $r.g0 rs rs)
+                 (sparc.jmpli as $r.millicode $m.negate $r.o7)
+                 (sparc.move  as rs $r.result))))
+          (else
+           (sparc.tsubrcc as $r.g0 rs rd)
+           (sparc.bvc.a   as L1)
+           (sparc.slot    as)
+           (cond ((= rs $r.result)
+                  (sparc.jmpli as $r.millicode $m.negate $r.o7)
+                  (sparc.clr   as rd))
+                 ((= rd $r.result)
+                  (sparc.jmpli as $r.millicode $m.negate $r.o7)
+                  (sparc.move  as rs $r.result))
+                 (else
+                  (sparc.clr   as rd)
+                  (sparc.jmpli as $r.millicode $m.negate $r.o7)
+                  (sparc.move  as rs $r.result)))))
+    (if (not (= rd $r.result))
+        (sparc.move as $r.result rd))
+    (sparc.label   as L1)))
+
+; Character comparison.
+
+; r is a register or a character constant.
+
+(define (emit-char-cmp as r btrue.a excode)
+  (emit-charcmp! as (lambda ()
+                      (let ((l2 (new-label)))
+                        (sparc.set   as $imm.false $r.result)
+                        (btrue.a     as L2)
+                        (sparc.set   as $imm.true $r.result)
+                        (sparc.label as L2)))
+                 $r.result
+                 r
+                 excode))
+; op1 is a hw register
+; op2 is a register or a character constant
+
+(define (emit-char-bcmp-primop! as bfalse.a op1 op2 L0 excode)
+  (emit-charcmp! as (lambda ()
+                      (bfalse.a   as L0)
+                      (sparc.slot as))
+                 op1
+                 op2
+                 excode))
+
+; We check the tags of both by xoring them and seeing if the low byte is 0.
+; If so, then we can subtract one from the other (tag and all) and check the
+; condition codes.  
+;
+; The branch-on-true instruction must have the annull bit set. (???)
+;
+; op1 is a hw register
+; op2 is a register or a character constant.
+
+(define (emit-charcmp! as tail op1 op2 excode)
+  (let ((op2 (if (char? op2)
+                 op2
+                 (force-hwreg! as op2 $r.argreg2))))
+    (cond ((not (unsafe-code))
+           (let ((L0 (new-label))
+                 (L1 (new-label))
+                 (FAULT (new-label)))
+             (sparc.label as L0)
+             (cond ((char? op2)
+                    (sparc.xori  as op1 $imm.character $r.tmp0)
+                    (sparc.btsti as $r.tmp0 #xFF)
+                    (sparc.srli  as op1 16 $r.tmp0)
+                    (sparc.be.a  as L1)
+                    (sparc.cmpi  as $r.tmp0 (char->integer op2)))
+                   (else
+                    (sparc.andi  as op1 #xFF $r.tmp0)
+                    (sparc.andi  as op2 #xFF $r.tmp1)
+                    (sparc.cmpr  as $r.tmp0 $r.tmp1)
+                    (sparc.bne   as FAULT)
+                    (sparc.cmpi  as $r.tmp0 $imm.character)
+                    (sparc.be.a  as L1)
+                    (sparc.cmpr  as op1 op2)))
+             (sparc.label as FAULT)
+             (if (not (eqv? op1 $r.result))
+                 (sparc.move as op1 $r.result))
+             (cond ((char? op2) 
+                    (emit-immediate->register! as
+                                               (char->immediate op2)
+                                               $r.argreg2))
+                   ((not (eqv? op2 $r.argreg2))
+                    (sparc.move as op2 $r.argreg2)))
+             (sparc.set   as (thefixnum excode) $r.tmp0)
+             (millicode-call/ret as $m.exception L0)
+             (sparc.label as L1)))
+          ((not (char? op2))
+           (sparc.cmpr as op1 op2))
+          (else
+           (sparc.srli as op1 16 $r.tmp0)
+           (sparc.cmpi as $r.tmp0 (char->integer op2))))
+    (tail)))
+
+; eof
+; Copyright 1998 Lars T Hansen.
+;
+; $Id: twobit.sch,v 1.3 1999/08/23 19:14:26 lth Exp $
+;
+; SPARC code generation macros for primitives, part 3b:
+;   helper procedures for data structures.
+
+
+; SET-CAR!, SET-CDR!, CELL-SET!
+;
+; Input:  RS1: a hardware register; has pair pointer (tag check must be
+;         performed by the caller).
+;         RS2: any register; has value to store.
+; Output: None.
+;
+; Having rs1 != RESULT is pretty silly with the current write barrier
+; but will be less silly with the new barrier.
+
+(define (emit-setcar/setcdr! as rs1 rs2 offs)
+  (cond ((and (write-barrier) (hardware-mapped? rs2))
+        (sparc.sti as rs2 (- offs $tag.pair-tag) rs1)
+         (if (not (= rs1 $r.result))
+             (sparc.move as rs1 $r.result))
+         (millicode-call/1arg as $m.addtrans rs2))
+        ((write-barrier)
+         (emit-move2hwreg! as rs2 $r.argreg2)
+         (sparc.sti as $r.argreg2 (- offs $tag.pair-tag) rs1)
+         (millicode-call/1arg-in-result as $m.addtrans rs1))
+        ((hardware-mapped? rs2)
+         (sparc.sti as rs2 (- offs $tag.pair-tag) rs1))
+        (else
+         (emit-move2hwreg! as rs2 $r.argreg2)
+         (sparc.sti as $r.argreg2 (- offs $tag.pair-tag) rs1))))
+
+
+
+
+; Representation predicate.
+;
+; RESULT has an object.  If the tag of RESULT is 'tag1' and the 
+; header byte of the object is 'tag2' then set RESULT to #t, else
+; set it to #f.
+
+(define (emit-double-tagcheck->bool! as tag1 tag2)
+  (let ((L1 (new-label)))
+    (sparc.andi  as $r.result $tag.tagmask $r.tmp0)
+    (sparc.cmpi  as $r.tmp0 tag1)
+    (sparc.bne.a as L1)
+    (sparc.set   as $imm.false $r.result)
+    (sparc.ldbi  as $r.result (+ (- tag1) 3) $r.tmp0)
+    (sparc.set   as $imm.true $r.result)
+    (sparc.cmpi  as $r.tmp0 tag2)
+    (sparc.bne.a as L1)
+    (sparc.set   as $imm.false $r.result)
+    (sparc.label as L1)))
+
+
+; Check structure tag.
+;
+; RS1 has an object.  If the tag of RS1 is not 'tag1', or if the tag is 
+; 'tag1' but the header byte of the object header is not 'tag2', then an
+; exception with code 'excode' is signalled.  The exception call is set
+; up to return to the first instruction of the emitted code.
+;
+; If RS1 is not RESULT then it is moved to RESULT before the exception 
+; is signalled.
+;
+; If RS2/IMM is not #f, then it is a register or immediate that is moved
+; to ARGREG2 before the exception is signalled; it is an immediate iff 
+; imm? = #t.  
+;
+; RS1 must be a hardware register.
+; RS2/IMM is a general register, ARGREG2, an immediate, or #f.
+; RS3 is a general register, ARGREG3, or #f.
+;
+; The procedure returns the label of the fault address.  If the execution
+; falls off the end of the emitted instruction sequence, then the following
+; are true:
+;  - the tag of the object in RS1 was 'tag1' and its header byte was 'tag2'
+;  - the object header word is in TMP0.
+
+(define (double-tagcheck-assert as tag1 tag2 rs1 rs2/imm rs3 excode imm?)
+  (let ((L0    (new-label))
+        (L1    (new-label))
+        (FAULT (new-label)))
+    (sparc.label as L0)
+    (sparc.andi  as rs1 $tag.tagmask $r.tmp0)
+    (sparc.cmpi  as $r.tmp0 tag1)
+    (sparc.be.a  as L1)
+    (sparc.ldi   as rs1 (- tag1) $r.tmp0)
+    (sparc.label as FAULT)
+    (if (not (= rs1 $r.result))
+        (sparc.move as rs1 $r.result))
+    (if rs2/imm 
+        (cond (imm?
+               (sparc.set as (thefixnum rs2/imm) $r.argreg2))
+              ((= rs2/imm $r.argreg2))
+              (else
+               (emit-move2hwreg! as rs2/imm $r.argreg2))))
+    (if (and rs3 (not (= rs3 $r.argreg3)))
+        (emit-move2hwreg! as rs3 $r.argreg3))
+    (sparc.set   as (thefixnum excode) $r.tmp0)
+    (millicode-call/ret as $m.exception L0)
+    (sparc.label as L1)
+    (sparc.andi  as $r.tmp0 255 $r.tmp1)
+    (sparc.cmpi  as $r.tmp1 tag2)
+    (sparc.bne.a as FAULT)
+    (sparc.slot  as)
+    FAULT))
+
+(define (emit-double-tagcheck-assert! as tag1 tag2 excode reg2)
+  (double-tagcheck-assert as tag1 tag2 $r.result reg2 #f excode #f))
+
+(define (emit-double-tagcheck-assert-reg/reg! as tag1 tag2 rs1 rs2 excode)
+  (double-tagcheck-assert as tag1 tag2 rs1 rs2 #f excode #f))
+  
+(define (emit-double-tagcheck-assert-reg/imm! as tag1 tag2 rs1 imm excode)
+  (double-tagcheck-assert as tag1 tag2 rs1 imm #f excode #t))
+  
+
+
+
+; Get the length of a vector or bytevector structure, with tag checking
+; included.
+;
+; Input: RS and RD are both hardware registers.
+
+(define (emit-get-length! as tag1 tag2 excode rs rd)
+  (if (not (unsafe-code))
+      (if tag2
+          (emit-double-tagcheck-assert-reg/reg! as tag1 tag2 rs rd excode)
+          (emit-single-tagcheck-assert-reg! as tag1 rs rd excode)))
+  (emit-get-length-trusted! as tag1 rs rd))
+
+; Get the length of a vector or bytevector structure, without tag checking.
+;
+; Input: RS and RD are both hardware registers.
+
+(define (emit-get-length-trusted! as tag1 rs rd)
+  (sparc.ldi  as rs (- tag1) $r.tmp0)
+  (sparc.srli as $r.tmp0 8 rd)
+  (if (= tag1 $tag.bytevector-tag)
+      (sparc.slli as rd 2 rd)))
+
+
+; Allocate a bytevector, leave untagged pointer in RESULT.
+
+(define (emit-allocate-bytevector as hdr preserved-result)
+
+  ; Preserve the length field, then calculate the number of words
+  ; to allocate.  The value `28' is an adjustment of 3 (for rounding 
+  ; up) plus another 4 bytes for the header, all represented as a fixnum.
+
+  (if (not preserved-result)
+      (sparc.move as $r.result $r.argreg2))
+  (sparc.addi as $r.result 28 $r.result)
+  (sparc.andi as $r.result (asm:signed #xFFFFFFF0) $r.result)
+
+  ; Allocate space
+
+  (sparc.jmpli as $r.millicode $m.alloc-bv $r.o7)
+  (sparc.srai  as $r.result 2 $r.result)
+  
+  ; Setup the header.
+
+  (if (not preserved-result)
+      (sparc.slli as $r.argreg2 6 $r.tmp0)
+      (sparc.slli as preserved-result 6 $r.tmp0))
+  (sparc.addi as $r.tmp0 hdr $r.tmp0)
+  (sparc.sti  as $r.tmp0 0 $r.result))
+
+
+; Given a nativeint count, a pointer to the first element of a 
+; bytevector-like structure, and a byte value, fill the bytevector
+; with the byte value.
+
+(define (emit-bytevector-fill as r-bytecount r-pointer r-value)
+  (let ((L2 (new-label))
+        (L1 (new-label)))
+    (sparc.label  as L2)
+    (sparc.deccc  as r-bytecount)
+    (sparc.bge.a  as L2)
+    (sparc.stbr   as r-value r-bytecount r-pointer)
+    (sparc.label  as L1)))
+
+
+; BYTEVECTOR-REF, BYTEVECTOR-LIKE-REF, STRING-REF.
+;
+; The pointer in RS1 is known to be bytevector-like.  RS2 is the fixnum
+; index into the structure.  Get the RS2'th element and place it in RD.
+;
+; RS1 and RD are hardware registers.
+; RS2 is a general register or ARGREG2.
+; 'fault' is defined iff (unsafe-code) = #f
+; header is in TMP0 iff (unsafe-code) = #f and 'header-loaded?' = #t
+; if 'charize?' is #t then store result as char, otherwise as fixnum.
+
+(define (emit-bytevector-like-ref! as rs1 rs2 rd fault charize? header-loaded?)
+  (let ((rs2 (force-hwreg! as rs2 $r.argreg2)))
+    (if (not (unsafe-code))
+        (begin
+          ; check that index is fixnum
+          (sparc.btsti  as rs2 3)
+          (sparc.bne    as fault)
+          (if (not header-loaded?)
+              (sparc.ldi as rs1 (- $tag.bytevector-tag) $r.tmp0))
+          ; check length
+          (sparc.srai   as rs2 2 $r.tmp1)
+          (sparc.srli   as $r.tmp0 8 $r.tmp0)
+          (sparc.cmpr   as $r.tmp0 $r.tmp1)
+          (sparc.bleu as fault)
+          ; No NOP or SLOT -- the SUBI below goes into the slot.
+          )
+        (begin
+          (sparc.srai   as rs2 2 $r.tmp1)))
+    ; Pointer is in RS1.
+    ; Shifted index is in TMP1.
+    (sparc.addi as rs1 (- 4 $tag.bytevector-tag) $r.tmp0)
+    (sparc.ldbr as $r.tmp0 $r.tmp1 $r.tmp0)
+    (if (not charize?)
+        (sparc.slli as $r.tmp0 2 rd)
+        (begin (sparc.slli as $r.tmp0 16 rd)
+               (sparc.ori  as rd $imm.character rd)))))
+
+; As above, but RS2 is replaced by an immediate, IMM.
+;
+; The immediate, represented as a fixnum, is guaranteed fit in the 
+; instruction's immediate field.
+
+(define (emit-bytevector-like-ref/imm! as rs1 imm rd fault charize?
+                                       header-loaded?)
+  (if (not (unsafe-code))
+      (begin
+        (if (not header-loaded?)
+            (sparc.ldi as rs1 (- $tag.bytevector-tag) $r.tmp0))
+        ; Range check.
+        (sparc.srli   as $r.tmp0 8 $r.tmp0)
+        (sparc.cmpi   as $r.tmp0 imm)
+        (sparc.bleu.a as fault)
+        (sparc.slot   as)))
+
+  ; Pointer is in RS1.
+
+  (let ((adjusted-offset (+ (- 4 $tag.bytevector-tag) imm)))
+    (if (immediate-literal? adjusted-offset)
+        (begin
+          (sparc.ldbi as rs1 adjusted-offset $r.tmp0))
+        (begin
+          (sparc.addi as rs1 (- 4 $tag.bytevector-tag) $r.tmp0)
+          (sparc.ldbr as $r.tmp0 imm $r.tmp0)))
+    (if (not charize?)
+        (sparc.slli as $r.tmp0 2 rd)
+        (begin (sparc.slli as $r.tmp0 16 rd)
+               (sparc.ori  as rd $imm.character rd)))))
+
+
+; BYTEVECTOR-SET!, BYTEVECTOR-LIKE-SET!
+;
+; Input:  RESULT -- a pointer to a bytevector-like structure.
+;         TMP0   -- the header iff (unsafe-code) = #f and header-loaded? = #t
+;         IDX    -- a register that holds the second argument
+;         BYTE   -- a register that holds the third argument
+; Output: Nothing.
+;
+; 'Fault' is the address of the error code iff (unsafe-code) = #f
+;
+; FIXME: 
+;   - Argument values passed to error handler appear to be bogus 
+;     (error message is very strange).
+;   - There's no check that the value actually fits in a byte.
+;   - Uses ARGREG3 and and TMP2.
+
+(define (emit-bytevector-like-set! as idx byte fault header-loaded?)
+  (let ((r1 (force-hwreg! as idx $r.tmp1))
+        (r2 (force-hwreg! as byte $r.argreg3)))
+    (if (not (unsafe-code))
+        (begin
+          (if (not header-loaded?)
+              (sparc.ldi     as $r.result (- $tag.bytevector-tag) $r.tmp0))
+          ; Both index and byte must be fixnums.  
+          ; Can't use tsubcc because the computation may really overflow.
+          (sparc.orr     as r1 r2 $r.tmp2)
+          (sparc.btsti   as $r.tmp2 3)
+          (sparc.bnz     as fault)
+          ; No NOP -- next instruction is OK in slot.
+          ; Index must be in range.
+          (sparc.srli    as $r.tmp0 8 $r.tmp0)    ; limit - in slot
+          (sparc.srai    as r1 2 $r.tmp1)         ; index
+          (sparc.cmpr    as $r.tmp1 $r.tmp0)
+          (sparc.bgeu    as fault)
+          ; No NOP -- next instruction is OK in slot.
+          )
+        (begin
+          (sparc.srai   as r1 2 $r.tmp1)))
+    (sparc.srli as r2 2 $r.tmp0)
+    ; Using ARGREG2 as the destination is OK because the resulting pointer
+    ; value always looks like a fixnum.  By doing so, we avoid needing TMP2.
+    (sparc.addi as $r.result (- 4 $tag.bytevector-tag) $r.argreg2)
+    (sparc.stbr as $r.tmp0 $r.tmp1 $r.argreg2)))
+
+
+; STRING-SET!
+
+(define (emit-string-set! as rs1 rs2 rs3)
+  (let* ((rs2 (force-hwreg! as rs2 $r.argreg2))
+         (rs3 (force-hwreg! as rs3 $r.argreg3))
+         (FAULT (if (not (unsafe-code))
+                    (double-tagcheck-assert 
+                     as 
+                     $tag.bytevector-tag
+                     (+ $imm.bytevector-header $tag.string-typetag)
+                     rs1 rs2 rs3
+                     $ex.sset
+                     #f))))
+    ; Header is in TMP0; TMP1 and TMP2 are free.
+    (if (not (unsafe-code))
+        (begin
+          ; RS2 must be a fixnum.
+          (sparc.btsti  as rs2 3)
+          (sparc.bne    as FAULT)
+          ; Index (in RS2) must be valid; header is in tmp0.
+          (sparc.srli   as $r.tmp0 8 $r.tmp0) ; limit
+          (sparc.srai   as rs2 2 $r.tmp1) ; index
+          (sparc.cmpr   as $r.tmp1 $r.tmp0)
+          (sparc.bgeu   as FAULT)
+          ; RS3 must be a character.
+          (sparc.andi   as rs3 #xFF $r.tmp0)
+          (sparc.cmpi   as $r.tmp0 $imm.character)
+          (sparc.bne    as FAULT)
+          ; No NOP -- the SRLI below goes in the slot
+          )
+        (begin
+          (sparc.srai as rs2 2 $r.tmp1)))
+    ; tmp1 has nativeint index. 
+    ; rs3/argreg3 has character.
+    ; tmp0 is garbage.
+    (sparc.subi as $r.tmp1 (- $tag.bytevector-tag 4) $r.tmp1)
+    (sparc.srli as rs3 16 $r.tmp0)
+    (sparc.stbr as $r.tmp0 rs1 $r.tmp1)))
+
+
+; VECTORS and PROCEDURES
+
+; Allocate short vectors of known length; faster than the general case.
+; FIXME: can also allocate in-line.
+
+(define (make-vector-n as length r)
+  (sparc.jmpli as $r.millicode $m.alloc $r.o7)
+  (sparc.set  as (thefixnum (+ length 1)) $r.result)
+  (emit-immediate->register! as (+ (* 256 (thefixnum length))
+                                   $imm.vector-header
+                                   $tag.vector-typetag)
+                             $r.tmp0)
+  (sparc.sti  as $r.tmp0 0 $r.result)
+  (let ((dest (force-hwreg! as r $r.argreg2)))
+    (do ((i 0 (+ i 1)))
+        ((= i length))
+      (sparc.sti as dest (* (+ i 1) 4) $r.result)))
+  (sparc.addi as $r.result $tag.vector-tag $r.result))
+
+
+; emit-make-vector-like! assumes argreg3 is not destroyed by alloci.
+; FIXME: bug: $ex.mkvl is not right if the operation is make-procedure
+; or make-vector.
+
+(define (emit-make-vector-like! as r hdr ptrtag)
+  (let ((FAULT (emit-assert-positive-fixnum! as $r.result $ex.mkvl)))
+    (sparc.move  as $r.result $r.argreg3)
+    (sparc.addi  as $r.result 4 $r.result)
+    (sparc.jmpli as $r.millicode $m.alloci $r.o7)
+    (if (null? r)
+        (sparc.set as $imm.null $r.argreg2)
+        (emit-move2hwreg! as r $r.argreg2))
+    (sparc.slli  as $r.argreg3 8 $r.tmp0)
+    (sparc.addi  as $r.tmp0 hdr $r.tmp0)
+    (sparc.sti   as $r.tmp0 0 $r.result)
+    (sparc.addi  as $r.result ptrtag $r.result)))
+
+
+; VECTOR-REF, VECTOR-LIKE-REF, PROCEDURE-REF
+;
+; FAULT is valid iff (unsafe-code) = #f
+; Header is in TMP0 iff (unsafe-code) = #f and header-loaded? = #t.
+
+(define (emit-vector-like-ref! as rs1 rs2 rd FAULT tag header-loaded?)
+  (let ((index (force-hwreg! as rs2 $r.argreg2)))
+    (if (not (unsafe-code))
+        (begin
+         (if (not header-loaded?)
+             (sparc.ldi   as rs1 (- tag) $r.tmp0))
+         ; Index must be fixnum.
+         (sparc.btsti as index 3)
+         (sparc.bne   as FAULT)
+         ; Index must be within bounds.
+         (sparc.srai  as $r.tmp0 8 $r.tmp0)
+         (sparc.cmpr  as $r.tmp0 index)
+         (sparc.bleu  as FAULT)
+         ; No NOP; the following instruction is valid in the slot.
+         ))
+    (emit-vector-like-ref-trusted! as rs1 index rd tag)))
+
+(define (emit-vector-like-ref-trusted! as rs1 rs2 rd tag)
+  (let ((index (force-hwreg! as rs2 $r.argreg2)))
+    (sparc.addi as rs1 (- 4 tag) $r.tmp0)
+    (sparc.ldr  as $r.tmp0 index rd)))
+
+
+; VECTOR-REF/IMM, VECTOR-LIKE-REF/IMM, PROCEDURE-REF/IMM
+;
+; 'rs1' is a hardware register containing a vectorish pointer (to a
+;       vector-like or procedure).
+; 'imm' is a fixnum s.t. (immediate-literal? imm) => #t.
+; 'rd' is a hardware register.
+; 'FAULT' is the label of the error code iff (unsafe-code) => #f
+; 'tag' is the tag of the pointer in rs1.
+; 'header-loaded?' is #t iff the structure header word is in $r.tmp0.
+
+(define (emit-vector-like-ref/imm! as rs1 imm rd FAULT tag header-loaded?)
+  (if (not (unsafe-code))
+      (begin
+        (if (not header-loaded?) (sparc.ldi as rs1 (- tag) $r.tmp0))
+        ; Check bounds.
+        (sparc.srai  as $r.tmp0 10 $r.tmp0)
+        (sparc.cmpi  as $r.tmp0 imm)
+        (sparc.bleu  as FAULT)
+        (sparc.nop   as)))
+  (emit-vector-like-ref/imm-trusted! as rs1 imm rd tag))
+
+; 'rs1' is a hardware register containing a vectorish pointer (to a
+;       vector-like or procedure).
+; 'imm' is a fixnum s.t. (immediate-literal? imm) => #t.
+; 'rd' is a hardware register.
+; 'tag' is the tag of the pointer in rs1.
+
+(define (emit-vector-like-ref/imm-trusted! as rs1 imm rd tag)
+  (let* ((offset (* imm 4))                       ; words->bytes
+         (adjusted-offset (+ (- 4 tag) offset)))
+    (if (immediate-literal? adjusted-offset)
+        (begin
+          (sparc.ldi as rs1 adjusted-offset rd))
+        (begin
+          (sparc.addi as rs1 (- 4 tag) $r.tmp0)
+          (sparc.ldi  as $r.tmp0 offset rd)))))
+
+
+
+; VECTOR-SET!, VECTOR-LIKE-SET!, PROCEDURE-SET!
+;
+; It is assumed that the pointer in RESULT is valid. We must check the index
+; in register x for validity and then perform the side effect (by calling
+; millicode). The tag is the pointer tag to be adjusted for.
+;
+; The use of vector-set is ok even if it is a procedure.
+
+; fault is valid iff (unsafe-code) = #f
+; header is in tmp0 iff (unsafe-code) = #f and header-loaded? = #t
+
+(define (emit-vector-like-set! as rs1 rs2 rs3 fault tag header-loaded?)
+  (let ((rs2 (force-hwreg! as rs2 $r.tmp1))
+        (rs3 (force-hwreg! as rs3 $r.argreg2)))
+    (if (not (unsafe-code))
+        (begin 
+         (if (not header-loaded?)
+             (sparc.ldi as $r.result (- tag) $r.tmp0))
+         (sparc.btsti as rs2 3)
+         (sparc.bne   as fault)
+         (sparc.srai  as $r.tmp0 8 $r.tmp0)
+         (sparc.cmpr  as $r.tmp0 rs2)
+         (sparc.bleu  as fault)))
+    (emit-vector-like-set-trusted! as rs1 rs2 rs3 tag)))
+
+; rs1 must be a hardware register.
+; tag is the pointer tag to be adjusted for.
+
+(define (emit-vector-like-set-trusted! as rs1 rs2 rs3 tag)
+  (let ((rs2 (force-hwreg! as rs2 $r.tmp1))
+        (rs3 (force-hwreg! as rs3 $r.argreg2)))
+    ;; The ADDR can go in the delay slot of a preceding BLEU.
+    (sparc.addr as rs1 rs2 $r.tmp0)
+    (cond ((not (write-barrier))
+           (sparc.sti  as rs3 (- 4 tag) $r.tmp0))
+          ((= rs1 $r.result)
+           (cond ((= rs3 $r.argreg2)
+                  (sparc.jmpli as $r.millicode $m.addtrans $r.o7)
+                  (sparc.sti  as rs3 (- 4 tag) $r.tmp0))
+                 (else
+                  (sparc.sti  as rs3 (- 4 tag) $r.tmp0)
+                  (millicode-call/1arg as $m.addtrans rs3))))
+          (else
+           (cond ((= rs3 $r.argreg2)
+                  (sparc.sti  as rs3 (- 4 tag) $r.tmp0)
+                  (millicode-call/1arg-in-result as $m.addtrans rs1))
+                 (else
+                  (sparc.sti  as rs3 (- 4 tag) $r.tmp0)
+                  (sparc.move as rs1 $r.result)
+                  (millicode-call/1arg as $m.addtrans rs3)))))))
+
+; eof
+; Copyright 1998 Lars T Hansen.
+;
+; $Id: twobit.sch,v 1.3 1999/08/23 19:14:26 lth Exp $
+;
+; 9 May 1999 / wdc
+;
+; SPARC code generation macros for primitives, part 3:
+;   fixnum-specific operations.
+;
+; Constraints for all the primops.
+;
+; RS1 is a general hardware register or RESULT.
+; RS2 is a general register or ARGREG2.
+; IMM is an exact integer in the range -1024 .. 1023.
+; RD is a general hardware register or RESULT.
+
+; FIXME
+;   Missing fxquotient, fxremainder
+;   When new pass1 in place:
+;     Must add code to pass1 to allow n-ary calls to be rewritten as binary
+;     Must add compiler macro for fxabs.
+
+
+; most-negative-fixnum, most-positive-fixnum.
+
+(define-primop 'most-negative-fixnum
+  (lambda (as)
+    (emit-immediate->register! as (asm:signed #x80000000) $r.result)))
+
+(define-primop 'most-positive-fixnum
+  (lambda (as)
+    (emit-immediate->register! as (asm:signed #x7FFFFFFC) $r.result)))
+
+
+; fx+, fx- w/o immediates
+
+(define-primop 'fx+
+  (lambda (as rs2)
+    (emit-fixnum-arithmetic as sparc.taddrcc sparc.addr $r.result rs2 $r.result
+                           $ex.fx+)))
+
+(define-primop 'internal:fx+
+  (lambda (as rs1 rs2 rd)
+    (emit-fixnum-arithmetic as sparc.taddrcc sparc.addr rs1 rs2 rd $ex.fx+)))
+
+(define-primop 'fx-
+  (lambda (as rs2)
+    (emit-fixnum-arithmetic as sparc.tsubrcc sparc.subr $r.result rs2 $r.result
+                           $ex.fx-)))
+
+(define-primop 'internal:fx-
+  (lambda (as rs1 rs2 rd)
+    (emit-fixnum-arithmetic as sparc.tsubrcc sparc.subr rs1 rs2 rd $ex.fx-)))
+
+(define-primop 'fx--
+  (lambda (as)
+    (emit-fixnum-arithmetic as sparc.tsubrcc sparc.subr
+                           $r.g0 $r.result $r.result $ex.fx--)))
+
+(define-primop 'internal:fx--
+  (lambda (as rs rd)
+    (emit-fixnum-arithmetic as sparc.tsubrcc sparc.subr $r.g0 rs rd $ex.fx--)))
+
+(define (emit-fixnum-arithmetic as op-check op-nocheck rs1 rs2 rd exn)
+  (if (unsafe-code)
+      (let ((rs2 (force-hwreg! as rs2 $r.argreg2)))
+       (op-nocheck as rs1 rs2 rd))
+      (let ((rs2 (force-hwreg! as rs2 $r.argreg2))
+           (L0  (new-label))
+           (L1  (new-label)))
+       (sparc.label  as L0)
+       (op-check     as rs1 rs2 $r.tmp0)
+       (sparc.bvc.a  as L1)
+       (sparc.move   as $r.tmp0 rd)
+        (if (not (= exn $ex.fx--))
+            (begin
+              (if (not (= rs1 $r.result)) (sparc.move as rs1 $r.result))
+              (if (not (= rs2 $r.argreg2)) (sparc.move as rs2 $r.argreg2)))
+            (begin
+              (if (not (= rs2 $r.result)) (sparc.move as rs2 $r.result))))
+       (sparc.set    as (thefixnum exn) $r.tmp0)
+       (millicode-call/ret as $m.exception L0)
+       (sparc.label  as L1))))
+
+; fx* w/o immediate
+
+(define-primop 'fx*
+  (lambda (as rs2)
+    (emit-multiply-code as rs2 #t)))
+
+; fx+, fx- w/immediates
+
+(define-primop 'internal:fx+/imm
+  (lambda (as rs imm rd)
+    (emit-fixnum-arithmetic/imm as sparc.taddicc sparc.addi
+                               rs imm rd $ex.fx+)))
+
+(define-primop 'internal:fx-/imm
+  (lambda (as rs imm rd)
+    (emit-fixnum-arithmetic/imm as sparc.tsubicc sparc.subi
+                               rs imm rd $ex.fx-)))
+
+(define (emit-fixnum-arithmetic/imm as op-check op-nocheck rs imm rd exn)
+  (if (unsafe-code)
+      (op-nocheck as rs (thefixnum imm) rd)
+      (let ((L0  (new-label))
+           (L1  (new-label)))
+       (sparc.label  as L0)
+       (op-check     as rs (thefixnum imm) $r.tmp0)
+       (sparc.bvc.a  as L1)
+       (sparc.move   as $r.tmp0 rd)
+       (if (not (= rs $r.result)) (sparc.move as rs $r.result))
+       (sparc.set    as (thefixnum imm) $r.argreg2)
+       (sparc.set    as (thefixnum exn) $r.tmp0)
+       (millicode-call/ret as $m.exception L0)
+       (sparc.label  as L1))))
+
+
+; fx=, fx<, fx<=, fx>, fx>=, fxpositive?, fxnegative?, fxzero? w/o immediates
+
+(define-primop 'fx=
+  (lambda (as rs2)
+    (emit-fixnum-compare as sparc.bne.a $r.result rs2 $r.result $ex.fx= #f)))
+
+(define-primop 'fx<
+  (lambda (as rs2)
+    (emit-fixnum-compare as sparc.bge.a $r.result rs2 $r.result $ex.fx< #f)))
+
+(define-primop 'fx<=
+  (lambda (as rs2)
+    (emit-fixnum-compare as sparc.bg.a $r.result rs2 $r.result $ex.fx<= #f)))
+
+(define-primop 'fx>
+  (lambda (as rs2)
+    (emit-fixnum-compare as sparc.ble.a $r.result rs2 $r.result $ex.fx> #f)))
+
+(define-primop 'fx>=
+  (lambda (as rs2)
+    (emit-fixnum-compare as sparc.bl.a $r.result rs2 $r.result $ex.fx>= #f)))
+
+(define-primop 'internal:fx=
+  (lambda (as rs1 rs2 rd)
+    (emit-fixnum-compare as sparc.bne.a rs1 rs2 rd $ex.fx= #f)))
+
+(define-primop 'internal:fx<
+  (lambda (as rs1 rs2 rd)
+    (emit-fixnum-compare as sparc.bge.a rs1 rs2 rd $ex.fx< #f)))
+
+(define-primop 'internal:fx<=
+  (lambda (as rs1 rs2 rd)
+    (emit-fixnum-compare as sparc.bg.a rs1 rs2 rd $ex.fx<= #f)))
+
+(define-primop 'internal:fx>
+  (lambda (as rs1 rs2 rd)
+    (emit-fixnum-compare as sparc.ble.a rs1 rs2 rd $ex.fx> #f)))
+
+(define-primop 'internal:fx>=
+  (lambda (as rs1 rs2 rd)
+    (emit-fixnum-compare as sparc.bl.a rs1 rs2 rd $ex.fx>= #f)))
+
+
+; Use '/imm' code for these because the generated code is better.
+
+(define-primop 'fxpositive?
+  (lambda (as)
+    (emit-fixnum-compare/imm as sparc.ble.a $r.result 0 $r.result
+                            $ex.fxpositive? #f)))
+
+(define-primop 'fxnegative?
+  (lambda (as)
+    (emit-fixnum-compare/imm as sparc.bge.a $r.result 0 $r.result
+                               $ex.fxnegative? #f)))
+
+(define-primop 'fxzero?
+  (lambda (as)
+    (emit-fixnum-compare/imm as sparc.bne.a $r.result 0 $r.result
+                               $ex.fxzero? #f)))
+
+(define-primop 'internal:fxpositive?
+  (lambda (as rs rd)
+    (emit-fixnum-compare/imm as sparc.ble.a rs 0 rd $ex.fxpositive? #f)))
+
+(define-primop 'internal:fxnegative?
+  (lambda (as rs rd)
+    (emit-fixnum-compare/imm as sparc.bge.a rs 0 rd $ex.fxnegative? #f)))
+
+(define-primop 'internal:fxzero?
+  (lambda (as rs rd)
+    (emit-fixnum-compare/imm as sparc.bne.a rs 0 rd $ex.fxzero? #f)))
+
+
+; fx=, fx<, fx<=, fx>, fx>=  w/immediates
+
+(define-primop 'internal:fx=/imm
+  (lambda (as rs imm rd)
+    (emit-fixnum-compare/imm as sparc.bne.a rs imm rd $ex.fx= #f)))
+
+(define-primop 'internal:fx</imm
+  (lambda (as rs imm rd)
+    (emit-fixnum-compare/imm as sparc.bge.a rs imm rd $ex.fx< #f)))
+
+(define-primop 'internal:fx<=/imm
+  (lambda (as rs imm rd)
+    (emit-fixnum-compare/imm as sparc.bg.a rs imm rd $ex.fx<= #f)))
+
+(define-primop 'internal:fx>/imm
+  (lambda (as rs imm rd)
+    (emit-fixnum-compare/imm as sparc.ble.a rs imm rd $ex.fx> #f)))
+
+(define-primop 'internal:fx>=/imm
+  (lambda (as rs imm rd)
+    (emit-fixnum-compare/imm as sparc.bl.a rs imm rd $ex.fx>= #f)))
+
+; fx=, fx<, fx<=, fx>, fx>=, fxpositive?, fxnegative?, fxzero? w/o immediates
+; for control.
+
+(define-primop 'internal:branchf-fx=
+  (lambda (as rs1 rs2 L)
+    (emit-fixnum-compare as sparc.bne.a rs1 rs2 #f $ex.fx= L)))
+
+(define-primop 'internal:branchf-fx<
+  (lambda (as rs1 rs2 L)
+    (emit-fixnum-compare as sparc.bge.a rs1 rs2 #f $ex.fx< L)))
+
+(define-primop 'internal:branchf-fx<=
+  (lambda (as rs1 rs2 L)
+    (emit-fixnum-compare as sparc.bg.a rs1 rs2 #f $ex.fx<= L)))
+
+(define-primop 'internal:branchf-fx>
+  (lambda (as rs1 rs2 L)
+    (emit-fixnum-compare as sparc.ble.a rs1 rs2 #f $ex.fx> L)))
+
+(define-primop 'internal:branchf-fx>=
+  (lambda (as rs1 rs2 L)
+    (emit-fixnum-compare as sparc.bl.a rs1 rs2 #f $ex.fx>= L)))
+
+(define-primop 'internal:branchf-fxpositive?
+  (lambda (as rs1 L)
+    (emit-fixnum-compare/imm as sparc.ble.a rs1 0 #f $ex.fxpositive? L)))
+
+(define-primop 'internal:branchf-fxnegative?
+  (lambda (as rs1 L)
+    (emit-fixnum-compare/imm as sparc.bge.a rs1 0 #f $ex.fxnegative? L)))
+
+(define-primop 'internal:branchf-fxzero?
+  (lambda (as rs1 L)
+    (emit-fixnum-compare/imm as sparc.bne.a rs1 0 #f $ex.fxzero? L)))
+
+
+; fx=, fx<, fx<=, fx>, fx>=  w/immediates for control.
+
+(define-primop 'internal:branchf-fx=/imm
+  (lambda (as rs imm L)
+    (emit-fixnum-compare/imm as sparc.bne.a rs imm #f $ex.fx= L)))
+
+(define-primop 'internal:branchf-fx</imm
+  (lambda (as rs imm L)
+    (emit-fixnum-compare/imm as sparc.bge.a rs imm #f $ex.fx< L)))
+
+(define-primop 'internal:branchf-fx<=/imm
+  (lambda (as rs imm L)
+    (emit-fixnum-compare/imm as sparc.bg.a rs imm #f $ex.fx<= L)))
+
+(define-primop 'internal:branchf-fx>/imm
+  (lambda (as rs imm L)
+    (emit-fixnum-compare/imm as sparc.ble.a rs imm #f $ex.fx> L)))
+
+(define-primop 'internal:branchf-fx>=/imm
+  (lambda (as rs imm L)
+    (emit-fixnum-compare/imm as sparc.bl.a rs imm #f $ex.fx>= L)))
+
+
+; Trusted fixnum comparisons.
+
+(define-primop '=:fix:fix
+  (lambda (as rs2)
+    (emit-fixnum-compare-trusted as sparc.bne.a $r.result rs2 $r.result #f)))
+
+(define-primop '<:fix:fix
+  (lambda (as rs2)
+    (emit-fixnum-compare-trusted as sparc.bge.a $r.result rs2 $r.result #f)))
+
+(define-primop '<=:fix:fix
+  (lambda (as rs2)
+    (emit-fixnum-compare-trusted as sparc.bg.a $r.result rs2 $r.result #f)))
+
+(define-primop '>:fix:fix
+  (lambda (as rs2)
+    (emit-fixnum-compare-trusted as sparc.ble.a $r.result rs2 $r.result #f)))
+
+(define-primop '>=:fix:fix
+  (lambda (as rs2)
+    (emit-fixnum-compare-trusted as sparc.bl.a $r.result rs2 $r.result #f)))
+
+(define-primop 'internal:=:fix:fix
+  (lambda (as rs1 rs2 rd)
+    (emit-fixnum-compare-trusted as sparc.bne.a rs1 rs2 rd #f)))
+
+(define-primop 'internal:<:fix:fix
+  (lambda (as rs1 rs2 rd)
+    (emit-fixnum-compare-trusted as sparc.bge.a rs1 rs2 rd #f)))
+
+(define-primop 'internal:<=:fix:fix
+  (lambda (as rs1 rs2 rd)
+    (emit-fixnum-compare-trusted as sparc.bg.a rs1 rs2 rd #f)))
+
+(define-primop 'internal:>:fix:fix
+  (lambda (as rs1 rs2 rd)
+    (emit-fixnum-compare-trusted as sparc.ble.a rs1 rs2 rd #f)))
+
+(define-primop 'internal:>=:fix:fix
+  (lambda (as rs1 rs2 rd)
+    (emit-fixnum-compare-trusted as sparc.bl.a rs1 rs2 rd #f)))
+
+; With immediates.
+
+(define-primop 'internal:=:fix:fix/imm
+  (lambda (as rs imm rd)
+    (emit-fixnum-compare/imm-trusted as sparc.bne.a rs imm rd #f)))
+
+(define-primop 'internal:<:fix:fix/imm
+  (lambda (as rs imm rd)
+    (emit-fixnum-compare/imm-trusted as sparc.bge.a rs imm rd #f)))
+
+(define-primop 'internal:<=:fix:fix/imm
+  (lambda (as rs imm rd)
+    (emit-fixnum-compare/imm-trusted as sparc.bg.a rs imm rd #f)))
+
+(define-primop 'internal:>:fix:fix/imm
+  (lambda (as rs imm rd)
+    (emit-fixnum-compare/imm-trusted as sparc.ble.a rs imm rd #f)))
+
+(define-primop 'internal:>=:fix:fix/imm
+  (lambda (as rs imm rd)
+    (emit-fixnum-compare/imm-trusted as sparc.bl.a rs imm rd #f)))
+
+; Without immediates, for control.
+
+(define-primop 'internal:branchf-=:fix:fix
+  (lambda (as rs1 rs2 L)
+    (emit-fixnum-compare-trusted as sparc.bne.a rs1 rs2 #f L)))
+
+(define-primop 'internal:branchf-<:fix:fix
+  (lambda (as rs1 rs2 L)
+    (emit-fixnum-compare-trusted as sparc.bge.a rs1 rs2 #f L)))
+
+(define-primop 'internal:branchf-<=:fix:fix
+  (lambda (as rs1 rs2 L)
+    (emit-fixnum-compare-trusted as sparc.bg.a rs1 rs2 #f L)))
+
+(define-primop 'internal:branchf->:fix:fix
+  (lambda (as rs1 rs2 L)
+    (emit-fixnum-compare-trusted as sparc.ble.a rs1 rs2 #f L)))
+
+(define-primop 'internal:branchf->=:fix:fix
+  (lambda (as rs1 rs2 L)
+    (emit-fixnum-compare-trusted as sparc.bl.a rs1 rs2 #f L)))
+
+; With immediates, for control.
+
+(define-primop 'internal:branchf-=:fix:fix/imm
+  (lambda (as rs imm L)
+    (emit-fixnum-compare/imm-trusted as sparc.bne.a rs imm #f L)))
+
+(define-primop 'internal:branchf-<:fix:fix/imm
+  (lambda (as rs imm L)
+    (emit-fixnum-compare/imm-trusted as sparc.bge.a rs imm #f L)))
+
+(define-primop 'internal:branchf-<=:fix:fix/imm
+  (lambda (as rs imm L)
+    (emit-fixnum-compare/imm-trusted as sparc.bg.a rs imm #f L)))
+
+(define-primop 'internal:branchf->:fix:fix/imm
+  (lambda (as rs imm L)
+    (emit-fixnum-compare/imm-trusted as sparc.ble.a rs imm #f L)))
+
+(define-primop 'internal:branchf->=:fix:fix/imm
+  (lambda (as rs imm L)
+    (emit-fixnum-compare/imm-trusted as sparc.bl.a rs imm #f L)))
+
+; Range check:  0 <= src1 < src2
+
+(define-primop 'internal:check-range
+  (lambda (as src1 src2 L1 livregs)
+    (let ((src2 (force-hwreg! as src2 $r.argreg2)))
+      (emit-fixnum-compare-check
+       as src2 src1 sparc.bleu L1 livregs))))
+
+; Trusted fixnum comparisons followed by a check.
+
+(define-primop 'internal:check-=:fix:fix
+  (lambda (as src1 src2 L1 liveregs)
+    (emit-fixnum-compare-check
+     as src1 src2 sparc.bne L1 liveregs)))
+
+(define-primop 'internal:check-<:fix:fix
+  (lambda (as src1 src2 L1 liveregs)
+    (emit-fixnum-compare-check
+     as src1 src2 sparc.bge L1 liveregs)))
+
+(define-primop 'internal:check-<=:fix:fix
+  (lambda (as src1 src2 L1 liveregs)
+    (emit-fixnum-compare-check
+     as src1 src2 sparc.bg L1 liveregs)))
+
+(define-primop 'internal:check->:fix:fix
+  (lambda (as src1 src2 L1 liveregs)
+    (emit-fixnum-compare-check
+     as src1 src2 sparc.ble L1 liveregs)))
+
+(define-primop 'internal:check->=:fix:fix
+  (lambda (as src1 src2 L1 liveregs)
+    (emit-fixnum-compare-check
+     as src1 src2 sparc.bl L1 liveregs)))
+
+(define-primop 'internal:check-=:fix:fix/imm
+  (lambda (as src1 imm L1 liveregs)
+    (emit-fixnum-compare/imm-check
+     as src1 imm sparc.bne L1 liveregs)))
+
+(define-primop 'internal:check-<:fix:fix/imm
+  (lambda (as src1 imm L1 liveregs)
+    (emit-fixnum-compare/imm-check
+     as src1 imm sparc.bge L1 liveregs)))
+
+(define-primop 'internal:check-<=:fix:fix/imm
+  (lambda (as src1 imm L1 liveregs)
+    (emit-fixnum-compare/imm-check
+     as src1 imm sparc.bg L1 liveregs)))
+
+(define-primop 'internal:check->:fix:fix/imm
+  (lambda (as src1 imm L1 liveregs)
+    (emit-fixnum-compare/imm-check
+     as src1 imm sparc.ble L1 liveregs)))
+
+(define-primop 'internal:check->=:fix:fix/imm
+  (lambda (as src1 imm L1 liveregs)
+    (emit-fixnum-compare/imm-check
+     as src1 imm sparc.bl L1 liveregs)))
+
+; Below, 'target' is a label or #f.  If #f, RD must be a general hardware
+; register or RESULT, and a boolean result is generated in RD.
+
+(define (emit-fixnum-compare as branchf.a rs1 rs2 rd exn target)
+  (if (unsafe-code)
+      (emit-fixnum-compare-trusted as branchf.a rs1 rs2 rd target)
+      (let ((rs2 (force-hwreg! as rs2 $r.argreg2))
+            (L0 (new-label))
+            (L1 (new-label)))
+        (sparc.label as L0)
+        (sparc.orr   as rs1 rs2 $r.tmp0)
+        (sparc.btsti as $r.tmp0 3)
+        (sparc.be.a  as L1)
+        (sparc.cmpr  as rs1 rs2)
+        (if (not (= rs1 $r.result)) (sparc.move as rs1 $r.result))
+        (if (not (= rs2 $r.argreg2)) (sparc.move as rs2 $r.argreg2))
+        (sparc.set   as (thefixnum exn) $r.tmp0)
+        (millicode-call/ret as $m.exception L0)
+        (sparc.label as L1)
+        (emit-evaluate-cc! as branchf.a rd target))))
+
+; Below, 'target' is a label or #f.  If #f, RD must be a general hardware
+; register or RESULT, and a boolean result is generated in RD.
+
+(define (emit-fixnum-compare-trusted as branchf.a rs1 rs2 rd target)
+  (let ((rs2 (force-hwreg! as rs2 $r.argreg2)))
+    (sparc.cmpr  as rs1 rs2)
+    (emit-evaluate-cc! as branchf.a rd target)))
+
+; rs must be a hardware register.
+
+(define (emit-fixnum-compare/imm as branchf.a rs imm rd exn target)
+  (if (unsafe-code)
+      (emit-fixnum-compare/imm-trusted as branchf.a rs imm rd target)
+      (let ((L0 (new-label))
+            (L1 (new-label)))
+        (sparc.label as L0)
+        (sparc.btsti as rs 3)
+        (sparc.be.a  as L1)
+        (sparc.cmpi  as rs (thefixnum imm))
+        (if (not (= rs $r.result)) (sparc.move as rs $r.result))
+        (sparc.set   as (thefixnum imm) $r.argreg2)
+        (sparc.set   as (thefixnum exn) $r.tmp0)
+        (millicode-call/ret as $m.exception L0)
+        (sparc.label as L1)))
+  (emit-evaluate-cc! as branchf.a rd target))
+
+; rs must be a hardware register.
+
+(define (emit-fixnum-compare/imm-trusted as branchf.a rs imm rd target)
+  (sparc.cmpi  as rs (thefixnum imm))
+  (emit-evaluate-cc! as branchf.a rd target))
+
+; Range checks.
+
+(define (emit-fixnum-compare-check
+         as src1 src2 branch-bad L1 liveregs)
+  (internal-primop-invariant1 'emit-fixnum-compare-check src1)
+  (let ((src2 (force-hwreg! as src2 $r.argreg2)))
+    (sparc.cmpr    as src1 src2)
+    (emit-checkcc! as branch-bad L1 liveregs)))
+
+(define (emit-fixnum-compare/imm-check
+         as src1 imm branch-bad L1 liveregs)
+  (internal-primop-invariant1 'emit-fixnum-compare/imm-check src1)
+  (sparc.cmpi    as src1 imm)
+  (emit-checkcc! as branch-bad L1 liveregs))
+
+; eof
+; Copyright 1998 Lars T Hansen.
+;
+; $Id: twobit.sch,v 1.3 1999/08/23 19:14:26 lth Exp $
+;
+; SPARC machine assembler flags.
+;
+; 12 April 1999
+
+
+; INTERNAL!
+(define short-effective-addresses
+  (make-twobit-flag 'short-effective-addresses))
+
+(define runtime-safety-checking
+  (make-twobit-flag 'runtime-safety-checking))
+
+(define catch-undefined-globals
+  (make-twobit-flag 'catch-undefined-globals))
+
+(define inline-allocation
+  (make-twobit-flag 'inline-allocation))
+  
+;(define inline-assignment
+;  (make-twobit-flag 'inline-assignment))
+
+(define write-barrier
+  (make-twobit-flag 'write-barrier))  
+
+(define peephole-optimization
+  (make-twobit-flag 'peephole-optimization))
+
+(define single-stepping
+  (make-twobit-flag 'single-stepping))
+
+(define fill-delay-slots
+  (make-twobit-flag 'fill-delay-slots))
+
+; For backward compatibility.
+
+;(define unsafe-code
+;  (make-twobit-flag 'unsafe-code))
+
+(define (unsafe-code . args)
+  (if (null? args)
+      (not (runtime-safety-checking))
+      (runtime-safety-checking (not (car args)))))
+
+(define (display-assembler-flags which)
+  (case which
+    ((debugging)
+     (display-twobit-flag single-stepping))
+    ((safety)
+     (display-twobit-flag write-barrier)
+     ;(display-twobit-flag unsafe-code)
+     (display-twobit-flag runtime-safety-checking)
+     (if (runtime-safety-checking)
+         (begin (display "  ")
+                (display-twobit-flag catch-undefined-globals))))
+    ((optimization)
+     (display-twobit-flag peephole-optimization)
+     (display-twobit-flag inline-allocation)
+     ;  (display-twobit-flag inline-assignment)
+     (display-twobit-flag fill-delay-slots))
+    (else #t)))
+
+(define (set-assembler-flags! mode)
+  (case mode
+    ((no-optimization)
+     (set-assembler-flags! 'standard)
+     (peephole-optimization #f)
+     (fill-delay-slots #f))
+    ((standard)
+     (short-effective-addresses #t)
+     (catch-undefined-globals #t)
+     (inline-allocation #f)
+     ; (inline-assignment #f)
+     (peephole-optimization #t)
+     (runtime-safety-checking #t)
+     (write-barrier #t)
+     (single-stepping #f)
+     (fill-delay-slots #t))
+    ((fast-safe default)
+     (set-assembler-flags! 'standard)
+     ; (inline-assignment #t)
+     (inline-allocation #t))
+    ((fast-unsafe)
+     (set-assembler-flags! 'fast-safe)
+     (catch-undefined-globals #f)
+     (runtime-safety-checking #f))
+    (else
+     (error "set-assembler-flags!: unknown mode " mode))))
+
+(set-assembler-flags! 'default)
+
+; eof
+; Copyright 1998 Lars T Hansen.
+;
+; $Id: twobit.sch,v 1.3 1999/08/23 19:14:26 lth Exp $
+;
+; SPARC disassembler.
+;
+; (disassemble-instruction instruction address)
+;     => decoded-instruction
+;
+; (disassemble-codevector codevector)
+;     => decoded-instruction-list
+;
+; (print-instructions decoded-instruction-list)
+;     => unspecified
+;     Also takes an optional port and optionally the symbol "native-names".
+;
+; (format-instruction decoded-instruction address larceny-names?) 
+;     => string
+; 
+; A `decoded-instruction' is a list where the car is a mnemonic and
+; the operands are appropriate for that mnemonic.
+;
+; A `mnemonic' is an exact nonnegative integer.  It encodes the name of
+; the instruction as well as its attributes (operand pattern and instruction
+; type).  See below for specific operations on mnemonics.
+
+(define (disassemble-codevector cv)
+  (define (loop addr ilist)
+    (if (< addr 0)
+       ilist
+       (loop (- addr 4)
+             (cons (disassemble-instruction (bytevector-word-ref cv addr)
+                                            addr)
+                   ilist))))
+  (loop (- (bytevector-length cv) 4) '()))
+
+(define disassemble-instruction)           ; Defined below.
+
+\f; Mnemonics
+
+(define *asm-annul* 1)
+(define *asm-immed* 2)
+(define *asm-store* 4)
+(define *asm-load* 8)
+(define *asm-branch* 16)
+(define *asm-freg* 32)
+(define *asm-fpop* 64)
+(define *asm-no-op2* 128)
+(define *asm-no-op3* 256)
+
+(define *asm-bits*
+  `((a . ,*asm-annul*) (i . ,*asm-immed*) (s . ,*asm-store*)
+    (l . ,*asm-load*) (b . ,*asm-branch*) (f . ,*asm-freg*)
+    (fpop . ,*asm-fpop*) (no-op2 . ,*asm-no-op2*) (no-op3 . ,*asm-no-op3*)))
+
+(define *asm-mnemonic-table* '())
+
+(define mnemonic 
+  (let ((n 0))
+    (lambda (name . rest)
+      (let* ((probe (assq name *asm-mnemonic-table*))
+            (code  (* 1024 
+                      (if probe
+                          (cdr probe)
+                          (let ((code n))
+                            (set! n (+ n 1))
+                            (set! *asm-mnemonic-table*
+                                  (cons (cons name code)
+                                        *asm-mnemonic-table*))
+                            code)))))
+       (for-each (lambda (x)
+                   (set! code (+ code (cdr (assq x *asm-bits*)))))
+                 rest)
+       code))))
+
+(define (mnemonic:name mnemonic)
+  (let ((mnemonic (quotient mnemonic 1024)))
+    (let loop ((t *asm-mnemonic-table*))
+      (cond ((null? t) #f)
+           ((= (cdar t) mnemonic) (caar t))
+           (else (loop (cdr t)))))))
+
+(define (mnemonic=? m name)
+  (= (quotient m 1024) (quotient (mnemonic name) 1024)))
+
+(define (mnemonic:test bit)
+  (lambda (mnemonic)
+    (not (zero? (logand mnemonic bit)))))
+
+(define (mnemonic:test-not bit)
+  (lambda (mnemonic)
+    (zero? (logand mnemonic bit))))
+
+(define mnemonic:annul? (mnemonic:test *asm-annul*))
+(define mnemonic:immediate? (mnemonic:test *asm-immed*))
+(define mnemonic:store? (mnemonic:test *asm-store*))
+(define mnemonic:load? (mnemonic:test *asm-load*))
+(define mnemonic:branch? (mnemonic:test *asm-branch*))
+(define mnemonic:freg? (mnemonic:test *asm-freg*))
+(define mnemonic:fpop? (mnemonic:test *asm-fpop*))
+(define mnemonic:op2? (mnemonic:test-not *asm-no-op2*))
+(define mnemonic:op3? (mnemonic:test-not *asm-no-op3*))
+
+\f; Instruction disassembler.
+
+(let ()
+
+  ;; Useful constants
+
+  (define two^3 (expt 2 3))
+  (define two^5 (expt 2 5))
+  (define two^6 (expt 2 6))
+  (define two^8 (expt 2 8))
+  (define two^9 (expt 2 9))
+  (define two^12 (expt 2 12))
+  (define two^13 (expt 2 13))
+  (define two^14 (expt 2 14))
+  (define two^16 (expt 2 16))
+  (define two^19 (expt 2 19))
+  (define two^21 (expt 2 21))
+  (define two^22 (expt 2 22))
+  (define two^24 (expt 2 24))
+  (define two^25 (expt 2 25))
+  (define two^29 (expt 2 29))
+  (define two^30 (expt 2 30))
+  (define two^32 (expt 2 32))
+
+  ;; Class 0 has branches and weirdness, like sethi and nop.
+  ;; We dispatch first on the op2 field and then on the op3 field.
+
+  (define class00
+    (let ((b-table
+          (vector (mnemonic 'bn 'b)
+                  (mnemonic 'be 'b)
+                  (mnemonic 'ble 'b)
+                  (mnemonic 'bl 'b)
+                  (mnemonic 'bleu 'b)
+                  (mnemonic 'bcs 'b)
+                  (mnemonic 'bneg 'b)
+                  (mnemonic 'bvs 'b)
+                  (mnemonic 'ba 'b)
+                  (mnemonic 'bne 'b)
+                  (mnemonic 'bg 'b)
+                  (mnemonic 'bge 'b)
+                  (mnemonic 'bgu 'b)
+                  (mnemonic 'bcc 'b)
+                  (mnemonic 'bpos 'b)
+                  (mnemonic 'bvc 'b)
+                  (mnemonic 'bn 'a 'b)
+                  (mnemonic 'be 'a 'b)
+                  (mnemonic 'ble 'a 'b)
+                  (mnemonic 'bl 'a 'b)
+                  (mnemonic 'bleu 'a 'b)
+                  (mnemonic 'bcs 'a 'b)
+                  (mnemonic 'bneg 'a 'b)
+                  (mnemonic 'bvs 'a 'b)
+                  (mnemonic 'ba 'a 'b)
+                  (mnemonic 'bne 'a 'b)
+                  (mnemonic 'bg 'a 'b)
+                  (mnemonic 'bge 'a 'b)
+                  (mnemonic 'bgu 'a 'b)
+                  (mnemonic 'bcc 'a 'b)
+                  (mnemonic 'bpos 'a 'b)
+                  (mnemonic 'bvc 'a 'b)))
+         (fb-table
+          (vector (mnemonic 'fbn 'b)
+                  (mnemonic 'fbne 'b)
+                  (mnemonic 'fblg 'b)
+                  (mnemonic 'fbul 'b)
+                  (mnemonic 'fbl 'b)
+                  (mnemonic 'fbug 'b)
+                  (mnemonic 'fbg 'b)
+                  (mnemonic 'fbu 'b)
+                  (mnemonic 'fba 'b)
+                  (mnemonic 'fbe 'b)
+                  (mnemonic 'fbue 'b)
+                  (mnemonic 'fbge 'b)
+                  (mnemonic 'fbuge 'b)
+                  (mnemonic 'fble 'b)
+                  (mnemonic 'fbule 'b)
+                  (mnemonic 'fbo 'b)
+                  (mnemonic 'fbn 'a 'b)
+                  (mnemonic 'fbne 'a 'b)
+                  (mnemonic 'fblg 'a 'b)
+                  (mnemonic 'fbul 'a 'b)
+                  (mnemonic 'fbl 'a 'b)
+                  (mnemonic 'fbug 'a 'b)
+                  (mnemonic 'fbg 'a 'b)
+                  (mnemonic 'fbu 'a 'b)
+                  (mnemonic 'fba 'a 'b)
+                  (mnemonic 'fbe 'a 'b)
+                  (mnemonic 'fbue 'a 'b)
+                  (mnemonic 'fbge 'a 'b)
+                  (mnemonic 'fbuge 'a 'b)
+                  (mnemonic 'fble 'a 'b)
+                  (mnemonic 'fbule 'a 'b)
+                  (mnemonic 'fbo 'a 'b)))
+         (nop (mnemonic 'nop))
+         (sethi (mnemonic 'sethi)))
+
+      (lambda (ip instr)
+       (let ((op2 (op2field instr)))
+         (cond ((= op2 #b100)
+                (if (zero? (rdfield instr))
+                    `(,nop)
+                    `(,sethi ,(imm22field instr) ,(rdfield instr))))
+               ((= op2 #b010)
+                `(,(vector-ref b-table (rdfield instr))
+                  ,(* 4 (imm22field instr))))
+               ((= op2 #b110)
+                `(,(vector-ref fb-table (rdfield instr))
+                  ,(* 4 (imm22field instr))))
+               (else
+                (disasm-error "Can't disassemble " (number->string instr 16)
+                              " at ip=" ip
+                              " with op2=" op2)))))))
+
+  ;; Class 1 is the call instruction; there's no choice.
+
+  (define (class01 ip instr)
+    `(,(mnemonic 'call) ,(* 4 (imm30field instr))))
+
+  ;; Class 2 is for the ALU. Dispatch on op3 field.
+
+  (define class10
+    (let ((op3-table
+          `#((,(mnemonic 'add)   ,(mnemonic 'add 'i))
+             (,(mnemonic 'and)   ,(mnemonic 'and 'i))
+             (,(mnemonic 'or)    ,(mnemonic 'or 'i))
+             (,(mnemonic 'xor)   ,(mnemonic 'xor 'i))
+             (,(mnemonic 'sub)   ,(mnemonic 'sub 'i))
+             (,(mnemonic 'andn)  ,(mnemonic 'andn 'i))
+             (,(mnemonic 'orn)   ,(mnemonic 'orn 'i))
+             (,(mnemonic 'xnor)  ,(mnemonic 'xnor 'i))
+             (0          0)
+             (0          0)
+             (0          0)                              ; 10
+             (,(mnemonic 'smul)  ,(mnemonic 'smul 'i))
+             (0          0)
+             (0          0)
+             (0          0)
+             (,(mnemonic 'sdiv)  ,(mnemonic 'sdiv 'i))
+             (,(mnemonic 'addcc) ,(mnemonic 'addcc 'i))
+             (,(mnemonic 'andcc) ,(mnemonic 'andcc 'i))
+             (,(mnemonic 'orcc)  ,(mnemonic 'orcc 'i))
+             (,(mnemonic 'xorcc) ,(mnemonic 'xorcc 'i))
+             (,(mnemonic 'subcc) ,(mnemonic 'subcc 'i))  ; 20
+             (0          0)
+             (0          0)
+             (0          0)
+             (0          0)
+             (0          0)
+             (0          0)
+             (,(mnemonic 'smulcc) ,(mnemonic 'smulcc 'i))
+             (0          0)
+             (0          0)
+             (0          0)                               ; 30
+             (,(mnemonic 'sdivcc) ,(mnemonic 'sdivcc 'i))
+             (,(mnemonic 'taddcc) ,(mnemonic 'taddcc 'i))
+             (,(mnemonic 'tsubcc) ,(mnemonic 'tsubcc 'i))
+             (0          0)
+             (0          0)
+             (0          0)
+             (,(mnemonic 'sll)   ,(mnemonic 'sll 'i))
+             (,(mnemonic 'srl)   ,(mnemonic 'srl 'i))
+             (,(mnemonic 'sra)   ,(mnemonic 'sra 'i))
+             (,(mnemonic 'rd)   0)                       ; 40
+             (0          0)
+             (0          0)
+             (0          0)
+             (0          0)
+             (0          0)
+             (0          0)
+             (0          0)
+             (,(mnemonic 'wr)  ,(mnemonic 'wr 'i))
+             (0          0)
+             (0          0)                               ; 50
+             (0          0)
+             (0          0)
+             (0          0)
+             (0          0)
+             (0          0)
+             (,(mnemonic 'jmpl)  ,(mnemonic 'jmpl 'i))
+             (0          0)
+             (0          0)
+             (0          0)
+             (,(mnemonic 'save)  ,(mnemonic 'save 'i))   ; 60
+             (,(mnemonic 'restore) ,(mnemonic 'restore 'i))
+             (0          0)
+             (0          0))))
+
+      (lambda (ip instr)
+       (let ((op3 (op3field instr)))
+         (if (or (= op3 #b110100) (= op3 #b110101))
+             (fpop-instruction ip instr)
+             (nice-instruction op3-table ip instr))))))
+
+
+  ;; Class 3 is memory stuff.
+
+  (define class11
+    (let ((op3-table
+          `#((,(mnemonic 'ld 'l)    ,(mnemonic 'ld 'i 'l))
+             (,(mnemonic 'ldb 'l)   ,(mnemonic 'ldb 'i 'l))
+             (,(mnemonic 'ldh 'l)   ,(mnemonic 'ldh 'i 'l))
+             (,(mnemonic 'ldd 'l)   ,(mnemonic 'ldd 'i 'l))
+             (,(mnemonic 'st 's)    ,(mnemonic 'st 'i 's))
+             (,(mnemonic 'stb 's)   ,(mnemonic 'stb 'i 's))
+             (,(mnemonic 'sth 's)   ,(mnemonic 'sth 'i 's))
+             (,(mnemonic 'std 's)   ,(mnemonic 'std 'i 's))
+             (0          0)
+             (0          0)
+             (0          0)            ; 10
+             (0          0)
+             (0          0)
+             (0          0)
+             (0          0)
+             (0          0)
+             (0          0)
+             (0          0)
+             (0          0)
+             (0          0)
+             (0          0)            ; 20
+             (0          0)
+             (0          0)
+             (0          0)
+             (0          0)
+             (0          0)
+             (0          0)
+             (0          0)
+             (0          0)
+             (0          0)
+             (0          0)            ; 30
+             (0          0)
+             (,(mnemonic 'ldf 'f 'l) ,(mnemonic 'ldf 'i 'f 'l))
+             (0          0)
+             (0          0)
+             (,(mnemonic 'lddf 'f 'l) ,(mnemonic 'lddf 'i 'f 'l))
+             (,(mnemonic 'stf 'f 's)  ,(mnemonic 'stf 'i 'f 's))
+             (0          0)
+             (0          0)
+             (,(mnemonic 'stdf 'f 's) ,(mnemonic 'stdf 'i 'f 's))
+             (0          0)            ; 40
+             (0          0)
+             (0          0)
+             (0          0)
+             (0          0)
+             (0          0)
+             (0          0)
+             (0          0)
+             (0          0)
+             (0          0)
+             (0          0)            ; 50
+             (0          0)
+             (0          0)
+             (0          0)
+             (0          0)
+             (0          0)
+             (0          0)
+             (0          0)
+             (0          0)
+             (0          0)
+             (0          0)            ; 60
+             (0          0)
+             (0          0)
+             (0          0))))
+
+      (lambda (ip instr)
+       (nice-instruction op3-table ip instr))))
+
+  ;; For classes 2 and 3
+
+  (define (nice-instruction op3-table ip instr)
+    (let* ((op3  (op3field instr))
+          (imm  (ifield instr))
+          (rd   (rdfield instr))
+          (rs1  (rs1field instr))
+          (src2 (if (zero? imm)
+                    (rs2field instr)
+                    (imm13field instr))))
+      (let ((op ((if (zero? imm) car cadr) (vector-ref op3-table op3))))
+       `(,op ,rs1 ,src2 ,rd))))
+
+  ;; Floating-point operate instructions
+
+  (define (fpop-instruction ip instr)
+    (let ((rd  (rdfield instr))
+         (rs1 (rs1field instr))
+         (rs2 (rs2field instr))
+         (fpop (fpop-field instr)))
+      `(,(cdr (assv fpop fpop-names)) ,rs1 ,rs2 ,rd)))
+
+  (define fpop-names
+    `((#b000000001 . ,(mnemonic 'fmovs 'fpop 'no-op2))
+      (#b000000101 . ,(mnemonic 'fnegs 'fpop 'no-op2))
+      (#b000001001 . ,(mnemonic 'fabss 'fpop 'no-op2))
+      (#b001000010 . ,(mnemonic 'faddd 'fpop))
+      (#b001000110 . ,(mnemonic 'fsubd 'fpop))
+      (#b001001010 . ,(mnemonic 'fmuld 'fpop))
+      (#b001001110 . ,(mnemonic 'fdivd 'fpop))
+      (#b001010010 . ,(mnemonic 'fcmpd 'fpop 'no-op3))))
+      
+
+  ;; The following procedures pick apart an instruction
+
+  (define (op2field instr)
+    (remainder (quotient instr two^22) two^3))
+
+  (define (op3field instr)
+    (remainder (quotient instr two^19) two^6))
+
+  (define (ifield instr)
+    (remainder (quotient instr two^13) 2))
+
+  (define (rs2field instr)
+    (remainder instr two^5))
+
+  (define (rs1field instr)
+    (remainder (quotient instr two^14) two^5))
+
+  (define (rdfield instr)
+    (remainder (quotient instr two^25) two^5))
+
+  (define (imm13field instr)
+    (let ((x (remainder instr two^13)))
+      (if (not (zero? (quotient x two^12)))
+         (- x two^13)
+         x)))
+       
+  (define (imm22field instr)
+    (let ((x (remainder instr two^22)))
+      (if (not (zero? (quotient x two^21)))
+         (- x two^22)
+         x)))
+
+  (define (imm30field instr)
+    (let ((x (remainder instr two^30)))
+      (if (not (zero? (quotient x two^29)))
+         (- x two^30)
+         x)))
+
+  (define (fpop-field instr)
+    (remainder (quotient instr two^5) two^9))
+
+  (set! disassemble-instruction
+       (let ((class-table (vector class00 class01 class10 class11)))
+         (lambda (instr addr)
+           ((vector-ref class-table (quotient instr two^30)) addr instr))))
+
+  'disassemble-instruction)
+
+
+\f; Instruction printer
+;
+; It assumes that the first instruction comes from address 0, and prints
+; addresses (and relative addresses) based on that assumption.
+;
+; If the optional symbol native-names is supplied, then SPARC register
+; names is used, and millicode calls are not annotated with millicode names.
+
+(define (print-instructions ilist . rest)
+
+  (define port (current-output-port))
+  (define larceny-names? #t)
+
+  (define (print-ilist ilist a)
+    (if (null? ilist)
+       '()
+       (begin (display (format-instruction (car ilist) a larceny-names?)
+                       port)
+              (newline port)
+              (print-ilist (cdr ilist) (+ a 4)))))
+  
+  (do ((rest rest (cdr rest)))
+      ((null? rest))
+    (cond ((port? (car rest))
+          (set! port (car rest)))
+         ((eq? (car rest) 'native-names)
+          (set! larceny-names? #f))))
+  
+  (print-ilist ilist 0))
+
+(define format-instruction)                ; Defined below
+
+(define *format-instructions-pretty* #t)
+
+; Instruction formatter.
+
+(let ()
+
+  (define use-larceny-registers #t)
+
+  (define sparc-register-table 
+    (vector "%g0" "%g1" "%g2" "%g3" "%g4" "%g5" "%g6" "%g7"
+           "%o0" "%o1" "%o2" "%o3" "%o4" "%o5" "%o6" "%o7"
+           "%l0" "%l1" "%l2" "%l3" "%l4" "%l5" "%l6" "%l7"
+           "%i0" "%i1" "%i2" "%i3" "%i4" "%i5" "%i6"  "%i7"))
+
+  (define larceny-register-table
+    (make-vector 32 #f))
+
+  (define (larceny-register-name reg . rest)
+    (if (null? rest)
+       (or (and use-larceny-registers
+                (vector-ref larceny-register-table reg))
+           (vector-ref sparc-register-table reg))
+       (vector-set! larceny-register-table reg (car rest))))
+
+  (define millicode-procs '())
+
+  (define (float-register-name reg)
+    (string-append "%f" (number->string reg)))
+    
+  (define op car)
+  (define op1 cadr)
+  (define op2 caddr)
+  (define op3 cadddr)
+  (define tabstring (string #\tab))
+
+  (define (heximm n)
+    (if (>= n 16)
+       (string-append tabstring "! 0x" (number->string n 16))
+       ""))
+
+  (define (millicode-name offset . rest)
+    (if (null? rest)
+       (let ((probe (assv offset millicode-procs)))
+         (if probe
+             (cdr probe)
+             "[unknown]"))
+       (set! millicode-procs
+             (cons (cons offset (car rest)) millicode-procs))))
+
+  (define (millicode-call offset)
+    (string-append tabstring "! " (millicode-name offset)))
+
+  (define (plus/minus n)
+    (cond ((< n 0)
+          (string-append " - " (number->string (abs n))))
+         ((and (= n 0) *format-instructions-pretty*) "")
+         (else
+          (string-append " + " (number->string n)))))
+
+  (define (srcreg instr extractor)
+    (if (mnemonic:freg? (op instr))
+       (float-register-name (extractor instr))
+       (larceny-register-name (extractor instr))))
+       
+  (define (sethi instr)
+    (string-append (number->string (* (op1 instr) 1024)) ", "
+                  (larceny-register-name (op2 instr))
+                  (heximm (* (op1 instr) 1024))))
+
+  (define (rrr instr)
+    (string-append (larceny-register-name (op1 instr)) ", "
+                  (larceny-register-name (op2 instr)) ", "
+                  (larceny-register-name (op3 instr))))
+
+  (define (rir instr)
+    (string-append (larceny-register-name (op1 instr)) ", "
+                  (number->string (op2 instr)) ", "
+                  (larceny-register-name (op3 instr))
+                  (heximm (op2 instr))))
+
+  (define (sir instr)
+    (string-append (srcreg instr op3) ", [ "
+                  (larceny-register-name (op1 instr))
+                  (plus/minus (op2 instr)) " ]"))
+
+  (define (srr instr)
+    (string-append (srcreg instr op3) ", [ "
+                  (larceny-register-name (op1 instr)) "+"
+                  (larceny-register-name (op2 instr)) " ]"))
+      
+  (define (lir instr)
+    (string-append "[ " (larceny-register-name (op1 instr))
+                  (plus/minus (op2 instr)) " ], "
+                  (srcreg instr op3)))
+
+  (define (lrr instr)
+    (string-append "[ " (larceny-register-name (op1 instr)) "+"
+                  (larceny-register-name (op2 instr)) " ], "
+                  (srcreg instr op3)))
+
+  (define (bimm instr addr)
+    (string-append "#" (number->string (+ (op1 instr) addr))))
+
+  (define (jmpli instr)
+    (string-append (larceny-register-name (op1 instr)) 
+                  (plus/minus (op2 instr)) ", "
+                  (larceny-register-name (op3 instr))
+                  (if (and (= (op1 instr) $r.globals)
+                           use-larceny-registers)
+                      (millicode-call (op2 instr))
+                      (heximm (op2 instr)))))
+
+  (define (jmplr instr)
+    (string-append (larceny-register-name (op1 instr)) "+"
+                  (larceny-register-name (op2 instr)) ", "
+                  (larceny-register-name (op3 instr))))
+
+  (define (call instr addr)
+    (string-append "#" (number->string (+ (op1 instr) addr))))
+
+  (define (rd instr)
+    (string-append "%y, " (srcreg instr op3)))
+
+  (define (wr instr imm?)
+    (if imm?
+       (string-append (larceny-register-name (op1 instr)) ", "
+                      (number->string (op2 instr)) ", %y"
+                      (larceny-register-name (op3 instr)))
+       (string-append (larceny-register-name (op1 instr)) ", "
+                      (larceny-register-name (op2 instr)) ", %y")))
+
+  (define (fpop instr op2-used? op3-used?)
+    (string-append (float-register-name (op1 instr)) ", "
+                  (cond ((and op2-used? op3-used?)
+                         (string-append
+                          (float-register-name (op2 instr)) ", "
+                          (float-register-name (op3 instr))))
+                        (op2-used?
+                         (float-register-name (op2 instr)))
+                        (else
+                         (float-register-name (op3 instr))))))
+
+  ;; If we want to handle instruction aliases (clr, mov, etc) then
+  ;; the structure of this procedure must change, because as it is,
+  ;; the printing of the name is independent of the operand values.
+
+  (define (format-instr i a larceny-names?)
+    (set! use-larceny-registers larceny-names?)
+    (let ((m (car i)))
+      (string-append (number->string a)
+                    tabstring
+                    (symbol->string (mnemonic:name m))
+                    (if (mnemonic:annul? m) ",a" "")
+                    tabstring
+                    (cond ((mnemonic:store? m) 
+                           (if (mnemonic:immediate? m) (sir i) (srr i)))
+                          ((mnemonic:load? m)
+                           (if (mnemonic:immediate? m) (lir i) (lrr i)))
+                          ((mnemonic:fpop? m)
+                           (fpop i (mnemonic:op2? m) (mnemonic:op3? m)))
+                          ((mnemonic:branch? m) (bimm i a))
+                          ((mnemonic=? m 'sethi) (sethi i))
+                          ((mnemonic=? m 'nop) "")
+                          ((mnemonic=? m 'jmpl)
+                           (if (mnemonic:immediate? m) (jmpli i) (jmplr i)))
+                          ((mnemonic=? m 'call) (call i a))
+                          ((mnemonic=? m 'rd) (rd i))
+                          ((mnemonic=? m 'wr) (wr i (mnemonic:immediate? m)))
+                          ((mnemonic:immediate? m) (rir i))
+                          (else (rrr i))))))
+
+  (larceny-register-name $r.tmp0 "%tmp0")
+  (larceny-register-name $r.result "%result")
+  (larceny-register-name $r.argreg2 "%argreg2")
+  (larceny-register-name $r.argreg3 "%argreg3")
+  (larceny-register-name $r.tmp1 "%tmp1")
+  (larceny-register-name $r.tmp2 "%tmp2")
+  (larceny-register-name $r.reg0 "%r0")
+  (larceny-register-name $r.reg1 "%r1")
+  (larceny-register-name $r.reg2 "%r2")
+  (larceny-register-name $r.reg3 "%r3")
+  (larceny-register-name $r.reg4 "%r4")
+  (larceny-register-name $r.reg5 "%r5")
+  (larceny-register-name $r.reg6 "%r6")
+  (larceny-register-name $r.reg7 "%r7")
+  (larceny-register-name $r.e-top "%etop")
+  (larceny-register-name $r.e-limit "%elim")
+  (larceny-register-name $r.timer "%timer")
+  (larceny-register-name $r.millicode "%millicode")
+  (larceny-register-name $r.globals "%globals")
+  (larceny-register-name $r.stkp "%stkp")       ; note: after elim
+
+  (millicode-name $m.alloc "alloc")
+  (millicode-name $m.alloci "alloci")
+  (millicode-name $m.gc "gc")
+  (millicode-name $m.addtrans "addtrans")
+  (millicode-name $m.stkoflow "stkoflow")
+  (millicode-name $m.stkuflow "stkuflow")
+  (millicode-name $m.creg "creg")
+  (millicode-name $m.creg-set! "creg-set!")
+  (millicode-name $m.add "+")
+  (millicode-name $m.subtract "- (binary)")
+  (millicode-name $m.multiply "*")
+  (millicode-name $m.quotient "quotient")
+  (millicode-name $m.remainder "remainder")
+  (millicode-name $m.divide "/")
+  (millicode-name $m.modulo "modulo")
+  (millicode-name $m.negate "- (unary)")
+  (millicode-name $m.numeq "=")
+  (millicode-name $m.numlt "<")
+  (millicode-name $m.numle "<=")
+  (millicode-name $m.numgt ">")
+  (millicode-name $m.numge ">=")
+  (millicode-name $m.zerop "zero?")
+  (millicode-name $m.complexp "complex?")
+  (millicode-name $m.realp "real?")
+  (millicode-name $m.rationalp "rational?")
+  (millicode-name $m.integerp "integer?")
+  (millicode-name $m.exactp "exact?")
+  (millicode-name $m.inexactp "inexact?")
+  (millicode-name $m.exact->inexact "exact->inexact")
+  (millicode-name $m.inexact->exact "inexact->exact")
+  (millicode-name $m.make-rectangular "make-rectangular")
+  (millicode-name $m.real-part "real-part")
+  (millicode-name $m.imag-part "imag-part")
+  (millicode-name $m.sqrt "sqrt")
+  (millicode-name $m.round "round")
+  (millicode-name $m.truncate "truncate")
+  (millicode-name $m.apply "apply")
+  (millicode-name $m.varargs "varargs")
+  (millicode-name $m.typetag "typetag")
+  (millicode-name $m.typetag-set "typetag-set")
+  (millicode-name $m.break "break")
+  (millicode-name $m.eqv "eqv?")
+  (millicode-name $m.partial-list->vector "partial-list->vector")
+  (millicode-name $m.timer-exception "timer-exception")
+  (millicode-name $m.exception "exception")
+  (millicode-name $m.singlestep "singlestep")
+  (millicode-name $m.syscall "syscall")
+  (millicode-name $m.bvlcmp "bvlcmp")
+  (millicode-name $m.enable-interrupts "enable-interrupts")
+  (millicode-name $m.disable-interrupts "disable-interrupts")
+  (millicode-name $m.alloc-bv "alloc-bv")
+  (millicode-name $m.global-ex "global-exception")
+  (millicode-name $m.invoke-ex "invoke-exception")
+  (millicode-name $m.global-invoke-ex "global-invoke-exception")
+  (millicode-name $m.argc-ex "argc-exception")
+
+  (set! format-instruction format-instr)
+  'format-instruction)
+
+
+; eof
+
+
+; ----------------------------------------------------------------------
+
+(define (twobit-benchmark type . rest)
+  (let ((k (if (null? rest) 1 (car rest))))
+    (run-benchmark 
+     "twobit"
+     k
+     (lambda () 
+       (case type
+         ((long) 
+          (compiler-switches 'fast-safe)
+          (benchmark-block-mode #f)
+          (compile-file "benchmarks/twobit-input-long.sch"))
+         ((short) 
+          (compiler-switches 'fast-safe)
+          (benchmark-block-mode #t)
+          (compile-file "benchmarks/twobit-input-short.sch"))
+         (else
+          (error "Benchmark type must be `long' or `short': " type))))
+     (lambda (result)
+       #t))))
+
+; eof