Refactor to closure-conversion
[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
8b1a4b23 45(define (analyze-closures exp dfg)
cf8bb037
AW
46 "Compute the set of free variables for all $fun instances in
47@var{exp}."
8b1a4b23 48 (let ((free-vars (make-hash-table))
983413a1 49 (named-funs (make-hash-table))
6dc886fa 50 (well-known-vars (make-bitvector (var-counter) #t)))
983413a1
AW
51 (define (add-named-fun! var cont)
52 (hashq-set! named-funs var cont))
8b1a4b23 53 (define (clear-well-known! var)
6dc886fa
AW
54 (bitvector-set! well-known-vars var #f))
55 (define (compute-well-known-labels)
56 (let ((bv (make-bitvector (label-counter) #f)))
57 (hash-for-each
58 (lambda (var cont)
59 (match cont
60 (($ $cont label ($ $kfun src meta self))
61 (unless (equal? var self)
62 (bitvector-set! bv label
63 (and (bitvector-ref well-known-vars var)
64 (bitvector-ref well-known-vars self)))))))
65 named-funs)
66 bv))
cf8bb037
AW
67 (define (union a b)
68 (lset-union eq? a b))
69 (define (difference a b)
70 (lset-difference eq? a b))
71 (define (visit-cont cont bound)
72 (match cont
73 (($ $cont label ($ $kargs names vars body))
74 (visit-term body (append vars bound)))
75 (($ $cont label ($ $kfun src meta self tail clause))
983413a1 76 (add-named-fun! self cont)
cf8bb037
AW
77 (let ((free (if clause
78 (visit-cont clause (list self))
79 '())))
6dc886fa 80 (hashq-set! free-vars label free)
cf8bb037
AW
81 (difference free bound)))
82 (($ $cont label ($ $kclause arity body alternate))
83 (let ((free (visit-cont body bound)))
84 (if alternate
85 (union (visit-cont alternate bound) free)
4b8de65e 86 free)))
cf8bb037
AW
87 (($ $cont) '())))
88 (define (visit-term term bound)
89 (match term
90 (($ $letk conts body)
91 (fold (lambda (cont free)
92 (union (visit-cont cont bound) free))
93 (visit-term body bound)
94 conts))
95 (($ $letrec names vars (($ $fun () cont) ...) body)
96 (let ((bound (append vars bound)))
983413a1 97 (for-each add-named-fun! vars cont)
cf8bb037
AW
98 (fold (lambda (cont free)
99 (union (visit-cont cont bound) free))
100 (visit-term body bound)
101 cont)))
102 (($ $continue k src ($ $fun () body))
8b1a4b23
AW
103 (match (lookup-predecessors k dfg)
104 ((_) (match (lookup-cont k dfg)
105 (($ $kargs (name) (var))
983413a1 106 (add-named-fun! var body))))
8b1a4b23 107 (_ #f))
cf8bb037
AW
108 (visit-cont body bound))
109 (($ $continue k src exp)
110 (visit-exp exp bound))))
111 (define (visit-exp exp bound)
112 (define (adjoin var free)
113 (if (or (memq var bound) (memq var free))
114 free
115 (cons var free)))
116 (match exp
117 ((or ($ $void) ($ $const) ($ $prim)) '())
118 (($ $call proc args)
8b1a4b23 119 (for-each clear-well-known! args)
cf8bb037
AW
120 (fold adjoin (adjoin proc '()) args))
121 (($ $primcall name args)
8b1a4b23 122 (for-each clear-well-known! args)
cf8bb037
AW
123 (fold adjoin '() args))
124 (($ $values args)
8b1a4b23 125 (for-each clear-well-known! args)
cf8bb037
AW
126 (fold adjoin '() args))
127 (($ $prompt escape? tag handler)
8b1a4b23 128 (clear-well-known! tag)
cf8bb037 129 (adjoin tag '()))))
4b8de65e 130
cf8bb037
AW
131 (let ((free (visit-cont exp '())))
132 (unless (null? free)
133 (error "Expected no free vars in toplevel thunk" free exp))
6dc886fa 134 (values free-vars named-funs (compute-well-known-labels)))))
4b8de65e 135
32e62c2d 136(define (prune-free-vars free-vars named-funs well-known var-aliases)
cd130361
AW
137 (define (well-known? label)
138 (bitvector-ref well-known label))
32e62c2d
AW
139 (let ((eliminated (make-bitvector (label-counter) #f))
140 (label-aliases (make-vector (label-counter) #f)))
cd130361
AW
141 (let lp ((label 0))
142 (let ((label (bit-position #t well-known label)))
143 (when label
144 (match (hashq-ref free-vars label)
32e62c2d
AW
145 ;; Mark all well-known closures that have no free variables
146 ;; for elimination.
cd130361 147 (() (bitvector-set! eliminated label #t))
32e62c2d
AW
148 ;; Replace well-known closures that have just one free
149 ;; variable by references to that free variable.
150 ((var)
151 (vector-set! label-aliases label var))
cd130361
AW
152 (_ #f))
153 (lp (1+ label)))))
32e62c2d 154 ;; Iterative free variable elimination.
cd130361
AW
155 (let lp ()
156 (let ((recurse? #f))
32e62c2d
AW
157 (define (adjoin elt list)
158 ;; Normally you wouldn't see duplicates in a free variable
159 ;; list, but with aliases that is possible.
160 (if (memq elt list) list (cons elt list)))
161 (define (filter-out-eliminated free)
162 (match free
163 (() '())
164 ((var . free)
165 (let lp ((var var) (alias-stack '()))
166 (match (hashq-ref named-funs var)
167 (($ $cont label)
168 (cond
169 ((bitvector-ref eliminated label)
170 (filter-out-eliminated free))
171 ((vector-ref label-aliases label)
172 => (lambda (var)
173 (cond
174 ((memq label alias-stack)
175 ;; We have found a set of mutually recursive
176 ;; well-known procedures, each of which only
177 ;; closes over one of the others. Mark them
178 ;; all for elimination.
179 (for-each (lambda (label)
180 (bitvector-set! eliminated label #t)
181 (set! recurse? #t))
182 alias-stack)
183 (filter-out-eliminated free))
184 (else
185 (lp var (cons label alias-stack))))))
186 (else
187 (adjoin var (filter-out-eliminated free)))))
188 (_ (adjoin var (filter-out-eliminated free))))))))
cd130361
AW
189 (hash-for-each-handle
190 (lambda (pair)
191 (match pair
192 ((label . ()) #t)
193 ((label . free)
32e62c2d
AW
194 (let ((orig-nfree (length free))
195 (free (filter-out-eliminated free)))
cd130361 196 (set-cdr! pair free)
32e62c2d
AW
197 ;; If we managed to eliminate one or more free variables
198 ;; from a well-known function, it could be that we can
199 ;; eliminate or alias this function as well.
200 (when (and (well-known? label)
201 (< (length free) orig-nfree))
202 (match free
203 (()
204 (bitvector-set! eliminated label #t)
205 (set! recurse? #t))
206 ((var)
207 (vector-set! label-aliases label var)
208 (set! recurse? #t))
209 (_ #t)))))))
cd130361
AW
210 free-vars)
211 ;; Iterate to fixed point.
32e62c2d
AW
212 (when recurse? (lp))))
213 ;; Populate var-aliases from label-aliases.
214 (hash-for-each (lambda (var cont)
215 (match cont
216 (($ $cont label)
217 (let ((alias (vector-ref label-aliases label)))
218 (when alias
219 (vector-set! var-aliases var alias))))))
220 named-funs)))
cd130361 221
32e62c2d 222(define (convert-one label fun free-vars named-funs well-known aliases)
cd130361
AW
223 (define (well-known? label)
224 (bitvector-ref well-known label))
225
6dc886fa 226 (let ((free (hashq-ref free-vars label))
cd130361 227 (self-known? (well-known? label))
6dc886fa 228 (self (match fun (($ $kfun _ _ self) self))))
2920554a
AW
229 (define (convert-free-var var k)
230 "Convert one possibly free variable reference to a bound reference.
231
232If @var{var} is free, it is replaced by a closure reference via a
233@code{free-ref} primcall, and @var{k} is called with the new var.
234Otherwise @var{var} is bound, so @var{k} is called with @var{var}."
235 (cond
236 ((list-index (cut eq? <> var) free)
237 => (lambda (free-idx)
238 (match (cons self-known? free)
239 ;; A reference to the one free var of a well-known function.
240 ((#t _) (k self))
241 ;; A reference to one of the two free vars in a well-known
242 ;; function.
243 ((#t _ _)
244 (let-fresh (k*) (var*)
245 (build-cps-term
246 ($letk ((k* ($kargs (var*) (var*) ,(k var*))))
247 ($continue k* #f
248 ($primcall (match free-idx (0 'car) (1 'cdr)) (self)))))))
249 (_
250 (let-fresh (k* kidx) (idx var*)
251 (build-cps-term
252 ($letk ((kidx ($kargs ('idx) (idx)
253 ($letk ((k* ($kargs (var*) (var*) ,(k var*))))
254 ($continue k* #f
255 ($primcall
256 (cond
257 ((not self-known?) 'free-ref)
258 ((<= free-idx #xff) 'vector-ref/immediate)
259 (else 'vector-ref))
260 (self idx)))))))
261 ($continue kidx #f ($const free-idx)))))))))
262 (else (k var))))
263
264 (define (convert-free-vars vars k)
265 "Convert a number of possibly free references to bound references.
266@var{k} is called with the bound references, and should return the
267term."
268 (match vars
269 (() (k '()))
270 ((var . vars)
271 (convert-free-var var
272 (lambda (var)
273 (convert-free-vars vars
274 (lambda (vars)
275 (k (cons var vars)))))))))
276
277 (define (allocate-closure src name var label known? free body)
278 "Allocate a new closure."
279 (match (cons known? free)
280 ((#f . _)
281 (let-fresh (k*) ()
282 (build-cps-term
283 ($letk ((k* ($kargs (name) (var) ,body)))
284 ($continue k* src
285 ($closure label (length free)))))))
286 ((#t)
287 ;; Well-known closure with no free variables; elide the
288 ;; binding entirely.
289 body)
290 ((#t _)
291 ;; Well-known closure with one free variable; the free var is the
292 ;; closure, and no new binding need be made.
293 body)
294 ((#t _ _)
295 ;; Well-known closure with two free variables; the closure is a
296 ;; pair.
297 (let-fresh (kinit kfalse) (false)
298 (build-cps-term
299 ($letk ((kinit ($kargs (name) (var)
300 ,body))
301 (kfalse ($kargs ('false) (false)
302 ($continue kinit src
303 ($primcall 'cons (false false))))))
304 ($continue kfalse src ($const #f))))))
305 ;; Well-known callee with more than two free variables; the closure
306 ;; is a vector.
307 ((#t . _)
308 (let ((nfree (length free)))
309 (let-fresh (kinit klen kfalse) (false len-var)
310 (build-cps-term
311 ($letk ((kinit ($kargs (name) (var) ,body))
312 (kfalse
313 ($kargs ('false) (false)
314 ($letk ((klen
315 ($kargs ('len) (len-var)
316 ($continue kinit src
317 ($primcall (if (<= nfree #xff)
318 'make-vector/immediate
319 'make-vector)
320 (len-var false))))))
321 ($continue klen src ($const nfree))))))
322 ($continue kfalse src ($const #f)))))))))
323
324 (define (init-closure src var known? closure-free body)
325 "Initialize the free variables @var{closure-free} in a closure
326bound to @var{var}, and continue with @var{body}."
327 (match (cons known? closure-free)
328 ;; Well-known callee with no free variables; no initialization
329 ;; necessary.
330 ((#t) body)
331 ;; Well-known callee with one free variable; no initialization
332 ;; necessary.
333 ((#t _) body)
334 ;; Well-known callee with two free variables; do a set-car! and
335 ;; set-cdr!.
336 ((#t v0 v1)
337 (let-fresh (kcar kcdr) ()
338 (convert-free-var
339 v0
340 (lambda (v0)
341 (build-cps-term
342 ($letk ((kcar ($kargs () ()
343 ,(convert-free-var
344 v1
345 (lambda (v1)
346 (build-cps-term
347 ($letk ((kcdr ($kargs () () ,body)))
348 ($continue kcdr src
349 ($primcall 'set-cdr! (var v1))))))))))
350 ($continue kcar src
351 ($primcall 'set-car! (var v0)))))))))
352 ;; Otherwise residualize a sequence of vector-set! or free-set!,
353 ;; depending on whether the callee is well-known or not.
354 (_
355 (fold (lambda (free idx body)
356 (let-fresh (k) (idxvar)
357 (build-cps-term
358 ($letk ((k ($kargs () () ,body)))
359 ,(convert-free-var
360 free
361 (lambda (free)
362 (build-cps-term
363 ($letconst (('idx idxvar idx))
364 ($continue k src
365 ($primcall (cond
366 ((not known?) 'free-set!)
367 ((<= idx #xff) 'vector-set!/immediate)
368 (else 'vector-set!))
369 (var idxvar free)))))))))))
370 body
371 closure-free
372 (iota (length closure-free))))))
373
374 ;; Load the closure for a known call. The callee may or may not be
375 ;; known at all call sites.
376 (define (convert-known-proc-call var label self self-known? free k)
377 ;; Well-known closures with one free variable are replaced at their
378 ;; use sites by uses of the one free variable. The use sites of a
379 ;; well-known closures are only in well-known proc calls, and in
380 ;; free lists of other closures. Here we handle the call case; the
381 ;; free list case is handled by prune-free-vars.
382 (define (rename var)
383 (let ((var* (vector-ref aliases var)))
384 (if var*
385 (rename var*)
386 var)))
387 (match (cons (well-known? label)
388 (hashq-ref free-vars label))
389 ((#t)
390 ;; Calling a well-known procedure with no free variables; pass #f
391 ;; as the closure.
392 (let-fresh (k*) (v*)
393 (build-cps-term
394 ($letk ((k* ($kargs (v*) (v*) ,(k v*))))
395 ($continue k* #f ($const #f))))))
396 ((#t _)
397 ;; Calling a well-known procedure with one free variable; pass
398 ;; the free variable as the closure.
399 (convert-free-var (rename var) k))
400 (_
401 (convert-free-var var k))))
402
6dc886fa
AW
403 (define (visit-cont cont)
404 (rewrite-cps-cont cont
405 (($ $cont label ($ $kargs names vars body))
406 (label ($kargs names vars ,(visit-term body))))
407 (($ $cont label ($ $kfun src meta self tail clause))
408 (label ($kfun src meta self ,tail
409 ,(and clause (visit-cont clause)))))
410 (($ $cont label ($ $kclause arity body alternate))
411 (label ($kclause ,arity ,(visit-cont body)
412 ,(and alternate (visit-cont alternate)))))
413 (($ $cont) ,cont)))
414 (define (visit-term term)
415 (match term
416 (($ $letk conts body)
417 (build-cps-term
418 ($letk ,(map visit-cont conts) ,(visit-term body))))
4b8de65e 419
6dc886fa
AW
420 ;; Remove letrec.
421 (($ $letrec names vars funs body)
422 (let lp ((in (map list names vars funs))
423 (bindings (lambda (body) body))
424 (body (visit-term body)))
425 (match in
426 (() (bindings body))
427 (((name var ($ $fun ()
428 (and fun-body
429 ($ $cont kfun ($ $kfun src))))) . in)
cd130361
AW
430 (let ((fun-free (hashq-ref free-vars kfun)))
431 (lp in
432 (lambda (body)
433 (allocate-closure
434 src name var kfun (well-known? kfun) fun-free
435 (bindings body)))
436 (init-closure
2920554a 437 src var (well-known? kfun) fun-free
cd130361 438 body)))))))
4b8de65e 439
6dc886fa
AW
440 (($ $continue k src (or ($ $void) ($ $const) ($ $prim)))
441 term)
b3ae2b50 442
6dc886fa 443 (($ $continue k src ($ $fun () ($ $cont kfun)))
cd130361 444 (let ((fun-free (hashq-ref free-vars kfun)))
32e62c2d
AW
445 (match (cons (well-known? kfun) fun-free)
446 ((known?)
6dc886fa 447 (build-cps-term
32e62c2d 448 ($continue k src ,(if known?
cd130361
AW
449 (build-cps-exp ($const #f))
450 (build-cps-exp ($closure kfun 0))))))
32e62c2d
AW
451 ((#t _)
452 ;; A well-known closure of one free variable is replaced
453 ;; at each use with the free variable itself, so we don't
454 ;; need a binding at all; and yet, the continuation
455 ;; expects one value, so give it something. DCE should
456 ;; clean up later.
457 (build-cps-term
458 ($continue k src ,(build-cps-exp ($const #f)))))
cd130361
AW
459 (_
460 (let-fresh () (var)
461 (allocate-closure
462 src #f var kfun (well-known? kfun) fun-free
463 (init-closure
2920554a 464 src var (well-known? kfun) fun-free
cd130361 465 (build-cps-term ($continue k src ($values (var)))))))))))
6dc886fa
AW
466
467 (($ $continue k src ($ $call proc args))
468 (match (hashq-ref named-funs proc)
cd130361 469 (($ $cont kfun)
6dc886fa 470 (convert-known-proc-call
cd130361 471 proc kfun self self-known? free
6dc886fa 472 (lambda (proc)
2920554a 473 (convert-free-vars args
6dc886fa
AW
474 (lambda (args)
475 (build-cps-term
476 ($continue k src
cd130361 477 ($callk kfun proc args))))))))
6dc886fa 478 (#f
2920554a 479 (convert-free-vars (cons proc args)
983413a1
AW
480 (match-lambda
481 ((proc . args)
6dc886fa
AW
482 (build-cps-term
483 ($continue k src
484 ($call proc args)))))))))
4b8de65e 485
6dc886fa 486 (($ $continue k src ($ $primcall name args))
2920554a 487 (convert-free-vars args
6dc886fa
AW
488 (lambda (args)
489 (build-cps-term
490 ($continue k src ($primcall name args))))))
4b8de65e 491
6dc886fa 492 (($ $continue k src ($ $values args))
2920554a 493 (convert-free-vars args
6dc886fa
AW
494 (lambda (args)
495 (build-cps-term
496 ($continue k src ($values args))))))
4b8de65e 497
6dc886fa 498 (($ $continue k src ($ $prompt escape? tag handler))
2920554a 499 (convert-free-var tag
6dc886fa
AW
500 (lambda (tag)
501 (build-cps-term
502 ($continue k src
503 ($prompt escape? tag handler))))))))
504 (visit-cont (build-cps-cont (label ,fun)))))
4b8de65e 505
a0329d01 506(define (convert-closures fun)
4b8de65e
AW
507 "Convert free reference in @var{exp} to primcalls to @code{free-ref},
508and allocate and initialize flat closures."
8b1a4b23
AW
509 (let ((dfg (compute-dfg fun)))
510 (with-fresh-name-state-from-dfg dfg
511 (call-with-values (lambda () (analyze-closures fun dfg))
983413a1 512 (lambda (free-vars named-funs well-known)
32e62c2d
AW
513 (let ((labels (sort (hash-map->list (lambda (k v) k) free-vars) <))
514 (aliases (make-vector (var-counter) #f)))
515 (prune-free-vars free-vars named-funs well-known aliases)
8b1a4b23 516 (build-cps-term
983413a1 517 ($program
6dc886fa
AW
518 ,(map (lambda (label)
519 (convert-one label (lookup-cont label dfg)
32e62c2d 520 free-vars named-funs well-known aliases))
983413a1 521 labels)))))))))