diff --git a/src/mir/builder/control_flow/joinir/patterns/ast_feature_extractor.rs b/src/mir/builder/control_flow/joinir/patterns/ast_feature_extractor.rs index db5f1378..7c93ffa7 100644 --- a/src/mir/builder/control_flow/joinir/patterns/ast_feature_extractor.rs +++ b/src/mir/builder/control_flow/joinir/patterns/ast_feature_extractor.rs @@ -90,6 +90,8 @@ pub fn extract_features( carrier_count, break_count: if has_break { 1 } else { 0 }, continue_count: if has_continue { 1 } else { 0 }, + // Phase 170-C-2b: AST-based extraction doesn't have carrier names yet + update_summary: None, } } diff --git a/src/mir/join_ir/lowering/loop_scope_shape/case_a_lowering_shape.rs b/src/mir/join_ir/lowering/loop_scope_shape/case_a_lowering_shape.rs index c6278ba4..34f0d965 100644 --- a/src/mir/join_ir/lowering/loop_scope_shape/case_a_lowering_shape.rs +++ b/src/mir/join_ir/lowering/loop_scope_shape/case_a_lowering_shape.rs @@ -202,6 +202,68 @@ impl CaseALoweringShape { } } + /// Phase 170-C-2b: LoopUpdateSummary を使った精度向上版 + /// + /// `features.update_summary` から UpdateKind を取得して判定。 + /// これにより、carrier 名を直接見るコードを CaseALoweringShape から排除できる。 + /// + /// # Arguments + /// * `features` - LoopFeatures (update_summary 含む) + /// * `carrier_count` - Number of carrier variables + /// * `has_progress_carrier` - Whether progress carrier exists + /// + /// # Returns + /// CaseALoweringShape classification based on UpdateSummary + pub fn detect_with_updates( + features: &crate::mir::loop_pattern_detection::LoopFeatures, + carrier_count: usize, + has_progress_carrier: bool, + ) -> Self { + use crate::mir::join_ir::lowering::loop_update_summary::UpdateKind; + + // Case-A requirement: must have a progress carrier + if !has_progress_carrier { + return CaseALoweringShape::NotCaseA; + } + + // Case-A requirement: no complex control flow (continue) + if features.has_continue { + return CaseALoweringShape::NotCaseA; + } + + // Phase 170-C-2b: Use UpdateSummary if available + if let Some(ref summary) = features.update_summary { + // Single carrier with CounterLike update → StringExamination + if summary.carriers.len() == 1 + && summary.carriers[0].kind == UpdateKind::CounterLike + { + return CaseALoweringShape::StringExamination; + } + + // Any AccumulationLike carrier → ArrayAccumulation (for single carrier) + // or IterationWithAccumulation (for multiple carriers) + if summary.carriers.iter().any(|c| c.kind == UpdateKind::AccumulationLike) { + if carrier_count == 1 { + return CaseALoweringShape::ArrayAccumulation; + } else { + return CaseALoweringShape::IterationWithAccumulation; + } + } + + // Multiple carriers without clear accumulation → IterationWithAccumulation + if carrier_count >= 2 { + return CaseALoweringShape::IterationWithAccumulation; + } + } + + // Fallback to carrier count heuristic + match carrier_count { + 0 => CaseALoweringShape::NotCaseA, + 1 => CaseALoweringShape::Generic, + 2.. => CaseALoweringShape::IterationWithAccumulation, + } + } + /// Typical index variable name detection /// /// StringExamination パターン(skip/trim)で使われる典型的なインデックス名: @@ -229,6 +291,12 @@ impl CaseALoweringShape { let has_progress_carrier = scope.progress_carrier.is_some(); let carrier_count = scope.carriers.len(); + // Phase 170-C-2b: Build update_summary from carrier names + // Note: carriers is BTreeSet, so each item is already a String + let carrier_names: Vec = scope.carriers.iter().cloned().collect(); + let update_summary = + crate::mir::join_ir::lowering::loop_update_summary::analyze_loop_updates(&carrier_names); + // Create stub features (Phase 170-B will use real LoopFeatures) let stub_features = crate::mir::loop_pattern_detection::LoopFeatures { has_break: false, // Unknown from LoopScopeShape alone @@ -238,9 +306,10 @@ impl CaseALoweringShape { carrier_count, break_count: 0, continue_count: 0, + update_summary: Some(update_summary), }; - Self::detect_from_features(&stub_features, carrier_count, has_progress_carrier) + Self::detect_with_updates(&stub_features, carrier_count, has_progress_carrier) } /// Is this a recognized lowering shape? diff --git a/src/mir/join_ir/lowering/loop_to_join.rs b/src/mir/join_ir/lowering/loop_to_join.rs index 32c8a58b..e6a37927 100644 --- a/src/mir/join_ir/lowering/loop_to_join.rs +++ b/src/mir/join_ir/lowering/loop_to_join.rs @@ -29,6 +29,7 @@ use crate::mir::control_form::{analyze_exits, ExitEdge, LoopControlShape, LoopId use crate::mir::join_ir::lowering::generic_case_a; use crate::mir::join_ir::lowering::loop_form_intake::intake_loop_form; use crate::mir::join_ir::lowering::loop_scope_shape::{CaseALoweringShape, LoopScopeShape}; +use crate::mir::join_ir::lowering::loop_update_summary; // Phase 170-C-2b use crate::mir::join_ir::JoinModule; use crate::mir::loop_form::LoopForm; // Phase 48-2: Trio imports removed - now internal to LoopScopeShape::from_loop_form() @@ -383,11 +384,12 @@ impl LoopToJoinLowerer { // This enables gradual migration: existing name-based code continues to work, // while new patterns can be added purely by structure detection. - // Phase 170-C-1: Use carrier name-based detection for improved accuracy - // Extract progress carrier name from scope - let progress_carrier_name = scope.progress_carrier.as_deref(); + // Phase 170-C-2b: Build update_summary from carrier names + // Note: carriers is BTreeSet, so each item is already a String + let carrier_names: Vec = scope.carriers.iter().cloned().collect(); + let update_summary = loop_update_summary::analyze_loop_updates(&carrier_names); - // Construct minimal LoopFeatures from scope (for now) + // Construct LoopFeatures with update_summary // TODO: Pass real LoopFeatures from LoopPatternDetection when available let stub_features = crate::mir::loop_pattern_detection::LoopFeatures { has_break: false, // Unknown at this level @@ -397,25 +399,25 @@ impl LoopToJoinLowerer { carrier_count: scope.carriers.len(), break_count: 0, continue_count: 0, + update_summary: Some(update_summary), }; let has_progress_carrier = scope.progress_carrier.is_some(); let carrier_count = scope.carriers.len(); - // Phase 170-C-1: Use new carrier name-based detection - let shape = CaseALoweringShape::detect_with_carrier_name( + // Phase 170-C-2b: Use new UpdateSummary-based detection + let shape = CaseALoweringShape::detect_with_updates( &stub_features, carrier_count, has_progress_carrier, - progress_carrier_name, ); if self.debug { eprintln!( - "[LoopToJoinLowerer] Phase 170-C-1: shape={:?}, name={:?}, carrier={:?}", + "[LoopToJoinLowerer] Phase 170-C-2b: shape={:?}, name={:?}, carriers={:?}", shape.name(), name, - progress_carrier_name + carrier_names ); } diff --git a/src/mir/loop_pattern_detection.rs b/src/mir/loop_pattern_detection.rs index 88954fbf..6873ba47 100644 --- a/src/mir/loop_pattern_detection.rs +++ b/src/mir/loop_pattern_detection.rs @@ -136,6 +136,13 @@ pub struct LoopFeatures { /// Number of continue targets pub continue_count: usize, + + /// Phase 170-C-2b: Carrier update pattern summary + /// + /// Contains UpdateKind (CounterLike/AccumulationLike/Other) for each carrier. + /// Used by CaseALoweringShape for more precise shape detection. + /// None if carrier names are not available. + pub update_summary: Option, } impl LoopFeatures { @@ -208,6 +215,13 @@ pub fn extract_features(loop_form: &LoopForm, scope: Option<&LoopScopeShape>) -> // For now, we infer it from carrier_count > 1 (Pattern 3 heuristic) let has_if = has_if_else_phi; + // Phase 170-C-2b: Build update_summary from carrier names + // Note: carriers is BTreeSet, so each item is already a String + let update_summary = scope.map(|s| { + let carrier_names: Vec = s.carriers.iter().cloned().collect(); + crate::mir::join_ir::lowering::loop_update_summary::analyze_loop_updates(&carrier_names) + }); + LoopFeatures { has_break, has_continue, @@ -216,6 +230,7 @@ pub fn extract_features(loop_form: &LoopForm, scope: Option<&LoopScopeShape>) -> carrier_count, break_count, continue_count, + update_summary, } }