diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index ed63783366bf8..97cf5f7850ae2 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -24,6 +24,7 @@ #![cfg_attr(not(stage0), deny(warnings))] #![feature(associated_consts)] +#![feature(inclusive_range_syntax)] #![feature(box_patterns)] #![feature(box_syntax)] #![feature(collections)] @@ -108,6 +109,8 @@ pub mod mir { pub mod visit; pub mod transform; pub mod mir_map; + pub mod cfg; + pub mod traversal; } pub mod session; diff --git a/src/librustc/mir/cfg.rs b/src/librustc/mir/cfg.rs new file mode 100644 index 0000000000000..3b6df96ec4fe6 --- /dev/null +++ b/src/librustc/mir/cfg.rs @@ -0,0 +1,146 @@ +use mir::repr::*; + +use std::ops::{Index, IndexMut}; +use syntax::codemap::Span; + +#[derive(Clone, RustcEncodable, RustcDecodable)] +pub struct CFG<'tcx> { + pub basic_blocks: Vec>, +} + +pub struct PredecessorIter(::std::vec::IntoIter); +impl Iterator for PredecessorIter { + type Item = BasicBlock; + fn next(&mut self) -> Option { + self.0.next() + } +} + +pub struct SuccessorIter(::std::vec::IntoIter); +impl<'a> Iterator for SuccessorIter { + type Item = BasicBlock; + fn next(&mut self) -> Option { + self.0.next() + } +} + +pub struct SuccessorIterMut<'a>(::std::vec::IntoIter<&'a mut BasicBlock>); +impl<'a> Iterator for SuccessorIterMut<'a> { + type Item = &'a mut BasicBlock; + fn next(&mut self) -> Option<&'a mut BasicBlock> { + self.0.next() + } +} + +impl<'tcx> CFG<'tcx> { + pub fn len(&self) -> usize { + self.basic_blocks.len() + } + + pub fn predecessors(&self, b: BasicBlock) -> PredecessorIter { + let mut preds = vec![]; + for idx in 0..self.len() { + let bb = BasicBlock::new(idx); + if let Some(_) = self.successors(bb).find(|&x| x == b) { + preds.push(bb) + } + } + PredecessorIter(preds.into_iter()) + } + + pub fn successors(&self, b: BasicBlock) -> SuccessorIter { + let v: Vec = self[b].terminator().kind.successors().into_owned(); + SuccessorIter(v.into_iter()) + } + + pub fn successors_mut(&mut self, b: BasicBlock) -> SuccessorIterMut { + SuccessorIterMut(self[b].terminator_mut().kind.successors_mut().into_iter()) + } + + + pub fn swap(&mut self, b1: BasicBlock, b2: BasicBlock) { + // TODO: find all the branches to b2 from subgraph starting at b2 and replace them with b1. + self.basic_blocks.swap(b1.index(), b2.index()); + } + + pub fn start_new_block(&mut self) -> BasicBlock { + let node_index = self.basic_blocks.len(); + self.basic_blocks.push(BasicBlockData::new(None)); + BasicBlock::new(node_index) + } + + pub fn start_new_cleanup_block(&mut self) -> BasicBlock { + let bb = self.start_new_block(); + self[bb].is_cleanup = true; + bb + } + + pub fn push(&mut self, block: BasicBlock, statement: Statement<'tcx>) { + debug!("push({:?}, {:?})", block, statement); + self[block].statements.push(statement); + } + + pub fn terminate(&mut self, + block: BasicBlock, + scope: ScopeId, + span: Span, + kind: TerminatorKind<'tcx>) { + debug_assert!(self[block].terminator.is_none(), + "terminate: block {:?} already has a terminator set", block); + self[block].terminator = Some(Terminator { + span: span, + scope: scope, + kind: kind, + }); + } + + pub fn push_assign(&mut self, + block: BasicBlock, + scope: ScopeId, + span: Span, + lvalue: &Lvalue<'tcx>, + rvalue: Rvalue<'tcx>) { + self.push(block, Statement { + scope: scope, + span: span, + kind: StatementKind::Assign(lvalue.clone(), rvalue) + }); + } + + pub fn push_assign_constant(&mut self, + block: BasicBlock, + scope: ScopeId, + span: Span, + temp: &Lvalue<'tcx>, + constant: Constant<'tcx>) { + self.push_assign(block, scope, span, temp, + Rvalue::Use(Operand::Constant(constant))); + } + + pub fn push_assign_unit(&mut self, + block: BasicBlock, + scope: ScopeId, + span: Span, + lvalue: &Lvalue<'tcx>) { + self.push_assign(block, scope, span, lvalue, Rvalue::Aggregate( + AggregateKind::Tuple, vec![] + )); + } +} + +impl<'tcx> Index for CFG<'tcx> { + type Output = BasicBlockData<'tcx>; + + #[inline] + fn index(&self, index: BasicBlock) -> &BasicBlockData<'tcx> { + &self.basic_blocks[index.index()] + } +} + +impl<'tcx> IndexMut for CFG<'tcx> { + #[inline] + fn index_mut(&mut self, index: BasicBlock) -> &mut BasicBlockData<'tcx> { + &mut self.basic_blocks[index.index()] + } +} + diff --git a/src/librustc/mir/repr.rs b/src/librustc/mir/repr.rs index 458cb28144adb..34c142ff6e770 100644 --- a/src/librustc/mir/repr.rs +++ b/src/librustc/mir/repr.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +pub use mir::cfg::*; + use graphviz::IntoCow; use middle::const_val::ConstVal; use rustc_const_math::{ConstUsize, ConstInt}; @@ -30,7 +32,7 @@ use syntax::codemap::Span; pub struct Mir<'tcx> { /// List of basic blocks. References to basic block use a newtyped index type `BasicBlock` /// that indexes into this vector. - pub basic_blocks: Vec>, + pub cfg: CFG<'tcx>, /// List of lexical scopes; these are referenced by statements and /// used (eventually) for debuginfo. Indexed by a `ScopeId`. @@ -70,17 +72,17 @@ pub const START_BLOCK: BasicBlock = BasicBlock(0); impl<'tcx> Mir<'tcx> { pub fn all_basic_blocks(&self) -> Vec { - (0..self.basic_blocks.len()) + (0..self.cfg.len()) .map(|i| BasicBlock::new(i)) .collect() } pub fn basic_block_data(&self, bb: BasicBlock) -> &BasicBlockData<'tcx> { - &self.basic_blocks[bb.index()] + &self.cfg[bb] } pub fn basic_block_data_mut(&mut self, bb: BasicBlock) -> &mut BasicBlockData<'tcx> { - &mut self.basic_blocks[bb.index()] + &mut self.cfg[bb] } } @@ -541,7 +543,7 @@ impl<'tcx> Debug for Statement<'tcx> { /// A path to a value; something that can be evaluated without /// changing or disturbing program state. -#[derive(Clone, PartialEq, RustcEncodable, RustcDecodable)] +#[derive(Clone, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)] pub enum Lvalue<'tcx> { /// local variable declared by the user Var(u32), @@ -726,7 +728,7 @@ pub struct ScopeData { /// These are values that can appear inside an rvalue (or an index /// lvalue). They are intentionally limited to prevent rvalues from /// being nested in one another. -#[derive(Clone, PartialEq, RustcEncodable, RustcDecodable)] +#[derive(Clone, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)] pub enum Operand<'tcx> { Consume(Lvalue<'tcx>), Constant(Constant<'tcx>), diff --git a/src/librustc/mir/transform/dataflow.rs b/src/librustc/mir/transform/dataflow.rs new file mode 100644 index 0000000000000..508f74e356a0f --- /dev/null +++ b/src/librustc/mir/transform/dataflow.rs @@ -0,0 +1,419 @@ +use mir::repr as mir; +use mir::cfg::CFG; +use mir::repr::BasicBlock; +use rustc_data_structures::bitvec::BitVector; + +use mir::transform::lattice::Lattice; + + +pub trait DataflowPass<'tcx> { + type Lattice: Lattice; + type Rewrite: Rewrite<'tcx, Self::Lattice>; + type Transfer: Transfer<'tcx, Self::Lattice>; +} + +pub trait Rewrite<'tcx, L: Lattice> { + /// The rewrite function which given a statement optionally produces an alternative graph to be + /// placed in place of the original statement. + /// + /// The 2nd BasicBlock *MUST NOT* have the terminator set. + /// + /// Correctness precondition: + /// * transfer_stmt(statement, fact) == transfer_stmt(rewrite_stmt(statement, fact)) + /// that is, given some fact `fact` true before both the statement and relacement graph, and + /// a fact `fact2` which is true after the statement, the same `fact2` must be true after the + /// replacement graph too. + fn stmt(&mir::Statement<'tcx>, &L, &mut CFG<'tcx>) -> StatementChange<'tcx>; + + /// The rewrite function which given a terminator optionally produces an alternative graph to + /// be placed in place of the original statement. + /// + /// The 2nd BasicBlock *MUST* have the terminator set. + /// + /// Correctness precondition: + /// * transfer_stmt(terminator, fact) == transfer_stmt(rewrite_term(terminator, fact)) + /// that is, given some fact `fact` true before both the terminator and relacement graph, and + /// a fact `fact2` which is true after the statement, the same `fact2` must be true after the + /// replacement graph too. + fn term(&mir::Terminator<'tcx>, &L, &mut CFG<'tcx>) -> TerminatorChange<'tcx>; +} + +/// This combinator has the following behaviour: +/// +/// * Rewrite the node with the first rewriter. +/// * if the first rewriter replaced the node, 2nd rewriter is used to rewrite the replacement. +/// * otherwise 2nd rewriter is used to rewrite the original node. +pub struct RewriteAndThen<'tcx, R1, R2>(::std::marker::PhantomData<(&'tcx (), R1, R2)>); +impl<'tcx, L, R1, R2> Rewrite<'tcx, L> for RewriteAndThen<'tcx, R1, R2> +where L: Lattice, R1: Rewrite<'tcx, L>, R2: Rewrite<'tcx, L> { + fn stmt(s: &mir::Statement<'tcx>, l: &L, c: &mut CFG<'tcx>) -> StatementChange<'tcx> { + let rs = >::stmt(s, l, c); + match rs { + StatementChange::None => >::stmt(s, l, c), + StatementChange::Remove => StatementChange::Remove, + StatementChange::Statement(ns) => + match >::stmt(&ns, l, c) { + StatementChange::None => StatementChange::Statement(ns), + x => x + }, + StatementChange::Statements(nss) => { + // We expect the common case of all statements in this vector being replaced/not + // replaced by other statements 1:1 + let mut new_new_stmts = Vec::with_capacity(nss.len()); + for s in nss { + match >::stmt(&s, l, c) { + StatementChange::None => new_new_stmts.push(s), + StatementChange::Remove => {}, + StatementChange::Statement(ns) => new_new_stmts.push(ns), + StatementChange::Statements(nss) => new_new_stmts.extend(nss) + } + } + StatementChange::Statements(new_new_stmts) + } + } + } + + fn term(t: &mir::Terminator<'tcx>, l: &L, c: &mut CFG<'tcx>) -> TerminatorChange<'tcx> { + let rt = >::term(t, l, c); + match rt { + TerminatorChange::None => >::term(t, l, c), + TerminatorChange::Terminator(nt) => match >::term(&nt, l, c) { + TerminatorChange::None => TerminatorChange::Terminator(nt), + x => x + } + } + } +} + +pub enum TerminatorChange<'tcx> { + /// No change + None, + /// Replace with another terminator + Terminator(mir::Terminator<'tcx>), +} + +pub enum StatementChange<'tcx> { + /// No change + None, + /// Remove the statement + Remove, + /// Replace with another single statement + Statement(mir::Statement<'tcx>), + /// Replace with a list of statements + Statements(Vec>), +} + +impl<'tcx> StatementChange<'tcx> { + fn normalise(&mut self) { + let old = ::std::mem::replace(self, StatementChange::None); + *self = match old { + StatementChange::Statements(mut stmts) => { + match stmts.len() { + 0 => StatementChange::Remove, + 1 => StatementChange::Statement(stmts.pop().unwrap()), + _ => StatementChange::Statements(stmts) + } + } + o => o + } + } +} + +pub trait Transfer<'tcx, L: Lattice> { + type TerminatorOut; + /// The transfer function which given a statement and a fact produces a fact which is true + /// after the statement. + fn stmt(&mir::Statement<'tcx>, L) -> L; + + /// The transfer function which given a terminator and a fact produces a fact for each + /// successor of the terminator. + /// + /// Corectness precondtition: + /// * The list of facts produced should only contain the facts for blocks which are successors + /// of the terminator being transfered. + fn term(&mir::Terminator<'tcx>, L) -> Self::TerminatorOut; +} + + +/// Facts is a mapping from basic block label (index) to the fact which is true about the first +/// statement in the block. +pub struct Facts(Vec); + +impl Facts { + pub fn new() -> Facts { + Facts(vec![]) + } + + fn put(&mut self, index: BasicBlock, value: F) { + let len = self.0.len(); + self.0.extend((len...index.index()).map(|_| ::bottom())); + self.0[index.index()] = value; + } +} + +impl ::std::ops::Index for Facts { + type Output = F; + fn index(&self, index: BasicBlock) -> &F { + &self.0.get(index.index()).expect("facts indexed immutably and the user is buggy!") + } +} + +impl ::std::ops::IndexMut for Facts { + fn index_mut(&mut self, index: BasicBlock) -> &mut F { + if let None = self.0.get_mut(index.index()) { + self.put(index, ::bottom()); + } + self.0.get_mut(index.index()).unwrap() + } +} + +/// Analyse and rewrite using dataflow in the forward direction +pub fn ar_forward<'tcx, T, P>(cfg: &CFG<'tcx>, fs: Facts, mut queue: BitVector) +-> (CFG<'tcx>, Facts) +// FIXME: shouldn’t need that T generic. +where T: Transfer<'tcx, P::Lattice, TerminatorOut=Vec>, + P: DataflowPass<'tcx, Transfer=T> +{ + fixpoint(cfg, Direction::Forward, |bb, fact, cfg| { + let new_graph = cfg.start_new_block(); + let mut fact = fact.clone(); + let mut changed = false; + // Swap out the vector of old statements for a duration of statement inspection. + let old_statements = ::std::mem::replace(&mut cfg[bb].statements, Vec::new()); + for stmt in &old_statements { + // Given a fact and statement produce a new fact and optionally a replacement + // graph. + let mut new_repl = P::Rewrite::stmt(&stmt, &fact, cfg); + new_repl.normalise(); + match new_repl { + StatementChange::None => { + fact = P::Transfer::stmt(stmt, fact); + cfg.push(new_graph, stmt.clone()); + } + StatementChange::Remove => changed = true, + StatementChange::Statement(stmt) => { + changed = true; + fact = P::Transfer::stmt(&stmt, fact); + cfg.push(new_graph, stmt); + } + StatementChange::Statements(stmts) => { + changed = true; + for stmt in &stmts { fact = P::Transfer::stmt(stmt, fact); } + cfg[new_graph].statements.extend(stmts); + } + + } + } + // Swap the statements back in. + ::std::mem::replace(&mut cfg[bb].statements, old_statements); + + // Handle the terminator replacement and transfer. + let terminator = ::std::mem::replace(&mut cfg[bb].terminator, None).unwrap(); + let repl = P::Rewrite::term(&terminator, &fact, cfg); + match repl { + TerminatorChange::None => { + cfg[new_graph].terminator = Some(terminator.clone()); + } + TerminatorChange::Terminator(t) => { + changed = true; + cfg[new_graph].terminator = Some(t); + } + } + let new_facts = P::Transfer::term(cfg[new_graph].terminator(), fact); + ::std::mem::replace(&mut cfg[bb].terminator, Some(terminator)); + + (if changed { Some(new_graph) } else { None }, new_facts) + }, &mut queue, fs) +} + +// /// The implementation of forward dataflow. +// pub struct ForwardDataflow(::std::marker::PhantomData); +// +// impl ForwardDataflow { +// pub fn new() -> ForwardDataflow { +// ForwardDataflow(::std::marker::PhantomData) +// } +// } +// +// impl Pass for ForwardDataflow {} +// +// impl<'tcx, P> MirPass<'tcx> for ForwardDataflow

