about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_codegen_gcc/src/debuginfo.rs57
-rw-r--r--compiler/rustc_codegen_llvm/src/debuginfo/mod.rs12
-rw-r--r--compiler/rustc_codegen_llvm/src/llvm/ffi.rs1
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/debuginfo.rs71
-rw-r--r--compiler/rustc_codegen_ssa/src/traits/debuginfo.rs5
-rw-r--r--compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp4
-rw-r--r--compiler/rustc_middle/src/mir/mod.rs53
-rw-r--r--compiler/rustc_middle/src/mir/visit.rs11
-rw-r--r--compiler/rustc_mir_transform/src/lib.rs3
-rw-r--r--compiler/rustc_mir_transform/src/sroa.rs348
-rw-r--r--src/test/mir-opt/const_debuginfo.main.ConstDebugInfo.diff40
-rw-r--r--src/test/mir-opt/const_prop/aggregate.main.PreCodegen.after.mir28
-rw-r--r--src/test/mir-opt/const_prop/aggregate.rs1
-rw-r--r--src/test/mir-opt/const_prop/mutable_variable_unprop_assign.main.ConstProp.diff23
-rw-r--r--src/test/mir-opt/const_prop/optimizes_into_variable.main.ConstProp.32bit.diff17
-rw-r--r--src/test/mir-opt/const_prop/optimizes_into_variable.main.ConstProp.64bit.diff17
-rw-r--r--src/test/mir-opt/const_prop/optimizes_into_variable.main.PreCodegen.after.32bit.mir27
-rw-r--r--src/test/mir-opt/const_prop/optimizes_into_variable.main.PreCodegen.after.64bit.mir27
-rw-r--r--src/test/mir-opt/const_prop/optimizes_into_variable.main.ScalarReplacementOfAggregates.32bit.diff72
-rw-r--r--src/test/mir-opt/const_prop/optimizes_into_variable.main.ScalarReplacementOfAggregates.64bit.diff72
-rw-r--r--src/test/mir-opt/const_prop/optimizes_into_variable.rs2
-rw-r--r--src/test/mir-opt/issue_73223.main.SimplifyArmIdentity.diff19
-rw-r--r--src/test/mir-opt/sroa.dropping.ScalarReplacementOfAggregates.diff50
-rw-r--r--src/test/mir-opt/sroa.enums.ScalarReplacementOfAggregates.diff45
-rw-r--r--src/test/mir-opt/sroa.escaping.ScalarReplacementOfAggregates.diff47
-rw-r--r--src/test/mir-opt/sroa.flat.ScalarReplacementOfAggregates.diff87
-rw-r--r--src/test/mir-opt/sroa.rs88
-rw-r--r--src/test/mir-opt/sroa.structs.ScalarReplacementOfAggregates.diff34
-rw-r--r--src/test/mir-opt/sroa.unions.ScalarReplacementOfAggregates.diff24
29 files changed, 1228 insertions, 57 deletions
diff --git a/compiler/rustc_codegen_gcc/src/debuginfo.rs b/compiler/rustc_codegen_gcc/src/debuginfo.rs
index 266759ed6cf..a81585d4128 100644
--- a/compiler/rustc_codegen_gcc/src/debuginfo.rs
+++ b/compiler/rustc_codegen_gcc/src/debuginfo.rs
@@ -4,8 +4,9 @@ use rustc_codegen_ssa::traits::{DebugInfoBuilderMethods, DebugInfoMethods};
 use rustc_middle::mir;
 use rustc_middle::ty::{Instance, PolyExistentialTraitRef, Ty};
 use rustc_span::{SourceFile, Span, Symbol};
-use rustc_target::abi::Size;
 use rustc_target::abi::call::FnAbi;
+use rustc_target::abi::Size;
+use std::ops::Range;
 
 use crate::builder::Builder;
 use crate::context::CodegenCx;
@@ -13,7 +14,15 @@ use crate::context::CodegenCx;
 impl<'a, 'gcc, 'tcx> DebugInfoBuilderMethods for Builder<'a, 'gcc, 'tcx> {
     // FIXME(eddyb) find a common convention for all of the debuginfo-related
     // names (choose between `dbg`, `debug`, `debuginfo`, `debug_info` etc.).
-    fn dbg_var_addr(&mut self, _dbg_var: Self::DIVariable, _scope_metadata: Self::DIScope, _variable_alloca: Self::Value, _direct_offset: Size, _indirect_offsets: &[Size]) {
+    fn dbg_var_addr(
+        &mut self,
+        _dbg_var: Self::DIVariable,
+        _scope_metadata: Self::DIScope,
+        _variable_alloca: Self::Value,
+        _direct_offset: Size,
+        _indirect_offsets: &[Size],
+        _fragment: Option<Range<Size>>,
+    ) {
         unimplemented!();
     }
 
@@ -31,16 +40,31 @@ impl<'a, 'gcc, 'tcx> DebugInfoBuilderMethods for Builder<'a, 'gcc, 'tcx> {
 }
 
 impl<'gcc, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
-    fn create_vtable_debuginfo(&self, _ty: Ty<'tcx>, _trait_ref: Option<PolyExistentialTraitRef<'tcx>>, _vtable: Self::Value) {
+    fn create_vtable_debuginfo(
+        &self,
+        _ty: Ty<'tcx>,
+        _trait_ref: Option<PolyExistentialTraitRef<'tcx>>,
+        _vtable: Self::Value,
+    ) {
         // TODO(antoyo)
     }
 
-    fn create_function_debug_context(&self, _instance: Instance<'tcx>, _fn_abi: &FnAbi<'tcx, Ty<'tcx>>, _llfn: RValue<'gcc>, _mir: &mir::Body<'tcx>) -> Option<FunctionDebugContext<Self::DIScope, Self::DILocation>> {
+    fn create_function_debug_context(
+        &self,
+        _instance: Instance<'tcx>,
+        _fn_abi: &FnAbi<'tcx, Ty<'tcx>>,
+        _llfn: RValue<'gcc>,
+        _mir: &mir::Body<'tcx>,
+    ) -> Option<FunctionDebugContext<Self::DIScope, Self::DILocation>> {
         // TODO(antoyo)
         None
     }
 
-    fn extend_scope_to_file(&self, _scope_metadata: Self::DIScope, _file: &SourceFile) -> Self::DIScope {
+    fn extend_scope_to_file(
+        &self,
+        _scope_metadata: Self::DIScope,
+        _file: &SourceFile,
+    ) -> Self::DIScope {
         unimplemented!();
     }
 
@@ -48,15 +72,32 @@ impl<'gcc, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
         // TODO(antoyo)
     }
 
-    fn create_dbg_var(&self, _variable_name: Symbol, _variable_type: Ty<'tcx>, _scope_metadata: Self::DIScope, _variable_kind: VariableKind, _span: Span) -> Self::DIVariable {
+    fn create_dbg_var(
+        &self,
+        _variable_name: Symbol,
+        _variable_type: Ty<'tcx>,
+        _scope_metadata: Self::DIScope,
+        _variable_kind: VariableKind,
+        _span: Span,
+    ) -> Self::DIVariable {
         unimplemented!();
     }
 
-    fn dbg_scope_fn(&self, _instance: Instance<'tcx>, _fn_abi: &FnAbi<'tcx, Ty<'tcx>>, _maybe_definition_llfn: Option<RValue<'gcc>>) -> Self::DIScope {
+    fn dbg_scope_fn(
+        &self,
+        _instance: Instance<'tcx>,
+        _fn_abi: &FnAbi<'tcx, Ty<'tcx>>,
+        _maybe_definition_llfn: Option<RValue<'gcc>>,
+    ) -> Self::DIScope {
         unimplemented!();
     }
 
-    fn dbg_loc(&self, _scope: Self::DIScope, _inlined_at: Option<Self::DILocation>, _span: Span) -> Self::DILocation {
+    fn dbg_loc(
+        &self,
+        _scope: Self::DIScope,
+        _inlined_at: Option<Self::DILocation>,
+        _span: Span,
+    ) -> Self::DILocation {
         unimplemented!();
     }
 }
diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs
index b23fe3fc9d5..ca7a07d8391 100644
--- a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs
+++ b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs
@@ -39,6 +39,7 @@ use smallvec::SmallVec;
 use std::cell::OnceCell;
 use std::cell::RefCell;
 use std::iter;
+use std::ops::Range;
 
 mod create_scope_map;
 pub mod gdb;
@@ -163,12 +164,14 @@ impl<'ll> DebugInfoBuilderMethods for Builder<'_, 'll, '_> {
         variable_alloca: Self::Value,
         direct_offset: Size,
         indirect_offsets: &[Size],
+        fragment: Option<Range<Size>>,
     ) {
-        // Convert the direct and indirect offsets to address ops.
+        // Convert the direct and indirect offsets and fragment byte range to address ops.
         // FIXME(eddyb) use `const`s instead of getting the values via FFI,
         // the values should match the ones in the DWARF standard anyway.
         let op_deref = || unsafe { llvm::LLVMRustDIBuilderCreateOpDeref() };
         let op_plus_uconst = || unsafe { llvm::LLVMRustDIBuilderCreateOpPlusUconst() };
+        let op_llvm_fragment = || unsafe { llvm::LLVMRustDIBuilderCreateOpLLVMFragment() };
         let mut addr_ops = SmallVec::<[u64; 8]>::new();
 
         if direct_offset.bytes() > 0 {
@@ -182,6 +185,13 @@ impl<'ll> DebugInfoBuilderMethods for Builder<'_, 'll, '_> {
                 addr_ops.push(offset.bytes() as u64);
             }
         }
+        if let Some(fragment) = fragment {
+            // `DW_OP_LLVM_fragment` takes as arguments the fragment's
+            // offset and size, both of them in bits.
+            addr_ops.push(op_llvm_fragment());
+            addr_ops.push(fragment.start.bits() as u64);
+            addr_ops.push((fragment.end - fragment.start).bits() as u64);
+        }
 
         unsafe {
             // FIXME(eddyb) replace `llvm.dbg.declare` with `llvm.dbg.addr`.
diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs
index e2d0390821d..8f7728da9dd 100644
--- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs
+++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs
@@ -2210,6 +2210,7 @@ extern "C" {
     ) -> &'a DILocation;
     pub fn LLVMRustDIBuilderCreateOpDeref() -> u64;
     pub fn LLVMRustDIBuilderCreateOpPlusUconst() -> u64;
+    pub fn LLVMRustDIBuilderCreateOpLLVMFragment() -> u64;
 
     #[allow(improper_ctypes)]
     pub fn LLVMRustWriteTypeToString(Type: &Type, s: &RustString);
diff --git a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs
index 157c1c82311..99283d3bb29 100644
--- a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs
@@ -14,6 +14,8 @@ use super::operand::{OperandRef, OperandValue};
 use super::place::PlaceRef;
 use super::{FunctionCx, LocalRef};
 
+use std::ops::Range;
+
 pub struct FunctionDebugContext<S, L> {
     pub scopes: IndexVec<mir::SourceScope, DebugScope<S, L>>,
 }
@@ -25,7 +27,7 @@ pub enum VariableKind {
 }
 
 /// Like `mir::VarDebugInfo`, but within a `mir::Local`.
-#[derive(Copy, Clone)]
+#[derive(Clone)]
 pub struct PerLocalVarDebugInfo<'tcx, D> {
     pub name: Symbol,
     pub source_info: mir::SourceInfo,
@@ -33,6 +35,10 @@ pub struct PerLocalVarDebugInfo<'tcx, D> {
     /// `DIVariable` returned by `create_dbg_var`.
     pub dbg_var: Option<D>,
 
+    /// Byte range in the `dbg_var` covered by this fragment,
+    /// if this is a fragment of a composite `VarDebugInfo`.
+    pub fragment: Option<Range<Size>>,
+
     /// `.place.projection` from `mir::VarDebugInfo`.
     pub projection: &'tcx ty::List<mir::PlaceElem<'tcx>>,
 }
@@ -145,7 +151,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
             Some(per_local) => &per_local[local],
             None => return,
         };
-        let whole_local_var = vars.iter().find(|var| var.projection.is_empty()).copied();
+        let whole_local_var = vars.iter().find(|var| var.projection.is_empty()).cloned();
         let has_proj = || vars.iter().any(|var| !var.projection.is_empty());
 
         let fallback_var = if self.mir.local_kind(local) == mir::LocalKind::Arg {
@@ -187,6 +193,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                     name,
                     source_info: decl.source_info,
                     dbg_var,
+                    fragment: None,
                     projection: ty::List::empty(),
                 })
             }
@@ -199,7 +206,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
         let name = if bx.sess().fewer_names() {
             None
         } else {
-            Some(match whole_local_var.or(fallback_var) {
+            Some(match whole_local_var.or(fallback_var.clone()) {
                 Some(var) if var.name != kw::Empty => var.name.to_string(),
                 _ => format!("{:?}", local),
             })
@@ -249,7 +256,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
             LocalRef::UnsizedPlace(_) => return,
         };
 
-        let vars = vars.iter().copied().chain(fallback_var);
+        let vars = vars.iter().cloned().chain(fallback_var);
 
         for var in vars {
             let Some(dbg_var) = var.dbg_var else { continue };
@@ -312,9 +319,16 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                 bx.store(place.llval, alloca.llval, alloca.align);
 
                 // Point the debug info to `*alloca` for the current variable
-                bx.dbg_var_addr(dbg_var, dbg_loc, alloca.llval, Size::ZERO, &[Size::ZERO]);
+                bx.dbg_var_addr(dbg_var, dbg_loc, alloca.llval, Size::ZERO, &[Size::ZERO], None);
             } else {
-                bx.dbg_var_addr(dbg_var, dbg_loc, base.llval, direct_offset, &indirect_offsets);
+                bx.dbg_var_addr(
+                    dbg_var,
+                    dbg_loc,
+                    base.llval,
+                    direct_offset,
+                    &indirect_offsets,
+                    None,
+                );
             }
         }
     }
@@ -382,6 +396,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                         let ty = self.monomorphize(c.ty());
                         (ty, VariableKind::LocalVariable)
                     }
+                    mir::VarDebugInfoContents::Composite { ty, fragments: _ } => {
+                        let ty = self.monomorphize(ty);
+                        (ty, VariableKind::LocalVariable)
+                    }
                 };
 
                 self.cx.create_dbg_var(var.name, var_ty, dbg_scope, var_kind, span)
@@ -393,6 +411,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                         name: var.name,
                         source_info: var.source_info,
                         dbg_var,
+                        fragment: None,
                         projection: place.projection,
                     });
                 }
