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]]
name = "oxide_builtins"
version = "0.16.11"
version = "0.16.12"
dependencies = [
"chrono",
"fast-float",
@@ -1185,7 +1185,7 @@ dependencies = [
[[package]]
name = "oxide_bytecode"
version = "0.16.11"
version = "0.16.12"
dependencies = [
"num-bigint",
"oxide_log",
@@ -1193,7 +1193,7 @@ dependencies = [
[[package]]
name = "oxide_cfg"
version = "0.16.11"
version = "0.16.12"
dependencies = [
"oxide_bytecode",
"oxide_emit",
@@ -1204,7 +1204,7 @@ dependencies = [
[[package]]
name = "oxide_cli"
version = "0.16.11"
version = "0.16.12"
dependencies = [
"ansi_term",
"bumpalo",
@@ -1228,7 +1228,7 @@ dependencies = [
[[package]]
name = "oxide_code_cache"
version = "0.16.11"
version = "0.16.12"
dependencies = [
"lru",
"oxide_bytecode",
@@ -1237,7 +1237,7 @@ dependencies = [
[[package]]
name = "oxide_compiler"
version = "0.16.11"
version = "0.16.12"
dependencies = [
"oxide_bytecode",
"oxide_cfg",
@@ -1257,7 +1257,7 @@ dependencies = [
[[package]]
name = "oxide_dce"
version = "0.16.11"
version = "0.16.12"
dependencies = [
"oxide_bytecode",
"oxide_cfg",
@@ -1271,7 +1271,7 @@ dependencies = [
[[package]]
name = "oxide_emit"
version = "0.16.11"
version = "0.16.12"
dependencies = [
"num-bigint",
"oxc_allocator",
@@ -1286,7 +1286,7 @@ dependencies = [
[[package]]
name = "oxide_engine"
version = "0.16.11"
version = "0.16.12"
dependencies = [
"oxide_bytecode",
"oxide_compiler",
@@ -1298,7 +1298,7 @@ dependencies = [
[[package]]
name = "oxide_ir"
version = "0.16.11"
version = "0.16.12"
dependencies = [
"oxide_bytecode",
"oxide_log",
@@ -1307,7 +1307,7 @@ dependencies = [
[[package]]
name = "oxide_kernel"
version = "0.16.11"
version = "0.16.12"
dependencies = [
"bumpalo",
"dashmap",
@@ -1321,7 +1321,7 @@ dependencies = [
[[package]]
name = "oxide_liveness"
version = "0.16.11"
version = "0.16.12"
dependencies = [
"oxide_bytecode",
"oxide_cfg",
@@ -1333,7 +1333,7 @@ dependencies = [
[[package]]
name = "oxide_log"
version = "0.16.11"
version = "0.16.12"
dependencies = [
"tracing",
"tracing-appender",
@@ -1342,11 +1342,11 @@ dependencies = [
[[package]]
name = "oxide_net"
version = "0.16.11"
version = "0.16.12"
[[package]]
name = "oxide_parser"
version = "0.16.11"
version = "0.16.12"
dependencies = [
"oxc_allocator",
"oxc_ast",
@@ -1361,7 +1361,7 @@ dependencies = [
[[package]]
name = "oxide_regalloc"
version = "0.16.11"
version = "0.16.12"
dependencies = [
"oxide_bytecode",
"oxide_cfg",
@@ -1377,7 +1377,7 @@ dependencies = [
[[package]]
name = "oxide_runtime_api"
version = "0.16.11"
version = "0.16.12"
dependencies = [
"num-bigint",
"num-traits",
@@ -1390,7 +1390,7 @@ dependencies = [
[[package]]
name = "oxide_test262"
version = "0.16.11"
version = "0.16.12"
dependencies = [
"oxide_bytecode",
"oxide_compiler",
@@ -1410,7 +1410,7 @@ dependencies = [
[[package]]
name = "oxide_types"
version = "0.16.11"
version = "0.16.12"
dependencies = [
"bumpalo",
"dashmap",
@@ -1423,7 +1423,7 @@ dependencies = [
[[package]]
name = "oxide_vm"
version = "0.16.11"
version = "0.16.12"
dependencies = [
"bumpalo",
"chrono",
+1 -1
View File
@@ -24,7 +24,7 @@ members = [
]
[workspace.package]
version = "0.16.11"
version = "0.16.12"
edition = "2021"
license = "MIT"
rust-version = "1.80"
+4 -4
View File
@@ -23,10 +23,10 @@ pub(super) fn run(f: &IRFunction, live: &LiveInfo) -> Result<AllocMap, String> {
} else {
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_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 slot_for_vreg: BTreeMap<u32, u16> = BTreeMap::new();
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());
}
// 真实 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 上限");
slot_for_vreg.insert(v, next_slot);
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。
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,
) -> Result<AllocMap, String> {
let mut map = BTreeMap::new();
+35 -23
View File
@@ -9,7 +9,7 @@
//!
//! 确定性:全 BTreeMap + Vec 排序,禁 HashMap 迭代序。
use std::collections::{BTreeMap, BTreeSet};
use std::collections::BTreeMap;
use oxide_bytecode::opcode::OpCode;
use oxide_ir::operand::Operand;
@@ -34,20 +34,20 @@ pub(super) struct InterferenceGraph {
pub arg_window_base: u32,
}
/// 收集全部真实 vreg(BTreeSet 保确定性):
/// 收集全部真实 vreg(Vec 排序去重保确定性):
/// 字面 `Operand::Reg(r)`(rd/a/b 三槽)+ CALL/CALL_NATIVE/NEW_EXPRESSION b 槽参数区间
/// + SUPER_CALL a 槽参数区间。0 无条件排除(reg 0 永非真实——emit 分配寄存器从 ≥1 起)。
///
/// 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();
pub(super) fn collect_real_vregs(f: &IRFunction) -> Vec<u32> {
let mut real: Vec<u32> = Vec::new();
for inst in &f.insts {
for o in [&inst.rd, &inst.a, &inst.b] {
if let Operand::Reg(r) = o {
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 {
for r in fr..fr + nargs {
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) {
let r = w & 0x7FFF_FFFF;
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) {
let r = inst.ext.first().copied().unwrap_or(0) & 0x7FFF_FFFF;
if r != 0 {
real.insert(r);
real.push(r);
}
}
// 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) {
let r = w & 0x7FFF_FFFF;
if r != 0 {
real.insert(r);
real.push(r);
}
}
}
}
real.sort_unstable();
real.dedup();
real
}
/// 构建干涉图:`build(f, live, spill_set, fresh) -> InterferenceGraph`。
/// spill_set 中的 vreg 不再是节点(其 fresh 替代品入图);fresh 按 at==i 并入该指令活集。
pub(super) fn build(
f: &IRFunction, live: &LiveInfo, spill_set: &BTreeSet<u32>, fresh: &[FreshVreg],
f: &IRFunction, live: &LiveInfo, spill_set: &[u32], fresh: &[FreshVreg],
) -> InterferenceGraph {
let mut real = collect_real_vregs(f);
// inst_live_before 长度 = 指令数(逐指令活集索引)
@@ -176,7 +178,7 @@ pub(super) fn build(
};
for i in 0..pl.count {
let vreg = pl.base + i;
real.insert(vreg);
real.push(vreg);
pre_colors.insert(vreg, physical_base + i);
}
}
@@ -244,8 +246,16 @@ pub(super) fn build(
adj: Vec::new(),
});
}
// 邻接表(BTreeMap<u32, BTreeSet<u32>> 去重后转 Vec 排序)
let mut adj_sets: BTreeMap<u32, BTreeSet<u32>> = BTreeMap::new();
// 节点成员位图:按 nodes 最大键定长(一次分配、建图期间不增长),逐活位 O(1)
// 查询替代 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();
for &v in &node_ids {
adj_sets.entry(v).or_default();
@@ -268,7 +278,7 @@ pub(super) fn build(
while bits != 0 {
let bit = bits.trailing_zeros() as usize;
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);
}
bits &= bits - 1;
@@ -281,24 +291,24 @@ pub(super) fn build(
for a in 0..at_i.len() {
for b in a + 1..at_i.len() {
let (x, y) = (at_i[a], at_i[b]);
adj_sets.entry(x).or_default().insert(y);
adj_sets.entry(y).or_default().insert(x);
adj_sets.entry(x).or_default().push(y);
adj_sets.entry(y).or_default().push(x);
}
}
// def 结果与执行后仍存活的值冲突(写覆盖):live_after 含 def 后存活者。
// live_before 已 kill def 自身,纯写指令(FOR_OF_NEXT 等)的结果若与长活
// 寄存器同色会覆盖其值(elision 解构返回值被覆盖),须补此干涉边。
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];
for (word_idx, word) in after.iter().enumerate() {
let mut bits = *word;
while bits != 0 {
let bit = bits.trailing_zeros() as usize;
let v = (word_idx * 64 + bit) as u32;
if v != d && nodes.contains_key(&v) {
adj_sets.entry(d).or_default().insert(v);
adj_sets.entry(v).or_default().insert(d);
if v != d && oxide_liveness::bitset_get(&is_node, v as usize) {
adj_sets.entry(d).or_default().push(v);
adj_sets.entry(v).or_default().push(d);
}
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) {
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 {
let cfg = oxide_cfg::build_cfg(f);
let live = oxide_liveness::liveness(f, &cfg);
build(f, &live, &BTreeSet::new(), &[])
build(f, &live, &[], &[])
}
#[test]