merge stable-2.0
[bpt/guile.git] / module / language / tree-il / compile-glil.scm
1 ;;; TREE-IL -> GLIL compiler
2
3 ;; Copyright (C) 2001,2008,2009,2010,2011,2012 Free Software Foundation, Inc.
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 ;;; Code:
20
21 (define-module (language tree-il compile-glil)
22 #:use-module (system base syntax)
23 #:use-module (system base pmatch)
24 #:use-module (system base message)
25 #:use-module (ice-9 receive)
26 #:use-module (language glil)
27 #:use-module (system vm instruction)
28 #:use-module (language tree-il)
29 #:use-module (language tree-il optimize)
30 #:use-module (language tree-il canonicalize)
31 #:use-module (language tree-il analyze)
32 #:use-module ((srfi srfi-1) #:select (filter-map))
33 #:export (compile-glil))
34
35 ;; allocation:
36 ;; sym -> {lambda -> address}
37 ;; lambda -> (labels . free-locs)
38 ;; lambda-case -> (gensym . nlocs)
39 ;;
40 ;; address ::= (local? boxed? . index)
41 ;; labels ::= ((sym . lambda) ...)
42 ;; free-locs ::= ((sym0 . address0) (sym1 . address1) ...)
43 ;; free variable addresses are relative to parent proc.
44
45 (define *comp-module* (make-fluid))
46
47 (define %warning-passes
48 `((unused-variable . ,unused-variable-analysis)
49 (unused-toplevel . ,unused-toplevel-analysis)
50 (unbound-variable . ,unbound-variable-analysis)
51 (arity-mismatch . ,arity-analysis)
52 (format . ,format-analysis)))
53
54 (define (compile-glil x e opts)
55 (define warnings
56 (or (and=> (memq #:warnings opts) cadr)
57 '()))
58
59 ;; Go through the warning passes.
60 (let ((analyses (filter-map (lambda (kind)
61 (assoc-ref %warning-passes kind))
62 warnings)))
63 (analyze-tree analyses x e))
64
65 (let* ((x (make-lambda (tree-il-src x) '()
66 (make-lambda-case #f '() #f #f #f '() '() x #f)))
67 (x (optimize! x e opts))
68 (x (canonicalize! x))
69 (allocation (analyze-lexicals x)))
70
71 (with-fluids ((*comp-module* e))
72 (values (flatten-lambda x #f allocation)
73 e
74 e))))
75
76 \f
77
78 (define *primcall-ops* (make-hash-table))
79 (for-each
80 (lambda (x) (hash-set! *primcall-ops* (car x) (cdr x)))
81 '(((eq? . 2) . eq?)
82 ((eqv? . 2) . eqv?)
83 ((equal? . 2) . equal?)
84 ((= . 2) . ee?)
85 ((< . 2) . lt?)
86 ((> . 2) . gt?)
87 ((<= . 2) . le?)
88 ((>= . 2) . ge?)
89 ((+ . 2) . add)
90 ((- . 2) . sub)
91 ((1+ . 1) . add1)
92 ((1- . 1) . sub1)
93 ((* . 2) . mul)
94 ((/ . 2) . div)
95 ((quotient . 2) . quo)
96 ((remainder . 2) . rem)
97 ((modulo . 2) . mod)
98 ((ash . 2) . ash)
99 ((logand . 2) . logand)
100 ((logior . 2) . logior)
101 ((logxor . 2) . logxor)
102 ((not . 1) . not)
103 ((pair? . 1) . pair?)
104 ((cons . 2) . cons)
105 ((car . 1) . car)
106 ((cdr . 1) . cdr)
107 ((set-car! . 2) . set-car!)
108 ((set-cdr! . 2) . set-cdr!)
109 ((null? . 1) . null?)
110 ((list? . 1) . list?)
111 ((symbol? . 1) . symbol?)
112 ((vector? . 1) . vector?)
113 ((nil? . 1) . nil?)
114 (list . list)
115 (vector . vector)
116 ((class-of . 1) . class-of)
117 ((@slot-ref . 2) . slot-ref)
118 ((@slot-set! . 3) . slot-set)
119 ((string-length . 1) . string-length)
120 ((string-ref . 2) . string-ref)
121 ((vector-length . 1) . vector-length)
122 ((vector-ref . 2) . vector-ref)
123 ((vector-set! . 3) . vector-set)
124 ((variable-ref . 1) . variable-ref)
125 ;; nb, *not* variable-set! -- the args are switched
126 ((variable-bound? . 1) . variable-bound?)
127 ((struct? . 1) . struct?)
128 ((struct-vtable . 1) . struct-vtable)
129 ((struct-ref . 2) . struct-ref)
130 ((struct-set! . 3) . struct-set)
131 (make-struct/no-tail . make-struct)
132
133 ;; hack for javascript
134 ((return . 1) . return)
135 ;; hack for lua
136 (return/values . return/values)
137
138 ((bytevector-u8-ref . 2) . bv-u8-ref)
139 ((bytevector-u8-set! . 3) . bv-u8-set)
140 ((bytevector-s8-ref . 2) . bv-s8-ref)
141 ((bytevector-s8-set! . 3) . bv-s8-set)
142
143 ((bytevector-u16-ref . 3) . bv-u16-ref)
144 ((bytevector-u16-set! . 4) . bv-u16-set)
145 ((bytevector-u16-native-ref . 2) . bv-u16-native-ref)
146 ((bytevector-u16-native-set! . 3) . bv-u16-native-set)
147 ((bytevector-s16-ref . 3) . bv-s16-ref)
148 ((bytevector-s16-set! . 4) . bv-s16-set)
149 ((bytevector-s16-native-ref . 2) . bv-s16-native-ref)
150 ((bytevector-s16-native-set! . 3) . bv-s16-native-set)
151
152 ((bytevector-u32-ref . 3) . bv-u32-ref)
153 ((bytevector-u32-set! . 4) . bv-u32-set)
154 ((bytevector-u32-native-ref . 2) . bv-u32-native-ref)
155 ((bytevector-u32-native-set! . 3) . bv-u32-native-set)
156 ((bytevector-s32-ref . 3) . bv-s32-ref)
157 ((bytevector-s32-set! . 4) . bv-s32-set)
158 ((bytevector-s32-native-ref . 2) . bv-s32-native-ref)
159 ((bytevector-s32-native-set! . 3) . bv-s32-native-set)
160
161 ((bytevector-u64-ref . 3) . bv-u64-ref)
162 ((bytevector-u64-set! . 4) . bv-u64-set)
163 ((bytevector-u64-native-ref . 2) . bv-u64-native-ref)
164 ((bytevector-u64-native-set! . 3) . bv-u64-native-set)
165 ((bytevector-s64-ref . 3) . bv-s64-ref)
166 ((bytevector-s64-set! . 4) . bv-s64-set)
167 ((bytevector-s64-native-ref . 2) . bv-s64-native-ref)
168 ((bytevector-s64-native-set! . 3) . bv-s64-native-set)
169
170 ((bytevector-ieee-single-ref . 3) . bv-f32-ref)
171 ((bytevector-ieee-single-set! . 4) . bv-f32-set)
172 ((bytevector-ieee-single-native-ref . 2) . bv-f32-native-ref)
173 ((bytevector-ieee-single-native-set! . 3) . bv-f32-native-set)
174 ((bytevector-ieee-double-ref . 3) . bv-f64-ref)
175 ((bytevector-ieee-double-set! . 4) . bv-f64-set)
176 ((bytevector-ieee-double-native-ref . 2) . bv-f64-native-ref)
177 ((bytevector-ieee-double-native-set! . 3) . bv-f64-native-set)))
178
179
180 \f
181
182 (define (make-label) (gensym ":L"))
183
184 (define (vars->bind-list ids vars allocation proc)
185 (map (lambda (id v)
186 (pmatch (hashq-ref (hashq-ref allocation v) proc)
187 ((#t ,boxed? . ,n)
188 (list id boxed? n))
189 (,x (error "bad var list element" id v x))))
190 ids
191 vars))
192
193 (define (emit-bindings src ids vars allocation proc emit-code)
194 (emit-code src (make-glil-bind
195 (vars->bind-list ids vars allocation proc))))
196
197 (define (with-output-to-code proc)
198 (let ((out '()))
199 (define (emit-code src x)
200 (set! out (cons x out))
201 (if src
202 (set! out (cons (make-glil-source src) out))))
203 (proc emit-code)
204 (reverse out)))
205
206 (define (flatten-lambda x self-label allocation)
207 (record-case x
208 ((<lambda> src meta body)
209 (make-glil-program
210 meta
211 (with-output-to-code
212 (lambda (emit-code)
213 ;; write source info for proc
214 (if src (emit-code #f (make-glil-source src)))
215 ;; compile the body, yo
216 (flatten-lambda-case body allocation x self-label
217 (car (hashq-ref allocation x))
218 emit-code)))))))
219
220 (define (flatten-lambda-case lcase allocation self self-label fix-labels
221 emit-code)
222 (define (emit-label label)
223 (emit-code #f (make-glil-label label)))
224 (define (emit-branch src inst label)
225 (emit-code src (make-glil-branch inst label)))
226
227 ;; RA: "return address"; #f unless we're in a non-tail fix with labels
228 ;; MVRA: "multiple-values return address"; #f unless we're in a let-values
229 (let comp ((x lcase) (context 'tail) (RA #f) (MVRA #f))
230 (define (comp-tail tree) (comp tree context RA MVRA))
231 (define (comp-push tree) (comp tree 'push #f #f))
232 (define (comp-drop tree) (comp tree 'drop #f #f))
233 (define (comp-vals tree MVRA) (comp tree 'vals #f MVRA))
234 (define (comp-fix tree RA) (comp tree context RA MVRA))
235
236 ;; A couple of helpers. Note that if we are in tail context, we
237 ;; won't have an RA.
238 (define (maybe-emit-return)
239 (if RA
240 (emit-branch #f 'br RA)
241 (if (eq? context 'tail)
242 (emit-code #f (make-glil-call 'return 1)))))
243
244 ;; After lexical binding forms in non-tail context, call this
245 ;; function to clear stack slots, allowing their previous values to
246 ;; be collected.
247 (define (clear-stack-slots context syms)
248 (case context
249 ((push drop)
250 (for-each (lambda (v)
251 (and=>
252 ;; Can be #f if the var is labels-allocated.
253 (hashq-ref allocation v)
254 (lambda (h)
255 (pmatch (hashq-ref h self)
256 ((#t _ . ,n)
257 (emit-code #f (make-glil-void))
258 (emit-code #f (make-glil-lexical #t #f 'set n)))
259 (,loc (error "bad let var allocation" x loc))))))
260 syms))))
261
262 (record-case x
263 ((<void>)
264 (case context
265 ((push vals tail)
266 (emit-code #f (make-glil-void))))
267 (maybe-emit-return))
268
269 ((<const> src exp)
270 (case context
271 ((push vals tail)
272 (emit-code src (make-glil-const exp))))
273 (maybe-emit-return))
274
275 ((<seq> head tail)
276 (comp-drop head)
277 (comp-tail tail))
278
279 ((<call> src proc args)
280 (cond
281 ;; call to the same lambda-case in tail position
282 ((and (lexical-ref? proc)
283 self-label (eq? (lexical-ref-gensym proc) self-label)
284 (eq? context 'tail)
285 (not (lambda-case-kw lcase))
286 (not (lambda-case-rest lcase))
287 (= (length args)
288 (+ (length (lambda-case-req lcase))
289 (or (and=> (lambda-case-opt lcase) length) 0))))
290 (for-each comp-push args)
291 (for-each (lambda (sym)
292 (pmatch (hashq-ref (hashq-ref allocation sym) self)
293 ((#t #f . ,index) ; unboxed
294 (emit-code #f (make-glil-lexical #t #f 'set index)))
295 ((#t #t . ,index) ; boxed
296 ;; new box
297 (emit-code #f (make-glil-lexical #t #t 'box index)))
298 (,x (error "bad lambda-case arg allocation" x))))
299 (reverse (lambda-case-gensyms lcase)))
300 (emit-branch src 'br (car (hashq-ref allocation lcase))))
301
302 ;; lambda, the ultimate goto
303 ((and (lexical-ref? proc)
304 (assq (lexical-ref-gensym proc) fix-labels))
305 ;; like the self-tail-call case, though we can handle "drop"
306 ;; contexts too. first, evaluate new values, pushing them on
307 ;; the stack
308 (for-each comp-push args)
309 ;; find the specific case, rename args, and goto the case label
310 (let lp ((lcase (lambda-body
311 (assq-ref fix-labels (lexical-ref-gensym proc)))))
312 (cond
313 ((and (lambda-case? lcase)
314 (not (lambda-case-kw lcase))
315 (not (lambda-case-opt lcase))
316 (not (lambda-case-rest lcase))
317 (= (length args) (length (lambda-case-req lcase))))
318 ;; we have a case that matches the args; rename variables
319 ;; and goto the case label
320 (for-each (lambda (sym)
321 (pmatch (hashq-ref (hashq-ref allocation sym) self)
322 ((#t #f . ,index) ; unboxed
323 (emit-code #f (make-glil-lexical #t #f 'set index)))
324 ((#t #t . ,index) ; boxed
325 (emit-code #f (make-glil-lexical #t #t 'box index)))
326 (,x (error "bad lambda-case arg allocation" x))))
327 (reverse (lambda-case-gensyms lcase)))
328 (emit-branch src 'br (car (hashq-ref allocation lcase))))
329 ((lambda-case? lcase)
330 ;; no match, try next case
331 (lp (lambda-case-alternate lcase)))
332 (else
333 ;; no cases left. we can't really handle this currently.
334 ;; ideally we would push on a new frame, then do a "local
335 ;; call" -- which doesn't require consing up a program
336 ;; object. but for now error, as this sort of case should
337 ;; preclude label allocation.
338 (error "couldn't find matching case for label call" x)))))
339
340 (else
341 (if (not (eq? context 'tail))
342 (emit-code src (make-glil-call 'new-frame 0)))
343 (comp-push proc)
344 (for-each comp-push args)
345 (let ((len (length args)))
346 (case context
347 ((tail) (emit-code src (make-glil-call 'tail-call len)))
348 ((push) (emit-code src (make-glil-call 'call len))
349 (maybe-emit-return))
350 ((vals) (emit-code src (make-glil-mv-call len MVRA))
351 (maybe-emit-return))
352 ((drop) (let ((MV (make-label)) (POST (make-label)))
353 (emit-code src (make-glil-mv-call len MV))
354 (emit-code #f (make-glil-call 'drop 1))
355 (emit-branch #f 'br (or RA POST))
356 (emit-label MV)
357 (emit-code #f (make-glil-mv-bind 0 #f))
358 (if RA
359 (emit-branch #f 'br RA)
360 (emit-label POST)))))))))
361
362 ((<primcall> src name args)
363 (pmatch (cons name args)
364 ((@apply ,proc . ,args)
365 (cond
366 ((and (primitive-ref? proc) (eq? (primitive-ref-name proc) 'values)
367 (not (eq? context 'push)) (not (eq? context 'vals)))
368 ;; tail: (lambda () (apply values '(1 2)))
369 ;; drop: (lambda () (apply values '(1 2)) 3)
370 ;; push: (lambda () (list (apply values '(10 12)) 1))
371 (case context
372 ((drop) (for-each comp-drop args) (maybe-emit-return))
373 ((tail)
374 (for-each comp-push args)
375 (emit-code src (make-glil-call 'return/values* (length args))))))
376
377 (else
378 (case context
379 ((tail)
380 (comp-push proc)
381 (for-each comp-push args)
382 (emit-code src (make-glil-call 'tail-apply (1+ (length args)))))
383 ((push)
384 (emit-code src (make-glil-call 'new-frame 0))
385 (comp-push proc)
386 (for-each comp-push args)
387 (emit-code src (make-glil-call 'apply (1+ (length args))))
388 (maybe-emit-return))
389 (else
390 (comp-tail (make-primcall src 'apply (cons proc args))))))))
391
392 ((values . _)
393 ;; tail: (lambda () (values '(1 2)))
394 ;; drop: (lambda () (values '(1 2)) 3)
395 ;; push: (lambda () (list (values '(10 12)) 1))
396 ;; vals: (let-values (((a b ...) (values 1 2 ...))) ...)
397 (case context
398 ((drop) (for-each comp-drop args) (maybe-emit-return))
399 ((push)
400 (case (length args)
401 ((0)
402 ;; FIXME: This is surely an error. We need to add a
403 ;; values-mismatch warning pass.
404 (comp-push (make-call src (make-primitive-ref #f 'values)
405 '())))
406 (else
407 ;; Taking advantage of unspecified order of evaluation of
408 ;; arguments.
409 (for-each comp-drop (cdr args))
410 (comp-push (car args))
411 (maybe-emit-return))))
412 ((vals)
413 (for-each comp-push args)
414 (emit-code #f (make-glil-const (length args)))
415 (emit-branch src 'br MVRA))
416 ((tail)
417 (for-each comp-push args)
418 (emit-code src (let ((len (length args)))
419 (if (= len 1)
420 (make-glil-call 'return 1)
421 (make-glil-call 'return/values len)))))))
422
423 ((@call-with-values ,producer ,consumer)
424 ;; CONSUMER
425 ;; PRODUCER
426 ;; (mv-call MV)
427 ;; ([tail]-call 1)
428 ;; goto POST
429 ;; MV: [tail-]call/nargs
430 ;; POST: (maybe-drop)
431 (case context
432 ((vals)
433 ;; Fall back.
434 (comp-tail (make-primcall src 'call-with-values args)))
435 (else
436 (let ((MV (make-label)) (POST (make-label)))
437 (if (not (eq? context 'tail))
438 (emit-code src (make-glil-call 'new-frame 0)))
439 (comp-push consumer)
440 (emit-code src (make-glil-call 'new-frame 0))
441 (comp-push producer)
442 (emit-code src (make-glil-mv-call 0 MV))
443 (case context
444 ((tail) (emit-code src (make-glil-call 'tail-call 1)))
445 (else (emit-code src (make-glil-call 'call 1))
446 (emit-branch #f 'br POST)))
447 (emit-label MV)
448 (case context
449 ((tail) (emit-code src (make-glil-call 'tail-call/nargs 0)))
450 (else (emit-code src (make-glil-call 'call/nargs 0))
451 (emit-label POST)
452 (if (eq? context 'drop)
453 (emit-code #f (make-glil-call 'drop 1)))
454 (maybe-emit-return)))))))
455
456 ((@call-with-current-continuation ,proc)
457 (case context
458 ((tail)
459 (comp-push proc)
460 (emit-code src (make-glil-call 'tail-call/cc 1)))
461 ((vals)
462 (comp-vals
463 (make-primcall src 'call-with-current-continuation args)
464 MVRA)
465 (maybe-emit-return))
466 ((push)
467 (comp-push proc)
468 (emit-code src (make-glil-call 'call/cc 1))
469 (maybe-emit-return))
470 ((drop)
471 ;; Fall back.
472 (comp-tail
473 (make-primcall src 'call-with-current-continuation args)))))
474
475 ;; A hack for variable-set, the opcode for which takes its args
476 ;; reversed, relative to the variable-set! function
477 ((variable-set! ,var ,val)
478 (comp-push val)
479 (comp-push var)
480 (emit-code src (make-glil-call 'variable-set 2))
481 (case context
482 ((tail push vals) (emit-code #f (make-glil-void))))
483 (maybe-emit-return))
484
485 (else
486 (cond
487 ((or (hash-ref *primcall-ops* (cons name (length args)))
488 (hash-ref *primcall-ops* name))
489 => (lambda (op)
490 (for-each comp-push args)
491 (emit-code src (make-glil-call op (length args)))
492 (case (instruction-pushes op)
493 ((0)
494 (case context
495 ((tail push vals) (emit-code #f (make-glil-void))))
496 (maybe-emit-return))
497 ((1)
498 (case context
499 ((drop) (emit-code #f (make-glil-call 'drop 1))))
500 (maybe-emit-return))
501 ((-1)
502 ;; A control instruction, like return/values. Here we
503 ;; just have to hope that the author of the tree-il
504 ;; knew what they were doing.
505 *unspecified*)
506 (else
507 (error "bad primitive op: too many pushes"
508 op (instruction-pushes op))))))
509 (else
510 ;; Fall back to the normal compilation strategy.
511 (comp-tail (make-call src (make-primitive-ref #f name) args)))))))
512
513 ((<conditional> src test consequent alternate)
514 ;; TEST
515 ;; (br-if-not L1)
516 ;; consequent
517 ;; (br L2)
518 ;; L1: alternate
519 ;; L2:
520 (let ((L1 (make-label)) (L2 (make-label)))
521 (record-case test
522 ((<primcall> name args)
523 (pmatch (cons name args)
524 ((eq? ,a ,b)
525 (comp-push a)
526 (comp-push b)
527 (emit-branch src 'br-if-not-eq L1))
528 ((null? ,x)
529 (comp-push x)
530 (emit-branch src 'br-if-not-null L1))
531 ((nil? ,x)
532 (comp-push x)
533 (emit-branch src 'br-if-not-nil L1))
534 ((not ,x)
535 (record-case x
536 ((<primcall> name args)
537 (pmatch (cons name args)
538 ((eq? ,a ,b)
539 (comp-push a)
540 (comp-push b)
541 (emit-branch src 'br-if-eq L1))
542 ((null? ,x)
543 (comp-push x)
544 (emit-branch src 'br-if-null L1))
545 ((nil? ,x)
546 (comp-push x)
547 (emit-branch src 'br-if-nil L1))
548 (else
549 (comp-push x)
550 (emit-branch src 'br-if L1))))
551 (else
552 (comp-push x)
553 (emit-branch src 'br-if L1))))
554 (else
555 (comp-push test)
556 (emit-branch src 'br-if-not L1))))
557 (else
558 (comp-push test)
559 (emit-branch src 'br-if-not L1)))
560
561 (comp-tail consequent)
562 ;; if there is an RA, comp-tail will cause a jump to it -- just
563 ;; have to clean up here if there is no RA.
564 (if (and (not RA) (not (eq? context 'tail)))
565 (emit-branch #f 'br L2))
566 (emit-label L1)
567 (comp-tail alternate)
568 (if (and (not RA) (not (eq? context 'tail)))
569 (emit-label L2))))
570
571 ((<primitive-ref> src name)
572 (cond
573 ((eq? (module-variable (fluid-ref *comp-module*) name)
574 (module-variable the-root-module name))
575 (case context
576 ((tail push vals)
577 (emit-code src (make-glil-toplevel 'ref name))))
578 (maybe-emit-return))
579 ((module-variable the-root-module name)
580 (case context
581 ((tail push vals)
582 (emit-code src (make-glil-module 'ref '(guile) name #f))))
583 (maybe-emit-return))
584 (else
585 (case context
586 ((tail push vals)
587 (emit-code src (make-glil-module
588 'ref (module-name (fluid-ref *comp-module*)) name #f))))
589 (maybe-emit-return))))
590
591 ((<lexical-ref> src gensym)
592 (case context
593 ((push vals tail)
594 (pmatch (hashq-ref (hashq-ref allocation gensym) self)
595 ((,local? ,boxed? . ,index)
596 (emit-code src (make-glil-lexical local? boxed? 'ref index)))
597 (,loc
598 (error "bad lexical allocation" x loc)))))
599 (maybe-emit-return))
600
601 ((<lexical-set> src gensym exp)
602 (comp-push exp)
603 (pmatch (hashq-ref (hashq-ref allocation gensym) self)
604 ((,local? ,boxed? . ,index)
605 (emit-code src (make-glil-lexical local? boxed? 'set index)))
606 (,loc
607 (error "bad lexical allocation" x loc)))
608 (case context
609 ((tail push vals)
610 (emit-code #f (make-glil-void))))
611 (maybe-emit-return))
612
613 ((<module-ref> src mod name public?)
614 (emit-code src (make-glil-module 'ref mod name public?))
615 (case context
616 ((drop) (emit-code #f (make-glil-call 'drop 1))))
617 (maybe-emit-return))
618
619 ((<module-set> src mod name public? exp)
620 (comp-push exp)
621 (emit-code src (make-glil-module 'set mod name public?))
622 (case context
623 ((tail push vals)
624 (emit-code #f (make-glil-void))))
625 (maybe-emit-return))
626
627 ((<toplevel-ref> src name)
628 (emit-code src (make-glil-toplevel 'ref name))
629 (case context
630 ((drop) (emit-code #f (make-glil-call 'drop 1))))
631 (maybe-emit-return))
632
633 ((<toplevel-set> src name exp)
634 (comp-push exp)
635 (emit-code src (make-glil-toplevel 'set name))
636 (case context
637 ((tail push vals)
638 (emit-code #f (make-glil-void))))
639 (maybe-emit-return))
640
641 ((<toplevel-define> src name exp)
642 (comp-push exp)
643 (emit-code src (make-glil-toplevel 'define name))
644 (case context
645 ((tail push vals)
646 (emit-code #f (make-glil-void))))
647 (maybe-emit-return))
648
649 ((<lambda>)
650 (let ((free-locs (cdr (hashq-ref allocation x))))
651 (case context
652 ((push vals tail)
653 (emit-code #f (flatten-lambda x #f allocation))
654 (if (not (null? free-locs))
655 (begin
656 (for-each
657 (lambda (loc)
658 (pmatch loc
659 ((,local? ,boxed? . ,n)
660 (emit-code #f (make-glil-lexical local? #f 'ref n)))
661 (else (error "bad lambda free var allocation" x loc))))
662 free-locs)
663 (emit-code #f (make-glil-call 'make-closure
664 (length free-locs))))))))
665 (maybe-emit-return))
666
667 ((<lambda-case> src req opt rest kw inits gensyms alternate body)
668 ;; o/~ feature on top of feature o/~
669 ;; req := (name ...)
670 ;; opt := (name ...) | #f
671 ;; rest := name | #f
672 ;; kw: (allow-other-keys? (keyword name var) ...) | #f
673 ;; gensyms: (sym ...)
674 ;; init: tree-il in context of gensyms
675 ;; gensyms map to named arguments in the following order:
676 ;; required, optional (positional), rest, keyword.
677 (let* ((nreq (length req))
678 (nopt (if opt (length opt) 0))
679 (rest-idx (and rest (+ nreq nopt)))
680 (opt-names (or opt '()))
681 (allow-other-keys? (if kw (car kw) #f))
682 (kw-indices (map (lambda (x)
683 (pmatch x
684 ((,key ,name ,var)
685 (cons key (list-index gensyms var)))
686 (else (error "bad kwarg" x))))
687 (if kw (cdr kw) '())))
688 (nargs (apply max (+ nreq nopt (if rest 1 0))
689 (map 1+ (map cdr kw-indices))))
690 (nlocs (cdr (hashq-ref allocation x)))
691 (alternate-label (and alternate (make-label))))
692 (or (= nargs
693 (length gensyms)
694 (+ nreq (length inits) (if rest 1 0)))
695 (error "lambda-case gensyms don't correspond to args"
696 req opt rest kw inits gensyms nreq nopt kw-indices nargs))
697 ;; the prelude, to check args & reset the stack pointer,
698 ;; allowing room for locals
699 (emit-code
700 src
701 (cond
702 (kw
703 (make-glil-kw-prelude nreq nopt rest-idx kw-indices
704 allow-other-keys? nlocs alternate-label))
705 ((or rest opt)
706 (make-glil-opt-prelude nreq nopt rest-idx nlocs alternate-label))
707 (#t
708 (make-glil-std-prelude nreq nlocs alternate-label))))
709 ;; box args if necessary
710 (for-each
711 (lambda (v)
712 (pmatch (hashq-ref (hashq-ref allocation v) self)
713 ((#t #t . ,n)
714 (emit-code #f (make-glil-lexical #t #f 'ref n))
715 (emit-code #f (make-glil-lexical #t #t 'box n)))))
716 gensyms)
717 ;; write bindings info
718 (if (not (null? gensyms))
719 (emit-bindings
720 #f
721 (let lp ((kw (if kw (cdr kw) '()))
722 (names (append (reverse opt-names) (reverse req)))
723 (gensyms (list-tail gensyms (+ nreq nopt
724 (if rest 1 0)))))
725 (pmatch kw
726 (()
727 ;; fixme: check that gensyms is empty
728 (reverse (if rest (cons rest names) names)))
729 (((,key ,name ,var) . ,kw)
730 (if (memq var gensyms)
731 (lp kw (cons name names) (delq var gensyms))
732 (lp kw names gensyms)))
733 (,kw (error "bad keywords, yo" kw))))
734 gensyms allocation self emit-code))
735 ;; init optional/kw args
736 (let lp ((inits inits) (n nreq) (gensyms (list-tail gensyms nreq)))
737 (cond
738 ((null? inits)) ; done
739 ((and rest-idx (= n rest-idx))
740 (lp inits (1+ n) (cdr gensyms)))
741 (#t
742 (pmatch (hashq-ref (hashq-ref allocation (car gensyms)) self)
743 ((#t ,boxed? . ,n*) (guard (= n* n))
744 (let ((L (make-label)))
745 (emit-code #f (make-glil-lexical #t boxed? 'bound? n))
746 (emit-code #f (make-glil-branch 'br-if L))
747 (comp-push (car inits))
748 (emit-code #f (make-glil-lexical #t boxed? 'set n))
749 (emit-label L)
750 (lp (cdr inits) (1+ n) (cdr gensyms))))
751 (#t (error "bad arg allocation" (car gensyms) inits))))))
752 ;; post-prelude case label for label calls
753 (emit-label (car (hashq-ref allocation x)))
754 (comp-tail body)
755 (if (not (null? gensyms))
756 (emit-code #f (make-glil-unbind)))
757 (if alternate-label
758 (begin
759 (emit-label alternate-label)
760 (flatten-lambda-case alternate allocation self self-label
761 fix-labels emit-code)))))
762
763 ((<let> src names gensyms vals body)
764 (for-each comp-push vals)
765 (emit-bindings src names gensyms allocation self emit-code)
766 (for-each (lambda (v)
767 (pmatch (hashq-ref (hashq-ref allocation v) self)
768 ((#t #f . ,n)
769 (emit-code src (make-glil-lexical #t #f 'set n)))
770 ((#t #t . ,n)
771 (emit-code src (make-glil-lexical #t #t 'box n)))
772 (,loc (error "bad let var allocation" x loc))))
773 (reverse gensyms))
774 (comp-tail body)
775 (clear-stack-slots context gensyms)
776 (emit-code #f (make-glil-unbind)))
777
778 ((<letrec> src in-order? names gensyms vals body)
779 ;; First prepare heap storage slots.
780 (for-each (lambda (v)
781 (pmatch (hashq-ref (hashq-ref allocation v) self)
782 ((#t #t . ,n)
783 (emit-code src (make-glil-lexical #t #t 'empty-box n)))
784 (,loc (error "bad letrec var allocation" x loc))))
785 gensyms)
786 ;; Even though the slots are empty, the bindings are valid.
787 (emit-bindings src names gensyms allocation self emit-code)
788 (cond
789 (in-order?
790 ;; For letrec*, bind values in order.
791 (for-each (lambda (name v val)
792 (pmatch (hashq-ref (hashq-ref allocation v) self)
793 ((#t #t . ,n)
794 (comp-push val)
795 (emit-code src (make-glil-lexical #t #t 'set n)))
796 (,loc (error "bad letrec var allocation" x loc))))
797 names gensyms vals))
798 (else
799 ;; But for letrec, eval all values, then bind.
800 (for-each comp-push vals)
801 (for-each (lambda (v)
802 (pmatch (hashq-ref (hashq-ref allocation v) self)
803 ((#t #t . ,n)
804 (emit-code src (make-glil-lexical #t #t 'set n)))
805 (,loc (error "bad letrec var allocation" x loc))))
806 (reverse gensyms))))
807 (comp-tail body)
808 (clear-stack-slots context gensyms)
809 (emit-code #f (make-glil-unbind)))
810
811 ((<fix> src names gensyms vals body)
812 ;; The ideal here is to just render the lambda bodies inline, and
813 ;; wire the code together with gotos. We can do that if
814 ;; analyze-lexicals has determined that a given var has "label"
815 ;; allocation -- which is the case if it is in `fix-labels'.
816 ;;
817 ;; But even for closures that we can't inline, we can do some
818 ;; tricks to avoid heap-allocation for the binding itself. Since
819 ;; we know the vals are lambdas, we can set them to their local
820 ;; var slots first, then capture their bindings, mutating them in
821 ;; place.
822 (let ((new-RA (if (or (eq? context 'tail) RA) #f (make-label))))
823 (for-each
824 (lambda (x v)
825 (cond
826 ((hashq-ref allocation x)
827 ;; allocating a closure
828 (emit-code #f (flatten-lambda x v allocation))
829 (let ((free-locs (cdr (hashq-ref allocation x))))
830 (if (not (null? free-locs))
831 ;; Need to make-closure first, so we have a fresh closure on
832 ;; the heap, but with a temporary free values.
833 (begin
834 (for-each (lambda (loc)
835 (emit-code #f (make-glil-const #f)))
836 free-locs)
837 (emit-code #f (make-glil-call 'make-closure
838 (length free-locs))))))
839 (pmatch (hashq-ref (hashq-ref allocation v) self)
840 ((#t #f . ,n)
841 (emit-code src (make-glil-lexical #t #f 'set n)))
842 (,loc (error "bad fix var allocation" x loc))))
843 (else
844 ;; labels allocation: emit label & body, but jump over it
845 (let ((POST (make-label)))
846 (emit-branch #f 'br POST)
847 (let lp ((lcase (lambda-body x)))
848 (if lcase
849 (record-case lcase
850 ((<lambda-case> src req gensyms body alternate)
851 (emit-label (car (hashq-ref allocation lcase)))
852 ;; FIXME: opt & kw args in the bindings
853 (emit-bindings #f req gensyms allocation self emit-code)
854 (if src
855 (emit-code #f (make-glil-source src)))
856 (comp-fix body (or RA new-RA))
857 (emit-code #f (make-glil-unbind))
858 (lp alternate)))
859 (emit-label POST)))))))
860 vals
861 gensyms)
862 ;; Emit bindings metadata for closures
863 (let ((binds (let lp ((out '()) (gensyms gensyms) (names names))
864 (cond ((null? gensyms) (reverse! out))
865 ((assq (car gensyms) fix-labels)
866 (lp out (cdr gensyms) (cdr names)))
867 (else
868 (lp (acons (car gensyms) (car names) out)
869 (cdr gensyms) (cdr names)))))))
870 (emit-bindings src (map cdr binds) (map car binds)
871 allocation self emit-code))
872 ;; Now go back and fix up the bindings for closures.
873 (for-each
874 (lambda (x v)
875 (let ((free-locs (if (hashq-ref allocation x)
876 (cdr (hashq-ref allocation x))
877 ;; can hit this latter case for labels allocation
878 '())))
879 (if (not (null? free-locs))
880 (begin
881 (for-each
882 (lambda (loc)
883 (pmatch loc
884 ((,local? ,boxed? . ,n)
885 (emit-code #f (make-glil-lexical local? #f 'ref n)))
886 (else (error "bad free var allocation" x loc))))
887 free-locs)
888 (pmatch (hashq-ref (hashq-ref allocation v) self)
889 ((#t #f . ,n)
890 (emit-code #f (make-glil-lexical #t #f 'fix n)))
891 (,loc (error "bad fix var allocation" x loc)))))))
892 vals
893 gensyms)
894 (comp-tail body)
895 (if new-RA
896 (emit-label new-RA))
897 (clear-stack-slots context gensyms)
898 (emit-code #f (make-glil-unbind))))
899
900 ((<let-values> src exp body)
901 (record-case body
902 ((<lambda-case> req opt kw rest gensyms body alternate)
903 (if (or opt kw alternate)
904 (error "unexpected lambda-case in let-values" x))
905 (let ((MV (make-label)))
906 (comp-vals exp MV)
907 (emit-code #f (make-glil-const 1))
908 (emit-label MV)
909 (emit-code src (make-glil-mv-bind
910 (vars->bind-list
911 (append req (if rest (list rest) '()))
912 gensyms allocation self)
913 (and rest #t)))
914 (for-each (lambda (v)
915 (pmatch (hashq-ref (hashq-ref allocation v) self)
916 ((#t #f . ,n)
917 (emit-code src (make-glil-lexical #t #f 'set n)))
918 ((#t #t . ,n)
919 (emit-code src (make-glil-lexical #t #t 'box n)))
920 (,loc (error "bad let-values var allocation" x loc))))
921 (reverse gensyms))
922 (comp-tail body)
923 (clear-stack-slots context gensyms)
924 (emit-code #f (make-glil-unbind))))))
925
926 ;; much trickier than i thought this would be, at first, due to the need
927 ;; to have body's return value(s) on the stack while the unwinder runs,
928 ;; then proceed with returning or dropping or what-have-you, interacting
929 ;; with RA and MVRA. What have you, I say.
930 ((<dynwind> src winder pre body post unwinder)
931 (define (thunk? x)
932 (and (lambda? x)
933 (null? (lambda-case-gensyms (lambda-body x)))))
934 (define (make-wrong-type-arg x)
935 (make-primcall src 'scm-error
936 (list
937 (make-const #f 'wrong-type-arg)
938 (make-const #f "dynamic-wind")
939 (make-const #f "Wrong type (expecting thunk): ~S")
940 (make-primcall #f 'list (list x))
941 (make-primcall #f 'list (list x)))))
942 (define (emit-thunk-check x)
943 (comp-drop (make-conditional
944 src
945 (make-primcall src 'thunk? (list x))
946 (make-void #f)
947 (make-wrong-type-arg x))))
948
949 ;; We know at this point that `winder' and `unwinder' are
950 ;; constant expressions and can be duplicated.
951 (if (not (thunk? winder))
952 (emit-thunk-check winder))
953 (comp-push winder)
954 (if (not (thunk? unwinder))
955 (emit-thunk-check unwinder))
956 (comp-push unwinder)
957 (comp-drop pre)
958 (emit-code #f (make-glil-call 'wind 2))
959
960 (case context
961 ((tail)
962 (let ((MV (make-label)))
963 (comp-vals body MV)
964 ;; one value: unwind...
965 (emit-code #f (make-glil-call 'unwind 0))
966 (comp-drop post)
967 ;; ...and return the val
968 (emit-code #f (make-glil-call 'return 1))
969
970 (emit-label MV)
971 ;; multiple values: unwind...
972 (emit-code #f (make-glil-call 'unwind 0))
973 (comp-drop post)
974 ;; and return the values.
975 (emit-code #f (make-glil-call 'return/nvalues 1))))
976
977 ((push)
978 ;; we only want one value. so ask for one value
979 (comp-push body)
980 ;; and unwind, leaving the val on the stack
981 (emit-code #f (make-glil-call 'unwind 0))
982 (comp-drop post))
983
984 ((vals)
985 (let ((MV (make-label)))
986 (comp-vals body MV)
987 ;; one value: push 1 and fall through to MV case
988 (emit-code #f (make-glil-const 1))
989
990 (emit-label MV)
991 ;; multiple values: unwind...
992 (emit-code #f (make-glil-call 'unwind 0))
993 (comp-drop post)
994 ;; and goto the MVRA.
995 (emit-branch #f 'br MVRA)))
996
997 ((drop)
998 ;; compile body, discarding values. then unwind...
999 (comp-drop body)
1000 (emit-code #f (make-glil-call 'unwind 0))
1001 (comp-drop post)
1002 ;; and fall through, or goto RA if there is one.
1003 (if RA
1004 (emit-branch #f 'br RA)))))
1005
1006 ((<dynlet> src fluids vals body)
1007 (for-each comp-push fluids)
1008 (for-each comp-push vals)
1009 (emit-code #f (make-glil-call 'wind-fluids (length fluids)))
1010
1011 (case context
1012 ((tail)
1013 (let ((MV (make-label)))
1014 ;; NB: in tail case, it is possible to preserve asymptotic tail
1015 ;; recursion, via merging unwind-fluids structures -- but we'd need
1016 ;; to compile in the body twice (once in tail context, assuming the
1017 ;; caller unwinds, and once with this trampoline thing, unwinding
1018 ;; ourselves).
1019 (comp-vals body MV)
1020 ;; one value: unwind and return
1021 (emit-code #f (make-glil-call 'unwind-fluids 0))
1022 (emit-code #f (make-glil-call 'return 1))
1023
1024 (emit-label MV)
1025 ;; multiple values: unwind and return values
1026 (emit-code #f (make-glil-call 'unwind-fluids 0))
1027 (emit-code #f (make-glil-call 'return/nvalues 1))))
1028
1029 ((push)
1030 (comp-push body)
1031 (emit-code #f (make-glil-call 'unwind-fluids 0)))
1032
1033 ((vals)
1034 (let ((MV (make-label)))
1035 (comp-vals body MV)
1036 ;; one value: push 1 and fall through to MV case
1037 (emit-code #f (make-glil-const 1))
1038
1039 (emit-label MV)
1040 ;; multiple values: unwind and goto MVRA
1041 (emit-code #f (make-glil-call 'unwind-fluids 0))
1042 (emit-branch #f 'br MVRA)))
1043
1044 ((drop)
1045 ;; compile body, discarding values. then unwind...
1046 (comp-drop body)
1047 (emit-code #f (make-glil-call 'unwind-fluids 0))
1048 ;; and fall through, or goto RA if there is one.
1049 (if RA
1050 (emit-branch #f 'br RA)))))
1051
1052 ((<dynref> src fluid)
1053 (case context
1054 ((drop)
1055 (comp-drop fluid))
1056 ((push vals tail)
1057 (comp-push fluid)
1058 (emit-code #f (make-glil-call 'fluid-ref 1))))
1059 (maybe-emit-return))
1060
1061 ((<dynset> src fluid exp)
1062 (comp-push fluid)
1063 (comp-push exp)
1064 (emit-code #f (make-glil-call 'fluid-set 2))
1065 (case context
1066 ((push vals tail)
1067 (emit-code #f (make-glil-void))))
1068 (maybe-emit-return))
1069
1070 ;; What's the deal here? The deal is that we are compiling the start of a
1071 ;; delimited continuation. We try to avoid heap allocation in the normal
1072 ;; case; so the body is an expression, not a thunk, and we try to render
1073 ;; the handler inline. Also we did some analysis, in analyze.scm, so that
1074 ;; if the continuation isn't referenced, we don't reify it. This makes it
1075 ;; possible to implement catch and throw with delimited continuations,
1076 ;; without any overhead.
1077 ((<prompt> src tag body handler)
1078 (let ((H (make-label))
1079 (POST (make-label))
1080 (escape-only? (hashq-ref allocation x)))
1081 ;; First, set up the prompt.
1082 (comp-push tag)
1083 (emit-code src (make-glil-prompt H escape-only?))
1084
1085 ;; Then we compile the body, with its normal return path, unwinding
1086 ;; before proceeding.
1087 (case context
1088 ((tail)
1089 (let ((MV (make-label)))
1090 (comp-vals body MV)
1091 ;; one value: unwind and return
1092 (emit-code #f (make-glil-call 'unwind 0))
1093 (emit-code #f (make-glil-call 'return 1))
1094 ;; multiple values: unwind and return
1095 (emit-label MV)
1096 (emit-code #f (make-glil-call 'unwind 0))
1097 (emit-code #f (make-glil-call 'return/nvalues 1))))
1098
1099 ((push)
1100 ;; we only want one value. so ask for one value, unwind, and jump to
1101 ;; post
1102 (comp-push body)
1103 (emit-code #f (make-glil-call 'unwind 0))
1104 (emit-branch #f 'br (or RA POST)))
1105
1106 ((vals)
1107 (let ((MV (make-label)))
1108 (comp-vals body MV)
1109 ;; one value: push 1 and fall through to MV case
1110 (emit-code #f (make-glil-const 1))
1111 ;; multiple values: unwind and goto MVRA
1112 (emit-label MV)
1113 (emit-code #f (make-glil-call 'unwind 0))
1114 (emit-branch #f 'br MVRA)))
1115
1116 ((drop)
1117 ;; compile body, discarding values, then unwind & fall through.
1118 (comp-drop body)
1119 (emit-code #f (make-glil-call 'unwind 0))
1120 (emit-branch #f 'br (or RA POST))))
1121
1122 (emit-label H)
1123 ;; Now the handler. The stack is now made up of the continuation, and
1124 ;; then the args to the continuation (pushed separately), and then the
1125 ;; number of args, including the continuation.
1126 (record-case handler
1127 ((<lambda-case> req opt kw rest gensyms body alternate)
1128 (if (or opt kw alternate)
1129 (error "unexpected lambda-case in prompt" x))
1130 (emit-code src (make-glil-mv-bind
1131 (vars->bind-list
1132 (append req (if rest (list rest) '()))
1133 gensyms allocation self)
1134 (and rest #t)))
1135 (for-each (lambda (v)
1136 (pmatch (hashq-ref (hashq-ref allocation v) self)
1137 ((#t #f . ,n)
1138 (emit-code src (make-glil-lexical #t #f 'set n)))
1139 ((#t #t . ,n)
1140 (emit-code src (make-glil-lexical #t #t 'box n)))
1141 (,loc
1142 (error "bad prompt handler arg allocation" x loc))))
1143 (reverse gensyms))
1144 (comp-tail body)
1145 (emit-code #f (make-glil-unbind))))
1146
1147 (if (and (not RA)
1148 (or (eq? context 'push) (eq? context 'drop)))
1149 (emit-label POST))))
1150
1151 ((<abort> src tag args tail)
1152 (comp-push tag)
1153 (for-each comp-push args)
1154 (comp-push tail)
1155 (emit-code src (make-glil-call 'abort (length args)))
1156 ;; so, the abort can actually return. if it does, the values will be on
1157 ;; the stack, then the MV marker, just as in an MV context.
1158 (case context
1159 ((tail)
1160 ;; Return values.
1161 (emit-code #f (make-glil-call 'return/nvalues 1)))
1162 ((drop)
1163 ;; Drop all values and goto RA, or otherwise fall through.
1164 (emit-code #f (make-glil-mv-bind 0 #f))
1165 (if RA (emit-branch #f 'br RA)))
1166 ((push)
1167 ;; Truncate to one value.
1168 (emit-code #f (make-glil-mv-bind 1 #f)))
1169 ((vals)
1170 ;; Go to MVRA.
1171 (emit-branch #f 'br MVRA)))))))