Files
hakorune/docs/development/current/main/phase33-10-local-pattern-mir-analysis.md
nyash-codex 588129db65 feat(joinir): Phase 34-6 MethodCall 構造と本物の substring 意味論
**Phase 34-6 実装完了**: MethodCall 構造を JoinIR に追加し、本物の substring
呼び出しを通すことに成功。

## 主要変更

### 1. MethodCall 構造追加 (34-6.1)
- `src/mir/join_ir/mod.rs`: JoinInst::MethodCall バリアント (+8 lines)
  - 構造: `{ dst, receiver, method, args }`
  - 設計原則: JoinIR は構造のみ、意味論は MIR レベル

### 2. extract_value 更新 (34-6.2)
- `src/mir/join_ir/frontend/ast_lowerer.rs`: Method 処理本物化 (+37 lines)
  - receiver/args を extract_value で再帰処理
  - ダミー Const(0) 削除 → 本物の MethodCall 生成
  - cond 処理修正: ValueId(0) ハードコード → extract_value で取得

### 3. JoinIR→MIR 変換実装 (34-6.3)
- `src/mir/join_ir_vm_bridge.rs`: MethodCall → BoxCall 変換 (+12 lines)
- `src/mir/join_ir/json.rs`: MethodCall JSON シリアライゼーション (+16 lines)
- `src/mir/join_ir_runner.rs`: MethodCall 未対応エラー (+7 lines)

### 4. テスト更新 (34-6.4)
- `docs/.../fixtures/json_shape_read_value.program.json`: 本物の substring 構造
- `src/tests/joinir_frontend_if_select.rs`: run_joinir_via_vm 使用
- テスト成功: v="hello", at=3 → "hel" 

## 成果

-  テスト全通過(1 passed; 0 failed)
-  設計原則確立: JoinIR = 構造 SSOT、意味論 = MIR レベル
-  Phase 33-10 原則との整合性: Method でも同じ原則適用

**ドキュメント更新**: CURRENT_TASK.md + TASKS.md(Phase 34-6 完了記録)

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-27 17:05:46 +09:00

631 lines
16 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.

