docs: 补充注释与格式化
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
//! AllocMap:寄存器染色决策输出视图(D-01:pass 输出,不住进 IR)。
|
||||
//! AllocMap:寄存器染色决策输出视图(pass 输出,不住进 IR)。
|
||||
//!
|
||||
//! `AllocMap.map` 含全部真实 + fresh vreg 的 Phys/Spill 去向;`spills` 为
|
||||
//! SPILL/UNSPILL 插入决策表(05-08 rewrite 消费);`phys_peak` 回写 n_registers;
|
||||
//! `arg_window_base` 供调用点参数连续性 MOV 补位。BTreeMap 保确定性(B010,禁 HashMap)。
|
||||
//! SPILL/UNSPILL 插入决策表(rewrite 消费);`phys_peak` 回写 n_registers;
|
||||
//! `arg_window_base` 供调用点参数连续性 MOV 补位。BTreeMap 保确定性。
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
@@ -31,7 +31,7 @@ pub struct FreshVreg {
|
||||
}
|
||||
|
||||
/// 溢出决策表:被 spill 的 vreg 的 def/use 点 + fresh vreg id 对照。
|
||||
/// defs/uses 两 Vec 按下标升序。05-08 rewrite 消费此结构插 SPILL/UNSPILL。
|
||||
/// defs/uses 两 Vec 按下标升序,rewrite 消费此结构插 SPILL/UNSPILL。
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct SpillPlan {
|
||||
pub vreg: u32,
|
||||
@@ -47,9 +47,9 @@ pub struct AllocMap {
|
||||
pub map: BTreeMap<u32, Alloc>,
|
||||
/// spill 决策表(按 vreg 升序)
|
||||
pub spills: Vec<SpillPlan>,
|
||||
/// 物理峰值(05-08 回写 n_registers,≤253)
|
||||
/// 物理峰值(finish 回写 n_registers,≤253)
|
||||
pub phys_peak: u32,
|
||||
/// 参数窗口基址(05-08 调用点 MOV 补位,max_nargs=0 时 = 254 无窗口)
|
||||
/// 参数窗口基址(调用点 MOV 补位,max_nargs=0 时 = 254 无窗口)
|
||||
pub arg_window_base: u32,
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
//! 染色:Kemp 简化 + 贪心选色 + spill 区间拆分循环 → AllocMap(D-09/D-05/D-02)。
|
||||
//! 染色:Kemp 简化 + 贪心选色 + spill 区间拆分循环 → AllocMap。
|
||||
//!
|
||||
//! 主循环:build 干涉图 → Kemp 简化(degree < k 入栈)→ 卡住选 spill 候选 → 被 spill 的
|
||||
//! vreg 按 def/use 点拆成单点活度 fresh vreg → 重建图重试(外循环固定点)→ 收敛后贪心
|
||||
//! 选色。fresh 染色失败 = 单点活度 ≥ k = 无可行染色 → Err(D-02 RangeError 路径)。
|
||||
//! 选色。fresh 染色失败 = 单点活度 ≥ k = 无可行染色 → Err(RangeError 路径,消息与
|
||||
//! lower 逐字一致)。
|
||||
//!
|
||||
//! 确定性:BTreeMap/BTreeSet + Vec 排序,禁 HashMap(B010)。
|
||||
//! 确定性:BTreeMap/BTreeSet + Vec 排序,禁 HashMap。
|
||||
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
|
||||
@@ -32,28 +33,35 @@ pub(super) fn run(f: &IRFunction, live: &LiveInfo) -> Result<AllocMap, String> {
|
||||
}
|
||||
for v in failed {
|
||||
if fresh.iter().any(|fr| fr.id == v) {
|
||||
// 单点活度 ≥ k:无可行染色(D-02 错误路径,消息与 lower 逐字一致)
|
||||
// 单点活度 ≥ k:无可行染色(消息与 lower 逐字一致)
|
||||
return Err("RangeError: function body uses too many registers (max 253)".into());
|
||||
}
|
||||
// 真实 vreg 溢出(D-05 区间拆分 v1)
|
||||
// 真实 vreg 溢出:按 def/use 点拆成单点活度 fresh,重建图重试
|
||||
spill_set.insert(v);
|
||||
debug_assert!(u32::from(next_slot) <= u16::MAX as u32, "spill slot 超 u16 上限");
|
||||
slot_for_vreg.insert(v, next_slot);
|
||||
next_slot += 1;
|
||||
for d in def_points(f, v) {
|
||||
fresh.push(FreshVreg { id: next_fresh_id, at: d, kind: FreshKind::Def, owner: v });
|
||||
fresh.push(FreshVreg {
|
||||
id: next_fresh_id,
|
||||
at: d,
|
||||
kind: FreshKind::Def,
|
||||
owner: v,
|
||||
});
|
||||
next_fresh_id += 1;
|
||||
}
|
||||
for u in use_points(f, v) {
|
||||
fresh.push(FreshVreg { id: next_fresh_id, at: u, kind: FreshKind::Use, owner: v });
|
||||
fresh.push(FreshVreg {
|
||||
id: next_fresh_id,
|
||||
at: u,
|
||||
kind: FreshKind::Use,
|
||||
owner: v,
|
||||
});
|
||||
next_fresh_id += 1;
|
||||
}
|
||||
}
|
||||
iters += 1;
|
||||
debug_assert!(
|
||||
iters < max_real as usize * 2 + fresh.len() + 8,
|
||||
"RegAlloc 染色疑似不收敛"
|
||||
);
|
||||
debug_assert!(iters < max_real as usize * 2 + fresh.len() + 8, "RegAlloc 染色疑似不收敛");
|
||||
};
|
||||
|
||||
assemble(f, colors, spill_set, fresh, slot_for_vreg, arg_window_base)
|
||||
@@ -183,7 +191,12 @@ fn assemble(
|
||||
.saturating_add(1)
|
||||
.min(254);
|
||||
|
||||
Ok(AllocMap { map, spills, phys_peak, arg_window_base })
|
||||
Ok(AllocMap {
|
||||
map,
|
||||
spills,
|
||||
phys_peak,
|
||||
arg_window_base,
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -271,10 +284,13 @@ mod tests {
|
||||
#[test]
|
||||
fn escaped_vreg_keeps_color_and_not_spilled() {
|
||||
let mut f = empty_function();
|
||||
f.insts.push(Inst::new(OpCode::ADD, Operand::Reg(5), Operand::Reg(3), Operand::Reg(4)));
|
||||
f.insts.push(Inst::new(OpCode::RETURN, Operand::Reg(5), Operand::None, Operand::None));
|
||||
f.insts
|
||||
.push(Inst::new(OpCode::ADD, Operand::Reg(5), Operand::Reg(3), Operand::Reg(4)));
|
||||
f.insts
|
||||
.push(Inst::new(OpCode::RETURN, Operand::Reg(5), Operand::None, Operand::None));
|
||||
let mut sub = empty_function();
|
||||
sub.insts.push(Inst::new(OpCode::LOAD_VAR, Operand::Reg(9), Operand::Reg(3), Operand::None));
|
||||
sub.insts
|
||||
.push(Inst::new(OpCode::LOAD_VAR, Operand::Reg(9), Operand::Reg(3), Operand::None));
|
||||
f.nested.push(sub);
|
||||
let cfg = oxide_cfg::build_cfg(&f);
|
||||
let live = oxide_liveness::liveness(&f, &cfg);
|
||||
@@ -311,11 +327,7 @@ mod tests {
|
||||
assert!(m.spills.iter().any(|s| s.vreg == 1000), "spill 的是 L");
|
||||
assert!(m.phys_peak <= 253, "phys_peak ≤ 253");
|
||||
// 全部 fresh 应有 Phys 分配
|
||||
let fresh_phys: Vec<&Alloc> = m
|
||||
.map
|
||||
.values()
|
||||
.filter(|a| matches!(a, Alloc::Phys(_)))
|
||||
.collect();
|
||||
let fresh_phys: Vec<&Alloc> = m.map.values().filter(|a| matches!(a, Alloc::Phys(_))).collect();
|
||||
assert!(!fresh_phys.is_empty(), "fresh vreg 应着色为 Phys");
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//! 元数据回写:n_registers / builtin_reg_map / param_layout / upvalue_captures(D-10/D-03)。
|
||||
//! 元数据回写:n_registers / builtin_reg_map / param_layout / upvalue_captures。
|
||||
//!
|
||||
//! - n_registers = map.phys_peak(≤253;不含参数窗口——窗口是调用点暂存槽,
|
||||
//! CALL handler 在 push 前收集 args 进 Vec,n_registers 只控 save_stack 窗口)
|
||||
@@ -39,23 +39,23 @@ pub(super) fn run(f: &mut IRFunction, map: &AllocMap) {
|
||||
}
|
||||
}
|
||||
|
||||
// (c) param_layout 不动(参数段 pre-colored 恒等;窗口与参数段重叠仅 05-07 标注的罕见情形)
|
||||
// (c) param_layout 不动(参数段预着色恒等;窗口与参数段重叠仅参数段贴 254 的罕见情形)
|
||||
let pl = f.param_layout;
|
||||
debug_assert!(pl.base + pl.count <= map.arg_window_base || map.arg_window_base == 254);
|
||||
|
||||
// (d) upvalue_captures 不动。enclosing_reg 是编译期产物(MAKE_CELL 定位用),VM 运行时
|
||||
// 不读(CREATE_CLOSURE 走 cell_idx,cell 捕获后值在 cell 中)——无需跨函数同步。
|
||||
// 直接槽引用(class field 计算键等)的 escaped vreg 已由 05-07 预着色恒等保留。
|
||||
// 注:cell 捕获变量的父 vreg 可被 RegAlloc 移动(MAKE_CELL 后值入 cell,register 无关),
|
||||
// 直接槽引用(class field 计算键等)的 escaped vreg 已由预着色恒等保留。
|
||||
// 注:cell 捕获变量的父 vreg 可被 RegAlloc 移动(MAKE_CELL 后值入 cell,与寄存器无关),
|
||||
// 此处不做恒等断言。
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use oxide_bytecode::opcode::OpCode;
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_ir::operand::Operand;
|
||||
use oxide_bytecode::opcode::OpCode;
|
||||
|
||||
#[test]
|
||||
fn n_registers_equals_phys_peak() {
|
||||
@@ -70,7 +70,8 @@ mod tests {
|
||||
fn builtin_moved_rewritten() {
|
||||
let mut f = IRFunction::new();
|
||||
f.builtin_reg_map = vec![("Math".to_string(), 5)];
|
||||
f.insts.push(Inst::new(OpCode::RETURN, Operand::Reg(1), Operand::None, Operand::None));
|
||||
f.insts
|
||||
.push(Inst::new(OpCode::RETURN, Operand::Reg(1), Operand::None, Operand::None));
|
||||
let mut map = AllocMap::new();
|
||||
map.map.insert(5, Alloc::Phys(9));
|
||||
map.map.insert(1, Alloc::Phys(1));
|
||||
@@ -82,7 +83,8 @@ mod tests {
|
||||
fn builtin_spilled_bound_to_free_color() {
|
||||
let mut f = IRFunction::new();
|
||||
f.builtin_reg_map = vec![("Math".to_string(), 5)];
|
||||
f.insts.push(Inst::new(OpCode::RETURN, Operand::Reg(1), Operand::None, Operand::None));
|
||||
f.insts
|
||||
.push(Inst::new(OpCode::RETURN, Operand::Reg(1), Operand::None, Operand::None));
|
||||
let mut map = AllocMap::new();
|
||||
map.map.insert(5, Alloc::Spill(0));
|
||||
map.map.insert(1, Alloc::Phys(1));
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
//! 干涉图构建 + 预着色 + 可分配色集(D-09/D-10/D-11 + RESEARCH 发现 4)。
|
||||
//! 干涉图构建 + 预着色 + 可分配色集。
|
||||
//!
|
||||
//! - 真实 vreg:字面 `Operand::Reg(r)`(rd/a/b)+ CALL 系参数连续区间 [first, first+nargs)
|
||||
//! - 干涉边:逐指令 `live_before[i]` 中同时 live 的真实 vreg 两两加边(fresh vreg 按 at==i 并入)
|
||||
//! - 预着色:参数段 [param_base, base+count) 钉死原号(VM 调用契约 D-10);escaped vreg
|
||||
//! (nested `LOAD_VAR.a`/`STORE_VAR.rd` 直引父槽)钉死原槽号且排除 spill(B012)
|
||||
//! - 预着色:参数段 [param_base, base+count) 钉死原号(VM 调用契约);escaped vreg
|
||||
//! (nested `LOAD_VAR.a`/`STORE_VAR.rd` 直引父槽)钉死原槽号且排除 spill
|
||||
//! - 可分配色集:`(1..=253) − 参数色 − escaped 色 − [arg_window_base, 253]`
|
||||
//! (arg_window_base = 254 − max_nargs,为 05-08 调用点 MOV 补位留空槽)
|
||||
//! (arg_window_base = 254 − max_nargs,为调用点参数连续性 MOV 补位留空槽)
|
||||
//!
|
||||
//! 确定性:全 BTreeMap + Vec 排序,禁 HashMap 迭代序(B010)。
|
||||
//! 确定性:全 BTreeMap + Vec 排序,禁 HashMap 迭代序。
|
||||
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
|
||||
@@ -36,10 +36,10 @@ pub(super) struct InterferenceGraph {
|
||||
|
||||
/// 收集全部真实 vreg(BTreeSet 保确定性):
|
||||
/// 字面 `Operand::Reg(r)`(rd/a/b 三槽)+ CALL/CALL_NATIVE/NEW_EXPRESSION b 槽参数区间
|
||||
/// + SUPER_CALL a 槽参数区间。0 无条件排除(reg 0 永非真实——emit alloc_reg 从 ≥1 起)。
|
||||
/// + SUPER_CALL a 槽参数区间。0 无条件排除(reg 0 永非真实——emit 分配寄存器从 ≥1 起)。
|
||||
///
|
||||
/// 254/255 的 over-approx 规则:live bitset 中位 254/255 无法区分 This/NewTarget 占用与
|
||||
/// 真实 vreg 活度,若函数存在该号真实 vreg(大函数 B005 场景)则按真实 vreg 建节点染色——
|
||||
/// 254/255 的过近似规则:live bitset 中位 254/255 无法区分 This/NewTarget 占用与
|
||||
/// 真实 vreg 活度,若函数存在该号真实 vreg(大函数场景)则按真实 vreg 建节点染色——
|
||||
/// 多出的边只造成过度约束(安全方向),缺失边才会错值。
|
||||
pub(super) fn collect_real_vregs(f: &IRFunction) -> BTreeSet<u32> {
|
||||
let mut real = BTreeSet::new();
|
||||
@@ -85,7 +85,7 @@ pub(super) fn build(
|
||||
|
||||
// ── 预着色 ──
|
||||
let mut pre_colors: BTreeMap<u32, u32> = BTreeMap::new();
|
||||
// 参数段:VM 调用写 regs[param_base+i],钉死不可动(D-10)
|
||||
// 参数段:VM 调用写 regs[param_base+i],钉死不可动(调用契约)
|
||||
let pl = f.param_layout;
|
||||
if pl.count > 0 {
|
||||
for i in 0..pl.count {
|
||||
@@ -93,12 +93,12 @@ pub(super) fn build(
|
||||
pre_colors.insert(v, v);
|
||||
}
|
||||
}
|
||||
// escaped:递归 nested 收集 LOAD_VAR.a / STORE_VAR.rd 直引的父槽(B012)
|
||||
// escaped:递归 nested 收集 LOAD_VAR.a / STORE_VAR.rd 直引的父槽
|
||||
let mut escaped_colors: Vec<u32> = Vec::new();
|
||||
collect_escaped(&f.nested, &mut pre_colors, &mut escaped_colors);
|
||||
// 对称缺口(B013 延伸):子模块自身引用的父槽也必须预着色恒等。父侧 collect_escaped
|
||||
// 只保证父不移动槽;但子模块 alloc 时,它引用父槽的 LOAD_VAR.a / STORE_VAR.rd 会被
|
||||
// 当作子模块自己的 vreg 参与染色而移走 → 子模块读错物理槽。分界线 = param_layout.base
|
||||
// 对称缺口:子模块自身引用的父槽也必须预着色恒等。父侧 collect_escaped 只保证父不
|
||||
// 移动槽;但子模块 alloc 时,它引用父槽的 LOAD_VAR.a / STORE_VAR.rd 会被当作子模块
|
||||
// 自己的 vreg 参与染色而移走 → 子模块读错物理槽。分界线 = param_layout.base
|
||||
// (emit 的 inherited_reg_start 继承机制:子模块 vreg ≥ base,父槽引用 < base)。
|
||||
collect_own_escaped(f, &mut pre_colors, &mut escaped_colors);
|
||||
|
||||
@@ -106,12 +106,7 @@ pub(super) fn build(
|
||||
let max_nargs = f
|
||||
.insts
|
||||
.iter()
|
||||
.filter(|i| {
|
||||
matches!(
|
||||
i.op,
|
||||
OpCode::CALL | OpCode::CALL_NATIVE | OpCode::NEW_EXPRESSION | OpCode::SUPER_CALL
|
||||
)
|
||||
})
|
||||
.filter(|i| matches!(i.op, OpCode::CALL | OpCode::CALL_NATIVE | OpCode::NEW_EXPRESSION | OpCode::SUPER_CALL))
|
||||
.map(|i| i.ext.first().copied().unwrap_or(0))
|
||||
.max()
|
||||
.unwrap_or(0);
|
||||
@@ -131,12 +126,18 @@ pub(super) fn build(
|
||||
if !spill_set.contains(&v) {
|
||||
nodes.insert(
|
||||
v,
|
||||
Node { pre_color: pre_colors.get(&v).copied(), adj: Vec::new() },
|
||||
Node {
|
||||
pre_color: pre_colors.get(&v).copied(),
|
||||
adj: Vec::new(),
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
for fr in fresh {
|
||||
nodes.entry(fr.id).or_insert(Node { pre_color: None, adj: Vec::new() });
|
||||
nodes.entry(fr.id).or_insert(Node {
|
||||
pre_color: None,
|
||||
adj: Vec::new(),
|
||||
});
|
||||
}
|
||||
// 邻接表(BTreeMap<u32, BTreeSet<u32>> 去重后转 Vec 排序)
|
||||
let mut adj_sets: BTreeMap<u32, BTreeSet<u32>> = BTreeMap::new();
|
||||
@@ -171,11 +172,16 @@ pub(super) fn build(
|
||||
}
|
||||
}
|
||||
|
||||
InterferenceGraph { nodes, allocatable, k, arg_window_base }
|
||||
InterferenceGraph {
|
||||
nodes,
|
||||
allocatable,
|
||||
k,
|
||||
arg_window_base,
|
||||
}
|
||||
}
|
||||
|
||||
/// 递归收集 nested 树中变量槽引用(LOAD_VAR.a 读槽、STORE_VAR.rd 写槽),
|
||||
/// 注入 pre_colors 并记录 escaped 色列表(可分配集排除 + spill 候选排除,B012)。
|
||||
/// 注入 pre_colors 并记录 escaped 色列表(可分配集排除 + spill 候选排除)。
|
||||
fn collect_escaped(nested: &[IRFunction], pre_colors: &mut BTreeMap<u32, u32>, out: &mut Vec<u32>) {
|
||||
for sub in nested {
|
||||
for inst in &sub.insts {
|
||||
@@ -196,7 +202,7 @@ fn collect_escaped(nested: &[IRFunction], pre_colors: &mut BTreeMap<u32, u32>, o
|
||||
out.sort_unstable();
|
||||
}
|
||||
|
||||
/// 收集当前函数自身对父槽的直接引用(B013 延伸,父侧保护的对称缺口):
|
||||
/// 收集当前函数自身对父槽的直接引用(父侧保护的对称缺口):
|
||||
/// `LOAD_VAR.a` / `STORE_VAR.rd` 中槽号 < param_layout.base 的 vreg 是父槽引用
|
||||
/// (emit inherited_reg_start 分界:子函数自身 vreg 从 base 起分配,父槽引用 < base)。
|
||||
/// 父侧 collect_escaped 只保护父不移动槽;此处保证子函数 alloc 时这些引用不被染色移走。
|
||||
@@ -245,8 +251,10 @@ mod tests {
|
||||
let mut f = empty_function();
|
||||
f.insts.push(Inst::load_const(Operand::Reg(1), 0));
|
||||
f.insts.push(Inst::load_const(Operand::Reg(2), 0));
|
||||
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));
|
||||
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));
|
||||
let g = build_graph(&f);
|
||||
assert!(g.nodes[&1].adj.contains(&2), "r1 与 r2 应邻接");
|
||||
assert!(g.nodes[&2].adj.contains(&1));
|
||||
@@ -259,11 +267,14 @@ mod tests {
|
||||
let mut f = empty_function();
|
||||
f.insts.push(Inst::load_const(Operand::Reg(1), 0));
|
||||
f.insts.push(Inst::load_const(Operand::Reg(2), 0));
|
||||
f.insts.push(Inst::new(OpCode::ADD, Operand::Reg(3), Operand::Reg(1), Operand::Reg(2)));
|
||||
f.insts
|
||||
.push(Inst::new(OpCode::ADD, Operand::Reg(3), Operand::Reg(1), Operand::Reg(2)));
|
||||
f.insts.push(Inst::load_const(Operand::Reg(4), 0));
|
||||
f.insts.push(Inst::load_const(Operand::Reg(5), 0));
|
||||
f.insts.push(Inst::new(OpCode::ADD, Operand::Reg(6), Operand::Reg(4), Operand::Reg(5)));
|
||||
f.insts.push(Inst::new(OpCode::RETURN, Operand::Reg(6), Operand::None, Operand::None));
|
||||
f.insts
|
||||
.push(Inst::new(OpCode::ADD, Operand::Reg(6), Operand::Reg(4), Operand::Reg(5)));
|
||||
f.insts
|
||||
.push(Inst::new(OpCode::RETURN, Operand::Reg(6), Operand::None, Operand::None));
|
||||
let g = build_graph(&f);
|
||||
assert!(!g.nodes[&1].adj.contains(&4), "r1 与 r4 活度不相交,无边");
|
||||
}
|
||||
@@ -272,8 +283,10 @@ mod tests {
|
||||
fn param_segment_precolored() {
|
||||
let mut f = empty_function();
|
||||
f.param_layout = oxide_ir::ParamLayout { base: 1, count: 2 };
|
||||
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));
|
||||
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));
|
||||
let g = build_graph(&f);
|
||||
assert_eq!(g.nodes[&1].pre_color, Some(1));
|
||||
assert_eq!(g.nodes[&2].pre_color, Some(2));
|
||||
@@ -284,10 +297,13 @@ mod tests {
|
||||
#[test]
|
||||
fn escaped_slots_precolored() {
|
||||
let mut f = empty_function();
|
||||
f.insts.push(Inst::new(OpCode::ADD, Operand::Reg(5), Operand::Reg(3), Operand::Reg(4)));
|
||||
f.insts.push(Inst::new(OpCode::RETURN, Operand::Reg(5), Operand::None, Operand::None));
|
||||
f.insts
|
||||
.push(Inst::new(OpCode::ADD, Operand::Reg(5), Operand::Reg(3), Operand::Reg(4)));
|
||||
f.insts
|
||||
.push(Inst::new(OpCode::RETURN, Operand::Reg(5), Operand::None, Operand::None));
|
||||
let mut sub = empty_function();
|
||||
sub.insts.push(Inst::new(OpCode::LOAD_VAR, Operand::Reg(9), Operand::Reg(3), Operand::None));
|
||||
sub.insts
|
||||
.push(Inst::new(OpCode::LOAD_VAR, Operand::Reg(9), Operand::Reg(3), Operand::None));
|
||||
f.nested.push(sub);
|
||||
let g = build_graph(&f);
|
||||
assert_eq!(g.nodes[&3].pre_color, Some(3), "escaped 槽 r3 预着色");
|
||||
@@ -297,8 +313,10 @@ mod tests {
|
||||
#[test]
|
||||
fn reserved_colors_excluded() {
|
||||
let mut f = empty_function();
|
||||
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));
|
||||
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));
|
||||
let g = build_graph(&f);
|
||||
assert!(!g.allocatable.contains(&0), "色 0 排除(CALL 隐式 reg0)");
|
||||
assert!(!g.allocatable.contains(&254) && !g.allocatable.contains(&255), "254/255 排除");
|
||||
@@ -309,7 +327,8 @@ mod tests {
|
||||
fn arg_window_reserved() {
|
||||
let mut f = empty_function();
|
||||
f.insts.push(Inst::call(Operand::Reg(1), Operand::Reg(2), Operand::Reg(3), 3));
|
||||
f.insts.push(Inst::new(OpCode::RETURN, Operand::Reg(5), Operand::None, Operand::None));
|
||||
f.insts
|
||||
.push(Inst::new(OpCode::RETURN, Operand::Reg(5), Operand::None, Operand::None));
|
||||
let g = build_graph(&f);
|
||||
assert_eq!(g.arg_window_base, 251, "max_nargs=3 → base 251");
|
||||
assert!(!g.allocatable.contains(&251) && !g.allocatable.contains(&252) && !g.allocatable.contains(&253));
|
||||
@@ -317,10 +336,12 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn high_numbered_vreg_254_is_real_node() {
|
||||
// 大函数场景:真实 vreg 254 必须建节点(B005 正确性前提)
|
||||
// 大函数场景:真实 vreg 254 必须建节点(正确性前提)
|
||||
let mut f = empty_function();
|
||||
f.insts.push(Inst::new(OpCode::ADD, Operand::Reg(254), Operand::Reg(252), Operand::Reg(253)));
|
||||
f.insts.push(Inst::new(OpCode::RETURN, Operand::Reg(254), Operand::None, Operand::None));
|
||||
f.insts
|
||||
.push(Inst::new(OpCode::ADD, Operand::Reg(254), Operand::Reg(252), Operand::Reg(253)));
|
||||
f.insts
|
||||
.push(Inst::new(OpCode::RETURN, Operand::Reg(254), Operand::None, Operand::None));
|
||||
let real = collect_real_vregs(&f);
|
||||
assert!(real.contains(&254), "真实 vreg 254 必须被收集");
|
||||
let g = build_graph(&f);
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
//! RegAlloc 改写 pass:干涉图染色决策(前半)+ 指令改写/元数据回写(后半)。
|
||||
//!
|
||||
//! 消费 `oxide_liveness::LiveInfo`(inst_live_before 逐指令活集,05-04 交付)与
|
||||
//! 消费 `oxide_liveness::LiveInfo`(inst_live_before 逐指令活集)与
|
||||
//! `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()` 只产决策。
|
||||
//! (元数据回写)。`alloc()` 是完整改写 pass 入口,`color()` 只产决策。
|
||||
|
||||
mod alloc_map;
|
||||
mod color;
|
||||
@@ -18,8 +18,8 @@ use oxide_ir::IRFunction;
|
||||
use oxide_liveness::LiveInfo;
|
||||
|
||||
/// 染色:`&IRFunction + &LiveInfo → Result<AllocMap, String>`。纯函数,只读 IR。
|
||||
/// 空 IRFunction 退化:Ok(AllocMap::new())。Err = 无可行染色(D-02 RangeError 路径,
|
||||
/// 05-08 接线时转 lower 错误)。
|
||||
/// 空 IRFunction 退化:Ok(AllocMap::new())。Err = 无可行染色(RangeError 路径,
|
||||
/// 消息与 lower 逐字一致,编译接线时原样传播)。
|
||||
pub fn color(f: &IRFunction, live: &LiveInfo) -> Result<AllocMap, String> {
|
||||
if f.insts.is_empty() {
|
||||
return Ok(AllocMap::new());
|
||||
@@ -27,12 +27,17 @@ pub fn color(f: &IRFunction, live: &LiveInfo) -> Result<AllocMap, String> {
|
||||
color::run(f, live)
|
||||
}
|
||||
|
||||
/// 寄存器分配改写:`&mut IRFunction + &LiveInfo → Result<(), String>`(D-16)。
|
||||
/// 寄存器分配改写:`&mut IRFunction + &LiveInfo → Result<(), String>`。
|
||||
///
|
||||
/// 流程:LiveInfo 维度守卫(inst 数不符 → 内部重跑 build_cfg + liveness,D-17 修正:
|
||||
/// 精确 DCE 删指令后 LiveInfo 过期,重跑纯函数一次调用,不重复建分析引擎)→
|
||||
/// `color()`(Err 原样传播)→ `rewrite::run` → `finish::run` → nested 递归自顶向下
|
||||
/// (每子函数独立 vreg 空间独立 alloc,D-03/D-10)。
|
||||
/// # 步骤
|
||||
/// - LiveInfo 维度守卫:inst 数不符说明 LiveInfo 已过期(精确 DCE 删指令后下标位移),
|
||||
/// 内部重跑 build_cfg + liveness 重建,不重复建分析引擎
|
||||
/// - `color()` 产染色决策(Err 原样传播)
|
||||
/// - `rewrite::run` 指令改写 → `finish::run` 元数据回写
|
||||
/// - nested 递归自顶向下:每子函数独立 vreg 空间独立 alloc
|
||||
///
|
||||
/// # 注意事项
|
||||
/// - 嵌套子函数不继承父 LiveInfo,各自重跑 build_cfg + liveness。
|
||||
pub fn alloc(f: &mut IRFunction, live: &LiveInfo) -> Result<(), String> {
|
||||
if f.insts.is_empty() && f.nested.is_empty() {
|
||||
return Ok(());
|
||||
@@ -71,8 +76,10 @@ mod tests {
|
||||
#[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));
|
||||
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());
|
||||
@@ -88,10 +95,12 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn alloc_err_propagates() {
|
||||
// 255 参数 CALL:窗口吞并 → k=0 → 无可行染色 → Err
|
||||
// 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));
|
||||
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();
|
||||
@@ -102,10 +111,12 @@ mod tests {
|
||||
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));
|
||||
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));
|
||||
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);
|
||||
@@ -122,4 +133,3 @@ mod tests {
|
||||
assert!(f.nested[0].n_registers <= 253, "子函数 n_registers 已回写");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
//! 指令改写:消费 AllocMap 就地重写 IR(D-05/D-07/D-20/RESEARCH 发现 4)。
|
||||
//! 指令改写:消费 AllocMap 就地重写 IR。
|
||||
//!
|
||||
//! - 槽位 vreg→phys 重写:仅 `Operand::Reg`;None/This/NewTarget/Const/Imm/Label 零改动
|
||||
//! (None→0、This→254、NewTarget→255 由 lower 映射,D-11)
|
||||
//! - spill 落点(D-05 区间拆分 v1):def 点后 SPILL、use 点前 UNSPILL;RMW(COMPOUND 系
|
||||
//! rd 读旧值)UNSPILL 进 def-fresh 寄存器、use-fresh 弃用
|
||||
//! - 调用点参数连续性(RESEARCH 发现 4):参数分散则 MOV 搬进 ARG_WINDOW
|
||||
//! (None→0、This→254、NewTarget→255 由 lower 映射)
|
||||
//! - spill 落点:def 点后 SPILL、use 点前 UNSPILL;RMW(COMPOUND 系 rd 读旧值)UNSPILL
|
||||
//! 进 def-fresh 寄存器、use-fresh 弃用
|
||||
//! - 调用点参数连续性:参数分散则 MOV 搬进 ARG_WINDOW
|
||||
//! - spilled builtin 入口 SPILL(builtin 无指令 def,VM 入口隐式绑定)
|
||||
//! - label_pos mark-sweep 重建:label 落 group 起点(跳转先执行 before-插入)
|
||||
//!
|
||||
//! 确定性:全 BTreeMap/Vec 排序,禁 HashMap(B010)。
|
||||
//! 确定性:全 BTreeMap/Vec 排序,禁 HashMap。
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
@@ -189,7 +189,7 @@ fn rewrite_inst(
|
||||
}
|
||||
}
|
||||
}
|
||||
other => other, // None/This/NewTarget/Const/Imm/Label 零改动(D-11)
|
||||
other => other, // None/This/NewTarget/Const/Imm/Label 零改动,lower 映射语义号
|
||||
}
|
||||
};
|
||||
|
||||
@@ -269,7 +269,7 @@ pub(super) fn spilled_builtin_bindings(f: &IRFunction, map: &AllocMap) -> Vec<(S
|
||||
}
|
||||
// escaped 色(递归 nested 的 LOAD_VAR.a / STORE_VAR.rd)
|
||||
collect_escaped(&f.nested, &mut excluded);
|
||||
// own-escaped 色(B013 延伸):本函数 LOAD_VAR.a / STORE_VAR.rd 引用父槽(< base)。
|
||||
// own-escaped 色:本函数 LOAD_VAR.a / STORE_VAR.rd 引用父槽(< base)。
|
||||
// 与 graph.rs collect_own_escaped 对称——spill 自由色不得占用父槽号。
|
||||
collect_own_escaped(f, &mut excluded);
|
||||
// 窗口
|
||||
@@ -364,7 +364,8 @@ mod tests {
|
||||
#[test]
|
||||
fn semantic_operands_untouched() {
|
||||
let mut f = empty_function();
|
||||
f.insts.push(Inst::new(OpCode::ADD, Operand::This, Operand::NewTarget, Operand::None));
|
||||
f.insts
|
||||
.push(Inst::new(OpCode::ADD, Operand::This, Operand::NewTarget, Operand::None));
|
||||
f.insts.push(Inst::load_const(Operand::Reg(1), 3));
|
||||
let map = AllocMap::new();
|
||||
run(&mut f, &map);
|
||||
@@ -376,8 +377,10 @@ mod tests {
|
||||
#[test]
|
||||
fn slots_rewritten_per_map() {
|
||||
let mut f = empty_function();
|
||||
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));
|
||||
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));
|
||||
let mut map = AllocMap::new();
|
||||
map.map.insert(1, Alloc::Phys(10));
|
||||
map.map.insert(2, Alloc::Phys(11));
|
||||
@@ -484,7 +487,11 @@ mod tests {
|
||||
assert!(spill_pos < ret_pos, "SPILL 在 RETURN 前");
|
||||
// RETURN rd → use-fresh 8,其前有 UNSPILL(8)
|
||||
assert_eq!(f.insts[ret_pos].rd, Operand::Reg(8));
|
||||
let unspill8 = f.insts.iter().position(|i| i.op == OpCode::UNSPILL && i.rd == Operand::Reg(8)).unwrap();
|
||||
let unspill8 = f
|
||||
.insts
|
||||
.iter()
|
||||
.position(|i| i.op == OpCode::UNSPILL && i.rd == Operand::Reg(8))
|
||||
.unwrap();
|
||||
assert!(unspill8 < ret_pos, "RETURN 前 UNSPILL use-fresh");
|
||||
// use-fresh 6(RMW 点弃用)不得产生 UNSPILL
|
||||
assert!(
|
||||
@@ -533,7 +540,8 @@ mod tests {
|
||||
#[test]
|
||||
fn spilled_builtin_entry_spill_inserted() {
|
||||
let mut f = empty_function();
|
||||
f.insts.push(Inst::new(OpCode::RETURN, Operand::Reg(5), Operand::None, Operand::None));
|
||||
f.insts
|
||||
.push(Inst::new(OpCode::RETURN, Operand::Reg(5), Operand::None, Operand::None));
|
||||
f.builtin_reg_map = vec![("Math".to_string(), 10)];
|
||||
let mut map = AllocMap::new();
|
||||
map.map.insert(10, Alloc::Spill(0));
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
//! B005 复现 + n_registers 汇总断言(REG-04 直接验收)。
|
||||
//! 大函数寄存器收缩复现 + n_registers 汇总断言。
|
||||
//!
|
||||
//! 低层管线直调(不经 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)。
|
||||
//! parse → emit → dce(保守)→(regalloc on/off)→ lower。on 分支按编译管线:
|
||||
//! 精确 DCE 后重跑 build_cfg + liveness 再 alloc(删指令后下标位移,重建避免错位);
|
||||
//! off 分支为降级路径(vreg 原样当物理号 → lower Reg>253 抛 RangeError)。
|
||||
|
||||
use oxide_bytecode::module::CompiledModule;
|
||||
|
||||
@@ -23,7 +23,7 @@ fn compile_source(src: &str, regalloc: bool) -> Result<CompiledModule, String> {
|
||||
oxide_ir::lower::lower(&ir)
|
||||
}
|
||||
|
||||
/// B005 样例生成器:n 个逐语句独立 vreg(各值立即死)→ vreg 总数 ≈ 2n ≫ 253,
|
||||
/// 大函数样例生成器:n 个逐语句独立 vreg(各值立即死)→ vreg 总数 ≈ 2n ≫ 253,
|
||||
/// 但峰值活度极小 → RegAlloc 有充分复用空间。
|
||||
fn gen_big(n: usize) -> String {
|
||||
let mut src = String::from("function f() { ");
|
||||
@@ -43,9 +43,9 @@ fn module_max_n_registers(m: &CompiledModule) -> u8 {
|
||||
max
|
||||
}
|
||||
|
||||
/// B005 前置复现:off 模式(降级路径)250 变量函数报 RangeError。
|
||||
/// 前置复现:off 模式(降级路径)250 变量函数报 RangeError。
|
||||
#[test]
|
||||
fn b005_repro_errors_without_regalloc() {
|
||||
fn large_function_errors_without_regalloc() {
|
||||
let err = match compile_source(&gen_big(250), false) {
|
||||
Err(e) => e,
|
||||
Ok(_) => panic!("off 模式应报 RangeError"),
|
||||
@@ -53,14 +53,14 @@ fn b005_repro_errors_without_regalloc() {
|
||||
assert!(err.contains("RangeError"), "off 模式应报 RangeError: {err}");
|
||||
}
|
||||
|
||||
/// B005 修复达成:on 模式 250 变量函数编译成功,模块树 n_registers ≤ 253。
|
||||
/// 修复达成:on 模式 250 变量函数编译成功,模块树 n_registers ≤ 253。
|
||||
#[test]
|
||||
fn b005_compiles_with_regalloc() {
|
||||
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 且至少一个严格下降(证明寄存器复用,REG-04 核心)。
|
||||
/// 样例集 n_registers on ≤ off 且至少一个严格下降(证明寄存器复用)。
|
||||
#[test]
|
||||
fn n_registers_on_off_aggregate() {
|
||||
let samples = [
|
||||
@@ -78,7 +78,7 @@ fn n_registers_on_off_aggregate() {
|
||||
"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 回归)
|
||||
// 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();",
|
||||
@@ -90,10 +90,7 @@ fn n_registers_on_off_aggregate() {
|
||||
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 不应增寄存器窗口"
|
||||
);
|
||||
assert!(on_max <= off_max, "样例 {i} on({on_max}) > off({off_max})——RegAlloc 不应增寄存器窗口");
|
||||
if on_max < off_max {
|
||||
strict_decrease = true;
|
||||
}
|
||||
@@ -102,7 +99,7 @@ fn n_registers_on_off_aggregate() {
|
||||
assert!(strict_decrease, "至少一个样例应严格 on < off(复用证明)");
|
||||
}
|
||||
|
||||
/// 确定性:同一源码 on 模式编译两次字节码逐字节相同(B010,structural_hash 缓存依赖)。
|
||||
/// 确定性:同一源码 on 模式编译两次字节码逐字节相同(确定性排序,缓存 key 依赖)。
|
||||
#[test]
|
||||
fn regalloc_deterministic_output() {
|
||||
let a = compile_source(&gen_big(50), true).expect("on 编译成功");
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//! B013 延伸回归:nested 子模块直接槽引用父槽,RegAlloc on/off 运行时等价。
|
||||
//! 嵌套父槽引用回归:nested 子模块直接槽引用父槽,RegAlloc on/off 运行时等价。
|
||||
//!
|
||||
//! 父侧 collect_escaped 保证父不移动槽;但子模块自身 alloc 时引用父槽的
|
||||
//! LOAD_VAR.a / STORE_VAR.rd 曾被当作子模块自己的 vreg 染色移走(→ 读错物理槽,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//! REG-04 断言:RegAlloc on ≤253 且 < off(寄存器收缩);B005 大函数 on 编译成功。
|
||||
//! 寄存器收缩断言:RegAlloc on ≤253 且 < off(寄存器复用);大函数 on 编译成功。
|
||||
|
||||
fn n_registers(src: &str, regalloc: bool) -> Result<u8, String> {
|
||||
let allocator = oxide_parser::Allocator::default();
|
||||
@@ -70,15 +70,15 @@ fn n_registers_shrinks_with_regalloc() {
|
||||
}
|
||||
}
|
||||
|
||||
/// B005 大函数:on 编译成功 ≤253;off 报 RangeError(B005 解决证据)。
|
||||
/// 大函数:on 编译成功 ≤253;off 报 RangeError(vreg 超上限)。
|
||||
#[test]
|
||||
fn b005_large_function_fits_in_253() {
|
||||
fn large_function_fits_in_253() {
|
||||
let src = gen_sum_function(220);
|
||||
let on = n_registers(&src, true);
|
||||
assert!(on.is_ok(), "B005 样例 on 应编译成功");
|
||||
assert!(on.is_ok(), "大函数样例 on 应编译成功");
|
||||
assert!(on.unwrap() <= 253, "on ≤253");
|
||||
let off = n_registers(&src, false);
|
||||
assert!(off.is_err(), "B005 样例 off 应报 RangeError");
|
||||
assert!(off.is_err(), "大函数样例 off 应报 RangeError");
|
||||
assert!(off.unwrap_err().contains("too many registers"), "off Err 消息");
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//! RegAlloc on/off 运行时等价(D-21 主验证)+ B005/spill 样例运行正确(REG-04)。
|
||||
//! RegAlloc on/off 运行时等价(主验证)+ 大函数/spill 样例运行正确。
|
||||
//!
|
||||
//! 同一 JS 源码走两条管线:parse → emit → dce(保守恒定)→(regalloc on/off)→ lower → run,
|
||||
//! 对比顶层执行结果。**不经 Compiler::compile**(测试层直调底层函数)。
|
||||
@@ -38,9 +38,9 @@ fn normalize(r: Result<JsValue, String>) -> (bool, String) {
|
||||
#[test]
|
||||
fn regalloc_preserves_semantics() {
|
||||
let samples = [
|
||||
// switch+const(B011 回归)
|
||||
// switch+const(const guard 回归)
|
||||
"function f(x) { switch (x) { case 1: break; } const c = 5; return c; } f(1);",
|
||||
// 类字段(B012 行为锚)
|
||||
// 类字段(escaped 槽行为锚)
|
||||
"var x = 0; class C { p = (x = 1); } new C(); x;",
|
||||
// try-catch
|
||||
"function f() { try { throw 1; } catch (e) { return e + 1; } } f();",
|
||||
@@ -95,15 +95,16 @@ fn gen_spill_function(n: u32) -> String {
|
||||
src
|
||||
}
|
||||
|
||||
/// B005 大函数(200+ 变量)与 spill 压力样例 on 运行正确。
|
||||
/// off 路径 lower 失败(B005 上限)——不能做 on/off 相等,on 运行正确即 REG-04 证据。
|
||||
/// 大函数(200+ 变量)与 spill 压力样例 on 运行正确。
|
||||
/// off 路径 lower 失败(vreg 超 253 上限)——不能做 on/off 相等,on 运行正确即
|
||||
/// 寄存器复用与 spill 的证据。
|
||||
#[test]
|
||||
fn b005_and_spill_samples_run_correctly() {
|
||||
// B005:220 变量 → vreg 数远超 253,但同时存活 < 253 → RegAlloc 复用解决
|
||||
let src_b005 = gen_sum_function(220);
|
||||
let expected_b005 = 220 * 219 / 2;
|
||||
let out = run_source(&src_b005, true).expect("B005 样例 on 应编译成功");
|
||||
assert_eq!(out.to_string(), expected_b005.to_string(), "B005 样例结果错误");
|
||||
fn large_and_spill_samples_run_correctly() {
|
||||
// 220 变量 → vreg 数远超 253,但同时存活 < 253 → RegAlloc 复用解决
|
||||
let src_large = gen_sum_function(220);
|
||||
let expected_large = 220 * 219 / 2;
|
||||
let out = run_source(&src_large, true).expect("大函数样例 on 应编译成功");
|
||||
assert_eq!(out.to_string(), expected_large.to_string(), "大函数样例结果错误");
|
||||
|
||||
// spill 压力:长活 L + 254 短活 → L 溢出,验证 SPILL/UNSPILL 真实发生且结果正确
|
||||
let src_spill = gen_spill_function(254);
|
||||
@@ -124,7 +125,10 @@ fn b005_and_spill_samples_run_correctly() {
|
||||
let module = oxide_ir::lower::lower(&ir).expect("lower");
|
||||
// 递归扫描模块树(spill 发生在嵌套函数 f 中,非顶层 f(1) 调用)
|
||||
fn has_op(m: &oxide_bytecode::module::CompiledModule, target: OpCode) -> bool {
|
||||
if m.bytecode.iter().any(|&instr| OpCode::try_from(instr as u8).ok() == Some(target)) {
|
||||
if m.bytecode
|
||||
.iter()
|
||||
.any(|&instr| OpCode::try_from(instr as u8).ok() == Some(target))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
m.sub_modules.iter().any(|s| has_op(s, target))
|
||||
|
||||
Reference in New Issue
Block a user