mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-23 18:21:13 +08:00
perf: Kemp 染色工作列表改两集合,弹出取首元素消除二次方扫描
This commit is contained in:
@@ -5,7 +5,8 @@
|
|||||||
//! 选色。fresh 染色失败 = 单点活度 ≥ k = 无可行染色 → Err(RangeError 路径,消息与
|
//! 选色。fresh 染色失败 = 单点活度 ≥ k = 无可行染色 → Err(RangeError 路径,消息与
|
||||||
//! lower 逐字一致)。
|
//! lower 逐字一致)。
|
||||||
//!
|
//!
|
||||||
//! 确定性:BTreeMap/BTreeSet + Vec 排序,禁 HashMap。
|
//! 确定性:BTreeMap/BTreeSet + Vec 排序,禁 HashMap;Kemp 两集合的弹出顺序取
|
||||||
|
//! BTreeSet 首元素即现判定(light 最小 vreg、heavy 最小 (degree, vreg)),禁动态度数递减。
|
||||||
|
|
||||||
use std::collections::{BTreeMap, BTreeSet};
|
use std::collections::{BTreeMap, BTreeSet};
|
||||||
|
|
||||||
@@ -98,39 +99,33 @@ fn use_points(f: &IRFunction, v: u32) -> Vec<usize> {
|
|||||||
/// Kemp 简化 + 贪心选色。返回 (vreg → 色, 无色的节点 = spill 候选)。
|
/// Kemp 简化 + 贪心选色。返回 (vreg → 色, 无色的节点 = spill 候选)。
|
||||||
fn kemp_and_select(graph: &InterferenceGraph) -> (BTreeMap<u32, u32>, Vec<u32>) {
|
fn kemp_and_select(graph: &InterferenceGraph) -> (BTreeMap<u32, u32>, Vec<u32>) {
|
||||||
let k = graph.k;
|
let k = graph.k;
|
||||||
// 工作集 = 未入栈的非预着色节点
|
// 两集合替代单工作列表,弹出顺序是现判定的直译:light 以 vreg 为键,首元素即
|
||||||
let mut worklist: BTreeSet<u32> = graph
|
// light 节点中最小 vreg(现 `find(degree < k)` 的命中);heavy 以 (degree, vreg)
|
||||||
.nodes
|
// 为键,首元素即最小 (degree, vreg)(现卡住分支的 min)。degree 取全图邻接长度,
|
||||||
.iter()
|
// 循环中不递减;初始化分流与弹出移除必须同步,同一节点只进一个集合。
|
||||||
.filter(|(_, n)| n.pre_color.is_none())
|
let mut light: BTreeSet<u32> = BTreeSet::new();
|
||||||
.map(|(v, _)| *v)
|
let mut heavy: BTreeSet<(u32, u32)> = BTreeSet::new();
|
||||||
.collect();
|
for (v, n) in &graph.nodes {
|
||||||
|
if n.pre_color.is_none() {
|
||||||
|
if n.adj.len() < k {
|
||||||
|
light.insert(*v);
|
||||||
|
} else {
|
||||||
|
heavy.insert((n.adj.len() as u32, *v));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
let mut stack: Vec<u32> = Vec::new();
|
let mut stack: Vec<u32> = Vec::new();
|
||||||
let mut failed: Vec<u32> = Vec::new();
|
let mut failed: Vec<u32> = Vec::new();
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
// 找 degree < k 的非预着色节点入栈
|
if let Some(v) = light.pop_first() {
|
||||||
let found = worklist.iter().copied().find(|v| graph.nodes[v].adj.len() < k);
|
|
||||||
match found {
|
|
||||||
Some(v) => {
|
|
||||||
stack.push(v);
|
stack.push(v);
|
||||||
worklist.remove(&v);
|
} else if let Some((_, v)) = heavy.pop_first() {
|
||||||
}
|
// 卡住:light 已空,取最小 (degree, vreg) 作 spill 候选
|
||||||
None => {
|
failed.push(v);
|
||||||
if worklist.is_empty() {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// 卡住:选 spill 候选(degree 最小 / vreg 号最小,确定性)
|
|
||||||
let cand = worklist
|
|
||||||
.iter()
|
|
||||||
.map(|v| (graph.nodes[v].adj.len(), *v))
|
|
||||||
.min()
|
|
||||||
.map(|(_, v)| v)
|
|
||||||
.unwrap();
|
|
||||||
failed.push(cand);
|
|
||||||
worklist.remove(&cand);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 选择阶段:预着色节点固定;栈顶弹出取最低可用色
|
// 选择阶段:预着色节点固定;栈顶弹出取最低可用色
|
||||||
|
|||||||
@@ -154,3 +154,57 @@ fn uncolorable_returns_error() {
|
|||||||
assert!(result.is_err(), "单点活度 ≥ k 应报 Err");
|
assert!(result.is_err(), "单点活度 ≥ k 应报 Err");
|
||||||
assert!(result.unwrap_err().contains("too many registers"), "Err 消息应含 too many registers");
|
assert!(result.unwrap_err().contains("too many registers"), "Err 消息应含 too many registers");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn pop_order_is_lowest_vreg_among_light_nodes() {
|
||||||
|
// 混合图:vreg 5 的 degree 为 2(邻 1/7)、vreg 7 的 degree 为 1(邻 5)、vreg 1 的 degree 为 1
|
||||||
|
// (邻 5),最小 vreg 的 degree 不是全图最小。弹出顺序是"light 节点中最小 vreg"(1, 5, 7 依次),
|
||||||
|
// 不是"最小 degree 优先"(后者会先弹 8/9)。两种顺序染色结果不同:现实现 5→2、7→1;
|
||||||
|
// 若按最小 degree 优先弹,则 5→1、7→2。断言值由现实现跑出后固化,钉死弹出顺序语义。
|
||||||
|
let m = color_of(
|
||||||
|
vec![
|
||||||
|
Inst::load_const(Operand::Reg(1), 0),
|
||||||
|
Inst::load_const(Operand::Reg(5), 0),
|
||||||
|
Inst::new(OpCode::ADD, Operand::Reg(8), Operand::Reg(1), Operand::Reg(5)),
|
||||||
|
Inst::load_const(Operand::Reg(7), 0),
|
||||||
|
Inst::new(OpCode::ADD, Operand::Reg(9), Operand::Reg(5), Operand::Reg(7)),
|
||||||
|
Inst::new(OpCode::RETURN, Operand::Reg(9), Operand::None, Operand::None),
|
||||||
|
],
|
||||||
|
ParamLayout { base: 0, count: 0 },
|
||||||
|
Vec::new(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(m.map[&5], Alloc::Phys(2), "5 先弹、后染色,7 先占 1 号色");
|
||||||
|
assert_eq!(m.map[&7], Alloc::Phys(1), "7 后弹、先染色,取最低可用色 1");
|
||||||
|
assert_eq!(m.map[&1], Alloc::Phys(1), "1 最后染色,邻 5 已占 2 号色");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn stuck_spill_order_is_degree_then_vreg() {
|
||||||
|
// 250 个未使用参数预着色 1..250(k=3)+ 六个非预着色节点全部 degree ≥ 3:
|
||||||
|
// 300/301/302/303 同时 live(K4),304 与 300/301/302 同时 live,305 是 303 的
|
||||||
|
// kill 结果(def 规则向 live_after 的 300/301/302 补边)。degree 为
|
||||||
|
// 304:3、305:3、303:4、300:5、301:5、302:5。全部卡住,spill 候选序按
|
||||||
|
// (degree, vreg) 升序:304、305、303、300、301、302(不是 vreg 号序 300..305)。
|
||||||
|
// slot 号记录第一轮 failed 顺序,断言其钉死卡住路径的取最小 (degree, vreg) 语义。
|
||||||
|
let insts = vec![
|
||||||
|
Inst::load_const(Operand::Reg(300), 0),
|
||||||
|
Inst::load_const(Operand::Reg(301), 0),
|
||||||
|
Inst::load_const(Operand::Reg(302), 0),
|
||||||
|
Inst::load_const(Operand::Reg(303), 0),
|
||||||
|
Inst::new(OpCode::ADD, Operand::Reg(305), Operand::Reg(303), Operand::Reg(1)),
|
||||||
|
Inst::load_const(Operand::Reg(304), 0),
|
||||||
|
Inst::new(OpCode::ADD, Operand::Reg(306), Operand::Reg(300), Operand::Reg(304)),
|
||||||
|
Inst::new(OpCode::ADD, Operand::Reg(307), Operand::Reg(301), Operand::Reg(302)),
|
||||||
|
Inst::new(OpCode::RETURN, Operand::Reg(307), Operand::None, Operand::None),
|
||||||
|
];
|
||||||
|
let m = color_of(insts, ParamLayout { base: 1, count: 250 }, Vec::new()).unwrap();
|
||||||
|
assert_eq!(m.spills.len(), 6, "六个非预着色节点应全部 spill");
|
||||||
|
let slot = |v: u32| m.spills.iter().find(|s| s.vreg == v).map(|s| s.slot);
|
||||||
|
assert_eq!(slot(304), Some(0), "最小 (degree, vreg) 是 304,最先 spill");
|
||||||
|
assert_eq!(slot(305), Some(1));
|
||||||
|
assert_eq!(slot(303), Some(2));
|
||||||
|
assert_eq!(slot(300), Some(3));
|
||||||
|
assert_eq!(slot(301), Some(4));
|
||||||
|
assert_eq!(slot(302), Some(5));
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user