//! 保守轮单元断言:空函数退化、幂等性、双分支可达保留。 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 不触碰"); }