;; VM Scheme -> C translation module
;; (c) 1996-1997 Sebastian Good
;; (c) 1997-2001 PLT

(module vm2c mzscheme
  (require mzlib/unit
	   mzlib/list)

  (require syntax/zodiac-sig
	   syntax/primitives)

  (require "sig.ss")
  (require "../sig.ss")

  (provide vm2c@)
  (define-unit vm2c@
      (import (prefix compiler:option: compiler:option^)
	      compiler:library^
	      compiler:cstructs^
	      (prefix zodiac: zodiac^)
	      compiler:zlayer^
	      compiler:analyze^
	      compiler:const^
	      compiler:rep^
	      compiler:closure^
	      compiler:vehicle^
	      compiler:vmstructs^
	      compiler:driver^)
      (export compiler:vm2c^)

      (define local-vars-at-top? #f)

      (define (interned? sym)
	(eq? sym (string->symbol (symbol->string sym))))

      (define vm->c:indent-by 4)
      (define vm->c:indent-spaces
	(make-string vm->c:indent-by #\space))

      (define (vm->c:generate-modglob-name m s)
	(when (symbol? m)
	  (compiler:get-symbol-const! #f m)) ;; generates symbol const
	(compiler:get-symbol-const! #f s) ;; generates symbol const
	(let ([mname (cond
		      [(symbol? m) m]
		      [(not m) '||]
		      [else 
		       ;; try to find a useful part of the module-path-index;
		       ;; this is just for debugging.
		       (let-values ([(path base) (module-path-index-split m)])
			 (cond
			  [(and (pair? path)
				(eq? 'lib (car path))
				(pair? (cdr path))
				(string? (cadr path)))
			   (string->symbol (cadr path))]
			  [else (string->symbol "MoD")]))])])
	  (let ([name (symbol-append 'GL (compiler:gensym) mname '_ s)])
	    (string->symbol (compiler:clean-string (symbol->string name))))))

      (define vm->c:bucket-name
	(lambda (mod var)
	  ;; Shouldn't generate any new names:
	  (mod-glob-cname (compiler:add-global-varref! mod var #f #f #f #f))))

      (define (vm->c:SYMBOLS-name)
	(if (compiler:multi-o-constant-pool)
	    (format "SYMBOLS~a" (compiler:get-setup-suffix))
	    "SYMBOLS"))

      (define (vm->c:INEXACTS-name)
	"INEXACTS")

      (define (vm->c:STRING-name)
	"STRINGS")

      (define (vm->c:make-symbol-const-string sc)
	(format "~a[~a]" (vm->c:SYMBOLS-name) (zodiac:varref-var sc)))

      (define (vm->c:emit-list! port comma c-comment? table counter -symbol->string)
	(let ([v (make-vector counter)])
	  (hash-table-for-each
	   table
	   (lambda (sym b)
	     (vector-set! v (string->number (symbol->string (zodiac:varref-var b))) sym)))
	  (let loop ([i 0])
	    (unless (= i (vector-length v))
	      (fprintf port "  ~s~a ~a\n" (-symbol->string (vector-ref v i)) comma 
		       (if c-comment? 
			   (format "/* ~a */" i)
			   (format "; ~a" i)))
	      (loop (add1 i))))))

      (define (vm->c:emit-symbol-list! port comma c-comment?)
	(vm->c:emit-list! port comma c-comment? (const:get-symbol-table) (const:get-symbol-counter) 
			  (if c-comment?
			      symbol->string
			      ;; Hack: wrap with parens to indicate uninterned
			      (lambda (s)
				((if (interned? s)
				     values
				     list)
				 (symbol->string s))))))

      (define (vm->c:emit-symbol-length-list! port comma c-comment?)
	(vm->c:emit-list! port comma c-comment? (const:get-symbol-table) (const:get-symbol-counter) 
			  (lambda (s) (string-length (symbol->string s)))))

      (define (vm->c:emit-symbol-declarations! port)
	(unless (zero? (const:get-symbol-counter))
	  (unless (compiler:multi-o-constant-pool)
	    (fprintf port "static const char *SYMBOL_STRS[~a] = {\n" (const:get-symbol-counter))
	    (vm->c:emit-symbol-list! port "," #t)
	    (fprintf port "}; /* end of SYMBOL_STRS */\n\n")
	    (fprintf port "static const long SYMBOL_LENS[~a] = {\n" (const:get-symbol-counter))
	    (vm->c:emit-symbol-length-list! port "," #t)
	    (fprintf port "}; /* end of SYMBOL_LENS */\n\n"))

	  (fprintf port "~aScheme_Object * ~a[~a];\n\n"
		   (if (compiler:multi-o-constant-pool) "" "static ")
		   (vm->c:SYMBOLS-name)
		   (const:get-symbol-counter))))

      (define (vm->c:emit-bytecode-string-definition! name bytecode port)
	(emit-string port
		     "char"
		     (let ([p (open-output-bytes)])
		       (write bytecode p)
		       (get-output-bytes p))
		     name))

      (define (vm->c:emit-inexact-list! port comma comment?)
	(vm->c:emit-list! port comma comment? (const:get-inexact-table) (const:get-inexact-counter)
			  (lambda (x) (string->number (symbol->string x)))))

      (define (vm->c:emit-inexact-declarations! port)
	(unless (zero? (const:get-inexact-counter))
	  (fprintf port "static const double INEXACT_NUMBERS[~a] = {\n" (const:get-inexact-counter))
	  (vm->c:emit-inexact-list! port "," #t)
	  (fprintf port "}; /* end of INEXACT_NUMBERS */\n\n")
	  (fprintf port "static Scheme_Object * ~a[~a];\n\n" 
		   (vm->c:INEXACTS-name)
		   (const:get-inexact-counter))))

      (define (emit-string-declarations! port table kind)
	(hash-table-for-each
	 table
	 (lambda (str index)
	   (emit-string port kind str (format "STRING_~a" index)))))

      (define (vm->c:emit-string-declarations! port)
	(emit-string-declarations! port (const:get-string-table) "mzchar")
	(emit-string-declarations! port (const:get-bytes-table) "char"))

      (define emit-string
	(lambda (port kind str name)
	  (let* ([len (if (string? str)
			  (string-length str)
			  (bytes-length str))])
	    (let ([friendly (if (string? str)
				(substring str 0 (min len 24))
				(bytes->string/latin-1 (subbytes str 0 (min len 24))))])
	      (fprintf port
		       "/* ~a */\n"
		       (list->string (map (lambda (i)
					    (cond
					     [(eq? i #\/) #\_]
					     [(<= 32 (char->integer i) 121)
					      i]
					     [else #\_]))
					  (string->list friendly)))))
	    (fprintf port "static const ~a ~a[~a] = {" kind name (add1 len))
	    (let loop ([i 0])
	      (unless (= i len)
		(when (zero? (modulo i 20))
		  (fprintf port "\n    "))
		(fprintf port "~a, " (if (string? str)
					 (char->integer (string-ref str i))
					 (bytes-ref str i)))
		(loop (add1 i)))))
	  (fprintf port "0 }; /* end of ~a */\n\n" name)))

      (define (vm->c:emit-symbol-definitions! port)
	(unless (zero? (const:get-symbol-counter))
	  (fprintf port "  int i;\n  Scheme_Object *s;\n")
	  (fprintf port "  for (i = ~a; i--; ) {\n"
		   (const:get-symbol-counter))
	  (fprintf port "    s = scheme_intern_exact_symbol(SYMBOL_STRS[i], SYMBOL_LENS[i]);\n")
	  (fprintf port "    SYMBOLS[i] = s; \n   }\n")
	  ;; Some symbols might be uninterned...
	  (hash-table-for-each
	   (const:get-symbol-table)
	   (lambda (sym b)
	     (unless (interned? sym)
	       (let ([pos (zodiac:varref-var b)])
		 (fprintf port "  s = scheme_make_exact_symbol(SYMBOL_STRS[~a], SYMBOL_LENS[~a]); /* uninterned */\n"
			  pos pos)
		 (fprintf port "  SYMBOLS[~a] = s;\n" pos)))))))

      (define (vm->c:emit-inexact-definitions! port)
	(unless (zero? (const:get-inexact-counter))
	  (fprintf port "  int i;\n  Scheme_Object *n;\n")
	  (fprintf port "  for (i = ~a; i--; ) {\n"
		   (const:get-inexact-counter))
	  (fprintf port "    n = scheme_make_double(INEXACT_NUMBERS[i]);\n")
	  (fprintf port "    INEXACTS[i] = n; \n   }\n")))

      (define vm->c:emit-prim-ref-declarations!
	(lambda (port)
	  (unless (set-empty? (compiler:get-primitive-refs))
	    (fprintf port "/* primitives referenced by the code */\n")
	    (fprintf port "static struct {\n")
	    (for-each (lambda (a)
			(fprintf port "  Scheme_Object * ~a;\n"
				 (vm->c:convert-symbol 
				  (vm->c:bucket-name
                                   (module-path-index-join ''#%kernel #f)
				   a))))
		      (set->list (compiler:get-primitive-refs)))
	    (fprintf port "} P;\n")
	    (newline port))))

      (define vm->c:emit-prim-ref-definitions!
	(lambda (port)
	  (unless (set-empty? (compiler:get-primitive-refs))
	    (fprintf port "   /* primitives referenced by the code */\n")
	    (for-each (lambda (a)
			(fprintf port "~aP.~a = scheme_module_bucket(~a, ~a, -1, env)->val;\n"
				 vm->c:indent-spaces
				 (vm->c:convert-symbol (vm->c:bucket-name (module-path-index-join ''#%kernel #f) a))
				 (vm->c:make-symbol-const-string (compiler:get-symbol-const! #f '#%kernel))
				 (vm->c:make-symbol-const-string (compiler:get-symbol-const! #f a))))
		      (set->list (compiler:get-primitive-refs))))))

      (define vm->c:emit-struct-definitions!
	(lambda (structs port)
	  (fprintf port "/* compiler-written structures */\n")
	  (for-each (lambda (struct)
		      (fprintf port "struct ~a\n{\n"
			       (vm->c:convert-symbol
				(rep:struct-name struct)))
		      (for-each
		       (lambda (field)
			 (fprintf port "~a~a ~a;\n"
				  vm->c:indent-spaces
				  (vm->c:convert-type-definition
				   (rep:struct-field-rep field))
				  (vm->c:convert-symbol
				   (rep:struct-field-name field)))) 
		       (rep:struct-fields struct))
		      (fprintf port "};\n"))
		    (reverse structs))
	  (newline port)))

      (define (compiler:any-statics?)
	(not (and (null? (compiler:get-static-list))
		  (null? (compiler:get-case-lambdas))
		  (null? (compiler:get-lifted-lambda-vars)))))

      (define (emit-static-variable-fields! port l)
	(unless (null? l)
	  (fprintf port "#ifndef MZ_PRECISE_GC\n")
	  (fprintf port "  /* Write fields as an array to help C compilers */\n")
	  (fprintf port "  /* that don't like really big records. */\n")
	  (fprintf port "  Scheme_Object * _consts_[~a];\n" (length l))
	  (let svloop ([l l][n 0])
	    (unless (null? l)
	      (fprintf port "# define ~a _consts_[~a]\n"
		       (vm->c:convert-symbol (car l)) n)
	      (svloop (cdr l) (add1 n))))
	  (fprintf port "#else\n")
	  (for-each (lambda (c)
		      (fprintf port "  Scheme_Object * ~a;\n"
			       (vm->c:convert-symbol c)))
		    l)
	  (fprintf port "#endif\n")))

      ;; when statics have binding information, this will look more like 
      ;; emit-local-variable-declarations!
      (define vm->c:emit-static-declarations!
	(lambda (port)
	  (unless (not (compiler:any-statics?))
	    (fprintf port "/* compiler-written static variables */\n")
	    (fprintf port "static struct {\n")
	    (emit-static-variable-fields! port (compiler:get-static-list))
	    (unless (null? (compiler:get-case-lambdas))
	      (fprintf port "  mzshort *casesArities[~a];\n"
		       (length (compiler:get-case-lambdas))))
	    (for-each
	     (lambda (ll)
	       (fprintf port "  Scheme_Object * ~a;\n" 
			(vm->c:convert-symbol (zodiac:varref-var ll))))
	     (compiler:get-lifted-lambda-vars))
	    (fprintf port "} S;\n\n"))

	  (fprintf port "/* compiler-written per-load static variables */\n")
	  (fprintf port "typedef struct Scheme_Per_Load_Statics {\n")
	  (if (null? (compiler:get-per-load-static-list))
	      (fprintf port "  int dummy;\n")
	      (emit-static-variable-fields! port (compiler:get-per-load-static-list)))
	  (fprintf port "} Scheme_Per_Load_Statics;\n")
	  (newline port)))
      
      ;; when statics have binding information, this need only register
      ;; pointer declarations
      (define vm->c:emit-registration!
	(lambda (port)
	  (fprintf port "~a/* register compiler-written static variables with GC */\n"
		   vm->c:indent-spaces)
	  (let ([register
		 (lambda (v)
		   (fprintf port "~ascheme_register_extension_global(&~a, sizeof(~a));\n"
			    vm->c:indent-spaces v v))])
	    (unless (or (zero? (const:get-symbol-counter)) (compiler:multi-o-constant-pool))
	      (register "SYMBOLS"))
	    (unless (zero? (const:get-inexact-counter))
	      (register "INEXACTS"))
	    (unless (set-empty? (compiler:get-primitive-refs))
	      (register "P"))
	    (unless (not (compiler:any-statics?))
	      (register "S")))
	  (newline port)))

      (define (vm->c:emit-case-arities-definitions! port)
	(fprintf port "   /* arity information for compiled case-lambdas */\n")
	(let caloop ([l (reverse (compiler:get-case-lambdas))][pos 0])
	  (unless (null? l)
	    (let* ([ast (car l)]
		   [args (zodiac:case-lambda-form-args ast)])
	      (if (null? args)
		  (fprintf port "~aS.casesArities[~a] = NULL;\n"
			   vm->c:indent-spaces pos)
		  (begin
		    (fprintf port "~a{\n~a  mzshort * arities;\n" 
			     vm->c:indent-spaces vm->c:indent-spaces)
		    (fprintf port "~a  arities = (mzshort *)scheme_malloc_atomic(~a * sizeof(mzshort));\n"
			     vm->c:indent-spaces
			     (* 2 (length args)))
		    (let cailoop ([l args][n 0])
		      (unless (null? l)
			(let-values ([(min-arity max-arity) (compiler:formals->arity (car l))])
			  (fprintf port "~a  arities[~a] = ~a;\n~a  arities[~a] = ~a;\n"
				   vm->c:indent-spaces (* 2 n) min-arity
				   vm->c:indent-spaces (add1 (* 2 n)) max-arity))
			(cailoop (cdr l) (add1 n))))
		    (fprintf port "~a  S.casesArities[~a] = arities;\n"
			     vm->c:indent-spaces pos)
		    (fprintf port "~a}\n" vm->c:indent-spaces))))
	    (caloop (cdr l) (add1 pos)))))

      (define (vm->c:emit-top-levels! kind return? per-load? null-self-modidx? count vm-list locals-list
				      globals-list max-arity module mod-syntax? c-port)
	;; count == -1 => go to the end of the list
	(let tls-loop ([i 0]
		       [n 0]
		       [vml vm-list]
		       [ll locals-list]
		       [bl globals-list])
	  (fprintf c-port
		   "static ~a ~a_~a(Scheme_Env * env~a)\n{\n"
		   (if return? "Scheme_Object *" "void")
		   kind i
		   (if (or per-load? module) ", Scheme_Per_Load_Statics *PLS" ""))
	  (when null-self-modidx? (fprintf c-port "#define self_modidx NULL\n"))
	  (when (> max-arity 0)
	    (fprintf c-port
		     "~aScheme_Object * arg[~a];\n"
		     vm->c:indent-spaces
		     max-arity)
	    (fprintf c-port "~aScheme_Object ** tail_buf;\n"
		     vm->c:indent-spaces))
	  (let loop ([c (compiler:option:max-exprs-per-top-level-set)][n n][vml vml][ll ll][bl bl])
	    (if (or (zero? c) (null? vml) (= n count))
		(begin
		  (unless (or (null? vml) (= n count) (not return?))
		    (fprintf c-port "~areturn NULL;\n" vm->c:indent-spaces))
		  (when null-self-modidx? (fprintf c-port "#undef self_modidx\n"))
		  (fprintf c-port 
			   "} /* end of ~a_~a */\n\n" kind i)
		  (if (or (null? vml) (= n count))
		      i
		      (tls-loop (add1 i) n vml ll bl)))
		(if module
		    (loop c n (cdr vml) (cdr ll) (cdr bl))
		    (begin
		      (let ([start (zodiac:zodiac-start (car vml))])
			(fprintf c-port "~a{ /* [~a,~a] */\n" vm->c:indent-spaces
				 (zodiac:location-line start)
				 (zodiac:location-column start)))
		      (vm->c:emit-local-variable-declarations! 
		       (car ll)
		       (string-append vm->c:indent-spaces vm->c:indent-spaces)
		       c-port)
		      (vm->c:emit-local-bucket-declarations! 
		       (car bl)
		       (string-append vm->c:indent-spaces vm->c:indent-spaces)
		       #t
		       c-port)
		      (vm->c:emit-bucket-lookups! 
		       (car bl)
		       (string-append vm->c:indent-spaces vm->c:indent-spaces)
		       c-port)
		      
		      (vm->c-expression (car vml) #f c-port vm->c:indent-by #t n)
		      
		      (fprintf c-port "~a}\n" vm->c:indent-spaces)
		      
		      (loop (sub1 c) (add1 n) (cdr vml) (cdr ll) (cdr bl))))))))

      (define vm->c:emit-vehicle-prototype
	(lambda (port number)
	  (let ([v (get-vehicle number)])
	    (fprintf port 
		     "static ~a vehicle_~a(~a)"
		     "Scheme_Object *"
		     number
		     (cond
		      [(procedure-vehicle? v)
		       "int argc, Scheme_Object *argv[], Scheme_Object *void_param"]
		      [else
		       (compiler:internal-error
			#f
			"vm->c:emit-vehicle-prototype: unknown closure kind ~a"
			v)])))))

      (define vm->c:emit-vehicle-declaration
	(lambda (port number)
	  (vm->c:emit-vehicle-prototype port number)
	  (fprintf port "; /* ~a */ \n"
		   (vehicle-total-labels (get-vehicle number)))))

      (define vm->c:emit-vehicle-header
	(lambda (port number)
	  (vm->c:emit-vehicle-prototype port number)
	  (fprintf port "\n{\n")))

      (define vm->c:emit-vehicle-prologue
	(lambda (port vehicle)
	  (let ([max-arity (vehicle-max-arity vehicle)]
		[max-args (if (procedure-vehicle? vehicle)
			      (procedure-vehicle-max-args vehicle)
			      0)])
	    (when (> max-arity 0)
	      ;; emit declaration of argument stack 
	      (fprintf port "~aScheme_Object * arg[~a];\n" 
		       vm->c:indent-spaces 
		       max-arity))
	    (when (> max-args 0)
	      ;; emit declaration of global variables for argument passing
	      (let loop ([n 0])
		(unless (= n max-args)
		  (fprintf port "~aregister long reg~a;\n" vm->c:indent-spaces n)
		  (loop (+ n 1)))))
	    (when (> max-arity 0)
	      ;; tail-buffer-setup
	      (fprintf port "~aScheme_Object ** tail_buf;\n"
		       vm->c:indent-spaces)))

	  (when local-vars-at-top?
	    (for-each
	     (lambda (L)
	       (let ([locals (code-local-vars (get-annotation L))])
		 (vm->c:emit-local-variable-declarations! locals vm->c:indent-spaces port)))
	     (vehicle-lambdas vehicle)))
	  
	  ;; emit jump to function...
	  (when (> (vehicle-total-labels vehicle) 1)
	    ;; emit switch dispatcher
	    (fprintf port "~aswitch(MZC_PARAM_TO_SWITCH(void_param))\n~a{ " 
		     vm->c:indent-spaces
		     vm->c:indent-spaces )
	    (let loop ([n 0])
	      (when (and (zero? (modulo n 3))
			 (not (= n (compiler:get-label-number))))
		(fprintf port "\n~a~a" vm->c:indent-spaces vm->c:indent-spaces))
	      (if (= n (sub1 (vehicle-total-labels vehicle)))
		  (fprintf port "default: goto FGN~a;" n)
		  (begin
		    (fprintf port "case ~a: goto FGN~a;" n n)
		    (loop (add1 n)))))
	    (fprintf port "\n~a}\n" vm->c:indent-spaces))))

      (define vm->c:emit-vehicle-epilogue
	(lambda (port number)
	  (fprintf port "} /* end of vehicle # ~a */\n" number)))

      ;; Will be expanded to hold environments, perhaps, etc.
      (define vm->c:convert-type-definition
	(lambda (rep)
	  (cond
	   [(rep:atomic? rep) (case (rep:atomic-type rep)
				[(scheme-object) "Scheme_Object *"]
				[(scheme-bucket) "Scheme_Bucket *"]
				[(scheme-per-load-static) "struct Scheme_Per_Load_Statics *"]
				[(label) "int"]
				[(prim) "Scheme_Primitive_Closure_Post"]
				[(prim-empty) "Scheme_Primitive_Proc"]
				[(prim-case) "Scheme_Primitive_Closure_Post"]
				[(prim-case-empty) "Scheme_Primitive_Proc"]
				[(begin0-saver) "_Scheme_Begin0_Rec"]
				[(wcm-saver) "_Scheme_WCM_Rec"]
				[else (compiler:internal-error 
				       #f
				       (format
					"vm->c:convert-type-definition: ~a not valid atomic type"
					(rep:atomic-type rep)))])]
	   [(rep:pointer? rep)
	    (string-append (vm->c:convert-type-definition (rep:pointer-to rep))
			   " *")]
	   [(rep:struct? rep)
	    (format "struct ~a" (vm->c:convert-symbol (rep:struct-name rep)))]
	   [else (compiler:internal-error 
		  #f
		  (format "vm->c:convert-type-definition: ~a not a valid representation" rep))])))

      ;; must handle structs as well as atomic types
      (define vm->c:type-definition->malloc
	(lambda (rep)
	  (let ([s (if (rep:struct? rep)
		       (string-append "struct " (vm->c:convert-symbol (rep:struct-name rep)))
		       (vm->c:convert-type-definition rep))]
		[tagged (if (let tagged? ([rep rep])
			      (or (and (rep:atomic? rep) 
				       (memq (rep:atomic-type rep)
					     '(prim prim-empty prim-case prim-case-empty)))
				  (and (rep:struct? rep)
				       (pair? (rep:struct-fields rep))
				       (tagged? (car (rep:struct-fields rep))))
				  (and (rep:struct-field? rep)
				       (tagged? (rep:struct-field-rep rep)))))
			    "_tagged"
			    "")])
	  (format "(~a *)scheme_malloc~a(sizeof(~a))" s tagged s))))

      (define vm->c:emit-local-variable-declarations!
	(lambda (locals indent port)
	  (let loop ([locals (set->list locals)])
	    (if (null? locals)
		(void)
		(let* ([bound (car locals)]
		       [rep (binding-rep (get-annotation bound))])
		  (fprintf port "~a~a ~a;\n"
			   indent
			   (vm->c:convert-type-definition rep)
			   (vm->c:convert-symbol (zodiac:binding-var bound)))
		  (loop (cdr locals)))))))

      (define vm->c:emit-local-bucket-declarations!
	(lambda (globals indent top-level? port)
	  (for-each 
	   (lambda (var)
	     (cond
	      [(const:per-load-statics-table? var)
	       (unless top-level?
		 (fprintf port "~aScheme_Per_Load_Statics * PLS;\n"
			  indent))]
	      [else
	       (fprintf port "~aScheme_Bucket * G~a;\n"
			indent
			(vm->c:convert-symbol (mod-glob-cname var)))]))
	   (set->list globals))))

      (define vm->c:emit-bucket-lookups!
	(lambda (globals indent port)
	  (for-each 
	   (lambda (var)
	     (unless (const:per-load-statics-table? var)
	       (let* ([name (vm->c:convert-symbol (mod-glob-cname var))]
		      [et? (mod-glob-exp-time? var)]
		      [ed? (mod-glob-exp-def? var)]
		      [position (mod-glob-position var)]
		      [mod (mod-glob-modname var)]
		      [in-mod? (mod-glob-in-module? var)]
		      [var (mod-glob-varname var)]
		      [modidx (and (not (symbol? mod))
				   (compiler:get-module-path-constant mod))]
		      [mod-local (and mod (not (symbol? mod)) (not modidx))]
		      [mod-far (and mod (or (symbol? mod) modidx))])
		 (fprintf port "~aG~a = scheme_~a~a~a_bucket(~a~a~a, ~a~a~a);\n"
			  indent 
			  name 
			  (if et? "exptime_" "")
			  (if ed? "expdef_" "")
			  (if mod-far "module" "global")
			  (if mod-far
			      (if (symbol? mod)
				  (vm->c:make-symbol-const-string (compiler:get-symbol-const! #f mod))
				  (format
				   "~a~a"
				   (cond
				    [(varref:has-attribute? modidx varref:per-load-static) "PLS->"]
				    [else "S."])
				   (vm->c:convert-symbol (zodiac:varref-var modidx))))
			      "")
			  (if mod-far ", " "")
			  (vm->c:make-symbol-const-string (compiler:get-symbol-const! #f var))
			  (if mod-far (or position -1) "")
			  (if mod-far ", " "")
			  (if (or mod-local in-mod?) "env" "SCHEME_CURRENT_ENV(pr)")))))
	   (set->list globals))))

      (define binding-boxed? binding-mutable?)

      (define vm->c:extract-arguments-into-variables!
	(lambda (args normal? get-rep get-dest dest-boxed? get-src get-cast has-default? indent port)
					; Reverse order for the sake of noticing default arguments
	  (let loop ([args (reverse args)] [n (sub1 (length args))] [last? #t])
	    (unless (null? args)
	      (let* ([has-default? (has-default? n)]
		     [argv-n
		      (lambda ()
			(if has-default?
			    (format "((argc > ~a) ? ~a : (arg_set_level = ~a, scheme_undefined))" n (get-src n) n)
			    (get-src n)))])
		(cond
		 [(or normal? (not last?))
		  (fprintf port "~a~a = " indent (get-dest n))
		  (if (dest-boxed? n)
		      ;; if the binding is mutable, we need to make a box and fill it with
		      ;; the correct value
		      (let ([rep (get-rep n)])
			(fprintf port "~ascheme_malloc(sizeof(~a));\n" 
				 (get-cast n #f)
				 (vm->c:convert-type-definition
				  (rep:pointer-to rep)))
			(fprintf port "~a*(~a)~a = (~a)~a;\n"
				 indent
				 (vm->c:convert-type-definition rep)
				 (get-dest n) 
				 (vm->c:convert-type-definition (rep:pointer-to rep))
				 (argv-n)))
		      
		      (fprintf port "~a~a;\n" (get-cast n #t) (argv-n)))
		  (loop (cdr args) (sub1 n) #f)]
		 
		 [else ; the rest get pulled into a list
		  (when (dest-boxed? n)
		    (fprintf port
			     "~a~a = ~ascheme_malloc(sizeof(Scheme_Object *));\n"
			     indent
			     (get-dest n)
			     (get-cast n #f)))
		  (fprintf port 
			   "~a~a~a = ~ascheme_build_list_offset(argc, argv, ~a);\n"
			   indent
			   (if (dest-boxed? n)
			       "*(Scheme_Object * *)" 
			       "")
			   (get-dest n)
			   (if (dest-boxed? n)
			       ""
			       (get-cast n #t))
			   n)

		  (loop (cdr args) (sub1 n) #f)]))))))

      (define vm->c:pack-global-registers!
	(lambda (L which indent port)
	  (let* ([arglist (list-ref (zodiac:case-lambda-form-args L) which)]
		 [args (zodiac:arglist-vars arglist)])
	    (vm->c:extract-arguments-into-variables!
	     args
	     (zodiac:list-arglist? arglist)
	     (lambda (n) "") ; rep not used since never boxed
	     (lambda (n) (format "reg~a" n))
	     (lambda (n) #f) ; never boxed
	     (lambda (n) (format "argv[~a]" n))
	     (lambda (n deref) "(long)")
	     (lambda (n) #f)
	     indent port))))

      (define vm->c:emit-private-box-initializations
					; Currently, each is filled with undefined, but specialized representations
					;  will require something different
	(lambda (bindings indent port)
	  (for-each
	   (lambda (binding)
	     (let* ([rep (binding-rep (get-annotation binding))]
		    [derep (rep:pointer-to rep)])
	       (fprintf port "~a~a = (~a)~a;\n~a*(~a) = scheme_undefined;\n"
			indent
			(vm->c:convert-symbol (zodiac:binding-var binding))
			(vm->c:convert-type-definition rep)
			(vm->c:type-definition->malloc derep)
			
			indent
			(vm->c:convert-symbol (zodiac:binding-var binding)))))
	   bindings)))

      (define vm->c:emit-undefines
	(lambda (undefines indent port)
	  (for-each
	   (lambda (name)
	     (fprintf port "#~aundef ~a\n"
		      indent name))
	   undefines)))

      (define vm->c:emit-function-prologue
	(lambda (L port)
	  (let* ([code (get-annotation L)]
		 [label (closure-code-label code)])
	    (if (= 1 (length (zodiac:case-lambda-form-bodies L)))
		(values 1 #f)
		(begin
		  ;; The foreign entry label      
		  (fprintf port "FGN~a:\n" label)
		  (let loop ([args (zodiac:case-lambda-form-args L)][i 0])
		    (if (null? args)
			(begin
			  (fprintf port "~a~ascheme_case_lambda_wrong_count(~s, argc, argv, ~a, ~a"
				   vm->c:indent-spaces vm->c:indent-spaces
				   (vm->c:extract-inferred-name (closure-code-name code))
				   (if (procedure-code-method? code) "1" "0")
				   (length (zodiac:case-lambda-form-args L)))
			  (let loop ([l (zodiac:case-lambda-form-args L)])
			    (unless (null? l)
			      (let-values ([(min-arity max-arity)
					    (compiler:formals->arity (car l))])
				(fprintf port ", ~a, ~a" min-arity max-arity)
				(loop (cdr l)))))
			  (fprintf port ");\n")
			  (fprintf port "~a~areturn NULL;\n"
				   vm->c:indent-spaces vm->c:indent-spaces)
			  (values i #t))
			(let ([a (car args)])
			  (cond
			   [(zodiac:sym-arglist? a)
			    (fprintf port "~a~agoto FGN~ac~a;\n" 
				     vm->c:indent-spaces vm->c:indent-spaces
				     label
				     i)
			    (values (add1 i) #t)]
			   [(zodiac:list-arglist? a)
			    (fprintf port "~a~aif (argc == ~a) goto FGN~ac~a;\n"
				     vm->c:indent-spaces vm->c:indent-spaces
				     (length (zodiac:arglist-vars a))
				     label
				     i)
			    (loop (cdr args) (add1 i))]
			   [else
			    (fprintf port "~a~aif (argc >= ~a) goto FGN~ac~a;\n"
				     vm->c:indent-spaces vm->c:indent-spaces
				     (sub1 (length (zodiac:arglist-vars a)))
				     label
				     i)
			    (loop (cdr args) (add1 i))])))))))))

      (define vm->c:emit-extract-env-variables
	(lambda (code vars indent port)
	  ;; now pull environment variables into registers
	  ;; this is easy because of the way we've set up environments
	  (let loop ([vars vars][undefines null])
	    (if (null? vars)
		undefines
		(let* ([var (if (pair? vars) (car vars) vars)]
		       [vname (zodiac:binding-var var)]
		       [name (vm->c:convert-symbol vname)]
		       [fname (rep:find-field (closure-code-rep code) vname)])
		  (fprintf port (if (compiler:option:unpack-environments)
				    "~a~a = env->~a;\n"
				    "#~adefine ~a env->~a\n")
			   indent
			   name
			   fname)
		  (let ([undefines (if (compiler:option:unpack-environments)
				       undefines
				       (cons name undefines))])
		    (if (pair? vars)
			(loop (cdr vars) undefines)
			undefines)))))))

      (define vm->c:emit-extract-bucket-variables
	(lambda (code vars indent port)
	  ;; pull bucket variables into registers
	  (let loop ([vars vars][undefines null])
	    (if (null? vars)
		undefines
		(let ([var (car vars)])
		  (cond
		   [(const:per-load-statics-table? var)
		    (begin
		      (fprintf port 
			       (if (compiler:option:unpack-environments)
				   "~aPLS = env->pls;\n"
				   "#~adefine PLS env->pls\n")
			       indent)
		      (loop (cdr vars)
			    (if (compiler:option:unpack-environments)
				undefines
				(cons "PLS" undefines))))]
		   [else
		    (let* ([vname (mod-glob-cname var)]
			   [name (vm->c:convert-symbol vname)]
			   [fname (rep:find-field (closure-code-rep code) vname)])
		      (fprintf port 
			       (if (compiler:option:unpack-environments)
				   "~aG~a = env->~a;\n"
				   "#~adefine G~a env->~a\n")
			       indent
			       name
			       fname)
		      (loop (cdr vars)
			    (if (compiler:option:unpack-environments)
				undefines
				(cons (string-append "G" name) undefines))))]))))))
      
      (define vm->c:emit-case-prologue
	(lambda (L which pre-decl lsuffix indent port)
	  (let* ([code (get-annotation L)]
		 [case-code (list-ref (procedure-code-case-codes code) which)]
		 [label (closure-code-label code)]
		 [undefines null]
		 [used-free-set
					; Only unpack anchors if they're captured
		  (let* ([free-set (code-free-vars case-code)]
			 [free-list (set->list free-set)]
			 [captured-list (set->list (code-captured-vars code))]
			 [uncaptured-anchor-set
			  (list->set
			   (let loop ([l free-list])
			     (if (null? l)
				 null
				 (let ([zb (car l)])
				   (let ([a (binding-anchor (get-annotation zb))])
				     (if (and a (not (member zb captured-list)))
					 (cons a (loop (cdr l)))
					 (loop (cdr l))))))))])
		    (set-minus free-set uncaptured-anchor-set))])
					; The foreign entry label      
	    (fprintf port "FGN~a~a:\n" label lsuffix)
					; Pull arguments to global registers      
	    (vm->c:pack-global-registers! L which indent port)

					; The local entry label
	    (fprintf port "LOC~a~a:\n" label lsuffix)
	    (pre-decl)
	    (unless local-vars-at-top?
	      (vm->c:emit-local-variable-declarations! (code-local-vars case-code) indent port))

	    (when (compiler:option:unpack-environments)
	      (vm->c:emit-local-variable-declarations! used-free-set indent port)
	      (vm->c:emit-local-bucket-declarations! (code-global-vars case-code) indent #f port))
	    
	    (let ([r (closure-code-rep code)])
	      (when r
		;; (fprintf port "~aconst ~a * env;\n" indent (vm->c:convert-type-definition r))
		(fprintf port "#~adefine env MZC_ENV_POINTER(~a, ~a, void_param)\n" 
			 indent 
			 (vm->c:convert-type-definition r)
			 (vm->c:convert-type-definition (closure-code-alloc-rep code)))))
	    
	    ;; Registers into local vars
	    (let* ([args (zodiac:arglist-vars (list-ref (zodiac:case-lambda-form-args L) which))])
	      (vm->c:extract-arguments-into-variables!
	       args
	       #t ; since regN already builds lists as appropriate
	       (lambda (n) (binding-rep (get-annotation (list-ref args n))))
	       (lambda (n) (vm->c:convert-symbol (zodiac:binding-var (list-ref args n))))
	       (lambda (n) (binding-boxed? (get-annotation (list-ref args n))))
	       (lambda (n) (format "reg~a" n))
	       (lambda (n deref) (format "(~a)"
					 (vm->c:convert-type-definition
					  (let* ([binding (get-annotation (list-ref args n))]
						 [rep (binding-rep binding)])
					    (if (and deref (binding-boxed? binding))
						(rep:pointer-to rep)
						rep)))))
	       (lambda (n) #f)
	       indent port))

					; reduce register pressure by doing all the env calculations
					; after the args have been done
					; equate the local registers with the global argument registers
					; starting with the env
	    #|
	    (let ([r (closure-code-rep code)])
	      (when r
		(fprintf port "~aenv = (~a *)void_param;\n"
			 indent
			 (vm->c:convert-type-definition r))))
	    |#

					; now pull environment variables into registers
	    (set! undefines
		  (append (vm->c:emit-extract-env-variables
			   code
			   (set->list used-free-set)
			   indent port)
			  undefines))

					; pull bucket variables into registers
	    (set! undefines
		  (append (vm->c:emit-extract-bucket-variables
			   code
			   (set->list (code-global-vars case-code))
			   indent port)
			  undefines))

	    (when (case-code-has-continue? case-code)
	      (fprintf port "~awhile(1)\n" indent))

	    undefines)))

      (define vm->c:emit-case-epilogue
	(lambda (code which undefines indent port)
	  (fprintf port "#~aundef env\n" indent)
	  (vm->c:emit-undefines undefines indent port)))

      (define vm->c:emit-function-epilogue
	(lambda (code close port)
	  (fprintf port "~a~a /* end of function body ~a */\n" 
		   vm->c:indent-spaces close (closure-code-label code))))

      (define vm->c:convert-symbol
	(lambda (sym)
	  (compiler:clean-string (symbol->string sym))))

      (define vm->c:convert-char
	(lambda (char)
	  (if ((char->integer char) . > . 127)
	      (char->integer char)
	      (format
	       "'~a'"
	       (cond 
		[(char=? char #\tab) "\\t"]
		[(char=? char #\newline) "\\n"]
		[(char=? char #\return) "\\r"]
		[(char=? char #\space) " "]
		[(or (char-alphabetic? char) (char-numeric? char)) (string char)]
		[else (let ([text (number->string (char->integer char) 8)])
			(string-append "\\"
				       (make-string (- 3 (string-length text)) #\0)
				       text))])))))

      (define vm->c:convert-special-constant
	(lambda (ast)
	  (cond
	   [(void? (syntax-e (zodiac:zodiac-stx ast))) "scheme_void"]
	   [(undefined? (syntax-e (zodiac:zodiac-stx ast))) "scheme_undefined"]
	   [else (compiler:internal-error 
		  #f 
		  (format
		   "vm->c:convert-special-constant: ~a not correct" ast))])))
      

      (define vm->c:block-statement?
	(one-of vm:if? vm:sequence?))

      (define vm->c:extract-inferred-name
	(let ([nullsym (string->symbol "NULL")])
	  (lambda (var)
	    (cond
	     [(list? var)
	      (if (= (length var) 1)
		  (vm->c:extract-inferred-name (car var))
		  nullsym)]
	     [(zodiac:binding? var)
	      (symbol->string (zodiac:binding-orig-name var))]
	     [(zodiac:bound-varref? var)
	      (vm->c:extract-inferred-name (zodiac:bound-varref-binding var))]
	     [(zodiac:varref? var)
	      (symbol->string (zodiac:varref-var var))]
	     [(not var) nullsym]
	     [else (compiler:internal-error
		    #f
		    (format "vm->c:extract-inferred-name: bad var type: ~a"
			    var))]))))

      (define single-arity?
	(one-of vm:global-varref?
		vm:local-varref?
		vm:static-varref?
		vm:primitive-varref?
		vm:symbol-varref?
		vm:struct-ref?
		vm:deref?
		vm:ref?
		vm:cast?
		vm:immediate?))

      (define is-primitive?
	(one-of primitive? primitive-closure?))

      (define vm->c-expression
	(lambda (ast code port indent-level no-seq-braces? top_level_n)
	  (let process ([ast ast] [indent-level indent-level] [own-line? #t] [braces? (not no-seq-braces?)])
	    (letrec ([emit-indentation (lambda () (display
						   (make-string indent-level #\ )
						   port))]
		     [indent (lambda () (+ indent-level vm->c:indent-by))]
		     [emit (lambda s (apply fprintf (cons port s)))]
		     [emit-expr (lambda s
				  (when own-line? (emit-indentation))
				  (apply emit s))]
		     [emit-macro-application
		      (lambda (ast)
			(let ([args (vm:macro-apply-args ast)])
			  (emit "~a(" (vm:macro-apply-name ast))
			  (process (vm:macro-apply-primitive ast) indent-level #f #f)
			  (for-each (lambda (a) 
				      (emit ", ~a" (vm->c:convert-symbol (zodiac:binding-var a))))
				    args)
			  (emit ")")))])

	      (cond
	       
	       ;; (%sequence V ...) -> { M; ... }
	       [(vm:sequence? ast)
		(let* ([seq (vm:sequence-vals ast)])
		  (when braces? (emit-indentation) (emit "{\n"))
		  (for-each (lambda (v)
			      (process v (indent) #t #t)
			      (unless (vm->c:block-statement? v) (emit ";\n")))
			    seq)
		  (when braces? (emit-indentation) (emit "}\n")))]
	       
	       ;; (if R (sequence V) (sequence V)) ->
	       ;;    if (!SCHEME_FALSEP(A)) { V ... } else { V ...}
	       [(vm:if? ast)
		(emit-indentation)
		(let iloop ([ast ast])
		  (let ([test (vm:if-test ast)]
			[then (vm:if-then ast)]
			[else (vm:if-else ast)])
		    
		    (emit "if (")
		    (let ([direct? (and (vm:macro-apply? test)
					(vm:macro-apply-bool? test))])
		      (if direct?
			  (emit-macro-application test)
			  (begin
			    (emit "!SCHEME_FALSEP(")
			    (process test indent-level #f #t)
			    (emit ")"))))
		    (emit ")\n")
		    (process (vm:if-then ast) indent-level #t #t)
		    (let ([else-vals (vm:sequence-vals else)])
		      (cond 
		       [(and (= 1 (length else-vals))
			     (vm:if? (car else-vals)))
			(emit-indentation) (emit "else ")
			(iloop (car else-vals))]
		       [(not (null? else-vals))
			(emit-indentation) (emit "else\n")
			(process (vm:if-else ast) indent-level #f #t)]
		       [else (void)]))))]
	       
	       ;; begin0 stuff
	       [(vm:begin0-mark!? ast)
		(let ([var (vm->c:convert-symbol
			    (vm:local-varref-var (vm:begin0-mark!-var ast)))])
		  (emit-indentation)
		  (emit "~a.val = " var)
		  (process (vm:begin0-mark!-val ast) indent-level #f #t))]
	       [(vm:begin0-setup!? ast)
		(let ([var (vm->c:convert-symbol
			    (vm:local-varref-var (vm:begin0-setup!-var ast)))])
		  (emit-indentation)
		  (emit "if (~a.val == SCHEME_MULTIPLE_VALUES) {\n" var)
		  (emit-indentation)
		  (emit "  Scheme_Thread *pr = scheme_current_thread;\n")
		  (emit-indentation)
		  (emit "  ~a.array = pr->ku.multiple.array;\n" var)
		  (emit-indentation)
		  (emit "  ~a.count = pr->ku.multiple.count;\n" var)
		  (emit-indentation)
		  (emit "  SCHEME_DETATCH_MV_BUFFER(~a.array, pr);\n" var)
		  (emit-indentation)
		  (emit "} else ~a.array = NULL" var))]
	       [(vm:begin0-extract? ast)
		(let ([var (vm->c:convert-symbol
			    (vm:local-varref-var (vm:begin0-extract-var ast)))])
		  (emit "(scheme_current_thread->ku.multiple.array = ~a.array," var)
		  (emit " scheme_current_thread->ku.multiple.count = ~a.count, " var)
		  (emit " ~a.val)" var))]

	       ;; single value: (set! L R) -> L = R;
	       ;; multiple value:
	       [(vm:set!? ast)
		(let* ([process-target!
			(lambda (target)
			  (let ([type (car target)]
				[target (cdr target)])
			    (cond
			     [(eq? type target-type:lexical) 
			      (process target indent-level #f #t)]
			     [(eq? type target-type:global)
			      (let ([bucket-name (vm->c:convert-symbol (mod-glob-cname target))])
				(emit "G~a->val" bucket-name))]
			     [else (compiler:internal-error 
				    #f
				    (format "~a: bad set! target type" type))])))]
		       [process-set!
			(lambda (target val process-val?)
			  (let ([mode (vm:set!-mode ast)])
			    (if mode
				(begin
				  (emit "scheme_set_global_bucket(~s, " (car mode))
				  (emit "G~a, " (vm->c:convert-symbol (mod-glob-cname (cdr target))))
				  (if process-val? 
				      (process val indent-level #f #t)
				      (emit val))
				  (emit ", ~a)" (cadr mode)))
				(begin
				  (process-target! target)
				  (emit " = ")
				  (if process-val? 
				      (process val indent-level #f #t)
				      (emit val))))))]
		       [vars (vm:set!-vars ast)]
		       [val (vm:set!-val ast)]
		       [num-to-set (length vars)]
		       [return-arity
			(or (and (single-arity? val)
				 1)
			    (and (vm:apply? val) 
				 (vm:apply-prim val)
				 (let ([proc (global-defined-value* (vm:apply-prim val))])
				   (and
				    (is-primitive? proc)
				    (primitive-result-arity proc))))
			    
			    )])
		  (emit-indentation)
		  (let ([return-arity-ok?
			 (and return-arity
			      (number? return-arity)
			      (= return-arity num-to-set))])
		    (if (= num-to-set 1)		      
			
			(process-set! (car vars) val #t)
			
			(begin
			  (emit "{ Scheme_Object * res; res = ")
			  (process val indent-level #f #t)
			  (emit "; ")
			  (unless return-arity-ok?
			    (emit "CHECK_MULTIPLE_VALUES(res, ~a);" num-to-set))
			  (emit "}")
			  (if (not (null? vars))
			      (emit "\n"))
			  (let aloop ([vars vars] [n 0])
			    (unless (null? vars)
			      (emit-indentation)
			      (process-set! (car vars) (format "scheme_multiple_array[~a]" n) #f)
			      (emit ";\n")
			      (aloop (cdr vars) (+ n 1))))
			  ))))]

	       [(or (vm:global-prepare? ast)
		    (vm:global-lookup? ast)
		    (vm:global-assign? ast)
		    (vm:safe-vector-ref? ast))
		(let-values ([(get-vec get-pos proc)
			      (cond
			       [(vm:global-prepare? ast)
				(values vm:global-prepare-vec
					vm:global-prepare-pos
					"MZC_GLOBAL_PREPARE")]
			       [(vm:global-lookup? ast)
				(values vm:global-lookup-vec
					vm:global-lookup-pos
					"MZC_GLOBAL_LOOKUP")]
			       [(vm:global-assign? ast)
				(values vm:global-assign-vec
					vm:global-assign-pos
					"MZC_GLOBAL_ASSIGN")]
			       [(vm:safe-vector-ref? ast)
				(values vm:safe-vector-ref-vec
					vm:safe-vector-ref-pos
					"MZC_KNOWN_SAFE_VECTOR_REF")])])
		  (emit "~a(" proc)
		  (process (get-vec ast) indent-level #f #t)
		  (emit ", ~a" (get-pos ast))
		  (when (vm:global-assign? ast)
		    (emit ", ")
		    (process (vm:global-assign-val ast) indent-level #f #t))
		  (emit ")"))]

	       ;; (%args A ...) -> arg[0] = A; ...
	       [(vm:args? ast)
		;; skip tail_buf setup if no args
		(when (and (eq? arg-type:tail-arg (vm:args-type ast))
			   (not (null? (vm:args-vals ast))))
		  (emit-indentation)
		  (emit "tail_buf = scheme_tail_apply_buffer_wp(~a, scheme_current_thread);\n"
			(length (vm:args-vals ast))))
		(if (null? (vm:args-vals ast))
		    (emit-indentation)
		    (let arloop ([n 0] [args (vm:args-vals ast)])
		      (unless (null? args)
			(emit-indentation)
			(let ([argtype (vm:args-type ast)])
			  (cond
			   [(eq? arg-type:arg argtype) (emit "arg[~a] = " n)]
			   [(eq? arg-type:tail-arg argtype) (emit "tail_buf[~a] = " n)]
			   [(eq? arg-type:register argtype) (emit "reg~a = (long)" n)]
			   [else (compiler:internal-error 
				  #f (format "vm->c: ~a unknown arg type" (vm:args-type ast)))]))
			;; (emit "DEBUG_CHECK(") ;; DEBUGGING
			(process (car args) indent-level #f #t)
			;; (emit ")") ;; DEBUGGING
			(unless (null? (cdr args))
			  (emit ";\n"))
			(arloop (add1 n) (cdr args)))))]

	       [(vm:register-args? ast)
		(let ([vars (vm:register-args-vars ast)]
		      [vals (vm:register-args-vals ast)])
		  (let raloop ([vars vars][vals vals])
		    (let ([var (car vars)]
			  [val (car vals)])
		      (emit-indentation)
		      (emit "~a = " (vm->c:convert-symbol (zodiac:binding-var var)))
		      (process val indent-level #f #f)
		      (unless (null? (cdr vars))
			(emit ";\n")
			(raloop (cdr vars) (cdr vals))))))]

	       ;; (alloc ) -> malloc
	       ;; a bit complicated
	       [(vm:alloc? ast)
		(emit (vm->c:type-definition->malloc (vm:alloc-type ast)))]

	       ;; (make-closure) -> _scheme_make_c_closure
	       [(vm:make-procedure-closure? ast)
		(emit "_scheme_make_c_proc_closure~a(vehicle_~a, " 
		      (if (vm:make-procedure-closure-empty? ast)
			  "_empty"
			  "")
		      (vm:make-procedure-closure-vehicle ast))
		(process (vm:make-closure-closure ast) indent-level #f #t)
		(emit ", ~s, ~a, ~a, ~a)" 
		      (vm->c:extract-inferred-name (vm:make-procedure-closure-name ast))
		      (vm:make-procedure-closure-min-arity ast)
		      (vm:make-procedure-closure-max-arity ast)
		      (if (vm:make-procedure-closure-method? ast) "SCHEME_PRIM_IS_METHOD" "0"))]

	       [(vm:make-case-procedure-closure? ast)
		(emit "_scheme_make_c_case_proc_closure~a(vehicle_~a, " 
		      (if (vm:make-case-procedure-closure-empty? ast)
			  "_empty"
			  "")
		      (vm:make-case-procedure-closure-vehicle ast))
		(process (vm:make-closure-closure ast) indent-level #f #t)
		(emit ", ~s, ~a, S.casesArities[~a], ~a)" 
		      (vm->c:extract-inferred-name (vm:make-case-procedure-closure-name ast))
		      (vm:make-case-procedure-closure-num-cases ast)
		      (vm:make-case-procedure-closure-case-arities ast)
		      (if (vm:make-case-procedure-closure-method? ast) "SCHEME_PRIM_IS_METHOD" "0"))]

	       [(vm:deref? ast)
		(emit "(*")
		(process (vm:deref-var ast) indent-level #f #t)
		(emit ")")]

	       [(vm:ref? ast)
		(emit "(&")
		(process (vm:ref-var ast) indent-level #f #t)
		(emit ")")]
	       
					; optimize (*X).Y to X->Y
	       [(vm:struct-ref? ast)
		(let ([var (vm:struct-ref-var ast)])
		  (if (vm:deref? var)
		      (begin
			(process (vm:deref-var var) indent-level #f #t)
			(emit "->"))
		      (begin
			(process (vm:struct-ref-var ast) indent-level #f #t)
			(emit ".")))
		  (emit "~a" (vm->c:convert-symbol (vm:struct-ref-field ast))))]

	       [(vm:cast? ast)
		(emit "(")
		(emit (vm->c:convert-type-definition (vm:cast-rep ast)))
		(emit ")(")
		(process (vm:cast-val ast) indent-level #f #t)
		(emit ")")]

	       [(vm:check-global? ast)
		(emit-expr (format "CHECK_GLOBAL_BOUND(G~a)"
				   (vm->c:convert-symbol
				    (mod-glob-cname (vm:check-global-var ast)))))]

	       ;; with-continuation-mark
	       [(vm:wcm-mark!? ast)
		(emit-expr "scheme_set_cont_mark(")
		(process (vm:wcm-mark!-key ast) indent-level #f #f)
		(emit ", ")
		(process (vm:wcm-mark!-val ast) indent-level #f #f)
		(emit ")")]
	       [(vm:wcm-push!? ast)
		(let ([var (vm->c:convert-symbol
			    (vm:local-varref-var (vm:wcm-push!-var ast)))])
		  (emit-indentation)
		  (emit "scheme_push_continuation_frame(&~a.cf)" var))]
	       [(vm:wcm-pop!? ast)
		(let ([var (vm->c:convert-symbol
			    (vm:local-varref-var (vm:wcm-pop!-var ast)))])
		  (emit-indentation)
		  (emit "scheme_pop_continuation_frame(&~a.cf)" var))]
	       [(vm:wcm-remember!? ast)
		(let ([var (vm->c:convert-symbol
			    (vm:local-varref-var (vm:wcm-remember!-var ast)))])
		  (emit-indentation)
		  (emit "scheme_temp_dec_mark_depth();\n")
		  (emit-indentation)
		  (emit "~a.val = " var)
		  (process (vm:wcm-remember!-val ast) indent-level #f #t)
		  (emit ";\n")
		  (emit-indentation)
		  (emit "scheme_temp_inc_mark_depth()"))]
	       [(vm:wcm-extract? ast)
		(let ([var (vm->c:convert-symbol
			    (vm:local-varref-var (vm:wcm-extract-var ast)))])
		  (emit "~a.val" var))]
	       
	       ;; (continue) -> continue;
	       [(vm:continue? ast)
		(unless (compiler:option:disable-interrupts)
		  (emit-expr "SCHEME_USE_FUEL(1);\n"))
		(emit-expr "continue")]
	       
	       ;; use NULL instead of tail_buf if no args
	       ;; (tail-apply A <argc>) -> return _scheme_tail_apply(A, argc);
	       [(vm:tail-apply? ast)
		(emit-expr "return _scheme_tail_apply_no_copy_wp(")
		(process (vm:tail-apply-closure ast) indent-level #f #t)
		(let ([c (vm:tail-apply-argc ast)])
		  (emit ", ~a, ~a, scheme_current_thread)" c (if (zero? c) "NULL" 'tail_buf)))]
	       
	       ;; (tail-call <label> <closure>) -> void_param = MZC_PRIM_CLS_DATA(<closure>);
	       ;;                                  goto LOC<label>;
	       [(vm:tail-call? ast)
		(when (vm:tail-call-set-env? ast)
		  (emit-indentation)
		  (emit "void_param = MZC_PRIM_CLS_DATA(")
		  (process (vm:tail-call-closure ast) indent-level #f #t)
		  (emit ");\n"))
		;; be nice to threads & user breaks:
		(unless (compiler:option:disable-interrupts)
		  (emit-indentation)
		  (emit "SCHEME_USE_FUEL(1);\n"))
		(emit-indentation)
					; unless its to a variable arity function! ARGH
		(let* ([label (vm:tail-call-label ast)]
		       [l (if (number? label)
			      label
			      (format "~ac~a" (car label) (cdr label)))])
		  (emit "goto LOC~a" l))]

	       ;; (return R) -> return R
	       [(vm:return? ast)
		(emit-indentation)
		(emit "return ")
		(when (vm:return-magic? ast)
		  (emit "MZC_APPLY_MAGIC("))
		(process (vm:return-val ast) indent-level #f #t)
		(when (vm:return-magic? ast)
		  (emit ", ~a)" top_level_n))]

	       ;; fortunately, void contexts can accept any number of values,
	       ;; so there's no need to check for return arity
	       [(vm:void? ast)
		(emit-indentation)
		(when (vm:void-magic? ast)
		  (emit "MZC_APPLY_MAGIC("))
		(process (vm:void-val ast) indent-level #f #t)
		(when (vm:void-magic? ast)
		  (emit ", ~a)" top_level_n))]
	       
	       ;; (global-varref x) --> GLOBAL_VARREF(x)
	       [(vm:global-varref? ast)
		(emit-expr "GLOBAL_VARREF(G~a)"
			   (vm->c:convert-symbol
			    (mod-glob-cname (vm:global-varref-var ast))))]

	       ;; (global-varref x) --> Gx
	       [(vm:bucket? ast)
		(emit-expr "G~a"
			   (vm->c:convert-symbol
			    (mod-glob-cname (vm:bucket-var ast))))]

	       [(vm:per-load-statics-table? ast)
		(emit-expr "PLS")]

	       ;; use apply-known? flag
	       ;; 0 args => pass NULL for arg vector
	       ;; (apply A <argc>) --> _scheme_apply(A, argc, arg)
	       [(vm:apply? ast)
		(emit-expr "")
		(when (vm:apply-simple-tail-prim? ast)
		  (emit "return "))
		(emit "_mzc_~a("
		      (let ([v (global-defined-value* (vm:apply-prim ast))])
			(cond
			 [(and (primitive-closure? v) 
			       (not (memq (object-name v) (internal-tail-chain-prims))))
			  (if (or (vm:apply-multi? ast)
				  (primitive-result-arity v))
			      "direct_apply_primitive_closure_multi"
			      "direct_apply_primitive_closure")]
			 [(and (primitive? v)
			       (not (memq (object-name v) (internal-tail-chain-prims))))
			  (if (or (vm:apply-multi? ast)
				  (primitive-result-arity v))
			      "direct_apply_primitive_multi"
			      "direct_apply_primitive")]
			 [(vm:apply-known? ast) 
			  (if (vm:apply-multi? ast)
			      (if (compiler:option:disable-interrupts)
				  "direct_apply_primitive_closure_multi_fv"
				  "apply_known_prim_closure_multi")
			      (if (compiler:option:disable-interrupts)
				  (if (compiler:option:unsafe)
				      "direct_apply_primitive_closure_multi_fv"
				      "direct_apply_primitive_closure_fv")
				  "apply_known_prim_closure"))]
			 [(vm:apply-multi? ast) "apply_multi"]
			 [else "apply"])))
		(process (vm:apply-closure ast) indent-level #f #t)
		(let ([c (vm:apply-argc ast)])
		  (emit ", ~a, ~a)" c (if (zero? c) "NULL" 'arg)))]

	       ;; Inlined macro-based applications
	       [(vm:macro-apply? ast) 
		(emit-expr "")
		(when (vm:macro-apply-tail? ast)
		  (emit "return "))
		(when (vm:macro-apply-magic? ast)
		  (emit "MZC_APPLY_MAGIC("))
		(when (vm:macro-apply-bool? ast) (emit "(("))
		(emit-macro-application ast)
		(when (vm:macro-apply-bool? ast) (emit ") ? scheme_true : scheme_false)"))
		(when (vm:macro-apply-magic? ast)
		  (emit ", ~a)" top_level_n))]
	       
	       [(vm:call? ast)
		(emit-expr "_scheme_force_value(compiled(MZC_PRIM_CLS_DATA(")
		(process (vm:call-closure ast) indent-level #f #t)
		(emit "), 0, arg))")]

	       ;; (bound-varref x) -> x
	       [(vm:local-varref? ast) 
		(emit-expr (vm->c:convert-symbol
			    (vm:local-varref-var ast)))]
	       
	       ;; (primitive-varref x) -> x->val
	       [(vm:primitive-varref? ast)
		(emit-expr "P.~a"
			   (vm->c:convert-symbol (mod-glob-cname (vm:primitive-varref-var ast))))]
	       
	       ;; (symbol-varref x) -> symbols[x]
	       [(vm:symbol-varref? ast)
		(emit-expr "~a[~a]" 
			   (vm->c:SYMBOLS-name)
			   (vm:symbol-varref-var ast))]

	       ;; (inexact-varref x) -> inexacts[x]
	       [(vm:inexact-varref? ast)
		(emit-expr "~a[~a]" 
			   (vm->c:INEXACTS-name)
			   (vm:inexact-varref-var ast))]

	       [(vm:per-load-static-varref? ast)
		(emit-expr "PLS->~a" (vm->c:convert-symbol (vm:static-varref-var ast)))]
	       
	       [(vm:static-varref? ast)
		(emit-expr "S.~a" (vm->c:convert-symbol (vm:static-varref-var ast)))]
	       
	       ;; (immediate x)
	       [(vm:immediate? ast)
		(let ([tast (vm:immediate-text ast)])
		  (cond 
		   
		   ;;--------------------------------------------------------------
		   ;; CONSTANTS
		   ;;
		   ;; labels
		   [(number? tast)
		    (emit-expr "~a" tast)]
		   
		   [(boolean? (zodiac:zread-object tast))
		    (if (zodiac:zread-object tast)
			(emit-expr "scheme_true")
			(emit-expr "scheme_false"))]
		   
		   [(number? (zodiac:zread-object tast))
		    (emit-expr "scheme_make_integer(~a)" (zodiac:zread-object tast))]
		   
		   [(char? (zodiac:zread-object tast))
		    (emit-expr "scheme_make_character(~a)"
			       (vm->c:convert-char
				(zodiac:zread-object tast)))]
		   
		   [(null? (zodiac:zread-object tast))
		    (emit-expr "scheme_null")]
		   
		   [(eq? (zodiac:zread-object tast) self_modidx)
		    (emit-expr "self_modidx")]
		   
		   [(or (void? (zodiac:zread-object tast))
			(undefined? (zodiac:zread-object tast)))
		    (emit-expr (vm->c:convert-special-constant tast))]
		   
		   [else (compiler:internal-error
			  ast
			  (format "vm->c-expression: ~a not an immediate: ~e" 
				  tast (zodiac:zread-object tast)))]))]
	       
	       [(vm:build-constant? ast)
		(let ([ast (vm:build-constant-text ast)])
		  (cond
		   [(string? (zodiac:zread-object ast))
		    (fprintf port "scheme_make_immutable_sized_char_string((mzchar *)STRING_~a, ~a, 0)" 
			     (const:intern-string (zodiac:zread-object ast))
			     (string-length (zodiac:zread-object ast)))]
		   [(bytes? (zodiac:zread-object ast))
		    (fprintf port "scheme_make_immutable_sized_byte_string((char *)STRING_~a, ~a, 0)" 
			     (const:intern-string (zodiac:zread-object ast))
			     (bytes-length (zodiac:zread-object ast)))]
		   [(symbol? (zodiac:zread-object ast))
		    (let ([s (symbol->string (zodiac:zread-object ast))])
		      (emit-expr "scheme_intern_exact_symbol(~s, ~a)" s (string-length s)))]
		   [(number? (zodiac:zread-object ast))
		    (let process ([num (zodiac:zread-object ast)])
		      (cond
		       ;; NaN, inf
		       [(member num  (list +NaN.0 +inf.0 -inf.0 -0.0)) 
			(emit-expr "scheme_eval_string(\"~a\", env)" num)]
		       ;; complex numbers
		       [(not (eqv? 0 (imag-part num)))
			(emit-expr "scheme_make_complex(")
			(process (real-part num))
			(emit ", ")
			(process (imag-part num))
			(emit ")")]
		       ;; floating point numbers
		       [(inexact? num)
			(emit-expr "scheme_make_double(~a)" num)]
		       ;; integers (fixnums & bignums)
		       [(integer? num)
			(if (vm:fixnum? num) 
			    (emit-expr "scheme_make_integer(~a)" num)
			    (emit-expr "scheme_read_bignum_bytes(\"~a\", 0, 10)" num))]
					; rational numbers
		       [else
			(emit-expr "scheme_make_rational(")
			(process (numerator num))
			(emit ", ")
			(process (denominator num))
			(emit ")")]))]

		   [(void? (zodiac:zread-object ast))
		    (emit "scheme_void")]

		   [(eq? (zodiac:zread-object ast) self_modidx)
		    (emit-expr "self_modidx")]
		   
		   ;; HACK! - abused constants to communicate
		   ;;  a direct call to scheme_make_prim_w_arity
		   [(c-lambda? (zodiac:zread-object ast))
		    (let ([cl (zodiac:zread-object ast)])
		      (emit-expr "scheme_make_prim_w_arity(~a, ~s, ~a, ~a)"
				 (c-lambda-function-name cl)
				 (symbol->string (c-lambda-scheme-name cl))
				 (c-lambda-arity cl)
				 (c-lambda-arity cl)))]
		   
		   ;; HACK! - abused constants to communicate
		   ;;  a direct call to scheme_eval_compiled_string():
		   [(compiled-string? (zodiac:zread-object ast))
		    (let ([cs (zodiac:zread-object ast)])
		      (emit-expr "scheme_eval_compiled_sized_string(STRING_~a, ~a, NULL)"
				 (compiled-string-id cs)
				 (compiled-string-len cs)))]
		   
		   [else (compiler:internal-error
			  ast
			  (format "vm:build-constant: not supported ~a" ast))]))]
	       
	       [else (compiler:internal-error #f (format "vm2c: ~a not supported" ast))])))))))
