use std::collections::BTreeSet;
use crate::cfg::{BlockRef, Cfg, DataflowFacts, GraphFacts};
use super::common::{
BranchKind, BranchRegionFact, BranchValueMergeCandidate, ShortCircuitCandidate,
ShortCircuitExit,
};
use super::helpers::collect_merge_arm_preds;
use super::phi_facts::branch_value_merges_in_block;
pub(super) fn analyze_branch_value_merges(
cfg: &Cfg,
graph_facts: &GraphFacts,
dataflow: &DataflowFacts,
branch_regions: &[BranchRegionFact],
short_circuit_candidates: &[ShortCircuitCandidate],
) -> Vec<BranchValueMergeCandidate> {
let short_circuit_merges = short_circuit_candidates
.iter()
.filter_map(|candidate| match candidate.exit {
ShortCircuitExit::ValueMerge(merge) => {
Some((candidate.header, merge, candidate.result_reg))
}
ShortCircuitExit::BranchExit { .. } => None,
})
.collect::<BTreeSet<_>>();
let mut candidates = Vec::new();
for branch_region in branch_regions {
let Some(candidate) =
analyze_branch_value_merge_candidate(dataflow, branch_region, &short_circuit_merges)
else {
continue;
};
candidates.push(candidate);
}
candidates.extend(analyze_guard_short_circuit_branch_value_merges(
cfg,
graph_facts,
dataflow,
short_circuit_candidates,
));
candidates.sort_by_key(|candidate| (candidate.header, candidate.merge));
candidates
}
fn analyze_guard_short_circuit_branch_value_merges(
cfg: &Cfg,
graph_facts: &GraphFacts,
dataflow: &DataflowFacts,
short_circuit_candidates: &[ShortCircuitCandidate],
) -> Vec<BranchValueMergeCandidate> {
let mut candidates = Vec::new();
for short in short_circuit_candidates {
let ShortCircuitExit::BranchExit { truthy, falsy } = short.exit else {
continue;
};
if !cfg.can_reach(truthy, falsy) || cfg.can_reach(falsy, truthy) {
continue;
}
let then_preds = collect_merge_arm_preds(cfg, &graph_facts.dominator_tree, truthy, falsy);
let else_preds = short.branch_exit_leaf_preds(false);
if then_preds.is_empty() || else_preds.is_empty() || !then_preds.is_disjoint(&else_preds) {
continue;
}
let values =
branch_value_merges_in_block(short.header, dataflow, falsy, &then_preds, &else_preds);
if !values.is_empty() {
candidates.push(BranchValueMergeCandidate {
header: short.header,
merge: falsy,
values,
});
}
}
candidates
}
fn analyze_branch_value_merge_candidate(
dataflow: &DataflowFacts,
branch_region: &BranchRegionFact,
short_circuit_merges: &BTreeSet<(BlockRef, BlockRef, Option<crate::transformer::Reg>)>,
) -> Option<BranchValueMergeCandidate> {
if branch_region.kind != BranchKind::IfElse {
return None;
}
let merge = branch_region.merge;
let then_preds = &branch_region.then_merge_preds;
let else_preds = &branch_region.else_merge_preds;
if then_preds.is_empty() || else_preds.is_empty() || !then_preds.is_disjoint(else_preds) {
return None;
}
let values = branch_value_merges_in_block(
branch_region.header,
dataflow,
merge,
then_preds,
else_preds,
)
.into_iter()
.filter(|value| !short_circuit_merges.contains(&(branch_region.header, merge, Some(value.reg))))
.collect::<Vec<_>>();
(!values.is_empty()).then_some(BranchValueMergeCandidate {
header: branch_region.header,
merge,
values,
})
}