Files
hakorune/docs/development/architecture/exit-phi-design.md
nyash-codex 447d4ea246 feat(llvm): Phase 132 - Pattern 1 exit value parity fix + Box-First refactoring
## Phase 132: Exit PHI Value Parity Fix

### Problem
Pattern 1 (Simple While) returned 0 instead of final loop variable value (3)
- VM: RC: 3  (correct)
- LLVM: Result: 0  (wrong)

### Root Cause (Two Layers)
1. **JoinIR/Boundary**: Missing exit_bindings → ExitLineReconnector not firing
2. **LLVM Python**: block_end_values snapshot dropping PHI values

### Fix
**JoinIR** (simple_while_minimal.rs):
- Jump(k_exit, [i_param]) passes exit value

**Boundary** (pattern1_minimal.rs):
- Added LoopExitBinding with carrier_name="i", role=LoopState
- Enables ExitLineReconnector to update variable_map

**LLVM** (block_lower.py):
- Use predeclared_ret_phis for reliable PHI filtering
- Protect builder.vmap PHIs from overwrites (SSOT principle)

### Result
-  VM: RC: 3
-  LLVM: Result: 3
-  VM/LLVM parity achieved

## Phase 132-Post: Box-First Refactoring

### Rust Side
**JoinModule::require_function()** (mod.rs):
- Encapsulate function search logic
- 10 lines → 1 line (90% reduction)
- Reusable for Pattern 2-5

### Python Side
**PhiManager Box** (phi_manager.py - new):
- Centralized PHI lifecycle management
- 47 lines → 8 lines (83% reduction)
- SSOT: builder.vmap owns PHIs
- Fail-Fast: No silent overwrites

**Integration**:
- LLVMBuilder: Added phi_manager
- block_lower.py: Delegated to PhiManager
- tagging.py: Register PHIs with manager

### Documentation
**New Files**:
- docs/development/architecture/exit-phi-design.md
- docs/development/current/main/investigations/phase132-llvm-exit-phi-wrong-result.md
- docs/development/current/main/phases/phase-132/

**Updated**:
- docs/development/current/main/10-Now.md
- docs/development/current/main/phase131-3-llvm-lowering-inventory.md

### Design Principles
- Box-First: Logic encapsulated in classes/methods
- SSOT: Single Source of Truth (builder.vmap for PHIs)
- Fail-Fast: Early explicit failures, no fallbacks
- Separation of Concerns: 3-layer architecture

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-15 03:17:31 +09:00

6.3 KiB
Raw Blame History

Exit PHI Design - Phase 132 Architecture

Overview

Phase 132 で完成した Exit PHI アーキテクチャの責務分離設計。

Three-Layer Responsibility Model

Layer 1: JoinIR (Frontend - データ生成層)

責務: ループ脱出時の変数バインディング情報を生成

実装箇所: src/mir/join_ir/lowering/inline_boundary/

主要コンポーネント:

  • LoopExitBinding: ループ脱出時の変数バインディング構造
    pub struct LoopExitBinding {
        pub carrier_name: String,       // 変数名 (e.g., "i")
        pub join_exit_value: ValueId,   // JoinIR k_exit 関数の引数
        pub host_slot: ValueId,         // Host MIR の PHI dst
    }
    

データフロー:

Pattern 1 Minimal:
  loop_step → Jump(k_exit, [i_param]) → exit_bindings = [LoopExitBinding { "i", i_param, host_slot }]

Phase 132 貢献:

  • exit_bindings フィールド追加(JoinInlineBoundary 構造体)
  • Pattern 1-5 各パターンで exit binding 生成ロジック実装

Layer 2: Boundary (Middleware - 接続実行層)

責務: JoinIR の exit_bindings を使って Host MIR に Exit PHI を接続

実装箇所: src/mir/builder/control_flow/joinir/merge/

主要コンポーネント:

  • ExitLineReconnector: Exit PHI 接続 Box (Phase 33-10 で箱化)
    impl ExitLineReconnector {
        fn connect_exit_line(
            &self,
            boundary: &JoinInlineBoundary,
            exit_block_id: BasicBlockId,
            exit_predecessor: BasicBlockId,
            builder: &mut Builder,
        ) -> Result<(), String>
    }
    

処理フロー:

  1. exit_bindings をイテレート
  2. 各 binding について:
    • host_slot (PHI dst) に対して
    • (exit_predecessor, join_exit_value) を incoming として追加

Phase 132 貢献:

  • exit_bindings を読み取る発火ロジック実装
  • Phase 131 の metadata 参照ロジックを完全削除SSOT化

Layer 3: LLVM Backend (Execution - PHI保護層)

責務: builder.vmap 内の PHI を SSOT として保護・管理

実装箇所: src/llvm_py/

