;; Compiler driver routines
;; (c) 1996-1997 Sebastian Good
;; (c) 1997-2011 PLT Scheme Inc
;;
;; Scheme->C compilation Overview
;; ------------------------------
;;
;; Compilation is performed in a number of phases,
;;  each of which is implemented in its own unit:
;;
;;   1) Reading/parsing - Zodiac collection
;;   2) Prephase - "prephase.rkt"
;;   3) A-normalization - "anorm.rkt"
;;   4) Known-value analysis - "known.rkt"
;;   5) Lexical analysis and inlining - "analyze.rkt"
;;   6) Static procedure lifting - "lift.rkt"
;;   7) Static procedure lifting after LWCC - "lift.rkt" (optional)
;;   8) Closure conversion - "closure.rkt"
;;   9) Closure vehicle assignment - "vehicle.rkt"
;;  10) Representation choosing - "rep.rkt"
;;  11) Scheme to virtual machine translation - "vmphase.rkt"
;;  12) Optimizations on VM code - "vmopt.rkt"
;;  13) VM to C translation - "vm2c.rkt"
;;
;; For more information about a phase, see the file
;;  implementing that phase.
;;
;; All steps up to "vmphase.rkt" work on a Scheme program, representated
;;  as a zodiac AST. The AST produced by zodiac is destructively
;;  modified by each phase (usually); mzc-specific information is
;;  stored in the AST as ``annotations''. At the implementation file
;;  for each phase, the annotations installed or changed by the phase
;;  are listed.
;;
;; All nodes in the AST must be unique, except for nodes representing
;;  constant values. Don't even reuse varref or binding nodes within
;;  an AST.
;;
;; C code is compiled and linked via procedures provided by the
;;  dynext collection.
;;
;; In this implementation, `var' is used for variable names in
;;  confusing and inconsistent ways. There are two different
;;  AST entities that could be called "var":
;;     1) binding instances of variables, e.g., the formal
;;        arguments of a lambda; these are always called
;;        `bindings' in Zodiac terminology
;;     2) bound instances of variables, e.g., a free variable 
;;        in a sub-expression; these are always called
;;        `varrefs' in Zodiac terminology.
;;  Almost all information about a variable is stored with a
;;   zodiac:binding AST node, and very little information is
;;   stored with a zodiac:varref AST node.
;;  To make matters worse, the name `binding' is overloaded.
;;    `zodiac:binding' is the name of a Zodiac structure
;;    type, and `binding' is also the name of the structure
;;    type for annotations attached to zodiac:binding objects.
;;  If you create a new lexical binding, note that the procedure
;;    zodiac:binding->lexical-varref will create varrefs to 
;;    the binding.

