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:
nyash-codex
2025-12-10 04:23:34 +09:00
parent 8ca30f375a
commit 33e80637dd
2 changed files with 207 additions and 59 deletions

View File

@ -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"));
}
}

View File

@ -21,6 +21,7 @@ mod loop_header_phi_info;
mod loop_header_phi_builder; mod loop_header_phi_builder;
mod tail_call_classifier; mod tail_call_classifier;
mod merge_result; mod merge_result;
mod expr_result_resolver;
// Phase 33-17: Re-export for use by other modules // Phase 33-17: Re-export for use by other modules
pub use loop_header_phi_info::LoopHeaderPhiInfo; 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(); builder.reserved_value_ids.clear();
} }
// Phase 221: Return expr_result if present (for expr-position loops) // Phase 221-R: Use ExprResultResolver Box
// The expr_result from boundary contains the JoinIR-local ValueId that should let expr_result_value = expr_result_resolver::ExprResultResolver::resolve(
// be returned. We need to map it to the HOST ValueId space. boundary.and_then(|b| b.expr_result),
// boundary.map(|b| b.exit_bindings.as_slice()).unwrap_or(&[]),
// IMPORTANT: If expr_result corresponds to a carrier variable, we should return &carrier_phis,
// the carrier PHI dst (from loop header), NOT the remapped JoinIR value. &remapper,
// This is because carriers use header PHIs which dominate the exit block. debug,
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
));
}
}
}
// expr_result is NOT a carrier - use remapped value // Return expr_result if present, otherwise fall back to exit_phi_result_id
if let Some(remapped_expr) = remapper.get_value(expr_result_id) { if let Some(resolved) = expr_result_value {
eprintln!( if debug {
"[cf_loop/joinir] Phase 221: Returning non-carrier expr_result: JoinIR {:?} → Host {:?}", eprintln!("[cf_loop/joinir] Phase 221-R: Returning expr_result_value {:?}", resolved);
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
));
}
} else {
eprintln!("[cf_loop/joinir] Phase 221: expr_result is None, using fallback");
} }
Ok(Some(resolved))
} else { } 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-R: Returning exit_phi_result_id (fallback): {:?}",
exit_phi_result_id
);
}
Ok(exit_phi_result_id)
} }
// 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): {:?}",
exit_phi_result_id
);
}
Ok(exit_phi_result_id)
} }
/// Phase 3: Allocate new ValueIds for all collected values /// Phase 3: Allocate new ValueIds for all collected values