feat: 指令改写 + 元数据回写 + alloc 入口,RegAlloc on/off 运行时等价(D-16/D-20)

This commit is contained in:
2026-08-06 17:44:31 +08:00
parent 0b0d36fa85
commit 08f061dfd3
6 changed files with 974 additions and 5 deletions
+104 -4
View File
@@ -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 已回写");
}
}