diff --git a/src/mir/join_ir/lowering/loop_scope_shape.rs b/src/mir/join_ir/lowering/loop_scope_shape.rs index 2acbe931..f4302adb 100644 --- a/src/mir/join_ir/lowering/loop_scope_shape.rs +++ b/src/mir/join_ir/lowering/loop_scope_shape.rs @@ -201,6 +201,25 @@ pub(crate) struct LoopScopeShape { /// Progress carrier for loop termination check (future Verifier use) /// Typically the loop index variable (i, pos, etc.) pub progress_carrier: Option, + + /// Phase 48-4: 変数定義ブロックのマッピング + /// + /// LocalScopeInspectorBox の var_definitions 情報を統合。 + /// + /// # Phase 48-4 Note + /// + /// 現在は空の BTreeMap で初期化(API のみ提供)。 + /// Phase 48-5+ で from_existing_boxes_legacy から LocalScopeInspectorBox の情報を抽出して統合予定。 + /// + /// # Structure + /// + /// - Key: 変数名 + /// - Value: その変数が定義されているブロック ID の集合 + /// + /// # Usage + /// + /// `is_available_in_all()` メソッドで、変数が全指定ブロックで利用可能かを判定。 + pub(crate) variable_definitions: BTreeMap>, } impl LoopScopeShape { @@ -481,6 +500,10 @@ impl LoopScopeShape { // Determine progress_carrier (heuristic: first carrier, typically 'i') let progress_carrier = carriers.iter().next().cloned(); + // Phase 48-4: variable_definitions を空で初期化(API のみ提供) + // Phase 48-5+ で LocalScopeInspectorBox から情報を抽出して統合予定 + let variable_definitions = BTreeMap::new(); + Some(Self { // Block IDs from LoopForm header, @@ -493,6 +516,8 @@ impl LoopScopeShape { body_locals, exit_live, progress_carrier, + // Phase 48-4: Trio 質問 API サポート + variable_definitions, }) } @@ -544,6 +569,84 @@ impl LoopScopeShape { .map(|name| (name.clone(), self.classify(name))) .collect() } + + // ======================================================================== + // Phase 48-4: Trio 質問 API の統合 + // ======================================================================== + + /// Phase 48-4: ループ終了時に live な変数集合を返す + /// + /// LoopExitLivenessBox::compute_live_at_exit() の代替 API。 + /// + /// # Returns + /// + /// ループ終了後に使用される変数の集合(pinned + carriers + BodyLocalExit) + /// + /// # Phase 48-4 設計 + /// + /// この API は「質問だけの薄い箱」原則に基づく: + /// - callsite は `scope.get_exit_live()` という質問形式でアクセス + /// - フィールド直接参照より API 安定性が高い + /// - 将来 exit_live の実装変更に強い + /// + /// # Example + /// + /// ```ignore + /// let exit_live = scope.get_exit_live(); + /// for var in exit_live { + /// // exit PHI 生成 + /// } + /// ``` + pub fn get_exit_live(&self) -> &BTreeSet { + &self.exit_live + } + + /// Phase 48-4: 変数が全指定ブロックで利用可能か判定 + /// + /// LocalScopeInspectorBox::is_available_in_all() の代替 API。 + /// + /// # Arguments + /// + /// - `var_name`: 判定する変数名 + /// - `required_blocks`: 全てで利用可能であるべきブロック集合 + /// + /// # Returns + /// + /// - `true`: 変数が全指定ブロックで利用可能(全ブロックで定義済み) + /// - `false`: 一部ブロックで未定義、または変数が存在しない + /// + /// # Phase 48-4 実装状況 + /// + /// 現在は `variable_definitions` が空のため常に false を返す(API のみ提供)。 + /// Phase 48-5+ で from_existing_boxes_legacy から LocalScopeInspectorBox の情報を抽出して統合予定。 + /// + /// # Phase 48-5+ 実装計画 + /// + /// ```ignore + /// // LocalScopeInspectorBox から情報抽出 + /// for var in all_vars { + /// let def_blocks = inspector.get_defining_blocks(var); + /// variable_definitions.insert(var.clone(), def_blocks); + /// } + /// ``` + /// + /// # Example + /// + /// ```ignore + /// // BodyLocalExit vs BodyLocalInternal の判定に使用 + /// if scope.is_available_in_all(&var_name, &[body, exit]) { + /// LoopVarClass::BodyLocalExit + /// } else { + /// LoopVarClass::BodyLocalInternal + /// } + /// ``` + pub fn is_available_in_all(&self, var_name: &str, required_blocks: &[BasicBlockId]) -> bool { + if let Some(def_blocks) = self.variable_definitions.get(var_name) { + required_blocks.iter().all(|bid| def_blocks.contains(bid)) + } else { + false + } + } } // ============================================================================ @@ -861,6 +964,7 @@ mod tests { body_locals: std::collections::BTreeSet::new(), exit_live: vec!["i".to_string()].into_iter().collect(), progress_carrier: Some("i".to_string()), + variable_definitions: BTreeMap::new(), // Phase 48-4 }; // from_scope は None を返すべき @@ -1011,6 +1115,7 @@ mod tests { .into_iter() .collect(), progress_carrier: Some("i".to_string()), + variable_definitions: BTreeMap::new(), // Phase 48-4 }; // Pinned classification @@ -1047,6 +1152,7 @@ mod tests { .into_iter() .collect(), progress_carrier: Some("i".to_string()), + variable_definitions: BTreeMap::new(), // Phase 48-4 }; // classify() と needs_header_phi() / needs_exit_phi() が一致することを確認 @@ -1066,4 +1172,102 @@ mod tests { ); } } + + // ======================================================================== + // Phase 48-4: Trio 質問 API のテスト + // ======================================================================== + + /// Phase 48-4: get_exit_live() API テスト + #[test] + fn test_get_exit_live() { + 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: BTreeSet::new(), + exit_live: vec!["s".to_string(), "i".to_string()].into_iter().collect(), + progress_carrier: Some("i".to_string()), + variable_definitions: BTreeMap::new(), + }; + + let exit_live = scope.get_exit_live(); + assert_eq!(exit_live.len(), 2); + assert!(exit_live.contains("s")); + assert!(exit_live.contains("i")); + } + + /// Phase 48-4: is_available_in_all() API テスト(空の variable_definitions) + #[test] + fn test_is_available_in_all_phase48_4() { + // Phase 48-4: variable_definitions が空のため常に false + 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: BTreeSet::new(), + exit_live: vec!["s".to_string(), "i".to_string()].into_iter().collect(), + progress_carrier: Some("i".to_string()), + variable_definitions: BTreeMap::new(), // Phase 48-4: 空で初期化 + }; + + // variable_definitions が空のため、すべて false を返す + assert!(!scope.is_available_in_all("x", &[BasicBlockId::new(3)])); + assert!(!scope.is_available_in_all("i", &[BasicBlockId::new(3)])); + assert!(!scope.is_available_in_all("unknown", &[BasicBlockId::new(3)])); + } + + /// Phase 48-5+ 想定: is_available_in_all() with variable_definitions + #[test] + fn test_is_available_in_all_phase48_5_future() { + // Phase 48-5+ で variable_definitions が統合された状態をシミュレート + let mut variable_definitions = BTreeMap::new(); + variable_definitions.insert( + "x".to_string(), + vec![BasicBlockId::new(3), BasicBlockId::new(4)] + .into_iter() + .collect(), + ); + variable_definitions.insert( + "i".to_string(), + vec![BasicBlockId::new(2), BasicBlockId::new(3), BasicBlockId::new(4)] + .into_iter() + .collect(), + ); + + 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()].into_iter().collect(), + exit_live: vec!["s".to_string(), "i".to_string(), "x".to_string()] + .into_iter() + .collect(), + progress_carrier: Some("i".to_string()), + variable_definitions, // Phase 48-5+ で統合された状態 + }; + + // x は block 3, 4 で定義 → block 3, 4 を要求すれば true + assert!(scope.is_available_in_all("x", &[BasicBlockId::new(3), BasicBlockId::new(4)])); + + // x は block 2 で未定義 → block 2, 3 を要求すれば false + assert!(!scope.is_available_in_all("x", &[BasicBlockId::new(2), BasicBlockId::new(3)])); + + // i は block 2, 3, 4 で定義 → すべて要求しても true + assert!(scope.is_available_in_all( + "i", + &[BasicBlockId::new(2), BasicBlockId::new(3), BasicBlockId::new(4)] + )); + + // unknown は variable_definitions にない → false + assert!(!scope.is_available_in_all("unknown", &[BasicBlockId::new(3)])); + } }