use std::collections::{BTreeMap, BTreeSet};
use crate::cfg::{BlockRef, DefId, EdgeRef, PhiId};
use crate::transformer::{InstrRef, Reg, RegRange};
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct StructureFacts {
pub branch_candidates: Vec<BranchCandidate>,
pub branch_region_facts: Vec<BranchRegionFact>,
pub branch_value_merge_candidates: Vec<BranchValueMergeCandidate>,
pub generic_phi_materializations: Vec<GenericPhiMaterialization>,
pub loop_candidates: Vec<LoopCandidate>,
pub short_circuit_candidates: Vec<ShortCircuitCandidate>,
pub goto_requirements: Vec<GotoRequirement>,
pub region_facts: Vec<RegionFact>,
pub scope_candidates: Vec<ScopeCandidate>,
pub children: Vec<StructureFacts>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Hash)]
pub struct GenericPhiMaterialization {
pub block: BlockRef,
pub phi_id: PhiId,
pub reg: Reg,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct BranchCandidate {
pub header: BlockRef,
pub then_entry: BlockRef,
pub else_entry: Option<BlockRef>,
pub merge: Option<BlockRef>,
pub kind: BranchKind,
pub invert_hint: bool,
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub enum BranchKind {
IfThen,
IfElse,
Guard,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct BranchRegionFact {
pub header: BlockRef,
pub merge: BlockRef,
pub kind: BranchKind,
pub flow_blocks: BTreeSet<BlockRef>,
pub structured_blocks: BTreeSet<BlockRef>,
pub then_merge_preds: BTreeSet<BlockRef>,
pub else_merge_preds: BTreeSet<BlockRef>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(super) struct IrreducibleRegion {
pub entry: BlockRef,
pub blocks: BTreeSet<BlockRef>,
pub entry_edges: Vec<EdgeRef>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct BranchValueMergeCandidate {
pub header: BlockRef,
pub merge: BlockRef,
pub values: Vec<BranchValueMergeValue>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct BranchValueMergeValue {
pub phi_id: PhiId,
pub reg: Reg,
pub then_arm: BranchValueMergeArm,
pub else_arm: BranchValueMergeArm,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct BranchValueMergeArm {
pub preds: BTreeSet<BlockRef>,
pub defs: BTreeSet<DefId>,
pub non_header_defs: BTreeSet<DefId>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct LoopCandidate {
pub header: BlockRef,
pub preheader: Option<BlockRef>,
pub blocks: BTreeSet<BlockRef>,
pub backedges: Vec<EdgeRef>,
pub exits: BTreeSet<BlockRef>,
pub continue_target: Option<BlockRef>,
pub kind_hint: LoopKindHint,
pub source_bindings: Option<LoopSourceBindings>,
pub header_value_merges: Vec<LoopValueMerge>,
pub exit_value_merges: Vec<LoopExitValueMergeCandidate>,
pub reducible: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LoopSourceBindings {
Numeric(Reg),
Generic(RegRange),
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct LoopValueIncoming {
pub pred: BlockRef,
pub defs: BTreeSet<DefId>,
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct LoopValueArm {
pub incomings: Vec<LoopValueIncoming>,
}
impl LoopValueArm {
pub fn is_empty(&self) -> bool {
self.incomings.is_empty()
}
pub fn contains_pred(&self, pred: BlockRef) -> bool {
self.incomings.iter().any(|incoming| incoming.pred == pred)
}
pub fn incoming_for_pred(&self, pred: BlockRef) -> Option<&LoopValueIncoming> {
self.incomings.iter().find(|incoming| incoming.pred == pred)
}
pub fn preds(&self) -> impl Iterator<Item = BlockRef> + '_ {
self.incomings.iter().map(|incoming| incoming.pred)
}
pub fn defs(&self) -> impl Iterator<Item = DefId> + '_ {
self.incomings
.iter()
.flat_map(|incoming| incoming.defs.iter().copied())
}
pub fn all_preds_within(&self, allowed_blocks: &BTreeSet<BlockRef>) -> bool {
self.incomings
.iter()
.all(|incoming| allowed_blocks.contains(&incoming.pred))
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct LoopValueMerge {
pub phi_id: PhiId,
pub reg: Reg,
pub inside_arm: LoopValueArm,
pub outside_arm: LoopValueArm,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct LoopExitValueMergeCandidate {
pub exit: BlockRef,
pub values: Vec<LoopValueMerge>,
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub enum LoopKindHint {
WhileLike,
RepeatLike,
NumericForLike,
GenericForLike,
Unknown,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ShortCircuitCandidate {
pub header: BlockRef,
pub blocks: BTreeSet<BlockRef>,
pub entry: ShortCircuitNodeRef,
pub nodes: Vec<ShortCircuitNode>,
pub exit: ShortCircuitExit,
pub result_reg: Option<Reg>,
pub result_phi_id: Option<PhiId>,
pub entry_defs: BTreeSet<DefId>,
pub value_incomings: Vec<ShortCircuitValueIncoming>,
pub reducible: bool,
}
impl ShortCircuitCandidate {
pub(crate) fn branch_exit_leaf_preds(&self, want_truthy: bool) -> BTreeSet<BlockRef> {
self.nodes
.iter()
.filter_map(|node| {
let matches_exit = if want_truthy {
matches!(&node.truthy, ShortCircuitTarget::TruthyExit)
|| matches!(&node.falsy, ShortCircuitTarget::TruthyExit)
} else {
matches!(&node.truthy, ShortCircuitTarget::FalsyExit)
|| matches!(&node.falsy, ShortCircuitTarget::FalsyExit)
};
matches_exit.then_some(node.header)
})
.collect()
}
pub(crate) fn value_truthiness_leaves(
&self,
) -> Option<(BTreeSet<BlockRef>, BTreeSet<BlockRef>)> {
let ShortCircuitExit::ValueMerge(_) = self.exit else {
return None;
};
fn collect_truthy_value_leaves(
short: &ShortCircuitCandidate,
node_ref: ShortCircuitNodeRef,
truthy_memo: &mut BTreeMap<ShortCircuitNodeRef, BTreeSet<BlockRef>>,
falsy_memo: &mut BTreeMap<ShortCircuitNodeRef, BTreeSet<BlockRef>>,
) -> Option<BTreeSet<BlockRef>> {
if let Some(leaves) = truthy_memo.get(&node_ref) {
return Some(leaves.clone());
}
let node = short.nodes.get(node_ref.index())?;
let leaves =
collect_target_value_leaves(short, &node.truthy, true, truthy_memo, falsy_memo)?;
Some(truthy_memo.entry(node_ref).or_insert(leaves).clone())
}
fn collect_falsy_value_leaves(
short: &ShortCircuitCandidate,
node_ref: ShortCircuitNodeRef,
truthy_memo: &mut BTreeMap<ShortCircuitNodeRef, BTreeSet<BlockRef>>,
falsy_memo: &mut BTreeMap<ShortCircuitNodeRef, BTreeSet<BlockRef>>,
) -> Option<BTreeSet<BlockRef>> {
if let Some(leaves) = falsy_memo.get(&node_ref) {
return Some(leaves.clone());
}
let node = short.nodes.get(node_ref.index())?;
let leaves =
collect_target_value_leaves(short, &node.falsy, false, truthy_memo, falsy_memo)?;
Some(falsy_memo.entry(node_ref).or_insert(leaves).clone())
}
fn collect_target_value_leaves(
short: &ShortCircuitCandidate,
target: &ShortCircuitTarget,
want_truthy: bool,
truthy_memo: &mut BTreeMap<ShortCircuitNodeRef, BTreeSet<BlockRef>>,
falsy_memo: &mut BTreeMap<ShortCircuitNodeRef, BTreeSet<BlockRef>>,
) -> Option<BTreeSet<BlockRef>> {
match target {
ShortCircuitTarget::Node(next_ref) => {
if want_truthy {
collect_truthy_value_leaves(short, *next_ref, truthy_memo, falsy_memo)
} else {
collect_falsy_value_leaves(short, *next_ref, truthy_memo, falsy_memo)
}
}
ShortCircuitTarget::Value(block) => Some(BTreeSet::from([*block])),
ShortCircuitTarget::TruthyExit | ShortCircuitTarget::FalsyExit => None,
}
}
let mut truthy_memo = BTreeMap::new();
let mut falsy_memo = BTreeMap::new();
let truthy_leaves =
collect_truthy_value_leaves(self, self.entry, &mut truthy_memo, &mut falsy_memo)?;
let falsy_leaves =
collect_falsy_value_leaves(self, self.entry, &mut truthy_memo, &mut falsy_memo)?;
Some((truthy_leaves, falsy_leaves))
}
pub(crate) fn node_depths(&self) -> BTreeMap<ShortCircuitNodeRef, usize> {
let mut depths = BTreeMap::new();
let mut worklist = vec![(self.entry, 0usize)];
while let Some((node_ref, depth)) = worklist.pop() {
if depths
.get(&node_ref)
.is_some_and(|known_depth| *known_depth <= depth)
{
continue;
}
depths.insert(node_ref, depth);
let Some(node) = self.nodes.get(node_ref.index()) else {
continue;
};
if let ShortCircuitTarget::Node(next_ref) = node.truthy {
worklist.push((next_ref, depth + 1));
}
if let ShortCircuitTarget::Node(next_ref) = node.falsy {
worklist.push((next_ref, depth + 1));
}
}
depths
}
pub(crate) fn node_leaves(
&self,
node_ref: ShortCircuitNodeRef,
memo: &mut BTreeMap<ShortCircuitNodeRef, BTreeSet<BlockRef>>,
) -> BTreeSet<BlockRef> {
if let Some(leaves) = memo.get(&node_ref) {
return leaves.clone();
}
let Some(node) = self.nodes.get(node_ref.index()) else {
return BTreeSet::new();
};
let mut leaves = self.target_leaves(&node.truthy, memo);
leaves.extend(self.target_leaves(&node.falsy, memo));
memo.entry(node_ref).or_insert(leaves).clone()
}
fn target_leaves(
&self,
target: &ShortCircuitTarget,
memo: &mut BTreeMap<ShortCircuitNodeRef, BTreeSet<BlockRef>>,
) -> BTreeSet<BlockRef> {
match target {
ShortCircuitTarget::Node(next_ref) => self.node_leaves(*next_ref, memo),
ShortCircuitTarget::Value(block) => BTreeSet::from([*block]),
ShortCircuitTarget::TruthyExit | ShortCircuitTarget::FalsyExit => BTreeSet::new(),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ShortCircuitValueIncoming {
pub pred: BlockRef,
pub defs: BTreeSet<DefId>,
pub latest_local_def: Option<DefId>,
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct ShortCircuitNodeRef(pub usize);
impl ShortCircuitNodeRef {
pub const fn index(self) -> usize {
self.0
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ShortCircuitNode {
pub id: ShortCircuitNodeRef,
pub header: BlockRef,
pub truthy: ShortCircuitTarget,
pub falsy: ShortCircuitTarget,
}
#[derive(Debug, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
pub enum ShortCircuitTarget {
Node(ShortCircuitNodeRef),
Value(BlockRef),
TruthyExit,
FalsyExit,
}
#[derive(Debug, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
pub enum ShortCircuitExit {
ValueMerge(BlockRef),
BranchExit { truthy: BlockRef, falsy: BlockRef },
}
#[derive(Debug, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
pub struct GotoRequirement {
pub from: BlockRef,
pub to: BlockRef,
pub reason: GotoReason,
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub enum GotoReason {
IrreducibleFlow,
CrossStructureJump,
MultiEntryRegion,
UnstructuredBreakLike,
UnstructuredContinueLike,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct RegionFact {
pub blocks: BTreeSet<BlockRef>,
pub entry: BlockRef,
pub exits: BTreeSet<BlockRef>,
pub kind: RegionKind,
pub reducible: bool,
pub structureable: bool,
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub enum RegionKind {
Linear,
BranchRegion,
LoopRegion,
Irreducible,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ScopeCandidate {
pub entry: BlockRef,
pub exit: Option<BlockRef>,
pub close_points: Vec<InstrRef>,
pub kind: ScopeKind,
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub enum ScopeKind {
BlockScope,
LoopScope,
BranchScope,
}