feat(joinir): Phase 213 AST-based if-sum lowerer for Pattern 3

Implement dual-mode architecture for Pattern 3 (Loop with If-Else PHI):

- Add is_simple_if_sum_pattern() detection helper
  - Detects 1 CounterLike + 1-2 AccumulationLike carrier patterns
  - Unit tests for various carrier compositions

- Add dual-mode dispatch in Pattern3 lowerer
  - ctx.is_if_sum_pattern() branches to AST-based vs legacy PoC
  - Legacy mode preserved for backward compatibility

- Create loop_with_if_phi_if_sum.rs (~420 lines)
  - AST extraction: loop condition, if condition, updates
  - JoinIR generation: main, loop_step, k_exit structure
  - Helper functions: extract_loop_condition, extract_if_condition, etc.

- Extend PatternPipelineContext for Pattern 3
  - is_if_sum_pattern() detection using LoopUpdateSummary
  - extract_if_statement() helper for body analysis

Note: E2E RC=2 not yet achieved due to pre-existing Pattern 3
pipeline issue (loop back branch targets wrong block). This
affects both if-sum and legacy modes. Fix planned for Phase 214.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
nyash-codex
2025-12-10 00:54:46 +09:00
parent 8394018694
commit 338d1aecf1
7 changed files with 810 additions and 28 deletions

View File

@ -117,6 +117,34 @@ impl LoopUpdateSummary {
.filter(|c| c.kind == UpdateKind::AccumulationLike)
.count()
}
/// Phase 213: Check if this is a simple if-sum pattern
///
/// Simple if-sum pattern:
/// - Has exactly 1 CounterLike carrier (loop index, e.g., "i")
/// - Has exactly 1 AccumulationLike carrier (accumulator, e.g., "sum")
/// - Optionally has additional accumulators (e.g., "count")
///
/// Examples:
/// - `loop(i < len) { if cond { sum = sum + 1 } i = i + 1 }` ✅
/// - `loop(i < len) { if cond { sum = sum + 1; count = count + 1 } i = i + 1 }` ✅
/// - `loop(i < len) { result = result + data[i]; i = i + 1 }` ❌ (no if statement)
pub fn is_simple_if_sum_pattern(&self) -> bool {
// Must have exactly 1 counter (loop index)
if self.counter_count() != 1 {
return false;
}
// Must have at least 1 accumulator (sum)
if self.accumulation_count() < 1 {
return false;
}
// For now, only support up to 2 accumulators (sum, count)
// This matches the Phase 212 if-sum minimal test case
if self.accumulation_count() > 2 {
return false;
}
true
}
}
/// キャリア名から UpdateKind を推定(暫定実装)
@ -221,4 +249,50 @@ mod tests {
assert_eq!(summary.counter_count(), 1);
assert_eq!(summary.accumulation_count(), 1);
}
// Phase 213 tests for is_simple_if_sum_pattern
#[test]
fn test_is_simple_if_sum_pattern_basic() {
// phase212_if_sum_min.hako pattern: i (counter) + sum (accumulator)
let names = vec!["i".to_string(), "sum".to_string()];
let summary = analyze_loop_updates(&names);
assert!(summary.is_simple_if_sum_pattern());
}
#[test]
fn test_is_simple_if_sum_pattern_with_count() {
// Phase 195 pattern: i (counter) + sum + count (2 accumulators)
let names = vec!["i".to_string(), "sum".to_string(), "count".to_string()];
let summary = analyze_loop_updates(&names);
assert!(summary.is_simple_if_sum_pattern());
}
#[test]
fn test_is_simple_if_sum_pattern_no_accumulator() {
// Only counter, no accumulator
let names = vec!["i".to_string()];
let summary = analyze_loop_updates(&names);
assert!(!summary.is_simple_if_sum_pattern()); // No accumulator
}
#[test]
fn test_is_simple_if_sum_pattern_no_counter() {
// Only accumulator, no counter
let names = vec!["sum".to_string()];
let summary = analyze_loop_updates(&names);
assert!(!summary.is_simple_if_sum_pattern()); // No counter
}
#[test]
fn test_is_simple_if_sum_pattern_multiple_counters() {
// Multiple counters (not supported)
let names = vec!["i".to_string(), "j".to_string(), "sum".to_string()];
let summary = analyze_loop_updates(&names);
assert!(!summary.is_simple_if_sum_pattern()); // 2 counters
}
}