]> git.lizzy.rs Git - rust.git/commitdiff
add control flow graph and algorithms. add dominator to mir
authorScott A Carr <s.carr1024@gmail.com>
Thu, 9 Jun 2016 22:49:07 +0000 (15:49 -0700)
committerScott A Carr <s.carr1024@gmail.com>
Thu, 23 Jun 2016 21:00:00 +0000 (14:00 -0700)
14 files changed:
src/librustc/mir/cache.rs
src/librustc/mir/repr.rs
src/librustc_data_structures/control_flow_graph/dominators/mod.rs [new file with mode: 0644]
src/librustc_data_structures/control_flow_graph/dominators/test.rs [new file with mode: 0644]
src/librustc_data_structures/control_flow_graph/iterate/mod.rs [new file with mode: 0644]
src/librustc_data_structures/control_flow_graph/iterate/test.rs [new file with mode: 0644]
src/librustc_data_structures/control_flow_graph/mod.rs [new file with mode: 0644]
src/librustc_data_structures/control_flow_graph/reachable/mod.rs [new file with mode: 0644]
src/librustc_data_structures/control_flow_graph/reachable/test.rs [new file with mode: 0644]
src/librustc_data_structures/control_flow_graph/reference.rs [new file with mode: 0644]
src/librustc_data_structures/control_flow_graph/test.rs [new file with mode: 0644]
src/librustc_data_structures/control_flow_graph/transpose.rs [new file with mode: 0644]
src/librustc_data_structures/indexed_vec.rs
src/librustc_data_structures/lib.rs

index 138fed2d64e23cb7375060b62dc9d043f6821869..1be7d00f072cd884b263ab2ae9208a3f2cce93a3 100644 (file)
@@ -15,7 +15,7 @@
 
 use rustc_serialize as serialize;
 
-#[derive(Clone)]
+#[derive(Clone, Debug)]
 pub struct Cache {
     predecessors: RefCell<Option<IndexVec<BasicBlock, Vec<BasicBlock>>>>
 }
index a6052f9aa75a13519ecd4ea96e4b647ebfc189ff..d39ff28841851d8a2539bb419a4e3e7eccf1730b 100644 (file)
@@ -12,6 +12,9 @@
 use middle::const_val::ConstVal;
 use rustc_const_math::{ConstUsize, ConstInt, ConstMathErr};
 use rustc_data_structures::indexed_vec::{IndexVec, Idx};
+use rustc_data_structures::control_flow_graph::dominators::{Dominators, dominators};
+use rustc_data_structures::control_flow_graph::{GraphPredecessors, GraphSuccessors};
+use rustc_data_structures::control_flow_graph::ControlFlowGraph;
 use hir::def_id::DefId;
 use ty::subst::Substs;
 use ty::{self, AdtDef, ClosureSubsts, FnOutput, Region, Ty};
@@ -24,6 +27,7 @@
 use std::fmt::{self, Debug, Formatter, Write};
 use std::{iter, u32};
 use std::ops::{Index, IndexMut};
+use std::vec::IntoIter;
 use syntax::ast::{self, Name};
 use syntax::codemap::Span;
 
@@ -54,7 +58,7 @@ fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
 }
 
 /// Lowered representation of a single function.
-#[derive(Clone, RustcEncodable, RustcDecodable)]
+#[derive(Clone, RustcEncodable, RustcDecodable, Debug)]
 pub struct Mir<'tcx> {
     /// List of basic blocks. References to basic block use a newtyped index type `BasicBlock`
     /// that indexes into this vector.
@@ -145,6 +149,11 @@ pub fn predecessors_for(&self, bb: BasicBlock) -> Ref<Vec<BasicBlock>> {
         Ref::map(self.predecessors(), |p| &p[bb])
     }
 
+    #[inline]
+    pub fn dominators(&self) -> Dominators<BasicBlock> {
+        dominators(self)
+    }
+
     /// Maps locals (Arg's, Var's, Temp's and ReturnPointer, in that order)
     /// to their index in the whole list of locals. This is useful if you
     /// want to treat all locals the same instead of repeating yourself.
