diff --git a/src/mir/builder/control_flow/joinir/merge/instruction_rewriter.rs b/src/mir/builder/control_flow/joinir/merge/instruction_rewriter.rs index 929ae7ac..8d15c55f 100644 --- a/src/mir/builder/control_flow/joinir/merge/instruction_rewriter.rs +++ b/src/mir/builder/control_flow/joinir/merge/instruction_rewriter.rs @@ -9,7 +9,7 @@ use crate::mir::{BasicBlock, BasicBlockId, MirInstruction, MirModule, ValueId}; use crate::mir::builder::joinir_id_remapper::JoinIrIdRemapper; use crate::mir::join_ir::lowering::inline_boundary::JoinInlineBoundary; -use super::loop_header_phi_builder::LoopHeaderPhiInfo; +use super::loop_header_phi_info::LoopHeaderPhiInfo; use super::tail_call_classifier::{TailCallKind, classify_tail_call}; use super::merge_result::MergeResult; use std::collections::HashMap; diff --git a/src/mir/builder/control_flow/joinir/merge/loop_header_phi_builder.rs b/src/mir/builder/control_flow/joinir/merge/loop_header_phi_builder.rs index 9581ff86..4ceb2dd0 100644 --- a/src/mir/builder/control_flow/joinir/merge/loop_header_phi_builder.rs +++ b/src/mir/builder/control_flow/joinir/merge/loop_header_phi_builder.rs @@ -1,6 +1,7 @@ //! Loop Header PHI Builder //! //! Phase 33-16: Generates PHI nodes at loop header blocks to track carrier values. +//! Phase 33-17-B: Refactored to separate data structures from builder logic. //! //! ## Problem //! @@ -26,74 +27,8 @@ //! Called from merge pipeline between Phase 3 (remap_values) and Phase 4 //! (instruction_rewriter). -use crate::mir::{BasicBlock, BasicBlockId, MirInstruction, ValueId}; -use std::collections::BTreeMap; - -/// Information about loop header PHIs -/// -/// Generated by LoopHeaderPhiBuilder and used by: -/// - exit_phi_builder: to reference the current loop value -/// - ExitLineReconnector: to update variable_map with final values -#[derive(Debug, Clone)] -pub struct LoopHeaderPhiInfo { - /// The block where header PHIs are placed - pub header_block: BasicBlockId, - - /// Carrier variable PHI mappings: carrier_name → PHI dst - /// - /// The PHI dst is the ValueId that represents the "current value" - /// of this carrier during loop iteration. - pub carrier_phis: BTreeMap, - - /// Expression result PHI dst (if loop is used as expression) - /// - /// For Pattern 2 (joinir_min_loop), this is the same as the loop - /// variable's PHI dst. For carrier-only patterns (trim), this is None. - pub expr_result_phi: Option, -} - -/// Entry for a single carrier's header PHI -#[derive(Debug, Clone)] -pub struct CarrierPhiEntry { - /// PHI destination ValueId (the "current value" during iteration) - pub phi_dst: ValueId, - - /// Entry edge: (from_block, init_value) - pub entry_incoming: (BasicBlockId, ValueId), - - /// Latch edge: (from_block, updated_value) - set after instruction rewrite - pub latch_incoming: Option<(BasicBlockId, ValueId)>, -} - -impl LoopHeaderPhiInfo { - /// Create empty LoopHeaderPhiInfo - pub fn empty(header_block: BasicBlockId) -> Self { - Self { - header_block, - carrier_phis: BTreeMap::new(), - expr_result_phi: None, - } - } - - /// Get the PHI dst for a carrier variable - pub fn get_carrier_phi(&self, name: &str) -> Option { - self.carrier_phis.get(name).map(|e| e.phi_dst) - } - - /// Set latch incoming for a carrier - /// - /// Called from instruction_rewriter after processing tail call Copy instructions. - pub fn set_latch_incoming(&mut self, name: &str, from_block: BasicBlockId, value: ValueId) { - if let Some(entry) = self.carrier_phis.get_mut(name) { - entry.latch_incoming = Some((from_block, value)); - } - } - - /// Check if all carriers have latch incoming set - pub fn all_latch_set(&self) -> bool { - self.carrier_phis.values().all(|e| e.latch_incoming.is_some()) - } -} +use crate::mir::{MirInstruction, ValueId, BasicBlockId}; +use super::loop_header_phi_info::{LoopHeaderPhiInfo, CarrierPhiEntry}; /// Builder for loop header PHIs /// @@ -280,39 +215,3 @@ impl LoopHeaderPhiBuilder { Ok(()) } } - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_loop_header_phi_info_creation() { - let header = BasicBlockId(10); - let info = LoopHeaderPhiInfo::empty(header); - - assert_eq!(info.header_block, header); - assert!(info.carrier_phis.is_empty()); - assert!(info.expr_result_phi.is_none()); - } - - #[test] - fn test_carrier_phi_entry() { - let mut info = LoopHeaderPhiInfo::empty(BasicBlockId(10)); - - info.carrier_phis.insert( - "i".to_string(), - CarrierPhiEntry { - phi_dst: ValueId(100), - entry_incoming: (BasicBlockId(1), ValueId(5)), - latch_incoming: None, - }, - ); - - assert_eq!(info.get_carrier_phi("i"), Some(ValueId(100))); - assert_eq!(info.get_carrier_phi("x"), None); - assert!(!info.all_latch_set()); - - info.set_latch_incoming("i", BasicBlockId(20), ValueId(50)); - assert!(info.all_latch_set()); - } -} diff --git a/src/mir/builder/control_flow/joinir/merge/loop_header_phi_info.rs b/src/mir/builder/control_flow/joinir/merge/loop_header_phi_info.rs new file mode 100644 index 00000000..88fe5ee0 --- /dev/null +++ b/src/mir/builder/control_flow/joinir/merge/loop_header_phi_info.rs @@ -0,0 +1,116 @@ +//! Loop Header PHI Information Structures +//! +//! Phase 33-17-B: Data structures for loop header PHI tracking. +//! +//! ## Overview +//! +//! These structures hold metadata about PHI nodes generated at loop headers +//! to track carrier variables. Used by: +//! - LoopHeaderPhiBuilder: to build PHIs +//! - exit_phi_builder: to reference current loop values +//! - ExitLineReconnector: to update variable_map with final values + +use crate::mir::{BasicBlockId, ValueId}; +use std::collections::BTreeMap; + +/// Information about loop header PHIs +/// +/// Generated by LoopHeaderPhiBuilder and used by: +/// - exit_phi_builder: to reference the current loop value +/// - ExitLineReconnector: to update variable_map with final values +#[derive(Debug, Clone)] +pub struct LoopHeaderPhiInfo { + /// The block where header PHIs are placed + pub header_block: BasicBlockId, + + /// Carrier variable PHI mappings: carrier_name → PHI dst + /// + /// The PHI dst is the ValueId that represents the "current value" + /// of this carrier during loop iteration. + pub carrier_phis: BTreeMap, + + /// Expression result PHI dst (if loop is used as expression) + /// + /// For Pattern 2 (joinir_min_loop), this is the same as the loop + /// variable's PHI dst. For carrier-only patterns (trim), this is None. + pub expr_result_phi: Option, +} + +/// Entry for a single carrier's header PHI +#[derive(Debug, Clone)] +pub struct CarrierPhiEntry { + /// PHI destination ValueId (the "current value" during iteration) + pub phi_dst: ValueId, + + /// Entry edge: (from_block, init_value) + pub entry_incoming: (BasicBlockId, ValueId), + + /// Latch edge: (from_block, updated_value) - set after instruction rewrite + pub latch_incoming: Option<(BasicBlockId, ValueId)>, +} + +impl LoopHeaderPhiInfo { + /// Create empty LoopHeaderPhiInfo + pub fn empty(header_block: BasicBlockId) -> Self { + Self { + header_block, + carrier_phis: BTreeMap::new(), + expr_result_phi: None, + } + } + + /// Get the PHI dst for a carrier variable + pub fn get_carrier_phi(&self, name: &str) -> Option { + self.carrier_phis.get(name).map(|e| e.phi_dst) + } + + /// Set latch incoming for a carrier + /// + /// Called from instruction_rewriter after processing tail call Copy instructions. + pub fn set_latch_incoming(&mut self, name: &str, from_block: BasicBlockId, value: ValueId) { + if let Some(entry) = self.carrier_phis.get_mut(name) { + entry.latch_incoming = Some((from_block, value)); + } + } + + /// Check if all carriers have latch incoming set + pub fn all_latch_set(&self) -> bool { + self.carrier_phis.values().all(|e| e.latch_incoming.is_some()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_loop_header_phi_info_creation() { + let header = BasicBlockId(10); + let info = LoopHeaderPhiInfo::empty(header); + + assert_eq!(info.header_block, header); + assert!(info.carrier_phis.is_empty()); + assert!(info.expr_result_phi.is_none()); + } + + #[test] + fn test_carrier_phi_entry() { + let mut info = LoopHeaderPhiInfo::empty(BasicBlockId(10)); + + info.carrier_phis.insert( + "i".to_string(), + CarrierPhiEntry { + phi_dst: ValueId(100), + entry_incoming: (BasicBlockId(1), ValueId(5)), + latch_incoming: None, + }, + ); + + assert_eq!(info.get_carrier_phi("i"), Some(ValueId(100))); + assert_eq!(info.get_carrier_phi("x"), None); + assert!(!info.all_latch_set()); + + info.set_latch_incoming("i", BasicBlockId(20), ValueId(50)); + assert!(info.all_latch_set()); + } +} diff --git a/src/mir/builder/control_flow/joinir/merge/mod.rs b/src/mir/builder/control_flow/joinir/merge/mod.rs index c8470e2a..715f9aff 100644 --- a/src/mir/builder/control_flow/joinir/merge/mod.rs +++ b/src/mir/builder/control_flow/joinir/merge/mod.rs @@ -17,13 +17,16 @@ mod value_collector; mod instruction_rewriter; mod exit_phi_builder; pub mod exit_line; -pub mod loop_header_phi_builder; +mod loop_header_phi_info; +mod loop_header_phi_builder; mod tail_call_classifier; mod merge_result; // Phase 33-17: Re-export for use by other modules pub use merge_result::MergeResult; pub use tail_call_classifier::{TailCallKind, classify_tail_call}; +pub use loop_header_phi_info::{LoopHeaderPhiInfo, CarrierPhiEntry}; +pub use loop_header_phi_builder::LoopHeaderPhiBuilder; use crate::mir::{BasicBlockId, MirModule, ValueId}; use crate::mir::join_ir::lowering::inline_boundary::JoinInlineBoundary; @@ -154,7 +157,7 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks( ); } - let phi_info = loop_header_phi_builder::LoopHeaderPhiBuilder::build( + let phi_info = LoopHeaderPhiBuilder::build( builder, entry_block_remapped, // header_block (JoinIR's entry block = loop header) host_entry_block, // entry_block (host's block that jumps to loop header) @@ -182,10 +185,10 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks( phi_info } else { - loop_header_phi_builder::LoopHeaderPhiInfo::empty(entry_block_remapped) + LoopHeaderPhiInfo::empty(entry_block_remapped) } } else { - loop_header_phi_builder::LoopHeaderPhiInfo::empty(entry_block_remapped) + LoopHeaderPhiInfo::empty(entry_block_remapped) }; // Phase 4: Merge blocks and rewrite instructions @@ -212,7 +215,7 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks( loop_header_phi_info.carrier_phis.len() ); } - loop_header_phi_builder::LoopHeaderPhiBuilder::finalize( + LoopHeaderPhiBuilder::finalize( builder, &loop_header_phi_info, debug,