//! Conservative PHI Generation - Box Theory Application //! //! Theory: Conservative ∘ Elimination = Minimal SSA //! - Conservative (this): correctness-first, generate all PHIs //! - Elimination (future): efficiency optimization, remove unused PHIs //! //! Phase 25.1q: Conservative PHI戦略の一元化 //! - phi.rs の Conservative PHI ロジック(L23-117)を統一 //! - void emission、predecessor fallback の一貫性保証 use crate::mir::ValueId; use std::collections::{HashMap, HashSet}; /// Conservative PHI 戦略による変数マージ分析 pub struct ConservativeMerge { /// 全ブランチに存在する変数のユニオン(Conservative戦略) pub all_vars: HashSet, /// 実際に変更された変数のセット(デバッグ/ヒント用) pub changed_vars: HashSet, } impl ConservativeMerge { /// 全変数のユニオンを計算(Conservative戦略) /// /// # Arguments /// * `pre_if` - if文前のスナップショット /// * `then_end` - then-branch終了時の変数マップ /// * `else_end_opt` - else-branch終了時の変数マップ(Noneの場合はempty else) pub fn analyze( pre_if: &HashMap, then_end: &HashMap, else_end_opt: &Option>, ) -> Self { let mut all_vars = HashSet::new(); all_vars.extend(pre_if.keys().cloned()); all_vars.extend(then_end.keys().cloned()); if let Some(ref else_map) = else_end_opt { all_vars.extend(else_map.keys().cloned()); } let changed = crate::mir::phi_core::if_phi::compute_modified_names(pre_if, then_end, else_end_opt); let changed_vars = changed.into_iter().collect(); Self { all_vars, changed_vars, } } /// Conservative フォールバック値取得 /// /// # Returns /// * `Some((then_v, else_v))` - 両ブランチの値(void emission 含む) /// * `None` - どこにも定義されていない変数(スキップ) /// /// # Conservative Rules /// 1. Both defined: use both values /// 2. Only then: use then + void /// 3. Only else: use void + else /// 4. Neither: skip (return None) pub fn get_conservative_values( &self, name: &str, pre_if: &HashMap, then_end: &HashMap, else_end_opt: &Option>, emit_void: F, ) -> Option<(ValueId, ValueId)> where F: Fn() -> ValueId, { let pre_val_opt = pre_if.get(name).copied(); // Fallback to predecessor value if not defined in a branch let then_v_opt = then_end.get(name).copied().or(pre_val_opt); let else_v_opt = else_end_opt .as_ref() .and_then(|m| m.get(name).copied()) .or(pre_val_opt); match (then_v_opt, else_v_opt) { (Some(tv), Some(ev)) => Some((tv, ev)), (Some(tv), None) => { // Variable exists in then branch but not else or predecessor // Emit a 'const void' instruction to represent undefined value Some((tv, emit_void())) } (None, Some(ev)) => { // Variable exists in else branch but not then or predecessor // Emit a 'const void' instruction to represent undefined value Some((emit_void(), ev)) } (None, None) => { // Variable doesn't exist anywhere - skip None } } } /// Debug trace 出力(Conservative PHI生成の可視化) pub fn trace_if_enabled( &self, pre_if: &HashMap, then_end: &HashMap, else_end_opt: &Option>, ) { let trace_conservative = std::env::var("NYASH_CONSERVATIVE_PHI_TRACE") .ok() .as_deref() == Some("1"); if trace_conservative { eprintln!("[Conservative PHI] all_vars count: {}", self.all_vars.len()); eprintln!( "[Conservative PHI] pre_if_snapshot: {:?}", pre_if.keys().collect::>() ); eprintln!( "[Conservative PHI] then_map_end: {:?}", then_end.keys().collect::>() ); if let Some(ref else_map) = else_end_opt { eprintln!( "[Conservative PHI] else_map_end: {:?}", else_map.keys().collect::>() ); } } } } #[cfg(test)] mod tests { use super::*; #[test] fn test_conservative_merge_both_defined() { let mut pre_if = HashMap::new(); pre_if.insert("x".to_string(), ValueId::new(1)); let mut then_end = HashMap::new(); then_end.insert("x".to_string(), ValueId::new(2)); let mut else_end = HashMap::new(); else_end.insert("x".to_string(), ValueId::new(3)); let merge = ConservativeMerge::analyze(&pre_if, &then_end, &Some(else_end)); assert_eq!(merge.all_vars.len(), 1); assert!(merge.all_vars.contains("x")); } #[test] fn test_conservative_merge_union() { let pre_if = HashMap::new(); let mut then_end = HashMap::new(); then_end.insert("x".to_string(), ValueId::new(1)); let mut else_end = HashMap::new(); else_end.insert("y".to_string(), ValueId::new(2)); let merge = ConservativeMerge::analyze(&pre_if, &then_end, &Some(else_end)); assert_eq!(merge.all_vars.len(), 2); assert!(merge.all_vars.contains("x")); assert!(merge.all_vars.contains("y")); } }