78 lines
2.3 KiB
Rust
78 lines
2.3 KiB
Rust
//! Pass to remove empty blocks.
|
|
|
|
use crate::entity::EntityRef;
|
|
use crate::ir::{Block, BlockTarget, FunctionBody, Terminator};
|
|
|
|
/// Determines whether a block (i) has no blockparams, and (ii) is
|
|
/// solely a jump to another block. We can remove these blocks.
|
|
///
|
|
/// Why can't we remove blocks that are solely jumps but *do* have
|
|
/// blockparams? Because They still serve a purpose in SSA: they
|
|
/// define these blockparams as a join of multiple possible other
|
|
/// definitions in preds.
|
|
fn block_is_empty_jump(body: &FunctionBody, block: Block) -> Option<BlockTarget> {
|
|
// Must be empty except for terminator, and must have no
|
|
// blockparams, and must have an unconditional-branch terminator.
|
|
if body.blocks[block].insts.len() > 0 {
|
|
return None;
|
|
}
|
|
if body.blocks[block].params.len() > 0 {
|
|
return None;
|
|
}
|
|
let target = match &body.blocks[block].terminator {
|
|
&Terminator::Br { ref target } => target,
|
|
_ => return None,
|
|
};
|
|
|
|
Some(target.clone())
|
|
}
|
|
|
|
fn rewrite_target(
|
|
forwardings: &[Option<BlockTarget>],
|
|
target: &BlockTarget,
|
|
) -> Option<BlockTarget> {
|
|
if target.args.len() > 0 {
|
|
return None;
|
|
}
|
|
forwardings[target.block.index()].clone()
|
|
}
|
|
|
|
pub fn run(body: &mut FunctionBody) {
|
|
log::trace!(
|
|
"empty_blocks: running on func:\n{}\n",
|
|
body.display_verbose("| ", None)
|
|
);
|
|
|
|
// Identify empty blocks, and to where they should forward.
|
|
let forwardings = body
|
|
.blocks
|
|
.iter()
|
|
.map(|block| {
|
|
if block != body.entry {
|
|
block_is_empty_jump(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) {
|
|
log::trace!("empty_blocks: replacing {:?} with {:?}", target, new_target);
|
|
*target = new_target;
|
|
}
|
|
});
|
|
}
|
|
|
|
// Recompute preds/succs.
|
|
body.recompute_edges();
|
|
|
|
log::trace!(
|
|
"empty_blocks: finished:\n{}\n",
|
|
body.display_verbose("| ", None)
|
|
);
|
|
}
|