Skip to content

[WIP] [MIR] Generic lattice-based dataflow framework, rebased #34164

New issue

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

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

Already on GitHub? Sign in to your account

Closed
wants to merge 24 commits into from
Closed
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
bb183e2
Distinct CFG type for MIR, traversals to librustc
nagisa May 14, 2016
d7a0466
Impl a generic lattice-based DF framework
nagisa May 14, 2016
060d840
Implement a ACS propagation pass
nagisa May 14, 2016
e20d181
Remove use of specialization in Lattice to avoid use of unstable feat…
gereeter May 16, 2016
ea689a0
Remove DataflowPass
gereeter May 16, 2016
c1bb060
Add &self arguments to Transfer and Rewrite and start taking them by …
gereeter May 16, 2016
52eff47
Remove unused and buggy support for dataflow passes that introduce mu…
gereeter May 16, 2016
6413fe6
Let ar_forward generate its own queue
gereeter May 16, 2016
0756739
Change the fully capitalized ACS to the partially capitalized Acs, ma…
gereeter May 16, 2016
a0eebd3
Fix various nits in MIR Dataflow
gereeter May 22, 2016
7cbcb4b
Actually rewrite constants in AcsPropagate
gereeter May 22, 2016
4af1473
Remove some unnecessary `pub`s in AcsPropagate
gereeter May 25, 2016
ad37533
Invalidate values in AcsLattice that are overwritten, either by a wri…
gereeter May 25, 2016
9b3ecda
Temporarily completely disable Backward dataflow to fix unused varian…
gereeter May 25, 2016
b0fbbae
Rewrite AcsLattice to be more correct
gereeter May 27, 2016
7f1d4c8
Further correctness improvements to AcsRewrite
gereeter May 31, 2016
cfcf7cd
Remove unused import left over from rebase
gereeter Jun 7, 2016
ae0c91c
Properly reset basic blocks optimized based on overspeculative inform…
gereeter Jun 8, 2016
ffc2406
Implement MIR's CFG::swap correctly
gereeter Jun 8, 2016
3bd906c
Mutably update blocks when analyzing instead of creating new ones. ru…
gereeter Jun 8, 2016
07d073e
Fix make tidy
gereeter Jun 8, 2016
282d51d
Also clear the Acs cache on DropAndReplace
gereeter Jun 8, 2016
f4452c8
Use the generic WBottom for AcsLattice
gereeter Jun 8, 2016
c045fa4
Properly implement intersection in AcsLattice::join
gereeter Jun 9, 2016
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/librustc/lib.rs
Original file line number Diff line number Diff line change
@@ -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;
167 changes: 167 additions & 0 deletions src/librustc/mir/cfg.rs
Original file line number Diff line number Diff line change
@@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use mir::repr::*;

use std::ops::{Index, IndexMut};
use syntax::codemap::Span;

#[derive(Clone, RustcEncodable, RustcDecodable)]
pub struct CFG<'tcx> {
pub basic_blocks: Vec<BasicBlockData<'tcx>>,
}

pub struct PredecessorIter(::std::vec::IntoIter<BasicBlock>);
impl Iterator for PredecessorIter {
type Item = BasicBlock;
fn next(&mut self) -> Option<BasicBlock> {
self.0.next()
}
}

pub struct SuccessorIter(::std::vec::IntoIter<BasicBlock>);
impl<'a> Iterator for SuccessorIter {
type Item = BasicBlock;
fn next(&mut self) -> Option<BasicBlock> {
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<BasicBlock> = 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<BasicBlock> for CFG<'tcx> {
type Output = BasicBlockData<'tcx>;

#[inline]
fn index(&self, index: BasicBlock) -> &BasicBlockData<'tcx> {
&self.basic_blocks[index.index()]
}
}

impl<'tcx> IndexMut<BasicBlock> for CFG<'tcx> {
#[inline]
fn index_mut(&mut self, index: BasicBlock) -> &mut BasicBlockData<'tcx> {
&mut self.basic_blocks[index.index()]
}
}

14 changes: 8 additions & 6 deletions src/librustc/mir/repr.rs
Original file line number Diff line number Diff line change
@@ -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<BasicBlockData<'tcx>>,
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<BasicBlock> {
(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>),
408 changes: 408 additions & 0 deletions src/librustc/mir/transform/dataflow.rs

Large diffs are not rendered by default.

125 changes: 125 additions & 0 deletions src/librustc/mir/transform/lattice.rs
Original file line number Diff line number Diff line change
@@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

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<T> {
Top,
Value(T)
}

impl<T: Lattice> Lattice for WTop<T> {
fn bottom() -> Self {
WTop::Value(<T as Lattice>::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)) => <T as Lattice>::join(this, o),
(&mut WTop::Top, _) => false,
(this, &WTop::Top) => {
*this = WTop::Top;
true
}
}
}
}

impl<T: Debug> Debug for WTop<T> {
fn fmt(&self, f: &mut Formatter) -> ::std::fmt::Result {
match *self {
WTop::Top => f.write_str("⊤"),
WTop::Value(ref t) => <T as Debug>::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<T> {
Bottom,
Value(T)
}

impl<T: Lattice> Lattice for WBottom<T> {
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)) =>
<T as Lattice>::join(this, o),
(_, &WBottom::Bottom) => false,
(this, o) => {
*this = o.clone();
true
}
}
}

}