+// where P: DataflowPass<'tcx> { +// fn run_pass<'a>(&mut self, _: TyCtxt<'a, 'tcx, 'tcx>, _: MirSource, mir: &mut mir::Mir<'tcx>) { +// let facts: Facts<

>::Lattice> = +// Facts::new(

>::input_fact()); +// let (new_cfg, _) = self.arf_body(&mir.cfg, facts, mir::START_BLOCK); +// mir.cfg = new_cfg; +// } +// } +// +// impl<'tcx, P> ForwardDataflow

+// where P: DataflowPass<'tcx> { +// +// +// /// The implementation of backward dataflow. +// pub struct BackwardDataflow(::std::marker::PhantomData); +// +// impl BackwardDataflow { +// pub fn new() -> BackwardDataflow { +// BackwardDataflow(::std::marker::PhantomData) +// } +// } +// +// impl Pass for BackwardDataflow {} +// +// impl<'tcx, P> MirPass<'tcx> for BackwardDataflow

+// where P: DataflowPass<'tcx> { +// fn run_pass<'a>(&mut self, _: TyCtxt<'a, 'tcx, 'tcx>, _: MirSource, mir: &mut mir::Mir<'tcx>) { +// let mut facts: Facts<

>::Lattice> = +// Facts::new(

>::input_fact()); +// // The desired effect here is that we should begin flowing from the blocks which terminate +// // the control flow (return, resume, calls of diverging functions, non-terminating loops +// // etc), but finding them all is a pain, so we just get the list of graph nodes postorder +// // and inspect them all! Perhaps not very effective, but certainly correct. +// let start_at = postorder(mir).filter(|&(_, d)| !d.is_cleanup).map(|(bb, _)| { +// facts.put(bb,

