DFA uses DFG var numbering
[bpt/guile.git] / module / language / cps / dfg.scm
CommitLineData
6e8ad823
AW
1;;; Continuation-passing style (CPS) intermediate language (IL)
2
6eb02960 3;; Copyright (C) 2013, 2014 Free Software Foundation, Inc.
6e8ad823
AW
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;;; Many passes rely on a local or global static analysis of a function.
22;;; This module implements a simple data-flow graph (DFG) analysis,
23;;; tracking the definitions and uses of variables and continuations.
f22979db 24;;; It also builds a table of continuations and scope links, to be able
6e8ad823
AW
25;;; to easily determine if one continuation is in the scope of another,
26;;; and to get to the expression inside a continuation.
27;;;
28;;; Note that the data-flow graph of continuation labels is a
29;;; control-flow graph.
30;;;
31;;; We currently don't expose details of the DFG type outside this
32;;; module, preferring to only expose accessors. That may change in the
33;;; future but it seems to work for now.
34;;;
35;;; Code:
36
37(define-module (language cps dfg)
38 #:use-module (ice-9 match)
39 #:use-module (srfi srfi-1)
40 #:use-module (srfi srfi-9)
41 #:use-module (srfi srfi-26)
42 #:use-module (language cps)
43 #:export (build-cont-table
6e8ad823
AW
44 lookup-cont
45
46 compute-dfg
47 dfg-cont-table
a8430ab1
AW
48 dfg-min-label
49 dfg-label-count
50 dfg-min-var
51 dfg-var-count
6e8ad823
AW
52 lookup-def
53 lookup-uses
f22979db
AW
54 lookup-predecessors
55 lookup-successors
c8ad7426 56 lookup-block-scope
6e8ad823
AW
57 find-call
58 call-expression
59 find-expression
60 find-defining-expression
61 find-constant-value
f22979db 62 continuation-bound-in?
d51fb1e6 63 variable-free-in?
6e8ad823 64 constant-needs-allocation?
e636f424 65 control-point?
db11440d
AW
66 lookup-bound-syms
67
dda5fd94
AW
68 ;; Control flow analysis.
69 analyze-control-flow
70 cfa-k-idx cfa-k-count cfa-k-sym cfa-predecessors
71
db11440d
AW
72 ;; Data flow analysis.
73 compute-live-variables
74 dfa-k-idx dfa-k-sym dfa-k-count dfa-k-in dfa-k-out
29619661 75 dfa-var-idx dfa-var-sym dfa-var-count
db11440d 76 print-dfa))
6e8ad823 77
48c2a539
AW
78;; These definitions are here because currently we don't do cross-module
79;; inlining. They can be removed once that restriction is gone.
80(define-inlinable (for-each f l)
81 (unless (list? l)
82 (scm-error 'wrong-type-arg "for-each" "Not a list: ~S" (list l) #f))
83 (let for-each1 ((l l))
84 (unless (null? l)
85 (f (car l))
86 (for-each1 (cdr l)))))
87
88(define-inlinable (for-each/2 f l1 l2)
89 (unless (= (length l1) (length l2))
90 (scm-error 'wrong-type-arg "for-each" "List of wrong length: ~S"
91 (list l2) #f))
92 (let for-each2 ((l1 l1) (l2 l2))
93 (unless (null? l1)
94 (f (car l1) (car l2))
95 (for-each2 (cdr l1) (cdr l2)))))
96
6e8ad823 97(define (build-cont-table fun)
fbdb69b2
AW
98 (let ((max-k (fold-conts (lambda (k cont max-k) (max k max-k))
99 -1 fun)))
100 (fold-conts (lambda (k cont table)
101 (vector-set! table k cont)
102 table)
103 (make-vector (1+ max-k) #f)
104 fun)))
105
6e8ad823
AW
106;; Data-flow graph for CPS: both for values and continuations.
107(define-record-type $dfg
4bf757b8 108 (make-dfg conts preds defs uses scopes scope-levels
a8430ab1 109 min-label label-count min-var var-count)
6e8ad823 110 dfg?
5e897908 111 ;; vector of label -> $kif, $kargs, etc
6e8ad823 112 (conts dfg-cont-table)
5fc40391 113 ;; vector of label -> (pred-label ...)
21d6d183 114 (preds dfg-preds)
5fc40391 115 ;; vector of var -> def-label
98c5b69f 116 (defs dfg-defs)
5fc40391 117 ;; vector of var -> (use-label ...)
98c5b69f 118 (uses dfg-uses)
5fc40391
AW
119 ;; vector of label -> label
120 (scopes dfg-scopes)
121 ;; vector of label -> int
122 (scope-levels dfg-scope-levels)
5e897908
AW
123
124 (min-label dfg-min-label)
a8430ab1 125 (label-count dfg-label-count)
5e897908 126 (min-var dfg-min-var)
a8430ab1 127 (var-count dfg-var-count))
6e8ad823 128
5bff3125
AW
129;; Some analyses assume that the only relevant set of nodes is the set
130;; that is reachable from some start node. Others need to include nodes
131;; that are reachable from an end node as well, or all nodes in a
132;; function. In that case pass an appropriate implementation of
6eb02960
AW
133;; fold-all-conts, as analyze-control-flow does.
134(define (reverse-post-order k0 get-successors fold-all-conts)
3aee6cfd
AW
135 (let ((order '())
136 (visited? (make-hash-table)))
137 (let visit ((k k0))
138 (hashq-set! visited? k #t)
0e2446d4
AW
139 (for-each (lambda (k)
140 (unless (hashq-ref visited? k)
141 (visit k)))
334bd8e3 142 (get-successors k))
0e2446d4 143 (set! order (cons k order)))
5bff3125
AW
144 (list->vector (fold-all-conts
145 (lambda (k seed)
146 (if (hashq-ref visited? k)
147 seed
148 (begin
149 (hashq-set! visited? k #t)
150 (cons k seed))))
151 order))))
3aee6cfd 152
334bd8e3
AW
153(define (make-block-mapping order)
154 (let ((mapping (make-hash-table)))
366eb4d7
AW
155 (let lp ((n 0))
156 (when (< n (vector-length order))
157 (hashq-set! mapping (vector-ref order n) n)
158 (lp (1+ n))))
334bd8e3
AW
159 mapping))
160
161(define (convert-predecessors order get-predecessors)
162 (let ((preds-vec (make-vector (vector-length order) #f)))
366eb4d7
AW
163 (let lp ((n 0))
164 (when (< n (vector-length order))
334bd8e3
AW
165 (vector-set! preds-vec n (get-predecessors (vector-ref order n)))
166 (lp (1+ n))))
3aee6cfd
AW
167 preds-vec))
168
334bd8e3
AW
169;; Control-flow analysis.
170(define-record-type $cfa
dda5fd94 171 (make-cfa k-map order preds)
334bd8e3
AW
172 cfa?
173 ;; Hash table mapping k-sym -> k-idx
174 (k-map cfa-k-map)
175 ;; Vector of k-idx -> k-sym, in reverse post order
176 (order cfa-order)
177 ;; Vector of k-idx -> list of k-idx
dda5fd94
AW
178 (preds cfa-preds))
179
180(define* (cfa-k-idx cfa k
181 #:key (default (lambda (k)
182 (error "unknown k" k))))
183 (or (hashq-ref (cfa-k-map cfa) k)
184 (default k)))
185
186(define (cfa-k-count cfa)
187 (vector-length (cfa-order cfa)))
188
189(define (cfa-k-sym cfa n)
190 (vector-ref (cfa-order cfa) n))
191
192(define (cfa-predecessors cfa n)
193 (vector-ref (cfa-preds cfa) n))
194
9002277d
AW
195(define-inlinable (vector-push! vec idx val)
196 (let ((v vec) (i idx))
197 (vector-set! v i (cons val (vector-ref v i)))))
198
199(define (compute-reachable cfa dfg)
200 "Given the forward control-flow analysis in CFA, compute and return
201the continuations that may be reached if flow reaches a continuation N.
202Returns a vector of bitvectors. The given CFA should be a forward CFA,
203for quickest convergence."
204 (let* ((k-count (cfa-k-count cfa))
205 ;; Vector of bitvectors, indicating that continuation N can
206 ;; reach a set M...
207 (reachable (make-vector k-count #f))
208 ;; Vector of lists, indicating that continuation N can directly
209 ;; reach continuations M...
210 (succs (make-vector k-count '())))
211
212 ;; All continuations are reachable from themselves.
213 (let lp ((n 0))
214 (when (< n k-count)
215 (let ((bv (make-bitvector k-count #f)))
216 (bitvector-set! bv n #t)
217 (vector-set! reachable n bv)
218 (lp (1+ n)))))
219
220 ;; Initialize successor lists.
221 (let lp ((n 0))
222 (when (< n k-count)
223 (for-each (lambda (succ)
224 (vector-push! succs n (cfa-k-idx cfa succ)))
21d6d183 225 (lookup-successors (cfa-k-sym cfa n) dfg))
9002277d
AW
226 (lp (1+ n))))
227
228 ;; Iterate cfa backwards, to converge quickly.
229 (let ((tmp (make-bitvector k-count #f)))
230 (let lp ((n k-count) (changed? #f))
231 (cond
232 ((zero? n)
233 (if changed?
234 (lp 0 #f)
235 reachable))
236 (else
237 (let ((n (1- n)))
238 (bitvector-fill! tmp #f)
239 (for-each (lambda (succ)
240 (bit-set*! tmp (vector-ref reachable succ) #t))
241 (vector-ref succs n))
242 (bitvector-set! tmp n #t)
243 (bit-set*! tmp (vector-ref reachable n) #f)
244 (cond
245 ((bit-position #t tmp 0)
246 (bit-set*! (vector-ref reachable n) tmp #t)
247 (lp n #t))
248 (else
249 (lp n changed?))))))))))
250
251(define (find-prompts cfa dfg)
252 "Find the prompts in CFA, and return them as a list of PROMPT-INDEX,
253HANDLER-INDEX pairs."
254 (let lp ((n 0) (prompts '()))
255 (cond
256 ((= n (cfa-k-count cfa))
257 (reverse prompts))
258 (else
fbdb69b2 259 (match (lookup-cont (cfa-k-sym cfa n) dfg)
9002277d
AW
260 (($ $kargs names syms body)
261 (match (find-expression body)
262 (($ $prompt escape? tag handler)
263 (lp (1+ n) (acons n (cfa-k-idx cfa handler) prompts)))
264 (_ (lp (1+ n) prompts))))
265 (_ (lp (1+ n) prompts)))))))
266
267(define (compute-interval cfa dfg reachable start end)
268 "Compute and return the set of continuations that may be reached from
269START, inclusive, but not reached by END, exclusive. Returns a
270bitvector."
271 (let ((body (make-bitvector (cfa-k-count cfa) #f)))
272 (bit-set*! body (vector-ref reachable start) #t)
273 (bit-set*! body (vector-ref reachable end) #f)
274 body))
275
276(define (find-prompt-bodies cfa dfg)
277 "Find all the prompts in CFA, and compute the set of continuations
278that is reachable from the prompt bodies but not from the corresponding
279handler. Returns a list of PROMPT, HANDLER, BODY lists, where the BODY
280is a bitvector."
281 (match (find-prompts cfa dfg)
282 (() '())
283 (((prompt . handler) ...)
284 (let ((reachable (compute-reachable cfa dfg)))
285 (map (lambda (prompt handler)
286 ;; FIXME: It isn't correct to use all continuations
287 ;; reachable from the prompt, because that includes
288 ;; continuations outside the prompt body. This point is
289 ;; moot if the handler's control flow joins with the the
290 ;; body, as is usually but not always the case.
291 ;;
292 ;; One counter-example is when the handler contifies an
293 ;; infinite loop; in that case we compute a too-large
294 ;; prompt body. This error is currently innocuous, but
295 ;; we should fix it at some point.
296 ;;
297 ;; The fix is to end the body at the corresponding "pop"
298 ;; primcall, if any.
299 (let ((body (compute-interval cfa dfg reachable prompt handler)))
300 (list prompt handler body)))
301 prompt handler)))))
302
303(define* (visit-prompt-control-flow cfa dfg f #:key complete?)
304 "For all prompts in CFA, invoke F with arguments PROMPT, HANDLER, and
305BODY for each body continuation in the prompt."
306 (for-each
307 (match-lambda
308 ((prompt handler body)
309 (define (out-or-back-edge? n)
310 ;; Most uses of visit-prompt-control-flow don't need every body
311 ;; continuation, and would be happy getting called only for
312 ;; continuations that postdominate the rest of the body. Unless
313 ;; you pass #:complete? #t, we only invoke F on continuations
314 ;; that can leave the body, or on back-edges in loops.
315 ;;
316 ;; You would think that looking for the final "pop" primcall
317 ;; would be sufficient, but that is incorrect; it's possible for
318 ;; a loop in the prompt body to be contified, and that loop need
319 ;; not continue to the pop if it never terminates. The pop could
320 ;; even be removed by DCE, in that case.
321 (or-map (lambda (succ)
322 (let ((succ (cfa-k-idx cfa succ)))
323 (or (not (bitvector-ref body succ))
324 (<= succ n))))
21d6d183 325 (lookup-successors (cfa-k-sym cfa n) dfg)))
9002277d
AW
326 (let lp ((n 0))
327 (let ((n (bit-position #t body n)))
328 (when n
329 (when (or complete? (out-or-back-edge? n))
330 (f prompt handler n))
331 (lp (1+ n)))))))
332 (find-prompt-bodies cfa dfg)))
333
334(define* (analyze-control-flow fun dfg #:key reverse? add-handler-preds?)
21d6d183
AW
335 (define (build-cfa kentry lookup-succs lookup-preds forward-cfa)
336 (define (reachable-preds mapping)
dda5fd94
AW
337 ;; It's possible for a predecessor to not be in the mapping, if
338 ;; the predecessor is not reachable from the entry node.
339 (lambda (k)
21d6d183 340 (filter-map (cut hashq-ref mapping <>) (lookup-preds k dfg))))
9002277d
AW
341 (let* ((order (reverse-post-order
342 kentry
6e5e9ffb
AW
343 (lambda (k)
344 ;; RPO numbering is going to visit this list of
345 ;; successors in the order that we give it. Sort
346 ;; it so that all things being equal, we preserve
347 ;; the existing numbering order. This also has the
348 ;; effect of preserving clause order.
349 (let ((succs (lookup-succs k dfg)))
350 (if (or (null? succs) (null? (cdr succs)))
351 succs
352 (sort succs >))))
9002277d
AW
353 (if forward-cfa
354 (lambda (f seed)
355 (let lp ((n (cfa-k-count forward-cfa)) (seed seed))
356 (if (zero? n)
357 seed
358 (lp (1- n)
359 (f (cfa-k-sym forward-cfa (1- n)) seed)))))
360 (lambda (f seed) seed))))
dda5fd94 361 (k-map (make-block-mapping order))
21d6d183 362 (preds (convert-predecessors order (reachable-preds k-map)))
9002277d
AW
363 (cfa (make-cfa k-map order preds)))
364 (when add-handler-preds?
365 ;; Any expression in the prompt body could cause an abort to the
366 ;; handler. This code adds links from every block in the prompt
367 ;; body to the handler. This causes all values used by the
368 ;; handler to be seen as live in the prompt body, as indeed they
369 ;; are.
370 (let ((forward-cfa (or forward-cfa cfa)))
371 (visit-prompt-control-flow
372 forward-cfa dfg
373 (lambda (prompt handler body)
374 (define (renumber n)
375 (if (eq? forward-cfa cfa)
376 n
377 (cfa-k-idx cfa (cfa-k-sym forward-cfa n))))
378 (let ((handler (renumber handler))
379 (body (renumber body)))
380 (if reverse?
381 (vector-push! preds body handler)
382 (vector-push! preds handler body)))))))
383 cfa))
dda5fd94 384 (match fun
6e422a35
AW
385 (($ $fun src meta free
386 ($ $cont kentry
90dce16d 387 (and entry ($ $kentry self ($ $cont ktail tail)))))
dda5fd94 388 (if reverse?
21d6d183 389 (build-cfa ktail lookup-predecessors lookup-successors
9002277d
AW
390 (analyze-control-flow fun dfg #:reverse? #f
391 #:add-handler-preds? #f))
21d6d183 392 (build-cfa kentry lookup-successors lookup-predecessors #f)))))
dda5fd94
AW
393
394;; Dominator analysis.
395(define-record-type $dominator-analysis
396 (make-dominator-analysis cfa idoms dom-levels loop-header irreducible)
397 dominator-analysis?
398 ;; The corresponding $cfa
399 (cfa dominator-analysis-cfa)
334bd8e3 400 ;; Vector of k-idx -> k-idx
dda5fd94 401 (idoms dominator-analysis-idoms)
334bd8e3 402 ;; Vector of k-idx -> dom-level
dda5fd94 403 (dom-levels dominator-analysis-dom-levels)
334bd8e3 404 ;; Vector of k-idx -> k-idx or -1
dda5fd94 405 (loop-header dominator-analysis-loop-header)
334bd8e3 406 ;; Vector of k-idx -> true or false value
dda5fd94 407 (irreducible dominator-analysis-irreducible))
334bd8e3 408
366eb4d7
AW
409(define (compute-dom-levels idoms)
410 (let ((dom-levels (make-vector (vector-length idoms) #f)))
3aee6cfd
AW
411 (define (compute-dom-level n)
412 (or (vector-ref dom-levels n)
413 (let ((dom-level (1+ (compute-dom-level (vector-ref idoms n)))))
414 (vector-set! dom-levels n dom-level)
415 dom-level)))
416 (vector-set! dom-levels 0 0)
417 (let lp ((n 0))
366eb4d7
AW
418 (when (< n (vector-length idoms))
419 (compute-dom-level n)
420 (lp (1+ n))))
421 dom-levels))
3aee6cfd 422
366eb4d7
AW
423(define (compute-idoms preds)
424 (let ((idoms (make-vector (vector-length preds) 0)))
3aee6cfd
AW
425 (define (common-idom d0 d1)
426 ;; We exploit the fact that a reverse post-order is a topological
427 ;; sort, and so the idom of a node is always numerically less than
428 ;; the node itself.
429 (cond
430 ((= d0 d1) d0)
431 ((< d0 d1) (common-idom d0 (vector-ref idoms d1)))
432 (else (common-idom (vector-ref idoms d0) d1))))
433 (define (compute-idom preds)
434 (match preds
435 (() 0)
436 ((pred . preds)
437 (let lp ((idom pred) (preds preds))
438 (match preds
439 (() idom)
440 ((pred . preds)
441 (lp (common-idom idom pred) preds)))))))
442 ;; This is the iterative O(n^2) fixpoint algorithm, originally from
443 ;; Allen and Cocke ("Graph-theoretic constructs for program flow
444 ;; analysis", 1972). See the discussion in Cooper, Harvey, and
445 ;; Kennedy's "A Simple, Fast Dominance Algorithm", 2001.
446 (let iterate ((n 0) (changed? #f))
447 (cond
448 ((< n (vector-length preds))
449 (let ((idom (vector-ref idoms n))
450 (idom* (compute-idom (vector-ref preds n))))
451 (cond
452 ((eqv? idom idom*)
453 (iterate (1+ n) changed?))
454 (else
455 (vector-set! idoms n idom*)
456 (iterate (1+ n) #t)))))
457 (changed?
458 (iterate 0 #f))
366eb4d7
AW
459 (else idoms)))))
460
96b8027c
AW
461;; Compute a vector containing, for each node, a list of the nodes that
462;; it immediately dominates. These are the "D" edges in the DJ tree.
463(define (compute-dom-edges idoms)
464 (let ((doms (make-vector (vector-length idoms) '())))
465 (let lp ((n 0))
466 (when (< n (vector-length idoms))
467 (let ((idom (vector-ref idoms n)))
468 (vector-push! doms idom n))
469 (lp (1+ n))))
470 doms))
471
472;; Compute a vector containing, for each node, a list of the successors
473;; of that node that are not dominated by that node. These are the "J"
474;; edges in the DJ tree.
475(define (compute-join-edges preds idoms)
476 (define (dominates? n1 n2)
477 (or (= n1 n2)
478 (and (< n1 n2)
479 (dominates? n1 (vector-ref idoms n2)))))
480 (let ((joins (make-vector (vector-length idoms) '())))
481 (let lp ((n 0))
482 (when (< n (vector-length preds))
483 (for-each (lambda (pred)
484 (unless (dominates? pred n)
485 (vector-push! joins pred n)))
486 (vector-ref preds n))
487 (lp (1+ n))))
488 joins))
489
490;; Compute a vector containing, for each node, a list of the back edges
491;; to that node. If a node is not the entry of a reducible loop, that
492;; list is empty.
493(define (compute-reducible-back-edges joins idoms)
494 (define (dominates? n1 n2)
495 (or (= n1 n2)
496 (and (< n1 n2)
497 (dominates? n1 (vector-ref idoms n2)))))
498 (let ((back-edges (make-vector (vector-length idoms) '())))
499 (let lp ((n 0))
500 (when (< n (vector-length joins))
501 (for-each (lambda (succ)
502 (when (dominates? succ n)
503 (vector-push! back-edges succ n)))
504 (vector-ref joins n))
505 (lp (1+ n))))
506 back-edges))
507
508;; Compute the levels in the dominator tree at which there are
509;; irreducible loops, as an integer. If a bit N is set in the integer,
510;; that indicates that at level N in the dominator tree, there is at
511;; least one irreducible loop.
512(define (compute-irreducible-dom-levels doms joins idoms dom-levels)
366eb4d7
AW
513 (define (dominates? n1 n2)
514 (or (= n1 n2)
515 (and (< n1 n2)
516 (dominates? n1 (vector-ref idoms n2)))))
96b8027c
AW
517 (let ((pre-order (make-vector (vector-length doms) #f))
518 (last-pre-order (make-vector (vector-length doms) #f))
519 (res 0)
520 (count 0))
521 ;; Is MAYBE-PARENT an ancestor of N on the depth-first spanning tree
522 ;; computed from the DJ graph? See Havlak 1997, "Nesting of
523 ;; Reducible and Irreducible Loops".
524 (define (ancestor? a b)
525 (let ((w (vector-ref pre-order a))
526 (v (vector-ref pre-order b)))
527 (and (<= w v)
528 (<= v (vector-ref last-pre-order w)))))
529 ;; Compute depth-first spanning tree of DJ graph.
530 (define (recurse n)
531 (unless (vector-ref pre-order n)
532 (visit n)))
533 (define (visit n)
534 ;; Pre-order visitation index.
535 (vector-set! pre-order n count)
536 (set! count (1+ count))
537 (for-each recurse (vector-ref doms n))
538 (for-each recurse (vector-ref joins n))
539 ;; Pre-order visitation index of last descendant.
540 (vector-set! last-pre-order (vector-ref pre-order n) (1- count)))
541
542 (visit 0)
543
544 (let lp ((n 0))
545 (when (< n (vector-length joins))
546 (for-each (lambda (succ)
547 ;; If this join edge is not a loop back edge but it
548 ;; does go to an ancestor on the DFST of the DJ
549 ;; graph, then we have an irreducible loop.
550 (when (and (not (dominates? succ n))
551 (ancestor? succ n))
552 (set! res (logior (ash 1 (vector-ref dom-levels succ))))))
553 (vector-ref joins n))
554 (lp (1+ n))))
555
556 res))
557
558(define (compute-nodes-by-level dom-levels)
559 (let* ((max-level (let lp ((n 0) (max-level 0))
560 (if (< n (vector-length dom-levels))
561 (lp (1+ n) (max (vector-ref dom-levels n) max-level))
562 max-level)))
563 (nodes-by-level (make-vector (1+ max-level) '())))
564 (let lp ((n (1- (vector-length dom-levels))))
565 (when (>= n 0)
566 (vector-push! nodes-by-level (vector-ref dom-levels n) n)
567 (lp (1- n))))
568 nodes-by-level))
569
570;; Collect all predecessors to the back-nodes that are strictly
571;; dominated by the loop header, and mark them as belonging to the loop.
572;; If they already have a loop header, that means they are either in a
573;; nested loop, or they have already been visited already.
574(define (mark-loop-body header back-nodes preds idoms loop-headers)
575 (define (strictly-dominates? n1 n2)
576 (and (< n1 n2)
577 (let ((idom (vector-ref idoms n2)))
578 (or (= n1 idom)
579 (strictly-dominates? n1 idom)))))
580 (define (visit node)
581 (when (strictly-dominates? header node)
582 (cond
583 ((vector-ref loop-headers node) => visit)
584 (else
585 (vector-set! loop-headers node header)
586 (for-each visit (vector-ref preds node))))))
587 (for-each visit back-nodes))
588
589(define (mark-irreducible-loops level idoms dom-levels loop-headers)
590 ;; FIXME: Identify strongly-connected components that are >= LEVEL in
591 ;; the dominator tree, and somehow mark them as irreducible.
592 (warn 'irreducible-loops-at-level level))
593
594;; "Identifying Loops Using DJ Graphs" by Sreedhar, Gao, and Lee, ACAPS
595;; Technical Memo 98, 1995.
596(define (identify-loops preds idoms dom-levels)
597 (let* ((doms (compute-dom-edges idoms))
598 (joins (compute-join-edges preds idoms))
599 (back-edges (compute-reducible-back-edges joins idoms))
600 (irreducible-levels
601 (compute-irreducible-dom-levels doms joins idoms dom-levels))
602 (loop-headers (make-vector (vector-length preds) #f))
603 (nodes-by-level (compute-nodes-by-level dom-levels)))
604 (let lp ((level (1- (vector-length nodes-by-level))))
605 (when (>= level 0)
606 (for-each (lambda (n)
607 (let ((edges (vector-ref back-edges n)))
608 (unless (null? edges)
609 (mark-loop-body n edges preds idoms loop-headers))))
610 (vector-ref nodes-by-level level))
611 (when (logbit? level irreducible-levels)
612 (mark-irreducible-loops level idoms dom-levels loop-headers))
613 (lp (1- level))))
614 loop-headers))
366eb4d7 615
dda5fd94
AW
616(define (analyze-dominators cfa)
617 (match cfa
618 (($ $cfa k-map order preds)
619 (let* ((idoms (compute-idoms preds))
620 (dom-levels (compute-dom-levels idoms))
621 (loop-headers (identify-loops preds idoms dom-levels)))
622 (make-dominator-analysis cfa idoms dom-levels loop-headers #f)))))
3aee6cfd 623
db11440d
AW
624
625;; Compute the maximum fixed point of the data-flow constraint problem.
626;;
627;; This always completes, as the graph is finite and the in and out sets
628;; are complete semi-lattices. If the graph is reducible and the blocks
629;; are sorted in reverse post-order, this completes in a maximum of LC +
630;; 2 iterations, where LC is the loop connectedness number. See Hecht
631;; and Ullman, "Analysis of a simple algorithm for global flow
632;; problems", POPL 1973, or the recent summary in "Notes on graph
633;; algorithms used in optimizing compilers", Offner 2013.
634(define (compute-maximum-fixed-point preds inv outv killv genv union?)
635 (define (bitvector-copy! dst src)
636 (bitvector-fill! dst #f)
637 (bit-set*! dst src #t))
638 (define (bitvector-meet! accum src)
639 (bit-set*! accum src union?))
640 (let lp ((n 0) (changed? #f))
641 (cond
642 ((< n (vector-length preds))
643 (let ((in (vector-ref inv n))
644 (out (vector-ref outv n))
645 (kill (vector-ref killv n))
646 (gen (vector-ref genv n)))
647 (let ((out-count (or changed? (bit-count #t out))))
648 (for-each
649 (lambda (pred)
650 (bitvector-meet! in (vector-ref outv pred)))
651 (vector-ref preds n))
652 (bitvector-copy! out in)
653 (for-each (cut bitvector-set! out <> #f) kill)
654 (for-each (cut bitvector-set! out <> #t) gen)
655 (lp (1+ n)
656 (or changed? (not (eqv? out-count (bit-count #t out))))))))
657 (changed?
658 (lp 0 #f)))))
659
660;; Data-flow analysis.
661(define-record-type $dfa
7c4977e6 662 (make-dfa cfa min-var var-count in out)
db11440d 663 dfa?
f235f926
AW
664 ;; CFA, for its reverse-post-order numbering
665 (cfa dfa-cfa)
7c4977e6
AW
666 ;; Minimum var in this function.
667 (min-var dfa-min-var)
668 ;; Minimum var in this function.
669 (var-count dfa-var-count)
db11440d
AW
670 ;; Vector of k-idx -> bitvector
671 (in dfa-in)
672 ;; Vector of k-idx -> bitvector
673 (out dfa-out))
674
675(define (dfa-k-idx dfa k)
f235f926 676 (cfa-k-idx (dfa-cfa dfa) k))
db11440d
AW
677
678(define (dfa-k-sym dfa idx)
f235f926 679 (cfa-k-sym (dfa-cfa dfa) idx))
db11440d
AW
680
681(define (dfa-k-count dfa)
f235f926 682 (cfa-k-count (dfa-cfa dfa)))
db11440d
AW
683
684(define (dfa-var-idx dfa var)
7c4977e6
AW
685 (let ((idx (- var (dfa-min-var dfa))))
686 (unless (< -1 idx (dfa-var-count dfa))
687 (error "var out of range" var))
688 idx))
db11440d 689
db11440d 690(define (dfa-var-sym dfa idx)
7c4977e6
AW
691 (unless (< -1 idx (dfa-var-count dfa))
692 (error "idx out of range" idx))
693 (+ idx (dfa-min-var dfa)))
db11440d
AW
694
695(define (dfa-k-in dfa idx)
696 (vector-ref (dfa-in dfa) idx))
697
698(define (dfa-k-out dfa idx)
699 (vector-ref (dfa-out dfa) idx))
700
5bff3125 701(define (compute-live-variables fun dfg)
7c4977e6
AW
702 (unless (and (= (vector-length (dfg-uses dfg)) (dfg-var-count dfg))
703 (= (vector-length (dfg-cont-table dfg)) (dfg-label-count dfg)))
704 (error "function needs renumbering"))
705 (let* ((min-var (dfg-min-var dfg))
a8430ab1 706 (nvars (dfg-var-count dfg))
5e897908
AW
707 (cfa (analyze-control-flow fun dfg #:reverse? #t
708 #:add-handler-preds? #t))
5e897908
AW
709 (usev (make-vector (cfa-k-count cfa) '()))
710 (defv (make-vector (cfa-k-count cfa) '()))
711 (live-in (make-vector (cfa-k-count cfa) #f))
712 (live-out (make-vector (cfa-k-count cfa) #f)))
7c4977e6
AW
713 (define (var->idx var) (- var min-var))
714 (define (idx->var idx) (+ idx min-var))
715
716 ;; Initialize defv and usev.
98c5b69f 717 (let ((defs (dfg-defs dfg))
7c4977e6 718 (uses (dfg-uses dfg)))
5e897908 719 (let lp ((n 0))
98c5b69f
AW
720 (when (< n (vector-length defs))
721 (let ((def (vector-ref defs n)))
7c4977e6
AW
722 (unless def
723 (error "internal error -- var array not packed"))
724 (for-each (lambda (def)
725 (vector-push! defv (cfa-k-idx cfa def) n))
726 (lookup-predecessors def dfg))
727 (for-each (lambda (use)
728 (vector-push! usev (cfa-k-idx cfa use) n))
729 (vector-ref uses n))
730 (lp (1+ n))))))
5e897908
AW
731
732 ;; Initialize live-in and live-out sets.
733 (let lp ((n 0))
734 (when (< n (vector-length live-out))
735 (vector-set! live-in n (make-bitvector nvars #f))
736 (vector-set! live-out n (make-bitvector nvars #f))
737 (lp (1+ n))))
738
739 ;; Liveness is a reverse data-flow problem, so we give
740 ;; compute-maximum-fixed-point a reversed graph, swapping in
741 ;; for out, and usev for defv. Note that since we are using
742 ;; a reverse CFA, cfa-preds are actually successors, and
743 ;; continuation 0 is ktail.
744 (compute-maximum-fixed-point (cfa-preds cfa)
745 live-out live-in defv usev #t)
746
7c4977e6 747 (make-dfa cfa min-var nvars live-in live-out)))
db11440d
AW
748
749(define (print-dfa dfa)
750 (match dfa
7c4977e6 751 (($ $dfa cfa min-var in out)
db11440d
AW
752 (define (print-var-set bv)
753 (let lp ((n 0))
754 (let ((n (bit-position #t bv n)))
755 (when n
7c4977e6 756 (format #t " ~A" (+ n min-var))
db11440d
AW
757 (lp (1+ n))))))
758 (let lp ((n 0))
f235f926
AW
759 (when (< n (cfa-k-count cfa))
760 (format #t "~A:\n" (cfa-k-sym cfa n))
db11440d
AW
761 (format #t " in:")
762 (print-var-set (vector-ref in n))
763 (newline)
764 (format #t " out:")
765 (print-var-set (vector-ref out n))
766 (newline)
767 (lp (1+ n)))))))
768
4bf757b8 769(define (visit-fun fun conts preds defs uses scopes scope-levels
5fc40391 770 min-label min-var global?)
cec43eb8 771 (define (add-def! var def-k)
98c5b69f 772 (vector-set! defs (- var min-var) def-k))
6e8ad823 773
5e897908 774 (define (add-use! var use-k)
98c5b69f 775 (vector-push! uses (- var min-var) use-k))
6e8ad823 776
f22979db
AW
777 (define* (declare-block! label cont parent
778 #:optional (level
5fc40391
AW
779 (1+ (vector-ref
780 scope-levels
781 (- parent min-label)))))
5e897908 782 (vector-set! conts (- label min-label) cont)
5fc40391
AW
783 (vector-set! scopes (- label min-label) parent)
784 (vector-set! scope-levels (- label min-label) level))
f22979db
AW
785
786 (define (link-blocks! pred succ)
21d6d183 787 (vector-push! preds (- succ min-label) pred))
6e8ad823
AW
788
789 (define (visit exp exp-k)
cec43eb8
AW
790 (define (def! sym)
791 (add-def! sym exp-k))
6e8ad823
AW
792 (define (use! sym)
793 (add-use! sym exp-k))
3aee6cfd
AW
794 (define (use-k! k)
795 (link-blocks! exp-k k))
6e8ad823
AW
796 (define (recur exp)
797 (visit exp exp-k))
798 (match exp
6e422a35 799 (($ $letk (($ $cont k cont) ...) body)
6e8ad823 800 ;; Set up recursive environment before visiting cont bodies.
48c2a539
AW
801 (for-each/2 (lambda (cont k)
802 (declare-block! k cont exp-k))
803 cont k)
804 (for-each/2 visit cont k)
6e8ad823
AW
805 (recur body))
806
807 (($ $kargs names syms body)
cec43eb8 808 (for-each def! syms)
6e8ad823
AW
809 (recur body))
810
811 (($ $kif kt kf)
f22979db
AW
812 (use-k! kt)
813 (use-k! kf))
6e8ad823 814
36527695 815 (($ $kreceive arity k)
f22979db 816 (use-k! k))
6e8ad823
AW
817
818 (($ $letrec names syms funs body)
819 (unless global?
820 (error "$letrec should not be present when building a local DFG"))
cec43eb8 821 (for-each def! syms)
5e897908 822 (for-each
4bf757b8 823 (cut visit-fun <> conts preds defs uses scopes scope-levels
5fc40391 824 min-label min-var global?)
5e897908 825 funs)
6e8ad823
AW
826 (visit body exp-k))
827
6e422a35 828 (($ $continue k src exp)
f22979db 829 (use-k! k)
6e8ad823 830 (match exp
6e8ad823
AW
831 (($ $call proc args)
832 (use! proc)
833 (for-each use! args))
834
b3ae2b50
AW
835 (($ $callk k proc args)
836 (use! proc)
837 (for-each use! args))
838
6e8ad823
AW
839 (($ $primcall name args)
840 (for-each use! args))
841
842 (($ $values args)
843 (for-each use! args))
844
7ab76a83 845 (($ $prompt escape? tag handler)
6e8ad823 846 (use! tag)
146ce52d 847 (use-k! handler))
6e8ad823
AW
848
849 (($ $fun)
850 (when global?
4bf757b8 851 (visit-fun exp conts preds defs uses scopes scope-levels
5fc40391 852 min-label min-var global?)))
6e8ad823
AW
853
854 (_ #f)))))
855
856 (match fun
6e422a35
AW
857 (($ $fun src meta free
858 ($ $cont kentry
6e8ad823 859 (and entry
90dce16d 860 ($ $kentry self ($ $cont ktail tail) clause))))
f22979db 861 (declare-block! kentry entry #f 0)
cec43eb8 862 (add-def! self kentry)
6e8ad823 863
f22979db 864 (declare-block! ktail tail kentry)
6e8ad823 865
90dce16d
AW
866 (let lp ((clause clause))
867 (match clause
868 (#f #t)
869 (($ $cont kclause
870 (and clause ($ $kclause arity ($ $cont kbody body)
871 alternate)))
872 (declare-block! kclause clause kentry)
873 (link-blocks! kentry kclause)
6e8ad823 874
90dce16d
AW
875 (declare-block! kbody body kclause)
876 (link-blocks! kclause kbody)
6e8ad823 877
90dce16d
AW
878 (visit body kbody)
879 (lp alternate)))))))
6e8ad823 880
5e897908
AW
881(define (compute-label-and-var-ranges fun global?)
882 (define (min* a b)
883 (if b (min a b) a))
884 ((make-cont-folder global?
885 min-label max-label label-count
886 min-var max-var var-count)
887 (lambda (label cont
888 min-label max-label label-count
889 min-var max-var var-count)
890 (let ((min-label (min* label min-label))
891 (max-label (max label max-label)))
892 (match cont
893 (($ $kargs names vars)
894 (values min-label max-label (1+ label-count)
de3cbadc
AW
895 (cond (min-var (fold min min-var vars))
896 ((pair? vars) (fold min (car vars) (cdr vars)))
5e897908 897 (else min-var))
de3cbadc 898 (fold max max-var vars)
5e897908
AW
899 (+ var-count (length vars))))
900 (($ $kentry self)
901 (values min-label max-label (1+ label-count)
902 (min* self min-var) (max self max-var) (1+ var-count)))
903 (_ (values min-label max-label (1+ label-count)
904 min-var max-var var-count)))))
905 fun
906 #f -1 0 #f -1 0))
907
6e8ad823 908(define* (compute-dfg fun #:key (global? #t))
5e897908
AW
909 (call-with-values (lambda () (compute-label-and-var-ranges fun global?))
910 (lambda (min-label max-label label-count min-var max-var var-count)
911 (when (or (zero? label-count) (zero? var-count))
912 (error "internal error (no vars or labels for fun?)"))
913 (let* ((nlabels (- (1+ max-label) min-label))
914 (nvars (- (1+ max-var) min-var))
915 (conts (make-vector nlabels #f))
21d6d183 916 (preds (make-vector nlabels '()))
98c5b69f 917 (defs (make-vector nvars #f))
5fc40391
AW
918 (uses (make-vector nvars '()))
919 (scopes (make-vector nlabels #f))
920 (scope-levels (make-vector nlabels #f)))
4bf757b8 921 (visit-fun fun conts preds defs uses scopes scope-levels
5fc40391 922 min-label min-var global?)
4bf757b8 923 (make-dfg conts preds defs uses scopes scope-levels
5e897908
AW
924 min-label label-count min-var var-count)))))
925
f49e994b
AW
926(define (lookup-cont label dfg)
927 (let ((res (vector-ref (dfg-cont-table dfg) (- label (dfg-min-label dfg)))))
928 (unless res
929 (error "Unknown continuation!" label))
930 res))
931
5fc40391
AW
932(define (lookup-predecessors k dfg)
933 (vector-ref (dfg-preds dfg) (- k (dfg-min-label dfg))))
934
935(define (lookup-successors k dfg)
2c3c086e
AW
936 (let ((cont (vector-ref (dfg-cont-table dfg) (- k (dfg-min-label dfg)))))
937 (visit-cont-successors list cont)))
6e8ad823 938
5e897908 939(define (lookup-def var dfg)
f49e994b 940 (vector-ref (dfg-defs dfg) (- var (dfg-min-var dfg))))
6e8ad823 941
5e897908 942(define (lookup-uses var dfg)
f49e994b 943 (vector-ref (dfg-uses dfg) (- var (dfg-min-var dfg))))
6e8ad823 944
c8ad7426 945(define (lookup-block-scope k dfg)
5fc40391 946 (vector-ref (dfg-scopes dfg) (- k (dfg-min-label dfg))))
f22979db 947
5fc40391
AW
948(define (lookup-scope-level k dfg)
949 (vector-ref (dfg-scope-levels dfg) (- k (dfg-min-label dfg))))
f22979db 950
6e8ad823 951(define (find-defining-term sym dfg)
f22979db 952 (match (lookup-predecessors (lookup-def sym dfg) dfg)
6e8ad823 953 ((def-exp-k)
fbdb69b2 954 (lookup-cont def-exp-k dfg))
6e8ad823
AW
955 (else #f)))
956
957(define (find-call term)
958 (match term
959 (($ $kargs names syms body) (find-call body))
960 (($ $letk conts body) (find-call body))
961 (($ $letrec names syms funs body) (find-call body))
962 (($ $continue) term)))
963
964(define (call-expression call)
965 (match call
6e422a35 966 (($ $continue k src exp) exp)))
6e8ad823
AW
967
968(define (find-expression term)
969 (call-expression (find-call term)))
970
971(define (find-defining-expression sym dfg)
972 (match (find-defining-term sym dfg)
973 (#f #f)
36527695 974 (($ $kreceive) #f)
f22979db 975 (($ $kclause) #f)
6e8ad823
AW
976 (term (find-expression term))))
977
978(define (find-constant-value sym dfg)
979 (match (find-defining-expression sym dfg)
980 (($ $const val)
981 (values #t val))
6e422a35 982 (($ $continue k src ($ $void))
6e8ad823
AW
983 (values #t *unspecified*))
984 (else
985 (values #f #f))))
986
987(define (constant-needs-allocation? sym val dfg)
607fe5a6
AW
988 (define (immediate-u8? val)
989 (and (integer? val) (exact? val) (<= 0 val 255)))
990
6e8ad823
AW
991 (define (find-exp term)
992 (match term
993 (($ $kargs names syms body) (find-exp body))
994 (($ $letk conts body) (find-exp body))
995 (else term)))
f49e994b
AW
996
997 (or-map
998 (lambda (use)
999 (match (find-expression (lookup-cont use dfg))
1000 (($ $call) #f)
1001 (($ $callk) #f)
1002 (($ $values) #f)
1003 (($ $primcall 'free-ref (closure slot))
1004 (not (eq? sym slot)))
1005 (($ $primcall 'free-set! (closure slot value))
1006 (not (eq? sym slot)))
1007 (($ $primcall 'cache-current-module! (mod . _))
1008 (eq? sym mod))
1009 (($ $primcall 'cached-toplevel-box _)
1010 #f)
1011 (($ $primcall 'cached-module-box _)
1012 #f)
1013 (($ $primcall 'resolve (name bound?))
1014 (eq? sym name))
1015 (($ $primcall 'make-vector/immediate (len init))
1016 (not (eq? sym len)))
1017 (($ $primcall 'vector-ref/immediate (v i))
1018 (not (eq? sym i)))
1019 (($ $primcall 'vector-set!/immediate (v i x))
1020 (not (eq? sym i)))
1021 (($ $primcall 'allocate-struct/immediate (vtable nfields))
1022 (not (eq? sym nfields)))
1023 (($ $primcall 'struct-ref/immediate (s n))
1024 (not (eq? sym n)))
1025 (($ $primcall 'struct-set!/immediate (s n x))
1026 (not (eq? sym n)))
1027 (($ $primcall 'builtin-ref (idx))
1028 #f)
1029 (_ #t)))
1030 (vector-ref (dfg-uses dfg) (- sym (dfg-min-var dfg)))))
6e8ad823 1031
5e897908
AW
1032(define (continuation-scope-contains? scope-k k dfg)
1033 (let ((scope-level (lookup-scope-level scope-k dfg)))
f22979db
AW
1034 (let lp ((k k))
1035 (or (eq? scope-k k)
5fc40391
AW
1036 (and (< scope-level (lookup-scope-level k dfg))
1037 (lp (lookup-block-scope k dfg)))))))
f22979db 1038
f22979db 1039(define (continuation-bound-in? k use-k dfg)
21d6d183 1040 (continuation-scope-contains? (lookup-block-scope k dfg) use-k dfg))
d51fb1e6
AW
1041
1042(define (variable-free-in? var k dfg)
5e897908
AW
1043 (or-map (lambda (use)
1044 (continuation-scope-contains? k use dfg))
1045 (lookup-uses var dfg)))
6e8ad823 1046
e636f424 1047;; A continuation is a control point if it has multiple predecessors, or
a3a45279 1048;; if its single predecessor does not have a single successor.
e636f424
AW
1049(define (control-point? k dfg)
1050 (match (lookup-predecessors k dfg)
1051 ((pred)
2c3c086e
AW
1052 (let ((cont (vector-ref (dfg-cont-table dfg)
1053 (- pred (dfg-min-label dfg)))))
1054 (visit-cont-successors (case-lambda
1055 (() #t)
1056 ((succ0) #f)
1057 ((succ1 succ2) #t))
1058 cont)))
e636f424 1059 (_ #t)))
6e8ad823
AW
1060
1061(define (lookup-bound-syms k dfg)
fbdb69b2
AW
1062 (match (lookup-cont k dfg)
1063 (($ $kargs names syms body)
1064 syms)))