fix(joinir): stabilize Phase 256 merge (jump_args, DCE, func names)
This commit is contained in:
@ -48,12 +48,14 @@
|
||||
## 2025-12-19:Phase 256(StringUtils.split/2 可変 step ループ)🔜
|
||||
|
||||
- Phase 256 README: `docs/development/current/main/phases/phase-256/README.md`
|
||||
- Current first FAIL: `StringUtils.split/2`(`jump_args length mismatch` → `Carrier 'i' has no latch incoming set`)
|
||||
- Current first FAIL: `StringUtils.split/2`(MIR verification: “Value defined multiple times” + SSA undef)
|
||||
- 状況:
|
||||
- `MirInstruction::Select` の導入は完了、Pattern6(index_of)は PASS 維持。
|
||||
- `ValueId(57)` undefined は根治(原因は `const_1` 未初期化)。
|
||||
- SSA undef(`%49/%67`)は P1.7 で根治(continuation 関数名の SSOT 不一致)。
|
||||
- 残りは Pattern7 の carrier PHI 配線(ExitLine / jump_args 契約)を直す段階。
|
||||
- P1.8で ExitLine/jump_args の余剰許容と関数名マッピングを整流。
|
||||
- P1.9で `JoinInst::Jump` を tail call として bridge に落とし、`jump_args` を SSOT として保持。
|
||||
- P1.10で DCE が `jump_args` を used 扱いし、`instruction_spans` を同期(SPAN MISMATCH 根治)。
|
||||
- P1.5-DBG: boundary entry params の契約チェックを追加(VM実行前 fail-fast)。
|
||||
- P1.6: 契約チェックの薄い集約 `run_all_pipeline_checks()` を導入(pipeline の責務を縮退)。
|
||||
|
||||
|
||||
@ -0,0 +1,169 @@
|
||||
# Phase 256: JoinIR Contract Questions (for ChatGPT Pro)
|
||||
|
||||
目的: Phase 256 の詰まり(Jump/continuation/params/jump_args の暗黙契約)を、設計として固めるための相談メモ。
|
||||
|
||||
---
|
||||
|
||||
## Q1. SSOT をどこに置くべき?
|
||||
|
||||
JoinIR の「意味論 SSOT」をどこに置くべきか。
|
||||
|
||||
- A) Structured JoinIR を SSOT として維持し、bridge/merge が意味解釈する
|
||||
- B) Normalized JoinIR を SSOT とし、Structured→Normalized の正規化箱を必須化する
|
||||
|
||||
判断材料として、現在の層の境界と責務:
|
||||
- Pattern lowerer(Structured JoinIR 生成)
|
||||
- `join_ir_vm_bridge`(JoinIR→MIR 変換)
|
||||
- merge(MIR inline + PHI/ExitLine wiring)
|
||||
|
||||
---
|
||||
|
||||
## Q2. `JoinInst::Jump` の正規形(不変条件)
|
||||
|
||||
現状の詰まりは `Jump` が層を跨ぐときに「tail call 等価」になったり「Return 化」になったりして、continuation が失われる点にある。
|
||||
|
||||
相談したい:
|
||||
- `JoinInst::Jump { cont, args, cond }` を SSOT 的にどう定義するべきか?
|
||||
- cond 付き Jump は JoinIR 語彙として残すべきか?それとも IfMerge に寄せるべきか?
|
||||
|
||||
最小コード(JoinIR 命令):
|
||||
|
||||
```rust
|
||||
// src/mir/join_ir/mod.rs
|
||||
pub enum JoinInst {
|
||||
// ...
|
||||
Jump {
|
||||
cont: JoinContId,
|
||||
args: Vec<VarId>,
|
||||
cond: Option<VarId>,
|
||||
},
|
||||
Call {
|
||||
func: JoinFuncId,
|
||||
args: Vec<VarId>,
|
||||
dst: Option<VarId>,
|
||||
k_next: Option<JoinContId>,
|
||||
},
|
||||
Ret { value: Option<VarId> },
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Q3. boundary/params/jump_args の順序契約をどこで固定する?
|
||||
|
||||
Phase 256 では、次の対応関係が暗黙で、崩れると SSA undef / PHI wiring fail-fast になりやすい。
|
||||
|
||||
- `JoinInlineBoundary.join_inputs` ↔ `JoinModule.entry.params`
|
||||
- `exit_bindings` ↔ ExitLine の carrier PHI reconnect
|
||||
- `jump_args`(tail call args metadata)↔ ExitLine の latch incoming 復元
|
||||
|
||||
最小コード(boundary):
|
||||
|
||||
```rust
|
||||
// src/mir/join_ir/lowering/inline_boundary.rs
|
||||
pub struct JoinInlineBoundary {
|
||||
pub join_inputs: Vec<ValueId>,
|
||||
pub host_inputs: Vec<ValueId>,
|
||||
pub loop_invariants: Vec<(String, ValueId)>,
|
||||
pub exit_bindings: Vec<LoopExitBinding>,
|
||||
pub expr_result: Option<ValueId>,
|
||||
pub loop_var_name: Option<String>,
|
||||
pub continuation_func_ids: std::collections::BTreeSet<String>,
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
契約の fail-fast は現在ここで行っている:
|
||||
|
||||
```rust
|
||||
// src/mir/builder/control_flow/joinir/merge/contract_checks.rs
|
||||
pub(in crate::mir::builder::control_flow::joinir) fn run_all_pipeline_checks(
|
||||
join_module: &crate::mir::join_ir::JoinModule,
|
||||
boundary: &JoinInlineBoundary,
|
||||
) -> Result<(), String> {
|
||||
verify_boundary_entry_params(join_module, boundary)?;
|
||||
// ...
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
|
||||
相談したい:
|
||||
- この順序契約は「boundary」「normalizer」「bridge」「merge」のどの層が SSOT になるべきか?
|
||||
- fail-fast の責務をどこに置くべきか(今は conversion_pipeline 直前)?
|
||||
|
||||
---
|
||||
|
||||
## Q4. continuation の識別: `JoinFuncId` vs `String`(関数名)
|
||||
|
||||
Phase 256 P1.7 で「continuation 関数が merge で見つからず SSA undef」になった。
|
||||
原因は bridge 側と merge 側の “関数名 SSOT” 不一致。
|
||||
|
||||
現状の暫定 SSOT は `canonical_names`:
|
||||
|
||||
```rust
|
||||
// src/mir/join_ir/lowering/canonical_names.rs
|
||||
pub const K_EXIT: &str = "k_exit";
|
||||
pub const K_EXIT_LEGACY: &str = "join_func_2";
|
||||
pub const LOOP_STEP: &str = "loop_step";
|
||||
pub const MAIN: &str = "main";
|
||||
```
|
||||
|
||||
相談したい:
|
||||
- continuation を `JoinFuncId` で保持し、bridge で 1 回だけ名前解決するべきか?
|
||||
- それとも `String` を SSOT にして “MirModule key” と一致させ続けるべきか?
|
||||
- 併存するなら、変換境界(片方→片方)をどこに置くべきか?
|
||||
|
||||
---
|
||||
|
||||
## Q5. 正規化 shadow(`join_func_N`)との共存戦略
|
||||
|
||||
normalized_shadow 側は `join_func_2` のような命名を使う箇所がある。
|
||||
この legacy をいつ・どう統一するべきか(または統一しないなら境界をどう明文化するか)。
|
||||
|
||||
---
|
||||
|
||||
## Q6. `jump_args` は MIR のどの層の SSOT か?
|
||||
|
||||
観測:
|
||||
- ExitLine/merge 側は `BasicBlock.jump_args` を「exit/carry 値の SSOT」として参照する。
|
||||
- bridge 側で tail call / Jump を生成するときに `jump_args` を落とし忘れると、ExitLine が fallback 経路へ入りやすく、
|
||||
SSA/dominance の破綻につながる。
|
||||
|
||||
最小コード(MIR basic block):
|
||||
|
||||
```rust
|
||||
// src/mir/mod.rs
|
||||
pub struct BasicBlock {
|
||||
pub instructions: Vec<MirInstruction>,
|
||||
pub instruction_spans: Vec<Span>,
|
||||
pub terminator: Option<MirInstruction>,
|
||||
pub jump_args: Option<Vec<ValueId>>,
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
相談したい:
|
||||
- `jump_args` は terminator(Jump/Branch/Return/Call)に埋め込むべきか、それとも BasicBlock の外部メタのままでよいか?
|
||||
- `jump_args` の契約(順序・長さ・expr_result の有無・invariants の混在)をどこで固定するべきか?
|
||||
|
||||
---
|
||||
|
||||
## Q7. Optimizer/DCE の不変条件(spans 同期と jump_args)
|
||||
|
||||
観測:
|
||||
- DCE が `jump_args` だけで使われる値を “unused” とみなすと、Copy/Const が消えて merge が壊れる。
|
||||
- DCE が `instructions` だけを削って `instruction_spans` を同期しないと、SPAN MISMATCH が発生しデバッグが困難になる。
|
||||
|
||||
相談したい:
|
||||
- `jump_args` は “use” として扱うのが SSOT として正しいか?
|
||||
- spans 同期は「各パスの責務」か、それとも `BasicBlock` の API(例: spanned filter)に閉じ込めるべきか?
|
||||
|
||||
---
|
||||
|
||||
## 観測された失敗例(短く)
|
||||
|
||||
- SSA undef(関数名不一致で continuation が merge 対象にならず到達不能/未定義が露出)
|
||||
- ExitLine: `jump_args` の長さ契約ミスマッチ(carriers と invariants の混在)
|
||||
- `JoinInst::Jump` が bridge で “Return 化” され、continuation の意味が落ちる疑い
|
||||
- DCE が `jump_args` 由来の use を落とし、Copy が消えて SSA/dominance が崩れる
|
||||
@ -8,12 +8,13 @@ Related:
|
||||
|
||||
## Current Status (SSOT)
|
||||
|
||||
- Current first FAIL: `StringUtils.split/2`(`jump_args length mismatch` → `Carrier 'i' has no latch incoming set`)
|
||||
- Current first FAIL: `StringUtils.split/2`(MIR verification: “Value defined multiple times” + SSA undef)
|
||||
- Pattern6 は PASS 維持
|
||||
- 直近の完了:
|
||||
- P1.10: DCE が `jump_args` 参照を保持し、`instruction_spans` と同期するよう修正(回帰テスト追加)
|
||||
- P1.7: SSA undef(`%49/%67`)根治(continuation 関数名の SSOT 不一致)
|
||||
- P1.6: pipeline contract checks を `run_all_pipeline_checks()` に集約
|
||||
- 次の作業: P1.8(Pattern7 の carrier PHI wiring / ExitLine + jump_args 契約の修正)
|
||||
- 次の作業: P1.11(merge 側の ExitLine/PHI/dominance を `--verify` で緑に戻す)
|
||||
|
||||
---
|
||||
|
||||
@ -384,6 +385,35 @@ Option A(Pattern 7 新設)を推奨。
|
||||
結果:
|
||||
- `./target/release/hakorune --backend vm --verify apps/tests/phase256_p0_split_min.hako` で SSA undef は消滅
|
||||
|
||||
---
|
||||
|
||||
## 進捗(P1.8)
|
||||
|
||||
### P1.8: ExitLine/jump_args と関数名マッピング整流(完了)
|
||||
|
||||
変更(要旨):
|
||||
- ExitArgsCollector 側で「余剰 jump_args(invariants)」を許容し、`expected 3 or 4 but got 5` を解消
|
||||
- JoinIR→MIR bridge 側で “join_func_N” 由来の名前と “JoinFunction.name” の不一致を解消するため、関数名マッピングを導入/伝播
|
||||
|
||||
結果:
|
||||
- 旧 first FAIL(jump_args length mismatch)は解消
|
||||
|
||||
### P1.9: Jump を tail call として表現(完了)
|
||||
|
||||
変更(要旨):
|
||||
- JoinIR→MIR bridge で `JoinInst::Jump` を “continuation への tail call” として落とす
|
||||
- `BasicBlock.jump_args` を tail call と同様に SSOT として保持(ExitLine/collector の復元入力)
|
||||
|
||||
結果:
|
||||
- `JoinInst::Jump` が “ret args[0]” 相当になり continuation が失われる問題は解消
|
||||
|
||||
### P1.10: DCE の jump_args + spans 同期(完了)
|
||||
|
||||
変更(要旨):
|
||||
- DCE が `jump_args` で使われる値を used として扱い、純命令の除去で Copy が消えないようにする
|
||||
- `instruction_spans` と `instructions` の同期不変条件を維持(SPAN MISMATCH 根治)
|
||||
- 回帰テストを追加(`test_dce_keeps_jump_args_values`, `test_dce_syncs_instruction_spans`)
|
||||
|
||||
### リファクタリング方針(P1.6候補 / 先送り推奨)
|
||||
|
||||
現時点(split がまだ FAIL)では、箱化のための箱化で複雑さが増えやすいので、以下を推奨する:
|
||||
|
||||
Reference in New Issue
Block a user