use crate::analysis::ssa::{SsaFunction, TrivialPhiOptions};
impl SsaFunction {
pub fn repair_ssa(&mut self) {
if self.blocks.is_empty() {
return;
}
self.strip_nops();
self.eliminate_trivial_phis(&TrivialPhiOptions { reachable: None });
self.eliminate_dead_phis();
self.compact_variables();
self.reindex_variables();
}
}
#[cfg(test)]
mod tests {
use crate::analysis::{ssa::SsaOp, SsaFunctionBuilder};
#[test]
fn test_repair_strips_nops() {
let mut ssa = SsaFunctionBuilder::new(1, 0)
.build_with(|f| {
f.block(0, |b| {
let c1 = b.const_i32(42);
b.nop();
b.nop();
let c2 = b.const_i32(100);
let _ = b.add(c1, c2);
b.ret();
});
})
.unwrap();
let pre_count = ssa.blocks()[0].instructions().len();
ssa.repair_ssa();
let post_count = ssa.blocks()[0].instructions().len();
assert_eq!(pre_count - post_count, 2);
assert!(ssa.validate().is_ok());
}
#[test]
fn test_repair_eliminates_trivial_phis() {
let mut ssa = SsaFunctionBuilder::new(2, 1)
.build_with(|f| {
f.block(0, |b| {
let _ = b.const_i32(42);
b.jump(1);
});
f.block(1, |b| {
b.ret();
});
})
.unwrap();
ssa.repair_ssa();
assert!(ssa.validate().is_ok());
}
#[test]
fn test_repair_strips_nops_and_compacts() {
let mut ssa = SsaFunctionBuilder::new(1, 0)
.build_with(|f| {
f.block(0, |b| {
let c1 = b.const_i32(42);
let c2 = b.const_i32(100);
let _ = b.add(c1, c2);
b.ret();
});
})
.unwrap();
let pre_instr_count = ssa.blocks()[0].instructions().len();
if let Some(block) = ssa.block_mut(0) {
if let Some(instr) = block.instructions_mut().get_mut(1) {
instr.set_op(SsaOp::Nop);
}
}
ssa.repair_ssa();
let post_instr_count = ssa.blocks()[0].instructions().len();
assert_eq!(pre_instr_count - post_instr_count, 1);
}
#[test]
fn test_repair_preserves_valid_ssa() {
let mut ssa = SsaFunctionBuilder::new(3, 1)
.build_with(|f| {
f.block(0, |b| {
let c = b.const_true();
b.branch(c, 1, 2);
});
f.block(1, |b| {
let _ = b.const_i32(10);
b.jump(2);
});
f.block(2, |b| {
b.ret();
});
})
.unwrap();
ssa.repair_ssa();
assert!(ssa.validate().is_ok());
}
#[test]
fn test_repair_is_idempotent() {
let mut ssa = SsaFunctionBuilder::new(2, 0)
.build_with(|f| {
f.block(0, |b| {
let c1 = b.const_i32(1);
let c2 = b.const_i32(2);
let _ = b.add(c1, c2);
b.jump(1);
});
f.block(1, |b| {
b.ret();
});
})
.unwrap();
ssa.repair_ssa();
let vars_after_first = ssa.variable_count();
let blocks_after_first = ssa.block_count();
ssa.repair_ssa();
assert_eq!(ssa.variable_count(), vars_after_first);
assert_eq!(ssa.block_count(), blocks_after_first);
}
}