Status: Active Scope: Phase 255 (Pattern 6 multi-param loop wiring/PHI 対応) Related: - docs/development/current/main/10-Now.md - docs/development/current/main/phases/phase-254/README.md # Phase 255: Multi-param loop の boundary/PHI/wiring を SSOT 化する ## 前フェーズ(Phase 254)からの引き継ぎ Phase 254 で Pattern 6 (ScanWithInit) の実装が完了したが、integration テストで失敗: ``` [ERROR] ❌ [rust-vm] VM error: Invalid value: [rust-vm] use of undefined value ValueId(10) (fn=StringUtils.index_of/2, last_block=Some(BasicBlockId(4)), last_inst=Some(Compare { dst: ValueId(14), op: Ge, lhs: ValueId(10), rhs: ValueId(13) })) ``` ## 根本原因 **JoinIR→MIR merge/boundary システムが複数ループ変数を想定していない** ### 現状の仕様(Pattern 1-5) - 単一ループ変数前提(例: `i` のみ) - `with_loop_var_name()` が1つの変数名を受け取る - `exit_bindings` が単一 carrier を想定 ### Pattern 6 の要求(3変数ループ) ```nyash index_of(s, ch) { local i = 0 loop(i < s.length()) { if s.substring(i, i + 1) == ch { return i } i = i + 1 } return -1 } ``` 必要な変数: - `s`: haystack(ループ不変) - `ch`: needle(ループ不変) - `i`: loop index(ループ状態) ### 問題の詳細 1. **PHI ノード作成の不足**: ```rust // 期待: 3つの PHI ノード %4 = phi [%0, entry], [%4, loop] // s %5 = phi [%1, entry], [%5, loop] // ch %6 = phi [%2, entry], [%6, loop] // i // 実際: 1つだけ作られる %4 = phi [%0, entry], [%4, loop] // s だけ // %5 と %6 が undefined → 実行時エラー! ``` 2. **影響箇所**: - `JoinInlineBoundaryBuilder::with_loop_var_name()` - 単一変数想定 - `LoopExitBinding` の使い方 - exit_bindings に複数指定しても PHI が作られない - `JoinIRConversionPipeline` の merge 処理 - 複数 PHI 作成に未対応 ## 解決方針(SSOT 化) ### 方針: LoopState と Invariants を分けて wiring **LoopState(変化する変数)**: - `i`: ループカウンタ - 毎イテレーションで更新される - PHI ノードが必要 - exit_binding で final value を取得 **Invariants(不変な変数)**: - `s`, `ch`: ループ内で参照するが変化しない - PHI ノードは必要だが、すべてのイテレーションで同じ値 - `phi [init, init, init, ...]` の形になる ### 実装戦略 #### Option A(推奨): Boundary に invariants フィールド追加 ```rust pub struct JoinInlineBoundary { pub join_inputs: Vec, // 既存(main の params) pub host_inputs: Vec, // 既存(host 側の ValueId) // 新規追加 pub loop_invariants: Vec<(String, ValueId)>, // (変数名, host ValueId) pub exit_bindings: Vec, // 既存(LoopState 用) pub loop_var_name: Option, // 既存(単一変数用、廃止予定) } ``` **利点**: - 既存の Pattern 1-5 に影響なし - invariants の扱いを明示的に分離 - PHI 生成ロジックを追加しやすい #### Option B: exit_bindings を拡張 ```rust pub enum CarrierRole { LoopState, // 既存: 変化する変数 ConditionOnly, // 既存: 条件のみ LoopInvariant, // 新規: ループ不変 } ``` **問題点**: - `CarrierRole::LoopInvariant` を追加しても、現状の merge ロジックが対応していない - exit_bindings は "exit value" を想定しているが、invariants は "同じ値を保持" という意味合いが異なる ### 推奨: Option A - invariants を明示的に分離 - merge ロジックに invariants 専用の PHI 生成を追加 ## 実装タスク(P0) ### Task 1: Boundary 構造拡張 **ファイル**: `src/mir/join_ir/lowering/inline_boundary.rs` 1. `JoinInlineBoundary` に `loop_invariants` フィールド追加 2. `JoinInlineBoundaryBuilder::with_loop_invariants()` メソッド追加 ### Task 2: PHI 生成ロジック拡張 **ファイル**: `src/mir/join_ir/lowering/inline_boundary.rs` または merge 関連 1. `loop_invariants` から PHI ノードを生成 2. すべてのブロックで同じ値を持つ PHI として作成 3. variable_map に登録 ### Task 3: Pattern 6 の boundary 構築を修正 **ファイル**: `src/mir/builder/control_flow/joinir/patterns/pattern6_scan_with_init.rs` 1. `with_loop_invariants()` を使って s, ch を登録 2. `exit_bindings` は i のみ(LoopState) ```rust let boundary = JoinInlineBoundaryBuilder::new() .with_inputs( vec![s_param, ch_param, i_param], vec![s_host, ch_host, i_host], ) .with_loop_invariants(vec![ (parts.haystack.clone(), s_host), (parts.needle.clone(), ch_host), ]) .with_exit_bindings(vec![i_exit_binding]) .with_loop_var_name(Some(parts.loop_var.clone())) .build(); ``` ### Task 4: Unit Tests **ファイル**: `src/mir/join_ir/lowering/inline_boundary.rs` (tests module) 1. invariants を含む boundary 作成テスト 2. PHI ノードが正しく生成されることを確認 ### Task 5: Integration Tests **実行**: ```bash HAKORUNE_BIN=./target/release/hakorune bash tools/smokes/v2/profiles/integration/apps/phase254_p0_index_of_vm.sh HAKORUNE_BIN=./target/release/hakorune bash tools/smokes/v2/profiles/integration/apps/phase254_p0_index_of_llvm_exe.sh ``` **期待**: 両方 PASS(exit code 1) ## 禁止事項 - ❌ workaround / by-name 分岐 / ハック禁止 - ❌ Pattern 1-5 の動作を変更しない(regression 禁止) - ❌ フォールバック処理の追加禁止(Fail-Fast 原則維持) ## 受け入れ基準 - ✅ phase254_p0_index_of_vm.sh が PASS - ✅ phase254_p0_index_of_llvm_exe.sh が PASS - ✅ 最終的に `--profile quick` の最初の FAIL が次へ進む(index_of で freeze しない) - ✅ Pattern 1-5 の既存テストがすべて PASS(regression なし) ## 進捗(P0) - Task 1: Boundary 構造拡張: 未着手 - Task 2: PHI 生成ロジック拡張: 未着手 - Task 3: Pattern 6 boundary 修正: 未着手 - Task 4: Unit Tests: 未着手 - Task 5: Integration Tests: 未着手