Optimize DomForest::push_node().

The dominator tree pre-order is defined at the EBB granularity, but we
are looking for dominating nodes at the instruction level. This means
that we sometimes need to look higher up the DomForest stack for a
dominating node, using DominatorTree::dominates() instead of
DominatorTreePreorder::dominates().

Each dominance check involves the domtree.last_dominator() function
scanning up the dominator tree, starting from the new node that was
pushed. We can eliminate this duplicate work by exposing the
last_dominator() function to push_node().

As we are searching through nodes on the stack, maintain a last_dom
program point representing the previous return value from
last_dominator(). This way, we're only scanning the dominator tree once.
This commit is contained in:
Jakob Stoklund Olesen
2018-01-24 13:44:51 -08:00
parent 416b21c18d
commit d56ce9e8bf
2 changed files with 24 additions and 12 deletions

View File

@@ -138,7 +138,7 @@ impl DominatorTree {
/// Find the last instruction in `a` that dominates `b`.
/// If no instructions in `a` dominate `b`, return `None`.
fn last_dominator<B>(&self, a: Ebb, b: B, layout: &Layout) -> Option<Inst>
pub fn last_dominator<B>(&self, a: Ebb, b: B, layout: &Layout) -> Option<Inst>
where
B: Into<ExpandedProgramPoint>,
{

View File

@@ -779,21 +779,33 @@ impl DomForest {
// not necessarily mean that `top.def` dominates `node.def`, though. The `top.def`
// program point may be below the last branch in `top.ebb` that dominates
// `node.def`.
debug_assert!(domtree.dominates(top.ebb, node.def, &func.layout));
//
// We do know, though, that if there is a nearest value dominating `node.def`, it
// will be on the stack. We just need to find the last stack entry that actually
// dominates.
//
// TODO: This search could be more efficient if we had access to
// `domtree.last_dominator()`. Each call to `dominates()` here ends up walking up
// the dominator tree starting from `node.ebb`.
let last_dom = self.stack[0..self.stack.len() - 1].iter().rposition(|n| {
domtree.dominates(n.def, node.def, &func.layout)
});
let mut last_dom = node.def;
for &n in self.stack.iter().rev().skip(1) {
// If the node is defined at the EBB header, it does in fact dominate
// everything else pushed on the stack.
let def_inst = match n.def {
ExpandedProgramPoint::Ebb(_) => return Some(n),
ExpandedProgramPoint::Inst(i) => i,
};
// If there is a dominating parent value, return it for interference checking.
return last_dom.map(|pos| self.stack[pos]);
// We need to find the last program point in `n.ebb` to dominate `node.def`.
last_dom = match domtree.last_dominator(n.ebb, last_dom, &func.layout) {
None => n.ebb.into(),
Some(inst) => {
if func.layout.cmp(def_inst, inst) != cmp::Ordering::Greater {
return Some(n);
}
inst.into()
}
};
}
// No real dominator found on the stack.
return None;
}
}