From fa5a891bdaddfa970f9c33f3175bba4c3f331a84 Mon Sep 17 00:00:00 2001 From: tomoaki Date: Tue, 30 Dec 2025 03:31:59 +0900 Subject: [PATCH] phase29an(p12): project exitmap kinds into canonical facts --- .../plan/normalize/canonicalize.rs | 70 ++++++-- .../control_flow/plan/planner/build.rs | 153 ++++++++++-------- 2 files changed, 147 insertions(+), 76 deletions(-) diff --git a/src/mir/builder/control_flow/plan/normalize/canonicalize.rs b/src/mir/builder/control_flow/plan/normalize/canonicalize.rs index 1715d387..93f85fce 100644 --- a/src/mir/builder/control_flow/plan/normalize/canonicalize.rs +++ b/src/mir/builder/control_flow/plan/normalize/canonicalize.rs @@ -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, } 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( diff --git a/src/mir/builder/control_flow/plan/planner/build.rs b/src/mir/builder/control_flow/plan/planner/build.rs index 1e289474..f7845a99 100644 --- a/src/mir/builder/control_flow/plan/planner/build.rs +++ b/src/mir/builder/control_flow/plan/planner/build.rs @@ -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 { } #[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, +) { + 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, +) { +} 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,