Files
hakorune/docs/development/roadmap/phases/phase-26-G/README.md
nyash-codex 2692eafbbf feat(mir): Phase 26-H JoinIR型定義実装完了 - ChatGPT設計
## 実装内容(Step 1-3 完全達成)

### Step 1: src/mir/join_ir.rs 型定義追加
- **JoinFuncId / JoinContId**: 関数・継続ID型
- **JoinFunction**: 関数(引数 = φノード)
- **JoinInst**: Call/Jump/Ret/Compute 最小命令セット
- **MirLikeInst**: 算術・比較命令ラッパー
- **JoinModule**: 複数関数保持コンテナ
- **単体テスト**: 型サニティチェック追加

### Step 2: テストケース追加
- **apps/tests/joinir_min_loop.hako**: 最小ループ+breakカナリア
- **src/tests/mir_joinir_min.rs**: 手書きJoinIR構築テスト
  - MIR → JoinIR手動構築で型妥当性確認
  - #[ignore] で手動実行専用化
  - NYASH_JOINIR_EXPERIMENT=1 トグル制御

### Step 3: 環境変数トグル実装
- **NYASH_JOINIR_EXPERIMENT=1**: 実験モード有効化
- **デフォルト挙動**: 既存MIR/LoopForm経路のみ(破壊的変更なし)
- **トグルON時**: JoinIR手書き構築テスト実行

## Phase 26-H スコープ遵守
 型定義のみ(変換ロジックは未実装)
 最小限の命令セット
 Debug 出力で妥当性確認
 既存パイプライン無影響

## テスト結果
```
$ NYASH_JOINIR_EXPERIMENT=1 cargo test --release mir_joinir_min_manual_construction -- --ignored --nocapture
[joinir/min] MIR module compiled, 3 functions
[joinir/min] JoinIR module constructed:
[joinir/min]  JoinIR型定義は妥当(Phase 26-H)
test result: ok. 1 passed; 0 failed
```

## JoinIR理論の実証
- **φノード = 関数引数**: `fn loop_step(i, k_exit)`
- **merge = join関数**: 分岐後の合流点
- **ループ = 再帰関数**: `loop_step` 自己呼び出し
- **break = 継続呼び出し**: `k_exit(i)`

## 次フェーズ (Phase 27.x)
- LoopForm v2 → JoinIR 自動変換実装
- break/continue ハンドリング
- Exit PHI の JoinIR 引数化

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

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: ChatGPT <noreply@openai.com>
2025-11-23 04:10:12 +09:00

