1 ;;; TREE-IL -> GLIL compiler
3 ;; Copyright (C) 2001,2008,2009 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)
100 ((set-car! . 2) . set-car!)
101 ((set-cdr! . 2) . set-cdr!)
102 ((null? . 1) . null?)
103 ((list? . 1) . list?)
106 ((class-of . 1) . class-of)
107 ((@slot-ref . 2) . slot-ref)
108 ((@slot-set! . 3) . slot-set)
109 ((vector-ref . 2) . vector-ref)
110 ((vector-set! . 3) . vector-set)
112 ;; hack for javascript
113 ((return . 1) return)
115 ((bytevector-u8-ref . 2) . bv-u8-ref)
116 ((bytevector-u8-set! . 3) . bv-u8-set)
117 ((bytevector-s8-ref . 2) . bv-s8-ref)
118 ((bytevector-s8-set! . 3) . bv-s8-set)
120 ((bytevector-u16-ref . 3) . bv-u16-ref)
121 ((bytevector-u16-set! . 4) . bv-u16-set)
122 ((bytevector-u16-native-ref . 2) . bv-u16-native-ref)
123 ((bytevector-u16-native-set! . 3) . bv-u16-native-set)
124 ((bytevector-s16-ref . 3) . bv-s16-ref)
125 ((bytevector-s16-set! . 4) . bv-s16-set)
126 ((bytevector-s16-native-ref . 2) . bv-s16-native-ref)
127 ((bytevector-s16-native-set! . 3) . bv-s16-native-set)
129 ((bytevector-u32-ref . 3) . bv-u32-ref)
130 ((bytevector-u32-set! . 4) . bv-u32-set)
131 ((bytevector-u32-native-ref . 2) . bv-u32-native-ref)
132 ((bytevector-u32-native-set! . 3) . bv-u32-native-set)
133 ((bytevector-s32-ref . 3) . bv-s32-ref)
134 ((bytevector-s32-set! . 4) . bv-s32-set)
135 ((bytevector-s32-native-ref . 2) . bv-s32-native-ref)
136 ((bytevector-s32-native-set! . 3) . bv-s32-native-set)
138 ((bytevector-u64-ref . 3) . bv-u64-ref)
139 ((bytevector-u64-set! . 4) . bv-u64-set)
140 ((bytevector-u64-native-ref . 2) . bv-u64-native-ref)
141 ((bytevector-u64-native-set! . 3) . bv-u64-native-set)
142 ((bytevector-s64-ref . 3) . bv-s64-ref)
143 ((bytevector-s64-set! . 4) . bv-s64-set)
144 ((bytevector-s64-native-ref . 2) . bv-s64-native-ref)
145 ((bytevector-s64-native-set! . 3) . bv-s64-native-set)
147 ((bytevector-ieee-single-ref . 3) . bv-f32-ref)
148 ((bytevector-ieee-single-set! . 4) . bv-f32-set)
149 ((bytevector-ieee-single-native-ref . 2) . bv-f32-native-ref)
150 ((bytevector-ieee-single-native-set! . 3) . bv-f32-native-set)
151 ((bytevector-ieee-double-ref . 3) . bv-f64-ref)
152 ((bytevector-ieee-double-set! . 4) . bv-f64-set)
153 ((bytevector-ieee-double-native-ref . 2) . bv-f64-native-ref)
154 ((bytevector-ieee-double-native-set! . 3) . bv-f64-native-set)))
159 (define (make-label) (gensym ":L"))
161 (define (vars->bind-list ids vars allocation proc)
163 (pmatch (hashq-ref (hashq-ref allocation v) proc)
166 (,x (error "badness" x))))
170 (define (emit-bindings src ids vars allocation proc emit-code)
171 (emit-code src (make-glil-bind
172 (vars->bind-list ids vars allocation proc))))
174 (define (with-output-to-code proc)
176 (define (emit-code src x)
177 (set! out (cons x out))
179 (set! out (cons (make-glil-source src) out))))
183 (define (flatten-lambda x self-label allocation)
185 ((<lambda> src meta body)
190 ;; write source info for proc
191 (if src (emit-code #f (make-glil-source src)))
192 ;; emit pre-prelude label for self tail calls in which the
193 ;; number of arguments doesn't check out at compile time
195 (emit-code #f (make-glil-label self-label)))
196 ;; compile the body, yo
197 (flatten body allocation x self-label (car (hashq-ref allocation x))
200 (define (flatten x allocation self self-label fix-labels emit-code)
201 (define (emit-label label)
202 (emit-code #f (make-glil-label label)))
203 (define (emit-branch src inst label)
204 (emit-code src (make-glil-branch inst label)))
206 ;; RA: "return address"; #f unless we're in a non-tail fix with labels
207 ;; MVRA: "multiple-values return address"; #f unless we're in a let-values
208 (let comp ((x x) (context 'tail) (RA #f) (MVRA #f))
209 (define (comp-tail tree) (comp tree context RA MVRA))
210 (define (comp-push tree) (comp tree 'push #f #f))
211 (define (comp-drop tree) (comp tree 'drop #f #f))
212 (define (comp-vals tree MVRA) (comp tree 'vals #f MVRA))
213 (define (comp-fix tree RA) (comp tree context RA MVRA))
215 ;; A couple of helpers. Note that if we are in tail context, we
217 (define (maybe-emit-return)
219 (emit-branch #f 'br RA)
220 (if (eq? context 'tail)
221 (emit-code #f (make-glil-call 'return 1)))))
227 (emit-code #f (make-glil-void))))
233 (emit-code src (make-glil-const exp))))
236 ;; FIXME: should represent sequence as exps tail
238 (let lp ((exps exps))
239 (if (null? (cdr exps))
240 (comp-tail (car exps))
242 (comp-drop (car exps))
245 ((<application> src proc args)
246 ;; FIXME: need a better pattern-matcher here
248 ((and (primitive-ref? proc)
249 (eq? (primitive-ref-name proc) '@apply)
250 (>= (length args) 1))
251 (let ((proc (car args))
254 ((and (primitive-ref? proc) (eq? (primitive-ref-name proc) 'values)
255 (not (eq? context 'push)) (not (eq? context 'vals)))
256 ;; tail: (lambda () (apply values '(1 2)))
257 ;; drop: (lambda () (apply values '(1 2)) 3)
258 ;; push: (lambda () (list (apply values '(10 12)) 1))
260 ((drop) (for-each comp-drop args) (maybe-emit-return))
262 (for-each comp-push args)
263 (emit-code src (make-glil-call 'return/values* (length args))))))
269 (for-each comp-push args)
270 (emit-code src (make-glil-call 'goto/apply (1+ (length args)))))
272 (emit-code src (make-glil-call 'new-frame 0))
274 (for-each comp-push args)
275 (emit-code src (make-glil-call 'apply (1+ (length args))))
279 (make-application src (make-primitive-ref #f 'apply)
284 ;; Well, shit. The proc might return any number of
285 ;; values (including 0), since it's in a drop context,
286 ;; yet apply does not create a MV continuation. So we
287 ;; mv-call out to our trampoline instead.
289 (make-application src (make-primitive-ref #f 'apply)
291 (maybe-emit-return)))))))
293 ((and (primitive-ref? proc) (eq? (primitive-ref-name proc) 'values)
294 (not (eq? context 'push)))
295 ;; tail: (lambda () (values '(1 2)))
296 ;; drop: (lambda () (values '(1 2)) 3)
297 ;; push: (lambda () (list (values '(10 12)) 1))
298 ;; vals: (let-values (((a b ...) (values 1 2 ...))) ...)
300 ((drop) (for-each comp-drop args) (maybe-emit-return))
302 (for-each comp-push args)
303 (emit-code #f (make-glil-const (length args)))
304 (emit-branch src 'br MVRA))
306 (for-each comp-push args)
307 (emit-code src (make-glil-call 'return/values (length args))))))
309 ((and (primitive-ref? proc)
310 (eq? (primitive-ref-name proc) '@call-with-values)
317 ;; MV: [tail-]call/nargs
318 ;; POST: (maybe-drop)
323 (make-application src (make-primitive-ref #f 'call-with-values)
328 (let ((MV (make-label)) (POST (make-label))
329 (producer (car args)) (consumer (cadr args)))
330 (if (not (eq? context 'tail))
331 (emit-code src (make-glil-call 'new-frame 0)))
333 (emit-code src (make-glil-call 'new-frame 0))
335 (emit-code src (make-glil-mv-call 0 MV))
337 ((tail) (emit-code src (make-glil-call 'goto/args 1)))
338 (else (emit-code src (make-glil-call 'call 1))
339 (emit-branch #f 'br POST)))
342 ((tail) (emit-code src (make-glil-call 'goto/nargs 0)))
343 (else (emit-code src (make-glil-call 'call/nargs 0))
345 (if (eq? context 'drop)
346 (emit-code #f (make-glil-call 'drop 1)))
347 (maybe-emit-return)))))))
349 ((and (primitive-ref? proc)
350 (eq? (primitive-ref-name proc) '@call-with-current-continuation)
354 (comp-push (car args))
355 (emit-code src (make-glil-call 'goto/cc 1)))
359 src (make-primitive-ref #f 'call-with-current-continuation)
364 (comp-push (car args))
365 (emit-code src (make-glil-call 'call/cc 1))
368 ;; Crap. Just like `apply' in drop context.
371 src (make-primitive-ref #f 'call-with-current-continuation)
373 (maybe-emit-return))))
375 ((and (primitive-ref? proc)
376 (or (hash-ref *primcall-ops*
377 (cons (primitive-ref-name proc) (length args)))
378 (hash-ref *primcall-ops* (primitive-ref-name proc))))
380 (for-each comp-push args)
381 (emit-code src (make-glil-call op (length args)))
382 (case (instruction-pushes op)
385 ((tail push vals) (emit-code #f (make-glil-void))))
389 ((drop) (emit-code #f (make-glil-call 'drop 1))))
392 (error "bad primitive op: too many pushes"
393 op (instruction-pushes op))))))
395 ;; self-call in tail position
396 ((and (lexical-ref? proc)
397 self-label (eq? (lexical-ref-gensym proc) self-label)
399 ;; first, evaluate new values, pushing them on the stack
400 (for-each comp-push args)
401 (let lp ((lcase (lambda-body self)))
403 ((and (lambda-case? lcase)
404 (not (lambda-case-kw lcase))
405 (not (lambda-case-opt lcase))
406 (not (lambda-case-rest lcase))
407 (= (length args) (length (lambda-case-req lcase))))
408 ;; we have a case that matches the args; rename variables
409 ;; and goto the case label
410 (for-each (lambda (sym)
411 (pmatch (hashq-ref (hashq-ref allocation sym) self)
412 ((#t #f . ,index) ; unboxed
413 (emit-code #f (make-glil-lexical #t #f 'set index)))
414 ((#t #t . ,index) ; boxed
416 (emit-code #f (make-glil-lexical #t #t 'box index)))
417 (,x (error "what" x))))
418 (reverse (lambda-case-vars lcase)))
419 (emit-branch src 'br (car (hashq-ref allocation lcase))))
420 ((lambda-case? lcase)
421 ;; no match, try next case
422 (lp (lambda-case-else lcase)))
424 ;; no cases left; shuffle args down and jump before the prelude.
425 (for-each (lambda (i)
426 (emit-code #f (make-glil-lexical #t #f 'set i)))
427 (reverse (iota (length args))))
428 (emit-branch src 'br self-label)))))
430 ;; lambda, the ultimate goto
431 ((and (lexical-ref? proc)
432 (assq (lexical-ref-gensym proc) fix-labels))
433 ;; like the self-tail-call case, though we can handle "drop"
434 ;; contexts too. first, evaluate new values, pushing them on
436 (for-each comp-push args)
437 ;; find the specific case, rename args, and goto the case label
438 (let lp ((lcase (lambda-body
439 (assq-ref fix-labels (lexical-ref-gensym proc)))))
441 ((and (lambda-case? lcase)
442 (not (lambda-case-kw lcase))
443 (not (lambda-case-opt lcase))
444 (not (lambda-case-rest lcase))
445 (= (length args) (length (lambda-case-req lcase))))
446 ;; we have a case that matches the args; rename variables
447 ;; and goto the case label
448 (for-each (lambda (sym)
449 (pmatch (hashq-ref (hashq-ref allocation sym) self)
450 ((#t #f . ,index) ; unboxed
451 (emit-code #f (make-glil-lexical #t #f 'set index)))
452 ((#t #t . ,index) ; boxed
453 (emit-code #f (make-glil-lexical #t #t 'box index)))
454 (,x (error "what" x))))
455 (reverse (lambda-case-vars lcase)))
456 (emit-branch src 'br (car (hashq-ref allocation lcase))))
457 ((lambda-case? lcase)
458 ;; no match, try next case
459 (lp (lambda-case-else lcase)))
461 ;; no cases left. we can't really handle this currently.
462 ;; ideally we would push on a new frame, then do a "local
463 ;; call" -- which doesn't require consing up a program
464 ;; object. but for now error, as this sort of case should
465 ;; preclude label allocation.
466 (error "couldn't find matching case for label call" x)))))
469 (if (not (eq? context 'tail))
470 (emit-code src (make-glil-call 'new-frame 0)))
472 (for-each comp-push args)
473 (let ((len (length args)))
475 ((tail) (emit-code src (make-glil-call 'goto/args len)))
476 ((push) (emit-code src (make-glil-call 'call len))
478 ((vals) (emit-code src (make-glil-mv-call len MVRA))
480 ((drop) (let ((MV (make-label)) (POST (make-label)))
481 (emit-code src (make-glil-mv-call len MV))
482 (emit-code #f (make-glil-call 'drop 1))
483 (emit-branch #f 'br (or RA POST))
485 (emit-code #f (make-glil-mv-bind '() #f))
486 (emit-code #f (make-glil-unbind))
488 (emit-branch #f 'br RA)
489 (emit-label POST)))))))))
491 ((<conditional> src test then (alternate else))
498 (let ((L1 (make-label)) (L2 (make-label)))
499 ;; need a pattern matcher
501 ((<application> proc args)
503 ((<primitive-ref> name)
504 (let ((len (length args)))
507 ((and (eq? name 'eq?) (= len 2))
508 (comp-push (car args))
509 (comp-push (cadr args))
510 (emit-branch src 'br-if-not-eq L1))
512 ((and (eq? name 'null?) (= len 1))
513 (comp-push (car args))
514 (emit-branch src 'br-if-not-null L1))
516 ((and (eq? name 'not) (= len 1))
517 (let ((app (car args)))
519 ((<application> proc args)
520 (let ((len (length args)))
522 ((<primitive-ref> name)
525 ((and (eq? name 'eq?) (= len 2))
526 (comp-push (car args))
527 (comp-push (cadr args))
528 (emit-branch src 'br-if-eq L1))
530 ((and (eq? name 'null?) (= len 1))
531 (comp-push (car args))
532 (emit-branch src 'br-if-null L1))
536 (emit-branch src 'br-if L1))))
539 (emit-branch src 'br-if L1)))))
542 (emit-branch src 'br-if L1)))))
546 (emit-branch src 'br-if-not L1)))))
549 (emit-branch src 'br-if-not L1))))
552 (emit-branch src 'br-if-not L1)))
555 ;; if there is an RA, comp-tail will cause a jump to it -- just
556 ;; have to clean up here if there is no RA.
557 (if (and (not RA) (not (eq? context 'tail)))
558 (emit-branch #f 'br L2))
560 (comp-tail alternate)
561 (if (and (not RA) (not (eq? context 'tail)))
564 ((<primitive-ref> src name)
566 ((eq? (module-variable (fluid-ref *comp-module*) name)
567 (module-variable the-root-module name))
570 (emit-code src (make-glil-toplevel 'ref name))))
572 ((module-variable the-root-module name)
575 (emit-code src (make-glil-module 'ref '(guile) name #f))))
580 (emit-code src (make-glil-module
581 'ref (module-name (fluid-ref *comp-module*)) name #f))))
582 (maybe-emit-return))))
584 ((<lexical-ref> src gensym)
587 (pmatch (hashq-ref (hashq-ref allocation gensym) self)
588 ((,local? ,boxed? . ,index)
589 (emit-code src (make-glil-lexical local? boxed? 'ref index)))
591 (error "badness" x loc)))))
594 ((<lexical-set> src gensym exp)
596 (pmatch (hashq-ref (hashq-ref allocation gensym) self)
597 ((,local? ,boxed? . ,index)
598 (emit-code src (make-glil-lexical local? boxed? 'set index)))
600 (error "badness" x loc)))
603 (emit-code #f (make-glil-void))))
606 ((<module-ref> src mod name public?)
607 (emit-code src (make-glil-module 'ref mod name public?))
609 ((drop) (emit-code #f (make-glil-call 'drop 1))))
612 ((<module-set> src mod name public? exp)
614 (emit-code src (make-glil-module 'set mod name public?))
617 (emit-code #f (make-glil-void))))
620 ((<toplevel-ref> src name)
621 (emit-code src (make-glil-toplevel 'ref name))
623 ((drop) (emit-code #f (make-glil-call 'drop 1))))
626 ((<toplevel-set> src name exp)
628 (emit-code src (make-glil-toplevel 'set name))
631 (emit-code #f (make-glil-void))))
634 ((<toplevel-define> src name exp)
636 (emit-code src (make-glil-toplevel 'define name))
639 (emit-code #f (make-glil-void))))
643 (let ((free-locs (cdr (hashq-ref allocation x))))
646 (emit-code #f (flatten-lambda x #f allocation))
647 (if (not (null? free-locs))
652 ((,local? ,boxed? . ,n)
653 (emit-code #f (make-glil-lexical local? #f 'ref n)))
654 (else (error "what" x loc))))
656 (emit-code #f (make-glil-call 'vector (length free-locs)))
657 (emit-code #f (make-glil-call 'make-closure 2)))))))
660 ((<lambda-case> src req opt rest kw inits vars else body)
661 ;; o/~ feature on top of feature o/~
663 ;; opt := (name ...) | #f
665 ;; kw: (allow-other-keys? (keyword name var) ...) | #f
667 ;; init: tree-il in context of vars
668 ;; vars map to named arguments in the following order:
669 ;; required, optional (positional), rest, keyword.
670 (let* ((nreq (length req))
671 (nopt (if opt (length opt) 0))
672 (rest-idx (and rest (+ nreq nopt)))
673 (opt-names (or opt '()))
674 (allow-other-keys? (if kw (car kw) #f))
675 (kw-indices (map (lambda (x)
678 (cons key (list-index vars var)))
679 (else (error "bad kwarg" x))))
680 (if kw (cdr kw) '())))
681 (nargs (apply max (+ nreq nopt (if rest 1 0))
682 (map 1+ (map cdr kw-indices))))
683 (nlocs (cdr (hashq-ref allocation x)))
684 (else-label (and else (make-label))))
687 (+ nreq (length inits) (if rest 1 0)))
688 (error "something went wrong"
689 req opt rest kw inits vars nreq nopt kw-indices nargs))
690 ;; the prelude, to check args & reset the stack pointer,
691 ;; allowing room for locals
696 (make-glil-kw-prelude nreq nopt rest-idx kw-indices
697 allow-other-keys? nlocs else-label))
699 (make-glil-opt-prelude nreq nopt rest-idx nlocs else-label))
701 (make-glil-std-prelude nreq nlocs else-label))))
702 ;; box args if necessary
705 (pmatch (hashq-ref (hashq-ref allocation v) self)
707 (emit-code #f (make-glil-lexical #t #f 'ref n))
708 (emit-code #f (make-glil-lexical #t #t 'box n)))))
710 ;; write bindings info
711 (if (not (null? vars))
714 (let lp ((kw (if kw (cdr kw) '()))
715 (names (append (reverse opt-names) (reverse req)))
716 (vars (list-tail vars (+ nreq nopt
720 ;; fixme: check that vars is empty
721 (reverse (if rest (cons rest names) names)))
722 (((,key ,name ,var) . ,kw)
724 (lp kw (cons name names) (delq var vars))
726 (,kw (error "bad keywords, yo" kw))))
727 vars allocation self emit-code))
728 ;; init optional/kw args
729 (let lp ((inits inits) (n nreq) (vars (list-tail vars nreq)))
731 ((null? inits)) ; done
732 ((and rest-idx (= n rest-idx))
733 (lp inits (1+ n) (cdr vars)))
735 (pmatch (hashq-ref (hashq-ref allocation (car vars)) self)
736 ((#t ,boxed? . ,n*) (guard (= n* n))
737 (let ((L (make-label)))
738 (emit-code #f (make-glil-lexical #t boxed? 'bound? n))
739 (emit-code #f (make-glil-branch 'br-if L))
740 (comp-push (car inits))
741 (emit-code #f (make-glil-lexical #t boxed? 'set n))
743 (lp (cdr inits) (1+ n) (cdr vars))))
744 (#t (error "what" inits))))))
745 ;; post-prelude case label for label calls
746 (emit-label (car (hashq-ref allocation x)))
748 (if (not (null? vars))
749 (emit-code #f (make-glil-unbind)))
752 (emit-label else-label)
755 ((<let> src names vars vals body)
756 (for-each comp-push vals)
757 (emit-bindings src names vars allocation self emit-code)
758 (for-each (lambda (v)
759 (pmatch (hashq-ref (hashq-ref allocation v) self)
761 (emit-code src (make-glil-lexical #t #f 'set n)))
763 (emit-code src (make-glil-lexical #t #t 'box n)))
764 (,loc (error "badness" x loc))))
767 (emit-code #f (make-glil-unbind)))
769 ((<letrec> src names vars vals body)
770 (for-each (lambda (v)
771 (pmatch (hashq-ref (hashq-ref allocation v) self)
773 (emit-code src (make-glil-lexical #t #t 'empty-box n)))
774 (,loc (error "badness" x loc))))
776 (for-each comp-push vals)
777 (emit-bindings src names vars allocation self emit-code)
778 (for-each (lambda (v)
779 (pmatch (hashq-ref (hashq-ref allocation v) self)
781 (emit-code src (make-glil-lexical #t #t 'set n)))
782 (,loc (error "badness" x loc))))
785 (emit-code #f (make-glil-unbind)))
787 ((<fix> src names vars vals body)
788 ;; The ideal here is to just render the lambda bodies inline, and
789 ;; wire the code together with gotos. We can do that if
790 ;; analyze-lexicals has determined that a given var has "label"
791 ;; allocation -- which is the case if it is in `fix-labels'.
793 ;; But even for closures that we can't inline, we can do some
794 ;; tricks to avoid heap-allocation for the binding itself. Since
795 ;; we know the vals are lambdas, we can set them to their local
796 ;; var slots first, then capture their bindings, mutating them in
798 (let ((new-RA (if (or (eq? context 'tail) RA) #f (make-label))))
802 ((hashq-ref allocation x)
803 ;; allocating a closure
804 (emit-code #f (flatten-lambda x v allocation))
805 (if (not (null? (cdr (hashq-ref allocation x))))
806 ;; Need to make-closure first, but with a temporary #f
807 ;; free-variables vector, so we are mutating fresh
808 ;; closures on the heap.
810 (emit-code #f (make-glil-const #f))
811 (emit-code #f (make-glil-call 'make-closure 2))))
812 (pmatch (hashq-ref (hashq-ref allocation v) self)
814 (emit-code src (make-glil-lexical #t #f 'set n)))
815 (,loc (error "badness" x loc))))
817 ;; labels allocation: emit label & body, but jump over it
818 (let ((POST (make-label)))
819 (emit-branch #f 'br POST)
820 (let lp ((lcase (lambda-body x)))
823 ((<lambda-case> src req vars body else)
824 (emit-label (car (hashq-ref allocation lcase)))
825 ;; FIXME: opt & kw args in the bindings
826 (emit-bindings #f req vars allocation self emit-code)
828 (emit-code #f (make-glil-source src)))
829 (comp-fix body (or RA new-RA))
830 (emit-code #f (make-glil-unbind))
832 (emit-label POST)))))))
835 ;; Emit bindings metadata for closures
836 (let ((binds (let lp ((out '()) (vars vars) (names names))
837 (cond ((null? vars) (reverse! out))
838 ((assq (car vars) fix-labels)
839 (lp out (cdr vars) (cdr names)))
841 (lp (acons (car vars) (car names) out)
842 (cdr vars) (cdr names)))))))
843 (emit-bindings src (map cdr binds) (map car binds)
844 allocation self emit-code))
845 ;; Now go back and fix up the bindings for closures.
848 (let ((free-locs (if (hashq-ref allocation x)
849 (cdr (hashq-ref allocation x))
850 ;; can hit this latter case for labels allocation
852 (if (not (null? free-locs))
857 ((,local? ,boxed? . ,n)
858 (emit-code #f (make-glil-lexical local? #f 'ref n)))
859 (else (error "what" x loc))))
861 (emit-code #f (make-glil-call 'vector (length free-locs)))
862 (pmatch (hashq-ref (hashq-ref allocation v) self)
864 (emit-code #f (make-glil-lexical #t #f 'fix n)))
865 (,loc (error "badness" x loc)))))))
871 (emit-code #f (make-glil-unbind))))
873 ((<let-values> src exp body)
875 ((<lambda-case> req opt kw rest vars body else)
877 (error "unexpected lambda-case in let-values" x))
878 (let ((MV (make-label)))
880 (emit-code #f (make-glil-const 1))
882 (emit-code src (make-glil-mv-bind
884 (append req (if rest (list rest) '()))
885 vars allocation self)
887 (for-each (lambda (v)
888 (pmatch (hashq-ref (hashq-ref allocation v) self)
890 (emit-code src (make-glil-lexical #t #f 'set n)))
892 (emit-code src (make-glil-lexical #t #t 'box n)))
893 (,loc (error "badness" x loc))))
896 (emit-code #f (make-glil-unbind)))))))))