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:
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user