VM has "builtins": primitives addressable by emitted RTL code
[bpt/guile.git] / module / language / cps / dfg.scm
CommitLineData
6e8ad823
AW
1;;; Continuation-passing style (CPS) intermediate language (IL)
2
3;; Copyright (C) 2013 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;;; 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
44 build-local-cont-table
45 lookup-cont
46
47 compute-dfg
48 dfg-cont-table
49 lookup-def
50 lookup-uses
f22979db
AW
51 lookup-predecessors
52 lookup-successors
c8ad7426 53 lookup-block-scope
6e8ad823
AW
54 find-call
55 call-expression
56 find-expression
57 find-defining-expression
58 find-constant-value
f22979db 59 continuation-bound-in?
d51fb1e6 60 variable-free-in?
6e8ad823 61 constant-needs-allocation?
e636f424 62 control-point?
db11440d
AW
63 lookup-bound-syms
64
65 ;; Data flow analysis.
66 compute-live-variables
67 dfa-k-idx dfa-k-sym dfa-k-count dfa-k-in dfa-k-out
68 dfa-var-idx dfa-var-name dfa-var-sym dfa-var-count
69 print-dfa))
6e8ad823
AW
70
71(define (build-cont-table fun)
72 (fold-conts (lambda (k src cont table)
73 (hashq-set! table k cont)
74 table)
75 (make-hash-table)
76 fun))
77
78(define (build-local-cont-table cont)
79 (fold-local-conts (lambda (k src cont table)
80 (hashq-set! table k cont)
81 table)
82 (make-hash-table)
83 cont))
84
85(define (lookup-cont sym conts)
86 (let ((res (hashq-ref conts sym)))
87 (unless res
88 (error "Unknown continuation!" sym (hash-fold acons '() conts)))
89 res))
90
91;; Data-flow graph for CPS: both for values and continuations.
92(define-record-type $dfg
f22979db 93 (make-dfg conts blocks use-maps)
6e8ad823 94 dfg?
f22979db 95 ;; hash table of sym -> $kif, $kargs, etc
6e8ad823 96 (conts dfg-cont-table)
f22979db
AW
97 ;; hash table of sym -> $block
98 (blocks dfg-blocks)
6e8ad823 99 ;; hash table of sym -> $use-map
f22979db 100 (use-maps dfg-use-maps))
6e8ad823
AW
101
102(define-record-type $use-map
fc95a944 103 (make-use-map name sym def uses)
6e8ad823 104 use-map?
fc95a944 105 (name use-map-name)
6e8ad823
AW
106 (sym use-map-sym)
107 (def use-map-def)
108 (uses use-map-uses set-use-map-uses!))
109
f22979db 110(define-record-type $block
0e2446d4
AW
111 (%make-block scope scope-level preds succs
112 idom dom-level
113 pdom pdom-level
114 loop-header irreducible)
f22979db
AW
115 block?
116 (scope block-scope set-block-scope!)
117 (scope-level block-scope-level set-block-scope-level!)
118 (preds block-preds set-block-preds!)
119 (succs block-succs set-block-succs!)
120 (idom block-idom set-block-idom!)
121 (dom-level block-dom-level set-block-dom-level!)
366eb4d7 122
0e2446d4
AW
123 (pdom block-pdom set-block-pdom!)
124 (pdom-level block-pdom-level set-block-pdom-level!)
125
366eb4d7
AW
126 ;; The loop header of this block, if this block is part of a reducible
127 ;; loop. Otherwise #f.
128 (loop-header block-loop-header set-block-loop-header!)
129
130 ;; Some sort of marker that this block is part of an irreducible
131 ;; (multi-entry) loop. Otherwise #f.
132 (irreducible block-irreducible set-block-irreducible!))
f22979db
AW
133
134(define (make-block scope scope-level)
0e2446d4 135 (%make-block scope scope-level '() '() #f #f #f #f #f #f))
f22979db 136
0e2446d4 137(define (reverse-post-order k0 blocks accessor)
3aee6cfd
AW
138 (let ((order '())
139 (visited? (make-hash-table)))
140 (let visit ((k k0))
141 (hashq-set! visited? k #t)
0e2446d4
AW
142 (for-each (lambda (k)
143 (unless (hashq-ref visited? k)
144 (visit k)))
145 (accessor (lookup-block k blocks)))
146 (set! order (cons k order)))
366eb4d7 147 (list->vector order)))
3aee6cfd 148
0e2446d4 149(define (convert-predecessors order blocks accessor)
366eb4d7
AW
150 (let* ((mapping (make-hash-table))
151 (preds-vec (make-vector (vector-length order) #f)))
152 (let lp ((n 0))
153 (when (< n (vector-length order))
154 (hashq-set! mapping (vector-ref order n) n)
155 (lp (1+ n))))
156 (let lp ((n 0))
157 (when (< n (vector-length order))
0e2446d4
AW
158 (let ((preds (accessor (lookup-block (vector-ref order n) blocks))))
159 (vector-set! preds-vec n
160 ;; It's possible for a predecessor to not be in
161 ;; the mapping, if the predecessor is not
162 ;; reachable from the entry node.
163 (filter-map (cut hashq-ref mapping <>) preds))
164 (lp (1+ n)))))
3aee6cfd
AW
165 preds-vec))
166
366eb4d7
AW
167(define (compute-dom-levels idoms)
168 (let ((dom-levels (make-vector (vector-length idoms) #f)))
3aee6cfd
AW
169 (define (compute-dom-level n)
170 (or (vector-ref dom-levels n)
171 (let ((dom-level (1+ (compute-dom-level (vector-ref idoms n)))))
172 (vector-set! dom-levels n dom-level)
173 dom-level)))
174 (vector-set! dom-levels 0 0)
175 (let lp ((n 0))
366eb4d7
AW
176 (when (< n (vector-length idoms))
177 (compute-dom-level n)
178 (lp (1+ n))))
179 dom-levels))
3aee6cfd 180
366eb4d7
AW
181(define (compute-idoms preds)
182 (let ((idoms (make-vector (vector-length preds) 0)))
3aee6cfd
AW
183 (define (common-idom d0 d1)
184 ;; We exploit the fact that a reverse post-order is a topological
185 ;; sort, and so the idom of a node is always numerically less than
186 ;; the node itself.
187 (cond
188 ((= d0 d1) d0)
189 ((< d0 d1) (common-idom d0 (vector-ref idoms d1)))
190 (else (common-idom (vector-ref idoms d0) d1))))
191 (define (compute-idom preds)
192 (match preds
193 (() 0)
194 ((pred . preds)
195 (let lp ((idom pred) (preds preds))
196 (match preds
197 (() idom)
198 ((pred . preds)
199 (lp (common-idom idom pred) preds)))))))
200 ;; This is the iterative O(n^2) fixpoint algorithm, originally from
201 ;; Allen and Cocke ("Graph-theoretic constructs for program flow
202 ;; analysis", 1972). See the discussion in Cooper, Harvey, and
203 ;; Kennedy's "A Simple, Fast Dominance Algorithm", 2001.
204 (let iterate ((n 0) (changed? #f))
205 (cond
206 ((< n (vector-length preds))
207 (let ((idom (vector-ref idoms n))
208 (idom* (compute-idom (vector-ref preds n))))
209 (cond
210 ((eqv? idom idom*)
211 (iterate (1+ n) changed?))
212 (else
213 (vector-set! idoms n idom*)
214 (iterate (1+ n) #t)))))
215 (changed?
216 (iterate 0 #f))
366eb4d7
AW
217 (else idoms)))))
218
96b8027c
AW
219(define-inlinable (vector-push! vec idx val)
220 (let ((v vec) (i idx))
221 (vector-set! v i (cons val (vector-ref v i)))))
222
223;; Compute a vector containing, for each node, a list of the nodes that
224;; it immediately dominates. These are the "D" edges in the DJ tree.
225(define (compute-dom-edges idoms)
226 (let ((doms (make-vector (vector-length idoms) '())))
227 (let lp ((n 0))
228 (when (< n (vector-length idoms))
229 (let ((idom (vector-ref idoms n)))
230 (vector-push! doms idom n))
231 (lp (1+ n))))
232 doms))
233
234;; Compute a vector containing, for each node, a list of the successors
235;; of that node that are not dominated by that node. These are the "J"
236;; edges in the DJ tree.
237(define (compute-join-edges preds idoms)
238 (define (dominates? n1 n2)
239 (or (= n1 n2)
240 (and (< n1 n2)
241 (dominates? n1 (vector-ref idoms n2)))))
242 (let ((joins (make-vector (vector-length idoms) '())))
243 (let lp ((n 0))
244 (when (< n (vector-length preds))
245 (for-each (lambda (pred)
246 (unless (dominates? pred n)
247 (vector-push! joins pred n)))
248 (vector-ref preds n))
249 (lp (1+ n))))
250 joins))
251
252;; Compute a vector containing, for each node, a list of the back edges
253;; to that node. If a node is not the entry of a reducible loop, that
254;; list is empty.
255(define (compute-reducible-back-edges joins idoms)
256 (define (dominates? n1 n2)
257 (or (= n1 n2)
258 (and (< n1 n2)
259 (dominates? n1 (vector-ref idoms n2)))))
260 (let ((back-edges (make-vector (vector-length idoms) '())))
261 (let lp ((n 0))
262 (when (< n (vector-length joins))
263 (for-each (lambda (succ)
264 (when (dominates? succ n)
265 (vector-push! back-edges succ n)))
266 (vector-ref joins n))
267 (lp (1+ n))))
268 back-edges))
269
270;; Compute the levels in the dominator tree at which there are
271;; irreducible loops, as an integer. If a bit N is set in the integer,
272;; that indicates that at level N in the dominator tree, there is at
273;; least one irreducible loop.
274(define (compute-irreducible-dom-levels doms joins idoms dom-levels)
366eb4d7
AW
275 (define (dominates? n1 n2)
276 (or (= n1 n2)
277 (and (< n1 n2)
278 (dominates? n1 (vector-ref idoms n2)))))
96b8027c
AW
279 (let ((pre-order (make-vector (vector-length doms) #f))
280 (last-pre-order (make-vector (vector-length doms) #f))
281 (res 0)
282 (count 0))
283 ;; Is MAYBE-PARENT an ancestor of N on the depth-first spanning tree
284 ;; computed from the DJ graph? See Havlak 1997, "Nesting of
285 ;; Reducible and Irreducible Loops".
286 (define (ancestor? a b)
287 (let ((w (vector-ref pre-order a))
288 (v (vector-ref pre-order b)))
289 (and (<= w v)
290 (<= v (vector-ref last-pre-order w)))))
291 ;; Compute depth-first spanning tree of DJ graph.
292 (define (recurse n)
293 (unless (vector-ref pre-order n)
294 (visit n)))
295 (define (visit n)
296 ;; Pre-order visitation index.
297 (vector-set! pre-order n count)
298 (set! count (1+ count))
299 (for-each recurse (vector-ref doms n))
300 (for-each recurse (vector-ref joins n))
301 ;; Pre-order visitation index of last descendant.
302 (vector-set! last-pre-order (vector-ref pre-order n) (1- count)))
303
304 (visit 0)
305
306 (let lp ((n 0))
307 (when (< n (vector-length joins))
308 (for-each (lambda (succ)
309 ;; If this join edge is not a loop back edge but it
310 ;; does go to an ancestor on the DFST of the DJ
311 ;; graph, then we have an irreducible loop.
312 (when (and (not (dominates? succ n))
313 (ancestor? succ n))
314 (set! res (logior (ash 1 (vector-ref dom-levels succ))))))
315 (vector-ref joins n))
316 (lp (1+ n))))
317
318 res))
319
320(define (compute-nodes-by-level dom-levels)
321 (let* ((max-level (let lp ((n 0) (max-level 0))
322 (if (< n (vector-length dom-levels))
323 (lp (1+ n) (max (vector-ref dom-levels n) max-level))
324 max-level)))
325 (nodes-by-level (make-vector (1+ max-level) '())))
326 (let lp ((n (1- (vector-length dom-levels))))
327 (when (>= n 0)
328 (vector-push! nodes-by-level (vector-ref dom-levels n) n)
329 (lp (1- n))))
330 nodes-by-level))
331
332;; Collect all predecessors to the back-nodes that are strictly
333;; dominated by the loop header, and mark them as belonging to the loop.
334;; If they already have a loop header, that means they are either in a
335;; nested loop, or they have already been visited already.
336(define (mark-loop-body header back-nodes preds idoms loop-headers)
337 (define (strictly-dominates? n1 n2)
338 (and (< n1 n2)
339 (let ((idom (vector-ref idoms n2)))
340 (or (= n1 idom)
341 (strictly-dominates? n1 idom)))))
342 (define (visit node)
343 (when (strictly-dominates? header node)
344 (cond
345 ((vector-ref loop-headers node) => visit)
346 (else
347 (vector-set! loop-headers node header)
348 (for-each visit (vector-ref preds node))))))
349 (for-each visit back-nodes))
350
351(define (mark-irreducible-loops level idoms dom-levels loop-headers)
352 ;; FIXME: Identify strongly-connected components that are >= LEVEL in
353 ;; the dominator tree, and somehow mark them as irreducible.
354 (warn 'irreducible-loops-at-level level))
355
356;; "Identifying Loops Using DJ Graphs" by Sreedhar, Gao, and Lee, ACAPS
357;; Technical Memo 98, 1995.
358(define (identify-loops preds idoms dom-levels)
359 (let* ((doms (compute-dom-edges idoms))
360 (joins (compute-join-edges preds idoms))
361 (back-edges (compute-reducible-back-edges joins idoms))
362 (irreducible-levels
363 (compute-irreducible-dom-levels doms joins idoms dom-levels))
364 (loop-headers (make-vector (vector-length preds) #f))
365 (nodes-by-level (compute-nodes-by-level dom-levels)))
366 (let lp ((level (1- (vector-length nodes-by-level))))
367 (when (>= level 0)
368 (for-each (lambda (n)
369 (let ((edges (vector-ref back-edges n)))
370 (unless (null? edges)
371 (mark-loop-body n edges preds idoms loop-headers))))
372 (vector-ref nodes-by-level level))
373 (when (logbit? level irreducible-levels)
374 (mark-irreducible-loops level idoms dom-levels loop-headers))
375 (lp (1- level))))
376 loop-headers))
366eb4d7 377
0e2446d4
AW
378(define (analyze-control-flow! kentry kexit blocks)
379 ;; First go forward in the graph, computing dominators and loop
380 ;; information.
381 (let* ((order (reverse-post-order kentry blocks block-succs))
382 (preds (convert-predecessors order blocks block-preds))
366eb4d7
AW
383 (idoms (compute-idoms preds))
384 (dom-levels (compute-dom-levels idoms))
385 (loop-headers (identify-loops preds idoms dom-levels)))
386 (let lp ((n 0))
387 (when (< n (vector-length order))
388 (let* ((k (vector-ref order n))
389 (idom (vector-ref idoms n))
0e2446d4 390 (dom-level (vector-ref dom-levels n))
366eb4d7
AW
391 (loop-header (vector-ref loop-headers n))
392 (b (lookup-block k blocks)))
393 (set-block-idom! b (vector-ref order idom))
394 (set-block-dom-level! b dom-level)
395 (set-block-loop-header! b (and loop-header
396 (vector-ref order loop-header)))
0e2446d4
AW
397 (lp (1+ n))))))
398 ;; Then go backwards, computing post-dominators.
399 (let* ((order (reverse-post-order kexit blocks block-preds))
400 (preds (convert-predecessors order blocks block-succs))
401 (idoms (compute-idoms preds))
402 (dom-levels (compute-dom-levels idoms)))
403 (let lp ((n 0))
404 (when (< n (vector-length order))
405 (let* ((k (vector-ref order n))
406 (pdom (vector-ref idoms n))
407 (pdom-level (vector-ref dom-levels n))
408 (b (lookup-block k blocks)))
409 (set-block-pdom! b (vector-ref order pdom))
410 (set-block-pdom-level! b pdom-level)
366eb4d7 411 (lp (1+ n)))))))
3aee6cfd 412
db11440d
AW
413
414;; Compute the maximum fixed point of the data-flow constraint problem.
415;;
416;; This always completes, as the graph is finite and the in and out sets
417;; are complete semi-lattices. If the graph is reducible and the blocks
418;; are sorted in reverse post-order, this completes in a maximum of LC +
419;; 2 iterations, where LC is the loop connectedness number. See Hecht
420;; and Ullman, "Analysis of a simple algorithm for global flow
421;; problems", POPL 1973, or the recent summary in "Notes on graph
422;; algorithms used in optimizing compilers", Offner 2013.
423(define (compute-maximum-fixed-point preds inv outv killv genv union?)
424 (define (bitvector-copy! dst src)
425 (bitvector-fill! dst #f)
426 (bit-set*! dst src #t))
427 (define (bitvector-meet! accum src)
428 (bit-set*! accum src union?))
429 (let lp ((n 0) (changed? #f))
430 (cond
431 ((< n (vector-length preds))
432 (let ((in (vector-ref inv n))
433 (out (vector-ref outv n))
434 (kill (vector-ref killv n))
435 (gen (vector-ref genv n)))
436 (let ((out-count (or changed? (bit-count #t out))))
437 (for-each
438 (lambda (pred)
439 (bitvector-meet! in (vector-ref outv pred)))
440 (vector-ref preds n))
441 (bitvector-copy! out in)
442 (for-each (cut bitvector-set! out <> #f) kill)
443 (for-each (cut bitvector-set! out <> #t) gen)
444 (lp (1+ n)
445 (or changed? (not (eqv? out-count (bit-count #t out))))))))
446 (changed?
447 (lp 0 #f)))))
448
449;; Data-flow analysis.
450(define-record-type $dfa
451 (make-dfa k->idx order var->idx names syms in out)
452 dfa?
453 ;; Function mapping k-sym -> k-idx
454 (k->idx dfa-k->idx)
455 ;; Vector of k-idx -> k-sym
456 (order dfa-order)
457 ;; Function mapping var-sym -> var-idx
458 (var->idx dfa-var->idx)
459 ;; Vector of var-idx -> name
460 (names dfa-names)
461 ;; Vector of var-idx -> var-sym
462 (syms dfa-syms)
463 ;; Vector of k-idx -> bitvector
464 (in dfa-in)
465 ;; Vector of k-idx -> bitvector
466 (out dfa-out))
467
468(define (dfa-k-idx dfa k)
469 ((dfa-k->idx dfa) k))
470
471(define (dfa-k-sym dfa idx)
472 (vector-ref (dfa-order dfa) idx))
473
474(define (dfa-k-count dfa)
475 (vector-length (dfa-order dfa)))
476
477(define (dfa-var-idx dfa var)
478 ((dfa-var->idx dfa) var))
479
480(define (dfa-var-name dfa idx)
481 (vector-ref (dfa-names dfa) idx))
482
483(define (dfa-var-sym dfa idx)
484 (vector-ref (dfa-syms dfa) idx))
485
486(define (dfa-var-count dfa)
487 (vector-length (dfa-syms dfa)))
488
489(define (dfa-k-in dfa idx)
490 (vector-ref (dfa-in dfa) idx))
491
492(define (dfa-k-out dfa idx)
493 (vector-ref (dfa-out dfa) idx))
494
495(define (compute-live-variables ktail dfg)
496 (define (make-variable-mapper use-maps)
497 (let ((mapping (make-hash-table))
498 (n 0))
499 (hash-for-each (lambda (sym use-map)
500 (hashq-set! mapping sym n)
501 (set! n (1+ n)))
502 use-maps)
503 (values (lambda (sym)
504 (or (hashq-ref mapping sym)
505 (error "unknown sym" sym)))
506 n)))
507 (define (make-block-mapper order)
508 (let ((mapping (make-hash-table)))
509 (let lp ((n 0))
510 (when (< n (vector-length order))
511 (hashq-set! mapping (vector-ref order n) n)
512 (lp (1+ n))))
513 (lambda (k)
514 (or (hashq-ref mapping k)
515 (error "unknown k" k)))))
516
517 (call-with-values (lambda () (make-variable-mapper (dfg-use-maps dfg)))
518 (lambda (var->idx nvars)
519 (let* ((blocks (dfg-blocks dfg))
520 (order (reverse-post-order ktail blocks block-preds))
521 (succs (convert-predecessors order blocks block-succs))
522 (k->idx (make-block-mapper order))
523 (syms (make-vector nvars #f))
524 (names (make-vector nvars #f))
525 (usev (make-vector (vector-length order) '()))
526 (defv (make-vector (vector-length order) '()))
527 (live-in (make-vector (vector-length order) #f))
528 (live-out (make-vector (vector-length order) #f)))
529 ;; Initialize syms, names, defv, and usev.
530 (hash-for-each
531 (lambda (sym use-map)
532 (match use-map
533 (($ $use-map name sym def uses)
534 (let ((v (var->idx sym)))
535 (vector-set! syms v sym)
536 (vector-set! names v name)
537 (for-each (lambda (def)
538 (vector-push! defv (k->idx def) v))
539 (block-preds (lookup-block def blocks)))
540 (for-each (lambda (use)
541 (vector-push! usev (k->idx use) v))
542 uses)))))
543 (dfg-use-maps dfg))
544
545 ;; Initialize live-in and live-out sets.
546 (let lp ((n 0))
547 (when (< n (vector-length live-out))
548 (vector-set! live-in n (make-bitvector nvars #f))
549 (vector-set! live-out n (make-bitvector nvars #f))
550 (lp (1+ n))))
551
552 ;; Liveness is a reverse data-flow problem, so we give
553 ;; compute-maximum-fixed-point a reversed graph, swapping in and
554 ;; out, usev and defv, using successors instead of predecessors,
555 ;; and starting with ktail instead of the entry.
556 (compute-maximum-fixed-point succs live-out live-in defv usev #t)
557
558 (make-dfa k->idx order var->idx names syms live-in live-out)))))
559
560(define (print-dfa dfa)
561 (match dfa
562 (($ $dfa k->idx order var->idx names syms in out)
563 (define (print-var-set bv)
564 (let lp ((n 0))
565 (let ((n (bit-position #t bv n)))
566 (when n
567 (format #t " ~A" (vector-ref syms n))
568 (lp (1+ n))))))
569 (let lp ((n 0))
570 (when (< n (vector-length order))
571 (format #t "~A:\n" (vector-ref order n))
572 (format #t " in:")
573 (print-var-set (vector-ref in n))
574 (newline)
575 (format #t " out:")
576 (print-var-set (vector-ref out n))
577 (newline)
578 (lp (1+ n)))))))
579
f22979db 580(define (visit-fun fun conts blocks use-maps global?)
fc95a944 581 (define (add-def! name sym def-k)
6e8ad823
AW
582 (unless def-k
583 (error "Term outside labelled continuation?"))
fc95a944 584 (hashq-set! use-maps sym (make-use-map name sym def-k '())))
6e8ad823
AW
585
586 (define (add-use! sym use-k)
587 (match (hashq-ref use-maps sym)
588 (#f (error "Symbol out of scope?" sym))
fc95a944 589 ((and use-map ($ $use-map name sym def uses))
6e8ad823
AW
590 (set-use-map-uses! use-map (cons use-k uses)))))
591
f22979db
AW
592 (define* (declare-block! label cont parent
593 #:optional (level
594 (1+ (lookup-scope-level parent blocks))))
595 (hashq-set! conts label cont)
596 (hashq-set! blocks label (make-block parent level)))
597
598 (define (link-blocks! pred succ)
599 (let ((pred-block (hashq-ref blocks pred))
600 (succ-block (hashq-ref blocks succ)))
601 (unless (and pred-block succ-block)
c8ad7426 602 (error "internal error" pred-block succ-block))
f22979db
AW
603 (set-block-succs! pred-block (cons succ (block-succs pred-block)))
604 (set-block-preds! succ-block (cons pred (block-preds succ-block)))))
6e8ad823
AW
605
606 (define (visit exp exp-k)
fc95a944
AW
607 (define (def! name sym)
608 (add-def! name sym exp-k))
6e8ad823
AW
609 (define (use! sym)
610 (add-use! sym exp-k))
3aee6cfd
AW
611 (define (use-k! k)
612 (link-blocks! exp-k k))
6e8ad823
AW
613 (define (recur exp)
614 (visit exp exp-k))
615 (match exp
616 (($ $letk (($ $cont k src cont) ...) body)
617 ;; Set up recursive environment before visiting cont bodies.
618 (for-each (lambda (cont k)
f22979db 619 (declare-block! k cont exp-k))
6e8ad823
AW
620 cont k)
621 (for-each visit cont k)
622 (recur body))
623
624 (($ $kargs names syms body)
fc95a944 625 (for-each def! names syms)
6e8ad823
AW
626 (recur body))
627
628 (($ $kif kt kf)
f22979db
AW
629 (use-k! kt)
630 (use-k! kf))
6e8ad823
AW
631
632 (($ $ktrunc arity k)
f22979db 633 (use-k! k))
6e8ad823
AW
634
635 (($ $letrec names syms funs body)
636 (unless global?
637 (error "$letrec should not be present when building a local DFG"))
fc95a944 638 (for-each def! names syms)
f22979db 639 (for-each (cut visit-fun <> conts blocks use-maps global?) funs)
6e8ad823
AW
640 (visit body exp-k))
641
642 (($ $continue k exp)
f22979db 643 (use-k! k)
6e8ad823
AW
644 (match exp
645 (($ $var sym)
646 (use! sym))
647
648 (($ $call proc args)
649 (use! proc)
650 (for-each use! args))
651
652 (($ $primcall name args)
653 (for-each use! args))
654
655 (($ $values args)
656 (for-each use! args))
657
658 (($ $prompt escape? tag handler)
659 (use! tag)
f22979db 660 (use-k! handler))
6e8ad823
AW
661
662 (($ $fun)
663 (when global?
f22979db 664 (visit-fun exp conts blocks use-maps global?)))
6e8ad823
AW
665
666 (_ #f)))))
667
668 (match fun
669 (($ $fun meta free
670 ($ $cont kentry src
671 (and entry
672 ($ $kentry self ($ $cont ktail _ tail) clauses))))
f22979db 673 (declare-block! kentry entry #f 0)
fc95a944 674 (add-def! #f self kentry)
6e8ad823 675
f22979db 676 (declare-block! ktail tail kentry)
6e8ad823
AW
677
678 (for-each
679 (match-lambda
680 (($ $cont kclause _
681 (and clause ($ $kclause arity ($ $cont kbody _ body))))
f22979db
AW
682 (declare-block! kclause clause kentry)
683 (link-blocks! kentry kclause)
6e8ad823 684
f22979db
AW
685 (declare-block! kbody body kclause)
686 (link-blocks! kclause kbody)
6e8ad823
AW
687
688 (visit body kbody)))
3aee6cfd 689 clauses)
f22979db 690
0e2446d4 691 (analyze-control-flow! kentry ktail blocks))))
6e8ad823
AW
692
693(define* (compute-dfg fun #:key (global? #t))
694 (let* ((conts (make-hash-table))
f22979db
AW
695 (blocks (make-hash-table))
696 (use-maps (make-hash-table)))
697 (visit-fun fun conts blocks use-maps global?)
698 (make-dfg conts blocks use-maps)))
6e8ad823 699
f22979db
AW
700(define (lookup-block k blocks)
701 (let ((res (hashq-ref blocks k)))
6e8ad823 702 (unless res
f22979db 703 (error "Unknown continuation!" k (hash-fold acons '() blocks)))
6e8ad823
AW
704 res))
705
f22979db
AW
706(define (lookup-scope-level k blocks)
707 (match (lookup-block k blocks)
708 (($ $block _ scope-level) scope-level)))
709
6e8ad823
AW
710(define (lookup-use-map sym use-maps)
711 (let ((res (hashq-ref use-maps sym)))
712 (unless res
713 (error "Unknown lexical!" sym (hash-fold acons '() use-maps)))
714 res))
715
716(define (lookup-def sym dfg)
717 (match dfg
f22979db 718 (($ $dfg conts blocks use-maps)
6e8ad823 719 (match (lookup-use-map sym use-maps)
fc95a944 720 (($ $use-map name sym def uses)
6e8ad823
AW
721 def)))))
722
723(define (lookup-uses sym dfg)
724 (match dfg
f22979db 725 (($ $dfg conts blocks use-maps)
6e8ad823 726 (match (lookup-use-map sym use-maps)
fc95a944 727 (($ $use-map name sym def uses)
6e8ad823
AW
728 uses)))))
729
c8ad7426
AW
730(define (lookup-block-scope k dfg)
731 (block-scope (lookup-block k (dfg-blocks dfg))))
732
f22979db
AW
733(define (lookup-predecessors k dfg)
734 (match (lookup-block k (dfg-blocks dfg))
735 (($ $block _ _ preds succs) preds)))
736
737(define (lookup-successors k dfg)
738 (match (lookup-block k (dfg-blocks dfg))
739 (($ $block _ _ preds succs) succs)))
740
6e8ad823 741(define (find-defining-term sym dfg)
f22979db 742 (match (lookup-predecessors (lookup-def sym dfg) dfg)
6e8ad823
AW
743 ((def-exp-k)
744 (lookup-cont def-exp-k (dfg-cont-table dfg)))
745 (else #f)))
746
747(define (find-call term)
748 (match term
749 (($ $kargs names syms body) (find-call body))
750 (($ $letk conts body) (find-call body))
751 (($ $letrec names syms funs body) (find-call body))
752 (($ $continue) term)))
753
754(define (call-expression call)
755 (match call
756 (($ $continue k exp) exp)))
757
758(define (find-expression term)
759 (call-expression (find-call term)))
760
761(define (find-defining-expression sym dfg)
762 (match (find-defining-term sym dfg)
763 (#f #f)
764 (($ $ktrunc) #f)
f22979db 765 (($ $kclause) #f)
6e8ad823
AW
766 (term (find-expression term))))
767
768(define (find-constant-value sym dfg)
769 (match (find-defining-expression sym dfg)
770 (($ $const val)
771 (values #t val))
772 (($ $continue k ($ $void))
773 (values #t *unspecified*))
774 (else
775 (values #f #f))))
776
777(define (constant-needs-allocation? sym val dfg)
607fe5a6
AW
778 (define (immediate-u8? val)
779 (and (integer? val) (exact? val) (<= 0 val 255)))
780
6e8ad823
AW
781 (define (find-exp term)
782 (match term
783 (($ $kargs names syms body) (find-exp body))
784 (($ $letk conts body) (find-exp body))
785 (else term)))
786 (match dfg
f22979db 787 (($ $dfg conts blocks use-maps)
6e8ad823 788 (match (lookup-use-map sym use-maps)
fc95a944 789 (($ $use-map _ _ def uses)
6e8ad823
AW
790 (or-map
791 (lambda (use)
792 (match (find-expression (lookup-cont use conts))
793 (($ $call) #f)
794 (($ $values) #f)
795 (($ $primcall 'free-ref (closure slot))
796 (not (eq? sym slot)))
797 (($ $primcall 'free-set! (closure slot value))
798 (not (eq? sym slot)))
799 (($ $primcall 'cache-current-module! (mod . _))
800 (eq? sym mod))
801 (($ $primcall 'cached-toplevel-box _)
802 #f)
803 (($ $primcall 'cached-module-box _)
804 #f)
805 (($ $primcall 'resolve (name bound?))
806 (eq? sym name))
607fe5a6
AW
807 (($ $primcall 'make-vector (len init))
808 (not (and (eq? sym len) (immediate-u8? val))))
8ba3f20c 809 (($ $primcall 'vector-ref (v i))
607fe5a6 810 (not (and (eq? sym i) (immediate-u8? val))))
8ba3f20c 811 (($ $primcall 'vector-set! (v i x))
607fe5a6 812 (not (and (eq? sym i) (immediate-u8? val))))
486013d6
AW
813 (($ $primcall 'builtin-ref (idx))
814 #f)
6e8ad823
AW
815 (_ #t)))
816 uses))))))
817
f22979db
AW
818(define (continuation-scope-contains? scope-k k blocks)
819 (let ((scope-level (lookup-scope-level scope-k blocks)))
820 (let lp ((k k))
821 (or (eq? scope-k k)
822 (match (lookup-block k blocks)
823 (($ $block scope level)
824 (and (< scope-level level)
825 (lp scope))))))))
826
f22979db 827(define (continuation-bound-in? k use-k dfg)
d51fb1e6 828 (match dfg
f22979db
AW
829 (($ $dfg conts blocks use-maps)
830 (match (lookup-block k blocks)
831 (($ $block def-k)
832 (continuation-scope-contains? def-k use-k blocks))))))
d51fb1e6
AW
833
834(define (variable-free-in? var k dfg)
6e8ad823 835 (match dfg
f22979db 836 (($ $dfg conts blocks use-maps)
6e8ad823 837 (or-map (lambda (use)
f22979db 838 (continuation-scope-contains? k use blocks))
6e8ad823 839 (match (lookup-use-map var use-maps)
fc95a944 840 (($ $use-map name sym def uses)
6e8ad823
AW
841 uses))))))
842
843;; Does k1 dominate k2?
238ef4cf 844(define (dominates? k1 k2 blocks)
0e2446d4
AW
845 (let ((b1 (lookup-block k1 blocks))
846 (b2 (lookup-block k2 blocks)))
847 (let ((k1-level (block-dom-level b1))
848 (k2-level (block-dom-level b2)))
849 (cond
850 ((> k1-level k2-level) #f)
851 ((< k1-level k2-level) (dominates? k1 (block-idom b2) blocks))
852 ((= k1-level k2-level) (eqv? k1 k2))))))
853
854;; Does k1 post-dominate k2?
855(define (post-dominates? k1 k2 blocks)
856 (let ((b1 (lookup-block k1 blocks))
857 (b2 (lookup-block k2 blocks)))
858 (let ((k1-level (block-pdom-level b1))
859 (k2-level (block-pdom-level b2)))
860 (cond
861 ((> k1-level k2-level) #f)
862 ((< k1-level k2-level) (post-dominates? k1 (block-pdom b2) blocks))
863 ((= k1-level k2-level) (eqv? k1 k2))))))
6e8ad823 864
b8da548f
AW
865(define (lookup-loop-header k blocks)
866 (block-loop-header (lookup-block k blocks)))
867
e636f424
AW
868;; A continuation is a control point if it has multiple predecessors, or
869;; if its single predecessor has multiple successors.
870(define (control-point? k dfg)
871 (match (lookup-predecessors k dfg)
872 ((pred)
873 (match (lookup-successors pred dfg)
874 ((_) #f)
875 (_ #t)))
876 (_ #t)))
6e8ad823
AW
877
878(define (lookup-bound-syms k dfg)
879 (match dfg
f22979db 880 (($ $dfg conts blocks use-maps)
6e8ad823
AW
881 (match (lookup-cont k conts)
882 (($ $kargs names syms body)
883 syms)))))