From 3cabfb70f9b3e916df46451ca8cf66e1fd34f4dc Mon Sep 17 00:00:00 2001 From: pjh456 <147148383@qq.com> Date: Mon, 12 Oct 2026 12:18:10 +0000 Subject: [PATCH] =?UTF-8?q?perf:=20=E9=80=90=E6=8C=87=E4=BB=A4=E6=B4=BB?= =?UTF-8?q?=E9=9B=86=E6=8B=8D=E5=B9=B3=E4=B8=BA=E5=8D=95=E4=B8=80=E4=BD=8D?= =?UTF-8?q?=E9=9B=86=E5=8A=A0=E6=B6=88=E8=B4=B9=E6=96=B9=E7=BB=B4=E5=BA=A6?= =?UTF-8?q?=E5=AE=88=E5=8D=AB=E5=90=8C=E6=AD=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cargo.lock | 40 ++++++++++----------- Cargo.toml | 2 +- crates/oxide_dce/src/lib.rs | 5 +-- crates/oxide_dce/src/precise_sweep.rs | 2 +- crates/oxide_dce/tests/precise_unit.rs | 22 ++++++------ crates/oxide_liveness/src/lib.rs | 1 + crates/oxide_liveness/src/live_info.rs | 13 ++++--- crates/oxide_liveness/src/ranges.rs | 46 ++++++++++++++---------- crates/oxide_liveness/tests/smoke.rs | 8 +++-- crates/oxide_regalloc/src/call_window.rs | 9 ++--- crates/oxide_regalloc/src/graph.rs | 8 ++--- crates/oxide_regalloc/src/lib.rs | 7 ++-- 12 files changed, 92 insertions(+), 71 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 60439341..b4d2fb1a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1160,7 +1160,7 @@ dependencies = [ [[package]] name = "oxide_builtins" -version = "0.16.13" +version = "0.16.14" dependencies = [ "chrono", "fast-float", @@ -1185,7 +1185,7 @@ dependencies = [ [[package]] name = "oxide_bytecode" -version = "0.16.13" +version = "0.16.14" dependencies = [ "num-bigint", "oxide_log", @@ -1193,7 +1193,7 @@ dependencies = [ [[package]] name = "oxide_cfg" -version = "0.16.13" +version = "0.16.14" dependencies = [ "oxide_bytecode", "oxide_emit", @@ -1204,7 +1204,7 @@ dependencies = [ [[package]] name = "oxide_cli" -version = "0.16.13" +version = "0.16.14" dependencies = [ "ansi_term", "bumpalo", @@ -1228,7 +1228,7 @@ dependencies = [ [[package]] name = "oxide_code_cache" -version = "0.16.13" +version = "0.16.14" dependencies = [ "lru", "oxide_bytecode", @@ -1237,7 +1237,7 @@ dependencies = [ [[package]] name = "oxide_compiler" -version = "0.16.13" +version = "0.16.14" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1257,7 +1257,7 @@ dependencies = [ [[package]] name = "oxide_dce" -version = "0.16.13" +version = "0.16.14" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1271,7 +1271,7 @@ dependencies = [ [[package]] name = "oxide_emit" -version = "0.16.13" +version = "0.16.14" dependencies = [ "num-bigint", "oxc_allocator", @@ -1286,7 +1286,7 @@ dependencies = [ [[package]] name = "oxide_engine" -version = "0.16.13" +version = "0.16.14" dependencies = [ "oxide_bytecode", "oxide_compiler", @@ -1298,7 +1298,7 @@ dependencies = [ [[package]] name = "oxide_ir" -version = "0.16.13" +version = "0.16.14" dependencies = [ "oxide_bytecode", "oxide_log", @@ -1307,7 +1307,7 @@ dependencies = [ [[package]] name = "oxide_kernel" -version = "0.16.13" +version = "0.16.14" dependencies = [ "bumpalo", "dashmap", @@ -1321,7 +1321,7 @@ dependencies = [ [[package]] name = "oxide_liveness" -version = "0.16.13" +version = "0.16.14" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1333,7 +1333,7 @@ dependencies = [ [[package]] name = "oxide_log" -version = "0.16.13" +version = "0.16.14" dependencies = [ "tracing", "tracing-appender", @@ -1342,11 +1342,11 @@ dependencies = [ [[package]] name = "oxide_net" -version = "0.16.13" +version = "0.16.14" [[package]] name = "oxide_parser" -version = "0.16.13" +version = "0.16.14" dependencies = [ "oxc_allocator", "oxc_ast", @@ -1361,7 +1361,7 @@ dependencies = [ [[package]] name = "oxide_regalloc" -version = "0.16.13" +version = "0.16.14" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1377,7 +1377,7 @@ dependencies = [ [[package]] name = "oxide_runtime_api" -version = "0.16.13" +version = "0.16.14" dependencies = [ "num-bigint", "num-traits", @@ -1390,7 +1390,7 @@ dependencies = [ [[package]] name = "oxide_test262" -version = "0.16.13" +version = "0.16.14" dependencies = [ "oxide_bytecode", "oxide_compiler", @@ -1410,7 +1410,7 @@ dependencies = [ [[package]] name = "oxide_types" -version = "0.16.13" +version = "0.16.14" dependencies = [ "bumpalo", "dashmap", @@ -1423,7 +1423,7 @@ dependencies = [ [[package]] name = "oxide_vm" -version = "0.16.13" +version = "0.16.14" dependencies = [ "bumpalo", "chrono", diff --git a/Cargo.toml b/Cargo.toml index e4bed476..c1adea69 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,7 +24,7 @@ members = [ ] [workspace.package] -version = "0.16.13" +version = "0.16.14" edition = "2021" license = "MIT" rust-version = "1.80" diff --git a/crates/oxide_dce/src/lib.rs b/crates/oxide_dce/src/lib.rs index 9d22e0b7..5b4eb04b 100644 --- a/crates/oxide_dce/src/lib.rs +++ b/crates/oxide_dce/src/lib.rs @@ -75,8 +75,9 @@ pub fn dce_precise(f: &mut IRFunction, live: &LiveInfo) -> PreciseOutcome { return PreciseOutcome { live_reusable: true }; } // 维度守卫:live 与 insts 对齐才可信(删改后的过期 LiveInfo 索引错位会误删活指令); - // 守卫不过时活集必过期,调用方须重建。 - if live.inst_live_after.len() != f.insts.len() { + // 拍平后外层长度 = N×words,空 LiveInfo 的 words 为 0(0==N*0 会误判对齐), + // 须同时判 words > 0。守卫不过时活集必过期,调用方须重建。 + if live.inst_live_after.len() != f.insts.len() * live.words || live.words == 0 { return PreciseOutcome { live_reusable: false }; } let mut keep = vec![true; f.insts.len()]; diff --git a/crates/oxide_dce/src/precise_sweep.rs b/crates/oxide_dce/src/precise_sweep.rs index 990aedb5..2070baf5 100644 --- a/crates/oxide_dce/src/precise_sweep.rs +++ b/crates/oxide_dce/src/precise_sweep.rs @@ -38,7 +38,7 @@ pub(super) fn pass_dead_with_liveness(f: &IRFunction, live: &LiveInfo, keep: &mu if !keep[i] || label_target[i] { continue; } - let after = &live.inst_live_after[i]; + let after = &live.inst_live_after[i * live.words..(i + 1) * live.words]; let not_live_after = |r: u32| !oxide_liveness::bitset_get(after, r as usize); // 通用规则:纯指令结果不被后续使用 → 删(is_pure=false 已防 CALL/SPILL/UNSPILL) if let Some(r) = inst.def_reg() { diff --git a/crates/oxide_dce/tests/precise_unit.rs b/crates/oxide_dce/tests/precise_unit.rs index f89baa14..cbc388b6 100644 --- a/crates/oxide_dce/tests/precise_unit.rs +++ b/crates/oxide_dce/tests/precise_unit.rs @@ -6,23 +6,21 @@ use oxide_ir::operand::Operand; use oxide_ir::IRFunction; use oxide_liveness::LiveInfo; -/// helper:稀疏号集 → u64 位集 live_after。 -fn live_sets(after: &[&[u32]], reg_count: usize) -> Vec> { +/// helper:稀疏号集 → u64 位集 live_after(拍平为单一 `Vec`,长度 = 指令数 × words)。 +fn live_sets(after: &[&[u32]], reg_count: usize) -> Vec { let words = (reg_count + 1).div_ceil(64); - after - .iter() - .map(|set| { - let mut v = vec![0u64; words]; - for &r in *set { - v[(r as usize) >> 6] |= 1u64 << ((r as usize) & 63); - } - v - }) - .collect() + let mut flat = vec![0u64; after.len() * words]; + for (i, set) in after.iter().enumerate() { + for &r in *set { + flat[i * words + ((r as usize) >> 6)] |= 1u64 << ((r as usize) & 63); + } + } + flat } fn run_precise(f: &mut IRFunction, after: &[&[u32]], reg_count: usize) { let mut live = LiveInfo::new(); + live.words = (reg_count + 1).div_ceil(64); live.inst_live_after = live_sets(after, reg_count); dce_precise(f, &live); } diff --git a/crates/oxide_liveness/src/lib.rs b/crates/oxide_liveness/src/lib.rs index 853b5c4a..ecbf8634 100644 --- a/crates/oxide_liveness/src/lib.rs +++ b/crates/oxide_liveness/src/lib.rs @@ -29,5 +29,6 @@ pub fn liveness(f: &IRFunction, cfg: &Cfg) -> LiveInfo { block_live_out: block_out, inst_live_before: inst_before, inst_live_after: inst_after, + words: (reg_count + 1).div_ceil(64), } } diff --git a/crates/oxide_liveness/src/live_info.rs b/crates/oxide_liveness/src/live_info.rs index 8eab405c..ec8f3ee0 100644 --- a/crates/oxide_liveness/src/live_info.rs +++ b/crates/oxide_liveness/src/live_info.rs @@ -4,25 +4,29 @@ //! 由 dataflow 求上界)。位 r 落在第 `r >> 6` 字的 `r & 63` 位;越界位恒 0(消费方 //! 用 `bitset_get` 越界返回 false 对齐旧 `Vec::get` 语义)。四字段语义: //! - `block_live_in` / `block_live_out`:块级数据流迭代结果(精确 DCE 消费) -//! - `inst_live_before` / `inst_live_after`:逐指令活集(RegAlloc 干涉图消费 live_before) +//! - `inst_live_before` / `inst_live_after`:逐指令活集(RegAlloc 干涉图消费 live_before), +//! 拍平为单一 `Vec`,长度 N×words,消费方按 i×words 切片取指 /// liveness 输出:块级 liveIn/liveOut + 逐指令 live_before/live_after。 #[derive(Debug, Clone)] pub struct LiveInfo { pub block_live_in: Vec>, pub block_live_out: Vec>, - pub inst_live_before: Vec>, - pub inst_live_after: Vec>, + pub inst_live_before: Vec, + pub inst_live_after: Vec, + /// 位集字数(`(reg_count+1).div_ceil(64)`);空 LiveInfo 为 0,消费方守卫据此判过期。 + pub words: usize, } impl LiveInfo { - /// 构造空 LiveInfo:全空 vec,等价于 Default。 + /// 构造空 LiveInfo:全空 vec,words 为 0,等价于 Default。 pub fn new() -> Self { Self { block_live_in: Vec::new(), block_live_out: Vec::new(), inst_live_before: Vec::new(), inst_live_after: Vec::new(), + words: 0, } } } @@ -60,6 +64,7 @@ mod tests { assert!(info.block_live_out.is_empty()); assert!(info.inst_live_before.is_empty()); assert!(info.inst_live_after.is_empty()); + assert_eq!(info.words, 0, "空 LiveInfo 的 words 为 0(守卫判过期依据)"); let default = LiveInfo::default(); assert!(default.block_live_in.is_empty()); assert!(default.block_live_out.is_empty()); diff --git a/crates/oxide_liveness/src/ranges.rs b/crates/oxide_liveness/src/ranges.rs index 3b9c57dc..210bb78b 100644 --- a/crates/oxide_liveness/src/ranges.rs +++ b/crates/oxide_liveness/src/ranges.rs @@ -6,25 +6,28 @@ //! live_before 错误剔除。Exception 目标块入口 reg 0 隐式 def 在此截断,并以 //! `debug_assert_eq!(live, block_live_in[b])` 校验与 dataflow 一致性。 //! -//! 位集为 `u64` 压缩(与 dataflow 同 words 布局),逐指令集经 `copy_from_slice` 落地。 +//! 位集为 `u64` 压缩(与 dataflow 同 words 布局),逐指令集拍平为单一 `Vec` +//! (长度 N×words)经 `copy_from_slice` 落地。 use oxide_cfg::Cfg; use oxide_ir::IRFunction; use crate::live_info::{bitset_clear, bitset_set}; -/// 逐指令 liveness:返回 (inst_live_before, inst_live_after),长度 = insts.len()。 +/// 逐指令 liveness:返回 (inst_live_before, inst_live_after),拍平为单一 `Vec`, +/// 长度 = insts.len() × words。 pub(super) fn inst_liveness( f: &IRFunction, cfg: &Cfg, block_live_out: &[Vec], block_live_in: &[Vec], reg_count: usize, -) -> (Vec>, Vec>) { +) -> (Vec, Vec) { let words = (reg_count + 1).div_ceil(64); - let mut before = vec![vec![0u64; words]; f.insts.len()]; - let mut after = vec![vec![0u64; words]; f.insts.len()]; + let n = f.insts.len(); + let mut before = vec![0u64; n * words]; + let mut after = vec![0u64; n * words]; let exc_targets = crate::dataflow::exception_target_blocks(cfg); for (b, block) in cfg.blocks.iter().enumerate() { let mut live = block_live_out[b].clone(); // 从块级 liveOut 出发(非 liveIn) for i in block.inst_range.clone().rev() { - after[i].copy_from_slice(&live); + after[i * words..(i + 1) * words].copy_from_slice(&live); // kill 先于 gen:本指令先写后读。COMPOUND_ADD 等读-写同寄存器指令的 // uses 含 Rd,见 oxide_ir::contract 的 def/use 约定。 if let Some(d) = f.insts[i].def_reg() { @@ -33,7 +36,7 @@ pub(super) fn inst_liveness( for u in f.insts[i].use_regs() { bitset_set(&mut live, u as usize); } - before[i].copy_from_slice(&live); + before[i * words..(i + 1) * words].copy_from_slice(&live); } // Exception 目标块:入口 reg 0 隐式 def(异常展开写 regs[0])截断无 def use。 // preds 是 Vec(无 EdgeKind),从源块 succs 侧检测 Exception 边。 @@ -69,11 +72,12 @@ mod tests { let cfg = oxide_cfg::build_cfg(&f); let (live_in, live_out, rc) = block_liveness(&f, &cfg); let (before, after) = inst_liveness(&f, &cfg, &live_out, &live_in, rc); - assert!(bitset_get(&after[0], 2), "ADD 后 r2 存活(RETURN 用)"); - assert!(bitset_get(&before[0], 0) && bitset_get(&before[0], 1), "ADD 前 r0、r1 存活"); - assert!(!bitset_get(&before[0], 2), "ADD 前 r2 不死"); - assert!(after[1].iter().all(|&w| w == 0), "RETURN 后无存活"); - assert!(bitset_get(&before[1], 2), "RETURN 前 r2 存活"); + let words = (rc + 1).div_ceil(64); + assert!(bitset_get(&after[0..words], 2), "ADD 后 r2 存活(RETURN 用)"); + assert!(bitset_get(&before[0..words], 0) && bitset_get(&before[0..words], 1), "ADD 前 r0、r1 存活"); + assert!(!bitset_get(&before[0..words], 2), "ADD 前 r2 不死"); + assert!(after[words..2 * words].iter().all(|&w| w == 0), "RETURN 后无存活"); + assert!(bitset_get(&before[words..2 * words], 2), "RETURN 前 r2 存活"); } #[test] @@ -88,8 +92,9 @@ mod tests { let cfg = oxide_cfg::build_cfg(&f); let (live_in, live_out, rc) = block_liveness(&f, &cfg); let (before, _) = inst_liveness(&f, &cfg, &live_out, &live_in, rc); - assert!(bitset_get(&before[0], 1), "COMPOUND_ADD 前 r1 旧值在读(kill 先于 gen)"); - assert!(bitset_get(&before[0], 2), "COMPOUND_ADD 前 r2 存活"); + let words = (rc + 1).div_ceil(64); + assert!(bitset_get(&before[0..words], 1), "COMPOUND_ADD 前 r1 旧值在读(kill 先于 gen)"); + assert!(bitset_get(&before[0..words], 2), "COMPOUND_ADD 前 r2 存活"); assert!(bitset_get(&live_in[0], 1) && bitset_get(&live_in[0], 2), "块级 liveIn 含 r1、r2"); } @@ -107,9 +112,10 @@ mod tests { let cfg = oxide_cfg::build_cfg(&f); let (live_in, live_out, rc) = block_liveness(&f, &cfg); let (before, after) = inst_liveness(&f, &cfg, &live_out, &live_in, rc); - assert!(bitset_get(&before[1], 0), "LOAD_VAR 前读 CALL 结果 reg0"); - assert!(bitset_get(&after[0], 0), "CALL 后 reg0 存活(结果)"); - assert!(!bitset_get(&before[0], 0), "CALL 前 reg0 不死(区间仅 [CALL, LOAD_VAR))"); + let words = (rc + 1).div_ceil(64); + assert!(bitset_get(&before[words..2 * words], 0), "LOAD_VAR 前读 CALL 结果 reg0"); + assert!(bitset_get(&after[0..words], 0), "CALL 后 reg0 存活(结果)"); + assert!(!bitset_get(&before[0..words], 0), "CALL 前 reg0 不死(区间仅 [CALL, LOAD_VAR))"); assert!(!bitset_get(&live_in[0], 0), "reg0 不污染块入口"); } @@ -132,7 +138,11 @@ mod tests { let cfg = oxide_cfg::build_cfg(&f); let (live_in, live_out, rc) = block_liveness(&f, &cfg); let (before, _) = inst_liveness(&f, &cfg, &live_out, &live_in, rc); - assert!(bitset_get(&before[3], 0), "catch 块内 STORE_VAR 读 reg0(异常值,正确)"); + let words = (rc + 1).div_ceil(64); + assert!( + bitset_get(&before[3 * words..4 * words], 0), + "catch 块内 STORE_VAR 读 reg0(异常值,正确)" + ); let catch_block = cfg.blocks.iter().position(|b| b.inst_range == (3..5usize)).unwrap(); assert!(!bitset_get(&live_in[catch_block], 0), "catch 块入口 reg0 被隐式 def 截断"); assert!(!bitset_get(&live_in[0], 0), "try 块(entry)reg0 不污染入口"); diff --git a/crates/oxide_liveness/tests/smoke.rs b/crates/oxide_liveness/tests/smoke.rs index fac3ffb3..5bd3044f 100644 --- a/crates/oxide_liveness/tests/smoke.rs +++ b/crates/oxide_liveness/tests/smoke.rs @@ -16,8 +16,12 @@ fn smoke(src: &str) { let (info, cfg, inst_len) = run_liveness(src); assert_eq!(info.block_live_in.len(), cfg.blocks.len(), "block 维度一致"); assert_eq!(info.block_live_out.len(), cfg.blocks.len(), "block 维度一致"); - assert_eq!(info.inst_live_before.len(), inst_len, "inst 维度一致"); - assert_eq!(info.inst_live_after.len(), inst_len, "inst 维度一致"); + assert_eq!( + info.inst_live_before.len(), + inst_len * info.words, + "inst 维度一致(拍平后长度 = N×words)" + ); + assert_eq!(info.inst_live_after.len(), inst_len * info.words, "inst 维度一致(拍平后长度 = N×words)"); } #[test] diff --git a/crates/oxide_regalloc/src/call_window.rs b/crates/oxide_regalloc/src/call_window.rs index 031ac236..8a53f2ca 100644 --- a/crates/oxide_regalloc/src/call_window.rs +++ b/crates/oxide_regalloc/src/call_window.rs @@ -32,9 +32,10 @@ pub fn encode_call_window(f: &mut IRFunction, live: &LiveInfo) { .insts .iter() .any(|i| matches!(i.op, OpCode::TRY_BEGIN | OpCode::TRY_FINALLY_BEGIN)); - // LiveInfo 维度守卫:与 alloc 相同纪律,过期则重算。维度匹配时借用而非复制 - // (LiveInfo 稠密矩阵全量 clone 只读数据无收益)。 - let live = if live.inst_live_before.len() == f.insts.len() && !f.insts.is_empty() { + // LiveInfo 维度守卫:与 alloc 相同纪律,过期则重算。拍平后外层长度 = N×words, + // 空 LiveInfo 的 words 为 0(0==N*0 会误判对齐),须同时判 words > 0。 + // 维度匹配时借用而非复制(LiveInfo 稠密矩阵全量 clone 只读数据无收益)。 + let live = if live.inst_live_before.len() == f.insts.len() * live.words && live.words > 0 && !f.insts.is_empty() { Cow::Borrowed(live) } else { let cfg = oxide_cfg::build_cfg(f); @@ -45,7 +46,7 @@ pub fn encode_call_window(f: &mut IRFunction, live: &LiveInfo) { if !has_try && !f.is_generator && !f.is_async { for (i, inst) in f.insts.iter_mut().enumerate() { if matches!(inst.op, OpCode::CALL | OpCode::NEW_EXPRESSION | OpCode::SUPER_CALL) { - let after = &live.inst_live_after[i]; + let after = &live.inst_live_after[i * live.words..(i + 1) * live.words]; // 存活上界 = 最大存活槽号 + 1;254/255 由帧单独保存,不进窗口。 // 按 u64 位集逐字取置位(活集小,仅遍历实际存活的槽)。 let mut upper = 0u32; diff --git a/crates/oxide_regalloc/src/graph.rs b/crates/oxide_regalloc/src/graph.rs index eb893d91..c4c4220b 100644 --- a/crates/oxide_regalloc/src/graph.rs +++ b/crates/oxide_regalloc/src/graph.rs @@ -106,8 +106,8 @@ pub(super) fn collect_real_vregs(f: &IRFunction) -> Vec { /// spill_set 中的 vreg 不再是节点(其 fresh 替代品入图);fresh 按 at==i 并入该指令活集。 pub(super) fn build(f: &IRFunction, live: &LiveInfo, spill_set: &[u32], fresh: &[FreshVreg]) -> InterferenceGraph { let mut real = collect_real_vregs(f); - // inst_live_before 长度 = 指令数(逐指令活集索引) - let inst_count = live.inst_live_before.len(); + // inst_live_before 拍平后长度 = 指令数 × words,指令数由 len/words 还原(words 恒 ≥1) + let inst_count = live.inst_live_before.len() / live.words; // 窗口只为参数连续性 MOV 桥预留;spread 调用实参经 ext 逐个读寄存器,无连续性要求。 let max_nargs = f @@ -270,7 +270,7 @@ pub(super) fn build(f: &IRFunction, live: &LiveInfo, spill_set: &[u32], fresh: & // 替代逐 node 全扫描——活集小(典型 2-4)时每指令从 O(nodes) 降到 O(live)。 // 升序遍历保证加边顺序与原 node 表升序扫描逐字节一致(确定性不漂移)。 let mut at_i: Vec = Vec::new(); - let before = &live.inst_live_before[i]; + let before = &live.inst_live_before[i * live.words..(i + 1) * live.words]; for (word_idx, word) in before.iter().enumerate() { let mut bits = *word; while bits != 0 { @@ -298,7 +298,7 @@ pub(super) fn build(f: &IRFunction, live: &LiveInfo, spill_set: &[u32], fresh: & // 寄存器同色会覆盖其值(elision 解构返回值被覆盖),须补此干涉边。 if let Some(d) = f.insts[i].def_reg() { if oxide_liveness::bitset_get(&is_node, d as usize) { - let after = &live.inst_live_after[i]; + let after = &live.inst_live_after[i * live.words..(i + 1) * live.words]; for (word_idx, word) in after.iter().enumerate() { let mut bits = *word; while bits != 0 { diff --git a/crates/oxide_regalloc/src/lib.rs b/crates/oxide_regalloc/src/lib.rs index 31c48bad..b499c9f9 100644 --- a/crates/oxide_regalloc/src/lib.rs +++ b/crates/oxide_regalloc/src/lib.rs @@ -48,9 +48,10 @@ pub fn alloc(f: &mut IRFunction, live: &LiveInfo) -> Result<(), String> { return Ok(()); } // LiveInfo 维度守卫:删改后的过期 LiveInfo 索引错位会误删活指令。 - // 维度匹配时借用而非复制——LiveInfo 是稠密 bitset 矩阵(大函数可达数百 MB), - // 全量 clone 白白复制一次只读数据。 - let live = if live.inst_live_before.len() == f.insts.len() && !f.insts.is_empty() { + // 拍平后外层长度 = N×words,空 LiveInfo 的 words 为 0(0==N*0 会误判对齐), + // 须同时判 words > 0。维度匹配时借用而非复制——LiveInfo 是稠密 bitset 矩阵 + // (大函数可达数百 MB),全量 clone 白白复制一次只读数据。 + let live = if live.inst_live_before.len() == f.insts.len() * live.words && live.words > 0 && !f.insts.is_empty() { Cow::Borrowed(live) } else { let cfg = oxide_cfg::build_cfg(f);