Files

80 lines
3.4 KiB
Rust
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! 保守轮单元断言:空函数退化、幂等性、双分支可达保留。
use oxide_bytecode::opcode::OpCode;
use oxide_dce::dce;
use oxide_ir::inst::Inst;
use oxide_ir::operand::Operand;
use oxide_ir::IRFunction;
/// 空函数退化:insts 空时 dce 直接返回,状态不变。
#[test]
fn empty_function_unchanged() {
let mut f = IRFunction::new();
let _ = dce(&mut f);
assert!(f.insts.is_empty());
assert!(f.label_pos.is_empty());
assert_eq!(f.label_count, 0);
}
/// 幂等性:dce 两次结果一致(不动点后无二次改写)。
#[test]
fn dce_is_idempotent() {
let mut f = IRFunction::new();
f.insts.push(Inst::load_const(Operand::Reg(1), 0)); // 0: 死
f.insts
.push(Inst::new(OpCode::ADD, Operand::Reg(2), Operand::Reg(1), Operand::Reg(1))); // 1: 死
f.insts.push(Inst::load_const(Operand::Reg(0), 1)); // 2: 存活(CALL 保活 r0)
f.insts.push(Inst::call(Operand::Reg(0), Operand::Reg(0), Operand::Reg(0), 0)); // 3
f.insts
.push(Inst::new(OpCode::RETURN, Operand::Reg(5), Operand::None, Operand::None)); // 4
f.label_pos = vec![Some(0)];
let _ = dce(&mut f);
let once_insts = f.insts.clone();
let once_labels = f.label_pos.clone();
let _ = dce(&mut f);
assert_eq!(f.insts, once_insts, "二次 dce 后 insts 不变");
assert_eq!(f.label_pos, once_labels, "二次 dce 后 label_pos 不变");
}
/// if/else 双分支完整链路:Jump + Fallthrough 双出边全可达,指令全保留,label 重映射正确。
#[test]
fn if_else_both_branches_reachable_kept() {
let mut f = IRFunction::new();
f.insts.push(Inst::jmp_if_false(0, 0)); // 0: cond → L0(else)
f.insts.push(Inst::call(Operand::Reg(2), Operand::Reg(0), Operand::Reg(3), 1)); // 1: then
f.insts.push(Inst::jmp(1)); // 2: → L1(end)
f.insts.push(Inst::call(Operand::Reg(4), Operand::Reg(0), Operand::Reg(3), 1)); // 3: else
f.insts
.push(Inst::new(OpCode::RETURN, Operand::Reg(5), Operand::None, Operand::None)); // 4
f.label_pos = vec![Some(3), Some(4)];
f.label_count = 2;
let _ = dce(&mut f);
assert_eq!(f.insts.len(), 5, "双分支全保留");
assert_eq!(f.label_pos, vec![Some(3), Some(4)], "存活 label 重映射到新下标");
assert_eq!(f.label_count, 2, "label_count 保持原值,不参与重建");
}
/// 全存活函数:含 label 与 nested 的函数过 dce 后 insts 与 label_pos 逐位不变。
/// 零删除时 Pass C 重建为恒等变换,快速路径跳过重建后输出须与执行前逐位一致。
#[test]
fn all_live_function_unchanged() {
let mut f = IRFunction::new();
f.insts.push(Inst::load_const(Operand::Reg(1), 0)); // 0: label 0 目标,值被 CALL 使用
f.insts.push(Inst::call(Operand::Reg(2), Operand::Reg(1), Operand::Reg(3), 1)); // 1: 非纯,永不删
f.insts
.push(Inst::new(OpCode::RETURN, Operand::Reg(2), Operand::None, Operand::None)); // 2
f.label_pos = vec![Some(0)];
f.label_count = 1;
f.nested.push(IRFunction::new()); // nested 不递归不回收
let before_insts = f.insts.clone();
let before_labels = f.label_pos.clone();
let _ = dce(&mut f);
assert_eq!(f.insts, before_insts, "全存活时 insts 逐位不变");
assert_eq!(f.label_pos, before_labels, "全存活时 label_pos 逐位不变");
assert_eq!(f.label_count, 1, "label_count 保持原值");
assert_eq!(f.nested.len(), 1, "nested 不触碰");
}