diff --git a/docs/archive/cleanup/CLEANUP_SUMMARY_2025-11-06.md b/docs/archive/cleanup/CLEANUP_SUMMARY_2025-11-06.md index 53acc627..e3d78408 100644 --- a/docs/archive/cleanup/CLEANUP_SUMMARY_2025-11-06.md +++ b/docs/archive/cleanup/CLEANUP_SUMMARY_2025-11-06.md @@ -1,3 +1,5 @@ +Status: Historical + # Hakorune コードベース重複・共通化調査 - 実行サマリー **調査日**: 2025-11-06 diff --git a/docs/archive/cleanup/CLEANUP_SWEEP.md b/docs/archive/cleanup/CLEANUP_SWEEP.md index 04afca81..e09810b4 100644 --- a/docs/archive/cleanup/CLEANUP_SWEEP.md +++ b/docs/archive/cleanup/CLEANUP_SWEEP.md @@ -1,3 +1,5 @@ +Status: Historical + # Cleanup Sweep (Phase 11.8–12 Bridge) 目的 diff --git a/docs/archive/cleanup/DUPLICATION_INDEX.md b/docs/archive/cleanup/DUPLICATION_INDEX.md index 96a2853d..37a1a0d7 100644 --- a/docs/archive/cleanup/DUPLICATION_INDEX.md +++ b/docs/archive/cleanup/DUPLICATION_INDEX.md @@ -1,3 +1,5 @@ +Status: Historical + # コードベース重複・共通化調査 - インデックス Hakorune Rustコードベースにおける重複コードの特定と、DRY原則に基づく改善計画のドキュメントハブです。 diff --git a/docs/archive/cleanup/EXECUTIVE_SUMMARY.md b/docs/archive/cleanup/EXECUTIVE_SUMMARY.md index f63565f3..4628f13d 100644 --- a/docs/archive/cleanup/EXECUTIVE_SUMMARY.md +++ b/docs/archive/cleanup/EXECUTIVE_SUMMARY.md @@ -1,3 +1,5 @@ +Status: Historical + # レガシーコード削除プロジェクト - エグゼクティブサマリー **調査日**: 2025-11-06 diff --git a/docs/archive/cleanup/INSTRUCTION_DEDUP_2025-11.md b/docs/archive/cleanup/INSTRUCTION_DEDUP_2025-11.md index b532c96e..5900e52e 100644 --- a/docs/archive/cleanup/INSTRUCTION_DEDUP_2025-11.md +++ b/docs/archive/cleanup/INSTRUCTION_DEDUP_2025-11.md @@ -1,3 +1,5 @@ +Status: Historical + Instruction Deduplication — 2025‑11 Sweep (BinOp / Loop / Control Flow) Purpose @@ -48,4 +50,3 @@ Acceptance (first pass) - BinOp lowering in Builder and Program v0 bridge uses ssot/binop_lower exclusively. - Continue semantics unified: apply_increment_before_continue used in both bridge and (if applicable) builder. - No regressions in quick core profile; all new canaries PASS. - diff --git a/docs/archive/cleanup/LEGACY_FILES_DETAILED_LIST.md b/docs/archive/cleanup/LEGACY_FILES_DETAILED_LIST.md index 1625f1de..3b992645 100644 --- a/docs/archive/cleanup/LEGACY_FILES_DETAILED_LIST.md +++ b/docs/archive/cleanup/LEGACY_FILES_DETAILED_LIST.md @@ -1,3 +1,5 @@ +Status: Historical + # レガシーファイル詳細リスト **調査日**: 2025-11-06 diff --git a/docs/archive/cleanup/PHASE1_IMPLEMENTATION_GUIDE.md b/docs/archive/cleanup/PHASE1_IMPLEMENTATION_GUIDE.md index 3e2120dd..12558d9d 100644 --- a/docs/archive/cleanup/PHASE1_IMPLEMENTATION_GUIDE.md +++ b/docs/archive/cleanup/PHASE1_IMPLEMENTATION_GUIDE.md @@ -1,3 +1,5 @@ +Status: Historical + # Phase 1実装ガイド - 低リスク・高効果ヘルパー関数 ## 概要 diff --git a/docs/archive/cleanup/QUICK_CLEANUP_GUIDE.md b/docs/archive/cleanup/QUICK_CLEANUP_GUIDE.md index 5f777ae7..f2c10d16 100644 --- a/docs/archive/cleanup/QUICK_CLEANUP_GUIDE.md +++ b/docs/archive/cleanup/QUICK_CLEANUP_GUIDE.md @@ -1,3 +1,5 @@ +Status: Historical + # クイック削除ガイド - 今すぐ実行可能なレガシーコード削除 **即実行可能**: Safe削除のみ (約3,900行削減) diff --git a/docs/archive/cleanup/QUICK_REFERENCE.md b/docs/archive/cleanup/QUICK_REFERENCE.md index 7fa23216..6af5bcd7 100644 --- a/docs/archive/cleanup/QUICK_REFERENCE.md +++ b/docs/archive/cleanup/QUICK_REFERENCE.md @@ -1,3 +1,5 @@ +Status: Historical + # 重複コード削減 - クイックリファレンスカード ## 📊 一目で分かる状況 diff --git a/docs/archive/cleanup/README.md b/docs/archive/cleanup/README.md index 48c22830..15bdfecc 100644 --- a/docs/archive/cleanup/README.md +++ b/docs/archive/cleanup/README.md @@ -1,3 +1,5 @@ +Status: Historical + # レガシーコード削除プロジェクト **調査日**: 2025-11-06 diff --git a/docs/archive/cleanup/legacy-byname-removal.md b/docs/archive/cleanup/legacy-byname-removal.md index 44a0fe9e..5089b386 100644 --- a/docs/archive/cleanup/legacy-byname-removal.md +++ b/docs/archive/cleanup/legacy-byname-removal.md @@ -1,3 +1,5 @@ +Status: Historical + Legacy By‑Name Call Removal Plan Context diff --git a/docs/archive/cleanup/mir-legacy-cleanup-phase1-2.md b/docs/archive/cleanup/mir-legacy-cleanup-phase1-2.md index cce8c983..effb71f1 100644 --- a/docs/archive/cleanup/mir-legacy-cleanup-phase1-2.md +++ b/docs/archive/cleanup/mir-legacy-cleanup-phase1-2.md @@ -1,3 +1,5 @@ +Status: Historical + # MIR レガシーコード削除 Phase 1-2 完了報告 ## 📅 実施日 @@ -52,4 +54,4 @@ Phase 15.5 JSON centralization作業前のコードベースクリーンアッ ## 📌 備考 Task先生の分析に基づく段階的削除戦略により、リスクゼロで実施完了。 -JSON作業前のクリーンな環境整備に成功。 \ No newline at end of file +JSON作業前のクリーンな環境整備に成功。 diff --git a/docs/development/current/main/phase242-ex-legacy-lowerer-analysis.md b/docs/development/current/main/phase242-ex-legacy-lowerer-analysis.md new file mode 100644 index 00000000..54bfba03 --- /dev/null +++ b/docs/development/current/main/phase242-ex-legacy-lowerer-analysis.md @@ -0,0 +1,357 @@ +# Phase 242-EX: Pattern3 Legacy Lowerer Analysis + +## Summary +**Recommendation: Option A - Delete Legacy Lowerer** + +The Pattern3 legacy lowerer (`loop_with_if_phi_minimal.rs`) contains hardcoded carrier names ("sum", "count") that violate the by-name hardcoding prohibition. However, investigation reveals that **the entire lowerer is a Phase 188 PoC** hardcoded for one specific test case, making partial fixes insufficient. + +## Problem Analysis + +### Hardcoded Carrier Names +**File**: `src/mir/join_ir/lowering/loop_with_if_phi_minimal.rs:423-424` + +```rust +// Phase 213: Build ExitMeta for dynamic exit binding generation +let mut exit_values = vec![]; +exit_values.push(("sum".to_string(), sum_final)); // ← hardcoded "sum" +exit_values.push(("count".to_string(), count_final)); // ← hardcoded "count" +``` + +### Full Scope of Hardcoding + +The legacy lowerer is **completely hardcoded** for `loop_if_phi.hako`: + +1. **Loop condition**: `i <= 5` (lines 218-243) +2. **If condition**: `i % 2 == 1` (lines 256-289) +3. **If branches**: `sum + i` vs `sum + 0` (lines 291-335) +4. **Counter updates**: `count + 1` vs `count + 0` (lines 301-345) +5. **Carrier names**: "sum", "count" (lines 423-424) +6. **Carrier count**: Exactly 2 carriers (hardcoded allocation) + +### Architecture Issue + +**AST-based Path (Pattern 3):** +- Uses `LoopScopeShapeBuilder::empty_body_locals()` → `carriers: BTreeSet::new()` +- `CarrierInfo` is populated from `variable_map` +- But `LoopScopeShape.carriers` remains **empty** + +**LoopForm-based Path:** +- Uses `LoopScopeShapeBuilder::from_loopform_intake()` → populates carriers from LoopFormIntake +- This path is for structured loop lowering, not applicable to AST-based MIR building + +**Result**: `scope.carriers` is empty → Option B (extract from LoopScopeShape) fails. + +## Option Evaluation + +### Option A: Delete Legacy Lowerer ✅ **RECOMMENDED** +**Status**: Feasible if if-sum mode can be enhanced. + +#### Current if-sum Mode Rejection Logic +File: `src/mir/builder/control_flow/joinir/patterns/pattern_pipeline.rs:194-210` + +```rust +pub fn is_if_sum_pattern(&self) -> bool { + // (a) Pattern check: must be SimpleComparison + let pattern = analyze_condition_pattern(condition); + if pattern != ConditionPattern::SimpleComparison { + // Complex condition → legacy mode (PoC lowering) + return false; // ← Rejects i % 2 == 1 + } + // ... +} +``` + +**Reason for Rejection**: `i % 2 == 1` has `BinaryOp` in LHS → `ConditionPattern::Complex` + +File: `src/mir/join_ir/lowering/condition_pattern.rs:79-125` + +```rust +pub fn analyze_condition_pattern(cond: &ASTNode) -> ConditionPattern { + match cond { + ASTNode::BinaryOp { operator, left, right, .. } => { + // Check LHS/RHS patterns + let left_is_var = matches!(left.as_ref(), ASTNode::Variable { .. }); + let right_is_literal = matches!(right.as_ref(), ASTNode::Literal { .. }); + + if left_is_var && right_is_literal { + return ConditionPattern::SimpleComparison; // e.g., i > 0 + } + + // Complex LHS/RHS (e.g., i % 2 == 1) → ConditionPattern::Complex + ConditionPattern::Complex // ← i % 2 is BinaryOp, not Variable + } + _ => ConditionPattern::Complex, + } +} +``` + +#### Enhancement Strategy + +**Approach**: Extend if-sum mode to lower complex if conditions dynamically. + +1. **Accept complex conditions**: Remove `SimpleComparison` check +2. **Lower condition AST**: Use existing AST-based lowering infrastructure +3. **Generate Select instruction**: Use condition result to select between branches + +**Implementation Changes**: +- `condition_pattern.rs`: Add `ConditionPattern::ComplexComparison` (still a comparison, but with expressions) +- `loop_with_if_phi_if_sum.rs`: Lower complex condition expressions using `lower_expression()` +- `pattern_pipeline.rs`: Accept `ComplexComparison` in `is_if_sum_pattern()` + +**Benefits**: +- Removes 438-line PoC file +- Unified lowering path (no legacy/if-sum split) +- No by-name hardcoding +- Supports arbitrary if conditions (not just `i % 2 == 1`) + +**Risks**: +- Need to handle arbitrary expressions in if condition +- May require additional ValueId allocation in condition lowering + +### Option B: Extract from LoopScopeShape ❌ **NOT VIABLE** +**Status**: Not feasible due to empty carriers. + +**Problem**: `LoopScopeShape.carriers` is empty in AST-based path. + +**Why it fails**: +```rust +// pattern_pipeline.rs:283-291 +let loop_scope = match variant { + PatternVariant::Pattern1 | PatternVariant::Pattern3 => { + LoopScopeShapeBuilder::empty_body_locals( + BasicBlockId(0), BasicBlockId(0), BasicBlockId(0), BasicBlockId(0), + BTreeSet::new(), // ← Empty pinned set + ) // ← Returns LoopScopeShape { carriers: BTreeSet::new(), .. } + } + // ... +} +``` + +**Alternative**: Extract from `ctx.carrier_info.carriers` instead. + +**Implementation**: +```rust +// pattern3_with_if_phi.rs:262 (in lower_pattern3_legacy) +let carrier_names: Vec = ctx.carrier_info.carriers + .iter() + .map(|c| c.name.clone()) + .collect(); + +// Pass to legacy lowerer +let (join_module, fragment_meta) = lower_loop_with_if_phi_pattern( + carrier_names, // New parameter + &mut join_value_space +)?; + +// loop_with_if_phi_minimal.rs:421-424 +let mut exit_values = vec![]; +for (i, carrier_name) in carrier_names.iter().enumerate() { + let value_id = match i { + 0 => sum_final, + 1 => count_final, + _ => return Err(format!("Legacy lowerer only supports 2 carriers")), + }; + exit_values.push((carrier_name.clone(), value_id)); +} +``` + +**Why still not recommended**: +- Only fixes carrier names, not loop/if conditions +- Still a PoC implementation (hardcoded logic remains) +- Better to invest in Option A (proper generalization) + +### Option C: Pass CarrierInfo ❌ **NOT RECOMMENDED** +**Status**: Same issues as Option B. + +**Implementation**: Change legacy lowerer signature to accept `CarrierInfo`. + +**Why not recommended**: +- Same fundamental issue: only fixes names, not logic +- Requires signature changes across multiple files +- Still doesn't generalize the PoC lowering + +## Test Impact Analysis + +### Tests Using Legacy Lowerer +**Only 1 test**: `apps/tests/loop_if_phi.hako` + +```nyash +static box Main { + main(args) { + local console = new ConsoleBox() + local i = 1 + local sum = 0 + loop(i <= 5) { + if (i % 2 == 1) { sum = sum + i } else { sum = sum + 0 } + i = i + 1 + } + console.println("sum=" + sum) + return 0 + } +} +``` + +**Why it uses legacy mode**: `i % 2 == 1` is a complex condition (BinaryOp in LHS). + +**Other Pattern 3 tests**: None found using `rg --type rust 'loop_if_phi\.hako'`. + +### Tests Using If-Sum Mode +**Tests using Pattern 3 if-sum mode**: Minimal (mostly `phase212_if_sum_min.hako`). + +**Pattern 3 detection**: File `src/mir/loop_pattern_detection/loop_patterns.rs` + +```rust +pub fn detect_pattern3_if_phi(body: &[ASTNode]) -> bool { + // Has if-else with PHI, no break/continue + has_if_else_phi(body) && !has_break_continue(body) +} +``` + +## Implementation Plan + +### Recommended: Option A - Enhanced If-Sum Mode + +#### Phase 242-EX-A1: Extend Condition Pattern Analysis +**File**: `src/mir/join_ir/lowering/condition_pattern.rs` + +1. Add `ConditionPattern::ComplexComparison`: + ```rust + pub enum ConditionPattern { + SimpleComparison, // var CmpOp literal (e.g., i > 0) + ComplexComparison, // expr CmpOp expr (e.g., i % 2 == 1) + Complex, // Non-comparison (e.g., a && b) + } + ``` + +2. Update `analyze_condition_pattern()`: + ```rust + pub fn analyze_condition_pattern(cond: &ASTNode) -> ConditionPattern { + match cond { + ASTNode::BinaryOp { operator, .. } => { + let is_comparison = matches!(operator, /* ... */); + if !is_comparison { + return ConditionPattern::Complex; + } + // Complex comparison (expr CmpOp expr) + ConditionPattern::ComplexComparison + } + _ => ConditionPattern::Complex, + } + } + ``` + +#### Phase 242-EX-A2: Accept Complex Comparisons in If-Sum Mode +**File**: `src/mir/builder/control_flow/joinir/patterns/pattern_pipeline.rs` + +```rust +pub fn is_if_sum_pattern(&self) -> bool { + // ... + let pattern = analyze_condition_pattern(condition); + if pattern == ConditionPattern::Complex { + // Non-comparison (e.g., a && b) → legacy mode + return false; + } + // Accept both SimpleComparison and ComplexComparison + // ... +} +``` + +#### Phase 242-EX-A3: Lower Complex Conditions in If-Sum Lowerer +**File**: `src/mir/join_ir/lowering/loop_with_if_phi_if_sum.rs` + +**Current**: Only lowers simple comparisons via `ConditionEnv`. + +**Enhanced**: Lower arbitrary condition expressions: + +```rust +// For complex conditions (e.g., i % 2 == 1): +// 1. Lower LHS expression (i % 2) +let lhs_vid = lower_expression_to_joinir(if_condition.lhs, env, body)?; +// 2. Lower RHS expression (1) +let rhs_vid = lower_expression_to_joinir(if_condition.rhs, env, body)?; +// 3. Generate Compare instruction +let cond_vid = alloc_value(); +body.push(JoinInst::Compute(MirLikeInst::Compare { + dst: cond_vid, + op: map_compare_op(if_condition.operator), + lhs: lhs_vid, + rhs: rhs_vid, +})); +``` + +**Infrastructure needed**: +- `lower_expression_to_joinir()` helper (similar to `lower_comparison()`) +- Handle `BinaryOp`, `Variable`, `Literal` recursively +- Allocate temporary ValueIds for intermediate results + +#### Phase 242-EX-A4: Delete Legacy Lowerer +**Files to delete**: +- `src/mir/join_ir/lowering/loop_with_if_phi_minimal.rs` (entire file, 438 lines) + +**Files to modify**: +- `src/mir/builder/control_flow/joinir/patterns/pattern3_with_if_phi.rs`: + - Remove `lower_pattern3_legacy()` function + - Remove dual-mode dispatch in `cf_loop_pattern3_with_if_phi()` + - Always use if-sum mode +- `src/mir/builder/control_flow/joinir/patterns/mod.rs`: + - Remove `pub mod loop_with_if_phi_minimal;` + +#### Phase 242-EX-A5: Verify Tests +```bash +# Build +cargo build --release + +# Test original case +./target/release/hakorune apps/tests/loop_if_phi.hako +# Expected output: sum=9 + +# Run full test suite +cargo test --release +# Expected: 909 tests pass +``` + +## Success Criteria + +1. ✅ `cargo build --release` success +2. ✅ All 909 tests pass +3. ✅ `loop_if_phi.hako` runs correctly (output: `sum=9`) +4. ✅ No hardcoded "sum"/"count" references +5. ✅ Legacy lowerer deleted (438 lines removed) +6. ✅ If-sum mode handles complex conditions + +## Timeline Estimate + +- **Phase 242-EX-A1**: 30 minutes (condition pattern analysis) +- **Phase 242-EX-A2**: 15 minutes (pattern acceptance) +- **Phase 242-EX-A3**: 2-3 hours (complex condition lowering) +- **Phase 242-EX-A4**: 15 minutes (deletion) +- **Phase 242-EX-A5**: 30 minutes (testing) + +**Total**: 3.5-4.5 hours + +## References + +- **Phase 188**: Original legacy lowerer implementation +- **Phase 195**: Multi-carrier support (hardcoded "sum", "count") +- **Phase 213**: ExitMeta-based exit binding generation +- **Phase 219**: AST-based if-sum mode introduction +- **Phase 222**: Condition normalization +- **Phase 241**: Removed hardcoded 'sum' check from loop body analyzer + +## Related Files + +### Core Legacy Lowerer +- `src/mir/join_ir/lowering/loop_with_if_phi_minimal.rs` (438 lines) + +### Callers +- `src/mir/builder/control_flow/joinir/patterns/pattern3_with_if_phi.rs:248-358` (legacy path) + +### Condition Analysis +- `src/mir/join_ir/lowering/condition_pattern.rs` (pattern detection) +- `src/mir/builder/control_flow/joinir/patterns/pattern_pipeline.rs:194-229` (is_if_sum_pattern) + +### If-Sum Mode +- `src/mir/join_ir/lowering/loop_with_if_phi_if_sum.rs` (AST-based lowerer) + +### Tests +- `apps/tests/loop_if_phi.hako` (only test using legacy mode) 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 7d3086f0..97296ff2 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 @@ -79,15 +79,16 @@ impl MirBuilder { PatternVariant::Pattern3, )?; - // Phase 213: Dual-mode dispatch based on if-sum pattern detection - if ctx.is_if_sum_pattern() { - trace::trace().debug("pattern3", "Detected if-sum pattern, using AST-based lowerer"); - return self.lower_pattern3_if_sum(&ctx, condition, body, debug); + // Phase 213: AST-based if-sum pattern detection + // Phase 242-EX-A: Legacy mode removed - all if-sum patterns now handled dynamically + if !ctx.is_if_sum_pattern() { + // Not an if-sum pattern → let router try other patterns or fall back + trace::trace().debug("pattern3", "Not an if-sum pattern, returning None to try other patterns"); + return Ok(None); } - // Legacy mode: Use hardcoded PoC lowering (e.g., loop_if_phi.hako) - trace::trace().debug("pattern3", "Using legacy PoC lowerer (hardcoded conditions)"); - self.lower_pattern3_legacy(&ctx, debug) + trace::trace().debug("pattern3", "Detected if-sum pattern, using AST-based lowerer"); + self.lower_pattern3_if_sum(&ctx, condition, body, debug) } /// Phase 213: AST-based if-sum lowerer @@ -242,118 +243,5 @@ impl MirBuilder { } } - /// Phase 188-195: Legacy PoC lowerer (hardcoded conditions) - /// - /// Kept for backward compatibility with existing tests like `loop_if_phi.hako`. - fn lower_pattern3_legacy( - &mut self, - ctx: &super::pattern_pipeline::PatternPipelineContext, - debug: bool, - ) -> Result, String> { - use crate::mir::join_ir::lowering::loop_with_if_phi_minimal::lower_loop_with_if_phi_pattern; - - // Phase 195: Use unified trace - trace::trace().varmap("pattern3_start", &self.variable_map); - - // Phase 202-B: Create JoinValueSpace for unified ValueId allocation - use crate::mir::join_ir::lowering::join_value_space::JoinValueSpace; - let mut join_value_space = JoinValueSpace::new(); - - // Call Pattern 3 lowerer with preprocessed scope - let (join_module, fragment_meta) = match lower_loop_with_if_phi_pattern(ctx.loop_scope.clone(), &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; - - // 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. - // Phase 228-8: Pass None for carrier_info (legacy path doesn't have ConditionOnly carriers) - let exit_bindings = ExitMetaCollector::collect( - self, - exit_meta, - None, // Phase 228-8: No carrier_info in legacy path - debug, - ); - - // Phase 214: Dynamically generate join_inputs based on exit_bindings - // Count: 1 loop_var + exit_bindings.len() = total inputs - // NOTE: exit_bindings already filtered out non-existent carriers - let total_inputs = 1 + exit_bindings.len(); - - // Allocate join_inputs dynamically from JoinValueSpace - let join_inputs: Vec = (0..total_inputs) - .map(|i| ValueId(i as u32)) - .collect(); - - // Build host_inputs: loop_var + exit_bindings (in order) - let mut host_inputs = vec![ctx.loop_var_id]; - for binding in &exit_bindings { - host_inputs.push(binding.host_slot); - } - - // Phase 214: Verify length consistency (fail-fast assertion) - debug_assert_eq!( - join_inputs.len(), - host_inputs.len(), - "[pattern3/legacy] join_inputs.len({}) != host_inputs.len({})", - join_inputs.len(), - host_inputs.len() - ); - - trace::trace().debug( - "pattern3/legacy", - &format!( - "Boundary inputs: {} total (loop_var + {} exit bindings)", - total_inputs, exit_bindings.len() - ) - ); - - let boundary = JoinInlineBoundaryBuilder::new() - .with_inputs(join_inputs, host_inputs) - .with_exit_bindings(exit_bindings) - .with_loop_var_name(Some(ctx.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; - let _ = JoinIRConversionPipeline::execute( - self, - join_module, - Some(&boundary), - "pattern3", - debug, - )?; - self.trace_varmap("pattern3_after_merge"); - - // Phase 179-B: Return Void (loop doesn't produce values) - let void_val = crate::mir::builder::emission::constant::emit_void(self); - - // Phase 195: Use unified trace - trace::trace().debug("pattern3", &format!("Loop complete, returning Void {:?}", void_val)); - - Ok(Some(void_val)) - } + // Phase 242-EX-A: lower_pattern3_legacy removed - all patterns now use AST-based lowering } diff --git a/src/mir/join_ir/lowering/condition_pattern.rs b/src/mir/join_ir/lowering/condition_pattern.rs index da78d923..3303340a 100644 --- a/src/mir/join_ir/lowering/condition_pattern.rs +++ b/src/mir/join_ir/lowering/condition_pattern.rs @@ -67,14 +67,14 @@ pub enum ConditionPattern { /// }; /// assert_eq!(analyze_condition_pattern(&simple), ConditionPattern::SimpleComparison); /// -/// // Complex: i % 2 == 1 +/// // Phase 242-EX-A: Now Simple: i % 2 == 1 (BinaryOp in LHS) /// let complex = ASTNode::BinaryOp { /// operator: BinaryOperator::Equal, /// left: Box::new(ASTNode::BinaryOp { ... }), // BinaryOp in LHS /// right: Box::new(ASTNode::Literal { value: LiteralValue::Integer(1), span: Span::unknown() }), /// span: Span::unknown(), /// }; -/// assert_eq!(analyze_condition_pattern(&complex), ConditionPattern::Complex); +/// assert_eq!(analyze_condition_pattern(&complex), ConditionPattern::SimpleComparison); /// ``` pub fn analyze_condition_pattern(cond: &ASTNode) -> ConditionPattern { match cond { @@ -96,11 +96,16 @@ pub fn analyze_condition_pattern(cond: &ASTNode) -> ConditionPattern { return ConditionPattern::Complex; } + // Phase 242-EX-A: Accept any expr CmpOp expr pattern + // The lowerer (loop_with_if_phi_if_sum.rs) will handle BinaryOp via lower_value_expression + // Check LHS/RHS patterns let left_is_var = matches!(left.as_ref(), ASTNode::Variable { .. }); let left_is_literal = matches!(left.as_ref(), ASTNode::Literal { .. }); + let left_is_binop = matches!(left.as_ref(), ASTNode::BinaryOp { .. }); let right_is_var = matches!(right.as_ref(), ASTNode::Variable { .. }); let right_is_literal = matches!(right.as_ref(), ASTNode::Literal { .. }); + let right_is_binop = matches!(right.as_ref(), ASTNode::BinaryOp { .. }); // Phase 219: var CmpOp literal (e.g., i > 0) if left_is_var && right_is_literal { @@ -117,7 +122,32 @@ pub fn analyze_condition_pattern(cond: &ASTNode) -> ConditionPattern { return ConditionPattern::SimpleComparison; } - // Complex LHS/RHS (e.g., i % 2 == 1, method_call() > 0) + // Phase 242-EX-A: BinaryOp CmpOp literal (e.g., i % 2 == 1) + if left_is_binop && right_is_literal { + return ConditionPattern::SimpleComparison; + } + + // Phase 242-EX-A: BinaryOp CmpOp var (e.g., i + j > k) + if left_is_binop && right_is_var { + return ConditionPattern::SimpleComparison; + } + + // Phase 242-EX-A: var CmpOp BinaryOp (e.g., i > j + 1) + if left_is_var && right_is_binop { + return ConditionPattern::SimpleComparison; + } + + // Phase 242-EX-A: literal CmpOp BinaryOp (e.g., 0 < i + 1) + if left_is_literal && right_is_binop { + return ConditionPattern::SimpleComparison; + } + + // Phase 242-EX-A: BinaryOp CmpOp BinaryOp (e.g., a + b > c + d) + if left_is_binop && right_is_binop { + return ConditionPattern::SimpleComparison; + } + + // Complex LHS/RHS (e.g., method_call() > 0) - MethodCall not yet supported ConditionPattern::Complex } // Any other node type → Complex @@ -350,21 +380,21 @@ mod tests { } #[test] - fn test_complex_binop_in_lhs() { - // i % 2 == 1 (BinaryOp in LHS) + fn test_simple_binop_in_lhs() { + // Phase 242-EX-A: i % 2 == 1 (BinaryOp in LHS) is now SimpleComparison let lhs = binop(BinaryOperator::Modulo, var("i"), int_lit(2)); let cond = binop(BinaryOperator::Equal, lhs, int_lit(1)); - assert_eq!(analyze_condition_pattern(&cond), ConditionPattern::Complex); - assert!(!is_simple_comparison(&cond)); + assert_eq!(analyze_condition_pattern(&cond), ConditionPattern::SimpleComparison); + assert!(is_simple_comparison(&cond)); } #[test] - fn test_complex_binop_in_rhs() { - // i == a + b (BinaryOp in RHS) + fn test_simple_binop_in_rhs() { + // Phase 242-EX-A: i == a + b (BinaryOp in RHS) is now SimpleComparison let rhs = binop(BinaryOperator::Add, var("a"), var("b")); let cond = binop(BinaryOperator::Equal, var("i"), rhs); - assert_eq!(analyze_condition_pattern(&cond), ConditionPattern::Complex); - assert!(!is_simple_comparison(&cond)); + assert_eq!(analyze_condition_pattern(&cond), ConditionPattern::SimpleComparison); + assert!(is_simple_comparison(&cond)); } #[test] @@ -470,8 +500,10 @@ mod tests { } #[test] - fn test_normalize_fails_on_complex() { - // i % 2 == 1 → 正規化失敗(BinaryOp in LHS) + fn test_normalize_fails_on_binop() { + // Phase 242-EX-A: i % 2 == 1 → 正規化失敗(BinaryOp in LHS) + // Note: This is OK - normalization is only for simple cases. + // The if-sum lowerer will use lower_value_expression() instead. let lhs = binop(BinaryOperator::Modulo, var("i"), int_lit(2)); let cond = binop(BinaryOperator::Equal, lhs, int_lit(1)); assert_eq!(normalize_comparison(&cond), None); 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 aa09e799..7dff1c17 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 @@ -83,46 +83,9 @@ pub fn lower_loop_with_conditional_phi_to_joinir( _loop_form: &LoopForm, _lowerer: &mut LoopToJoinLowerer, ) -> Option { - // Phase 188-Impl-3: Delegate to minimal lowerer - // TODO: Extract LoopScopeShape from loop_form for generic implementation - 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::lowering::loop_with_if_phi_minimal::lower_loop_with_if_phi_pattern; - use crate::mir::BasicBlockId; - use std::collections::{BTreeMap, BTreeSet}; - - // For now, use a placeholder LoopScopeShape - // TODO: Build actual LoopScopeShape from loop_form - let placeholder_scope = LoopScopeShape { - header: BasicBlockId(0), - body: BasicBlockId(0), - latch: BasicBlockId(0), - exit: BasicBlockId(0), - pinned: BTreeSet::new(), - carriers: BTreeSet::new(), - body_locals: BTreeSet::new(), - exit_live: BTreeSet::new(), - progress_carrier: None, - variable_definitions: BTreeMap::new(), - }; - - // Phase 202-B: Create JoinValueSpace for unified ValueId allocation - let mut join_value_space = JoinValueSpace::new(); - - // Generate JoinIR module - // 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 - // - // TODO: Either: - // 1. Remove this function and rely only on router integration, OR - // 2. Implement JoinModule → JoinInst conversion here (future phase) - - eprintln!("[loop_patterns] Pattern 3: Lowering delegated to loop_with_if_phi_minimal"); - - // Temporary: Return None to trigger fallback - // Pattern 3 currently works via router which calls minimal lowerer directly + // Phase 242-EX-A: Legacy stub removed + // Pattern 3 is now fully handled via router → pattern3_with_if_phi.rs → loop_with_if_phi_if_sum.rs + // This stub function is unused and kept only for API compatibility + eprintln!("[loop_patterns] Pattern 3: Stub - routing via pattern3_with_if_phi.rs"); None } diff --git a/src/mir/join_ir/lowering/loop_with_if_phi_if_sum.rs b/src/mir/join_ir/lowering/loop_with_if_phi_if_sum.rs index 72e0af41..c16fc643 100644 --- a/src/mir/join_ir/lowering/loop_with_if_phi_if_sum.rs +++ b/src/mir/join_ir/lowering/loop_with_if_phi_if_sum.rs @@ -67,14 +67,16 @@ pub fn lower_if_sum_pattern( // Step 1: Extract loop condition info (e.g., i < len → var="i", op=Lt, limit=ValueId) // Phase 220-D: Now returns ValueId and instructions for limit + // Phase 242-EX-A: Now supports complex LHS (e.g., `i % 2 == 1`) // Uses cond_env for variable resolution (e.g., `len` in `i < len`) - let (loop_var, loop_op, loop_limit_val, loop_limit_insts) = + let (loop_var, loop_op, loop_lhs_val, loop_limit_val, loop_limit_insts) = extract_loop_condition(loop_condition, &mut alloc_value, cond_env)?; eprintln!("[joinir/pattern3/if-sum] Loop condition: {} {:?} ValueId({})", loop_var, loop_op, loop_limit_val.0); // Step 2: Extract if condition info (e.g., i > 0 → var="i", op=Gt, value=ValueId) // Phase 220-D: Now returns ValueId and instructions - let (if_var, if_op, if_value_val, if_value_insts) = + // Phase 242-EX-A: Now supports complex LHS + let (if_var, if_op, if_lhs_val, if_value_val, if_value_insts) = extract_if_condition(if_stmt, &mut alloc_value, cond_env)?; eprintln!("[joinir/pattern3/if-sum] If condition: {} {:?} ValueId({})", if_var, if_op, if_value_val.0); @@ -172,16 +174,19 @@ pub fn lower_if_sum_pattern( // --- Exit Condition Check --- // Phase 220-D: Prepend loop limit instructions (generated from AST) + // Phase 242-EX-A: Now handles complex LHS expressions // This handles both literals (Const) and variables (from ConditionEnv) for inst in loop_limit_insts { loop_step_func.body.push(inst); } // Compare: i < limit (or other op from AST) + // Phase 242-EX-A: Use computed LHS if available, otherwise use loop parameter + let loop_lhs = loop_lhs_val.unwrap_or(i_param); loop_step_func.body.push(JoinInst::Compute(MirLikeInst::Compare { dst: cmp_loop, op: loop_op, - lhs: i_param, + lhs: loop_lhs, rhs: loop_limit_val, })); @@ -201,15 +206,18 @@ pub fn lower_if_sum_pattern( // --- If Condition (AST-based) --- // Phase 220-D: Prepend if value instructions (generated from AST) + // Phase 242-EX-A: Now handles complex LHS expressions for inst in if_value_insts { loop_step_func.body.push(inst); } // Compare: if_var if_value + // Phase 242-EX-A: Use computed LHS if available, otherwise use loop parameter + let if_lhs = if_lhs_val.unwrap_or(i_param); loop_step_func.body.push(JoinInst::Compute(MirLikeInst::Compare { dst: if_cmp, op: if_op, - lhs: i_param, // Assuming if_var == loop_var (common case) + lhs: if_lhs, rhs: if_value_val, })); @@ -333,78 +341,121 @@ pub fn lower_if_sum_pattern( /// Extract loop condition: variable, operator, and limit /// /// Phase 220-D: Now supports both literals and variables via ConditionEnv. +/// Phase 242-EX-A: Now supports complex LHS expressions (e.g., `i % 2 == 1`). /// /// Supports: `var < lit`, `var <= lit`, `var > lit`, `var >= lit` /// Supports: `var < var2`, `var <= var2`, etc. +/// Supports: `expr CmpOp lit` (e.g., `i % 2 == 1`) /// /// # Returns /// -/// * `Ok((var_name, op, limit_value, limit_instructions))` where: -/// - `var_name`: Loop variable name (e.g., "i") +/// * `Ok((var_name, op, lhs_value, rhs_value, instructions))` where: +/// - `var_name`: Loop variable name (e.g., "i"), empty string for complex LHS /// - `op`: Comparison operator -/// - `limit_value`: ValueId for the limit (either from literal or variable lookup) -/// - `limit_instructions`: JoinIR instructions to generate limit_value +/// - `lhs_value`: Optional LHS ValueId (None for simple variable, Some for complex expr) +/// - `rhs_value`: ValueId for the RHS operand +/// - `instructions`: JoinIR instructions to generate both LHS and RHS values fn extract_loop_condition( cond: &ASTNode, alloc_value: &mut F, cond_env: &ConditionEnv, -) -> Result<(String, CompareOp, ValueId, Vec), String> +) -> Result<(String, CompareOp, Option, ValueId, Vec), String> where F: FnMut() -> ValueId, { - // Phase 222: Normalize condition to canonical form (var on left) use crate::mir::join_ir::lowering::condition_pattern::{normalize_comparison, ConditionValue}; - let norm = normalize_comparison(cond) - .ok_or_else(|| "[if-sum] Condition normalization failed".to_string())?; + // Phase 242-EX-A: Try normalization first for simple cases (fast path) + if let Some(norm) = normalize_comparison(cond) { + // Extract normalized variable name and operator + let var_name = norm.left_var; - // Extract normalized variable name and operator - let var_name = norm.left_var; + // Convert mir::CompareOp to join_ir::CompareOp + let op = match norm.op { + crate::mir::CompareOp::Lt => CompareOp::Lt, + crate::mir::CompareOp::Gt => CompareOp::Gt, + crate::mir::CompareOp::Le => CompareOp::Le, + crate::mir::CompareOp::Ge => CompareOp::Ge, + crate::mir::CompareOp::Eq => CompareOp::Eq, + crate::mir::CompareOp::Ne => CompareOp::Ne, + }; - // Convert mir::CompareOp to join_ir::CompareOp - let op = match norm.op { - crate::mir::CompareOp::Lt => CompareOp::Lt, - crate::mir::CompareOp::Gt => CompareOp::Gt, - crate::mir::CompareOp::Le => CompareOp::Le, - crate::mir::CompareOp::Ge => CompareOp::Ge, - crate::mir::CompareOp::Eq => CompareOp::Eq, - crate::mir::CompareOp::Ne => CompareOp::Ne, - }; + // Lower the right-hand side using condition_lowerer + let mut limit_instructions = Vec::new(); + let limit_value = match norm.right { + ConditionValue::Literal(lit) => { + let val_id = alloc_value(); + limit_instructions.push(JoinInst::Compute(MirLikeInst::Const { + dst: val_id, + value: ConstValue::Integer(lit), + })); + val_id + } + ConditionValue::Variable(var_name) => { + let var_node = ASTNode::Variable { + name: var_name, + span: crate::ast::Span { start: 0, end: 0, line: 1, column: 1 }, + }; + lower_value_expression(&var_node, alloc_value, cond_env, &mut limit_instructions)? + } + }; - // Lower the right-hand side using condition_lowerer - // This handles both literals and variables via ConditionEnv - let mut limit_instructions = Vec::new(); - let limit_value = match norm.right { - ConditionValue::Literal(lit) => { - // Create Const instruction for literal - let val_id = alloc_value(); - limit_instructions.push(JoinInst::Compute(MirLikeInst::Const { - dst: val_id, - value: ConstValue::Integer(lit), - })); - val_id - } - ConditionValue::Variable(var_name) => { - // Create Variable node and lower it via condition_lowerer - let var_node = ASTNode::Variable { - name: var_name, - span: crate::ast::Span { start: 0, end: 0, line: 1, column: 1 }, + return Ok((var_name, op, None, limit_value, limit_instructions)); + } + + // Phase 242-EX-A: Normalization failed → handle complex conditions dynamically + // Support: `expr CmpOp expr` (e.g., `i % 2 == 1`, `a + b > c`) + match cond { + ASTNode::BinaryOp { operator, left, right, .. } => { + use crate::ast::BinaryOperator; + + // Convert operator to CompareOp + let op = match operator { + BinaryOperator::Less => CompareOp::Lt, + BinaryOperator::Greater => CompareOp::Gt, + BinaryOperator::LessEqual => CompareOp::Le, + BinaryOperator::GreaterEqual => CompareOp::Ge, + BinaryOperator::Equal => CompareOp::Eq, + BinaryOperator::NotEqual => CompareOp::Ne, + _ => return Err(format!("[if-sum] Unsupported operator in condition: {:?}", operator)), }; - lower_value_expression(&var_node, alloc_value, cond_env, &mut limit_instructions)? - } - }; - Ok((var_name, op, limit_value, limit_instructions)) + // Lower left-hand side (complex expression) + let mut instructions = Vec::new(); + let lhs_val = lower_value_expression(left, alloc_value, cond_env, &mut instructions)?; + + // Lower right-hand side + let rhs_val = lower_value_expression(right, alloc_value, cond_env, &mut instructions)?; + + // Extract base variable name from LHS if possible + let var_name = extract_base_variable(left); + + Ok((var_name, op, Some(lhs_val), rhs_val, instructions)) + } + _ => Err("[if-sum] Expected comparison in condition".to_string()), + } +} + +/// Extract base variable name from an expression +/// +/// For `i % 2`, returns "i". For `a + b`, returns "a". For literals, returns empty string. +fn extract_base_variable(expr: &ASTNode) -> String { + match expr { + ASTNode::Variable { name, .. } => name.clone(), + ASTNode::BinaryOp { left, .. } => extract_base_variable(left), + _ => String::new(), + } } /// Extract if condition: variable, operator, and value /// /// Phase 220-D: Now supports variables via ConditionEnv +/// Phase 242-EX-A: Now supports complex LHS via extract_loop_condition fn extract_if_condition( if_stmt: &ASTNode, alloc_value: &mut F, cond_env: &ConditionEnv, -) -> Result<(String, CompareOp, ValueId, Vec), String> +) -> Result<(String, CompareOp, Option, ValueId, Vec), String> where F: FnMut() -> ValueId, { 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 deleted file mode 100644 index cacb7e66..00000000 --- a/src/mir/join_ir/lowering/loop_with_if_phi_minimal.rs +++ /dev/null @@ -1,437 +0,0 @@ -//! Phase 188-Impl-3: Pattern 3 (Loop with If-Else PHI) Minimal Lowerer -//! -//! Target: apps/tests/loop_if_phi.hako -//! -//! Code: -//! ```nyash -//! static box Main { -//! main(args) { -//! local console = new ConsoleBox() -//! local i = 1 -//! local sum = 0 -//! loop(i <= 5) { -//! if (i % 2 == 1) { sum = sum + i } else { sum = sum + 0 } -//! i = i + 1 -//! } -//! console.println("sum=" + sum) -//! return 0 -//! } -//! } -//! ``` -//! -//! Expected JoinIR: -//! ```text -//! fn main(): -//! i_init = 1 -//! sum_init = 0 -//! result = loop_step(i_init, sum_init) -//! return result -//! -//! fn loop_step(i, sum): -//! // Exit condition check -//! const_5 = 5 -//! cmp_le = (i <= 5) -//! exit_cond = !cmp_le -//! Jump(k_exit, [sum], cond=exit_cond) // natural exit -//! -//! // If-Else PHI: if (i % 2 == 1) { sum + i } else { sum + 0 } -//! const_2 = 2 -//! mod_result = i % 2 -//! const_1_eq = 1 -//! if_cond = (mod_result == 1) -//! sum_then = sum + i -//! const_0 = 0 -//! sum_else = sum + 0 -//! sum_new = Select(if_cond, sum_then, sum_else) -//! -//! // Update counter -//! const_1_inc = 1 -//! i_next = i + 1 -//! -//! // Tail recursion -//! Call(loop_step, [i_next, sum_new]) // tail call -//! -//! fn k_exit(sum_final): -//! return sum_final -//! ``` -//! -//! ## Design Notes -//! -//! This is a MINIMAL implementation targeting loop_if_phi.hako specifically. -//! It establishes the infrastructure for Pattern 3 lowering, building on Patterns 1 and 2. -//! -//! Key differences from Patterns 1/2: -//! - **Multiple Carrier Variables**: Both i (counter) and sum (accumulator) -//! - **In-Loop If-Else**: PHI node in loop body using Select instruction -//! - **Both Carriers Updated**: Pass [i_next, sum_new] to next iteration -//! -//! 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::{ - BinOpKind, CompareOp, ConstValue, JoinFuncId, JoinFunction, JoinInst, JoinModule, - MirLikeInst, UnaryOp, -}; - -/// Lower Pattern 3 (Loop with If-Else PHI) to JoinIR -/// -/// # Phase 188-Impl-3: Pure JoinIR Fragment Generation -/// -/// This version generates JoinIR using **local ValueIds only** (0, 1, 2, ...). -/// It has NO knowledge of the host function's ValueId space. The boundary mapping -/// is handled separately via JoinInlineBoundary. -/// -/// ## Design Philosophy -/// -/// - **Box A**: JoinIR Frontend (doesn't know about host ValueIds) -/// - **Box B**: This function - converts to JoinIR with local IDs -/// - **Box C**: JoinInlineBoundary - stores boundary info -/// - **Box D**: merge_joinir_mir_blocks - injects Copy instructions -/// -/// This clean separation ensures JoinIR lowerers are: -/// - Pure transformers (no side effects) -/// - Reusable (same lowerer works in any context) -/// - Testable (can test JoinIR independently) -/// -/// # Arguments -/// -/// * `_scope` - LoopScopeShape (reserved for future generic implementation) -/// * `join_value_space` - Phase 202-B: Unified JoinIR ValueId allocator (Local region: 1000+) -/// -/// # Returns -/// -/// * `Ok((JoinModule, JoinFragmentMeta))` - Successfully lowered to JoinIR with exit metadata -/// * `Err(String)` - Pattern lowering failed with error message -/// -/// # Boundary Contract -/// -/// This function returns a JoinModule with: -/// - **Input slots**: ValueId(0), ValueId(1) in main function (i_init, sum_init) -/// - **Output slot**: k_exit returns the final sum value -/// - **Caller responsibility**: Create JoinInlineBoundary to map ValueIds -pub(crate) fn lower_loop_with_if_phi_pattern( - _scope: LoopScopeShape, - join_value_space: &mut JoinValueSpace, -) -> 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(); - - let mut join_module = JoinModule::new(); - - // ================================================================== - // Function IDs allocation - // ================================================================== - let main_id = JoinFuncId::new(0); - let loop_step_id = JoinFuncId::new(1); - let k_exit_id = JoinFuncId::new(2); - - // ================================================================== - // ValueId allocation (Phase 188-Impl-3: Sequential local IDs) - // ================================================================== - // main() locals - let i_init_val = alloc_value(); // ValueId(0) - i = 1 - let sum_init_val = alloc_value(); // ValueId(1) - sum = 0 - let count_init_val = alloc_value(); // ValueId(2) - count = 0 (Phase 195: multi-carrier) - let loop_result = alloc_value(); // ValueId(3) - result from loop_step - - // loop_step locals - let i_param = alloc_value(); // ValueId(4) - i parameter - let sum_param = alloc_value(); // ValueId(5) - sum parameter - let count_param = alloc_value(); // ValueId(6) - count parameter (Phase 195: multi-carrier) - let const_5 = alloc_value(); // ValueId(7) - exit limit (5) - let cmp_le = alloc_value(); // ValueId(8) - i <= 5 - let exit_cond = alloc_value(); // ValueId(9) - !(i <= 5) - let const_2 = alloc_value(); // ValueId(10) - modulo constant (2) - let mod_result = alloc_value(); // ValueId(11) - i % 2 - let const_1_eq = alloc_value(); // ValueId(12) - equality constant (1) - let if_cond = alloc_value(); // ValueId(13) - (i % 2) == 1 - let sum_then = alloc_value(); // ValueId(14) - sum + i (then branch) - let const_1_count = alloc_value(); // ValueId(15) - count increment constant (1) (Phase 195) - let count_then = alloc_value(); // ValueId(16) - count + 1 (then branch) (Phase 195) - let const_0 = alloc_value(); // ValueId(17) - else branch constant (0) - let sum_else = alloc_value(); // ValueId(18) - sum + 0 (else branch) - let count_else = alloc_value(); // ValueId(19) - count + 0 (else branch) (Phase 195) - let sum_new = alloc_value(); // ValueId(20) - Select result for sum - let count_new = alloc_value(); // ValueId(21) - Select result for count (Phase 195) - let const_1_inc = alloc_value(); // ValueId(22) - increment constant (1) - let i_next = alloc_value(); // ValueId(23) - i + 1 - - // k_exit locals - let sum_final = alloc_value(); // ValueId(24) - final sum parameter - let count_final = alloc_value(); // ValueId(25) - final count parameter (Phase 195: multi-carrier) - - // ================================================================== - // main() function - // ================================================================== - // Phase 195: main() initializes loop variables (i, sum, count) and calls loop_step - let mut main_func = JoinFunction::new(main_id, "main".to_string(), vec![]); - - // i_init = 1 - main_func.body.push(JoinInst::Compute(MirLikeInst::Const { - dst: i_init_val, - value: ConstValue::Integer(1), - })); - - // sum_init = 0 - main_func.body.push(JoinInst::Compute(MirLikeInst::Const { - dst: sum_init_val, - value: ConstValue::Integer(0), - })); - - // count_init = 0 (Phase 195: multi-carrier) - main_func.body.push(JoinInst::Compute(MirLikeInst::Const { - dst: count_init_val, - value: ConstValue::Integer(0), - })); - - // result = loop_step(i_init, sum_init, count_init) (Phase 195: 3 parameters) - main_func.body.push(JoinInst::Call { - func: loop_step_id, - args: vec![i_init_val, sum_init_val, count_init_val], - k_next: None, - dst: Some(loop_result), - }); - - // return result (Pattern 3 returns the final sum value) - main_func.body.push(JoinInst::Ret { - value: Some(loop_result), - }); - - join_module.add_function(main_func); - - // ================================================================== - // loop_step(i, sum, count) function - // ================================================================== - // Phase 195: Multi-carrier - now takes i, sum, count as parameters - let mut loop_step_func = JoinFunction::new( - loop_step_id, - "loop_step".to_string(), - vec![i_param, sum_param, count_param], // Phase 195: 3 carriers as parameters - ); - - // ------------------------------------------------------------------ - // Exit Condition Check: !(i <= 5) - // ------------------------------------------------------------------ - // Step 1: const 5 - loop_step_func - .body - .push(JoinInst::Compute(MirLikeInst::Const { - dst: const_5, - value: ConstValue::Integer(5), - })); - - // Step 2: cmp_le = (i <= 5) - loop_step_func - .body - .push(JoinInst::Compute(MirLikeInst::Compare { - dst: cmp_le, - op: CompareOp::Le, - lhs: i_param, - rhs: const_5, - })); - - // Step 3: exit_cond = !cmp_le - loop_step_func - .body - .push(JoinInst::Compute(MirLikeInst::UnaryOp { - dst: exit_cond, - op: UnaryOp::Not, - operand: cmp_le, - })); - - // Jump(k_exit, [sum, count], cond=exit_cond) // Phase 195: Natural exit path with multi-carrier - loop_step_func.body.push(JoinInst::Jump { - cont: k_exit_id.as_cont(), - args: vec![sum_param, count_param], // Phase 195: Pass current sum and count as exit values - cond: Some(exit_cond), - }); - - // ------------------------------------------------------------------ - // In-Loop If-Else: if (i % 2 == 1) { sum + i } else { sum + 0 } - // ------------------------------------------------------------------ - // Step 1: const 2 - loop_step_func - .body - .push(JoinInst::Compute(MirLikeInst::Const { - dst: const_2, - value: ConstValue::Integer(2), - })); - - // Step 2: mod_result = i % 2 - loop_step_func - .body - .push(JoinInst::Compute(MirLikeInst::BinOp { - dst: mod_result, - op: BinOpKind::Mod, - lhs: i_param, - rhs: const_2, - })); - - // Step 3: const 1 (for equality check) - loop_step_func - .body - .push(JoinInst::Compute(MirLikeInst::Const { - dst: const_1_eq, - value: ConstValue::Integer(1), - })); - - // Step 4: if_cond = (mod_result == 1) - loop_step_func - .body - .push(JoinInst::Compute(MirLikeInst::Compare { - dst: if_cond, - op: CompareOp::Eq, - lhs: mod_result, - rhs: const_1_eq, - })); - - // Step 5: sum_then = sum + i (then branch) - loop_step_func - .body - .push(JoinInst::Compute(MirLikeInst::BinOp { - dst: sum_then, - op: BinOpKind::Add, - lhs: sum_param, - rhs: i_param, - })); - - // Step 6: const 1 for count increment (Phase 195: multi-carrier) - loop_step_func - .body - .push(JoinInst::Compute(MirLikeInst::Const { - dst: const_1_count, - value: ConstValue::Integer(1), - })); - - // Step 7: count_then = count + 1 (then branch) (Phase 195: multi-carrier) - loop_step_func - .body - .push(JoinInst::Compute(MirLikeInst::BinOp { - dst: count_then, - op: BinOpKind::Add, - lhs: count_param, - rhs: const_1_count, - })); - - // Step 8: const 0 (for else branch) - loop_step_func - .body - .push(JoinInst::Compute(MirLikeInst::Const { - dst: const_0, - value: ConstValue::Integer(0), - })); - - // Step 9: sum_else = sum + 0 (else branch) - loop_step_func - .body - .push(JoinInst::Compute(MirLikeInst::BinOp { - dst: sum_else, - op: BinOpKind::Add, - lhs: sum_param, - rhs: const_0, - })); - - // Step 10: count_else = count + 0 (else branch) (Phase 195: multi-carrier) - loop_step_func - .body - .push(JoinInst::Compute(MirLikeInst::BinOp { - dst: count_else, - op: BinOpKind::Add, - lhs: count_param, - rhs: const_0, - })); - - // Step 11: sum_new = Select(if_cond, sum_then, sum_else) - loop_step_func - .body - .push(JoinInst::Compute(MirLikeInst::Select { - dst: sum_new, - cond: if_cond, - then_val: sum_then, - else_val: sum_else, - })); - - // Step 12: count_new = Select(if_cond, count_then, count_else) (Phase 195: multi-carrier) - loop_step_func - .body - .push(JoinInst::Compute(MirLikeInst::Select { - dst: count_new, - cond: if_cond, - then_val: count_then, - else_val: count_else, - })); - - // ------------------------------------------------------------------ - // Update Counter: i_next = i + 1 - // ------------------------------------------------------------------ - // Step 1: const 1 - loop_step_func - .body - .push(JoinInst::Compute(MirLikeInst::Const { - dst: const_1_inc, - value: ConstValue::Integer(1), - })); - - // Step 2: i_next = i + 1 - loop_step_func - .body - .push(JoinInst::Compute(MirLikeInst::BinOp { - dst: i_next, - op: BinOpKind::Add, - lhs: i_param, - rhs: const_1_inc, - })); - - // ------------------------------------------------------------------ - // Tail Recursion: Call(loop_step, [i_next, sum_new, count_new]) - // ------------------------------------------------------------------ - // Phase 195: Multi-carrier tail call with i, sum, count - loop_step_func.body.push(JoinInst::Call { - func: loop_step_id, - args: vec![i_next, sum_new, count_new], // Phase 195: ALL 3 updated carriers - k_next: None, // CRITICAL: None for tail call - dst: None, - }); - - join_module.add_function(loop_step_func); - - // ================================================================== - // k_exit(sum_final, count_final) function - Exit PHI - // ================================================================== - // Phase 195: Multi-carrier k_exit receives final sum and count values - let mut k_exit_func = JoinFunction::new( - k_exit_id, - "k_exit".to_string(), - vec![sum_final, count_final], // Phase 195: Exit PHI receives sum and count from exit path - ); - - // return sum_final (Pattern 3 convention: return first carrier value) - k_exit_func.body.push(JoinInst::Ret { - value: Some(sum_final), - }); - - join_module.add_function(k_exit_func); - - // 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]"); - eprintln!("[joinir/pattern3] If-Else PHI in loop body:"); - 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]"); - - Ok((join_module, fragment_meta)) -} diff --git a/src/mir/join_ir/lowering/mod.rs b/src/mir/join_ir/lowering/mod.rs index 36deab25..506a3fa5 100644 --- a/src/mir/join_ir/lowering/mod.rs +++ b/src/mir/join_ir/lowering/mod.rs @@ -62,8 +62,8 @@ pub mod loop_to_join; pub(crate) mod loop_view_builder; // Phase 33-23: Loop lowering dispatch pub mod loop_with_break_minimal; // Phase 188-Impl-2: Pattern 2 minimal lowerer pub mod loop_with_continue_minimal; // Phase 195: Pattern 4 minimal lowerer -pub mod loop_with_if_phi_minimal; // Phase 188-Impl-3: Pattern 3 minimal lowerer -pub mod loop_with_if_phi_if_sum; // Phase 213: Pattern 3 AST-based if-sum lowerer +// Phase 242-EX-A: loop_with_if_phi_minimal removed - replaced by loop_with_if_phi_if_sum +pub mod loop_with_if_phi_if_sum; // Phase 213: Pattern 3 AST-based if-sum lowerer (Phase 242-EX-A: supports complex conditions) pub mod simple_while_minimal; // Phase 188-Impl-1: Pattern 1 minimal lowerer pub mod min_loop; pub mod skip_ws;