diff --git a/src/mir/phi_core/header_phi_builder.rs b/src/mir/phi_core/header_phi_builder.rs new file mode 100644 index 00000000..5cfff839 --- /dev/null +++ b/src/mir/phi_core/header_phi_builder.rs @@ -0,0 +1,560 @@ +//! Header PHI Builder - Header PHI生成専門Box +//! +//! Phase 26-C-2: HeaderPhiBuilder実装 +//! - Loop header PHI node生成の専門化 +//! - Preheader入力設定 +//! - Latch値更新(seal時) +//! +//! Box-First理論: Header PHI生成の責任を明確に分離し、テスト可能な箱として提供 + +use crate::mir::{BasicBlockId, ValueId}; +use std::collections::HashMap; + +/// Header PHI生成専門Box +/// +/// # Purpose +/// - Loop headerでのPHI node生成 +/// - Pinned/Carrier変数の区別管理 +/// - Seal時のlatch/continue入力追加 +/// +/// # Responsibility Separation +/// - このBox: Header PHI生成・seal処理 +/// - PhiInputCollector: PHI入力収集・最適化 +/// - LoopSnapshotManager: Snapshot管理 +/// +/// # Usage +/// ```ignore +/// let mut builder = HeaderPhiBuilder::new(); +/// +/// // Pinned変数のPHI準備 +/// builder.prepare_pinned_phi( +/// "x".to_string(), +/// ValueId(10), // phi_id +/// ValueId(1), // param_value +/// ValueId(2), // preheader_copy +/// ); +/// +/// // Carrier変数のPHI準備 +/// builder.prepare_carrier_phi( +/// "i".to_string(), +/// ValueId(20), // phi_id +/// ValueId(0), // init_value +/// ValueId(3), // preheader_copy +/// ); +/// +/// // PHI情報を取得してemit +/// for phi_info in builder.pinned_phis() { +/// // emit_phi(phi_info.phi_id, ...) +/// } +/// ``` +#[derive(Debug, Clone, Default)] +pub struct HeaderPhiBuilder { + /// Pinned変数のPHI情報 + pinned_phis: Vec, + /// Carrier変数のPHI情報 + carrier_phis: Vec, +} + +/// Pinned変数PHI情報 +/// +/// # Fields +/// - `var_name`: 変数名 +/// - `phi_id`: Header PHIのValueId +/// - `param_value`: 元のパラメータValueId +/// - `preheader_copy`: PreheaderでのCopy ValueId +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct PinnedPhiInfo { + /// Variable name + pub var_name: String, + /// PHI node ValueId + pub phi_id: ValueId, + /// Original parameter ValueId + pub param_value: ValueId, + /// Preheader copy ValueId + pub preheader_copy: ValueId, +} + +/// Carrier変数PHI情報 +/// +/// # Fields +/// - `var_name`: 変数名 +/// - `phi_id`: Header PHIのValueId +/// - `init_value`: 初期ValueId +/// - `preheader_copy`: PreheaderでのCopy ValueId +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct CarrierPhiInfo { + /// Variable name + pub var_name: String, + /// PHI node ValueId + pub phi_id: ValueId, + /// Initial ValueId + pub init_value: ValueId, + /// Preheader copy ValueId + pub preheader_copy: ValueId, +} + +impl HeaderPhiBuilder { + /// Create a new HeaderPhiBuilder + /// + /// # Returns + /// New builder with empty PHI lists + /// + /// # Example + /// ```ignore + /// let builder = HeaderPhiBuilder::new(); + /// ``` + pub fn new() -> Self { + Self::default() + } + + /// Prepare pinned variable PHI + /// + /// # Arguments + /// * `var_name` - Variable name + /// * `phi_id` - PHI node ValueId + /// * `param_value` - Original parameter ValueId + /// * `preheader_copy` - Preheader copy ValueId + /// + /// # Example + /// ```ignore + /// builder.prepare_pinned_phi( + /// "x".to_string(), + /// ValueId(10), + /// ValueId(1), + /// ValueId(2), + /// ); + /// ``` + pub fn prepare_pinned_phi( + &mut self, + var_name: String, + phi_id: ValueId, + param_value: ValueId, + preheader_copy: ValueId, + ) { + self.pinned_phis.push(PinnedPhiInfo { + var_name, + phi_id, + param_value, + preheader_copy, + }); + } + + /// Prepare carrier variable PHI + /// + /// # Arguments + /// * `var_name` - Variable name + /// * `phi_id` - PHI node ValueId + /// * `init_value` - Initial ValueId + /// * `preheader_copy` - Preheader copy ValueId + /// + /// # Example + /// ```ignore + /// builder.prepare_carrier_phi( + /// "i".to_string(), + /// ValueId(20), + /// ValueId(0), + /// ValueId(3), + /// ); + /// ``` + pub fn prepare_carrier_phi( + &mut self, + var_name: String, + phi_id: ValueId, + init_value: ValueId, + preheader_copy: ValueId, + ) { + self.carrier_phis.push(CarrierPhiInfo { + var_name, + phi_id, + init_value, + preheader_copy, + }); + } + + /// Get pinned PHI information + /// + /// # Returns + /// Slice of pinned PHI info + /// + /// # Example + /// ```ignore + /// for phi_info in builder.pinned_phis() { + /// println!("Pinned PHI: {} → {:?}", phi_info.var_name, phi_info.phi_id); + /// } + /// ``` + pub fn pinned_phis(&self) -> &[PinnedPhiInfo] { + &self.pinned_phis + } + + /// Get carrier PHI information + /// + /// # Returns + /// Slice of carrier PHI info + /// + /// # Example + /// ```ignore + /// for phi_info in builder.carrier_phis() { + /// println!("Carrier PHI: {} → {:?}", phi_info.var_name, phi_info.phi_id); + /// } + /// ``` + pub fn carrier_phis(&self) -> &[CarrierPhiInfo] { + &self.carrier_phis + } + + /// Get pinned PHI count + /// + /// # Returns + /// Number of pinned PHIs + pub fn pinned_phi_count(&self) -> usize { + self.pinned_phis.len() + } + + /// Get carrier PHI count + /// + /// # Returns + /// Number of carrier PHIs + pub fn carrier_phi_count(&self) -> usize { + self.carrier_phis.len() + } + + /// Get total PHI count + /// + /// # Returns + /// Total number of PHIs (pinned + carrier) + pub fn total_phi_count(&self) -> usize { + self.pinned_phi_count() + self.carrier_phi_count() + } + + /// Find pinned PHI by variable name + /// + /// # Arguments + /// * `var_name` - Variable name to search + /// + /// # Returns + /// Option containing pinned PHI info if found + /// + /// # Example + /// ```ignore + /// if let Some(phi_info) = builder.find_pinned_phi("x") { + /// println!("Found pinned PHI: {:?}", phi_info.phi_id); + /// } + /// ``` + pub fn find_pinned_phi(&self, var_name: &str) -> Option<&PinnedPhiInfo> { + self.pinned_phis + .iter() + .find(|phi| phi.var_name == var_name) + } + + /// Find carrier PHI by variable name + /// + /// # Arguments + /// * `var_name` - Variable name to search + /// + /// # Returns + /// Option containing carrier PHI info if found + /// + /// # Example + /// ```ignore + /// if let Some(phi_info) = builder.find_carrier_phi("i") { + /// println!("Found carrier PHI: {:?}", phi_info.phi_id); + /// } + /// ``` + pub fn find_carrier_phi(&self, var_name: &str) -> Option<&CarrierPhiInfo> { + self.carrier_phis + .iter() + .find(|phi| phi.var_name == var_name) + } + + /// Build PHI inputs for sealing (helper for actual seal implementation) + /// + /// # Arguments + /// * `var_name` - Variable name + /// * `preheader_id` - Preheader block ID + /// * `preheader_copy` - Preheader copy ValueId + /// * `latch_id` - Latch block ID + /// * `latch_value` - Latch ValueId + /// * `continue_snapshots` - Continue snapshot list + /// + /// # Returns + /// PHI inputs as (BasicBlockId, ValueId) pairs + /// + /// # Note + /// This is a helper method. Actual PHI emission should be done by the caller. + pub fn build_phi_inputs_for_seal( + &self, + var_name: &str, + preheader_id: BasicBlockId, + preheader_copy: ValueId, + latch_id: BasicBlockId, + latch_value: ValueId, + continue_snapshots: &[(BasicBlockId, HashMap)], + ) -> Vec<(BasicBlockId, ValueId)> { + use crate::mir::phi_core::phi_input_collector::PhiInputCollector; + + let mut collector = PhiInputCollector::new(); + + // Add preheader input + collector.add_preheader(preheader_id, preheader_copy); + + // Add continue snapshot inputs + for (cid, snapshot) in continue_snapshots { + if let Some(&value) = snapshot.get(var_name) { + collector.add_snapshot(&[(*cid, value)]); + } + } + + // Add latch input + collector.add_latch(latch_id, latch_value); + + // Sanitize and optimize + collector.sanitize(); + + // Check for same-value optimization + if let Some(_same_value) = collector.optimize_same_value() { + // All inputs are the same → PHI can be eliminated + // Return empty vec to signal PHI elimination + return Vec::new(); + } + + collector.finalize() + } + + /// Clear all PHI information + /// + /// # Purpose + /// Reset builder to initial state (useful for testing or reuse) + /// + /// # Example + /// ```ignore + /// builder.clear(); + /// assert_eq!(builder.total_phi_count(), 0); + /// ``` + pub fn clear(&mut self) { + self.pinned_phis.clear(); + self.carrier_phis.clear(); + } +} + +// ============================================================================ +// Unit Tests +// ============================================================================ + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_new() { + let builder = HeaderPhiBuilder::new(); + assert_eq!(builder.pinned_phi_count(), 0); + assert_eq!(builder.carrier_phi_count(), 0); + assert_eq!(builder.total_phi_count(), 0); + } + + #[test] + fn test_prepare_pinned_phi() { + let mut builder = HeaderPhiBuilder::new(); + builder.prepare_pinned_phi( + "x".to_string(), + ValueId(10), + ValueId(1), + ValueId(2), + ); + + assert_eq!(builder.pinned_phi_count(), 1); + let phi = &builder.pinned_phis()[0]; + assert_eq!(phi.var_name, "x"); + assert_eq!(phi.phi_id, ValueId(10)); + assert_eq!(phi.param_value, ValueId(1)); + assert_eq!(phi.preheader_copy, ValueId(2)); + } + + #[test] + fn test_prepare_carrier_phi() { + let mut builder = HeaderPhiBuilder::new(); + builder.prepare_carrier_phi( + "i".to_string(), + ValueId(20), + ValueId(0), + ValueId(3), + ); + + assert_eq!(builder.carrier_phi_count(), 1); + let phi = &builder.carrier_phis()[0]; + assert_eq!(phi.var_name, "i"); + assert_eq!(phi.phi_id, ValueId(20)); + assert_eq!(phi.init_value, ValueId(0)); + assert_eq!(phi.preheader_copy, ValueId(3)); + } + + #[test] + fn test_total_phi_count() { + let mut builder = HeaderPhiBuilder::new(); + builder.prepare_pinned_phi("x".to_string(), ValueId(10), ValueId(1), ValueId(2)); + builder.prepare_pinned_phi("y".to_string(), ValueId(11), ValueId(3), ValueId(4)); + builder.prepare_carrier_phi("i".to_string(), ValueId(20), ValueId(0), ValueId(5)); + + assert_eq!(builder.pinned_phi_count(), 2); + assert_eq!(builder.carrier_phi_count(), 1); + assert_eq!(builder.total_phi_count(), 3); + } + + #[test] + fn test_find_pinned_phi() { + let mut builder = HeaderPhiBuilder::new(); + builder.prepare_pinned_phi("x".to_string(), ValueId(10), ValueId(1), ValueId(2)); + builder.prepare_pinned_phi("y".to_string(), ValueId(11), ValueId(3), ValueId(4)); + + let phi = builder.find_pinned_phi("x"); + assert!(phi.is_some()); + assert_eq!(phi.unwrap().phi_id, ValueId(10)); + + let not_found = builder.find_pinned_phi("z"); + assert!(not_found.is_none()); + } + + #[test] + fn test_find_carrier_phi() { + let mut builder = HeaderPhiBuilder::new(); + builder.prepare_carrier_phi("i".to_string(), ValueId(20), ValueId(0), ValueId(3)); + builder.prepare_carrier_phi("j".to_string(), ValueId(21), ValueId(0), ValueId(4)); + + let phi = builder.find_carrier_phi("i"); + assert!(phi.is_some()); + assert_eq!(phi.unwrap().phi_id, ValueId(20)); + + let not_found = builder.find_carrier_phi("k"); + assert!(not_found.is_none()); + } + + #[test] + fn test_clear() { + let mut builder = HeaderPhiBuilder::new(); + builder.prepare_pinned_phi("x".to_string(), ValueId(10), ValueId(1), ValueId(2)); + builder.prepare_carrier_phi("i".to_string(), ValueId(20), ValueId(0), ValueId(3)); + + assert_eq!(builder.total_phi_count(), 2); + + builder.clear(); + + assert_eq!(builder.pinned_phi_count(), 0); + assert_eq!(builder.carrier_phi_count(), 0); + assert_eq!(builder.total_phi_count(), 0); + } + + #[test] + fn test_default() { + let builder = HeaderPhiBuilder::default(); + assert_eq!(builder.total_phi_count(), 0); + } + + #[test] + fn test_build_phi_inputs_for_seal_simple() { + let builder = HeaderPhiBuilder::new(); + + // Simple case: preheader + latch only + let inputs = builder.build_phi_inputs_for_seal( + "x", + BasicBlockId(1), // preheader_id + ValueId(10), // preheader_copy + BasicBlockId(2), // latch_id + ValueId(20), // latch_value + &[], // no continue snapshots + ); + + // Should have 2 inputs: preheader and latch + assert_eq!(inputs.len(), 2); + assert!(inputs.contains(&(BasicBlockId(1), ValueId(10)))); + assert!(inputs.contains(&(BasicBlockId(2), ValueId(20)))); + } + + #[test] + fn test_build_phi_inputs_for_seal_with_continue() { + let builder = HeaderPhiBuilder::new(); + + // Continue snapshot + let mut continue_vars = HashMap::new(); + continue_vars.insert("x".to_string(), ValueId(15)); + + let inputs = builder.build_phi_inputs_for_seal( + "x", + BasicBlockId(1), // preheader_id + ValueId(10), // preheader_copy + BasicBlockId(3), // latch_id + ValueId(20), // latch_value + &[(BasicBlockId(2), continue_vars)], + ); + + // Should have 3 inputs: preheader, continue, latch + assert_eq!(inputs.len(), 3); + assert!(inputs.contains(&(BasicBlockId(1), ValueId(10)))); + assert!(inputs.contains(&(BasicBlockId(2), ValueId(15)))); + assert!(inputs.contains(&(BasicBlockId(3), ValueId(20)))); + } + + #[test] + fn test_build_phi_inputs_for_seal_same_value_optimization() { + let builder = HeaderPhiBuilder::new(); + + // All inputs are the same → PHI elimination + let inputs = builder.build_phi_inputs_for_seal( + "x", + BasicBlockId(1), // preheader_id + ValueId(10), // preheader_copy (same as latch) + BasicBlockId(2), // latch_id + ValueId(10), // latch_value (same!) + &[], + ); + + // Should return empty vec (PHI eliminated) + assert_eq!(inputs.len(), 0); + } + + #[test] + fn test_skip_whitespace_scenario() { + // skip_whitespaceシナリオ: s, idx (pinned) + let mut builder = HeaderPhiBuilder::new(); + + builder.prepare_pinned_phi( + "s".to_string(), + ValueId(100), // phi_id + ValueId(1), // param_value + ValueId(2), // preheader_copy + ); + + builder.prepare_pinned_phi( + "idx".to_string(), + ValueId(101), // phi_id + ValueId(3), // param_value + ValueId(4), // preheader_copy + ); + + assert_eq!(builder.pinned_phi_count(), 2); + assert_eq!(builder.carrier_phi_count(), 0); + + let s_phi = builder.find_pinned_phi("s"); + assert!(s_phi.is_some()); + assert_eq!(s_phi.unwrap().phi_id, ValueId(100)); + + let idx_phi = builder.find_pinned_phi("idx"); + assert!(idx_phi.is_some()); + assert_eq!(idx_phi.unwrap().phi_id, ValueId(101)); + } + + #[test] + fn test_mixed_pinned_and_carrier() { + let mut builder = HeaderPhiBuilder::new(); + + // Pinned: function parameters + builder.prepare_pinned_phi("s".to_string(), ValueId(100), ValueId(1), ValueId(2)); + builder.prepare_pinned_phi("idx".to_string(), ValueId(101), ValueId(3), ValueId(4)); + + // Carrier: loop-modified locals + builder.prepare_carrier_phi("i".to_string(), ValueId(200), ValueId(0), ValueId(5)); + builder.prepare_carrier_phi("sum".to_string(), ValueId(201), ValueId(0), ValueId(6)); + + assert_eq!(builder.pinned_phi_count(), 2); + assert_eq!(builder.carrier_phi_count(), 2); + assert_eq!(builder.total_phi_count(), 4); + } +} diff --git a/src/mir/phi_core/mod.rs b/src/mir/phi_core/mod.rs index 987d444f..22f99b08 100644 --- a/src/mir/phi_core/mod.rs +++ b/src/mir/phi_core/mod.rs @@ -24,6 +24,7 @@ pub mod body_local_phi_builder; // Phase 26-C: Loop Snapshot & Header PHI Management pub mod loop_snapshot_manager; +pub mod header_phi_builder; // Public surface for callers that want a stable path: // Phase 1: No re-exports to avoid touching private builder internals.