(module driver mzscheme
  (require mzlib/unit
           mzlib/list
           mzlib/file
           mzlib/port
           mzlib/etc
           mzlib/pretty
           (prefix src2src: "../src2src.rkt"))

  (require syntax/zodiac-sig
           syntax/toplevel
           dynext/compile-sig
           dynext/link-sig
           dynext/file-sig
           setup/dirs
           (only scheme/base
                 define-namespace-anchor
                 namespace-anchor->empty-namespace))

  (require "../sig.rkt"
           "sig.rkt"
           "../to-core.rkt"
           "../xform.rkt")

  (provide driver@)

  (define-namespace-anchor anchor)

  (define-unit driver@
      (import (prefix compiler:option: compiler:option^)
	      compiler:library^
	      compiler:cstructs^
	      (prefix zodiac: zodiac^)
	      compiler:zlayer^
	      compiler:prephase^
	      compiler:anorm^
	      compiler:known^
	      compiler:analyze^
	      compiler:const^
	      compiler:lift^
	      compiler:closure^
	      compiler:vehicle^
	      compiler:rep^
	      compiler:vmstructs^
	      compiler:vmphase^
	      compiler:vmopt^
	      compiler:vm2c^
	      compiler:top-level^
	      dynext:compile^
	      dynext:link^
	      dynext:file^)
      (export (rename compiler:driver^
                      [compile-extension* compile-extension]))
      
      (define debug:file "dump.txt")
      (define debug:port #f)
      (define (debug:get-port) debug:port)
      (define debug
	(lambda x
	  (when (and (compiler:option:debug) debug:port)
	    (apply fprintf (cons debug:port x))
	    (flush-output debug:port))))
  
      ;;----------------------------------------------------------------------------
      ;; FILE PROCESSING FUNCTIONS
      ;;
      
      ;; takes an input-name from the compile command and returns many values:
      ;; 1) an input path
      ;; 2) a C output path
      ;; 3) a constant pool output path
      ;; 4) an obj output path
      ;; 5) a dll output path
      ;; 6) a scheme_setup suffix
      (define s:process-filenames
	(lambda (input-name dest-dir from-c? 3m? tmp-c? tmp-c3m? tmp-o?)
	  (let-values ([(basedir file dir?) (split-path input-name)])
	    (let* ([dest-dir (if (eq? dest-dir 'auto)
				 (let* ([d0 (build-path (if (eq? basedir 'relative)
							    'same
							    basedir)
							"compiled"
							"native"
							(system-library-subpath #f))]
					[d (if 3m?
					       (build-path d0 "3m")
					       d0)])
				   (unless (directory-exists? d)
				     (make-directory* d))
				   d)
				 dest-dir)]
		   [path-prefix (lambda (a b)
				  (bytes->path (bytes-append a (path->bytes b))))]
		   [path-suffix (lambda (b a)
				  (bytes->path (bytes-append (path->bytes b) a)))]
		   [sbase (extract-base-filename/ss file (if from-c? #f 'mzc))]
		   [cbase (extract-base-filename/c file (if from-c? 'mzc #f))]
		   [base (if sbase 
                             (path-replace-suffix (path-add-suffix input-name #".x") #"")
                             cbase)]
		   [c-dir (if tmp-c?
			      (find-system-path 'temp-dir)
			      dest-dir)]
		   [c3m-dir (if tmp-c3m?
				(find-system-path 'temp-dir)
				dest-dir)]
		   [c-prefix (if tmp-c?
				 (lambda (s) (path-prefix #"mzcTMP" s))
				 values)]
		   [c3m-prefix (if tmp-c3m?
				   (lambda (s) (path-prefix #"mzcTMP" s))
				   values)]
		   [o-dir (if tmp-o?
			      (find-system-path 'temp-dir)
			      dest-dir)]
		   [o-prefix (if tmp-o?
				 (lambda (s) (path-prefix #"mzcTMP" s))
				 values)])
	      (unless base
		(error 'mzc "not a Scheme or C file: ~a" input-name))
	      (values (if sbase 
			  input-name
			  #f)
		      (if cbase 
			  input-name 
			  (build-path c-dir (c-prefix (append-c-suffix base))))
		      (and 3m?
			   (build-path c3m-dir (c3m-prefix (append-c-suffix (path-suffix base #"3m")))))
		      (build-path o-dir (o-prefix (append-constant-pool-suffix base)))
		      (build-path o-dir (o-prefix (append-object-suffix base)))
		      (build-path dest-dir (append-extension-suffix base))
		      (string-append (compiler:clean-string (compiler:option:setup-prefix)) 
				     "_" 
				     (compiler:clean-string (path->string base))))))))
      
      (define elaboration-exn-handler
	(lambda (exn)
	  (compiler:fatal-error 
	   #f
	   (format "Error during elaboration: ~a" 
		   (if (exn? exn)
		       (exn-message exn)
		       exn)))
	  (raise exn)))
      
      (define prefix-exn-handler
	(lambda (exn)
	  (compiler:fatal-error 
	   #f
	   (format "Error during prefix loading: ~a" 
		   (if (exn? exn)
		       (exn-message exn)
		       exn)))
	  (raise exn)))
      
      (define top-level-exn-handler
	(lambda (exn)
	  (set! compiler:messages (reverse compiler:messages))
	  (compiler:report-messages! #t)
	  (raise-user-error "compile failed")))
  
      (define s:expand-top-level-expressions!
	(lambda (input-directory reader verbose?)
	  (when verbose? (printf "\n Reading... ") (flush-output))
	  ;; During reads, errors are truly fatal
	  (let ([exprs (let ([failed? #f])
			 (let loop ([n 1])
			   (let ([sexp (reader)])
			     (if (eof-object? sexp)
				 null
				 (begin 
				   (when (compiler:option:debug)
				     (debug "~a[~a.~a]_"
					    n
					    (syntax-line sexp)
					    (syntax-column sexp)))
				   (cons sexp (loop (+ n 1))))))))])
	    (unless (null? compiler:messages) (when (compiler:option:verbose) (newline)))
	    (compiler:report-messages! #t)
	    (when verbose? (printf " expanding...\n"))
	    (parameterize ([current-load-relative-directory input-directory])
	      (map (lambda (expr)
		     (let ([expanded ((if has-prefix?
					  expand-top-level-with-compile-time-evals
					  expand) 
				      expr)])
		       (values ; use to be zodiac:syntax->zodiac here
			(let ([p (src2src:optimize expanded #t)])
			  '(with-output-to-file "/tmp/l.rkt"
			     (lambda () (pretty-print (syntax-object->datum p)))
			     'replace)
			  (let ([opt-expanded (expand p)])
			    ;; (pretty-print (syntax-object->datum opt-expanded))
			    opt-expanded)))))
		   exprs)))))

      (define elaborate-namespace 
        (let ([ns (make-namespace)])
          (namespace-attach-module (namespace-anchor->empty-namespace anchor)
                                   'scheme/base
                                   ns)
          ns))

      (define has-prefix? #f)

      (define (eval-compile-prefix prefix)
	(set! has-prefix? (and prefix #t))
	(with-handlers ([void top-level-exn-handler])
	  (with-handlers ([void prefix-exn-handler])
	    (parameterize ([current-namespace elaborate-namespace])
	      (eval (or prefix
			;; Need MzScheme and cffi:
			'(begin
			   (require compiler/cffi)
			   (require-for-syntax mzscheme))))))))

      ;;----------------------------------------------------------------------
      ;; Misc utils      

      (define (simple-constant? s)
	(or (identifier? s)
	    (number? (syntax-e s))
	    (empty? (syntax-e s))
	    (memq (syntax-e s) '(#t #f))))

      ;; takes a list of a-normalized expressions and analyzes them
      ;; returns the analyzed code, a list of local variable lists, 
      ;; used variable lists, and captured variable lists
      (define s:analyze-source-list
	(lambda (source)
	  (let loop ([sexps source] [source-acc null] 
				    [locals-acc null] [globals-acc null] [used-acc null] [captured-acc null]
				    [children-acc null]
				    [max-arity 0])
	    (if (null? sexps)
		(values (reverse source-acc)
			(map (lambda (loc glob used cap children)
			       (let ([c (make-code empty-set
						   loc
						   glob
						   used
						   cap
						   #f
						   #f
						   children)])
				 (for-each (lambda (child) (set-code-parent! child c)) children)
				 c))
			     (reverse locals-acc)
			     (reverse globals-acc)
			     (reverse used-acc)
			     (reverse captured-acc)
			     (reverse children-acc))
			max-arity)
		(begin

		  ;; (printf "~a\n" (syntax-line (zodiac:zodiac-stx (car sexps))))

		  (let-values ([(exp free-vars local-vars global-vars used-vars captured-vars
				     children new-max-arity multi)
				(analyze-expression! (car sexps) empty-set null (null? (cdr sexps)))])

		    
		    (let ([sc-max-arity 0])

		      (loop (cdr sexps) 
			    (cons exp source-acc) 
			    (cons local-vars locals-acc)
			    (cons global-vars globals-acc)
			    (cons used-vars used-acc)
			    (cons captured-vars captured-acc)
			    (cons children children-acc)
			    (max max-arity new-max-arity sc-max-arity)))))))))

      ;; Lift static procedures
      (define s:lift
	(lambda ()
	  (compiler:init-lifted-lambda-list!)
	  (compiler:init-once-closure-lists!)

	  (let ([l (map lift-lambdas! (block-source s:file-block) (block-codes s:file-block))]
		[reset-globals (lambda (code globals)
				 (set-code-global-vars! code globals)
				 code)])
	    
	    ;; Splice lifted lambda definitions into the program in the right
	    ;; place: statics after true constants, and per-load statics after
	    ;; per-load constants.
	    (let loop ([n number-of-true-constants]
		       [l l][c (block-codes s:file-block)]
		       [l-acc null][c-acc null])
	      (if (zero? n)
		  (let loop ([n number-of-per-load-constants]
			     [l l][c c]
			     [pll-acc null][plc-acc null])
		    (if (zero? n)
			(let ([lifted-lambdas (compiler:get-lifted-lambdas)]
			      [once-closures (compiler:get-once-closures-list)])

			  (let ([naya (append lifted-lambdas once-closures)])
			    (set-block-magics! s:file-block (append (map (lambda (x) #f) naya)
								    (block-magics s:file-block)))
			    (set-block-bytecodes! s:file-block (append (map (lambda (x) #f) naya)
								       (block-bytecodes s:file-block))))
	    
			  (set-block-source! 
			   s:file-block
			   (append (reverse l-acc)
				   lifted-lambdas
				   (reverse pll-acc)
				   once-closures
				   (map car l)))
			  (set-block-codes!
			   s:file-block 
			   (append (reverse c-acc) 
				   (map
				    (lambda (ll)
				      (make-code empty-set
						 empty-set
						 empty-set ; no globals
						 empty-set
						 empty-set
						 #f #f
						 (list 
						  (get-annotation 
						   (zodiac:define-values-form-val ll)))))
				    (compiler:get-lifted-lambdas))
				   (reverse plc-acc)
				   (map (lambda (ll globs) 
					  (make-code empty-set
						     empty-set
						     globs
						     empty-set
						     empty-set
						     #f #f
						     (list
						      (get-annotation 
						       (zodiac:define-values-form-val 
							ll)))))
					(compiler:get-once-closures-list)
					(compiler:get-once-closures-globals-list))
				   (map reset-globals c (map cdr l)))))
			(loop (sub1 n) (cdr l) (cdr c) 
			      (cons (caar l) pll-acc) (cons (reset-globals (car c) (cdar l)) plc-acc))))
		  (loop (sub1 n) (cdr l) (cdr c) 
			(cons (caar l) l-acc) (cons (reset-globals (car c) (cdar l)) c-acc)))))
	  
	  ;; Lifted lambdas are true constants:
	  (set! number-of-true-constants (+ number-of-true-constants
					    (length (compiler:get-lifted-lambdas))))))

      (define s:append-block-sources!
	(lambda (file-block l)
	  (set-block-codes!
	   file-block
	   (append
	    (map
	     (lambda (glob)
	       (make-code empty-set
			  empty-set
			  empty-set
			  empty-set
			  empty-set
			  #f #f null))
	     l)
	    (block-codes file-block)))
	  (set-block-source! 
	   file-block
	   (append l (block-source file-block)))
	  (set-block-bytecodes! 
	   file-block
	   (append (map (lambda (x) #f) l)
		   (block-bytecodes file-block)))
	  (set-block-magics! 
	   file-block
	   (append (map (lambda (x) #f) l)
		   (block-magics file-block)))))

      (define (open-input-scheme-file path)
	(let ([p (let ([open (with-handlers ([exn:fail? (lambda (x) #f)])
			       (dynamic-require 'mred 'open-input-graphical-file))])
		   (if open
		       ;; Handles WXME files:
		       (open path)
		       ;; Check for WXME and give a nice error message:
		       (let ([p (open-input-file path)])
			 (when (regexp-match-peek "^WXME01[0-9][0-9] ## " p)
			   (close-input-port p)
			   (error 'compile-file
				  "file appears to have graphical syntax (try gmzc): ~a"
				  path))
			 p)))])
	  p))

      ;;-------------------------------------------------------------------------------
      ;; ERROR/WARNING REPORTING/HANDLING ROUTINES
      ;;
      (define compiler:messages null)
      (define compiler:make-message
	(lambda (constructor)
	  (lambda (ast message)
	    (set! compiler:messages (cons (constructor ast message)
					  compiler:messages)))))
      (define compiler:error (compiler:make-message make-compiler:fatal-error-msg))
      (define compiler:fatal-error compiler:error)
      (define compiler:internal-error
	(case-lambda
	 [(ast message)
	  (set! compiler:messages 
		(reverse (cons (make-compiler:internal-error-msg ast message)
				compiler:messages)))
	  (compiler:report-messages! #t)]
	 [(ast fmt . args)
	  (compiler:internal-error ast (apply format fmt args))]))
      
      (define compiler:warning (compiler:make-message make-compiler:warning-msg))

      (define compiler:report-messages!
	(lambda (stop-on-errors?)
	  (let ([error-count 0]
		[fatal-error-count 0]
		[msgs (reverse compiler:messages)])
	    (set! compiler:messages null)
	    (for-each (lambda (message)
			(when (compiler:error-msg? message) 
			  (set! error-count (add1 error-count)))
			(when (or (compiler:fatal-error-msg? message) 
				  (compiler:internal-error-msg? message))
			  (set! fatal-error-count (add1 fatal-error-count)))

			(let* ([ast (compiler:message-ast message)]
			       [string (compiler:message-message message)])
			  (zodiac:print-start! (current-output-port) ast)
			  (printf 
			   "~a: ~a\n"
			   (cond
			    [(compiler:error-msg? message) "Error"]
			    [(compiler:warning-msg? message) "Warning"]
			    [(compiler:fatal-error-msg? message) "Error"]
			    [(compiler:internal-error-msg? message) "INTERNAL ERROR"]
			    [else (error 'report-messages "internal error")])
			   string)
			  (when (compiler:internal-error-msg? message)
			    (printf 
			     (string-append
			      " please report the bug using DrRacket\n"
			      "  or http://bugs.racket-lang.org/\n"
			      "  and include a transcript in verbose mode\n")))))
		      
		      msgs)
	    (when (and stop-on-errors?
		       (or (positive? error-count)
			   (positive? fatal-error-count)))
	      (raise-user-error "Errors encountered.  Compilation aborted.")))))

      (define total-cpu-time 0)
      (define total-real-time 0)
      (define verbose-time
	(lambda (thunk)
	  (let-values ([(vals cpu real gc) (time-apply thunk null)])
	    (set! total-cpu-time (+ total-cpu-time cpu))
	    (set! total-real-time (+ total-real-time real))
	    (when (compiler:option:verbose)
	      (printf "      [cpu: ~ams, real: ~ams, gc: ~ams]\n" cpu real gc))
	    (apply values vals))))

      ;;-----------------------------------------------------------------------------
      ;; File-level Block information
      
      (define s:file-block (make-empty-block))
      (define s:max-arity 0) ; compilation-wide max
      (define s:register-max-arity!
	(lambda (n) (set! s:max-arity (max s:max-arity n))))

      (define number-of-true-constants 0)
      (define number-of-per-load-constants 0)
      
      (define s:unit-list null) ; list of units in the code
      
      (define compiler:setup-suffix "")

      (define (get-s:file-block) s:file-block)
      (define (compiler:get-setup-suffix) compiler:setup-suffix)

      (define c-declares null)
      (define (register-c-declaration str)
	(set! c-declares (cons str c-declares)))

      (define c-lambdas null)
      (define (register-c-lambda-function name body)
	(set! c-lambdas (cons (cons name body) c-lambdas)))

      ;;-----------------------------------------------------------------------------
      ;; THE MAIN DRIVING ROUTINE

      (define (compile-extension* input-name dest-directory)
	(s:compile #f #f #f input-name dest-directory))
      (define (compile-extension-to-c input-name dest-directory)
	(s:compile #t #f #f input-name dest-directory))
      (define (compile-c-extension input-name dest-directory)
	(s:compile #f #f #t input-name dest-directory))

      (define (compile-extension-part input-name dest-directory)
	(s:compile #f #t #f input-name dest-directory))
      (define (compile-extension-part-to-c input-name dest-directory)
	(s:compile #t #t #f input-name dest-directory))
      (define (compile-c-extension-part input-name dest-directory)
	(s:compile #f #t #t input-name dest-directory))

      (define compiler:multi-o-constant-pool (make-parameter #f))

      (define compiler:module-decl-name #f)

      (define s:compile
	(lambda (c-only? multi-o? from-c? input-name dest-directory)
	  (define input-directory 
	    (let-values ([(base file dir?)
			  (split-path (path->complete-path input-name))])
	      base))
	  (compiler:multi-o-constant-pool multi-o?)
	  (set! s:file-block (make-empty-block))
	  (set! s:max-arity 0)
	  (set! total-cpu-time 0)
	  (set! total-real-time 0)
	  (random-seed (compiler:option:seed))
	  (set! compiler:messages null)
	  (set! c-declares null)
	  (set! c-lambdas null)
	  (const:init-tables!)
	  (compiler:init-closure-lists!)
	  ; process the input string - try to open the input file
	  (let-values ([(input-path c-output-path c3m-output-path
				    constant-pool-output-path obj-output-path dll-output-path 
				    setup-suffix)
			(s:process-filenames input-name dest-directory from-c?
					     (compiler:option:3m)
					     (and (compiler:option:clean-intermediate-files)
						  (or (not c-only?)
						      (compiler:option:3m)))
					     (and (compiler:option:clean-intermediate-files)
						  (not c-only?))
					     (and (compiler:option:clean-intermediate-files)
						  (not multi-o?)))])
	    (unless (or (not input-path) (file-exists? input-path))
	      (error 's:compile "could not open ~a for input" input-path))
	    (set! compiler:setup-suffix
		  (if multi-o?
		      setup-suffix
		      ""))
	    
	    (for-each (lambda (path)
			(when (file-exists? path) (delete-file path)))
		      (list (if input-path c-output-path obj-output-path) 
			    (if input-path constant-pool-output-path obj-output-path) 
			    (or c3m-output-path obj-output-path)
			    obj-output-path dll-output-path))
	
	    (when (compiler:option:debug)
	      (when (file-exists? debug:file) (delete-file debug:file))
	      (set! debug:port (open-output-file debug:file 'text)))
	
	    (when input-path 
	      (parameterize ([main-source-file input-path])
		(let ([input-port (open-input-scheme-file input-path)])
		  (port-count-lines! input-port)

		  ;;-----------------------------------------------------------------------
		  ;; read all top-level s-expressions
		  ;;
	      
                  (when (compiler:option:somewhat-verbose)
                    (printf "\"~a\": " input-path)
                    (unless (compiler:option:verbose) (newline)))
		  (let ([read-thunk
			 (lambda ()
			   (with-handlers ([void top-level-exn-handler])
			     (with-handlers ([void elaboration-exn-handler])
			       (parameterize ([current-namespace elaborate-namespace]
					      [compiler:escape-on-error #t])
				 (set-block-source! 
				  s:file-block
				  (s:expand-top-level-expressions! 
				   input-directory
				   (lambda ()
                                     (parameterize ([read-accept-reader #t])
                                       (read-syntax (path->complete-path input-path) input-port)))
				   (compiler:option:verbose)))))))])
		    (verbose-time read-thunk)
		    (close-input-port input-port)
		    (set! input-port #f)
		
		    (compiler:report-messages! #t))
	
		  ;; (print-struct #t) (map (lambda (ast) (pretty-print ast)) (block-source s:file-block))
		  
		  ;; (map (lambda(ast)(pretty-print (zodiac->sexp/annotate ast))) (block-source s:file-block))
		  
		  ;; (print-graph #t) (display (car (block-source s:file-block))) (newline)

		  ;;-----------------------------------------------------------------------
		  ;; record module name, if a single declaration
		  ;;

		  (set-single-module-mode! #f)
		  (when (= 1 (length (block-source s:file-block)))
		    (syntax-case (car (block-source s:file-block)) (module)
		      [(module name . _)
		       (begin
			 (set-single-module-mode! #t)
			 (set! compiler:module-decl-name (syntax-e #'name)))]
		      [_else (void)]))
		  
		  ;;-----------------------------------------------------------------------
		  ;; ensure that no `module', `require', or `require-for-syntax'
		  ;; expression is inside a `begin'

		  (letrec ([needs-split?
			    (lambda (stx saw-begin?)
			      (syntax-case stx (begin module require require-for-syntax)
				[(module . _) saw-begin?]
				[(require . _) saw-begin?]
				[(require-for-syntax . _) saw-begin?]
				[(begin . e)
				 (ormap (lambda (x) (needs-split? x #t))
					(syntax->list #'e))]
				[_else #f]))]
			   [split
			    (lambda (stx)
			      (syntax-case stx (begin)
				[(begin . e)
				 (apply append (map split (syntax->list #'e)))]
				[_else (list stx)]))])
		    (set-block-source! 
		     s:file-block
		     (apply
		      append
		      (map (lambda (e)
			     (if (needs-split? e #f)
				 (split e)
				 (list e)))
			   (block-source s:file-block)))))
		  
		  ;;-----------------------------------------------------------------------
		  ;; Extract stateless, phaseless core, leaving the rest of bytecode
		  ;;
		  
		  (when (compiler:option:verbose) (printf " extracting core expressions\n"))
		  (when (compiler:option:debug) (debug " = CORE =\n"))

		  (let ([core-thunk
			 (lambda ()
			   (parameterize ([current-namespace elaborate-namespace]
					  [current-load-relative-directory input-directory]
					  [compile-enforce-module-constants #f])
                             (let ([sources+bytecodes+magics
				    (map (lambda (src)
					   (let-values ([(src bytecode magic-sym)
							 (top-level-to-core src 
									    #`'#,zodiac:global-lookup-id
									    #`'#,zodiac:global-assign-id
									    #`'#,zodiac:safe-vector-ref-id
									    #`'#,zodiac:global-prepare-id
									    simple-constant?
									    '(mzc-cffi))])
					     (list (zodiac:syntax->zodiac src) 
						   bytecode magic-sym)))
					 (block-source s:file-block))])
			       (set-block-source! s:file-block (map car sources+bytecodes+magics))
                               (set-block-bytecodes! s:file-block 
						     (map compile
							  (map cadr sources+bytecodes+magics)))
			       (set-block-magics! s:file-block (map caddr sources+bytecodes+magics)))))])
		    (verbose-time core-thunk))

		  ;;-----------------------------------------------------------------------
		  ;; Run a preprocessing phase on the input
		  ;;
	      
		  (when (compiler:option:verbose) (printf " pre-processing and scanning for errors\n"))
		  (when (compiler:option:debug) (debug " = PREPHASE =\n"))
	      
		  (let ([prephase-thunk 
			 (lambda () 
			   (set-block-source! 
			    s:file-block
			    (let loop ([source (block-source s:file-block)]
				       [errors compiler:messages])
			      (if (null? source)
				  source
				  (let ([ast (prephase! (car source) #f (null? (cdr source)) #f)])
				    (if (eq? errors compiler:messages)
					
					;; no errors here
					(cons ast (loop (cdr source) errors))
					
					;; error, drop this one
					(loop (cdr source) compiler:messages)))))))])
		    (verbose-time prephase-thunk))
		  (compiler:report-messages! (not (compiler:option:test)))
		  (when (compiler:option:test)
		    (printf "skipping over top-level expressions with errors...\n"))
	      
	      ; (map (lambda (ast) (pretty-print (zodiac->sexp/annotate ast))) (block-source s:file-block))
	      
	      ;;-----------------------------------------------------------------------
	      ;; A-normalize input
	      ;;
	      
	      (when (compiler:option:verbose) (printf " transforming to a-normal form\n"))
	      (when (compiler:option:debug) (debug " = ANORM =\n"))
	      
	      (let ([anorm-thunk
		     (lambda ()
		       (set-block-source! 
			s:file-block 
			(map (lambda (s) (a-normalize s identity)) 
			     (block-source s:file-block))))])
		(verbose-time anorm-thunk))
	      
	      ; (map (lambda (ast) (pretty-print (zodiac->sexp/annotate ast))) (block-source s:file-block))
	      
	      ;;-----------------------------------------------------------------------
	      ;; known-value analysis
	      ;;

	      (when (compiler:option:verbose) 
		(printf " determining known bindings\n"))
	      (when (compiler:option:debug)
		(debug " = KNOWN =\n"))

	      ; analyze top level expressions
	      (let ([known-thunk
		     (lambda ()
		       (set-block-source! 
			s:file-block 
			(map (lambda (s) (analyze-knowns! s)) 
			     (block-source s:file-block))))])
		(verbose-time known-thunk))

	      (compiler:report-messages! #t)
	      
	      ; (map (lambda (ast) (pretty-print (zodiac->sexp/annotate ast))) (block-source s:file-block))

	      ;;-----------------------------------------------------------------------
	      ;; B-form transformation and analysis
	      ;;

	      (when (compiler:option:verbose) 
		(printf " transforming to b-normal form, analyzing, and inlining\n"))
	      (when (compiler:option:debug)
		(debug " = ANALYZE =\n"))

	      ; analyze top level expressions, cataloguing local variables
	      (compiler:init-define-lists!)
	      (let ([bnorm-thunk
		     (lambda ()
		       (let-values ([(new-source new-codes max-arity)
				     (s:analyze-source-list 
				      (block-source s:file-block))])
			 (set-block-source! s:file-block new-source)
			 (set-block-codes! s:file-block new-codes)
			 (block:register-max-arity! s:file-block max-arity)
			 (s:register-max-arity! max-arity))

		       ;; take constant construction code and place it in front of the 
		       ;; previously generated code. True constants first.
		       (set! number-of-true-constants (length (compiler:get-define-list)))
		       (set! number-of-per-load-constants (+ (length (compiler:get-per-load-define-list))))
		       (s:append-block-sources! s:file-block 
						(append
						 (compiler:get-define-list)
						 (compiler:get-per-load-define-list))))])
		(verbose-time bnorm-thunk))
	      (compiler:report-messages! #t)

	      ; (map (lambda (ast) (pretty-print (zodiac->sexp/annotate ast))) (block-source s:file-block))

	      ;;-----------------------------------------------------------------------
	      ;; Lift static procedures
	      ;;

	      (when (compiler:option:verbose) 
		(printf " finding static procedures\n"))
	      (when (compiler:option:debug)
		(debug " = LIFT =\n"))

	      (let ([lift-thunk s:lift])
		(verbose-time lift-thunk))
	      (compiler:report-messages! #t)
	      
	      ; (map (lambda (ast) (pretty-print (zodiac->sexp/annotate ast))) (block-source s:file-block))
	      
	      ;;-----------------------------------------------------------------------
	      ;; Closure conversion and explicit control transformation
	      ;;
	      
	      (when (compiler:option:verbose) 
		(printf " closure conversion and explicit control transformation\n"))
	      
	      (let ([closure-thunk
		     (lambda ()
		       (set-block-source! 
			s:file-block
			(map closure-expression! (block-source s:file-block))))])
		(verbose-time closure-thunk))
	      
	      ;;-----------------------------------------------------------------------
	      ;; Vehicle assignment
	      ;;
	      ;; Set export list offset for units at the same time
	      ;;
	      
	      (when (compiler:option:verbose)
		(printf " closure->vehicle mapping\n"))
	      
	      (when (eq? (compiler:option:vehicles) 'vehicles:automatic)
		(for-each 
		 (lambda (L)
		   (when (zodiac:case-lambda-form? L)
		     (map (lambda (body)
			    (relate-lambdas! L body))
			  (zodiac:case-lambda-form-bodies L))))
		 (compiler:get-closure-list)))
	      
	      (when (eq? (compiler:option:vehicles) 'vehicles:units)
		(compiler:fatal-error 
		 #f 
		 "unit-wise vehicle mapping not currently supported\n"))
	      (let ([vehicle-thunk
		     (lambda ()
		       (compiler:init-vehicles!)
		       (compiler:reset-label-number!)
		       (choose-vehicles!))])
		(verbose-time vehicle-thunk))

	      ;;-----------------------------------------------------------------------
	      ;; Representation Choosing 
	      ;;    From this stage, we have to work with separate code bodies, as well
	      ;;    as the list of top-level expressions.
	      
	      (when (compiler:option:verbose) 
		(printf 
		 " choosing data representations\n"))
	      
	      (let ([rep-thunk
		     (lambda ()
		       (compiler:init-structs!)
		       ; top-level
		       (map
			(lambda (c)
			  (choose-binding-representations! 
			   (code-local-vars c)
			   (code-global-vars c)
			   (code-used-vars c)
			   (code-captured-vars c)))
			(block-codes s:file-block))
		       ; code-bodies
		       (for-each (lambda (L)
				   (let* ([code (get-annotation L)]
					  [locals (code-local-vars code)]
					  [globals (code-global-vars code)]
					  [used (code-used-vars code)]
					  [captured (code-captured-vars code)])
				     (choose-binding-representations! locals globals used captured)
				     (choose-closure-representation! code)))
				 (compiler:get-closure-list)))])
		(verbose-time rep-thunk))

	      ; (map (lambda (ast) (pretty-print (zodiac->sexp/annotate ast))) (block-source s:file-block))
	      
	      ;;-----------------------------------------------------------------------
	      ;; Virtual Machine Scheme translation
	      ;;   Here we turn our code into VM Scheme as we enter the arena of
	      ;;    low level transformations and optimizations.
	      ;;   This transformation may create new local variables, so
	      ;;    we have to update the local variable set for each top-level
	      ;;    expression or code body.
	      
	      (when (compiler:option:verbose) (printf " transforming to Virtual Machine form\n"))
	      (when (compiler:option:debug) (debug " = VMPHASE =\n"))
	      
	      (let ([vmphase-thunk
		     (lambda ()
		       ; top-level.  The last expression will be in tail position and should
		       ; return its value
                       (set-block-source!
                        s:file-block
                        (let loop ([s (block-source s:file-block)]
                                   [l (block-codes s:file-block)]
                                   [m (block-magics s:file-block)])
                          (if (null? s)
                              null
                              (let-values ([(vm new-locals)
                                            (vm-phase (car s) 
                                                      #t
                                                      #f
                                                      (if (null? (cdr s))
                                                          (lambda (ast)
                                                            (make-vm:return 
                                                             (zodiac:zodiac-stx ast)
                                                             ast
                                                             (and (car m) #t)))
                                                          (lambda (ast)
                                                            (make-vm:void
                                                             (zodiac:zodiac-stx ast)
                                                             ast
                                                             (and (car m) #t))))
                                                      (null? (cdr s))
                                                      (and (car m) #t))])
                                (add-code-local+used-vars! (car l) new-locals)
                                (cons vm
                                      (loop (cdr s) (cdr l) (cdr m)))))))
		       ; code-bodies
		       (for-each 
			(lambda (L)
			  (let* ([code (get-annotation L)]
				 [tail-pos (lambda (ast)
					     (make-vm:return 
					      (zodiac:zodiac-stx ast)
					      ast
					      #f))]
				 [new-locals
				  (cond
				   [(zodiac:case-lambda-form? L)
				    (let-values ([(vms new-locals)
						  (let loop ([l (zodiac:case-lambda-form-bodies L)]
							     [case-codes (procedure-code-case-codes 
									  (get-annotation L))]
							     [vms null])
						    (if (null? l)
							(values (reverse vms) 
								; empty: already added via case
								empty-set) 
							(let-values ([(vm new-locals)
								      (vm-phase (car l) #t #f tail-pos #t #f)])
							  (add-code-local+used-vars! 
							   (car case-codes)
							   new-locals )
							  (loop (cdr l)
								(cdr case-codes)
								(cons vm vms)))))])
				      (zodiac:set-case-lambda-form-bodies! L vms)
				      new-locals)]
				   [else (compiler:internal-error
					  L
					  "vmphase: unknown closure type")])])
			    (add-code-local+used-vars! code new-locals)))
			(compiler:get-closure-list)))])
		(verbose-time vmphase-thunk))

	      (compiler:report-messages! #t)

	      ; (print-struct #t)
	      ; (map (lambda (ast) (pretty-print ast)) (block-source s:file-block))

	      ;;-----------------------------------------------------------------------
	      ;; Virtual Machine Optimization Pass
	      ;;
	      ;;  As in the previous phase, new local variables may be created.
	      
	      (when (compiler:option:verbose) (printf " optimizing Virtual Machine code\n"))
	      
	      (let ([vmopt-thunk
		     (lambda ()
		       ; top-level
                       (set-block-source!
                        s:file-block
                        (let loop ([bl (block-source s:file-block)]
                                   [cl (block-codes s:file-block)])
                          (if (null? bl)
                              null
                              (let-values ([(b new-locs) ((vm-optimize! #f #f) (car bl))])
                                (add-code-local+used-vars! (car cl) new-locs)
                                (cons b (loop (cdr bl) (cdr cl)))))))
		       
		       ; code-bodies
		       (for-each (lambda (L)
				   (let ([code (get-annotation L)])
				     (cond
				      [(zodiac:case-lambda-form? L)
                                       (zodiac:set-case-lambda-form-bodies!
                                        L
                                        (let loop ([bodies (zodiac:case-lambda-form-bodies L)]
                                                   [case-codes (procedure-code-case-codes code)]
                                                   [i 0])
                                          (if (null? bodies)
                                              null
                                              (let-values ([(new-body new-locs) ((vm-optimize! L i) (car bodies))])
                                                (add-code-local+used-vars! (car case-codes)
                                                                           new-locs)
                                                (cons new-body
                                                      (loop (cdr bodies)
                                                            (cdr case-codes)
                                                            (add1 i)))))))]
				      [else (compiler:internal-error
					     L
					     "vmopt: unknown closure type")])))
				 (compiler:get-closure-list)))])
		(verbose-time vmopt-thunk))
		 
	      (compiler:report-messages! #t)
			  
	      ;;-----------------------------------------------------------------------
	      ;; Virtual Machine -> ANSI C translation
	      ;;
	      (when (compiler:option:verbose)
		(printf " [emitting ~a C to \"~a\"]\n" 
			"ANSI"
			c-output-path))
	
	      (let ([vm2c-thunk
		     (lambda ()
		       (parameterize ([read-case-sensitive #t]) ;; so symbols containing uppercase print like we want
			 (let ([c-port #f])
			   (dynamic-wind 
			       ;;pre
			       (lambda () (set! c-port (open-output-file c-output-path)))
			       
			       ;;value
			       (lambda ()
				 (fprintf c-port "#define MZC_SRC_FILE ~s\n" input-name)
				 (when (compiler:option:unsafe) (fprintf c-port "#define MZC_UNSAFE 1\n"))
				 (when (compiler:option:disable-interrupts) (fprintf c-port "#define MZC_DISABLE_INTERRUPTS 1\n"))
				 (when (compiler:option:fixnum-arithmetic) (fprintf c-port "#define MZC_FIXNUM 1\n"))
				 
				 (fprintf c-port "\n#include \"~ascheme.h\"\n"
					  (if (compiler:option:compile-for-embedded)
					      ""
					      "e"))
				 
				 (unless (null? c-declares)
				   (fprintf c-port "\n/* c-declare literals */\n\n")
				   (for-each
				    (lambda (c-declare)
				      (fprintf c-port "~a\n" c-declare))
				    (reverse c-declares))
				   (fprintf c-port "\n/* done with c-declare literals */\n\n"))

				 (unless (null? c-lambdas)
				   (fprintf c-port "\n/* c-lambda implementations */\n\n")
				   (for-each
				    (lambda (c-lambda)
				      (let ([name (car c-lambda)]
					    [body (cdr c-lambda)])
					(fprintf c-port "Scheme_Object *~a(int argc, Scheme_Object **argv) {\n"
						 name)
					(fprintf c-port "~a\n" body)
					(fprintf c-port "}\n")))
				    (reverse c-lambdas))
				   (fprintf c-port "\n/* done with c-lambda implementations */\n\n"))

				 (fprintf c-port "#include \"mzc.h\"\n\n")
				 (vm->c:emit-struct-definitions! (compiler:get-structs) c-port)
				 (vm->c:emit-symbol-declarations! c-port)
				 (vm->c:emit-inexact-declarations! c-port)
				 (vm->c:emit-string-declarations! c-port)
				 (vm->c:emit-prim-ref-declarations! c-port)
				 (vm->c:emit-static-declarations! c-port)

				 (let loop ([c 0][l (block-bytecodes s:file-block)][m (block-magics s:file-block)])
				   (cond
				    [(null? l) (void)]
				    [(not (car l)) (loop c (cdr l) (cdr m))]
				    [else
				     (vm->c:emit-bytecode-string-definition! 
				      (format "top_level_bytecode_~a" c)
				      (car l)
				      c-port)
				     (fprintf c-port "#define top_level_magic_sym_~a ~s\n\n"
					      c
					      (symbol->string (car m)))
				     (loop (add1 c) (cdr l) (cdr m))]))
				 
				 (let loop ([n 0])
				   (unless (= n (compiler:get-total-vehicles))
				     (vm->c:emit-vehicle-declaration c-port n)
				     (loop (+ n 1))))
				 (newline c-port)
				 
				 (unless (compiler:multi-o-constant-pool)
				   (fprintf c-port "\nstatic void make_symbols()\n{\n")
				   (vm->c:emit-symbol-definitions! c-port)
				   (fprintf c-port "}\n"))

				 (unless (zero? (const:get-inexact-counter))
				   (fprintf c-port "\nstatic void make_inexacts()\n{\n")
				   (vm->c:emit-inexact-definitions! c-port)
				   (fprintf c-port "}\n"))

				 (fprintf c-port "\nstatic void gc_registration()\n{\n")
				 (vm->c:emit-registration! c-port)
				 (fprintf c-port "}\n")

				 (fprintf c-port "\nstatic void init_prims(Scheme_Env * env)\n{\n")
				 (vm->c:emit-prim-ref-definitions! c-port)
				 (fprintf c-port "}\n")
				 
				 (unless (null? (compiler:get-case-lambdas))
				   (fprintf c-port "\nstatic void init_cases_arities()\n{\n")
				   (vm->c:emit-case-arities-definitions! c-port)
				   (fprintf c-port "}\n"))
				 (newline c-port)

				 (let* ([codes (block-codes s:file-block)]
					[locals (map code-local-vars codes)]
					[globals (map code-global-vars codes)]
					[init-constants-count
					 (if (zero? number-of-true-constants)
					     -1
					     (vm->c:emit-top-levels! "init_constants" #f #f #t number-of-true-constants
								     (block-source s:file-block)
								     locals globals
								     (block-max-arity s:file-block)
								     #f #f ; no module entries
								     c-port))]
					[top-level-count
					 (vm->c:emit-top-levels! "top_level" #t #t #f -1
								 (list-tail (block-source s:file-block) number-of-true-constants)
								 (list-tail locals number-of-true-constants)
								 (list-tail globals number-of-true-constants)
								 (block-max-arity s:file-block)
								 #f #f ; no module entries
								 c-port)])
				   (fprintf c-port
					    "static Scheme_Object * do_scheme_reload(Scheme_Env * env)\n{\n")
				   (fprintf c-port"~aScheme_Per_Load_Statics *PLS;\n"
					    vm->c:indent-spaces)
				   (fprintf c-port 
					    "~aPLS = (Scheme_Per_Load_Statics *)scheme_malloc(sizeof(Scheme_Per_Load_Statics));\n"
					    vm->c:indent-spaces)
				   (let loop ([c 0])
				     (fprintf c-port "~a~atop_level_~a(env, PLS);\n" 
					      vm->c:indent-spaces 
					      (if (= c top-level-count) "return " "")
					      c)
				     (unless (= c top-level-count)
				       (loop (add1 c))))
				   (fprintf c-port 
					    "}\n\n")


				   (fprintf c-port
					    "Scheme_Object * scheme_reload~a(Scheme_Env * env)\n{\n"
					    compiler:setup-suffix)
				   (fprintf c-port"~areturn do_scheme_reload(env);\n"
					    vm->c:indent-spaces)
				   (fprintf c-port 
					    "}\n\n")

				   (fprintf c-port
					    "\nstatic void do_scheme_setup(Scheme_Env * env)\n{\n")
				   (fprintf c-port
					    "~ascheme_set_tail_buffer_size(~a);\n"
					    vm->c:indent-spaces
					    s:max-arity)
				   (fprintf c-port "~agc_registration();\n"
					    vm->c:indent-spaces)
				   (unless (compiler:multi-o-constant-pool)
				     (fprintf c-port "~amake_symbols();\n"
					      vm->c:indent-spaces))
				   (unless (zero? (const:get-inexact-counter))
				     (fprintf c-port "~amake_inexacts();\n"
					      vm->c:indent-spaces))
				   (fprintf c-port "~ainit_prims(env);\n"
					    vm->c:indent-spaces)
				   (unless (null? (compiler:get-case-lambdas))
				     (fprintf c-port "~ainit_cases_arities();\n"
					      vm->c:indent-spaces))		       

				   (let loop ([c 0])
				     (unless (> c init-constants-count)
				       (fprintf c-port "~ainit_constants_~a(env);\n" 
						vm->c:indent-spaces
						c)
				       (loop (add1 c))))

				   (fprintf c-port 
					    "}\n\n")

                                   (fprintf c-port
					    "\nvoid scheme_setup~a(Scheme_Env * env)\n{\n"
					    compiler:setup-suffix)
				   (fprintf c-port
					    "~ado_scheme_setup(env);\n"
					    vm->c:indent-spaces)
				   (fprintf c-port 
					    "}\n\n")
				   
				   (when (string=? "" compiler:setup-suffix)
				     (fprintf c-port
					      "\nScheme_Object * scheme_initialize(Scheme_Env * env)\n{\n")
				     (fprintf c-port "~ado_scheme_setup~a(env);\n"
					      vm->c:indent-spaces
					      compiler:setup-suffix)
				     (fprintf c-port "~areturn do_scheme_reload~a(env);\n"
					      vm->c:indent-spaces
					      compiler:setup-suffix)
				     (fprintf c-port 
					      "}\n\n"))

				   (fprintf c-port
					    "\nScheme_Object * ~ascheme_module_name()\n{\n~areturn "
					    compiler:setup-suffix
					    vm->c:indent-spaces)
				   (if compiler:module-decl-name
				       (let ([s (symbol->string compiler:module-decl-name)])
					 (fprintf c-port "scheme_intern_exact_symbol(~s, ~a)" s (string-length s)))
				       (fprintf c-port "scheme_false"))
				   (fprintf c-port ";\n}\n"))

				 (let emit-vehicles ([vehicle-number 0])
				   (unless (= vehicle-number (compiler:get-total-vehicles))
				     (let* ([vehicle (get-vehicle vehicle-number)]
					    [lambda-list (vehicle-lambdas vehicle)])
				       
				       (vm->c:emit-vehicle-header c-port vehicle-number)
				       (vm->c:emit-vehicle-prologue c-port vehicle)
				       
				       ;; get the lambdas that appear in this vehicle
				       
				       ;; sort the functions by index to get an optimal case statement
				       ;; even for stupid compilers
				       (set! lambda-list
					     (sort lambda-list
                                                   (lambda (l1 l2)
                                                     (< (closure-code-label (get-annotation l1))
                                                        (closure-code-label (get-annotation l2))))))
				       (for-each (lambda (L)
						   (let ([code (get-annotation L)]
							 [start (zodiac:zodiac-start L)])
						     (fprintf c-port "~a/* code body ~a ~a [~a,~a] */\n"
							      vm->c:indent-spaces (closure-code-label code)
							      (let ([n (closure-code-name code)])
								(if n
								    (protect-comment 
								     (vm->c:extract-inferred-name n))
								    ""))
							      (zodiac:location-line start)
							      (zodiac:location-column start))
						     (cond
						      [(zodiac:case-lambda-form? L)
						       (let-values ([(count suffix?) 
								     (vm->c:emit-function-prologue L c-port)])
							 (let loop ([i 0])
							   (unless (= i count)
							     (let* ([indent
								     (string-append
								      vm->c:indent-spaces vm->c:indent-spaces
								      (if suffix?  vm->c:indent-spaces ""))]
								    [undefines
								     (vm->c:emit-case-prologue L i
											       (lambda ()
												 (if suffix?
												     (fprintf c-port "~a~a/* begin case ~a */\n~a~a{\n" 
													      vm->c:indent-spaces vm->c:indent-spaces i
													      vm->c:indent-spaces vm->c:indent-spaces)
												     (when (zero? i)
												       (fprintf c-port "~a{\n" vm->c:indent-spaces))))
											       (if suffix? (format "c~a" i) "")
											       indent
											       c-port)])
							       (vm->c-expression (list-ref (zodiac:case-lambda-form-bodies L) i)
										 code
										 c-port
										 (* (if suffix? 3 2) vm->c:indent-by)
										 #f
										 -1)
							       (vm->c:emit-case-epilogue L i undefines indent c-port)
							       (when suffix?
								 (fprintf c-port "~a~a} /* end case ~a */\n" 
									  vm->c:indent-spaces 
									  vm->c:indent-spaces i)))
							     
							     (loop (add1 i))))
							 (vm->c:emit-function-epilogue code 
										       (if suffix? "" "}")
										       c-port))]
						      [else
						       (compiler:internal-error
							L
							"vm2c: unknown closure type")])
						     (newline c-port)))
						 lambda-list))
				     
				     (vm->c:emit-vehicle-epilogue c-port vehicle-number)
				     (newline c-port)
				     (emit-vehicles (+ 1 vehicle-number)))))
			       
			       ;; post (dynamic wind cleanup)
			       (lambda ()  (close-output-port c-port))))))])
		(with-handlers ([void (lambda (exn)
					(delete-file c-output-path)
					(raise exn))])
		  (verbose-time vm2c-thunk)))
	      
	      (compiler:report-messages! #t)
	      
	      ;; Write out symbols for multi-o constant pool
	      (when (compiler:multi-o-constant-pool)
		(call-with-output-file constant-pool-output-path
		  (lambda (port)
		    (fprintf port "(~s\n (symbols\n" compiler:setup-suffix)
		    (vm->c:emit-symbol-list! port "" #f)
		    (fprintf port "  )\n )\n")))))))

	;;-----------------------------------------------------------------------
	;; 3m xform
	;;
	
        (when c3m-output-path
	  (when (compiler:option:verbose)
	    (printf " [xforming C to \"~a\"]\n" 
		    c3m-output-path))

	  (let ([clean-up-src-c
		 (lambda ()
		   (when (and (compiler:option:clean-intermediate-files)
			      (not from-c?)
			      (file-exists? c-output-path))
		     (delete-file c-output-path)))])
	    (with-handlers ([void
			     (lambda (exn)
			       (when (compiler:option:clean-intermediate-files)
				 (when (file-exists? c3m-output-path)
				   (delete-file c3m-output-path)))
			       (clean-up-src-c)
			       (raise exn))])
	      
	      (xform (not (compiler:option:verbose)) 
		     (path->string c-output-path)
		     c3m-output-path
		     (list (find-include-dir)
			   (collection-path "compiler")))
	      
	      (clean-up-src-c))))

	;;--------------------------------------------------------------------
	;; COMPILATION TO NATIVE CODE
	;;
	
	(if c-only?
            (when (compiler:option:somewhat-verbose)
              (printf " [output to \"~a\"]\n" (or c3m-output-path c-output-path)))
	    
	    (begin
	      (unless input-path
                (when (compiler:option:somewhat-verbose)
                  (printf "\"~a\": \n" (or c3m-output-path c-output-path))))
	      
	      (when (compiler:option:verbose) (printf " [compiling native code to \"~a\"]\n"
						      obj-output-path))
	      
              (let ([clean-up
                     (lambda ()
                       (when (and (compiler:option:clean-intermediate-files)
                                  input-path)
                         (if c3m-output-path
                             (delete-file c3m-output-path)
                             (delete-file c-output-path))))])

                ;; Compile
                (let ([compile-thunk
                       (lambda ()
                         (with-handlers
                             ([void (lambda (exn)
                                      (clean-up)
                                      (compiler:fatal-error
                                       #f
                                       (string-append
                                        " C compiler did not complete successfully"
                                        (string #\newline)
                                        (exn-message exn)))
                                      (compiler:report-messages! #t))])
                           (compile-extension (not (compiler:option:verbose)) 
                                              (or c3m-output-path c-output-path) obj-output-path
                                              (list (collection-path "compiler")))))])
                  (verbose-time compile-thunk))

                (clean-up))
	      
	      (if multi-o?
                  (when (compiler:option:somewhat-verbose)
                    (printf " [output to \"~a\"]\n" obj-output-path))
		  
		  (begin
		    ;; Link
		    (when (compiler:option:verbose) (printf " [linking to \"~a\"]\n"
							    dll-output-path))
		    (let ([link-thunk
			   (lambda ()
			     (with-handlers
				 ([void (lambda (exn)
					  (compiler:fatal-error 
					   #f 
					   (string-append 
					    " linker did not link successfully"
					    (string #\newline)
					    (exn-message exn)))
					  (compiler:report-messages! #t))])
			       (link-extension (not (compiler:option:verbose)) (list obj-output-path) dll-output-path)))])
		      (verbose-time link-thunk))
		    
		    ;; clean-up
		    (when (compiler:option:clean-intermediate-files)
		      (delete-file obj-output-path))
		    
                    (when (compiler:option:somewhat-verbose)
                      (printf " [output to \"~a\"]\n" dll-output-path))))))
	
	(when debug:port
	  (close-output-port debug:port))
	
	;; clean up for the garbage collector
	(compiler:init-define-lists!)
	(const:init-tables!)
	(compiler:init-closure-lists!)
	(compiler:init-structs!)
	(set! s:file-block #f)
	(when (compiler:option:verbose)
	  (printf " finished [cpu ~a, real ~a].\n"
		  total-cpu-time
		  total-real-time)))))))
