From f4bcd4f6b9b2fa715b350fe69382f32155d6f3e5 Mon Sep 17 00:00:00 2001 From: tomoaki Date: Tue, 30 Dec 2025 19:51:30 +0900 Subject: [PATCH] phase29ao(p46): coreloop v0 composes pattern7 subset --- 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 + .../control_flow/plan/composer/coreloop_v0.rs | 167 +++++++++++++++++- .../plan/composer/shadow_adopt.rs | 18 +- .../plan/normalizer/pattern_split_scan.rs | 2 +- 7 files changed, 179 insertions(+), 19 deletions(-) diff --git a/docs/development/current/main/10-Now.md b/docs/development/current/main/10-Now.md index e8ecb84b..ddd45bfc 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 (P45 done; see `docs/development/current/main/phases/phase-29ao/README.md`) +- Next: TBD (P46 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 51831a33..87ee7bc0 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, P45 done) +- CorePlan migration: `docs/development/current/main/phases/phase-29ao/README.md`(Next: TBD, P46 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 de94d39f..a4abfa33 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 (P45 done) +- Next step: TBD (P46 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 5e4c5472..33ec96d5 100644 --- a/docs/development/current/main/phases/phase-29ao/README.md +++ b/docs/development/current/main/phases/phase-29ao/README.md @@ -251,6 +251,11 @@ Gate(SSOT): - 指示書: `docs/development/current/main/phases/phase-29ao/P45-CORELOOPCOMPOSER-V0-PATTERN6-SCANWITHINIT-INSTRUCTIONS.md` - ねらい: Pattern6 planner subset の最小合成を v0 で開始し、composer に合成のSSOTを寄せる(仕様不変) +## P46: CoreLoopComposer v0 — Pattern7 (SplitScan) minimal composition ✅ + +- 指示書: `docs/development/current/main/phases/phase-29ao/P46-CORELOOPCOMPOSER-V0-PATTERN7-SPLITSCAN-INSTRUCTIONS.md` +- ねらい: Pattern7 planner subset の最小合成を v0 で開始し、composer に合成のSSOTを寄せる(仕様不変) + ## Next(planned) - Next: TBD diff --git a/src/mir/builder/control_flow/plan/composer/coreloop_v0.rs b/src/mir/builder/control_flow/plan/composer/coreloop_v0.rs index e0e53c57..a4c39cff 100644 --- a/src/mir/builder/control_flow/plan/composer/coreloop_v0.rs +++ b/src/mir/builder/control_flow/plan/composer/coreloop_v0.rs @@ -11,7 +11,9 @@ use crate::mir::builder::control_flow::plan::normalize::CanonicalLoopFacts; use crate::mir::builder::control_flow::plan::normalizer::{ build_pattern1_coreloop, PlanNormalizer, }; -use crate::mir::builder::control_flow::plan::{CorePlan, ScanDirection, ScanWithInitPlan}; +use crate::mir::builder::control_flow::plan::{ + CorePlan, ScanDirection, ScanWithInitPlan, SplitScanPlan, +}; use crate::mir::builder::MirBuilder; #[allow(dead_code)] @@ -85,6 +87,52 @@ pub(in crate::mir::builder) fn try_compose_core_loop_v0_scan_with_init( Ok(Some(core)) } +#[allow(dead_code)] +pub(in crate::mir::builder) fn try_compose_core_loop_v0_split_scan( + builder: &mut MirBuilder, + facts: &CanonicalLoopFacts, + ctx: &LoopPatternContext, +) -> Result, String> { + if !matches!(facts.skeleton_kind, SkeletonKind::Loop) { + return Ok(None); + } + if facts.value_join_needed { + return Ok(None); + } + if !facts.cleanup_kinds_present.is_empty() { + return Ok(None); + } + + let Some(split_scan) = facts.facts.split_scan.as_ref() else { + return Ok(None); + }; + + if !facts.exit_kinds_present.is_empty() + && !(facts.exit_kinds_present.len() == 1 + && facts.exit_kinds_present.contains(&ExitKindFacts::Return)) + { + return Ok(None); + } + + let shapes_match = matches!( + (&facts.facts.condition_shape, &facts.facts.step_shape), + (ConditionShape::Unknown, StepShape::Unknown) + ); + if !shapes_match { + return Ok(None); + } + + let plan = SplitScanPlan { + s_var: split_scan.s_var.clone(), + sep_var: split_scan.sep_var.clone(), + result_var: split_scan.result_var.clone(), + i_var: split_scan.i_var.clone(), + start_var: split_scan.start_var.clone(), + }; + let core = PlanNormalizer::normalize_split_scan(builder, plan, ctx)?; + Ok(Some(core)) +} + #[allow(dead_code)] pub(in crate::mir::builder) fn try_compose_core_loop_v0( builder: &mut MirBuilder, @@ -94,6 +142,9 @@ pub(in crate::mir::builder) fn try_compose_core_loop_v0( if let Some(core) = try_compose_core_loop_v0_scan_with_init(builder, facts, ctx)? { return Ok(Some(core)); } + if let Some(core) = try_compose_core_loop_v0_split_scan(builder, facts, ctx)? { + return Ok(Some(core)); + } if !matches!(facts.skeleton_kind, SkeletonKind::Loop) { return Ok(None); } @@ -457,4 +508,118 @@ mod tests { try_compose_core_loop_v0(&mut builder, &canonical, &ctx).expect("Ok"); assert!(composed.is_none()); } + + #[test] + fn coreloop_v0_composes_split_scan_subset() { + let condition = ASTNode::Literal { + value: LiteralValue::Bool(true), + span: Span::unknown(), + }; + let facts = LoopFacts { + condition_shape: ConditionShape::Unknown, + step_shape: StepShape::Unknown, + skeleton: SkeletonFacts { + kind: SkeletonKind::Loop, + }, + features: LoopFeatureFacts::default(), + scan_with_init: None, + split_scan: Some(crate::mir::builder::control_flow::plan::facts::loop_facts::SplitScanFacts { + s_var: "s".to_string(), + sep_var: "sep".to_string(), + result_var: "result".to_string(), + i_var: "i".to_string(), + start_var: "start".to_string(), + }), + 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: None, + pattern2_loopbodylocal: None, + }; + let canonical = canonicalize_loop_facts(facts); + let mut builder = MirBuilder::new(); + builder.enter_function_for_test("coreloop_v0_split_scan".to_string()); + let s_val = builder.alloc_typed(MirType::String); + let sep_val = builder.alloc_typed(MirType::String); + let result_val = builder.alloc_typed(MirType::Array(Box::new(MirType::String))); + let i_val = builder.alloc_typed(MirType::Integer); + let start_val = builder.alloc_typed(MirType::Integer); + builder + .variable_ctx + .variable_map + .insert("s".to_string(), s_val); + builder + .variable_ctx + .variable_map + .insert("sep".to_string(), sep_val); + builder + .variable_ctx + .variable_map + .insert("result".to_string(), result_val); + builder + .variable_ctx + .variable_map + .insert("i".to_string(), i_val); + builder + .variable_ctx + .variable_map + .insert("start".to_string(), start_val); + let ctx = + LoopPatternContext::new(&condition, &[], "coreloop_v0_split_scan", false, false); + let composed = + try_compose_core_loop_v0(&mut builder, &canonical, &ctx).expect("Ok"); + assert!(matches!(composed, Some(CorePlan::Loop(_)))); + } + + #[test] + fn coreloop_v0_rejects_split_scan_when_value_join_needed() { + let condition = ASTNode::Literal { + value: LiteralValue::Bool(true), + 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: Some(crate::mir::builder::control_flow::plan::facts::loop_facts::SplitScanFacts { + s_var: "s".to_string(), + sep_var: "sep".to_string(), + result_var: "result".to_string(), + i_var: "i".to_string(), + start_var: "start".to_string(), + }), + 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: None, + pattern2_loopbodylocal: None, + }; + let canonical = canonicalize_loop_facts(facts); + let mut builder = MirBuilder::new(); + builder.enter_function_for_test("coreloop_v0_split_scan_join".to_string()); + let ctx = LoopPatternContext::new( + &condition, + &[], + "coreloop_v0_split_scan_join", + false, + false, + ); + let composed = + try_compose_core_loop_v0(&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 94c1c453..30b122b2 100644 --- a/src/mir/builder/control_flow/plan/composer/shadow_adopt.rs +++ b/src/mir/builder/control_flow/plan/composer/shadow_adopt.rs @@ -1,6 +1,8 @@ //! Phase 29ao P30: shadow adopt composer entrypoints (Facts -> CorePlan). -use super::coreloop_v0::try_compose_core_loop_v0_scan_with_init; +use super::coreloop_v0::{ + try_compose_core_loop_v0_scan_with_init, try_compose_core_loop_v0_split_scan, +}; use super::PlanNormalizer; use crate::mir::builder::control_flow::joinir::patterns::router::LoopPatternContext; use crate::mir::builder::control_flow::plan::normalize::CanonicalLoopFacts; @@ -9,7 +11,6 @@ use crate::mir::builder::control_flow::plan::policies::pattern1_subset_policy::i use crate::mir::builder::control_flow::plan::{ CorePlan, DomainPlan, Pattern2BreakPlan, Pattern2PromotionHint, Pattern3IfPhiPlan, Pattern5InfiniteEarlyExitPlan, - SplitScanPlan, }; use crate::mir::builder::MirBuilder; @@ -267,18 +268,7 @@ pub(in crate::mir::builder) fn compose_coreplan_for_pattern7_split_scan( facts: &CanonicalLoopFacts, ctx: &LoopPatternContext, ) -> Result, String> { - let Some(split_scan) = facts.facts.split_scan.as_ref() else { - return Ok(None); - }; - let plan = DomainPlan::SplitScan(SplitScanPlan { - s_var: split_scan.s_var.clone(), - sep_var: split_scan.sep_var.clone(), - result_var: split_scan.result_var.clone(), - i_var: split_scan.i_var.clone(), - start_var: split_scan.start_var.clone(), - }); - let core = PlanNormalizer::normalize(builder, plan, ctx)?; - Ok(Some(core)) + try_compose_core_loop_v0_split_scan(builder, facts, ctx) } pub(in crate::mir::builder) fn try_shadow_adopt_core_plan( diff --git a/src/mir/builder/control_flow/plan/normalizer/pattern_split_scan.rs b/src/mir/builder/control_flow/plan/normalizer/pattern_split_scan.rs index 13e01a26..c3010c98 100644 --- a/src/mir/builder/control_flow/plan/normalizer/pattern_split_scan.rs +++ b/src/mir/builder/control_flow/plan/normalizer/pattern_split_scan.rs @@ -18,7 +18,7 @@ impl super::PlanNormalizer { /// - 6 blocks: preheader, header, body, then, else, step, after /// - 4 PHI nodes: header (i_current, start_current) + step (i_next, start_next) /// - Side effect: result.push(segment) in then_bb - pub(super) fn normalize_split_scan( + pub(in crate::mir::builder) fn normalize_split_scan( builder: &mut MirBuilder, parts: SplitScanPlan, ctx: &LoopPatternContext,