diff --git a/lib/cretonne/src/lib.rs b/lib/cretonne/src/lib.rs index 986c45355b..1f51b38d3f 100644 --- a/lib/cretonne/src/lib.rs +++ b/lib/cretonne/src/lib.rs @@ -35,4 +35,5 @@ mod packed_option; mod partition_slice; mod predicates; mod ref_slice; +mod topo_order; mod write; diff --git a/lib/cretonne/src/regalloc/coloring.rs b/lib/cretonne/src/regalloc/coloring.rs index 544c5b5da0..a1b3899577 100644 --- a/lib/cretonne/src/regalloc/coloring.rs +++ b/lib/cretonne/src/regalloc/coloring.rs @@ -43,19 +43,13 @@ use regalloc::affinity::Affinity; use regalloc::allocatable_set::AllocatableSet; use regalloc::live_value_tracker::{LiveValue, LiveValueTracker}; use regalloc::liveness::Liveness; -use sparse_map::SparseSet; +use topo_order::TopoOrder; /// Data structures for the coloring pass. /// /// These are scratch space data structures that can be reused between invocations. -pub struct Coloring { - /// Set of visited EBBs. - visited: SparseSet, - - /// Stack of EBBs to be visited next. - stack: Vec, -} +pub struct Coloring {} /// Bundle of references that the coloring algorithm needs. /// @@ -83,10 +77,7 @@ struct Context<'a> { impl Coloring { /// Allocate scratch space data structures for the coloring pass. pub fn new() -> Coloring { - Coloring { - visited: SparseSet::new(), - stack: Vec::new(), - } + Coloring {} } /// Run the coloring algorithm over `func`. @@ -95,6 +86,7 @@ impl Coloring { func: &mut Function, domtree: &DominatorTree, liveness: &mut Liveness, + topo: &mut TopoOrder, tracker: &mut LiveValueTracker) { let mut ctx = Context { reginfo: isa.register_info(), @@ -103,45 +95,17 @@ impl Coloring { liveness, usable_regs: isa.allocatable_registers(func), }; - ctx.run(self, func, tracker) + ctx.run(func, topo, tracker) } } impl<'a> Context<'a> { /// Run the coloring algorithm. - fn run(&mut self, data: &mut Coloring, func: &mut Function, tracker: &mut LiveValueTracker) { - // Just visit blocks in layout order, letting `process_ebb` enforce a topological ordering. + fn run(&mut self, func: &mut Function, topo: &mut TopoOrder, tracker: &mut LiveValueTracker) { + // Just visit blocks in layout order, letting `topo` enforce a topological ordering. // TODO: Once we have a loop tree, we could visit hot blocks first. - let mut next = func.layout.entry_block(); - while let Some(ebb) = next { - self.process_ebb(ebb, data, func, tracker); - next = func.layout.next_ebb(ebb); - } - } - - /// Process `ebb`, but only after ensuring that the immediate dominator has been processed. - /// - /// This method can be called with the most desired order of visiting the EBBs. It will convert - /// that order into a valid topological order by visiting dominators first. - fn process_ebb(&mut self, - mut ebb: Ebb, - data: &mut Coloring, - func: &mut Function, - tracker: &mut LiveValueTracker) { - // The stack is just a scratch space for this algorithm. We leave it empty when returning. - assert!(data.stack.is_empty()); - - // Trace up the dominator tree until we reach a dominator that has already been visited. - while data.visited.insert(ebb).is_none() { - data.stack.push(ebb); - match self.domtree.idom(ebb) { - Some(idom) => ebb = func.layout.inst_ebb(idom).expect("idom not in layout"), - None => break, - } - } - - // Pop off blocks in topological order. - while let Some(ebb) = data.stack.pop() { + topo.reset(func.layout.ebbs()); + while let Some(ebb) = topo.next(&func.layout, self.domtree) { self.visit_ebb(ebb, func, tracker); } } diff --git a/lib/cretonne/src/regalloc/context.rs b/lib/cretonne/src/regalloc/context.rs index b57edebb5f..b8cebd1bcc 100644 --- a/lib/cretonne/src/regalloc/context.rs +++ b/lib/cretonne/src/regalloc/context.rs @@ -12,11 +12,13 @@ use regalloc::coloring::Coloring; use regalloc::live_value_tracker::LiveValueTracker; use regalloc::liveness::Liveness; use result::CtonResult; +use topo_order::TopoOrder; use verifier::{verify_context, verify_liveness}; /// Persistent memory allocations for register allocation. pub struct Context { liveness: Liveness, + topo: TopoOrder, tracker: LiveValueTracker, coloring: Coloring, } @@ -29,6 +31,7 @@ impl Context { pub fn new() -> Context { Context { liveness: Liveness::new(), + topo: TopoOrder::new(), tracker: LiveValueTracker::new(), coloring: Coloring::new(), } @@ -60,7 +63,12 @@ impl Context { // Third pass: Reload and coloring. self.coloring - .run(isa, func, domtree, &mut self.liveness, &mut self.tracker); + .run(isa, + func, + domtree, + &mut self.liveness, + &mut self.topo, + &mut self.tracker); if isa.flags().enable_verifier() { verify_context(func, cfg, domtree, Some(isa))?; diff --git a/lib/cretonne/src/topo_order.rs b/lib/cretonne/src/topo_order.rs new file mode 100644 index 0000000000..55cece2c2f --- /dev/null +++ b/lib/cretonne/src/topo_order.rs @@ -0,0 +1,125 @@ +//! Topological order of EBBs, according to the dominator tree. + +use dominator_tree::DominatorTree; +use ir::{Ebb, Layout}; +use sparse_map::SparseSet; + +/// Present EBBs in a topological order such that all dominating EBBs are guaranteed to be visited +/// before the current EBB. +/// +/// There are many topological orders of the EBBs in a function, so it is possible to provide a +/// preferred order, and the `TopoOrder` will present EBBs in an order that is as close as possible +/// to the preferred order. +pub struct TopoOrder { + /// Preferred order of EBBs to visit. + preferred: Vec, + + /// Next entry to get from `preferred`. + next: usize, + + /// Set of visited EBBs. + visited: SparseSet, + + /// Stack of EBBs to be visited next, already in `visited`. + stack: Vec, +} + +impl TopoOrder { + /// Create a new empty topological order. + pub fn new() -> TopoOrder { + TopoOrder { + preferred: Vec::new(), + next: 0, + visited: SparseSet::new(), + stack: Vec::new(), + } + } + + /// Reset and initialize with a preferred sequence of EBBs. The resulting topological order is + /// guaranteed to contain all of the EBBs in `preferred` as well as any dominators. + pub fn reset(&mut self, preferred: Ebbs) + where Ebbs: IntoIterator + { + self.preferred.clear(); + self.preferred.extend(preferred); + self.next = 0; + self.visited.clear(); + self.stack.clear(); + } + + /// Get the next EBB in the topological order. + /// + /// Two things are guaranteed about the EBBs returned by this function: + /// + /// - All EBBs in the `preferred` iterator given to `reset` will be returned. + /// - All dominators are visited before the EBB returned. + pub fn next(&mut self, layout: &Layout, domtree: &DominatorTree) -> Option { + // Any entries in `stack` should be returned immediately. They have already been added to + // `visited`. + while self.stack.is_empty() { + match self.preferred.get(self.next).cloned() { + None => return None, + Some(mut ebb) => { + // We have the next EBB in the preferred order. + self.next += 1; + // Push it along with any non-visited dominators. + while self.visited.insert(ebb).is_none() { + self.stack.push(ebb); + match domtree.idom(ebb) { + Some(idom) => ebb = layout.inst_ebb(idom).expect("idom not in layout"), + None => break, + } + } + } + } + } + return self.stack.pop(); + } +} + +#[cfg(test)] +mod test { + use flowgraph::ControlFlowGraph; + use dominator_tree::DominatorTree; + use ir::{Function, InstBuilder, Cursor}; + use std::iter; + use super::*; + + #[test] + fn empty() { + let func = Function::new(); + let cfg = ControlFlowGraph::with_function(&func); + let domtree = DominatorTree::with_function(&func, &cfg); + let mut topo = TopoOrder::new(); + + assert_eq!(topo.next(&func.layout, &domtree), None); + topo.reset(func.layout.ebbs()); + assert_eq!(topo.next(&func.layout, &domtree), None); + } + + #[test] + fn simple() { + let mut func = Function::new(); + let ebb0 = func.dfg.make_ebb(); + let ebb1 = func.dfg.make_ebb(); + + { + let dfg = &mut func.dfg; + let cur = &mut Cursor::new(&mut func.layout); + + cur.insert_ebb(ebb0); + dfg.ins(cur).jump(ebb1, &[]); + cur.insert_ebb(ebb1); + dfg.ins(cur).jump(ebb1, &[]); + } + + let cfg = ControlFlowGraph::with_function(&func); + let domtree = DominatorTree::with_function(&func, &cfg); + let mut topo = TopoOrder::new(); + + topo.reset(iter::once(ebb1)); + assert_eq!(topo.next(&func.layout, &domtree), Some(ebb0)); + assert_eq!(topo.next(&func.layout, &domtree), Some(ebb1)); + assert_eq!(topo.next(&func.layout, &domtree), None); + } +}