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

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

  • LoopFormControlForm:
    • 既存の LoopForm v2LoopFormBuilder が返す構造)から 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.rsStage1 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 LoopSSAlang/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 で表現):
      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 / exitsexits は BlockId の配列)
  • 将来構文版Layer 3 の理想形・variant 導入前提):
    • 将来の Nyash variant 構文が入ったら、ControlKind を enum 的に表現し、
      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:
      • ControlFormBoxkind_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_withIfや LoopForm v2 Exit PHI が直接 BlockId/ValueId を持っている部分を、 ControlForm 経由で読めるように段階的に寄せていく。
    • 方針:
      • まず If から(ControlKind::If)→ ループ Exit PHIControlKind::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 に設計として落とす。