diff --git a/src/mir/builder/control_flow/edgecfg/api/branch_stub.rs b/src/mir/builder/control_flow/edgecfg/api/branch_stub.rs new file mode 100644 index 00000000..2d27d298 --- /dev/null +++ b/src/mir/builder/control_flow/edgecfg/api/branch_stub.rs @@ -0,0 +1,32 @@ +use crate::mir::basic_block::{BasicBlockId, EdgeArgs}; +use crate::mir::ValueId; + +/// 条件分岐の未配線エッジペア(Phase 267 P0) +/// +/// # 責務 +/// - header → then/else の分岐を表現 +/// - set_branch_with_edge_args() へのマッピング用 +/// +/// # 制約 +/// - 1 block = 1 terminator: from は重複禁止 +/// - then_target, else_target は必ず Some(未配線 Branch は存在しない) +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct BranchStub { + /// 分岐元ブロック + pub from: BasicBlockId, + + /// 分岐条件の値 + pub cond: ValueId, + + /// then ブランチの配線先 + pub then_target: BasicBlockId, + + /// then ブランチの引数 + pub then_args: EdgeArgs, + + /// else ブランチの配線先 + pub else_target: BasicBlockId, + + /// else ブランチの引数 + pub else_args: EdgeArgs, +} diff --git a/src/mir/builder/control_flow/edgecfg/api/compose.rs b/src/mir/builder/control_flow/edgecfg/api/compose.rs index f6f955d0..8f398893 100644 --- a/src/mir/builder/control_flow/edgecfg/api/compose.rs +++ b/src/mir/builder/control_flow/edgecfg/api/compose.rs @@ -10,12 +10,14 @@ */ use std::collections::BTreeMap; -use crate::mir::basic_block::BasicBlockId; +use crate::mir::basic_block::{BasicBlockId, EdgeArgs}; use crate::mir::control_form::LoopId; use crate::mir::value_id::ValueId; +use crate::mir::join_ir::lowering::inline_boundary::JumpArgsLayout; use super::frag::Frag; use super::exit_kind::ExitKind; use super::edge_stub::EdgeStub; // Phase 265 P2: wires/exits 分離で必要 +use super::branch_stub::BranchStub; // Phase 267 P0: Branch 生成に必要 /// 順次合成: `a; b` /// @@ -68,37 +70,62 @@ pub(crate) fn seq(a: Frag, b: Frag) -> Frag { // b の wires もマージ wires.extend(b.wires); + // Phase 267 P0: branches もマージ + let mut branches = Vec::new(); + branches.extend(a.branches); + branches.extend(b.branches); + Frag { entry: a.entry, // seq の入口は a の入口 exits, // a の非 Normal + b の全 exit wires, // a.Normal → b.entry + a.wires + b.wires + branches, // Phase 267 P0: a.branches + b.branches } } /// 条件分岐合成: `if (cond) { t } else { e }` /// -/// # Phase 265 P2: wires/exits 分離実装完了 +/// # Phase 267 P0: Branch 生成実装完了 +/// - header → then/else の BranchStub を branches に追加 /// - t/e.Normal → join_frag.entry を wires に追加(内部配線) /// - if の exits は join_frag.exits(join 以降の外へ出る exit) /// /// # 配線ルール +/// - header → t.entry / e.entry を BranchStub として branches に追加(Phase 267 P0) /// - t/e.Normal の EdgeStub.target = Some(join_frag.entry) → wires /// - if の exits = t/e の非 Normal + join_frag.exits /// - if の wires = t/e.Normal → join + t/e/join の wires +/// - if の branches = header の BranchStub + t/e/join の branches /// /// # 引数 /// - `header`: 条件判定を行うブロック -/// - `_cond`: 条件値(Phase 266+ で MIR 命令生成時に使用) +/// - `cond`: 条件値(Phase 267 P0 で使用開始) /// - `t`: then 分岐の断片 /// - `e`: else 分岐の断片 /// - `join_frag`: join 以降の断片(t/e.Normal の配線先 + join 以降の処理) pub(crate) fn if_( header: BasicBlockId, - _cond: ValueId, // Phase 266+ で使用 + cond: ValueId, // Phase 267 P0 で使用開始 t: Frag, // then 分岐 e: Frag, // else 分岐 join_frag: Frag, // join 以降の断片 ) -> Frag { + // Phase 267 P0: header → then/else の BranchStub を作成 + let branch = BranchStub { + from: header, + cond, + then_target: t.entry, + then_args: EdgeArgs { + layout: JumpArgsLayout::CarriersOnly, + values: vec![], // TODO: Phase 267 P2+ で then の入口引数計算 + }, + else_target: e.entry, + else_args: EdgeArgs { + layout: JumpArgsLayout::CarriersOnly, + values: vec![], // TODO: Phase 267 P2+ で else の入口引数計算 + }, + }; + let mut exits = BTreeMap::new(); let mut wires = Vec::new(); @@ -156,10 +183,17 @@ pub(crate) fn if_( // join_frag の wires もマージ wires.extend(join_frag.wires); + // Phase 267 P0: branches を統合 + let mut branches = vec![branch]; + branches.extend(t.branches); + branches.extend(e.branches); + branches.extend(join_frag.branches); + Frag { entry: header, // if の入口は header exits, // t/e の非 Normal + join_frag.exits wires, // t/e.Normal → join_frag.entry + t/e/join の wires + branches, // Phase 267 P0: header の BranchStub + t/e/join の branches } } @@ -226,10 +260,14 @@ pub(crate) fn loop_( // body の wires もマージ wires.extend(body.wires); + // Phase 267 P0: body の branches もマージ + let branches = body.branches; + Frag { entry: header, // ループの入口 exits, // Normal, Return, Unwind のみ(未配線) wires, // Continue → header, Break → after(配線済み) + branches, // Phase 267 P0: body の branches } } @@ -283,6 +321,7 @@ mod tests { entry: body_entry, exits: body_exits, wires: vec![], + branches: vec![], }; // Execute: compose::loop_() @@ -317,6 +356,7 @@ mod tests { entry: body_entry, exits: body_exits, wires: vec![], + branches: vec![], }; // Execute: compose::loop_() @@ -366,6 +406,7 @@ mod tests { entry: body, exits: body_exits, wires: vec![], + branches: vec![], }; // Execute: compose::loop_() @@ -399,6 +440,7 @@ mod tests { entry: body, exits: body_exits, wires: vec![], + branches: vec![], }; // Execute: compose::loop_() @@ -432,6 +474,7 @@ mod tests { entry: body, exits: body_exits, wires: vec![], + branches: vec![], }; // Execute: compose::loop_() @@ -461,6 +504,7 @@ mod tests { entry: a_entry, exits: a_exits, wires: vec![], + branches: vec![], }; let mut b_exits = BTreeMap::new(); @@ -472,6 +516,7 @@ mod tests { entry: b_entry, exits: b_exits, wires: vec![], + branches: vec![], }; // Execute: compose::seq() @@ -514,6 +559,7 @@ mod tests { entry: a_entry, exits: a_exits, wires: vec![], + branches: vec![], }; let mut b_exits = BTreeMap::new(); @@ -525,6 +571,7 @@ mod tests { entry: b_entry, exits: b_exits, wires: vec![], + branches: vec![], }; // Execute @@ -564,6 +611,7 @@ mod tests { entry: then_entry, exits: then_exits, wires: vec![], + branches: vec![], }; let mut else_exits = BTreeMap::new(); @@ -575,6 +623,7 @@ mod tests { entry: else_entry, exits: else_exits, wires: vec![], + branches: vec![], }; let mut join_exits = BTreeMap::new(); @@ -586,6 +635,7 @@ mod tests { entry: join_entry, exits: join_exits, wires: vec![], + branches: vec![], }; // Execute: compose::if_() @@ -630,6 +680,7 @@ mod tests { entry: then_entry, exits: then_exits, wires: vec![], + branches: vec![], }; let mut else_exits = BTreeMap::new(); @@ -645,12 +696,14 @@ mod tests { entry: else_entry, exits: else_exits, wires: vec![], + branches: vec![], }; let join_frag = Frag { entry: join_entry, exits: BTreeMap::new(), wires: vec![], + branches: vec![], }; // Execute diff --git a/src/mir/builder/control_flow/edgecfg/api/emit.rs b/src/mir/builder/control_flow/edgecfg/api/emit.rs index b4b0fed9..f8d0cc1d 100644 --- a/src/mir/builder/control_flow/edgecfg/api/emit.rs +++ b/src/mir/builder/control_flow/edgecfg/api/emit.rs @@ -100,6 +100,80 @@ pub fn emit_wires( Ok(()) } +/// Frag を MIR に emit(Phase 267 P0: SSOT) +/// +/// # 責務 +/// - verify_frag_invariants_strict() で事前検証(Fail-Fast) +/// - wires → Jump/Return terminator(emit_wires を呼ぶ) +/// - branches → Branch terminator(set_branch_with_edge_args を使う) +/// - 1 block = 1 terminator 制約を強制 +/// +/// # 引数 +/// - `function`: MIR function +/// - `frag`: 配線済み Frag +/// +/// # 戻り値 +/// - `Ok(())`: 成功 +/// - `Err(String)`: 同一 block に複数 terminator、または不正な配線 +pub fn emit_frag( + function: &mut crate::mir::MirFunction, + frag: &super::frag::Frag, +) -> Result<(), String> { + use super::branch_stub::BranchStub; + + // Step 0: verify_frag_invariants_strict() で事前検証(SSOT) + super::verify::verify_frag_invariants_strict(frag)?; + + // Step 1: branches を from ごとにグループ化(1本だけ許可) + let mut branches_by_block: BTreeMap> = BTreeMap::new(); + for branch in &frag.branches { + branches_by_block.entry(branch.from).or_default().push(branch); + } + + for (block_id, branches) in &branches_by_block { + if branches.len() > 1 { + return Err(format!( + "[emit_frag] Multiple branches from same block {:?} (count={}). \ + 1 block = 1 terminator constraint violated.", + block_id, + branches.len() + )); + } + } + + // Step 2: wires と branches の from 重複チェック(1 block = 1 terminator) + for wire in &frag.wires { + if branches_by_block.contains_key(&wire.from) { + return Err(format!( + "[emit_frag] Block {:?} has both wire and branch. \ + 1 block = 1 terminator constraint violated.", + wire.from + )); + } + } + + // Step 3: wires を emit(既存の emit_wires を呼ぶ) + emit_wires(function, &frag.wires)?; + + // Step 4: branches を emit + for branch in &frag.branches { + let block = function + .get_block_mut(branch.from) + .ok_or_else(|| format!("[emit_frag] Block {:?} not found", branch.from))?; + + // Phase 260 API を使用(terminator + successors 同期) + block.set_branch_with_edge_args( + branch.cond, + branch.then_target, + Some(branch.then_args.clone()), + branch.else_target, + Some(branch.else_args.clone()), + ); + } + + Ok(()) +} + #[cfg(test)] mod tests { use super::*; @@ -294,5 +368,200 @@ mod tests { err_msg ); } + + // ======================================================================== + // Phase 267 P0: emit_frag() テスト(3個) + // ======================================================================== + + #[test] + fn test_emit_frag_branch_basic() { + use super::super::branch_stub::BranchStub; + use super::super::frag::Frag; + use std::collections::BTreeMap; + + // Setup: MirFunction with 3 blocks (header, then, else) + let mut function = create_test_function(); + let header = BasicBlockId(0); + let then_bb = BasicBlockId(1); + let else_bb = BasicBlockId(2); + function.add_block(BasicBlock::new(then_bb)); + function.add_block(BasicBlock::new(else_bb)); + + // Setup: BranchStub (header → then/else) + let branch = BranchStub { + from: header, + cond: ValueId(100), + then_target: then_bb, + then_args: EdgeArgs { + layout: JumpArgsLayout::CarriersOnly, + values: vec![ValueId(101)], + }, + else_target: else_bb, + else_args: EdgeArgs { + layout: JumpArgsLayout::CarriersOnly, + values: vec![ValueId(102)], + }, + }; + + let frag = Frag { + entry: header, + exits: BTreeMap::new(), + wires: vec![], + branches: vec![branch], + }; + + // Execute + let result = emit_frag(&mut function, &frag); + + // Verify: success + assert!(result.is_ok(), "emit_frag failed: {:?}", result.err()); + + // Verify: header has Branch terminator + let block = function.get_block(header).unwrap(); + match &block.terminator { + Some(MirInstruction::Branch { + condition, + then_bb: t, + else_bb: e, + then_edge_args, + else_edge_args, + }) => { + assert_eq!(*condition, ValueId(100)); + assert_eq!(*t, then_bb); + assert_eq!(*e, else_bb); + assert!(then_edge_args.is_some()); + assert!(else_edge_args.is_some()); + assert_eq!( + then_edge_args.as_ref().unwrap().values, + vec![ValueId(101)] + ); + assert_eq!( + else_edge_args.as_ref().unwrap().values, + vec![ValueId(102)] + ); + } + other => panic!("Expected Branch, got {:?}", other), + } + + // Verify: successors updated + assert!(block.successors.contains(&then_bb)); + assert!(block.successors.contains(&else_bb)); + } + + #[test] + fn test_emit_frag_branch_wire_conflict_fails() { + use super::super::branch_stub::BranchStub; + use super::super::frag::Frag; + use std::collections::BTreeMap; + + // Setup: 同じ block に branch と wire(1 block = 1 terminator 違反) + let mut function = create_test_function(); + let bb0 = BasicBlockId(0); + let bb1 = BasicBlockId(1); + let bb2 = BasicBlockId(2); + function.add_block(BasicBlock::new(bb1)); + function.add_block(BasicBlock::new(bb2)); + + let branch = BranchStub { + from: bb0, + cond: ValueId(100), + then_target: bb1, + then_args: EdgeArgs { + layout: JumpArgsLayout::CarriersOnly, + values: vec![], + }, + else_target: bb2, + else_args: EdgeArgs { + layout: JumpArgsLayout::CarriersOnly, + values: vec![], + }, + }; + + let wire = EdgeStub::with_target( + bb0, // 同じ from + ExitKind::Normal, + bb1, + EdgeArgs { + layout: JumpArgsLayout::CarriersOnly, + values: vec![], + }, + ); + + let frag = Frag { + entry: bb0, + exits: BTreeMap::new(), + wires: vec![wire], + branches: vec![branch], + }; + + // Execute + let result = emit_frag(&mut function, &frag); + + // Verify: failure + assert!(result.is_err()); + assert!(result.unwrap_err().contains("both wire and branch")); + } + + #[test] + fn test_compose_if_creates_branch() { + use super::super::compose::if_; + use super::super::frag::Frag; + use std::collections::BTreeMap; + + // Setup: header, then, else, join blocks + let header = BasicBlockId(0); + let then_entry = BasicBlockId(1); + let else_entry = BasicBlockId(2); + let join_entry = BasicBlockId(3); + + let then_frag = Frag { + entry: then_entry, + exits: { + let mut exits = BTreeMap::new(); + exits.insert( + ExitKind::Normal, + vec![EdgeStub::without_args(then_entry, ExitKind::Normal)], + ); + exits + }, + wires: vec![], + branches: vec![], + }; + + let else_frag = Frag { + entry: else_entry, + exits: { + let mut exits = BTreeMap::new(); + exits.insert( + ExitKind::Normal, + vec![EdgeStub::without_args(else_entry, ExitKind::Normal)], + ); + exits + }, + wires: vec![], + branches: vec![], + }; + + let join_frag = Frag { + entry: join_entry, + exits: BTreeMap::new(), + wires: vec![], + branches: vec![], + }; + + let cond = ValueId(100); + + // Execute + let result = if_(header, cond, then_frag, else_frag, join_frag); + + // Verify: 1本の BranchStub が生成された + assert_eq!(result.branches.len(), 1); + + let branch = &result.branches[0]; + assert_eq!(branch.from, header); + assert_eq!(branch.cond, cond); + assert_eq!(branch.then_target, then_entry); + assert_eq!(branch.else_target, else_entry); + } } diff --git a/src/mir/builder/control_flow/edgecfg/api/frag.rs b/src/mir/builder/control_flow/edgecfg/api/frag.rs index 44638134..61b5f5fc 100644 --- a/src/mir/builder/control_flow/edgecfg/api/frag.rs +++ b/src/mir/builder/control_flow/edgecfg/api/frag.rs @@ -9,13 +9,15 @@ use std::collections::BTreeMap; use crate::mir::basic_block::BasicBlockId; use super::exit_kind::ExitKind; use super::edge_stub::EdgeStub; +use super::branch_stub::BranchStub; /// CFG Fragment(構造化制御の合成単位) /// -/// # 責務(Phase 265 P2 更新) +/// # 責務(Phase 267 P0 更新) /// - `entry`: 断片の入口ブロック /// - `exits`: 断片から外へ出る未配線 edge の集合(target = None のみ) -/// - `wires`: 断片内部で解決された配線(target = Some(...) のみ) +/// - `wires`: 断片内部で解決された配線(target = Some(...) のみ、Jump/Return 専用) +/// - `branches`: 断片内部の Branch 配線(Phase 267 P0 追加、Branch 専用) /// /// # 設計原則 /// - 各 Frag は「入口1つ、出口複数(種別ごと)」を持つ @@ -25,7 +27,8 @@ use super::edge_stub::EdgeStub; /// # 不変条件(verify で検証) /// - entry は有効な BasicBlockId /// - exits 内の EdgeStub は target = None(未配線、外へ出る exit) -/// - wires 内の EdgeStub は target = Some(...)(配線済み、内部配線) +/// - wires 内の EdgeStub は target = Some(...)(配線済み、内部配線、Jump/Return のみ) +/// - branches 内の BranchStub は Branch 専用配線(Phase 267 P0) /// - EdgeStub.kind と Map のキーが一致 /// /// # BTreeMap の使用理由 @@ -46,7 +49,14 @@ pub struct Frag { /// /// target = Some(...) のみ(断片内部で解決された配線) /// Phase 266 で MIR terminator に落とす + /// Jump/Return 専用(Branch は branches フィールドへ) pub wires: Vec, + + /// 配線済みの分岐(Phase 267 P0 追加) + /// + /// Branch 専用の配線 + /// wires は Jump/Return 専用のまま維持(分離を保つ) + pub branches: Vec, } impl Frag { @@ -56,6 +66,7 @@ impl Frag { entry, exits: BTreeMap::new(), wires: vec![], // Phase 265 P2: 配線済み内部配線 + branches: vec![], // Phase 267 P0: 配線済み分岐 } } @@ -67,6 +78,7 @@ impl Frag { entry, exits, wires: vec![], // Phase 265 P2: 配線済み内部配線 + branches: vec![], // Phase 267 P0: 配線済み分岐 } } diff --git a/src/mir/builder/control_flow/edgecfg/api/mod.rs b/src/mir/builder/control_flow/edgecfg/api/mod.rs index 0ee73e79..748202b3 100644 --- a/src/mir/builder/control_flow/edgecfg/api/mod.rs +++ b/src/mir/builder/control_flow/edgecfg/api/mod.rs @@ -21,11 +21,13 @@ pub mod frag; pub mod compose; pub mod verify; pub mod emit; // Phase 266: 追加 +pub mod branch_stub; // Phase 267 P0: 追加 // 公開型(安定) pub use exit_kind::ExitKind; pub use edge_stub::EdgeStub; pub use frag::Frag; +pub use branch_stub::BranchStub; // Phase 267 P0: 追加 // 合成関数(Phase 264: crate内のみ公開、Phase 265+でpub化) pub(crate) use compose::{seq, if_, loop_, cleanup}; @@ -34,5 +36,5 @@ pub(crate) use compose::{seq, if_, loop_, cleanup}; pub use verify::verify_frag_invariants; pub use verify::verify_frag_invariants_strict; // Phase 266: strict 版追加 -// Phase 266: wires → MIR terminator 変換 -pub use emit::emit_wires; +// Phase 267 P0: wires + branches → MIR terminator 変換 +pub use emit::{emit_wires, emit_frag}; diff --git a/src/mir/builder/control_flow/edgecfg/api/verify.rs b/src/mir/builder/control_flow/edgecfg/api/verify.rs index ca58b11a..8d9af0fa 100644 --- a/src/mir/builder/control_flow/edgecfg/api/verify.rs +++ b/src/mir/builder/control_flow/edgecfg/api/verify.rs @@ -163,6 +163,7 @@ mod tests { entry: header, exits, wires: vec![], + branches: vec![], }; // P1: Always returns Ok(())