Files
hakorune/docs/development/current/main/refactoring-5-1-pattern3-exitmeta.md
nyash-codex 8394018694 refactor(joinir): Pattern 3 ExitMeta化 - Hardcoded ValueIds削除
Refactoring 5.1: Pattern 3 を Pattern 4 と同じ ExitMeta ベースアーキテクチャに統一化

Changes:
1. loop_with_if_phi_minimal.rs
   - 署名: Option<JoinModule> → Result<(JoinModule, JoinFragmentMeta), String>
   - ExitMeta 動的生成ロジック追加(sum, count)
   - インポート追加: carrier_info::{ExitMeta, JoinFragmentMeta}

2. pattern3_with_if_phi.rs
   - Hardcoded 定数削除(PATTERN3_K_EXIT_*_ID 2個削除)
   - Manual exit binding 42行 → ExitMetaCollector 4行に置き換え
   - インポート追加: ExitMetaCollector

3. loop_patterns/with_if_phi.rs
   - Result型変更に対応(.ok()? で変換)

Benefits:
- Pattern 3/4 アーキテクチャ統一化 
- 19行純削減(+55 -74行、3ファイル合計) 
- Hardcoded ValueIds 完全撤廃 
- Phase 213 AST-based generalization の基盤強化 

Tests: All tests passing, loop_if_phi.hako outputs "sum=9" correctly
2025-12-10 00:29:25 +09:00

