Added verify_exit_phi_no_collision() to contract_checks.rs for early detection of ValueId collisions between exit PHIs and other instructions. Problem detected: - If exit_phi_builder uses builder.value_gen.next() (module-level) instead of func.next_value_id() (function-level), ValueIds can collide: Example: - bb0: %1 = const 0 (counter init) - bb3: %1 = phi ... (exit PHI - collision!) Previous behavior: - Error only detected at LLVM backend runtime - Cryptic error: "Cannot overwrite PHI dst=1" New behavior: - Panic at Rust compile time (debug build) - Clear error message with fix suggestion: "Exit PHI dst %1 collides with instruction in block 0 Fix: Use func.next_value_id() in exit_phi_builder.rs" Benefits: - 🔥 Fail-Fast: Catch errors during Rust compilation, not LLVM execution - 📋 Clear messages: Exact collision point + fix suggestion - 🧪 Testable: verify_exit_phi_no_collision() can be unit tested 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Phase 132: Exit Values Parity (VM == LLVM)
Date: 2025-12-15
Status: ✅ Done
Scope: ループ exit 値(exit PHI / boundary)が VM/LLVM で一致することを固定する(Pattern 1 と Case C を含む)
背景
最小ケース:
static box Main {
main() {
local i = 0
loop(i < 3) { i = i + 1 }
return i // 期待: 3
}
}
- VM:
RC: 3✅ - LLVM:
Result: 0❌(修正前)
根本原因(2層)
1) JoinIR/Boundary 層: exit 値が境界を通っていない
- Pattern 1 の JoinIR で
k_exitに渡す args が空のままだと、exit 側でホストのvariable_map["i"]が更新されない。 - その結果
return iが初期値(0)を返し得る。
2) LLVM Python 層: PHI の SSOT が壊れている
phi.basic_blockが llvmlite で確定前はNoneになり得るため、PHI を落とす filter を信頼できない。builder.vmapにある PHI placeholder を、Pass A の “sync created values” が上書きしてしまい、retが PHI ではなく0を参照することがある。
修正内容
JoinIR/Boundary 層(exit 値を明示的に渡す)
src/mir/join_ir/lowering/simple_while_minimal.rsJump(k_exit, [i_param])として exit 値を渡す
src/mir/builder/control_flow/joinir/patterns/pattern1_minimal.rsLoopExitBindingを作成してwith_exit_bindings()で境界に設定
LLVM Python 層(PHI SSOT を保護する)
src/llvm_py/builders/block_lower.py- PHI filtering を
predeclared_ret_phisベースにし、phi.basic_block依存を排除 builder.vmapの既存 PHI(placeholder)を上書きしない(PHI を SSOT として保護)
- PHI filtering を
検証
/tmp/p1_return_i.hakoが VM/LLVM で3を返すNYASH_LLVM_STRICT=1でも “miss→0” に落ちない(Fail-Fast が維持される)
詳細ログ:
docs/development/current/main/investigations/phase132-llvm-exit-phi-wrong-result.md
追加: Phase 132-P2(Case C)Exit PHI ValueId collision fix
Case C(apps/tests/llvm_stage3_loop_only.hako)の LLVM EXE 検証で、
exit PHI の ValueId 衝突が原因で Result: 0 になる問題が見つかった。
Root Cause
src/mir/builder/control_flow/joinir/merge/exit_phi_builder.rsが PHI dst の割り当てにbuilder.value_gen.next()(module-level)を使っており、 同一関数内で ValueId が衝突し得た。
Fix
- PHI dst の割り当てを
func.next_value_id()(function-level)へ統一。
コミット:
bd07b7f4 fix(joinir): Phase 132-P2 - Exit PHI ValueId collision fix
Verification
- Pattern 1: VM/LLVM ともに
3 - Case C: VM/LLVM ともに
Result: 3
調査ログ:
docs/development/current/main/investigations/phase132-case-c-llvm-exe.md