Files
hakorune/docs/development/roadmap/phases/phase-25.1f/README.md
nyash-codex d3cbc71c9b feat(mir): Phase 25.1f完了 - Conservative PHI + ControlForm観測レイヤー
🎉 Conservative PHI Box理論による完全SSA構築

**Phase 7-B: Conservative PHI実装**
- 片方branchのみ定義変数に対応(emit_void使用)
- 全変数にPHI生成(Conservative Box理論)
- Stage-1 resolver全テスト緑化(3/3 PASS)

**Phase 25.1f: ControlForm観測レイヤー**
- LoopShape/IfShape/ControlForm構造定義
- Loop/If統一インターフェース実装
- debug_dump/debug_validate機能追加
- NYASH_CONTROL_FORM_TRACE環境変数対応

**主な変更**:
- src/mir/builder/phi.rs: Conservative PHI実装
- src/mir/control_form.rs: ControlForm構造(NEW)
- src/mir/loop_builder.rs: LoopForm v2デフォルト化

**テスト結果**:
 mir_stage1_using_resolver_min_fragment_verifies
 mir_stage1_using_resolver_full_collect_entries_verifies
 mir_parserbox_parse_program2_harness_parses_minimal_source

🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: ChatGPT <chatgpt@openai.com>
2025-11-18 18:56:35 +09:00

