From 4d962e19275bd23d8b24e98be138170198221dea Mon Sep 17 00:00:00 2001 From: tomoaki Date: Tue, 30 Dec 2025 04:57:53 +0900 Subject: [PATCH] phase29ao(p4): project exitmap presence into frag exits (unconnected) --- 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 | 173 +++++++++++++++++- .../plan/normalizer/skeleton_loop.rs | 24 ++- 6 files changed, 206 insertions(+), 11 deletions(-) diff --git a/CURRENT_TASK.md b/CURRENT_TASK.md index 0f5a5482..e84b6707 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 P4)** -`docs/development/current/main/phases/phase-29ao/P4-EXITMAP-PRESENCE-WIRE-INSTRUCTIONS.md` +**Next implementation (Phase 29ao P5)** +`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 124fa64c..207ed28e 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 P4(ExitMap presence → Frag/ExitMap) +Next: Phase 29ao P5(Cleanup presence → ExitKind) 運用ルール: integration filter で phase143_* は回さない(JoinIR 回帰は phase29ae pack のみ) 運用ルール: phase286_pattern9_* は legacy pack (SKIP) を使う 移行道筋 SSOT: `docs/development/current/main/design/coreplan-migration-roadmap-ssot.md` @@ -27,6 +27,11 @@ Next: Phase 29ao P4(ExitMap presence → Frag/ExitMap) - 変更: `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-30: Phase 29ao P4 完了** ✅ +- 目的: `exit_kinds_present` を `Frag.exits` に投影(未配線のまま語彙のみ固定) +- 変更: `src/mir/builder/control_flow/plan/normalizer/skeleton_loop.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 08ce8d32..4f526d0e 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/P3 ✅ 完了 / Next: P4 + - 状況: P0/P1/P2/P3/P4 ✅ 完了 / Next: P5 - **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 861b0b5c..67f4476a 100644 --- a/docs/development/current/main/phases/phase-29ao/README.md +++ b/docs/development/current/main/phases/phase-29ao/README.md @@ -38,9 +38,12 @@ Gate(SSOT): - 指示書: `docs/development/current/main/phases/phase-29ao/P3-CORELOOP-SKELETON-COMPOSE-INSTRUCTIONS.md` - ねらい: `CanonicalLoopFacts` から `CorePlan::Loop`(skeleton)を direct 生成(Pattern1 subset のみ) +## P4: ExitMap presence を Frag.exits に投影(未接続・仕様不変)✅ + +- 指示書: `docs/development/current/main/phases/phase-29ao/P4-EXITMAP-PRESENCE-WIRE-INSTRUCTIONS.md` +- ねらい: `exit_kinds_present` の presence を `Frag.exits` に投影(未配線のまま語彙だけ固定) + ## Next(planned) -- P4: ExitMap presence → `Frag.exits`(未接続・仕様不変) - - 指示書: `docs/development/current/main/phases/phase-29ao/P4-EXITMAP-PRESENCE-WIRE-INSTRUCTIONS.md` - 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 bd4b3c36..5aaacde2 100644 --- a/src/mir/builder/control_flow/plan/composer/mod.rs +++ b/src/mir/builder/control_flow/plan/composer/mod.rs @@ -70,7 +70,9 @@ mod tests { try_compose_domain_plan_from_canonical_facts, }; use crate::ast::{ASTNode, BinaryOperator, LiteralValue, Span}; - use crate::mir::builder::control_flow::plan::facts::feature_facts::LoopFeatureFacts; + use crate::mir::builder::control_flow::plan::facts::feature_facts::{ + ExitKindFacts, ExitMapFacts, ExitUsageFacts, LoopFeatureFacts, + }; use crate::mir::builder::control_flow::plan::facts::loop_facts::LoopFacts; use crate::mir::builder::control_flow::plan::facts::pattern1_simplewhile_facts::Pattern1SimpleWhileFacts; use crate::mir::builder::control_flow::plan::facts::scan_shapes::{ @@ -82,9 +84,12 @@ mod tests { 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::edgecfg::api::ExitKind; use crate::mir::builder::control_flow::joinir::patterns::router::LoopPatternContext; use crate::mir::builder::MirBuilder; + use crate::mir::control_form::LoopId; use crate::mir::MirType; + use std::collections::BTreeSet; fn v(name: &str) -> ASTNode { ASTNode::Variable { @@ -336,7 +341,11 @@ mod tests { try_compose_core_plan_direct(&mut builder, &canonical, &ctx) .expect("Ok"); let core = plan.expect("Some"); - assert!(matches!(core, super::CorePlan::Loop(_))); + let loop_plan = match &core { + super::CorePlan::Loop(loop_plan) => loop_plan, + _ => panic!("expected CorePlan::Loop"), + }; + assert!(loop_plan.frag.exits.is_empty()); PlanVerifier::verify(&core).expect("verify"); } @@ -375,4 +384,164 @@ mod tests { .expect("Ok"); assert!(plan.is_none()); } + + #[test] + fn composer_direct_projects_return_exitmap_presence() { + 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 mut kinds_present = BTreeSet::new(); + kinds_present.insert(ExitKindFacts::Return); + let exit_usage = ExitUsageFacts { + has_break: false, + has_continue: false, + has_return: true, + }; + let facts = LoopFacts { + condition_shape: ConditionShape::Unknown, + step_shape: StepShape::Unknown, + skeleton: SkeletonFacts { + kind: SkeletonKind::Loop, + }, + features: LoopFeatureFacts { + exit_usage, + exit_map: Some(ExitMapFacts { kinds_present }), + ..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") + .expect("Some"); + let loop_plan = match plan { + super::CorePlan::Loop(loop_plan) => loop_plan, + _ => panic!("expected CorePlan::Loop"), + }; + let exits = loop_plan.frag.exits.get(&ExitKind::Return).expect("Return exit"); + assert_eq!(exits.len(), 1); + assert!(exits[0].target.is_none()); + } + + #[test] + fn composer_direct_projects_break_continue_exitmap_presence() { + 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 mut kinds_present = BTreeSet::new(); + kinds_present.insert(ExitKindFacts::Break); + kinds_present.insert(ExitKindFacts::Continue); + let exit_usage = ExitUsageFacts { + has_break: true, + has_continue: true, + has_return: false, + }; + let facts = LoopFacts { + condition_shape: ConditionShape::Unknown, + step_shape: StepShape::Unknown, + skeleton: SkeletonFacts { + kind: SkeletonKind::Loop, + }, + features: LoopFeatureFacts { + exit_usage, + exit_map: Some(ExitMapFacts { kinds_present }), + ..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") + .expect("Some"); + let loop_plan = match plan { + super::CorePlan::Loop(loop_plan) => loop_plan, + _ => panic!("expected CorePlan::Loop"), + }; + let break_kind = ExitKind::Break(LoopId(0)); + let continue_kind = ExitKind::Continue(LoopId(0)); + let break_exits = loop_plan.frag.exits.get(&break_kind).expect("Break exit"); + let continue_exits = loop_plan + .frag + .exits + .get(&continue_kind) + .expect("Continue exit"); + assert_eq!(break_exits.len(), 1); + assert_eq!(continue_exits.len(), 1); + assert!(break_exits[0].target.is_none()); + assert!(continue_exits[0].target.is_none()); + } } diff --git a/src/mir/builder/control_flow/plan/normalizer/skeleton_loop.rs b/src/mir/builder/control_flow/plan/normalizer/skeleton_loop.rs index d006fe26..3a72fd47 100644 --- a/src/mir/builder/control_flow/plan/normalizer/skeleton_loop.rs +++ b/src/mir/builder/control_flow/plan/normalizer/skeleton_loop.rs @@ -1,14 +1,16 @@ use super::helpers::{create_phi_bindings, LoopBlocksStandard5}; use super::{CoreEffectPlan, CoreLoopPlan, CorePhiInfo, CorePlan}; -use crate::mir::basic_block::EdgeArgs; +use crate::mir::basic_block::{BasicBlockId, EdgeArgs}; use crate::mir::builder::control_flow::joinir::patterns::router::LoopPatternContext; +use crate::mir::builder::control_flow::plan::facts::feature_facts::ExitKindFacts; 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::control_form::LoopId; use crate::mir::MirType; -use std::collections::BTreeMap; +use std::collections::{BTreeMap, BTreeSet}; impl super::PlanNormalizer { pub(in crate::mir::builder) fn normalize_loop_skeleton_from_facts( @@ -117,7 +119,7 @@ impl super::PlanNormalizer { let frag = Frag { entry: header_bb, - exits: BTreeMap::new(), + exits: build_exitmap_from_presence(&facts.exit_kinds_present, body_bb), wires, branches, }; @@ -143,3 +145,19 @@ impl super::PlanNormalizer { Ok(Some(CorePlan::Loop(loop_plan))) } } + +fn build_exitmap_from_presence( + present: &BTreeSet, + from: BasicBlockId, +) -> BTreeMap> { + let mut exits = BTreeMap::new(); + for kind in present { + let exit_kind = match kind { + ExitKindFacts::Return => ExitKind::Return, + ExitKindFacts::Break => ExitKind::Break(LoopId(0)), + ExitKindFacts::Continue => ExitKind::Continue(LoopId(0)), + }; + exits.insert(exit_kind, vec![EdgeStub::without_args(from, exit_kind)]); + } + exits +}