mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-24 18:51:13 +08:00
fix: 寄存器分配支持高位寄存器槽
This commit is contained in:
@@ -95,30 +95,10 @@ pub(super) fn collect_real_vregs(f: &IRFunction) -> BTreeSet<u32> {
|
||||
pub(super) fn build(
|
||||
f: &IRFunction, live: &LiveInfo, spill_set: &BTreeSet<u32>, fresh: &[FreshVreg],
|
||||
) -> InterferenceGraph {
|
||||
let real = collect_real_vregs(f);
|
||||
let mut real = collect_real_vregs(f);
|
||||
// inst_live_before 长度 = 指令数(逐指令活集索引)
|
||||
let inst_count = live.inst_live_before.len();
|
||||
|
||||
// ── 预着色 ──
|
||||
let mut pre_colors: BTreeMap<u32, u32> = BTreeMap::new();
|
||||
// 参数段:VM 调用写 regs[param_base+i],钉死不可动(调用契约)
|
||||
let pl = f.param_layout;
|
||||
if pl.count > 0 {
|
||||
for i in 0..pl.count {
|
||||
let v = pl.base + i;
|
||||
pre_colors.insert(v, v);
|
||||
}
|
||||
}
|
||||
// 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);
|
||||
// 对称缺口:子模块自身引用的父槽也必须预着色恒等。父侧 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);
|
||||
|
||||
// ── 可分配色集 ──
|
||||
// 窗口只为参数连续性 MOV 桥预留;spread 调用实参经 ext 逐个读寄存器,无连续性要求。
|
||||
let max_nargs = f
|
||||
.insts
|
||||
@@ -129,6 +109,68 @@ pub(super) fn build(
|
||||
.unwrap_or(0);
|
||||
let arg_window_base = 254u32.saturating_sub(max_nargs);
|
||||
|
||||
// ── 预着色 ──
|
||||
let mut pre_colors: BTreeMap<u32, u32> = BTreeMap::new();
|
||||
// 当前函数读取的父槽已经是父函数分配后的物理号,必须恒等保护。
|
||||
let mut escaped_colors: Vec<u32> = Vec::new();
|
||||
collect_own_escaped(f, &mut pre_colors, &mut escaped_colors);
|
||||
|
||||
// 被子函数捕获的当前函数槽需要固定颜色,但高虚拟槽不能恒等映射到 254+。
|
||||
// 父函数完成分配后,alloc 会把选定物理色同步到整个 nested 树的父槽引用。
|
||||
let mut nested_escaped = Vec::new();
|
||||
collect_escaped(&f.nested, &mut nested_escaped);
|
||||
let param_end = f.param_layout.base.saturating_add(f.param_layout.count);
|
||||
for vreg in nested_escaped {
|
||||
if !real.contains(&vreg) || (f.param_layout.base..param_end).contains(&vreg) {
|
||||
continue;
|
||||
}
|
||||
let identity_available =
|
||||
vreg >= 1 && vreg < arg_window_base && !pre_colors.values().any(|color| *color == vreg);
|
||||
let physical = if identity_available {
|
||||
vreg
|
||||
} else {
|
||||
(1..arg_window_base)
|
||||
.find(|color| !pre_colors.values().any(|existing| existing == color))
|
||||
.unwrap_or_else(|| {
|
||||
debug_assert!(false, "escaped 槽无自由色(vreg={vreg})");
|
||||
1
|
||||
})
|
||||
};
|
||||
pre_colors.insert(vreg, physical);
|
||||
escaped_colors.push(physical);
|
||||
}
|
||||
escaped_colors.sort_unstable();
|
||||
escaped_colors.dedup();
|
||||
|
||||
// 参数段必须连续。继承父上下文后虚拟段可能高于 253,此时选择不与 escaped 槽
|
||||
// 冲突的低位连续物理段;finish 按首参数颜色同步回写 param_layout.base。
|
||||
let pl = f.param_layout;
|
||||
if pl.count > 0 {
|
||||
let identity_end = pl.base.saturating_add(pl.count);
|
||||
let identity_available = pl.base >= 1
|
||||
&& identity_end <= arg_window_base
|
||||
&& (pl.base..identity_end).all(|color| !escaped_colors.contains(&color));
|
||||
let physical_base = if identity_available {
|
||||
pl.base
|
||||
} else {
|
||||
(1..=arg_window_base.saturating_sub(pl.count))
|
||||
.find(|base| {
|
||||
(*base..*base + pl.count).all(|color| {
|
||||
!pre_colors.values().any(|existing| *existing == color) && !escaped_colors.contains(&color)
|
||||
})
|
||||
})
|
||||
.unwrap_or_else(|| {
|
||||
debug_assert!(false, "参数段无连续自由色(base={}, count={})", pl.base, pl.count);
|
||||
1
|
||||
})
|
||||
};
|
||||
for i in 0..pl.count {
|
||||
let vreg = pl.base + i;
|
||||
real.insert(vreg);
|
||||
pre_colors.insert(vreg, physical_base + i);
|
||||
}
|
||||
}
|
||||
|
||||
// ── builtin 槽 ──
|
||||
// VM 帧推入时写 regs[slot]=全局值(无指令 def 却活到入口),物理号须恒定且排除出
|
||||
// 可分配集,否则 prologue 死定义临时(如解构 FOR_OF_DONE 结果)与它同色,在
|
||||
@@ -166,7 +208,7 @@ pub(super) fn build(
|
||||
|
||||
let mut allocatable: Vec<u32> = Vec::new();
|
||||
for c in 1u32..=253 {
|
||||
if pre_colors.contains_key(&c) || escaped_colors.contains(&c) || c >= arg_window_base {
|
||||
if pre_colors.values().any(|color| *color == c) || escaped_colors.contains(&c) || c >= arg_window_base {
|
||||
continue;
|
||||
}
|
||||
allocatable.push(c);
|
||||
@@ -249,9 +291,8 @@ pub(super) fn build(
|
||||
}
|
||||
}
|
||||
|
||||
/// 递归收集 nested 树中变量槽引用(LOAD_VAR.a 读槽、STORE_VAR.rd 写槽),
|
||||
/// 注入 pre_colors 并记录 escaped 色列表(可分配集排除 + spill 候选排除)。
|
||||
fn collect_escaped(nested: &[IRFunction], pre_colors: &mut BTreeMap<u32, u32>, out: &mut Vec<u32>) {
|
||||
/// 递归收集 nested 树中的父槽虚拟号(LOAD_VAR.a 读槽、STORE_VAR.rd 写槽)。
|
||||
fn collect_escaped(nested: &[IRFunction], out: &mut Vec<u32>) {
|
||||
for sub in nested {
|
||||
for inst in &sub.insts {
|
||||
let slot = match inst.op {
|
||||
@@ -260,13 +301,12 @@ fn collect_escaped(nested: &[IRFunction], pre_colors: &mut BTreeMap<u32, u32>, o
|
||||
_ => Operand::None,
|
||||
};
|
||||
if let Operand::Reg(r) = slot {
|
||||
pre_colors.entry(r).or_insert(r);
|
||||
if !out.contains(&r) {
|
||||
out.push(r);
|
||||
}
|
||||
}
|
||||
}
|
||||
collect_escaped(&sub.nested, pre_colors, out);
|
||||
collect_escaped(&sub.nested, out);
|
||||
}
|
||||
out.sort_unstable();
|
||||
}
|
||||
@@ -379,6 +419,23 @@ mod tests {
|
||||
assert!(!g.allocatable.contains(&3), "escaped 色排除");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn high_escaped_slot_uses_encodable_color() {
|
||||
let mut f = empty_function();
|
||||
f.insts.push(Inst::load_const(Operand::Reg(254), 0));
|
||||
f.insts
|
||||
.push(Inst::new(OpCode::RETURN, Operand::Reg(254), Operand::None, Operand::None));
|
||||
let mut sub = empty_function();
|
||||
sub.param_layout = oxide_ir::ParamLayout { base: 300, count: 0 };
|
||||
sub.insts
|
||||
.push(Inst::new(OpCode::LOAD_VAR, Operand::Reg(301), Operand::Reg(254), Operand::None));
|
||||
f.nested.push(sub);
|
||||
let g = build_graph(&f);
|
||||
let color = g.nodes[&254].pre_color.expect("escaped 槽应预着色");
|
||||
assert!((1..=253).contains(&color));
|
||||
assert_ne!(color, 254);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reserved_colors_excluded() {
|
||||
let mut f = empty_function();
|
||||
|
||||
Reference in New Issue
Block a user