@@ -407,10 +426,48 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                                 bx,
                             );
 
-                            bx.dbg_var_addr(dbg_var, dbg_loc, base.llval, Size::ZERO, &[]);
+                            bx.dbg_var_addr(dbg_var, dbg_loc, base.llval, Size::ZERO, &[], None);
                         }
                     }
                 }
+                mir::VarDebugInfoContents::Composite { ty, ref fragments } => {
+                    let var_ty = self.monomorphize(ty);
+                    let var_layout = self.cx.layout_of(var_ty);
+                    for fragment in fragments {
+                        let mut fragment_start = Size::ZERO;
+                        let mut fragment_layout = var_layout;
+
+                        for elem in &fragment.projection {
+                            match *elem {
+                                mir::ProjectionElem::Field(field, _) => {
+                                    let i = field.index();
+                                    fragment_start += fragment_layout.fields.offset(i);
+                                    fragment_layout = fragment_layout.field(self.cx, i);
+                                }
+                                _ => span_bug!(
+                                    var.source_info.span,
+                                    "unsupported fragment projection `{:?}`",
+                                    elem,
+                                ),
+                            }
+                        }
+
+                        let place = fragment.contents;
+                        per_local[place.local].push(PerLocalVarDebugInfo {
+                            name: var.name,
+                            source_info: var.source_info,
+                            dbg_var,
+                            fragment: if fragment_layout.size == var_layout.size {
+                                // Fragment covers entire variable, so as far as
+                                // DWARF is concerned, it's not really a fragment.
+                                None
+                            } else {
+                                Some(fragment_start..fragment_start + fragment_layout.size)
+                            },
+                            projection: place.projection,
+                        });
+                    }
+                }
             }
         }
         Some(per_local)
diff --git a/compiler/rustc_codegen_ssa/src/traits/debuginfo.rs b/compiler/rustc_codegen_ssa/src/traits/debuginfo.rs
index f310789d144..63fecaf34fd 100644
--- a/compiler/rustc_codegen_ssa/src/traits/debuginfo.rs
+++ b/compiler/rustc_codegen_ssa/src/traits/debuginfo.rs
@@ -6,6 +6,8 @@ use rustc_span::{SourceFile, Span, Symbol};
 use rustc_target::abi::call::FnAbi;
 use rustc_target::abi::Size;
 
+use std::ops::Range;
+
 pub trait DebugInfoMethods<'tcx>: BackendTypes {
     fn create_vtable_debuginfo(
         &self,
@@ -72,6 +74,9 @@ pub trait DebugInfoBuilderMethods: BackendTypes {
         direct_offset: Size,
         // NB: each offset implies a deref (i.e. they're steps in a pointer chain).
         indirect_offsets: &[Size],
+        // Byte range in the `dbg_var` covered by this fragment,
+        // if this is a fragment of a composite `DIVariable`.
+        fragment: Option<Range<Size>>,
     );
     fn set_dbg_loc(&mut self, dbg_loc: Self::DILocation);
     fn insert_reference_to_gdb_debug_scripts_section_global(&mut self);
diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
index 85c520a7911..6f8bb676104 100644
--- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
+++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
@@ -1111,6 +1111,10 @@ extern "C" uint64_t LLVMRustDIBuilderCreateOpPlusUconst() {
   return dwarf::DW_OP_plus_uconst;
 }
 
+extern "C" int64_t LLVMRustDIBuilderCreateOpLLVMFragment() {
+  return dwarf::DW_OP_LLVM_fragment;
+}
+
 extern "C" void LLVMRustWriteTypeToString(LLVMTypeRef Ty, RustStringRef Str) {
   RawRustStringOstream OS(Str);
   unwrap<llvm::Type>(Ty)->print(OS);
diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs
index 4781651071d..12e9ebfeaec 100644
--- a/compiler/rustc_middle/src/mir/mod.rs
+++ b/compiler/rustc_middle/src/mir/mod.rs
@@ -1071,6 +1071,18 @@ pub enum VarDebugInfoContents<'tcx> {
     /// based on a `Local`, not a `Static`, and contains no indexing.
     Place(Place<'tcx>),
     Const(Constant<'tcx>),
+    /// The user variable's data is split across several fragments,
+    /// each described by a `VarDebugInfoFragment`.
+    /// See DWARF 5's "2.6.1.2 Composite Location Descriptions"
+    /// and LLVM's `DW_OP_LLVM_fragment` for more details on
+    /// the underlying debuginfo feature this relies on.
+    Composite {
+        /// Type of the original user variable.
+        ty: Ty<'tcx>,
+        /// All the parts of the original user variable, which ended
+        /// up in disjoint places, due to optimizations.
+        fragments: Vec<VarDebugInfoFragment<'tcx>>,
+    },
 }
 
 impl<'tcx> Debug for VarDebugInfoContents<'tcx> {
@@ -1078,7 +1090,48 @@ impl<'tcx> Debug for VarDebugInfoContents<'tcx> {
         match self {
             VarDebugInfoContents::Const(c) => write!(fmt, "{}", c),
             VarDebugInfoContents::Place(p) => write!(fmt, "{:?}", p),
+            VarDebugInfoContents::Composite { ty, fragments } => {
+                write!(fmt, "{:?}{{ ", ty)?;
+                for f in fragments.iter() {
+                    write!(fmt, "{:?}, ", f)?;
+                }
+                write!(fmt, "}}")
+            }
+        }
+    }
+}
+
+#[derive(Clone, TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable)]
+pub struct VarDebugInfoFragment<'tcx> {
+    /// Where in the composite user variable this fragment is,
+    /// represented as a "projection" into the composite variable.
+    /// At lower levels, this corresponds to a byte/bit range.
+    // NOTE(eddyb) there's an unenforced invariant that this contains
+    // only `Field`s, and not into `enum` variants or `union`s.
+    // FIXME(eddyb) support this for `enum`s by either using DWARF's
+    // more advanced control-flow features (unsupported by LLVM?)
+    // to match on the discriminant, or by using custom type debuginfo
+    // with non-overlapping variants for the composite variable.
+    pub projection: Vec<PlaceElem<'tcx>>,
+
+    /// Where the data for this fragment can be found.
+    // NOTE(eddyb) There's an unenforced invariant that this `Place` is
+    // contains no indexing (with a non-constant index).
+    pub contents: Place<'tcx>,
+}
+
+impl Debug for VarDebugInfoFragment<'_> {
+    fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
+        for elem in self.projection.iter() {
+            match elem {
+                ProjectionElem::Field(field, _) => {
+                    write!(fmt, ".{:?}", field.index())?;
+                }
+                _ => bug!("unsupported fragment projection `{:?}`", elem),
+            }
         }
+
+        write!(fmt, " => {:?}", self.contents)
     }
 }
 
diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs
index d87eb28970e..b21f50ae5ea 100644
--- a/compiler/rustc_middle/src/mir/visit.rs
+++ b/compiler/rustc_middle/src/mir/visit.rs
@@ -847,6 +847,17 @@ macro_rules! make_mir_visitor {
                             PlaceContext::NonUse(NonUseContext::VarDebugInfo),
                             location
                         ),
+                    VarDebugInfoContents::Composite { ty, fragments } => {
+                        // FIXME(eddyb) use a better `TyContext` here.
+                        self.visit_ty($(& $mutability)? *ty, TyContext::Location(location));
+                        for VarDebugInfoFragment { projection: _, contents } in fragments {
+                            self.visit_place(
+                                contents,
+                                PlaceContext::NonUse(NonUseContext::VarDebugInfo),
+                                location,
+                            );
+                        }
+                    }
                 }
             }
 
diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs
index 692eeddfb98..5e85d1f0db4 100644
--- a/compiler/rustc_mir_transform/src/lib.rs
+++ b/compiler/rustc_mir_transform/src/lib.rs
@@ -1,5 +1,6 @@
 #![allow(rustc::potential_query_instability)]
 #![feature(box_patterns)]
+#![feature(drain_filter)]
 #![feature(let_chains)]
 #![feature(map_try_insert)]
 #![feature(min_specialization)]
@@ -92,6 +93,7 @@ pub mod simplify;
 mod simplify_branches;
 mod simplify_comparison_integral;
 mod simplify_try;
+mod sroa;
 mod uninhabited_enum_branching;
 mod unreachable_prop;
 
@@ -562,6 +564,7 @@ fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
             &remove_zsts::RemoveZsts,
             &const_goto::ConstGoto,
             &remove_unneeded_drops::RemoveUnneededDrops,
+            &sroa::ScalarReplacementOfAggregates,
             &match_branches::MatchBranchSimplification,
             // inst combine is after MatchBranchSimplification to clean up Ne(_1, false)
             &multiple_return_terminators::MultipleReturnTerminators,