361 lines
9.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Refactoring 5.1: Pattern 3 Hardcoded ValueIds → ExitMeta化
**Date**: 2025-12-09
**Status**: 🚧 In Progress
**Estimated Time**: 3-4 hours
**Priority**: HIGH
---
## 📋 目標
Pattern 3 lowerer`loop_with_if_phi_minimal.rs`)を Pattern 4 と同じ ExitMeta ベースのアーキテクチャに統一化する。これにより:
1. ✅ Hardcoded ValueIds 定数削除(`PATTERN3_K_EXIT_*_ID`
2. ✅ Exit binding の動的生成
3. ✅ Multi-carrier support の完全化
4. ✅ Pattern 3/4 の共通化度向上
---
## 🔄 変更対象ファイル
### ファイル1: `src/mir/join_ir/lowering/loop_with_if_phi_minimal.rs`
**現在:**
```rust
pub(crate) fn lower_loop_with_if_phi_pattern(
_scope: LoopScopeShape,
join_value_space: &mut JoinValueSpace,
) -> Option<JoinModule>
```
**変更後:**
```rust
use crate::mir::join_ir::lowering::join_fragment_meta::JoinFragmentMeta;
pub(crate) fn lower_loop_with_if_phi_pattern(
_scope: LoopScopeShape,
join_value_space: &mut JoinValueSpace,
) -> Result<(JoinModule, JoinFragmentMeta), String>
```
**変更内容:**
1. 戻り値を `Option<JoinModule>``Result<(JoinModule, JoinFragmentMeta), String>` に変更
2. `JoinFragmentMeta` を構築して返す
3. ExitMeta を動的に生成
### ファイル2: `src/mir/builder/control_flow/joinir/patterns/pattern3_with_if_phi.rs`
**現在:**
```rust
const PATTERN3_K_EXIT_SUM_FINAL_ID: ValueId = ValueId(24); // Hardcoded!
const PATTERN3_K_EXIT_COUNT_FINAL_ID: ValueId = ValueId(25); // Hardcoded!
// Lines 118-164: has_count 条件分岐で exit_bindings を手動構築
let exit_bindings = if has_count {
vec![
LoopExitBinding {
join_exit_value: PATTERN3_K_EXIT_SUM_FINAL_ID, // ← Hardcoded!
...
},
...
]
} else {
// Dummy count hack
...
}
```
**変更後:**
```rust
// Hardcoded 定数削除(削除)
// Lines 300-314: ExitMeta から exit_bindings を動的生成
let exit_bindings = ExitMetaCollector::collect(
self,
&exit_meta,
debug,
);
// Carrier validationPattern 4と同じ
for carrier in &carrier_info.carriers {
if !exit_bindings.iter().any(|b| b.carrier_name == carrier.name) {
return Err(format!(
"[cf_loop/pattern3] Carrier '{}' not found in exit bindings",
carrier.name
));
}
}
```
---
## 🔧 実装ステップ
### Step 1: `loop_with_if_phi_minimal.rs` の k_exit 関数を分析
**現在の k_exit 実装lines 401-415:**
```rust
let mut k_exit_func = JoinFunction::new(
k_exit_id,
"k_exit".to_string(),
vec![sum_final, count_final], // Phase 195: Multi-carrier
);
k_exit_func.body.push(JoinInst::Ret {
value: Some(sum_final),
});
```
**分析:**
- `k_exit` の parameters: `[sum_final, count_final]` が exit PHI
- k_exit は `sum_final` を return最初の carrier
- ExitMeta として記録すべき情報:
- `sum``sum_final` (ValueId(24))
- `count``count_final` (ValueId(25))
### Step 2: ExitMeta 構築ロジック追加
lowerer の最後に以下を追加:
```rust
// Phase 213: Build ExitMeta for dynamic exit binding generation
use crate::mir::join_ir::lowering::join_fragment_meta::JoinFragmentMeta;
use crate::mir::join_ir::lowering::exit_meta::ExitMeta;
use std::collections::HashMap;
let mut exit_values = HashMap::new();
// Map carrier names to their k_exit parameter ValueIds
exit_values.insert("sum".to_string(), sum_final);
if has_count {
exit_values.insert("count".to_string(), count_final);
}
let exit_meta = ExitMeta {
exit_values,
exit_func_id: k_exit_id,
};
let fragment_meta = JoinFragmentMeta {
exit_meta,
// その他フィールド
};
Ok((join_module, fragment_meta))
```
**注:** `has_count` フラグは必要。multi-carrier に対応するため。
### Step 3: `pattern3_with_if_phi.rs` での lowerer 呼び出し変更
**現在lines 109-116:**
```rust
let join_module = match lower_loop_with_if_phi_pattern(ctx.loop_scope, &mut join_value_space) {
Some(module) => module,
None => {
trace::trace().debug("pattern3", "Pattern 3 lowerer returned None");
return Ok(None);
}
};
```
**変更後:**
```rust
let (join_module, exit_meta) = match lower_loop_with_if_phi_pattern(ctx.loop_scope, &mut join_value_space) {
Ok(result) => result,
Err(e) => {
trace::trace().debug("pattern3", &format!("Pattern 3 lowerer failed: {}", e));
return Err(format!("[cf_loop/pattern3] Lowering failed: {}", e));
}
};
trace::trace().debug(
"pattern3",
&format!("ExitMeta: {} exit values", exit_meta.exit_values.len())
);
for (carrier_name, join_value) in &exit_meta.exit_values {
trace::trace().debug(
"pattern3",
&format!(" {} → ValueId({})", carrier_name, join_value.0)
);
}
```
### Step 4: Exit binding 動的生成Hardcoded 定数削除)
**現在lines 8-30, 118-164:**
```rust
// Hardcoded constants
const PATTERN3_K_EXIT_SUM_FINAL_ID: ValueId = ValueId(24);
const PATTERN3_K_EXIT_COUNT_FINAL_ID: ValueId = ValueId(25);
// Manual exit_bindings construction with has_count branching
let exit_bindings = if has_count {
vec![
LoopExitBinding {
carrier_name: "sum".to_string(),
join_exit_value: PATTERN3_K_EXIT_SUM_FINAL_ID, // ← Hardcoded!
host_slot: sum_var_id,
},
LoopExitBinding {
carrier_name: "count".to_string(),
join_exit_value: PATTERN3_K_EXIT_COUNT_FINAL_ID, // ← Hardcoded!
host_slot: count_var_id,
}
]
} else {
// Single-carrier hack
...
}
```
**削除/変更:**
```rust
// Hardcoded constants削除
// const PATTERN3_K_EXIT_SUM_FINAL_ID: ValueId = ValueId(24);
// const PATTERN3_K_EXIT_COUNT_FINAL_ID: ValueId = ValueId(25);
// Dynamic exit binding generation追加
use super::super::merge::exit_line::meta_collector::ExitMetaCollector;
let exit_bindings = ExitMetaCollector::collect(
self,
&exit_meta,
debug,
);
// Carrier validation
for carrier in &carrier_info.carriers {
if !exit_bindings.iter().any(|b| b.carrier_name == carrier.name) {
return Err(format!(
"[cf_loop/pattern3] Carrier '{}' not found in exit bindings",
carrier.name
));
}
}
```
### Step 5: Dummy Count Backward Compat 簡略化
**現在lines 145-164:**
```rust
if has_count {
// Multi-carrier case
(vec![...], vec![...], vec![...])
} else {
// Single-carrier case with Dummy void
let dummy_count_id = constant::emit_void(self);
(vec![...], vec![..., dummy_count_id], vec![...])
}
```
**簡略化:**
```rust
// Phase 213: Always use multi-carrier structure
// Single-carrier tests will use dummy void internally
let join_inputs = vec![ValueId(0), ValueId(1), ValueId(2)];
let mut host_inputs = vec![ctx.loop_var_id, sum_var_id];
if has_count {
host_inputs.push(carrier_count_var_id);
} else {
// Use void dummy for backward compat
host_inputs.push(constant::emit_void(self));
}
```
---
## 📊 Before/After Code Change Summary
### `loop_with_if_phi_minimal.rs`
| 項目 | Before | After | 削減 |
|------|--------|-------|------|
| 関数署名 | `Option<JoinModule>` | `Result<(JoinModule, JoinFragmentMeta)>` | - |
| k_exit 構築 | あり | あり(変更なし) | 0行 |
| ExitMeta 構築 | なし | 新規追加 | +20行 |
| **計** | 428行 | 448行 | +20行 |
### `pattern3_with_if_phi.rs`
| 項目 | Before | After | 削減 |
|------|--------|-------|------|
| Hardcoded 定数 | 2個lines 8-30 | 削除 | -2行 |
| Manual exit binding | ありlines 118-164 | ExitMetaCollector化 | -40行 |
| Dummy count hack | あり | 簡略化 | -5行 |
| Lowerer呼び出し | None/Some | Ok/Err | -5行 |
| ExitMeta debug | なし | 新規追加 | +10行 |
| **計** | 191行 | 149行 | **-42行22%削減)** |
---
## ✅ テスト戦略
### Test 1: `loop_if_phi.hako` (既存 test)
**動作確認:**
```bash
./target/release/hakorune --dump-mir apps/tests/loop_if_phi.hako 2>&1 | grep -A 5 "k_exit"
```
**期待:** k_exit が sum/count を正しく処理(変わらず)
### Test 2: Multi-carrier testsPhase 195
**確認対象:**
- `test_pattern3_multi_carrier_sum_count`
- Carrier binding が動的に生成されることを確認
### Test 3: Cargo test
```bash
cargo test --release pattern3 2>&1 | tail -20
```
---
## 🚨 リスク & ミティゲーション
### リスク 1: ExitMeta 構築が複雑
**ミティゲーション:**
- Pattern 4 のコードをコピーペースト基準にする
- 最小限の変更に留める
### リスク 2: ExitMetaCollector の動作確認
**ミティゲーション:**
- Pattern 4 で既に使用済み(実績あり)
- Carrier validation で エラー検出
### リスク 3: Dummy count backward compat 破損
**ミティゲーション:**
- has_count フラグで分岐を保つ
- 古い単一キャリア テストは動作のまま
---
## 📝 Checklist
- [ ] Step 1: k_exit 実装分析完了
- [ ] Step 2: ExitMeta 構築ロジック追加
- [ ] Step 3: lowerer 呼び出し変更
- [ ] Step 4: Hardcoded 定数削除 + ExitMetaCollector 導入
- [ ] Step 5: Dummy count 簡略化
- [ ] Step 6: Build 成功確認
- [ ] Step 7: Test 実行確認
- [ ] Step 8: Commit & Document 更新
- [ ] Refactoring 5.1 COMPLETE ✅
---
## 📚 参考資料
- Pattern 4 実装: `src/mir/builder/control_flow/joinir/patterns/pattern4_with_continue.rs` (lines 300-380)
- ExitMetaCollector: `src/mir/builder/control_flow/joinir/merge/exit_line/meta_collector.rs`
- JoinFragmentMeta: `src/mir/join_ir/lowering/join_fragment_meta.rs`