//! JoinIR MIR Block Merging Coordinator //! //! This module coordinates the merging of JoinIR-generated MIR functions //! into the host MIR builder. The process is broken into 6 phases: //! //! 1. Block ID allocation (block_allocator.rs) //! 2. Value collection (value_collector.rs) //! 3. ValueId remapping (uses JoinIrIdRemapper) //! 4. Instruction rewriting (instruction_rewriter.rs) //! 5. Exit PHI construction (exit_phi_builder.rs) //! 6. Boundary reconnection (inline in this file) //! //! Phase 4 Refactoring: Breaking down 714-line merge_joinir_mir_blocks() into focused modules mod block_allocator; mod value_collector; mod instruction_rewriter; mod exit_phi_builder; use crate::mir::{MirModule, ValueId}; use crate::mir::join_ir::lowering::inline_boundary::JoinInlineBoundary; /// Phase 49-3.2: Merge JoinIR-generated MIR blocks into current_function /// /// # Phase 189: Multi-Function MIR Merge /// /// This merges JoinIR-generated blocks by: /// 1. Remapping all block IDs across ALL functions to avoid conflicts /// 2. Remapping all value IDs across ALL functions to avoid conflicts /// 3. Adding all blocks from all functions to current_function /// 4. Jumping from current_block to the entry block /// 5. Converting Return → Jump to exit block for all functions /// /// **Multi-Function Support** (Phase 189): /// - Pattern 1 (Simple While) generates 3 functions: entry + loop_step + k_exit /// - All functions are flattened into current_function with global ID remapping /// - Single exit block receives all Return instructions from all functions /// /// # Phase 188-Impl-3: JoinInlineBoundary Support /// /// When `boundary` is provided, injects Copy instructions at the entry block /// to connect host ValueIds to JoinIR local ValueIds: /// /// ```text /// entry_block: /// // Injected by boundary /// ValueId(100) = Copy ValueId(4) // join_input → host_input /// // Original JoinIR instructions follow... /// ``` /// /// This enables clean separation: JoinIR uses local IDs (0,1,2...), /// host uses its own IDs, and Copy instructions bridge the gap. /// /// # Returns /// /// Returns `Ok(Some(exit_phi_id))` if the merged JoinIR functions have return values /// that were collected into an exit block PHI. さらに、`boundary` に /// host_outputs が指定されている場合は、exit PHI の結果をホスト側の /// SSA スロットへ再接続する(variable_map 内の ValueId を更新する)。 pub(in crate::mir::builder) fn merge_joinir_mir_blocks( builder: &mut crate::mir::builder::MirBuilder, mir_module: &MirModule, boundary: Option<&JoinInlineBoundary>, debug: bool, ) -> Result, String> { if debug { eprintln!( "[cf_loop/joinir] merge_joinir_mir_blocks called with {} functions", mir_module.functions.len() ); } // Phase 1: Allocate block IDs for all functions let mut remapper = block_allocator::allocate_blocks(builder, mir_module, debug)?; // Phase 2: Collect values from all functions let (used_values, value_to_func_name, function_params) = value_collector::collect_values(mir_module, &remapper, debug)?; // Phase 3: Remap ValueIds remap_values(builder, &used_values, &mut remapper, debug)?; // Phase 4: Merge blocks and rewrite instructions let (exit_block_id, exit_phi_inputs) = instruction_rewriter::merge_and_rewrite( builder, mir_module, &mut remapper, &value_to_func_name, &function_params, boundary, debug, )?; // Phase 5: Build exit PHI let exit_phi_result_id = exit_phi_builder::build_exit_phi( builder, exit_block_id, &exit_phi_inputs, debug, )?; // Phase 6: Reconnect boundary (if specified) if let Some(boundary) = boundary { reconnect_boundary(builder, boundary, exit_phi_result_id, debug)?; } // Jump from current block to entry function's entry block let (entry_func_name, entry_func) = mir_module .functions .iter() .next() .ok_or("JoinIR module has no functions")?; let entry_block = remapper .get_block(entry_func_name, entry_func.entry_block) .ok_or_else(|| format!("Entry block not found for {}", entry_func_name))?; if debug { eprintln!("[cf_loop/joinir] Entry function name: {}", entry_func_name); eprintln!( "[cf_loop/joinir] Entry function's entry_block (JoinIR local): {:?}", entry_func.entry_block ); eprintln!("[cf_loop/joinir] Entry block (remapped): {:?}", entry_block); eprintln!( "[cf_loop/joinir] Current block before emit_jump: {:?}", builder.current_block ); eprintln!("[cf_loop/joinir] Jumping to entry block: {:?}", entry_block); } crate::mir::builder::emission::branch::emit_jump(builder, entry_block)?; if debug { eprintln!( "[cf_loop/joinir] After emit_jump, current_block: {:?}", builder.current_block ); } // Switch to exit block for subsequent code builder.start_new_block(exit_block_id)?; if debug { eprintln!( "[cf_loop/joinir] Phase 189: Merge complete: {} functions merged, continuing from {:?}", mir_module.functions.len(), exit_block_id ); } Ok(exit_phi_result_id) } /// Phase 3: Allocate new ValueIds for all collected values fn remap_values( builder: &mut crate::mir::builder::MirBuilder, used_values: &std::collections::BTreeSet, remapper: &mut crate::mir::builder::joinir_id_remapper::JoinIrIdRemapper, debug: bool, ) -> Result<(), String> { if debug { eprintln!("[cf_loop/joinir] Phase 3: Remapping {} ValueIds", used_values.len()); } for old_value in used_values { let new_value = builder.next_value_id(); remapper.set_value(*old_value, new_value); if debug { eprintln!( "[cf_loop/joinir] Value remap: {:?} → {:?}", old_value, new_value ); } } Ok(()) } /// Phase 6: Reconnect boundary to update host variable_map fn reconnect_boundary( builder: &mut crate::mir::builder::MirBuilder, boundary: &JoinInlineBoundary, exit_phi_result: Option, debug: bool, ) -> Result<(), String> { // Phase 190: Use explicit LoopExitBinding to reconnect exit PHI to variable_map // Each binding explicitly names the carrier variable and maps exit PHI to it. if let Some(phi_result) = exit_phi_result { // Phase 190: Use exit_bindings for explicit carrier naming // This eliminates ambiguity about which variable is being updated for binding in &boundary.exit_bindings { // Find the variable in variable_map that matches the binding's host_slot for (var_name, vid) in builder.variable_map.iter_mut() { if *vid == binding.host_slot { *vid = phi_result; if debug { eprintln!( "[cf_loop/joinir] Phase 190: Reconnected exit PHI {:?} to variable_map['{}'] (carrier: {})", phi_result, var_name, binding.carrier_name ); } // Validate carrier name matches if var_name != &binding.carrier_name && debug { eprintln!( "[cf_loop/joinir] WARNING: Carrier name mismatch: expected '{}', found '{}'", binding.carrier_name, var_name ); } } } } // 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(()) }