diff --git a/src/librustc/ich/impls_mir.rs b/src/librustc/ich/impls_mir.rs
index a73fe2b8a1ab3..1bd02dfeacaad 100644
--- a/src/librustc/ich/impls_mir.rs
+++ b/src/librustc/ich/impls_mir.rs
@@ -257,6 +257,9 @@ for mir::StatementKind<'gcx> {
             mir::StatementKind::EndRegion(ref region_scope) => {
                 region_scope.hash_stable(hcx, hasher);
             }
+            mir::StatementKind::EscapeToRaw(ref place) => {
+                place.hash_stable(hcx, hasher);
+            }
             mir::StatementKind::Retag { fn_entry, ref place } => {
                 fn_entry.hash_stable(hcx, hasher);
                 place.hash_stable(hcx, hasher);
diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs
index 36bc2edcf584e..0817c2e8bf273 100644
--- a/src/librustc/mir/mod.rs
+++ b/src/librustc/mir/mod.rs
@@ -1766,6 +1766,13 @@ pub enum StatementKind<'tcx> {
         place: Place<'tcx>,
     },
 
+    /// Escape the given reference to a raw pointer, so that it can be accessed
+    /// without precise provenance tracking. These statements are currently only interpreted
+    /// by miri and only generated when "-Z mir-emit-retag" is passed.
+    /// See 
+    /// for more details.
+    EscapeToRaw(Operand<'tcx>),
+
     /// Mark one terminating point of a region scope (i.e. static region).
     /// (The starting point(s) arise implicitly from borrows.)
     EndRegion(region::Scope),
@@ -1827,6 +1834,7 @@ impl<'tcx> Debug for Statement<'tcx> {
             EndRegion(ref ce) => write!(fmt, "EndRegion({})", ty::ReScope(*ce)),
             Retag { fn_entry, ref place } =>
                 write!(fmt, "Retag({}{:?})", if fn_entry { "[fn entry] " } else { "" }, place),
+            EscapeToRaw(ref place) => write!(fmt, "EscapeToRaw({:?})", place),
             StorageLive(ref place) => write!(fmt, "StorageLive({:?})", place),
             StorageDead(ref place) => write!(fmt, "StorageDead({:?})", place),
             SetDiscriminant {
@@ -2968,6 +2976,7 @@ EnumTypeFoldableImpl! {
         (StatementKind::StorageDead)(a),
         (StatementKind::InlineAsm) { asm, outputs, inputs },
         (StatementKind::Retag) { fn_entry, place },
+        (StatementKind::EscapeToRaw)(place),
         (StatementKind::EndRegion)(a),
         (StatementKind::AscribeUserType)(a, v, b),
         (StatementKind::Nop),
diff --git a/src/librustc/mir/visit.rs b/src/librustc/mir/visit.rs
index c130e047e47d1..2a994ee0509c2 100644
--- a/src/librustc/mir/visit.rs
+++ b/src/librustc/mir/visit.rs
@@ -385,6 +385,9 @@ macro_rules! make_mir_visitor {
                             location
                         );
                     }
+                    StatementKind::EscapeToRaw(ref $($mutability)* op) => {
+                        self.visit_operand(op, location);
+                    }
                     StatementKind::StorageLive(ref $($mutability)* local) => {
                         self.visit_local(
                             local,
@@ -1022,7 +1025,7 @@ pub enum MutatingUseContext<'tcx> {
     ///     f(&mut x.y);
     ///
     Projection,
-    /// Retagging (updating the "Stacked Borrows" tag)
+    /// Retagging, a "Stacked Borrows" shadow state operation
     Retag,
 }
 
diff --git a/src/librustc_codegen_llvm/mir/statement.rs b/src/librustc_codegen_llvm/mir/statement.rs
index c8c8e02bf05f7..8bda2c98594e5 100644
--- a/src/librustc_codegen_llvm/mir/statement.rs
+++ b/src/librustc_codegen_llvm/mir/statement.rs
@@ -105,8 +105,9 @@ impl FunctionCx<'a, 'll, 'tcx> {
                 bx
             }
             mir::StatementKind::FakeRead(..) |
-            mir::StatementKind::EndRegion(_) |
+            mir::StatementKind::EndRegion(..) |
             mir::StatementKind::Retag { .. } |
+            mir::StatementKind::EscapeToRaw { .. } |
             mir::StatementKind::AscribeUserType(..) |
             mir::StatementKind::Nop => bx,
         }
diff --git a/src/librustc_mir/borrow_check/mod.rs b/src/librustc_mir/borrow_check/mod.rs
index d4f00ab3bb91a..533b777d67d73 100644
--- a/src/librustc_mir/borrow_check/mod.rs
+++ b/src/librustc_mir/borrow_check/mod.rs
@@ -601,6 +601,7 @@ impl<'cx, 'gcx, 'tcx> DataflowResultsConsumer<'cx, 'tcx> for MirBorrowckCtxt<'cx
             StatementKind::Nop
             | StatementKind::AscribeUserType(..)
             | StatementKind::Retag { .. }
+            | StatementKind::EscapeToRaw { .. }
             | StatementKind::StorageLive(..) => {
                 // `Nop`, `AscribeUserType`, `Retag`, and `StorageLive` are irrelevant
                 // to borrow check.
diff --git a/src/librustc_mir/borrow_check/nll/invalidation.rs b/src/librustc_mir/borrow_check/nll/invalidation.rs
index cfe03c2d1c71e..576509c0fddda 100644
--- a/src/librustc_mir/borrow_check/nll/invalidation.rs
+++ b/src/librustc_mir/borrow_check/nll/invalidation.rs
@@ -137,6 +137,7 @@ impl<'cx, 'tcx, 'gcx> Visitor<'tcx> for InvalidationGenerator<'cx, 'tcx, 'gcx> {
             StatementKind::Nop |
             StatementKind::AscribeUserType(..) |
             StatementKind::Retag { .. } |
+            StatementKind::EscapeToRaw { .. } |
             StatementKind::StorageLive(..) => {
                 // `Nop`, `AscribeUserType`, `Retag`, and `StorageLive` are irrelevant
                 // to borrow check.
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 734ddbc3ab9a7..3c2301ba4a7d4 100644
--- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs
+++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs
@@ -1290,11 +1290,12 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
                 }
             }
             StatementKind::FakeRead(..)
-            | StatementKind::StorageLive(_)
-            | StatementKind::StorageDead(_)
+            | StatementKind::StorageLive(..)
+            | StatementKind::StorageDead(..)
             | StatementKind::InlineAsm { .. }
             | StatementKind::EndRegion(_)
             | StatementKind::Retag { .. }
+            | StatementKind::EscapeToRaw { .. }
             | StatementKind::Nop => {}
         }
     }