110 lines
6.0 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 26-G — Exit Liveness MIR Scan (実装フェーズ)
Status: planning
## ゴール(何を達成するフェーズか)
- ExitLivenessProvider の本実装MIR 命令列スキャン版を作り、FuncScanner のカナリア
- `mir_funcscanner_skip_ws_direct_vm`
- `mir_funcscanner_parse_params_trim_min_verify_and_vm`
を緑にする。
- 26-F で用意した差し替え口MirScanExitLivenessに、use/def ベースの liveness を実装する。
- `NYASH_EXIT_LIVE_ENABLE=1` で MIR スキャン版を有効化し、デフォルトは従来挙動のまま安全側。
## スコープ(このフェーズでやること)
- LoopFormOps / LoopBuilder / JSON bridge (LoopFormJsonOps) に「MirFunction への参照を返す API」を追加する設計と最小実装。
- MirScanExitLiveness を `MirQuery` ベースの use/def スキャンに置き換える。
- 代表テストFuncScanner カナリア)で緑を確認するところまで。
## 実装ステップ(順序)
### Step 1: API 設計と LoopFormOps 拡張
- 目的: Exit 側から MirFunctionまたは MirQueryに到達できるようにする。
- 具体案:
- `LoopFormOps` に「MirFunction への参照を返すメソッド」を追加する:
- 例: `fn mir_function(&self) -> &MirFunction;`
- 実装箇所:
- `impl<'a> LoopFormOps for LoopBuilder<'a>`:
- `self.parent_builder.current_function.as_ref().expect(...)` 経由で &MirFunction を返す。
- `impl LoopFormOps for LoopFormJsonOps<'_>`:
- 既に `f: &mut MirFunction` を保持しているので、その参照を返す。
- テスト用 `MockOps` など:
- 最小限の MirFunction スタブを持つ or `unimplemented!()` でテストを段階的に更新。
### Step 2: ExitPhiBuilder に MirQuery フックを追加
- 目的: Exit PHI 生成時に MirQuery にアクセスできるようにする。
- 具体案:
- `ExitPhiBuilder::build_exit_phis` のシグネチャを拡張:
- 現状: `fn build_exit_phis<O: LoopFormOps>(..., exit_snapshots, pinned, carrier) -> Result<(), String>`
- 追加: `mir_query: &dyn MirQuery` を引数に足す、または内部で `ops.mir_function()` から `MirQueryBox` を組み立てる。
- 検討:
- a) `build_exit_phis``&dyn MirQuery` を直渡し → テストで差し替えやすい。
- b) `LoopFormOps``mir_function()` を足し、`ExitPhiBuilder` 側で `MirQueryBox::new(ops.mir_function())` を作る。
- このフェーズでは b) 案を優先(既に LoopBuilder/JSON bridge は MirFunction を握っているため)。
### Step 3: MirScanExitLiveness の本実装use/def スキャン)
- 前提: Step 2 で `MirQueryBox` を構築できる。
- 実装方針:
- 入口:
- `MirScanExitLiveness::compute_live_at_exit(header_vals, exit_snapshots)` の中で
- `MirQueryBox``exit_blocks` を受け取れるようにする(必要なら `ExitLivenessProvider` トレイトのシグネチャ拡張も検討)。
- 最小アルゴリズム:
1. スキャン対象ブロック集合:
- exit ブロックLoopShape.exit exit_snapshots に現れる break 元ブロック。
2. 初期 live 集合:
- 対象ブロックの命令を後ろから走査し、`reads_of(inst)` を live に追加。
3. 1-step backward 伝播:
- それぞれのブロックで `writes_of(inst)` で kill、`reads_of(inst)` で add。
- `succs(bb)` が対象集合に含まれている場合、succ の live を bb に流す。
4. 固定点反復:
- live 集合が変わらなくなるまで 2〜3 を繰り返す。
5. 名前へのマッピング:
- header_vals / exit_snapshots に現れる `(name, ValueId)` を逆引きテーブルに集約し、
live に含まれる ValueId に対応する name だけを `live_at_exit` に含める。
- 返り値:
- `BTreeSet<String>`BodyLocalPhiBuilder からそのまま使える)。
- 既存ロジックとの整合:
- BodyLocalInternal でも「exit後で本当に live」なものだけが rescue されることが期待される。
### Step 4: ExitPhiBuilder / BodyLocalPhiBuilder 統合確認
- 目的: 新しい live_at_exit を使っても PhiInvariantsBox で落ちないことを確認する。
- 作業:
- `BodyLocalPhiBuilder::filter_exit_phi_candidates`
- `class.needs_exit_phi()` (Pinned/Carrier/BodyLocalExit) と
- `live_at_exit` + `is_available_in_all` の OR を行っていることを再確認。
- `PhiInvariantsBox::ensure_exit_phi_availability`
- 「選ばれた変数はすべての exit pred で定義済み」であることを保証し、
- それでも穴があれば即 Fail-Fast で教えてくれることを前提に、MirScan 側のロジックを調整。
### Step 5: カナリア検証
- コマンド:
- `NYASH_EXIT_LIVE_ENABLE=1 cargo test --release --lib mir_funcscanner_skip_ws_direct_vm`
- `NYASH_EXIT_LIVE_ENABLE=1 cargo test --release --lib mir_funcscanner_parse_params_trim_min_verify_and_vm`
- 期待:
- MirVerifier が `use of undefined value` を報告しない。
- VM 実行で RC が期待通りskip_ws の sentinel が正しく動き、trim/parse_params も undefined を出さない)。
### Step 6: ドキュメント更新
- `loopform_ssot.md` に:
- MirQuery / MirQueryBox の役割と ExitLiveness との関係。
- 「MirScanExitLiveness は 26-G で use/def スキャン実装済み」と記載。
- `phase-26-G/README.md` 自体も「実装完了」セクションを追記。
## やらないこと
- Loop 形状や PHI 生成ロジックの意味変更ExitPhiBuilder/BodyLocalPhiBuilder のアルゴリズム変更はしない)。
- env 名の変更や追加(既存の `NYASH_EXIT_LIVE_ENABLE` を継続利用)。
## 受け入れ条件
- `NYASH_EXIT_LIVE_ENABLE=0/未設定` で従来のテスト結果を維持。
- `NYASH_EXIT_LIVE_ENABLE=1` で FuncScanner カナリアが緑MirVerifier/VM
- docs 更新: 26-F/loopform_ssot に「MIR スキャン実装済み」を追記。