253 lines
14 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 25.1f — ControlFormLoop/If 共通ビュー)& StageB ハーネス準備
Status: planning設計観測レイヤ追加挙動は変えない
## ゴール
- LoopForm v2ループと If まわり(`phi_core::if_phi`)に対して、**制御構造の共通ビュー `ControlForm`** を用意する。
- すでに導入済みの Conservative PHI BoxIf/Loop 用 SSA/PHI ロジック)を、
- ループ専用If 専用でバラバラに持つのではなく、
- `ControlForm` を単一の SSOTSingle Source of Truthとして参照する方向に寄せる。
- StageB 最小ハーネスや Stage1 Resolver など、「複雑な if + loop」を含む経路で、
- ループと条件分岐の形を **構造として観測・検証できる足場** を整える(いきなり本番導線は切り替えない)。
※ このフェーズでは Rust の挙動は変えず、「制御構造の箱LoopShape / IfShape ControlForm の定義」と、
デバッグ/テスト用の観測導線(トレース)までに留める。
## 背景25.1d / 25.1e までの位置づけ)
- 25.1d:
- Rust MIR ビルダーの SSA/PHI バグValueId 二重定義nondominating useundefined Valueを、
小さな Rust テスト(`mir_*_verifies`)で炙り出して修正済み。
- CalleeBoxKind / BoxCompilationContext によって StageB / Stage1 の静的 Box とランタイム Box の混線も構造的に解消。
- 25.1e:
- ループの PHI 生成の SSOT を LoopForm v2 + `phi_core` に寄せ、
- Exit PHI、break/continue、pinned/carrier/invariant 変数を整理。
- Conservative PHI BoxIf/Loop 両方で「安全側に倒す PHI 生成」を行い、不要な PHI は将来の削除で最適化する方針)を実装。
- Stage1 UsingResolver 系テストと Program v0 PHI スモーク、StageB 風ループテストはすべて緑。
ここまでで「LoopForm v2 + Conservative PHI」の根本バグはだいたい取り切れたが、
- PHI ロジックが If 用と Loop 用で散在している。
- StageB ハーネス側で「どの loop/if がどう組み合わさっているか」を横断的に眺める手段がまだ弱い。
→ 25.1f では、**Loop / If を一段上から包む ControlForm レイヤ**を定義し、StageB ハーネスに進む前準備として構造を固める。
## 方針 — ControlForm / Shape の設計
### 型と場所
- 新規モジュール案:
- `src/mir/control_form.rs`
- 基本構造:
- `LoopShape`:
- `preheader: BasicBlockId`
- `header: BasicBlockId`
- `body: BasicBlockId`(代表 body ブロック)
- `latch: BasicBlockId`
- `exit: BasicBlockId`
- `continue_targets: Vec<BasicBlockId>`
- `break_targets: Vec<BasicBlockId>`
- `IfShape`:
- `cond_block: BasicBlockId`
- `then_block: BasicBlockId`
- `else_block: Option<BasicBlockId>`
- `merge_block: BasicBlockId`
- `ControlKind`:
- `Loop(LoopShape)` / `If(IfShape)`(将来 `Switch` / `Match` も追加可能な余地だけ確保)
- `ControlForm`:
- `entry: BasicBlockId`(構造全体の入口)
- `exits: Vec<BasicBlockId>`(構造を抜けた先のブロック群)
- `kind: ControlKind`
### 生成パスfrom_loop / from_if
- `LoopForm``ControlForm`:
- 既存の LoopForm v2`LoopFormBuilder` が返す構造)から `LoopShape` を構築し、
- `ControlForm::from_loop(loop_form: &LoopForm)` で共通ビューに変換する。
- `entry = preheader`, `exits = [exit]` というルールで統一。
- If 用:
- 既存の `phi_core::if_phi` / builder 側の if lowering から、必要な情報をまとめた薄い `IfForm`(もしくは `IfShape` 直構築)を定義。
- `ControlForm::from_if(if_form: &IfForm)` で共通ビューに変換。
- `entry = cond_block`, `exits = [merge_block]` を基本とし、将来 `else if` や switch なども扱える形で設計。
### Invariant / Debug 用の Trait
- 25.1f では「挙動を変えない」ため、ControlForm 自体は **観測・検証専用** として設計する:
- `debug_dump(&self)`:
- Loop / If の各ブロック ID を一括でログ出力し、Stage1 / StageB テストから CFG の形を視覚的に確認できるようにする。
- `CfgLike` トレイト(任意):
- `fn has_edge(&self, from: BasicBlockId, to: BasicBlockId) -> bool;`
- `fn predecessors_len(&self, block: BasicBlockId) -> usize;`
- `LoopShape::debug_validate<C: CfgLike>(&self, cfg: &C)` などを `#[cfg(debug_assertions)]` で用意し、
Loop/If の形が想定どおりかを assert できるようにする。
## タスク粒度25.1f TODO
### F1: ControlForm / Shape の設計ドキュメント
- 本 README で:
- `LoopShape` / `IfShape` / `ControlKind` / `ControlForm` の責務とフィールドを明文化。
- LoopForm v2 との対応関係preheader/header/latch/exit/continue/breakを図解レベルで通るところまで整理。
- 25.1e の LoopForm 仕様Carrier/Pinned/Invariant / break/continue / exit PHIと整合していることを確認。
### F2: Rust 側のスケルトン実装(導線のみ)
- `src/mir/control_form.rs` を追加し、以下のみ実装:
- 型定義(`LoopShape` / `IfShape` / `ControlKind` / `ControlForm`)。
- `ControlForm::from_loop(&LoopForm)` / `ControlForm::from_if(&IfForm)` の雛形。
- `debug_dump()``debug_validate()``#[cfg(debug_assertions)]` 前提、もしくはテスト専用)。
- 既存のビルダー/PHI ロジックは **まだ ControlForm を使わない**:
- 25.1e で安定した Conservative PHI 実装を壊さないため、Phase 25.1f では「観測オンリー」に留める。
### F3: Stage1 / StageB テストへの観測フック
- 対象テスト:
- `src/tests/mir_stage1_using_resolver_verify.rs`Stage1 UsingResolver の min/full
- StageB 風ループProgram v0 PHI スモーク(`mir_loopform_exit_phi.rs` / `mir_stageb_loop_break_continue.rs` / `tools/smokes/v2/...`)。
- 方針:
- `NYASH_CONTROL_FORM_TRACE=1` などの dev フラグが立っているときだけ、
- LoopForm v2 から `ControlForm` を生成し、
- `debug_dump()` で構造をトレースログに出す。
- これにより、StageB ハーネスを組む前に「実際にどんな Loop/If 形が発生しているか」を CFG レベルで確認できる。
### F4: Conservative PHI との接続計画(設計のみ)
- 25.1e で実装した Conservative PHI BoxIf/Loopのロジックを整理し、将来像を設計として固定する:
- 目標: `merge_modified_at_merge_with` / LoopForm v2 Exit PHI 生成などを、
- `ControlForm` ベースの API で呼べるようにする(例: `build_phi_for_control(form: &ControlForm, ...)`)。
- 25.1f では:
- どの関数をどこまで ControlForm 受けにするか、
- 既存 API との互換性をどう保つか、
を README 上で設計するところまで。
- 実際の実装切り替えPHI ロジックを ControlForm ベースに差し替えるは、StageB ハーネスと合わせて **別フェーズ25.1g または 25.2 以降)** に送る。
### F5: StageB ハーネスへの橋渡し準備
- StageB 最小ハーネス(`tools/test_stageb_min.sh` / `lang/src/compiler/tests/stageb_min_sample.hako`)における:
- ループパターン(`skip_ws` などの単純ループ、複数 break/continue を含むループ)。
- if + loop のネスト構造。
を洗い出し、LoopForm v2 + IfShape + ControlForm ですべて表現できることを確認する。
- 将来のタスク(このフェーズでは設計のみ):
- StageB ハーネスの SSA/PHI 検証を `ControlForm` 経由で行う Rust テストをどう設計するかをまとめる。
- StageB 側の .hako LoopSSA`lang/src/compiler/builder/ssa/loopssa.hako`が、LoopForm v2 / ControlForm の設計と齟齬を起こさないように、必要な制約・前提条件を明記する。
### F6: .hako 側 ControlFormBoxLayer 2 の箱)定義
- 目的:
- Rust 側 `LoopShape` / `IfShape` / `ControlForm` に対応する **Hakorune 実装版の箱** を用意し、
StageB / LoopSSA が Rust と同じモデルでループ/if を扱えるようにする。
- 将来の LoopSSA 実装は、この箱経由で構造を受け取ることを前提に設計する。
- 現行構文版Layer 2:
- ファイル案: `lang/src/shared/mir/control_form_box.hako`
- 定義案BlockId は現状 i64/IntegerBox で表現):
```hako
static box ControlFormBox {
kind_name: StringBox // "loop" or "if"
entry: IntegerBox // BasicBlockId 相当
exits: ArrayBox // of IntegerBox (BlockId)
// Loop fields (kind_name == "loop" の時のみ有効)
loop_preheader: IntegerBox
loop_header: IntegerBox
loop_body: IntegerBox
loop_latch: IntegerBox
loop_exit: IntegerBox
// If fields (kind_name == "if" の時のみ有効)
if_cond: IntegerBox
if_then: IntegerBox
if_else: IntegerBox
if_merge: IntegerBox
birth(kind) {
me.kind_name = kind
me.exits = new ArrayBox()
}
is_loop() {
return me.kind_name == "loop"
}
is_if() {
return me.kind_name == "if"
}
}
```
- 対応表Rust ↔ .hako:
- Rust `LoopShape.preheader/header/body/latch/exit`
↔ Hako `loop_preheader/loop_header/loop_body/loop_latch/loop_exit`
- Rust `IfShape.cond_block/then_block/else_block/merge_block`
↔ Hako `if_cond/if_then/if_else/if_merge`
- Rust `ControlForm.entry/exits`
↔ Hako `entry` / `exits``exits` は BlockId の配列)
- 将来構文版Layer 3 の理想形・variant 導入前提):
- 将来の Nyash variant 構文が入ったら、`ControlKind` を enum 的に表現し、
```hako
box ControlKind {
variant Loop { preheader: BlockIdBox, header: BlockIdBox, ... }
variant If { cond_block: BlockIdBox, then_block: BlockIdBox, ... }
}
static box ControlFormBox {
kind: ControlKind
entry: BlockIdBox
exits: ArrayBox
}
```
のような形に寄せる(現段階では設計メモのみ)。
- 25.1f でのスコープ:
- `control_form_box.hako` に上記 Layer 2 版 `ControlFormBox` を実装する。
- ただし StageB / LoopSSA からはまだ使用しない(コンパイルが通る最小限の実装+将来用の箱)。
- StageB LoopSSA 統合ControlFormBox を実際に使って JSON v0→MIR を整理する)は、次フェーズに回す。
## 移行ステップOption A → B → C のロードマップ)
このフェーズ25.1f)では、あくまで **Option A = 観測レイヤーの完成** までをスコープにするよ。その上で、将来の B/C も含めて合意しておきたい移行順序はこうだよ:
1. **Option A: 観測レイヤーを完璧にするPhase 25.1f**
- Rust:
- `LoopShape` / `IfShape` / `ControlForm` 実装済み。
- `LoopBuilder` から LoopForm v2 / `lower_if_in_loop` の出口で `ControlForm::Loop/If` を生成して `debug_dump` 済み。
- `NYASH_CONTROL_FORM_TRACE` は「未設定=ON, 0/false=OFF」のヘルパーに統一済み。
- .hako:
- `ControlFormBox`kind_name + loop_* / if_* + entry/exitsの箱だけ定義まだ未使用
- テスト/スモーク:
- `mir_stage1_using_resolver_*` / `mir_loopform_exit_phi.rs` / `mir_stageb_loop_break_continue.rs` / `tools/test_stageb_min.sh` を回しつつ、
ControlForm トレースが panic せずに構造ログだけを出すことを確認する。
2. **Option B: Conservative PHI ↔ ControlForm の統合(別フェーズ: 25.1g 想定)**
- ねらい:
- いま `merge_modified_at_merge_with`Ifや LoopForm v2 Exit PHI が直接 BlockId/ValueId を持っている部分を、
`ControlForm` 経由で読めるように段階的に寄せていく。
- 方針:
- まず If から(`ControlKind::If`)→ ループ Exit PHI`ControlKind::Loop`)の順に小さく進める。
- 各ステップごとに:
- 対応するテストStage1 / Program v0 / StageB 風)+ `test_stageb_min.sh` を回し、
SSA/PHI の赤ログが増えていないことを確認しながら移行する。
- 注意:
- これは 25.1f のスコープ外。Phase 25.1g(仮)として小さな差分に分割しながら実装する。
3. **Option C: LoopBuilder/If 降下の ControlForm ベース正規化(さらに後ろのフェーズ)**
- ねらい:
- 最終的には「LoopBuilder/If 降下がまず ControlForm を組み立てる」スタイルに寄せ、
Conservative PHI / LoopSSA / .hako 側 LoopSSA が同じ ControlForm を揃って見る状態にする。
- 規模:
- 数百行単位のリファクタになるため、Phase 25.2 以降Selfhost/StageB の安定を見ながら)に分割してやる。
- ガード:
- 各ステップで代表スモークStage1 / StageB / Program v0 / `test_stageb_min.sh`)を流しながら進める。
- `NYASH_CONTROL_FORM_TRACE` と `.hako ControlFormBox` を使って、常に「形」が崩れていないかを観測できるようにしておく。
このフェーズ25.1fは「Option A をやり切って足場を固める」ことに専念し、その上で 25.1g 以降で Option B/C に進む、というロードマップで進めるよ。
## このフェーズで「やらないこと」
- if を loop に書き換えるような、意味論レベルの正規化は **行わない**:
- If は IfShape、Loop は LoopShape として、それぞれの形を尊重する。
- StageB ハーネス本体の導入・既存パイプラインの切り替え:
- `test_stageb_min.sh` の経路変更や `.hako` コンパイラ側の LoopSSA 実装変更は、25.1f では触らず、
ControlForm の設計と観測レイヤ整備が完了してから別フェーズで扱う。
- Conservative PHI ロジックの大規模リライト:
- 25.1e で安定させた If/Loop PHI 実装を、いきなり ControlForm ベースに書き換えることはしない。
- まずは「どう書き換えるのが構造的に美しいか」を、このフェーズの README に設計として落とす。