racket/collects/compiler/demodularizer/update-toplevels.rkt
Matthew Flatt 2ada6d0e89 break link to namespaces from from closures over top-/module-level vars
- the `lam' structure from `compiler/zo-struct' changed to include a
   `toplevel-map' field

 This change helps solve a finalization problem in `racket/draw',
 which in turn sigificantly reduces the peak memory use of `raco setup'
 during the doc-building phase (because some documents load `racket/draw'
 to render images, and multiple copies of `racket/draw' were retained
 before finalization was fixed).

 The change is an extreme way to solve a specific finalization
 problem, but it's a kind of space-safety improvement; space safety
 almost never matters, but when it does, then working around a lack of
 space safety is practically impossible. In this case, it's not clear
 how to otherwise solve the `racket/draw' finalization problem.

 The improvement doesn't change the representation of closures, but it
 requires special cooperation with the GC. All closures in a module
 continue to share the same array of globals (plus syntax objects);
 that is, instead of completely flat closures, Racket uses a two-level
 environment where top-/module-level variables are grouped
 together. The code half of a closure now records which
 top-/module-level variables the body code actually uses, and the mark
 phase of GC consults this information to retain only parts of the
 top-/module-level environment frame that are actually used by some
 closure (or all of the frame if it is accessible through some other
 route).  In other words, the GC supports a kind of "dependent
 reference" to an array that is indexed by positions into the array
 --- except that the code is more in the "Racket" directory instead of
 the "GC" directory, since it's so specific to the closure
 representation.
2011-05-03 06:57:49 -06:00

99 lines
3.5 KiB
Racket

#lang racket
(require compiler/zo-structs
"util.rkt")
(define (update-toplevels toplevel-updater topsyntax-updater topsyntax-new-midpt)
(define (inner-update form)
(match form
[(struct def-values (ids rhs))
(make-def-values (map update ids)
(update rhs))]
[(? def-syntaxes?)
(error 'increment "Doesn't handle syntax")]
[(? def-for-syntax?)
(error 'increment "Doesn't handle syntax")]
[(struct req (reqs dummy))
(make-req reqs (update dummy))]
[(? mod?)
(error 'increment "Doesn't handle modules")]
[(struct seq (forms))
(make-seq (map update forms))]
[(struct splice (forms))
(make-splice (map update forms))]
[(and l (struct lam (name flags num-params param-types rest? closure-map closure-types tl-map max-let-depth body)))
(struct-copy lam l
[toplevel-map #f] ; conservative
[body (update body)])]
[(and c (struct closure (code gen-id)))
(struct-copy closure c
[code (update code)])]
[(and cl (struct case-lam (name clauses)))
(define new-clauses
(map update clauses))
(struct-copy case-lam cl
[clauses new-clauses])]
[(struct let-one (rhs body flonum? unused?))
(make-let-one (update rhs) (update body) flonum? unused?)] ; Q: is it okay to just pass in the old value for flonum?
[(and f (struct let-void (count boxes? body)))
(struct-copy let-void f
[body (update body)])]
[(and f (struct install-value (_ _ _ rhs body)))
(struct-copy install-value f
[rhs (update rhs)]
[body (update body)])]
[(struct let-rec (procs body))
(make-let-rec (map update procs) (update body))]
[(and f (struct boxenv (_ body)))
(struct-copy boxenv f [body (update body)])]
[(and f (struct toplevel (_ pos _ _)))
(struct-copy toplevel f
[pos (toplevel-updater pos)])]
[(and f (struct topsyntax (_ pos _)))
(struct-copy topsyntax f
[pos (topsyntax-updater pos)]
[midpt topsyntax-new-midpt])]
[(struct application (rator rands))
(make-application
(update rator)
(map update rands))]
[(struct branch (test then else))
(make-branch
(update test)
(update then)
(update else))]
[(struct with-cont-mark (key val body))
(make-with-cont-mark
(update key)
(update val)
(update body))]
[(struct beg0 (seq))
(make-beg0 (map update seq))]
[(struct varref (tl))
(make-varref (update tl))]
[(and f (struct assign (id rhs undef-ok?)))
(struct-copy assign f
[id (update id)]
[rhs (update rhs)])]
[(struct apply-values (proc args-expr))
(make-apply-values
(update proc)
(update args-expr))]
[(and f (struct primval (id)))
f]
[(and f (struct localref (unbox? pos clear? other-clears? flonum?)))
f]
[(and f (not (? form?)))
f]
))
(define-values (first-update update)
(build-form-memo inner-update))
first-update)
(provide/contract
[update-toplevels
((exact-nonnegative-integer? . -> . exact-nonnegative-integer?)
(exact-nonnegative-integer? . -> . exact-nonnegative-integer?)
exact-nonnegative-integer?
. -> .
(form? . -> . form?))])