diff --git a/docs/development/current/main/10-Now.md b/docs/development/current/main/10-Now.md index f4008b1d..f1060acb 100644 --- a/docs/development/current/main/10-Now.md +++ b/docs/development/current/main/10-Now.md @@ -3,7 +3,7 @@ ## Current Focus - Phase: `docs/development/current/main/phases/phase-29ap/README.md` -- Next: Phase 29ap P12 (planned; see `docs/development/current/main/phases/phase-29ap/README.md`) +- Next: Phase 29ap P13 (planned; see `docs/development/current/main/phases/phase-29ap/README.md`) ## Gate (SSOT) diff --git a/docs/development/current/main/30-Backlog.md b/docs/development/current/main/30-Backlog.md index f4a14ace..9a24536e 100644 --- a/docs/development/current/main/30-Backlog.md +++ b/docs/development/current/main/30-Backlog.md @@ -5,7 +5,7 @@ Scope: 「次にやる候補」を短く列挙するメモ。入口は `docs/dev ## Active -- Phase 29ap: `docs/development/current/main/phases/phase-29ap/README.md` (Next: P12 planned) +- Phase 29ap: `docs/development/current/main/phases/phase-29ap/README.md` (Next: P13 planned) - JoinIR regression gate SSOT: `docs/development/current/main/phases/phase-29ae/README.md` - CorePlan hardening (docs-first): `docs/development/current/main/phases/phase-29al/README.md` diff --git a/docs/development/current/main/design/coreplan-migration-done-criteria-ssot.md b/docs/development/current/main/design/coreplan-migration-done-criteria-ssot.md index 314f5b9e..3497cd84 100644 --- a/docs/development/current/main/design/coreplan-migration-done-criteria-ssot.md +++ b/docs/development/current/main/design/coreplan-migration-done-criteria-ssot.md @@ -6,7 +6,7 @@ This document defines when the CorePlan migration can be considered done. - Release adopt gate patterns execute via CorePlan without strict/dev flags. - Gate smokes cover the release paths and remain green. -- The legacy path remains only as a fallback for non-gate cases. +- JoinIR legacy loop table is removed; routing is plan/composer only. ## Stage-3 Done (composer v0/v1) @@ -17,5 +17,5 @@ This document defines when the CorePlan migration can be considered done. ## Still allowed to remain - `DomainPlan` can remain as an intent layer for normalization/compatibility. -- Legacy extractors and router branches remain until Phase 29ap removes them. - +- Legacy extractors may remain in the plan layer as long as they do not reintroduce + JoinIR-side routing tables. diff --git a/docs/development/current/main/design/coreplan-migration-roadmap-ssot.md b/docs/development/current/main/design/coreplan-migration-roadmap-ssot.md index a34d0075..b32b31fe 100644 --- a/docs/development/current/main/design/coreplan-migration-roadmap-ssot.md +++ b/docs/development/current/main/design/coreplan-migration-roadmap-ssot.md @@ -34,7 +34,7 @@ Related: ## 1.1 Current (active) - Active phase: `docs/development/current/main/phases/phase-29ap/README.md` -- Next step: Phase 29ap P12 (planned) +- Next step: Phase 29ap P13 (planned) ## 2. すでに固めた SSOT(再発防止の土台) diff --git a/docs/development/current/main/phases/phase-29ae/README.md b/docs/development/current/main/phases/phase-29ae/README.md index c1c08395..bb5bfb56 100644 --- a/docs/development/current/main/phases/phase-29ae/README.md +++ b/docs/development/current/main/phases/phase-29ae/README.md @@ -27,6 +27,7 @@ Goal: JoinIR の最小回帰セットを SSOT として固定する。 - Pattern7 (release adopt, VM): `phase29ao_pattern7_release_adopt_vm` - Pattern7: `phase29ab_pattern7_*` - この pack が JoinIR 回帰の唯一の integration gate(phase143_* は対象外) +- JoinIR routing is plan/composer SSOT only (legacy loop table removed in Phase 29ap P12) - phase143_* は LoopBuilder 撤去 / plugin disable 固定 / LLVM exe 期待が古いので除外 - phase286_pattern9_* は plugins disabled 経路の mismatch があるため legacy pack 側で SKIP(phase29ae pack には含めない) - shadow adopt tag(`[coreplan/shadow_adopt:*]`)は `filter_noise` で除去される diff --git a/docs/development/current/main/phases/phase-29ap/README.md b/docs/development/current/main/phases/phase-29ap/README.md index c1c7a948..b549fc32 100644 --- a/docs/development/current/main/phases/phase-29ap/README.md +++ b/docs/development/current/main/phases/phase-29ap/README.md @@ -128,6 +128,15 @@ Gate (SSOT): - No new logs/tags in release. - Gate stays green. +## P12: Remove JoinIR legacy loop table ✅ + +- Scope: + - Delete legacy loop table routing from JoinIR router. + - Remove legacy-only modules now that plan/composer is SSOT. +- Guardrails: + - No change to logs or error strings. + - Gate stays green. + ## Next (planned) -- P12: Decide final removal of legacy nested loop entry (gate-first) +- P13: Dead-code cleanup (warnings-only, no behavior change) diff --git a/src/mir/builder/control_flow/joinir/patterns/mod.rs b/src/mir/builder/control_flow/joinir/patterns/mod.rs index ad2e6fe4..7c2ad26c 100644 --- a/src/mir/builder/control_flow/joinir/patterns/mod.rs +++ b/src/mir/builder/control_flow/joinir/patterns/mod.rs @@ -5,10 +5,9 @@ //! - Pattern 3: Loop with If-Else PHI (pattern3_with_if_phi.rs) //! - Pattern 4: Loop with Continue (migrated to plan routing in Phase 29ap P8) //! -//! Phase 194: Table-driven router for pattern dispatch -//! - Router module provides table-driven pattern matching -//! - Each pattern exports can_lower() and lower() functions -//! - See router.rs for how to add new patterns +//! Phase 29ap P12: Router delegates to plan/composer SSOT (legacy table removed) +//! - Router only coordinates planner/composer adoption +//! - Pattern-specific logic lives in plan layer //! //! Phase 193: AST Feature Extraction Modularization //! - ast_feature_extractor.rs: Pure function module for analyzing loop AST @@ -77,7 +76,6 @@ pub(in crate::mir::builder) mod exit_binding_validator; // Phase 222.5-C pub(in crate::mir::builder) mod loop_scope_shape_builder; pub(in crate::mir::builder) mod pattern1_minimal; pub(in crate::mir::builder) mod pattern3_with_if_phi; -pub(in crate::mir::builder) mod pattern6_nested_minimal; // Phase 188.3: 1-level nested loop (Pattern1 outer + Pattern1 inner) pub(in crate::mir::builder) mod pattern8_scan_bool_predicate; // Phase 259 P0: boolean predicate scan (is_integer/is_valid) pub(in crate::mir::builder) mod pattern_pipeline; pub(in crate::mir::builder) mod router; diff --git a/src/mir/builder/control_flow/joinir/patterns/pattern6_nested_minimal.rs b/src/mir/builder/control_flow/joinir/patterns/pattern6_nested_minimal.rs deleted file mode 100644 index 9b82f410..00000000 --- a/src/mir/builder/control_flow/joinir/patterns/pattern6_nested_minimal.rs +++ /dev/null @@ -1,211 +0,0 @@ -//! Phase 188.3: Pattern6 NestedLoopMinimal - 1-level nested loop lowering -//! -//! Handles loops of the form: -//! ```nyash -//! loop(outer_cond) { -//! loop(inner_cond) { -//! // inner body -//! } -//! // outer body (after inner loop) -//! } -//! ``` -//! -//! Requirements (Pattern1 for both): -//! - Outer loop: no break, no continue (Pattern1) -//! - Inner loop: no break, no continue (Pattern1) -//! - Exactly 1 inner loop -//! - max_loop_depth == 2 -//! -//! Strategy: -//! - Generate outer_step continuation (contains inner loop call) -//! - Generate inner_step continuation (tail recursion) -//! - Generate k_inner_exit (bridges to outer continuation) -//! - Wire continuations - -use crate::ast::ASTNode; -use crate::mir::builder::control_flow::joinir::patterns::LoopPatternContext; -use crate::mir::builder::MirBuilder; -use crate::mir::loop_pattern_detection::LoopPatternKind; -use crate::mir::ValueId; - -/// Phase 188.3: Validate strict mode constraints -/// -/// Requirements (most already checked in is_pattern6_lowerable): -/// - Inner loop has no break (checked in routing.rs) -/// - Inner loop has no continue (checked in routing.rs) -/// - Outer loop has no break (checked in routing.rs) -/// - Outer loop has no continue (checked in routing.rs) -/// -/// Additional checks (Phase 188.4+): -/// - Outer variable write-back detection (TODO: add to is_pattern6_lowerable) -/// -/// This function serves as final safety check before lowering. -/// If validation fails → Fail-Fast (not silent fallback). -fn validate_strict_mode(_inner_ast: &ASTNode, _ctx: &LoopPatternContext) -> Result<(), String> { - // Phase 188.3: Most validation already done in is_pattern6_lowerable() - // This is a placeholder for future strict mode checks - - // Future Phase 188.4+ checks: - // - Detect outer variable write-back in inner loop body - // - Validate carrier compatibility - // - Check for unsupported AST patterns - - Ok(()) -} - -/// Phase 188.3: Extract inner loop AST from outer loop body -/// -/// Requirements: -/// - Outer body must contain exactly 1 Loop node -/// - 0 loops → Error (shouldn't happen after Pattern6 selection) -/// - 2+ loops → Error (multiple inner loops not supported) -/// -/// Returns reference to inner loop ASTNode -fn extract_inner_loop_ast<'a>(ctx: &'a LoopPatternContext) -> Result<&'a ASTNode, String> { - // Find all Loop nodes in outer body - let mut inner_loop: Option<&ASTNode> = None; - let mut loop_count = 0; - - for stmt in ctx.body.iter() { - if matches!(stmt, ASTNode::Loop { .. }) { - loop_count += 1; - if inner_loop.is_none() { - inner_loop = Some(stmt); - } - } - } - - // Validate exactly 1 inner loop - match loop_count { - 0 => Err( - "[Pattern6/extract] No inner loop found (should not happen after Pattern6 selection)" - .to_string(), - ), - 1 => Ok(inner_loop.unwrap()), // Safe: loop_count == 1 guarantees Some - _ => Err(format!( - "[Pattern6/extract] Multiple inner loops ({}) not supported. \ - Phase 188.3 supports exactly 1 inner loop only.", - loop_count - )), - } -} - -/// Detect if this context can be lowered as Pattern6 (NestedLoopMinimal) -/// -/// Pattern selection happens in choose_pattern_kind() (SSOT). -/// This function just verifies ctx.pattern_kind matches. -pub(crate) fn can_lower(_builder: &MirBuilder, ctx: &LoopPatternContext) -> bool { - ctx.pattern_kind == LoopPatternKind::Pattern6NestedLoopMinimal -} - -/// Lower Pattern6 (NestedLoopMinimal) to MIR -/// -/// Phase 188.3 P1: Full implementation with JoinIR pipeline -pub(crate) fn lower( - builder: &mut MirBuilder, - ctx: &LoopPatternContext, -) -> Result, String> { - use super::super::trace; - - // Phase 3-1: Extract inner loop AST (validate exactly 1 inner loop) - let _inner_ast = extract_inner_loop_ast(ctx)?; - - // Phase 3-2: Validate strict mode constraints (Fail-Fast) - validate_strict_mode(_inner_ast, ctx)?; - - // Phase 3-3: Full implementation with JoinIR pipeline - trace::trace().debug("pattern6", "Calling Pattern 6 nested loop minimal lowerer"); - - // Build preprocessing context - Pattern6 shares Pattern1's infrastructure - use super::pattern_pipeline::{build_pattern_context, PatternVariant}; - let pattern_ctx = build_pattern_context(builder, ctx.condition, ctx.body, PatternVariant::Pattern1)?; - - trace::trace().varmap("pattern6_start", &builder.variable_ctx.variable_map); - - // Create JoinValueSpace for unified ValueId allocation - use crate::mir::join_ir::lowering::join_value_space::JoinValueSpace; - let mut join_value_space = JoinValueSpace::new(); - - // Call Pattern 6 lowerer with preprocessed scope - use crate::mir::join_ir::lowering::nested_loop_minimal::lower_nested_loop_minimal; - let join_module = match lower_nested_loop_minimal(pattern_ctx.loop_scope, &mut join_value_space) { - Some(module) => module, - None => { - trace::trace().debug("pattern6", "Pattern 6 lowerer returned None"); - return Ok(None); - } - }; - - // Create boundary from context - // Phase 188.3: Critical - must include continuation_func_ids to prevent merge misdetection - use crate::mir::join_ir::lowering::carrier_info::CarrierRole; - use crate::mir::join_ir::lowering::inline_boundary::LoopExitBinding; - use crate::mir::join_ir::lowering::join_value_space::PARAM_MIN; - use crate::mir::join_ir::lowering::JoinInlineBoundaryBuilder; - use crate::mir::join_ir::lowering::nested_loop_minimal::{INNER_STEP, K_INNER_EXIT}; - - // Extract k_exit's parameter ValueId from join_module - let k_exit_func = join_module.require_function("k_exit", "Pattern 6"); - let sum_exit_value = k_exit_func - .params - .first() - .copied() - .expect("k_exit must have parameter for exit value (sum)"); - - // Phase 188.3: Extract variables from variable_map - let sum_var_id = builder - .variable_ctx - .variable_map - .get("sum") - .copied() - .ok_or_else(|| "[Pattern6] sum variable not found in variable_map".to_string())?; - - let i_var_id = pattern_ctx.loop_var_id; - - // Create exit binding for sum (the final return value) - let sum_exit_binding = LoopExitBinding { - carrier_name: "sum".to_string(), - join_exit_value: sum_exit_value, - host_slot: sum_var_id, - role: CarrierRole::LoopState, - }; - - // Phase 188.3 CRITICAL: Include continuation_func_ids to prevent merge from - // selecting inner_step as loop header - use std::collections::BTreeSet; - let continuation_funcs = BTreeSet::from([ - "k_exit".to_string(), - K_INNER_EXIT.to_string(), - INNER_STEP.to_string(), - ]); - - let boundary = JoinInlineBoundaryBuilder::new() - .with_inputs( - vec![ValueId(PARAM_MIN), ValueId(PARAM_MIN + 1)], // main(i0, sum0) - vec![i_var_id, sum_var_id], // host variables - ) - .with_exit_bindings(vec![sum_exit_binding]) - .with_loop_var_name(Some(pattern_ctx.loop_var_name.clone())) // Phase 188.3: Enables header PHI for 'i' - .with_continuation_funcs(continuation_funcs) - .build(); - - // Use JoinIRConversionPipeline for unified conversion flow - use super::conversion_pipeline::JoinIRConversionPipeline; - let _ = JoinIRConversionPipeline::execute( - builder, - join_module, - Some(&boundary), - "pattern6", - ctx.debug, - )?; - - // Return Void (loops don't produce values) - let void_val = crate::mir::builder::emission::constant::emit_void(builder); - - trace::trace().debug( - "pattern6", - &format!("Nested loop complete, returning Void {:?}", void_val), - ); - - Ok(Some(void_val)) -} diff --git a/src/mir/builder/control_flow/joinir/patterns/router.rs b/src/mir/builder/control_flow/joinir/patterns/router.rs index 7dccf5cc..f8d0c109 100644 --- a/src/mir/builder/control_flow/joinir/patterns/router.rs +++ b/src/mir/builder/control_flow/joinir/patterns/router.rs @@ -1,22 +1,19 @@ -//! Pattern Router - Table-driven dispatch for loop patterns +//! Pattern Router - Plan/Composer routing for loop patterns //! -//! Phase 194: Replace if/else chain with table-driven routing +//! Phase 29ap P12: Legacy loop table removed (plan/composer SSOT only) //! //! # 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 +//! - single_planner derives a DomainPlan + facts outcome (SSOT) +//! - composer adopts CorePlan (strict/dev shadow or release adopt) +//! - PlanLowerer emits MIR from CorePlan (emit_frag SSOT) //! //! # 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. +//! 1. Add Facts/Planner extraction in plan layer +//! 2. Normalize/verify in plan normalizer/verifier +//! 3. Compose CorePlan in composer (shadow/release adopt as needed) +//! 4. Keep router unchanged (it only delegates to plan/composer) use crate::ast::ASTNode; use crate::mir::builder::MirBuilder; @@ -169,109 +166,16 @@ fn lower_via_plan( // Phase 29ai P5: Plan extractor routing moved to `plan::single_planner`. -/// Phase 272 P0.2 Refactoring: can_lower() strategy classification +/// Route loop patterns via plan/composer SSOT. /// -/// Clarifies the two main detection strategies used across patterns: +/// Returns Ok(Some(value_id)) if a plan matched and lowered successfully. +/// Returns Ok(None) if no plan matched. +/// Returns Err if a plan matched but lowering failed. /// -/// ## ExtractionBased (SSOT Approach) -/// - Used by: Pattern6, Pattern7 -/// - Strategy: Try pattern extraction, if successful → match -/// - Pros: Single source of truth (extract function defines pattern) -/// - Cons: Extraction can be expensive (but amortized over lowering) +/// # Router Architecture (Plan/Composer SSOT) /// -/// ## StructureBased (Feature Classification) -/// - Used by: Pattern6_NestedLoopMinimal (legacy) -/// - Strategy: Check pattern_kind (from LoopPatternContext), plus optional structural checks -/// - Pros: Fast classification, reuses centralized feature detection -/// - Cons: Two sources of truth (classify + structural checks) -/// -/// ## Rationale for Dual Strategy: -/// - Pattern6/7: Complex extraction logic (variable step, carrier tracking) -/// → ExtractionBased avoids duplication between detect and extract -/// - Other patterns: Legacy structural features -/// → StructureBased leverages centralized LoopFeatures classification -/// -/// This documentation prevents bugs like Phase 272 P0.2's Pattern7 issue -/// (pattern_kind check was too restrictive, extraction-based approach fixed it). -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -#[allow(dead_code)] // Documentation purpose - not enforced in code yet -pub(crate) enum CanLowerStrategy { - /// Extraction-based detection: Try extract(), success → match - /// Used by Pattern6, Pattern7 - ExtractionBased, - - /// Structure-based detection: Check pattern_kind from LoopPatternContext - /// Used by legacy patterns - StructureBased, -} - -/// 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. -/// Pattern6_NestedLoopMinimal (legacy) -/// -/// # Current Patterns (Structure-based detection, established Phase 131-11+) -/// -/// Pattern detection strategies (updated Phase 286): -/// - Pattern6/7: ExtractionBased (Plan line, Phase 273+) -/// - Pattern8/9: ExtractionBased (extraction functions, Plan line Phase 286+) -/// -/// - Pattern3: moved to plan-based routing (planner+composer SSOT) -/// -/// Note: func_name is now only used for debug logging, not pattern detection -/// Phase 286: Pattern5 removed (migrated to Plan-based routing) -/// Phase 29ap P4: Pattern8 removed (migrated to Plan-based routing) -/// Phase 29ap P5: Pattern2 removed (migrated to Plan-based routing) -/// Phase 29ap P7: Pattern9 removed (migrated to Plan-based routing) -pub(crate) static LOOP_PATTERNS: &[LoopPatternEntry] = &[ - LoopPatternEntry { - name: "Pattern6_NestedLoopMinimal", // Phase 188.3: 1-level nested loop - detect: super::pattern6_nested_minimal::can_lower, - lower: super::pattern6_nested_minimal::lower, - }, - // Phase 273 P0.1: Pattern6 entry removed (migrated to Plan-based routing) - // Pattern6_ScanWithInit now handled via extract_scan_with_init_plan() + PlanLowerer - // Phase 273 P2: Pattern7 entry removed (migrated to Plan-based routing) - // Pattern7_SplitScan now handled via extract_split_scan_plan() + PlanLowerer -]; - -/// 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. -/// -/// # Router Architecture (Structure-based routing established Phase 183) -/// -/// This router uses multiple detection strategies: -/// - Plan-based (Pattern6/7): extract_*_plan() → DomainPlan (Phase 273+ SSOT) -/// - Extraction-based (Pattern8/9): extract_*() functions (already implemented) -/// - Structure-based (Pattern6_NestedLoopMinimal): ctx.pattern_kind classification (legacy) -/// -/// # Plan Line SSOT for Pattern6/7 (Phase 273+) -/// -/// This function implements the following routing strategy: -/// 1. Try Plan-based Pattern6 (extract_scan_with_init_plan) → DomainPlan -/// 2. Try Plan-based Pattern7 (extract_split_scan_plan) → DomainPlan -/// 3. Fall through to legacy Pattern6_NestedLoopMinimal table for other patterns -/// -/// The Plan line (Extractor → Normalizer → Verifier → Lowerer) is the -/// current operational SSOT for Pattern6/7. Legacy patterns -/// (Pattern6_NestedLoopMinimal) use the traditional LoopPatternContext-based routing. +/// The plan line (Extractor → Normalizer → Verifier → Lowerer) is the +/// operational SSOT for loop routing (Phase 273+). /// /// Plan-based architecture (Phase 273 P1-P3): /// - extract_*_plan() → DomainPlan (pure extraction, no builder) @@ -282,7 +186,6 @@ pub(crate) static LOOP_PATTERNS: &[LoopPatternEntry] = &[ /// SSOT Entry Points: /// - Pattern6: src/mir/builder/control_flow/plan/normalizer.rs (ScanWithInit normalization) /// - Pattern7: src/mir/builder/control_flow/plan/normalizer.rs (SplitScan normalization) -/// - Pattern6_NestedLoopMinimal: src/mir/builder/control_flow/joinir/patterns/pattern6_nested_minimal.rs pub(crate) fn route_loop_pattern( builder: &mut MirBuilder, ctx: &LoopPatternContext, @@ -344,20 +247,6 @@ pub(crate) fn route_loop_pattern( return lower_via_plan(builder, domain_plan, ctx); } - // 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 - // Phase 273 P0.1: Pattern6 skip logic removed (entry no longer in LOOP_PATTERNS) - for entry in LOOP_PATTERNS { - if (entry.detect)(builder, ctx) { - let log_msg = format!("route=joinir strategy=extract pattern={} (Phase 194+)", entry.name); - trace::trace().pattern("route", &log_msg, true); - return (entry.lower)(builder, ctx); - } - } - // No pattern matched - return None (caller will handle error) if ctx.debug { trace::trace().debug(