mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-24 10:41:11 +08:00
perf: DCE 管线复用,零删除免重建 CFG 与活集
This commit is contained in:
@@ -12,6 +12,10 @@
|
||||
//!
|
||||
//! 精确二轮 `dce_precise`:liveness 驱动的死指令 + 局部死 STORE_VAR 删除,
|
||||
//! 消费 `oxide_liveness::LiveInfo`,mark-sweep 重建复用 Pass C。
|
||||
//!
|
||||
//! 两入口均暴露零删除事实供管线复用:`dce` 零删除时返回 Pass A 的 `Cfg`
|
||||
//! (`Some`,有删除为 `None`),`dce_precise` 返回 `PreciseOutcome`
|
||||
//! (`live_reusable` 标记第一轮活集是否仍有效)。
|
||||
|
||||
mod dce_log;
|
||||
mod iter_sweep;
|
||||
@@ -19,17 +23,24 @@ mod precise_sweep;
|
||||
mod reachability;
|
||||
mod rebuild;
|
||||
|
||||
use oxide_cfg::Cfg;
|
||||
use oxide_ir::IRFunction;
|
||||
use oxide_liveness::LiveInfo;
|
||||
|
||||
/// 死代码消除:块级不可达删除 + 全函数 use 计数迭代删除到不动点 + mark-sweep 重建。
|
||||
pub fn dce(f: &mut IRFunction) {
|
||||
// 空函数无指令可删,直接返回(no-op,f 不变)。
|
||||
///
|
||||
/// 返回 `Some(Cfg)`:零删除(含空函数早退),insts 未变,Pass A 的 CFG 仍有效,
|
||||
/// 调用方可直接复用于 liveness(无需重建)。返回 `None`:有删除,insts 已 compact,
|
||||
/// 旧 CFG 失效,调用方须重建。
|
||||
pub fn dce(f: &mut IRFunction) -> Option<Cfg> {
|
||||
// 空函数无指令可删,直接返回(no-op,f 不变);退化 CFG 与编译器对空函数自建
|
||||
// 的退化形态一致,保持「零删除 → Some」语义。
|
||||
if f.insts.is_empty() {
|
||||
return;
|
||||
return Some(oxide_cfg::build_cfg(f));
|
||||
}
|
||||
// Pass A:块级可达性(消费 build_cfg,Exception 边保守)→ 指令粒度 keep。
|
||||
let mut keep = reachability::pass_a_reachable(f);
|
||||
let cfg = oxide_cfg::build_cfg(f);
|
||||
let mut keep = reachability::pass_a_reachable(f, &cfg);
|
||||
// Pass B:可达存活指令上全函数 use 计数迭代删除到不动点(连锁删上游写者)。
|
||||
iter_sweep::pass_b_dead_code(f, &mut keep);
|
||||
let dead = keep.iter().filter(|k| !**k).count();
|
||||
@@ -39,20 +50,38 @@ pub fn dce(f: &mut IRFunction) {
|
||||
if dead > 0 {
|
||||
rebuild::pass_c_sweep(f, &keep);
|
||||
}
|
||||
// 零删除 ⇒ 函数未变 ⇒ Pass A CFG 仍有效(复用守卫与零删除判定同源)。
|
||||
if dead == 0 {
|
||||
Some(cfg)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// 精确二轮结果:`live_reusable` 为第一轮 liveness 是否仍可复用(零删除时真)。
|
||||
pub struct PreciseOutcome {
|
||||
/// 零删除:inst 下标未位移,传入活集仍有效,调用方可复用第一轮 CFG 与活集。
|
||||
pub live_reusable: bool,
|
||||
}
|
||||
|
||||
/// 精确二轮:liveness 驱动的死指令 + 局部死 STORE_VAR 删除。
|
||||
///
|
||||
/// 消费 oxide_liveness::LiveInfo(RegAlloc 管线传入;若 LiveInfo 已过期,重建 liveness
|
||||
/// 是调用方 RegAlloc 的职责,本 pass 不重复建分析引擎)。mark-sweep 重建复用既有 Pass C。
|
||||
pub fn dce_precise(f: &mut IRFunction, live: &LiveInfo) {
|
||||
// 空函数无指令可删,直接返回(no-op,f 不变)。
|
||||
/// 返回 `PreciseOutcome`:零删除时 `live_reusable` 为真(函数未变,第一轮活集仍有效)。
|
||||
pub fn dce_precise(f: &mut IRFunction, live: &LiveInfo) -> PreciseOutcome {
|
||||
// 空函数无指令可删,直接返回(no-op,f 不变);未改任何指令,活集可复用。
|
||||
if f.insts.is_empty() {
|
||||
return;
|
||||
return PreciseOutcome {
|
||||
live_reusable: true,
|
||||
};
|
||||
}
|
||||
// 维度守卫:live 与 insts 对齐才可信(删改后的过期 LiveInfo 索引错位会误删活指令)
|
||||
// 维度守卫:live 与 insts 对齐才可信(删改后的过期 LiveInfo 索引错位会误删活指令);
|
||||
// 守卫不过时活集必过期,调用方须重建。
|
||||
if live.inst_live_after.len() != f.insts.len() {
|
||||
return;
|
||||
return PreciseOutcome {
|
||||
live_reusable: false,
|
||||
};
|
||||
}
|
||||
let mut keep = vec![true; f.insts.len()];
|
||||
precise_sweep::pass_dead_with_liveness(f, live, &mut keep);
|
||||
@@ -61,4 +90,8 @@ pub fn dce_precise(f: &mut IRFunction, live: &LiveInfo) {
|
||||
if dead > 0 {
|
||||
rebuild::pass_c_sweep(f, &keep);
|
||||
}
|
||||
// 零删除 ⇒ 函数未变 ⇒ 第一轮活集仍有效(复用守卫与零删除判定同源)。
|
||||
PreciseOutcome {
|
||||
live_reusable: dead == 0,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,12 +3,13 @@
|
||||
//! 消费 `oxide_cfg::build_cfg` 的 Cfg,从 entry 沿 succs DFS(**遍历含 EdgeKind::Exception 边**,
|
||||
//! catch/finally 入口强制可达),不可达块的全部指令标记删除。
|
||||
|
||||
use oxide_cfg::build_cfg;
|
||||
use oxide_cfg::Cfg;
|
||||
use oxide_ir::IRFunction;
|
||||
|
||||
/// 从 entry 可达的指令位图(keep 前置:不可达指令为 false)。
|
||||
pub(super) fn pass_a_reachable(f: &IRFunction) -> Vec<bool> {
|
||||
let cfg = build_cfg(f);
|
||||
///
|
||||
/// 消费调用方传入的 `Cfg`(`dce` 建一次复用,不重复构建)。
|
||||
pub(super) fn pass_a_reachable(f: &IRFunction, cfg: &Cfg) -> Vec<bool> {
|
||||
let mut reachable = vec![false; cfg.blocks.len()];
|
||||
let mut stack = vec![cfg.entry];
|
||||
while let Some(b) = stack.pop() {
|
||||
@@ -47,7 +48,8 @@ mod tests {
|
||||
f.insts.push(Inst::load_const(Operand::Reg(1), 0));
|
||||
f.insts
|
||||
.push(Inst::new(OpCode::RETURN, Operand::Reg(1), Operand::None, Operand::None));
|
||||
let keep = pass_a_reachable(&f);
|
||||
let cfg = oxide_cfg::build_cfg(&f);
|
||||
let keep = pass_a_reachable(&f, &cfg);
|
||||
assert!(keep.iter().all(|k| *k), "线性函数全指令可达");
|
||||
}
|
||||
|
||||
@@ -63,7 +65,8 @@ mod tests {
|
||||
f.label_pos = vec![Some(1)];
|
||||
f.label_count = 1;
|
||||
|
||||
let keep = pass_a_reachable(&f);
|
||||
let cfg = oxide_cfg::build_cfg(&f);
|
||||
let keep = pass_a_reachable(&f, &cfg);
|
||||
assert!(keep[0], "RETURN 指令可达");
|
||||
assert!(!keep[1] && !keep[2], "return 后死块不可达");
|
||||
}
|
||||
@@ -83,7 +86,8 @@ mod tests {
|
||||
f.label_pos = vec![Some(3)];
|
||||
f.label_count = 1;
|
||||
|
||||
let keep = pass_a_reachable(&f);
|
||||
let cfg = oxide_cfg::build_cfg(&f);
|
||||
let keep = pass_a_reachable(&f, &cfg);
|
||||
assert!(keep[3] && keep[4], "catch 入口经 Exception 边强制可达");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user