diff --git a/crates/oxide_ir/src/contract.rs b/crates/oxide_ir/src/contract.rs new file mode 100644 index 00000000..81d46c4f --- /dev/null +++ b/crates/oxide_ir/src/contract.rs @@ -0,0 +1,585 @@ +//! Inst 寄存器契约:def/use 扫描 + 副作用判定(DCE 与 Phase 5 liveness 共用,D-06)。 +//! +//! `def_reg` / `use_regs` / `is_pure` 是 `Inst` 的类型契约方法(`impl Inst` 与 `inst.rs` +//! 中的构造 API 同属一类型),落在 oxide_ir 契约层供 DCE 消费、liveness 复用。 +//! 寄存器规则逐条对照 VM dispatch handler(RESEARCH Pattern 1/2 全表): +//! CALL 隐式写 reg 0、GET_PROP 结果写 a/b 槽、HALT 隐式读 reg 0、None 槽映射 Reg(0)。 + +use oxide_bytecode::opcode::OpCode; +use smallvec::SmallVec; + +use crate::inst::Inst; +use crate::operand::Operand; +use crate::IRFunction; + +impl Inst { + /// 本指令定义的寄存器(None 槽按 Reg(0) 映射;CALL 系含隐式 reg 0)。 + /// 无写入返回 None。 + pub fn def_reg(&self) -> Option { + match self.op { + // CALL 系:结果隐式写 reg 0,rd 是 callee use(Pitfall 1) + OpCode::CALL | OpCode::CALL_NATIVE => Some(0), + // GET_PROP 系:结果写 a/b 槽而非 rd(Pitfall 3) + OpCode::GET_PROP | OpCode::IC_GET_PROP => reg_of(&self.a), + OpCode::GET_PROP_DYNAMIC => reg_of(&self.b), + // 成员读写:val 槽原地更新(a 槽 / b 槽) + OpCode::MEMBER_INC | OpCode::MEMBER_DEC => reg_of(&self.a), + OpCode::DYN_MEMBER_INC | OpCode::DYN_MEMBER_DEC => reg_of(&self.b), + OpCode::COMPOUND_MEMBER_ADD + | OpCode::COMPOUND_MEMBER_SUB + | OpCode::COMPOUND_MEMBER_MUL + | OpCode::COMPOUND_MEMBER_DIV + | OpCode::COMPOUND_MEMBER_MOD + | OpCode::COMPOUND_MEMBER_EXP => reg_of(&self.a), + // 无 def:控制流 / 写共享状态 / 纯写对象 + OpCode::HALT + | OpCode::RETURN + | OpCode::THROW + | OpCode::JMP + | OpCode::JMP_IF_FALSE + | OpCode::JMP_IF_TRUE + | OpCode::JMP_IF_NULLISH + | OpCode::TRY_BEGIN + | OpCode::TRY_END + | OpCode::TRY_FINALLY_BEGIN + | OpCode::TRY_FINALLY_END + | OpCode::MAKE_CELL + | OpCode::CELL_SET + | OpCode::STORE_UPVALUE + | OpCode::SET_PROP + | OpCode::SET_PROP_DYNAMIC + | OpCode::IC_SET_PROP + | OpCode::SET_ELEM + | OpCode::GET_PRIVATE + | OpCode::SET_PRIVATE + | OpCode::INIT_PRIVATE + | OpCode::PRIVATE_BRAND_IN + | OpCode::DELETE_PROP_STATIC + | OpCode::DELETE_PROP_DYNAMIC + | OpCode::DEFINE_ACCESSOR + | OpCode::SET_HOME_OBJECT + | OpCode::FOR_IN_INIT + | OpCode::FOR_OF_INIT + | OpCode::FOR_IN_CLEANUP + | OpCode::FOR_OF_CLOSE => None, + // 其余指令 rd 即 def(算术/比较/位/逻辑/加载族/迭代器 NEXT 等) + _ => reg_of(&self.rd), + } + } + + /// 本指令读取的寄存器(None 槽按 Reg(0) 映射;HALT 特判 reg 0;TEMPLATE_STR 解析 ext)。 + pub fn use_regs(&self) -> SmallVec<[u32; 4]> { + let mut uses = SmallVec::new(); + match self.op { + // CALL 系:rd=callee, a=this, b..b+nargs 参数区间(Pitfall 1) + OpCode::CALL | OpCode::CALL_NATIVE => { + push_operand(&mut uses, &self.rd); + push_operand(&mut uses, &self.a); + let nargs = self.ext.first().copied().unwrap_or(0); + push_range(&mut uses, reg_of(&self.b), nargs); + } + // NEW_EXPRESSION:a=ctor, b..b+nargs + OpCode::NEW_EXPRESSION => { + push_operand(&mut uses, &self.a); + let nargs = self.ext.first().copied().unwrap_or(0); + push_range(&mut uses, reg_of(&self.b), nargs); + } + // SUPER_CALL:a..a+nargs + OpCode::SUPER_CALL => { + let nargs = self.ext.first().copied().unwrap_or(0); + push_range(&mut uses, reg_of(&self.a), nargs); + } + // GET_PROP 系:结果写 a/b 槽,rd=obj 是 use(Pitfall 3) + OpCode::GET_PROP => { + push_operand(&mut uses, &self.rd); + push_operand(&mut uses, &self.b); + } + OpCode::GET_PROP_DYNAMIC => { + push_operand(&mut uses, &self.rd); + push_operand(&mut uses, &self.a); + } + OpCode::IC_GET_PROP => { + push_operand(&mut uses, &self.a); + push_operand(&mut uses, &self.b); + } + // 成员更新:obj + key 是 use,val 槽是 def + OpCode::MEMBER_INC | OpCode::MEMBER_DEC => { + push_operand(&mut uses, &self.rd); + push_operand(&mut uses, &self.b); + } + OpCode::DYN_MEMBER_INC | OpCode::DYN_MEMBER_DEC => { + push_operand(&mut uses, &self.rd); + push_operand(&mut uses, &self.a); + } + OpCode::COMPOUND_MEMBER_ADD + | OpCode::COMPOUND_MEMBER_SUB + | OpCode::COMPOUND_MEMBER_MUL + | OpCode::COMPOUND_MEMBER_DIV + | OpCode::COMPOUND_MEMBER_MOD + | OpCode::COMPOUND_MEMBER_EXP => { + push_operand(&mut uses, &self.rd); + push_operand(&mut uses, &self.a); + push_operand(&mut uses, &self.b); + } + // 复合赋值:rd 与 a 都是 use(coerce 读双寄存器) + OpCode::COMPOUND_ADD + | OpCode::COMPOUND_SUB + | OpCode::COMPOUND_MUL + | OpCode::COMPOUND_DIV + | OpCode::COMPOUND_MOD + | OpCode::COMPOUND_EXP + | OpCode::COMPOUND_AND + | OpCode::COMPOUND_OR + | OpCode::COMPOUND_XOR + | OpCode::COMPOUND_SHL + | OpCode::COMPOUND_SHR + | OpCode::COMPOUND_USHR => { + push_operand(&mut uses, &self.rd); + push_operand(&mut uses, &self.a); + } + // 自增/自减:读 rd(写 rd 和 a) + OpCode::INC_PRE | OpCode::INC_POST | OpCode::DEC_PRE | OpCode::DEC_POST => { + push_operand(&mut uses, &self.rd); + } + // 二元运算:a, b + OpCode::ADD | OpCode::SUB | OpCode::MUL | OpCode::DIV | OpCode::MOD | OpCode::EQ + | OpCode::NEQ | OpCode::LT | OpCode::GT | OpCode::LTE | OpCode::GTE | OpCode::AND + | OpCode::OR | OpCode::NULLISH | OpCode::STRICT_EQ | OpCode::STRICT_NEQ + | OpCode::BIT_AND | OpCode::BIT_OR | OpCode::BIT_XOR | OpCode::SHL | OpCode::SHR + | OpCode::USHR | OpCode::INSTANCEOF | OpCode::IN | OpCode::CREATE_REGEXP => { + push_operand(&mut uses, &self.a); + push_operand(&mut uses, &self.b); + } + // 一元:a(emit 常 rd==a) + OpCode::NEG | OpCode::NOT | OpCode::UNARY_PLUS | OpCode::BIT_NOT | OpCode::TYPEOF => { + push_operand(&mut uses, &self.a); + } + // 无条件跳转 / try 标记:b 是 Label,无寄存器 + OpCode::JMP | OpCode::TRY_BEGIN | OpCode::TRY_FINALLY_BEGIN | OpCode::TRY_END + | OpCode::TRY_FINALLY_END | OpCode::FOR_IN_CLEANUP | OpCode::FOR_OF_CLOSE => {} + // 条件跳转:rd=cond + OpCode::JMP_IF_FALSE | OpCode::JMP_IF_TRUE | OpCode::JMP_IF_NULLISH => { + push_operand(&mut uses, &self.rd); + } + // HALT:隐式读 reg 0(顶层返回值,Pitfall 2) + OpCode::HALT => uses.push(0), + // RETURN/THROW:读 rd(None→0) + OpCode::RETURN | OpCode::THROW => push_operand(&mut uses, &self.rd), + // 加载族:a 槽是 Const/Imm 立即数,无寄存器 use + OpCode::LOAD_CONST | OpCode::CREATE_CLOSURE | OpCode::LOAD_UPVALUE | OpCode::VOID + | OpCode::NEW_OBJECT | OpCode::NEW_ARRAY | OpCode::NOP => {} + // 变量读写:LOAD_VAR/STORE_VAR/CELL_GET 读 a(None→0) + OpCode::LOAD_VAR | OpCode::STORE_VAR | OpCode::CELL_GET => { + push_operand(&mut uses, &self.a); + } + // MAKE_CELL:cell 初值读 rd;CELL_SET/STORE_UPVALUE 读 a + OpCode::MAKE_CELL => push_operand(&mut uses, &self.rd), + OpCode::CELL_SET | OpCode::STORE_UPVALUE => push_operand(&mut uses, &self.a), + // 写对象属性:rd/a/b 全 use + OpCode::SET_PROP | OpCode::SET_PROP_DYNAMIC | OpCode::IC_SET_PROP | OpCode::SET_ELEM + | OpCode::GET_PRIVATE | OpCode::SET_PRIVATE | OpCode::INIT_PRIVATE + | OpCode::PRIVATE_BRAND_IN | OpCode::DEFINE_ACCESSOR => { + push_operand(&mut uses, &self.rd); + push_operand(&mut uses, &self.a); + push_operand(&mut uses, &self.b); + } + // delete:读 obj(+dynamic 读 key) + OpCode::DELETE_PROP_STATIC => push_operand(&mut uses, &self.rd), + OpCode::DELETE_PROP_DYNAMIC => { + push_operand(&mut uses, &self.rd); + push_operand(&mut uses, &self.b); + } + // super 属性读:a=this, b=key + OpCode::SUPER_GET_PROP | OpCode::SUPER_STATIC_GET_PROP => { + push_operand(&mut uses, &self.a); + push_operand(&mut uses, &self.b); + } + // SET_HOME_OBJECT:rd=func, a=home + OpCode::SET_HOME_OBJECT => { + push_operand(&mut uses, &self.rd); + push_operand(&mut uses, &self.a); + } + // for-in/of 迭代器:INIT 读 a;NEXT/DONE 读迭代器栈隐式状态 + OpCode::FOR_IN_INIT | OpCode::FOR_OF_INIT => push_operand(&mut uses, &self.a), + OpCode::FOR_IN_NEXT | OpCode::FOR_IN_DONE | OpCode::FOR_OF_NEXT | OpCode::FOR_OF_DONE => {} + // REST_OBJECT:读 a(ext 是 excluded_idx 常量) + OpCode::REST_OBJECT => push_operand(&mut uses, &self.a), + // TEMPLATE_STR:解析 ext,跳过 ext[0],后续 seg>>31==1 则低 8 位是 expr_reg(A1) + OpCode::TEMPLATE_STR => { + for seg in self.ext.iter().skip(1) { + if seg >> 31 == 1 { + uses.push(seg & 0xFF); + } + } + } + // 占位 opcode(emit 不产):按无 use 保守处理,不影响合法产物 + OpCode::SWITCH_TABLE | OpCode::PROFILE_TYPE | OpCode::PROFILE_SHAPE + | OpCode::PROFILE_BRANCH | OpCode::PROFILE_CALL | OpCode::FORK | OpCode::JOIN => {} + } + uses + } + + /// 本指令是否无观察副作用(Pattern 1 纯表;LOAD_VAR 的 This+derived 特判需 &IRFunction)。 + /// + /// 纯 = 结果未用时可删。表外 opcode 一律有副作用(D-05 精确表,宁少删不错删)。 + pub fn is_pure(&self, f: &IRFunction) -> bool { + match self.op { + // 算术 / 比较 / 位 / 逻辑:coerce 对象路径有抛错风险,D-05 决策算纯(运行时等价兜底) + OpCode::ADD | OpCode::SUB | OpCode::MUL | OpCode::DIV | OpCode::MOD | OpCode::NEG + | OpCode::UNARY_PLUS | OpCode::EQ | OpCode::NEQ | OpCode::LT | OpCode::GT + | OpCode::LTE | OpCode::GTE | OpCode::STRICT_EQ | OpCode::STRICT_NEQ + | OpCode::BIT_AND | OpCode::BIT_OR | OpCode::BIT_XOR | OpCode::SHL | OpCode::SHR + | OpCode::USHR | OpCode::BIT_NOT | OpCode::AND | OpCode::OR | OpCode::NOT + | OpCode::NULLISH => true, + // 分配 / 常量 / 空操作(a 槽非寄存器) + OpCode::NOP | OpCode::LOAD_CONST | OpCode::VOID | OpCode::TYPEOF | OpCode::NEW_OBJECT + | OpCode::NEW_ARRAY => true, + // 闭包 / cell 读取:无抛错路径(D-07) + OpCode::CREATE_CLOSURE | OpCode::LOAD_UPVALUE | OpCode::CELL_GET => true, + // 模板字符串:纯拼接写 rd,无抛错(use 统计覆盖其 ext) + OpCode::TEMPLATE_STR => true, + // LOAD_VAR:仅 a==This 且 derived 构造函数读 this 可能抛 ReferenceError(misc.rs:52-63) + OpCode::LOAD_VAR => { + !(self.a == Operand::This && f.is_derived_constructor) + } + // STORE_VAR:写变量目标寄存器。脚本顶层/模块作用域的变量是**全局可观察状态** + // (Pitfall 6:`let x=0; class C{[x=1](){}}` 无后续本地读取时删除 `x=1` 会改变 + // 外部 `assert(x===1)` 的观察结果);函数内局部未用赋值的优化留给 Phase 5 精确活度。 + OpCode::STORE_VAR => false, + // 其余全部有副作用(getter/调用/控制流/迭代器/复合更新/写共享状态/占位防御) + _ => false, + } + } +} + +/// 操作数 → 物理寄存器号。None 槽映射 reg 0(A3,与 lower.rs operand_to_u8 一致); +/// Const/Imm/Label 不是寄存器槽,返回 None。 +fn reg_of(o: &Operand) -> Option { + match o { + Operand::Reg(r) => Some(*r), + Operand::This => Some(254), + Operand::NewTarget => Some(255), + Operand::None => Some(0), + Operand::Const(_) | Operand::Imm(_) | Operand::Label(_) => None, + } +} + +/// 把寄存器操作数推入 use 集合(非寄存器槽跳过)。 +fn push_operand(uses: &mut SmallVec<[u32; 4]>, o: &Operand) { + if let Some(r) = reg_of(o) { + uses.push(r); + } +} + +/// 推入连续参数区间 [first, first+nargs)。 +fn push_range(uses: &mut SmallVec<[u32; 4]>, first: Option, nargs: u32) { + if let Some(f) = first { + uses.extend((0..nargs).map(|i| f + i)); + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::inst::Inst; + use crate::operand::Operand; + use crate::IRFunction; + + // ── def_reg / use_regs 契约测试(Pattern 2 全表,反直觉槽位锁定)── + + #[test] + fn binary_op_def_rd_use_ab() { + let inst = Inst::new(OpCode::ADD, Operand::Reg(0), Operand::Reg(1), Operand::Reg(2)); + assert_eq!(inst.def_reg(), Some(0)); + assert_eq!(inst.use_regs().as_slice(), &[1, 2]); + } + + #[test] + fn unary_op_def_rd_use_a() { + let inst = Inst::new(OpCode::NEG, Operand::Reg(0), Operand::Reg(1), Operand::None); + assert_eq!(inst.def_reg(), Some(0)); + assert_eq!(inst.use_regs().as_slice(), &[1]); + } + + #[test] + fn get_prop_def_is_a_slot_not_rd() { + // Pitfall 3:GET_PROP rd=obj 是 use,结果写 a 槽 + let inst = Inst::new(OpCode::GET_PROP, Operand::Reg(0), Operand::Reg(1), Operand::Reg(2)); + assert_eq!(inst.def_reg(), Some(1)); + assert_eq!(inst.use_regs().as_slice(), &[0, 2]); + } + + #[test] + fn get_prop_dynamic_def_is_b_slot() { + // Pitfall 3:GET_PROP_DYNAMIC rd=obj 是 use,结果写 b 槽 + let inst = Inst::new(OpCode::GET_PROP_DYNAMIC, Operand::Reg(0), Operand::Reg(1), Operand::Reg(2)); + assert_eq!(inst.def_reg(), Some(2)); + assert_eq!(inst.use_regs().as_slice(), &[0, 1]); + } + + #[test] + fn ic_get_prop_a_slot_is_use_and_def() { + // Pitfall 3:IC_GET_PROP a 槽既是对象 use 又是结果 def + let inst = Inst::ic_get(Operand::Reg(1), Operand::Reg(2)); + assert_eq!(inst.def_reg(), Some(1)); + let uses = inst.use_regs(); + assert_eq!(uses.as_slice(), &[1, 2]); + } + + #[test] + fn call_def_is_implicit_reg0_with_arg_range() { + // Pitfall 1:CALL rd=callee 是 use,结果隐式写 reg 0;参数 b..b+nargs 连续 + let inst = Inst::call(Operand::Reg(0), Operand::Reg(1), Operand::Reg(2), 3); + assert_eq!(inst.def_reg(), Some(0)); + assert_eq!(inst.use_regs().as_slice(), &[0, 1, 2, 3, 4]); + } + + #[test] + fn call_native_def_is_implicit_reg0() { + let inst = Inst::call_native(Operand::Reg(0), Operand::Reg(1), Operand::Reg(2), 1); + assert_eq!(inst.def_reg(), Some(0)); + assert_eq!(inst.use_regs().as_slice(), &[0, 1, 2]); + } + + #[test] + fn new_expression_uses_ctor_and_arg_range() { + let inst = Inst::new_expression(Operand::Reg(3), Operand::Reg(0), Operand::Reg(1), 2); + assert_eq!(inst.def_reg(), Some(3)); + assert_eq!(inst.use_regs().as_slice(), &[0, 1, 2]); + } + + #[test] + fn super_call_uses_arg_range() { + let inst = Inst::super_call(Operand::Reg(3), Operand::Reg(1), 1); + assert_eq!(inst.def_reg(), Some(3)); + assert_eq!(inst.use_regs().as_slice(), &[1]); + } + + #[test] + fn halt_implicitly_uses_reg0() { + // Pitfall 2:HALT 返回 regs[0],顶层 LOAD_VAR(None, r) 链靠它保活 + let inst = Inst::new(OpCode::HALT, Operand::None, Operand::None, Operand::None); + assert_eq!(inst.def_reg(), None); + assert_eq!(inst.use_regs().as_slice(), &[0]); + } + + #[test] + fn void_defs_rd_no_use() { + // VM 的 VOID handler 不读 a 槽 + let inst = Inst::new(OpCode::VOID, Operand::Reg(1), Operand::Reg(2), Operand::None); + assert_eq!(inst.def_reg(), Some(1)); + assert!(inst.use_regs().is_empty()); + } + + #[test] + fn load_const_create_closure_load_upvalue_no_use() { + // a 槽是 Const/Imm 立即数,非寄存器 + let lc = Inst::load_const(Operand::Reg(1), 5); + assert_eq!(lc.def_reg(), Some(1)); + assert!(lc.use_regs().is_empty()); + + let cc = Inst::create_closure(Operand::Reg(1), 0); + assert_eq!(cc.def_reg(), Some(1)); + assert!(cc.use_regs().is_empty()); + + let lu = Inst::new(OpCode::LOAD_UPVALUE, Operand::Reg(1), Operand::Imm(0), Operand::None); + assert_eq!(lu.def_reg(), Some(1)); + assert!(lu.use_regs().is_empty()); + } + + #[test] + fn none_slot_maps_to_reg0() { + // A3:None 槽与 lower.rs operand_to_u8 一致,统一映射物理 reg 0 + let lv = Inst::new(OpCode::LOAD_VAR, Operand::Reg(1), Operand::None, Operand::None); + assert_eq!(lv.use_regs().as_slice(), &[0]); + + let cg = Inst::new(OpCode::CELL_GET, Operand::Reg(0), Operand::None, Operand::Imm(0)); + assert_eq!(cg.use_regs().as_slice(), &[0]); + } + + #[test] + fn template_str_parses_expr_regs_from_ext() { + // ext[0] 跳过;后续 seg>>31==1 则低 8 位是 expr_reg(A1,emit 8 位编码) + let with_expr = Inst::template_str(Operand::Reg(1), 2, 10, &[0x1234, 0x8000_0000 | 5]); + assert_eq!(with_expr.use_regs().as_slice(), &[5]); + + // 纯 quasi(无表达式):不误报寄存器 + let no_expr = Inst::template_str(Operand::Reg(1), 1, 10, &[0x1234]); + assert!(no_expr.use_regs().is_empty()); + } + + #[test] + fn jmp_if_false_uses_cond_reg() { + let inst = Inst::jmp_if_false(3, 9); + assert_eq!(inst.def_reg(), None); + assert_eq!(inst.use_regs().as_slice(), &[3]); + } + + #[test] + fn jmp_and_try_no_use() { + assert!(Inst::jmp(9).use_regs().is_empty()); + assert!(Inst::try_begin(9).use_regs().is_empty()); + assert!(Inst::try_finally_begin(9).use_regs().is_empty()); + } + + #[test] + fn make_cell_no_def_uses_rd() { + let inst = Inst::new(OpCode::MAKE_CELL, Operand::Reg(5), Operand::None, Operand::None); + assert_eq!(inst.def_reg(), None); + assert_eq!(inst.use_regs().as_slice(), &[5]); + } + + #[test] + fn cell_set_and_store_upvalue_use_a_no_def() { + let cs = Inst::new(OpCode::CELL_SET, Operand::None, Operand::Reg(1), Operand::Imm(0)); + assert_eq!(cs.def_reg(), None); + assert_eq!(cs.use_regs().as_slice(), &[1]); + + let su = Inst::new(OpCode::STORE_UPVALUE, Operand::None, Operand::Reg(1), Operand::Imm(0)); + assert_eq!(su.def_reg(), None); + assert_eq!(su.use_regs().as_slice(), &[1]); + } + + #[test] + fn return_throw_use_rd_with_none_mapping_reg0() { + let ret = Inst::new(OpCode::RETURN, Operand::None, Operand::None, Operand::None); + assert_eq!(ret.def_reg(), None); + assert_eq!(ret.use_regs().as_slice(), &[0]); + + let thr = Inst::new(OpCode::THROW, Operand::Reg(2), Operand::None, Operand::None); + assert_eq!(thr.def_reg(), None); + assert_eq!(thr.use_regs().as_slice(), &[2]); + } + + #[test] + fn rest_object_uses_a() { + let inst = Inst::rest_object(Operand::Reg(0), Operand::Reg(1), 7); + assert_eq!(inst.def_reg(), Some(0)); + assert_eq!(inst.use_regs().as_slice(), &[1]); + } + + #[test] + fn for_in_next_defs_rd_no_use() { + let inst = Inst::new(OpCode::FOR_IN_NEXT, Operand::Reg(0), Operand::None, Operand::None); + assert_eq!(inst.def_reg(), Some(0)); + assert!(inst.use_regs().is_empty()); + } + + #[test] + fn store_var_def_rd_use_a() { + let inst = Inst::new(OpCode::STORE_VAR, Operand::Reg(0), Operand::Reg(1), Operand::None); + assert_eq!(inst.def_reg(), Some(0)); + assert_eq!(inst.use_regs().as_slice(), &[1]); + } + + // ── is_pure 契约测试(Pattern 1 表,纯/非纯族代表 opcode)── + + #[test] + fn pure_ops_are_deletable() { + let f = IRFunction::new(); + // 算术 / 比较 / 位 / 逻辑 + assert!(Inst::new(OpCode::ADD, Operand::Reg(0), Operand::Reg(1), Operand::Reg(2)).is_pure(&f)); + assert!(Inst::new(OpCode::SUB, Operand::Reg(0), Operand::Reg(1), Operand::Reg(2)).is_pure(&f)); + assert!(Inst::new(OpCode::MUL, Operand::Reg(0), Operand::Reg(1), Operand::Reg(2)).is_pure(&f)); + assert!(Inst::new(OpCode::NEG, Operand::Reg(0), Operand::Reg(1), Operand::None).is_pure(&f)); + assert!(Inst::new(OpCode::EQ, Operand::Reg(0), Operand::Reg(1), Operand::Reg(2)).is_pure(&f)); + assert!(Inst::new(OpCode::STRICT_EQ, Operand::Reg(0), Operand::Reg(1), Operand::Reg(2)).is_pure(&f)); + assert!(Inst::new(OpCode::LT, Operand::Reg(0), Operand::Reg(1), Operand::Reg(2)).is_pure(&f)); + assert!(Inst::new(OpCode::BIT_AND, Operand::Reg(0), Operand::Reg(1), Operand::Reg(2)).is_pure(&f)); + assert!(Inst::new(OpCode::BIT_NOT, Operand::Reg(0), Operand::Reg(1), Operand::None).is_pure(&f)); + assert!(Inst::new(OpCode::AND, Operand::Reg(0), Operand::Reg(1), Operand::Reg(2)).is_pure(&f)); + assert!(Inst::new(OpCode::OR, Operand::Reg(0), Operand::Reg(1), Operand::Reg(2)).is_pure(&f)); + assert!(Inst::new(OpCode::NOT, Operand::Reg(0), Operand::Reg(1), Operand::None).is_pure(&f)); + assert!(Inst::new(OpCode::NULLISH, Operand::Reg(0), Operand::Reg(1), Operand::Reg(2)).is_pure(&f)); + // 分配/常量/空操作 + assert!(Inst::new(OpCode::NOP, Operand::None, Operand::None, Operand::None).is_pure(&f)); + assert!(Inst::load_const(Operand::Reg(1), 0).is_pure(&f)); + assert!(Inst::new(OpCode::VOID, Operand::Reg(1), Operand::None, Operand::None).is_pure(&f)); + assert!(Inst::new(OpCode::TYPEOF, Operand::Reg(1), Operand::Reg(2), Operand::None).is_pure(&f)); + assert!(Inst::new(OpCode::NEW_OBJECT, Operand::Reg(1), Operand::None, Operand::None).is_pure(&f)); + assert!(Inst::new(OpCode::NEW_ARRAY, Operand::Reg(1), Operand::None, Operand::None).is_pure(&f)); + // 闭包/单元读取 + assert!(Inst::create_closure(Operand::Reg(1), 0).is_pure(&f)); + assert!(Inst::new(OpCode::LOAD_UPVALUE, Operand::Reg(1), Operand::Imm(0), Operand::None).is_pure(&f)); + assert!(Inst::new(OpCode::CELL_GET, Operand::Reg(0), Operand::Reg(1), Operand::Imm(0)).is_pure(&f)); + // 模板字符串 + assert!(Inst::template_str(Operand::Reg(1), 1, 10, &[0x1234]).is_pure(&f)); + // LOAD_VAR 普通(a=Reg)纯 + assert!(Inst::new(OpCode::LOAD_VAR, Operand::Reg(1), Operand::Reg(2), Operand::None).is_pure(&f)); + // STORE_VAR 一律有副作用(Pitfall 6:顶层变量赋值全局可观察,不删) + assert!(!Inst::new(OpCode::STORE_VAR, Operand::Reg(0), Operand::Reg(1), Operand::None).is_pure(&f)); + assert!(!Inst::new(OpCode::STORE_VAR, Operand::Reg(0), Operand::Reg(1), Operand::Imm(0)).is_pure(&f)); + assert!(!Inst::new(OpCode::STORE_VAR, Operand::Reg(0), Operand::Reg(1), Operand::Imm(1)).is_pure(&f)); + } + + #[test] + fn impure_ops_are_not_deletable() { + let f = IRFunction::new(); + // getter / 写对象属性 + assert!(!Inst::ic_get(Operand::Reg(1), Operand::Reg(2)).is_pure(&f)); + assert!(!Inst::new(OpCode::GET_PROP, Operand::Reg(0), Operand::Reg(1), Operand::Reg(2)).is_pure(&f)); + assert!(!Inst::new(OpCode::GET_PROP_DYNAMIC, Operand::Reg(0), Operand::Reg(1), Operand::Reg(2)).is_pure(&f)); + assert!(!Inst::ic_set(Operand::Reg(0), Operand::Reg(1), Operand::Reg(2)).is_pure(&f)); + assert!(!Inst::new(OpCode::SET_PROP, Operand::Reg(0), Operand::Reg(1), Operand::Reg(2)).is_pure(&f)); + assert!(!Inst::new(OpCode::SUPER_GET_PROP, Operand::Reg(0), Operand::Reg(1), Operand::Reg(2)).is_pure(&f)); + assert!(!Inst::new(OpCode::DEFINE_ACCESSOR, Operand::Reg(0), Operand::Reg(1), Operand::Reg(2)).is_pure(&f)); + assert!(!Inst::new(OpCode::DELETE_PROP_STATIC, Operand::Reg(0), Operand::Reg(0), Operand::None).is_pure(&f)); + assert!(!Inst::new(OpCode::DELETE_PROP_DYNAMIC, Operand::Reg(0), Operand::None, Operand::Reg(2)).is_pure(&f)); + // 调用 / 异常 / 控制流 + assert!(!Inst::call(Operand::Reg(0), Operand::Reg(1), Operand::Reg(2), 0).is_pure(&f)); + assert!(!Inst::call_native(Operand::Reg(0), Operand::Reg(1), Operand::Reg(2), 0).is_pure(&f)); + assert!(!Inst::new_expression(Operand::Reg(3), Operand::Reg(0), Operand::Reg(1), 0).is_pure(&f)); + assert!(!Inst::super_call(Operand::Reg(3), Operand::Reg(1), 0).is_pure(&f)); + assert!(!Inst::new(OpCode::CREATE_REGEXP, Operand::Reg(0), Operand::Reg(1), Operand::Reg(2)).is_pure(&f)); + assert!(!Inst::new(OpCode::RETURN, Operand::None, Operand::None, Operand::None).is_pure(&f)); + assert!(!Inst::new(OpCode::THROW, Operand::Reg(2), Operand::None, Operand::None).is_pure(&f)); + assert!(!Inst::new(OpCode::HALT, Operand::None, Operand::None, Operand::None).is_pure(&f)); + assert!(!Inst::jmp(9).is_pure(&f)); + assert!(!Inst::jmp_if_false(3, 9).is_pure(&f)); + assert!(!Inst::try_begin(9).is_pure(&f)); + assert!(!Inst::try_finally_begin(9).is_pure(&f)); + // 迭代器 / 复合更新 / 写共享状态 + assert!(!Inst::new(OpCode::FOR_IN_NEXT, Operand::Reg(0), Operand::None, Operand::None).is_pure(&f)); + assert!(!Inst::new(OpCode::FOR_OF_NEXT, Operand::Reg(0), Operand::None, Operand::None).is_pure(&f)); + assert!(!Inst::new(OpCode::INC_PRE, Operand::Reg(0), Operand::Reg(0), Operand::None).is_pure(&f)); + assert!(!Inst::new(OpCode::COMPOUND_ADD, Operand::Reg(0), Operand::Reg(1), Operand::None).is_pure(&f)); + assert!(!Inst::new(OpCode::MEMBER_INC, Operand::Reg(0), Operand::Reg(1), Operand::Reg(2)).is_pure(&f)); + assert!(!Inst::new(OpCode::COMPOUND_MEMBER_ADD, Operand::Reg(0), Operand::Reg(1), Operand::Reg(2)).is_pure(&f)); + assert!(!Inst::rest_object(Operand::Reg(0), Operand::Reg(1), 7).is_pure(&f)); + assert!(!Inst::new(OpCode::INSTANCEOF, Operand::Reg(0), Operand::Reg(1), Operand::Reg(2)).is_pure(&f)); + assert!(!Inst::new(OpCode::IN, Operand::Reg(0), Operand::Reg(1), Operand::Reg(2)).is_pure(&f)); + assert!(!Inst::new(OpCode::GET_PRIVATE, Operand::Reg(0), Operand::Reg(1), Operand::Reg(2)).is_pure(&f)); + assert!(!Inst::new(OpCode::SET_HOME_OBJECT, Operand::Reg(0), Operand::Reg(1), Operand::None).is_pure(&f)); + assert!(!Inst::new(OpCode::STORE_UPVALUE, Operand::None, Operand::Reg(1), Operand::Imm(0)).is_pure(&f)); + assert!(!Inst::new(OpCode::CELL_SET, Operand::None, Operand::Reg(1), Operand::Imm(0)).is_pure(&f)); + assert!(!Inst::new(OpCode::MAKE_CELL, Operand::Reg(5), Operand::None, Operand::None).is_pure(&f)); + // 占位 opcode 防御性有副作用 + assert!(!Inst::new(OpCode::SWITCH_TABLE, Operand::None, Operand::None, Operand::None).is_pure(&f)); + } + + #[test] + fn store_var_all_branches_are_impure() { + // Pitfall 6:STORE_VAR 写目标变量寄存器,顶层/模块作用域变量全局可观察,一律不删 + // (const guard 抛错、普通 var/let 赋值都可能改变外部观察状态) + let f = IRFunction::new(); + assert!(!Inst::new(OpCode::STORE_VAR, Operand::Reg(0), Operand::Reg(1), Operand::None).is_pure(&f)); + assert!(!Inst::new(OpCode::STORE_VAR, Operand::Reg(0), Operand::Reg(1), Operand::Imm(0)).is_pure(&f)); + assert!(!Inst::new(OpCode::STORE_VAR, Operand::Reg(0), Operand::Reg(1), Operand::Imm(1)).is_pure(&f)); + } + + #[test] + fn load_var_this_derived_constructor_special_case() { + // a==This + is_derived_constructor 时读 this 可能抛 ReferenceError,不可删 + let mut derived = IRFunction::new(); + derived.is_derived_constructor = true; + let non_derived = IRFunction::new(); + + let this_load = Inst::new(OpCode::LOAD_VAR, Operand::Reg(1), Operand::This, Operand::None); + assert!(!this_load.is_pure(&derived)); + assert!(this_load.is_pure(&non_derived)); + + // a=Reg 普通 LOAD_VAR 在 derived 函数中也纯 + let reg_load = Inst::new(OpCode::LOAD_VAR, Operand::Reg(1), Operand::Reg(2), Operand::None); + assert!(reg_load.is_pure(&derived)); + } +} diff --git a/crates/oxide_ir/src/inst.rs b/crates/oxide_ir/src/inst.rs index acf00a71..97cbb6a7 100644 --- a/crates/oxide_ir/src/inst.rs +++ b/crates/oxide_ir/src/inst.rs @@ -2,12 +2,12 @@ //! //! `ext` 内部是裸 u32(lowering 直拼字节码扩展字)。扩展字的**数量与语义值编码** //! 全部由 `inst_*` 构造 API 保证——emit 代码不直接触碰 `ext` 字段。 +//! 寄存器 def/use 契约与副作用判定在 `contract` 模块(`impl Inst`,DCE/liveness 共用)。 use oxide_bytecode::opcode::OpCode; use smallvec::SmallVec; use crate::operand::{LabelId, Operand}; -use crate::IRFunction; /// IR 指令:opcode + 三个操作数槽(rd/a/b)+ 扩展字 `ext`。 /// `ext` 内部是裸 u32,其数量与语义值编码由下方 `inst_*` 构造 API 保证。 @@ -206,274 +206,6 @@ impl Inst { pub fn try_finally_begin(label: LabelId) -> Self { Self::new(OpCode::TRY_FINALLY_BEGIN, Operand::None, Operand::None, Operand::Label(label)) } - - // ── 寄存器契约(DCE 与 liveness 共用,D-06)── - - /// 本指令定义的寄存器(None 槽按 Reg(0) 映射;CALL 系含隐式 reg 0)。 - /// 无写入返回 None。 - pub fn def_reg(&self) -> Option { - match self.op { - // CALL 系:结果隐式写 reg 0,rd 是 callee use(Pitfall 1) - OpCode::CALL | OpCode::CALL_NATIVE => Some(0), - // GET_PROP 系:结果写 a/b 槽而非 rd(Pitfall 3) - OpCode::GET_PROP | OpCode::IC_GET_PROP => reg_of(&self.a), - OpCode::GET_PROP_DYNAMIC => reg_of(&self.b), - // 成员读写:val 槽原地更新(a 槽 / b 槽) - OpCode::MEMBER_INC | OpCode::MEMBER_DEC => reg_of(&self.a), - OpCode::DYN_MEMBER_INC | OpCode::DYN_MEMBER_DEC => reg_of(&self.b), - OpCode::COMPOUND_MEMBER_ADD - | OpCode::COMPOUND_MEMBER_SUB - | OpCode::COMPOUND_MEMBER_MUL - | OpCode::COMPOUND_MEMBER_DIV - | OpCode::COMPOUND_MEMBER_MOD - | OpCode::COMPOUND_MEMBER_EXP => reg_of(&self.a), - // 无 def:控制流 / 写共享状态 / 纯写对象 - OpCode::HALT - | OpCode::RETURN - | OpCode::THROW - | OpCode::JMP - | OpCode::JMP_IF_FALSE - | OpCode::JMP_IF_TRUE - | OpCode::JMP_IF_NULLISH - | OpCode::TRY_BEGIN - | OpCode::TRY_END - | OpCode::TRY_FINALLY_BEGIN - | OpCode::TRY_FINALLY_END - | OpCode::MAKE_CELL - | OpCode::CELL_SET - | OpCode::STORE_UPVALUE - | OpCode::SET_PROP - | OpCode::SET_PROP_DYNAMIC - | OpCode::IC_SET_PROP - | OpCode::SET_ELEM - | OpCode::GET_PRIVATE - | OpCode::SET_PRIVATE - | OpCode::INIT_PRIVATE - | OpCode::PRIVATE_BRAND_IN - | OpCode::DELETE_PROP_STATIC - | OpCode::DELETE_PROP_DYNAMIC - | OpCode::DEFINE_ACCESSOR - | OpCode::SET_HOME_OBJECT - | OpCode::FOR_IN_INIT - | OpCode::FOR_OF_INIT - | OpCode::FOR_IN_CLEANUP - | OpCode::FOR_OF_CLOSE => None, - // 其余指令 rd 即 def(算术/比较/位/逻辑/加载族/迭代器 NEXT 等) - _ => reg_of(&self.rd), - } - } - - /// 本指令读取的寄存器(None 槽按 Reg(0) 映射;HALT 特判 reg 0;TEMPLATE_STR 解析 ext)。 - pub fn use_regs(&self) -> SmallVec<[u32; 4]> { - let mut uses = SmallVec::new(); - match self.op { - // CALL 系:rd=callee, a=this, b..b+nargs 参数区间(Pitfall 1) - OpCode::CALL | OpCode::CALL_NATIVE => { - push_operand(&mut uses, &self.rd); - push_operand(&mut uses, &self.a); - let nargs = self.ext.first().copied().unwrap_or(0); - push_range(&mut uses, reg_of(&self.b), nargs); - } - // NEW_EXPRESSION:a=ctor, b..b+nargs - OpCode::NEW_EXPRESSION => { - push_operand(&mut uses, &self.a); - let nargs = self.ext.first().copied().unwrap_or(0); - push_range(&mut uses, reg_of(&self.b), nargs); - } - // SUPER_CALL:a..a+nargs - OpCode::SUPER_CALL => { - let nargs = self.ext.first().copied().unwrap_or(0); - push_range(&mut uses, reg_of(&self.a), nargs); - } - // GET_PROP 系:结果写 a/b 槽,rd=obj 是 use(Pitfall 3) - OpCode::GET_PROP => { - push_operand(&mut uses, &self.rd); - push_operand(&mut uses, &self.b); - } - OpCode::GET_PROP_DYNAMIC => { - push_operand(&mut uses, &self.rd); - push_operand(&mut uses, &self.a); - } - OpCode::IC_GET_PROP => { - push_operand(&mut uses, &self.a); - push_operand(&mut uses, &self.b); - } - // 成员更新:obj + key 是 use,val 槽是 def - OpCode::MEMBER_INC | OpCode::MEMBER_DEC => { - push_operand(&mut uses, &self.rd); - push_operand(&mut uses, &self.b); - } - OpCode::DYN_MEMBER_INC | OpCode::DYN_MEMBER_DEC => { - push_operand(&mut uses, &self.rd); - push_operand(&mut uses, &self.a); - } - OpCode::COMPOUND_MEMBER_ADD - | OpCode::COMPOUND_MEMBER_SUB - | OpCode::COMPOUND_MEMBER_MUL - | OpCode::COMPOUND_MEMBER_DIV - | OpCode::COMPOUND_MEMBER_MOD - | OpCode::COMPOUND_MEMBER_EXP => { - push_operand(&mut uses, &self.rd); - push_operand(&mut uses, &self.a); - push_operand(&mut uses, &self.b); - } - // 复合赋值:rd 与 a 都是 use(coerce 读双寄存器) - OpCode::COMPOUND_ADD - | OpCode::COMPOUND_SUB - | OpCode::COMPOUND_MUL - | OpCode::COMPOUND_DIV - | OpCode::COMPOUND_MOD - | OpCode::COMPOUND_EXP - | OpCode::COMPOUND_AND - | OpCode::COMPOUND_OR - | OpCode::COMPOUND_XOR - | OpCode::COMPOUND_SHL - | OpCode::COMPOUND_SHR - | OpCode::COMPOUND_USHR => { - push_operand(&mut uses, &self.rd); - push_operand(&mut uses, &self.a); - } - // 自增/自减:读 rd(写 rd 和 a) - OpCode::INC_PRE | OpCode::INC_POST | OpCode::DEC_PRE | OpCode::DEC_POST => { - push_operand(&mut uses, &self.rd); - } - // 二元运算:a, b - OpCode::ADD | OpCode::SUB | OpCode::MUL | OpCode::DIV | OpCode::MOD | OpCode::EQ - | OpCode::NEQ | OpCode::LT | OpCode::GT | OpCode::LTE | OpCode::GTE | OpCode::AND - | OpCode::OR | OpCode::NULLISH | OpCode::STRICT_EQ | OpCode::STRICT_NEQ - | OpCode::BIT_AND | OpCode::BIT_OR | OpCode::BIT_XOR | OpCode::SHL | OpCode::SHR - | OpCode::USHR | OpCode::INSTANCEOF | OpCode::IN | OpCode::CREATE_REGEXP => { - push_operand(&mut uses, &self.a); - push_operand(&mut uses, &self.b); - } - // 一元:a(emit 常 rd==a) - OpCode::NEG | OpCode::NOT | OpCode::UNARY_PLUS | OpCode::BIT_NOT | OpCode::TYPEOF => { - push_operand(&mut uses, &self.a); - } - // 无条件跳转 / try 标记:b 是 Label,无寄存器 - OpCode::JMP | OpCode::TRY_BEGIN | OpCode::TRY_FINALLY_BEGIN | OpCode::TRY_END - | OpCode::TRY_FINALLY_END | OpCode::FOR_IN_CLEANUP | OpCode::FOR_OF_CLOSE => {} - // 条件跳转:rd=cond - OpCode::JMP_IF_FALSE | OpCode::JMP_IF_TRUE | OpCode::JMP_IF_NULLISH => { - push_operand(&mut uses, &self.rd); - } - // HALT:隐式读 reg 0(顶层返回值,Pitfall 2) - OpCode::HALT => uses.push(0), - // RETURN/THROW:读 rd(None→0) - OpCode::RETURN | OpCode::THROW => push_operand(&mut uses, &self.rd), - // 加载族:a 槽是 Const/Imm 立即数,无寄存器 use - OpCode::LOAD_CONST | OpCode::CREATE_CLOSURE | OpCode::LOAD_UPVALUE | OpCode::VOID - | OpCode::NEW_OBJECT | OpCode::NEW_ARRAY | OpCode::NOP => {} - // 变量读写:LOAD_VAR/STORE_VAR/CELL_GET 读 a(None→0) - OpCode::LOAD_VAR | OpCode::STORE_VAR | OpCode::CELL_GET => { - push_operand(&mut uses, &self.a); - } - // MAKE_CELL:cell 初值读 rd;CELL_SET/STORE_UPVALUE 读 a - OpCode::MAKE_CELL => push_operand(&mut uses, &self.rd), - OpCode::CELL_SET | OpCode::STORE_UPVALUE => push_operand(&mut uses, &self.a), - // 写对象属性:rd/a/b 全 use - OpCode::SET_PROP | OpCode::SET_PROP_DYNAMIC | OpCode::IC_SET_PROP | OpCode::SET_ELEM - | OpCode::GET_PRIVATE | OpCode::SET_PRIVATE | OpCode::INIT_PRIVATE - | OpCode::PRIVATE_BRAND_IN | OpCode::DEFINE_ACCESSOR => { - push_operand(&mut uses, &self.rd); - push_operand(&mut uses, &self.a); - push_operand(&mut uses, &self.b); - } - // delete:读 obj(+dynamic 读 key) - OpCode::DELETE_PROP_STATIC => push_operand(&mut uses, &self.rd), - OpCode::DELETE_PROP_DYNAMIC => { - push_operand(&mut uses, &self.rd); - push_operand(&mut uses, &self.b); - } - // super 属性读:a=this, b=key - OpCode::SUPER_GET_PROP | OpCode::SUPER_STATIC_GET_PROP => { - push_operand(&mut uses, &self.a); - push_operand(&mut uses, &self.b); - } - // SET_HOME_OBJECT:rd=func, a=home - OpCode::SET_HOME_OBJECT => { - push_operand(&mut uses, &self.rd); - push_operand(&mut uses, &self.a); - } - // for-in/of 迭代器:INIT 读 a;NEXT/DONE 读迭代器栈隐式状态 - OpCode::FOR_IN_INIT | OpCode::FOR_OF_INIT => push_operand(&mut uses, &self.a), - OpCode::FOR_IN_NEXT | OpCode::FOR_IN_DONE | OpCode::FOR_OF_NEXT | OpCode::FOR_OF_DONE => {} - // REST_OBJECT:读 a(ext 是 excluded_idx 常量) - OpCode::REST_OBJECT => push_operand(&mut uses, &self.a), - // TEMPLATE_STR:解析 ext,跳过 ext[0],后续 seg>>31==1 则低 8 位是 expr_reg(A1) - OpCode::TEMPLATE_STR => { - for seg in self.ext.iter().skip(1) { - if seg >> 31 == 1 { - uses.push(seg & 0xFF); - } - } - } - // 占位 opcode(emit 不产):按无 use 保守处理,不影响合法产物 - OpCode::SWITCH_TABLE | OpCode::PROFILE_TYPE | OpCode::PROFILE_SHAPE - | OpCode::PROFILE_BRANCH | OpCode::PROFILE_CALL | OpCode::FORK | OpCode::JOIN => {} - } - uses - } -} - -/// 操作数 → 物理寄存器号。None 槽映射 reg 0(A3,与 lower.rs operand_to_u8 一致); -/// Const/Imm/Label 不是寄存器槽,返回 None。 -fn reg_of(o: &Operand) -> Option { - match o { - Operand::Reg(r) => Some(*r), - Operand::This => Some(254), - Operand::NewTarget => Some(255), - Operand::None => Some(0), - Operand::Const(_) | Operand::Imm(_) | Operand::Label(_) => None, - } -} - -/// 把寄存器操作数推入 use 集合(非寄存器槽跳过)。 -fn push_operand(uses: &mut SmallVec<[u32; 4]>, o: &Operand) { - if let Some(r) = reg_of(o) { - uses.push(r); - } -} - -/// 推入连续参数区间 [first, first+nargs)。 -fn push_range(uses: &mut SmallVec<[u32; 4]>, first: Option, nargs: u32) { - if let Some(f) = first { - uses.extend((0..nargs).map(|i| f + i)); - } -} - -impl Inst { - /// 本指令是否无观察副作用(Pattern 1 纯表;LOAD_VAR 的 This+derived 特判需 &IRFunction)。 - /// - /// 纯 = 结果未用时可删。表外 opcode 一律有副作用(D-05 精确表,宁少删不错删)。 - pub fn is_pure(&self, f: &IRFunction) -> bool { - match self.op { - // 算术 / 比较 / 位 / 逻辑:coerce 对象路径有抛错风险,D-05 决策算纯(运行时等价兜底) - OpCode::ADD | OpCode::SUB | OpCode::MUL | OpCode::DIV | OpCode::MOD | OpCode::NEG - | OpCode::UNARY_PLUS | OpCode::EQ | OpCode::NEQ | OpCode::LT | OpCode::GT - | OpCode::LTE | OpCode::GTE | OpCode::STRICT_EQ | OpCode::STRICT_NEQ - | OpCode::BIT_AND | OpCode::BIT_OR | OpCode::BIT_XOR | OpCode::SHL | OpCode::SHR - | OpCode::USHR | OpCode::BIT_NOT | OpCode::AND | OpCode::OR | OpCode::NOT - | OpCode::NULLISH => true, - // 分配 / 常量 / 空操作(a 槽非寄存器) - OpCode::NOP | OpCode::LOAD_CONST | OpCode::VOID | OpCode::TYPEOF | OpCode::NEW_OBJECT - | OpCode::NEW_ARRAY => true, - // 闭包 / cell 读取:无抛错路径(D-07) - OpCode::CREATE_CLOSURE | OpCode::LOAD_UPVALUE | OpCode::CELL_GET => true, - // 模板字符串:纯拼接写 rd,无抛错(use 统计覆盖其 ext) - OpCode::TEMPLATE_STR => true, - // LOAD_VAR:仅 a==This 且 derived 构造函数读 this 可能抛 ReferenceError(misc.rs:52-63) - OpCode::LOAD_VAR => { - !(self.a == Operand::This && f.is_derived_constructor) - } - // STORE_VAR:写变量目标寄存器。脚本顶层/模块作用域的变量是**全局可观察状态** - // (Pitfall 6:`let x=0; class C{[x=1](){}}` 无后续本地读取时删除 `x=1` 会改变 - // 外部 `assert(x===1)` 的观察结果);函数内局部未用赋值的优化留给 Phase 5 精确活度。 - OpCode::STORE_VAR => false, - // 其余全部有副作用(getter/调用/控制流/迭代器/复合更新/写共享状态/占位防御) - _ => false, - } - } } #[cfg(test)] @@ -588,302 +320,4 @@ mod tests { assert_eq!(inst.ext[1], 0x1234); assert_eq!(inst.ext[2], 0x8000_0000 | 5); } - - // ── def_reg / use_regs 契约测试(Pattern 2 表,反直觉槽位手工 IR 锁定)── - - #[test] - fn binary_op_def_rd_use_ab() { - let inst = Inst::new(OpCode::ADD, Operand::Reg(0), Operand::Reg(1), Operand::Reg(2)); - assert_eq!(inst.def_reg(), Some(0)); - assert_eq!(inst.use_regs().as_slice(), &[1, 2]); - } - - #[test] - fn unary_op_def_rd_use_a() { - let inst = Inst::new(OpCode::NEG, Operand::Reg(0), Operand::Reg(1), Operand::None); - assert_eq!(inst.def_reg(), Some(0)); - assert_eq!(inst.use_regs().as_slice(), &[1]); - } - - #[test] - fn get_prop_def_is_a_slot_not_rd() { - // Pitfall 3:GET_PROP rd=obj 是 use,结果写 a 槽 - let inst = Inst::new(OpCode::GET_PROP, Operand::Reg(0), Operand::Reg(1), Operand::Reg(2)); - assert_eq!(inst.def_reg(), Some(1)); - assert_eq!(inst.use_regs().as_slice(), &[0, 2]); - } - - #[test] - fn get_prop_dynamic_def_is_b_slot() { - // Pitfall 3:GET_PROP_DYNAMIC rd=obj 是 use,结果写 b 槽 - let inst = Inst::new(OpCode::GET_PROP_DYNAMIC, Operand::Reg(0), Operand::Reg(1), Operand::Reg(2)); - assert_eq!(inst.def_reg(), Some(2)); - assert_eq!(inst.use_regs().as_slice(), &[0, 1]); - } - - #[test] - fn ic_get_prop_a_slot_is_use_and_def() { - // Pitfall 3:IC_GET_PROP a 槽既是对象 use 又是结果 def - let inst = Inst::ic_get(Operand::Reg(1), Operand::Reg(2)); - assert_eq!(inst.def_reg(), Some(1)); - let uses = inst.use_regs(); - assert_eq!(uses.as_slice(), &[1, 2]); - } - - #[test] - fn call_def_is_implicit_reg0_with_arg_range() { - // Pitfall 1:CALL rd=callee 是 use,结果隐式写 reg 0;参数 b..b+nargs 连续 - let inst = Inst::call(Operand::Reg(0), Operand::Reg(1), Operand::Reg(2), 3); - assert_eq!(inst.def_reg(), Some(0)); - assert_eq!(inst.use_regs().as_slice(), &[0, 1, 2, 3, 4]); - } - - #[test] - fn call_native_def_is_implicit_reg0() { - let inst = Inst::call_native(Operand::Reg(0), Operand::Reg(1), Operand::Reg(2), 1); - assert_eq!(inst.def_reg(), Some(0)); - assert_eq!(inst.use_regs().as_slice(), &[0, 1, 2]); - } - - #[test] - fn new_expression_uses_ctor_and_arg_range() { - let inst = Inst::new_expression(Operand::Reg(3), Operand::Reg(0), Operand::Reg(1), 2); - assert_eq!(inst.def_reg(), Some(3)); - assert_eq!(inst.use_regs().as_slice(), &[0, 1, 2]); - } - - #[test] - fn super_call_uses_arg_range() { - let inst = Inst::super_call(Operand::Reg(3), Operand::Reg(1), 1); - assert_eq!(inst.def_reg(), Some(3)); - assert_eq!(inst.use_regs().as_slice(), &[1]); - } - - #[test] - fn halt_implicitly_uses_reg0() { - // Pitfall 2:HALT 返回 regs[0],顶层 LOAD_VAR(None, r) 链靠它保活 - let inst = Inst::new(OpCode::HALT, Operand::None, Operand::None, Operand::None); - assert_eq!(inst.def_reg(), None); - assert_eq!(inst.use_regs().as_slice(), &[0]); - } - - #[test] - fn void_defs_rd_no_use() { - // VM 的 VOID handler 不读 a 槽 - let inst = Inst::new(OpCode::VOID, Operand::Reg(1), Operand::Reg(2), Operand::None); - assert_eq!(inst.def_reg(), Some(1)); - assert!(inst.use_regs().is_empty()); - } - - #[test] - fn load_const_create_closure_load_upvalue_no_use() { - // a 槽是 Const/Imm 立即数,非寄存器 - let lc = Inst::load_const(Operand::Reg(1), 5); - assert_eq!(lc.def_reg(), Some(1)); - assert!(lc.use_regs().is_empty()); - - let cc = Inst::create_closure(Operand::Reg(1), 0); - assert_eq!(cc.def_reg(), Some(1)); - assert!(cc.use_regs().is_empty()); - - let lu = Inst::new(OpCode::LOAD_UPVALUE, Operand::Reg(1), Operand::Imm(0), Operand::None); - assert_eq!(lu.def_reg(), Some(1)); - assert!(lu.use_regs().is_empty()); - } - - #[test] - fn none_slot_maps_to_reg0() { - // A3:None 槽与 lower.rs operand_to_u8 一致,统一映射物理 reg 0 - let lv = Inst::new(OpCode::LOAD_VAR, Operand::Reg(1), Operand::None, Operand::None); - assert_eq!(lv.use_regs().as_slice(), &[0]); - - let cg = Inst::new(OpCode::CELL_GET, Operand::Reg(0), Operand::None, Operand::Imm(0)); - assert_eq!(cg.use_regs().as_slice(), &[0]); - } - - #[test] - fn template_str_parses_expr_regs_from_ext() { - // ext[0] 跳过;后续 seg>>31==1 则低 8 位是 expr_reg(A1,emit 8 位编码) - let with_expr = Inst::template_str(Operand::Reg(1), 2, 10, &[0x1234, 0x8000_0000 | 5]); - assert_eq!(with_expr.use_regs().as_slice(), &[5]); - - // 纯 quasi(无表达式):不误报寄存器 - let no_expr = Inst::template_str(Operand::Reg(1), 1, 10, &[0x1234]); - assert!(no_expr.use_regs().is_empty()); - } - - #[test] - fn jmp_if_false_uses_cond_reg() { - let inst = Inst::jmp_if_false(3, 9); - assert_eq!(inst.def_reg(), None); - assert_eq!(inst.use_regs().as_slice(), &[3]); - } - - #[test] - fn jmp_and_try_no_use() { - assert!(Inst::jmp(9).use_regs().is_empty()); - assert!(Inst::try_begin(9).use_regs().is_empty()); - assert!(Inst::try_finally_begin(9).use_regs().is_empty()); - } - - #[test] - fn make_cell_no_def_uses_rd() { - let inst = Inst::new(OpCode::MAKE_CELL, Operand::Reg(5), Operand::None, Operand::None); - assert_eq!(inst.def_reg(), None); - assert_eq!(inst.use_regs().as_slice(), &[5]); - } - - #[test] - fn cell_set_and_store_upvalue_use_a_no_def() { - let cs = Inst::new(OpCode::CELL_SET, Operand::None, Operand::Reg(1), Operand::Imm(0)); - assert_eq!(cs.def_reg(), None); - assert_eq!(cs.use_regs().as_slice(), &[1]); - - let su = Inst::new(OpCode::STORE_UPVALUE, Operand::None, Operand::Reg(1), Operand::Imm(0)); - assert_eq!(su.def_reg(), None); - assert_eq!(su.use_regs().as_slice(), &[1]); - } - - #[test] - fn return_throw_use_rd_with_none_mapping_reg0() { - let ret = Inst::new(OpCode::RETURN, Operand::None, Operand::None, Operand::None); - assert_eq!(ret.def_reg(), None); - assert_eq!(ret.use_regs().as_slice(), &[0]); - - let thr = Inst::new(OpCode::THROW, Operand::Reg(2), Operand::None, Operand::None); - assert_eq!(thr.def_reg(), None); - assert_eq!(thr.use_regs().as_slice(), &[2]); - } - - #[test] - fn rest_object_uses_a() { - let inst = Inst::rest_object(Operand::Reg(0), Operand::Reg(1), 7); - assert_eq!(inst.def_reg(), Some(0)); - assert_eq!(inst.use_regs().as_slice(), &[1]); - } - - #[test] - fn for_in_next_defs_rd_no_use() { - let inst = Inst::new(OpCode::FOR_IN_NEXT, Operand::Reg(0), Operand::None, Operand::None); - assert_eq!(inst.def_reg(), Some(0)); - assert!(inst.use_regs().is_empty()); - } - - #[test] - fn store_var_def_rd_use_a() { - let inst = Inst::new(OpCode::STORE_VAR, Operand::Reg(0), Operand::Reg(1), Operand::None); - assert_eq!(inst.def_reg(), Some(0)); - assert_eq!(inst.use_regs().as_slice(), &[1]); - } - - // ── is_pure 契约测试(Pattern 1 表,纯/非纯族代表 opcode)── - - #[test] - fn pure_ops_are_deletable() { - let f = IRFunction::new(); - // 算术 / 比较 / 位 / 逻辑 - assert!(Inst::new(OpCode::ADD, Operand::Reg(0), Operand::Reg(1), Operand::Reg(2)).is_pure(&f)); - assert!(Inst::new(OpCode::SUB, Operand::Reg(0), Operand::Reg(1), Operand::Reg(2)).is_pure(&f)); - assert!(Inst::new(OpCode::MUL, Operand::Reg(0), Operand::Reg(1), Operand::Reg(2)).is_pure(&f)); - assert!(Inst::new(OpCode::NEG, Operand::Reg(0), Operand::Reg(1), Operand::None).is_pure(&f)); - assert!(Inst::new(OpCode::EQ, Operand::Reg(0), Operand::Reg(1), Operand::Reg(2)).is_pure(&f)); - assert!(Inst::new(OpCode::STRICT_EQ, Operand::Reg(0), Operand::Reg(1), Operand::Reg(2)).is_pure(&f)); - assert!(Inst::new(OpCode::LT, Operand::Reg(0), Operand::Reg(1), Operand::Reg(2)).is_pure(&f)); - assert!(Inst::new(OpCode::BIT_AND, Operand::Reg(0), Operand::Reg(1), Operand::Reg(2)).is_pure(&f)); - assert!(Inst::new(OpCode::BIT_NOT, Operand::Reg(0), Operand::Reg(1), Operand::None).is_pure(&f)); - assert!(Inst::new(OpCode::AND, Operand::Reg(0), Operand::Reg(1), Operand::Reg(2)).is_pure(&f)); - assert!(Inst::new(OpCode::OR, Operand::Reg(0), Operand::Reg(1), Operand::Reg(2)).is_pure(&f)); - assert!(Inst::new(OpCode::NOT, Operand::Reg(0), Operand::Reg(1), Operand::None).is_pure(&f)); - assert!(Inst::new(OpCode::NULLISH, Operand::Reg(0), Operand::Reg(1), Operand::Reg(2)).is_pure(&f)); - // 分配/常量/空操作 - assert!(Inst::new(OpCode::NOP, Operand::None, Operand::None, Operand::None).is_pure(&f)); - assert!(Inst::load_const(Operand::Reg(1), 0).is_pure(&f)); - assert!(Inst::new(OpCode::VOID, Operand::Reg(1), Operand::None, Operand::None).is_pure(&f)); - assert!(Inst::new(OpCode::TYPEOF, Operand::Reg(1), Operand::Reg(2), Operand::None).is_pure(&f)); - assert!(Inst::new(OpCode::NEW_OBJECT, Operand::Reg(1), Operand::None, Operand::None).is_pure(&f)); - assert!(Inst::new(OpCode::NEW_ARRAY, Operand::Reg(1), Operand::None, Operand::None).is_pure(&f)); - // 闭包/单元读取 - assert!(Inst::create_closure(Operand::Reg(1), 0).is_pure(&f)); - assert!(Inst::new(OpCode::LOAD_UPVALUE, Operand::Reg(1), Operand::Imm(0), Operand::None).is_pure(&f)); - assert!(Inst::new(OpCode::CELL_GET, Operand::Reg(0), Operand::Reg(1), Operand::Imm(0)).is_pure(&f)); - // 模板字符串 - assert!(Inst::template_str(Operand::Reg(1), 1, 10, &[0x1234]).is_pure(&f)); - // LOAD_VAR 普通(a=Reg)纯 - assert!(Inst::new(OpCode::LOAD_VAR, Operand::Reg(1), Operand::Reg(2), Operand::None).is_pure(&f)); - // STORE_VAR 一律有副作用(Pitfall 6:顶层变量赋值全局可观察,不删) - assert!(!Inst::new(OpCode::STORE_VAR, Operand::Reg(0), Operand::Reg(1), Operand::None).is_pure(&f)); - assert!(!Inst::new(OpCode::STORE_VAR, Operand::Reg(0), Operand::Reg(1), Operand::Imm(0)).is_pure(&f)); - assert!(!Inst::new(OpCode::STORE_VAR, Operand::Reg(0), Operand::Reg(1), Operand::Imm(1)).is_pure(&f)); - } - - #[test] - fn impure_ops_are_not_deletable() { - let f = IRFunction::new(); - // getter / 写对象属性 - assert!(!Inst::ic_get(Operand::Reg(1), Operand::Reg(2)).is_pure(&f)); - assert!(!Inst::new(OpCode::GET_PROP, Operand::Reg(0), Operand::Reg(1), Operand::Reg(2)).is_pure(&f)); - assert!(!Inst::new(OpCode::GET_PROP_DYNAMIC, Operand::Reg(0), Operand::Reg(1), Operand::Reg(2)).is_pure(&f)); - assert!(!Inst::ic_set(Operand::Reg(0), Operand::Reg(1), Operand::Reg(2)).is_pure(&f)); - assert!(!Inst::new(OpCode::SET_PROP, Operand::Reg(0), Operand::Reg(1), Operand::Reg(2)).is_pure(&f)); - assert!(!Inst::new(OpCode::SUPER_GET_PROP, Operand::Reg(0), Operand::Reg(1), Operand::Reg(2)).is_pure(&f)); - assert!(!Inst::new(OpCode::DEFINE_ACCESSOR, Operand::Reg(0), Operand::Reg(1), Operand::Reg(2)).is_pure(&f)); - assert!(!Inst::new(OpCode::DELETE_PROP_STATIC, Operand::Reg(0), Operand::Reg(0), Operand::None).is_pure(&f)); - assert!(!Inst::new(OpCode::DELETE_PROP_DYNAMIC, Operand::Reg(0), Operand::None, Operand::Reg(2)).is_pure(&f)); - // 调用 / 异常 / 控制流 - assert!(!Inst::call(Operand::Reg(0), Operand::Reg(1), Operand::Reg(2), 0).is_pure(&f)); - assert!(!Inst::call_native(Operand::Reg(0), Operand::Reg(1), Operand::Reg(2), 0).is_pure(&f)); - assert!(!Inst::new_expression(Operand::Reg(3), Operand::Reg(0), Operand::Reg(1), 0).is_pure(&f)); - assert!(!Inst::super_call(Operand::Reg(3), Operand::Reg(1), 0).is_pure(&f)); - assert!(!Inst::new(OpCode::CREATE_REGEXP, Operand::Reg(0), Operand::Reg(1), Operand::Reg(2)).is_pure(&f)); - assert!(!Inst::new(OpCode::RETURN, Operand::None, Operand::None, Operand::None).is_pure(&f)); - assert!(!Inst::new(OpCode::THROW, Operand::Reg(2), Operand::None, Operand::None).is_pure(&f)); - assert!(!Inst::new(OpCode::HALT, Operand::None, Operand::None, Operand::None).is_pure(&f)); - assert!(!Inst::jmp(9).is_pure(&f)); - assert!(!Inst::jmp_if_false(3, 9).is_pure(&f)); - assert!(!Inst::try_begin(9).is_pure(&f)); - assert!(!Inst::try_finally_begin(9).is_pure(&f)); - // 迭代器 / 复合更新 / 写共享状态 - assert!(!Inst::new(OpCode::FOR_IN_NEXT, Operand::Reg(0), Operand::None, Operand::None).is_pure(&f)); - assert!(!Inst::new(OpCode::FOR_OF_NEXT, Operand::Reg(0), Operand::None, Operand::None).is_pure(&f)); - assert!(!Inst::new(OpCode::INC_PRE, Operand::Reg(0), Operand::Reg(0), Operand::None).is_pure(&f)); - assert!(!Inst::new(OpCode::COMPOUND_ADD, Operand::Reg(0), Operand::Reg(1), Operand::None).is_pure(&f)); - assert!(!Inst::new(OpCode::MEMBER_INC, Operand::Reg(0), Operand::Reg(1), Operand::Reg(2)).is_pure(&f)); - assert!(!Inst::new(OpCode::COMPOUND_MEMBER_ADD, Operand::Reg(0), Operand::Reg(1), Operand::Reg(2)).is_pure(&f)); - assert!(!Inst::rest_object(Operand::Reg(0), Operand::Reg(1), 7).is_pure(&f)); - assert!(!Inst::new(OpCode::INSTANCEOF, Operand::Reg(0), Operand::Reg(1), Operand::Reg(2)).is_pure(&f)); - assert!(!Inst::new(OpCode::IN, Operand::Reg(0), Operand::Reg(1), Operand::Reg(2)).is_pure(&f)); - assert!(!Inst::new(OpCode::GET_PRIVATE, Operand::Reg(0), Operand::Reg(1), Operand::Reg(2)).is_pure(&f)); - assert!(!Inst::new(OpCode::SET_HOME_OBJECT, Operand::Reg(0), Operand::Reg(1), Operand::None).is_pure(&f)); - assert!(!Inst::new(OpCode::STORE_UPVALUE, Operand::None, Operand::Reg(1), Operand::Imm(0)).is_pure(&f)); - assert!(!Inst::new(OpCode::CELL_SET, Operand::None, Operand::Reg(1), Operand::Imm(0)).is_pure(&f)); - assert!(!Inst::new(OpCode::MAKE_CELL, Operand::Reg(5), Operand::None, Operand::None).is_pure(&f)); - // 占位 opcode 防御性有副作用 - assert!(!Inst::new(OpCode::SWITCH_TABLE, Operand::None, Operand::None, Operand::None).is_pure(&f)); - } - - #[test] - fn store_var_all_branches_are_impure() { - // Pitfall 6:STORE_VAR 写目标变量寄存器,顶层/模块作用域变量全局可观察,一律不删 - // (const guard 抛错、普通 var/let 赋值都可能改变外部观察状态) - let f = IRFunction::new(); - assert!(!Inst::new(OpCode::STORE_VAR, Operand::Reg(0), Operand::Reg(1), Operand::None).is_pure(&f)); - assert!(!Inst::new(OpCode::STORE_VAR, Operand::Reg(0), Operand::Reg(1), Operand::Imm(0)).is_pure(&f)); - assert!(!Inst::new(OpCode::STORE_VAR, Operand::Reg(0), Operand::Reg(1), Operand::Imm(1)).is_pure(&f)); - } - - #[test] - fn load_var_this_derived_constructor_special_case() { - // a==This + is_derived_constructor 时读 this 可能抛 ReferenceError,不可删 - let mut derived = IRFunction::new(); - derived.is_derived_constructor = true; - let non_derived = IRFunction::new(); - - let this_load = Inst::new(OpCode::LOAD_VAR, Operand::Reg(1), Operand::This, Operand::None); - assert!(!this_load.is_pure(&derived)); - assert!(this_load.is_pure(&non_derived)); - - // a=Reg 普通 LOAD_VAR 在 derived 函数中也纯 - let reg_load = Inst::new(OpCode::LOAD_VAR, Operand::Reg(1), Operand::Reg(2), Operand::None); - assert!(reg_load.is_pure(&derived)); - } } diff --git a/crates/oxide_ir/src/lib.rs b/crates/oxide_ir/src/lib.rs index 5d225c29..0d94cf75 100644 --- a/crates/oxide_ir/src/lib.rs +++ b/crates/oxide_ir/src/lib.rs @@ -11,6 +11,7 @@ //! //! 分析视图(CFG/LiveInfo/AllocMap)是 pass 输出,不住进 IR。 +pub mod contract; pub mod inst; pub mod lower; pub mod operand;