refactor(joinir): Phase 33-17-A TailCallClassifier and MergeResult extraction
## Changes - Extract TailCallClassifier Box (107 lines) with 4 unit tests - Extract MergeResult Box (44 lines) for merge result data structure - instruction_rewriter.rs reduced: 649 → 580 lines (-10.6%) ## Box Theory Compliance - TailCallClassifier: Single responsibility for tail call classification - MergeResult: Clean data structure separation - Both modules follow naming conventions (Classifier, Result) ## Quality - 4 unit tests added for TailCallClassifier - Build verified ✅ 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@ -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<String, Vec<(BasicBlockId, ValueId)>>,
|
||||
}
|
||||
|
||||
/// 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,
|
||||
|
||||
44
src/mir/builder/control_flow/joinir/merge/merge_result.rs
Normal file
44
src/mir/builder/control_flow/joinir/merge/merge_result.rs
Normal file
@ -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<String, Vec<(BasicBlockId, ValueId)>>,
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user