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    /// Check if the CFG is in a valid state.
171    ///
172    /// Note that this doesn't perform any kind of validity checks. It simply checks if the
173    /// `compute()` method has been called since the last `clear()`. It does not check that the
174    /// CFG is consistent with the function.
175    pub fn is_valid(&self) -> bool {
176        self.valid
177    }
178}
179
180/// An iterator over block predecessors. The iterator type is `BlockPredecessor`.
181///
182/// Each predecessor is an instruction that branches to the block.
183pub struct PredIter<'a>(bforest::MapIter<'a, Inst, Block>);
184
185impl<'a> Iterator for PredIter<'a> {
186    type Item = BlockPredecessor;
187
188    fn next(&mut self) -> Option<BlockPredecessor> {
189        self.0.next().map(|(i, e)| BlockPredecessor::new(e, i))
190    }
191}
192
193/// An iterator over block successors. The iterator type is `Block`.
194pub type SuccIter<'a> = bforest::SetIter<'a, Block>;
195
196#[cfg(test)]
197mod tests {
198    use super::*;
199    use crate::cursor::{Cursor, FuncCursor};
200    use crate::ir::{types, InstBuilder};
201    use alloc::vec::Vec;
202
203    #[test]
204    fn empty() {
205        let func = Function::new();
206        ControlFlowGraph::with_function(&func);
207    }
208
209    #[test]
210    fn no_predecessors() {
211        let mut func = Function::new();
212        let block0 = func.dfg.make_block();
213        let block1 = func.dfg.make_block();
214        let block2 = func.dfg.make_block();
215        func.layout.append_block(block0);
216        func.layout.append_block(block1);
217        func.layout.append_block(block2);
218
219        let cfg = ControlFlowGraph::with_function(&func);
220
221        let mut fun_blocks = func.layout.blocks();
222        for block in func.layout.blocks() {
223            assert_eq!(block, fun_blocks.next().unwrap());
224            assert_eq!(cfg.pred_iter(block).count(), 0);
225            assert_eq!(cfg.succ_iter(block).count(), 0);
226        }
227    }
228
229    #[test]
230    fn branches_and_jumps() {
231        let mut func = Function::new();
232        let block0 = func.dfg.make_block();
233        let cond = func.dfg.append_block_param(block0, types::I32);
234        let block1 = func.dfg.make_block();
235        let block2 = func.dfg.make_block();
236
237        let br_block0_block2_block1;
238        let br_block1_block1_block2;
239
240        {
241            let mut cur = FuncCursor::new(&mut func);
242
243            cur.insert_block(block0);
244            br_block0_block2_block1 = cur.ins().brif(cond, block2, &[], block1, &[]);
245
246            cur.insert_block(block1);
247            br_block1_block1_block2 = cur.ins().brif(cond, block1, &[], block2, &[]);
248
249            cur.insert_block(block2);
250        }
251
252        let mut cfg = ControlFlowGraph::with_function(&func);
253
254        {
255            let block0_predecessors = cfg.pred_iter(block0).collect::<Vec<_>>();
256            let block1_predecessors = cfg.pred_iter(block1).collect::<Vec<_>>();
257            let block2_predecessors = cfg.pred_iter(block2).collect::<Vec<_>>();
258
259            let block0_successors = cfg.succ_iter(block0).collect::<Vec<_>>();
260            let block1_successors = cfg.succ_iter(block1).collect::<Vec<_>>();
261            let block2_successors = cfg.succ_iter(block2).collect::<Vec<_>>();
262
263            assert_eq!(block0_predecessors.len(), 0);
264            assert_eq!(block1_predecessors.len(), 2);
265            assert_eq!(block2_predecessors.len(), 2);
266
267            assert_eq!(
268                block1_predecessors
269                    .contains(&BlockPredecessor::new(block0, br_block0_block2_block1)),
270                true
271            );
272            assert_eq!(
273                block1_predecessors
274                    .contains(&BlockPredecessor::new(block1, br_block1_block1_block2)),
275                true
276            );
277            assert_eq!(
278                block2_predecessors
279                    .contains(&BlockPredecessor::new(block0, br_block0_block2_block1)),
280                true
281            );
282            assert_eq!(
283                block2_predecessors
284                    .contains(&BlockPredecessor::new(block1, br_block1_block1_block2)),
285                true
286            );
287
288            assert_eq!(block0_successors, [block1, block2]);
289            assert_eq!(block1_successors, [block1, block2]);
290            assert_eq!(block2_successors, []);
291        }
292
293        // Add a new block to hold a return instruction
294        let ret_block = func.dfg.make_block();
295
296        {
297            let mut cur = FuncCursor::new(&mut func);
298            cur.insert_block(ret_block);
299            cur.ins().return_(&[]);
300        }
301
302        // Change some instructions and recompute block0 and ret_block
303        func.dfg
304            .replace(br_block0_block2_block1)
305            .brif(cond, block1, &[], ret_block, &[]);
306        cfg.recompute_block(&func, block0);
307        cfg.recompute_block(&func, ret_block);
308        let br_block0_block1_ret_block = br_block0_block2_block1;
309
310        {
311            let block0_predecessors = cfg.pred_iter(block0).collect::<Vec<_>>();
312            let block1_predecessors = cfg.pred_iter(block1).collect::<Vec<_>>();
313            let block2_predecessors = cfg.pred_iter(block2).collect::<Vec<_>>();
314
315            let block0_successors = cfg.succ_iter(block0);
316            let block1_successors = cfg.succ_iter(block1);
317            let block2_successors = cfg.succ_iter(block2);
318
319            assert_eq!(block0_predecessors.len(), 0);
320            assert_eq!(block1_predecessors.len(), 2);
321            assert_eq!(block2_predecessors.len(), 1);
322
323            assert_eq!(
324                block1_predecessors
325                    .contains(&BlockPredecessor::new(block0, br_block0_block1_ret_block)),
326                true
327            );
328            assert_eq!(
329                block1_predecessors
330                    .contains(&BlockPredecessor::new(block1, br_block1_block1_block2)),
331                true
332            );
333            assert_eq!(
334                block2_predecessors
335                    .contains(&BlockPredecessor::new(block0, br_block0_block1_ret_block)),
336                false
337            );
338            assert_eq!(
339                block2_predecessors
340                    .contains(&BlockPredecessor::new(block1, br_block1_block1_block2)),
341                true
342            );
343
344            assert_eq!(block0_successors.collect::<Vec<_>>(), [block1, ret_block]);
345            assert_eq!(block1_successors.collect::<Vec<_>>(), [block1, block2]);
346            assert_eq!(block2_successors.collect::<Vec<_>>(), []);
347        }
348    }
349}