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