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>
This commit is contained in:
16
CLAUDE.md
16
CLAUDE.md
@ -55,6 +55,22 @@ done
|
|||||||
- **解決**: `BTreeSet`/`BTreeMap`で決定的イテレーション保証
|
- **解決**: `BTreeSet`/`BTreeMap`で決定的イテレーション保証
|
||||||
- **残課題**: `variable_map: HashMap<String, ValueId>` (builder.rs等)
|
- **残課題**: `variable_map: HashMap<String, ValueId>` (builder.rs等)
|
||||||
|
|
||||||
|
### 🎊 **Phase 33-10完了!JoinIR If Lowering箱化モジュール化達成** (2025-11-27)
|
||||||
|
- **✅ 箱化モジュール化完了**: if_dry_runner.rs作成(176行)、vm.rs 83行→9行(89%削減)
|
||||||
|
- **✅ PHI guard実装**: 早期PHIチェック(パターンマッチング前)でJoinIR設計原則確立
|
||||||
|
- **✅ Local pattern対応**: Const命令許容(実用MIR完全対応)
|
||||||
|
- **✅ 設計原則確立**: JoinIR = PHI生成器(SSOT)、PHI変換器ではない
|
||||||
|
- **テスト結果**:
|
||||||
|
- Simple pattern: ✅ Lowered=1 (100%) - 後方互換性維持
|
||||||
|
- Local pattern: ✅ Lowered=0 (0%) with "PHI already exists, skipping" - 設計原則動作
|
||||||
|
- **修正ファイル**:
|
||||||
|
- `if_dry_runner.rs`: 新規作成(+176行)
|
||||||
|
- `if_select.rs`: PHI guard追加(+50行)
|
||||||
|
- `vm.rs`: 箱化で大幅削減(-74行)
|
||||||
|
- `if_joinir_design.md`: Section 13追加(+113行)
|
||||||
|
- **Phase 33完了判定基準**: ✅ 達成(Simple pattern動作、箱化完成、設計原則確立)
|
||||||
|
- **次のステップ**: Phase 34移行 or IfMerge実装継続
|
||||||
|
|
||||||
### 🎉 **Phase 25 MVP 完全成功!** (2025-11-15)
|
### 🎉 **Phase 25 MVP 完全成功!** (2025-11-15)
|
||||||
- **numeric_core BoxCall→Call変換** 完全動作!
|
- **numeric_core BoxCall→Call変換** 完全動作!
|
||||||
- **2つの重大バグ修正**:
|
- **2つの重大バグ修正**:
|
||||||
|
|||||||
@ -439,7 +439,7 @@
|
|||||||
|
|
||||||
**次のステップ(Phase 34-5 以降)**
|
**次のステップ(Phase 34-5 以降)**
|
||||||
- Phase 34-5: Method 呼び出し/Var 対応(`extract_value` 汎用化)
|
- Phase 34-5: Method 呼び出し/Var 対応(`extract_value` 汎用化)
|
||||||
- Phase 34-6: Loop/Break/Continue の AST→JoinIR 対応
|
- Phase 34-6: MethodCall 構造の JoinIR への明示 + JoinIR→MIR 変換側での Method/Call 意味論実装
|
||||||
|
|
||||||
**実装ファイル**
|
**実装ファイル**
|
||||||
- `src/mir/join_ir/frontend/ast_lowerer.rs` (match 分岐 1行追加、68行目)
|
- `src/mir/join_ir/frontend/ast_lowerer.rs` (match 分岐 1行追加、68行目)
|
||||||
@ -496,7 +496,7 @@
|
|||||||
**技術的意義**
|
**技術的意義**
|
||||||
- **段階的移行成功**: Int ダミー → 本物の式(Var)への移行完了
|
- **段階的移行成功**: Int ダミー → 本物の式(Var)への移行完了
|
||||||
- **拡張性向上**: `extract_value` 統一により、今後の expr 拡張が容易
|
- **拡張性向上**: `extract_value` 統一により、今後の expr 拡張が容易
|
||||||
- **Method 準備完了**: pattern match 実装済み(Phase 34-6 への準備)
|
- **Method 準備完了**: 構造レベルの pattern match 実装済み(JoinIR→MIR 側での Method/Call 意味論実装=Phase 34-6 への準備)
|
||||||
|
|
||||||
**ガードレール遵守**
|
**ガードレール遵守**
|
||||||
- ✅ Phase 34 frontend テスト専用(既定経路不変)
|
- ✅ Phase 34 frontend テスト専用(既定経路不変)
|
||||||
@ -504,7 +504,7 @@
|
|||||||
- ✅ 未対応パターンは panic(tiny テスト専用で合理的)
|
- ✅ 未対応パターンは panic(tiny テスト専用で合理的)
|
||||||
|
|
||||||
**次のステップ(Phase 34-6 以降)**
|
**次のステップ(Phase 34-6 以降)**
|
||||||
- Phase 34-6: Loop/Break/Continue の AST→JoinIR 対応
|
- Phase 34-6: MethodCall 構造の JoinIR への明示 + JoinIR→MIR 変換での Method/Call 意味論実装
|
||||||
|
|
||||||
**実装ファイル**
|
**実装ファイル**
|
||||||
- `src/mir/join_ir/frontend/ast_lowerer.rs` (ExtractCtx + extract_value + リファクタリング、~90 lines 追加)
|
- `src/mir/join_ir/frontend/ast_lowerer.rs` (ExtractCtx + extract_value + リファクタリング、~90 lines 追加)
|
||||||
@ -515,6 +515,68 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
### 1-00k. Phase 34-6 — MethodCall 構造と本物の substring 意味論(**完了** 2025-11-27)
|
||||||
|
|
||||||
|
**Phase 34-6 の目的**
|
||||||
|
- MethodCall 構造を JoinIR に明示的に追加
|
||||||
|
- JoinIR→MIR 変換で Method 呼び出し意味論を実装
|
||||||
|
- `JsonShapeToMap._read_value_from_pair/1` の本物の substring 呼び出しを通す
|
||||||
|
|
||||||
|
**実装内容**
|
||||||
|
1. **MethodCall 構造追加** (34-6.1)
|
||||||
|
- `src/mir/join_ir/mod.rs` に `JoinInst::MethodCall` バリアント追加
|
||||||
|
- 構造: `{ dst, receiver, method, args }`
|
||||||
|
- コメント: "Phase 34-6: メソッド呼び出し構造。意味論は JoinIR→MIR ブリッジで実装"
|
||||||
|
|
||||||
|
2. **extract_value 更新** (34-6.2)
|
||||||
|
- `ast_lowerer.rs` の Method 処理を本物の MethodCall 生成に変更
|
||||||
|
- receiver/args を extract_value で再帰的に処理
|
||||||
|
- ダミー Const(0) → 本物の MethodCall 構造
|
||||||
|
|
||||||
|
3. **JoinIR→MIR 変換実装** (34-6.3)
|
||||||
|
- `join_ir_vm_bridge.rs` に MethodCall → BoxCall 変換追加
|
||||||
|
- `join_ir/json.rs` に MethodCall JSON シリアライゼーション追加
|
||||||
|
- `join_ir_runner.rs` に MethodCall 未対応エラー追加(JoinIR Runner は使用しない)
|
||||||
|
|
||||||
|
4. **テスト更新** (34-6.4)
|
||||||
|
- フィクスチャ更新: Int 10/20 → 本物の substring 呼び出し
|
||||||
|
- テスト更新: `run_joinir_function` → `run_joinir_via_vm`(JoinIR→MIR→VM ブリッジ)
|
||||||
|
- 検証: v="hello", at=3 → "hel" / v="world", at=0 → "world"
|
||||||
|
- **cond 処理修正**: ValueId(0) ハードコード → extract_value で取得
|
||||||
|
|
||||||
|
5. **ドキュメント更新** (34-6.5)
|
||||||
|
- README/TASKS/CURRENT_TASK 更新
|
||||||
|
|
||||||
|
**成果**
|
||||||
|
- ✅ テスト全通過(1 passed; 0 failed)
|
||||||
|
- `joinir_frontend_json_shape_read_value_ab_test`: 本物の substring 呼び出し成功
|
||||||
|
- ✅ **MethodCall 構造**: JoinIR で Method 呼び出しを明示的に表現
|
||||||
|
- ✅ **意味論分離**: JoinIR = 構造 SSOT、JoinIR→MIR = 意味論実装
|
||||||
|
- ✅ **VM 実行**: 既存の StringBox.substring が正しく呼ばれる
|
||||||
|
|
||||||
|
**技術的意義**
|
||||||
|
- **設計原則確立**: JoinIR は構造のみ保持、意味論は MIR レベルで実装
|
||||||
|
- **Phase 33-10 原則との整合性**: "JoinIR = PHI 生成器" と同じ原則を Method にも適用
|
||||||
|
- **拡張性**: 今後の Method 種類追加が容易(MethodCall → BoxCall/Call 変換の1箇所で対応)
|
||||||
|
|
||||||
|
**修正ファイル**
|
||||||
|
- `src/mir/join_ir/mod.rs` (MethodCall バリアント +8 lines)
|
||||||
|
- `src/mir/join_ir/frontend/ast_lowerer.rs` (Method 処理 +37 lines, cond 処理修正)
|
||||||
|
- `src/mir/join_ir_vm_bridge.rs` (MethodCall → BoxCall 変換 +12 lines, dst 型修正)
|
||||||
|
- `src/mir/join_ir/json.rs` (MethodCall JSON +16 lines)
|
||||||
|
- `src/mir/join_ir_runner.rs` (MethodCall 未対応 +7 lines)
|
||||||
|
- `src/tests/joinir_frontend_if_select.rs` (import 更新, run_joinir_via_vm 使用)
|
||||||
|
- `docs/.../fixtures/json_shape_read_value.program.json` (本物の substring 構造)
|
||||||
|
|
||||||
|
**更新ドキュメント**
|
||||||
|
- `docs/private/roadmap2/phases/phase-34-joinir-frontend/README.md` (Phase 34-6 セクション)
|
||||||
|
- `docs/private/roadmap2/phases/phase-34-joinir-frontend/TASKS.md` (34-6.1 〜 34-6.5 完了)
|
||||||
|
|
||||||
|
**次のステップ(Phase 34-7 以降)**
|
||||||
|
- Phase 34-7: Loop/Break/Continue の AST→JoinIR 対応
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
### 1-00u. Phase 32 L-4.3a — llvmlite ハーネスでの JoinIR 実験(**完了** 2025-11-26)
|
### 1-00u. Phase 32 L-4.3a — llvmlite ハーネスでの JoinIR 実験(**完了** 2025-11-26)
|
||||||
|
|
||||||
**目的**
|
**目的**
|
||||||
|
|||||||
@ -0,0 +1,291 @@
|
|||||||
|
# Phase 33-10: 実装推奨事項(5分で完了)
|
||||||
|
|
||||||
|
**作成日**: 2025-11-27
|
||||||
|
**前提**: [phase33-10-local-pattern-mir-analysis.md](phase33-10-local-pattern-mir-analysis.md) の分析完了
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. 実装すべき内容(たった5行)
|
||||||
|
|
||||||
|
### 1.1 ファイル: `src/mir/join_ir/lowering/if_select.rs`
|
||||||
|
|
||||||
|
**修正箇所**: Line 250-252(merge blockの命令チェック部分)
|
||||||
|
|
||||||
|
**修正前**:
|
||||||
|
```rust
|
||||||
|
// merge ブロックが「return dst」だけか確認
|
||||||
|
let merge_block = func.blocks.get(&merge_block_id)?;
|
||||||
|
match merge_block.terminator.as_ref()? {
|
||||||
|
MirInstruction::Return {
|
||||||
|
value: Some(v),
|
||||||
|
} if *v == dst_then => {
|
||||||
|
// OK
|
||||||
|
}
|
||||||
|
_ => return None,
|
||||||
|
}
|
||||||
|
|
||||||
|
if !merge_block.instructions.is_empty() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**修正後**:
|
||||||
|
```rust
|
||||||
|
// merge ブロックが「return dst」だけか確認
|
||||||
|
let merge_block = func.blocks.get(&merge_block_id)?;
|
||||||
|
|
||||||
|
// Phase 33-10: PHI命令が既に存在する場合は対象外
|
||||||
|
// (JoinIRの役割はPHI生成であり、既存PHI変換ではない)
|
||||||
|
if merge_block.instructions.iter().any(|inst| {
|
||||||
|
matches!(inst, MirInstruction::Phi { .. })
|
||||||
|
}) {
|
||||||
|
return None; // 既にPHIがあるので、JoinIR変換不要
|
||||||
|
}
|
||||||
|
|
||||||
|
match merge_block.terminator.as_ref()? {
|
||||||
|
MirInstruction::Return {
|
||||||
|
value: Some(v),
|
||||||
|
} if *v == dst_then => {
|
||||||
|
// OK
|
||||||
|
}
|
||||||
|
_ => return None,
|
||||||
|
}
|
||||||
|
|
||||||
|
if !merge_block.instructions.is_empty() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**追加行数**: 5行(コメント含む)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. 動作確認
|
||||||
|
|
||||||
|
### 2.1 ユニットテスト(既存)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo test --release test_if_select_pattern_matching
|
||||||
|
```
|
||||||
|
|
||||||
|
**期待結果**: ✅ PASS(PHI命令なしケースは引き続き動作)
|
||||||
|
|
||||||
|
### 2.2 実用MIR(PHI命令あり)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# テストケース作成
|
||||||
|
cat > /tmp/test_phi_local.hako << 'EOF'
|
||||||
|
static box Main {
|
||||||
|
main() {
|
||||||
|
local x
|
||||||
|
local cond
|
||||||
|
cond = 1
|
||||||
|
|
||||||
|
if cond {
|
||||||
|
x = 100
|
||||||
|
} else {
|
||||||
|
x = 200
|
||||||
|
}
|
||||||
|
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# MIR確認
|
||||||
|
NYASH_PARSER_STAGE3=1 HAKO_PARSER_STAGE3=1 NYASH_VM_DUMP_MIR=1 \
|
||||||
|
./target/release/hakorune /tmp/test_phi_local.hako 2>&1 | grep -A 20 "define i64 @main"
|
||||||
|
```
|
||||||
|
|
||||||
|
**期待結果**:
|
||||||
|
```mir
|
||||||
|
bb5:
|
||||||
|
1: %12 = phi [%8, bb3], [%11, bb4] ← PHI命令が存在
|
||||||
|
1: ret %12
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.3 JoinIR lowering(dry-run)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Phase 33-9.2のdry-run統合を使用
|
||||||
|
NYASH_PARSER_STAGE3=1 HAKO_PARSER_STAGE3=1 NYASH_JOINIR_IF_SELECT=1 \
|
||||||
|
NYASH_JOINIR_DEBUG=1 ./target/release/hakorune /tmp/test_phi_local.hako 2>&1 | \
|
||||||
|
grep -E "\[joinir|IfSelectLowerer\]"
|
||||||
|
```
|
||||||
|
|
||||||
|
**期待ログ**:
|
||||||
|
```
|
||||||
|
[IfSelectLowerer] ❌ no pattern matched ← PHIチェックで正しくフォールバック
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. 完了判定
|
||||||
|
|
||||||
|
### 3.1 チェックリスト
|
||||||
|
|
||||||
|
- [ ] PHIチェック追加(5行)
|
||||||
|
- [ ] ビルド成功(`cargo build --release`)
|
||||||
|
- [ ] ユニットテスト PASS(7テスト)
|
||||||
|
- [ ] 実用MIR dry-run確認(フォールバック動作)
|
||||||
|
- [ ] コミットメッセージ作成
|
||||||
|
|
||||||
|
### 3.2 コミットメッセージ例
|
||||||
|
|
||||||
|
```
|
||||||
|
feat(phase33-10): Add PHI instruction guard to try_match_local_pattern
|
||||||
|
|
||||||
|
Phase 33-10 完了: local patternのPHI命令チェック追加
|
||||||
|
|
||||||
|
実装内容:
|
||||||
|
- try_match_local_pattern()にPHI命令チェック追加(5行)
|
||||||
|
- 既にPHI命令がある場合はJoinIR変換対象外として早期return
|
||||||
|
- JoinIRの役割は「PHI生成」であり「既存PHI変換」ではない
|
||||||
|
|
||||||
|
効果:
|
||||||
|
- 責務の明確化: JoinIRは「PHI生成器」として機能
|
||||||
|
- 無駄な処理の防止: 既存PHI → Select → PHI の往復変換を回避
|
||||||
|
- 設計意図との整合性: Phase 33の目的に沿った実装
|
||||||
|
|
||||||
|
検証:
|
||||||
|
- ユニットテスト: 全7テスト PASS ✅
|
||||||
|
- 実用MIR: PHI命令ありケースを正しくフォールバック ✅
|
||||||
|
- dry-run: Phase 33-9.2統合で動作確認済み ✅
|
||||||
|
|
||||||
|
参考:
|
||||||
|
- docs/development/current/main/phase33-10-local-pattern-mir-analysis.md
|
||||||
|
- docs/private/roadmap2/phases/phase-33-joinir-if-phi-cleanup/if_joinir_design.md
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Phase 33-10後の状況
|
||||||
|
|
||||||
|
### 4.1 パターンカバレッジ(更新)
|
||||||
|
|
||||||
|
| パターン | 対応状況 | 備考 |
|
||||||
|
|---------|---------|------|
|
||||||
|
| Simple pattern | ✅ 完全対応 | Phase 33-9.2で実装完了 |
|
||||||
|
| Local pattern | ⚠️ **対象外** | **PHI命令ありは if_phi.rs が処理** |
|
||||||
|
| IfMerge pattern | 🔄 未実装 | Phase 33-11以降で検討 |
|
||||||
|
|
||||||
|
### 4.2 レガシー箱削除の見通し
|
||||||
|
|
||||||
|
**Phase 33-10完了後**:
|
||||||
|
- `if_phi.rs` (316行): **保持**(local patternで必要)
|
||||||
|
- `phi_invariants.rs` (92行): **保持**(PHI検証で必要)
|
||||||
|
- `conservative.rs` (169行): **保持**(複数変数PHIで必要)
|
||||||
|
|
||||||
|
**完全削除の条件**(Phase 34以降):
|
||||||
|
1. AST → JoinIR 経路の確立(PHI生成前にJoinIR変換)
|
||||||
|
2. MIR Builder本線での if_phi 経路削除
|
||||||
|
3. Stage-1/Stage-B での本格実証
|
||||||
|
|
||||||
|
**Phase 33のスコープ修正**:
|
||||||
|
- ❌ 当初目標: if_phi.rs完全削除
|
||||||
|
- ✅ 修正後目標: JoinIR経路の実証(if_phi.rs保持)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. 次のフェーズ選択肢
|
||||||
|
|
||||||
|
### Option A: Phase 33-11(IfMerge実装)
|
||||||
|
|
||||||
|
**目的**: 複数変数PHIのJoinIR表現
|
||||||
|
|
||||||
|
**条件**:
|
||||||
|
- IfMerge設計(Phase 33-6.1)完了済み
|
||||||
|
- Runner/Bridge実装が必要(推定4-6h)
|
||||||
|
|
||||||
|
**効果**:
|
||||||
|
- 複数変数 if/else の JoinIR 表現
|
||||||
|
- conservative.rs の一部機能移譲
|
||||||
|
|
||||||
|
### Option B: Phase 34(AST→JoinIR経路)
|
||||||
|
|
||||||
|
**目的**: MIR Builder本線でのJoinIR統合
|
||||||
|
|
||||||
|
**条件**:
|
||||||
|
- Select/IfMerge両方実装完了
|
||||||
|
- Stage-1/Stage-B実証済み
|
||||||
|
|
||||||
|
**効果**:
|
||||||
|
- if_phi.rs の実際の削除
|
||||||
|
- ~600行削減達成
|
||||||
|
|
||||||
|
### Option C: Phase 33完了判定
|
||||||
|
|
||||||
|
**判断基準**:
|
||||||
|
- Simple pattern: ✅ 完全動作(Phase 33-9.2)
|
||||||
|
- Local pattern: ✅ 責務明確化(Phase 33-10)
|
||||||
|
- IfMerge: ⚠️ 未実装だが、基盤は整った
|
||||||
|
|
||||||
|
**完了宣言の条件**:
|
||||||
|
- 技術的基盤整備完了 ✅
|
||||||
|
- 実コード実証(simple pattern)完了 ✅
|
||||||
|
- 設計意図の明確化完了 ✅
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. 推奨アクション
|
||||||
|
|
||||||
|
### 6.1 即座に実施(今日)
|
||||||
|
|
||||||
|
1. **PHIチェック追加**(5分)
|
||||||
|
2. **テスト実行**(2分)
|
||||||
|
3. **コミット**(3分)
|
||||||
|
4. **ドキュメント更新**(既に完了)
|
||||||
|
|
||||||
|
**合計時間**: 10分
|
||||||
|
|
||||||
|
### 6.2 短期(今週)
|
||||||
|
|
||||||
|
**Phase 33完了判定会議**:
|
||||||
|
- 現状確認: Simple完了、Local責務明確化
|
||||||
|
- 判断: Phase 34移行 vs IfMerge実装
|
||||||
|
- 記録: Phase 33 完了報告書作成
|
||||||
|
|
||||||
|
### 6.3 中期(来週以降)
|
||||||
|
|
||||||
|
**選択肢A**: IfMerge実装(Phase 33-11)
|
||||||
|
- 複数変数PHI対応
|
||||||
|
- conservative.rs機能移譲
|
||||||
|
|
||||||
|
**選択肢B**: Phase 34移行
|
||||||
|
- AST→JoinIR経路確立
|
||||||
|
- if_phi.rs削除
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. まとめ
|
||||||
|
|
||||||
|
### 7.1 Phase 33-10の成果
|
||||||
|
|
||||||
|
**達成**:
|
||||||
|
- ✅ local patternの実MIR構造解明
|
||||||
|
- ✅ JoinIR変換の責務明確化
|
||||||
|
- ✅ PHI命令チェック実装(5行)
|
||||||
|
- ✅ 設計意図の再確認
|
||||||
|
|
||||||
|
**学び**:
|
||||||
|
- PHI命令ありMIRはJoinIR変換対象外
|
||||||
|
- JoinIRの役割は「PHI生成」であり「PHI変換」ではない
|
||||||
|
- if_phi.rsは現時点で必要な機能
|
||||||
|
|
||||||
|
### 7.2 Phase 33全体の再評価
|
||||||
|
|
||||||
|
**技術的成功**:
|
||||||
|
- ✅ Select命令実装・実証完了
|
||||||
|
- ✅ JoinIR経路の動作確認済み
|
||||||
|
- ✅ 責務分離の明確化完了
|
||||||
|
|
||||||
|
**スコープ調整**:
|
||||||
|
- ❌ if_phi.rs完全削除(当初目標)
|
||||||
|
- ✅ JoinIR経路確立(修正後目標)
|
||||||
|
|
||||||
|
**Phase 33完了判定**: 技術的基盤整備は**完了**とみなせる
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**作業開始**: PHIチェック5行追加 → 10分で完了
|
||||||
|
**次のステップ**: Phase 33完了判定会議 → Phase 34移行検討
|
||||||
@ -0,0 +1,630 @@
|
|||||||
|
# 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生成されていない)
|
||||||
|
出力: MIR(PHI命令を含む)
|
||||||
|
|
||||||
|
経路選択:
|
||||||
|
Route A(従来): AST → if_phi.rs → MIR(PHI)
|
||||||
|
Route B(新規): AST → JoinIR lowering → JoinIR Select → reverse lowering → MIR(PHI)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Phase 33-10が**扱うべきでない**ケース**:
|
||||||
|
```
|
||||||
|
入力: MIR(既にPHI命令が存在)
|
||||||
|
出力: ???(何に変換する意味がある?)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Phase 33-10実装方針
|
||||||
|
|
||||||
|
### 6.1 推奨方針: Option A(PHI命令を変換対象外とする)
|
||||||
|
|
||||||
|
**実装内容**:
|
||||||
|
|
||||||
|
```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 B(PHI → Select/IfMerge変換)の問題点
|
||||||
|
|
||||||
|
**実装した場合の問題**:
|
||||||
|
|
||||||
|
1. **条件式の復元困難**:
|
||||||
|
```mir
|
||||||
|
bb0: br %5, label bb3, label bb4 ← %5はここで消費
|
||||||
|
...
|
||||||
|
bb5: %12 = phi [%8, bb3], [%11, bb4] ← %5にアクセスできない
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **無意味な往復変換**:
|
||||||
|
- MIR(PHI) → JoinIR(Select) → MIR(PHI)
|
||||||
|
- 何も変わらない
|
||||||
|
|
||||||
|
3. **Phase 33の意図に反する**:
|
||||||
|
- 目的: if_phi.rsを削除して、JoinIR経由でPHI生成
|
||||||
|
- 実際: 既にあるPHIを変換(意味なし)
|
||||||
|
|
||||||
|
### 6.3 Option C(新しいパターン定義)の不要性
|
||||||
|
|
||||||
|
**「PHI-based pattern」を定義する必要はない**:
|
||||||
|
|
||||||
|
理由:
|
||||||
|
1. PHIがある = 既にMIR Builder(if_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 ← 直接return(merge blockなし)
|
||||||
|
|
||||||
|
bb4 (else):
|
||||||
|
%11 = const 200
|
||||||
|
ret %11 ← 直接return(merge 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 Pattern(JoinIR変換すべきケース)
|
||||||
|
|
||||||
|
**仮想的な構造**(実際には生成されない):
|
||||||
|
```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 Pattern(Phase 33-9.2)
|
||||||
|
|
||||||
|
**現状**: ✅ 完全動作
|
||||||
|
- Const/Copy許容で100%成功
|
||||||
|
- PHI命令なし → JoinIR変換が有意義
|
||||||
|
|
||||||
|
### 8.2 Local Pattern(Phase 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 A(PHI命令を対象外とする)**
|
||||||
|
|
||||||
|
```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. ✅ 実用MIR(PHI命令あり)は正しくフォールバック
|
||||||
|
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 MIR(Phase 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-11(IfMerge実装、またはPhase 34へ移行)
|
||||||
@ -79,7 +79,7 @@ impl AstToJoinIrLowerer {
|
|||||||
/// Program(JSON v0) → JoinModule
|
/// Program(JSON v0) → JoinModule
|
||||||
///
|
///
|
||||||
/// Phase 34-2/34-3/34-4: simple/local/json_shape pattern に対応
|
/// Phase 34-2/34-3/34-4: simple/local/json_shape pattern に対応
|
||||||
/// Phase 34-5: extract_value 統一化(Int/Var/Method 対応)
|
/// Phase 34-5: extract_value 統一化(Int/Var/Method 構造まで)
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
@ -112,7 +112,7 @@ impl AstToJoinIrLowerer {
|
|||||||
/// If Return pattern の共通 lowering
|
/// If Return pattern の共通 lowering
|
||||||
///
|
///
|
||||||
/// Phase 34-2/34-3/34-4: simple/local/json_shape 対応
|
/// Phase 34-2/34-3/34-4: simple/local/json_shape 対応
|
||||||
/// Phase 34-5: extract_value ベースに統一(Int/Var/Method 対応)
|
/// Phase 34-5: extract_value ベースに統一(Int/Var/Method 構造まで)
|
||||||
///
|
///
|
||||||
/// - simple: `if cond { return 10 } else { return 20 }`
|
/// - simple: `if cond { return 10 } else { return 20 }`
|
||||||
/// - local: `if cond { x=10 } else { x=20 }; return x` (意味論的)
|
/// - local: `if cond { x=10 } else { x=20 }; return x` (意味論的)
|
||||||
@ -192,23 +192,24 @@ impl AstToJoinIrLowerer {
|
|||||||
ctx.register_param(param_name, crate::mir::ValueId(i as u32));
|
ctx.register_param(param_name, crate::mir::ValueId(i as u32));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 6. then/else の expr を extract_value で処理
|
// Phase 34-6: cond/then/else の expr を extract_value で処理
|
||||||
let (then_var, mut then_insts) = self.extract_value(&then_ret["expr"], &mut ctx);
|
let (cond_var, cond_insts) = self.extract_value(&if_stmt["cond"], &mut ctx);
|
||||||
|
let (then_var, then_insts) = self.extract_value(&then_ret["expr"], &mut ctx);
|
||||||
let (else_var, else_insts) = self.extract_value(&else_ret["expr"], &mut ctx);
|
let (else_var, else_insts) = self.extract_value(&else_ret["expr"], &mut ctx);
|
||||||
|
|
||||||
// 7. Select 結果変数を割り当て
|
// 7. Select 結果変数を割り当て
|
||||||
let result_var = ctx.alloc_var();
|
let result_var = ctx.alloc_var();
|
||||||
|
|
||||||
// 8. JoinIR 命令列を組み立て
|
// 8. JoinIR 命令列を組み立て(cond → then → else → Select の順)
|
||||||
let mut insts = Vec::new();
|
let mut insts = Vec::new();
|
||||||
|
|
||||||
|
// cond の計算命令を先頭に追加
|
||||||
|
insts.extend(cond_insts);
|
||||||
|
|
||||||
// then/else の計算命令を追加
|
// then/else の計算命令を追加
|
||||||
insts.extend(then_insts);
|
insts.extend(then_insts);
|
||||||
insts.extend(else_insts);
|
insts.extend(else_insts);
|
||||||
|
|
||||||
// パラメータ: cond (VarId(0))
|
|
||||||
let cond_var = crate::mir::ValueId(0);
|
|
||||||
|
|
||||||
// Select: result = Select(cond, then_var, else_var)
|
// Select: result = Select(cond, then_var, else_var)
|
||||||
insts.push(JoinInst::Select {
|
insts.push(JoinInst::Select {
|
||||||
dst: result_var,
|
dst: result_var,
|
||||||
@ -315,27 +316,44 @@ impl AstToJoinIrLowerer {
|
|||||||
(var_id, vec![])
|
(var_id, vec![])
|
||||||
}
|
}
|
||||||
|
|
||||||
// 段階 2: Method 呼び出し対応(最小版 - pattern match のみ)
|
// Phase 34-6: Method 呼び出し構造の完全実装
|
||||||
"Method" => {
|
"Method" => {
|
||||||
// Phase 34-5: pattern match のみ実装
|
// receiver.method(args...) の構造を抽出
|
||||||
// 実際の JoinIR 生成は Phase 34-6 以降で対応
|
let receiver_expr = &expr["receiver"];
|
||||||
let method = expr["method"]
|
let method_name = expr["method"]
|
||||||
.as_str()
|
.as_str()
|
||||||
.expect("Method must have 'method' field");
|
.expect("Method must have 'method' field");
|
||||||
|
let args_array = expr["args"]
|
||||||
|
.as_array()
|
||||||
|
.expect("Method must have 'args' array");
|
||||||
|
|
||||||
// substring メソッドのパターンマッチ
|
// receiver を extract_value で処理
|
||||||
match method {
|
let (receiver_var, mut receiver_insts) = self.extract_value(receiver_expr, ctx);
|
||||||
"substring" => {
|
|
||||||
// Phase 34-5: ダミー実装(Int 0 を返す)
|
// args を extract_value で処理
|
||||||
let dst = ctx.alloc_var();
|
let mut arg_vars = Vec::new();
|
||||||
let inst = JoinInst::Compute(crate::mir::join_ir::MirLikeInst::Const {
|
let mut arg_insts = Vec::new();
|
||||||
dst,
|
for arg_expr in args_array {
|
||||||
value: ConstValue::Integer(0),
|
let (arg_var, arg_inst) = self.extract_value(arg_expr, ctx);
|
||||||
});
|
arg_vars.push(arg_var);
|
||||||
(dst, vec![inst])
|
arg_insts.extend(arg_inst);
|
||||||
}
|
|
||||||
_ => panic!("Unsupported method: {}", method),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MethodCall 命令を生成
|
||||||
|
let dst = ctx.alloc_var();
|
||||||
|
let method_call_inst = JoinInst::MethodCall {
|
||||||
|
dst,
|
||||||
|
receiver: receiver_var,
|
||||||
|
method: method_name.to_string(),
|
||||||
|
args: arg_vars,
|
||||||
|
};
|
||||||
|
|
||||||
|
// すべての命令を結合(receiver → args → MethodCall の順)
|
||||||
|
let mut insts = receiver_insts;
|
||||||
|
insts.extend(arg_insts);
|
||||||
|
insts.push(method_call_inst);
|
||||||
|
|
||||||
|
(dst, insts)
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => panic!("Unsupported expr type: {}", expr_type),
|
_ => panic!("Unsupported expr type: {}", expr_type),
|
||||||
@ -352,5 +370,5 @@ impl Default for AstToJoinIrLowerer {
|
|||||||
// Phase 34-2: IfSelectTest.* simple pattern 実装
|
// Phase 34-2: IfSelectTest.* simple pattern 実装
|
||||||
// Phase 34-3: local pattern 対応(simple と同じ JoinIR 出力)
|
// Phase 34-3: local pattern 対応(simple と同じ JoinIR 出力)
|
||||||
// Phase 34-4: Stage-1/meta 実用関数対応(JsonShapeToMap._read_value_from_pair/1)
|
// Phase 34-4: Stage-1/meta 実用関数対応(JsonShapeToMap._read_value_from_pair/1)
|
||||||
// Phase 34-5: extract_value 統一化(Int/Var/Method 対応)
|
// Phase 34-5: extract_value 統一化(Int/Var/Method 構造まで)
|
||||||
// Phase 34-6 以降: Loop/Break/Continue の AST→JoinIR 対応
|
// Phase 34-6 以降: MethodCall 構造の JoinIR への明示と JoinIR→MIR 変換側での Method/Call 意味論実装、その後 Loop/Break/Continue への拡張
|
||||||
|
|||||||
@ -142,6 +142,44 @@ fn write_inst<W: Write>(inst: &JoinInst, out: &mut W) -> std::io::Result<()> {
|
|||||||
write!(out, ",\"else_val\":{}", else_val.0)?;
|
write!(out, ",\"else_val\":{}", else_val.0)?;
|
||||||
write!(out, "}}")?;
|
write!(out, "}}")?;
|
||||||
}
|
}
|
||||||
|
// Phase 33-6: IfMerge instruction JSON serialization
|
||||||
|
JoinInst::IfMerge { cond, merges, k_next } => {
|
||||||
|
write!(out, "{{\"type\":\"if_merge\"")?;
|
||||||
|
write!(out, ",\"cond\":{}", cond.0)?;
|
||||||
|
write!(out, ",\"merges\":[")?;
|
||||||
|
for (i, merge) in merges.iter().enumerate() {
|
||||||
|
if i > 0 {
|
||||||
|
write!(out, ",")?;
|
||||||
|
}
|
||||||
|
write!(out, "{{")?;
|
||||||
|
write!(out, "\"dst\":{}", merge.dst.0)?;
|
||||||
|
write!(out, ",\"then_val\":{}", merge.then_val.0)?;
|
||||||
|
write!(out, ",\"else_val\":{}", merge.else_val.0)?;
|
||||||
|
write!(out, "}}")?;
|
||||||
|
}
|
||||||
|
write!(out, "]")?;
|
||||||
|
match k_next {
|
||||||
|
Some(k) => write!(out, ",\"k_next\":{}", k.0)?,
|
||||||
|
None => write!(out, ",\"k_next\":null")?,
|
||||||
|
}
|
||||||
|
write!(out, "}}")?;
|
||||||
|
}
|
||||||
|
// Phase 34-6: MethodCall instruction JSON serialization
|
||||||
|
JoinInst::MethodCall { dst, receiver, method, args } => {
|
||||||
|
write!(out, "{{\"type\":\"method_call\"")?;
|
||||||
|
write!(out, ",\"dst\":{}", dst.0)?;
|
||||||
|
write!(out, ",\"receiver\":{}", receiver.0)?;
|
||||||
|
write!(out, ",\"method\":\"{}\"", escape_json_string(method))?;
|
||||||
|
write!(out, ",\"args\":[")?;
|
||||||
|
for (i, arg) in args.iter().enumerate() {
|
||||||
|
if i > 0 {
|
||||||
|
write!(out, ",")?;
|
||||||
|
}
|
||||||
|
write!(out, "{}", arg.0)?;
|
||||||
|
}
|
||||||
|
write!(out, "]")?;
|
||||||
|
write!(out, "}}")?;
|
||||||
|
}
|
||||||
JoinInst::Compute(mir_like) => {
|
JoinInst::Compute(mir_like) => {
|
||||||
write!(out, "{{\"type\":\"compute\",\"op\":")?;
|
write!(out, "{{\"type\":\"compute\",\"op\":")?;
|
||||||
write_mir_like_inst(mir_like, out)?;
|
write_mir_like_inst(mir_like, out)?;
|
||||||
@ -270,7 +308,7 @@ pub fn join_module_to_json_string(module: &JoinModule) -> String {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::mir::join_ir::{JoinContId, JoinFuncId};
|
use crate::mir::join_ir::{JoinContId, JoinFuncId, MergePair};
|
||||||
use crate::mir::ValueId;
|
use crate::mir::ValueId;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -404,4 +442,44 @@ mod tests {
|
|||||||
assert!(json.contains("\\n"));
|
assert!(json.contains("\\n"));
|
||||||
assert!(json.contains("\\\""));
|
assert!(json.contains("\\\""));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Phase 33-6: IfMerge instruction JSON serialization test
|
||||||
|
#[test]
|
||||||
|
fn test_if_merge_instruction() {
|
||||||
|
let mut module = JoinModule::new();
|
||||||
|
let mut func = JoinFunction::new(JoinFuncId::new(0), "main".to_string(), vec![]);
|
||||||
|
|
||||||
|
// Add IfMerge instruction with 2 merge pairs
|
||||||
|
func.body.push(JoinInst::IfMerge {
|
||||||
|
cond: ValueId(1),
|
||||||
|
merges: vec![
|
||||||
|
MergePair {
|
||||||
|
dst: ValueId(10),
|
||||||
|
then_val: ValueId(20),
|
||||||
|
else_val: ValueId(30),
|
||||||
|
},
|
||||||
|
MergePair {
|
||||||
|
dst: ValueId(11),
|
||||||
|
then_val: ValueId(21),
|
||||||
|
else_val: ValueId(31),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
k_next: None,
|
||||||
|
});
|
||||||
|
module.add_function(func);
|
||||||
|
|
||||||
|
let json = join_module_to_json_string(&module);
|
||||||
|
|
||||||
|
// Verify JSON structure
|
||||||
|
assert!(json.contains("\"type\":\"if_merge\""));
|
||||||
|
assert!(json.contains("\"cond\":1"));
|
||||||
|
assert!(json.contains("\"merges\":["));
|
||||||
|
assert!(json.contains("\"dst\":10"));
|
||||||
|
assert!(json.contains("\"then_val\":20"));
|
||||||
|
assert!(json.contains("\"else_val\":30"));
|
||||||
|
assert!(json.contains("\"dst\":11"));
|
||||||
|
assert!(json.contains("\"then_val\":21"));
|
||||||
|
assert!(json.contains("\"else_val\":31"));
|
||||||
|
assert!(json.contains("\"k_next\":null"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
171
src/mir/join_ir/lowering/if_dry_runner.rs
Normal file
171
src/mir/join_ir/lowering/if_dry_runner.rs
Normal file
@ -0,0 +1,171 @@
|
|||||||
|
//! Phase 33-10.0: If lowering dry-run スキャナー(箱化版)
|
||||||
|
//!
|
||||||
|
//! ## 責務
|
||||||
|
//! - MIR モジュール内のすべての Branch ブロックをスキャン
|
||||||
|
//! - try_lower_if_to_joinir() でパターンマッチングを試行(MIR書き換えなし)
|
||||||
|
//! - パフォーマンス計測と統計情報の収集
|
||||||
|
//!
|
||||||
|
//! ## 非責務
|
||||||
|
//! - MIR の書き換え(Route B実装時に別モジュールで実施)
|
||||||
|
//! - Loop lowering(別のdispatch経路)
|
||||||
|
|
||||||
|
use crate::mir::join_ir::JoinInst;
|
||||||
|
use crate::mir::{MirFunction, MirInstruction};
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
|
/// If lowering dry-run スキャナー
|
||||||
|
pub struct IfLoweringDryRunner {
|
||||||
|
debug_level: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Dry-run スキャン結果の統計情報
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct DryRunStats {
|
||||||
|
pub total_branches: usize,
|
||||||
|
pub lowered_count: usize,
|
||||||
|
pub select_count: usize,
|
||||||
|
pub ifmerge_count: usize,
|
||||||
|
pub scan_duration: Duration,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IfLoweringDryRunner {
|
||||||
|
/// 新しい dry-run スキャナーを作成
|
||||||
|
pub fn new(debug_level: u8) -> Self {
|
||||||
|
Self { debug_level }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// MIR モジュール全体をスキャンして If lowering 成功率を計測
|
||||||
|
///
|
||||||
|
/// ## 実装方針(Phase 33-9.2)
|
||||||
|
/// - Loop専任関数はスキップ(is_loop_lowered_function())
|
||||||
|
/// - 各 Branch ブロックで try_lower_if_to_joinir() 試行
|
||||||
|
/// - パフォーマンス計測(マイクロ秒レベル)
|
||||||
|
/// - 統計情報収集(Select/IfMerge分類)
|
||||||
|
pub fn scan_module(
|
||||||
|
&self,
|
||||||
|
functions: &HashMap<String, MirFunction>,
|
||||||
|
) -> DryRunStats {
|
||||||
|
let mut total_branches = 0;
|
||||||
|
let mut lowered_count = 0;
|
||||||
|
let mut select_count = 0;
|
||||||
|
let mut ifmerge_count = 0;
|
||||||
|
let start_scan = Instant::now();
|
||||||
|
|
||||||
|
for (func_name, func) in functions {
|
||||||
|
// Phase 33-9.1: Loop専任関数をスキップ
|
||||||
|
if crate::mir::join_ir::lowering::is_loop_lowered_function(func_name) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 各Branchブロックに対してtry_lower_if_to_joinir()試行
|
||||||
|
for (block_id, block) in &func.blocks {
|
||||||
|
if matches!(
|
||||||
|
block.terminator,
|
||||||
|
Some(MirInstruction::Branch { .. })
|
||||||
|
) {
|
||||||
|
total_branches += 1;
|
||||||
|
let start = Instant::now();
|
||||||
|
|
||||||
|
match crate::mir::join_ir::lowering::try_lower_if_to_joinir(
|
||||||
|
func,
|
||||||
|
*block_id,
|
||||||
|
self.debug_level >= 3,
|
||||||
|
) {
|
||||||
|
Some(join_inst) => {
|
||||||
|
lowered_count += 1;
|
||||||
|
let elapsed = start.elapsed();
|
||||||
|
|
||||||
|
let inst_type = match &join_inst {
|
||||||
|
JoinInst::Select { .. } => {
|
||||||
|
select_count += 1;
|
||||||
|
"Select"
|
||||||
|
}
|
||||||
|
JoinInst::IfMerge { .. } => {
|
||||||
|
ifmerge_count += 1;
|
||||||
|
"IfMerge"
|
||||||
|
}
|
||||||
|
_ => "Other",
|
||||||
|
};
|
||||||
|
|
||||||
|
if self.debug_level >= 1 {
|
||||||
|
eprintln!(
|
||||||
|
"[joinir/if_lowering] ✅ {} block {:?}: {} ({:.2}μs)",
|
||||||
|
func_name,
|
||||||
|
block_id,
|
||||||
|
inst_type,
|
||||||
|
elapsed.as_micros()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
if self.debug_level >= 2 {
|
||||||
|
eprintln!(
|
||||||
|
"[joinir/if_lowering] ⏭️ {} block {:?}: pattern not matched",
|
||||||
|
func_name, block_id
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let scan_duration = start_scan.elapsed();
|
||||||
|
|
||||||
|
DryRunStats {
|
||||||
|
total_branches,
|
||||||
|
lowered_count,
|
||||||
|
select_count,
|
||||||
|
ifmerge_count,
|
||||||
|
scan_duration,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 統計情報を標準エラー出力に表示
|
||||||
|
pub fn print_stats(&self, stats: &DryRunStats) {
|
||||||
|
if self.debug_level >= 1 && stats.total_branches > 0 {
|
||||||
|
eprintln!("[joinir/if_lowering] 📊 Scan complete:");
|
||||||
|
eprintln!(" Total branches: {}", stats.total_branches);
|
||||||
|
eprintln!(
|
||||||
|
" Lowered: {} ({:.1}%)",
|
||||||
|
stats.lowered_count,
|
||||||
|
(stats.lowered_count as f64 / stats.total_branches as f64) * 100.0
|
||||||
|
);
|
||||||
|
eprintln!(" - Select: {}", stats.select_count);
|
||||||
|
eprintln!(" - IfMerge: {}", stats.ifmerge_count);
|
||||||
|
eprintln!(
|
||||||
|
" Scan time: {:.2}ms",
|
||||||
|
stats.scan_duration.as_secs_f64() * 1000.0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_dry_runner_creation() {
|
||||||
|
let runner = IfLoweringDryRunner::new(0);
|
||||||
|
assert_eq!(runner.debug_level, 0);
|
||||||
|
|
||||||
|
let runner_verbose = IfLoweringDryRunner::new(3);
|
||||||
|
assert_eq!(runner_verbose.debug_level, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_dry_run_stats_default() {
|
||||||
|
let stats = DryRunStats {
|
||||||
|
total_branches: 0,
|
||||||
|
lowered_count: 0,
|
||||||
|
select_count: 0,
|
||||||
|
ifmerge_count: 0,
|
||||||
|
scan_duration: Duration::from_millis(10),
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(stats.total_branches, 0);
|
||||||
|
assert_eq!(stats.lowered_count, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -119,6 +119,21 @@ impl IfSelectLowerer {
|
|||||||
let then_block = func.blocks.get(&branch.then_block)?;
|
let then_block = func.blocks.get(&branch.then_block)?;
|
||||||
let else_block = func.blocks.get(&branch.else_block)?;
|
let else_block = func.blocks.get(&branch.else_block)?;
|
||||||
|
|
||||||
|
// Phase 33-10: PHI早期チェック(パターンマッチング前)
|
||||||
|
// JoinIRは「PHI生成器」であり「PHI変換器」ではない
|
||||||
|
// then/elseがJumpで終わる場合、merge blockにPHI命令があるか早期確認
|
||||||
|
if let Some(merge_block_id) = self.get_merge_block_if_jump_pattern(&branch, then_block, else_block) {
|
||||||
|
let merge_block = func.blocks.get(&merge_block_id)?;
|
||||||
|
if merge_block.instructions.iter().any(|inst| {
|
||||||
|
matches!(inst, MirInstruction::Phi { .. })
|
||||||
|
}) {
|
||||||
|
if self.debug_level >= 2 {
|
||||||
|
eprintln!("[IfSelectLowerer] ⏭️ PHI already exists in merge block, skipping");
|
||||||
|
}
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 3. simple パターンのチェック
|
// 3. simple パターンのチェック
|
||||||
if let Some(pattern) = self.try_match_simple_pattern(&branch, then_block, else_block) {
|
if let Some(pattern) = self.try_match_simple_pattern(&branch, then_block, else_block) {
|
||||||
// Phase 33-8: Level 2 - Pattern matching details
|
// Phase 33-8: Level 2 - Pattern matching details
|
||||||
@ -146,6 +161,10 @@ impl IfSelectLowerer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// simple パターン: if cond { return 1 } else { return 2 }
|
/// simple パターン: if cond { return 1 } else { return 2 }
|
||||||
|
///
|
||||||
|
/// Phase 33-9.2: 実用MIR対応 - 副作用なし命令(Const/Copy)を許容
|
||||||
|
/// - 旧: Return のみ(instructions empty)
|
||||||
|
/// - 新: Const/Copy → Return を許容(実MIRパターン)
|
||||||
fn try_match_simple_pattern(
|
fn try_match_simple_pattern(
|
||||||
&self,
|
&self,
|
||||||
branch: &IfBranch,
|
branch: &IfBranch,
|
||||||
@ -164,8 +183,12 @@ impl IfSelectLowerer {
|
|||||||
_ => return None,
|
_ => return None,
|
||||||
};
|
};
|
||||||
|
|
||||||
// 両方のブロックが命令を持たない(Return のみ)ことを確認
|
// Phase 33-9.2: 副作用なし命令(Const/Copy)のみを許容
|
||||||
if !then_block.instructions.is_empty() || !else_block.instructions.is_empty() {
|
// - ユニットテスト(empty)も通過(空配列 → all() = true)
|
||||||
|
// - 実用MIR(const + ret)も通過
|
||||||
|
if !self.is_side_effect_free(&then_block.instructions)
|
||||||
|
|| !self.is_side_effect_free(&else_block.instructions)
|
||||||
|
{
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -178,7 +201,53 @@ impl IfSelectLowerer {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Phase 33-9.2: 副作用なし命令判定ヘルパー
|
||||||
|
///
|
||||||
|
/// Const/Copy のみを許容(分岐・call・書き込み等は除外)
|
||||||
|
fn is_side_effect_free(&self, instructions: &[MirInstruction]) -> bool {
|
||||||
|
instructions.iter().all(|inst| {
|
||||||
|
matches!(
|
||||||
|
inst,
|
||||||
|
MirInstruction::Const { .. } | MirInstruction::Copy { .. }
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Phase 33-10: Jump pattern 検出ヘルパー
|
||||||
|
///
|
||||||
|
/// then/else 両方が Jump で終わり、同じ merge block に飛んでいる場合、
|
||||||
|
/// その merge block IDを返す
|
||||||
|
fn get_merge_block_if_jump_pattern(
|
||||||
|
&self,
|
||||||
|
_branch: &IfBranch,
|
||||||
|
then_block: &crate::mir::BasicBlock,
|
||||||
|
else_block: &crate::mir::BasicBlock,
|
||||||
|
) -> Option<BasicBlockId> {
|
||||||
|
// then が Jump で終わるか確認
|
||||||
|
let then_target = match then_block.terminator.as_ref()? {
|
||||||
|
MirInstruction::Jump { target } => *target,
|
||||||
|
_ => return None,
|
||||||
|
};
|
||||||
|
|
||||||
|
// else が Jump で終わるか確認
|
||||||
|
let else_target = match else_block.terminator.as_ref()? {
|
||||||
|
MirInstruction::Jump { target } => *target,
|
||||||
|
_ => return None,
|
||||||
|
};
|
||||||
|
|
||||||
|
// 両方が同じ merge block に飛んでいるか確認
|
||||||
|
if then_target == else_target {
|
||||||
|
Some(then_target)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// local パターン: if cond { x = a } else { x = b }; return x
|
/// local パターン: if cond { x = a } else { x = b }; return x
|
||||||
|
///
|
||||||
|
/// Phase 33-10: 実用MIR対応 - Const命令を許容
|
||||||
|
/// - 旧: Copy命令のみ(ユニットテスト想定)
|
||||||
|
/// - 新: Const/Copy命令を許容(実MIR対応、Simple patternと同じ修正)
|
||||||
fn try_match_local_pattern(
|
fn try_match_local_pattern(
|
||||||
&self,
|
&self,
|
||||||
func: &MirFunction,
|
func: &MirFunction,
|
||||||
@ -186,15 +255,21 @@ impl IfSelectLowerer {
|
|||||||
then_block: &crate::mir::BasicBlock,
|
then_block: &crate::mir::BasicBlock,
|
||||||
else_block: &crate::mir::BasicBlock,
|
else_block: &crate::mir::BasicBlock,
|
||||||
) -> Option<IfPattern> {
|
) -> Option<IfPattern> {
|
||||||
// then ブロックが「1命令 + Jump」の形か確認
|
// Phase 33-10: 副作用なし命令のみを許容
|
||||||
if then_block.instructions.len() != 1 {
|
if !self.is_side_effect_free(&then_block.instructions) {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
// then ブロックの命令が代入(Copy)か確認
|
// then ブロックの最後の値を取得
|
||||||
let (dst_then, val_then) = match &then_block.instructions[0] {
|
// Phase 33-10: Const命令も許容(実MIR対応)
|
||||||
MirInstruction::Copy { dst, src } => (*dst, *src),
|
let (dst_then, val_then) = if then_block.instructions.len() == 1 {
|
||||||
_ => return None,
|
match &then_block.instructions[0] {
|
||||||
|
MirInstruction::Copy { dst, src } => (*dst, *src),
|
||||||
|
MirInstruction::Const { dst, .. } => (*dst, *dst), // Constの場合、dst自身が値
|
||||||
|
_ => return None,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return None;
|
||||||
};
|
};
|
||||||
|
|
||||||
// then ブロックが Jump で終わるか確認
|
// then ブロックが Jump で終わるか確認
|
||||||
@ -203,14 +278,20 @@ impl IfSelectLowerer {
|
|||||||
_ => return None,
|
_ => return None,
|
||||||
};
|
};
|
||||||
|
|
||||||
// else ブロックも同じ構造か確認
|
// Phase 33-10: else ブロックも副作用なし命令のみを許容
|
||||||
if else_block.instructions.len() != 1 {
|
if !self.is_side_effect_free(&else_block.instructions) {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let (dst_else, val_else) = match &else_block.instructions[0] {
|
// else ブロックの最後の値を取得
|
||||||
MirInstruction::Copy { dst, src } => (*dst, *src),
|
let (dst_else, val_else) = if else_block.instructions.len() == 1 {
|
||||||
_ => return None,
|
match &else_block.instructions[0] {
|
||||||
|
MirInstruction::Copy { dst, src } => (*dst, *src),
|
||||||
|
MirInstruction::Const { dst, .. } => (*dst, *dst), // Constの場合、dst自身が値
|
||||||
|
_ => return None,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return None;
|
||||||
};
|
};
|
||||||
|
|
||||||
// 代入先が同じ変数か確認
|
// 代入先が同じ変数か確認
|
||||||
@ -230,6 +311,8 @@ impl IfSelectLowerer {
|
|||||||
|
|
||||||
// merge ブロックが「return dst」だけか確認
|
// merge ブロックが「return dst」だけか確認
|
||||||
let merge_block = func.blocks.get(&merge_block_id)?;
|
let merge_block = func.blocks.get(&merge_block_id)?;
|
||||||
|
// Phase 33-10: PHIチェックは find_if_pattern() で早期実行済み
|
||||||
|
|
||||||
match merge_block.terminator.as_ref()? {
|
match merge_block.terminator.as_ref()? {
|
||||||
MirInstruction::Return {
|
MirInstruction::Return {
|
||||||
value: Some(v),
|
value: Some(v),
|
||||||
|
|||||||
@ -13,12 +13,14 @@
|
|||||||
//! - `stage1_using_resolver.rs`: Stage1UsingResolverBox.resolve_for_source entries loop lowering(Phase 27.12)
|
//! - `stage1_using_resolver.rs`: Stage1UsingResolverBox.resolve_for_source entries loop lowering(Phase 27.12)
|
||||||
//! - `funcscanner_append_defs.rs`: FuncScannerBox._append_defs/2 の配列結合 lowering(Phase 27.14)
|
//! - `funcscanner_append_defs.rs`: FuncScannerBox._append_defs/2 の配列結合 lowering(Phase 27.14)
|
||||||
//! - `if_select.rs`: Phase 33 If/Else → Select lowering
|
//! - `if_select.rs`: Phase 33 If/Else → Select lowering
|
||||||
|
//! - `if_dry_runner.rs`: Phase 33-10 If lowering dry-run スキャナー(箱化版)
|
||||||
|
|
||||||
pub mod common;
|
pub mod common;
|
||||||
pub mod exit_args_resolver;
|
pub mod exit_args_resolver;
|
||||||
pub mod funcscanner_append_defs;
|
pub mod funcscanner_append_defs;
|
||||||
pub mod funcscanner_trim;
|
pub mod funcscanner_trim;
|
||||||
pub mod generic_case_a;
|
pub mod generic_case_a;
|
||||||
|
pub mod if_dry_runner; // Phase 33-10.0
|
||||||
pub mod if_merge; // Phase 33-7
|
pub mod if_merge; // Phase 33-7
|
||||||
pub mod if_select; // Phase 33
|
pub mod if_select; // Phase 33
|
||||||
pub mod loop_form_intake;
|
pub mod loop_form_intake;
|
||||||
@ -116,6 +118,7 @@ pub fn try_lower_if_to_joinir(
|
|||||||
let is_allowed =
|
let is_allowed =
|
||||||
// Test functions (always enabled)
|
// Test functions (always enabled)
|
||||||
func.signature.name.starts_with("IfSelectTest.") ||
|
func.signature.name.starts_with("IfSelectTest.") ||
|
||||||
|
func.signature.name.starts_with("IfSelectLocalTest.") || // Phase 33-10 test
|
||||||
func.signature.name.starts_with("IfMergeTest.") ||
|
func.signature.name.starts_with("IfMergeTest.") ||
|
||||||
func.signature.name.starts_with("Stage1JsonScannerTestBox.") || // Phase 33-5 test
|
func.signature.name.starts_with("Stage1JsonScannerTestBox.") || // Phase 33-5 test
|
||||||
|
|
||||||
|
|||||||
@ -33,6 +33,9 @@ pub mod verify;
|
|||||||
// Phase 30.x: JSON serialization (jsonir v0)
|
// Phase 30.x: JSON serialization (jsonir v0)
|
||||||
pub mod json;
|
pub mod json;
|
||||||
|
|
||||||
|
// Phase 34-1: Frontend (AST→JoinIR) — skeleton only
|
||||||
|
pub mod frontend;
|
||||||
|
|
||||||
// Re-export lowering functions for backward compatibility
|
// Re-export lowering functions for backward compatibility
|
||||||
pub use lowering::{
|
pub use lowering::{
|
||||||
lower_funcscanner_trim_to_joinir, lower_min_loop_to_joinir, lower_skip_ws_to_joinir,
|
lower_funcscanner_trim_to_joinir, lower_min_loop_to_joinir, lower_skip_ws_to_joinir,
|
||||||
@ -162,6 +165,17 @@ impl JoinFunction {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Phase 33-6: 複数変数を merge する if/else のペア
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct MergePair {
|
||||||
|
/// merge 先の変数
|
||||||
|
pub dst: VarId,
|
||||||
|
/// then 分岐での値
|
||||||
|
pub then_val: VarId,
|
||||||
|
/// else 分岐での値
|
||||||
|
pub else_val: VarId,
|
||||||
|
}
|
||||||
|
|
||||||
/// JoinIR 命令セット(最小版)
|
/// JoinIR 命令セット(最小版)
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum JoinInst {
|
pub enum JoinInst {
|
||||||
@ -185,7 +199,7 @@ pub enum JoinInst {
|
|||||||
/// ルート関数 or 上位への戻り
|
/// ルート関数 or 上位への戻り
|
||||||
Ret { value: Option<VarId> },
|
Ret { value: Option<VarId> },
|
||||||
|
|
||||||
/// Phase 33: If/Else の単純な値選択
|
/// Phase 33: If/Else の単純な値選択(単一値)
|
||||||
/// cond が true なら then_val、false なら else_val を dst に代入
|
/// cond が true なら then_val、false なら else_val を dst に代入
|
||||||
Select {
|
Select {
|
||||||
dst: VarId,
|
dst: VarId,
|
||||||
@ -194,6 +208,25 @@ pub enum JoinInst {
|
|||||||
else_val: VarId,
|
else_val: VarId,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/// Phase 33-6: If/Else の複数変数 merge
|
||||||
|
/// cond が true なら各 dst に then_val を、false なら else_val を代入
|
||||||
|
/// 複数の PHI ノードを一括で表現する
|
||||||
|
IfMerge {
|
||||||
|
cond: VarId,
|
||||||
|
merges: Vec<MergePair>,
|
||||||
|
k_next: Option<JoinContId>,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// Phase 34-6: メソッド呼び出し構造
|
||||||
|
/// receiver.method(args...) の構造を JoinIR で表現
|
||||||
|
/// 意味論(BoxCall/Call への変換)は JoinIR→MIR ブリッジで実装
|
||||||
|
MethodCall {
|
||||||
|
dst: VarId,
|
||||||
|
receiver: VarId,
|
||||||
|
method: String,
|
||||||
|
args: Vec<VarId>,
|
||||||
|
},
|
||||||
|
|
||||||
/// それ以外の演算は、現行 MIR の算術/比較/boxcall を再利用
|
/// それ以外の演算は、現行 MIR の算術/比較/boxcall を再利用
|
||||||
Compute(MirLikeInst),
|
Compute(MirLikeInst),
|
||||||
}
|
}
|
||||||
|
|||||||
@ -112,6 +112,7 @@ fn execute_function(
|
|||||||
JoinInst::Select { dst, cond, then_val, else_val } => {
|
JoinInst::Select { dst, cond, then_val, else_val } => {
|
||||||
// 1. Evaluate cond (Bool or Int)
|
// 1. Evaluate cond (Bool or Int)
|
||||||
let cond_value = read_var(&locals, *cond)?;
|
let cond_value = read_var(&locals, *cond)?;
|
||||||
|
eprintln!("[SELECT DEBUG] cond={:?}, cond_value={:?}", cond, cond_value);
|
||||||
let cond_bool = match cond_value {
|
let cond_bool = match cond_value {
|
||||||
JoinValue::Bool(b) => b,
|
JoinValue::Bool(b) => b,
|
||||||
JoinValue::Int(i) => i != 0, // Int も許す(0=false, それ以外=true)
|
JoinValue::Int(i) => i != 0, // Int も許す(0=false, それ以外=true)
|
||||||
@ -121,13 +122,55 @@ fn execute_function(
|
|||||||
};
|
};
|
||||||
|
|
||||||
// 2. Select then_val or else_val
|
// 2. Select then_val or else_val
|
||||||
|
let then_value = read_var(&locals, *then_val)?;
|
||||||
|
let else_value = read_var(&locals, *else_val)?;
|
||||||
|
eprintln!("[SELECT DEBUG] cond_bool={}, then_val={:?}={:?}, else_val={:?}={:?}",
|
||||||
|
cond_bool, then_val, then_value, else_val, else_value);
|
||||||
|
|
||||||
let selected_id = if cond_bool { *then_val } else { *else_val };
|
let selected_id = if cond_bool { *then_val } else { *else_val };
|
||||||
let selected_value = read_var(&locals, selected_id)?;
|
let selected_value = read_var(&locals, selected_id)?;
|
||||||
|
eprintln!("[SELECT DEBUG] selected_id={:?}, selected_value={:?}", selected_id, selected_value);
|
||||||
|
|
||||||
// 3. Write to dst
|
// 3. Write to dst
|
||||||
locals.insert(*dst, selected_value);
|
locals.insert(*dst, selected_value);
|
||||||
ip += 1;
|
ip += 1;
|
||||||
}
|
}
|
||||||
|
// Phase 33-6: IfMerge instruction execution (複数変数 PHI)
|
||||||
|
JoinInst::IfMerge { cond, merges, k_next } => {
|
||||||
|
// Phase 33-6 最小実装: k_next は None のみサポート
|
||||||
|
if k_next.is_some() {
|
||||||
|
return Err(JoinRuntimeError::new(
|
||||||
|
"IfMerge: k_next continuation is not yet supported (Phase 33-6 minimal)",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1. Evaluate cond (Bool or Int)
|
||||||
|
let cond_value = read_var(&locals, *cond)?;
|
||||||
|
let cond_bool = match cond_value {
|
||||||
|
JoinValue::Bool(b) => b,
|
||||||
|
JoinValue::Int(i) => i != 0,
|
||||||
|
_ => return Err(JoinRuntimeError::new(format!(
|
||||||
|
"IfMerge: cond must be Bool or Int, got {:?}", cond_value
|
||||||
|
))),
|
||||||
|
};
|
||||||
|
|
||||||
|
// 2. 各 merge ペアについて、cond に応じて値を選択して代入
|
||||||
|
for merge in merges {
|
||||||
|
let selected_id = if cond_bool { merge.then_val } else { merge.else_val };
|
||||||
|
let selected_value = read_var(&locals, selected_id)?;
|
||||||
|
locals.insert(merge.dst, selected_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
ip += 1;
|
||||||
|
}
|
||||||
|
// Phase 34-6: MethodCall instruction execution
|
||||||
|
JoinInst::MethodCall { .. } => {
|
||||||
|
// Phase 34-6: MethodCall は JoinIR Runner では未対応
|
||||||
|
// JoinIR → MIR 変換経由で VM が実行する
|
||||||
|
return Err(JoinRuntimeError::new(
|
||||||
|
"MethodCall is not supported in JoinIR Runner (use JoinIR→MIR→VM bridge instead)"
|
||||||
|
));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -392,4 +435,271 @@ mod tests {
|
|||||||
|
|
||||||
assert_eq!(result, JoinValue::Int(200)); // 0 is false, so should select else
|
assert_eq!(result, JoinValue::Int(200)); // 0 is false, so should select else
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Phase 33-6: IfMerge instruction tests
|
||||||
|
#[test]
|
||||||
|
fn test_if_merge_true() {
|
||||||
|
// if true { x=1; y=2 } else { x=3; y=4 }
|
||||||
|
// expected: x=1, y=2
|
||||||
|
let mut module = JoinModule::new();
|
||||||
|
let mut func = JoinFunction::new(JoinFuncId::new(0), "test_func".to_string(), vec![]);
|
||||||
|
|
||||||
|
let v_cond = ValueId(1);
|
||||||
|
let v_then_x = ValueId(2);
|
||||||
|
let v_then_y = ValueId(3);
|
||||||
|
let v_else_x = ValueId(4);
|
||||||
|
let v_else_y = ValueId(5);
|
||||||
|
let v_result_x = ValueId(6);
|
||||||
|
let v_result_y = ValueId(7);
|
||||||
|
|
||||||
|
// const v1 = true
|
||||||
|
func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||||
|
dst: v_cond,
|
||||||
|
value: ConstValue::Bool(true),
|
||||||
|
}));
|
||||||
|
|
||||||
|
// const v2 = 1 (then x)
|
||||||
|
func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||||
|
dst: v_then_x,
|
||||||
|
value: ConstValue::Integer(1),
|
||||||
|
}));
|
||||||
|
|
||||||
|
// const v3 = 2 (then y)
|
||||||
|
func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||||
|
dst: v_then_y,
|
||||||
|
value: ConstValue::Integer(2),
|
||||||
|
}));
|
||||||
|
|
||||||
|
// const v4 = 3 (else x)
|
||||||
|
func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||||
|
dst: v_else_x,
|
||||||
|
value: ConstValue::Integer(3),
|
||||||
|
}));
|
||||||
|
|
||||||
|
// const v5 = 4 (else y)
|
||||||
|
func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||||
|
dst: v_else_y,
|
||||||
|
value: ConstValue::Integer(4),
|
||||||
|
}));
|
||||||
|
|
||||||
|
// if_merge v1 { v6=v2; v7=v3 } else { v6=v4; v7=v5 }
|
||||||
|
func.body.push(JoinInst::IfMerge {
|
||||||
|
cond: v_cond,
|
||||||
|
merges: vec![
|
||||||
|
crate::mir::join_ir::MergePair {
|
||||||
|
dst: v_result_x,
|
||||||
|
then_val: v_then_x,
|
||||||
|
else_val: v_else_x,
|
||||||
|
},
|
||||||
|
crate::mir::join_ir::MergePair {
|
||||||
|
dst: v_result_y,
|
||||||
|
then_val: v_then_y,
|
||||||
|
else_val: v_else_y,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
k_next: None,
|
||||||
|
});
|
||||||
|
|
||||||
|
// return v6 + v7
|
||||||
|
let v_sum = ValueId(8);
|
||||||
|
func.body.push(JoinInst::Compute(MirLikeInst::BinOp {
|
||||||
|
dst: v_sum,
|
||||||
|
op: crate::mir::join_ir::BinOpKind::Add,
|
||||||
|
lhs: v_result_x,
|
||||||
|
rhs: v_result_y,
|
||||||
|
}));
|
||||||
|
|
||||||
|
func.body.push(JoinInst::Ret { value: Some(v_sum) });
|
||||||
|
|
||||||
|
module.add_function(func);
|
||||||
|
|
||||||
|
let mut vm = MirInterpreter::new();
|
||||||
|
let result = run_joinir_function(&mut vm, &module, JoinFuncId::new(0), &[]).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(result, JoinValue::Int(3)); // 1 + 2 = 3
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_if_merge_false() {
|
||||||
|
// if false { x=1; y=2 } else { x=3; y=4 }
|
||||||
|
// expected: x=3, y=4
|
||||||
|
let mut module = JoinModule::new();
|
||||||
|
let mut func = JoinFunction::new(JoinFuncId::new(0), "test_func".to_string(), vec![]);
|
||||||
|
|
||||||
|
let v_cond = ValueId(1);
|
||||||
|
let v_then_x = ValueId(2);
|
||||||
|
let v_then_y = ValueId(3);
|
||||||
|
let v_else_x = ValueId(4);
|
||||||
|
let v_else_y = ValueId(5);
|
||||||
|
let v_result_x = ValueId(6);
|
||||||
|
let v_result_y = ValueId(7);
|
||||||
|
|
||||||
|
// const v1 = false
|
||||||
|
func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||||
|
dst: v_cond,
|
||||||
|
value: ConstValue::Bool(false),
|
||||||
|
}));
|
||||||
|
|
||||||
|
// const v2 = 1 (then x)
|
||||||
|
func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||||
|
dst: v_then_x,
|
||||||
|
value: ConstValue::Integer(1),
|
||||||
|
}));
|
||||||
|
|
||||||
|
// const v3 = 2 (then y)
|
||||||
|
func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||||
|
dst: v_then_y,
|
||||||
|
value: ConstValue::Integer(2),
|
||||||
|
}));
|
||||||
|
|
||||||
|
// const v4 = 3 (else x)
|
||||||
|
func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||||
|
dst: v_else_x,
|
||||||
|
value: ConstValue::Integer(3),
|
||||||
|
}));
|
||||||
|
|
||||||
|
// const v5 = 4 (else y)
|
||||||
|
func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||||
|
dst: v_else_y,
|
||||||
|
value: ConstValue::Integer(4),
|
||||||
|
}));
|
||||||
|
|
||||||
|
// if_merge v1 { v6=v2; v7=v3 } else { v6=v4; v7=v5 }
|
||||||
|
func.body.push(JoinInst::IfMerge {
|
||||||
|
cond: v_cond,
|
||||||
|
merges: vec![
|
||||||
|
crate::mir::join_ir::MergePair {
|
||||||
|
dst: v_result_x,
|
||||||
|
then_val: v_then_x,
|
||||||
|
else_val: v_else_x,
|
||||||
|
},
|
||||||
|
crate::mir::join_ir::MergePair {
|
||||||
|
dst: v_result_y,
|
||||||
|
then_val: v_then_y,
|
||||||
|
else_val: v_else_y,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
k_next: None,
|
||||||
|
});
|
||||||
|
|
||||||
|
// return v6 + v7
|
||||||
|
let v_sum = ValueId(8);
|
||||||
|
func.body.push(JoinInst::Compute(MirLikeInst::BinOp {
|
||||||
|
dst: v_sum,
|
||||||
|
op: crate::mir::join_ir::BinOpKind::Add,
|
||||||
|
lhs: v_result_x,
|
||||||
|
rhs: v_result_y,
|
||||||
|
}));
|
||||||
|
|
||||||
|
func.body.push(JoinInst::Ret { value: Some(v_sum) });
|
||||||
|
|
||||||
|
module.add_function(func);
|
||||||
|
|
||||||
|
let mut vm = MirInterpreter::new();
|
||||||
|
let result = run_joinir_function(&mut vm, &module, JoinFuncId::new(0), &[]).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(result, JoinValue::Int(7)); // 3 + 4 = 7
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_if_merge_multiple() {
|
||||||
|
// if true { x=10; y=20; z=30 } else { x=1; y=2; z=3 }
|
||||||
|
// expected: x=10, y=20, z=30 → sum=60
|
||||||
|
let mut module = JoinModule::new();
|
||||||
|
let mut func = JoinFunction::new(JoinFuncId::new(0), "test_func".to_string(), vec![]);
|
||||||
|
|
||||||
|
let v_cond = ValueId(1);
|
||||||
|
let v_then_x = ValueId(2);
|
||||||
|
let v_then_y = ValueId(3);
|
||||||
|
let v_then_z = ValueId(4);
|
||||||
|
let v_else_x = ValueId(5);
|
||||||
|
let v_else_y = ValueId(6);
|
||||||
|
let v_else_z = ValueId(7);
|
||||||
|
let v_result_x = ValueId(8);
|
||||||
|
let v_result_y = ValueId(9);
|
||||||
|
let v_result_z = ValueId(10);
|
||||||
|
|
||||||
|
// const v1 = true
|
||||||
|
func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||||
|
dst: v_cond,
|
||||||
|
value: ConstValue::Bool(true),
|
||||||
|
}));
|
||||||
|
|
||||||
|
// then values
|
||||||
|
func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||||
|
dst: v_then_x,
|
||||||
|
value: ConstValue::Integer(10),
|
||||||
|
}));
|
||||||
|
func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||||
|
dst: v_then_y,
|
||||||
|
value: ConstValue::Integer(20),
|
||||||
|
}));
|
||||||
|
func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||||
|
dst: v_then_z,
|
||||||
|
value: ConstValue::Integer(30),
|
||||||
|
}));
|
||||||
|
|
||||||
|
// else values
|
||||||
|
func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||||
|
dst: v_else_x,
|
||||||
|
value: ConstValue::Integer(1),
|
||||||
|
}));
|
||||||
|
func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||||
|
dst: v_else_y,
|
||||||
|
value: ConstValue::Integer(2),
|
||||||
|
}));
|
||||||
|
func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||||
|
dst: v_else_z,
|
||||||
|
value: ConstValue::Integer(3),
|
||||||
|
}));
|
||||||
|
|
||||||
|
// if_merge with 3 variables
|
||||||
|
func.body.push(JoinInst::IfMerge {
|
||||||
|
cond: v_cond,
|
||||||
|
merges: vec![
|
||||||
|
crate::mir::join_ir::MergePair {
|
||||||
|
dst: v_result_x,
|
||||||
|
then_val: v_then_x,
|
||||||
|
else_val: v_else_x,
|
||||||
|
},
|
||||||
|
crate::mir::join_ir::MergePair {
|
||||||
|
dst: v_result_y,
|
||||||
|
then_val: v_then_y,
|
||||||
|
else_val: v_else_y,
|
||||||
|
},
|
||||||
|
crate::mir::join_ir::MergePair {
|
||||||
|
dst: v_result_z,
|
||||||
|
then_val: v_then_z,
|
||||||
|
else_val: v_else_z,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
k_next: None,
|
||||||
|
});
|
||||||
|
|
||||||
|
// return x + y + z
|
||||||
|
let v_sum_xy = ValueId(11);
|
||||||
|
func.body.push(JoinInst::Compute(MirLikeInst::BinOp {
|
||||||
|
dst: v_sum_xy,
|
||||||
|
op: crate::mir::join_ir::BinOpKind::Add,
|
||||||
|
lhs: v_result_x,
|
||||||
|
rhs: v_result_y,
|
||||||
|
}));
|
||||||
|
|
||||||
|
let v_sum_xyz = ValueId(12);
|
||||||
|
func.body.push(JoinInst::Compute(MirLikeInst::BinOp {
|
||||||
|
dst: v_sum_xyz,
|
||||||
|
op: crate::mir::join_ir::BinOpKind::Add,
|
||||||
|
lhs: v_sum_xy,
|
||||||
|
rhs: v_result_z,
|
||||||
|
}));
|
||||||
|
|
||||||
|
func.body.push(JoinInst::Ret { value: Some(v_sum_xyz) });
|
||||||
|
|
||||||
|
module.add_function(func);
|
||||||
|
|
||||||
|
let mut vm = MirInterpreter::new();
|
||||||
|
let result = run_joinir_function(&mut vm, &module, JoinFuncId::new(0), &[]).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(result, JoinValue::Int(60)); // 10 + 20 + 30 = 60
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -210,6 +210,19 @@ fn convert_join_function_to_mir(
|
|||||||
let mir_inst = convert_mir_like_inst(mir_like)?;
|
let mir_inst = convert_mir_like_inst(mir_like)?;
|
||||||
current_instructions.push(mir_inst);
|
current_instructions.push(mir_inst);
|
||||||
}
|
}
|
||||||
|
JoinInst::MethodCall { dst, receiver, method, args } => {
|
||||||
|
// Phase 34-6: MethodCall → MIR BoxCall 変換
|
||||||
|
// receiver.method(args...) を BoxCall(receiver, method, args) に変換
|
||||||
|
let mir_inst = MirInstruction::BoxCall {
|
||||||
|
dst: Some(*dst),
|
||||||
|
box_val: *receiver,
|
||||||
|
method: method.clone(),
|
||||||
|
method_id: None,
|
||||||
|
args: args.clone(),
|
||||||
|
effects: EffectMask::PURE,
|
||||||
|
};
|
||||||
|
current_instructions.push(mir_inst);
|
||||||
|
}
|
||||||
JoinInst::Call {
|
JoinInst::Call {
|
||||||
func,
|
func,
|
||||||
args,
|
args,
|
||||||
@ -386,6 +399,78 @@ fn convert_join_function_to_mir(
|
|||||||
current_block_id = merge_block;
|
current_block_id = merge_block;
|
||||||
current_instructions = Vec::new();
|
current_instructions = Vec::new();
|
||||||
}
|
}
|
||||||
|
// Phase 33-6: IfMerge instruction conversion to MIR
|
||||||
|
JoinInst::IfMerge { cond, merges, k_next } => {
|
||||||
|
// Phase 33-6: IfMerge を MIR の if/phi に変換
|
||||||
|
// Select と同じ 4 ブロック構造だが、複数の Copy を生成
|
||||||
|
|
||||||
|
// Phase 33-6 最小実装: k_next は None のみサポート
|
||||||
|
if k_next.is_some() {
|
||||||
|
return Err(JoinIrVmBridgeError::new(
|
||||||
|
"IfMerge: k_next continuation is not yet supported (Phase 33-6 minimal)".to_string(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
debug_log!(
|
||||||
|
"[joinir_vm_bridge] Converting IfMerge: cond={:?}, merges.len()={}",
|
||||||
|
cond, merges.len()
|
||||||
|
);
|
||||||
|
|
||||||
|
// 1. cond ブロック(現在のブロック)
|
||||||
|
let cond_block = current_block_id;
|
||||||
|
|
||||||
|
// 2. then ブロック作成
|
||||||
|
let then_block = BasicBlockId(next_block_id);
|
||||||
|
next_block_id += 1;
|
||||||
|
|
||||||
|
// 3. else ブロック作成
|
||||||
|
let else_block = BasicBlockId(next_block_id);
|
||||||
|
next_block_id += 1;
|
||||||
|
|
||||||
|
// 4. merge ブロック作成
|
||||||
|
let merge_block = BasicBlockId(next_block_id);
|
||||||
|
next_block_id += 1;
|
||||||
|
|
||||||
|
// 5. cond ブロックで分岐
|
||||||
|
let branch_terminator = MirInstruction::Branch {
|
||||||
|
condition: *cond,
|
||||||
|
then_bb: then_block,
|
||||||
|
else_bb: else_block,
|
||||||
|
};
|
||||||
|
finalize_block(&mut mir_func, cond_block, current_instructions, branch_terminator);
|
||||||
|
|
||||||
|
// 6. then ブロック: 各 merge について dst = then_val; jump merge
|
||||||
|
let mut then_block_obj = crate::mir::BasicBlock::new(then_block);
|
||||||
|
for merge in merges {
|
||||||
|
then_block_obj.instructions.push(MirInstruction::Copy {
|
||||||
|
dst: merge.dst,
|
||||||
|
src: merge.then_val,
|
||||||
|
});
|
||||||
|
then_block_obj.instruction_spans.push(Span::unknown());
|
||||||
|
}
|
||||||
|
then_block_obj.terminator = Some(MirInstruction::Jump { target: merge_block });
|
||||||
|
mir_func.blocks.insert(then_block, then_block_obj);
|
||||||
|
|
||||||
|
// 7. else ブロック: 各 merge について dst = else_val; jump merge
|
||||||
|
let mut else_block_obj = crate::mir::BasicBlock::new(else_block);
|
||||||
|
for merge in merges {
|
||||||
|
else_block_obj.instructions.push(MirInstruction::Copy {
|
||||||
|
dst: merge.dst,
|
||||||
|
src: merge.else_val,
|
||||||
|
});
|
||||||
|
else_block_obj.instruction_spans.push(Span::unknown());
|
||||||
|
}
|
||||||
|
else_block_obj.terminator = Some(MirInstruction::Jump { target: merge_block });
|
||||||
|
mir_func.blocks.insert(else_block, else_block_obj);
|
||||||
|
|
||||||
|
// 8. merge ブロック作成(空)
|
||||||
|
let merge_block_obj = crate::mir::BasicBlock::new(merge_block);
|
||||||
|
mir_func.blocks.insert(merge_block, merge_block_obj);
|
||||||
|
|
||||||
|
// 9. merge ブロックに移動
|
||||||
|
current_block_id = merge_block;
|
||||||
|
current_instructions = Vec::new();
|
||||||
|
}
|
||||||
JoinInst::Ret { value } => {
|
JoinInst::Ret { value } => {
|
||||||
// Phase 30.x: Return terminator (separate from instructions)
|
// Phase 30.x: Return terminator (separate from instructions)
|
||||||
let return_terminator = MirInstruction::Return { value: *value };
|
let return_terminator = MirInstruction::Return { value: *value };
|
||||||
|
|||||||
@ -499,6 +499,17 @@ impl NyashRunner {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Phase 33-10.0: If lowering ドライラン統合(箱化版)
|
||||||
|
// HAKO_JOINIR_IF_SELECT=1 で有効化、IfLoweringDryRunner を使用
|
||||||
|
if crate::config::env::joinir_if_select_enabled() {
|
||||||
|
let debug_level = crate::config::env::joinir_debug_level();
|
||||||
|
let runner = crate::mir::join_ir::lowering::if_dry_runner::IfLoweringDryRunner::new(
|
||||||
|
debug_level,
|
||||||
|
);
|
||||||
|
let stats = runner.scan_module(&module_vm.functions);
|
||||||
|
runner.print_stats(&stats);
|
||||||
|
}
|
||||||
|
|
||||||
// Phase 30 F-4.4: JoinIR VM Bridge experimental path (consolidated dispatch)
|
// Phase 30 F-4.4: JoinIR VM Bridge experimental path (consolidated dispatch)
|
||||||
// Activated when NYASH_JOINIR_EXPERIMENT=1 AND NYASH_JOINIR_VM_BRIDGE=1
|
// Activated when NYASH_JOINIR_EXPERIMENT=1 AND NYASH_JOINIR_VM_BRIDGE=1
|
||||||
// Routing logic is centralized in join_ir_vm_bridge_dispatch module
|
// Routing logic is centralized in join_ir_vm_bridge_dispatch module
|
||||||
|
|||||||
@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
use crate::mir::join_ir::frontend::AstToJoinIrLowerer;
|
use crate::mir::join_ir::frontend::AstToJoinIrLowerer;
|
||||||
use crate::mir::join_ir_runner::{run_joinir_function, JoinValue};
|
use crate::mir::join_ir_runner::{run_joinir_function, JoinValue};
|
||||||
|
use crate::mir::join_ir_vm_bridge::run_joinir_via_vm;
|
||||||
|
|
||||||
/// Phase 34-2: IfSelect simple pattern の A/B テスト
|
/// Phase 34-2: IfSelect simple pattern の A/B テスト
|
||||||
///
|
///
|
||||||
@ -138,11 +139,11 @@ fn joinir_frontend_if_select_local_ab_test() {
|
|||||||
// 「値としての if」の本質が Select であることを確認
|
// 「値としての if」の本質が Select であることを確認
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Phase 34-4: JsonShapeToMap._read_value_from_pair/1 の A/B テスト
|
/// Phase 34-6: JsonShapeToMap._read_value_from_pair/1 の完全実装テスト
|
||||||
///
|
///
|
||||||
/// 入力: `fixtures/json_shape_read_value.program.json`
|
/// 入力: `fixtures/json_shape_read_value.program.json`
|
||||||
/// パターン: `if at { return 10 } else { return 20 }` (簡略版・構造確認)
|
/// パターン: `if at { return v.substring(0, at) } else { return v }`
|
||||||
/// 期待: Phase 34-2/34-3 と同じ JoinIR 出力(Select ベース)
|
/// 期待: 本物の substring 呼び出しが JoinIR MethodCall → MIR BoxCall で実行される
|
||||||
#[test]
|
#[test]
|
||||||
fn joinir_frontend_json_shape_read_value_ab_test() {
|
fn joinir_frontend_json_shape_read_value_ab_test() {
|
||||||
// フィクスチャ読み込み
|
// フィクスチャ読み込み
|
||||||
@ -157,7 +158,7 @@ fn joinir_frontend_json_shape_read_value_ab_test() {
|
|||||||
let join_module = lowerer.lower_program_json(&program_json);
|
let join_module = lowerer.lower_program_json(&program_json);
|
||||||
|
|
||||||
// デバッグ: JoinIR Module の内容を確認
|
// デバッグ: JoinIR Module の内容を確認
|
||||||
eprintln!("=== JoinIR Module (json_shape_read_value) ===");
|
eprintln!("=== JoinIR Module (json_shape_read_value - Phase 34-6) ===");
|
||||||
eprintln!("Entry: {:?}", join_module.entry);
|
eprintln!("Entry: {:?}", join_module.entry);
|
||||||
for (func_id, func) in &join_module.functions {
|
for (func_id, func) in &join_module.functions {
|
||||||
eprintln!("\nFunction {:?}: {}", func_id, func.name);
|
eprintln!("\nFunction {:?}: {}", func_id, func.name);
|
||||||
@ -168,38 +169,35 @@ fn joinir_frontend_json_shape_read_value_ab_test() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// JoinIR Runner で実行
|
// Phase 34-6: JoinIR→MIR→VM ブリッジ経由で実行(MethodCall サポート)
|
||||||
let mut vm = crate::backend::mir_interpreter::MirInterpreter::new();
|
|
||||||
|
|
||||||
// at = 1 (truthy) の場合
|
// テストケース 1: v="hello", at=3 → "hel" (substring)
|
||||||
let result_true = run_joinir_function(
|
let result_substring = run_joinir_via_vm(
|
||||||
&mut vm,
|
|
||||||
&join_module,
|
&join_module,
|
||||||
join_module.entry.unwrap(),
|
join_module.entry.unwrap(),
|
||||||
&[JoinValue::Int(1)],
|
&[JoinValue::Str("hello".to_string()), JoinValue::Int(3)],
|
||||||
)
|
)
|
||||||
.expect("Failed to run JoinIR function (at=1)");
|
.expect("Failed to run JoinIR function (v=\"hello\", at=3)");
|
||||||
|
|
||||||
// at = 0 (falsy) の場合
|
// テストケース 2: v="world", at=0 → "world" (v そのまま)
|
||||||
let result_false = run_joinir_function(
|
let result_original = run_joinir_via_vm(
|
||||||
&mut vm,
|
|
||||||
&join_module,
|
&join_module,
|
||||||
join_module.entry.unwrap(),
|
join_module.entry.unwrap(),
|
||||||
&[JoinValue::Int(0)],
|
&[JoinValue::Str("world".to_string()), JoinValue::Int(0)],
|
||||||
)
|
)
|
||||||
.expect("Failed to run JoinIR function (at=0)");
|
.expect("Failed to run JoinIR function (v=\"world\", at=0)");
|
||||||
|
|
||||||
// 検証: at=1 → 10, at=0 → 20 (簡略版・構造確認)
|
// 検証: substring 呼び出し結果
|
||||||
match result_true {
|
match result_substring {
|
||||||
JoinValue::Int(v) => assert_eq!(v, 10, "at=1 should return 10"),
|
JoinValue::Str(s) => assert_eq!(s, "hel", "v.substring(0, 3) should return \"hel\""),
|
||||||
_ => panic!("Expected Int, got {:?}", result_true),
|
_ => panic!("Expected Str, got {:?}", result_substring),
|
||||||
}
|
}
|
||||||
|
|
||||||
match result_false {
|
// 検証: 元の文字列そのまま
|
||||||
JoinValue::Int(v) => assert_eq!(v, 20, "at=0 should return 20"),
|
match result_original {
|
||||||
_ => panic!("Expected Int, got {:?}", result_false),
|
JoinValue::Str(s) => assert_eq!(s, "world", "v should return \"world\""),
|
||||||
|
_ => panic!("Expected Str, got {:?}", result_original),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Phase 34-4: Stage-1/meta 実用関数でも simple pattern が Select JoinIR に正規化されることを実証
|
// Phase 34-6: 本物の Method 呼び出し意味論が JoinIR MethodCall → MIR BoxCall で実行されることを実証
|
||||||
// 構造確認フェーズ(Method 呼び出し意味論は Phase 34-5 で対応)
|
|
||||||
}
|
}
|
||||||
|
|||||||
33
test_joinir_debug.rs
Normal file
33
test_joinir_debug.rs
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
//! JoinIR Frontend Debug Test
|
||||||
|
|
||||||
|
use std::fs;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// フィクスチャ読み込み
|
||||||
|
let fixture_path = "docs/private/roadmap2/phases/phase-34-joinir-frontend/fixtures/joinir_if_select_simple.program.json";
|
||||||
|
let fixture_json = fs::read_to_string(fixture_path)
|
||||||
|
.expect("Failed to read fixture JSON");
|
||||||
|
let program_json: serde_json::Value = serde_json::from_str(&fixture_json)
|
||||||
|
.expect("Failed to parse JSON");
|
||||||
|
|
||||||
|
println!("=== Program JSON ===");
|
||||||
|
println!("{}", serde_json::to_string_pretty(&program_json).unwrap());
|
||||||
|
|
||||||
|
// Lowerer 実行
|
||||||
|
use nyash_rust::mir::join_ir::frontend::AstToJoinIrLowerer;
|
||||||
|
|
||||||
|
let mut lowerer = AstToJoinIrLowerer::new();
|
||||||
|
let join_module = lowerer.lower_program_json(&program_json);
|
||||||
|
|
||||||
|
println!("\n=== JoinIR Module ===");
|
||||||
|
println!("Entry: {:?}", join_module.entry);
|
||||||
|
|
||||||
|
for (func_id, func) in &join_module.functions {
|
||||||
|
println!("\nFunction {:?}: {}", func_id, func.name);
|
||||||
|
println!(" Params: {:?}", func.params);
|
||||||
|
println!(" Instructions:");
|
||||||
|
for (i, inst) in func.body.iter().enumerate() {
|
||||||
|
println!(" {}: {:?}", i, inst);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user