mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-23 18:21:13 +08:00
103 lines
3.1 KiB
Rust
103 lines
3.1 KiB
Rust
//! 集成冒烟:真实编译产物(parse→emit→build_cfg→liveness→color)不崩 + 完整性/确定性断言。
|
|
|
|
use oxide_regalloc::{Alloc, AllocMap};
|
|
use std::collections::BTreeSet;
|
|
|
|
fn run_color(src: &str) -> (AllocMap, oxide_ir::IRFunction) {
|
|
let allocator = oxide_parser::Allocator::default();
|
|
let program = oxide_parser::parse(&allocator, src).expect("parse");
|
|
let ir = oxide_emit::Emitter::new().emit_program(&program).expect("emit");
|
|
let cfg = oxide_cfg::build_cfg(&ir);
|
|
let live = oxide_liveness::liveness(&ir, &cfg);
|
|
let m = oxide_regalloc::color(&ir, &live).expect("color");
|
|
(m, ir)
|
|
}
|
|
|
|
/// 收集函数内全部字面真实 vreg(对照 contract def/use,排除 0/254/255 保留位)。
|
|
fn literal_regs(ir: &oxide_ir::IRFunction) -> BTreeSet<u32> {
|
|
let mut set = BTreeSet::new();
|
|
for inst in &ir.insts {
|
|
for o in [&inst.rd, &inst.a, &inst.b] {
|
|
if let oxide_ir::operand::Operand::Reg(r) = o {
|
|
if *r != 0 && *r != 254 && *r != 255 {
|
|
set.insert(*r);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
set
|
|
}
|
|
|
|
fn smoke(src: &str) {
|
|
let (m, ir) = run_color(src);
|
|
// 完整性:全部真实 vreg 有条目
|
|
for r in literal_regs(&ir) {
|
|
assert!(m.map.contains_key(&r), "vreg {r} 无分配");
|
|
}
|
|
// phys ≤ 253;非预着色色 < arg_window_base
|
|
for a in m.map.values() {
|
|
if let Alloc::Phys(c) = a {
|
|
assert!(*c <= 253, "Phys 色 {c} 超 253");
|
|
assert!(*c < m.arg_window_base || is_precolored(&ir, *c), "色 {c} 应在可分配集或为预着色");
|
|
}
|
|
}
|
|
// spill slot 不重复
|
|
let mut slots = BTreeSet::new();
|
|
for s in &m.spills {
|
|
assert!(slots.insert(s.slot), "spill slot {} 重复", s.slot);
|
|
}
|
|
assert!(m.phys_peak <= 254, "phys_peak {} 超 254", m.phys_peak);
|
|
}
|
|
|
|
fn is_precolored(ir: &oxide_ir::IRFunction, c: u32) -> bool {
|
|
let pl = ir.param_layout;
|
|
if c >= pl.base && c < pl.base + pl.count {
|
|
return true;
|
|
}
|
|
// escaped 预着色(简化:允许参数段色)
|
|
false
|
|
}
|
|
|
|
#[test]
|
|
fn smoke_simple_function() {
|
|
smoke("function f(a, b) { return a + b; } f(1, 2)");
|
|
}
|
|
|
|
#[test]
|
|
fn smoke_if_else() {
|
|
smoke("function f(x) { if (x > 0) { return 1; } else { return 2; } } f(5)");
|
|
}
|
|
|
|
#[test]
|
|
fn smoke_loop() {
|
|
smoke("function f(n) { var s = 0; for (var i = 0; i < n; i++) { s = s + i; } return s; } f(10)");
|
|
}
|
|
|
|
#[test]
|
|
fn smoke_try_catch() {
|
|
smoke("function x() {} function y(e) {} function f() { try { x(); } catch (e) { y(e); } } f()");
|
|
}
|
|
|
|
#[test]
|
|
fn smoke_nested_calls() {
|
|
smoke("function g(x) { return x + 1; } function f(x) { return g(x) + g(x + 1); } f(1)");
|
|
}
|
|
|
|
#[test]
|
|
fn smoke_class_field() {
|
|
smoke("var outerVar = 42; class C { p = outerVar; } new C()");
|
|
}
|
|
|
|
#[test]
|
|
fn smoke_destructuring() {
|
|
smoke("function f() { var { a, b } = { a: 1, b: 2 }; return a + b; } f()");
|
|
}
|
|
|
|
#[test]
|
|
fn color_is_deterministic_on_real_ir() {
|
|
let src = "function f(n) { var s = 0; for (var i = 0; i < n; i++) { s = s + i; } return s; } f(10)";
|
|
let (a, _) = run_color(src);
|
|
let (b, _) = run_color(src);
|
|
assert_eq!(a, b);
|
|
}
|