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 #:export (compile-glil))
34 ;; sym -> {lambda -> address}
35 ;; lambda -> (nlocs labels . free-locs)
37 ;; address := (local? boxed? . index)
38 ;; free-locs ::= ((sym0 . address0) (sym1 . address1) ...)
39 ;; free variable addresses are relative to parent proc.
41 (define *comp-module* (make-fluid))
43 (define %warning-passes
44 `((unused-variable . ,report-unused-variables)
45 (unbound-variable . ,report-possibly-unbound-variables)))
47 (define (compile-glil x e opts)
49 (or (and=> (memq #:warnings opts) cadr)
52 ;; Go through the warning passes.
53 (for-each (lambda (kind)
54 (let ((warn (assoc-ref %warning-passes kind)))
55 (and (procedure? warn)
59 (let* ((x (make-lambda (tree-il-src x) '() '() '() x))
60 (x (optimize! x e opts))
61 (allocation (analyze-lexicals x)))
63 (with-fluid* *comp-module* e
65 (values (flatten-lambda x #f allocation)
71 (define *primcall-ops* (make-hash-table))
73 (lambda (x) (hash-set! *primcall-ops* (car x) (cdr x)))
76 ((equal? . 2) . equal?)
88 ((quotient . 2) . quo)
89 ((remainder . 2) . rem)
96 ((set-car! . 2) . set-car!)
97 ((set-cdr! . 2) . set-cdr!)
102 ((@slot-ref . 2) . slot-ref)
103 ((@slot-set! . 3) . slot-set)
104 ((vector-ref . 2) . vector-ref)
105 ((vector-set! . 3) . vector-set)
107 ((bytevector-u8-ref . 2) . bv-u8-ref)
108 ((bytevector-u8-set! . 3) . bv-u8-set)
109 ((bytevector-s8-ref . 2) . bv-s8-ref)
110 ((bytevector-s8-set! . 3) . bv-s8-set)
112 ((bytevector-u16-ref . 3) . bv-u16-ref)
113 ((bytevector-u16-set! . 4) . bv-u16-set)
114 ((bytevector-u16-native-ref . 2) . bv-u16-native-ref)
115 ((bytevector-u16-native-set! . 3) . bv-u16-native-set)
116 ((bytevector-s16-ref . 3) . bv-s16-ref)
117 ((bytevector-s16-set! . 4) . bv-s16-set)
118 ((bytevector-s16-native-ref . 2) . bv-s16-native-ref)
119 ((bytevector-s16-native-set! . 3) . bv-s16-native-set)
121 ((bytevector-u32-ref . 3) . bv-u32-ref)
122 ((bytevector-u32-set! . 4) . bv-u32-set)
123 ((bytevector-u32-native-ref . 2) . bv-u32-native-ref)
124 ((bytevector-u32-native-set! . 3) . bv-u32-native-set)
125 ((bytevector-s32-ref . 3) . bv-s32-ref)
126 ((bytevector-s32-set! . 4) . bv-s32-set)
127 ((bytevector-s32-native-ref . 2) . bv-s32-native-ref)
128 ((bytevector-s32-native-set! . 3) . bv-s32-native-set)
130 ((bytevector-u64-ref . 3) . bv-u64-ref)
131 ((bytevector-u64-set! . 4) . bv-u64-set)
132 ((bytevector-u64-native-ref . 2) . bv-u64-native-ref)
133 ((bytevector-u64-native-set! . 3) . bv-u64-native-set)
134 ((bytevector-s64-ref . 3) . bv-s64-ref)
135 ((bytevector-s64-set! . 4) . bv-s64-set)
136 ((bytevector-s64-native-ref . 2) . bv-s64-native-ref)
137 ((bytevector-s64-native-set! . 3) . bv-s64-native-set)
139 ((bytevector-ieee-single-ref . 3) . bv-f32-ref)
140 ((bytevector-ieee-single-set! . 4) . bv-f32-set)
141 ((bytevector-ieee-single-native-ref . 2) . bv-f32-native-ref)
142 ((bytevector-ieee-single-native-set! . 3) . bv-f32-native-set)
143 ((bytevector-ieee-double-ref . 3) . bv-f64-ref)
144 ((bytevector-ieee-double-set! . 4) . bv-f64-set)
145 ((bytevector-ieee-double-native-ref . 2) . bv-f64-native-ref)
146 ((bytevector-ieee-double-native-set! . 3) . bv-f64-native-set)))
151 (define (make-label) (gensym ":L"))
153 (define (vars->bind-list ids vars allocation proc)
155 (pmatch (hashq-ref (hashq-ref allocation v) proc)
158 (,x (error "badness" x))))
162 (define (emit-bindings src ids vars allocation proc emit-code)
163 (emit-code src (make-glil-bind
164 (vars->bind-list ids vars allocation proc))))
166 (define (with-output-to-code proc)
168 (define (emit-code src x)
169 (set! out (cons x out))
171 (set! out (cons (make-glil-source src) out))))
175 (define (flatten-lambda x self-label allocation)
176 (receive (ids vars nargs nrest)
177 (let lp ((ids (lambda-names x)) (vars (lambda-vars x))
178 (oids '()) (ovars '()) (n 0))
179 (cond ((null? vars) (values (reverse oids) (reverse ovars) n 0))
180 ((pair? vars) (lp (cdr ids) (cdr vars)
181 (cons (car ids) oids) (cons (car vars) ovars)
183 (else (values (reverse (cons ids oids))
184 (reverse (cons vars ovars))
186 (let ((nlocs (car (hashq-ref allocation x)))
187 (labels (cadr (hashq-ref allocation x))))
189 nargs nrest nlocs (lambda-meta x)
192 ;; emit label for self tail calls
194 (emit-code #f (make-glil-label self-label)))
195 ;; write bindings and source debugging info
196 (if (not (null? ids))
197 (emit-bindings #f ids vars allocation x emit-code))
199 (emit-code #f (make-glil-source (lambda-src x))))
200 ;; box args if necessary
203 (pmatch (hashq-ref (hashq-ref allocation v) x)
205 (emit-code #f (make-glil-lexical #t #f 'ref n))
206 (emit-code #f (make-glil-lexical #t #t 'box n)))))
208 ;; and here, here, dear reader: we compile.
209 (flatten (lambda-body x) allocation x self-label
210 labels emit-code)))))))
212 (define (flatten x allocation self self-label fix-labels emit-code)
213 (define (emit-label label)
214 (emit-code #f (make-glil-label label)))
215 (define (emit-branch src inst label)
216 (emit-code src (make-glil-branch inst label)))
218 ;; RA: "return address"; #f unless we're in a non-tail fix with labels
219 ;; MVRA: "multiple-values return address"; #f unless we're in a let-values
220 (let comp ((x x) (context 'tail) (RA #f) (MVRA #f))
221 (define (comp-tail tree) (comp tree context RA MVRA))
222 (define (comp-push tree) (comp tree 'push #f #f))
223 (define (comp-drop tree) (comp tree 'drop #f #f))
224 (define (comp-vals tree MVRA) (comp tree 'vals #f MVRA))
225 (define (comp-fix tree RA) (comp tree context RA MVRA))
227 ;; A couple of helpers. Note that if we are in tail context, we
229 (define (maybe-emit-return)
231 (emit-branch #f 'br RA)
232 (if (eq? context 'tail)
233 (emit-code #f (make-glil-call 'return 1)))))
239 (emit-code #f (make-glil-void))))
245 (emit-code src (make-glil-const exp))))
248 ;; FIXME: should represent sequence as exps tail
250 (let lp ((exps exps))
251 (if (null? (cdr exps))
252 (comp-tail (car exps))
254 (comp-drop (car exps))
257 ((<application> src proc args)
258 ;; FIXME: need a better pattern-matcher here
260 ((and (primitive-ref? proc)
261 (eq? (primitive-ref-name proc) '@apply)
262 (>= (length args) 1))
263 (let ((proc (car args))
266 ((and (primitive-ref? proc) (eq? (primitive-ref-name proc) 'values)
267 (not (eq? context 'push)) (not (eq? context 'vals)))
268 ;; tail: (lambda () (apply values '(1 2)))
269 ;; drop: (lambda () (apply values '(1 2)) 3)
270 ;; push: (lambda () (list (apply values '(10 12)) 1))
272 ((drop) (for-each comp-drop args) (maybe-emit-return))
274 (for-each comp-push args)
275 (emit-code src (make-glil-call 'return/values* (length args))))))
281 (for-each comp-push args)
282 (emit-code src (make-glil-call 'goto/apply (1+ (length args)))))
284 (emit-code src (make-glil-call 'new-frame 0))
286 (for-each comp-push args)
287 (emit-code src (make-glil-call 'apply (1+ (length args))))
291 (make-application src (make-primitive-ref #f 'apply)
296 ;; Well, shit. The proc might return any number of
297 ;; values (including 0), since it's in a drop context,
298 ;; yet apply does not create a MV continuation. So we
299 ;; mv-call out to our trampoline instead.
301 (make-application src (make-primitive-ref #f 'apply)
303 (maybe-emit-return)))))))
305 ((and (primitive-ref? proc) (eq? (primitive-ref-name proc) 'values)
306 (not (eq? context 'push)))
307 ;; tail: (lambda () (values '(1 2)))
308 ;; drop: (lambda () (values '(1 2)) 3)
309 ;; push: (lambda () (list (values '(10 12)) 1))
310 ;; vals: (let-values (((a b ...) (values 1 2 ...))) ...)
312 ((drop) (for-each comp-drop args) (maybe-emit-return))
314 (for-each comp-push args)
315 (emit-code #f (make-glil-const (length args)))
316 (emit-branch src 'br MVRA))
318 (for-each comp-push args)
319 (emit-code src (make-glil-call 'return/values (length args))))))
321 ((and (primitive-ref? proc)
322 (eq? (primitive-ref-name proc) '@call-with-values)
329 ;; MV: [tail-]call/nargs
330 ;; POST: (maybe-drop)
335 (make-application src (make-primitive-ref #f 'call-with-values)
340 (let ((MV (make-label)) (POST (make-label))
341 (producer (car args)) (consumer (cadr args)))
342 (if (not (eq? context 'tail))
343 (emit-code src (make-glil-call 'new-frame 0)))
345 (emit-code src (make-glil-call 'new-frame 0))
347 (emit-code src (make-glil-mv-call 0 MV))
349 ((tail) (emit-code src (make-glil-call 'goto/args 1)))
350 (else (emit-code src (make-glil-call 'call 1))
351 (emit-branch #f 'br POST)))
354 ((tail) (emit-code src (make-glil-call 'goto/nargs 0)))
355 (else (emit-code src (make-glil-call 'call/nargs 0))
357 (if (eq? context 'drop)
358 (emit-code #f (make-glil-call 'drop 1)))
359 (maybe-emit-return)))))))
361 ((and (primitive-ref? proc)
362 (eq? (primitive-ref-name proc) '@call-with-current-continuation)
366 (comp-push (car args))
367 (emit-code src (make-glil-call 'goto/cc 1)))
371 src (make-primitive-ref #f 'call-with-current-continuation)
376 (comp-push (car args))
377 (emit-code src (make-glil-call 'call/cc 1))
380 ;; Crap. Just like `apply' in drop context.
383 src (make-primitive-ref #f 'call-with-current-continuation)
385 (maybe-emit-return))))
387 ((and (primitive-ref? proc)
388 (or (hash-ref *primcall-ops*
389 (cons (primitive-ref-name proc) (length args)))
390 (hash-ref *primcall-ops* (primitive-ref-name proc))))
392 (for-each comp-push args)
393 (emit-code src (make-glil-call op (length args)))
394 (case (instruction-pushes op)
397 ((tail push vals) (emit-code #f (make-glil-void))))
401 ((drop) (emit-code #f (make-glil-call 'drop 1))))
404 (error "bad primitive op: too many pushes"
405 op (instruction-pushes op))))))
408 ((and (lexical-ref? proc)
409 self-label (eq? (lexical-ref-gensym proc) self-label)
410 ;; self-call in tail position is a goto
412 ;; make sure the arity is right
413 (list? (lambda-vars self))
414 (= (length args) (length (lambda-vars self))))
415 ;; evaluate new values
416 (for-each comp-push args)
418 (for-each (lambda (sym)
419 (pmatch (hashq-ref (hashq-ref allocation sym) self)
420 ((#t ,boxed? . ,index)
421 ;; set unboxed, as the proc prelude will box if needed
422 (emit-code #f (make-glil-lexical #t #f 'set index)))
423 (,x (error "what" x))))
424 (reverse (lambda-vars self)))
425 (emit-branch src 'br self-label))
427 ;; lambda, the ultimate goto
428 ((and (lexical-ref? proc)
429 (assq (lexical-ref-gensym proc) fix-labels))
430 ;; evaluate new values, assuming that analyze-lexicals did its
431 ;; job, and that the arity was right
432 (for-each comp-push args)
434 (for-each (lambda (sym)
435 (pmatch (hashq-ref (hashq-ref allocation sym) self)
437 (emit-code #f (make-glil-lexical #t #f 'set index)))
439 (emit-code #f (make-glil-lexical #t #t 'box index)))
440 (,x (error "what" x))))
441 (reverse (assq-ref fix-labels (lexical-ref-gensym proc))))
443 (emit-branch src 'br (lexical-ref-gensym proc)))
446 (if (not (eq? context 'tail))
447 (emit-code src (make-glil-call 'new-frame 0)))
449 (for-each comp-push args)
450 (let ((len (length args)))
452 ((tail) (emit-code src (make-glil-call 'goto/args len)))
453 ((push) (emit-code src (make-glil-call 'call len))
455 ((vals) (emit-code src (make-glil-mv-call len MVRA))
457 ((drop) (let ((MV (make-label)) (POST (make-label)))
458 (emit-code src (make-glil-mv-call len MV))
459 (emit-code #f (make-glil-call 'drop 1))
460 (emit-branch #f 'br (or RA POST))
462 (emit-code #f (make-glil-mv-bind '() #f))
463 (emit-code #f (make-glil-unbind))
465 (emit-branch #f 'br RA)
466 (emit-label POST)))))))))
468 ((<conditional> src test then else)
475 (let ((L1 (make-label)) (L2 (make-label)))
477 (emit-branch src 'br-if-not L1)
479 ;; if there is an RA, comp-tail will cause a jump to it -- just
480 ;; have to clean up here if there is no RA.
481 (if (and (not RA) (not (eq? context 'tail)))
482 (emit-branch #f 'br L2))
485 (if (and (not RA) (not (eq? context 'tail)))
488 ((<primitive-ref> src name)
490 ((eq? (module-variable (fluid-ref *comp-module*) name)
491 (module-variable the-root-module name))
494 (emit-code src (make-glil-toplevel 'ref name))))
496 ((module-variable the-root-module name)
499 (emit-code src (make-glil-module 'ref '(guile) name #f))))
504 (emit-code src (make-glil-module
505 'ref (module-name (fluid-ref *comp-module*)) name #f))))
506 (maybe-emit-return))))
508 ((<lexical-ref> src gensym)
511 (pmatch (hashq-ref (hashq-ref allocation gensym) self)
512 ((,local? ,boxed? . ,index)
513 (emit-code src (make-glil-lexical local? boxed? 'ref index)))
515 (error "badness" x loc)))))
518 ((<lexical-set> src gensym exp)
520 (pmatch (hashq-ref (hashq-ref allocation gensym) self)
521 ((,local? ,boxed? . ,index)
522 (emit-code src (make-glil-lexical local? boxed? 'set index)))
524 (error "badness" x loc)))
527 (emit-code #f (make-glil-void))))
530 ((<module-ref> src mod name public?)
531 (emit-code src (make-glil-module 'ref mod name public?))
533 ((drop) (emit-code #f (make-glil-call 'drop 1))))
536 ((<module-set> src mod name public? exp)
538 (emit-code src (make-glil-module 'set mod name public?))
541 (emit-code #f (make-glil-void))))
544 ((<toplevel-ref> src name)
545 (emit-code src (make-glil-toplevel 'ref name))
547 ((drop) (emit-code #f (make-glil-call 'drop 1))))
550 ((<toplevel-set> src name exp)
552 (emit-code src (make-glil-toplevel 'set name))
555 (emit-code #f (make-glil-void))))
558 ((<toplevel-define> src name exp)
560 (emit-code src (make-glil-toplevel 'define name))
563 (emit-code #f (make-glil-void))))
567 (let ((free-locs (cddr (hashq-ref allocation x))))
570 (emit-code #f (flatten-lambda x #f allocation))
571 (if (not (null? free-locs))
576 ((,local? ,boxed? . ,n)
577 (emit-code #f (make-glil-lexical local? #f 'ref n)))
578 (else (error "what" x loc))))
580 (emit-code #f (make-glil-call 'vector (length free-locs)))
581 (emit-code #f (make-glil-call 'make-closure 2)))))))
584 ((<let> src names vars vals body)
585 (for-each comp-push vals)
586 (emit-bindings src names vars allocation self emit-code)
587 (for-each (lambda (v)
588 (pmatch (hashq-ref (hashq-ref allocation v) self)
590 (emit-code src (make-glil-lexical #t #f 'set n)))
592 (emit-code src (make-glil-lexical #t #t 'box n)))
593 (,loc (error "badness" x loc))))
596 (emit-code #f (make-glil-unbind)))
598 ((<letrec> src names vars vals body)
599 (for-each (lambda (v)
600 (pmatch (hashq-ref (hashq-ref allocation v) self)
602 (emit-code src (make-glil-lexical #t #t 'empty-box n)))
603 (,loc (error "badness" x loc))))
605 (for-each comp-push vals)
606 (emit-bindings src names vars allocation self emit-code)
607 (for-each (lambda (v)
608 (pmatch (hashq-ref (hashq-ref allocation v) self)
610 (emit-code src (make-glil-lexical #t #t 'set n)))
611 (,loc (error "badness" x loc))))
614 (emit-code #f (make-glil-unbind)))
616 ((<fix> src names vars vals body)
617 ;; The ideal here is to just render the lambda bodies inline, and
618 ;; wire the code together with gotos. We can do that if
619 ;; analyze-lexicals has determined that a given var has "label"
620 ;; allocation -- which is the case if it is in `fix-labels'.
622 ;; But even for closures that we can't inline, we can do some
623 ;; tricks to avoid heap-allocation for the binding itself. Since
624 ;; we know the vals are lambdas, we can set them to their local
625 ;; var slots first, then capture their bindings, mutating them in
627 (let ((new-RA (if (or (eq? context 'tail) RA) #f (make-label))))
631 ((hashq-ref allocation x)
632 ;; allocating a closure
633 (emit-code #f (flatten-lambda x v allocation))
634 (if (not (null? (cddr (hashq-ref allocation x))))
635 ;; Need to make-closure first, but with a temporary #f
636 ;; free-variables vector, so we are mutating fresh
637 ;; closures on the heap.
639 (emit-code #f (make-glil-const #f))
640 (emit-code #f (make-glil-call 'make-closure 2))))
641 (pmatch (hashq-ref (hashq-ref allocation v) self)
643 (emit-code src (make-glil-lexical #t #f 'set n)))
644 (,loc (error "badness" x loc))))
646 ;; labels allocation: emit label & body, but jump over it
647 (let ((POST (make-label)))
648 (emit-branch #f 'br POST)
650 ;; we know the lambda vars are a list
651 (emit-bindings #f (lambda-names x) (lambda-vars x)
652 allocation self emit-code)
654 (emit-code #f (make-glil-source (lambda-src x))))
655 (comp-fix (lambda-body x) (or RA new-RA))
656 (emit-code #f (make-glil-unbind))
657 (emit-label POST)))))
660 ;; Emit bindings metadata for closures
661 (let ((binds (let lp ((out '()) (vars vars) (names names))
662 (cond ((null? vars) (reverse! out))
663 ((assq (car vars) fix-labels)
664 (lp out (cdr vars) (cdr names)))
666 (lp (acons (car vars) (car names) out)
667 (cdr vars) (cdr names)))))))
668 (emit-bindings src (map cdr binds) (map car binds)
669 allocation self emit-code))
670 ;; Now go back and fix up the bindings for closures.
673 (let ((free-locs (if (hashq-ref allocation x)
674 (cddr (hashq-ref allocation x))
675 ;; can hit this latter case for labels allocation
677 (if (not (null? free-locs))
682 ((,local? ,boxed? . ,n)
683 (emit-code #f (make-glil-lexical local? #f 'ref n)))
684 (else (error "what" x loc))))
686 (emit-code #f (make-glil-call 'vector (length free-locs)))
687 (pmatch (hashq-ref (hashq-ref allocation v) self)
689 (emit-code #f (make-glil-lexical #t #f 'fix n)))
690 (,loc (error "badness" x loc)))))))
696 (emit-code #f (make-glil-unbind))))
698 ((<let-values> src names vars exp body)
699 (let lp ((names '()) (vars '()) (inames names) (ivars vars) (rest? #f))
702 (lp (cons (car inames) names) (cons (car ivars) vars)
703 (cdr inames) (cdr ivars) #f))
704 ((not (null? inames))
705 (lp (cons inames names) (cons ivars vars) '() '() #t))
707 (let ((names (reverse! names))
708 (vars (reverse! vars))
711 (emit-code #f (make-glil-const 1))
713 (emit-code src (make-glil-mv-bind
714 (vars->bind-list names vars allocation self)
716 (for-each (lambda (v)
717 (pmatch (hashq-ref (hashq-ref allocation v) self)
719 (emit-code src (make-glil-lexical #t #f 'set n)))
721 (emit-code src (make-glil-lexical #t #t 'box n)))
722 (,loc (error "badness" x loc))))
725 (emit-code #f (make-glil-unbind))))))))))