liveness: 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:
@@ -4,13 +4,18 @@
|
||||
//! None→0 / This→254 / NewTarget→255 / CALL 隐式 reg0 全内置,不重复建。
|
||||
//! Exception 边当普通边参与迭代;Exception 目标块入口 reg 0 隐式 def
|
||||
//! (异常展开写 regs[0],vm_runtime.rs:204-205)在此建模:gen.remove(0)+kill.insert(0)。
|
||||
//!
|
||||
//! 位集为 `u64` 压缩(1 字 64 位,密度为 `Vec<bool>` 的 8 倍);不动点迭代
|
||||
//! 复用 out/input 两块缓冲(clear + 覆盖),不逐块逐迭代分配。
|
||||
|
||||
use oxide_cfg::{Cfg, EdgeKind};
|
||||
use oxide_ir::IRFunction;
|
||||
|
||||
use crate::live_info::{bitset_clear, bitset_set};
|
||||
|
||||
/// 块级 liveness:返回 (block_live_in, block_live_out, reg_count)。
|
||||
/// reg_count = 全部 def/use 最大 reg 号(bitset 长度 = reg_count+1)。
|
||||
pub(super) fn block_liveness(f: &IRFunction, cfg: &Cfg) -> (Vec<Vec<bool>>, Vec<Vec<bool>>, usize) {
|
||||
/// reg_count = 全部 def/use 最大 reg 号(bitset 容量 = reg_count+1)。
|
||||
pub(super) fn block_liveness(f: &IRFunction, cfg: &Cfg) -> (Vec<Vec<u64>>, Vec<Vec<u64>>, usize) {
|
||||
// 1. reg_count 上界扫描(不信任 f.n_registers,动态求上界,照 iter_sweep 先例)
|
||||
let mut reg_count = 255usize; // 兜底 This/NewTarget 254/255
|
||||
for inst in &f.insts {
|
||||
@@ -22,23 +27,23 @@ pub(super) fn block_liveness(f: &IRFunction, cfg: &Cfg) -> (Vec<Vec<bool>>, Vec<
|
||||
}
|
||||
}
|
||||
|
||||
let bits = reg_count + 1;
|
||||
let words = (reg_count + 1).div_ceil(64);
|
||||
let n = cfg.blocks.len();
|
||||
|
||||
// 2. 每块 gen/kill
|
||||
let mut gen: Vec<Vec<bool>> = vec![vec![false; bits]; n];
|
||||
let mut kill: Vec<Vec<bool>> = vec![vec![false; bits]; n];
|
||||
let mut gen: Vec<Vec<u64>> = vec![vec![0u64; words]; n];
|
||||
let mut kill: Vec<Vec<u64>> = vec![vec![0u64; words]; n];
|
||||
for (b, block) in cfg.blocks.iter().enumerate() {
|
||||
// gen:块内反向扫描,kill 先于 gen(`live = (live − def) ∪ use`)。
|
||||
// 顺序理由:COMPOUND_ADD 等读-写同寄存器指令 use 含 rd(contract.rs:137),
|
||||
// gen 先于 kill 会把 rd 旧值从 live_before 错误剔除。
|
||||
let mut live = vec![false; bits];
|
||||
let mut live = vec![0u64; words];
|
||||
for i in block.inst_range.clone().rev() {
|
||||
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);
|
||||
}
|
||||
}
|
||||
gen[b] = live;
|
||||
@@ -46,7 +51,7 @@ pub(super) fn block_liveness(f: &IRFunction, cfg: &Cfg) -> (Vec<Vec<bool>>, Vec<
|
||||
// kill:块内正向扫描,def_reg 非 None 即插入
|
||||
for i in block.inst_range.clone() {
|
||||
if let Some(d) = f.insts[i].def_reg() {
|
||||
kill[b][d as usize] = true;
|
||||
bitset_set(&mut kill[b], d as usize);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,39 +63,39 @@ pub(super) fn block_liveness(f: &IRFunction, cfg: &Cfg) -> (Vec<Vec<bool>>, Vec<
|
||||
.iter()
|
||||
.any(|src| src.succs.iter().any(|&(dst, k)| dst == b && k == EdgeKind::Exception));
|
||||
if is_exception_target {
|
||||
gen[b][0] = false;
|
||||
kill[b][0] = true;
|
||||
bitset_clear(&mut gen[b], 0);
|
||||
bitset_set(&mut kill[b], 0);
|
||||
}
|
||||
}
|
||||
|
||||
// 3. reverse_postorder(确定性,禁 HashMap 迭代序)
|
||||
let rpo = reverse_postorder(cfg);
|
||||
|
||||
// 4. 不动点迭代
|
||||
let mut live_in = vec![vec![false; bits]; n];
|
||||
let mut live_out = vec![vec![false; bits]; n];
|
||||
// 4. 不动点迭代:out/input 缓冲循环外分配复用,clear + 覆盖免逐迭代分配
|
||||
let mut live_in = vec![vec![0u64; words]; n];
|
||||
let mut live_out = vec![vec![0u64; words]; n];
|
||||
let mut out = vec![0u64; words];
|
||||
let mut input = vec![0u64; words];
|
||||
let mut iters = 0usize;
|
||||
loop {
|
||||
let mut changed = false;
|
||||
for &b in &rpo {
|
||||
// live_out[b] = ∪ live_in[s](全部 succs,含 Exception 边当普通边)
|
||||
let mut out = vec![false; bits];
|
||||
out.fill(0);
|
||||
for &(succ, _kind) in &cfg.blocks[b].succs {
|
||||
for (r, &v) in live_in[succ].iter().enumerate() {
|
||||
if v {
|
||||
out[r] = true;
|
||||
}
|
||||
let si = &live_in[succ];
|
||||
for (w, word) in out.iter_mut().enumerate() {
|
||||
*word |= si[w];
|
||||
}
|
||||
}
|
||||
// live_in[b] = gen[b] | (live_out[b] − kill[b])
|
||||
let mut input = vec![false; bits];
|
||||
for r in 0..bits {
|
||||
input[r] = gen[b][r] || (out[r] && !kill[b][r]);
|
||||
for w in 0..words {
|
||||
input[w] = gen[b][w] | (out[w] & !kill[b][w]);
|
||||
}
|
||||
if input != live_in[b] || out != live_out[b] {
|
||||
changed = true;
|
||||
live_in[b] = input;
|
||||
live_out[b] = out;
|
||||
live_in[b].copy_from_slice(&input);
|
||||
live_out[b].copy_from_slice(&out);
|
||||
}
|
||||
}
|
||||
if !changed {
|
||||
@@ -135,8 +140,8 @@ mod tests {
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_ir::operand::Operand;
|
||||
|
||||
fn set(v: &[bool], regs: &[u32]) -> bool {
|
||||
regs.iter().all(|&r| v[r as usize])
|
||||
fn set(v: &[u64], regs: &[u32]) -> bool {
|
||||
regs.iter().all(|&r| crate::live_info::bitset_get(v, r as usize))
|
||||
}
|
||||
|
||||
fn empty_function() -> IRFunction {
|
||||
@@ -154,12 +159,12 @@ mod tests {
|
||||
let (live_in, live_out, reg_count) = block_liveness(&f, &cfg);
|
||||
assert!(reg_count >= 2);
|
||||
assert!(set(&live_in[0], &[0, 1]), "entry liveIn 应含 0、1");
|
||||
assert!(!live_in[0][2], "r2 是 def 不应在 liveIn");
|
||||
assert!(!crate::live_info::bitset_get(&live_in[0], 2), "r2 是 def 不应在 liveIn");
|
||||
assert!(set(&live_out[0], &[]), "live_out 空");
|
||||
// exit 哨兵为空块,live 全空
|
||||
let exit = cfg.exit;
|
||||
assert!(live_in[exit].iter().all(|&v| !v));
|
||||
assert!(live_out[exit].iter().all(|&v| !v));
|
||||
assert!(live_in[exit].iter().all(|&w| w == 0));
|
||||
assert!(live_out[exit].iter().all(|&w| w == 0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -187,15 +192,15 @@ mod tests {
|
||||
let (live_in, _, _) = block_liveness(&f, &cfg);
|
||||
// join 块(NOP 所在块)liveIn 含 6(RETURN 用)
|
||||
let join = cfg.blocks.iter().position(|b| b.inst_range == (4..6usize)).unwrap();
|
||||
assert!(live_in[join][6], "join 块 liveIn 应含 r6");
|
||||
assert!(crate::live_info::bitset_get(&live_in[join], 6), "join 块 liveIn 应含 r6");
|
||||
// then 分支块 liveIn 含 2、3,不含 5
|
||||
let then = cfg.blocks.iter().position(|b| b.inst_range == (1..3usize)).unwrap();
|
||||
assert!(set(&live_in[then], &[2, 3]));
|
||||
assert!(!live_in[then][5]);
|
||||
assert!(!crate::live_info::bitset_get(&live_in[then], 5));
|
||||
// else 分支块 liveIn 含 4、5,不含 2
|
||||
let els = cfg.blocks.iter().position(|b| b.inst_range == (3..4usize)).unwrap();
|
||||
assert!(set(&live_in[els], &[4, 5]));
|
||||
assert!(!live_in[els][2]);
|
||||
assert!(!crate::live_info::bitset_get(&live_in[els], 2));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -219,11 +224,11 @@ mod tests {
|
||||
let cfg = oxide_cfg::build_cfg(&f);
|
||||
let (live_in, _, _) = block_liveness(&f, &cfg);
|
||||
// header 块(含 inst 0)liveIn 含 2(r2 跨回边存活)与 1(cond)
|
||||
assert!(live_in[0][1], "header liveIn 应含 cond r1");
|
||||
assert!(live_in[0][2], "header liveIn 应含 r2(跨回边存活)");
|
||||
assert!(crate::live_info::bitset_get(&live_in[0], 1), "header liveIn 应含 cond r1");
|
||||
assert!(crate::live_info::bitset_get(&live_in[0], 2), "header liveIn 应含 r2(跨回边存活)");
|
||||
// body 块 liveIn 含 2
|
||||
let body = cfg.blocks.iter().position(|b| b.inst_range == (1..3usize)).unwrap();
|
||||
assert!(live_in[body][2]);
|
||||
assert!(crate::live_info::bitset_get(&live_in[body], 2));
|
||||
// 测试通过本身即不动点收敛证明
|
||||
}
|
||||
|
||||
@@ -250,11 +255,11 @@ mod tests {
|
||||
let (live_in, _, _) = block_liveness(&f, &cfg);
|
||||
// catch 块 liveIn 不含 0(隐式 def 截断)
|
||||
let catch = cfg.blocks.iter().position(|b| b.inst_range == (3..5usize)).unwrap();
|
||||
assert!(!live_in[catch][0], "catch 块入口 reg0 应被隐式 def 截断");
|
||||
assert!(!crate::live_info::bitset_get(&live_in[catch], 0), "catch 块入口 reg0 应被隐式 def 截断");
|
||||
// try 块(entry)liveIn 不含 0(未污染入口)
|
||||
assert!(!live_in[0][0], "reg0 use 不得污染函数入口");
|
||||
assert!(!crate::live_info::bitset_get(&live_in[0], 0), "reg0 use 不得污染函数入口");
|
||||
// try 块 liveIn 含 2(正常变量照常存活)
|
||||
assert!(live_in[0][2], "try 块 liveIn 应含 r2");
|
||||
assert!(crate::live_info::bitset_get(&live_in[0], 2), "try 块 liveIn 应含 r2");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -268,6 +273,6 @@ mod tests {
|
||||
.push(Inst::new(OpCode::HALT, Operand::None, Operand::None, Operand::None));
|
||||
let cfg = oxide_cfg::build_cfg(&f);
|
||||
let (live_in, _, _) = block_liveness(&f, &cfg);
|
||||
assert!(live_in[0][0], "None→0 映射由 contract.rs 消费生效");
|
||||
assert!(crate::live_info::bitset_get(&live_in[0], 0), "None→0 映射由 contract.rs 消费生效");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ mod live_info;
|
||||
mod liveness_log;
|
||||
mod ranges;
|
||||
|
||||
pub use live_info::LiveInfo;
|
||||
pub use live_info::{bitset_get, LiveInfo};
|
||||
|
||||
use oxide_cfg::Cfg;
|
||||
use oxide_ir::IRFunction;
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
//! LiveInfo:liveness 分析输出视图(pass 输出,不住进 IR)。
|
||||
//!
|
||||
//! 全部 live 集为按 reg 号索引的 bitset(`Vec<bool>`,长度 = reg_count+1,由
|
||||
//! dataflow 求上界)。四字段语义:
|
||||
//! 全部 live 集为按 reg 号索引的 `u64` 位集(长度 = `(reg_count+1 + 63) / 64` 字,
|
||||
//! 由 dataflow 求上界)。位 r 落在第 `r >> 6` 字的 `r & 63` 位;越界位恒 0(消费方
|
||||
//! 用 `bitset_get` 越界返回 false 对齐旧 `Vec<bool>::get` 语义)。四字段语义:
|
||||
//! - `block_live_in` / `block_live_out`:块级数据流迭代结果(精确 DCE 消费)
|
||||
//! - `inst_live_before` / `inst_live_after`:逐指令活集(RegAlloc 干涉图消费 live_before)
|
||||
|
||||
/// liveness 输出:块级 liveIn/liveOut + 逐指令 live_before/live_after。
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct LiveInfo {
|
||||
pub block_live_in: Vec<Vec<bool>>,
|
||||
pub block_live_out: Vec<Vec<bool>>,
|
||||
pub inst_live_before: Vec<Vec<bool>>,
|
||||
pub inst_live_after: Vec<Vec<bool>>,
|
||||
pub block_live_in: Vec<Vec<u64>>,
|
||||
pub block_live_out: Vec<Vec<u64>>,
|
||||
pub inst_live_before: Vec<Vec<u64>>,
|
||||
pub inst_live_after: Vec<Vec<u64>>,
|
||||
}
|
||||
|
||||
impl LiveInfo {
|
||||
@@ -32,6 +33,22 @@ impl Default for LiveInfo {
|
||||
}
|
||||
}
|
||||
|
||||
/// 查询位集:位 r 置 1 返回 true;越界(r 超出位集容量)返回 false。
|
||||
/// 语义对齐旧 `Vec<bool>::get(r).copied().unwrap_or(false)`。
|
||||
pub fn bitset_get(bitset: &[u64], r: usize) -> bool {
|
||||
bitset.get(r >> 6).is_some_and(|w| (w >> (r & 63)) & 1 == 1)
|
||||
}
|
||||
|
||||
/// 置位:位 r 写 1。调用方须保证 r 在位集容量内(def/use reg 号 ≤ reg_count 上界)。
|
||||
pub(crate) fn bitset_set(bitset: &mut [u64], r: usize) {
|
||||
bitset[r >> 6] |= 1u64 << (r & 63);
|
||||
}
|
||||
|
||||
/// 清位:位 r 写 0。调用方须保证 r 在位集容量内。
|
||||
pub(crate) fn bitset_clear(bitset: &mut [u64], r: usize) {
|
||||
bitset[r >> 6] &= !(1u64 << (r & 63));
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
@@ -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 不污染入口");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ fn smoke_try_catch() {
|
||||
let (info, cfg, _) =
|
||||
run_liveness("function x() {} function y(e) {} function f() { try { x(); } catch (e) { y(e); } } f()");
|
||||
// entry 块 liveIn 不含 reg 0(异常边 reg0 截断在真实产物上成立)
|
||||
assert!(!info.block_live_in[cfg.entry][0], "entry 块 liveIn 不得含 reg0");
|
||||
assert!(!oxide_liveness::bitset_get(&info.block_live_in[cfg.entry], 0), "entry 块 liveIn 不得含 reg0");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user