mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-23 18:21:13 +08:00
feat: 新增 MOV/SPILL/UNSPILL opcode 登记 + 构造 API + 契约层(D-06/D-07)
This commit is contained in:
@@ -64,6 +64,9 @@ define_opcodes! {
|
||||
COMPOUND_DIV = 0x09 => "COMPOUND_DIV",
|
||||
COMPOUND_MOD = 0x0A => "COMPOUND_MOD",
|
||||
COMPOUND_EXP = 0x0B => "COMPOUND_EXP",
|
||||
MOV = 0x0C => "MOV", // rd=dst, a=src(寄存器复制,D-07,区间拆分搬值)
|
||||
SPILL = 0x0D => "SPILL", // rd=src, ext=[slot u16](溢出写 VM spill 栈,D-06)
|
||||
UNSPILL = 0x0E => "UNSPILL", // rd=dst, ext=[slot u16](从 VM spill 栈恢复,D-06)
|
||||
|
||||
// -- Comparison (0x10-0x1F) --
|
||||
EQ = 0x10 => "EQ",
|
||||
@@ -322,8 +325,14 @@ mod tests {
|
||||
assert_eq!(OpCode::STRICT_EQ as u8, 0x1A);
|
||||
assert_eq!(OpCode::JMP_IF_NULLISH as u8, 0x8F);
|
||||
assert_eq!(OpCode::VOID as u8, 0xF3);
|
||||
assert_eq!(OpCode::MOV as u8, 0x0C);
|
||||
assert_eq!(OpCode::SPILL as u8, 0x0D);
|
||||
assert_eq!(OpCode::UNSPILL as u8, 0x0E);
|
||||
assert_eq!(OpCode::ADD.to_string(), "ADD");
|
||||
assert_eq!(OpCode::COMPOUND_MEMBER_EXP.to_string(), "COMPOUND_MEMBER_EXP");
|
||||
assert_eq!(OpCode::MOV.to_string(), "MOV");
|
||||
assert_eq!(OpCode::SPILL.to_string(), "SPILL");
|
||||
assert_eq!(OpCode::UNSPILL.to_string(), "UNSPILL");
|
||||
|
||||
assert!(OpCode::try_from(0x1B).is_err());
|
||||
assert!(OpCode::try_from(0xFF).is_err());
|
||||
|
||||
@@ -58,6 +58,8 @@ impl Inst {
|
||||
| OpCode::DELETE_PROP_DYNAMIC
|
||||
| OpCode::DEFINE_ACCESSOR
|
||||
| OpCode::SET_HOME_OBJECT
|
||||
// SPILL 写 VM spill 栈而非寄存器(恢复由 UNSPILL 写 rd)
|
||||
| OpCode::SPILL
|
||||
| OpCode::FOR_IN_INIT
|
||||
| OpCode::FOR_OF_INIT
|
||||
| OpCode::FOR_IN_CLEANUP
|
||||
@@ -172,6 +174,10 @@ impl Inst {
|
||||
OpCode::LOAD_VAR | OpCode::STORE_VAR | OpCode::CELL_GET => {
|
||||
push_operand(&mut uses, &self.a);
|
||||
}
|
||||
// RegAlloc 辅助:MOV 读 src(a 槽);SPILL 读待溢出寄存器(rd);UNSPILL 仅写 rd 无寄存器读
|
||||
OpCode::MOV => push_operand(&mut uses, &self.a),
|
||||
OpCode::SPILL => push_operand(&mut uses, &self.rd),
|
||||
OpCode::UNSPILL => {}
|
||||
// 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),
|
||||
@@ -234,6 +240,10 @@ impl Inst {
|
||||
// 分配 / 常量 / 空操作(a 槽非寄存器)
|
||||
OpCode::NOP | OpCode::LOAD_CONST | OpCode::VOID | OpCode::TYPEOF | OpCode::NEW_OBJECT
|
||||
| OpCode::NEW_ARRAY => true,
|
||||
// MOV:寄存器复制无副作用,可删无害
|
||||
OpCode::MOV => true,
|
||||
// SPILL/UNSPILL:RegAlloc 插入的指令,删 SPILL 后 UNSPILL 读陈旧值(Pitfall 7),永不删
|
||||
OpCode::SPILL | OpCode::UNSPILL => false,
|
||||
// 闭包 / cell 读取:无抛错路径(D-07)
|
||||
OpCode::CREATE_CLOSURE | OpCode::LOAD_UPVALUE | OpCode::CELL_GET => true,
|
||||
// 模板字符串:纯拼接写 rd,无抛错(use 统计覆盖其 ext)
|
||||
@@ -472,6 +482,21 @@ mod tests {
|
||||
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 契约测试(Pattern 1 表,纯/非纯族代表 opcode)──
|
||||
|
||||
#[test]
|
||||
@@ -504,6 +529,8 @@ mod tests {
|
||||
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 一律有副作用(Pitfall 6:顶层变量赋值全局可观察,不删)
|
||||
@@ -553,6 +580,9 @@ mod tests {
|
||||
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 读陈旧值,Pitfall 7)
|
||||
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));
|
||||
}
|
||||
|
||||
@@ -148,6 +148,24 @@ impl Inst {
|
||||
}
|
||||
}
|
||||
|
||||
// ── RegAlloc 辅助指令(D-06/D-07)──
|
||||
|
||||
/// 寄存器复制 rd = a(D-07,RegAlloc 区间拆分搬值)。
|
||||
/// 不可用 LOAD_VAR/STORE_VAR 组合模拟——引入变量绑定语义会误触 const guard。
|
||||
pub fn inst_mov(dst: Operand, src: Operand) -> Self {
|
||||
Self::with_ext(OpCode::MOV, dst, src, Operand::None, &[])
|
||||
}
|
||||
|
||||
/// 溢出:regs[rd] → spill_stack[frame_base + slot],ext=[slot u16](D-06)。
|
||||
pub fn inst_spill(src: Operand, slot: u16) -> Self {
|
||||
Self::with_ext(OpCode::SPILL, src, Operand::None, Operand::None, &[slot as u32])
|
||||
}
|
||||
|
||||
/// 恢复:spill_stack[frame_base + slot] → regs[rd],ext=[slot u16](D-06)。
|
||||
pub fn inst_unspill(dst: Operand, slot: u16) -> Self {
|
||||
Self::with_ext(OpCode::UNSPILL, dst, Operand::None, Operand::None, &[slot as u32])
|
||||
}
|
||||
|
||||
// ── 无 ext:立即数/索引指令(拆字是 lowering 职责)──
|
||||
|
||||
/// 加载常量池常量:`dst = constants[idx]`。a 槽 Const 下标由 lowering 拆字。
|
||||
@@ -273,6 +291,30 @@ mod tests {
|
||||
assert_eq!(rest.ext.as_slice(), &[7]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn regalloc_constructors_carry_expected_slots() {
|
||||
let mov = Inst::inst_mov(Operand::Reg(3), Operand::Reg(7));
|
||||
assert_eq!(mov.op, OpCode::MOV);
|
||||
assert_eq!(mov.rd, Operand::Reg(3));
|
||||
assert_eq!(mov.a, Operand::Reg(7));
|
||||
assert_eq!(mov.b, Operand::None);
|
||||
assert!(mov.ext.is_empty());
|
||||
|
||||
let spill = Inst::inst_spill(Operand::Reg(5), 42);
|
||||
assert_eq!(spill.op, OpCode::SPILL);
|
||||
assert_eq!(spill.rd, Operand::Reg(5));
|
||||
assert_eq!(spill.a, Operand::None);
|
||||
assert_eq!(spill.b, Operand::None);
|
||||
assert_eq!(spill.ext.as_slice(), &[42]);
|
||||
|
||||
let unspill = Inst::inst_unspill(Operand::Reg(9), 0xFFFF);
|
||||
assert_eq!(unspill.op, OpCode::UNSPILL);
|
||||
assert_eq!(unspill.rd, Operand::Reg(9));
|
||||
assert_eq!(unspill.a, Operand::None);
|
||||
assert_eq!(unspill.b, Operand::None);
|
||||
assert_eq!(unspill.ext.as_slice(), &[0xFFFF]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn load_const_and_create_closure_keep_semantic_operands() {
|
||||
let lc = Inst::load_const(Operand::Reg(4), 300);
|
||||
|
||||
Reference in New Issue
Block a user