//! 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; pub mod exit_line; pub mod loop_header_phi_builder; use crate::mir::{BasicBlockId, 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 (mut used_values, value_to_func_name, function_params) = value_collector::collect_values(mir_module, &remapper, debug)?; // Phase 171-fix: Add condition_bindings' join_values to used_values for remapping if let Some(boundary) = boundary { for binding in &boundary.condition_bindings { if debug { eprintln!( "[cf_loop/joinir] Phase 171-fix: Adding condition binding '{}' JoinIR {:?} to used_values", binding.name, binding.join_value ); } used_values.insert(binding.join_value); } // Phase 172-3: Add exit_bindings' join_exit_values to used_values for remapping for binding in &boundary.exit_bindings { if debug { eprintln!( "[cf_loop/joinir] Phase 172-3: Adding exit binding '{}' JoinIR {:?} to used_values", binding.carrier_name, binding.join_exit_value ); } used_values.insert(binding.join_exit_value); } } // Phase 3: Remap ValueIds remap_values(builder, &used_values, &mut remapper, debug)?; // Phase 3.5: Build loop header PHIs (if loop pattern with loop_var_name) // // We need to know PHI dsts before instruction_rewriter runs, so that: // 1. Tail call handling can set latch_incoming // 2. Return handling can use PHI dsts for exit values let (entry_func_name, entry_func) = mir_module .functions .iter() .next() .ok_or("JoinIR module has no functions (Phase 3.5)")?; let entry_block_remapped = remapper .get_block(entry_func_name, entry_func.entry_block) .ok_or_else(|| format!("Entry block not found for {} (Phase 3.5)", entry_func_name))?; // Phase 33-16: Get host's current block as the entry edge (the block that jumps INTO the loop) let host_entry_block = builder.current_block.ok_or( "Phase 33-16: No current block when building header PHIs" )?; let mut loop_header_phi_info = if let Some(boundary) = boundary { if let Some(loop_var_name) = &boundary.loop_var_name { // Phase 33-16: Get loop variable's initial value from HOST (not JoinIR's ValueId(0)) // boundary.host_inputs[0] is the host ValueId that holds the initial loop var value let loop_var_init = boundary.host_inputs.first().copied().ok_or( "Phase 33-16: No host_inputs in boundary for loop_var_init" )?; if debug { eprintln!( "[cf_loop/joinir] Phase 3.5: Building header PHIs for loop_var='{}' at {:?}", loop_var_name, entry_block_remapped ); eprintln!( "[cf_loop/joinir] loop_var_init={:?} (from boundary.host_inputs[0])", loop_var_init ); eprintln!( "[cf_loop/joinir] host_entry_block={:?} (where initial value comes from)", host_entry_block ); } let phi_info = loop_header_phi_builder::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) loop_var_name, loop_var_init, &[], // No other carriers for Pattern 2 (loop var is the only carrier) boundary.expr_result.is_some(), // expr_result_is_loop_var debug, )?; // Phase 33-16: Override remapper so that JoinIR's loop var parameter (ValueId(0)) // references the PHI dst instead of a separate remapped value. // This ensures all uses of the loop variable in the loop body use the PHI result. if let Some(phi_dst) = phi_info.get_carrier_phi(loop_var_name) { // JoinIR uses ValueId(0) for loop var in loop_step's first parameter // After remapping, we want those uses to refer to the header PHI dst remapper.set_value(ValueId(0), phi_dst); if debug { eprintln!( "[cf_loop/joinir] Phase 33-16: Override remap ValueId(0) → {:?} (PHI dst)", phi_dst ); } } phi_info } else { loop_header_phi_builder::LoopHeaderPhiInfo::empty(entry_block_remapped) } } else { loop_header_phi_builder::LoopHeaderPhiInfo::empty(entry_block_remapped) }; // Phase 4: Merge blocks and rewrite instructions // Phase 33-16: Pass mutable loop_header_phi_info for latch_incoming tracking let merge_result = instruction_rewriter::merge_and_rewrite( builder, mir_module, &mut remapper, &value_to_func_name, &function_params, boundary, &mut loop_header_phi_info, debug, )?; // Phase 4.5: Finalize loop header PHIs (insert into header block) // // By now, instruction_rewriter has set latch_incoming for all carriers. // We can finalize the PHIs and insert them into the header block. if !loop_header_phi_info.carrier_phis.is_empty() { if debug { eprintln!( "[cf_loop/joinir] Phase 4.5: Finalizing {} header PHIs", loop_header_phi_info.carrier_phis.len() ); } loop_header_phi_builder::LoopHeaderPhiBuilder::finalize( builder, &loop_header_phi_info, debug, )?; } // Phase 5: Build exit PHI (expr result and carrier PHIs) // Phase 33-13: Now also builds carrier PHIs and returns their mapping let (exit_phi_result_id, carrier_phis) = exit_phi_builder::build_exit_phi( builder, merge_result.exit_block_id, &merge_result.exit_phi_inputs, &merge_result.carrier_inputs, debug, )?; // 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 // Phase 33-13: Pass carrier_phis for proper variable_map update if let Some(boundary) = boundary { exit_line::ExitLineOrchestrator::execute(builder, boundary, &carrier_phis, debug)?; } let exit_block_id = merge_result.exit_block_id; // Jump from current block to entry function's entry block // (Reuse entry_func_name and entry_block_remapped from Phase 3.5) let entry_block = entry_block_remapped; 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(()) }