refactor(mir): phase260 p0 edge-args plumbing (strangler) + ssot api + docs

This commit is contained in:
2025-12-21 04:34:22 +09:00
parent 4496b6243d
commit 4dfe3349bf
42 changed files with 1044 additions and 187 deletions

View File

@ -0,0 +1,167 @@
# Phase 259: Block-Parameterized CFG / Join-Explicit CFG — ChatGPT Pro 相談パケット
目的: ChatGPT Pro に「正規化normalizedをどう設計し、小さい強い箱Boxで結び、block-parameterized CFG へ段階移行するか」を相談するためのコピペ用 SSOT を用意する。
更新日: 2025-12-20
---
## 1) ChatGPT Pro へ投げる文章(コピペ用)
以下の条件で、段階移行できる設計を提案してください。
### コンテキスト
Nyash/Hakorune の JoinIR→MIR 経路で、`Jump/continuation/params/edge-args` が「暗黙 ABI順序/長さ/役割/識別)」として散在し、パターン追加のたびに merge/optimizer/verify が連鎖で壊れました。最近は SSOT 化と Fail-Fast を進め、`JumpArgsLayout` を boundary に持たせて推測を排除しましたが、最終的には **block-parameterized CFG**edge-args を第一級に持つ CFGに収束させたいです。
北極星north star: “Join-Explicit CFG Construction”
### ゴール(最終形)
- `Jump/continuation/params/edge-args` を第一級explicitとして扱う
- JoinIR↔MIR 間の暗黙 ABI を消し、変換を「意味解釈」ではなく「写像mapping」に縮退する
- 長期的には JoinIR を builder DSL に降格できる状態にする(削除は急がない)
### 制約 / 方針
- フォールバックより Fail-Fastprod/CI 既定で安全)
- by-name ハードコード分岐は禁止(特定 Box 名での条件分岐など)
- 新しい環境変数の増殖は禁止(既存 debug gate の範囲で)
- 段階導入Stranglerで、差分を小さく・可逆に
### 質問(回答してほしいこと)
1. **Normalized JoinIR の最小語彙**は何がよいか?
- `Jump(args)` / `Branch(then_args, else_args)` / `Return` / `Call` のセット
- `cond 付き Jump` を禁止し、`Branch` に寄せるべきか
2. **ABI役割・順序SSOT をどこに置くべきか?**
- `JoinAbi`sig/roles/special conts/aliasを新設するか、`JoinModule` に持たせるか
- boundary の `join_inputs/host_inputs` を Vec 順序のまま段階移行する案(最小差分)と、`ParamId→ValueId` 束縛にする案(最終形)
3. **block-parameterized CFG 移行の最小ステップ**を、Phase で刻んで提示してほしい
- `jump_args` を BasicBlock メタから terminator operand に埋め込む手順(併存→移行→削除)
- verify / optimizer / printer / builder への影響の最小化
4. **箱Box分割**は最小でどう切るべきか?
- “推測禁止” と “Fail-Fast” をどの箱に閉じ込めるか
- 例: `AbiBox`, `BoundaryContractCheckBox`, `ExitArgsPlumbingBox`, `CfgSuccessorSyncBox`
5. **不変条件invariants**と検証地点の設計
- Normalizer直後 / merge直前 / `--verify` の各地点で、何を verify するのが最短で強いか
### 期待する出力(形式)
- 「いきなり最終形」ではなく、**段階移行1フェーズ=小差分)**のマイルストーンを提示
- 各フェーズの **受け入れ基準**smoke/verify/contractを明文化
- 新規箱は最小2〜3個から開始で、増やす判断基準も書く
---
## 2) リポジトリ現状SSOTメモ
### 最近の到達点(コミット)
- `73ddc5f58` feat(joinir): Phase 257 P1.1/P1.2/P1.3Pattern6 SSOT / PHI predecessor verify / LoopHeaderPhi 修正)
- `23531bf64` feat(joinir): Phase 258 P0index_of_string dynamic needle window scan
- `e4f57ea83` docs: Phase 257-259 SSOT 更新
### 現在の “意味データが IR 外” の例
- `jump_args``BasicBlock` のメタとして存在DCE/verify が追う必要がある)
- spans が `instructions` と並行 Vec同期漏れで SPAN MISMATCH が起きる)
---
## 3) 最低限のソースコード断片Pro に見せる用)
### 3.1 `BasicBlock`jump_args と span の現状)
`src/mir/basic_block.rs:45`
```rust
pub struct BasicBlock {
pub id: BasicBlockId,
pub instructions: Vec<MirInstruction>,
pub instruction_spans: Vec<Span>,
pub terminator: Option<MirInstruction>,
pub terminator_span: Option<Span>,
pub predecessors: BTreeSet<BasicBlockId>,
pub successors: BTreeSet<BasicBlockId>,
// ...
pub jump_args: Option<Vec<ValueId>>, // Phase 246-EX: Jump args metadata
}
```
### 3.2 `JumpArgsLayout`(推測排除の SSOT
`src/mir/join_ir/lowering/inline_boundary.rs:107`
```rust
pub enum JumpArgsLayout {
CarriersOnly,
ExprResultPlusCarriers,
}
```
### 3.3 Exit args collectionlayout に従うだけ)
`src/mir/builder/control_flow/joinir/merge/exit_args_collector.rs:94`
```rust
pub fn collect(
&self,
exit_bindings: &[LoopExitBinding],
remapped_args: &[ValueId],
block_id: BasicBlockId,
strict_exit: bool,
layout: JumpArgsLayout,
) -> Result<ExitArgsCollectionResult, String>
```
### 3.4 DCE が jump_args を use として数える(現状の暫定対応)
`src/mir/passes/dce.rs:40`
```rust
if let Some(args) = &block.jump_args {
for &u in args {
used_values.insert(u);
}
}
```
---
## 4) 相談したい設計上の痛点(要約)
- `jump_args` がメタなので、最適化/検証/表示/CFG更新が「忘れると壊れる」になりやすい
- continuation の識別が ID/名前/legacy alias で揺れると merge が壊れる
- “順序” が暗黙だと、expr_result と carrier が同一 ValueId のときにズレて誤配線になりやすい
- spans が並行 Vec だと、パスが1箇所でも同期を忘れると壊れる
- `this`/`me` の表面名と内部 `variable_map` キーがズレると、Pattern 側で receiver を取り違えやすいSSOT不足
- `Branch` が入ると “edge-args の参照点” が曖昧になりやすいthen/else のどちらかだけ見て事故る)
---
## 5) 移行の北極星(既存 SSOT へのリンク)
- North Star: `docs/development/current/main/design/join-explicit-cfg-construction.md`
- Phase 256 で露出した契約論点: `docs/development/current/main/investigations/phase-256-joinir-contract-questions.md`
## 6) 追加質問receiver SSOT
`me` receiver の host ValueId を Pattern 側が直接 `"me"`/`"this"` で参照しないように、API/Box として封印したい。
- どの層に置くべきか?(例: `joinir/api/receiver.rs`
- Fail-Fast の位置builder で未登録なら即死pattern detect の時点で弾く?)
- `this`/`me` の将来拡張Stage-3/4に耐える最小設計は
---
## 7) ChatGPT Pro 追記(設計レビュー観点)
段階移行ロードマップの細部で、次の2点を優先して “迷子防止” を強化したい。
1. **edge-args の参照 API は Branch 前提にする**
- `edge_args()` 単発は曖昧になりやすい
- 推奨: `out_edges()` / `edge_args_to(target)` のように「edge を列挙」できる形を SSOT にする
2. **terminator operand の edge-args は “意味付き” にする**
- `Vec<ValueId>` だけだと layout`CarriersOnly` / `ExprResultPlusCarriers`)の推測が残る
- 最小: `EdgeArgs { layout: JumpArgsLayout, values: Vec<ValueId> }` を同梱(将来は `ContSigId` に置換)

