refactor(joinir): unify boundary join_inputs SSOT (pattern4/6/7)
Apply Phase 256.8 SSOT fix to Pattern4/6/7: - Use join_module.entry.params.clone() instead of hardcoded ValueIds - Add fail-fast validation for params count mismatch - Remove ValueId(0), ValueId(PARAM_MIN + k) patterns - Clean up unused PARAM_MIN imports This prevents entry_param_mismatch errors structurally and maintains consistency with Pattern2/3. Changes: - pattern4_with_continue.rs: Lines 442-476 (SSOT extraction + validation) - pattern6_scan_with_init.rs: Lines 447-471 (SSOT extraction + validation) - pattern7_split_scan.rs: Lines 495-526 (SSOT extraction + validation) All patterns now use the same SSOT principle: 1. Extract entry function (priority: join_module.entry → fallback "main") 2. Use params as SSOT: join_inputs = entry_func.params.clone() 3. Build host_inputs in expected order (pattern-specific) 4. Fail-fast validation: join_inputs.len() == host_inputs.len() Verification: - cargo build --release: ✅ PASS (no PARAM_MIN warnings) - Quick profile: ✅ First FAIL still json_lint_vm (baseline maintained) - Pattern6 smoke: ✅ PASS (index_of test) - Pattern7 smoke: Pre-existing phi pred mismatch (not introduced by SSOT) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@ -45,12 +45,14 @@
|
||||
- Pattern6/index_of が VM/LLVM で PASS
|
||||
- `loop_invariants` を導入して ConditionOnly 誤用を根治
|
||||
|
||||
## 2025-12-19:Phase 256(StringUtils.split/2 可変 step ループ)🔜
|
||||
## 2025-12-19:Phase 256(StringUtils.split/2 可変 step ループ)✅
|
||||
|
||||
- Phase 256 README: `docs/development/current/main/phases/phase-256/README.md`
|
||||
- Current first FAIL: `json_lint_vm`(Pattern2 break cond: `this.is_whitespace(...)` needs `current_static_box_name`)
|
||||
- 状況:
|
||||
- `MirInstruction::Select` の導入は完了、Pattern6(index_of)は PASS 維持。
|
||||
- Status:
|
||||
- `StringUtils.split/2` は VM `--verify` / integration smoke まで PASS
|
||||
- `--profile quick` の最初の FAIL は Phase 257(`StringUtils.last_index_of/2`)へ移動
|
||||
- 設計SSOT: `docs/development/current/main/design/join-explicit-cfg-construction.md`
|
||||
- 直近の主要fix:
|
||||
- `ValueId(57)` undefined は根治(原因は `const_1` 未初期化)。
|
||||
- SSA undef(`%49/%67`)は P1.7 で根治(continuation 関数名の SSOT 不一致)。
|
||||
- P1.8で ExitLine/jump_args の余剰許容と関数名マッピングを整流。
|
||||
@ -59,6 +61,13 @@
|
||||
- P1.11で ExitArgsCollector の expr_result slot 判定を明確化し、split が `--verify` / integration smoke まで PASS。
|
||||
- P1.5-DBG: boundary entry params の契約チェックを追加(VM実行前 fail-fast)。
|
||||
- P1.6: 契約チェックの薄い集約 `run_all_pipeline_checks()` を導入(pipeline の責務を縮退)。
|
||||
- P1.13: Pattern2 boundary entry params を `join_module.entry.params` SSOT へ寄せた(ValueId 推測生成の撤去)。
|
||||
|
||||
## 2025-12-20:Phase 257(last_index_of early return loop)🔜
|
||||
|
||||
- Phase 257 README: `docs/development/current/main/phases/phase-257/README.md`
|
||||
- Goal: `StringUtils.last_index_of/2` を JoinIR で受理し、`--profile quick` を緑に戻す
|
||||
- Investigation(最小再現/論点): `docs/development/current/main/investigations/phase-257-last-index-of-loop-shape.md`
|
||||
|
||||
## 2025-12-19:Phase 254(index_of loop pattern)✅ 完了(Blocked by Phase 255)
|
||||
|
||||
|
||||
@ -0,0 +1,61 @@
|
||||
Status: Active
|
||||
Scope: `json_lint_vm / StringUtils.last_index_of/2` の最初の FAIL を、最小の再現と論点で固定する。
|
||||
Related:
|
||||
- Phase 257 SSOT: `docs/development/current/main/phases/phase-257/README.md`
|
||||
- Design goal: `docs/development/current/main/design/join-explicit-cfg-construction.md`
|
||||
|
||||
# Phase 257 Investigation: `last_index_of/2` loop shape
|
||||
|
||||
## Symptom(SSOT)
|
||||
|
||||
`./tools/smokes/v2/run.sh --profile quick` の最初の FAIL:
|
||||
|
||||
- `json_lint_vm` が `StringUtils.last_index_of/2` で停止
|
||||
- エラー: `[joinir/freeze] Loop lowering failed: JoinIR does not support this pattern, and LoopBuilder has been removed.`
|
||||
|
||||
## Minimal Fixture
|
||||
|
||||
- `apps/tests/phase257_p0_last_index_of_min.hako`
|
||||
|
||||
形(要旨):
|
||||
|
||||
```nyash
|
||||
local i = s.length() - 1
|
||||
loop(i >= 0) {
|
||||
if s.substring(i, i + 1) == ch { return i }
|
||||
i = i - 1
|
||||
}
|
||||
return -1
|
||||
```
|
||||
|
||||
## StepTree / Capabilities(ログ観測)
|
||||
|
||||
(実ログは `tools/smokes/v2/profiles/quick/apps/json_lint_vm.sh` の tail を参照)
|
||||
|
||||
- caps: `If,Loop,Return`(break/continue なし)
|
||||
- loop cond: `i >= 0`
|
||||
- step: `i = i - 1`(const step のはず)
|
||||
- early exit: `return i`
|
||||
- not-found: `return -1`
|
||||
|
||||
`loop_canonicalizer` は `Missing caps: [ConstStep]` で FAIL_FAST しているが、ここは “Pattern2 側の試行ログ” であり、
|
||||
JoinIR パターンがこの形を受理できていないのが本体。
|
||||
|
||||
## Root Cause Hypothesis(現状の仮説)
|
||||
|
||||
- Pattern6(ScanWithInit)が forward scan 前提(`i = i + 1`, `i < bound`)で、reverse scan を検出できていない。
|
||||
- Pattern7(SplitScan)は適用対象外。
|
||||
- Pattern3/1 は `return` を含む loop を扱わない(or 目的が違う)。
|
||||
|
||||
## Decision(Phase 257 の方針)
|
||||
|
||||
Phase 257 では、以下で進める:
|
||||
|
||||
- Pattern6(ScanWithInit)を “scan direction” 付きに一般化し、reverse scan + early return を受理する。
|
||||
- Phase 256 で固めた Contract(`JumpArgsLayout`, pipeline contract checks)に従い、merge 側で推測しない。
|
||||
|
||||
## Questions(将来に残す設計論点)
|
||||
|
||||
1. `LoopPatternKind` に Pattern6/7 を増やすべきか?(router 側での分類SSOTを揃える)
|
||||
2. scan 系の “forward/reverse” を 1 パターンにまとめるか、専用 Pattern を増やすか?
|
||||
3. `return` を loop 語彙として Pattern 側で扱い続けるか、Normalization で “early exit” に正規化すべきか?
|
||||
@ -1,6 +1,6 @@
|
||||
# Phase 256: StringUtils.split/2 Pattern Support
|
||||
|
||||
Status: Active
|
||||
Status: Completed
|
||||
Scope: Loop pattern recognition for split/tokenization operations
|
||||
Related:
|
||||
- Phase 255 完了(loop_invariants 導入、Pattern 6 完成)
|
||||
@ -9,14 +9,16 @@ Related:
|
||||
|
||||
## Current Status (SSOT)
|
||||
|
||||
- Current first FAIL: `json_lint_vm`(Pattern2 break cond: `this.is_whitespace(...)` needs `current_static_box_name`)
|
||||
- Current first FAIL: `json_lint_vm / StringUtils.last_index_of/2`(Loop with early return pattern - unsupported)
|
||||
- `StringUtils.split/2` は VM `--verify` / smoke まで PASS
|
||||
- Pattern6(index_of)は PASS 維持
|
||||
- 次フェーズ: Phase 257(`last_index_of/2` の reverse scan + early return loop)
|
||||
- 直近の完了:
|
||||
- P1.13: Pattern2 boundary entry_param_mismatch 根治(`join_module.entry.params` SSOT 化)
|
||||
- P1.10: DCE が `jump_args` 参照を保持し、`instruction_spans` と同期するよう修正(回帰テスト追加)
|
||||
- P1.7: SSA undef(`%49/%67`)根治(continuation 関数名の SSOT 不一致)
|
||||
- P1.6: pipeline contract checks を `run_all_pipeline_checks()` に集約
|
||||
- 次の作業: Pattern2 の static box context を break condition lowering に渡す(次フェーズ)
|
||||
- 次の作業: Phase 257(last_index_of pattern - loop with return support)
|
||||
- 設計メモ(ChatGPT Pro 相談まとめ): `docs/development/current/main/investigations/phase-256-joinir-contract-questions.md`
|
||||
|
||||
---
|
||||
@ -477,6 +479,49 @@ Option A(Pattern 7 新設)を推奨。
|
||||
- legacy 掃除候補:
|
||||
- `join_func_name(id)` の利用箇所を棚卸しし、「structured JoinIR では使用禁止 / normalized shadow だけで使用」など境界を明文化
|
||||
|
||||
---
|
||||
|
||||
## 進捗(P1.13)
|
||||
|
||||
### P1.13: Pattern2 boundary entry_param_mismatch 根治(完了)
|
||||
|
||||
症状(json_lint_vm / StringUtils.trim_end/1):
|
||||
```
|
||||
[ERROR] ❌ MIR compilation error: [joinir/phase1.5/boundary/entry_param_mismatch]
|
||||
Entry param[0] in 'main': expected ValueId(1000), but boundary.join_inputs[0] = ValueId(0)
|
||||
Hint: parameter ValueId mismatch indicates boundary.join_inputs constructed in wrong order
|
||||
```
|
||||
|
||||
根本原因(SSOT):
|
||||
- `emit_joinir_step_box.rs` が `boundary.join_inputs` を hardcoded ValueId(0), ValueId(1)... で構築していた
|
||||
- JoinIR lowerer は `alloc_param()` / `alloc_local()` で実際のパラメータ ValueId を割り当てている
|
||||
- 両者が一致しないため、boundary contract check で fail-fast
|
||||
|
||||
修正方針(SSOT原則):
|
||||
- **SSOT**: `join_module.entry.params` が `boundary.join_inputs` の唯一の真実
|
||||
- **禁止**: ValueId(0..N) の推測生成、Param/Local 領域の決めつけ、JoinModule とは独立に ValueId を作ること
|
||||
- **実装**: `emit_joinir_step_box.rs` で `join_input_slots = entry_func.params.clone()` に置き換え
|
||||
|
||||
実装(SSOT):
|
||||
- `src/mir/builder/control_flow/joinir/patterns/pattern2_steps/emit_joinir_step_box.rs` (lines 71-96)
|
||||
- Entry function extraction (priority: `join_module.entry` → fallback to "main")
|
||||
- `join_input_slots = main_func.params.clone()` (SSOT from JoinModule)
|
||||
- `host_input_values` を同じ順序で構築(loop_var + carriers)
|
||||
- Fail-fast validation for params count mismatch
|
||||
|
||||
結果:
|
||||
- `./tools/smokes/v2/run.sh --profile quick` の first FAIL が `StringUtils.trim_end/1` から `StringUtils.last_index_of/2` へ移動
|
||||
- Pattern2 の boundary contract は安定化
|
||||
|
||||
次のブロッカー(Phase 257):
|
||||
- `StringUtils.last_index_of/2` - Loop with early return pattern (unsupported)
|
||||
- Structure: `loop(i >= 0) { if (cond) { return value } i = i - 1 } return default`
|
||||
- Capabilities: `caps=If,Loop,Return` (no Break)
|
||||
- Missing: ConstStep capability
|
||||
- Approach: Extend Pattern2 to handle return (similar to break) or create Pattern2Return variant
|
||||
- Fixture: `apps/tests/phase257_p0_last_index_of_min.hako`
|
||||
- Integration smokes: `phase257_p0_last_index_of_vm.sh`, `phase257_p0_last_index_of_llvm_exe.sh`
|
||||
|
||||
次(P1.5 Task 3):
|
||||
- `ValueId(57)` が「何の JoinIR 値の remap 結果か」を確定し、定義側(dst)が MIR に落ちているかを追う
|
||||
- 例: `sep_len = sep.length()` の BoxCall dst が収集/変換/順序のどこかで欠けていないか
|
||||
|
||||
188
docs/development/current/main/phases/phase-257/README.md
Normal file
188
docs/development/current/main/phases/phase-257/README.md
Normal file
@ -0,0 +1,188 @@
|
||||
# Phase 257: Loop with Early Return Pattern
|
||||
|
||||
Status: Active
|
||||
Scope: Pattern6(ScanWithInit)拡張で reverse scan + early return を受理する
|
||||
Related:
|
||||
- Phase 256 完了(Pattern2 boundary SSOT 化、entry_param_mismatch 根治)
|
||||
- North star: `docs/development/current/main/design/join-explicit-cfg-construction.md`
|
||||
|
||||
## Current Status (SSOT)
|
||||
|
||||
- Target first FAIL: `json_lint_vm / StringUtils.last_index_of/2`
|
||||
- Pattern: Loop with early return (backward scan)
|
||||
- Approach: Extend Pattern6(ScanWithInit)to support reverse scan + early return
|
||||
|
||||
---
|
||||
|
||||
## Background
|
||||
|
||||
### 失敗詳細
|
||||
|
||||
**テスト**: json_lint_vm (quick profile)
|
||||
**エラー**: `[joinir/freeze] Loop lowering failed: JoinIR does not support this pattern`
|
||||
**関数**: `StringUtils.last_index_of/2`
|
||||
|
||||
#### エラーメッセージ全体
|
||||
|
||||
```
|
||||
[phase143/debug] Attempting loop_true_if_break/continue pattern (P0/P1)
|
||||
[phase143/debug] Pattern out-of-scope: NotLoopTrue
|
||||
[trace:dev] phase121/shadow: shadow=skipped signature_basis=kinds=Block,Stmt(local(i)),Loop,Block,If,Block,Stmt(return(value)),Stmt(assign(i)),Stmt(return(value));exits=return;writes=i;reads=ch,i,s;caps=If,Loop,Return;conds=(var:i >= lit:int:0)|(other:MethodCall == var:ch)
|
||||
[trace:dev] loop_canonicalizer: Function: StringUtils.last_index_of/2
|
||||
[trace:dev] loop_canonicalizer: Skeleton steps: 0
|
||||
[trace:dev] loop_canonicalizer: Carriers: 0
|
||||
[trace:dev] loop_canonicalizer: Has exits: false
|
||||
[trace:dev] loop_canonicalizer: Decision: FAIL_FAST
|
||||
[trace:dev] loop_canonicalizer: Missing caps: [ConstStep]
|
||||
[trace:dev] loop_canonicalizer: Reason: Phase 143-P2: Loop does not match read_digits(loop(true)), skip_whitespace, parse_number, continue, parse_string, or parse_array pattern
|
||||
[ERROR] ❌ MIR compilation error: [joinir/freeze] Loop lowering failed: JoinIR does not support this pattern, and LoopBuilder has been removed.
|
||||
Function: StringUtils.last_index_of/2
|
||||
Hint: This loop pattern is not supported. All loops must use JoinIR lowering.
|
||||
```
|
||||
|
||||
### 期待される動作
|
||||
|
||||
`StringUtils.last_index_of(s, ch)` が正常にコンパイルされ、文字列内の最後の文字位置を返すこと。
|
||||
|
||||
### 実際の動作
|
||||
|
||||
Loop canonicalizer が `ConstStep` を要求しているが、このループは早期リターンを持つため Pattern2Break として認識されていない。
|
||||
|
||||
### 最小再現コード
|
||||
|
||||
```nyash
|
||||
// apps/tests/phase257_p0_last_index_of_min.hako
|
||||
static box Main {
|
||||
main() {
|
||||
local s = "hello world"
|
||||
local ch = "o"
|
||||
|
||||
// Find last occurrence of ch in s
|
||||
local i = s.length() - 1
|
||||
|
||||
loop(i >= 0) {
|
||||
if s.substring(i, i + 1) == ch {
|
||||
return i // Early return when found
|
||||
}
|
||||
i = i - 1 // Backward scan
|
||||
}
|
||||
|
||||
return -1 // Not found
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 分析
|
||||
|
||||
#### ループ構造
|
||||
|
||||
1. **条件**: `i >= 0` (backward scan)
|
||||
2. **ボディ**:
|
||||
- If branch: マッチング検出時
|
||||
- `return i` で早期リターン
|
||||
- After if: マッチなし
|
||||
- `i = i - 1` で 1 つ戻る(定数ステップ)
|
||||
3. **特徴**:
|
||||
- **Early return**: break ではなく return を使用
|
||||
- **Backward scan**: `i--` で逆方向スキャン
|
||||
- **Capabilities**: `caps=If,Loop,Return` (no Break)
|
||||
- **Exits**: `exits=return` (両方の return を検出)
|
||||
|
||||
#### Pattern6(index_of)との比較
|
||||
|
||||
| Feature | Pattern6/index_of | last_index_of |
|
||||
|---------|-------------------|---------------|
|
||||
| 走査方向 | forward(`i = i + 1`) | reverse(`i = i - 1`) |
|
||||
| 早期終了 | 見つかったら return / exit PHI | 見つかったら return |
|
||||
| 通常終了 | not-found return(例: `-1`) | not-found return(`-1`) |
|
||||
| JoinIR | `main/loop_step/k_exit` | 同じ語彙で表現できる |
|
||||
|
||||
#### 提案される実装アプローチ
|
||||
|
||||
**Option A: Pattern6 拡張(推奨 / 最小差分)**
|
||||
|
||||
Pattern6(ScanWithInit)を “scan direction” を持つ形に一般化する(forward / reverse)。
|
||||
|
||||
1. **検出条件**:
|
||||
- init: `i = s.length() - 1`(reverse)または `i = 0`(forward)
|
||||
- loop cond: `i >= 0`(reverse)または `i < bound`(forward)
|
||||
- body: `if s.substring(i, i + 1) == ch { return i }` + step update
|
||||
- step: `i = i - 1`(reverse const step)または `i = i + 1`(forward const step)
|
||||
|
||||
2. **JoinIR lowering**:
|
||||
- Pattern6 の語彙(`main/loop_step/k_exit`)を維持
|
||||
- scan direction に応じて:
|
||||
- stop 判定(`i < 0` or `i >= bound`)
|
||||
- const step(`+1` / `-1`)
|
||||
- return は既存の “exit PHI + post-loop guard” を必要最小で再利用する(DCE回避は既存Box優先)
|
||||
|
||||
3. **Boundary construction**:
|
||||
- Phase 256 系の契約(`JumpArgsLayout` / contract checks)に従い、推測しない
|
||||
- `expr_result` は `return i` の経路でのみ使用(not-found は `-1`)
|
||||
|
||||
**Option B: Pattern8_ReverseScanReturn 新設**
|
||||
|
||||
Pattern6 を触らずに、reverse scan 専用パターンとして箱を追加する。
|
||||
(影響範囲は狭いが、scan 系が分裂する)
|
||||
|
||||
**Option C: Normalization(将来)**
|
||||
|
||||
return/break を正規化して共通語彙へ落とし、JoinIR patterns を縮退させる。
|
||||
(Phase 257 ではやらない)
|
||||
|
||||
### 実装計画
|
||||
|
||||
#### 推奨方針
|
||||
|
||||
**Option A**(Pattern6 拡張)を推奨。
|
||||
|
||||
理由:
|
||||
- 既に index_of/find 系で “scan + not-found return” を Pattern6 が担っている
|
||||
- last_index_of はその自然な派生(reverse scan + const step -1)
|
||||
- Pattern2(break 前提)を膨らませずに済む
|
||||
|
||||
#### P0 タスク
|
||||
|
||||
1) **Fixture & integration smokes**(完了)
|
||||
- `apps/tests/phase257_p0_last_index_of_min.hako`
|
||||
- `tools/smokes/v2/profiles/integration/apps/phase257_p0_last_index_of_vm.sh`
|
||||
- `tools/smokes/v2/profiles/integration/apps/phase257_p0_last_index_of_llvm_exe.sh`
|
||||
|
||||
2) **Pattern6 detector/extractor 拡張(reverse scan)**
|
||||
- `src/mir/builder/control_flow/joinir/patterns/pattern6_scan_with_init.rs`
|
||||
- reverse scan 形を accept し、parts(init/cond/step/return/not-found)を抽出
|
||||
|
||||
3) **Pattern6 lowerer 拡張**
|
||||
- reverse scan の stop 判定・step を JoinIR へ落とす(語彙は既存 Pattern6 を維持)
|
||||
|
||||
4) **検証**
|
||||
- `bash tools/smokes/v2/profiles/integration/apps/phase257_p0_last_index_of_vm.sh`
|
||||
- `./tools/smokes/v2/run.sh --profile quick`(最初の FAIL が次へ進む)
|
||||
|
||||
### 注意(P0ではやらない)
|
||||
|
||||
- 正規化での return/break 統一(Phase 257 ではやらない)
|
||||
- scan 系の大改造(まずは reverse scan の受理を最小差分で固定)
|
||||
|
||||
---
|
||||
|
||||
## 備考
|
||||
|
||||
- Phase 256 で Pattern2Break の境界構築が SSOT 化済み(entry_param_mismatch 根治)
|
||||
- この知見を活かし、Pattern2Return も同じ原則で実装する
|
||||
- "Early exit" として break と return を統一的に扱うことで、将来の拡張性も高まる
|
||||
|
||||
---
|
||||
|
||||
## 進捗(P0)
|
||||
|
||||
### 次のステップ
|
||||
|
||||
1. Pattern6 の forward scan parts と差分を整理(init/cond/step/return)
|
||||
2. reverse scan の extractor を実装(Fail-Fast)
|
||||
3. JoinIR lowerer へ reverse scan を追加(既存 contract に従う)
|
||||
4. integration smokes + quick profile を回して SSOT 更新
|
||||
|
||||
---
|
||||
|
||||
**最終更新**: 2025-12-20
|
||||
Reference in New Issue
Block a user