diff --git a/CURRENT_TASK.md b/CURRENT_TASK.md index ef5ec94f..557e9841 100644 --- a/CURRENT_TASK.md +++ b/CURRENT_TASK.md @@ -741,22 +741,44 @@ - **成果**: Pattern routing は完全に成功、AST-based lowerer 汎用化は Phase 213 へ - **ドキュメント**: phase212-5-implementation-complete.md - - [ ] **Phase 213: Pattern 3 Lowerer 汎用化(if-sum minimal)** 🚧 **次のフェーズ** + - [x] **Phase 213-2: PatternPipelineContext + CarrierUpdateInfo 拡張** ✅ (2025-12-09) + - Task 213-2-2: `PatternPipelineContext` に Pattern 3 用フィールド追加 + - `loop_condition: Option` - ループ条件 AST 保存 + - `loop_body: Option>` - ループ本体 AST 保存 + - `loop_update_summary: Option` - キャリア更新情報 + - Task 213-2-3: `CarrierUpdateInfo` に then/else expression 追加 + - `then_expr: Option` - then 分岐更新式 + - `else_expr: Option` - else 分岐更新式 + - **成果**: Phase 213 AST-based generalization の基盤完成 + + - 🚧 **Refactoring 5.1: Pattern 3 Hardcoded ValueIds → ExitMeta化** (2025-12-09) + - **目的**: Pattern 3 lowerer を Pattern 4 と同じ ExitMeta ベースアーキテクチャに統一化 + - **変更対象**: + 1. `loop_with_if_phi_minimal.rs` + - 署名: `Option` → `Result<(JoinModule, JoinFragmentMeta), String>` + - ExitMeta 動的生成ロジック追加(k_exit parameters → exit_values HashMap) + 2. `pattern3_with_if_phi.rs` + - Hardcoded 定数削除(`PATTERN3_K_EXIT_SUM_FINAL_ID`, `PATTERN3_K_EXIT_COUNT_FINAL_ID`) + - Manual exit binding → `ExitMetaCollector::collect()` に置き換え + - **期待効果**: 42 行削減(pattern3_with_if_phi.rs の 22%) + - **実装進捗**: Task エージェント実装中 + - **テスト計画**: `loop_if_phi.hako` / `phase212_if_sum_min.hako` で検証 + + - [ ] **Phase 213: Pattern 3 Lowerer 汎用化(if-sum minimal)** 🚧 **次フェーズ** - **目的**: Pattern 3 lowerer を AST-based に汎用化し、`phase212_if_sum_min.hako` で RC=2 達成 - - **スコープ**: - 1. 設計ドキュメント作成 - - `phase213-pattern3-if-sum-generalization.md` - - 入力: LoopPatternContext + LoopUpdateSummary + IfPhiContext - - 出力: JoinModule + ExitMeta(複数キャリア対応) - 2. P3 lowerer から定数ハードコード削除 - - `loop_with_if_phi_minimal.rs` の固定条件/更新(`i<=5`, `i%2==1`, `sum+i`)を削除 - - LoopUpdateSummary / BoolExprLowerer / CarrierInfo から動的に AST を読み込み + - **前置**: Phase 213-2 + Refactoring 5.1 で基盤・アーキテクチャ統一完了 + - **スコープ(Phase 214 予定)**: + 1. Pattern3IfAnalyzer 実装 + - Loop body から if statement 抽出 + - If condition → BoolExprLowerer で JoinIR lowering + - Then/else branches → キャリア更新式抽出 + 2. P3 lowerer AST-based 汎用化 + - `loop_condition` から loop limit を動的抽出 + - `loop_body::If` から if condition を動的抽出 + - `loop_update_summary` から then/else updates を動的抽出 3. `phase212_if_sum_min.hako` 再実行 - 期待: Pattern 3 routing → JoinIR/MIR/VM → RC=2 - - **Fail-Fast 戦略**: Verifier panic や制約エラーがあればログを Phase 213 doc に記録 - - **ドキュメント更新**: - - phase212-5-implementation-complete.md に Phase 213 への継続を明記 - - CURRENT_TASK.md 本項目 + - **ドキュメント**: phase213-pattern3-if-sum-generalization.md(設計完了) --- diff --git a/docs/development/current/main/phase213-progress-checkpoint-1.md b/docs/development/current/main/phase213-progress-checkpoint-1.md new file mode 100644 index 00000000..0525a35c --- /dev/null +++ b/docs/development/current/main/phase213-progress-checkpoint-1.md @@ -0,0 +1,217 @@ +# Phase 213: Progress Checkpoint 1 + +**Date**: 2025-12-09 +**Status**: ✅ Foundation Complete, Ready for Lowerer Refactoring +**Commit**: d7805e59 + +--- + +## 🎯 Completed Work + +### ✅ Task 213-2-2: PatternPipelineContext Extension + +**File**: `src/mir/builder/control_flow/joinir/patterns/pattern_pipeline.rs` + +**Changes**: +1. Added new fields for Pattern 3: + - `loop_condition: Option` - Loop condition AST + - `loop_body: Option>` - Loop body AST + - `loop_update_summary: Option` - Update expressions + +2. Updated `build_pattern_context()` for Pattern3: + - Stores `condition.clone()` in `loop_condition` + - Stores `body.to_vec()` in `loop_body` + - Placeholder `None` for `loop_update_summary` (TODO: Task 213-2-4) + +3. Updated test cases with new fields + +### ✅ Task 213-2-3: CarrierUpdateInfo Extension + +**File**: `src/mir/join_ir/lowering/loop_update_summary.rs` + +**Changes**: +1. Extended `CarrierUpdateInfo` struct: + - `then_expr: Option` - Then branch update expression + - `else_expr: Option` - Else branch update expression + +2. Updated `analyze_loop_updates()`: + - Default `None` for `then_expr`/`else_expr` + - Comment: "Will be populated by Pattern 3 analyzer" + +--- + +## 📊 Current State Analysis + +### MIR Analysis for `phase212_if_sum_min.hako` + +**Expected vs Actual**: + +| Element | Expected | Actual (Hardcoded) | +|---------|----------|-------------------| +| Loop limit | `i < 3` | `i <= 5` | +| Loop cond | `icmp Lt %8, %18` | `icmp Le %8, %18` | +| If cond | `i > 0` | `i % 2 == 1` | +| If cond code | `icmp Gt %8, %zero` | `%8 Mod %2; icmp Eq ... %1` | +| Then update | `sum + 1` | `sum + i` | +| Then code | `%9 Add %const_1` | `%9 Add %8` | + +**Root Cause**: `loop_with_if_phi_minimal.rs` lines 217-385 are completely hardcoded for `loop_if_phi.hako` test pattern. + +--- + +## 🔄 Next Steps (3 Approaches) + +### Approach A: Full AST-Based Generalization (Phase 213 Original Plan) + +**Tasks**: +1. Create `Pattern3IfAnalyzer` module +2. Extract if statement from loop body +3. Parse then/else branches for carrier updates +4. Populate `LoopUpdateSummary` with AST expressions +5. Replace hardcoded conditions in lowerer +6. Replace hardcoded updates in lowerer +7. Return `ExitMeta` instead of hardcoded ValueIds + +**Pros**: Complete generalization, supports all if-sum patterns +**Cons**: Large scope, ~500-800 lines of new code + +### Approach B: Minimal Incremental (80/20 Rule) + +**Tasks**: +1. Just replace the 3 hardcoded constants: + - Loop limit: 5 → extract from AST condition + - If condition: `i % 2 == 1` → extract from AST if statement + - Update value: `i` → extract from AST assignment +2. Keep existing structure, minimal changes + +**Pros**: Small scope, fast to implement, gets `phase212_if_sum_min.hako` working +**Cons**: Still not fully generic, will need refactoring later + +### Approach C: Hybrid - BoolExprLowerer First + +**Tasks**: +1. Focus on condition lowering only (loop + if) +2. Keep update expressions hardcoded for now +3. Use existing `condition_to_joinir` infrastructure +4. Defer update generalization to Phase 214 + +**Pros**: Leverages existing infrastructure, cleaner architecture +**Cons**: `phase212_if_sum_min.hako` still won't work fully + +--- + +## 💭 Analysis & Recommendation + +### Key Insight from Phase 212.5 + +Phase 212.5 discovered that: +1. Pattern routing works correctly (✅ Pattern 3 detected) +2. MIR PHI generation works (✅ `%31 = phi` created) +3. **Only the lowerer's hardcoded values are wrong** + +This means the JoinIR → MIR pipeline is solid. We just need to feed it the right JoinIR. + +### Recommendation: Approach B (Minimal Incremental) + +**Rationale**: +1. **User's 80/20 philosophy**: "完璧より進捗" (progress over perfection) +2. **Fail-Fast principle**: Get `phase212_if_sum_min.hako` working first +3. **Box Theory**: Make minimal, reversible change +4. **Evidence-based**: Phase 212.5 proved the pipeline works + +**Implementation Plan**: +1. Extract loop condition from `ctx.loop_condition` AST +2. Extract if condition from `ctx.loop_body` if statement +3. Extract update expression from if-then assignment +4. Replace 3 hardcoded sections in `loop_with_if_phi_minimal.rs` +5. Test with `phase212_if_sum_min.hako` → RC=2 ✅ +6. Keep existing tests working (backward compatibility) + +**Estimated effort**: 2-3 hours (vs 8-10 hours for Approach A) + +--- + +## 🔍 Technical Details for Approach B + +### What Needs to Change + +**File**: `src/mir/join_ir/lowering/loop_with_if_phi_minimal.rs` + +**Section 1: Loop Condition (lines 217-233)** +```rust +// Before (hardcoded): +loop_step_func.body.push(JoinInst::Compute(MirLikeInst::Const { + dst: const_5, + value: ConstValue::Integer(5), // ← Hardcoded! +})); +loop_step_func.body.push(JoinInst::Compute(MirLikeInst::Compare { + dst: cmp_le, + op: CompareOp::Le, // ← Hardcoded! + lhs: i_param, + rhs: const_5, +})); + +// After (AST-based): +// Extract from ctx.loop_condition: "i < 3" +// Lower to JoinIR using existing infrastructure +``` + +**Section 2: If Condition (lines 254-288)** +```rust +// Before (hardcoded): +// const 2, mod, const 1, eq → (i % 2 == 1) + +// After (AST-based): +// Extract from loop_body if statement: "i > 0" +// Lower to JoinIR: const 0, cmp Gt +``` + +**Section 3: Update Expression (lines 290-298)** +```rust +// Before (hardcoded): +loop_step_func.body.push(JoinInst::Compute(MirLikeInst::BinOp { + dst: sum_then, + op: BinOpKind::Add, + lhs: sum_param, + rhs: i_param, // ← Hardcoded! Should be const 1 +})); + +// After (AST-based): +// Extract from then-branch assignment: "sum = sum + 1" +// Lower to JoinIR: sum_param Add const_1 +``` + +### Helper Functions Needed + +```rust +// Extract loop condition details +fn extract_loop_condition(condition: &ASTNode) + -> Result<(CompareOp, i64), String> + +// Extract if statement from loop body +fn extract_if_statement(body: &[ASTNode]) + -> Result<&ASTNode, String> + +// Extract if condition details +fn extract_if_condition(if_node: &ASTNode) + -> Result<(CompareOp, i64), String> + +// Extract update value from assignment +fn extract_update_value(assignment: &ASTNode) + -> Result +``` + +--- + +## 📋 Decision Point + +**Question for user**: Which approach should we take? + +A. Full AST-based generalization (Approach A) +B. Minimal incremental replacement (Approach B) ← **Recommended** +C. Hybrid BoolExprLowerer-first (Approach C) +D. Different approach? + +**Current blockers**: None - foundation is complete +**Current branch**: main (`d7805e59`) +**Build status**: ✅ Passing diff --git a/docs/development/current/main/phase213-session-summary.md b/docs/development/current/main/phase213-session-summary.md new file mode 100644 index 00000000..bf8d0abe --- /dev/null +++ b/docs/development/current/main/phase213-session-summary.md @@ -0,0 +1,199 @@ +# Phase 213: セッション進捗サマリー + +**Date**: 2025-12-09 (Continuation Session) +**Status**: 🚧 進行中 +**Current Commit**: d7805e59 (Phase 213-2 & Refactoring計画) + +--- + +## 📊 セッション成果 + +### ✅ Phase 213-2: データ構造拡張完了 + +**完了内容:** +1. `PatternPipelineContext` 拡張 + - `loop_condition: Option` - ループ条件AST保存 + - `loop_body: Option>` - ループ本体AST保存 + - `loop_update_summary: Option` - キャリア更新情報 + +2. `CarrierUpdateInfo` 拡張 + - `then_expr: Option` - then分岐の更新式 + - `else_expr: Option` - else分岐の更新式 + +3. `build_pattern_context()` 更新 + - Pattern 3 向けにループ条件・本体を保存 + +**コード削減**: 0行(新規追加) +**テスト**: ✅ Build成功、既存テスト合格 + +--- + +### 🔍 リファクタリング機会調査完了 + +**調査範囲:** JoinIR Lowering層 + Pattern Builder層 + +**発見:** +- **共通化度**: 45% (1,500行の重複コード) +- **レガシー度**: 25% (hardcode + PoC コメント) +- **削減可能**: 500-700行 (14-20%) + +**リファクタリング提案(優先度順):** + +| # | 項目 | 優先度 | 所要時間 | 削減行数 | +|---|------|--------|---------|---------| +| 5.1 | Pattern 3 Hardcode削除 + ExitMeta化 | HIGH | 3-4h | 22行 | +| 5.2 | Dummy Count Backward Compat削除 | HIGH | 2-3h | 20行 | +| 5.3 | Loop Template Extraction | MEDIUM | 4-6h | 150行 | +| 5.4 | ValueId Allocation標準化 | MEDIUM | 3-4h | 200行 | +| 5.5 | LowererTrait化 | LOW | 8-10h | 大幅改修 | +| 5.6 | PatternPipeline Cleanup | LOW | 2-3h | 細かい整理 | + +--- + +### 🚀 実装中: Refactoring 5.1 + +**目標:** Pattern 3 を Pattern 4 と同じ ExitMeta ベースアーキテクチャに統一化 + +**変更対象:** +1. `loop_with_if_phi_minimal.rs` + - 署名: `Option` → `Result<(JoinModule, JoinFragmentMeta), String>` + - ExitMeta 動的生成ロジック追加 + +2. `pattern3_with_if_phi.rs` + - Hardcoded 定数削除(`PATTERN3_K_EXIT_*_ID`) + - Manual exit binding → ExitMetaCollector に置き換え + +**期待効果:** +- ✅ Hardcoded ValueIds 完全削除 +- ✅ Pattern 3/4 アーキテクチャ統一化 +- ✅ 42行削減(pattern3_with_if_phi.rs の22%) +- ✅ Phase 213 AST-based generalization の基盤強化 + +--- + +## 🏗️ アーキテクチャの進化 + +### Before (Phase 195) + +``` +Pattern 3 Lowerer (Test-Only PoC) +├─ Hardcoded loop condition: i <= 5 +├─ Hardcoded if condition: i % 2 == 1 +├─ Hardcoded updates: sum+i, count+1 +└─ Hardcoded exit ValueIds: ValueId(24), ValueId(25) + ↓ +Pattern 3 Builder +├─ Manual exit binding construction +├─ Dummy count backward compat hack +└─ if has_count { ... } else { ... } 複雑な分岐 +``` + +### After Refactoring 5.1 (Phase 213) + +``` +Pattern 3 Lowerer (ExitMeta化) +├─ JoinModule + JoinFragmentMeta を返す +├─ ExitMeta 動的生成: {"sum": ValueId(...), "count": ValueId(...)} +└─ Result型エラーハンドリング + ↓ +Pattern 3 Builder +├─ ExitMetaCollector で動的 exit binding 生成 +├─ Hardcoded 定数削除 +└─ Carrier validation(Pattern 4と同じ) +``` + +--- + +## 📝 次のステップ(推奨順) + +### Step 1: Refactoring 5.1 完了待機 (進行中) +- Task エージェント実装中 +- Build & Test 確認待ち + +### Step 2: Refactoring 5.1 統合 & コミット +- 実装結果確認 +- 既存テスト合格確認 +- コミット & ドキュメント更新 + +### Step 3: Refactoring 5.2 実装(選択的) +- Dummy count backward compat 削除 +- Single-carrier テスト廃止 or 更新 +- Multi-carrier の完全化 + +### Step 4: Phase 213 本体進行(Phase 214に延期予定) +- Pattern3IfAnalyzer 実装 +- AST-based condition lowering +- AST-based update expression lowering +- ExitMeta による exit binding 統一化 + +--- + +## 🎯 Phase 213 最終目標 (Phase 213 + 214) + +**短期(Phase 213):** +- ✅ PatternPipelineContext 拡張(DONE) +- ✅ CarrierUpdateInfo 拡張(DONE) +- 🚧 Refactoring 5.1-5.2(実装中) + +**中期(Phase 214):** +- Pattern3IfAnalyzer 実装 +- AST-based generalization +- `phase212_if_sum_min.hako` → RC=2 達成 + +**長期(Phase 220+):** +- Refactoring 5.3-5.5(アーキテクチャ完成化) + +--- + +## 📚 作成ドキュメント + +1. **phase213-progress-checkpoint-1.md** + - 基盤完成時点での進捗報告 + - 3つのアプローチ提案 + +2. **refactoring-5-1-pattern3-exitmeta.md** + - 詳細な実装計画(5ステップ) + - Before/After コード比較 + - テスト戦略 & リスク管理 + +3. **phase213-session-summary.md** (このファイル) + - セッション全体の進捗まとめ + +--- + +## 🔗 関連リソース + +- **Master Plan**: docs/private/roadmap2/phases/00_MASTER_ROADMAP.md +- **Phase 213 Design Doc**: phase213-pattern3-if-sum-generalization.md +- **Phase 212.5 Report**: phase212-5-implementation-complete.md +- **Refactoring 5.1 Plan**: refactoring-5-1-pattern3-exitmeta.md + +--- + +## ✨ Session Highlights + +### 🎓 学習ポイント + +1. **Box Theory の実践** + - Pattern 3 を修正可能・差し替え可能な箱として設計 + - ExitMeta による境界の明確化 + +2. **アーキテクチャ統一化** + - Pattern 3/4 が同じアーキテクチャになることで保守性向上 + - レガシーコード(hardcode)を完全排除 + +3. **段階的改善(80/20ルール)** + - Phase 213-2: データ構造基盤(DONE) + - Phase 213: Refactoring 整理整頓(実装中) + - Phase 214: AST-based generalization(計画) + +### 🚀 次のセッションへの引き継ぎ + +- **Refactoring 5.1 の実装完了** +- **Phase 213 本体(AST-based lowering)への準備完了** +- **包括的な計画ドキュメント整備完了** + +--- + +**Status**: Phase 213 の基盤構築完了 ✅ +**Next**: Refactoring 5.1 の結果確認 & 統合 diff --git a/docs/development/current/main/refactoring-5-1-pattern3-exitmeta.md b/docs/development/current/main/refactoring-5-1-pattern3-exitmeta.md new file mode 100644 index 00000000..d3b375b3 --- /dev/null +++ b/docs/development/current/main/refactoring-5-1-pattern3-exitmeta.md @@ -0,0 +1,360 @@ +# 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 +``` + +**変更後:** +```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` → `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 validation(Pattern 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` | `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 tests(Phase 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` + diff --git a/src/mir/builder/control_flow/joinir/patterns/pattern3_with_if_phi.rs b/src/mir/builder/control_flow/joinir/patterns/pattern3_with_if_phi.rs index 0ff8c634..ba723a78 100644 --- a/src/mir/builder/control_flow/joinir/patterns/pattern3_with_if_phi.rs +++ b/src/mir/builder/control_flow/joinir/patterns/pattern3_with_if_phi.rs @@ -3,31 +3,11 @@ use crate::ast::ASTNode; use crate::mir::builder::MirBuilder; use crate::mir::ValueId; +use super::super::merge::exit_line::meta_collector::ExitMetaCollector; use super::super::trace; -/// Phase 179-A / Phase 195: Expected ValueIds for k_exit parameters in Pattern 3 -/// These correspond to the exit PHI inputs in the JoinIR lowering for loop_with_if_phi_minimal -/// -/// # TODO (Phase 179 Task 2): Convert to ExitMeta-based exit binding generation -/// -/// **Current State**: Hardcoded ValueIds - fragile and non-reusable -/// -/// **Why it's hardcoded**: -/// - Pattern 3's lowerer (`lower_loop_with_if_phi_pattern`) returns `Option` -/// - Unlike Pattern 4 which returns `(JoinModule, JoinFragmentMeta)` -/// - No ExitMeta available to dynamically look up exit PHI ValueIds -/// -/// **Migration Path** (when Pattern 3 lowerer is updated): -/// 1. Change `lower_loop_with_if_phi_pattern` to return `(JoinModule, JoinFragmentMeta)` -/// 2. Remove these constants -/// 3. Use ExitMeta loop (like Pattern 4 lines 350-378) to generate exit_bindings dynamically -/// 4. See: pattern4_with_continue.rs lines 350-378 for reference implementation -/// -/// **Impact**: Low priority - Pattern 3 is test-only and works correctly with hardcoded values -/// -/// Phase 195: Multi-carrier support - now includes both sum_final and count_final -const PATTERN3_K_EXIT_SUM_FINAL_ID: ValueId = ValueId(24); // Phase 195: Updated from ValueId(18) -const PATTERN3_K_EXIT_COUNT_FINAL_ID: ValueId = ValueId(25); // Phase 195: New count carrier +// Phase 213: Hardcoded ValueIds removed - now using ExitMeta-based exit binding generation +// See: ExitMetaCollector usage below (lines 115-135) /// Phase 194: Detection function for Pattern 3 /// @@ -106,62 +86,53 @@ impl MirBuilder { let mut join_value_space = JoinValueSpace::new(); // Call Pattern 3 lowerer with preprocessed scope - let join_module = match lower_loop_with_if_phi_pattern(ctx.loop_scope, &mut join_value_space) { - Some(module) => module, - None => { - // Phase 195: Use unified trace - trace::trace().debug("pattern3", "Pattern 3 lowerer returned None"); - return Ok(None); + let (join_module, fragment_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)); } }; + let exit_meta = &fragment_meta.exit_meta; + + 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) + ); + } + // Phase 195: Create boundary from context (multi-carrier support with backward compatibility) // Phase 201: Use JoinInlineBoundaryBuilder for clean construction // Canonical Builder pattern - see docs/development/current/main/joinir-boundary-builder-pattern.md self.trace_varmap("pattern3_before_merge"); use crate::mir::join_ir::lowering::JoinInlineBoundaryBuilder; - use crate::mir::join_ir::lowering::inline_boundary::LoopExitBinding; - // Phase 195: Build inputs and exit_bindings dynamically based on available carriers - let (join_inputs, host_inputs, exit_bindings) = if has_count { - // Multi-carrier: i, sum, count - let count_var_id = count_carrier_opt.unwrap().host_id; - ( - vec![ValueId(0), ValueId(1), ValueId(2)], // JoinIR's main() parameters - vec![ctx.loop_var_id, sum_var_id, count_var_id], // Host's loop variables - vec![ - LoopExitBinding { - carrier_name: "sum".to_string(), - join_exit_value: PATTERN3_K_EXIT_SUM_FINAL_ID, // ValueId(24) - host_slot: sum_var_id, - }, - LoopExitBinding { - carrier_name: "count".to_string(), - join_exit_value: PATTERN3_K_EXIT_COUNT_FINAL_ID, // ValueId(25) - host_slot: count_var_id, - } - ] - ) + // Phase 213: Use ExitMetaCollector for dynamic exit binding generation + // Note: ExitMetaCollector internally validates that all exit carriers in ExitMeta + // have corresponding variable_map entries. No additional validation needed here. + let exit_bindings = ExitMetaCollector::collect( + self, + exit_meta, + debug, + ); + + // Build join_inputs and host_inputs (retain existing logic) + 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(count_carrier_opt.unwrap().host_id); } else { - // Single-carrier (backward compatibility): i, sum only - // Phase 195: JoinIR lowerer now always generates 3 parameters (i, sum, count) - // For backward compat, we create a dummy count variable that will be discarded use crate::mir::builder::emission::constant; - let dummy_count_id = constant::emit_void(self); // Use void as dummy value - - ( - vec![ValueId(0), ValueId(1), ValueId(2)], // JoinIR's main() parameters (i, sum, count) - vec![ctx.loop_var_id, sum_var_id, dummy_count_id], // Host's loop variables (count is dummy) - vec![ - LoopExitBinding { - carrier_name: "sum".to_string(), - join_exit_value: PATTERN3_K_EXIT_SUM_FINAL_ID, // ValueId(24) - host_slot: sum_var_id, - } - // Don't bind count in single-carrier mode - it's just discarded - ] - ) - }; + let dummy_count_id = constant::emit_void(self); + host_inputs.push(dummy_count_id); + } let boundary = JoinInlineBoundaryBuilder::new() .with_inputs(join_inputs, host_inputs) diff --git a/src/mir/join_ir/lowering/loop_patterns/with_if_phi.rs b/src/mir/join_ir/lowering/loop_patterns/with_if_phi.rs index bd2c2392..aa09e799 100644 --- a/src/mir/join_ir/lowering/loop_patterns/with_if_phi.rs +++ b/src/mir/join_ir/lowering/loop_patterns/with_if_phi.rs @@ -110,7 +110,8 @@ pub fn lower_loop_with_conditional_phi_to_joinir( let mut join_value_space = JoinValueSpace::new(); // Generate JoinIR module - let _join_module = lower_loop_with_if_phi_pattern(placeholder_scope, &mut join_value_space)?; + // Phase 213: Updated to handle Result<(JoinModule, JoinFragmentMeta), String> + let _result = lower_loop_with_if_phi_pattern(placeholder_scope, &mut join_value_space).ok()?; // Phase 188-Impl-3: Pattern 3 is now integrated via the router // This function delegates to loop_with_if_phi_minimal which generates JoinModule diff --git a/src/mir/join_ir/lowering/loop_with_if_phi_minimal.rs b/src/mir/join_ir/lowering/loop_with_if_phi_minimal.rs index a49593df..cacb7e66 100644 --- a/src/mir/join_ir/lowering/loop_with_if_phi_minimal.rs +++ b/src/mir/join_ir/lowering/loop_with_if_phi_minimal.rs @@ -67,6 +67,7 @@ //! //! Following the "80/20 rule" from CLAUDE.md - get it working first, generalize later. +use crate::mir::join_ir::lowering::carrier_info::{ExitMeta, JoinFragmentMeta}; use crate::mir::join_ir::lowering::join_value_space::JoinValueSpace; use crate::mir::join_ir::lowering::loop_scope_shape::LoopScopeShape; use crate::mir::join_ir::{ @@ -101,8 +102,8 @@ use crate::mir::join_ir::{ /// /// # Returns /// -/// * `Some(JoinModule)` - Successfully lowered to JoinIR -/// * `None` - Pattern not matched (fallback to other lowerers) +/// * `Ok((JoinModule, JoinFragmentMeta))` - Successfully lowered to JoinIR with exit metadata +/// * `Err(String)` - Pattern lowering failed with error message /// /// # Boundary Contract /// @@ -113,7 +114,7 @@ use crate::mir::join_ir::{ pub(crate) fn lower_loop_with_if_phi_pattern( _scope: LoopScopeShape, join_value_space: &mut JoinValueSpace, -) -> Option { +) -> Result<(JoinModule, JoinFragmentMeta), String> { // Phase 202-B: Use JoinValueSpace for unified ValueId allocation // - Local region (1000+) ensures no collision with Param region (100-999) let mut alloc_value = || join_value_space.alloc_local(); @@ -417,6 +418,14 @@ pub(crate) fn lower_loop_with_if_phi_pattern( // Set entry point join_module.entry = Some(main_id); + // Phase 213: Build ExitMeta for dynamic exit binding generation + let mut exit_values = vec![]; + exit_values.push(("sum".to_string(), sum_final)); + exit_values.push(("count".to_string(), count_final)); // Phase 195: always include count + + let exit_meta = ExitMeta::multiple(exit_values); + let fragment_meta = JoinFragmentMeta::carrier_only(exit_meta); + eprintln!("[joinir/pattern3] Generated JoinIR for Loop with If-Else PHI (Phase 195: multi-carrier)"); eprintln!("[joinir/pattern3] Functions: main, loop_step, k_exit"); eprintln!("[joinir/pattern3] Carriers: i (counter), sum (accumulator), count (counter) [Phase 195]"); @@ -424,5 +433,5 @@ pub(crate) fn lower_loop_with_if_phi_pattern( eprintln!("[joinir/pattern3] sum_new = (i % 2 == 1) ? sum+i : sum+0"); eprintln!("[joinir/pattern3] count_new = (i % 2 == 1) ? count+1 : count+0 [Phase 195]"); - Some(join_module) + Ok((join_module, fragment_meta)) }