Commit | Line | Data |
---|---|---|
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 | 48 | If @var{var} is free (i.e., present in @var{free},), it is replaced |
4b8de65e | 49 | by a closure reference via a @code{free-ref} primcall, and @var{k} is |
cf8bb037 AW |
50 | called with the new var. Otherwise @var{var} is bound, so @var{k} is |
51 | called 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 |
67 | term." | |
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 | |
80 | label of the outer procedure, where the initialization will be | |
cf8bb037 | 81 | performed, 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}, |
279 | and 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))))))))) |