diff --git a/src/librustc_mir/build/expr/as_place.rs b/src/librustc_mir/build/expr/as_place.rs
index 77746e5538d65..cb3c88876a3a8 100644
--- a/src/librustc_mir/build/expr/as_place.rs
+++ b/src/librustc_mir/build/expr/as_place.rs
@@ -86,6 +86,9 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
                 // region_scope=None so place indexes live forever. They are scalars so they
                 // do not need storage annotations, and they are often copied between
                 // places.
+                // Making this a *fresh* temporary also means we do not have to worry about
+                // the index changing later: Nothing will ever change this temporary.
+                // The "retagging" transformation (for Stacked Borrows) relies on this.
                 let idx = unpack!(block = this.as_temp(block, None, index, Mutability::Mut));
 
                 // bounds check:
diff --git a/src/librustc_mir/const_eval.rs b/src/librustc_mir/const_eval.rs
index 011887090eefe..cbcc6709c0199 100644
--- a/src/librustc_mir/const_eval.rs
+++ b/src/librustc_mir/const_eval.rs
@@ -351,7 +351,6 @@ impl<'a, 'mir, 'tcx> interpret::Machine<'a, 'mir, 'tcx>
     type MemoryMap = FxHashMap, Allocation)>;
 
     const STATIC_KIND: Option = None; // no copying of statics allowed
