use std::collections::{BTreeSet, HashMap};
use either::Either;
use rustc_hash::FxHashSet;
use crate::asm_lang::{ControlFlowOp, JumpType, Label};
use super::super::{abstract_instruction_set::AbstractInstructionSet, analyses::liveness_analysis};
impl AbstractInstructionSet {
pub(crate) fn dce(mut self) -> AbstractInstructionSet {
let liveness = liveness_analysis(&self.ops, false);
let ops = &self.ops;
let mut cur_live = BTreeSet::default();
let mut dead_indices = FxHashSet::default();
for (rev_ix, op) in ops.iter().rev().enumerate() {
if let Either::Right(ControlFlowOp::JumpToAddr(..)) = &op.opcode {
return self;
}
let ix = ops.len() - rev_ix - 1;
let op_use = op.use_registers();
let mut op_def = op.def_registers();
op_def.append(&mut op.def_const_registers());
if let Either::Right(ControlFlowOp::Jump { type_, .. }) = &op.opcode {
if !matches!(type_, JumpType::Call) {
cur_live.clone_from(liveness.get(ix).expect("Incorrect liveness info"));
for u in op_use {
cur_live.insert(u.clone());
}
continue;
}
}
let dead = op_def.iter().all(|def| !cur_live.contains(def))
&& match &op.opcode {
Either::Left(op) => !op.has_side_effect(),
Either::Right(_) => false,
};
for def in &op_def {
cur_live.remove(def);
}
if dead {
dead_indices.insert(ix);
} else {
for u in op_use {
cur_live.insert(u.clone());
}
}
}
let mut new_ops: Vec<_> = std::mem::take(&mut self.ops)
.into_iter()
.enumerate()
.filter_map(|(idx, op)| {
if !dead_indices.contains(&idx) {
Some(op)
} else {
None
}
})
.collect();
std::mem::swap(&mut self.ops, &mut new_ops);
self
}
pub(crate) fn simplify_cfg(mut self) -> AbstractInstructionSet {
let ops = &self.ops;
if ops.is_empty() {
return self;
}
let mut label_to_index: HashMap<Label, usize> = HashMap::default();
for (idx, op) in ops.iter().enumerate() {
match &op.opcode {
Either::Right(ControlFlowOp::Label(op_label)) => {
label_to_index.insert(*op_label, idx);
}
Either::Right(ControlFlowOp::JumpToAddr(..)) => {
return self;
}
_ => {}
}
}
let mut reachables = vec![false; ops.len()];
let mut worklist = vec![0];
while let Some(op_idx) = worklist.pop() {
if reachables[op_idx] {
continue;
}
reachables[op_idx] = true;
let op = &ops[op_idx];
for s in &op.successors(op_idx, ops, &label_to_index) {
if reachables[*s] {
continue;
}
worklist.push(*s);
}
}
let reachable_ops = self
.ops
.into_iter()
.enumerate()
.filter_map(|(idx, op)| if reachables[idx] { Some(op) } else { None })
.collect();
self.ops = reachable_ops;
self
}
}