diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs
index 301c6e3dd1c4a..fc2d68d7262e6 100644
--- a/src/librustc/lib.rs
+++ b/src/librustc/lib.rs
@@ -28,6 +28,7 @@
 #![feature(box_syntax)]
 #![feature(collections)]
 #![feature(const_fn)]
+#![feature(core_intrinsics)]
 #![feature(enumset)]
 #![feature(iter_arith)]
 #![feature(libc)]
@@ -102,6 +103,7 @@ pub mod middle {
 }
 
 pub mod mir {
+    mod cache;
     pub mod repr;
     pub mod tcx;
     pub mod visit;
diff --git a/src/librustc/mir/cache.rs b/src/librustc/mir/cache.rs
new file mode 100644
index 0000000000000..138fed2d64e23
--- /dev/null
+++ b/src/librustc/mir/cache.rs
@@ -0,0 +1,69 @@
+// 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::cell::{Ref, RefCell};
+use rustc_data_structures::indexed_vec::IndexVec;
+
+use mir::repr::{Mir, BasicBlock};
+
+use rustc_serialize as serialize;
+
+#[derive(Clone)]
+pub struct Cache {
+    predecessors: RefCell<Option<IndexVec<BasicBlock, Vec<BasicBlock>>>>
+}
+
+
+impl serialize::Encodable for Cache {
+    fn encode<S: serialize::Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
+        serialize::Encodable::encode(&(), s)
+    }
+}
+
+impl serialize::Decodable for Cache {
+    fn decode<D: serialize::Decoder>(d: &mut D) -> Result<Self, D::Error> {
+        serialize::Decodable::decode(d).map(|_v: ()| Self::new())
+    }
+}
+
+
+impl Cache {
+    pub fn new() -> Self {
+        Cache {
+            predecessors: RefCell::new(None)
+        }
+    }
+
+    pub fn invalidate(&self) {
+        // FIXME: consider being more fine-grained
+        *self.predecessors.borrow_mut() = None;
+    }
+
+    pub fn predecessors(&self, mir: &Mir) -> Ref<IndexVec<BasicBlock, Vec<BasicBlock>>> {
+        if self.predecessors.borrow().is_none() {
+            *self.predecessors.borrow_mut() = Some(calculate_predecessors(mir));
+        }
+
+        Ref::map(self.predecessors.borrow(), |p| p.as_ref().unwrap())
+    }
+}
+
+fn calculate_predecessors(mir: &Mir) -> IndexVec<BasicBlock, Vec<BasicBlock>> {
+    let mut result = IndexVec::from_elem(vec![], mir.basic_blocks());
+    for (bb, data) in mir.basic_blocks().iter_enumerated() {
+        if let Some(ref term) = data.terminator {
+            for &tgt in term.successors().iter() {
+                result[tgt].push(bb);
+            }
+        }
+    }
+
+    result
+}
diff --git a/src/librustc/mir/repr.rs b/src/librustc/mir/repr.rs
index 7aaaf38400e87..03ae91fefb925 100644
--- a/src/librustc/mir/repr.rs
+++ b/src/librustc/mir/repr.rs
@@ -11,6 +11,7 @@
 use graphviz::IntoCow;
 use middle::const_val::ConstVal;
 use rustc_const_math::{ConstUsize, ConstInt, ConstMathErr};
+use rustc_data_structures::indexed_vec::{IndexVec, Idx};
 use hir::def_id::DefId;
 use ty::subst::Substs;
 use ty::{self, AdtDef, ClosureSubsts, FnOutput, Region, Ty};
@@ -19,43 +20,70 @@ use rustc_back::slice;
 use hir::InlineAsm;
 use std::ascii;
 use std::borrow::{Cow};
+use std::cell::Ref;
 use std::fmt::{self, Debug, Formatter, Write};
 use std::{iter, u32};
 use std::ops::{Index, IndexMut};
 use syntax::ast::{self, Name};
 use syntax::codemap::Span;
 
+use super::cache::Cache;
+
+macro_rules! newtype_index {
+    ($name:ident, $debug_name:expr) => (
+        #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord,
+         RustcEncodable, RustcDecodable)]
+        pub struct $name(u32);
+
+        impl Idx for $name {
+            fn new(value: usize) -> Self {
+                assert!(value < (u32::MAX) as usize);
+                $name(value as u32)
+            }
+            fn index(self) -> usize {
+                self.0 as usize
+            }
+        }
+
+        impl Debug for $name {
+            fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
+                write!(fmt, "{}{}", $debug_name, self.0)
+            }
+        }
+    )
+}
+
 /// Lowered representation of a single function.
 #[derive(Clone, RustcEncodable, RustcDecodable)]
 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>>,
+    basic_blocks: IndexVec<BasicBlock, BasicBlockData<'tcx>>,
 
     /// List of visibility (lexical) scopes; these are referenced by statements
     /// and used (eventually) for debuginfo. Indexed by a `VisibilityScope`.
-    pub visibility_scopes: Vec<VisibilityScopeData>,
+    pub visibility_scopes: IndexVec<VisibilityScope, VisibilityScopeData>,
 
     /// Rvalues promoted from this function, such as borrows of constants.
     /// Each of them is the Mir of a constant with the fn's type parameters
     /// in scope, but no vars or args and a separate set of temps.
-    pub promoted: Vec<Mir<'tcx>>,
+    pub promoted: IndexVec<Promoted, Mir<'tcx>>,
 
     /// Return type of the function.
     pub return_ty: FnOutput<'tcx>,
 
     /// Variables: these are stack slots corresponding to user variables. They may be
     /// assigned many times.
-    pub var_decls: Vec<VarDecl<'tcx>>,
+    pub var_decls: IndexVec<Var, VarDecl<'tcx>>,
 
     /// Args: these are stack slots corresponding to the input arguments.
-    pub arg_decls: Vec<ArgDecl<'tcx>>,
+    pub arg_decls: IndexVec<Arg, ArgDecl<'tcx>>,
 
     /// Temp declarations: stack slots that for temporaries created by
     /// the compiler. These are assigned once, but they are not SSA
     /// values in that it is possible to borrow them and mutate them
     /// through the resulting reference.
-    pub temp_decls: Vec<TempDecl<'tcx>>,
+    pub temp_decls: IndexVec<Temp, TempDecl<'tcx>>,
 
     /// Names and capture modes of all the closure upvars, assuming
     /// the first argument is either the closure or a reference to it.
@@ -63,24 +91,58 @@ pub struct Mir<'tcx> {
 
     /// A span representing this MIR, for error reporting
     pub span: Span,
+
+    /// A cache for various calculations
+    cache: Cache
 }
 
 /// where execution begins
 pub const START_BLOCK: BasicBlock = BasicBlock(0);
 
 impl<'tcx> Mir<'tcx> {
-    pub fn all_basic_blocks(&self) -> Vec<BasicBlock> {
-        (0..self.basic_blocks.len())
-            .map(|i| BasicBlock::new(i))
-            .collect()
+    pub fn new(basic_blocks: IndexVec<BasicBlock, BasicBlockData<'tcx>>,
+               visibility_scopes: IndexVec<VisibilityScope, VisibilityScopeData>,
+               promoted: IndexVec<Promoted, Mir<'tcx>>,
+               return_ty: FnOutput<'tcx>,
+               var_decls: IndexVec<Var, VarDecl<'tcx>>,
+               arg_decls: IndexVec<Arg, ArgDecl<'tcx>>,
+               temp_decls: IndexVec<Temp, TempDecl<'tcx>>,
+               upvar_decls: Vec<UpvarDecl>,
+               span: Span) -> Self
+    {
+        Mir {
+            basic_blocks: basic_blocks,
+            visibility_scopes: visibility_scopes,
+            promoted: promoted,
+            return_ty: return_ty,
+            var_decls: var_decls,
+            arg_decls: arg_decls,
+            temp_decls: temp_decls,
+            upvar_decls: upvar_decls,
+            span: span,
+            cache: Cache::new()
+        }
+    }
+
+    #[inline]
+    pub fn basic_blocks(&self) -> &IndexVec<BasicBlock, BasicBlockData<'tcx>> {
+        &self.basic_blocks
     }
 
-    pub fn basic_block_data(&self, bb: BasicBlock) -> &BasicBlockData<'tcx> {
-        &self.basic_blocks[bb.index()]
+    #[inline]
+    pub fn basic_blocks_mut(&mut self) -> &mut IndexVec<BasicBlock, BasicBlockData<'tcx>> {
+        self.cache.invalidate();
+        &mut self.basic_blocks
     }
 
-    pub fn basic_block_data_mut(&mut self, bb: BasicBlock) -> &mut BasicBlockData<'tcx> {
-        &mut self.basic_blocks[bb.index()]
+    #[inline]
+    pub fn predecessors(&self) -> Ref<IndexVec<BasicBlock, Vec<BasicBlock>>> {
+        self.cache.predecessors(self)
+    }
+
+    #[inline]
+    pub fn predecessors_for(&self, bb: BasicBlock) -> Ref<Vec<BasicBlock>> {
+        Ref::map(self.predecessors(), |p| &p[bb])
     }
 }
 
@@ -89,14 +151,14 @@ impl<'tcx> Index<BasicBlock> for Mir<'tcx> {
 
     #[inline]
     fn index(&self, index: BasicBlock) -> &BasicBlockData<'tcx> {
-        self.basic_block_data(index)
+        &self.basic_blocks()[index]
     }
 }
 
 impl<'tcx> IndexMut<BasicBlock> for Mir<'tcx> {
     #[inline]
     fn index_mut(&mut self, index: BasicBlock) -> &mut BasicBlockData<'tcx> {
-        self.basic_block_data_mut(index)
+        &mut self.basic_blocks_mut()[index]
     }
 }
 
@@ -231,31 +293,7 @@ pub struct UpvarDecl {
 ///////////////////////////////////////////////////////////////////////////
 // BasicBlock
 
-/// The index of a particular basic block. The index is into the `basic_blocks`
-/// list of the `Mir`.
-///
-/// (We use a `u32` internally just to save memory.)
-#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord,
-         RustcEncodable, RustcDecodable)]
-pub struct BasicBlock(u32);
-
-impl BasicBlock {
-    pub fn new(index: usize) -> BasicBlock {
-        assert!(index < (u32::MAX as usize));
-        BasicBlock(index as u32)
-    }
-
-    /// Extract the index.
-    pub fn index(self) -> usize {
-        self.0 as usize
-    }
-}
-
-impl Debug for BasicBlock {
-    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
-        write!(fmt, "bb{}", self.0)
-    }
-}
+newtype_index!(BasicBlock, "bb");
 
 ///////////////////////////////////////////////////////////////////////////
 // BasicBlockData and Terminator
@@ -336,6 +374,9 @@ pub enum TerminatorKind<'tcx> {
     /// have been filled in by now. This should occur at most once.
     Return,
 
