phase29ao(p3): direct coreloop skeleton from facts (pattern1 subset)

This commit is contained in:
2025-12-30 04:41:08 +09:00
parent 2dd6c2cac5
commit 9c3704315f
7 changed files with 266 additions and 6 deletions

View File

@ -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 移行の土台を作った。

View File

@ -2,7 +2,7 @@
## Current Focus: Phase 29aoCorePlan composition
Next: Phase 29ao P3CoreLoop skeleton → CorePlan
Next: Phase 29ao P4ExitMap 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 P3CoreLoop 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 完了**
- 目的: P0P14 の成果を 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`

View File

@ -15,7 +15,7 @@ Related:
- **Phase 29aoactive: 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`

View File

@ -33,10 +33,13 @@ GateSSOT:
- 指示書: `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 のみ)
## Nextplanned
- 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 単位で wireeffect/cleanup SSOT に従う、観測差分なし)
- P6: ValueJoin presence を post-phi SSOT に沿って wire局所 verify から)

View File

@ -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<Option<CorePlan>, 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());
}
}

View File

@ -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::{

View File

@ -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<Option<CorePlan>, 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)))
}
}