@@ -1190,3 +1199,33 @@ fn node_to_string(node_id: ast::NodeId) -> String {
 fn item_path_str(def_id: DefId) -> String {
     ty::tls::with(|tcx| tcx.item_path_str(def_id))
 }
+
+impl<'tcx> ControlFlowGraph for Mir<'tcx> {
+
+    type Node = BasicBlock;
+
+    fn num_nodes(&self) -> usize { self.basic_blocks.len() }
+
+    fn start_node(&self) -> Self::Node { START_BLOCK }
+
+    fn predecessors<'graph>(&'graph self, node: Self::Node)
+                            -> <Self as GraphPredecessors<'graph>>::Iter
+    {
+        self.predecessors_for(node).clone().into_iter()
+    }
+    fn successors<'graph>(&'graph self, node: Self::Node)
+                          -> <Self as GraphSuccessors<'graph>>::Iter
+    {
+        self.basic_blocks[node].terminator().successors().into_owned().into_iter()
+    }
+}
+
+impl<'a, 'b> GraphPredecessors<'b> for Mir<'a> {
+    type Item = BasicBlock;
+    type Iter = IntoIter<BasicBlock>;
+}
+
+impl<'a, 'b>  GraphSuccessors<'b> for Mir<'a> {
+    type Item = BasicBlock;
+    type Iter = IntoIter<BasicBlock>;
+}
diff --git a/src/librustc_data_structures/control_flow_graph/dominators/mod.rs b/src/librustc_data_structures/control_flow_graph/dominators/mod.rs
new file mode 100644 (file)
index 0000000..250b89d
--- /dev/null
@@ -0,0 +1,284 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! Algorithm citation:
+//! A Simple, Fast Dominance Algorithm.
+//! Keith D. Cooper, Timothy J. Harvey, and Ken Kennedy
+//! Rice Computer Science TS-06-33870
+//! https://www.cs.rice.edu/~keith/EMBED/dom.pdf
+
+use super::ControlFlowGraph;
+use super::iterate::reverse_post_order;
+use super::super::indexed_vec::{IndexVec, Idx};
+
+use std::fmt;
+
+#[cfg(test)]
+mod test;
+
+pub fn dominators<G: ControlFlowGraph>(graph: &G) -> Dominators<G::Node> {
+    let start_node = graph.start_node();
+    let rpo = reverse_post_order(graph, start_node);
+    dominators_given_rpo(graph, &rpo)
+}
+
+pub fn dominators_given_rpo<G: ControlFlowGraph>(graph: &G,
+                                                 rpo: &[G::Node])
+                                                 -> Dominators<G::Node> {
+    let start_node = graph.start_node();
+    assert_eq!(rpo[0], start_node);
+
+    // compute the post order index (rank) for each node
+    let mut post_order_rank: IndexVec<G::Node, usize> = IndexVec::from_elem_n(usize::default(),
+                                                                              graph.num_nodes());
+    for (index, node) in rpo.iter().rev().cloned().enumerate() {
+        post_order_rank[node] = index;
+    }
+
+    let mut immediate_dominators: IndexVec<G::Node, Option<G::Node>> =
+        IndexVec::from_elem_n(Option::default(), graph.num_nodes());
+    immediate_dominators[start_node] = Some(start_node);
+
+    let mut changed = true;
+    while changed {
+        changed = false;
+
+        for &node in &rpo[1..] {
+            let mut new_idom = None;
+            for pred in graph.predecessors(node) {
+                if immediate_dominators[pred].is_some() {
+                    // (*)
+                    // (*) dominators for `pred` have been calculated
+                    new_idom = intersect_opt(&post_order_rank,
+                                                  &immediate_dominators,
+                                                  new_idom,
+                                                  Some(pred));
+                }
+            }
+
+            if new_idom != immediate_dominators[node] {
+                immediate_dominators[node] = new_idom;
+                changed = true;
+            }
+        }
+    }
+
+    Dominators {
+        post_order_rank: post_order_rank,
+        immediate_dominators: immediate_dominators,
+    }
+}
+
+fn intersect_opt<Node: Idx>(post_order_rank: &IndexVec<Node, usize>,
+                                      immediate_dominators: &IndexVec<Node, Option<Node>>,
+                                      node1: Option<Node>,
+                                      node2: Option<Node>)
+                                      -> Option<Node> {
+    match (node1, node2) {
+        (None, None) => None,
+        (Some(n), None) | (None, Some(n)) => Some(n),
+        (Some(n1), Some(n2)) => Some(intersect(post_order_rank, immediate_dominators, n1, n2)),
+    }
+}
+
+fn intersect<Node: Idx>(post_order_rank: &IndexVec<Node, usize>,
+                                  immediate_dominators: &IndexVec<Node, Option<Node>>,
+                                  mut node1: Node,
+                                  mut node2: Node)
+                                  -> Node {
+    while node1 != node2 {
+        while post_order_rank[node1] < post_order_rank[node2] {
+            node1 = immediate_dominators[node1].unwrap();
+        }
+
+        while post_order_rank[node2] < post_order_rank[node1] {
+            node2 = immediate_dominators[node2].unwrap();
+        }
+    }
+    return node1;
+}
+
+#[derive(Clone, Debug)]
+pub struct Dominators<N: Idx> {
+    post_order_rank: IndexVec<N, usize>,
+    immediate_dominators: IndexVec<N, Option<N>>,
+}
+
+impl<Node: Idx> Dominators<Node> {
+    pub fn is_reachable(&self, node: Node) -> bool {
+        self.immediate_dominators[node].is_some()
+    }
+
+    pub fn immediate_dominator(&self, node: Node) -> Node {
+        assert!(self.is_reachable(node), "node {:?} is not reachable", node);
+        self.immediate_dominators[node].unwrap()
+    }
+
+    pub fn dominators(&self, node: Node) -> Iter<Node> {
+        assert!(self.is_reachable(node), "node {:?} is not reachable", node);
+        Iter {
+            dominators: self,
+            node: Some(node),
+        }
+    }
+
+    pub fn is_dominated_by(&self, node: Node, dom: Node) -> bool {
+        // FIXME -- could be optimized by using post-order-rank
+        self.dominators(node).any(|n| n == dom)
+    }
+
+    pub fn mutual_dominator_node(&self, node1: Node, node2: Node) -> Node {
+        assert!(self.is_reachable(node1),
+                "node {:?} is not reachable",
+                node1);
+        assert!(self.is_reachable(node2),
+                "node {:?} is not reachable",
+                node2);
+        intersect::<Node>(&self.post_order_rank,
+                  &self.immediate_dominators,
+                  node1,
+                  node2)
+    }
+
+    pub fn mutual_dominator<I>(&self, iter: I) -> Option<Node>
+        where I: IntoIterator<Item = Node>
+    {
+        let mut iter = iter.into_iter();
+        iter.next()
+            .map(|dom| iter.fold(dom, |dom, node| self.mutual_dominator_node(dom, node)))
+    }
+
+    pub fn all_immediate_dominators(&self) -> &IndexVec<Node, Option<Node>> {
+        &self.immediate_dominators
+    }
+
+    pub fn dominator_tree(&self) -> DominatorTree<Node> {
+        let elem: Vec<Node> = Vec::new();
+        let mut children: IndexVec<Node, Vec<Node>> =
+            IndexVec::from_elem_n(elem, self.immediate_dominators.len());
+        let mut root = None;
+        for (index, immed_dom) in self.immediate_dominators.iter().enumerate() {
+            let node = Node::new(index);
+            match *immed_dom {
+                None => {
+                    // node not reachable
+                }
+                Some(immed_dom) => {
+                    if node == immed_dom {
+                        root = Some(node);
+                    } else {
+                        children[immed_dom].push(node);
+                    }
+                }
+            }
+        }
+        DominatorTree {
+            root: root.unwrap(),
+            children: children,
+        }
+    }
+}
+
+pub struct Iter<'dom, Node: Idx + 'dom> {
+    dominators: &'dom Dominators<Node>,
+    node: Option<Node>,
+}
+
+impl<'dom, Node: Idx> Iterator for Iter<'dom, Node> {
+    type Item = Node;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        if let Some(node) = self.node {
+            let dom = self.dominators.immediate_dominator(node);
+            if dom == node {
+                self.node = None; // reached the root
+            } else {
+                self.node = Some(dom);
+            }
+            return Some(node);
+        } else {
+            return None;
+        }
+    }
+}
+
+pub struct DominatorTree<N: Idx> {
+    root: N,
+    children: IndexVec<N, Vec<N>>,
+}
+
+impl<Node: Idx> DominatorTree<Node> {
+    pub fn root(&self) -> Node {
+        self.root
+    }
+
+    pub fn children(&self, node: Node) -> &[Node] {
+        &self.children[node]
+    }
+
+    pub fn iter_children_of(&self, node: Node) -> IterChildrenOf<Node> {
+        IterChildrenOf {
+            tree: self,
+            stack: vec![node],
+        }
+    }
+}
+
+pub struct IterChildrenOf<'iter, Node: Idx + 'iter> {
+    tree: &'iter DominatorTree<Node>,
+    stack: Vec<Node>,
+}
+
+impl<'iter, Node: Idx> Iterator for IterChildrenOf<'iter, Node> {
+    type Item = Node;
+
+    fn next(&mut self) -> Option<Node> {
+        if let Some(node) = self.stack.pop() {
+            self.stack.extend(self.tree.children(node));
+            Some(node)
+        } else {
+            None
+        }
+    }
+}
+
+impl<Node: Idx> fmt::Debug for DominatorTree<Node> {
+    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
+        fmt::Debug::fmt(&DominatorTreeNode {
+                            tree: self,
+                            node: self.root,
+                        },
+                        fmt)
+    }
+}
+
+struct DominatorTreeNode<'tree, Node: Idx> {
+    tree: &'tree DominatorTree<Node>,
+    node: Node,
+}
+
+impl<'tree, Node: Idx> fmt::Debug for DominatorTreeNode<'tree, Node> {
+    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
+        let subtrees: Vec<_> = self.tree
+            .children(self.node)
+            .iter()
+            .map(|&child| {
+                DominatorTreeNode {
+                    tree: self.tree,
+                    node: child,
+                }
+            })
+            .collect();
+        fmt.debug_tuple("")
+            .field(&self.node)
+            .field(&subtrees)
+            .finish()
+    }
+}
diff --git a/src/librustc_data_structures/control_flow_graph/dominators/test.rs b/src/librustc_data_structures/control_flow_graph/dominators/test.rs
new file mode 100644 (file)
index 0000000..a6db5f2
--- /dev/null
@@ -0,0 +1,57 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use super::super::test::TestGraph;
+
+use super::*;
+
+#[test]
+fn diamond() {
+    let graph = TestGraph::new(0, &[
+        (0, 1),
+        (0, 2),
+        (1, 3),
+        (2, 3),
+    ]);
+
+    let dominators = dominators(&graph);
+    let immediate_dominators = dominators.all_immediate_dominators();
+    assert_eq!(immediate_dominators[0], Some(0));
+    assert_eq!(immediate_dominators[1], Some(0));
+    assert_eq!(immediate_dominators[2], Some(0));
+    assert_eq!(immediate_dominators[3], Some(0));
+}
+
+#[test]
+fn paper() {
+    // example from the paper:
+    let graph = TestGraph::new(6, &[
+        (6, 5),
+        (6, 4),
+        (5, 1),
+        (4, 2),
+        (4, 3),
+        (1, 2),
+        (2, 3),
+        (3, 2),
+        (2, 1),
+    ]);
+
+    let dominators = dominators(&graph);
+    let immediate_dominators = dominators.all_immediate_dominators();
+    assert_eq!(immediate_dominators[0], None); // <-- note that 0 is not in graph
+    assert_eq!(immediate_dominators[1], Some(6));
+    assert_eq!(immediate_dominators[2], Some(6));
+    assert_eq!(immediate_dominators[3], Some(6));
+    assert_eq!(immediate_dominators[4], Some(6));
+    assert_eq!(immediate_dominators[5], Some(6));
+    assert_eq!(immediate_dominators[6], Some(6));
+}
+
diff --git a/src/librustc_data_structures/control_flow_graph/iterate/mod.rs b/src/librustc_data_structures/control_flow_graph/iterate/mod.rs
new file mode 100644 (file)
index 0000000..11b557c
--- /dev/null
@@ -0,0 +1,70 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use super::ControlFlowGraph;
+use super::super::indexed_vec::IndexVec;
+
+#[cfg(test)]
+mod test;
+
+pub fn post_order_from<G: ControlFlowGraph>(graph: &G, start_node: G::Node) -> Vec<G::Node> {
+    post_order_from_to(graph, start_node, None)
+}
+
+pub fn post_order_from_to<G: ControlFlowGraph>(graph: &G,
+                                               start_node: G::Node,
+                                               end_node: Option<G::Node>)
+                                               -> Vec<G::Node> {
+    let mut visited: IndexVec<G::Node, bool> = IndexVec::from_elem_n(false, graph.num_nodes());
+    let mut result: Vec<G::Node> = Vec::with_capacity(graph.num_nodes());
+    if let Some(end_node) = end_node {
+        visited[end_node] = true;
+    }
+    post_order_walk(graph, start_node, &mut result, &mut visited);
+    result
+}
+
+fn post_order_walk<G: ControlFlowGraph>(graph: &G,
+                                        node: G::Node,
+                                        result: &mut Vec<G::Node>,
+                                        visited: &mut IndexVec<G::Node, bool>) {
+    if visited[node] {
+        return;
+    }
+    visited[node] = true;
+
+    for successor in graph.successors(node) {
+        post_order_walk(graph, successor, result, visited);
+    }
+
+    result.push(node);
+}
+
+pub fn pre_order_walk<G: ControlFlowGraph>(graph: &G,
+                                           node: G::Node,
+                                           result: &mut Vec<G::Node>,
+                                           visited: &mut IndexVec<G::Node, bool>) {
+    if visited[node] {
+        return;
+    }
+    visited[node] = true;
+
+    result.push(node);
+
+    for successor in graph.successors(node) {
+        pre_order_walk(graph, successor, result, visited);
+    }
+}
+
+pub fn reverse_post_order<G: ControlFlowGraph>(graph: &G, start_node: G::Node) -> Vec<G::Node> {
+    let mut vec = post_order_from(graph, start_node);
+    vec.reverse();
+    vec
+}
diff --git a/src/librustc_data_structures/control_flow_graph/iterate/test.rs b/src/librustc_data_structures/control_flow_graph/iterate/test.rs
new file mode 100644 (file)
index 0000000..28297d5
--- /dev/null
@@ -0,0 +1,55 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use super::super::test::TestGraph;
+use super::super::transpose::TransposedGraph;
+
+use super::*;
+
+#[test]
+fn diamond_post_order() {
+    let graph = TestGraph::new(0, &[
+        (0, 1),
+        (0, 2),
+        (1, 3),
+        (2, 3),
+    ]);
+
+    let result = post_order_from(&graph, 0);
+    assert_eq!(result, vec![3, 1, 2, 0]);
+}
+
+
+#[test]
+fn rev_post_order_inner_loop() {
+    // 0 -> 1 ->     2     -> 3 -> 5
+    //      ^     ^    v      |
+    //      |     6 <- 4      |
+    //      +-----------------+
+    let graph = TestGraph::new(0, &[
+        (0, 1),
+        (1, 2),
+        (2, 3),
+        (3, 5),
+        (3, 1),
+        (2, 4),
+        (4, 6),
+        (6, 2),
+    ]);
+
+    let rev_graph = TransposedGraph::new(&graph);
+
+    let result = post_order_from_to(&rev_graph, 6, Some(2));
+    assert_eq!(result, vec![4, 6]);
+
+    let result = post_order_from_to(&rev_graph, 3, Some(1));
+    assert_eq!(result, vec![4, 6, 2, 3]);
+}
+
diff --git a/src/librustc_data_structures/control_flow_graph/mod.rs b/src/librustc_data_structures/control_flow_graph/mod.rs
new file mode 100644 (file)
index 0000000..f9e75b1
--- /dev/null
@@ -0,0 +1,45 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use super::indexed_vec::Idx;
+pub use std::slice::Iter;
+
+pub mod dominators;
+pub mod iterate;
+pub mod reachable;
+mod reference;
+pub mod transpose;
+
+#[cfg(test)]
+mod test;
+
+pub trait ControlFlowGraph
+    where Self: for<'graph> GraphPredecessors<'graph, Item=<Self as ControlFlowGraph>::Node>,
+          Self: for<'graph> GraphSuccessors<'graph, Item=<Self as ControlFlowGraph>::Node>
+{
+    type Node: Idx;
+
+    fn num_nodes(&self) -> usize;
+    fn start_node(&self) -> Self::Node;
+    fn predecessors<'graph>(&'graph self, node: Self::Node)
+                            -> <Self as GraphPredecessors<'graph>>::Iter;
+    fn successors<'graph>(&'graph self, node: Self::Node)
+                            -> <Self as GraphSuccessors<'graph>>::Iter;
+}
+
+pub trait GraphPredecessors<'graph> {
+    type Item;
+    type Iter: Iterator<Item=Self::Item>;
+}
+
+pub trait GraphSuccessors<'graph> {
+    type Item;
+    type Iter: Iterator<Item=Self::Item>;
+}
\ No newline at end of file
diff --git a/src/librustc_data_structures/control_flow_graph/reachable/mod.rs b/src/librustc_data_structures/control_flow_graph/reachable/mod.rs
new file mode 100644 (file)
index 0000000..e520e23
--- /dev/null
@@ -0,0 +1,65 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! Compute reachability using a simple dataflow propagation.
+//! Store end-result in a big NxN bit matrix.
+
+use super::ControlFlowGraph;
+use super::super::bitvec::BitVector;
+use super::iterate::reverse_post_order;
+use super::super::indexed_vec::{IndexVec, Idx};
+
+#[cfg(test)]
+mod test;
+
+pub fn reachable<G: ControlFlowGraph>(graph: &G)
+                                      -> Reachability<G::Node> {
+    let reverse_post_order = reverse_post_order(graph, graph.start_node());
+    reachable_given_rpo(graph, &reverse_post_order)
+}
+
+pub fn reachable_given_rpo<G: ControlFlowGraph>(graph: &G,
+                                                reverse_post_order: &[G::Node])
+                                                -> Reachability<G::Node> {
+    let mut reachability = Reachability::new(graph);
+    let mut changed = true;
+    while changed {
+        changed = false;
+        for &node in reverse_post_order.iter().rev() {
+            // every node can reach itself
+            changed |= reachability.bits[node].insert(node.index());
+
+            // and every pred can reach everything node can reach
+            for pred in graph.predecessors(node) {
+                let nodes_bits = reachability.bits[node].clone();
+                changed |= reachability.bits[pred].insert_all(&nodes_bits);
+            }
+        }
+    }
+    reachability
+}
+
+pub struct Reachability<Node: Idx> {
+    bits: IndexVec<Node, BitVector>,
+}
+
+impl<Node: Idx> Reachability<Node> {
+    fn new<G: ControlFlowGraph>(graph: &G) -> Self {
+        let num_nodes = graph.num_nodes();
+        Reachability {
+            bits: IndexVec::from_elem_n(BitVector::new(num_nodes), num_nodes),
+        }
+    }
+
+    pub fn can_reach(&self, source: Node, target: Node)-> bool {
+        let bit: usize = target.index();
+        self.bits[source].contains(bit)
+    }
+}
diff --git a/src/librustc_data_structures/control_flow_graph/reachable/test.rs b/src/librustc_data_structures/control_flow_graph/reachable/test.rs
new file mode 100644 (file)
index 0000000..6aa906a
--- /dev/null
@@ -0,0 +1,64 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use super::super::test::TestGraph;
+
+use super::*;
+
+#[test]
+fn test1() {
+    // 0 -> 1 -> 2 -> 3
+    //      ^    v
+    //      6 <- 4 -> 5
+    let graph = TestGraph::new(0, &[
+        (0, 1),
+        (1, 2),
+        (2, 3),
+        (2, 4),
+        (4, 5),
+        (4, 6),
+        (6, 1),
+    ]);
+    let reachable = reachable(&graph);
+    assert!((0..6).all(|i| reachable.can_reach(0, i)));
+    assert!((1..6).all(|i| reachable.can_reach(1, i)));
+    assert!((1..6).all(|i| reachable.can_reach(2, i)));
+    assert!((1..6).all(|i| reachable.can_reach(4, i)));
+    assert!((1..6).all(|i| reachable.can_reach(6, i)));
+    assert!(reachable.can_reach(3, 3));
+    assert!(!reachable.can_reach(3, 5));
+    assert!(!reachable.can_reach(5, 3));
+}
+
+/// use bigger indices to cross between words in the bit set
+#[test]
+fn test2() {
+    // 30 -> 31 -> 32 -> 33
+    //       ^      v
+    //       36 <- 34 -> 35
+    let graph = TestGraph::new(30, &[
+        (30, 31),
+        (31, 32),
+        (32, 33),
+        (32, 34),
+        (34, 35),
+        (34, 36),
+        (36, 31),
+    ]);
+    let reachable = reachable(&graph);
+    assert!((30..36).all(|i| reachable.can_reach(30, i)));
+    assert!((31..36).all(|i| reachable.can_reach(31, i)));
+    assert!((31..36).all(|i| reachable.can_reach(32, i)));
+    assert!((31..36).all(|i| reachable.can_reach(34, i)));
+    assert!((31..36).all(|i| reachable.can_reach(36, i)));
+    assert!(reachable.can_reach(33, 33));
+    assert!(!reachable.can_reach(33, 35));
+    assert!(!reachable.can_reach(35, 33));
+}
diff --git a/src/librustc_data_structures/control_flow_graph/reference.rs b/src/librustc_data_structures/control_flow_graph/reference.rs
new file mode 100644 (file)
index 0000000..d735be1
--- /dev/null
@@ -0,0 +1,43 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use super::*;
+
+impl<'graph, G: ControlFlowGraph> ControlFlowGraph for &'graph G {
+    type Node = G::Node;
+
+    fn num_nodes(&self) -> usize {
+        (**self).num_nodes()
+    }
+
+    fn start_node(&self) -> Self::Node {
+        (**self).start_node()
+    }
+
+    fn predecessors<'iter>(&'iter self, node: Self::Node)
+                            -> <Self as GraphPredecessors<'iter>>::Iter {
+        (**self).predecessors(node)
+    }
+
+    fn successors<'iter>(&'iter self, node: Self::Node)
+                          -> <Self as GraphSuccessors<'iter>>::Iter {
+        (**self).successors(node)
+    }
+}
+
+impl<'iter, 'graph, G: ControlFlowGraph> GraphPredecessors<'iter> for &'graph G {
+    type Item = G::Node;
+    type Iter = <G as GraphPredecessors<'iter>>::Iter;
+}
+
+impl<'iter, 'graph, G: ControlFlowGraph> GraphSuccessors<'iter> for &'graph G {
+    type Item = G::Node;
+    type Iter = <G as GraphSuccessors<'iter>>::Iter;
+}
diff --git a/src/librustc_data_structures/control_flow_graph/test.rs b/src/librustc_data_structures/control_flow_graph/test.rs
new file mode 100644 (file)
index 0000000..57b2a85
--- /dev/null
@@ -0,0 +1,78 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use std::collections::HashMap;
+use std::cmp::max;
+use std::slice;
+use std::iter;
+
+use super::{ControlFlowGraph, GraphPredecessors, GraphSuccessors};
+
+pub struct TestGraph {
+    num_nodes: usize,
+    start_node: usize,
+    successors: HashMap<usize, Vec<usize>>,
+    predecessors: HashMap<usize, Vec<usize>>,
+}
+
+impl TestGraph {
+    pub fn new(start_node: usize, edges: &[(usize, usize)]) -> Self {
+        let mut graph = TestGraph {
+            num_nodes: start_node + 1,
+            start_node: start_node,
+            successors: HashMap::new(),
+            predecessors: HashMap::new()
+        };
+        for &(source, target) in edges {
+            graph.num_nodes = max(graph.num_nodes, source + 1);
+            graph.num_nodes = max(graph.num_nodes, target + 1);
+            graph.successors.entry(source).or_insert(vec![]).push(target);
+            graph.predecessors.entry(target).or_insert(vec![]).push(source);
+        }
+        for node in 0..graph.num_nodes {
+            graph.successors.entry(node).or_insert(vec![]);
+            graph.predecessors.entry(node).or_insert(vec![]);
+        }
+        graph
+    }
+}
+
+impl ControlFlowGraph for TestGraph {
+    type Node = usize;
+
+    fn start_node(&self) -> usize {
+        self.start_node
+    }
+
+    fn num_nodes(&self) -> usize {
+        self.num_nodes
+    }
+
+    fn predecessors<'graph>(&'graph self, node: usize)
+                            -> <Self as GraphPredecessors<'graph>>::Iter {
+       self.predecessors[&node].iter().cloned()
+    }
+
+    fn successors<'graph>(&'graph self, node: usize)
+                            -> <Self as GraphSuccessors<'graph>>::Iter {
+        self.successors[&node].iter().cloned()
+    }
+}
+
+impl<'graph> GraphPredecessors<'graph> for TestGraph {
+    type Item = usize;
+    type Iter = iter::Cloned<slice::Iter<'graph, usize>>;
+}
+
+impl<'graph> GraphSuccessors<'graph> for TestGraph {
+    type Item = usize;
+    type Iter = iter::Cloned<slice::Iter<'graph, usize>>;
+}
+
diff --git a/src/librustc_data_structures/control_flow_graph/transpose.rs b/src/librustc_data_structures/control_flow_graph/transpose.rs
new file mode 100644 (file)
index 0000000..792e079
--- /dev/null
@@ -0,0 +1,59 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use super::*;
+
+pub struct TransposedGraph<G: ControlFlowGraph> {
+    base_graph: G,
+    start_node: G::Node,
+}
+
+impl<G: ControlFlowGraph> TransposedGraph<G> {
+    pub fn new(base_graph: G) -> Self {
+        let start_node = base_graph.start_node();
+        Self::with_start(base_graph, start_node)
+    }
+
+    pub fn with_start(base_graph: G, start_node: G::Node) -> Self {
+        TransposedGraph { base_graph: base_graph, start_node: start_node }
+    }
+}
+
+impl<G: ControlFlowGraph> ControlFlowGraph for TransposedGraph<G> {
+    type Node = G::Node;
+
+    fn num_nodes(&self) -> usize {
+        self.base_graph.num_nodes()
+    }
+
+    fn start_node(&self) -> Self::Node {
+        self.start_node
+    }
+
+    fn predecessors<'graph>(&'graph self, node: Self::Node)
+                            -> <Self as GraphPredecessors<'graph>>::Iter {
+        self.base_graph.successors(node)
+    }
+
+    fn successors<'graph>(&'graph self, node: Self::Node)
+                          -> <Self as GraphSuccessors<'graph>>::Iter {
+        self.base_graph.predecessors(node)
+    }
+}
+
+impl<'graph, G: ControlFlowGraph> GraphPredecessors<'graph> for TransposedGraph<G> {
+    type Item = G::Node;
+    type Iter = <G as GraphSuccessors<'graph>>::Iter;
+}
+
+impl<'graph, G: ControlFlowGraph> GraphSuccessors<'graph> for TransposedGraph<G> {
+    type Item = G::Node;
+    type Iter = <G as GraphPredecessors<'graph>>::Iter;
+}
index db054477f75a10517956dc457e03692c8cf43330..b3918f1e4bc8b271900f9ceb329374348885e93e 100644 (file)
@@ -8,6 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+use std::fmt::Debug;
 use std::iter::{self, FromIterator};
 use std::slice;
 use std::marker::PhantomData;
