Box theory refactoring: Extract exit_bindings construction logic from pattern lowerers into focused, reusable ExitMetaCollector Box. **Changes**: 1. Created meta_collector.rs (+102 lines): - ExitMetaCollector::collect() builds exit_bindings from ExitMeta - Pure function (no side effects) - Reusable by all pattern lowerers 2. Updated pattern2_with_break.rs (-20 lines): - Use ExitMetaCollector::collect() instead of inline filter_map - Removed manual binding construction loop - Cleaner caller code 3. Made exit_line module public: - Allows pattern lowerers to use ExitMetaCollector - Clear module visibility boundaries **Box Design**: - Single responsibility: Convert ExitMeta + variable_map → exit_bindings - Pure function: No side effects, testable independently - Reusable: Can be used by Pattern 3, Pattern 4, etc. **Testing**: - Build: ✅ Success (1m 04s) - Execution: ✅ RC: 0 (Pattern 2 verified) - Regression: ✅ No issues **Metrics**: - New lines: +102 (meta_collector.rs) - Removed lines: -20 (pattern2_with_break.rs) - Net change: +82 lines - Code clarity: Significantly improved 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
205 lines
7.1 KiB
Rust
205 lines
7.1 KiB
Rust
//! 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;
|
||
|
||
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<Option<ValueId>, 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 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)
|
||
// 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 {
|
||
exit_line::ExitLineOrchestrator::execute(builder, boundary, &remapper, 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<ValueId>,
|
||
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(())
|
||
}
|