racket/s/mkgc.ss
Matthew Flatt 58282cbb5f correct GC problem with mark mode
The problem especially affects `enable-object-counts`, where mark mode
can update an old rtd instead of a copy that is already made.

original commit: 31e2df63894d39b005c6b9984745b44409198d02
2020-05-31 17:09:07 -06:00

2488 lines
93 KiB
Scheme

;; This file defines the traversal of objects for the GC and similar
;; purposes. The description supports the generatation of multiple C
;; functions, each specialized to a particular traversal mode, while
;; sharing the overall traversal implementation.
;; Roughy the first half of this file is the semi-declarative
;; specification in Parenthe-C, and the second half is the Parenthe-C
;; compiler that generates C code. The lines between the
;; specification, compiler, and supporting C code in "gc.c" are
;; (unfortunately) not very strict.
;; Code is generated by calling the functions listed here:
(disable-unbound-warning
mkgc-ocd.inc
mkgc-oce.inc
mkvfasl.inc)
;; Currently supported traversal modes:
;; - copy
;; - sweep
;; - mark
;; - self-test : check immediate pointers only for self references
;; - size : immediate size, so does not recur
;; - measure : recurs for reachable size
;; - vfasl-copy
;; - vfasl-sweep
;; For the specification, there are a few declaration forms described
;; below, such as `trace` to declare a pointer-valued field within an
;; object (to be copied in copy mode and swept in sweep mode).
;; Otherwise, the "declaration" nature of the specification is based
;; on selecting code fragments statically via `case-mode` and
;; `case-flag`. Macros that expand to those forms (e.g., `trace-tlc`)
;; provide a further declarative vaneer.
;; Internals:
(disable-unbound-warning
trace-base-types
trace-object-types
trace-macros)
(define trace-base-types '())
(define trace-object-types '())
(define trace-macros (make-eq-hashtable))
;; This macro just makes sure our main specification has a fixed
;; shape:
(define-syntax define-trace-root
(syntax-rules (case-type typed-object case-typedfield)
[(_ (case-type
[type type-stmt ...]
...
[typed-object
(case-typefield
[object-type object-type-stmt ...]
...)]))
(begin
(set! trace-base-types '((type type-stmt ...) ...))
(set! trace-object-types '((object-type object-type-stmt ...) ...)))]))
;; A "trace macro" is non-hygienically expanded:
(define-syntax define-trace-macro
(syntax-rules ()
[(_ (id arg ...) body ...)
(eq-hashtable-set! trace-macros 'id '((arg ...) body ...))]))
;; Primitive actions/declarations, must be used as statements in roughly
;; this order (but there are exceptions to the order):
;; - (space <space>) : target for copy; works as a constraint for other modes
;; - (vspace <vspace>) : target for vfasl
;; - (size <size> [<scale>]) : size for copy; skips rest in size mode
;; - (mark <flag>) : in mark mode, skips rest except counting;
;; possible <flags>:
;; * one-bit : record as one bit per segment; inferred when size matches
;; alignment or for `space-data`
;; * within-segment : alloacted within on segment; can be inferred from size
;; * no-sweep : no need to sweep content (perhaps covered by `trace-now`);
;; inferred for `space-data`
;; * counting-root : check a counting root before pushing to sweep stack
;; - (trace <field>) : relocate for sweep, copy for copy, recur otherwise
;; - (trace-early <field>) : relocate for sweep, copy, and mark; recur otherwise
;; - (trace-now <field>) : direct recur
;; - (trace-early-rtd <field>) : for record types, avoids recur on #!base-rtd
;; - (trace-ptrs <field> <count>) : trace an array of pointerrs
;; - (copy <field>) : copy for copy, ignore otherwise
;; - (copy-bytes <field> <count>) : copy an array of bytes
;; - (copy-flonum <field>) : copy flonum and forward
;; - (copy-flonum* <field>) : copy potentially forwaded flonum
;; - (copy-type <field>) : copy type from `_` to `_copy_`
;; - (count <counter> [<size> [<scale> [<modes>]]]) :
;; uses preceding `size` declaration unless <size>;
;; normally counts in copy mode, but <modes> can override
;; - (as-mark-end <statment> ...) : declares that <statement>s implement counting,
;; which means that it's included for mark mode
;; - (skip-forwarding) : disable forward-pointer installation in copy mode
;; - (assert <expr>) : assertion
;;
;; In the above declarations, nonterminals like <space> can be
;; an identifier or a Parenthe-C expression. The meaning of a plain
;; identifier depends on the nonterminal:
;; - <space> : should be a `space-...` from cmacro
;; - <vspace> : should be a `vspace_...`
;; - <size> : should be a constant from cmacro
;; - <field> : accessor from cmacro, implicitly applied to `_` and `_copy_`
;; Parenthe-C is just what it sounds like: C code written in S-expression
;; form. Use `(<op> <arg> ...)` as usual, and the generated code transforms
;; to infix as appropriate for regonized operators. The statement versus
;; expression distnction is important; primitive declarations must be in
;; statement positions.
;;
;; Statements:
;; - <expr>
;; - <declaration> : like `(space <space>)`, etc., above
;; - (set! <id> <expr>) : renders as `<id> = <expr>;`
;; - (set! <id> <assign-op> <expr>) : renders as `<id> <assign-op> <expr>;`
;; - (cond [<expr> <stmt> ...] ... [else <stmt> ...])
;; - (when <expr> <stmt> ...) : shorthand for `(cond [<expr> <stmt> ...] [else])`
;; - (while :? <expr> <stmt> ...)
;; - (do-while <stmt> ... :? <expr>)
;; - (break)
;; - (define <id> : <type> <expr>) : discarded if <id> is unused
;; - (let* ([<id> : <type> <expr>] ...) <stmt> ...)
;; - (case-mode [<modes> <stmt> ...] ... [else <stmt>]) : static
;; case dispatch based on mode, where <modes> can be one <mode> or
;; a parenthesized sequence of <mode>s
;; - (case-flag <flag> [on <stmt> ...] [off <stmt> ...]) : static dispatch
;; based on a configuration flag
;; - (case-space [<space> <stmt> ...] .... [else <stmt> ...]) : run-time
;; dispatch based on the space of _
;;
;; Expressions:
;; - <id> : a constant from cmacros or a C name
;; - <literal> : a literal number or string
;; - (<field-or-expr> <arg>) : function call, operation use, or field access
;; - (<field-or-expr> <arg> <arg2>) : function call, operation use, or array
;; field access
;; - (<id-or-expr> <arg> <arg> ...) : function call or operation use
;; - (just <expr>) : same as <expr>, sometimes useful when <expr> is a symbol
;; - (cond [<expr> <expr>] ... [else <expr>])
;; - (case-flag <flag> [on <expr>] [off <expr>]) : static dispatch
;; - (cast <type> <expr>)
;; - (array-ref <expr> <expr>)
;;
;; Built-in variables:
;; - _ : object being copied, swept, etc.
;; - _copy_ : target in copy or vfasl mode, same as _ otherwise
;; - _tf_ : type word
;; - _tg_ : target generation
;; - _backreferences?_ : dynamic flag indicating whether backreferences are on
;;
;; Stylistically, prefer constants and fields using the hyphenated
;; names from cmacros instead of the corresponding C name. Use C names
;; for derived functions, like `size_record_inst` or `FIX`.
(define-trace-root
(case-type
[pair
(case-space
[space-ephemeron
(space space-ephemeron)
(vfasl-fail "ephemeron")
(size size-ephemeron)
(copy pair-car)
(copy pair-cdr)
(case-mode
[(copy)
(set! (ephemeron-prev-ref _copy_) NULL)
(set! (ephemeron-next _copy_) NULL)]
[else])
(add-ephemeron-to-pending)
(mark one-bit no-sweep)
(assert-ephemeron-size-ok)
(count countof-ephemeron)]
[space-weakpair
(space space-weakpair)
(vfasl-fail "weakpair")
(try-double-pair copy pair-car
trace pair-cdr
countof-weakpair)]
[else
(space space-impure)
(vspace vspace_impure)
(try-double-pair trace pair-car
trace pair-cdr
countof-pair)])]
[closure
(define code : ptr (CLOSCODE _))
(trace-code-early code)
(cond
[(and-not-as-dirty
(or-assume-continuation
(& (code-type code) (<< code-flag-continuation code-flags-offset))))
;; continuation
(space (cond
[(and-counts (is_counting_root si _)) space-count-pure]
[else space-continuation]))
(vfasl-fail "closure")
(size size-continuation)
(mark one-bit counting-root)
(case-mode
[self-test]
[else
(copy-clos-code code)
(copy-stack-length continuation-stack-length continuation-stack-clength)
(copy continuation-stack-clength)
(trace-nonself continuation-winders)
(trace-nonself continuation-attachments)
(cond
[(== (continuation-stack-length _) scaled-shot-1-shot-flag)]
[else
(case-mode
[sweep
(when (OLDSPACE (continuation-stack _))
(set! (continuation-stack _)
(copy_stack (continuation-stack _)
(& (continuation-stack-length _))
(continuation-stack-clength _))))]
[else])
(count countof-stack (continuation-stack-length _) 1 [sweep measure])
(trace continuation-link)
(trace-return continuation-return-address (continuation-return-address _))
(case-mode
[copy (copy continuation-stack)]
[else
(define stack : uptr (cast uptr (continuation-stack _)))
(trace-stack stack
(+ stack (continuation-stack-clength _))
(cast uptr (continuation-return-address _)))])])])
(count countof-continuation)]
[else
;; closure (not a continuation)
(space
(cond
[(and-counts (is_counting_root si _)) space-count-impure]
[_backreferences?_
space-closure]
[else
(cond
[(& (code-type code) (<< code-flag-mutable-closure code-flags-offset))
space-impure]
[else
space-pure])]))
(vspace vspace_closure)
(when-vfasl
(when (& (code-type code) (<< code-flag-mutable-closure code-flags-offset))
(vfasl-fail "mutable closure")))
(define len : uptr (code-closure-length code))
(size (size_closure len))
(when-mark
(case-space
[space-pure
(mark one-bit counting-root)
(count countof-closure)]
[else
(mark counting-root)
(count countof-closure)]))
(when (or-not-as-dirty
(& (code-type code) (<< code-flag-mutable-closure code-flags-offset)))
(copy-clos-code code)
(trace-ptrs closure-data len))
(pad (when (== (& len 1) 0)
(set! (closure-data _copy_ len) (FIX 0))))
(count countof-closure)])]
[symbol
(space space-symbol)
(vspace vspace_symbol)
(size size-symbol)
(mark one-bit)
(trace/define symbol-value val :vfasl-as (FIX (vfasl_symbol_to_index vfi _)))
(trace-symcode symbol-pvalue val)
(trace-nonself/vfasl-as-nil symbol-plist)
(trace-nonself symbol-name)
(trace-nonself/vfasl-as-nil symbol-splist)
(trace-nonself symbol-hash)
(count countof-symbol)]
[flonum
(space space-data)
(vspace vspace_data)
(size size-flonum)
(mark)
(copy-flonum flonum-data)
(count countof-flonum)
(skip-forwarding)]
[typed-object
(case-typefield
[record
(trace-early-rtd record-type)
;; If the rtd is the only pointer and is immutable, put the record
;; into space-data. If the record contains only pointers, put it
;; into space-pure or space-impure. Otherwise, put it into
;; space-pure-typed-object or space-impure-record. We could put all
;; records into space-{pure,impure}-record or even into
;; space-impure-record, but by picking the target space more
;; carefully, we may reduce fragmentation and sweeping cost.
(define rtd : ptr (record-type _))
(space
(cond
[(and-counts (is_counting_root si _))
space-count-impure]
[(&& (== (record-type-pm rtd) (FIX 1))
(== (record-type-mpm rtd) (FIX 0)))
;; No pointers except for type
space-data]
[(== (record-type-pm rtd) (FIX -1))
;; All pointers
(cond
[_backreferences?_
(cond
[(== (record-type-mpm rtd) (FIX 0))
;; All immutable
space-pure-typed-object]
[else
space-impure-record])]
[else
(cond
[(== (record-type-mpm rtd) (FIX 0))
;; All immutable
space-pure]
[else
space-impure])])]
[else
;; Mixture of pointers and non-pointers
(cond
[(== (record-type-mpm rtd) (FIX 0))
;; All immutable
space-pure-typed-object]
[else
space-impure-record])]))
(vspace (cond
[(is_rtd rtd vfi) vspace_rtd]
[(== (record-type-mpm rtd) (FIX 0)) vspace_pure_typed]
[else vspace_impure_record]))
(vfasl-check-parent-rtd rtd)
(define len : uptr (UNFIX (record-type-size rtd)))
(size (size_record_inst len))
(mark counting-root)
(trace-record rtd len)
(vfasl-set-base-rtd)
(pad (when (or-vfasl
(\|\| (== p_spc space-pure) (\|\| (== p_spc space-impure)
(and-counts (== p_spc space-count-impure)))))
(let* ([ua_size : uptr (unaligned_size_record_inst len)])
(when (!= p_sz ua_size)
(set! (* (cast ptr* (+ (cast uptr (UNTYPE _copy_ type_typed_object)) ua_size)))
(FIX 0))))))
(count-record rtd)]
[vector
;; Assumes vector lengths look like fixnums;
;; if not, vectors will need their own space
(space
(cond
[(& (cast uptr _tf_) vector_immutable_flag)
(cond
[_backreferences?_ space-pure-typed-object]
[else space-pure])]
[else
(cond
[_backreferences?_ space-impure-typed-object]
[else space-impure])]))
(vspace vspace_impure)
(define len : uptr (Svector_length _))
(size (size_vector len))
(mark)
(copy-type vector-type)
(trace-ptrs vector-data len)
(pad (when (== (& len 1) 0)
(set! (vector-data _copy_ len) (FIX 0))))
(count countof-vector)]
[stencil-vector
;; Assumes stencil-vector masks look like fixnums;
;; if not, stencil vectors will need their own space
(space
(cond
[_backreferences?_ space-impure-typed-object]
[else space-impure]))
(vspace vspace_impure)
(define len : uptr (Sstencil_vector_length _))
(size (size_stencil_vector len))
(mark within-segment) ; see assertion
(assert-stencil-vector-size)
(copy-type stencil-vector-type)
(trace-ptrs stencil-vector-data len)
(pad (when (== (& len 1) 0)
(set! (stencil-vector-data _copy_ len) (FIX 0))))
(count countof-stencil-vector)]
[string
(space space-data)
(vspace vspace_data)
(define sz : uptr (size_string (Sstring_length _)))
(size (just sz))
(mark)
(copy-bytes string-type sz)
(count countof-string)]
[fxvector
(space space-data)
(vspace vspace_data)
(define sz : uptr (size_fxvector (Sfxvector_length _)))
(size (just sz))
(mark)
(copy-bytes fxvector-type sz)
(count countof-fxvector)]
[bytevector
(space space-data)
(vspace vspace_data)
(define sz : uptr (size_bytevector (Sbytevector_length _)))
(size (just sz))
(mark)
(copy-bytes bytevector-type sz)
(count countof-bytevector)]
[tlc
(space
(cond
[_backreferences?_ space-impure-typed-object]
[else space-impure]))
(vfasl-fail "tlc")
(size size-tlc)
(mark)
(copy-type tlc-type)
(trace-nonself tlc-ht)
(as-mark-end
(trace-tlc tlc-next tlc-keyval)
(count countof-tlc))]
[box
(space
(cond
[(== (box-type _) type-immutable-box)
(cond
[_backreferences?_ space-pure-typed-object]
[else space-pure])]
[else
(cond
[_backreferences?_ space-impure-typed-object]
[else space-impure])]))
(vspace vspace_impure)
(size size-box)
(mark)
(copy-type box-type)
(trace box-ref)
(count countof-box)]
[ratnum
(space space-data)
(vspace vspace_impure) ; would be better if we had pure, but these are rare
(size size-ratnum)
(copy-type ratnum-type)
(trace-immutable-now ratnum-numerator)
(trace-immutable-now ratnum-denominator)
(mark)
(vfasl-pad-word)
(count countof-ratnum)]
[exactnum
(space space-data)
(vspace vspace_impure) ; same rationale as ratnum
(size size-exactnum)
(copy-type exactnum-type)
(trace-immutable-now exactnum-real)
(trace-immutable-now exactnum-imag)
(mark)
(vfasl-pad-word)
(count countof-exactnum)]
[inexactnum
(space space-data)
(vspace vspace_data)
(size size-inexactnum)
(mark)
(copy-type inexactnum-type)
(copy-flonum* inexactnum-real)
(copy-flonum* inexactnum-imag)
(count countof-inexactnum)]
[bignum
(space space-data)
(vspace vspace_data)
(define sz : uptr (size_bignum (BIGLEN _)))
(size (just sz))
(mark)
(copy-bytes bignum-type sz)
(count countof-bignum)]
[port
(space space-port)
(vfasl-fail "port")
(size size-port)
(mark one-bit)
(copy-type port-type)
(trace-nonself port-handler)
(copy port-ocount)
(copy port-icount)
(trace-buffer PORT_FLAG_OUTPUT port-obuffer port-olast)
(trace-buffer PORT_FLAG_INPUT port-ibuffer port-ilast)
(trace port-info)
(trace-nonself port-name)
(count countof-port)]
[code
(space space-code)
(vspace vspace_code)
(define len : uptr (code-length _)) ; in bytes
(size (size_code len))
(mark one-bit)
(when (and-not-as-dirty 1)
(copy-type code-type)
(copy code-length)
(copy code-reloc)
(trace-nonself code-name)
(trace-nonself code-arity-mask)
(copy code-closure-length)
(trace-nonself code-info)
(trace-nonself code-pinfo*)
(trace-code len))
(count countof-code)]
[thread
(space (cond
[(and-counts (is_counting_root si _)) space-count-pure]
[else space-pure-typed-object]))
(vfasl-fail "thread")
(size size-thread)
(mark one-bit)
(case-mode
[self-test]
[else
(copy-type thread-type)
(when (and-not-as-dirty 1)
(trace-tc thread-tc))])
(count countof-thread)]
[rtd-counts
(space space-data)
(vfasl-as-false "rtd-counts") ; prune counts, since GC will recreate as needed
(size size-rtd-counts)
(mark)
(copy-bytes rtd-counts-type size_rtd_counts)
(count countof-rtd-counts)]
[phantom
(space space-data)
(vfasl-fail "phantom")
(size size-phantom)
(mark)
(copy-type phantom-type)
(copy phantom-length)
(case-mode
[(copy mark)
(as-mark-end
(count countof-phantom)
;; Separate from `count`, because we want to track sizes even
;; if counting is not enabled:
(set! (array-ref (array-ref S_G.bytesof _tg_) countof-phantom)
+=
(phantom-length _)))]
[measure (set! measure_total += (phantom-length _))]
[else])])]))
(define-trace-macro (trace-nonself field)
(case-mode
[self-test]
[else
(trace field)]))
(define-trace-macro (trace-nonself/vfasl-as-nil field)
(case-mode
[vfasl-copy
(set! (field _copy_) Snil)]
[else
(trace-nonself field)]))
(define-trace-macro (try-double-pair do-car pair-car
do-cdr pair-cdr
count-pair)
(case-mode
[copy
;; Try to copy two pairs at a time
(define cdr_p : ptr (Scdr _))
(define qsi : seginfo* NULL)
(cond
[(&& (!= cdr_p _)
(&& (== (TYPEBITS cdr_p) type_pair)
(&& (!= (set! qsi (MaybeSegInfo (ptr_get_segment cdr_p))) NULL)
(&& (-> qsi old_space)
(&& (== (-> qsi space) (-> si space))
(&& (!= (FWDMARKER cdr_p) forward_marker)
(&& (! (-> qsi use_marks))
;; Checking `marked_mask`, too, in
;; case the pair is locked
(! (-> qsi marked_mask)))))))))
(check_triggers qsi)
(size size-pair 2)
(define new_cdr_p : ptr (cast ptr (+ (cast uptr _copy_) size_pair)))
(set! (pair-car _copy_) (pair-car _))
(set! (pair-cdr _copy_) new_cdr_p)
(set! (pair-car new_cdr_p) (pair-car cdr_p))
(set! (pair-cdr new_cdr_p) (pair-cdr cdr_p))
(set! (FWDMARKER cdr_p) forward_marker)
(set! (FWDADDRESS cdr_p) new_cdr_p)
(case-flag maybe-backreferences?
[on (ADD_BACKREFERENCE_FROM new_cdr_p new_p)]
[off])
(count count-pair size-pair 2)]
[else
(size size-pair)
(do-car pair-car)
(do-cdr pair-cdr)
(count count-pair)])]
[else
(size size-pair)
(mark)
(assert (= (constant size-pair) (constant byte-alignment)))
(do-car pair-car)
(do-cdr pair-cdr)
(count count-pair)]))
(define-trace-macro (add-ephemeron-to-pending)
(case-mode
[(sweep mark)
(add_ephemeron_to_pending _)]
[measure
(add_ephemeron_to_pending_measure _)]
[else]))
(define-trace-macro (assert-ephemeron-size-ok)
;; needed for dirty sweep strategy:
(assert (zero? (modulo (constant bytes-per-card) (constant size-ephemeron)))))
(define-trace-macro (assert-stencil-vector-size)
;; needed for within-mark-byte
(assert (< (+ (* (constant stencil-vector-mask-bits) (constant ptr-bytes))
(constant header-size-stencil-vector)
(constant byte-alignment))
(constant bytes-per-segment))))
(define-trace-macro (trace-immutable-now ref)
(when (and-not-as-dirty 1)
(trace-now ref)))
(define-trace-macro (trace-code-early code)
(unless-code-relocated
(case-mode
[(vfasl-sweep)
;; Special relocation handling for code in a closure:
(set! code (vfasl_relocate_code vfi code))]
[else
(trace-early (just code))])))
(define-trace-macro (copy-clos-code code)
(case-mode
[(copy vfasl-copy)
(SETCLOSCODE _copy_ code)]
[(sweep)
(unless-code-relocated
(SETCLOSCODE _copy_ code))]
[(vfasl-sweep)
;; Make the code pointer relative to the base address.
;; It's turned back absolute when loading from vfasl
(define rel_code : ptr (cast ptr (ptr_diff code (-> vfi base_addr))))
(SETCLOSCODE p rel_code)]
[else]))
(define-trace-macro (copy-stack-length continuation-stack-length continuation-stack-clength)
(case-mode
[copy
;; Don't promote general one-shots, but promote opportunistic one-shots
(cond
[(== (continuation-stack-length _) opportunistic-1-shot-flag)
(set! (continuation-stack-length _copy_) (continuation-stack-clength _))
;; May need to recur at end to promote link:
(set! conts_to_promote (S_cons_in space_new 0 new_p conts_to_promote))]
[else
(copy continuation-stack-length)])]
[else
(copy continuation-stack-length)]))
(define-trace-macro (trace/define ref val :vfasl-as vfasl-val)
(case-mode
[(copy measure)
(trace ref)]
[sweep
(define val : ptr (ref _))
(trace (just val))
(set! (ref _) val)]
[vfasl-copy
(set! (ref _copy_) vfasl-val)]
[else]))
(define-trace-macro (trace-symcode symbol-pvalue val)
(case-mode
[sweep
(define code : ptr (cond
[(Sprocedurep val) (CLOSCODE val)]
[else (SYMCODE _)]))
(trace (just code))
(INITSYMCODE _ code)]
[measure]
[vfasl-copy
(set! (symbol-pvalue _copy_) Snil)]
[else
(copy symbol-pvalue)]))
(define-trace-macro (trace-tlc tlc-next tlc-keyval)
(case-mode
[(copy mark)
(define next : ptr (tlc-next _))
(define keyval : ptr (tlc-keyval _))
(case-mode
[copy
(set! (tlc-next _copy_) next)
(set! (tlc-keyval _copy_) keyval)]
[else])
;; If next isn't false and keyval is old, add tlc to a list of tlcs
;; to process later. Determining if keyval is old is a (conservative)
;; approximation to determining if key is old. We can't easily
;; determine if key is old, since keyval might or might not have been
;; swept already. NB: assuming keyvals are always pairs.
(when (&& (!= next Sfalse) (OLDSPACE keyval))
(set! tlcs_to_rehash (S_cons_in space_new 0 _copy_ tlcs_to_rehash)))]
[else
(trace-nonself tlc-keyval)
(trace-nonself tlc-next)]))
(define-trace-macro (trace-record trd len)
(case-mode
[(copy vfasl-copy)
(copy-bytes record-data (- len ptr_bytes))]
[else
;; record-type descriptor was forwarded already
(let* ([num : ptr (case-flag as-dirty?
[on (record-type-mpm rtd)]
[off (record-type-pm rtd)])]
[pp : ptr* (& (record-data _ 0))])
;; Process cells for which bit in pm is set, and quit when pm == 0
(cond
[(Sfixnump num)
;; Ignore bit for already forwarded rtd
(let* ([mask : uptr (>> (cast uptr (UNFIX num)) 1)])
(cond
[(case-flag as-dirty?
[on 0]
[off (== mask (>> (cast uptr -1) 1))])
(let* ([ppend : ptr* (- (cast ptr* (+ (cast uptr pp) len)) 1)])
(while
:? (< pp ppend)
(trace (* pp))
(set! pp += 1)))]
[else
(while
:? (!= mask 0)
(when (& mask 1)
(trace (* pp)))
(set! mask >>= 1)
(set! pp += 1))]))]
[else
(case-flag as-dirty?
[on]
[off
(case-mode
[(sweep self-test)
;; Bignum pointer mask may need forwarding
(trace (record-type-pm rtd))
(set! num (record-type-pm rtd))]
[else])])
(let* ([index : iptr (- (BIGLEN num) 1)]
;; Ignore bit for already forwarded rtd
[mask : bigit (>> (bignum-data num index) 1)]
[bits : INT (- bigit_bits 1)])
(while
:? 1
(do-while
(when (& mask 1)
(trace (* pp)))
(set! mask >>= 1)
(set! pp += 1)
(set! bits -= 1)
;; while:
:? (> bits 0))
(when (== index 0) (break))
(set! index -= 1)
(set! mask (bignum-data num index))
(set! bits bigit_bits)))]))]))
(define-trace-macro (vfasl-check-parent-rtd rtd)
(case-mode
[(vfasl-copy)
(when (is_rtd rtd vfi)
(when (!= _ S_G.base_rtd)
;; Make sure rtd's type is registered firs, but
;; discard the relocated pointer (leaving to sweep)
(cast void (vfasl_relocate_help vfi rtd)))
;; Need parent before child
(vfasl_relocate_parents vfi (record-type-parent _)))]
[(vfasl-sweep)
;; Don't need to save fields of base-rtd
(when (== _ (-> vfi base_rtd))
(let* ([pp : ptr* (& (record-data _ 0))]
[ppend : ptr* (- (cast ptr* (+ (cast uptr pp) (UNFIX (record-type-size rtd)))) 1)])
(while
:? (< pp ppend)
(set! (* pp) Snil)
(set! pp += 1))
(return (size_record_inst (UNFIX (record-type-size rtd))))))
;; Relocation of rtd fields was deferred
(vfasl_relocate vfi (& (record-type _)))]
[else]))
(define-trace-macro (vfasl-set-base-rtd)
(case-mode
[(vfasl-copy)
(when (== _ S_G.base_rtd)
(set! (-> vfi base_rtd) _copy_))]
[else]))
(define-trace-macro (count-record rtd)
(case-mode
[(copy mark)
(as-mark-end
(case-flag counts?
[on
(when S_G.enable_object_counts
(let* ([c_rtd : ptr (cond
[(== _tf_ _) _copy_]
[else rtd])]
[counts : ptr (record-type-counts c_rtd)])
(cond
[(== counts Sfalse)
(let* ([grtd : IGEN (GENERATION c_rtd)])
(set! (array-ref (array-ref S_G.countof grtd) countof_rtd_counts) += 1)
;; Allocate counts struct in same generation as rtd. Initialize timestamp & counts.
(find_room space_data grtd type_typed_object size_rtd_counts counts)
(set! (rtd-counts-type counts) type_rtd_counts)
(set! (rtd-counts-timestamp counts) (array-ref S_G.gctimestamp 0))
(let* ([g : IGEN 0])
(while
:? (<= g static_generation)
(set! (rtd-counts-data counts g) 0)
(set! g += 1)))
(set! (record-type-counts c_rtd) counts)
(set! (array-ref S_G.rtds_with_counts grtd)
;; For max_copied_generation, the list will get copied again in `rtds_with_counts` fixup;
;; meanwhile, allocating in `space_impure` would copy and sweep old list entries causing
;; otherwise inaccessible rtds to be retained
(S_cons_in (cond [(<= grtd max_copied_generation) space_new] [else space_impure])
(cond [(<= grtd max_copied_generation) 0] [else grtd])
c_rtd
(array-ref S_G.rtds_with_counts grtd)))
(set! (array-ref (array-ref S_G.countof grtd) countof_pair) += 1))]
[else
(trace-early (just counts))
(set! (record-type-counts c_rtd) counts)
(when (!= (rtd-counts-timestamp counts) (array-ref S_G.gctimestamp 0))
(S_fixup_counts counts))])
(set! (rtd-counts-data counts _tg_) (+ (rtd-counts-data counts _tg_) 1))))
;; Copies size that we may have already gathered, but needed for counting from roots:
(case-mode
[(copy)
(when (== p_spc space-count-impure) (set! count_root_bytes += p_sz))]
[else])
(count countof-record)]
[off]))]
[else]))
(define-trace-macro (trace-buffer flag port-buffer port-last)
(case-mode
[(copy measure)
(copy port-last)
(copy port-buffer)]
[sweep
(when (& (cast uptr _tf_) flag)
(define n : iptr (- (cast iptr (port-last _))
(cast iptr (port-buffer _))))
(trace port-buffer)
(set! (port-last _) (cast ptr (+ (cast iptr (port-buffer _)) n))))]
[else
(trace-nonself port-buffer)]))
(define-trace-macro (trace-tc offset)
(case-mode
[copy
(copy offset)]
[else
(define tc : ptr (cast ptr (offset _)))
(when (!= tc (cast ptr 0))
(case-mode
[sweep
(let* ([old_stack : ptr (tc-scheme-stack tc)])
(when (OLDSPACE old_stack)
(let* ([clength : iptr (- (cast uptr (SFP tc)) (cast uptr old_stack))])
;; Include SFP[0], which contains the return address
(set! (tc-scheme-stack tc) (copy_stack old_stack
(& (tc-scheme-stack-size tc))
(+ clength (sizeof ptr))))
(count countof-stack (tc-scheme-stack-size tc) 1 sweep)
(set! (tc-sfp tc) (cast ptr (+ (cast uptr (tc-scheme-stack tc)) clength)))
(set! (tc-esp tc) (cast ptr (- (+ (cast uptr (tc-scheme-stack tc))
(tc-scheme-stack-size tc))
stack_slop))))))]
[measure
(measure_add_stack_size (tc-scheme-stack tc) (tc-scheme-stack-size tc))]
[else])
(set! (tc-stack-cache tc) Snil)
(trace (tc-cchain tc))
(trace (tc-stack-link tc))
(trace (tc-winders tc))
(trace (tc-attachments tc))
(case-mode
[sweep
(set! (tc-cached-frame tc) Sfalse)]
[else])
(trace-return NO-COPY-MODE (FRAME tc 0))
(trace-stack (cast uptr (tc-scheme-stack tc))
(cast uptr (SFP tc))
(cast uptr (FRAME tc 0)))
(case-mode
[(sweep)
(set! (tc-U tc) 0)
(set! (tc-V tc) 0)
(set! (tc-W tc) 0)
(set! (tc-X tc) 0)
(set! (tc-Y tc) 0)]
[else])
(trace (tc-threadno tc))
(trace (tc-current-input tc))
(trace (tc-current-output tc))
(trace (tc-current-error tc))
(trace (tc-sfd tc))
(trace (tc-current-mso tc))
(trace (tc-target-machine tc))
(trace (tc-fxlength-bv tc))
(trace (tc-fxfirst-bit-set-bv tc))
(trace (tc-null-immutable-vector tc))
(trace (tc-null-immutable-fxvector tc))
(trace (tc-null-immutable-bytevector tc))
(trace (tc-null-immutable-string tc))
(trace (tc-compile-profile tc))
(trace (tc-subset-mode tc))
(trace (tc-default-record-equal-procedure tc))
(trace (tc-default-record-hash-procedure tc))
(trace (tc-compress-format tc))
(trace (tc-compress-level tc))
(trace (tc-parameters tc))
(let* ([i : INT 0])
(while
:? (< i virtual_register_count)
(trace (tc-virtual-registers tc i))
(set! i += 1))))]))
(define-trace-macro (trace-stack base-expr fp-expr ret-expr)
(define base : uptr base-expr)
(define fp : uptr fp-expr)
(define ret : uptr ret-expr)
(while
:? (!= fp base)
(when (< fp base)
(S_error_abort "sweep_stack(gc): malformed stack"))
(set! fp (- fp (ENTRYFRAMESIZE ret)))
(let* ([pp : ptr* (cast ptr* fp)]
[oldret : iptr ret])
(set! ret (cast iptr (* pp)))
(trace-return NO-COPY-MODE (* pp))
(let* ([num : ptr (ENTRYLIVEMASK oldret)])
(cond
[(Sfixnump num)
(let* ([mask : uptr (UNFIX num)])
(while
:? (!= mask 0)
(set! pp += 1)
(when (& mask #x0001)
(trace (* pp)))
(set! mask >>= 1)))]
[else
(trace (* (ENTRYNONCOMPACTLIVEMASKADDR oldret)))
(let* ([num : ptr (ENTRYLIVEMASK oldret)]
[index : iptr (BIGLEN num)])
(while
:? (!= index 0)
(set! index -= 1)
(let* ([bits : INT bigit_bits]
[mask : bigit (bignum-data num index)])
(while
:? (> bits 0)
(set! bits -= 1)
(set! pp += 1)
(when (& mask 1) (trace (* pp)))
(set! mask >>= 1)))))])))))
(define-trace-macro (trace-return copy-field field)
(case-mode
[copy
(copy copy-field)]
[else
(define xcp : ptr field)
(trace-return-code field xcp)]))
(define-trace-macro (trace-return-code field xcp)
(define co : iptr (+ (ENTRYOFFSET xcp) (- (cast uptr xcp) (cast uptr (ENTRYOFFSETADDR xcp)))))
(define c_p : ptr (cast ptr (- (cast uptr xcp) co)))
(case-mode
[sweep
(define x_si : seginfo* (SegInfo (ptr_get_segment c_p)))
(when (-> x_si old_space)
(relocate_code c_p x_si)
(set! field (cast ptr (+ (cast uptr c_p) co))))]
[else
(trace (just c_p))]))
(define-trace-macro (trace-code len)
(case-mode
[(copy vfasl-copy)
(copy-bytes code-data len)]
[else
(define t : ptr (code-reloc _))
(case-mode
[(sweep vfasl-sweep)
(define m : iptr (reloc-table-size t))
(define oldco : ptr (reloc-table-code t))]
[else
(define m : iptr (cond
[t (reloc-table-size t)]
[else 0]))
(define oldco : ptr (cond
[t (reloc-table-code t)]
[else 0]))])
(case-mode
[vfasl-sweep
(let* ([r_sz : uptr (size_reloc_table m)]
[new_t : ptr (vfasl_find_room vfi vspace_reloc typemod r_sz)])
(memcpy_aligned new_t t r_sz)
(set! t new_t))]
[else])
(define a : iptr 0)
(define n : iptr 0)
(while
:? (< n m)
(let* ([entry : uptr (reloc-table-data t n)]
[item_off : uptr 0]
[code_off : uptr 0])
(set! n (+ n 1))
(cond
[(RELOC_EXTENDED_FORMAT entry)
(set! item_off (reloc-table-data t n))
(set! n (+ n 1))
(set! code_off (reloc-table-data t n))
(set! n (+ n 1))]
[else
(set! item_off (RELOC_ITEM_OFFSET entry))
(set! code_off (RELOC_CODE_OFFSET entry))])
(set! a (+ a code_off))
(let* ([obj : ptr (S_get_code_obj (RELOC_TYPE entry) oldco a item_off)])
(case-mode
[vfasl-sweep
(set! obj (vfasl_encode_relocation vfi obj))]
[else
(trace (just obj))])
(case-mode
[sweep
(S_set_code_obj "gc" (RELOC_TYPE entry) _ a obj item_off)]
[vfasl-sweep
(S_set_code_obj "vfasl" (abs-for-vfasl (RELOC_TYPE entry)) _ a obj item_off)]
[else]))))
(case-mode
[sweep
(cond
[(&& (== target_generation static_generation)
(&& (! S_G.retain_static_relocation)
(== 0 (& (code-type _) (<< code_flag_template code_flags_offset)))))
(set! (code-reloc _) (cast ptr 0))]
[else
(let* ([t_si : seginfo* (SegInfo (ptr_get_segment t))])
(when (-> t_si old_space)
(set! n (size_reloc_table (reloc-table-size t)))
(count countof-relocation-table (just n) 1 sweep)
(cond
[(-> t_si use_marks)
;; Assert: (! (marked t_si t))
(mark_typemod_data_object t n t_si)]
[else
(let* ([oldt : ptr t])
(find_room space_data target_generation typemod n t)
(memcpy_aligned t oldt n))])))
(set! (reloc-table-code t) _)
(set! (code-reloc _) t)])
(S_record_code_mod tc_in (cast uptr (& (code-data _ 0))) (cast uptr (code-length _)))]
[vfasl-sweep
;; no vfasl_register_pointer, since relink_code can handle it
(set! (reloc-table-code t) (cast ptr (ptr_diff _ (-> vfi base_addr))))
(set! (code-reloc _) (cast ptr (ptr_diff t (-> vfi base_addr))))]
[else])]))
(define-trace-macro (unless-code-relocated stmt)
(case-flag code-relocated?
[on]
[off
(case-flag as-dirty?
[on]
[off stmt])]))
(define-trace-macro (or-assume-continuation e)
(case-flag assume-continuation?
[on 1]
[off e]))
(define-trace-macro (and-counts e)
(case-flag counts?
[on e]
[off 0]))
(define-trace-macro (and-not-as-dirty e)
(case-flag as-dirty?
[on 0]
[off e]))
(define-trace-macro (or-not-as-dirty e)
(case-flag as-dirty?
[on e]
[off 1]))
(define-trace-macro (or-vfasl e)
(case-mode
[vfasl-copy 1]
[else e]))
(define-trace-macro (when-vfasl e)
(case-mode
[(vfasl-copy vfasl-sweep) e]
[else]))
(define-trace-macro (abs-for-vfasl e)
(case-mode
[vfasl-sweep reloc_abs]
[else e]))
(define-trace-macro (when-mark e)
(case-mode
[(mark) e]
[else]))
(define-trace-macro (pad e)
(case-mode
[(copy vfasl-copy) e]
[else]))
(define-trace-macro (vfasl-pad-word)
(case-mode
[(vfasl-copy)
(set! (array-ref (cast void** (UNTYPE _copy_ type_typed_object)) 3)
(cast ptr 0))]
[else]))
(define-trace-macro (vfasl-fail what)
(case-mode
[(vfasl-copy vfasl-sweep)
(vfasl_fail vfi what)
(case-mode
[vfasl-copy (return (cast ptr 0))]
[vfasl-sweep (return 0)])
(vspace #f)]
[else]))
(define-trace-macro (vfasl-as-false what)
(case-mode
[(vfasl-copy)
(return Sfalse)
(vspace #f)]
[(vfasl-sweep)
(vfasl-fail what)
(vspace #f)]
[else]))
;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Parenthe-C compiler
;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Every compiler needs its own little implementation of `match`, right?
;; Just pairs and literals, no ellipses.
(define-syntax (match stx)
(syntax-case stx (else)
[(_ expr [pattern rhs ...] ... [else else-rhs ...])
#'(let ([v expr]) (matching v [pattern rhs ...] ... [else else-rhs ...]))]
[(_ expr [pattern rhs ...] ...)
#'(let ([v expr]) (match v [pattern rhs ...] ... [else (error 'match "no matching clause: ~s" v)]))]))
(define-syntax (matching stx)
(syntax-case stx ()
[(_ v [else rhs ...])
#'(let () rhs ...)]
[(_ v [pattern rhs ...] more ...)
(letrec ([gen-match (lambda (pat quoted?)
(cond
[(identifier? pat)
(if quoted?
#`(eq? v '#,pat)
#t)]
[else
(syntax-case pat (quasiquote unquote)
[(quasiquote p)
(if quoted?
(error 'match "bad quasiquote")
(gen-match #'p #t))]
[(unquote p)
(if quoted?
(gen-match #'p #f)
(error 'match "bad unquote"))]
[(a . b)
#`(and (pair? v)
(let ([v (car v)])
#,(gen-match #'a quoted?))
(let ([v (cdr v)])
#,(gen-match #'b quoted?)))]
[other
#'(equal? v 'other)])]))]
[get-binds (lambda (pat quoted?)
(cond
[(identifier? pat)
(if quoted?
'()
(list pat))]
[else
(syntax-case pat (quasiquote unquote)
[(quasiquote p)
(get-binds #'p #t)]
[(unquote p)
(get-binds #'p #f)]
[(a . b)
(append (get-binds #'a quoted?)
(get-binds #'b quoted?))]
[other '()])]))]
[get-vals (lambda (pat quoted?)
(cond
[(identifier? pat)
(if quoted?
#''()
#'(list v))]
[else
(syntax-case pat (quasiquote unquote)
[(quasiquote p)
(get-vals #'p #t)]
[(unquote p)
(get-vals #'p #f)]
[(a . b)
#`(append (let ([v (car v)])
#,(get-vals #'a quoted?))
(let ([v (cdr v)])
#,(get-vals #'b quoted?)))]
[other #''()])]))])
(syntax-case #'pattern (quasiquote)
[(quasiquote p)
#`(if #,(gen-match #'pattern #f)
(let-values ([#,(get-binds #'pattern #f)
(apply values #,(get-vals #'pattern #f))])
rhs ...)
(matching v more ...))]
[_
(error 'match "bad pattern ~s" #'pattern)]))]))
(let ()
(define preserve-flonum-eq? #t)
;; A config is an association list. Mostly, it determines the
;; generation mode, but it is also used to some degree as an
;; environment-like map to communicate information from one
;; statement to later statements.
;;
;; Some keys:
;; - 'mode [required]
;; - 'maybe-backreferences?
;; - 'known-space [to prune generated cases]
;; - 'known-types [to prune generated cases]
(define lookup
(case-lambda
[(key config default)
(let ([a (assq key config)])
(if a
(cadr a)
default))]
[(key config)
(let ([a (assq key config)])
(if a
(cadr a)
(error 'lookup "not found: ~s" key)))]))
;; A sqeuence wraps a list of string and other sequences with
;; formatting information
(define-record-type seq
(fields l))
(define-record-type block-seq
(fields l))
(define-record-type indent-seq
(fields pre mid post))
;; More convenient constructors for sequences:
(define (code . l) (make-seq l))
(define (code-block . l) (make-block-seq l))
(define (code-indent pre mid post) (make-indent-seq pre mid post))
;; Main C-generation entry point:
(define (generate name config)
(define base-types (prune trace-base-types config))
(define object-types (prune trace-object-types config))
(define mode (lookup 'mode config))
(code
(format "static ~a ~a(~aptr p~a)"
(case (lookup 'mode config)
[(copy vfasl-copy) "ptr"]
[(size vfasl-sweep) "uptr"]
[(self-test) "IBOOL"]
[(sweep) (if (lookup 'as-dirty? config #f)
"IGEN"
"void")]
[(mark) "void"]
[else "void"])
name
(case (lookup 'mode config)
[(sweep)
(if (and (type-included? 'code config)
(not (lookup 'as-dirty? config #f)))
"ptr tc_in, "
"")]
[(vfasl-copy vfasl-sweep)
"vfasl_info *vfi, "]
[else ""])
(case (lookup 'mode config)
[(copy mark vfasl-copy) ", seginfo *si"]
[(sweep)
(if (lookup 'as-dirty? config #f)
", IGEN tg, IGEN youngest"
"")]
[else ""]))
(let ([body
(lambda ()
(let ([config (cons (list 'used (make-eq-hashtable)) config)])
(cond
[(null? base-types)
(cond
[(null? object-types)
(error 'generate "no relevant types")]
[(null? (cdr object-types))
(code-block (statements (cdar object-types)
(cons `(type ,(caar object-types)) config)))]
[else
(generate-typed-object-dispatch object-types (cons '(basetype typed-object) config))])]
[else
(cond
[(null? object-types)
(generate-type-dispatch base-types config)]
[else
(generate-type-dispatch
(cons (cons 'typed-object
(generate-typed-object-dispatch object-types (cons '(basetype typed-object)
config)))
base-types)
config)])])))])
(case (lookup 'mode config)
[(copy)
(code-block
"change = 1;"
"check_triggers(si);"
(code-block
"ptr new_p;"
"IGEN tg = target_generation;"
(body)
"FWDMARKER(p) = forward_marker;"
"FWDADDRESS(p) = new_p;"
(and (lookup 'maybe-backreferences? config #f)
"ADD_BACKREFERENCE(p)")
"return new_p;"))]
[(mark)
(code-block
"change = 1;"
"check_triggers(si);"
(ensure-segment-mark-mask "si" "" '())
(body)
"ADD_BACKREFERENCE(p)")]
[(sweep)
(code-block
(and (lookup 'maybe-backreferences? config #f)
"PUSH_BACKREFERENCE(p)")
(body)
(and (lookup 'maybe-backreferences? config #f)
"POP_BACKREFERENCE()")
(and (lookup 'as-dirty? config #f)
"return youngest;"))]
[(measure)
(body)]
[(self-test)
(code-block
(body)
"return 0;")]
[(vfasl-copy)
(code-block
"ptr new_p;"
(body)
"vfasl_register_forward(vfi, p, new_p);"
"return new_p;")]
[(vfasl-sweep)
(code-block
"uptr result_sz;"
(body)
"return result_sz;")]
[else
(body)]))))
(define (generate-type-dispatch l config)
(let ([multi? (and (pair? l) (pair? (cdr l)))])
(code-block
(and multi? "ITYPE t = TYPEBITS(p);")
(let loop ([l l] [else? #f])
(cond
[(null? l)
(and multi?
(code "else"
(code-block
(format "S_error_abort(\"~a: illegal type\");" (lookup 'mode config)))))]
[else
(code
(and multi?
(format "~aif (t == ~a)" (if else? "else " "") (as-c 'type (caar l))))
(let ([c (cdar l)])
(if (block-seq? c)
c
(code-block (statements c (cons (list 'basetype (caar l))
config)))))
(loop (cdr l) #t))])))))
(define (generate-typed-object-dispatch l config)
(code-block
"ptr tf = TYPEFIELD(p);"
(let loop ([l l] [else? #f])
(cond
[(null? l)
(code "else"
(code-block
(format "S_error_abort(\"~a: illegal typed object type\");" (lookup 'mode config))))]
[else
(let* ([ty (caar l)]
[mask (lookup-constant (string->symbol (format "mask-~a" ty)))]
[type-constant? (eqv? mask (constant byte-constant-mask))])
(code (format "~aif (~a)" (if else? "else " "")
(if type-constant?
(format "(iptr)tf == ~a" (as-c 'type ty))
(format "TYPEP(tf, ~a, ~a)" (as-c 'mask ty) (as-c 'type ty))))
(code-block (statements (cdar l) (cons* (list 'tf "tf")
(list 'type ty)
(if type-constant?
(cons `(type-constant ,(as-c 'type ty))
config)
config))))
(loop (cdr l) #t)))]))))
;; list of S-expressions -> code sequence
(define (statements l config)
(cond
[(null? l) (code)]
[else
(let ([a (car l)])
(match a
[`(case-mode . ,all-clauses)
(let ([body (find-matching-mode (lookup 'mode config) all-clauses)])
(statements (append body (cdr l)) config))]
[`(case-space . ,all-clauses)
(code
(code-block
(format "ISPC p_at_spc = ~a;"
(case (lookup 'mode config)
[(copy mark vfasl-copy) "si->space"]
[else "SPACE(p)"]))
(let loop ([all-clauses all-clauses] [else? #f])
(match all-clauses
[`([else . ,body])
(code
"else"
(code-block (statements body config)))]
[`([,spc . ,body] . ,rest)
(code
(format "~aif (p_at_spc == ~a)"
(if else? "else " "")
(as-c spc))
(code-block (statements body config))
(loop rest #t))])))
(statements (cdr l) config))]
[`(case-flag ,flag
[on . ,on]
[off . ,off])
(let ([body (if (lookup flag config #f)
on
off)])
(statements (append body (cdr l)) config))]
[`(trace-early-rtd ,field)
(code (case (and (not (lookup 'as-dirty? config #f))
(not (lookup 'rtd-relocated? config #f))
(lookup 'mode config))
[(copy sweep mark)
(code
"/* Relocate to make sure we aren't using an oldspace descriptor"
" that has been overwritten by a forwarding marker, but don't loop"
" on tag-reflexive base descriptor */"
(format "if (p != ~a)"
(lookup 'tf config (format "TYPEFIELD(p)")))
(code-block
(statements `((trace-early ,field)) config)))]
[(measure)
(statements `((trace-early ,field)) config)]
[else #f])
(statements (cdr l) (cons `(copy-extra-rtd ,field) config)))]
[`(trace ,field)
(code (trace-statement field config #f)
(statements (cdr l) config))]
[`(trace-early ,field)
(code (trace-statement field config #t)
(statements (cdr l) (if (symbol? field)
(cons `(copy-extra ,field) config)
config)))]
[`(trace-now ,field)
(code
(case (lookup 'mode config)
[(copy)
(code-block
(format "ptr tmp_p = ~a;" (field-expression field config "p" #f))
(relocate-statement "tmp_p" config)
(format "~a = tmp_p;" (field-expression field config "new_p" #f)))]
[(self-test) #f]
[(measure vfasl-copy vfasl-sweep)
(statements (list `(trace ,field)) config)]
[(mark)
(relocate-statement (field-expression field config "p" #t) config)]
[else
(trace-statement field config #f)])
(statements (cdr l) config))]
[`(copy ,field)
(code (copy-statement field config)
(statements (cdr l) config))]
[`(copy-flonum ,field)
(cond
[(and preserve-flonum-eq?
(eq? 'copy (lookup 'mode config)))
(code (copy-statement field config)
"flonum_set_forwarded(p, si);"
"FLONUM_FWDADDRESS(p) = new_p;"
(statements (cdr l) config))]
[else
(statements (cons `(copy ,field) (cdr l)) config)])]
[`(copy-flonum* ,field)
(cond
[preserve-flonum-eq?
(case (lookup 'mode config)
[(copy)
(code (code-block
(format "ptr tmp_p = TYPE(&~a, type_flonum);" (field-expression field config "p" #t))
"if (flonum_is_forwarded_p(tmp_p, si))"
(format " ~a = FLODAT(FLONUM_FWDADDRESS(tmp_p));"
(field-expression field config "new_p" #f))
"else"
(format " ~a = ~a;"
(field-expression field config "new_p" #f)
(field-expression field config "p" #f)))
(statements (cdr l) config))]
[(vfasl-copy)
(statements (cons `(copy ,field) (cdr l)) config)]
[else (statements (cdr l) config)])]
[else
(statements (cons `(copy ,field) (cdr l)) config)])]
[`(copy-bytes ,offset ,len)
(code (case (lookup 'mode config)
[(copy vfasl-copy)
(format "memcpy_aligned(&~a, &~a, ~a);"
(field-expression offset config "new_p" #t)
(field-expression offset config "p" #t)
(expression len config))]
[else #f])
(statements (cdr l) config))]
[`(copy-type ,field)
(case (lookup 'mode config)
[(copy vfasl-copy)
(code
(format "~a = ~a;"
(field-expression field config "new_p" #f)
(or (lookup 'type-constant config #f)
"(uptr)tf"))
(statements (cdr l) config))]
[else
(statements (cons `(copy ,field) (cdr l)) config)])]
[`(trace-ptrs ,offset ,len)
(case (lookup 'mode config)
[(copy vfasl-copy)
(statements (cons `(copy-bytes ,offset (* ptr_bytes ,len))
(cdr l))
config)]
[(sweep measure vfasl-sweep)
(code
(loop-over-pointers
(field-expression offset config "p" #t)
len
(trace-statement `(array-ref p_p idx) config #f)
config))]
[(self-test)
(code
(loop-over-pointers (field-expression offset config "p" #t)
len
(code "if (p_p[idx] == p) return 1;")
config)
(statements (cdr l) config))]
[else (statements (cdr l) config)])]
[`(count ,counter)
(code (count-statement counter #f 1 'copy config)
(statements (cdr l) config))]
[`(count ,counter ,size)
(statements (cons `(count ,counter ,size 1 copy) (cdr l)) config)]
[`(count ,counter ,size ,scale)
(statements (cons `(count ,counter ,size ,scale copy) (cdr l)) config)]
[`(count ,counter ,size ,scale ,modes)
(code (count-statement counter size scale modes
(cons `(constant-size? ,(symbol? size))
config))
(statements (cdr l) config))]
[`(as-mark-end . ,stmts)
(statements (append stmts (cdr l))
config)]
[`(space ,s)
(case (lookup 'mode config)
[(copy)
(code (code-indent "ISPC p_spc = "
(expression s config #f #t)
";")
(statements (cdr l) (cons '(space-ready? #t) config)))]
[(mark)
(statements (cdr l) (if (symbol? s)
(cons `(known-space ,s) config)
config))]
[else (statements (cdr l) config)])]
[`(vspace ,s)
(case (lookup 'mode config)
[(vfasl-copy)
(cond
[(not s) (code)]
[else
(code (code-indent "int p_vspc = "
(expression s config #f #t)
";")
(statements (cdr l) (cons '(vspace-ready? #t) config)))])]
[(vfasl-sweep)
(cond
[(not s) (code)]
[else (statements (cdr l) config)])]
[else (statements (cdr l) config)])]
[`(size ,sz)
(statements (cons `(size ,sz ,1) (cdr l)) config)]
[`(size ,sz ,scale)
(let* ([mode (lookup 'mode config)]
[mode (if (lookup 'return-size? config #f)
(case mode
[(sweep) 'sweep+size]
[else mode])
mode)]
[was-used? (let ([used? (hashtable-ref (lookup 'used config) 'p_sz #f)])
(hashtable-set! (lookup 'used config) 'p_sz #f)
used?)]
[config (if (and (symbol? sz)
(eqv? scale 1))
(cons `(known-size ,sz) config)
config)]
[config (if (symbol? sz)
(cons '(constant-size? #t)
config)
config)]
[rest
(case mode
[(copy vfasl-copy)
(case mode
[(copy) (unless (lookup 'space-ready? config #f)
(error 'generate "size before space"))]
[(vfasl-copy) (unless (lookup 'vspace-ready? config #f)
(error 'generate "size before vspace for ~a/~a"
(lookup 'basetype config)
(lookup 'type config #f)))])
(hashtable-set! (lookup 'used config) 'p_sz #t)
(code (format "~a, ~a, p_sz, new_p);"
(case mode
[(copy) "find_room(p_spc, tg"]
[(vfasl-copy) "FIND_ROOM(vfi, p_vspc"])
(as-c 'type (lookup 'basetype config)))
(statements (let ([extra (lookup 'copy-extra config #f)])
(if extra
(cons `(copy ,extra) (cdr l))
(let* ([mode (lookup 'mode config)]
[extra (and (memq mode '(copy vfasl-copy))
(lookup 'copy-extra-rtd config #f))])
(if extra
(cons `(set! (,extra _copy_)
,(case mode
[(copy)
`(cond
[(== tf _) _copy_]
[else rtd])]
[else 'rtd]))
(cdr l))
(cdr l)))))
(cons '(copy-ready? #t)
config)))]
[(size)
(hashtable-set! (lookup 'used config) 'p_sz #t)
(code "return p_sz;")]
[(vfasl-sweep)
(hashtable-set! (lookup 'used config) 'p_sz #t)
(code "result_sz = p_sz;"
(statements (cdr l) config))]
[(measure)
(hashtable-set! (lookup 'used config) 'p_sz #t)
(code "measure_total += p_sz;"
(statements (cdr l) config))]
[else (statements (cdr l) config)])]
[used? (hashtable-ref (lookup 'used config) 'p_sz #f)])
(hashtable-set! (lookup 'used config) 'p_sz was-used?)
(cond
[used?
(code-block
(format "uptr p_sz = ~a;" (let ([s (size-expression sz config)])
(if (= scale 1)
s
(format "~a * (~a)" scale s))))
rest)]
[else rest]))]
[`(skip-forwarding)
(case (lookup 'mode config)
[(copy)
(unless (null? (cdr l))
(error 'skip-forwarding "not at end"))
(code "return new_p;")]
[else
(statements (cdr l) config)])]
[`(mark . ,flags)
(for-each (lambda (flag)
(unless (memq flag '(one-bit no-sweep within-segment counting-root))
(error 'mark "bad flag ~s" flag)))
flags)
(case (lookup 'mode config)
[(mark)
(let* ([count-stmt (let loop ([l (cdr l)])
(cond
[(null? l) (error 'mark "could not find `count` or `as-mark-end` ~s" config)]
[else
(match (car l)
[`(count . ,rest) (car l)]
[`(as-mark-end . ,stmts) (car l)]
[`(case-mode . ,all-clauses)
(let ([body (find-matching-mode 'mark all-clauses)])
(loop (append body (cdr l))))]
[`(,id . ,args)
(let ([m (eq-hashtable-ref trace-macros id #f)])
(if m
(loop (append (apply-macro m args)
(cdr l)))
(loop (cdr l))))]
[else (loop (cdr l))])]))])
(code
(mark-statement flags config)
(statements (list count-stmt) config)))]
[else
(statements (cdr l) config)])]
[`(define ,id : ,type ,rhs)
(let* ([used (lookup 'used config)]
[prev-used? (hashtable-ref used id #f)])
(hashtable-set! used id #f)
(let* ([rest (statements (cdr l) config)]
[used? (hashtable-ref (lookup 'used config) id #f)])
(hashtable-set! used id prev-used?)
(if used?
(code-block (code-indent (format "~a ~a = " type id)
(expression rhs config #f #t)
";")
rest)
rest)))]
[`(cond . ,clauses)
(code
(let loop ([clauses clauses] [else? #f])
(match clauses
[`() (code)]
[`([else . ,rhss])
(cond
[(null? rhss)
(code)]
[else
(if else?
(code "else"
(code-block
(statements rhss config)))
(statements rhss config))])]
[`([,test . ,rhss] . ,clauses)
(let ([tst (expression test config)])
(cond
[(equal? tst "0")
(loop clauses else?)]
[else
(let ([rhs (statements rhss config)])
(cond
[(equal? tst "1")
(if else?
(code-block "else" rhs)
rhs)]
[else
(code (format "~aif (~a)" (if else? "else " "") tst)
(code-block rhs)
(loop clauses #t))]))]))]))
(statements (cdr l) config))]
[`(let* ,binds . ,body)
(code
(code-block
(let loop ([binds binds])
(match binds
[`() (statements body config)]
[`([,id : ,type ,rhs] . ,binds)
(code (code-indent (format "~a ~a = " type id)
(expression rhs config #f #t)
";")
(loop binds))])))
(statements (cdr l) config))]
[`(while :? ,tst . ,body)
(code (format "while (~a)" (expression tst config))
(code-block
(statements body config))
(statements (cdr l) config))]
[`(do-while . ,body+test)
(let-values ([(body tst)
(let loop ([body+test body+test] [rev-body '()])
(match body+test
[`(:? ,test) (values (reverse rev-body) test)]
[`(,e . ,rest)
(loop rest (cons e rev-body))]))])
(code "do"
(code-block
(statements body config))
(format "while (~a);" (expression tst config))
(statements (cdr l) config)))]
[`(when ,tst . ,body)
(statements (cons `(cond [,tst . ,body][else]) (cdr l))
config)]
[`(set! ,lhs ,rhs)
(code (code-indent (format "~a = "
(expression lhs config))
(expression rhs config #f #t)
";")
(statements (cdr l) config))]
[`(set! ,lhs ,op ,rhs)
(unless (memq op '(+= -= <<= >>=))
(error 'set! "not an update op ~s" op))
(code (format "~a ~a ~a;"
(expression lhs config)
op
(expression rhs config))
(statements (cdr l) config))]
[`(break)
(code "break;")]
[`(assert ,expr)
(unless (eval expr)
(error 'assert "failed: ~s" expr))
(statements (cdr l) config)]
[`(,id . ,args)
(let ([m (eq-hashtable-ref trace-macros id #f)])
(if m
(statements (append (apply-macro m args)
(cdr l))
config)
(code (format "~a;" (expression a config #f #t))
(statements (cdr l) config))))]
[else
(code (format "~a;" (expression a config #f #t))
(statements (cdr l) config))]))]))
;; S-expresison -> string
(define expression
(case-lambda
[(a config) (expression a config #f #f)]
[(a config protect?) (expression a config protect? #f)]
[(a config protect? multiline?)
(define (protect s)
(if protect? (format "(~a)" s) s))
(match a
[`_ "p"]
[`_copy_ (case (lookup 'mode config)
[(copy vfasl-copy) "new_p"]
[else "p"])]
[`_tf_
(lookup 'tf config "TYPEFIELD(p)")]
[`_tg_
(case (lookup 'mode config)
[(copy) "tg"]
[else "target_generation"])]
[`_backreferences?_
(if (lookup 'maybe-backreferences? config #f)
"BACKREFERENCES_ENABLED"
"0")]
[`(just ,id)
(hashtable-set! (lookup 'used config) id #t)
(symbol->string id)]
[`(case-flag ,flag
[on ,on]
[off ,off])
(let ([e (if (lookup flag config #f)
on
off)])
(expression e config protect? multiline?))]
[`(case-mode . ,all-clauses)
(match (find-matching-mode (lookup 'mode config) all-clauses)
[`(,e)
(expression e config protect? multiline?)]
[`,any
(error 'case-mode "bad form ~s" a)])]
[`(cond . ,clauses)
(let loop ([clauses clauses] [protect? protect?])
(match clauses
[`([else ,rhs]) (expression rhs config protect? multiline?)]
[`([,test ,rhs] . ,clauses)
(let ([tst (expression test config #t #t)])
(cond
[(equal? tst "0")
(loop clauses protect?)]
[(equal? tst "1")
(expression rhs config protect? multiline?)]
[else
(if multiline?
(format "(~a\n ? ~a\n : ~a)"
tst
(indent-newlines (expression rhs config #t #t) 3)
(indent-newlines (loop clauses #t) 3))
(format "(~a ? ~a : ~a)"
tst
(expression rhs config #t #f)
(loop clauses #t)))]))]))]
[`(cast ,type ,e)
(protect (format "(~a)~a" type (expression e config #t)))]
[`(array-ref ,array ,index)
(protect (format "~a[~a]"
(expression array config #t)
(expression index config)))]
[`(set! ,lhs ,rhs) ; a `set!` used as an expression
(format "(~a = ~a)"
(expression lhs config #t)
(expression rhs config #t))]
[`(,op ,a)
(cond
[(memq op '(& - !))
(protect (format "~a~a" op (expression a config #t)))]
[(get-offset-value op)
=> (lambda (v)
(protect (field-ref-expression (expression a config) v op #f config)))]
[(eq-hashtable-ref trace-macros op #f)
=> (lambda (m)
(expression (car (apply-macro m (list a))) config protect? multiline?))]
[else
(protect (format "~a(~a)" op (expression a config #t)))])]
[`(,op ,a ,b)
(cond
[(memq op '(& && \|\| == != + - * < > <= >= << >> ->))
(protect (format "~a ~a ~a" (expression a config #t) op (expression b config #t)))]
[(get-offset-value op)
=> (lambda (v)
(protect (field-ref-expression (expression a config) v op b config)))]
[else
(protect (format "~a(~a, ~a)" op (expression a config) (expression b config)))])]
[`(,rator . ,rands)
(unless (symbol? rator)
(error 'expression "expected a symbol for funciton name: ~s" rator))
(format "~a(~a)"
rator
(comma-ize (map (lambda (r) (expression r config)) rands)))]
[else
(cond
[(eq? a #f) "Sfalse"]
[(eq? a #t) "Strue"]
[(symbol? a)
(cond
[(getprop a '*c-name* #f)
=> (lambda (c-name) c-name)]
[else
(hashtable-set! (lookup 'used config) a #t)
(symbol->string a)])]
[else
(format "~s" a)])])]))
(define (find-matching-mode mode all-clauses)
(let loop ([clauses all-clauses])
(match clauses
[`([else . ,body])
body]
[`([,cl-mode . ,cl-body] . ,clauses)
(if (or (eq? cl-mode mode)
(and (pair? cl-mode)
(memq mode cl-mode)))
cl-body
(loop clauses))]
[`()
(error 'case-mode "no matching case for ~s in ~s" mode all-clauses)])))
(define (loop-over-pointers ptr-e len body config)
(code-block
(format "uptr idx, p_len = ~a;" (expression len config))
(format "ptr *p_p = &~a;" ptr-e)
"for (idx = 0; idx < p_len; idx++)"
(code-block body)))
(define (trace-statement field config early?)
(define mode (lookup 'mode config))
(cond
[(or (eq? mode 'sweep)
(eq? mode 'vfasl-sweep)
(and early? (or (eq? mode 'copy)
(eq? mode 'mark))))
(relocate-statement (field-expression field config "p" #t) config)]
[(or (eq? mode 'copy)
(eq? mode 'vfasl-copy))
(copy-statement field config)]
[(eq? mode 'measure)
(measure-statement (field-expression field config "p" #f))]
[(eq? mode 'self-test)
(format "if (p == ~a) return 1;" (field-expression field config "p" #f))]
[else #f]))
(define (relocate-statement e config)
(define mode (lookup 'mode config))
(case mode
[(vfasl-sweep)
(format "vfasl_relocate(vfi, &~a);" e)]
[else
(if (lookup 'as-dirty? config #f)
(format "relocate_dirty(&~a, tg, youngest);" e)
(format "relocate(&~a);" e))]))
(define (measure-statement e)
(code
"{ /* measure */"
(format " ptr r_p = ~a;" e)
" if (!IMMEDIATE(r_p))"
" push_measure(r_p);"
"}"))
(define (copy-statement field config)
(define mode (lookup 'mode config))
(case mode
[(copy vfasl-copy)
(cond
[(symbol? field)
(unless (lookup 'copy-ready? config #f)
(error 'copy "need size before: ~s" field))
(format "~a = ~a;"
(field-expression field config "new_p" #f)
(field-expression field config "p" #f))]
[else
(when (eq? mode 'copy)
(error 'copy "pointless copy to self for ~s" field))
#f])]
[else #f]))
(define (count-statement counter size scale modes config)
(let* ([real-mode (lookup 'mode config)]
[mode (if (eq? real-mode 'mark) 'copy real-mode)])
(cond
[(or (eq? mode modes) (and (pair? modes) (memq mode modes)))
(cond
[(lookup 'counts? config #f)
(let ([tg (if (eq? real-mode 'copy)
"tg"
"target_generation")])
(code
(format "S_G.countof[~a][~a] += ~a;" tg (as-c counter) scale)
(if (lookup 'constant-size? config #f)
#f
(format "S_G.bytesof[~a][~a] += ~a;"
tg
(as-c counter)
(let ([s (if size
(expression size config)
(begin
(hashtable-set! (lookup 'used config) 'p_sz #t)
"p_sz"))])
(if (eqv? scale 1)
s
(format "~a * (~a)" scale s)))))))]
[else #f])]
[else #f])))
(define (mark-statement flags config)
(let* ([known-space (lookup 'known-space config #f)]
[sz (let ([sz (lookup 'known-size config #f)])
(and sz (get-size-value sz)))]
[one-bit? (or (memq 'one-bit flags)
(eq? 'space-data known-space)
(eqv? sz (constant byte-alignment)))]
[within-segment? (or (memq 'within-segment flags)
(and sz
(< sz (constant bytes-per-segment))))]
[no-sweep? (or (memq 'no-sweep flags)
(eq? known-space 'space-data))]
[within-loop-statement
(lambda (decl si step count?)
(code-block
"uptr offset = 0;"
"while (offset < p_sz) {"
" ptr mark_p = (ptr)((uptr)p + offset);"
decl
(format " ~a->marked_mask[segment_bitmap_byte(mark_p)] |= segment_bitmap_bit(mark_p);" si)
(and count? (format " ~a->marked_count += ~a;" si step))
(format " offset += ~a;" step)
"}"))]
[type (let ([t (lookup 'basetype config)])
(if (eq? t 'typemod)
#f
(as-c 'type (lookup 'basetype config))))]
[untype (lambda ()
(if type
(format "(uptr)UNTYPE(p, ~a)" type)
(format "(uptr)p")))])
(hashtable-set! (lookup 'used config) 'p_sz #t)
(code
(cond
[one-bit?
(code
"si->marked_mask[segment_bitmap_byte(p)] |= segment_bitmap_bit(p);"
(cond
[within-segment?
"si->marked_count += p_sz;"]
[else
(code-block
(format "uptr addr = ~a;" (untype))
"uptr seg = addr_get_segment(addr);"
"uptr end_seg = addr_get_segment(addr + p_sz - 1);"
"if (seg == end_seg) {"
" si->marked_count += p_sz;"
"} else {"
" seginfo *mark_si;"
" si->marked_count += ((uptr)build_ptr(seg+1,0)) - addr;"
" seg++;"
" while (seg < end_seg) {"
" mark_si = SegInfo(seg);"
" if (!fully_marked_mask) init_fully_marked_mask();"
" mark_si->marked_mask = fully_marked_mask;"
" mark_si->marked_count = segment_bitmap_bytes;"
" seg++;"
" }"
" mark_si = SegInfo(end_seg);"
(ensure-segment-mark-mask "mark_si" " " '())
" /* no need to set a bit: just make sure `marked_mask` is non-NULL */"
" mark_si->marked_count += addr + p_sz - (uptr)build_ptr(end_seg,0);"
"}")]))]
[within-segment?
(code
"si->marked_count += p_sz;"
(cond
[sz
(code-block
"ptr mark_p = p;"
(let loop ([sz sz])
(code
"si->marked_mask[segment_bitmap_byte(mark_p)] |= segment_bitmap_bit(mark_p);"
(let ([sz (- sz (constant byte-alignment))])
(if (zero? sz)
#f
(code
"mark_p = (ptr)((uptr)mark_p + byte_alignment);"
(loop sz)))))))]
[else
(within-loop-statement #f "si" "byte_alignment" #f)]))]
[else
(let ([step "byte_alignment"])
(code-block
(format "uptr addr = (uptr)UNTYPE(p, ~a);" type)
"if (addr_get_segment(addr) == addr_get_segment(addr + p_sz - 1))"
(code-block
"si->marked_count += p_sz;"
(within-loop-statement #f "si" step #f))
"else"
(within-loop-statement (code
" seginfo *mark_si = SegInfo(ptr_get_segment(mark_p));"
(ensure-segment-mark-mask "mark_si" " " '()))
"mark_si"
step
#t)))])
(cond
[no-sweep? #f]
[else
(let ([push "push_sweep(p);"])
(cond
[(and (memq 'counting-root flags)
(lookup 'counts? config #f))
(code "if (!is_counting_root(si, p))"
(code-block push))]
[else push]))]))))
(define (field-expression field config arg protect?)
(if (symbol? field)
(cond
[(get-offset-value field)
=> (lambda (v)
(field-ref-expression arg v field 0 config))]
[else
(error 'field "identifier is not a field accessor: ~s" field)])
(expression field config protect?)))
(define (size-expression sz config)
(if (symbol? sz)
(cond
[(get-size-value sz)
=> (lambda (v) (as-c sz))]
[else
(error 'size "identifier is not a size: ~s" sz)])
(expression sz config)))
(define (field-ref-expression obj v acc-name index config)
(let ([c-ref (getprop acc-name '*c-ref* #f)])
(unless c-ref
(error 'field-ref "could not find accessor for ~s" acc-name))
(cond
[(pair? c-ref)
(unless index
(error 'field-ref "missing index for array field ~s" acc-name))
(format "~a(~a, ~a)" (car c-ref) obj (expression index config))]
[else
(when (and index (not (eq? index 0)))
(error 'field-ref "index not allowed for non-array field ~s" acc-name))
(format "~a(~a)" c-ref obj)])))
(define (ensure-segment-mark-mask si inset flags)
(code
(format "~aif (!~a->marked_mask) {" inset si)
(format "~a find_room(space_data, target_generation, typemod, ptr_align(segment_bitmap_bytes), ~a->marked_mask);"
inset si)
(if (memq 'no-clear flags)
(format "~a /* no clearing needed */" inset)
(format "~a memset(~a->marked_mask, 0, segment_bitmap_bytes);" inset si))
(format "~a}" inset)))
(define (just-mark-bit-space? sp)
(case sp
[(space-symbol space-port) #t]
[else (atomic-space? sp)]))
(define (atomic-space? sp)
(case sp
[(space-data) #t]
[else #f]))
;; Slightly hacky way to check whether `op` is an accessor
(define (get-offset-value op)
(getprop (string->symbol (format "~a-disp" op)) '*constant* #f))
;; Check whether `op` is a size (probably)
(define (get-size-value op)
(getprop op '*constant* #f))
;; Convert to C constant name
(define as-c
(case-lambda
[(sym)
(or (getprop sym '*c-name* #f)
(error 'as-type "failed for ~s" sym))]
[(prefix base)
(or (getprop (string->symbol (format "~a-~a" prefix base)) '*c-name* #f)
(error 'as-type "failed for ~s ~s" prefix base))]))
(define (comma-ize l)
(apply string-append
(let loop ([l l])
(if (null? l)
'("")
(if (null? (cdr l))
(list (car l))
(list* (car l) ", " (loop (cdr l))))))))
(define (apply-macro m l)
(define args (car m))
(define body (cdr m))
(unless (= (length args) (length l))
(error 'apply-macro "wrong macro argument count: ~s vs ~s" args l))
(let ([subs (map cons args l)])
(let loop ([m body])
(cond
[(symbol? m)
(let ([a (assq m subs)])
(if a
(cdr a)
m))]
[(pair? m)
(cons (loop (car m)) (loop (cdr m)))]
[else m]))))
(define (type-included? type config)
(let ([types (lookup 'known-types config #f)])
(if (not types)
#t
(memq type types))))
(define (prune types config)
(let loop ([types types])
(if (null? types)
'()
(let ([s (prune-one (car types) config)])
(if s
(cons s (loop (cdr types)))
(loop (cdr types)))))))
(define (prune-one type config)
(define known-types (lookup 'known-types config #f))
(cond
[(or (not known-types)
(memq (car type) known-types))
(let ([known-space (lookup 'known-space config #f)])
(cond
[(or (not known-space)
(body-has-space? (cdr type) known-space config))
type]
[else #f]))]
[else #f]))
(define (body-has-space? body space config)
(cond
[(null? body) (error 'base-has-space? "no `space` specification in body")]
[else
(let ([a (car body)])
(cond
[(and (pair? a) (eq? (car a) 'space))
(body-has-tail? (cdr a) space config)]
[(and (pair? a) (memq (car a) '(case-space cond)))
(unless (null? (cdr body)) (error 'body-has-space? "there's more?"))
(let loop ([clauses (cdr a)])
(if (null? clauses)
#f
(or (body-has-space? (cdar clauses) space config)
(loop (cdr clauses)))))]
[else
(body-has-space? (cdr body) space config)]))]))
(define (body-has-tail? body key config)
(cond
[(null? body) #f]
[else
(let ([a (car body)])
(match a
[`(cond . ,clauses)
(ormap (lambda (clause)
(body-has-tail? (cdr clause) key config))
clauses)]
[else
(body-has-tail? (cdr body) key config)]))]))
(define print-code
(case-lambda
[(c)
(print-code c 0)
(newline)]
[(c indentation)
(cond
[(not c) (void)]
[(seq? c)
(for-each (lambda (p)
(print-code p indentation))
(seq-l c))]
[(block-seq? c)
(let ([l (block-seq-l c)])
(cond
[(and (pair? l)
(null? (cdr l))
(block-seq? (car l)))
(print-code (car l) indentation)]
[else
(indent indentation)
(printf "{\n")
(for-each (lambda (p)
(print-code p (+ indentation 2)))
l)
(indent indentation)
(printf "}\n")]))]
[(indent-seq? c)
(indent indentation)
(printf "~a" (indent-seq-pre c))
(printf "~a" (indent-newlines (indent-seq-mid c)
(+ indentation (string-length (indent-seq-pre c)))))
(printf "~a" (indent-seq-post c))
(newline)]
[else
(indent indentation)
(printf "~a\n" (indent-newlines c indentation))])]))
(define (indent n)
(display (make-string n #\space)))
(define (indent-newlines s n)
(list->string
(let loop ([l (string->list s)])
(cond
[(null? l) '()]
[(eqv? #\newline (car l))
(cons #\newline (append (string->list (make-string n #\space))
(loop (cdr l))))]
[else (cons (car l) (loop (cdr l)))]))))
(define (gen-gc ofn count? measure?)
(guard
(x [#t (raise x)])
(parameterize ([current-output-port (open-output-file ofn 'replace)])
(print-code (generate "copy"
`((mode copy)
(maybe-backreferences? ,count?)
(counts? ,count?))))
(print-code (generate "sweep"
`((mode sweep)
(maybe-backreferences? ,count?)
(counts? ,count?))))
(print-code (generate "sweep_dirty_object"
`((mode sweep)
(maybe-backreferences? ,count?)
(counts? ,count?)
(as-dirty? #t))))
(letrec ([sweep1
(case-lambda
[(type) (sweep1 type (format "sweep_~a" type) '())]
[(type name) (sweep1 type name '())]
[(type name extra-configs)
(print-code (generate name
(append
extra-configs
`((mode sweep)
(known-types (,type))
(maybe-backreferences? ,count?)
(counts? ,count?)))))])])
(sweep1 'record "sweep_record" '((rtd-relocated? #t)))
(sweep1 'record "sweep_dirty_record" '((rtd-relocated? #t)
(as-dirty? #t)))
(sweep1 'symbol)
(sweep1 'symbol "sweep_dirty_symbol" '((as-dirty? #t)))
(sweep1 'thread)
(sweep1 'port)
(sweep1 'port "sweep_dirty_port" '((as-dirty? #t)))
(sweep1 'closure "sweep_continuation" '((code-relocated? #t)
(assume-continuation? #t)))
(sweep1 'code "sweep_code_object"))
(print-code (generate "size_object"
`((mode size))))
(print-code (generate "mark_object"
`((mode mark)
(counts? ,count?))))
(print-code (generate "object_directly_refers_to_self"
`((mode self-test))))
(print-code (code "static void mark_typemod_data_object(ptr p, uptr p_sz, seginfo *si)"
(code-block
(ensure-segment-mark-mask "si" "" '())
(mark-statement '(one-bit no-sweep)
(cons
(list 'used (make-eq-hashtable))
'((basetype typemod)))))))
(when measure?
(print-code (generate "measure" `((mode measure))))))))
(define (gen-vfasl ofn)
(guard
(x [#t (raise x)])
(parameterize ([current-output-port (open-output-file ofn 'replace)])
(print-code (generate "copy"
`((mode vfasl-copy))))
(print-code (generate "sweep"
`((mode vfasl-sweep)
(return-size? #t)))))))
;; Render via mkequates to record a mapping from selectors to C
;; macros:
(let-values ([(op get) (open-bytevector-output-port (native-transcoder))])
(mkequates.h op))
(set! mkgc-ocd.inc (lambda (ofn) (gen-gc ofn #f #f)))
(set! mkgc-oce.inc (lambda (ofn) (gen-gc ofn #t #t)))
(set! mkvfasl.inc (lambda (ofn) (gen-vfasl ofn))))