Skip to main content

cranelift_codegen/
flowgraph.rs

1//! A control flow graph represented as mappings of basic blocks to their predecessors
2//! and successors.
3//!
4//! Successors are represented as basic blocks while predecessors are represented by basic
5//! blocks. Basic blocks are denoted by tuples of block and branch/jump instructions. Each
6//! predecessor tuple corresponds to the end of a basic block.
7//!
8//! ```c
9//!     Block0:
10//!         ...          ; beginning of basic block
11//!
12//!         ...
13//!
14//!         brif vx, Block1, Block2 ; end of basic block
15//!
16//!     Block1:
17//!         jump block3
18//! ```
19//!
20//! Here `Block1` and `Block2` would each have a single predecessor denoted as `(Block0, brif)`,
21//! while `Block3` would have a single predecessor denoted as `(Block1, jump block3)`.
22
23use crate::bforest;
24use crate::entity::SecondaryMap;
25use crate::inst_predicates;
26use crate::ir::{Block, Function, Inst};
27use crate::timing;
28use core::mem;
29
30/// A basic block denoted by its enclosing Block and last instruction.
31#[derive(Debug, PartialEq, Eq)]
32pub struct BlockPredecessor {
33    /// Enclosing Block key.
34    pub block: Block,
35    /// Last instruction in the basic block.
36    pub inst: Inst,
37}
38
39impl BlockPredecessor {
40    /// Convenient method to construct new BlockPredecessor.
41    pub fn new(block: Block, inst: Inst) -> Self {
42        Self { block, inst }
43    }
44}
45
46/// A container for the successors and predecessors of some Block.
47#[derive(Clone, Default)]
48struct CFGNode {
49    /// Instructions that can branch or jump to this block.
50    ///
51    /// This maps branch instruction -> predecessor block which is redundant since the block containing
52    /// the branch instruction is available from the `layout.inst_block()` method. We store the
53    /// redundant information because:
54    ///
55    /// 1. Many `pred_iter()` consumers want the block anyway, so it is handily available.
56    /// 2. The `invalidate_block_successors()` may be called *after* branches have been removed from
57    ///    their block, but we still need to remove them form the old block predecessor map.
58    ///
59    /// The redundant block stored here is always consistent with the CFG successor lists, even after
60    /// the IR has been edited.
61    pub predecessors: bforest::Map<Inst, Block>,
62
63    /// Set of blocks that are the targets of branches and jumps in this block.
64    /// The set is ordered by block number, indicated by the `()` comparator type.
65    pub successors: bforest::Set<Block>,
66}
67
68/// The Control Flow Graph maintains a mapping of blocks to their predecessors
69/// and successors where predecessors are basic blocks and successors are
70/// basic blocks.
71pub struct ControlFlowGraph {
72    data: SecondaryMap<Block, CFGNode>,
73    pred_forest: bforest::MapForest<Inst, Block>,
74    succ_forest: bforest::SetForest<Block>,
75    valid: bool,
76}
77
78impl ControlFlowGraph {
79    /// Allocate a new blank control flow graph.
80    pub fn new() -> Self {
81        Self {
82            data: SecondaryMap::new(),
83            valid: false,
84            pred_forest: bforest::MapForest::new(),
85            succ_forest: bforest::SetForest::new(),
86        }
87    }
88
89    /// Clear all data structures in this control flow graph.
90    pub fn clear(&mut self) {
91        self.data.clear();
92        self.pred_forest.clear();
93        self.succ_forest.clear();
94        self.valid = false;
95    }
96
97    /// Allocate and compute the control flow graph for `func`.
98    pub fn with_function(func: &Function) -> Self {
99        let mut cfg = Self::new();
100        cfg.compute(func);
101        cfg
102    }
103
104    /// Compute the control flow graph of `func`.
105    ///
106    /// This will clear and overwrite any information already stored in this data structure.
107    pub fn compute(&mut self, func: &Function) {
108        let _tt = timing::flowgraph();
109        self.clear();
110        self.data.resize(func.dfg.num_blocks());
111
112        for block in &func.layout {
113            self.compute_block(func, block);
114        }
115
116        self.valid = true;
117    }
118
119    fn compute_block(&mut self, func: &Function, block: Block) {
120        inst_predicates::visit_block_succs(func, block, |inst, dest, _| {
121            self.add_edge(block, inst, dest);
122        });
123    }
124
125    fn invalidate_block_successors(&mut self, block: Block) {
126        // Temporarily take ownership because we need mutable access to self.data inside the loop.
127        // Unfortunately borrowck cannot see that our mut accesses to predecessors don't alias
128        // our iteration over successors.
129        let mut successors = mem::replace(&mut self.data[block].successors, Default::default());
130        for succ in successors.iter(&self.succ_forest) {
131            self.data[succ]
132                .predecessors
133                .retain(&mut self.pred_forest, |_, &mut e| e != block);
134        }
135        successors.clear(&mut self.succ_forest);
136    }
137
138    /// Recompute the control flow graph of `block`.
139    ///
140    /// This is for use after modifying instructions within a specific block. It recomputes all edges
141    /// from `block` while leaving edges to `block` intact. Its functionality a subset of that of the
142    /// more expensive `compute`, and should be used when we know we don't need to recompute the CFG
143    /// from scratch, but rather that our changes have been restricted to specific blocks.
144    pub fn recompute_block(&mut self, func: &Function, block: Block) {
145        debug_assert!(self.is_valid());
146        self.invalidate_block_successors(block);
147        self.compute_block(func, block);
148    }
149
150    fn add_edge(&mut self, from: Block, from_inst: Inst, to: Block) {
151        self.data[from]
152            .successors
153            .insert(to, &mut self.succ_forest, &());
154        self.data[to]
155            .predecessors
156            .insert(from_inst, from, &mut self.pred_forest, &());
157    }
158
159    /// Get an iterator over the CFG predecessors to `block`.
160    pub fn pred_iter(&self, block: Block) -> PredIter<'_> {
161        PredIter(self.data[block].predecessors.iter(&self.pred_forest))
162    }
163
164    /// Get an iterator over the CFG successors to `block`.
165    pub fn succ_iter(&self, block: Block) -> SuccIter<'_> {
166        debug_assert!(self.is_valid());
167        self.data[block].successors.iter(&self.succ_forest)
168    }
169
170    /// Get an iterator over all blocks this control-flow graph has storage for,
171    /// in block order.
172    ///
173    /// This may include blocks that are not in the function's layout (e.g. a
174    /// block that was created but never inserted, or has since been removed);
175    /// such blocks have no predecessors or successors in the graph.
176    pub fn blocks(&self) -> impl ExactSizeIterator<Item = Block> + '_ {
177        self.data.keys()
178    }
179
180    /// Get the number of blocks this control-flow graph has storage for.
181    ///
182    /// This equals `func.dfg.num_blocks()` for the function the CFG was
183    /// computed from, and is an upper bound on the block indices appearing in
184    /// the graph.
185    pub fn num_blocks(&self) -> usize {
186        self.blocks().len()
187    }
188
189    /// Check if the CFG is in a valid state.
190    ///
191    /// Note that this doesn't perform any kind of validity checks. It simply checks if the
192    /// `compute()` method has been called since the last `clear()`. It does not check that the
193    /// CFG is consistent with the function.
194    pub fn is_valid(&self) -> bool {
195        self.valid
196    }
197}
198
199/// An iterator over block predecessors. The iterator type is `BlockPredecessor`.
200///
201/// Each predecessor is an instruction that branches to the block.
202pub struct PredIter<'a>(bforest::MapIter<'a, Inst, Block>);
203
204impl<'a> Iterator for PredIter<'a> {
205    type Item = BlockPredecessor;
206
207    fn next(&mut self) -> Option<BlockPredecessor> {
208        self.0.next().map(|(i, e)| BlockPredecessor::new(e, i))
209    }
210}
211
212/// An iterator over block successors. The iterator type is `Block`.
213pub type SuccIter<'a> = bforest::SetIter<'a, Block>;
214
215#[cfg(test)]
216mod tests {
217    use super::*;
218    use crate::cursor::{Cursor, FuncCursor};
219    use crate::ir::{InstBuilder, types};
220    use alloc::vec::Vec;
221
222    #[test]
223    fn empty() {
224        let func = Function::new();
225        ControlFlowGraph::with_function(&func);
226    }
227
228    #[test]
229    fn no_predecessors() {
230        let mut func = Function::new();
231        let block0 = func.dfg.make_block();
232        let block1 = func.dfg.make_block();
233        let block2 = func.dfg.make_block();
234        func.layout.append_block(block0);
235        func.layout.append_block(block1);
236        func.layout.append_block(block2);
237
238        let cfg = ControlFlowGraph::with_function(&func);
239
240        let mut fun_blocks = func.layout.blocks();
241        for block in func.layout.blocks() {
242            assert_eq!(block, fun_blocks.next().unwrap());
243            assert_eq!(cfg.pred_iter(block).count(), 0);
244            assert_eq!(cfg.succ_iter(block).count(), 0);
245        }
246    }
247
248    #[test]
249    fn branches_and_jumps() {
250        let mut func = Function::new();
251        let block0 = func.dfg.make_block();
252        let cond = func.dfg.append_block_param(block0, types::I32);
253        let block1 = func.dfg.make_block();
254        let block2 = func.dfg.make_block();
255
256        let br_block0_block2_block1;
257        let br_block1_block1_block2;
258
259        {
260            let mut cur = FuncCursor::new(&mut func);
261
262            cur.insert_block(block0);
263            br_block0_block2_block1 = cur.ins().brif(cond, block2, &[], block1, &[]);
264
265            cur.insert_block(block1);
266            br_block1_block1_block2 = cur.ins().brif(cond, block1, &[], block2, &[]);
267
268            cur.insert_block(block2);
269        }
270
271        let mut cfg = ControlFlowGraph::with_function(&func);
272
273        {
274            let block0_predecessors = cfg.pred_iter(block0).collect::<Vec<_>>();
275            let block1_predecessors = cfg.pred_iter(block1).collect::<Vec<_>>();
276            let block2_predecessors = cfg.pred_iter(block2).collect::<Vec<_>>();
277
278            let block0_successors = cfg.succ_iter(block0).collect::<Vec<_>>();
279            let block1_successors = cfg.succ_iter(block1).collect::<Vec<_>>();
280            let block2_successors = cfg.succ_iter(block2).collect::<Vec<_>>();
281
282            assert_eq!(block0_predecessors.len(), 0);
283            assert_eq!(block1_predecessors.len(), 2);
284            assert_eq!(block2_predecessors.len(), 2);
285
286            assert_eq!(
287                block1_predecessors
288                    .contains(&BlockPredecessor::new(block0, br_block0_block2_block1)),
289                true
290            );
291            assert_eq!(
292                block1_predecessors
293                    .contains(&BlockPredecessor::new(block1, br_block1_block1_block2)),
294                true
295            );
296            assert_eq!(
297                block2_predecessors
298                    .contains(&BlockPredecessor::new(block0, br_block0_block2_block1)),
299                true
300            );
301            assert_eq!(
302                block2_predecessors
303                    .contains(&BlockPredecessor::new(block1, br_block1_block1_block2)),
304                true
305            );
306
307            assert_eq!(block0_successors, [block1, block2]);
308            assert_eq!(block1_successors, [block1, block2]);
309            assert_eq!(block2_successors, []);
310        }
311
312        // Add a new block to hold a return instruction
313        let ret_block = func.dfg.make_block();
314
315        {
316            let mut cur = FuncCursor::new(&mut func);
317            cur.insert_block(ret_block);
318            cur.ins().return_(&[]);
319        }
320
321        // Change some instructions and recompute block0 and ret_block
322        func.replace(br_block0_block2_block1)
323            .brif(cond, block1, &[], ret_block, &[]);
324        cfg.recompute_block(&func, block0);
325        cfg.recompute_block(&func, ret_block);
326        let br_block0_block1_ret_block = br_block0_block2_block1;
327
328        {
329            let block0_predecessors = cfg.pred_iter(block0).collect::<Vec<_>>();
330            let block1_predecessors = cfg.pred_iter(block1).collect::<Vec<_>>();
331            let block2_predecessors = cfg.pred_iter(block2).collect::<Vec<_>>();
332
333            let block0_successors = cfg.succ_iter(block0);
334            let block1_successors = cfg.succ_iter(block1);
335            let block2_successors = cfg.succ_iter(block2);
336
337            assert_eq!(block0_predecessors.len(), 0);
338            assert_eq!(block1_predecessors.len(), 2);
339            assert_eq!(block2_predecessors.len(), 1);
340
341            assert_eq!(
342                block1_predecessors
343                    .contains(&BlockPredecessor::new(block0, br_block0_block1_ret_block)),
344                true
345            );
346            assert_eq!(
347                block1_predecessors
348                    .contains(&BlockPredecessor::new(block1, br_block1_block1_block2)),
349                true
350            );
351            assert_eq!(
352                block2_predecessors
353                    .contains(&BlockPredecessor::new(block0, br_block0_block1_ret_block)),
354                false
355            );
356            assert_eq!(
357                block2_predecessors
358                    .contains(&BlockPredecessor::new(block1, br_block1_block1_block2)),
359                true
360            );
361
362            assert_eq!(block0_successors.collect::<Vec<_>>(), [block1, ret_block]);
363            assert_eq!(block1_successors.collect::<Vec<_>>(), [block1, block2]);
364            assert_eq!(block2_successors.collect::<Vec<_>>(), []);
365        }
366    }
367}