From e5045c40ecdbd392283c8d3c476386c55a71321c Mon Sep 17 00:00:00 2001 From: tomoaki Date: Tue, 30 Dec 2025 20:36:12 +0900 Subject: [PATCH] phase29ao(p48): compose pattern2 break via v1 value-join --- docs/development/current/main/10-Now.md | 2 +- docs/development/current/main/30-Backlog.md | 2 +- .../design/coreplan-migration-roadmap-ssot.md | 2 +- .../current/main/phases/phase-29ao/README.md | 5 + .../plan/composer/coreloop_gates.rs | 8 + .../control_flow/plan/composer/coreloop_v1.rs | 204 +++++++++++++++++- .../plan/composer/shadow_adopt.rs | 14 +- .../plan/normalizer/pattern2_break.rs | 2 +- 8 files changed, 225 insertions(+), 14 deletions(-) diff --git a/docs/development/current/main/10-Now.md b/docs/development/current/main/10-Now.md index 178986e0..05998f20 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-29ao/README.md` -- Next: TBD (P47 done; see `docs/development/current/main/phases/phase-29ao/README.md`) +- Next: TBD (P48 done; see `docs/development/current/main/phases/phase-29ao/README.md`) ## Gate (SSOT) diff --git a/docs/development/current/main/30-Backlog.md b/docs/development/current/main/30-Backlog.md index c5b39a8b..7cd5c086 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 -- CorePlan migration: `docs/development/current/main/phases/phase-29ao/README.md`(Next: TBD, P47 done) +- CorePlan migration: `docs/development/current/main/phases/phase-29ao/README.md`(Next: TBD, P48 done) ## Near-Term Candidates 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 c0f2598c..8fd57ee8 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-29ao/README.md` -- Next step: TBD (P47 done) +- Next step: TBD (P48 done) ## 2. すでに固めた SSOT(再発防止の土台) diff --git a/docs/development/current/main/phases/phase-29ao/README.md b/docs/development/current/main/phases/phase-29ao/README.md index 19ee8bb9..49e0ea14 100644 --- a/docs/development/current/main/phases/phase-29ao/README.md +++ b/docs/development/current/main/phases/phase-29ao/README.md @@ -261,6 +261,11 @@ Gate(SSOT): - 指示書: `docs/development/current/main/phases/phase-29ao/P47-CORELOOPCOMPOSER-V1-SPLITSCAN-VALUEJOIN-INSTRUCTIONS.md` - ねらい: Pattern7 value-join を v1 で受理し、block_params/EdgeArgs 経由の PHI 一本化を維持(仕様不変) +## P48: CoreLoopComposer v1 — Pattern2 (Break) value-join minimal composition ✅ + +- 指示書: `docs/development/current/main/phases/phase-29ao/P48-CORELOOPCOMPOSER-V1-PATTERN2-BREAK-VALUEJOIN-INSTRUCTIONS.md` +- ねらい: Pattern2 after-join を v1 で受理し、block_params/EdgeArgs 経由の PHI 一本化を維持(仕様不変) + ## Next(planned) - Next: TBD diff --git a/src/mir/builder/control_flow/plan/composer/coreloop_gates.rs b/src/mir/builder/control_flow/plan/composer/coreloop_gates.rs index 726d958b..fe9c69dd 100644 --- a/src/mir/builder/control_flow/plan/composer/coreloop_gates.rs +++ b/src/mir/builder/control_flow/plan/composer/coreloop_gates.rs @@ -7,6 +7,10 @@ pub(super) fn coreloop_base_gate(facts: &CanonicalLoopFacts) -> bool { && facts.cleanup_kinds_present.is_empty() } +pub(super) fn coreloop_value_join_gate(facts: &CanonicalLoopFacts) -> bool { + coreloop_base_gate(facts) && facts.value_join_needed +} + pub(super) fn exit_kinds_allow_return_only(facts: &CanonicalLoopFacts) -> bool { facts.exit_kinds_present.is_empty() || (facts.exit_kinds_present.len() == 1 @@ -16,3 +20,7 @@ pub(super) fn exit_kinds_allow_return_only(facts: &CanonicalLoopFacts) -> bool { pub(super) fn exit_kinds_empty(facts: &CanonicalLoopFacts) -> bool { facts.exit_kinds_present.is_empty() } + +pub(super) fn pattern2_value_join_gate(facts: &CanonicalLoopFacts) -> bool { + coreloop_value_join_gate(facts) && exit_kinds_allow_return_only(facts) +} diff --git a/src/mir/builder/control_flow/plan/composer/coreloop_v1.rs b/src/mir/builder/control_flow/plan/composer/coreloop_v1.rs index 154b1380..e7fee178 100644 --- a/src/mir/builder/control_flow/plan/composer/coreloop_v1.rs +++ b/src/mir/builder/control_flow/plan/composer/coreloop_v1.rs @@ -1,23 +1,32 @@ use crate::mir::builder::control_flow::joinir::patterns::router::LoopPatternContext; use crate::mir::builder::control_flow::plan::composer::coreloop_gates::{ - coreloop_base_gate, exit_kinds_allow_return_only, + coreloop_value_join_gate, exit_kinds_allow_return_only, pattern2_value_join_gate, }; use crate::mir::builder::control_flow::plan::facts::scan_shapes::SplitScanShape; use crate::mir::builder::control_flow::plan::normalize::CanonicalLoopFacts; use crate::mir::builder::control_flow::plan::normalizer::PlanNormalizer; -use crate::mir::builder::control_flow::plan::{CorePlan, SplitScanPlan}; +use crate::mir::builder::control_flow::plan::{ + CorePlan, Pattern2BreakPlan, Pattern2PromotionHint, SplitScanPlan, +}; use crate::mir::builder::MirBuilder; -#[allow(dead_code)] pub(in crate::mir::builder) fn try_compose_core_loop_v1( builder: &mut MirBuilder, facts: &CanonicalLoopFacts, ctx: &LoopPatternContext, ) -> Result, String> { - if !coreloop_base_gate(facts) { - return Ok(None); + if let Some(core) = try_compose_core_loop_v1_pattern2_break(builder, facts, ctx)? { + return Ok(Some(core)); } - if !facts.value_join_needed { + try_compose_core_loop_v1_split_scan(builder, facts, ctx) +} + +fn try_compose_core_loop_v1_split_scan( + builder: &mut MirBuilder, + facts: &CanonicalLoopFacts, + ctx: &LoopPatternContext, +) -> Result, String> { + if !coreloop_value_join_gate(facts) { return Ok(None); } @@ -43,14 +52,48 @@ pub(in crate::mir::builder) fn try_compose_core_loop_v1( Ok(Some(core)) } +pub(in crate::mir::builder) fn try_compose_core_loop_v1_pattern2_break( + builder: &mut MirBuilder, + facts: &CanonicalLoopFacts, + ctx: &LoopPatternContext, +) -> Result, String> { + if !pattern2_value_join_gate(facts) { + return Ok(None); + } + + let Some(pattern2) = facts.facts.pattern2_break.as_ref() else { + return Ok(None); + }; + let promotion = facts + .facts + .pattern2_loopbodylocal + .as_ref() + .map(|facts| Pattern2PromotionHint::LoopBodyLocal(facts.clone())); + + let plan = Pattern2BreakPlan { + loop_var: pattern2.loop_var.clone(), + carrier_var: pattern2.carrier_var.clone(), + loop_condition: pattern2.loop_condition.clone(), + break_condition: pattern2.break_condition.clone(), + carrier_update_in_break: pattern2.carrier_update_in_break.clone(), + carrier_update_in_body: pattern2.carrier_update_in_body.clone(), + loop_increment: pattern2.loop_increment.clone(), + promotion, + }; + let core = PlanNormalizer::normalize_pattern2_break(builder, plan, ctx)?; + Ok(Some(core)) +} + #[cfg(test)] mod tests { use super::try_compose_core_loop_v1; - use crate::ast::{ASTNode, LiteralValue, Span}; + use crate::ast::{ASTNode, BinaryOperator, LiteralValue, Span}; use crate::mir::builder::control_flow::plan::facts::feature_facts::{ - ExitKindFacts, ExitMapFacts, LoopFeatureFacts, ValueJoinFacts, + CleanupFacts, CleanupKindFacts, ExitKindFacts, ExitMapFacts, LoopFeatureFacts, + ValueJoinFacts, }; use crate::mir::builder::control_flow::plan::facts::loop_facts::LoopFacts; + use crate::mir::builder::control_flow::plan::facts::pattern2_break_facts::Pattern2BreakFacts; use crate::mir::builder::control_flow::plan::facts::scan_shapes::{ ConditionShape, SplitScanShape, StepShape, }; @@ -63,6 +106,20 @@ mod tests { use crate::mir::MirType; use std::collections::BTreeSet; + fn v(name: &str) -> ASTNode { + ASTNode::Variable { + name: name.to_string(), + span: Span::unknown(), + } + } + + fn lit_int(value: i64) -> ASTNode { + ASTNode::Literal { + value: LiteralValue::Integer(value), + span: Span::unknown(), + } + } + #[test] fn coreloop_v1_composes_split_scan_with_value_join() { let condition = ASTNode::Literal { @@ -185,4 +242,135 @@ mod tests { try_compose_core_loop_v1(&mut builder, &canonical, &ctx).expect("Ok"); assert!(composed.is_none()); } + + #[test] + fn coreloop_v1_composes_pattern2_with_value_join() { + let loop_condition = ASTNode::BinaryOp { + operator: BinaryOperator::Less, + left: Box::new(v("i")), + right: Box::new(lit_int(3)), + span: Span::unknown(), + }; + let break_condition = ASTNode::BinaryOp { + operator: BinaryOperator::Equal, + left: Box::new(v("i")), + right: Box::new(lit_int(1)), + span: Span::unknown(), + }; + let carrier_update_in_body = ASTNode::BinaryOp { + operator: BinaryOperator::Add, + left: Box::new(v("sum")), + right: Box::new(lit_int(1)), + span: Span::unknown(), + }; + let loop_increment = ASTNode::BinaryOp { + operator: BinaryOperator::Add, + left: Box::new(v("i")), + right: Box::new(lit_int(1)), + span: Span::unknown(), + }; + + let features = LoopFeatureFacts { + value_join: Some(ValueJoinFacts { needed: true }), + ..LoopFeatureFacts::default() + }; + let facts = LoopFacts { + condition_shape: ConditionShape::Unknown, + step_shape: StepShape::Unknown, + skeleton: SkeletonFacts { + kind: SkeletonKind::Loop, + }, + features, + scan_with_init: None, + split_scan: None, + pattern1_simplewhile: None, + pattern3_ifphi: None, + pattern4_continue: None, + pattern5_infinite_early_exit: None, + pattern8_bool_predicate_scan: None, + pattern9_accum_const_loop: None, + pattern2_break: Some(Pattern2BreakFacts { + loop_var: "i".to_string(), + carrier_var: "sum".to_string(), + loop_condition: loop_condition.clone(), + break_condition, + carrier_update_in_break: None, + carrier_update_in_body, + loop_increment, + }), + pattern2_loopbodylocal: None, + }; + let canonical = canonicalize_loop_facts(facts); + let mut builder = MirBuilder::new(); + builder.enter_function_for_test("coreloop_v1_pattern2".to_string()); + let i_val = builder.alloc_typed(MirType::Integer); + let sum_val = builder.alloc_typed(MirType::Integer); + builder + .variable_ctx + .variable_map + .insert("i".to_string(), i_val); + builder + .variable_ctx + .variable_map + .insert("sum".to_string(), sum_val); + let ctx = + LoopPatternContext::new(&loop_condition, &[], "coreloop_v1_pattern2", false, false); + let composed = + try_compose_core_loop_v1(&mut builder, &canonical, &ctx).expect("Ok"); + assert!(matches!(composed, Some(crate::mir::builder::control_flow::plan::CorePlan::Loop(_)))); + } + + #[test] + fn coreloop_v1_rejects_pattern2_with_cleanup() { + let condition = ASTNode::Literal { + value: LiteralValue::Bool(true), + span: Span::unknown(), + }; + let mut kinds_present = BTreeSet::new(); + kinds_present.insert(CleanupKindFacts::Return); + let features = LoopFeatureFacts { + cleanup: Some(CleanupFacts { kinds_present }), + value_join: Some(ValueJoinFacts { needed: true }), + ..LoopFeatureFacts::default() + }; + let facts = LoopFacts { + condition_shape: ConditionShape::Unknown, + step_shape: StepShape::Unknown, + skeleton: SkeletonFacts { + kind: SkeletonKind::Loop, + }, + features, + scan_with_init: None, + split_scan: None, + pattern1_simplewhile: None, + pattern3_ifphi: None, + pattern4_continue: None, + pattern5_infinite_early_exit: None, + pattern8_bool_predicate_scan: None, + pattern9_accum_const_loop: None, + pattern2_break: Some(Pattern2BreakFacts { + loop_var: "i".to_string(), + carrier_var: "sum".to_string(), + loop_condition: condition.clone(), + break_condition: condition.clone(), + carrier_update_in_break: None, + carrier_update_in_body: lit_int(0), + loop_increment: lit_int(0), + }), + pattern2_loopbodylocal: None, + }; + let canonical = canonicalize_loop_facts(facts); + let mut builder = MirBuilder::new(); + builder.enter_function_for_test("coreloop_v1_pattern2_cleanup".to_string()); + let ctx = LoopPatternContext::new( + &condition, + &[], + "coreloop_v1_pattern2_cleanup", + false, + false, + ); + let composed = + try_compose_core_loop_v1(&mut builder, &canonical, &ctx).expect("Ok"); + assert!(composed.is_none()); + } } diff --git a/src/mir/builder/control_flow/plan/composer/shadow_adopt.rs b/src/mir/builder/control_flow/plan/composer/shadow_adopt.rs index 211be4c3..88979243 100644 --- a/src/mir/builder/control_flow/plan/composer/shadow_adopt.rs +++ b/src/mir/builder/control_flow/plan/composer/shadow_adopt.rs @@ -4,6 +4,7 @@ use super::coreloop_v0::{ try_compose_core_loop_v0_scan_with_init, try_compose_core_loop_v0_split_scan, }; use super::coreloop_v1::try_compose_core_loop_v1; +use super::coreloop_v1::try_compose_core_loop_v1_pattern2_break; use super::PlanNormalizer; use crate::mir::builder::control_flow::joinir::patterns::router::LoopPatternContext; use crate::mir::builder::control_flow::plan::normalize::CanonicalLoopFacts; @@ -131,7 +132,12 @@ pub(in crate::mir::builder) fn try_release_adopt_core_plan_for_pattern2_break_su return Ok(None); } - match compose_coreplan_for_pattern2_break_subset(builder, facts, ctx) { + let composed = if facts.value_join_needed { + try_compose_core_loop_v1_pattern2_break(builder, facts, ctx) + } else { + compose_coreplan_for_pattern2_break_subset(builder, facts, ctx) + }; + match composed { Ok(Some(core)) => Ok(Some(core)), Ok(None) | Err(_) => Ok(None), } @@ -410,7 +416,11 @@ pub(in crate::mir::builder) fn try_shadow_adopt_core_plan( if facts.facts.pattern2_break.is_none() { return Err("pattern2 strict/dev adopt failed: facts mismatch".to_string()); } - let core_plan = compose_coreplan_for_pattern2_break_subset(builder, facts, ctx)? + let core_plan = if facts.value_join_needed { + try_compose_core_loop_v1_pattern2_break(builder, facts, ctx)? + } else { + compose_coreplan_for_pattern2_break_subset(builder, facts, ctx)? + } .ok_or_else(|| "pattern2 strict/dev adopt failed: compose rejected".to_string())?; Ok(Some(ShadowAdoptOutcome { core_plan, diff --git a/src/mir/builder/control_flow/plan/normalizer/pattern2_break.rs b/src/mir/builder/control_flow/plan/normalizer/pattern2_break.rs index 1f1d7f11..0e1e18be 100644 --- a/src/mir/builder/control_flow/plan/normalizer/pattern2_break.rs +++ b/src/mir/builder/control_flow/plan/normalizer/pattern2_break.rs @@ -30,7 +30,7 @@ impl super::PlanNormalizer { /// ``` /// /// Key: after_bb PHI merges break path and natural exit path carrier values. - pub(super) fn normalize_pattern2_break( + pub(in crate::mir::builder) fn normalize_pattern2_break( builder: &mut MirBuilder, parts: Pattern2BreakPlan, ctx: &LoopPatternContext,