This repository has been archived on 2026-09-15. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
OxideJS/crates/oxide_cfg/src/partition.rs
T
2026-08-06 23:10:27 +08:00

105 lines
4.2 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! Pass 1:块头识别。
//!
//! 块头集合 = `{0}` ∪ label 目标位置 ∪ 跳转目标 ∪ 条件跳转 fallthrough 后继。
use oxide_bytecode::opcode::OpCode;
use oxide_ir::operand::Operand;
use oxide_ir::IRFunction;
/// 识别块头位置:返回 `heads`,长度 `insts.len()+1`(+1 容纳指向末尾的空尾块)。
pub(super) fn partition_blocks(f: &IRFunction) -> Vec<bool> {
let len = f.insts.len();
let mut heads = vec![false; len + 1];
heads[0] = true;
for p in f.label_pos.iter().flatten() {
heads[*p] = true;
}
for (i, inst) in f.insts.iter().enumerate() {
// label 恒在 b 槽(inst.rs 构造 API 保证,不查 rd/a 槽)。
if let Operand::Label(l) = inst.b {
match f.label_pos.get(l as usize).and_then(|p| *p) {
Some(p) => heads[p] = true,
None => debug_assert!(false, "unresolved label {l}"),
}
}
// 跳转的 fallthrough 后继是块头:条件跳转否则 cond+then 融块、条件边丢失;
// 无条件 JMP 同样必须切块头——JMP 不 fallthrough,但其后继位置需成块边界,
// 否则 JMP 与后续指令融块、块尾判定错、Jump 边丢失(DCE 可达性误删目标块)。
if matches!(inst.op, OpCode::JMP | OpCode::JMP_IF_TRUE | OpCode::JMP_IF_FALSE | OpCode::JMP_IF_NULLISH) {
heads[i + 1] = true;
}
}
heads
}
#[cfg(test)]
mod tests {
use super::*;
use oxide_ir::inst::Inst;
/// 线性无跳转函数:仅 entry 为块头,其余位置非块头。
#[test]
fn linear_function_only_entry_is_head() {
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));
let heads = partition_blocks(&f);
assert_eq!(heads, vec![true, false, false, false], "仅 inst 0 是块头");
}
/// label 目标是块头:`label_pos` 指向的指令位置置 heads。
#[test]
fn label_target_is_head() {
let mut f = IRFunction::new();
f.insts
.push(Inst::new(OpCode::NOP, Operand::None, Operand::None, Operand::None)); // 0
f.insts
.push(Inst::new(OpCode::NOP, Operand::None, Operand::None, Operand::None)); // 1: label 0 目标
f.insts
.push(Inst::new(OpCode::RETURN, Operand::None, Operand::None, Operand::None)); // 2
f.label_pos = vec![Some(1)];
f.label_count = 1;
let heads = partition_blocks(&f);
assert!(heads[0] && heads[1], "entry 与 label 目标都是块头");
assert!(!heads[2], "非块头位置保持 false");
}
/// 条件跳转的 fallthrough 后继是块头(否则 cond+then 融块、条件边丢失)。
#[test]
fn conditional_jump_fallthrough_successor_is_head() {
let mut f = IRFunction::new();
f.insts.push(Inst::jmp_if_false(1, 0)); // 0: cond → L0(else)
f.insts
.push(Inst::new(OpCode::NOP, Operand::None, Operand::None, Operand::None)); // 1: then
f.insts
.push(Inst::new(OpCode::RETURN, Operand::None, Operand::None, Operand::None)); // 2
f.label_pos = vec![Some(2)];
f.label_count = 1;
let heads = partition_blocks(&f);
assert!(heads[1], "条件跳转 fallthrough 后继是块头");
}
/// 无条件 JMP 的后继位置成块头(JMP 不 fallthrough,后续代码需独立成块,修复 Jump 边丢失)。
#[test]
fn unconditional_jmp_successor_is_head() {
let mut f = IRFunction::new();
f.insts.push(Inst::jmp(0)); // 0: → L0
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.label_pos = vec![Some(2)];
f.label_count = 1;
let heads = partition_blocks(&f);
assert!(heads[1], "无条件 JMP 后继位置是块头");
}
}