Files
OxideJS-New/crates/oxide_ir/src/contract.rs
T
pjh123 abeab6f216 fix: finally 体直落进入时置位 finally_active 防完成穿越计数错乱
catch 体正常结束后指令直落入 finally 体,此前 finally_active 未被置位,
finally 内 break/continue/return/throw 触发未进入分支导致 finally 体重复
执行、完成计数错乱。新增无操作数标记指令 TRY_FINALLY_ENTER,在 finally
体入口统一置位栈顶 handler 的 finally_active,覆盖 JMP/直落/unwind/完成
穿越/挂起恢复全部进入路径,并删除 control_flow.rs 中对跳转目标匹配的
脆弱栈顶检测。
2026-08-15 07:47:54 +08:00

808 lines
38 KiB
Rust
Raw 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.
//! Inst 寄存器契约:def/use 扫描 + 副作用判定(DCE 与 liveness 共用单源)。
//!
//! `def_reg` / `use_regs` / `is_pure` 是 `Inst` 的类型契约方法(`impl Inst` 与 `inst.rs`
//! 中的构造 API 同属一类型),落在 oxide_ir 契约层供 DCE 消费、liveness 复用。
//! 寄存器规则与 VM dispatch handler 行为一致:
//! CALL 隐式写 reg 0、GET_PROP 结果写 a/b 槽、HALT 隐式读 reg 0、None 槽映射 Reg(0)。
use oxide_bytecode::opcode::{OpCode, Slot, SlotSpec};
use smallvec::SmallVec;
use crate::inst::Inst;
use crate::operand::Operand;
use crate::IRFunction;
impl Inst {
/// 本指令定义的寄存器(None 槽按 Reg(0) 映射;CALL 系含隐式 reg 0)。
/// 无写入返回 None。
///
/// # 步骤
/// 表驱动解析:取语义表 def 槽位 → 解出对应操作数槽寄存器;Reg0 恒为常量 0。
///
/// # 边界与前提
/// - `SlotSpec::Range/SpreadArgs/TemplateExprs/BrandReg` 不可能作 def,返回 None。
pub fn def_reg(&self) -> Option<u32> {
match self.op.semantics().def? {
SlotSpec::Slot(slot) => reg_of(slot_operand(slot, self)),
SlotSpec::Reg0 => Some(0),
_ => None,
}
}
/// 本指令读取的寄存器(None 槽按 Reg(0) 映射;HALT 特判 reg 0;TEMPLATE_STR 解析 ext)。
///
/// # 步骤
/// 按语义表 uses 槽位序逐项解析,序即输出序。
///
/// # 边界与前提
/// - `Range`:nargs=ext[0],从指定槽起连续 nargs 个寄存器。
/// - `SpreadArgs`:ext[1..] 每个字 `& 0x7FFF_FFFF`。
/// - `TemplateExprs`:ext[1..] 中 `seg>>31==1` 的低 31 位。
/// - `BrandReg`:ext[0] 非 0 才产生 use。
/// - `ExtReg`:ext[0] `& 0x7FFF_FFFF`(高位标记的寄存器号)。
pub fn use_regs(&self) -> SmallVec<[u32; 4]> {
let mut uses = SmallVec::new();
for &spec in self.op.semantics().uses {
match spec {
SlotSpec::Slot(slot) => push_operand(&mut uses, slot_operand(slot, self)),
SlotSpec::Reg0 => uses.push(0),
SlotSpec::Range(slot) => {
let nargs = self.ext.first().copied().unwrap_or(0);
push_range(&mut uses, reg_of(slot_operand(slot, self)), nargs);
}
SlotSpec::SpreadArgs => {
for &w in self.ext.iter().skip(1) {
uses.push(w & 0x7FFF_FFFF);
}
}
SlotSpec::TemplateExprs => {
for seg in self.ext.iter().skip(1) {
if seg >> 31 == 1 {
uses.push(seg & 0x7FFF_FFFF);
}
}
}
SlotSpec::BrandReg => {
let brand_reg = self.ext.first().copied().unwrap_or(0);
if brand_reg != 0 {
uses.push(brand_reg);
}
}
SlotSpec::ExtReg => {
let w = self.ext.first().copied().unwrap_or(0);
uses.push(w & 0x7FFF_FFFF);
}
}
}
uses
}
/// 本指令是否无观察副作用(LOAD_VAR 的 This+derived 特判需要函数元信息)。
///
/// 纯 = 结果未用时可删。表内 pure 是静态字段,LOAD_VAR 上下文例外在此叠加。
pub fn is_pure(&self, f: &IRFunction) -> bool {
let base = self.op.semantics().pure;
base && !(self.op == OpCode::LOAD_VAR && self.a == Operand::This && f.is_derived_constructor)
}
}
/// 槽位 → 对应操作数(表解析用;Const/Imm/Label 由 reg_of 过滤)。
fn slot_operand(slot: Slot, i: &Inst) -> &Operand {
match slot {
Slot::Rd => &i.rd,
Slot::A => &i.a,
Slot::B => &i.b,
}
}
/// 操作数 → 物理寄存器号。None 槽映射 reg 0(与 lower.rs operand_to_u8 一致);
/// Const/Imm/Label 不是寄存器槽,返回 None。
fn reg_of(o: &Operand) -> Option<u32> {
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<u32>, 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 契约测试(反直觉槽位断言)──
#[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() {
// 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() {
// 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() {
// 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() {
// 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() {
// 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 spread_call_def_and_uses() {
// CALL_SPREAD:结果隐式写 reg 0;use = callee + this + ext 有序实参字(静态/spread 同编)
let call = Inst::call_spread(Operand::Reg(0), Operand::Reg(1), &[5, 0x8000_0000 | 9, 300]);
assert_eq!(call.def_reg(), Some(0));
assert_eq!(call.use_regs().as_slice(), &[0, 1, 5, 9, 300]);
// NEW_EXPRESSION_SPREAD:rd 即 def;use = ctor + 实参字
let ne = Inst::new_expression_spread(Operand::Reg(3), Operand::Reg(0), &[7, 0x8000_0000 | 12]);
assert_eq!(ne.def_reg(), Some(3));
assert_eq!(ne.use_regs().as_slice(), &[0, 7, 12]);
// SUPER_CALL_SPREAD:无 callee/this 槽
let sc = Inst::super_call_spread(Operand::Reg(3), &[4, 0x8000_0000 | 8]);
assert_eq!(sc.def_reg(), Some(3));
assert_eq!(sc.use_regs().as_slice(), &[4, 8]);
}
#[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() {
// 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] 跳过;表达式段在 RegAlloc 前保留完整 u32 vreg。
let with_expr = Inst::template_str(Operand::Reg(1), 2, 10, &[0x1234, 0x8000_0000 | 300]);
assert_eq!(with_expr.use_regs().as_slice(), &[300]);
// 纯 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, None);
assert_eq!(inst.def_reg(), Some(0));
// b 槽 None 映射 reg 0(excl_arr 缺省),REST_OBJECT 运行时同时读源与排除键。
assert_eq!(inst.use_regs().as_slice(), &[1, 0]);
}
#[test]
fn spread_object_def_rd_uses_rd_and_a() {
// SPREAD_OBJECT 原地改目标对象:def=rd,use=[rd, a],不可删除(有观察副作用)
let inst = Inst::spread_object(Operand::Reg(3), Operand::Reg(7));
assert_eq!(inst.def_reg(), Some(3));
assert_eq!(inst.use_regs().as_slice(), &[3, 7]);
let f = IRFunction::new();
assert!(!inst.is_pure(&f));
}
#[test]
fn create_arguments_defs_rd_no_register_use_impure() {
// CREATE_ARGUMENTS 写 rd;实参从 spill 栈读取,无寄存器 use;
// 创建对象有观察副作用(分配的 arguments 可被外部观察到),不可删除。
let inst = Inst::create_arguments(Operand::Reg(6));
assert_eq!(inst.def_reg(), Some(6));
assert!(inst.use_regs().is_empty());
let f = IRFunction::new();
assert!(!inst.is_pure(&f));
}
#[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]);
}
#[test]
fn regalloc_opcodes_def_use_contract() {
let mov = Inst::inst_mov(Operand::Reg(3), Operand::Reg(7));
assert_eq!(mov.def_reg(), Some(3));
assert_eq!(mov.use_regs().as_slice(), &[7]);
let spill = Inst::inst_spill(Operand::Reg(5), 42);
assert_eq!(spill.def_reg(), None);
assert_eq!(spill.use_regs().as_slice(), &[5]);
let unspill = Inst::inst_unspill(Operand::Reg(9), 42);
assert_eq!(unspill.def_reg(), Some(9));
assert!(unspill.use_regs().is_empty());
}
// ── is_pure 契约测试(纯/非纯族代表 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));
// RegAlloc 辅助:MOV 纯
assert!(Inst::inst_mov(Operand::Reg(1), Operand::Reg(2)).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 一律有副作用(顶层变量赋值全局可观察,不删)
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::DEFINE_ACCESSOR_DYNAMIC, 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::call_spread(Operand::Reg(0), Operand::Reg(1), &[3]).is_pure(&f));
assert!(!Inst::new_expression_spread(Operand::Reg(3), Operand::Reg(0), &[2]).is_pure(&f));
assert!(!Inst::super_call_spread(Operand::Reg(3), &[2]).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, None).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));
// RegAlloc 辅助:SPILL/UNSPILL 有副作用(删 SPILL 后 UNSPILL 读陈旧值)
assert!(!Inst::inst_spill(Operand::Reg(5), 42).is_pure(&f));
assert!(!Inst::inst_unspill(Operand::Reg(9), 42).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() {
// 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));
}
// ── 表驱动解析 vs 老 match 的一致性(Phase B1 迁移校验)──
/// 每个 opcode 构造一条规范指令:rd/a/b 置 Reg(1/2/3),ext 按族填真实布局。
fn canonical_inst(op: OpCode) -> Inst {
match op {
OpCode::CALL | OpCode::CALL_NATIVE => {
Inst::call(Operand::Reg(1), Operand::Reg(2), Operand::Reg(3), 2)
}
OpCode::NEW_EXPRESSION => {
Inst::new_expression(Operand::Reg(1), Operand::Reg(2), Operand::Reg(3), 2)
}
OpCode::SUPER_CALL => Inst::super_call(Operand::Reg(1), Operand::Reg(2), 2),
OpCode::CALL_SPREAD => {
Inst::call_spread(Operand::Reg(1), Operand::Reg(2), &[5, 0x8000_0000 | 9])
}
OpCode::NEW_EXPRESSION_SPREAD => {
Inst::new_expression_spread(Operand::Reg(1), Operand::Reg(2), &[5, 0x8000_0000 | 9])
}
OpCode::SUPER_CALL_SPREAD => {
Inst::super_call_spread(Operand::Reg(1), &[5, 0x8000_0000 | 9])
}
OpCode::IC_GET_PROP => Inst::ic_get(Operand::Reg(1), Operand::Reg(2)),
OpCode::IC_SET_PROP => Inst::ic_set(Operand::Reg(1), Operand::Reg(2), Operand::Reg(3)),
OpCode::MEMBER_INC => {
Inst::member_inc(Operand::Reg(1), Operand::Reg(2), Operand::Reg(3))
}
OpCode::MEMBER_DEC => {
Inst::member_dec(Operand::Reg(1), Operand::Reg(2), Operand::Reg(3))
}
OpCode::COMPOUND_MEMBER_ADD => {
Inst::compound_member_add(Operand::Reg(1), Operand::Reg(2), Operand::Reg(3))
}
OpCode::COMPOUND_MEMBER_SUB => {
Inst::compound_member_sub(Operand::Reg(1), Operand::Reg(2), Operand::Reg(3))
}
OpCode::COMPOUND_MEMBER_MUL => {
Inst::compound_member_mul(Operand::Reg(1), Operand::Reg(2), Operand::Reg(3))
}
OpCode::COMPOUND_MEMBER_DIV => {
Inst::compound_member_div(Operand::Reg(1), Operand::Reg(2), Operand::Reg(3))
}
OpCode::COMPOUND_MEMBER_MOD => {
Inst::compound_member_mod(Operand::Reg(1), Operand::Reg(2), Operand::Reg(3))
}
OpCode::COMPOUND_MEMBER_EXP => {
Inst::compound_member_exp(Operand::Reg(1), Operand::Reg(2), Operand::Reg(3))
}
OpCode::COMPOUND_MEMBER_BIT_AND => {
Inst::compound_member_bit_and(Operand::Reg(1), Operand::Reg(2), Operand::Reg(3))
}
OpCode::COMPOUND_MEMBER_BIT_OR => {
Inst::compound_member_bit_or(Operand::Reg(1), Operand::Reg(2), Operand::Reg(3))
}
OpCode::COMPOUND_MEMBER_BIT_XOR => {
Inst::compound_member_bit_xor(Operand::Reg(1), Operand::Reg(2), Operand::Reg(3))
}
OpCode::COMPOUND_MEMBER_SHL => {
Inst::compound_member_shl(Operand::Reg(1), Operand::Reg(2), Operand::Reg(3))
}
OpCode::COMPOUND_MEMBER_SHR => {
Inst::compound_member_shr(Operand::Reg(1), Operand::Reg(2), Operand::Reg(3))
}
OpCode::COMPOUND_MEMBER_USHR => {
Inst::compound_member_ushr(Operand::Reg(1), Operand::Reg(2), Operand::Reg(3))
}
OpCode::TEMPLATE_STR => Inst::template_str(Operand::Reg(1), 2, 10, &[0x1234, 0x8000_0000 | 5]),
OpCode::GET_PRIVATE => {
Inst::get_private(Operand::Reg(1), Operand::Reg(2), Operand::Reg(3), 7, 99)
}
OpCode::SET_PRIVATE => {
Inst::set_private(Operand::Reg(1), Operand::Reg(2), Operand::Reg(3), 7, 99)
}
OpCode::INIT_PRIVATE => {
Inst::init_private(Operand::Reg(1), Operand::Reg(2), Operand::Reg(3), true)
}
OpCode::PRIVATE_BRAND_IN => {
Inst::private_brand_in(Operand::Reg(1), Operand::Reg(2), Operand::Reg(3), 7, 99)
}
OpCode::SPILL => Inst::inst_spill(Operand::Reg(1), 42),
OpCode::UNSPILL => Inst::inst_unspill(Operand::Reg(1), 42),
OpCode::REST_OBJECT => Inst::rest_object(Operand::Reg(1), Operand::Reg(2), 7, None),
OpCode::DEFINE_ACCESSOR => {
Inst::define_accessor(Operand::Reg(1), Operand::Reg(2), Operand::Reg(3), 42)
}
OpCode::DEFINE_ACCESSOR_DYNAMIC => {
Inst::define_accessor_dynamic(Operand::Reg(1), Operand::Reg(2), Operand::Reg(3), 7)
}
OpCode::DELETE_PROP_STATIC => Inst::delete_prop_static(Operand::Reg(1), 9),
_ => Inst::new(op, Operand::Reg(1), Operand::Reg(2), Operand::Reg(3)),
}
}
/// 全 opcode 表遍历断言(golden):def/use/pure 与表逐项一致。
fn assert_contract(op: OpCode, def: Option<u32>, uses: &[u32], pure: bool) {
let inst = canonical_inst(op);
let f = IRFunction::new();
assert_eq!(inst.def_reg(), def, "{op}: def");
assert_eq!(inst.use_regs().as_slice(), uses, "{op}: uses");
assert_eq!(inst.is_pure(&f), pure, "{op}: pure");
}
/// 同一规格的多个 opcode 批量断言。
fn assert_group(ops: &[OpCode], def: Option<u32>, uses: &[u32], pure: bool) {
for &op in ops {
assert_contract(op, def, uses, pure);
}
}
/// 每个 opcode 的 def/use/pure 与现有语义表一致(canonical rd=1, a=2, b=3)。
#[test]
fn opcode_semantics_golden_table() {
// 二元运算:def=rd, uses=[a,b],纯
assert_group(
&[
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::STRICT_EQ,
OpCode::STRICT_NEQ,
OpCode::BIT_AND,
OpCode::BIT_OR,
OpCode::BIT_XOR,
OpCode::SHL,
OpCode::SHR,
OpCode::USHR,
OpCode::NULLISH,
],
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,
);
// 复合赋值:def=rd, uses=[rd,a],不可删
assert_group(
&[
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,
],
Some(1),
&[1, 2],
false,
);
// RegAlloc 辅助
assert_contract(OpCode::MOV, Some(1), &[2], true);
assert_contract(OpCode::SPILL, None, &[1], false);
assert_contract(OpCode::UNSPILL, Some(1), &[], false);
assert_contract(OpCode::SPREAD_OBJECT, Some(1), &[1, 2], false);
// 无条件跳转 / try 标记 / 生成器 body 标记:无 def 无 use
assert_group(
&[
OpCode::BREAK,
OpCode::CONTINUE,
OpCode::JMP,
OpCode::TRY_BEGIN,
OpCode::TRY_END,
OpCode::TRY_FINALLY_BEGIN,
OpCode::TRY_FINALLY_ENTER,
OpCode::TRY_FINALLY_END,
OpCode::FOR_IN_CLEANUP,
OpCode::FOR_OF_CLOSE,
OpCode::SUSPEND_BODY,
],
None,
&[],
false,
);
// 条件跳转:uses=[rd]
assert_group(&[OpCode::JMP_IF_FALSE, OpCode::JMP_IF_TRUE, OpCode::JMP_IF_NULLISH], None, &[1], false);
// 迭代器 INIT 读 a;NEXT/DONE 无 use 写 rd
assert_group(&[OpCode::FOR_OF_INIT, OpCode::FOR_IN_INIT, OpCode::FOR_AWAIT_OF_INIT], None, &[2], false);
assert_group(
&[
OpCode::FOR_OF_NEXT,
OpCode::FOR_IN_NEXT,
OpCode::FOR_IN_DONE,
OpCode::FOR_OF_DONE,
OpCode::FOR_AWAIT_OF_NEXT,
],
Some(1),
&[],
false,
);
assert_contract(OpCode::FOR_AWAIT_OF_DONE, Some(1), &[2], false);
// 自增/自减:def=rd, uses=[rd]
assert_group(&[OpCode::INC_PRE, OpCode::INC_POST, OpCode::DEC_PRE, OpCode::DEC_POST], Some(1), &[1], false);
// 占位 opcode:def=rd 保守,不可删
assert_group(
&[
OpCode::SWITCH_TABLE,
OpCode::PROFILE_SHAPE,
OpCode::PROFILE_BRANCH,
OpCode::PROFILE_CALL,
OpCode::FORK,
OpCode::JOIN,
],
Some(1),
&[],
false,
);
// 异常 / 返回 / 停机
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);
// 小语言特性
assert_contract(OpCode::DELETE_PROP_STATIC, None, &[1], false);
assert_contract(OpCode::DELETE_PROP_DYNAMIC, None, &[1, 3], false);
assert_contract(OpCode::MAKE_CELL, None, &[1], false);
assert_contract(OpCode::MAKE_CELL_FRESH, None, &[1], false);
assert_contract(OpCode::CELL_SET, None, &[2], false);
assert_contract(OpCode::CELL_GET, Some(1), &[2], true);
assert_contract(OpCode::REST_OBJECT, Some(1), &[2, 0], false);
// 变量加载族
assert_contract(OpCode::LOAD_VAR, Some(1), &[2], true);
assert_contract(OpCode::STORE_VAR, Some(1), &[2], false);
assert_contract(OpCode::LOAD_CONST, Some(1), &[], true);
assert_contract(OpCode::LOAD_UPVALUE, Some(1), &[], true);
assert_contract(OpCode::CREATE_CLOSURE, Some(1), &[], true);
assert_contract(OpCode::STORE_UPVALUE, None, &[2], false);
// 调用族:CALL 系隐式写 reg 0
assert_contract(OpCode::CALL, Some(0), &[1, 2, 3, 4], false);
assert_contract(OpCode::CALL_NATIVE, Some(0), &[1, 2, 3, 4], false);
assert_contract(OpCode::NEW_EXPRESSION, Some(1), &[2, 3, 4], false);
assert_contract(OpCode::SUPER_CALL, Some(1), &[2, 3], false);
assert_contract(OpCode::CALL_SPREAD, Some(0), &[1, 2, 5, 9], false);
assert_contract(OpCode::NEW_EXPRESSION_SPREAD, Some(1), &[2, 5, 9], false);
assert_contract(OpCode::SUPER_CALL_SPREAD, Some(1), &[5, 9], false);
// super 属性读 / home object / accessor
assert_group(&[OpCode::SUPER_GET_PROP, OpCode::SUPER_STATIC_GET_PROP], Some(1), &[2, 3], false);
assert_contract(OpCode::SET_HOME_OBJECT, None, &[1, 2], false);
assert_contract(OpCode::DEFINE_ACCESSOR, None, &[1, 2, 3], false);
assert_contract(OpCode::DEFINE_ACCESSOR_DYNAMIC, None, &[1, 2, 3, 7], false);
// Object Property:GET_PROP 写 a 槽、DYNAMIC 写 b 槽
assert_contract(OpCode::GET_PROP, Some(2), &[1, 3], false);
assert_contract(OpCode::GET_PROP_DYNAMIC, Some(3), &[1, 2], false);
assert_contract(OpCode::IC_GET_PROP, Some(1), &[1, 2], false);
assert_contract(OpCode::SET_PROP, None, &[1, 2, 3], false);
assert_contract(OpCode::SET_PROP_DYNAMIC, None, &[1, 2, 3], false);
// SET_PROP_BATCH:b 槽是 Imm(slot) 非寄存器,canonical 构造下仍按槽位读。
assert_contract(OpCode::SET_PROP_BATCH, None, &[1, 2, 3], false);
assert_contract(OpCode::IC_SET_PROP, None, &[1, 2, 3], false);
assert_contract(OpCode::SET_ELEM, None, &[1, 2, 3], false);
assert_group(&[OpCode::NEW_OBJECT, OpCode::NEW_ARRAY], Some(1), &[], true);
// define 语义属性写入 / arguments / rest
assert_group(&[OpCode::DEFINE_PROP, OpCode::DEFINE_GLOBAL_PROP], None, &[1, 2, 3], false);
assert_contract(OpCode::DEFINE_PROP_ATTRS, None, &[1, 2, 3], false);
assert_contract(OpCode::DEFINE_ACCESSOR_ATTRS, None, &[1, 2, 3], false);
assert_group(&[OpCode::CREATE_ARGUMENTS, OpCode::CREATE_REST_ARRAY], Some(1), &[], false);
// 成员更新:val 槽原地写(a/b 槽)
assert_group(&[OpCode::MEMBER_INC, OpCode::MEMBER_DEC], Some(2), &[1, 3], false);
assert_group(&[OpCode::DYN_MEMBER_INC, OpCode::DYN_MEMBER_DEC], Some(3), &[1, 2], false);
assert_group(
&[
OpCode::COMPOUND_MEMBER_ADD,
OpCode::COMPOUND_MEMBER_SUB,
OpCode::COMPOUND_MEMBER_MUL,
OpCode::COMPOUND_MEMBER_DIV,
OpCode::COMPOUND_MEMBER_MOD,
OpCode::COMPOUND_MEMBER_EXP,
OpCode::COMPOUND_MEMBER_BIT_AND,
OpCode::COMPOUND_MEMBER_BIT_OR,
OpCode::COMPOUND_MEMBER_BIT_XOR,
OpCode::COMPOUND_MEMBER_SHL,
OpCode::COMPOUND_MEMBER_SHR,
OpCode::COMPOUND_MEMBER_USHR,
],
Some(2),
&[1, 2, 3],
false,
);
// 私有成员:brand_reg 非 0 时产生额外 use
assert_contract(OpCode::GET_PRIVATE, Some(1), &[2, 3, 7], false);
assert_contract(OpCode::SET_PRIVATE, None, &[1, 2, 3, 7], false);
assert_contract(OpCode::INIT_PRIVATE, None, &[1, 2, 3], false);
assert_contract(OpCode::PRIVATE_BRAND_IN, Some(1), &[2, 3], false);
// 生成器 / 异步:恢复值经 reg 0
assert_group(&[OpCode::YIELD, OpCode::YIELD_STAR, OpCode::AWAIT], Some(0), &[1], false);
assert_contract(OpCode::FOR_AWAIT_OF_CLOSE, None, &[], false);
// 位 / 对象 / 杂项
assert_contract(OpCode::TO_OBJECT, Some(1), &[1], false);
// NOP:rd=None 时 def_reg 仍为 Some(0)(catch-all 语义,表内保持 def=Rd)
assert_eq!(Inst::new(OpCode::NOP, Operand::None, Operand::None, Operand::None).def_reg(), Some(0));
assert_contract(OpCode::NOP, Some(1), &[], true);
assert_contract(OpCode::VOID, Some(1), &[], true);
}
}