# Phase 33-10: Local Pattern の実MIR構造分析レポート
**作成日**: 2025-11-27
**分析者**: Claude Code
**目的**: If lowering における「local pattern」の実MIR構造を分析し、Phase 33-10の実装方針を決定する
---
## 1. Executive Summary
### 1.1 重要な結論
**PHI命令を持つMIRは、JoinIR変換の対象外とすべきOption A推奨**
理由:
1. **PHI命令は既にSSAの標準的な値合流表現** - これ以上の変換は不要
2. **JoinIR Select/IfMergeの役割は "PHI生成"** - 既存PHIを変換するのは逆行
3. **try_match_local_pattern()の想定は誤り** - 実MIRとの構造差分が根本的
### 1.2 実装への影響
**Phase 33-10の方針**:
- ❌ try_match_local_pattern()の修正は**不要**
- ✅ local patternは**Phase 33-10の対象外**として扱う
- ✅ JoinIR loweringは「PHI命令がない場合のみ」実施
- ✅ 既存のif_phi.rsによるPHI生成は維持これが正常動作
---
## 2. 構造比較詳細
### 2.1 ユニットテスト想定(誤り)
**try_match_local_pattern()が期待していた構造**:
```mir
bb0: br cond, bb1, bb2
bb1 (then):
Copy { dst: x, src: 100 } ← 単一の代入命令
Jump bb3
bb2 (else):
Copy { dst: x, src: 200 } ← 単一の代入命令
Jump bb3
bb3 (merge):
[空 - 命令なし] ← 重要: 命令がない!
Return x
```
**検証コード**if_select.rs:262-263:
```rust
if !merge_block.instructions.is_empty() {
return None; // ← merge blockが空でなければ失敗
}
```
### 2.2 実MIR構造正しい
**実際のコンパイラ出力** (`/tmp/test_phi_local.hako`):
```mir
define i64 @main() {
bb0:
1: %3: Integer = const 1
1: %4: Integer = copy %3
1: %5: Integer = copy %4
1: br %5, label bb3, label bb4
bb3 (then):
1: %8: Integer = const 100 ← Const命令Copy不使用
1: br label bb5
bb4 (else):
1: %11: Integer = const 200 ← Const命令Copy不使用
1: br label bb5
bb5 (merge):
1: %12 = phi [%8, bb3], [%11, bb4] ← PHI命令が存在
1: ret %12
}
```
**構造差分**:
| 要素 | ユニットテスト想定 | 実MIR | 差分の意味 |
|------|------------------|-------|----------|
| Then/Else命令 | `Copy { dst: x, src: 100 }` | `Const { dst: %8, value: 100 }` | Copyではなく直接Const |
| Merge命令 | **空(なし)** | **`Phi { dst: %12, inputs: [...] }`** | PHI命令が存在 |
| Return値 | `Return x`then/elseで書き込んだ変数 | `Return %12`PHI結果の新しいValueId | 異なるValueId |
### 2.3 Phase 33-9.2の修正との比較
**Phase 33-9.2で修正したSimple pattern**:
```rust
// 修正内容: Const/Copyを許容
fn is_side_effect_free(&self, instructions: &[MirInstruction]) -> bool {
instructions.iter().all(|inst| {
matches!(inst, MirInstruction::Const { .. } | MirInstruction::Copy { .. })
})
}
```
**Simple patternの実MIR**:
```mir
bb3 (then):
%8: Integer = const 100
ret %8 ← Return命令merge blockなし
bb4 (else):
%11: Integer = const 200
ret %11 ← Return命令merge blockなし
```
**重要な違い**:
- **Simple**: merge blockなし → return直接
- **Local**: merge blockあり → **PHI命令で合流**
---
## 3. PHI命令の妥当性検証
### 3.1 SSA形式の正しさ
**実MIRのPHI構造は完全に正しい**:
1. **定義Definition**:
- SSA形式では、複数の前任ブロックから値が流入する場合、PHI命令で合流させる
- これが**唯一の正規的な表現**
2. **実MIRの構造**:
```mir
bb5 (merge):
%12 = phi [%8, bb3], [%11, bb4]
```
- bb3から%8が流入
- bb4から%11が流入
- bb5では%12として合流
- **完璧なSSA形式**
3. **検証**:
- ✅ 各ValueIdは一度だけ定義されるSingle Assignment
- ✅ 前任ブロックごとの値が明確Static Single
- ✅ PHI配置は支配フロンティアに従っている
### 3.2 PHI命令の配置
**標準的なPHI配置規則**:
- PHI命令はmerge blockの**先頭**に配置される
- Return/Jump等の終端命令の**前**に配置される
**実MIRの配置**:
```mir
bb5:
1: %12 = phi [%8, bb3], [%11, bb4] ← 先頭に配置
1: ret %12 ← 終端命令
```
✅ **完全に標準的な配置**
---
## 4. JoinIR変換の必要性分析
### 4.1 JoinIR Select/IfMergeの役割
**設計文書より** (if_joinir_design.md):
```rust
/// Phase 33.1: 単純な値選択
Select {
dst: VarId,
cond: VarId,
then_val: VarId,
else_val: VarId,
}
```
**Select命令の意味論**:
- 条件に応じて値を選択する
- **MIR reverse loweringでPHI命令を生成する**(重要!)
### 4.2 PHI → Select変換の問題点
**仮にPHI → Selectを実装した場合**:
```
MIR (入力):
bb5: %12 = phi [%8, bb3], [%11, bb4]
↓ lowering (MIR → JoinIR)
JoinIR:
Select { dst: %12, cond: ???, then_val: %8, else_val: %11 }
↓ reverse lowering (JoinIR → MIR)
MIR (出力):
bb5: %12 = phi [%8, bb3], [%11, bb4] ← 元に戻る
```
**問題**:
1. **情報損失**: PHI → Select時に条件式が不明%5はbb0で消費済み
2. **無意味な往復**: MIR → JoinIR → MIR で同じPHIに戻る
3. **責務の逆転**: JoinIRの役割は「PHI生成」であり、「PHI変換」ではない
### 4.3 JoinIR変換が有意義なケース
**唯一有意義なのは「PHI命令がまだ生成されていない場合」**:
```
高レベルAST:
if cond { x = 100 } else { x = 200 }; return x
↓ (従来の方式: if_phi.rs経由)
MIR:
bb5: %12 = phi [%8, bb3], [%11, bb4]
ret %12
↓ (新しい方式: JoinIR経由)
JoinIR:
Select { dst: %12, cond: %5, then_val: %8, else_val: %11 }
↓ reverse lowering
MIR:
bb5: %12 = phi [%8, bb3], [%11, bb4]
ret %12
```
**結果**: 最終MIRは同じだが、生成経路が異なる
**価値**:
- ✅ if_phi.rsの削除が可能コード削減
- ✅ JoinIR側で型検証が可能
- ❌ 既にPHIがある場合は意味なし
---
## 5. Phase 33の設計意図確認
### 5.1 設計文書の記述
**if_joinir_design.md:11-27より**:
```
1. If/Else の「値としての if」`if ... then ... else ...` の PHIを JoinIR 側で表現できるようにする。
- ループ PHI と同じく、「PHI を関数引数や join 関数の引数に押し出す」設計に寄せる。
- MIR Builder 側の if_phi.rs / conservative.rs / phi_invariants.rs に依存しない JoinIR lowering を用意する。
```
**重要な前提**:
- **「MIR Builder側のif_phi.rsに依存しない」** = if_phi.rsを**使わずに**PHIを生成する
- **PHI生成の新しい経路**としてJoinIRを使う
### 5.2 if_phi.rsの役割
**現状のif_phi.rs**:
- ASTから直接PHI命令を生成MIR Builder層
- if/else構造を解析してPHI配置を決定
- 316行のロジック
**JoinIR経由の場合**:
- ASTからJoinIR Select/IfMergeを生成
- JoinIR → MIR reverse lowering時にPHI命令を生成
- if_phi.rsは**削除可能**
### 5.3 Phase 33のターゲット
**Phase 33-10の実装対象**:
```
入力: ASTまだPHI生成されていない
出力: MIRPHI命令を含む
経路選択:
Route A従来: AST → if_phi.rs → MIRPHI
Route B新規: AST → JoinIR lowering → JoinIR Select → reverse lowering → MIRPHI
```
**Phase 33-10が**扱うべきでない**ケース**:
```
入力: MIR既にPHI命令が存在
出力: ???(何に変換する意味がある?)
```
---
## 6. Phase 33-10実装方針
### 6.1 推奨方針: Option APHI命令を変換対象外とする
**実装内容**:
```rust
// src/mir/join_ir/lowering/if_select.rs
fn try_match_local_pattern(
&self,
func: &MirFunction,
branch: &IfBranch,
then_block: &crate::mir::BasicBlock,
else_block: &crate::mir::BasicBlock,
) -> Option<IfPattern> {
// Phase 33-10: PHI命令が既に存在する場合は対象外
// JoinIRの役割はPHI生成であり、既存PHI変換ではない
// merge blockの取得
let merge_block_id = match then_block.terminator.as_ref()? {
MirInstruction::Jump { target } => *target,
_ => return None,
};
let merge_block = func.blocks.get(&merge_block_id)?;
// ❌ PHI命令がある場合は変換対象外重要
for inst in &merge_block.instructions {
if matches!(inst, MirInstruction::Phi { .. }) {
return None; // 既にPHIがあるので、JoinIR変換不要
}
}
// ✅ PHI命令がない場合のみ、local patternとして処理
// (この場合は現在の実装で正しく動作する)
// ... 既存のロジック継続 ...
}
```
**効果**:
- ✅ PHI命令がある = 既にif_phi.rsで処理済み → JoinIR変換不要
- ✅ PHI命令がない = JoinIR経由で処理すべき → 現在の実装が機能
- ✅ 責務の明確化: JoinIRは「PHI生成器」として機能
### 6.2 Option BPHI → Select/IfMerge変換の問題点
**実装した場合の問題**:
1. **条件式の復元困難**:
```mir
bb0: br %5, label bb3, label bb4 ← %5はここで消費
...
bb5: %12 = phi [%8, bb3], [%11, bb4] ← %5にアクセスできない
```
2. **無意味な往復変換**:
- MIRPHI → JoinIRSelect → MIRPHI
- 何も変わらない
3. **Phase 33の意図に反する**:
- 目的: if_phi.rsを削除して、JoinIR経由でPHI生成
- 実際: 既にあるPHIを変換意味なし
### 6.3 Option C新しいパターン定義の不要性
**「PHI-based pattern」を定義する必要はない**:
理由:
1. PHIがある = 既にMIR Builderif_phi.rsで処理済み
2. JoinIRの役割は「**まだPHIがない**場合にPHI生成する」こと
3. 既存PHIを再処理するパターンは設計意図に反する
---
## 7. Simple PatternとLocal Patternの真の違い
### 7.1 Simple Pattern正しく動作
**構造**:
```mir
bb0: br cond, bb3, bb4
bb3 (then):
%8 = const 100
ret %8 ← 直接returnmerge blockなし
bb4 (else):
%11 = const 200
ret %11 ← 直接returnmerge blockなし
```
**特徴**:
- ❌ merge blockが存在しない
- ❌ PHI命令が不要各ブロックで直接return
- ✅ JoinIR Selectで表現可能
### 7.2 Local Pattern既にPHI生成済み
**構造**:
```mir
bb0: br cond, bb3, bb4
bb3 (then):
%8 = const 100
br bb5 ← mergeへジャンプ
bb4 (else):
%11 = const 200
br bb5 ← mergeへジャンプ
bb5 (merge):
%12 = phi [%8, bb3], [%11, bb4] ← PHI命令既に生成済み
ret %12
```
**特徴**:
- ✅ merge blockが存在
- ✅ **PHI命令が既に存在**if_phi.rsで生成済み
- ❌ JoinIR変換の必要なし既にSSA形式完成
### 7.3 真のLocal PatternJoinIR変換すべきケース
**仮想的な構造**(実際には生成されない):
```mir
bb0: br cond, bb3, bb4
bb3 (then):
Store { ptr: &x, value: 100 } ← PHIではなくStore
br bb5
bb4 (else):
Store { ptr: &x, value: 200 } ← PHIではなくStore
br bb5
bb5 (merge):
[空 - 命令なし] ← PHIなし
%v = Load { ptr: &x } ← 値の読み込み
ret %v
```
**現実**:
- このような構造は**MIR Builderが生成しない**
- Nyashは常にSSA形式 → 必ずPHI命令を使用
- したがって、try_match_local_pattern()の想定自体が**実装されないケース**を前提にしている
---
## 8. Phase 33全体への影響
### 8.1 Simple PatternPhase 33-9.2
**現状**: ✅ 完全動作
- Const/Copy許容で100%成功
- PHI命令なし → JoinIR変換が有意義
### 8.2 Local PatternPhase 33-10
**結論**: ⚠️ **Phase 33-10の対象外**
- PHI命令あり → 既にif_phi.rsで処理完了
- JoinIR変換は不要無意味な往復
**推奨**:
- try_match_local_pattern()を**削除**するか、
- PHI命令チェックを追加して早期return
### 8.3 IfMerge Pattern
**Phase 33-6の設計との整合性**:
IfMerge設計if_joinir_design.md:748-823:
```rust
JoinInst::IfMerge {
cond: ValueId,
merges: Vec<MergePair>, // 複数のPHI相当
}
```
**重要**: IfMergeも「PHI生成」が目的
- 入力: AST複数変数代入
- 出力: MIR複数PHI命令
- **既存PHI → IfMerge変換ではない**
---
## 9. 最終推奨事項
### 9.1 Phase 33-10の実装方針
**推奨: Option APHI命令を対象外とする**
```rust
// Phase 33-10: 実装修正
fn try_match_local_pattern(...) -> Option<IfPattern> {
// Step 1: merge blockの取得
let merge_block_id = ...;
let merge_block = func.blocks.get(&merge_block_id)?;
// Step 2: PHI命令チェック追加
if merge_block.instructions.iter().any(|inst| {
matches!(inst, MirInstruction::Phi { .. })
}) {
// PHI命令がある = if_phi.rsで処理済み → JoinIR変換不要
return None;
}
// Step 3: 既存のロジック継続
// PHI命令がない場合のみ到達
// ...
}
```
### 9.2 Phase 33-10の完了判定
**完了条件**:
1. ✅ try_match_local_pattern()にPHIチェック追加
2. ✅ 既存ユニットテスト維持PHI命令なしケース
3. ✅ 実用MIRPHI命令ありは正しくフォールバック
4. ✅ ドキュメント更新(本レポート)
**非目標**:
- ❌ PHI → Select変換実装不要
- ❌ local patternの実用MIR対応既にif_phi.rsで処理済み
### 9.3 Phase 33全体の戦略修正
**修正前の理解**:
- Simple/Local/IfMerge の3パターンをJoinIRで実装 → if_phi.rs削除
**修正後の理解**:
- **Simple**: PHI不要 → JoinIR変換有意義 ✅
- **Local**: PHI既存 → JoinIR変換不要if_phi.rs保持 ⚠️
- **IfMerge**: 複数PHI → AST→JoinIR経路でのみ有意義 ✅
**結論**:
- if_phi.rsの完全削除は**Phase 34以降に延期**
- Phase 33は「PHI生成前の経路にJoinIR挿入」に集中
- 既存PHIの再処理は無意味 → 実装しない
---
## 10. 参考資料
### 10.1 実MIR出力完全版
```mir
; MIR Module: main
; Source: /tmp/test_phi_local.hako
define i64 @main() {
bb0:
1: %3: Integer = const 1
1: %4: Integer = copy %3
1: %5: Integer = copy %4
1: br %5, label bb3, label bb4
bb3:
1: %8: Integer = const 100
1: br label bb5
bb4:
1: %11: Integer = const 200
1: br label bb5
bb5:
1: %12 = phi [%8, bb3], [%11, bb4]
1: ret %12
}
```
### 10.2 Simple Pattern MIRPhase 33-9.2
```mir
define i64 @IfSelectTest.test/1(i64 %0) {
bb2:
br %3, label bb3, label bb4
bb3:
%4: Integer = const 10
ret %4
bb4:
%6: Integer = const 20
ret %6
}
```
### 10.3 関連ファイル
- `src/mir/join_ir/lowering/if_select.rs:201-273` - try_match_local_pattern()実装
- `docs/private/roadmap2/phases/phase-33-joinir-if-phi-cleanup/if_joinir_design.md` - 設計文書
- `src/mir/builder/if_phi.rs` - 現行PHI生成ロジック316行
---
## 11. ChatGPT実装者への提言
### 11.1 実装不要の理由
**結論**: Phase 33-10で local pattern のMIR変換実装は**不要**
理由:
1. 実MIRは既に正しいPHI命令を含む
2. JoinIRの役割は「PHI生成」であり「PHI変換」ではない
3. 既存のif_phi.rsが正しく動作している
4. 無意味な往復変換を避けるべき
### 11.2 実装すべき内容
**唯一実装すべきこと**: PHI命令チェックの追加
```rust
// Phase 33-10: 5行の追加のみ
if merge_block.instructions.iter().any(|inst| {
matches!(inst, MirInstruction::Phi { .. })
}) {
return None; // PHI命令がある場合は対象外
}
```
**効果**:
- ✅ 責務の明確化
- ✅ 無駄な処理の防止
- ✅ 設計意図との整合性
### 11.3 Phase 33-10完了判定
**完了条件(簡略化)**:
1. ✅ PHIチェック追加5行
2. ✅ 既存テスト維持
3. ✅ ドキュメント更新
**作業時間**: 30分以内
---
**Phase 33-10完了判定**: PHIチェック追加のみで完了とする
**次のフェーズ**: Phase 33-11IfMerge実装、またはPhase 34へ移行