mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-23 18:21:13 +08:00
perf: DCE 管线复用,零删除免重建 CFG 与活集
This commit is contained in:
Generated
+20
-20
@@ -1160,7 +1160,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_builtins"
|
name = "oxide_builtins"
|
||||||
version = "0.16.10"
|
version = "0.16.11"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
"fast-float",
|
"fast-float",
|
||||||
@@ -1185,7 +1185,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_bytecode"
|
name = "oxide_bytecode"
|
||||||
version = "0.16.10"
|
version = "0.16.11"
|
||||||
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.10"
|
version = "0.16.11"
|
||||||
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.10"
|
version = "0.16.11"
|
||||||
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.10"
|
version = "0.16.11"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"lru",
|
"lru",
|
||||||
"oxide_bytecode",
|
"oxide_bytecode",
|
||||||
@@ -1237,7 +1237,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_compiler"
|
name = "oxide_compiler"
|
||||||
version = "0.16.10"
|
version = "0.16.11"
|
||||||
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.10"
|
version = "0.16.11"
|
||||||
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.10"
|
version = "0.16.11"
|
||||||
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.10"
|
version = "0.16.11"
|
||||||
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.10"
|
version = "0.16.11"
|
||||||
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.10"
|
version = "0.16.11"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bumpalo",
|
"bumpalo",
|
||||||
"dashmap",
|
"dashmap",
|
||||||
@@ -1321,7 +1321,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_liveness"
|
name = "oxide_liveness"
|
||||||
version = "0.16.10"
|
version = "0.16.11"
|
||||||
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.10"
|
version = "0.16.11"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"tracing",
|
"tracing",
|
||||||
"tracing-appender",
|
"tracing-appender",
|
||||||
@@ -1342,11 +1342,11 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_net"
|
name = "oxide_net"
|
||||||
version = "0.16.10"
|
version = "0.16.11"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_parser"
|
name = "oxide_parser"
|
||||||
version = "0.16.10"
|
version = "0.16.11"
|
||||||
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.10"
|
version = "0.16.11"
|
||||||
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.10"
|
version = "0.16.11"
|
||||||
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.10"
|
version = "0.16.11"
|
||||||
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.10"
|
version = "0.16.11"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bumpalo",
|
"bumpalo",
|
||||||
"dashmap",
|
"dashmap",
|
||||||
@@ -1423,7 +1423,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oxide_vm"
|
name = "oxide_vm"
|
||||||
version = "0.16.10"
|
version = "0.16.11"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bumpalo",
|
"bumpalo",
|
||||||
"chrono",
|
"chrono",
|
||||||
|
|||||||
+1
-1
@@ -24,7 +24,7 @@ members = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
version = "0.16.10"
|
version = "0.16.11"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
rust-version = "1.80"
|
rust-version = "1.80"
|
||||||
|
|||||||
@@ -93,24 +93,37 @@ impl Compiler {
|
|||||||
|
|
||||||
/// 统一 IR 后处理管线:(可选 DCE)→(可选 liveness/精确 DCE/RegAlloc)→
|
/// 统一 IR 后处理管线:(可选 DCE)→(可选 liveness/精确 DCE/RegAlloc)→
|
||||||
/// bytecode(`lower`)→ 子模块拍平。
|
/// bytecode(`lower`)→ 子模块拍平。
|
||||||
|
///
|
||||||
|
/// 零删除时复用分析产物:DCE 零删除返回其 Pass A CFG(免重建),精确 DCE
|
||||||
|
/// 零删除返回 `live_reusable`(免第二轮 CFG/活集重建)。
|
||||||
fn compile_ir(&self, mut ir: oxide_ir::IRFunction) -> Result<CompiledModule, String> {
|
fn compile_ir(&self, mut ir: oxide_ir::IRFunction) -> Result<CompiledModule, String> {
|
||||||
crate::compiler_debug!("compile_ir: after emit {} insts", ir.insts.len());
|
crate::compiler_debug!("compile_ir: after emit {} insts", ir.insts.len());
|
||||||
if self.enable_dce {
|
// DCE 零删除时返回 Pass A CFG 供 liveness 复用;有删除或 DCE 关闭时为 None(须重建)。
|
||||||
oxide_dce::dce(&mut ir); // 顶层函数;nested 不递归
|
let dce_cfg: Option<oxide_cfg::Cfg> = if self.enable_dce {
|
||||||
|
let cfg = oxide_dce::dce(&mut ir); // 顶层函数;nested 不递归
|
||||||
crate::compiler_debug!("compile: after DCE {} insts", ir.insts.len());
|
crate::compiler_debug!("compile: after DCE {} insts", ir.insts.len());
|
||||||
}
|
cfg
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
if self.enable_regalloc && !ir.const_overflow {
|
if self.enable_regalloc && !ir.const_overflow {
|
||||||
// 优化管线:liveness → 精确 DCE → 重算 liveness → RegAlloc → 调用窗口编码
|
// 优化管线:liveness → 精确 DCE → 重算 liveness → RegAlloc → 调用窗口编码
|
||||||
// const_overflow 时跳过:程序已知无效(lower 报 "too many constants"),
|
// const_overflow 时跳过:程序已知无效(lower 报 "too many constants"),
|
||||||
// 且超大常量池伴生的海量虚拟寄存器会使 liveness 稠密 bitset 爆内存。
|
// 且超大常量池伴生的海量虚拟寄存器会使 liveness 稠密 bitset 爆内存。
|
||||||
let cfg = oxide_cfg::build_cfg(&ir);
|
// 零删除复用 DCE 的 Pass A CFG;DCE 关闭或有删除时重建
|
||||||
|
let cfg = dce_cfg.unwrap_or_else(|| oxide_cfg::build_cfg(&ir));
|
||||||
let live = oxide_liveness::liveness(&ir, &cfg);
|
let live = oxide_liveness::liveness(&ir, &cfg);
|
||||||
// 精确 DCE 以第一轮 liveness 的指令级活集为输入,删除死指令
|
// 精确 DCE 以第一轮 liveness 的指令级活集为输入,删除死指令
|
||||||
oxide_dce::dce_precise(&mut ir, &live);
|
let outcome = oxide_dce::dce_precise(&mut ir, &live);
|
||||||
// 删指令后 inst 下标位移,旧 CFG 失效,按新程序重建
|
// 零删除时函数未变,复用第一轮活集;有删除时 inst 下标位移,须重建
|
||||||
let cfg2 = oxide_cfg::build_cfg(&ir);
|
// CFG 并重算活集(重建的 CFG 仅服务活集计算,不单独命名)。
|
||||||
// 指令集已变,旧活集失效,须重算
|
let (_, live2) = if outcome.live_reusable {
|
||||||
let live2 = oxide_liveness::liveness(&ir, &cfg2);
|
(cfg, live)
|
||||||
|
} else {
|
||||||
|
let cfg2 = oxide_cfg::build_cfg(&ir);
|
||||||
|
let live2 = oxide_liveness::liveness(&ir, &cfg2);
|
||||||
|
(cfg2, live2)
|
||||||
|
};
|
||||||
// 无可行染色 → RangeError 上抛
|
// 无可行染色 → RangeError 上抛
|
||||||
oxide_regalloc::alloc(&mut ir, &live2)?;
|
oxide_regalloc::alloc(&mut ir, &live2)?;
|
||||||
// RegAlloc 改写指令(虚拟寄存器→物理号并插入 spill)后旧活集失效,
|
// RegAlloc 改写指令(虚拟寄存器→物理号并插入 spill)后旧活集失效,
|
||||||
|
|||||||
@@ -12,6 +12,10 @@
|
|||||||
//!
|
//!
|
||||||
//! 精确二轮 `dce_precise`:liveness 驱动的死指令 + 局部死 STORE_VAR 删除,
|
//! 精确二轮 `dce_precise`:liveness 驱动的死指令 + 局部死 STORE_VAR 删除,
|
||||||
//! 消费 `oxide_liveness::LiveInfo`,mark-sweep 重建复用 Pass C。
|
//! 消费 `oxide_liveness::LiveInfo`,mark-sweep 重建复用 Pass C。
|
||||||
|
//!
|
||||||
|
//! 两入口均暴露零删除事实供管线复用:`dce` 零删除时返回 Pass A 的 `Cfg`
|
||||||
|
//! (`Some`,有删除为 `None`),`dce_precise` 返回 `PreciseOutcome`
|
||||||
|
//! (`live_reusable` 标记第一轮活集是否仍有效)。
|
||||||
|
|
||||||
mod dce_log;
|
mod dce_log;
|
||||||
mod iter_sweep;
|
mod iter_sweep;
|
||||||
@@ -19,17 +23,24 @@ mod precise_sweep;
|
|||||||
mod reachability;
|
mod reachability;
|
||||||
mod rebuild;
|
mod rebuild;
|
||||||
|
|
||||||
|
use oxide_cfg::Cfg;
|
||||||
use oxide_ir::IRFunction;
|
use oxide_ir::IRFunction;
|
||||||
use oxide_liveness::LiveInfo;
|
use oxide_liveness::LiveInfo;
|
||||||
|
|
||||||
/// 死代码消除:块级不可达删除 + 全函数 use 计数迭代删除到不动点 + mark-sweep 重建。
|
/// 死代码消除:块级不可达删除 + 全函数 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() {
|
if f.insts.is_empty() {
|
||||||
return;
|
return Some(oxide_cfg::build_cfg(f));
|
||||||
}
|
}
|
||||||
// Pass A:块级可达性(消费 build_cfg,Exception 边保守)→ 指令粒度 keep。
|
// 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 计数迭代删除到不动点(连锁删上游写者)。
|
// Pass B:可达存活指令上全函数 use 计数迭代删除到不动点(连锁删上游写者)。
|
||||||
iter_sweep::pass_b_dead_code(f, &mut keep);
|
iter_sweep::pass_b_dead_code(f, &mut keep);
|
||||||
let dead = keep.iter().filter(|k| !**k).count();
|
let dead = keep.iter().filter(|k| !**k).count();
|
||||||
@@ -39,20 +50,38 @@ pub fn dce(f: &mut IRFunction) {
|
|||||||
if dead > 0 {
|
if dead > 0 {
|
||||||
rebuild::pass_c_sweep(f, &keep);
|
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 删除。
|
/// 精确二轮:liveness 驱动的死指令 + 局部死 STORE_VAR 删除。
|
||||||
///
|
///
|
||||||
/// 消费 oxide_liveness::LiveInfo(RegAlloc 管线传入;若 LiveInfo 已过期,重建 liveness
|
/// 消费 oxide_liveness::LiveInfo(RegAlloc 管线传入;若 LiveInfo 已过期,重建 liveness
|
||||||
/// 是调用方 RegAlloc 的职责,本 pass 不重复建分析引擎)。mark-sweep 重建复用既有 Pass C。
|
/// 是调用方 RegAlloc 的职责,本 pass 不重复建分析引擎)。mark-sweep 重建复用既有 Pass C。
|
||||||
pub fn dce_precise(f: &mut IRFunction, live: &LiveInfo) {
|
/// 返回 `PreciseOutcome`:零删除时 `live_reusable` 为真(函数未变,第一轮活集仍有效)。
|
||||||
// 空函数无指令可删,直接返回(no-op,f 不变)。
|
pub fn dce_precise(f: &mut IRFunction, live: &LiveInfo) -> PreciseOutcome {
|
||||||
|
// 空函数无指令可删,直接返回(no-op,f 不变);未改任何指令,活集可复用。
|
||||||
if f.insts.is_empty() {
|
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() {
|
if live.inst_live_after.len() != f.insts.len() {
|
||||||
return;
|
return PreciseOutcome {
|
||||||
|
live_reusable: false,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
let mut keep = vec![true; f.insts.len()];
|
let mut keep = vec![true; f.insts.len()];
|
||||||
precise_sweep::pass_dead_with_liveness(f, live, &mut keep);
|
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 {
|
if dead > 0 {
|
||||||
rebuild::pass_c_sweep(f, &keep);
|
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 边**,
|
//! 消费 `oxide_cfg::build_cfg` 的 Cfg,从 entry 沿 succs DFS(**遍历含 EdgeKind::Exception 边**,
|
||||||
//! catch/finally 入口强制可达),不可达块的全部指令标记删除。
|
//! catch/finally 入口强制可达),不可达块的全部指令标记删除。
|
||||||
|
|
||||||
use oxide_cfg::build_cfg;
|
use oxide_cfg::Cfg;
|
||||||
use oxide_ir::IRFunction;
|
use oxide_ir::IRFunction;
|
||||||
|
|
||||||
/// 从 entry 可达的指令位图(keep 前置:不可达指令为 false)。
|
/// 从 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 reachable = vec![false; cfg.blocks.len()];
|
||||||
let mut stack = vec![cfg.entry];
|
let mut stack = vec![cfg.entry];
|
||||||
while let Some(b) = stack.pop() {
|
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::load_const(Operand::Reg(1), 0));
|
||||||
f.insts
|
f.insts
|
||||||
.push(Inst::new(OpCode::RETURN, Operand::Reg(1), Operand::None, Operand::None));
|
.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), "线性函数全指令可达");
|
assert!(keep.iter().all(|k| *k), "线性函数全指令可达");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,7 +65,8 @@ mod tests {
|
|||||||
f.label_pos = vec![Some(1)];
|
f.label_pos = vec![Some(1)];
|
||||||
f.label_count = 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[0], "RETURN 指令可达");
|
||||||
assert!(!keep[1] && !keep[2], "return 后死块不可达");
|
assert!(!keep[1] && !keep[2], "return 后死块不可达");
|
||||||
}
|
}
|
||||||
@@ -83,7 +86,8 @@ mod tests {
|
|||||||
f.label_pos = vec![Some(3)];
|
f.label_pos = vec![Some(3)];
|
||||||
f.label_count = 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[3] && keep[4], "catch 入口经 Exception 边强制可达");
|
assert!(keep[3] && keep[4], "catch 入口经 Exception 边强制可达");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ use oxide_ir::IRFunction;
|
|||||||
#[test]
|
#[test]
|
||||||
fn empty_function_unchanged() {
|
fn empty_function_unchanged() {
|
||||||
let mut f = IRFunction::new();
|
let mut f = IRFunction::new();
|
||||||
dce(&mut f);
|
let _ = dce(&mut f);
|
||||||
assert!(f.insts.is_empty());
|
assert!(f.insts.is_empty());
|
||||||
assert!(f.label_pos.is_empty());
|
assert!(f.label_pos.is_empty());
|
||||||
assert_eq!(f.label_count, 0);
|
assert_eq!(f.label_count, 0);
|
||||||
@@ -29,10 +29,10 @@ fn dce_is_idempotent() {
|
|||||||
.push(Inst::new(OpCode::RETURN, Operand::Reg(5), Operand::None, Operand::None)); // 4
|
.push(Inst::new(OpCode::RETURN, Operand::Reg(5), Operand::None, Operand::None)); // 4
|
||||||
f.label_pos = vec![Some(0)];
|
f.label_pos = vec![Some(0)];
|
||||||
|
|
||||||
dce(&mut f);
|
let _ = dce(&mut f);
|
||||||
let once_insts = f.insts.clone();
|
let once_insts = f.insts.clone();
|
||||||
let once_labels = f.label_pos.clone();
|
let once_labels = f.label_pos.clone();
|
||||||
dce(&mut f);
|
let _ = dce(&mut f);
|
||||||
assert_eq!(f.insts, once_insts, "二次 dce 后 insts 不变");
|
assert_eq!(f.insts, once_insts, "二次 dce 后 insts 不变");
|
||||||
assert_eq!(f.label_pos, once_labels, "二次 dce 后 label_pos 不变");
|
assert_eq!(f.label_pos, once_labels, "二次 dce 后 label_pos 不变");
|
||||||
}
|
}
|
||||||
@@ -50,7 +50,7 @@ fn if_else_both_branches_reachable_kept() {
|
|||||||
f.label_pos = vec![Some(3), Some(4)];
|
f.label_pos = vec![Some(3), Some(4)];
|
||||||
f.label_count = 2;
|
f.label_count = 2;
|
||||||
|
|
||||||
dce(&mut f);
|
let _ = dce(&mut f);
|
||||||
assert_eq!(f.insts.len(), 5, "双分支全保留");
|
assert_eq!(f.insts.len(), 5, "双分支全保留");
|
||||||
assert_eq!(f.label_pos, vec![Some(3), Some(4)], "存活 label 重映射到新下标");
|
assert_eq!(f.label_pos, vec![Some(3), Some(4)], "存活 label 重映射到新下标");
|
||||||
assert_eq!(f.label_count, 2, "label_count 保持原值,不参与重建");
|
assert_eq!(f.label_count, 2, "label_count 保持原值,不参与重建");
|
||||||
@@ -71,7 +71,7 @@ fn all_live_function_unchanged() {
|
|||||||
|
|
||||||
let before_insts = f.insts.clone();
|
let before_insts = f.insts.clone();
|
||||||
let before_labels = f.label_pos.clone();
|
let before_labels = f.label_pos.clone();
|
||||||
dce(&mut f);
|
let _ = dce(&mut f);
|
||||||
assert_eq!(f.insts, before_insts, "全存活时 insts 逐位不变");
|
assert_eq!(f.insts, before_insts, "全存活时 insts 逐位不变");
|
||||||
assert_eq!(f.label_pos, before_labels, "全存活时 label_pos 逐位不变");
|
assert_eq!(f.label_pos, before_labels, "全存活时 label_pos 逐位不变");
|
||||||
assert_eq!(f.label_count, 1, "label_count 保持原值");
|
assert_eq!(f.label_count, 1, "label_count 保持原值");
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ fn run_source(src: &str, precise: bool) -> Result<JsValue, String> {
|
|||||||
let mut ir = oxide_emit::Emitter::new()
|
let mut ir = oxide_emit::Emitter::new()
|
||||||
.emit_program(&program, false, false)
|
.emit_program(&program, false, false)
|
||||||
.expect("emit failed");
|
.expect("emit failed");
|
||||||
oxide_dce::dce(&mut ir); // 保守轮恒定(两跑一致)
|
let _ = oxide_dce::dce(&mut ir); // 保守轮恒定(两跑一致)
|
||||||
if precise {
|
if precise {
|
||||||
let cfg = oxide_cfg::build_cfg(&ir);
|
let cfg = oxide_cfg::build_cfg(&ir);
|
||||||
let live = oxide_liveness::liveness(&ir, &cfg);
|
let live = oxide_liveness::liveness(&ir, &cfg);
|
||||||
@@ -78,7 +78,7 @@ fn top_level_store_var_survives_precise() {
|
|||||||
let mut ir = oxide_emit::Emitter::new()
|
let mut ir = oxide_emit::Emitter::new()
|
||||||
.emit_program(&program, false, false)
|
.emit_program(&program, false, false)
|
||||||
.expect("emit failed");
|
.expect("emit failed");
|
||||||
oxide_dce::dce(&mut ir);
|
let _ = oxide_dce::dce(&mut ir);
|
||||||
let cfg = oxide_cfg::build_cfg(&ir);
|
let cfg = oxide_cfg::build_cfg(&ir);
|
||||||
let live = oxide_liveness::liveness(&ir, &cfg);
|
let live = oxide_liveness::liveness(&ir, &cfg);
|
||||||
oxide_dce::dce_precise(&mut ir, &live);
|
oxide_dce::dce_precise(&mut ir, &live);
|
||||||
@@ -104,7 +104,7 @@ fn precise_removes_dead_local_store() {
|
|||||||
.expect("emit failed");
|
.expect("emit failed");
|
||||||
assert_eq!(ir.nested.len(), 1);
|
assert_eq!(ir.nested.len(), 1);
|
||||||
let mut sub = ir.nested[0].clone();
|
let mut sub = ir.nested[0].clone();
|
||||||
oxide_dce::dce(&mut sub);
|
let _ = oxide_dce::dce(&mut sub);
|
||||||
let cfg = oxide_cfg::build_cfg(&sub);
|
let cfg = oxide_cfg::build_cfg(&sub);
|
||||||
let live = oxide_liveness::liveness(&sub, &cfg);
|
let live = oxide_liveness::liveness(&sub, &cfg);
|
||||||
oxide_dce::dce_precise(&mut sub, &live);
|
oxide_dce::dce_precise(&mut sub, &live);
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ fn run_source(src: &str, dce: bool) -> Result<JsValue, String> {
|
|||||||
.emit_program(&program, false, false)
|
.emit_program(&program, false, false)
|
||||||
.expect("emit failed");
|
.expect("emit failed");
|
||||||
if dce {
|
if dce {
|
||||||
oxide_dce::dce(&mut ir);
|
let _ = oxide_dce::dce(&mut ir);
|
||||||
}
|
}
|
||||||
let module = oxide_ir::lower::lower(&ir).expect("lower failed");
|
let module = oxide_ir::lower::lower(&ir).expect("lower failed");
|
||||||
let mut vm = Vm::new();
|
let mut vm = Vm::new();
|
||||||
@@ -80,7 +80,7 @@ fn top_level_store_var_kept() {
|
|||||||
let mut ir = oxide_emit::Emitter::new()
|
let mut ir = oxide_emit::Emitter::new()
|
||||||
.emit_program(&program, false, false)
|
.emit_program(&program, false, false)
|
||||||
.expect("emit failed");
|
.expect("emit failed");
|
||||||
oxide_dce::dce(&mut ir);
|
let _ = oxide_dce::dce(&mut ir);
|
||||||
let module = oxide_ir::lower::lower(&ir).expect("lower failed");
|
let module = oxide_ir::lower::lower(&ir).expect("lower failed");
|
||||||
let has_store_var = module
|
let has_store_var = module
|
||||||
.bytecode
|
.bytecode
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ fn run_source(src: &str, regalloc: bool) -> (bool, String) {
|
|||||||
Ok(i) => i,
|
Ok(i) => i,
|
||||||
Err(e) => return (false, format!("emit error: {e}")),
|
Err(e) => return (false, format!("emit error: {e}")),
|
||||||
};
|
};
|
||||||
oxide_dce::dce(&mut ir);
|
let _ = oxide_dce::dce(&mut ir);
|
||||||
if regalloc {
|
if regalloc {
|
||||||
let cfg = oxide_cfg::build_cfg(&ir);
|
let cfg = oxide_cfg::build_cfg(&ir);
|
||||||
let live = oxide_liveness::liveness(&ir, &cfg);
|
let live = oxide_liveness::liveness(&ir, &cfg);
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ fn n_registers(src: &str, regalloc: bool) -> Result<u8, String> {
|
|||||||
let mut ir = oxide_emit::Emitter::new()
|
let mut ir = oxide_emit::Emitter::new()
|
||||||
.emit_program(&program, false, false)
|
.emit_program(&program, false, false)
|
||||||
.expect("emit failed");
|
.expect("emit failed");
|
||||||
oxide_dce::dce(&mut ir);
|
let _ = oxide_dce::dce(&mut ir);
|
||||||
if regalloc {
|
if regalloc {
|
||||||
let cfg = oxide_cfg::build_cfg(&ir);
|
let cfg = oxide_cfg::build_cfg(&ir);
|
||||||
let live = oxide_liveness::liveness(&ir, &cfg);
|
let live = oxide_liveness::liveness(&ir, &cfg);
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ fn compile_source(src: &str, regalloc: bool) -> Result<CompiledModule, String> {
|
|||||||
let mut ir = oxide_emit::Emitter::new()
|
let mut ir = oxide_emit::Emitter::new()
|
||||||
.emit_program(&program, false, false)
|
.emit_program(&program, false, false)
|
||||||
.expect("emit failed");
|
.expect("emit failed");
|
||||||
oxide_dce::dce(&mut ir);
|
let _ = oxide_dce::dce(&mut ir);
|
||||||
if regalloc {
|
if regalloc {
|
||||||
let cfg = oxide_cfg::build_cfg(&ir);
|
let cfg = oxide_cfg::build_cfg(&ir);
|
||||||
let live = oxide_liveness::liveness(&ir, &cfg);
|
let live = oxide_liveness::liveness(&ir, &cfg);
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ fn run_source(src: &str, regalloc: bool) -> Result<JsValue, String> {
|
|||||||
let mut ir = oxide_emit::Emitter::new()
|
let mut ir = oxide_emit::Emitter::new()
|
||||||
.emit_program(&program, false, false)
|
.emit_program(&program, false, false)
|
||||||
.expect("emit failed");
|
.expect("emit failed");
|
||||||
oxide_dce::dce(&mut ir);
|
let _ = oxide_dce::dce(&mut ir);
|
||||||
if regalloc {
|
if regalloc {
|
||||||
let cfg = oxide_cfg::build_cfg(&ir);
|
let cfg = oxide_cfg::build_cfg(&ir);
|
||||||
let live = oxide_liveness::liveness(&ir, &cfg);
|
let live = oxide_liveness::liveness(&ir, &cfg);
|
||||||
@@ -128,7 +128,7 @@ fn large_and_spill_samples_run_correctly() {
|
|||||||
let allocator = oxide_parser::Allocator::default();
|
let allocator = oxide_parser::Allocator::default();
|
||||||
let program = oxide_parser::parse(&allocator, &src_spill).expect("parse");
|
let program = oxide_parser::parse(&allocator, &src_spill).expect("parse");
|
||||||
let mut ir = oxide_emit::Emitter::new().emit_program(&program, false, false).expect("emit");
|
let mut ir = oxide_emit::Emitter::new().emit_program(&program, false, false).expect("emit");
|
||||||
oxide_dce::dce(&mut ir);
|
let _ = oxide_dce::dce(&mut ir);
|
||||||
let cfg = oxide_cfg::build_cfg(&ir);
|
let cfg = oxide_cfg::build_cfg(&ir);
|
||||||
let live = oxide_liveness::liveness(&ir, &cfg);
|
let live = oxide_liveness::liveness(&ir, &cfg);
|
||||||
oxide_dce::dce_precise(&mut ir, &live);
|
oxide_dce::dce_precise(&mut ir, &live);
|
||||||
|
|||||||
Reference in New Issue
Block a user