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