-    const ENABLE_PTR_TRACKING_HOOKS: bool = false; // we don't have no provenance
 
     #[inline(always)]
     fn enforce_validity(_ecx: &EvalContext<'a, 'mir, 'tcx, Self>) -> bool {
diff --git a/src/librustc_mir/dataflow/impls/borrows.rs b/src/librustc_mir/dataflow/impls/borrows.rs
index 69d2a89b5f237..811da9e1acca7 100644
--- a/src/librustc_mir/dataflow/impls/borrows.rs
+++ b/src/librustc_mir/dataflow/impls/borrows.rs
@@ -339,6 +339,7 @@ impl<'a, 'gcx, 'tcx> BitDenotation for Borrows<'a, 'gcx, 'tcx> {
             mir::StatementKind::SetDiscriminant { .. } |
             mir::StatementKind::StorageLive(..) |
             mir::StatementKind::Retag { .. } |
+            mir::StatementKind::EscapeToRaw { .. } |
             mir::StatementKind::AscribeUserType(..) |
             mir::StatementKind::Nop => {}
 
diff --git a/src/librustc_mir/dataflow/move_paths/builder.rs b/src/librustc_mir/dataflow/move_paths/builder.rs
index 874e862de238b..e6e165ef3de4a 100644
--- a/src/librustc_mir/dataflow/move_paths/builder.rs
+++ b/src/librustc_mir/dataflow/move_paths/builder.rs
@@ -301,8 +301,9 @@ impl<'b, 'a, 'gcx, 'tcx> Gatherer<'b, 'a, 'gcx, 'tcx> {
                 span_bug!(stmt.source_info.span,
                           "SetDiscriminant should not exist during borrowck");
             }
-            StatementKind::EndRegion(_) |
+            StatementKind::EndRegion(..) |
             StatementKind::Retag { .. } |
+            StatementKind::EscapeToRaw { .. } |
             StatementKind::AscribeUserType(..) |
             StatementKind::Nop => {}
         }
diff --git a/src/librustc_mir/interpret/cast.rs b/src/librustc_mir/interpret/cast.rs
index 06748d60e4583..118539fc58ebf 100644
--- a/src/librustc_mir/interpret/cast.rs
+++ b/src/librustc_mir/interpret/cast.rs
@@ -44,28 +44,16 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M>
             }
 
             Misc => {
-                let src_layout = src.layout;
                 let src = self.read_immediate(src)?;
 
-                let src = if M::ENABLE_PTR_TRACKING_HOOKS && src_layout.ty.is_region_ptr() {
-                    // The only `Misc` casts on references are those creating raw pointers.
-                    assert!(dest.layout.ty.is_unsafe_ptr());
-                    // For the purpose of the "ptr tag hooks", treat this as creating
-                    // a new, raw reference.
-                    let place = self.ref_to_mplace(src)?;
-                    self.create_ref(place, None)?
-                } else {
-                    *src
-                };
-
-                if self.type_is_fat_ptr(src_layout.ty) {
-                    match (src, self.type_is_fat_ptr(dest.layout.ty)) {
+                if self.type_is_fat_ptr(src.layout.ty) {
+                    match (*src, self.type_is_fat_ptr(dest.layout.ty)) {
                         // pointers to extern types
                         (Immediate::Scalar(_),_) |
                         // slices and trait objects to other slices/trait objects
                         (Immediate::ScalarPair(..), true) => {
                             // No change to immediate
-                            self.write_immediate(src, dest)?;
+                            self.write_immediate(*src, dest)?;
                         }
                         // slices and trait objects to thin pointers (dropping the metadata)
                         (Immediate::ScalarPair(data, _), false) => {
@@ -73,11 +61,11 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M>
                         }
                     }
                 } else {
-                    match src_layout.variants {
+                    match src.layout.variants {
                         layout::Variants::Single { index } => {
-                            if let Some(def) = src_layout.ty.ty_adt_def() {
+                            if let Some(def) = src.layout.ty.ty_adt_def() {
                                 // Cast from a univariant enum
-                                assert!(src_layout.is_zst());
+                                assert!(src.layout.is_zst());
                                 let discr_val = def
                                     .discriminant_for_variant(*self.tcx, index)
                                     .val;
@@ -90,8 +78,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M>
                         layout::Variants::NicheFilling { .. } => {},
                     }
 
-                    let src = src.to_scalar()?;
-                    let dest_val = self.cast_scalar(src, src_layout, dest.layout)?;
+                    let dest_val = self.cast_scalar(src.to_scalar()?, src.layout, dest.layout)?;
                     self.write_scalar(dest_val, dest)?;
                 }
             }
diff --git a/src/librustc_mir/interpret/intrinsics.rs b/src/librustc_mir/interpret/intrinsics.rs
index cb2a750f4e3b6..7c704e9791140 100644
--- a/src/librustc_mir/interpret/intrinsics.rs
+++ b/src/librustc_mir/interpret/intrinsics.rs
@@ -183,8 +183,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M>
         } else if Some(def_id) == self.tcx.lang_items().panic_fn() {
             assert!(args.len() == 1);
             // &(&'static str, &'static str, u32, u32)
-            let ptr = self.read_immediate(args[0])?;
-            let place = self.ref_to_mplace(ptr)?;
+            let place = self.deref_operand(args[0])?;
             let (msg, file, line, col) = (
                 self.mplace_field(place, 0)?,
                 self.mplace_field(place, 1)?,
@@ -192,9 +191,9 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M>
                 self.mplace_field(place, 3)?,
             );
 
-            let msg_place = self.ref_to_mplace(self.read_immediate(msg.into())?)?;
+            let msg_place = self.deref_operand(msg.into())?;
             let msg = Symbol::intern(self.read_str(msg_place)?);
-            let file_place = self.ref_to_mplace(self.read_immediate(file.into())?)?;
+            let file_place = self.deref_operand(file.into())?;
             let file = Symbol::intern(self.read_str(file_place)?);
             let line = self.read_scalar(line.into())?.to_u32()?;
             let col = self.read_scalar(col.into())?.to_u32()?;
@@ -203,17 +202,16 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M>
             assert!(args.len() == 2);
             // &'static str, &(&'static str, u32, u32)
             let msg = args[0];
-            let ptr = self.read_immediate(args[1])?;
-            let place = self.ref_to_mplace(ptr)?;
+            let place = self.deref_operand(args[1])?;
             let (file, line, col) = (
                 self.mplace_field(place, 0)?,
                 self.mplace_field(place, 1)?,
                 self.mplace_field(place, 2)?,
             );
 
-            let msg_place = self.ref_to_mplace(self.read_immediate(msg.into())?)?;
+            let msg_place = self.deref_operand(msg.into())?;
             let msg = Symbol::intern(self.read_str(msg_place)?);
-            let file_place = self.ref_to_mplace(self.read_immediate(file.into())?)?;
+            let file_place = self.deref_operand(file.into())?;
             let file = Symbol::intern(self.read_str(file_place)?);
             let line = self.read_scalar(line.into())?.to_u32()?;
             let col = self.read_scalar(col.into())?.to_u32()?;
diff --git a/src/librustc_mir/interpret/machine.rs b/src/librustc_mir/interpret/machine.rs
index 27cf28ef41e8a..55da12a68e385 100644
--- a/src/librustc_mir/interpret/machine.rs
+++ b/src/librustc_mir/interpret/machine.rs
@@ -95,11 +95,6 @@ pub trait Machine<'a, 'mir, 'tcx>: Sized {
     /// that is added to the memory so that the work is not done twice.
     const STATIC_KIND: Option;
 
-    /// As an optimization, you can prevent the pointer tracking hooks from ever being
-    /// called.  You should only do this if you do not care about provenance tracking.
-    /// This controls the `tag_reference` and `tag_dereference` hooks.
-    const ENABLE_PTR_TRACKING_HOOKS: bool;
-
     /// Whether to enforce the validity invariant
     fn enforce_validity(ecx: &EvalContext<'a, 'mir, 'tcx, Self>) -> bool;
 
@@ -211,18 +206,6 @@ pub trait Machine<'a, 'mir, 'tcx>: Sized {
         kind: MemoryKind,
     ) -> EvalResult<'tcx, Pointer>;
 
-    /// Executed when evaluating the `&` operator: Creating a new reference.
-    /// This has the chance to adjust the tag.  It should not change anything else!
-    /// `mutability` can be `None` in case a raw ptr is being created.
-    #[inline]
-    fn tag_reference(
-        _ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
-        place: MPlaceTy<'tcx, Self::PointerTag>,
-        _mutability: Option,
-    ) -> EvalResult<'tcx, Scalar> {
-        Ok(place.ptr)
-    }
-
     /// Executed when evaluating the `*` operator: Following a reference.
     /// This has the chance to adjust the tag.  It should not change anything else!
     /// `mutability` can be `None` in case a raw ptr is being dereferenced.
@@ -235,7 +218,7 @@ pub trait Machine<'a, 'mir, 'tcx>: Sized {
         Ok(place.ptr)
     }
 
-    /// Execute a validation operation
+    /// Execute a retagging operation
     #[inline]
     fn retag(
         _ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
@@ -244,4 +227,13 @@ pub trait Machine<'a, 'mir, 'tcx>: Sized {
     ) -> EvalResult<'tcx> {
         Ok(())
     }
+
+    /// Execute an escape-to-raw operation
+    #[inline]
+    fn escape_to_raw(
+        _ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
+        _ptr: OpTy<'tcx, Self::PointerTag>,
+    ) -> EvalResult<'tcx> {
+        Ok(())
+    }
 }
diff --git a/src/librustc_mir/interpret/operand.rs b/src/librustc_mir/interpret/operand.rs
index 83a2d14b7ca4c..a97bf95e7345d 100644
--- a/src/librustc_mir/interpret/operand.rs
+++ b/src/librustc_mir/interpret/operand.rs
@@ -555,17 +555,6 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M>
         })
     }
 
