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