123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896 |
- ;;; Continuation-passing style (CPS) intermediate language (IL)
- ;; Copyright (C) 2013-2019 Free Software Foundation, Inc.
- ;;;; This library is free software; you can redistribute it and/or
- ;;;; modify it under the terms of the GNU Lesser General Public
- ;;;; License as published by the Free Software Foundation; either
- ;;;; version 3 of the License, or (at your option) any later version.
- ;;;;
- ;;;; This library is distributed in the hope that it will be useful,
- ;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
- ;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- ;;;; Lesser General Public License for more details.
- ;;;;
- ;;;; You should have received a copy of the GNU Lesser General Public
- ;;;; License along with this library; if not, write to the Free Software
- ;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- ;;; Commentary:
- ;;;
- ;;; This pass converts a CPS term in such a way that no function has any
- ;;; free variables. Instead, closures are built explicitly as heap
- ;;; objects, and free variables are referenced through the closure.
- ;;;
- ;;; Closure conversion also removes any $rec expressions that
- ;;; contification did not handle. See (language cps) for a further
- ;;; discussion of $rec.
- ;;;
- ;;; Before closure conversion, function self variables are always bound.
- ;;; After closure conversion, well-known functions with no free
- ;;; variables may have no self reference.
- ;;;
- ;;; Code:
- (define-module (language cps closure-conversion)
- #:use-module (ice-9 match)
- #:use-module ((srfi srfi-1) #:select (fold
- filter-map
- ))
- #:use-module (srfi srfi-11)
- #:use-module (system base types internal)
- #:use-module (language cps)
- #:use-module (language cps utils)
- #:use-module (language cps with-cps)
- #:use-module (language cps intmap)
- #:use-module (language cps intset)
- #:export (convert-closures))
- (define (compute-function-bodies conts kfun)
- "Compute a map from FUN-LABEL->BODY-LABEL... for all $fun instances in
- conts."
- (let visit-fun ((kfun kfun) (out empty-intmap))
- (let ((body (compute-function-body conts kfun)))
- (intset-fold
- (lambda (label out)
- (match (intmap-ref conts label)
- (($ $kargs _ _ ($ $continue _ _ ($ $fun kfun)))
- (visit-fun kfun out))
- (($ $kargs _ _ ($ $continue _ _ ($ $rec _ _ (($ $fun kfun) ...))))
- (fold visit-fun out kfun))
- (_ out)))
- body
- (intmap-add out kfun body)))))
- (define (compute-program-body functions)
- (intmap-fold (lambda (label body out) (intset-union body out))
- functions
- empty-intset))
- (define (filter-reachable conts functions)
- (let ((reachable (compute-program-body functions)))
- (intmap-fold
- (lambda (label cont out)
- (if (intset-ref reachable label)
- out
- (intmap-remove out label)))
- conts conts)))
- (define (compute-non-operator-uses conts)
- (persistent-intset
- (intmap-fold
- (lambda (label cont uses)
- (define (add-use var uses) (intset-add! uses var))
- (define (add-uses vars uses)
- (match vars
- (() uses)
- ((var . vars) (add-uses vars (add-use var uses)))))
- (match cont
- (($ $kargs _ _ ($ $continue _ _ exp))
- (match exp
- ((or ($ $const) ($ $prim) ($ $fun) ($ $rec)) uses)
- (($ $values args)
- (add-uses args uses))
- (($ $call proc args)
- (add-uses args uses))
- (($ $primcall name param args)
- (add-uses args uses))))
- (($ $kargs _ _ ($ $branch kf kt src op param args))
- (add-uses args uses))
- (($ $kargs _ _ ($ $prompt k kh src escape? tag))
- (add-use tag uses))
- (($ $kargs _ _ ($ $throw src op param args))
- (add-uses args uses))
- (_ uses)))
- conts
- empty-intset)))
- (define (compute-singly-referenced-labels conts body)
- (define (add-ref label single multiple)
- (define (ref k single multiple)
- (if (intset-ref single k)
- (values single (intset-add! multiple k))
- (values (intset-add! single k) multiple)))
- (define (ref0) (values single multiple))
- (define (ref1 k) (ref k single multiple))
- (define (ref2 k k*)
- (if k*
- (let-values (((single multiple) (ref k single multiple)))
- (ref k* single multiple))
- (ref1 k)))
- (match (intmap-ref conts label)
- (($ $kreceive arity k) (ref1 k))
- (($ $kfun src meta self ktail kclause) (ref2 ktail kclause))
- (($ $ktail) (ref0))
- (($ $kclause arity kbody kalt) (ref2 kbody kalt))
- (($ $kargs _ _ ($ $continue k)) (ref1 k))
- (($ $kargs _ _ ($ $branch kf kt)) (ref2 kf kt))
- (($ $kargs _ _ ($ $prompt k kh)) (ref2 k kh))
- (($ $kargs _ _ ($ $throw)) (ref0))))
- (let*-values (((single multiple) (values empty-intset empty-intset))
- ((single multiple) (intset-fold add-ref body single multiple)))
- (intset-subtract (persistent-intset single)
- (persistent-intset multiple))))
- (define (compute-function-names conts functions)
- "Compute a map of FUN-LABEL->BOUND-VAR... for each labelled function
- whose bound vars we know."
- (define (add-named-fun var kfun out)
- (let ((self (match (intmap-ref conts kfun)
- (($ $kfun src meta self) self))))
- (intmap-add out kfun (intset var self))))
- (intmap-fold
- (lambda (label body out)
- (let ((single (compute-singly-referenced-labels conts body)))
- (intset-fold
- (lambda (label out)
- (match (intmap-ref conts label)
- (($ $kargs _ _ ($ $continue k _ ($ $fun kfun)))
- (if (intset-ref single k)
- (match (intmap-ref conts k)
- (($ $kargs (_) (var)) (add-named-fun var kfun out))
- (_ out))
- out))
- (($ $kargs _ _ ($ $continue k _ ($ $rec _ vars (($ $fun kfun) ...))))
- (unless (intset-ref single k)
- (error "$rec continuation has multiple predecessors??"))
- (fold add-named-fun out vars kfun))
- (_ out)))
- body
- out)))
- functions
- empty-intmap))
- (define (compute-well-known-functions conts bound->label)
- "Compute a set of labels indicating the well-known functions in
- @var{conts}. A well-known function is a function whose bound names we
- know and which is never used in a non-operator position."
- (intset-subtract
- (persistent-intset
- (intmap-fold (lambda (bound label candidates)
- (intset-add! candidates label))
- bound->label
- empty-intset))
- (persistent-intset
- (intset-fold (lambda (var not-well-known)
- (match (intmap-ref bound->label var (lambda (_) #f))
- (#f not-well-known)
- (label (intset-add! not-well-known label))))
- (compute-non-operator-uses conts)
- empty-intset))))
- (define (intset-cons i set)
- (intset-add set i))
- (define (compute-shared-closures conts well-known)
- "Compute a map LABEL->VAR indicating the sets of functions that will
- share a closure. If a functions's label is in the map, it is shared.
- The entries indicate the var of the shared closure, which will be one of
- the bound vars of the closure."
- (intmap-fold
- (lambda (label cont out)
- (match cont
- (($ $kargs _ _
- ($ $continue _ _ ($ $rec names vars (($ $fun kfuns) ...))))
- ;; The split-rec pass should have ensured that this $rec forms a
- ;; strongly-connected component, so the free variables from all of
- ;; the functions will be alive as long as one of the closures is
- ;; alive. For that reason we can consider storing all free
- ;; variables in one closure and sharing it.
- (let* ((kfuns-set (fold intset-cons empty-intset kfuns))
- (unknown-kfuns (intset-subtract kfuns-set well-known)))
- (cond
- ((or (eq? empty-intset kfuns-set) (trivial-intset kfuns-set))
- ;; There is only zero or one function bound here. Trivially
- ;; shared already.
- out)
- ((eq? empty-intset unknown-kfuns)
- ;; All functions are well-known; we can share a closure. Use
- ;; the first bound variable.
- (let ((closure (car vars)))
- (intset-fold (lambda (kfun out)
- (intmap-add out kfun closure))
- kfuns-set out)))
- ((trivial-intset unknown-kfuns)
- => (lambda (unknown-kfun)
- ;; Only one function is not-well-known. Use that
- ;; function's closure as the shared closure.
- (let ((closure (assq-ref (map cons kfuns vars) unknown-kfun)))
- (intset-fold (lambda (kfun out)
- (intmap-add out kfun closure))
- kfuns-set out))))
- (else
- ;; More than one not-well-known function means we need more
- ;; than one proper closure, so we can't share.
- out))))
- (_ out)))
- conts
- empty-intmap))
- (define* (rewrite-shared-closure-calls cps functions label->bound shared kfun)
- "Rewrite CPS such that every call to a function with a shared closure
- instead is a $callk to that label, but passing the shared closure as the
- proc argument. For recursive calls, use the appropriate 'self'
- variable, if possible. Also rewrite uses of the non-well-known but
- shared closures to use the appropriate 'self' variable, if possible."
- ;; env := var -> (var . label)
- (define (visit-fun kfun cps env)
- (define (subst var)
- (match (intmap-ref env var (lambda (_) #f))
- (#f var)
- ((var . label) var)))
- (define (visit-exp exp)
- (rewrite-exp exp
- ((or ($ $const) ($ $prim)) ,exp)
- (($ $call proc args)
- ,(let ((args (map subst args)))
- (rewrite-exp (intmap-ref env proc (lambda (_) #f))
- (#f ($call proc ,args))
- ((closure . label) ($callk label closure ,args)))))
- (($ $primcall name param args)
- ($primcall name param ,(map subst args)))
- (($ $values args)
- ($values ,(map subst args)))))
- (define (visit-term term)
- (rewrite-term term
- (($ $continue k src exp)
- ($continue k src ,(visit-exp exp)))
- (($ $branch kf kt src op param args)
- ($branch kf kt src op param ,(map subst args)))
- (($ $prompt k kh src escape? tag)
- ($prompt k kh src escape? (subst tag)))
- (($ $throw src op param args)
- ($throw src op param ,(map subst args)))))
- (define (visit-rec labels vars cps)
- (define (compute-env label bound self rec-bound rec-labels env)
- (define (add-bound-var bound label env)
- (intmap-add env bound (cons self label) (lambda (old new) new)))
- (if (intmap-ref shared label (lambda (_) #f))
- ;; Within a function with a shared closure, rewrite
- ;; references to bound vars to use the "self" var.
- (fold add-bound-var env rec-bound rec-labels)
- ;; Otherwise be sure to use "self" references in any
- ;; closure.
- (add-bound-var bound label env)))
- (fold (lambda (label var cps)
- (match (intmap-ref cps label)
- (($ $kfun src meta self)
- (visit-fun label cps
- (compute-env label var self vars labels env)))))
- cps labels vars))
- (define (visit-cont label cps)
- (match (intmap-ref cps label)
- (($ $kargs names vars
- ($ $continue k src ($ $fun label)))
- (visit-fun label cps env))
- (($ $kargs _ _
- ($ $continue k src ($ $rec names vars (($ $fun labels) ...))))
- (visit-rec labels vars cps))
- (($ $kargs names vars term)
- (with-cps cps
- (setk label ($kargs names vars ,(visit-term term)))))
- (_ cps)))
- (intset-fold visit-cont (intmap-ref functions kfun) cps))
- ;; Initial environment is bound-var -> (shared-var . label) map for
- ;; functions with shared closures.
- (let ((env (intmap-fold (lambda (label shared env)
- (intset-fold (lambda (bound env)
- (intmap-add env bound
- (cons shared label)))
- (intset-remove
- (intmap-ref label->bound label)
- (match (intmap-ref cps label)
- (($ $kfun src meta self) self)))
- env))
- shared
- empty-intmap)))
- (persistent-intmap (visit-fun kfun cps env))))
- (define (compute-free-vars conts kfun shared)
- "Compute a FUN-LABEL->FREE-VAR... map describing all free variable
- references."
- (define (add-def var defs) (intset-add! defs var))
- (define (add-defs vars defs)
- (match vars
- (() defs)
- ((var . vars) (add-defs vars (add-def var defs)))))
- (define (add-use var uses)
- (intset-add! uses var))
- (define (add-uses vars uses)
- (match vars
- (() uses)
- ((var . vars) (add-uses vars (add-use var uses)))))
- (define (visit-nested-funs body)
- (intset-fold
- (lambda (label out)
- (match (intmap-ref conts label)
- (($ $kargs _ _ ($ $continue _ _
- ($ $fun kfun)))
- (intmap-union out (visit-fun kfun)))
- (($ $kargs _ _ ($ $continue _ _
- ($ $rec _ _ (($ $fun labels) ...))))
- (let* ((out (fold (lambda (kfun out)
- (intmap-union out (visit-fun kfun)))
- out labels))
- (free (fold (lambda (kfun free)
- (intset-union free (intmap-ref out kfun)))
- empty-intset labels)))
- (fold (lambda (kfun out)
- ;; For functions that share a closure, the free
- ;; variables for one will be the union of the free
- ;; variables for all.
- (if (intmap-ref shared kfun (lambda (_) #f))
- (intmap-replace out kfun free)
- out))
- out
- labels)))
- (_ out)))
- body
- empty-intmap))
- (define (visit-fun kfun)
- (let* ((body (compute-function-body conts kfun))
- (free (visit-nested-funs body)))
- (call-with-values
- (lambda ()
- (intset-fold
- (lambda (label defs uses)
- (match (intmap-ref conts label)
- (($ $kargs names vars term)
- (values
- (add-defs vars defs)
- (match term
- (($ $continue k src exp)
- (match exp
- ((or ($ $const) ($ $prim)) uses)
- (($ $fun kfun)
- (intset-union (persistent-intset uses)
- (intmap-ref free kfun)))
- (($ $rec names vars (($ $fun kfun) ...))
- (fold (lambda (kfun uses)
- (intset-union (persistent-intset uses)
- (intmap-ref free kfun)))
- uses kfun))
- (($ $values args)
- (add-uses args uses))
- (($ $call proc args)
- (add-use proc (add-uses args uses)))
- (($ $callk label proc args)
- (add-use proc (add-uses args uses)))
- (($ $primcall name param args)
- (add-uses args uses))))
- (($ $branch kf kt src op param args)
- (add-uses args uses))
- (($ $prompt k kh src escape? tag)
- (add-use tag uses))
- (($ $throw src op param args)
- (add-uses args uses)))))
- (($ $kfun src meta self)
- (values (add-def self defs) uses))
- (_ (values defs uses))))
- body empty-intset empty-intset))
- (lambda (defs uses)
- (intmap-add free kfun (intset-subtract
- (persistent-intset uses)
- (persistent-intset defs)))))))
- (visit-fun kfun))
- (define (eliminate-closure? label free-vars)
- (eq? (intmap-ref free-vars label) empty-intset))
- (define (closure-label label shared bound->label)
- (cond
- ((intmap-ref shared label (lambda (_) #f))
- => (lambda (closure)
- (intmap-ref bound->label closure)))
- (else label)))
- (define (closure-alias label well-known free-vars)
- (and (intset-ref well-known label)
- (trivial-intset (intmap-ref free-vars label))))
- (define (prune-free-vars free-vars bound->label well-known shared)
- "Given the label->bound-var map @var{free-vars}, remove free variables
- that are known functions with zero free variables, and replace
- references to well-known functions with one free variable with that free
- variable, until we reach a fixed point on the free-vars map."
- (define (prune-free in-label free free-vars)
- (intset-fold (lambda (var free)
- (match (intmap-ref bound->label var (lambda (_) #f))
- (#f free)
- (label
- (cond
- ((eliminate-closure? label free-vars)
- (intset-remove free var))
- ((closure-alias (closure-label label shared bound->label)
- well-known free-vars)
- => (lambda (alias)
- ;; If VAR is free in LABEL, then ALIAS must
- ;; also be free because its definition must
- ;; precede VAR's definition.
- (intset-add (intset-remove free var) alias)))
- (else free)))))
- free free))
- (fixpoint (lambda (free-vars)
- (intmap-fold (lambda (label free free-vars)
- (intmap-replace free-vars label
- (prune-free label free free-vars)))
- free-vars
- free-vars))
- free-vars))
- (define (intset-find set i)
- (let lp ((idx 0) (start #f))
- (let ((start (intset-next set start)))
- (cond
- ((not start) (error "not found" set i))
- ((= start i) idx)
- (else (lp (1+ idx) (1+ start)))))))
- (define (intset-count set)
- (intset-fold (lambda (_ count) (1+ count)) set 0))
- (define (compute-elidable-closures cps well-known shared free-vars)
- "Compute the set of well-known callees with no free variables. Calls
- to these functions can avoid passing a closure parameter. Note however
- that we have to exclude well-known callees that are part of a shared
- closure that contains any not-well-known member."
- (define (intset-map f set)
- (persistent-intset
- (intset-fold (lambda (i out) (if (f i) (intset-add! out i) out))
- set
- empty-intset)))
- (let ((no-free-vars (persistent-intset
- (intmap-fold (lambda (label free out)
- (if (eq? empty-intset free)
- (intset-add! out label)
- out))
- free-vars empty-intset)))
- (shared
- (intmap-fold
- (lambda (label cont out)
- (match cont
- (($ $kargs _ _
- ($ $continue _ _ ($ $rec _ _ (($ $fun kfuns) ...))))
- ;; Either all of these functions share a closure, in
- ;; which all or all except one of them are well-known, or
- ;; none of the functions share a closure.
- (if (intmap-ref shared (car kfuns) (lambda (_) #f))
- (let* ((scc (fold intset-cons empty-intset kfuns)))
- (intset-fold (lambda (label out)
- (intmap-add out label scc))
- scc out))
- out))
- (_ out)))
- cps
- empty-intmap)))
- (intmap-fold (lambda (label labels elidable)
- (if (eq? labels (intset-intersect labels well-known))
- elidable
- (intset-subtract elidable labels)))
- shared
- (intset-intersect well-known no-free-vars))))
- (define (convert-one cps label body free-vars bound->label well-known shared
- elidable)
- (define (well-known? label)
- (intset-ref well-known label))
- (let* ((free (intmap-ref free-vars label))
- (nfree (intset-count free))
- (self-known? (well-known? (closure-label label shared bound->label)))
- (self (match (intmap-ref cps label) (($ $kfun _ _ self) self))))
- (define (convert-arg cps var k)
- "Convert one possibly free variable reference to a bound reference.
- If @var{var} is free, it is replaced by a closure reference via a
- @code{free-ref} primcall, and @var{k} is called with the new var.
- Otherwise @var{var} is bound, so @var{k} is called with @var{var}."
- ;; We know that var is not the name of a well-known function.
- (cond
- ((and=> (intmap-ref bound->label var (lambda (_) #f))
- (lambda (kfun)
- (and (eq? empty-intset (intmap-ref free-vars kfun))
- kfun)))
- ;; A not-well-known function with zero free vars. Copy as a
- ;; constant, relying on the linker to reify just one copy.
- => (lambda (kfun)
- (with-cps cps
- (letv var*)
- (let$ body (k var*))
- (letk k* ($kargs (#f) (var*) ,body))
- (build-term ($continue k* #f ($const-fun kfun))))))
- ((intset-ref free var)
- (if (and self-known? (eqv? 1 nfree))
- ;; A reference to the one free var of a well-known function.
- (with-cps cps
- ($ (k self)))
- (let* ((idx (intset-find free var))
- (param (cond
- ((not self-known?) (cons 'closure (+ idx 2)))
- ((= nfree 2) (cons 'pair idx))
- (else (cons 'vector (+ idx 1))))))
- (with-cps cps
- (letv var*)
- (let$ body (k var*))
- (letk k* ($kargs (#f) (var*) ,body))
- (build-term
- ($continue k* #f
- ($primcall 'scm-ref/immediate param (self))))))))
- (else
- (with-cps cps
- ($ (k var))))))
-
- (define (convert-args cps vars k)
- "Convert a number of possibly free references to bound references.
- @var{k} is called with the bound references, and should return the
- term."
- (match vars
- (()
- (with-cps cps
- ($ (k '()))))
- ((var . vars)
- (convert-arg cps var
- (lambda (cps var)
- (convert-args cps vars
- (lambda (cps vars)
- (with-cps cps
- ($ (k (cons var vars)))))))))))
-
- (define (allocate-closure cps k src label known? nfree)
- "Allocate a new closure, and pass it to $var{k}."
- (match (vector known? nfree)
- (#(#f 0)
- ;; The call sites cannot be enumerated, but the closure has no
- ;; identity; statically allocate it.
- (with-cps cps
- (build-term ($continue k src ($const-fun label)))))
- (#(#f nfree)
- ;; The call sites cannot be enumerated; allocate a closure.
- (with-cps cps
- (letv closure tag code)
- (letk k* ($kargs () ()
- ($continue k src ($values (closure)))))
- (letk kinit ($kargs ('code) (code)
- ($continue k* src
- ($primcall 'word-set!/immediate '(closure . 1)
- (closure code)))))
- (letk kcode ($kargs () ()
- ($continue kinit src ($code label))))
- (letk ktag1
- ($kargs ('tag) (tag)
- ($continue kcode src
- ($primcall 'word-set!/immediate '(closure . 0)
- (closure tag)))))
- (letk ktag0
- ($kargs ('closure) (closure)
- ($continue ktag1 src
- ($primcall 'load-u64 (+ %tc7-program (ash nfree 16)) ()))))
- (build-term
- ($continue ktag0 src
- ($primcall 'allocate-words/immediate `(closure . ,(+ nfree 2))
- ())))))
- (#(#t 2)
- ;; Well-known closure with two free variables; the closure is a
- ;; pair.
- (with-cps cps
- (build-term
- ($continue k src
- ($primcall 'allocate-words/immediate `(pair . 2) ())))))
- ;; Well-known callee with more than two free variables; the closure
- ;; is a vector.
- (#(#t nfree)
- (unless (> nfree 2)
- (error "unexpected well-known nullary, unary, or binary closure"))
- (with-cps cps
- (letv v w0)
- (letk k* ($kargs () () ($continue k src ($values (v)))))
- (letk ktag1
- ($kargs ('w0) (w0)
- ($continue k* src
- ($primcall 'word-set!/immediate '(vector . 0) (v w0)))))
- (letk ktag0
- ($kargs ('v) (v)
- ($continue ktag1 src
- ($primcall 'load-u64 (+ %tc7-vector (ash nfree 8)) ()))))
- (build-term
- ($continue ktag0 src
- ($primcall 'allocate-words/immediate `(vector . ,(1+ nfree))
- ())))))))
- (define (init-closure cps k src var known? free)
- "Initialize the free variables @var{closure-free} in a closure
- bound to @var{var}, and continue to @var{k}."
- (let ((count (intset-count free)))
- (cond
- ((and known? (<= count 1))
- ;; Well-known callee with zero or one free variables; no
- ;; initialization necessary.
- (with-cps cps
- (build-term ($continue k src ($values ())))))
- (else
- ;; Otherwise residualize a sequence of scm-set!.
- (let-values (((kind offset)
- ;; What are we initializing? A closure if the
- ;; procedure is not well-known; a pair if it has
- ;; only 2 free variables; otherwise, a vector.
- (cond
- ((not known?) (values 'closure 2))
- ((= count 2) (values 'pair 0))
- (else (values 'vector 1)))))
- (let lp ((cps cps) (prev #f) (idx 0))
- (match (intset-next free prev)
- (#f (with-cps cps
- (build-term ($continue k src ($values ())))))
- (v (with-cps cps
- (let$ body (lp (1+ v) (1+ idx)))
- (letk k ($kargs () () ,body))
- ($ (convert-arg v
- (lambda (cps v)
- (with-cps cps
- (build-term
- ($continue k src
- ($primcall 'scm-set!/immediate
- (cons kind (+ offset idx))
- (var v)))))))))))))))))
- (define (make-single-closure cps k src kfun)
- (let ((free (intmap-ref free-vars kfun)))
- (match (vector (well-known? kfun) (intset-count free))
- (#(#f 0)
- (with-cps cps
- (build-term ($continue k src ($const-fun kfun)))))
- (#(#t 0)
- (with-cps cps
- (build-term ($continue k src ($const #f)))))
- (#(#t 1)
- ;; A well-known closure of one free variable is replaced
- ;; at each use with the free variable itself, so we don't
- ;; need a binding at all; and yet, the continuation
- ;; expects one value, so give it something. DCE should
- ;; clean up later.
- (with-cps cps
- (build-term ($continue k src ($const #f)))))
- (#(well-known? nfree)
- ;; A bit of a mess, but beta conversion should remove the
- ;; final $values if possible.
- (with-cps cps
- (letv closure)
- (letk k* ($kargs () () ($continue k src ($values (closure)))))
- (let$ init (init-closure k* src closure well-known? free))
- (letk knew ($kargs (#f) (closure) ,init))
- ($ (allocate-closure knew src kfun well-known? nfree)))))))
- ;; The callee is known, but not necessarily well-known.
- (define (convert-known-proc-call cps k src label closure args)
- (define (have-closure cps closure)
- (convert-args cps args
- (lambda (cps args)
- (with-cps cps
- (build-term
- ($continue k src ($callk label closure args)))))))
- (cond
- ((eq? (intmap-ref free-vars label) empty-intset)
- ;; Known call, no free variables; no closure needed. If the
- ;; callee is well-known, elide the closure argument entirely.
- ;; Otherwise pass #f.
- (if (intset-ref elidable label)
- (have-closure cps #f)
- (with-cps cps
- ($ (with-cps-constants ((false #f))
- ($ (have-closure false)))))))
- ((and (well-known? (closure-label label shared bound->label))
- (trivial-intset (intmap-ref free-vars label)))
- ;; Well-known closures with one free variable are
- ;; replaced at their use sites by uses of the one free
- ;; variable.
- => (lambda (var)
- (convert-arg cps var have-closure)))
- (else
- ;; Otherwise just load the proc.
- (convert-arg cps closure have-closure))))
- (define (visit-term cps term)
- (match term
- (($ $continue k src (or ($ $const) ($ $prim)))
- (with-cps cps
- term))
- (($ $continue k src ($ $fun kfun))
- (with-cps cps
- ($ (make-single-closure k src kfun))))
- ;; Remove letrec.
- (($ $continue k src ($ $rec names vars (($ $fun kfuns) ...)))
- (match (vector names vars kfuns)
- (#(() () ())
- ;; Trivial empty case.
- (with-cps cps
- (build-term ($continue k src ($values ())))))
- (#((name) (var) (kfun))
- ;; Trivial single case. We have already proven that K has
- ;; only LABEL as its predecessor, so we have been able
- ;; already to rewrite free references to the bound name with
- ;; the self name.
- (with-cps cps
- ($ (make-single-closure k src kfun))))
- (#(_ _ (kfun0 . _))
- ;; A non-trivial strongly-connected component. Does it have
- ;; a shared closure?
- (match (intmap-ref shared kfun0 (lambda (_) #f))
- (#f
- ;; Nope. Allocate closures for each function.
- (let lp ((cps (match (intmap-ref cps k)
- ;; Steal declarations from the continuation.
- (($ $kargs names vals body)
- (intmap-replace cps k
- (build-cont
- ($kargs () () ,body))))))
- (in (map vector names vars kfuns))
- (init (lambda (cps)
- (with-cps cps
- (build-term
- ($continue k src ($values ())))))))
- (match in
- (() (init cps))
- ((#(name var kfun) . in)
- (let* ((known? (well-known? kfun))
- (free (intmap-ref free-vars kfun))
- (nfree (intset-count free)))
- (define (next-init cps)
- (with-cps cps
- (let$ body (init))
- (letk k ($kargs () () ,body))
- ($ (init-closure k src var known? free))))
- (with-cps cps
- (let$ body (lp in next-init))
- (letk k ($kargs (name) (var) ,body))
- ($ (allocate-closure k src kfun known? nfree))))))))
- (shared
- ;; If shared is in the bound->var map, that means one of
- ;; the functions is not well-known. Otherwise use kfun0
- ;; as the function label, but just so make-single-closure
- ;; can find the free vars, not for embedding in the
- ;; closure.
- (let* ((kfun (intmap-ref bound->label shared (lambda (_) kfun0)))
- (cps (match (intmap-ref cps k)
- ;; Make continuation declare only the shared
- ;; closure.
- (($ $kargs names vals body)
- (intmap-replace cps k
- (build-cont
- ($kargs (#f) (shared) ,body)))))))
- (with-cps cps
- ($ (make-single-closure k src kfun)))))))))
- (($ $continue k src ($ $call proc args))
- (match (intmap-ref bound->label proc (lambda (_) #f))
- (#f
- (convert-arg cps proc
- (lambda (cps proc)
- (convert-args cps args
- (lambda (cps args)
- (with-cps cps
- (build-term
- ($continue k src ($call proc args)))))))))
- (label
- (convert-known-proc-call cps k src label proc args))))
- (($ $continue k src ($ $callk label proc args))
- (convert-known-proc-call cps k src label proc args))
- (($ $continue k src ($ $primcall name param args))
- (convert-args cps args
- (lambda (cps args)
- (with-cps cps
- (build-term
- ($continue k src ($primcall name param args)))))))
- (($ $continue k src ($ $values args))
- (convert-args cps args
- (lambda (cps args)
- (with-cps cps
- (build-term
- ($continue k src ($values args)))))))
- (($ $branch kf kt src op param args)
- (convert-args cps args
- (lambda (cps args)
- (with-cps cps
- (build-term
- ($branch kf kt src op param args))))))
- (($ $prompt k kh src escape? tag)
- (convert-arg cps tag
- (lambda (cps tag)
- (with-cps cps
- (build-term
- ($prompt k kh src escape? tag))))))
- (($ $throw src op param args)
- (convert-args cps args
- (lambda (cps args)
- (with-cps cps
- (build-term
- ($throw src op param args))))))))
- (intset-fold (lambda (label cps)
- (match (intmap-ref cps label (lambda (_) #f))
- (($ $kargs names vars term)
- (with-cps cps
- (let$ term (visit-term term))
- (setk label ($kargs names vars ,term))))
- (($ $kfun src meta self ktail kclause)
- (if (intset-ref elidable label)
- (with-cps cps
- (setk label ($kfun src meta #f ktail kclause)))
- cps))
- (_ cps)))
- body
- cps)))
- (define (convert-closures cps)
- "Convert free reference in @var{cps} to primcalls to @code{free-ref},
- and allocate and initialize flat closures."
- (let* ((kfun 0) ;; Ass-u-me.
- ;; label -> body-label...
- (functions (compute-function-bodies cps kfun))
- (cps (filter-reachable cps functions))
- ;; label -> bound-var...
- (label->bound (compute-function-names cps functions))
- ;; bound-var -> label
- (bound->label (invert-partition label->bound))
- ;; label...
- (well-known (compute-well-known-functions cps bound->label))
- ;; label -> closure-var
- (shared (compute-shared-closures cps well-known))
- (cps (rewrite-shared-closure-calls cps functions label->bound shared
- kfun))
- ;; label -> free-var...
- (free-vars (compute-free-vars cps kfun shared))
- (free-vars (prune-free-vars free-vars bound->label well-known shared))
- ;; label...
- (elidable (compute-elidable-closures cps well-known shared free-vars)))
- (let ((free-in-program (intmap-ref free-vars kfun)))
- (unless (eq? empty-intset free-in-program)
- (error "Expected no free vars in program" free-in-program)))
- (with-fresh-name-state cps
- (persistent-intmap
- (intmap-fold
- (lambda (label body cps)
- (convert-one cps label body free-vars bound->label well-known shared
- elidable))
- functions
- cps)))))
- ;;; Local Variables:
- ;;; eval: (put 'convert-arg 'scheme-indent-function 2)
- ;;; eval: (put 'convert-args 'scheme-indent-function 2)
- ;;; End:
|