diff --git a/src/mir/join_ir/lowering/loop_scope_shape.rs b/src/mir/join_ir/lowering/loop_scope_shape.rs index bcb512c5..430e8853 100644 --- a/src/mir/join_ir/lowering/loop_scope_shape.rs +++ b/src/mir/join_ir/lowering/loop_scope_shape.rs @@ -44,8 +44,52 @@ use crate::mir::{BasicBlockId, MirFunction, MirQuery, ValueId}; /// ループ変数スコープの統合ビュー /// -/// # Fields +/// # Phase 30: 変数分類の唯一の仕様ソース (SSOT) /// +/// ## 4分類の定義 (from LoopVarClassBox) +/// +/// ### 1. Pinned(ループ外パラメータ) +/// - **定義**: ループ開始前に定義され、ループ内で**変更されない**変数 +/// - **needs_header_phi**: ✅ true(ループ再入時に値を維持) +/// - **needs_exit_phi**: ✅ true(ループ後も値が必要) +/// - **例**: `loop(i < limit) { ... }` の `limit` +/// +/// ### 2. Carrier(ループ更新変数) +/// - **定義**: 各イテレーションで**更新される**変数 +/// - **needs_header_phi**: ✅ true(前回値と新値をマージ) +/// - **needs_exit_phi**: ✅ true(最終値が必要) +/// - **例**: `loop(i < 10) { i = i + 1 }` の `i` +/// +/// ### 3. BodyLocalExit(全exit経路で定義) +/// - **定義**: ループ内で定義され、**全ての exit predecessor** で利用可能 +/// - **needs_header_phi**: ❌ false(ループ外には存在しない) +/// - **needs_exit_phi**: ✅ true(全exitで値が定義済み) +/// - **例**: `loop { local x = compute(); if x > 10 { break } }` +/// +/// ### 4. BodyLocalInternal(一部exit経路のみ) +/// - **定義**: ループ内で定義され、**一部の exit predecessor** でのみ利用可能 +/// - **needs_header_phi**: ❌ false +/// - **needs_exit_phi**: ❌ false(Option C: PHI pred mismatch 防止!) +/// - **例**: skip_whitespace の `ch`(一部break経路でのみ定義) +/// +/// ## PHI生成ルール早見表 +/// +/// | 分類 | header_phi | exit_phi | +/// |-------------------|------------|----------| +/// | Pinned | ✅ | ✅ | +/// | Carrier | ✅ | ✅ | +/// | BodyLocalExit | ❌ | ✅ | +/// | BodyLocalInternal | ❌ | ❌ | +/// +/// # Fields (Phase 30 Final Form) +/// +/// ## Block IDs (from LoopForm) +/// - `header`: ループヘッダブロック(条件チェック) +/// - `body`: ループボディブロック(メイン処理) +/// - `latch`: ループラッチブロック(header へ戻る) +/// - `exit`: ループ終了ブロック(ループ後の継続) +/// +/// ## Variable Classification /// - `pinned`: ループ外から来て変わらない変数(header/exit PHI 両方必要) /// - `carriers`: 各イテレーションで更新される変数(header/exit PHI 両方必要) /// - `body_locals`: ループ内だけで完結する変数(JoinIR では参照しない) @@ -53,6 +97,22 @@ use crate::mir::{BasicBlockId, MirFunction, MirQuery, ValueId}; /// - `progress_carrier`: ループを前に進める変数(将来の Verifier 用) #[derive(Debug, Clone)] pub(crate) struct LoopScopeShape { + // === Block IDs (Phase 30: from LoopForm) === + + /// Loop header block (condition check) + pub header: BasicBlockId, + + /// Loop body block (main processing) + pub body: BasicBlockId, + + /// Loop latch block (back-edge to header) + pub latch: BasicBlockId, + + /// Loop exit block (continuation after loop) + pub exit: BasicBlockId, + + // === Variable Classification === + /// Loop-crossing parameters (always need header/exit PHI) pub pinned: BTreeSet, @@ -65,6 +125,27 @@ pub(crate) struct LoopScopeShape { /// Variables live at exit (needs_exit_phi() == true) /// = pinned ∪ carriers ∪ BodyLocalExit + /// + /// # Phase 30: exit_live の唯一の情報源 (SSOT) + /// + /// このフィールドはループ終了後に使用される変数の唯一の正式ソース。 + /// LoopExitLivenessBox が返していた情報はここに統合される。 + /// + /// ## 計算方法 + /// + /// 1. LoopVarClassBox の分類結果から: + /// - Pinned → exit_live に追加 + /// - Carrier → exit_live に追加 + /// - BodyLocalExit → exit_live に追加 + /// - BodyLocalInternal → exit_live には**追加しない**(Option C) + /// + /// 2. LoopExitLivenessBox.compute_live_at_exit() の結果をマージ + /// (Phase 1: 空集合、Phase 2+: MIRスキャン結果) + /// + /// ## 将来計画 + /// + /// Phase 30完了後、LoopExitLivenessBox は削除され、 + /// このフィールドが唯一の live_at_exit 情報源になる。 pub exit_live: BTreeSet, /// Progress carrier for loop termination check (future Verifier use) @@ -73,31 +154,37 @@ pub(crate) struct LoopScopeShape { } impl LoopScopeShape { - /// Create LoopScopeShape from existing boxes (Phase 29: thin wrapper) + /// Create LoopScopeShape from existing boxes (Phase 30: unified interface) /// /// # Arguments /// + /// - `loop_form`: LoopForm containing block structure /// - `intake`: LoopFormIntake containing classified variable info /// - `var_classes`: LoopVarClassBox for classification /// - `exit_live_box`: LoopExitLivenessBox for exit liveness /// - `query`: MirQuery for liveness computation - /// - `exit_block`: Exit block ID /// /// # Returns /// /// Some(LoopScopeShape) if successful, None if critical data is missing. /// - /// # Phase 29 Behavior + /// # Phase 30 Design /// - /// Currently just reads from existing boxes without modifying behavior. - /// Future Phase 30 will absorb the boxes entirely. + /// This is the primary entry point for creating LoopScopeShape. + /// Block IDs come from loop_form, variable classification from existing boxes. + /// Future phases will internalize the box logic entirely. pub(crate) fn from_existing_boxes( + loop_form: &LoopForm, intake: &LoopFormIntake, var_classes: &LoopVarClassBox, exit_live_box: &LoopExitLivenessBox, query: &impl MirQuery, - exit_block: BasicBlockId, ) -> Option { + // Extract block IDs from LoopForm + let header = loop_form.header; + let body = loop_form.body; + let latch = loop_form.latch; + let exit = loop_form.exit; // Extract pinned and carriers from intake (already classified) let pinned: BTreeSet = intake.pinned_ordered.iter().cloned().collect(); let carriers: BTreeSet = intake.carrier_ordered.iter().cloned().collect(); @@ -105,7 +192,7 @@ impl LoopScopeShape { // Build LocalScopeInspectorBox for body_local classification let mut inspector = crate::mir::phi_core::local_scope_inspector::LocalScopeInspectorBox::new(); - inspector.record_snapshot(exit_block, &intake.header_snapshot); + inspector.record_snapshot(exit, &intake.header_snapshot); for (bb, snap) in &intake.exit_snapshots { inspector.record_snapshot(*bb, snap); } @@ -145,7 +232,7 @@ impl LoopScopeShape { // Compute exit_live from LoopExitLivenessBox (Phase 1: usually empty) let exit_live_from_box = exit_live_box.compute_live_at_exit( query, - exit_block, + exit, // Use exit from LoopForm &intake.header_snapshot, &intake.exit_snapshots, ); @@ -159,6 +246,12 @@ impl LoopScopeShape { let progress_carrier = carriers.iter().next().cloned(); Some(Self { + // Block IDs from LoopForm + header, + body, + latch, + exit, + // Variable classification pinned, carriers, body_locals, @@ -198,6 +291,55 @@ impl LoopScopeShape { pub fn exit_phi_vars(&self) -> Vec { self.exit_live.iter().cloned().collect() } + + /// Phase 30 F-1.1: 変数を4分類に分類する + /// + /// LoopVarClassBox.classify() と同じロジックを LoopScopeShape の内部状態から導出。 + /// 将来は LoopVarClassBox がこのメソッドに委譲するようになる。 + /// + /// # Returns + /// + /// - Pinned: pinned に含まれる + /// - Carrier: carriers に含まれる + /// - BodyLocalExit: body_locals かつ exit_live に含まれる + /// - BodyLocalInternal: body_locals に含まれるが exit_live には含まれない + /// + /// # Note + /// + /// 既知の変数でない場合は BodyLocalInternal を返す(保守的) + pub fn classify(&self, var_name: &str) -> LoopVarClass { + // Priority 1: Check if it's a pinned variable + if self.pinned.contains(var_name) { + return LoopVarClass::Pinned; + } + + // Priority 2: Check if it's a carrier variable + if self.carriers.contains(var_name) { + return LoopVarClass::Carrier; + } + + // Priority 3: Check body_local classification + if self.body_locals.contains(var_name) { + if self.exit_live.contains(var_name) { + return LoopVarClass::BodyLocalExit; + } else { + return LoopVarClass::BodyLocalInternal; + } + } + + // Unknown variable: conservative fallback (no PHI) + LoopVarClass::BodyLocalInternal + } + + /// Phase 30 F-1.1: 複数変数を一括分類 + /// + /// LoopVarClassBox.classify_all() の代替 + pub fn classify_all(&self, var_names: &[String]) -> Vec<(String, LoopVarClass)> { + var_names + .iter() + .map(|name| (name.clone(), self.classify(name))) + .collect() + } } // ============================================================================ @@ -223,8 +365,7 @@ impl LoopScopeShape { /// ``` #[derive(Debug, Clone)] pub(crate) struct CaseAContext { - /// LoopScopeShape(変数スコープ情報) - pub scope: LoopScopeShape, + // Phase 30: scope フィールド削除(ordered_pinned/carriers/exit_args に情報コピー済みで重複) /// 順序付き pinned 変数名 pub ordered_pinned: Vec, @@ -285,13 +426,13 @@ impl CaseAContext { // 2) MIR から pinned/carrier/exit 情報を抽出 let intake = intake_loop_form(loop_form, var_classes, query, mir_func)?; - // 3) LoopScopeShape を構築 + // 3) LoopScopeShape を構築 (Phase 30: includes block IDs) let scope = LoopScopeShape::from_existing_boxes( + loop_form, &intake, var_classes, exit_live, query, - loop_form.exit, )?; let ordered_pinned = scope.pinned_ordered(); @@ -323,7 +464,72 @@ impl CaseAContext { let exit_args = resolve_exit_args(&scope.exit_live, &name_to_loop_id, &ordered_carriers)?; Some(Self { - scope, + ordered_pinned, + ordered_carriers, + name_to_loop_id, + pinned_ids, + carrier_ids, + exit_args, + }) + } + + /// Phase 30: LoopScopeShape を直接受け取る新コンストラクタ + /// + /// # Arguments + /// + /// - `scope`: LoopScopeShape(変数スコープ情報) + /// - `log_tag`: ログ出力用タグ(例: "skip_ws", "trim") + /// - `loop_step_id_fn`: offset から ValueId を生成する関数 + /// + /// # Returns + /// + /// Some(CaseAContext) if successful, None if validation fails. + pub(crate) fn from_scope( + scope: LoopScopeShape, + log_tag: &str, + loop_step_id_fn: F, + ) -> Option + where + F: Fn(u32) -> ValueId, + { + // LoopForm validation using scope's block IDs + if scope.header == scope.exit { + eprintln!( + "[joinir/generic_case_a/{}] loop_form malformed (header == exit), fallback", + log_tag + ); + return None; + } + + let ordered_pinned = scope.pinned_ordered(); + let ordered_carriers = scope.carriers_ordered(); + + // 変数名 → ValueId マッピングを構築 + let mut name_to_loop_id: BTreeMap = BTreeMap::new(); + let mut offset: u32 = 0; + for name in &ordered_pinned { + name_to_loop_id.insert(name.clone(), loop_step_id_fn(offset)); + offset += 1; + } + for name in &ordered_carriers { + name_to_loop_id.insert(name.clone(), loop_step_id_fn(offset)); + offset += 1; + } + + // pinned_ids / carrier_ids を構築 + let pinned_ids: Vec = ordered_pinned + .iter() + .filter_map(|k| name_to_loop_id.get(k).copied()) + .collect(); + let carrier_ids: Vec = ordered_carriers + .iter() + .filter_map(|k| name_to_loop_id.get(k).copied()) + .collect(); + + // exit_args を解決 + let exit_args = resolve_exit_args(&scope.exit_live, &name_to_loop_id, &ordered_carriers)?; + + Some(Self { ordered_pinned, ordered_carriers, name_to_loop_id, @@ -365,6 +571,18 @@ mod tests { use crate::mir::ValueId; use std::collections::BTreeMap; + fn make_dummy_loop_form() -> LoopForm { + LoopForm { + preheader: BasicBlockId::new(1), + header: BasicBlockId::new(2), + body: BasicBlockId::new(3), + latch: BasicBlockId::new(4), + exit: BasicBlockId::new(100), + continue_targets: vec![], + break_targets: vec![], + } + } + fn make_dummy_intake() -> LoopFormIntake { let mut header_snapshot = BTreeMap::new(); header_snapshot.insert("s".to_string(), ValueId(10)); @@ -398,22 +616,29 @@ mod tests { #[test] fn test_from_existing_boxes_basic() { + let loop_form = make_dummy_loop_form(); let intake = make_dummy_intake(); let var_classes = LoopVarClassBox::new(); let exit_live_box = LoopExitLivenessBox::new(); let query = EmptyQuery; let scope = LoopScopeShape::from_existing_boxes( + &loop_form, &intake, &var_classes, &exit_live_box, &query, - BasicBlockId::new(100), ); assert!(scope.is_some()); let scope = scope.unwrap(); + // Block IDs should match loop_form + assert_eq!(scope.header, BasicBlockId::new(2)); + assert_eq!(scope.body, BasicBlockId::new(3)); + assert_eq!(scope.latch, BasicBlockId::new(4)); + assert_eq!(scope.exit, BasicBlockId::new(100)); + // pinned should be {s, n} assert!(scope.pinned.contains("s")); assert!(scope.pinned.contains("n")); @@ -429,17 +654,18 @@ mod tests { #[test] fn test_needs_header_phi() { + let loop_form = make_dummy_loop_form(); let intake = make_dummy_intake(); let var_classes = LoopVarClassBox::new(); let exit_live_box = LoopExitLivenessBox::new(); let query = EmptyQuery; let scope = LoopScopeShape::from_existing_boxes( + &loop_form, &intake, &var_classes, &exit_live_box, &query, - BasicBlockId::new(100), ) .unwrap(); @@ -451,17 +677,18 @@ mod tests { #[test] fn test_needs_exit_phi() { + let loop_form = make_dummy_loop_form(); let intake = make_dummy_intake(); let var_classes = LoopVarClassBox::new(); let exit_live_box = LoopExitLivenessBox::new(); let query = EmptyQuery; let scope = LoopScopeShape::from_existing_boxes( + &loop_form, &intake, &var_classes, &exit_live_box, &query, - BasicBlockId::new(100), ) .unwrap(); @@ -473,17 +700,18 @@ mod tests { #[test] fn test_ordered_accessors() { + let loop_form = make_dummy_loop_form(); let intake = make_dummy_intake(); let var_classes = LoopVarClassBox::new(); let exit_live_box = LoopExitLivenessBox::new(); let query = EmptyQuery; let scope = LoopScopeShape::from_existing_boxes( + &loop_form, &intake, &var_classes, &exit_live_box, &query, - BasicBlockId::new(100), ) .unwrap(); @@ -496,4 +724,216 @@ mod tests { assert_eq!(carriers.len(), 1); assert!(carriers.contains(&"i".to_string())); } + + // Phase 30: 追加テスト(4本) + + /// CaseAContext::from_scope で header == exit のとき None を返すテスト + #[test] + fn test_from_scope_validation_header_eq_exit() { + use crate::mir::join_ir::lowering::value_id_ranges::skip_ws as vid; + + // header == exit の不正な LoopScopeShape を作成 + let scope = LoopScopeShape { + header: BasicBlockId::new(10), + body: BasicBlockId::new(11), + latch: BasicBlockId::new(12), + exit: BasicBlockId::new(10), // header と同じ! + pinned: vec!["s".to_string()].into_iter().collect(), + carriers: vec!["i".to_string()].into_iter().collect(), + body_locals: std::collections::BTreeSet::new(), + exit_live: vec!["i".to_string()].into_iter().collect(), + progress_carrier: Some("i".to_string()), + }; + + // from_scope は None を返すべき + let ctx = CaseAContext::from_scope(scope, "test", |offset| vid::loop_step(offset)); + assert!(ctx.is_none(), "from_scope should return None when header == exit"); + } + + /// block IDs が LoopForm から正しく伝播されるテスト + #[test] + fn test_block_ids_preserved() { + let loop_form = LoopForm { + preheader: BasicBlockId::new(100), + header: BasicBlockId::new(200), + body: BasicBlockId::new(300), + latch: BasicBlockId::new(400), + exit: BasicBlockId::new(500), + continue_targets: vec![], + break_targets: vec![], + }; + + let intake = make_dummy_intake(); + let var_classes = LoopVarClassBox::new(); + let exit_live_box = LoopExitLivenessBox::new(); + let query = EmptyQuery; + + let scope = LoopScopeShape::from_existing_boxes( + &loop_form, + &intake, + &var_classes, + &exit_live_box, + &query, + ) + .unwrap(); + + // 正確に LoopForm の値が伝播されている + assert_eq!(scope.header, BasicBlockId::new(200)); + assert_eq!(scope.body, BasicBlockId::new(300)); + assert_eq!(scope.latch, BasicBlockId::new(400)); + assert_eq!(scope.exit, BasicBlockId::new(500)); + } + + /// BTreeSet による順序決定性の確認テスト + #[test] + fn test_deterministic_order() { + // 異なる挿入順で同じ要素を持つセット + let mut set1: std::collections::BTreeSet = std::collections::BTreeSet::new(); + set1.insert("z".to_string()); + set1.insert("a".to_string()); + set1.insert("m".to_string()); + + let mut set2: std::collections::BTreeSet = std::collections::BTreeSet::new(); + set2.insert("m".to_string()); + set2.insert("z".to_string()); + set2.insert("a".to_string()); + + // BTreeSet はソート順でイテレート + let vec1: Vec<_> = set1.iter().cloned().collect(); + let vec2: Vec<_> = set2.iter().cloned().collect(); + + assert_eq!(vec1, vec2); + assert_eq!(vec1, vec!["a".to_string(), "m".to_string(), "z".to_string()]); + } + + /// needs_header_phi と needs_exit_phi の一貫性テスト + #[test] + fn test_needs_phi_consistency() { + let loop_form = make_dummy_loop_form(); + let intake = make_dummy_intake(); + let var_classes = LoopVarClassBox::new(); + let exit_live_box = LoopExitLivenessBox::new(); + let query = EmptyQuery; + + let scope = LoopScopeShape::from_existing_boxes( + &loop_form, + &intake, + &var_classes, + &exit_live_box, + &query, + ) + .unwrap(); + + // pinned 変数: header_phi必要、exit_phi必要 + for var in &scope.pinned { + assert!( + scope.needs_header_phi(var), + "pinned var {} should need header phi", + var + ); + assert!( + scope.needs_exit_phi(var), + "pinned var {} should need exit phi", + var + ); + } + + // carrier 変数: header_phi必要、exit_phi必要 + for var in &scope.carriers { + assert!( + scope.needs_header_phi(var), + "carrier var {} should need header phi", + var + ); + assert!( + scope.needs_exit_phi(var), + "carrier var {} should need exit phi", + var + ); + } + + // body_local 変数: header_phi不要 + for var in &scope.body_locals { + assert!( + !scope.needs_header_phi(var), + "body_local var {} should NOT need header phi", + var + ); + } + } + + /// Phase 30 F-1.1: classify() メソッドのテスト + #[test] + fn test_classify_method() { + // 手動で LoopScopeShape を構築(body_locals も含む) + let scope = LoopScopeShape { + header: BasicBlockId::new(2), + body: BasicBlockId::new(3), + latch: BasicBlockId::new(4), + exit: BasicBlockId::new(100), + pinned: vec!["s".to_string(), "n".to_string()].into_iter().collect(), + carriers: vec!["i".to_string()].into_iter().collect(), + body_locals: vec!["x".to_string(), "ch".to_string()].into_iter().collect(), + exit_live: vec![ + "s".to_string(), + "n".to_string(), + "i".to_string(), + "x".to_string(), // BodyLocalExit + ] + .into_iter() + .collect(), + progress_carrier: Some("i".to_string()), + }; + + // Pinned classification + assert_eq!(scope.classify("s"), LoopVarClass::Pinned); + assert_eq!(scope.classify("n"), LoopVarClass::Pinned); + + // Carrier classification + assert_eq!(scope.classify("i"), LoopVarClass::Carrier); + + // BodyLocalExit classification (in body_locals AND exit_live) + assert_eq!(scope.classify("x"), LoopVarClass::BodyLocalExit); + + // BodyLocalInternal classification (in body_locals but NOT in exit_live) + assert_eq!(scope.classify("ch"), LoopVarClass::BodyLocalInternal); + + // Unknown variable → BodyLocalInternal (conservative) + assert_eq!(scope.classify("unknown"), LoopVarClass::BodyLocalInternal); + } + + /// Phase 30 F-1.1: classify() と needs_*_phi() の一貫性 + #[test] + fn test_classify_phi_consistency() { + let scope = LoopScopeShape { + header: BasicBlockId::new(2), + body: BasicBlockId::new(3), + latch: BasicBlockId::new(4), + exit: BasicBlockId::new(100), + pinned: vec!["s".to_string()].into_iter().collect(), + carriers: vec!["i".to_string()].into_iter().collect(), + body_locals: vec!["x".to_string(), "ch".to_string()].into_iter().collect(), + exit_live: vec!["s".to_string(), "i".to_string(), "x".to_string()] + .into_iter() + .collect(), + progress_carrier: Some("i".to_string()), + }; + + // classify() と needs_header_phi() / needs_exit_phi() が一致することを確認 + for var in ["s", "i", "x", "ch", "unknown"] { + let class = scope.classify(var); + assert_eq!( + class.needs_header_phi(), + scope.needs_header_phi(var), + "classify and needs_header_phi mismatch for {}", + var + ); + assert_eq!( + class.needs_exit_phi(), + scope.needs_exit_phi(var), + "classify and needs_exit_phi mismatch for {}", + var + ); + } + } } diff --git a/src/mir/phi_core/header_phi_builder.rs b/src/mir/phi_core/header_phi_builder.rs index ae91b05d..5f1f5283 100644 --- a/src/mir/phi_core/header_phi_builder.rs +++ b/src/mir/phi_core/header_phi_builder.rs @@ -51,8 +51,7 @@ pub(crate) fn is_joinir_header_bypass_target(fn_name: &str) -> bool { pub(crate) struct LoopBypassFlags { /// Header φ バイパスが有効か pub header: bool, - /// Exit φ バイパスが有効か(Phase 27.6-2, 現在未使用) - pub exit: bool, + // Phase 30: exit フィールド削除(完全未使用、将来 JoinIR で代替予定) } /// Phase 27.4-C Refactor: JoinIR Loop φ バイパスフラグを取得 @@ -80,8 +79,7 @@ pub(crate) fn get_loop_bypass_flags(fn_name: &str) -> LoopBypassFlags { LoopBypassFlags { header: joinir_exp && header_exp && is_joinir_header_bypass_target(fn_name), - // Phase 27.6-2: Exit φ バイパスは将来的に追加予定 - exit: false, + // Phase 30: exit フィールド削除済み } } diff --git a/src/mir/phi_core/local_scope_inspector.rs b/src/mir/phi_core/local_scope_inspector.rs index af239864..12025960 100644 --- a/src/mir/phi_core/local_scope_inspector.rs +++ b/src/mir/phi_core/local_scope_inspector.rs @@ -1,5 +1,14 @@ /// LocalScopeInspectorBox - Variable definition tracker /// +/// # Phase 30: LoopScopeShape 移行中 +/// +/// このBoxは将来 LoopScopeShape に吸収される予定。 +/// 定義位置情報は LoopScopeShape::from_existing_boxes() 内部で集約され、 +/// 変数分類に使用される。 +/// +/// **新規コード**: LoopScopeShape が利用可能な場合は、 +/// `classify()` メソッドを直接使うことを推奨。 +/// /// # Purpose /// /// This box tracks which variables are defined in which basic blocks. @@ -145,6 +154,30 @@ impl LocalScopeInspectorBox { .map(|blocks| blocks.len()) .unwrap_or(0) } + + // ======================================================================== + // Phase 30 F-1.3: LoopScopeShape 委譲メソッド + // ======================================================================== + + /// Phase 30: LoopScopeShape を使って変数が全 exit pred で利用可能か判定 + /// + /// 新規コードでは LoopScopeShape::classify() を直接使うことを推奨。 + /// このメソッドは LoopScopeShape が既にある場合の便利メソッド。 + /// + /// # Note + /// + /// LoopScopeShape::classify() は既に exit_live 情報を含んでいるため、 + /// 直接 classify() → needs_exit_phi() を使う方が効率的。 + pub fn is_available_in_all_with_scope( + &self, + var_name: &str, + scope: &crate::mir::join_ir::lowering::loop_scope_shape::LoopScopeShape, + ) -> bool { + // LoopScopeShape の分類を使用 + // Pinned/Carrier/BodyLocalExit → available in all (exit PHI needed) + // BodyLocalInternal → NOT available in all (no exit PHI) + scope.needs_exit_phi(var_name) + } } // ============================================================================ diff --git a/src/mir/phi_core/loop_exit_liveness.rs b/src/mir/phi_core/loop_exit_liveness.rs index 7863a3f5..076d780f 100644 --- a/src/mir/phi_core/loop_exit_liveness.rs +++ b/src/mir/phi_core/loop_exit_liveness.rs @@ -43,6 +43,13 @@ pub trait ExitLivenessProvider: Send + Sync { /// Loop Exit Liveness Box(Legacy/Phase 1) /// +/// # Phase 30: LoopScopeShape 移行中 +/// +/// このBoxは将来 LoopScopeShape に吸収される予定。 +/// LoopScopeShape.exit_live が唯一の live_at_exit 情報源になる。 +/// +/// **新規コード**: `LoopScopeShape::exit_live` を直接参照すること。 +/// /// # Purpose /// Exit後で本当に使われる変数を決定する専門箱 /// @@ -171,6 +178,29 @@ impl LoopExitLivenessBox { live_vars } + + // ======================================================================== + // Phase 30 F-1.2: LoopScopeShape 委譲メソッド + // ======================================================================== + + /// Phase 30: LoopScopeShape の exit_live を直接取得 + /// + /// 新規コードではこのメソッドを使うことを推奨。 + /// 将来的には旧 compute_live_at_exit() メソッドを削除し、 + /// LoopScopeShape::exit_live を直接参照するのが標準になる。 + /// + /// # Example + /// + /// ```ignore + /// let scope = LoopScopeShape::from_existing_boxes(...)?; + /// let live = liveness_box.get_exit_live_from_scope(&scope); + /// ``` + pub fn get_exit_live_from_scope( + &self, + scope: &crate::mir::join_ir::lowering::loop_scope_shape::LoopScopeShape, + ) -> BTreeSet { + scope.exit_live.clone() + } } impl ExitLivenessProvider for LoopExitLivenessBox { diff --git a/src/mir/phi_core/loop_var_classifier.rs b/src/mir/phi_core/loop_var_classifier.rs index 25de55d5..9a4236cf 100644 --- a/src/mir/phi_core/loop_var_classifier.rs +++ b/src/mir/phi_core/loop_var_classifier.rs @@ -108,7 +108,12 @@ impl LoopVarClass { /// Loop variable classifier box /// -/// # Usage +/// # Phase 30: LoopScopeShape 移行中 +/// +/// このBoxは将来 LoopScopeShape に吸収される予定。 +/// 新規コードは `LoopScopeShape::classify()` を直接使うことを推奨。 +/// +/// # Usage (Legacy) /// /// ``` /// let inspector = LocalScopeInspectorBox::new(); @@ -127,6 +132,13 @@ impl LoopVarClass { /// // Generate exit PHI /// } /// ``` +/// +/// # Usage (Phase 30 Recommended) +/// +/// ``` +/// // LoopScopeShape が利用可能な場合は直接使う +/// let class = scope.classify("ch"); +/// ``` #[derive(Debug, Clone, Default)] pub struct LoopVarClassBox; @@ -138,6 +150,11 @@ impl LoopVarClassBox { /// Classify a variable for PHI generation decision /// + /// # Phase 30 TODO + /// + /// このメソッドは将来 `LoopScopeShape::classify()` に置き換える予定。 + /// 呼び出し側が LoopScopeShape を持っている場合は、そちらを直接使うこと。 + /// /// # Parameters /// /// - `var_name`: Variable to classify @@ -245,6 +262,38 @@ impl LoopVarClassBox { .cloned() .collect() } + + // ======================================================================== + // Phase 30 F-1.1: LoopScopeShape 委譲メソッド + // ======================================================================== + + /// Phase 30: LoopScopeShape を使って変数を分類 + /// + /// 新規コードではこのメソッドを使うことを推奨。 + /// 将来的には旧 classify() メソッドを削除し、このメソッドが標準になる。 + /// + /// # Example + /// + /// ```ignore + /// let scope = LoopScopeShape::from_existing_boxes(...)?; + /// let class = classifier.classify_with_scope("ch", &scope); + /// ``` + pub fn classify_with_scope( + &self, + var_name: &str, + scope: &crate::mir::join_ir::lowering::loop_scope_shape::LoopScopeShape, + ) -> LoopVarClass { + scope.classify(var_name) + } + + /// Phase 30: LoopScopeShape を使って複数変数を一括分類 + pub fn classify_all_with_scope( + &self, + var_names: &[String], + scope: &crate::mir::join_ir::lowering::loop_scope_shape::LoopScopeShape, + ) -> Vec<(String, LoopVarClass)> { + scope.classify_all(var_names) + } } // ============================================================================ diff --git a/src/mir/phi_core/phi_builder_box.rs b/src/mir/phi_core/phi_builder_box.rs index dd7c1d54..901fbe0c 100644 --- a/src/mir/phi_core/phi_builder_box.rs +++ b/src/mir/phi_core/phi_builder_box.rs @@ -51,8 +51,7 @@ use std::collections::BTreeMap; pub struct PhiBuilderBox { /// If PHI生成時のコンテキスト(将来拡張用) if_context: Option, - /// Loop PHI生成時のコンテキスト(将来拡張用) - loop_context: Option, + // Phase 30: loop_context 削除(完全未使用、将来 JoinIR で代替予定) } /// If PHI生成コンテキスト(Phase 26-F-2: 箱理論による責務分離) @@ -85,19 +84,13 @@ pub struct IfPhiContext { pub loop_carrier_names: std::collections::BTreeSet, } -/// Loop PHI生成コンテキスト(将来拡張用) -#[derive(Debug, Clone)] -struct LoopPhiContext { - /// 予約済み(Phase 3で実装) - _reserved: (), -} +// Phase 30: LoopPhiContext 削除(完全未使用、将来 JoinIR で代替予定) impl PhiBuilderBox { /// 新しいPhiBuilderBoxを作成 pub fn new() -> Self { Self { if_context: None, - loop_context: None, } } @@ -576,7 +569,7 @@ mod tests { fn test_phi_builder_box_creation() { let builder = PhiBuilderBox::new(); assert!(builder.if_context.is_none()); - assert!(builder.loop_context.is_none()); + // Phase 30: loop_context 削除済み } #[test]