use std::collections::{BTreeMap, BTreeSet};
use super::super::basic_block::{BasicBlock, BlockId, Terminator};
use super::edge::{Edge, EdgeKind};
#[derive(Debug, Clone)]
pub struct Cfg {
pub(super) blocks: BTreeMap<BlockId, BasicBlock>,
pub(super) edges: Vec<Edge>,
pub(super) entry: BlockId,
pub(super) exits: BTreeSet<BlockId>,
pub(super) successors: BTreeMap<BlockId, Vec<BlockId>>,
pub(super) predecessors: BTreeMap<BlockId, Vec<BlockId>>,
pub(super) offset_to_block: BTreeMap<usize, BlockId>,
}
impl Cfg {
#[must_use]
pub fn new() -> Self {
Self {
blocks: BTreeMap::new(),
edges: Vec::new(),
entry: BlockId::ENTRY,
exits: BTreeSet::new(),
successors: BTreeMap::new(),
predecessors: BTreeMap::new(),
offset_to_block: BTreeMap::new(),
}
}
pub fn add_block(&mut self, block: BasicBlock) {
let id = block.id;
let start_offset = block.start_offset;
if matches!(
block.terminator,
Terminator::Return | Terminator::Throw | Terminator::Abort
) {
self.exits.insert(id);
}
self.offset_to_block.insert(start_offset, id);
self.blocks.insert(id, block);
}
pub fn add_edge(&mut self, from: BlockId, to: BlockId, kind: EdgeKind) {
self.edges.push(Edge { from, to, kind });
self.successors.entry(from).or_default().push(to);
self.predecessors.entry(to).or_default().push(from);
}
#[must_use]
pub fn block(&self, id: BlockId) -> Option<&BasicBlock> {
self.blocks.get(&id)
}
pub fn block_mut(&mut self, id: BlockId) -> Option<&mut BasicBlock> {
self.blocks.get_mut(&id)
}
#[must_use]
pub fn entry_block(&self) -> Option<&BasicBlock> {
self.blocks.get(&self.entry)
}
pub fn blocks(&self) -> impl Iterator<Item = &BasicBlock> {
self.blocks.values()
}
#[must_use]
pub fn block_count(&self) -> usize {
self.blocks.len()
}
#[must_use]
pub fn edges(&self) -> &[Edge] {
&self.edges
}
#[must_use]
pub fn successors(&self, id: BlockId) -> &[BlockId] {
self.successors.get(&id).map(Vec::as_slice).unwrap_or(&[])
}
#[must_use]
pub fn predecessors(&self, id: BlockId) -> &[BlockId] {
self.predecessors.get(&id).map(Vec::as_slice).unwrap_or(&[])
}
#[must_use]
pub fn exit_blocks(&self) -> &BTreeSet<BlockId> {
&self.exits
}
#[must_use]
pub fn block_at_offset(&self, offset: usize) -> Option<&BasicBlock> {
let (_, &block_id) = self.offset_to_block.range(..=offset).next_back()?;
let block = self.blocks.get(&block_id)?;
if block.contains_offset(offset) {
Some(block)
} else {
None
}
}
}
impl Default for Cfg {
fn default() -> Self {
Self::new()
}
}