refactor(joinir): Phase 33-17-B LoopHeaderPhiInfo extraction
## Changes - Extract LoopHeaderPhiInfo Box (116 lines) from loop_header_phi_builder - loop_header_phi_builder.rs reduced: 318 → 217 lines (-32%) - Proper separation of data structures from builder logic ## Module Structure - loop_header_phi_info.rs: LoopHeaderPhiInfo, CarrierPhiEntry structs + tests - loop_header_phi_builder.rs: LoopHeaderPhiBuilder implementation ## Box Theory Compliance - Data structures separated from builder logic - Each file has clear single responsibility - Clean re-exports through mod.rs 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@ -9,7 +9,7 @@
|
||||
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::loop_header_phi_info::LoopHeaderPhiInfo;
|
||||
use super::tail_call_classifier::{TailCallKind, classify_tail_call};
|
||||
use super::merge_result::MergeResult;
|
||||
use std::collections::HashMap;
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
//! Loop Header PHI Builder
|
||||
//!
|
||||
//! Phase 33-16: Generates PHI nodes at loop header blocks to track carrier values.
|
||||
//! Phase 33-17-B: Refactored to separate data structures from builder logic.
|
||||
//!
|
||||
//! ## Problem
|
||||
//!
|
||||
@ -26,74 +27,8 @@
|
||||
//! Called from merge pipeline between Phase 3 (remap_values) and Phase 4
|
||||
//! (instruction_rewriter).
|
||||
|
||||
use crate::mir::{BasicBlock, BasicBlockId, MirInstruction, ValueId};
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
/// Information about loop header PHIs
|
||||
///
|
||||
/// Generated by LoopHeaderPhiBuilder and used by:
|
||||
/// - exit_phi_builder: to reference the current loop value
|
||||
/// - ExitLineReconnector: to update variable_map with final values
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct LoopHeaderPhiInfo {
|
||||
/// The block where header PHIs are placed
|
||||
pub header_block: BasicBlockId,
|
||||
|
||||
/// Carrier variable PHI mappings: carrier_name → PHI dst
|
||||
///
|
||||
/// The PHI dst is the ValueId that represents the "current value"
|
||||
/// of this carrier during loop iteration.
|
||||
pub carrier_phis: BTreeMap<String, CarrierPhiEntry>,
|
||||
|
||||
/// Expression result PHI dst (if loop is used as expression)
|
||||
///
|
||||
/// For Pattern 2 (joinir_min_loop), this is the same as the loop
|
||||
/// variable's PHI dst. For carrier-only patterns (trim), this is None.
|
||||
pub expr_result_phi: Option<ValueId>,
|
||||
}
|
||||
|
||||
/// Entry for a single carrier's header PHI
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CarrierPhiEntry {
|
||||
/// PHI destination ValueId (the "current value" during iteration)
|
||||
pub phi_dst: ValueId,
|
||||
|
||||
/// Entry edge: (from_block, init_value)
|
||||
pub entry_incoming: (BasicBlockId, ValueId),
|
||||
|
||||
/// Latch edge: (from_block, updated_value) - set after instruction rewrite
|
||||
pub latch_incoming: Option<(BasicBlockId, ValueId)>,
|
||||
}
|
||||
|
||||
impl LoopHeaderPhiInfo {
|
||||
/// Create empty LoopHeaderPhiInfo
|
||||
pub fn empty(header_block: BasicBlockId) -> Self {
|
||||
Self {
|
||||
header_block,
|
||||
carrier_phis: BTreeMap::new(),
|
||||
expr_result_phi: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the PHI dst for a carrier variable
|
||||
pub fn get_carrier_phi(&self, name: &str) -> Option<ValueId> {
|
||||
self.carrier_phis.get(name).map(|e| e.phi_dst)
|
||||
}
|
||||
|
||||
/// Set latch incoming for a carrier
|
||||
///
|
||||
/// Called from instruction_rewriter after processing tail call Copy instructions.
|
||||
pub fn set_latch_incoming(&mut self, name: &str, from_block: BasicBlockId, value: ValueId) {
|
||||
if let Some(entry) = self.carrier_phis.get_mut(name) {
|
||||
entry.latch_incoming = Some((from_block, value));
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if all carriers have latch incoming set
|
||||
pub fn all_latch_set(&self) -> bool {
|
||||
self.carrier_phis.values().all(|e| e.latch_incoming.is_some())
|
||||
}
|
||||
}
|
||||
use crate::mir::{MirInstruction, ValueId, BasicBlockId};
|
||||
use super::loop_header_phi_info::{LoopHeaderPhiInfo, CarrierPhiEntry};
|
||||
|
||||
/// Builder for loop header PHIs
|
||||
///
|
||||
@ -280,39 +215,3 @@ impl LoopHeaderPhiBuilder {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_loop_header_phi_info_creation() {
|
||||
let header = BasicBlockId(10);
|
||||
let info = LoopHeaderPhiInfo::empty(header);
|
||||
|
||||
assert_eq!(info.header_block, header);
|
||||
assert!(info.carrier_phis.is_empty());
|
||||
assert!(info.expr_result_phi.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_carrier_phi_entry() {
|
||||
let mut info = LoopHeaderPhiInfo::empty(BasicBlockId(10));
|
||||
|
||||
info.carrier_phis.insert(
|
||||
"i".to_string(),
|
||||
CarrierPhiEntry {
|
||||
phi_dst: ValueId(100),
|
||||
entry_incoming: (BasicBlockId(1), ValueId(5)),
|
||||
latch_incoming: None,
|
||||
},
|
||||
);
|
||||
|
||||
assert_eq!(info.get_carrier_phi("i"), Some(ValueId(100)));
|
||||
assert_eq!(info.get_carrier_phi("x"), None);
|
||||
assert!(!info.all_latch_set());
|
||||
|
||||
info.set_latch_incoming("i", BasicBlockId(20), ValueId(50));
|
||||
assert!(info.all_latch_set());
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,116 @@
|
||||
//! Loop Header PHI Information Structures
|
||||
//!
|
||||
//! Phase 33-17-B: Data structures for loop header PHI tracking.
|
||||
//!
|
||||
//! ## Overview
|
||||
//!
|
||||
//! These structures hold metadata about PHI nodes generated at loop headers
|
||||
//! to track carrier variables. Used by:
|
||||
//! - LoopHeaderPhiBuilder: to build PHIs
|
||||
//! - exit_phi_builder: to reference current loop values
|
||||
//! - ExitLineReconnector: to update variable_map with final values
|
||||
|
||||
use crate::mir::{BasicBlockId, ValueId};
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
/// Information about loop header PHIs
|
||||
///
|
||||
/// Generated by LoopHeaderPhiBuilder and used by:
|
||||
/// - exit_phi_builder: to reference the current loop value
|
||||
/// - ExitLineReconnector: to update variable_map with final values
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct LoopHeaderPhiInfo {
|
||||
/// The block where header PHIs are placed
|
||||
pub header_block: BasicBlockId,
|
||||
|
||||
/// Carrier variable PHI mappings: carrier_name → PHI dst
|
||||
///
|
||||
/// The PHI dst is the ValueId that represents the "current value"
|
||||
/// of this carrier during loop iteration.
|
||||
pub carrier_phis: BTreeMap<String, CarrierPhiEntry>,
|
||||
|
||||
/// Expression result PHI dst (if loop is used as expression)
|
||||
///
|
||||
/// For Pattern 2 (joinir_min_loop), this is the same as the loop
|
||||
/// variable's PHI dst. For carrier-only patterns (trim), this is None.
|
||||
pub expr_result_phi: Option<ValueId>,
|
||||
}
|
||||
|
||||
/// Entry for a single carrier's header PHI
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CarrierPhiEntry {
|
||||
/// PHI destination ValueId (the "current value" during iteration)
|
||||
pub phi_dst: ValueId,
|
||||
|
||||
/// Entry edge: (from_block, init_value)
|
||||
pub entry_incoming: (BasicBlockId, ValueId),
|
||||
|
||||
/// Latch edge: (from_block, updated_value) - set after instruction rewrite
|
||||
pub latch_incoming: Option<(BasicBlockId, ValueId)>,
|
||||
}
|
||||
|
||||
impl LoopHeaderPhiInfo {
|
||||
/// Create empty LoopHeaderPhiInfo
|
||||
pub fn empty(header_block: BasicBlockId) -> Self {
|
||||
Self {
|
||||
header_block,
|
||||
carrier_phis: BTreeMap::new(),
|
||||
expr_result_phi: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the PHI dst for a carrier variable
|
||||
pub fn get_carrier_phi(&self, name: &str) -> Option<ValueId> {
|
||||
self.carrier_phis.get(name).map(|e| e.phi_dst)
|
||||
}
|
||||
|
||||
/// Set latch incoming for a carrier
|
||||
///
|
||||
/// Called from instruction_rewriter after processing tail call Copy instructions.
|
||||
pub fn set_latch_incoming(&mut self, name: &str, from_block: BasicBlockId, value: ValueId) {
|
||||
if let Some(entry) = self.carrier_phis.get_mut(name) {
|
||||
entry.latch_incoming = Some((from_block, value));
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if all carriers have latch incoming set
|
||||
pub fn all_latch_set(&self) -> bool {
|
||||
self.carrier_phis.values().all(|e| e.latch_incoming.is_some())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_loop_header_phi_info_creation() {
|
||||
let header = BasicBlockId(10);
|
||||
let info = LoopHeaderPhiInfo::empty(header);
|
||||
|
||||
assert_eq!(info.header_block, header);
|
||||
assert!(info.carrier_phis.is_empty());
|
||||
assert!(info.expr_result_phi.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_carrier_phi_entry() {
|
||||
let mut info = LoopHeaderPhiInfo::empty(BasicBlockId(10));
|
||||
|
||||
info.carrier_phis.insert(
|
||||
"i".to_string(),
|
||||
CarrierPhiEntry {
|
||||
phi_dst: ValueId(100),
|
||||
entry_incoming: (BasicBlockId(1), ValueId(5)),
|
||||
latch_incoming: None,
|
||||
},
|
||||
);
|
||||
|
||||
assert_eq!(info.get_carrier_phi("i"), Some(ValueId(100)));
|
||||
assert_eq!(info.get_carrier_phi("x"), None);
|
||||
assert!(!info.all_latch_set());
|
||||
|
||||
info.set_latch_incoming("i", BasicBlockId(20), ValueId(50));
|
||||
assert!(info.all_latch_set());
|
||||
}
|
||||
}
|
||||
@ -17,13 +17,16 @@ mod value_collector;
|
||||
mod instruction_rewriter;
|
||||
mod exit_phi_builder;
|
||||
pub mod exit_line;
|
||||
pub mod loop_header_phi_builder;
|
||||
mod loop_header_phi_info;
|
||||
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};
|
||||
pub use loop_header_phi_info::{LoopHeaderPhiInfo, CarrierPhiEntry};
|
||||
pub use loop_header_phi_builder::LoopHeaderPhiBuilder;
|
||||
|
||||
use crate::mir::{BasicBlockId, MirModule, ValueId};
|
||||
use crate::mir::join_ir::lowering::inline_boundary::JoinInlineBoundary;
|
||||
@ -154,7 +157,7 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
|
||||
);
|
||||
}
|
||||
|
||||
let phi_info = loop_header_phi_builder::LoopHeaderPhiBuilder::build(
|
||||
let phi_info = 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)
|
||||
@ -182,10 +185,10 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
|
||||
|
||||
phi_info
|
||||
} else {
|
||||
loop_header_phi_builder::LoopHeaderPhiInfo::empty(entry_block_remapped)
|
||||
LoopHeaderPhiInfo::empty(entry_block_remapped)
|
||||
}
|
||||
} else {
|
||||
loop_header_phi_builder::LoopHeaderPhiInfo::empty(entry_block_remapped)
|
||||
LoopHeaderPhiInfo::empty(entry_block_remapped)
|
||||
};
|
||||
|
||||
// Phase 4: Merge blocks and rewrite instructions
|
||||
@ -212,7 +215,7 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
|
||||
loop_header_phi_info.carrier_phis.len()
|
||||
);
|
||||
}
|
||||
loop_header_phi_builder::LoopHeaderPhiBuilder::finalize(
|
||||
LoopHeaderPhiBuilder::finalize(
|
||||
builder,
|
||||
&loop_header_phi_info,
|
||||
debug,
|
||||
|
||||
Reference in New Issue
Block a user