perf: 寄存器分配建图改向量收集加位图成员判定

This commit is contained in:
2026-10-12 12:01:10 +00:00
parent 73907623bb
commit 754d528840
4 changed files with 60 additions and 48 deletions
Generated
+20 -20
View File
@@ -1160,7 +1160,7 @@ dependencies = [
[[package]] [[package]]
name = "oxide_builtins" name = "oxide_builtins"
version = "0.16.11" version = "0.16.12"
dependencies = [ dependencies = [
"chrono", "chrono",
"fast-float", "fast-float",
@@ -1185,7 +1185,7 @@ dependencies = [
[[package]] [[package]]
name = "oxide_bytecode" name = "oxide_bytecode"
version = "0.16.11" version = "0.16.12"
dependencies = [ dependencies = [
"num-bigint", "num-bigint",
"oxide_log", "oxide_log",
@@ -1193,7 +1193,7 @@ dependencies = [
[[package]] [[package]]
name = "oxide_cfg" name = "oxide_cfg"
version = "0.16.11" version = "0.16.12"
dependencies = [ dependencies = [
"oxide_bytecode", "oxide_bytecode",
"oxide_emit", "oxide_emit",
@@ -1204,7 +1204,7 @@ dependencies = [
[[package]] [[package]]
name = "oxide_cli" name = "oxide_cli"
version = "0.16.11" version = "0.16.12"
dependencies = [ dependencies = [
"ansi_term", "ansi_term",
"bumpalo", "bumpalo",
@@ -1228,7 +1228,7 @@ dependencies = [
[[package]] [[package]]
name = "oxide_code_cache" name = "oxide_code_cache"
version = "0.16.11" version = "0.16.12"
dependencies = [ dependencies = [
"lru", "lru",
"oxide_bytecode", "oxide_bytecode",
@@ -1237,7 +1237,7 @@ dependencies = [
[[package]] [[package]]
name = "oxide_compiler" name = "oxide_compiler"
version = "0.16.11" version = "0.16.12"
dependencies = [ dependencies = [
"oxide_bytecode", "oxide_bytecode",
"oxide_cfg", "oxide_cfg",
@@ -1257,7 +1257,7 @@ dependencies = [
[[package]] [[package]]
name = "oxide_dce" name = "oxide_dce"
version = "0.16.11" version = "0.16.12"
dependencies = [ dependencies = [
"oxide_bytecode", "oxide_bytecode",
"oxide_cfg", "oxide_cfg",
@@ -1271,7 +1271,7 @@ dependencies = [
[[package]] [[package]]
name = "oxide_emit" name = "oxide_emit"
version = "0.16.11" version = "0.16.12"
dependencies = [ dependencies = [
"num-bigint", "num-bigint",
"oxc_allocator", "oxc_allocator",
@@ -1286,7 +1286,7 @@ dependencies = [
[[package]] [[package]]
name = "oxide_engine" name = "oxide_engine"
version = "0.16.11" version = "0.16.12"
dependencies = [ dependencies = [
"oxide_bytecode", "oxide_bytecode",
"oxide_compiler", "oxide_compiler",
@@ -1298,7 +1298,7 @@ dependencies = [
[[package]] [[package]]
name = "oxide_ir" name = "oxide_ir"
version = "0.16.11" version = "0.16.12"
dependencies = [ dependencies = [
"oxide_bytecode", "oxide_bytecode",
"oxide_log", "oxide_log",
@@ -1307,7 +1307,7 @@ dependencies = [
[[package]] [[package]]
name = "oxide_kernel" name = "oxide_kernel"
version = "0.16.11" version = "0.16.12"
dependencies = [ dependencies = [
"bumpalo", "bumpalo",
"dashmap", "dashmap",
@@ -1321,7 +1321,7 @@ dependencies = [
[[package]] [[package]]
name = "oxide_liveness" name = "oxide_liveness"
version = "0.16.11" version = "0.16.12"
dependencies = [ dependencies = [
"oxide_bytecode", "oxide_bytecode",
"oxide_cfg", "oxide_cfg",
@@ -1333,7 +1333,7 @@ dependencies = [
[[package]] [[package]]
name = "oxide_log" name = "oxide_log"
version = "0.16.11" version = "0.16.12"
dependencies = [ dependencies = [
"tracing", "tracing",
"tracing-appender", "tracing-appender",
@@ -1342,11 +1342,11 @@ dependencies = [
[[package]] [[package]]
name = "oxide_net" name = "oxide_net"
version = "0.16.11" version = "0.16.12"
[[package]] [[package]]
name = "oxide_parser" name = "oxide_parser"
version = "0.16.11" version = "0.16.12"
dependencies = [ dependencies = [
"oxc_allocator", "oxc_allocator",
"oxc_ast", "oxc_ast",
@@ -1361,7 +1361,7 @@ dependencies = [
[[package]] [[package]]
name = "oxide_regalloc" name = "oxide_regalloc"
version = "0.16.11" version = "0.16.12"
dependencies = [ dependencies = [
"oxide_bytecode", "oxide_bytecode",
"oxide_cfg", "oxide_cfg",
@@ -1377,7 +1377,7 @@ dependencies = [
[[package]] [[package]]
name = "oxide_runtime_api" name = "oxide_runtime_api"
version = "0.16.11" version = "0.16.12"
dependencies = [ dependencies = [
"num-bigint", "num-bigint",
"num-traits", "num-traits",
@@ -1390,7 +1390,7 @@ dependencies = [
[[package]] [[package]]
name = "oxide_test262" name = "oxide_test262"
version = "0.16.11" version = "0.16.12"
dependencies = [ dependencies = [
"oxide_bytecode", "oxide_bytecode",
"oxide_compiler", "oxide_compiler",
@@ -1410,7 +1410,7 @@ dependencies = [
[[package]] [[package]]
name = "oxide_types" name = "oxide_types"
version = "0.16.11" version = "0.16.12"
dependencies = [ dependencies = [
"bumpalo", "bumpalo",
"dashmap", "dashmap",
@@ -1423,7 +1423,7 @@ dependencies = [
[[package]] [[package]]
name = "oxide_vm" name = "oxide_vm"
version = "0.16.11" version = "0.16.12"
dependencies = [ dependencies = [
"bumpalo", "bumpalo",
"chrono", "chrono",
+1 -1
View File
@@ -24,7 +24,7 @@ members = [
] ]
[workspace.package] [workspace.package]
version = "0.16.11" version = "0.16.12"
edition = "2021" edition = "2021"
license = "MIT" license = "MIT"
rust-version = "1.80" rust-version = "1.80"
+4 -4
View File
@@ -23,10 +23,10 @@ pub(super) fn run(f: &IRFunction, live: &LiveInfo) -> Result<AllocMap, String> {
} else { } else {
f.param_layout.base + f.param_layout.count - 1 f.param_layout.base + f.param_layout.count - 1
}; };
let max_real = graph::collect_real_vregs(f).into_iter().max().unwrap_or(0).max(max_param); let max_real = graph::collect_real_vregs(f).last().copied().unwrap_or(0).max(max_param);
let mut next_fresh_id = max_real + 1; let mut next_fresh_id = max_real + 1;
let mut next_slot: u16 = 0; let mut next_slot: u16 = 0;
let mut spill_set: BTreeSet<u32> = BTreeSet::new(); let mut spill_set: Vec<u32> = Vec::new();
let mut fresh: Vec<FreshVreg> = Vec::new(); let mut fresh: Vec<FreshVreg> = Vec::new();
let mut slot_for_vreg: BTreeMap<u32, u16> = BTreeMap::new(); let mut slot_for_vreg: BTreeMap<u32, u16> = BTreeMap::new();
let mut iters = 0usize; let mut iters = 0usize;
@@ -43,7 +43,7 @@ pub(super) fn run(f: &IRFunction, live: &LiveInfo) -> Result<AllocMap, String> {
return Err("RangeError: function body uses too many registers (max 253)".into()); return Err("RangeError: function body uses too many registers (max 253)".into());
} }
// 真实 vreg 溢出:按 def/use 点拆成单点活度 fresh,重建图重试 // 真实 vreg 溢出:按 def/use 点拆成单点活度 fresh,重建图重试
spill_set.insert(v); spill_set.push(v);
debug_assert!(u32::from(next_slot) <= u16::MAX as u32, "spill slot 超 u16 上限"); debug_assert!(u32::from(next_slot) <= u16::MAX as u32, "spill slot 超 u16 上限");
slot_for_vreg.insert(v, next_slot); slot_for_vreg.insert(v, next_slot);
next_slot += 1; next_slot += 1;
@@ -154,7 +154,7 @@ fn kemp_and_select(graph: &InterferenceGraph) -> (BTreeMap<u32, u32>, Vec<u32>)
/// 装配 AllocMap:map(真实 + fresh)+ spills 决策表 + phys_peak + arg_window_base。 /// 装配 AllocMap:map(真实 + fresh)+ spills 决策表 + phys_peak + arg_window_base。
fn assemble( fn assemble(
colors: BTreeMap<u32, u32>, spill_set: BTreeSet<u32>, fresh: Vec<FreshVreg>, slot_for_vreg: BTreeMap<u32, u16>, colors: BTreeMap<u32, u32>, spill_set: Vec<u32>, fresh: Vec<FreshVreg>, slot_for_vreg: BTreeMap<u32, u16>,
arg_window_base: u32, arg_window_base: u32,
) -> Result<AllocMap, String> { ) -> Result<AllocMap, String> {
let mut map = BTreeMap::new(); let mut map = BTreeMap::new();
+35 -23
View File
@@ -9,7 +9,7 @@
//! //!
//! 确定性:全 BTreeMap + Vec 排序,禁 HashMap 迭代序。 //! 确定性:全 BTreeMap + Vec 排序,禁 HashMap 迭代序。
use std::collections::{BTreeMap, BTreeSet}; use std::collections::BTreeMap;
use oxide_bytecode::opcode::OpCode; use oxide_bytecode::opcode::OpCode;
use oxide_ir::operand::Operand; use oxide_ir::operand::Operand;
@@ -34,20 +34,20 @@ pub(super) struct InterferenceGraph {
pub arg_window_base: u32, pub arg_window_base: u32,
} }
/// 收集全部真实 vreg(BTreeSet 保确定性): /// 收集全部真实 vreg(Vec 排序去重保确定性):
/// 字面 `Operand::Reg(r)`(rd/a/b 三槽)+ CALL/CALL_NATIVE/NEW_EXPRESSION b 槽参数区间 /// 字面 `Operand::Reg(r)`(rd/a/b 三槽)+ CALL/CALL_NATIVE/NEW_EXPRESSION b 槽参数区间
/// + SUPER_CALL a 槽参数区间。0 无条件排除(reg 0 永非真实——emit 分配寄存器从 ≥1 起)。 /// + SUPER_CALL a 槽参数区间。0 无条件排除(reg 0 永非真实——emit 分配寄存器从 ≥1 起)。
/// ///
/// 254/255 的过近似规则:live bitset 中位 254/255 无法区分 This/NewTarget 占用与 /// 254/255 的过近似规则:live bitset 中位 254/255 无法区分 This/NewTarget 占用与
/// 真实 vreg 活度,若函数存在该号真实 vreg(大函数场景)则按真实 vreg 建节点染色—— /// 真实 vreg 活度,若函数存在该号真实 vreg(大函数场景)则按真实 vreg 建节点染色——
/// 多出的边只造成过度约束(安全方向),缺失边才会错值。 /// 多出的边只造成过度约束(安全方向),缺失边才会错值。
pub(super) fn collect_real_vregs(f: &IRFunction) -> BTreeSet<u32> { pub(super) fn collect_real_vregs(f: &IRFunction) -> Vec<u32> {
let mut real = BTreeSet::new(); let mut real: Vec<u32> = Vec::new();
for inst in &f.insts { for inst in &f.insts {
for o in [&inst.rd, &inst.a, &inst.b] { for o in [&inst.rd, &inst.a, &inst.b] {
if let Operand::Reg(r) = o { if let Operand::Reg(r) = o {
if *r != 0 { if *r != 0 {
real.insert(*r); real.push(*r);
} }
} }
} }
@@ -66,7 +66,7 @@ pub(super) fn collect_real_vregs(f: &IRFunction) -> BTreeSet<u32> {
if let Some(fr) = first { if let Some(fr) = first {
for r in fr..fr + nargs { for r in fr..fr + nargs {
if r != 0 { if r != 0 {
real.insert(r); real.push(r);
} }
} }
} }
@@ -75,7 +75,7 @@ pub(super) fn collect_real_vregs(f: &IRFunction) -> BTreeSet<u32> {
for &w in inst.ext.iter().skip(1) { for &w in inst.ext.iter().skip(1) {
let r = w & 0x7FFF_FFFF; let r = w & 0x7FFF_FFFF;
if r != 0 { if r != 0 {
real.insert(r); real.push(r);
} }
} }
} }
@@ -83,7 +83,7 @@ pub(super) fn collect_real_vregs(f: &IRFunction) -> BTreeSet<u32> {
if matches!(inst.op, OpCode::DEFINE_ACCESSOR_DYNAMIC | OpCode::DEFINE_ACCESSOR_ATTRS_DYNAMIC) { if matches!(inst.op, OpCode::DEFINE_ACCESSOR_DYNAMIC | OpCode::DEFINE_ACCESSOR_ATTRS_DYNAMIC) {
let r = inst.ext.first().copied().unwrap_or(0) & 0x7FFF_FFFF; let r = inst.ext.first().copied().unwrap_or(0) & 0x7FFF_FFFF;
if r != 0 { if r != 0 {
real.insert(r); real.push(r);
} }
} }
// CONCAT_N:ext[1..] 每个字是完整操作数 vreg(无高位标记)——漏收集则 // CONCAT_N:ext[1..] 每个字是完整操作数 vreg(无高位标记)——漏收集则
@@ -92,18 +92,20 @@ pub(super) fn collect_real_vregs(f: &IRFunction) -> BTreeSet<u32> {
for &w in inst.ext.iter().skip(1) { for &w in inst.ext.iter().skip(1) {
let r = w & 0x7FFF_FFFF; let r = w & 0x7FFF_FFFF;
if r != 0 { if r != 0 {
real.insert(r); real.push(r);
} }
} }
} }
} }
real.sort_unstable();
real.dedup();
real real
} }
/// 构建干涉图:`build(f, live, spill_set, fresh) -> InterferenceGraph`。 /// 构建干涉图:`build(f, live, spill_set, fresh) -> InterferenceGraph`。
/// spill_set 中的 vreg 不再是节点(其 fresh 替代品入图);fresh 按 at==i 并入该指令活集。 /// spill_set 中的 vreg 不再是节点(其 fresh 替代品入图);fresh 按 at==i 并入该指令活集。
pub(super) fn build( pub(super) fn build(
f: &IRFunction, live: &LiveInfo, spill_set: &BTreeSet<u32>, fresh: &[FreshVreg], f: &IRFunction, live: &LiveInfo, spill_set: &[u32], fresh: &[FreshVreg],
) -> InterferenceGraph { ) -> InterferenceGraph {
let mut real = collect_real_vregs(f); let mut real = collect_real_vregs(f);
// inst_live_before 长度 = 指令数(逐指令活集索引) // inst_live_before 长度 = 指令数(逐指令活集索引)
@@ -176,7 +178,7 @@ pub(super) fn build(
}; };
for i in 0..pl.count { for i in 0..pl.count {
let vreg = pl.base + i; let vreg = pl.base + i;
real.insert(vreg); real.push(vreg);
pre_colors.insert(vreg, physical_base + i); pre_colors.insert(vreg, physical_base + i);
} }
} }
@@ -244,8 +246,16 @@ pub(super) fn build(
adj: Vec::new(), adj: Vec::new(),
}); });
} }
// 邻接表(BTreeMap<u32, BTreeSet<u32>> 去重后转 Vec 排序) // 节点成员位图:按 nodes 最大键定长(一次分配、建图期间不增长),逐活位 O(1)
let mut adj_sets: BTreeMap<u32, BTreeSet<u32>> = BTreeMap::new(); // 查询替代 BTreeMap 探测;活位号可超最大键(spilled vreg 活但非节点),越界
// 返回 false 恰好正确。
let max_id = nodes.keys().next_back().copied().unwrap_or(0);
let mut is_node: Vec<u64> = vec![0u64; (max_id as usize / 64) + 1];
for v in nodes.keys() {
is_node[*v as usize / 64] |= 1u64 << (*v as usize % 64);
}
// 邻接表(Vec 收集,收尾排序去重,与 BTreeSet 输出逐字节完全一致)
let mut adj_sets: BTreeMap<u32, Vec<u32>> = BTreeMap::new();
let node_ids: Vec<u32> = nodes.keys().copied().collect(); let node_ids: Vec<u32> = nodes.keys().copied().collect();
for &v in &node_ids { for &v in &node_ids {
adj_sets.entry(v).or_default(); adj_sets.entry(v).or_default();
@@ -268,7 +278,7 @@ pub(super) fn build(
while bits != 0 { while bits != 0 {
let bit = bits.trailing_zeros() as usize; let bit = bits.trailing_zeros() as usize;
let v = (word_idx * 64 + bit) as u32; let v = (word_idx * 64 + bit) as u32;
if nodes.contains_key(&v) { if oxide_liveness::bitset_get(&is_node, v as usize) {
at_i.push(v); at_i.push(v);
} }
bits &= bits - 1; bits &= bits - 1;
@@ -281,24 +291,24 @@ pub(super) fn build(
for a in 0..at_i.len() { for a in 0..at_i.len() {
for b in a + 1..at_i.len() { for b in a + 1..at_i.len() {
let (x, y) = (at_i[a], at_i[b]); let (x, y) = (at_i[a], at_i[b]);
adj_sets.entry(x).or_default().insert(y); adj_sets.entry(x).or_default().push(y);
adj_sets.entry(y).or_default().insert(x); adj_sets.entry(y).or_default().push(x);
} }
} }
// def 结果与执行后仍存活的值冲突(写覆盖):live_after 含 def 后存活者。 // def 结果与执行后仍存活的值冲突(写覆盖):live_after 含 def 后存活者。
// live_before 已 kill def 自身,纯写指令(FOR_OF_NEXT 等)的结果若与长活 // live_before 已 kill def 自身,纯写指令(FOR_OF_NEXT 等)的结果若与长活
// 寄存器同色会覆盖其值(elision 解构返回值被覆盖),须补此干涉边。 // 寄存器同色会覆盖其值(elision 解构返回值被覆盖),须补此干涉边。
if let Some(d) = f.insts[i].def_reg() { if let Some(d) = f.insts[i].def_reg() {
if nodes.contains_key(&d) { if oxide_liveness::bitset_get(&is_node, d as usize) {
let after = &live.inst_live_after[i]; let after = &live.inst_live_after[i];
for (word_idx, word) in after.iter().enumerate() { for (word_idx, word) in after.iter().enumerate() {
let mut bits = *word; let mut bits = *word;
while bits != 0 { while bits != 0 {
let bit = bits.trailing_zeros() as usize; let bit = bits.trailing_zeros() as usize;
let v = (word_idx * 64 + bit) as u32; let v = (word_idx * 64 + bit) as u32;
if v != d && nodes.contains_key(&v) { if v != d && oxide_liveness::bitset_get(&is_node, v as usize) {
adj_sets.entry(d).or_default().insert(v); adj_sets.entry(d).or_default().push(v);
adj_sets.entry(v).or_default().insert(d); adj_sets.entry(v).or_default().push(d);
} }
bits &= bits - 1; bits &= bits - 1;
} }
@@ -306,9 +316,11 @@ pub(super) fn build(
} }
} }
} }
for (v, set) in adj_sets { for (v, mut adj) in adj_sets {
if let Some(node) = nodes.get_mut(&v) { if let Some(node) = nodes.get_mut(&v) {
node.adj = set.into_iter().collect(); adj.sort_unstable();
adj.dedup();
node.adj = adj;
} }
} }
@@ -380,7 +392,7 @@ mod tests {
fn build_graph(f: &IRFunction) -> InterferenceGraph { fn build_graph(f: &IRFunction) -> InterferenceGraph {
let cfg = oxide_cfg::build_cfg(f); let cfg = oxide_cfg::build_cfg(f);
let live = oxide_liveness::liveness(f, &cfg); let live = oxide_liveness::liveness(f, &cfg);
build(f, &live, &BTreeSet::new(), &[]) build(f, &live, &[], &[])
} }
#[test] #[test]