Remove $void CPS expression type
[bpt/guile.git] / module / language / tree-il / compile-cps.scm
CommitLineData
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)
9a1dfb7d 527 (let-fresh () (self)
4fefc3a8
AW
528 (build-cps-term
529 ($letrec names
e6cf744a 530 (map bound-var gensyms)
4fefc3a8
AW
531 (map (lambda (fun)
532 (match (convert fun k subst)
6e422a35 533 (($ $continue _ _ (and fun ($ $fun)))
4fefc3a8
AW
534 fun)))
535 funs)
536 ,(convert body k subst))))
48e65b44
AW
537 (let ((scope-id (fresh-scope-id)))
538 (let-fresh (kscope) ()
539 (build-cps-term
540 ($letk ((kscope
541 ($kargs () ()
542 ,(parameterize ((current-topbox-scope scope-id))
543 (convert exp k subst)))))
544 ,(capture-toplevel-scope src scope-id kscope)))))))
4fefc3a8
AW
545
546 (($ <let-values> src exp
547 ($ <lambda-case> lsrc req #f rest #f () syms body #f))
e6cf744a
AW
548 (let ((names (append req (if rest (list rest) '())))
549 (bound-vars (map bound-var syms)))
9a1dfb7d 550 (let-fresh (kreceive kargs) ()
4fefc3a8 551 (build-cps-term
e6cf744a 552 ($letk* ((kargs ($kargs names bound-vars
6e422a35
AW
553 ,(fold box-bound-var
554 (convert body k subst)
555 names syms)))
36527695
AW
556 (kreceive ($kreceive req rest kargs)))
557 ,(convert exp kreceive subst))))))))
4fefc3a8
AW
558
559(define (build-subst exp)
e6cf744a
AW
560 "Compute a mapping from lexical gensyms to CPS variable indexes. CPS
561uses small integers to identify variables, instead of gensyms.
562
563This subst table serves an additional purpose of mapping variables to
564replacements. The usual reason to replace one variable by another is
565assignment conversion. Default argument values is the other reason.
566
567The result is a hash table mapping symbols to substitutions (in the case
568that a variable is substituted) or to indexes. A substitution is a list
569of the form:
570
571 (ORIG-INDEX SUBST-INDEX BOXED?)
572
573A true value for BOXED? indicates that the replacement variable is in a
574box. If a variable is not substituted, the mapped value is a small
575integer."
576 (let ((table (make-hash-table)))
577 (define (down exp)
578 (match exp
579 (($ <lexical-set> src name sym exp)
580 (match (hashq-ref table sym)
581 ((orig subst #t) #t)
582 ((orig subst #f) (hashq-set! table sym (list orig subst #t)))
583 ((? number? idx) (hashq-set! table sym (list idx (fresh-var) #t)))))
584 (($ <lambda-case> src req opt rest kw inits gensyms body alternate)
585 (fold-formals (lambda (name sym init seed)
586 (hashq-set! table sym
587 (if init
588 (list (fresh-var) (fresh-var) #f)
589 (fresh-var))))
590 #f
591 (make-$arity req (or opt '()) rest
592 (if kw (cdr kw) '()) (and kw (car kw)))
593 gensyms
594 inits))
595 (($ <let> src names gensyms vals body)
596 (for-each (lambda (sym)
597 (hashq-set! table sym (fresh-var)))
598 gensyms))
599 (($ <fix> src names gensyms vals body)
600 (for-each (lambda (sym)
601 (hashq-set! table sym (fresh-var)))
602 gensyms))
603 (_ #t))
604 (values))
605 (define (up exp) (values))
606 ((make-tree-il-folder) exp down up)
607 table))
4fefc3a8
AW
608
609(define (cps-convert/thunk exp)
9a1dfb7d 610 (parameterize ((label-counter 0)
48e65b44
AW
611 (var-counter 0)
612 (scope-counter 0))
9a1dfb7d
AW
613 (let ((src (tree-il-src exp)))
614 (let-fresh (kinit ktail kclause kbody) (init)
a0329d01
AW
615 (build-cps-cont
616 (kinit ($kfun src '() init (ktail ($ktail))
617 (kclause
618 ($kclause ('() '() #f '() #f)
619 (kbody ($kargs () ()
620 ,(convert exp ktail
621 (build-subst exp))))
622 ,#f)))))))))
4fefc3a8
AW
623
624(define *comp-module* (make-fluid))
625
626(define %warning-passes
627 `((unused-variable . ,unused-variable-analysis)
628 (unused-toplevel . ,unused-toplevel-analysis)
629 (unbound-variable . ,unbound-variable-analysis)
630 (arity-mismatch . ,arity-analysis)
631 (format . ,format-analysis)))
632
633(define (optimize-tree-il x e opts)
634 (define warnings
635 (or (and=> (memq #:warnings opts) cadr)
636 '()))
637
638 ;; Go through the warning passes.
639 (let ((analyses (filter-map (lambda (kind)
640 (assoc-ref %warning-passes kind))
641 warnings)))
642 (analyze-tree analyses x e))
643
644 (optimize x e opts))
645
e6cf744a 646(define (canonicalize exp)
ef58442a
AW
647 (post-order
648 (lambda (exp)
649 (match exp
e6cf744a
AW
650 (($ <primcall> src 'vector
651 (and args
652 ((or ($ <const>) ($ <void>) ($ <lambda>) ($ <lexical-ref>))
653 ...)))
654 ;; Some macros generate calls to "vector" with like 300
655 ;; arguments. Since we eventually compile to make-vector and
656 ;; vector-set!, it reduces live variable pressure to allocate the
657 ;; vector first, then set values as they are produced, if we can
658 ;; prove that no value can capture the continuation. (More on
659 ;; that caveat here:
660 ;; http://wingolog.org/archives/2013/11/02/scheme-quiz-time).
661 ;;
662 ;; Normally we would do this transformation in the compiler, but
663 ;; it's quite tricky there and quite easy here, so hold your nose
664 ;; while we drop some smelly code.
665 (let ((len (length args))
666 (v (gensym "v ")))
667 (make-let src
668 (list 'v)
669 (list v)
670 (list (make-primcall src 'make-vector
671 (list (make-const #f len)
672 (make-const #f #f))))
673 (fold (lambda (arg n tail)
674 (make-seq
675 src
676 (make-primcall
677 src 'vector-set!
678 (list (make-lexical-ref src 'v v)
679 (make-const #f n)
680 arg))
681 tail))
682 (make-lexical-ref src 'v v)
683 (reverse args) (reverse (iota len))))))
684
e2fafeb9
AW
685 (($ <primcall> src 'struct-set! (struct index value))
686 ;; Unhappily, and undocumentedly, struct-set! returns the value
687 ;; that was set. There is code that relies on this. Hackety
688 ;; hack...
689 (let ((v (gensym "v ")))
690 (make-let src
691 (list 'v)
692 (list v)
693 (list value)
694 (make-seq src
695 (make-primcall src 'struct-set!
696 (list struct
697 index
698 (make-lexical-ref src 'v v)))
699 (make-lexical-ref src 'v v)))))
700
ef58442a
AW
701 (($ <prompt> src escape-only? tag body
702 ($ <lambda> hsrc hmeta
703 ($ <lambda-case> _ hreq #f hrest #f () hsyms hbody #f)))
704 exp)
705
706 ;; Eta-convert prompts without inline handlers.
707 (($ <prompt> src escape-only? tag body handler)
708 (let ((h (gensym "h "))
709 (args (gensym "args ")))
710 (make-let
711 src (list 'h) (list h) (list handler)
712 (make-seq
713 src
714 (make-conditional
715 src
716 (make-primcall src 'procedure? (list (make-lexical-ref #f 'h h)))
717 (make-void src)
718 (make-primcall
719 src 'scm-error
720 (list
721 (make-const #f 'wrong-type-arg)
722 (make-const #f "call-with-prompt")
723 (make-const #f "Wrong type (expecting procedure): ~S")
724 (make-primcall #f 'list (list (make-lexical-ref #f 'h h)))
725 (make-primcall #f 'list (list (make-lexical-ref #f 'h h))))))
726 (make-prompt
727 src escape-only? tag body
728 (make-lambda
729 src '()
730 (make-lambda-case
731 src '() #f 'args #f '() (list args)
732 (make-primcall
733 src 'apply
734 (list (make-lexical-ref #f 'h h)
735 (make-lexical-ref #f 'args args)))
736 #f)))))))
737 (_ exp)))
738 exp))
739
4fefc3a8 740(define (compile-cps exp env opts)
ef58442a 741 (values (cps-convert/thunk
e6cf744a 742 (canonicalize (optimize-tree-il exp env opts)))
4fefc3a8
AW
743 env
744 env))
745
746;;; Local Variables:
747;;; eval: (put 'convert-arg 'scheme-indent-function 1)
748;;; eval: (put 'convert-args 'scheme-indent-function 1)
749;;; End: