Recompute the dominator tree on demand.

The legalizer can invalidate the dominator tree, but we don't actually
need a dominator tree during legalization, so defer the construction of
the domtree.

- Add an "invalid" state to the dominator tree along with clear() and
  is_valid() methods to test it.
- Invalidate the dominator tree as part of legalization.
- Ensure that a valid dominator tree exists before the passes that need
  it.

Together these features add up to a manual invalidation mechanism for
the dominator tree.
This commit is contained in:
Jakob Stoklund Olesen
2017-08-28 11:13:53 -07:00
parent fecbcbb7b4
commit 6d9198d55f
3 changed files with 34 additions and 5 deletions

View File

@@ -63,7 +63,7 @@ impl Context {
/// ///
/// Returns the size of the function's code. /// Returns the size of the function's code.
pub fn compile(&mut self, isa: &TargetIsa) -> Result<CodeOffset, CtonError> { pub fn compile(&mut self, isa: &TargetIsa) -> Result<CodeOffset, CtonError> {
self.flowgraph(); self.cfg.compute(&self.func);
self.verify_if(isa)?; self.verify_if(isa)?;
self.legalize(isa)?; self.legalize(isa)?;
@@ -103,6 +103,8 @@ impl Context {
/// Run the legalizer for `isa` on the function. /// Run the legalizer for `isa` on the function.
pub fn legalize(&mut self, isa: &TargetIsa) -> CtonResult { pub fn legalize(&mut self, isa: &TargetIsa) -> CtonResult {
// Legalization invalidates the domtree by mutating the CFG.
self.domtree.clear();
legalize_function(&mut self.func, &mut self.cfg, isa); legalize_function(&mut self.func, &mut self.cfg, isa);
self.verify_if(isa) self.verify_if(isa)
} }
@@ -113,6 +115,13 @@ impl Context {
self.domtree.compute(&self.func, &self.cfg); self.domtree.compute(&self.func, &self.cfg);
} }
/// Ensure that a valid domtree exists.
pub fn ensure_domtree(&mut self) {
if !self.domtree.is_valid() {
self.domtree.compute(&self.func, &self.cfg);
}
}
/// Perform simple GVN on the function. /// Perform simple GVN on the function.
pub fn simple_gvn(&mut self) -> CtonResult { pub fn simple_gvn(&mut self) -> CtonResult {
do_simple_gvn(&mut self.func, &mut self.cfg); do_simple_gvn(&mut self.func, &mut self.cfg);
@@ -123,6 +132,7 @@ impl Context {
/// Perform LICM on the function. /// Perform LICM on the function.
pub fn licm(&mut self) -> CtonResult { pub fn licm(&mut self) -> CtonResult {
self.ensure_domtree();
do_licm(&mut self.func, do_licm(&mut self.func,
&mut self.cfg, &mut self.cfg,
&mut self.domtree, &mut self.domtree,
@@ -132,6 +142,7 @@ impl Context {
/// Run the register allocator. /// Run the register allocator.
pub fn regalloc(&mut self, isa: &TargetIsa) -> CtonResult { pub fn regalloc(&mut self, isa: &TargetIsa) -> CtonResult {
self.ensure_domtree();
self.regalloc self.regalloc
.run(isa, &mut self.func, &self.cfg, &self.domtree) .run(isa, &mut self.func, &self.cfg, &self.domtree)
} }

View File

@@ -205,14 +205,30 @@ impl DominatorTree {
self.compute_domtree(func, cfg); self.compute_domtree(func, cfg);
} }
/// Clear the data structures used to represent the dominator tree. This will leave the tree in
/// a state where `is_valid()` returns false.
pub fn clear(&mut self) {
self.nodes.clear();
self.postorder.clear();
assert!(self.stack.is_empty());
}
/// Check if the dominator tree is in a valid state.
///
/// Note that this doesn't perform any kind of validity checks. It simply checks if the
/// `compute()` method has been called since the last `clear()`. It does not check that the
/// dominator tree is consistent
/// with the CFG>
pub fn is_valid(&self) -> bool {
!self.nodes.is_empty()
}
/// Reset all internal data structures and compute a post-order for `cfg`. /// Reset all internal data structures and compute a post-order for `cfg`.
/// ///
/// This leaves `rpo_number == 1` for all reachable EBBs, 0 for unreachable ones. /// This leaves `rpo_number == 1` for all reachable EBBs, 0 for unreachable ones.
fn compute_postorder(&mut self, func: &Function, cfg: &ControlFlowGraph) { fn compute_postorder(&mut self, func: &Function, cfg: &ControlFlowGraph) {
self.nodes.clear(); self.clear();
self.nodes.resize(func.dfg.num_ebbs()); self.nodes.resize(func.dfg.num_ebbs());
self.postorder.clear();
assert!(self.stack.is_empty());
// During this algorithm only, use `rpo_number` to hold the following state: // During this algorithm only, use `rpo_number` to hold the following state:
// //

View File

@@ -134,7 +134,9 @@ pub fn verify_context(func: &Function,
-> Result { -> Result {
let verifier = Verifier::new(func, isa); let verifier = Verifier::new(func, isa);
verifier.cfg_integrity(cfg)?; verifier.cfg_integrity(cfg)?;
verifier.domtree_integrity(domtree)?; if domtree.is_valid() {
verifier.domtree_integrity(domtree)?;
}
verifier.run() verifier.run()
} }