use crate::{
AnalysisResults, Block, Context, Function, Instruction, IrError, Module, Pass, PassMutability,
ScopedPass, Value, ValueDatum,
};
use std::collections::{HashMap, HashSet};
pub const DCE_NAME: &str = "dce";
pub fn create_dce_pass() -> Pass {
Pass {
name: DCE_NAME,
descr: "Dead code elimination.",
runner: ScopedPass::FunctionPass(PassMutability::Transform(dce)),
deps: vec![],
}
}
pub const FUNC_DCE_NAME: &str = "func_dce";
pub fn create_func_dce_pass() -> Pass {
Pass {
name: FUNC_DCE_NAME,
descr: "Dead function elimination.",
deps: vec![],
runner: ScopedPass::ModulePass(PassMutability::Transform(func_dce)),
}
}
fn can_eliminate_instruction(context: &Context, val: Value) -> bool {
let inst = val.get_instruction(context).unwrap();
!inst.is_terminator() && !inst.may_have_side_effect()
}
pub fn dce(
context: &mut Context,
_: &AnalysisResults,
function: Function,
) -> Result<bool, IrError> {
let mut num_uses: HashMap<Value, (Block, u32)> = HashMap::new();
for (block, inst) in function.instruction_iter(context) {
let opds = inst.get_instruction(context).unwrap().get_operands();
for v in opds {
match context.values[v.0].value {
ValueDatum::Instruction(_) => {
num_uses
.entry(v)
.and_modify(|(_block, count)| *count += 1)
.or_insert((block, 1));
}
ValueDatum::Configurable(_) | ValueDatum::Constant(_) | ValueDatum::Argument(_) => {
}
}
}
}
let mut worklist = function
.instruction_iter(context)
.filter(|(_block, inst)| num_uses.get(inst).is_none())
.collect::<Vec<_>>();
let mut modified = false;
while !worklist.is_empty() {
let (in_block, dead) = worklist.pop().unwrap();
if !can_eliminate_instruction(context, dead) {
continue;
}
let opds = dead.get_instruction(context).unwrap().get_operands();
for v in opds {
match context.values[v.0].value {
ValueDatum::Instruction(_) => {
let (block, nu) = num_uses.get_mut(&v).unwrap();
*nu -= 1;
if *nu == 0 {
worklist.push((*block, v));
}
}
ValueDatum::Configurable(_) | ValueDatum::Constant(_) | ValueDatum::Argument(_) => {
}
}
}
in_block.remove_instruction(context, dead);
modified = true;
}
Ok(modified)
}
pub fn func_dce(
context: &mut Context,
_: &AnalysisResults,
module: Module,
) -> Result<bool, IrError> {
let entry_fns = module
.function_iter(context)
.filter(|func| func.is_entry(context))
.collect::<Vec<_>>();
fn grow_called_function_set(
context: &Context,
caller: Function,
called_set: &mut HashSet<Function>,
) {
if called_set.insert(caller) {
for func in caller
.instruction_iter(context)
.filter_map(|(_block, ins_value)| {
ins_value
.get_instruction(context)
.and_then(|ins| match ins {
Instruction::Call(f, _args) => Some(f),
_otherwise => None,
})
})
{
grow_called_function_set(context, *func, called_set);
}
}
}
let mut called_fns: HashSet<Function> = HashSet::new();
for entry_fn in entry_fns {
grow_called_function_set(context, entry_fn, &mut called_fns);
}
let dead_fns = module
.function_iter(context)
.filter(|f| !called_fns.contains(f))
.collect::<Vec<_>>();
let modified = !dead_fns.is_empty();
for dead_fn in dead_fns {
module.remove_function(context, &dead_fn);
}
Ok(modified)
}