Extract header PHI pre-build orchestration to header_phi_prebuild.rs (~220 lines). This is orchestration logic that coordinates entry selection, block remapping, carrier extraction, and PHI building. Changes: - NEW: merge/header_phi_prebuild.rs (220 lines) - MOD: merge/mod.rs: 1,233 → ~1,027 lines (-206 lines) - Function: prebuild_header_phis() - orchestrates PHI pre-build - Helper: get_default_entry_block() - fallback for no boundary Orchestration Responsibilities: - Entry function selection (via entry_selector SSOT) - Block remapping (loop_header vs merge_entry) - Carrier extraction from boundary (with exit_bindings filtering) - LoopHeaderPhiBuilder invocation - Reserved ValueId collection (PHI dsts + function params) Verification: - Build: 0 errors - Pattern6: RC:9 ✅ - Smoke: 154/154 PASS (verified via quick profile) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
JoinIR Merge Coordinator(箱の地図)
責務: JoinIR 生成物をホスト MIR に統合するフェーズ(block/ValueId remap → rewrite → PHI/ExitLine 再接続)。
主な箱と入口:
instruction_rewriter.rs— JoinIR→MIR 命令を書き換えつつブロックを組み立てるメイン導線phi_block_remapper.rs— PHI の block-id だけを再マップする専用箱(ValueId は remapper 済みを前提)loop_header_phi_builder.rs/exit_phi_builder.rs— ヘッダ/出口 PHI の組み立てinline_boundary_injector.rs— Host↔JoinIR の ValueId 接続(Boundary 注入)value_collector.rs/block_allocator.rs— 再マップ前の収集と ID 割り当てtail_call_classifier.rs— tail call 判定(loop_step の末尾呼び出し検出)
Fail-Fast の基本:
- block/ValueId 衝突や PHI 契約違反は握りつぶさず
Errで止める - remap は「ValueId remap」→「block-id remap」の順で一貫させる(PHI 二度 remap は禁止)
拡張時のチェックリスト:
- 新しい JoinInst → MIR 変換を追加する場合、
instruction_rewriterに閉じて追加し、PHI/ExitLine 契約が壊れないか確認。 - PHI の入力ブロックを触るときは
phi_block_remapper経由に寄せて二重 remap を防ぐ。 - 増やした box/契約はここ(README)に一言追記して入口を明示。
JoinIR Merge Contracts (SSOT)
Phase 132-R0: Continuation Contract
Continuation Functions
- Source: continuation funcs は
JoinInlineBoundary.continuation_func_idsから来る - Responsibility: router/lowerer が責務(merge は推測しない)
- Forbidden: merge は by-name/by-id で continuation 判定しない
Skip Conditions
Merge は構造条件のみで continuation のスキップを決定する:
- Structural only: 構造条件のみで決定
- 1 block
- instruction なし
- Return のみ
- Skippable continuation: 上記条件を満たす continuation
- Non-skippable continuation: TailCall(post_k) など他関数への呼び出しを含む
判定関数: is_skippable_continuation(func: &MirFunction) -> bool
Input Contracts
JoinInlineBoundary.continuation_func_ids: Set- Merge は受け取った ID のみを continuation として扱う
- SSOT:
JoinInlineBoundary::default_continuations()を使用
Prohibited Behaviors
- ❌ By-name classification (例: "join_func_2" という名前で判定)
- ❌ By-id heuristics (例: id == 2 だから continuation)
- ❌ Implicit inference (continuation 候補を merge が推測)
Verification
Continuation 契約のテストは tests/continuation_contract.rs に配置する。
Design Rationale
なぜ merge は推測してはいけないのか?
- 責任分離: Router/lowerer が JoinIR 構造を知っている。Merge は受け取った指示に従う。
- 拡張性: 将来的に複数の continuation パターン(k_exit, k_continue など)が増える可能性がある。
- デバッグ性: Continuation 判定ロジックが router/lowerer に集約されているため、トラブル時の追跡が容易。
- Fail-Fast: Merge が勝手に推測して間違った挙動をするより、明示的な契約違反でエラーを出す方が安全。
構造判定は許可される理由:
- 1-block + empty instructions + Return は「何もしない関数」の普遍的な構造的特徴。
- この判定に名前や ID は不要(構造のみで決定可能)。
- スキップは最適化であり、スキップしなくても正しさは保たれる。