use core::fmt;
use smallvec::SmallVec;
use crate::{BlockRef, adt::SmallDenseMap};
#[derive(Copy, Clone, PartialEq, Eq)]
pub enum CfgUpdateKind {
Insert,
Delete,
}
#[derive(Copy, Clone, PartialEq, Eq)]
pub struct CfgUpdate {
kind: CfgUpdateKind,
from: BlockRef,
to: BlockRef,
}
impl CfgUpdate {
#[inline(always)]
pub const fn kind(&self) -> CfgUpdateKind {
self.kind
}
#[inline(always)]
pub const fn from(&self) -> BlockRef {
self.from
}
#[inline(always)]
pub const fn to(&self) -> BlockRef {
self.to
}
}
impl fmt::Debug for CfgUpdate {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct(match self.kind {
CfgUpdateKind::Insert => "Insert",
CfgUpdateKind::Delete => "Delete",
})
.field("from", &self.from)
.field("to", &self.to)
.finish()
}
}
#[derive(Default, Clone)]
struct DeletesInserts {
deletes: SmallVec<[BlockRef; 2]>,
inserts: SmallVec<[BlockRef; 2]>,
}
impl DeletesInserts {
pub fn di(&self, is_insert: bool) -> &SmallVec<[BlockRef; 2]> {
if is_insert {
&self.inserts
} else {
&self.deletes
}
}
pub fn di_mut(&mut self, is_insert: bool) -> &mut SmallVec<[BlockRef; 2]> {
if is_insert {
&mut self.inserts
} else {
&mut self.deletes
}
}
}
pub trait GraphDiff {
fn is_empty(&self) -> bool;
fn legalized_updates(&self) -> &[CfgUpdate];
fn num_legalized_updates(&self) -> usize {
self.legalized_updates().len()
}
fn pop_update_for_incremental_updates(&mut self) -> CfgUpdate;
fn get_children<const INVERSE_EDGE: bool>(&self, node: BlockRef) -> SmallVec<[BlockRef; 8]>;
}
#[derive(Clone)]
pub struct CfgDiff<const INVERSE_GRAPH: bool = false> {
succ: SmallDenseMap<BlockRef, DeletesInserts>,
pred: SmallDenseMap<BlockRef, DeletesInserts>,
updated_are_reverse_applied: bool,
legalized_updates: SmallVec<[CfgUpdate; 4]>,
}
impl<const INVERSE_GRAPH: bool> Default for CfgDiff<INVERSE_GRAPH> {
fn default() -> Self {
Self {
succ: Default::default(),
pred: Default::default(),
updated_are_reverse_applied: false,
legalized_updates: Default::default(),
}
}
}
impl<const INVERSE_GRAPH: bool> CfgDiff<INVERSE_GRAPH> {
pub fn new<I>(updates: I, reverse_apply_updates: bool) -> Self
where
I: ExactSizeIterator<Item = CfgUpdate>,
{
let mut this = Self {
legalized_updates: legalize_updates(updates, INVERSE_GRAPH, false),
..Default::default()
};
for update in this.legalized_updates.iter() {
let is_insert = matches!(update.kind(), CfgUpdateKind::Insert) || reverse_apply_updates;
this.succ.entry(update.from).or_default().di_mut(is_insert).push(update.to);
this.pred.entry(update.to).or_default().di_mut(is_insert).push(update.from);
}
this.updated_are_reverse_applied = reverse_apply_updates;
this
}
}
impl<const INVERSE_GRAPH: bool> GraphDiff for CfgDiff<INVERSE_GRAPH> {
fn is_empty(&self) -> bool {
self.succ.is_empty() && self.pred.is_empty() && self.legalized_updates.is_empty()
}
#[inline(always)]
fn legalized_updates(&self) -> &[CfgUpdate] {
&self.legalized_updates
}
fn pop_update_for_incremental_updates(&mut self) -> CfgUpdate {
assert!(!self.legalized_updates.is_empty(), "no updates to apply");
let update = self.legalized_updates.pop().unwrap();
let is_insert =
matches!(update.kind(), CfgUpdateKind::Insert) || self.updated_are_reverse_applied;
let succ_di_list = &mut self.succ[&update.from];
let is_empty = {
let succ_list = succ_di_list.di_mut(is_insert);
assert_eq!(succ_list.last(), Some(&update.to));
succ_list.pop();
succ_list.is_empty()
};
if is_empty && succ_di_list.di(!is_insert).is_empty() {
self.succ.remove(&update.from);
}
let pred_di_list = &mut self.pred[&update.to];
let pred_list = pred_di_list.di_mut(is_insert);
assert_eq!(pred_list.last(), Some(&update.from));
pred_list.pop();
if pred_list.is_empty() && pred_di_list.di(!is_insert).is_empty() {
self.pred.remove(&update.to);
}
update
}
fn get_children<const INVERSE_EDGE: bool>(&self, node: BlockRef) -> SmallVec<[BlockRef; 8]> {
let mut r = crate::dominance::nca::get_children::<INVERSE_EDGE>(node);
if !INVERSE_EDGE {
r.reverse();
}
let children = if INVERSE_EDGE != INVERSE_GRAPH {
&self.pred
} else {
&self.succ
};
let Some(found) = children.get(&node) else {
return r;
};
for child in found.di(false) {
r.retain(|c| c != child);
}
r.extend(found.di(true).iter().cloned());
r
}
}
fn legalize_updates<I>(
all_updates: I,
inverse_graph: bool,
reverse_result_order: bool,
) -> SmallVec<[CfgUpdate; 4]>
where
I: ExactSizeIterator<Item = CfgUpdate>,
{
#[derive(Default, Copy, Clone)]
struct UpdateOp {
num_insertions: i32,
index: u32,
}
let mut operations =
SmallDenseMap::<(BlockRef, BlockRef), UpdateOp, 4>::with_capacity(all_updates.len());
for (
i,
CfgUpdate {
kind,
mut from,
mut to,
},
) in all_updates.enumerate()
{
if inverse_graph {
core::mem::swap(&mut from, &mut to);
}
operations
.entry((from, to))
.or_insert_with(|| UpdateOp {
num_insertions: 0,
index: i as u32,
})
.num_insertions += match kind {
CfgUpdateKind::Insert => 1,
CfgUpdateKind::Delete => -1,
};
}
let mut result = SmallVec::<[CfgUpdate; 4]>::with_capacity(operations.len());
for (&(from, to), update_op) in operations.iter() {
assert!(update_op.num_insertions.abs() <= 1, "unbalanced operations!");
if update_op.num_insertions == 0 {
continue;
}
let kind = if update_op.num_insertions > 0 {
CfgUpdateKind::Insert
} else {
CfgUpdateKind::Delete
};
result.push(CfgUpdate { kind, from, to });
}
result.sort_by(|a, b| {
let op_a = &operations[&(a.from, a.to)];
let op_b = &operations[&(b.from, b.to)];
if reverse_result_order {
op_a.index.cmp(&op_b.index)
} else {
op_a.index.cmp(&op_b.index).reverse()
}
});
result
}