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..bc38b84301aef 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>, + pub 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); } diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index e31a1f1624fe1..f9af0533b2e68 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -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); }); diff --git a/src/librustc_mir/transform/mod.rs b/src/librustc_mir/transform/mod.rs index 7b707b4adb69a..84149c84fa93c 100644 --- a/src/librustc_mir/transform/mod.rs +++ b/src/librustc_mir/transform/mod.rs @@ -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; diff --git a/src/librustc_mir/transform/move_up_propagation.rs b/src/librustc_mir/transform/move_up_propagation.rs new file mode 100644 index 0000000000000..9d15c1e289cb6 --- /dev/null +++ b/src/librustc_mir/transform/move_up_propagation.rs @@ -0,0 +1,495 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use rustc::ty::TyCtxt; +use rustc::mir::repr::*; +use rustc::mir::transform::{MirPass, MirSource, Pass}; +// use rustc_data_structures::indexed_vec::{Idx, IndexVec}; +use rustc::mir::visit::{Visitor, LvalueContext}; +use std::collections::HashMap; +//use std::collections::hash_map::Entry; +use rustc_data_structures::tuple_slice::TupleSlice; +//use rustc_data_structures::control_flow_graph::ControlFlowGraph; +use rustc_data_structures::control_flow_graph::dominators::{dominators, Dominators}; +use rustc_data_structures::control_flow_graph::transpose::TransposedGraph; +use rustc_data_structures::bitvec::BitVector; +//use rustc_data_structures::control_flow_graph::reference; + +pub struct MoveUpPropagation; + +impl<'tcx> MirPass<'tcx> for MoveUpPropagation { + fn run_pass<'a>(&mut self, + tcx: TyCtxt<'a, 'tcx, 'tcx>, + src: MirSource, + mir: &mut Mir<'tcx>) { + let node_id = src.item_id(); + let node_path = tcx.item_path_str(tcx.map.local_def_id(node_id)); + debug!("move-up-propagation on {:?}", node_path); + + let post_dominators_res = { + let mir_e = MirWithExit::new(mir); + let exit = mir_e.exit_node.clone(); + let tgraph = TransposedGraph::with_start(mir_e, exit); + dominators(&tgraph) + }; + + let post_dominators = match post_dominators_res { + Ok(pdoms) => pdoms, + Err(_) => return, // we cant do the optimization when finding the post dominators fails + }; + + let tduf = TempDefUseFinder::new(mir); + tduf.print(mir); + let work_list: Vec<_> = tduf.lists.iter().filter(|&(&tmp, ref lists)| { + if lists.uses.len() == 1 && lists.defs.len() == 1 { + let ldef = match lists.defs.first() { + Some(x) => x, + None => panic!("we already checked the len?!?"), + }; + let luse = match lists.uses.first() { + Some(x) => x, + None => panic!("we already checked the len?!?"), + }; + debug!("the combindation of:"); + luse.print(mir); + debug!("and:"); + ldef.print(mir); + debug!("is a move up candidate"); + if !any_funny_business(ldef, luse, mir, &post_dominators, tmp) { + // do something + debug!("we should move:"); + luse.print(mir); + debug!("up to:"); + ldef.print(mir); + return true; + } + return false; + } + return false; + }).collect(); + debug!("we're going to do {:?} replacements", work_list.len()); + let mut old_2_new = HashMap::new(); + let mut dead = HashMap::new(); + for &(_, lists) in work_list.iter() { + let ldef = lists.defs.first().expect("we already checked the list had one element?"); + let luse = lists.uses.first().expect("we already checked the list had one element?"); + if let InnerLocation::StatementIndex(use_idx) = luse.inner_location { + if let InnerLocation::StatementIndex(def_idx) = ldef.inner_location { + let bb_mut = mir.basic_blocks(); + let StatementKind::Assign(ref use_lval, _) = bb_mut[luse.basic_block] + .statements[use_idx].kind; + let StatementKind::Assign(_, ref def_rval) = bb_mut[ldef.basic_block] + .statements[def_idx].kind; + let new_statement = StatementKind::Assign(use_lval.clone(), def_rval.clone()); + let num_statements = bb_mut[luse.basic_block].statements.len(); + old_2_new.entry(ldef.basic_block) + .or_insert(HashMap::new()) + .insert(def_idx, new_statement); + dead.entry(luse.basic_block) + .or_insert(BitVector::new(num_statements)) + .insert(use_idx); + continue; + } + } + panic!("We should have already checked for this"); + } + + { + let bbs = mir.basic_blocks_mut(); + for (bb, repls) in old_2_new { + let new_stmts: Vec<_> = bbs[bb].statements + .iter() + .enumerate() + .map(|(stmt_idx, orig_stmt)| { + if let Some(repl) = repls.get(&stmt_idx) { + let repl_stmt = Statement { kind: repl.clone(), + source_info: orig_stmt.source_info, + }; + debug!("replacing {:?} with {:?}", orig_stmt, repl_stmt); + repl_stmt + } else { + //debug!("repl idx: {:?} didnt match {:?}", idx, stmt_idx); + orig_stmt.clone() + } + }).collect(); + bbs[bb] = BasicBlockData { + statements: new_stmts, + terminator: bbs[bb].terminator.clone(), + is_cleanup: bbs[bb].is_cleanup, + }; + } + + for (bb, dead_idxs) in dead { + let mut idx_cnt = 0; + bbs[bb].statements.retain(|_| { + let dead = dead_idxs.contains(idx_cnt); + idx_cnt += 1; + !dead + }); + } + } + + } +} + +fn get_next_locs(curr: UseDefLocation, mir: &Mir) -> Vec { + match curr.inner_location { + InnerLocation::Terminator => { + mir.basic_blocks()[curr.basic_block].terminator().successors().iter().map(|&s| { + UseDefLocation { + basic_block: s, + inner_location: InnerLocation::StatementIndex(0), + } + }).collect() + } + InnerLocation::StatementIndex(idx) => { + if idx + 1 < mir.basic_blocks()[curr.basic_block].statements.len() { + vec![UseDefLocation{ + basic_block: curr.basic_block, + inner_location: InnerLocation::StatementIndex(idx + 1), + }] + } else { + let next = UseDefLocation{ basic_block: curr.basic_block, + inner_location: InnerLocation::Terminator, + }; + get_next_locs(next, mir) + } + } + } +} + +fn paths_contain_call(start: UseDefLocation, + target: UseDefLocation, + mir: &Mir, + visited: &mut HashMap) + -> bool { + // walk the paths from ldef -> ~ -> luse + // make sure there are no calls + // there can't be any borrows because we know luse is the only use + // (because we checked before) + if start == target { + false + } else { + // check for out stopping condition, + // if we do not stop, go to the next location + if let TerminatorKind::Call {..} = mir.basic_blocks()[start.basic_block].terminator().kind { + true + } else { + let mut any = false; + for &s in get_next_locs(start, mir).iter() { + if !visited.contains_key(&s) { + visited.insert(s, true); + any |= paths_contain_call(s, target, mir, visited); + } + } + any + } + } +} + +fn any_funny_business(ldef: &UseDefLocation, + luse: &UseDefLocation, + mir: &Mir, + post_dominators: &Dominators, + tmp: Temp) + -> bool { + + // IF + // -- Def: L = foo + // is post dominated by + // -- Use: bar = ... L ... + // AND + // on the path(s) from Def -> ~ -> Use + // there are no calls + // THEN, + // replace Def wit + // -- Repl: bar = ... foo ... + + let mut visited = HashMap::new(); + if !ldef.is_post_dominated_by(luse, post_dominators) { + return true; + }; + + if paths_contain_call(*ldef, *luse, mir, &mut visited) { + true; + } + + // we really only know how to replace statements for now ... + if let InnerLocation::Terminator = ldef.inner_location { + return true; + } + if let InnerLocation::Terminator = luse.inner_location { + return true; + } + + // check if the luse is like: foo = ldef + // if it's more compiled than that, we give up. for now ... + if let InnerLocation::StatementIndex(idx) = luse.inner_location { + match mir.basic_blocks()[luse.basic_block].statements[idx].kind { + StatementKind::Assign(_, ref rval) => { + if let &Rvalue::Use(Operand::Consume(Lvalue::Temp(rtmp))) = rval { + if rtmp == tmp { return false; }; + } + } + } + }; + + return true; +} + +impl Pass for MoveUpPropagation {} + +#[derive(Debug, Eq, PartialEq, Copy, Clone, Hash)] +struct UseDefLocation { + basic_block: BasicBlock, + inner_location: InnerLocation, +} +impl UseDefLocation { + fn print(&self, mir: &Mir) { + let ref bb = mir[self.basic_block]; + match self.inner_location { + InnerLocation::StatementIndex(idx) => { + debug!("{:?}", bb.statements[idx]); + }, + InnerLocation::Terminator => { + debug!("{:?}", bb.terminator); + } + } + } + fn is_post_dominated_by(&self, other: &Self, post_dominators: &Dominators) -> bool { + if self.basic_block == other.basic_block { + match (&self.inner_location, &other.inner_location) { + // Assumptions: Terminator post dominates all statements + // Terminator does not post dominate itself + (&InnerLocation::StatementIndex(_), &InnerLocation::Terminator) => { true } + (&InnerLocation::Terminator, &InnerLocation::Terminator) => { false }, + (&InnerLocation::Terminator, &InnerLocation::StatementIndex(_)) => { false } + (&InnerLocation::StatementIndex(self_idx), + &InnerLocation::StatementIndex(other_idx)) => { + self_idx < other_idx + } + } + } else { // self.basic_block != other.basic_block + post_dominators.is_dominated_by(self.basic_block, other.basic_block) + } + } +} + +#[derive(Debug, Eq, PartialEq, Copy, Clone, Hash)] +enum InnerLocation { + StatementIndex(usize), + Terminator, +} + +struct DefUseLists { + pub defs: Vec, + pub uses: Vec, +} + +impl DefUseLists { + fn new() -> Self { + DefUseLists{ + uses: vec![], + defs: vec![], + } + } +} + +struct TempDefUseFinder { + pub lists: HashMap, + curr_basic_block: BasicBlock, + statement_index: usize, + kind: AccessKind, + is_in_terminator: bool, +} + +enum AccessKind { + Def, + Use, +} + +impl TempDefUseFinder { + fn new(mir: &Mir) -> Self { + let mut tuc = TempDefUseFinder { + lists: HashMap::new(), + curr_basic_block: START_BLOCK, + statement_index: 0, + kind: AccessKind::Def, // will get updated when we see an assign + is_in_terminator: false, + }; + tuc.visit_mir(mir); + tuc + } + fn add_to_map_if_temp<'a>(&mut self, + lvalue: &Lvalue<'a>) { + match lvalue { + &Lvalue::Temp(tmp_id) => { + let loc = if self.is_in_terminator { + InnerLocation::Terminator + } else { + InnerLocation::StatementIndex(self.statement_index) + }; + let ent = UseDefLocation { + basic_block: self.curr_basic_block, + inner_location: loc, + }; + match self.kind { + AccessKind::Def => self.lists.entry(tmp_id) + .or_insert(DefUseLists::new()) + .defs + .push(ent), + AccessKind::Use => self.lists.entry(tmp_id) + .or_insert(DefUseLists::new()) + .uses + .push(ent), + }; + } + _ => {} + } + } + fn print(&self, mir: &Mir) { + for (k, ref v) in self.lists.iter() { + debug!("{:?} uses:", k); + debug!("{:?}", v.uses); + // this assertion was wrong + // you can have an unused temporary, ex: the result of a call is never used + //assert!(v.uses.len() > 0); // every temp should have at least one use + v.uses.iter().map(|e| UseDefLocation::print(&e, mir)).count(); + } + for (k, ref v) in self.lists.iter() { + debug!("{:?} defs:", k); + debug!("{:?}", v.defs); + // this may be too strict? maybe the def was optimized out? + //assert!(v.defs.len() > 0); // every temp should have at least one def + v.defs.iter().map(|e| UseDefLocation::print(&e, mir)).count(); + } + } +} +impl<'a> Visitor<'a> for TempDefUseFinder { + fn visit_basic_block_data(&mut self, block: BasicBlock, data: &BasicBlockData<'a>) { + self.curr_basic_block = block; + self.statement_index = 0; + self.is_in_terminator = false; + self.super_basic_block_data(block, data); + } + fn visit_statement(&mut self, _: BasicBlock, statement: &Statement<'a>) { + match statement.kind { + StatementKind::Assign(ref lvalue, ref rvalue) => { + self.kind = AccessKind::Def; + self.visit_lvalue(lvalue, LvalueContext::Store); + self.kind = AccessKind::Use; + self.visit_rvalue(rvalue); + }, + } + self.statement_index += 1; + } + fn visit_lvalue(&mut self, lvalue: &Lvalue<'a>, context: LvalueContext) { + self.add_to_map_if_temp(lvalue); + self.super_lvalue(lvalue, context); + } + fn visit_terminator(&mut self, block: BasicBlock, terminator: &Terminator<'a>) { + self.is_in_terminator = true; + self.super_terminator(block, terminator); + } + fn visit_terminator_kind(&mut self, block: BasicBlock, kind: &TerminatorKind<'a>) { + match *kind { + TerminatorKind::Goto { target } => { + self.visit_branch(block, target); + } + + TerminatorKind::If { ref cond, ref targets } => { + self.kind = AccessKind::Use; + self.visit_operand(cond); + for &target in targets.as_slice() { + self.visit_branch(block, target); + } + } + + TerminatorKind::Switch { ref discr, + adt_def: _, + ref targets } => { + self.kind = AccessKind::Use; + self.visit_lvalue(discr, LvalueContext::Inspect); + for &target in targets { + self.visit_branch(block, target); + } + } + + TerminatorKind::SwitchInt { ref discr, + ref switch_ty, + ref values, + ref targets } => { + self.kind = AccessKind::Use; + self.visit_lvalue(discr, LvalueContext::Inspect); + self.visit_ty(switch_ty); + for value in values { + self.visit_const_val(value); + } + for &target in targets { + self.visit_branch(block, target); + } + } + + TerminatorKind::Resume | + TerminatorKind::Return | + TerminatorKind::Unreachable => { + } + + TerminatorKind::Drop { ref location, + target, + unwind } => { + self.kind = AccessKind::Use; + self.visit_lvalue(location, LvalueContext::Drop); + self.visit_branch(block, target); + unwind.map(|t| self.visit_branch(block, t)); + } + + TerminatorKind::DropAndReplace { ref location, + ref value, + target, + unwind } => { + self.kind = AccessKind::Use; + self.visit_lvalue(location, LvalueContext::Drop); + self.visit_operand(value); + self.visit_branch(block, target); + unwind.map(|t| self.visit_branch(block, t)); + } + + TerminatorKind::Call { ref func, + ref args, + ref destination, + cleanup } => { + self.visit_operand(func); + for arg in args { + self.visit_operand(arg); + } + if let Some((ref destination, target)) = *destination { + self.kind = AccessKind::Def; // this is the whole reason for this function + self.visit_lvalue(destination, LvalueContext::Call); + self.kind = AccessKind::Use; // this is the whole reason for this function + self.visit_branch(block, target); + } + cleanup.map(|t| self.visit_branch(block, t)); + } + + TerminatorKind::Assert { ref cond, + expected: _, + ref msg, + target, + cleanup } => { + self.kind = AccessKind::Use; + self.visit_operand(cond); + self.visit_assert_message(msg); + self.visit_branch(block, target); + cleanup.map(|t| self.visit_branch(block, t)); + } + } + } +}