docs: 清理寄存器分配注释并重命名样例测试

This commit is contained in:
2026-09-17 16:05:45 +00:00
parent 0b33f4253a
commit d81777e8b2
4 changed files with 9 additions and 8 deletions
@@ -0,0 +1,125 @@
//! 大函数寄存器收缩复现 + n_registers 汇总断言。
//!
//! 低层管线直调(不经 Compiler——oxide_regalloc dev-dep oxide_compiler 会构成依赖环):
//! parse → emit → dce(保守)→(regalloc on/off)→ lower。on 分支按编译管线:
//! 精确 DCE 后重跑 build_cfg + liveness 再 alloc(删指令后下标位移,重建避免错位);
//! off 分支为降级路径(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, false, false)
.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)
}
/// 大函数样例生成器: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
}
/// 前置复现:off 模式(降级路径)250 变量函数报 RangeError。
#[test]
fn large_function_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}");
}
/// 大函数晚引用全局(builtin 槽号 >253):builtin 槽须重映射到低号物理色,不得以
/// 恒等号 >253 编码(RegAlloc 后 lower 的 Reg>253 兜底检查报 RangeError,见
/// temporalHelpers.js + 测试文件拼接场景)。`late` 首次引用在函数尾部,槽号必 >253。
#[test]
fn late_builtin_slot_beyond_253_compiles() {
let mut src = String::new();
for i in 0..160 {
src.push_str(&format!("var v{i} = {i}; v{i}; "));
}
src.push_str("var result = late;");
let module = compile_source(&src, true).expect("on 模式应编译成功");
assert!(module_max_n_registers(&module) <= 253, "模块树 n_registers 应 ≤253");
}
/// 修复达成:on 模式 250 变量函数编译成功,模块树 n_registers ≤ 253。
#[test]
fn large_function_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 且至少一个严格下降(证明寄存器复用)。
#[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(const guard 回归)
"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 模式编译两次字节码逐字节相同(确定性排序,缓存 key 依赖)。
#[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);
}