fix(joinir): Pattern 4 PHI loss fix with exit_bindings and block reuse

Two root causes for Pattern 4 outputting 0 instead of 25:

1. instruction_rewriter.rs:92 - BasicBlock::new() was overwriting
   existing blocks that already contained PHI instructions from
   JoinIR Select lowering. Fixed by removing and reusing existing
   blocks instead of creating new ones.

2. pattern4_with_continue.rs - JoinIR exit PHI (ValueId 15) was not
   connected to host's sum variable, causing DCE to eliminate the PHI
   as "unused". Fixed by using new_with_exit_bindings() with explicit
   LoopExitBinding to connect k_exit's sum_exit to host's sum slot.

Test result: loop_continue_pattern4.hako now correctly outputs 25.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
nyash-codex
2025-12-06 01:54:47 +09:00
parent ee04ba4406
commit 318dceebfe
2 changed files with 19 additions and 3 deletions

View File

@ -89,7 +89,15 @@ pub(super) fn merge_and_rewrite(
let new_block_id = remapper
.get_block(func_name, *old_block_id)
.ok_or_else(|| format!("Block {:?} not found for {}", old_block_id, func_name))?;
let mut new_block = BasicBlock::new(new_block_id);
// Phase 195 FIX: Reuse existing block if present (preserves PHI from JoinIR Select lowering)
// ultrathink "finalizer集約案": Don't overwrite blocks with BasicBlock::new()
let mut new_block = if let Some(ref mut current_func) = builder.current_function {
current_func.blocks.remove(&new_block_id)
.unwrap_or_else(|| BasicBlock::new(new_block_id))
} else {
BasicBlock::new(new_block_id)
};
// Phase 189 FIX: Check if this is entry function's entry block (for boundary input skipping)
let is_entry_func_entry_block =

View File

@ -182,10 +182,18 @@ impl MirBuilder {
);
// Merge JoinIR blocks into current function
// Phase 195: Create and pass JoinInlineBoundary for Pattern 4
let boundary = crate::mir::join_ir::lowering::inline_boundary::JoinInlineBoundary::new_inputs_only(
// Phase 195 FIX: Use exit_bindings to connect k_exit's sum_exit to host's sum variable
// Pattern 4: k_exit(sum_exit) returns sum_exit, so we bind ValueId(15) to host's sum
let boundary = crate::mir::join_ir::lowering::inline_boundary::JoinInlineBoundary::new_with_exit_bindings(
vec![ValueId(0), ValueId(1)], // JoinIR's main() parameters (i_init, sum_init)
vec![loop_var_id, sum_var_id], // Host's loop variables
vec![
crate::mir::join_ir::lowering::inline_boundary::LoopExitBinding {
carrier_name: "sum".to_string(),
join_exit_value: ValueId(15), // k_exit's parameter (sum_exit)
host_slot: sum_var_id, // variable_map["sum"]
}
],
);
// Phase 195: Capture exit PHI result (Pattern 4 returns sum)
let result_val = self.merge_joinir_mir_blocks(&mir_module, Some(&boundary), debug)?;