主要コンポーネント (Phase 132-Post):

  • PhiManager Box: PHI ライフサイクル管理
    class PhiManager:
        def register_phi(bid: int, vid: int, phi_value)
        def is_phi_owned(bid: int, vid: int) -> bool
        def filter_vmap_preserve_phis(vmap: dict, target_bid: int) -> dict
        def sync_protect_phis(target_vmap: dict, source_vmap: dict)
    

SSOT Principle:

  • builder.vmap の PHI は Single Source of Truth
  • PHI は絶対に上書きしない
  • ブロック間で PHI 所有権を明確に管理

Phase 132 貢献:

  • predeclared_ret_phis dict による PHI ownership tracking
  • vmap filtering: ブロック外の PHI を除外
  • sync protection: 既存 PHI を上書きしない

Phase 132-Post 貢献 (Box-First Refactoring):

  • PhiManager Box 化で PHI 管理ロジック集約
  • filter_vmap_preserve_phis(): PHI フィルタリングをカプセル化
  • sync_protect_phis(): PHI 保護ロジックをカプセル化

Data Flow Example (Pattern 1 Minimal)

【JoinIR Layer】
  loop_step 関数:
    Jump(k_exit, [i_param], cond=exit_cond)
  ↓
  Pattern 1 lowering:
    exit_bindings = [
      LoopExitBinding {
        carrier_name: "i",
        join_exit_value: ValueId(1003),  // JoinIR i_param
        host_slot: ValueId(3),           // Host MIR PHI dst
      }
    ]

【Boundary Layer】
  ExitLineReconnector::connect_exit_line():
    for binding in exit_bindings:
      builder.add_phi_incoming(
        block: exit_block_id,
        dst: ValueId(3),                  // host_slot
        incoming: (bb6, ValueId(3))       // (exit_pred, remapped join_exit_value)
      )
  ↓
  Host MIR:
    bb3: ValueId(3) = phi [(bb6, ValueId(3))]

【LLVM Layer】
  PhiManager::register_phi(3, 3, phi_value)  // PHI を登録
  ↓
  block_lower.py Pass A (非終端命令処理):
    vmap_cur = PhiManager.filter_vmap_preserve_phis(builder.vmap, 3)
    # → bb3 所有の PHI(3) のみ保持、他ブロックの PHI は除外
  ↓
  Pass A sync:
    PhiManager.sync_protect_phis(builder.vmap, vmap_cur)
    # → builder.vmap の PHI を上書きしない
  ↓
  Pass B (PHI finalization):
    phi_3.add_incoming(val_3, bb6)
  ↓
  Pass C (終端命令処理):
    ret phi_3

Design Principles

1. Separation of Concerns

  • JoinIR: データ生成のみ(実行なし)
  • Boundary: 接続実行のみ(データ保護なし)
  • LLVM: PHI保護・管理のみ生成なし

2. Box-First Architecture (Phase 132-Post)

  • ロジックを Box (クラス/メソッド) にカプセル化
  • ExitLineReconnector Box (Boundary)
  • PhiManager Box (LLVM)

3. SSOT (Single Source of Truth)

  • exit_bindings が変数バインディングの唯一の真実
  • builder.vmap の PHI が SSA 値の唯一の真実
  • metadata 参照の完全排除

4. Fail-Fast

  • エラーは早期に明示的に失敗
  • フォールバック処理は禁止
  • PHI 上書きは panic

Migration from Phase 131

Phase 131 (Before)

  • Jump metadata (jump_args) から exit PHI を復元
  • Block.metadata 参照の散在
  • PHI 管理ロジックの分散

Phase 132 (After)

  • exit_bindings で明示的データフロー
  • Boundary 層での一元的 PHI 接続
  • metadata 完全削除Block からも削除)

Phase 132-Post (Box-First Refactoring)

  • PhiManager Box による PHI 管理ロジック集約
  • filter_vmap_preserve_phis() でフィルタリング簡潔化
  • sync_protect_phis() で保護ロジック再利用可能

Future Work

Pattern 2-5 への拡張

  • Pattern 2 (If-in-Loop): 複数変数 exit bindings
  • Pattern 3 (Loop-with-If): exit line 分岐処理
  • Pattern 4-5: 複雑な exit 条件

PhiManager 拡張候補

  • PHI タイプヒント管理
  • PHI incoming 検証
  • PHI 最適化ヒント

References

  • Phase 132 実装ログ: docs/development/current/main/phases/phase-132/
  • Boundary アーキテクチャ: docs/development/architecture/phase-33-modularization.md
  • JoinIR 全体設計: docs/development/current/main/joinir-architecture-overview.md

Last Updated: 2025-12-15 (Phase 132-Post Box-First Refactoring)