racket/collects/compiler/private/vm2c.rkt

1584 lines
54 KiB
Racket

;; 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))])))))))