//! Pattern Router - Table-driven dispatch for loop patterns //! //! Phase 194: Replace if/else chain with table-driven routing //! Phase 193: Modularized feature extraction using ast_feature_extractor module //! //! # Architecture //! //! - Each pattern registers a detect function and a lower function //! - Patterns are tried in priority order (lower = tried first) //! - First matching pattern wins //! - Feature extraction delegated to ast_feature_extractor module //! //! # Adding New Patterns //! //! 1. Create a new module in `patterns/` (e.g., `pattern4_your_name.rs`) //! 2. Implement `pub fn can_lower(ctx: &LoopPatternContext) -> bool` //! 3. Implement `pub fn lower(builder: &mut MirBuilder, ctx: &LoopPatternContext) -> Result, String>` //! 4. Add entry to `LOOP_PATTERNS` table below //! //! That's it! No need to modify routing logic. use crate::ast::ASTNode; use crate::mir::builder::MirBuilder; use crate::mir::ValueId; use crate::mir::loop_pattern_detection::{LoopFeatures, LoopPatternKind}; /// Phase 193: Import AST Feature Extractor Box /// (declared in mod.rs as pub module, import from parent) use super::ast_feature_extractor as ast_features; /// Phase 92 P0-2: Import LoopSkeleton for Option A use crate::mir::loop_canonicalizer::LoopSkeleton; /// Context passed to pattern detect/lower functions pub(crate) struct LoopPatternContext<'a> { /// Loop condition AST node pub condition: &'a ASTNode, /// Loop body statements pub body: &'a [ASTNode], /// Current function name (for routing) pub func_name: &'a str, /// Debug logging enabled pub debug: bool, /// Has continue statement(s) in body? (Phase 194+) #[allow(dead_code)] pub has_continue: bool, /// Has break statement(s) in body? (Phase 194+) #[allow(dead_code)] pub has_break: bool, /// Phase 192: Loop features extracted from AST #[allow(dead_code)] pub features: LoopFeatures, /// Phase 192: Pattern classification based on features pub pattern_kind: LoopPatternKind, /// Phase 200-C: Optional function body AST for capture analysis /// None if not available, Some(&[ASTNode]) if function body is accessible pub fn_body: Option<&'a [ASTNode]>, /// Phase 92 P0-2: Optional LoopSkeleton from canonicalizer /// This provides ConditionalStep information for Pattern2 lowering. /// None if canonicalizer hasn't run yet (backward compatibility). /// SSOT Principle: Avoid re-detecting ConditionalStep in lowering phase. #[allow(dead_code)] pub skeleton: Option<&'a LoopSkeleton>, } impl<'a> LoopPatternContext<'a> { /// Create new context from routing parameters /// /// Phase 194+: Automatically detects continue/break statements in body /// Phase 192: Extract features and classify pattern from AST /// Phase 193: Feature extraction delegated to ast_feature_extractor module /// Phase 131-11: Detects infinite loop condition /// Phase 137-6-S1: Use choose_pattern_kind() SSOT entry point /// Phase 92 P0-2: Added skeleton parameter (None for backward compatibility) pub(crate) fn new( condition: &'a ASTNode, body: &'a [ASTNode], func_name: &'a str, debug: bool, ) -> Self { // Phase 193: Use AST Feature Extractor Box for break/continue detection let has_continue = ast_features::detect_continue_in_body(body); let has_break = ast_features::detect_break_in_body(body); // Phase 193: Extract features using modularized extractor // Phase 131-11: Pass condition for infinite loop detection let features = ast_features::extract_features(condition, body, has_continue, has_break); // Phase 137-6-S1: Use SSOT pattern selection entry point use crate::mir::builder::control_flow::joinir::routing::choose_pattern_kind; let pattern_kind = choose_pattern_kind(condition, body); Self { condition, body, func_name, debug, has_continue, has_break, features, pattern_kind, fn_body: None, // Phase 200-C: Default to None skeleton: None, // Phase 92 P0-2: Default to None } } /// Phase 200-C: Create context with fn_body for capture analysis pub(crate) fn with_fn_body( condition: &'a ASTNode, body: &'a [ASTNode], func_name: &'a str, debug: bool, fn_body: &'a [ASTNode], ) -> Self { let mut ctx = Self::new(condition, body, func_name, debug); ctx.fn_body = Some(fn_body); ctx } /// Phase 92 P0-2: Set skeleton (for canonicalizer integration) #[allow(dead_code)] pub(crate) fn with_skeleton(mut self, skeleton: &'a LoopSkeleton) -> Self { self.skeleton = Some(skeleton); self } } /// Phase 193: Feature extraction moved to ast_feature_extractor module /// See: src/mir/builder/control_flow/joinir/patterns/ast_feature_extractor.rs /// Entry in the loop pattern router table. /// Each pattern registers a detect function and a lower function. pub(crate) struct LoopPatternEntry { /// Human-readable pattern name for debugging pub(crate) name: &'static str, /// Detection function: returns true if this pattern matches pub(crate) detect: fn(&MirBuilder, &LoopPatternContext) -> bool, /// Lowering function: performs the actual JoinIR generation pub(crate) lower: fn(&mut MirBuilder, &LoopPatternContext) -> Result, String>, } /// Static table of all registered loop patterns. /// /// **IMPORTANT**: Patterns are tried in array order (SSOT). /// Array order defines priority - earlier entries are tried first. /// Pattern5 (most specific) → Pattern4 → Pattern3 → Pattern1 → Pattern2 /// /// # Current Patterns (Phase 131-11: Pattern 5 added) /// /// All patterns now use structure-based detection via LoopFeatures and classify(): /// /// - Pattern 5: Infinite Loop with Early Exit (llvm_stage3_loop_only.hako) [Phase 131-11] /// - Detection: pattern_kind == InfiniteEarlyExit /// - Structure: is_infinite_loop && has_break && has_continue /// /// - Pattern 4: Loop with Continue (loop_continue_pattern4.hako) /// - Detection: pattern_kind == Pattern4Continue /// - Structure: has_continue && !has_break /// /// - Pattern 3: Loop with If-Else PHI (loop_if_phi.hako) /// - Detection: pattern_kind == Pattern3IfPhi /// - Structure: has_if_else_phi && !has_break && !has_continue /// /// - Pattern 1: Simple While Loop (loop_min_while.hako) /// - Detection: pattern_kind == Pattern1SimpleWhile /// - Structure: !has_break && !has_continue && !has_if_else_phi /// /// - Pattern 2: Loop with Conditional Break (joinir_min_loop.hako) /// - Detection: pattern_kind == Pattern2Break /// - Structure: has_break && !has_continue /// /// Note: func_name is now only used for debug logging, not pattern detection pub(crate) static LOOP_PATTERNS: &[LoopPatternEntry] = &[ LoopPatternEntry { name: "Pattern5_InfiniteEarlyExit", detect: super::pattern5_infinite_early_exit::can_lower, lower: super::pattern5_infinite_early_exit::lower, }, LoopPatternEntry { name: "Pattern4_WithContinue", detect: super::pattern4_with_continue::can_lower, lower: super::pattern4_with_continue::lower, }, LoopPatternEntry { name: "Pattern3_WithIfPhi", detect: super::pattern3_with_if_phi::can_lower, lower: super::pattern3_with_if_phi::lower, }, LoopPatternEntry { name: "Pattern1_Minimal", detect: super::pattern1_minimal::can_lower, lower: super::pattern1_minimal::lower, }, LoopPatternEntry { name: "Pattern2_WithBreak", detect: super::pattern2_with_break::can_lower, lower: super::pattern2_with_break::lower, }, ]; /// Try all registered patterns in priority order. /// /// Returns Ok(Some(value_id)) if a pattern matched and lowered successfully. /// Returns Ok(None) if no pattern matched. /// Returns Err if a pattern matched but lowering failed. /// /// # Phase 183: Structure-based routing /// /// This router uses the centralized pattern classification system: /// - Pattern detection: `ctx.pattern_kind` (from `loop_pattern_detection::classify`) /// - No redundant pattern detection in detect functions /// - All patterns use structure-based classification pub(crate) fn route_loop_pattern( builder: &mut MirBuilder, ctx: &LoopPatternContext, ) -> Result, String> { use super::super::trace; // Phase 183: Route based on pre-classified pattern kind // Pattern kind was already determined by ctx.pattern_kind in LoopPatternContext::new() // This eliminates duplicate detection logic across routers. // Find matching pattern entry based on pattern_kind for entry in LOOP_PATTERNS { if (entry.detect)(builder, ctx) { // Phase 195: Use unified trace for pattern matching trace::trace().pattern("route", entry.name, true); return (entry.lower)(builder, ctx); } } // No pattern matched - return None (caller will handle error) // Phase 187-2: Legacy LoopBuilder removed, all loops must use JoinIR if ctx.debug { trace::trace().debug( "route", &format!( "No pattern matched for function '{}' (pattern_kind={:?})", ctx.func_name, ctx.pattern_kind ), ); } Ok(None) }