Skip to content

Commit

Permalink
add graph algorithms. add dominator to mir
Browse files Browse the repository at this point in the history
  • Loading branch information
scottcarr committed Jun 17, 2016
1 parent ee00760 commit d704a3c
Show file tree
Hide file tree
Showing 14 changed files with 884 additions and 3 deletions.
2 changes: 1 addition & 1 deletion src/librustc/mir/cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use mir::repr::{Mir, BasicBlock};

use rustc_serialize as serialize;

#[derive(Clone)]
#[derive(Clone, Debug)]
pub struct Cache {
predecessors: RefCell<Option<IndexVec<BasicBlock, Vec<BasicBlock>>>>
}
Expand Down
54 changes: 53 additions & 1 deletion src/librustc/mir/repr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ use graphviz::IntoCow;
use middle::const_val::ConstVal;
use rustc_const_math::{ConstUsize, ConstInt, ConstMathErr};
use rustc_data_structures::indexed_vec::{IndexVec, Idx};
use rustc_data_structures::graph_algorithms::dominators::{Dominators, dominators};
use rustc_data_structures::graph_algorithms::{Graph, GraphPredecessors, GraphSuccessors};
use hir::def_id::DefId;
use ty::subst::Substs;
use ty::{self, AdtDef, ClosureSubsts, FnOutput, Region, Ty};
Expand All @@ -24,6 +26,7 @@ use std::cell::Ref;
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;

Expand Down Expand Up @@ -54,7 +57,7 @@ macro_rules! newtype_index {
}

/// 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.
Expand Down Expand Up @@ -144,6 +147,13 @@ impl<'tcx> Mir<'tcx> {
pub fn predecessors_for(&self, bb: BasicBlock) -> Ref<Vec<BasicBlock>> {
Ref::map(self.predecessors(), |p| &p[bb])
}

#[inline]
//pub fn dominators(&'tcx self) -> Ref<Dominators<Self>> {
pub fn dominators(&self) -> Dominators<Self> {
//self.cache.dominators(self)
dominators(self)
}
}

impl<'tcx> Index<BasicBlock> for Mir<'tcx> {
Expand All @@ -162,6 +172,18 @@ impl<'tcx> IndexMut<BasicBlock> for Mir<'tcx> {
}
}

impl From<usize> for BasicBlock {
fn from(n: usize) -> BasicBlock {
assert!(n < (u32::MAX as usize));
BasicBlock(n as u32)
}
}
impl Into<usize> for BasicBlock {
fn into(self: BasicBlock) -> usize {
self.index()
}
}

/// Grouped information about the source code origin of a MIR entity.
/// Intended to be inspected by diagnostics and debuginfo.
/// Most passes can work with it as a whole, within a single function.
Expand Down Expand Up @@ -1155,3 +1177,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> Graph 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>;
}
282 changes: 282 additions & 0 deletions src/librustc_data_structures/graph_algorithms/dominators/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,282 @@
// 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::Graph;
use super::iterate::reverse_post_order;
use super::super::indexed_vec::{IndexVec, Idx};

use std::fmt;

#[cfg(test)]
mod test;

pub fn dominators<G: Graph>(graph: &G) -> Dominators<G> {
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: Graph>(graph: &G, rpo: &[G::Node]) -> Dominators<G> {
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::<G>(&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<G: Graph>(post_order_rank: &IndexVec<G::Node, usize>,
immediate_dominators: &IndexVec<G::Node, Option<G::Node>>,
node1: Option<G::Node>,
node2: Option<G::Node>)
-> Option<G::Node> {
match (node1, node2) {
(None, None) => None,
(Some(n), None) | (None, Some(n)) => Some(n),
(Some(n1), Some(n2)) => Some(intersect::<G>(post_order_rank, immediate_dominators, n1, n2)),
}
}

fn intersect<G: Graph>(post_order_rank: &IndexVec<G::Node, usize>,
immediate_dominators: &IndexVec<G::Node, Option<G::Node>>,
mut node1: G::Node,
mut node2: G::Node)
-> G::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<G: Graph> {
post_order_rank: IndexVec<G::Node, usize>,
immediate_dominators: IndexVec<G::Node, Option<G::Node>>,
}

impl<G: Graph> Dominators<G> {
pub fn is_reachable(&self, node: G::Node) -> bool {
self.immediate_dominators[node].is_some()
}

pub fn immediate_dominator(&self, node: G::Node) -> G::Node {
assert!(self.is_reachable(node), "node {:?} is not reachable", node);
self.immediate_dominators[node].unwrap()
}

pub fn dominators(&self, node: G::Node) -> Iter<G> {
assert!(self.is_reachable(node), "node {:?} is not reachable", node);
Iter {
dominators: self,
node: Some(node),
}
}

pub fn is_dominated_by(&self, node: G::Node, dom: G::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: G::Node, node2: G::Node) -> G::Node {
assert!(self.is_reachable(node1),
"node {:?} is not reachable",
node1);
assert!(self.is_reachable(node2),
"node {:?} is not reachable",
node2);
intersect::<G>(&self.post_order_rank,
&self.immediate_dominators,
node1,
node2)
}

pub fn mutual_dominator<I>(&self, iter: I) -> Option<G::Node>
where I: IntoIterator<Item = G::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<G::Node, Option<G::Node>> {
&self.immediate_dominators
}

pub fn dominator_tree(&self) -> DominatorTree<G> {
let elem: Vec<G::Node> = Vec::new();
let mut children: IndexVec<G::Node, Vec<G::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 = G::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, G: Graph + 'dom> {
dominators: &'dom Dominators<G>,
node: Option<G::Node>,
}

impl<'dom, G: Graph> Iterator for Iter<'dom, G> {
type Item = G::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<G: Graph> {
root: G::Node,
children: IndexVec<G::Node, Vec<G::Node>>,
}

impl<G: Graph> DominatorTree<G> {
pub fn root(&self) -> G::Node {
self.root
}

pub fn children(&self, node: G::Node) -> &[G::Node] {
&self.children[node]
}

pub fn iter_children_of(&self, node: G::Node) -> IterChildrenOf<G> {
IterChildrenOf {
tree: self,
stack: vec![node],
}
}
}

pub struct IterChildrenOf<'iter, G: Graph + 'iter> {
tree: &'iter DominatorTree<G>,
stack: Vec<G::Node>,
}

impl<'iter, G: Graph> Iterator for IterChildrenOf<'iter, G> {
type Item = G::Node;

fn next(&mut self) -> Option<G::Node> {
if let Some(node) = self.stack.pop() {
self.stack.extend(self.tree.children(node));
Some(node)
} else {
None
}
}
}

impl<G: Graph> fmt::Debug for DominatorTree<G> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
fmt::Debug::fmt(&DominatorTreeNode {
tree: self,
node: self.root,
},
fmt)
}
}

struct DominatorTreeNode<'tree, G: Graph + 'tree> {
tree: &'tree DominatorTree<G>,
node: G::Node,
}

impl<'tree, G: Graph> fmt::Debug for DominatorTreeNode<'tree, G> {
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()
}
}
Loading

0 comments on commit d704a3c

Please sign in to comment.