Extract the topological ordering into a module.
Multiple passes will need to iterate over EBBs in a dominator-topological order. Move that functionality into a separate module.
This commit is contained in:
@@ -35,4 +35,5 @@ mod packed_option;
|
|||||||
mod partition_slice;
|
mod partition_slice;
|
||||||
mod predicates;
|
mod predicates;
|
||||||
mod ref_slice;
|
mod ref_slice;
|
||||||
|
mod topo_order;
|
||||||
mod write;
|
mod write;
|
||||||
|
|||||||
@@ -43,19 +43,13 @@ use regalloc::affinity::Affinity;
|
|||||||
use regalloc::allocatable_set::AllocatableSet;
|
use regalloc::allocatable_set::AllocatableSet;
|
||||||
use regalloc::live_value_tracker::{LiveValue, LiveValueTracker};
|
use regalloc::live_value_tracker::{LiveValue, LiveValueTracker};
|
||||||
use regalloc::liveness::Liveness;
|
use regalloc::liveness::Liveness;
|
||||||
use sparse_map::SparseSet;
|
use topo_order::TopoOrder;
|
||||||
|
|
||||||
|
|
||||||
/// Data structures for the coloring pass.
|
/// Data structures for the coloring pass.
|
||||||
///
|
///
|
||||||
/// These are scratch space data structures that can be reused between invocations.
|
/// These are scratch space data structures that can be reused between invocations.
|
||||||
pub struct Coloring {
|
pub struct Coloring {}
|
||||||
/// Set of visited EBBs.
|
|
||||||
visited: SparseSet<Ebb>,
|
|
||||||
|
|
||||||
/// Stack of EBBs to be visited next.
|
|
||||||
stack: Vec<Ebb>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Bundle of references that the coloring algorithm needs.
|
/// Bundle of references that the coloring algorithm needs.
|
||||||
///
|
///
|
||||||
@@ -83,10 +77,7 @@ struct Context<'a> {
|
|||||||
impl Coloring {
|
impl Coloring {
|
||||||
/// Allocate scratch space data structures for the coloring pass.
|
/// Allocate scratch space data structures for the coloring pass.
|
||||||
pub fn new() -> Coloring {
|
pub fn new() -> Coloring {
|
||||||
Coloring {
|
Coloring {}
|
||||||
visited: SparseSet::new(),
|
|
||||||
stack: Vec::new(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Run the coloring algorithm over `func`.
|
/// Run the coloring algorithm over `func`.
|
||||||
@@ -95,6 +86,7 @@ impl Coloring {
|
|||||||
func: &mut Function,
|
func: &mut Function,
|
||||||
domtree: &DominatorTree,
|
domtree: &DominatorTree,
|
||||||
liveness: &mut Liveness,
|
liveness: &mut Liveness,
|
||||||
|
topo: &mut TopoOrder,
|
||||||
tracker: &mut LiveValueTracker) {
|
tracker: &mut LiveValueTracker) {
|
||||||
let mut ctx = Context {
|
let mut ctx = Context {
|
||||||
reginfo: isa.register_info(),
|
reginfo: isa.register_info(),
|
||||||
@@ -103,45 +95,17 @@ impl Coloring {
|
|||||||
liveness,
|
liveness,
|
||||||
usable_regs: isa.allocatable_registers(func),
|
usable_regs: isa.allocatable_registers(func),
|
||||||
};
|
};
|
||||||
ctx.run(self, func, tracker)
|
ctx.run(func, topo, tracker)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Context<'a> {
|
impl<'a> Context<'a> {
|
||||||
/// Run the coloring algorithm.
|
/// Run the coloring algorithm.
|
||||||
fn run(&mut self, data: &mut Coloring, func: &mut Function, tracker: &mut LiveValueTracker) {
|
fn run(&mut self, func: &mut Function, topo: &mut TopoOrder, tracker: &mut LiveValueTracker) {
|
||||||
// Just visit blocks in layout order, letting `process_ebb` enforce a topological ordering.
|
// 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.
|
// TODO: Once we have a loop tree, we could visit hot blocks first.
|
||||||
let mut next = func.layout.entry_block();
|
topo.reset(func.layout.ebbs());
|
||||||
while let Some(ebb) = next {
|
while let Some(ebb) = topo.next(&func.layout, self.domtree) {
|
||||||
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() {
|
|
||||||
self.visit_ebb(ebb, func, tracker);
|
self.visit_ebb(ebb, func, tracker);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,11 +12,13 @@ use regalloc::coloring::Coloring;
|
|||||||
use regalloc::live_value_tracker::LiveValueTracker;
|
use regalloc::live_value_tracker::LiveValueTracker;
|
||||||
use regalloc::liveness::Liveness;
|
use regalloc::liveness::Liveness;
|
||||||
use result::CtonResult;
|
use result::CtonResult;
|
||||||
|
use topo_order::TopoOrder;
|
||||||
use verifier::{verify_context, verify_liveness};
|
use verifier::{verify_context, verify_liveness};
|
||||||
|
|
||||||
/// Persistent memory allocations for register allocation.
|
/// Persistent memory allocations for register allocation.
|
||||||
pub struct Context {
|
pub struct Context {
|
||||||
liveness: Liveness,
|
liveness: Liveness,
|
||||||
|
topo: TopoOrder,
|
||||||
tracker: LiveValueTracker,
|
tracker: LiveValueTracker,
|
||||||
coloring: Coloring,
|
coloring: Coloring,
|
||||||
}
|
}
|
||||||
@@ -29,6 +31,7 @@ impl Context {
|
|||||||
pub fn new() -> Context {
|
pub fn new() -> Context {
|
||||||
Context {
|
Context {
|
||||||
liveness: Liveness::new(),
|
liveness: Liveness::new(),
|
||||||
|
topo: TopoOrder::new(),
|
||||||
tracker: LiveValueTracker::new(),
|
tracker: LiveValueTracker::new(),
|
||||||
coloring: Coloring::new(),
|
coloring: Coloring::new(),
|
||||||
}
|
}
|
||||||
@@ -60,7 +63,12 @@ impl Context {
|
|||||||
|
|
||||||
// Third pass: Reload and coloring.
|
// Third pass: Reload and coloring.
|
||||||
self.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() {
|
if isa.flags().enable_verifier() {
|
||||||
verify_context(func, cfg, domtree, Some(isa))?;
|
verify_context(func, cfg, domtree, Some(isa))?;
|
||||||
|
|||||||
125
lib/cretonne/src/topo_order.rs
Normal file
125
lib/cretonne/src/topo_order.rs
Normal file
@@ -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<Ebb>,
|
||||||
|
|
||||||
|
/// Next entry to get from `preferred`.
|
||||||
|
next: usize,
|
||||||
|
|
||||||
|
/// Set of visited EBBs.
|
||||||
|
visited: SparseSet<Ebb>,
|
||||||
|
|
||||||
|
/// Stack of EBBs to be visited next, already in `visited`.
|
||||||
|
stack: Vec<Ebb>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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<Ebbs>(&mut self, preferred: Ebbs)
|
||||||
|
where Ebbs: IntoIterator<Item = Ebb>
|
||||||
|
{
|
||||||
|
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<Ebb> {
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user