Update phase-260/README.md: - Status: P2 完了 ✅(EdgeCFG SSOT確立) - Add P2 completion record with test results - Add EdgeCFG SSOT definition (Jump/Branch: terminator SSOT, Return: metadata exception) - Document unified API (read/write separation) - Note Option B' trade-off (Return metadata exception) Update 10-Now.md: - Add Phase 260 P2 completion entry (2025-12-21) - Test results: 1368 lib tests, 45/46 smoke, phase258 tail call - Verification: 0 jump_args references (comments excluded) - 9 files modified 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
9.5 KiB
Phase 260: Block-Parameterized CFG(edge-args)段階導入
Status: P2 完了 ✅(EdgeCFG SSOT確立) Last updated: 2025-12-21
進捗(セーブポイント)
- P0(併存導入の芯): 完了(読む側 SSOT を
out_edges()/edge_args_to()に寄せた)- Commit:
4dfe3349b
- Commit:
- P0.1(hardening): 完了(legacy layout 無し禁止 + DCE/verify の参照点整理)
- Commit:
1fe5be347
- Commit:
- P0.2(instruction_rewriter モジュール化): 完了
- Commits:
cbed040a7,aa3fdf3c1,c2e8099ff,84cd653ae,875bfee1b,666de9d3e - 抽出モジュール: exit_collection, block_allocator, merge_variable_handler, call_generator, handlers/(8ファイル)
- Commits:
- P0.3(joinir_block_converter モジュール化): 完了 ✅
- Commits:
e7f9adcfe,2c01a7335 - Phase 1: terminator_builder, block_finalizer
- Phase 2: handlers/(call, jump, conditional_method_call, if_merge, nested_if_merge)
- 合計抽出: 15モジュール、約3941行、53単体テスト全てpass
- 統合テスト: 45/46 pass(既知のPattern2 LoopBodyLocal問題1件のみ)
- Commits:
- P2(削除): 完了 ✅
- BasicBlock.jump_args 完全削除、edge-args SSOT を terminator operand 側に一本化
- フィールド変更:
jump_args→return_env(Return専用metadata) - API簡略化: legacy helper 8個削除、terminator更新API保持(successors同期維持)
- Verification簡素化: dual-source検証削除(cfg.rs 62行削除)
- テスト結果: cargo test --lib 1368 PASS、quick smoke 45/46 PASS、phase258 tail call PASS
- 検証:
rg 'jump_args' src/= 0件(コメント除く)
P0.2/P0.3 モジュール分割の責務一覧
Utilities(7モジュール)
- block_allocator.rs: ブロックID割り当て統一(allocate_one/two/three/n)
- merge_variable_handler.rs: マージコピー生成(emit_merge_copies, MergeBranch)
- call_generator.rs: Call命令生成統一(emit_call_pair, emit_call_pair_with_spans)
- terminator_builder.rs: Terminator生成統一(create_branch/jump/return_terminator, emit_branch_and_finalize)
- block_finalizer.rs: PHI保持ブロック確定(finalize_block, finalize_remaining_instructions)
- exit_collection.rs: Exit値収集(collect_exit_values, ExitCollectionResult)
- convert.rs: MirLikeInst変換(convert_mir_like_inst)
Handlers(8モジュール)
- handlers/ret.rs: Return処理
- handlers/method_call.rs: MethodCall → BoxCall変換
- handlers/field_access.rs: FieldAccess → BoxCall(getter pattern)
- handlers/new_box.rs: NewBox命令処理
- handlers/select.rs: Select命令(直接、展開なし)
- handlers/call.rs: Call + tail call処理(Phase 131 P2: 安定ValueId、legacy jump_args)
- handlers/jump.rs: Jump → tail call変換(Phase 256 P1.9: continuation、Phase 246-EX: legacy jump_args)
- handlers/conditional_method_call.rs: ConditionalMethodCall → if/phi展開(単一変数)
- handlers/if_merge.rs: IfMerge → if/phi(複数変数)
- handlers/nested_if_merge.rs: NestedIfMerge → 多段分岐(N+3ブロック、最複雑)
SSOT維持の確認項目(Phase 260の契約)
✅ jump_args直参照ゼロ維持: 全モジュールでBasicBlock.jump_argsへの直接参照なし(out_edges()/edge_args_to()経由のみ)
✅ out_edges()/edge_args_to() SSOT維持: 読む側APIが単一参照点に統一(Branchを含む複数edge前提)
✅ terminator operand優先: 新規コードはterminator operandを優先的に使用(legacy jump_argsは併存のみ)
✅ PHI preservation: block_finalizer.rsでPhase 189 FIX(PHI命令をブロック先頭保持)を維持
✅ Phase metadata保持: Phase 131 P2(安定ValueId)、Phase 246-EX(legacy jump_args)、Phase 256 P1.9(continuation名前解決)の契約を全て保持
EdgeCFG SSOT確立(P2完了後)
Edge-args の SSOT定義:
- Jump/Branch: terminator operand
edge_argsが SSOT ✅ - Return: metadata
return_envが SSOT(例外、terminator に operand フィールドなし)⚠️
統一API:
- 読み出し:
out_edges(),edge_args_to(),edge_args_from_terminator() - 書き込み:
set_jump_with_edge_args(),set_branch_with_edge_args(),set_return_env()- 直代入禁止(successors 同期漏れ防止、Phase 260/261の核心成果)
妥協点の明記:
- Returnは「CFG successorを持たない」semantic上の特殊ケースのため、metadata許容
- 理想形(Option A)はReturn terminatorにenvフィールド追加だが、P2では工数削減のためOption B'採用
目的(P0)
JoinIR→MIR の暗黙 ABI(jump_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 scope(P0ではやらない)
BasicBlock.jump_argsの削除(削除は Phase 261+)- spans の内部表現を
Vec<Spanned<_>>に一気に切替(Phase 261+ で段階導入) - JoinIR を削除する(builder DSL 降格は長期)
実装タスク(P0)
MirTerminator(または既存 terminator に edge-args を持てる variant)を追加(併存導入)JumpだけでなくBranchを含むため、API は “複数 edge” を前提にする
- bridge が
Jump/Branchの edge-args を terminator operand としてもセット(旧jump_argsも併記してよい) - merge/ExitLine/DCE/verify/printer が参照する入口を一本化(読む側の Strangler)
- 推奨:
block.out_edges()/block.edge_args_to(target)のような API(edge_args()単発は Branch で曖昧)
- 推奨:
- 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)
P3(spans 収束)
instructions+instruction_spansの並行 Vec を段階導入で廃止- 先に編集APIを一本化 → 最終的に
Vec<Spanned<MirInstruction>>へ
- 先に編集APIを一本化 → 最終的に
メモ(設計SSOT)
- 相談パケット:
docs/development/current/main/investigations/phase-259-block-parameterized-cfg-consult.md - decisions:
docs/development/current/main/20-Decisions.md