mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-24 10:41:11 +08:00
feat: 指令改写 + 元数据回写 + alloc 入口,RegAlloc on/off 运行时等价(D-16/D-20)
This commit is contained in:
@@ -1,13 +1,16 @@
|
||||
//! RegAlloc 改写 pass(前半):干涉图染色决策,产出 AllocMap。
|
||||
//! RegAlloc 改写 pass:干涉图染色决策(前半)+ 指令改写/元数据回写(后半)。
|
||||
//!
|
||||
//! 消费 `oxide_liveness::LiveInfo`(inst_live_before 逐指令活集,05-04 交付)与
|
||||
//! `oxide_ir::IRFunction`(param_layout 参数段 + nested escaped 收集),产出 AllocMap
|
||||
//! 分析视图(D-01:pass 输出不住进 IR)。染色:干涉图(graph.rs)+ Kemp 简化/贪心/
|
||||
//! spill 区间拆分(color.rs)。**本模块不改写指令**——05-08 rewrite/finish 消费 AllocMap。
|
||||
//! `oxide_ir::IRFunction`(param_layout 参数段 + nested escaped 收集)。染色:干涉图
|
||||
//! (graph.rs)+ Kemp 简化/贪心/spill 区间拆分(color.rs)→ AllocMap;改写:rewrite.rs
|
||||
//! (指令槽 vreg→phys 重写 + spill 插入 + 参数连续性 MOV + label 重建)+ finish.rs
|
||||
//! (元数据回写)。`alloc()` 是完整改写 pass 入口(D-16),`color()` 只产决策。
|
||||
|
||||
mod alloc_map;
|
||||
mod color;
|
||||
mod finish;
|
||||
mod graph;
|
||||
mod rewrite;
|
||||
|
||||
pub use alloc_map::{Alloc, AllocMap, FreshKind, FreshVreg, SpillPlan};
|
||||
|
||||
@@ -24,3 +27,100 @@ pub fn color(f: &IRFunction, live: &LiveInfo) -> Result<AllocMap, String> {
|
||||
let graph = graph::build(f, live, &std::collections::BTreeSet::new(), &[]);
|
||||
color::run(f, live, &graph)
|
||||
}
|
||||
|
||||
/// 寄存器分配改写:`&mut IRFunction + &LiveInfo → Result<(), String>`(D-16)。
|
||||
///
|
||||
/// 流程:LiveInfo 维度守卫(inst 数不符 → 内部重跑 build_cfg + liveness,D-17 修正:
|
||||
/// 精确 DCE 删指令后 LiveInfo 过期,重跑纯函数一次调用,不重复建分析引擎)→
|
||||
/// `color()`(Err 原样传播)→ `rewrite::run` → `finish::run` → nested 递归自顶向下
|
||||
/// (每子函数独立 vreg 空间独立 alloc,D-03/D-10)。
|
||||
pub fn alloc(f: &mut IRFunction, live: &LiveInfo) -> Result<(), String> {
|
||||
if f.insts.is_empty() && f.nested.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
// LiveInfo 维度守卫:删改后的过期 LiveInfo 索引错位会误删活指令
|
||||
let live = if live.inst_live_before.len() == f.insts.len() && !f.insts.is_empty() {
|
||||
live.clone()
|
||||
} else {
|
||||
let cfg = oxide_cfg::build_cfg(f);
|
||||
oxide_liveness::liveness(f, &cfg)
|
||||
};
|
||||
let map = color(f, &live)?;
|
||||
rewrite::run(f, &map);
|
||||
finish::run(f, &map);
|
||||
for child in &mut f.nested {
|
||||
let cfg = oxide_cfg::build_cfg(child);
|
||||
let l = oxide_liveness::liveness(child, &cfg);
|
||||
alloc(child, &l)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use oxide_bytecode::opcode::OpCode;
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_ir::operand::Operand;
|
||||
|
||||
#[test]
|
||||
fn alloc_empty_ok() {
|
||||
let mut f = IRFunction::new();
|
||||
assert!(alloc(&mut f, &LiveInfo::new()).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn alloc_stale_live_reruns() {
|
||||
let mut f = IRFunction::new();
|
||||
f.insts.push(Inst::new(OpCode::ADD, Operand::Reg(3), Operand::Reg(1), Operand::Reg(2)));
|
||||
f.insts.push(Inst::new(OpCode::RETURN, Operand::Reg(3), Operand::None, Operand::None));
|
||||
// 空 LiveInfo(维度不符)→ 内部重跑
|
||||
let stale = LiveInfo::new();
|
||||
assert!(alloc(&mut f, &stale).is_ok());
|
||||
// 重写后全部槽 ≤253
|
||||
for inst in &f.insts {
|
||||
for o in [&inst.rd, &inst.a, &inst.b] {
|
||||
if let Operand::Reg(r) = o {
|
||||
assert!(*r <= 253, "重写后槽 {r} 超 253");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn alloc_err_propagates() {
|
||||
// 255 参数 CALL:窗口吞并 → k=0 → 无可行染色 → Err
|
||||
let mut f = IRFunction::new();
|
||||
f.insts.push(Inst::call(Operand::Reg(1), Operand::Reg(2), Operand::Reg(3000), 255));
|
||||
f.insts.push(Inst::new(OpCode::RETURN, Operand::Reg(5), Operand::None, Operand::None));
|
||||
let cfg = oxide_cfg::build_cfg(&f);
|
||||
let live = oxide_liveness::liveness(&f, &cfg);
|
||||
let err = alloc(&mut f, &live).unwrap_err();
|
||||
assert!(err.contains("too many registers"), "Err: {err}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn alloc_recurses_nested() {
|
||||
let mut f = IRFunction::new();
|
||||
f.insts.push(Inst::load_const(Operand::Reg(1), 0));
|
||||
f.insts.push(Inst::new(OpCode::RETURN, Operand::Reg(1), Operand::None, Operand::None));
|
||||
let mut sub = IRFunction::new();
|
||||
sub.insts.push(Inst::load_const(Operand::Reg(3), 0));
|
||||
sub.insts.push(Inst::new(OpCode::RETURN, Operand::Reg(3), Operand::None, Operand::None));
|
||||
f.nested.push(sub);
|
||||
let cfg = oxide_cfg::build_cfg(&f);
|
||||
let live = oxide_liveness::liveness(&f, &cfg);
|
||||
alloc(&mut f, &live).unwrap();
|
||||
// 父与子函数全部槽 ≤253
|
||||
for inst in &f.insts {
|
||||
for o in [&inst.rd, &inst.a, &inst.b] {
|
||||
if let Operand::Reg(r) = o {
|
||||
assert!(*r <= 253);
|
||||
}
|
||||
}
|
||||
}
|
||||
assert!(!f.nested[0].insts.is_empty(), "子函数也被重写");
|
||||
assert!(f.nested[0].n_registers <= 253, "子函数 n_registers 已回写");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user