docs: 补充注释与格式化
This commit is contained in:
@@ -1,11 +1,11 @@
|
||||
//! Pass B:全函数 use 计数迭代删除到不动点(D-02 连锁语义)。
|
||||
//! Pass B:全函数 use 计数迭代删除到不动点(连锁删上游写者)。
|
||||
//!
|
||||
//! 只对可达存活指令聚合 use 计数(逐条 `inst.use_regs()` 累加)→ 收集 `def_reg` 为
|
||||
//! Some(r) 且 use_count[r]==0 且 `is_pure(f)` 的指令 → 删除并减去其 use_regs → 迭代。
|
||||
//! CALL 隐式 def reg0 不参与死删(CALL 本身 is_pure=false 永不删,Pitfall 1),但 CALL
|
||||
//! CALL 隐式 def reg0 不参与死删(CALL 本身 is_pure=false 永不删),但 CALL
|
||||
//! 的 use 计数正常聚合——喂活上游写其参数的纯指令。
|
||||
//! **label 目标指令强制保留**:块头是控制流汇合点,即使 def 无 use 也删除会使存活
|
||||
//! JMP 的 label 引用悬空(lowering "Label not found")——保守保留(D-01 少删不错删)。
|
||||
//! **label 目标指令强制保留**:块头是控制流汇合点,删除会使存活 JMP 的 label 引用
|
||||
//! 悬空(lowering "Label not found")——保守保留,少删不错删。
|
||||
|
||||
use oxide_bytecode::opcode::OpCode;
|
||||
use oxide_ir::operand::Operand;
|
||||
@@ -41,11 +41,11 @@ pub(super) fn pass_b_dead_code(f: &IRFunction, keep: &mut [bool]) {
|
||||
}
|
||||
}
|
||||
// nested 树引用的变量槽位图:闭包可跨函数读外层变量槽(LOAD_VAR.a 读槽 / STORE_VAR.rd 写槽
|
||||
// 以寄存器号编码)。D-03 不递归删除 nested,但跨函数引用必须保活当前函数内写该槽的
|
||||
// 以寄存器号编码)。不递归删除 nested,但跨函数引用必须保活当前函数内写该槽的
|
||||
// STORE_VAR——否则死 `var k='x'` + 构造器内 `[k]` computed key 读槽被误删(回归锚)。
|
||||
let mut escaped_slot = vec![false; max_reg as usize + 1];
|
||||
collect_escaped_slots(&f.nested, &mut escaped_slot);
|
||||
// 迭代删除至不动点:删一条纯指令 → 减其 use_regs → 上游写者可能连锁变死(D-02)
|
||||
// 迭代删除至不动点:删一条纯指令 → 减其 use_regs → 上游写者可能连锁变死
|
||||
loop {
|
||||
let mut deleted = false;
|
||||
for (i, k) in keep.iter_mut().enumerate() {
|
||||
@@ -53,12 +53,12 @@ pub(super) fn pass_b_dead_code(f: &IRFunction, keep: &mut [bool]) {
|
||||
continue;
|
||||
}
|
||||
let inst = &f.insts[i];
|
||||
// CALL 系隐式 def reg0 是注册写入不参与死删收集;is_pure=false 已保证永不删(Pitfall 1)
|
||||
// CALL 系隐式 def reg0 是注册写入不参与死删收集;is_pure=false 已保证永不删
|
||||
if let Some(r) = inst.def_reg() {
|
||||
// STORE_VAR 写变量槽可被 nested 闭包跨函数读取(computed class field key 等
|
||||
// 场景:构造函数 submodule 内 LOAD_VAR a 槽引外层变量槽)。D-03 不递归 nested,
|
||||
// 场景:构造函数 submodule 内 LOAD_VAR a 槽引外层变量槽)。DCE 不递归 nested,
|
||||
// 顶层 use 计数看不到跨函数读——凡 nested 树引用的变量槽,其顶层写者保活
|
||||
//(保守"少删不错删",D-01)。
|
||||
//(保守"少删不错删")。
|
||||
let slot_escapes = inst.op == OpCode::STORE_VAR
|
||||
&& matches!(inst.rd, Operand::Reg(_))
|
||||
&& escaped_slot.get(r as usize).copied().unwrap_or(false);
|
||||
@@ -78,8 +78,9 @@ pub(super) fn pass_b_dead_code(f: &IRFunction, keep: &mut [bool]) {
|
||||
}
|
||||
|
||||
/// 递归收集 nested 树中全部变量槽引用(LOAD_VAR.a 读槽、STORE_VAR.rd 写槽)。
|
||||
///
|
||||
/// 槽号 = 寄存器号编码的变量槽;其余操作数(局部寄存器/Imm/This)不构成跨函数引用。
|
||||
/// precise_sweep 复用(B012 约束同一来源)。
|
||||
/// precise_sweep 复用此函数,保证跨函数保活判定同一来源。
|
||||
pub(crate) fn collect_escaped_slots(nested: &[IRFunction], out: &mut Vec<bool>) {
|
||||
for sub in nested {
|
||||
for inst in &sub.insts {
|
||||
@@ -111,8 +112,10 @@ mod tests {
|
||||
fn linear_dead_chain_deleted() {
|
||||
let mut f = IRFunction::new();
|
||||
f.insts.push(Inst::load_const(Operand::Reg(0), 0)); // 0: r0 = const
|
||||
f.insts.push(Inst::new(OpCode::ADD, Operand::Reg(1), Operand::Reg(0), Operand::Reg(0))); // 1: r1 = r0 + r0
|
||||
f.insts.push(Inst::new(OpCode::RETURN, Operand::Reg(2), Operand::None, Operand::None)); // 2: return r2
|
||||
f.insts
|
||||
.push(Inst::new(OpCode::ADD, Operand::Reg(1), Operand::Reg(0), Operand::Reg(0))); // 1: r1 = r0 + r0
|
||||
f.insts
|
||||
.push(Inst::new(OpCode::RETURN, Operand::Reg(2), Operand::None, Operand::None)); // 2: return r2
|
||||
let mut keep = vec![true; f.insts.len()];
|
||||
|
||||
pass_b_dead_code(&f, &mut keep);
|
||||
@@ -127,24 +130,30 @@ mod tests {
|
||||
f.insts.push(Inst::call(Operand::Reg(0), Operand::Reg(0), Operand::Reg(0), 0)); // 1: CALL
|
||||
f.insts.push(Inst::load_const(Operand::Reg(1), 1)); // 2
|
||||
f.insts.push(Inst::load_const(Operand::Reg(2), 2)); // 3
|
||||
f.insts.push(Inst::new(OpCode::STORE_VAR, Operand::Reg(1), Operand::Reg(2), Operand::Imm(1))); // 4
|
||||
f.insts.push(Inst::new(OpCode::GET_PROP, Operand::Reg(3), Operand::Reg(1), Operand::Reg(2))); // 5
|
||||
f.insts.push(Inst::new(OpCode::SET_PROP, Operand::Reg(3), Operand::Reg(1), Operand::Reg(2))); // 6
|
||||
f.insts.push(Inst::new(OpCode::INC_POST, Operand::Reg(4), Operand::Reg(5), Operand::None)); // 7
|
||||
f.insts
|
||||
.push(Inst::new(OpCode::STORE_VAR, Operand::Reg(1), Operand::Reg(2), Operand::Imm(1))); // 4
|
||||
f.insts
|
||||
.push(Inst::new(OpCode::GET_PROP, Operand::Reg(3), Operand::Reg(1), Operand::Reg(2))); // 5
|
||||
f.insts
|
||||
.push(Inst::new(OpCode::SET_PROP, Operand::Reg(3), Operand::Reg(1), Operand::Reg(2))); // 6
|
||||
f.insts
|
||||
.push(Inst::new(OpCode::INC_POST, Operand::Reg(4), Operand::Reg(5), Operand::None)); // 7
|
||||
let mut keep = vec![true; f.insts.len()];
|
||||
|
||||
pass_b_dead_code(&f, &mut keep);
|
||||
assert!(keep.iter().all(|k| *k), "有副作用指令一律保留");
|
||||
}
|
||||
|
||||
/// STORE_VAR 一律保留(Pitfall 6):写变量槽可能被外部观察(脚本顶层/模块作用域变量、
|
||||
/// STORE_VAR 一律保留:写变量槽可能被外部观察(脚本顶层/模块作用域变量、
|
||||
/// nested 闭包跨函数读槽),即使本函数内无 use 也不删。
|
||||
#[test]
|
||||
fn store_var_never_deleted() {
|
||||
let mut f = IRFunction::new();
|
||||
f.insts.push(Inst::load_const(Operand::Reg(1), 1)); // 0
|
||||
f.insts.push(Inst::new(OpCode::STORE_VAR, Operand::Reg(0), Operand::Reg(1), Operand::None)); // 1
|
||||
f.insts.push(Inst::new(OpCode::RETURN, Operand::Reg(5), Operand::None, Operand::None)); // 2
|
||||
f.insts
|
||||
.push(Inst::new(OpCode::STORE_VAR, Operand::Reg(0), Operand::Reg(1), Operand::None)); // 1
|
||||
f.insts
|
||||
.push(Inst::new(OpCode::RETURN, Operand::Reg(5), Operand::None, Operand::None)); // 2
|
||||
let mut keep = vec![true; f.insts.len()];
|
||||
|
||||
pass_b_dead_code(&f, &mut keep);
|
||||
@@ -152,12 +161,13 @@ mod tests {
|
||||
assert!(keep[0] && keep[2], "源与 RETURN 保留");
|
||||
}
|
||||
|
||||
/// 死闭包:CREATE_CLOSURE 结果未用 → 删除;nested 槽位不动(D-03 不递归不回收)。
|
||||
/// 死闭包:CREATE_CLOSURE 结果未用 → 删除;nested 不递归不回收。
|
||||
#[test]
|
||||
fn dead_create_closure_deleted_nested_untouched() {
|
||||
let mut f = IRFunction::new();
|
||||
f.insts.push(Inst::create_closure(Operand::Reg(1), 0)); // 0: r1 = nested[0]
|
||||
f.insts.push(Inst::new(OpCode::RETURN, Operand::Reg(5), Operand::None, Operand::None)); // 1
|
||||
f.insts
|
||||
.push(Inst::new(OpCode::RETURN, Operand::Reg(5), Operand::None, Operand::None)); // 1
|
||||
f.nested.push(IRFunction::new());
|
||||
let mut keep = vec![true; f.insts.len()];
|
||||
|
||||
@@ -165,14 +175,16 @@ mod tests {
|
||||
assert_eq!(keep, vec![false, true], "死 CREATE_CLOSURE 删,RETURN 保留");
|
||||
}
|
||||
|
||||
/// 连锁迭代(D-02):写链 LOAD_CONST→ADD→LOAD_CONST 全死,逐轮删除至不动点。
|
||||
/// 连锁迭代:写链 LOAD_CONST→ADD→LOAD_CONST 全死,逐轮删除至不动点。
|
||||
#[test]
|
||||
fn chained_dead_writes_deleted_iteratively() {
|
||||
let mut f = IRFunction::new();
|
||||
f.insts.push(Inst::load_const(Operand::Reg(0), 0)); // 0: r0(ADD 读,轮 2 删)
|
||||
f.insts.push(Inst::new(OpCode::ADD, Operand::Reg(1), Operand::Reg(0), Operand::Reg(0))); // 1: r1(轮 1 删)
|
||||
f.insts
|
||||
.push(Inst::new(OpCode::ADD, Operand::Reg(1), Operand::Reg(0), Operand::Reg(0))); // 1: r1(轮 1 删)
|
||||
f.insts.push(Inst::load_const(Operand::Reg(2), 2)); // 2: r2 无人读(轮 1 删)
|
||||
f.insts.push(Inst::new(OpCode::RETURN, Operand::Reg(5), Operand::None, Operand::None)); // 3
|
||||
f.insts
|
||||
.push(Inst::new(OpCode::RETURN, Operand::Reg(5), Operand::None, Operand::None)); // 3
|
||||
let mut keep = vec![true; f.insts.len()];
|
||||
|
||||
pass_b_dead_code(&f, &mut keep);
|
||||
@@ -181,14 +193,15 @@ mod tests {
|
||||
|
||||
/// label 目标指令强制保留(回归:do-while 形态)。`LOAD_CONST r0` 是 label 0 的目标,
|
||||
/// 虽然 def 无 use 属死值,但删除会使存活 JMP_IF_TRUE 的 label 引用悬空
|
||||
/// (lowering 报 "Label not found")——保守保留(D-01 少删不错删)。
|
||||
/// (lowering 报 "Label not found")——保守保留,少删不错删。
|
||||
#[test]
|
||||
fn label_target_inst_never_deleted() {
|
||||
let mut f = IRFunction::new();
|
||||
f.insts.push(Inst::load_const(Operand::Reg(0), 1)); // 0: body 头,label 0 目标(死值)
|
||||
f.insts.push(Inst::load_const(Operand::Reg(1), 1)); // 1: 条件 true
|
||||
f.insts.push(Inst::jmp_if_true(1, 0)); // 2: 回跳 label 0
|
||||
f.insts.push(Inst::new(OpCode::RETURN, Operand::Reg(5), Operand::None, Operand::None)); // 3
|
||||
f.insts
|
||||
.push(Inst::new(OpCode::RETURN, Operand::Reg(5), Operand::None, Operand::None)); // 3
|
||||
f.label_pos = vec![Some(0)];
|
||||
f.label_count = 1;
|
||||
let mut keep = vec![true; f.insts.len()];
|
||||
@@ -199,18 +212,22 @@ mod tests {
|
||||
|
||||
/// STORE_VAR 跨函数逃逸(回归:computed class field key)。顶层写变量槽 Reg(1) 的
|
||||
/// STORE_VAR 在本函数无 use,但 nested 构造器内 LOAD_VAR a=Reg(1) 跨函数读该槽——
|
||||
/// DCE 只统计当前函数(D-03 不递归),nested 引用槽保活顶层写者。
|
||||
/// DCE 只统计当前函数(不递归 nested),nested 引用槽保活顶层写者。
|
||||
#[test]
|
||||
fn store_var_slot_read_by_nested_kept() {
|
||||
let mut f = IRFunction::new();
|
||||
f.insts.push(Inst::load_const(Operand::Reg(2), 0)); // 0: 'x'
|
||||
f.insts.push(Inst::new(OpCode::STORE_VAR, Operand::Reg(1), Operand::Reg(2), Operand::Imm(0))); // 1: k = 'x'(槽 1)
|
||||
f.insts
|
||||
.push(Inst::new(OpCode::STORE_VAR, Operand::Reg(1), Operand::Reg(2), Operand::Imm(0))); // 1: k = 'x'(槽 1)
|
||||
f.insts.push(Inst::create_closure(Operand::Reg(4), 0)); // 2
|
||||
f.insts.push(Inst::new(OpCode::RETURN, Operand::Reg(5), Operand::None, Operand::None)); // 3
|
||||
// nested 构造器:LOAD_VAR a=Reg(1) 跨函数读外层变量槽 1(computed key)
|
||||
f.insts
|
||||
.push(Inst::new(OpCode::RETURN, Operand::Reg(5), Operand::None, Operand::None)); // 3
|
||||
// nested 构造器:LOAD_VAR a=Reg(1) 跨函数读外层变量槽 1(computed key)
|
||||
let mut sub = IRFunction::new();
|
||||
sub.insts.push(Inst::new(OpCode::LOAD_VAR, Operand::Reg(5), Operand::Reg(1), Operand::None));
|
||||
sub.insts.push(Inst::new(OpCode::RETURN, Operand::Reg(7), Operand::None, Operand::None));
|
||||
sub.insts
|
||||
.push(Inst::new(OpCode::LOAD_VAR, Operand::Reg(5), Operand::Reg(1), Operand::None));
|
||||
sub.insts
|
||||
.push(Inst::new(OpCode::RETURN, Operand::Reg(7), Operand::None, Operand::None));
|
||||
f.nested.push(sub);
|
||||
let mut keep = vec![true; f.insts.len()];
|
||||
|
||||
|
||||
+21
-17
@@ -1,16 +1,16 @@
|
||||
//! DCE 改写 pass:IRFunction → 死代码消除后 compact 重建。
|
||||
//!
|
||||
//! 改写 pass(D-09):`&mut IRFunction` 就地 compact(D-06 方案 A 索引关联保证无借用冲突),
|
||||
//! 改写 pass:`&mut IRFunction` 就地 compact(以指令下标索引 keep 位图,无借用冲突),
|
||||
//! 三 Pass 各居一模块(照 oxide_cfg 四阶段拆文件先例):
|
||||
//! - `reachability`(Pass A):块级可达性,消费 `oxide_cfg::build_cfg`,Exception 边保守(D-07/Pitfall 4)
|
||||
//! - `iter_sweep`(Pass B):全函数 use 计数迭代删除死指令到不动点(D-01/D-02 连锁语义)
|
||||
//! - `rebuild`(Pass C):mark-sweep 一次性重建 insts + label_pos 重映射(D-13,不用就地逐删平移)
|
||||
//! - `reachability`(Pass A):块级可达性,消费 `oxide_cfg::build_cfg`,Exception 边保守遍历
|
||||
//! - `iter_sweep`(Pass B):全函数 use 计数迭代删除死指令到不动点(连锁删上游写者)
|
||||
//! - `rebuild`(Pass C):mark-sweep 一次性重建 insts + label_pos 重映射(不用就地逐删平移)
|
||||
//!
|
||||
//! 本 pass 不碰 nested(D-03 不递归)、常量池(D-14 不清理)、n_registers(D-04 不收缩)、
|
||||
//! label_count(D-13 不读不改)。中间产物(keep/use_count)用局部 Vec 显式传参,
|
||||
//! 本 pass 不碰 nested(不递归)、常量池(不清理)、n_registers(不收缩)、
|
||||
//! label_count(不读不改)。中间产物(keep/use_count)用局部 Vec 显式传参,
|
||||
//! 不做 struct 状态持有(无共享可变状态约定)。零 unsafe。
|
||||
//!
|
||||
//! 精确二轮 `dce_precise`(D-17/D-19/D-20):liveness 驱动的死指令 + 局部死 STORE_VAR 删除,
|
||||
//! 精确二轮 `dce_precise`:liveness 驱动的死指令 + 局部死 STORE_VAR 删除,
|
||||
//! 消费 `oxide_liveness::LiveInfo`,mark-sweep 重建复用 Pass C。
|
||||
|
||||
mod iter_sweep;
|
||||
@@ -29,15 +29,16 @@ pub fn dce(f: &mut IRFunction) {
|
||||
}
|
||||
// Pass A:块级可达性(消费 build_cfg,Exception 边保守)→ 指令粒度 keep。
|
||||
let mut keep = reachability::pass_a_reachable(f);
|
||||
// Pass B:可达存活指令上全函数 use 计数迭代删除到不动点(D-02 连锁语义)。
|
||||
// Pass B:可达存活指令上全函数 use 计数迭代删除到不动点(连锁删上游写者)。
|
||||
iter_sweep::pass_b_dead_code(f, &mut keep);
|
||||
// Pass C:mark-sweep 一次性重建 insts + label_pos 重映射(D-13)。
|
||||
// Pass C:mark-sweep 一次性重建 insts + label_pos 重映射。
|
||||
rebuild::pass_c_sweep(f, &keep);
|
||||
}
|
||||
|
||||
/// 精确二轮(D-17/D-19/D-20):liveness 驱动的死指令 + 局部死 STORE_VAR 删除。
|
||||
/// 消费 oxide_liveness::LiveInfo(RegAlloc 前由 05-09 管线传入;入口处重跑 liveness
|
||||
/// 是 RegAlloc 的职责,D-17 意图:不重复建分析引擎)。mark-sweep 重建复用既有 Pass C。
|
||||
/// 精确二轮:liveness 驱动的死指令 + 局部死 STORE_VAR 删除。
|
||||
///
|
||||
/// 消费 oxide_liveness::LiveInfo(RegAlloc 管线传入;若 LiveInfo 已过期,重建 liveness
|
||||
/// 是调用方 RegAlloc 的职责,本 pass 不重复建分析引擎)。mark-sweep 重建复用既有 Pass C。
|
||||
pub fn dce_precise(f: &mut IRFunction, live: &LiveInfo) {
|
||||
// 空函数退化(照 dce 先例)
|
||||
if f.insts.is_empty() {
|
||||
@@ -49,7 +50,7 @@ pub fn dce_precise(f: &mut IRFunction, live: &LiveInfo) {
|
||||
}
|
||||
let mut keep = vec![true; f.insts.len()];
|
||||
precise_sweep::pass_dead_with_liveness(f, live, &mut keep);
|
||||
// D-20:mark-sweep 一次性重建
|
||||
// mark-sweep 一次性重建
|
||||
rebuild::pass_c_sweep(f, &keep);
|
||||
}
|
||||
|
||||
@@ -75,10 +76,12 @@ mod tests {
|
||||
fn dce_is_idempotent() {
|
||||
let mut f = IRFunction::new();
|
||||
f.insts.push(Inst::load_const(Operand::Reg(1), 0)); // 0: 死
|
||||
f.insts.push(Inst::new(OpCode::ADD, Operand::Reg(2), Operand::Reg(1), Operand::Reg(1))); // 1: 死
|
||||
f.insts
|
||||
.push(Inst::new(OpCode::ADD, Operand::Reg(2), Operand::Reg(1), Operand::Reg(1))); // 1: 死
|
||||
f.insts.push(Inst::load_const(Operand::Reg(0), 1)); // 2: 存活(CALL 保活 r0)
|
||||
f.insts.push(Inst::call(Operand::Reg(0), Operand::Reg(0), Operand::Reg(0), 0)); // 3
|
||||
f.insts.push(Inst::new(OpCode::RETURN, Operand::Reg(5), Operand::None, Operand::None)); // 4
|
||||
f.insts
|
||||
.push(Inst::new(OpCode::RETURN, Operand::Reg(5), Operand::None, Operand::None)); // 4
|
||||
f.label_pos = vec![Some(0)];
|
||||
|
||||
dce(&mut f);
|
||||
@@ -97,13 +100,14 @@ mod tests {
|
||||
f.insts.push(Inst::call(Operand::Reg(2), Operand::Reg(0), Operand::Reg(3), 1)); // 1: then
|
||||
f.insts.push(Inst::jmp(1)); // 2: → L1(end)
|
||||
f.insts.push(Inst::call(Operand::Reg(4), Operand::Reg(0), Operand::Reg(3), 1)); // 3: else
|
||||
f.insts.push(Inst::new(OpCode::RETURN, Operand::Reg(5), Operand::None, Operand::None)); // 4
|
||||
f.insts
|
||||
.push(Inst::new(OpCode::RETURN, Operand::Reg(5), Operand::None, Operand::None)); // 4
|
||||
f.label_pos = vec![Some(3), Some(4)];
|
||||
f.label_count = 2;
|
||||
|
||||
dce(&mut f);
|
||||
assert_eq!(f.insts.len(), 5, "双分支全保留");
|
||||
assert_eq!(f.label_pos, vec![Some(3), Some(4)], "存活 label 重映射到新下标");
|
||||
assert_eq!(f.label_count, 2, "label_count 不动(D-13)");
|
||||
assert_eq!(f.label_count, 2, "label_count 保持原值,不参与重建");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
//! 精确轮(Pass D):liveness 驱动的死指令 + 局部死 STORE_VAR 删除(D-17/D-19)。
|
||||
//! 精确轮(Pass D):liveness 驱动的死指令 + 局部死 STORE_VAR 删除。
|
||||
//!
|
||||
//! 消费 `oxide_liveness::LiveInfo::inst_live_after` 判定(D-22 契约复用,不重复建):
|
||||
//! 消费 `oxide_liveness::LiveInfo::inst_live_after` 判定(契约复用 contract.rs,不重复建分析引擎):
|
||||
//! - 通用规则:纯指令 def_reg 不在 live_after → 删(含寄存器复用死写,保守 use 计数抓不到)
|
||||
//! - STORE_VAR 特例(def_reg=None 不命中通用规则):非顶层 && b==Imm(0) && 槽非 escaped && 槽不在 live_after → 删
|
||||
//!
|
||||
//! label 目标保守保留;单遍不级联(保守轮已做连锁);nested 不递归(D-03 延续)。
|
||||
//! label 目标保守保留;单遍不级联(保守轮已做连锁);nested 不递归。
|
||||
|
||||
use oxide_bytecode::opcode::OpCode;
|
||||
use oxide_ir::operand::Operand;
|
||||
@@ -20,8 +20,8 @@ pub(super) fn pass_dead_with_liveness(f: &IRFunction, live: &LiveInfo, keep: &mu
|
||||
*t = true;
|
||||
}
|
||||
}
|
||||
// escaped 槽位图:nested 树 LOAD_VAR.a / STORE_VAR.rd 直引的父槽(B012——父 liveness
|
||||
// 看不到跨函数读,删 STORE_VAR 后子模块读陈旧/undefined 槽)
|
||||
// escaped 槽位图:nested 树 LOAD_VAR.a / STORE_VAR.rd 直引的父槽(父 liveness 看不到
|
||||
// 跨函数读,删 STORE_VAR 后子模块会读陈旧/undefined 槽)
|
||||
let mut max_reg: usize = 0;
|
||||
for inst in &f.insts {
|
||||
if let Some(d) = inst.def_reg() {
|
||||
@@ -47,17 +47,17 @@ pub(super) fn pass_dead_with_liveness(f: &IRFunction, live: &LiveInfo, keep: &mu
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// STORE_VAR 特例:def_reg=None 不命中通用规则,单独判定(四条件全满足才删)
|
||||
// 槽活度条件是必须的(RESEARCH Pattern 5 补齐):`var y=1; return y` 删掉
|
||||
// STORE_VAR 会让 LOAD_VAR 读到陈旧/undefined 槽(vreg 化 + RegAlloc 复用后脏槽)。
|
||||
// STORE_VAR 特例:def_reg=None 不命中通用规则,单独判定(四条件全满足才删)。
|
||||
// 槽活度条件是必须的:`var y=1; return y` 删掉 STORE_VAR 会让 LOAD_VAR 读到
|
||||
// 陈旧/undefined 槽(vreg 化 + RegAlloc 复用后脏槽)。
|
||||
if inst.op == OpCode::STORE_VAR {
|
||||
let slot = match inst.rd {
|
||||
Operand::Reg(s) => s as usize,
|
||||
_ => continue,
|
||||
};
|
||||
let deletable = !f.is_top_level // B008:顶层赋值全局可观察
|
||||
&& matches!(inst.b, Operand::Imm(0)) // const 赋值 b=1 抛 TypeError 不可删
|
||||
&& !escaped_slot.get(slot).copied().unwrap_or(false) // B012:nested 直读槽不可动
|
||||
let deletable = !f.is_top_level // 顶层赋值全局可观察,不可删
|
||||
&& matches!(inst.b, Operand::Imm(0)) // const 赋值 b=1 抛 TypeError,不可删
|
||||
&& !escaped_slot.get(slot).copied().unwrap_or(false) // nested 直读槽不可动
|
||||
&& not_live_after(slot as u32); // 槽无后续读(vreg 复用后脏槽风险)
|
||||
if deletable {
|
||||
keep[i] = false;
|
||||
@@ -96,9 +96,24 @@ mod tests {
|
||||
#[test]
|
||||
fn reuse_killed_dead_write_deleted() {
|
||||
let mut f = IRFunction::new();
|
||||
f.insts.push(Inst::new(oxide_bytecode::opcode::OpCode::ADD, Operand::Reg(5), Operand::Reg(1), Operand::Reg(2)));
|
||||
f.insts.push(Inst::new(oxide_bytecode::opcode::OpCode::ADD, Operand::Reg(5), Operand::Reg(3), Operand::Reg(4)));
|
||||
f.insts.push(Inst::new(oxide_bytecode::opcode::OpCode::RETURN, Operand::Reg(5), Operand::None, Operand::None));
|
||||
f.insts.push(Inst::new(
|
||||
oxide_bytecode::opcode::OpCode::ADD,
|
||||
Operand::Reg(5),
|
||||
Operand::Reg(1),
|
||||
Operand::Reg(2),
|
||||
));
|
||||
f.insts.push(Inst::new(
|
||||
oxide_bytecode::opcode::OpCode::ADD,
|
||||
Operand::Reg(5),
|
||||
Operand::Reg(3),
|
||||
Operand::Reg(4),
|
||||
));
|
||||
f.insts.push(Inst::new(
|
||||
oxide_bytecode::opcode::OpCode::RETURN,
|
||||
Operand::Reg(5),
|
||||
Operand::None,
|
||||
Operand::None,
|
||||
));
|
||||
// inst_live_after = [{3,4}, {5}, {}]:inst0 def 5 在 live_after[0] 无 5(后写杀死)
|
||||
run_precise(&mut f, &[&[3, 4], &[5], &[]], 5);
|
||||
assert_eq!(f.insts.len(), 2, "死写(前一个 ADD)应被删");
|
||||
@@ -109,37 +124,72 @@ mod tests {
|
||||
fn local_dead_store_var_deleted() {
|
||||
let mut f = IRFunction::new();
|
||||
f.insts.push(Inst::load_const(Operand::Reg(1), 0));
|
||||
f.insts.push(Inst::new(oxide_bytecode::opcode::OpCode::STORE_VAR, Operand::Reg(2), Operand::Reg(1), Operand::Imm(0)));
|
||||
f.insts.push(Inst::new(oxide_bytecode::opcode::OpCode::RETURN, Operand::Reg(5), Operand::None, Operand::None));
|
||||
f.insts.push(Inst::new(
|
||||
oxide_bytecode::opcode::OpCode::STORE_VAR,
|
||||
Operand::Reg(2),
|
||||
Operand::Reg(1),
|
||||
Operand::Imm(0),
|
||||
));
|
||||
f.insts.push(Inst::new(
|
||||
oxide_bytecode::opcode::OpCode::RETURN,
|
||||
Operand::Reg(5),
|
||||
Operand::None,
|
||||
Operand::None,
|
||||
));
|
||||
// is_top_level 默认 false;inst_live_after = [{1,5}, {5}, {}]
|
||||
run_precise(&mut f, &[&[1, 5], &[5], &[]], 5);
|
||||
assert_eq!(f.insts.len(), 2, "局部死 STORE_VAR 应被删(LOAD_CONST 单遍不级联保留)");
|
||||
}
|
||||
|
||||
/// 顶层 STORE_VAR 永不删(B008)。
|
||||
/// 顶层 STORE_VAR 永不删(顶层赋值全局可观察)。
|
||||
#[test]
|
||||
fn top_level_store_var_kept() {
|
||||
let mut f = IRFunction::new();
|
||||
f.is_top_level = true;
|
||||
f.insts.push(Inst::load_const(Operand::Reg(1), 0));
|
||||
f.insts.push(Inst::new(oxide_bytecode::opcode::OpCode::STORE_VAR, Operand::Reg(2), Operand::Reg(1), Operand::Imm(0)));
|
||||
f.insts.push(Inst::new(oxide_bytecode::opcode::OpCode::RETURN, Operand::Reg(5), Operand::None, Operand::None));
|
||||
f.insts.push(Inst::new(
|
||||
oxide_bytecode::opcode::OpCode::STORE_VAR,
|
||||
Operand::Reg(2),
|
||||
Operand::Reg(1),
|
||||
Operand::Imm(0),
|
||||
));
|
||||
f.insts.push(Inst::new(
|
||||
oxide_bytecode::opcode::OpCode::RETURN,
|
||||
Operand::Reg(5),
|
||||
Operand::None,
|
||||
Operand::None,
|
||||
));
|
||||
run_precise(&mut f, &[&[1, 5], &[5], &[]], 5);
|
||||
assert_eq!(f.insts.len(), 3, "顶层 STORE_VAR 不可删");
|
||||
}
|
||||
|
||||
/// escaped 槽 STORE_VAR 永不删(B012 nested 直读)。
|
||||
/// escaped 槽 STORE_VAR 永不删(nested 直读)。
|
||||
#[test]
|
||||
fn escaped_slot_store_var_kept() {
|
||||
let mut f = IRFunction::new();
|
||||
f.insts.push(Inst::load_const(Operand::Reg(1), 0));
|
||||
f.insts.push(Inst::new(oxide_bytecode::opcode::OpCode::STORE_VAR, Operand::Reg(2), Operand::Reg(1), Operand::Imm(0)));
|
||||
f.insts.push(Inst::new(oxide_bytecode::opcode::OpCode::RETURN, Operand::Reg(5), Operand::None, Operand::None));
|
||||
f.insts.push(Inst::new(
|
||||
oxide_bytecode::opcode::OpCode::STORE_VAR,
|
||||
Operand::Reg(2),
|
||||
Operand::Reg(1),
|
||||
Operand::Imm(0),
|
||||
));
|
||||
f.insts.push(Inst::new(
|
||||
oxide_bytecode::opcode::OpCode::RETURN,
|
||||
Operand::Reg(5),
|
||||
Operand::None,
|
||||
Operand::None,
|
||||
));
|
||||
let mut sub = IRFunction::new();
|
||||
sub.insts.push(Inst::new(oxide_bytecode::opcode::OpCode::LOAD_VAR, Operand::Reg(5), Operand::Reg(2), Operand::None));
|
||||
sub.insts.push(Inst::new(
|
||||
oxide_bytecode::opcode::OpCode::LOAD_VAR,
|
||||
Operand::Reg(5),
|
||||
Operand::Reg(2),
|
||||
Operand::None,
|
||||
));
|
||||
f.nested.push(sub);
|
||||
run_precise(&mut f, &[&[1, 5], &[5], &[]], 5);
|
||||
assert_eq!(f.insts.len(), 3, "escaped 槽 STORE_VAR 不可删(B012)");
|
||||
assert_eq!(f.insts.len(), 3, "被嵌套函数直引的 escaped 槽 STORE_VAR 不可删");
|
||||
}
|
||||
|
||||
/// const 赋值路径 b=1 保留(运行时抛 TypeError 可观察)。
|
||||
@@ -147,8 +197,18 @@ mod tests {
|
||||
fn const_guard_store_var_kept() {
|
||||
let mut f = IRFunction::new();
|
||||
f.insts.push(Inst::load_const(Operand::Reg(1), 0));
|
||||
f.insts.push(Inst::new(oxide_bytecode::opcode::OpCode::STORE_VAR, Operand::Reg(2), Operand::Reg(1), Operand::Imm(1)));
|
||||
f.insts.push(Inst::new(oxide_bytecode::opcode::OpCode::RETURN, Operand::Reg(5), Operand::None, Operand::None));
|
||||
f.insts.push(Inst::new(
|
||||
oxide_bytecode::opcode::OpCode::STORE_VAR,
|
||||
Operand::Reg(2),
|
||||
Operand::Reg(1),
|
||||
Operand::Imm(1),
|
||||
));
|
||||
f.insts.push(Inst::new(
|
||||
oxide_bytecode::opcode::OpCode::RETURN,
|
||||
Operand::Reg(5),
|
||||
Operand::None,
|
||||
Operand::None,
|
||||
));
|
||||
run_precise(&mut f, &[&[1, 5], &[5], &[]], 5);
|
||||
assert_eq!(f.insts.len(), 3, "b=1 const 赋值路径不可删");
|
||||
}
|
||||
@@ -158,7 +218,12 @@ mod tests {
|
||||
fn label_target_inst_never_deleted() {
|
||||
let mut f = IRFunction::new();
|
||||
f.insts.push(Inst::load_const(Operand::Reg(1), 0)); // label 0 目标,纯且结果未用
|
||||
f.insts.push(Inst::new(oxide_bytecode::opcode::OpCode::RETURN, Operand::Reg(5), Operand::None, Operand::None));
|
||||
f.insts.push(Inst::new(
|
||||
oxide_bytecode::opcode::OpCode::RETURN,
|
||||
Operand::Reg(5),
|
||||
Operand::None,
|
||||
Operand::None,
|
||||
));
|
||||
f.label_pos = vec![Some(0)];
|
||||
run_precise(&mut f, &[&[5], &[]], 5);
|
||||
assert_eq!(f.insts.len(), 2, "label 目标指令不可删");
|
||||
@@ -171,7 +236,12 @@ mod tests {
|
||||
f.insts.push(Inst::inst_spill(Operand::Reg(1), 0));
|
||||
f.insts.push(Inst::inst_unspill(Operand::Reg(2), 0));
|
||||
f.insts.push(Inst::inst_mov(Operand::Reg(3), Operand::Reg(2)));
|
||||
f.insts.push(Inst::new(oxide_bytecode::opcode::OpCode::RETURN, Operand::Reg(3), Operand::None, Operand::None));
|
||||
f.insts.push(Inst::new(
|
||||
oxide_bytecode::opcode::OpCode::RETURN,
|
||||
Operand::Reg(3),
|
||||
Operand::None,
|
||||
Operand::None,
|
||||
));
|
||||
run_precise(&mut f, &[&[], &[2], &[3], &[]], 5);
|
||||
assert_eq!(f.insts.len(), 4, "SPILL/UNSPILL 永不删,活 MOV 保留");
|
||||
}
|
||||
@@ -181,8 +251,18 @@ mod tests {
|
||||
fn stale_liveinfo_no_op() {
|
||||
let mut f = IRFunction::new();
|
||||
f.insts.push(Inst::load_const(Operand::Reg(1), 0));
|
||||
f.insts.push(Inst::new(oxide_bytecode::opcode::OpCode::STORE_VAR, Operand::Reg(2), Operand::Reg(1), Operand::Imm(0)));
|
||||
f.insts.push(Inst::new(oxide_bytecode::opcode::OpCode::RETURN, Operand::Reg(5), Operand::None, Operand::None));
|
||||
f.insts.push(Inst::new(
|
||||
oxide_bytecode::opcode::OpCode::STORE_VAR,
|
||||
Operand::Reg(2),
|
||||
Operand::Reg(1),
|
||||
Operand::Imm(0),
|
||||
));
|
||||
f.insts.push(Inst::new(
|
||||
oxide_bytecode::opcode::OpCode::RETURN,
|
||||
Operand::Reg(5),
|
||||
Operand::None,
|
||||
Operand::None,
|
||||
));
|
||||
let live = LiveInfo::new(); // 空 LiveInfo:维度不符
|
||||
dce_precise(&mut f, &live);
|
||||
assert_eq!(f.insts.len(), 3, "过期 LiveInfo 不得删除任何指令");
|
||||
@@ -194,8 +274,18 @@ mod tests {
|
||||
fn dce_precise_converges() {
|
||||
let mut f = IRFunction::new();
|
||||
f.insts.push(Inst::load_const(Operand::Reg(1), 0));
|
||||
f.insts.push(Inst::new(oxide_bytecode::opcode::OpCode::STORE_VAR, Operand::Reg(2), Operand::Reg(1), Operand::Imm(0)));
|
||||
f.insts.push(Inst::new(oxide_bytecode::opcode::OpCode::RETURN, Operand::Reg(5), Operand::None, Operand::None));
|
||||
f.insts.push(Inst::new(
|
||||
oxide_bytecode::opcode::OpCode::STORE_VAR,
|
||||
Operand::Reg(2),
|
||||
Operand::Reg(1),
|
||||
Operand::Imm(0),
|
||||
));
|
||||
f.insts.push(Inst::new(
|
||||
oxide_bytecode::opcode::OpCode::RETURN,
|
||||
Operand::Reg(5),
|
||||
Operand::None,
|
||||
Operand::None,
|
||||
));
|
||||
// 第一遍:手填 live(STORE_VAR 死)
|
||||
run_precise(&mut f, &[&[1, 5], &[5], &[]], 5);
|
||||
assert_eq!(f.insts.len(), 2, "第一遍删 STORE_VAR");
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
//! Pass A:块级可达性。
|
||||
//!
|
||||
//! 消费 `oxide_cfg::build_cfg` 的 Cfg,从 entry 沿 succs DFS(**遍历含 EdgeKind::Exception 边**,
|
||||
//! D-07/Pitfall 4——catch/finally 入口强制可达),不可达块的全部指令标记删除。
|
||||
//! catch/finally 入口强制可达),不可达块的全部指令标记删除。
|
||||
|
||||
use oxide_cfg::build_cfg;
|
||||
use oxide_ir::IRFunction;
|
||||
@@ -16,7 +16,7 @@ pub(super) fn pass_a_reachable(f: &IRFunction) -> Vec<bool> {
|
||||
continue;
|
||||
}
|
||||
reachable[b] = true;
|
||||
// succs 三边全遍历:Jump / Fallthrough / Exception(D-07 保守)
|
||||
// succs 三边全遍历:Jump / Fallthrough / Exception(保守,异常入口强制可达)
|
||||
for &(succ, _kind) in &cfg.blocks[b].succs {
|
||||
stack.push(succ);
|
||||
}
|
||||
@@ -36,16 +36,17 @@ pub(super) fn pass_a_reachable(f: &IRFunction) -> Vec<bool> {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use oxide_bytecode::opcode::OpCode;
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_ir::operand::Operand;
|
||||
use oxide_bytecode::opcode::OpCode;
|
||||
|
||||
/// 线性函数全可达:所有指令 keep=true。
|
||||
#[test]
|
||||
fn linear_all_insts_reachable() {
|
||||
let mut f = IRFunction::new();
|
||||
f.insts.push(Inst::load_const(Operand::Reg(1), 0));
|
||||
f.insts.push(Inst::new(OpCode::RETURN, Operand::Reg(1), Operand::None, Operand::None));
|
||||
f.insts
|
||||
.push(Inst::new(OpCode::RETURN, Operand::Reg(1), Operand::None, Operand::None));
|
||||
let keep = pass_a_reachable(&f);
|
||||
assert!(keep.iter().all(|k| *k), "线性函数全指令可达");
|
||||
}
|
||||
@@ -54,9 +55,11 @@ mod tests {
|
||||
#[test]
|
||||
fn unreachable_block_marked_dead() {
|
||||
let mut f = IRFunction::new();
|
||||
f.insts.push(Inst::new(OpCode::RETURN, Operand::Reg(0), Operand::None, Operand::None)); // 0
|
||||
f.insts
|
||||
.push(Inst::new(OpCode::RETURN, Operand::Reg(0), Operand::None, Operand::None)); // 0
|
||||
f.insts.push(Inst::jmp(0)); // 1: 死块(L0 目标),自指 JMP
|
||||
f.insts.push(Inst::new(OpCode::RETURN, Operand::Reg(1), Operand::None, Operand::None)); // 2
|
||||
f.insts
|
||||
.push(Inst::new(OpCode::RETURN, Operand::Reg(1), Operand::None, Operand::None)); // 2
|
||||
f.label_pos = vec![Some(1)];
|
||||
f.label_count = 1;
|
||||
|
||||
@@ -65,16 +68,18 @@ mod tests {
|
||||
assert!(!keep[1] && !keep[2], "return 后死块不可达");
|
||||
}
|
||||
|
||||
/// Exception 边保守(Pitfall 4):TRY_BEGIN 的 Exception 边使 catch 入口强制可达,
|
||||
/// Exception 边保守:TRY_BEGIN 的 Exception 边使 catch 入口强制可达,
|
||||
/// 即使没有正常边指向它。
|
||||
#[test]
|
||||
fn exception_edge_keeps_catch_entry() {
|
||||
let mut f = IRFunction::new();
|
||||
f.insts.push(Inst::try_begin(0)); // 0: TRY_BEGIN → catch 入口(label 0 → inst 3)
|
||||
f.insts.push(Inst::load_const(Operand::Reg(5), 1)); // 1
|
||||
f.insts.push(Inst::new(OpCode::RETURN, Operand::Reg(5), Operand::None, Operand::None)); // 2
|
||||
f.insts
|
||||
.push(Inst::new(OpCode::RETURN, Operand::Reg(5), Operand::None, Operand::None)); // 2
|
||||
f.insts.push(Inst::load_const(Operand::Reg(6), 2)); // 3: catch 入口
|
||||
f.insts.push(Inst::new(OpCode::RETURN, Operand::Reg(6), Operand::None, Operand::None)); // 4
|
||||
f.insts
|
||||
.push(Inst::new(OpCode::RETURN, Operand::Reg(6), Operand::None, Operand::None)); // 4
|
||||
f.label_pos = vec![Some(3)];
|
||||
f.label_count = 1;
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//! Pass C:mark-sweep 一次性重建 insts + label_pos 重映射(D-13,不用就地逐删平移——
|
||||
//! Pass C:mark-sweep 一次性重建 insts + label_pos 重映射(不用就地逐删平移——
|
||||
//! 多删点叠加偏移易错)。sweep:keep 序 compact 重建;label_pos 逐 id 重写,
|
||||
//! 目标被删的 label 置 None 保留 id 槽位(lowering 只读 label_pos 不读 label_count)。
|
||||
|
||||
@@ -17,13 +17,13 @@ pub(super) fn pass_c_sweep(f: &mut IRFunction, keep: &[bool]) {
|
||||
}
|
||||
}
|
||||
f.insts = new_insts;
|
||||
// label_pos 逐 id 重写:get + usize::MAX 过滤做越界守卫(T-04-03,不信任 label_pos 长度)
|
||||
// label_pos 逐 id 重写:get + usize::MAX 过滤做越界守卫(不信任 label_pos 长度)
|
||||
for pos in f.label_pos.iter_mut() {
|
||||
if let Some(old) = *pos {
|
||||
*pos = old_to_new.get(old).filter(|n| **n != usize::MAX).copied();
|
||||
}
|
||||
}
|
||||
// 防御性校验(T-04-03):存活 label 新下标不越界(空尾块映射 new insts.len() 合法透传)
|
||||
// 防御性校验:存活 label 新下标不越界(空尾块映射 new insts.len() 合法透传)
|
||||
let new_len = f.insts.len();
|
||||
debug_assert!(f.label_pos.iter().all(|p| p.map_or(true, |np| np <= new_len)));
|
||||
}
|
||||
@@ -42,7 +42,8 @@ mod tests {
|
||||
let mut f = IRFunction::new();
|
||||
f.insts.push(Inst::load_const(Operand::Reg(5), 1)); // 0: 存活
|
||||
f.insts.push(Inst::load_const(Operand::Reg(9), 2)); // 1: 死(删)
|
||||
f.insts.push(Inst::new(OpCode::RETURN, Operand::Reg(5), Operand::None, Operand::None)); // 2
|
||||
f.insts
|
||||
.push(Inst::new(OpCode::RETURN, Operand::Reg(5), Operand::None, Operand::None)); // 2
|
||||
f.label_pos = vec![Some(2)]; // label 0 → inst 2(RETURN)
|
||||
f.label_count = 1;
|
||||
|
||||
@@ -53,14 +54,15 @@ mod tests {
|
||||
assert_eq!(f.label_pos, vec![Some(1)], "存活 label 2 → 新下标 1");
|
||||
}
|
||||
|
||||
/// 不动域断言(D-14/D-04/D-03/D-13):constants / n_registers / nested / label_count
|
||||
/// 一律不动;死 label 置 None、存活 label 重映射、原 None 保持。
|
||||
/// 不动域断言:constants / n_registers / nested / label_count 一律不动;
|
||||
/// 死 label 置 None、存活 label 重映射、原 None 保持。
|
||||
#[test]
|
||||
fn untouched_domains_asserted() {
|
||||
let mut f = IRFunction::new();
|
||||
f.insts.push(Inst::load_const(Operand::Reg(1), 0)); // 0: label 0 目标(死值,保守保留)
|
||||
f.insts.push(Inst::load_const(Operand::Reg(2), 1)); // 1: 非 label 目标的死指令(删)
|
||||
f.insts.push(Inst::new(OpCode::RETURN, Operand::Reg(5), Operand::None, Operand::None)); // 2
|
||||
f.insts
|
||||
.push(Inst::new(OpCode::RETURN, Operand::Reg(5), Operand::None, Operand::None)); // 2
|
||||
f.label_pos = vec![Some(0), Some(2), None];
|
||||
f.label_count = 3;
|
||||
f.n_registers = 10;
|
||||
@@ -72,9 +74,9 @@ mod tests {
|
||||
assert_eq!(f.insts[0].op, OpCode::LOAD_CONST);
|
||||
assert_eq!(f.insts[1].op, OpCode::RETURN);
|
||||
assert_eq!(f.label_pos, vec![Some(0), Some(1), None], "存活 label→新下标、原 None 保持");
|
||||
assert_eq!(f.label_count, 3, "label_count 不动(D-13)");
|
||||
assert_eq!(f.n_registers, 10, "n_registers 不收缩(D-04)");
|
||||
assert_eq!(f.constants, vec![Constant::Number(1.0)], "常量池不清理(D-14)");
|
||||
assert_eq!(f.nested.len(), 1, "nested 不递归不回收(D-03)");
|
||||
assert_eq!(f.label_count, 3, "label_count 保持原值,不参与重建");
|
||||
assert_eq!(f.n_registers, 10, "n_registers 不收缩");
|
||||
assert_eq!(f.constants, vec![Constant::Number(1.0)], "常量池不清理");
|
||||
assert_eq!(f.nested.len(), 1, "nested 不递归不回收");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
//! 精确 DCE 二轮(D-17/D-19/D-20)运行时等价 + 语义断言。
|
||||
//! 精确 DCE 二轮运行时等价 + 语义断言。
|
||||
//!
|
||||
//! 同一 JS 源码走两条管线:parse → emit → dce(保守恒定)→(precise on/off)→ lower → run,
|
||||
//! 对比顶层执行结果。**不经 `Compiler::compile`**(测试层直调底层函数)。B008/B012 样例做
|
||||
//! 字节码/IR 级断言,is_top_level 标志直接断言。
|
||||
//! 对比顶层执行结果。**不经 `Compiler::compile`**(测试层直调底层函数)。顶层赋值 /
|
||||
//! escaped 槽样例做字节码/IR 级断言,is_top_level 标志直接断言。
|
||||
|
||||
use oxide_bytecode::opcode::OpCode;
|
||||
use oxide_vm::vm::Vm;
|
||||
@@ -32,7 +32,7 @@ fn normalize(r: Result<JsValue, String>) -> (bool, String) {
|
||||
}
|
||||
}
|
||||
|
||||
/// 精确轮 on/off 运行时等价样例集(覆盖 B008/B012/const 抛错/try-catch/双写/纯链)。
|
||||
/// 精确轮 on/off 运行时等价样例集(覆盖顶层赋值 / escaped 槽 / const 抛错 / try-catch / 双写 / 纯链)。
|
||||
#[test]
|
||||
fn precise_dce_semantics_preserved() {
|
||||
let samples = [
|
||||
@@ -40,13 +40,13 @@ fn precise_dce_semantics_preserved() {
|
||||
"function f() { var y = 1; return 5; } f();",
|
||||
// 死 a 链(a 从未读)
|
||||
"function f() { var a = 1 + 2; var b = 3 + 4; return b; } f();",
|
||||
// 顶层 STORE_VAR(B008:is_top_level 保护,不删)
|
||||
// 顶层 STORE_VAR(is_top_level 保护,不删)
|
||||
"var x = 1; 2;",
|
||||
// B008 既有回归样例(顶层赋值全局可观察)
|
||||
// 顶层赋值全局可观察(回归样例)
|
||||
"let x = 0; class C { [x = 1]() { return 2; } } new C();",
|
||||
// B012 回归:k 只被嵌套子模块 LOAD_VAR.a 直读(父 liveness 死、escaped 保活)
|
||||
// escaped 回归:k 只被嵌套子模块 LOAD_VAR.a 直读(父 liveness 死、escaped 保活)
|
||||
"function f() { let k = 'a'; class C { [k]() { return 1; } } return Object.keys(new C())[0]; } f();",
|
||||
// 死 const 声明存储(05-01 后 b=0,删之无害)
|
||||
// 死 const 声明存储(b=0 无 const guard,删之无害)
|
||||
"function f() { const c = 5; return 6; } f();",
|
||||
// const 重赋值抛 TypeError(b=1 保留,两跑同抛)
|
||||
"const x = 1; x = 2;",
|
||||
@@ -66,7 +66,7 @@ fn precise_dce_semantics_preserved() {
|
||||
}
|
||||
}
|
||||
|
||||
/// 字节码级断言:顶层 `var x = 1; 2;` 经 dce+dce_precise 后 STORE_VAR 仍存在(B008)。
|
||||
/// 字节码级断言:顶层 `var x = 1; 2;` 经 dce+dce_precise 后 STORE_VAR 仍存在(顶层赋值全局可观察)。
|
||||
#[test]
|
||||
fn top_level_store_var_survives_precise() {
|
||||
let allocator = oxide_parser::Allocator::default();
|
||||
@@ -78,14 +78,17 @@ fn top_level_store_var_survives_precise() {
|
||||
oxide_dce::dce_precise(&mut ir, &live);
|
||||
let module = oxide_ir::lower::lower(&ir).expect("lower failed");
|
||||
assert!(
|
||||
module.bytecode.iter().any(|&i| OpCode::try_from(i as u8).ok() == Some(OpCode::STORE_VAR)),
|
||||
module
|
||||
.bytecode
|
||||
.iter()
|
||||
.any(|&i| OpCode::try_from(i as u8).ok() == Some(OpCode::STORE_VAR)),
|
||||
"顶层 STORE_VAR 必须存活"
|
||||
);
|
||||
}
|
||||
|
||||
/// IR 级断言:函数内局部死 STORE_VAR 被精确轮删除。
|
||||
/// dce_precise 按 D-03 不递归 nested,管线(05-09)逐函数处理——此处直接对
|
||||
/// 嵌套函数本体跑 liveness + dce_precise 证明删除生效。
|
||||
/// dce_precise 不递归 nested,编译管线逐函数处理——此处直接对嵌套函数本体
|
||||
/// 跑 liveness + dce_precise 证明删除生效。
|
||||
#[test]
|
||||
fn precise_removes_dead_local_store() {
|
||||
let allocator = oxide_parser::Allocator::default();
|
||||
@@ -97,21 +100,15 @@ fn precise_removes_dead_local_store() {
|
||||
let cfg = oxide_cfg::build_cfg(&sub);
|
||||
let live = oxide_liveness::liveness(&sub, &cfg);
|
||||
oxide_dce::dce_precise(&mut sub, &live);
|
||||
assert!(
|
||||
sub.insts.iter().all(|i| i.op != OpCode::STORE_VAR),
|
||||
"函数内局部死 STORE_VAR 应被删除"
|
||||
);
|
||||
assert!(sub.insts.iter().all(|i| i.op != OpCode::STORE_VAR), "函数内局部死 STORE_VAR 应被删除");
|
||||
}
|
||||
|
||||
/// is_top_level 标志直接断言(D-19):顶层 true / 嵌套 false。
|
||||
/// is_top_level 标志直接断言:顶层 true / 嵌套 false。
|
||||
#[test]
|
||||
fn top_level_flag_filled_by_emit() {
|
||||
let allocator = oxide_parser::Allocator::default();
|
||||
let program = oxide_parser::parse(&allocator, "var x = 1; function g() { return 2; }").expect("parse failed");
|
||||
let ir = oxide_emit::Emitter::new().emit_program(&program).expect("emit failed");
|
||||
assert!(ir.is_top_level, "顶层脚本 is_top_level 应为 true");
|
||||
assert!(
|
||||
ir.nested.iter().any(|sub| !sub.is_top_level),
|
||||
"嵌套函数 is_top_level 应为 false"
|
||||
);
|
||||
assert!(ir.nested.iter().any(|sub| !sub.is_top_level), "嵌套函数 is_top_level 应为 false");
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
//! 运行时等价对比(D-08):DCE 开/关两份字节码 lower + VM 执行结果一致。
|
||||
//! 运行时等价对比:DCE 开/关两份字节码 lower + VM 执行结果一致。
|
||||
//!
|
||||
//! 同一 JS 源码走两条管线:parse → emit →(dce 开/关)→ lower → `Vm::run`,
|
||||
//! 对比顶层执行结果。纯表读错 VM handler 也能被此兜底(D-08)。
|
||||
//! **不经 `Compiler::compile`**(D-10:测试层直调底层函数,不依赖开关)。
|
||||
//! 对比顶层执行结果。副作用纯表读错 VM handler 也能被此兜底。
|
||||
//! **不经 `Compiler::compile`**:测试层直调底层函数,不依赖编译器开关。
|
||||
|
||||
use oxide_vm::vm::Vm;
|
||||
use oxide_vm::JsValue;
|
||||
@@ -29,25 +29,25 @@ fn normalize(r: Result<JsValue, String>) -> (bool, String) {
|
||||
}
|
||||
}
|
||||
|
||||
/// 死代码样例集(04-RESEARCH.md Pattern 5 清单)。
|
||||
/// 死代码样例集(覆盖表达式丢弃、死分支、死闭包、coerce 抛错、const 重赋值等路径)。
|
||||
/// 对每个样例:DCE 开与关分别执行,断言结果一致。
|
||||
#[test]
|
||||
fn dead_code_removed_semantics_preserved() {
|
||||
// 覆盖:表达式语句丢弃结果、重复赋值只读最后值、if(false) 分支、死闭包、
|
||||
// try/catch 内死代码、对象操作数算术(D-05 coerce 风险兜底)、
|
||||
// 顶层表达式返回值(HALT reg0 链,Pitfall 2)、先调用后丢弃(CALL 隐式 reg0,Pitfall 1)、
|
||||
// const 重赋值抛错保留(Pitfall 5)。
|
||||
// try/catch 内死代码、对象操作数算术(coerce 抛错风险兜底)、
|
||||
// 顶层表达式返回值(HALT 隐式读 reg0 链)、先调用后丢弃(CALL 隐式写 reg0)、
|
||||
// const 重赋值抛错保留。
|
||||
let samples = [
|
||||
"1+2;3+4", // 语句丢弃结果
|
||||
"'a'+'b';5;", // 语句丢弃结果(字符串)
|
||||
"var x=1; x=2; x;", // 重复赋值只读最后值
|
||||
"function a(){return 1;} if(false){a();} 3;", // if(false) 分支
|
||||
"function f(){}; 1;", // 死闭包
|
||||
"try{1+2;}catch(e){3;} 4;", // try/catch 内死代码
|
||||
"({valueOf(){side=1; return 1}})+2;", // 对象操作数算术(coerce 兜底)
|
||||
"1+2", // 顶层表达式返回值(Pitfall 2)
|
||||
"function f(){return 1;} f(); 2+3;", // 先调用后丢弃(Pitfall 1)
|
||||
"const x=1; x=2;", // const 重赋值(Pitfall 5)
|
||||
"1+2;3+4", // 语句丢弃结果
|
||||
"'a'+'b';5;", // 语句丢弃结果(字符串)
|
||||
"var x=1; x=2; x;", // 重复赋值只读最后值
|
||||
"function a(){return 1;} if(false){a();} 3;", // if(false) 分支
|
||||
"function f(){}; 1;", // 死闭包
|
||||
"try{1+2;}catch(e){3;} 4;", // try/catch 内死代码
|
||||
"({valueOf(){side=1; return 1}})+2;", // 对象操作数算术(coerce 兜底)
|
||||
"1+2", // 顶层表达式返回值(HALT 隐式读 reg0 链)
|
||||
"function f(){return 1;} f(); 2+3;", // 先调用后丢弃(CALL 隐式写 reg0)
|
||||
"const x=1; x=2;", // const 重赋值(运行时抛错保留)
|
||||
];
|
||||
for src in samples {
|
||||
let a = normalize(run_source(src, true));
|
||||
@@ -65,7 +65,7 @@ fn single_sample_top_level_return_value() {
|
||||
assert_eq!(a, (true, "3".to_string()), "顶层 1+2 应返回 3");
|
||||
}
|
||||
|
||||
/// Pitfall 6 回归:脚本顶层变量赋值是全局可观察状态(class 计算属性名里的 `x = 1`
|
||||
/// 顶层变量赋值是全局可观察状态(class 计算属性名里的 `x = 1`
|
||||
/// 会被测试 harness 的 `assert.sameValue(x, 1)` 观察),即使本函数内无 use,
|
||||
/// DCE 也**不得删除**对应 STORE_VAR——删除会改变外部可观察语义。
|
||||
#[test]
|
||||
@@ -80,10 +80,7 @@ fn top_level_store_var_kept() {
|
||||
.bytecode
|
||||
.iter()
|
||||
.any(|&i| oxide_bytecode::opcode::opcode(i) == oxide_bytecode::opcode::OpCode::STORE_VAR);
|
||||
assert!(
|
||||
has_store_var,
|
||||
"顶层 x 赋值(全局可观察)不应被 DCE 删除: {src}"
|
||||
);
|
||||
assert!(has_store_var, "顶层 x 赋值(全局可观察)不应被 DCE 删除: {src}");
|
||||
|
||||
// 运行时兜底:DCE 开/关执行结果一致,且 x 最终值为 1(未被删赋值破坏)
|
||||
let a = normalize(run_source(src, true));
|
||||
|
||||
Reference in New Issue
Block a user