2025-12-21 12:49:53 +09:00
|
|
|
|
/*!
|
|
|
|
|
|
* Frag - CFG Fragment(Phase 264: 構造化制御の合成単位)
|
|
|
|
|
|
*
|
|
|
|
|
|
* 構造化制御(if/loop/catch/cleanup)から CFG への lowering において、
|
|
|
|
|
|
* 未配線の脱出エッジを持つ CFG 断片を表現する。
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
use std::collections::BTreeMap;
|
|
|
|
|
|
use crate::mir::basic_block::BasicBlockId;
|
|
|
|
|
|
use super::exit_kind::ExitKind;
|
|
|
|
|
|
use super::edge_stub::EdgeStub;
|
2025-12-21 20:33:11 +09:00
|
|
|
|
use super::branch_stub::BranchStub;
|
2025-12-21 12:49:53 +09:00
|
|
|
|
|
|
|
|
|
|
/// CFG Fragment(構造化制御の合成単位)
|
|
|
|
|
|
///
|
2025-12-21 20:33:11 +09:00
|
|
|
|
/// # 責務(Phase 267 P0 更新)
|
2025-12-21 12:49:53 +09:00
|
|
|
|
/// - `entry`: 断片の入口ブロック
|
2025-12-21 16:47:47 +09:00
|
|
|
|
/// - `exits`: 断片から外へ出る未配線 edge の集合(target = None のみ)
|
2025-12-21 20:33:11 +09:00
|
|
|
|
/// - `wires`: 断片内部で解決された配線(target = Some(...) のみ、Jump/Return 専用)
|
|
|
|
|
|
/// - `branches`: 断片内部の Branch 配線(Phase 267 P0 追加、Branch 専用)
|
2025-12-21 12:49:53 +09:00
|
|
|
|
///
|
|
|
|
|
|
/// # 設計原則
|
|
|
|
|
|
/// - 各 Frag は「入口1つ、出口複数(種別ごと)」を持つ
|
|
|
|
|
|
/// - 合成則(seq/if_/loop_/cleanup)で複数 Frag を組み合わせる
|
|
|
|
|
|
/// - pattern番号は「形の認識」までに留め、配線層へ逆流させない
|
|
|
|
|
|
///
|
|
|
|
|
|
/// # 不変条件(verify で検証)
|
|
|
|
|
|
/// - entry は有効な BasicBlockId
|
2025-12-21 16:47:47 +09:00
|
|
|
|
/// - exits 内の EdgeStub は target = None(未配線、外へ出る exit)
|
2025-12-21 20:33:11 +09:00
|
|
|
|
/// - wires 内の EdgeStub は target = Some(...)(配線済み、内部配線、Jump/Return のみ)
|
|
|
|
|
|
/// - branches 内の BranchStub は Branch 専用配線(Phase 267 P0)
|
2025-12-21 12:49:53 +09:00
|
|
|
|
/// - EdgeStub.kind と Map のキーが一致
|
|
|
|
|
|
///
|
|
|
|
|
|
/// # BTreeMap の使用理由
|
|
|
|
|
|
/// - Phase 69-3 の教訓: HashMap は非決定的イテレーションを起こす
|
|
|
|
|
|
/// - ExitKind の順序を決定的にすることで、デバッグ出力・テストが安定
|
|
|
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
|
|
pub struct Frag {
|
|
|
|
|
|
/// 断片の入口ブロック
|
|
|
|
|
|
pub entry: BasicBlockId,
|
|
|
|
|
|
|
|
|
|
|
|
/// 断片からの未配線脱出エッジ(ExitKind → EdgeStub のリスト)
|
|
|
|
|
|
///
|
2025-12-21 16:47:47 +09:00
|
|
|
|
/// Phase 265 P2: target = None のみ(外へ出る exit)
|
2025-12-21 12:49:53 +09:00
|
|
|
|
/// BTreeMap を使用して決定的順序を保証(Phase 69-3 の教訓)
|
|
|
|
|
|
pub exits: BTreeMap<ExitKind, Vec<EdgeStub>>,
|
2025-12-21 16:47:47 +09:00
|
|
|
|
|
|
|
|
|
|
/// 配線済みの内部配線(Phase 265 P2 追加)
|
|
|
|
|
|
///
|
|
|
|
|
|
/// target = Some(...) のみ(断片内部で解決された配線)
|
|
|
|
|
|
/// Phase 266 で MIR terminator に落とす
|
2025-12-21 20:33:11 +09:00
|
|
|
|
/// Jump/Return 専用(Branch は branches フィールドへ)
|
2025-12-21 16:47:47 +09:00
|
|
|
|
pub wires: Vec<EdgeStub>,
|
2025-12-21 20:33:11 +09:00
|
|
|
|
|
|
|
|
|
|
/// 配線済みの分岐(Phase 267 P0 追加)
|
|
|
|
|
|
///
|
|
|
|
|
|
/// Branch 専用の配線
|
|
|
|
|
|
/// wires は Jump/Return 専用のまま維持(分離を保つ)
|
|
|
|
|
|
pub branches: Vec<BranchStub>,
|
2025-12-21 12:49:53 +09:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
impl Frag {
|
|
|
|
|
|
/// 新規 Frag を生成(出口なし)
|
|
|
|
|
|
pub fn new(entry: BasicBlockId) -> Self {
|
|
|
|
|
|
Self {
|
|
|
|
|
|
entry,
|
|
|
|
|
|
exits: BTreeMap::new(),
|
2025-12-21 16:47:47 +09:00
|
|
|
|
wires: vec![], // Phase 265 P2: 配線済み内部配線
|
2025-12-21 20:33:11 +09:00
|
|
|
|
branches: vec![], // Phase 267 P0: 配線済み分岐
|
2025-12-21 12:49:53 +09:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// 単一出口を持つ Frag を生成
|
|
|
|
|
|
pub fn with_single_exit(entry: BasicBlockId, stub: EdgeStub) -> Self {
|
|
|
|
|
|
let mut exits = BTreeMap::new();
|
|
|
|
|
|
exits.insert(stub.kind, vec![stub]);
|
2025-12-21 16:47:47 +09:00
|
|
|
|
Self {
|
|
|
|
|
|
entry,
|
|
|
|
|
|
exits,
|
|
|
|
|
|
wires: vec![], // Phase 265 P2: 配線済み内部配線
|
2025-12-21 20:33:11 +09:00
|
|
|
|
branches: vec![], // Phase 267 P0: 配線済み分岐
|
2025-12-21 16:47:47 +09:00
|
|
|
|
}
|
2025-12-21 12:49:53 +09:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// 特定 ExitKind の未配線 edge を追加
|
|
|
|
|
|
pub fn add_exit(&mut self, stub: EdgeStub) {
|
|
|
|
|
|
self.exits.entry(stub.kind).or_insert_with(Vec::new).push(stub);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// 特定 ExitKind の未配線 edge を取得
|
|
|
|
|
|
pub fn get_exits(&self, kind: &ExitKind) -> Option<&Vec<EdgeStub>> {
|
|
|
|
|
|
self.exits.get(kind)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// すべての ExitKind を列挙
|
|
|
|
|
|
pub fn exit_kinds(&self) -> impl Iterator<Item = &ExitKind> {
|
|
|
|
|
|
self.exits.keys()
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ============================================================================
|
|
|
|
|
|
// ユニットテスト(Phase 264: 最小3個)
|
|
|
|
|
|
// ============================================================================
|
|
|
|
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
|
mod tests {
|
|
|
|
|
|
use super::*;
|
|
|
|
|
|
use crate::mir::basic_block::EdgeArgs;
|
|
|
|
|
|
use crate::mir::join_ir::lowering::inline_boundary::JumpArgsLayout;
|
|
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
|
fn test_frag_basic_construction() {
|
|
|
|
|
|
// 最小のテスト: Frag が ExitKind::Normal を保持できる
|
|
|
|
|
|
let entry = BasicBlockId::new(0);
|
|
|
|
|
|
let from = BasicBlockId::new(1);
|
|
|
|
|
|
|
|
|
|
|
|
let stub = EdgeStub::without_args(from, ExitKind::Normal);
|
|
|
|
|
|
let frag = Frag::with_single_exit(entry, stub.clone());
|
|
|
|
|
|
|
|
|
|
|
|
assert_eq!(frag.entry, entry);
|
|
|
|
|
|
|
|
|
|
|
|
let exits = frag.get_exits(&ExitKind::Normal).unwrap();
|
|
|
|
|
|
assert_eq!(exits.len(), 1);
|
|
|
|
|
|
assert_eq!(exits[0].from, from);
|
|
|
|
|
|
assert_eq!(exits[0].kind, ExitKind::Normal);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
|
fn test_frag_multiple_exits() {
|
|
|
|
|
|
// 複数 ExitKind のテスト
|
|
|
|
|
|
let entry = BasicBlockId::new(0);
|
|
|
|
|
|
let mut frag = Frag::new(entry);
|
|
|
|
|
|
|
|
|
|
|
|
frag.add_exit(EdgeStub::without_args(BasicBlockId::new(1), ExitKind::Normal));
|
|
|
|
|
|
frag.add_exit(EdgeStub::without_args(BasicBlockId::new(2), ExitKind::Return));
|
|
|
|
|
|
|
|
|
|
|
|
assert_eq!(frag.exits.len(), 2);
|
|
|
|
|
|
assert!(frag.get_exits(&ExitKind::Normal).is_some());
|
|
|
|
|
|
assert!(frag.get_exits(&ExitKind::Return).is_some());
|
|
|
|
|
|
assert!(frag.get_exits(&ExitKind::Unwind).is_none());
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
|
fn test_exit_kind_helpers() {
|
|
|
|
|
|
use crate::mir::control_form::LoopId;
|
|
|
|
|
|
|
|
|
|
|
|
assert!(!ExitKind::Normal.is_loop_exit());
|
|
|
|
|
|
assert!(ExitKind::Break(LoopId(0)).is_loop_exit());
|
|
|
|
|
|
assert!(ExitKind::Continue(LoopId(0)).is_loop_exit());
|
|
|
|
|
|
|
|
|
|
|
|
assert!(ExitKind::Return.is_function_exit());
|
|
|
|
|
|
assert!(ExitKind::Unwind.is_function_exit());
|
|
|
|
|
|
assert!(!ExitKind::Normal.is_function_exit());
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|