phase29ap(p0): reduce legacy extractors (pattern1/3)
This commit is contained in:
@ -2,8 +2,8 @@
|
||||
|
||||
## Current Focus
|
||||
|
||||
- Phase: `docs/development/current/main/phases/phase-29ao/README.md` (closeout)
|
||||
- Next: Phase 29ap (planned; see `docs/development/current/main/phases/phase-29ao/README.md`)
|
||||
- Phase: `docs/development/current/main/phases/phase-29ap/README.md`
|
||||
- Next: Phase 29ap P1 (planned; see `docs/development/current/main/phases/phase-29ap/README.md`)
|
||||
|
||||
## Gate (SSOT)
|
||||
|
||||
|
||||
@ -5,7 +5,7 @@ Scope: 「次にやる候補」を短く列挙するメモ。入口は `docs/dev
|
||||
|
||||
## Active
|
||||
|
||||
- Phase 29ap (planned): legacy削減の段階計画(extractor/router)+ composer SSOT
|
||||
- Phase 29ap: `docs/development/current/main/phases/phase-29ap/README.md` (Next: P1 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`
|
||||
|
||||
|
||||
@ -33,8 +33,8 @@ Related:
|
||||
|
||||
## 1.1 Current (active)
|
||||
|
||||
- Active phase: `docs/development/current/main/phases/phase-29ao/README.md`
|
||||
- Next step: Phase 29ap (planned; P55 done)
|
||||
- Active phase: `docs/development/current/main/phases/phase-29ap/README.md`
|
||||
- Next step: Phase 29ap P1 (planned)
|
||||
|
||||
## 2. すでに固めた SSOT(再発防止の土台)
|
||||
|
||||
|
||||
32
docs/development/current/main/phases/phase-29ap/README.md
Normal file
32
docs/development/current/main/phases/phase-29ap/README.md
Normal file
@ -0,0 +1,32 @@
|
||||
---
|
||||
Status: Active
|
||||
Scope: Legacy extractor reduction (planner+composer SSOT)
|
||||
Related:
|
||||
- docs/development/current/main/design/coreplan-migration-roadmap-ssot.md
|
||||
- docs/development/current/main/design/coreplan-migration-done-criteria-ssot.md
|
||||
- docs/development/current/main/design/coreloop-composer-v0-v1-boundary-ssot.md
|
||||
- docs/development/current/main/phases/phase-29ae/README.md
|
||||
---
|
||||
|
||||
# Phase 29ap: Legacy extractor reduction (Step-E)
|
||||
|
||||
Goal: Reduce legacy extractor fallbacks while keeping planner+composer as the SSOT path.
|
||||
|
||||
Gate (SSOT):
|
||||
- `./tools/smokes/v2/profiles/integration/joinir/phase29ae_regression_pack_vm.sh`
|
||||
|
||||
## P0: Reduce legacy extractors (Pattern1/3) ✅
|
||||
|
||||
- Scope:
|
||||
- Remove Pattern1/3 extractor fallbacks in `single_planner`.
|
||||
- Remove Pattern3 JoinIR router entry so plan line is the only path.
|
||||
- Pattern1 JoinIR entry is retained for now (stdlib loops still rely on it).
|
||||
- Guardrails:
|
||||
- Outcome remains `Ok(None)` for non-matching cases.
|
||||
- No new logs or error strings.
|
||||
|
||||
## Next (planned)
|
||||
|
||||
- P1: Pattern6/7 JoinIR wrapper → composer entry only
|
||||
- P2: Router pattern-name branching reduction (planner outcome + composer SSOT)
|
||||
- P3: Facts/Feature expansion if needed
|
||||
@ -180,7 +180,7 @@ fn lower_via_plan(
|
||||
/// - Cons: Extraction can be expensive (but amortized over lowering)
|
||||
///
|
||||
/// ## StructureBased (Feature Classification)
|
||||
/// - Used by: Pattern1, Pattern2, Pattern3, Pattern4, Pattern5, Pattern8, Pattern9
|
||||
/// - Used by: Pattern1, Pattern2, Pattern4 (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)
|
||||
@ -201,7 +201,7 @@ pub(crate) enum CanLowerStrategy {
|
||||
ExtractionBased,
|
||||
|
||||
/// Structure-based detection: Check pattern_kind from LoopPatternContext
|
||||
/// Used by Pattern1, Pattern2, Pattern3, Pattern4, Pattern5, Pattern8, Pattern9
|
||||
/// Used by Pattern1, Pattern2, Pattern4
|
||||
StructureBased,
|
||||
}
|
||||
|
||||
@ -222,26 +222,20 @@ pub(crate) struct LoopPatternEntry {
|
||||
///
|
||||
/// **IMPORTANT**: Patterns are tried in array order (SSOT).
|
||||
/// Array order defines priority - earlier entries are tried first.
|
||||
/// Pattern4 → Pattern8 → Pattern9 → Pattern3 → Pattern1 → Pattern2
|
||||
/// Pattern4 → Pattern8 → Pattern9 → Pattern1 → Pattern2
|
||||
///
|
||||
/// # Current Patterns (Structure-based detection, established Phase 131-11+)
|
||||
///
|
||||
/// Pattern detection strategies (updated Phase 286):
|
||||
/// - Pattern6/7: ExtractionBased (Plan line, Phase 273+)
|
||||
/// - Pattern5/8/9: ExtractionBased (extraction functions, Plan line Phase 286+)
|
||||
/// - Pattern1-4: StructureBased (LoopFeatures classification, legacy)
|
||||
/// - Pattern8/9: ExtractionBased (extraction functions, Plan line Phase 286+)
|
||||
/// - Pattern1/2/4: StructureBased (LoopFeatures classification, legacy)
|
||||
///
|
||||
/// - 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
|
||||
/// - Pattern3: moved to plan-based routing (planner+composer SSOT)
|
||||
///
|
||||
/// - Pattern 2: Loop with Conditional Break (joinir_min_loop.hako)
|
||||
/// - Detection: pattern_kind == Pattern2Break
|
||||
@ -274,11 +268,6 @@ pub(crate) static LOOP_PATTERNS: &[LoopPatternEntry] = &[
|
||||
detect: super::pattern9_accum_const_loop::can_lower,
|
||||
lower: super::pattern9_accum_const_loop::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,
|
||||
@ -302,17 +291,17 @@ pub(crate) static LOOP_PATTERNS: &[LoopPatternEntry] = &[
|
||||
/// 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 (Pattern1-5): ctx.pattern_kind classification (legacy)
|
||||
/// - Structure-based (Pattern1/2/4): 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 Pattern1-5 table for other patterns
|
||||
/// 3. Fall through to legacy Pattern1/2/4/8/9 table for other patterns
|
||||
///
|
||||
/// The Plan line (Extractor → Normalizer → Verifier → Lowerer) is the
|
||||
/// current operational SSOT for Pattern6/7. Legacy patterns (1-5) use
|
||||
/// current operational SSOT for Pattern6/7. Legacy patterns (1/2/4/8/9) use
|
||||
/// the traditional LoopPatternContext-based routing.
|
||||
///
|
||||
/// Plan-based architecture (Phase 273 P1-P3):
|
||||
@ -324,7 +313,7 @@ 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)
|
||||
/// - Pattern1-5: src/mir/builder/control_flow/joinir/patterns/pattern*.rs (direct lowering)
|
||||
/// - Pattern1/2/4/8/9: src/mir/builder/control_flow/joinir/patterns/pattern*.rs (direct lowering)
|
||||
pub(crate) fn route_loop_pattern(
|
||||
builder: &mut MirBuilder,
|
||||
ctx: &LoopPatternContext,
|
||||
|
||||
@ -5,7 +5,6 @@ use crate::ast::{ASTNode, BinaryOperator};
|
||||
|
||||
// Phase 282 P9a: Use common_helpers
|
||||
use super::common_helpers::has_control_flow_statement as common_has_control_flow;
|
||||
use crate::mir::builder::control_flow::plan::policies::pattern1_subset_policy::is_pattern1_step_only_body;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct Pattern1Parts {
|
||||
@ -165,82 +164,6 @@ fn has_control_flow_statement(body: &[ASTNode]) -> bool {
|
||||
common_has_control_flow(body)
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Phase 286 P2.1: Pattern1 → Plan/Frag SSOT extractor
|
||||
// ============================================================================
|
||||
|
||||
/// Phase 286 P2.1: Minimal subset extractor for Pattern1 Plan line
|
||||
///
|
||||
/// Supported subset (PoC safety - Compare + Add only):
|
||||
/// - Loop condition: `<var> < <int_lit>`
|
||||
/// - Loop increment: `<var> = <var> + <int_lit>`
|
||||
///
|
||||
/// Returns Ok(None) for unsupported patterns → legacy fallback
|
||||
pub(crate) fn extract_pattern1_plan(
|
||||
condition: &ASTNode,
|
||||
body: &[ASTNode],
|
||||
) -> Result<Option<crate::mir::builder::control_flow::plan::DomainPlan>, String> {
|
||||
use crate::mir::builder::control_flow::plan::{DomainPlan, Pattern1SimpleWhilePlan};
|
||||
|
||||
// Step 1: Validate via existing extractor
|
||||
let parts = extract_simple_while_parts(condition, body)?;
|
||||
if parts.is_none() {
|
||||
return Ok(None); // Not Pattern1 → legacy fallback
|
||||
}
|
||||
let parts = parts.unwrap();
|
||||
|
||||
// Step 2: Validate loop condition is `<var> < <int_lit>`
|
||||
if !validate_loop_condition_plan(condition, &parts.loop_var) {
|
||||
return Ok(None); // Unsupported condition format
|
||||
}
|
||||
|
||||
// Step 3: Extract loop increment `<var> = <var> + <int_lit>`
|
||||
// Phase 286 P2.2: Use common helper from common_helpers
|
||||
let loop_increment = match super::common_helpers::extract_loop_increment_plan(body, &parts.loop_var)? {
|
||||
Some(inc) => inc,
|
||||
None => return Ok(None), // No loop increment found
|
||||
};
|
||||
|
||||
if !is_pattern1_step_only_body(body, &parts.loop_var) {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
Ok(Some(DomainPlan::Pattern1SimpleWhile(Pattern1SimpleWhilePlan {
|
||||
loop_var: parts.loop_var,
|
||||
condition: condition.clone(),
|
||||
loop_increment,
|
||||
})))
|
||||
}
|
||||
|
||||
/// Validate loop condition: supports `<var> < <int_lit>` only
|
||||
fn validate_loop_condition_plan(cond: &ASTNode, loop_var: &str) -> bool {
|
||||
use crate::ast::LiteralValue;
|
||||
|
||||
if let ASTNode::BinaryOp { operator, left, right, .. } = cond {
|
||||
if !matches!(operator, BinaryOperator::Less) {
|
||||
return false; // Only < supported for PoC
|
||||
}
|
||||
|
||||
// Left must be the loop variable
|
||||
if let ASTNode::Variable { name, .. } = left.as_ref() {
|
||||
if name != loop_var {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Right must be integer literal
|
||||
if !matches!(right.as_ref(), ASTNode::Literal { value: LiteralValue::Integer(_), .. }) {
|
||||
return false;
|
||||
}
|
||||
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
@ -468,97 +468,3 @@ mod tests {
|
||||
assert!(result.unwrap().is_none()); // Has break → Not Pattern3
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Phase 286 P2.6: Pattern3 → Plan/Frag SSOT extractor
|
||||
// ============================================================================
|
||||
|
||||
/// Phase 286 P2.6: Extract Pattern3 (Loop with If-Phi) Plan
|
||||
///
|
||||
/// Leverages existing `extract_loop_with_if_phi_parts()` for validation,
|
||||
/// then extracts detailed AST components for Plan normalization.
|
||||
///
|
||||
/// # Returns
|
||||
/// - Ok(Some(DomainPlan::Pattern3IfPhi)) if Pattern3 match confirmed
|
||||
/// - Ok(None) if not Pattern3 (structural mismatch)
|
||||
/// - Err if malformed AST (rare)
|
||||
pub(crate) fn extract_pattern3_plan(
|
||||
condition: &ASTNode,
|
||||
body: &[ASTNode],
|
||||
) -> Result<Option<crate::mir::builder::control_flow::plan::DomainPlan>, String> {
|
||||
use crate::mir::builder::control_flow::plan::{DomainPlan, Pattern3IfPhiPlan};
|
||||
|
||||
// Step 1: Validate via existing extractor
|
||||
let parts = extract_loop_with_if_phi_parts(condition, body)?;
|
||||
if parts.is_none() {
|
||||
return Ok(None); // Not Pattern3 → fallback
|
||||
}
|
||||
let parts = parts.unwrap();
|
||||
|
||||
// Step 2: Extract if-else statement (validated to exist)
|
||||
let if_stmt = super::common_helpers::find_if_else_statement(body)
|
||||
.ok_or_else(|| "Pattern3: if-else statement not found after validation".to_string())?;
|
||||
|
||||
// Step 3: Extract if condition and branch updates
|
||||
let (if_condition, then_update, else_update) = match if_stmt {
|
||||
ASTNode::If {
|
||||
condition: if_cond,
|
||||
then_body,
|
||||
else_body: Some(else_body),
|
||||
..
|
||||
} => {
|
||||
// Extract carrier update from then branch
|
||||
let then_update = extract_single_update(then_body, &parts.merged_var)?;
|
||||
|
||||
// Extract carrier update from else branch
|
||||
let else_update = extract_single_update(else_body, &parts.merged_var)?;
|
||||
|
||||
(if_cond.as_ref().clone(), then_update, else_update)
|
||||
}
|
||||
_ => {
|
||||
return Err("Pattern3: if-else structure mismatch after validation".to_string());
|
||||
}
|
||||
};
|
||||
|
||||
// Step 4: Extract loop increment
|
||||
let loop_increment = match super::common_helpers::extract_loop_increment_plan(body, &parts.loop_var)? {
|
||||
Some(inc) => inc,
|
||||
None => {
|
||||
return Err(format!(
|
||||
"Pattern3: loop increment not found for variable '{}'",
|
||||
parts.loop_var
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
Ok(Some(DomainPlan::Pattern3IfPhi(Pattern3IfPhiPlan {
|
||||
loop_var: parts.loop_var,
|
||||
carrier_var: parts.merged_var,
|
||||
condition: condition.clone(),
|
||||
if_condition,
|
||||
then_update,
|
||||
else_update,
|
||||
loop_increment,
|
||||
})))
|
||||
}
|
||||
|
||||
/// Extract update expression from a branch body
|
||||
///
|
||||
/// Expects a single assignment: `carrier = <expr>`
|
||||
/// Returns the RHS expression AST
|
||||
fn extract_single_update(body: &[ASTNode], carrier_var: &str) -> Result<ASTNode, String> {
|
||||
for stmt in body {
|
||||
if let ASTNode::Assignment { target, value, .. } = stmt {
|
||||
if let ASTNode::Variable { name, .. } = target.as_ref() {
|
||||
if name == carrier_var {
|
||||
return Ok(value.as_ref().clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Err(format!(
|
||||
"Pattern3: carrier update not found for variable '{}' in branch",
|
||||
carrier_var
|
||||
))
|
||||
}
|
||||
|
||||
@ -96,11 +96,11 @@ fn fallback_extract(
|
||||
allow_pattern8: bool,
|
||||
) -> Result<Option<DomainPlan>, String> {
|
||||
match kind {
|
||||
PlanRuleId::Pattern1 => extractors::pattern1::extract_pattern1_plan(ctx.condition, ctx.body),
|
||||
PlanRuleId::Pattern1 => Ok(None),
|
||||
PlanRuleId::Pattern2 => {
|
||||
extractors::pattern2_break::extract_pattern2_plan(ctx.condition, ctx.body)
|
||||
}
|
||||
PlanRuleId::Pattern3 => extractors::pattern3::extract_pattern3_plan(ctx.condition, ctx.body),
|
||||
PlanRuleId::Pattern3 => Ok(None),
|
||||
PlanRuleId::Pattern4 => extractors::pattern4::extract_pattern4_plan(ctx.condition, ctx.body),
|
||||
PlanRuleId::Pattern5 => extractors::pattern5::extract_pattern5_plan(ctx.condition, ctx.body),
|
||||
PlanRuleId::Pattern6 => {
|
||||
|
||||
Reference in New Issue
Block a user