test: vreg 超 253 高复用复现样例 + n_registers 汇总断言

This commit is contained in:
2026-08-06 19:33:16 +08:00
parent 50b843a72a
commit c47a0a73d1
+112
View File
@@ -0,0 +1,112 @@
//! B005 复现 + n_registers 汇总断言(REG-04 直接验收)。
//!
//! 低层管线直调(不经 Compiler——oxide_regalloc dev-dep oxide_compiler 会构成依赖环):
//! parse → emit → dce(保守)→(regalloc on/off)→ lower。on 分支按 RESEARCH 例 6:
//! 精确 DCE 后重跑 build_cfg + liveness 再 alloc(D-17 索引位移修正);off 分支 = D-18
//! 降级路径(vreg 原样当物理号 → lower Reg>253 抛 RangeError)。
use oxide_bytecode::module::CompiledModule;
fn compile_source(src: &str, regalloc: bool) -> Result<CompiledModule, String> {
let allocator = oxide_parser::Allocator::default();
let program = oxide_parser::parse(&allocator, src).expect("parse failed");
let mut ir = oxide_emit::Emitter::new().emit_program(&program).expect("emit failed");
oxide_dce::dce(&mut ir);
if regalloc {
let cfg = oxide_cfg::build_cfg(&ir);
let live = oxide_liveness::liveness(&ir, &cfg);
oxide_dce::dce_precise(&mut ir, &live);
let cfg2 = oxide_cfg::build_cfg(&ir);
let live2 = oxide_liveness::liveness(&ir, &cfg2);
oxide_regalloc::alloc(&mut ir, &live2)?;
}
oxide_ir::lower::lower(&ir)
}
/// B005 样例生成器:n 个逐语句独立 vreg(各值立即死)→ vreg 总数 ≈ 2n ≫ 253,
/// 但峰值活度极小 → RegAlloc 有充分复用空间。
fn gen_big(n: usize) -> String {
let mut src = String::from("function f() { ");
for i in 0..n {
src.push_str(&format!("let v{i} = {i}; v{i}; "));
}
src.push_str(&format!("return v{}; }} f();", n - 1));
src
}
/// 遍历模块树找最大 n_registers。
fn module_max_n_registers(m: &CompiledModule) -> u8 {
let mut max = m.n_registers;
for sub in &m.sub_modules {
max = max.max(module_max_n_registers(sub));
}
max
}
/// B005 前置复现:off 模式(降级路径)250 变量函数报 RangeError。
#[test]
fn b005_repro_errors_without_regalloc() {
let err = match compile_source(&gen_big(250), false) {
Err(e) => e,
Ok(_) => panic!("off 模式应报 RangeError"),
};
assert!(err.contains("RangeError"), "off 模式应报 RangeError: {err}");
}
/// B005 修复达成:on 模式 250 变量函数编译成功,模块树 n_registers ≤ 253。
#[test]
fn b005_compiles_with_regalloc() {
let module = compile_source(&gen_big(250), true).expect("on 模式应编译成功");
assert!(module_max_n_registers(&module) <= 253, "模块树 n_registers 应 ≤253");
}
/// 样例集 n_registers on ≤ off 且至少一个严格下降(证明寄存器复用,REG-04 核心)。
#[test]
fn n_registers_on_off_aggregate() {
let samples = [
// 12 变量顺序死链:on 个位 vs off 数十(复用空间大)
"function f() { let v0=0; v0; let v1=1; v1; let v2=2; v2; let v3=3; v3; let v4=4; v4; let v5=5; v5; let v6=6; v6; let v7=7; v7; let v8=8; v8; let v9=9; v9; let v10=10; v10; let v11=11; v11; return v11; } f();",
// for 循环累加
"function f(n) { var s = 0; for (var i = 0; i < n; i++) { s += i * i; } return s; } f(10);",
// 嵌套调用
"function f(a, b, c) { return a * 100 + b * 10 + c; } function g() { return f(1, 2, 3) + f(4, 5, 6); } g();",
// try/catch
"function f() { try { throw 1; } catch (e) { return e + 1; } } f();",
// 闭包捕获
"function f() { var x = 1; return function() { return x++; }; } var g = f(); g();",
// 解构
"function f() { var [a, b] = [1, 2]; var { c, d } = { c: 3, d: 4 }; return a + b + c + d; } f();",
// 箭头函数
"var f = (a, b) => a * b; f(6, 7);",
// switch+const(B011 回归)
"function f(x) { switch (x) { case 1: break; } const c = 5; return c; } f(1);",
// 数组字面量
"function f() { var a = [1, 2, 3, 4, 5]; return a[0] + a[4]; } f();",
];
let mut strict_decrease = false;
for (i, src) in samples.iter().enumerate() {
let on = compile_source(src, true).expect("on 应编译成功");
let on_max = module_max_n_registers(&on);
assert!(on_max <= 253, "样例 {i} on n_registers {on_max} 超 253");
if let Ok(off) = compile_source(src, false) {
let off_max = module_max_n_registers(&off);
assert!(
on_max <= off_max,
"样例 {i} on({on_max}) > off({off_max})——RegAlloc 不应增寄存器窗口"
);
if on_max < off_max {
strict_decrease = true;
}
}
}
assert!(strict_decrease, "至少一个样例应严格 on < off(复用证明)");
}
/// 确定性:同一源码 on 模式编译两次字节码逐字节相同(B010,structural_hash 缓存依赖)。
#[test]
fn regalloc_deterministic_output() {
let a = compile_source(&gen_big(50), true).expect("on 编译成功");
let b = compile_source(&gen_big(50), true).expect("on 编译成功");
assert_eq!(a.bytecode, b.bytecode, "两次编译字节码应逐字节相同");
assert_eq!(a.n_registers, b.n_registers);
}