199 lines
6.1 KiB
Markdown
199 lines
6.1 KiB
Markdown
|
|
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<ValueId>, // 既存(main の params)
|
|||
|
|
pub host_inputs: Vec<ValueId>, // 既存(host 側の ValueId)
|
|||
|
|
|
|||
|
|
// 新規追加
|
|||
|
|
pub loop_invariants: Vec<(String, ValueId)>, // (変数名, host ValueId)
|
|||
|
|
|
|||
|
|
pub exit_bindings: Vec<LoopExitBinding>, // 既存(LoopState 用)
|
|||
|
|
pub loop_var_name: Option<String>, // 既存(単一変数用、廃止予定)
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**利点**:
|
|||
|
|
- 既存の 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: 未着手
|