phase29ao(p46): coreloop v0 composes pattern7 subset

This commit is contained in:
2025-12-30 19:51:30 +09:00
parent b126346e7f
commit f4bcd4f6b9
7 changed files with 179 additions and 19 deletions

View File

@ -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)

View File

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

View File

@ -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再発防止の土台

View File

@ -251,6 +251,11 @@ GateSSOT:
- 指示書: `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を寄せる仕様不変
## Nextplanned
- Next: TBD

View File

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

View File

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

View File

@ -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,