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.
|
//! into the host MIR builder.
|
||||||
//!
|
//!
|
||||||
//! Phase 4 Extraction: Separated from merge_joinir_mir_blocks (lines 260-546)
|
//! 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::{BasicBlock, BasicBlockId, MirInstruction, MirModule, ValueId};
|
||||||
use crate::mir::builder::joinir_id_remapper::JoinIrIdRemapper;
|
use crate::mir::builder::joinir_id_remapper::JoinIrIdRemapper;
|
||||||
use crate::mir::join_ir::lowering::inline_boundary::JoinInlineBoundary;
|
use crate::mir::join_ir::lowering::inline_boundary::JoinInlineBoundary;
|
||||||
use super::loop_header_phi_builder::LoopHeaderPhiInfo;
|
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;
|
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
|
/// Phase 4: Merge ALL functions and rewrite instructions
|
||||||
///
|
///
|
||||||
/// Returns:
|
/// Returns:
|
||||||
@ -90,6 +24,12 @@ pub struct MergeResult {
|
|||||||
/// The `loop_header_phi_info` parameter is used to:
|
/// The `loop_header_phi_info` parameter is used to:
|
||||||
/// 1. Set latch_incoming when processing tail calls
|
/// 1. Set latch_incoming when processing tail calls
|
||||||
/// 2. Provide PHI dsts for exit value collection (instead of undefined parameters)
|
/// 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(
|
pub(super) fn merge_and_rewrite(
|
||||||
builder: &mut crate::mir::builder::MirBuilder,
|
builder: &mut crate::mir::builder::MirBuilder,
|
||||||
mir_module: &MirModule,
|
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;
|
mod exit_phi_builder;
|
||||||
pub mod exit_line;
|
pub mod exit_line;
|
||||||
pub mod loop_header_phi_builder;
|
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::{BasicBlockId, MirModule, ValueId};
|
||||||
use crate::mir::join_ir::lowering::inline_boundary::JoinInlineBoundary;
|
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