fix(mir/exit_phi): Pass branch_source_block to build_exit_phis()
## Problem Exit PHI generation was using header_id as predecessor, but when build_expression(condition) creates new blocks, the actual branch instruction is emitted from a different block, causing: "phi pred mismatch: no input for predecessor BasicBlockId(X)" ## Solution - Modified build_exit_phis() to accept branch_source_block parameter - Capture actual block after condition evaluation in loop_builder.rs - Use branch_source_block instead of header_id for PHI inputs ## Progress - Error changed from ValueId(5941)/BasicBlockId(4674) to ValueId(5927)/BasicBlockId(4672), showing partial fix - Added comprehensive test suite in mir_loopform_exit_phi.rs - Added debug logging to trace condition block creation ## Status Partial fix - unit tests pass, but Test 2 (Stage-B compilation) still has errors. Needs further investigation of complex nested compilation scenarios. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@ -222,29 +222,50 @@ impl LoopFormBuilder {
|
||||
/// Similar to header PHIs, but merges:
|
||||
/// - Header fallthrough (normal loop exit)
|
||||
/// - Break snapshots (early exit from loop body)
|
||||
///
|
||||
/// # Parameters
|
||||
/// - `branch_source_block`: The ACTUAL block that emitted the branch to exit
|
||||
/// (might differ from header_id if condition evaluation created new blocks)
|
||||
pub fn build_exit_phis<O: LoopFormOps>(
|
||||
&self,
|
||||
ops: &mut O,
|
||||
exit_id: BasicBlockId,
|
||||
branch_source_block: BasicBlockId,
|
||||
exit_snapshots: &[(BasicBlockId, HashMap<String, ValueId>)],
|
||||
) -> Result<(), String> {
|
||||
ops.set_current_block(exit_id)?;
|
||||
|
||||
let debug = std::env::var("NYASH_LOOPFORM_DEBUG").is_ok();
|
||||
if debug {
|
||||
eprintln!("[DEBUG/exit_phi] ====== Exit PHI Generation ======");
|
||||
eprintln!("[DEBUG/exit_phi] exit_id = {:?}, header_id = {:?}, branch_source = {:?}",
|
||||
exit_id, self.header_id, branch_source_block);
|
||||
eprintln!("[DEBUG/exit_phi] exit_snapshots.len() = {}", exit_snapshots.len());
|
||||
for (i, (bb, snap)) in exit_snapshots.iter().enumerate() {
|
||||
eprintln!("[DEBUG/exit_phi] snapshot[{}]: block = {:?}, num_vars = {}",
|
||||
i, bb, snap.len());
|
||||
}
|
||||
eprintln!("[DEBUG/exit_phi] pinned.len() = {}, carriers.len() = {}",
|
||||
self.pinned.len(), self.carriers.len());
|
||||
}
|
||||
|
||||
// Collect all variables that need exit PHIs
|
||||
let mut all_vars: HashMap<String, Vec<(BasicBlockId, ValueId)>> = HashMap::new();
|
||||
|
||||
// Add header fallthrough values (pinned + carriers)
|
||||
// Use branch_source_block instead of header_id because condition evaluation
|
||||
// might create new blocks, and the branch is emitted from the LAST block
|
||||
for pinned in &self.pinned {
|
||||
all_vars
|
||||
.entry(pinned.name.clone())
|
||||
.or_default()
|
||||
.push((self.header_id, pinned.header_phi));
|
||||
.push((branch_source_block, pinned.header_phi));
|
||||
}
|
||||
for carrier in &self.carriers {
|
||||
all_vars
|
||||
.entry(carrier.name.clone())
|
||||
.or_default()
|
||||
.push((self.header_id, carrier.header_phi));
|
||||
.push((branch_source_block, carrier.header_phi));
|
||||
}
|
||||
|
||||
// Add break snapshot values
|
||||
@ -262,15 +283,33 @@ impl LoopFormBuilder {
|
||||
// Deduplicate inputs by predecessor block
|
||||
sanitize_phi_inputs(&mut inputs);
|
||||
|
||||
if debug {
|
||||
eprintln!("[DEBUG/exit_phi] Variable '{}': {} inputs before dedup", var_name, inputs.len());
|
||||
for (bb, val) in &inputs {
|
||||
eprintln!("[DEBUG/exit_phi] pred={:?} val={:?}", bb, val);
|
||||
}
|
||||
}
|
||||
|
||||
match inputs.len() {
|
||||
0 => {} // No inputs, skip
|
||||
1 => {
|
||||
// Single predecessor: direct binding
|
||||
if debug {
|
||||
eprintln!("[DEBUG/exit_phi] Variable '{}': single predecessor, direct binding to {:?}",
|
||||
var_name, inputs[0].1);
|
||||
}
|
||||
ops.update_var(var_name, inputs[0].1);
|
||||
}
|
||||
_ => {
|
||||
// Multiple predecessors: create PHI node
|
||||
let phi_id = ops.new_value();
|
||||
if debug {
|
||||
eprintln!("[DEBUG/exit_phi] Creating PHI {:?} for var '{}' with {} inputs",
|
||||
phi_id, var_name, inputs.len());
|
||||
for (bb, val) in &inputs {
|
||||
eprintln!("[DEBUG/exit_phi] PHI input: pred={:?} val={:?}", bb, val);
|
||||
}
|
||||
}
|
||||
ops.emit_phi(phi_id, inputs)?;
|
||||
ops.update_var(var_name, phi_id);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user