Files
hakorune/docs/development/roadmap/phases/phase-26-G
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
..

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) LoopFormOpsmir_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) の中で
        • MirQueryBoxexit_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 スキャン実装済み」を追記。