phase29ap(p12): remove joinir legacy loop table

This commit is contained in:
2025-12-31 08:07:58 +09:00
parent 1ceadf361f
commit b53c12eb29
9 changed files with 36 additions and 350 deletions

View File

@ -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)

View File

@ -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`

View File

@ -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.

View File

@ -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再発防止の土台

View File

@ -27,6 +27,7 @@ Goal: JoinIR の最小回帰セットを SSOT として固定する。
- Pattern7 (release adopt, VM): `phase29ao_pattern7_release_adopt_vm`
- Pattern7: `phase29ab_pattern7_*`
- この pack が JoinIR 回帰の唯一の integration gatephase143_* は対象外)
- 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 側で SKIPphase29ae pack には含めない)
- shadow adopt tag`[coreplan/shadow_adopt:*]`)は `filter_noise` で除去される

View File

@ -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)

View File

@ -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;

View File

@ -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<Option<ValueId>, 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))
}

View File

@ -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<Option<ValueId>, 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<Option<ValueId>, 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(