about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/librustc/arena.rs8
-rw-r--r--src/librustc/mir/cache.rs184
-rw-r--r--src/librustc/mir/mod.rs4
-rw-r--r--src/librustc/mir/visit.rs4
-rw-r--r--src/librustc/query/mod.rs26
-rw-r--r--src/librustc/ty/context.rs14
-rw-r--r--src/librustc/ty/mod.rs10
-rw-r--r--src/librustc_codegen_ssa/base.rs3
-rw-r--r--src/librustc_codegen_ssa/mir/analyze.rs16
-rw-r--r--src/librustc_codegen_ssa/mir/block.rs58
-rw-r--r--src/librustc_codegen_ssa/mir/constant.rs2
-rw-r--r--src/librustc_codegen_ssa/mir/mod.rs17
-rw-r--r--src/librustc_codegen_ssa/mir/operand.rs2
-rw-r--r--src/librustc_codegen_ssa/mir/place.rs2
-rw-r--r--src/librustc_codegen_ssa/mir/rvalue.rs4
-rw-r--r--src/librustc_codegen_ssa/mir/statement.rs2
-rw-r--r--src/librustc_metadata/rmeta/decoder.rs7
-rw-r--r--src/librustc_metadata/rmeta/mod.rs4
-rw-r--r--src/librustc_mir/borrow_check/borrow_set.rs6
-rw-r--r--src/librustc_mir/borrow_check/mod.rs21
-rw-r--r--src/librustc_mir/borrow_check/mutability_errors.rs4
-rw-r--r--src/librustc_mir/borrow_check/nll/invalidation.rs4
-rw-r--r--src/librustc_mir/borrow_check/nll/mod.rs20
-rw-r--r--src/librustc_mir/borrow_check/nll/region_infer/values.rs2
-rw-r--r--src/librustc_mir/borrow_check/nll/renumber.rs6
-rw-r--r--src/librustc_mir/borrow_check/nll/type_check/liveness/local_use_map.rs2
-rw-r--r--src/librustc_mir/borrow_check/nll/type_check/liveness/mod.rs4
-rw-r--r--src/librustc_mir/borrow_check/nll/type_check/liveness/polonius.rs2
-rw-r--r--src/librustc_mir/borrow_check/nll/type_check/liveness/trace.rs8
-rw-r--r--src/librustc_mir/borrow_check/nll/type_check/mod.rs18
-rw-r--r--src/librustc_mir/borrow_check/used_muts.rs2
-rw-r--r--src/librustc_mir/build/mod.rs4
-rw-r--r--src/librustc_mir/const_eval.rs4
-rw-r--r--src/librustc_mir/dataflow/impls/storage_liveness.rs8
-rw-r--r--src/librustc_mir/interpret/eval_context.rs6
-rw-r--r--src/librustc_mir/monomorphize/collector.rs8
-rw-r--r--src/librustc_mir/shim.rs25
-rw-r--r--src/librustc_mir/transform/add_call_guards.rs14
-rw-r--r--src/librustc_mir/transform/add_moves_for_packed_drops.rs12
-rw-r--r--src/librustc_mir/transform/add_retag.rs6
-rw-r--r--src/librustc_mir/transform/check_unsafety.rs2
-rw-r--r--src/librustc_mir/transform/cleanup_post_borrowck.rs6
-rw-r--r--src/librustc_mir/transform/const_prop.rs41
-rw-r--r--src/librustc_mir/transform/copy_prop.rs41
-rw-r--r--src/librustc_mir/transform/deaggregator.rs4
-rw-r--r--src/librustc_mir/transform/dump_mir.rs4
-rw-r--r--src/librustc_mir/transform/elaborate_drops.rs10
-rw-r--r--src/librustc_mir/transform/erase_regions.rs4
-rw-r--r--src/librustc_mir/transform/generator.rs215
-rw-r--r--src/librustc_mir/transform/inline.rs46
-rw-r--r--src/librustc_mir/transform/instcombine.rs11
-rw-r--r--src/librustc_mir/transform/mod.rs42
-rw-r--r--src/librustc_mir/transform/no_landing_pads.rs6
-rw-r--r--src/librustc_mir/transform/promote_consts.rs67
-rw-r--r--src/librustc_mir/transform/remove_noop_landing_pads.rs26
-rw-r--r--src/librustc_mir/transform/rustc_peek.rs32
-rw-r--r--src/librustc_mir/transform/simplify.rs46
-rw-r--r--src/librustc_mir/transform/simplify_branches.rs4
-rw-r--r--src/librustc_mir/transform/uniform_array_move_out.rs26
-rw-r--r--src/librustc_mir/util/collect_writes.rs2
-rw-r--r--src/librustc_mir/util/def_use.rs6
-rw-r--r--src/librustc_mir/util/liveness.rs4
-rw-r--r--src/librustc_mir/util/patch.rs2
63 files changed, 588 insertions, 612 deletions
diff --git a/src/librustc/arena.rs b/src/librustc/arena.rs
index 1d6a3420ed9..364a35f1b6f 100644
--- a/src/librustc/arena.rs
+++ b/src/librustc/arena.rs
@@ -23,17 +23,17 @@ macro_rules! arena_types {
             [] generics: rustc::ty::Generics,
             [] trait_def: rustc::ty::TraitDef,
             [] adt_def: rustc::ty::AdtDef,
-            [] steal_mir: rustc::ty::steal::Steal<rustc::mir::Body<$tcx>>,
-            [] mir: rustc::mir::Body<$tcx>,
+            [] steal_mir: rustc::ty::steal::Steal<rustc::mir::BodyCache<$tcx>>,
+            [] mir: rustc::mir::BodyCache<$tcx>,
             [] steal_promoted: rustc::ty::steal::Steal<
                 rustc_index::vec::IndexVec<
                     rustc::mir::Promoted,
-                    rustc::mir::Body<$tcx>
+                    rustc::mir::BodyCache<$tcx>
                 >
             >,
             [] promoted: rustc_index::vec::IndexVec<
                 rustc::mir::Promoted,
-                rustc::mir::Body<$tcx>
+                rustc::mir::BodyCache<$tcx>
             >,
             [] tables: rustc::ty::TypeckTables<$tcx>,
             [] const_allocs: rustc::mir::interpret::Allocation,
diff --git a/src/librustc/mir/cache.rs b/src/librustc/mir/cache.rs
index b6682470606..8958a31b51c 100644
--- a/src/librustc/mir/cache.rs
+++ b/src/librustc/mir/cache.rs
@@ -1,7 +1,7 @@
 use rustc_index::vec::IndexVec;
-//use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
-//use rustc_serialize::{Encodable, Encoder, Decodable, Decoder};
-//use crate::ich::StableHashingContext;
+use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
+use rustc_serialize::{Encodable, Encoder, Decodable, Decoder};
+use crate::ich::StableHashingContext;
 use crate::mir::{BasicBlock, BasicBlockData, Body, LocalDecls, Location, Successors};
 use rustc_data_structures::graph::{self, GraphPredecessors, GraphSuccessors};
 use rustc_data_structures::graph::dominators::{dominators, Dominators};
@@ -14,23 +14,23 @@ pub struct Cache {
     predecessors: Option<IndexVec<BasicBlock, Vec<BasicBlock>>>,
 }
 
-//impl<'tcx, T> rustc_serialize::Encodable for Cache<'tcx, T> {
-//    fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
-//        Encodable::encode(&(), s)
-//    }
-//}
-//
-//impl<'tcx, T> rustc_serialize::Decodable for Cache<'tcx, T> {
-//    fn decode<D: Decoder>(d: &mut D) -> Result<Self, D::Error> {
-//        Decodable::decode(d).map(|_v: ()| Self::new())
-//    }
-//}
-//
-//impl<'a, 'tcx, T> HashStable<StableHashingContext<'a>> for Cache<'tcx, T> {
-//    fn hash_stable(&self, _: &mut StableHashingContext<'a>, _: &mut StableHasher) {
-//        // Do nothing.
-//    }
-//}
+impl rustc_serialize::Encodable for Cache {
+    fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
+        Encodable::encode(&(), s)
+    }
+}
+
+impl rustc_serialize::Decodable for Cache {
+    fn decode<D: Decoder>(d: &mut D) -> Result<Self, D::Error> {
+        Decodable::decode(d).map(|_v: ()| Self::new())
+    }
+}
+
+impl<'a> HashStable<StableHashingContext<'a>> for Cache {
+    fn hash_stable(&self, _: &mut StableHashingContext<'a>, _: &mut StableHasher) {
+        // Do nothing.
+    }
+}
 
 macro_rules! get_predecessors {
     (mut $self:ident, $block:expr, $body:expr) => {
@@ -98,13 +98,13 @@ impl Cache {
 
     #[inline]
     /// This will recompute the predecessors cache if it is not available
-    pub fn predecessors(&mut self, body: &Body<'_>) -> &IndexVec<BasicBlock, Vec<BasicBlock>> {
+    fn predecessors(&mut self, body: &Body<'_>) -> &IndexVec<BasicBlock, Vec<BasicBlock>> {
         self.ensure_predecessors(body);
         self.predecessors.as_ref().unwrap()
     }
 
     #[inline]
-    pub fn predecessors_for(&mut self, bb: BasicBlock, body: &Body<'_>) -> &[BasicBlock] {
+    fn predecessors_for(&mut self, bb: BasicBlock, body: &Body<'_>) -> &[BasicBlock] {
         &self.predecessors(body)[bb]
     }
 
@@ -136,55 +136,58 @@ impl Cache {
     }
 }
 
-pub struct BodyCache<T> {
+#[derive(Clone, Debug, RustcEncodable, RustcDecodable)]
+pub struct BodyCache<'tcx> {
     cache: Cache,
-    body: T,
+    body: Body<'tcx>,
 }
 
-impl<T> BodyCache<T> {
-    pub fn new(body: T) -> Self {
+impl BodyCache<'tcx> {
+    pub fn new(body: Body<'tcx>) -> Self {
         Self {
             cache: Cache::new(),
-            body
+            body,
         }
     }
 }
 
-impl<'a, 'tcx> BodyCache<&'a Body<'tcx>> {
-    #[inline]
-    pub fn predecessors_for(&mut self, bb: BasicBlock) -> &[BasicBlock] {
-        self.cache.predecessors_for(bb, self.body)
+impl BodyCache<'tcx> {
+    pub fn ensure_predecessors(&mut self) {
+        self.cache.ensure_predecessors(&self.body);
     }
 
-    #[inline]
-    pub fn body(&self) -> &'a Body<'tcx> {
-        self.body
+    pub fn predecessors(&mut self) -> &IndexVec<BasicBlock, Vec<BasicBlock>> {
+        self.cache.predecessors(&self.body)
     }
 
-    #[inline]
-    pub fn read_only(mut self) -> ReadOnlyBodyCache<'a, 'tcx> {
-        self.cache.ensure_predecessors(self.body);
+    pub fn read_only(&self) -> ReadOnlyBodyCache<'_, '_> {
+        assert!(self.cache.predecessors.is_some(), "");
         ReadOnlyBodyCache {
-            cache: self.cache,
-            body: self.body,
+            cache: &self.cache,
+            body: &self.body,
         }
     }
 
-    #[inline]
-    pub fn basic_blocks(&self) -> &IndexVec<BasicBlock, BasicBlockData<'tcx>> {
-        &self.body.basic_blocks
+    pub fn body(&self) -> &Body<'tcx> {
+        &self.body
     }
-}
 
-impl<'a, 'tcx> Deref for BodyCache<&'a Body<'tcx>> {
-    type Target = Body<'tcx>;
+    pub fn body_mut(&mut self) -> &mut Body<'tcx> {
+        &mut self.body
+    }
 
-    fn deref(&self) -> &Self::Target {
-        self.body
+    pub fn basic_blocks_mut(&mut self) -> &mut IndexVec<BasicBlock, BasicBlockData<'tcx>> {
+        self.cache.basic_blocks_mut(&mut self.body)
+    }
+
+    pub fn basic_blocks_and_local_decls_mut(
+        &mut self
+    ) -> (&mut IndexVec<BasicBlock, BasicBlockData<'tcx>>, &mut LocalDecls<'tcx>) {
+        self.cache.basic_blocks_and_local_decls_mut(&mut self.body)
     }
 }
 
-impl<'a, 'tcx> Index<BasicBlock> for BodyCache<&'a Body<'tcx>> {
+impl<'tcx> Index<BasicBlock> for BodyCache<'tcx> {
     type Output = BasicBlockData<'tcx>;
 
     #[inline]
@@ -193,69 +196,29 @@ impl<'a, 'tcx> Index<BasicBlock> for BodyCache<&'a Body<'tcx>> {
     }
 }
 
-impl<'a, 'tcx> BodyCache<&'a mut Body<'tcx>> {
-    #[inline]
-    pub fn body(&self) -> &Body<'tcx> {
-        self.body
-    }
-
-    #[inline]
-    pub fn body_mut(&mut self) -> &mut Body<'tcx> {
-        self.body
-    }
-
-    #[inline]
-    pub fn read_only(mut self) -> ReadOnlyBodyCache<'a, 'tcx> {
-        self.cache.ensure_predecessors(self.body);
-        ReadOnlyBodyCache {
-            cache: self.cache,
-            body: self.body,
-        }
-    }
-
-    #[inline]
-    pub fn basic_blocks(&self) -> &IndexVec<BasicBlock, BasicBlockData<'tcx>> {
-        &self.body.basic_blocks
-    }
-
-    #[inline]
-    pub fn basic_blocks_mut(&mut self) -> &mut IndexVec<BasicBlock, BasicBlockData<'tcx>> {
-        self.cache.basic_blocks_mut(&mut self.body)
+impl<'tcx> IndexMut<BasicBlock> for BodyCache<'tcx> {
+    fn index_mut(&mut self, index: BasicBlock) -> &mut Self::Output {
+        &mut self.basic_blocks_mut()[index]
     }
 }
 
-impl<'a, 'tcx> Deref for BodyCache<&'a mut Body<'tcx>> {
+impl<'tcx> Deref for BodyCache<'tcx> {
     type Target = Body<'tcx>;
 
     fn deref(&self) -> &Self::Target {
-        self.body
-    }
-}
-
-impl<'a, 'tcx> DerefMut for BodyCache<&'a mut Body<'tcx>> {
-    fn deref_mut(&mut self) -> &mut Body<'tcx> {
-        self.body
-    }
-}
-
-impl<'a, 'tcx> Index<BasicBlock> for BodyCache<&'a mut Body<'tcx>> {
-    type Output = BasicBlockData<'tcx>;
-
-    #[inline]
-    fn index(&self, index: BasicBlock) -> &BasicBlockData<'tcx> {
-        &self.body[index]
+        &self.body
     }
 }
 
-impl<'a, 'tcx> IndexMut<BasicBlock> for BodyCache<&'a mut Body<'tcx>> {
-    fn index_mut(&mut self, index: BasicBlock) -> &mut Self::Output {
-        self.cache.invalidate_predecessors();
-        &mut self.body.basic_blocks[index]
+impl<'tcx> DerefMut for BodyCache<'tcx> {
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        &mut self.body
     }
 }
 
+#[derive(Copy, Clone, Debug)]
 pub struct ReadOnlyBodyCache<'a, 'tcx> {
-    cache: Cache,
+    cache: &'a Cache,
     body: &'a Body<'tcx>,
 }
 
@@ -289,13 +252,6 @@ impl ReadOnlyBodyCache<'a, 'tcx> {
     pub fn dominators(&self) -> Dominators<BasicBlock> {
         dominators(self)
     }
-
-    pub fn to_owned(self) -> BodyCache<&'a Body<'tcx>> {
-        BodyCache {
-            cache: self.cache,
-            body: self.body,
-        }
-    }
 }
 
 impl graph::DirectedGraph for ReadOnlyBodyCache<'a, 'tcx> {
@@ -358,4 +314,20 @@ impl Index<BasicBlock> for ReadOnlyBodyCache<'a, 'tcx> {
     fn index(&self, index: BasicBlock) -> &BasicBlockData<'tcx> {
         &self.body[index]
     }
-}
\ No newline at end of file
+}
+
+CloneTypeFoldableAndLiftImpls! {
+    Cache,
+}
+
+impl_stable_hash_for!(struct BodyCache<'tcx> {
+    cache,
+    body,
+});
+
+BraceStructTypeFoldableImpl! {
+    impl<'tcx> TypeFoldable<'tcx> for BodyCache<'tcx> {
+        cache,
+        body
+    }
+}
diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs
index 57d396ae933..b6d1c78cc4f 100644
--- a/src/librustc/mir/mod.rs
+++ b/src/librustc/mir/mod.rs
@@ -107,7 +107,7 @@ pub struct Body<'tcx> {
     pub yield_ty: Option<Ty<'tcx>>,
 
     /// Generator drop glue.
-    pub generator_drop: Option<Box<Body<'tcx>>>,
+    pub generator_drop: Option<Box<BodyCache<'tcx>>>,
 
     /// The layout of a generator. Produced by the state transformation.
     pub generator_layout: Option<GeneratorLayout<'tcx>>,
@@ -2600,7 +2600,7 @@ impl Location {
     pub fn is_predecessor_of<'tcx>(
         &self,
         other: Location,
-        body_cache: &ReadOnlyBodyCache<'_, 'tcx>
+        body_cache: ReadOnlyBodyCache<'_, 'tcx>
     ) -> bool {
         // If we are in the same block as the other location and are an earlier statement
         // then we are a predecessor of `other`.
diff --git a/src/librustc/mir/visit.rs b/src/librustc/mir/visit.rs
index c464247c4b4..68694f1b717 100644
--- a/src/librustc/mir/visit.rs
+++ b/src/librustc/mir/visit.rs
@@ -67,10 +67,10 @@ use syntax_pos::Span;
 
 macro_rules! body_cache_type {
     (mut $a:lifetime, $tcx:lifetime) => {
-        &mut BodyCache<& $a mut Body<$tcx>>
+        &mut BodyCache<$tcx>
     };
     ($a:lifetime, $tcx:lifetime) => {
-        &ReadOnlyBodyCache<$a, $tcx>
+        ReadOnlyBodyCache<$a, $tcx>
     };
 }
 
diff --git a/src/librustc/query/mod.rs b/src/librustc/query/mod.rs
index d715ddb1b81..cdfdcee5823 100644
--- a/src/librustc/query/mod.rs
+++ b/src/librustc/query/mod.rs
@@ -106,42 +106,46 @@ rustc_queries! {
 
         /// Fetch the MIR for a given `DefId` right after it's built - this includes
         /// unreachable code.
-        query mir_built(_: DefId) -> &'tcx Steal<mir::Body<'tcx>> {}
+        query mir_built(_: DefId) -> &'tcx Steal<mir::BodyCache<'tcx>> {}
 
         /// Fetch the MIR for a given `DefId` up till the point where it is
         /// ready for const evaluation.
         ///
         /// See the README for the `mir` module for details.
-        query mir_const(_: DefId) -> &'tcx Steal<mir::Body<'tcx>> {
+        query mir_const(_: DefId) -> &'tcx Steal<mir::BodyCache<'tcx>> {
             no_hash
         }
 
         query mir_validated(_: DefId) ->
             (
-                &'tcx Steal<mir::Body<'tcx>>,
-                &'tcx Steal<IndexVec<mir::Promoted, mir::Body<'tcx>>>
+                &'tcx Steal<mir::BodyCache<'tcx>>,
+                &'tcx Steal<IndexVec<mir::Promoted, mir::BodyCache<'tcx>>>
             ) {
             no_hash
         }
 
         /// MIR after our optimization passes have run. This is MIR that is ready
         /// for codegen. This is also the only query that can fetch non-local MIR, at present.
-        query optimized_mir(key: DefId) -> &'tcx mir::Body<'tcx> {
+        query optimized_mir(key: DefId) -> &'tcx mir::BodyCache<'tcx> {
             cache_on_disk_if { key.is_local() }
             load_cached(tcx, id) {
-                let mir: Option<crate::mir::Body<'tcx>> = tcx.queries.on_disk_cache
-                                                            .try_load_query_result(tcx, id);
-                mir.map(|x| &*tcx.arena.alloc(x))
+                let mir: Option<crate::mir::BodyCache<'tcx>>
+                    = tcx.queries.on_disk_cache.try_load_query_result(tcx, id);
+                mir.map(|x| {
+                    let cache = tcx.arena.alloc(x);
+                    cache.ensure_predecessors();
+                    &*cache
+                })
             }
         }
 
-        query promoted_mir(key: DefId) -> &'tcx IndexVec<mir::Promoted, mir::Body<'tcx>> {
+        query promoted_mir(key: DefId) -> &'tcx IndexVec<mir::Promoted, mir::BodyCache<'tcx>> {
             cache_on_disk_if { key.is_local() }
             load_cached(tcx, id) {
                 let promoted: Option<
                     rustc_index::vec::IndexVec<
                         crate::mir::Promoted,
-                        crate::mir::Body<'tcx>
+                        crate::mir::BodyCache<'tcx>
                     >> = tcx.queries.on_disk_cache.try_load_query_result(tcx, id);
                 promoted.map(|p| &*tcx.arena.alloc(p))
             }
@@ -502,7 +506,7 @@ rustc_queries! {
         /// in the case of closures, this will be redirected to the enclosing function.
         query region_scope_tree(_: DefId) -> &'tcx region::ScopeTree {}
 
-        query mir_shims(key: ty::InstanceDef<'tcx>) -> &'tcx mir::Body<'tcx> {
+        query mir_shims(key: ty::InstanceDef<'tcx>) -> &'tcx mir::BodyCache<'tcx> {
             no_force
             desc { |tcx| "generating MIR shim for `{}`", tcx.def_path_str(key.def_id()) }
         }
diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs
index 776ae7dc141..07d86a5f86a 100644
--- a/src/librustc/ty/context.rs
+++ b/src/librustc/ty/context.rs
@@ -22,7 +22,7 @@ use crate::middle::cstore::EncodedMetadata;
 use crate::middle::lang_items;
 use crate::middle::resolve_lifetime::{self, ObjectLifetimeDefault};
 use crate::middle::stability;
-use crate::mir::{Body, Field, interpret, Local, Place, PlaceElem, ProjectionKind, Promoted};
+use crate::mir::{BodyCache, Field, interpret, Local, Place, PlaceElem, ProjectionKind, Promoted};
 use crate::mir::interpret::{ConstValue, Allocation, Scalar};
 use crate::ty::subst::{GenericArg, InternalSubsts, SubstsRef, Subst};
 use crate::ty::ReprOptions;
@@ -1083,17 +1083,17 @@ impl<'tcx> TyCtxt<'tcx> {
         &self.hir_map
     }
 
-    pub fn alloc_steal_mir(self, mir: Body<'tcx>) -> &'tcx Steal<Body<'tcx>> {
-        self.arena.alloc(Steal::new(mir))
+    pub fn alloc_steal_mir(self, mir_cache: BodyCache<'tcx>) -> &'tcx Steal<BodyCache<'tcx>> {
+        self.arena.alloc(Steal::new(mir_cache))
     }
 
-    pub fn alloc_steal_promoted(self, promoted: IndexVec<Promoted, Body<'tcx>>) ->
-        &'tcx Steal<IndexVec<Promoted, Body<'tcx>>> {
+    pub fn alloc_steal_promoted(self, promoted: IndexVec<Promoted, BodyCache<'tcx>>) ->
+        &'tcx Steal<IndexVec<Promoted, BodyCache<'tcx>>> {
         self.arena.alloc(Steal::new(promoted))
     }
 
-    pub fn intern_promoted(self, promoted: IndexVec<Promoted, Body<'tcx>>) ->
-        &'tcx IndexVec<Promoted, Body<'tcx>> {
+    pub fn intern_promoted(self, promoted: IndexVec<Promoted, BodyCache<'tcx>>) ->
+        &'tcx IndexVec<Promoted, BodyCache<'tcx>> {
         self.arena.alloc(promoted)
     }
 
diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs
index 8ccfc467f4a..f8feff0def9 100644
--- a/src/librustc/ty/mod.rs
+++ b/src/librustc/ty/mod.rs
@@ -18,7 +18,7 @@ use crate::infer::canonical::Canonical;
 use crate::middle::cstore::CrateStoreDyn;
 use crate::middle::lang_items::{FnTraitLangItem, FnMutTraitLangItem, FnOnceTraitLangItem};
 use crate::middle::resolve_lifetime::ObjectLifetimeDefault;
-use crate::mir::Body;
+use crate::mir::ReadOnlyBodyCache;
 use crate::mir::interpret::{GlobalId, ErrorHandled};
 use crate::mir::GeneratorLayout;
 use crate::session::CrateDisambiguator;
@@ -2985,10 +2985,10 @@ impl<'tcx> TyCtxt<'tcx> {
     }
 
     /// Returns the possibly-auto-generated MIR of a `(DefId, Subst)` pair.
-    pub fn instance_mir(self, instance: ty::InstanceDef<'tcx>) -> &'tcx Body<'tcx> {
+    pub fn instance_mir(self, instance: ty::InstanceDef<'tcx>) -> ReadOnlyBodyCache<'tcx, 'tcx> {
         match instance {
             ty::InstanceDef::Item(did) => {
-                self.optimized_mir(did)
+                self.optimized_mir(did).read_only()
             }
             ty::InstanceDef::VtableShim(..) |
             ty::InstanceDef::ReifyShim(..) |
@@ -2998,7 +2998,7 @@ impl<'tcx> TyCtxt<'tcx> {
             ty::InstanceDef::ClosureOnceShim { .. } |
             ty::InstanceDef::DropGlue(..) |
             ty::InstanceDef::CloneShim(..) => {
-                self.mir_shims(instance)
+                self.mir_shims(instance).read_only()
             }
         }
     }
@@ -3023,7 +3023,7 @@ impl<'tcx> TyCtxt<'tcx> {
     }
 
     pub fn generator_layout(self, def_id: DefId) -> &'tcx GeneratorLayout<'tcx> {
-        self.optimized_mir(def_id).generator_layout.as_ref().unwrap()
+        self.optimized_mir(def_id).body().generator_layout.as_ref().unwrap()
     }
 
     /// Given the `DefId` of an impl, returns the `DefId` of the trait it implements.
diff --git a/src/librustc_codegen_ssa/base.rs b/src/librustc_codegen_ssa/base.rs
index f50be08c15c..e460a4a2e8c 100644
--- a/src/librustc_codegen_ssa/base.rs
+++ b/src/librustc_codegen_ssa/base.rs
@@ -31,7 +31,6 @@ use rustc::hir::def_id::{DefId, LOCAL_CRATE};
 use rustc::middle::cstore::EncodedMetadata;
 use rustc::middle::lang_items::StartFnLangItem;
 use rustc::middle::weak_lang_items;
-use rustc::mir::BodyCache;
 use rustc::mir::mono::{CodegenUnitNameBuilder, CodegenUnit, MonoItem};
 use rustc::ty::{self, Ty, TyCtxt, Instance};
 use rustc::ty::layout::{self, Align, TyLayout, LayoutOf, VariantIdx, HasTyCtxt};
@@ -375,8 +374,6 @@ pub fn codegen_instance<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>>(
     let lldecl = cx.get_fn(instance);
 
     let mir = cx.tcx().instance_mir(instance.def);
-    // TODO(nashenas88) move this into instance_mir before merging PR
-    let mir = BodyCache::new(mir);
     mir::codegen_mir::<Bx>(cx, lldecl, mir, instance, sig);
 }
 
diff --git a/src/librustc_codegen_ssa/mir/analyze.rs b/src/librustc_codegen_ssa/mir/analyze.rs
index fff01178341..830b7da8c03 100644
--- a/src/librustc_codegen_ssa/mir/analyze.rs
+++ b/src/librustc_codegen_ssa/mir/analyze.rs
@@ -16,8 +16,8 @@ use syntax_pos::DUMMY_SP;
 use super::FunctionCx;
 use crate::traits::*;
 
-pub fn non_ssa_locals<'a, 'b, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
-    fx: &FunctionCx<'a, 'b, 'tcx, Bx>
+pub fn non_ssa_locals<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
+    fx: &FunctionCx<'a, 'tcx, Bx>
 ) -> BitSet<mir::Local> {
     let mut analyzer = LocalAnalyzer::new(fx);
 
@@ -56,8 +56,8 @@ pub fn non_ssa_locals<'a, 'b, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
     analyzer.non_ssa_locals
 }
 
-struct LocalAnalyzer<'mir, 'a, 'b, 'tcx, Bx: BuilderMethods<'a, 'tcx>> {
-    fx: &'mir FunctionCx<'a, 'b, 'tcx, Bx>,
+struct LocalAnalyzer<'mir, 'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> {
+    fx: &'mir FunctionCx<'a, 'tcx, Bx>,
     dominators: Dominators<mir::BasicBlock>,
     non_ssa_locals: BitSet<mir::Local>,
     // The location of the first visited direct assignment to each
@@ -65,8 +65,8 @@ struct LocalAnalyzer<'mir, 'a, 'b, 'tcx, Bx: BuilderMethods<'a, 'tcx>> {
     first_assignment: IndexVec<mir::Local, Location>,
 }
 
-impl<'mir, 'a, 'b, 'tcx, Bx: BuilderMethods<'a, 'tcx>> LocalAnalyzer<'mir, 'a, 'b, 'tcx, Bx> {
-    fn new(fx: &'mir FunctionCx<'a, 'b, 'tcx, Bx>) -> Self {
+impl<'mir, 'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> LocalAnalyzer<'mir, 'a, 'tcx, Bx> {
+    fn new(fx: &'mir FunctionCx<'a, 'tcx, Bx>) -> Self {
         let invalid_location =
             mir::BasicBlock::new(fx.mir.basic_blocks().len()).start_location();
         let dominators = fx.mir.dominators();
@@ -232,8 +232,8 @@ impl<'mir, 'a, 'b, 'tcx, Bx: BuilderMethods<'a, 'tcx>> LocalAnalyzer<'mir, 'a, '
 
 }
 
-impl<'mir, 'a, 'b, 'tcx, Bx: BuilderMethods<'a, 'tcx>> Visitor<'tcx>
-    for LocalAnalyzer<'mir, 'a, 'b, 'tcx, Bx>
+impl<'mir, 'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> Visitor<'tcx>
+    for LocalAnalyzer<'mir, 'a, 'tcx, Bx>
 {
     fn visit_assign(&mut self,
                     place: &mir::Place<'tcx>,
diff --git a/src/librustc_codegen_ssa/mir/block.rs b/src/librustc_codegen_ssa/mir/block.rs
index e2cae0aa565..f661e7bba80 100644
--- a/src/librustc_codegen_ssa/mir/block.rs
+++ b/src/librustc_codegen_ssa/mir/block.rs
@@ -33,19 +33,19 @@ struct TerminatorCodegenHelper<'a, 'tcx> {
 impl<'a, 'tcx> TerminatorCodegenHelper<'a, 'tcx> {
     /// Returns the associated funclet from `FunctionCx::funclets` for the
     /// `funclet_bb` member if it is not `None`.
-    fn funclet<'d, 'c, 'b, Bx: BuilderMethods<'b, 'tcx>>(
+    fn funclet<'c, 'b, Bx: BuilderMethods<'b, 'tcx>>(
         &self,
-        fx: &'d mut FunctionCx<'b, 'c, 'tcx, Bx>,
-    ) -> Option<&'d Bx::Funclet> {
+        fx: &'c mut FunctionCx<'b, 'tcx, Bx>,
+    ) -> Option<&'c Bx::Funclet> {
         match self.funclet_bb {
             Some(funcl) => fx.funclets[funcl].as_ref(),
             None => None,
         }
     }
 
-    fn lltarget<'b, 'c, 'd, Bx: BuilderMethods<'b, 'tcx>>(
+    fn lltarget<'b, 'c, Bx: BuilderMethods<'b, 'tcx>>(
         &self,
-        fx: &'d mut FunctionCx<'b, 'c, 'tcx, Bx>,
+        fx: &'c mut FunctionCx<'b, 'tcx, Bx>,
         target: mir::BasicBlock
     ) -> (Bx::BasicBlock, bool) {
         let span = self.terminator.source_info.span;
@@ -63,9 +63,9 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'a, 'tcx> {
     }
 
     /// Create a basic block.
-    fn llblock<'d, 'c, 'b, Bx: BuilderMethods<'b, 'tcx>>(
+    fn llblock<'c, 'b, Bx: BuilderMethods<'b, 'tcx>>(
         &self,
-        fx: &'d mut FunctionCx<'b, 'c, 'tcx, Bx>,
+        fx: &'c mut FunctionCx<'b, 'tcx, Bx>,
         target: mir::BasicBlock
     ) -> Bx::BasicBlock {
         let (lltarget, is_cleanupret) = self.lltarget(fx, target);
@@ -83,9 +83,9 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'a, 'tcx> {
         }
     }
 
-    fn funclet_br<'d, 'c, 'b, Bx: BuilderMethods<'b, 'tcx>>(
+    fn funclet_br<'c, 'b, Bx: BuilderMethods<'b, 'tcx>>(
         &self,
-        fx: &'d mut FunctionCx<'b, 'c, 'tcx, Bx>,
+        fx: &'c mut FunctionCx<'b, 'tcx, Bx>,
         bx: &mut Bx,
         target: mir::BasicBlock,
     ) {
@@ -101,9 +101,9 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'a, 'tcx> {
 
     /// Call `fn_ptr` of `fn_abi` with the arguments `llargs`, the optional
     /// return destination `destination` and the cleanup function `cleanup`.
-    fn do_call<'d, 'c, 'b, Bx: BuilderMethods<'b, 'tcx>>(
+    fn do_call<'c, 'b, Bx: BuilderMethods<'b, 'tcx>>(
         &self,
-        fx: &'d mut FunctionCx<'b, 'c, 'tcx, Bx>,
+        fx: &'c mut FunctionCx<'b, 'tcx, Bx>,
         bx: &mut Bx,
         fn_abi: FnAbi<'tcx, Ty<'tcx>>,
         fn_ptr: Bx::Value,
@@ -153,7 +153,7 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'a, 'tcx> {
     // a loop.
     fn maybe_sideeffect<'b, 'tcx2: 'b, Bx: BuilderMethods<'b, 'tcx2>>(
         &self,
-        mir: &mir::ReadOnlyBodyCache<'_, 'tcx>,
+        mir: mir::ReadOnlyBodyCache<'_, 'tcx>,
         bx: &mut Bx,
         targets: &[mir::BasicBlock],
     ) {
@@ -171,7 +171,7 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'a, 'tcx> {
 }
 
 /// Codegen implementations for some terminator variants.
-impl<'a, 'b, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'b, 'tcx, Bx> {
+impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
     /// Generates code for a `Resume` terminator.
     fn codegen_resume_terminator<'c>(
         &mut self,
@@ -216,7 +216,7 @@ impl<'a, 'b, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'b, 'tcx, Bx> {
             let lltrue = helper.llblock(self, targets[0]);
             let llfalse = helper.llblock(self, targets[1]);
             if switch_ty == bx.tcx().types.bool {
-                helper.maybe_sideeffect(&self.mir, &mut bx, targets.as_slice());
+                helper.maybe_sideeffect(self.mir, &mut bx, targets.as_slice());
                 // Don't generate trivial icmps when switching on bool
                 if let [0] = values[..] {
                     bx.cond_br(discr.immediate(), llfalse, lltrue);
@@ -230,11 +230,11 @@ impl<'a, 'b, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'b, 'tcx, Bx> {
                 );
                 let llval = bx.const_uint_big(switch_llty, values[0]);
                 let cmp = bx.icmp(IntPredicate::IntEQ, discr.immediate(), llval);
-                helper.maybe_sideeffect(&self.mir, &mut bx, targets.as_slice());
+                helper.maybe_sideeffect(self.mir, &mut bx, targets.as_slice());
                 bx.cond_br(cmp, lltrue, llfalse);
             }
         } else {
-            helper.maybe_sideeffect(&self.mir, &mut bx, targets.as_slice());
+            helper.maybe_sideeffect(self.mir, &mut bx, targets.as_slice());
             let (otherwise, targets) = targets.split_last().unwrap();
             bx.switch(
                 discr.immediate(),
@@ -330,7 +330,7 @@ impl<'a, 'b, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'b, 'tcx, Bx> {
 
         if let ty::InstanceDef::DropGlue(_, None) = drop_fn.def {
             // we don't actually need to drop anything.
-            helper.maybe_sideeffect(&self.mir, &mut bx, &[target]);
+            helper.maybe_sideeffect(self.mir, &mut bx, &[target]);
             helper.funclet_br(self, &mut bx, target);
             return
         }
@@ -361,7 +361,7 @@ impl<'a, 'b, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'b, 'tcx, Bx> {
                  FnAbi::of_instance(&bx, drop_fn))
             }
         };
-        helper.maybe_sideeffect(&self.mir, &mut bx, &[target]);
+        helper.maybe_sideeffect(self.mir, &mut bx, &[target]);
         helper.do_call(self, &mut bx, fn_ty, drop_fn, args,
                        Some((ReturnDest::Nothing, target)),
                        unwind);
@@ -397,7 +397,7 @@ impl<'a, 'b, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'b, 'tcx, Bx> {
 
         // Don't codegen the panic block if success if known.
         if const_cond == Some(expected) {
-            helper.maybe_sideeffect(&self.mir, &mut bx, &[target]);
+            helper.maybe_sideeffect(self.mir, &mut bx, &[target]);
             helper.funclet_br(self, &mut bx, target);
             return;
         }
@@ -408,7 +408,7 @@ impl<'a, 'b, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'b, 'tcx, Bx> {
         // Create the failure block and the conditional branch to it.
         let lltarget = helper.llblock(self, target);
         let panic_block = self.new_block("panic");
-        helper.maybe_sideeffect(&self.mir, &mut bx, &[target]);
+        helper.maybe_sideeffect(self.mir, &mut bx, &[target]);
         if expected {
             bx.cond_br(cond, lltarget, panic_block.llbb());
         } else {
@@ -493,7 +493,7 @@ impl<'a, 'b, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'b, 'tcx, Bx> {
             if let Some(destination_ref) = destination.as_ref() {
                 let &(ref dest, target) = destination_ref;
                 self.codegen_transmute(&mut bx, &args[0], dest);
-                helper.maybe_sideeffect(&self.mir, &mut bx, &[target]);
+                helper.maybe_sideeffect(self.mir, &mut bx, &[target]);
                 helper.funclet_br(self, &mut bx, target);
             } else {
                 // If we are trying to transmute to an uninhabited type,
@@ -521,7 +521,7 @@ impl<'a, 'b, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'b, 'tcx, Bx> {
             Some(ty::InstanceDef::DropGlue(_, None)) => {
                 // Empty drop glue; a no-op.
                 let &(_, target) = destination.as_ref().unwrap();
-                helper.maybe_sideeffect(&self.mir, &mut bx, &[target]);
+                helper.maybe_sideeffect(self.mir, &mut bx, &[target]);
                 helper.funclet_br(self, &mut bx, target);
                 return;
             }
@@ -553,7 +553,7 @@ impl<'a, 'b, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'b, 'tcx, Bx> {
                 let llfn = bx.get_fn_addr(instance);
 
                 if let Some((_, target)) = destination.as_ref() {
-                    helper.maybe_sideeffect(&self.mir, &mut bx, &[*target]);
+                    helper.maybe_sideeffect(self.mir, &mut bx, &[*target]);
                 }
                 // Codegen the actual panic invoke/call.
                 helper.do_call(
@@ -568,7 +568,7 @@ impl<'a, 'b, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'b, 'tcx, Bx> {
             } else {
                 // a NOP
                 let target = destination.as_ref().unwrap().1;
-                helper.maybe_sideeffect(&self.mir, &mut bx, &[target]);
+                helper.maybe_sideeffect(self.mir, &mut bx, &[target]);
                 helper.funclet_br(self, &mut bx, destination.as_ref().unwrap().1)
             }
             return;
@@ -682,7 +682,7 @@ impl<'a, 'b, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'b, 'tcx, Bx> {
             }
 
             if let Some((_, target)) = *destination {
-                helper.maybe_sideeffect(&self.mir, &mut bx, &[target]);
+                helper.maybe_sideeffect(self.mir, &mut bx, &[target]);
                 helper.funclet_br(self, &mut bx, target);
             } else {
                 bx.unreachable();
@@ -776,7 +776,7 @@ impl<'a, 'b, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'b, 'tcx, Bx> {
         };
 
         if let Some((_, target)) = destination.as_ref() {
-            helper.maybe_sideeffect(&self.mir, &mut bx, &[*target]);
+            helper.maybe_sideeffect(self.mir, &mut bx, &[*target]);
         }
         helper.do_call(self, &mut bx, fn_ty, fn_ptr, &llargs,
                        destination.as_ref().map(|&(_, target)| (ret_dest, target)),
@@ -784,13 +784,13 @@ impl<'a, 'b, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'b, 'tcx, Bx> {
     }
 }
 
-impl<'a, 'b, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'b, 'tcx, Bx> {
+impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
     pub fn codegen_block(
         &mut self,
         bb: mir::BasicBlock,
     ) {
         let mut bx = self.build_block(bb);
-        let data = &self.mir[bb];
+        let data = &self.mir.body()[bb];
 
         debug!("codegen_block({:?}={:?})", bb, data);
 
@@ -827,7 +827,7 @@ impl<'a, 'b, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'b, 'tcx, Bx> {
             }
 
             mir::TerminatorKind::Goto { target } => {
-                helper.maybe_sideeffect(&self.mir, &mut bx, &[target]);
+                helper.maybe_sideeffect(self.mir, &mut bx, &[target]);
                 helper.funclet_br(self, &mut bx, target);
             }
 
diff --git a/src/librustc_codegen_ssa/mir/constant.rs b/src/librustc_codegen_ssa/mir/constant.rs
index 71d0abf2bff..27891be6b82 100644
--- a/src/librustc_codegen_ssa/mir/constant.rs
+++ b/src/librustc_codegen_ssa/mir/constant.rs
@@ -9,7 +9,7 @@ use crate::mir::operand::OperandRef;
 
 use super::FunctionCx;
 
-impl<'a, 'b, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'b, 'tcx, Bx> {
+impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
     pub fn eval_mir_constant_to_operand(
         &mut self,
         bx: &mut Bx,
diff --git a/src/librustc_codegen_ssa/mir/mod.rs b/src/librustc_codegen_ssa/mir/mod.rs
index a58b13ce102..2a012cd2ed9 100644
--- a/src/librustc_codegen_ssa/mir/mod.rs
+++ b/src/librustc_codegen_ssa/mir/mod.rs
@@ -1,6 +1,6 @@
 use rustc::ty::{self, Ty, TypeFoldable, Instance};
 use rustc::ty::layout::{TyLayout, HasTyCtxt, FnAbiExt};
-use rustc::mir::{self, Body, BodyCache};
+use rustc::mir::{self, Body, ReadOnlyBodyCache};
 use rustc_target::abi::call::{FnAbi, PassMode};
 use crate::base;
 use crate::traits::*;
@@ -18,10 +18,10 @@ use rustc::mir::traversal;
 use self::operand::{OperandRef, OperandValue};
 
 /// Master context for codegenning from MIR.
-pub struct FunctionCx<'a, 'b, 'tcx, Bx: BuilderMethods<'a, 'tcx>> {
+pub struct FunctionCx<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> {
     instance: Instance<'tcx>,
 
-    mir: &'b mir::ReadOnlyBodyCache<'a, 'tcx>,
+    mir: mir::ReadOnlyBodyCache<'a, 'tcx>,
 
     debug_context: Option<FunctionDebugContext<Bx::DIScope>>,
 
@@ -79,7 +79,7 @@ pub struct FunctionCx<'a, 'b, 'tcx, Bx: BuilderMethods<'a, 'tcx>> {
     per_local_var_debug_info: Option<IndexVec<mir::Local, Vec<&'a mir::VarDebugInfo<'tcx>>>>,
 }
 
-impl<'a, 'b, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'b, 'tcx, Bx> {
+impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
     pub fn monomorphize<T>(&self, value: &T) -> T
         where T: TypeFoldable<'tcx>
     {
@@ -122,7 +122,7 @@ impl<'a, 'tcx, V: CodegenObject> LocalRef<'tcx, V> {
 pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
     cx: &'a Bx::CodegenCx,
     llfn: Bx::Function,
-    mut mir: BodyCache<&'a Body<'tcx>>,
+    mir: ReadOnlyBodyCache<'a, 'tcx>,
     instance: Instance<'tcx>,
     sig: ty::FnSig<'tcx>,
 ) {
@@ -157,10 +157,9 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
 
     let (landing_pads, funclets) = create_funclets(&mir, &mut bx, &cleanup_kinds, &block_bxs);
     let mir_body = mir.body();
-    let readonly_mir = mir.read_only();
     let mut fx = FunctionCx {
         instance,
-        mir: &readonly_mir,
+        mir,
         llfn,
         fn_abi,
         cx,
@@ -319,9 +318,9 @@ fn create_funclets<'a, 'b, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
 /// Produces, for each argument, a `Value` pointing at the
 /// argument's value. As arguments are places, these are always
 /// indirect.
-fn arg_local_refs<'a, 'b, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
+fn arg_local_refs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
     bx: &mut Bx,
-    fx: &FunctionCx<'a, 'b, 'tcx, Bx>,
+    fx: &FunctionCx<'a, 'tcx, Bx>,
     memory_locals: &BitSet<mir::Local>,
 ) -> Vec<LocalRef<'tcx, Bx::Value>> {
     let mut idx = 0;
diff --git a/src/librustc_codegen_ssa/mir/operand.rs b/src/librustc_codegen_ssa/mir/operand.rs
index 947dc2df87d..310b8aeb4db 100644
--- a/src/librustc_codegen_ssa/mir/operand.rs
+++ b/src/librustc_codegen_ssa/mir/operand.rs
@@ -377,7 +377,7 @@ impl<'a, 'tcx, V: CodegenObject> OperandValue<V> {
     }
 }
 
-impl<'a, 'b, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'b, 'tcx, Bx> {
+impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
     fn maybe_codegen_consume_direct(
         &mut self,
         bx: &mut Bx,
diff --git a/src/librustc_codegen_ssa/mir/place.rs b/src/librustc_codegen_ssa/mir/place.rs
index 34f2b56adee..281539277cb 100644
--- a/src/librustc_codegen_ssa/mir/place.rs
+++ b/src/librustc_codegen_ssa/mir/place.rs
@@ -435,7 +435,7 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> {
     }
 }
 
-impl<'a, 'b, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'b, 'tcx, Bx> {
+impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
     pub fn codegen_place(
         &mut self,
         bx: &mut Bx,
diff --git a/src/librustc_codegen_ssa/mir/rvalue.rs b/src/librustc_codegen_ssa/mir/rvalue.rs
index 19554156b22..680c5b873b0 100644
--- a/src/librustc_codegen_ssa/mir/rvalue.rs
+++ b/src/librustc_codegen_ssa/mir/rvalue.rs
@@ -18,7 +18,7 @@ use syntax::source_map::{DUMMY_SP, Span};
 
 use std::{u128, i128};
 
-impl<'a, 'b, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'b, 'tcx, Bx> {
+impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
     pub fn codegen_rvalue(
         &mut self,
         mut bx: Bx,
@@ -695,7 +695,7 @@ impl<'a, 'b, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'b, 'tcx, Bx> {
     }
 }
 
-impl<'a, 'b, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'b, 'tcx, Bx> {
+impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
     pub fn rvalue_creates_operand(
         &self,
         rvalue: &mir::Rvalue<'tcx>,
diff --git a/src/librustc_codegen_ssa/mir/statement.rs b/src/librustc_codegen_ssa/mir/statement.rs
index 5307108f857..0b82edea157 100644
--- a/src/librustc_codegen_ssa/mir/statement.rs
+++ b/src/librustc_codegen_ssa/mir/statement.rs
@@ -8,7 +8,7 @@ use crate::traits::*;
 
 use rustc_error_codes::*;
 
-impl<'a, 'b, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'b, 'tcx, Bx> {
+impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
     pub fn codegen_statement(
         &mut self,
         mut bx: Bx,
diff --git a/src/librustc_metadata/rmeta/decoder.rs b/src/librustc_metadata/rmeta/decoder.rs
index 820783bab6d..30dad03648c 100644
--- a/src/librustc_metadata/rmeta/decoder.rs
+++ b/src/librustc_metadata/rmeta/decoder.rs
@@ -18,12 +18,11 @@ use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::svh::Svh;
 use rustc::dep_graph::{self, DepNodeIndex};
 use rustc::middle::lang_items;
-use rustc::mir::{self, interpret};
+use rustc::mir::{self, BodyCache, interpret, Promoted};
 use rustc::mir::interpret::{AllocDecodingSession, AllocDecodingState};
 use rustc::session::Session;
 use rustc::ty::{self, Ty, TyCtxt};
 use rustc::ty::codec::TyDecoder;
-use rustc::mir::{Body, Promoted};
 use rustc::util::common::record_time;
 use rustc::util::captures::Captures;
 
@@ -1080,7 +1079,7 @@ impl<'a, 'tcx> CrateMetadata {
             self.root.per_def.mir.get(self, id).is_some()
     }
 
-    fn get_optimized_mir(&self, tcx: TyCtxt<'tcx>, id: DefIndex) -> Body<'tcx> {
+    fn get_optimized_mir(&self, tcx: TyCtxt<'tcx>, id: DefIndex) -> BodyCache<'tcx> {
         self.root.per_def.mir.get(self, id)
             .filter(|_| !self.is_proc_macro(id))
             .unwrap_or_else(|| {
@@ -1093,7 +1092,7 @@ impl<'a, 'tcx> CrateMetadata {
         &self,
         tcx: TyCtxt<'tcx>,
         id: DefIndex,
-    ) -> IndexVec<Promoted, Body<'tcx>> {
+    ) -> IndexVec<Promoted, BodyCache<'tcx>> {
         self.root.per_def.promoted_mir.get(self, id)
             .filter(|_| !self.is_proc_macro(id))
             .unwrap_or_else(|| {
diff --git a/src/librustc_metadata/rmeta/mod.rs b/src/librustc_metadata/rmeta/mod.rs
index 4ea562fced3..fdf43f06eb1 100644
--- a/src/librustc_metadata/rmeta/mod.rs
+++ b/src/librustc_metadata/rmeta/mod.rs
@@ -276,8 +276,8 @@ define_per_def_tables! {
     // Also, as an optimization, a missing entry indicates an empty `&[]`.
     inferred_outlives: Table<DefIndex, Lazy!(&'tcx [(ty::Predicate<'tcx>, Span)])>,
     super_predicates: Table<DefIndex, Lazy!(ty::GenericPredicates<'tcx>)>,
-    mir: Table<DefIndex, Lazy!(mir::Body<'tcx>)>,
-    promoted_mir: Table<DefIndex, Lazy!(IndexVec<mir::Promoted, mir::Body<'tcx>>)>,
+    mir: Table<DefIndex, Lazy!(mir::BodyCache<'tcx>)>,
+    promoted_mir: Table<DefIndex, Lazy!(IndexVec<mir::Promoted, mir::BodyCache<'tcx>>)>,
 }
 
 #[derive(Copy, Clone, RustcEncodable, RustcDecodable)]
diff --git a/src/librustc_mir/borrow_check/borrow_set.rs b/src/librustc_mir/borrow_check/borrow_set.rs
index 6191a93d228..d1af7461f47 100644
--- a/src/librustc_mir/borrow_check/borrow_set.rs
+++ b/src/librustc_mir/borrow_check/borrow_set.rs
@@ -90,7 +90,7 @@ crate enum LocalsStateAtExit {
 impl LocalsStateAtExit {
     fn build(
         locals_are_invalidated_at_exit: bool,
-        body_cache: &ReadOnlyBodyCache<'_, 'tcx>,
+        body_cache: ReadOnlyBodyCache<'_, 'tcx>,
         move_data: &MoveData<'tcx>
     ) -> Self {
         struct HasStorageDead(BitSet<Local>);
@@ -123,7 +123,7 @@ impl LocalsStateAtExit {
 impl<'tcx> BorrowSet<'tcx> {
     pub fn build(
         tcx: TyCtxt<'tcx>,
-        body_cache: &ReadOnlyBodyCache<'_, 'tcx>,
+        body_cache: ReadOnlyBodyCache<'_, 'tcx>,
         locals_are_invalidated_at_exit: bool,
         move_data: &MoveData<'tcx>,
     ) -> Self {
@@ -139,7 +139,7 @@ impl<'tcx> BorrowSet<'tcx> {
                 LocalsStateAtExit::build(locals_are_invalidated_at_exit, body_cache, move_data),
         };
 
-        for (block, block_data) in traversal::preorder(body_cache) {
+        for (block, block_data) in traversal::preorder(&body_cache) {
             visitor.visit_basic_block_data(block, block_data);
         }
 
diff --git a/src/librustc_mir/borrow_check/mod.rs b/src/librustc_mir/borrow_check/mod.rs
index f8ae07aa394..0170cc8b42a 100644
--- a/src/librustc_mir/borrow_check/mod.rs
+++ b/src/librustc_mir/borrow_check/mod.rs
@@ -102,7 +102,7 @@ fn mir_borrowck(tcx: TyCtxt<'_>, def_id: DefId) -> BorrowCheckResult<'_> {
 fn do_mir_borrowck<'a, 'tcx>(
     infcx: &InferCtxt<'a, 'tcx>,
     input_body: &Body<'tcx>,
-    input_promoted: &IndexVec<Promoted, Body<'tcx>>,
+    input_promoted: &IndexVec<Promoted, BodyCache<'tcx>>,
     def_id: DefId,
 ) -> BorrowCheckResult<'tcx> {
     debug!("do_mir_borrowck(def_id = {:?})", def_id);
@@ -162,14 +162,13 @@ fn do_mir_borrowck<'a, 'tcx>(
     // requires first making our own copy of the MIR. This copy will
     // be modified (in place) to contain non-lexical lifetimes. It
     // will have a lifetime tied to the inference context.
-    let mut body: Body<'tcx> = input_body.clone();
+    let body: Body<'tcx> = input_body.clone();
     let mut promoted = input_promoted.clone();
-    let mut promoted_cache: IndexVec<Promoted, BodyCache<&mut Body<'tcx>>> = input_promoted.clone().iter_mut().map(|body| BodyCache::new(body)).collect();
-    let mut body_cache = BodyCache::new(&mut body);
+    let mut body_cache = BodyCache::new(body);
     let free_regions =
-        nll::replace_regions_in_mir(infcx, def_id, param_env, &mut body_cache, &mut promoted_cache);
-    let body_cache = BodyCache::new(&body).read_only(); // no further changes
-    let promoted: IndexVec<Promoted, ReadOnlyBodyCache<'_, 'tcx>> = promoted_cache.into_iter().map(|body_cache| body_cache.read_only()).collect();
+        nll::replace_regions_in_mir(infcx, def_id, param_env, &mut body_cache, &mut promoted);
+    let body_cache = body_cache.read_only(); // no further changes
+    let promoted: IndexVec<_, _> = promoted.iter().map(|body_cache| body_cache.read_only()).collect();
 
     let location_table = &LocationTable::new(&body_cache);
 
@@ -198,14 +197,14 @@ fn do_mir_borrowck<'a, 'tcx>(
 
     let locals_are_invalidated_at_exit = tcx.hir().body_owner_kind(id).is_fn_or_closure();
     let borrow_set = Rc::new(BorrowSet::build(
-            tcx, &body_cache, locals_are_invalidated_at_exit, &mdpe.move_data));
+            tcx, body_cache, locals_are_invalidated_at_exit, &mdpe.move_data));
 
     // If we are in non-lexical mode, compute the non-lexical lifetimes.
     let (regioncx, polonius_output, opt_closure_req) = nll::compute_regions(
         infcx,
         def_id,
         free_regions,
-        &body_cache,
+        body_cache,
         &promoted,
         &local_names,
         &upvars,
@@ -1338,7 +1337,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                             _ => bug!("temporary initialized in arguments"),
                         };
 
-                        let bbd = &self.body_cache[loc.block];
+                        let bbd = &self.body_cache.body()[loc.block];
                         let stmt = &bbd.statements[loc.statement_index];
                         debug!("temporary assigned in: stmt={:?}", stmt);
 
@@ -1860,7 +1859,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                     if def.is_union() {
                         if this.move_data.path_map[mpi].iter().any(|moi| {
                             this.move_data.moves[*moi].source.is_predecessor_of(
-                                location, &this.body_cache,
+                                location, this.body_cache,
                             )
                         }) {
                             return;
diff --git a/src/librustc_mir/borrow_check/mutability_errors.rs b/src/librustc_mir/borrow_check/mutability_errors.rs
index 44e139889c9..feaa5fd3a55 100644
--- a/src/librustc_mir/borrow_check/mutability_errors.rs
+++ b/src/librustc_mir/borrow_check/mutability_errors.rs
@@ -377,7 +377,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
                         },
                     ))) => Some(suggest_ampmut(
                         self.infcx.tcx,
-                        &self.body_cache,
+                        self.body_cache,
                         *local,
                         local_decl,
                         opt_ty_info,
@@ -529,7 +529,7 @@ fn suggest_ampmut_self<'tcx>(
 // by trying (3.), then (2.) and finally falling back on (1.).
 fn suggest_ampmut<'tcx>(
     tcx: TyCtxt<'tcx>,
-    body_cache: &ReadOnlyBodyCache<'_, 'tcx>,
+    body_cache: ReadOnlyBodyCache<'_, 'tcx>,
     local: Local,
     local_decl: &mir::LocalDecl<'tcx>,
     opt_ty_info: Option<Span>,
diff --git a/src/librustc_mir/borrow_check/nll/invalidation.rs b/src/librustc_mir/borrow_check/nll/invalidation.rs
index 7cb5b839cee..8d5466e545d 100644
--- a/src/librustc_mir/borrow_check/nll/invalidation.rs
+++ b/src/librustc_mir/borrow_check/nll/invalidation.rs
@@ -22,7 +22,7 @@ pub(super) fn generate_invalidates<'tcx>(
     param_env: ty::ParamEnv<'tcx>,
     all_facts: &mut Option<AllFacts>,
     location_table: &LocationTable,
-    body_cache: &ReadOnlyBodyCache<'_, 'tcx>,
+    body_cache: ReadOnlyBodyCache<'_, 'tcx>,
     borrow_set: &BorrowSet<'tcx>,
 ) {
     if all_facts.is_none() {
@@ -41,7 +41,7 @@ pub(super) fn generate_invalidates<'tcx>(
             body: body_cache.body(),
             dominators,
         };
-        ig.visit_body(&body_cache);
+        ig.visit_body(body_cache);
     }
 }
 
diff --git a/src/librustc_mir/borrow_check/nll/mod.rs b/src/librustc_mir/borrow_check/nll/mod.rs
index 5fa2c6605f6..bf11d348409 100644
--- a/src/librustc_mir/borrow_check/nll/mod.rs
+++ b/src/librustc_mir/borrow_check/nll/mod.rs
@@ -55,8 +55,8 @@ pub(in crate::borrow_check) fn replace_regions_in_mir<'cx, 'tcx>(
     infcx: &InferCtxt<'cx, 'tcx>,
     def_id: DefId,
     param_env: ty::ParamEnv<'tcx>,
-    body_cache: &mut BodyCache<&mut Body<'tcx>>,
-    promoted: &mut IndexVec<Promoted, BodyCache<&mut Body<'tcx>>>,
+    body_cache: &mut BodyCache<'tcx>,
+    promoted: &mut IndexVec<Promoted, BodyCache<'tcx>>,
 ) -> UniversalRegions<'tcx> {
     debug!("replace_regions_in_mir(def_id={:?})", def_id);
 
@@ -158,7 +158,7 @@ pub(in crate::borrow_check) fn compute_regions<'cx, 'tcx>(
     infcx: &InferCtxt<'cx, 'tcx>,
     def_id: DefId,
     universal_regions: UniversalRegions<'tcx>,
-    body_cache: &ReadOnlyBodyCache<'_, 'tcx>,
+    body_cache: ReadOnlyBodyCache<'_, 'tcx>,
     promoted_cache: &IndexVec<Promoted, ReadOnlyBodyCache<'_, 'tcx>>,
     local_names: &IndexVec<Local, Option<Symbol>>,
     upvars: &[Upvar],
@@ -181,7 +181,7 @@ pub(in crate::borrow_check) fn compute_regions<'cx, 'tcx>(
 
     let universal_regions = Rc::new(universal_regions);
 
-    let elements = &Rc::new(RegionValueElements::new(body_cache));
+    let elements = &Rc::new(RegionValueElements::new(body_cache.body()));
 
     // Run the MIR type-checker.
     let MirTypeckResults {
@@ -206,7 +206,7 @@ pub(in crate::borrow_check) fn compute_regions<'cx, 'tcx>(
         all_facts
             .universal_region
             .extend(universal_regions.universal_regions());
-        populate_polonius_move_facts(all_facts, move_data, location_table, body_cache);
+        populate_polonius_move_facts(all_facts, move_data, location_table, body_cache.body());
     }
 
     // Create the region inference context, taking ownership of the
@@ -230,7 +230,7 @@ pub(in crate::borrow_check) fn compute_regions<'cx, 'tcx>(
         &mut liveness_constraints,
         &mut all_facts,
         location_table,
-        body_cache,
+        body_cache.body(),
         borrow_set,
     );
 
@@ -239,7 +239,7 @@ pub(in crate::borrow_check) fn compute_regions<'cx, 'tcx>(
         universal_regions,
         placeholder_indices,
         universal_region_relations,
-        body_cache,
+        body_cache.body(),
         outlives_constraints,
         member_constraints,
         closure_bounds_mapping,
@@ -284,21 +284,21 @@ pub(in crate::borrow_check) fn compute_regions<'cx, 'tcx>(
 
     // Solve the region constraints.
     let closure_region_requirements =
-        regioncx.solve(infcx, body_cache, local_names, upvars, def_id, errors_buffer);
+        regioncx.solve(infcx, body_cache.body(), local_names, upvars, def_id, errors_buffer);
 
     // Dump MIR results into a file, if that is enabled. This let us
     // write unit-tests, as well as helping with debugging.
     dump_mir_results(
         infcx,
         MirSource::item(def_id),
-        body_cache,
+        body_cache.body(),
         &regioncx,
         &closure_region_requirements,
     );
 
     // We also have a `#[rustc_nll]` annotation that causes us to dump
     // information
-    dump_annotation(infcx, body_cache, def_id, &regioncx, &closure_region_requirements, errors_buffer);
+    dump_annotation(infcx, body_cache.body(), def_id, &regioncx, &closure_region_requirements, errors_buffer);
 
     (regioncx, polonius_output, closure_region_requirements)
 }
diff --git a/src/librustc_mir/borrow_check/nll/region_infer/values.rs b/src/librustc_mir/borrow_check/nll/region_infer/values.rs
index 5d1891c0bf6..16d2eca8f20 100644
--- a/src/librustc_mir/borrow_check/nll/region_infer/values.rs
+++ b/src/librustc_mir/borrow_check/nll/region_infer/values.rs
@@ -92,7 +92,7 @@ impl RegionValueElements {
     /// Pushes all predecessors of `index` onto `stack`.
     crate fn push_predecessors(
         &self,
-        body_cache: &ReadOnlyBodyCache<'_, '_>,
+        body_cache: ReadOnlyBodyCache<'_, '_>,
         index: PointIndex,
         stack: &mut Vec<PointIndex>,
     ) {
diff --git a/src/librustc_mir/borrow_check/nll/renumber.rs b/src/librustc_mir/borrow_check/nll/renumber.rs
index 57e977eacba..882b825c528 100644
--- a/src/librustc_mir/borrow_check/nll/renumber.rs
+++ b/src/librustc_mir/borrow_check/nll/renumber.rs
@@ -1,6 +1,6 @@
 use rustc::ty::subst::SubstsRef;
 use rustc::ty::{self, Ty, TyCtxt, TypeFoldable};
-use rustc::mir::{Body, BodyCache, Location, PlaceElem, Promoted};
+use rustc::mir::{BodyCache, Location, PlaceElem, Promoted};
 use rustc::mir::visit::{MutVisitor, TyContext};
 use rustc::infer::{InferCtxt, NLLRegionVariableOrigin};
 use rustc_index::vec::IndexVec;
@@ -9,8 +9,8 @@ use rustc_index::vec::IndexVec;
 /// inference variables, returning the number of variables created.
 pub fn renumber_mir<'tcx>(
     infcx: &InferCtxt<'_, 'tcx>,
-    body_cache: &mut BodyCache<&mut Body<'tcx>>,
-    promoted: &mut IndexVec<Promoted, BodyCache<&mut Body<'tcx>>>,
+    body_cache: &mut BodyCache<'tcx>,
+    promoted: &mut IndexVec<Promoted, BodyCache<'tcx>>,
 ) {
     debug!("renumber_mir()");
     debug!("renumber_mir: body.arg_count={:?}", body_cache.arg_count);
diff --git a/src/librustc_mir/borrow_check/nll/type_check/liveness/local_use_map.rs b/src/librustc_mir/borrow_check/nll/type_check/liveness/local_use_map.rs
index 181508a983a..0cd5dd80594 100644
--- a/src/librustc_mir/borrow_check/nll/type_check/liveness/local_use_map.rs
+++ b/src/librustc_mir/borrow_check/nll/type_check/liveness/local_use_map.rs
@@ -60,7 +60,7 @@ impl LocalUseMap {
     crate fn build(
         live_locals: &Vec<Local>,
         elements: &RegionValueElements,
-        body_cache: &ReadOnlyBodyCache<'_, '_>,
+        body_cache: ReadOnlyBodyCache<'_, '_>,
     ) -> Self {
         let nones = IndexVec::from_elem_n(None, body_cache.local_decls.len());
         let mut local_use_map = LocalUseMap {
diff --git a/src/librustc_mir/borrow_check/nll/type_check/liveness/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/liveness/mod.rs
index 833958814af..2dea6797ec7 100644
--- a/src/librustc_mir/borrow_check/nll/type_check/liveness/mod.rs
+++ b/src/librustc_mir/borrow_check/nll/type_check/liveness/mod.rs
@@ -28,7 +28,7 @@ mod trace;
 /// performed before
 pub(super) fn generate<'tcx>(
     typeck: &mut TypeChecker<'_, 'tcx>,
-    body_cache: &ReadOnlyBodyCache<'_, 'tcx>,
+    body_cache: ReadOnlyBodyCache<'_, 'tcx>,
     elements: &Rc<RegionValueElements>,
     flow_inits: &mut FlowAtLocation<'tcx, MaybeInitializedPlaces<'_, 'tcx>>,
     move_data: &MoveData<'tcx>,
@@ -48,7 +48,7 @@ pub(super) fn generate<'tcx>(
         let mut drop_used = Vec::new();
         polonius::populate_access_facts(
             typeck,
-            &body_cache,
+            body_cache,
             location_table,
             move_data,
             &mut drop_used,
diff --git a/src/librustc_mir/borrow_check/nll/type_check/liveness/polonius.rs b/src/librustc_mir/borrow_check/nll/type_check/liveness/polonius.rs
index 3320815688f..c17fbf0cdc9 100644
--- a/src/librustc_mir/borrow_check/nll/type_check/liveness/polonius.rs
+++ b/src/librustc_mir/borrow_check/nll/type_check/liveness/polonius.rs
@@ -97,7 +97,7 @@ fn add_var_uses_regions(typeck: &mut TypeChecker<'_, 'tcx>, local: Local, ty: Ty
 
 pub(super) fn populate_access_facts(
     typeck: &mut TypeChecker<'_, 'tcx>,
-    body_cache: &ReadOnlyBodyCache<'_, 'tcx>,
+    body_cache: ReadOnlyBodyCache<'_, 'tcx>,
     location_table: &LocationTable,
     move_data: &MoveData<'_>,
     drop_used: &mut Vec<(Local, Location)>,
diff --git a/src/librustc_mir/borrow_check/nll/type_check/liveness/trace.rs b/src/librustc_mir/borrow_check/nll/type_check/liveness/trace.rs
index 5929516ec4e..9a0392ab1d8 100644
--- a/src/librustc_mir/borrow_check/nll/type_check/liveness/trace.rs
+++ b/src/librustc_mir/borrow_check/nll/type_check/liveness/trace.rs
@@ -32,7 +32,7 @@ use std::rc::Rc;
 /// this respects `#[may_dangle]` annotations).
 pub(super) fn trace(
     typeck: &mut TypeChecker<'_, 'tcx>,
-    body_cache: &ReadOnlyBodyCache<'_, 'tcx>,
+    body_cache: ReadOnlyBodyCache<'_, 'tcx>,
     elements: &Rc<RegionValueElements>,
     flow_inits: &mut FlowAtLocation<'tcx, MaybeInitializedPlaces<'_, 'tcx>>,
     move_data: &MoveData<'tcx>,
@@ -41,7 +41,7 @@ pub(super) fn trace(
 ) {
     debug!("trace()");
 
-    let local_use_map = &LocalUseMap::build(&live_locals, elements, &body_cache);
+    let local_use_map = &LocalUseMap::build(&live_locals, elements, body_cache);
 
     let cx = LivenessContext {
         typeck,
@@ -71,7 +71,7 @@ struct LivenessContext<'me, 'typeck, 'flow, 'tcx> {
     elements: &'me RegionValueElements,
 
     /// MIR we are analyzing.
-    body_cache: &'me ReadOnlyBodyCache<'me, 'tcx>,
+    body_cache: ReadOnlyBodyCache<'me, 'tcx>,
 
     /// Mapping to/from the various indices used for initialization tracking.
     move_data: &'me MoveData<'tcx>,
@@ -211,7 +211,7 @@ impl LivenessResults<'me, 'typeck, 'flow, 'tcx> {
             }
 
             if self.use_live_at.insert(p) {
-                self.cx.elements.push_predecessors(&self.cx.body_cache, p, &mut self.stack)
+                self.cx.elements.push_predecessors(self.cx.body_cache, p, &mut self.stack)
             }
         }
     }
diff --git a/src/librustc_mir/borrow_check/nll/type_check/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs
index 8e966033bb8..92efd383c3c 100644
--- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs
+++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs
@@ -115,7 +115,7 @@ mod relate_tys;
 pub(crate) fn type_check<'tcx>(
     infcx: &InferCtxt<'_, 'tcx>,
     param_env: ty::ParamEnv<'tcx>,
-    body_cache: &ReadOnlyBodyCache<'_, 'tcx>,
+    body_cache: ReadOnlyBodyCache<'_, 'tcx>,
     promoted_cache: &IndexVec<Promoted, ReadOnlyBodyCache<'_, 'tcx>>,
     mir_def_id: DefId,
     universal_regions: &Rc<UniversalRegions<'tcx>>,
@@ -168,7 +168,7 @@ pub(crate) fn type_check<'tcx>(
         &mut borrowck_context,
         &universal_region_relations,
         |mut cx| {
-            cx.equate_inputs_and_outputs(body_cache, universal_regions, &normalized_inputs_and_output);
+            cx.equate_inputs_and_outputs(body_cache.body(), universal_regions, &normalized_inputs_and_output);
             liveness::generate(&mut cx, body_cache, elements, flow_inits, move_data, location_table);
 
             translate_outlives_facts(cx.borrowck_context);
@@ -185,7 +185,7 @@ fn type_check_internal<'a, 'tcx, R>(
     infcx: &'a InferCtxt<'a, 'tcx>,
     mir_def_id: DefId,
     param_env: ty::ParamEnv<'tcx>,
-    body_cache: &ReadOnlyBodyCache<'a, 'tcx>,
+    body_cache: ReadOnlyBodyCache<'a, 'tcx>,
     promoted_cache: &'a IndexVec<Promoted, ReadOnlyBodyCache<'_, 'tcx>>,
     region_bound_pairs: &'a RegionBoundPairs<'tcx>,
     implicit_region_bound: ty::Region<'tcx>,
@@ -195,7 +195,7 @@ fn type_check_internal<'a, 'tcx, R>(
 ) -> R where {
     let mut checker = TypeChecker::new(
         infcx,
-        body_cache,
+        body_cache.body(),
         mir_def_id,
         param_env,
         region_bound_pairs,
@@ -204,14 +204,14 @@ fn type_check_internal<'a, 'tcx, R>(
         universal_region_relations,
     );
     let errors_reported = {
-        let mut verifier = TypeVerifier::new(&mut checker, body_cache, promoted_cache);
+        let mut verifier = TypeVerifier::new(&mut checker, body_cache.body(), promoted_cache);
         verifier.visit_body(body_cache);
         verifier.errors_reported
     };
 
     if !errors_reported {
         // if verifier failed, don't do further checks to avoid ICEs
-        checker.typeck_mir(body_cache);
+        checker.typeck_mir(body_cache.body());
     }
 
     extra(&mut checker)
@@ -385,7 +385,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> {
         }
     }
 
-    fn visit_body(&mut self, body_cache: &ReadOnlyBodyCache<'_, 'tcx>) {
+    fn visit_body(&mut self, body_cache: ReadOnlyBodyCache<'_, 'tcx>) {
         self.sanitize_type(&"return type", body_cache.return_ty());
         for local_decl in &body_cache.local_decls {
             self.sanitize_type(local_decl, local_decl.ty);
@@ -464,7 +464,7 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> {
                 match kind {
                     StaticKind::Promoted(promoted, _) => {
                         if !self.errors_reported {
-                            let promoted_body_cache = &self.promoted_cache[*promoted];
+                            let promoted_body_cache = self.promoted_cache[*promoted];
                             self.sanitize_promoted(promoted_body_cache, location);
 
                             let promoted_ty = promoted_body_cache.return_ty();
@@ -535,7 +535,7 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> {
         place_ty
     }
 
-    fn sanitize_promoted(&mut self, promoted_body_cache: &ReadOnlyBodyCache<'b, 'tcx>, location: Location) {
+    fn sanitize_promoted(&mut self, promoted_body_cache: ReadOnlyBodyCache<'b, 'tcx>, location: Location) {
         // Determine the constraints from the promoted MIR by running the type
         // checker on the promoted MIR, then transfer the constraints back to
         // the main MIR, changing the locations to the provided location.
diff --git a/src/librustc_mir/borrow_check/used_muts.rs b/src/librustc_mir/borrow_check/used_muts.rs
index 430452efe42..27e6d803bd0 100644
--- a/src/librustc_mir/borrow_check/used_muts.rs
+++ b/src/librustc_mir/borrow_check/used_muts.rs
@@ -32,7 +32,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                 never_initialized_mut_locals: &mut never_initialized_mut_locals,
                 mbcx: self,
             };
-            visitor.visit_body(&visitor.mbcx.body_cache);
+            visitor.visit_body(visitor.mbcx.body_cache);
         }
 
         // Take the union of the existed `used_mut` set with those variables we've found were
diff --git a/src/librustc_mir/build/mod.rs b/src/librustc_mir/build/mod.rs
index eb9b401f272..28b4c9fd09b 100644
--- a/src/librustc_mir/build/mod.rs
+++ b/src/librustc_mir/build/mod.rs
@@ -24,7 +24,7 @@ use syntax_pos::Span;
 use super::lints;
 
 /// Construct the MIR for a given `DefId`.
-pub fn mir_build(tcx: TyCtxt<'_>, def_id: DefId) -> Body<'_> {
+pub fn mir_build(tcx: TyCtxt<'_>, def_id: DefId) -> BodyCache<'_> {
     let id = tcx.hir().as_local_hir_id(def_id).unwrap();
 
     // Figure out what primary body this item has.
@@ -196,7 +196,7 @@ pub fn mir_build(tcx: TyCtxt<'_>, def_id: DefId) -> Body<'_> {
 
         lints::check(tcx, &body, def_id);
 
-        body
+        BodyCache::new(body)
     })
 }
 
diff --git a/src/librustc_mir/const_eval.rs b/src/librustc_mir/const_eval.rs
index 0967b257885..0a205be0c58 100644
--- a/src/librustc_mir/const_eval.rs
+++ b/src/librustc_mir/const_eval.rs
@@ -354,7 +354,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
         }
         // This is a const fn. Call it.
         Ok(Some(match ecx.load_mir(instance.def, None) {
-            Ok(body) => body,
+            Ok(body_cache) => body_cache.body(),
             Err(err) => {
                 if let err_unsup!(NoMirFor(ref path)) = err.kind {
                     return Err(
@@ -697,7 +697,7 @@ pub fn const_eval_raw_provider<'tcx>(
 
     let res = ecx.load_mir(cid.instance.def, cid.promoted);
     res.and_then(
-        |body| eval_body_using_ecx(&mut ecx, cid, body)
+        |body_cache| eval_body_using_ecx(&mut ecx, cid, body_cache.body())
     ).and_then(|place| {
         Ok(RawConst {
             alloc_id: place.ptr.assert_ptr().alloc_id,
diff --git a/src/librustc_mir/dataflow/impls/storage_liveness.rs b/src/librustc_mir/dataflow/impls/storage_liveness.rs
index 2e164765043..fc344f9b252 100644
--- a/src/librustc_mir/dataflow/impls/storage_liveness.rs
+++ b/src/librustc_mir/dataflow/impls/storage_liveness.rs
@@ -75,19 +75,19 @@ impl<'a, 'tcx> BottomValue for MaybeStorageLive<'a, 'tcx> {
 /// Dataflow analysis that determines whether each local requires storage at a
 /// given location; i.e. whether its storage can go away without being observed.
 pub struct RequiresStorage<'mir, 'tcx> {
-    body_cache: &'mir ReadOnlyBodyCache<'mir, 'tcx>,
+    body_cache: ReadOnlyBodyCache<'mir, 'tcx>,
     borrowed_locals:
         RefCell<DataflowResultsRefCursor<'mir, 'tcx, HaveBeenBorrowedLocals<'mir, 'tcx>>>,
 }
 
 impl<'mir, 'tcx: 'mir> RequiresStorage<'mir, 'tcx> {
     pub fn new(
-        body_cache: &'mir ReadOnlyBodyCache<'mir, 'tcx>,
+        body_cache: ReadOnlyBodyCache<'mir, 'tcx>,
         borrowed_locals: &'mir DataflowResults<'tcx, HaveBeenBorrowedLocals<'mir, 'tcx>>,
     ) -> Self {
         RequiresStorage {
             body_cache,
-            borrowed_locals: RefCell::new(DataflowResultsCursor::new(borrowed_locals, body_cache)),
+            borrowed_locals: RefCell::new(DataflowResultsCursor::new(borrowed_locals, body_cache.body())),
         }
     }
 
@@ -187,7 +187,7 @@ impl<'mir, 'tcx> RequiresStorage<'mir, 'tcx> {
             sets,
             borrowed_locals: &self.borrowed_locals,
         };
-        visitor.visit_location(&self.body_cache, loc);
+        visitor.visit_location(self.body_cache, loc);
     }
 
     /// Gen locals that are newly borrowed. This includes borrowing any part of
diff --git a/src/librustc_mir/interpret/eval_context.rs b/src/librustc_mir/interpret/eval_context.rs
index 6c16c4f2219..043ba09f52a 100644
--- a/src/librustc_mir/interpret/eval_context.rs
+++ b/src/librustc_mir/interpret/eval_context.rs
@@ -292,7 +292,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         &self,
         instance: ty::InstanceDef<'tcx>,
         promoted: Option<mir::Promoted>,
-    ) -> InterpResult<'tcx, &'tcx mir::Body<'tcx>> {
+    ) -> InterpResult<'tcx, mir::ReadOnlyBodyCache<'tcx, 'tcx>> {
         // do not continue if typeck errors occurred (can only occur in local crate)
         let did = instance.def_id();
         if did.is_local()
@@ -303,11 +303,11 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         }
         trace!("load mir(instance={:?}, promoted={:?})", instance, promoted);
         if let Some(promoted) = promoted {
-            return Ok(&self.tcx.promoted_mir(did)[promoted]);
+            return Ok(self.tcx.promoted_mir(did)[promoted].read_only());
         }
         match instance {
             ty::InstanceDef::Item(def_id) => if self.tcx.is_mir_available(did) {
-                Ok(self.tcx.optimized_mir(did))
+                Ok(self.tcx.optimized_mir(did).read_only())
             } else {
                 throw_unsup!(NoMirFor(self.tcx.def_path_str(def_id)))
             },
diff --git a/src/librustc_mir/monomorphize/collector.rs b/src/librustc_mir/monomorphize/collector.rs
index 285bdf50d4a..ecfbe529b95 100644
--- a/src/librustc_mir/monomorphize/collector.rs
+++ b/src/librustc_mir/monomorphize/collector.rs
@@ -186,7 +186,7 @@ use rustc::ty::{self, TypeFoldable, Ty, TyCtxt, GenericParamDefKind, Instance};
 use rustc::ty::print::obsolete::DefPathBasedNames;
 use rustc::ty::adjustment::{CustomCoerceUnsized, PointerCast};
 use rustc::session::config::EntryFnType;
-use rustc::mir::{self, BodyCache, Location, PlaceBase, Static, StaticKind};
+use rustc::mir::{self, Location, PlaceBase, Static, StaticKind};
 use rustc::mir::visit::Visitor as MirVisitor;
 use rustc::mir::mono::{MonoItem, InstantiationMode};
 use rustc::mir::interpret::{Scalar, GlobalId, GlobalAlloc, ErrorHandled};
@@ -1248,15 +1248,15 @@ fn collect_neighbours<'tcx>(
     output: &mut Vec<MonoItem<'tcx>>,
 ) {
     debug!("collect_neighbours: {:?}", instance.def_id());
-    let body = tcx.instance_mir(instance.def);
-    let body_cache = BodyCache::new(body).read_only();
+    let body_cache = tcx.instance_mir(instance.def);
+    let body = body_cache.body();
 
     MirNeighborCollector {
         tcx,
         body: &body,
         output,
         param_substs: instance.substs,
-    }.visit_body(&body_cache);
+    }.visit_body(body_cache);
 }
 
 fn def_id_to_string(tcx: TyCtxt<'_>, def_id: DefId) -> String {
diff --git a/src/librustc_mir/shim.rs b/src/librustc_mir/shim.rs
index c80b6e38ac5..8f97a4e7f5c 100644
--- a/src/librustc_mir/shim.rs
+++ b/src/librustc_mir/shim.rs
@@ -26,7 +26,7 @@ pub fn provide(providers: &mut Providers<'_>) {
     providers.mir_shims = make_shim;
 }
 
-fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>) -> &'tcx Body<'tcx> {
+fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>) -> &'tcx BodyCache<'tcx> {
     debug!("make_shim({:?})", instance);
 
     let mut result = match instance {
@@ -113,7 +113,7 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>) -> &'tcx
             bug!("creating shims from intrinsics ({:?}) is unsupported", instance)
         }
     };
-    debug!("make_shim({:?}) = untransformed {:?}", instance, result);
+    debug!("make_shim({:?}) = untransformed {:?}", instance, result.body());
 
     run_passes(tcx, &mut result, instance, None, MirPhase::Const, &[
         &add_moves_for_packed_drops::AddMovesForPackedDrops,
@@ -123,7 +123,7 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>) -> &'tcx
         &add_call_guards::CriticalCallEdges,
     ]);
 
-    debug!("make_shim({:?}) = {:?}", instance, result);
+    debug!("make_shim({:?}) = {:?}", instance, result.body());
 
     tcx.arena.alloc(result)
 }
@@ -164,7 +164,7 @@ fn local_decls_for_sig<'tcx>(sig: &ty::FnSig<'tcx>, span: Span)
         .collect()
 }
 
-fn build_drop_shim<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, ty: Option<Ty<'tcx>>) -> Body<'tcx> {
+fn build_drop_shim<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, ty: Option<Ty<'tcx>>) -> BodyCache<'tcx> {
     debug!("build_drop_shim(def_id={:?}, ty={:?})", def_id, ty);
 
     // Check if this is a generator, if so, return the drop glue for it
@@ -202,7 +202,7 @@ fn build_drop_shim<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, ty: Option<Ty<'tcx>>)
         sig.inputs().len(),
         span);
 
-    let mut body_cache = BodyCache::new(&mut body);
+    let mut body_cache = BodyCache::new(body);
 
     if let Some(..) = ty {
         // The first argument (index 0), but add 1 for the return value.
@@ -238,8 +238,7 @@ fn build_drop_shim<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, ty: Option<Ty<'tcx>>)
         patch.apply(&mut body_cache);
     }
 
-    // TODO(pfaia) return owning body cache...
-    body
+    body_cache
 }
 
 fn new_body<'tcx>(
@@ -317,7 +316,7 @@ impl<'a, 'tcx> DropElaborator<'a, 'tcx> for DropShimElaborator<'a, 'tcx> {
 }
 
 /// Builds a `Clone::clone` shim for `self_ty`. Here, `def_id` is `Clone::clone`.
-fn build_clone_shim<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, self_ty: Ty<'tcx>) -> Body<'tcx> {
+fn build_clone_shim<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, self_ty: Ty<'tcx>) -> BodyCache<'tcx> {
     debug!("build_clone_shim(def_id={:?})", def_id);
 
     let param_env = tcx.param_env(def_id);
@@ -346,7 +345,7 @@ fn build_clone_shim<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, self_ty: Ty<'tcx>) -
         }
     };
 
-    builder.into_mir()
+    BodyCache::new(builder.into_mir())
 }
 
 struct CloneShimBuilder<'tcx> {
@@ -707,7 +706,7 @@ fn build_call_shim<'tcx>(
     rcvr_adjustment: Adjustment,
     call_kind: CallKind,
     untuple_args: Option<&[Ty<'tcx>]>,
-) -> Body<'tcx> {
+) -> BodyCache<'tcx> {
     debug!("build_call_shim(def_id={:?}, rcvr_adjustment={:?}, \
             call_kind={:?}, untuple_args={:?})",
            def_id, rcvr_adjustment, call_kind, untuple_args);
@@ -842,10 +841,10 @@ fn build_call_shim<'tcx>(
     if let Abi::RustCall = sig.abi {
         body.spread_arg = Some(Local::new(sig.inputs().len()));
     }
-    body
+    BodyCache::new(body)
 }
 
-pub fn build_adt_ctor(tcx: TyCtxt<'_>, ctor_id: DefId) -> &Body<'_> {
+pub fn build_adt_ctor(tcx: TyCtxt<'_>, ctor_id: DefId) -> &BodyCache<'_> {
     debug_assert!(tcx.is_constructor(ctor_id));
 
     let span = tcx.hir().span_if_local(ctor_id)
@@ -929,5 +928,5 @@ pub fn build_adt_ctor(tcx: TyCtxt<'_>, ctor_id: DefId) -> &Body<'_> {
         |_, _| Ok(()),
     );
 
-    tcx.arena.alloc(body)
+    tcx.arena.alloc(BodyCache::new(body))
 }
diff --git a/src/librustc_mir/transform/add_call_guards.rs b/src/librustc_mir/transform/add_call_guards.rs
index bf3df1ae2fd..071ad002c16 100644
--- a/src/librustc_mir/transform/add_call_guards.rs
+++ b/src/librustc_mir/transform/add_call_guards.rs
@@ -31,22 +31,22 @@ pub use self::AddCallGuards::*;
  */
 
 impl<'tcx> MirPass<'tcx> for AddCallGuards {
-    fn run_pass(&self, _tcx: TyCtxt<'tcx>, _src: MirSource<'tcx>, body: &mut Body<'tcx>) {
-        self.add_call_guards(body);
+    fn run_pass(&self, _tcx: TyCtxt<'tcx>, _src: MirSource<'tcx>, body_cache: &mut BodyCache<'tcx>) {
+        self.add_call_guards(body_cache);
     }
 }
 
 impl AddCallGuards {
-    pub fn add_call_guards(&self, body: &mut Body<'_>) {
+    pub fn add_call_guards(&self, body_cache: &mut BodyCache<'_>) {
         let pred_count: IndexVec<_, _> =
-            body.predecessors().iter().map(|ps| ps.len()).collect();
+            body_cache.predecessors().iter().map(|ps| ps.len()).collect();
 
         // We need a place to store the new blocks generated
         let mut new_blocks = Vec::new();
 
-        let cur_len = body.basic_blocks().len();
+        let cur_len = body_cache.basic_blocks().len();
 
-        for block in body.basic_blocks_mut() {
+        for block in body_cache.basic_blocks_mut() {
             match block.terminator {
                 Some(Terminator {
                     kind: TerminatorKind::Call {
@@ -78,6 +78,6 @@ impl AddCallGuards {
 
         debug!("Broke {} N edges", new_blocks.len());
 
-        body.basic_blocks_mut().extend(new_blocks);
+        body_cache.basic_blocks_mut().extend(new_blocks);
     }
 }
diff --git a/src/librustc_mir/transform/add_moves_for_packed_drops.rs b/src/librustc_mir/transform/add_moves_for_packed_drops.rs
index 052631ddff3..aebd632978a 100644
--- a/src/librustc_mir/transform/add_moves_for_packed_drops.rs
+++ b/src/librustc_mir/transform/add_moves_for_packed_drops.rs
@@ -40,15 +40,15 @@ use crate::util;
 pub struct AddMovesForPackedDrops;
 
 impl<'tcx> MirPass<'tcx> for AddMovesForPackedDrops {
-    fn run_pass(&self, tcx: TyCtxt<'tcx>, src: MirSource<'tcx>, body: &mut Body<'tcx>) {
-        debug!("add_moves_for_packed_drops({:?} @ {:?})", src, body.span);
-        add_moves_for_packed_drops(tcx, body, src.def_id());
+    fn run_pass(&self, tcx: TyCtxt<'tcx>, src: MirSource<'tcx>, body_cache: &mut BodyCache<'tcx>) {
+        debug!("add_moves_for_packed_drops({:?} @ {:?})", src, body_cache.span);
+        add_moves_for_packed_drops(tcx, body_cache, src.def_id());
     }
 }
 
-pub fn add_moves_for_packed_drops<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>, def_id: DefId) {
-    let patch = add_moves_for_packed_drops_patch(tcx, body, def_id);
-    patch.apply(body);
+pub fn add_moves_for_packed_drops<'tcx>(tcx: TyCtxt<'tcx>, body_cache: &mut BodyCache<'tcx>, def_id: DefId) {
+    let patch = add_moves_for_packed_drops_patch(tcx, body_cache, def_id);
+    patch.apply(body_cache);
 }
 
 fn add_moves_for_packed_drops_patch<'tcx>(
diff --git a/src/librustc_mir/transform/add_retag.rs b/src/librustc_mir/transform/add_retag.rs
index b56a1b263fd..25594ab20a9 100644
--- a/src/librustc_mir/transform/add_retag.rs
+++ b/src/librustc_mir/transform/add_retag.rs
@@ -59,12 +59,12 @@ fn may_be_reference<'tcx>(ty: Ty<'tcx>) -> bool {
 }
 
 impl<'tcx> MirPass<'tcx> for AddRetag {
-    fn run_pass(&self, tcx: TyCtxt<'tcx>, _src: MirSource<'tcx>, body: &mut Body<'tcx>) {
+    fn run_pass(&self, tcx: TyCtxt<'tcx>, _src: MirSource<'tcx>, body_cache: &mut BodyCache<'tcx>) {
         if !tcx.sess.opts.debugging_opts.mir_emit_retag {
             return;
         }
-        let (span, arg_count) = (body.span, body.arg_count);
-        let (basic_blocks, local_decls) = body.basic_blocks_and_local_decls_mut();
+        let (span, arg_count) = (body_cache.span, body_cache.arg_count);
+        let (basic_blocks, local_decls) = body_cache.basic_blocks_and_local_decls_mut();
         let needs_retag = |place: &Place<'tcx>| {
             // FIXME: Instead of giving up for unstable places, we should introduce
             // a temporary and retag on that.
diff --git a/src/librustc_mir/transform/check_unsafety.rs b/src/librustc_mir/transform/check_unsafety.rs
index d12d21aee6a..78628474bce 100644
--- a/src/librustc_mir/transform/check_unsafety.rs
+++ b/src/librustc_mir/transform/check_unsafety.rs
@@ -528,7 +528,7 @@ fn unsafety_check_result(tcx: TyCtxt<'_>, def_id: DefId) -> UnsafetyCheckResult
         hir::BodyOwnerKind::Static(_) => (true, false),
     };
     let mut checker = UnsafetyChecker::new(const_context, min_const_fn, body, tcx, param_env);
-    checker.visit_body(body);
+    checker.visit_body(body.read_only());
 
     check_unused_unsafe(tcx, def_id, &checker.used_unsafe, &mut checker.inherited_blocks);
     UnsafetyCheckResult {
diff --git a/src/librustc_mir/transform/cleanup_post_borrowck.rs b/src/librustc_mir/transform/cleanup_post_borrowck.rs
index 4fd4fe45cd4..3ea41816ebe 100644
--- a/src/librustc_mir/transform/cleanup_post_borrowck.rs
+++ b/src/librustc_mir/transform/cleanup_post_borrowck.rs
@@ -16,7 +16,7 @@
 //! [`FakeRead`]: rustc::mir::StatementKind::FakeRead
 //! [`Nop`]: rustc::mir::StatementKind::Nop
 
-use rustc::mir::{BorrowKind, Rvalue, Location, Body};
+use rustc::mir::{BodyCache, BorrowKind, Rvalue, Location};
 use rustc::mir::{Statement, StatementKind};
 use rustc::mir::visit::MutVisitor;
 use rustc::ty::TyCtxt;
@@ -29,9 +29,9 @@ pub struct DeleteNonCodegenStatements<'tcx> {
 }
 
 impl<'tcx> MirPass<'tcx> for CleanupNonCodegenStatements {
-    fn run_pass(&self, tcx: TyCtxt<'tcx>, _source: MirSource<'tcx>, body: &mut Body<'tcx>) {
+    fn run_pass(&self, tcx: TyCtxt<'tcx>, _source: MirSource<'tcx>, body_cache: &mut BodyCache<'tcx>) {
         let mut delete = DeleteNonCodegenStatements { tcx };
-        delete.visit_body(body);
+        delete.visit_body(body_cache);
     }
 }
 
diff --git a/src/librustc_mir/transform/const_prop.rs b/src/librustc_mir/transform/const_prop.rs
index 67958af3460..f79f375a7e8 100644
--- a/src/librustc_mir/transform/const_prop.rs
+++ b/src/librustc_mir/transform/const_prop.rs
@@ -7,9 +7,10 @@ use std::cell::Cell;
 use rustc::hir::def::DefKind;
 use rustc::hir::def_id::DefId;
 use rustc::mir::{
-    AggregateKind, Constant, Location, Place, PlaceBase, Body, Operand, Rvalue, Local, UnOp,
-    StatementKind, Statement, LocalKind, TerminatorKind, Terminator,  ClearCrossCrate, SourceInfo,
-    BinOp, SourceScope, SourceScopeData, LocalDecl, BasicBlock, RETURN_PLACE,
+    AggregateKind, Constant, Location, Place, PlaceBase, Body, BodyCache, Operand, Local, UnOp,
+    Rvalue. StatementKind, Statement, LocalKind, TerminatorKind, Terminator,  ClearCrossCrate,
+    SourceInfo, BinOp, SourceScope, SourceScopeData, LocalDecl, BasicBlock, ReadOnlyBodyCache,
+    RETURN_PLACE
 };
 use rustc::mir::visit::{
     Visitor, PlaceContext, MutatingUseContext, MutVisitor, NonMutatingUseContext,
@@ -41,7 +42,7 @@ const MAX_ALLOC_LIMIT: u64 = 1024;
 pub struct ConstProp;
 
 impl<'tcx> MirPass<'tcx> for ConstProp {
-    fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut Body<'tcx>) {
+    fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body_cache: &mut BodyCache<'tcx>) {
         // will be evaluated by miri and produce its errors there
         if source.promoted.is_some() {
             return;
@@ -76,15 +77,15 @@ impl<'tcx> MirPass<'tcx> for ConstProp {
 
         let dummy_body =
             &Body::new(
-                body.basic_blocks().clone(),
-                body.source_scopes.clone(),
-                body.local_decls.clone(),
+                body_cache.basic_blocks().clone(),
+                body_cache.source_scopes.clone(),
+                body_cache.local_decls.clone(),
                 Default::default(),
-                body.arg_count,
+                body_cache.arg_count,
                 Default::default(),
                 tcx.def_span(source.def_id()),
                 Default::default(),
-                body.generator_kind,
+                body_cache.generator_kind,
             );
 
         // FIXME(oli-obk, eddyb) Optimize locals (or even local paths) to hold
@@ -92,12 +93,12 @@ impl<'tcx> MirPass<'tcx> for ConstProp {
         // That would require an uniform one-def no-mutation analysis
         // and RPO (or recursing when needing the value of a local).
         let mut optimization_finder = ConstPropagator::new(
-            body,
+            body_cache.read_only(),
             dummy_body,
             tcx,
             source
         );
-        optimization_finder.visit_body(body);
+        optimization_finder.visit_body(body_cache);
 
         trace!("ConstProp done for {:?}", source.def_id());
     }
@@ -284,7 +285,7 @@ impl<'mir, 'tcx> HasTyCtxt<'tcx> for ConstPropagator<'mir, 'tcx> {
 
 impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
     fn new(
-        body: &Body<'tcx>,
+        body_cache: ReadOnlyBodyCache<'mir, 'tcx>,
         dummy_body: &'mir Body<'tcx>,
         tcx: TyCtxt<'tcx>,
         source: MirSource<'tcx>,
@@ -293,7 +294,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
         let param_env = tcx.param_env(def_id);
         let span = tcx.def_span(def_id);
         let mut ecx = InterpCx::new(tcx.at(span), param_env, ConstPropMachine, ());
-        let can_const_prop = CanConstProp::check(body);
+        let can_const_prop = CanConstProp::check(body_cache);
 
         let substs = &InternalSubsts::identity_for_item(tcx, def_id);
 
@@ -325,9 +326,9 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
             can_const_prop,
             // FIXME(eddyb) avoid cloning these two fields more than once,
             // by accessing them through `ecx` instead.
-            source_scopes: body.source_scopes.clone(),
+            source_scopes: body_cache.source_scopes.clone(),
             //FIXME(wesleywiser) we can't steal this because `Visitor::super_visit_body()` needs it
-            local_decls: body.local_decls.clone(),
+            local_decls: body_cache.local_decls.clone(),
             ret: ret.map(Into::into),
         }
     }
@@ -678,10 +679,10 @@ struct CanConstProp {
 
 impl CanConstProp {
     /// returns true if `local` can be propagated
-    fn check(body: &Body<'_>) -> IndexVec<Local, bool> {
+    fn check(body_cache: ReadOnlyBodyCache<'_, '_>) -> IndexVec<Local, bool> {
         let mut cpv = CanConstProp {
-            can_const_prop: IndexVec::from_elem(true, &body.local_decls),
-            found_assignment: IndexVec::from_elem(false, &body.local_decls),
+            can_const_prop: IndexVec::from_elem(true, &body_cache.local_decls),
+            found_assignment: IndexVec::from_elem(false, &body_cache.local_decls),
         };
         for (local, val) in cpv.can_const_prop.iter_enumerated_mut() {
             // cannot use args at all
@@ -689,14 +690,14 @@ impl CanConstProp {
             //        lint for x != y
             // FIXME(oli-obk): lint variables until they are used in a condition
             // FIXME(oli-obk): lint if return value is constant
-            let local_kind = body.local_kind(local);
+            let local_kind = body_cache.local_kind(local);
             *val = local_kind == LocalKind::Temp || local_kind == LocalKind::ReturnPointer;
 
             if !*val {
                 trace!("local {:?} can't be propagated because it's not a temporary", local);
             }
         }
-        cpv.visit_body(body);
+        cpv.visit_body(body_cache);
         cpv.can_const_prop
     }
 }
diff --git a/src/librustc_mir/transform/copy_prop.rs b/src/librustc_mir/transform/copy_prop.rs
index 4c26feac4af..637f4792029 100644
--- a/src/librustc_mir/transform/copy_prop.rs
+++ b/src/librustc_mir/transform/copy_prop.rs
@@ -19,7 +19,10 @@
 //! (non-mutating) use of `SRC`. These restrictions are conservative and may be relaxed in the
 //! future.
 
-use rustc::mir::{Constant, Local, LocalKind, Location, Place, Body, Operand, Rvalue, StatementKind};
+use rustc::mir::{
+    Constant, Local, LocalKind, Location, Place, Body, BodyCache, Operand, Rvalue,
+    StatementKind
+};
 use rustc::mir::visit::MutVisitor;
 use rustc::ty::TyCtxt;
 use crate::transform::{MirPass, MirSource};
@@ -28,23 +31,23 @@ use crate::util::def_use::DefUseAnalysis;
 pub struct CopyPropagation;
 
 impl<'tcx> MirPass<'tcx> for CopyPropagation {
-    fn run_pass(&self, tcx: TyCtxt<'tcx>, _source: MirSource<'tcx>, body: &mut Body<'tcx>) {
+    fn run_pass(&self, tcx: TyCtxt<'tcx>, _source: MirSource<'tcx>, body_cache: &mut BodyCache<'tcx>) {
         // We only run when the MIR optimization level is > 1.
         // This avoids a slow pass, and messing up debug info.
         if tcx.sess.opts.debugging_opts.mir_opt_level <= 1 {
             return;
         }
 
-        let mut def_use_analysis = DefUseAnalysis::new(body);
+        let mut def_use_analysis = DefUseAnalysis::new(body_cache);
         loop {
-            def_use_analysis.analyze(body);
+            def_use_analysis.analyze(body_cache.read_only());
 
-            if eliminate_self_assignments(body, &def_use_analysis) {
-                def_use_analysis.analyze(body);
+            if eliminate_self_assignments(body_cache, &def_use_analysis) {
+                def_use_analysis.analyze(body_cache.read_only());
             }
 
             let mut changed = false;
-            for dest_local in body.local_decls.indices() {
+            for dest_local in body_cache.local_decls.indices() {
                 debug!("considering destination local: {:?}", dest_local);
 
                 let action;
@@ -71,7 +74,7 @@ impl<'tcx> MirPass<'tcx> for CopyPropagation {
                     }
                     // Conservatively gives up if the dest is an argument,
                     // because there may be uses of the original argument value.
-                    if body.local_kind(dest_local) == LocalKind::Arg {
+                    if body_cache.local_kind(dest_local) == LocalKind::Arg {
                         debug!("  Can't copy-propagate local: dest {:?} (argument)",
                             dest_local);
                         continue;
@@ -79,7 +82,7 @@ impl<'tcx> MirPass<'tcx> for CopyPropagation {
                     let dest_place_def = dest_use_info.defs_not_including_drop().next().unwrap();
                     location = dest_place_def.location;
 
-                    let basic_block = &body[location.block];
+                    let basic_block = &body_cache[location.block];
                     let statement_index = location.statement_index;
                     let statement = match basic_block.statements.get(statement_index) {
                         Some(statement) => statement,
@@ -97,7 +100,7 @@ impl<'tcx> MirPass<'tcx> for CopyPropagation {
                                     let maybe_action = match operand {
                                         Operand::Copy(ref src_place) |
                                         Operand::Move(ref src_place) => {
-                                            Action::local_copy(&body, &def_use_analysis, src_place)
+                                            Action::local_copy(&body_cache, &def_use_analysis, src_place)
                                         }
                                         Operand::Constant(ref src_constant) => {
                                             Action::constant(src_constant)
@@ -127,7 +130,7 @@ impl<'tcx> MirPass<'tcx> for CopyPropagation {
                 }
 
                 changed =
-                    action.perform(body, &def_use_analysis, dest_local, location, tcx) || changed;
+                    action.perform(body_cache, &def_use_analysis, dest_local, location, tcx) || changed;
                 // FIXME(pcwalton): Update the use-def chains to delete the instructions instead of
                 // regenerating the chains.
                 break
@@ -242,7 +245,7 @@ impl<'tcx> Action<'tcx> {
     }
 
     fn perform(self,
-               body: &mut Body<'tcx>,
+               body_cache: &mut BodyCache<'tcx>,
                def_use_analysis: &DefUseAnalysis,
                dest_local: Local,
                location: Location,
@@ -260,21 +263,21 @@ impl<'tcx> Action<'tcx> {
                        src_local);
                 for place_use in &def_use_analysis.local_info(dest_local).defs_and_uses {
                     if place_use.context.is_storage_marker() {
-                        body.make_statement_nop(place_use.location)
+                        body_cache.make_statement_nop(place_use.location)
                     }
                 }
                 for place_use in &def_use_analysis.local_info(src_local).defs_and_uses {
                     if place_use.context.is_storage_marker() {
-                        body.make_statement_nop(place_use.location)
+                        body_cache.make_statement_nop(place_use.location)
                     }
                 }
 
                 // Replace all uses of the destination local with the source local.
-                def_use_analysis.replace_all_defs_and_uses_with(dest_local, body, src_local, tcx);
+                def_use_analysis.replace_all_defs_and_uses_with(dest_local, body_cache, src_local, tcx);
 
                 // Finally, zap the now-useless assignment instruction.
                 debug!("  Deleting assignment");
-                body.make_statement_nop(location);
+                body_cache.make_statement_nop(location);
 
                 true
             }
@@ -288,7 +291,7 @@ impl<'tcx> Action<'tcx> {
                 let dest_local_info = def_use_analysis.local_info(dest_local);
                 for place_use in &dest_local_info.defs_and_uses {
                     if place_use.context.is_storage_marker() {
-                        body.make_statement_nop(place_use.location)
+                        body_cache.make_statement_nop(place_use.location)
                     }
                 }
 
@@ -297,7 +300,7 @@ impl<'tcx> Action<'tcx> {
                                                                   src_constant,
                                                                   tcx);
                 for dest_place_use in &dest_local_info.defs_and_uses {
-                    visitor.visit_location(body, dest_place_use.location)
+                    visitor.visit_location(body_cache, dest_place_use.location)
                 }
 
                 // Zap the assignment instruction if we eliminated all the uses. We won't have been
@@ -308,7 +311,7 @@ impl<'tcx> Action<'tcx> {
                     debug!("  {} of {} use(s) replaced; deleting assignment",
                            visitor.uses_replaced,
                            use_count);
-                    body.make_statement_nop(location);
+                    body_cache.make_statement_nop(location);
                     true
                 } else if visitor.uses_replaced == 0 {
                     debug!("  No uses replaced; not deleting assignment");
diff --git a/src/librustc_mir/transform/deaggregator.rs b/src/librustc_mir/transform/deaggregator.rs
index cdde9e12edc..3d0bf6192dc 100644
--- a/src/librustc_mir/transform/deaggregator.rs
+++ b/src/librustc_mir/transform/deaggregator.rs
@@ -6,8 +6,8 @@ use crate::util::expand_aggregate;
 pub struct Deaggregator;
 
 impl<'tcx> MirPass<'tcx> for Deaggregator {
-    fn run_pass(&self, tcx: TyCtxt<'tcx>, _source: MirSource<'tcx>, body: &mut Body<'tcx>) {
-        let (basic_blocks, local_decls) = body.basic_blocks_and_local_decls_mut();
+    fn run_pass(&self, tcx: TyCtxt<'tcx>, _source: MirSource<'tcx>, body_cache: &mut BodyCache<'tcx>) {
+        let (basic_blocks, local_decls) = body_cache.basic_blocks_and_local_decls_mut();
         let local_decls = &*local_decls;
         for bb in basic_blocks {
             bb.expand_statements(|stmt| {
diff --git a/src/librustc_mir/transform/dump_mir.rs b/src/librustc_mir/transform/dump_mir.rs
index ed0eff943a1..987f0fde2e3 100644
--- a/src/librustc_mir/transform/dump_mir.rs
+++ b/src/librustc_mir/transform/dump_mir.rs
@@ -5,7 +5,7 @@ use std::fmt;
 use std::fs::File;
 use std::io;
 
-use rustc::mir::Body;
+use rustc::mir::{Body, BodyCache};
 use rustc::session::config::{OutputFilenames, OutputType};
 use rustc::ty::TyCtxt;
 use crate::transform::{MirPass, MirSource};
@@ -18,7 +18,7 @@ impl<'tcx> MirPass<'tcx> for Marker {
         Cow::Borrowed(self.0)
     }
 
-    fn run_pass(&self, _tcx: TyCtxt<'tcx>, _source: MirSource<'tcx>, _body: &mut Body<'tcx>) {
+    fn run_pass(&self, _tcx: TyCtxt<'tcx>, _source: MirSource<'tcx>, _body_cache: &mut BodyCache<'tcx>) {
     }
 }
 
diff --git a/src/librustc_mir/transform/elaborate_drops.rs b/src/librustc_mir/transform/elaborate_drops.rs
index f91a08bcd9a..378d991025a 100644
--- a/src/librustc_mir/transform/elaborate_drops.rs
+++ b/src/librustc_mir/transform/elaborate_drops.rs
@@ -21,17 +21,17 @@ use syntax_pos::Span;
 pub struct ElaborateDrops;
 
 impl<'tcx> MirPass<'tcx> for ElaborateDrops {
-    fn run_pass(&self, tcx: TyCtxt<'tcx>, src: MirSource<'tcx>, body: &mut Body<'tcx>) {
-        debug!("elaborate_drops({:?} @ {:?})", src, body.span);
+    fn run_pass(&self, tcx: TyCtxt<'tcx>, src: MirSource<'tcx>, body_cache: &mut BodyCache<'tcx>) {
+        debug!("elaborate_drops({:?} @ {:?})", src, body_cache.span);
 
         let def_id = src.def_id();
         let param_env = tcx.param_env(src.def_id()).with_reveal_all();
-        let move_data = match MoveData::gather_moves(body, tcx) {
+        let move_data = match MoveData::gather_moves(body_cache, tcx) {
             Ok(move_data) => move_data,
             Err(_) => bug!("No `move_errors` should be allowed in MIR borrowck"),
         };
         let elaborate_patch = {
-            let body = &*body;
+            let body = &*body_cache;
             let env = MoveDataParamEnv {
                 move_data,
                 param_env,
@@ -56,7 +56,7 @@ impl<'tcx> MirPass<'tcx> for ElaborateDrops {
                 patch: MirPatch::new(body),
             }.elaborate()
         };
-        elaborate_patch.apply(body);
+        elaborate_patch.apply(body_cache);
     }
 }
 
diff --git a/src/librustc_mir/transform/erase_regions.rs b/src/librustc_mir/transform/erase_regions.rs
index b30e2de4ca0..ff07f65d22c 100644
--- a/src/librustc_mir/transform/erase_regions.rs
+++ b/src/librustc_mir/transform/erase_regions.rs
@@ -62,7 +62,7 @@ impl MutVisitor<'tcx> for EraseRegionsVisitor<'tcx> {
 pub struct EraseRegions;
 
 impl<'tcx> MirPass<'tcx> for EraseRegions {
-    fn run_pass(&self, tcx: TyCtxt<'tcx>, _: MirSource<'tcx>, body: &mut Body<'tcx>) {
-        EraseRegionsVisitor::new(tcx).visit_body(body);
+    fn run_pass(&self, tcx: TyCtxt<'tcx>, _: MirSource<'tcx>, body_cache: &mut BodyCache<'tcx>) {
+        EraseRegionsVisitor::new(tcx).visit_body(body_cache);
     }
 }
diff --git a/src/librustc_mir/transform/generator.rs b/src/librustc_mir/transform/generator.rs
index 37205e65a0a..503d24e56ae 100644
--- a/src/librustc_mir/transform/generator.rs
+++ b/src/librustc_mir/transform/generator.rs
@@ -378,9 +378,9 @@ impl MutVisitor<'tcx> for TransformVisitor<'tcx> {
 fn make_generator_state_argument_indirect<'tcx>(
     tcx: TyCtxt<'tcx>,
     def_id: DefId,
-    body: &mut Body<'tcx>,
+    body_cache: &mut BodyCache<'tcx>,
 ) {
-    let gen_ty = body.local_decls.raw[1].ty;
+    let gen_ty = body_cache.local_decls.raw[1].ty;
 
     let region = ty::ReFree(ty::FreeRegion {
         scope: def_id,
@@ -395,14 +395,14 @@ fn make_generator_state_argument_indirect<'tcx>(
     });
 
     // Replace the by value generator argument
-    body.local_decls.raw[1].ty = ref_gen_ty;
+    body_cache.local_decls.raw[1].ty = ref_gen_ty;
 
     // Add a deref to accesses of the generator state
-    DerefArgVisitor { tcx }.visit_body(body);
+    DerefArgVisitor { tcx }.visit_body(body_cache);
 }
 
-fn make_generator_state_argument_pinned<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
-    let ref_gen_ty = body.local_decls.raw[1].ty;
+fn make_generator_state_argument_pinned<'tcx>(tcx: TyCtxt<'tcx>, body_cache: &mut BodyCache<'tcx>) {
+    let ref_gen_ty = body_cache.local_decls.raw[1].ty;
 
     let pin_did = tcx.lang_items().pin_type().unwrap();
     let pin_adt_ref = tcx.adt_def(pin_did);
@@ -410,18 +410,18 @@ fn make_generator_state_argument_pinned<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body
     let pin_ref_gen_ty = tcx.mk_adt(pin_adt_ref, substs);
 
     // Replace the by ref generator argument
-    body.local_decls.raw[1].ty = pin_ref_gen_ty;
+    body_cache.local_decls.raw[1].ty = pin_ref_gen_ty;
 
     // Add the Pin field access to accesses of the generator state
-    PinArgVisitor { ref_gen_ty, tcx }.visit_body(body);
+    PinArgVisitor { ref_gen_ty, tcx }.visit_body(body_cache);
 }
 
 fn replace_result_variable<'tcx>(
     ret_ty: Ty<'tcx>,
-    body: &mut Body<'tcx>,
+    body_cache: &mut BodyCache<'tcx>,
     tcx: TyCtxt<'tcx>,
 ) -> Local {
-    let source_info = source_info(body);
+    let source_info = source_info(body_cache);
     let new_ret = LocalDecl {
         mutability: Mutability::Mut,
         ty: ret_ty,
@@ -431,15 +431,15 @@ fn replace_result_variable<'tcx>(
         is_block_tail: None,
         local_info: LocalInfo::Other
     };
-    let new_ret_local = Local::new(body.local_decls.len());
-    body.local_decls.push(new_ret);
-    body.local_decls.swap(RETURN_PLACE, new_ret_local);
+    let new_ret_local = Local::new(body_cache.local_decls.len());
+    body_cache.local_decls.push(new_ret);
+    body_cache.local_decls.swap(RETURN_PLACE, new_ret_local);
 
     RenameLocalVisitor {
         from: RETURN_PLACE,
         to: new_ret_local,
         tcx,
-    }.visit_body(body);
+    }.visit_body(body_cache);
 
     new_ret_local
 }
@@ -481,12 +481,13 @@ struct LivenessInfo {
 
 fn locals_live_across_suspend_points(
     tcx: TyCtxt<'tcx>,
-    body: &Body<'tcx>,
+    body_cache: ReadOnlyBodyCache<'_, 'tcx>,
     source: MirSource<'tcx>,
     movable: bool,
 ) -> LivenessInfo {
-    let dead_unwinds = BitSet::new_empty(body.basic_blocks().len());
+    let dead_unwinds = BitSet::new_empty(body_cache.basic_blocks().len());
     let def_id = source.def_id();
+    let body = body_cache.body();
 
     // Calculate when MIR locals have live storage. This gives us an upper bound of their
     // lifetimes.
@@ -498,8 +499,8 @@ fn locals_live_across_suspend_points(
 
     // Find the MIR locals which do not use StorageLive/StorageDead statements.
     // The storage of these locals are always live.
-    let mut ignored = StorageIgnored(BitSet::new_filled(body.local_decls.len()));
-    ignored.visit_body(body);
+    let mut ignored = StorageIgnored(BitSet::new_filled(body_cache.local_decls.len()));
+    ignored.visit_body(body_cache);
 
     // Calculate the MIR locals which have been previously
     // borrowed (even if they are still active).
@@ -511,16 +512,16 @@ fn locals_live_across_suspend_points(
 
     // Calculate the MIR locals that we actually need to keep storage around
     // for.
-    let requires_storage_analysis = RequiresStorage::new(body, &borrowed_locals_results);
+    let requires_storage_analysis = RequiresStorage::new(body_cache, &borrowed_locals_results);
     let requires_storage_results =
         do_dataflow(tcx, body, def_id, &[], &dead_unwinds, requires_storage_analysis,
                     |bd, p| DebugFormatted::new(&bd.body().local_decls[p]));
     let mut requires_storage_cursor = DataflowResultsCursor::new(&requires_storage_results, body);
 
     // Calculate the liveness of MIR locals ignoring borrows.
-    let mut live_locals = liveness::LiveVarSet::new_empty(body.local_decls.len());
+    let mut live_locals = liveness::LiveVarSet::new_empty(body_cache.local_decls.len());
     let mut liveness = liveness::liveness_of_locals(
-        body,
+        body_cache,
     );
     liveness::dump_mir(
         tcx,
@@ -533,7 +534,7 @@ fn locals_live_across_suspend_points(
     let mut storage_liveness_map = FxHashMap::default();
     let mut live_locals_at_suspension_points = Vec::new();
 
-    for (block, data) in body.basic_blocks().iter_enumerated() {
+    for (block, data) in body_cache.basic_blocks().iter_enumerated() {
         if let TerminatorKind::Yield { .. } = data.terminator().kind {
             let loc = Location {
                 block: block,
@@ -749,7 +750,7 @@ fn compute_layout<'tcx>(
     upvars: &Vec<Ty<'tcx>>,
     interior: Ty<'tcx>,
     movable: bool,
-    body: &mut Body<'tcx>,
+    body_cache: &mut BodyCache<'tcx>,
 ) -> (
     FxHashMap<Local, (Ty<'tcx>, VariantIdx, usize)>,
     GeneratorLayout<'tcx>,
@@ -758,7 +759,7 @@ fn compute_layout<'tcx>(
     // Use a liveness analysis to compute locals which are live across a suspension point
     let LivenessInfo {
         live_locals, live_locals_at_suspension_points, storage_conflicts, storage_liveness
-    } = locals_live_across_suspend_points(tcx, body, source, movable);
+    } = locals_live_across_suspend_points(tcx, body_cache.read_only(), source, movable);
 
     // Erase regions from the types passed in from typeck so we can compare them with
     // MIR types
@@ -768,7 +769,7 @@ fn compute_layout<'tcx>(
         _ => bug!(),
     };
 
-    for (local, decl) in body.local_decls.iter_enumerated() {
+    for (local, decl) in body_cache.local_decls.iter_enumerated() {
         // Ignore locals which are internal or not live
         if !live_locals.contains(local) || decl.internal {
             continue;
@@ -777,7 +778,7 @@ fn compute_layout<'tcx>(
         // Sanity check that typeck knows about the type of locals which are
         // live across a suspension point
         if !allowed.contains(&decl.ty) && !allowed_upvars.contains(&decl.ty) {
-            span_bug!(body.span,
+            span_bug!(body_cache.span,
                       "Broken MIR: generator contains type {} in MIR, \
                        but typeck only knows about {}",
                       decl.ty,
@@ -790,7 +791,7 @@ fn compute_layout<'tcx>(
     let mut tys = IndexVec::<GeneratorSavedLocal, _>::new();
     for (idx, local) in live_locals.iter().enumerate() {
         locals.push(local);
-        tys.push(body.local_decls[local].ty);
+        tys.push(body_cache.local_decls[local].ty);
         debug!("generator saved local {:?} => {:?}", GeneratorSavedLocal::from(idx), local);
     }
 
@@ -828,13 +829,13 @@ fn compute_layout<'tcx>(
 }
 
 fn insert_switch<'tcx>(
-    body: &mut Body<'tcx>,
+    body_cache: &mut BodyCache<'tcx>,
     cases: Vec<(usize, BasicBlock)>,
     transform: &TransformVisitor<'tcx>,
     default: TerminatorKind<'tcx>,
 ) {
-    let default_block = insert_term_block(body, default);
-    let (assign, discr) = transform.get_discr(body);
+    let default_block = insert_term_block(body_cache, default);
+    let (assign, discr) = transform.get_discr(body_cache);
     let switch = TerminatorKind::SwitchInt {
         discr: Operand::Move(discr),
         switch_ty: transform.discr_ty,
@@ -842,8 +843,8 @@ fn insert_switch<'tcx>(
         targets: cases.iter().map(|&(_, d)| d).chain(iter::once(default_block)).collect(),
     };
 
-    let source_info = source_info(body);
-    body.basic_blocks_mut().raw.insert(0, BasicBlockData {
+    let source_info = source_info(body_cache);
+    body_cache.basic_blocks_mut().raw.insert(0, BasicBlockData {
         statements: vec![assign],
         terminator: Some(Terminator {
             source_info,
@@ -852,14 +853,14 @@ fn insert_switch<'tcx>(
         is_cleanup: false,
     });
 
-    let blocks = body.basic_blocks_mut().iter_mut();
+    let blocks = body_cache.basic_blocks_mut().iter_mut();
 
     for target in blocks.flat_map(|b| b.terminator_mut().successors_mut()) {
         *target = BasicBlock::new(target.index() + 1);
     }
 }
 
-fn elaborate_generator_drops<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, body: &mut Body<'tcx>) {
+fn elaborate_generator_drops<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, body_cache: &mut BodyCache<'tcx>) {
     use crate::util::elaborate_drops::{elaborate_drop, Unwind};
     use crate::util::patch::MirPatch;
     use crate::shim::DropShimElaborator;
@@ -872,13 +873,13 @@ fn elaborate_generator_drops<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, body: &mut
     let gen = self_arg();
 
     let mut elaborator = DropShimElaborator {
-        body: body,
-        patch: MirPatch::new(body),
+        body: body_cache,
+        patch: MirPatch::new(body_cache),
         tcx,
         param_env
     };
 
-    for (block, block_data) in body.basic_blocks().iter_enumerated() {
+    for (block, block_data) in body_cache.basic_blocks().iter_enumerated() {
         let (target, unwind, source_info) = match block_data.terminator() {
             Terminator {
                 source_info,
@@ -915,7 +916,7 @@ fn elaborate_generator_drops<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, body: &mut
             block,
         );
     }
-    elaborator.patch.apply(body);
+    elaborator.patch.apply(body_cache);
 }
 
 fn create_generator_drop_shim<'tcx>(
@@ -924,23 +925,23 @@ fn create_generator_drop_shim<'tcx>(
     def_id: DefId,
     source: MirSource<'tcx>,
     gen_ty: Ty<'tcx>,
-    body: &Body<'tcx>,
+    body_cache: &mut BodyCache<'tcx>,
     drop_clean: BasicBlock,
-) -> Body<'tcx> {
-    let mut body = body.clone();
+) -> BodyCache<'tcx> {
+    let mut body_cache = body_cache.clone();
 
-    let source_info = source_info(&body);
+    let source_info = source_info(body_cache.body());
 
-    let mut cases = create_cases(&mut body, transform, |point| point.drop);
+    let mut cases = create_cases(&mut body_cache, transform, |point| point.drop);
 
     cases.insert(0, (UNRESUMED, drop_clean));
 
     // The returned state and the poisoned state fall through to the default
     // case which is just to return
 
-    insert_switch(&mut body, cases, &transform, TerminatorKind::Return);
+    insert_switch(&mut body_cache, cases, &transform, TerminatorKind::Return);
 
-    for block in body.basic_blocks_mut() {
+    for block in body_cache.basic_blocks_mut() {
         let kind = &mut block.terminator_mut().kind;
         if let TerminatorKind::GeneratorDrop = *kind {
             *kind = TerminatorKind::Return;
@@ -948,7 +949,7 @@ fn create_generator_drop_shim<'tcx>(
     }
 
     // Replace the return variable
-    body.local_decls[RETURN_PLACE] = LocalDecl {
+    body_cache.local_decls[RETURN_PLACE] = LocalDecl {
         mutability: Mutability::Mut,
         ty: tcx.mk_unit(),
         user_ty: UserTypeProjections::none(),
@@ -958,10 +959,10 @@ fn create_generator_drop_shim<'tcx>(
         local_info: LocalInfo::Other
     };
 
-    make_generator_state_argument_indirect(tcx, def_id, &mut body);
+    make_generator_state_argument_indirect(tcx, def_id, &mut body_cache);
 
     // Change the generator argument from &mut to *mut
-    body.local_decls[self_arg()] = LocalDecl {
+    body_cache.local_decls[self_arg()] = LocalDecl {
         mutability: Mutability::Mut,
         ty: tcx.mk_ptr(ty::TypeAndMut {
             ty: gen_ty,
@@ -975,27 +976,27 @@ fn create_generator_drop_shim<'tcx>(
     };
     if tcx.sess.opts.debugging_opts.mir_emit_retag {
         // Alias tracking must know we changed the type
-        body.basic_blocks_mut()[START_BLOCK].statements.insert(0, Statement {
+        body_cache.basic_blocks_mut()[START_BLOCK].statements.insert(0, Statement {
             source_info,
             kind: StatementKind::Retag(RetagKind::Raw, box Place::from(self_arg())),
         })
     }
 
-    no_landing_pads(tcx, &mut body);
+    no_landing_pads(tcx, &mut body_cache);
 
     // Make sure we remove dead blocks to remove
     // unrelated code from the resume part of the function
-    simplify::remove_dead_blocks(&mut body);
+    simplify::remove_dead_blocks(&mut body_cache);
 
-    dump_mir(tcx, None, "generator_drop", &0, source, &mut body, |_, _| Ok(()) );
+    dump_mir(tcx, None, "generator_drop", &0, source, &mut body_cache, |_, _| Ok(()) );
 
-    body
+    body_cache
 }
 
-fn insert_term_block<'tcx>(body: &mut Body<'tcx>, kind: TerminatorKind<'tcx>) -> BasicBlock {
-    let term_block = BasicBlock::new(body.basic_blocks().len());
-    let source_info = source_info(body);
-    body.basic_blocks_mut().push(BasicBlockData {
+fn insert_term_block<'tcx>(body_cache: &mut BodyCache<'tcx>, kind: TerminatorKind<'tcx>) -> BasicBlock {
+    let term_block = BasicBlock::new(body_cache.basic_blocks().len());
+    let source_info = source_info(body_cache);
+    body_cache.basic_blocks_mut().push(BasicBlockData {
         statements: Vec::new(),
         terminator: Some(Terminator {
             source_info,
@@ -1008,13 +1009,13 @@ fn insert_term_block<'tcx>(body: &mut Body<'tcx>, kind: TerminatorKind<'tcx>) ->
 
 fn insert_panic_block<'tcx>(
     tcx: TyCtxt<'tcx>,
-    body: &mut Body<'tcx>,
+    body_cache: &mut BodyCache<'tcx>,
     message: AssertMessage<'tcx>,
 ) -> BasicBlock {
-    let assert_block = BasicBlock::new(body.basic_blocks().len());
+    let assert_block = BasicBlock::new(body_cache.basic_blocks().len());
     let term = TerminatorKind::Assert {
         cond: Operand::Constant(box Constant {
-            span: body.span,
+            span: body_cache.span,
             user_ty: None,
             literal: ty::Const::from_bool(tcx, false),
         }),
@@ -1024,8 +1025,8 @@ fn insert_panic_block<'tcx>(
         cleanup: None,
     };
 
-    let source_info = source_info(body);
-    body.basic_blocks_mut().push(BasicBlockData {
+    let source_info = source_info(body_cache);
+    body_cache.basic_blocks_mut().push(BasicBlockData {
         statements: Vec::new(),
         terminator: Some(Terminator {
             source_info,
@@ -1042,10 +1043,10 @@ fn create_generator_resume_function<'tcx>(
     transform: TransformVisitor<'tcx>,
     def_id: DefId,
     source: MirSource<'tcx>,
-    body: &mut Body<'tcx>,
+    body_cache: &mut BodyCache<'tcx>,
 ) {
     // Poison the generator when it unwinds
-    for block in body.basic_blocks_mut() {
+    for block in body_cache.basic_blocks_mut() {
         let source_info = block.terminator().source_info;
         if let &TerminatorKind::Resume = &block.terminator().kind {
             block.statements.push(
@@ -1053,7 +1054,7 @@ fn create_generator_resume_function<'tcx>(
         }
     }
 
-    let mut cases = create_cases(body, &transform, |point| Some(point.resume));
+    let mut cases = create_cases(body_cache, &transform, |point| Some(point.resume));
 
     use rustc::mir::interpret::PanicInfo::{
         ResumedAfterPanic,
@@ -1067,25 +1068,25 @@ fn create_generator_resume_function<'tcx>(
     let generator_kind = body.generator_kind.unwrap();
     cases.insert(1, (RETURNED, insert_panic_block(
         tcx,
-        body,
+        body_cache,
         ResumedAfterReturn(generator_kind))));
     cases.insert(2, (POISONED, insert_panic_block(
         tcx,
-        body,
+        body_cache,
         ResumedAfterPanic(generator_kind))));
 
-    insert_switch(body, cases, &transform, TerminatorKind::Unreachable);
+    insert_switch(body_cache, cases, &transform, TerminatorKind::Unreachable);
 
-    make_generator_state_argument_indirect(tcx, def_id, body);
-    make_generator_state_argument_pinned(tcx, body);
+    make_generator_state_argument_indirect(tcx, def_id, body_cache);
+    make_generator_state_argument_pinned(tcx, body_cache);
 
-    no_landing_pads(tcx, body);
+    no_landing_pads(tcx, body_cache);
 
     // Make sure we remove dead blocks to remove
     // unrelated code from the drop part of the function
-    simplify::remove_dead_blocks(body);
+    simplify::remove_dead_blocks(body_cache);
 
-    dump_mir(tcx, None, "generator_resume", &0, source, body, |_, _| Ok(()) );
+    dump_mir(tcx, None, "generator_resume", &0, source, body_cache, |_, _| Ok(()) );
 }
 
 fn source_info(body: &Body<'_>) -> SourceInfo {
@@ -1095,18 +1096,18 @@ fn source_info(body: &Body<'_>) -> SourceInfo {
     }
 }
 
-fn insert_clean_drop(body: &mut Body<'_>) -> BasicBlock {
-    let return_block = insert_term_block(body, TerminatorKind::Return);
+fn insert_clean_drop(body_cache: &mut BodyCache<'_>) -> BasicBlock {
+    let return_block = insert_term_block(body_cache, TerminatorKind::Return);
 
     // Create a block to destroy an unresumed generators. This can only destroy upvars.
-    let drop_clean = BasicBlock::new(body.basic_blocks().len());
+    let drop_clean = BasicBlock::new(body_cache.basic_blocks().len());
     let term = TerminatorKind::Drop {
         location: Place::from(self_arg()),
         target: return_block,
         unwind: None,
     };
-    let source_info = source_info(body);
-    body.basic_blocks_mut().push(BasicBlockData {
+    let source_info = source_info(body_cache);
+    body_cache.basic_blocks_mut().push(BasicBlockData {
         statements: Vec::new(),
         terminator: Some(Terminator {
             source_info,
@@ -1119,23 +1120,23 @@ fn insert_clean_drop(body: &mut Body<'_>) -> BasicBlock {
 }
 
 fn create_cases<'tcx, F>(
-    body: &mut Body<'tcx>,
+    body_cache: &mut BodyCache<'tcx>,
     transform: &TransformVisitor<'tcx>,
     target: F,
 ) -> Vec<(usize, BasicBlock)>
 where
     F: Fn(&SuspensionPoint) -> Option<BasicBlock>,
 {
-    let source_info = source_info(body);
+    let source_info = source_info(body_cache);
 
     transform.suspension_points.iter().filter_map(|point| {
         // Find the target for this suspension point, if applicable
         target(point).map(|target| {
-            let block = BasicBlock::new(body.basic_blocks().len());
+            let block = BasicBlock::new(body_cache.basic_blocks().len());
             let mut statements = Vec::new();
 
             // Create StorageLive instructions for locals with live storage
-            for i in 0..(body.local_decls.len()) {
+            for i in 0..(body_cache.local_decls.len()) {
                 let l = Local::new(i);
                 if point.storage_liveness.contains(l) && !transform.remap.contains_key(&l) {
                     statements.push(Statement {
@@ -1146,7 +1147,7 @@ where
             }
 
             // Then jump to the real target
-            body.basic_blocks_mut().push(BasicBlockData {
+            body_cache.basic_blocks_mut().push(BasicBlockData {
                 statements,
                 terminator: Some(Terminator {
                     source_info,
@@ -1163,20 +1164,20 @@ where
 }
 
 impl<'tcx> MirPass<'tcx> for StateTransform {
-    fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut Body<'tcx>) {
-        let yield_ty = if let Some(yield_ty) = body.yield_ty {
+    fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body_cache: &mut BodyCache<'tcx>) {
+        let yield_ty = if let Some(yield_ty) = body_cache.yield_ty {
             yield_ty
         } else {
             // This only applies to generators
             return
         };
 
-        assert!(body.generator_drop.is_none());
+        assert!(body_cache.generator_drop.is_none());
 
         let def_id = source.def_id();
 
         // The first argument is the generator type passed by value
-        let gen_ty = body.local_decls.raw[1].ty;
+        let gen_ty = body_cache.local_decls.raw[1].ty;
 
         // Get the interior types and substs which typeck computed
         let (upvars, interior, discr_ty, movable) = match gen_ty.kind {
@@ -1195,13 +1196,13 @@ impl<'tcx> MirPass<'tcx> for StateTransform {
         let state_adt_ref = tcx.adt_def(state_did);
         let state_substs = tcx.intern_substs(&[
             yield_ty.into(),
-            body.return_ty().into(),
+            body_cache.return_ty().into(),
         ]);
         let ret_ty = tcx.mk_adt(state_adt_ref, state_substs);
 
         // We rename RETURN_PLACE which has type mir.return_ty to new_ret_local
         // RETURN_PLACE then is a fresh unused local with type ret_ty.
-        let new_ret_local = replace_result_variable(ret_ty, body, tcx);
+        let new_ret_local = replace_result_variable(ret_ty, body_cache, tcx);
 
         // Extract locals which are live across suspension point into `layout`
         // `remap` gives a mapping from local indices onto generator struct indices
@@ -1212,7 +1213,7 @@ impl<'tcx> MirPass<'tcx> for StateTransform {
             &upvars,
             interior,
             movable,
-            body);
+            body_cache);
 
         // Run the transformation which converts Places from Local to generator struct
         // accesses for locals in `remap`.
@@ -1228,40 +1229,40 @@ impl<'tcx> MirPass<'tcx> for StateTransform {
             new_ret_local,
             discr_ty,
         };
-        transform.visit_body(body);
+        transform.visit_body(body_cache);
 
         // Update our MIR struct to reflect the changed we've made
-        body.yield_ty = None;
-        body.arg_count = 1;
-        body.spread_arg = None;
-        body.generator_layout = Some(layout);
+        body_cache.yield_ty = None;
+        body_cache.arg_count = 1;
+        body_cache.spread_arg = None;
+        body_cache.generator_layout = Some(layout);
 
         // Insert `drop(generator_struct)` which is used to drop upvars for generators in
         // the unresumed state.
         // This is expanded to a drop ladder in `elaborate_generator_drops`.
-        let drop_clean = insert_clean_drop(body);
+        let drop_clean = insert_clean_drop(body_cache);
 
-        dump_mir(tcx, None, "generator_pre-elab", &0, source, body, |_, _| Ok(()) );
+        dump_mir(tcx, None, "generator_pre-elab", &0, source, body_cache, |_, _| Ok(()) );
 
         // Expand `drop(generator_struct)` to a drop ladder which destroys upvars.
         // If any upvars are moved out of, drop elaboration will handle upvar destruction.
         // However we need to also elaborate the code generated by `insert_clean_drop`.
-        elaborate_generator_drops(tcx, def_id, body);
+        elaborate_generator_drops(tcx, def_id, body_cache);
 
-        dump_mir(tcx, None, "generator_post-transform", &0, source, body, |_, _| Ok(()) );
+        dump_mir(tcx, None, "generator_post-transform", &0, source, body_cache, |_, _| Ok(()) );
 
         // Create a copy of our MIR and use it to create the drop shim for the generator
         let drop_shim = create_generator_drop_shim(tcx,
-            &transform,
-            def_id,
-            source,
-            gen_ty,
-            &body,
-            drop_clean);
+                                                   &transform,
+                                                   def_id,
+                                                   source,
+                                                   gen_ty,
+                                                   body_cache,
+                                                   drop_clean);
 
-        body.generator_drop = Some(box drop_shim);
+        body_cache.generator_drop = Some(box drop_shim);
 
         // Create the Generator::resume function
-        create_generator_resume_function(tcx, transform, def_id, source, body);
+        create_generator_resume_function(tcx, transform, def_id, source, body_cache);
     }
 }
diff --git a/src/librustc_mir/transform/inline.rs b/src/librustc_mir/transform/inline.rs
index ebfadd0cfd3..c1c3d39bdc0 100644
--- a/src/librustc_mir/transform/inline.rs
+++ b/src/librustc_mir/transform/inline.rs
@@ -38,9 +38,9 @@ struct CallSite<'tcx> {
 }
 
 impl<'tcx> MirPass<'tcx> for Inline {
-    fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut Body<'tcx>) {
+    fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body_cache: &mut BodyCache<'tcx>) {
         if tcx.sess.opts.debugging_opts.mir_opt_level >= 2 {
-            Inliner { tcx, source }.run_pass(body);
+            Inliner { tcx, source }.run_pass(body_cache);
         }
     }
 }
@@ -51,7 +51,7 @@ struct Inliner<'tcx> {
 }
 
 impl Inliner<'tcx> {
-    fn run_pass(&self, caller_body: &mut Body<'tcx>) {
+    fn run_pass(&self, caller_body_cache: &mut BodyCache<'tcx>) {
         // Keep a queue of callsites to try inlining on. We take
         // advantage of the fact that queries detect cycles here to
         // allow us to try and fetch the fully optimized MIR of a
@@ -73,11 +73,11 @@ impl Inliner<'tcx> {
         if self.tcx.hir().body_owner_kind(id).is_fn_or_closure()
             && self.source.promoted.is_none()
         {
-            for (bb, bb_data) in caller_body.basic_blocks().iter_enumerated() {
+            for (bb, bb_data) in caller_body_cache.basic_blocks().iter_enumerated() {
                 if let Some(callsite) = self.get_valid_function_call(bb,
-                                                                    bb_data,
-                                                                    caller_body,
-                                                                    param_env) {
+                                                                     bb_data,
+                                                                     caller_body_cache,
+                                                                     param_env) {
                     callsites.push_back(callsite);
                 }
             }
@@ -127,19 +127,19 @@ impl Inliner<'tcx> {
                     continue;
                 };
 
-                let start = caller_body.basic_blocks().len();
+                let start = caller_body_cache.basic_blocks().len();
                 debug!("attempting to inline callsite {:?} - body={:?}", callsite, callee_body);
-                if !self.inline_call(callsite, caller_body, callee_body) {
+                if !self.inline_call(callsite, caller_body_cache, callee_body) {
                     debug!("attempting to inline callsite {:?} - failure", callsite);
                     continue;
                 }
                 debug!("attempting to inline callsite {:?} - success", callsite);
 
                 // Add callsites from inlined function
-                for (bb, bb_data) in caller_body.basic_blocks().iter_enumerated().skip(start) {
+                for (bb, bb_data) in caller_body_cache.basic_blocks().iter_enumerated().skip(start) {
                     if let Some(new_callsite) = self.get_valid_function_call(bb,
                                                                              bb_data,
-                                                                             caller_body,
+                                                                             caller_body_cache,
                                                                              param_env) {
                         // Don't inline the same function multiple times.
                         if callsite.callee != new_callsite.callee {
@@ -160,8 +160,8 @@ impl Inliner<'tcx> {
         // Simplify if we inlined anything.
         if changed {
             debug!("running simplify cfg on {:?}", self.source);
-            CfgSimplifier::new(caller_body).simplify();
-            remove_dead_blocks(caller_body);
+            CfgSimplifier::new(caller_body_cache).simplify();
+            remove_dead_blocks(caller_body_cache);
         }
     }
 
@@ -377,23 +377,23 @@ impl Inliner<'tcx> {
 
     fn inline_call(&self,
                    callsite: CallSite<'tcx>,
-                   caller_body: &mut Body<'tcx>,
-                   mut callee_body: Body<'tcx>) -> bool {
+                   caller_body: &mut BodyCache<'tcx>,
+                   mut callee_body_cache: BodyCache<'tcx>) -> bool {
         let terminator = caller_body[callsite.bb].terminator.take().unwrap();
         match terminator.kind {
             // FIXME: Handle inlining of diverging calls
             TerminatorKind::Call { args, destination: Some(destination), cleanup, .. } => {
                 debug!("inlined {:?} into {:?}", callsite.callee, self.source);
 
-                let mut local_map = IndexVec::with_capacity(callee_body.local_decls.len());
-                let mut scope_map = IndexVec::with_capacity(callee_body.source_scopes.len());
+                let mut local_map = IndexVec::with_capacity(callee_body_cache.local_decls.len());
+                let mut scope_map = IndexVec::with_capacity(callee_body_cache.source_scopes.len());
 
-                for mut scope in callee_body.source_scopes.iter().cloned() {
+                for mut scope in callee_body_cache.source_scopes.iter().cloned() {
                     if scope.parent_scope.is_none() {
                         scope.parent_scope = Some(callsite.location.scope);
                         // FIXME(eddyb) is this really needed?
                         // (also note that it's always overwritten below)
-                        scope.span = callee_body.span;
+                        scope.span = callee_body_cache.span;
                     }
 
                     // FIXME(eddyb) this doesn't seem right at all.
@@ -405,8 +405,8 @@ impl Inliner<'tcx> {
                     scope_map.push(idx);
                 }
 
-                for loc in callee_body.vars_and_temps_iter() {
-                    let mut local = callee_body.local_decls[loc].clone();
+                for loc in callee_body_cache.vars_and_temps_iter() {
+                    let mut local = callee_body_cache.local_decls[loc].clone();
 
                     local.source_info.scope =
                         scope_map[local.source_info.scope];
@@ -445,7 +445,7 @@ impl Inliner<'tcx> {
                         BorrowKind::Mut { allow_two_phase_borrow: false },
                         destination.0);
 
-                    let ty = dest.ty(caller_body, self.tcx);
+                    let ty = dest.ty(caller_body.body(), self.tcx);
 
                     let temp = LocalDecl::new_temp(ty, callsite.location.span);
 
@@ -486,7 +486,7 @@ impl Inliner<'tcx> {
                     caller_body.var_debug_info.push(var_debug_info);
                 }
 
-                for (bb, mut block) in callee_body.basic_blocks_mut().drain_enumerated(..) {
+                for (bb, mut block) in callee_body_cache.basic_blocks_mut().drain_enumerated(..) {
                     integrator.visit_basic_block_data(bb, &mut block);
                     caller_body.basic_blocks_mut().push(block);
                 }
diff --git a/src/librustc_mir/transform/instcombine.rs b/src/librustc_mir/transform/instcombine.rs
index a567ed668bf..e466b0cb77b 100644
--- a/src/librustc_mir/transform/instcombine.rs
+++ b/src/librustc_mir/transform/instcombine.rs
@@ -1,7 +1,8 @@
 //! Performs various peephole optimizations.
 
 use rustc::mir::{
-    Constant, Location, Place, PlaceBase, PlaceRef, Body, Operand, ProjectionElem, Rvalue, Local
+    Constant, Location, Place, PlaceBase, PlaceRef, Body, BodyCache, Operand, ProjectionElem,
+    Rvalue, Local
 };
 use rustc::mir::visit::{MutVisitor, Visitor};
 use rustc::ty::{self, TyCtxt};
@@ -13,7 +14,7 @@ use crate::transform::{MirPass, MirSource};
 pub struct InstCombine;
 
 impl<'tcx> MirPass<'tcx> for InstCombine {
-    fn run_pass(&self, tcx: TyCtxt<'tcx>, _: MirSource<'tcx>, body: &mut Body<'tcx>) {
+    fn run_pass(&self, tcx: TyCtxt<'tcx>, _: MirSource<'tcx>, body_cache: &mut BodyCache<'tcx>) {
         // We only run when optimizing MIR (at any level).
         if tcx.sess.opts.debugging_opts.mir_opt_level == 0 {
             return
@@ -23,13 +24,13 @@ impl<'tcx> MirPass<'tcx> for InstCombine {
         // read-only so that we can do global analyses on the MIR in the process (e.g.
         // `Place::ty()`).
         let optimizations = {
-            let mut optimization_finder = OptimizationFinder::new(body, tcx);
-            optimization_finder.visit_body(body);
+            let mut optimization_finder = OptimizationFinder::new(body_cache, tcx);
+            optimization_finder.visit_body(body_cache.read_only());
             optimization_finder.optimizations
         };
 
         // Then carry out those optimizations.
-        MutVisitor::visit_body(&mut InstCombineVisitor { optimizations, tcx }, body);
+        MutVisitor::visit_body(&mut InstCombineVisitor { optimizations, tcx }, body_cache);
     }
 }
 
diff --git a/src/librustc_mir/transform/mod.rs b/src/librustc_mir/transform/mod.rs
index 1eac40fc591..97c4efc08a3 100644
--- a/src/librustc_mir/transform/mod.rs
+++ b/src/librustc_mir/transform/mod.rs
@@ -1,7 +1,7 @@
 use crate::{build, shim};
 use rustc_index::vec::IndexVec;
 use rustc::hir::def_id::{CrateNum, DefId, LOCAL_CRATE};
-use rustc::mir::{Body, MirPhase, Promoted, ConstQualifs};
+use rustc::mir::{BodyCache, MirPhase, Promoted, ConstQualifs};
 use rustc::ty::{TyCtxt, InstanceDef, TypeFoldable};
 use rustc::ty::query::Providers;
 use rustc::ty::steal::Steal;
@@ -97,7 +97,7 @@ fn mir_keys(tcx: TyCtxt<'_>, krate: CrateNum) -> &DefIdSet {
     tcx.arena.alloc(set)
 }
 
-fn mir_built(tcx: TyCtxt<'_>, def_id: DefId) -> &Steal<Body<'_>> {
+fn mir_built(tcx: TyCtxt<'_>, def_id: DefId) -> &Steal<BodyCache<'_>> {
     let mir = build::mir_build(tcx, def_id);
     tcx.alloc_steal_mir(mir)
 }
@@ -144,12 +144,12 @@ pub trait MirPass<'tcx> {
         default_name::<Self>()
     }
 
-    fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut Body<'tcx>);
+    fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut BodyCache<'tcx>);
 }
 
 pub fn run_passes(
     tcx: TyCtxt<'tcx>,
-    body: &mut Body<'tcx>,
+    body: &mut BodyCache<'tcx>,
     instance: InstanceDef<'tcx>,
     promoted: Option<Promoted>,
     mir_phase: MirPhase,
@@ -220,47 +220,47 @@ fn mir_const_qualif(tcx: TyCtxt<'_>, def_id: DefId) -> ConstQualifs {
     validator.qualifs_in_return_place().into()
 }
 
-fn mir_const(tcx: TyCtxt<'_>, def_id: DefId) -> &Steal<Body<'_>> {
+fn mir_const(tcx: TyCtxt<'_>, def_id: DefId) -> &Steal<BodyCache<'_>> {
     // Unsafety check uses the raw mir, so make sure it is run
     let _ = tcx.unsafety_check_result(def_id);
 
-    let mut body = tcx.mir_built(def_id).steal();
-    run_passes(tcx, &mut body, InstanceDef::Item(def_id), None, MirPhase::Const, &[
+    let mut body_cache = tcx.mir_built(def_id).steal();
+    run_passes(tcx, &mut body_cache, InstanceDef::Item(def_id), None, MirPhase::Const, &[
         // What we need to do constant evaluation.
         &simplify::SimplifyCfg::new("initial"),
         &rustc_peek::SanityCheck,
         &uniform_array_move_out::UniformArrayMoveOut,
     ]);
-    tcx.alloc_steal_mir(body)
+    tcx.alloc_steal_mir(body_cache)
 }
 
 fn mir_validated(
     tcx: TyCtxt<'tcx>,
     def_id: DefId,
-) -> (&'tcx Steal<Body<'tcx>>, &'tcx Steal<IndexVec<Promoted, Body<'tcx>>>) {
+) -> (&'tcx Steal<BodyCache<'tcx>>, &'tcx Steal<IndexVec<Promoted, BodyCache<'tcx>>>) {
     // Ensure that we compute the `mir_const_qualif` for constants at
     // this point, before we steal the mir-const result.
     let _ = tcx.mir_const_qualif(def_id);
 
-    let mut body = tcx.mir_const(def_id).steal();
+    let mut body_cache = tcx.mir_const(def_id).steal();
     let promote_pass = promote_consts::PromoteTemps::default();
-    run_passes(tcx, &mut body, InstanceDef::Item(def_id), None, MirPhase::Validated, &[
+    run_passes(tcx, &mut body_cache, InstanceDef::Item(def_id), None, MirPhase::Validated, &[
         // What we need to run borrowck etc.
         &promote_pass,
         &simplify::SimplifyCfg::new("qualify-consts"),
     ]);
 
     let promoted = promote_pass.promoted_fragments.into_inner();
-    (tcx.alloc_steal_mir(body), tcx.alloc_steal_promoted(promoted))
+    (tcx.alloc_steal_mir(body_cache), tcx.alloc_steal_promoted(promoted))
 }
 
 fn run_optimization_passes<'tcx>(
     tcx: TyCtxt<'tcx>,
-    body: &mut Body<'tcx>,
+    body_cache: &mut BodyCache<'tcx>,
     def_id: DefId,
     promoted: Option<Promoted>,
 ) {
-    run_passes(tcx, body, InstanceDef::Item(def_id), promoted, MirPhase::Optimized, &[
+    run_passes(tcx, body_cache, InstanceDef::Item(def_id), promoted, MirPhase::Optimized, &[
         // Remove all things only needed by analysis
         &no_landing_pads::NoLandingPads::new(tcx),
         &simplify_branches::SimplifyBranches::new("initial"),
@@ -318,7 +318,7 @@ fn run_optimization_passes<'tcx>(
     ]);
 }
 
-fn optimized_mir(tcx: TyCtxt<'_>, def_id: DefId) -> &Body<'_> {
+fn optimized_mir(tcx: TyCtxt<'_>, def_id: DefId) -> &BodyCache<'_> {
     if tcx.is_constructor(def_id) {
         // There's no reason to run all of the MIR passes on constructors when
         // we can just output the MIR we want directly. This also saves const
@@ -332,12 +332,12 @@ fn optimized_mir(tcx: TyCtxt<'_>, def_id: DefId) -> &Body<'_> {
     tcx.ensure().mir_borrowck(def_id);
 
     let (body, _) = tcx.mir_validated(def_id);
-    let mut body = body.steal();
-    run_optimization_passes(tcx, &mut body, def_id, None);
-    tcx.arena.alloc(body)
+    let mut body_cache = body.steal();
+    run_optimization_passes(tcx, &mut body_cache, def_id, None);
+    tcx.arena.alloc(body_cache)
 }
 
-fn promoted_mir<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> &'tcx IndexVec<Promoted, Body<'tcx>> {
+fn promoted_mir<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> &'tcx IndexVec<Promoted, BodyCache<'tcx>> {
     if tcx.is_constructor(def_id) {
         return tcx.intern_promoted(IndexVec::new());
     }
@@ -346,8 +346,8 @@ fn promoted_mir<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> &'tcx IndexVec<Promot
     let (_, promoted) = tcx.mir_validated(def_id);
     let mut promoted = promoted.steal();
 
-    for (p, mut body) in promoted.iter_enumerated_mut() {
-        run_optimization_passes(tcx, &mut body, def_id, Some(p));
+    for (p, mut body_cache) in promoted.iter_enumerated_mut() {
+        run_optimization_passes(tcx, &mut body_cache, def_id, Some(p));
     }
 
     tcx.intern_promoted(promoted)
diff --git a/src/librustc_mir/transform/no_landing_pads.rs b/src/librustc_mir/transform/no_landing_pads.rs
index fbd14d9ef61..07dd4120528 100644
--- a/src/librustc_mir/transform/no_landing_pads.rs
+++ b/src/librustc_mir/transform/no_landing_pads.rs
@@ -17,14 +17,14 @@ impl<'tcx> NoLandingPads<'tcx> {
 }
 
 impl<'tcx> MirPass<'tcx> for NoLandingPads<'tcx> {
-    fn run_pass(&self, tcx: TyCtxt<'tcx>, _: MirSource<'tcx>, body: &mut Body<'tcx>) {
+    fn run_pass(&self, tcx: TyCtxt<'tcx>, _: MirSource<'tcx>, body_cache: &mut BodyCache<'tcx>) {
         no_landing_pads(tcx, body)
     }
 }
 
-pub fn no_landing_pads<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
+pub fn no_landing_pads<'tcx>(tcx: TyCtxt<'tcx>, body_cache: &mut BodyCache<'tcx>) {
     if tcx.sess.no_landing_pads() {
-        NoLandingPads::new(tcx).visit_body(body);
+        NoLandingPads::new(tcx).visit_body(body_cache);
     }
 }
 
diff --git a/src/librustc_mir/transform/promote_consts.rs b/src/librustc_mir/transform/promote_consts.rs
index 591f3ca4400..e409bb08429 100644
--- a/src/librustc_mir/transform/promote_consts.rs
+++ b/src/librustc_mir/transform/promote_consts.rs
@@ -770,8 +770,8 @@ pub fn validate_candidates(
 
 struct Promoter<'a, 'tcx> {
     tcx: TyCtxt<'tcx>,
-    source: &'a mut Body<'tcx>,
-    promoted: Body<'tcx>,
+    source_cache: &'a mut BodyCache<'tcx>,
+    promoted_cache: BodyCache<'tcx>,
     temps: &'a mut IndexVec<Local, TempState>,
 
     /// If true, all nested temps are also kept in the
@@ -781,8 +781,8 @@ struct Promoter<'a, 'tcx> {
 
 impl<'a, 'tcx> Promoter<'a, 'tcx> {
     fn new_block(&mut self) -> BasicBlock {
-        let span = self.promoted.span;
-        self.promoted.basic_blocks_mut().push(BasicBlockData {
+        let span = self.promoted_cache.span;
+        self.promoted_cache.basic_blocks_mut().push(BasicBlockData {
             statements: vec![],
             terminator: Some(Terminator {
                 source_info: SourceInfo {
@@ -796,8 +796,8 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> {
     }
 
     fn assign(&mut self, dest: Local, rvalue: Rvalue<'tcx>, span: Span) {
-        let last = self.promoted.basic_blocks().last().unwrap();
-        let data = &mut self.promoted[last];
+        let last = self.promoted_cache.basic_blocks().last().unwrap();
+        let data = &mut self.promoted_cache[last];
         data.statements.push(Statement {
             source_info: SourceInfo {
                 span,
@@ -808,7 +808,7 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> {
     }
 
     fn is_temp_kind(&self, local: Local) -> bool {
-        self.source.local_kind(local) == LocalKind::Temp
+        self.source_cache.local_kind(local) == LocalKind::Temp
     }
 
     /// Copies the initialization of this temp to the
@@ -823,7 +823,7 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> {
                 location
             }
             state =>  {
-                span_bug!(self.promoted.span, "{:?} not promotable: {:?}",
+                span_bug!(self.promoted_cache.span, "{:?} not promotable: {:?}",
                           temp, state);
             }
         };
@@ -831,10 +831,10 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> {
             self.temps[temp] = TempState::PromotedOut;
         }
 
-        let num_stmts = self.source[loc.block].statements.len();
-        let new_temp = self.promoted.local_decls.push(
-            LocalDecl::new_temp(self.source.local_decls[temp].ty,
-                                self.source.local_decls[temp].source_info.span));
+        let num_stmts = self.source_cacje[loc.block].statements.len();
+        let new_temp = self.promoted_cache.local_decls.push(
+            LocalDecl::new_temp(self.source_cache.local_decls[temp].ty,
+                                self.source_cache.local_decls[temp].source_info.span));
 
         debug!("promote({:?} @ {:?}/{:?}, {:?})",
                temp, loc, num_stmts, self.keep_original);
@@ -843,7 +843,7 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> {
         // or duplicate it, depending on keep_original.
         if loc.statement_index < num_stmts {
             let (mut rvalue, source_info) = {
-                let statement = &mut self.source[loc.block].statements[loc.statement_index];
+                let statement = &mut self.source_cache[loc.block].statements[loc.statement_index];
                 let rhs = match statement.kind {
                     StatementKind::Assign(box(_, ref mut rhs)) => rhs,
                     _ => {
@@ -864,9 +864,9 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> {
             self.assign(new_temp, rvalue, source_info.span);
         } else {
             let terminator = if self.keep_original {
-                self.source[loc.block].terminator().clone()
+                self.source_cache[loc.block].terminator().clone()
             } else {
-                let terminator = self.source[loc.block].terminator_mut();
+                let terminator = self.source_cache[loc.block].terminator_mut();
                 let target = match terminator.kind {
                     TerminatorKind::Call { destination: Some((_, target)), .. } => target,
                     ref kind => {
@@ -888,10 +888,10 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> {
                         self.visit_operand(arg, loc);
                     }
 
-                    let last = self.promoted.basic_blocks().last().unwrap();
+                    let last = self.promoted_cache.basic_blocks().last().unwrap();
                     let new_target = self.new_block();
 
-                    *self.promoted[last].terminator_mut() = Terminator {
+                    *self.promoted_cache[last].terminator_mut() = Terminator {
                         kind: TerminatorKind::Call {
                             func,
                             args,
@@ -919,9 +919,9 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> {
         def_id: DefId,
         candidate: Candidate,
         next_promoted_id: usize,
-    ) -> Option<Body<'tcx>> {
+    ) -> Option<BodyCache<'tcx>> {
         let mut operand = {
-            let promoted = &mut self.promoted;
+            let promoted = &mut self.promoted_cache;
             let promoted_id = Promoted::new(next_promoted_id);
             let tcx = self.tcx;
             let mut promoted_place = |ty, span| {
@@ -940,7 +940,7 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> {
                     projection: List::empty(),
                 }
             };
-            let (blocks, local_decls) = self.source.basic_blocks_and_local_decls_mut();
+            let (blocks, local_decls) = self.source_cache.basic_blocks_and_local_decls_mut();
             match candidate {
                 Candidate::Ref(loc) => {
                     let ref mut statement = blocks[loc.block].statements[loc.statement_index];
@@ -1004,9 +1004,9 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> {
             statement_index: usize::MAX
         });
 
-        let span = self.promoted.span;
+        let span = self.promoted_cache.span;
         self.assign(RETURN_PLACE, Rvalue::Use(operand), span);
-        Some(self.promoted)
+        Some(self.promoted_cache)
     }
 }
 
@@ -1040,11 +1040,11 @@ impl<'a, 'tcx> MutVisitor<'tcx> for Promoter<'a, 'tcx> {
 
 pub fn promote_candidates<'tcx>(
     def_id: DefId,
-    body: &mut Body<'tcx>,
+    body_cache: &mut BodyCache<'tcx>,
     tcx: TyCtxt<'tcx>,
     mut temps: IndexVec<Local, TempState>,
     candidates: Vec<Candidate>,
-) -> IndexVec<Promoted, Body<'tcx>> {
+) -> IndexVec<Promoted, BodyCache<'tcx>> {
     // Visit candidates in reverse, in case they're nested.
     debug!("promote_candidates({:?})", candidates);
 
@@ -1054,7 +1054,7 @@ pub fn promote_candidates<'tcx>(
         match candidate {
             Candidate::Repeat(Location { block, statement_index }) |
             Candidate::Ref(Location { block, statement_index }) => {
-                match &body[block].statements[statement_index].kind {
+                match &body_cache[block].statements[statement_index].kind {
                     StatementKind::Assign(box(place, _)) => {
                         if let Some(local) = place.as_local() {
                             if temps[local] == TempState::PromotedOut {
@@ -1072,25 +1072,26 @@ pub fn promote_candidates<'tcx>(
 
         // Declare return place local so that `mir::Body::new` doesn't complain.
         let initial_locals = iter::once(
-            LocalDecl::new_return_place(tcx.types.never, body.span)
+            LocalDecl::new_return_place(tcx.types.never, body_cache.span)
         ).collect();
 
         let promoter = Promoter {
-            promoted: Body::new(
+            promoted_cache: BodyCache::new(Body::new(
                 IndexVec::new(),
                 // FIXME: maybe try to filter this to avoid blowing up
                 // memory usage?
-                body.source_scopes.clone(),
+                body_cache.source_scopes.clone(),
+                body_cache.source_scope_local_data.clone(),
                 initial_locals,
                 IndexVec::new(),
                 0,
                 vec![],
-                body.span,
+                body_cache.span,
                 vec![],
-                body.generator_kind,
-            ),
+                body_cache.generator_kind,
+            )),
             tcx,
-            source: body,
+            source_cache: body_cache,
             temps: &mut temps,
             keep_original: false
         };
@@ -1103,7 +1104,7 @@ pub fn promote_candidates<'tcx>(
 
     // Eliminate assignments to, and drops of promoted temps.
     let promoted = |index: Local| temps[index] == TempState::PromotedOut;
-    for block in body.basic_blocks_mut() {
+    for block in body_cache.basic_blocks_mut() {
         block.statements.retain(|statement| {
             match &statement.kind {
                 StatementKind::Assign(box(place, _)) => {
diff --git a/src/librustc_mir/transform/remove_noop_landing_pads.rs b/src/librustc_mir/transform/remove_noop_landing_pads.rs
index 130393e2c4c..3a7d7d17f06 100644
--- a/src/librustc_mir/transform/remove_noop_landing_pads.rs
+++ b/src/librustc_mir/transform/remove_noop_landing_pads.rs
@@ -9,18 +9,18 @@ use crate::util::patch::MirPatch;
 /// code for these.
 pub struct RemoveNoopLandingPads;
 
-pub fn remove_noop_landing_pads<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
+pub fn remove_noop_landing_pads<'tcx>(tcx: TyCtxt<'tcx>, body_cache: &mut BodyCache<'tcx>) {
     if tcx.sess.no_landing_pads() {
         return
     }
-    debug!("remove_noop_landing_pads({:?})", body);
+    debug!("remove_noop_landing_pads({:?})", body_cache.body());
 
-    RemoveNoopLandingPads.remove_nop_landing_pads(body)
+    RemoveNoopLandingPads.remove_nop_landing_pads(body_cache)
 }
 
 impl<'tcx> MirPass<'tcx> for RemoveNoopLandingPads {
-    fn run_pass(&self, tcx: TyCtxt<'tcx>, _src: MirSource<'tcx>, body: &mut Body<'tcx>) {
-        remove_noop_landing_pads(tcx, body);
+    fn run_pass(&self, tcx: TyCtxt<'tcx>, _src: MirSource<'tcx>, body_cache: &mut BodyCache<'tcx>) {
+        remove_noop_landing_pads(tcx, body_cache);
     }
 }
 
@@ -84,26 +84,26 @@ impl RemoveNoopLandingPads {
         }
     }
 
-    fn remove_nop_landing_pads(&self, body: &mut Body<'_>) {
+    fn remove_nop_landing_pads(&self, body_cache: &mut BodyCache<'_>) {
         // make sure there's a single resume block
         let resume_block = {
-            let patch = MirPatch::new(body);
+            let patch = MirPatch::new(body_cache);
             let resume_block = patch.resume_block();
-            patch.apply(body);
+            patch.apply(body_cache);
             resume_block
         };
         debug!("remove_noop_landing_pads: resume block is {:?}", resume_block);
 
         let mut jumps_folded = 0;
         let mut landing_pads_removed = 0;
-        let mut nop_landing_pads = BitSet::new_empty(body.basic_blocks().len());
+        let mut nop_landing_pads = BitSet::new_empty(body_cache.basic_blocks().len());
 
         // This is a post-order traversal, so that if A post-dominates B
         // then A will be visited before B.
-        let postorder: Vec<_> = traversal::postorder(body).map(|(bb, _)| bb).collect();
+        let postorder: Vec<_> = traversal::postorder(body_cache).map(|(bb, _)| bb).collect();
         for bb in postorder {
             debug!("  processing {:?}", bb);
-            for target in body[bb].terminator_mut().successors_mut() {
+            for target in body_cache[bb].terminator_mut().successors_mut() {
                 if *target != resume_block && nop_landing_pads.contains(*target) {
                     debug!("    folding noop jump to {:?} to resume block", target);
                     *target = resume_block;
@@ -111,7 +111,7 @@ impl RemoveNoopLandingPads {
                 }
             }
 
-            match body[bb].terminator_mut().unwind_mut() {
+            match body_cache[bb].terminator_mut().unwind_mut() {
                 Some(unwind) => {
                     if *unwind == Some(resume_block) {
                         debug!("    removing noop landing pad");
@@ -123,7 +123,7 @@ impl RemoveNoopLandingPads {
                 _ => {}
             }
 
-            let is_nop_landing_pad = self.is_nop_landing_pad(bb, body, &nop_landing_pads);
+            let is_nop_landing_pad = self.is_nop_landing_pad(bb, body_cache, &nop_landing_pads);
             if is_nop_landing_pad {
                 nop_landing_pads.insert(bb);
             }
diff --git a/src/librustc_mir/transform/rustc_peek.rs b/src/librustc_mir/transform/rustc_peek.rs
index aada7641df6..87fcef69238 100644
--- a/src/librustc_mir/transform/rustc_peek.rs
+++ b/src/librustc_mir/transform/rustc_peek.rs
@@ -5,7 +5,7 @@ use syntax_pos::Span;
 
 use rustc::ty::{self, TyCtxt, Ty};
 use rustc::hir::def_id::DefId;
-use rustc::mir::{self, Body, Location, Local};
+use rustc::mir::{self, Body, BodyCache, Location, Local};
 use rustc_index::bit_set::BitSet;
 use crate::transform::{MirPass, MirSource};
 
@@ -26,7 +26,7 @@ use crate::dataflow::has_rustc_mir_with;
 pub struct SanityCheck;
 
 impl<'tcx> MirPass<'tcx> for SanityCheck {
-    fn run_pass(&self, tcx: TyCtxt<'tcx>, src: MirSource<'tcx>, body: &mut Body<'tcx>) {
+    fn run_pass(&self, tcx: TyCtxt<'tcx>, src: MirSource<'tcx>, body_cache: &mut BodyCache<'tcx>) {
         let def_id = src.def_id();
         if !tcx.has_attr(def_id, sym::rustc_mir) {
             debug!("skipping rustc_peek::SanityCheck on {}", tcx.def_path_str(def_id));
@@ -37,37 +37,37 @@ impl<'tcx> MirPass<'tcx> for SanityCheck {
 
         let attributes = tcx.get_attrs(def_id);
         let param_env = tcx.param_env(def_id);
-        let move_data = MoveData::gather_moves(body, tcx).unwrap();
+        let move_data = MoveData::gather_moves(body_cache, tcx).unwrap();
         let mdpe = MoveDataParamEnv { move_data: move_data, param_env: param_env };
-        let dead_unwinds = BitSet::new_empty(body.basic_blocks().len());
+        let dead_unwinds = BitSet::new_empty(body_cache.basic_blocks().len());
         let flow_inits =
-            do_dataflow(tcx, body, def_id, &attributes, &dead_unwinds,
-                        MaybeInitializedPlaces::new(tcx, body, &mdpe),
+            do_dataflow(tcx, body_cache, def_id, &attributes, &dead_unwinds,
+                        MaybeInitializedPlaces::new(tcx, body_cache, &mdpe),
                         |bd, i| DebugFormatted::new(&bd.move_data().move_paths[i]));
         let flow_uninits =
-            do_dataflow(tcx, body, def_id, &attributes, &dead_unwinds,
-                        MaybeUninitializedPlaces::new(tcx, body, &mdpe),
+            do_dataflow(tcx, body_cache, def_id, &attributes, &dead_unwinds,
+                        MaybeUninitializedPlaces::new(tcx, body_cache, &mdpe),
                         |bd, i| DebugFormatted::new(&bd.move_data().move_paths[i]));
         let flow_def_inits =
-            do_dataflow(tcx, body, def_id, &attributes, &dead_unwinds,
-                        DefinitelyInitializedPlaces::new(tcx, body, &mdpe),
+            do_dataflow(tcx, body_cache, def_id, &attributes, &dead_unwinds,
+                        DefinitelyInitializedPlaces::new(tcx, body_cache, &mdpe),
                         |bd, i| DebugFormatted::new(&bd.move_data().move_paths[i]));
         let flow_indirectly_mut =
-            do_dataflow(tcx, body, def_id, &attributes, &dead_unwinds,
-                        IndirectlyMutableLocals::new(tcx, body, param_env),
+            do_dataflow(tcx, body_cache, def_id, &attributes, &dead_unwinds,
+                        IndirectlyMutableLocals::new(tcx, body_cache, param_env),
                         |_, i| DebugFormatted::new(&i));
 
         if has_rustc_mir_with(&attributes, sym::rustc_peek_maybe_init).is_some() {
-            sanity_check_via_rustc_peek(tcx, body, def_id, &attributes, &flow_inits);
+            sanity_check_via_rustc_peek(tcx, body_cache.body(), def_id, &attributes, &flow_inits);
         }
         if has_rustc_mir_with(&attributes, sym::rustc_peek_maybe_uninit).is_some() {
-            sanity_check_via_rustc_peek(tcx, body, def_id, &attributes, &flow_uninits);
+            sanity_check_via_rustc_peek(tcx, body_cache.body(), def_id, &attributes, &flow_uninits);
         }
         if has_rustc_mir_with(&attributes, sym::rustc_peek_definite_init).is_some() {
-            sanity_check_via_rustc_peek(tcx, body, def_id, &attributes, &flow_def_inits);
+            sanity_check_via_rustc_peek(tcx, body_cache.body(), def_id, &attributes, &flow_def_inits);
         }
         if has_rustc_mir_with(&attributes, sym::rustc_peek_indirectly_mutable).is_some() {
-            sanity_check_via_rustc_peek(tcx, body, def_id, &attributes, &flow_indirectly_mut);
+            sanity_check_via_rustc_peek(tcx, body_cache.body(), def_id, &attributes, &flow_indirectly_mut);
         }
         if has_rustc_mir_with(&attributes, sym::stop_after_dataflow).is_some() {
             tcx.sess.fatal("stop_after_dataflow ended compilation");
diff --git a/src/librustc_mir/transform/simplify.rs b/src/librustc_mir/transform/simplify.rs
index 040bc5c309a..75016cf1746 100644
--- a/src/librustc_mir/transform/simplify.rs
+++ b/src/librustc_mir/transform/simplify.rs
@@ -43,12 +43,12 @@ impl SimplifyCfg {
     }
 }
 
-pub fn simplify_cfg(body: &mut Body<'_>) {
-    CfgSimplifier::new(body).simplify();
-    remove_dead_blocks(body);
+pub fn simplify_cfg(body_cache: &mut BodyCache<'_>) {
+    CfgSimplifier::new(body_cache).simplify();
+    remove_dead_blocks(body_cache);
 
     // FIXME: Should probably be moved into some kind of pass manager
-    body.basic_blocks_mut().raw.shrink_to_fit();
+    body_cache.basic_blocks_mut().raw.shrink_to_fit();
 }
 
 impl<'tcx> MirPass<'tcx> for SimplifyCfg {
@@ -56,9 +56,9 @@ impl<'tcx> MirPass<'tcx> for SimplifyCfg {
         Cow::Borrowed(&self.label)
     }
 
-    fn run_pass(&self, _tcx: TyCtxt<'tcx>, _src: MirSource<'tcx>, body: &mut Body<'tcx>) {
-        debug!("SimplifyCfg({:?}) - simplifying {:?}", self.label, body);
-        simplify_cfg(body);
+    fn run_pass(&self, _tcx: TyCtxt<'tcx>, _src: MirSource<'tcx>, body_cache: &mut BodyCache<'tcx>) {
+        debug!("SimplifyCfg({:?}) - simplifying {:?}", self.label, body_cache.body());
+        simplify_cfg(body_cache);
     }
 }
 
@@ -68,14 +68,14 @@ pub struct CfgSimplifier<'a, 'tcx> {
 }
 
 impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> {
-    pub fn new(body: &'a mut Body<'tcx>) -> Self {
-        let mut pred_count = IndexVec::from_elem(0u32, body.basic_blocks());
+    pub fn new(body_cache: &'a mut BodyCache<'tcx>) -> Self {
+        let mut pred_count = IndexVec::from_elem(0u32, body_cache.basic_blocks());
 
         // we can't use mir.predecessors() here because that counts
         // dead blocks, which we don't want to.
         pred_count[START_BLOCK] = 1;
 
-        for (_, data) in traversal::preorder(body) {
+        for (_, data) in traversal::preorder(body_cache) {
             if let Some(ref term) = data.terminator {
                 for &tgt in term.successors() {
                     pred_count[tgt] += 1;
@@ -83,7 +83,7 @@ impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> {
             }
         }
 
-        let basic_blocks = body.basic_blocks_mut();
+        let basic_blocks = body_cache.basic_blocks_mut();
 
         CfgSimplifier {
             basic_blocks,
@@ -260,13 +260,13 @@ impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> {
     }
 }
 
-pub fn remove_dead_blocks(body: &mut Body<'_>) {
-    let mut seen = BitSet::new_empty(body.basic_blocks().len());
-    for (bb, _) in traversal::preorder(body) {
+pub fn remove_dead_blocks(body_cache: &mut BodyCache<'_>) {
+    let mut seen = BitSet::new_empty(body_cache.basic_blocks().len());
+    for (bb, _) in traversal::preorder(body_cache.body()) {
         seen.insert(bb.index());
     }
 
-    let basic_blocks = body.basic_blocks_mut();
+    let basic_blocks = body_cache.basic_blocks_mut();
 
     let num_blocks = basic_blocks.len();
     let mut replacements : Vec<_> = (0..num_blocks).map(BasicBlock::new).collect();
@@ -293,27 +293,27 @@ pub fn remove_dead_blocks(body: &mut Body<'_>) {
 pub struct SimplifyLocals;
 
 impl<'tcx> MirPass<'tcx> for SimplifyLocals {
-    fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body: &mut Body<'tcx>) {
+    fn run_pass(&self, tcx: TyCtxt<'tcx>, source: MirSource<'tcx>, body_cache: &mut BodyCache<'tcx>) {
         trace!("running SimplifyLocals on {:?}", source);
         let locals = {
             let mut marker = DeclMarker {
-                locals: BitSet::new_empty(body.local_decls.len()),
-                body,
+                locals: BitSet::new_empty(body_cache.local_decls.len()),
+                body: body_cache,
             };
-            marker.visit_body(body);
+            marker.visit_body(body_cache.read_only());
             // Return pointer and arguments are always live
             marker.locals.insert(RETURN_PLACE);
-            for arg in body.args_iter() {
+            for arg in body_cache.args_iter() {
                 marker.locals.insert(arg);
             }
 
             marker.locals
         };
 
-        let map = make_local_map(&mut body.local_decls, locals);
+        let map = make_local_map(&mut body_cache.local_decls, locals);
         // Update references to all vars and tmps now
-        LocalUpdater { map, tcx }.visit_body(body);
-        body.local_decls.shrink_to_fit();
+        LocalUpdater { map, tcx }.visit_body(body_cache);
+        body_cache.local_decls.shrink_to_fit();
     }
 }
 
diff --git a/src/librustc_mir/transform/simplify_branches.rs b/src/librustc_mir/transform/simplify_branches.rs
index 0a509666d34..df5d40484bf 100644
--- a/src/librustc_mir/transform/simplify_branches.rs
+++ b/src/librustc_mir/transform/simplify_branches.rs
@@ -19,9 +19,9 @@ impl<'tcx> MirPass<'tcx> for SimplifyBranches {
         Cow::Borrowed(&self.label)
     }
 
-    fn run_pass(&self, tcx: TyCtxt<'tcx>, src: MirSource<'tcx>, body: &mut Body<'tcx>) {
+    fn run_pass(&self, tcx: TyCtxt<'tcx>, src: MirSource<'tcx>, body_cache: &mut BodyCache<'tcx>) {
         let param_env = tcx.param_env(src.def_id());
-        for block in body.basic_blocks_mut() {
+        for block in body_cache.basic_blocks_mut() {
             let terminator = block.terminator_mut();
             terminator.kind = match terminator.kind {
                 TerminatorKind::SwitchInt {
diff --git a/src/librustc_mir/transform/uniform_array_move_out.rs b/src/librustc_mir/transform/uniform_array_move_out.rs
index 28f68ac2de0..d3fbfaf384a 100644
--- a/src/librustc_mir/transform/uniform_array_move_out.rs
+++ b/src/librustc_mir/transform/uniform_array_move_out.rs
@@ -37,14 +37,14 @@ use crate::util::patch::MirPatch;
 pub struct UniformArrayMoveOut;
 
 impl<'tcx> MirPass<'tcx> for UniformArrayMoveOut {
-    fn run_pass(&self, tcx: TyCtxt<'tcx>, src: MirSource<'tcx>, body: &mut Body<'tcx>) {
-        let mut patch = MirPatch::new(body);
+    fn run_pass(&self, tcx: TyCtxt<'tcx>, src: MirSource<'tcx>, body_cache: &mut BodyCache<'tcx>) {
+        let mut patch = MirPatch::new(body_cache);
         let param_env = tcx.param_env(src.def_id());
         {
-            let mut visitor = UniformArrayMoveOutVisitor{body, patch: &mut patch, tcx, param_env};
-            visitor.visit_body(body);
+            let mut visitor = UniformArrayMoveOutVisitor{ body: body_cache, patch: &mut patch, tcx, param_env};
+            visitor.visit_body(body_cache.read_only());
         }
-        patch.apply(body);
+        patch.apply(body_cache);
     }
 }
 
@@ -184,18 +184,18 @@ pub struct RestoreSubsliceArrayMoveOut<'tcx> {
 }
 
 impl<'tcx> MirPass<'tcx> for RestoreSubsliceArrayMoveOut<'tcx> {
-    fn run_pass(&self, tcx: TyCtxt<'tcx>, src: MirSource<'tcx>, body: &mut Body<'tcx>) {
-        let mut patch = MirPatch::new(body);
+    fn run_pass(&self, tcx: TyCtxt<'tcx>, src: MirSource<'tcx>, body_cache: &mut BodyCache<'tcx>) {
+        let mut patch = MirPatch::new(body_cache);
         let param_env = tcx.param_env(src.def_id());
         {
             let mut visitor = RestoreDataCollector {
-                locals_use: IndexVec::from_elem(LocalUse::new(), &body.local_decls),
+                locals_use: IndexVec::from_elem(LocalUse::new(), &body_cache.local_decls),
                 candidates: vec![],
             };
-            visitor.visit_body(body);
+            visitor.visit_body(body_cache.read_only());
 
             for candidate in &visitor.candidates {
-                let statement = &body[candidate.block].statements[candidate.statement_index];
+                let statement = &body_cache[candidate.block].statements[candidate.statement_index];
                 if let StatementKind::Assign(box(ref dst_place, ref rval)) = statement.kind {
                     if let Rvalue::Aggregate(box AggregateKind::Array(_), ref items) = *rval {
                         let items : Vec<_> = items.iter().map(|item| {
@@ -203,7 +203,7 @@ impl<'tcx> MirPass<'tcx> for RestoreSubsliceArrayMoveOut<'tcx> {
                                 if let Some(local) = place.as_local() {
                                     let local_use = &visitor.locals_use[local];
                                     let opt_index_and_place =
-                                        Self::try_get_item_source(local_use, body);
+                                        Self::try_get_item_source(local_use, body_cache);
                                     // each local should be used twice:
                                     //  in assign and in aggregate statements
                                     if local_use.use_count == 2 && opt_index_and_place.is_some() {
@@ -218,7 +218,7 @@ impl<'tcx> MirPass<'tcx> for RestoreSubsliceArrayMoveOut<'tcx> {
                         let opt_src_place = items.first().and_then(|x| *x).map(|x| x.2);
                         let opt_size = opt_src_place.and_then(|src_place| {
                             let src_ty =
-                                Place::ty_from(src_place.base, src_place.projection, body, tcx).ty;
+                                Place::ty_from(src_place.base, src_place.projection, body_cache.body(), tcx).ty;
                             if let ty::Array(_, ref size_o) = src_ty.kind {
                                 size_o.try_eval_usize(tcx, param_env)
                             } else {
@@ -232,7 +232,7 @@ impl<'tcx> MirPass<'tcx> for RestoreSubsliceArrayMoveOut<'tcx> {
                 }
             }
         }
-        patch.apply(body);
+        patch.apply(body_cache);
     }
 }
 
diff --git a/src/librustc_mir/util/collect_writes.rs b/src/librustc_mir/util/collect_writes.rs
index 0092c3c86d7..f94bea20024 100644
--- a/src/librustc_mir/util/collect_writes.rs
+++ b/src/librustc_mir/util/collect_writes.rs
@@ -12,7 +12,7 @@ crate trait FindAssignments {
 impl<'a, 'tcx> FindAssignments for ReadOnlyBodyCache<'a, 'tcx>{
     fn find_assignments(&self, local: Local) -> Vec<Location>{
             let mut visitor = FindLocalAssignmentVisitor{ needle: local, locations: vec![]};
-            visitor.visit_body(self);
+            visitor.visit_body(*self);
             visitor.locations
     }
 }
diff --git a/src/librustc_mir/util/def_use.rs b/src/librustc_mir/util/def_use.rs
index 11b61bcd484..256ba94af44 100644
--- a/src/librustc_mir/util/def_use.rs
+++ b/src/librustc_mir/util/def_use.rs
@@ -30,7 +30,7 @@ impl DefUseAnalysis {
         }
     }
 
-    pub fn analyze(&mut self, body_cache: &ReadOnlyBodyCache<'_, '_>) {
+    pub fn analyze(&mut self, body_cache: ReadOnlyBodyCache<'_, '_>) {
         self.clear();
 
         let mut finder = DefUseFinder {
@@ -55,7 +55,7 @@ impl DefUseAnalysis {
     fn mutate_defs_and_uses(
         &self,
         local: Local,
-        body_cache: &mut BodyCache<&mut Body<'tcx>>,
+        body_cache: &mut BodyCache<'tcx>,
         new_local: Local,
         tcx: TyCtxt<'tcx>,
     ) {
@@ -73,7 +73,7 @@ impl DefUseAnalysis {
     // FIXME(pcwalton): this should update the def-use chains.
     pub fn replace_all_defs_and_uses_with(&self,
                                           local: Local,
-                                          body_cache: &mut BodyCache<&mut Body<'tcx>>,
+                                          body_cache: &mut BodyCache<'tcx>,
                                           new_local: Local,
                                           tcx: TyCtxt<'tcx>) {
         self.mutate_defs_and_uses(local, body_cache, new_local, tcx)
diff --git a/src/librustc_mir/util/liveness.rs b/src/librustc_mir/util/liveness.rs
index 87e6291a454..37611561d58 100644
--- a/src/librustc_mir/util/liveness.rs
+++ b/src/librustc_mir/util/liveness.rs
@@ -57,7 +57,7 @@ pub struct LivenessResult {
 /// Computes which local variables are live within the given function
 /// `mir`, including drops.
 pub fn liveness_of_locals(
-    body_cache: &ReadOnlyBodyCache<'_, '_>,
+    body_cache: ReadOnlyBodyCache<'_, '_>,
 ) -> LivenessResult {
     let num_live_vars = body_cache.local_decls.len();
 
@@ -84,7 +84,7 @@ pub fn liveness_of_locals(
     // order when cycles are present, but the overhead of computing the reverse CFG may outweigh
     // any benefits. Benchmark this and find out.
     let mut dirty_queue: WorkQueue<BasicBlock> = WorkQueue::with_none(body_cache.basic_blocks().len());
-    for (bb, _) in traversal::postorder(body_cache) {
+    for (bb, _) in traversal::postorder(body_cache.body()) {
         dirty_queue.insert(bb);
     }
 
diff --git a/src/librustc_mir/util/patch.rs b/src/librustc_mir/util/patch.rs
index 01be0f598ed..6e110b1177f 100644
--- a/src/librustc_mir/util/patch.rs
+++ b/src/librustc_mir/util/patch.rs
@@ -127,7 +127,7 @@ impl<'tcx> MirPatch<'tcx> {
         self.make_nop.push(loc);
     }
 
-    pub fn apply(self, body_cache: &mut BodyCache<&'_ mut Body<'tcx>>) {
+    pub fn apply(self, body_cache: &mut BodyCache<'tcx>) {
         debug!("MirPatch: make nops at: {:?}", self.make_nop);
         for loc in self.make_nop {
             body_cache.make_statement_nop(loc);