Path: blob/main/cranelift/codegen/src/flowgraph.rs
1693 views
//! A control flow graph represented as mappings of basic blocks to their predecessors1//! and successors.2//!3//! Successors are represented as basic blocks while predecessors are represented by basic4//! blocks. Basic blocks are denoted by tuples of block and branch/jump instructions. Each5//! predecessor tuple corresponds to the end of a basic block.6//!7//! ```c8//! Block0:9//! ... ; beginning of basic block10//!11//! ...12//!13//! brif vx, Block1, Block2 ; end of basic block14//!15//! Block1:16//! jump block317//! ```18//!19//! Here `Block1` and `Block2` would each have a single predecessor denoted as `(Block0, brif)`,20//! while `Block3` would have a single predecessor denoted as `(Block1, jump block3)`.2122use crate::bforest;23use crate::entity::SecondaryMap;24use crate::inst_predicates;25use crate::ir::{Block, Function, Inst};26use crate::timing;27use core::mem;2829/// A basic block denoted by its enclosing Block and last instruction.30#[derive(Debug, PartialEq, Eq)]31pub struct BlockPredecessor {32/// Enclosing Block key.33pub block: Block,34/// Last instruction in the basic block.35pub inst: Inst,36}3738impl BlockPredecessor {39/// Convenient method to construct new BlockPredecessor.40pub fn new(block: Block, inst: Inst) -> Self {41Self { block, inst }42}43}4445/// A container for the successors and predecessors of some Block.46#[derive(Clone, Default)]47struct CFGNode {48/// Instructions that can branch or jump to this block.49///50/// This maps branch instruction -> predecessor block which is redundant since the block containing51/// the branch instruction is available from the `layout.inst_block()` method. We store the52/// redundant information because:53///54/// 1. Many `pred_iter()` consumers want the block anyway, so it is handily available.55/// 2. The `invalidate_block_successors()` may be called *after* branches have been removed from56/// their block, but we still need to remove them form the old block predecessor map.57///58/// The redundant block stored here is always consistent with the CFG successor lists, even after59/// the IR has been edited.60pub predecessors: bforest::Map<Inst, Block>,6162/// Set of blocks that are the targets of branches and jumps in this block.63/// The set is ordered by block number, indicated by the `()` comparator type.64pub successors: bforest::Set<Block>,65}6667/// The Control Flow Graph maintains a mapping of blocks to their predecessors68/// and successors where predecessors are basic blocks and successors are69/// basic blocks.70pub struct ControlFlowGraph {71data: SecondaryMap<Block, CFGNode>,72pred_forest: bforest::MapForest<Inst, Block>,73succ_forest: bforest::SetForest<Block>,74valid: bool,75}7677impl ControlFlowGraph {78/// Allocate a new blank control flow graph.79pub fn new() -> Self {80Self {81data: SecondaryMap::new(),82valid: false,83pred_forest: bforest::MapForest::new(),84succ_forest: bforest::SetForest::new(),85}86}8788/// Clear all data structures in this control flow graph.89pub fn clear(&mut self) {90self.data.clear();91self.pred_forest.clear();92self.succ_forest.clear();93self.valid = false;94}9596/// Allocate and compute the control flow graph for `func`.97pub fn with_function(func: &Function) -> Self {98let mut cfg = Self::new();99cfg.compute(func);100cfg101}102103/// Compute the control flow graph of `func`.104///105/// This will clear and overwrite any information already stored in this data structure.106pub fn compute(&mut self, func: &Function) {107let _tt = timing::flowgraph();108self.clear();109self.data.resize(func.dfg.num_blocks());110111for block in &func.layout {112self.compute_block(func, block);113}114115self.valid = true;116}117118fn compute_block(&mut self, func: &Function, block: Block) {119inst_predicates::visit_block_succs(func, block, |inst, dest, _| {120self.add_edge(block, inst, dest);121});122}123124fn invalidate_block_successors(&mut self, block: Block) {125// Temporarily take ownership because we need mutable access to self.data inside the loop.126// Unfortunately borrowck cannot see that our mut accesses to predecessors don't alias127// our iteration over successors.128let mut successors = mem::replace(&mut self.data[block].successors, Default::default());129for succ in successors.iter(&self.succ_forest) {130self.data[succ]131.predecessors132.retain(&mut self.pred_forest, |_, &mut e| e != block);133}134successors.clear(&mut self.succ_forest);135}136137/// Recompute the control flow graph of `block`.138///139/// This is for use after modifying instructions within a specific block. It recomputes all edges140/// from `block` while leaving edges to `block` intact. Its functionality a subset of that of the141/// more expensive `compute`, and should be used when we know we don't need to recompute the CFG142/// from scratch, but rather that our changes have been restricted to specific blocks.143pub fn recompute_block(&mut self, func: &Function, block: Block) {144debug_assert!(self.is_valid());145self.invalidate_block_successors(block);146self.compute_block(func, block);147}148149fn add_edge(&mut self, from: Block, from_inst: Inst, to: Block) {150self.data[from]151.successors152.insert(to, &mut self.succ_forest, &());153self.data[to]154.predecessors155.insert(from_inst, from, &mut self.pred_forest, &());156}157158/// Get an iterator over the CFG predecessors to `block`.159pub fn pred_iter(&self, block: Block) -> PredIter<'_> {160PredIter(self.data[block].predecessors.iter(&self.pred_forest))161}162163/// Get an iterator over the CFG successors to `block`.164pub fn succ_iter(&self, block: Block) -> SuccIter<'_> {165debug_assert!(self.is_valid());166self.data[block].successors.iter(&self.succ_forest)167}168169/// Check if the CFG is in a valid state.170///171/// Note that this doesn't perform any kind of validity checks. It simply checks if the172/// `compute()` method has been called since the last `clear()`. It does not check that the173/// CFG is consistent with the function.174pub fn is_valid(&self) -> bool {175self.valid176}177}178179/// An iterator over block predecessors. The iterator type is `BlockPredecessor`.180///181/// Each predecessor is an instruction that branches to the block.182pub struct PredIter<'a>(bforest::MapIter<'a, Inst, Block>);183184impl<'a> Iterator for PredIter<'a> {185type Item = BlockPredecessor;186187fn next(&mut self) -> Option<BlockPredecessor> {188self.0.next().map(|(i, e)| BlockPredecessor::new(e, i))189}190}191192/// An iterator over block successors. The iterator type is `Block`.193pub type SuccIter<'a> = bforest::SetIter<'a, Block>;194195#[cfg(test)]196mod tests {197use super::*;198use crate::cursor::{Cursor, FuncCursor};199use crate::ir::{InstBuilder, types};200use alloc::vec::Vec;201202#[test]203fn empty() {204let func = Function::new();205ControlFlowGraph::with_function(&func);206}207208#[test]209fn no_predecessors() {210let mut func = Function::new();211let block0 = func.dfg.make_block();212let block1 = func.dfg.make_block();213let block2 = func.dfg.make_block();214func.layout.append_block(block0);215func.layout.append_block(block1);216func.layout.append_block(block2);217218let cfg = ControlFlowGraph::with_function(&func);219220let mut fun_blocks = func.layout.blocks();221for block in func.layout.blocks() {222assert_eq!(block, fun_blocks.next().unwrap());223assert_eq!(cfg.pred_iter(block).count(), 0);224assert_eq!(cfg.succ_iter(block).count(), 0);225}226}227228#[test]229fn branches_and_jumps() {230let mut func = Function::new();231let block0 = func.dfg.make_block();232let cond = func.dfg.append_block_param(block0, types::I32);233let block1 = func.dfg.make_block();234let block2 = func.dfg.make_block();235236let br_block0_block2_block1;237let br_block1_block1_block2;238239{240let mut cur = FuncCursor::new(&mut func);241242cur.insert_block(block0);243br_block0_block2_block1 = cur.ins().brif(cond, block2, &[], block1, &[]);244245cur.insert_block(block1);246br_block1_block1_block2 = cur.ins().brif(cond, block1, &[], block2, &[]);247248cur.insert_block(block2);249}250251let mut cfg = ControlFlowGraph::with_function(&func);252253{254let block0_predecessors = cfg.pred_iter(block0).collect::<Vec<_>>();255let block1_predecessors = cfg.pred_iter(block1).collect::<Vec<_>>();256let block2_predecessors = cfg.pred_iter(block2).collect::<Vec<_>>();257258let block0_successors = cfg.succ_iter(block0).collect::<Vec<_>>();259let block1_successors = cfg.succ_iter(block1).collect::<Vec<_>>();260let block2_successors = cfg.succ_iter(block2).collect::<Vec<_>>();261262assert_eq!(block0_predecessors.len(), 0);263assert_eq!(block1_predecessors.len(), 2);264assert_eq!(block2_predecessors.len(), 2);265266assert_eq!(267block1_predecessors268.contains(&BlockPredecessor::new(block0, br_block0_block2_block1)),269true270);271assert_eq!(272block1_predecessors273.contains(&BlockPredecessor::new(block1, br_block1_block1_block2)),274true275);276assert_eq!(277block2_predecessors278.contains(&BlockPredecessor::new(block0, br_block0_block2_block1)),279true280);281assert_eq!(282block2_predecessors283.contains(&BlockPredecessor::new(block1, br_block1_block1_block2)),284true285);286287assert_eq!(block0_successors, [block1, block2]);288assert_eq!(block1_successors, [block1, block2]);289assert_eq!(block2_successors, []);290}291292// Add a new block to hold a return instruction293let ret_block = func.dfg.make_block();294295{296let mut cur = FuncCursor::new(&mut func);297cur.insert_block(ret_block);298cur.ins().return_(&[]);299}300301// Change some instructions and recompute block0 and ret_block302func.dfg303.replace(br_block0_block2_block1)304.brif(cond, block1, &[], ret_block, &[]);305cfg.recompute_block(&func, block0);306cfg.recompute_block(&func, ret_block);307let br_block0_block1_ret_block = br_block0_block2_block1;308309{310let block0_predecessors = cfg.pred_iter(block0).collect::<Vec<_>>();311let block1_predecessors = cfg.pred_iter(block1).collect::<Vec<_>>();312let block2_predecessors = cfg.pred_iter(block2).collect::<Vec<_>>();313314let block0_successors = cfg.succ_iter(block0);315let block1_successors = cfg.succ_iter(block1);316let block2_successors = cfg.succ_iter(block2);317318assert_eq!(block0_predecessors.len(), 0);319assert_eq!(block1_predecessors.len(), 2);320assert_eq!(block2_predecessors.len(), 1);321322assert_eq!(323block1_predecessors324.contains(&BlockPredecessor::new(block0, br_block0_block1_ret_block)),325true326);327assert_eq!(328block1_predecessors329.contains(&BlockPredecessor::new(block1, br_block1_block1_block2)),330true331);332assert_eq!(333block2_predecessors334.contains(&BlockPredecessor::new(block0, br_block0_block1_ret_block)),335false336);337assert_eq!(338block2_predecessors339.contains(&BlockPredecessor::new(block1, br_block1_block1_block2)),340true341);342343assert_eq!(block0_successors.collect::<Vec<_>>(), [block1, ret_block]);344assert_eq!(block1_successors.collect::<Vec<_>>(), [block1, block2]);345assert_eq!(block2_successors.collect::<Vec<_>>(), []);346}347}348}349350351