Hard-wire calls to known procedures
[bpt/guile.git] / module / language / cps / closure-conversion.scm
CommitLineData
4b8de65e
AW
1;;; Continuation-passing style (CPS) intermediate language (IL)
2
7ab76a83 3;; Copyright (C) 2013, 2014 Free Software Foundation, Inc.
4b8de65e
AW
4
5;;;; This library is free software; you can redistribute it and/or
6;;;; modify it under the terms of the GNU Lesser General Public
7;;;; License as published by the Free Software Foundation; either
8;;;; version 3 of the License, or (at your option) any later version.
9;;;;
10;;;; This library is distributed in the hope that it will be useful,
11;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
12;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13;;;; Lesser General Public License for more details.
14;;;;
15;;;; You should have received a copy of the GNU Lesser General Public
16;;;; License along with this library; if not, write to the Free Software
17;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18
19;;; Commentary:
20;;;
21;;; This pass converts a CPS term in such a way that no function has any
22;;; free variables. Instead, closures are built explicitly with
23;;; make-closure primcalls, and free variables are referenced through
24;;; the closure.
25;;;
26;;; Closure conversion also removes any $letrec forms that contification
27;;; did not handle. See (language cps) for a further discussion of
28;;; $letrec.
29;;;
30;;; Code:
31
32(define-module (language cps closure-conversion)
33 #:use-module (ice-9 match)
34 #:use-module ((srfi srfi-1) #:select (fold
35 lset-union lset-difference
36 list-index))
8b1a4b23 37 #:use-module (srfi srfi-9)
4b8de65e
AW
38 #:use-module (srfi srfi-26)
39 #:use-module (language cps)
8b1a4b23 40 #:use-module (language cps dfg)
4b8de65e
AW
41 #:export (convert-closures))
42
cf8bb037 43;; free := var ...
4b8de65e 44
cf8bb037 45(define (convert-free-var var self free k)
4b8de65e
AW
46 "Convert one possibly free variable reference to a bound reference.
47
cf8bb037 48If @var{var} is free (i.e., present in @var{free},), it is replaced
4b8de65e 49by a closure reference via a @code{free-ref} primcall, and @var{k} is
cf8bb037
AW
50called with the new var. Otherwise @var{var} is bound, so @var{k} is
51called with @var{var}."
52 (cond
53 ((list-index (cut eq? <> var) free)
54 => (lambda (free-idx)
55 (let-fresh (k* kidx) (idx var*)
56 (build-cps-term
57 ($letk ((kidx ($kargs ('idx) (idx)
58 ($letk ((k* ($kargs (var*) (var*) ,(k var*))))
59 ($continue k* #f
60 ($primcall 'free-ref (self idx)))))))
61 ($continue kidx #f ($const free-idx)))))))
62 (else (k var))))
4b8de65e 63
cf8bb037 64(define (convert-free-vars vars self free k)
4b8de65e 65 "Convert a number of possibly free references to bound references.
cf8bb037
AW
66@var{k} is called with the bound references, and should return the
67term."
68 (match vars
4b8de65e 69 (() (k '()))
cf8bb037
AW
70 ((var . vars)
71 (convert-free-var var self free
72 (lambda (var)
73 (convert-free-vars vars self free
74 (lambda (vars)
75 (k (cons var vars)))))))))
4b8de65e 76
cf8bb037 77(define (init-closure src v free outer-self outer-free body)
4b8de65e
AW
78 "Initialize the free variables @var{free} in a closure bound to
79@var{v}, and continue with @var{body}. @var{outer-self} must be the
80label of the outer procedure, where the initialization will be
cf8bb037 81performed, and @var{outer-free} is the list of free variables there."
4b8de65e 82 (fold (lambda (free idx body)
cf8bb037 83 (let-fresh (k) (idxvar)
4b8de65e 84 (build-cps-term
6e422a35 85 ($letk ((k ($kargs () () ,body)))
4b8de65e 86 ,(convert-free-var
cf8bb037 87 free outer-self outer-free
4b8de65e
AW
88 (lambda (free)
89 (values (build-cps-term
cf8bb037 90 ($letconst (('idx idxvar idx))
6e422a35 91 ($continue k src
cf8bb037 92 ($primcall 'free-set! (v idxvar free)))))
4b8de65e
AW
93 '())))))))
94 body
95 free
96 (iota (length free))))
97
8b1a4b23 98(define (analyze-closures exp dfg)
cf8bb037
AW
99 "Compute the set of free variables for all $fun instances in
100@var{exp}."
8b1a4b23 101 (let ((free-vars (make-hash-table))
983413a1
AW
102 (named-funs (make-hash-table))
103 (well-known (make-bitvector (var-counter) #t)))
104 (define (add-named-fun! var cont)
105 (hashq-set! named-funs var cont))
8b1a4b23 106 (define (clear-well-known! var)
983413a1 107 (bitvector-set! well-known var #f))
cf8bb037
AW
108 (define (union a b)
109 (lset-union eq? a b))
110 (define (difference a b)
111 (lset-difference eq? a b))
112 (define (visit-cont cont bound)
113 (match cont
114 (($ $cont label ($ $kargs names vars body))
115 (visit-term body (append vars bound)))
116 (($ $cont label ($ $kfun src meta self tail clause))
983413a1 117 (add-named-fun! self cont)
cf8bb037
AW
118 (let ((free (if clause
119 (visit-cont clause (list self))
120 '())))
8b1a4b23 121 (hashq-set! free-vars label (cons free cont))
cf8bb037
AW
122 (difference free bound)))
123 (($ $cont label ($ $kclause arity body alternate))
124 (let ((free (visit-cont body bound)))
125 (if alternate
126 (union (visit-cont alternate bound) free)
4b8de65e 127 free)))
cf8bb037
AW
128 (($ $cont) '())))
129 (define (visit-term term bound)
130 (match term
131 (($ $letk conts body)
132 (fold (lambda (cont free)
133 (union (visit-cont cont bound) free))
134 (visit-term body bound)
135 conts))
136 (($ $letrec names vars (($ $fun () cont) ...) body)
137 (let ((bound (append vars bound)))
983413a1 138 (for-each add-named-fun! vars cont)
cf8bb037
AW
139 (fold (lambda (cont free)
140 (union (visit-cont cont bound) free))
141 (visit-term body bound)
142 cont)))
143 (($ $continue k src ($ $fun () body))
8b1a4b23
AW
144 (match (lookup-predecessors k dfg)
145 ((_) (match (lookup-cont k dfg)
146 (($ $kargs (name) (var))
983413a1 147 (add-named-fun! var body))))
8b1a4b23 148 (_ #f))
cf8bb037
AW
149 (visit-cont body bound))
150 (($ $continue k src exp)
151 (visit-exp exp bound))))
152 (define (visit-exp exp bound)
153 (define (adjoin var free)
154 (if (or (memq var bound) (memq var free))
155 free
156 (cons var free)))
157 (match exp
158 ((or ($ $void) ($ $const) ($ $prim)) '())
159 (($ $call proc args)
8b1a4b23 160 (for-each clear-well-known! args)
cf8bb037
AW
161 (fold adjoin (adjoin proc '()) args))
162 (($ $primcall name args)
8b1a4b23 163 (for-each clear-well-known! args)
cf8bb037
AW
164 (fold adjoin '() args))
165 (($ $values args)
8b1a4b23 166 (for-each clear-well-known! args)
cf8bb037
AW
167 (fold adjoin '() args))
168 (($ $prompt escape? tag handler)
8b1a4b23 169 (clear-well-known! tag)
cf8bb037 170 (adjoin tag '()))))
4b8de65e 171
cf8bb037
AW
172 (let ((free (visit-cont exp '())))
173 (unless (null? free)
174 (error "Expected no free vars in toplevel thunk" free exp))
983413a1 175 (values free-vars named-funs well-known))))
4b8de65e 176
983413a1 177(define (convert-one label free-vars named-funs well-known)
8b1a4b23 178 (match (hashq-ref free-vars label)
cf8bb037
AW
179 ((free . (and fun ($ $cont _ ($ $kfun _ _ self))))
180 (define (visit-cont cont)
181 (rewrite-cps-cont cont
182 (($ $cont label ($ $kargs names vars body))
183 (label ($kargs names vars ,(visit-term body))))
184 (($ $cont label ($ $kfun src meta self tail clause))
983413a1
AW
185 (label ($kfun src meta self ,tail
186 ,(and clause (visit-cont clause)))))
cf8bb037
AW
187 (($ $cont label ($ $kclause arity body alternate))
188 (label ($kclause ,arity ,(visit-cont body)
189 ,(and alternate (visit-cont alternate)))))
190 (($ $cont) ,cont)))
191 (define (visit-term term)
192 (match term
193 (($ $letk conts body)
194 (build-cps-term
195 ($letk ,(map visit-cont conts) ,(visit-term body))))
4b8de65e 196
cf8bb037
AW
197 ;; Remove letrec.
198 (($ $letrec names vars funs body)
199 (let lp ((in (map list names vars funs))
200 (bindings (lambda (body) body))
201 (body (visit-term body)))
202 (match in
203 (() (bindings body))
204 (((name var ($ $fun ()
205 (and fun-body
206 ($ $cont kfun ($ $kfun src))))) . in)
8b1a4b23 207 (match (hashq-ref free-vars kfun)
cf8bb037
AW
208 ((fun-free . _)
209 (lp in
210 (lambda (body)
211 (let-fresh (k) ()
212 (build-cps-term
213 ($letk ((k ($kargs (name) (var) ,(bindings body))))
214 ($continue k src
215 ($closure kfun (length fun-free)))))))
216 (init-closure src var fun-free self free body))))))))
4b8de65e 217
cf8bb037
AW
218 (($ $continue k src (or ($ $void) ($ $const) ($ $prim)))
219 term)
4b8de65e 220
cf8bb037 221 (($ $continue k src ($ $fun () ($ $cont kfun)))
8b1a4b23 222 (match (hashq-ref free-vars kfun)
cf8bb037
AW
223 ((() . _)
224 (build-cps-term ($continue k src ($closure kfun 0))))
225 ((fun-free . _)
226 (let-fresh (kinit) (v)
227 (build-cps-term
228 ($letk ((kinit ($kargs (v) (v)
229 ,(init-closure
230 src v fun-free self free
231 (build-cps-term
232 ($continue k src ($values (v))))))))
233 ($continue kinit src
234 ($closure kfun (length fun-free)))))))))
b3ae2b50 235
cf8bb037 236 (($ $continue k src ($ $call proc args))
983413a1
AW
237 (let ((def (hashq-ref named-funs proc))
238 (known? (bitvector-ref well-known proc)))
239 (convert-free-vars (cons proc args) self free
240 (match-lambda
241 ((proc . args)
242 (rewrite-cps-term def
243 (($ $cont label)
244 ($continue k src
245 ($callk label proc args)))
246 (#f
247 ($continue k src
248 ($call proc args)))))))))
4b8de65e 249
cf8bb037
AW
250 (($ $continue k src ($ $callk k* proc args))
251 (convert-free-vars (cons proc args) self free
252 (match-lambda
253 ((proc . args)
254 (build-cps-term
255 ($continue k src ($callk k* proc args)))))))
4b8de65e 256
cf8bb037
AW
257 (($ $continue k src ($ $primcall name args))
258 (convert-free-vars args self free
259 (lambda (args)
260 (build-cps-term
261 ($continue k src ($primcall name args))))))
4b8de65e 262
cf8bb037
AW
263 (($ $continue k src ($ $values args))
264 (convert-free-vars args self free
265 (lambda (args)
266 (build-cps-term
267 ($continue k src ($values args))))))
4b8de65e 268
cf8bb037
AW
269 (($ $continue k src ($ $prompt escape? tag handler))
270 (convert-free-var tag self free
271 (lambda (tag)
272 (build-cps-term
273 ($continue k src
274 ($prompt escape? tag handler))))))))
275 (visit-cont fun))))
4b8de65e 276
a0329d01 277(define (convert-closures fun)
4b8de65e
AW
278 "Convert free reference in @var{exp} to primcalls to @code{free-ref},
279and allocate and initialize flat closures."
8b1a4b23
AW
280 (let ((dfg (compute-dfg fun)))
281 (with-fresh-name-state-from-dfg dfg
282 (call-with-values (lambda () (analyze-closures fun dfg))
983413a1 283 (lambda (free-vars named-funs well-known)
8b1a4b23
AW
284 (let ((labels (sort (hash-map->list (lambda (k v) k) free-vars) <)))
285 (build-cps-term
983413a1
AW
286 ($program
287 ,(map (cut convert-one <> free-vars named-funs well-known)
288 labels)))))))))