diff --git a/crates/oxide_bytecode/src/opcode.rs b/crates/oxide_bytecode/src/opcode.rs index f055e754..aae6429c 100644 --- a/crates/oxide_bytecode/src/opcode.rs +++ b/crates/oxide_bytecode/src/opcode.rs @@ -159,22 +159,22 @@ define_opcodes! { // ── Arithmetic (0x00-0x0F) ── ADD = 0x00 => "ADD", def = Some(SlotSpec::Slot(Slot::Rd)), uses = [SlotSpec::Slot(Slot::A), SlotSpec::Slot(Slot::B)], - pure = true, jump = false, term = false, ic = false, + pure = false, jump = false, term = false, ic = false, SUB = 0x01 => "SUB", def = Some(SlotSpec::Slot(Slot::Rd)), uses = [SlotSpec::Slot(Slot::A), SlotSpec::Slot(Slot::B)], - pure = true, jump = false, term = false, ic = false, + pure = false, jump = false, term = false, ic = false, MUL = 0x02 => "MUL", def = Some(SlotSpec::Slot(Slot::Rd)), uses = [SlotSpec::Slot(Slot::A), SlotSpec::Slot(Slot::B)], - pure = true, jump = false, term = false, ic = false, + pure = false, jump = false, term = false, ic = false, DIV = 0x03 => "DIV", def = Some(SlotSpec::Slot(Slot::Rd)), uses = [SlotSpec::Slot(Slot::A), SlotSpec::Slot(Slot::B)], - pure = true, jump = false, term = false, ic = false, + pure = false, jump = false, term = false, ic = false, MOD = 0x04 => "MOD", def = Some(SlotSpec::Slot(Slot::Rd)), uses = [SlotSpec::Slot(Slot::A), SlotSpec::Slot(Slot::B)], - pure = true, jump = false, term = false, ic = false, + pure = false, jump = false, term = false, ic = false, NEG = 0x05 => "NEG", def = Some(SlotSpec::Slot(Slot::Rd)), uses = [SlotSpec::Slot(Slot::A)], - pure = true, jump = false, term = false, ic = false, + pure = false, jump = false, term = false, ic = false, COMPOUND_ADD = 0x06 => "COMPOUND_ADD", def = Some(SlotSpec::Slot(Slot::Rd)), uses = [SlotSpec::Slot(Slot::Rd), SlotSpec::Slot(Slot::A)], pure = false, jump = false, term = false, ic = false, @@ -196,7 +196,7 @@ define_opcodes! { // 二元幂运算 `a ** b`(三寄存器,与 SUB/MUL 同族;0x00-0x0F 已满,值用空闲槽 0x94)。 EXP = 0x94 => "EXP", def = Some(SlotSpec::Slot(Slot::Rd)), uses = [SlotSpec::Slot(Slot::A), SlotSpec::Slot(Slot::B)], - pure = true, jump = false, term = false, ic = false, + pure = false, jump = false, term = false, ic = false, MOV = 0x0C => "MOV", // rd=dst, a=src(寄存器复制,区间拆分搬值) def = Some(SlotSpec::Slot(Slot::Rd)), uses = [SlotSpec::Slot(Slot::A)], pure = true, jump = false, term = false, ic = false, @@ -213,22 +213,22 @@ define_opcodes! { // ── 比较 (0x10-0x1F) ── EQ = 0x10 => "EQ", def = Some(SlotSpec::Slot(Slot::Rd)), uses = [SlotSpec::Slot(Slot::A), SlotSpec::Slot(Slot::B)], - pure = true, jump = false, term = false, ic = false, + pure = false, jump = false, term = false, ic = false, NEQ = 0x11 => "NEQ", def = Some(SlotSpec::Slot(Slot::Rd)), uses = [SlotSpec::Slot(Slot::A), SlotSpec::Slot(Slot::B)], - pure = true, jump = false, term = false, ic = false, + pure = false, jump = false, term = false, ic = false, LT = 0x12 => "LT", def = Some(SlotSpec::Slot(Slot::Rd)), uses = [SlotSpec::Slot(Slot::A), SlotSpec::Slot(Slot::B)], - pure = true, jump = false, term = false, ic = false, + pure = false, jump = false, term = false, ic = false, GT = 0x13 => "GT", def = Some(SlotSpec::Slot(Slot::Rd)), uses = [SlotSpec::Slot(Slot::A), SlotSpec::Slot(Slot::B)], - pure = true, jump = false, term = false, ic = false, + pure = false, jump = false, term = false, ic = false, LTE = 0x14 => "LTE", def = Some(SlotSpec::Slot(Slot::Rd)), uses = [SlotSpec::Slot(Slot::A), SlotSpec::Slot(Slot::B)], - pure = true, jump = false, term = false, ic = false, + pure = false, jump = false, term = false, ic = false, GTE = 0x15 => "GTE", def = Some(SlotSpec::Slot(Slot::Rd)), uses = [SlotSpec::Slot(Slot::A), SlotSpec::Slot(Slot::B)], - pure = true, jump = false, term = false, ic = false, + pure = false, jump = false, term = false, ic = false, IN = 0x16 => "IN", def = Some(SlotSpec::Slot(Slot::Rd)), uses = [SlotSpec::Slot(Slot::A), SlotSpec::Slot(Slot::B)], pure = false, jump = false, term = false, ic = false, @@ -249,7 +249,7 @@ define_opcodes! { pure = true, jump = false, term = false, ic = false, UNARY_PLUS = 0x1D => "UNARY_PLUS", def = Some(SlotSpec::Slot(Slot::Rd)), uses = [SlotSpec::Slot(Slot::A)], - pure = true, jump = false, term = false, ic = false, + pure = false, jump = false, term = false, ic = false, // ── 控制流 (0x1E-0x2F) ── BREAK = 0x1E => "BREAK", @@ -330,7 +330,7 @@ define_opcodes! { // ── 模板字符串 (0x38) ── TEMPLATE_STR = 0x38 => "TEMPLATE_STR", def = Some(SlotSpec::Slot(Slot::Rd)), uses = [SlotSpec::TemplateExprs], - pure = true, jump = false, term = false, ic = false, + pure = false, jump = false, term = false, ic = false, // ── 标签模板对象 (0x97) ── // GetTemplateObject:按 site 缓存模板对象(同一编译树同 site 恒返回同一对象), @@ -473,10 +473,10 @@ define_opcodes! { pure = false, jump = false, term = false, ic = false, // 多操作数拼接(连续 `+` 左结合链摊平):rd=结果,a=首操作数,ext=[n, op2..opn], // n=操作数总数,ext 字数 = 1+(n-1) = n;op 字无高位标记(纯 vreg,≤255), - // SpreadArgs 直接解析。与 ADD 同标纯(含 ToPrimitive 副作用仍静态可删,语义同 ADD)。 + // SpreadArgs 直接解析。含 ToPrimitive 副作用,不可删。 CONCAT_N = 0x93 => "CONCAT_N", def = Some(SlotSpec::Slot(Slot::Rd)), uses = [SlotSpec::Slot(Slot::A), SlotSpec::SpreadArgs], - pure = true, jump = false, term = false, ic = false, + pure = false, jump = false, term = false, ic = false, // ── 成员更新 (0x59-0x62) ── MEMBER_INC = 0x59 => "MEMBER_INC", // 结果写 a 槽(val 槽原地更新) @@ -668,25 +668,25 @@ define_opcodes! { // ── 位运算 (0x80-0x8F) ── BIT_AND = 0x80 => "BIT_AND", def = Some(SlotSpec::Slot(Slot::Rd)), uses = [SlotSpec::Slot(Slot::A), SlotSpec::Slot(Slot::B)], - pure = true, jump = false, term = false, ic = false, + pure = false, jump = false, term = false, ic = false, BIT_OR = 0x81 => "BIT_OR", def = Some(SlotSpec::Slot(Slot::Rd)), uses = [SlotSpec::Slot(Slot::A), SlotSpec::Slot(Slot::B)], - pure = true, jump = false, term = false, ic = false, + pure = false, jump = false, term = false, ic = false, BIT_XOR = 0x82 => "BIT_XOR", def = Some(SlotSpec::Slot(Slot::Rd)), uses = [SlotSpec::Slot(Slot::A), SlotSpec::Slot(Slot::B)], - pure = true, jump = false, term = false, ic = false, + pure = false, jump = false, term = false, ic = false, SHL = 0x83 => "SHL", def = Some(SlotSpec::Slot(Slot::Rd)), uses = [SlotSpec::Slot(Slot::A), SlotSpec::Slot(Slot::B)], - pure = true, jump = false, term = false, ic = false, + pure = false, jump = false, term = false, ic = false, SHR = 0x84 => "SHR", def = Some(SlotSpec::Slot(Slot::Rd)), uses = [SlotSpec::Slot(Slot::A), SlotSpec::Slot(Slot::B)], - pure = true, jump = false, term = false, ic = false, + pure = false, jump = false, term = false, ic = false, USHR = 0x85 => "USHR", def = Some(SlotSpec::Slot(Slot::Rd)), uses = [SlotSpec::Slot(Slot::A), SlotSpec::Slot(Slot::B)], - pure = true, jump = false, term = false, ic = false, + pure = false, jump = false, term = false, ic = false, BIT_NOT = 0x86 => "BIT_NOT", def = Some(SlotSpec::Slot(Slot::Rd)), uses = [SlotSpec::Slot(Slot::A)], - pure = true, jump = false, term = false, ic = false, + pure = false, jump = false, term = false, ic = false, TO_OBJECT = 0x87 => "TO_OBJECT", // rd 原地转换(读且写) def = Some(SlotSpec::Slot(Slot::Rd)), uses = [SlotSpec::Slot(Slot::Rd)], pure = false, jump = false, term = false, ic = false, diff --git a/crates/oxide_compiler/tests/compile_expr_tests.rs b/crates/oxide_compiler/tests/compile_expr_tests.rs index 7542afd0..e8bbae95 100644 --- a/crates/oxide_compiler/tests/compile_expr_tests.rs +++ b/crates/oxide_compiler/tests/compile_expr_tests.rs @@ -792,3 +792,12 @@ fn compile_delete_new_instanceof_in() { ); assert!(module.bytecode.iter().any(|&i| opcode::opcode(i) == OpCode::IN), "in should emit IN"); } + +#[test] +fn compile_nonfinal_relational_comparison_kept() { + let module = compile_source("let s = 1; s <= 1; s + 1;"); + assert!( + module.bytecode.iter().any(|&i| opcode::opcode(i) == OpCode::LTE), + "非末语句形 s <= 1 应保留 LTE 指令(结果虽未用,ToPrimitive 副作用不可删)" + ); +} diff --git a/crates/oxide_dce/src/iter_sweep.rs b/crates/oxide_dce/src/iter_sweep.rs index fef53f25..c52160e8 100644 --- a/crates/oxide_dce/src/iter_sweep.rs +++ b/crates/oxide_dce/src/iter_sweep.rs @@ -107,13 +107,12 @@ mod tests { use oxide_ir::inst::Inst; use oxide_ir::operand::Operand; - /// 线性死链:LOAD_CONST→ADD→结果丢弃,全删(剩 RETURN)。 + /// 线性死链:LOAD_CONST→MOV→结果丢弃,全删(剩 RETURN)。 #[test] fn linear_dead_chain_deleted() { let mut f = IRFunction::new(); f.insts.push(Inst::load_const(Operand::Reg(0), 0)); // 0: r0 = const - f.insts - .push(Inst::new(OpCode::ADD, Operand::Reg(1), Operand::Reg(0), Operand::Reg(0))); // 1: r1 = r0 + r0 + f.insts.push(Inst::inst_mov(Operand::Reg(1), Operand::Reg(0))); // 1: r1 = r0 f.insts .push(Inst::new(OpCode::RETURN, Operand::Reg(2), Operand::None, Operand::None)); // 2: return r2 let mut keep = vec![true; f.insts.len()]; @@ -175,13 +174,12 @@ mod tests { assert_eq!(keep, vec![false, true], "死 CREATE_CLOSURE 删,RETURN 保留"); } - /// 连锁迭代:写链 LOAD_CONST→ADD→LOAD_CONST 全死,逐轮删除至不动点。 + /// 连锁迭代:写链 LOAD_CONST→MOV→LOAD_CONST 全死,逐轮删除至不动点。 #[test] fn chained_dead_writes_deleted_iteratively() { let mut f = IRFunction::new(); - f.insts.push(Inst::load_const(Operand::Reg(0), 0)); // 0: r0(ADD 读,轮 2 删) - f.insts - .push(Inst::new(OpCode::ADD, Operand::Reg(1), Operand::Reg(0), Operand::Reg(0))); // 1: r1(轮 1 删) + f.insts.push(Inst::load_const(Operand::Reg(0), 0)); // 0: r0(MOV 读,轮 2 删) + f.insts.push(Inst::inst_mov(Operand::Reg(1), Operand::Reg(0))); // 1: r1(轮 1 删) f.insts.push(Inst::load_const(Operand::Reg(2), 2)); // 2: r2 无人读(轮 1 删) f.insts .push(Inst::new(OpCode::RETURN, Operand::Reg(5), Operand::None, Operand::None)); // 3 diff --git a/crates/oxide_dce/tests/precise_unit.rs b/crates/oxide_dce/tests/precise_unit.rs index 3d9971f7..b43d8fac 100644 --- a/crates/oxide_dce/tests/precise_unit.rs +++ b/crates/oxide_dce/tests/precise_unit.rs @@ -31,18 +31,8 @@ fn run_precise(f: &mut IRFunction, after: &[&[u32]], reg_count: usize) { #[test] fn reuse_killed_dead_write_deleted() { let mut f = IRFunction::new(); - f.insts.push(Inst::new( - oxide_bytecode::opcode::OpCode::ADD, - Operand::Reg(5), - Operand::Reg(1), - Operand::Reg(2), - )); - f.insts.push(Inst::new( - oxide_bytecode::opcode::OpCode::ADD, - Operand::Reg(5), - Operand::Reg(3), - Operand::Reg(4), - )); + f.insts.push(Inst::inst_mov(Operand::Reg(5), Operand::Reg(1))); + f.insts.push(Inst::inst_mov(Operand::Reg(5), Operand::Reg(3))); f.insts.push(Inst::new( oxide_bytecode::opcode::OpCode::RETURN, Operand::Reg(5), @@ -51,7 +41,7 @@ fn reuse_killed_dead_write_deleted() { )); // inst_live_after = [{3,4}, {5}, {}]:inst0 def 5 在 live_after[0] 无 5(后写杀死) run_precise(&mut f, &[&[3, 4], &[5], &[]], 5); - assert_eq!(f.insts.len(), 2, "死写(前一个 ADD)应被删"); + assert_eq!(f.insts.len(), 2, "死写(前一个 MOV)应被删"); } /// 局部死 STORE_VAR 删除(四条件全满足)。 diff --git a/crates/oxide_ir/tests/contract_tests.rs b/crates/oxide_ir/tests/contract_tests.rs index 9d268e78..2c484bac 100644 --- a/crates/oxide_ir/tests/contract_tests.rs +++ b/crates/oxide_ir/tests/contract_tests.rs @@ -252,16 +252,8 @@ fn regalloc_opcodes_def_use_contract() { #[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)); @@ -277,8 +269,6 @@ fn pure_ops_are_deletable() { 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)); // RegAlloc 辅助:MOV 纯 assert!(Inst::inst_mov(Operand::Reg(1), Operand::Reg(2)).is_pure(&f)); // LOAD_VAR 普通(a=Reg)纯 @@ -292,6 +282,16 @@ fn pure_ops_are_deletable() { #[test] fn impure_ops_are_not_deletable() { let f = IRFunction::new(); + // 算术 / 比较 / 位 / 模板字符串(ToNumber/ToPrimitive 可抛,不可删) + 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::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::template_str(Operand::Reg(1), 1, 10, &[0x1234]).is_pure(&f)); // 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)); @@ -440,7 +440,14 @@ fn assert_group(ops: &[OpCode], def: Option, uses: &[u32], pure: bool) { /// 每个 opcode 的 def/use/pure 与现有语义表一致(canonical rd=1, a=2, b=3)。 #[test] fn opcode_semantics_golden_table() { - // 二元运算:def=rd, uses=[a,b],纯 + // 二元运算:def=rd, uses=[a,b],纯(逻辑/严格相等不抛) + assert_group( + &[OpCode::AND, OpCode::OR, OpCode::STRICT_EQ, OpCode::STRICT_NEQ, OpCode::NULLISH], + Some(1), + &[2, 3], + true, + ); + // 二元但不可删:ToNumber/ToPrimitive 可抛,或 coerce 有观察路径 assert_group( &[ OpCode::ADD, @@ -454,31 +461,24 @@ fn opcode_semantics_golden_table() { OpCode::GT, OpCode::LTE, OpCode::GTE, - OpCode::AND, - OpCode::OR, - OpCode::STRICT_EQ, - OpCode::STRICT_NEQ, OpCode::BIT_AND, OpCode::BIT_OR, OpCode::BIT_XOR, OpCode::SHL, OpCode::SHR, OpCode::USHR, - OpCode::NULLISH, + OpCode::IN, + OpCode::INSTANCEOF, + OpCode::CREATE_REGEXP, ], Some(1), &[2, 3], - true, - ); - // 二元但不可删:coerce 有观察路径 - assert_group(&[OpCode::IN, OpCode::INSTANCEOF, OpCode::CREATE_REGEXP], Some(1), &[2, 3], false); - // 一元:def=rd, uses=[a] - assert_group( - &[OpCode::NEG, OpCode::NOT, OpCode::UNARY_PLUS, OpCode::BIT_NOT, OpCode::TYPEOF], - Some(1), - &[2], - true, + false, ); + // 一元:def=rd, uses=[a],纯(NOT/TYPEOF 不抛) + assert_group(&[OpCode::NOT, OpCode::TYPEOF], Some(1), &[2], true); + // 一元但不可删:ToNumber/ToPrimitive 可抛 + assert_group(&[OpCode::NEG, OpCode::UNARY_PLUS, OpCode::BIT_NOT], Some(1), &[2], false); // 复合赋值:def=rd, uses=[rd,a],不可删 assert_group( &[ @@ -551,10 +551,10 @@ fn opcode_semantics_golden_table() { assert_contract(OpCode::THROW, None, &[1], false); assert_contract(OpCode::RETURN, None, &[1], false); assert_contract(OpCode::HALT, None, &[0], false); - // 模板字符串:表达式寄存器来自 ext - assert_contract(OpCode::TEMPLATE_STR, Some(1), &[5], true); - // 多操作数拼接:a 槽首操作数 + ext[1..] 后续操作数(SpreadArgs 解析) - assert_contract(OpCode::CONCAT_N, Some(1), &[2, 5, 9], true); + // 模板字符串:表达式寄存器来自 ext;ToPrimitive 可抛,不可删 + assert_contract(OpCode::TEMPLATE_STR, Some(1), &[5], false); + // 多操作数拼接:a 槽首操作数 + ext[1..] 后续操作数(SpreadArgs 解析);ToPrimitive 可抛,不可删 + assert_contract(OpCode::CONCAT_N, Some(1), &[2, 5, 9], false); // 小语言特性 assert_contract(OpCode::DELETE_PROP_STATIC, None, &[1], false); assert_contract(OpCode::DELETE_PROP_DYNAMIC, None, &[1, 3], false);