feat(joinir): Phase 259 P0 complete - Pattern8 final fixes + docs (pre-block-params migration)
Phase 259 P0: Pattern8 (BoolPredicateScan) 完全完了 is_integer/1 を Pattern8 で受理し、VM/LLVM EXE 両方で動作確認完了。 次の大工事(block-parameterized CFG への移行)前のマイルストーンとして記録。 ## Key Fixes Applied 1. **skipped_entry_redirects** (instruction_rewriter.rs) - k_exit のスキップ時、entry block 参照を exit_block_id へリダイレクト - BasicBlockId not found エラーを根治 2. **loop_var_name** (pattern8_scan_bool_predicate.rs) - merge_entry_block 選択に使用(`Some(parts.loop_var.clone())`) - 未設定時の誤った entry block 選択を修正 3. **loop_invariants** (pattern8_scan_bool_predicate.rs) - PHI-free 不変量パラメータ(`[(me, me_host), (s, s_host)]`) - loop_var_name 設定時、BoundaryInjector が join_inputs Copy を全スキップするため必要 - Pattern6 と同じ設計(header PHI で不変量を保持) 4. **expr_result** (pattern8_scan_bool_predicate.rs) - k_exit からの返り値を明示設定(`Some(join_exit_value)`) - Pattern7 style(推測ではなく明示) 5. **Smoke test scripts** - set +e パターンで exit code 7 をキャプチャ - LLVM EXE スクリプトにコメント追加(tools/build_llvm.sh 経由の明記) ## Contract Documentation - join-explicit-cfg-construction.md に Pattern8 契約の具体例を追加 - "pattern増でも推測増にしない" の実例として記録 - loop_var_name / loop_invariants / expr_result / jump_args_layout の契約を明示 - 20-Decisions.md に正規化(Semantic/Plumbing)の分離方針を追記 - DOCS_LAYOUT.md に重要ドキュメントへの参照を追加 ## Test Results - ✅ VM smoke test: `[PASS] phase259_p0_is_integer_vm` (exit 7) - ✅ LLVM EXE: tools/build_llvm.sh 経由で exit 7 確認 - ✅ --verify: PASS ## Next FAIL (Phase 260+) - Function: `Main.main/0` in `apps/examples/json_lint/main.hako` - Error: `[cf_loop/pattern2] Failed to extract break condition from loop body` - Pattern: Nested loop(外側 loop + 内側 loop with break) 🚀 次の大工事: block-parameterized CFG への移行を開始します。 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@ -27,6 +27,45 @@ Related:
|
||||
- `jump_args` が IR の外側メタ扱いだと、DCE/最適化が “use” を見落としやすい
|
||||
- spans が並行 Vec だと、パスが 1 箇所でも取りこぼすと SPAN MISMATCH になる
|
||||
|
||||
## 方針の核(Phase 259+)
|
||||
|
||||
“正規化(normalized)” を 2 つに分けて SSOT を縮退させる:
|
||||
|
||||
1. **Semantic Normalization(意味SSOT)**
|
||||
- terminator 語彙を固定し、意味の揺れを禁止する
|
||||
- 例: `cond 付き Jump` を **正規形から禁止**し、`Branch` に落とす
|
||||
2. **Plumbing Normalization(配線SSOT)**
|
||||
- edge-args / CFG successor / spans など「壊れやすい配線」を IR 構造に閉じ込める
|
||||
- 目標: “忘れると壊れるメタ” を減らし、変換を写像に縮退させる
|
||||
|
||||
これにより、パターン追加が「意味SSOTに従う局所変更」になり、merge/optimizer 側の推測や補正が増殖しにくくなる。
|
||||
|
||||
### 具体例: Pattern8 契約(Phase 259 P0)
|
||||
|
||||
Pattern8(BoolPredicateScan)の実装で明示した契約要素("pattern増でも推測増にしない"の実例):
|
||||
|
||||
- **`loop_var_name`**: merge_entry_block 選択に使用(`Some(parts.loop_var.clone())`)
|
||||
- 未設定だと誤った entry block が選ばれる
|
||||
- **`loop_invariants`**: PHI-free 不変量パラメータ(`[(me, me_host), (s, s_host)]`)
|
||||
- `loop_var_name` 設定時、BoundaryInjector が ALL join_inputs Copy をスキップするため必要
|
||||
- 不変量は header PHI で持つ(Pattern6 と同じ設計)
|
||||
- **`expr_result`**: k_exit からの返り値を明示(`Some(join_exit_value)`)
|
||||
- Pattern7 style(推測ではなく明示設定)
|
||||
- **`jump_args_layout`**: ExprResultPlusCarriers(carriers=0)
|
||||
- Pattern8 は carriers なし、expr_result のみ
|
||||
- **`exit_bindings`**: Empty
|
||||
- carriers なしなので binding も不要
|
||||
|
||||
これらを「boundary builder で明示」することで、merge 側の推測を完全に排除。
|
||||
|
||||
## 最小の箱(Box)構成(小さく強く)
|
||||
|
||||
- `NormalizeBox`(意味SSOT): Structured → Normalized、terminator 語彙の固定、Fail-Fast verify
|
||||
- `AbiBox`(役割/順序SSOT): `JoinAbi`(sig/roles/special/alias)で暗黙 ABI を封印し、pack/unpack を一箇所に集約
|
||||
- `EdgeArgsPlumbingBox`(配線SSOT): edge-args を terminator operand に寄せる段階導入、CFG/spans の同期点を一本化
|
||||
|
||||
増やす基準: 同じ不変条件を 2 箇所以上で守り始めたら箱を追加し、参照点を 1 箇所に縮退させる。
|
||||
|
||||
## 移行戦略(段階導入 / Strangler)
|
||||
|
||||
原則:
|
||||
@ -38,7 +77,7 @@ Related:
|
||||
狙い: 推測をなくし、順序/役割の SSOT を 1 箇所へ寄せる。
|
||||
|
||||
- boundary に `jump_args_layout` のような **layout SSOT** を持たせ、collector/rewriter が推測しない
|
||||
- `JoinInst::Jump` を terminator 語彙として正規化(cond 付きは `Branch` へ寄せる)
|
||||
- terminator 語彙を固定し、`cond 付き Jump` を `Branch` へ寄せる(正規形から禁止)
|
||||
- continuation の識別は **ID SSOT**(String は debug/serialize のみに縮退)
|
||||
|
||||
受け入れ:
|
||||
@ -49,7 +88,11 @@ Related:
|
||||
|
||||
狙い: `jump_args` を “意味データ” として IR に埋め込み、DCE/CFG が自然に追える形へ収束する。
|
||||
|
||||
- `jump_args` を BasicBlock 外メタから terminator operand へ寄せる(段階導入: 互換フィールド併存→移行)
|
||||
- `jump_args` を BasicBlock メタから terminator operand へ寄せる(段階導入: 互換フィールド併存→移行→削除)
|
||||
- “参照 API” は Branch を含むので **複数 edge を前提**にする(単発 `edge_args()` は曖昧になりやすい)
|
||||
- 例: `block.out_edges()` / `block.edge_args_to(target)`
|
||||
- terminator operand 側は `Vec<ValueId>` だけでなく **意味(layout)**も同梱する
|
||||
- 例: `EdgeArgs { layout: JumpArgsLayout, values: Vec<ValueId> }`
|
||||
- spans は `Vec<Spanned<_>>` へ(API で不変条件を守る)
|
||||
|
||||
受け入れ:
|
||||
@ -66,3 +109,15 @@ Related:
|
||||
|
||||
- 新パターン/新機能は「新しい Contract で記述できる場合のみ」追加する
|
||||
- Contract の導入中は “機能追加より SSOT 固め” を優先する(泥沼デバッグの再発防止)
|
||||
|
||||
## API の作り方(迷子防止)
|
||||
|
||||
Strangler 期間は “読む側だけ寄せる” と取りこぼしが起きやすい。読む側/書く側を両方とも API に閉じ込める。
|
||||
|
||||
- **読む側(参照点の一本化)**
|
||||
- `out_edges()` のように edge を列挙できる API を SSOT にする(`Jump`/`Branch` を同じ形で扱える)
|
||||
- 旧メタ(`jump_args`)は API 内部でのみ参照し、外部は見ない
|
||||
- **書く側(terminator 更新の一本化)**
|
||||
- `set_terminator(...)` のような入口に寄せ、successors/preds の同期漏れを構造で潰す
|
||||
- **verify(Fail-Fast)**
|
||||
- terminator から計算した successors と、キャッシュ `block.successors` の一致をチェックして “同期漏れ” を即死させる
|
||||
|
||||
Reference in New Issue
Block a user