perf: live 集改 u64 位集 + 不动点缓冲复用

LiveInfo 四矩阵由逐位 Vec<bool>(1 字节/位,长度 = max_vreg+1)改为
u64 压缩位集(1 字 64 位,长度 ÷8),dataflow 不动点 out/input 缓冲
提为循环外复用(clear + 覆盖,免逐迭代逐块分配)。dce_precise /
graph 建图 / call_window 三处消费方同步改为字内位移查询,越界语义
与旧 Vec<bool>::get 一致(越界 = 不 live)。位集遍历(call_window
存活上界)按 trailing_zeros 只走实际置位,活集小则近 O(活集) 而非
O(254)。

实测(debug,start-unicode-17.0.0.js):liveness 三趟合计
~536ms → ~118ms(-78%),矩阵内存 ÷8;graph 全 node 扫描为剩余
瓶颈,另行跟进。
This commit is contained in:
2026-08-16 03:20:02 +08:00
parent 3962a329bd
commit b8611d2c4b
8 changed files with 117 additions and 82 deletions
+30 -25
View File
@@ -5,29 +5,33 @@
//! 同寄存器指令 use 含 rd(contract.rs:137),gen 先于 kill 会把 rd 旧值从
//! live_before 错误剔除。Exception 目标块入口 reg 0 隐式 def 在此截断,并以
//! `debug_assert_eq!(live, block_live_in[b])` 校验与 dataflow 一致性。
//!
//! 位集为 `u64` 压缩(与 dataflow 同 words 布局),逐指令集经 `copy_from_slice` 落地。
use oxide_cfg::{Cfg, EdgeKind};
use oxide_ir::IRFunction;
use crate::live_info::{bitset_clear, bitset_set};
/// 逐指令 liveness:返回 (inst_live_before, inst_live_after),长度 = insts.len()。
pub(super) fn inst_liveness(
f: &IRFunction, cfg: &Cfg, block_live_out: &[Vec<bool>], block_live_in: &[Vec<bool>], reg_count: usize,
) -> (Vec<Vec<bool>>, Vec<Vec<bool>>) {
let bits = reg_count + 1;
let mut before = vec![vec![false; bits]; f.insts.len()];
let mut after = vec![vec![false; bits]; f.insts.len()];
f: &IRFunction, cfg: &Cfg, block_live_out: &[Vec<u64>], block_live_in: &[Vec<u64>], reg_count: usize,
) -> (Vec<Vec<u64>>, Vec<Vec<u64>>) {
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()];
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] = live.clone();
after[i].copy_from_slice(&live);
// kill 先于 gen:本指令先写后读(COMPOUND_ADD 等读-写同寄存器,contract.rs:137)
if let Some(d) = f.insts[i].def_reg() {
live[d as usize] = false;
bitset_clear(&mut live, d as usize);
}
for u in f.insts[i].use_regs() {
live[u as usize] = true;
bitset_set(&mut live, u as usize);
}
before[i] = live.clone();
before[i].copy_from_slice(&live);
}
// Exception 目标块:入口 reg 0 隐式 def(异常展开写 regs[0])截断无 def use。
// preds 是 Vec<BBId>(无 EdgeKind),从源块 succs 侧检测 Exception 边。
@@ -36,7 +40,7 @@ pub(super) fn inst_liveness(
.iter()
.any(|src| src.succs.iter().any(|&(dst, k)| dst == b && k == EdgeKind::Exception));
if is_exception_target {
live[0] = false;
bitset_clear(&mut live, 0);
}
debug_assert_eq!(live, block_live_in[b], "ranges 反向扫描与 dataflow liveIn 不一致,块 {b}");
}
@@ -47,6 +51,7 @@ pub(super) fn inst_liveness(
mod tests {
use super::*;
use crate::dataflow::block_liveness;
use crate::live_info::bitset_get;
use oxide_bytecode::opcode::OpCode;
use oxide_ir::inst::Inst;
use oxide_ir::operand::Operand;
@@ -66,11 +71,11 @@ 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!(after[0][2], "ADD 后 r2 存活(RETURN 用)");
assert!(before[0][0] && before[0][1], "ADD 前 r0、r1 存活");
assert!(!before[0][2], "ADD 前 r2 不死");
assert!(after[1].iter().all(|&v| !v), "RETURN 后无存活");
assert!(before[1][2], "RETURN 前 r2 存活");
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 存活");
}
#[test]
@@ -85,9 +90,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!(before[0][1], "COMPOUND_ADD 前 r1 旧值在读(kill 先于 gen)");
assert!(before[0][2], "COMPOUND_ADD 前 r2 存活");
assert!(live_in[0][1] && live_in[0][2], "块级 liveIn 含 r1、r2");
assert!(bitset_get(&before[0], 1), "COMPOUND_ADD 前 r1 旧值在读(kill 先于 gen)");
assert!(bitset_get(&before[0], 2), "COMPOUND_ADD 前 r2 存活");
assert!(bitset_get(&live_in[0], 1) && bitset_get(&live_in[0], 2), "块级 liveIn 含 r1、r2");
}
#[test]
@@ -104,10 +109,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!(before[1][0], "LOAD_VAR 前读 CALL 结果 reg0");
assert!(after[0][0], "CALL 后 reg0 存活(结果)");
assert!(!before[0][0], "CALL 前 reg0 不死(区间仅 [CALL, LOAD_VAR))");
assert!(!live_in[0][0], "reg0 不污染块入口");
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))");
assert!(!bitset_get(&live_in[0], 0), "reg0 不污染块入口");
}
#[test]
@@ -129,9 +134,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!(before[3][0], "catch 块内 STORE_VAR 读 reg0(异常值,正确)");
assert!(bitset_get(&before[3], 0), "catch 块内 STORE_VAR 读 reg0(异常值,正确)");
let catch_block = cfg.blocks.iter().position(|b| b.inst_range == (3..5usize)).unwrap();
assert!(!live_in[catch_block][0], "catch 块入口 reg0 被隐式 def 截断");
assert!(!live_in[0][0], "try 块(entry)reg0 不污染入口");
assert!(!bitset_get(&live_in[catch_block], 0), "catch 块入口 reg0 被隐式 def 截断");
assert!(!bitset_get(&live_in[0], 0), "try 块(entry)reg0 不污染入口");
}
}