1 ;;; TREE-IL -> GLIL compiler
3 ;; Copyright (C) 2001,2008,2009,2010 Free Software Foundation, Inc.
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.
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.
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
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 analyze)
31 #:use-module ((srfi srfi-1) #:select (filter-map))
32 #:export (compile-glil))
35 ;; sym -> {lambda -> address}
36 ;; lambda -> (labels . free-locs)
37 ;; lambda-case -> (gensym . nlocs)
39 ;; address ::= (local? boxed? . index)
40 ;; labels ::= ((sym . lambda) ...)
41 ;; free-locs ::= ((sym0 . address0) (sym1 . address1) ...)
42 ;; free variable addresses are relative to parent proc.
44 (define *comp-module* (make-fluid))
46 (define %warning-passes
47 `((unused-variable . ,unused-variable-analysis)
48 (unbound-variable . ,unbound-variable-analysis)
49 (arity-mismatch . ,arity-analysis)))
51 (define (compile-glil x e opts)
53 (or (and=> (memq #:warnings opts) cadr)
56 ;; Go through the warning passes.
57 (let ((analyses (filter-map (lambda (kind)
58 (assoc-ref %warning-passes kind))
60 (analyze-tree analyses x e))
62 (let* ((x (make-lambda (tree-il-src x) '()
63 (make-lambda-case #f '() #f #f #f '() '() x #f)))
64 (x (optimize! x e opts))
65 (allocation (analyze-lexicals x)))
67 (with-fluid* *comp-module* e
69 (values (flatten-lambda x #f allocation)
75 (define *primcall-ops* (make-hash-table))
77 (lambda (x) (hash-set! *primcall-ops* (car x) (cdr x)))
80 ((equal? . 2) . equal?)
92 ((quotient . 2) . quo)
93 ((remainder . 2) . rem)
96 ((logand . 2) . logand)
97 ((logior . 2) . logior)
98 ((logxor . 2) . logxor)
100 ((pair? . 1) . pair?)
104 ((set-car! . 2) . set-car!)
105 ((set-cdr! . 2) . set-cdr!)
106 ((null? . 1) . null?)
107 ((list? . 1) . list?)
110 ((class-of . 1) . class-of)
111 ((@slot-ref . 2) . slot-ref)
112 ((@slot-set! . 3) . slot-set)
113 ((vector-ref . 2) . vector-ref)
114 ((vector-set! . 3) . vector-set)
115 ((variable-ref . 1) . variable-ref)
116 ;; nb, *not* variable-set! -- the args are switched
117 ((variable-set . 2) . variable-set)
118 ((struct? . 1) . struct?)
119 ((struct-vtable . 1) . struct-vtable)
120 (make-struct . make-struct)
122 ;; hack for javascript
123 ((return . 1) return)
125 ((bytevector-u8-ref . 2) . bv-u8-ref)
126 ((bytevector-u8-set! . 3) . bv-u8-set)
127 ((bytevector-s8-ref . 2) . bv-s8-ref)
128 ((bytevector-s8-set! . 3) . bv-s8-set)
130 ((bytevector-u16-ref . 3) . bv-u16-ref)
131 ((bytevector-u16-set! . 4) . bv-u16-set)
132 ((bytevector-u16-native-ref . 2) . bv-u16-native-ref)
133 ((bytevector-u16-native-set! . 3) . bv-u16-native-set)
134 ((bytevector-s16-ref . 3) . bv-s16-ref)
135 ((bytevector-s16-set! . 4) . bv-s16-set)
136 ((bytevector-s16-native-ref . 2) . bv-s16-native-ref)
137 ((bytevector-s16-native-set! . 3) . bv-s16-native-set)
139 ((bytevector-u32-ref . 3) . bv-u32-ref)
140 ((bytevector-u32-set! . 4) . bv-u32-set)
141 ((bytevector-u32-native-ref . 2) . bv-u32-native-ref)
142 ((bytevector-u32-native-set! . 3) . bv-u32-native-set)
143 ((bytevector-s32-ref . 3) . bv-s32-ref)
144 ((bytevector-s32-set! . 4) . bv-s32-set)
145 ((bytevector-s32-native-ref . 2) . bv-s32-native-ref)
146 ((bytevector-s32-native-set! . 3) . bv-s32-native-set)
148 ((bytevector-u64-ref . 3) . bv-u64-ref)
149 ((bytevector-u64-set! . 4) . bv-u64-set)
150 ((bytevector-u64-native-ref . 2) . bv-u64-native-ref)
151 ((bytevector-u64-native-set! . 3) . bv-u64-native-set)
152 ((bytevector-s64-ref . 3) . bv-s64-ref)
153 ((bytevector-s64-set! . 4) . bv-s64-set)
154 ((bytevector-s64-native-ref . 2) . bv-s64-native-ref)
155 ((bytevector-s64-native-set! . 3) . bv-s64-native-set)
157 ((bytevector-ieee-single-ref . 3) . bv-f32-ref)
158 ((bytevector-ieee-single-set! . 4) . bv-f32-set)
159 ((bytevector-ieee-single-native-ref . 2) . bv-f32-native-ref)
160 ((bytevector-ieee-single-native-set! . 3) . bv-f32-native-set)
161 ((bytevector-ieee-double-ref . 3) . bv-f64-ref)
162 ((bytevector-ieee-double-set! . 4) . bv-f64-set)
163 ((bytevector-ieee-double-native-ref . 2) . bv-f64-native-ref)
164 ((bytevector-ieee-double-native-set! . 3) . bv-f64-native-set)))
169 (define (make-label) (gensym ":L"))
171 (define (vars->bind-list ids vars allocation proc)
173 (pmatch (hashq-ref (hashq-ref allocation v) proc)
176 (,x (error "badness" id v x))))
180 (define (emit-bindings src ids vars allocation proc emit-code)
181 (emit-code src (make-glil-bind
182 (vars->bind-list ids vars allocation proc))))
184 (define (with-output-to-code proc)
186 (define (emit-code src x)
187 (set! out (cons x out))
189 (set! out (cons (make-glil-source src) out))))
193 (define (flatten-lambda x self-label allocation)
195 ((<lambda> src meta body)
200 ;; write source info for proc
201 (if src (emit-code #f (make-glil-source src)))
202 ;; emit pre-prelude label for self tail calls in which the
203 ;; number of arguments doesn't check out at compile time
205 (emit-code #f (make-glil-label self-label)))
206 ;; compile the body, yo
207 (flatten body allocation x self-label (car (hashq-ref allocation x))
210 (define (flatten x allocation self self-label fix-labels emit-code)
211 (define (emit-label label)
212 (emit-code #f (make-glil-label label)))
213 (define (emit-branch src inst label)
214 (emit-code src (make-glil-branch inst label)))
216 ;; RA: "return address"; #f unless we're in a non-tail fix with labels
217 ;; MVRA: "multiple-values return address"; #f unless we're in a let-values
218 (let comp ((x x) (context 'tail) (RA #f) (MVRA #f))
219 (define (comp-tail tree) (comp tree context RA MVRA))
220 (define (comp-push tree) (comp tree 'push #f #f))
221 (define (comp-drop tree) (comp tree 'drop #f #f))
222 (define (comp-vals tree MVRA) (comp tree 'vals #f MVRA))
223 (define (comp-fix tree RA) (comp tree context RA MVRA))
225 ;; A couple of helpers. Note that if we are in tail context, we
227 (define (maybe-emit-return)
229 (emit-branch #f 'br RA)
230 (if (eq? context 'tail)
231 (emit-code #f (make-glil-call 'return 1)))))
237 (emit-code #f (make-glil-void))))
243 (emit-code src (make-glil-const exp))))
246 ;; FIXME: should represent sequence as exps tail
248 (let lp ((exps exps))
249 (if (null? (cdr exps))
250 (comp-tail (car exps))
252 (comp-drop (car exps))
255 ((<application> src proc args)
256 ;; FIXME: need a better pattern-matcher here
258 ((and (primitive-ref? proc)
259 (eq? (primitive-ref-name proc) '@apply)
260 (>= (length args) 1))
261 (let ((proc (car args))
264 ((and (primitive-ref? proc) (eq? (primitive-ref-name proc) 'values)
265 (not (eq? context 'push)) (not (eq? context 'vals)))
266 ;; tail: (lambda () (apply values '(1 2)))
267 ;; drop: (lambda () (apply values '(1 2)) 3)
268 ;; push: (lambda () (list (apply values '(10 12)) 1))
270 ((drop) (for-each comp-drop args) (maybe-emit-return))
272 (for-each comp-push args)
273 (emit-code src (make-glil-call 'return/values* (length args))))))
279 (for-each comp-push args)
280 (emit-code src (make-glil-call 'tail-apply (1+ (length args)))))
282 (emit-code src (make-glil-call 'new-frame 0))
284 (for-each comp-push args)
285 (emit-code src (make-glil-call 'apply (1+ (length args))))
289 (make-application src (make-primitive-ref #f 'apply)
294 ;; Well, shit. The proc might return any number of
295 ;; values (including 0), since it's in a drop context,
296 ;; yet apply does not create a MV continuation. So we
297 ;; mv-call out to our trampoline instead.
299 (make-application src (make-primitive-ref #f 'apply)
301 (maybe-emit-return)))))))
303 ((and (primitive-ref? proc) (eq? (primitive-ref-name proc) 'values)
304 (not (eq? context 'push)))
305 ;; tail: (lambda () (values '(1 2)))
306 ;; drop: (lambda () (values '(1 2)) 3)
307 ;; push: (lambda () (list (values '(10 12)) 1))
308 ;; vals: (let-values (((a b ...) (values 1 2 ...))) ...)
310 ((drop) (for-each comp-drop args) (maybe-emit-return))
312 (for-each comp-push args)
313 (emit-code #f (make-glil-const (length args)))
314 (emit-branch src 'br MVRA))
316 (for-each comp-push args)
317 (emit-code src (make-glil-call 'return/values (length args))))))
319 ((and (primitive-ref? proc)
320 (eq? (primitive-ref-name proc) '@call-with-values)
327 ;; MV: [tail-]call/nargs
328 ;; POST: (maybe-drop)
333 (make-application src (make-primitive-ref #f 'call-with-values)
338 (let ((MV (make-label)) (POST (make-label))
339 (producer (car args)) (consumer (cadr args)))
340 (if (not (eq? context 'tail))
341 (emit-code src (make-glil-call 'new-frame 0)))
343 (emit-code src (make-glil-call 'new-frame 0))
345 (emit-code src (make-glil-mv-call 0 MV))
347 ((tail) (emit-code src (make-glil-call 'tail-call 1)))
348 (else (emit-code src (make-glil-call 'call 1))
349 (emit-branch #f 'br POST)))
352 ((tail) (emit-code src (make-glil-call 'tail-call/nargs 0)))
353 (else (emit-code src (make-glil-call 'call/nargs 0))
355 (if (eq? context 'drop)
356 (emit-code #f (make-glil-call 'drop 1)))
357 (maybe-emit-return)))))))
359 ((and (primitive-ref? proc)
360 (eq? (primitive-ref-name proc) '@call-with-current-continuation)
364 (comp-push (car args))
365 (emit-code src (make-glil-call 'tail-call/cc 1)))
369 src (make-primitive-ref #f 'call-with-current-continuation)
374 (comp-push (car args))
375 (emit-code src (make-glil-call 'call/cc 1))
378 ;; Crap. Just like `apply' in drop context.
381 src (make-primitive-ref #f 'call-with-current-continuation)
383 (maybe-emit-return))))
385 ((and (primitive-ref? proc)
386 (or (hash-ref *primcall-ops*
387 (cons (primitive-ref-name proc) (length args)))
388 (hash-ref *primcall-ops* (primitive-ref-name proc))))
390 (for-each comp-push args)
391 (emit-code src (make-glil-call op (length args)))
392 (case (instruction-pushes op)
395 ((tail push vals) (emit-code #f (make-glil-void))))
399 ((drop) (emit-code #f (make-glil-call 'drop 1))))
402 (error "bad primitive op: too many pushes"
403 op (instruction-pushes op))))))
405 ;; self-call in tail position
406 ((and (lexical-ref? proc)
407 self-label (eq? (lexical-ref-gensym proc) self-label)
409 ;; first, evaluate new values, pushing them on the stack
410 (for-each comp-push args)
411 (let lp ((lcase (lambda-body self)))
413 ((and (lambda-case? lcase)
414 (not (lambda-case-kw lcase))
415 (not (lambda-case-opt lcase))
416 (not (lambda-case-rest lcase))
417 (= (length args) (length (lambda-case-req lcase))))
418 ;; we have a case that matches the args; rename variables
419 ;; and goto the case label
420 (for-each (lambda (sym)
421 (pmatch (hashq-ref (hashq-ref allocation sym) self)
422 ((#t #f . ,index) ; unboxed
423 (emit-code #f (make-glil-lexical #t #f 'set index)))
424 ((#t #t . ,index) ; boxed
426 (emit-code #f (make-glil-lexical #t #t 'box index)))
427 (,x (error "what" x))))
428 (reverse (lambda-case-vars lcase)))
429 (emit-branch src 'br (car (hashq-ref allocation lcase))))
430 ((lambda-case? lcase)
431 ;; no match, try next case
432 (lp (lambda-case-alternate lcase)))
434 ;; no cases left; shuffle args down and jump before the prelude.
435 (for-each (lambda (i)
436 (emit-code #f (make-glil-lexical #t #f 'set i)))
437 (reverse (iota (length args))))
438 (emit-branch src 'br self-label)))))
440 ;; lambda, the ultimate goto
441 ((and (lexical-ref? proc)
442 (assq (lexical-ref-gensym proc) fix-labels))
443 ;; like the self-tail-call case, though we can handle "drop"
444 ;; contexts too. first, evaluate new values, pushing them on
446 (for-each comp-push args)
447 ;; find the specific case, rename args, and goto the case label
448 (let lp ((lcase (lambda-body
449 (assq-ref fix-labels (lexical-ref-gensym proc)))))
451 ((and (lambda-case? lcase)
452 (not (lambda-case-kw lcase))
453 (not (lambda-case-opt lcase))
454 (not (lambda-case-rest lcase))
455 (= (length args) (length (lambda-case-req lcase))))
456 ;; we have a case that matches the args; rename variables
457 ;; and goto the case label
458 (for-each (lambda (sym)
459 (pmatch (hashq-ref (hashq-ref allocation sym) self)
460 ((#t #f . ,index) ; unboxed
461 (emit-code #f (make-glil-lexical #t #f 'set index)))
462 ((#t #t . ,index) ; boxed
463 (emit-code #f (make-glil-lexical #t #t 'box index)))
464 (,x (error "what" x))))
465 (reverse (lambda-case-vars lcase)))
466 (emit-branch src 'br (car (hashq-ref allocation lcase))))
467 ((lambda-case? lcase)
468 ;; no match, try next case
469 (lp (lambda-case-alternate lcase)))
471 ;; no cases left. we can't really handle this currently.
472 ;; ideally we would push on a new frame, then do a "local
473 ;; call" -- which doesn't require consing up a program
474 ;; object. but for now error, as this sort of case should
475 ;; preclude label allocation.
476 (error "couldn't find matching case for label call" x)))))
479 (if (not (eq? context 'tail))
480 (emit-code src (make-glil-call 'new-frame 0)))
482 (for-each comp-push args)
483 (let ((len (length args)))
485 ((tail) (emit-code src (make-glil-call 'tail-call len)))
486 ((push) (emit-code src (make-glil-call 'call len))
488 ((vals) (emit-code src (make-glil-mv-call len MVRA))
490 ((drop) (let ((MV (make-label)) (POST (make-label)))
491 (emit-code src (make-glil-mv-call len MV))
492 (emit-code #f (make-glil-call 'drop 1))
493 (emit-branch #f 'br (or RA POST))
495 (emit-code #f (make-glil-mv-bind '() #f))
496 (emit-code #f (make-glil-unbind))
498 (emit-branch #f 'br RA)
499 (emit-label POST)))))))))
501 ((<conditional> src test consequent alternate)
508 (let ((L1 (make-label)) (L2 (make-label)))
509 ;; need a pattern matcher
511 ((<application> proc args)
513 ((<primitive-ref> name)
514 (let ((len (length args)))
517 ((and (eq? name 'eq?) (= len 2))
518 (comp-push (car args))
519 (comp-push (cadr args))
520 (emit-branch src 'br-if-not-eq L1))
522 ((and (eq? name 'null?) (= len 1))
523 (comp-push (car args))
524 (emit-branch src 'br-if-not-null L1))
526 ((and (eq? name 'not) (= len 1))
527 (let ((app (car args)))
529 ((<application> proc args)
530 (let ((len (length args)))
532 ((<primitive-ref> name)
535 ((and (eq? name 'eq?) (= len 2))
536 (comp-push (car args))
537 (comp-push (cadr args))
538 (emit-branch src 'br-if-eq L1))
540 ((and (eq? name 'null?) (= len 1))
541 (comp-push (car args))
542 (emit-branch src 'br-if-null L1))
546 (emit-branch src 'br-if L1))))
549 (emit-branch src 'br-if L1)))))
552 (emit-branch src 'br-if L1)))))
556 (emit-branch src 'br-if-not L1)))))
559 (emit-branch src 'br-if-not L1))))
562 (emit-branch src 'br-if-not L1)))
564 (comp-tail consequent)
565 ;; if there is an RA, comp-tail will cause a jump to it -- just
566 ;; have to clean up here if there is no RA.
567 (if (and (not RA) (not (eq? context 'tail)))
568 (emit-branch #f 'br L2))
570 (comp-tail alternate)
571 (if (and (not RA) (not (eq? context 'tail)))
574 ((<primitive-ref> src name)
576 ((eq? (module-variable (fluid-ref *comp-module*) name)
577 (module-variable the-root-module name))
580 (emit-code src (make-glil-toplevel 'ref name))))
582 ((module-variable the-root-module name)
585 (emit-code src (make-glil-module 'ref '(guile) name #f))))
590 (emit-code src (make-glil-module
591 'ref (module-name (fluid-ref *comp-module*)) name #f))))
592 (maybe-emit-return))))
594 ((<lexical-ref> src gensym)
597 (pmatch (hashq-ref (hashq-ref allocation gensym) self)
598 ((,local? ,boxed? . ,index)
599 (emit-code src (make-glil-lexical local? boxed? 'ref index)))
601 (error "badness" x loc)))))
604 ((<lexical-set> src gensym exp)
606 (pmatch (hashq-ref (hashq-ref allocation gensym) self)
607 ((,local? ,boxed? . ,index)
608 (emit-code src (make-glil-lexical local? boxed? 'set index)))
610 (error "badness" x loc)))
613 (emit-code #f (make-glil-void))))
616 ((<module-ref> src mod name public?)
617 (emit-code src (make-glil-module 'ref mod name public?))
619 ((drop) (emit-code #f (make-glil-call 'drop 1))))
622 ((<module-set> src mod name public? exp)
624 (emit-code src (make-glil-module 'set mod name public?))
627 (emit-code #f (make-glil-void))))
630 ((<toplevel-ref> src name)
631 (emit-code src (make-glil-toplevel 'ref name))
633 ((drop) (emit-code #f (make-glil-call 'drop 1))))
636 ((<toplevel-set> src name exp)
638 (emit-code src (make-glil-toplevel 'set name))
641 (emit-code #f (make-glil-void))))
644 ((<toplevel-define> src name exp)
646 (emit-code src (make-glil-toplevel 'define name))
649 (emit-code #f (make-glil-void))))
653 (let ((free-locs (cdr (hashq-ref allocation x))))
656 (emit-code #f (flatten-lambda x #f allocation))
657 (if (not (null? free-locs))
662 ((,local? ,boxed? . ,n)
663 (emit-code #f (make-glil-lexical local? #f 'ref n)))
664 (else (error "what" x loc))))
666 (emit-code #f (make-glil-call 'vector (length free-locs)))
667 (emit-code #f (make-glil-call 'make-closure 2)))))))
670 ((<lambda-case> src req opt rest kw inits vars alternate body)
671 ;; o/~ feature on top of feature o/~
673 ;; opt := (name ...) | #f
675 ;; kw: (allow-other-keys? (keyword name var) ...) | #f
677 ;; init: tree-il in context of vars
678 ;; vars map to named arguments in the following order:
679 ;; required, optional (positional), rest, keyword.
680 (let* ((nreq (length req))
681 (nopt (if opt (length opt) 0))
682 (rest-idx (and rest (+ nreq nopt)))
683 (opt-names (or opt '()))
684 (allow-other-keys? (if kw (car kw) #f))
685 (kw-indices (map (lambda (x)
688 (cons key (list-index vars var)))
689 (else (error "bad kwarg" x))))
690 (if kw (cdr kw) '())))
691 (nargs (apply max (+ nreq nopt (if rest 1 0))
692 (map 1+ (map cdr kw-indices))))
693 (nlocs (cdr (hashq-ref allocation x)))
694 (alternate-label (and alternate (make-label))))
697 (+ nreq (length inits) (if rest 1 0)))
698 (error "something went wrong"
699 req opt rest kw inits vars nreq nopt kw-indices nargs))
700 ;; the prelude, to check args & reset the stack pointer,
701 ;; allowing room for locals
706 (make-glil-kw-prelude nreq nopt rest-idx kw-indices
707 allow-other-keys? nlocs alternate-label))
709 (make-glil-opt-prelude nreq nopt rest-idx nlocs alternate-label))
711 (make-glil-std-prelude nreq nlocs alternate-label))))
712 ;; box args if necessary
715 (pmatch (hashq-ref (hashq-ref allocation v) self)
717 (emit-code #f (make-glil-lexical #t #f 'ref n))
718 (emit-code #f (make-glil-lexical #t #t 'box n)))))
720 ;; write bindings info
721 (if (not (null? vars))
724 (let lp ((kw (if kw (cdr kw) '()))
725 (names (append (reverse opt-names) (reverse req)))
726 (vars (list-tail vars (+ nreq nopt
730 ;; fixme: check that vars is empty
731 (reverse (if rest (cons rest names) names)))
732 (((,key ,name ,var) . ,kw)
734 (lp kw (cons name names) (delq var vars))
736 (,kw (error "bad keywords, yo" kw))))
737 vars allocation self emit-code))
738 ;; init optional/kw args
739 (let lp ((inits inits) (n nreq) (vars (list-tail vars nreq)))
741 ((null? inits)) ; done
742 ((and rest-idx (= n rest-idx))
743 (lp inits (1+ n) (cdr vars)))
745 (pmatch (hashq-ref (hashq-ref allocation (car vars)) self)
746 ((#t ,boxed? . ,n*) (guard (= n* n))
747 (let ((L (make-label)))
748 (emit-code #f (make-glil-lexical #t boxed? 'bound? n))
749 (emit-code #f (make-glil-branch 'br-if L))
750 (comp-push (car inits))
751 (emit-code #f (make-glil-lexical #t boxed? 'set n))
753 (lp (cdr inits) (1+ n) (cdr vars))))
754 (#t (error "what" inits))))))
755 ;; post-prelude case label for label calls
756 (emit-label (car (hashq-ref allocation x)))
758 (if (not (null? vars))
759 (emit-code #f (make-glil-unbind)))
762 (emit-label alternate-label)
763 (comp-tail alternate)))))
765 ((<let> src names vars vals body)
766 (for-each comp-push vals)
767 (emit-bindings src names vars allocation self emit-code)
768 (for-each (lambda (v)
769 (pmatch (hashq-ref (hashq-ref allocation v) self)
771 (emit-code src (make-glil-lexical #t #f 'set n)))
773 (emit-code src (make-glil-lexical #t #t 'box n)))
774 (,loc (error "badness" x loc))))
777 (emit-code #f (make-glil-unbind)))
779 ((<letrec> src names vars vals body)
780 (for-each (lambda (v)
781 (pmatch (hashq-ref (hashq-ref allocation v) self)
783 (emit-code src (make-glil-lexical #t #t 'empty-box n)))
784 (,loc (error "badness" x loc))))
786 (for-each comp-push vals)
787 (emit-bindings src names vars allocation self emit-code)
788 (for-each (lambda (v)
789 (pmatch (hashq-ref (hashq-ref allocation v) self)
791 (emit-code src (make-glil-lexical #t #t 'set n)))
792 (,loc (error "badness" x loc))))
795 (emit-code #f (make-glil-unbind)))
797 ((<fix> src names vars vals body)
798 ;; The ideal here is to just render the lambda bodies inline, and
799 ;; wire the code together with gotos. We can do that if
800 ;; analyze-lexicals has determined that a given var has "label"
801 ;; allocation -- which is the case if it is in `fix-labels'.
803 ;; But even for closures that we can't inline, we can do some
804 ;; tricks to avoid heap-allocation for the binding itself. Since
805 ;; we know the vals are lambdas, we can set them to their local
806 ;; var slots first, then capture their bindings, mutating them in
808 (let ((new-RA (if (or (eq? context 'tail) RA) #f (make-label))))
812 ((hashq-ref allocation x)
813 ;; allocating a closure
814 (emit-code #f (flatten-lambda x v allocation))
815 (if (not (null? (cdr (hashq-ref allocation x))))
816 ;; Need to make-closure first, but with a temporary #f
817 ;; free-variables vector, so we are mutating fresh
818 ;; closures on the heap.
820 (emit-code #f (make-glil-const #f))
821 (emit-code #f (make-glil-call 'make-closure 2))))
822 (pmatch (hashq-ref (hashq-ref allocation v) self)
824 (emit-code src (make-glil-lexical #t #f 'set n)))
825 (,loc (error "badness" x loc))))
827 ;; labels allocation: emit label & body, but jump over it
828 (let ((POST (make-label)))
829 (emit-branch #f 'br POST)
830 (let lp ((lcase (lambda-body x)))
833 ((<lambda-case> src req vars body alternate)
834 (emit-label (car (hashq-ref allocation lcase)))
835 ;; FIXME: opt & kw args in the bindings
836 (emit-bindings #f req vars allocation self emit-code)
838 (emit-code #f (make-glil-source src)))
839 (comp-fix body (or RA new-RA))
840 (emit-code #f (make-glil-unbind))
842 (emit-label POST)))))))
845 ;; Emit bindings metadata for closures
846 (let ((binds (let lp ((out '()) (vars vars) (names names))
847 (cond ((null? vars) (reverse! out))
848 ((assq (car vars) fix-labels)
849 (lp out (cdr vars) (cdr names)))
851 (lp (acons (car vars) (car names) out)
852 (cdr vars) (cdr names)))))))
853 (emit-bindings src (map cdr binds) (map car binds)
854 allocation self emit-code))
855 ;; Now go back and fix up the bindings for closures.
858 (let ((free-locs (if (hashq-ref allocation x)
859 (cdr (hashq-ref allocation x))
860 ;; can hit this latter case for labels allocation
862 (if (not (null? free-locs))
867 ((,local? ,boxed? . ,n)
868 (emit-code #f (make-glil-lexical local? #f 'ref n)))
869 (else (error "what" x loc))))
871 (emit-code #f (make-glil-call 'vector (length free-locs)))
872 (pmatch (hashq-ref (hashq-ref allocation v) self)
874 (emit-code #f (make-glil-lexical #t #f 'fix n)))
875 (,loc (error "badness" x loc)))))))
881 (emit-code #f (make-glil-unbind))))
883 ((<let-values> src exp body)
885 ((<lambda-case> req opt kw rest vars body alternate)
886 (if (or opt kw alternate)
887 (error "unexpected lambda-case in let-values" x))
888 (let ((MV (make-label)))
890 (emit-code #f (make-glil-const 1))
892 (emit-code src (make-glil-mv-bind
894 (append req (if rest (list rest) '()))
895 vars allocation self)
897 (for-each (lambda (v)
898 (pmatch (hashq-ref (hashq-ref allocation v) self)
900 (emit-code src (make-glil-lexical #t #f 'set n)))
902 (emit-code src (make-glil-lexical #t #t 'box n)))
903 (,loc (error "badness" x loc))))
906 (emit-code #f (make-glil-unbind)))))))))