Files
hakorune/docs/development/current/main/phase33-20-loop-exit-semantics.md
nyash-codex 4e32a803a7 feat(joinir): Phase 33-22 CommonPatternInitializer & JoinIRConversionPipeline integration
Unifies initialization and conversion logic across all 4 loop patterns,
eliminating code duplication and establishing single source of truth.

## Changes

### Infrastructure (New)
- CommonPatternInitializer (117 lines): Unified loop var extraction + CarrierInfo building
- JoinIRConversionPipeline (127 lines): Unified JoinIR→MIR→Merge flow

### Pattern Refactoring
- Pattern 1: Uses CommonPatternInitializer + JoinIRConversionPipeline (-25 lines)
- Pattern 2: Uses CommonPatternInitializer + JoinIRConversionPipeline (-25 lines)
- Pattern 3: Uses CommonPatternInitializer + JoinIRConversionPipeline (-25 lines)
- Pattern 4: Uses CommonPatternInitializer + JoinIRConversionPipeline (-40 lines)

### Code Reduction
- Total reduction: ~115 lines across all patterns
- Zero code duplication in initialization/conversion
- Pattern files: 806 lines total (down from ~920)

### Quality Improvements
- Single source of truth for initialization
- Consistent conversion flow across all patterns
- Guaranteed boundary.loop_var_name setting (prevents SSA-undef bugs)
- Improved maintainability and testability

### Testing
- All 4 patterns tested and passing:
  - Pattern 1 (Simple While): 
  - Pattern 2 (With Break): 
  - Pattern 3 (If-Else PHI): 
  - Pattern 4 (With Continue): 

### Documentation
- Phase 33-22 inventory and results document
- Updated joinir-architecture-overview.md with new infrastructure

## Breaking Changes
None - pure refactoring with no API changes

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

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2025-12-07 21:02:20 +09:00

5.7 KiB
Raw Blame History

今後は continue 系と JsonParserBox のような実アプリ側ロジックを順番に乗せていく段階に入る。**

Phase 3320: Loop Exit Semantics Fix — Completion Summary

日付: 20251207
状態: 実装完了Pattern1/2/3/4 正常、複雑 continue パターンのみ残課題)


1. ゴール

LoopBuilder 完全削除後の JoinIR ループラインにおいて、

  • loop header PHI
  • ExitLineExitMeta/ExitBinding/ExitLineReconnector
  • JoinInlineBoundary + BoundaryInjector

のあいだで「ループ出口値expr + carrier」の意味論を揃え、

  • SSAundef を起こさない
  • Pattern1/2/3/4 代表ケースでループ終了時の値が正しく戻ってくる

状態にすること。


2. 変更内容

2.1 BoundaryInjector の修正loop_var_name 対応)

ファイル: src/mir/builder/joinir_inline_boundary_injector.rs

問題:

  • loop header PHI の dstループ変数の現在値に対して、BoundaryInjector が entry block で Copy を挿し、 header PHI の意味を上書きしてしまうケースがあった。

修正:

  • JoinInlineBoundary.loop_var_name が設定されている場合は、 すべての join_inputs について entry block での Copy 挿入をスキップするように変更。
  • これにより、header PHI で決まった dst が entry の Copy で壊されることがなくなり、 header PHI が「ループ変数の SSOT」として機能するようになった。

2.2 Pattern3(IfElse PHI) の Boundary 設定

ファイル: src/mir/join_ir/lowering/loop_with_if_phi_minimal.rs

問題:

  • Pattern3 lowerer が JoinInlineBoundary に loop_var_name を設定しておらず、 BoundaryInjector/InstructionRewriter が「このループに expr/キャリア出口がある」ことを認識できていなかった。

修正:

  • Pattern2 と同様に、Pattern3 でも boundary.loop_var_name = Some(..) を設定。
  • これにより、JoinIR merge 時に header PHI / exit PHI のラインが Pattern3 でも有効になる。

2.3 merge/mod.rs — LoopHeader PHI + carrier PHI の連携

ファイル: src/mir/builder/control_flow/joinir/merge/mod.rs

修正ポイント:

  1. ExitLine 側から header PHI に必要な carrier 名一覧(other_carriers)を抽出し、 LoopHeaderPhiBuilder に渡すように変更。
  2. LoopHeaderPhiBuilder が生成した header PHI の dstcarrier_phisを、 LoopExitBinding/ExitLineReconnector が利用するように接続。
  3. function_params のキーを "join_func_0", "join_func_1" 等の実際の JoinIR 関数名に合わせるよう修正 (誤って "main", "loop_step" を参照していたため Pattern4 で PHI が正しく構築されていなかった)。

これにより、

  • header PHI dst を起点に、carrier 用の出口値が ExitLine へ正しく流れるようになった。

2.4 instruction_rewriter.rs — header PHI への Copy スキップlatch incoming 設定

ファイル: src/mir/builder/control_flow/joinir/merge/instruction_rewriter.rs

修正内容:

  • LoopHeader PHI の dst に対して、余計な Copy が挿入されないようにするガードを追加。
  • 複数キャリアのケースで、latch block からの incoming を header PHI の入力として正しく構成するよう調整。

これにより、

  • Pattern1/2/3/4 のループ変数が header PHI → exit PHI → variable_map の順で一貫して伝播するようになった。

3. テスト結果

3.1 Pattern1: Simple While

  • テスト: apps/tests/loop_min_while.hako
  • 期待値: 0, 1, 2 を出力し、RC は 0。
  • 結果: 期待どおり。

3.2 Pattern2: Loop with Breakexpr ループ)

  • テスト: apps/tests/joinir_min_loop.hako
  • 期待値: ループ終了時の i の値2が expr result として返る。
  • 結果: RC: 2、SSAundef なし。

3.3 Pattern3: Loop with IfElse PHI

  • テスト: apps/tests/loop_if_phi.hako
  • 期待値: sum = 91+3+5
  • 結果: sum = 9 を出力、RC も期待どおり。

3.4 Pattern4: Loop with Continue

  • テスト: apps/tests/loop_continue_pattern4.hako
  • 期待値: 251+3+5+7+9
  • 結果: 出力は 25。
    • [joinir/freeze] / SSAundef は発生しない。
    • function_params キーの誤参照("main"/"loop_step""join_func_0"/"join_func_1") を修正したことで、 header PHI / ExitLine との結線が正しくなり、Pattern4 の単純 continue ケースでも期待どおりの値になった。

4. まとめと今後

達成点

  • header PHI を起点とした Loop exit の意味論が Pattern1〜4 の代表ケースで一貫するようになった。
  • ExitLinecarrierと expr PHI ラインが、LoopBuilder なしの JoinIR パイプラインで安全に動く。
  • trim や JsonParser のような複雑なループに対しても、基盤として使える出口経路が整った。

残課題

  • 「else 側 continue」を含む複雑な continue パターンPhase 3318 の Pattern Bはまだ JoinIR 側で正規化されていない。
    • これらは BoolExprLowerer/ContinueBranchNormalizer で if (!cond) { … } 形に正規化し、 Pattern4 lowerer に統合する計画Phase 3319 以降)。

関連フェーズ

  • Phase 3316: Loop header PHI SSOT 導入
  • Phase 3319: Continue + if/else パターンの正規化(設計済み)
  • Phase 170: JsonParserBox / trim の JoinIR 準備ライン

このフェーズで「LoopBuilder 無しの JoinIR ループ出口ラインの基礎」は固まったので、
今後は continue 系と JsonParserBox のような実アプリ側ロジックを順番に乗せていく段階に入る。**