phase29an(p12): project exitmap kinds into canonical facts

This commit is contained in:
2025-12-30 03:31:59 +09:00
parent 8518f1acc5
commit fa5a891bda
2 changed files with 147 additions and 76 deletions

View File

@ -2,21 +2,31 @@
#![allow(dead_code)]
use crate::mir::builder::control_flow::plan::facts::feature_facts::ExitUsageFacts;
use crate::mir::builder::control_flow::plan::facts::feature_facts::{
ExitKindFacts, ExitUsageFacts,
};
use crate::mir::builder::control_flow::plan::facts::skeleton_facts::SkeletonKind;
use crate::mir::builder::control_flow::plan::facts::LoopFacts;
use std::collections::BTreeSet;
#[derive(Debug, Clone)]
pub(in crate::mir::builder) struct CanonicalLoopFacts {
pub facts: LoopFacts,
pub skeleton_kind: SkeletonKind,
pub exit_usage: ExitUsageFacts,
pub exit_kinds_present: BTreeSet<ExitKindFacts>,
}
pub(in crate::mir::builder) fn canonicalize_loop_facts(facts: LoopFacts) -> CanonicalLoopFacts {
CanonicalLoopFacts {
skeleton_kind: facts.skeleton.kind,
exit_usage: facts.features.exit_usage.clone(),
exit_kinds_present: facts
.features
.exit_map
.as_ref()
.map(|map| map.kinds_present.clone())
.unwrap_or_default(),
facts,
}
}
@ -25,11 +35,15 @@ pub(in crate::mir::builder) fn canonicalize_loop_facts(facts: LoopFacts) -> Cano
mod tests {
use super::canonicalize_loop_facts;
use crate::ast::{ASTNode, BinaryOperator, LiteralValue, Span};
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::scan_shapes::{
ConditionShape, StepShape,
};
use crate::mir::builder::control_flow::plan::facts::skeleton_facts::SkeletonKind;
use std::collections::BTreeSet;
fn v(name: &str) -> ASTNode {
ASTNode::Variable {
@ -47,23 +61,26 @@ mod tests {
#[test]
fn canonical_projects_skeleton_and_exit_usage() {
let mut kinds_present = BTreeSet::new();
kinds_present.insert(ExitKindFacts::Break);
kinds_present.insert(ExitKindFacts::Continue);
kinds_present.insert(ExitKindFacts::Return);
let facts = LoopFacts {
condition_shape: ConditionShape::Unknown,
step_shape: StepShape::Unknown,
skeleton: crate::mir::builder::control_flow::plan::facts::skeleton_facts::SkeletonFacts {
kind: SkeletonKind::Loop,
},
features:
crate::mir::builder::control_flow::plan::facts::feature_facts::LoopFeatureFacts {
exit_usage:
crate::mir::builder::control_flow::plan::facts::feature_facts::ExitUsageFacts {
has_break: true,
has_continue: true,
has_return: true,
},
value_join: None,
cleanup: None,
features: LoopFeatureFacts {
exit_usage: ExitUsageFacts {
has_break: true,
has_continue: true,
has_return: true,
},
exit_map: Some(ExitMapFacts { kinds_present }),
value_join: None,
cleanup: None,
},
scan_with_init: None,
split_scan: None,
pattern1_simplewhile: None,
@ -80,6 +97,34 @@ mod tests {
assert!(canonical.exit_usage.has_break);
assert!(canonical.exit_usage.has_continue);
assert!(canonical.exit_usage.has_return);
assert_eq!(canonical.exit_kinds_present.len(), 3);
assert!(canonical.exit_kinds_present.contains(&ExitKindFacts::Break));
assert!(canonical.exit_kinds_present.contains(&ExitKindFacts::Continue));
assert!(canonical.exit_kinds_present.contains(&ExitKindFacts::Return));
}
#[test]
fn canonical_projects_empty_exit_kinds_present() {
let facts = LoopFacts {
condition_shape: ConditionShape::Unknown,
step_shape: StepShape::Unknown,
skeleton: crate::mir::builder::control_flow::plan::facts::skeleton_facts::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);
assert!(canonical.exit_kinds_present.is_empty());
}
#[test]
@ -90,8 +135,7 @@ mod tests {
skeleton: crate::mir::builder::control_flow::plan::facts::skeleton_facts::SkeletonFacts {
kind: SkeletonKind::Loop,
},
features:
crate::mir::builder::control_flow::plan::facts::feature_facts::LoopFeatureFacts::default(),
features: LoopFeatureFacts::default(),
scan_with_init: None,
split_scan: None,
pattern1_simplewhile: Some(

View File

@ -4,9 +4,12 @@
use crate::ast::ASTNode;
use crate::mir::builder::control_flow::plan::facts::feature_facts::ExitUsageFacts;
use crate::mir::builder::control_flow::plan::facts::feature_facts::{
ExitKindFacts, ExitUsageFacts,
};
use crate::mir::builder::control_flow::plan::facts::skeleton_facts::SkeletonKind;
use crate::mir::builder::control_flow::plan::normalize::CanonicalLoopFacts;
use std::collections::BTreeSet;
use super::candidates::{CandidateSet, PlanCandidate};
use super::context::PlannerContext;
@ -84,7 +87,26 @@ fn infer_exit_usage(facts: &CanonicalLoopFacts) -> Option<ExitUsageFacts> {
}
#[cfg(debug_assertions)]
fn debug_assert_exit_usage_matches_plan(plan: &DomainPlan, exit_usage: &ExitUsageFacts) {
fn debug_assert_exit_usage_matches_plan(
plan: &DomainPlan,
exit_usage: &ExitUsageFacts,
exit_kinds_present: &BTreeSet<ExitKindFacts>,
) {
debug_assert_eq!(
exit_usage.has_break,
exit_kinds_present.contains(&ExitKindFacts::Break),
"exit usage break presence mismatch"
);
debug_assert_eq!(
exit_usage.has_continue,
exit_kinds_present.contains(&ExitKindFacts::Continue),
"exit usage continue presence mismatch"
);
debug_assert_eq!(
exit_usage.has_return,
exit_kinds_present.contains(&ExitKindFacts::Return),
"exit usage return presence mismatch"
);
match plan {
DomainPlan::Pattern1SimpleWhile(_) => {
debug_assert!(
@ -111,7 +133,12 @@ fn debug_assert_exit_usage_matches_plan(plan: &DomainPlan, exit_usage: &ExitUsag
}
#[cfg(not(debug_assertions))]
fn debug_assert_exit_usage_matches_plan(_plan: &DomainPlan, _exit_usage: &ExitUsageFacts) {}
fn debug_assert_exit_usage_matches_plan(
_plan: &DomainPlan,
_exit_usage: &ExitUsageFacts,
_exit_kinds_present: &BTreeSet<ExitKindFacts>,
) {
}
fn push_scan_with_init(candidates: &mut CandidateSet, facts: &CanonicalLoopFacts) {
let Some(scan) = &facts.facts.scan_with_init else {
@ -170,7 +197,7 @@ fn push_pattern2_break(candidates: &mut CandidateSet, facts: &CanonicalLoopFacts
loop_increment: pattern2.loop_increment.clone(),
promotion,
});
debug_assert_exit_usage_matches_plan(&plan, &facts.exit_usage);
debug_assert_exit_usage_matches_plan(&plan, &facts.exit_usage, &facts.exit_kinds_present);
candidates.push(PlanCandidate {
plan,
rule: "loop/pattern2_break",
@ -207,7 +234,7 @@ fn push_pattern4_continue(candidates: &mut CandidateSet, facts: &CanonicalLoopFa
carrier_updates: pattern4.carrier_updates.clone(),
loop_increment: pattern4.loop_increment.clone(),
});
debug_assert_exit_usage_matches_plan(&plan, &facts.exit_usage);
debug_assert_exit_usage_matches_plan(&plan, &facts.exit_usage, &facts.exit_kinds_present);
candidates.push(PlanCandidate {
plan,
rule: "loop/pattern4_continue",
@ -227,7 +254,7 @@ fn push_pattern5_infinite_early_exit(candidates: &mut CandidateSet, facts: &Cano
carrier_update: pattern5.carrier_update.clone(),
loop_increment: pattern5.loop_increment.clone(),
});
debug_assert_exit_usage_matches_plan(&plan, &facts.exit_usage);
debug_assert_exit_usage_matches_plan(&plan, &facts.exit_usage, &facts.exit_kinds_present);
candidates.push(PlanCandidate {
plan,
rule: "loop/pattern5_infinite_early_exit",
@ -290,7 +317,7 @@ fn push_pattern1_simplewhile(
condition: pattern1.condition.clone(),
loop_increment: pattern1.loop_increment.clone(),
});
debug_assert_exit_usage_matches_plan(&plan, &facts.exit_usage);
debug_assert_exit_usage_matches_plan(&plan, &facts.exit_usage, &facts.exit_kinds_present);
candidates.push(PlanCandidate {
plan,
rule: "loop/pattern1_simplewhile",
@ -304,7 +331,7 @@ mod tests {
ConditionShape, StepShape,
};
use crate::mir::builder::control_flow::plan::facts::feature_facts::{
ExitUsageFacts, LoopFeatureFacts,
ExitKindFacts, ExitMapFacts, ExitUsageFacts, LoopFeatureFacts,
};
use crate::mir::builder::control_flow::plan::facts::loop_facts::{
LoopFacts, ScanWithInitFacts, SplitScanFacts,
@ -337,7 +364,7 @@ mod tests {
use crate::mir::builder::control_flow::plan::normalize::canonicalize_loop_facts;
use crate::mir::builder::control_flow::plan::{Pattern2PromotionHint, Pattern5ExitKind};
use crate::ast::{ASTNode, BinaryOperator, LiteralValue, Span};
use std::collections::BTreeMap;
use std::collections::{BTreeMap, BTreeSet};
fn v(name: &str) -> ASTNode {
ASTNode::Variable {
@ -353,6 +380,30 @@ mod tests {
}
}
fn feature_facts_with_usage(exit_usage: ExitUsageFacts) -> LoopFeatureFacts {
let mut kinds_present = BTreeSet::new();
if exit_usage.has_return {
kinds_present.insert(ExitKindFacts::Return);
}
if exit_usage.has_break {
kinds_present.insert(ExitKindFacts::Break);
}
if exit_usage.has_continue {
kinds_present.insert(ExitKindFacts::Continue);
}
let exit_map = if kinds_present.is_empty() {
None
} else {
Some(ExitMapFacts { kinds_present })
};
LoopFeatureFacts {
exit_usage,
exit_map,
value_join: None,
cleanup: None,
}
}
#[test]
fn planner_builds_split_scan_plan_from_facts() {
let facts = LoopFacts {
@ -462,15 +513,11 @@ mod tests {
skeleton: SkeletonFacts {
kind: SkeletonKind::Loop,
},
features: LoopFeatureFacts {
exit_usage: ExitUsageFacts {
has_break: true,
has_continue: false,
has_return: false,
},
value_join: None,
cleanup: None,
},
features: feature_facts_with_usage(ExitUsageFacts {
has_break: true,
has_continue: false,
has_return: false,
}),
scan_with_init: Some(ScanWithInitFacts {
loop_var: "i".to_string(),
haystack: "s".to_string(),
@ -647,15 +694,11 @@ mod tests {
skeleton: SkeletonFacts {
kind: SkeletonKind::Loop,
},
features: LoopFeatureFacts {
exit_usage: ExitUsageFacts {
has_break: true,
has_continue: false,
has_return: false,
},
value_join: None,
cleanup: None,
},
features: feature_facts_with_usage(ExitUsageFacts {
has_break: true,
has_continue: false,
has_return: false,
}),
scan_with_init: None,
split_scan: None,
pattern1_simplewhile: None,
@ -702,15 +745,11 @@ mod tests {
skeleton: SkeletonFacts {
kind: SkeletonKind::Loop,
},
features: LoopFeatureFacts {
exit_usage: ExitUsageFacts {
has_break: true,
has_continue: false,
has_return: false,
},
value_join: None,
cleanup: None,
},
features: feature_facts_with_usage(ExitUsageFacts {
has_break: true,
has_continue: false,
has_return: false,
}),
scan_with_init: None,
split_scan: None,
pattern1_simplewhile: Some(Pattern1SimpleWhileFacts {
@ -834,15 +873,11 @@ mod tests {
skeleton: SkeletonFacts {
kind: SkeletonKind::Loop,
},
features: LoopFeatureFacts {
exit_usage: ExitUsageFacts {
has_break: false,
has_continue: true,
has_return: false,
},
value_join: None,
cleanup: None,
},
features: feature_facts_with_usage(ExitUsageFacts {
has_break: false,
has_continue: true,
has_return: false,
}),
scan_with_init: None,
split_scan: None,
pattern1_simplewhile: None,
@ -893,15 +928,11 @@ mod tests {
skeleton: SkeletonFacts {
kind: SkeletonKind::Loop,
},
features: LoopFeatureFacts {
exit_usage: ExitUsageFacts {
has_break: false,
has_continue: false,
has_return: true,
},
value_join: None,
cleanup: None,
},
features: feature_facts_with_usage(ExitUsageFacts {
has_break: false,
has_continue: false,
has_return: true,
}),
scan_with_init: None,
split_scan: None,
pattern1_simplewhile: None,
@ -953,15 +984,11 @@ mod tests {
skeleton: SkeletonFacts {
kind: SkeletonKind::Loop,
},
features: LoopFeatureFacts {
exit_usage: ExitUsageFacts {
has_break: true,
has_continue: false,
has_return: false,
},
value_join: None,
cleanup: None,
},
features: feature_facts_with_usage(ExitUsageFacts {
has_break: true,
has_continue: false,
has_return: false,
}),
scan_with_init: None,
split_scan: None,
pattern1_simplewhile: None,