phase29ao(p48): compose pattern2 break via v1 value-join
This commit is contained in:
@ -3,7 +3,7 @@
|
||||
## Current Focus
|
||||
|
||||
- Phase: `docs/development/current/main/phases/phase-29ao/README.md`
|
||||
- Next: TBD (P47 done; see `docs/development/current/main/phases/phase-29ao/README.md`)
|
||||
- Next: TBD (P48 done; see `docs/development/current/main/phases/phase-29ao/README.md`)
|
||||
|
||||
## Gate (SSOT)
|
||||
|
||||
|
||||
@ -5,7 +5,7 @@ Scope: 「次にやる候補」を短く列挙するメモ。入口は `docs/dev
|
||||
|
||||
## Active
|
||||
|
||||
- CorePlan migration: `docs/development/current/main/phases/phase-29ao/README.md`(Next: TBD, P47 done)
|
||||
- CorePlan migration: `docs/development/current/main/phases/phase-29ao/README.md`(Next: TBD, P48 done)
|
||||
|
||||
## Near-Term Candidates
|
||||
|
||||
|
||||
@ -34,7 +34,7 @@ Related:
|
||||
## 1.1 Current (active)
|
||||
|
||||
- Active phase: `docs/development/current/main/phases/phase-29ao/README.md`
|
||||
- Next step: TBD (P47 done)
|
||||
- Next step: TBD (P48 done)
|
||||
|
||||
## 2. すでに固めた SSOT(再発防止の土台)
|
||||
|
||||
|
||||
@ -261,6 +261,11 @@ Gate(SSOT):
|
||||
- 指示書: `docs/development/current/main/phases/phase-29ao/P47-CORELOOPCOMPOSER-V1-SPLITSCAN-VALUEJOIN-INSTRUCTIONS.md`
|
||||
- ねらい: Pattern7 value-join を v1 で受理し、block_params/EdgeArgs 経由の PHI 一本化を維持(仕様不変)
|
||||
|
||||
## P48: CoreLoopComposer v1 — Pattern2 (Break) value-join minimal composition ✅
|
||||
|
||||
- 指示書: `docs/development/current/main/phases/phase-29ao/P48-CORELOOPCOMPOSER-V1-PATTERN2-BREAK-VALUEJOIN-INSTRUCTIONS.md`
|
||||
- ねらい: Pattern2 after-join を v1 で受理し、block_params/EdgeArgs 経由の PHI 一本化を維持(仕様不変)
|
||||
|
||||
## Next(planned)
|
||||
|
||||
- Next: TBD
|
||||
|
||||
@ -7,6 +7,10 @@ pub(super) fn coreloop_base_gate(facts: &CanonicalLoopFacts) -> bool {
|
||||
&& facts.cleanup_kinds_present.is_empty()
|
||||
}
|
||||
|
||||
pub(super) fn coreloop_value_join_gate(facts: &CanonicalLoopFacts) -> bool {
|
||||
coreloop_base_gate(facts) && facts.value_join_needed
|
||||
}
|
||||
|
||||
pub(super) fn exit_kinds_allow_return_only(facts: &CanonicalLoopFacts) -> bool {
|
||||
facts.exit_kinds_present.is_empty()
|
||||
|| (facts.exit_kinds_present.len() == 1
|
||||
@ -16,3 +20,7 @@ pub(super) fn exit_kinds_allow_return_only(facts: &CanonicalLoopFacts) -> bool {
|
||||
pub(super) fn exit_kinds_empty(facts: &CanonicalLoopFacts) -> bool {
|
||||
facts.exit_kinds_present.is_empty()
|
||||
}
|
||||
|
||||
pub(super) fn pattern2_value_join_gate(facts: &CanonicalLoopFacts) -> bool {
|
||||
coreloop_value_join_gate(facts) && exit_kinds_allow_return_only(facts)
|
||||
}
|
||||
|
||||
@ -1,23 +1,32 @@
|
||||
use crate::mir::builder::control_flow::joinir::patterns::router::LoopPatternContext;
|
||||
use crate::mir::builder::control_flow::plan::composer::coreloop_gates::{
|
||||
coreloop_base_gate, exit_kinds_allow_return_only,
|
||||
coreloop_value_join_gate, exit_kinds_allow_return_only, pattern2_value_join_gate,
|
||||
};
|
||||
use crate::mir::builder::control_flow::plan::facts::scan_shapes::SplitScanShape;
|
||||
use crate::mir::builder::control_flow::plan::normalize::CanonicalLoopFacts;
|
||||
use crate::mir::builder::control_flow::plan::normalizer::PlanNormalizer;
|
||||
use crate::mir::builder::control_flow::plan::{CorePlan, SplitScanPlan};
|
||||
use crate::mir::builder::control_flow::plan::{
|
||||
CorePlan, Pattern2BreakPlan, Pattern2PromotionHint, SplitScanPlan,
|
||||
};
|
||||
use crate::mir::builder::MirBuilder;
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(in crate::mir::builder) fn try_compose_core_loop_v1(
|
||||
builder: &mut MirBuilder,
|
||||
facts: &CanonicalLoopFacts,
|
||||
ctx: &LoopPatternContext,
|
||||
) -> Result<Option<CorePlan>, String> {
|
||||
if !coreloop_base_gate(facts) {
|
||||
return Ok(None);
|
||||
if let Some(core) = try_compose_core_loop_v1_pattern2_break(builder, facts, ctx)? {
|
||||
return Ok(Some(core));
|
||||
}
|
||||
if !facts.value_join_needed {
|
||||
try_compose_core_loop_v1_split_scan(builder, facts, ctx)
|
||||
}
|
||||
|
||||
fn try_compose_core_loop_v1_split_scan(
|
||||
builder: &mut MirBuilder,
|
||||
facts: &CanonicalLoopFacts,
|
||||
ctx: &LoopPatternContext,
|
||||
) -> Result<Option<CorePlan>, String> {
|
||||
if !coreloop_value_join_gate(facts) {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
@ -43,14 +52,48 @@ pub(in crate::mir::builder) fn try_compose_core_loop_v1(
|
||||
Ok(Some(core))
|
||||
}
|
||||
|
||||
pub(in crate::mir::builder) fn try_compose_core_loop_v1_pattern2_break(
|
||||
builder: &mut MirBuilder,
|
||||
facts: &CanonicalLoopFacts,
|
||||
ctx: &LoopPatternContext,
|
||||
) -> Result<Option<CorePlan>, String> {
|
||||
if !pattern2_value_join_gate(facts) {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let Some(pattern2) = facts.facts.pattern2_break.as_ref() else {
|
||||
return Ok(None);
|
||||
};
|
||||
let promotion = facts
|
||||
.facts
|
||||
.pattern2_loopbodylocal
|
||||
.as_ref()
|
||||
.map(|facts| Pattern2PromotionHint::LoopBodyLocal(facts.clone()));
|
||||
|
||||
let plan = Pattern2BreakPlan {
|
||||
loop_var: pattern2.loop_var.clone(),
|
||||
carrier_var: pattern2.carrier_var.clone(),
|
||||
loop_condition: pattern2.loop_condition.clone(),
|
||||
break_condition: pattern2.break_condition.clone(),
|
||||
carrier_update_in_break: pattern2.carrier_update_in_break.clone(),
|
||||
carrier_update_in_body: pattern2.carrier_update_in_body.clone(),
|
||||
loop_increment: pattern2.loop_increment.clone(),
|
||||
promotion,
|
||||
};
|
||||
let core = PlanNormalizer::normalize_pattern2_break(builder, plan, ctx)?;
|
||||
Ok(Some(core))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::try_compose_core_loop_v1;
|
||||
use crate::ast::{ASTNode, LiteralValue, Span};
|
||||
use crate::ast::{ASTNode, BinaryOperator, LiteralValue, Span};
|
||||
use crate::mir::builder::control_flow::plan::facts::feature_facts::{
|
||||
ExitKindFacts, ExitMapFacts, LoopFeatureFacts, ValueJoinFacts,
|
||||
CleanupFacts, CleanupKindFacts, ExitKindFacts, ExitMapFacts, LoopFeatureFacts,
|
||||
ValueJoinFacts,
|
||||
};
|
||||
use crate::mir::builder::control_flow::plan::facts::loop_facts::LoopFacts;
|
||||
use crate::mir::builder::control_flow::plan::facts::pattern2_break_facts::Pattern2BreakFacts;
|
||||
use crate::mir::builder::control_flow::plan::facts::scan_shapes::{
|
||||
ConditionShape, SplitScanShape, StepShape,
|
||||
};
|
||||
@ -63,6 +106,20 @@ mod tests {
|
||||
use crate::mir::MirType;
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
fn v(name: &str) -> ASTNode {
|
||||
ASTNode::Variable {
|
||||
name: name.to_string(),
|
||||
span: Span::unknown(),
|
||||
}
|
||||
}
|
||||
|
||||
fn lit_int(value: i64) -> ASTNode {
|
||||
ASTNode::Literal {
|
||||
value: LiteralValue::Integer(value),
|
||||
span: Span::unknown(),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn coreloop_v1_composes_split_scan_with_value_join() {
|
||||
let condition = ASTNode::Literal {
|
||||
@ -185,4 +242,135 @@ mod tests {
|
||||
try_compose_core_loop_v1(&mut builder, &canonical, &ctx).expect("Ok");
|
||||
assert!(composed.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn coreloop_v1_composes_pattern2_with_value_join() {
|
||||
let loop_condition = ASTNode::BinaryOp {
|
||||
operator: BinaryOperator::Less,
|
||||
left: Box::new(v("i")),
|
||||
right: Box::new(lit_int(3)),
|
||||
span: Span::unknown(),
|
||||
};
|
||||
let break_condition = ASTNode::BinaryOp {
|
||||
operator: BinaryOperator::Equal,
|
||||
left: Box::new(v("i")),
|
||||
right: Box::new(lit_int(1)),
|
||||
span: Span::unknown(),
|
||||
};
|
||||
let carrier_update_in_body = ASTNode::BinaryOp {
|
||||
operator: BinaryOperator::Add,
|
||||
left: Box::new(v("sum")),
|
||||
right: Box::new(lit_int(1)),
|
||||
span: Span::unknown(),
|
||||
};
|
||||
let loop_increment = ASTNode::BinaryOp {
|
||||
operator: BinaryOperator::Add,
|
||||
left: Box::new(v("i")),
|
||||
right: Box::new(lit_int(1)),
|
||||
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: 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: Some(Pattern2BreakFacts {
|
||||
loop_var: "i".to_string(),
|
||||
carrier_var: "sum".to_string(),
|
||||
loop_condition: loop_condition.clone(),
|
||||
break_condition,
|
||||
carrier_update_in_break: None,
|
||||
carrier_update_in_body,
|
||||
loop_increment,
|
||||
}),
|
||||
pattern2_loopbodylocal: None,
|
||||
};
|
||||
let canonical = canonicalize_loop_facts(facts);
|
||||
let mut builder = MirBuilder::new();
|
||||
builder.enter_function_for_test("coreloop_v1_pattern2".to_string());
|
||||
let i_val = builder.alloc_typed(MirType::Integer);
|
||||
let sum_val = builder.alloc_typed(MirType::Integer);
|
||||
builder
|
||||
.variable_ctx
|
||||
.variable_map
|
||||
.insert("i".to_string(), i_val);
|
||||
builder
|
||||
.variable_ctx
|
||||
.variable_map
|
||||
.insert("sum".to_string(), sum_val);
|
||||
let ctx =
|
||||
LoopPatternContext::new(&loop_condition, &[], "coreloop_v1_pattern2", false, false);
|
||||
let composed =
|
||||
try_compose_core_loop_v1(&mut builder, &canonical, &ctx).expect("Ok");
|
||||
assert!(matches!(composed, Some(crate::mir::builder::control_flow::plan::CorePlan::Loop(_))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn coreloop_v1_rejects_pattern2_with_cleanup() {
|
||||
let condition = ASTNode::Literal {
|
||||
value: LiteralValue::Bool(true),
|
||||
span: Span::unknown(),
|
||||
};
|
||||
let mut kinds_present = BTreeSet::new();
|
||||
kinds_present.insert(CleanupKindFacts::Return);
|
||||
let features = LoopFeatureFacts {
|
||||
cleanup: Some(CleanupFacts { kinds_present }),
|
||||
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: 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: Some(Pattern2BreakFacts {
|
||||
loop_var: "i".to_string(),
|
||||
carrier_var: "sum".to_string(),
|
||||
loop_condition: condition.clone(),
|
||||
break_condition: condition.clone(),
|
||||
carrier_update_in_break: None,
|
||||
carrier_update_in_body: lit_int(0),
|
||||
loop_increment: lit_int(0),
|
||||
}),
|
||||
pattern2_loopbodylocal: None,
|
||||
};
|
||||
let canonical = canonicalize_loop_facts(facts);
|
||||
let mut builder = MirBuilder::new();
|
||||
builder.enter_function_for_test("coreloop_v1_pattern2_cleanup".to_string());
|
||||
let ctx = LoopPatternContext::new(
|
||||
&condition,
|
||||
&[],
|
||||
"coreloop_v1_pattern2_cleanup",
|
||||
false,
|
||||
false,
|
||||
);
|
||||
let composed =
|
||||
try_compose_core_loop_v1(&mut builder, &canonical, &ctx).expect("Ok");
|
||||
assert!(composed.is_none());
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,6 +4,7 @@ use super::coreloop_v0::{
|
||||
try_compose_core_loop_v0_scan_with_init, try_compose_core_loop_v0_split_scan,
|
||||
};
|
||||
use super::coreloop_v1::try_compose_core_loop_v1;
|
||||
use super::coreloop_v1::try_compose_core_loop_v1_pattern2_break;
|
||||
use super::PlanNormalizer;
|
||||
use crate::mir::builder::control_flow::joinir::patterns::router::LoopPatternContext;
|
||||
use crate::mir::builder::control_flow::plan::normalize::CanonicalLoopFacts;
|
||||
@ -131,7 +132,12 @@ pub(in crate::mir::builder) fn try_release_adopt_core_plan_for_pattern2_break_su
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
match compose_coreplan_for_pattern2_break_subset(builder, facts, ctx) {
|
||||
let composed = if facts.value_join_needed {
|
||||
try_compose_core_loop_v1_pattern2_break(builder, facts, ctx)
|
||||
} else {
|
||||
compose_coreplan_for_pattern2_break_subset(builder, facts, ctx)
|
||||
};
|
||||
match composed {
|
||||
Ok(Some(core)) => Ok(Some(core)),
|
||||
Ok(None) | Err(_) => Ok(None),
|
||||
}
|
||||
@ -410,7 +416,11 @@ pub(in crate::mir::builder) fn try_shadow_adopt_core_plan(
|
||||
if facts.facts.pattern2_break.is_none() {
|
||||
return Err("pattern2 strict/dev adopt failed: facts mismatch".to_string());
|
||||
}
|
||||
let core_plan = compose_coreplan_for_pattern2_break_subset(builder, facts, ctx)?
|
||||
let core_plan = if facts.value_join_needed {
|
||||
try_compose_core_loop_v1_pattern2_break(builder, facts, ctx)?
|
||||
} else {
|
||||
compose_coreplan_for_pattern2_break_subset(builder, facts, ctx)?
|
||||
}
|
||||
.ok_or_else(|| "pattern2 strict/dev adopt failed: compose rejected".to_string())?;
|
||||
Ok(Some(ShadowAdoptOutcome {
|
||||
core_plan,
|
||||
|
||||
@ -30,7 +30,7 @@ impl super::PlanNormalizer {
|
||||
/// ```
|
||||
///
|
||||
/// Key: after_bb PHI merges break path and natural exit path carrier values.
|
||||
pub(super) fn normalize_pattern2_break(
|
||||
pub(in crate::mir::builder) fn normalize_pattern2_break(
|
||||
builder: &mut MirBuilder,
|
||||
parts: Pattern2BreakPlan,
|
||||
ctx: &LoopPatternContext,
|
||||
|
||||
Reference in New Issue
Block a user