phase29ap(p7): remove pattern9 legacy routing

This commit is contained in:
2025-12-31 06:17:49 +09:00
parent 2ccc2c75e4
commit abff2ca682
7 changed files with 18 additions and 496 deletions

View File

@ -3,7 +3,7 @@
## Current Focus
- Phase: `docs/development/current/main/phases/phase-29ap/README.md`
- Next: Phase 29ap P7 (planned; see `docs/development/current/main/phases/phase-29ap/README.md`)
- Next: Phase 29ap P8 (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: P7 planned)
- Phase 29ap: `docs/development/current/main/phases/phase-29ap/README.md` (Next: P8 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

@ -34,7 +34,7 @@ Related:
## 1.1 Current (active)
- Active phase: `docs/development/current/main/phases/phase-29ap/README.md`
- Next step: Phase 29ap P7 (planned)
- Next step: Phase 29ap P8 (planned)
## 2. すでに固めた SSOT再発防止の土台

View File

@ -83,5 +83,14 @@ Gate (SSOT):
## Next (planned)
- P7: Pattern9 legacy table removal decision
- `docs/development/current/main/phases/phase-29ap/P7-PATTERN9-LEGACY-REMOVAL-DECISION-INSTRUCTIONS.md`
## P7: Pattern9 legacy table removal
- Scope:
- Pattern9 is not referenced by stdlib/quick/gate; only legacy pack SKIP remains.
- Removed Pattern9 from JoinIR legacy table (plan/composer remains SSOT).
- Guardrails:
- No change to logs or error strings.
## Next (planned)
- P8: Follow-up legacy table shrink or leave as-is with justification

View File

@ -82,7 +82,6 @@ pub(in crate::mir::builder) mod pattern4_carrier_analyzer;
pub(in crate::mir::builder) mod pattern4_with_continue;
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 pattern9_accum_const_loop; // Phase 270 P1: accumulator const loop (橋渡しパターン)
pub(in crate::mir::builder) mod pattern_pipeline;
pub(in crate::mir::builder) mod router;
pub(in crate::mir::builder) mod trim_loop_lowering; // Phase 180: Dedicated Trim/P5 lowering module

View File

@ -1,482 +0,0 @@
//! Phase 270 P1: Pattern 9 - Accumulator Const Loop (固定橋渡しパターン)
//!
//! **目的**: Phase 270 fixture (`phase270_p0_loop_min_const.hako`) を JoinIR 経路で通す
//!
//! **設計方針**:
//! - Pattern1 は test-only stub として保存(触らない)
//! - Pattern9 は Phase270 専用の最小固定パターン(汎用実装ではない)
//! - 将来 ExitKind+Frag に吸収される前提の橋渡しパターン
//!
//! ## 受理条件Fail-Fast 固定)
//!
//! 1. **ループ条件**: `i < <int literal>` のみ
//! 2. **ループ本体**: 代入2本のみ順序固定
//! - `sum = sum + i`
//! - `i = i + 1`
//! 3. **制御構文**: break/continue/return があれば `Ok(None)` でフォールバック
//! 4. **loop後**: `return sum`
//!
//! ## JoinIR 構造
//!
//! ```text
//! main(i_init, sum_init):
//! result = loop_step(i_init, sum_init)
//! return result
//!
//! loop_step(i, sum):
//! cond = (i < limit)
//! exit_cond = !cond
//! Jump(k_exit, [sum], cond=exit_cond)
//! sum_next = sum + i
//! i_next = i + 1
//! Call(loop_step, [i_next, sum_next]) // tail recursion
//!
//! k_exit(sum):
//! return sum
//! ```
use crate::ast::{ASTNode, BinaryOperator, LiteralValue};
use crate::mir::builder::MirBuilder;
use crate::mir::ValueId;
/// Phase 270 P1: Pattern 9 detection
///
/// **Fail-Fast 判定**: Phase270 fixture の形に厳密一致する場合のみ true
pub(crate) fn can_lower(builder: &MirBuilder, ctx: &super::router::LoopPatternContext) -> bool {
// Step 1: ループ条件チェック: i < <int literal>
let (loop_var, _limit) = match extract_loop_condition(ctx.condition) {
Some(result) => result,
None => return false,
};
// Step 2: ループ変数が variable_map に存在するか確認
if !builder.variable_ctx.variable_map.contains_key(&loop_var) {
return false;
}
// Step 3: ループ本体チェック: sum = sum + i; i = i + 1
let (_sum_var, _i_var) = match extract_loop_body_assignments(ctx.body, &loop_var) {
Some(result) => result,
None => return false,
};
// Step 4: break/continue/return があれば reject
if has_control_flow(ctx.body) {
return false;
}
// Pattern9 受理
true
}
/// Phase 270 P1: Pattern 9 lowering
///
/// **void stub 禁止**: JoinIR を生成して JoinIRConversionPipeline::execute へ渡す
pub(crate) fn lower(
builder: &mut MirBuilder,
ctx: &super::router::LoopPatternContext,
) -> Result<Option<ValueId>, String> {
// Step 1: 最終検証can_lower で漏れた条件確認)
if !can_lower(builder, ctx) {
return Ok(None); // Phase 263 PromoteDecision 思想: 他パターンへフォールバック
}
// Step 2: ループ条件とキャリア変数を抽出
let (loop_var, limit) = extract_loop_condition(ctx.condition)
.ok_or_else(|| "[pattern9] Failed to extract loop condition".to_string())?;
let (sum_var, _i_var) = extract_loop_body_assignments(ctx.body, &loop_var)
.ok_or_else(|| "[pattern9] Failed to extract body assignments".to_string())?;
// Step 3: variable_map から HOST ValueId を取得
let i_host_id = builder
.variable_ctx
.variable_map
.get(&loop_var)
.copied()
.ok_or_else(|| format!("[pattern9] Loop variable '{}' not found", loop_var))?;
let sum_host_id = builder
.variable_ctx
.variable_map
.get(&sum_var)
.copied()
.ok_or_else(|| format!("[pattern9] Accumulator variable '{}' not found", sum_var))?;
// Step 4: JoinIR lowerer を呼び出し
use crate::mir::join_ir::lowering::join_value_space::JoinValueSpace;
let mut join_value_space = JoinValueSpace::new();
let join_module = lower_accum_const_loop_joinir(limit, &mut join_value_space)?;
// Step 5: JoinInlineBoundary を構築
use crate::mir::join_ir::lowering::carrier_info::CarrierRole;
use crate::mir::join_ir::lowering::inline_boundary::LoopExitBinding;
use crate::mir::join_ir::lowering::JoinInlineBoundaryBuilder;
// k_exit のパラメータsum_exitを取得
let k_exit_func = join_module.require_function("k_exit", "Pattern 9");
let sum_exit_value = k_exit_func
.params
.first()
.copied()
.expect("[pattern9] k_exit must have parameter for sum");
// exit_bindings: sum のみi は捨てる)
let exit_binding = LoopExitBinding {
carrier_name: sum_var.clone(),
join_exit_value: sum_exit_value,
host_slot: sum_host_id,
role: CarrierRole::LoopState,
};
// main のパラメータi_init, sum_initを取得
let main_func = join_module.require_function("main", "Pattern 9");
let i_init_param = main_func
.params
.get(0)
.copied()
.expect("[pattern9] main must have i parameter");
let sum_init_param = main_func
.params
.get(1)
.copied()
.expect("[pattern9] main must have sum parameter");
let boundary = JoinInlineBoundaryBuilder::new()
.with_inputs(
vec![i_init_param, sum_init_param],
vec![i_host_id, sum_host_id],
)
.with_exit_bindings(vec![exit_binding])
.with_loop_var_name(Some(loop_var.clone()))
.build();
// Step 6: JoinIRConversionPipeline で MIR に変換
use super::conversion_pipeline::JoinIRConversionPipeline;
eprintln!("[pattern9] Lowering Phase270 fixture with Pattern9 (AccumConstLoop)");
let _ = JoinIRConversionPipeline::execute(
builder,
join_module,
Some(&boundary),
"pattern9",
ctx.debug,
)?;
// Phase 270 P1: Return Void (loop doesn't produce value)
let void_val = crate::mir::builder::emission::constant::emit_void(builder);
Ok(Some(void_val))
}
// ================================================================
// JoinIR Lowerer (Phase 270 P1 - 固定2キャリア: i, sum)
// ================================================================
/// Phase 270 P1: JoinIR lowerer for accumulator const loop
///
/// **構造**:
/// - main(i_init, sum_init) → loop_step(i, sum) → k_exit(sum)
/// - loop_step: 条件チェック → sum_next = sum + i → i_next = i + 1 → tail recursion
fn lower_accum_const_loop_joinir(
limit: i64,
join_value_space: &mut crate::mir::join_ir::lowering::join_value_space::JoinValueSpace,
) -> Result<crate::mir::join_ir::JoinModule, String> {
use crate::mir::join_ir::lowering::canonical_names as cn;
use crate::mir::join_ir::{
BinOpKind, CompareOp, ConstValue, JoinFuncId, JoinFunction, JoinInst, JoinModule,
MirLikeInst, UnaryOp,
};
let mut join_module = JoinModule::new();
// Function IDs
let main_id = JoinFuncId::new(0);
let loop_step_id = JoinFuncId::new(1);
let k_exit_id = JoinFuncId::new(2);
// ValueId allocation
// main() params/locals
let i_main_param = join_value_space.alloc_param(); // i_init
let sum_main_param = join_value_space.alloc_param(); // sum_init
let loop_result = join_value_space.alloc_local(); // result from loop_step
// loop_step params/locals
let i_step_param = join_value_space.alloc_param(); // i
let sum_step_param = join_value_space.alloc_param(); // sum
let const_limit = join_value_space.alloc_local(); // limit constant
let cmp_lt = join_value_space.alloc_local(); // i < limit
let exit_cond = join_value_space.alloc_local(); // !(i < limit)
let sum_next = join_value_space.alloc_local(); // sum + i
let const_1 = join_value_space.alloc_local(); // increment constant
let i_next = join_value_space.alloc_local(); // i + 1
// k_exit params
let sum_exit_param = join_value_space.alloc_param(); // sum
// ================================================================
// main(i_init, sum_init) function
// ================================================================
let mut main_func = JoinFunction::new(
main_id,
"main".to_string(),
vec![i_main_param, sum_main_param],
);
// result = loop_step(i_main_param, sum_main_param)
main_func.body.push(JoinInst::Call {
func: loop_step_id,
args: vec![i_main_param, sum_main_param],
k_next: None,
dst: Some(loop_result),
});
// return result
main_func.body.push(JoinInst::Ret {
value: Some(loop_result),
});
join_module.add_function(main_func);
// ================================================================
// loop_step(i, sum) function
// ================================================================
let mut loop_step_func = JoinFunction::new(
loop_step_id,
cn::LOOP_STEP.to_string(),
vec![i_step_param, sum_step_param],
);
// cond = (i < limit)
loop_step_func
.body
.push(JoinInst::Compute(MirLikeInst::Const {
dst: const_limit,
value: ConstValue::Integer(limit),
}));
loop_step_func
.body
.push(JoinInst::Compute(MirLikeInst::Compare {
dst: cmp_lt,
op: CompareOp::Lt,
lhs: i_step_param,
rhs: const_limit,
}));
// exit_cond = !cond
loop_step_func
.body
.push(JoinInst::Compute(MirLikeInst::UnaryOp {
dst: exit_cond,
op: UnaryOp::Not,
operand: cmp_lt,
}));
// Jump(k_exit, [sum], cond=exit_cond)
loop_step_func.body.push(JoinInst::Jump {
cont: k_exit_id.as_cont(),
args: vec![sum_step_param],
cond: Some(exit_cond),
});
// sum_next = sum + i
loop_step_func
.body
.push(JoinInst::Compute(MirLikeInst::BinOp {
dst: sum_next,
op: BinOpKind::Add,
lhs: sum_step_param,
rhs: i_step_param,
}));
// i_next = i + 1
loop_step_func
.body
.push(JoinInst::Compute(MirLikeInst::Const {
dst: const_1,
value: ConstValue::Integer(1),
}));
loop_step_func
.body
.push(JoinInst::Compute(MirLikeInst::BinOp {
dst: i_next,
op: BinOpKind::Add,
lhs: i_step_param,
rhs: const_1,
}));
// Call(loop_step, [i_next, sum_next]) - tail recursion
loop_step_func.body.push(JoinInst::Call {
func: loop_step_id,
args: vec![i_next, sum_next],
k_next: None,
dst: None,
});
join_module.add_function(loop_step_func);
// ================================================================
// k_exit(sum) function
// ================================================================
let mut k_exit_func = JoinFunction::new(k_exit_id, cn::K_EXIT.to_string(), vec![sum_exit_param]);
// return sum
k_exit_func.body.push(JoinInst::Ret {
value: Some(sum_exit_param),
});
join_module.add_function(k_exit_func);
// Set entry point
join_module.entry = Some(main_id);
eprintln!("[joinir/pattern9] Generated JoinIR for AccumConstLoop Pattern");
eprintln!("[joinir/pattern9] Functions: main, loop_step, k_exit");
Ok(join_module)
}
// ================================================================
// Helper Functions (Fail-Fast 検証)
// ================================================================
/// ループ条件を抽出: `i < <int literal>` のみ受理
///
/// Returns: Some((loop_var, limit)) or None
fn extract_loop_condition(condition: &ASTNode) -> Option<(String, i64)> {
match condition {
ASTNode::BinaryOp {
operator: BinaryOperator::Less,
left,
right,
..
} => {
// left: Variable
let loop_var = match &**left {
ASTNode::Variable { name, .. } => name.clone(),
_ => return None,
};
// right: Literal::Integer
let limit = match &**right {
ASTNode::Literal {
value: LiteralValue::Integer(val),
..
} => *val,
_ => return None,
};
Some((loop_var, limit))
}
_ => None,
}
}
/// ループ本体の代入を抽出: `sum = sum + i; i = i + 1` のみ受理
///
/// Returns: Some((sum_var, i_var)) or None
fn extract_loop_body_assignments(body: &[ASTNode], expected_i: &str) -> Option<(String, String)> {
// 厳密に2つの代入のみ
if body.len() != 2 {
return None;
}
// 1つ目: sum = sum + i
let sum_var = match &body[0] {
ASTNode::Assignment { target, value, .. } => {
let sum_name = match target.as_ref() {
ASTNode::Variable { name, .. } => name.clone(),
_ => return None,
};
// value: sum + i
match value.as_ref() {
ASTNode::BinaryOp {
operator: BinaryOperator::Add,
left,
right,
..
} => {
// left: sum
let left_var = match left.as_ref() {
ASTNode::Variable { name, .. } => name,
_ => return None,
};
if left_var != &sum_name {
return None;
}
// right: i
let right_var = match right.as_ref() {
ASTNode::Variable { name, .. } => name,
_ => return None,
};
if right_var != expected_i {
return None;
}
sum_name
}
_ => return None,
}
}
_ => return None,
};
// 2つ目: i = i + 1
match &body[1] {
ASTNode::Assignment { target, value, .. } => {
let i_name = match target.as_ref() {
ASTNode::Variable { name, .. } => name,
_ => return None,
};
if i_name != expected_i {
return None;
}
// value: i + 1
match value.as_ref() {
ASTNode::BinaryOp {
operator: BinaryOperator::Add,
left,
right,
..
} => {
// left: i
let left_var = match left.as_ref() {
ASTNode::Variable { name, .. } => name,
_ => return None,
};
if left_var != expected_i {
return None;
}
// right: 1
match right.as_ref() {
ASTNode::Literal {
value: LiteralValue::Integer(1),
..
} => {}
_ => return None,
}
}
_ => return None,
}
}
_ => return None,
}
Some((sum_var, expected_i.to_string()))
}
/// break/continue/return の有無をチェック
fn has_control_flow(body: &[ASTNode]) -> bool {
body.iter().any(|stmt| {
matches!(
stmt,
ASTNode::Break { .. } | ASTNode::Continue { .. } | ASTNode::Return { .. }
)
})
}

View File

@ -222,7 +222,7 @@ pub(crate) struct LoopPatternEntry {
///
/// **IMPORTANT**: Patterns are tried in array order (SSOT).
/// Array order defines priority - earlier entries are tried first.
/// Pattern6_NestedLoopMinimal → Pattern4 → Pattern9
/// Pattern6_NestedLoopMinimal → Pattern4
///
/// # Current Patterns (Structure-based detection, established Phase 131-11+)
///
@ -241,6 +241,7 @@ pub(crate) struct LoopPatternEntry {
/// 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
@ -256,11 +257,6 @@ pub(crate) static LOOP_PATTERNS: &[LoopPatternEntry] = &[
// 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
LoopPatternEntry {
name: "Pattern9_AccumConstLoop", // Phase 270 P1: accumulator const loop (橋渡しパターン, before P1)
detect: super::pattern9_accum_const_loop::can_lower,
lower: super::pattern9_accum_const_loop::lower,
},
];
/// Try all registered patterns in priority order.
@ -281,7 +277,7 @@ pub(crate) static LOOP_PATTERNS: &[LoopPatternEntry] = &[
/// 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 Pattern4/9 table for other patterns
/// 3. Fall through to legacy Pattern4 table for other patterns
///
/// The Plan line (Extractor → Normalizer → Verifier → Lowerer) is the
/// current operational SSOT for Pattern6/7. Legacy patterns (1/2/4/8/9) use
@ -296,7 +292,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)
/// - Pattern4/9: src/mir/builder/control_flow/joinir/patterns/pattern*.rs (direct lowering)
/// - Pattern4: src/mir/builder/control_flow/joinir/patterns/pattern*.rs (direct lowering)
/// - Pattern6_NestedLoopMinimal: src/mir/builder/control_flow/joinir/patterns/pattern6_nested_minimal.rs
pub(crate) fn route_loop_pattern(
builder: &mut MirBuilder,