feat(joinir): Phase 197 Pattern4 multi-carrier exit fix & AST-based update
Phase 197-B: Multi-Carrier Exit Mechanism - Fixed reconnect_boundary() to use remapper for per-carrier exit values - ExitMeta now uses carrier_param_ids (Jump arguments) instead of carrier_exit_ids (k_exit parameters) - Root cause: k_exit parameters aren't defined when JoinIR functions merge into host MIR Phase 197-C: AST-Based Update Expression - LoopUpdateAnalyzer extracts update patterns from loop body AST - Pattern4 lowerer uses UpdateExpr for semantically correct RHS - sum = sum + i → uses i_next (current iteration value) - count = count + 1 → uses const_1 Files modified: - src/mir/builder/control_flow/joinir/merge/mod.rs - src/mir/join_ir/lowering/loop_with_continue_minimal.rs - src/mir/join_ir/lowering/loop_update_analyzer.rs (new) Test results: - loop_continue_multi_carrier.hako: 25, 5 ✅ - loop_continue_pattern4.hako: 25 ✅ Pattern 1–4 now unified with: - Structure-based detection (LoopFeatures + classify) - Carrier/Exit metadata (CarrierInfo + ExitMeta) - Boundary connection (JoinInlineBoundary + LoopExitBinding) - AST-based update expressions (LoopUpdateAnalyzer) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@ -100,8 +100,9 @@ pub(in crate::mir::builder) fn merge_joinir_mir_blocks(
|
||||
)?;
|
||||
|
||||
// Phase 6: Reconnect boundary (if specified)
|
||||
// Phase 197-B: Pass remapper to enable per-carrier exit value lookup
|
||||
if let Some(boundary) = boundary {
|
||||
reconnect_boundary(builder, boundary, exit_phi_result_id, debug)?;
|
||||
reconnect_boundary(builder, boundary, &remapper, exit_phi_result_id, debug)?;
|
||||
}
|
||||
|
||||
// Jump from current block to entry function's entry block
|
||||
@ -177,47 +178,90 @@ fn remap_values(
|
||||
}
|
||||
|
||||
/// Phase 6: Reconnect boundary to update host variable_map
|
||||
///
|
||||
/// Phase 197-B: Multi-carrier support
|
||||
/// Previously, a single exit_phi_result was applied to all carriers, causing
|
||||
/// all carriers to get the same value (e.g., both sum and count = 5).
|
||||
///
|
||||
/// Now, we use each binding's join_exit_value and the remapper to find
|
||||
/// the actual exit value for each carrier.
|
||||
fn reconnect_boundary(
|
||||
builder: &mut crate::mir::builder::MirBuilder,
|
||||
boundary: &JoinInlineBoundary,
|
||||
exit_phi_result: Option<ValueId>,
|
||||
remapper: &crate::mir::builder::joinir_id_remapper::JoinIrIdRemapper,
|
||||
_exit_phi_result: Option<ValueId>,
|
||||
debug: bool,
|
||||
) -> Result<(), String> {
|
||||
// Phase 190: Use explicit LoopExitBinding to reconnect exit PHI to variable_map
|
||||
// Each binding explicitly names the carrier variable and maps exit PHI to it.
|
||||
if let Some(phi_result) = exit_phi_result {
|
||||
// Phase 190: Use exit_bindings for explicit carrier naming
|
||||
// This eliminates ambiguity about which variable is being updated
|
||||
for binding in &boundary.exit_bindings {
|
||||
// Find the variable in variable_map that matches the binding's host_slot
|
||||
for (var_name, vid) in builder.variable_map.iter_mut() {
|
||||
if *vid == binding.host_slot {
|
||||
*vid = phi_result;
|
||||
if debug {
|
||||
eprintln!(
|
||||
"[cf_loop/joinir] Phase 190: Reconnected exit PHI {:?} to variable_map['{}'] (carrier: {})",
|
||||
phi_result, var_name, binding.carrier_name
|
||||
);
|
||||
}
|
||||
// Validate carrier name matches
|
||||
if var_name != &binding.carrier_name && debug {
|
||||
eprintln!(
|
||||
"[cf_loop/joinir] WARNING: Carrier name mismatch: expected '{}', found '{}'",
|
||||
binding.carrier_name, var_name
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Phase 197-B: Multi-carrier exit binding using join_exit_value directly
|
||||
//
|
||||
// Problem: Previously used single exit_phi_result for all carriers.
|
||||
// Solution: Each carrier's exit value is in k_exit parameters.
|
||||
// We use remapper to find the remapped ValueId for each carrier.
|
||||
//
|
||||
// For Pattern 4 with 2 carriers (sum, count):
|
||||
// - k_exit(sum_exit, count_exit) receives values via Jump args
|
||||
// - sum_exit = ValueId(N) in JoinIR → remapped to ValueId(M) in host
|
||||
// - count_exit = ValueId(N+1) in JoinIR → remapped to ValueId(M+1) in host
|
||||
//
|
||||
// Each carrier's variable_map entry is updated with its specific remapped value.
|
||||
|
||||
if boundary.exit_bindings.is_empty() {
|
||||
if debug {
|
||||
eprintln!("[cf_loop/joinir] Phase 197-B: No exit bindings, skipping reconnect");
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if debug {
|
||||
eprintln!(
|
||||
"[cf_loop/joinir] Phase 197-B: Reconnecting {} exit bindings",
|
||||
boundary.exit_bindings.len()
|
||||
);
|
||||
}
|
||||
|
||||
// For each binding, look up the remapped exit value and update variable_map
|
||||
for binding in &boundary.exit_bindings {
|
||||
// Phase 197-B: Use remapper to find the remapped exit value
|
||||
let remapped_exit = remapper.get_value(binding.join_exit_value);
|
||||
|
||||
if debug {
|
||||
eprintln!(
|
||||
"[cf_loop/joinir] Phase 197-B: Carrier '{}' join_exit={:?} → remapped={:?}",
|
||||
binding.carrier_name, binding.join_exit_value, remapped_exit
|
||||
);
|
||||
}
|
||||
|
||||
// Phase 190: Backward compatibility - also check deprecated host_outputs
|
||||
#[allow(deprecated)]
|
||||
if !boundary.host_outputs.is_empty() && debug {
|
||||
if let Some(remapped_value) = remapped_exit {
|
||||
// Update variable_map with the remapped exit value
|
||||
if let Some(var_vid) = builder.variable_map.get_mut(&binding.carrier_name) {
|
||||
if debug {
|
||||
eprintln!(
|
||||
"[cf_loop/joinir] Phase 197-B: Updated variable_map['{}'] {:?} → {:?}",
|
||||
binding.carrier_name, var_vid, remapped_value
|
||||
);
|
||||
}
|
||||
*var_vid = remapped_value;
|
||||
} else if debug {
|
||||
eprintln!(
|
||||
"[cf_loop/joinir] Phase 197-B WARNING: Carrier '{}' not found in variable_map",
|
||||
binding.carrier_name
|
||||
);
|
||||
}
|
||||
} else if debug {
|
||||
eprintln!(
|
||||
"[cf_loop/joinir] WARNING: Using deprecated host_outputs. Migrate to exit_bindings."
|
||||
"[cf_loop/joinir] Phase 197-B WARNING: No remapped value for join_exit={:?}",
|
||||
binding.join_exit_value
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Phase 190: Backward compatibility - also check deprecated host_outputs
|
||||
#[allow(deprecated)]
|
||||
if !boundary.host_outputs.is_empty() && debug {
|
||||
eprintln!(
|
||||
"[cf_loop/joinir] WARNING: Using deprecated host_outputs. Migrate to exit_bindings."
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user