-    // Take an operand, representing a pointer, and dereference it to a place -- that
-    // will always be a MemPlace.
-    pub(super) fn deref_operand(
-        &self,
-        src: OpTy<'tcx, M::PointerTag>,
-    ) -> EvalResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> {
-        let val = self.read_immediate(src)?;
-        trace!("deref to {} on {:?}", val.layout.ty, *val);
-        Ok(self.ref_to_mplace(val)?)
-    }
-
     pub fn operand_projection(
         &self,
         base: OpTy<'tcx, M::PointerTag>,
diff --git a/src/librustc_mir/interpret/place.rs b/src/librustc_mir/interpret/place.rs
index 19430c85cf73c..510b5826e489b 100644
--- a/src/librustc_mir/interpret/place.rs
+++ b/src/librustc_mir/interpret/place.rs
@@ -151,6 +151,16 @@ impl MemPlace {
         // it now must be aligned.
         self.to_scalar_ptr_align().0.to_ptr()
     }
+
+    /// Turn a mplace into a (thin or fat) pointer, as a reference, pointing to the same space.
+    /// This is the inverse of `ref_to_mplace`.
+    #[inline(always)]
+    pub fn to_ref(self) -> Immediate {
+        match self.meta {
+            None => Immediate::Scalar(self.ptr.into()),
+            Some(meta) => Immediate::ScalarPair(self.ptr.into(), meta.into()),
+        }
+    }
 }
 
 impl<'tcx, Tag> MPlaceTy<'tcx, Tag> {
@@ -266,7 +276,9 @@ where
     M::MemoryMap: AllocMap, Allocation)>,
 {
     /// Take a value, which represents a (thin or fat) reference, and make it a place.
-    /// Alignment is just based on the type.  This is the inverse of `create_ref`.
+    /// Alignment is just based on the type.  This is the inverse of `MemPlace::to_ref()`.
+    /// This does NOT call the "deref" machine hook, so it does NOT count as a
+    /// deref as far as Stacked Borrows is concerned.  Use `deref_operand` for that!
     pub fn ref_to_mplace(
         &self,
         val: ImmTy<'tcx, M::PointerTag>,
@@ -274,42 +286,35 @@ where
         let pointee_type = val.layout.ty.builtin_deref(true).unwrap().ty;
         let layout = self.layout_of(pointee_type)?;
 
-        let align = layout.align;
-        let meta = val.to_meta()?;
-        let ptr = val.to_scalar_ptr()?;
-        let mplace = MemPlace { ptr, align, meta };
-        let mut mplace = MPlaceTy { mplace, layout };
-        // Pointer tag tracking might want to adjust the tag.
-        if M::ENABLE_PTR_TRACKING_HOOKS {
-            let mutbl = match val.layout.ty.sty {
-                // `builtin_deref` considers boxes immutable, that's useless for our purposes
-                ty::Ref(_, _, mutbl) => Some(mutbl),
-                ty::Adt(def, _) if def.is_box() => Some(hir::MutMutable),
-                ty::RawPtr(_) => None,
-                _ => bug!("Unexpected pointer type {}", val.layout.ty.sty),
-            };
-            mplace.mplace.ptr = M::tag_dereference(self, mplace, mutbl)?;
-        }
-        // Done
-        Ok(mplace)
+        let mplace = MemPlace {
+            ptr: val.to_scalar_ptr()?,
+            align: layout.align,
+            meta: val.to_meta()?,
+        };
+        Ok(MPlaceTy { mplace, layout })
     }
 
-    /// Turn a mplace into a (thin or fat) pointer, as a reference, pointing to the same space.
-    /// This is the inverse of `ref_to_mplace`.
-    /// `mutbl` indicates whether we are create a shared or mutable ref, or a raw pointer (`None`).
-    pub fn create_ref(
-        &mut self,
-        mut place: MPlaceTy<'tcx, M::PointerTag>,
-        mutbl: Option,
-    ) -> EvalResult<'tcx, Immediate> {
-        // Pointer tag tracking might want to adjust the tag
-        if M::ENABLE_PTR_TRACKING_HOOKS {
-            place.mplace.ptr = M::tag_reference(self, place, mutbl)?
-        }
-        Ok(match place.meta {
-            None => Immediate::Scalar(place.ptr.into()),
-            Some(meta) => Immediate::ScalarPair(place.ptr.into(), meta.into()),
-        })
+    // Take an operand, representing a pointer, and dereference it to a place -- that
+    // will always be a MemPlace.  Lives in `place.rs` because it creates a place.
+    // This calls the "deref" machine hook, and counts as a deref as far as
+    // Stacked Borrows is concerned.
+    pub fn deref_operand(
+        &self,
+        src: OpTy<'tcx, M::PointerTag>,
+    ) -> EvalResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> {
+        let val = self.read_immediate(src)?;
+        trace!("deref to {} on {:?}", val.layout.ty, *val);
+        let mut place = self.ref_to_mplace(val)?;
+        // Pointer tag tracking might want to adjust the tag.
+        let mutbl = match val.layout.ty.sty {
+            // `builtin_deref` considers boxes immutable, that's useless for our purposes
+            ty::Ref(_, _, mutbl) => Some(mutbl),
+            ty::Adt(def, _) if def.is_box() => Some(hir::MutMutable),
+            ty::RawPtr(_) => None,
+            _ => bug!("Unexpected pointer type {}", val.layout.ty.sty),
+        };
+        place.mplace.ptr = M::tag_dereference(self, place, mutbl)?;
+        Ok(place)
     }
 
     /// Offset a pointer to project to a field. Unlike place_field, this is always
diff --git a/src/librustc_mir/interpret/step.rs b/src/librustc_mir/interpret/step.rs
index db055204c0a19..ac13e5982dae3 100644
--- a/src/librustc_mir/interpret/step.rs
+++ b/src/librustc_mir/interpret/step.rs
@@ -12,7 +12,7 @@
 //!
 //! The main entry point is the `step` method.
 
-use rustc::{hir, mir};
+use rustc::mir;
 use rustc::ty::layout::LayoutOf;
 use rustc::mir::interpret::{EvalResult, Scalar, PointerArithmetic};
 
@@ -118,12 +118,17 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M>
             // interpreter is solely intended for borrowck'ed code.
             FakeRead(..) => {}
 
-            // Retagging.
+            // Stacked Borrows.
             Retag { fn_entry, ref place } => {
                 let dest = self.eval_place(place)?;
                 M::retag(self, fn_entry, dest)?;
             }
+            EscapeToRaw(ref op) => {
+                let op = self.eval_operand(op, None)?;
+                M::escape_to_raw(self, op)?;
+            }
 
+            // Statements we do not track.
             EndRegion(..) => {}
             AscribeUserType(..) => {}
 
@@ -247,19 +252,10 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M>
                 )?;
             }
 