+    /// Indicates a terminator that can never be reached.
+    Unreachable,
+
     /// Drop the Lvalue
     Drop {
         location: Lvalue<'tcx>,
@@ -394,6 +435,7 @@ impl<'tcx> TerminatorKind<'tcx> {
             SwitchInt { targets: ref b, .. } => b[..].into_cow(),
             Resume => (&[]).into_cow(),
             Return => (&[]).into_cow(),
+            Unreachable => (&[]).into_cow(),
             Call { destination: Some((_, t)), cleanup: Some(c), .. } => vec![t, c].into_cow(),
             Call { destination: Some((_, ref t)), cleanup: None, .. } =>
                 slice::ref_slice(t).into_cow(),
@@ -423,6 +465,7 @@ impl<'tcx> TerminatorKind<'tcx> {
             SwitchInt { targets: ref mut b, .. } => b.iter_mut().collect(),
             Resume => Vec::new(),
             Return => Vec::new(),
+            Unreachable => Vec::new(),
             Call { destination: Some((_, ref mut t)), cleanup: Some(ref mut c), .. } => vec![t, c],
             Call { destination: Some((_, ref mut t)), cleanup: None, .. } => vec![t],
             Call { destination: None, cleanup: Some(ref mut c), .. } => vec![c],
@@ -501,6 +544,7 @@ impl<'tcx> TerminatorKind<'tcx> {
             SwitchInt { discr: ref lv, .. } => write!(fmt, "switchInt({:?})", lv),
             Return => write!(fmt, "return"),
             Resume => write!(fmt, "resume"),
+            Unreachable => write!(fmt, "unreachable"),
             Drop { ref location, .. } => write!(fmt, "drop({:?})", location),
             DropAndReplace { ref location, ref value, .. } =>
                 write!(fmt, "replace({:?} <- {:?})", location, value),
@@ -544,7 +588,7 @@ impl<'tcx> TerminatorKind<'tcx> {
     pub fn fmt_successor_labels(&self) -> Vec<Cow<'static, str>> {
         use self::TerminatorKind::*;
         match *self {
-            Return | Resume => vec![],
+            Return | Resume | Unreachable => vec![],
             Goto { .. } => vec!["".into()],
             If { .. } => vec!["true".into(), "false".into()],
             Switch { ref adt_def, .. } => {
@@ -616,19 +660,23 @@ impl<'tcx> Debug for Statement<'tcx> {
 ///////////////////////////////////////////////////////////////////////////
 // Lvalues
 
+newtype_index!(Var, "var");
+newtype_index!(Temp, "tmp");
+newtype_index!(Arg, "arg");
+
 /// A path to a value; something that can be evaluated without
 /// changing or disturbing program state.
 #[derive(Clone, PartialEq, RustcEncodable, RustcDecodable)]
 pub enum Lvalue<'tcx> {
     /// local variable declared by the user
-    Var(u32),
+    Var(Var),
 
     /// temporary introduced during lowering into MIR
-    Temp(u32),
+    Temp(Temp),
 
     /// formal parameter of the function; note that these are NOT the
     /// bindings that the user declares, which are vars
-    Arg(u32),
+    Arg(Arg),
 
     /// static or static mut variable
     Static(DefId),
@@ -696,20 +744,7 @@ pub type LvalueProjection<'tcx> = Projection<'tcx, Lvalue<'tcx>, Operand<'tcx>>;
 /// and the index is an operand.
 pub type LvalueElem<'tcx> = ProjectionElem<'tcx, Operand<'tcx>>;
 
-/// Index into the list of fields found in a `VariantDef`
-#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)]
-pub struct Field(u32);
-
-impl Field {
-    pub fn new(value: usize) -> Field {
-        assert!(value < (u32::MAX) as usize);
-        Field(value as u32)
-    }
-
-    pub fn index(self) -> usize {
-        self.0 as usize
-    }
-}
+newtype_index!(Field, "field");
 
 impl<'tcx> Lvalue<'tcx> {
     pub fn field(self, f: Field, ty: Ty<'tcx>) -> Lvalue<'tcx> {
@@ -737,12 +772,9 @@ impl<'tcx> Debug for Lvalue<'tcx> {
         use self::Lvalue::*;
 
         match *self {
-            Var(id) =>
-                write!(fmt, "var{:?}", id),
-            Arg(id) =>
-                write!(fmt, "arg{:?}", id),
-            Temp(id) =>
-                write!(fmt, "tmp{:?}", id),
+            Var(id) => write!(fmt, "{:?}", id),
+            Arg(id) => write!(fmt, "{:?}", id),
+            Temp(id) => write!(fmt, "{:?}", id),
             Static(def_id) =>
                 write!(fmt, "{}", ty::tls::with(|tcx| tcx.item_path_str(def_id))),
             ReturnPointer =>
@@ -777,38 +809,8 @@ impl<'tcx> Debug for Lvalue<'tcx> {
 ///////////////////////////////////////////////////////////////////////////
 // Scopes
 
-impl Index<VisibilityScope> for Vec<VisibilityScopeData> {
-    type Output = VisibilityScopeData;
-
-    #[inline]
-    fn index(&self, index: VisibilityScope) -> &VisibilityScopeData {
-        &self[index.index()]
-    }
-}
-
-impl IndexMut<VisibilityScope> for Vec<VisibilityScopeData> {
-    #[inline]
-    fn index_mut(&mut self, index: VisibilityScope) -> &mut VisibilityScopeData {
-        &mut self[index.index()]
-    }
-}
-
-#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, RustcEncodable, RustcDecodable)]
-pub struct VisibilityScope(u32);
-
-/// The visibility scope all arguments go into.
-pub const ARGUMENT_VISIBILITY_SCOPE: VisibilityScope = VisibilityScope(0);
-
-impl VisibilityScope {
-    pub fn new(index: usize) -> VisibilityScope {
-        assert!(index < (u32::MAX as usize));
-        VisibilityScope(index as u32)
-    }
-
-    pub fn index(self) -> usize {
-        self.0 as usize
-    }
-}
+newtype_index!(VisibilityScope, "scope");
+pub const ARGUMENT_VISIBILITY_SCOPE : VisibilityScope = VisibilityScope(0);
 
 #[derive(Clone, Debug, RustcEncodable, RustcDecodable)]
 pub struct VisibilityScopeData {
@@ -1080,6 +1082,8 @@ impl<'tcx> Debug for TypedConstVal<'tcx> {
     }
 }
 
+newtype_index!(Promoted, "promoted");
+
 #[derive(Clone, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)]
 pub enum Literal<'tcx> {
     Item {
@@ -1091,7 +1095,7 @@ pub enum Literal<'tcx> {
     },
     Promoted {
         // Index into the `promoted` vector of `Mir`.
-        index: usize
+        index: Promoted
     },
 }
 
@@ -1115,7 +1119,7 @@ impl<'tcx> Debug for Literal<'tcx> {
                 fmt_const_val(fmt, value)
             }
             Promoted { index } => {
-                write!(fmt, "promoted{}", index)
+                write!(fmt, "{:?}", index)
             }
         }
     }
diff --git a/src/librustc/mir/tcx.rs b/src/librustc/mir/tcx.rs
index 270e33c48f1a0..e3905c39daa9c 100644
--- a/src/librustc/mir/tcx.rs
+++ b/src/librustc/mir/tcx.rs
@@ -154,11 +154,11 @@ impl<'a, 'gcx, 'tcx> Mir<'tcx> {
     {
         match *lvalue {
             Lvalue::Var(index) =>
-                LvalueTy::Ty { ty: self.var_decls[index as usize].ty },
+                LvalueTy::Ty { ty: self.var_decls[index].ty },
             Lvalue::Temp(index) =>
-                LvalueTy::Ty { ty: self.temp_decls[index as usize].ty },
+                LvalueTy::Ty { ty: self.temp_decls[index].ty },
             Lvalue::Arg(index) =>
-                LvalueTy::Ty { ty: self.arg_decls[index as usize].ty },
+                LvalueTy::Ty { ty: self.arg_decls[index].ty },
             Lvalue::Static(def_id) =>
                 LvalueTy::Ty { ty: tcx.lookup_item_type(def_id).ty },
             Lvalue::ReturnPointer =>
diff --git a/src/librustc/mir/transform.rs b/src/librustc/mir/transform.rs
index 828a48532a2fc..f17984d380459 100644
--- a/src/librustc/mir/transform.rs
+++ b/src/librustc/mir/transform.rs
@@ -17,6 +17,8 @@ use mir::repr::Mir;
 use ty::TyCtxt;
 use syntax::ast::NodeId;
 
+use std::fmt;
+
 /// Where a specific Mir comes from.
 #[derive(Debug, Copy, Clone)]
 pub enum MirSource {
@@ -70,16 +72,34 @@ impl<'a, 'tcx> MirSource {
 
 /// Various information about pass.
 pub trait Pass {
-    // fn name() for printouts of various sorts?
     // fn should_run(Session) to check if pass should run?
     fn dep_node(&self, def_id: DefId) -> DepNode<DefId> {
         DepNode::MirPass(def_id)
     }
+    fn name(&self) -> &str {
+        unsafe { ::std::intrinsics::type_name::<Self>() }
+    }
+    fn disambiguator<'a>(&'a self) -> Option<Box<fmt::Display+'a>> { None }
 }
 
 /// A pass which inspects the whole MirMap.
 pub trait MirMapPass<'tcx>: Pass {
-    fn run_pass<'a>(&mut self, tcx: TyCtxt<'a, 'tcx, 'tcx>, map: &mut MirMap<'tcx>);
+    fn run_pass<'a>(
+        &mut self,
+        tcx: TyCtxt<'a, 'tcx, 'tcx>,
+        map: &mut MirMap<'tcx>,
+        hooks: &mut [Box<for<'s> MirPassHook<'s>>]);
+}
+
+pub trait MirPassHook<'tcx>: Pass {
+    fn on_mir_pass<'a>(
+        &mut self,
+        tcx: TyCtxt<'a, 'tcx, 'tcx>,
+        src: MirSource,
+        mir: &Mir<'tcx>,
+        pass: &Pass,
+        is_after: bool
+    );
 }
 
 /// A pass which inspects Mir of functions in isolation.
@@ -94,16 +114,33 @@ pub trait MirPass<'tcx>: Pass {
 }
 
 impl<'tcx, T: MirPass<'tcx>> MirMapPass<'tcx> for T {
-    fn run_pass<'a>(&mut self, tcx: TyCtxt<'a, 'tcx, 'tcx>, map: &mut MirMap<'tcx>) {
+    fn run_pass<'a>(&mut self,
+                    tcx: TyCtxt<'a, 'tcx, 'tcx>,
+                    map: &mut MirMap<'tcx>,
+                    hooks: &mut [Box<for<'s> MirPassHook<'s>>])
+    {
         for (&id, mir) in &mut map.map {
             let def_id = tcx.map.local_def_id(id);
             let _task = tcx.dep_graph.in_task(self.dep_node(def_id));
 
             let src = MirSource::from_node(tcx, id);
+
+            for hook in &mut *hooks {
+                hook.on_mir_pass(tcx, src, mir, self, false);
+            }
             MirPass::run_pass(self, tcx, src, mir);
+            for hook in &mut *hooks {
+                hook.on_mir_pass(tcx, src, mir, self, true);
+            }
 
             for (i, mir) in mir.promoted.iter_mut().enumerate() {
+                for hook in &mut *hooks {
+                    hook.on_mir_pass(tcx, src, mir, self, false);
+                }
                 self.run_pass_on_promoted(tcx, id, i, mir);
+                for hook in &mut *hooks {
+                    hook.on_mir_pass(tcx, src, mir, self, true);
+                }
             }
         }
     }
@@ -112,6 +149,7 @@ impl<'tcx, T: MirPass<'tcx>> MirMapPass<'tcx> for T {
 /// A manager for MIR passes.
 pub struct Passes {
     passes: Vec<Box<for<'tcx> MirMapPass<'tcx>>>,
+    pass_hooks: Vec<Box<for<'tcx> MirPassHook<'tcx>>>,
     plugin_passes: Vec<Box<for<'tcx> MirMapPass<'tcx>>>
 }
 
@@ -119,6 +157,7 @@ impl<'a, 'tcx> Passes {
     pub fn new() -> Passes {
         let passes = Passes {
             passes: Vec::new(),
+            pass_hooks: Vec::new(),
             plugin_passes: Vec::new()
         };
         passes
@@ -126,10 +165,10 @@ impl<'a, 'tcx> Passes {
 
     pub fn run_passes(&mut self, tcx: TyCtxt<'a, 'tcx, 'tcx>, map: &mut MirMap<'tcx>) {
         for pass in &mut self.plugin_passes {
-            pass.run_pass(tcx, map);
+            pass.run_pass(tcx, map, &mut self.pass_hooks);
         }
         for pass in &mut self.passes {
-            pass.run_pass(tcx, map);
+            pass.run_pass(tcx, map, &mut self.pass_hooks);
         }
     }
 
@@ -137,6 +176,11 @@ impl<'a, 'tcx> Passes {
     pub fn push_pass(&mut self, pass: Box<for<'b> MirMapPass<'b>>) {
         self.passes.push(pass);
     }
+
+    /// Pushes a pass hook.
+    pub fn push_hook(&mut self, hook: Box<for<'b> MirPassHook<'b>>) {
+        self.pass_hooks.push(hook);
+    }
 }
 
 /// Copies the plugin passes.
diff --git a/src/librustc/mir/traversal.rs b/src/librustc/mir/traversal.rs
index c4e4936df9541..1af5123b4df60 100644
--- a/src/librustc/mir/traversal.rs
+++ b/src/librustc/mir/traversal.rs
@@ -11,6 +11,7 @@
 use std::vec;
 
 use rustc_data_structures::bitvec::BitVector;
+use rustc_data_structures::indexed_vec::Idx;
 
 use super::repr::*;
 
@@ -44,7 +45,7 @@ impl<'a, 'tcx> Preorder<'a, 'tcx> {
 
         Preorder {
             mir: mir,
-            visited: BitVector::new(mir.basic_blocks.len()),
+            visited: BitVector::new(mir.basic_blocks().len()),
             worklist: worklist
         }
     }
@@ -63,7 +64,7 @@ impl<'a, 'tcx> Iterator for Preorder<'a, 'tcx> {
                 continue;
             }
 
-            let data = self.mir.basic_block_data(idx);
+            let data = &self.mir[idx];
 
             if let Some(ref term) = data.terminator {
                 for &succ in term.successors().iter() {
@@ -106,12 +107,12 @@ 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.basic_blocks().len()),
             visit_stack: Vec::new()
         };
 
 
-        let data = po.mir.basic_block_data(root);
+        let data = &po.mir[root];
 
         if let Some(ref term) = data.terminator {
             po.visited.insert(root.index());
@@ -185,9 +186,7 @@ impl<'a, 'tcx> Postorder<'a, 'tcx> {
             };
 
             if self.visited.insert(bb.index()) {
-                let data = self.mir.basic_block_data(bb);
-
-                if let Some(ref term) = data.terminator {
+                if let Some(ref term) = self.mir[bb].terminator {
                     let succs = term.successors().into_owned().into_iter();
                     self.visit_stack.push((bb, succs));
                 }
@@ -209,10 +208,7 @@ impl<'a, 'tcx> Iterator for Postorder<'a, 'tcx> {
             self.traverse_successor();
         }
 
-        next.map(|(bb, _)| {
-            let data = self.mir.basic_block_data(bb);
-            (bb, data)
-        })
+        next.map(|(bb, _)| (bb, &self.mir[bb]))
     }
 }
 
@@ -278,9 +274,6 @@ impl<'a, 'tcx> Iterator for ReversePostorder<'a, 'tcx> {
         if self.idx == 0 { return None; }
         self.idx -= 1;
 
-        self.blocks.get(self.idx).map(|&bb| {
-            let data = self.mir.basic_block_data(bb);
-            (bb, data)
-        })
+        self.blocks.get(self.idx).map(|&bb| (bb, &self.mir[bb]))
     }
 }
diff --git a/src/librustc/mir/visit.rs b/src/librustc/mir/visit.rs
index 22b3caf6d0c0f..bc45a730c2e21 100644
--- a/src/librustc/mir/visit.rs
+++ b/src/librustc/mir/visit.rs
@@ -15,6 +15,7 @@ use ty::{ClosureSubsts, FnOutput, Region, Ty};
 use mir::repr::*;
 use rustc_const_math::ConstUsize;
 use rustc_data_structures::tuple_slice::TupleSlice;
+use rustc_data_structures::indexed_vec::Idx;
 use syntax::codemap::Span;
 
 // # The MIR Visitor
@@ -251,42 +252,30 @@ macro_rules! make_mir_visitor {
 
             fn super_mir(&mut self,
                          mir: & $($mutability)* Mir<'tcx>) {
-                let Mir {
-                    ref $($mutability)* basic_blocks,
-                    ref $($mutability)* visibility_scopes,
-                    promoted: _, // Visited by passes separately.
-                    ref $($mutability)* return_ty,
-                    ref $($mutability)* var_decls,
-                    ref $($mutability)* arg_decls,
-                    ref $($mutability)* temp_decls,
-                    upvar_decls: _,
-                    ref $($mutability)* span,
-                } = *mir;
-
-                for (index, data) in basic_blocks.into_iter().enumerate() {
+                for index in 0..mir.basic_blocks().len() {
                     let block = BasicBlock::new(index);
-                    self.visit_basic_block_data(block, data);
+                    self.visit_basic_block_data(block, &$($mutability)* mir[block]);
                 }
 
-                for scope in visibility_scopes {
+                for scope in &$($mutability)* mir.visibility_scopes {
                     self.visit_visibility_scope_data(scope);
                 }
 
-                self.visit_fn_output(return_ty);
+                self.visit_fn_output(&$($mutability)* mir.return_ty);
 
-                for var_decl in var_decls {
+                for var_decl in &$($mutability)* mir.var_decls {
                     self.visit_var_decl(var_decl);
                 }
 
-                for arg_decl in arg_decls {
+                for arg_decl in &$($mutability)* mir.arg_decls {
                     self.visit_arg_decl(arg_decl);
                 }
 
-                for temp_decl in temp_decls {
+                for temp_decl in &$($mutability)* mir.temp_decls {
                     self.visit_temp_decl(temp_decl);
                 }
 
-                self.visit_span(span);
+                self.visit_span(&$($mutability)* mir.span);
             }
 
             fn super_basic_block_data(&mut self,
@@ -397,7 +386,8 @@ macro_rules! make_mir_visitor {
                     }
 
                     TerminatorKind::Resume |
-                    TerminatorKind::Return => {
+                    TerminatorKind::Return |
+                    TerminatorKind::Unreachable => {
                     }
 
                     TerminatorKind::Drop { ref $($mutability)* location,
diff --git a/src/librustc_borrowck/Cargo.toml b/src/librustc_borrowck/Cargo.toml
index fbc267aaa6a06..c8a71ea350555 100644
--- a/src/librustc_borrowck/Cargo.toml
+++ b/src/librustc_borrowck/Cargo.toml
@@ -14,4 +14,5 @@ log = { path = "../liblog" }
 syntax = { path = "../libsyntax" }
 graphviz = { path = "../libgraphviz" }
 rustc = { path = "../librustc" }
+rustc_data_structures = { path = "../librustc_data_structures" }
 rustc_mir = { path = "../librustc_mir" }
diff --git a/src/librustc_borrowck/borrowck/mir/dataflow/graphviz.rs b/src/librustc_borrowck/borrowck/mir/dataflow/graphviz.rs
index 63c11fb3545b0..91be50d11f952 100644
--- a/src/librustc_borrowck/borrowck/mir/dataflow/graphviz.rs
+++ b/src/librustc_borrowck/borrowck/mir/dataflow/graphviz.rs
@@ -12,6 +12,7 @@
 
 use syntax::ast::NodeId;
 use rustc::mir::repr::{BasicBlock, Mir};
+use rustc_data_structures::indexed_vec::Idx;
 
 use dot;
 use dot::IntoCow;
@@ -27,7 +28,7 @@ use std::path::Path;
 use super::super::MoveDataParamEnv;
 use super::super::MirBorrowckCtxtPreDataflow;
 use bitslice::bits_to_string;
-use indexed_set::{Idx, IdxSet};
+use indexed_set::{IdxSet};
 use super::{BitDenotation, DataflowState};
 
 impl<O: BitDenotation> DataflowState<O> {
@@ -126,7 +127,7 @@ pub type Node = BasicBlock;
 pub struct Edge { source: BasicBlock, index: usize }
 
 fn outgoing(mir: &Mir, bb: BasicBlock) -> Vec<Edge> {
-    let succ_len = mir.basic_block_data(bb).terminator().successors().len();
+    let succ_len = mir[bb].terminator().successors().len();
     (0..succ_len).map(|index| Edge { source: bb, index: index}).collect()
 }
 
@@ -312,17 +313,20 @@ impl<'a, 'tcx, MWF, P> dot::GraphWalk<'a> for Graph<'a, 'tcx, MWF, P>
     type Node = Node;
     type Edge = Edge;
     fn nodes(&self) -> dot::Nodes<Node> {
-        self.mbcx.mir().all_basic_blocks().into_cow()
+        self.mbcx.mir()
+            .basic_blocks()
+            .indices()
+            .collect::<Vec<_>>()
+            .into_cow()
     }
 
     fn edges(&self) -> dot::Edges<Edge> {
         let mir = self.mbcx.mir();
-        let blocks = mir.all_basic_blocks();
         // base initial capacity on assumption every block has at
         // least one outgoing edge (Which should be true for all
         // blocks but one, the exit-block).
-        let mut edges = Vec::with_capacity(blocks.len());
-        for bb in blocks {
+        let mut edges = Vec::with_capacity(mir.basic_blocks().len());
+        for bb in mir.basic_blocks().indices() {
             let outgoing = outgoing(mir, bb);
             edges.extend(outgoing.into_iter());
         }
@@ -335,6 +339,6 @@ impl<'a, 'tcx, MWF, P> dot::GraphWalk<'a> for Graph<'a, 'tcx, MWF, P>
 
     fn target(&self, edge: &Edge) -> Node {
         let mir = self.mbcx.mir();
-        mir.basic_block_data(edge.source).terminator().successors()[edge.index]
+        mir[edge.source].terminator().successors()[edge.index]
     }
 }
diff --git a/src/librustc_borrowck/borrowck/mir/dataflow/impls.rs b/src/librustc_borrowck/borrowck/mir/dataflow/impls.rs
index e3435ed990506..932b748520170 100644
--- a/src/librustc_borrowck/borrowck/mir/dataflow/impls.rs
+++ b/src/librustc_borrowck/borrowck/mir/dataflow/impls.rs
@@ -10,6 +10,7 @@
 
 use rustc::ty::TyCtxt;
 use rustc::mir::repr::{self, Mir};
+use rustc_data_structures::indexed_vec::Idx;
 
 use super::super::gather_moves::{Location};
 use super::super::gather_moves::{MoveOutIndex, MovePathIndex};
@@ -23,7 +24,7 @@ use super::{BitDenotation, BlockSets, DataflowOperator};
 
 use bitslice::BitSlice; // adds set_bit/get_bit to &[usize] bitvector rep.
 use bitslice::{BitwiseOperator};
-use indexed_set::{Idx, IdxSet};
+use indexed_set::{IdxSet};
 
 // Dataflow analyses are built upon some interpretation of the
 // bitvectors attached to each basic block, represented via a
@@ -425,7 +426,7 @@ impl<'a, 'tcx> BitDenotation for MovingOutStatements<'a, 'tcx> {
                         bb: repr::BasicBlock,
                         idx: usize) {
         let (tcx, mir, move_data) = (self.tcx, self.mir, &ctxt.move_data);
-        let stmt = &mir.basic_block_data(bb).statements[idx];
+        let stmt = &mir[bb].statements[idx];
         let loc_map = &move_data.loc_map;
         let path_map = &move_data.path_map;
         let rev_lookup = &move_data.rev_lookup;
@@ -451,7 +452,7 @@ impl<'a, 'tcx> BitDenotation for MovingOutStatements<'a, 'tcx> {
                                      move_data,
                                      move_path_index,
                                      |mpi| for moi in &path_map[mpi] {
-                                         assert!(moi.idx() < bits_per_block);
+                                         assert!(moi.index() < bits_per_block);
                                          sets.kill_set.add(&moi);
                                      });
             }
@@ -465,14 +466,14 @@ impl<'a, 'tcx> BitDenotation for MovingOutStatements<'a, 'tcx> {
                          statements_len: usize)
     {
         let (mir, move_data) = (self.mir, &ctxt.move_data);
-        let term = mir.basic_block_data(bb).terminator.as_ref().unwrap();
+        let term = mir[bb].terminator();
         let loc_map = &move_data.loc_map;
         let loc = Location { block: bb, index: statements_len };
         debug!("terminator {:?} at loc {:?} moves out of move_indexes {:?}",
                term, loc, &loc_map[loc]);
         let bits_per_block = self.bits_per_block(ctxt);
         for move_index in &loc_map[loc] {
-            assert!(move_index.idx() < bits_per_block);
+            assert!(move_index.index() < bits_per_block);
             zero_to_one(sets.gen_set.words_mut(), *move_index);
         }
     }
@@ -493,14 +494,14 @@ impl<'a, 'tcx> BitDenotation for MovingOutStatements<'a, 'tcx> {
                              move_data,
                              move_path_index,
                              |mpi| for moi in &path_map[mpi] {
-                                 assert!(moi.idx() < bits_per_block);
+                                 assert!(moi.index() < bits_per_block);
                                  in_out.remove(&moi);
                              });
     }
 }
 
 fn zero_to_one(bitvec: &mut [usize], move_index: MoveOutIndex) {
-    let retval = bitvec.set_bit(move_index.idx());
+    let retval = bitvec.set_bit(move_index.index());
     assert!(retval);
 }
 
diff --git a/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs b/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs
index 81655b5e386f6..a9b4de450967c 100644
--- a/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs
+++ b/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs
@@ -8,6 +8,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+use rustc_data_structures::indexed_vec::Idx;
+
 use rustc::ty::TyCtxt;
 use rustc::mir::repr::{self, Mir};
 
@@ -21,7 +23,7 @@ use super::MirBorrowckCtxtPreDataflow;
 use super::MoveDataParamEnv;
 
 use bitslice::{bitwise, BitwiseOperator};
-use indexed_set::{Idx, IdxSet, IdxSetBuf};
+use indexed_set::{IdxSet, IdxSetBuf};
 
 pub use self::sanity_check::sanity_check_via_rustc_peek;
 pub use self::impls::{MaybeInitializedLvals, MaybeUninitializedLvals};
@@ -81,11 +83,10 @@ impl<'a, 'tcx: 'a, BD> DataflowAnalysis<'a, 'tcx, BD>
             self.flow_state.operator.start_block_effect(&self.ctxt, sets);
         }
 
-        for bb in self.mir.all_basic_blocks() {
+        for (bb, data) in self.mir.basic_blocks().iter_enumerated() {
             let &repr::BasicBlockData { ref statements,
                                         ref terminator,
-                                        is_cleanup: _ } =
-                self.mir.basic_block_data(bb);
+                                        is_cleanup: _ } = data;
 
             let sets = &mut self.flow_state.sets.for_block(bb.index());
             for j_stmt in 0..statements.len() {
@@ -112,7 +113,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.basic_blocks().iter().enumerate() {
             let builder = &mut self.builder;
             {
                 let sets = builder.flow_state.sets.for_block(bb_idx);
@@ -396,7 +397,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.basic_blocks().len();
         let num_overall = num_blocks * bits_per_block;
 
         let zeroes = Bits::new(IdxSetBuf::new_empty(num_overall));
@@ -448,7 +449,8 @@ impl<'a, 'tcx: 'a, D> DataflowAnalysis<'a, 'tcx, D>
     {
         match bb_data.terminator().kind {
             repr::TerminatorKind::Return |
-            repr::TerminatorKind::Resume => {}
+            repr::TerminatorKind::Resume |
+            repr::TerminatorKind::Unreachable => {}
             repr::TerminatorKind::Goto { ref target } |
             repr::TerminatorKind::Assert { ref target, cleanup: None, .. } |
             repr::TerminatorKind::Drop { ref target, location: _, unwind: None } |
diff --git a/src/librustc_borrowck/borrowck/mir/dataflow/sanity_check.rs b/src/librustc_borrowck/borrowck/mir/dataflow/sanity_check.rs
index 8c528f10b57ba..c8d3ff01b6c1a 100644
--- a/src/librustc_borrowck/borrowck/mir/dataflow/sanity_check.rs
+++ b/src/librustc_borrowck/borrowck/mir/dataflow/sanity_check.rs
@@ -14,6 +14,7 @@ use syntax::codemap::Span;
 
 use rustc::ty::{self, TyCtxt};
 use rustc::mir::repr::{self, Mir};
+use rustc_data_structures::indexed_vec::Idx;
 
 use super::super::gather_moves::{MovePathIndex};
 use super::super::MoveDataParamEnv;
@@ -49,8 +50,7 @@ pub fn sanity_check_via_rustc_peek<'a, 'tcx, O>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
     // `dataflow::build_sets`. (But note it is doing non-standard
     // stuff, so such generalization may not be realistic.)
 
-    let blocks = mir.all_basic_blocks();
-    'next_block: for bb in blocks {
+    for bb in mir.basic_blocks().indices() {
         each_block(tcx, mir, flow_ctxt, results, bb);
     }
 }
@@ -63,10 +63,9 @@ fn each_block<'a, 'tcx, O>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
     O: BitDenotation<Ctxt=MoveDataParamEnv<'tcx>, Idx=MovePathIndex>
 {
     let move_data = &ctxt.move_data;
-    let bb_data = mir.basic_block_data(bb);
-    let &repr::BasicBlockData { ref statements,
-                                ref terminator,
-                                is_cleanup: _ } = bb_data;
+    let repr::BasicBlockData { ref statements,
+                               ref terminator,
+                               is_cleanup: _ } = mir[bb];
 
     let (args, span) = match is_rustc_peek(tcx, terminator) {
         Some(args_and_span) => args_and_span,
diff --git a/src/librustc_borrowck/borrowck/mir/elaborate_drops.rs b/src/librustc_borrowck/borrowck/mir/elaborate_drops.rs
index b09db70e7b88a..362d6ce2acafc 100644
--- a/src/librustc_borrowck/borrowck/mir/elaborate_drops.rs
+++ b/src/librustc_borrowck/borrowck/mir/elaborate_drops.rs
@@ -22,7 +22,7 @@ use rustc::mir::transform::{Pass, MirPass, MirSource};
 use rustc::middle::const_val::ConstVal;
 use rustc::middle::lang_items;
 use rustc::util::nodemap::FnvHashMap;
-use rustc_mir::pretty;
+use rustc_data_structures::indexed_vec::Idx;
 use syntax::codemap::Span;
 
 use std::fmt;
@@ -65,9 +65,7 @@ impl<'tcx> MirPass<'tcx> for ElaborateDrops {
                 patch: MirPatch::new(mir),
             }.elaborate()
         };
-        pretty::dump_mir(tcx, "elaborate_drops", &0, src, mir, None);
         elaborate_patch.apply(mir);
-        pretty::dump_mir(tcx, "elaborate_drops", &1, src, mir, None);
     }
 }
 
@@ -118,7 +116,7 @@ struct ElaborateDropsCtxt<'a, 'tcx: 'a> {
     env: &'a MoveDataParamEnv<'tcx>,
     flow_inits: DataflowResults<MaybeInitializedLvals<'a, 'tcx>>,
     flow_uninits:  DataflowResults<MaybeUninitializedLvals<'a, 'tcx>>,
-    drop_flags: FnvHashMap<MovePathIndex, u32>,
+    drop_flags: FnvHashMap<MovePathIndex, Temp>,
     patch: MirPatch<'tcx>,
 }
 
@@ -224,8 +222,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> {
 
     fn collect_drop_flags(&mut self)
     {
-        for bb in self.mir.all_basic_blocks() {
-            let data = self.mir.basic_block_data(bb);
+        for (bb, data) in self.mir.basic_blocks().iter_enumerated() {
             let terminator = data.terminator();
             let location = match terminator.kind {
                 TerminatorKind::Drop { ref location, .. } |
@@ -261,8 +258,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> {
 
     fn elaborate_drops(&mut self)
     {
-        for bb in self.mir.all_basic_blocks() {
-            let data = self.mir.basic_block_data(bb);
+        for (bb, data) in self.mir.basic_blocks().iter_enumerated() {
             let loc = Location { block: bb, index: data.statements.len() };
             let terminator = data.terminator();
 
@@ -322,7 +318,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> {
         unwind: Option<BasicBlock>)
     {
         let bb = loc.block;
-        let data = self.mir.basic_block_data(bb);
+        let data = &self.mir[bb];
         let terminator = data.terminator();
 
         let assign = Statement {
@@ -941,8 +937,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> {
     }
 
     fn drop_flags_for_fn_rets(&mut self) {
-        for bb in self.mir.all_basic_blocks() {
-            let data = self.mir.basic_block_data(bb);
+        for (bb, data) in self.mir.basic_blocks().iter_enumerated() {
             if let TerminatorKind::Call {
                 destination: Some((ref lv, tgt)), cleanup: Some(_), ..
             } = data.terminator().kind {
@@ -974,8 +969,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> {
         // drop flags by themselves, to avoid the drop flags being
         // clobbered before they are read.
 
-        for bb in self.mir.all_basic_blocks() {
-            let data = self.mir.basic_block_data(bb);
+        for (bb, data) in self.mir.basic_blocks().iter_enumerated() {
             debug!("drop_flags_for_locs({:?})", data);
             for i in 0..(data.statements.len()+1) {
                 debug!("drop_flag_for_locs: stmt {}", i);
diff --git a/src/librustc_borrowck/borrowck/mir/gather_moves.rs b/src/librustc_borrowck/borrowck/mir/gather_moves.rs
index 1acee8e64a9af..05412216d487c 100644
--- a/src/librustc_borrowck/borrowck/mir/gather_moves.rs
+++ b/src/librustc_borrowck/borrowck/mir/gather_moves.rs
@@ -12,6 +12,7 @@
 use rustc::ty::{FnOutput, TyCtxt};
 use rustc::mir::repr::*;
 use rustc::util::nodemap::FnvHashMap;
+use rustc_data_structures::indexed_vec::{Idx, IndexVec};
 
 use std::cell::{Cell};
 use std::collections::hash_map::Entry;
@@ -20,7 +21,6 @@ use std::iter;
 use std::ops::Index;
 
 use super::abs_domain::{AbstractElem, Lift};
-use indexed_set::{Idx};
 
 // This submodule holds some newtype'd Index wrappers that are using
 // NonZero to ensure that Option<Index> occupies only a single word.
@@ -29,7 +29,7 @@ use indexed_set::{Idx};
 // (which is likely to yield a subtle off-by-one error).
 mod indexes {
     use core::nonzero::NonZero;
-    use indexed_set::Idx;
+    use rustc_data_structures::indexed_vec::Idx;
 
     macro_rules! new_index {
         ($Index:ident) => {
@@ -43,7 +43,7 @@ mod indexes {
                 fn new(idx: usize) -> Self {
                     unsafe { $Index(NonZero::new(idx + 1)) }
                 }
-                fn idx(&self) -> usize {
+                fn index(self) -> usize {
                     *self.0 - 1
                 }
             }
@@ -62,7 +62,7 @@ pub use self::indexes::MoveOutIndex;
 
 impl self::indexes::MoveOutIndex {
     pub fn move_path_index(&self, move_data: &MoveData) -> MovePathIndex {
-        move_data.moves[self.idx()].path
+        move_data.moves[self.index()].path
     }
 }
 
@@ -176,7 +176,7 @@ pub struct PathMap {
 impl Index<MovePathIndex> for PathMap {
     type Output = [MoveOutIndex];
     fn index(&self, index: MovePathIndex) -> &Self::Output {
-        &self.map[index.idx()]
+        &self.map[index.index()]
     }
 }
 
@@ -196,7 +196,7 @@ pub struct MoveOut {
 
 impl fmt::Debug for MoveOut {
     fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
-        write!(fmt, "p{}@{:?}", self.path.idx(), self.source)
+        write!(fmt, "p{}@{:?}", self.path.index(), self.source)
     }
 }
 
@@ -227,14 +227,10 @@ impl<'tcx> MovePathData<'tcx> {
 impl<'tcx> Index<MovePathIndex> for MovePathData<'tcx> {
     type Output = MovePath<'tcx>;
     fn index(&self, i: MovePathIndex) -> &MovePath<'tcx> {
-        &self.move_paths[i.idx()]
+        &self.move_paths[i.index()]
     }
 }
 
-/// MovePathInverseMap maps from a uint in an lvalue-category to the
-/// MovePathIndex for the MovePath for that lvalue.
-type MovePathInverseMap = Vec<Option<MovePathIndex>>;
-
 struct MovePathDataBuilder<'a, 'tcx: 'a> {
     mir: &'a Mir<'tcx>,
     pre_move_paths: Vec<PreMovePath<'tcx>>,
@@ -244,9 +240,9 @@ struct MovePathDataBuilder<'a, 'tcx: 'a> {
 /// Tables mapping from an l-value to its MovePathIndex.
 #[derive(Debug)]
 pub struct MovePathLookup<'tcx> {
-    vars: MovePathInverseMap,
-    temps: MovePathInverseMap,
-    args: MovePathInverseMap,
+    vars: IndexVec<Var, Option<MovePathIndex>>,
+    temps: IndexVec<Temp, Option<MovePathIndex>>,
+    args: IndexVec<Arg, Option<MovePathIndex>>,
 
     /// The move path representing the return value is constructed
     /// lazily when we first encounter it in the input MIR.
@@ -295,15 +291,15 @@ enum LookupKind { Generate, Reuse }
 struct Lookup<T>(LookupKind, T);
 
 impl Lookup<MovePathIndex> {
-    fn idx(&self) -> usize { (self.1).idx() }
+    fn index(&self) -> usize { (self.1).index() }
 }
 
 impl<'tcx> MovePathLookup<'tcx> {
-    fn new() -> Self {
+    fn new(mir: &Mir) -> Self {
         MovePathLookup {
-            vars: vec![],
-            temps: vec![],
-            args: vec![],
+            vars: IndexVec::from_elem(None, &mir.var_decls),
+            temps: IndexVec::from_elem(None, &mir.temp_decls),
+            args: IndexVec::from_elem(None, &mir.arg_decls),
             statics: None,
             return_ptr: None,
             projections: vec![],
@@ -313,15 +309,14 @@ impl<'tcx> MovePathLookup<'tcx> {
 
     fn next_index(next: &mut MovePathIndex) -> MovePathIndex {
         let i = *next;
-        *next = MovePathIndex::new(i.idx() + 1);
+        *next = MovePathIndex::new(i.index() + 1);
         i
     }
 
-    fn lookup_or_generate(vec: &mut Vec<Option<MovePathIndex>>,
-                          idx: u32,
-                          next_index: &mut MovePathIndex) -> Lookup<MovePathIndex> {
-        let idx = idx as usize;
-        vec.fill_to_with(idx, None);
+    fn lookup_or_generate<I: Idx>(vec: &mut IndexVec<I, Option<MovePathIndex>>,
+                                  idx: I,
+                                  next_index: &mut MovePathIndex)
+                                  -> Lookup<MovePathIndex> {
         let entry = &mut vec[idx];
         match *entry {
             None => {
@@ -335,19 +330,19 @@ impl<'tcx> MovePathLookup<'tcx> {
         }
     }
 
-    fn lookup_var(&mut self, var_idx: u32) -> Lookup<MovePathIndex> {
+    fn lookup_var(&mut self, var_idx: Var) -> Lookup<MovePathIndex> {
         Self::lookup_or_generate(&mut self.vars,
                                  var_idx,
                                  &mut self.next_index)
     }
 
-    fn lookup_temp(&mut self, temp_idx: u32) -> Lookup<MovePathIndex> {
+    fn lookup_temp(&mut self, temp_idx: Temp) -> Lookup<MovePathIndex> {
         Self::lookup_or_generate(&mut self.temps,
                                  temp_idx,
                                  &mut self.next_index)
     }
 
-    fn lookup_arg(&mut self, arg_idx: u32) -> Lookup<MovePathIndex> {
+    fn lookup_arg(&mut self, arg_idx: Arg) -> Lookup<MovePathIndex> {
         Self::lookup_or_generate(&mut self.args,
                                  arg_idx,
                                  &mut self.next_index)
@@ -384,8 +379,8 @@ impl<'tcx> MovePathLookup<'tcx> {
                    base: MovePathIndex) -> Lookup<MovePathIndex> {
         let MovePathLookup { ref mut projections,
                              ref mut next_index, .. } = *self;
-        projections.fill_to(base.idx());
-        match projections[base.idx()].entry(proj.elem.lift()) {
+        projections.fill_to(base.index());
+        match projections[base.index()].entry(proj.elem.lift()) {
             Entry::Occupied(ent) => {
                 Lookup(LookupKind::Reuse, *ent.get())
             }
@@ -404,14 +399,14 @@ impl<'tcx> MovePathLookup<'tcx> {
     // unknown l-value; it will simply panic.
     pub fn find(&self, lval: &Lvalue<'tcx>) -> MovePathIndex {
         match *lval {
-            Lvalue::Var(var_idx) => self.vars[var_idx as usize].unwrap(),
-            Lvalue::Temp(temp_idx) => self.temps[temp_idx as usize].unwrap(),
-            Lvalue::Arg(arg_idx) => self.args[arg_idx as usize].unwrap(),
+            Lvalue::Var(var) => self.vars[var].unwrap(),
+            Lvalue::Temp(temp) => self.temps[temp].unwrap(),
+            Lvalue::Arg(arg) => self.args[arg].unwrap(),
             Lvalue::Static(ref _def_id) => self.statics.unwrap(),
             Lvalue::ReturnPointer => self.return_ptr.unwrap(),
             Lvalue::Projection(ref proj) => {
                 let base_index = self.find(&proj.base);
-                self.projections[base_index.idx()][&proj.elem.lift()]
+                self.projections[base_index.index()][&proj.elem.lift()]
             }
         }
     }
@@ -451,7 +446,7 @@ impl<'a, 'tcx> MovePathDataBuilder<'a, 'tcx> {
 
         // `lookup` is either the previously assigned index or a
         // newly-allocated one.
-        debug_assert!(lookup.idx() <= self.pre_move_paths.len());
+        debug_assert!(lookup.index() <= self.pre_move_paths.len());
 
         if let Lookup(LookupKind::Generate, mpi) = lookup {
             let parent;
@@ -482,7 +477,7 @@ impl<'a, 'tcx> MovePathDataBuilder<'a, 'tcx> {
                     let idx = self.move_path_for(&proj.base);
                     parent = Some(idx);
 
-                    let parent_move_path = &mut self.pre_move_paths[idx.idx()];
+                    let parent_move_path = &mut self.pre_move_paths[idx.index()];
 
                     // At last: Swap in the new first_child.
                     sibling = parent_move_path.first_child.get();
@@ -524,9 +519,9 @@ enum StmtKind {
 fn gather_moves<'a, 'tcx>(mir: &Mir<'tcx>, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> MoveData<'tcx> {
     use self::StmtKind as SK;
 
-    let bbs = mir.all_basic_blocks();
-    let mut moves = Vec::with_capacity(bbs.len());
-    let mut loc_map: Vec<_> = iter::repeat(Vec::new()).take(bbs.len()).collect();
+    let bb_count = mir.basic_blocks().len();
+    let mut moves = vec![];
+    let mut loc_map: Vec<_> = iter::repeat(Vec::new()).take(bb_count).collect();
     let mut path_map = Vec::new();
 
     // this is mutable only because we will move it to and fro' the
@@ -535,7 +530,7 @@ fn gather_moves<'a, 'tcx>(mir: &Mir<'tcx>, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> MoveD
     let mut builder = MovePathDataBuilder {
         mir: mir,
         pre_move_paths: Vec::new(),
-        rev_lookup: MovePathLookup::new(),
+        rev_lookup: MovePathLookup::new(mir),
     };
 
     // Before we analyze the program text, we create the MovePath's
@@ -546,22 +541,21 @@ fn gather_moves<'a, 'tcx>(mir: &Mir<'tcx>, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> MoveD
     assert!(mir.var_decls.len() <= ::std::u32::MAX as usize);
     assert!(mir.arg_decls.len() <= ::std::u32::MAX as usize);
     assert!(mir.temp_decls.len() <= ::std::u32::MAX as usize);
-    for var_idx in 0..mir.var_decls.len() {
-        let path_idx = builder.move_path_for(&Lvalue::Var(var_idx as u32));
-        path_map.fill_to(path_idx.idx());
+    for var in mir.var_decls.indices() {
+        let path_idx = builder.move_path_for(&Lvalue::Var(var));
+        path_map.fill_to(path_idx.index());
     }
-    for arg_idx in 0..mir.arg_decls.len() {
-        let path_idx = builder.move_path_for(&Lvalue::Arg(arg_idx as u32));
-        path_map.fill_to(path_idx.idx());
+    for arg in mir.arg_decls.indices() {
+        let path_idx = builder.move_path_for(&Lvalue::Arg(arg));
+        path_map.fill_to(path_idx.index());
     }
-    for temp_idx in 0..mir.temp_decls.len() {
-        let path_idx = builder.move_path_for(&Lvalue::Temp(temp_idx as u32));
-        path_map.fill_to(path_idx.idx());
+    for temp in mir.temp_decls.indices() {
+        let path_idx = builder.move_path_for(&Lvalue::Temp(temp));
+        path_map.fill_to(path_idx.index());
     }
 
-    for bb in bbs {
+    for (bb, bb_data) in mir.basic_blocks().iter_enumerated() {
         let loc_map_bb = &mut loc_map[bb.index()];
-        let bb_data = mir.basic_block_data(bb);
 
         debug_assert!(loc_map_bb.len() == 0);
         let len = bb_data.statements.len();
@@ -585,7 +579,7 @@ fn gather_moves<'a, 'tcx>(mir: &Mir<'tcx>, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> MoveD
                     // Ensure that the path_map contains entries even
                     // if the lvalue is assigned and never read.
                     let assigned_path = bb_ctxt.builder.move_path_for(lval);
-                    bb_ctxt.path_map.fill_to(assigned_path.idx());
+                    bb_ctxt.path_map.fill_to(assigned_path.index());
 
                     match *rval {
                         Rvalue::Use(ref operand) => {
@@ -627,7 +621,9 @@ fn gather_moves<'a, 'tcx>(mir: &Mir<'tcx>, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> MoveD
 
         debug!("gather_moves({:?})", bb_data.terminator());
         match bb_data.terminator().kind {
-            TerminatorKind::Goto { target: _ } | TerminatorKind::Resume => { }
+            TerminatorKind::Goto { target: _ } |
+            TerminatorKind::Resume |
+            TerminatorKind::Unreachable => { }
 
             TerminatorKind::Return => {
                 let source = Location { block: bb,
@@ -679,7 +675,7 @@ fn gather_moves<'a, 'tcx>(mir: &Mir<'tcx>, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> MoveD
             }
             TerminatorKind::DropAndReplace { ref location, ref value, .. } => {
                 let assigned_path = bb_ctxt.builder.move_path_for(location);
-                bb_ctxt.path_map.fill_to(assigned_path.idx());
+                bb_ctxt.path_map.fill_to(assigned_path.index());
 
                 let source = Location { block: bb,
                                         index: bb_data.statements.len() };
@@ -699,7 +695,7 @@ fn gather_moves<'a, 'tcx>(mir: &Mir<'tcx>, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> MoveD
                     // Ensure that the path_map contains entries even
                     // if the lvalue is assigned and never read.
                     let assigned_path = bb_ctxt.builder.move_path_for(destination);
-                    bb_ctxt.path_map.fill_to(assigned_path.idx());
+                    bb_ctxt.path_map.fill_to(assigned_path.index());
 
                     bb_ctxt.builder.create_move_path(destination);
                 }
@@ -729,8 +725,8 @@ fn gather_moves<'a, 'tcx>(mir: &Mir<'tcx>, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> MoveD
         let mut seen: Vec<_> = move_paths.iter().map(|_| false).collect();
         for (j, &MoveOut { ref path, ref source }) in moves.iter().enumerate() {
             debug!("MovePathData moves[{}]: MoveOut {{ path: {:?} = {:?}, source: {:?} }}",
-                   j, path, move_paths[path.idx()], source);
-            seen[path.idx()] = true;
+                   j, path, move_paths[path.index()], source);
+            seen[path.index()] = true;
         }
         for (j, path) in move_paths.iter().enumerate() {
             if !seen[j] {
@@ -767,7 +763,7 @@ impl<'b, 'a: 'b, 'tcx: 'a> BlockContext<'b, 'a, 'tcx> {
 
         let path = self.builder.move_path_for(lval);
         self.moves.push(MoveOut { path: path, source: source.clone() });
-        self.path_map.fill_to(path.idx());
+        self.path_map.fill_to(path.index());
 
         debug!("ctxt: {:?} add consume of lval: {:?} \
                 at index: {:?} \
@@ -775,12 +771,12 @@ impl<'b, 'a: 'b, 'tcx: 'a> BlockContext<'b, 'a, 'tcx> {
                 to loc_map for loc: {:?}",
                stmt_kind, lval, index, path, source);
 
-        debug_assert!(path.idx() < self.path_map.len());
+        debug_assert!(path.index() < self.path_map.len());
         // this is actually a questionable assert; at the very
         // least, incorrect input code can probably cause it to
         // fire.
-        assert!(self.path_map[path.idx()].iter().find(|idx| **idx == index).is_none());
-        self.path_map[path.idx()].push(index);
+        assert!(self.path_map[path.index()].iter().find(|idx| **idx == index).is_none());
+        self.path_map[path.index()].push(index);
 
         debug_assert!(i < self.loc_map_bb.len());
         debug_assert!(self.loc_map_bb[i].iter().find(|idx| **idx == index).is_none());
diff --git a/src/librustc_borrowck/borrowck/mir/mod.rs b/src/librustc_borrowck/borrowck/mir/mod.rs
index 007cde156f40f..270b16fcf49fe 100644
--- a/src/librustc_borrowck/borrowck/mir/mod.rs
+++ b/src/librustc_borrowck/borrowck/mir/mod.rs
@@ -111,7 +111,7 @@ pub fn borrowck_mir<'a, 'tcx: 'a>(
         flow_uninits: flow_uninits,
     };
 
-    for bb in mir.all_basic_blocks() {
+    for bb in mir.basic_blocks().indices() {
         mbcx.process_basic_block(bb);
     }
 
@@ -180,8 +180,8 @@ pub struct MirBorrowckCtxt<'b, 'a: 'b, 'tcx: 'a> {
 
 impl<'b, 'a: 'b, 'tcx: 'a> MirBorrowckCtxt<'b, 'a, 'tcx> {
     fn process_basic_block(&mut self, bb: BasicBlock) {
-        let &BasicBlockData { ref statements, ref terminator, is_cleanup: _ } =
-            self.mir.basic_block_data(bb);
+        let BasicBlockData { ref statements, ref terminator, is_cleanup: _ } =
+            self.mir[bb];
         for stmt in statements {
             self.process_statement(bb, stmt);
         }
@@ -298,8 +298,8 @@ fn drop_flag_effects_for_function_entry<'a, 'tcx, F>(
     where F: FnMut(MovePathIndex, DropFlagState)
 {
     let move_data = &ctxt.move_data;
-    for i in 0..(mir.arg_decls.len() as u32) {
-        let lvalue = repr::Lvalue::Arg(i);
+    for (arg, _) in mir.arg_decls.iter_enumerated() {
+        let lvalue = repr::Lvalue::Arg(arg);
         let move_path_index = move_data.rev_lookup.find(&lvalue);
         on_all_children_bits(tcx, mir, move_data,
                              move_path_index,
@@ -337,8 +337,8 @@ fn drop_flag_effects_for_location<'a, 'tcx, F>(
                              |moi| callback(moi, DropFlagState::Absent))
     }
 
-    let bb = mir.basic_block_data(loc.block);
-    match bb.statements.get(loc.index) {
+    let block = &mir[loc.block];
+    match block.statements.get(loc.index) {
         Some(stmt) => match stmt.kind {
             repr::StatementKind::Assign(ref lvalue, _) => {
                 debug!("drop_flag_effects: assignment {:?}", stmt);
@@ -348,8 +348,8 @@ fn drop_flag_effects_for_location<'a, 'tcx, F>(
             }
         },
         None => {
-            debug!("drop_flag_effects: replace {:?}", bb.terminator());
-            match bb.terminator().kind {
+            debug!("drop_flag_effects: replace {:?}", block.terminator());
+            match block.terminator().kind {
                 repr::TerminatorKind::DropAndReplace { ref location, .. } => {
                     on_all_children_bits(tcx, mir, move_data,
                                          move_data.rev_lookup.find(location),
diff --git a/src/librustc_borrowck/borrowck/mir/patch.rs b/src/librustc_borrowck/borrowck/mir/patch.rs
index b838881251da3..417e719a9dcb9 100644
--- a/src/librustc_borrowck/borrowck/mir/patch.rs
+++ b/src/librustc_borrowck/borrowck/mir/patch.rs
@@ -11,31 +11,28 @@
 use super::gather_moves::Location;
 use rustc::ty::Ty;
 use rustc::mir::repr::*;
-
-use std::iter;
-use std::u32;
+use rustc_data_structures::indexed_vec::{IndexVec, Idx};
 
 /// This struct represents a patch to MIR, which can add
 /// new statements and basic blocks and patch over block
 /// terminators.
 pub struct MirPatch<'tcx> {
-    patch_map: Vec<Option<TerminatorKind<'tcx>>>,
+    patch_map: IndexVec<BasicBlock, Option<TerminatorKind<'tcx>>>,
     new_blocks: Vec<BasicBlockData<'tcx>>,
     new_statements: Vec<(Location, StatementKind<'tcx>)>,
     new_temps: Vec<TempDecl<'tcx>>,
     resume_block: BasicBlock,
-    next_temp: u32,
+    next_temp: usize,
 }
 
 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(),
+            patch_map: IndexVec::from_elem(None, mir.basic_blocks()),
             new_blocks: vec![],
             new_temps: vec![],
             new_statements: vec![],
-            next_temp: mir.temp_decls.len() as u32,
+            next_temp: mir.temp_decls.len(),
             resume_block: START_BLOCK
         };
 
@@ -46,13 +43,12 @@ impl<'tcx> MirPatch<'tcx> {
 
         let mut resume_block = None;
         let mut resume_stmt_block = None;
-        for block in mir.all_basic_blocks() {
-            let data = mir.basic_block_data(block);
-            if let TerminatorKind::Resume = data.terminator().kind {
-                if data.statements.len() > 0 {
-                    resume_stmt_block = Some(block);
+        for (bb, block) in mir.basic_blocks().iter_enumerated() {
+            if let TerminatorKind::Resume = block.terminator().kind {
+                if block.statements.len() > 0 {
+                    resume_stmt_block = Some(bb);
                 } else {
-                    resume_block = Some(block);
+                    resume_block = Some(bb);
                 }
                 break
             }
@@ -83,13 +79,13 @@ impl<'tcx> MirPatch<'tcx> {
     }
 
     pub fn is_patched(&self, bb: BasicBlock) -> bool {
-        self.patch_map[bb.index()].is_some()
+        self.patch_map[bb].is_some()
     }
 
     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.basic_blocks().len()) {
             Some(index) => self.new_blocks[index].statements.len(),
-            None => mir.basic_block_data(bb).statements.len()
+            None => mir[bb].statements.len()
         };
         Location {
             block: bb,
@@ -97,12 +93,11 @@ impl<'tcx> MirPatch<'tcx> {
         }
     }
 
-    pub fn new_temp(&mut self, ty: Ty<'tcx>) -> u32 {
+    pub fn new_temp(&mut self, ty: Ty<'tcx>) -> Temp {
         let index = self.next_temp;
-        assert!(self.next_temp < u32::MAX);
         self.next_temp += 1;
         self.new_temps.push(TempDecl { ty: ty });
-        index
+        Temp::new(index as usize)
     }
 
     pub fn new_block(&mut self, data: BasicBlockData<'tcx>) -> BasicBlock {
@@ -114,9 +109,9 @@ impl<'tcx> MirPatch<'tcx> {
     }
 
     pub fn patch_terminator(&mut self, block: BasicBlock, new: TerminatorKind<'tcx>) {
-        assert!(self.patch_map[block.index()].is_none());
+        assert!(self.patch_map[block].is_none());
         debug!("MirPatch: patch_terminator({:?}, {:?})", block, new);
-        self.patch_map[block.index()] = Some(new);
+        self.patch_map[block] = Some(new);
     }
 
     pub fn add_statement(&mut self, loc: Location, stmt: StatementKind<'tcx>) {
@@ -132,13 +127,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.basic_blocks().len());
+        mir.basic_blocks_mut().extend(self.new_blocks);
         mir.temp_decls.extend(self.new_temps);
-        for (src, patch) in self.patch_map.into_iter().enumerate() {
+        for (src, patch) in self.patch_map.into_iter_enumerated() {
             if let Some(patch) = patch {
                 debug!("MirPatch: patching block {:?}", src);
-                mir.basic_blocks[src].terminator_mut().kind = patch;
+                mir[src].terminator_mut().kind = patch;
             }
         }
 
@@ -156,9 +151,9 @@ impl<'tcx> MirPatch<'tcx> {
                    stmt, loc, delta);
             loc.index += delta;
             let source_info = Self::source_info_for_index(
-                mir.basic_block_data(loc.block), loc
+                &mir[loc.block], loc
             );
-            mir.basic_block_data_mut(loc.block).statements.insert(
+            mir[loc.block].statements.insert(
                 loc.index, Statement {
                     source_info: source_info,
                     kind: stmt
@@ -175,9 +170,9 @@ impl<'tcx> MirPatch<'tcx> {
     }
 
     pub fn source_info_for_location(&self, mir: &Mir, loc: Location) -> SourceInfo {
-        let data = match loc.block.index().checked_sub(mir.basic_blocks.len()) {
+        let data = match loc.block.index().checked_sub(mir.basic_blocks().len()) {
             Some(new) => &self.new_blocks[new],
-            None => mir.basic_block_data(loc.block)
+            None => &mir[loc.block]
         };
         Self::source_info_for_index(data, loc)
     }
diff --git a/src/librustc_borrowck/indexed_set.rs b/src/librustc_borrowck/indexed_set.rs
index 3fee1dbc05660..671aff97d20aa 100644
--- a/src/librustc_borrowck/indexed_set.rs
+++ b/src/librustc_borrowck/indexed_set.rs
@@ -17,13 +17,7 @@ use std::ops::{Deref, DerefMut, Range};
 use bitslice::{BitSlice, Word};
 use bitslice::{bitwise, Union, Subtract};
 
-/// Represents some newtyped `usize` wrapper.
-///
-/// (purpose: avoid mixing indexes for different bitvector domains.)
-pub trait Idx: 'static {
-    fn new(usize) -> Self;
-    fn idx(&self) -> usize;
-}
+use rustc_data_structures::indexed_vec::Idx;
 
 /// Represents a set (or packed family of sets), of some element type
 /// E, where each E is identified by some unique index type `T`.
@@ -120,27 +114,27 @@ impl<T: Idx> IdxSet<T> {
 
     /// Removes `elem` from the set `self`; returns true iff this changed `self`.
     pub fn remove(&mut self, elem: &T) -> bool {
-        self.bits.clear_bit(elem.idx())
+        self.bits.clear_bit(elem.index())
     }
 
     /// Adds `elem` to the set `self`; returns true iff this changed `self`.
     pub fn add(&mut self, elem: &T) -> bool {
-        self.bits.set_bit(elem.idx())
+        self.bits.set_bit(elem.index())
     }
 
     pub fn range(&self, elems: &Range<T>) -> &Self {
-        let elems = elems.start.idx()..elems.end.idx();
+        let elems = elems.start.index()..elems.end.index();
         unsafe { Self::from_slice(&self.bits[elems]) }
     }
 
     pub fn range_mut(&mut self, elems: &Range<T>) -> &mut Self {
-        let elems = elems.start.idx()..elems.end.idx();
+        let elems = elems.start.index()..elems.end.index();
         unsafe { Self::from_slice_mut(&mut self.bits[elems]) }
     }
 
     /// Returns true iff set `self` contains `elem`.
     pub fn contains(&self, elem: &T) -> bool {
-        self.bits.get_bit(elem.idx())
+        self.bits.get_bit(elem.index())
     }
 
     pub fn words(&self) -> &[Word] {
diff --git a/src/librustc_borrowck/lib.rs b/src/librustc_borrowck/lib.rs
index cc694c59245f7..f3c39bd5fd165 100644
--- a/src/librustc_borrowck/lib.rs
+++ b/src/librustc_borrowck/lib.rs
@@ -34,6 +34,7 @@
 extern crate graphviz as dot;
 #[macro_use]
 extern crate rustc;
+extern crate rustc_data_structures;
 extern crate rustc_mir;
 extern crate core; // for NonZero
 
diff --git a/src/librustc_data_structures/indexed_vec.rs b/src/librustc_data_structures/indexed_vec.rs
new file mode 100644
index 0000000000000..db054477f75a1
--- /dev/null
+++ b/src/librustc_data_structures/indexed_vec.rs
@@ -0,0 +1,228 @@
+// 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::iter::{self, FromIterator};
+use std::slice;
+use std::marker::PhantomData;
+use std::ops::{Index, IndexMut, Range};
+use std::fmt;
+use std::vec;
+
+use rustc_serialize as serialize;
+
+/// Represents some newtyped `usize` wrapper.
+///
+/// (purpose: avoid mixing indexes for different bitvector domains.)
+pub trait Idx: Copy + 'static {
+    fn new(usize) -> Self;
+    fn index(self) -> usize;
+}
+
+impl Idx for usize {
+    fn new(idx: usize) -> Self { idx }
+    fn index(self) -> usize { self }
+}
+
+#[derive(Clone)]
+pub struct IndexVec<I: Idx, T> {
+    pub raw: Vec<T>,
+    _marker: PhantomData<Fn(&I)>
+}
+
+impl<I: Idx, T: serialize::Encodable> serialize::Encodable for IndexVec<I, T> {
+    fn encode<S: serialize::Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
+        serialize::Encodable::encode(&self.raw, s)
+    }
+}
+
+impl<I: Idx, T: serialize::Decodable> serialize::Decodable for IndexVec<I, T> {
+    fn decode<D: serialize::Decoder>(d: &mut D) -> Result<Self, D::Error> {
+        serialize::Decodable::decode(d).map(|v| {
+            IndexVec { raw: v, _marker: PhantomData }
+        })
+    }
+}
+
+impl<I: Idx, T: fmt::Debug> fmt::Debug for IndexVec<I, T> {
+    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+        fmt::Debug::fmt(&self.raw, fmt)
+    }
+}
+
+pub type Enumerated<I, J> = iter::Map<iter::Enumerate<J>, IntoIdx<I>>;
+
+impl<I: Idx, T> IndexVec<I, T> {
+    #[inline]
+    pub fn new() -> Self {
+        IndexVec { raw: Vec::new(), _marker: PhantomData }
+    }
+
+    #[inline]
+    pub fn with_capacity(capacity: usize) -> Self {
+        IndexVec { raw: Vec::with_capacity(capacity), _marker: PhantomData }
+    }
+
+    #[inline]
+    pub fn from_elem<S>(elem: T, universe: &IndexVec<I, S>) -> Self
+        where T: Clone
+    {
+        IndexVec { raw: vec![elem; universe.len()], _marker: PhantomData }
+    }
+
+    #[inline]
+    pub fn push(&mut self, d: T) -> I {
+        let idx = I::new(self.len());
+        self.raw.push(d);
+        idx
+    }
+
+    #[inline]
+    pub fn len(&self) -> usize {
+        self.raw.len()
+    }
+
+    #[inline]
+    pub fn is_empty(&self) -> bool {
+        self.raw.is_empty()
+    }
+
+    #[inline]
+    pub fn into_iter(self) -> vec::IntoIter<T> {
+        self.raw.into_iter()
+    }
+
+    #[inline]
+    pub fn into_iter_enumerated(self) -> Enumerated<I, vec::IntoIter<T>>
+    {
+        self.raw.into_iter().enumerate().map(IntoIdx { _marker: PhantomData })
+    }
+
+    #[inline]
+    pub fn iter(&self) -> slice::Iter<T> {
+        self.raw.iter()
+    }
+
+    #[inline]
+    pub fn iter_enumerated(&self) -> Enumerated<I, slice::Iter<T>>
+    {
+        self.raw.iter().enumerate().map(IntoIdx { _marker: PhantomData })
+    }
+
+    #[inline]
+    pub fn indices(&self) -> iter::Map<Range<usize>, IntoIdx<I>> {
+        (0..self.len()).map(IntoIdx { _marker: PhantomData })
+    }
+
+    #[inline]
+    pub fn iter_mut(&mut self) -> slice::IterMut<T> {
+        self.raw.iter_mut()
+    }
+
+    #[inline]
+    pub fn iter_enumerated_mut(&mut self) -> Enumerated<I, slice::IterMut<T>>
+    {
+        self.raw.iter_mut().enumerate().map(IntoIdx { _marker: PhantomData })
+    }
+
+    #[inline]
+    pub fn last(&self) -> Option<I> {
+        self.len().checked_sub(1).map(I::new)
+    }
+}
+
+impl<I: Idx, T> Index<I> for IndexVec<I, T> {
+    type Output = T;
+
+    #[inline]
+    fn index(&self, index: I) -> &T {
+        &self.raw[index.index()]
+    }
+}
+
+impl<I: Idx, T> IndexMut<I> for IndexVec<I, T> {
+    #[inline]
+    fn index_mut(&mut self, index: I) -> &mut T {
+        &mut self.raw[index.index()]
+    }
+}
+
+impl<I: Idx, T> Extend<T> for IndexVec<I, T> {
+    #[inline]
+    fn extend<J: IntoIterator<Item = T>>(&mut self, iter: J) {
+        self.raw.extend(iter);
+    }
+}
+
+impl<I: Idx, T> FromIterator<T> for IndexVec<I, T> {
+    #[inline]
+    fn from_iter<J>(iter: J) -> Self where J: IntoIterator<Item=T> {
+        IndexVec { raw: FromIterator::from_iter(iter), _marker: PhantomData }
+    }
+}
+
+impl<I: Idx, T> IntoIterator for IndexVec<I, T> {
+    type Item = T;
+    type IntoIter = vec::IntoIter<T>;
+
+    #[inline]
+    fn into_iter(self) -> vec::IntoIter<T> {
+        self.raw.into_iter()
+    }
+
+}
+
+impl<'a, I: Idx, T> IntoIterator for &'a IndexVec<I, T> {
+    type Item = &'a T;
+    type IntoIter = slice::Iter<'a, T>;
+
+    #[inline]
+    fn into_iter(self) -> slice::Iter<'a, T> {
+        self.raw.iter()
+    }
+}
+
+impl<'a, I: Idx, T> IntoIterator for &'a mut IndexVec<I, T> {
+    type Item = &'a mut T;
+    type IntoIter = slice::IterMut<'a, T>;
+
+    #[inline]
+    fn into_iter(mut self) -> slice::IterMut<'a, T> {
+        self.raw.iter_mut()
+    }
+}
+
+pub struct IntoIdx<I: Idx> { _marker: PhantomData<fn(&I)> }
+impl<I: Idx, T> FnOnce<((usize, T),)> for IntoIdx<I> {
+    type Output = (I, T);
+
+    extern "rust-call" fn call_once(self, ((n, t),): ((usize, T),)) -> Self::Output {
+        (I::new(n), t)
+    }
+}
+
+impl<I: Idx, T> FnMut<((usize, T),)> for IntoIdx<I> {
+    extern "rust-call" fn call_mut(&mut self, ((n, t),): ((usize, T),)) -> Self::Output {
+        (I::new(n), t)
+    }
+}
+
+impl<I: Idx> FnOnce<(usize,)> for IntoIdx<I> {
+    type Output = I;
+
+    extern "rust-call" fn call_once(self, (n,): (usize,)) -> Self::Output {
+        I::new(n)
+    }
+}
+
+impl<I: Idx> FnMut<(usize,)> for IntoIdx<I> {
+    extern "rust-call" fn call_mut(&mut self, (n,): (usize,)) -> Self::Output {
+        I::new(n)
+    }
+}
diff --git a/src/librustc_data_structures/lib.rs b/src/librustc_data_structures/lib.rs
index 00f797d1b9022..9370ad016ef1e 100644
--- a/src/librustc_data_structures/lib.rs
+++ b/src/librustc_data_structures/lib.rs
@@ -41,6 +41,7 @@ extern crate serialize as rustc_serialize; // used by deriving
 pub mod bitvec;
 pub mod graph;
 pub mod ivar;
+pub mod indexed_vec;
 pub mod obligation_forest;
 pub mod snapshot_map;
 pub mod snapshot_vec;
diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs
index c63122948ff3a..1205d688b85a8 100644
--- a/src/librustc_driver/driver.rs
+++ b/src/librustc_driver/driver.rs
@@ -976,11 +976,13 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session,
         time(time_passes, "MIR passes", || {
             let mut passes = sess.mir_passes.borrow_mut();
             // Push all the built-in passes.
-            passes.push_pass(box mir::transform::remove_dead_blocks::RemoveDeadBlocks);
+            passes.push_hook(box mir::transform::dump_mir::DumpMir);
+            passes.push_pass(box mir::transform::simplify_cfg::SimplifyCfg::new("initial"));
             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::remove_dead_blocks::RemoveDeadBlocks);
+            passes.push_pass(
+                box mir::transform::simplify_branches::SimplifyBranches::new("initial"));
+            passes.push_pass(box mir::transform::simplify_cfg::SimplifyCfg::new("qualify-consts"));
             // And run everything.
             passes.run_passes(tcx, &mut mir_map);
         });
@@ -1046,15 +1048,20 @@ pub fn phase_4_translate_to_llvm<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
     // to LLVM code.
     time(time_passes, "Prepare MIR codegen passes", || {
         let mut passes = ::rustc::mir::transform::Passes::new();
+        passes.push_hook(box mir::transform::dump_mir::DumpMir);
         passes.push_pass(box mir::transform::no_landing_pads::NoLandingPads);
-        passes.push_pass(box mir::transform::remove_dead_blocks::RemoveDeadBlocks);
+        passes.push_pass(box mir::transform::simplify_cfg::SimplifyCfg::new("no-landing-pads"));
+
         passes.push_pass(box mir::transform::erase_regions::EraseRegions);
+
         passes.push_pass(box mir::transform::add_call_guards::AddCallGuards);
         passes.push_pass(box borrowck::ElaborateDrops);
         passes.push_pass(box mir::transform::no_landing_pads::NoLandingPads);
-        passes.push_pass(box mir::transform::simplify_cfg::SimplifyCfg);
+        passes.push_pass(box mir::transform::simplify_cfg::SimplifyCfg::new("elaborate-drops"));
+
         passes.push_pass(box mir::transform::add_call_guards::AddCallGuards);
-        passes.push_pass(box mir::transform::dump_mir::DumpMir("pre_trans"));
+        passes.push_pass(box mir::transform::dump_mir::Marker("PreTrans"));
+
         passes.run_passes(tcx, &mut mir_map);
     });
 
diff --git a/src/librustc_mir/build/cfg.rs b/src/librustc_mir/build/cfg.rs
index 95f87bf832614..83f8c3b42c850 100644
--- a/src/librustc_mir/build/cfg.rs
+++ b/src/librustc_mir/build/cfg.rs
@@ -18,17 +18,15 @@ use rustc::mir::repr::*;
 
 impl<'tcx> CFG<'tcx> {
     pub fn block_data(&self, blk: BasicBlock) -> &BasicBlockData<'tcx> {
-        &self.basic_blocks[blk.index()]
+        &self.basic_blocks[blk]
     }
 
     pub fn block_data_mut(&mut self, blk: BasicBlock) -> &mut BasicBlockData<'tcx> {
-        &mut self.basic_blocks[blk.index()]
+        &mut self.basic_blocks[blk]
     }
 
     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)
+        self.basic_blocks.push(BasicBlockData::new(None))
     }
 
     pub fn start_new_cleanup_block(&mut self) -> BasicBlock {
@@ -80,8 +78,11 @@ impl<'tcx> CFG<'tcx> {
                      block: BasicBlock,
                      source_info: SourceInfo,
                      kind: TerminatorKind<'tcx>) {
+        debug!("terminating block {:?} <- {:?}", block, kind);
         debug_assert!(self.block_data(block).terminator.is_none(),
-                      "terminate: block {:?} already has a terminator set", block);
+                      "terminate: block {:?}={:?} already has a terminator set",
+                      block,
+                      self.block_data(block));
         self.block_data_mut(block).terminator = Some(Terminator {
             source_info: source_info,
             kind: kind,
diff --git a/src/librustc_mir/build/expr/as_lvalue.rs b/src/librustc_mir/build/expr/as_lvalue.rs
index 8e33cfa9b0b52..dd6c9c02f5644 100644
--- a/src/librustc_mir/build/expr/as_lvalue.rs
+++ b/src/librustc_mir/build/expr/as_lvalue.rs
@@ -15,6 +15,8 @@ use build::expr::category::Category;
 use hair::*;
 use rustc::mir::repr::*;
 
+use rustc_data_structures::indexed_vec::Idx;
+
 impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
     /// Compile `expr`, yielding an lvalue that we can move from etc.
     pub fn as_lvalue<M>(&mut self,
@@ -75,7 +77,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                 success.and(slice.index(idx))
             }
             ExprKind::SelfRef => {
-                block.and(Lvalue::Arg(0))
+                block.and(Lvalue::Arg(Arg::new(0)))
             }
             ExprKind::VarRef { id } => {
                 let index = this.var_indices[&id];
diff --git a/src/librustc_mir/build/expr/as_rvalue.rs b/src/librustc_mir/build/expr/as_rvalue.rs
index 6524124c13b06..c0c27ac5943aa 100644
--- a/src/librustc_mir/build/expr/as_rvalue.rs
+++ b/src/librustc_mir/build/expr/as_rvalue.rs
@@ -14,6 +14,7 @@ use std;
 
 use rustc_const_math::{ConstMathErr, Op};
 use rustc_data_structures::fnv::FnvHashMap;
+use rustc_data_structures::indexed_vec::Idx;
 
 use build::{BlockAnd, BlockAndExtension, Builder};
 use build::expr::category::{Category, RvalueFunc};
diff --git a/src/librustc_mir/build/matches/mod.rs b/src/librustc_mir/build/matches/mod.rs
index 14b4dbdd9cff4..b3315ab7d290f 100644
--- a/src/librustc_mir/build/matches/mod.rs
+++ b/src/librustc_mir/build/matches/mod.rs
@@ -78,12 +78,21 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
         // branch to the appropriate arm block
         let otherwise = self.match_candidates(span, &mut arm_blocks, candidates, block);
 
-        // because all matches are exhaustive, in principle we expect
-        // an empty vector to be returned here, but the algorithm is
-        // not entirely precise
         if !otherwise.is_empty() {
-            let join_block = self.join_otherwise_blocks(span, otherwise);
-            self.panic(join_block, "something about matches algorithm not being precise", span);
+            // All matches are exhaustive. However, because some matches
+            // only have exponentially-large exhaustive decision trees, we
+            // sometimes generate an inexhaustive decision tree.
+            //
+            // In that case, the inexhaustive tips of the decision tree
+            // can't be reached - terminate them with an `unreachable`.
+            let source_info = self.source_info(span);
+
+            let mut otherwise = otherwise;
+            otherwise.sort();
+            otherwise.dedup(); // variant switches can introduce duplicate target blocks
+            for block in otherwise {
+                self.cfg.terminate(block, source_info, TerminatorKind::Unreachable);
+            }
         }
 
         // all the arm blocks will rejoin here
@@ -667,25 +676,23 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                        name: Name,
                        var_id: NodeId,
                        var_ty: Ty<'tcx>)
-                       -> u32
+                       -> Var
     {
         debug!("declare_binding(var_id={:?}, name={:?}, var_ty={:?}, source_info={:?})",
                var_id, name, var_ty, source_info);
 
-        let index = self.var_decls.len();
-        self.var_decls.push(VarDecl::<'tcx> {
+        let var = self.var_decls.push(VarDecl::<'tcx> {
             source_info: source_info,
             mutability: mutability,
             name: name,
             ty: var_ty.clone(),
         });
-        let index = index as u32;
         let extent = self.extent_of_innermost_scope();
-        self.schedule_drop(source_info.span, extent, &Lvalue::Var(index), var_ty);
-        self.var_indices.insert(var_id, index);
+        self.schedule_drop(source_info.span, extent, &Lvalue::Var(var), var_ty);
+        self.var_indices.insert(var_id, var);
 
-        debug!("declare_binding: index={:?}", index);
+        debug!("declare_binding: var={:?}", var);
 
-        index
+        var
     }
 }
diff --git a/src/librustc_mir/build/misc.rs b/src/librustc_mir/build/misc.rs
index a9d5c0c4f85c3..0d7a502834881 100644
--- a/src/librustc_mir/build/misc.rs
+++ b/src/librustc_mir/build/misc.rs
@@ -18,7 +18,6 @@ use rustc::middle::const_val::ConstVal;
 use rustc::ty::{self, Ty};
 
 use rustc::mir::repr::*;
-use std::u32;
 use syntax::ast;
 use syntax::codemap::Span;
 
@@ -29,12 +28,10 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
     /// NB: **No cleanup is scheduled for this temporary.** You should
     /// call `schedule_drop` once the temporary is initialized.
     pub fn temp(&mut self, ty: Ty<'tcx>) -> Lvalue<'tcx> {
-        let index = self.temp_decls.len();
-        self.temp_decls.push(TempDecl { ty: ty });
-        assert!(index < (u32::MAX) as usize);
-        let lvalue = Lvalue::Temp(index as u32);
+        let temp = self.temp_decls.push(TempDecl { ty: ty });
+        let lvalue = Lvalue::Temp(temp);
         debug!("temp: created temp {:?} with type {:?}",
-               lvalue, self.temp_decls.last().unwrap().ty);
+               lvalue, self.temp_decls[temp].ty);
         lvalue
     }
 
diff --git a/src/librustc_mir/build/mod.rs b/src/librustc_mir/build/mod.rs
index 9c1b9fa4437e0..2626a02281f7d 100644
--- a/src/librustc_mir/build/mod.rs
+++ b/src/librustc_mir/build/mod.rs
@@ -12,15 +12,17 @@ use hair::cx::Cx;
 use rustc::middle::region::{CodeExtent, CodeExtentData, ROOT_CODE_EXTENT};
 use rustc::ty::{self, Ty};
 use rustc::mir::repr::*;
-use rustc_data_structures::fnv::FnvHashMap;
+use rustc::util::nodemap::NodeMap;
 use rustc::hir;
-use std::ops::{Index, IndexMut};
-use std::u32;
 use syntax::abi::Abi;
 use syntax::ast;
 use syntax::codemap::Span;
 use syntax::parse::token::keywords;
 
+use rustc_data_structures::indexed_vec::{IndexVec, Idx};
+
+use std::u32;
+
 pub struct Builder<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
     hir: Cx<'a, 'gcx, 'tcx>,
     cfg: CFG<'tcx>,
@@ -36,7 +38,7 @@ pub struct Builder<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
     ///  but these are liable to get out of date once optimization
     ///  begins. They are also hopefully temporary, and will be
     ///  no longer needed when we adopt graph-based regions.
-    scope_auxiliary: ScopeAuxiliaryVec,
+    scope_auxiliary: IndexVec<ScopeId, ScopeAuxiliary>,
 
     /// the current set of loops; see the `scope` module for more
     /// details
@@ -44,12 +46,12 @@ pub struct Builder<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
 
     /// the vector of all scopes that we have created thus far;
     /// we track this for debuginfo later
-    visibility_scopes: Vec<VisibilityScopeData>,
+    visibility_scopes: IndexVec<VisibilityScope, VisibilityScopeData>,
     visibility_scope: VisibilityScope,
 
-    var_decls: Vec<VarDecl<'tcx>>,
-    var_indices: FnvHashMap<ast::NodeId, u32>,
-    temp_decls: Vec<TempDecl<'tcx>>,
+    var_decls: IndexVec<Var, VarDecl<'tcx>>,
+    var_indices: NodeMap<Var>,
+    temp_decls: IndexVec<Temp, TempDecl<'tcx>>,
     unit_temp: Option<Lvalue<'tcx>>,
 
     /// cached block with the RESUME terminator; this is created
@@ -60,19 +62,19 @@ pub struct Builder<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
 }
 
 struct CFG<'tcx> {
-    basic_blocks: Vec<BasicBlockData<'tcx>>,
+    basic_blocks: IndexVec<BasicBlock, BasicBlockData<'tcx>>,
 }
 
 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
 pub struct ScopeId(u32);
 
-impl ScopeId {
-    pub fn new(index: usize) -> ScopeId {
+impl Idx for ScopeId {
+    fn new(index: usize) -> ScopeId {
         assert!(index < (u32::MAX as usize));
         ScopeId(index as u32)
     }
 
-    pub fn index(self) -> usize {
+    fn index(self) -> usize {
         self.0 as usize
     }
 }
@@ -109,25 +111,7 @@ pub struct Location {
     pub statement_index: usize,
 }
 
-pub struct ScopeAuxiliaryVec {
-    pub vec: Vec<ScopeAuxiliary>
-}
-
-impl Index<ScopeId> for ScopeAuxiliaryVec {
-    type Output = ScopeAuxiliary;
-
-    #[inline]
-    fn index(&self, index: ScopeId) -> &ScopeAuxiliary {
-        &self.vec[index.index()]
-    }
-}
-
-impl IndexMut<ScopeId> for ScopeAuxiliaryVec {
-    #[inline]
-    fn index_mut(&mut self, index: ScopeId) -> &mut ScopeAuxiliary {
-        &mut self.vec[index.index()]
-    }
-}
+pub type ScopeAuxiliaryVec = IndexVec<ScopeId, ScopeAuxiliary>;
 
 ///////////////////////////////////////////////////////////////////////////
 /// The `BlockAnd` "monad" packages up the new basic block along with a
@@ -213,8 +197,8 @@ pub fn construct_fn<'a, 'gcx, 'tcx, A>(hir: Cx<'a, 'gcx, 'tcx>,
     match tcx.node_id_to_type(fn_id).sty {
         ty::TyFnDef(_, _, f) if f.abi == Abi::RustCall => {
             // RustCall pseudo-ABI untuples the last argument.
-            if let Some(arg_decl) = arg_decls.last_mut() {
-                arg_decl.spread = true;
+            if let Some(last_arg) = arg_decls.last() {
+                arg_decls[last_arg].spread = true;
             }
         }
         _ => {}
@@ -271,23 +255,23 @@ pub fn construct_const<'a, 'gcx, 'tcx>(hir: Cx<'a, 'gcx, 'tcx>,
     });
 
     let ty = tcx.expr_ty_adjusted(ast_expr);
-    builder.finish(vec![], vec![], ty::FnConverging(ty))
+    builder.finish(vec![], IndexVec::new(), ty::FnConverging(ty))
 }
 
 impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
     fn new(hir: Cx<'a, 'gcx, 'tcx>, span: Span) -> Builder<'a, 'gcx, 'tcx> {
         let mut builder = Builder {
             hir: hir,
-            cfg: CFG { basic_blocks: vec![] },
+            cfg: CFG { basic_blocks: IndexVec::new() },
             fn_span: span,
             scopes: vec![],
-            visibility_scopes: vec![],
+            visibility_scopes: IndexVec::new(),
             visibility_scope: ARGUMENT_VISIBILITY_SCOPE,
-            scope_auxiliary: ScopeAuxiliaryVec { vec: vec![] },
+            scope_auxiliary: IndexVec::new(),
             loop_scopes: vec![],
-            temp_decls: vec![],
-            var_decls: vec![],
-            var_indices: FnvHashMap(),
+            temp_decls: IndexVec::new(),
+            var_decls: IndexVec::new(),
+            var_indices: NodeMap(),
             unit_temp: None,
             cached_resume_block: None,
             cached_return_block: None
@@ -302,7 +286,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
 
     fn finish(self,
               upvar_decls: Vec<UpvarDecl>,
-              arg_decls: Vec<ArgDecl<'tcx>>,
+              arg_decls: IndexVec<Arg, ArgDecl<'tcx>>,
               return_ty: ty::FnOutput<'tcx>)
               -> (Mir<'tcx>, ScopeAuxiliaryVec) {
         for (index, block) in self.cfg.basic_blocks.iter().enumerate() {
@@ -311,17 +295,16 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
             }
         }
 
-        (Mir {
-            basic_blocks: self.cfg.basic_blocks,
-            visibility_scopes: self.visibility_scopes,
-            promoted: vec![],
-            var_decls: self.var_decls,
-            arg_decls: arg_decls,
-            temp_decls: self.temp_decls,
-            upvar_decls: upvar_decls,
-            return_ty: return_ty,
-            span: self.fn_span
-        }, self.scope_auxiliary)
+        (Mir::new(self.cfg.basic_blocks,
+                  self.visibility_scopes,
+                  IndexVec::new(),
+                  return_ty,
+                  self.var_decls,
+                  arg_decls,
+                  self.temp_decls,
+                  upvar_decls,
+                  self.fn_span
+        ), self.scope_auxiliary)
     }
 
     fn args_and_body<A>(&mut self,
@@ -330,13 +313,13 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                         arguments: A,
                         argument_extent: CodeExtent,
                         ast_block: &'gcx hir::Block)
-                        -> BlockAnd<Vec<ArgDecl<'tcx>>>
+                        -> BlockAnd<IndexVec<Arg, ArgDecl<'tcx>>>
         where A: Iterator<Item=(Ty<'gcx>, Option<&'gcx hir::Pat>)>
     {
         // to start, translate the argument patterns and collect the argument types.
         let mut scope = None;
         let arg_decls = arguments.enumerate().map(|(index, (ty, pattern))| {
-            let lvalue = Lvalue::Arg(index as u32);
+            let lvalue = Lvalue::Arg(Arg::new(index));
             if let Some(pattern) = pattern {
                 let pattern = self.hir.irrefutable_pat(pattern);
                 scope = self.declare_bindings(scope, ast_block.span, &pattern);
diff --git a/src/librustc_mir/build/scope.rs b/src/librustc_mir/build/scope.rs
index 2b7efa52cbd05..9cc6b60eec005 100644
--- a/src/librustc_mir/build/scope.rs
+++ b/src/librustc_mir/build/scope.rs
@@ -90,12 +90,10 @@ use build::{BlockAnd, BlockAndExtension, Builder, CFG, ScopeAuxiliary, ScopeId};
 use rustc::middle::region::{CodeExtent, CodeExtentData};
 use rustc::middle::lang_items;
 use rustc::ty::subst::{Substs, Subst, VecPerParamSpace};
-use rustc::ty::{self, Ty, TyCtxt};
+use rustc::ty::{Ty, TyCtxt};
 use rustc::mir::repr::*;
-use syntax::codemap::{Span, DUMMY_SP};
-use syntax::parse::token::intern_and_get_ident;
-use rustc::middle::const_val::ConstVal;
-use rustc_const_math::ConstInt;
+use syntax::codemap::Span;
+use rustc_data_structures::indexed_vec::Idx;
 
 pub struct Scope<'tcx> {
     /// the scope-id within the scope_auxiliary
@@ -264,7 +262,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
     /// wrapper maybe preferable.
     pub fn push_scope(&mut self, extent: CodeExtent, entry: BasicBlock) {
         debug!("push_scope({:?})", extent);
-        let id = ScopeId::new(self.scope_auxiliary.vec.len());
+        let id = ScopeId::new(self.scope_auxiliary.len());
         let vis_scope = self.visibility_scope;
         self.scopes.push(Scope {
             id: id,
@@ -274,7 +272,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
             free: None,
             cached_block: None,
         });
-        self.scope_auxiliary.vec.push(ScopeAuxiliary {
+        self.scope_auxiliary.push(ScopeAuxiliary {
             extent: extent,
             dom: self.cfg.current_location(entry),
             postdoms: vec![]
@@ -555,50 +553,6 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
         next_target.unit()
     }
 
-    /// Create diverge cleanup and branch to it from `block`.
-    // FIXME: Remove this (used only for unreachable cases in match).
-    pub fn panic(&mut self, block: BasicBlock, msg: &'static str, span: Span) {
-        // fn(&(msg: &'static str filename: &'static str, line: u32)) -> !
-        let region = ty::ReStatic; // FIXME(mir-borrowck): use a better region?
-        let func = self.lang_function(lang_items::PanicFnLangItem);
-        let args = self.hir.tcx().replace_late_bound_regions(&func.ty.fn_args(), |_| region).0;
-
-        let ref_ty = args[0];
-        let tup_ty = if let ty::TyRef(_, tyandmut) = ref_ty.sty {
-            tyandmut.ty
-        } else {
-            span_bug!(span, "unexpected panic type: {:?}", func.ty);
-        };
-
-        let (tuple, tuple_ref) = (self.temp(tup_ty), self.temp(ref_ty));
-        let (file, line) = self.span_to_fileline_args(span);
-        let message = Constant {
-            span: span,
-            ty: self.hir.tcx().mk_static_str(),
-            literal: self.hir.str_literal(intern_and_get_ident(msg))
-        };
-        let elems = vec![Operand::Constant(message),
-                         Operand::Constant(file),
-                         Operand::Constant(line)];
-        let source_info = self.source_info(span);
-        // FIXME: We should have this as a constant, rather than a stack variable (to not pollute
-        // icache with cold branch code), however to achieve that we either have to rely on rvalue
-        // promotion or have some way, in MIR, to create constants.
-        self.cfg.push_assign(block, source_info, &tuple, // [1]
-                             Rvalue::Aggregate(AggregateKind::Tuple, elems));
-        // [1] tuple = (message_arg, file_arg, line_arg);
-        // FIXME: is this region really correct here?
-        self.cfg.push_assign(block, source_info, &tuple_ref, // tuple_ref = &tuple;
-                             Rvalue::Ref(region, BorrowKind::Shared, tuple));
-        let cleanup = self.diverge_cleanup();
-        self.cfg.terminate(block, source_info, TerminatorKind::Call {
-            func: Operand::Constant(func),
-            args: vec![Operand::Consume(tuple_ref)],
-            cleanup: cleanup,
-            destination: None,
-        });
-    }
-
     /// Create an Assert terminator and return the success block.
     /// If the boolean condition operand is not the expected value,
     /// a runtime panic will be caused with the given message.
@@ -624,39 +578,6 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
 
         success_block
     }
-
-    fn lang_function(&mut self, lang_item: lang_items::LangItem) -> Constant<'tcx> {
-        let funcdid = match self.hir.tcx().lang_items.require(lang_item) {
-            Ok(d) => d,
-            Err(m) => {
-                self.hir.tcx().sess.fatal(&m)
-            }
-        };
-        Constant {
-            span: DUMMY_SP,
-            ty: self.hir.tcx().lookup_item_type(funcdid).ty,
-            literal: Literal::Item {
-                def_id: funcdid,
-                substs: self.hir.tcx().mk_substs(Substs::empty())
-            }
-        }
-    }
-
-    fn span_to_fileline_args(&mut self, span: Span) -> (Constant<'tcx>, Constant<'tcx>) {
-        let span_lines = self.hir.tcx().sess.codemap().lookup_char_pos(span.lo);
-        (Constant {
-            span: span,
-            ty: self.hir.tcx().mk_static_str(),
-            literal: self.hir.str_literal(intern_and_get_ident(&span_lines.file.name))
-        }, Constant {
-            span: span,
-            ty: self.hir.tcx().types.u32,
-            literal: Literal::Value {
-                value: ConstVal::Integral(ConstInt::U32(span_lines.line as u32)),
-            },
-        })
-    }
-
 }
 
 /// Builds drops for pop_scope and exit_scope.
diff --git a/src/librustc_mir/graphviz.rs b/src/librustc_mir/graphviz.rs
index 6a34d9ff0b4c4..fdfa872b0b698 100644
--- a/src/librustc_mir/graphviz.rs
+++ b/src/librustc_mir/graphviz.rs
@@ -15,6 +15,8 @@ use std::fmt::Debug;
 use std::io::{self, Write};
 use syntax::ast::NodeId;
 
+use rustc_data_structures::indexed_vec::Idx;
+
 /// Write a graphviz DOT graph of a list of MIRs.
 pub fn write_mir_graphviz<'a, 'b, 'tcx, W, I>(tcx: TyCtxt<'b, 'tcx, 'tcx>,
                                               iter: I, w: &mut W)
@@ -32,12 +34,12 @@ where W: Write, I: Iterator<Item=(&'a NodeId, &'a Mir<'a>)> {
         write_graph_label(tcx, nodeid, mir, w)?;
 
         // Nodes
-        for block in mir.all_basic_blocks() {
+        for (block, _) in mir.basic_blocks().iter_enumerated() {
             write_node(block, mir, w)?;
         }
 
         // Edges
-        for source in mir.all_basic_blocks() {
+        for (source, _) in mir.basic_blocks().iter_enumerated() {
             write_edges(source, mir, w)?;
         }
         writeln!(w, "}}")?
@@ -61,7 +63,7 @@ pub fn write_node_label<W: Write, INIT, FINI>(block: BasicBlock,
     where INIT: Fn(&mut W) -> io::Result<()>,
           FINI: Fn(&mut W) -> io::Result<()>
 {
-    let data = mir.basic_block_data(block);
+    let data = &mir[block];
 
     write!(w, r#"<table border="0" cellborder="1" cellspacing="0">"#)?;
 
@@ -105,7 +107,7 @@ fn write_node<W: Write>(block: BasicBlock, mir: &Mir, w: &mut W) -> io::Result<(
 
 /// Write graphviz DOT edges with labels between the given basic block and all of its successors.
 fn write_edges<W: Write>(source: BasicBlock, mir: &Mir, w: &mut W) -> io::Result<()> {
-    let terminator = &mir.basic_block_data(source).terminator();
+    let terminator = mir[source].terminator();
     let labels = terminator.kind.fmt_successor_labels();
 
     for (&target, label) in terminator.successors().iter().zip(labels) {
@@ -130,7 +132,7 @@ fn write_graph_label<'a, 'tcx, W: Write>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
         if i > 0 {
             write!(w, ", ")?;
         }
-        write!(w, "{:?}: {}", Lvalue::Arg(i as u32), escape(&arg.ty))?;
+        write!(w, "{:?}: {}", Lvalue::Arg(Arg::new(i)), escape(&arg.ty))?;
     }
 
     write!(w, ") -&gt; ")?;
@@ -150,13 +152,13 @@ fn write_graph_label<'a, 'tcx, W: Write>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
             write!(w, "mut ")?;
         }
         write!(w, r#"{:?}: {}; // {}<br align="left"/>"#,
-               Lvalue::Var(i as u32), escape(&var.ty), var.name)?;
+               Lvalue::Var(Var::new(i)), escape(&var.ty), var.name)?;
     }
 
     // Compiler-introduced temporary types.
     for (i, temp) in mir.temp_decls.iter().enumerate() {
         write!(w, r#"let mut {:?}: {};<br align="left"/>"#,
-               Lvalue::Temp(i as u32), escape(&temp.ty))?;
+               Lvalue::Temp(Temp::new(i)), escape(&temp.ty))?;
     }
 
     writeln!(w, ">;")
diff --git a/src/librustc_mir/hair/cx/expr.rs b/src/librustc_mir/hair/cx/expr.rs
index 1f560672b62a8..cebdaad13db48 100644
--- a/src/librustc_mir/hair/cx/expr.rs
+++ b/src/librustc_mir/hair/cx/expr.rs
@@ -10,6 +10,7 @@
 
 use hair::*;
 use rustc_data_structures::fnv::FnvHashMap;
+use rustc_data_structures::indexed_vec::Idx;
 use rustc_const_math::ConstInt;
 use hair::cx::Cx;
 use hair::cx::block;
diff --git a/src/librustc_mir/hair/cx/mod.rs b/src/librustc_mir/hair/cx/mod.rs
index 25860ae7ef1ee..81b098281d6a1 100644
--- a/src/librustc_mir/hair/cx/mod.rs
+++ b/src/librustc_mir/hair/cx/mod.rs
@@ -21,6 +21,7 @@ use rustc::mir::transform::MirSource;
 
 use rustc::middle::const_val::ConstVal;
 use rustc_const_eval as const_eval;
+use rustc_data_structures::indexed_vec::Idx;
 use rustc::hir::def_id::DefId;
 use rustc::hir::intravisit::FnKind;
 use rustc::hir::map::blocks::FnLikeNode;
diff --git a/src/librustc_mir/hair/cx/pattern.rs b/src/librustc_mir/hair/cx/pattern.rs
index acde81979f927..d527936c8993f 100644
--- a/src/librustc_mir/hair/cx/pattern.rs
+++ b/src/librustc_mir/hair/cx/pattern.rs
@@ -11,6 +11,7 @@
 use hair::*;
 use hair::cx::Cx;
 use rustc_data_structures::fnv::FnvHashMap;
+use rustc_data_structures::indexed_vec::Idx;
 use rustc_const_eval as const_eval;
 use rustc::hir::def::Def;
 use rustc::hir::pat_util::{EnumerateAndAdjustIterator, pat_is_resolved_const};
diff --git a/src/librustc_mir/pretty.rs b/src/librustc_mir/pretty.rs
index 8c21928c2513b..856d6cda5af0f 100644
--- a/src/librustc_mir/pretty.rs
+++ b/src/librustc_mir/pretty.rs
@@ -14,6 +14,7 @@ use rustc::mir::repr::*;
 use rustc::mir::transform::MirSource;
 use rustc::ty::{self, TyCtxt};
 use rustc_data_structures::fnv::FnvHashMap;
+use rustc_data_structures::indexed_vec::{Idx};
 use std::fmt::Display;
 use std::fs;
 use std::io::{self, Write};
@@ -111,9 +112,7 @@ fn scope_entry_exit_annotations(auxiliary: Option<&ScopeAuxiliaryVec>)
     // compute scope/entry exit annotations
     let mut annotations = FnvHashMap();
     if let Some(auxiliary) = auxiliary {
-        for (index, auxiliary) in auxiliary.vec.iter().enumerate() {
-            let scope_id = ScopeId::new(index);
-
+        for (scope_id, auxiliary) in auxiliary.iter_enumerated() {
             annotations.entry(auxiliary.dom)
                        .or_insert(vec![])
                        .push(Annotation::EnterScope(scope_id));
@@ -136,9 +135,9 @@ pub fn write_mir_fn<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                               -> io::Result<()> {
     let annotations = scope_entry_exit_annotations(auxiliary);
     write_mir_intro(tcx, src, mir, w)?;
-    for block in mir.all_basic_blocks() {
+    for block in mir.basic_blocks().indices() {
         write_basic_block(tcx, block, mir, w, &annotations)?;
-        if block.index() + 1 != mir.basic_blocks.len() {
+        if block.index() + 1 != mir.basic_blocks().len() {
             writeln!(w, "")?;
         }
     }
@@ -154,7 +153,7 @@ fn write_basic_block(tcx: TyCtxt,
                      w: &mut Write,
                      annotations: &FnvHashMap<Location, Vec<Annotation>>)
                      -> io::Result<()> {
-    let data = mir.basic_block_data(block);
+    let data = &mir[block];
 
     // Basic block label at the top.
     writeln!(w, "{}{:?}: {{", INDENT, block)?;
@@ -218,7 +217,7 @@ fn write_scope_tree(tcx: TyCtxt,
         writeln!(w, "{0:1$}scope {2} {{", "", indent, child.index())?;
 
         // User variable types (including the user's name in a comment).
-        for (i, var) in mir.var_decls.iter().enumerate() {
+        for (id, var) in mir.var_decls.iter_enumerated() {
             // Skip if not declared in this scope.
             if var.source_info.scope != child {
                 continue;
@@ -235,7 +234,7 @@ fn write_scope_tree(tcx: TyCtxt,
                                        INDENT,
                                        indent,
                                        mut_str,
-                                       Lvalue::Var(i as u32),
+                                       id,
                                        var.ty);
             writeln!(w, "{0:1$} // \"{2}\" in {3}",
                      indented_var,
@@ -297,11 +296,11 @@ fn write_mir_sig(tcx: TyCtxt, src: MirSource, mir: &Mir, w: &mut Write)
         write!(w, "(")?;
 
         // fn argument types.
-        for (i, arg) in mir.arg_decls.iter().enumerate() {
-            if i > 0 {
+        for (i, arg) in mir.arg_decls.iter_enumerated() {
+            if i.index() != 0 {
                 write!(w, ", ")?;
             }
-            write!(w, "{:?}: {}", Lvalue::Arg(i as u32), arg.ty)?;
+            write!(w, "{:?}: {}", Lvalue::Arg(i), arg.ty)?;
         }
 
         write!(w, ") -> ")?;
@@ -319,8 +318,8 @@ fn write_mir_sig(tcx: TyCtxt, src: MirSource, mir: &Mir, w: &mut Write)
 
 fn write_mir_decls(mir: &Mir, w: &mut Write) -> io::Result<()> {
     // Compiler-introduced temporary types.
-    for (i, temp) in mir.temp_decls.iter().enumerate() {
-        writeln!(w, "{}let mut {:?}: {};", INDENT, Lvalue::Temp(i as u32), temp.ty)?;
+    for (id, temp) in mir.temp_decls.iter_enumerated() {
+        writeln!(w, "{}let mut {:?}: {};", INDENT, id, temp.ty)?;
     }
 
     // Wrote any declaration? Add an empty line before the first block is printed.
diff --git a/src/librustc_mir/transform/add_call_guards.rs b/src/librustc_mir/transform/add_call_guards.rs
index 987f12ab71b4a..63e975128c792 100644
--- a/src/librustc_mir/transform/add_call_guards.rs
+++ b/src/librustc_mir/transform/add_call_guards.rs
@@ -11,7 +11,8 @@
 use rustc::ty::TyCtxt;
 use rustc::mir::repr::*;
 use rustc::mir::transform::{MirPass, MirSource, Pass};
-use rustc::mir::traversal;
+use rustc_data_structures::indexed_vec::{Idx, IndexVec};
+
 use pretty;
 
 pub struct AddCallGuards;
@@ -38,38 +39,27 @@ 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()];
-
-        // Build the precedecessor map for the MIR
-        for (_, data) in traversal::preorder(mir) {
-            if let Some(ref term) = data.terminator {
-                for &tgt in term.successors().iter() {
-                    pred_count[tgt.index()] += 1;
-                }
-            }
-        }
+        let pred_count: IndexVec<_, _> =
+            mir.predecessors().iter().map(|ps| ps.len()).collect();
 
         // We need a place to store the new blocks generated
         let mut new_blocks = Vec::new();
 
-        let bbs = mir.all_basic_blocks();
-        let cur_len = mir.basic_blocks.len();
-
-        for &bb in &bbs {
-            let data = mir.basic_block_data_mut(bb);
+        let cur_len = mir.basic_blocks().len();
 
-            match data.terminator {
+        for block in mir.basic_blocks_mut() {
+            match block.terminator {
                 Some(Terminator {
                     kind: TerminatorKind::Call {
                         destination: Some((_, ref mut destination)),
                         cleanup: Some(_),
                         ..
                     }, source_info
-                }) if pred_count[destination.index()] > 1 => {
+                }) if pred_count[*destination] > 1 => {
                     // It's a critical edge, break it
                     let call_guard = BasicBlockData {
                         statements: vec![],
-                        is_cleanup: data.is_cleanup,
+                        is_cleanup: block.is_cleanup,
                         terminator: Some(Terminator {
                             source_info: source_info,
                             kind: TerminatorKind::Goto { target: *destination }
@@ -88,7 +78,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.basic_blocks_mut().extend(new_blocks);
     }
 }
 
diff --git a/src/librustc_mir/transform/dump_mir.rs b/src/librustc_mir/transform/dump_mir.rs
index fb49f951ecd58..642adeee5cd63 100644
--- a/src/librustc_mir/transform/dump_mir.rs
+++ b/src/librustc_mir/transform/dump_mir.rs
@@ -10,18 +10,64 @@
 
 //! This pass just dumps MIR at a specified point.
 
+use std::fmt;
+
 use rustc::ty::TyCtxt;
 use rustc::mir::repr::*;
-use rustc::mir::transform::{Pass, MirPass, MirSource};
+use rustc::mir::transform::{Pass, MirPass, MirPassHook, MirSource};
 use pretty;
 
-pub struct DumpMir<'a>(pub &'a str);
+pub struct Marker<'a>(pub &'a str);
+
+impl<'b, 'tcx> MirPass<'tcx> for Marker<'b> {
+    fn run_pass<'a>(&mut self, _tcx: TyCtxt<'a, 'tcx, 'tcx>,
+                    _src: MirSource, _mir: &mut Mir<'tcx>)
+    {}
+}
+
+impl<'b> Pass for Marker<'b> {
+    fn name(&self) -> &str { self.0 }
+}
+
+pub struct Disambiguator<'a> {
+    pass: &'a Pass,
+    is_after: bool
+}
+
+impl<'a> fmt::Display for Disambiguator<'a> {
+    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+        let title = if self.is_after { "after" } else { "before" };
+        if let Some(fmt) = self.pass.disambiguator() {
+            write!(formatter, "{}-{}", fmt, title)
+        } else {
+            write!(formatter, "{}", title)
+        }
+    }
+}
+
+pub struct DumpMir;
 
-impl<'b, 'tcx> MirPass<'tcx> for DumpMir<'b> {
-    fn run_pass<'a>(&mut self, tcx: TyCtxt<'a, 'tcx, 'tcx>,
-                    src: MirSource, mir: &mut Mir<'tcx>) {
-        pretty::dump_mir(tcx, self.0, &0, src, mir, None);
+impl<'tcx> MirPassHook<'tcx> for DumpMir {
+    fn on_mir_pass<'a>(
+        &mut self,
+        tcx: TyCtxt<'a, 'tcx, 'tcx>,
+        src: MirSource,
+        mir: &Mir<'tcx>,
+        pass: &Pass,
+        is_after: bool)
+    {
+        pretty::dump_mir(
+            tcx,
+            pass.name(),
+            &Disambiguator {
+                pass: pass,
+                is_after: is_after
+            },
+            src,
+            mir,
+            None
+        );
     }
 }
 
-impl<'b> Pass for DumpMir<'b> {}
+impl<'b> Pass for DumpMir {}
diff --git a/src/librustc_mir/transform/mod.rs b/src/librustc_mir/transform/mod.rs
index 339dcdec06080..7b707b4adb69a 100644
--- a/src/librustc_mir/transform/mod.rs
+++ b/src/librustc_mir/transform/mod.rs
@@ -8,7 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-pub mod remove_dead_blocks;
+pub mod simplify_branches;
 pub mod simplify_cfg;
 pub mod erase_regions;
 pub mod no_landing_pads;
diff --git a/src/librustc_mir/transform/no_landing_pads.rs b/src/librustc_mir/transform/no_landing_pads.rs
index 590106e0a225b..818f060ed445c 100644
--- a/src/librustc_mir/transform/no_landing_pads.rs
+++ b/src/librustc_mir/transform/no_landing_pads.rs
@@ -24,6 +24,7 @@ impl<'tcx> MutVisitor<'tcx> for NoLandingPads {
             TerminatorKind::Goto { .. } |
             TerminatorKind::Resume |
             TerminatorKind::Return |
+            TerminatorKind::Unreachable |
             TerminatorKind::If { .. } |
             TerminatorKind::Switch { .. } |
             TerminatorKind::SwitchInt { .. } => {
diff --git a/src/librustc_mir/transform/promote_consts.rs b/src/librustc_mir/transform/promote_consts.rs
index 985bc9ac2d8d7..3ebfef10d4311 100644
--- a/src/librustc_mir/transform/promote_consts.rs
+++ b/src/librustc_mir/transform/promote_consts.rs
@@ -30,6 +30,8 @@ use syntax::codemap::Span;
 
 use build::Location;
 
+use rustc_data_structures::indexed_vec::{IndexVec, Idx};
+
 use std::mem;
 
 /// State of a temporary during collection and promotion.
@@ -74,7 +76,7 @@ pub enum Candidate {
 }
 
 struct TempCollector {
-    temps: Vec<TempState>,
+    temps: IndexVec<Temp, TempState>,
     location: Location,
     span: Span
 }
@@ -89,7 +91,7 @@ impl<'tcx> Visitor<'tcx> for TempCollector {
                 return;
             }
 
-            let temp = &mut self.temps[index as usize];
+            let temp = &mut self.temps[index];
             if *temp == TempState::Undefined {
                 match context {
                     LvalueContext::Store |
@@ -134,9 +136,9 @@ impl<'tcx> Visitor<'tcx> for TempCollector {
     }
 }
 
-pub fn collect_temps(mir: &Mir, rpo: &mut ReversePostorder) -> Vec<TempState> {
+pub fn collect_temps(mir: &Mir, rpo: &mut ReversePostorder) -> IndexVec<Temp, TempState> {
     let mut collector = TempCollector {
-        temps: vec![TempState::Undefined; mir.temp_decls.len()],
+        temps: IndexVec::from_elem(TempState::Undefined, &mir.temp_decls),
         location: Location {
             block: START_BLOCK,
             statement_index: 0
@@ -152,7 +154,7 @@ pub fn collect_temps(mir: &Mir, rpo: &mut ReversePostorder) -> Vec<TempState> {
 struct Promoter<'a, 'tcx: 'a> {
     source: &'a mut Mir<'tcx>,
     promoted: Mir<'tcx>,
-    temps: &'a mut Vec<TempState>,
+    temps: &'a mut IndexVec<Temp, TempState>,
 
     /// If true, all nested temps are also kept in the
     /// source MIR, not moved to the promoted MIR.
@@ -161,23 +163,23 @@ 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 span = self.promoted.span;
+        self.promoted.basic_blocks_mut().push(BasicBlockData {
             statements: vec![],
             terminator: Some(Terminator {
                 source_info: SourceInfo {
-                    span: self.promoted.span,
+                    span: span,
                     scope: ARGUMENT_VISIBILITY_SCOPE
                 },
                 kind: TerminatorKind::Return
             }),
             is_cleanup: false
-        });
-        BasicBlock::new(index)
+        })
     }
 
     fn assign(&mut self, dest: Lvalue<'tcx>, rvalue: Rvalue<'tcx>, span: Span) {
-        let data = self.promoted.basic_blocks.last_mut().unwrap();
+        let last = self.promoted.basic_blocks().last().unwrap();
+        let data = &mut self.promoted[last];
         data.statements.push(Statement {
             source_info: SourceInfo {
                 span: span,
@@ -189,10 +191,9 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> {
 
     /// Copy the initialization of this temp to the
     /// promoted MIR, recursing through temps.
-    fn promote_temp(&mut self, index: u32) -> u32 {
-        let index = index as usize;
+    fn promote_temp(&mut self, temp: Temp) -> Temp {
         let old_keep_original = self.keep_original;
-        let (bb, stmt_idx) = match self.temps[index] {
+        let (bb, stmt_idx) = match self.temps[temp] {
             TempState::Defined {
                 location: Location { block, statement_index },
                 uses
@@ -202,13 +203,13 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> {
                 }
                 (block, statement_index)
             }
-            temp =>  {
-                span_bug!(self.promoted.span, "tmp{} not promotable: {:?}",
-                          index, temp);
+            state =>  {
+                span_bug!(self.promoted.span, "{:?} not promotable: {:?}",
+                          temp, state);
             }
         };
         if !self.keep_original {
-            self.temps[index] = TempState::PromotedOut;
+            self.temps[temp] = TempState::PromotedOut;
         }
 
         let no_stmts = self.source[bb].statements.len();
@@ -260,26 +261,24 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> {
             self.visit_terminator_kind(bb, call.as_mut().unwrap());
         }
 
-        let new_index = self.promoted.temp_decls.len() as u32;
-        let new_temp = Lvalue::Temp(new_index);
-        self.promoted.temp_decls.push(TempDecl {
-            ty: self.source.temp_decls[index].ty
+        let new_temp = self.promoted.temp_decls.push(TempDecl {
+            ty: self.source.temp_decls[temp].ty
         });
 
         // Inject the Rvalue or Call into the promoted MIR.
         if stmt_idx < no_stmts {
-            self.assign(new_temp, rvalue.unwrap(), source_info.span);
+            self.assign(Lvalue::Temp(new_temp), rvalue.unwrap(), source_info.span);
         } else {
-            let last = self.promoted.basic_blocks.len() - 1;
+            let last = self.promoted.basic_blocks().last().unwrap();
             let new_target = self.new_block();
             let mut call = call.unwrap();
             match call {
                 TerminatorKind::Call { ref mut destination, ..}  => {
-                    *destination = Some((new_temp, new_target));
+                    *destination = Some((Lvalue::Temp(new_temp), new_target));
                 }
                 _ => bug!()
             }
-            let terminator = &mut self.promoted.basic_blocks[last].terminator_mut();
+            let terminator = self.promoted[last].terminator_mut();
             terminator.source_info.span = source_info.span;
             terminator.kind = call;
         }
@@ -287,7 +286,7 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> {
         // Restore the old duplication state.
         self.keep_original = old_keep_original;
 
-        new_index
+        new_temp
     }
 
     fn promote_candidate(mut self, candidate: Candidate) {
@@ -296,7 +295,7 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> {
             span: span,
             ty: self.promoted.return_ty.unwrap(),
             literal: Literal::Promoted {
-                index: self.source.promoted.len()
+                index: Promoted::new(self.source.promoted.len())
             }
         });
         let mut rvalue = match candidate {
@@ -325,8 +324,8 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> {
 /// Replaces all temporaries with their promoted counterparts.
 impl<'a, 'tcx> MutVisitor<'tcx> for Promoter<'a, 'tcx> {
     fn visit_lvalue(&mut self, lvalue: &mut Lvalue<'tcx>, context: LvalueContext) {
-        if let Lvalue::Temp(ref mut index) = *lvalue {
-            *index = self.promote_temp(*index);
+        if let Lvalue::Temp(ref mut temp) = *lvalue {
+            *temp = self.promote_temp(*temp);
         }
         self.super_lvalue(lvalue, context);
     }
@@ -334,7 +333,7 @@ impl<'a, 'tcx> MutVisitor<'tcx> for Promoter<'a, 'tcx> {
 
 pub fn promote_candidates<'a, 'tcx>(mir: &mut Mir<'tcx>,
                                     tcx: TyCtxt<'a, 'tcx, 'tcx>,
-                                    mut temps: Vec<TempState>,
+                                    mut temps: IndexVec<Temp, TempState>,
                                     candidates: Vec<Candidate>) {
     // Visit candidates in reverse, in case they're nested.
     for candidate in candidates.into_iter().rev() {
@@ -343,7 +342,7 @@ pub fn promote_candidates<'a, 'tcx>(mir: &mut Mir<'tcx>,
                 let statement = &mir[bb].statements[stmt_idx];
                 let StatementKind::Assign(ref dest, _) = statement.kind;
                 if let Lvalue::Temp(index) = *dest {
-                    if temps[index as usize] == TempState::PromotedOut {
+                    if temps[index] == TempState::PromotedOut {
                         // Already promoted.
                         continue;
                     }
@@ -367,20 +366,20 @@ pub fn promote_candidates<'a, 'tcx>(mir: &mut Mir<'tcx>,
 
         let mut promoter = Promoter {
             source: mir,
-            promoted: Mir {
-                basic_blocks: vec![],
-                visibility_scopes: vec![VisibilityScopeData {
+            promoted: Mir::new(
+                IndexVec::new(),
+                Some(VisibilityScopeData {
                     span: span,
                     parent_scope: None
-                }],
-                promoted: vec![],
-                return_ty: ty::FnConverging(ty),
-                var_decls: vec![],
-                arg_decls: vec![],
-                temp_decls: vec![],
-                upvar_decls: vec![],
-                span: span
-            },
+                }).into_iter().collect(),
+                IndexVec::new(),
+                ty::FnConverging(ty),
+                IndexVec::new(),
+                IndexVec::new(),
+                IndexVec::new(),
+                vec![],
+                span
+            ),
             temps: &mut temps,
             keep_original: false
         };
@@ -389,8 +388,8 @@ 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 {
+    let promoted = |index: Temp| temps[index] == TempState::PromotedOut;
+    for block in mir.basic_blocks_mut() {
         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 3fdf492611d46..784ddc1ede4f1 100644
--- a/src/librustc_mir/transform/qualify_consts.rs
+++ b/src/librustc_mir/transform/qualify_consts.rs
@@ -15,6 +15,7 @@
 //! diagnostics as to why a constant rvalue wasn't promoted.
 
 use rustc_data_structures::bitvec::BitVector;
+use rustc_data_structures::indexed_vec::{IndexVec, Idx};
 use rustc::hir;
 use rustc::hir::def_id::DefId;
 use rustc::hir::intravisit::FnKind;
@@ -24,8 +25,8 @@ use rustc::ty::{self, TyCtxt, Ty};
 use rustc::ty::cast::CastTy;
 use rustc::mir::repr::*;
 use rustc::mir::mir_map::MirMap;
-use rustc::mir::transform::{Pass, MirMapPass, MirSource};
 use rustc::mir::traversal::{self, ReversePostorder};
+use rustc::mir::transform::{Pass, MirMapPass, MirPassHook, MirSource};
 use rustc::mir::visit::{LvalueContext, Visitor};
 use rustc::util::nodemap::DefIdMap;
 use syntax::abi::Abi;
@@ -141,12 +142,12 @@ struct Qualifier<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
     param_env: ty::ParameterEnvironment<'tcx>,
     qualif_map: &'a mut DefIdMap<Qualif>,
     mir_map: Option<&'a MirMap<'tcx>>,
-    temp_qualif: Vec<Option<Qualif>>,
+    temp_qualif: IndexVec<Temp, Option<Qualif>>,
     return_qualif: Option<Qualif>,
     qualif: Qualif,
     const_fn_arg_vars: BitVector,
     location: Location,
-    temp_promotion_state: Vec<TempState>,
+    temp_promotion_state: IndexVec<Temp, TempState>,
     promotion_candidates: Vec<Candidate>
 }
 
@@ -172,7 +173,7 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> {
             param_env: param_env,
             qualif_map: qualif_map,
             mir_map: mir_map,
-            temp_qualif: vec![None; mir.temp_decls.len()],
+            temp_qualif: IndexVec::from_elem(None, &mir.temp_decls),
             return_qualif: None,
             qualif: Qualif::empty(),
             const_fn_arg_vars: BitVector::new(mir.var_decls.len()),
@@ -301,22 +302,22 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> {
         // Only handle promotable temps in non-const functions.
         if self.mode == Mode::Fn {
             if let Lvalue::Temp(index) = *dest {
-                if self.temp_promotion_state[index as usize].is_promotable() {
-                    store(&mut self.temp_qualif[index as usize]);
+                if self.temp_promotion_state[index].is_promotable() {
+                    store(&mut self.temp_qualif[index]);
                 }
             }
             return;
         }
 
         match *dest {
-            Lvalue::Temp(index) => store(&mut self.temp_qualif[index as usize]),
+            Lvalue::Temp(index) => store(&mut self.temp_qualif[index]),
             Lvalue::ReturnPointer => store(&mut self.return_qualif),
 
             Lvalue::Projection(box Projection {
                 base: Lvalue::Temp(index),
                 elem: ProjectionElem::Deref
-            }) if self.mir.temp_decls[index as usize].ty.is_unique()
-               && self.temp_qualif[index as usize].map_or(false, |qualif| {
+            }) if self.mir.temp_decls[index].ty.is_unique()
+               && self.temp_qualif[index].map_or(false, |qualif| {
                     qualif.intersects(Qualif::NOT_CONST)
                }) => {
                 // Part of `box expr`, we should've errored
@@ -336,7 +337,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.basic_blocks().len());
         let mut bb = START_BLOCK;
         loop {
             seen_blocks.insert(bb.index());
@@ -361,17 +362,18 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> {
                 TerminatorKind::Switch {..} |
                 TerminatorKind::SwitchInt {..} |
                 TerminatorKind::DropAndReplace { .. } |
-                TerminatorKind::Resume => None,
+                TerminatorKind::Resume |
+                TerminatorKind::Unreachable => None,
 
                 TerminatorKind::Return => {
                     // Check for unused values. This usually means
                     // there are extra statements in the AST.
-                    for i in 0..mir.temp_decls.len() {
-                        if self.temp_qualif[i].is_none() {
+                    for temp in mir.temp_decls.indices() {
+                        if self.temp_qualif[temp].is_none() {
                             continue;
                         }
 
-                        let state = self.temp_promotion_state[i];
+                        let state = self.temp_promotion_state[temp];
                         if let TempState::Defined { location, uses: 0 } = state {
                             let data = &mir[location.block];
                             let stmt_idx = location.statement_index;
@@ -393,7 +395,7 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> {
                     self.qualif = Qualif::NOT_CONST;
                     for index in 0..mir.var_decls.len() {
                         if !self.const_fn_arg_vars.contains(index) {
-                            self.assign(&Lvalue::Var(index as u32));
+                            self.assign(&Lvalue::Var(Var::new(index)));
                         }
                     }
 
@@ -448,11 +450,11 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
                 self.add(Qualif::NOT_CONST);
             }
             Lvalue::Temp(index) => {
-                if !self.temp_promotion_state[index as usize].is_promotable() {
+                if !self.temp_promotion_state[index].is_promotable() {
                     self.add(Qualif::NOT_PROMOTABLE);
                 }
 
-                if let Some(qualif) = self.temp_qualif[index as usize] {
+                if let Some(qualif) = self.temp_qualif[index] {
                     self.add(qualif);
                 } else {
                     self.not_const();
@@ -822,7 +824,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
 
         // Check the allowed const fn argument forms.
         if let (Mode::ConstFn, &Lvalue::Var(index)) = (self.mode, dest) {
-            if self.const_fn_arg_vars.insert(index as usize) {
+            if self.const_fn_arg_vars.insert(index.index()) {
                 // Direct use of an argument is permitted.
                 if let Rvalue::Use(Operand::Consume(Lvalue::Arg(_))) = *rvalue {
                     return;
@@ -830,7 +832,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
 
                 // Avoid a generic error for other uses of arguments.
                 if self.qualif.intersects(Qualif::FN_ARGUMENT) {
-                    let decl = &self.mir.var_decls[index as usize];
+                    let decl = &self.mir.var_decls[index];
                     span_err!(self.tcx.sess, decl.source_info.span, E0022,
                               "arguments of constant functions can only \
                                be immutable by-value bindings");
@@ -907,7 +909,10 @@ pub struct QualifyAndPromoteConstants;
 impl Pass for QualifyAndPromoteConstants {}
 
 impl<'tcx> MirMapPass<'tcx> for QualifyAndPromoteConstants {
-    fn run_pass<'a>(&mut self, tcx: TyCtxt<'a, 'tcx, 'tcx>, map: &mut MirMap<'tcx>) {
+    fn run_pass<'a>(&mut self,
+                    tcx: TyCtxt<'a, 'tcx, 'tcx>,
+                    map: &mut MirMap<'tcx>,
+                    hooks: &mut [Box<for<'s> MirPassHook<'s>>]) {
         let mut qualif_map = DefIdMap();
 
         // First, visit `const` items, potentially recursing, to get
@@ -943,6 +948,10 @@ impl<'tcx> MirMapPass<'tcx> for QualifyAndPromoteConstants {
             };
             let param_env = ty::ParameterEnvironment::for_item(tcx, id);
 
+            for hook in &mut *hooks {
+                hook.on_mir_pass(tcx, src, mir, self, false);
+            }
+
             if mode == Mode::Fn || mode == Mode::ConstFn {
                 // This is ugly because Qualifier holds onto mir,
                 // which can't be mutated until its scope ends.
@@ -970,6 +979,10 @@ impl<'tcx> MirMapPass<'tcx> for QualifyAndPromoteConstants {
                 qualifier.qualify_const();
             }
 
+            for hook in &mut *hooks {
+                hook.on_mir_pass(tcx, src, mir, self, true);
+            }
+
             // Statics must be Sync.
             if mode == Mode::Static {
                 let ty = mir.return_ty.unwrap();
diff --git a/src/librustc_mir/transform/remove_dead_blocks.rs b/src/librustc_mir/transform/remove_dead_blocks.rs
deleted file mode 100644
index 44f3ce7361cf4..0000000000000
--- a/src/librustc_mir/transform/remove_dead_blocks.rs
+++ /dev/null
@@ -1,86 +0,0 @@
-// 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.
-
-//! A pass that erases the contents of dead blocks. This pass must
-//! run before any analysis passes because some of the dead blocks
-//! can be ill-typed.
-//!
-//! The main problem is that typeck lets most blocks whose end is not
-//! reachable have an arbitrary return type, rather than having the
-//! usual () return type (as a note, typeck's notion of reachability
-//! is in fact slightly weaker than MIR CFG reachability - see #31617).
-//!
-//! A standard example of the situation is:
-//! ```rust
-//!   fn example() {
-//!       let _a: char = { return; };
-//!   }
-//! ```
-//!
-//! Here the block (`{ return; }`) has the return type `char`,
-//! rather than `()`, but the MIR we naively generate still contains
-//! the `_a = ()` write in the unreachable block "after" the return.
-//!
-//! As we have to run this pass even when we want to debug the MIR,
-//! this pass just replaces the blocks with empty "return" blocks
-//! and does not renumber anything.
-
-use rustc_data_structures::bitvec::BitVector;
-use rustc::ty::TyCtxt;
-use rustc::mir::repr::*;
-use rustc::mir::transform::{Pass, MirPass, MirSource};
-
-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());
-        // This block is always required.
-        seen.insert(START_BLOCK.index());
-
-        let mut worklist = Vec::with_capacity(4);
-        worklist.push(START_BLOCK);
-        while let Some(bb) = worklist.pop() {
-            for succ in mir.basic_block_data(bb).terminator().successors().iter() {
-                if seen.insert(succ.index()) {
-                    worklist.push(*succ);
-                }
-            }
-        }
-        retain_basic_blocks(mir, &seen);
-    }
-}
-
-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 mut replacements: Vec<_> = (0..num_blocks).map(BasicBlock::new).collect();
-    let mut used_blocks = 0;
-    for alive_index in keep.iter() {
-        replacements[alive_index] = BasicBlock::new(used_blocks);
-        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);
-        }
-        used_blocks += 1;
-    }
-    mir.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() {
-            *target = replacements[target.index()];
-        }
-    }
-}
diff --git a/src/librustc_mir/transform/simplify_branches.rs b/src/librustc_mir/transform/simplify_branches.rs
new file mode 100644
index 0000000000000..f93de6ca382bb
--- /dev/null
+++ b/src/librustc_mir/transform/simplify_branches.rs
@@ -0,0 +1,63 @@
+// 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.
+
+//! A pass that simplifies branches when their condition is known.
+
+use rustc::ty::TyCtxt;
+use rustc::middle::const_val::ConstVal;
+use rustc::mir::transform::{MirPass, MirSource, Pass};
+use rustc::mir::repr::*;
+
+use std::fmt;
+
+pub struct SimplifyBranches<'a> { label: &'a str }
+
+impl<'a> SimplifyBranches<'a> {
+    pub fn new(label: &'a str) -> Self {
+        SimplifyBranches { label: label }
+    }
+}
+
+impl<'l, 'tcx> MirPass<'tcx> for SimplifyBranches<'l> {
+    fn run_pass<'a>(&mut self, _tcx: TyCtxt<'a, 'tcx, 'tcx>, _src: MirSource, mir: &mut Mir<'tcx>) {
+        for block in mir.basic_blocks_mut() {
+            let terminator = block.terminator_mut();
+            terminator.kind = match terminator.kind {
+                TerminatorKind::If { ref targets, cond: Operand::Constant(Constant {
+                    literal: Literal::Value {
+                        value: ConstVal::Bool(cond)
+                    }, ..
+                }) } => {
+                    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 => {
+                    TerminatorKind::Goto { target: target }
+                }
+
+                _ => continue
+            };
+        }
+    }
+}
+
+impl<'l> Pass for SimplifyBranches<'l> {
+    fn disambiguator<'a>(&'a self) -> Option<Box<fmt::Display+'a>> {
+        Some(Box::new(self.label))
+    }
+}
diff --git a/src/librustc_mir/transform/simplify_cfg.rs b/src/librustc_mir/transform/simplify_cfg.rs
index 3a0055c564feb..8bbcc3fe2d70f 100644
--- a/src/librustc_mir/transform/simplify_cfg.rs
+++ b/src/librustc_mir/transform/simplify_cfg.rs
@@ -8,197 +8,239 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+//! A pass that removes various redundancies in the CFG. It should be
+//! called after every significant CFG modification to tidy things
+//! up.
+//!
+//! This pass must also be run before any analysis passes because it removes
+//! dead blocks, and some of these can be ill-typed.
+//!
+//! The cause of that is that typeck lets most blocks whose end is not
+//! reachable have an arbitrary return type, rather than having the
+//! usual () return type (as a note, typeck's notion of reachability
+//! is in fact slightly weaker than MIR CFG reachability - see #31617).
+//!
+//! A standard example of the situation is:
+//! ```rust
+//!   fn example() {
+//!       let _a: char = { return; };
+//!   }
+//! ```
+//!
+//! Here the block (`{ return; }`) has the return type `char`,
+//! rather than `()`, but the MIR we naively generate still contains
+//! the `_a = ()` write in the unreachable block "after" the return.
+
+
 use rustc_data_structures::bitvec::BitVector;
-use rustc::middle::const_val::ConstVal;
+use rustc_data_structures::indexed_vec::{Idx, IndexVec};
 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 std::fmt;
 
-pub struct SimplifyCfg;
+pub struct SimplifyCfg<'a> { label: &'a str }
 
-impl SimplifyCfg {
-    pub fn new() -> SimplifyCfg {
-        SimplifyCfg
+impl<'a> SimplifyCfg<'a> {
+    pub fn new(label: &'a str) -> Self {
+        SimplifyCfg { label: label }
     }
 }
 
-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);
+impl<'l, 'tcx> MirPass<'tcx> for SimplifyCfg<'l> {
+    fn run_pass<'a>(&mut self, _tcx: TyCtxt<'a, 'tcx, 'tcx>, _src: MirSource, mir: &mut Mir<'tcx>) {
+        CfgSimplifier::new(mir).simplify();
+        remove_dead_blocks(mir);
 
         // FIXME: Should probably be moved into some kind of pass manager
-        mir.basic_blocks.shrink_to_fit();
+        mir.basic_blocks_mut().raw.shrink_to_fit();
     }
 }
 
-impl Pass for SimplifyCfg {}
+impl<'l> Pass for SimplifyCfg<'l> {
+    fn disambiguator<'a>(&'a self) -> Option<Box<fmt::Display+'a>> {
+        Some(Box::new(self.label))
+    }
+}
 
-fn merge_consecutive_blocks(mir: &mut Mir) {
-    // Build the precedecessor map for the MIR
-    let mut pred_count = vec![0u32; mir.basic_blocks.len()];
-    for (_, data) in traversal::preorder(mir) {
-        if let Some(ref term) = data.terminator {
-            for &tgt in term.successors().iter() {
-                pred_count[tgt.index()] += 1;
+pub struct CfgSimplifier<'a, 'tcx: 'a> {
+    basic_blocks: &'a mut IndexVec<BasicBlock, BasicBlockData<'tcx>>,
+    pred_count: IndexVec<BasicBlock, u32>
+}
+
+impl<'a, 'tcx: 'a> CfgSimplifier<'a, 'tcx> {
+    fn new(mir: &'a mut Mir<'tcx>) -> Self {
+        let mut pred_count = IndexVec::from_elem(0u32, mir.basic_blocks());
+
+        // we can't use mir.predecessors() here because that counts
+        // dead blocks, which we don't want to.
+        for (_, data) in traversal::preorder(mir) {
+            if let Some(ref term) = data.terminator {
+                for &tgt in term.successors().iter() {
+                    pred_count[tgt] += 1;
+                }
             }
         }
+
+        let basic_blocks = mir.basic_blocks_mut();
+
+        CfgSimplifier {
+            basic_blocks: basic_blocks,
+            pred_count: pred_count
+        }
     }
 
-    loop {
-        let mut changed = false;
-        let mut seen = BitVector::new(mir.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
-            let mut terminator = mir.basic_block_data_mut(bb).terminator.take()
-                .expect("invalid terminator state");
-
-            // See if we can merge the target block into this one
-            loop {
-                let mut inner_change = false;
-
-                if let TerminatorKind::Goto { target } = terminator.kind {
-                    // Don't bother trying to merge a block into itself
-                    if target == bb {
-                        break;
-                    }
-
-                    let num_insts = mir.basic_block_data(target).statements.len();
-                    match mir.basic_block_data(target).terminator().kind {
-                        TerminatorKind::Goto { target: new_target } if num_insts == 0 => {
-                            inner_change = true;
-                            terminator.kind = TerminatorKind::Goto { target: new_target };
-                            pred_count[target.index()] -= 1;
-                            pred_count[new_target.index()] += 1;
-                        }
-                        _ if pred_count[target.index()] == 1 => {
-                            inner_change = true;
-                            let mut stmts = Vec::new();
-                            {
-                                let target_data = mir.basic_block_data_mut(target);
-                                mem::swap(&mut stmts, &mut target_data.statements);
-                                mem::swap(&mut terminator, target_data.terminator_mut());
-                            }
-
-                            mir.basic_block_data_mut(bb).statements.append(&mut stmts);
-                        }
-                        _ => {}
-                    };
+    fn simplify(mut self) {
+        loop {
+            let mut changed = false;
+
+            for bb in (0..self.basic_blocks.len()).map(BasicBlock::new) {
+                if self.pred_count[bb] == 0 {
+                    continue
                 }
 
-                for target in terminator.successors_mut() {
-                    let new_target = match final_target(mir, *target) {
-                        Some(new_target) => new_target,
-                        None if mir.basic_block_data(bb).statements.is_empty() => bb,
-                        None => continue
-                    };
-                    if *target != new_target {
-                        inner_change = true;
-                        pred_count[target.index()] -= 1;
-                        pred_count[new_target.index()] += 1;
-                        *target = new_target;
-                    }
+                debug!("simplifying {:?}", bb);
+
+                let mut terminator = self.basic_blocks[bb].terminator.take()
+                    .expect("invalid terminator state");
+
+                for successor in terminator.successors_mut() {
+                    self.collapse_goto_chain(successor, &mut changed);
                 }
 
-                changed |= inner_change;
-                if !inner_change {
-                    break;
+                let mut new_stmts = vec![];
+                let mut inner_changed = true;
+                while inner_changed {
+                    inner_changed = false;
+                    inner_changed |= self.simplify_branch(&mut terminator);
+                    inner_changed |= self.merge_successor(&mut new_stmts, &mut terminator);
+                    changed |= inner_changed;
                 }
-            }
 
-            mir.basic_block_data_mut(bb).terminator = Some(terminator);
+                self.basic_blocks[bb].statements.extend(new_stmts);
+                self.basic_blocks[bb].terminator = Some(terminator);
 
-            for succ in mir.basic_block_data(bb).terminator().successors().iter() {
-                if seen.insert(succ.index()) {
-                    worklist.push(*succ);
-                }
+                changed |= inner_changed;
             }
-        }
 
-        if !changed {
-            break;
+            if !changed { break }
         }
     }
-}
 
-// Find the target at the end of the jump chain, return None if there is a loop
-fn final_target(mir: &Mir, mut target: BasicBlock) -> Option<BasicBlock> {
-    // Keep track of already seen blocks to detect loops
-    let mut seen: Vec<BasicBlock> = Vec::with_capacity(8);
-
-    while mir.basic_block_data(target).statements.is_empty() {
-        // NB -- terminator may have been swapped with `None` in
-        // merge_consecutive_blocks, in which case we have a cycle and just want
-        // to stop
-        match mir.basic_block_data(target).terminator {
-            Some(Terminator { kind: TerminatorKind::Goto { target: next }, .. }) =>  {
-                if seen.contains(&next) {
-                    return None;
-                }
-                seen.push(next);
-                target = next;
+    // Collapse a goto chain starting from `start`
+    fn collapse_goto_chain(&mut self, start: &mut BasicBlock, changed: &mut bool) {
+        let mut terminator = match self.basic_blocks[*start] {
+            BasicBlockData {
+                ref statements,
+                terminator: ref mut terminator @ Some(Terminator {
+                    kind: TerminatorKind::Goto { .. }, ..
+                }), ..
+            } if statements.is_empty() => terminator.take(),
+            // if `terminator` is None, this means we are in a loop. In that
+            // case, let all the loop collapse to its entry.
+            _ => return
+        };
+
+        let target = match terminator {
+            Some(Terminator { kind: TerminatorKind::Goto { ref mut target }, .. }) => {
+                self.collapse_goto_chain(target, changed);
+                *target
             }
-            _ => break
-        }
-    }
+            _ => unreachable!()
+        };
+        self.basic_blocks[*start].terminator = terminator;
 
-    Some(target)
-}
+        debug!("collapsing goto chain from {:?} to {:?}", *start, 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 }
-                }
+        *changed |= *start != target;
+        self.pred_count[target] += 1;
+        self.pred_count[*start] -= 1;
+        *start = target;
+    }
 
-                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 }
-                    }
-                }
+    // merge a block with 1 `goto` predecessor to its parent
+    fn merge_successor(&mut self,
+                       new_stmts: &mut Vec<Statement<'tcx>>,
+                       terminator: &mut Terminator<'tcx>)
+                       -> bool
+    {
+        let target = match terminator.kind {
+            TerminatorKind::Goto { target }
+                if self.pred_count[target] == 1
+                => target,
+            _ => return false
+        };
+
+        debug!("merging block {:?} into {:?}", target, terminator);
+        *terminator = match self.basic_blocks[target].terminator.take() {
+            Some(terminator) => terminator,
+            None => {
+                // unreachable loop - this should not be possible, as we
+                // don't strand blocks, but handle it correctly.
+                return false
+            }
+        };
+        new_stmts.extend(self.basic_blocks[target].statements.drain(..));
+        self.pred_count[target] = 0;
 
-                TerminatorKind::Assert { target, cond: Operand::Constant(Constant {
-                    literal: Literal::Value {
-                        value: ConstVal::Bool(cond)
-                    }, ..
-                }), expected, .. } if cond == expected => {
-                    changed = true;
-                    TerminatorKind::Goto { target: target }
-                }
+        true
+    }
 
-                TerminatorKind::SwitchInt { ref targets, .. } if targets.len() == 1 => {
-                    changed = true;
-                    TerminatorKind::Goto { target: targets[0] }
+    // turn a branch with all successors identical to a goto
+    fn simplify_branch(&mut self, terminator: &mut Terminator<'tcx>) -> bool {
+        match terminator.kind {
+            TerminatorKind::If { .. } |
+            TerminatorKind::Switch { .. } |
+            TerminatorKind::SwitchInt { .. } => {},
+            _ => return false
+        };
+
+        let first_succ = {
+            let successors = terminator.successors();
+            if let Some(&first_succ) = terminator.successors().get(0) {
+                if successors.iter().all(|s| *s == first_succ) {
+                    self.pred_count[first_succ] -= (successors.len()-1) as u32;
+                    first_succ
+                } else {
+                    return false
                 }
-                _ => continue
+            } else {
+                return false
             }
+        };
+
+        debug!("simplifying branch {:?}", terminator);
+        terminator.kind = TerminatorKind::Goto { target: first_succ };
+        true
+    }
+}
+
+fn remove_dead_blocks(mir: &mut Mir) {
+    let mut seen = BitVector::new(mir.basic_blocks().len());
+    for (bb, _) in traversal::preorder(mir) {
+        seen.insert(bb.index());
+    }
+
+    let basic_blocks = mir.basic_blocks_mut();
+
+    let num_blocks = basic_blocks.len();
+    let mut replacements : Vec<_> = (0..num_blocks).map(BasicBlock::new).collect();
+    let mut used_blocks = 0;
+    for alive_index in seen.iter() {
+        replacements[alive_index] = BasicBlock::new(used_blocks);
+        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.
+            basic_blocks.raw.swap(alive_index, used_blocks);
         }
+        used_blocks += 1;
+    }
+    basic_blocks.raw.truncate(used_blocks);
 
-        if !changed {
-            break;
+    for block in basic_blocks {
+        for target in block.terminator_mut().successors_mut() {
+            *target = replacements[target.index()];
         }
     }
 }
diff --git a/src/librustc_mir/transform/type_check.rs b/src/librustc_mir/transform/type_check.rs
index c38f1f1e6c0fa..e4398fcab3163 100644
--- a/src/librustc_mir/transform/type_check.rs
+++ b/src/librustc_mir/transform/type_check.rs
@@ -24,6 +24,8 @@ use rustc::mir::visit::{self, Visitor};
 use std::fmt;
 use syntax::codemap::{Span, DUMMY_SP};
 
+use rustc_data_structures::indexed_vec::Idx;
+
 macro_rules! span_mirbug {
     ($context:expr, $elem:expr, $($message:tt)*) => ({
         $context.tcx().sess.span_warn(
@@ -129,11 +131,9 @@ impl<'a, 'b, 'gcx, 'tcx> TypeVerifier<'a, 'b, 'gcx, 'tcx> {
     fn sanitize_lvalue(&mut self, lvalue: &Lvalue<'tcx>) -> LvalueTy<'tcx> {
         debug!("sanitize_lvalue: {:?}", lvalue);
         match *lvalue {
-            Lvalue::Var(index) => LvalueTy::Ty { ty: self.mir.var_decls[index as usize].ty },
-            Lvalue::Temp(index) =>
-                LvalueTy::Ty { ty: self.mir.temp_decls[index as usize].ty },
-            Lvalue::Arg(index) =>
-                LvalueTy::Ty { ty: self.mir.arg_decls[index as usize].ty },
+            Lvalue::Var(index) => LvalueTy::Ty { ty: self.mir.var_decls[index].ty },
+            Lvalue::Temp(index) => LvalueTy::Ty { ty: self.mir.temp_decls[index].ty },
+            Lvalue::Arg(index) => LvalueTy::Ty { ty: self.mir.arg_decls[index].ty },
             Lvalue::Static(def_id) =>
                 LvalueTy::Ty { ty: self.tcx().lookup_item_type(def_id).ty },
             Lvalue::ReturnPointer => {
@@ -379,6 +379,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
             TerminatorKind::Goto { .. } |
             TerminatorKind::Resume |
             TerminatorKind::Return |
+            TerminatorKind::Unreachable |
             TerminatorKind::Drop { .. } => {
                 // no checks needed for these
             }
@@ -595,6 +596,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
                     span_mirbug!(self, block, "return on cleanup block")
                 }
             }
+            TerminatorKind::Unreachable => {}
             TerminatorKind::Drop { target, unwind, .. } |
             TerminatorKind::DropAndReplace { target, unwind, .. } |
             TerminatorKind::Assert { target, cleanup: unwind, .. } => {
@@ -626,7 +628,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
                         bb: BasicBlock,
                         iscleanuppad: bool)
     {
-        if mir.basic_block_data(bb).is_cleanup != iscleanuppad {
+        if mir[bb].is_cleanup != iscleanuppad {
             span_mirbug!(self, ctxt, "cleanuppad mismatch: {:?} should be {:?}",
                          bb, iscleanuppad);
         }
@@ -635,7 +637,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.basic_blocks() {
             for stmt in &block.statements {
                 if stmt.source_info.span != DUMMY_SP {
                     self.last_span = stmt.source_info.span;
diff --git a/src/librustc_trans/base.rs b/src/librustc_trans/base.rs
index b7e8e618abbc4..381bd24d9a4ae 100644
--- a/src/librustc_trans/base.rs
+++ b/src/librustc_trans/base.rs
@@ -1844,7 +1844,10 @@ pub fn trans_closure<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
         attributes::emit_uwtable(llfndecl, true);
     }
 
-    debug!("trans_closure(..., {})", instance);
+    // this is an info! to allow collecting monomorphization statistics
+    // and to allow finding the last function before LLVM aborts from
+    // release builds.
+    info!("trans_closure(..., {})", instance);
 
     let fn_ty = FnType::new(ccx, abi, sig, &[]);
 
diff --git a/src/librustc_trans/debuginfo/create_scope_map.rs b/src/librustc_trans/debuginfo/create_scope_map.rs
index f75f973f68aab..f1d9e2c5a5710 100644
--- a/src/librustc_trans/debuginfo/create_scope_map.rs
+++ b/src/librustc_trans/debuginfo/create_scope_map.rs
@@ -26,6 +26,7 @@ use syntax::codemap::{Span, Pos};
 use syntax::{ast, codemap};
 
 use rustc_data_structures::bitvec::BitVector;
+use rustc_data_structures::indexed_vec::{Idx, IndexVec};
 use rustc::hir::{self, PatKind};
 
 // This procedure builds the *scope map* for a given function, which maps any
@@ -69,9 +70,9 @@ pub fn create_scope_map(cx: &CrateContext,
 
 /// Produce DIScope DIEs for each MIR Scope which has variables defined in it.
 /// If debuginfo is disabled, the returned vector is empty.
-pub fn create_mir_scopes(fcx: &FunctionContext) -> Vec<DIScope> {
+pub fn create_mir_scopes(fcx: &FunctionContext) -> IndexVec<VisibilityScope, DIScope> {
     let mir = fcx.mir.clone().expect("create_mir_scopes: missing MIR for fn");
-    let mut scopes = vec![ptr::null_mut(); mir.visibility_scopes.len()];
+    let mut scopes = IndexVec::from_elem(ptr::null_mut(), &mir.visibility_scopes);
 
     let fn_metadata = match fcx.debug_context {
         FunctionDebugContext::RegularContext(box ref data) => data.fn_metadata,
@@ -101,23 +102,22 @@ fn make_mir_scope(ccx: &CrateContext,
                   has_variables: &BitVector,
                   fn_metadata: DISubprogram,
                   scope: VisibilityScope,
-                  scopes: &mut [DIScope]) {
-    let idx = scope.index();
-    if !scopes[idx].is_null() {
+                  scopes: &mut IndexVec<VisibilityScope, DIScope>) {
+    if !scopes[scope].is_null() {
         return;
     }
 
     let scope_data = &mir.visibility_scopes[scope];
     let parent_scope = if let Some(parent) = scope_data.parent_scope {
         make_mir_scope(ccx, mir, has_variables, fn_metadata, parent, scopes);
-        scopes[parent.index()]
+        scopes[parent]
     } else {
         // The root is the function itself.
-        scopes[idx] = fn_metadata;
+        scopes[scope] = fn_metadata;
         return;
     };
 
-    if !has_variables.contains(idx) {
+    if !has_variables.contains(scope.index()) {
         // Do not create a DIScope if there are no variables
         // defined in this MIR Scope, to avoid debuginfo bloat.
 
@@ -125,14 +125,14 @@ fn make_mir_scope(ccx: &CrateContext,
         // our parent is the root, because we might want to
         // put arguments in the root and not have shadowing.
         if parent_scope != fn_metadata {
-            scopes[idx] = parent_scope;
+            scopes[scope] = parent_scope;
             return;
         }
     }
 
     let loc = span_start(ccx, scope_data.span);
     let file_metadata = file_metadata(ccx, &loc.file.name);
-    scopes[idx] = unsafe {
+    scopes[scope] = unsafe {
         llvm::LLVMDIBuilderCreateLexicalBlock(
             DIB(ccx),
             parent_scope,
diff --git a/src/librustc_trans/mir/analyze.rs b/src/librustc_trans/mir/analyze.rs
index 4476163a7f108..d1c1053ac46b2 100644
--- a/src/librustc_trans/mir/analyze.rs
+++ b/src/librustc_trans/mir/analyze.rs
@@ -12,6 +12,7 @@
 //! which do not.
 
 use rustc_data_structures::bitvec::BitVector;
+use rustc_data_structures::indexed_vec::{Idx, IndexVec};
 use rustc::mir::repr as mir;
 use rustc::mir::repr::TerminatorKind;
 use rustc::mir::visit::{Visitor, LvalueContext};
@@ -94,10 +95,10 @@ impl<'mir, 'bcx, 'tcx> Visitor<'tcx> for TempAnalyzer<'mir, 'bcx, 'tcx> {
         debug!("visit_assign(block={:?}, lvalue={:?}, rvalue={:?})", block, lvalue, rvalue);
 
         match *lvalue {
-            mir::Lvalue::Temp(index) => {
-                self.mark_assigned(index as usize);
+            mir::Lvalue::Temp(temp) => {
+                self.mark_assigned(temp.index());
                 if !rvalue::rvalue_creates_operand(self.mir, self.bcx, rvalue) {
-                    self.mark_as_lvalue(index as usize);
+                    self.mark_as_lvalue(temp.index());
                 }
             }
             _ => {
@@ -115,8 +116,8 @@ impl<'mir, 'bcx, 'tcx> Visitor<'tcx> for TempAnalyzer<'mir, 'bcx, 'tcx> {
 
         // Allow uses of projections of immediate pair fields.
         if let mir::Lvalue::Projection(ref proj) = *lvalue {
-            if let mir::Lvalue::Temp(index) = proj.base {
-                let ty = self.mir.temp_decls[index as usize].ty;
+            if let mir::Lvalue::Temp(temp) = proj.base {
+                let ty = self.mir.temp_decls[temp].ty;
                 let ty = self.bcx.monomorphize(&ty);
                 if common::type_is_imm_pair(self.bcx.ccx(), ty) {
                     if let mir::ProjectionElem::Field(..) = proj.elem {
@@ -129,10 +130,10 @@ impl<'mir, 'bcx, 'tcx> Visitor<'tcx> for TempAnalyzer<'mir, 'bcx, 'tcx> {
         }
 
         match *lvalue {
-            mir::Lvalue::Temp(index) => {
+            mir::Lvalue::Temp(temp) => {
                 match context {
                     LvalueContext::Call => {
-                        self.mark_assigned(index as usize);
+                        self.mark_assigned(temp.index());
                     }
                     LvalueContext::Consume => {
                     }
@@ -142,7 +143,7 @@ impl<'mir, 'bcx, 'tcx> Visitor<'tcx> for TempAnalyzer<'mir, 'bcx, 'tcx> {
                     LvalueContext::Borrow { .. } |
                     LvalueContext::Slice { .. } |
                     LvalueContext::Projection => {
-                        self.mark_as_lvalue(index as usize);
+                        self.mark_as_lvalue(temp.index());
                     }
                 }
             }
@@ -163,15 +164,16 @@ pub enum CleanupKind {
 
 pub fn cleanup_kinds<'bcx,'tcx>(_bcx: Block<'bcx,'tcx>,
                                 mir: &mir::Mir<'tcx>)
-                                -> Vec<CleanupKind>
+                                -> IndexVec<mir::BasicBlock, CleanupKind>
 {
-    fn discover_masters<'tcx>(result: &mut [CleanupKind], mir: &mir::Mir<'tcx>) {
-        for bb in mir.all_basic_blocks() {
-            let data = mir.basic_block_data(bb);
+    fn discover_masters<'tcx>(result: &mut IndexVec<mir::BasicBlock, CleanupKind>,
+                              mir: &mir::Mir<'tcx>) {
+        for (bb, data) in mir.basic_blocks().iter_enumerated() {
             match data.terminator().kind {
                 TerminatorKind::Goto { .. } |
                 TerminatorKind::Resume |
                 TerminatorKind::Return |
+                TerminatorKind::Unreachable |
                 TerminatorKind::If { .. } |
                 TerminatorKind::Switch { .. } |
                 TerminatorKind::SwitchInt { .. } => {
@@ -184,19 +186,19 @@ pub fn cleanup_kinds<'bcx,'tcx>(_bcx: Block<'bcx,'tcx>,
                     if let Some(unwind) = unwind {
                         debug!("cleanup_kinds: {:?}/{:?} registering {:?} as funclet",
                                bb, data, unwind);
-                        result[unwind.index()] = CleanupKind::Funclet;
+                        result[unwind] = CleanupKind::Funclet;
                     }
                 }
             }
         }
     }
 
-    fn propagate<'tcx>(result: &mut [CleanupKind], mir: &mir::Mir<'tcx>) {
-        let mut funclet_succs : Vec<_> =
-            mir.all_basic_blocks().iter().map(|_| None).collect();
+    fn propagate<'tcx>(result: &mut IndexVec<mir::BasicBlock, CleanupKind>,
+                       mir: &mir::Mir<'tcx>) {
+        let mut funclet_succs = IndexVec::from_elem(None, mir.basic_blocks());
 
         let mut set_successor = |funclet: mir::BasicBlock, succ| {
-            match funclet_succs[funclet.index()] {
+            match funclet_succs[funclet] {
                 ref mut s @ None => {
                     debug!("set_successor: updating successor of {:?} to {:?}",
                            funclet, succ);
@@ -210,22 +212,22 @@ pub fn cleanup_kinds<'bcx,'tcx>(_bcx: Block<'bcx,'tcx>,
         };
 
         for (bb, data) in traversal::reverse_postorder(mir) {
-            let funclet = match result[bb.index()] {
+            let funclet = match result[bb] {
                 CleanupKind::NotCleanup => continue,
                 CleanupKind::Funclet => bb,
                 CleanupKind::Internal { funclet } => funclet,
             };
 
             debug!("cleanup_kinds: {:?}/{:?}/{:?} propagating funclet {:?}",
-                   bb, data, result[bb.index()], funclet);
+                   bb, data, result[bb], funclet);
 
             for &succ in data.terminator().successors().iter() {
-                let kind = result[succ.index()];
+                let kind = result[succ];
                 debug!("cleanup_kinds: propagating {:?} to {:?}/{:?}",
                        funclet, succ, kind);
                 match kind {
                     CleanupKind::NotCleanup => {
-                        result[succ.index()] = CleanupKind::Internal { funclet: funclet };
+                        result[succ] = CleanupKind::Internal { funclet: funclet };
                     }
                     CleanupKind::Funclet => {
                         set_successor(funclet, succ);
@@ -237,7 +239,7 @@ pub fn cleanup_kinds<'bcx,'tcx>(_bcx: Block<'bcx,'tcx>,
 
                             debug!("promoting {:?} to a funclet and updating {:?}", succ,
                                    succ_funclet);
-                            result[succ.index()] = CleanupKind::Funclet;
+                            result[succ] = CleanupKind::Funclet;
                             set_successor(succ_funclet, succ);
                             set_successor(funclet, succ);
                         }
@@ -247,8 +249,7 @@ pub fn cleanup_kinds<'bcx,'tcx>(_bcx: Block<'bcx,'tcx>,
         }
     }
 
-    let mut result : Vec<_> =
-        mir.all_basic_blocks().iter().map(|_| CleanupKind::NotCleanup).collect();
+    let mut result = IndexVec::from_elem(CleanupKind::NotCleanup, mir.basic_blocks());
 
     discover_masters(&mut result, mir);
     propagate(&mut result, mir);
diff --git a/src/librustc_trans/mir/block.rs b/src/librustc_trans/mir/block.rs
index 88c3fede1e1ed..bdcf3dd8cd418 100644
--- a/src/librustc_trans/mir/block.rs
+++ b/src/librustc_trans/mir/block.rs
@@ -43,7 +43,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
     pub fn trans_block(&mut self, bb: mir::BasicBlock) {
         let mut bcx = self.bcx(bb);
         let mir = self.mir.clone();
-        let data = mir.basic_block_data(bb);
+        let data = &mir[bb];
 
         debug!("trans_block({:?}={:?})", bb, data);
 
@@ -52,9 +52,9 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
         let cleanup_bundle = bcx.lpad().and_then(|l| l.bundle());
 
         let funclet_br = |this: &Self, bcx: BlockAndBuilder, bb: mir::BasicBlock| {
-            let lltarget = this.blocks[bb.index()].llbb;
+            let lltarget = this.blocks[bb].llbb;
             if let Some(cp) = cleanup_pad {
-                match this.cleanup_kind(bb) {
+                match this.cleanup_kinds[bb] {
                     CleanupKind::Funclet => {
                         // micro-optimization: generate a `ret` rather than a jump
                         // to a return block
@@ -69,10 +69,10 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
         };
 
         let llblock = |this: &mut Self, target: mir::BasicBlock| {
-            let lltarget = this.blocks[target.index()].llbb;
+            let lltarget = this.blocks[target].llbb;
 
             if let Some(cp) = cleanup_pad {
-                match this.cleanup_kind(target) {
+                match this.cleanup_kinds[target] {
                     CleanupKind::Funclet => {
                         // MSVC cross-funclet jump - need a trampoline
 
@@ -89,7 +89,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                 }
             } else {
                 if let (CleanupKind::NotCleanup, CleanupKind::Funclet) =
-                    (this.cleanup_kind(bb), this.cleanup_kind(target))
+                    (this.cleanup_kinds[bb], this.cleanup_kinds[target])
                 {
                     // jump *into* cleanup - need a landing pad if GNU
                     this.landing_pad_to(target).llbb
@@ -191,6 +191,10 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                 })
             }
 
+            mir::TerminatorKind::Unreachable => {
+                bcx.unreachable();
+            }
+
             mir::TerminatorKind::Drop { ref location, target, unwind } => {
                 let lvalue = self.trans_lvalue(&bcx, location);
                 let ty = lvalue.ty.to_ty(bcx.tcx());
@@ -209,7 +213,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                 if let Some(unwind) = unwind {
                     bcx.invoke(drop_fn,
                                &[llvalue],
-                               self.blocks[target.index()].llbb,
+                               self.blocks[target].llbb,
                                llblock(self, unwind),
                                cleanup_bundle);
                 } else {
@@ -488,7 +492,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                 // Many different ways to call a function handled here
                 if let &Some(cleanup) = cleanup {
                     let ret_bcx = if let Some((_, target)) = *destination {
-                        self.blocks[target.index()]
+                        self.blocks[target]
                     } else {
                         self.unreachable_block()
                     };
@@ -693,27 +697,23 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
         }
     }
 
-    fn cleanup_kind(&self, bb: mir::BasicBlock) -> CleanupKind {
-        self.cleanup_kinds[bb.index()]
-    }
-
     /// Return the landingpad wrapper around the given basic block
     ///
     /// No-op in MSVC SEH scheme.
     fn landing_pad_to(&mut self, target_bb: mir::BasicBlock) -> Block<'bcx, 'tcx>
     {
-        if let Some(block) = self.landing_pads[target_bb.index()] {
+        if let Some(block) = self.landing_pads[target_bb] {
             return block;
         }
 
         if base::wants_msvc_seh(self.fcx.ccx.sess()) {
-            return self.blocks[target_bb.index()];
+            return self.blocks[target_bb];
         }
 
         let target = self.bcx(target_bb);
 
         let block = self.fcx.new_block("cleanup", None);
-        self.landing_pads[target_bb.index()] = Some(block);
+        self.landing_pads[target_bb] = Some(block);
 
         let bcx = block.build();
         let ccx = bcx.ccx();
@@ -729,10 +729,10 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
 
     pub fn init_cpad(&mut self, bb: mir::BasicBlock) {
         let bcx = self.bcx(bb);
-        let data = self.mir.basic_block_data(bb);
+        let data = &self.mir[bb];
         debug!("init_cpad({:?})", data);
 
-        match self.cleanup_kinds[bb.index()] {
+        match self.cleanup_kinds[bb] {
             CleanupKind::NotCleanup => {
                 bcx.set_lpad(None)
             }
@@ -763,7 +763,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
     }
 
     fn bcx(&self, bb: mir::BasicBlock) -> BlockAndBuilder<'bcx, 'tcx> {
-        self.blocks[bb.index()].build()
+        self.blocks[bb].build()
     }
 
     fn make_return_dest(&mut self, bcx: &BlockAndBuilder<'bcx, 'tcx>,
@@ -776,7 +776,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
         let dest = match *dest {
             mir::Lvalue::Temp(idx) => {
                 let ret_ty = self.lvalue_ty(dest);
-                match self.temps[idx as usize] {
+                match self.temps[idx] {
                     TempRef::Lvalue(dest) => dest,
                     TempRef::Operand(None) => {
                         // Handle temporary lvalues, specifically Operand ones, as
@@ -838,6 +838,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
         self.store_operand(bcx, cast_ptr, val);
     }
 
+
     // Stores the return value of a function call into it's final location.
     fn store_return(&mut self,
                     bcx: &BlockAndBuilder<'bcx, 'tcx>,
@@ -851,7 +852,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
             Store(dst) => ret_ty.store(bcx, op.immediate(), dst),
             IndirectOperand(tmp, idx) => {
                 let op = self.trans_load(bcx, tmp, op.ty);
-                self.temps[idx as usize] = TempRef::Operand(Some(op));
+                self.temps[idx] = TempRef::Operand(Some(op));
             }
             DirectOperand(idx) => {
                 // If there is a cast, we have to store and reload.
@@ -864,7 +865,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                 } else {
                     op.unpack_if_pair(bcx)
                 };
-                self.temps[idx as usize] = TempRef::Operand(Some(op));
+                self.temps[idx] = TempRef::Operand(Some(op));
             }
         }
     }
@@ -876,7 +877,7 @@ enum ReturnDest {
     // Store the return value to the pointer
     Store(ValueRef),
     // Stores an indirect return value to an operand temporary lvalue
-    IndirectOperand(ValueRef, u32),
+    IndirectOperand(ValueRef, mir::Temp),
     // Stores a direct return value to an operand temporary lvalue
-    DirectOperand(u32)
+    DirectOperand(mir::Temp)
 }
diff --git a/src/librustc_trans/mir/constant.rs b/src/librustc_trans/mir/constant.rs
index 4e0cf729d4cd6..316c7341ef142 100644
--- a/src/librustc_trans/mir/constant.rs
+++ b/src/librustc_trans/mir/constant.rs
@@ -22,6 +22,7 @@ use rustc::traits;
 use rustc::ty::{self, Ty, TyCtxt, TypeFoldable};
 use rustc::ty::cast::{CastTy, IntTy};
 use rustc::ty::subst::Substs;
+use rustc_data_structures::indexed_vec::{Idx, IndexVec};
 use {abi, adt, base, Disr};
 use callee::Callee;
 use common::{self, BlockAndBuilder, CrateContext, const_get_elt, val_ty};
@@ -203,13 +204,13 @@ struct MirConstContext<'a, 'tcx: 'a> {
     substs: &'tcx Substs<'tcx>,
 
     /// Arguments passed to a const fn.
-    args: Vec<Const<'tcx>>,
+    args: IndexVec<mir::Arg, Const<'tcx>>,
 
     /// Variable values - specifically, argument bindings of a const fn.
-    vars: Vec<Option<Const<'tcx>>>,
+    vars: IndexVec<mir::Var, Option<Const<'tcx>>>,
 
     /// Temp values.
-    temps: Vec<Option<Const<'tcx>>>,
+    temps: IndexVec<mir::Temp, Option<Const<'tcx>>>,
 
     /// Value assigned to Return, which is the resulting constant.
     return_value: Option<Const<'tcx>>
@@ -220,22 +221,22 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> {
     fn new(ccx: &'a CrateContext<'a, 'tcx>,
            mir: &'a mir::Mir<'tcx>,
            substs: &'tcx Substs<'tcx>,
-           args: Vec<Const<'tcx>>)
+           args: IndexVec<mir::Arg, Const<'tcx>>)
            -> MirConstContext<'a, 'tcx> {
         MirConstContext {
             ccx: ccx,
             mir: mir,
             substs: substs,
             args: args,
-            vars: vec![None; mir.var_decls.len()],
-            temps: vec![None; mir.temp_decls.len()],
+            vars: IndexVec::from_elem(None, &mir.var_decls),
+            temps: IndexVec::from_elem(None, &mir.temp_decls),
             return_value: None
         }
     }
 
     fn trans_def(ccx: &'a CrateContext<'a, 'tcx>,
                  mut instance: Instance<'tcx>,
-                 args: Vec<Const<'tcx>>)
+                 args: IndexVec<mir::Arg, Const<'tcx>>)
                  -> Result<Const<'tcx>, ConstEvalFailure> {
         // Try to resolve associated constants.
         if instance.substs.self_ty().is_some() {
@@ -279,7 +280,7 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> {
         let mut failure = Ok(());
 
         loop {
-            let data = self.mir.basic_block_data(bb);
+            let data = &self.mir[bb];
             for statement in &data.statements {
                 let span = statement.source_info.span;
                 match statement.kind {
@@ -342,10 +343,10 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> {
                                        func, fn_ty)
                     };
 
-                    let mut const_args = Vec::with_capacity(args.len());
+                    let mut const_args = IndexVec::with_capacity(args.len());
                     for arg in args {
                         match self.const_operand(arg, span) {
-                            Ok(arg) => const_args.push(arg),
+                            Ok(arg) => { const_args.push(arg); },
                             Err(err) => if failure.is_ok() { failure = Err(err); }
                         }
                     }
@@ -366,8 +367,8 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> {
 
     fn store(&mut self, dest: &mir::Lvalue<'tcx>, value: Const<'tcx>, span: Span) {
         let dest = match *dest {
-            mir::Lvalue::Var(index) => &mut self.vars[index as usize],
-            mir::Lvalue::Temp(index) => &mut self.temps[index as usize],
+            mir::Lvalue::Var(var) => &mut self.vars[var],
+            mir::Lvalue::Temp(temp) => &mut self.temps[temp],
             mir::Lvalue::ReturnPointer => &mut self.return_value,
             _ => span_bug!(span, "assignment to {:?} in constant", dest)
         };
@@ -378,17 +379,17 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> {
                     -> Result<ConstLvalue<'tcx>, ConstEvalFailure> {
         let tcx = self.ccx.tcx();
         let lvalue = match *lvalue {
-            mir::Lvalue::Var(index) => {
-                self.vars[index as usize].unwrap_or_else(|| {
-                    span_bug!(span, "var{} not initialized", index)
+            mir::Lvalue::Var(var) => {
+                self.vars[var].unwrap_or_else(|| {
+                    span_bug!(span, "{:?} not initialized", var)
                 }).as_lvalue()
             }
-            mir::Lvalue::Temp(index) => {
-                self.temps[index as usize].unwrap_or_else(|| {
-                    span_bug!(span, "tmp{} not initialized", index)
+            mir::Lvalue::Temp(temp) => {
+                self.temps[temp].unwrap_or_else(|| {
+                    span_bug!(span, "{:?} not initialized", temp)
                 }).as_lvalue()
             }
-            mir::Lvalue::Arg(index) => self.args[index as usize].as_lvalue(),
+            mir::Lvalue::Arg(arg) => self.args[arg].as_lvalue(),
             mir::Lvalue::Static(def_id) => {
                 ConstLvalue {
                     base: Base::Static(consts::get_static(self.ccx, def_id).val),
@@ -489,11 +490,11 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> {
 
                         let substs = self.monomorphize(&substs);
                         let instance = Instance::new(def_id, substs);
-                        MirConstContext::trans_def(self.ccx, instance, vec![])
+                        MirConstContext::trans_def(self.ccx, instance, IndexVec::new())
                     }
                     mir::Literal::Promoted { index } => {
                         let mir = &self.mir.promoted[index];
-                        MirConstContext::new(self.ccx, mir, self.substs, vec![]).trans()
+                        MirConstContext::new(self.ccx, mir, self.substs, IndexVec::new()).trans()
                     }
                     mir::Literal::Value { value } => {
                         Ok(Const::from_constval(self.ccx, value, ty))
@@ -914,11 +915,12 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
 
                 let substs = bcx.monomorphize(&substs);
                 let instance = Instance::new(def_id, substs);
-                MirConstContext::trans_def(bcx.ccx(), instance, vec![])
+                MirConstContext::trans_def(bcx.ccx(), instance, IndexVec::new())
             }
             mir::Literal::Promoted { index } => {
                 let mir = &self.mir.promoted[index];
-                MirConstContext::new(bcx.ccx(), mir, bcx.fcx().param_substs, vec![]).trans()
+                MirConstContext::new(bcx.ccx(), mir, bcx.fcx().param_substs,
+                                     IndexVec::new()).trans()
             }
             mir::Literal::Value { value } => {
                 Ok(Const::from_constval(bcx.ccx(), value, ty))
@@ -945,5 +947,5 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
 pub fn trans_static_initializer(ccx: &CrateContext, def_id: DefId)
                                 -> Result<ValueRef, ConstEvalFailure> {
     let instance = Instance::mono(ccx.shared(), def_id);
-    MirConstContext::trans_def(ccx, instance, vec![]).map(|c| c.llval)
+    MirConstContext::trans_def(ccx, instance, IndexVec::new()).map(|c| c.llval)
 }
diff --git a/src/librustc_trans/mir/lvalue.rs b/src/librustc_trans/mir/lvalue.rs
index 523dfef5a2409..0a66a147568e6 100644
--- a/src/librustc_trans/mir/lvalue.rs
+++ b/src/librustc_trans/mir/lvalue.rs
@@ -12,6 +12,7 @@ use llvm::ValueRef;
 use rustc::ty::{self, Ty, TypeFoldable};
 use rustc::mir::repr as mir;
 use rustc::mir::tcx::LvalueTy;
+use rustc_data_structures::indexed_vec::Idx;
 use abi;
 use adt;
 use base;
@@ -90,14 +91,14 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
         let ccx = bcx.ccx();
         let tcx = bcx.tcx();
         let result = match *lvalue {
-            mir::Lvalue::Var(index) => self.vars[index as usize],
-            mir::Lvalue::Temp(index) => match self.temps[index as usize] {
+            mir::Lvalue::Var(var) => self.vars[var],
+            mir::Lvalue::Temp(temp) => match self.temps[temp] {
                 TempRef::Lvalue(lvalue) =>
                     lvalue,
                 TempRef::Operand(..) =>
                     bug!("using operand temp {:?} as lvalue", lvalue),
             },
-            mir::Lvalue::Arg(index) => self.args[index as usize],
+            mir::Lvalue::Arg(arg) => self.args[arg],
             mir::Lvalue::Static(def_id) => {
                 let const_ty = self.lvalue_ty(lvalue);
                 LvalueRef::new_sized(consts::get_static(ccx, def_id).val,
@@ -233,8 +234,8 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
     where F: FnOnce(&mut Self, LvalueRef<'tcx>) -> U
     {
         match *lvalue {
-            mir::Lvalue::Temp(idx) => {
-                match self.temps[idx as usize] {
+            mir::Lvalue::Temp(temp) => {
+                match self.temps[temp] {
                     TempRef::Lvalue(lvalue) => f(self, lvalue),
                     TempRef::Operand(None) => {
                         let lvalue_ty = self.lvalue_ty(lvalue);
@@ -243,7 +244,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                                                        "lvalue_temp");
                         let ret = f(self, lvalue);
                         let op = self.trans_load(bcx, lvalue.llval, lvalue_ty);
-                        self.temps[idx as usize] = TempRef::Operand(Some(op));
+                        self.temps[temp] = TempRef::Operand(Some(op));
                         ret
                     }
                     TempRef::Operand(Some(_)) => {
diff --git a/src/librustc_trans/mir/mod.rs b/src/librustc_trans/mir/mod.rs
index e1fa08fd8b307..1932a2023fac8 100644
--- a/src/librustc_trans/mir/mod.rs
+++ b/src/librustc_trans/mir/mod.rs
@@ -30,6 +30,7 @@ use std::rc::Rc;
 use basic_block::BasicBlock;
 
 use rustc_data_structures::bitvec::BitVector;
+use rustc_data_structures::indexed_vec::{IndexVec, Idx};
 
 pub use self::constant::trans_static_initializer;
 
@@ -71,20 +72,20 @@ pub struct MirContext<'bcx, 'tcx:'bcx> {
     llpersonalityslot: Option<ValueRef>,
 
     /// A `Block` for each MIR `BasicBlock`
-    blocks: Vec<Block<'bcx, 'tcx>>,
+    blocks: IndexVec<mir::BasicBlock, Block<'bcx, 'tcx>>,
 
     /// The funclet status of each basic block
-    cleanup_kinds: Vec<analyze::CleanupKind>,
+    cleanup_kinds: IndexVec<mir::BasicBlock, analyze::CleanupKind>,
 
     /// This stores the landing-pad block for a given BB, computed lazily on GNU
     /// and eagerly on MSVC.
-    landing_pads: Vec<Option<Block<'bcx, 'tcx>>>,
+    landing_pads: IndexVec<mir::BasicBlock, Option<Block<'bcx, 'tcx>>>,
 
     /// Cached unreachable block
     unreachable_block: Option<Block<'bcx, 'tcx>>,
 
     /// An LLVM alloca for each MIR `VarDecl`
-    vars: Vec<LvalueRef<'tcx>>,
+    vars: IndexVec<mir::Var, LvalueRef<'tcx>>,
 
     /// The location where each MIR `TempDecl` is stored. This is
     /// usually an `LvalueRef` representing an alloca, but not always:
@@ -101,20 +102,20 @@ pub struct MirContext<'bcx, 'tcx:'bcx> {
     ///
     /// Avoiding allocs can also be important for certain intrinsics,
     /// notably `expect`.
-    temps: Vec<TempRef<'tcx>>,
+    temps: IndexVec<mir::Temp, TempRef<'tcx>>,
 
     /// The arguments to the function; as args are lvalues, these are
     /// always indirect, though we try to avoid creating an alloca
     /// when we can (and just reuse the pointer the caller provided).
-    args: Vec<LvalueRef<'tcx>>,
+    args: IndexVec<mir::Arg, LvalueRef<'tcx>>,
 
     /// Debug information for MIR scopes.
-    scopes: Vec<DIScope>
+    scopes: IndexVec<mir::VisibilityScope, DIScope>
 }
 
 impl<'blk, 'tcx> MirContext<'blk, 'tcx> {
     pub fn debug_loc(&self, source_info: mir::SourceInfo) -> DebugLoc {
-        DebugLoc::ScopeAt(self.scopes[source_info.scope.index()], source_info.span)
+        DebugLoc::ScopeAt(self.scopes[source_info.scope], source_info.span)
     }
 }
 
@@ -154,8 +155,6 @@ pub fn trans_mir<'blk, 'tcx: 'blk>(fcx: &'blk FunctionContext<'blk, 'tcx>) {
     let bcx = fcx.init(false, None).build();
     let mir = bcx.mir();
 
-    let mir_blocks = mir.all_basic_blocks();
-
     // Analyze the temps to determine which must be lvalues
     // FIXME
     let (lvalue_temps, cleanup_kinds) = bcx.with_block(|bcx| {
@@ -173,7 +172,7 @@ pub fn trans_mir<'blk, 'tcx: 'blk>(fcx: &'blk FunctionContext<'blk, 'tcx>) {
                             .map(|(mty, decl)| {
         let lvalue = LvalueRef::alloca(&bcx, mty, &decl.name.as_str());
 
-        let scope = scopes[decl.source_info.scope.index()];
+        let scope = scopes[decl.source_info.scope];
         if !scope.is_null() && bcx.sess().opts.debuginfo == FullDebugInfo {
             bcx.with_block(|bcx| {
                 declare_local(bcx, decl.name, mty, scope,
@@ -200,19 +199,17 @@ pub fn trans_mir<'blk, 'tcx: 'blk>(fcx: &'blk FunctionContext<'blk, 'tcx>) {
                               .collect();
 
     // Allocate a `Block` for every basic block
-    let block_bcxs: Vec<Block<'blk,'tcx>> =
-        mir_blocks.iter()
-                  .map(|&bb|{
-                      if bb == mir::START_BLOCK {
-                          fcx.new_block("start", None)
-                      } else {
-                          fcx.new_block(&format!("{:?}", bb), None)
-                      }
-                  })
-                  .collect();
+    let block_bcxs: IndexVec<mir::BasicBlock, Block<'blk,'tcx>> =
+        mir.basic_blocks().indices().map(|bb| {
+            if bb == mir::START_BLOCK {
+                fcx.new_block("start", None)
+            } else {
+                fcx.new_block(&format!("{:?}", bb), None)
+            }
+        }).collect();
 
     // Branch to the START block
-    let start_bcx = block_bcxs[mir::START_BLOCK.index()];
+    let start_bcx = block_bcxs[mir::START_BLOCK];
     bcx.br(start_bcx.llbb);
 
     // Up until here, IR instructions for this function have explicitly not been annotated with
@@ -227,14 +224,14 @@ pub fn trans_mir<'blk, 'tcx: 'blk>(fcx: &'blk FunctionContext<'blk, 'tcx>) {
         blocks: block_bcxs,
         unreachable_block: None,
         cleanup_kinds: cleanup_kinds,
-        landing_pads: mir_blocks.iter().map(|_| None).collect(),
+        landing_pads: IndexVec::from_elem(None, mir.basic_blocks()),
         vars: vars,
         temps: temps,
         args: args,
         scopes: scopes
     };
 
-    let mut visited = BitVector::new(mir_blocks.len());
+    let mut visited = BitVector::new(mir.basic_blocks().len());
 
     let mut rpo = traversal::reverse_postorder(&mir);
 
@@ -252,8 +249,8 @@ pub fn trans_mir<'blk, 'tcx: 'blk>(fcx: &'blk FunctionContext<'blk, 'tcx>) {
 
     // Remove blocks that haven't been visited, or have no
     // predecessors.
-    for &bb in &mir_blocks {
-        let block = mircx.blocks[bb.index()];
+    for bb in mir.basic_blocks().indices() {
+        let block = mircx.blocks[bb];
         let block = BasicBlock(block.llbb);
         // Unreachable block
         if !visited.contains(bb.index()) {
@@ -271,15 +268,15 @@ pub fn trans_mir<'blk, 'tcx: 'blk>(fcx: &'blk FunctionContext<'blk, 'tcx>) {
 /// indirect.
 fn arg_value_refs<'bcx, 'tcx>(bcx: &BlockAndBuilder<'bcx, 'tcx>,
                               mir: &mir::Mir<'tcx>,
-                              scopes: &[DIScope])
-                              -> Vec<LvalueRef<'tcx>> {
+                              scopes: &IndexVec<mir::VisibilityScope, DIScope>)
+                              -> IndexVec<mir::Arg, LvalueRef<'tcx>> {
     let fcx = bcx.fcx();
     let tcx = bcx.tcx();
     let mut idx = 0;
     let mut llarg_idx = fcx.fn_ty.ret.is_indirect() as usize;
 
     // Get the argument scope, if it exists and if we need it.
-    let arg_scope = scopes[mir::ARGUMENT_VISIBILITY_SCOPE.index()];
+    let arg_scope = scopes[mir::ARGUMENT_VISIBILITY_SCOPE];
     let arg_scope = if !arg_scope.is_null() && bcx.sess().opts.debuginfo == FullDebugInfo {
         Some(arg_scope)
     } else {
diff --git a/src/librustc_trans/mir/operand.rs b/src/librustc_trans/mir/operand.rs
index c21f112b5f603..80ff0a92d8d46 100644
--- a/src/librustc_trans/mir/operand.rs
+++ b/src/librustc_trans/mir/operand.rs
@@ -11,6 +11,8 @@
 use llvm::ValueRef;
 use rustc::ty::Ty;
 use rustc::mir::repr as mir;
+use rustc_data_structures::indexed_vec::Idx;
+
 use base;
 use common::{self, Block, BlockAndBuilder};
 use value::Value;
@@ -174,7 +176,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                 // watch out for temporaries that do not have an
                 // alloca; they are handled somewhat differently
                 if let &mir::Lvalue::Temp(index) = lvalue {
-                    match self.temps[index as usize] {
+                    match self.temps[index] {
                         TempRef::Operand(Some(o)) => {
                             return o;
                         }
@@ -190,7 +192,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
                 // Moves out of pair fields are trivial.
                 if let &mir::Lvalue::Projection(ref proj) = lvalue {
                     if let mir::Lvalue::Temp(index) = proj.base {
-                        let temp_ref = &self.temps[index as usize];
+                        let temp_ref = &self.temps[index];
                         if let &TempRef::Operand(Some(o)) = temp_ref {
                             match (o.val, &proj.elem) {
                                 (OperandValue::Pair(a, b),
diff --git a/src/librustc_trans/mir/statement.rs b/src/librustc_trans/mir/statement.rs
index 63472d582330f..d592f5ee1b936 100644
--- a/src/librustc_trans/mir/statement.rs
+++ b/src/librustc_trans/mir/statement.rs
@@ -9,6 +9,7 @@
 // except according to those terms.
 
 use rustc::mir::repr as mir;
+
 use common::{self, BlockAndBuilder};
 
 use super::MirContext;
@@ -28,8 +29,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
             mir::StatementKind::Assign(ref lvalue, ref rvalue) => {
                 match *lvalue {
                     mir::Lvalue::Temp(index) => {
-                        let index = index as usize;
-                        match self.temps[index as usize] {
+                        match self.temps[index] {
                             TempRef::Lvalue(tr_dest) => {
                                 self.trans_rvalue(bcx, tr_dest, rvalue, debug_loc)
                             }
diff --git a/src/rustc/Cargo.lock b/src/rustc/Cargo.lock
index 50d9f61e2c160..3e8277e28ba73 100644
--- a/src/rustc/Cargo.lock
+++ b/src/rustc/Cargo.lock
@@ -87,6 +87,7 @@ dependencies = [
  "graphviz 0.0.0",
  "log 0.0.0",
  "rustc 0.0.0",
+ "rustc_data_structures 0.0.0",
  "rustc_mir 0.0.0",
  "syntax 0.0.0",
 ]
diff --git a/src/test/run-pass-fulldeps/auxiliary/dummy_mir_pass.rs b/src/test/run-pass-fulldeps/auxiliary/dummy_mir_pass.rs
index 0abf71ba444ae..604933d40a12c 100644
--- a/src/test/run-pass-fulldeps/auxiliary/dummy_mir_pass.rs
+++ b/src/test/run-pass-fulldeps/auxiliary/dummy_mir_pass.rs
@@ -29,6 +29,7 @@ use rustc_plugin::Registry;
 struct Pass;
 
 impl transform::Pass for Pass {}
+
 impl<'tcx> MirPass<'tcx> for Pass {
     fn run_pass<'a>(&mut self, _: TyCtxt<'a, 'tcx, 'tcx>,
                     _: MirSource, mir: &mut Mir<'tcx>) {