refactor(joinir): Phase 242-EX-A - Delete legacy Pattern3 lowerer
Remove hardcoded Pattern3 PoC implementation (loop_with_if_phi_minimal.rs) and enhance if-sum mode to handle complex conditions like `i % 2 == 1`. Key changes: - condition_pattern.rs: Accept BinaryOp in comparison operands (+58 lines) - loop_with_if_phi_if_sum.rs: Dynamic complex condition lowering (+147 lines) - pattern3_with_if_phi.rs: Remove lower_pattern3_legacy() (-130 lines) - loop_with_if_phi_minimal.rs: Delete entire file (-437 lines) - loop_patterns/with_if_phi.rs: Update stub (-45 lines) - mod.rs: Remove module reference (-4 lines) Net reduction: -664 lines of hardcoded PoC code Test results: 909/909 PASS (legacy mode completely removed) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@ -1,3 +1,5 @@
|
||||
Status: Historical
|
||||
|
||||
# Hakorune コードベース重複・共通化調査 - 実行サマリー
|
||||
|
||||
**調査日**: 2025-11-06
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
Status: Historical
|
||||
|
||||
# Cleanup Sweep (Phase 11.8–12 Bridge)
|
||||
|
||||
目的
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
Status: Historical
|
||||
|
||||
# コードベース重複・共通化調査 - インデックス
|
||||
|
||||
Hakorune Rustコードベースにおける重複コードの特定と、DRY原則に基づく改善計画のドキュメントハブです。
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
Status: Historical
|
||||
|
||||
# レガシーコード削除プロジェクト - エグゼクティブサマリー
|
||||
|
||||
**調査日**: 2025-11-06
|
||||
|
||||
@ -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.
|
||||
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
Status: Historical
|
||||
|
||||
# レガシーファイル詳細リスト
|
||||
|
||||
**調査日**: 2025-11-06
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
Status: Historical
|
||||
|
||||
# Phase 1実装ガイド - 低リスク・高効果ヘルパー関数
|
||||
|
||||
## 概要
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
Status: Historical
|
||||
|
||||
# クイック削除ガイド - 今すぐ実行可能なレガシーコード削除
|
||||
|
||||
**即実行可能**: Safe削除のみ (約3,900行削減)
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
Status: Historical
|
||||
|
||||
# 重複コード削減 - クイックリファレンスカード
|
||||
|
||||
## 📊 一目で分かる状況
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
Status: Historical
|
||||
|
||||
# レガシーコード削除プロジェクト
|
||||
|
||||
**調査日**: 2025-11-06
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
Status: Historical
|
||||
|
||||
Legacy By‑Name Call Removal Plan
|
||||
|
||||
Context
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
Status: Historical
|
||||
|
||||
# MIR レガシーコード削除 Phase 1-2 完了報告
|
||||
|
||||
## 📅 実施日
|
||||
@ -52,4 +54,4 @@ Phase 15.5 JSON centralization作業前のコードベースクリーンアッ
|
||||
|
||||
## 📌 備考
|
||||
Task先生の分析に基づく段階的削除戦略により、リスクゼロで実施完了。
|
||||
JSON作業前のクリーンな環境整備に成功。
|
||||
JSON作業前のクリーンな環境整備に成功。
|
||||
|
||||
@ -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<String> = 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)
|
||||
@ -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<Option<ValueId>, 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<ValueId> = (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
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -83,46 +83,9 @@ pub fn lower_loop_with_conditional_phi_to_joinir(
|
||||
_loop_form: &LoopForm,
|
||||
_lowerer: &mut LoopToJoinLowerer,
|
||||
) -> Option<JoinInst> {
|
||||
// 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
|
||||
}
|
||||
|
||||
@ -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 <op> 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<F>(
|
||||
cond: &ASTNode,
|
||||
alloc_value: &mut F,
|
||||
cond_env: &ConditionEnv,
|
||||
) -> Result<(String, CompareOp, ValueId, Vec<JoinInst>), String>
|
||||
) -> Result<(String, CompareOp, Option<ValueId>, ValueId, Vec<JoinInst>), 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<F>(
|
||||
if_stmt: &ASTNode,
|
||||
alloc_value: &mut F,
|
||||
cond_env: &ConditionEnv,
|
||||
) -> Result<(String, CompareOp, ValueId, Vec<JoinInst>), String>
|
||||
) -> Result<(String, CompareOp, Option<ValueId>, ValueId, Vec<JoinInst>), String>
|
||||
where
|
||||
F: FnMut() -> ValueId,
|
||||
{
|
||||
|
||||
@ -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))
|
||||
}
|
||||
@ -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;
|
||||
|
||||
Reference in New Issue
Block a user