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

216 lines
6.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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)