1 //! Performs control flow analysis.
2
3 use log::{debug, info};
4 use std::cmp::Ordering;
5
6 use crate::analysis_main::AnalysisError;
7 use crate::data_structures::{BlockIx, InstIx, Range, Set, TypedIxVec};
8 use crate::sparse_set::{SparseSetU, SparseSetUIter};
9 use crate::Function;
10
11 use smallvec::SmallVec;
12
13 //=============================================================================
14 // Debugging config. Set all these to `false` for normal operation.
15
16 // DEBUGGING: set to true to cross-check the dominator-tree computation.
17 const CROSSCHECK_DOMS: bool = false;
18
19 //===========================================================================//
20 // //
21 // CONTROL FLOW ANALYSIS //
22 // //
23 //===========================================================================//
24
25 //=============================================================================
26 // Control flow analysis: create the InstIx-to-BlockIx mapping
27
28 // This is trivial, but it's sometimes useful to have.
29 // Note: confusingly, the `Range` here is data_structures::Range, not
30 // std::ops::Range.
31 pub struct InstIxToBlockIxMap {
32 vek: TypedIxVec<BlockIx, Range<InstIx>>,
33 }
34
35 impl InstIxToBlockIxMap {
36 #[inline(never)]
new<F: Function>(func: &F) -> Self37 pub fn new<F: Function>(func: &F) -> Self {
38 let mut vek = TypedIxVec::<BlockIx, Range<InstIx>>::new();
39 for bix in func.blocks() {
40 let r: Range<InstIx> = func.block_insns(bix);
41 assert!(r.start() <= r.last_plus1());
42 vek.push(r);
43 }
44
45 fn cmp_ranges(r1: &Range<InstIx>, r2: &Range<InstIx>) -> Ordering {
46 if r1.last_plus1() <= r2.first() {
47 return Ordering::Less;
48 }
49 if r2.last_plus1() <= r1.first() {
50 return Ordering::Greater;
51 }
52 if r1.first() == r2.first() && r1.last_plus1() == r2.last_plus1() {
53 return Ordering::Equal;
54 }
55 // If this happens, F::block_insns is telling us something that isn't right.
56 panic!("InstIxToBlockIxMap::cmp_ranges: overlapping InstIx ranges!");
57 }
58
59 vek.sort_unstable_by(|r1, r2| cmp_ranges(r1, r2));
60 // Sanity check: ascending, non-overlapping, no gaps. We need this in
61 // order to ensure that binary searching in `map` works properly.
62 for i in 1..vek.len() {
63 let r_m1 = &vek[BlockIx::new(i - 1)];
64 let r_m0 = &vek[BlockIx::new(i - 0)];
65 assert!(r_m1.last_plus1() == r_m0.first());
66 }
67
68 Self { vek }
69 }
70
71 #[inline(never)]
map(&self, iix: InstIx) -> BlockIx72 pub fn map(&self, iix: InstIx) -> BlockIx {
73 if self.vek.len() > 0 {
74 let mut lo = 0isize;
75 let mut hi = self.vek.len() as isize - 1;
76 loop {
77 if lo > hi {
78 break;
79 }
80 let mid = (lo + hi) / 2;
81 let midv = &self.vek[BlockIx::new(mid as u32)];
82 if iix < midv.start() {
83 hi = mid - 1;
84 continue;
85 }
86 if iix >= midv.last_plus1() {
87 lo = mid + 1;
88 continue;
89 }
90 assert!(midv.start() <= iix && iix < midv.last_plus1());
91 return BlockIx::new(mid as u32);
92 }
93 }
94 panic!("InstIxToBlockIxMap::map: can't map {:?}", iix);
95 }
96 }
97
98 //=============================================================================
99 // Control flow analysis: calculation of block successor and predecessor maps
100
101 // Returned TypedIxVecs contain one element per block
102 #[inline(never)]
calc_preds_and_succs<F: Function>( func: &F, num_blocks: u32, ) -> ( TypedIxVec<BlockIx, SparseSetU<[BlockIx; 4]>>, TypedIxVec<BlockIx, SparseSetU<[BlockIx; 4]>>, )103 fn calc_preds_and_succs<F: Function>(
104 func: &F,
105 num_blocks: u32,
106 ) -> (
107 TypedIxVec<BlockIx, SparseSetU<[BlockIx; 4]>>,
108 TypedIxVec<BlockIx, SparseSetU<[BlockIx; 4]>>,
109 ) {
110 info!(" calc_preds_and_succs: begin");
111
112 assert!(func.blocks().len() == num_blocks as usize);
113
114 // First calculate the succ map, since we can do that directly from the
115 // Func.
116 //
117 // Func::finish() ensures that all blocks are non-empty, and that only the
118 // last instruction is a control flow transfer. Hence the following won't
119 // miss any edges.
120 let mut succ_map = TypedIxVec::<BlockIx, SparseSetU<[BlockIx; 4]>>::new();
121 for b in func.blocks() {
122 let mut bix_set = SparseSetU::<[BlockIx; 4]>::empty();
123 for bix in func.block_succs(b).iter() {
124 bix_set.insert(*bix);
125 }
126 succ_map.push(bix_set);
127 }
128
129 // Now invert the mapping
130 let mut pred_map = TypedIxVec::<BlockIx, SparseSetU<[BlockIx; 4]>>::new();
131 pred_map.resize(num_blocks, SparseSetU::<[BlockIx; 4]>::empty());
132 for (src, dst_set) in (0..).zip(succ_map.iter()) {
133 for dst in dst_set.iter() {
134 pred_map[*dst].insert(BlockIx::new(src));
135 }
136 }
137
138 // Stay sane ..
139 assert!(pred_map.len() == num_blocks);
140 assert!(succ_map.len() == num_blocks);
141
142 let mut n = 0;
143 debug!("");
144 for (preds, succs) in pred_map.iter().zip(succ_map.iter()) {
145 debug!(
146 "{:<3?} preds {:<16?} succs {:?}",
147 BlockIx::new(n),
148 preds,
149 succs
150 );
151 n += 1;
152 }
153
154 info!(" calc_preds_and_succs: end");
155 (pred_map, succ_map)
156 }
157
158 //=============================================================================
159 // Control flow analysis: calculation of block preorder and postorder sequences
160
161 // Returned Vecs contain one element per block. `None` is returned if the
162 // sequences do not contain `num_blocks` elements, in which case the input
163 // contains blocks not reachable from the entry point, and is invalid.
164 #[inline(never)]
calc_preord_and_postord<F: Function>( func: &F, num_blocks: u32, succ_map: &TypedIxVec<BlockIx, SparseSetU<[BlockIx; 4]>>, ) -> Option<(Vec<BlockIx>, Vec<BlockIx>)>165 fn calc_preord_and_postord<F: Function>(
166 func: &F,
167 num_blocks: u32,
168 succ_map: &TypedIxVec<BlockIx, SparseSetU<[BlockIx; 4]>>,
169 ) -> Option<(Vec<BlockIx>, Vec<BlockIx>)> {
170 info!(" calc_preord_and_postord: begin");
171
172 let mut pre_ord = Vec::<BlockIx>::new();
173 let mut post_ord = Vec::<BlockIx>::new();
174
175 let mut visited = TypedIxVec::<BlockIx, bool>::new();
176 visited.resize(num_blocks, false);
177
178 // Set up initial state: entry block on the stack, marked as visited, and placed at the
179 // start of the pre-ord sequence.
180 let mut stack = SmallVec::<[(BlockIx, SparseSetUIter<[BlockIx; 4]>); 64]>::new();
181 let bix_entry = func.entry_block();
182 visited[bix_entry] = true;
183 pre_ord.push(bix_entry);
184 stack.push((bix_entry, succ_map[bix_entry].iter()));
185
186 'outer: while let Some((bix, bix_succ_iter)) = stack.last_mut() {
187 // Consider the block on the top of the stack. Does it have any successors we
188 // haven't yet visited?
189 while let Some(bix_next_succ) = bix_succ_iter.next() {
190 if !visited[*bix_next_succ] {
191 // Yes. Push just one of them on the stack, along with a newly initialised
192 // iterator for it, and continue by considering the new stack top. Because
193 // blocks are only ever pushed onto the stack once, we must also add the
194 // block to the pre-ord sequence at this point.
195 visited[*bix_next_succ] = true;
196 pre_ord.push(*bix_next_succ);
197 stack.push((*bix_next_succ, succ_map[*bix_next_succ].iter()));
198 continue 'outer;
199 }
200 }
201 // No. This is the last time we'll ever hear of it. So add it to the post-ord
202 // sequence, remove the now-defunct stack-top item, and move on.
203 post_ord.push(*bix);
204 stack.pop();
205 }
206
207 assert!(pre_ord.len() == post_ord.len());
208 assert!(pre_ord.len() <= num_blocks as usize);
209 if pre_ord.len() < num_blocks as usize {
210 info!(
211 " calc_preord_and_postord: invalid: {} blocks, {} reachable",
212 num_blocks,
213 pre_ord.len()
214 );
215 return None;
216 }
217
218 assert!(pre_ord.len() == num_blocks as usize);
219 assert!(post_ord.len() == num_blocks as usize);
220 #[cfg(debug_assertions)]
221 {
222 let mut pre_ord_sorted: Vec<BlockIx> = pre_ord.clone();
223 let mut post_ord_sorted: Vec<BlockIx> = post_ord.clone();
224 pre_ord_sorted.sort_by(|bix1, bix2| bix1.get().partial_cmp(&bix2.get()).unwrap());
225 post_ord_sorted.sort_by(|bix1, bix2| bix1.get().partial_cmp(&bix2.get()).unwrap());
226 let expected: Vec<BlockIx> = (0..num_blocks).map(|u| BlockIx::new(u)).collect();
227 debug_assert!(pre_ord_sorted == expected);
228 debug_assert!(post_ord_sorted == expected);
229 }
230
231 info!(" calc_preord_and_postord: end. {} blocks", num_blocks);
232 Some((pre_ord, post_ord))
233 }
234
235 //=============================================================================
236 // Computation of per-block dominator sets. Note, this is slow, and will be
237 // removed at some point.
238
239 // Calculate the dominance relationship, given `pred_map` and a start node
240 // `start`. The resulting vector maps each block to the set of blocks that
241 // dominate it. This algorithm is from Fig 7.14 of Muchnick 1997. The
242 // algorithm is described as simple but not as performant as some others.
243 #[inline(never)]
calc_dom_sets_slow( num_blocks: u32, pred_map: &TypedIxVec<BlockIx, SparseSetU<[BlockIx; 4]>>, post_ord: &Vec<BlockIx>, start: BlockIx, ) -> TypedIxVec<BlockIx, Set<BlockIx>>244 fn calc_dom_sets_slow(
245 num_blocks: u32,
246 pred_map: &TypedIxVec<BlockIx, SparseSetU<[BlockIx; 4]>>,
247 post_ord: &Vec<BlockIx>,
248 start: BlockIx,
249 ) -> TypedIxVec<BlockIx, Set<BlockIx>> {
250 info!(" calc_dom_sets_slow: begin");
251
252 let mut dom_map = TypedIxVec::<BlockIx, Set<BlockIx>>::new();
253
254 // FIXME find better names for n/d/t sets.
255 {
256 let root: BlockIx = start;
257 let n_set: Set<BlockIx> =
258 Set::from_vec((0..num_blocks).map(|bix| BlockIx::new(bix)).collect());
259 let mut d_set: Set<BlockIx>;
260 let mut t_set: Set<BlockIx>;
261
262 dom_map.resize(num_blocks, Set::<BlockIx>::empty());
263 dom_map[root] = Set::unit(root);
264 for block_i in 0..num_blocks {
265 let block_ix = BlockIx::new(block_i);
266 if block_ix != root {
267 dom_map[block_ix] = n_set.clone();
268 }
269 }
270
271 let mut num_iter = 0;
272 loop {
273 num_iter += 1;
274 info!(" calc_dom_sets_slow: outer loop {}", num_iter);
275 let mut change = false;
276 for i in 0..num_blocks {
277 // block_ix travels in "reverse preorder"
278 let block_ix = post_ord[(num_blocks - 1 - i) as usize];
279 if block_ix == root {
280 continue;
281 }
282 t_set = n_set.clone();
283 for pred_ix in pred_map[block_ix].iter() {
284 t_set.intersect(&dom_map[*pred_ix]);
285 }
286 d_set = t_set.clone();
287 d_set.insert(block_ix);
288 if !d_set.equals(&dom_map[block_ix]) {
289 change = true;
290 dom_map[block_ix] = d_set;
291 }
292 }
293 if !change {
294 break;
295 }
296 }
297 }
298
299 debug!("");
300 let mut block_ix = 0;
301 for dom_set in dom_map.iter() {
302 debug!("{:<3?} dom_set {:<16?}", BlockIx::new(block_ix), dom_set);
303 block_ix += 1;
304 }
305 info!(" calc_dom_sets_slow: end");
306 dom_map
307 }
308
309 //=============================================================================
310 // Computation of per-block dominator sets by first computing trees.
311 //
312 // This is an implementation of the algorithm described in
313 //
314 // A Simple, Fast Dominance Algorithm
315 // Keith D. Cooper, Timothy J. Harvey, and Ken Kennedy
316 // Department of Computer Science, Rice University, Houston, Texas, USA
317 // TR-06-33870
318 // https://www.cs.rice.edu/~keith/EMBED/dom.pdf
319 //
320 // which appears to be the de-facto standard scheme for computing dominance
321 // quickly nowadays.
322
323 // Unfortunately it seems like local consts are not allowed in Rust.
324 const DT_INVALID_POSTORD: u32 = 0xFFFF_FFFF;
325 const DT_INVALID_BLOCKIX: BlockIx = BlockIx::BlockIx(0xFFFF_FFFF);
326
327 // Helper
dt_merge_sets( idom: &TypedIxVec<BlockIx, BlockIx>, bix2rpostord: &TypedIxVec<BlockIx, u32>, mut node1: BlockIx, mut node2: BlockIx, ) -> BlockIx328 fn dt_merge_sets(
329 idom: &TypedIxVec<BlockIx, BlockIx>,
330 bix2rpostord: &TypedIxVec<BlockIx, u32>,
331 mut node1: BlockIx,
332 mut node2: BlockIx,
333 ) -> BlockIx {
334 while node1 != node2 {
335 if node1 == DT_INVALID_BLOCKIX || node2 == DT_INVALID_BLOCKIX {
336 return DT_INVALID_BLOCKIX;
337 }
338 let rpo1 = bix2rpostord[node1];
339 let rpo2 = bix2rpostord[node2];
340 if rpo1 > rpo2 {
341 node1 = idom[node1];
342 } else if rpo2 > rpo1 {
343 node2 = idom[node2];
344 }
345 }
346 assert!(node1 == node2);
347 node1
348 }
349
350 #[inline(never)]
calc_dom_tree( num_blocks: u32, pred_map: &TypedIxVec<BlockIx, SparseSetU<[BlockIx; 4]>>, post_ord: &Vec<BlockIx>, start: BlockIx, ) -> TypedIxVec<BlockIx, BlockIx>351 fn calc_dom_tree(
352 num_blocks: u32,
353 pred_map: &TypedIxVec<BlockIx, SparseSetU<[BlockIx; 4]>>,
354 post_ord: &Vec<BlockIx>,
355 start: BlockIx,
356 ) -> TypedIxVec<BlockIx, BlockIx> {
357 info!(" calc_dom_tree: begin");
358
359 // We use 2^32-1 as a marker for an invalid BlockIx or postorder number.
360 // Hence we need this:
361 assert!(num_blocks < DT_INVALID_POSTORD);
362
363 // We have post_ord, which is the postorder sequence.
364
365 // Compute bix2rpostord, which maps a BlockIx to its reverse postorder
366 // number. And rpostord2bix, which maps a reverse postorder number to its
367 // BlockIx.
368 let mut bix2rpostord = TypedIxVec::<BlockIx, u32>::new();
369 let mut rpostord2bix = Vec::<BlockIx>::new();
370 bix2rpostord.resize(num_blocks, DT_INVALID_POSTORD);
371 rpostord2bix.resize(num_blocks as usize, DT_INVALID_BLOCKIX);
372 for n in 0..num_blocks {
373 // bix visits the blocks in reverse postorder
374 let bix = post_ord[(num_blocks - 1 - n) as usize];
375 // Hence:
376 bix2rpostord[bix] = n;
377 // and
378 rpostord2bix[n as usize] = bix;
379 }
380 for n in 0..num_blocks {
381 debug_assert!(bix2rpostord[BlockIx::new(n)] < num_blocks);
382 }
383
384 let mut idom = TypedIxVec::<BlockIx, BlockIx>::new();
385 idom.resize(num_blocks, DT_INVALID_BLOCKIX);
386
387 // The start node must have itself as a parent.
388 idom[start] = start;
389
390 for i in 0..num_blocks {
391 let block_ix = BlockIx::new(i);
392 let preds_of_i = &pred_map[block_ix];
393 // All nodes must be reachable from the root. That means that all nodes
394 // that aren't `start` must have at least one predecessor. However, we
395 // can't assert the inverse case -- that the start node has no
396 // predecessors -- because the start node might be a self-loop, in which
397 // case it will have itself as a pred. See tests/domtree_fuzz1.rat.
398 if block_ix != start {
399 assert!(!preds_of_i.is_empty());
400 }
401 }
402
403 let mut changed = true;
404 while changed {
405 changed = false;
406 for n in 0..num_blocks {
407 // Consider blocks in reverse postorder.
408 let node = rpostord2bix[n as usize];
409 assert!(node != DT_INVALID_BLOCKIX);
410 let node_preds = &pred_map[node];
411 let rponum = bix2rpostord[node];
412
413 let mut parent = DT_INVALID_BLOCKIX;
414 if node_preds.is_empty() {
415 // No preds, `parent` remains invalid.
416 } else {
417 for pred in node_preds.iter() {
418 let pred_rpo = bix2rpostord[*pred];
419 if pred_rpo < rponum {
420 parent = *pred;
421 break;
422 }
423 }
424 }
425
426 if parent != DT_INVALID_BLOCKIX {
427 for pred in node_preds.iter() {
428 if *pred == parent {
429 continue;
430 }
431 if idom[*pred] == DT_INVALID_BLOCKIX {
432 continue;
433 }
434 parent = dt_merge_sets(&idom, &bix2rpostord, parent, *pred);
435 }
436 }
437
438 if parent != DT_INVALID_BLOCKIX && parent != idom[node] {
439 idom[node] = parent;
440 changed = true;
441 }
442 }
443 }
444
445 // Check what we can. The start node should be its own parent. All other
446 // nodes should not be their own parent, since we are assured that there are
447 // no dead blocks in the graph, and hence that there is only one dominator
448 // tree, that covers the whole graph.
449 assert!(idom[start] == start);
450 for i in 0..num_blocks {
451 let block_ix = BlockIx::new(i);
452 // All "parent pointers" are valid.
453 assert!(idom[block_ix] != DT_INVALID_BLOCKIX);
454 // The only node whose parent pointer points to itself is the start node.
455 assert!((idom[block_ix] == block_ix) == (block_ix == start));
456 }
457
458 if CROSSCHECK_DOMS {
459 // Crosscheck the dom tree, by computing dom sets using the simple
460 // iterative algorithm. Then, for each block, construct the dominator set
461 // by walking up the tree to the root, and check that it's the same as
462 // what the simple algorithm produced.
463
464 info!(" calc_dom_tree crosscheck: begin");
465 let slow_sets = calc_dom_sets_slow(num_blocks, pred_map, post_ord, start);
466 assert!(slow_sets.len() == idom.len());
467
468 for i in 0..num_blocks {
469 let mut block_ix = BlockIx::new(i);
470 let mut set = Set::<BlockIx>::empty();
471 loop {
472 set.insert(block_ix);
473 let other_block_ix = idom[block_ix];
474 if other_block_ix == block_ix {
475 break;
476 }
477 block_ix = other_block_ix;
478 }
479 assert!(set.to_vec() == slow_sets[BlockIx::new(i)].to_vec());
480 }
481 info!(" calc_dom_tree crosscheck: end");
482 }
483
484 info!(" calc_dom_tree: end");
485 idom
486 }
487
488 //=============================================================================
489 // Computation of per-block loop-depths
490
491 #[inline(never)]
calc_loop_depths( num_blocks: u32, pred_map: &TypedIxVec<BlockIx, SparseSetU<[BlockIx; 4]>>, succ_map: &TypedIxVec<BlockIx, SparseSetU<[BlockIx; 4]>>, post_ord: &Vec<BlockIx>, start: BlockIx, ) -> TypedIxVec<BlockIx, u32>492 fn calc_loop_depths(
493 num_blocks: u32,
494 pred_map: &TypedIxVec<BlockIx, SparseSetU<[BlockIx; 4]>>,
495 succ_map: &TypedIxVec<BlockIx, SparseSetU<[BlockIx; 4]>>,
496 post_ord: &Vec<BlockIx>,
497 start: BlockIx,
498 ) -> TypedIxVec<BlockIx, u32> {
499 info!(" calc_loop_depths: begin");
500 let idom = calc_dom_tree(num_blocks, pred_map, post_ord, start);
501
502 // Find the loops. First, find the "loop header nodes", and from those,
503 // derive the loops.
504 //
505 // loop_set headers:
506 // A "back edge" m->n is some edge m->n where n dominates m. 'n' is
507 // the loop header node.
508 //
509 // `back_edges` is a set rather than a vector so as to avoid complications
510 // that might later arise if the same loop is enumerated more than once.
511 //
512 // Iterate over all edges (m->n)
513 let mut back_edges = Set::<(BlockIx, BlockIx)>::empty();
514 for block_m_ix in BlockIx::new(0).dotdot(BlockIx::new(num_blocks)) {
515 for block_n_ix in succ_map[block_m_ix].iter() {
516 // Figure out if N dominates M. Do this by walking the dom tree from M
517 // back up to the root, and seeing if we encounter N on the way.
518 let mut n_dominates_m = false;
519 let mut block_ix = block_m_ix;
520 loop {
521 if block_ix == *block_n_ix {
522 n_dominates_m = true;
523 break;
524 }
525 let other_block_ix = idom[block_ix];
526 if other_block_ix == block_ix {
527 break;
528 }
529 block_ix = other_block_ix;
530 }
531 if n_dominates_m {
532 //println!("QQQQ back edge {} -> {}",
533 // block_m_ix.show(), block_n_ix.show());
534 back_edges.insert((block_m_ix, *block_n_ix));
535 }
536 }
537 }
538
539 // Now collect the sets of Blocks for each loop. For each back edge,
540 // collect up all the blocks in the natural loop defined by the back edge
541 // M->N. This algorithm is from Fig 7.21 of Muchnick 1997 (an excellent
542 // book). Order in `natural_loops` has no particular meaning.
543 let mut natural_loops = Vec::<Set<BlockIx>>::new();
544 for (block_m_ix, block_n_ix) in back_edges.iter() {
545 let mut loop_set: Set<BlockIx>;
546 let mut stack: Vec<BlockIx>;
547 stack = Vec::<BlockIx>::new();
548 loop_set = Set::<BlockIx>::two(*block_m_ix, *block_n_ix);
549 if block_m_ix != block_n_ix {
550 // The next line is missing in the Muchnick description. Without it the
551 // algorithm doesn't make any sense, though.
552 stack.push(*block_m_ix);
553 while let Some(block_p_ix) = stack.pop() {
554 for block_q_ix in pred_map[block_p_ix].iter() {
555 if !loop_set.contains(*block_q_ix) {
556 loop_set.insert(*block_q_ix);
557 stack.push(*block_q_ix);
558 }
559 }
560 }
561 }
562 natural_loops.push(loop_set);
563 }
564
565 // Here is a kludgey way to compute the depth of each loop. First, order
566 // `natural_loops` by increasing size, so the largest loops are at the end.
567 // Then, repeatedly scan forwards through the vector, in "upper triangular
568 // matrix" style. For each scan, remember the "current loop". Initially
569 // the "current loop is the start point of each scan. If, during the scan,
570 // we encounter a loop which is a superset of the "current loop", change the
571 // "current loop" to this new loop, and increment a counter associated with
572 // the start point of the scan. The effect is that the counter records the
573 // nesting depth of the loop at the start of the scan. For this to be
574 // completely accurate, I _think_ this requires the property that loops are
575 // either disjoint or nested, but are in no case intersecting.
576
577 natural_loops.sort_by(|left_block_set, right_block_set| {
578 left_block_set
579 .card()
580 .partial_cmp(&right_block_set.card())
581 .unwrap()
582 });
583
584 let num_loops = natural_loops.len();
585 let mut loop_depths = Vec::<u32>::new();
586 loop_depths.resize(num_loops, 0);
587
588 for i in 0..num_loops {
589 let mut curr = i;
590 let mut depth = 1;
591 for j in i + 1..num_loops {
592 debug_assert!(curr < j);
593 if natural_loops[curr].is_subset_of(&natural_loops[j]) {
594 depth += 1;
595 curr = j;
596 }
597 }
598 loop_depths[i] = depth;
599 }
600
601 // Now that we have a depth for each loop, we can finally compute the depth
602 // for each block.
603 let mut depth_map = TypedIxVec::<BlockIx, u32>::new();
604 depth_map.resize(num_blocks, 0);
605 for (loop_block_indexes, depth) in natural_loops.iter().zip(loop_depths) {
606 for loop_block_ix in loop_block_indexes.iter() {
607 if depth_map[*loop_block_ix] < depth {
608 depth_map[*loop_block_ix] = depth;
609 }
610 }
611 }
612
613 debug_assert!(depth_map.len() == num_blocks);
614
615 let mut n = 0;
616 debug!("");
617 for (depth, idom_by) in depth_map.iter().zip(idom.iter()) {
618 debug!(
619 "{:<3?} depth {} idom {:?}",
620 BlockIx::new(n),
621 depth,
622 idom_by
623 );
624 n += 1;
625 }
626
627 info!(" calc_loop_depths: end");
628 depth_map
629 }
630
631 //=============================================================================
632 // Control-flow analysis top level: For a Func: predecessors, successors,
633 // preord and postord sequences, and loop depths.
634
635 // CFGInfo contains CFG-related info computed from a Func.
636 pub struct CFGInfo {
637 // All these TypedIxVecs and plain Vecs contain one element per Block in the
638 // Func.
639
640 // Predecessor and successor maps.
641 pub pred_map: TypedIxVec<BlockIx, SparseSetU<[BlockIx; 4]>>,
642 pub succ_map: TypedIxVec<BlockIx, SparseSetU<[BlockIx; 4]>>,
643
644 // Pre- and post-order sequences. Iterating forwards through these
645 // vectors enumerates the blocks in preorder and postorder respectively.
646 pub pre_ord: Vec<BlockIx>,
647 pub _post_ord: Vec<BlockIx>,
648
649 // This maps from a Block to the loop depth that it is at
650 pub depth_map: TypedIxVec<BlockIx, u32>,
651 }
652
653 impl CFGInfo {
654 #[inline(never)]
create<F: Function>(func: &F) -> Result<Self, AnalysisError>655 pub fn create<F: Function>(func: &F) -> Result<Self, AnalysisError> {
656 info!(" CFGInfo::create: begin");
657
658 // Throw out insanely large inputs. They'll probably cause failure later
659 // on.
660 let num_blocks_usize = func.blocks().len();
661 if num_blocks_usize >= 1 * 1024 * 1024 {
662 // 1 million blocks should be enough for anyone. That will soak up 20
663 // index bits, leaving a "safety margin" of 12 bits for indices for
664 // induced structures (RangeFragIx, InstIx, VirtualRangeIx, RealRangeIx,
665 // etc).
666 return Err(AnalysisError::ImplementationLimitsExceeded);
667 }
668
669 // Similarly, limit the number of instructions to 16 million. This allows
670 // 16 insns per block with the worst-case number of blocks. Because each
671 // insn typically generates somewhat less than one new value, this check
672 // also has the effect of limiting the number of virtual registers to
673 // roughly the same amount (16 million).
674 if func.insns().len() >= 16 * 1024 * 1024 {
675 return Err(AnalysisError::ImplementationLimitsExceeded);
676 }
677
678 // Now we know we're safe to narrow it to u32.
679 let num_blocks = num_blocks_usize as u32;
680
681 // === BEGIN compute successor and predecessor maps ===
682 //
683 let (pred_map, succ_map) = calc_preds_and_succs(func, num_blocks);
684 assert!(pred_map.len() == num_blocks);
685 assert!(succ_map.len() == num_blocks);
686 //
687 // === END compute successor and predecessor maps ===
688
689 // === BEGIN check that critical edges have been split ===
690 //
691 for (src, dst_set) in (0..).zip(succ_map.iter()) {
692 if dst_set.card() < 2 {
693 continue;
694 }
695 for dst in dst_set.iter() {
696 if pred_map[*dst].card() >= 2 {
697 return Err(AnalysisError::CriticalEdge {
698 from: BlockIx::new(src),
699 to: *dst,
700 });
701 }
702 }
703 }
704 //
705 // === END check that critical edges have been split ===
706
707 // === BEGIN compute preord/postord sequences ===
708 //
709 let mb_pre_ord_and_post_ord = calc_preord_and_postord(func, num_blocks, &succ_map);
710 if mb_pre_ord_and_post_ord.is_none() {
711 return Err(AnalysisError::UnreachableBlocks);
712 }
713
714 let (pre_ord, post_ord) = mb_pre_ord_and_post_ord.unwrap();
715 assert!(pre_ord.len() == num_blocks as usize);
716 assert!(post_ord.len() == num_blocks as usize);
717 //
718 // === END compute preord/postord sequences ===
719
720 // === BEGIN compute loop depth of all Blocks
721 //
722 let depth_map = calc_loop_depths(
723 num_blocks,
724 &pred_map,
725 &succ_map,
726 &post_ord,
727 func.entry_block(),
728 );
729 debug_assert!(depth_map.len() == num_blocks);
730 //
731 // === END compute loop depth of all Blocks
732
733 info!(" CFGInfo::create: end");
734 Ok(CFGInfo {
735 pred_map,
736 succ_map,
737 pre_ord,
738 _post_ord: post_ord,
739 depth_map,
740 })
741 }
742 }
743