Commit | Line | Data |
---|---|---|
4fefc3a8 AW |
1 | ;;; Continuation-passing style (CPS) intermediate language (IL) |
2 | ||
e2fafeb9 | 3 | ;; Copyright (C) 2013, 2014, 2015 Free Software Foundation, Inc. |
4fefc3a8 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 Tree-IL to the continuation-passing style (CPS) | |
22 | ;;; language. | |
23 | ;;; | |
24 | ;;; CPS is a lower-level representation than Tree-IL. Converting to | |
25 | ;;; CPS, beyond adding names for all control points and all values, | |
26 | ;;; simplifies expressions in the following ways, among others: | |
27 | ;;; | |
28 | ;;; * Fixing the order of evaluation. | |
29 | ;;; | |
30 | ;;; * Converting assigned variables to boxed variables. | |
31 | ;;; | |
32 | ;;; * Requiring that Scheme's <letrec> has already been lowered to | |
33 | ;;; <fix>. | |
34 | ;;; | |
35 | ;;; * Inlining default-value initializers into lambda-case | |
36 | ;;; expressions. | |
37 | ;;; | |
38 | ;;; * Inlining prompt bodies. | |
39 | ;;; | |
40 | ;;; * Turning toplevel and module references into primcalls. This | |
41 | ;;; involves explicitly modelling the "scope" of toplevel lookups | |
42 | ;;; (indicating the module with respect to which toplevel bindings | |
43 | ;;; are resolved). | |
44 | ;;; | |
45 | ;;; The utility of CPS is that it gives a name to everything: every | |
46 | ;;; intermediate value, and every control point (continuation). As such | |
47 | ;;; it is more verbose than Tree-IL, but at the same time more simple as | |
48 | ;;; the number of concepts is reduced. | |
49 | ;;; | |
50 | ;;; Code: | |
51 | ||
52 | (define-module (language tree-il compile-cps) | |
53 | #:use-module (ice-9 match) | |
54 | #:use-module ((srfi srfi-1) #:select (fold fold-right filter-map)) | |
55 | #:use-module (srfi srfi-26) | |
56 | #:use-module ((system foreign) #:select (make-pointer pointer->scm)) | |
57 | #:use-module (language cps) | |
58 | #:use-module (language cps primitives) | |
59 | #:use-module (language tree-il analyze) | |
60 | #:use-module (language tree-il optimize) | |
828ed944 | 61 | #:use-module (language tree-il) |
4fefc3a8 AW |
62 | #:export (compile-cps)) |
63 | ||
64 | ;;; Guile's semantics are that a toplevel lambda captures a reference on | |
65 | ;;; the current module, and that all contained lambdas use that module | |
66 | ;;; to resolve toplevel variables. This parameter tracks whether or not | |
67 | ;;; we are in a toplevel lambda. If we are in a lambda, the parameter | |
68 | ;;; is bound to a fresh name identifying the module that was current | |
69 | ;;; when the toplevel lambda is defined. | |
70 | ;;; | |
71 | ;;; This is more complicated than it need be. Ideally we should resolve | |
72 | ;;; all toplevel bindings to bindings from specific modules, unless the | |
73 | ;;; binding is unbound. This is always valid if the compilation unit | |
74 | ;;; sets the module explicitly, as when compiling a module, but it | |
75 | ;;; doesn't work for files auto-compiled for use with `load'. | |
76 | ;;; | |
77 | (define current-topbox-scope (make-parameter #f)) | |
48e65b44 AW |
78 | (define scope-counter (make-parameter #f)) |
79 | ||
80 | (define (fresh-scope-id) | |
81 | (let ((scope-id (scope-counter))) | |
82 | (scope-counter (1+ scope-id)) | |
83 | scope-id)) | |
4fefc3a8 AW |
84 | |
85 | (define (toplevel-box src name bound? val-proc) | |
9a1dfb7d | 86 | (let-fresh (kbox) (name-sym bound?-sym box) |
4fefc3a8 AW |
87 | (build-cps-term |
88 | ($letconst (('name name-sym name) | |
89 | ('bound? bound?-sym bound?)) | |
6e422a35 | 90 | ($letk ((kbox ($kargs ('box) (box) ,(val-proc box)))) |
4fefc3a8 AW |
91 | ,(match (current-topbox-scope) |
92 | (#f | |
93 | (build-cps-term | |
6e422a35 | 94 | ($continue kbox src |
4fefc3a8 AW |
95 | ($primcall 'resolve |
96 | (name-sym bound?-sym))))) | |
48e65b44 | 97 | (scope-id |
9a1dfb7d | 98 | (let-fresh () (scope-sym) |
4fefc3a8 | 99 | (build-cps-term |
48e65b44 | 100 | ($letconst (('scope scope-sym scope-id)) |
6e422a35 | 101 | ($continue kbox src |
4fefc3a8 AW |
102 | ($primcall 'cached-toplevel-box |
103 | (scope-sym name-sym bound?-sym))))))))))))) | |
104 | ||
105 | (define (module-box src module name public? bound? val-proc) | |
9a1dfb7d | 106 | (let-fresh (kbox) (module-sym name-sym public?-sym bound?-sym box) |
4fefc3a8 AW |
107 | (build-cps-term |
108 | ($letconst (('module module-sym module) | |
109 | ('name name-sym name) | |
110 | ('public? public?-sym public?) | |
111 | ('bound? bound?-sym bound?)) | |
6e422a35 AW |
112 | ($letk ((kbox ($kargs ('box) (box) ,(val-proc box)))) |
113 | ($continue kbox src | |
4fefc3a8 AW |
114 | ($primcall 'cached-module-box |
115 | (module-sym name-sym public?-sym bound?-sym)))))))) | |
116 | ||
48e65b44 | 117 | (define (capture-toplevel-scope src scope-id k) |
9a1dfb7d | 118 | (let-fresh (kmodule) (module scope-sym) |
4fefc3a8 | 119 | (build-cps-term |
48e65b44 | 120 | ($letconst (('scope scope-sym scope-id)) |
6e422a35 AW |
121 | ($letk ((kmodule ($kargs ('module) (module) |
122 | ($continue k src | |
123 | ($primcall 'cache-current-module! | |
124 | (module scope-sym)))))) | |
125 | ($continue kmodule src | |
4fefc3a8 AW |
126 | ($primcall 'current-module ()))))))) |
127 | ||
128 | (define (fold-formals proc seed arity gensyms inits) | |
129 | (match arity | |
130 | (($ $arity req opt rest kw allow-other-keys?) | |
131 | (let () | |
132 | (define (fold-req names gensyms seed) | |
133 | (match names | |
134 | (() (fold-opt opt gensyms inits seed)) | |
135 | ((name . names) | |
136 | (proc name (car gensyms) #f | |
137 | (fold-req names (cdr gensyms) seed))))) | |
138 | (define (fold-opt names gensyms inits seed) | |
139 | (match names | |
140 | (() (fold-rest rest gensyms inits seed)) | |
141 | ((name . names) | |
142 | (proc name (car gensyms) (car inits) | |
143 | (fold-opt names (cdr gensyms) (cdr inits) seed))))) | |
144 | (define (fold-rest rest gensyms inits seed) | |
145 | (match rest | |
146 | (#f (fold-kw kw gensyms inits seed)) | |
147 | (name (proc name (car gensyms) #f | |
148 | (fold-kw kw (cdr gensyms) inits seed))))) | |
149 | (define (fold-kw kw gensyms inits seed) | |
150 | (match kw | |
151 | (() | |
152 | (unless (null? gensyms) | |
153 | (error "too many gensyms")) | |
154 | (unless (null? inits) | |
155 | (error "too many inits")) | |
156 | seed) | |
157 | (((key name var) . kw) | |
e6cf744a AW |
158 | ;; Could be that var is not a gensym any more. |
159 | (when (symbol? var) | |
160 | (unless (eq? var (car gensyms)) | |
161 | (error "unexpected keyword arg order"))) | |
162 | (proc name (car gensyms) (car inits) | |
4fefc3a8 AW |
163 | (fold-kw kw (cdr gensyms) (cdr inits) seed))))) |
164 | (fold-req req gensyms seed))))) | |
165 | ||
e6cf744a | 166 | (define (unbound? src var kt kf) |
4fefc3a8 AW |
167 | (define tc8-iflag 4) |
168 | (define unbound-val 9) | |
169 | (define unbound-bits (logior (ash unbound-val 8) tc8-iflag)) | |
fd610047 | 170 | (let-fresh () (unbound) |
4fefc3a8 | 171 | (build-cps-term |
9a1dfb7d AW |
172 | ($letconst (('unbound unbound |
173 | (pointer->scm (make-pointer unbound-bits)))) | |
fd610047 AW |
174 | ($continue kf src |
175 | ($branch kt ($primcall 'eq? (var unbound)))))))) | |
4fefc3a8 AW |
176 | |
177 | (define (init-default-value name sym subst init body) | |
e6cf744a AW |
178 | (match (hashq-ref subst sym) |
179 | ((orig-var subst-var box?) | |
4fefc3a8 AW |
180 | (let ((src (tree-il-src init))) |
181 | (define (maybe-box k make-body) | |
182 | (if box? | |
9a1dfb7d | 183 | (let-fresh (kbox) (phi) |
4fefc3a8 | 184 | (build-cps-term |
6e422a35 AW |
185 | ($letk ((kbox ($kargs (name) (phi) |
186 | ($continue k src ($primcall 'box (phi)))))) | |
4fefc3a8 AW |
187 | ,(make-body kbox)))) |
188 | (make-body k))) | |
9a1dfb7d | 189 | (let-fresh (knext kbound kunbound kreceive krest) (val rest) |
4fefc3a8 | 190 | (build-cps-term |
e6cf744a | 191 | ($letk ((knext ($kargs (name) (subst-var) ,body))) |
4fefc3a8 AW |
192 | ,(maybe-box |
193 | knext | |
194 | (lambda (k) | |
195 | (build-cps-term | |
13085a82 | 196 | ($letk ((kbound ($kargs () () ($continue k src |
e6cf744a | 197 | ($values (orig-var))))) |
31086641 AW |
198 | (krest ($kargs (name 'rest) (val rest) |
199 | ($continue k src ($values (val))))) | |
36527695 | 200 | (kreceive ($kreceive (list name) 'rest krest)) |
31086641 | 201 | (kunbound ($kargs () () |
36527695 | 202 | ,(convert init kreceive subst)))) |
e6cf744a | 203 | ,(unbound? src orig-var kunbound kbound)))))))))))) |
4fefc3a8 AW |
204 | |
205 | ;; exp k-name alist -> term | |
206 | (define (convert exp k subst) | |
207 | ;; exp (v-name -> term) -> term | |
208 | (define (convert-arg exp k) | |
209 | (match exp | |
210 | (($ <lexical-ref> src name sym) | |
e6cf744a AW |
211 | (match (hashq-ref subst sym) |
212 | ((orig-var box #t) | |
9a1dfb7d | 213 | (let-fresh (kunboxed) (unboxed) |
4fefc3a8 | 214 | (build-cps-term |
6e422a35 AW |
215 | ($letk ((kunboxed ($kargs ('unboxed) (unboxed) ,(k unboxed)))) |
216 | ($continue kunboxed src ($primcall 'box-ref (box))))))) | |
e6cf744a AW |
217 | ((orig-var subst-var #f) (k subst-var)) |
218 | (var (k var)))) | |
4fefc3a8 | 219 | (else |
9a1dfb7d | 220 | (let-fresh (kreceive karg) (arg rest) |
6e422a35 | 221 | (build-cps-term |
31086641 | 222 | ($letk ((karg ($kargs ('arg 'rest) (arg rest) ,(k arg))) |
36527695 AW |
223 | (kreceive ($kreceive '(arg) 'rest karg))) |
224 | ,(convert exp kreceive subst))))))) | |
4fefc3a8 AW |
225 | ;; (exp ...) ((v-name ...) -> term) -> term |
226 | (define (convert-args exps k) | |
227 | (match exps | |
228 | (() (k '())) | |
229 | ((exp . exps) | |
230 | (convert-arg exp | |
231 | (lambda (name) | |
232 | (convert-args exps | |
233 | (lambda (names) | |
234 | (k (cons name names))))))))) | |
235 | (define (box-bound-var name sym body) | |
e6cf744a AW |
236 | (match (hashq-ref subst sym) |
237 | ((orig-var subst-var #t) | |
9a1dfb7d | 238 | (let-fresh (k) () |
4fefc3a8 | 239 | (build-cps-term |
e6cf744a AW |
240 | ($letk ((k ($kargs (name) (subst-var) ,body))) |
241 | ($continue k #f ($primcall 'box (orig-var))))))) | |
4fefc3a8 | 242 | (else body))) |
e6cf744a AW |
243 | (define (bound-var sym) |
244 | (match (hashq-ref subst sym) | |
245 | ((var . _) var) | |
246 | ((? exact-integer? var) var))) | |
4fefc3a8 AW |
247 | |
248 | (match exp | |
249 | (($ <lexical-ref> src name sym) | |
e6cf744a AW |
250 | (rewrite-cps-term (hashq-ref subst sym) |
251 | ((orig-var box #t) ($continue k src ($primcall 'box-ref (box)))) | |
252 | ((orig-var subst-var #f) ($continue k src ($values (subst-var)))) | |
253 | (var ($continue k src ($values (var)))))) | |
4fefc3a8 AW |
254 | |
255 | (($ <void> src) | |
a9ec16f9 | 256 | (build-cps-term ($continue k src ($const *unspecified*)))) |
4fefc3a8 AW |
257 | |
258 | (($ <const> src exp) | |
6e422a35 | 259 | (build-cps-term ($continue k src ($const exp)))) |
4fefc3a8 AW |
260 | |
261 | (($ <primitive-ref> src name) | |
6e422a35 | 262 | (build-cps-term ($continue k src ($prim name)))) |
4fefc3a8 AW |
263 | |
264 | (($ <lambda> fun-src meta body) | |
265 | (let () | |
266 | (define (convert-clauses body ktail) | |
267 | (match body | |
90dce16d | 268 | (#f #f) |
4fefc3a8 AW |
269 | (($ <lambda-case> src req opt rest kw inits gensyms body alternate) |
270 | (let* ((arity (make-$arity req (or opt '()) rest | |
e6cf744a AW |
271 | (map (match-lambda |
272 | ((kw name sym) | |
273 | (list kw name (bound-var sym)))) | |
274 | (if kw (cdr kw) '())) | |
275 | (and kw (car kw)))) | |
4fefc3a8 AW |
276 | (names (fold-formals (lambda (name sym init names) |
277 | (cons name names)) | |
278 | '() | |
279 | arity gensyms inits))) | |
90dce16d AW |
280 | (let ((bound-vars (map bound-var gensyms))) |
281 | (let-fresh (kclause kargs) () | |
282 | (build-cps-cont | |
283 | (kclause | |
284 | ($kclause ,arity | |
285 | (kargs | |
286 | ($kargs names bound-vars | |
287 | ,(fold-formals | |
288 | (lambda (name sym init body) | |
289 | (if init | |
290 | (init-default-value name sym subst init body) | |
291 | (box-bound-var name sym body))) | |
292 | (convert body ktail subst) | |
293 | arity gensyms inits))) | |
294 | ,(convert-clauses alternate ktail)))))))))) | |
4fefc3a8 | 295 | (if (current-topbox-scope) |
8320f504 | 296 | (let-fresh (kfun ktail) (self) |
4fefc3a8 | 297 | (build-cps-term |
6e422a35 | 298 | ($continue k fun-src |
24b611e8 | 299 | ($fun '() |
8320f504 | 300 | (kfun ($kfun fun-src meta self (ktail ($ktail)) |
24b611e8 | 301 | ,(convert-clauses body ktail))))))) |
48e65b44 AW |
302 | (let ((scope-id (fresh-scope-id))) |
303 | (let-fresh (kscope) () | |
304 | (build-cps-term | |
305 | ($letk ((kscope | |
306 | ($kargs () () | |
307 | ,(parameterize ((current-topbox-scope scope-id)) | |
308 | (convert exp k subst))))) | |
309 | ,(capture-toplevel-scope fun-src scope-id kscope)))))))) | |
4fefc3a8 AW |
310 | |
311 | (($ <module-ref> src mod name public?) | |
312 | (module-box | |
313 | src mod name public? #t | |
314 | (lambda (box) | |
6e422a35 | 315 | (build-cps-term ($continue k src ($primcall 'box-ref (box))))))) |
4fefc3a8 AW |
316 | |
317 | (($ <module-set> src mod name public? exp) | |
318 | (convert-arg exp | |
319 | (lambda (val) | |
320 | (module-box | |
321 | src mod name public? #f | |
322 | (lambda (box) | |
6e422a35 AW |
323 | (build-cps-term |
324 | ($continue k src ($primcall 'box-set! (box val))))))))) | |
4fefc3a8 AW |
325 | |
326 | (($ <toplevel-ref> src name) | |
327 | (toplevel-box | |
328 | src name #t | |
329 | (lambda (box) | |
6e422a35 | 330 | (build-cps-term ($continue k src ($primcall 'box-ref (box))))))) |
4fefc3a8 AW |
331 | |
332 | (($ <toplevel-set> src name exp) | |
333 | (convert-arg exp | |
334 | (lambda (val) | |
335 | (toplevel-box | |
336 | src name #f | |
337 | (lambda (box) | |
6e422a35 AW |
338 | (build-cps-term |
339 | ($continue k src ($primcall 'box-set! (box val))))))))) | |
4fefc3a8 AW |
340 | |
341 | (($ <toplevel-define> src name exp) | |
342 | (convert-arg exp | |
343 | (lambda (val) | |
9a1dfb7d | 344 | (let-fresh (kname) (name-sym) |
4fefc3a8 AW |
345 | (build-cps-term |
346 | ($letconst (('name name-sym name)) | |
6e422a35 | 347 | ($continue k src ($primcall 'define! (name-sym val))))))))) |
4fefc3a8 AW |
348 | |
349 | (($ <call> src proc args) | |
350 | (convert-args (cons proc args) | |
351 | (match-lambda | |
352 | ((proc . args) | |
6e422a35 | 353 | (build-cps-term ($continue k src ($call proc args))))))) |
4fefc3a8 AW |
354 | |
355 | (($ <primcall> src name args) | |
58dee5b9 AW |
356 | (cond |
357 | ((branching-primitive? name) | |
e6cf744a AW |
358 | (convert-args args |
359 | (lambda (args) | |
fd610047 | 360 | (let-fresh (kt kf) () |
e6cf744a AW |
361 | (build-cps-term |
362 | ($letk ((kt ($kargs () () ($continue k src ($const #t)))) | |
fd610047 AW |
363 | (kf ($kargs () () ($continue k src ($const #f))))) |
364 | ($continue kf src | |
365 | ($branch kt ($primcall name args))))))))) | |
ae67b159 AW |
366 | ((and (eq? name 'not) (match args ((_) #t) (_ #f))) |
367 | (convert-args args | |
368 | (lambda (args) | |
369 | (let-fresh (kt kf) () | |
370 | (build-cps-term | |
371 | ($letk ((kt ($kargs () () ($continue k src ($const #f)))) | |
372 | (kf ($kargs () () ($continue k src ($const #t))))) | |
373 | ($continue kf src | |
374 | ($branch kt ($values args))))))))) | |
0d046513 AW |
375 | ((and (eq? name 'list) |
376 | (and-map (match-lambda | |
377 | ((or ($ <const>) | |
378 | ($ <void>) | |
379 | ($ <lambda>) | |
380 | ($ <lexical-ref>)) #t) | |
381 | (_ #f)) | |
382 | args)) | |
e6cf744a AW |
383 | ;; See note below in `canonicalize' about `vector'. The same |
384 | ;; thing applies to `list'. | |
0d046513 AW |
385 | (let lp ((args args) (k k)) |
386 | (match args | |
387 | (() | |
388 | (build-cps-term | |
6e422a35 | 389 | ($continue k src ($const '())))) |
0d046513 | 390 | ((arg . args) |
9a1dfb7d | 391 | (let-fresh (ktail) (tail) |
0d046513 | 392 | (build-cps-term |
6e422a35 | 393 | ($letk ((ktail ($kargs ('tail) (tail) |
0d046513 AW |
394 | ,(convert-arg arg |
395 | (lambda (head) | |
396 | (build-cps-term | |
6e422a35 | 397 | ($continue k src |
0d046513 AW |
398 | ($primcall 'cons (head tail))))))))) |
399 | ,(lp args ktail)))))))) | |
58dee5b9 AW |
400 | (else |
401 | (convert-args args | |
402 | (lambda (args) | |
6e422a35 | 403 | (build-cps-term ($continue k src ($primcall name args)))))))) |
4fefc3a8 AW |
404 | |
405 | ;; Prompts with inline handlers. | |
406 | (($ <prompt> src escape-only? tag body | |
407 | ($ <lambda> hsrc hmeta | |
408 | ($ <lambda-case> _ hreq #f hrest #f () hsyms hbody #f))) | |
409 | ;; Handler: | |
410 | ;; khargs: check args returned to handler, -> khbody | |
411 | ;; khbody: the handler, -> k | |
412 | ;; | |
413 | ;; Post-body: | |
414 | ;; krest: collect return vals from body to list, -> kpop | |
415 | ;; kpop: pop the prompt, -> kprim | |
416 | ;; kprim: load the values primitive, -> kret | |
417 | ;; kret: (apply values rvals), -> k | |
418 | ;; | |
419 | ;; Escape prompts evaluate the body with the continuation of krest. | |
420 | ;; Otherwise we do a no-inline call to body, continuing to krest. | |
421 | (convert-arg tag | |
422 | (lambda (tag) | |
e6cf744a AW |
423 | (let ((hnames (append hreq (if hrest (list hrest) '()))) |
424 | (bound-vars (map bound-var hsyms))) | |
9a1dfb7d | 425 | (let-fresh (khargs khbody kret kprim kpop krest kbody) (prim vals) |
4fefc3a8 | 426 | (build-cps-term |
36527695 | 427 | ;; FIXME: Attach hsrc to $kreceive. |
e6cf744a | 428 | ($letk* ((khbody ($kargs hnames bound-vars |
6e422a35 AW |
429 | ,(fold box-bound-var |
430 | (convert hbody k subst) | |
431 | hnames hsyms))) | |
36527695 | 432 | (khargs ($kreceive hreq hrest khbody)) |
6e422a35 | 433 | (kpop ($kargs ('rest) (vals) |
4fefc3a8 | 434 | ($letk ((kret |
4fefc3a8 AW |
435 | ($kargs () () |
436 | ($letk ((kprim | |
4fefc3a8 | 437 | ($kargs ('prim) (prim) |
6e422a35 | 438 | ($continue k src |
4fefc3a8 AW |
439 | ($primcall 'apply |
440 | (prim vals)))))) | |
6e422a35 | 441 | ($continue kprim src |
4fefc3a8 | 442 | ($prim 'values)))))) |
6e422a35 | 443 | ($continue kret src |
8d59d55e | 444 | ($primcall 'unwind ()))))) |
36527695 | 445 | (krest ($kreceive '() 'rest kpop))) |
4fefc3a8 AW |
446 | ,(if escape-only? |
447 | (build-cps-term | |
6e422a35 | 448 | ($letk ((kbody ($kargs () () |
4fefc3a8 | 449 | ,(convert body krest subst)))) |
7ab76a83 | 450 | ($continue kbody src ($prompt #t tag khargs)))) |
4fefc3a8 AW |
451 | (convert-arg body |
452 | (lambda (thunk) | |
453 | (build-cps-term | |
6e422a35 AW |
454 | ($letk ((kbody ($kargs () () |
455 | ($continue krest (tree-il-src body) | |
4fefc3a8 AW |
456 | ($primcall 'call-thunk/no-inline |
457 | (thunk)))))) | |
6e422a35 | 458 | ($continue kbody (tree-il-src body) |
7ab76a83 | 459 | ($prompt #f tag khargs)))))))))))))) |
4fefc3a8 | 460 | |
486013d6 AW |
461 | (($ <abort> src tag args ($ <const> _ ())) |
462 | (convert-args (cons tag args) | |
463 | (lambda (args*) | |
464 | (build-cps-term | |
6e422a35 AW |
465 | ($continue k src |
466 | ($primcall 'abort-to-prompt args*)))))) | |
486013d6 | 467 | |
4fefc3a8 | 468 | (($ <abort> src tag args tail) |
486013d6 AW |
469 | (convert-args (append (list (make-primitive-ref #f 'abort-to-prompt) |
470 | tag) | |
471 | args | |
472 | (list tail)) | |
4fefc3a8 | 473 | (lambda (args*) |
486013d6 | 474 | (build-cps-term |
6e422a35 | 475 | ($continue k src ($primcall 'apply args*)))))) |
4fefc3a8 AW |
476 | |
477 | (($ <conditional> src test consequent alternate) | |
fd610047 | 478 | (let-fresh (kt kf) () |
4fefc3a8 | 479 | (build-cps-term |
6e422a35 | 480 | ($letk* ((kt ($kargs () () ,(convert consequent k subst))) |
fd610047 | 481 | (kf ($kargs () () ,(convert alternate k subst)))) |
4fefc3a8 AW |
482 | ,(match test |
483 | (($ <primcall> src (? branching-primitive? name) args) | |
484 | (convert-args args | |
485 | (lambda (args) | |
6e422a35 | 486 | (build-cps-term |
fd610047 AW |
487 | ($continue kf src |
488 | ($branch kt ($primcall name args))))))) | |
4fefc3a8 AW |
489 | (_ (convert-arg test |
490 | (lambda (test) | |
6e422a35 | 491 | (build-cps-term |
fd610047 AW |
492 | ($continue kf src |
493 | ($branch kt ($values (test))))))))))))) | |
4fefc3a8 AW |
494 | |
495 | (($ <lexical-set> src name gensym exp) | |
496 | (convert-arg exp | |
497 | (lambda (exp) | |
e6cf744a AW |
498 | (match (hashq-ref subst gensym) |
499 | ((orig-var box #t) | |
4fefc3a8 | 500 | (build-cps-term |
6e422a35 | 501 | ($continue k src ($primcall 'box-set! (box exp))))))))) |
4fefc3a8 AW |
502 | |
503 | (($ <seq> src head tail) | |
9a1dfb7d | 504 | (let-fresh (kreceive kseq) (vals) |
4fefc3a8 | 505 | (build-cps-term |
31086641 | 506 | ($letk* ((kseq ($kargs ('vals) (vals) |
6e422a35 | 507 | ,(convert tail k subst))) |
36527695 AW |
508 | (kreceive ($kreceive '() 'vals kseq))) |
509 | ,(convert head kreceive subst))))) | |
4fefc3a8 AW |
510 | |
511 | (($ <let> src names syms vals body) | |
512 | (let lp ((names names) (syms syms) (vals vals)) | |
513 | (match (list names syms vals) | |
514 | ((() () ()) (convert body k subst)) | |
515 | (((name . names) (sym . syms) (val . vals)) | |
9a1dfb7d | 516 | (let-fresh (kreceive klet) (rest) |
4fefc3a8 | 517 | (build-cps-term |
e6cf744a | 518 | ($letk* ((klet ($kargs (name 'rest) ((bound-var sym) rest) |
31086641 AW |
519 | ,(box-bound-var name sym |
520 | (lp names syms vals)))) | |
36527695 AW |
521 | (kreceive ($kreceive (list name) 'rest klet))) |
522 | ,(convert val kreceive subst)))))))) | |
4fefc3a8 AW |
523 | |
524 | (($ <fix> src names gensyms funs body) | |
525 | ;; Some letrecs can be contified; that happens later. | |
526 | (if (current-topbox-scope) | |
34ff3af9 AW |
527 | (let ((vars (map bound-var gensyms))) |
528 | (let-fresh (krec) () | |
529 | (build-cps-term | |
530 | ($letk ((krec ($kargs names vars | |
531 | ,(convert body k subst)))) | |
532 | ($continue krec src | |
533 | ($rec names vars | |
534 | (map (lambda (fun) | |
535 | (match (convert fun k subst) | |
536 | (($ $continue _ _ (and fun ($ $fun))) | |
537 | fun))) | |
538 | funs))))))) | |
48e65b44 AW |
539 | (let ((scope-id (fresh-scope-id))) |
540 | (let-fresh (kscope) () | |
541 | (build-cps-term | |
542 | ($letk ((kscope | |
543 | ($kargs () () | |
544 | ,(parameterize ((current-topbox-scope scope-id)) | |
545 | (convert exp k subst))))) | |
546 | ,(capture-toplevel-scope src scope-id kscope))))))) | |
4fefc3a8 AW |
547 | |
548 | (($ <let-values> src exp | |
549 | ($ <lambda-case> lsrc req #f rest #f () syms body #f)) | |
e6cf744a AW |
550 | (let ((names (append req (if rest (list rest) '()))) |
551 | (bound-vars (map bound-var syms))) | |
9a1dfb7d | 552 | (let-fresh (kreceive kargs) () |
4fefc3a8 | 553 | (build-cps-term |
e6cf744a | 554 | ($letk* ((kargs ($kargs names bound-vars |
6e422a35 AW |
555 | ,(fold box-bound-var |
556 | (convert body k subst) | |
557 | names syms))) | |
36527695 AW |
558 | (kreceive ($kreceive req rest kargs))) |
559 | ,(convert exp kreceive subst)))))))) | |
4fefc3a8 AW |
560 | |
561 | (define (build-subst exp) | |
e6cf744a AW |
562 | "Compute a mapping from lexical gensyms to CPS variable indexes. CPS |
563 | uses small integers to identify variables, instead of gensyms. | |
564 | ||
565 | This subst table serves an additional purpose of mapping variables to | |
566 | replacements. The usual reason to replace one variable by another is | |
567 | assignment conversion. Default argument values is the other reason. | |
568 | ||
569 | The result is a hash table mapping symbols to substitutions (in the case | |
570 | that a variable is substituted) or to indexes. A substitution is a list | |
571 | of the form: | |
572 | ||
573 | (ORIG-INDEX SUBST-INDEX BOXED?) | |
574 | ||
575 | A true value for BOXED? indicates that the replacement variable is in a | |
576 | box. If a variable is not substituted, the mapped value is a small | |
577 | integer." | |
578 | (let ((table (make-hash-table))) | |
579 | (define (down exp) | |
580 | (match exp | |
581 | (($ <lexical-set> src name sym exp) | |
582 | (match (hashq-ref table sym) | |
583 | ((orig subst #t) #t) | |
584 | ((orig subst #f) (hashq-set! table sym (list orig subst #t))) | |
585 | ((? number? idx) (hashq-set! table sym (list idx (fresh-var) #t))))) | |
586 | (($ <lambda-case> src req opt rest kw inits gensyms body alternate) | |
587 | (fold-formals (lambda (name sym init seed) | |
588 | (hashq-set! table sym | |
589 | (if init | |
590 | (list (fresh-var) (fresh-var) #f) | |
591 | (fresh-var)))) | |
592 | #f | |
593 | (make-$arity req (or opt '()) rest | |
594 | (if kw (cdr kw) '()) (and kw (car kw))) | |
595 | gensyms | |
596 | inits)) | |
597 | (($ <let> src names gensyms vals body) | |
598 | (for-each (lambda (sym) | |
599 | (hashq-set! table sym (fresh-var))) | |
600 | gensyms)) | |
601 | (($ <fix> src names gensyms vals body) | |
602 | (for-each (lambda (sym) | |
603 | (hashq-set! table sym (fresh-var))) | |
604 | gensyms)) | |
605 | (_ #t)) | |
606 | (values)) | |
607 | (define (up exp) (values)) | |
608 | ((make-tree-il-folder) exp down up) | |
609 | table)) | |
4fefc3a8 AW |
610 | |
611 | (define (cps-convert/thunk exp) | |
9a1dfb7d | 612 | (parameterize ((label-counter 0) |
48e65b44 AW |
613 | (var-counter 0) |
614 | (scope-counter 0)) | |
9a1dfb7d AW |
615 | (let ((src (tree-il-src exp))) |
616 | (let-fresh (kinit ktail kclause kbody) (init) | |
a0329d01 AW |
617 | (build-cps-cont |
618 | (kinit ($kfun src '() init (ktail ($ktail)) | |
619 | (kclause | |
620 | ($kclause ('() '() #f '() #f) | |
621 | (kbody ($kargs () () | |
622 | ,(convert exp ktail | |
623 | (build-subst exp)))) | |
624 | ,#f))))))))) | |
4fefc3a8 AW |
625 | |
626 | (define *comp-module* (make-fluid)) | |
627 | ||
628 | (define %warning-passes | |
629 | `((unused-variable . ,unused-variable-analysis) | |
630 | (unused-toplevel . ,unused-toplevel-analysis) | |
631 | (unbound-variable . ,unbound-variable-analysis) | |
632 | (arity-mismatch . ,arity-analysis) | |
633 | (format . ,format-analysis))) | |
634 | ||
635 | (define (optimize-tree-il x e opts) | |
636 | (define warnings | |
637 | (or (and=> (memq #:warnings opts) cadr) | |
638 | '())) | |
639 | ||
640 | ;; Go through the warning passes. | |
641 | (let ((analyses (filter-map (lambda (kind) | |
642 | (assoc-ref %warning-passes kind)) | |
643 | warnings))) | |
644 | (analyze-tree analyses x e)) | |
645 | ||
646 | (optimize x e opts)) | |
647 | ||
e6cf744a | 648 | (define (canonicalize exp) |
ef58442a AW |
649 | (post-order |
650 | (lambda (exp) | |
651 | (match exp | |
e6cf744a AW |
652 | (($ <primcall> src 'vector |
653 | (and args | |
654 | ((or ($ <const>) ($ <void>) ($ <lambda>) ($ <lexical-ref>)) | |
655 | ...))) | |
656 | ;; Some macros generate calls to "vector" with like 300 | |
657 | ;; arguments. Since we eventually compile to make-vector and | |
658 | ;; vector-set!, it reduces live variable pressure to allocate the | |
659 | ;; vector first, then set values as they are produced, if we can | |
660 | ;; prove that no value can capture the continuation. (More on | |
661 | ;; that caveat here: | |
662 | ;; http://wingolog.org/archives/2013/11/02/scheme-quiz-time). | |
663 | ;; | |
664 | ;; Normally we would do this transformation in the compiler, but | |
665 | ;; it's quite tricky there and quite easy here, so hold your nose | |
666 | ;; while we drop some smelly code. | |
667 | (let ((len (length args)) | |
668 | (v (gensym "v "))) | |
669 | (make-let src | |
670 | (list 'v) | |
671 | (list v) | |
672 | (list (make-primcall src 'make-vector | |
673 | (list (make-const #f len) | |
674 | (make-const #f #f)))) | |
675 | (fold (lambda (arg n tail) | |
676 | (make-seq | |
677 | src | |
678 | (make-primcall | |
679 | src 'vector-set! | |
680 | (list (make-lexical-ref src 'v v) | |
681 | (make-const #f n) | |
682 | arg)) | |
683 | tail)) | |
684 | (make-lexical-ref src 'v v) | |
685 | (reverse args) (reverse (iota len)))))) | |
686 | ||
e2fafeb9 AW |
687 | (($ <primcall> src 'struct-set! (struct index value)) |
688 | ;; Unhappily, and undocumentedly, struct-set! returns the value | |
689 | ;; that was set. There is code that relies on this. Hackety | |
690 | ;; hack... | |
691 | (let ((v (gensym "v "))) | |
692 | (make-let src | |
693 | (list 'v) | |
694 | (list v) | |
695 | (list value) | |
696 | (make-seq src | |
697 | (make-primcall src 'struct-set! | |
698 | (list struct | |
699 | index | |
700 | (make-lexical-ref src 'v v))) | |
701 | (make-lexical-ref src 'v v))))) | |
702 | ||
ef58442a AW |
703 | (($ <prompt> src escape-only? tag body |
704 | ($ <lambda> hsrc hmeta | |
705 | ($ <lambda-case> _ hreq #f hrest #f () hsyms hbody #f))) | |
706 | exp) | |
707 | ||
708 | ;; Eta-convert prompts without inline handlers. | |
709 | (($ <prompt> src escape-only? tag body handler) | |
710 | (let ((h (gensym "h ")) | |
711 | (args (gensym "args "))) | |
712 | (make-let | |
713 | src (list 'h) (list h) (list handler) | |
714 | (make-seq | |
715 | src | |
716 | (make-conditional | |
717 | src | |
718 | (make-primcall src 'procedure? (list (make-lexical-ref #f 'h h))) | |
719 | (make-void src) | |
720 | (make-primcall | |
721 | src 'scm-error | |
722 | (list | |
723 | (make-const #f 'wrong-type-arg) | |
724 | (make-const #f "call-with-prompt") | |
725 | (make-const #f "Wrong type (expecting procedure): ~S") | |
726 | (make-primcall #f 'list (list (make-lexical-ref #f 'h h))) | |
727 | (make-primcall #f 'list (list (make-lexical-ref #f 'h h)))))) | |
728 | (make-prompt | |
729 | src escape-only? tag body | |
730 | (make-lambda | |
731 | src '() | |
732 | (make-lambda-case | |
733 | src '() #f 'args #f '() (list args) | |
734 | (make-primcall | |
735 | src 'apply | |
736 | (list (make-lexical-ref #f 'h h) | |
737 | (make-lexical-ref #f 'args args))) | |
738 | #f))))))) | |
739 | (_ exp))) | |
740 | exp)) | |
741 | ||
4fefc3a8 | 742 | (define (compile-cps exp env opts) |
ef58442a | 743 | (values (cps-convert/thunk |
e6cf744a | 744 | (canonicalize (optimize-tree-il exp env opts))) |
4fefc3a8 AW |
745 | env |
746 | env)) | |
747 | ||
748 | ;;; Local Variables: | |
749 | ;;; eval: (put 'convert-arg 'scheme-indent-function 1) | |
750 | ;;; eval: (put 'convert-args 'scheme-indent-function 1) | |
751 | ;;; End: |