diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index e1fb701e641bf..0932f1320e8f7 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)] @@ -107,6 +108,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..65d8e832ec010 --- /dev/null +++ b/src/librustc/mir/cfg.rs @@ -0,0 +1,167 @@ +// 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 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) { + if b1 != b2 { + for idx in 0..self.len() { + let bb = BasicBlock::new(idx); + for target in self.successors_mut(bb) { + if *target == b1 { + *target = b2; + } else if *target == b2 { + *target = 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 9666741d032b8..adfe8ad5c6ae6 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, ConstMathErr}; @@ -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] } } @@ -611,7 +613,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), @@ -796,7 +798,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..6f12fba5b126b --- /dev/null +++ b/src/librustc/mir/transform/dataflow.rs @@ -0,0 +1,408 @@ +// 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 mir::repr as mir; +use mir::cfg::CFG; +use mir::repr::{BasicBlock, START_BLOCK}; +use rustc_data_structures::bitvec::BitVector; + +use mir::transform::lattice::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(&self, &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(&self, &mir::Terminator<'tcx>, &L, &mut CFG<'tcx>) -> TerminatorChange<'tcx>; + + fn and_then(self, other: R2) -> RewriteAndThen where Self: Sized { + RewriteAndThen(self, other) + } +} + +/// 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(R1, R2); +impl<'tcx, L, R1, R2> Rewrite<'tcx, L> for RewriteAndThen +where L: Lattice, R1: Rewrite<'tcx, L>, R2: Rewrite<'tcx, L> { + fn stmt(&self, s: &mir::Statement<'tcx>, l: &L, c: &mut CFG<'tcx>) -> StatementChange<'tcx> { + let rs = self.0.stmt(s, l, c); + match rs { + StatementChange::None => self.1.stmt(s, l, c), + StatementChange::Remove => StatementChange::Remove, + StatementChange::Statement(ns) => + match self.1.stmt(&ns, l, c) { + StatementChange::None => StatementChange::Statement(ns), + x => x + } + } + } + + fn term(&self, t: &mir::Terminator<'tcx>, l: &L, c: &mut CFG<'tcx>) -> TerminatorChange<'tcx> { + let rt = self.0.term(t, l, c); + match rt { + TerminatorChange::None => self.1.term(t, l, c), + TerminatorChange::Terminator(nt) => match self.1.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>), +} + +pub trait Transfer<'tcx> { + type Lattice: Lattice; + + /// The transfer function which given a statement and a fact produces a fact which is true + /// after the statement. + fn stmt(&self, &mir::Statement<'tcx>, Self::Lattice) -> Self::Lattice; + + /// 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(&self, &mir::Terminator<'tcx>, Self::Lattice) -> Vec; +} + + +/// 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 self.0.get(index.index()).is_none() { + 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, R>(cfg: &CFG<'tcx>, fs: Facts, transfer: T, rewrite: R) +-> (CFG<'tcx>, Facts) +where T: Transfer<'tcx>, + R: Rewrite<'tcx, T::Lattice> +{ + let mut queue = BitVector::new(cfg.len()); + queue.insert(START_BLOCK.index()); + + fixpoint(cfg, Direction::Forward, |bb, fact, cfg| { + let mut fact = fact.clone(); + let mut changed = false; + // Swap out the vector of old statements for a duration of statement inspection. + let mut statements = ::std::mem::replace(&mut cfg[bb].statements, Vec::new()); + + // FIXME(#25477): This could be implemented much more cleanly with `retain_mut` + let num_statements = statements.len(); + let mut num_removed = 0; + for stmt_idx in 0..num_statements { + // Given a fact and statement produce a new fact and optionally a replacement + // graph. + match rewrite.stmt(&statements[stmt_idx], &fact, cfg) { + StatementChange::None => {} + StatementChange::Remove => { + changed = true; + num_removed += 1; + continue; + } + StatementChange::Statement(new_stmt) => { + changed = true; + fact = transfer.stmt(&new_stmt, fact); + statements[stmt_idx] = new_stmt; + } + } + statements.swap(stmt_idx - num_removed, stmt_idx); + } + statements.truncate(num_statements - num_removed); + + // Swap the statements back in. + cfg[bb].statements = statements; + + // Handle the terminator replacement and transfer. + let terminator = cfg[bb].terminator.take().unwrap(); + let repl = rewrite.term(&terminator, &fact, cfg); + match repl { + TerminatorChange::None => { + cfg[bb].terminator = Some(terminator) + } + TerminatorChange::Terminator(new_terminator) => { + changed = true; + cfg[bb].terminator = Some(new_terminator); + } + } + let new_facts = transfer.term(cfg[bb].terminator(), fact); + + (changed, 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); +// // FIXME: 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>(original_cfg: &CFG<'tcx>, + direction: Direction, + f: BF, + to_visit: &mut BitVector, + mut init_facts: Facts) -> (CFG<'tcx>, Facts) +// FIXME: we probably want to pass in a list of basicblocks as successors (predecessors in +// backward fixpoint) and let BF return just a list of F. +where BF: Fn(BasicBlock, &F, &mut CFG<'tcx>) -> (bool, 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 = original_cfg.clone(); + + while let Some(block) = to_visit.iter().next() { + to_visit.remove(block); + let block = BasicBlock::new(block); + + let (_changed, new_facts) = { + let fact = &mut init_facts[block]; + f(block, fact, &mut cfg) + }; + + // Then we record the facts in the correct direction. + match direction { + Direction::Forward => { + let mut changed_targets = vec![]; + + for (f, &target) in new_facts.into_iter() + .zip(cfg[block].terminator().successors().iter()) { + if Lattice::join(&mut init_facts[target], &f) { + changed_targets.push(target); + } + } + + for target in changed_targets { + to_visit.insert(target.index()); + cfg[target].clone_from(&original_cfg[target]); + } + } + // Direction::Backward => 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..e0c6f43164c6a --- /dev/null +++ b/src/librustc/mir/transform/lattice.rs @@ -0,0 +1,125 @@ +// 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 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) + 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 99% rename from src/librustc/mir/transform.rs rename to src/librustc/mir/transform/mod.rs index 828a48532a2fc..3c9257086bc42 100644 --- a/src/librustc/mir/transform.rs +++ b/src/librustc/mir/transform/mod.rs @@ -8,6 +8,9 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +pub mod lattice; +pub mod 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 5c9582b945bb8..d06b3550d5278 100644 --- a/src/librustc/mir/visit.rs +++ b/src/librustc/mir/visit.rs @@ -247,7 +247,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, @@ -258,6 +258,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/mod.rs b/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs index 81655b5e386f6..2f6a9e1b74b7f 100644 --- a/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs +++ b/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs @@ -112,7 +112,7 @@ impl<'b, 'a: 'b, 'tcx: 'a, BD> PropagationContext<'b, 'a, 'tcx, BD> fn walk_cfg(&mut self, in_out: &mut IdxSet) { let mir = self.builder.mir; - for (bb_idx, bb_data) in mir.basic_blocks.iter().enumerate() { + for (bb_idx, bb_data) in mir.cfg.basic_blocks.iter().enumerate() { let builder = &mut self.builder; { let sets = builder.flow_state.sets.for_block(bb_idx); @@ -396,7 +396,7 @@ impl<'a, 'tcx: 'a, D> DataflowAnalysis<'a, 'tcx, D> // (now rounded up to multiple of word size) let bits_per_block = words_per_block * usize_bits; - let num_blocks = mir.basic_blocks.len(); + let num_blocks = mir.cfg.basic_blocks.len(); let num_overall = num_blocks * bits_per_block; let zeroes = Bits::new(IdxSetBuf::new_empty(num_overall)); diff --git a/src/librustc_borrowck/borrowck/mir/patch.rs b/src/librustc_borrowck/borrowck/mir/patch.rs index b390c19af1a5b..2326f2b275960 100644 --- a/src/librustc_borrowck/borrowck/mir/patch.rs +++ b/src/librustc_borrowck/borrowck/mir/patch.rs @@ -32,7 +32,7 @@ impl<'tcx> MirPatch<'tcx> { pub fn new(mir: &Mir<'tcx>) -> Self { let mut result = MirPatch { patch_map: iter::repeat(None) - .take(mir.basic_blocks.len()).collect(), + .take(mir.cfg.basic_blocks.len()).collect(), new_blocks: vec![], new_temps: vec![], new_statements: vec![], @@ -86,7 +86,7 @@ impl<'tcx> MirPatch<'tcx> { } pub fn terminator_loc(&self, mir: &Mir<'tcx>, bb: BasicBlock) -> Location { - let offset = match bb.index().checked_sub(mir.basic_blocks.len()) { + let offset = match bb.index().checked_sub(mir.cfg.basic_blocks.len()) { Some(index) => self.new_blocks[index].statements.len(), None => mir.basic_block_data(bb).statements.len() }; @@ -131,13 +131,13 @@ impl<'tcx> MirPatch<'tcx> { debug!("MirPatch: {:?} new temps, starting from index {}: {:?}", self.new_temps.len(), mir.temp_decls.len(), self.new_temps); debug!("MirPatch: {} new blocks, starting from index {}", - self.new_blocks.len(), mir.basic_blocks.len()); - mir.basic_blocks.extend(self.new_blocks); + self.new_blocks.len(), mir.cfg.basic_blocks.len()); + mir.cfg.basic_blocks.extend(self.new_blocks); mir.temp_decls.extend(self.new_temps); for (src, patch) in self.patch_map.into_iter().enumerate() { if let Some(patch) = patch { debug!("MirPatch: patching block {:?}", src); - mir.basic_blocks[src].terminator_mut().kind = patch; + mir.cfg.basic_blocks[src].terminator_mut().kind = patch; } } @@ -175,7 +175,7 @@ impl<'tcx> MirPatch<'tcx> { } pub fn context_for_location(&self, mir: &Mir, loc: Location) -> (Span, ScopeId) { - let data = match loc.block.index().checked_sub(mir.basic_blocks.len()) { + let data = match loc.block.index().checked_sub(mir.cfg.basic_blocks.len()) { Some(new) => &self.new_blocks[new], None => mir.basic_block_data(loc.block) }; diff --git a/src/librustc_data_structures/bitvec.rs b/src/librustc_data_structures/bitvec.rs index 79d3f0cf68846..c8981555813ec 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 b28d203ed8dc2..13b18da1e96cd 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -967,7 +967,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 9d7818a9ba4d6..7250d4f404f4c 100644 --- a/src/librustc_mir/build/mod.rs +++ b/src/librustc_mir/build/mod.rs @@ -57,10 +57,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. @@ -293,7 +289,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 209649dd2fd18..93d87bf78c643 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..6cbde390a1517 --- /dev/null +++ b/src/librustc_mir/transform/acs_propagate.rs @@ -0,0 +1,314 @@ +// 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 belief 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::mir::repr::*; +use rustc::mir::visit::{MutVisitor, LvalueContext}; +use rustc::mir::transform::lattice::{Lattice, WBottom}; +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)] +enum Either<'tcx> { + Lvalue(Lvalue<'tcx>), + Const(Constant<'tcx>), +} + +#[derive(Debug, Clone)] +struct AcsLattice<'tcx> { + known_values: FnvHashMap, Either<'tcx>> +} + +impl<'tcx> Lattice for AcsLattice<'tcx> { + fn bottom() -> Self { unimplemented!() } + fn join(&mut self, other: &Self) -> bool { + let mut to_remove = vec![]; + + for (k, v) in &self.known_values { + if other.known_values.get(k).map_or(true, |other_v| other_v != v) { + to_remove.push(k.clone()); + } + } + + for k in &to_remove { + self.known_values.remove(k); + } + + !to_remove.is_empty() + } +} + +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 ret = ar_forward( + &mut mir.cfg, + Facts::new(), + AcsPropagateTransfer, + AliasRewrite.and_then(ConstRewrite).and_then(SimplifyRewrite) + ); + mir.cfg = ret.0; + pretty::dump_mir(tcx, "acs_propagate", &0, src, mir, None); + } + +} + +struct AcsPropagateTransfer; + +fn base_lvalue<'a, 'tcx>(mut lval: &'a Lvalue<'tcx>) -> &'a Lvalue<'tcx> { + while let &Lvalue::Projection(ref proj) = lval { + lval = &proj.base; + } + lval +} + +fn invalidate<'tcx>(map: &mut FnvHashMap, Either<'tcx>>, lval: &Lvalue<'tcx>) { + map.remove(lval); + + let mut repl = None; + + for (k, v) in &mut *map { + if let Either::Lvalue(ref mut nlval) = *v { + if nlval == lval { + match repl { + None => { + repl = Some(k.clone()) + }, + Some(ref r) => { + *nlval = r.clone(); + } + } + } + } + } + + if let Some(repl) = repl { + map.remove(&repl); + } +} + +impl<'tcx> Transfer<'tcx> for AcsPropagateTransfer { + type Lattice = WBottom>; + + fn stmt(&self, s: &Statement<'tcx>, lat: WBottom>) -> WBottom> { + let mut lat_map = match lat { + WBottom::Bottom => FnvHashMap::default(), + WBottom::Value(lat) => lat.known_values + }; + + let StatementKind::Assign(ref lval, ref rval) = s.kind; + invalidate(&mut lat_map, base_lvalue(lval)); + + if let &Lvalue::Projection(_) = lval { + return WBottom::Value(AcsLattice { + known_values: lat_map + }); + } + + match *rval { + Rvalue::Use(Operand::Consume(ref nlval)) => { + lat_map.insert(lval.clone(), Either::Lvalue(nlval.clone())); + }, + Rvalue::Use(Operand::Constant(ref c)) => { + lat_map.insert(lval.clone(), Either::Const(c.clone())); + }, + _ => { } + }; + + WBottom::Value(AcsLattice { + known_values: lat_map + }) + } + + fn term(&self, t: &Terminator<'tcx>, mut lat: WBottom>) -> Vec>> { + match t.kind { + TerminatorKind::Call { .. } | + TerminatorKind::Drop { .. } | + TerminatorKind::DropAndReplace { .. } => { + // FIXME: Be smarter here by using an alias analysis + lat = WBottom::Value(AcsLattice { + known_values: FnvHashMap::default() + }); + }, + _ => { } + } + + // 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 + } +} + +struct AliasRewrite; + +impl<'tcx> Rewrite<'tcx, WBottom>> for AliasRewrite { + fn stmt(&self, s: &Statement<'tcx>, l: &WBottom>, _: &mut CFG<'tcx>) + -> StatementChange<'tcx> { + if let &WBottom::Value(ref lat) = l { + let mut ns = s.clone(); + let mut vis = RewriteAliasVisitor(&lat.known_values, false); + vis.visit_statement(START_BLOCK, &mut ns); + if vis.1 { + return StatementChange::Statement(ns); + } + } + StatementChange::None + } + + fn term(&self, t: &Terminator<'tcx>, l: &WBottom>, _: &mut CFG<'tcx>) + -> TerminatorChange<'tcx> { + if let &WBottom::Value(ref lat) = l { + let mut nt = t.clone(); + let mut vis = RewriteAliasVisitor(&lat.known_values, false); + vis.visit_terminator(START_BLOCK, &mut nt); + if vis.1 { + return TerminatorChange::Terminator(nt); + } + } + TerminatorChange::None + } +} + +struct RewriteAliasVisitor<'a, 'tcx: 'a>(&'a FnvHashMap, Either<'tcx>>, bool); +impl<'a, 'tcx> MutVisitor<'tcx> for RewriteAliasVisitor<'a, 'tcx> { + fn visit_lvalue(&mut self, lvalue: &mut Lvalue<'tcx>, context: LvalueContext) { + match context { + LvalueContext::Consume => { + if let Some(&Either::Lvalue(ref nlval)) = self.0.get(lvalue) { + self.1 = true; + *lvalue = nlval.clone(); + } + }, + _ => { } + } + self.super_lvalue(lvalue, context); + } +} + +struct ConstRewrite; + +impl<'tcx> Rewrite<'tcx, WBottom>> for ConstRewrite { + fn stmt(&self, s: &Statement<'tcx>, l: &WBottom>, _: &mut CFG<'tcx>) + -> StatementChange<'tcx> { + if let &WBottom::Value(ref lat) = l { + let mut ns = s.clone(); + let mut vis = RewriteConstVisitor(&lat.known_values, false); + vis.visit_statement(START_BLOCK, &mut ns); + if vis.1 { + return StatementChange::Statement(ns); + } + } + StatementChange::None + } + + fn term(&self, t: &Terminator<'tcx>, l: &WBottom>, _: &mut CFG<'tcx>) + -> TerminatorChange<'tcx> { + if let &WBottom::Value(ref lat) = l { + let mut nt = t.clone(); + let mut vis = RewriteConstVisitor(&lat.known_values, false); + vis.visit_terminator(START_BLOCK, &mut nt); + if vis.1 { + return TerminatorChange::Terminator(nt); + } + } + TerminatorChange::None + } +} + +struct RewriteConstVisitor<'a, 'tcx: 'a>(&'a FnvHashMap, Either<'tcx>>, bool); +impl<'a, 'tcx> MutVisitor<'tcx> for RewriteConstVisitor<'a, 'tcx> { + fn visit_operand(&mut self, op: &mut Operand<'tcx>) { + // To satisy borrow checker, modify `op` after inspecting it + 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 { + self.1 = true; + *op = Operand::Constant(c); + } + + self.super_operand(op); + } +} + + +struct SimplifyRewrite; + +impl<'tcx, L: Lattice> Rewrite<'tcx, L> for SimplifyRewrite { + fn stmt(&self, _: &Statement<'tcx>, _: &L, _: &mut CFG<'tcx>) + -> StatementChange<'tcx> { + StatementChange::None + } + + fn term(&self, t: &Terminator<'tcx>, _: &L, _: &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/add_call_guards.rs b/src/librustc_mir/transform/add_call_guards.rs index bcdd62c189972..fa6bbda54c79e 100644 --- a/src/librustc_mir/transform/add_call_guards.rs +++ b/src/librustc_mir/transform/add_call_guards.rs @@ -11,11 +11,10 @@ use rustc::ty::TyCtxt; use rustc::mir::repr::*; use rustc::mir::transform::{MirPass, MirSource, Pass}; +use rustc::mir::traversal; use pretty; -use traversal; - pub struct AddCallGuards; /** @@ -40,7 +39,7 @@ pub struct AddCallGuards; impl<'tcx> MirPass<'tcx> for AddCallGuards { fn run_pass<'a>(&mut self, tcx: TyCtxt<'a, 'tcx, 'tcx>, src: MirSource, mir: &mut Mir<'tcx>) { - 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) { @@ -55,7 +54,7 @@ impl<'tcx> MirPass<'tcx> for AddCallGuards { 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); @@ -91,7 +90,7 @@ impl<'tcx> MirPass<'tcx> for AddCallGuards { pretty::dump_mir(tcx, "break_cleanup_edges", &0, src, mir, None); debug!("Broke {} N edges", new_blocks.len()); - mir.basic_blocks.extend_from_slice(&new_blocks); + mir.cfg.basic_blocks.extend_from_slice(&new_blocks); } } diff --git a/src/librustc_mir/transform/mod.rs b/src/librustc_mir/transform/mod.rs index 339dcdec06080..56be0223be4f5 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 acs_propagate; diff --git a/src/librustc_mir/transform/promote_consts.rs b/src/librustc_mir/transform/promote_consts.rs index d81c4e2dfb68e..ca59340fe6362 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 b9eec6ecd9c58..52d45b8ac6650 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}; @@ -336,7 +336,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 d008918026ab8..e07b01678fc17 100644 --- a/src/librustc_mir/transform/simplify_cfg.rs +++ b/src/librustc_mir/transform/simplify_cfg.rs @@ -9,17 +9,15 @@ // except according to those terms. use rustc_data_structures::bitvec::BitVector; -use rustc::middle::const_val::ConstVal; use rustc::ty::TyCtxt; use rustc::mir::repr::*; use rustc::mir::transform::{MirPass, MirSource, Pass}; +use rustc::mir::traversal; use pretty; use std::mem; use super::remove_dead_blocks::RemoveDeadBlocks; -use traversal; - pub struct SimplifyCfg; impl SimplifyCfg { @@ -30,14 +28,13 @@ impl SimplifyCfg { impl<'tcx> MirPass<'tcx> for SimplifyCfg { fn run_pass<'a>(&mut self, tcx: TyCtxt<'a, 'tcx, 'tcx>, src: MirSource, mir: &mut Mir<'tcx>) { - simplify_branches(mir); RemoveDeadBlocks.run_pass(tcx, src, mir); merge_consecutive_blocks(mir); RemoveDeadBlocks.run_pass(tcx, src, mir); pretty::dump_mir(tcx, "simplify_cfg", &0, src, mir, None); // FIXME: Should probably be moved into some kind of pass manager - mir.basic_blocks.shrink_to_fit(); + mir.cfg.basic_blocks.shrink_to_fit(); } } @@ -45,7 +42,7 @@ impl Pass for SimplifyCfg {} fn merge_consecutive_blocks(mir: &mut Mir) { // Build the precedecessor map for the MIR - let mut pred_count = vec![0u32; mir.basic_blocks.len()]; + let mut pred_count = vec![0u32; mir.cfg.basic_blocks.len()]; for (_, data) in traversal::preorder(mir) { if let Some(ref term) = data.terminator { for &tgt in term.successors().iter() { @@ -56,7 +53,7 @@ fn merge_consecutive_blocks(mir: &mut Mir) { loop { let mut changed = false; - let mut seen = BitVector::new(mir.basic_blocks.len()); + let mut seen = BitVector::new(mir.cfg.basic_blocks.len()); let mut worklist = vec![START_BLOCK]; while let Some(bb) = worklist.pop() { // Temporarily take ownership of the terminator we're modifying to keep borrowck happy @@ -154,52 +151,3 @@ fn final_target(mir: &Mir, mut target: BasicBlock) -> Option { Some(target) } - -fn simplify_branches(mir: &mut Mir) { - loop { - 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::Assert { target, cond: Operand::Constant(Constant { - literal: Literal::Value { - value: ConstVal::Bool(cond) - }, .. - }), expected, .. } if cond == expected => { - changed = true; - TerminatorKind::Goto { target: target } - } - - TerminatorKind::SwitchInt { ref targets, .. } if targets.len() == 1 => { - changed = true; - TerminatorKind::Goto { target: targets[0] } - } - _ => continue - } - } - - if !changed { - break; - } - } -} diff --git a/src/librustc_mir/transform/type_check.rs b/src/librustc_mir/transform/type_check.rs index 019ed670d1f83..f3c8b6a7f65dc 100644 --- a/src/librustc_mir/transform/type_check.rs +++ b/src/librustc_mir/transform/type_check.rs @@ -615,7 +615,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/analyze.rs b/src/librustc_trans/mir/analyze.rs index 59143bc01bf7a..9e2e23729315a 100644 --- a/src/librustc_trans/mir/analyze.rs +++ b/src/librustc_trans/mir/analyze.rs @@ -14,8 +14,8 @@ use rustc_data_structures::bitvec::BitVector; use rustc::mir::repr as mir; use rustc::mir::repr::TerminatorKind; +use rustc::mir::traversal; use rustc::mir::visit::{Visitor, LvalueContext}; -use rustc_mir::traversal; use common::{self, Block, BlockAndBuilder}; use super::rvalue; diff --git a/src/librustc_trans/mir/mod.rs b/src/librustc_trans/mir/mod.rs index d1206550b13d6..9f91cb445d611 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};