diff --git a/compiler/rustc_mir_transform/src/sroa.rs b/compiler/rustc_mir_transform/src/sroa.rs
new file mode 100644
index 00000000000..558a372fb1e
--- /dev/null
+++ b/compiler/rustc_mir_transform/src/sroa.rs
@@ -0,0 +1,348 @@
+use crate::MirPass;
+use rustc_data_structures::fx::{FxIndexMap, IndexEntry};
+use rustc_index::bit_set::BitSet;
+use rustc_index::vec::IndexVec;
+use rustc_middle::mir::visit::*;
+use rustc_middle::mir::*;
+use rustc_middle::ty::TyCtxt;
+
+pub struct ScalarReplacementOfAggregates;
+
+impl<'tcx> MirPass<'tcx> for ScalarReplacementOfAggregates {
+    fn is_enabled(&self, sess: &rustc_session::Session) -> bool {
+        sess.mir_opt_level() >= 3
+    }
+
+    fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
+        let escaping = escaping_locals(&*body);
+        debug!(?escaping);
+        let replacements = compute_flattening(tcx, body, escaping);
+        debug!(?replacements);
+        replace_flattened_locals(tcx, body, replacements);
+    }
+}
+
+/// Identify all locals that are not eligible for SROA.
+///
+/// There are 3 cases:
+/// - the aggegated local is used or passed to other code (function parameters and arguments);
+/// - the locals is a union or an enum;
+/// - the local's address is taken, and thus the relative addresses of the fields are observable to
+///   client code.
+fn escaping_locals(body: &Body<'_>) -> BitSet<Local> {
+    let mut set = BitSet::new_empty(body.local_decls.len());
+    set.insert_range(RETURN_PLACE..=Local::from_usize(body.arg_count));
+    for (local, decl) in body.local_decls().iter_enumerated() {
+        if decl.ty.is_union() || decl.ty.is_enum() {
+            set.insert(local);
+        }
+    }
+    let mut visitor = EscapeVisitor { set };
+    visitor.visit_body(body);
+    return visitor.set;
+
+    struct EscapeVisitor {
+        set: BitSet<Local>,
+    }
+
+    impl<'tcx> Visitor<'tcx> for EscapeVisitor {
+        fn visit_local(&mut self, local: Local, _: PlaceContext, _: Location) {
+            self.set.insert(local);
+        }
+
+        fn visit_place(&mut self, place: &Place<'tcx>, context: PlaceContext, location: Location) {
+            // Mirror the implementation in PreFlattenVisitor.
+            if let &[PlaceElem::Field(..), ..] = &place.projection[..] {
+                return;
+            }
+            self.super_place(place, context, location);
+        }
+
+        fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
+            if let Rvalue::AddressOf(.., place) | Rvalue::Ref(.., place) = rvalue {
+                if !place.is_indirect() {
+                    // Raw pointers may be used to access anything inside the enclosing place.
+                    self.set.insert(place.local);
+                    return;
+                }
+            }
+            self.super_rvalue(rvalue, location)
+        }
+
+        fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) {
+            if let StatementKind::StorageLive(..)
+            | StatementKind::StorageDead(..)
+            | StatementKind::Deinit(..) = statement.kind
+            {
+                // Storage statements are expanded in run_pass.
+                return;
+            }
+            self.super_statement(statement, location)
+        }
+
+        fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
+            // Drop implicitly calls `drop_in_place`, which takes a `&mut`.
+            // This implies that `Drop` implicitly takes the address of the place.
+            if let TerminatorKind::Drop { place, .. }
+            | TerminatorKind::DropAndReplace { place, .. } = terminator.kind
+            {
+                if !place.is_indirect() {
+                    // Raw pointers may be used to access anything inside the enclosing place.
+                    self.set.insert(place.local);
+                    return;
+                }
+            }
+            self.super_terminator(terminator, location);
+        }
+
+        // We ignore anything that happens in debuginfo, since we expand it using
+        // `VarDebugInfoContents::Composite`.
+        fn visit_var_debug_info(&mut self, _: &VarDebugInfo<'tcx>) {}
+    }
+}
+
+#[derive(Default, Debug)]
+struct ReplacementMap<'tcx> {
+    fields: FxIndexMap<PlaceRef<'tcx>, Local>,
+}
+
+/// Compute the replacement of flattened places into locals.
+///
+/// For each eligible place, we assign a new local to each accessed field.
+/// The replacement will be done later in `ReplacementVisitor`.
+fn compute_flattening<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    body: &mut Body<'tcx>,
+    escaping: BitSet<Local>,
+) -> ReplacementMap<'tcx> {
+    let mut visitor = PreFlattenVisitor {
+        tcx,
+        escaping,
+        local_decls: &mut body.local_decls,
+        map: Default::default(),
+    };
+    for (block, bbdata) in body.basic_blocks.iter_enumerated() {
+        visitor.visit_basic_block_data(block, bbdata);
+    }
+    return visitor.map;
+
+    struct PreFlattenVisitor<'tcx, 'll> {
+        tcx: TyCtxt<'tcx>,
+        local_decls: &'ll mut LocalDecls<'tcx>,
+        escaping: BitSet<Local>,
+        map: ReplacementMap<'tcx>,
+    }
+
+    impl<'tcx, 'll> PreFlattenVisitor<'tcx, 'll> {
+        fn create_place(&mut self, place: PlaceRef<'tcx>) {
+            if self.escaping.contains(place.local) {
+                return;
+            }
+
+            match self.map.fields.entry(place) {
+                IndexEntry::Occupied(_) => {}
+                IndexEntry::Vacant(v) => {
+                    let ty = place.ty(&*self.local_decls, self.tcx).ty;
+                    let local = self.local_decls.push(LocalDecl {
+                        ty,
+                        user_ty: None,
+                        ..self.local_decls[place.local].clone()
+                    });
+                    v.insert(local);
+                }
+            }
+        }
+    }
+
+    impl<'tcx, 'll> Visitor<'tcx> for PreFlattenVisitor<'tcx, 'll> {
+        fn visit_place(&mut self, place: &Place<'tcx>, _: PlaceContext, _: Location) {
+            if let &[PlaceElem::Field(..), ..] = &place.projection[..] {
+                let pr = PlaceRef { local: place.local, projection: &place.projection[..1] };
+                self.create_place(pr)
+            }
+        }
+    }
+}
+
+/// Perform the replacement computed by `compute_flattening`.
+fn replace_flattened_locals<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    body: &mut Body<'tcx>,
+    replacements: ReplacementMap<'tcx>,
+) {
+    let mut all_dead_locals = BitSet::new_empty(body.local_decls.len());
+    for p in replacements.fields.keys() {
+        all_dead_locals.insert(p.local);
+    }
+    debug!(?all_dead_locals);
+    if all_dead_locals.is_empty() {
+        return;
+    }
+
+    let mut fragments = IndexVec::new();
+    for (k, v) in &replacements.fields {
+        fragments.ensure_contains_elem(k.local, || Vec::new());
+        fragments[k.local].push((&k.projection[..], *v));
+    }
+    debug!(?fragments);
+
+    let mut visitor = ReplacementVisitor {
+        tcx,
+        local_decls: &body.local_decls,
+        replacements,
+        all_dead_locals,
+        fragments,
+    };
+    for (bb, data) in body.basic_blocks.as_mut_preserves_cfg().iter_enumerated_mut() {
+        visitor.visit_basic_block_data(bb, data);
+    }
+    for scope in &mut body.source_scopes {
+        visitor.visit_source_scope_data(scope);
+    }
+    for (index, annotation) in body.user_type_annotations.iter_enumerated_mut() {
+        visitor.visit_user_type_annotation(index, annotation);
+    }
+    for var_debug_info in &mut body.var_debug_info {
+        visitor.visit_var_debug_info(var_debug_info);
+    }
+}
+
+struct ReplacementVisitor<'tcx, 'll> {
+    tcx: TyCtxt<'tcx>,
+    /// This is only used to compute the type for `VarDebugInfoContents::Composite`.
+    local_decls: &'ll LocalDecls<'tcx>,
+    /// Work to do.
+    replacements: ReplacementMap<'tcx>,
+    /// This is used to check that we are not leaving references to replaced locals behind.
+    all_dead_locals: BitSet<Local>,
+    /// Pre-computed list of all "new" locals for each "old" local.  This is used to expand storage
+    /// and deinit statement and debuginfo.
+    fragments: IndexVec<Local, Vec<(&'tcx [PlaceElem<'tcx>], Local)>>,
+}
+
+impl<'tcx, 'll> ReplacementVisitor<'tcx, 'll> {
+    fn gather_debug_info_fragments(
+        &self,
+        place: PlaceRef<'tcx>,
+    ) -> Vec<VarDebugInfoFragment<'tcx>> {
+        let mut fragments = Vec::new();
+        let parts = &self.fragments[place.local];
+        for (proj, replacement_local) in parts {
+            if proj.starts_with(place.projection) {
+                fragments.push(VarDebugInfoFragment {
+                    projection: proj[place.projection.len()..].to_vec(),
+                    contents: Place::from(*replacement_local),
+                });
+            }
+        }
+        fragments
+    }
+
+    fn replace_place(&self, place: PlaceRef<'tcx>) -> Option<Place<'tcx>> {
+        if let &[PlaceElem::Field(..), ref rest @ ..] = place.projection {
+            let pr = PlaceRef { local: place.local, projection: &place.projection[..1] };
+            let local = self.replacements.fields.get(&pr)?;
+            Some(Place { local: *local, projection: self.tcx.intern_place_elems(&rest) })
+        } else {
+            None
+        }
+    }
+}
+
+impl<'tcx, 'll> MutVisitor<'tcx> for ReplacementVisitor<'tcx, 'll> {
+    fn tcx(&self) -> TyCtxt<'tcx> {
+        self.tcx
+    }
+
+    fn visit_statement(&mut self, statement: &mut Statement<'tcx>, location: Location) {
+        if let StatementKind::StorageLive(..)
+        | StatementKind::StorageDead(..)
+        | StatementKind::Deinit(..) = statement.kind
+        {
+            // Storage statements are expanded in run_pass.
+            return;
+        }
+        self.super_statement(statement, location)
+    }
+
+    fn visit_place(&mut self, place: &mut Place<'tcx>, context: PlaceContext, location: Location) {
+        if let Some(repl) = self.replace_place(place.as_ref()) {
+            *place = repl
+        } else {
+            self.super_place(place, context, location)
+        }
+    }
+
+    fn visit_var_debug_info(&mut self, var_debug_info: &mut VarDebugInfo<'tcx>) {
+        match &mut var_debug_info.value {
+            VarDebugInfoContents::Place(ref mut place) => {
+                if let Some(repl) = self.replace_place(place.as_ref()) {
+                    *place = repl;
+                } else if self.all_dead_locals.contains(place.local) {
+                    let ty = place.ty(self.local_decls, self.tcx).ty;
+                    let fragments = self.gather_debug_info_fragments(place.as_ref());
+                    var_debug_info.value = VarDebugInfoContents::Composite { ty, fragments };
+                }
+            }
+            VarDebugInfoContents::Composite { ty: _, ref mut fragments } => {
+                let mut new_fragments = Vec::new();
+                fragments
+                    .drain_filter(|fragment| {
+                        if let Some(repl) = self.replace_place(fragment.contents.as_ref()) {
+                            fragment.contents = repl;
+                            true
+                        } else if self.all_dead_locals.contains(fragment.contents.local) {
+                            let frg = self.gather_debug_info_fragments(fragment.contents.as_ref());
+                            new_fragments.extend(frg.into_iter().map(|mut f| {
+                                f.projection.splice(0..0, fragment.projection.iter().copied());
+                                f
+                            }));
+                            false
+                        } else {
+                            true
+                        }
+                    })
+                    .for_each(drop);
+                fragments.extend(new_fragments);
+            }
+            VarDebugInfoContents::Const(_) => {}
+        }
+    }
+
+    fn visit_basic_block_data(&mut self, bb: BasicBlock, bbdata: &mut BasicBlockData<'tcx>) {
+        self.super_basic_block_data(bb, bbdata);
+
+        #[derive(Debug)]
+        enum Stmt {
+            StorageLive,
+            StorageDead,
+            Deinit,
+        }
+
+        bbdata.expand_statements(|stmt| {
+            let source_info = stmt.source_info;
+            let (stmt, origin_local) = match &stmt.kind {
+                StatementKind::StorageLive(l) => (Stmt::StorageLive, *l),
+                StatementKind::StorageDead(l) => (Stmt::StorageDead, *l),
+                StatementKind::Deinit(p) if let Some(l) = p.as_local() => (Stmt::Deinit, l),
+                _ => return None,
+            };
+            if !self.all_dead_locals.contains(origin_local) {
+                return None;
+            }
+            let final_locals = self.fragments.get(origin_local)?;
+            Some(final_locals.iter().map(move |&(_, l)| {
+                let kind = match stmt {
+                    Stmt::StorageLive => StatementKind::StorageLive(l),
+                    Stmt::StorageDead => StatementKind::StorageDead(l),
+                    Stmt::Deinit => StatementKind::Deinit(Box::new(l.into())),
+                };
+                Statement { source_info, kind }
+            }))
+        });
+    }
+
+    fn visit_local(&mut self, local: &mut Local, _: PlaceContext, _: Location) {
+        assert!(!self.all_dead_locals.contains(*local));
+    }
+}
diff --git a/src/test/mir-opt/const_debuginfo.main.ConstDebugInfo.diff b/src/test/mir-opt/const_debuginfo.main.ConstDebugInfo.diff
index a092f375291..e959e1b2f2c 100644
--- a/src/test/mir-opt/const_debuginfo.main.ConstDebugInfo.diff
+++ b/src/test/mir-opt/const_debuginfo.main.ConstDebugInfo.diff
@@ -30,14 +30,19 @@
 -                         debug s => _9;   // in scope 5 at $DIR/const_debuginfo.rs:+6:9: +6:10
 +                         debug s => const "hello, world!"; // in scope 5 at $DIR/const_debuginfo.rs:+6:9: +6:10
                           let _10: (bool, bool, u32); // in scope 5 at $DIR/const_debuginfo.rs:+8:9: +8:10