-            Ref(_, borrow_kind, ref place) => {
+            Ref(_, _, ref place) => {
                 let src = self.eval_place(place)?;
                 let val = self.force_allocation(src)?;
-                let mutbl = match borrow_kind {
-                    mir::BorrowKind::Mut { .. } |
-                    mir::BorrowKind::Unique =>
-                        hir::MutMutable,
-                    mir::BorrowKind::Shared |
-                    mir::BorrowKind::Shallow =>
-                        hir::MutImmutable,
-                };
-                let val = self.create_ref(val, Some(mutbl))?;
-                self.write_immediate(val, dest)?;
+                self.write_immediate(val.to_ref(), dest)?;
             }
 
             NullaryOp(mir::NullOp::Box, _) => {
diff --git a/src/librustc_mir/interpret/terminator.rs b/src/librustc_mir/interpret/terminator.rs
index b5df625b3028b..6070b31d3e7a3 100644
--- a/src/librustc_mir/interpret/terminator.rs
+++ b/src/librustc_mir/interpret/terminator.rs
@@ -402,7 +402,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M>
             ty::InstanceDef::Virtual(_, idx) => {
                 let ptr_size = self.pointer_size();
                 let ptr_align = self.tcx.data_layout.pointer_align;
-                let ptr = self.ref_to_mplace(self.read_immediate(args[0])?)?;
+                let ptr = self.deref_operand(args[0])?;
                 let vtable = ptr.vtable()?;
                 let fn_ptr = self.memory.read_ptr_sized(
                     vtable.offset(ptr_size * (idx as u64 + 3), self)?,
@@ -447,10 +447,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M>
         };
 
         let arg = OpTy {
-            op: Operand::Immediate(self.create_ref(
-                place,
-                None // this is a "raw reference"
-            )?),
+            op: Operand::Immediate(place.to_ref()),
             layout: self.layout_of(self.tcx.mk_mut_ptr(place.layout.ty))?,
         };
 
diff --git a/src/librustc_mir/interpret/validity.rs b/src/librustc_mir/interpret/validity.rs
index 8c8b3e2ca77c4..97bce651c05a1 100644
--- a/src/librustc_mir/interpret/validity.rs
+++ b/src/librustc_mir/interpret/validity.rs
@@ -322,13 +322,10 @@ impl<'rt, 'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>>
                         }
                     }
                 }
