From 85103dd40ce58a00cb39e01d67837245fab7c4e6 Mon Sep 17 00:00:00 2001 From: tomoaki Date: Tue, 30 Dec 2025 20:52:21 +0900 Subject: [PATCH] phase29ao(p50): compose pattern3 if-phi 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 | 4 + .../control_flow/plan/composer/coreloop_v1.rs | 173 +++++++++++++++++- .../plan/composer/shadow_adopt.rs | 18 +- .../plan/normalizer/pattern3_if_phi.rs | 2 +- 8 files changed, 197 insertions(+), 11 deletions(-) diff --git a/docs/development/current/main/10-Now.md b/docs/development/current/main/10-Now.md index 75f058b9..fd6dfab2 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 (P49 done; see `docs/development/current/main/phases/phase-29ao/README.md`) +- Next: TBD (P50 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 38fd8afa..1e5a019d 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, P49 done) +- CorePlan migration: `docs/development/current/main/phases/phase-29ao/README.md`(Next: TBD, P50 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 9ed85369..db42ee75 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 (P49 done) +- Next step: TBD (P50 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 a05df1ae..f891600d 100644 --- a/docs/development/current/main/phases/phase-29ao/README.md +++ b/docs/development/current/main/phases/phase-29ao/README.md @@ -271,6 +271,11 @@ Gate(SSOT): - 指示書: `docs/development/current/main/phases/phase-29ao/P49-CORELOOPCOMPOSER-V1-PATTERN5-INFINITE-EARLY-EXIT-VALUEJOIN-INSTRUCTIONS.md` - ねらい: Pattern5 after-join を v1 で受理し、block_params/EdgeArgs 経由の PHI 一本化を維持(仕様不変) +## P50: CoreLoopComposer v1 — Pattern3 (If-Phi) value-join minimal composition ✅ + +- 指示書: `docs/development/current/main/phases/phase-29ao/P50-CORELOOPCOMPOSER-V1-PATTERN3-IFPHI-VALUEJOIN-INSTRUCTIONS.md` +- ねらい: Pattern3 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 77d139ed..2b65affd 100644 --- a/src/mir/builder/control_flow/plan/composer/coreloop_gates.rs +++ b/src/mir/builder/control_flow/plan/composer/coreloop_gates.rs @@ -28,3 +28,7 @@ pub(super) fn pattern2_value_join_gate(facts: &CanonicalLoopFacts) -> bool { pub(super) fn pattern5_value_join_gate(facts: &CanonicalLoopFacts) -> bool { coreloop_value_join_gate(facts) && exit_kinds_allow_return_only(facts) } + +pub(super) fn pattern3_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 3801a60f..dc209947 100644 --- a/src/mir/builder/control_flow/plan/composer/coreloop_v1.rs +++ b/src/mir/builder/control_flow/plan/composer/coreloop_v1.rs @@ -1,14 +1,14 @@ use crate::mir::builder::control_flow::joinir::patterns::router::LoopPatternContext; use crate::mir::builder::control_flow::plan::composer::coreloop_gates::{ coreloop_value_join_gate, exit_kinds_allow_return_only, pattern2_value_join_gate, - pattern5_value_join_gate, + pattern3_value_join_gate, pattern5_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, Pattern2BreakPlan, Pattern2PromotionHint, Pattern5InfiniteEarlyExitPlan, - SplitScanPlan, + CorePlan, Pattern2BreakPlan, Pattern2PromotionHint, Pattern3IfPhiPlan, + Pattern5InfiniteEarlyExitPlan, SplitScanPlan, }; use crate::mir::builder::MirBuilder; @@ -25,6 +25,9 @@ pub(in crate::mir::builder) fn try_compose_core_loop_v1( )? { return Ok(Some(core)); } + if let Some(core) = try_compose_core_loop_v1_pattern3_ifphi(builder, facts, ctx)? { + return Ok(Some(core)); + } try_compose_core_loop_v1_split_scan(builder, facts, ctx) } @@ -117,6 +120,32 @@ pub(in crate::mir::builder) fn try_compose_core_loop_v1_pattern5_infinite_early_ Ok(Some(core)) } +pub(in crate::mir::builder) fn try_compose_core_loop_v1_pattern3_ifphi( + builder: &mut MirBuilder, + facts: &CanonicalLoopFacts, + ctx: &LoopPatternContext, +) -> Result, String> { + if !pattern3_value_join_gate(facts) { + return Ok(None); + } + + let Some(pattern3) = facts.facts.pattern3_ifphi.as_ref() else { + return Ok(None); + }; + + let plan = Pattern3IfPhiPlan { + loop_var: pattern3.loop_var.clone(), + carrier_var: pattern3.carrier_var.clone(), + condition: pattern3.condition.clone(), + if_condition: pattern3.if_condition.clone(), + then_update: pattern3.then_update.clone(), + else_update: pattern3.else_update.clone(), + loop_increment: pattern3.loop_increment.clone(), + }; + let core = PlanNormalizer::normalize_pattern3_if_phi(builder, plan, ctx)?; + Ok(Some(core)) +} + #[cfg(test)] mod tests { use super::try_compose_core_loop_v1; @@ -126,6 +155,7 @@ mod tests { ValueJoinFacts, }; use crate::mir::builder::control_flow::plan::facts::loop_facts::LoopFacts; + use crate::mir::builder::control_flow::plan::facts::pattern3_ifphi_facts::Pattern3IfPhiFacts; use crate::mir::builder::control_flow::plan::facts::pattern5_infinite_early_exit_facts::Pattern5InfiniteEarlyExitFacts; use crate::mir::builder::control_flow::plan::facts::pattern2_break_facts::Pattern2BreakFacts; use crate::mir::builder::control_flow::plan::facts::scan_shapes::{ @@ -537,4 +567,141 @@ mod tests { try_compose_core_loop_v1(&mut builder, &canonical, &ctx).expect("Ok"); assert!(composed.is_none()); } + + #[test] + fn coreloop_v1_composes_pattern3_with_value_join() { + let condition = ASTNode::BinaryOp { + operator: BinaryOperator::Less, + left: Box::new(v("i")), + right: Box::new(lit_int(3)), + span: Span::unknown(), + }; + let if_condition = ASTNode::BinaryOp { + operator: BinaryOperator::Greater, + left: Box::new(v("i")), + right: Box::new(lit_int(0)), + span: Span::unknown(), + }; + let then_update = ASTNode::BinaryOp { + operator: BinaryOperator::Add, + left: Box::new(v("sum")), + right: Box::new(lit_int(1)), + span: Span::unknown(), + }; + let else_update = ASTNode::BinaryOp { + operator: BinaryOperator::Add, + left: Box::new(v("sum")), + right: Box::new(lit_int(0)), + 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: Some(Pattern3IfPhiFacts { + loop_var: "i".to_string(), + carrier_var: "sum".to_string(), + condition: condition.clone(), + if_condition, + then_update, + else_update, + loop_increment, + }), + pattern4_continue: None, + pattern5_infinite_early_exit: None, + pattern8_bool_predicate_scan: None, + pattern9_accum_const_loop: None, + pattern2_break: None, + pattern2_loopbodylocal: None, + }; + let canonical = canonicalize_loop_facts(facts); + let mut builder = MirBuilder::new(); + builder.enter_function_for_test("coreloop_v1_pattern3".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(&condition, &[], "coreloop_v1_pattern3", 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_pattern3_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: Some(Pattern3IfPhiFacts { + loop_var: "i".to_string(), + carrier_var: "sum".to_string(), + condition: condition.clone(), + if_condition: condition.clone(), + then_update: lit_int(0), + else_update: lit_int(0), + loop_increment: lit_int(0), + }), + pattern4_continue: None, + pattern5_infinite_early_exit: None, + pattern8_bool_predicate_scan: None, + pattern9_accum_const_loop: None, + pattern2_break: None, + pattern2_loopbodylocal: None, + }; + let canonical = canonicalize_loop_facts(facts); + let mut builder = MirBuilder::new(); + builder.enter_function_for_test("coreloop_v1_pattern3_cleanup".to_string()); + let ctx = LoopPatternContext::new( + &condition, + &[], + "coreloop_v1_pattern3_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 49d1dc78..beb04666 100644 --- a/src/mir/builder/control_flow/plan/composer/shadow_adopt.rs +++ b/src/mir/builder/control_flow/plan/composer/shadow_adopt.rs @@ -5,7 +5,8 @@ use super::coreloop_v0::{ }; use super::coreloop_v1::try_compose_core_loop_v1; use super::coreloop_v1::{ - try_compose_core_loop_v1_pattern2_break, try_compose_core_loop_v1_pattern5_infinite_early_exit, + try_compose_core_loop_v1_pattern2_break, try_compose_core_loop_v1_pattern3_ifphi, + try_compose_core_loop_v1_pattern5_infinite_early_exit, }; use super::PlanNormalizer; use crate::mir::builder::control_flow::joinir::patterns::router::LoopPatternContext; @@ -165,7 +166,12 @@ pub(in crate::mir::builder) fn try_release_adopt_core_plan_for_pattern3_ifphi( return Ok(None); } - match compose_coreplan_for_pattern3_ifphi(builder, facts, ctx) { + let composed = if facts.value_join_needed { + try_compose_core_loop_v1_pattern3_ifphi(builder, facts, ctx) + } else { + compose_coreplan_for_pattern3_ifphi(builder, facts, ctx) + }; + match composed { Ok(Some(core)) => Ok(Some(core)), Ok(None) | Err(_) => Ok(None), } @@ -350,8 +356,12 @@ pub(in crate::mir::builder) fn try_shadow_adopt_core_plan( if facts.facts.pattern3_ifphi.is_none() { return Err("pattern3 strict/dev adopt failed: facts mismatch".to_string()); } - let core_plan = compose_coreplan_for_pattern3_ifphi(builder, facts, ctx)? - .ok_or_else(|| "pattern3 strict/dev adopt failed: compose rejected".to_string())?; + let core_plan = if facts.value_join_needed { + try_compose_core_loop_v1_pattern3_ifphi(builder, facts, ctx)? + } else { + compose_coreplan_for_pattern3_ifphi(builder, facts, ctx)? + } + .ok_or_else(|| "pattern3 strict/dev adopt failed: compose rejected".to_string())?; Ok(Some(ShadowAdoptOutcome { core_plan, tag: "[coreplan/shadow_adopt:pattern3_ifphi]", diff --git a/src/mir/builder/control_flow/plan/normalizer/pattern3_if_phi.rs b/src/mir/builder/control_flow/plan/normalizer/pattern3_if_phi.rs index c68d0608..83769d5e 100644 --- a/src/mir/builder/control_flow/plan/normalizer/pattern3_if_phi.rs +++ b/src/mir/builder/control_flow/plan/normalizer/pattern3_if_phi.rs @@ -16,7 +16,7 @@ impl super::PlanNormalizer { /// - 2 PHIs: in header (loop_var, carrier) /// - merge join (carrier_next) is expressed via `Frag.block_params + EdgeArgs` /// - If-else branching with PHI merge (no Select instruction) - pub(super) fn normalize_pattern3_if_phi( + pub(in crate::mir::builder) fn normalize_pattern3_if_phi( builder: &mut MirBuilder, parts: Pattern3IfPhiPlan, ctx: &LoopPatternContext,