fix(joinir): Phase 132-P2 - Exit PHI ValueId collision fix

Root cause: exit_phi_builder.rs used builder.value_gen.next() (module-level counter)
instead of func.next_value_id() (function-level counter) for allocating exit PHI dst.

This caused ValueId collisions in functions like:
- bb0: %1 = const 0  (initial counter value)
- bb3: %1 = phi ...  (exit PHI - collision!)

The LLVM backend then failed with:
"Cannot overwrite PHI dst=1. ValueId namespace collision detected."

Fix: Use func.next_value_id() to allocate from the function's local counter,
ensuring unique ValueIds within each function.

Verified:
- Pattern 1 (simple while): VM RC:3, LLVM RC:3 
- Case C (Pattern 5): VM returns 3, LLVM Result: 3 

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
nyash-codex
2025-12-15 05:54:22 +09:00
parent 11e794d0ff
commit bd07b7f41f

View File

@ -32,8 +32,10 @@ pub(super) fn build_exit_phi(
// Phase 189-Fix: If we collected return values, create a PHI in exit block
// This merges all return values from JoinIR functions into a single value
let phi_result = if !exit_phi_inputs.is_empty() {
// Allocate a new ValueId for the PHI result
let phi_dst = builder.value_gen.next();
// Phase 132-P2: Use function-level next_value_id() to allocate
// Previously used builder.value_gen.next() which is module-level, causing ValueId collisions
// Note: We use func.next_value_id() directly since builder.current_function is already borrowed
let phi_dst = func.next_value_id();
exit_block.instructions.push(MirInstruction::Phi {
dst: phi_dst,
inputs: exit_phi_inputs.to_vec(),
@ -61,8 +63,10 @@ pub(super) fn build_exit_phi(
continue;
}
// Allocate a new ValueId for this carrier's PHI
let phi_dst = builder.value_gen.next();
// Phase 132-P2: Use function-level next_value_id() to allocate
// Previously used builder.value_gen.next() which is module-level, causing ValueId collisions
// Note: We use func.next_value_id() directly since builder.current_function is already borrowed
let phi_dst = func.next_value_id();
exit_block.instructions.push(MirInstruction::Phi {
dst: phi_dst,
inputs: inputs.clone(),