Mass rename Ebb and relatives to Block (#1365)

* Manually rename BasicBlock to BlockPredecessor

BasicBlock is a pair of (Ebb, Inst) that is used to represent the
basic block subcomponent of an Ebb that is a predecessor to an Ebb.

Eventually we will be able to remove this struct, but for now it
makes sense to give it a non-conflicting name so that we can start
to transition Ebb to represent a basic block.

I have not updated any comments that refer to BasicBlock, as
eventually we will remove BlockPredecessor and replace with Block,
which is a basic block, so the comments will become correct.

* Manually rename SSABuilder block types to avoid conflict

SSABuilder has its own Block and BlockData types. These along with
associated identifier will cause conflicts in a later commit, so
they are renamed to be more verbose here.

* Automatically rename 'Ebb' to 'Block' in *.rs

* Automatically rename 'EBB' to 'block' in *.rs

* Automatically rename 'ebb' to 'block' in *.rs

* Automatically rename 'extended basic block' to 'basic block' in *.rs

* Automatically rename 'an basic block' to 'a basic block' in *.rs

* Manually update comment for `Block`

`Block`'s wikipedia article required an update.

* Automatically rename 'an `Block`' to 'a `Block`' in *.rs

* Automatically rename 'extended_basic_block' to 'basic_block' in *.rs

* Automatically rename 'ebb' to 'block' in *.clif

* Manually rename clif constant that contains 'ebb' as substring to avoid conflict

* Automatically rename filecheck uses of 'EBB' to 'BB'

'regex: EBB' -> 'regex: BB'
'$EBB' -> '$BB'

* Automatically rename 'EBB' 'Ebb' to 'block' in *.clif

* Automatically rename 'an block' to 'a block' in *.clif

* Fix broken testcase when function name length increases

Test function names are limited to 16 characters. This causes
the new longer name to be truncated and fail a filecheck test. An
outdated comment was also fixed.
This commit is contained in:
Ryan Hunt
2020-02-07 10:46:47 -06:00
committed by GitHub
parent a136d1cb00
commit 832666c45e
370 changed files with 8090 additions and 7988 deletions

View File

@@ -1,9 +1,9 @@
//! A Dominator Tree represented as mappings of Ebbs to their immediate dominator.
//! A Dominator Tree represented as mappings of Blocks to their immediate dominator.
use crate::entity::SecondaryMap;
use crate::flowgraph::{BasicBlock, ControlFlowGraph};
use crate::flowgraph::{BlockPredecessor, ControlFlowGraph};
use crate::ir::instructions::BranchInfo;
use crate::ir::{Ebb, ExpandedProgramPoint, Function, Inst, Layout, ProgramOrder, Value};
use crate::ir::{Block, ExpandedProgramPoint, Function, Inst, Layout, ProgramOrder, Value};
use crate::packed_option::PackedOption;
use crate::timing;
use alloc::vec::Vec;
@@ -19,7 +19,7 @@ const STRIDE: u32 = 4;
const DONE: u32 = 1;
const SEEN: u32 = 2;
/// Dominator tree node. We keep one of these per EBB.
/// Dominator tree node. We keep one of these per block.
#[derive(Clone, Default)]
struct DomNode {
/// Number of this node in a reverse post-order traversal of the CFG, starting from 1.
@@ -28,7 +28,7 @@ struct DomNode {
/// Unreachable nodes get number 0, all others are positive.
rpo_number: u32,
/// The immediate dominator of this EBB, represented as the branch or jump instruction at the
/// The immediate dominator of this block, represented as the branch or jump instruction at the
/// end of the dominating basic block.
///
/// This is `None` for unreachable blocks and the entry block which doesn't have an immediate
@@ -38,53 +38,53 @@ struct DomNode {
/// The dominator tree for a single function.
pub struct DominatorTree {
nodes: SecondaryMap<Ebb, DomNode>,
nodes: SecondaryMap<Block, DomNode>,
/// CFG post-order of all reachable EBBs.
postorder: Vec<Ebb>,
/// CFG post-order of all reachable blocks.
postorder: Vec<Block>,
/// Scratch memory used by `compute_postorder()`.
stack: Vec<Ebb>,
stack: Vec<Block>,
valid: bool,
}
/// Methods for querying the dominator tree.
impl DominatorTree {
/// Is `ebb` reachable from the entry block?
pub fn is_reachable(&self, ebb: Ebb) -> bool {
self.nodes[ebb].rpo_number != 0
/// Is `block` reachable from the entry block?
pub fn is_reachable(&self, block: Block) -> bool {
self.nodes[block].rpo_number != 0
}
/// Get the CFG post-order of EBBs that was used to compute the dominator tree.
/// Get the CFG post-order of blocks that was used to compute the dominator tree.
///
/// Note that this post-order is not updated automatically when the CFG is modified. It is
/// computed from scratch and cached by `compute()`.
pub fn cfg_postorder(&self) -> &[Ebb] {
pub fn cfg_postorder(&self) -> &[Block] {
debug_assert!(self.is_valid());
&self.postorder
}
/// Returns the immediate dominator of `ebb`.
/// Returns the immediate dominator of `block`.
///
/// The immediate dominator of an extended basic block is a basic block which we represent by
/// The immediate dominator of a basic block is a basic block which we represent by
/// the branch or jump instruction at the end of the basic block. This does not have to be the
/// terminator of its EBB.
/// terminator of its block.
///
/// A branch or jump is said to *dominate* `ebb` if all control flow paths from the function
/// entry to `ebb` must go through the branch.
/// A branch or jump is said to *dominate* `block` if all control flow paths from the function
/// entry to `block` must go through the branch.
///
/// The *immediate dominator* is the dominator that is closest to `ebb`. All other dominators
/// The *immediate dominator* is the dominator that is closest to `block`. All other dominators
/// also dominate the immediate dominator.
///
/// This returns `None` if `ebb` is not reachable from the entry EBB, or if it is the entry EBB
/// This returns `None` if `block` is not reachable from the entry block, or if it is the entry block
/// which has no dominators.
pub fn idom(&self, ebb: Ebb) -> Option<Inst> {
self.nodes[ebb].idom.into()
pub fn idom(&self, block: Block) -> Option<Inst> {
self.nodes[block].idom.into()
}
/// Compare two EBBs relative to the reverse post-order.
fn rpo_cmp_ebb(&self, a: Ebb, b: Ebb) -> Ordering {
/// Compare two blocks relative to the reverse post-order.
fn rpo_cmp_block(&self, a: Block, b: Block) -> Ordering {
self.nodes[a].rpo_number.cmp(&self.nodes[b].rpo_number)
}
@@ -93,7 +93,7 @@ impl DominatorTree {
///
/// Return `Ordering::Less` if `a` comes before `b` in the RPO.
///
/// If `a` and `b` belong to the same EBB, compare their relative position in the EBB.
/// If `a` and `b` belong to the same block, compare their relative position in the block.
pub fn rpo_cmp<A, B>(&self, a: A, b: B, layout: &Layout) -> Ordering
where
A: Into<ExpandedProgramPoint>,
@@ -101,7 +101,7 @@ impl DominatorTree {
{
let a = a.into();
let b = b.into();
self.rpo_cmp_ebb(layout.pp_ebb(a), layout.pp_ebb(b))
self.rpo_cmp_block(layout.pp_block(a), layout.pp_block(b))
.then(layout.cmp(a, b))
}
@@ -110,7 +110,7 @@ impl DominatorTree {
/// This means that every control-flow path from the function entry to `b` must go through `a`.
///
/// Dominance is ill defined for unreachable blocks. This function can always determine
/// dominance for instructions in the same EBB, but otherwise returns `false` if either block
/// dominance for instructions in the same block, but otherwise returns `false` if either block
/// is unreachable.
///
/// An instruction is considered to dominate itself.
@@ -122,12 +122,14 @@ impl DominatorTree {
let a = a.into();
let b = b.into();
match a {
ExpandedProgramPoint::Ebb(ebb_a) => {
a == b || self.last_dominator(ebb_a, b, layout).is_some()
ExpandedProgramPoint::Block(block_a) => {
a == b || self.last_dominator(block_a, b, layout).is_some()
}
ExpandedProgramPoint::Inst(inst_a) => {
let ebb_a = layout.inst_ebb(inst_a).expect("Instruction not in layout.");
match self.last_dominator(ebb_a, b, layout) {
let block_a = layout
.inst_block(inst_a)
.expect("Instruction not in layout.");
match self.last_dominator(block_a, b, layout) {
Some(last) => layout.cmp(inst_a, last) != Ordering::Greater,
None => false,
}
@@ -137,14 +139,14 @@ impl DominatorTree {
/// Find the last instruction in `a` that dominates `b`.
/// If no instructions in `a` dominate `b`, return `None`.
pub fn last_dominator<B>(&self, a: Ebb, b: B, layout: &Layout) -> Option<Inst>
pub fn last_dominator<B>(&self, a: Block, b: B, layout: &Layout) -> Option<Inst>
where
B: Into<ExpandedProgramPoint>,
{
let (mut ebb_b, mut inst_b) = match b.into() {
ExpandedProgramPoint::Ebb(ebb) => (ebb, None),
let (mut block_b, mut inst_b) = match b.into() {
ExpandedProgramPoint::Block(block) => (block, None),
ExpandedProgramPoint::Inst(inst) => (
layout.inst_ebb(inst).expect("Instruction not in layout."),
layout.inst_block(inst).expect("Instruction not in layout."),
Some(inst),
),
};
@@ -152,15 +154,15 @@ impl DominatorTree {
// Run a finger up the dominator tree from b until we see a.
// Do nothing if b is unreachable.
while rpo_a < self.nodes[ebb_b].rpo_number {
let idom = match self.idom(ebb_b) {
while rpo_a < self.nodes[block_b].rpo_number {
let idom = match self.idom(block_b) {
Some(idom) => idom,
None => return None, // a is unreachable, so we climbed past the entry
};
ebb_b = layout.inst_ebb(idom).expect("Dominator got removed.");
block_b = layout.inst_block(idom).expect("Dominator got removed.");
inst_b = Some(idom);
}
if a == ebb_b {
if a == block_b {
inst_b
} else {
None
@@ -172,25 +174,25 @@ impl DominatorTree {
/// Both basic blocks are assumed to be reachable.
pub fn common_dominator(
&self,
mut a: BasicBlock,
mut b: BasicBlock,
mut a: BlockPredecessor,
mut b: BlockPredecessor,
layout: &Layout,
) -> BasicBlock {
) -> BlockPredecessor {
loop {
match self.rpo_cmp_ebb(a.ebb, b.ebb) {
match self.rpo_cmp_block(a.block, b.block) {
Ordering::Less => {
// `a` comes before `b` in the RPO. Move `b` up.
let idom = self.nodes[b.ebb].idom.expect("Unreachable basic block?");
b = BasicBlock::new(
layout.inst_ebb(idom).expect("Dangling idom instruction"),
let idom = self.nodes[b.block].idom.expect("Unreachable basic block?");
b = BlockPredecessor::new(
layout.inst_block(idom).expect("Dangling idom instruction"),
idom,
);
}
Ordering::Greater => {
// `b` comes before `a` in the RPO. Move `a` up.
let idom = self.nodes[a.ebb].idom.expect("Unreachable basic block?");
a = BasicBlock::new(
layout.inst_ebb(idom).expect("Dangling idom instruction"),
let idom = self.nodes[a.block].idom.expect("Unreachable basic block?");
a = BlockPredecessor::new(
layout.inst_block(idom).expect("Dangling idom instruction"),
idom,
);
}
@@ -199,11 +201,11 @@ impl DominatorTree {
}
debug_assert_eq!(
a.ebb, b.ebb,
a.block, b.block,
"Unreachable block passed to common_dominator?"
);
// We're in the same EBB. The common dominator is the earlier instruction.
// We're in the same block. The common dominator is the earlier instruction.
if layout.cmp(a.inst, b.inst) == Ordering::Less {
a
} else {
@@ -226,10 +228,10 @@ impl DominatorTree {
/// Allocate and compute a dominator tree.
pub fn with_function(func: &Function, cfg: &ControlFlowGraph) -> Self {
let ebb_capacity = func.layout.ebb_capacity();
let block_capacity = func.layout.block_capacity();
let mut domtree = Self {
nodes: SecondaryMap::with_capacity(ebb_capacity),
postorder: Vec::with_capacity(ebb_capacity),
nodes: SecondaryMap::with_capacity(block_capacity),
postorder: Vec::with_capacity(block_capacity),
stack: Vec::new(),
valid: false,
};
@@ -266,13 +268,13 @@ impl DominatorTree {
/// Reset all internal data structures and compute a post-order of the control flow graph.
///
/// This leaves `rpo_number == 1` for all reachable EBBs, 0 for unreachable ones.
/// This leaves `rpo_number == 1` for all reachable blocks, 0 for unreachable ones.
fn compute_postorder(&mut self, func: &Function) {
self.clear();
self.nodes.resize(func.dfg.num_ebbs());
self.nodes.resize(func.dfg.num_blocks());
// This algorithm is a depth first traversal (DFT) of the control flow graph, computing a
// post-order of the EBBs that are reachable form the entry block. A DFT post-order is not
// post-order of the blocks that are reachable form the entry block. A DFT post-order is not
// unique. The specific order we get is controlled by two factors:
//
// 1. The order each node's children are visited, and
@@ -280,76 +282,76 @@ impl DominatorTree {
//
// There are two ways of viewing the CFG as a graph:
//
// 1. Each EBB is a node, with outgoing edges for all the branches in the EBB.
// 1. Each block is a node, with outgoing edges for all the branches in the block.
// 2. Each basic block is a node, with outgoing edges for the single branch at the end of
// the BB. (An EBB is a linear sequence of basic blocks).
// the BB. (An block is a linear sequence of basic blocks).
//
// The first graph is a contraction of the second one. We want to compute an EBB post-order
// The first graph is a contraction of the second one. We want to compute an block post-order
// that is compatible both graph interpretations. That is, if you compute a BB post-order
// and then remove those BBs that do not correspond to EBB headers, you get a post-order of
// the EBB graph.
// and then remove those BBs that do not correspond to block headers, you get a post-order of
// the block graph.
//
// Node child order:
//
// In the BB graph, we always go down the fall-through path first and follow the branch
// destination second.
//
// In the EBB graph, this is equivalent to visiting EBB successors in a bottom-up
// order, starting from the destination of the EBB's terminating jump, ending at the
// destination of the first branch in the EBB.
// In the block graph, this is equivalent to visiting block successors in a bottom-up
// order, starting from the destination of the block's terminating jump, ending at the
// destination of the first branch in the block.
//
// Edge pruning:
//
// In the BB graph, we keep an edge to an EBB the first time we visit the *source* side
// of the edge. Any subsequent edges to the same EBB are pruned.
// In the BB graph, we keep an edge to an block the first time we visit the *source* side
// of the edge. Any subsequent edges to the same block are pruned.
//
// The equivalent tree is reached in the EBB graph by keeping the first edge to an EBB
// The equivalent tree is reached in the block graph by keeping the first edge to an block
// in a top-down traversal of the successors. (And then visiting edges in a bottom-up
// order).
//
// This pruning method makes it possible to compute the DFT without storing lots of
// information about the progress through an EBB.
// information about the progress through an block.
// During this algorithm only, use `rpo_number` to hold the following state:
//
// 0: EBB has not yet been reached in the pre-order.
// SEEN: EBB has been pushed on the stack but successors not yet pushed.
// 0: block has not yet been reached in the pre-order.
// SEEN: block has been pushed on the stack but successors not yet pushed.
// DONE: Successors pushed.
match func.layout.entry_block() {
Some(ebb) => {
self.stack.push(ebb);
self.nodes[ebb].rpo_number = SEEN;
Some(block) => {
self.stack.push(block);
self.nodes[block].rpo_number = SEEN;
}
None => return,
}
while let Some(ebb) = self.stack.pop() {
match self.nodes[ebb].rpo_number {
while let Some(block) = self.stack.pop() {
match self.nodes[block].rpo_number {
SEEN => {
// This is the first time we pop the EBB, so we need to scan its successors and
// This is the first time we pop the block, so we need to scan its successors and
// then revisit it.
self.nodes[ebb].rpo_number = DONE;
self.stack.push(ebb);
self.push_successors(func, ebb);
self.nodes[block].rpo_number = DONE;
self.stack.push(block);
self.push_successors(func, block);
}
DONE => {
// This is the second time we pop the EBB, so all successors have been
// This is the second time we pop the block, so all successors have been
// processed.
self.postorder.push(ebb);
self.postorder.push(block);
}
_ => unreachable!(),
}
}
}
/// Push `ebb` successors onto `self.stack`, filtering out those that have already been seen.
/// Push `block` successors onto `self.stack`, filtering out those that have already been seen.
///
/// The successors are pushed in program order which is important to get a split-invariant
/// post-order. Split-invariant means that if an EBB is split in two, we get the same
/// post-order except for the insertion of the new EBB header at the split point.
fn push_successors(&mut self, func: &Function, ebb: Ebb) {
for inst in func.layout.ebb_insts(ebb) {
/// post-order. Split-invariant means that if an block is split in two, we get the same
/// post-order except for the insertion of the new block header at the split point.
fn push_successors(&mut self, func: &Function, block: Block) {
for inst in func.layout.block_insts(block) {
match func.dfg.analyze_branch(inst) {
BranchInfo::SingleDest(succ, _) => self.push_if_unseen(succ),
BranchInfo::Table(jt, dest) => {
@@ -365,11 +367,11 @@ impl DominatorTree {
}
}
/// Push `ebb` onto `self.stack` if it has not already been seen.
fn push_if_unseen(&mut self, ebb: Ebb) {
if self.nodes[ebb].rpo_number == 0 {
self.nodes[ebb].rpo_number = SEEN;
self.stack.push(ebb);
/// Push `block` onto `self.stack` if it has not already been seen.
fn push_if_unseen(&mut self, block: Block) {
if self.nodes[block].rpo_number == 0 {
self.nodes[block].rpo_number = SEEN;
self.stack.push(block);
}
}
@@ -378,10 +380,10 @@ impl DominatorTree {
fn compute_domtree(&mut self, func: &Function, cfg: &ControlFlowGraph) {
// During this algorithm, `rpo_number` has the following values:
//
// 0: EBB is not reachable.
// 1: EBB is reachable, but has not yet been visited during the first pass. This is set by
// 0: block is not reachable.
// 1: block is reachable, but has not yet been visited during the first pass. This is set by
// `compute_postorder`.
// 2+: EBB is reachable and has an assigned RPO number.
// 2+: block is reachable and has an assigned RPO number.
// We'll be iterating over a reverse post-order of the CFG, skipping the entry block.
let (entry_block, postorder) = match self.postorder.as_slice().split_last() {
@@ -392,7 +394,7 @@ impl DominatorTree {
// Do a first pass where we assign RPO numbers to all reachable nodes.
self.nodes[entry_block].rpo_number = 2 * STRIDE;
for (rpo_idx, &ebb) in postorder.iter().rev().enumerate() {
for (rpo_idx, &block) in postorder.iter().rev().enumerate() {
// Update the current node and give it an RPO number.
// The entry block got 2, the rest start at 3 by multiples of STRIDE to leave
// room for future dominator tree modifications.
@@ -402,8 +404,8 @@ impl DominatorTree {
//
// Due to the nature of the post-order traversal, every node we visit will have at
// least one predecessor that has previously been visited during this RPO.
self.nodes[ebb] = DomNode {
idom: self.compute_idom(ebb, cfg, &func.layout).into(),
self.nodes[block] = DomNode {
idom: self.compute_idom(block, cfg, &func.layout).into(),
rpo_number: (rpo_idx as u32 + 3) * STRIDE,
}
}
@@ -415,30 +417,30 @@ impl DominatorTree {
let mut changed = true;
while changed {
changed = false;
for &ebb in postorder.iter().rev() {
let idom = self.compute_idom(ebb, cfg, &func.layout).into();
if self.nodes[ebb].idom != idom {
self.nodes[ebb].idom = idom;
for &block in postorder.iter().rev() {
let idom = self.compute_idom(block, cfg, &func.layout).into();
if self.nodes[block].idom != idom {
self.nodes[block].idom = idom;
changed = true;
}
}
}
}
// Compute the immediate dominator for `ebb` using the current `idom` states for the reachable
// Compute the immediate dominator for `block` using the current `idom` states for the reachable
// nodes.
fn compute_idom(&self, ebb: Ebb, cfg: &ControlFlowGraph, layout: &Layout) -> Inst {
// Get an iterator with just the reachable, already visited predecessors to `ebb`.
fn compute_idom(&self, block: Block, cfg: &ControlFlowGraph, layout: &Layout) -> Inst {
// Get an iterator with just the reachable, already visited predecessors to `block`.
// Note that during the first pass, `rpo_number` is 1 for reachable blocks that haven't
// been visited yet, 0 for unreachable blocks.
let mut reachable_preds = cfg
.pred_iter(ebb)
.filter(|&BasicBlock { ebb: pred, .. }| self.nodes[pred].rpo_number > 1);
.pred_iter(block)
.filter(|&BlockPredecessor { block: pred, .. }| self.nodes[pred].rpo_number > 1);
// The RPO must visit at least one predecessor before this node.
let mut idom = reachable_preds
.next()
.expect("EBB node must have one reachable predecessor");
.expect("block node must have one reachable predecessor");
for pred in reachable_preds {
idom = self.common_dominator(idom, pred, layout);
@@ -453,25 +455,25 @@ impl DominatorTree {
/// This data structure is computed from a `DominatorTree` and provides:
///
/// - A forward traversable dominator tree through the `children()` iterator.
/// - An ordering of EBBs according to a dominator tree pre-order.
/// - Constant time dominance checks at the EBB granularity.
/// - An ordering of blocks according to a dominator tree pre-order.
/// - Constant time dominance checks at the block granularity.
///
/// The information in this auxiliary data structure is not easy to update when the control flow
/// graph changes, which is why it is kept separate.
pub struct DominatorTreePreorder {
nodes: SecondaryMap<Ebb, ExtraNode>,
nodes: SecondaryMap<Block, ExtraNode>,
// Scratch memory used by `compute_postorder()`.
stack: Vec<Ebb>,
stack: Vec<Block>,
}
#[derive(Default, Clone)]
struct ExtraNode {
/// First child node in the domtree.
child: PackedOption<Ebb>,
child: PackedOption<Block>,
/// Next sibling node in the domtree. This linked list is ordered according to the CFG RPO.
sibling: PackedOption<Ebb>,
sibling: PackedOption<Block>,
/// Sequence number for this node in a pre-order traversal of the dominator tree.
/// Unreachable blocks have number 0, the entry block is 1.
@@ -501,23 +503,23 @@ impl DominatorTreePreorder {
//
// By following the CFG post-order and pushing to the front of the lists, we make sure that
// sibling lists are ordered according to the CFG reverse post-order.
for &ebb in domtree.cfg_postorder() {
if let Some(idom_inst) = domtree.idom(ebb) {
let idom = layout.pp_ebb(idom_inst);
let sib = mem::replace(&mut self.nodes[idom].child, ebb.into());
self.nodes[ebb].sibling = sib;
for &block in domtree.cfg_postorder() {
if let Some(idom_inst) = domtree.idom(block) {
let idom = layout.pp_block(idom_inst);
let sib = mem::replace(&mut self.nodes[idom].child, block.into());
self.nodes[block].sibling = sib;
} else {
// The only EBB without an immediate dominator is the entry.
self.stack.push(ebb);
// The only block without an immediate dominator is the entry.
self.stack.push(block);
}
}
// Step 2. Assign pre-order numbers from a DFS of the dominator tree.
debug_assert!(self.stack.len() <= 1);
let mut n = 0;
while let Some(ebb) = self.stack.pop() {
while let Some(block) = self.stack.pop() {
n += 1;
let node = &mut self.nodes[ebb];
let node = &mut self.nodes[block];
node.pre_number = n;
node.pre_max = n;
if let Some(n) = node.sibling.expand() {
@@ -531,29 +533,29 @@ impl DominatorTreePreorder {
// Step 3. Propagate the `pre_max` numbers up the tree.
// The CFG post-order is topologically ordered w.r.t. dominance so a node comes after all
// its dominator tree children.
for &ebb in domtree.cfg_postorder() {
if let Some(idom_inst) = domtree.idom(ebb) {
let idom = layout.pp_ebb(idom_inst);
let pre_max = cmp::max(self.nodes[ebb].pre_max, self.nodes[idom].pre_max);
for &block in domtree.cfg_postorder() {
if let Some(idom_inst) = domtree.idom(block) {
let idom = layout.pp_block(idom_inst);
let pre_max = cmp::max(self.nodes[block].pre_max, self.nodes[idom].pre_max);
self.nodes[idom].pre_max = pre_max;
}
}
}
}
/// An iterator that enumerates the direct children of an EBB in the dominator tree.
/// An iterator that enumerates the direct children of an block in the dominator tree.
pub struct ChildIter<'a> {
dtpo: &'a DominatorTreePreorder,
next: PackedOption<Ebb>,
next: PackedOption<Block>,
}
impl<'a> Iterator for ChildIter<'a> {
type Item = Ebb;
type Item = Block;
fn next(&mut self) -> Option<Ebb> {
fn next(&mut self) -> Option<Block> {
let n = self.next.expand();
if let Some(ebb) = n {
self.next = self.dtpo.nodes[ebb].sibling;
if let Some(block) = n {
self.next = self.dtpo.nodes[block].sibling;
}
n
}
@@ -561,32 +563,32 @@ impl<'a> Iterator for ChildIter<'a> {
/// Query interface for the dominator tree pre-order.
impl DominatorTreePreorder {
/// Get an iterator over the direct children of `ebb` in the dominator tree.
/// Get an iterator over the direct children of `block` in the dominator tree.
///
/// These are the EBB's whose immediate dominator is an instruction in `ebb`, ordered according
/// These are the block's whose immediate dominator is an instruction in `block`, ordered according
/// to the CFG reverse post-order.
pub fn children(&self, ebb: Ebb) -> ChildIter {
pub fn children(&self, block: Block) -> ChildIter {
ChildIter {
dtpo: self,
next: self.nodes[ebb].child,
next: self.nodes[block].child,
}
}
/// Fast, constant time dominance check with EBB granularity.
/// Fast, constant time dominance check with block granularity.
///
/// This computes the same result as `domtree.dominates(a, b)`, but in guaranteed fast constant
/// time. This is less general than the `DominatorTree` method because it only works with EBB
/// time. This is less general than the `DominatorTree` method because it only works with block
/// program points.
///
/// An EBB is considered to dominate itself.
pub fn dominates(&self, a: Ebb, b: Ebb) -> bool {
/// An block is considered to dominate itself.
pub fn dominates(&self, a: Block, b: Block) -> bool {
let na = &self.nodes[a];
let nb = &self.nodes[b];
na.pre_number <= nb.pre_number && na.pre_max >= nb.pre_max
}
/// Compare two EBBs according to the dominator pre-order.
pub fn pre_cmp_ebb(&self, a: Ebb, b: Ebb) -> Ordering {
/// Compare two blocks according to the dominator pre-order.
pub fn pre_cmp_block(&self, a: Block, b: Block) -> Ordering {
self.nodes[a].pre_number.cmp(&self.nodes[b].pre_number)
}
@@ -601,7 +603,7 @@ impl DominatorTreePreorder {
{
let a = a.into();
let b = b.into();
self.pre_cmp_ebb(layout.pp_ebb(a), layout.pp_ebb(b))
self.pre_cmp_block(layout.pp_block(a), layout.pp_block(b))
.then(layout.cmp(a, b))
}
@@ -643,23 +645,23 @@ mod tests {
#[test]
fn unreachable_node() {
let mut func = Function::new();
let ebb0 = func.dfg.make_ebb();
let v0 = func.dfg.append_ebb_param(ebb0, I32);
let ebb1 = func.dfg.make_ebb();
let ebb2 = func.dfg.make_ebb();
let block0 = func.dfg.make_block();
let v0 = func.dfg.append_block_param(block0, I32);
let block1 = func.dfg.make_block();
let block2 = func.dfg.make_block();
let mut cur = FuncCursor::new(&mut func);
cur.insert_ebb(ebb0);
cur.ins().brnz(v0, ebb2, &[]);
cur.insert_block(block0);
cur.ins().brnz(v0, block2, &[]);
cur.ins().trap(TrapCode::User(0));
cur.insert_ebb(ebb1);
cur.insert_block(block1);
let v1 = cur.ins().iconst(I32, 1);
let v2 = cur.ins().iadd(v0, v1);
cur.ins().jump(ebb0, &[v2]);
cur.ins().jump(block0, &[v2]);
cur.insert_ebb(ebb2);
cur.insert_block(block2);
cur.ins().return_(&[v0]);
let cfg = ControlFlowGraph::with_function(cur.func);
@@ -667,96 +669,99 @@ mod tests {
// Fall-through-first, prune-at-source DFT:
//
// ebb0 {
// brnz ebb2 {
// block0 {
// brnz block2 {
// trap
// ebb2 {
// block2 {
// return
// } ebb2
// } ebb0
assert_eq!(dt.cfg_postorder(), &[ebb2, ebb0]);
// } block2
// } block0
assert_eq!(dt.cfg_postorder(), &[block2, block0]);
let v2_def = cur.func.dfg.value_def(v2).unwrap_inst();
assert!(!dt.dominates(v2_def, ebb0, &cur.func.layout));
assert!(!dt.dominates(ebb0, v2_def, &cur.func.layout));
assert!(!dt.dominates(v2_def, block0, &cur.func.layout));
assert!(!dt.dominates(block0, v2_def, &cur.func.layout));
let mut dtpo = DominatorTreePreorder::new();
dtpo.compute(&dt, &cur.func.layout);
assert!(dtpo.dominates(ebb0, ebb0));
assert!(!dtpo.dominates(ebb0, ebb1));
assert!(dtpo.dominates(ebb0, ebb2));
assert!(!dtpo.dominates(ebb1, ebb0));
assert!(dtpo.dominates(ebb1, ebb1));
assert!(!dtpo.dominates(ebb1, ebb2));
assert!(!dtpo.dominates(ebb2, ebb0));
assert!(!dtpo.dominates(ebb2, ebb1));
assert!(dtpo.dominates(ebb2, ebb2));
assert!(dtpo.dominates(block0, block0));
assert!(!dtpo.dominates(block0, block1));
assert!(dtpo.dominates(block0, block2));
assert!(!dtpo.dominates(block1, block0));
assert!(dtpo.dominates(block1, block1));
assert!(!dtpo.dominates(block1, block2));
assert!(!dtpo.dominates(block2, block0));
assert!(!dtpo.dominates(block2, block1));
assert!(dtpo.dominates(block2, block2));
}
#[test]
fn non_zero_entry_block() {
let mut func = Function::new();
let ebb0 = func.dfg.make_ebb();
let ebb1 = func.dfg.make_ebb();
let ebb2 = func.dfg.make_ebb();
let ebb3 = func.dfg.make_ebb();
let cond = func.dfg.append_ebb_param(ebb3, I32);
let block0 = func.dfg.make_block();
let block1 = func.dfg.make_block();
let block2 = func.dfg.make_block();
let block3 = func.dfg.make_block();
let cond = func.dfg.append_block_param(block3, I32);
let mut cur = FuncCursor::new(&mut func);
cur.insert_ebb(ebb3);
let jmp_ebb3_ebb1 = cur.ins().jump(ebb1, &[]);
cur.insert_block(block3);
let jmp_block3_block1 = cur.ins().jump(block1, &[]);
cur.insert_ebb(ebb1);
let br_ebb1_ebb0 = cur.ins().brnz(cond, ebb0, &[]);
let jmp_ebb1_ebb2 = cur.ins().jump(ebb2, &[]);
cur.insert_block(block1);
let br_block1_block0 = cur.ins().brnz(cond, block0, &[]);
let jmp_block1_block2 = cur.ins().jump(block2, &[]);
cur.insert_ebb(ebb2);
cur.ins().jump(ebb0, &[]);
cur.insert_block(block2);
cur.ins().jump(block0, &[]);
cur.insert_ebb(ebb0);
cur.insert_block(block0);
let cfg = ControlFlowGraph::with_function(cur.func);
let dt = DominatorTree::with_function(cur.func, &cfg);
// Fall-through-first, prune-at-source DFT:
//
// ebb3 {
// ebb3:jump ebb1 {
// ebb1 {
// ebb1:brnz ebb0 {
// ebb1:jump ebb2 {
// ebb2 {
// ebb2:jump ebb0 (seen)
// } ebb2
// } ebb1:jump ebb2
// ebb0 {
// } ebb0
// } ebb1:brnz ebb0
// } ebb1
// } ebb3:jump ebb1
// } ebb3
// block3 {
// block3:jump block1 {
// block1 {
// block1:brnz block0 {
// block1:jump block2 {
// block2 {
// block2:jump block0 (seen)
// } block2
// } block1:jump block2
// block0 {
// } block0
// } block1:brnz block0
// } block1
// } block3:jump block1
// } block3
assert_eq!(dt.cfg_postorder(), &[ebb2, ebb0, ebb1, ebb3]);
assert_eq!(dt.cfg_postorder(), &[block2, block0, block1, block3]);
assert_eq!(cur.func.layout.entry_block().unwrap(), ebb3);
assert_eq!(dt.idom(ebb3), None);
assert_eq!(dt.idom(ebb1).unwrap(), jmp_ebb3_ebb1);
assert_eq!(dt.idom(ebb2).unwrap(), jmp_ebb1_ebb2);
assert_eq!(dt.idom(ebb0).unwrap(), br_ebb1_ebb0);
assert_eq!(cur.func.layout.entry_block().unwrap(), block3);
assert_eq!(dt.idom(block3), None);
assert_eq!(dt.idom(block1).unwrap(), jmp_block3_block1);
assert_eq!(dt.idom(block2).unwrap(), jmp_block1_block2);
assert_eq!(dt.idom(block0).unwrap(), br_block1_block0);
assert!(dt.dominates(br_ebb1_ebb0, br_ebb1_ebb0, &cur.func.layout));
assert!(!dt.dominates(br_ebb1_ebb0, jmp_ebb3_ebb1, &cur.func.layout));
assert!(dt.dominates(jmp_ebb3_ebb1, br_ebb1_ebb0, &cur.func.layout));
assert!(dt.dominates(br_block1_block0, br_block1_block0, &cur.func.layout));
assert!(!dt.dominates(br_block1_block0, jmp_block3_block1, &cur.func.layout));
assert!(dt.dominates(jmp_block3_block1, br_block1_block0, &cur.func.layout));
assert_eq!(dt.rpo_cmp(ebb3, ebb3, &cur.func.layout), Ordering::Equal);
assert_eq!(dt.rpo_cmp(ebb3, ebb1, &cur.func.layout), Ordering::Less);
assert_eq!(
dt.rpo_cmp(ebb3, jmp_ebb3_ebb1, &cur.func.layout),
dt.rpo_cmp(block3, block3, &cur.func.layout),
Ordering::Equal
);
assert_eq!(dt.rpo_cmp(block3, block1, &cur.func.layout), Ordering::Less);
assert_eq!(
dt.rpo_cmp(block3, jmp_block3_block1, &cur.func.layout),
Ordering::Less
);
assert_eq!(
dt.rpo_cmp(jmp_ebb3_ebb1, jmp_ebb1_ebb2, &cur.func.layout),
dt.rpo_cmp(jmp_block3_block1, jmp_block1_block2, &cur.func.layout),
Ordering::Less
);
}
@@ -764,69 +769,69 @@ mod tests {
#[test]
fn backwards_layout() {
let mut func = Function::new();
let ebb0 = func.dfg.make_ebb();
let ebb1 = func.dfg.make_ebb();
let ebb2 = func.dfg.make_ebb();
let block0 = func.dfg.make_block();
let block1 = func.dfg.make_block();
let block2 = func.dfg.make_block();
let mut cur = FuncCursor::new(&mut func);
cur.insert_ebb(ebb0);
let jmp02 = cur.ins().jump(ebb2, &[]);
cur.insert_block(block0);
let jmp02 = cur.ins().jump(block2, &[]);
cur.insert_ebb(ebb1);
cur.insert_block(block1);
let trap = cur.ins().trap(TrapCode::User(5));
cur.insert_ebb(ebb2);
let jmp21 = cur.ins().jump(ebb1, &[]);
cur.insert_block(block2);
let jmp21 = cur.ins().jump(block1, &[]);
let cfg = ControlFlowGraph::with_function(cur.func);
let dt = DominatorTree::with_function(cur.func, &cfg);
assert_eq!(cur.func.layout.entry_block(), Some(ebb0));
assert_eq!(dt.idom(ebb0), None);
assert_eq!(dt.idom(ebb1), Some(jmp21));
assert_eq!(dt.idom(ebb2), Some(jmp02));
assert_eq!(cur.func.layout.entry_block(), Some(block0));
assert_eq!(dt.idom(block0), None);
assert_eq!(dt.idom(block1), Some(jmp21));
assert_eq!(dt.idom(block2), Some(jmp02));
assert!(dt.dominates(ebb0, ebb0, &cur.func.layout));
assert!(dt.dominates(ebb0, jmp02, &cur.func.layout));
assert!(dt.dominates(ebb0, ebb1, &cur.func.layout));
assert!(dt.dominates(ebb0, trap, &cur.func.layout));
assert!(dt.dominates(ebb0, ebb2, &cur.func.layout));
assert!(dt.dominates(ebb0, jmp21, &cur.func.layout));
assert!(dt.dominates(block0, block0, &cur.func.layout));
assert!(dt.dominates(block0, jmp02, &cur.func.layout));
assert!(dt.dominates(block0, block1, &cur.func.layout));
assert!(dt.dominates(block0, trap, &cur.func.layout));
assert!(dt.dominates(block0, block2, &cur.func.layout));
assert!(dt.dominates(block0, jmp21, &cur.func.layout));
assert!(!dt.dominates(jmp02, ebb0, &cur.func.layout));
assert!(!dt.dominates(jmp02, block0, &cur.func.layout));
assert!(dt.dominates(jmp02, jmp02, &cur.func.layout));
assert!(dt.dominates(jmp02, ebb1, &cur.func.layout));
assert!(dt.dominates(jmp02, block1, &cur.func.layout));
assert!(dt.dominates(jmp02, trap, &cur.func.layout));
assert!(dt.dominates(jmp02, ebb2, &cur.func.layout));
assert!(dt.dominates(jmp02, block2, &cur.func.layout));
assert!(dt.dominates(jmp02, jmp21, &cur.func.layout));
assert!(!dt.dominates(ebb1, ebb0, &cur.func.layout));
assert!(!dt.dominates(ebb1, jmp02, &cur.func.layout));
assert!(dt.dominates(ebb1, ebb1, &cur.func.layout));
assert!(dt.dominates(ebb1, trap, &cur.func.layout));
assert!(!dt.dominates(ebb1, ebb2, &cur.func.layout));
assert!(!dt.dominates(ebb1, jmp21, &cur.func.layout));
assert!(!dt.dominates(block1, block0, &cur.func.layout));
assert!(!dt.dominates(block1, jmp02, &cur.func.layout));
assert!(dt.dominates(block1, block1, &cur.func.layout));
assert!(dt.dominates(block1, trap, &cur.func.layout));
assert!(!dt.dominates(block1, block2, &cur.func.layout));
assert!(!dt.dominates(block1, jmp21, &cur.func.layout));
assert!(!dt.dominates(trap, ebb0, &cur.func.layout));
assert!(!dt.dominates(trap, block0, &cur.func.layout));
assert!(!dt.dominates(trap, jmp02, &cur.func.layout));
assert!(!dt.dominates(trap, ebb1, &cur.func.layout));
assert!(!dt.dominates(trap, block1, &cur.func.layout));
assert!(dt.dominates(trap, trap, &cur.func.layout));
assert!(!dt.dominates(trap, ebb2, &cur.func.layout));
assert!(!dt.dominates(trap, block2, &cur.func.layout));
assert!(!dt.dominates(trap, jmp21, &cur.func.layout));
assert!(!dt.dominates(ebb2, ebb0, &cur.func.layout));
assert!(!dt.dominates(ebb2, jmp02, &cur.func.layout));
assert!(dt.dominates(ebb2, ebb1, &cur.func.layout));
assert!(dt.dominates(ebb2, trap, &cur.func.layout));
assert!(dt.dominates(ebb2, ebb2, &cur.func.layout));
assert!(dt.dominates(ebb2, jmp21, &cur.func.layout));
assert!(!dt.dominates(block2, block0, &cur.func.layout));
assert!(!dt.dominates(block2, jmp02, &cur.func.layout));
assert!(dt.dominates(block2, block1, &cur.func.layout));
assert!(dt.dominates(block2, trap, &cur.func.layout));
assert!(dt.dominates(block2, block2, &cur.func.layout));
assert!(dt.dominates(block2, jmp21, &cur.func.layout));
assert!(!dt.dominates(jmp21, ebb0, &cur.func.layout));
assert!(!dt.dominates(jmp21, block0, &cur.func.layout));
assert!(!dt.dominates(jmp21, jmp02, &cur.func.layout));
assert!(dt.dominates(jmp21, ebb1, &cur.func.layout));
assert!(dt.dominates(jmp21, block1, &cur.func.layout));
assert!(dt.dominates(jmp21, trap, &cur.func.layout));
assert!(!dt.dominates(jmp21, ebb2, &cur.func.layout));
assert!(!dt.dominates(jmp21, block2, &cur.func.layout));
assert!(dt.dominates(jmp21, jmp21, &cur.func.layout));
}
}