feat(joinir): Phase 33-22 CommonPatternInitializer & JoinIRConversionPipeline integration
Unifies initialization and conversion logic across all 4 loop patterns, eliminating code duplication and establishing single source of truth. ## Changes ### Infrastructure (New) - CommonPatternInitializer (117 lines): Unified loop var extraction + CarrierInfo building - JoinIRConversionPipeline (127 lines): Unified JoinIR→MIR→Merge flow ### Pattern Refactoring - Pattern 1: Uses CommonPatternInitializer + JoinIRConversionPipeline (-25 lines) - Pattern 2: Uses CommonPatternInitializer + JoinIRConversionPipeline (-25 lines) - Pattern 3: Uses CommonPatternInitializer + JoinIRConversionPipeline (-25 lines) - Pattern 4: Uses CommonPatternInitializer + JoinIRConversionPipeline (-40 lines) ### Code Reduction - Total reduction: ~115 lines across all patterns - Zero code duplication in initialization/conversion - Pattern files: 806 lines total (down from ~920) ### Quality Improvements - Single source of truth for initialization - Consistent conversion flow across all patterns - Guaranteed boundary.loop_var_name setting (prevents SSA-undef bugs) - Improved maintainability and testability ### Testing - All 4 patterns tested and passing: - Pattern 1 (Simple While): ✅ - Pattern 2 (With Break): ✅ - Pattern 3 (If-Else PHI): ✅ - Pattern 4 (With Continue): ✅ ### Documentation - Phase 33-22 inventory and results document - Updated joinir-architecture-overview.md with new infrastructure ## Breaking Changes None - pure refactoring with no API changes 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
194
docs/development/current/main/phase-33-16-design.md
Normal file
194
docs/development/current/main/phase-33-16-design.md
Normal file
@ -0,0 +1,194 @@
|
||||
# Phase 33-16: Loop Header PHI SSOT 設計書
|
||||
|
||||
## 現状
|
||||
|
||||
Phase 33-15 で SSA-undef エラーを修正したが、対処療法として exit_phi_inputs と carrier_inputs 収集をスキップした。
|
||||
結果、joinir_min_loop.hako のループ結果値が正しくない(期待値2、実際0)。
|
||||
|
||||
## 根本原因
|
||||
|
||||
JoinIR の関数パラメータ(i_param, i_exit)は MIR 空間で SSA 定義を持たない。
|
||||
|
||||
```
|
||||
JoinIR:
|
||||
fn loop_step(i_param): // ← i_param は関数パラメータ
|
||||
...
|
||||
Jump(k_exit, [i_param]) // ← exit 時に i_param を渡す
|
||||
...
|
||||
i_next = i_param + 1
|
||||
Call(loop_step, [i_next]) // ← tail call
|
||||
|
||||
fn k_exit(i_exit): // ← i_exit も関数パラメータ
|
||||
Return(i_exit)
|
||||
```
|
||||
|
||||
MIR にインライン化すると:
|
||||
- `i_param` は BoundaryInjector Copy でエントリー時に設定される
|
||||
- tail call は `Copy { dst: i_param_remapped, src: i_next_remapped }` に変換される
|
||||
- **しかし exit path では i_param_remapped に有効な値がない!**
|
||||
|
||||
## 解決策: LoopHeaderPhiBuilder
|
||||
|
||||
ループヘッダーブロックに PHI を配置して、carrier の「現在値」を追跡する。
|
||||
|
||||
### 目標構造
|
||||
|
||||
```
|
||||
host_entry:
|
||||
i_init = 0
|
||||
Jump → loop_header
|
||||
|
||||
loop_header:
|
||||
i_phi = PHI [(host_entry, i_init), (latch, i_next)] ← NEW!
|
||||
exit_cond = !(i_phi < 3)
|
||||
Branch exit_cond → exit, body
|
||||
|
||||
body:
|
||||
break_cond = (i_phi >= 2)
|
||||
Branch break_cond → exit, continue
|
||||
|
||||
continue:
|
||||
i_next = i_phi + 1
|
||||
Jump → loop_header // latch edge
|
||||
|
||||
exit:
|
||||
// i_phi がループ終了時の正しい値!
|
||||
```
|
||||
|
||||
### 実装計画
|
||||
|
||||
#### 1. LoopHeaderPhiBuilder (完了)
|
||||
|
||||
`src/mir/builder/control_flow/joinir/merge/loop_header_phi_builder.rs` に実装済み。
|
||||
|
||||
```rust
|
||||
pub struct LoopHeaderPhiInfo {
|
||||
pub header_block: BasicBlockId,
|
||||
pub carrier_phis: BTreeMap<String, CarrierPhiEntry>,
|
||||
pub expr_result_phi: Option<ValueId>,
|
||||
}
|
||||
```
|
||||
|
||||
#### 2. merge パイプラインへの組み込み (TODO)
|
||||
|
||||
`merge/mod.rs` の merge_joinir_mir_blocks() を修正:
|
||||
|
||||
```rust
|
||||
// Phase 3: Remap ValueIds
|
||||
remap_values(builder, &used_values, &mut remapper, debug)?;
|
||||
|
||||
// Phase 3.5: Build loop header PHIs (NEW!)
|
||||
let loop_header_info = if boundary.is_some() && is_loop_pattern {
|
||||
loop_header_phi_builder::LoopHeaderPhiBuilder::build(
|
||||
builder,
|
||||
header_block_id, // ← Pattern lowerer から渡す必要あり
|
||||
entry_block_id,
|
||||
loop_var_name,
|
||||
loop_var_init,
|
||||
&[], // carriers
|
||||
expr_result_is_loop_var,
|
||||
debug,
|
||||
)?
|
||||
} else {
|
||||
LoopHeaderPhiInfo::empty(BasicBlockId(0))
|
||||
};
|
||||
|
||||
// Phase 4: Merge blocks and rewrite instructions
|
||||
// instruction_rewriter に loop_header_info を渡す
|
||||
let merge_result = instruction_rewriter::merge_and_rewrite(
|
||||
builder,
|
||||
mir_module,
|
||||
&mut remapper,
|
||||
&value_to_func_name,
|
||||
&function_params,
|
||||
&mut loop_header_info, // latch_incoming を設定するため mut
|
||||
boundary,
|
||||
debug,
|
||||
)?;
|
||||
|
||||
// Phase 4.5: Finalize loop header PHIs (NEW!)
|
||||
if boundary.is_some() && loop_header_info.carrier_phis.len() > 0 {
|
||||
loop_header_phi_builder::LoopHeaderPhiBuilder::finalize(
|
||||
builder,
|
||||
&loop_header_info,
|
||||
debug,
|
||||
)?;
|
||||
}
|
||||
```
|
||||
|
||||
#### 3. instruction_rewriter 修正 (TODO)
|
||||
|
||||
Phase 33-15 の skip ロジックを削除し、以下を実装:
|
||||
|
||||
**Tail call 処理時** (276-335行):
|
||||
```rust
|
||||
// tail call の args を latch_incoming として記録
|
||||
for (i, arg_val_remapped) in args.iter().enumerate() {
|
||||
// carrier 名を特定(loop_var_name or carrier_name)
|
||||
let carrier_name = get_carrier_name_for_index(i);
|
||||
loop_header_info.set_latch_incoming(
|
||||
carrier_name,
|
||||
new_block_id, // latch block
|
||||
*arg_val_remapped, // 次のイテレーション値
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
**Exit path 処理時** (350-435行):
|
||||
```rust
|
||||
// exit_phi_inputs に header PHI の dst を使う
|
||||
if let Some(phi_dst) = loop_header_info.get_carrier_phi(loop_var_name) {
|
||||
exit_phi_inputs.push((new_block_id, phi_dst));
|
||||
}
|
||||
|
||||
// carrier_inputs にも header PHI の dst を使う
|
||||
for (carrier_name, entry) in &loop_header_info.carrier_phis {
|
||||
carrier_inputs.entry(carrier_name.clone())
|
||||
.or_insert_with(Vec::new)
|
||||
.push((new_block_id, entry.phi_dst));
|
||||
}
|
||||
```
|
||||
|
||||
#### 4. JoinInlineBoundary 拡張 (TODO)
|
||||
|
||||
header_block 情報を Pattern lowerer から渡す:
|
||||
|
||||
```rust
|
||||
pub struct JoinInlineBoundary {
|
||||
// ... existing fields ...
|
||||
|
||||
/// Phase 33-16: Loop header block ID (for LoopHeaderPhiBuilder)
|
||||
/// Set by Pattern lowerers that need header PHI generation.
|
||||
pub loop_header_block: Option<BasicBlockId>,
|
||||
}
|
||||
```
|
||||
|
||||
#### 5. Pattern 2 lowerer 修正 (TODO)
|
||||
|
||||
`pattern2_with_break.rs` で header_block を設定:
|
||||
|
||||
```rust
|
||||
boundary.loop_header_block = Some(loop_step_entry_block);
|
||||
```
|
||||
|
||||
### テスト戦略
|
||||
|
||||
| テスト | 期待値 | 検証ポイント |
|
||||
|--------|--------|-------------|
|
||||
| joinir_min_loop.hako | `return i` → 2 | expr_result PHI が正しい値を持つ |
|
||||
| trim 系テスト | 回帰なし | carrier PHI が正しく動作 |
|
||||
| SSA-undef | エラーなし | 定義済み ValueId のみ参照 |
|
||||
|
||||
## 実装の複雑さ
|
||||
|
||||
**中程度**。主な変更点:
|
||||
1. merge/mod.rs: Phase 3.5 と 4.5 追加
|
||||
2. instruction_rewriter.rs: latch_incoming 記録と PHI dst 参照
|
||||
3. Pattern lowerers: header_block 情報の追加
|
||||
|
||||
## 次のステップ
|
||||
|
||||
1. merge/mod.rs に Phase 3.5/4.5 フックを追加
|
||||
2. instruction_rewriter に loop_header_info 引数を追加
|
||||
3. Pattern 2 lowerer で header_block を設定
|
||||
4. テストで検証
|
||||
Reference in New Issue
Block a user