Files
hakorune/docs/development/current/main/phases/phase-132
nyash-codex aad01a1079 feat(llvm): Phase 132-P2 - Dict ctx removal (FunctionLowerContext SSOT completion)
Completed SSOT unification for FunctionLowerContext by removing the manual
dict ctx creation and assignment in function_lower.py.

Changes:
- Removed builder.ctx = dict(...) creation (18 lines, lines 313-330)
- Removed builder.resolver.ctx assignment (no longer needed)
- Confirmed instruction_lower.py uses context=owner.context throughout
- Added phase132_multifunc_isolation_min.hako test for multi-function isolation
- Extended phase132_exit_phi_parity.sh with Case C (Rust VM context test)

Testing:
- Phase 132 smoke test: All 3 cases PASS
- Phase 87 LLVM exe test: PASS (Result: 42)
- STRICT mode: PASS
- No regressions: Behavior identical before/after (RC:6 maintained)

Impact:
- Reduced manual context management complexity
- FunctionLowerContext now sole source of truth (SSOT)
- Per-function state properly isolated, no cross-function collisions
- Cleaner architecture: context parameter passed explicitly vs manual dict

🤖 Generated with Claude Code
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2025-12-15 12:12:54 +09:00
..

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.rs
    • Jump(k_exit, [i_param]) として exit 値を渡す
  • src/mir/builder/control_flow/joinir/patterns/pattern1_minimal.rs
    • LoopExitBinding を作成して with_exit_bindings() で境界に設定

LLVM Python 層PHI SSOT を保護する)

  • src/llvm_py/builders/block_lower.py
    • PHI filtering を predeclared_ret_phis ベースにし、phi.basic_block 依存を排除
    • builder.vmap の既存 PHIplaceholderを上書きしないPHI を SSOT として保護)

検証

  • /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-P2Case CExit PHI ValueId collision fix

Case Capps/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

追加: Phase 132-P3debug-onlyExit PHI collision early detection

同種の ValueId 衝突が “LLVM 実行時” にしか露見しないと切り分けコストが高いので、 JoinIR merge の契約検証debug build早期に Fail-Fast する検証 Box を追加した。

  • 検証: verify_exit_phi_no_collision()
  • 実装: src/mir/builder/control_flow/joinir/merge/contract_checks.rs
  • 呼び出し元: verify_exit_line(...)#[cfg(debug_assertions)]

検出例(概念):

  • bb0: %1 = const 0
  • exit block: %1 = phi ...(衝突)

期待される振る舞い:

  • debug build では compile/run 中に panic で即停止(原因と修正案が出る)
  • release build の既定挙動は変えない(検証は debug_assertions のみ)

追加: Phase 132-P1LLVM PythonFunctionLowerContext Box

Phase 131132 で顕在化した「関数間の状態漏洩block id / value id の衝突)」を、 tuple-key の小手先ではなく 構造で根治するために、関数ローカル状態を 1 箱に隔離した。

  • 実装: src/llvm_py/context/function_lower_context.py
  • 統合: src/llvm_py/builders/function_lower.py

箱の責務SSOT:

  • block_end_valuessnapshot
  • def_blocks
  • jump_only_blocks
  • phi_manager
  • resolver cachesi64/ptr/f64/end_i64 など)

狙い:

  • 関数単位で state を自動破棄し、cross-function collision を "起こせない構造" にする
  • 追加の手動クリアや tuple-key を不要にする

Phase 132-P2 Follow-up: Dict ctx Cleanup

完成: SSOT 統一を完全化するため、src/llvm_py/builders/function_lower.pybuilder.ctx = dict(...) を削除し、FunctionLowerContext への統一を完成させた。

  • 削除: lines 313-330dict ctx の作成と builder.resolver.ctx の割り当て)
  • 入口: instruction_lower.py の全ハンドラが既に context=owner.context で呼び出されているため、 manual ctx dict は不要
  • テスト追加: phase132_multifunc_isolation_min.hakof(2) + f(3) = 5 を exit code で検証)

状態:

  • Dict ctx 削除
  • Instruction handlers は context=owner.context で統一
  • Multi-function isolation test Case C 追加smoke test phase132_exit_phi_parity.sh