View File

@ -0,0 +1,92 @@
# Phase 260: Block-Parameterized CFGedge-args段階導入
Status: Planned
Last updated: 2025-12-20
## 目的P0
JoinIR→MIR の暗黙 ABIjump_args / carriers / expr_result slot / successorsを減らし、将来的な **block-parameterized CFG**edge-args を第一級に持つCFGへ収束するための「大工事パート」を開始する。
このフェーズは “一括置換” ではなく、**併存導入Strangler**で可逆に進める。
## 背景Phase 256-259 で露出した型)
- `jump_args` が IR 外メタとして存在すると、DCE/verify/CFG 更新が「忘れると壊れる」になる
- spans が並行 Vec だと、最適化や変換で同期漏れが起きやすい
- continuation / entry / exit の識別と args 順序が散在すると、推測・補正が増殖する
North Star: `docs/development/current/main/design/join-explicit-cfg-construction.md`
## 方針2段正規化
- **Semantic Normalization意味SSOT**: terminator 語彙の固定(例: cond付きJumpを正規形から禁止しBranchへ
- **Plumbing Normalization配線SSOT**: edge-args / CFG successor / spans を IR 構造に閉じ込め、写像に縮退
## スコープPhase 260
### In scope
- MIR に「edge-args を持つ terminator 表現」を **併存導入**する(旧 `BasicBlock.jump_args` は残す)
- “読む側” を単一APIに寄せる`Branch` を含むので “複数 edge” 前提で一本化する)
- 例: `block.out_edges()` / `block.edge_args_to(target)`
- 互換期間は **一致検証を Fail-Fast**(両方ある場合は矛盾で即死)
### Out of scopeP0ではやらない
- `BasicBlock.jump_args` の削除(削除は Phase 261+
- spans の内部表現を `Vec<Spanned<_>>` に一気に切替Phase 261+ で段階導入)
- JoinIR を削除するbuilder DSL 降格は長期)
## 実装タスクP0
1. `MirTerminator`(または既存 terminator に edge-args を持てる variantを追加併存導入
- `Jump` だけでなく `Branch` を含むため、API は “複数 edge” を前提にする
2. bridge が `Jump/Branch` の edge-args を terminator operand としてもセット旧jump_argsも併記してよい
3. merge/ExitLine/DCE/verify/printer が参照する入口を一本化(読む側の Strangler
- 推奨: `block.out_edges()` / `block.edge_args_to(target)` のような API`edge_args()` 単発は Branch で曖昧)
4. Fail-Fast 契約チェック(`--verify` 時に必須)
- “両方ある場合は一致” を verify で保証
- 追加: “terminator から計算した successors” と “block.successors キャッシュ” の一致も verify で保証(同期漏れを即死)
## 受け入れ基準P0
- `cargo build --release` が通る
- `./tools/smokes/v2/run.sh --profile quick` が少なくとも悪化しないsame first FAIL 以上)
- `--verify` の既存テストが壊れないPHI/CFG検証が健全
- legacy 依存の “推測” が増えていない(新規の env var 追加なし)
- `rg "jump_args"` を走らせて、移行コードと API 以外に参照が増えていない(読む側の寄せ漏れを検出)
- DCE 回帰が 1 本以上あり、「edge-args だけで使われる値」が消されないことを固定できている
## ロードマップP0→P3
### P0併存導入の芯
- **単一参照API**を作るBranch を含むので “複数 edge” 前提)
- 例: `BasicBlock::out_edges()` / `BasicBlock::edge_args_to(target)`
- MIR terminator に edge-args を持てる表現を追加(旧 `jump_args`**併存**
- 推奨: `EdgeArgs { layout: JumpArgsLayout, values: Vec<ValueId> }` のように “意味layout” も同梱する
- bridge が edge-args を terminator operand に必ず埋める旧jump_argsも同内容でセットしてよい
- merge/ExitLine/DCE/verify/printer は参照点を `out_edges()`/`edge_args_to(...)` に寄せる
- 両方ある場合の **一致検証を Fail-Fast**`--verify` で必須)
### P1切替
- `jump_args` を読む経路を段階的に減らす(参照点は `out_edges()`/`edge_args_to(...)` のみ)
- terminator 更新の **API一本化**successors/preds の同期漏れを構造で潰す)
- 読む側だけでなく、書く側terminator 設定/edge-args 設定)も API 経由に寄せる
- DCE/verify が terminator operand から自然に use/pred を追えることを固定する
### P2削除
- `BasicBlock.jump_args` を削除(併存チェックも撤去)
- `jump_args` 特例の DCE/verify コードを削除terminator operand が SSOT
### P3spans 収束)
- `instructions` + `instruction_spans` の並行 Vec を段階導入で廃止
- 先に編集APIを一本化 → 最終的に `Vec<Spanned<MirInstruction>>`
## メモ設計SSOT
- 相談パケット: `docs/development/current/main/investigations/phase-259-block-parameterized-cfg-consult.md`
- decisions: `docs/development/current/main/20-Decisions.md`