refactor(joinir): Phase 287 P2 - Strengthen BackEdge/latch conditions (WIP)
**Problem**: Phase 188.3 Pattern6 (nested loop) encounters infinite loop - inner_step → inner_step (self-recursion) incorrectly classified as BackEdge - → redirects to outer header (loop_step) instead of inner_step entry - is_recursive_call causes inner recursion to overwrite outer latch values - → PHI receives wrong values → i doesn't increment → infinite loop **Fix 1: BackEdge classification strictness** (tail_call_classifier.rs) - Add `is_target_loop_entry` parameter to classify_tail_call() - BackEdge ONLY when target==loop_step (entry_func), not inner_step - Prevents inner_step → inner_step from redirecting to outer header **Fix 2: latch_incoming guard** (instruction_rewriter.rs) - Change condition from `is_recursive_call || is_target_loop_entry` to `is_target_loop_entry` only - Prevents inner_step self-recursion from overwriting outer loop's latch - set_latch_incoming() now called with correct values (verified by debug) **Status**: 🚧 WIP - Infinite loop still occurs - set_latch_incoming('i', BasicBlockId(8), ValueId(21)) ✅ Called correctly - But final PHI: `phi [%4, bb8]` instead of `phi [%21, bb8]` ❌ - Root cause likely in PHI generation (merge/mod.rs), not latch_incoming - Next: Investigate why latch_incoming values aren't used in PHI **Files**: - src/mir/builder/control_flow/joinir/merge/tail_call_classifier.rs - src/mir/builder/control_flow/joinir/merge/instruction_rewriter.rs 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@ -6,7 +6,8 @@
|
||||
- StepTreeの `max_loop_depth` を SSOT に採用(Option A)
|
||||
- strict mode で depth > 2 を明示エラー化(Fail-Fast)
|
||||
- quick 154/154 PASS、integration selfhost FAIL=0 維持
|
||||
- 次: `docs/development/current/main/phases/phase-188.3/README.md`(depth=2 を JoinIR lowering で通す / Phase 188.3は“最小のwrite-back”をcarrierとして明示、一般化は次フェーズ)
|
||||
- 次: `docs/development/current/main/phases/phase-188.3/README.md`(depth=2 を JoinIR lowering で通す / “最小write-back”は carrier として明示)
|
||||
- 実装導線(手順書): `docs/development/current/main/phases/phase-188.3/P1-INSTRUCTIONS.md`(merge/rewriter の “undef ValueId” 典型罠もここに固定)
|
||||
|
||||
**2025-12-27: Phase S0.1 完了** ✅
|
||||
- integration selfhost を「落ちない状態」に収束(FAIL=0)
|
||||
|
||||
@ -129,9 +129,34 @@ nested loop では `inner_step` が混ざるので、**`inner_step` / `k_inner_e
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting: `use of undefined value ValueId(...)`(Pattern6)
|
||||
|
||||
典型ログ:
|
||||
|
||||
- `[cf_loop/joinir] Function 'inner_step' params: [ValueId(104), ...]`
|
||||
- `use of undefined value ValueId(104)`
|
||||
|
||||
意味:
|
||||
- JoinIR の “param ValueId” は SSA 命令で定義されないため、`Copy(dst=param, src=arg)` が入らないと undefined になる。
|
||||
|
||||
優先して疑う場所(責務の順):
|
||||
|
||||
1. `src/mir/builder/control_flow/joinir/merge/instruction_rewriter.rs`
|
||||
- `plan_rewrites()` の “tail-call param binding” の **skip 条件**
|
||||
- SSOT: **skip は “target が loop header” のときだけ**(header PHI dst を上書きしないため)
|
||||
- `loop_step(entry func)→ inner_step` まで “entry func だから” という理由で skip すると、inner_step param が未定義になる
|
||||
2. `JoinInlineBoundary.continuation_func_ids`
|
||||
- `inner_step` / `k_inner_exit` が continuation 扱いになっていないと、merge 側の entry func 選定がズレて skip 判定も破綻しやすい
|
||||
|
||||
注意(避けたい対処):
|
||||
- `merge/mod.rs` で “param ValueId を index で PHI dst にリマップする” は危険
|
||||
- Pattern6 は `inner_step(j, i, sum)` のように先頭に loop-local があり、carrier index と一致しない
|
||||
- index remap は `j` を `i` の PHI dst に誤接続しやすい
|
||||
|
||||
---
|
||||
|
||||
## 追加の注意(Fail-Fast)
|
||||
|
||||
- Pattern6 が選ばれたあとに `Ok(None)` で他パターンに流すのは禁止(silent fallback)
|
||||
- “選ぶ前に落とす” が最も安全:
|
||||
- `is_pattern6_lowerable()` を「lowering が確実に通る形だけ true」に強化する
|
||||
|
||||
|
||||
@ -126,6 +126,26 @@ Phase 188.3 の最小形は「outer の loop_step の中で inner の loop_step
|
||||
|
||||
---
|
||||
|
||||
## Merge/Rewrite contract (SSOT) — “undef ValueId” を防ぐ
|
||||
|
||||
JoinIR merge は「JoinIR の param ValueId は SSA 命令で定義されない」前提なので、**適切な Copy(param binding)** が入らないと即 `use of undefined value ValueId(...)` になる。
|
||||
|
||||
特に Pattern6 では `loop_step`(outer)から `inner_step` へ tail-call するため、以下を SSOT として固定する:
|
||||
|
||||
- **Skip するのは “target が loop header” のときだけ**
|
||||
- header では PHI dst が carrier の SSOT なので、param Copy を入れて上書きしてはいけない
|
||||
- 一方で `loop_step → inner_step` は “target が header ではない” ため、**inner_step params を定義する Copy が必要**
|
||||
- **param の index ベース remap は危険**
|
||||
- `inner_step(j, i, sum)` のように、先頭に loop-local(`j`)が混ざると carrier の index と一致しない
|
||||
- index remap は `j` を `i` の PHI dst に誤接続しやすい
|
||||
|
||||
この契約に反する場合の典型症状:
|
||||
- `Function 'inner_step' params: [ValueId(104), ...]` の直後に `use of undefined value ValueId(104)`
|
||||
|
||||
修正の第一候補は `merge/instruction_rewriter.rs` 側(tail-call param binding の skip 条件)であり、merge/mod.rs の “パラメータ再マップを拡張する” で逃げない(責務を混ぜない)。
|
||||
|
||||
---
|
||||
|
||||
## Deliverables
|
||||
|
||||
1. **Fixture + integration smoke(exit code SSOT)**
|
||||
@ -161,6 +181,14 @@ Phase 188.3 の最小形は「outer の loop_step の中で inner の loop_step
|
||||
- **Phase 188.3**: depth=2 の最小形を “確実に通す” + PoC fixture を smoke 固定
|
||||
- **Phase 188.4+**: write-back(outer carrier reconnection)と “再帰 lowering の一般化(depthを増やしても壊れない)” を docs-first で設計してから実装
|
||||
|
||||
### Planned cleanup (after Phase 188.3)
|
||||
|
||||
Pattern6 を通す過程で露出しやすい “暗黙ルール” を SSOT 化して、今後の nested/generalization を楽にする:
|
||||
|
||||
- `JoinInlineBoundary` に **loop header func name を明示する SSOT**(merge の “entry func 推定” を段階的に減らす)
|
||||
- `ParamBinding` の規則を “source-based ではなく target-based” に統一(header だけ特別扱い)
|
||||
- (将来)param role(carrier vs local)を明文化し、index remap の誘惑を消す
|
||||
|
||||
---
|
||||
|
||||
## Out of Scope
|
||||
|
||||
Reference in New Issue
Block a user