Files
hakorune/docs/development/current/main/design/edgecfg-fragments.md
tomoaki cda034fe8f feat(edgecfg): Phase 265 P1 - compose 配線ロジック実装(test-only PoC)
## 目的
Frag/ExitKind が BasicBlockId 層で配線できることを証明

## 実装完了内容
- EdgeStub に target: Option<BasicBlockId> 追加
- compose::loop_() 配線ロジック実装(Continue → header, Break → after)
- verify_frag_invariants() 配線契約検証追加
- test-only PoC で実証完了(5個のテスト)

## 配線契約
- Continue(loop_id) の EdgeStub.target = Some(header)
- Break(loop_id) の EdgeStub.target = Some(after)
- Normal/Return/Unwind の EdgeStub.target = None(上位へ伝搬)

## テスト
- compose::tests: 5 PASS(既存2個更新 + 新規3個追加)
- verify::tests: 1 PASS(基本smoke test)
- cargo test -p nyash-rust --lib: SUCCESS

## 重要な制約
- MIR 命令生成はまだしない(Frag 層の配線能力証明のみ)
- NormalizedShadow/JoinIR層への適用は Phase 266 に繰り越し
- Pattern6/7/8 未改変(配線能力の証明に集中)

## 次のステップ
- Phase 265 P2: seq/if_ 実装(順次合成・条件分岐合成)
- Phase 266: JoinIR-VM Bridge 改修後、NormalizedShadow への適用

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-21 16:22:46 +09:00

6.8 KiB
Raw Blame History

EdgeCFG Flow FragmentsFrag / ExitKind— Structured→CFG lowering SSOT

Status: Draftdesign SSOT candidate
Last updated: 2025-12-21

Related:

  • North starCFG/ABI: docs/development/current/main/design/join-explicit-cfg-construction.md
  • Catch/Cleanup/Async: docs/development/current/main/design/exception-cleanup-async.md

目的(なぜ必要?)

EdgeCFGblock-parameterized CFG / edge-args SSOTが固まると、次に残る “泥沼” はここだけになる:

  • 構造化制御if/loop + catch/cleanup→ CFG の lowering で起きる exit 配線問題
  • 「pattern番号で推測分岐」が増殖しやすい領域長期的には消したい

この文書は「pattern番号の列挙」を設計の中心にしないために、Structured→CFG の lowering を **合成代数fragment composition**として SSOT 化する。

結論(本書の北極星):

  • “分岐の中心” は pattern番号ではなく ExitKindFragfragment に置く
  • 値の合流は EdgeCFG の block params + edge-args で表し、PHI/推測/メタに逃げない
  • pattern は「Extractor形の認識/ Plan最小要件の抽出」までに縮退し、merge/配線層へ逆流させない

“フロー” は 2 層ある(混ぜると崩れる)

  1. CFG層EdgeCFG / plumbing

    • terminator 語彙: Jump/Branch/Return/Invoke
    • edge-args: terminator operand が SSOT
    • out_edges の参照点が SSOT複数 edge 前提)
  2. Structured→CFG lowering 層flow composition

    • if/loop/catch/cleanup/seq を “Frag の合成” として書く
    • 難しさの本体は exit脱出の種類ネスト合流

コア概念(最小の強い箱)

ExitKind脱出の種類を一次概念にする

最低限の ExitKind:

  • Normalfallthrough
  • Break(loop_id) / Continue(loop_id)
  • Return
  • UnwindInvoke.err / catch へ)
  • Cancelasync の drop/cancel 用。今は予約)

EdgeStub未配線の脱出エッジ

“どこへ飛ぶべきか未確定” な edge を表す。最終的に EdgeCFG の terminator edge に落ちる。

例(概念):

  • from: BlockId
  • kind: ExitKind
  • args: EdgeArgs(ターゲット params に対応する値。target が未確定でも “役割” はここで決める)

Fragfragment

Frag = { entry_block, exits: Map<ExitKind, Vec<EdgeStub>> }
  • entry_block: 断片の入口
  • exits: 断片から外へ出る未配線 edge の集合

合成則pattern列挙を写像へ落とす

seq(a, b)

  • a.exits[Normal]b.entry へ接続するedge-args を必要に応じて写像)
  • それ以外の exit は上位へ伝搬する

