mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-23 18:21:13 +08:00
feat: 新增 oxide_liveness crate,CFG 数据流 liveness + 逐指令 live range(D-15)
This commit is contained in:
Generated
+11
@@ -1175,6 +1175,17 @@ dependencies = [
|
||||
"rustc-hash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "oxide_liveness"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"oxide_bytecode",
|
||||
"oxide_cfg",
|
||||
"oxide_emit",
|
||||
"oxide_ir",
|
||||
"oxide_parser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "oxide_log"
|
||||
version = "0.1.0"
|
||||
|
||||
@@ -10,6 +10,7 @@ members = [
|
||||
"crates/oxide_ir",
|
||||
"crates/oxide_cfg",
|
||||
"crates/oxide_dce",
|
||||
"crates/oxide_liveness",
|
||||
"crates/oxide_types",
|
||||
"crates/oxide_vm",
|
||||
"crates/oxide_kernel",
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
[package]
|
||||
name = "oxide_liveness"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
rust-version.workspace = true
|
||||
description = "OxideJS - liveness analysis pass over IR (CFG dataflow + per-instruction live ranges)"
|
||||
|
||||
[dependencies]
|
||||
oxide_ir = { path = "../oxide_ir" }
|
||||
oxide_cfg = { path = "../oxide_cfg" }
|
||||
|
||||
[dev-dependencies]
|
||||
oxide_parser = { path = "../oxide_parser" }
|
||||
oxide_emit = { path = "../oxide_emit" }
|
||||
oxide_bytecode = { path = "../oxide_bytecode" }
|
||||
@@ -0,0 +1,258 @@
|
||||
//! 块级数据流迭代:gen/kill + reverse_postorder 不动点。
|
||||
//!
|
||||
//! gen/kill 直接消费 `oxide_ir::contract` 的 `def_reg`/`use_regs`(D-22),
|
||||
//! None→0 / This→254 / NewTarget→255 / CALL 隐式 reg0 全内置,不重复建。
|
||||
//! Exception 边当普通边参与迭代(D-13);Exception 目标块入口 reg 0 隐式 def
|
||||
//! (异常展开写 regs[0],vm_runtime.rs:204-205)在此建模:gen.remove(0)+kill.insert(0)。
|
||||
|
||||
use oxide_cfg::{Cfg, EdgeKind};
|
||||
use oxide_ir::IRFunction;
|
||||
|
||||
/// 块级 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) {
|
||||
// 1. reg_count 上界扫描(不信任 f.n_registers,动态求上界,照 iter_sweep 先例)
|
||||
let mut reg_count = 255usize; // 兜底 This/NewTarget 254/255
|
||||
for inst in &f.insts {
|
||||
if let Some(d) = inst.def_reg() {
|
||||
reg_count = reg_count.max(d as usize);
|
||||
}
|
||||
for u in inst.use_regs() {
|
||||
reg_count = reg_count.max(u as usize);
|
||||
}
|
||||
}
|
||||
|
||||
let bits = reg_count + 1;
|
||||
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];
|
||||
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];
|
||||
for i in block.inst_range.clone().rev() {
|
||||
if let Some(d) = f.insts[i].def_reg() {
|
||||
live[d as usize] = false;
|
||||
}
|
||||
for u in f.insts[i].use_regs() {
|
||||
live[u as usize] = true;
|
||||
}
|
||||
}
|
||||
gen[b] = live;
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
||||
// Exception 目标块:入口 reg 0 隐式 def(异常展开在块外写 regs[0])。
|
||||
// gen 去 0 防"无 def use"把活度污染到函数入口;kill 插 0 防 0 从任何 pred 流入。
|
||||
// 注:preds 是 Vec<BBId>(无 EdgeKind),须从源块 succs 侧检测 Exception 边。
|
||||
let is_exception_target = cfg
|
||||
.blocks
|
||||
.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;
|
||||
}
|
||||
}
|
||||
|
||||
// 3. reverse_postorder(确定性,禁 HashMap 迭代序——B010)
|
||||
let rpo = reverse_postorder(cfg);
|
||||
|
||||
// 4. 不动点迭代
|
||||
let mut live_in = vec![vec![false; bits]; n];
|
||||
let mut live_out = vec![vec![false; bits]; n];
|
||||
let mut iters = 0usize;
|
||||
loop {
|
||||
let mut changed = false;
|
||||
for &b in &rpo {
|
||||
// live_out[b] = ∪ live_in[s](全部 succs,含 Exception 边——D-13 当普通边)
|
||||
let mut out = vec![false; bits];
|
||||
for &(succ, _kind) in &cfg.blocks[b].succs {
|
||||
for (r, &v) in live_in[succ].iter().enumerate() {
|
||||
if v {
|
||||
out[r] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 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]);
|
||||
}
|
||||
if input != live_in[b] || out != live_out[b] {
|
||||
changed = true;
|
||||
live_in[b] = input;
|
||||
live_out[b] = out;
|
||||
}
|
||||
}
|
||||
if !changed {
|
||||
break;
|
||||
}
|
||||
iters += 1;
|
||||
debug_assert!(iters < cfg.blocks.len() * 2 + 16, "liveness 不动点疑似不收敛");
|
||||
}
|
||||
|
||||
(live_in, live_out, reg_count)
|
||||
}
|
||||
|
||||
/// 从 entry 出发 DFS 沿 succs 存储序(确定序)收集 postorder 后反转;
|
||||
/// 不可达块按升序追加在末尾。纯 Vec + visited 位图(B010)。
|
||||
fn reverse_postorder(cfg: &Cfg) -> Vec<usize> {
|
||||
let n = cfg.blocks.len();
|
||||
let mut visited = vec![false; n];
|
||||
let mut postorder = Vec::with_capacity(n);
|
||||
fn dfs(cfg: &Cfg, visited: &mut [bool], postorder: &mut Vec<usize>, b: usize) {
|
||||
visited[b] = true;
|
||||
for &(succ, _) in &cfg.blocks[b].succs {
|
||||
if !visited[succ] {
|
||||
dfs(cfg, visited, postorder, succ);
|
||||
}
|
||||
}
|
||||
postorder.push(b);
|
||||
}
|
||||
dfs(cfg, &mut visited, &mut postorder, cfg.entry);
|
||||
postorder.reverse();
|
||||
for (b, &vis) in visited.iter().enumerate() {
|
||||
if !vis {
|
||||
postorder.push(b);
|
||||
}
|
||||
}
|
||||
postorder
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_ir::operand::Operand;
|
||||
use oxide_bytecode::opcode::OpCode;
|
||||
|
||||
fn set(v: &[bool], regs: &[u32]) -> bool {
|
||||
regs.iter().all(|&r| v[r as usize])
|
||||
}
|
||||
|
||||
fn empty_function() -> IRFunction {
|
||||
IRFunction::new()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn linear_function_liveness() {
|
||||
let mut f = empty_function();
|
||||
f.insts.push(Inst::new(OpCode::ADD, Operand::Reg(2), Operand::Reg(0), Operand::Reg(1)));
|
||||
f.insts.push(Inst::new(OpCode::RETURN, Operand::Reg(2), Operand::None, Operand::None));
|
||||
let cfg = oxide_cfg::build_cfg(&f);
|
||||
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!(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));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn if_else_join_unions_live_sets() {
|
||||
// 0: jmp_if_false(1, L1) cond=Reg(1)
|
||||
// 1: ADD r3=r3+r2 def 3 use {3,2}
|
||||
// 2: jmp L2
|
||||
// 3: SUB r4=r4+r5 def 4 use {4,5}
|
||||
// 4: NOP
|
||||
// 5: RETURN Reg(6)
|
||||
let mut f = empty_function();
|
||||
f.insts.push(Inst::jmp_if_false(1, 1));
|
||||
f.insts.push(Inst::new(OpCode::ADD, Operand::Reg(3), Operand::Reg(3), Operand::Reg(2)));
|
||||
f.insts.push(Inst::jmp(2));
|
||||
f.insts.push(Inst::new(OpCode::SUB, Operand::Reg(4), Operand::Reg(4), Operand::Reg(5)));
|
||||
f.insts.push(Inst::new(OpCode::NOP, Operand::None, Operand::None, Operand::None));
|
||||
f.insts.push(Inst::new(OpCode::RETURN, Operand::Reg(6), Operand::None, Operand::None));
|
||||
f.label_pos = vec![None, Some(3), Some(4)];
|
||||
f.label_count = 3;
|
||||
let cfg = oxide_cfg::build_cfg(&f);
|
||||
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");
|
||||
// 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]);
|
||||
// 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]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn loop_back_edge_keeps_value_alive() {
|
||||
// 0: jmp_if_false(1, L1)
|
||||
// 1: ADD r3=r3+r2
|
||||
// 2: jmp L0
|
||||
// 3: NOP
|
||||
// 4: RETURN Reg(6)
|
||||
let mut f = empty_function();
|
||||
f.insts.push(Inst::jmp_if_false(1, 1));
|
||||
f.insts.push(Inst::new(OpCode::ADD, Operand::Reg(3), Operand::Reg(3), Operand::Reg(2)));
|
||||
f.insts.push(Inst::jmp(0));
|
||||
f.insts.push(Inst::new(OpCode::NOP, Operand::None, Operand::None, Operand::None));
|
||||
f.insts.push(Inst::new(OpCode::RETURN, Operand::Reg(6), Operand::None, Operand::None));
|
||||
f.label_pos = vec![Some(0), Some(3)];
|
||||
f.label_count = 2;
|
||||
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(跨回边存活)");
|
||||
// body 块 liveIn 含 2
|
||||
let body = cfg.blocks.iter().position(|b| b.inst_range == (1..3usize)).unwrap();
|
||||
assert!(live_in[body][2]);
|
||||
// 测试通过本身即不动点收敛证明
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn try_catch_reg0_not_polluting_entry() {
|
||||
// 0: try_begin(L_catch)
|
||||
// 1: ADD r3=r3+r2
|
||||
// 2: RETURN Reg(3)
|
||||
// 3: STORE_VAR(Reg(5), None, None) catch 入口:a=None→0 读 reg 0
|
||||
// 4: RETURN Reg(5)
|
||||
let mut f = empty_function();
|
||||
f.insts.push(Inst::try_begin(3));
|
||||
f.insts.push(Inst::new(OpCode::ADD, Operand::Reg(3), Operand::Reg(3), Operand::Reg(2)));
|
||||
f.insts.push(Inst::new(OpCode::RETURN, Operand::Reg(3), Operand::None, Operand::None));
|
||||
f.insts.push(Inst::new(OpCode::STORE_VAR, Operand::Reg(5), Operand::None, Operand::None));
|
||||
f.insts.push(Inst::new(OpCode::RETURN, Operand::Reg(5), Operand::None, Operand::None));
|
||||
f.label_pos = vec![None, None, None, Some(3)];
|
||||
f.label_count = 1;
|
||||
let cfg = oxide_cfg::build_cfg(&f);
|
||||
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 截断");
|
||||
// try 块(entry)liveIn 不含 0(未污染入口)
|
||||
assert!(!live_in[0][0], "reg0 use 不得污染函数入口");
|
||||
// try 块 liveIn 含 2(正常变量照常存活)
|
||||
assert!(live_in[0][2], "try 块 liveIn 应含 r2");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn none_operand_maps_to_reg0() {
|
||||
// 0: LOAD_VAR(Reg(5), None, None) def 5 use {0}
|
||||
// 1: HALT(None, None, None) contract.rs HALT 读 regs[0]
|
||||
let mut f = empty_function();
|
||||
f.insts.push(Inst::new(OpCode::LOAD_VAR, Operand::Reg(5), Operand::None, Operand::None));
|
||||
f.insts.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 消费生效");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
//! liveness 分析 pass:IRFunction + CFG → LiveInfo(只读,D-15)。
|
||||
//!
|
||||
//! 消费 `oxide_ir::IRFunction`(只读)与 `oxide_cfg::Cfg`(preds/succs + exit 哨兵,D-12),
|
||||
//! 产出 LiveInfo 分析视图(D-01:pass 输出不住进 IR)。两阶段:`dataflow`(块级
|
||||
//! liveIn/liveOut 不动点)→ `ranges`(块内反向扫描逐指令 live 集)。gen/kill 直接消费
|
||||
//! contract.rs def_reg/use_regs(D-22),None→0 / This→254 / NewTarget→255 零重复映射。
|
||||
|
||||
mod dataflow;
|
||||
mod live_info;
|
||||
mod ranges;
|
||||
|
||||
pub use live_info::LiveInfo;
|
||||
|
||||
use oxide_cfg::Cfg;
|
||||
use oxide_ir::IRFunction;
|
||||
|
||||
/// 计算 liveness:`&IRFunction + &Cfg → LiveInfo`。纯函数,只读 IR(D-15)。
|
||||
/// 空 IRFunction 退化:返回 LiveInfo::new()(照 oxide_dce 空函数先例)。
|
||||
pub fn liveness(f: &IRFunction, cfg: &Cfg) -> LiveInfo {
|
||||
if f.insts.is_empty() {
|
||||
return LiveInfo::new();
|
||||
}
|
||||
let (block_in, block_out, reg_count) = dataflow::block_liveness(f, cfg);
|
||||
let (inst_before, inst_after) = ranges::inst_liveness(f, cfg, &block_out, &block_in, reg_count);
|
||||
LiveInfo {
|
||||
block_live_in: block_in,
|
||||
block_live_out: block_out,
|
||||
inst_live_before: inst_before,
|
||||
inst_live_after: inst_after,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
//! LiveInfo:liveness 分析输出视图(D-15:pass 输出,不住进 IR)。
|
||||
//!
|
||||
//! 全部 live 集为按 reg 号索引的 bitset(`Vec<bool>`,长度 = reg_count+1,由
|
||||
//! dataflow 求上界)。四字段语义:
|
||||
//! - `block_live_in` / `block_live_out`:块级数据流迭代结果(05-06 精确 DCE 消费)
|
||||
//! - `inst_live_before` / `inst_live_after`:逐指令活集(05-07 干涉图消费 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>>,
|
||||
}
|
||||
|
||||
impl LiveInfo {
|
||||
/// 构造空 LiveInfo:全空 vec,等价于 Default。
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
block_live_in: Vec::new(),
|
||||
block_live_out: Vec::new(),
|
||||
inst_live_before: Vec::new(),
|
||||
inst_live_after: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for LiveInfo {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn live_info_new_is_empty() {
|
||||
let info = LiveInfo::new();
|
||||
assert!(info.block_live_in.is_empty());
|
||||
assert!(info.block_live_out.is_empty());
|
||||
assert!(info.inst_live_before.is_empty());
|
||||
assert!(info.inst_live_after.is_empty());
|
||||
let default = LiveInfo::default();
|
||||
assert!(default.block_live_in.is_empty());
|
||||
assert!(default.block_live_out.is_empty());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
//! 块内反向扫描:块级 liveOut → 逐指令 live_before/live_after。
|
||||
//!
|
||||
//! 起点是 **block_live_out**(非 liveIn,RESEARCH 伪代码笔误已修正——从 liveIn
|
||||
//! 出发会得到错误放大的逐指令集)。反向扫描 **kill 先于 gen**(`live = (live − def)
|
||||
//! ∪ use`):COMPOUND_ADD 等读-写同寄存器指令 use 含 rd(contract.rs:137),
|
||||
//! gen 先于 kill 会把 rd 旧值从 live_before 错误剔除。Exception 目标块入口
|
||||
//! reg 0 隐式 def 在此截断,并以 `debug_assert_eq!(live, block_live_in[b])`
|
||||
//! 校验与 dataflow 一致性。
|
||||
|
||||
use oxide_cfg::{Cfg, EdgeKind};
|
||||
use oxide_ir::IRFunction;
|
||||
|
||||
/// 逐指令 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()];
|
||||
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();
|
||||
// kill 先于 gen:本指令先写后读(COMPOUND_ADD 等读-写同寄存器,contract.rs:137)
|
||||
if let Some(d) = f.insts[i].def_reg() {
|
||||
live[d as usize] = false;
|
||||
}
|
||||
for u in f.insts[i].use_regs() {
|
||||
live[u as usize] = true;
|
||||
}
|
||||
before[i] = live.clone();
|
||||
}
|
||||
// Exception 目标块:入口 reg 0 隐式 def(异常展开写 regs[0])截断无 def use。
|
||||
// preds 是 Vec<BBId>(无 EdgeKind),从源块 succs 侧检测 Exception 边。
|
||||
let is_exception_target = cfg
|
||||
.blocks
|
||||
.iter()
|
||||
.any(|src| src.succs.iter().any(|&(dst, k)| dst == b && k == EdgeKind::Exception));
|
||||
if is_exception_target {
|
||||
live[0] = false;
|
||||
}
|
||||
debug_assert_eq!(live, block_live_in[b], "ranges 反向扫描与 dataflow liveIn 不一致,块 {b}");
|
||||
}
|
||||
(before, after)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::dataflow::block_liveness;
|
||||
use oxide_ir::inst::Inst;
|
||||
use oxide_ir::operand::Operand;
|
||||
use oxide_bytecode::opcode::OpCode;
|
||||
|
||||
fn empty_function() -> IRFunction {
|
||||
IRFunction::new()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn linear_block_instruction_sets() {
|
||||
// 0: ADD r2=r0+r1 1: RETURN r2
|
||||
let mut f = empty_function();
|
||||
f.insts.push(Inst::new(OpCode::ADD, Operand::Reg(2), Operand::Reg(0), Operand::Reg(1)));
|
||||
f.insts.push(Inst::new(OpCode::RETURN, Operand::Reg(2), Operand::None, Operand::None));
|
||||
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 存活");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_modify_write_kills_before_gen() {
|
||||
// 0: COMPOUND_ADD rd=1, a=2 def {1} use {1,2}
|
||||
// 1: RETURN r1
|
||||
let mut f = empty_function();
|
||||
f.insts.push(Inst::new(OpCode::COMPOUND_ADD, Operand::Reg(1), Operand::Reg(2), Operand::None));
|
||||
f.insts.push(Inst::new(OpCode::RETURN, Operand::Reg(1), Operand::None, Operand::None));
|
||||
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");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn call_result_reg0_range_is_short() {
|
||||
// 0: call(callee=1, this=2, first_arg=3, nargs=1) def {0} use {1,2,3}
|
||||
// 1: LOAD_VAR(Reg(4), None, None) def {4} use {0}
|
||||
// 2: RETURN r4
|
||||
let mut f = empty_function();
|
||||
f.insts.push(Inst::call(Operand::Reg(1), Operand::Reg(2), Operand::Reg(3), 1));
|
||||
f.insts.push(Inst::new(OpCode::LOAD_VAR, Operand::Reg(4), Operand::None, Operand::None));
|
||||
f.insts.push(Inst::new(OpCode::RETURN, Operand::Reg(4), Operand::None, Operand::None));
|
||||
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 不污染块入口");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn catch_entry_reg0_not_live_before_block() {
|
||||
// 0: try_begin(3) 1: ADD r3=r3+r2 2: RETURN r3
|
||||
// 3: STORE_VAR(r5, None, None) 4: RETURN r5
|
||||
let mut f = empty_function();
|
||||
f.insts.push(Inst::try_begin(3));
|
||||
f.insts.push(Inst::new(OpCode::ADD, Operand::Reg(3), Operand::Reg(3), Operand::Reg(2)));
|
||||
f.insts.push(Inst::new(OpCode::RETURN, Operand::Reg(3), Operand::None, Operand::None));
|
||||
f.insts.push(Inst::new(OpCode::STORE_VAR, Operand::Reg(5), Operand::None, Operand::None));
|
||||
f.insts.push(Inst::new(OpCode::RETURN, Operand::Reg(5), Operand::None, Operand::None));
|
||||
f.label_pos = vec![None, None, None, Some(3)];
|
||||
f.label_count = 1;
|
||||
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(异常值,正确)");
|
||||
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 不污染入口");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
//! 集成冒烟:真实编译产物(parse→emit→build_cfg→liveness)跑不崩 + 维度一致性 + try/catch reg0 断言 + 确定性。
|
||||
|
||||
use oxide_cfg::build_cfg;
|
||||
use oxide_liveness::{liveness, LiveInfo};
|
||||
|
||||
fn run_liveness(src: &str) -> (LiveInfo, oxide_cfg::Cfg, usize) {
|
||||
let allocator = oxide_parser::Allocator::default();
|
||||
let program = oxide_parser::parse(&allocator, src).expect("parse");
|
||||
let ir = oxide_emit::Emitter::new().emit_program(&program).expect("emit");
|
||||
let cfg = build_cfg(&ir);
|
||||
let info = liveness(&ir, &cfg);
|
||||
(info, cfg, ir.insts.len())
|
||||
}
|
||||
|
||||
fn smoke(src: &str) {
|
||||
let (info, cfg, inst_len) = run_liveness(src);
|
||||
assert_eq!(info.block_live_in.len(), cfg.blocks.len(), "block 维度一致");
|
||||
assert_eq!(info.block_live_out.len(), cfg.blocks.len(), "block 维度一致");
|
||||
assert_eq!(info.inst_live_before.len(), inst_len, "inst 维度一致");
|
||||
assert_eq!(info.inst_live_after.len(), inst_len, "inst 维度一致");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn smoke_simple_function() {
|
||||
smoke("function f(a, b) { return a + b; } f(1, 2)");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn smoke_if_else() {
|
||||
smoke("function f(x) { if (x > 0) { return 1; } else { return 2; } } f(5)");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn smoke_loop_with_variables() {
|
||||
smoke("function f(n) { var s = 0; for (var i = 0; i < n; i++) { s = s + i; } return s; } f(10)");
|
||||
}
|
||||
|
||||
#[test]
|
||||
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");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn smoke_nested_calls() {
|
||||
smoke("function g(x) { return x + 1; } function f(x) { return g(x) + g(x + 1); } f(1)");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn liveness_is_deterministic() {
|
||||
let src = "function f(n) { var s = 0; for (var i = 0; i < n; i++) { s = s + i; } return s; } f(10)";
|
||||
let (a, _, _) = run_liveness(src);
|
||||
let (b, _, _) = run_liveness(src);
|
||||
assert_eq!(a.block_live_in, b.block_live_in);
|
||||
assert_eq!(a.block_live_out, b.block_live_out);
|
||||
assert_eq!(a.inst_live_before, b.inst_live_before);
|
||||
assert_eq!(a.inst_live_after, b.inst_live_after);
|
||||
}
|
||||
Reference in New Issue
Block a user