Merge branch 'stable-2.0'
[bpt/guile.git] / module / language / cps / slot-allocation.scm
1 ;;; Continuation-passing style (CPS) intermediate language (IL)
2
3 ;; Copyright (C) 2013, 2014 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 ;;; Commentary:
20 ;;;
21 ;;; A module to assign stack slots to variables in a CPS term.
22 ;;;
23 ;;; Code:
24
25 (define-module (language cps slot-allocation)
26 #:use-module (ice-9 match)
27 #:use-module (srfi srfi-1)
28 #:use-module (srfi srfi-9)
29 #:use-module (srfi srfi-26)
30 #:use-module (language cps)
31 #:use-module (language cps dfg)
32 #:export (allocate-slots
33 lookup-slot
34 lookup-maybe-slot
35 lookup-constant-value
36 lookup-maybe-constant-value
37 lookup-nlocals
38 lookup-call-proc-slot
39 lookup-parallel-moves))
40
41 (define-record-type $allocation
42 (make-allocation dfa slots
43 has-constv constant-values
44 call-allocations
45 nlocals)
46 allocation?
47
48 ;; A DFA records all variables bound in a function, and assigns them
49 ;; indices. The slot in which a variable is stored at runtime can be
50 ;; had by indexing into the SLOTS vector with the variable's index.
51 ;;
52 (dfa allocation-dfa)
53 (slots allocation-slots)
54
55 ;; Not all variables have slots allocated. Variables that are
56 ;; constant and that are only used by primcalls that can accept
57 ;; constants directly are not allocated to slots, and their SLOT value
58 ;; is false. Likewise constants that are only used by calls are not
59 ;; allocated into slots, to avoid needless copying. If a variable is
60 ;; constant, its constant value is set in the CONSTANT-VALUES vector
61 ;; and the corresponding bit in the HAS-CONSTV bitvector is set.
62 ;;
63 (has-constv allocation-has-constv)
64 (constant-values allocation-constant-values)
65
66 ;; Some continuations have additional associated information. This
67 ;; addition information is a /call allocation/. Call allocations
68 ;; record the way that functions are passed values, and how their
69 ;; return values are rebound to local variables.
70 ;;
71 ;; A call allocation contains two pieces of information: the call's
72 ;; /proc slot/, and a set of /parallel moves/. The proc slot
73 ;; indicates the slot of a procedure in a procedure call, or where the
74 ;; procedure would be in a multiple-value return. The parallel moves
75 ;; shuffle locals into position for a call, or shuffle returned values
76 ;; back into place. Though they use the same slot, moves for a call
77 ;; are called "call moves", and moves to handle a return are "return
78 ;; moves".
79 ;;
80 ;; $kreceive continuations record a proc slot and a set of return moves
81 ;; to adapt multiple values from the stack to local variables.
82 ;;
83 ;; Tail calls record arg moves, but no proc slot.
84 ;;
85 ;; Non-tail calls record arg moves and a call slot. Multiple-valued
86 ;; returns will have an associated $kreceive continuation, which records
87 ;; the same proc slot, but has return moves.
88 ;;
89 ;; $prompt handlers are $kreceive continuations like any other.
90 ;;
91 ;; $values expressions with more than 1 value record moves but have no
92 ;; proc slot.
93 ;;
94 ;; A set of moves is expressed as an ordered list of (SRC . DST)
95 ;; moves, where SRC and DST are slots. This may involve a temporary
96 ;; variable.
97 ;;
98 (call-allocations allocation-call-allocations)
99
100 ;; The number of locals for a $kclause.
101 ;;
102 (nlocals allocation-nlocals))
103
104 (define-record-type $call-allocation
105 (make-call-allocation proc-slot moves)
106 call-allocation?
107 (proc-slot call-allocation-proc-slot)
108 (moves call-allocation-moves))
109
110 (define (find-first-zero n)
111 ;; Naive implementation.
112 (let lp ((slot 0))
113 (if (logbit? slot n)
114 (lp (1+ slot))
115 slot)))
116
117 (define (find-first-trailing-zero n)
118 (let lp ((slot (let lp ((count 2))
119 (if (< n (ash 1 (1- count)))
120 count
121 ;; Grow upper bound slower than factor 2 to avoid
122 ;; needless bignum allocation on 32-bit systems
123 ;; when there are more than 16 locals.
124 (lp (+ count (ash count -1)))))))
125 (if (or (zero? slot) (logbit? (1- slot) n))
126 slot
127 (lp (1- slot)))))
128
129 (define (lookup-maybe-slot sym allocation)
130 (match allocation
131 (($ $allocation dfa slots)
132 (vector-ref slots (dfa-var-idx dfa sym)))))
133
134 (define (lookup-slot sym allocation)
135 (or (lookup-maybe-slot sym allocation)
136 (error "Variable not allocated to a slot" sym)))
137
138 (define (lookup-constant-value sym allocation)
139 (match allocation
140 (($ $allocation dfa slots has-constv constant-values)
141 (let ((idx (dfa-var-idx dfa sym)))
142 (if (bitvector-ref has-constv idx)
143 (vector-ref constant-values idx)
144 (error "Variable does not have constant value" sym))))))
145
146 (define (lookup-maybe-constant-value sym allocation)
147 (match allocation
148 (($ $allocation dfa slots has-constv constant-values)
149 (let ((idx (dfa-var-idx dfa sym)))
150 (values (bitvector-ref has-constv idx)
151 (vector-ref constant-values idx))))))
152
153 (define (lookup-call-allocation k allocation)
154 (or (hashq-ref (allocation-call-allocations allocation) k)
155 (error "Continuation not a call" k)))
156
157 (define (lookup-call-proc-slot k allocation)
158 (or (call-allocation-proc-slot (lookup-call-allocation k allocation))
159 (error "Call has no proc slot" k)))
160
161 (define (lookup-parallel-moves k allocation)
162 (or (call-allocation-moves (lookup-call-allocation k allocation))
163 (error "Call has no use parallel moves slot" k)))
164
165 (define (lookup-nlocals k allocation)
166 (or (hashq-ref (allocation-nlocals allocation) k)
167 (error "Not a clause continuation" k)))
168
169 (define (solve-parallel-move src dst tmp)
170 "Solve the parallel move problem between src and dst slot lists, which
171 are comparable with eqv?. A tmp slot may be used."
172
173 ;; This algorithm is taken from: "Tilting at windmills with Coq:
174 ;; formal verification of a compilation algorithm for parallel moves"
175 ;; by Laurence Rideau, Bernard Paul Serpette, and Xavier Leroy
176 ;; <http://gallium.inria.fr/~xleroy/publi/parallel-move.pdf>
177
178 (define (split-move moves reg)
179 (let loop ((revhead '()) (tail moves))
180 (match tail
181 (((and s+d (s . d)) . rest)
182 (if (eqv? s reg)
183 (cons d (append-reverse revhead rest))
184 (loop (cons s+d revhead) rest)))
185 (_ #f))))
186
187 (define (replace-last-source reg moves)
188 (match moves
189 ((moves ... (s . d))
190 (append moves (list (cons reg d))))))
191
192 (let loop ((to-move (map cons src dst))
193 (being-moved '())
194 (moved '())
195 (last-source #f))
196 ;; 'last-source' should always be equivalent to:
197 ;; (and (pair? being-moved) (car (last being-moved)))
198 (match being-moved
199 (() (match to-move
200 (() (reverse moved))
201 (((and s+d (s . d)) . t1)
202 (if (or (eqv? s d) ; idempotent
203 (not s)) ; src is a constant and can be loaded directly
204 (loop t1 '() moved #f)
205 (loop t1 (list s+d) moved s)))))
206 (((and s+d (s . d)) . b)
207 (match (split-move to-move d)
208 ((r . t1) (loop t1 (acons d r being-moved) moved last-source))
209 (#f (match b
210 (() (loop to-move '() (cons s+d moved) #f))
211 (_ (if (eqv? d last-source)
212 (loop to-move
213 (replace-last-source tmp b)
214 (cons s+d (acons d tmp moved))
215 tmp)
216 (loop to-move b (cons s+d moved) last-source))))))))))
217
218 (define (dead-after-def? def-k v-idx dfa)
219 (let ((l (dfa-k-idx dfa def-k)))
220 (not (bitvector-ref (dfa-k-in dfa l) v-idx))))
221
222 (define (dead-after-use? use-k v-idx dfa)
223 (let ((l (dfa-k-idx dfa use-k)))
224 (not (bitvector-ref (dfa-k-out dfa l) v-idx))))
225
226 (define (allocate-slots fun dfg)
227 (let* ((dfa (compute-live-variables fun dfg))
228 (cfa (analyze-control-flow fun dfg))
229 (usev (make-vector (cfa-k-count cfa) '()))
230 (defv (make-vector (cfa-k-count cfa) '()))
231 (contv (make-vector (cfa-k-count cfa) #f))
232 (slots (make-vector (dfa-var-count dfa) #f))
233 (constant-values (make-vector (dfa-var-count dfa) #f))
234 (has-constv (make-bitvector (dfa-var-count dfa) #f))
235 (has-slotv (make-bitvector (dfa-var-count dfa) #t))
236 (needs-slotv (make-bitvector (dfa-var-count dfa) #t))
237 (needs-hintv (make-bitvector (dfa-var-count dfa) #f))
238 (call-allocations (make-hash-table))
239 (nlocals 0) ; Mutable. It pains me.
240 (nlocals-table (make-hash-table)))
241
242 (define (bump-nlocals! nlocals*)
243 (when (< nlocals nlocals*)
244 (set! nlocals nlocals*)))
245
246 (define (empty-live-slots)
247 #b0)
248
249 (define (add-live-slot slot live-slots)
250 (logior live-slots (ash 1 slot)))
251
252 (define (kill-dead-slot slot live-slots)
253 (logand live-slots (lognot (ash 1 slot))))
254
255 (define (compute-slot live-slots hint)
256 (if (and hint (not (logbit? hint live-slots)))
257 hint
258 (find-first-zero live-slots)))
259
260 (define (compute-call-proc-slot live-slots)
261 (+ 2 (find-first-trailing-zero live-slots)))
262
263 (define (compute-prompt-handler-proc-slot live-slots)
264 (1- (find-first-trailing-zero live-slots)))
265
266 (define (recompute-live-slots k nargs)
267 (let ((in (dfa-k-in dfa (dfa-k-idx dfa k))))
268 (let lp ((v 0) (live-slots (1- (ash 1 (1+ nargs)))))
269 (let ((v (bit-position #t in v)))
270 (if v
271 (let ((slot (vector-ref slots v)))
272 (lp (1+ v)
273 (if slot
274 (add-live-slot slot live-slots)
275 live-slots)))
276 live-slots)))))
277
278 (define* (allocate! var-idx hint live)
279 (cond
280 ((not (bitvector-ref needs-slotv var-idx)) live)
281 ((vector-ref slots var-idx) => (cut add-live-slot <> live))
282 ((and (not hint) (bitvector-ref needs-hintv var-idx)) live)
283 (else
284 (let ((slot (compute-slot live hint)))
285 (bump-nlocals! (1+ slot))
286 (vector-set! slots var-idx slot)
287 (add-live-slot slot live)))))
288
289 ;; Although some parallel moves may proceed without a temporary
290 ;; slot, in general one is needed. That temporary slot must not be
291 ;; part of the source or destination sets, and that slot should not
292 ;; correspond to a live variable. Usually the source and
293 ;; destination sets are a subset of the union of the live sets
294 ;; before and after the move. However for stack slots that don't
295 ;; have names -- those slots that correspond to function arguments
296 ;; or to function return values -- it could be that they are out of
297 ;; the computed live set. In that case they need to be adjoined to
298 ;; the live set, used when choosing a temporary slot.
299 (define (compute-tmp-slot live stack-slots)
300 (find-first-zero (fold add-live-slot live stack-slots)))
301
302 (define (parallel-move src-slots dst-slots tmp-slot)
303 (let ((moves (solve-parallel-move src-slots dst-slots tmp-slot)))
304 (when (assv tmp-slot moves)
305 (bump-nlocals! (1+ tmp-slot)))
306 moves))
307
308 ;; Find variables that are actually constant, and determine which
309 ;; of those can avoid slot allocation.
310 (define (compute-constants!)
311 (let lp ((n 0))
312 (when (< n (vector-length constant-values))
313 (let ((sym (dfa-var-sym dfa n)))
314 (call-with-values (lambda () (find-constant-value sym dfg))
315 (lambda (has-const? const)
316 (when has-const?
317 (bitvector-set! has-constv n has-const?)
318 (vector-set! constant-values n const)
319 (when (not (constant-needs-allocation? sym const dfg))
320 (bitvector-set! needs-slotv n #f)))
321 (lp (1+ n))))))))
322
323 ;; Transform the DFG's continuation table to a vector, for easy
324 ;; access.
325 (define (compute-conts!)
326 (let ((cont-table (dfg-cont-table dfg)))
327 (let lp ((n 0))
328 (when (< n (vector-length contv))
329 (vector-set! contv n (lookup-cont (cfa-k-sym cfa n) cont-table))
330 (lp (1+ n))))))
331
332 ;; Record uses and defs, as lists of variable indexes, indexed by
333 ;; CFA continuation index.
334 (define (compute-uses-and-defs!)
335 (let lp ((n 0))
336 (when (< n (vector-length usev))
337 (match (vector-ref contv n)
338 (($ $kentry self)
339 (vector-set! defv n (list (dfa-var-idx dfa self))))
340 (($ $kargs names syms body)
341 (vector-set! defv n (map (cut dfa-var-idx dfa <>) syms))
342 (vector-set! usev n
343 (map (cut dfa-var-idx dfa <>)
344 (match (find-expression body)
345 (($ $call proc args)
346 (cons proc args))
347 (($ $primcall name args)
348 args)
349 (($ $values args)
350 args)
351 (($ $prompt escape? tag handler)
352 (list tag))
353 (_ '())))))
354 (_ #f))
355 (lp (1+ n)))))
356
357 ;; Results of function calls that are not used don't need to be
358 ;; allocated to slots.
359 (define (compute-unused-results!)
360 (define (kreceive-get-kargs n)
361 (match (vector-ref contv n)
362 (($ $kreceive arity kargs) (cfa-k-idx cfa kargs))
363 (_ #f)))
364 (let ((candidates (make-bitvector (vector-length contv) #f)))
365 ;; Find all $kargs that are the successors of $kreceive nodes.
366 (let lp ((n 0))
367 (when (< n (vector-length contv))
368 (and=> (kreceive-get-kargs n)
369 (lambda (kargs)
370 (bitvector-set! candidates kargs #t)))
371 (lp (1+ n))))
372 ;; For $kargs that only have $kreceive predecessors, remove unused
373 ;; variables from the needs-slotv set.
374 (let lp ((n 0))
375 (let ((n (bit-position #t candidates n)))
376 (when n
377 (match (cfa-predecessors cfa n)
378 ;; At least one kreceive is in the predecessor set, so we
379 ;; only need to do the check for nodes with >1
380 ;; predecessor.
381 ((or (_) ((? kreceive-get-kargs) ...))
382 (for-each (lambda (var)
383 (when (dead-after-def? (cfa-k-sym cfa n) var dfa)
384 (bitvector-set! needs-slotv var #f)))
385 (vector-ref defv n)))
386 (_ #f))
387 (lp (1+ n)))))))
388
389 ;; Compute the set of variables whose allocation should be delayed
390 ;; until a "hint" is known about where to allocate them. This is
391 ;; the case for some procedure arguments.
392 ;;
393 ;; This algorithm used is a conservative approximation of what
394 ;; really should happen, which would be eager allocation of call
395 ;; frames as soon as it's known that a call will happen. It would
396 ;; be nice to recast this as a proper data-flow problem.
397 (define (compute-needs-hint!)
398 ;; We traverse the graph using reverse-post-order on a forward
399 ;; control-flow graph, but we did the live variable analysis in
400 ;; the opposite direction -- so the continuation numbers don't
401 ;; correspond. This helper adapts them.
402 (define (cfa-k-idx->dfa-k-idx n)
403 (dfa-k-idx dfa (cfa-k-sym cfa n)))
404
405 (define (live-before n)
406 (dfa-k-in dfa (cfa-k-idx->dfa-k-idx n)))
407 (define (live-after n)
408 (dfa-k-out dfa (cfa-k-idx->dfa-k-idx n)))
409
410 ;; Walk backwards. At a call, compute the set of variables that
411 ;; have allocated slots and are live before but not after. This
412 ;; set contains candidates for needs-hintv.
413 (define (scan-for-call n)
414 (when (<= 0 n)
415 (match (vector-ref contv n)
416 (($ $kargs names syms body)
417 (match (find-expression body)
418 (($ $call)
419 (let ((args (make-bitvector (bitvector-length needs-slotv) #f)))
420 (bit-set*! args (live-before n) #t)
421 (bit-set*! args (live-after n) #f)
422 (bit-set*! args no-slot-needed #f)
423 (if (bit-position #t args 0)
424 (scan-for-hints (1- n) args)
425 (scan-for-call (1- n)))))
426 (_ (scan-for-call (1- n)))))
427 (_ (scan-for-call (1- n))))))
428
429 ;; Walk backwards in the current basic block. Stop when the block
430 ;; ends, we reach a call, or when an expression kills a value.
431 (define (scan-for-hints n args)
432 (when (< 0 n)
433 (match (vector-ref contv n)
434 (($ $kargs names syms body)
435 (match (cfa-predecessors cfa (1+ n))
436 (((? (cut eqv? <> n)))
437 ;; If we are indeed in the same basic block, then if we
438 ;; are finished with the scan, we kill uses of the
439 ;; terminator, but leave its definitions.
440 (match (find-expression body)
441 ((or ($ $void) ($ $const) ($ $prim) ($ $fun)
442 ($ $primcall) ($ $prompt)
443 ;; If $values has more than one argument, it may
444 ;; use a temporary, which would invalidate our
445 ;; assumptions that slots not allocated are not
446 ;; used.
447 ($ $values (or () (_))))
448 (let ((dead (make-bitvector (bitvector-length args) #f)))
449 (bit-set*! dead (live-before n) #t)
450 (bit-set*! dead (live-after n) #f)
451 (bit-set*! dead no-slot-needed #f)
452 (if (bit-position #t dead 0)
453 (finish-hints n (live-before n) args)
454 (scan-for-hints (1- n) args))))
455 ((or ($ $call) ($ $values))
456 (finish-hints n (live-before n) args))))
457 ;; Otherwise we kill uses of the block entry.
458 (_ (finish-hints n (live-before (1+ n)) args))))
459 (_ (finish-hints n (live-before (1+ n)) args)))))
460
461 ;; Add definitions ARGS minus KILL to NEED-HINTS, and go back to
462 ;; looking for calls.
463 (define (finish-hints n kill args)
464 (bit-invert! args)
465 (bit-set*! args kill #t)
466 (bit-invert! args)
467 (bit-set*! needs-hintv args #t)
468 (scan-for-call n))
469
470 (define no-slot-needed
471 (make-bitvector (bitvector-length needs-slotv) #f))
472
473 (bit-set*! no-slot-needed needs-slotv #t)
474 (bit-invert! no-slot-needed)
475 (scan-for-call (1- (vector-length contv))))
476
477 (define (allocate-call label k uses pre-live post-live)
478 (match (vector-ref contv (cfa-k-idx cfa k))
479 (($ $ktail)
480 (let* ((tail-nlocals (length uses))
481 (tail-slots (iota tail-nlocals))
482 (pre-live (fold allocate! pre-live uses tail-slots))
483 (moves (parallel-move (map (cut vector-ref slots <>) uses)
484 tail-slots
485 (compute-tmp-slot pre-live tail-slots))))
486 (bump-nlocals! tail-nlocals)
487 (hashq-set! call-allocations label
488 (make-call-allocation #f moves))))
489 (($ $kreceive arity kargs)
490 (let* ((proc-slot (compute-call-proc-slot post-live))
491 (call-slots (map (cut + proc-slot <>) (iota (length uses))))
492 (pre-live (fold allocate! pre-live uses call-slots))
493 (arg-moves (parallel-move (map (cut vector-ref slots <>) uses)
494 call-slots
495 (compute-tmp-slot pre-live
496 call-slots)))
497 (result-vars (vector-ref defv (cfa-k-idx cfa kargs)))
498 (value-slots (map (cut + proc-slot 1 <>)
499 (iota (length result-vars))))
500 ;; Shuffle the first result down to the lowest slot, and
501 ;; leave any remaining results where they are. This
502 ;; strikes a balance between avoiding shuffling,
503 ;; especially for unused extra values, and avoiding
504 ;; frame size growth due to sparse locals.
505 (result-live (match (cons result-vars value-slots)
506 ((() . ()) post-live)
507 (((var . vars) . (slot . slots))
508 (fold allocate!
509 (allocate! var #f post-live)
510 vars slots))))
511 (result-slots (map (cut vector-ref slots <>) result-vars))
512 ;; Filter out unused results.
513 (value-slots (filter-map (lambda (val result) (and result val))
514 value-slots result-slots))
515 (result-slots (filter (lambda (x) x) result-slots))
516 (result-moves (parallel-move value-slots
517 result-slots
518 (compute-tmp-slot result-live
519 value-slots))))
520 (bump-nlocals! (+ proc-slot (length uses)))
521 (hashq-set! call-allocations label
522 (make-call-allocation proc-slot arg-moves))
523 (hashq-set! call-allocations k
524 (make-call-allocation proc-slot result-moves))))
525
526 (_
527 (let* ((proc-slot (compute-call-proc-slot post-live))
528 (call-slots (map (cut + proc-slot <>) (iota (length uses))))
529 (pre-live (fold allocate! pre-live uses call-slots))
530 (arg-moves (parallel-move (map (cut vector-ref slots <>) uses)
531 call-slots
532 (compute-tmp-slot pre-live
533 call-slots))))
534 (bump-nlocals! (+ proc-slot (length uses)))
535 (hashq-set! call-allocations label
536 (make-call-allocation proc-slot arg-moves))))))
537
538 (define (allocate-values label k uses pre-live post-live)
539 (match (vector-ref contv (cfa-k-idx cfa k))
540 (($ $ktail)
541 (let* ((src-slots (map (cut vector-ref slots <>) uses))
542 (tail-nlocals (1+ (length uses)))
543 (dst-slots (cdr (iota tail-nlocals)))
544 (moves (parallel-move src-slots dst-slots
545 (compute-tmp-slot pre-live dst-slots))))
546 (bump-nlocals! tail-nlocals)
547 (hashq-set! call-allocations label
548 (make-call-allocation #f moves))))
549 (($ $kargs (_) (_))
550 ;; When there is only one value in play, we allow the dst to be
551 ;; hinted (see scan-for-hints). If the src doesn't have a
552 ;; slot, then the actual slot for the dst would end up being
553 ;; decided by the call that uses it. Because we don't know the
554 ;; slot, we can't really compute the parallel moves in that
555 ;; case, so just bail and rely on the bytecode emitter to
556 ;; handle the one-value case specially.
557 (match (cons uses (vector-ref defv (cfa-k-idx cfa k)))
558 (((src) . (dst))
559 (allocate! dst (vector-ref slots src) post-live))))
560 (($ $kargs)
561 (let* ((src-slots (map (cut vector-ref slots <>) uses))
562 (dst-vars (vector-ref defv (cfa-k-idx cfa k)))
563 (result-live (fold allocate! post-live dst-vars src-slots))
564 (dst-slots (map (cut vector-ref slots <>) dst-vars))
565 (moves (parallel-move src-slots dst-slots
566 (compute-tmp-slot (logior pre-live result-live)
567 '()))))
568 (hashq-set! call-allocations label
569 (make-call-allocation #f moves))))
570 (($ $kif) #f)))
571
572 (define (allocate-prompt label k handler nargs)
573 (match (vector-ref contv (cfa-k-idx cfa handler))
574 (($ $kreceive arity kargs)
575 (let* ((handler-live (recompute-live-slots handler nargs))
576 (proc-slot (compute-prompt-handler-proc-slot handler-live))
577 (result-vars (vector-ref defv (cfa-k-idx cfa kargs)))
578 (value-slots (map (cut + proc-slot 1 <>)
579 (iota (length result-vars))))
580 (result-live (fold allocate!
581 handler-live result-vars value-slots))
582 (result-slots (map (cut vector-ref slots <>) result-vars))
583 ;; Filter out unused results.
584 (value-slots (filter-map (lambda (val result) (and result val))
585 value-slots result-slots))
586 (result-slots (filter (lambda (x) x) result-slots))
587 (moves (parallel-move value-slots
588 result-slots
589 (compute-tmp-slot result-live
590 value-slots))))
591 (bump-nlocals! (+ proc-slot 1 (length result-vars)))
592 (hashq-set! call-allocations handler
593 (make-call-allocation proc-slot moves))))))
594
595 (define (allocate-defs! n live)
596 (fold (cut allocate! <> #f <>) live (vector-ref defv n)))
597
598 ;; This traversal will visit definitions before uses, as
599 ;; definitions dominate uses and a block's dominator will appear
600 ;; before it, in reverse post-order.
601 (define (visit-clause n nargs live)
602 (let lp ((n n) (live live))
603 (define (kill-dead live vars-by-cfa-idx pred)
604 (fold (lambda (v live)
605 (let ((slot (vector-ref slots v)))
606 (if (and slot
607 (> slot nargs)
608 (pred (cfa-k-sym cfa n) v dfa))
609 (kill-dead-slot slot live)
610 live)))
611 live
612 (vector-ref vars-by-cfa-idx n)))
613 (define (kill-dead-defs live)
614 (kill-dead live defv dead-after-def?))
615 (define (kill-dead-uses live)
616 (kill-dead live usev dead-after-use?))
617 (if (= n (cfa-k-count cfa))
618 n
619 (let* ((label (cfa-k-sym cfa n))
620 (live (if (control-point? label dfg)
621 (recompute-live-slots label nargs)
622 live))
623 (live (kill-dead-defs (allocate-defs! n live)))
624 (post-live (kill-dead-uses live)))
625 ;; LIVE are the live slots coming into the term.
626 ;; POST-LIVE is the subset that is still live after the
627 ;; term uses its inputs.
628 (match (vector-ref contv n)
629 (($ $kclause) n)
630 (($ $kargs names syms body)
631 (let ((uses (vector-ref usev n)))
632 (match (find-call body)
633 (($ $continue k src ($ $call))
634 (allocate-call label k uses live post-live))
635 (($ $continue k src ($ $primcall)) #t)
636 (($ $continue k src ($ $values))
637 (allocate-values label k uses live post-live))
638 (($ $continue k src ($ $prompt escape? tag handler))
639 (allocate-prompt label k handler nargs))
640 (_ #f)))
641 (lp (1+ n) post-live))
642 ((or ($ $kreceive) ($ $kif) ($ $ktail))
643 (lp (1+ n) post-live)))))))
644
645 (define (visit-entry)
646 (define (visit-clauses n live)
647 (unless (eqv? live (add-live-slot 0 (empty-live-slots)))
648 (error "Unexpected clause live set"))
649 (set! nlocals 1)
650 (match (vector-ref contv n)
651 (($ $kclause arity ($ $cont kbody ($ $kargs names)))
652 (unless (eq? (cfa-k-sym cfa (1+ n)) kbody)
653 (error "Unexpected CFA order"))
654 (let* ((nargs (length names))
655 (next (visit-clause (1+ n)
656 nargs
657 (fold allocate! live
658 (vector-ref defv (1+ n))
659 (cdr (iota (1+ nargs)))))))
660 (hashq-set! nlocals-table (cfa-k-sym cfa n) nlocals)
661 (when (< next (cfa-k-count cfa))
662 (visit-clauses next live))))))
663 (match (vector-ref contv 0)
664 (($ $kentry self)
665 (visit-clauses 1 (allocate-defs! 0 (empty-live-slots))))))
666
667 (compute-conts!)
668 (compute-constants!)
669 (compute-uses-and-defs!)
670 (compute-unused-results!)
671 (compute-needs-hint!)
672 (visit-entry)
673
674 (make-allocation dfa slots
675 has-constv constant-values
676 call-allocations
677 nlocals-table)))