From 67c6d3a53ad7b26d2bf2d2e919e8b857b3724865 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=88=B1=E7=89=B9=E6=AF=8F=E7=BE=8A=E9=98=9F?= Date: Tue, 11 Aug 2026 17:17:30 +0800 Subject: [PATCH] fix: preserve high template expression registers --- crates/oxide_ir/src/contract.rs | 10 +++---- crates/oxide_regalloc/src/rewrite.rs | 18 ++++++++++--- crates/oxide_vm/src/vm_dispatch_misc.rs | 4 +-- crates/oxide_vm/tests/template_tests.rs | 35 +++++++++++++++++++++++++ 4 files changed, 57 insertions(+), 10 deletions(-) diff --git a/crates/oxide_ir/src/contract.rs b/crates/oxide_ir/src/contract.rs index fdd6d0b..f6dbb1a 100644 --- a/crates/oxide_ir/src/contract.rs +++ b/crates/oxide_ir/src/contract.rs @@ -37,7 +37,7 @@ impl Inst { /// # 边界与前提 /// - `Range`:nargs=ext[0],从指定槽起连续 nargs 个寄存器。 /// - `SpreadArgs`:ext[1..] 每个字 `& 0x7FFF_FFFF`。 - /// - `TemplateExprs`:ext[1..] 中 `seg>>31==1` 的低 8 位。 + /// - `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]> { @@ -58,7 +58,7 @@ impl Inst { SlotSpec::TemplateExprs => { for seg in self.ext.iter().skip(1) { if seg >> 31 == 1 { - uses.push(seg & 0xFF); + uses.push(seg & 0x7FFF_FFFF); } } } @@ -260,9 +260,9 @@ mod tests { #[test] fn template_str_parses_expr_regs_from_ext() { - // ext[0] 跳过;后续 seg>>31==1 则低 8 位是 expr_reg(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]); + // 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]); diff --git a/crates/oxide_regalloc/src/rewrite.rs b/crates/oxide_regalloc/src/rewrite.rs index 01a2c5b..769cf2e 100644 --- a/crates/oxide_regalloc/src/rewrite.rs +++ b/crates/oxide_regalloc/src/rewrite.rs @@ -197,7 +197,7 @@ fn rewrite_inst( let rd = rewrite(inst.rd); let mut a = rewrite(inst.a); let mut b = rewrite(inst.b); - // TEMPLATE_STR 的 ext 编码表达式寄存器(seg>>31==1 时低 8 位为 expr_reg)——必须随 + // TEMPLATE_STR 的 ext 编码表达式寄存器(seg>>31==1 时低 31 位为 expr_reg)——必须随 // RegAlloc 重映射,否则读旧 vreg 号对应的物理槽(错值)。spread 调用系 ext[1..] 每个 // 字是完整 spread 源 vreg,同样需重映射到物理号。 let ext = match inst.op { @@ -205,9 +205,9 @@ fn rewrite_inst( let mut ext = inst.ext.clone(); for seg in ext.iter_mut().skip(1) { if *seg >> 31 == 1 { - let r = *seg & 0xFF; + let r = *seg & 0x7FFF_FFFF; let nr = remap_ext_reg(r, slot_color, map); - *seg = (*seg & !0xFFu32) | (nr & 0xFF); + *seg = 0x8000_0000 | nr; } } ext @@ -445,6 +445,18 @@ mod tests { assert_eq!(f.insts[1].rd, Operand::Reg(12)); } + #[test] + fn template_expr_ext_rewrites_full_vreg() { + let inst = Inst::template_str(Operand::Reg(1), 3, 0, &[0, 0x8000_0000 | 300, 1]); + let mut map = AllocMap::new(); + map.map.insert(1, Alloc::Phys(2)); + map.map.insert(300, Alloc::Phys(200)); + let f = rewrite_with_map(vec![inst], map); + + assert_eq!(f.insts[0].rd, Operand::Reg(2)); + assert_eq!(f.insts[0].ext.as_slice(), &[3 << 16, 0, 0x8000_0000 | 200, 1]); + } + #[test] fn scattered_args_insert_mov_bridge() { let insts = vec![Inst::call(Operand::Reg(2), Operand::Reg(3), Operand::Reg(4), 3)]; diff --git a/crates/oxide_vm/src/vm_dispatch_misc.rs b/crates/oxide_vm/src/vm_dispatch_misc.rs index 4085f63..58a0efa 100644 --- a/crates/oxide_vm/src/vm_dispatch_misc.rs +++ b/crates/oxide_vm/src/vm_dispatch_misc.rs @@ -199,8 +199,8 @@ impl Vm { let seg = self.bytecode[self.pc]; self.pc += 1; if (seg >> 31) == 1 { - let reg = (seg & 0x7F) as u8; - let val = self.regs[reg as usize]; + let reg = (seg & 0x7FFF_FFFF) as usize; + let val = self.regs[reg]; let s = if val.is_string() { // SAFETY: val 是字符串值。 unsafe { (*val.as_string_ptr()).data.clone() } diff --git a/crates/oxide_vm/tests/template_tests.rs b/crates/oxide_vm/tests/template_tests.rs index c19700a..4355a7c 100644 --- a/crates/oxide_vm/tests/template_tests.rs +++ b/crates/oxide_vm/tests/template_tests.rs @@ -1,4 +1,9 @@ +use oxide_bytecode::module::Constant; +use oxide_bytecode::opcode::OpCode; use oxide_compiler::compiler::Compiler; +use oxide_ir::inst::Inst; +use oxide_ir::operand::Operand; +use oxide_ir::IRFunction; use oxide_parser::Allocator; use oxide_types::value::JsValue; use oxide_vm::vm::Vm; @@ -87,6 +92,36 @@ fn template_numeric_expression() { assert_eq!(to_str(&vm, result.unwrap()), "2"); } +#[test] +fn template_expression_reads_physical_register_above_127() { + let mut ir = IRFunction::new(); + ir.constants = vec![Constant::String("value".to_string()), Constant::String(String::new())]; + ir.n_registers = 202; + ir.insts = vec![ + Inst::load_const(Operand::Reg(200), 0), + Inst::template_str(Operand::Reg(201), 3, 0, &[1, 0x8000_0000 | 200, 1]), + Inst::inst_mov(Operand::Reg(0), Operand::Reg(201)), + Inst::new(OpCode::HALT, Operand::None, Operand::None, Operand::None), + ]; + + let module = oxide_ir::lower::lower(&ir).expect("lower template IR"); + let mut vm = Vm::new(); + let result = vm.run(&module).expect("run template IR"); + assert_eq!(to_str(&vm, result), "value"); +} + +#[test] +fn template_expression_survives_high_vreg_regalloc() { + let mut source = String::from("function f() {"); + for i in 0..180 { + source.push_str(&format!("let v{i}={i};")); + } + source.push_str("let value='unit'; return `${value}s`; } f();"); + + let (vm, result) = eval_val(&source); + assert_eq!(to_str(&vm, result.unwrap()), "units"); +} + // ── 模板标签调用测试(基础)── #[test]