From 9c3704315f5048435966cdc654901b0fcead99a9 Mon Sep 17 00:00:00 2001 From: tomoaki Date: Tue, 30 Dec 2025 04:41:08 +0900 Subject: [PATCH] phase29ao(p3): direct coreloop skeleton from facts (pattern1 subset) --- CURRENT_TASK.md | 4 +- docs/development/current/main/10-Now.md | 7 +- docs/development/current/main/30-Backlog.md | 2 +- .../current/main/phases/phase-29ao/README.md | 7 +- .../builder/control_flow/plan/composer/mod.rs | 106 +++++++++++++ .../control_flow/plan/normalizer/mod.rs | 1 + .../plan/normalizer/skeleton_loop.rs | 145 ++++++++++++++++++ 7 files changed, 266 insertions(+), 6 deletions(-) create mode 100644 src/mir/builder/control_flow/plan/normalizer/skeleton_loop.rs diff --git a/CURRENT_TASK.md b/CURRENT_TASK.md index 8517a07a..bacac628 100644 --- a/CURRENT_TASK.md +++ b/CURRENT_TASK.md @@ -18,8 +18,8 @@ Scope: Repo root の旧リンク互換。現行の入口は `docs/development/cu **CorePlan migration 道筋 SSOT** `docs/development/current/main/design/coreplan-migration-roadmap-ssot.md` が移行タスクの Done 判定の入口。 -**Next implementation (Phase 29ao P3)** -`docs/development/current/main/phases/phase-29ao/P3-CORELOOP-SKELETON-COMPOSE-INSTRUCTIONS.md` +**Next implementation (Phase 29ao P4)** +`docs/development/current/main/phases/phase-29ao/README.md` **2025-12-29: Phase 29am P0 COMPLETE (CorePlan If/Exit lowerer/verifier)** CorePlan の If/Exit を lowerer/verifier で扱えるようにして、CorePlan 移行の土台を作った。 diff --git a/docs/development/current/main/10-Now.md b/docs/development/current/main/10-Now.md index 08bf9417..124fa64c 100644 --- a/docs/development/current/main/10-Now.md +++ b/docs/development/current/main/10-Now.md @@ -2,7 +2,7 @@ ## Current Focus: Phase 29ao(CorePlan composition) -Next: Phase 29ao P3(CoreLoop skeleton → CorePlan) +Next: Phase 29ao P4(ExitMap presence → Frag/ExitMap) 運用ルール: integration filter で phase143_* は回さない(JoinIR 回帰は phase29ae pack のみ) 運用ルール: phase286_pattern9_* は legacy pack (SKIP) を使う 移行道筋 SSOT: `docs/development/current/main/design/coreplan-migration-roadmap-ssot.md` @@ -22,6 +22,11 @@ Next: Phase 29ao P3(CoreLoop skeleton → CorePlan) - 変更: `src/mir/builder/control_flow/plan/composer/mod.rs` / `docs/development/current/main/phases/phase-29ao/README.md` / `docs/development/current/main/10-Now.md` / `docs/development/current/main/30-Backlog.md` / `CURRENT_TASK.md` - 検証: `cargo build --release` / `./tools/smokes/v2/run.sh --profile quick` / `./tools/smokes/v2/profiles/integration/joinir/phase29ae_regression_pack_vm.sh` +**2025-12-30: Phase 29ao P3 完了** ✅ +- 目的: `CanonicalLoopFacts` から `CorePlan::Loop`(skeleton)を direct 生成(Pattern1 subset のみ) +- 変更: `src/mir/builder/control_flow/plan/normalizer/skeleton_loop.rs` / `src/mir/builder/control_flow/plan/normalizer/mod.rs` / `src/mir/builder/control_flow/plan/composer/mod.rs` / `docs/development/current/main/phases/phase-29ao/README.md` / `docs/development/current/main/10-Now.md` / `docs/development/current/main/30-Backlog.md` / `CURRENT_TASK.md` +- 検証: `cargo build --release` / `./tools/smokes/v2/run.sh --profile quick` / `./tools/smokes/v2/profiles/integration/joinir/phase29ae_regression_pack_vm.sh` + **2025-12-29: Phase 29an P15 完了** ✅ - 目的: P0–P14 の成果を closeout 形式でまとめ、次フェーズ(Phase 29ao)入口を固定 - 変更: `docs/development/current/main/phases/phase-29an/README.md` / `docs/development/current/main/10-Now.md` / `docs/development/current/main/30-Backlog.md` / `CURRENT_TASK.md` diff --git a/docs/development/current/main/30-Backlog.md b/docs/development/current/main/30-Backlog.md index 096dcba3..08ce8d32 100644 --- a/docs/development/current/main/30-Backlog.md +++ b/docs/development/current/main/30-Backlog.md @@ -15,7 +15,7 @@ Related: - **Phase 29ao(active): CorePlan composition from Skeleton/Feature** - 入口: `docs/development/current/main/phases/phase-29ao/README.md` - - 状況: P0/P1/P2 ✅ 完了 / Next: P3 + - 状況: P0/P1/P2/P3 ✅ 完了 / Next: P4 - **Phase 29af(✅ COMPLETE): Boundary hygiene / regression entrypoint / carrier layout SSOT** - 入口: `docs/development/current/main/phases/phase-29af/README.md` diff --git a/docs/development/current/main/phases/phase-29ao/README.md b/docs/development/current/main/phases/phase-29ao/README.md index 56b4dae2..fff0b205 100644 --- a/docs/development/current/main/phases/phase-29ao/README.md +++ b/docs/development/current/main/phases/phase-29ao/README.md @@ -33,10 +33,13 @@ Gate(SSOT): - 指示書: `docs/development/current/main/phases/phase-29ao/P2-COREPLAN-COMPOSER-VIA-NORMALIZER-INSTRUCTIONS.md` - ねらい: `CanonicalLoopFacts → DomainPlan → PlanNormalizer → CorePlan` の橋渡しを未接続で固定 +## P3: CoreLoop skeleton を CorePlan で直接生成(未接続・仕様不変)✅ + +- 指示書: `docs/development/current/main/phases/phase-29ao/P3-CORELOOP-SKELETON-COMPOSE-INSTRUCTIONS.md` +- ねらい: `CanonicalLoopFacts` から `CorePlan::Loop`(skeleton)を direct 生成(Pattern1 subset のみ) + ## Next(planned) -- P3: CoreLoop skeleton を `CorePlan` 語彙で直接生成(DomainPlan 非依存・未接続) - - 指示書: `docs/development/current/main/phases/phase-29ao/P3-CORELOOP-SKELETON-COMPOSE-INSTRUCTIONS.md` - P4: ExitMap presence を `Frag/ExitMap` と結ぶ(break/continue/return を feature 合成へ寄せる) - P5: Cleanup presence を ExitKind 単位で wire(effect/cleanup SSOT に従う、観測差分なし) - P6: ValueJoin presence を post-phi SSOT に沿って wire(局所 verify から) diff --git a/src/mir/builder/control_flow/plan/composer/mod.rs b/src/mir/builder/control_flow/plan/composer/mod.rs index 6aa40651..bd4b3c36 100644 --- a/src/mir/builder/control_flow/plan/composer/mod.rs +++ b/src/mir/builder/control_flow/plan/composer/mod.rs @@ -41,6 +41,15 @@ pub(in crate::mir::builder) fn try_compose_core_plan_via_normalizer( Ok(Some(core)) } +#[allow(dead_code)] +pub(in crate::mir::builder) fn try_compose_core_plan_direct( + builder: &mut MirBuilder, + facts: &CanonicalLoopFacts, + ctx: &LoopPatternContext, +) -> Result, String> { + PlanNormalizer::normalize_loop_skeleton_from_facts(builder, facts, ctx) +} + #[allow(dead_code)] pub(in crate::mir::builder) fn try_compose_core_plan_from_canonical_facts( facts: &CanonicalLoopFacts, @@ -55,6 +64,7 @@ pub(in crate::mir::builder) fn try_compose_core_plan_from_canonical_facts( #[cfg(test)] mod tests { use super::{ + try_compose_core_plan_direct, try_compose_core_plan_from_canonical_facts, try_compose_core_plan_via_normalizer, try_compose_domain_plan_from_canonical_facts, @@ -70,6 +80,7 @@ mod tests { SkeletonFacts, SkeletonKind, }; use crate::mir::builder::control_flow::plan::normalize::canonicalize_loop_facts; + use crate::mir::builder::control_flow::plan::verifier::PlanVerifier; use crate::mir::builder::control_flow::plan::DomainPlan; use crate::mir::builder::control_flow::joinir::patterns::router::LoopPatternContext; use crate::mir::builder::MirBuilder; @@ -269,4 +280,99 @@ mod tests { .expect("Ok"); assert!(plan.is_none()); } + + #[test] + fn composer_direct_builds_coreloop_for_pattern1() { + let pattern1 = Pattern1SimpleWhileFacts { + loop_var: "i".to_string(), + condition: ASTNode::BinaryOp { + operator: BinaryOperator::Less, + left: Box::new(v("i")), + right: Box::new(lit_int(3)), + span: Span::unknown(), + }, + loop_increment: ASTNode::BinaryOp { + operator: BinaryOperator::Add, + left: Box::new(v("i")), + right: Box::new(lit_int(1)), + 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: None, + pattern1_simplewhile: Some(pattern1), + 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 condition = canonical + .facts + .pattern1_simplewhile + .as_ref() + .unwrap() + .condition + .clone(); + let ctx = LoopPatternContext::new(&condition, &[], "composer_test", false, false); + let mut builder = MirBuilder::new(); + builder.enter_function_for_test("composer_test".to_string()); + let loop_var_init = builder.alloc_typed(MirType::Integer); + builder + .variable_ctx + .variable_map + .insert("i".to_string(), loop_var_init); + let plan = + try_compose_core_plan_direct(&mut builder, &canonical, &ctx) + .expect("Ok"); + let core = plan.expect("Some"); + assert!(matches!(core, super::CorePlan::Loop(_))); + PlanVerifier::verify(&core).expect("verify"); + } + + #[test] + fn composer_direct_returns_none_without_pattern1() { + 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: 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: None, + pattern2_loopbodylocal: None, + }; + let canonical = canonicalize_loop_facts(facts); + let condition = ASTNode::BinaryOp { + operator: BinaryOperator::Less, + left: Box::new(v("i")), + right: Box::new(lit_int(3)), + span: Span::unknown(), + }; + let ctx = LoopPatternContext::new(&condition, &[], "composer_test", false, false); + let mut builder = MirBuilder::new(); + builder.enter_function_for_test("composer_test".to_string()); + let plan = + try_compose_core_plan_direct(&mut builder, &canonical, &ctx) + .expect("Ok"); + assert!(plan.is_none()); + } } diff --git a/src/mir/builder/control_flow/plan/normalizer/mod.rs b/src/mir/builder/control_flow/plan/normalizer/mod.rs index 66e46927..35a1895d 100644 --- a/src/mir/builder/control_flow/plan/normalizer/mod.rs +++ b/src/mir/builder/control_flow/plan/normalizer/mod.rs @@ -21,6 +21,7 @@ mod pattern8_bool_predicate_scan; mod pattern9_accum_const_loop; mod pattern_scan_with_init; mod pattern_split_scan; +mod skeleton_loop; mod common; use super::{ diff --git a/src/mir/builder/control_flow/plan/normalizer/skeleton_loop.rs b/src/mir/builder/control_flow/plan/normalizer/skeleton_loop.rs new file mode 100644 index 00000000..d006fe26 --- /dev/null +++ b/src/mir/builder/control_flow/plan/normalizer/skeleton_loop.rs @@ -0,0 +1,145 @@ +use super::helpers::{create_phi_bindings, LoopBlocksStandard5}; +use super::{CoreEffectPlan, CoreLoopPlan, CorePhiInfo, CorePlan}; +use crate::mir::basic_block::EdgeArgs; +use crate::mir::builder::control_flow::joinir::patterns::router::LoopPatternContext; +use crate::mir::builder::control_flow::plan::facts::skeleton_facts::SkeletonKind; +use crate::mir::builder::control_flow::plan::normalize::CanonicalLoopFacts; +use crate::mir::builder::control_flow::edgecfg::api::{BranchStub, EdgeStub, ExitKind, Frag}; +use crate::mir::builder::MirBuilder; +use crate::mir::join_ir::lowering::inline_boundary::JumpArgsLayout; +use crate::mir::MirType; +use std::collections::BTreeMap; + +impl super::PlanNormalizer { + pub(in crate::mir::builder) fn normalize_loop_skeleton_from_facts( + builder: &mut MirBuilder, + facts: &CanonicalLoopFacts, + _ctx: &LoopPatternContext, + ) -> Result, String> { + if !matches!(facts.skeleton_kind, SkeletonKind::Loop) { + return Ok(None); + } + let Some(pattern1) = facts.facts.pattern1_simplewhile.as_ref() else { + return Ok(None); + }; + + let loop_var_init = builder + .variable_ctx + .variable_map + .get(&pattern1.loop_var) + .copied() + .ok_or_else(|| { + format!( + "[normalizer] Loop variable {} not found", + pattern1.loop_var + ) + })?; + + let blocks = LoopBlocksStandard5::allocate(builder)?; + let LoopBlocksStandard5 { + preheader_bb, + header_bb, + body_bb, + step_bb, + after_bb, + } = blocks; + + let loop_var_current = builder.alloc_typed(MirType::Integer); + let cond_loop = builder.alloc_typed(MirType::Bool); + let loop_var_next = builder.alloc_typed(MirType::Integer); + + let phi_bindings = create_phi_bindings(&[(&pattern1.loop_var, loop_var_current)]); + + let (loop_cond_lhs, loop_cond_op, loop_cond_rhs, loop_cond_consts) = + Self::lower_compare_ast(&pattern1.condition, builder, &phi_bindings)?; + + let (loop_inc_lhs, loop_inc_op, loop_inc_rhs, loop_inc_consts) = + Self::lower_binop_ast(&pattern1.loop_increment, builder, &phi_bindings)?; + + let mut header_effects = loop_cond_consts; + header_effects.push(CoreEffectPlan::Compare { + dst: cond_loop, + lhs: loop_cond_lhs, + op: loop_cond_op, + rhs: loop_cond_rhs, + }); + + let mut step_effects = loop_inc_consts; + step_effects.push(CoreEffectPlan::BinOp { + dst: loop_var_next, + lhs: loop_inc_lhs, + op: loop_inc_op, + rhs: loop_inc_rhs, + }); + + let block_effects = vec![ + (preheader_bb, vec![]), + (header_bb, header_effects), + (body_bb, vec![]), + (step_bb, step_effects), + ]; + + let phis = vec![CorePhiInfo { + block: header_bb, + dst: loop_var_current, + inputs: vec![(preheader_bb, loop_var_init), (step_bb, loop_var_next)], + tag: format!("loop_var_{}", pattern1.loop_var), + }]; + + let empty_args = EdgeArgs { + layout: JumpArgsLayout::CarriersOnly, + values: vec![], + }; + + let branches = vec![BranchStub { + from: header_bb, + cond: cond_loop, + then_target: body_bb, + then_args: empty_args.clone(), + else_target: after_bb, + else_args: empty_args.clone(), + }]; + + let wires = vec![ + EdgeStub { + from: body_bb, + kind: ExitKind::Normal, + target: Some(step_bb), + args: empty_args.clone(), + }, + EdgeStub { + from: step_bb, + kind: ExitKind::Normal, + target: Some(header_bb), + args: empty_args.clone(), + }, + ]; + + let frag = Frag { + entry: header_bb, + exits: BTreeMap::new(), + wires, + branches, + }; + + let final_values = vec![(pattern1.loop_var.clone(), loop_var_current)]; + + let loop_plan = CoreLoopPlan { + preheader_bb, + header_bb, + body_bb, + step_bb, + after_bb, + found_bb: after_bb, + body: vec![], + cond_loop, + cond_match: cond_loop, + block_effects, + phis, + frag, + final_values, + }; + + Ok(Some(CorePlan::Loop(loop_plan))) + } +}