dfg,body: implement simple dfg + body
This commit is contained in:
parent
38d2bc6e45
commit
73dd1215a8
5 changed files with 772 additions and 0 deletions
|
|
@ -1,11 +1,17 @@
|
|||
//! Slonik normalized SSA IR.
|
||||
|
||||
mod body;
|
||||
mod dfg;
|
||||
mod inst;
|
||||
mod layout;
|
||||
mod petgraph;
|
||||
mod ty;
|
||||
|
||||
pub use body::*;
|
||||
pub use dfg::*;
|
||||
pub use inst::*;
|
||||
pub use layout::*;
|
||||
pub use petgraph::*;
|
||||
pub use ty::*;
|
||||
|
||||
/// Defines a thin `u32` entity handle.
|
||||
|
|
|
|||
167
src/ir/body.rs
Normal file
167
src/ir/body.rs
Normal file
|
|
@ -0,0 +1,167 @@
|
|||
//! The owning container for a normalized Slonik IR body.
|
||||
|
||||
use crate::ir::{Block, DataFlowGraph, Inst, InstructionData, Layout, Type, Value};
|
||||
|
||||
/// A normalized SSA body.
|
||||
#[derive(Default)]
|
||||
pub struct Body {
|
||||
/// Semantic storage for blocks, instructions, values, and value lists.
|
||||
pub dfg: DataFlowGraph,
|
||||
|
||||
/// Program order and instruction/block containment.
|
||||
pub layout: Layout,
|
||||
}
|
||||
|
||||
impl Body {
|
||||
/// Creates an empty body.
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
/// Returns whether the body contains no blocks.
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.dfg.num_blocks() == 0
|
||||
}
|
||||
|
||||
/// Returns the number of blocks in the body.
|
||||
pub fn block_count(&self) -> usize {
|
||||
self.dfg.num_blocks()
|
||||
}
|
||||
|
||||
/// Returns the number of instructions in the body.
|
||||
pub fn inst_count(&self) -> usize {
|
||||
self.dfg.num_insts()
|
||||
}
|
||||
|
||||
/// Returns the number of SSA values in the body.
|
||||
pub fn value_count(&self) -> usize {
|
||||
self.dfg.num_values()
|
||||
}
|
||||
|
||||
/// Creates a new block and appends it to the block layout order.
|
||||
pub fn create_block(&mut self) -> Block {
|
||||
let block = self.dfg.create_block();
|
||||
self.layout.append_block(block);
|
||||
block
|
||||
}
|
||||
|
||||
/// Appends a block parameter of type `ty` to `block`.
|
||||
///
|
||||
/// The returned SSA value is defined as a block parameter.
|
||||
pub fn append_block_param(&mut self, block: Block, ty: Type) -> Value {
|
||||
self.dfg.append_block_param(block, ty)
|
||||
}
|
||||
|
||||
/// Creates an instruction with `data`, assigns SSA result values for
|
||||
/// `result_tys`, and appends the instruction to the end of `block`.
|
||||
pub fn append_inst(
|
||||
&mut self,
|
||||
block: Block,
|
||||
data: InstructionData,
|
||||
result_tys: &[Type],
|
||||
) -> Inst {
|
||||
let inst = self.dfg.create_inst(data, result_tys);
|
||||
self.layout.append_inst(block, inst);
|
||||
inst
|
||||
}
|
||||
|
||||
/// Returns the first block in layout order, if any.
|
||||
pub fn first_block(&self) -> Option<Block> {
|
||||
self.layout.first_block()
|
||||
}
|
||||
|
||||
/// Returns the last block in layout order, if any.
|
||||
pub fn last_block(&self) -> Option<Block> {
|
||||
self.layout.last_block()
|
||||
}
|
||||
|
||||
/// Returns the last instruction in `block`, if any.
|
||||
pub fn last_inst(&self, block: Block) -> Option<Inst> {
|
||||
self.layout.last_inst(block)
|
||||
}
|
||||
|
||||
/// Returns the terminator instruction of `block`, if the block ends in one.
|
||||
pub fn block_terminator(&self, block: Block) -> Option<Inst> {
|
||||
let inst = self.layout.last_inst(block)?;
|
||||
self.dfg.inst_data(inst).is_terminator().then_some(inst)
|
||||
}
|
||||
|
||||
/// Returns the terminator data for `block`, if the block ends in a terminator.
|
||||
pub fn block_terminator_data(&self, block: Block) -> Option<&InstructionData> {
|
||||
let inst = self.block_terminator(block)?;
|
||||
Some(self.dfg.inst_data(inst))
|
||||
}
|
||||
|
||||
/// Returns an iterator over the successor blocks of `block`.
|
||||
///
|
||||
/// Successors are derived from the block's terminator instruction.
|
||||
/// Non-terminating blocks and blocks ending in `return` have no successors.
|
||||
pub fn block_successors(&self, block: Block) -> BlockSuccessors {
|
||||
let Some(term) = self.block_terminator_data(block) else {
|
||||
return BlockSuccessors::Empty;
|
||||
};
|
||||
|
||||
match term {
|
||||
InstructionData::Jump { dst, .. } => BlockSuccessors::One(Some(dst.block)),
|
||||
|
||||
InstructionData::BrIf {
|
||||
then_dst, else_dst, ..
|
||||
} => BlockSuccessors::Two {
|
||||
first: Some(then_dst.block),
|
||||
second: Some(else_dst.block),
|
||||
},
|
||||
|
||||
InstructionData::Return { .. } => BlockSuccessors::Empty,
|
||||
|
||||
_ => BlockSuccessors::Empty,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the number of CFG successors of `block`.
|
||||
pub fn block_successor_count(&self, block: Block) -> usize {
|
||||
self.block_successors(block).count()
|
||||
}
|
||||
}
|
||||
|
||||
/// Iterator over the successor blocks of a basic block.
|
||||
///
|
||||
/// This is intentionally tiny because the normalized IR currently has very
|
||||
/// simple terminators:
|
||||
///
|
||||
/// - `jump` has one successor
|
||||
/// - `br_if` has two successors
|
||||
/// - `return` has zero successors
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum BlockSuccessors {
|
||||
/// No successors.
|
||||
Empty,
|
||||
|
||||
/// Exactly one successor.
|
||||
One(Option<Block>),
|
||||
|
||||
/// Exactly two successors.
|
||||
Two {
|
||||
first: Option<Block>,
|
||||
second: Option<Block>,
|
||||
},
|
||||
}
|
||||
|
||||
impl Iterator for BlockSuccessors {
|
||||
type Item = Block;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
match self {
|
||||
Self::Empty => None,
|
||||
|
||||
Self::One(slot) => slot.take(),
|
||||
|
||||
Self::Two { first, second } => {
|
||||
if let Some(block) = first.take() {
|
||||
Some(block)
|
||||
} else {
|
||||
second.take()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
172
src/ir/dfg.rs
172
src/ir/dfg.rs
|
|
@ -1 +1,173 @@
|
|||
//! Data-flow storage for Slonik IR.
|
||||
|
||||
use cranelift_entity::{ListPool, PrimaryMap};
|
||||
|
||||
use crate::ir::{Block, Inst, InstructionData, Type, Value, ValueData, ValueDef, ValueList};
|
||||
|
||||
/// Per-block data owned by the [`DataFlowGraph`].
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct BlockData {
|
||||
/// The SSA parameters of this block.
|
||||
pub params: ValueList,
|
||||
}
|
||||
|
||||
/// Per-instruction data owned by the [`DataFlowGraph`].
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct InstData {
|
||||
/// The semantic payload of the instruction.
|
||||
pub data: InstructionData,
|
||||
|
||||
/// The SSA results defined by this instruction.
|
||||
pub results: ValueList,
|
||||
}
|
||||
|
||||
/// The semantic storage for a Slonik IR body.
|
||||
///
|
||||
/// The DFG owns all blocks, instructions, and SSA values, but does not own
|
||||
/// their order. Program order is tracked by [`crate::ir::Layout`].
|
||||
#[derive(Default)]
|
||||
pub struct DataFlowGraph {
|
||||
/// All blocks in the body.
|
||||
pub blocks: PrimaryMap<Block, BlockData>,
|
||||
|
||||
/// All instructions in the body.
|
||||
pub insts: PrimaryMap<Inst, InstData>,
|
||||
|
||||
/// All SSA values in the body.
|
||||
pub values: PrimaryMap<Value, ValueData>,
|
||||
|
||||
/// Pooled storage for compact value lists.
|
||||
pub value_lists: ListPool<Value>,
|
||||
}
|
||||
|
||||
impl DataFlowGraph {
|
||||
/// Creates an empty data-flow graph.
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
/// Returns the number of blocks in the graph.
|
||||
pub fn num_blocks(&self) -> usize {
|
||||
self.blocks.len()
|
||||
}
|
||||
|
||||
/// Returns the number of instructions in the graph.
|
||||
pub fn num_insts(&self) -> usize {
|
||||
self.insts.len()
|
||||
}
|
||||
|
||||
/// Returns the number of SSA values in the graph.
|
||||
pub fn num_values(&self) -> usize {
|
||||
self.values.len()
|
||||
}
|
||||
|
||||
/// Creates a new empty block.
|
||||
pub fn create_block(&mut self) -> Block {
|
||||
self.blocks.push(BlockData::default())
|
||||
}
|
||||
|
||||
/// Returns the data for `block`.
|
||||
pub fn block_data(&self, block: Block) -> &BlockData {
|
||||
&self.blocks[block]
|
||||
}
|
||||
|
||||
/// Returns the mutable data for `block`.
|
||||
pub fn block_data_mut(&mut self, block: Block) -> &mut BlockData {
|
||||
&mut self.blocks[block]
|
||||
}
|
||||
|
||||
/// Returns the parameter values of `block`.
|
||||
pub fn block_params(&self, block: Block) -> &[Value] {
|
||||
self.blocks[block].params.as_slice(&self.value_lists)
|
||||
}
|
||||
|
||||
/// Appends a block parameter of type `ty` to `block`.
|
||||
///
|
||||
/// The returned SSA value is defined by `ValueDef::Param(block, index)`.
|
||||
pub fn append_block_param(&mut self, block: Block, ty: Type) -> Value {
|
||||
let index = self.blocks[block].params.len(&self.value_lists) as u16;
|
||||
|
||||
let value = self.values.push(ValueData {
|
||||
ty,
|
||||
def: ValueDef::Param(block, index),
|
||||
});
|
||||
|
||||
self.blocks[block].params.push(value, &mut self.value_lists);
|
||||
value
|
||||
}
|
||||
|
||||
/// Creates a pooled value list from `values`.
|
||||
///
|
||||
/// This is mainly useful when constructing variable-arity instructions such
|
||||
/// as calls or returns.
|
||||
pub fn make_value_list(&mut self, values: &[Value]) -> ValueList {
|
||||
ValueList::from_slice(values, &mut self.value_lists)
|
||||
}
|
||||
|
||||
/// Creates a new instruction with the given semantic payload and result
|
||||
/// types.
|
||||
///
|
||||
/// One SSA result value is created for each entry in `result_tys`, in order.
|
||||
/// Those values are recorded as `ValueDef::Result(inst, index)`.
|
||||
pub fn create_inst(&mut self, data: InstructionData, result_tys: &[Type]) -> Inst {
|
||||
let inst = self.insts.push(InstData {
|
||||
data,
|
||||
results: ValueList::new(),
|
||||
});
|
||||
|
||||
let mut results = ValueList::new();
|
||||
for (index, ty) in result_tys.iter().copied().enumerate() {
|
||||
let value = self.values.push(ValueData {
|
||||
ty,
|
||||
def: ValueDef::Result(inst, index as u16),
|
||||
});
|
||||
|
||||
results.push(value, &mut self.value_lists);
|
||||
}
|
||||
|
||||
self.insts[inst].results = results;
|
||||
inst
|
||||
}
|
||||
|
||||
/// Returns the data for `inst`.
|
||||
pub fn inst_data(&self, inst: Inst) -> &InstructionData {
|
||||
&self.insts[inst].data
|
||||
}
|
||||
|
||||
/// Returns the mutable data for `inst`.
|
||||
pub fn inst_data_mut(&mut self, inst: Inst) -> &mut InstructionData {
|
||||
&mut self.insts[inst].data
|
||||
}
|
||||
|
||||
/// Replaces the semantic payload of `inst`.
|
||||
///
|
||||
/// This does not change the instruction’s existing result values.
|
||||
pub fn replace_inst_data(&mut self, inst: Inst, data: InstructionData) {
|
||||
self.insts[inst].data = data;
|
||||
}
|
||||
|
||||
/// Returns the SSA results defined by `inst`.
|
||||
pub fn inst_results(&self, inst: Inst) -> &[Value] {
|
||||
self.insts[inst].results.as_slice(&self.value_lists)
|
||||
}
|
||||
|
||||
/// Returns the first SSA result of `inst`, if any.
|
||||
pub fn first_result(&self, inst: Inst) -> Option<Value> {
|
||||
self.insts[inst].results.first(&self.value_lists)
|
||||
}
|
||||
|
||||
/// Returns the full value record for `value`.
|
||||
pub fn value_data(&self, value: Value) -> &ValueData {
|
||||
&self.values[value]
|
||||
}
|
||||
|
||||
/// Returns the type of `value`.
|
||||
pub fn value_type(&self, value: Value) -> Type {
|
||||
self.values[value].ty
|
||||
}
|
||||
|
||||
/// Returns the definition site of `value`.
|
||||
pub fn value_def(&self, value: Value) -> ValueDef {
|
||||
self.values[value].def
|
||||
}
|
||||
}
|
||||
|
|
|
|||
211
src/ir/layout.rs
Normal file
211
src/ir/layout.rs
Normal file
|
|
@ -0,0 +1,211 @@
|
|||
//! Program order and containment for Slonik IR.
|
||||
|
||||
use cranelift_entity::{SecondaryMap, packed_option::PackedOption};
|
||||
|
||||
use crate::ir::{Block, Inst};
|
||||
|
||||
/// Structural layout information for one block.
|
||||
#[derive(Copy, Clone, Debug, Default)]
|
||||
pub struct BlockNode {
|
||||
/// The previous block in layout order.
|
||||
pub prev: PackedOption<Block>,
|
||||
|
||||
/// The next block in layout order.
|
||||
pub next: PackedOption<Block>,
|
||||
|
||||
/// The first instruction in this block, if any.
|
||||
pub first_inst: PackedOption<Inst>,
|
||||
|
||||
/// The last instruction in this block, if any.
|
||||
pub last_inst: PackedOption<Inst>,
|
||||
}
|
||||
|
||||
/// Structural layout information for one instruction.
|
||||
#[derive(Copy, Clone, Debug, Default)]
|
||||
pub struct InstNode {
|
||||
/// The block containing this instruction.
|
||||
pub block: PackedOption<Block>,
|
||||
|
||||
/// The previous instruction in the containing block.
|
||||
pub prev: PackedOption<Inst>,
|
||||
|
||||
/// The next instruction in the containing block.
|
||||
pub next: PackedOption<Inst>,
|
||||
}
|
||||
|
||||
/// Program order and block membership for a body.
|
||||
#[derive(Default)]
|
||||
pub struct Layout {
|
||||
/// The first block in layout order.
|
||||
pub first_block: PackedOption<Block>,
|
||||
|
||||
/// The last block in layout order.
|
||||
pub last_block: PackedOption<Block>,
|
||||
|
||||
/// Per-block layout links.
|
||||
pub blocks: SecondaryMap<Block, BlockNode>,
|
||||
|
||||
/// Per-instruction layout links.
|
||||
pub insts: SecondaryMap<Inst, InstNode>,
|
||||
}
|
||||
|
||||
impl Layout {
|
||||
/// Creates an empty layout.
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
/// Returns whether the layout contains no blocks.
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.first_block.is_none()
|
||||
}
|
||||
|
||||
/// Appends `block` to the end of the block list.
|
||||
pub fn append_block(&mut self, block: Block) {
|
||||
let prev = self.last_block;
|
||||
|
||||
self.blocks[block].prev = prev;
|
||||
self.blocks[block].next = PackedOption::default();
|
||||
|
||||
if let Some(prev_block) = prev.expand() {
|
||||
self.blocks[prev_block].next = block.into();
|
||||
} else {
|
||||
self.first_block = block.into();
|
||||
}
|
||||
|
||||
self.last_block = block.into();
|
||||
}
|
||||
|
||||
/// Returns the first block in layout order.
|
||||
pub fn first_block(&self) -> Option<Block> {
|
||||
self.first_block.expand()
|
||||
}
|
||||
|
||||
/// Returns the last block in layout order.
|
||||
pub fn last_block(&self) -> Option<Block> {
|
||||
self.last_block.expand()
|
||||
}
|
||||
|
||||
/// Returns the previous block before `block`, if any.
|
||||
pub fn prev_block(&self, block: Block) -> Option<Block> {
|
||||
self.blocks[block].prev.expand()
|
||||
}
|
||||
|
||||
/// Returns the next block after `block`, if any.
|
||||
pub fn next_block(&self, block: Block) -> Option<Block> {
|
||||
self.blocks[block].next.expand()
|
||||
}
|
||||
|
||||
/// Returns the first instruction in `block`, if any.
|
||||
pub fn first_inst(&self, block: Block) -> Option<Inst> {
|
||||
self.blocks[block].first_inst.expand()
|
||||
}
|
||||
|
||||
/// Returns the last instruction in `block`, if any.
|
||||
pub fn last_inst(&self, block: Block) -> Option<Inst> {
|
||||
self.blocks[block].last_inst.expand()
|
||||
}
|
||||
|
||||
/// Returns the block containing `inst`, if the instruction is laid out.
|
||||
pub fn inst_block(&self, inst: Inst) -> Option<Block> {
|
||||
self.insts[inst].block.expand()
|
||||
}
|
||||
|
||||
/// Returns the previous instruction before `inst` in its containing block.
|
||||
pub fn prev_inst(&self, inst: Inst) -> Option<Inst> {
|
||||
self.insts[inst].prev.expand()
|
||||
}
|
||||
|
||||
/// Returns the next instruction after `inst` in its containing block.
|
||||
pub fn next_inst(&self, inst: Inst) -> Option<Inst> {
|
||||
self.insts[inst].next.expand()
|
||||
}
|
||||
|
||||
/// Returns whether `inst` is currently in the layout.
|
||||
pub fn is_inst_inserted(&self, inst: Inst) -> bool {
|
||||
self.insts[inst].block.is_some()
|
||||
}
|
||||
|
||||
/// Appends `inst` to the end of `block`.
|
||||
pub fn append_inst(&mut self, block: Block, inst: Inst) {
|
||||
let prev = self.blocks[block].last_inst;
|
||||
|
||||
self.insts[inst].block = block.into();
|
||||
self.insts[inst].prev = prev;
|
||||
self.insts[inst].next = PackedOption::default();
|
||||
|
||||
if let Some(prev_inst) = prev.expand() {
|
||||
self.insts[prev_inst].next = inst.into();
|
||||
} else {
|
||||
self.blocks[block].first_inst = inst.into();
|
||||
}
|
||||
|
||||
self.blocks[block].last_inst = inst.into();
|
||||
}
|
||||
}
|
||||
|
||||
/// Iterator over blocks in layout order.
|
||||
#[derive(Clone)]
|
||||
pub struct Blocks<'a> {
|
||||
layout: &'a Layout,
|
||||
cur: Option<Block>,
|
||||
}
|
||||
|
||||
impl<'a> Blocks<'a> {
|
||||
/// Creates a block iterator starting at the first block.
|
||||
pub fn new(layout: &'a Layout) -> Self {
|
||||
Self {
|
||||
layout,
|
||||
cur: layout.first_block(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for Blocks<'_> {
|
||||
type Item = Block;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let block = self.cur?;
|
||||
self.cur = self.layout.next_block(block);
|
||||
Some(block)
|
||||
}
|
||||
}
|
||||
|
||||
/// Iterator over instructions in one block.
|
||||
#[derive(Clone)]
|
||||
pub struct Insts<'a> {
|
||||
layout: &'a Layout,
|
||||
cur: Option<Inst>,
|
||||
}
|
||||
|
||||
impl<'a> Insts<'a> {
|
||||
/// Creates an instruction iterator starting at the first instruction in `block`.
|
||||
pub fn new(layout: &'a Layout, block: Block) -> Self {
|
||||
Self {
|
||||
layout,
|
||||
cur: layout.first_inst(block),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for Insts<'_> {
|
||||
type Item = Inst;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let inst = self.cur?;
|
||||
self.cur = self.layout.next_inst(inst);
|
||||
Some(inst)
|
||||
}
|
||||
}
|
||||
|
||||
impl Layout {
|
||||
/// Returns an iterator over all blocks in layout order.
|
||||
pub fn blocks(&self) -> Blocks<'_> {
|
||||
Blocks::new(self)
|
||||
}
|
||||
|
||||
/// Returns an iterator over the instructions in `block`.
|
||||
pub fn insts(&self, block: Block) -> Insts<'_> {
|
||||
Insts::new(self, block)
|
||||
}
|
||||
}
|
||||
216
src/ir/petgraph.rs
Normal file
216
src/ir/petgraph.rs
Normal file
|
|
@ -0,0 +1,216 @@
|
|||
//! [`petgraph`] adapters for Slonik.
|
||||
|
||||
use std::collections::HashSet;
|
||||
|
||||
use cranelift_entity::EntityRef;
|
||||
use petgraph::{
|
||||
Directed, Direction,
|
||||
visit::{
|
||||
GraphBase, GraphProp, GraphRef, IntoNeighbors, IntoNeighborsDirected, IntoNodeIdentifiers,
|
||||
NodeCompactIndexable, NodeCount, NodeIndexable, Visitable,
|
||||
},
|
||||
};
|
||||
|
||||
use crate::ir::{Block, BlockSuccessors, Body};
|
||||
|
||||
/// A borrowed control-flow graph view over a [`Body`].
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct CfgView<'a> {
|
||||
body: &'a Body,
|
||||
}
|
||||
|
||||
impl<'a> CfgView<'a> {
|
||||
/// Creates a borrowed CFG view over `body`.
|
||||
pub const fn new(body: &'a Body) -> Self {
|
||||
Self { body }
|
||||
}
|
||||
|
||||
/// Returns the underlying IR body.
|
||||
pub const fn body(self) -> &'a Body {
|
||||
self.body
|
||||
}
|
||||
}
|
||||
|
||||
impl Body {
|
||||
/// Returns a borrowed `petgraph`-compatible CFG view.
|
||||
pub const fn cfg(&self) -> CfgView<'_> {
|
||||
CfgView::new(self)
|
||||
}
|
||||
}
|
||||
|
||||
/// Iterator over blocks in layout order.
|
||||
#[derive(Clone)]
|
||||
pub struct Blocks<'a> {
|
||||
body: &'a Body,
|
||||
cur: Option<Block>,
|
||||
}
|
||||
|
||||
impl<'a> Blocks<'a> {
|
||||
fn new(body: &'a Body) -> Self {
|
||||
Self {
|
||||
body,
|
||||
cur: body.layout.first_block.expand(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for Blocks<'_> {
|
||||
type Item = Block;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let cur = self.cur?;
|
||||
self.cur = self.body.layout.blocks[cur].next.expand();
|
||||
Some(cur)
|
||||
}
|
||||
}
|
||||
|
||||
/// Iterator over outgoing CFG neighbors.
|
||||
#[derive(Clone)]
|
||||
pub struct OutNeighbors {
|
||||
iter: BlockSuccessors,
|
||||
}
|
||||
|
||||
impl OutNeighbors {
|
||||
fn new(body: &Body, block: Block) -> Self {
|
||||
Self {
|
||||
iter: body.block_successors(block),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for OutNeighbors {
|
||||
type Item = Block;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.iter.next()
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: cached predecessor map?
|
||||
/// Iterator over incoming CFG neighbors.
|
||||
#[derive(Clone)]
|
||||
pub struct InNeighbors<'a> {
|
||||
body: &'a Body,
|
||||
cur: Option<Block>,
|
||||
target: Block,
|
||||
}
|
||||
|
||||
impl<'a> InNeighbors<'a> {
|
||||
fn new(body: &'a Body, target: Block) -> Self {
|
||||
Self {
|
||||
body,
|
||||
cur: body.layout.first_block(),
|
||||
target,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for InNeighbors<'_> {
|
||||
type Item = Block;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
while let Some(block) = self.cur {
|
||||
self.cur = self.body.layout.next_block(block);
|
||||
|
||||
if self
|
||||
.body
|
||||
.block_successors(block)
|
||||
.any(|succ| succ == self.target)
|
||||
{
|
||||
return Some(block);
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Iterator over neighbors in a requested direction.
|
||||
#[derive(Clone)]
|
||||
pub enum Neighbors<'a> {
|
||||
Out(OutNeighbors),
|
||||
In(InNeighbors<'a>),
|
||||
}
|
||||
|
||||
impl Iterator for Neighbors<'_> {
|
||||
type Item = Block;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
match self {
|
||||
Self::Out(iter) => iter.next(),
|
||||
Self::In(iter) => iter.next(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl GraphBase for CfgView<'_> {
|
||||
type NodeId = Block;
|
||||
type EdgeId = (Block, Block);
|
||||
}
|
||||
|
||||
impl GraphRef for CfgView<'_> {}
|
||||
|
||||
impl GraphProp for CfgView<'_> {
|
||||
type EdgeType = Directed;
|
||||
}
|
||||
|
||||
impl NodeCount for CfgView<'_> {
|
||||
fn node_count(&self) -> usize {
|
||||
self.body.block_count()
|
||||
}
|
||||
}
|
||||
|
||||
impl NodeIndexable for CfgView<'_> {
|
||||
fn node_bound(&self) -> usize {
|
||||
self.body.block_count()
|
||||
}
|
||||
|
||||
fn to_index(&self, a: Self::NodeId) -> usize {
|
||||
a.index()
|
||||
}
|
||||
|
||||
fn from_index(&self, i: usize) -> Self::NodeId {
|
||||
Block::new(i)
|
||||
}
|
||||
}
|
||||
|
||||
impl NodeCompactIndexable for CfgView<'_> {}
|
||||
|
||||
impl<'a> IntoNodeIdentifiers for CfgView<'a> {
|
||||
type NodeIdentifiers = Blocks<'a>;
|
||||
|
||||
fn node_identifiers(self) -> Self::NodeIdentifiers {
|
||||
Blocks::new(self.body)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoNeighbors for CfgView<'a> {
|
||||
type Neighbors = OutNeighbors;
|
||||
|
||||
fn neighbors(self, a: Self::NodeId) -> Self::Neighbors {
|
||||
OutNeighbors::new(self.body, a)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoNeighborsDirected for CfgView<'a> {
|
||||
type NeighborsDirected = Neighbors<'a>;
|
||||
|
||||
fn neighbors_directed(self, n: Self::NodeId, d: Direction) -> Self::NeighborsDirected {
|
||||
match d {
|
||||
Direction::Outgoing => Neighbors::Out(OutNeighbors::new(self.body, n)),
|
||||
Direction::Incoming => Neighbors::In(InNeighbors::new(self.body, n)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Visitable for CfgView<'_> {
|
||||
type Map = HashSet<Block>;
|
||||
|
||||
fn visit_map(&self) -> Self::Map {
|
||||
HashSet::with_capacity(self.body.block_count())
|
||||
}
|
||||
|
||||
fn reset_map(&self, map: &mut Self::Map) {
|
||||
map.clear();
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue