fix: preserve high template expression registers

This commit is contained in:
爱特每羊队
2026-08-11 17:17:30 +08:00
parent e049a8df5d
commit 67c6d3a53a
4 changed files with 57 additions and 10 deletions
+5 -5
View File
@@ -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]);
+15 -3
View File
@@ -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)];
+2 -2
View File
@@ -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() }
+35
View File
@@ -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]