+                          let _16: bool;   // in scope 5 at $DIR/const_debuginfo.rs:+8:9: +8:10
+                          let _17: bool;   // in scope 5 at $DIR/const_debuginfo.rs:+8:9: +8:10
+                          let _18: u32;    // in scope 5 at $DIR/const_debuginfo.rs:+8:9: +8:10
                           scope 6 {
-                              debug f => _10; // in scope 6 at $DIR/const_debuginfo.rs:+8:9: +8:10
+                              debug f => (bool, bool, u32){ .0 => _16, .1 => _17, .2 => _18, }; // in scope 6 at $DIR/const_debuginfo.rs:+8:9: +8:10
                               let _11: std::option::Option<u16>; // in scope 6 at $DIR/const_debuginfo.rs:+10:9: +10:10
                               scope 7 {
                                   debug o => _11; // in scope 7 at $DIR/const_debuginfo.rs:+10:9: +10:10
                                   let _12: Point; // in scope 7 at $DIR/const_debuginfo.rs:+12:9: +12:10
+                                  let _19: u32; // in scope 7 at $DIR/const_debuginfo.rs:+12:9: +12:10
+                                  let _20: u32; // in scope 7 at $DIR/const_debuginfo.rs:+12:9: +12:10
                                   scope 8 {
-                                      debug p => _12; // in scope 8 at $DIR/const_debuginfo.rs:+12:9: +12:10
+                                      debug p => Point{ .0 => _19, .1 => _20, }; // in scope 8 at $DIR/const_debuginfo.rs:+12:9: +12:10
                                       let _13: u32; // in scope 8 at $DIR/const_debuginfo.rs:+13:9: +13:10
                                       scope 9 {
 -                                         debug a => _13; // in scope 9 at $DIR/const_debuginfo.rs:+13:9: +13:10
@@ -78,19 +83,25 @@
                                            // mir::Constant
                                            // + span: $DIR/const_debuginfo.rs:14:13: 14:28
                                            // + literal: Const { ty: &str, val: Value(Slice(..)) }
-          StorageLive(_10);                // scope 5 at $DIR/const_debuginfo.rs:+8:9: +8:10
-          Deinit(_10);                     // scope 5 at $DIR/const_debuginfo.rs:+8:13: +8:34
-          (_10.0: bool) = const true;      // scope 5 at $DIR/const_debuginfo.rs:+8:13: +8:34
-          (_10.1: bool) = const false;     // scope 5 at $DIR/const_debuginfo.rs:+8:13: +8:34
-          (_10.2: u32) = const 123_u32;    // scope 5 at $DIR/const_debuginfo.rs:+8:13: +8:34
+          StorageLive(_16);                // scope 5 at $DIR/const_debuginfo.rs:+8:9: +8:10
+          StorageLive(_17);                // scope 5 at $DIR/const_debuginfo.rs:+8:9: +8:10
+          StorageLive(_18);                // scope 5 at $DIR/const_debuginfo.rs:+8:9: +8:10
+          Deinit(_16);                     // scope 5 at $DIR/const_debuginfo.rs:+8:13: +8:34
+          Deinit(_17);                     // scope 5 at $DIR/const_debuginfo.rs:+8:13: +8:34
+          Deinit(_18);                     // scope 5 at $DIR/const_debuginfo.rs:+8:13: +8:34
+          _16 = const true;                // scope 5 at $DIR/const_debuginfo.rs:+8:13: +8:34
+          _17 = const false;               // scope 5 at $DIR/const_debuginfo.rs:+8:13: +8:34
+          _18 = const 123_u32;             // scope 5 at $DIR/const_debuginfo.rs:+8:13: +8:34
           StorageLive(_11);                // scope 6 at $DIR/const_debuginfo.rs:+10:9: +10:10
           Deinit(_11);                     // scope 6 at $DIR/const_debuginfo.rs:+10:13: +10:24
           ((_11 as Some).0: u16) = const 99_u16; // scope 6 at $DIR/const_debuginfo.rs:+10:13: +10:24
           discriminant(_11) = 1;           // scope 6 at $DIR/const_debuginfo.rs:+10:13: +10:24
-          StorageLive(_12);                // scope 7 at $DIR/const_debuginfo.rs:+12:9: +12:10
-          Deinit(_12);                     // scope 7 at $DIR/const_debuginfo.rs:+12:13: +12:35
-          (_12.0: u32) = const 32_u32;     // scope 7 at $DIR/const_debuginfo.rs:+12:13: +12:35
-          (_12.1: u32) = const 32_u32;     // scope 7 at $DIR/const_debuginfo.rs:+12:13: +12:35
+          StorageLive(_19);                // scope 7 at $DIR/const_debuginfo.rs:+12:9: +12:10
+          StorageLive(_20);                // scope 7 at $DIR/const_debuginfo.rs:+12:9: +12:10
+          Deinit(_19);                     // scope 7 at $DIR/const_debuginfo.rs:+12:13: +12:35
+          Deinit(_20);                     // scope 7 at $DIR/const_debuginfo.rs:+12:13: +12:35
+          _19 = const 32_u32;              // scope 7 at $DIR/const_debuginfo.rs:+12:13: +12:35
+          _20 = const 32_u32;              // scope 7 at $DIR/const_debuginfo.rs:+12:13: +12:35
           StorageLive(_13);                // scope 8 at $DIR/const_debuginfo.rs:+13:9: +13:10
           StorageLive(_14);                // scope 8 at $DIR/const_debuginfo.rs:+13:13: +13:16
           _14 = const 32_u32;              // scope 8 at $DIR/const_debuginfo.rs:+13:13: +13:16
@@ -101,9 +112,12 @@
           StorageDead(_14);                // scope 8 at $DIR/const_debuginfo.rs:+13:21: +13:22
           nop;                             // scope 0 at $DIR/const_debuginfo.rs:+0:11: +14:2
           StorageDead(_13);                // scope 8 at $DIR/const_debuginfo.rs:+14:1: +14:2
-          StorageDead(_12);                // scope 7 at $DIR/const_debuginfo.rs:+14:1: +14:2
+          StorageDead(_19);                // scope 7 at $DIR/const_debuginfo.rs:+14:1: +14:2
+          StorageDead(_20);                // scope 7 at $DIR/const_debuginfo.rs:+14:1: +14:2
           StorageDead(_11);                // scope 6 at $DIR/const_debuginfo.rs:+14:1: +14:2
-          StorageDead(_10);                // scope 5 at $DIR/const_debuginfo.rs:+14:1: +14:2
+          StorageDead(_16);                // scope 5 at $DIR/const_debuginfo.rs:+14:1: +14:2
+          StorageDead(_17);                // scope 5 at $DIR/const_debuginfo.rs:+14:1: +14:2
+          StorageDead(_18);                // scope 5 at $DIR/const_debuginfo.rs:+14:1: +14:2
           StorageDead(_9);                 // scope 4 at $DIR/const_debuginfo.rs:+14:1: +14:2
           StorageDead(_4);                 // scope 3 at $DIR/const_debuginfo.rs:+14:1: +14:2
           StorageDead(_3);                 // scope 2 at $DIR/const_debuginfo.rs:+14:1: +14:2
diff --git a/src/test/mir-opt/const_prop/aggregate.main.PreCodegen.after.mir b/src/test/mir-opt/const_prop/aggregate.main.PreCodegen.after.mir
new file mode 100644
index 00000000000..cfc9a72e3b2
--- /dev/null
+++ b/src/test/mir-opt/const_prop/aggregate.main.PreCodegen.after.mir
@@ -0,0 +1,28 @@
+// MIR for `main` after PreCodegen
+
+fn main() -> () {
+    let mut _0: ();                      // return place in scope 0 at $DIR/aggregate.rs:+0:11: +0:11
+    let _1: i32;                         // in scope 0 at $DIR/aggregate.rs:+1:9: +1:10
+    let mut _2: i32;                     // in scope 0 at $DIR/aggregate.rs:+1:13: +1:24
+    let mut _3: (i32, i32, i32);         // in scope 0 at $DIR/aggregate.rs:+1:13: +1:22
+    scope 1 {
+        debug x => _1;                   // in scope 1 at $DIR/aggregate.rs:+1:9: +1:10
+    }
+
+    bb0: {
+        StorageLive(_1);                 // scope 0 at $DIR/aggregate.rs:+1:9: +1:10
+        StorageLive(_2);                 // scope 0 at $DIR/aggregate.rs:+1:13: +1:24
+        StorageLive(_3);                 // scope 0 at $DIR/aggregate.rs:+1:13: +1:22
+        Deinit(_3);                      // scope 0 at $DIR/aggregate.rs:+1:13: +1:22
+        (_3.0: i32) = const 0_i32;       // scope 0 at $DIR/aggregate.rs:+1:13: +1:22
+        (_3.1: i32) = const 1_i32;       // scope 0 at $DIR/aggregate.rs:+1:13: +1:22
+        (_3.2: i32) = const 2_i32;       // scope 0 at $DIR/aggregate.rs:+1:13: +1:22
+        _2 = const 1_i32;                // scope 0 at $DIR/aggregate.rs:+1:13: +1:24
+        _1 = const 1_i32;                // scope 0 at $DIR/aggregate.rs:+1:13: +1:28
+        StorageDead(_2);                 // scope 0 at $DIR/aggregate.rs:+1:27: +1:28
+        StorageDead(_3);                 // scope 0 at $DIR/aggregate.rs:+1:28: +1:29
+        _0 = const ();                   // scope 0 at $DIR/aggregate.rs:+0:11: +2:2
+        StorageDead(_1);                 // scope 0 at $DIR/aggregate.rs:+2:1: +2:2
+        return;                          // scope 0 at $DIR/aggregate.rs:+2:2: +2:2
+    }
+}
diff --git a/src/test/mir-opt/const_prop/aggregate.rs b/src/test/mir-opt/const_prop/aggregate.rs
index 493d0508a04..6a3080384da 100644
--- a/src/test/mir-opt/const_prop/aggregate.rs
+++ b/src/test/mir-opt/const_prop/aggregate.rs
@@ -2,6 +2,7 @@
 // compile-flags: -O
 
 // EMIT_MIR aggregate.main.ConstProp.diff
+// EMIT_MIR aggregate.main.PreCodegen.after.mir
 fn main() {
     let x = (0, 1, 2).1 + 0;
 }
diff --git a/src/test/mir-opt/const_prop/mutable_variable_unprop_assign.main.ConstProp.diff b/src/test/mir-opt/const_prop/mutable_variable_unprop_assign.main.ConstProp.diff
index 186a9537356..2e4b0e79e9f 100644
--- a/src/test/mir-opt/const_prop/mutable_variable_unprop_assign.main.ConstProp.diff
+++ b/src/test/mir-opt/const_prop/mutable_variable_unprop_assign.main.ConstProp.diff
@@ -8,8 +8,10 @@
       scope 1 {
           debug a => _1;                   // in scope 1 at $DIR/mutable_variable_unprop_assign.rs:+1:9: +1:10
           let mut _2: (i32, i32);          // in scope 1 at $DIR/mutable_variable_unprop_assign.rs:+2:9: +2:14
+          let mut _6: i32;                 // in scope 1 at $DIR/mutable_variable_unprop_assign.rs:+2:9: +2:14
+          let mut _7: i32;                 // in scope 1 at $DIR/mutable_variable_unprop_assign.rs:+2:9: +2:14
           scope 2 {
-              debug x => _2;               // in scope 2 at $DIR/mutable_variable_unprop_assign.rs:+2:9: +2:14
+              debug x => (i32, i32){ .0 => _6, .1 => _7, }; // in scope 2 at $DIR/mutable_variable_unprop_assign.rs:+2:9: +2:14
               let _4: i32;                 // in scope 2 at $DIR/mutable_variable_unprop_assign.rs:+4:9: +4:10
               scope 3 {
                   debug y => _4;           // in scope 3 at $DIR/mutable_variable_unprop_assign.rs:+4:9: +4:10
@@ -30,23 +32,26 @@
       }
   
       bb1: {
-          StorageLive(_2);                 // scope 1 at $DIR/mutable_variable_unprop_assign.rs:+2:9: +2:14
-          Deinit(_2);                      // scope 1 at $DIR/mutable_variable_unprop_assign.rs:+2:29: +2:35
-          (_2.0: i32) = const 1_i32;       // scope 1 at $DIR/mutable_variable_unprop_assign.rs:+2:29: +2:35
-          (_2.1: i32) = const 2_i32;       // scope 1 at $DIR/mutable_variable_unprop_assign.rs:+2:29: +2:35
+          StorageLive(_6);                 // scope 1 at $DIR/mutable_variable_unprop_assign.rs:+2:9: +2:14
+          StorageLive(_7);                 // scope 1 at $DIR/mutable_variable_unprop_assign.rs:+2:9: +2:14
+          Deinit(_6);                      // scope 1 at $DIR/mutable_variable_unprop_assign.rs:+2:29: +2:35
+          Deinit(_7);                      // scope 1 at $DIR/mutable_variable_unprop_assign.rs:+2:29: +2:35
+          _6 = const 1_i32;                // scope 1 at $DIR/mutable_variable_unprop_assign.rs:+2:29: +2:35
+          _7 = const 2_i32;                // scope 1 at $DIR/mutable_variable_unprop_assign.rs:+2:29: +2:35
           StorageLive(_3);                 // scope 2 at $DIR/mutable_variable_unprop_assign.rs:+3:11: +3:12
           _3 = _1;                         // scope 2 at $DIR/mutable_variable_unprop_assign.rs:+3:11: +3:12
-          (_2.1: i32) = move _3;           // scope 2 at $DIR/mutable_variable_unprop_assign.rs:+3:5: +3:12
+          _7 = move _3;                    // scope 2 at $DIR/mutable_variable_unprop_assign.rs:+3:5: +3:12
           StorageDead(_3);                 // scope 2 at $DIR/mutable_variable_unprop_assign.rs:+3:11: +3:12
           StorageLive(_4);                 // scope 2 at $DIR/mutable_variable_unprop_assign.rs:+4:9: +4:10
-          _4 = (_2.1: i32);                // scope 2 at $DIR/mutable_variable_unprop_assign.rs:+4:13: +4:16
+          _4 = _7;                         // scope 2 at $DIR/mutable_variable_unprop_assign.rs:+4:13: +4:16
           StorageLive(_5);                 // scope 3 at $DIR/mutable_variable_unprop_assign.rs:+5:9: +5:10
--         _5 = (_2.0: i32);                // scope 3 at $DIR/mutable_variable_unprop_assign.rs:+5:13: +5:16
+-         _5 = _6;                         // scope 3 at $DIR/mutable_variable_unprop_assign.rs:+5:13: +5:16
 +         _5 = const 1_i32;                // scope 3 at $DIR/mutable_variable_unprop_assign.rs:+5:13: +5:16
           nop;                             // scope 0 at $DIR/mutable_variable_unprop_assign.rs:+0:11: +6:2
           StorageDead(_5);                 // scope 3 at $DIR/mutable_variable_unprop_assign.rs:+6:1: +6:2
           StorageDead(_4);                 // scope 2 at $DIR/mutable_variable_unprop_assign.rs:+6:1: +6:2
-          StorageDead(_2);                 // scope 1 at $DIR/mutable_variable_unprop_assign.rs:+6:1: +6:2
+          StorageDead(_6);                 // scope 1 at $DIR/mutable_variable_unprop_assign.rs:+6:1: +6:2
+          StorageDead(_7);                 // scope 1 at $DIR/mutable_variable_unprop_assign.rs:+6:1: +6:2
           StorageDead(_1);                 // scope 0 at $DIR/mutable_variable_unprop_assign.rs:+6:1: +6:2
           return;                          // scope 0 at $DIR/mutable_variable_unprop_assign.rs:+6:2: +6:2
       }
diff --git a/src/test/mir-opt/const_prop/optimizes_into_variable.main.ConstProp.32bit.diff b/src/test/mir-opt/const_prop/optimizes_into_variable.main.ConstProp.32bit.diff
index 94aadfaf8d5..7e8ebd31ad1 100644
--- a/src/test/mir-opt/const_prop/optimizes_into_variable.main.ConstProp.32bit.diff
+++ b/src/test/mir-opt/const_prop/optimizes_into_variable.main.ConstProp.32bit.diff
@@ -10,6 +10,8 @@
       let mut _6: usize;                   // in scope 0 at $DIR/optimizes_into_variable.rs:+2:13: +2:34
       let mut _7: bool;                    // in scope 0 at $DIR/optimizes_into_variable.rs:+2:13: +2:34
       let mut _9: Point;                   // in scope 0 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
+      let mut _10: u32;                    // in scope 0 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
+      let mut _11: u32;                    // in scope 0 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
       scope 1 {
           debug x => _1;                   // in scope 1 at $DIR/optimizes_into_variable.rs:+1:9: +1:10
           let _3: i32;                     // in scope 1 at $DIR/optimizes_into_variable.rs:+2:9: +2:10
@@ -51,13 +53,16 @@
           StorageDead(_5);                 // scope 1 at $DIR/optimizes_into_variable.rs:+2:34: +2:35
           StorageDead(_4);                 // scope 1 at $DIR/optimizes_into_variable.rs:+2:34: +2:35
           StorageLive(_8);                 // scope 2 at $DIR/optimizes_into_variable.rs:+3:9: +3:10
-          StorageLive(_9);                 // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
-          Deinit(_9);                      // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
-          (_9.0: u32) = const 12_u32;      // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
-          (_9.1: u32) = const 42_u32;      // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
--         _8 = (_9.1: u32);                // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:38
+          StorageLive(_10);                // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
+          StorageLive(_11);                // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
+          Deinit(_10);                     // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
+          Deinit(_11);                     // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
+          _10 = const 12_u32;              // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
+          _11 = const 42_u32;              // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
+-         _8 = _11;                        // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:38
 +         _8 = const 42_u32;               // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:38
-          StorageDead(_9);                 // scope 2 at $DIR/optimizes_into_variable.rs:+3:38: +3:39
+          StorageDead(_10);                // scope 2 at $DIR/optimizes_into_variable.rs:+3:38: +3:39
+          StorageDead(_11);                // scope 2 at $DIR/optimizes_into_variable.rs:+3:38: +3:39
           nop;                             // scope 0 at $DIR/optimizes_into_variable.rs:+0:11: +4:2
           StorageDead(_8);                 // scope 2 at $DIR/optimizes_into_variable.rs:+4:1: +4:2
           StorageDead(_3);                 // scope 1 at $DIR/optimizes_into_variable.rs:+4:1: +4:2
diff --git a/src/test/mir-opt/const_prop/optimizes_into_variable.main.ConstProp.64bit.diff b/src/test/mir-opt/const_prop/optimizes_into_variable.main.ConstProp.64bit.diff
index 94aadfaf8d5..7e8ebd31ad1 100644
--- a/src/test/mir-opt/const_prop/optimizes_into_variable.main.ConstProp.64bit.diff
+++ b/src/test/mir-opt/const_prop/optimizes_into_variable.main.ConstProp.64bit.diff
@@ -10,6 +10,8 @@
       let mut _6: usize;                   // in scope 0 at $DIR/optimizes_into_variable.rs:+2:13: +2:34
       let mut _7: bool;                    // in scope 0 at $DIR/optimizes_into_variable.rs:+2:13: +2:34
       let mut _9: Point;                   // in scope 0 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
+      let mut _10: u32;                    // in scope 0 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
+      let mut _11: u32;                    // in scope 0 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
       scope 1 {
           debug x => _1;                   // in scope 1 at $DIR/optimizes_into_variable.rs:+1:9: +1:10
           let _3: i32;                     // in scope 1 at $DIR/optimizes_into_variable.rs:+2:9: +2:10
@@ -51,13 +53,16 @@
           StorageDead(_5);                 // scope 1 at $DIR/optimizes_into_variable.rs:+2:34: +2:35
           StorageDead(_4);                 // scope 1 at $DIR/optimizes_into_variable.rs:+2:34: +2:35
           StorageLive(_8);                 // scope 2 at $DIR/optimizes_into_variable.rs:+3:9: +3:10
-          StorageLive(_9);                 // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
-          Deinit(_9);                      // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
-          (_9.0: u32) = const 12_u32;      // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
-          (_9.1: u32) = const 42_u32;      // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
--         _8 = (_9.1: u32);                // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:38
+          StorageLive(_10);                // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
+          StorageLive(_11);                // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
+          Deinit(_10);                     // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
+          Deinit(_11);                     // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
+          _10 = const 12_u32;              // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
+          _11 = const 42_u32;              // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
+-         _8 = _11;                        // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:38
 +         _8 = const 42_u32;               // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:38
-          StorageDead(_9);                 // scope 2 at $DIR/optimizes_into_variable.rs:+3:38: +3:39
+          StorageDead(_10);                // scope 2 at $DIR/optimizes_into_variable.rs:+3:38: +3:39
+          StorageDead(_11);                // scope 2 at $DIR/optimizes_into_variable.rs:+3:38: +3:39
           nop;                             // scope 0 at $DIR/optimizes_into_variable.rs:+0:11: +4:2
           StorageDead(_8);                 // scope 2 at $DIR/optimizes_into_variable.rs:+4:1: +4:2
           StorageDead(_3);                 // scope 1 at $DIR/optimizes_into_variable.rs:+4:1: +4:2
diff --git a/src/test/mir-opt/const_prop/optimizes_into_variable.main.PreCodegen.after.32bit.mir b/src/test/mir-opt/const_prop/optimizes_into_variable.main.PreCodegen.after.32bit.mir
new file mode 100644
index 00000000000..9db87cfc879
--- /dev/null
+++ b/src/test/mir-opt/const_prop/optimizes_into_variable.main.PreCodegen.after.32bit.mir
@@ -0,0 +1,27 @@
+// MIR for `main` after PreCodegen
+
+fn main() -> () {
+    let mut _0: ();                      // return place in scope 0 at $DIR/optimizes_into_variable.rs:+0:11: +0:11
+    let _1: i32;                         // in scope 0 at $DIR/optimizes_into_variable.rs:+1:9: +1:10
+    scope 1 {
+        debug x => _1;                   // in scope 1 at $DIR/optimizes_into_variable.rs:+1:9: +1:10
+        let _2: i32;                     // in scope 1 at $DIR/optimizes_into_variable.rs:+2:9: +2:10
+        scope 2 {
+            debug y => _2;               // in scope 2 at $DIR/optimizes_into_variable.rs:+2:9: +2:10
+            let _3: u32;                 // in scope 2 at $DIR/optimizes_into_variable.rs:+3:9: +3:10
+            scope 3 {
+                debug z => _3;           // in scope 3 at $DIR/optimizes_into_variable.rs:+3:9: +3:10
+            }
+        }
+    }
+
+    bb0: {
+        StorageLive(_1);                 // scope 0 at $DIR/optimizes_into_variable.rs:+1:9: +1:10
+        StorageLive(_2);                 // scope 1 at $DIR/optimizes_into_variable.rs:+2:9: +2:10
+        StorageLive(_3);                 // scope 2 at $DIR/optimizes_into_variable.rs:+3:9: +3:10
+        StorageDead(_3);                 // scope 2 at $DIR/optimizes_into_variable.rs:+4:1: +4:2
+        StorageDead(_2);                 // scope 1 at $DIR/optimizes_into_variable.rs:+4:1: +4:2
+        StorageDead(_1);                 // scope 0 at $DIR/optimizes_into_variable.rs:+4:1: +4:2
+        return;                          // scope 0 at $DIR/optimizes_into_variable.rs:+4:2: +4:2
+    }
+}
diff --git a/src/test/mir-opt/const_prop/optimizes_into_variable.main.PreCodegen.after.64bit.mir b/src/test/mir-opt/const_prop/optimizes_into_variable.main.PreCodegen.after.64bit.mir
new file mode 100644
index 00000000000..9db87cfc879
--- /dev/null
+++ b/src/test/mir-opt/const_prop/optimizes_into_variable.main.PreCodegen.after.64bit.mir
@@ -0,0 +1,27 @@
+// MIR for `main` after PreCodegen
+
+fn main() -> () {
+    let mut _0: ();                      // return place in scope 0 at $DIR/optimizes_into_variable.rs:+0:11: +0:11
+    let _1: i32;                         // in scope 0 at $DIR/optimizes_into_variable.rs:+1:9: +1:10
+    scope 1 {
+        debug x => _1;                   // in scope 1 at $DIR/optimizes_into_variable.rs:+1:9: +1:10
+        let _2: i32;                     // in scope 1 at $DIR/optimizes_into_variable.rs:+2:9: +2:10
+        scope 2 {
+            debug y => _2;               // in scope 2 at $DIR/optimizes_into_variable.rs:+2:9: +2:10
+            let _3: u32;                 // in scope 2 at $DIR/optimizes_into_variable.rs:+3:9: +3:10
+            scope 3 {
+                debug z => _3;           // in scope 3 at $DIR/optimizes_into_variable.rs:+3:9: +3:10
+            }
+        }
+    }
+
+    bb0: {
+        StorageLive(_1);                 // scope 0 at $DIR/optimizes_into_variable.rs:+1:9: +1:10
+        StorageLive(_2);                 // scope 1 at $DIR/optimizes_into_variable.rs:+2:9: +2:10
+        StorageLive(_3);                 // scope 2 at $DIR/optimizes_into_variable.rs:+3:9: +3:10
+        StorageDead(_3);                 // scope 2 at $DIR/optimizes_into_variable.rs:+4:1: +4:2
+        StorageDead(_2);                 // scope 1 at $DIR/optimizes_into_variable.rs:+4:1: +4:2
+        StorageDead(_1);                 // scope 0 at $DIR/optimizes_into_variable.rs:+4:1: +4:2
+        return;                          // scope 0 at $DIR/optimizes_into_variable.rs:+4:2: +4:2
+    }
+}
diff --git a/src/test/mir-opt/const_prop/optimizes_into_variable.main.ScalarReplacementOfAggregates.32bit.diff b/src/test/mir-opt/const_prop/optimizes_into_variable.main.ScalarReplacementOfAggregates.32bit.diff
new file mode 100644
index 00000000000..3f9f3b2eac7
--- /dev/null
+++ b/src/test/mir-opt/const_prop/optimizes_into_variable.main.ScalarReplacementOfAggregates.32bit.diff
@@ -0,0 +1,72 @@
+- // MIR for `main` before ScalarReplacementOfAggregates
++ // MIR for `main` after ScalarReplacementOfAggregates
+  
+  fn main() -> () {
+      let mut _0: ();                      // return place in scope 0 at $DIR/optimizes_into_variable.rs:+0:11: +0:11
+      let _1: i32;                         // in scope 0 at $DIR/optimizes_into_variable.rs:+1:9: +1:10
+      let mut _2: (i32, bool);             // in scope 0 at $DIR/optimizes_into_variable.rs:+1:13: +1:18
+      let mut _4: [i32; 6];                // in scope 0 at $DIR/optimizes_into_variable.rs:+2:13: +2:31
+      let _5: usize;                       // in scope 0 at $DIR/optimizes_into_variable.rs:+2:32: +2:33
+      let mut _6: usize;                   // in scope 0 at $DIR/optimizes_into_variable.rs:+2:13: +2:34
+      let mut _7: bool;                    // in scope 0 at $DIR/optimizes_into_variable.rs:+2:13: +2:34
+      let mut _9: Point;                   // in scope 0 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
++     let mut _10: u32;                    // in scope 0 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
++     let mut _11: u32;                    // in scope 0 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
+      scope 1 {
+          debug x => _1;                   // in scope 1 at $DIR/optimizes_into_variable.rs:+1:9: +1:10
+          let _3: i32;                     // in scope 1 at $DIR/optimizes_into_variable.rs:+2:9: +2:10
+          scope 2 {
+              debug y => _3;               // in scope 2 at $DIR/optimizes_into_variable.rs:+2:9: +2:10
+              let _8: u32;                 // in scope 2 at $DIR/optimizes_into_variable.rs:+3:9: +3:10
+              scope 3 {
+                  debug z => _8;           // in scope 3 at $DIR/optimizes_into_variable.rs:+3:9: +3:10
+              }
+          }
+      }
+  
+      bb0: {
+          StorageLive(_1);                 // scope 0 at $DIR/optimizes_into_variable.rs:+1:9: +1:10
+          _2 = CheckedAdd(const 2_i32, const 2_i32); // scope 0 at $DIR/optimizes_into_variable.rs:+1:13: +1:18
+          assert(!move (_2.1: bool), "attempt to compute `{} + {}`, which would overflow", const 2_i32, const 2_i32) -> bb1; // scope 0 at $DIR/optimizes_into_variable.rs:+1:13: +1:18
+      }
+  
+      bb1: {
+          _1 = move (_2.0: i32);           // scope 0 at $DIR/optimizes_into_variable.rs:+1:13: +1:18
+          StorageLive(_3);                 // scope 1 at $DIR/optimizes_into_variable.rs:+2:9: +2:10
+          StorageLive(_4);                 // scope 1 at $DIR/optimizes_into_variable.rs:+2:13: +2:31
+          _4 = [const 0_i32, const 1_i32, const 2_i32, const 3_i32, const 4_i32, const 5_i32]; // scope 1 at $DIR/optimizes_into_variable.rs:+2:13: +2:31
+          StorageLive(_5);                 // scope 1 at $DIR/optimizes_into_variable.rs:+2:32: +2:33
+          _5 = const 3_usize;              // scope 1 at $DIR/optimizes_into_variable.rs:+2:32: +2:33
+          _6 = Len(_4);                    // scope 1 at $DIR/optimizes_into_variable.rs:+2:13: +2:34
+          _7 = Lt(_5, _6);                 // scope 1 at $DIR/optimizes_into_variable.rs:+2:13: +2:34
+          assert(move _7, "index out of bounds: the length is {} but the index is {}", move _6, _5) -> bb2; // scope 1 at $DIR/optimizes_into_variable.rs:+2:13: +2:34
+      }
+  
+      bb2: {
+          _3 = _4[_5];                     // scope 1 at $DIR/optimizes_into_variable.rs:+2:13: +2:34
+          StorageDead(_5);                 // scope 1 at $DIR/optimizes_into_variable.rs:+2:34: +2:35
+          StorageDead(_4);                 // scope 1 at $DIR/optimizes_into_variable.rs:+2:34: +2:35
+          StorageLive(_8);                 // scope 2 at $DIR/optimizes_into_variable.rs:+3:9: +3:10
+-         StorageLive(_9);                 // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
+-         Deinit(_9);                      // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
+-         (_9.0: u32) = const 12_u32;      // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
+-         (_9.1: u32) = const 42_u32;      // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
+-         _8 = (_9.1: u32);                // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:38
+-         StorageDead(_9);                 // scope 2 at $DIR/optimizes_into_variable.rs:+3:38: +3:39
++         StorageLive(_10);                // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
++         StorageLive(_11);                // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
++         Deinit(_10);                     // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
++         Deinit(_11);                     // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
++         _10 = const 12_u32;              // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
++         _11 = const 42_u32;              // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
++         _8 = _11;                        // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:38
++         StorageDead(_10);                // scope 2 at $DIR/optimizes_into_variable.rs:+3:38: +3:39
++         StorageDead(_11);                // scope 2 at $DIR/optimizes_into_variable.rs:+3:38: +3:39
+          nop;                             // scope 0 at $DIR/optimizes_into_variable.rs:+0:11: +4:2
+          StorageDead(_8);                 // scope 2 at $DIR/optimizes_into_variable.rs:+4:1: +4:2
+          StorageDead(_3);                 // scope 1 at $DIR/optimizes_into_variable.rs:+4:1: +4:2
+          StorageDead(_1);                 // scope 0 at $DIR/optimizes_into_variable.rs:+4:1: +4:2
+          return;                          // scope 0 at $DIR/optimizes_into_variable.rs:+4:2: +4:2
+      }
+  }
+  
diff --git a/src/test/mir-opt/const_prop/optimizes_into_variable.main.ScalarReplacementOfAggregates.64bit.diff b/src/test/mir-opt/const_prop/optimizes_into_variable.main.ScalarReplacementOfAggregates.64bit.diff
new file mode 100644
index 00000000000..3f9f3b2eac7
--- /dev/null
+++ b/src/test/mir-opt/const_prop/optimizes_into_variable.main.ScalarReplacementOfAggregates.64bit.diff
@@ -0,0 +1,72 @@
+- // MIR for `main` before ScalarReplacementOfAggregates
++ // MIR for `main` after ScalarReplacementOfAggregates
+  
+  fn main() -> () {
+      let mut _0: ();                      // return place in scope 0 at $DIR/optimizes_into_variable.rs:+0:11: +0:11
+      let _1: i32;                         // in scope 0 at $DIR/optimizes_into_variable.rs:+1:9: +1:10
+      let mut _2: (i32, bool);             // in scope 0 at $DIR/optimizes_into_variable.rs:+1:13: +1:18
+      let mut _4: [i32; 6];                // in scope 0 at $DIR/optimizes_into_variable.rs:+2:13: +2:31
+      let _5: usize;                       // in scope 0 at $DIR/optimizes_into_variable.rs:+2:32: +2:33
+      let mut _6: usize;                   // in scope 0 at $DIR/optimizes_into_variable.rs:+2:13: +2:34
+      let mut _7: bool;                    // in scope 0 at $DIR/optimizes_into_variable.rs:+2:13: +2:34
+      let mut _9: Point;                   // in scope 0 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
++     let mut _10: u32;                    // in scope 0 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
++     let mut _11: u32;                    // in scope 0 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
+      scope 1 {
+          debug x => _1;                   // in scope 1 at $DIR/optimizes_into_variable.rs:+1:9: +1:10
+          let _3: i32;                     // in scope 1 at $DIR/optimizes_into_variable.rs:+2:9: +2:10
+          scope 2 {
+              debug y => _3;               // in scope 2 at $DIR/optimizes_into_variable.rs:+2:9: +2:10
+              let _8: u32;                 // in scope 2 at $DIR/optimizes_into_variable.rs:+3:9: +3:10
+              scope 3 {
+                  debug z => _8;           // in scope 3 at $DIR/optimizes_into_variable.rs:+3:9: +3:10
+              }
+          }
+      }
+  
+      bb0: {
+          StorageLive(_1);                 // scope 0 at $DIR/optimizes_into_variable.rs:+1:9: +1:10
+          _2 = CheckedAdd(const 2_i32, const 2_i32); // scope 0 at $DIR/optimizes_into_variable.rs:+1:13: +1:18
+          assert(!move (_2.1: bool), "attempt to compute `{} + {}`, which would overflow", const 2_i32, const 2_i32) -> bb1; // scope 0 at $DIR/optimizes_into_variable.rs:+1:13: +1:18
+      }
+  
+      bb1: {
+          _1 = move (_2.0: i32);           // scope 0 at $DIR/optimizes_into_variable.rs:+1:13: +1:18
+          StorageLive(_3);                 // scope 1 at $DIR/optimizes_into_variable.rs:+2:9: +2:10
+          StorageLive(_4);                 // scope 1 at $DIR/optimizes_into_variable.rs:+2:13: +2:31
+          _4 = [const 0_i32, const 1_i32, const 2_i32, const 3_i32, const 4_i32, const 5_i32]; // scope 1 at $DIR/optimizes_into_variable.rs:+2:13: +2:31
+          StorageLive(_5);                 // scope 1 at $DIR/optimizes_into_variable.rs:+2:32: +2:33
+          _5 = const 3_usize;              // scope 1 at $DIR/optimizes_into_variable.rs:+2:32: +2:33
+          _6 = Len(_4);                    // scope 1 at $DIR/optimizes_into_variable.rs:+2:13: +2:34
+          _7 = Lt(_5, _6);                 // scope 1 at $DIR/optimizes_into_variable.rs:+2:13: +2:34
+          assert(move _7, "index out of bounds: the length is {} but the index is {}", move _6, _5) -> bb2; // scope 1 at $DIR/optimizes_into_variable.rs:+2:13: +2:34
+      }
+  
+      bb2: {
+          _3 = _4[_5];                     // scope 1 at $DIR/optimizes_into_variable.rs:+2:13: +2:34
+          StorageDead(_5);                 // scope 1 at $DIR/optimizes_into_variable.rs:+2:34: +2:35
+          StorageDead(_4);                 // scope 1 at $DIR/optimizes_into_variable.rs:+2:34: +2:35
+          StorageLive(_8);                 // scope 2 at $DIR/optimizes_into_variable.rs:+3:9: +3:10
+-         StorageLive(_9);                 // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
+-         Deinit(_9);                      // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
+-         (_9.0: u32) = const 12_u32;      // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
+-         (_9.1: u32) = const 42_u32;      // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
+-         _8 = (_9.1: u32);                // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:38
+-         StorageDead(_9);                 // scope 2 at $DIR/optimizes_into_variable.rs:+3:38: +3:39
++         StorageLive(_10);                // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
++         StorageLive(_11);                // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
++         Deinit(_10);                     // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
++         Deinit(_11);                     // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
++         _10 = const 12_u32;              // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
++         _11 = const 42_u32;              // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
++         _8 = _11;                        // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:38
++         StorageDead(_10);                // scope 2 at $DIR/optimizes_into_variable.rs:+3:38: +3:39
++         StorageDead(_11);                // scope 2 at $DIR/optimizes_into_variable.rs:+3:38: +3:39
+          nop;                             // scope 0 at $DIR/optimizes_into_variable.rs:+0:11: +4:2
+          StorageDead(_8);                 // scope 2 at $DIR/optimizes_into_variable.rs:+4:1: +4:2
+          StorageDead(_3);                 // scope 1 at $DIR/optimizes_into_variable.rs:+4:1: +4:2
+          StorageDead(_1);                 // scope 0 at $DIR/optimizes_into_variable.rs:+4:1: +4:2
+          return;                          // scope 0 at $DIR/optimizes_into_variable.rs:+4:2: +4:2
+      }
+  }
+  
diff --git a/src/test/mir-opt/const_prop/optimizes_into_variable.rs b/src/test/mir-opt/const_prop/optimizes_into_variable.rs
index c0fbd2558cd..02566654818 100644
--- a/src/test/mir-opt/const_prop/optimizes_into_variable.rs
+++ b/src/test/mir-opt/const_prop/optimizes_into_variable.rs
@@ -7,8 +7,10 @@ struct Point {
 }
 
 // EMIT_MIR_FOR_EACH_BIT_WIDTH
+// EMIT_MIR optimizes_into_variable.main.ScalarReplacementOfAggregates.diff
 // EMIT_MIR optimizes_into_variable.main.ConstProp.diff
 // EMIT_MIR optimizes_into_variable.main.SimplifyLocals.after.mir
+// EMIT_MIR optimizes_into_variable.main.PreCodegen.after.mir
 fn main() {
     let x = 2 + 2;
     let y = [0, 1, 2, 3, 4, 5][3];
diff --git a/src/test/mir-opt/issue_73223.main.SimplifyArmIdentity.diff b/src/test/mir-opt/issue_73223.main.SimplifyArmIdentity.diff
index c9a9511586d..b88cdfcbc96 100644
--- a/src/test/mir-opt/issue_73223.main.SimplifyArmIdentity.diff
+++ b/src/test/mir-opt/issue_73223.main.SimplifyArmIdentity.diff
@@ -26,6 +26,8 @@
       let mut _25: &i32;                   // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
       let _26: &i32;                       // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
       let mut _27: std::option::Option<std::fmt::Arguments<'_>>; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+      let mut _29: &i32;                   // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+      let mut _30: &i32;                   // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
       scope 1 {
           debug split => _1;               // in scope 1 at $DIR/issue_73223.rs:+1:9: +1:14
           let _6: std::option::Option<i32>; // in scope 1 at $DIR/issue_73223.rs:+6:9: +6:14
@@ -83,7 +85,8 @@
           discriminant(_6) = 1;            // scope 1 at $DIR/issue_73223.rs:+6:17: +6:28
           StorageDead(_7);                 // scope 1 at $DIR/issue_73223.rs:+6:27: +6:28
           StorageLive(_8);                 // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
-          StorageLive(_9);                 // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+          StorageLive(_29);                // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+          StorageLive(_30);                // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
           StorageLive(_10);                // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
           _10 = &_1;                       // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
           StorageLive(_11);                // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
@@ -92,15 +95,16 @@
                                            // + span: $SRC_DIR/core/src/macros/mod.rs:LL:COL
                                            // + literal: Const { ty: &i32, val: Unevaluated(main, [], Some(promoted[0])) }
           _11 = _28;                       // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
-          Deinit(_9);                      // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
-          (_9.0: &i32) = move _10;         // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
-          (_9.1: &i32) = move _11;         // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+          Deinit(_29);                     // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+          Deinit(_30);                     // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+          _29 = move _10;                  // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+          _30 = move _11;                  // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
           StorageDead(_11);                // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
           StorageDead(_10);                // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
           StorageLive(_13);                // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
-          _13 = (_9.0: &i32);              // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+          _13 = _29;                       // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
           StorageLive(_14);                // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
-          _14 = (_9.1: &i32);              // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+          _14 = _30;                       // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
           StorageLive(_15);                // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
           StorageLive(_16);                // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
           StorageLive(_17);                // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
@@ -150,7 +154,8 @@
           StorageDead(_15);                // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
           StorageDead(_14);                // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
           StorageDead(_13);                // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
-          StorageDead(_9);                 // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+          StorageDead(_29);                // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+          StorageDead(_30);                // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
           StorageDead(_8);                 // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
           nop;                             // scope 0 at $DIR/issue_73223.rs:+0:11: +8:2
           StorageDead(_6);                 // scope 1 at $DIR/issue_73223.rs:+8:1: +8:2
diff --git a/src/test/mir-opt/sroa.dropping.ScalarReplacementOfAggregates.diff b/src/test/mir-opt/sroa.dropping.ScalarReplacementOfAggregates.diff
new file mode 100644
index 00000000000..eb88304466e
--- /dev/null
+++ b/src/test/mir-opt/sroa.dropping.ScalarReplacementOfAggregates.diff
@@ -0,0 +1,50 @@
+- // MIR for `dropping` before ScalarReplacementOfAggregates
++ // MIR for `dropping` after ScalarReplacementOfAggregates
+  
+  fn dropping() -> () {
+      let mut _0: ();                      // return place in scope 0 at $DIR/sroa.rs:+0:19: +0:19
+      let _1: Tag;                         // in scope 0 at $DIR/sroa.rs:+1:5: +1:32
+      let mut _2: S;                       // in scope 0 at $DIR/sroa.rs:+1:5: +1:30
+      let mut _3: Tag;                     // in scope 0 at $DIR/sroa.rs:+1:7: +1:13
+      let mut _4: Tag;                     // in scope 0 at $DIR/sroa.rs:+1:15: +1:21
+      let mut _5: Tag;                     // in scope 0 at $DIR/sroa.rs:+1:23: +1:29
+  
+      bb0: {
+          StorageLive(_1);                 // scope 0 at $DIR/sroa.rs:+1:5: +1:32
+          StorageLive(_2);                 // scope 0 at $DIR/sroa.rs:+1:5: +1:30
+          StorageLive(_3);                 // scope 0 at $DIR/sroa.rs:+1:7: +1:13
+          Deinit(_3);                      // scope 0 at $DIR/sroa.rs:+1:7: +1:13
+          (_3.0: usize) = const 0_usize;   // scope 0 at $DIR/sroa.rs:+1:7: +1:13
+          StorageLive(_4);                 // scope 0 at $DIR/sroa.rs:+1:15: +1:21
+          Deinit(_4);                      // scope 0 at $DIR/sroa.rs:+1:15: +1:21
+          (_4.0: usize) = const 1_usize;   // scope 0 at $DIR/sroa.rs:+1:15: +1:21
+          StorageLive(_5);                 // scope 0 at $DIR/sroa.rs:+1:23: +1:29
+          Deinit(_5);                      // scope 0 at $DIR/sroa.rs:+1:23: +1:29
+          (_5.0: usize) = const 2_usize;   // scope 0 at $DIR/sroa.rs:+1:23: +1:29
+          Deinit(_2);                      // scope 0 at $DIR/sroa.rs:+1:5: +1:30
+          (_2.0: Tag) = move _3;           // scope 0 at $DIR/sroa.rs:+1:5: +1:30
+          (_2.1: Tag) = move _4;           // scope 0 at $DIR/sroa.rs:+1:5: +1:30
+          (_2.2: Tag) = move _5;           // scope 0 at $DIR/sroa.rs:+1:5: +1:30
+          StorageDead(_5);                 // scope 0 at $DIR/sroa.rs:+1:29: +1:30
+          StorageDead(_4);                 // scope 0 at $DIR/sroa.rs:+1:29: +1:30
+          StorageDead(_3);                 // scope 0 at $DIR/sroa.rs:+1:29: +1:30
+          _1 = move (_2.1: Tag);           // scope 0 at $DIR/sroa.rs:+1:5: +1:32
+          drop(_1) -> bb1;                 // scope 0 at $DIR/sroa.rs:+1:32: +1:33
+      }
+  
+      bb1: {
+          drop((_2.0: Tag)) -> bb3;        // scope 0 at $DIR/sroa.rs:+1:32: +1:33
+      }
+  
+      bb2: {
+          StorageDead(_2);                 // scope 0 at $DIR/sroa.rs:+1:32: +1:33
+          StorageDead(_1);                 // scope 0 at $DIR/sroa.rs:+1:32: +1:33
+          _0 = const ();                   // scope 0 at $DIR/sroa.rs:+0:19: +2:2
+          return;                          // scope 0 at $DIR/sroa.rs:+2:2: +2:2
+      }
+  
+      bb3: {
+          drop((_2.2: Tag)) -> bb2;        // scope 0 at $DIR/sroa.rs:+1:32: +1:33
+      }
+  }
+  
diff --git a/src/test/mir-opt/sroa.enums.ScalarReplacementOfAggregates.diff b/src/test/mir-opt/sroa.enums.ScalarReplacementOfAggregates.diff
new file mode 100644
index 00000000000..7c7e87c32a2
--- /dev/null
+++ b/src/test/mir-opt/sroa.enums.ScalarReplacementOfAggregates.diff
@@ -0,0 +1,45 @@
+- // MIR for `enums` before ScalarReplacementOfAggregates
++ // MIR for `enums` after ScalarReplacementOfAggregates
+  
+  fn enums(_1: usize) -> usize {
+      debug a => _1;                       // in scope 0 at $DIR/sroa.rs:+0:14: +0:15
+      let mut _0: usize;                   // return place in scope 0 at $DIR/sroa.rs:+0:27: +0:32
+      let mut _2: std::option::Option<usize>; // in scope 0 at $DIR/sroa.rs:+1:22: +1:29
+      let mut _3: usize;                   // in scope 0 at $DIR/sroa.rs:+1:27: +1:28
+      let mut _4: isize;                   // in scope 0 at $DIR/sroa.rs:+1:12: +1:19
+      scope 1 {
+          debug a => _5;                   // in scope 1 at $DIR/sroa.rs:+1:17: +1:18
+          let _5: usize;                   // in scope 1 at $DIR/sroa.rs:+1:17: +1:18
+      }
+  
+      bb0: {
+          StorageLive(_2);                 // scope 1 at $DIR/sroa.rs:+1:22: +1:29
+          StorageLive(_3);                 // scope 1 at $DIR/sroa.rs:+1:27: +1:28
+          _3 = _1;                         // scope 1 at $DIR/sroa.rs:+1:27: +1:28
+          Deinit(_2);                      // scope 1 at $DIR/sroa.rs:+1:22: +1:29
+          ((_2 as Some).0: usize) = move _3; // scope 1 at $DIR/sroa.rs:+1:22: +1:29
+          discriminant(_2) = 1;            // scope 1 at $DIR/sroa.rs:+1:22: +1:29
+          StorageDead(_3);                 // scope 1 at $DIR/sroa.rs:+1:28: +1:29
+          _4 = discriminant(_2);           // scope 1 at $DIR/sroa.rs:+1:12: +1:19
+          switchInt(move _4) -> [1_isize: bb1, otherwise: bb2]; // scope 1 at $DIR/sroa.rs:+1:12: +1:19
+      }
+  
+      bb1: {
+          StorageLive(_5);                 // scope 1 at $DIR/sroa.rs:+1:17: +1:18
+          _5 = ((_2 as Some).0: usize);    // scope 1 at $DIR/sroa.rs:+1:17: +1:18
+          _0 = _5;                         // scope 1 at $DIR/sroa.rs:+1:32: +1:33
+          StorageDead(_5);                 // scope 0 at $DIR/sroa.rs:+1:34: +1:35
+          goto -> bb3;                     // scope 0 at $DIR/sroa.rs:+1:5: +1:46
+      }
+  
+      bb2: {
+          _0 = const 0_usize;              // scope 0 at $DIR/sroa.rs:+1:43: +1:44
+          goto -> bb3;                     // scope 0 at $DIR/sroa.rs:+1:5: +1:46
+      }
+  
+      bb3: {
+          StorageDead(_2);                 // scope 0 at $DIR/sroa.rs:+2:1: +2:2
+          return;                          // scope 0 at $DIR/sroa.rs:+2:2: +2:2
+      }
+  }
+  
diff --git a/src/test/mir-opt/sroa.escaping.ScalarReplacementOfAggregates.diff b/src/test/mir-opt/sroa.escaping.ScalarReplacementOfAggregates.diff
new file mode 100644
index 00000000000..64559b58f61
--- /dev/null
+++ b/src/test/mir-opt/sroa.escaping.ScalarReplacementOfAggregates.diff
@@ -0,0 +1,47 @@
+- // MIR for `escaping` before ScalarReplacementOfAggregates
++ // MIR for `escaping` after ScalarReplacementOfAggregates
+  
+  fn escaping() -> () {
+      let mut _0: ();                      // return place in scope 0 at $DIR/sroa.rs:+0:19: +0:19
+      let _1: ();                          // in scope 0 at $DIR/sroa.rs:+2:5: +2:42
+      let mut _2: *const u32;              // in scope 0 at $DIR/sroa.rs:+2:7: +2:41
+      let _3: &u32;                        // in scope 0 at $DIR/sroa.rs:+2:7: +2:41
+      let _4: Escaping;                    // in scope 0 at $DIR/sroa.rs:+2:8: +2:39
+      let mut _5: u32;                     // in scope 0 at $DIR/sroa.rs:+2:34: +2:37
+  
+      bb0: {
+          StorageLive(_1);                 // scope 0 at $DIR/sroa.rs:+2:5: +2:42
+          StorageLive(_2);                 // scope 0 at $DIR/sroa.rs:+2:7: +2:41
+          StorageLive(_3);                 // scope 0 at $DIR/sroa.rs:+2:7: +2:41
+          StorageLive(_4);                 // scope 0 at $DIR/sroa.rs:+2:8: +2:39
+          StorageLive(_5);                 // scope 0 at $DIR/sroa.rs:+2:34: +2:37
+          _5 = g() -> bb1;                 // scope 0 at $DIR/sroa.rs:+2:34: +2:37
+                                           // mir::Constant
+                                           // + span: $DIR/sroa.rs:78:34: 78:35
+                                           // + literal: Const { ty: fn() -> u32 {g}, val: Value(<ZST>) }
+      }
+  
+      bb1: {
+          Deinit(_4);                      // scope 0 at $DIR/sroa.rs:+2:8: +2:39
+          (_4.0: u32) = const 1_u32;       // scope 0 at $DIR/sroa.rs:+2:8: +2:39
+          (_4.1: u32) = const 2_u32;       // scope 0 at $DIR/sroa.rs:+2:8: +2:39
+          (_4.2: u32) = move _5;           // scope 0 at $DIR/sroa.rs:+2:8: +2:39
+          StorageDead(_5);                 // scope 0 at $DIR/sroa.rs:+2:38: +2:39
+          _3 = &(_4.0: u32);               // scope 0 at $DIR/sroa.rs:+2:7: +2:41
+          _2 = &raw const (*_3);           // scope 0 at $DIR/sroa.rs:+2:7: +2:41
+          _1 = f(move _2) -> bb2;          // scope 0 at $DIR/sroa.rs:+2:5: +2:42
+                                           // mir::Constant
+                                           // + span: $DIR/sroa.rs:78:5: 78:6
+                                           // + literal: Const { ty: fn(*const u32) {f}, val: Value(<ZST>) }
+      }
+  
+      bb2: {
+          StorageDead(_2);                 // scope 0 at $DIR/sroa.rs:+2:41: +2:42
+          StorageDead(_4);                 // scope 0 at $DIR/sroa.rs:+2:42: +2:43
+          StorageDead(_3);                 // scope 0 at $DIR/sroa.rs:+2:42: +2:43
+          StorageDead(_1);                 // scope 0 at $DIR/sroa.rs:+2:42: +2:43
+          _0 = const ();                   // scope 0 at $DIR/sroa.rs:+0:19: +3:2
+          return;                          // scope 0 at $DIR/sroa.rs:+3:2: +3:2
+      }
+  }
+  
diff --git a/src/test/mir-opt/sroa.flat.ScalarReplacementOfAggregates.diff b/src/test/mir-opt/sroa.flat.ScalarReplacementOfAggregates.diff
new file mode 100644
index 00000000000..d4c04d5e68b
--- /dev/null
+++ b/src/test/mir-opt/sroa.flat.ScalarReplacementOfAggregates.diff
@@ -0,0 +1,87 @@
+- // MIR for `flat` before ScalarReplacementOfAggregates
++ // MIR for `flat` after ScalarReplacementOfAggregates
+  
+  fn flat() -> () {
+      let mut _0: ();                      // return place in scope 0 at $DIR/sroa.rs:+0:15: +0:15
+      let _1: u8;                          // in scope 0 at $DIR/sroa.rs:+1:15: +1:16
+      let _2: ();                          // in scope 0 at $DIR/sroa.rs:+1:18: +1:19
+      let _3: &str;                        // in scope 0 at $DIR/sroa.rs:+1:21: +1:22
+      let _4: std::option::Option<isize>;  // in scope 0 at $DIR/sroa.rs:+1:24: +1:25
+      let mut _5: Foo;                     // in scope 0 at $DIR/sroa.rs:+1:30: +1:70
+      let mut _6: ();                      // in scope 0 at $DIR/sroa.rs:+1:45: +1:47
+      let mut _7: std::option::Option<isize>; // in scope 0 at $DIR/sroa.rs:+1:60: +1:68
++     let mut _8: u8;                      // in scope 0 at $DIR/sroa.rs:+1:30: +1:70
++     let mut _9: ();                      // in scope 0 at $DIR/sroa.rs:+1:30: +1:70
++     let mut _10: &str;                   // in scope 0 at $DIR/sroa.rs:+1:30: +1:70
++     let mut _11: std::option::Option<isize>; // in scope 0 at $DIR/sroa.rs:+1:30: +1:70
+      scope 1 {
+          debug a => _1;                   // in scope 1 at $DIR/sroa.rs:+1:15: +1:16
+          debug b => _2;                   // in scope 1 at $DIR/sroa.rs:+1:18: +1:19
+          debug c => _3;                   // in scope 1 at $DIR/sroa.rs:+1:21: +1:22
+          debug d => _4;                   // in scope 1 at $DIR/sroa.rs:+1:24: +1:25
+          scope 2 {
+              scope 3 {
+                  scope 4 {
+                      scope 5 {
+                      }
+                  }
+              }
+          }
+      }
+  
+      bb0: {
+-         StorageLive(_5);                 // scope 0 at $DIR/sroa.rs:+1:30: +1:70
++         StorageLive(_8);                 // scope 0 at $DIR/sroa.rs:+1:30: +1:70
++         StorageLive(_9);                 // scope 0 at $DIR/sroa.rs:+1:30: +1:70
++         StorageLive(_10);                // scope 0 at $DIR/sroa.rs:+1:30: +1:70
++         StorageLive(_11);                // scope 0 at $DIR/sroa.rs:+1:30: +1:70
+          StorageLive(_6);                 // scope 0 at $DIR/sroa.rs:+1:45: +1:47
+          Deinit(_6);                      // scope 0 at $DIR/sroa.rs:+1:45: +1:47
+          StorageLive(_7);                 // scope 0 at $DIR/sroa.rs:+1:60: +1:68
+          Deinit(_7);                      // scope 0 at $DIR/sroa.rs:+1:60: +1:68
+          ((_7 as Some).0: isize) = const -4_isize; // scope 0 at $DIR/sroa.rs:+1:60: +1:68
+          discriminant(_7) = 1;            // scope 0 at $DIR/sroa.rs:+1:60: +1:68
+-         Deinit(_5);                      // scope 0 at $DIR/sroa.rs:+1:30: +1:70
+-         (_5.0: u8) = const 5_u8;         // scope 0 at $DIR/sroa.rs:+1:30: +1:70
+-         (_5.1: ()) = move _6;            // scope 0 at $DIR/sroa.rs:+1:30: +1:70
+-         (_5.2: &str) = const "a";        // scope 0 at $DIR/sroa.rs:+1:30: +1:70
++         Deinit(_8);                      // scope 0 at $DIR/sroa.rs:+1:30: +1:70
++         Deinit(_9);                      // scope 0 at $DIR/sroa.rs:+1:30: +1:70
++         Deinit(_10);                     // scope 0 at $DIR/sroa.rs:+1:30: +1:70
++         Deinit(_11);                     // scope 0 at $DIR/sroa.rs:+1:30: +1:70
++         _8 = const 5_u8;                 // scope 0 at $DIR/sroa.rs:+1:30: +1:70
++         _9 = move _6;                    // scope 0 at $DIR/sroa.rs:+1:30: +1:70
++         _10 = const "a";                 // scope 0 at $DIR/sroa.rs:+1:30: +1:70
+                                           // mir::Constant
+                                           // + span: $DIR/sroa.rs:57:52: 57:55
+                                           // + literal: Const { ty: &str, val: Value(Slice(..)) }
+-         (_5.3: std::option::Option<isize>) = move _7; // scope 0 at $DIR/sroa.rs:+1:30: +1:70
++         _11 = move _7;                   // scope 0 at $DIR/sroa.rs:+1:30: +1:70
+          StorageDead(_7);                 // scope 0 at $DIR/sroa.rs:+1:69: +1:70
+          StorageDead(_6);                 // scope 0 at $DIR/sroa.rs:+1:69: +1:70
+          StorageLive(_1);                 // scope 0 at $DIR/sroa.rs:+1:15: +1:16
+-         _1 = (_5.0: u8);                 // scope 0 at $DIR/sroa.rs:+1:15: +1:16
++         _1 = _8;                         // scope 0 at $DIR/sroa.rs:+1:15: +1:16
+          StorageLive(_2);                 // scope 0 at $DIR/sroa.rs:+1:18: +1:19
+-         _2 = (_5.1: ());                 // scope 0 at $DIR/sroa.rs:+1:18: +1:19
++         _2 = _9;                         // scope 0 at $DIR/sroa.rs:+1:18: +1:19
+          StorageLive(_3);                 // scope 0 at $DIR/sroa.rs:+1:21: +1:22
+-         _3 = (_5.2: &str);               // scope 0 at $DIR/sroa.rs:+1:21: +1:22
++         _3 = _10;                        // scope 0 at $DIR/sroa.rs:+1:21: +1:22
+          StorageLive(_4);                 // scope 0 at $DIR/sroa.rs:+1:24: +1:25
+-         _4 = (_5.3: std::option::Option<isize>); // scope 0 at $DIR/sroa.rs:+1:24: +1:25
+-         StorageDead(_5);                 // scope 0 at $DIR/sroa.rs:+1:70: +1:71
++         _4 = _11;                        // scope 0 at $DIR/sroa.rs:+1:24: +1:25
++         StorageDead(_8);                 // scope 0 at $DIR/sroa.rs:+1:70: +1:71
++         StorageDead(_9);                 // scope 0 at $DIR/sroa.rs:+1:70: +1:71
++         StorageDead(_10);                // scope 0 at $DIR/sroa.rs:+1:70: +1:71
++         StorageDead(_11);                // scope 0 at $DIR/sroa.rs:+1:70: +1:71
+          _0 = const ();                   // scope 0 at $DIR/sroa.rs:+0:15: +6:2
+          StorageDead(_4);                 // scope 0 at $DIR/sroa.rs:+6:1: +6:2
+          StorageDead(_3);                 // scope 0 at $DIR/sroa.rs:+6:1: +6:2
+          StorageDead(_2);                 // scope 0 at $DIR/sroa.rs:+6:1: +6:2
+          StorageDead(_1);                 // scope 0 at $DIR/sroa.rs:+6:1: +6:2
+          return;                          // scope 0 at $DIR/sroa.rs:+6:2: +6:2
+      }
+  }
+  
diff --git a/src/test/mir-opt/sroa.rs b/src/test/mir-opt/sroa.rs
new file mode 100644
index 00000000000..ff8deb40d7d
--- /dev/null
+++ b/src/test/mir-opt/sroa.rs
@@ -0,0 +1,88 @@
+// unit-test: ScalarReplacementOfAggregates
+// compile-flags: -Cpanic=abort
+// no-prefer-dynamic
+
+struct Tag(usize);
+
+#[repr(C)]
+struct S(Tag, Tag, Tag);
+
+impl Drop for Tag {
+    #[inline(never)]
+    fn drop(&mut self) {}
+}
+
+// EMIT_MIR sroa.dropping.ScalarReplacementOfAggregates.diff
+pub fn dropping() {
+    S(Tag(0), Tag(1), Tag(2)).1;
+}
+
+// EMIT_MIR sroa.enums.ScalarReplacementOfAggregates.diff
+pub fn enums(a: usize) -> usize {
+    if let Some(a) = Some(a) { a } else { 0 }
+}
+
+// EMIT_MIR sroa.structs.ScalarReplacementOfAggregates.diff
+pub fn structs(a: f32) -> f32 {
+    struct U {
+        _foo: usize,
+        a: f32,
+    }
+
+    U { _foo: 0, a }.a
+}
+
+// EMIT_MIR sroa.unions.ScalarReplacementOfAggregates.diff
+pub fn unions(a: f32) -> u32 {
+    union Repr {
+        f: f32,
+        u: u32,
+    }
+    unsafe { Repr { f: a }.u }
+}
+
+struct Foo {
+    a: u8,
+    b: (),
+    c: &'static str,
+    d: Option<isize>,
+}
+
+fn g() -> u32 {
+    3
+}
+
+// EMIT_MIR sroa.flat.ScalarReplacementOfAggregates.diff
+pub fn flat() {
+    let Foo { a, b, c, d } = Foo { a: 5, b: (), c: "a", d: Some(-4) };
+    let _ = a;
+    let _ = b;
+    let _ = c;
+    let _ = d;
+}
+
+#[repr(C)]
+struct Escaping {
+    a: u32,
+    b: u32,
+    c: u32,
+}
+
+fn f(a: *const u32) {
+    println!("{}", unsafe { *a.add(2) });
+}
+
+// EMIT_MIR sroa.escaping.ScalarReplacementOfAggregates.diff
+pub fn escaping() {
+    // Verify this struct is not flattened.
+    f(&Escaping { a: 1, b: 2, c: g() }.a);
+}
+
+fn main() {
+    dropping();
+    enums(5);
+    structs(5.);
+    unions(5.);
+    flat();
+    escaping();
+}
diff --git a/src/test/mir-opt/sroa.structs.ScalarReplacementOfAggregates.diff b/src/test/mir-opt/sroa.structs.ScalarReplacementOfAggregates.diff
new file mode 100644
index 00000000000..69d74c351de
--- /dev/null
+++ b/src/test/mir-opt/sroa.structs.ScalarReplacementOfAggregates.diff
@@ -0,0 +1,34 @@
+- // MIR for `structs` before ScalarReplacementOfAggregates
++ // MIR for `structs` after ScalarReplacementOfAggregates
+  
+  fn structs(_1: f32) -> f32 {
+      debug a => _1;                       // in scope 0 at $DIR/sroa.rs:+0:16: +0:17
+      let mut _0: f32;                     // return place in scope 0 at $DIR/sroa.rs:+0:27: +0:30
+      let mut _2: structs::U;              // in scope 0 at $DIR/sroa.rs:+6:5: +6:21
+      let mut _3: f32;                     // in scope 0 at $DIR/sroa.rs:+6:18: +6:19
++     let mut _4: usize;                   // in scope 0 at $DIR/sroa.rs:+6:5: +6:21
++     let mut _5: f32;                     // in scope 0 at $DIR/sroa.rs:+6:5: +6:21
+  
+      bb0: {
+-         StorageLive(_2);                 // scope 0 at $DIR/sroa.rs:+6:5: +6:21
++         StorageLive(_4);                 // scope 0 at $DIR/sroa.rs:+6:5: +6:21
++         StorageLive(_5);                 // scope 0 at $DIR/sroa.rs:+6:5: +6:21
+          StorageLive(_3);                 // scope 0 at $DIR/sroa.rs:+6:18: +6:19
+          _3 = _1;                         // scope 0 at $DIR/sroa.rs:+6:18: +6:19
+-         Deinit(_2);                      // scope 0 at $DIR/sroa.rs:+6:5: +6:21
+-         (_2.0: usize) = const 0_usize;   // scope 0 at $DIR/sroa.rs:+6:5: +6:21
+-         (_2.1: f32) = move _3;           // scope 0 at $DIR/sroa.rs:+6:5: +6:21
++         Deinit(_4);                      // scope 0 at $DIR/sroa.rs:+6:5: +6:21
++         Deinit(_5);                      // scope 0 at $DIR/sroa.rs:+6:5: +6:21
++         _4 = const 0_usize;              // scope 0 at $DIR/sroa.rs:+6:5: +6:21
++         _5 = move _3;                    // scope 0 at $DIR/sroa.rs:+6:5: +6:21
+          StorageDead(_3);                 // scope 0 at $DIR/sroa.rs:+6:20: +6:21
+-         _0 = (_2.1: f32);                // scope 0 at $DIR/sroa.rs:+6:5: +6:23
+-         StorageDead(_2);                 // scope 0 at $DIR/sroa.rs:+7:1: +7:2
++         _0 = _5;                         // scope 0 at $DIR/sroa.rs:+6:5: +6:23
++         StorageDead(_4);                 // scope 0 at $DIR/sroa.rs:+7:1: +7:2
++         StorageDead(_5);                 // scope 0 at $DIR/sroa.rs:+7:1: +7:2
+          return;                          // scope 0 at $DIR/sroa.rs:+7:2: +7:2
+      }
+  }
+  
diff --git a/src/test/mir-opt/sroa.unions.ScalarReplacementOfAggregates.diff b/src/test/mir-opt/sroa.unions.ScalarReplacementOfAggregates.diff
new file mode 100644
index 00000000000..03ca976df7b
--- /dev/null
+++ b/src/test/mir-opt/sroa.unions.ScalarReplacementOfAggregates.diff
@@ -0,0 +1,24 @@
+- // MIR for `unions` before ScalarReplacementOfAggregates
++ // MIR for `unions` after ScalarReplacementOfAggregates
+  
+  fn unions(_1: f32) -> u32 {
+      debug a => _1;                       // in scope 0 at $DIR/sroa.rs:+0:15: +0:16
+      let mut _0: u32;                     // return place in scope 0 at $DIR/sroa.rs:+0:26: +0:29
+      let mut _2: unions::Repr;            // in scope 0 at $DIR/sroa.rs:+5:14: +5:27
+      let mut _3: f32;                     // in scope 0 at $DIR/sroa.rs:+5:24: +5:25
+      scope 1 {
+      }
+  
+      bb0: {
+          StorageLive(_2);                 // scope 1 at $DIR/sroa.rs:+5:14: +5:27
+          StorageLive(_3);                 // scope 1 at $DIR/sroa.rs:+5:24: +5:25
+          _3 = _1;                         // scope 1 at $DIR/sroa.rs:+5:24: +5:25
+          Deinit(_2);                      // scope 1 at $DIR/sroa.rs:+5:14: +5:27
+          (_2.0: f32) = move _3;           // scope 1 at $DIR/sroa.rs:+5:14: +5:27
+          StorageDead(_3);                 // scope 1 at $DIR/sroa.rs:+5:26: +5:27
+          _0 = (_2.1: u32);                // scope 1 at $DIR/sroa.rs:+5:14: +5:29
+          StorageDead(_2);                 // scope 0 at $DIR/sroa.rs:+6:1: +6:2
+          return;                          // scope 0 at $DIR/sroa.rs:+6:2: +6:2
+      }
+  }
+