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 f5d8c81b..929ae7ac 100644 --- a/src/mir/builder/control_flow/joinir/merge/instruction_rewriter.rs +++ b/src/mir/builder/control_flow/joinir/merge/instruction_rewriter.rs @@ -4,82 +4,16 @@ //! into the host MIR builder. //! //! Phase 4 Extraction: Separated from merge_joinir_mir_blocks (lines 260-546) +//! Phase 33-17: Further modularization - extracted TailCallClassifier and MergeResult 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::tail_call_classifier::{TailCallKind, classify_tail_call}; +use super::merge_result::MergeResult; use std::collections::HashMap; -/// Phase 33-16: Tail Call Classification -/// -/// Classifies tail calls in JoinIR loops into three semantic categories: -/// -/// 1. **LoopEntry**: First entry into the loop (main → loop_step) -/// - Occurs from the entry function's entry block -/// - Should jump directly to target (not redirect to header) -/// - Reason: The entry block IS the header block; redirecting creates self-loop -/// -/// 2. **BackEdge**: Loop continuation (loop_step → loop_step) -/// - Occurs from loop body blocks (not entry function's entry block) -/// - MUST redirect to header block where PHI nodes are located -/// - Reason: PHI nodes need to merge values from all back edges -/// -/// 3. **ExitJump**: Loop termination (→ k_exit) -/// - Occurs when jumping to continuation functions -/// - Handled separately via Return conversion -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -enum TailCallKind { - /// First entry into loop - no redirection needed - LoopEntry, - /// Back edge in loop - redirect to header PHI - BackEdge, - /// Exit from loop - becomes Return conversion - ExitJump, -} - -/// Classifies a tail call based on context -/// -/// # Arguments -/// * `is_entry_func_entry_block` - True if this is the first function's first block (loop entry point) -/// * `has_loop_header_phis` - True if loop header PHI nodes exist -/// * `has_boundary` - True if JoinInlineBoundary exists (indicates loop context) -/// -/// # Returns -/// The classification of this tail call -fn classify_tail_call( - is_entry_func_entry_block: bool, - has_loop_header_phis: bool, - has_boundary: bool, -) -> TailCallKind { - // Entry function's entry block is the loop entry point - // It already IS at the header, so no redirection needed - if is_entry_func_entry_block { - return TailCallKind::LoopEntry; - } - - // If we have boundary and header PHIs, this is a back edge - // Must redirect to header for PHI merging - if has_boundary && has_loop_header_phis { - return TailCallKind::BackEdge; - } - - // Otherwise, treat as exit (will be handled by Return conversion) - TailCallKind::ExitJump -} - -/// Phase 33-13: Return type for merge_and_rewrite -/// -/// Contains all information needed for exit PHI construction. -pub struct MergeResult { - /// The ID of the exit block where all Returns jump to - pub exit_block_id: BasicBlockId, - /// Vec of (from_block, return_value) for expr result PHI generation - pub exit_phi_inputs: Vec<(BasicBlockId, ValueId)>, - /// Map of carrier_name → Vec of (from_block, exit_value) for carrier PHI generation - pub carrier_inputs: std::collections::BTreeMap>, -} - /// Phase 4: Merge ALL functions and rewrite instructions /// /// Returns: @@ -90,6 +24,12 @@ pub struct MergeResult { /// The `loop_header_phi_info` parameter is used to: /// 1. Set latch_incoming when processing tail calls /// 2. Provide PHI dsts for exit value collection (instead of undefined parameters) +/// +/// # Phase 33-17 +/// +/// Uses extracted modules: +/// - tail_call_classifier: TailCallKind classification +/// - merge_result: MergeResult data structure pub(super) fn merge_and_rewrite( builder: &mut crate::mir::builder::MirBuilder, mir_module: &MirModule, diff --git a/src/mir/builder/control_flow/joinir/merge/merge_result.rs b/src/mir/builder/control_flow/joinir/merge/merge_result.rs new file mode 100644 index 00000000..0be00d0c --- /dev/null +++ b/src/mir/builder/control_flow/joinir/merge/merge_result.rs @@ -0,0 +1,44 @@ +//! Phase 33-17: Merge Result Data Structure +//! +//! Contains all information needed for exit PHI construction. +//! +//! Extracted from instruction_rewriter.rs (lines 71-81) for single responsibility. + +use crate::mir::{BasicBlockId, ValueId}; +use std::collections::BTreeMap; + +/// Phase 33-13: Return type for merge_and_rewrite +/// +/// Contains all information needed for exit PHI construction. +pub struct MergeResult { + /// The ID of the exit block where all Returns jump to + pub exit_block_id: BasicBlockId, + /// Vec of (from_block, return_value) for expr result PHI generation + pub exit_phi_inputs: Vec<(BasicBlockId, ValueId)>, + /// Map of carrier_name → Vec of (from_block, exit_value) for carrier PHI generation + pub carrier_inputs: BTreeMap>, +} + +impl MergeResult { + /// Create a new MergeResult with empty inputs + pub fn new(exit_block_id: BasicBlockId) -> Self { + Self { + exit_block_id, + exit_phi_inputs: Vec::new(), + carrier_inputs: BTreeMap::new(), + } + } + + /// Add an exit PHI input + pub fn add_exit_phi_input(&mut self, from_block: BasicBlockId, value: ValueId) { + self.exit_phi_inputs.push((from_block, value)); + } + + /// Add a carrier input + pub fn add_carrier_input(&mut self, carrier_name: String, from_block: BasicBlockId, value: ValueId) { + self.carrier_inputs + .entry(carrier_name) + .or_insert_with(Vec::new) + .push((from_block, value)); + } +} diff --git a/src/mir/builder/control_flow/joinir/merge/mod.rs b/src/mir/builder/control_flow/joinir/merge/mod.rs index 501f52e9..c8470e2a 100644 --- a/src/mir/builder/control_flow/joinir/merge/mod.rs +++ b/src/mir/builder/control_flow/joinir/merge/mod.rs @@ -18,6 +18,12 @@ mod instruction_rewriter; mod exit_phi_builder; pub mod exit_line; pub 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}; use crate::mir::{BasicBlockId, MirModule, ValueId}; use crate::mir::join_ir::lowering::inline_boundary::JoinInlineBoundary; diff --git a/src/mir/builder/control_flow/joinir/merge/tail_call_classifier.rs b/src/mir/builder/control_flow/joinir/merge/tail_call_classifier.rs new file mode 100644 index 00000000..602e7211 --- /dev/null +++ b/src/mir/builder/control_flow/joinir/merge/tail_call_classifier.rs @@ -0,0 +1,107 @@ +//! Phase 33-17: Tail Call Classification +//! +//! Classifies tail calls in JoinIR loops into three semantic categories. +//! +//! Extracted from instruction_rewriter.rs (lines 14-69) for single responsibility. + +/// Phase 33-16: Tail Call Classification +/// +/// Classifies tail calls in JoinIR loops into three semantic categories: +/// +/// 1. **LoopEntry**: First entry into the loop (main → loop_step) +/// - Occurs from the entry function's entry block +/// - Should jump directly to target (not redirect to header) +/// - Reason: The entry block IS the header block; redirecting creates self-loop +/// +/// 2. **BackEdge**: Loop continuation (loop_step → loop_step) +/// - Occurs from loop body blocks (not entry function's entry block) +/// - MUST redirect to header block where PHI nodes are located +/// - Reason: PHI nodes need to merge values from all back edges +/// +/// 3. **ExitJump**: Loop termination (→ k_exit) +/// - Occurs when jumping to continuation functions +/// - Handled separately via Return conversion +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum TailCallKind { + /// First entry into loop - no redirection needed + LoopEntry, + /// Back edge in loop - redirect to header PHI + BackEdge, + /// Exit from loop - becomes Return conversion + ExitJump, +} + +/// Classifies a tail call based on context +/// +/// # Arguments +/// * `is_entry_func_entry_block` - True if this is the first function's first block (loop entry point) +/// * `has_loop_header_phis` - True if loop header PHI nodes exist +/// * `has_boundary` - True if JoinInlineBoundary exists (indicates loop context) +/// +/// # Returns +/// The classification of this tail call +pub fn classify_tail_call( + is_entry_func_entry_block: bool, + has_loop_header_phis: bool, + has_boundary: bool, +) -> TailCallKind { + // Entry function's entry block is the loop entry point + // It already IS at the header, so no redirection needed + if is_entry_func_entry_block { + return TailCallKind::LoopEntry; + } + + // If we have boundary and header PHIs, this is a back edge + // Must redirect to header for PHI merging + if has_boundary && has_loop_header_phis { + return TailCallKind::BackEdge; + } + + // Otherwise, treat as exit (will be handled by Return conversion) + TailCallKind::ExitJump +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_classify_loop_entry() { + let result = classify_tail_call( + true, // is_entry_func_entry_block + true, // has_loop_header_phis + true, // has_boundary + ); + assert_eq!(result, TailCallKind::LoopEntry); + } + + #[test] + fn test_classify_back_edge() { + let result = classify_tail_call( + false, // is_entry_func_entry_block (not entry block) + true, // has_loop_header_phis + true, // has_boundary + ); + assert_eq!(result, TailCallKind::BackEdge); + } + + #[test] + fn test_classify_exit_jump() { + let result = classify_tail_call( + false, // is_entry_func_entry_block + false, // has_loop_header_phis (no header PHIs) + true, // has_boundary + ); + assert_eq!(result, TailCallKind::ExitJump); + } + + #[test] + fn test_classify_no_boundary() { + let result = classify_tail_call( + false, // is_entry_func_entry_block + true, // has_loop_header_phis + false, // has_boundary (no boundary → exit) + ); + assert_eq!(result, TailCallKind::ExitJump); + } +}