feat(joinir): Phase 201 - JoinInlineBoundaryBuilder expansion to Pattern3/4
- Task 201-1: Established canonical Builder pattern documentation - Created docs/development/current/main/joinir-boundary-builder-pattern.md - Documented Builder usage patterns for all patterns (P1/P2/P3/P4) - Added reference comments in pattern lowerers - Task 201-2: Refactored Pattern3 to use Builder (removed field mutations) - Replaced new_with_exit_bindings + field mutation with Builder chain - Pattern3: 2 carriers (i + sum), exit_bindings, loop_var_name - Proper LoopExitBinding struct usage - Task 201-3: Refactored Pattern4 to use Builder (continue/Trim support) - Replaced new_with_exit_bindings + field mutation with Builder chain - Pattern4: Dynamic carrier count, proper boundary construction - Task 201-4: Added unit tests for Pattern3/4 style boundaries - test_builder_pattern3_style: Two carriers, exit_bindings validation - test_builder_pattern4_style: Dynamic carrier count validation - Verified no field mutations remain (exit_binding.rs uses deprecated fields only) - Task 201-5: Updated architecture docs and CURRENT_TASK - joinir-architecture-overview.md: Builder now applied to all patterns - CURRENT_TASK.md: Phase 201 completion entry All patterns now use consistent boundary construction via Builder. Tests: All patterns pass (挙動不変).
This commit is contained in:
@ -47,6 +47,13 @@
|
||||
→ Task 200-2: JoinInlineBoundaryBuilder 導入(Pattern2 で試験導入、Builder パターンで構築統一化)。
|
||||
→ Task 200-3: JoinIRVerifier 追加(LoopHeader PHI / ExitLine 契約のデバッグ検証)。
|
||||
→ Task 200-4: CURRENT_TASK/overview 微修正(Phase 200 内容を反映)。
|
||||
- [x] Phase 201: JoinInlineBoundaryBuilder Pattern3/4 展開 ✅ (2025-12-08)
|
||||
→ Task 201-1: Pattern2 の Builder 使用パターンを正として固める(設計ドキュメント作成)。
|
||||
→ Task 201-2: Pattern3 を Builder に載せ替え(フィールド直書き排除)。
|
||||
→ Task 201-3: Pattern4 を Builder に載せ替え(continue/Trim 特例対応)。
|
||||
→ Task 201-4: 共通ルールチェック & unit test 拡張(2つの新テスト追加)。
|
||||
→ Task 201-5: ドキュメント更新(overview + CURRENT_TASK)。
|
||||
→ **成果**: 全パターン(P1/P2/P3/P4)で Builder 統一完了、フィールド直書き完全排除、挙動不変(テスト全 PASS)。
|
||||
|
||||
---
|
||||
|
||||
|
||||
@ -171,12 +171,12 @@ JoinIR ラインで守るべきルールを先に書いておくよ:
|
||||
- 変数再接続はヘッダ PHI の dst を使って `builder.variable_map` を更新(Reconnector)。
|
||||
- expr 用の PHI には一切触れない(carrier 専用ライン)。
|
||||
|
||||
- **JoinInlineBoundaryBuilder(Phase 200-2 追加)**
|
||||
- **JoinInlineBoundaryBuilder(Phase 200-2 / Phase 201 完了)**
|
||||
- ファイル: `src/mir/join_ir/lowering/inline_boundary_builder.rs`
|
||||
- 責務:
|
||||
- JoinInlineBoundary の構築を Builder パターンで統一化。
|
||||
- フィールド直書きの散乱を防ぎ、inputs/outputs/condition_bindings/exit_bindings/loop_var_name/expr_result の設定を fluent API で実施。
|
||||
- Pattern2 で試験導入済み(Pattern3/4 は将来適用予定)。
|
||||
- **Phase 201 で Pattern1/2/3/4 全てに適用完了**(境界情報組み立てを 1 箇所に集約)。
|
||||
|
||||
- **JoinIRVerifier(Phase 200-3 追加)**
|
||||
- ファイル: `src/mir/builder/control_flow/joinir/merge/mod.rs`(debug_assertions 専用関数)
|
||||
|
||||
@ -0,0 +1,95 @@
|
||||
# JoinInlineBoundaryBuilder 使用パターン(Phase 201)
|
||||
|
||||
## Pattern2 の Canonical 形式
|
||||
|
||||
Pattern2 で確立した Builder 使用パターンを他の Pattern に適用する際の基準。
|
||||
|
||||
### Builder 構築順序
|
||||
|
||||
```rust
|
||||
let boundary = JoinInlineBoundaryBuilder::new()
|
||||
.with_inputs(join_inputs, host_inputs)
|
||||
.with_condition_bindings(condition_bindings)
|
||||
.with_exit_bindings(exit_bindings)
|
||||
.with_loop_var_name(Some(loop_var_name.clone()))
|
||||
.with_expr_result(fragment_meta.expr_result)
|
||||
.build();
|
||||
```
|
||||
|
||||
### 各メソッドの意味
|
||||
|
||||
1. **with_inputs(join_inputs, host_inputs)**
|
||||
- ループパラメータの橋渡し
|
||||
- join_inputs: JoinIR 側の ValueId リスト
|
||||
- host_inputs: MIR 側の ValueId リスト
|
||||
|
||||
2. **with_condition_bindings(bindings)**
|
||||
- 条件式専用変数の橋渡し
|
||||
- ConditionBinding のリスト(変数名 + JoinIR ValueId のペア)
|
||||
|
||||
3. **with_exit_bindings(bindings)**
|
||||
- キャリア出口の橋渡し
|
||||
- (変数名, JoinIR ValueId) のペア
|
||||
|
||||
4. **with_loop_var_name(name)**
|
||||
- ループ変数名(LoopHeader PHI 生成用)
|
||||
- Some(変数名) または None
|
||||
|
||||
5. **with_expr_result(expr)**
|
||||
- ループが式として返す値
|
||||
- Some(ValueId) または None
|
||||
|
||||
### フィールド直書き禁止
|
||||
|
||||
- ❌ `boundary.loop_var_name = ...;` 等の直接代入は禁止
|
||||
- ✅ Builder の fluent API のみ使用
|
||||
|
||||
### Pattern別の使用パターン
|
||||
|
||||
#### Pattern2 (with break)
|
||||
```rust
|
||||
let boundary = JoinInlineBoundaryBuilder::new()
|
||||
.with_inputs(vec![ValueId(0)], vec![loop_var_id])
|
||||
.with_condition_bindings(condition_bindings)
|
||||
.with_exit_bindings(exit_bindings)
|
||||
.with_loop_var_name(Some(loop_var_name.clone()))
|
||||
.with_expr_result(fragment_meta.expr_result)
|
||||
.build();
|
||||
```
|
||||
|
||||
#### Pattern3 (with if-phi)
|
||||
```rust
|
||||
let boundary = JoinInlineBoundaryBuilder::new()
|
||||
.with_inputs(
|
||||
vec![ValueId(0), ValueId(1)], // i, sum
|
||||
vec![loop_var_id, sum_var_id],
|
||||
)
|
||||
.with_exit_bindings(vec![
|
||||
("sum".to_string(), ValueId(18))
|
||||
])
|
||||
.with_loop_var_name(Some(loop_var_name.clone()))
|
||||
.build();
|
||||
```
|
||||
|
||||
#### Pattern4 (with continue)
|
||||
```rust
|
||||
let boundary = JoinInlineBoundaryBuilder::new()
|
||||
.with_inputs(join_inputs, host_inputs) // Dynamic carrier count
|
||||
.with_exit_bindings(exit_bindings)
|
||||
.with_loop_var_name(Some(loop_var_name.clone()))
|
||||
.build();
|
||||
```
|
||||
|
||||
## Phase 201 目標
|
||||
|
||||
- ✅ Pattern2/3/4 全てで Builder 使用に統一
|
||||
- ✅ フィールド直書き(`boundary.field = value;`)完全排除
|
||||
- ✅ 境界情報の組み立てを 1 箇所(Builder)に集約
|
||||
- ✅ テスト全 PASS(挙動不変)
|
||||
|
||||
## 実装ファイル
|
||||
|
||||
- Builder 本体: `src/mir/join_ir/lowering/inline_boundary_builder.rs`
|
||||
- Pattern2: `src/mir/builder/control_flow/joinir/patterns/pattern2_with_break.rs`
|
||||
- Pattern3: `src/mir/builder/control_flow/joinir/patterns/pattern3_with_if_phi.rs`
|
||||
- Pattern4: `src/mir/builder/control_flow/joinir/patterns/pattern4_with_continue.rs`
|
||||
@ -92,13 +92,16 @@ impl MirBuilder {
|
||||
};
|
||||
|
||||
// Phase 33-22: Create boundary for JoinIR conversion
|
||||
let mut boundary = crate::mir::join_ir::lowering::inline_boundary::JoinInlineBoundary::new_inputs_only(
|
||||
vec![ValueId(0)], // JoinIR's main() parameter (loop variable)
|
||||
vec![loop_var_id], // Host's loop variable
|
||||
);
|
||||
// Phase 33-16: Set loop_var_name to enable header PHI generation
|
||||
// This is required for the merge pipeline to generate header PHIs for SSA correctness
|
||||
boundary.loop_var_name = Some(loop_var_name.clone());
|
||||
// Phase 201: Use JoinInlineBoundaryBuilder for clean construction
|
||||
// Canonical Builder pattern - see docs/development/current/main/joinir-boundary-builder-pattern.md
|
||||
use crate::mir::join_ir::lowering::JoinInlineBoundaryBuilder;
|
||||
let boundary = JoinInlineBoundaryBuilder::new()
|
||||
.with_inputs(
|
||||
vec![ValueId(0)], // JoinIR's main() parameter (loop variable)
|
||||
vec![loop_var_id], // Host's loop variable
|
||||
)
|
||||
.with_loop_var_name(Some(loop_var_name.clone())) // Phase 33-16: Enable header PHI generation for SSA correctness
|
||||
.build();
|
||||
|
||||
// Phase 33-22: Use JoinIRConversionPipeline for unified conversion flow
|
||||
use super::conversion_pipeline::JoinIRConversionPipeline;
|
||||
|
||||
@ -329,6 +329,7 @@ impl MirBuilder {
|
||||
let exit_bindings = ExitMetaCollector::collect(self, &exit_meta, debug);
|
||||
|
||||
// Phase 200-2: Use JoinInlineBoundaryBuilder for clean construction
|
||||
// Canonical Builder pattern - see docs/development/current/main/joinir-boundary-builder-pattern.md
|
||||
use crate::mir::join_ir::lowering::JoinInlineBoundaryBuilder;
|
||||
let boundary = JoinInlineBoundaryBuilder::new()
|
||||
.with_inputs(
|
||||
|
||||
@ -100,24 +100,28 @@ impl MirBuilder {
|
||||
};
|
||||
|
||||
// Phase 33-22: Create boundary for JoinIR conversion
|
||||
// Phase 201: Use JoinInlineBoundaryBuilder for clean construction
|
||||
// Canonical Builder pattern - see docs/development/current/main/joinir-boundary-builder-pattern.md
|
||||
// Pattern 3 has TWO carriers: i and sum
|
||||
self.trace_varmap("pattern3_before_merge");
|
||||
let mut boundary = crate::mir::join_ir::lowering::inline_boundary::JoinInlineBoundary::new_with_exit_bindings(
|
||||
vec![ValueId(0), ValueId(1)], // JoinIR's main() parameters (i, sum init)
|
||||
vec![loop_var_id, sum_var_id], // Host's loop variables
|
||||
vec![
|
||||
use crate::mir::join_ir::lowering::JoinInlineBoundaryBuilder;
|
||||
use crate::mir::join_ir::lowering::inline_boundary::LoopExitBinding;
|
||||
let boundary = JoinInlineBoundaryBuilder::new()
|
||||
.with_inputs(
|
||||
vec![ValueId(0), ValueId(1)], // JoinIR's main() parameters (i, sum init)
|
||||
vec![loop_var_id, sum_var_id], // Host's loop variables
|
||||
)
|
||||
.with_exit_bindings(vec![
|
||||
// Phase 33-16: Only include non-loop-variable carriers in exit_bindings
|
||||
// The loop variable is handled separately via boundary.loop_var_name
|
||||
crate::mir::join_ir::lowering::inline_boundary::LoopExitBinding {
|
||||
LoopExitBinding {
|
||||
carrier_name: "sum".to_string(),
|
||||
join_exit_value: ValueId(18), // k_exit's parameter (sum_final)
|
||||
host_slot: sum_var_id, // variable_map["sum"]
|
||||
}
|
||||
],
|
||||
);
|
||||
// Phase 33-16: Set loop_var_name to enable header PHI generation
|
||||
// This is required for the merge pipeline to generate header PHIs for SSA correctness
|
||||
boundary.loop_var_name = Some(loop_var_name.clone());
|
||||
])
|
||||
.with_loop_var_name(Some(loop_var_name.clone())) // Phase 33-16: Enable header PHI generation for SSA correctness
|
||||
.build();
|
||||
|
||||
// Phase 33-22: Use JoinIRConversionPipeline for unified conversion flow
|
||||
use super::conversion_pipeline::JoinIRConversionPipeline;
|
||||
|
||||
@ -358,13 +358,14 @@ impl MirBuilder {
|
||||
&format!("join_inputs: {:?}", join_inputs.iter().map(|v| v.0).collect::<Vec<_>>())
|
||||
);
|
||||
|
||||
let mut boundary = crate::mir::join_ir::lowering::inline_boundary::JoinInlineBoundary::new_with_exit_bindings(
|
||||
join_inputs, // JoinIR's main() parameters (dynamic)
|
||||
host_inputs, // Host's loop variables (dynamic)
|
||||
exit_bindings,
|
||||
);
|
||||
// Phase 33-19: Set loop_var_name for proper exit PHI collection
|
||||
boundary.loop_var_name = Some(loop_var_name.clone());
|
||||
// Phase 201: Use JoinInlineBoundaryBuilder for clean construction
|
||||
// Canonical Builder pattern - see docs/development/current/main/joinir-boundary-builder-pattern.md
|
||||
use crate::mir::join_ir::lowering::JoinInlineBoundaryBuilder;
|
||||
let boundary = JoinInlineBoundaryBuilder::new()
|
||||
.with_inputs(join_inputs, host_inputs) // Dynamic carrier count
|
||||
.with_exit_bindings(exit_bindings)
|
||||
.with_loop_var_name(Some(loop_var_name.clone())) // Phase 33-19: Enable exit PHI collection
|
||||
.build();
|
||||
|
||||
// Phase 33-22: Use JoinIRConversionPipeline for unified conversion flow
|
||||
use super::conversion_pipeline::JoinIRConversionPipeline;
|
||||
|
||||
@ -201,4 +201,58 @@ mod tests {
|
||||
assert_eq!(boundary.join_inputs.len(), 0);
|
||||
assert_eq!(boundary.host_inputs.len(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_builder_pattern3_style() {
|
||||
// Pattern3 style: Two carriers (i + sum), exit_bindings, loop_var_name
|
||||
use super::super::condition_to_joinir::ConditionBinding;
|
||||
|
||||
let boundary = JoinInlineBoundaryBuilder::new()
|
||||
.with_inputs(vec![ValueId(0), ValueId(1)], vec![ValueId(100), ValueId(101)])
|
||||
.with_exit_bindings(vec![
|
||||
LoopExitBinding {
|
||||
carrier_name: "sum".to_string(),
|
||||
join_exit_value: ValueId(18),
|
||||
host_slot: ValueId(101),
|
||||
}
|
||||
])
|
||||
.with_loop_var_name(Some("i".to_string()))
|
||||
.build();
|
||||
|
||||
assert_eq!(boundary.join_inputs.len(), 2);
|
||||
assert_eq!(boundary.host_inputs.len(), 2);
|
||||
assert_eq!(boundary.exit_bindings.len(), 1);
|
||||
assert_eq!(boundary.exit_bindings[0].carrier_name, "sum");
|
||||
assert_eq!(boundary.loop_var_name, Some("i".to_string()));
|
||||
assert_eq!(boundary.expr_result, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_builder_pattern4_style() {
|
||||
// Pattern4 style: Dynamic carrier count, continue support
|
||||
let boundary = JoinInlineBoundaryBuilder::new()
|
||||
.with_inputs(
|
||||
vec![ValueId(0), ValueId(1), ValueId(2)], // i + 2 carriers
|
||||
vec![ValueId(100), ValueId(101), ValueId(102)]
|
||||
)
|
||||
.with_exit_bindings(vec![
|
||||
LoopExitBinding {
|
||||
carrier_name: "i".to_string(),
|
||||
join_exit_value: ValueId(11),
|
||||
host_slot: ValueId(100),
|
||||
},
|
||||
LoopExitBinding {
|
||||
carrier_name: "sum".to_string(),
|
||||
join_exit_value: ValueId(20),
|
||||
host_slot: ValueId(101),
|
||||
}
|
||||
])
|
||||
.with_loop_var_name(Some("i".to_string()))
|
||||
.build();
|
||||
|
||||
assert_eq!(boundary.exit_bindings.len(), 2);
|
||||
assert!(boundary.loop_var_name.is_some());
|
||||
assert_eq!(boundary.join_inputs.len(), 3);
|
||||
assert_eq!(boundary.host_inputs.len(), 3);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user