refactor(joinir): Phase 221-R ExprResultResolver Box extraction
Extracted 64 lines of expr_result handling from merge/mod.rs into dedicated ExprResultResolver Box following Phase 33 modularization. - New module: expr_result_resolver.rs (185 lines, 4 unit tests) - merge/mod.rs: -37 lines (net reduction) - Single responsibility: expr_result resolution only - Improved testability and maintainability Phase 221-R completes the box-first refactoring of Phase 221's expr_result routing implementation, aligning with Phase 33 modularization patterns (ExitMetaCollector, ExitLineReconnector). Test results: ✅ phase212_if_sum_min.hako: RC=2 ✅ loop_if_phi.hako: sum=9 (legacy mode) ✅ loop_min_while.hako: correct output
This commit is contained in:
@ -0,0 +1,185 @@
|
||||
//! ExprResultResolver: expr_result ValueId を解決する Box
|
||||
//!
|
||||
//! Phase 221-R: ExprResult 処理を箱化
|
||||
//!
|
||||
//! # 責任
|
||||
//! JoinIR-local expr_result ValueId をホスト側 ValueId に変換する
|
||||
//!
|
||||
//! # Algorithm
|
||||
//! 1. expr_result が存在するか確認
|
||||
//! 2. exit_bindings で expr_result が carrier に対応しているか検査
|
||||
//! 3. 対応する場合 → carrier_phis[carrier] (PHI dst) を返す
|
||||
//! 4. 非対応の場合 → remapper.get_value(expr_result) を返す
|
||||
//! 5. expr_result が None → None を返す
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use crate::mir::ValueId;
|
||||
use crate::mir::join_ir::lowering::inline_boundary::LoopExitBinding;
|
||||
use crate::mir::builder::joinir_id_remapper::JoinIrIdRemapper;
|
||||
|
||||
pub struct ExprResultResolver;
|
||||
|
||||
impl ExprResultResolver {
|
||||
/// expr_result を解決する
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `expr_result` - JoinIR-local expr_result ValueId (boundary.expr_result)
|
||||
/// * `exit_bindings` - Exit bindings from boundary
|
||||
/// * `carrier_phis` - Carrier name → PHI dst ValueId mapping
|
||||
/// * `remapper` - JoinIR ValueId → Host ValueId remapper
|
||||
/// * `debug` - Debug logging enabled
|
||||
///
|
||||
/// # Returns
|
||||
/// * `Ok(Some(ValueId))` - Resolved host ValueId
|
||||
/// * `Ok(None)` - No expr_result to resolve
|
||||
/// * `Err(String)` - Resolution error
|
||||
///
|
||||
/// # Phase 221 Logic
|
||||
/// This preserves the exact logic from merge/mod.rs L613-676:
|
||||
/// - If expr_result matches a carrier exit binding → use carrier PHI dst
|
||||
/// - Otherwise → use remapper to translate JoinIR ValueId to host ValueId
|
||||
pub fn resolve(
|
||||
expr_result: Option<ValueId>,
|
||||
exit_bindings: &[LoopExitBinding],
|
||||
carrier_phis: &BTreeMap<String, ValueId>,
|
||||
remapper: &JoinIrIdRemapper,
|
||||
debug: bool,
|
||||
) -> Result<Option<ValueId>, String> {
|
||||
// Step 1: Check if expr_result exists
|
||||
let expr_result_id = match expr_result {
|
||||
Some(id) => id,
|
||||
None => {
|
||||
if debug {
|
||||
eprintln!("[cf_loop/joinir] Phase 221: expr_result is None, returning None");
|
||||
}
|
||||
return Ok(None);
|
||||
}
|
||||
};
|
||||
|
||||
if debug {
|
||||
eprintln!(
|
||||
"[cf_loop/joinir] Phase 221: Resolving expr_result {:?}, exit_bindings={:?}",
|
||||
expr_result_id,
|
||||
exit_bindings.iter().map(|b| (b.carrier_name.as_str(), b.join_exit_value)).collect::<Vec<_>>()
|
||||
);
|
||||
}
|
||||
|
||||
// Step 2: Check if expr_result corresponds to a carrier exit binding
|
||||
// If so, use the carrier PHI dst instead of remapped value
|
||||
for binding in exit_bindings {
|
||||
if binding.join_exit_value == expr_result_id {
|
||||
// expr_result is a carrier! Use the carrier PHI dst
|
||||
if let Some(&carrier_phi_dst) = carrier_phis.get(&binding.carrier_name) {
|
||||
if debug {
|
||||
eprintln!(
|
||||
"[cf_loop/joinir] Phase 221: expr_result {:?} is carrier '{}', returning PHI dst {:?}",
|
||||
expr_result_id, binding.carrier_name, carrier_phi_dst
|
||||
);
|
||||
}
|
||||
return Ok(Some(carrier_phi_dst));
|
||||
} else {
|
||||
return Err(format!(
|
||||
"[cf_loop/joinir] Phase 221: Carrier '{}' not found in carrier_phis",
|
||||
binding.carrier_name
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Step 3: expr_result is NOT a carrier - use remapped value
|
||||
if let Some(remapped_expr) = remapper.get_value(expr_result_id) {
|
||||
if debug {
|
||||
eprintln!(
|
||||
"[cf_loop/joinir] Phase 221: Returning non-carrier expr_result: JoinIR {:?} → Host {:?}",
|
||||
expr_result_id, remapped_expr
|
||||
);
|
||||
}
|
||||
Ok(Some(remapped_expr))
|
||||
} else {
|
||||
// expr_result was not remapped - this is an error
|
||||
Err(format!(
|
||||
"[cf_loop/joinir] Phase 221: expr_result {:?} was not found in remapper",
|
||||
expr_result_id
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_expr_result_none() {
|
||||
let carrier_phis = BTreeMap::new();
|
||||
let remapper = JoinIrIdRemapper::new();
|
||||
|
||||
let result = ExprResultResolver::resolve(
|
||||
None,
|
||||
&[],
|
||||
&carrier_phis,
|
||||
&remapper,
|
||||
false,
|
||||
);
|
||||
|
||||
assert_eq!(result.unwrap(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_expr_result_carrier() {
|
||||
let mut carrier_phis = BTreeMap::new();
|
||||
carrier_phis.insert("sum".to_string(), ValueId(100));
|
||||
|
||||
let binding = LoopExitBinding {
|
||||
carrier_name: "sum".to_string(),
|
||||
join_exit_value: ValueId(18),
|
||||
host_slot: ValueId(5),
|
||||
};
|
||||
|
||||
let remapper = JoinIrIdRemapper::new();
|
||||
|
||||
let result = ExprResultResolver::resolve(
|
||||
Some(ValueId(18)),
|
||||
&[binding],
|
||||
&carrier_phis,
|
||||
&remapper,
|
||||
false,
|
||||
);
|
||||
|
||||
assert_eq!(result.unwrap(), Some(ValueId(100)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_expr_result_non_carrier() {
|
||||
let carrier_phis = BTreeMap::new();
|
||||
let mut remapper = JoinIrIdRemapper::new();
|
||||
remapper.set_value(ValueId(42), ValueId(200));
|
||||
|
||||
let result = ExprResultResolver::resolve(
|
||||
Some(ValueId(42)),
|
||||
&[],
|
||||
&carrier_phis,
|
||||
&remapper,
|
||||
false,
|
||||
);
|
||||
|
||||
assert_eq!(result.unwrap(), Some(ValueId(200)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_expr_result_not_found() {
|
||||
let carrier_phis = BTreeMap::new();
|
||||
let remapper = JoinIrIdRemapper::new();
|
||||
|
||||
let result = ExprResultResolver::resolve(
|
||||
Some(ValueId(999)),
|
||||
&[],
|
||||
&carrier_phis,
|
||||
&remapper,
|
||||
false,
|
||||
);
|
||||
|
||||
assert!(result.is_err());
|
||||
assert!(result.unwrap_err().contains("not found in remapper"));
|
||||
}
|
||||
}
|
||||
@ -21,6 +21,7 @@ mod loop_header_phi_info;
|
||||
mod loop_header_phi_builder;
|
||||
mod tail_call_classifier;
|
||||
mod merge_result;
|
||||
mod expr_result_resolver;
|
||||
|
||||
// Phase 33-17: Re-export for use by other modules
|
||||
pub use loop_header_phi_info::LoopHeaderPhiInfo;
|
||||
@ -610,69 +611,31 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
|
||||
builder.reserved_value_ids.clear();
|
||||
}
|
||||
|
||||
// Phase 221: Return expr_result if present (for expr-position loops)
|
||||
// The expr_result from boundary contains the JoinIR-local ValueId that should
|
||||
// be returned. We need to map it to the HOST ValueId space.
|
||||
//
|
||||
// IMPORTANT: If expr_result corresponds to a carrier variable, we should return
|
||||
// the carrier PHI dst (from loop header), NOT the remapped JoinIR value.
|
||||
// This is because carriers use header PHIs which dominate the exit block.
|
||||
if let Some(boundary) = boundary {
|
||||
eprintln!(
|
||||
"[cf_loop/joinir] Phase 221: Boundary present, expr_result={:?}, exit_bindings={:?}",
|
||||
boundary.expr_result,
|
||||
boundary.exit_bindings.iter().map(|b| (b.carrier_name.as_str(), b.join_exit_value)).collect::<Vec<_>>()
|
||||
);
|
||||
if let Some(expr_result_id) = boundary.expr_result {
|
||||
// Check if expr_result corresponds to a carrier exit binding
|
||||
// If so, use the carrier PHI dst instead of remapped value
|
||||
for binding in &boundary.exit_bindings {
|
||||
if binding.join_exit_value == expr_result_id {
|
||||
// expr_result is a carrier! Use the carrier PHI dst
|
||||
if let Some(&carrier_phi_dst) = carrier_phis.get(&binding.carrier_name) {
|
||||
eprintln!(
|
||||
"[cf_loop/joinir] Phase 221: expr_result {:?} is carrier '{}', returning PHI dst {:?}",
|
||||
expr_result_id, binding.carrier_name, carrier_phi_dst
|
||||
);
|
||||
return Ok(Some(carrier_phi_dst));
|
||||
} else {
|
||||
return Err(format!(
|
||||
"[cf_loop/joinir] Phase 221: Carrier '{}' not found in carrier_phis",
|
||||
binding.carrier_name
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
// Phase 221-R: Use ExprResultResolver Box
|
||||
let expr_result_value = expr_result_resolver::ExprResultResolver::resolve(
|
||||
boundary.and_then(|b| b.expr_result),
|
||||
boundary.map(|b| b.exit_bindings.as_slice()).unwrap_or(&[]),
|
||||
&carrier_phis,
|
||||
&remapper,
|
||||
debug,
|
||||
)?;
|
||||
|
||||
// expr_result is NOT a carrier - use remapped value
|
||||
if let Some(remapped_expr) = remapper.get_value(expr_result_id) {
|
||||
eprintln!(
|
||||
"[cf_loop/joinir] Phase 221: Returning non-carrier expr_result: JoinIR {:?} → Host {:?}",
|
||||
expr_result_id, remapped_expr
|
||||
);
|
||||
return Ok(Some(remapped_expr));
|
||||
} else {
|
||||
// expr_result was not remapped - this is an error
|
||||
return Err(format!(
|
||||
"[cf_loop/joinir] Phase 221: expr_result {:?} was not found in remapper",
|
||||
expr_result_id
|
||||
));
|
||||
// Return expr_result if present, otherwise fall back to exit_phi_result_id
|
||||
if let Some(resolved) = expr_result_value {
|
||||
if debug {
|
||||
eprintln!("[cf_loop/joinir] Phase 221-R: Returning expr_result_value {:?}", resolved);
|
||||
}
|
||||
Ok(Some(resolved))
|
||||
} else {
|
||||
eprintln!("[cf_loop/joinir] Phase 221: expr_result is None, using fallback");
|
||||
}
|
||||
} else {
|
||||
eprintln!("[cf_loop/joinir] Phase 221: No boundary, using fallback");
|
||||
}
|
||||
|
||||
// Fallback: return exit_phi_result_id (for legacy patterns or carrier-only loops)
|
||||
if debug && exit_phi_result_id.is_some() {
|
||||
eprintln!(
|
||||
"[cf_loop/joinir] Phase 221: Returning exit_phi_result_id (fallback): {:?}",
|
||||
"[cf_loop/joinir] Phase 221-R: Returning exit_phi_result_id (fallback): {:?}",
|
||||
exit_phi_result_id
|
||||
);
|
||||
}
|
||||
Ok(exit_phi_result_id)
|
||||
}
|
||||
}
|
||||
|
||||
/// Phase 3: Allocate new ValueIds for all collected values
|
||||
|
||||
Reference in New Issue
Block a user