fix: 可抛算术比较位 opcode 纯标翻转,DCE 不再删副作用指令

This commit is contained in:
2026-09-27 01:28:49 +00:00
parent f8722ca91d
commit 18737c70ea
5 changed files with 72 additions and 75 deletions
+24 -24
View File
@@ -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,
@@ -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 副作用不可删)"
);
}
+5 -7
View File
@@ -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
+3 -13
View File
@@ -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 删除(四条件全满足)。
+31 -31
View File
@@ -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<u32>, 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);