Skip to content

[WIP] MIR Move up propagation #34585

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 16 commits into from
Closed
Binary file added rustc_data_structures
Binary file not shown.
80 changes: 79 additions & 1 deletion src/librustc/mir/repr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -151,7 +152,10 @@ impl<'tcx> Mir<'tcx> {

#[inline]
pub fn dominators(&self) -> Dominators<BasicBlock> {
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)
Expand Down Expand Up @@ -1229,3 +1233,77 @@ impl<'a, 'b> GraphSuccessors<'b> for Mir<'a> {
type Item = BasicBlock;
type Iter = IntoIter<BasicBlock>;
}

pub struct MirWithExit<'m> {
mir: &'m Mir<'m>,
pub exit_node: BasicBlock,
exit_node_predecessors: Vec<BasicBlock>,
}

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<BasicBlock> {
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)
-> <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.successors_for(node).into_owned().into_iter()
}
}

impl<'a, 'b> GraphPredecessors<'b> for MirWithExit<'a> {
type Item = BasicBlock;
type Iter = IntoIter<BasicBlock>;
}

impl<'a, 'b> GraphSuccessors<'b> for MirWithExit<'a> {
type Item = BasicBlock;
type Iter = IntoIter<BasicBlock>;
}
28 changes: 14 additions & 14 deletions src/librustc_data_structures/control_flow_graph/dominators/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,12 @@ use std::fmt;
#[cfg(test)]
mod test;

pub fn dominators<G: ControlFlowGraph>(graph: &G) -> Dominators<G::Node> {
pub fn dominators<G: ControlFlowGraph>(graph: &G) -> Result<Dominators<G::Node>, 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<G: ControlFlowGraph>(graph: &G,
Expand Down Expand Up @@ -105,6 +107,9 @@ fn intersect<Node: Idx>(post_order_rank: &IndexVec<Node, usize>,
return node1;
}

#[derive(Debug)]
pub struct UnreachableNode;

#[derive(Clone, Debug)]
pub struct Dominators<N: Idx> {
post_order_rank: IndexVec<N, usize>,
Expand All @@ -116,13 +121,11 @@ impl<Node: Idx> Dominators<Node> {
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<Node> {
self.immediate_dominators[node]
}

pub fn dominators(&self, node: Node) -> Iter<Node> {
assert!(self.is_reachable(node), "node {:?} is not reachable", node);
Iter {
dominators: self,
node: Some(node),
Expand All @@ -135,18 +138,15 @@ impl<Node: Idx> Dominators<Node> {
}

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,
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<I>(&self, iter: I) -> Option<Node>
where I: IntoIterator<Item = Node>
{
Expand Down Expand Up @@ -196,7 +196,7 @@ impl<'dom, Node: Idx> Iterator for Iter<'dom, Node> {

fn next(&mut self) -> Option<Self::Item> {
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 {
Expand Down
72 changes: 68 additions & 4 deletions src/librustc_data_structures/control_flow_graph/dominators/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand All @@ -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),
Expand All @@ -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));
Expand All @@ -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
}
2 changes: 2 additions & 0 deletions src/librustc_data_structures/control_flow_graph/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
40 changes: 20 additions & 20 deletions src/librustc_data_structures/control_flow_graph/reachable/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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));
}
11 changes: 6 additions & 5 deletions src/librustc_data_structures/control_flow_graph/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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);
}
Expand Down
1 change: 1 addition & 0 deletions src/librustc_driver/driver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -955,6 +955,7 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session,
passes.push_pass(
box mir::transform::simplify_branches::SimplifyBranches::new("initial"));
passes.push_pass(box mir::transform::simplify_cfg::SimplifyCfg::new("qualify-consts"));
passes.push_pass(box mir::transform::move_up_propagation::MoveUpPropagation);
// And run everything.
passes.run_passes(tcx, &mut mir_map);
});
Expand Down
1 change: 1 addition & 0 deletions src/librustc_mir/transform/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@ pub mod add_call_guards;
pub mod promote_consts;
pub mod qualify_consts;
pub mod dump_mir;
pub mod move_up_propagation;
Loading