## 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>
216 lines
6.3 KiB
Markdown
216 lines
6.3 KiB
Markdown
# 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`: ループ脱出時の変数バインディング構造
|
||
```rust
|
||
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 で箱化)
|
||
```rust
|
||
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 ライフサイクル管理
|
||
```python
|
||
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)
|