2025-12-05 07:47:22 +09:00
|
|
|
//! Loop Pattern Detection Module
|
|
|
|
|
//!
|
|
|
|
|
//! Phase 188 Task 188-4: Pattern detection helpers for JoinIR loop lowering.
|
|
|
|
|
//!
|
|
|
|
|
//! This module provides detection functions for 3 loop patterns:
|
|
|
|
|
//! - Pattern 1: Simple While Loop (foundational)
|
|
|
|
|
//! - Pattern 2: Loop with Conditional Break (early exit)
|
|
|
|
|
//! - Pattern 3: Loop with If-Else PHI (variable mutation)
|
|
|
|
|
//!
|
|
|
|
|
//! These functions are "thin boxes" that take LoopForm and return bool.
|
|
|
|
|
//! No side effects, pure detection logic.
|
|
|
|
|
//!
|
|
|
|
|
//! Reference: docs/private/roadmap2/phases/phase-188-joinir-loop-pattern-expansion/design.md
|
|
|
|
|
|
|
|
|
|
use crate::mir::loop_form::LoopForm;
|
|
|
|
|
|
|
|
|
|
// ============================================================================
|
|
|
|
|
// Pattern 1: Simple While Loop
|
|
|
|
|
// ============================================================================
|
|
|
|
|
|
|
|
|
|
/// Detect Pattern 1: Simple While Loop
|
|
|
|
|
///
|
|
|
|
|
/// Returns true ONLY if:
|
|
|
|
|
/// - Loop condition is simple comparison (no &&, ||)
|
|
|
|
|
/// - Loop body contains only assignments + prints (no nested loops, no breaks)
|
|
|
|
|
/// - Loop has single increment/decrement
|
|
|
|
|
/// - NO break statements (break_targets is empty)
|
|
|
|
|
/// - NO continue statements (continue_targets is empty)
|
|
|
|
|
/// - Single backedge (latches.len() == 1)
|
|
|
|
|
///
|
|
|
|
|
/// # Arguments
|
|
|
|
|
/// * `loop_form` - The loop structure to analyze
|
|
|
|
|
///
|
|
|
|
|
/// # Returns
|
|
|
|
|
/// * `true` if the loop matches Pattern 1 (Simple While), `false` otherwise
|
|
|
|
|
///
|
|
|
|
|
/// # Reference
|
|
|
|
|
/// See design.md § Pattern 1 → LoopScopeShape Recognition
|
|
|
|
|
///
|
|
|
|
|
/// # Example
|
|
|
|
|
/// ```rust,ignore
|
|
|
|
|
/// let loop_form = /* ... */;
|
|
|
|
|
/// if is_simple_while_pattern(&loop_form) {
|
|
|
|
|
/// // Lower to simple while pattern
|
|
|
|
|
/// }
|
|
|
|
|
/// ```
|
|
|
|
|
pub fn is_simple_while_pattern(loop_form: &LoopForm) -> bool {
|
|
|
|
|
// Pattern 1 Recognition Criteria (from design.md § Pattern 1):
|
|
|
|
|
// 1. break_targets: EMPTY (no break statements)
|
|
|
|
|
// 2. continue_targets: EMPTY (no continue statements)
|
|
|
|
|
// 3. Single backedge (single latch - LoopShape has singular latch field)
|
|
|
|
|
//
|
|
|
|
|
// Note: LoopShape has a singular `latch` field, not `latches`, so we don't
|
|
|
|
|
// need to check length. The existence of a LoopShape implies a valid latch.
|
|
|
|
|
|
|
|
|
|
// Check 1: No break statements
|
|
|
|
|
if !loop_form.break_targets.is_empty() {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check 2: No continue statements
|
|
|
|
|
if !loop_form.continue_targets.is_empty() {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check 3: All other checks passed
|
|
|
|
|
// The LoopShape structure guarantees:
|
|
|
|
|
// - Single preheader, header, body, latch, exit
|
|
|
|
|
// - Valid loop structure
|
|
|
|
|
//
|
|
|
|
|
// Pattern 1 ONLY requires:
|
|
|
|
|
// - No breaks, no continues
|
|
|
|
|
// - Natural loop structure (which LoopShape guarantees)
|
|
|
|
|
//
|
|
|
|
|
// Advanced checks (nested loops, complex conditions) are deferred to
|
|
|
|
|
// lowering phase where we can fail gracefully if needed.
|
|
|
|
|
|
|
|
|
|
true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ============================================================================
|
|
|
|
|
// Pattern 2: Loop with Conditional Break
|
|
|
|
|
// ============================================================================
|
|
|
|
|
|
|
|
|
|
/// Detect Pattern 2: Loop with Conditional Break
|
|
|
|
|
///
|
|
|
|
|
/// Returns true ONLY if:
|
|
|
|
|
/// - Loop condition exists
|
|
|
|
|
/// - Loop body contains exactly ONE if statement with break
|
|
|
|
|
/// - Break is in then-branch
|
|
|
|
|
/// - NO nested loops
|
|
|
|
|
/// - break_targets is NON-EMPTY (has at least one break)
|
|
|
|
|
///
|
|
|
|
|
/// # Arguments
|
|
|
|
|
/// * `loop_form` - The loop structure to analyze
|
|
|
|
|
///
|
|
|
|
|
/// # Returns
|
|
|
|
|
/// * `true` if the loop matches Pattern 2 (Break), `false` otherwise
|
|
|
|
|
///
|
|
|
|
|
/// # Reference
|
|
|
|
|
/// See design.md § Pattern 2 → LoopScopeShape Recognition
|
|
|
|
|
///
|
|
|
|
|
/// # Example
|
|
|
|
|
/// ```rust,ignore
|
|
|
|
|
/// let loop_form = /* ... */;
|
|
|
|
|
/// if is_loop_with_break_pattern(&loop_form) {
|
|
|
|
|
/// // Lower to loop with break pattern
|
|
|
|
|
/// }
|
|
|
|
|
/// ```
|
|
|
|
|
pub fn is_loop_with_break_pattern(loop_form: &LoopForm) -> bool {
|
2025-12-05 15:28:54 +09:00
|
|
|
// Pattern 2 Recognition Criteria (from design.md § Pattern 2):
|
|
|
|
|
// 1. break_targets: NON-EMPTY (at least 1 break)
|
|
|
|
|
// 2. continue_targets: EMPTY (for simplicity)
|
|
|
|
|
// 3. Exactly ONE break target
|
2025-12-05 07:47:22 +09:00
|
|
|
//
|
2025-12-05 15:28:54 +09:00
|
|
|
// Phase 188-Impl-2: Minimal implementation
|
|
|
|
|
// Advanced checks (nested loops, if-statement structure) are deferred to
|
|
|
|
|
// lowering phase where we can fail gracefully if needed.
|
|
|
|
|
|
|
|
|
|
// Check 1: break_targets is NON-EMPTY (has at least 1 break)
|
|
|
|
|
if loop_form.break_targets.is_empty() {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check 2: Exactly ONE break target (pattern assumes single break)
|
|
|
|
|
if loop_form.break_targets.len() != 1 {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check 3: No continue statements (for simplicity in Pattern 2)
|
|
|
|
|
if !loop_form.continue_targets.is_empty() {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Pattern 2 matched
|
|
|
|
|
// The LoopForm structure guarantees:
|
|
|
|
|
// - Valid loop structure
|
|
|
|
|
// - Single break target
|
|
|
|
|
// - No continues
|
2025-12-05 07:47:22 +09:00
|
|
|
//
|
2025-12-05 15:28:54 +09:00
|
|
|
// Advanced checks (break is in if-statement, etc.) are deferred to
|
|
|
|
|
// lowering phase for graceful failure.
|
|
|
|
|
|
|
|
|
|
true
|
2025-12-05 07:47:22 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ============================================================================
|
|
|
|
|
// Pattern 3: Loop with If-Else PHI
|
|
|
|
|
// ============================================================================
|
|
|
|
|
|
|
|
|
|
/// Detect Pattern 3: Loop with If-Else PHI
|
|
|
|
|
///
|
|
|
|
|
/// Returns true ONLY if:
|
|
|
|
|
/// - Loop has if-else statement assigning to variable(s)
|
|
|
|
|
/// - Both branches assign to same variable
|
|
|
|
|
/// - NO nested loops
|
|
|
|
|
/// - NO break or continue statements
|
|
|
|
|
/// - Loop has multiple carrier variables (e.g., i + sum)
|
|
|
|
|
///
|
|
|
|
|
/// # Arguments
|
|
|
|
|
/// * `loop_form` - The loop structure to analyze
|
|
|
|
|
///
|
|
|
|
|
/// # Returns
|
|
|
|
|
/// * `true` if the loop matches Pattern 3 (If-Else PHI), `false` otherwise
|
|
|
|
|
///
|
|
|
|
|
/// # Reference
|
|
|
|
|
/// See design.md § Pattern 3 → LoopScopeShape Recognition
|
|
|
|
|
///
|
|
|
|
|
/// # Example
|
|
|
|
|
/// ```rust,ignore
|
|
|
|
|
/// let loop_form = /* ... */;
|
|
|
|
|
/// if is_loop_with_conditional_phi_pattern(&loop_form) {
|
|
|
|
|
/// // Lower to loop with if-else phi pattern
|
|
|
|
|
/// }
|
|
|
|
|
/// ```
|
|
|
|
|
pub fn is_loop_with_conditional_phi_pattern(loop_form: &LoopForm) -> bool {
|
feat(joinir): Phase 188-Impl-3 Pattern 3 (Loop with If-Else PHI) implementation
Add Pattern 3 lowerer for `loop { if cond { x = a } else { x = b } ... }` pattern.
New files:
- loop_with_if_phi_minimal.rs (381 lines): JoinIR lowerer for Pattern 3
- Multiple loop variables (counter + accumulator)
- In-loop if/else PHI using Select instruction
- Carriers passed to next iteration via tail recursion
Modified files:
- join_ir/mod.rs: Add Mod to BinOpKind, Select to MirLikeInst
- loop_pattern_detection.rs: Add is_loop_with_conditional_phi_pattern() detection
- lowering/mod.rs: Pattern 3 router integration
- loop_patterns.rs: Pattern 3 entry point delegation
- json.rs: Mod/Select JSON serialization
- join_ir_ops.rs: Mod operation evaluation (a % b)
- join_ir_runner.rs: Select instruction execution
- join_ir_vm_bridge/convert.rs: Mod/Select conversion handlers
Implementation:
- Pattern 3 generates 3 JoinIR functions: main, loop_step(i, sum), k_exit(sum_final)
- Exit condition: !(i <= 5) with Jump to k_exit
- In-loop if/else: if (i % 2 == 1) { sum + i } else { sum + 0 }
- Select instruction: sum_new = Select(if_cond, sum_then, sum_else)
- Both carriers updated: Call(loop_step, [i_next, sum_new])
Build status: ✅ Compiles successfully (0 errors, 34 warnings)
Integration: Infrastructure complete, MIR boundary mapping pending
All 3 patterns now have lowering infrastructure in place for Phase 188.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-05 15:45:42 +09:00
|
|
|
// Phase 188-Impl-3: Minimal implementation
|
|
|
|
|
// Pattern 3 Recognition Criteria (from design.md § Pattern 3):
|
|
|
|
|
// 1. break_targets: EMPTY (no break statements)
|
|
|
|
|
// 2. continue_targets: EMPTY (no continue statements)
|
|
|
|
|
// 3. All Pattern 3 loops are valid Pattern 1 loops with extra PHI nodes
|
2025-12-05 07:47:22 +09:00
|
|
|
//
|
feat(joinir): Phase 188-Impl-3 Pattern 3 (Loop with If-Else PHI) implementation
Add Pattern 3 lowerer for `loop { if cond { x = a } else { x = b } ... }` pattern.
New files:
- loop_with_if_phi_minimal.rs (381 lines): JoinIR lowerer for Pattern 3
- Multiple loop variables (counter + accumulator)
- In-loop if/else PHI using Select instruction
- Carriers passed to next iteration via tail recursion
Modified files:
- join_ir/mod.rs: Add Mod to BinOpKind, Select to MirLikeInst
- loop_pattern_detection.rs: Add is_loop_with_conditional_phi_pattern() detection
- lowering/mod.rs: Pattern 3 router integration
- loop_patterns.rs: Pattern 3 entry point delegation
- json.rs: Mod/Select JSON serialization
- join_ir_ops.rs: Mod operation evaluation (a % b)
- join_ir_runner.rs: Select instruction execution
- join_ir_vm_bridge/convert.rs: Mod/Select conversion handlers
Implementation:
- Pattern 3 generates 3 JoinIR functions: main, loop_step(i, sum), k_exit(sum_final)
- Exit condition: !(i <= 5) with Jump to k_exit
- In-loop if/else: if (i % 2 == 1) { sum + i } else { sum + 0 }
- Select instruction: sum_new = Select(if_cond, sum_then, sum_else)
- Both carriers updated: Call(loop_step, [i_next, sum_new])
Build status: ✅ Compiles successfully (0 errors, 34 warnings)
Integration: Infrastructure complete, MIR boundary mapping pending
All 3 patterns now have lowering infrastructure in place for Phase 188.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-05 15:45:42 +09:00
|
|
|
// For now: return true as fallback for Pattern 1 loops
|
|
|
|
|
// Advanced checks (if-else detection, multiple carriers) are deferred to
|
|
|
|
|
// lowering phase where we can fail gracefully if needed.
|
|
|
|
|
|
|
|
|
|
// Check 1: No break statements
|
|
|
|
|
if !loop_form.break_targets.is_empty() {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check 2: No continue statements
|
|
|
|
|
if !loop_form.continue_targets.is_empty() {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Pattern 3 matched (fallback for now)
|
|
|
|
|
// Since all Pattern 3 loops are also Pattern 1 loops, we can safely return true
|
|
|
|
|
// The lowering phase will determine if the specific pattern is supported
|
|
|
|
|
true
|
2025-12-05 07:47:22 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ============================================================================
|
|
|
|
|
// Helper Functions (Future Use)
|
|
|
|
|
// ============================================================================
|
|
|
|
|
|
|
|
|
|
/// Count the number of carrier variables in a loop
|
|
|
|
|
///
|
|
|
|
|
/// Carrier variables are loop variables that are updated in the loop body
|
|
|
|
|
/// and carried through PHI nodes in the header.
|
|
|
|
|
///
|
|
|
|
|
/// # Arguments
|
|
|
|
|
/// * `loop_form` - The loop structure to analyze
|
|
|
|
|
///
|
|
|
|
|
/// # Returns
|
|
|
|
|
/// * Number of carrier variables
|
|
|
|
|
///
|
|
|
|
|
/// # TODO
|
|
|
|
|
/// Implement by analyzing header PHI nodes
|
|
|
|
|
#[allow(dead_code)]
|
|
|
|
|
fn count_carrier_variables(loop_form: &LoopForm) -> usize {
|
|
|
|
|
// TODO: Implement carrier variable counting
|
|
|
|
|
// Step 1: Access loop_form.header block
|
|
|
|
|
// Step 2: Count PHI instructions in header
|
|
|
|
|
// Step 3: Return count
|
|
|
|
|
0
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Check if loop body contains nested loops
|
|
|
|
|
///
|
|
|
|
|
/// # Arguments
|
|
|
|
|
/// * `loop_form` - The loop structure to analyze
|
|
|
|
|
///
|
|
|
|
|
/// # Returns
|
|
|
|
|
/// * `true` if nested loops found, `false` otherwise
|
|
|
|
|
///
|
|
|
|
|
/// # TODO
|
|
|
|
|
/// Implement by checking for LoopForm within body blocks
|
|
|
|
|
#[allow(dead_code)]
|
|
|
|
|
fn has_nested_loops(loop_form: &LoopForm) -> bool {
|
|
|
|
|
// TODO: Implement nested loop detection
|
|
|
|
|
// Step 1: Traverse body blocks
|
|
|
|
|
// Step 2: Check for loop headers in body
|
|
|
|
|
// Step 3: Return true if any found
|
|
|
|
|
false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Check if loop condition is simple (single comparison, no && or ||)
|
|
|
|
|
///
|
|
|
|
|
/// # Arguments
|
|
|
|
|
/// * `loop_form` - The loop structure to analyze
|
|
|
|
|
///
|
|
|
|
|
/// # Returns
|
|
|
|
|
/// * `true` if condition is simple, `false` otherwise
|
|
|
|
|
///
|
|
|
|
|
/// # TODO
|
|
|
|
|
/// Implement by checking header condition complexity
|
|
|
|
|
#[allow(dead_code)]
|
|
|
|
|
fn has_simple_condition(loop_form: &LoopForm) -> bool {
|
|
|
|
|
// TODO: Implement condition complexity check
|
|
|
|
|
// Step 1: Access loop_form.header block
|
|
|
|
|
// Step 2: Find condition instruction
|
|
|
|
|
// Step 3: Check for && or || operators
|
|
|
|
|
// Step 4: Return true if no complex operators
|
|
|
|
|
true // Assume simple for now
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
mod tests {
|
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
|
|
// ========================================================================
|
|
|
|
|
// Pattern 1: Simple While Loop Tests
|
|
|
|
|
// ========================================================================
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
#[ignore] // TODO: Implement test after detection logic is complete
|
|
|
|
|
fn test_pattern1_simple_while_detection() {
|
|
|
|
|
// TODO: Add unit test for simple while pattern detection
|
|
|
|
|
// Step 1: Create mock LoopForm with:
|
|
|
|
|
// - Empty break_targets
|
|
|
|
|
// - Empty continue_targets
|
|
|
|
|
// - Single latch
|
|
|
|
|
// Step 2: Call is_simple_while_pattern()
|
|
|
|
|
// Step 3: Assert returns true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
#[ignore] // TODO: Implement test after detection logic is complete
|
|
|
|
|
fn test_pattern1_rejects_break() {
|
|
|
|
|
// TODO: Add test that rejects loop with break
|
|
|
|
|
// Step 1: Create mock LoopForm with non-empty break_targets
|
|
|
|
|
// Step 2: Call is_simple_while_pattern()
|
|
|
|
|
// Step 3: Assert returns false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ========================================================================
|
|
|
|
|
// Pattern 2: Loop with Break Tests
|
|
|
|
|
// ========================================================================
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
#[ignore] // TODO: Implement test after detection logic is complete
|
|
|
|
|
fn test_pattern2_break_detection() {
|
|
|
|
|
// TODO: Add unit test for break pattern detection
|
|
|
|
|
// Step 1: Create mock LoopForm with:
|
|
|
|
|
// - Non-empty break_targets (exactly 1)
|
|
|
|
|
// - Empty continue_targets
|
|
|
|
|
// - If statement with break
|
|
|
|
|
// Step 2: Call is_loop_with_break_pattern()
|
|
|
|
|
// Step 3: Assert returns true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
#[ignore] // TODO: Implement test after detection logic is complete
|
|
|
|
|
fn test_pattern2_rejects_no_break() {
|
|
|
|
|
// TODO: Add test that rejects loop without break
|
|
|
|
|
// Step 1: Create mock LoopForm with empty break_targets
|
|
|
|
|
// Step 2: Call is_loop_with_break_pattern()
|
|
|
|
|
// Step 3: Assert returns false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ========================================================================
|
|
|
|
|
// Pattern 3: Loop with If-Else PHI Tests
|
|
|
|
|
// ========================================================================
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
#[ignore] // TODO: Implement test after detection logic is complete
|
|
|
|
|
fn test_pattern3_if_else_phi_detection() {
|
|
|
|
|
// TODO: Add unit test for if-else phi pattern detection
|
|
|
|
|
// Step 1: Create mock LoopForm with:
|
|
|
|
|
// - Empty break_targets
|
|
|
|
|
// - Empty continue_targets
|
|
|
|
|
// - If-else statement in body
|
|
|
|
|
// - Multiple carrier variables
|
|
|
|
|
// Step 2: Call is_loop_with_conditional_phi_pattern()
|
|
|
|
|
// Step 3: Assert returns true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
#[ignore] // TODO: Implement test after detection logic is complete
|
|
|
|
|
fn test_pattern3_rejects_break() {
|
|
|
|
|
// TODO: Add test that rejects loop with break
|
|
|
|
|
// Step 1: Create mock LoopForm with non-empty break_targets
|
|
|
|
|
// Step 2: Call is_loop_with_conditional_phi_pattern()
|
|
|
|
|
// Step 3: Assert returns false
|
|
|
|
|
}
|
|
|
|
|
}
|