diff --git a/lib/cretonne/src/context.rs b/lib/cretonne/src/context.rs index ce4b269574..9b0b3b31a7 100644 --- a/lib/cretonne/src/context.rs +++ b/lib/cretonne/src/context.rs @@ -12,6 +12,7 @@ use dominator_tree::DominatorTree; use flowgraph::ControlFlowGraph; use ir::Function; +use loop_analysis::LoopAnalysis; use isa::TargetIsa; use legalize_function; use regalloc; @@ -32,6 +33,9 @@ pub struct Context { /// Register allocation context. pub regalloc: regalloc::Context, + + /// Loop analysis of `func`. + pub loop_analysis: LoopAnalysis, } impl Context { @@ -45,6 +49,7 @@ impl Context { cfg: ControlFlowGraph::new(), domtree: DominatorTree::new(), regalloc: regalloc::Context::new(), + loop_analysis: LoopAnalysis::new(), } } diff --git a/lib/cretonne/src/entity_list.rs b/lib/cretonne/src/entity_list.rs index 5121d8e4cc..946bb7ba04 100644 --- a/lib/cretonne/src/entity_list.rs +++ b/lib/cretonne/src/entity_list.rs @@ -94,7 +94,7 @@ impl PartialEq for EntityList { impl Eq for EntityList {} /// A memory pool for storing lists of `T`. -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct ListPool { // The main array containing the lists. data: Vec, diff --git a/lib/cretonne/src/entity_map.rs b/lib/cretonne/src/entity_map.rs index cee4215a4e..8227a19fb6 100644 --- a/lib/cretonne/src/entity_map.rs +++ b/lib/cretonne/src/entity_map.rs @@ -71,7 +71,7 @@ impl EntityMap pub fn keys(&self) -> Keys { Keys { pos: 0, - len: self.elems.len(), + rev_pos: self.elems.len(), unused: PhantomData, } } @@ -183,7 +183,7 @@ pub struct Keys where K: EntityRef { pos: usize, - len: usize, + rev_pos: usize, unused: PhantomData, } @@ -193,7 +193,7 @@ impl Iterator for Keys type Item = K; fn next(&mut self) -> Option { - if self.pos < self.len { + if self.pos < self.rev_pos { let k = K::new(self.pos); self.pos += 1; Some(k) @@ -203,6 +203,20 @@ impl Iterator for Keys } } +impl DoubleEndedIterator for Keys + where K: EntityRef +{ + fn next_back(&mut self) -> Option { + if self.rev_pos > self.pos { + let k = K::new(self.rev_pos - 1); + self.rev_pos -= 1; + Some(k) + } else { + None + } + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/lib/cretonne/src/ir/dfg.rs b/lib/cretonne/src/ir/dfg.rs index 35d7a18fa4..c3c5422e75 100644 --- a/lib/cretonne/src/ir/dfg.rs +++ b/lib/cretonne/src/ir/dfg.rs @@ -8,7 +8,6 @@ use ir::layout::Cursor; use ir::types; use ir::{Ebb, Inst, Value, Type, SigRef, Signature, FuncRef, ValueList, ValueListPool}; use write::write_operands; - use std::fmt; use std::iter; use std::ops::{Index, IndexMut}; diff --git a/lib/cretonne/src/lib.rs b/lib/cretonne/src/lib.rs index 264e808cbf..5ac263d4ff 100644 --- a/lib/cretonne/src/lib.rs +++ b/lib/cretonne/src/lib.rs @@ -20,6 +20,7 @@ pub mod entity_map; pub mod flowgraph; pub mod ir; pub mod isa; +pub mod loop_analysis; pub mod regalloc; pub mod result; pub mod settings; diff --git a/lib/cretonne/src/loop_analysis.rs b/lib/cretonne/src/loop_analysis.rs new file mode 100644 index 0000000000..1e36ced8f9 --- /dev/null +++ b/lib/cretonne/src/loop_analysis.rs @@ -0,0 +1,336 @@ +//! A loop analysis represented as mappings of loops to their header Ebb +//! and parent in the loop tree. + +use ir::{Function, Ebb, Layout}; +use flowgraph::ControlFlowGraph; +use dominator_tree::DominatorTree; +use entity_map::{EntityMap, PrimaryEntityData}; +use packed_option::{PackedOption, ReservedValue}; +use entity_map::{EntityRef, Keys}; +use std::u32; + +/// A opaque reference to a code loop. +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub struct Loop(u32); +impl EntityRef for Loop { + fn new(index: usize) -> Self { + assert!(index < (u32::MAX as usize)); + Loop(index as u32) + } + + fn index(self) -> usize { + self.0 as usize + } +} + +impl ReservedValue for Loop { + fn reserved_value() -> Loop { + Loop(u32::MAX) + } +} + + +/// Loop tree information for a single function. +/// +/// Loops are referenced by the Loop object, and for each loop you can access its header EBB, +/// its eventual parent in the loop tree and all the EBB belonging to the loop. +pub struct LoopAnalysis { + loops: EntityMap, + ebb_loop_map: EntityMap>, +} + +struct LoopData { + header: Ebb, + parent: PackedOption, +} + +impl PrimaryEntityData for LoopData {} + +impl LoopData { + /// Creates a `LoopData` object with the loop header and its eventual parent in the loop tree. + pub fn new(header: Ebb, parent: Option) -> LoopData { + LoopData { + header: header, + parent: parent.into(), + } + } +} + +/// Methods for querying the loop analysis. +impl LoopAnalysis { + /// Allocate a new blank loop analysis struct. Use `compute` to compute the loop analysis for + /// a function. + pub fn new() -> LoopAnalysis { + LoopAnalysis { + loops: EntityMap::new(), + ebb_loop_map: EntityMap::new(), + } + } + + /// Returns all the loops contained in a function. + pub fn loops(&self) -> Keys { + self.loops.keys() + } + + /// Returns the header EBB of a particular loop. + /// + /// The characteristic property of a loop header block is that it dominates some of its + /// predecessors. + pub fn loop_header(&self, lp: Loop) -> Ebb { + self.loops[lp].header + } + + /// Return the eventual parent of a loop in the loop tree. + pub fn loop_parent(&self, lp: Loop) -> Option { + self.loops[lp].parent.expand() + } + + /// Determine if an Ebb belongs to a loop by running a finger along the loop tree. + /// + /// Returns `true` if `ebb` is in loop `lp`. + pub fn is_in_loop(&self, ebb: Ebb, lp: Loop) -> bool { + let ebb_loop = self.ebb_loop_map[ebb]; + match ebb_loop.expand() { + None => false, + Some(ebb_loop) => self.is_child_loop(ebb_loop, lp), + } + } + + /// Determines if a loop is contained in another loop. + /// + /// `is_child_loop(child,parent)` returns `true` if and only if `child` is a child loop of + /// `parent` (or `child == parent`). + pub fn is_child_loop(&self, child: Loop, parent: Loop) -> bool { + let mut finger = Some(child); + while let Some(finger_loop) = finger { + if finger_loop == parent { + return true; + } + finger = self.loop_parent(finger_loop); + } + false + } +} + +impl LoopAnalysis { + /// Detects the loops in a function. Needs the control flow graph and the dominator tree. + pub fn compute(&mut self, func: &Function, cfg: &ControlFlowGraph, domtree: &DominatorTree) { + self.loops.clear(); + self.ebb_loop_map.clear(); + self.ebb_loop_map.resize(func.dfg.num_ebbs()); + self.find_loop_headers(cfg, domtree, &func.layout); + self.discover_loop_blocks(cfg, domtree, &func.layout) + } + + // Traverses the CFG in reverse postorder and create a loop object for every EBB having a + // back edge. + fn find_loop_headers(&mut self, + cfg: &ControlFlowGraph, + domtree: &DominatorTree, + layout: &Layout) { + // We traverse the CFg in reverse postorder + for ebb in cfg.postorder_ebbs().iter().rev() { + for &(_, pred_inst) in cfg.get_predecessors(*ebb) { + // If the ebb dominates one of its predecessors it is a back edge + if domtree.ebb_dominates(ebb.clone(), pred_inst, layout) { + // This ebb is a loop header, so we create its associated loop + let lp = self.loops.push(LoopData::new(*ebb, None)); + self.ebb_loop_map[*ebb] = lp.into(); + break; + // We break because we only need one back edge to identify a loop header. + } + } + } + } + + // Intended to be called after `find_loop_headers`. For each detected loop header, + // discovers all the ebb belonging to the loop and its inner loops. After a call to this + // function, the loop tree is fully constructed. + fn discover_loop_blocks(&mut self, + cfg: &ControlFlowGraph, + domtree: &DominatorTree, + layout: &Layout) { + let mut stack: Vec = Vec::new(); + // We handle each loop header in reverse order, corresponding to a pesudo postorder + // traversal of the graph. + for lp in self.loops().rev() { + for &(pred, pred_inst) in cfg.get_predecessors(self.loops[lp].header) { + // We follow the back edges + if domtree.ebb_dominates(self.loops[lp].header, pred_inst, layout) { + stack.push(pred); + } + } + while let Some(node) = stack.pop() { + let continue_dfs: Option; + match self.ebb_loop_map[node].expand() { + None => { + // The node hasn't been visited yet, we tag it as part of the loop + self.ebb_loop_map[node] = PackedOption::from(lp); + continue_dfs = Some(node); + } + Some(node_loop) => { + // We copy the node_loop into a mutable reference passed along the while + let mut node_loop = node_loop; + // The node is part of a loop, which can be lp or an inner loop + let mut node_loop_parent_option = self.loops[node_loop].parent; + while let Some(node_loop_parent) = node_loop_parent_option.expand() { + if node_loop_parent == lp { + // We have encounterd lp so we stop (already visited) + break; + } else { + // + node_loop = node_loop_parent; + // We lookup the parent loop + node_loop_parent_option = self.loops[node_loop].parent; + } + } + // Now node_loop_parent is either: + // - None and node_loop is an new inner loop of lp + // - Some(...) and the initial node_loop was a known inner loop of lp + match node_loop_parent_option.expand() { + Some(_) => continue_dfs = None, + None => { + if node_loop != lp { + self.loops[node_loop].parent = lp.into(); + continue_dfs = Some(self.loops[node_loop].header) + } else { + // If lp is a one-block loop then we make sure we stop + continue_dfs = None + } + } + } + } + } + // Now we have handled the popped node and need to continue the DFS by adding the + // predecessors of that node + if let Some(continue_dfs) = continue_dfs { + for &(pred, _) in cfg.get_predecessors(continue_dfs) { + stack.push(pred) + } + } + } + + } + } +} + +#[cfg(test)] +mod test { + + use ir::{Function, InstBuilder, Cursor, types}; + use loop_analysis::{Loop, LoopAnalysis}; + use flowgraph::ControlFlowGraph; + use dominator_tree::DominatorTree; + + #[test] + fn nested_loops_detection() { + 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_arg(ebb0, types::I32); + + { + 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(ebb2, &[]); + + cur.insert_ebb(ebb2); + dfg.ins(cur).brnz(cond, ebb1, &[]); + dfg.ins(cur).jump(ebb3, &[]); + + cur.insert_ebb(ebb3); + dfg.ins(cur).brnz(cond, ebb0, &[]); + + } + + let mut loop_analysis = LoopAnalysis::new(); + let mut cfg = ControlFlowGraph::new(); + let mut domtree = DominatorTree::new(); + cfg.compute(&func); + domtree.compute(&func, &cfg); + loop_analysis.compute(&func, &cfg, &domtree); + + let loops = loop_analysis.loops().collect::>(); + assert_eq!(loops.len(), 2); + assert_eq!(loop_analysis.loop_header(loops[0]), ebb0); + assert_eq!(loop_analysis.loop_header(loops[1]), ebb1); + assert_eq!(loop_analysis.loop_parent(loops[1]), Some(loops[0])); + assert_eq!(loop_analysis.loop_parent(loops[0]), None); + assert_eq!(loop_analysis.is_in_loop(ebb0, loops[0]), true); + assert_eq!(loop_analysis.is_in_loop(ebb0, loops[1]), false); + assert_eq!(loop_analysis.is_in_loop(ebb1, loops[1]), true); + assert_eq!(loop_analysis.is_in_loop(ebb1, loops[0]), true); + assert_eq!(loop_analysis.is_in_loop(ebb2, loops[1]), true); + assert_eq!(loop_analysis.is_in_loop(ebb2, loops[0]), true); + assert_eq!(loop_analysis.is_in_loop(ebb3, loops[0]), true); + assert_eq!(loop_analysis.is_in_loop(ebb0, loops[1]), false); + } + + #[test] + fn complex_loop_detection() { + 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 ebb4 = func.dfg.make_ebb(); + let ebb5 = func.dfg.make_ebb(); + let cond = func.dfg.append_ebb_arg(ebb0, types::I32); + + { + let dfg = &mut func.dfg; + let cur = &mut Cursor::new(&mut func.layout); + + cur.insert_ebb(ebb0); + dfg.ins(cur).brnz(cond, ebb1, &[]); + dfg.ins(cur).jump(ebb3, &[]); + + cur.insert_ebb(ebb1); + dfg.ins(cur).jump(ebb2, &[]); + + cur.insert_ebb(ebb2); + dfg.ins(cur).brnz(cond, ebb1, &[]); + dfg.ins(cur).jump(ebb5, &[]); + + cur.insert_ebb(ebb3); + dfg.ins(cur).jump(ebb4, &[]); + + cur.insert_ebb(ebb4); + dfg.ins(cur).brnz(cond, ebb3, &[]); + dfg.ins(cur).jump(ebb5, &[]); + + cur.insert_ebb(ebb5); + dfg.ins(cur).brnz(cond, ebb0, &[]); + + } + + let mut loop_analysis = LoopAnalysis::new(); + let mut cfg = ControlFlowGraph::new(); + let mut domtree = DominatorTree::new(); + cfg.compute(&func); + domtree.compute(&func, &cfg); + loop_analysis.compute(&func, &cfg, &domtree); + + let loops = loop_analysis.loops().collect::>(); + assert_eq!(loops.len(), 3); + assert_eq!(loop_analysis.loop_header(loops[0]), ebb0); + assert_eq!(loop_analysis.loop_header(loops[1]), ebb1); + assert_eq!(loop_analysis.loop_header(loops[2]), ebb3); + assert_eq!(loop_analysis.loop_parent(loops[1]), Some(loops[0])); + assert_eq!(loop_analysis.loop_parent(loops[2]), Some(loops[0])); + assert_eq!(loop_analysis.loop_parent(loops[0]), None); + assert_eq!(loop_analysis.is_in_loop(ebb0, loops[0]), true); + assert_eq!(loop_analysis.is_in_loop(ebb1, loops[1]), true); + assert_eq!(loop_analysis.is_in_loop(ebb2, loops[1]), true); + assert_eq!(loop_analysis.is_in_loop(ebb3, loops[2]), true); + assert_eq!(loop_analysis.is_in_loop(ebb4, loops[2]), true); + assert_eq!(loop_analysis.is_in_loop(ebb5, loops[0]), true); + } +}