if(cond, t, e)

  • header に Branch(cond, t.entry, e.entry) を置く
  • t.Normale.Normal は join へ集める(必要なら join block params を作る)
  • Break/Continue/Return/Unwind は上位へ伝搬

loop(body)

  • header / latch / after を組み、Continue を header に戻す
  • Break を after へ出す
  • Return/Unwind は上位へ伝搬

cleanup(body, cleanup_block)finally の後継)

狙い: “脱出 edge 正規化”

  • body の全 exitNormal/Break/Continue/Return/Unwind/Cancelを cleanup 経由へリライトする
  • cleanup 後に “元の exit” を再発射するExitTag + payload を block params で運ぶ)

重要: 例外 edgeInvoke.errも漏れなく cleanup に寄せる。

pattern は最終的に消える?(設計としての答え)

消える(実装の中心概念から降格する)。

  • pattern番号は 回帰テスト名/症状ラベルとしては残して良い
  • 実装の中心は Frag/ExitKind/join(block params) の合成則になる
  • 各 pattern 実装は “Extractor形の認識→ Frag 合成呼び出し” の薄い層へ縮退する

実装の入口SSOT API を先に作る)

目的: “どこを触ればいいか” を 1 箇所に固定し、推測・部分続行・場当たり分岐を減らす。

推奨の入口:

  • EdgeCFG の plumbing API既存: BasicBlock::out_edges()
  • Structured→CFG の入口 API新規: Frag / ExitKind / compose::{seq, if_, loop_ , cleanup}

物理配置(案):

  • src/mir/builder/control_flow/edgecfg/api/(または .../joinir/api/ に併設してもよい)
    • frag.rs / exit_kind.rs / compose.rs / patch.rs

verifyFail-Fast の置き場所)

  • NormalizeBox 直後: terminator 語彙固定・edge-args 長さ一致・cond付きJump禁止など “意味SSOT” を確定
  • merge直前: boundary/ABI/edge-args の矛盾を即死させ “配線SSOT” を確定
  • --verify: PHI predecessor / CFG cache 整合 / edge-args の長さ一致を常設

直近の導入ステップ(最小で始める)

  1. Frag/ExitKind/EdgeStub の型を追加docs+code 入口 SSOT
  2. seq/if/loop の合成だけ実装cleanup/Invoke は後段)
  3. 既存 pattern のうち 1 本だけ Frag 合成に寄せるPattern8 推奨)
  4. 2 本目で再利用が見えたら "pattern番号での枝刈り" を削って合成側へ寄せる

実装入口(コード SSOT

Phase 264 で入口API を作成完了

  • 物理配置: src/mir/builder/control_flow/edgecfg/api/
  • コア型: ExitKind, EdgeStub, Frag
  • 合成関数: seq, if_, loop_, cleanupシグネチャのみ、中身TODO
  • 検証: verify_frag_invariants(空実装)

Phase 265 P0 で最小実装完了

  • compose::loop_(): exit集合の分類実装配線なし、P1以降
  • verify_frag_invariants(): 最小検証追加(デバッグガード付き)
  • Pattern8適用: P0ではやらない偽Frag回避、P1から実戦投入

Phase 265 P1: 配線ロジック実装完了

目的: Frag/ExitKind が BasicBlockId 層で配線できることを証明

実装完了内容:

  • EdgeStub に target: Option<BasicBlockId> 追加
  • compose::loop_() が Continue → header, Break → after への配線を実行
  • verify_frag_invariants() が配線契約を検証(デバッグモード)
  • test-only PoC で配線の実証完了5個のテスト

配線契約:

  • Continue(loop_id) の EdgeStub.target = Some(header)
  • Break(loop_id) の EdgeStub.target = Some(after)
  • Normal/Return/Unwind の EdgeStub.target = None上位へ伝搬

Phase 265 P1 のスコープ:

  • Frag 層での配線ロジック
  • BasicBlockId 層でのテスト証明
  • MIR 命令生成Phase 266+
  • NormalizedShadow/JoinIR層への適用Phase 266+、JoinIR-VM Bridge 改修後)

次フェーズPhase 265 P2で seq/if_ 実装 + pattern番号分岐削減へ。 現時点では既存 pattern6/7/8 や merge/EdgeCFG は未改変(配線能力の証明のみ)。