a809e2d322560ffaba3ca9a49ea06a10ab011140
[bpt/guile.git] / module / language / tree-il / compile-glil.scm
1 ;;; TREE-IL -> GLIL compiler
2
3 ;; Copyright (C) 2001,2008,2009 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 analyze)
31 #:export (compile-glil))
32
33 ;; allocation:
34 ;; sym -> {lambda -> address}
35 ;; lambda -> (labels . free-locs)
36 ;; lambda-case -> (gensym . nlocs)
37 ;;
38 ;; address ::= (local? boxed? . index)
39 ;; labels ::= ((sym . lambda) ...)
40 ;; free-locs ::= ((sym0 . address0) (sym1 . address1) ...)
41 ;; free variable addresses are relative to parent proc.
42
43 (define *comp-module* (make-fluid))
44
45 (define %warning-passes
46 `((unused-variable . ,report-unused-variables)
47 (unbound-variable . ,report-possibly-unbound-variables)))
48
49 (define (compile-glil x e opts)
50 (define warnings
51 (or (and=> (memq #:warnings opts) cadr)
52 '()))
53
54 ;; Go through the warning passes.
55 (for-each (lambda (kind)
56 (let ((warn (assoc-ref %warning-passes kind)))
57 (and (procedure? warn)
58 (warn x e))))
59 warnings)
60
61 (let* ((x (make-lambda (tree-il-src x) '()
62 (make-lambda-case #f '() #f #f #f '() '() #f x #f)))
63 (x (optimize! x e opts))
64 (allocation (analyze-lexicals x)))
65
66 (with-fluid* *comp-module* e
67 (lambda ()
68 (values (flatten-lambda x #f allocation)
69 e
70 e)))))
71
72 \f
73
74 (define *primcall-ops* (make-hash-table))
75 (for-each
76 (lambda (x) (hash-set! *primcall-ops* (car x) (cdr x)))
77 '(((eq? . 2) . eq?)
78 ((eqv? . 2) . eqv?)
79 ((equal? . 2) . equal?)
80 ((= . 2) . ee?)
81 ((< . 2) . lt?)
82 ((> . 2) . gt?)
83 ((<= . 2) . le?)
84 ((>= . 2) . ge?)
85 ((+ . 2) . add)
86 ((- . 2) . sub)
87 ((1+ . 1) . add1)
88 ((1- . 1) . sub1)
89 ((* . 2) . mul)
90 ((/ . 2) . div)
91 ((quotient . 2) . quo)
92 ((remainder . 2) . rem)
93 ((modulo . 2) . mod)
94 ((not . 1) . not)
95 ((pair? . 1) . pair?)
96 ((cons . 2) . cons)
97 ((car . 1) . car)
98 ((cdr . 1) . cdr)
99 ((set-car! . 2) . set-car!)
100 ((set-cdr! . 2) . set-cdr!)
101 ((null? . 1) . null?)
102 ((list? . 1) . list?)
103 (list . list)
104 (vector . vector)
105 ((@slot-ref . 2) . slot-ref)
106 ((@slot-set! . 3) . slot-set)
107 ((vector-ref . 2) . vector-ref)
108 ((vector-set! . 3) . vector-set)
109
110 ((bytevector-u8-ref . 2) . bv-u8-ref)
111 ((bytevector-u8-set! . 3) . bv-u8-set)
112 ((bytevector-s8-ref . 2) . bv-s8-ref)
113 ((bytevector-s8-set! . 3) . bv-s8-set)
114
115 ((bytevector-u16-ref . 3) . bv-u16-ref)
116 ((bytevector-u16-set! . 4) . bv-u16-set)
117 ((bytevector-u16-native-ref . 2) . bv-u16-native-ref)
118 ((bytevector-u16-native-set! . 3) . bv-u16-native-set)
119 ((bytevector-s16-ref . 3) . bv-s16-ref)
120 ((bytevector-s16-set! . 4) . bv-s16-set)
121 ((bytevector-s16-native-ref . 2) . bv-s16-native-ref)
122 ((bytevector-s16-native-set! . 3) . bv-s16-native-set)
123
124 ((bytevector-u32-ref . 3) . bv-u32-ref)
125 ((bytevector-u32-set! . 4) . bv-u32-set)
126 ((bytevector-u32-native-ref . 2) . bv-u32-native-ref)
127 ((bytevector-u32-native-set! . 3) . bv-u32-native-set)
128 ((bytevector-s32-ref . 3) . bv-s32-ref)
129 ((bytevector-s32-set! . 4) . bv-s32-set)
130 ((bytevector-s32-native-ref . 2) . bv-s32-native-ref)
131 ((bytevector-s32-native-set! . 3) . bv-s32-native-set)
132
133 ((bytevector-u64-ref . 3) . bv-u64-ref)
134 ((bytevector-u64-set! . 4) . bv-u64-set)
135 ((bytevector-u64-native-ref . 2) . bv-u64-native-ref)
136 ((bytevector-u64-native-set! . 3) . bv-u64-native-set)
137 ((bytevector-s64-ref . 3) . bv-s64-ref)
138 ((bytevector-s64-set! . 4) . bv-s64-set)
139 ((bytevector-s64-native-ref . 2) . bv-s64-native-ref)
140 ((bytevector-s64-native-set! . 3) . bv-s64-native-set)
141
142 ((bytevector-ieee-single-ref . 3) . bv-f32-ref)
143 ((bytevector-ieee-single-set! . 4) . bv-f32-set)
144 ((bytevector-ieee-single-native-ref . 2) . bv-f32-native-ref)
145 ((bytevector-ieee-single-native-set! . 3) . bv-f32-native-set)
146 ((bytevector-ieee-double-ref . 3) . bv-f64-ref)
147 ((bytevector-ieee-double-set! . 4) . bv-f64-set)
148 ((bytevector-ieee-double-native-ref . 2) . bv-f64-native-ref)
149 ((bytevector-ieee-double-native-set! . 3) . bv-f64-native-set)))
150
151
152 \f
153
154 (define (make-label) (gensym ":L"))
155
156 (define (vars->bind-list ids vars allocation proc)
157 (map (lambda (id v)
158 (pmatch (hashq-ref (hashq-ref allocation v) proc)
159 ((#t ,boxed? . ,n)
160 (list id boxed? n))
161 (,x (error "badness" x))))
162 ids
163 vars))
164
165 (define (emit-bindings src ids vars allocation proc emit-code)
166 (emit-code src (make-glil-bind
167 (vars->bind-list ids vars allocation proc))))
168
169 (define (with-output-to-code proc)
170 (let ((out '()))
171 (define (emit-code src x)
172 (set! out (cons x out))
173 (if src
174 (set! out (cons (make-glil-source src) out))))
175 (proc emit-code)
176 (reverse out)))
177
178 (define (flatten-lambda x self-label allocation)
179 (record-case x
180 ((<lambda> src meta body)
181 (make-glil-program
182 meta
183 (with-output-to-code
184 (lambda (emit-code)
185 ;; write source info for proc
186 (if src (emit-code #f (make-glil-source src)))
187 ;; emit pre-prelude label for self tail calls in which the
188 ;; number of arguments doesn't check out at compile time
189 (if self-label
190 (emit-code #f (make-glil-label self-label)))
191 ;; compile the body, yo
192 (flatten body allocation x self-label (car (hashq-ref allocation x))
193 emit-code)))))))
194
195 (define (flatten x allocation self self-label fix-labels emit-code)
196 (define (emit-label label)
197 (emit-code #f (make-glil-label label)))
198 (define (emit-branch src inst label)
199 (emit-code src (make-glil-branch inst label)))
200
201 ;; RA: "return address"; #f unless we're in a non-tail fix with labels
202 ;; MVRA: "multiple-values return address"; #f unless we're in a let-values
203 (let comp ((x x) (context 'tail) (RA #f) (MVRA #f))
204 (define (comp-tail tree) (comp tree context RA MVRA))
205 (define (comp-push tree) (comp tree 'push #f #f))
206 (define (comp-drop tree) (comp tree 'drop #f #f))
207 (define (comp-vals tree MVRA) (comp tree 'vals #f MVRA))
208 (define (comp-fix tree RA) (comp tree context RA MVRA))
209
210 ;; A couple of helpers. Note that if we are in tail context, we
211 ;; won't have an RA.
212 (define (maybe-emit-return)
213 (if RA
214 (emit-branch #f 'br RA)
215 (if (eq? context 'tail)
216 (emit-code #f (make-glil-call 'return 1)))))
217
218 (record-case x
219 ((<void>)
220 (case context
221 ((push vals tail)
222 (emit-code #f (make-glil-void))))
223 (maybe-emit-return))
224
225 ((<const> src exp)
226 (case context
227 ((push vals tail)
228 (emit-code src (make-glil-const exp))))
229 (maybe-emit-return))
230
231 ;; FIXME: should represent sequence as exps tail
232 ((<sequence> exps)
233 (let lp ((exps exps))
234 (if (null? (cdr exps))
235 (comp-tail (car exps))
236 (begin
237 (comp-drop (car exps))
238 (lp (cdr exps))))))
239
240 ((<application> src proc args)
241 ;; FIXME: need a better pattern-matcher here
242 (cond
243 ((and (primitive-ref? proc)
244 (eq? (primitive-ref-name proc) '@apply)
245 (>= (length args) 1))
246 (let ((proc (car args))
247 (args (cdr args)))
248 (cond
249 ((and (primitive-ref? proc) (eq? (primitive-ref-name proc) 'values)
250 (not (eq? context 'push)) (not (eq? context 'vals)))
251 ;; tail: (lambda () (apply values '(1 2)))
252 ;; drop: (lambda () (apply values '(1 2)) 3)
253 ;; push: (lambda () (list (apply values '(10 12)) 1))
254 (case context
255 ((drop) (for-each comp-drop args) (maybe-emit-return))
256 ((tail)
257 (for-each comp-push args)
258 (emit-code src (make-glil-call 'return/values* (length args))))))
259
260 (else
261 (case context
262 ((tail)
263 (comp-push proc)
264 (for-each comp-push args)
265 (emit-code src (make-glil-call 'goto/apply (1+ (length args)))))
266 ((push)
267 (emit-code src (make-glil-call 'new-frame 0))
268 (comp-push proc)
269 (for-each comp-push args)
270 (emit-code src (make-glil-call 'apply (1+ (length args))))
271 (maybe-emit-return))
272 ((vals)
273 (comp-vals
274 (make-application src (make-primitive-ref #f 'apply)
275 (cons proc args))
276 MVRA)
277 (maybe-emit-return))
278 ((drop)
279 ;; Well, shit. The proc might return any number of
280 ;; values (including 0), since it's in a drop context,
281 ;; yet apply does not create a MV continuation. So we
282 ;; mv-call out to our trampoline instead.
283 (comp-drop
284 (make-application src (make-primitive-ref #f 'apply)
285 (cons proc args)))
286 (maybe-emit-return)))))))
287
288 ((and (primitive-ref? proc) (eq? (primitive-ref-name proc) 'values)
289 (not (eq? context 'push)))
290 ;; tail: (lambda () (values '(1 2)))
291 ;; drop: (lambda () (values '(1 2)) 3)
292 ;; push: (lambda () (list (values '(10 12)) 1))
293 ;; vals: (let-values (((a b ...) (values 1 2 ...))) ...)
294 (case context
295 ((drop) (for-each comp-drop args) (maybe-emit-return))
296 ((vals)
297 (for-each comp-push args)
298 (emit-code #f (make-glil-const (length args)))
299 (emit-branch src 'br MVRA))
300 ((tail)
301 (for-each comp-push args)
302 (emit-code src (make-glil-call 'return/values (length args))))))
303
304 ((and (primitive-ref? proc)
305 (eq? (primitive-ref-name proc) '@call-with-values)
306 (= (length args) 2))
307 ;; CONSUMER
308 ;; PRODUCER
309 ;; (mv-call MV)
310 ;; ([tail]-call 1)
311 ;; goto POST
312 ;; MV: [tail-]call/nargs
313 ;; POST: (maybe-drop)
314 (case context
315 ((vals)
316 ;; Fall back.
317 (comp-vals
318 (make-application src (make-primitive-ref #f 'call-with-values)
319 args)
320 MVRA)
321 (maybe-emit-return))
322 (else
323 (let ((MV (make-label)) (POST (make-label))
324 (producer (car args)) (consumer (cadr args)))
325 (if (not (eq? context 'tail))
326 (emit-code src (make-glil-call 'new-frame 0)))
327 (comp-push consumer)
328 (emit-code src (make-glil-call 'new-frame 0))
329 (comp-push producer)
330 (emit-code src (make-glil-mv-call 0 MV))
331 (case context
332 ((tail) (emit-code src (make-glil-call 'goto/args 1)))
333 (else (emit-code src (make-glil-call 'call 1))
334 (emit-branch #f 'br POST)))
335 (emit-label MV)
336 (case context
337 ((tail) (emit-code src (make-glil-call 'goto/nargs 0)))
338 (else (emit-code src (make-glil-call 'call/nargs 0))
339 (emit-label POST)
340 (if (eq? context 'drop)
341 (emit-code #f (make-glil-call 'drop 1)))
342 (maybe-emit-return)))))))
343
344 ((and (primitive-ref? proc)
345 (eq? (primitive-ref-name proc) '@call-with-current-continuation)
346 (= (length args) 1))
347 (case context
348 ((tail)
349 (comp-push (car args))
350 (emit-code src (make-glil-call 'goto/cc 1)))
351 ((vals)
352 (comp-vals
353 (make-application
354 src (make-primitive-ref #f 'call-with-current-continuation)
355 args)
356 MVRA)
357 (maybe-emit-return))
358 ((push)
359 (comp-push (car args))
360 (emit-code src (make-glil-call 'call/cc 1))
361 (maybe-emit-return))
362 ((drop)
363 ;; Crap. Just like `apply' in drop context.
364 (comp-drop
365 (make-application
366 src (make-primitive-ref #f 'call-with-current-continuation)
367 args))
368 (maybe-emit-return))))
369
370 ((and (primitive-ref? proc)
371 (or (hash-ref *primcall-ops*
372 (cons (primitive-ref-name proc) (length args)))
373 (hash-ref *primcall-ops* (primitive-ref-name proc))))
374 => (lambda (op)
375 (for-each comp-push args)
376 (emit-code src (make-glil-call op (length args)))
377 (case (instruction-pushes op)
378 ((0)
379 (case context
380 ((tail push vals) (emit-code #f (make-glil-void))))
381 (maybe-emit-return))
382 ((1)
383 (case context
384 ((drop) (emit-code #f (make-glil-call 'drop 1))))
385 (maybe-emit-return))
386 (else
387 (error "bad primitive op: too many pushes"
388 op (instruction-pushes op))))))
389
390 ;; self-call in tail position
391 ((and (lexical-ref? proc)
392 self-label (eq? (lexical-ref-gensym proc) self-label)
393 (eq? context 'tail))
394 ;; first, evaluate new values, pushing them on the stack
395 (for-each comp-push args)
396 (let lp ((lcase (lambda-body self)))
397 (cond
398 ((and (lambda-case? lcase)
399 (not (lambda-case-kw lcase))
400 (not (lambda-case-opt lcase))
401 (not (lambda-case-rest lcase))
402 (= (length args) (length (lambda-case-req lcase))))
403 ;; we have a case that matches the args; rename variables
404 ;; and goto the case label
405 (for-each (lambda (sym)
406 (pmatch (hashq-ref (hashq-ref allocation sym) self)
407 ((#t #f . ,index) ; unboxed
408 (emit-code #f (make-glil-lexical #t #f 'set index)))
409 ((#t #t . ,index) ; boxed
410 ;; new box
411 (emit-code #f (make-glil-lexical #t #t 'box index)))
412 (,x (error "what" x))))
413 (reverse (lambda-case-vars lcase)))
414 (emit-branch src 'br (car (hashq-ref allocation lcase))))
415 ((lambda-case? lcase)
416 ;; no match, try next case
417 (lp (lambda-case-else lcase)))
418 (else
419 ;; no cases left; shuffle args down and jump before the prelude.
420 (for-each (lambda (i)
421 (emit-code #f (make-glil-lexical #t #f 'set i)))
422 (reverse (iota (length args))))
423 (emit-branch src 'br self-label)))))
424
425 ;; lambda, the ultimate goto
426 ((and (lexical-ref? proc)
427 (assq (lexical-ref-gensym proc) fix-labels))
428 ;; like the self-tail-call case, though we can handle "drop"
429 ;; contexts too. first, evaluate new values, pushing them on
430 ;; the stack
431 (for-each comp-push args)
432 ;; find the specific case, rename args, and goto the case label
433 (let lp ((lcase (lambda-body
434 (assq-ref fix-labels (lexical-ref-gensym proc)))))
435 (cond
436 ((and (lambda-case? lcase)
437 (not (lambda-case-kw lcase))
438 (not (lambda-case-opt lcase))
439 (not (lambda-case-rest lcase))
440 (= (length args) (length (lambda-case-req lcase))))
441 ;; we have a case that matches the args; rename variables
442 ;; and goto the case label
443 (for-each (lambda (sym)
444 (pmatch (hashq-ref (hashq-ref allocation sym) self)
445 ((#t #f . ,index) ; unboxed
446 (emit-code #f (make-glil-lexical #t #f 'set index)))
447 ((#t #t . ,index) ; boxed
448 (emit-code #f (make-glil-lexical #t #t 'box index)))
449 (,x (error "what" x))))
450 (reverse (lambda-case-vars lcase)))
451 (emit-branch src 'br (car (hashq-ref allocation lcase))))
452 ((lambda-case? lcase)
453 ;; no match, try next case
454 (lp (lambda-case-else lcase)))
455 (else
456 ;; no cases left. we can't really handle this currently.
457 ;; ideally we would push on a new frame, then do a "local
458 ;; call" -- which doesn't require consing up a program
459 ;; object. but for now error, as this sort of case should
460 ;; preclude label allocation.
461 (error "couldn't find matching case for label call" x)))))
462
463 (else
464 (if (not (eq? context 'tail))
465 (emit-code src (make-glil-call 'new-frame 0)))
466 (comp-push proc)
467 (for-each comp-push args)
468 (let ((len (length args)))
469 (case context
470 ((tail) (emit-code src (make-glil-call 'goto/args len)))
471 ((push) (emit-code src (make-glil-call 'call len))
472 (maybe-emit-return))
473 ((vals) (emit-code src (make-glil-mv-call len MVRA))
474 (maybe-emit-return))
475 ((drop) (let ((MV (make-label)) (POST (make-label)))
476 (emit-code src (make-glil-mv-call len MV))
477 (emit-code #f (make-glil-call 'drop 1))
478 (emit-branch #f 'br (or RA POST))
479 (emit-label MV)
480 (emit-code #f (make-glil-mv-bind '() #f))
481 (emit-code #f (make-glil-unbind))
482 (if RA
483 (emit-branch #f 'br RA)
484 (emit-label POST)))))))))
485
486 ((<conditional> src test then else)
487 ;; TEST
488 ;; (br-if-not L1)
489 ;; THEN
490 ;; (br L2)
491 ;; L1: ELSE
492 ;; L2:
493 (let ((L1 (make-label)) (L2 (make-label)))
494 (comp-push test)
495 (emit-branch src 'br-if-not L1)
496 (comp-tail then)
497 ;; if there is an RA, comp-tail will cause a jump to it -- just
498 ;; have to clean up here if there is no RA.
499 (if (and (not RA) (not (eq? context 'tail)))
500 (emit-branch #f 'br L2))
501 (emit-label L1)
502 (comp-tail else)
503 (if (and (not RA) (not (eq? context 'tail)))
504 (emit-label L2))))
505
506 ((<primitive-ref> src name)
507 (cond
508 ((eq? (module-variable (fluid-ref *comp-module*) name)
509 (module-variable the-root-module name))
510 (case context
511 ((tail push vals)
512 (emit-code src (make-glil-toplevel 'ref name))))
513 (maybe-emit-return))
514 ((module-variable the-root-module name)
515 (case context
516 ((tail push vals)
517 (emit-code src (make-glil-module 'ref '(guile) name #f))))
518 (maybe-emit-return))
519 (else
520 (case context
521 ((tail push vals)
522 (emit-code src (make-glil-module
523 'ref (module-name (fluid-ref *comp-module*)) name #f))))
524 (maybe-emit-return))))
525
526 ((<lexical-ref> src gensym)
527 (case context
528 ((push vals tail)
529 (pmatch (hashq-ref (hashq-ref allocation gensym) self)
530 ((,local? ,boxed? . ,index)
531 (emit-code src (make-glil-lexical local? boxed? 'ref index)))
532 (,loc
533 (error "badness" x loc)))))
534 (maybe-emit-return))
535
536 ((<lexical-set> src gensym exp)
537 (comp-push exp)
538 (pmatch (hashq-ref (hashq-ref allocation gensym) self)
539 ((,local? ,boxed? . ,index)
540 (emit-code src (make-glil-lexical local? boxed? 'set index)))
541 (,loc
542 (error "badness" x loc)))
543 (case context
544 ((tail push vals)
545 (emit-code #f (make-glil-void))))
546 (maybe-emit-return))
547
548 ((<module-ref> src mod name public?)
549 (emit-code src (make-glil-module 'ref mod name public?))
550 (case context
551 ((drop) (emit-code #f (make-glil-call 'drop 1))))
552 (maybe-emit-return))
553
554 ((<module-set> src mod name public? exp)
555 (comp-push exp)
556 (emit-code src (make-glil-module 'set mod name public?))
557 (case context
558 ((tail push vals)
559 (emit-code #f (make-glil-void))))
560 (maybe-emit-return))
561
562 ((<toplevel-ref> src name)
563 (emit-code src (make-glil-toplevel 'ref name))
564 (case context
565 ((drop) (emit-code #f (make-glil-call 'drop 1))))
566 (maybe-emit-return))
567
568 ((<toplevel-set> src name exp)
569 (comp-push exp)
570 (emit-code src (make-glil-toplevel 'set name))
571 (case context
572 ((tail push vals)
573 (emit-code #f (make-glil-void))))
574 (maybe-emit-return))
575
576 ((<toplevel-define> src name exp)
577 (comp-push exp)
578 (emit-code src (make-glil-toplevel 'define name))
579 (case context
580 ((tail push vals)
581 (emit-code #f (make-glil-void))))
582 (maybe-emit-return))
583
584 ((<lambda>)
585 (let ((free-locs (cdr (hashq-ref allocation x))))
586 (case context
587 ((push vals tail)
588 (emit-code #f (flatten-lambda x #f allocation))
589 (if (not (null? free-locs))
590 (begin
591 (for-each
592 (lambda (loc)
593 (pmatch loc
594 ((,local? ,boxed? . ,n)
595 (emit-code #f (make-glil-lexical local? #f 'ref n)))
596 (else (error "what" x loc))))
597 free-locs)
598 (emit-code #f (make-glil-call 'vector (length free-locs)))
599 (emit-code #f (make-glil-call 'make-closure 2)))))))
600 (maybe-emit-return))
601
602 ((<lambda-case> src req opt rest kw inits vars predicate else body)
603 ;; o/~ feature on top of feature o/~
604 ;; req := (name ...)
605 ;; opt := (name ...) | #f
606 ;; rest := name | #f
607 ;; kw: (allow-other-keys? (keyword name var) ...) | #f
608 ;; vars: (sym ...)
609 ;; predicate: tree-il in context of vars
610 ;; init: tree-il in context of vars
611 ;; vars map to named arguments in the following order:
612 ;; required, optional (positional), rest, keyword.
613 (let* ((nreq (length req))
614 (nopt (if opt (length opt) 0))
615 (rest-idx (and rest (+ nreq nopt)))
616 (opt-names (or opt '()))
617 (allow-other-keys? (if kw (car kw) #f))
618 (kw-indices (map (lambda (x)
619 (pmatch x
620 ((,key ,name ,var)
621 (cons key (list-index vars var)))
622 (else (error "bad kwarg" x))))
623 (if kw (cdr kw) '())))
624 (nargs (apply max (+ nreq nopt (if rest 1 0))
625 (map 1+ (map cdr kw-indices))))
626 (nlocs (cdr (hashq-ref allocation x)))
627 (else-label (and else (make-label))))
628 (or (= nargs
629 (length vars)
630 (+ nreq (length inits) (if rest 1 0)))
631 (error "something went wrong"
632 req opt rest kw inits vars nreq nopt kw-indices nargs))
633 ;; the prelude, to check args & reset the stack pointer,
634 ;; allowing room for locals
635 (emit-code
636 src
637 (cond
638 (kw
639 (make-glil-kw-prelude nreq nopt rest-idx kw-indices
640 allow-other-keys? nlocs else-label))
641 ((or rest opt)
642 (make-glil-opt-prelude nreq nopt rest-idx nlocs else-label))
643 (#t
644 (make-glil-std-prelude nreq nlocs else-label))))
645 ;; box args if necessary
646 (for-each
647 (lambda (v)
648 (pmatch (hashq-ref (hashq-ref allocation v) self)
649 ((#t #t . ,n)
650 (emit-code #f (make-glil-lexical #t #f 'ref n))
651 (emit-code #f (make-glil-lexical #t #t 'box n)))))
652 vars)
653 ;; write bindings info
654 (if (not (null? vars))
655 (emit-bindings
656 #f
657 (let lp ((kw (if kw (cdr kw) '()))
658 (names (append (reverse opt-names) (reverse req)))
659 (vars (list-tail vars (+ nreq nopt
660 (if rest 1 0)))))
661 (pmatch kw
662 (()
663 ;; fixme: check that vars is empty
664 (reverse (if rest (cons rest names) names)))
665 (((,key ,name ,var) . ,kw)
666 (if (memq var vars)
667 (lp kw (cons name names) (delq var vars))
668 (lp kw names vars)))
669 (,kw (error "bad keywords, yo" kw))))
670 vars allocation self emit-code))
671 ;; init optional/kw args
672 (let lp ((inits inits) (n nreq) (vars (list-tail vars nreq)))
673 (cond
674 ((null? inits)) ; done
675 ((and rest-idx (= n rest-idx))
676 (lp inits (1+ n) (cdr vars)))
677 (#t
678 (pmatch (hashq-ref (hashq-ref allocation (car vars)) self)
679 ((#t ,boxed? . ,n*) (guard (= n* n))
680 (let ((L (make-label)))
681 (emit-code #f (make-glil-lexical #t boxed? 'bound? n))
682 (emit-code #f (make-glil-branch 'br-if L))
683 (comp-push (car inits))
684 (emit-code #f (make-glil-lexical #t boxed? 'set n))
685 (emit-label L)
686 (lp (cdr inits) (1+ n) (cdr vars))))
687 (#t (error "what" inits))))))
688 ;; post-prelude case label for label calls
689 (emit-label (car (hashq-ref allocation x)))
690 (if predicate
691 (begin
692 (comp-push predicate)
693 (if else-label
694 ;; fixme: debox if necessary
695 (emit-branch src 'br-if-not else-label)
696 (comp-push (make-application
697 src (make-primitive-ref #f 'error)
698 (list (make-const #f "precondition not met")))))))
699 (comp-tail body)
700 (if (not (null? vars))
701 (emit-code #f (make-glil-unbind)))
702 (if else-label
703 (begin
704 (emit-label else-label)
705 (comp-tail else)))))
706
707 ((<let> src names vars vals body)
708 (for-each comp-push vals)
709 (emit-bindings src names vars allocation self emit-code)
710 (for-each (lambda (v)
711 (pmatch (hashq-ref (hashq-ref allocation v) self)
712 ((#t #f . ,n)
713 (emit-code src (make-glil-lexical #t #f 'set n)))
714 ((#t #t . ,n)
715 (emit-code src (make-glil-lexical #t #t 'box n)))
716 (,loc (error "badness" x loc))))
717 (reverse vars))
718 (comp-tail body)
719 (emit-code #f (make-glil-unbind)))
720
721 ((<letrec> src names vars vals body)
722 (for-each (lambda (v)
723 (pmatch (hashq-ref (hashq-ref allocation v) self)
724 ((#t #t . ,n)
725 (emit-code src (make-glil-lexical #t #t 'empty-box n)))
726 (,loc (error "badness" x loc))))
727 vars)
728 (for-each comp-push vals)
729 (emit-bindings src names vars allocation self emit-code)
730 (for-each (lambda (v)
731 (pmatch (hashq-ref (hashq-ref allocation v) self)
732 ((#t #t . ,n)
733 (emit-code src (make-glil-lexical #t #t 'set n)))
734 (,loc (error "badness" x loc))))
735 (reverse vars))
736 (comp-tail body)
737 (emit-code #f (make-glil-unbind)))
738
739 ((<fix> src names vars vals body)
740 ;; The ideal here is to just render the lambda bodies inline, and
741 ;; wire the code together with gotos. We can do that if
742 ;; analyze-lexicals has determined that a given var has "label"
743 ;; allocation -- which is the case if it is in `fix-labels'.
744 ;;
745 ;; But even for closures that we can't inline, we can do some
746 ;; tricks to avoid heap-allocation for the binding itself. Since
747 ;; we know the vals are lambdas, we can set them to their local
748 ;; var slots first, then capture their bindings, mutating them in
749 ;; place.
750 (let ((new-RA (if (or (eq? context 'tail) RA) #f (make-label))))
751 (for-each
752 (lambda (x v)
753 (cond
754 ((hashq-ref allocation x)
755 ;; allocating a closure
756 (emit-code #f (flatten-lambda x v allocation))
757 (if (not (null? (cdr (hashq-ref allocation x))))
758 ;; Need to make-closure first, but with a temporary #f
759 ;; free-variables vector, so we are mutating fresh
760 ;; closures on the heap.
761 (begin
762 (emit-code #f (make-glil-const #f))
763 (emit-code #f (make-glil-call 'make-closure 2))))
764 (pmatch (hashq-ref (hashq-ref allocation v) self)
765 ((#t #f . ,n)
766 (emit-code src (make-glil-lexical #t #f 'set n)))
767 (,loc (error "badness" x loc))))
768 (else
769 ;; labels allocation: emit label & body, but jump over it
770 (let ((POST (make-label)))
771 (emit-branch #f 'br POST)
772 (let lp ((lcase (lambda-body x)))
773 (if lcase
774 (record-case lcase
775 ((<lambda-case> src req vars body else)
776 (emit-label (car (hashq-ref allocation lcase)))
777 ;; FIXME: opt & kw args in the bindings
778 (emit-bindings #f req vars allocation self emit-code)
779 (if src
780 (emit-code #f (make-glil-source src)))
781 (comp-fix body (or RA new-RA))
782 (emit-code #f (make-glil-unbind))
783 (lp else)))
784 (emit-label POST)))))))
785 vals
786 vars)
787 ;; Emit bindings metadata for closures
788 (let ((binds (let lp ((out '()) (vars vars) (names names))
789 (cond ((null? vars) (reverse! out))
790 ((assq (car vars) fix-labels)
791 (lp out (cdr vars) (cdr names)))
792 (else
793 (lp (acons (car vars) (car names) out)
794 (cdr vars) (cdr names)))))))
795 (emit-bindings src (map cdr binds) (map car binds)
796 allocation self emit-code))
797 ;; Now go back and fix up the bindings for closures.
798 (for-each
799 (lambda (x v)
800 (let ((free-locs (if (hashq-ref allocation x)
801 (cdr (hashq-ref allocation x))
802 ;; can hit this latter case for labels allocation
803 '())))
804 (if (not (null? free-locs))
805 (begin
806 (for-each
807 (lambda (loc)
808 (pmatch loc
809 ((,local? ,boxed? . ,n)
810 (emit-code #f (make-glil-lexical local? #f 'ref n)))
811 (else (error "what" x loc))))
812 free-locs)
813 (emit-code #f (make-glil-call 'vector (length free-locs)))
814 (pmatch (hashq-ref (hashq-ref allocation v) self)
815 ((#t #f . ,n)
816 (emit-code #f (make-glil-lexical #t #f 'fix n)))
817 (,loc (error "badness" x loc)))))))
818 vals
819 vars)
820 (comp-tail body)
821 (if new-RA
822 (emit-label new-RA))
823 (emit-code #f (make-glil-unbind))))
824
825 ((<let-values> src exp body)
826 (record-case body
827 ((<lambda-case> req opt kw rest vars predicate body else)
828 (if (or opt kw predicate else)
829 (error "unexpected lambda-case in let-values" x))
830 (let ((MV (make-label)))
831 (comp-vals exp MV)
832 (emit-code #f (make-glil-const 1))
833 (emit-label MV)
834 (emit-code src (make-glil-mv-bind
835 (vars->bind-list
836 (append req (if rest (list rest) '()))
837 vars allocation self)
838 (and rest #t)))
839 (for-each (lambda (v)
840 (pmatch (hashq-ref (hashq-ref allocation v) self)
841 ((#t #f . ,n)
842 (emit-code src (make-glil-lexical #t #f 'set n)))
843 ((#t #t . ,n)
844 (emit-code src (make-glil-lexical #t #t 'box n)))
845 (,loc (error "badness" x loc))))
846 (reverse vars))
847 (comp-tail body)
848 (emit-code #f (make-glil-unbind)))))))))