diff --git a/src/opt/deadcode.rs b/src/opt/deadcode.rs index 75b99f2..72ae5ca 100644 --- a/src/opt/deadcode.rs +++ b/src/opt/deadcode.rs @@ -1,5 +1,6 @@ use core::ops::Deref; use std::collections::{HashMap, HashSet}; +use std::ops::DerefMut; use crate::ir::*; use crate::opt::opt_utils::*; @@ -12,6 +13,190 @@ pub struct DeadcodeInner {} impl Optimize for DeadcodeInner { fn optimize(&mut self, code: &mut FunctionDefinition) -> bool { - todo!() + let mut used_allocations = HashSet::new(); + let mut used_registers = HashSet::new(); + + for (bid, block) in &code.blocks { + for (i, inst) in block.instructions.iter().enumerate() { + match inst.deref() { + Instruction::BinOp { lhs, rhs, .. } => { + mark_as_used(lhs, &mut used_allocations, &mut used_registers); + mark_as_used(rhs, &mut used_allocations, &mut used_registers); + } + Instruction::UnaryOp { operand, .. } => { + mark_as_used(operand, &mut used_allocations, &mut used_registers) + } + Instruction::Store { ptr, value } => { + mark_as_used(ptr, &mut used_allocations, &mut used_registers); + mark_as_used(value, &mut used_allocations, &mut used_registers); + // mark_as_used( + // &Operand::register(RegisterId::temp(*bid, i), inst.dtype()), + // &mut used_allocations, + // &mut used_registers, + // ); + } + Instruction::Load { ptr } => { + mark_as_used(ptr, &mut used_allocations, &mut used_registers) + } + Instruction::Call { callee, args, .. } => { + mark_as_used(callee, &mut used_allocations, &mut used_registers); + for arg in args { + mark_as_used(arg, &mut used_allocations, &mut used_registers); + } + // mark_as_used( + // &Operand::register(RegisterId::temp(*bid, i), inst.dtype()), + // &mut used_allocations, + // &mut used_registers, + // ); + } + Instruction::TypeCast { value, .. } => { + mark_as_used(value, &mut used_allocations, &mut used_registers) + } + Instruction::GetElementPtr { ptr, offset, .. } => { + mark_as_used(ptr, &mut used_allocations, &mut used_registers); + mark_as_used(offset, &mut used_allocations, &mut used_registers); + } + _ => (), + } + } + + match &block.exit { + BlockExit::Jump { arg } => { + mark_jump_args_as_used(arg, &mut used_allocations, &mut used_registers); + } + BlockExit::ConditionalJump { + condition, + arg_then, + arg_else, + } => { + mark_as_used(condition, &mut used_allocations, &mut used_registers); + mark_jump_args_as_used(arg_then, &mut used_allocations, &mut used_registers); + mark_jump_args_as_used(arg_else, &mut used_allocations, &mut used_registers); + } + BlockExit::Switch { + value, + default, + cases, + } => { + mark_as_used(value, &mut used_allocations, &mut used_registers); + mark_jump_args_as_used(default, &mut used_allocations, &mut used_registers); + for (_, arg) in cases { + mark_jump_args_as_used(arg, &mut used_allocations, &mut used_registers); + } + } + BlockExit::Return { value } => { + mark_as_used(value, &mut used_allocations, &mut used_registers); + } + _ => (), + } + } + + println!("{used_allocations:?} /// {used_registers:?}"); + + let mut replaces = HashMap::new(); + + let survived_allocations = code + .allocations + .iter() + .enumerate() + .filter(|(aid, _)| used_allocations.contains(aid)) + .collect::>(); + if survived_allocations.len() != code.allocations.len() { + for (new_aid, (old_aid, dtype)) in survived_allocations.iter().enumerate() { + let _unused = replaces.insert( + RegisterId::local(*old_aid), + Operand::register( + RegisterId::local(new_aid), + Dtype::pointer((*dtype).deref().clone()), + ), + ); + } + + code.allocations = survived_allocations + .into_iter() + .map(|(_, dtype)| dtype.clone()) + .collect(); + } + + for (bid, block) in &mut code.blocks { + let nop_instructions = block + .instructions + .iter() + .enumerate() + .filter_map(|(iid, inst)| match (*inst).deref() { + Instruction::Nop => Some(iid), + _ => None, + }) + .collect::>(); + + let survived_instructions = block + .instructions + .iter() + .enumerate() + .filter(|(iid, inst)| { + (used_registers.contains(&RegisterId::temp(*bid, *iid)) + && !nop_instructions.contains(iid)) + || !inst.has_no_side_effects() + }) + .collect::>(); + if survived_instructions.len() != block.instructions.len() { + for (new_iid, (old_iid, inst)) in survived_instructions.iter().enumerate() { + let _unused = replaces.insert( + RegisterId::temp(*bid, *old_iid), + Operand::register(RegisterId::temp(*bid, new_iid), inst.dtype()), + ); + } + + for iid_nop in &nop_instructions { + let _unused = replaces.insert( + RegisterId::temp(*bid, *iid_nop), + Operand::constant(Constant::unit()), + ); + } + + block.instructions = survived_instructions + .into_iter() + .map(|(_, inst)| inst.clone()) + .collect(); + } + } + + for (bid, block) in code.blocks.iter_mut() { + let mut changed = false; + for inst in block.instructions.iter_mut() { + changed = replace_instruction_operands(inst, &replaces) || changed; + } + changed = replace_exit_operands(&mut block.exit, &replaces) || changed; + } + + !replaces.is_empty() + } +} + +fn mark_as_used( + operand: &Operand, + used_allocations: &mut HashSet, + used_registers: &mut HashSet, +) { + match operand { + Operand::Register { rid, .. } => match rid { + RegisterId::Local { aid } => { + let _ = used_allocations.insert(*aid); + } + _ => { + let _ = used_registers.insert(*rid); + } + }, + _ => (), + } +} + +fn mark_jump_args_as_used( + arg: &JumpArg, + used_allocations: &mut HashSet, + used_registers: &mut HashSet, +) { + for arg in &arg.args { + mark_as_used(arg, used_allocations, used_registers); } }