//! Pass to remove empty blocks. use crate::entity::EntityRef; use crate::ir::{Block, BlockTarget, FunctionBody, Terminator, Value, ValueDef}; use std::borrow::Cow; use std::collections::HashSet; #[derive(Clone, Debug)] struct Forwarding { to: Block, args: Vec<ForwardingArg>, } #[derive(Clone, Copy, Debug)] enum ForwardingArg { BlockParam(usize), Value(Value), } impl Forwarding { fn compose(a: &Forwarding, b: &Forwarding) -> Forwarding { // `b` should be the target of `a.to`, but we can't assert // that here. The composed target is thus `b.to`. let to = b.to; // For each arg in `b.args`, evaluate, replacing any // `BlockParam` with the corresponding value from `a.args`. let args = b .args .iter() .map(|&arg| match arg { ForwardingArg::BlockParam(idx) => a.args[idx].clone(), ForwardingArg::Value(v) => ForwardingArg::Value(v), }) .collect::<Vec<_>>(); Forwarding { to, args } } } fn block_to_forwarding(body: &FunctionBody, block: Block) -> Option<Forwarding> { // Must be empty except for terminator, and must have an // unconditional-branch terminator. if body.blocks[block].insts.len() > 0 { return None; } let target = match &body.blocks[block].terminator { &Terminator::Br { ref target } => target, _ => return None, }; // If conditions met, then gather ForwardingArgs. let args = target .args .iter() .map(|&arg| { let arg = body.resolve_alias(arg); match &body.values[arg] { &ValueDef::BlockParam(param_block, index, _) if param_block == block => { ForwardingArg::BlockParam(index) } _ => ForwardingArg::Value(arg), } }) .collect::<Vec<_>>(); Some(Forwarding { to: target.block, args, }) } fn rewrite_target(forwardings: &[Option<Forwarding>], target: &BlockTarget) -> Option<BlockTarget> { if !forwardings[target.block.index()].is_some() { return None; } let mut forwarding = Cow::Borrowed(forwardings[target.block.index()].as_ref().unwrap()); let mut seen = HashSet::new(); while forwardings[forwarding.to.index()].is_some() && seen.insert(forwarding.to.index()) { forwarding = Cow::Owned(Forwarding::compose( &forwarding, forwardings[forwarding.to.index()].as_ref().unwrap(), )); } let args = forwarding .args .iter() .map(|arg| match arg { &ForwardingArg::Value(v) => v, &ForwardingArg::BlockParam(idx) => target.args[idx], }) .collect::<Vec<_>>(); Some(BlockTarget { block: forwarding.to, args, }) } pub fn run(body: &mut FunctionBody) { // Identify empty blocks, and to where they should forward. let forwardings = body .blocks .iter() .map(|block| { if block != body.entry { block_to_forwarding(body, block) } else { None } }) .collect::<Vec<_>>(); // Rewrite every target according to a forwarding (or potentially // a chain of composed forwardings). for block_data in body.blocks.values_mut() { block_data.terminator.update_targets(|target| { if let Some(new_target) = rewrite_target(&forwardings[..], target) { *target = new_target; } }); } // Recompute preds/succs. body.recompute_edges(); }