diff --git a/rustc_data_structures b/rustc_data_structures new file mode 100755 index 0000000000000..aeceef7b22ea8 Binary files /dev/null and b/rustc_data_structures differ diff --git a/src/librustc/mir/repr.rs b/src/librustc/mir/repr.rs index d39ff28841851..8ea4f29666de0 100644 --- a/src/librustc/mir/repr.rs +++ b/src/librustc/mir/repr.rs @@ -15,6 +15,7 @@ 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 rustc_data_structures::control_flow_graph::transpose::TransposedGraph; use hir::def_id::DefId; use ty::subst::Substs; use ty::{self, AdtDef, ClosureSubsts, FnOutput, Region, Ty}; @@ -151,7 +152,10 @@ impl<'tcx> Mir<'tcx> { #[inline] pub fn dominators(&self) -> Dominators { - dominators(self) + // For the normal Mir CFG the dominators + // will succeed because all nodes should be reachable + // from the start node + dominators(self).unwrap() } /// Maps locals (Arg's, Var's, Temp's and ReturnPointer, in that order) @@ -1229,3 +1233,77 @@ impl<'a, 'b> GraphSuccessors<'b> for Mir<'a> { type Item = BasicBlock; type Iter = IntoIter; } + +pub struct MirWithExit<'m> { + mir: &'m Mir<'m>, + exit_node: BasicBlock, + exit_node_predecessors: Vec, +} + +impl<'m> MirWithExit<'m> { + pub fn new(mir: &'m Mir<'m>) -> Self { + let exit_node = BasicBlock(mir.basic_blocks().len() as u32); + let mut exit_node_preds = Vec::new(); + for (idx, ref data) in mir.basic_blocks().iter().enumerate() { + if data.terminator().successors().len() == 0 { + exit_node_preds.push(BasicBlock::new(idx)); + } + }; + MirWithExit {mir: mir, + exit_node: exit_node, + exit_node_predecessors: exit_node_preds, + } + } + pub fn transpose_graph(&self) -> TransposedGraph<&Self> { + TransposedGraph::with_start(self, self.exit_node) + } + fn predecessors_for(&self, node: BasicBlock) -> IntoIter { + if node == self.exit_node { + self.exit_node_predecessors.clone().into_iter() + } else { + self.mir.predecessors_for(node).clone().into_iter() + } + } + fn successors_for(&self, node: BasicBlock) -> Cow<[BasicBlock]> { + if node == self.exit_node { + vec![].into_cow() + } else { + let succs = self.mir.basic_blocks()[node].terminator().successors(); + if succs.len() == 0 { + vec![self.exit_node].into_cow() + } else { + succs + } + } + } +} + +impl<'tcx> ControlFlowGraph for MirWithExit<'tcx> { + + type Node = BasicBlock; + + fn num_nodes(&self) -> usize { self.mir.basic_blocks().len() + 1 } + + fn start_node(&self) -> Self::Node { START_BLOCK } + + fn predecessors<'graph>(&'graph self, node: Self::Node) + -> >::Iter + { + self.predecessors_for(node).clone().into_iter() + } + fn successors<'graph>(&'graph self, node: Self::Node) + -> >::Iter + { + self.successors_for(node).into_owned().into_iter() + } +} + +impl<'a, 'b> GraphPredecessors<'b> for MirWithExit<'a> { + type Item = BasicBlock; + type Iter = IntoIter; +} + +impl<'a, 'b> GraphSuccessors<'b> for MirWithExit<'a> { + type Item = BasicBlock; + type Iter = IntoIter; +} \ No newline at end of file diff --git a/src/librustc_data_structures/control_flow_graph/dominators/mod.rs b/src/librustc_data_structures/control_flow_graph/dominators/mod.rs index 250b89d12ed05..f0afb0007ee19 100644 --- a/src/librustc_data_structures/control_flow_graph/dominators/mod.rs +++ b/src/librustc_data_structures/control_flow_graph/dominators/mod.rs @@ -23,10 +23,12 @@ use std::fmt; #[cfg(test)] mod test; -pub fn dominators(graph: &G) -> Dominators { +pub fn dominators(graph: &G) -> Result, UnreachableNode> { let start_node = graph.start_node(); let rpo = reverse_post_order(graph, start_node); - dominators_given_rpo(graph, &rpo) + let dominators = dominators_given_rpo(graph, &rpo); + if rpo.len() < graph.num_nodes() { return Err(UnreachableNode); } + Ok(dominators) } pub fn dominators_given_rpo(graph: &G, @@ -105,6 +107,9 @@ fn intersect(post_order_rank: &IndexVec, return node1; } +#[derive(Debug)] +pub struct UnreachableNode; + #[derive(Clone, Debug)] pub struct Dominators { post_order_rank: IndexVec, @@ -116,13 +121,11 @@ impl Dominators { 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 immediate_dominator(&self, node: Node) -> Option { + self.immediate_dominators[node] } pub fn dominators(&self, node: Node) -> Iter { - assert!(self.is_reachable(node), "node {:?} is not reachable", node); Iter { dominators: self, node: Some(node), @@ -135,18 +138,15 @@ impl Dominators { } 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::(&self.post_order_rank, + intersect(&self.post_order_rank, &self.immediate_dominators, node1, node2) } + // `mutal_dominator` only returns None when iter has only one element + // otherwise any combination of nodes have some mutual dominator, + // the start node in the degenerate case pub fn mutual_dominator(&self, iter: I) -> Option where I: IntoIterator { @@ -196,7 +196,7 @@ impl<'dom, Node: Idx> Iterator for Iter<'dom, Node> { fn next(&mut self) -> Option { if let Some(node) = self.node { - let dom = self.dominators.immediate_dominator(node); + let dom = self.dominators.immediate_dominator(node).unwrap(); if dom == node { self.node = None; // reached the root } else { diff --git a/src/librustc_data_structures/control_flow_graph/dominators/test.rs b/src/librustc_data_structures/control_flow_graph/dominators/test.rs index a6db5f2fe3ea1..372058a4200c7 100644 --- a/src/librustc_data_structures/control_flow_graph/dominators/test.rs +++ b/src/librustc_data_structures/control_flow_graph/dominators/test.rs @@ -21,7 +21,7 @@ fn diamond() { (2, 3), ]); - let dominators = dominators(&graph); + let dominators = dominators(&graph).unwrap(); let immediate_dominators = dominators.all_immediate_dominators(); assert_eq!(immediate_dominators[0], Some(0)); assert_eq!(immediate_dominators[1], Some(0)); @@ -31,8 +31,9 @@ fn diamond() { #[test] fn paper() { - // example from the paper: + // example from the paper (with 0 exit node added): let graph = TestGraph::new(6, &[ + (3, 0), // this is the added edge (6, 5), (6, 4), (5, 1), @@ -44,9 +45,8 @@ fn paper() { (2, 1), ]); - let dominators = dominators(&graph); + let dominators = dominators(&graph).unwrap(); 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)); @@ -55,3 +55,67 @@ fn paper() { assert_eq!(immediate_dominators[6], Some(6)); } +#[test] +#[should_panic(expected = "called `Result::unwrap()` on an `Err` value: UnreachableNode")] +fn no_start() { + // Test error handling for graphs without a start node + // 0 -> 1 + // v + // 2 -> 3 + // Dominators for this graph are undefined because there is + // no start node which every path begins with + let graph = TestGraph::new(0, &[ + (0, 1), + (1, 3), + (2, 3), + ]); + // this should panic: + let dominators = dominators(&graph).unwrap(); + assert_eq!(dominators.is_dominated_by(1, 0), false); +} + +#[test] +fn infinite_loop() { + // Test handling of infinite loops + // 0 -> 1 -> 4 + // v + // 2 -> 3 + // ^ - v + let graph = TestGraph::new(0, &[ + (0, 1), + (0, 2), + (1, 4), + (2, 3), + (3, 2), + ]); + let dominators = dominators(&graph).unwrap(); + assert!(dominators.is_dominated_by(1, 0)); + assert!(dominators.is_dominated_by(4, 0)); + assert!(dominators.is_dominated_by(2, 0)); + assert!(dominators.is_dominated_by(3, 0)); + assert!(dominators.is_dominated_by(3, 2)); + assert!(!dominators.is_dominated_by(2, 3)); +} + +#[test] +#[should_panic(expected = "called `Result::unwrap()` on an `Err` value: UnreachableNode")] +fn transpose_infinite_loop() { + // If we transpose the graph from `infinite_loop` + // we get a graph with an unreachable loop + // in this case there are unreachable nodes and dominators + // should return a error. + // This is simulating transposing the Mir CFG + // 0 <- 1 <- 4 + // ^ + // 2 <- 3 + // v - ^ + let graph = TestGraph::new(4, &[ + (1, 0), + (2, 0), + (4, 1), + (3, 2), + (2, 3), + ]); + let dominators = dominators(&graph).unwrap(); // should panic + assert!(dominators.is_dominated_by(1, 4)); // should never get here +} \ No newline at end of file diff --git a/src/librustc_data_structures/control_flow_graph/mod.rs b/src/librustc_data_structures/control_flow_graph/mod.rs index f9e75b12e0358..825090ac19a64 100644 --- a/src/librustc_data_structures/control_flow_graph/mod.rs +++ b/src/librustc_data_structures/control_flow_graph/mod.rs @@ -26,6 +26,8 @@ pub trait ControlFlowGraph { type Node: Idx; + // Since we now use IndexVec start_node must be zero + // The nodes should be indexed 0..num_nodes fn num_nodes(&self) -> usize; fn start_node(&self) -> Self::Node; fn predecessors<'graph>(&'graph self, node: Self::Node) diff --git a/src/librustc_data_structures/control_flow_graph/reachable/test.rs b/src/librustc_data_structures/control_flow_graph/reachable/test.rs index 6aa906a0804e2..fbbd37cfac610 100644 --- a/src/librustc_data_structures/control_flow_graph/reachable/test.rs +++ b/src/librustc_data_structures/control_flow_graph/reachable/test.rs @@ -37,28 +37,28 @@ fn test1() { 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), + // 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!((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)); + 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)); } diff --git a/src/librustc_data_structures/control_flow_graph/test.rs b/src/librustc_data_structures/control_flow_graph/test.rs index 57b2a858de568..1ab249f41b485 100644 --- a/src/librustc_data_structures/control_flow_graph/test.rs +++ b/src/librustc_data_structures/control_flow_graph/test.rs @@ -8,13 +8,13 @@ // 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::collections::{HashMap, HashSet}; use std::slice; use std::iter; use super::{ControlFlowGraph, GraphPredecessors, GraphSuccessors}; + pub struct TestGraph { num_nodes: usize, start_node: usize, @@ -24,15 +24,16 @@ pub struct TestGraph { impl TestGraph { pub fn new(start_node: usize, edges: &[(usize, usize)]) -> Self { + let mut seen_nodes = HashSet::new(); let mut graph = TestGraph { - num_nodes: start_node + 1, + num_nodes: 0, 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); + if seen_nodes.insert(target) { graph.num_nodes += 1 }; + if seen_nodes.insert(source) { graph.num_nodes += 1 }; graph.successors.entry(source).or_insert(vec![]).push(target); graph.predecessors.entry(target).or_insert(vec![]).push(source); }