phase29ao(p4): project exitmap presence into frag exits (unconnected)

This commit is contained in:
2025-12-30 04:57:53 +09:00
parent 59f7daa470
commit 4d962e1927
6 changed files with 206 additions and 11 deletions

View File

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

View File

@ -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<ExitKindFacts>,
from: BasicBlockId,
) -> BTreeMap<ExitKind, Vec<EdgeStub>> {
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
}