@@ -20,7 +21,7 @@
 /// Represents some newtyped `usize` wrapper.
 ///
 /// (purpose: avoid mixing indexes for different bitvector domains.)
-pub trait Idx: Copy + 'static {
+pub trait Idx: Copy + 'static + Eq + Debug {
     fn new(usize) -> Self;
     fn index(self) -> usize;
 }
@@ -76,6 +77,13 @@ pub fn from_elem<S>(elem: T, universe: &IndexVec<I, S>) -> Self
         IndexVec { raw: vec![elem; universe.len()], _marker: PhantomData }
     }
 
+    #[inline]
+    pub fn from_elem_n(elem: T, n: usize) -> Self
+        where T: Clone
+    {
+        IndexVec { raw: vec![elem; n], _marker: PhantomData }
+    }
+
     #[inline]
     pub fn push(&mut self, d: T) -> I {
         let idx = I::new(self.len());
index 9370ad016ef1ed604af5d4da75ed2452675df02c..34c3961d5b4c18980c4b2ea36f6729d06dd22f20 100644 (file)
@@ -50,6 +50,7 @@
 pub mod fnv;
 pub mod tuple_slice;
 pub mod veccell;
+pub mod control_flow_graph;
 
 // See comments in src/librustc/lib.rs
 #[doc(hidden)]