/* * Released under the terms of the Apache 2.0 license with LLVM * exception. See `LICENSE` for details. */ //! Lightweight CFG analyses. use crate::{domtree, postorder, Block, Function, Inst, ProgPoint, RegAllocError}; use alloc::vec; use alloc::vec::Vec; use smallvec::{smallvec, SmallVec}; #[derive(Clone, Debug)] pub struct CFGInfo { /// Postorder traversal of blocks. pub postorder: Vec, /// Domtree parents, indexed by block. pub domtree: Vec, /// For each instruction, the block it belongs to. pub insn_block: Vec, /// For each block, the first instruction. pub block_entry: Vec, /// For each block, the last instruction. pub block_exit: Vec, /// For each block, what is the approximate loop depth? /// /// This measure is fully precise iff the input CFG is reducible /// and blocks are in RPO, so that loop backedges are precisely /// those whose block target indices are less than their source /// indices. Otherwise, it will be approximate, but should still /// be usable for heuristic purposes. pub approx_loop_depth: Vec, } impl CFGInfo { pub fn new(f: &F, needs_loop_depth: bool) -> Result { let postorder = postorder::calculate(f.num_blocks(), f.entry_block(), |block| { f.block_succs(block) }); let domtree = domtree::calculate( f.num_blocks(), |block| f.block_preds(block), &postorder[..], f.entry_block(), ); let mut insn_block = vec![Block::invalid(); f.num_insts()]; let mut block_entry = vec![ProgPoint::before(Inst::invalid()); f.num_blocks()]; let mut block_exit = vec![ProgPoint::before(Inst::invalid()); f.num_blocks()]; let mut backedge_in = vec![0; f.num_blocks()]; let mut backedge_out = vec![0; f.num_blocks()]; for block in 0..f.num_blocks() { let block = Block::new(block); for inst in f.block_insns(block).iter() { insn_block[inst.index()] = block; } block_entry[block.index()] = ProgPoint::before(f.block_insns(block).first()); block_exit[block.index()] = ProgPoint::after(f.block_insns(block).last()); // Check critical edge condition: if there is more than // one predecessor, each must have only one successor // (this block). let preds = f.block_preds(block).len() + if block == f.entry_block() { 1 } else { 0 }; if preds > 1 { for &pred in f.block_preds(block) { let succs = f.block_succs(pred).len(); if succs > 1 { return Err(RegAllocError::CritEdge(pred, block)); } } } // Check branch-arg condition: if any successors have more // than one predecessor (given above, there will only be // one such successor), then the last instruction of this // block (the branch) cannot have any args other than the // blockparams. let mut require_no_branch_args = false; for &succ in f.block_succs(block) { let preds = f.block_preds(succ).len() + if succ == f.entry_block() { 1 } else { 0 }; if preds > 1 { require_no_branch_args = true; } } if require_no_branch_args { let last = f.block_insns(block).last(); if !f.inst_operands(last).is_empty() { return Err(RegAllocError::DisallowedBranchArg(last)); } } for &succ in f.block_succs(block) { if succ.index() <= block.index() { backedge_in[succ.index()] += 1; backedge_out[block.index()] += 1; } } } let mut approx_loop_depth = vec![]; if needs_loop_depth { let mut backedge_stack: SmallVec<[usize; 4]> = smallvec![]; let mut cur_depth = 0; for block in 0..f.num_blocks() { if backedge_in[block] > 0 { cur_depth += 1; backedge_stack.push(backedge_in[block]); } approx_loop_depth.push(cur_depth); while backedge_stack.len() > 0 && backedge_out[block] > 0 { backedge_out[block] -= 1; *backedge_stack.last_mut().unwrap() -= 1; if *backedge_stack.last().unwrap() == 0 { cur_depth -= 1; backedge_stack.pop(); } } } } Ok(CFGInfo { postorder, domtree, insn_block, block_entry, block_exit, approx_loop_depth, }) } pub fn dominates(&self, a: Block, b: Block) -> bool { domtree::dominates(&self.domtree[..], a, b) } }