use crate::graphs::scc::{Scc, SccExit, SccInfo};
use rustc_data_structures::fx::FxHashSet;
use rustc_middle::{
mir::{BasicBlock, Terminator},
ty::TyCtxt,
};
use rustc_span::def_id::DefId;
#[derive(Debug, Clone)]
pub struct CfgBlock {
pub index: usize,
pub is_cleanup: bool,
pub next: FxHashSet<usize>,
pub scc: SccInfo,
}
impl CfgBlock {
pub fn new(index: usize, is_cleanup: bool) -> Self {
Self {
index,
is_cleanup,
next: FxHashSet::default(),
scc: SccInfo::new(index),
}
}
pub fn add_next(&mut self, index: usize) {
self.next.insert(index);
}
}
#[derive(Clone)]
pub struct ControlFlowGraph<'tcx> {
pub def_id: DefId,
pub tcx: TyCtxt<'tcx>,
pub blocks: Vec<CfgBlock>,
}
impl<'tcx> ControlFlowGraph<'tcx> {
pub fn new(def_id: DefId, tcx: TyCtxt<'tcx>, blocks: Vec<CfgBlock>) -> Self {
Self {
def_id,
tcx,
blocks,
}
}
pub fn block(&self, index: usize) -> &CfgBlock {
&self.blocks[index]
}
pub fn block_mut(&mut self, index: usize) -> &mut CfgBlock {
&mut self.blocks[index]
}
pub fn terminator(&self, index: usize) -> Option<&Terminator<'tcx>> {
let body = self.tcx.optimized_mir(self.def_id);
body.basic_blocks
.get(BasicBlock::from(index))
.and_then(|bb| bb.terminator.as_ref())
}
}
fn record_root_exits<'tcx>(
graph: &mut ControlFlowGraph<'tcx>,
root: usize,
scc_components: &[usize],
) {
let nexts = graph.block(root).next.clone();
for next in nexts {
if !scc_components.contains(&next) {
graph
.block_mut(root)
.scc
.exits
.insert(SccExit::new(root, next));
}
}
}
fn record_member_nodes<'tcx>(
graph: &mut ControlFlowGraph<'tcx>,
root: usize,
scc_components: &[usize],
) {
for &node in &scc_components[1..] {
graph.block_mut(root).scc.nodes.insert(node);
graph.block_mut(node).scc.enter = root;
let nexts = graph.block(node).next.clone();
for next in nexts {
if !scc_components.contains(&next) {
graph
.block_mut(root)
.scc
.exits
.insert(SccExit::new(node, next));
}
if next == root && !graph.block(root).scc.backedges.contains(&(node, root)) {
graph.block_mut(root).scc.backedges.push((node, root));
}
}
}
}
fn rerun_scc_in_isolation<'tcx>(
graph: &mut ControlFlowGraph<'tcx>,
root: usize,
_scc_components: &[usize],
) {
let scc_exits = graph.block(root).scc.exits.clone();
let backedges = graph.block(root).scc.backedges.clone();
let mut backups: Vec<(usize, FxHashSet<usize>)> = Vec::new();
let block0 = graph.block_mut(0);
backups.push((0, block0.next.clone()));
block0.next.clear();
block0.next.insert(root);
for &(node, target) in &backedges {
if target != root {
continue;
}
let block = graph.block_mut(node);
backups.push((node, block.next.clone()));
block.next.remove(&root);
}
for exit in &scc_exits {
let block_to = graph.block_mut(exit.to);
backups.push((exit.to, block_to.next.clone()));
block_to.next.clear();
}
graph.find_scc();
for (idx, saved_next) in backups {
graph.block_mut(idx).next = saved_next;
}
}
fn scc_handler<'tcx>(graph: &mut ControlFlowGraph<'tcx>, root: usize, scc_components: &[usize]) {
rap_debug!(
"Scc found: root = {}, components = {:?}",
root,
scc_components
);
graph.block_mut(root).scc.enter = root;
if scc_components.len() <= 1 {
return;
}
record_root_exits(graph, root, scc_components);
record_member_nodes(graph, root, scc_components);
rap_debug!("Scc Info: {:?}", graph.block(root).scc);
rerun_scc_in_isolation(graph, root, scc_components);
}
impl<'tcx> Scc for ControlFlowGraph<'tcx> {
fn on_scc_found(&mut self, root: usize, scc_components: &[usize]) {
scc_handler(self, root, scc_components);
}
fn get_next(&mut self, root: usize) -> FxHashSet<usize> {
self.block(root).next.clone()
}
fn get_size(&mut self) -> usize {
self.blocks.len()
}
}