refactor(joinir): make jump_args layout explicit (Phase 256)
This commit is contained in:
@ -167,3 +167,93 @@ pub struct BasicBlock {
|
||||
- ExitLine: `jump_args` の長さ契約ミスマッチ(carriers と invariants の混在)
|
||||
- `JoinInst::Jump` が bridge で “Return 化” され、continuation の意味が落ちる疑い
|
||||
- DCE が `jump_args` 由来の use を落とし、Copy が消えて SSA/dominance が崩れる
|
||||
|
||||
---
|
||||
|
||||
## ChatGPT Pro からの設計回答(要約)
|
||||
|
||||
診断(短く):
|
||||
- JoinIR/MIR 間に「暗黙 ABI(呼び出し規約)」が生えており、SSOT が分裂している
|
||||
- `Jump/cont/params/jump_args` が層を跨ぐたびに意味が揺れて、SSA/dominance/DCE が壊れやすい
|
||||
|
||||
命名:
|
||||
- この収束先(north star)を **Join-Explicit CFG Construction** と呼ぶ
|
||||
|
||||
提案の大枠(3案):
|
||||
|
||||
### 案1: JoinIR ABI / Contract モジュール(推奨)
|
||||
|
||||
狙い:
|
||||
- いま暗黙のまま散っている契約(順序/長さ/名前/役割)を “ABI オブジェクト” として 1 箇所に封印する
|
||||
- `Vec` の順序契約に魂を預けない(pack/unpack を ABI 経由にする)
|
||||
|
||||
最小イメージ(雰囲気):
|
||||
|
||||
```rust
|
||||
pub struct JoinAbi {
|
||||
pub cont_sigs: Vec<ContSig>, // continuation signature SSOT
|
||||
pub special: SpecialConts, // main/loop_step/k_exit
|
||||
pub legacy_alias: AliasTable, // join_func_2 -> k_exit 等
|
||||
}
|
||||
|
||||
pub struct ContSig {
|
||||
pub params: Vec<Param>,
|
||||
}
|
||||
|
||||
pub enum ParamRole {
|
||||
Carrier,
|
||||
Invariant,
|
||||
Result,
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
Jump の正規形(Normalized JoinIR):
|
||||
- `Jump` は cond を持たず、必ず終端
|
||||
- cond 付きは `Branch { then_cont+args, else_cont+args }` に寄せる
|
||||
|
||||
### 案2: MIR を “ブロック引数 SSA” に昇格(強力)
|
||||
|
||||
狙い:
|
||||
- JoinIR の cont/args と MIR の block/jump_args を同型化し、bridge/merge の解釈余地を消す
|
||||
|
||||
即効の 2 点(最優先):
|
||||
- `jump_args` を BasicBlock 外メタではなく terminator に埋め込む(use-def / DCE が自然に追える)
|
||||
- spans を並行 Vec から `Vec<Spanned<_>>` へ(SPAN MISMATCH を構造で防ぐ)
|
||||
|
||||
### 案3: JoinIR をやめて CPS CFG 一本化(最終収束案)
|
||||
|
||||
狙い:
|
||||
- SSOT を 1 個にする(JoinIR/MIR の “橋” を消す)
|
||||
- ただし移行は重いので、案2 の延長として収束させるのが現実的
|
||||
|
||||
---
|
||||
|
||||
## 推奨(この repo の制約込み)
|
||||
|
||||
Phase 256 の “今日の詰まり” に効く順で:
|
||||
|
||||
1) **案1(JoinIR ABI/Contract)を設計 SSOT として採用**
|
||||
- まず “順序契約を殺す” のが最大のデバッグ短縮になる
|
||||
2) **案2 のうち即効ポイントを段階導入**
|
||||
- `jump_args` の SSOT は “terminator operand” に移す(大工事なので Phase 256 を緑に戻した後に着手が安全)
|
||||
- spans の `Spanned` 化も同様に段階導入(いまは pass 側で不変条件をテストで固定)
|
||||
|
||||
ここまでで、JoinIR を増やさずに「暗黙 ABI を明文化」できる。
|
||||
|
||||
---
|
||||
|
||||
## 次に設計として決めたいこと(Decision 候補)
|
||||
|
||||
- `JoinInst::Jump` の SSOT は “tail call 等価” ではなく、Normalized JoinIR の terminator 語彙として固定する
|
||||
- continuation の識別は **ID SSOT**(String は debug/serialize 用に限る)
|
||||
- `jump_args` を SSOT にするなら、最終的に MIR terminator に埋め込む(DCE/CFG の整合性が自然になる)
|
||||
|
||||
---
|
||||
|
||||
## Phase 256 実装から得た追加の教訓(SSOT)
|
||||
|
||||
- `expr_result` と LoopState carrier が同一 ValueId になるケースが現実に起きる(例: ループ式の返り値が `result`)。
|
||||
このとき “legacy expr_result slot(jump_args[0])” を機械的に仮定すると、offset がずれて ExitLine の配線が崩れる。
|
||||
- 対策として `ExitArgsCollector` には “slot があるかどうか” を推測させず、呼び出し側が `expect_expr_result_slot`
|
||||
を明示して渡すのが安全(Fail-Fast + 構造)。
|
||||
|
||||
Reference in New Issue
Block a user