feat(joinir): Structural pattern detection + Pattern 4 scaffold
- Add LoopFeatures struct for structure-based detection (no name deps) - Add LoopPatternKind enum and classify() function - Pattern 3: has_if_else_phi && !has_break && !has_continue - Pattern 4: has_continue == true (detection only, lowering TODO) - Unify router to use extract_features()/classify() instead of legacy - Remove AST dependency, use LoopForm/LoopScope only - Add Pattern 4 test file (loop_continue_pattern4.hako) - Pattern 3 test passes (sum=9) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@ -2,17 +2,183 @@
|
||||
//!
|
||||
//! Phase 188 Task 188-4: Pattern detection helpers for JoinIR loop lowering.
|
||||
//!
|
||||
//! This module provides detection functions for 3 loop patterns:
|
||||
//! This module provides detection functions for 4 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)
|
||||
//! - Pattern 4: Loop with Continue (skip iteration)
|
||||
//!
|
||||
//! These functions are "thin boxes" that take LoopForm and return bool.
|
||||
//! No side effects, pure detection logic.
|
||||
//! Phase 194+: Structure-based detection using LoopFeatures.
|
||||
//! Patterns are classified based on CFG structure, not function names.
|
||||
//!
|
||||
//! # Architecture
|
||||
//!
|
||||
//! ```
|
||||
//! LoopForm → extract_features() → LoopFeatures → classify() → LoopPatternKind
|
||||
//! ```
|
||||
//!
|
||||
//! Reference: docs/private/roadmap2/phases/phase-188-joinir-loop-pattern-expansion/design.md
|
||||
|
||||
use crate::mir::loop_form::LoopForm;
|
||||
use crate::mir::join_ir::lowering::loop_scope_shape::LoopScopeShape;
|
||||
|
||||
// ============================================================================
|
||||
// Pattern Classification System (Phase 194+)
|
||||
// ============================================================================
|
||||
|
||||
/// Loop pattern classification based on structure.
|
||||
///
|
||||
/// This enum represents the 4 main loop patterns we support.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum LoopPatternKind {
|
||||
/// Pattern 1: Simple While Loop
|
||||
/// - No break, no continue
|
||||
/// - Single backedge
|
||||
Pattern1SimpleWhile,
|
||||
|
||||
/// Pattern 2: Loop with Conditional Break
|
||||
/// - Has break statement(s)
|
||||
/// - No continue statements
|
||||
Pattern2Break,
|
||||
|
||||
/// Pattern 3: Loop with If-Else PHI
|
||||
/// - Has if-else statement with PHI
|
||||
/// - No break, no continue
|
||||
/// - Multiple carrier variables
|
||||
Pattern3IfPhi,
|
||||
|
||||
/// Pattern 4: Loop with Continue
|
||||
/// - Has continue statement(s)
|
||||
/// - No break statements (for simplicity)
|
||||
Pattern4Continue,
|
||||
|
||||
/// Pattern not recognized
|
||||
Unknown,
|
||||
}
|
||||
|
||||
/// Feature vector extracted from loop structure.
|
||||
///
|
||||
/// This structure captures all relevant properties needed for pattern classification.
|
||||
/// It is name-agnostic and purely structure-based.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct LoopFeatures {
|
||||
/// Has break statement(s)?
|
||||
pub has_break: bool,
|
||||
|
||||
/// Has continue statement(s)?
|
||||
pub has_continue: bool,
|
||||
|
||||
/// Has if statement(s) in body?
|
||||
pub has_if: bool,
|
||||
|
||||
/// Has if-else statement with PHI nodes?
|
||||
/// (detected via multiple carriers or specific CFG patterns)
|
||||
pub has_if_else_phi: bool,
|
||||
|
||||
/// Number of carrier variables (loop variables that are updated)
|
||||
pub carrier_count: usize,
|
||||
|
||||
/// Number of break targets
|
||||
pub break_count: usize,
|
||||
|
||||
/// Number of continue targets
|
||||
pub continue_count: usize,
|
||||
}
|
||||
|
||||
/// Extract features from LoopForm for pattern classification.
|
||||
///
|
||||
/// This function is the entry point for structure-based pattern detection.
|
||||
/// It analyzes the CFG structure without relying on variable names.
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `loop_form` - The loop structure to analyze
|
||||
/// * `scope` - Optional LoopScopeShape for carrier analysis
|
||||
///
|
||||
/// # Returns
|
||||
/// * `LoopFeatures` - Feature vector for pattern classification
|
||||
pub fn extract_features(loop_form: &LoopForm, scope: Option<&LoopScopeShape>) -> LoopFeatures {
|
||||
// Phase 194: Basic feature extraction from LoopForm
|
||||
let has_break = !loop_form.break_targets.is_empty();
|
||||
let has_continue = !loop_form.continue_targets.is_empty();
|
||||
let break_count = loop_form.break_targets.len();
|
||||
let continue_count = loop_form.continue_targets.len();
|
||||
|
||||
// Phase 194+: Extract carrier_count from LoopScopeShape if available
|
||||
let carrier_count = scope.map(|s| s.carriers.len()).unwrap_or(0);
|
||||
|
||||
// Pattern 3 heuristic: has_if_else_phi if carrier_count > 1
|
||||
// This is a conservative heuristic - multiple carriers typically
|
||||
// indicate if-else statements with PHI nodes.
|
||||
let has_if_else_phi = carrier_count > 1;
|
||||
|
||||
// TODO: Implement has_if detection via CFG analysis
|
||||
// For now, we infer it from carrier_count > 1 (Pattern 3 heuristic)
|
||||
let has_if = has_if_else_phi;
|
||||
|
||||
LoopFeatures {
|
||||
has_break,
|
||||
has_continue,
|
||||
has_if,
|
||||
has_if_else_phi,
|
||||
carrier_count,
|
||||
break_count,
|
||||
continue_count,
|
||||
}
|
||||
}
|
||||
|
||||
/// Classify loop pattern based on feature vector.
|
||||
///
|
||||
/// This function implements the pattern classification logic using
|
||||
/// structure-based rules. It does NOT depend on function names or
|
||||
/// variable names like "sum".
|
||||
///
|
||||
/// # Pattern Classification Rules
|
||||
///
|
||||
/// 1. **Pattern 4 (Continue)**: `has_continue == true`
|
||||
/// - Priority: Check first (most specific)
|
||||
///
|
||||
/// 2. **Pattern 3 (If-Else PHI)**: `has_if_else_phi && !has_break && !has_continue`
|
||||
/// - Multiple carriers indicate if-else with PHI
|
||||
///
|
||||
/// 3. **Pattern 2 (Break)**: `has_break && !has_continue`
|
||||
/// - Has break but no continue
|
||||
///
|
||||
/// 4. **Pattern 1 (Simple While)**: `!has_break && !has_continue && !has_if_else_phi`
|
||||
/// - No control flow alterations
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `features` - Feature vector from extract_features()
|
||||
///
|
||||
/// # Returns
|
||||
/// * `LoopPatternKind` - Classified pattern
|
||||
pub fn classify(features: &LoopFeatures) -> LoopPatternKind {
|
||||
// Pattern 4: Continue (highest priority)
|
||||
if features.has_continue {
|
||||
return LoopPatternKind::Pattern4Continue;
|
||||
}
|
||||
|
||||
// Pattern 3: If-Else PHI (check before Pattern 1)
|
||||
if features.has_if_else_phi && !features.has_break && !features.has_continue {
|
||||
return LoopPatternKind::Pattern3IfPhi;
|
||||
}
|
||||
|
||||
// Pattern 2: Break
|
||||
if features.has_break && !features.has_continue {
|
||||
return LoopPatternKind::Pattern2Break;
|
||||
}
|
||||
|
||||
// Pattern 1: Simple While
|
||||
if !features.has_break && !features.has_continue && !features.has_if_else_phi {
|
||||
return LoopPatternKind::Pattern1SimpleWhile;
|
||||
}
|
||||
|
||||
// Unknown pattern
|
||||
LoopPatternKind::Unknown
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Legacy Detection Functions (Phase 188)
|
||||
// ============================================================================
|
||||
|
||||
// ============================================================================
|
||||
// Pattern 1: Simple While Loop
|
||||
@ -200,6 +366,71 @@ pub fn is_loop_with_conditional_phi_pattern(loop_form: &LoopForm) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Pattern 4: Loop with Continue
|
||||
// ============================================================================
|
||||
|
||||
/// Detect Pattern 4: Loop with Continue
|
||||
///
|
||||
/// Returns true ONLY if:
|
||||
/// - Loop has continue statement(s)
|
||||
/// - Continue is typically in an if statement
|
||||
/// - NO break statements (for simplicity)
|
||||
/// - Loop has multiple carrier variables
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `loop_form` - The loop structure to analyze
|
||||
///
|
||||
/// # Returns
|
||||
/// * `true` if the loop matches Pattern 4 (Continue), `false` otherwise
|
||||
///
|
||||
/// # Reference
|
||||
/// See design.md § Pattern 4 → LoopScopeShape Recognition
|
||||
///
|
||||
/// # Example
|
||||
/// ```rust,ignore
|
||||
/// let loop_form = /* ... */;
|
||||
/// if is_loop_with_continue_pattern(&loop_form) {
|
||||
/// // Lower to loop with continue pattern
|
||||
/// }
|
||||
/// ```
|
||||
pub fn is_loop_with_continue_pattern(loop_form: &LoopForm) -> bool {
|
||||
// Pattern 4 Recognition Criteria:
|
||||
// 1. continue_targets: NON-EMPTY (at least 1 continue)
|
||||
// 2. break_targets: EMPTY (for simplicity in Pattern 4)
|
||||
// 3. At least ONE continue target
|
||||
//
|
||||
// Phase 188-Impl-4: Minimal implementation
|
||||
// Advanced checks (nested loops, if-statement structure) are deferred to
|
||||
// lowering phase where we can fail gracefully if needed.
|
||||
|
||||
// Check 1: continue_targets is NON-EMPTY (has at least 1 continue)
|
||||
if loop_form.continue_targets.is_empty() {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check 2: At least ONE continue target (pattern assumes single continue for now)
|
||||
if loop_form.continue_targets.len() < 1 {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check 3: No break statements (for simplicity in Pattern 4)
|
||||
if !loop_form.break_targets.is_empty() {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Pattern 4 matched
|
||||
// The LoopForm structure guarantees:
|
||||
// - Valid loop structure
|
||||
// - At least one continue target
|
||||
// - No breaks
|
||||
//
|
||||
// Advanced checks (continue is in if-statement, etc.) are deferred to
|
||||
// lowering phase for graceful failure.
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Helper Functions (Future Use)
|
||||
// ============================================================================
|
||||
@ -344,4 +575,29 @@ mod tests {
|
||||
// Step 2: Call is_loop_with_conditional_phi_pattern()
|
||||
// Step 3: Assert returns false
|
||||
}
|
||||
|
||||
// ========================================================================
|
||||
// Pattern 4: Loop with Continue Tests
|
||||
// ========================================================================
|
||||
|
||||
#[test]
|
||||
#[ignore] // TODO: Implement test after detection logic is complete
|
||||
fn test_pattern4_continue_detection() {
|
||||
// TODO: Add unit test for continue pattern detection
|
||||
// Step 1: Create mock LoopForm with:
|
||||
// - Non-empty continue_targets (at least 1)
|
||||
// - Empty break_targets
|
||||
// - If statement with continue
|
||||
// Step 2: Call is_loop_with_continue_pattern()
|
||||
// Step 3: Assert returns true
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore] // TODO: Implement test after detection logic is complete
|
||||
fn test_pattern4_rejects_no_continue() {
|
||||
// TODO: Add test that rejects loop without continue
|
||||
// Step 1: Create mock LoopForm with empty continue_targets
|
||||
// Step 2: Call is_loop_with_continue_pattern()
|
||||
// Step 3: Assert returns false
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user