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 //! brz vx, Block1 ; end of basic block 15 //! 16 //! ... ; beginning of basic block 17 //! 18 //! ... 19 //! 20 //! jmp Block2 ; end of basic block 21 //! ``` 22 //! 23 //! Here `Block1` and `Block2` would each have a single predecessor denoted as `(Block0, brz)` 24 //! and `(Block0, jmp Block2)` respectively. 25 26 use crate::bforest; 27 use crate::entity::SecondaryMap; 28 use crate::ir::instructions::BranchInfo; 29 use crate::ir::{Block, Function, Inst}; 30 use crate::timing; 31 use core::mem; 32 33 /// A basic block denoted by its enclosing Block and last instruction. 34 #[derive(Debug, PartialEq, Eq)] 35 pub struct BlockPredecessor { 36 /// Enclosing Block key. 37 pub block: Block, 38 /// Last instruction in the basic block. 39 pub inst: Inst, 40 } 41 42 impl BlockPredecessor { 43 /// Convenient method to construct new BlockPredecessor. 44 pub fn new(block: Block, inst: Inst) -> Self { 45 Self { block, inst } 46 } 47 } 48 49 /// A container for the successors and predecessors of some Block. 50 #[derive(Clone, Default)] 51 struct CFGNode { 52 /// Instructions that can branch or jump to this block. 53 /// 54 /// This maps branch instruction -> predecessor block which is redundant since the block containing 55 /// the branch instruction is available from the `layout.inst_block()` method. We store the 56 /// redundant information because: 57 /// 58 /// 1. Many `pred_iter()` consumers want the block anyway, so it is handily available. 59 /// 2. The `invalidate_block_successors()` may be called *after* branches have been removed from 60 /// their block, but we still need to remove them form the old block predecessor map. 61 /// 62 /// The redundant block stored here is always consistent with the CFG successor lists, even after 63 /// the IR has been edited. 64 pub predecessors: bforest::Map<Inst, Block>, 65 66 /// Set of blocks that are the targets of branches and jumps in this block. 67 /// The set is ordered by block number, indicated by the `()` comparator type. 68 pub successors: bforest::Set<Block>, 69 } 70 71 /// The Control Flow Graph maintains a mapping of blocks to their predecessors 72 /// and successors where predecessors are basic blocks and successors are 73 /// basic blocks. 74 pub struct ControlFlowGraph { 75 data: SecondaryMap<Block, CFGNode>, 76 pred_forest: bforest::MapForest<Inst, Block>, 77 succ_forest: bforest::SetForest<Block>, 78 valid: bool, 79 } 80 81 impl ControlFlowGraph { 82 /// Allocate a new blank control flow graph. 83 pub fn new() -> Self { 84 Self { 85 data: SecondaryMap::new(), 86 valid: false, 87 pred_forest: bforest::MapForest::new(), 88 succ_forest: bforest::SetForest::new(), 89 } 90 } 91 92 /// Clear all data structures in this control flow graph. 93 pub fn clear(&mut self) { 94 self.data.clear(); 95 self.pred_forest.clear(); 96 self.succ_forest.clear(); 97 self.valid = false; 98 } 99 100 /// Allocate and compute the control flow graph for `func`. 101 pub fn with_function(func: &Function) -> Self { 102 let mut cfg = Self::new(); 103 cfg.compute(func); 104 cfg 105 } 106 107 /// Compute the control flow graph of `func`. 108 /// 109 /// This will clear and overwrite any information already stored in this data structure. 110 pub fn compute(&mut self, func: &Function) { 111 let _tt = timing::flowgraph(); 112 self.clear(); 113 self.data.resize(func.dfg.num_blocks()); 114 115 for block in &func.layout { 116 self.compute_block(func, block); 117 } 118 119 self.valid = true; 120 } 121 122 fn compute_block(&mut self, func: &Function, block: Block) { 123 for inst in func.layout.block_likely_branches(block) { 124 match func.dfg.analyze_branch(inst) { 125 BranchInfo::SingleDest(dest) => { 126 self.add_edge(block, inst, dest.block(&func.dfg.value_lists)); 127 } 128 BranchInfo::Conditional(block_then, block_else) => { 129 self.add_edge(block, inst, block_then.block(&func.dfg.value_lists)); 130 self.add_edge(block, inst, block_else.block(&func.dfg.value_lists)); 131 } 132 BranchInfo::Table(jt, dest) => { 133 self.add_edge(block, inst, dest); 134 135 for dest in func.jump_tables[jt].iter() { 136 self.add_edge(block, inst, *dest); 137 } 138 } 139 BranchInfo::NotABranch => {} 140 } 141 } 142 } 143 144 fn invalidate_block_successors(&mut self, block: Block) { 145 // Temporarily take ownership because we need mutable access to self.data inside the loop. 146 // Unfortunately borrowck cannot see that our mut accesses to predecessors don't alias 147 // our iteration over successors. 148 let mut successors = mem::replace(&mut self.data[block].successors, Default::default()); 149 for succ in successors.iter(&self.succ_forest) { 150 self.data[succ] 151 .predecessors 152 .retain(&mut self.pred_forest, |_, &mut e| e != block); 153 } 154 successors.clear(&mut self.succ_forest); 155 } 156 157 /// Recompute the control flow graph of `block`. 158 /// 159 /// This is for use after modifying instructions within a specific block. It recomputes all edges 160 /// from `block` while leaving edges to `block` intact. Its functionality a subset of that of the 161 /// more expensive `compute`, and should be used when we know we don't need to recompute the CFG 162 /// from scratch, but rather that our changes have been restricted to specific blocks. 163 pub fn recompute_block(&mut self, func: &Function, block: Block) { 164 debug_assert!(self.is_valid()); 165 self.invalidate_block_successors(block); 166 self.compute_block(func, block); 167 } 168 169 fn add_edge(&mut self, from: Block, from_inst: Inst, to: Block) { 170 self.data[from] 171 .successors 172 .insert(to, &mut self.succ_forest, &()); 173 self.data[to] 174 .predecessors 175 .insert(from_inst, from, &mut self.pred_forest, &()); 176 } 177 178 /// Get an iterator over the CFG predecessors to `block`. 179 pub fn pred_iter(&self, block: Block) -> PredIter { 180 PredIter(self.data[block].predecessors.iter(&self.pred_forest)) 181 } 182 183 /// Get an iterator over the CFG successors to `block`. 184 pub fn succ_iter(&self, block: Block) -> SuccIter { 185 debug_assert!(self.is_valid()); 186 self.data[block].successors.iter(&self.succ_forest) 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. 202 pub struct PredIter<'a>(bforest::MapIter<'a, Inst, Block>); 203 204 impl<'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`. 213 pub type SuccIter<'a> = bforest::SetIter<'a, Block>; 214 215 #[cfg(test)] 216 mod tests { 217 use super::*; 218 use crate::cursor::{Cursor, FuncCursor}; 219 use crate::ir::{types, Function, InstBuilder}; 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; 257 let br_block1_block1; 258 let jmp_block0_block1; 259 let jmp_block1_block2; 260 261 { 262 let mut cur = FuncCursor::new(&mut func); 263 264 cur.insert_block(block0); 265 br_block0_block2 = cur.ins().brnz(cond, block2, &[]); 266 jmp_block0_block1 = cur.ins().jump(block1, &[]); 267 268 cur.insert_block(block1); 269 br_block1_block1 = cur.ins().brnz(cond, block1, &[]); 270 jmp_block1_block2 = cur.ins().jump(block2, &[]); 271 272 cur.insert_block(block2); 273 } 274 275 let mut cfg = ControlFlowGraph::with_function(&func); 276 277 { 278 let block0_predecessors = cfg.pred_iter(block0).collect::<Vec<_>>(); 279 let block1_predecessors = cfg.pred_iter(block1).collect::<Vec<_>>(); 280 let block2_predecessors = cfg.pred_iter(block2).collect::<Vec<_>>(); 281 282 let block0_successors = cfg.succ_iter(block0).collect::<Vec<_>>(); 283 let block1_successors = cfg.succ_iter(block1).collect::<Vec<_>>(); 284 let block2_successors = cfg.succ_iter(block2).collect::<Vec<_>>(); 285 286 assert_eq!(block0_predecessors.len(), 0); 287 assert_eq!(block1_predecessors.len(), 2); 288 assert_eq!(block2_predecessors.len(), 2); 289 290 assert_eq!( 291 block1_predecessors.contains(&BlockPredecessor::new(block0, jmp_block0_block1)), 292 true 293 ); 294 assert_eq!( 295 block1_predecessors.contains(&BlockPredecessor::new(block1, br_block1_block1)), 296 true 297 ); 298 assert_eq!( 299 block2_predecessors.contains(&BlockPredecessor::new(block0, br_block0_block2)), 300 true 301 ); 302 assert_eq!( 303 block2_predecessors.contains(&BlockPredecessor::new(block1, jmp_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 // Change some instructions and recompute block0 313 func.dfg.replace(br_block0_block2).brnz(cond, block1, &[]); 314 func.dfg.replace(jmp_block0_block1).return_(&[]); 315 cfg.recompute_block(&mut func, block0); 316 let br_block0_block1 = br_block0_block2; 317 318 { 319 let block0_predecessors = cfg.pred_iter(block0).collect::<Vec<_>>(); 320 let block1_predecessors = cfg.pred_iter(block1).collect::<Vec<_>>(); 321 let block2_predecessors = cfg.pred_iter(block2).collect::<Vec<_>>(); 322 323 let block0_successors = cfg.succ_iter(block0); 324 let block1_successors = cfg.succ_iter(block1); 325 let block2_successors = cfg.succ_iter(block2); 326 327 assert_eq!(block0_predecessors.len(), 0); 328 assert_eq!(block1_predecessors.len(), 2); 329 assert_eq!(block2_predecessors.len(), 1); 330 331 assert_eq!( 332 block1_predecessors.contains(&BlockPredecessor::new(block0, br_block0_block1)), 333 true 334 ); 335 assert_eq!( 336 block1_predecessors.contains(&BlockPredecessor::new(block1, br_block1_block1)), 337 true 338 ); 339 assert_eq!( 340 block2_predecessors.contains(&BlockPredecessor::new(block0, br_block0_block2)), 341 false 342 ); 343 assert_eq!( 344 block2_predecessors.contains(&BlockPredecessor::new(block1, jmp_block1_block2)), 345 true 346 ); 347 348 assert_eq!(block0_successors.collect::<Vec<_>>(), [block1]); 349 assert_eq!(block1_successors.collect::<Vec<_>>(), [block1, block2]); 350 assert_eq!(block2_successors.collect::<Vec<_>>(), []); 351 } 352 } 353 } 354