diff --git a/src/mir/builder/control_flow/joinir/merge/exit_line/mod.rs b/src/mir/builder/control_flow/joinir/merge/exit_line/mod.rs new file mode 100644 index 00000000..26a2e30d --- /dev/null +++ b/src/mir/builder/control_flow/joinir/merge/exit_line/mod.rs @@ -0,0 +1,58 @@ +//! Phase 33-10-Refactor: Exit Line Module +//! +//! Modularizes exit line handling (Phase 6 from merge/mod.rs) into focused Boxes. +//! +//! **Architecture**: +//! ```text +//! ExitLineOrchestrator (facade) +//! ├── ExitMetaCollector (collects exit_bindings) +//! └── ExitLineReconnector (updates variable_map) +//! ``` + +pub mod reconnector; + +pub use reconnector::ExitLineReconnector; + +/// Phase 33-10-Refactor-P2: ExitLineOrchestrator facade +/// +/// Coordinates the entire exit line reconnection process for Phase 6. +/// Acts as a single-entry-point for merge/mod.rs. +pub struct ExitLineOrchestrator; + +impl ExitLineOrchestrator { + /// Orchestrate the complete exit line reconnection + /// + /// # Inputs + /// - builder: MirBuilder with variable_map to update + /// - boundary: JoinInlineBoundary with exit_bindings + /// - remapper: JoinIrIdRemapper for ValueId lookup + /// - debug: Debug logging enabled + /// + /// # Returns + /// - Result<(), String> + /// + /// # Process + /// 1. Validate exit_bindings (empty case) + /// 2. Delegate to ExitLineReconnector + pub fn execute( + builder: &mut crate::mir::builder::MirBuilder, + boundary: &crate::mir::join_ir::lowering::inline_boundary::JoinInlineBoundary, + remapper: &crate::mir::builder::joinir_id_remapper::JoinIrIdRemapper, + debug: bool, + ) -> Result<(), String> { + if debug { + eprintln!( + "[cf_loop/joinir/exit_line] ExitLineOrchestrator: Starting Phase 6 reconnection" + ); + } + + // Delegate to ExitLineReconnector + ExitLineReconnector::reconnect(builder, boundary, remapper, debug)?; + + if debug { + eprintln!("[cf_loop/joinir/exit_line] ExitLineOrchestrator: Phase 6 complete"); + } + + Ok(()) + } +} diff --git a/src/mir/builder/control_flow/joinir/merge/exit_line/reconnector.rs b/src/mir/builder/control_flow/joinir/merge/exit_line/reconnector.rs new file mode 100644 index 00000000..4beda740 --- /dev/null +++ b/src/mir/builder/control_flow/joinir/merge/exit_line/reconnector.rs @@ -0,0 +1,130 @@ +//! Phase 33-10-Refactor-P0: ExitLineReconnector Box +//! +//! Modularizes the exit line reconnection logic (Phase 6 from merge/mod.rs) +//! into a focused, testable Box. +//! +//! **Responsibility**: Update host variable_map with remapped exit values from JoinIR + +use crate::mir::builder::joinir_id_remapper::JoinIrIdRemapper; +use crate::mir::builder::MirBuilder; +use crate::mir::join_ir::lowering::inline_boundary::JoinInlineBoundary; +use crate::mir::ValueId; + +/// ExitLineReconnector: A Box that manages exit value reconnection +/// +/// # Responsibility (Single: exit_bindings → variable_map update) +/// +/// Takes JoinInlineBoundary exit_bindings and remapper, then updates the host's +/// variable_map with the final exit values from JoinIR k_exit parameters. +/// +/// # Box Contract +/// +/// **Input**: +/// - JoinInlineBoundary with exit_bindings (Carrier → join_exit_value mappings) +/// - JoinIrIdRemapper (JoinIR local → host function ValueId remappings) +/// +/// **Effect**: +/// - Updates builder.variable_map entries for each carrier with remapped exit values +/// +/// **Output**: +/// - Result<(), String> (side effect on builder) +pub struct ExitLineReconnector; + +impl ExitLineReconnector { + /// Reconnect exit values to host variable_map + /// + /// # Phase 197-B: Multi-carrier support + /// + /// Previously, a single exit_phi_result was applied to all carriers, causing + /// all carriers to get the same value (e.g., both sum and count = 5). + /// + /// Now, we use each binding's join_exit_value and the remapper to find + /// the actual exit value for each carrier. + /// + /// # Algorithm + /// + /// For each exit_binding: + /// 1. Look up the remapped ValueId for binding.join_exit_value + /// 2. Update variable_map[binding.carrier_name] with remapped value + /// 3. Log each update (if debug enabled) + pub fn reconnect( + builder: &mut MirBuilder, + boundary: &JoinInlineBoundary, + remapper: &JoinIrIdRemapper, + debug: bool, + ) -> Result<(), String> { + // Early return for empty exit_bindings + if boundary.exit_bindings.is_empty() { + if debug { + eprintln!("[cf_loop/joinir/exit_line] ExitLineReconnector: No exit bindings, skipping reconnect"); + } + return Ok(()); + } + + if debug { + eprintln!( + "[cf_loop/joinir/exit_line] ExitLineReconnector: Reconnecting {} exit bindings", + boundary.exit_bindings.len() + ); + } + + // Process each exit binding + for binding in &boundary.exit_bindings { + // Look up the remapped exit value + let remapped_exit = remapper.get_value(binding.join_exit_value); + + if debug { + eprintln!( + "[cf_loop/joinir/exit_line] ExitLineReconnector: Carrier '{}' join_exit={:?} → remapped={:?}", + binding.carrier_name, binding.join_exit_value, remapped_exit + ); + } + + // Update variable_map if remapping found + if let Some(remapped_value) = remapped_exit { + if let Some(var_vid) = builder.variable_map.get_mut(&binding.carrier_name) { + if debug { + eprintln!( + "[cf_loop/joinir/exit_line] ExitLineReconnector: Updated variable_map['{}'] {:?} → {:?}", + binding.carrier_name, var_vid, remapped_value + ); + } + *var_vid = remapped_value; + } else if debug { + eprintln!( + "[cf_loop/joinir/exit_line] ExitLineReconnector WARNING: Carrier '{}' not found in variable_map", + binding.carrier_name + ); + } + } else if debug { + eprintln!( + "[cf_loop/joinir/exit_line] ExitLineReconnector WARNING: No remapped value for join_exit={:?}", + binding.join_exit_value + ); + } + } + + // Backward compatibility warning for deprecated host_outputs + #[allow(deprecated)] + if !boundary.host_outputs.is_empty() && debug { + eprintln!( + "[cf_loop/joinir/exit_line] WARNING: Using deprecated host_outputs. Migrate to exit_bindings." + ); + } + + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_empty_exit_bindings() { + // This test would require full MirBuilder setup + // Placeholder for future detailed testing + // When exit_bindings is empty, reconnect should return Ok immediately + assert!(true); + } +} diff --git a/src/mir/builder/control_flow/joinir/merge/mod.rs b/src/mir/builder/control_flow/joinir/merge/mod.rs index 85b3f4b1..67d9bdad 100644 --- a/src/mir/builder/control_flow/joinir/merge/mod.rs +++ b/src/mir/builder/control_flow/joinir/merge/mod.rs @@ -16,6 +16,7 @@ mod block_allocator; mod value_collector; mod instruction_rewriter; mod exit_phi_builder; +mod exit_line; use crate::mir::{MirModule, ValueId}; use crate::mir::join_ir::lowering::inline_boundary::JoinInlineBoundary; @@ -125,8 +126,9 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks( // Phase 6: Reconnect boundary (if specified) // Phase 197-B: Pass remapper to enable per-carrier exit value lookup + // Phase 33-10-Refactor-P3: Delegate to ExitLineOrchestrator if let Some(boundary) = boundary { - reconnect_boundary(builder, boundary, &remapper, exit_phi_result_id, debug)?; + exit_line::ExitLineOrchestrator::execute(builder, boundary, &remapper, debug)?; } // Jump from current block to entry function's entry block @@ -200,92 +202,3 @@ fn remap_values( Ok(()) } - -/// Phase 6: Reconnect boundary to update host variable_map -/// -/// Phase 197-B: Multi-carrier support -/// Previously, a single exit_phi_result was applied to all carriers, causing -/// all carriers to get the same value (e.g., both sum and count = 5). -/// -/// Now, we use each binding's join_exit_value and the remapper to find -/// the actual exit value for each carrier. -fn reconnect_boundary( - builder: &mut crate::mir::builder::MirBuilder, - boundary: &JoinInlineBoundary, - remapper: &crate::mir::builder::joinir_id_remapper::JoinIrIdRemapper, - _exit_phi_result: Option, - debug: bool, -) -> Result<(), String> { - // Phase 197-B: Multi-carrier exit binding using join_exit_value directly - // - // Problem: Previously used single exit_phi_result for all carriers. - // Solution: Each carrier's exit value is in k_exit parameters. - // We use remapper to find the remapped ValueId for each carrier. - // - // For Pattern 4 with 2 carriers (sum, count): - // - k_exit(sum_exit, count_exit) receives values via Jump args - // - sum_exit = ValueId(N) in JoinIR → remapped to ValueId(M) in host - // - count_exit = ValueId(N+1) in JoinIR → remapped to ValueId(M+1) in host - // - // Each carrier's variable_map entry is updated with its specific remapped value. - - if boundary.exit_bindings.is_empty() { - if debug { - eprintln!("[cf_loop/joinir] Phase 197-B: No exit bindings, skipping reconnect"); - } - return Ok(()); - } - - if debug { - eprintln!( - "[cf_loop/joinir] Phase 197-B: Reconnecting {} exit bindings", - boundary.exit_bindings.len() - ); - } - - // For each binding, look up the remapped exit value and update variable_map - for binding in &boundary.exit_bindings { - // Phase 197-B: Use remapper to find the remapped exit value - let remapped_exit = remapper.get_value(binding.join_exit_value); - - if debug { - eprintln!( - "[cf_loop/joinir] Phase 197-B: Carrier '{}' join_exit={:?} → remapped={:?}", - binding.carrier_name, binding.join_exit_value, remapped_exit - ); - } - - if let Some(remapped_value) = remapped_exit { - // Update variable_map with the remapped exit value - if let Some(var_vid) = builder.variable_map.get_mut(&binding.carrier_name) { - if debug { - eprintln!( - "[cf_loop/joinir] Phase 197-B: Updated variable_map['{}'] {:?} → {:?}", - binding.carrier_name, var_vid, remapped_value - ); - } - *var_vid = remapped_value; - } else if debug { - eprintln!( - "[cf_loop/joinir] Phase 197-B WARNING: Carrier '{}' not found in variable_map", - binding.carrier_name - ); - } - } else if debug { - eprintln!( - "[cf_loop/joinir] Phase 197-B WARNING: No remapped value for join_exit={:?}", - binding.join_exit_value - ); - } - } - - // Phase 190: Backward compatibility - also check deprecated host_outputs - #[allow(deprecated)] - if !boundary.host_outputs.is_empty() && debug { - eprintln!( - "[cf_loop/joinir] WARNING: Using deprecated host_outputs. Migrate to exit_bindings." - ); - } - - Ok(()) -}