>::Lattice::bottom()); +// (bb, ()) +// }).collect(); +// let (new_cfg, _) = self.arb_body(&mir.cfg, facts, start_at); +// mir.cfg = new_cfg; +// } +// } +// +// impl<'tcx, P> BackwardDataflow

+// where P: DataflowPass<'tcx> { +// fn arb_body(&self, cfg: &CFG<'tcx>, +// facts: Facts<

>::Lattice>, mut map: HashMap) +// -> (CFG<'tcx>, Facts<

>::Lattice>){ +// fixpoint(cfg, Direction::Backward, |bb, fact, cfg| { +// let new_graph = cfg.start_new_block(); +// let mut fact = fact.clone(); +// // This is a reverse thing so we inspect the terminator first and statements in reverse +// // order later. +// // +// // Handle the terminator replacement and transfer. +// let terminator = ::std::mem::replace(&mut cfg[bb].terminator, None).unwrap(); +// let repl = P::rewrite_term(&terminator, &fact, cfg); +// // TODO: this really needs to get factored out +// let mut new_facts = match repl { +// TerminatorReplacement::Terminator(t) => { +// cfg[new_graph].terminator = Some(t); +// P::transfer_term(cfg[new_graph].terminator(), fact) +// } +// TerminatorReplacement::Graph(from, _) => { +// // FIXME: a more optimal approach would be to copy the from to the tail of our +// // new_graph. (1 less extra block). However there’s a problem with inspecting +// // the statements of the merged block, because we just did the statements +// // for this block already. +// cfg.terminate(new_graph, terminator.scope, terminator.span, +// mir::TerminatorKind::Goto { target: from }); +// P::transfer_term(&cfg[new_graph].terminator(), fact) +// } +// }; +// // FIXME: this should just have a different API. +// assert!(new_facts.len() == 1, "transfer_term function is incorrect"); +// fact = new_facts.pop().unwrap().1; +// ::std::mem::replace(&mut cfg[bb].terminator, Some(terminator)); +// +// // Swap out the vector of old statements for a duration of statement inspection. +// let old_statements = ::std::mem::replace(&mut cfg[bb].statements, Vec::new()); +// for stmt in old_statements.iter().rev() { +// // Given a fact and statement produce a new fact and optionally a replacement +// // graph. +// let mut new_repl = P::rewrite_stmt(&stmt, &fact, cfg); +// new_repl.normalise(); +// match new_repl { +// StatementReplacement::None => {} +// StatementReplacement::Statement(nstmt) => { +// fact = P::transfer_stmt(&nstmt, fact); +// cfg.push(new_graph, nstmt) +// } +// StatementReplacement::Statements(stmts) => { +// for stmt in &stmts { +// fact = P::transfer_stmt(stmt, fact); +// } +// cfg[new_graph].statements.extend(stmts) +// } +// StatementReplacement::Graph(..) => unimplemented!(), +// // debug_assert!(cfg[replacement.1].terminator.is_none(), +// // "buggy pass: replacement tail has a terminator set!"); +// }; +// } +// // Reverse the statements, because we were analysing bottom-top but pusshing +// // top-bottom. +// cfg[new_graph].statements.reverse(); +// ::std::mem::replace(&mut cfg[bb].statements, old_statements); +// (Some(new_graph), vec![(mir::START_BLOCK, fact)]) +// }, &mut map, facts) +// } +// } + +enum Direction { + Forward, + Backward +} + +/// The fixpoint function is the engine of this whole thing. Important part of it is the `f: BF` +/// callback. This for each basic block and its facts has to produce a replacement graph and a +/// bunch of facts which are to be joined with the facts in the graph elsewhere. +fn fixpoint<'tcx, F: Lattice, BF>(cfg: &CFG<'tcx>, + direction: Direction, + f: BF, + to_visit: &mut BitVector, + mut init_facts: Facts) -> (CFG<'tcx>, Facts) +// TODO: we probably want to pass in a list of basicblocks as successors (predecessors in backward +// fixpoing) and let BF return just a list of F. +where BF: Fn(BasicBlock, &F, &mut CFG<'tcx>) -> (Option, Vec), + // ^~ This function given a single block and fact before it optionally produces a replacement + // graph (if not, the original block is the “replacement graph”) for the block and a list of + // facts for arbitrary blocks (most likely for the blocks in the replacement graph and blocks + // into which data flows from the replacement graph) + // + // Invariant: + // * None of the already existing blocks in CFG may be modified; +{ + let mut cfg = cfg.clone(); + + while let Some(block) = to_visit.iter().next() { + to_visit.remove(block); + let block = BasicBlock::new(block); + + let (new_target, new_facts) = { + let fact = &mut init_facts[block]; + f(block, fact, &mut cfg) + }; + + // First of all, we merge in the replacement graph, if any. + if let Some(replacement_bb) = new_target { + cfg.swap(replacement_bb, block); + } + + // Then we record the facts in the correct direction. + if let Direction::Forward = direction { + for (f, &target) in new_facts.into_iter() + .zip(cfg[block].terminator().successors().iter()) { + let facts_changed = Lattice::join(&mut init_facts[target], &f); + if facts_changed { + to_visit.insert(target.index()); + } + } + } else { + unimplemented!() + // let mut new_facts = new_facts; + // let fact = new_facts.pop().unwrap().1; + // for pred in cfg.predecessors(block) { + // if init_facts.exists(pred) { + // let old_fact = &mut init_facts[pred]; + // let facts_changed = Lattice::join(old_fact, &fact); + // if facts_changed { + // to_visit.insert(pred, ()); + // } + // } else { + // init_facts.put(pred, fact.clone()); + // to_visit.insert(pred, ()); + // } + // } + } + } + (cfg, init_facts) +} diff --git a/src/librustc/mir/transform/lattice.rs b/src/librustc/mir/transform/lattice.rs new file mode 100644 index 0000000000000..129291b349b7f --- /dev/null +++ b/src/librustc/mir/transform/lattice.rs @@ -0,0 +1,115 @@ +use std::fmt::{Debug, Formatter}; +use std::collections::hash_map::Entry; +use std::collections::HashMap; + +pub trait Lattice: Clone { + fn bottom() -> Self; + fn join(&mut self, other: &Self) -> bool; +} + +/// Extend the type with a Top point. +#[derive(Clone, PartialEq)] +pub enum WTop { + Top, + Value(T) +} + +impl Lattice for WTop { + fn bottom() -> Self { + WTop::Value(::bottom()) + } + + /// V + V = join(v, v) + /// ⊤ + V = ⊤ (no change) + /// V + ⊤ = ⊤ + /// ⊤ + ⊤ = ⊤ (no change) + default fn join(&mut self, other: &Self) -> bool { + match (self, other) { + (&mut WTop::Value(ref mut this), &WTop::Value(ref o)) => ::join(this, o), + (&mut WTop::Top, _) => false, + (this, &WTop::Top) => { + *this = WTop::Top; + true + } + } + } +} + +impl Debug for WTop { + fn fmt(&self, f: &mut Formatter) -> ::std::fmt::Result { + match *self { + WTop::Top => f.write_str("⊤"), + WTop::Value(ref t) => ::fmt(t, f) + } + } +} + +/// Extend the type with a bottom point +/// +/// This guarantees the bottom() of the underlying lattice won’t get called so it may be +/// implemented as a `panic!()` or something. +#[derive(Clone, PartialEq)] +pub enum WBottom { + Bottom, + Value(T) +} + +impl Lattice for WBottom { + fn bottom() -> Self { + WBottom::Bottom + } + + /// V + V = join(v, v) + /// ⊥ + V = V + /// V + ⊥ = V (no change) + /// ⊥ + ⊥ = ⊥ (no change) + fn join(&mut self, other: &Self) -> bool { + match (self, other) { + (&mut WBottom::Value(ref mut this), &WBottom::Value(ref o)) => + ::join(this, o), + (_, &WBottom::Bottom) => false, + (this, o) => { + *this = o.clone(); + true + } + } + } + +} + +impl Debug for WBottom { + fn fmt(&self, f: &mut Formatter) -> ::std::fmt::Result { + match *self { + WBottom::Bottom => f.write_str("⊥"), + WBottom::Value(ref t) => ::fmt(t, f) + } + } +} + +/// Extend the type with both bottom and top points. +type WTopBottom = WTop>; + +impl Lattice for HashMap +where K: Clone + Eq + ::std::hash::Hash, + T: Lattice, + H: Clone + ::std::hash::BuildHasher + ::std::default::Default +{ + fn bottom() -> Self { + HashMap::default() + } + fn join(&mut self, other: &Self) -> bool { + let mut changed = false; + for (key, val) in other.iter() { + match self.entry(key.clone()) { + Entry::Vacant(e) => { + e.insert(val.clone()); + changed = true + } + Entry::Occupied(mut e) => changed |= e.get_mut().join(val) + } + } + changed + } +} + + diff --git a/src/librustc/mir/transform.rs b/src/librustc/mir/transform/mod.rs similarity index 98% rename from src/librustc/mir/transform.rs rename to src/librustc/mir/transform/mod.rs index 79c44b2b851c7..fbbf56358ae93 100644 --- a/src/librustc/mir/transform.rs +++ b/src/librustc/mir/transform/mod.rs @@ -8,6 +8,11 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +pub mod lattice; +pub mod dataflow; + +pub use self::dataflow::*; + use dep_graph::DepNode; use hir; use hir::map::DefPathData; diff --git a/src/librustc_mir/traversal.rs b/src/librustc/mir/traversal.rs similarity index 98% rename from src/librustc_mir/traversal.rs rename to src/librustc/mir/traversal.rs index c58b5c8772461..8e130721db854 100644 --- a/src/librustc_mir/traversal.rs +++ b/src/librustc/mir/traversal.rs @@ -12,7 +12,7 @@ use std::vec; use rustc_data_structures::bitvec::BitVector; -use rustc::mir::repr::*; +use mir::repr::*; /// Preorder traversal of a graph. /// @@ -44,7 +44,7 @@ impl<'a, 'tcx> Preorder<'a, 'tcx> { Preorder { mir: mir, - visited: BitVector::new(mir.basic_blocks.len()), + visited: BitVector::new(mir.cfg.basic_blocks.len()), worklist: worklist } } @@ -106,7 +106,7 @@ impl<'a, 'tcx> Postorder<'a, 'tcx> { pub fn new(mir: &'a Mir<'tcx>, root: BasicBlock) -> Postorder<'a, 'tcx> { let mut po = Postorder { mir: mir, - visited: BitVector::new(mir.basic_blocks.len()), + visited: BitVector::new(mir.cfg.basic_blocks.len()), visit_stack: Vec::new() }; diff --git a/src/librustc/mir/visit.rs b/src/librustc/mir/visit.rs index 8846065135253..32ee4983d6a1d 100644 --- a/src/librustc/mir/visit.rs +++ b/src/librustc/mir/visit.rs @@ -242,7 +242,7 @@ macro_rules! make_mir_visitor { fn super_mir(&mut self, mir: & $($mutability)* Mir<'tcx>) { let Mir { - ref $($mutability)* basic_blocks, + ref $($mutability)* cfg, ref $($mutability)* scopes, promoted: _, // Visited by passes separately. ref $($mutability)* return_ty, @@ -253,6 +253,10 @@ macro_rules! make_mir_visitor { ref $($mutability)* span, } = *mir; + let CFG { + ref $($mutability)* basic_blocks + } = *cfg; + for (index, data) in basic_blocks.into_iter().enumerate() { let block = BasicBlock::new(index); self.visit_basic_block_data(block, data); diff --git a/src/librustc_borrowck/borrowck/mir/dataflow.rs b/src/librustc_borrowck/borrowck/mir/dataflow.rs index d6dd176e3ba28..6f537404058f6 100644 --- a/src/librustc_borrowck/borrowck/mir/dataflow.rs +++ b/src/librustc_borrowck/borrowck/mir/dataflow.rs @@ -167,7 +167,7 @@ impl<'c, 'b: 'c, 'a: 'b, 'tcx: 'a, OnReturn> PropagationContext<'c, 'b, 'a, 'tcx fn walk_cfg(&mut self, in_out: &mut [usize]) { let &mut MirBorrowckCtxt { ref mir, ref mut flow_state, .. } = self.mbcx; - for (idx, bb) in mir.basic_blocks.iter().enumerate() { + for (idx, bb) in mir.cfg.basic_blocks.iter().enumerate() { { let sets = flow_state.sets.for_block(idx); debug_assert!(in_out.len() == sets.on_entry.len()); @@ -371,7 +371,7 @@ impl DataflowState { let bits_per_block = denotation.bits_per_block(); let usize_bits = mem::size_of::() * 8; let words_per_block = (bits_per_block + usize_bits - 1) / usize_bits; - let num_blocks = mir.basic_blocks.len(); + let num_blocks = mir.cfg.basic_blocks.len(); let num_words = num_blocks * words_per_block; let entry = if denotation.initial_value() { usize::MAX } else {0}; diff --git a/src/librustc_data_structures/bitvec.rs b/src/librustc_data_structures/bitvec.rs index cb648038c3436..7b07b71f69121 100644 --- a/src/librustc_data_structures/bitvec.rs +++ b/src/librustc_data_structures/bitvec.rs @@ -27,6 +27,12 @@ impl BitVector { (self.data[word] & mask) != 0 } + pub fn clear(&mut self) { + for datum in &mut self.data { + *datum = 0; + } + } + /// Returns true if the bit has changed. pub fn insert(&mut self, bit: usize) -> bool { let (word, mask) = word_mask(bit); @@ -37,6 +43,16 @@ impl BitVector { new_value != value } + /// Returns true if the bit has changed. + pub fn remove(&mut self, bit: usize) -> bool { + let (word, mask) = word_mask(bit); + let data = &mut self.data[word]; + let value = *data; + let new_value = value & !mask; + *data = new_value; + new_value != value + } + pub fn insert_all(&mut self, all: &BitVector) -> bool { assert!(self.data.len() == all.data.len()); let mut changed = false; diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index f0c2de2932775..d74eb959af8e7 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -939,7 +939,8 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session, passes.push_pass(box mir::transform::remove_dead_blocks::RemoveDeadBlocks); passes.push_pass(box mir::transform::qualify_consts::QualifyAndPromoteConstants); passes.push_pass(box mir::transform::type_check::TypeckMir); - passes.push_pass(box mir::transform::simplify_cfg::SimplifyCfg); + passes.push_pass(box mir::transform::acs_propagate::ACSPropagate); + // passes.push_pass(box mir::transform::simplify_cfg::SimplifyCfg); passes.push_pass(box mir::transform::remove_dead_blocks::RemoveDeadBlocks); // And run everything. passes.run_passes(tcx, &mut mir_map); diff --git a/src/librustc_mir/build/cfg.rs b/src/librustc_mir/build/cfg.rs index 4859257f291c9..041b626b73276 100644 --- a/src/librustc_mir/build/cfg.rs +++ b/src/librustc_mir/build/cfg.rs @@ -8,90 +8,19 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. - - - //! Routines for manipulating the control-flow graph. -use build::{CFG, Location}; +use build::Location; use rustc::mir::repr::*; -use syntax::codemap::Span; - -impl<'tcx> CFG<'tcx> { - pub fn block_data(&self, blk: BasicBlock) -> &BasicBlockData<'tcx> { - &self.basic_blocks[blk.index()] - } - pub fn block_data_mut(&mut self, blk: BasicBlock) -> &mut BasicBlockData<'tcx> { - &mut self.basic_blocks[blk.index()] - } - - pub fn start_new_block(&mut self) -> BasicBlock { - let node_index = self.basic_blocks.len(); - self.basic_blocks.push(BasicBlockData::new(None)); - BasicBlock::new(node_index) - } - - pub fn start_new_cleanup_block(&mut self) -> BasicBlock { - let bb = self.start_new_block(); - self.block_data_mut(bb).is_cleanup = true; - bb - } +pub trait CfgExt<'tcx> { + fn current_location(&mut self, block: BasicBlock) -> Location; - pub fn push(&mut self, block: BasicBlock, statement: Statement<'tcx>) { - debug!("push({:?}, {:?})", block, statement); - self.block_data_mut(block).statements.push(statement); - } +} - pub fn current_location(&mut self, block: BasicBlock) -> Location { - let index = self.block_data(block).statements.len(); +impl<'tcx> CfgExt<'tcx> for CFG<'tcx> { + fn current_location(&mut self, block: BasicBlock) -> Location { + let index = self[block].statements.len(); Location { block: block, statement_index: index } } - - pub fn push_assign(&mut self, - block: BasicBlock, - scope: ScopeId, - span: Span, - lvalue: &Lvalue<'tcx>, - rvalue: Rvalue<'tcx>) { - self.push(block, Statement { - scope: scope, - span: span, - kind: StatementKind::Assign(lvalue.clone(), rvalue) - }); - } - - pub fn push_assign_constant(&mut self, - block: BasicBlock, - scope: ScopeId, - span: Span, - temp: &Lvalue<'tcx>, - constant: Constant<'tcx>) { - self.push_assign(block, scope, span, temp, - Rvalue::Use(Operand::Constant(constant))); - } - - pub fn push_assign_unit(&mut self, - block: BasicBlock, - scope: ScopeId, - span: Span, - lvalue: &Lvalue<'tcx>) { - self.push_assign(block, scope, span, lvalue, Rvalue::Aggregate( - AggregateKind::Tuple, vec![] - )); - } - - pub fn terminate(&mut self, - block: BasicBlock, - scope: ScopeId, - span: Span, - kind: TerminatorKind<'tcx>) { - debug_assert!(self.block_data(block).terminator.is_none(), - "terminate: block {:?} already has a terminator set", block); - self.block_data_mut(block).terminator = Some(Terminator { - span: span, - scope: scope, - kind: kind, - }); - } } diff --git a/src/librustc_mir/build/mod.rs b/src/librustc_mir/build/mod.rs index 77499a0f96cde..02222368588c1 100644 --- a/src/librustc_mir/build/mod.rs +++ b/src/librustc_mir/build/mod.rs @@ -58,10 +58,6 @@ pub struct Builder<'a, 'gcx: 'a+'tcx, 'tcx: 'a> { cached_return_block: Option, } -struct CFG<'tcx> { - basic_blocks: Vec>, -} - /// For each scope, we track the extent (from the HIR) and a /// single-entry-multiple-exit subgraph that contains all the /// statements/terminators within it. @@ -294,7 +290,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { } (Mir { - basic_blocks: self.cfg.basic_blocks, + cfg: self.cfg, scopes: self.scope_datas, promoted: vec![], var_decls: self.var_decls, diff --git a/src/librustc_mir/build/scope.rs b/src/librustc_mir/build/scope.rs index 071c8d618c845..3064b67297af5 100644 --- a/src/librustc_mir/build/scope.rs +++ b/src/librustc_mir/build/scope.rs @@ -86,7 +86,8 @@ should go to. */ -use build::{BlockAnd, BlockAndExtension, Builder, CFG, ScopeAuxiliary}; +use build::{BlockAnd, BlockAndExtension, Builder, ScopeAuxiliary}; +use build::cfg::CfgExt; use rustc::middle::region::{CodeExtent, CodeExtentData}; use rustc::middle::lang_items; use rustc::ty::subst::{Substs, Subst, VecPerParamSpace}; diff --git a/src/librustc_mir/lib.rs b/src/librustc_mir/lib.rs index 79d11e78bde5a..3d1ef31bd5c2a 100644 --- a/src/librustc_mir/lib.rs +++ b/src/librustc_mir/lib.rs @@ -49,4 +49,3 @@ mod hair; pub mod mir_map; pub mod pretty; pub mod transform; -pub mod traversal; diff --git a/src/librustc_mir/transform/acs_propagate.rs b/src/librustc_mir/transform/acs_propagate.rs new file mode 100644 index 0000000000000..38ce707f4edc5 --- /dev/null +++ b/src/librustc_mir/transform/acs_propagate.rs @@ -0,0 +1,224 @@ +// 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. + +//! This is Alias-Constant-Simplify propagation pass. This is a composition of three distinct +//! dataflow passes: alias-propagation, constant-propagation and terminator simplification. +//! +//! All these are very similar in their nature: +//! +//! | Constant | Alias | Simplify | +//!|----------------|-----------|----------|-----------| +//!| Lattice Domain | Lvalue | Lvalue | Lvalue | +//!| Lattice Value | Constant | Lvalue | Constant | +//!| Transfer | x = const | x = lval | x = const | +//!| Rewrite | x → const | x → lval | T(x) → T' | +//!| Bottom | {} | {} | {} | +//! +//! For all of them we will be using a lattice of Hashmap from Lvalue to +//! WTop> +//! +//! My personal believ is that it should be possible to make a way to compose two hashmap lattices +//! into one, but I can’t seem to get it just right yet, so we do the composing and decomposing +//! manually here. + +use rustc_data_structures::fnv::FnvHashMap; +use rustc_data_structures::bitvec::BitVector; +use rustc::mir::repr::*; +use rustc::mir::visit::{MutVisitor, LvalueContext}; +use rustc::mir::transform::lattice::Lattice; +use rustc::mir::transform::dataflow::*; +use rustc::mir::transform::{Pass, MirPass, MirSource}; +use rustc::ty::TyCtxt; +use rustc::middle::const_val::ConstVal; +use pretty; + +#[derive(PartialEq, Debug, Eq, Clone)] +pub enum Either<'tcx> { + Top, + Lvalue(Lvalue<'tcx>), + Const(Constant<'tcx>), +} + +impl<'tcx> Lattice for Either<'tcx> { + fn bottom() -> Self { unimplemented!() } + fn join(&mut self, other: &Self) -> bool { + if self == other { + false + } else { + *self = Either::Top; + true + } + } +} + +pub type ACSLattice<'a> = FnvHashMap, Either<'a>>; + +pub struct ACSPropagate; + +impl Pass for ACSPropagate {} + +impl<'tcx> MirPass<'tcx> for ACSPropagate { + fn run_pass<'a>(&mut self, tcx: TyCtxt<'a, 'tcx, 'tcx>, src: MirSource, mir: &mut Mir<'tcx>) { + let mut q = BitVector::new(mir.cfg.len()); + q.insert(START_BLOCK.index()); + let ret = ar_forward::(&mut mir.cfg, Facts::new(), q); + mir.cfg = ret.0; + pretty::dump_mir(tcx, "acs_propagate", &0, src, mir, None); + } + +} + +impl<'tcx> DataflowPass<'tcx> for ACSPropagate { + type Lattice = ACSLattice<'tcx>; + type Rewrite = RewriteAndThen<'tcx, AliasRewrite, + RewriteAndThen<'tcx, ConstRewrite, SimplifyRewrite>>; + type Transfer = ACSPropagateTransfer; +} + +pub struct ACSPropagateTransfer; + +impl<'tcx> Transfer<'tcx, ACSLattice<'tcx>> for ACSPropagateTransfer { + type TerminatorOut = Vec>; + fn stmt(s: &Statement<'tcx>, mut lat: ACSLattice<'tcx>) -> ACSLattice<'tcx> { + let StatementKind::Assign(ref lval, ref rval) = s.kind; + match *rval { + Rvalue::Use(Operand::Consume(ref nlval)) => + lat.insert(lval.clone(), Either::Lvalue(nlval.clone())), + Rvalue::Use(Operand::Constant(ref c)) => + lat.insert(lval.clone(), Either::Const(c.clone())), + _ => lat.insert(lval.clone(), Either::Top) + }; + lat + } + fn term(t: &Terminator<'tcx>, lat: ACSLattice<'tcx>) -> Self::TerminatorOut { + // FIXME: this should inspect the terminators and set their known values to constants. Esp. + // for the if: in the truthy branch the operand is known to be true and in the falsy branch + // the operand is known to be false. Now we just ignore the potential here. + let mut ret = vec![]; + ret.resize(t.successors().len(), lat); + ret + } +} + +pub struct AliasRewrite; + +impl<'tcx> Rewrite<'tcx, ACSLattice<'tcx>> for AliasRewrite { + fn stmt(s: &Statement<'tcx>, l: &ACSLattice<'tcx>, cfg: &mut CFG<'tcx>) + -> StatementChange<'tcx> { + let mut ns = s.clone(); + let mut vis = RewriteAliasVisitor(&l, false); + vis.visit_statement(START_BLOCK, &mut ns); + if vis.1 { StatementChange::Statement(ns) } else { StatementChange::None } + } + fn term(t: &Terminator<'tcx>, l: &ACSLattice<'tcx>, cfg: &mut CFG<'tcx>) + -> TerminatorChange<'tcx> { + let mut nt = t.clone(); + let mut vis = RewriteAliasVisitor(&l, false); + vis.visit_terminator(START_BLOCK, &mut nt); + if vis.1 { TerminatorChange::Terminator(nt) } else { TerminatorChange::None } + } +} + +struct RewriteAliasVisitor<'a, 'tcx: 'a>(pub &'a ACSLattice<'tcx>, pub bool); +impl<'a, 'tcx> MutVisitor<'tcx> for RewriteAliasVisitor<'a, 'tcx> { + fn visit_lvalue(&mut self, lvalue: &mut Lvalue<'tcx>, context: LvalueContext) { + match context { + LvalueContext::Store | LvalueContext::Call => {} + _ => { + let replacement = self.0.get(lvalue); + match replacement { + Some(&Either::Lvalue(ref nlval)) => { + self.1 = true; + *lvalue = nlval.clone(); + } + _ => {} + } + } + } + self.super_lvalue(lvalue, context); + } +} + +pub struct ConstRewrite; + +impl<'tcx> Rewrite<'tcx, ACSLattice<'tcx>> for ConstRewrite { + fn stmt(s: &Statement<'tcx>, l: &ACSLattice<'tcx>, cfg: &mut CFG<'tcx>) + -> StatementChange<'tcx> { + let mut ns = s.clone(); + let mut vis = RewriteConstVisitor(&l, false); + vis.visit_statement(START_BLOCK, &mut ns); + if vis.1 { StatementChange::Statement(ns) } else { StatementChange::None } + } + fn term(t: &Terminator<'tcx>, l: &ACSLattice<'tcx>, cfg: &mut CFG<'tcx>) + -> TerminatorChange<'tcx> { + let mut nt = t.clone(); + let mut vis = RewriteConstVisitor(&l, false); + vis.visit_terminator(START_BLOCK, &mut nt); + if vis.1 { TerminatorChange::Terminator(nt) } else { TerminatorChange::None } + } +} + +struct RewriteConstVisitor<'a, 'tcx: 'a>(pub &'a ACSLattice<'tcx>, pub bool); +impl<'a, 'tcx> MutVisitor<'tcx> for RewriteConstVisitor<'a, 'tcx> { + fn visit_operand(&mut self, op: &mut Operand<'tcx>) { + let repl = if let Operand::Consume(ref lval) = *op { + if let Some(&Either::Const(ref c)) = self.0.get(lval) { + Some(c.clone()) + } else { + None + } + } else { + None + }; + if let Some(c) = repl { + *op = Operand::Constant(c); + } + self.super_operand(op); + } +} + + +pub struct SimplifyRewrite; + +impl<'tcx> Rewrite<'tcx, ACSLattice<'tcx>> for SimplifyRewrite { + fn stmt(s: &Statement<'tcx>, l: &ACSLattice<'tcx>, cfg: &mut CFG<'tcx>) + -> StatementChange<'tcx> { + StatementChange::None + } + fn term(t: &Terminator<'tcx>, l: &ACSLattice<'tcx>, cfg: &mut CFG<'tcx>) + -> TerminatorChange<'tcx> { + match t.kind { + TerminatorKind::If { ref targets, .. } if targets.0 == targets.1 => { + let mut nt = t.clone(); + nt.kind = TerminatorKind::Goto { target: targets.0 }; + TerminatorChange::Terminator(nt) + } + TerminatorKind::If { ref targets, cond: Operand::Constant(Constant { + literal: Literal::Value { + value: ConstVal::Bool(cond) + }, .. + }) } => { + let mut nt = t.clone(); + if cond { + nt.kind = TerminatorKind::Goto { target: targets.0 }; + } else { + nt.kind = TerminatorKind::Goto { target: targets.1 }; + } + TerminatorChange::Terminator(nt) + } + TerminatorKind::SwitchInt { ref targets, .. } if targets.len() == 1 => { + let mut nt = t.clone(); + nt.kind = TerminatorKind::Goto { target: targets[0] }; + TerminatorChange::Terminator(nt) + } + _ => TerminatorChange::None + } + } +} diff --git a/src/librustc_mir/transform/break_critical_edges.rs b/src/librustc_mir/transform/break_critical_edges.rs index a6af30b7eec08..ee8d4c2d1bd03 100644 --- a/src/librustc_mir/transform/break_critical_edges.rs +++ b/src/librustc_mir/transform/break_critical_edges.rs @@ -11,10 +11,10 @@ use rustc::ty::TyCtxt; use rustc::mir::repr::*; use rustc::mir::transform::{MirPass, MirSource, Pass}; +use rustc::mir::traversal; use rustc_data_structures::bitvec::BitVector; -use traversal; pub struct BreakCriticalEdges; @@ -51,7 +51,7 @@ impl<'tcx> MirPass<'tcx> for BreakCriticalEdges { impl Pass for BreakCriticalEdges {} fn break_critical_edges(mir: &mut Mir) { - let mut pred_count = vec![0u32; mir.basic_blocks.len()]; + let mut pred_count = vec![0u32; mir.cfg.basic_blocks.len()]; // Build the precedecessor map for the MIR for (_, data) in traversal::preorder(mir) { @@ -62,14 +62,14 @@ fn break_critical_edges(mir: &mut Mir) { } } - let cleanup_map : BitVector = mir.basic_blocks + let cleanup_map : BitVector = mir.cfg.basic_blocks .iter().map(|bb| bb.is_cleanup).collect(); // We need a place to store the new blocks generated let mut new_blocks = Vec::new(); let bbs = mir.all_basic_blocks(); - let cur_len = mir.basic_blocks.len(); + let cur_len = mir.cfg.basic_blocks.len(); for &bb in &bbs { let data = mir.basic_block_data_mut(bb); @@ -104,7 +104,7 @@ fn break_critical_edges(mir: &mut Mir) { debug!("Broke {} N edges", new_blocks.len()); - mir.basic_blocks.extend_from_slice(&new_blocks); + mir.cfg.basic_blocks.extend_from_slice(&new_blocks); } // Returns true if the terminator would use an invoke in LLVM. diff --git a/src/librustc_mir/transform/mod.rs b/src/librustc_mir/transform/mod.rs index 51f5c3cd7f53d..71b98613eabbb 100644 --- a/src/librustc_mir/transform/mod.rs +++ b/src/librustc_mir/transform/mod.rs @@ -16,3 +16,4 @@ pub mod type_check; pub mod break_critical_edges; pub mod promote_consts; pub mod qualify_consts; +pub mod acs_propagate; diff --git a/src/librustc_mir/transform/promote_consts.rs b/src/librustc_mir/transform/promote_consts.rs index 431568b004d3d..25a2d74999aff 100644 --- a/src/librustc_mir/transform/promote_consts.rs +++ b/src/librustc_mir/transform/promote_consts.rs @@ -24,11 +24,11 @@ use rustc::mir::repr::*; use rustc::mir::visit::{LvalueContext, MutVisitor, Visitor}; +use rustc::mir::traversal::ReversePostorder; use rustc::ty::{self, TyCtxt}; use syntax::codemap::Span; use build::Location; -use traversal::ReversePostorder; use std::mem; @@ -163,8 +163,8 @@ struct Promoter<'a, 'tcx: 'a> { impl<'a, 'tcx> Promoter<'a, 'tcx> { fn new_block(&mut self) -> BasicBlock { - let index = self.promoted.basic_blocks.len(); - self.promoted.basic_blocks.push(BasicBlockData { + let index = self.promoted.cfg.basic_blocks.len(); + self.promoted.cfg.basic_blocks.push(BasicBlockData { statements: vec![], terminator: Some(Terminator { span: self.promoted.span, @@ -177,7 +177,7 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> { } fn assign(&mut self, dest: Lvalue<'tcx>, rvalue: Rvalue<'tcx>, span: Span) { - let data = self.promoted.basic_blocks.last_mut().unwrap(); + let data = self.promoted.cfg.basic_blocks.last_mut().unwrap(); data.statements.push(Statement { span: span, scope: ScopeId::new(0), @@ -268,7 +268,7 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> { if stmt_idx < no_stmts { self.assign(new_temp, rvalue.unwrap(), span); } else { - let last = self.promoted.basic_blocks.len() - 1; + let last = self.promoted.cfg.basic_blocks.len() - 1; let new_target = self.new_block(); let mut call = call.unwrap(); match call { @@ -277,7 +277,7 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> { } _ => bug!() } - let terminator = &mut self.promoted.basic_blocks[last].terminator_mut(); + let terminator = &mut self.promoted.cfg.basic_blocks[last].terminator_mut(); terminator.span = span; terminator.kind = call; } @@ -366,7 +366,7 @@ pub fn promote_candidates<'a, 'tcx>(mir: &mut Mir<'tcx>, let mut promoter = Promoter { source: mir, promoted: Mir { - basic_blocks: vec![], + cfg: CFG { basic_blocks: vec![] }, scopes: vec![ScopeData { span: span, parent_scope: None @@ -388,7 +388,7 @@ pub fn promote_candidates<'a, 'tcx>(mir: &mut Mir<'tcx>, // Eliminate assignments to, and drops of promoted temps. let promoted = |index: u32| temps[index as usize] == TempState::PromotedOut; - for block in &mut mir.basic_blocks { + for block in &mut mir.cfg.basic_blocks { block.statements.retain(|statement| { match statement.kind { StatementKind::Assign(Lvalue::Temp(index), _) => { diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs index 54ac04bea9c4c..e74b0597ea87a 100644 --- a/src/librustc_mir/transform/qualify_consts.rs +++ b/src/librustc_mir/transform/qualify_consts.rs @@ -26,6 +26,7 @@ use rustc::mir::repr::*; use rustc::mir::mir_map::MirMap; use rustc::mir::transform::{Pass, MirMapPass, MirSource}; use rustc::mir::visit::{LvalueContext, Visitor}; +use rustc::mir::traversal::{self, ReversePostorder}; use rustc::util::nodemap::DefIdMap; use syntax::abi::Abi; use syntax::codemap::Span; @@ -35,7 +36,6 @@ use std::collections::hash_map::Entry; use std::fmt; use build::Location; -use traversal::{self, ReversePostorder}; use super::promote_consts::{self, Candidate, TempState}; @@ -386,7 +386,7 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> { fn qualify_const(&mut self) -> Qualif { let mir = self.mir; - let mut seen_blocks = BitVector::new(mir.basic_blocks.len()); + let mut seen_blocks = BitVector::new(mir.cfg.basic_blocks.len()); let mut bb = START_BLOCK; loop { seen_blocks.insert(bb.index()); diff --git a/src/librustc_mir/transform/remove_dead_blocks.rs b/src/librustc_mir/transform/remove_dead_blocks.rs index 44f3ce7361cf4..5b71c912bf28d 100644 --- a/src/librustc_mir/transform/remove_dead_blocks.rs +++ b/src/librustc_mir/transform/remove_dead_blocks.rs @@ -42,7 +42,7 @@ pub struct RemoveDeadBlocks; impl<'tcx> MirPass<'tcx> for RemoveDeadBlocks { fn run_pass<'a>(&mut self, _: TyCtxt<'a, 'tcx, 'tcx>, _: MirSource, mir: &mut Mir<'tcx>) { - let mut seen = BitVector::new(mir.basic_blocks.len()); + let mut seen = BitVector::new(mir.cfg.basic_blocks.len()); // This block is always required. seen.insert(START_BLOCK.index()); @@ -63,7 +63,7 @@ impl Pass for RemoveDeadBlocks {} /// Mass removal of basic blocks to keep the ID-remapping cheap. fn retain_basic_blocks(mir: &mut Mir, keep: &BitVector) { - let num_blocks = mir.basic_blocks.len(); + let num_blocks = mir.cfg.basic_blocks.len(); let mut replacements: Vec<_> = (0..num_blocks).map(BasicBlock::new).collect(); let mut used_blocks = 0; @@ -72,11 +72,11 @@ fn retain_basic_blocks(mir: &mut Mir, keep: &BitVector) { if alive_index != used_blocks { // Swap the next alive block data with the current available slot. Since alive_index is // non-decreasing this is a valid operation. - mir.basic_blocks.swap(alive_index, used_blocks); + mir.cfg.basic_blocks.swap(alive_index, used_blocks); } used_blocks += 1; } - mir.basic_blocks.truncate(used_blocks); + mir.cfg.basic_blocks.truncate(used_blocks); for bb in mir.all_basic_blocks() { for target in mir.basic_block_data_mut(bb).terminator_mut().successors_mut() { diff --git a/src/librustc_mir/transform/simplify_cfg.rs b/src/librustc_mir/transform/simplify_cfg.rs index fa897384a542a..228fca054c23d 100644 --- a/src/librustc_mir/transform/simplify_cfg.rs +++ b/src/librustc_mir/transform/simplify_cfg.rs @@ -8,7 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use rustc::middle::const_val::ConstVal; use rustc::ty::TyCtxt; use rustc::mir::repr::*; use rustc::mir::transform::{MirPass, MirSource, Pass}; @@ -74,40 +73,6 @@ impl SimplifyCfg { changed } - fn simplify_branches(&self, mir: &mut Mir) -> bool { - let mut changed = false; - - for bb in mir.all_basic_blocks() { - let basic_block = mir.basic_block_data_mut(bb); - let mut terminator = basic_block.terminator_mut(); - terminator.kind = match terminator.kind { - TerminatorKind::If { ref targets, .. } if targets.0 == targets.1 => { - changed = true; - TerminatorKind::Goto { target: targets.0 } - } - - TerminatorKind::If { ref targets, cond: Operand::Constant(Constant { - literal: Literal::Value { - value: ConstVal::Bool(cond) - }, .. - }) } => { - changed = true; - if cond { - TerminatorKind::Goto { target: targets.0 } - } else { - TerminatorKind::Goto { target: targets.1 } - } - } - - TerminatorKind::SwitchInt { ref targets, .. } if targets.len() == 1 => { - TerminatorKind::Goto { target: targets[0] } - } - _ => continue - } - } - - changed - } } impl<'tcx> MirPass<'tcx> for SimplifyCfg { @@ -118,12 +83,11 @@ impl<'tcx> MirPass<'tcx> for SimplifyCfg { while changed { pretty::dump_mir(tcx, "simplify_cfg", &counter, src, mir, None); counter += 1; - changed = self.simplify_branches(mir); changed |= self.remove_goto_chains(mir); RemoveDeadBlocks.run_pass(tcx, src, mir); } // FIXME: Should probably be moved into some kind of pass manager - mir.basic_blocks.shrink_to_fit(); + mir.cfg.basic_blocks.shrink_to_fit(); } } diff --git a/src/librustc_mir/transform/type_check.rs b/src/librustc_mir/transform/type_check.rs index 40157aa934c65..f03e620fa2e01 100644 --- a/src/librustc_mir/transform/type_check.rs +++ b/src/librustc_mir/transform/type_check.rs @@ -544,7 +544,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { fn typeck_mir(&mut self, mir: &Mir<'tcx>) { self.last_span = mir.span; debug!("run_on_mir: {:?}", mir.span); - for block in &mir.basic_blocks { + for block in &mir.cfg.basic_blocks { for stmt in &block.statements { if stmt.span != DUMMY_SP { self.last_span = stmt.span; diff --git a/src/librustc_trans/mir/mod.rs b/src/librustc_trans/mir/mod.rs index b98e04e51c007..9714d066500d2 100644 --- a/src/librustc_trans/mir/mod.rs +++ b/src/librustc_trans/mir/mod.rs @@ -14,6 +14,7 @@ use llvm::debuginfo::DIScope; use rustc::ty; use rustc::mir::repr as mir; use rustc::mir::tcx::LvalueTy; +use rustc::mir::traversal; use session::config::FullDebugInfo; use base; use common::{self, Block, BlockAndBuilder, CrateContext, FunctionContext}; @@ -34,7 +35,6 @@ use rustc_data_structures::bitvec::BitVector; pub use self::constant::trans_static_initializer; use self::lvalue::{LvalueRef, get_dataptr, get_meta}; -use rustc_mir::traversal; use self::operand::{OperandRef, OperandValue};