impl<T: Debug> Debug for WBottom<T> {
fn fmt(&self, f: &mut Formatter) -> ::std::fmt::Result {
match *self {
WBottom::Bottom => f.write_str("⊥"),
WBottom::Value(ref t) => <T as Debug>::fmt(t, f)
}
}
}

/// Extend the type with both bottom and top points.
type WTopBottom<T> = WTop<WBottom<T>>;

impl<K, T, H> Lattice for HashMap<K, T, H>
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
}
}


Original file line number Diff line number Diff line change
@@ -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;
Original file line number Diff line number Diff line change
@@ -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()
};

6 changes: 5 additions & 1 deletion src/librustc/mir/visit.rs
Original file line number Diff line number Diff line change
@@ -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);
4 changes: 2 additions & 2 deletions src/librustc_borrowck/borrowck/mir/dataflow/mod.rs
Original file line number Diff line number Diff line change
@@ -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<BD::Idx>) {
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));
12 changes: 6 additions & 6 deletions src/librustc_borrowck/borrowck/mir/patch.rs
Original file line number Diff line number Diff line change
@@ -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)
};
16 changes: 16 additions & 0 deletions src/librustc_data_structures/bitvec.rs
Original file line number Diff line number Diff line change
@@ -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;
3 changes: 2 additions & 1 deletion src/librustc_driver/driver.rs
Original file line number Diff line number Diff line change
@@ -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);
85 changes: 7 additions & 78 deletions src/librustc_mir/build/cfg.rs
Original file line number Diff line number Diff line change
@@ -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,
});
}
}
6 changes: 1 addition & 5 deletions src/librustc_mir/build/mod.rs
Original file line number Diff line number Diff line change
@@ -57,10 +57,6 @@ pub struct Builder<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
cached_return_block: Option<BasicBlock>,
}

struct CFG<'tcx> {
basic_blocks: Vec<BasicBlockData<'tcx>>,
}

/// 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,
3 changes: 2 additions & 1 deletion src/librustc_mir/build/scope.rs
Original file line number Diff line number Diff line change
@@ -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};
1 change: 0 additions & 1 deletion src/librustc_mir/lib.rs
Original file line number Diff line number Diff line change
@@ -49,4 +49,3 @@ mod hair;
pub mod mir_map;
pub mod pretty;
pub mod transform;
pub mod traversal;
314 changes: 314 additions & 0 deletions src/librustc_mir/transform/acs_propagate.rs
Original file line number Diff line number Diff line change
@@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! 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<Either<Lvalue, Constant>>
//!
//! 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<Lvalue<'tcx>, 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> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It'd be reasonable to just add this as a method on Lvalue since other transformations will almost certainly find it useful as well.

while let &Lvalue::Projection(ref proj) = lval {
lval = &proj.base;
}
lval
}

fn invalidate<'tcx>(map: &mut FnvHashMap<Lvalue<'tcx>, 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<AcsLattice<'tcx>>;

fn stmt(&self, s: &Statement<'tcx>, lat: WBottom<AcsLattice<'tcx>>) -> WBottom<AcsLattice<'tcx>> {
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<AcsLattice<'tcx>>) -> Vec<WBottom<AcsLattice<'tcx>>> {
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<AcsLattice<'tcx>>> for AliasRewrite {
fn stmt(&self, s: &Statement<'tcx>, l: &WBottom<AcsLattice<'tcx>>, _: &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<AcsLattice<'tcx>>, _: &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<Lvalue<'tcx>, 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<AcsLattice<'tcx>>> for ConstRewrite {
fn stmt(&self, s: &Statement<'tcx>, l: &WBottom<AcsLattice<'tcx>>, _: &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<AcsLattice<'tcx>>, _: &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<Lvalue<'tcx>, 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
}
}
}
9 changes: 4 additions & 5 deletions src/librustc_mir/transform/add_call_guards.rs
Original file line number Diff line number Diff line change
@@ -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);
}
}

1 change: 1 addition & 0 deletions src/librustc_mir/transform/mod.rs
Original file line number Diff line number Diff line change
@@ -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;
16 changes: 8 additions & 8 deletions src/librustc_mir/transform/promote_consts.rs
Original file line number Diff line number Diff line change
@@ -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), _) => {
4 changes: 2 additions & 2 deletions src/librustc_mir/transform/qualify_consts.rs
Original file line number Diff line number Diff line change
@@ -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());
8 changes: 4 additions & 4 deletions src/librustc_mir/transform/remove_dead_blocks.rs
Original file line number Diff line number Diff line change
@@ -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);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I prefer having RemoveDeadBlocks being a "GC" pass and doing the swapping "manually", with other passes not removing BBs.

}
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() {
60 changes: 4 additions & 56 deletions src/librustc_mir/transform/simplify_cfg.rs
Original file line number Diff line number Diff line change
@@ -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,22 +28,21 @@ 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();
}
}

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<BasicBlock> {

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;
}
}
}
2 changes: 1 addition & 1 deletion src/librustc_mir/transform/type_check.rs
Original file line number Diff line number Diff line change
@@ -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;
2 changes: 1 addition & 1 deletion src/librustc_trans/mir/analyze.rs
Original file line number Diff line number Diff line change
@@ -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;

2 changes: 1 addition & 1 deletion src/librustc_trans/mir/mod.rs
Original file line number Diff line number Diff line change
@@ -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};