docs: 补充注释与格式化

This commit is contained in:
爱特每羊队
2026-08-06 23:10:27 +08:00
parent 4e0fd762c2
commit 328b8a3ddc
156 changed files with 2393 additions and 1687 deletions
+57 -35
View File
@@ -1,9 +1,8 @@
//! CFG 分析 pass:IRFunction → 基本块划分 + 控制流图。
//!
//! 消费 `oxide_ir::IRFunction`(只读,D-06 索引关联,不拷贝指令),产出一次性
//! 分析快照(D-01:pass 输出视图,不住进 IR)。本 crate 只管 CFG(D-02):
//! 不建通用分析框架、不递归 nested(D-11)。无生命周期参数——CFG 只存指令
//! 下标区间,使用方同时持有 `&IRFunction` 与 `&Cfg` 联合访问。
//! 消费 `oxide_ir::IRFunction`(只读,不拷贝指令,仅存指令下标区间),产出一次性
//! 分析快照——pass 输出视图不住进 IR。本 crate 只管 CFG:不建通用分析框架、
//! 不递归 nested。无生命周期参数——CFG 与 `&IRFunction` 由使用方联合持有访问。
//!
//! 构建是四阶段分发:`partition`(块头)→ `split`(切块+出边)→ `exception`
//! (异常边)→ `finalize`(exit 哨兵 + preds 反推)。每阶段是独立文件的纯函数,
@@ -17,10 +16,10 @@ mod split;
use oxide_bytecode::opcode::OpCode;
use oxide_ir::IRFunction;
/// 基本块 id:blocks 向量下标(D-03)。
/// 基本块 id:blocks 向量下标。
pub type BBId = usize;
/// 边三分类(D-04):无条件/条件跳转、顺序落入、异常处理入口。
/// 边三分类:无条件/条件跳转、顺序落入、异常处理入口。
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum EdgeKind {
Jump,
@@ -28,8 +27,8 @@ pub enum EdgeKind {
Exception,
}
/// 基本块:`inst_range` 直接索引 `IRFunction.insts` 切片(D-03,不拷贝指令),
/// preds/succs 双向就位,liveness 数据流迭代可直接用。
/// 基本块:`inst_range` 直接索引 `IRFunction.insts` 切片(不拷贝指令),
/// preds/succs 双向就位,供 liveness 数据流迭代直接消费。
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct BasicBlock {
pub inst_range: std::ops::Range<usize>,
@@ -37,7 +36,7 @@ pub struct BasicBlock {
pub succs: Vec<(BBId, EdgeKind)>,
}
/// 控制流图快照:实块按指令序编号,exit 哨兵(空块 `0..0`)恒在 blocks 末尾(D-05)。
/// 控制流图快照:实块按指令序编号,exit 哨兵(空块 `0..0`)恒在 blocks 末尾。
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Cfg {
pub blocks: Vec<BasicBlock>,
@@ -48,7 +47,11 @@ pub struct Cfg {
impl Cfg {
/// 构造空 CFG:blocks 空、entry/exit 置 0。
pub fn new() -> Self {
Self { blocks: Vec::new(), entry: 0, exit: 0 }
Self {
blocks: Vec::new(),
entry: 0,
exit: 0,
}
}
}
@@ -58,12 +61,12 @@ impl Default for Cfg {
}
}
/// terminator 判定(D-09):块尾指令决定出边。
/// terminator 判定:块尾指令决定出边。
///
/// 注意与 `oxide_ir::lower.rs::is_jump_op` 的差异:lowering 需要为 TRY_BEGIN /
/// TRY_FINALLY_BEGIN 回填偏移所以把它们并列进 is_jump_op;但 CFG 里它们是**块内
/// 标记**(异常边起点,见 Pass 3),不是控制流终结——抄用 is_jump_op 会导致 try 体
/// 被切块、异常边爆炸(RESEARCH Pitfall 1)。
/// 被切块、异常边爆炸。
fn is_terminator(op: OpCode) -> bool {
matches!(
op,
@@ -77,16 +80,20 @@ fn is_terminator(op: OpCode) -> bool {
)
}
/// 构建 CFG:`&IRFunction → Cfg`。纯函数,只读 IR(D-06 方案 A)。
/// 构建 CFG:`&IRFunction → Cfg`。纯函数,只读 IR。
///
/// 四阶段分发:`partition::partition_blocks`(Pass 1 块头)→ `split::split_and_edges`
/// (Pass 2 切块 + 出边)→ `exception::add_exception_edges`(Pass 3 异常边)
/// → `finalize::finalize`(Pass 4 exit 哨兵 + preds 反推)。幂等(CFG-03)。
/// → `finalize::finalize`(Pass 4 exit 哨兵 + preds 反推)。幂等:同一 IR 两次构建结果相等。
pub fn build_cfg(f: &IRFunction) -> Cfg {
// 空 IRFunction 退化形态:仅 exit 哨兵一块(Pitfall 5)。
// 空 IRFunction 退化形态:仅 exit 哨兵一块。
if f.insts.is_empty() {
return Cfg {
blocks: vec![BasicBlock { inst_range: 0..0, preds: Vec::new(), succs: Vec::new() }],
blocks: vec![BasicBlock {
inst_range: 0..0,
preds: Vec::new(),
succs: Vec::new(),
}],
entry: 0,
exit: 0,
};
@@ -116,9 +123,12 @@ mod tests {
#[test]
fn linear_function_is_single_block_plus_exit() {
let mut f = IRFunction::new();
f.insts.push(Inst::new(OpCode::NOP, Operand::None, Operand::None, Operand::None));
f.insts.push(Inst::new(OpCode::NOP, Operand::None, Operand::None, Operand::None));
f.insts.push(Inst::new(OpCode::RETURN, Operand::None, Operand::None, Operand::None));
f.insts
.push(Inst::new(OpCode::NOP, Operand::None, Operand::None, Operand::None));
f.insts
.push(Inst::new(OpCode::NOP, Operand::None, Operand::None, Operand::None));
f.insts
.push(Inst::new(OpCode::RETURN, Operand::None, Operand::None, Operand::None));
let cfg = build_cfg(&f);
assert_eq!(cfg.blocks.len(), 2); // 1 实块 + exit 哨兵
@@ -139,7 +149,8 @@ mod tests {
f.insts.push(Inst::call(Operand::Reg(2), Operand::Reg(0), Operand::Reg(3), 1)); // 1: then a()
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 b()
f.insts.push(Inst::new(OpCode::RETURN, Operand::None, Operand::None, Operand::None)); // 4
f.insts
.push(Inst::new(OpCode::RETURN, Operand::None, Operand::None, Operand::None)); // 4
f.label_pos = vec![Some(3), Some(4)]; // L0→else 块头(3), L1→end(4)
f.label_count = 2;
@@ -171,22 +182,26 @@ mod tests {
assert_eq!(succs_total(&cfg), preds_total(&cfg));
}
/// try/catch:TRY_BEGIN 是块内标记不切块(Pitfall 1),所在 BB 出 Exception 边到 catch 入口。
/// try/catch:TRY_BEGIN 是块内标记不切块,所在 BB 出 Exception 边到 catch 入口。
#[test]
fn try_begin_does_not_split_block_and_emits_exception_edge() {
let mut f = IRFunction::new();
f.insts.push(Inst::try_begin(0)); // 0: TRY_BEGIN(块内标记)
f.insts.push(Inst::new(OpCode::NOP, Operand::None, Operand::None, Operand::None)); // 1
f.insts.push(Inst::new(OpCode::RETURN, Operand::None, Operand::None, Operand::None)); // 2
f.insts.push(Inst::new(OpCode::NOP, Operand::None, Operand::None, Operand::None)); // 3: catch 入口
f.insts.push(Inst::new(OpCode::RETURN, Operand::None, Operand::None, Operand::None)); // 4
f.insts
.push(Inst::new(OpCode::NOP, Operand::None, Operand::None, Operand::None)); // 1
f.insts
.push(Inst::new(OpCode::RETURN, Operand::None, Operand::None, Operand::None)); // 2
f.insts
.push(Inst::new(OpCode::NOP, Operand::None, Operand::None, Operand::None)); // 3: catch 入口
f.insts
.push(Inst::new(OpCode::RETURN, Operand::None, Operand::None, Operand::None)); // 4
f.label_pos = vec![Some(3)];
f.label_count = 1;
let cfg = build_cfg(&f);
assert_eq!(cfg.blocks.len(), 3); // 2 实块 + exit 哨兵(TRY_BEGIN 不切块)
assert_eq!(cfg.blocks[0].inst_range, 0..3); // try 体未被切块
// Exception 边从 TRY_BEGIN 所在 BB 出发指向 catch 入口块(D-07/D-08)
// Exception 边从 TRY_BEGIN 所在 BB 出发指向 catch 入口块。
assert!(cfg.blocks[0].succs.contains(&(1, EdgeKind::Exception)));
assert_eq!(cfg.blocks[1].succs, vec![(2, EdgeKind::Fallthrough)]);
assert_eq!(cfg.blocks[2].preds, vec![0, 1]);
@@ -198,10 +213,14 @@ mod tests {
fn try_finally_begin_emits_exception_edge() {
let mut f = IRFunction::new();
f.insts.push(Inst::try_finally_begin(0)); // 0
f.insts.push(Inst::new(OpCode::NOP, Operand::None, Operand::None, Operand::None)); // 1
f.insts.push(Inst::new(OpCode::RETURN, Operand::None, Operand::None, Operand::None)); // 2
f.insts.push(Inst::new(OpCode::NOP, Operand::None, Operand::None, Operand::None)); // 3: finally 入口
f.insts.push(Inst::new(OpCode::RETURN, Operand::None, Operand::None, Operand::None)); // 4
f.insts
.push(Inst::new(OpCode::NOP, Operand::None, Operand::None, Operand::None)); // 1
f.insts
.push(Inst::new(OpCode::RETURN, Operand::None, Operand::None, Operand::None)); // 2
f.insts
.push(Inst::new(OpCode::NOP, Operand::None, Operand::None, Operand::None)); // 3: finally 入口
f.insts
.push(Inst::new(OpCode::RETURN, Operand::None, Operand::None, Operand::None)); // 4
f.label_pos = vec![Some(3)];
f.label_count = 1;
@@ -216,8 +235,10 @@ mod tests {
#[test]
fn trailing_return_flows_to_exit() {
let mut f = IRFunction::new();
f.insts.push(Inst::new(OpCode::NOP, Operand::None, Operand::None, Operand::None));
f.insts.push(Inst::new(OpCode::RETURN, Operand::None, Operand::None, Operand::None));
f.insts
.push(Inst::new(OpCode::NOP, Operand::None, Operand::None, Operand::None));
f.insts
.push(Inst::new(OpCode::RETURN, Operand::None, Operand::None, Operand::None));
let cfg = build_cfg(&f);
assert_eq!(cfg.blocks.len(), 2);
@@ -225,7 +246,7 @@ mod tests {
assert_eq!(cfg.blocks[1].preds, vec![0]);
}
/// 空 IRFunction:退化 CFG,entry == exit == 0,blocks 长度 1(Pitfall 5)。
/// 空 IRFunction:退化 CFG,entry == exit == 0,blocks 长度 1。
#[test]
fn empty_function_yields_degenerate_cfg() {
let f = IRFunction::new();
@@ -238,13 +259,14 @@ mod tests {
assert!(cfg.blocks[0].preds.is_empty());
}
/// 幂等性(CFG-03):同一 IR 两次构建结果相等。
/// 幂等性:同一 IR 两次构建结果相等。
#[test]
fn build_cfg_is_idempotent() {
let mut f = IRFunction::new();
f.insts.push(Inst::jmp_if_false(1, 0)); // 0: → L0(回环目标)
f.insts.push(Inst::jmp(0)); // 1: → L0
f.insts.push(Inst::new(OpCode::RETURN, Operand::None, Operand::None, Operand::None)); // 2
f.insts
.push(Inst::new(OpCode::RETURN, Operand::None, Operand::None, Operand::None)); // 2
f.label_pos = vec![Some(2), Some(0)]; // L0→2(尾部), L1→0(回环)
f.label_count = 2;