Merge remote-tracking branch 'origin/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 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 (list . list)
114 (vector . vector)
115 ((class-of . 1) . class-of)
116 ((@slot-ref . 2) . slot-ref)
117 ((@slot-set! . 3) . slot-set)
118 ((string-length . 1) . string-length)
119 ((string-ref . 2) . string-ref)
120 ((vector-length . 1) . vector-length)
121 ((vector-ref . 2) . vector-ref)
122 ((vector-set! . 3) . vector-set)
123 ((variable-ref . 1) . variable-ref)
124 ;; nb, *not* variable-set! -- the args are switched
125 ((variable-bound? . 1) . variable-bound?)
126 ((struct? . 1) . struct?)
127 ((struct-vtable . 1) . struct-vtable)
128 ((struct-ref . 2) . struct-ref)
129 ((struct-set! . 3) . struct-set)
130 (make-struct/no-tail . make-struct)
131
132 ;; hack for javascript
133 ((return . 1) . return)
134 ;; hack for lua
135 (return/values . return/values)
136
137 ((bytevector-u8-ref . 2) . bv-u8-ref)
138 ((bytevector-u8-set! . 3) . bv-u8-set)
139 ((bytevector-s8-ref . 2) . bv-s8-ref)
140 ((bytevector-s8-set! . 3) . bv-s8-set)
141
142 ((bytevector-u16-ref . 3) . bv-u16-ref)
143 ((bytevector-u16-set! . 4) . bv-u16-set)
144 ((bytevector-u16-native-ref . 2) . bv-u16-native-ref)
145 ((bytevector-u16-native-set! . 3) . bv-u16-native-set)
146 ((bytevector-s16-ref . 3) . bv-s16-ref)
147 ((bytevector-s16-set! . 4) . bv-s16-set)
148 ((bytevector-s16-native-ref . 2) . bv-s16-native-ref)
149 ((bytevector-s16-native-set! . 3) . bv-s16-native-set)
150
151 ((bytevector-u32-ref . 3) . bv-u32-ref)
152 ((bytevector-u32-set! . 4) . bv-u32-set)
153 ((bytevector-u32-native-ref . 2) . bv-u32-native-ref)
154 ((bytevector-u32-native-set! . 3) . bv-u32-native-set)
155 ((bytevector-s32-ref . 3) . bv-s32-ref)
156 ((bytevector-s32-set! . 4) . bv-s32-set)
157 ((bytevector-s32-native-ref . 2) . bv-s32-native-ref)
158 ((bytevector-s32-native-set! . 3) . bv-s32-native-set)
159
160 ((bytevector-u64-ref . 3) . bv-u64-ref)
161 ((bytevector-u64-set! . 4) . bv-u64-set)
162 ((bytevector-u64-native-ref . 2) . bv-u64-native-ref)
163 ((bytevector-u64-native-set! . 3) . bv-u64-native-set)
164 ((bytevector-s64-ref . 3) . bv-s64-ref)
165 ((bytevector-s64-set! . 4) . bv-s64-set)
166 ((bytevector-s64-native-ref . 2) . bv-s64-native-ref)
167 ((bytevector-s64-native-set! . 3) . bv-s64-native-set)
168
169 ((bytevector-ieee-single-ref . 3) . bv-f32-ref)
170 ((bytevector-ieee-single-set! . 4) . bv-f32-set)
171 ((bytevector-ieee-single-native-ref . 2) . bv-f32-native-ref)
172 ((bytevector-ieee-single-native-set! . 3) . bv-f32-native-set)
173 ((bytevector-ieee-double-ref . 3) . bv-f64-ref)
174 ((bytevector-ieee-double-set! . 4) . bv-f64-set)
175 ((bytevector-ieee-double-native-ref . 2) . bv-f64-native-ref)
176 ((bytevector-ieee-double-native-set! . 3) . bv-f64-native-set)))
177
178
179 \f
180
181 (define (make-label) (gensym ":L"))
182
183 (define (vars->bind-list ids vars allocation proc)
184 (map (lambda (id v)
185 (pmatch (hashq-ref (hashq-ref allocation v) proc)
186 ((#t ,boxed? . ,n)
187 (list id boxed? n))
188 (,x (error "bad var list element" id v x))))
189 ids
190 vars))
191
192 (define (emit-bindings src ids vars allocation proc emit-code)
193 (emit-code src (make-glil-bind
194 (vars->bind-list ids vars allocation proc))))
195
196 (define (with-output-to-code proc)
197 (let ((out '()))
198 (define (emit-code src x)
199 (set! out (cons x out))
200 (if src
201 (set! out (cons (make-glil-source src) out))))
202 (proc emit-code)
203 (reverse out)))
204
205 (define (flatten-lambda x self-label allocation)
206 (record-case x
207 ((<lambda> src meta body)
208 (make-glil-program
209 meta
210 (with-output-to-code
211 (lambda (emit-code)
212 ;; write source info for proc
213 (if src (emit-code #f (make-glil-source src)))
214 ;; compile the body, yo
215 (flatten-lambda-case body allocation x self-label
216 (car (hashq-ref allocation x))
217 emit-code)))))))
218
219 (define (flatten-lambda-case lcase allocation self self-label fix-labels
220 emit-code)
221 (define (emit-label label)
222 (emit-code #f (make-glil-label label)))
223 (define (emit-branch src inst label)
224 (emit-code src (make-glil-branch inst label)))
225
226 ;; RA: "return address"; #f unless we're in a non-tail fix with labels
227 ;; MVRA: "multiple-values return address"; #f unless we're in a let-values
228 (let comp ((x lcase) (context 'tail) (RA #f) (MVRA #f))
229 (define (comp-tail tree) (comp tree context RA MVRA))
230 (define (comp-push tree) (comp tree 'push #f #f))
231 (define (comp-drop tree) (comp tree 'drop #f #f))
232 (define (comp-vals tree MVRA) (comp tree 'vals #f MVRA))
233 (define (comp-fix tree RA) (comp tree context RA MVRA))
234
235 ;; A couple of helpers. Note that if we are in tail context, we
236 ;; won't have an RA.
237 (define (maybe-emit-return)
238 (if RA
239 (emit-branch #f 'br RA)
240 (if (eq? context 'tail)
241 (emit-code #f (make-glil-call 'return 1)))))
242
243 ;; After lexical binding forms in non-tail context, call this
244 ;; function to clear stack slots, allowing their previous values to
245 ;; be collected.
246 (define (clear-stack-slots context syms)
247 (case context
248 ((push drop)
249 (for-each (lambda (v)
250 (and=>
251 ;; Can be #f if the var is labels-allocated.
252 (hashq-ref allocation v)
253 (lambda (h)
254 (pmatch (hashq-ref h self)
255 ((#t _ . ,n)
256 (emit-code #f (make-glil-void))
257 (emit-code #f (make-glil-lexical #t #f 'set n)))
258 (,loc (error "bad let var allocation" x loc))))))
259 syms))))
260
261 (record-case x
262 ((<void>)
263 (case context
264 ((push vals tail)
265 (emit-code #f (make-glil-void))))
266 (maybe-emit-return))
267
268 ((<const> src exp)
269 (case context
270 ((push vals tail)
271 (emit-code src (make-glil-const exp))))
272 (maybe-emit-return))
273
274 ((<seq> head tail)
275 (comp-drop head)
276 (comp-tail tail))
277
278 ((<call> src proc args)
279 (cond
280 ;; call to the same lambda-case in tail position
281 ((and (lexical-ref? proc)
282 self-label (eq? (lexical-ref-gensym proc) self-label)
283 (eq? context 'tail)
284 (not (lambda-case-kw lcase))
285 (not (lambda-case-rest lcase))
286 (= (length args)
287 (+ (length (lambda-case-req lcase))
288 (or (and=> (lambda-case-opt lcase) length) 0))))
289 (for-each comp-push args)
290 (for-each (lambda (sym)
291 (pmatch (hashq-ref (hashq-ref allocation sym) self)
292 ((#t #f . ,index) ; unboxed
293 (emit-code #f (make-glil-lexical #t #f 'set index)))
294 ((#t #t . ,index) ; boxed
295 ;; new box
296 (emit-code #f (make-glil-lexical #t #t 'box index)))
297 (,x (error "bad lambda-case arg allocation" x))))
298 (reverse (lambda-case-gensyms lcase)))
299 (emit-branch src 'br (car (hashq-ref allocation lcase))))
300
301 ;; lambda, the ultimate goto
302 ((and (lexical-ref? proc)
303 (assq (lexical-ref-gensym proc) fix-labels))
304 ;; like the self-tail-call case, though we can handle "drop"
305 ;; contexts too. first, evaluate new values, pushing them on
306 ;; the stack
307 (for-each comp-push args)
308 ;; find the specific case, rename args, and goto the case label
309 (let lp ((lcase (lambda-body
310 (assq-ref fix-labels (lexical-ref-gensym proc)))))
311 (cond
312 ((and (lambda-case? lcase)
313 (not (lambda-case-kw lcase))
314 (not (lambda-case-opt lcase))
315 (not (lambda-case-rest lcase))
316 (= (length args) (length (lambda-case-req lcase))))
317 ;; we have a case that matches the args; rename variables
318 ;; and goto the case label
319 (for-each (lambda (sym)
320 (pmatch (hashq-ref (hashq-ref allocation sym) self)
321 ((#t #f . ,index) ; unboxed
322 (emit-code #f (make-glil-lexical #t #f 'set index)))
323 ((#t #t . ,index) ; boxed
324 (emit-code #f (make-glil-lexical #t #t 'box index)))
325 (,x (error "bad lambda-case arg allocation" x))))
326 (reverse (lambda-case-gensyms lcase)))
327 (emit-branch src 'br (car (hashq-ref allocation lcase))))
328 ((lambda-case? lcase)
329 ;; no match, try next case
330 (lp (lambda-case-alternate lcase)))
331 (else
332 ;; no cases left. we can't really handle this currently.
333 ;; ideally we would push on a new frame, then do a "local
334 ;; call" -- which doesn't require consing up a program
335 ;; object. but for now error, as this sort of case should
336 ;; preclude label allocation.
337 (error "couldn't find matching case for label call" x)))))
338
339 (else
340 (if (not (eq? context 'tail))
341 (emit-code src (make-glil-call 'new-frame 0)))
342 (comp-push proc)
343 (for-each comp-push args)
344 (let ((len (length args)))
345 (case context
346 ((tail) (emit-code src (make-glil-call 'tail-call len)))
347 ((push) (emit-code src (make-glil-call 'call len))
348 (maybe-emit-return))
349 ((vals) (emit-code src (make-glil-mv-call len MVRA))
350 (maybe-emit-return))
351 ((drop) (let ((MV (make-label)) (POST (make-label)))
352 (emit-code src (make-glil-mv-call len MV))
353 (emit-code #f (make-glil-call 'drop 1))
354 (emit-branch #f 'br (or RA POST))
355 (emit-label MV)
356 (emit-code #f (make-glil-mv-bind 0 #f))
357 (if RA
358 (emit-branch #f 'br RA)
359 (emit-label POST)))))))))
360
361 ((<primcall> src name args)
362 (pmatch (cons name args)
363 ((@apply ,proc . ,args)
364 (cond
365 ((and (primitive-ref? proc) (eq? (primitive-ref-name proc) 'values)
366 (not (eq? context 'push)) (not (eq? context 'vals)))
367 ;; tail: (lambda () (apply values '(1 2)))
368 ;; drop: (lambda () (apply values '(1 2)) 3)
369 ;; push: (lambda () (list (apply values '(10 12)) 1))
370 (case context
371 ((drop) (for-each comp-drop args) (maybe-emit-return))
372 ((tail)
373 (for-each comp-push args)
374 (emit-code src (make-glil-call 'return/values* (length args))))))
375
376 (else
377 (case context
378 ((tail)
379 (comp-push proc)
380 (for-each comp-push args)
381 (emit-code src (make-glil-call 'tail-apply (1+ (length args)))))
382 ((push)
383 (emit-code src (make-glil-call 'new-frame 0))
384 (comp-push proc)
385 (for-each comp-push args)
386 (emit-code src (make-glil-call 'apply (1+ (length args))))
387 (maybe-emit-return))
388 (else
389 (comp-tail (make-primcall src 'apply (cons proc args))))))))
390
391 ((values . _)
392 ;; tail: (lambda () (values '(1 2)))
393 ;; drop: (lambda () (values '(1 2)) 3)
394 ;; push: (lambda () (list (values '(10 12)) 1))
395 ;; vals: (let-values (((a b ...) (values 1 2 ...))) ...)
396 (case context
397 ((drop) (for-each comp-drop args) (maybe-emit-return))
398 ((push)
399 (case (length args)
400 ((0)
401 ;; FIXME: This is surely an error. We need to add a
402 ;; values-mismatch warning pass.
403 (comp-push (make-call src (make-primitive-ref #f 'values)
404 '())))
405 ((1)
406 (comp-push (car args)))
407 (else
408 ;; Taking advantage of unspecified order of evaluation of
409 ;; arguments.
410 (for-each comp-drop (cdr args))
411 (comp-push (car args)))))
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 ((not ,x)
532 (record-case x
533 ((<primcall> name args)
534 (pmatch (cons name args)
535 ((eq? ,a ,b)
536 (comp-push a)
537 (comp-push b)
538 (emit-branch src 'br-if-eq L1))
539 ((null? ,x)
540 (comp-push x)
541 (emit-branch src 'br-if-null L1))
542 (else
543 (comp-push x)
544 (emit-branch src 'br-if L1))))
545 (else
546 (comp-push x)
547 (emit-branch src 'br-if L1))))
548 (else
549 (comp-push test)
550 (emit-branch src 'br-if-not L1))))
551 (else
552 (comp-push test)
553 (emit-branch src 'br-if-not L1)))
554
555 (comp-tail consequent)
556 ;; if there is an RA, comp-tail will cause a jump to it -- just
557 ;; have to clean up here if there is no RA.
558 (if (and (not RA) (not (eq? context 'tail)))
559 (emit-branch #f 'br L2))
560 (emit-label L1)
561 (comp-tail alternate)
562 (if (and (not RA) (not (eq? context 'tail)))
563 (emit-label L2))))
564
565 ((<primitive-ref> src name)
566 (cond
567 ((eq? (module-variable (fluid-ref *comp-module*) name)
568 (module-variable the-root-module name))
569 (case context
570 ((tail push vals)
571 (emit-code src (make-glil-toplevel 'ref name))))
572 (maybe-emit-return))
573 ((module-variable the-root-module name)
574 (case context
575 ((tail push vals)
576 (emit-code src (make-glil-module 'ref '(guile) name #f))))
577 (maybe-emit-return))
578 (else
579 (case context
580 ((tail push vals)
581 (emit-code src (make-glil-module
582 'ref (module-name (fluid-ref *comp-module*)) name #f))))
583 (maybe-emit-return))))
584
585 ((<lexical-ref> src gensym)
586 (case context
587 ((push vals tail)
588 (pmatch (hashq-ref (hashq-ref allocation gensym) self)
589 ((,local? ,boxed? . ,index)
590 (emit-code src (make-glil-lexical local? boxed? 'ref index)))
591 (,loc
592 (error "bad lexical allocation" x loc)))))
593 (maybe-emit-return))
594
595 ((<lexical-set> src gensym exp)
596 (comp-push exp)
597 (pmatch (hashq-ref (hashq-ref allocation gensym) self)
598 ((,local? ,boxed? . ,index)
599 (emit-code src (make-glil-lexical local? boxed? 'set index)))
600 (,loc
601 (error "bad lexical allocation" x loc)))
602 (case context
603 ((tail push vals)
604 (emit-code #f (make-glil-void))))
605 (maybe-emit-return))
606
607 ((<module-ref> src mod name public?)
608 (emit-code src (make-glil-module 'ref mod name public?))
609 (case context
610 ((drop) (emit-code #f (make-glil-call 'drop 1))))
611 (maybe-emit-return))
612
613 ((<module-set> src mod name public? exp)
614 (comp-push exp)
615 (emit-code src (make-glil-module 'set mod name public?))
616 (case context
617 ((tail push vals)
618 (emit-code #f (make-glil-void))))
619 (maybe-emit-return))
620
621 ((<toplevel-ref> src name)
622 (emit-code src (make-glil-toplevel 'ref name))
623 (case context
624 ((drop) (emit-code #f (make-glil-call 'drop 1))))
625 (maybe-emit-return))
626
627 ((<toplevel-set> src name exp)
628 (comp-push exp)
629 (emit-code src (make-glil-toplevel 'set name))
630 (case context
631 ((tail push vals)
632 (emit-code #f (make-glil-void))))
633 (maybe-emit-return))
634
635 ((<toplevel-define> src name exp)
636 (comp-push exp)
637 (emit-code src (make-glil-toplevel 'define name))
638 (case context
639 ((tail push vals)
640 (emit-code #f (make-glil-void))))
641 (maybe-emit-return))
642
643 ((<lambda>)
644 (let ((free-locs (cdr (hashq-ref allocation x))))
645 (case context
646 ((push vals tail)
647 (emit-code #f (flatten-lambda x #f allocation))
648 (if (not (null? free-locs))
649 (begin
650 (for-each
651 (lambda (loc)
652 (pmatch loc
653 ((,local? ,boxed? . ,n)
654 (emit-code #f (make-glil-lexical local? #f 'ref n)))
655 (else (error "bad lambda free var allocation" x loc))))
656 free-locs)
657 (emit-code #f (make-glil-call 'make-closure
658 (length free-locs))))))))
659 (maybe-emit-return))
660
661 ((<lambda-case> src req opt rest kw inits gensyms alternate body)
662 ;; o/~ feature on top of feature o/~
663 ;; req := (name ...)
664 ;; opt := (name ...) | #f
665 ;; rest := name | #f
666 ;; kw: (allow-other-keys? (keyword name var) ...) | #f
667 ;; gensyms: (sym ...)
668 ;; init: tree-il in context of gensyms
669 ;; gensyms map to named arguments in the following order:
670 ;; required, optional (positional), rest, keyword.
671 (let* ((nreq (length req))
672 (nopt (if opt (length opt) 0))
673 (rest-idx (and rest (+ nreq nopt)))
674 (opt-names (or opt '()))
675 (allow-other-keys? (if kw (car kw) #f))
676 (kw-indices (map (lambda (x)
677 (pmatch x
678 ((,key ,name ,var)
679 (cons key (list-index gensyms var)))
680 (else (error "bad kwarg" x))))
681 (if kw (cdr kw) '())))
682 (nargs (apply max (+ nreq nopt (if rest 1 0))
683 (map 1+ (map cdr kw-indices))))
684 (nlocs (cdr (hashq-ref allocation x)))
685 (alternate-label (and alternate (make-label))))
686 (or (= nargs
687 (length gensyms)
688 (+ nreq (length inits) (if rest 1 0)))
689 (error "lambda-case gensyms don't correspond to args"
690 req opt rest kw inits gensyms nreq nopt kw-indices nargs))
691 ;; the prelude, to check args & reset the stack pointer,
692 ;; allowing room for locals
693 (emit-code
694 src
695 (cond
696 (kw
697 (make-glil-kw-prelude nreq nopt rest-idx kw-indices
698 allow-other-keys? nlocs alternate-label))
699 ((or rest opt)
700 (make-glil-opt-prelude nreq nopt rest-idx nlocs alternate-label))
701 (#t
702 (make-glil-std-prelude nreq nlocs alternate-label))))
703 ;; box args if necessary
704 (for-each
705 (lambda (v)
706 (pmatch (hashq-ref (hashq-ref allocation v) self)
707 ((#t #t . ,n)
708 (emit-code #f (make-glil-lexical #t #f 'ref n))
709 (emit-code #f (make-glil-lexical #t #t 'box n)))))
710 gensyms)
711 ;; write bindings info
712 (if (not (null? gensyms))
713 (emit-bindings
714 #f
715 (let lp ((kw (if kw (cdr kw) '()))
716 (names (append (reverse opt-names) (reverse req)))
717 (gensyms (list-tail gensyms (+ nreq nopt
718 (if rest 1 0)))))
719 (pmatch kw
720 (()
721 ;; fixme: check that gensyms is empty
722 (reverse (if rest (cons rest names) names)))
723 (((,key ,name ,var) . ,kw)
724 (if (memq var gensyms)
725 (lp kw (cons name names) (delq var gensyms))
726 (lp kw names gensyms)))
727 (,kw (error "bad keywords, yo" kw))))
728 gensyms allocation self emit-code))
729 ;; init optional/kw args
730 (let lp ((inits inits) (n nreq) (gensyms (list-tail gensyms nreq)))
731 (cond
732 ((null? inits)) ; done
733 ((and rest-idx (= n rest-idx))
734 (lp inits (1+ n) (cdr gensyms)))
735 (#t
736 (pmatch (hashq-ref (hashq-ref allocation (car gensyms)) self)
737 ((#t ,boxed? . ,n*) (guard (= n* n))
738 (let ((L (make-label)))
739 (emit-code #f (make-glil-lexical #t boxed? 'bound? n))
740 (emit-code #f (make-glil-branch 'br-if L))
741 (comp-push (car inits))
742 (emit-code #f (make-glil-lexical #t boxed? 'set n))
743 (emit-label L)
744 (lp (cdr inits) (1+ n) (cdr gensyms))))
745 (#t (error "bad arg allocation" (car gensyms) inits))))))
746 ;; post-prelude case label for label calls
747 (emit-label (car (hashq-ref allocation x)))
748 (comp-tail body)
749 (if (not (null? gensyms))
750 (emit-code #f (make-glil-unbind)))
751 (if alternate-label
752 (begin
753 (emit-label alternate-label)
754 (flatten-lambda-case alternate allocation self self-label
755 fix-labels emit-code)))))
756
757 ((<let> src names gensyms vals body)
758 (for-each comp-push vals)
759 (emit-bindings src names gensyms allocation self emit-code)
760 (for-each (lambda (v)
761 (pmatch (hashq-ref (hashq-ref allocation v) self)
762 ((#t #f . ,n)
763 (emit-code src (make-glil-lexical #t #f 'set n)))
764 ((#t #t . ,n)
765 (emit-code src (make-glil-lexical #t #t 'box n)))
766 (,loc (error "bad let var allocation" x loc))))
767 (reverse gensyms))
768 (comp-tail body)
769 (clear-stack-slots context gensyms)
770 (emit-code #f (make-glil-unbind)))
771
772 ((<letrec> src in-order? names gensyms vals body)
773 ;; First prepare heap storage slots.
774 (for-each (lambda (v)
775 (pmatch (hashq-ref (hashq-ref allocation v) self)
776 ((#t #t . ,n)
777 (emit-code src (make-glil-lexical #t #t 'empty-box n)))
778 (,loc (error "bad letrec var allocation" x loc))))
779 gensyms)
780 ;; Even though the slots are empty, the bindings are valid.
781 (emit-bindings src names gensyms allocation self emit-code)
782 (cond
783 (in-order?
784 ;; For letrec*, bind values in order.
785 (for-each (lambda (name v val)
786 (pmatch (hashq-ref (hashq-ref allocation v) self)
787 ((#t #t . ,n)
788 (comp-push val)
789 (emit-code src (make-glil-lexical #t #t 'set n)))
790 (,loc (error "bad letrec var allocation" x loc))))
791 names gensyms vals))
792 (else
793 ;; But for letrec, eval all values, then bind.
794 (for-each comp-push vals)
795 (for-each (lambda (v)
796 (pmatch (hashq-ref (hashq-ref allocation v) self)
797 ((#t #t . ,n)
798 (emit-code src (make-glil-lexical #t #t 'set n)))
799 (,loc (error "bad letrec var allocation" x loc))))
800 (reverse gensyms))))
801 (comp-tail body)
802 (clear-stack-slots context gensyms)
803 (emit-code #f (make-glil-unbind)))
804
805 ((<fix> src names gensyms vals body)
806 ;; The ideal here is to just render the lambda bodies inline, and
807 ;; wire the code together with gotos. We can do that if
808 ;; analyze-lexicals has determined that a given var has "label"
809 ;; allocation -- which is the case if it is in `fix-labels'.
810 ;;
811 ;; But even for closures that we can't inline, we can do some
812 ;; tricks to avoid heap-allocation for the binding itself. Since
813 ;; we know the vals are lambdas, we can set them to their local
814 ;; var slots first, then capture their bindings, mutating them in
815 ;; place.
816 (let ((new-RA (if (or (eq? context 'tail) RA) #f (make-label))))
817 (for-each
818 (lambda (x v)
819 (cond
820 ((hashq-ref allocation x)
821 ;; allocating a closure
822 (emit-code #f (flatten-lambda x v allocation))
823 (let ((free-locs (cdr (hashq-ref allocation x))))
824 (if (not (null? free-locs))
825 ;; Need to make-closure first, so we have a fresh closure on
826 ;; the heap, but with a temporary free values.
827 (begin
828 (for-each (lambda (loc)
829 (emit-code #f (make-glil-const #f)))
830 free-locs)
831 (emit-code #f (make-glil-call 'make-closure
832 (length free-locs))))))
833 (pmatch (hashq-ref (hashq-ref allocation v) self)
834 ((#t #f . ,n)
835 (emit-code src (make-glil-lexical #t #f 'set n)))
836 (,loc (error "bad fix var allocation" x loc))))
837 (else
838 ;; labels allocation: emit label & body, but jump over it
839 (let ((POST (make-label)))
840 (emit-branch #f 'br POST)
841 (let lp ((lcase (lambda-body x)))
842 (if lcase
843 (record-case lcase
844 ((<lambda-case> src req gensyms body alternate)
845 (emit-label (car (hashq-ref allocation lcase)))
846 ;; FIXME: opt & kw args in the bindings
847 (emit-bindings #f req gensyms allocation self emit-code)
848 (if src
849 (emit-code #f (make-glil-source src)))
850 (comp-fix body (or RA new-RA))
851 (emit-code #f (make-glil-unbind))
852 (lp alternate)))
853 (emit-label POST)))))))
854 vals
855 gensyms)
856 ;; Emit bindings metadata for closures
857 (let ((binds (let lp ((out '()) (gensyms gensyms) (names names))
858 (cond ((null? gensyms) (reverse! out))
859 ((assq (car gensyms) fix-labels)
860 (lp out (cdr gensyms) (cdr names)))
861 (else
862 (lp (acons (car gensyms) (car names) out)
863 (cdr gensyms) (cdr names)))))))
864 (emit-bindings src (map cdr binds) (map car binds)
865 allocation self emit-code))
866 ;; Now go back and fix up the bindings for closures.
867 (for-each
868 (lambda (x v)
869 (let ((free-locs (if (hashq-ref allocation x)
870 (cdr (hashq-ref allocation x))
871 ;; can hit this latter case for labels allocation
872 '())))
873 (if (not (null? free-locs))
874 (begin
875 (for-each
876 (lambda (loc)
877 (pmatch loc
878 ((,local? ,boxed? . ,n)
879 (emit-code #f (make-glil-lexical local? #f 'ref n)))
880 (else (error "bad free var allocation" x loc))))
881 free-locs)
882 (pmatch (hashq-ref (hashq-ref allocation v) self)
883 ((#t #f . ,n)
884 (emit-code #f (make-glil-lexical #t #f 'fix n)))
885 (,loc (error "bad fix var allocation" x loc)))))))
886 vals
887 gensyms)
888 (comp-tail body)
889 (if new-RA
890 (emit-label new-RA))
891 (clear-stack-slots context gensyms)
892 (emit-code #f (make-glil-unbind))))
893
894 ((<let-values> src exp body)
895 (record-case body
896 ((<lambda-case> req opt kw rest gensyms body alternate)
897 (if (or opt kw alternate)
898 (error "unexpected lambda-case in let-values" x))
899 (let ((MV (make-label)))
900 (comp-vals exp MV)
901 (emit-code #f (make-glil-const 1))
902 (emit-label MV)
903 (emit-code src (make-glil-mv-bind
904 (vars->bind-list
905 (append req (if rest (list rest) '()))
906 gensyms allocation self)
907 (and rest #t)))
908 (for-each (lambda (v)
909 (pmatch (hashq-ref (hashq-ref allocation v) self)
910 ((#t #f . ,n)
911 (emit-code src (make-glil-lexical #t #f 'set n)))
912 ((#t #t . ,n)
913 (emit-code src (make-glil-lexical #t #t 'box n)))
914 (,loc (error "bad let-values var allocation" x loc))))
915 (reverse gensyms))
916 (comp-tail body)
917 (clear-stack-slots context gensyms)
918 (emit-code #f (make-glil-unbind))))))
919
920 ;; much trickier than i thought this would be, at first, due to the need
921 ;; to have body's return value(s) on the stack while the unwinder runs,
922 ;; then proceed with returning or dropping or what-have-you, interacting
923 ;; with RA and MVRA. What have you, I say.
924 ((<dynwind> src winder pre body post unwinder)
925 (comp-push winder)
926 (comp-push unwinder)
927 (comp-drop pre)
928 (emit-code #f (make-glil-call 'wind 2))
929
930 (case context
931 ((tail)
932 (let ((MV (make-label)))
933 (comp-vals body MV)
934 ;; one value: unwind...
935 (emit-code #f (make-glil-call 'unwind 0))
936 (comp-drop post)
937 ;; ...and return the val
938 (emit-code #f (make-glil-call 'return 1))
939
940 (emit-label MV)
941 ;; multiple values: unwind...
942 (emit-code #f (make-glil-call 'unwind 0))
943 (comp-drop post)
944 ;; and return the values.
945 (emit-code #f (make-glil-call 'return/nvalues 1))))
946
947 ((push)
948 ;; we only want one value. so ask for one value
949 (comp-push body)
950 ;; and unwind, leaving the val on the stack
951 (emit-code #f (make-glil-call 'unwind 0))
952 (comp-drop post))
953
954 ((vals)
955 (let ((MV (make-label)))
956 (comp-vals body MV)
957 ;; one value: push 1 and fall through to MV case
958 (emit-code #f (make-glil-const 1))
959
960 (emit-label MV)
961 ;; multiple values: unwind...
962 (emit-code #f (make-glil-call 'unwind 0))
963 (comp-drop post)
964 ;; and goto the MVRA.
965 (emit-branch #f 'br MVRA)))
966
967 ((drop)
968 ;; compile body, discarding values. then unwind...
969 (comp-drop body)
970 (emit-code #f (make-glil-call 'unwind 0))
971 (comp-drop post)
972 ;; and fall through, or goto RA if there is one.
973 (if RA
974 (emit-branch #f 'br RA)))))
975
976 ((<dynlet> src fluids vals body)
977 (for-each comp-push fluids)
978 (for-each comp-push vals)
979 (emit-code #f (make-glil-call 'wind-fluids (length fluids)))
980
981 (case context
982 ((tail)
983 (let ((MV (make-label)))
984 ;; NB: in tail case, it is possible to preserve asymptotic tail
985 ;; recursion, via merging unwind-fluids structures -- but we'd need
986 ;; to compile in the body twice (once in tail context, assuming the
987 ;; caller unwinds, and once with this trampoline thing, unwinding
988 ;; ourselves).
989 (comp-vals body MV)
990 ;; one value: unwind and return
991 (emit-code #f (make-glil-call 'unwind-fluids 0))
992 (emit-code #f (make-glil-call 'return 1))
993
994 (emit-label MV)
995 ;; multiple values: unwind and return values
996 (emit-code #f (make-glil-call 'unwind-fluids 0))
997 (emit-code #f (make-glil-call 'return/nvalues 1))))
998
999 ((push)
1000 (comp-push body)
1001 (emit-code #f (make-glil-call 'unwind-fluids 0)))
1002
1003 ((vals)
1004 (let ((MV (make-label)))
1005 (comp-vals body MV)
1006 ;; one value: push 1 and fall through to MV case
1007 (emit-code #f (make-glil-const 1))
1008
1009 (emit-label MV)
1010 ;; multiple values: unwind and goto MVRA
1011 (emit-code #f (make-glil-call 'unwind-fluids 0))
1012 (emit-branch #f 'br MVRA)))
1013
1014 ((drop)
1015 ;; compile body, discarding values. then unwind...
1016 (comp-drop body)
1017 (emit-code #f (make-glil-call 'unwind-fluids 0))
1018 ;; and fall through, or goto RA if there is one.
1019 (if RA
1020 (emit-branch #f 'br RA)))))
1021
1022 ((<dynref> src fluid)
1023 (case context
1024 ((drop)
1025 (comp-drop fluid))
1026 ((push vals tail)
1027 (comp-push fluid)
1028 (emit-code #f (make-glil-call 'fluid-ref 1))))
1029 (maybe-emit-return))
1030
1031 ((<dynset> src fluid exp)
1032 (comp-push fluid)
1033 (comp-push exp)
1034 (emit-code #f (make-glil-call 'fluid-set 2))
1035 (case context
1036 ((push vals tail)
1037 (emit-code #f (make-glil-void))))
1038 (maybe-emit-return))
1039
1040 ;; What's the deal here? The deal is that we are compiling the start of a
1041 ;; delimited continuation. We try to avoid heap allocation in the normal
1042 ;; case; so the body is an expression, not a thunk, and we try to render
1043 ;; the handler inline. Also we did some analysis, in analyze.scm, so that
1044 ;; if the continuation isn't referenced, we don't reify it. This makes it
1045 ;; possible to implement catch and throw with delimited continuations,
1046 ;; without any overhead.
1047 ((<prompt> src tag body handler)
1048 (let ((H (make-label))
1049 (POST (make-label))
1050 (escape-only? (hashq-ref allocation x)))
1051 ;; First, set up the prompt.
1052 (comp-push tag)
1053 (emit-code src (make-glil-prompt H escape-only?))
1054
1055 ;; Then we compile the body, with its normal return path, unwinding
1056 ;; before proceeding.
1057 (case context
1058 ((tail)
1059 (let ((MV (make-label)))
1060 (comp-vals body MV)
1061 ;; one value: unwind and return
1062 (emit-code #f (make-glil-call 'unwind 0))
1063 (emit-code #f (make-glil-call 'return 1))
1064 ;; multiple values: unwind and return
1065 (emit-label MV)
1066 (emit-code #f (make-glil-call 'unwind 0))
1067 (emit-code #f (make-glil-call 'return/nvalues 1))))
1068
1069 ((push)
1070 ;; we only want one value. so ask for one value, unwind, and jump to
1071 ;; post
1072 (comp-push body)
1073 (emit-code #f (make-glil-call 'unwind 0))
1074 (emit-branch #f 'br (or RA POST)))
1075
1076 ((vals)
1077 (let ((MV (make-label)))
1078 (comp-vals body MV)
1079 ;; one value: push 1 and fall through to MV case
1080 (emit-code #f (make-glil-const 1))
1081 ;; multiple values: unwind and goto MVRA
1082 (emit-label MV)
1083 (emit-code #f (make-glil-call 'unwind 0))
1084 (emit-branch #f 'br MVRA)))
1085
1086 ((drop)
1087 ;; compile body, discarding values, then unwind & fall through.
1088 (comp-drop body)
1089 (emit-code #f (make-glil-call 'unwind 0))
1090 (emit-branch #f 'br (or RA POST))))
1091
1092 (emit-label H)
1093 ;; Now the handler. The stack is now made up of the continuation, and
1094 ;; then the args to the continuation (pushed separately), and then the
1095 ;; number of args, including the continuation.
1096 (record-case handler
1097 ((<lambda-case> req opt kw rest gensyms body alternate)
1098 (if (or opt kw alternate)
1099 (error "unexpected lambda-case in prompt" x))
1100 (emit-code src (make-glil-mv-bind
1101 (vars->bind-list
1102 (append req (if rest (list rest) '()))
1103 gensyms allocation self)
1104 (and rest #t)))
1105 (for-each (lambda (v)
1106 (pmatch (hashq-ref (hashq-ref allocation v) self)
1107 ((#t #f . ,n)
1108 (emit-code src (make-glil-lexical #t #f 'set n)))
1109 ((#t #t . ,n)
1110 (emit-code src (make-glil-lexical #t #t 'box n)))
1111 (,loc
1112 (error "bad prompt handler arg allocation" x loc))))
1113 (reverse gensyms))
1114 (comp-tail body)
1115 (emit-code #f (make-glil-unbind))))
1116
1117 (if (and (not RA)
1118 (or (eq? context 'push) (eq? context 'drop)))
1119 (emit-label POST))))
1120
1121 ((<abort> src tag args tail)
1122 (comp-push tag)
1123 (for-each comp-push args)
1124 (comp-push tail)
1125 (emit-code src (make-glil-call 'abort (length args)))
1126 ;; so, the abort can actually return. if it does, the values will be on
1127 ;; the stack, then the MV marker, just as in an MV context.
1128 (case context
1129 ((tail)
1130 ;; Return values.
1131 (emit-code #f (make-glil-call 'return/nvalues 1)))
1132 ((drop)
1133 ;; Drop all values and goto RA, or otherwise fall through.
1134 (emit-code #f (make-glil-mv-bind 0 #f))
1135 (if RA (emit-branch #f 'br RA)))
1136 ((push)
1137 ;; Truncate to one value.
1138 (emit-code #f (make-glil-mv-bind 1 #f)))
1139 ((vals)
1140 ;; Go to MVRA.
1141 (emit-branch #f 'br MVRA)))))))