-                // Turn ptr into place.
-                // `ref_to_mplace` also calls the machine hook for (re)activating the tag,
-                // which in turn will (in full miri) check if the pointer is dereferencable.
-                let place = self.ecx.ref_to_mplace(value)?;
                 // Recursive checking
                 if let Some(ref mut ref_tracking) = self.ref_tracking {
                     assert!(self.const_mode, "We should only do recursie checking in const mode");
+                    let place = self.ecx.ref_to_mplace(value)?;
                     if size != Size::ZERO {
                         // Non-ZST also have to be dereferencable
                         let ptr = try_validation!(place.ptr.to_ptr(),
diff --git a/src/librustc_mir/shim.rs b/src/librustc_mir/shim.rs
index 76a8501fb177a..2d548dd8cf10a 100644
--- a/src/librustc_mir/shim.rs
+++ b/src/librustc_mir/shim.rs
@@ -222,6 +222,15 @@ fn build_drop_shim<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
     );
 
     if let Some(..) = ty {
+        // The first argument (index 0), but add 1 for the return value.
+        let dropee_ptr = Place::Local(Local::new(1+0));
+        if tcx.sess.opts.debugging_opts.mir_emit_retag {
+            // We use raw ptr operations, better prepare the alias tracking for that
+            mir.basic_blocks_mut()[START_BLOCK].statements.insert(0, Statement {
+                source_info,
+                kind: StatementKind::EscapeToRaw(Operand::Copy(dropee_ptr.clone())),
+            })
+        }
         let patch = {
             let param_env = tcx.param_env(def_id).with_reveal_all();
             let mut elaborator = DropShimElaborator {
@@ -230,7 +239,7 @@ fn build_drop_shim<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                 tcx,
                 param_env
             };
-            let dropee = Place::Local(Local::new(1+0)).deref();
+            let dropee = dropee_ptr.deref();
             let resume_block = elaborator.patch.resume_block();
             elaborate_drops::elaborate_drop(
                 &mut elaborator,
diff --git a/src/librustc_mir/transform/add_retag.rs b/src/librustc_mir/transform/add_retag.rs
index a50011cf5a15e..be7e34e2dcb3c 100644
--- a/src/librustc_mir/transform/add_retag.rs
+++ b/src/librustc_mir/transform/add_retag.rs
@@ -20,42 +20,46 @@ use transform::{MirPass, MirSource};
 
 pub struct AddRetag;
 
-/// Determines whether this place is local: If it is part of a local variable.
-/// We do not consider writes to pointers local, only writes that immediately assign
-/// to a local variable.
-/// One important property here is that evaluating the place immediately after
-/// the assignment must produce the same place as what was used during the assignment.
-fn is_local<'tcx>(
+/// Determines whether this place is "stable": Whether, if we evaluate it again
+/// after the assignment, we can be sure to obtain the same place value.
+/// (Concurrent accesses by other threads are no problem as these are anyway non-atomic
+/// copies.  Data races are UB.)
+fn is_stable<'tcx>(
     place: &Place<'tcx>,
 ) -> bool {
     use rustc::mir::Place::*;
 
     match *place {
-        Local { .. } => true,
-        Promoted(_) |
-        Static(_) => false,
+        // Locals and statics have stable addresses, for sure
+        Local { .. } |
+        Promoted { .. } |
+        Static { .. } =>
+            true,
+        // Recurse for projections
         Projection(ref proj) => {
             match proj.elem {
-                ProjectionElem::Deref |
-                ProjectionElem::Index(_) =>
-                    // Which place these point to depends on external circumstances
-                    // (a local storing the array index, the current value of
-                    // the projection base), so we stop tracking here.
+                // Which place this evaluates to can change with any memory write,
+                // so cannot assume this to be stable.
+                ProjectionElem::Deref =>
                     false,
+                // Array indices are intersting, but MIR building generates a *fresh*
+                // temporary for every array access, so the index cannot be changed as
+                // a side-effect.
+                ProjectionElem::Index { .. } |
+                // The rest is completely boring, they just offset by a constant.
                 ProjectionElem::Field { .. } |
                 ProjectionElem::ConstantIndex { .. } |
                 ProjectionElem::Subslice { .. } |
                 ProjectionElem::Downcast { .. } =>
-                    // These just offset by a constant, entirely independent of everything else.
-                    is_local(&proj.base),
+                    is_stable(&proj.base),
             }
         }
     }
 }
 
-/// Determine whether this type has a reference in it, recursing below compound types but
+/// Determine whether this type may have a reference in it, recursing below compound types but
 /// not below references.
-fn has_reference<'a, 'gcx, 'tcx>(ty: Ty<'tcx>, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> bool {
+fn may_have_reference<'a, 'gcx, 'tcx>(ty: Ty<'tcx>, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> bool {
     match ty.sty {
         // Primitive types that are not references
         ty::Bool | ty::Char |
@@ -68,12 +72,12 @@ fn has_reference<'a, 'gcx, 'tcx>(ty: Ty<'tcx>, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> b
         ty::Adt(..) if ty.is_box() => true,
         // Compound types
         ty::Array(ty, ..) | ty::Slice(ty) =>
-            has_reference(ty, tcx),
+            may_have_reference(ty, tcx),
         ty::Tuple(tys) =>
-            tys.iter().any(|ty| has_reference(ty, tcx)),
+            tys.iter().any(|ty| may_have_reference(ty, tcx)),
         ty::Adt(adt, substs) =>
             adt.variants.iter().any(|v| v.fields.iter().any(|f|
-                has_reference(f.ty(tcx, substs), tcx)
+                may_have_reference(f.ty(tcx, substs), tcx)
             )),
         // Conservative fallback
         _ => true,
@@ -92,7 +96,9 @@ impl MirPass for AddRetag {
         let (span, arg_count) = (mir.span, mir.arg_count);
         let (basic_blocks, local_decls) = mir.basic_blocks_and_local_decls_mut();
         let needs_retag = |place: &Place<'tcx>| {
-            is_local(place) && has_reference(place.ty(&*local_decls, tcx).to_ty(tcx), tcx)
+            // FIXME: Instead of giving up for unstable places, we should introduce
+            // a temporary and retag on that.
+            is_stable(place) && may_have_reference(place.ty(&*local_decls, tcx).to_ty(tcx), tcx)
         };
 
         // PART 1
@@ -118,23 +124,29 @@ impl MirPass for AddRetag {
         }
 
         // PART 2
-        // Retag return values of functions.
+        // Retag return values of functions.  Also escape-to-raw the argument of `drop`.
         // We collect the return destinations because we cannot mutate while iterating.
         let mut returns: Vec<(SourceInfo, Place<'tcx>, BasicBlock)> = Vec::new();
         for block_data in basic_blocks.iter_mut() {
-            match block_data.terminator {
-                Some(Terminator { kind: TerminatorKind::Call { ref destination, .. },
-                                  source_info }) => {
+            match block_data.terminator().kind {
+                TerminatorKind::Call { ref destination, .. } => {
                     // Remember the return destination for later
                     if let Some(ref destination) = destination {
                         if needs_retag(&destination.0) {
-                            returns.push((source_info, destination.0.clone(), destination.1));
+                            returns.push((
+                                block_data.terminator().source_info,
+                                destination.0.clone(),
+                                destination.1,
+                            ));
                         }
                     }
                 }
+                TerminatorKind::Drop { .. } |
+                TerminatorKind::DropAndReplace { .. } => {
+                    // `Drop` is also a call, but it doesn't return anything so we are good.
+                }
                 _ => {
                     // Not a block ending in a Call -> ignore.
-                    // `Drop` is also a call, but it doesn't return anything so we are good.
                 }
             }
         }
@@ -153,21 +165,43 @@ impl MirPass for AddRetag {
             // iterate backwards using indices.
             for i in (0..block_data.statements.len()).rev() {
                 match block_data.statements[i].kind {
-                    // Assignments can make values obtained elsewhere "local".
-                    // We could try to be smart here and e.g. only retag if the assignment
-                    // loaded from memory, but that seems risky: We might miss a subtle corner
-                    // case.
-                    StatementKind::Assign(ref place, box Rvalue::Use(..))
-                    if needs_retag(place) => {
+                    // If we are casting *from* a reference, we may have to escape-to-raw.
+                    StatementKind::Assign(_, box Rvalue::Cast(
+                        CastKind::Misc,
+                        ref src,
+                        dest_ty,
+                    )) => {
+                        let src_ty = src.ty(&*local_decls, tcx);
+                        if src_ty.is_region_ptr() {
+                            // The only `Misc` casts on references are those creating raw pointers.
+                            assert!(dest_ty.is_unsafe_ptr());
+                            // Insert escape-to-raw before the cast.  We are not concerned
+                            // with stability here: Our EscapeToRaw will not change the value
+                            // that the cast will then use.
+                            // `src` might be a "move", but we rely on this not actually moving
+                            // but just doing a memcpy.  It is crucial that we do EscapeToRaw
+                            // on the src because we need it with its original type.
+                            let source_info = block_data.statements[i].source_info;
+                            block_data.statements.insert(i, Statement {
+                                source_info,
+                                kind: StatementKind::EscapeToRaw(src.clone()),
+                            });
+                        }
+                    }
+                    // Assignments of reference or ptr type are the ones where we may have
+                    // to update tags.  This includes `x = &[mut] ...` and hence
+                    // we also retag after taking a reference!
+                    StatementKind::Assign(ref place, _) if needs_retag(place) => {
                         // Insert a retag after the assignment.
                         let source_info = block_data.statements[i].source_info;
-                        block_data.statements.insert(i+1,Statement {
+                        block_data.statements.insert(i+1, Statement {
                             source_info,
                             kind: StatementKind::Retag { fn_entry: false, place: place.clone() },
                         });
                     }
+                    // Do nothing for the rest
                     _ => {},
-                }
+                };
             }
         }
     }
diff --git a/src/librustc_mir/transform/check_unsafety.rs b/src/librustc_mir/transform/check_unsafety.rs
index c28bb0ca35704..4ebeebca2273b 100644
--- a/src/librustc_mir/transform/check_unsafety.rs
+++ b/src/librustc_mir/transform/check_unsafety.rs
@@ -114,6 +114,7 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> {
             StatementKind::StorageDead(..) |
             StatementKind::EndRegion(..) |
             StatementKind::Retag { .. } |
+            StatementKind::EscapeToRaw { .. } |
             StatementKind::AscribeUserType(..) |
             StatementKind::Nop => {
                 // safe (at least as emitted during MIR construction)
diff --git a/src/librustc_mir/transform/generator.rs b/src/librustc_mir/transform/generator.rs
index 5889fabee9d6e..a292887b17e47 100644
--- a/src/librustc_mir/transform/generator.rs
+++ b/src/librustc_mir/transform/generator.rs
@@ -684,6 +684,13 @@ fn create_generator_drop_shim<'a, 'tcx>(
         is_block_tail: None,
         is_user_variable: None,
     };
+    if tcx.sess.opts.debugging_opts.mir_emit_retag {
+        // Alias tracking must know we changed the type
+        mir.basic_blocks_mut()[START_BLOCK].statements.insert(0, Statement {
+            source_info,
+            kind: StatementKind::EscapeToRaw(Operand::Copy(Place::Local(self_arg()))),
+        })
+    }
 
     no_landing_pads(tcx, &mut mir);
 
diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs
index ca9c4eb9b8bb9..5fd8332129664 100644
--- a/src/librustc_mir/transform/qualify_consts.rs
+++ b/src/librustc_mir/transform/qualify_consts.rs
@@ -1168,6 +1168,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
                 StatementKind::InlineAsm {..} |
                 StatementKind::EndRegion(_) |
                 StatementKind::Retag { .. } |
+                StatementKind::EscapeToRaw { .. } |
                 StatementKind::AscribeUserType(..) |
                 StatementKind::Nop => {}
             }
diff --git a/src/librustc_mir/transform/qualify_min_const_fn.rs b/src/librustc_mir/transform/qualify_min_const_fn.rs
index 1e19348595057..ed13063cfdf17 100644
--- a/src/librustc_mir/transform/qualify_min_const_fn.rs
+++ b/src/librustc_mir/transform/qualify_min_const_fn.rs
@@ -242,6 +242,7 @@ fn check_statement(
         | StatementKind::StorageLive(_)
         | StatementKind::StorageDead(_)
         | StatementKind::Retag { .. }
+        | StatementKind::EscapeToRaw { .. }
         | StatementKind::EndRegion(_)
         | StatementKind::AscribeUserType(..)
         | StatementKind::Nop => Ok(()),
diff --git a/src/librustc_mir/transform/remove_noop_landing_pads.rs b/src/librustc_mir/transform/remove_noop_landing_pads.rs
index c1c127fa8d648..445ffbbcf3407 100644
--- a/src/librustc_mir/transform/remove_noop_landing_pads.rs
+++ b/src/librustc_mir/transform/remove_noop_landing_pads.rs
@@ -65,10 +65,11 @@ impl RemoveNoopLandingPads {
                     // turn a landing pad to a non-nop
                 }
 
-                StatementKind::Assign(_, _) |
+                StatementKind::Assign { .. } |
                 StatementKind::SetDiscriminant { .. } |
                 StatementKind::InlineAsm { .. } |
-                StatementKind::Retag { .. } => {
+                StatementKind::Retag { .. } |
+                StatementKind::EscapeToRaw { .. } => {
                     return false;
                 }
             }
diff --git a/src/librustc_mir/transform/rustc_peek.rs b/src/librustc_mir/transform/rustc_peek.rs
index a5a19f04b7e8e..8f026c706fdf7 100644
--- a/src/librustc_mir/transform/rustc_peek.rs
+++ b/src/librustc_mir/transform/rustc_peek.rs
@@ -163,6 +163,7 @@ fn each_block<'a, 'tcx, O>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
             mir::StatementKind::InlineAsm { .. } |
             mir::StatementKind::EndRegion(_) |
             mir::StatementKind::Retag { .. } |
+            mir::StatementKind::EscapeToRaw { .. } |
             mir::StatementKind::AscribeUserType(..) |
             mir::StatementKind::Nop => continue,
             mir::StatementKind::SetDiscriminant{ .. } =>
diff --git a/src/librustc_passes/mir_stats.rs b/src/librustc_passes/mir_stats.rs
index ecfe7d13782de..68840ed4a4804 100644
--- a/src/librustc_passes/mir_stats.rs
+++ b/src/librustc_passes/mir_stats.rs
@@ -85,6 +85,7 @@ impl<'a, 'tcx> mir_visit::Visitor<'tcx> for StatCollector<'a, 'tcx> {
             StatementKind::FakeRead(..) => "StatementKind::FakeRead",
             StatementKind::EndRegion(..) => "StatementKind::EndRegion",
             StatementKind::Retag { .. } => "StatementKind::Retag",
+            StatementKind::EscapeToRaw { .. } => "StatementKind::EscapeToRaw",
             StatementKind::SetDiscriminant { .. } => "StatementKind::SetDiscriminant",
             StatementKind::StorageLive(..) => "StatementKind::StorageLive",
             StatementKind::StorageDead(..) => "StatementKind::StorageDead",
diff --git a/src/test/mir-opt/array-index-is-temporary.rs b/src/test/mir-opt/array-index-is-temporary.rs
new file mode 100644
index 0000000000000..856e1063f6001
--- /dev/null
+++ b/src/test/mir-opt/array-index-is-temporary.rs
@@ -0,0 +1,43 @@
+// Retagging (from Stacked Borrows) relies on the array index being a fresh
+// temporary, so that side-effects cannot change it.
+// Test that this is indeed the case.
+
+unsafe fn foo(z: *mut usize) -> u32 {
+    *z = 2;
+    99
+}
+
+fn main() {
+    let mut x = [42, 43, 44];
+    let mut y = 1;
+    let z: *mut usize = &mut y;
+    x[y] = unsafe { foo(z) };
+}
+
+// END RUST SOURCE
+// START rustc.main.EraseRegions.after.mir
+//     bb0: {
+//         ...
+//         _6 = &mut _2;
+//         _5 = &mut (*_6);
+//         _4 = move _5 as *mut usize (Misc);
+//         _3 = move _4;
+//         ...
+//         _8 = _3;
+//         _7 = const foo(move _8) -> bb1;
+//     }
+//
+//     bb1: {
+//         ...
+//         _9 = _2;
+//         _10 = Len(_1);
+//         _11 = Lt(_9, _10);
+//         assert(move _11, "index out of bounds: the len is move _10 but the index is _9") -> bb2;
+//     }
+//
+//     bb2: {
+//         _1[_9] = move _7;
+//         ...
+//         return;
+//     }
+// END rustc.main.EraseRegions.after.mir
diff --git a/src/test/mir-opt/inline-retag.rs b/src/test/mir-opt/inline-retag.rs
index 4b3280ee56151..1e5e1ad5ed1c5 100644
--- a/src/test/mir-opt/inline-retag.rs
+++ b/src/test/mir-opt/inline-retag.rs
@@ -32,6 +32,8 @@ fn bar() -> bool {
 //     bb0: {
 //         ...
 //         Retag(_3);
+//         ...
+//         Retag(_3);
 //         Retag(_6);
 //         StorageLive(_9);
 //         _9 = (*_3);
diff --git a/src/test/mir-opt/retag.rs b/src/test/mir-opt/retag.rs
index 9c013008ab272..7da55c0868cd2 100644
--- a/src/test/mir-opt/retag.rs
+++ b/src/test/mir-opt/retag.rs
@@ -26,7 +26,9 @@ fn main() {
     {
         let v = Test(0).foo(&mut x); // just making sure we do not panic when there is a tuple struct ctor
         let w = { v }; // assignment
-        let _w = w; // reborrow
+        let w = w; // reborrow
+        // escape-to-raw (mut)
+        let _w = w as *mut _;
     }
 
     // Also test closures
@@ -35,6 +37,9 @@ fn main() {
 
     // need to call `foo_shr` or it doesn't even get generated
     Test(0).foo_shr(&0);
+
+    // escape-to-raw (shr)
+    let _w = _w as *const _;
 }
 
 // END RUST SOURCE
@@ -44,6 +49,7 @@ fn main() {
 //         Retag([fn entry] _2);
 //         ...
 //         _0 = &mut (*_3);
+//         Retag(_0);
 //         ...
 //         return;
 //     }
@@ -73,23 +79,36 @@ fn main() {
 //         _9 = move _3;
 //         Retag(_9);
 //         _8 = &mut (*_9);
+//         Retag(_8);
 //         StorageDead(_9);
 //         StorageLive(_10);
 //         _10 = move _8;
 //         Retag(_10);
 //         ...
-//         _13 = move _14(move _15) -> bb2;
+//         _14 = &mut (*_10);
+//         Retag(_14);
+//         EscapeToRaw(move _14);
+//         _13 = move _14 as *mut i32 (Misc);
+//         ...
+//         _17 = move _18(move _19) -> bb2;
 //     }
 //
 //     bb2: {
-//         Retag(_13);
+//         Retag(_17);
+//         ...
+//         _21 = const Test::foo_shr(move _22, move _24) -> bb3;
+//     }
+//
+//     bb3: {
 //         ...
+//         return;
 //     }
+//
 //     ...
 // }
 // END rustc.main.EraseRegions.after.mir
 // START rustc.main-{{closure}}.EraseRegions.after.mir
-// fn main::{{closure}}(_1: &[closure@NodeId(117)], _2: &i32) -> &i32 {
+// fn main::{{closure}}(_1: &[closure@NodeId(124)], _2: &i32) -> &i32 {
 //     ...
 //     bb0: {
 //         Retag([fn entry] _1);