From fdc931899d837b2dbf3d373848c50a201303193c Mon Sep 17 00:00:00 2001
From: Oliver Scherer <github35764891676564198441@oli-obk.de>
Date: Thu, 9 Jan 2020 18:36:55 +0100
Subject: [PATCH 1/2] Remove unused argument

---
 src/tools/compiletest/src/main.rs | 21 +++------------------
 1 file changed, 3 insertions(+), 18 deletions(-)

diff --git a/src/tools/compiletest/src/main.rs b/src/tools/compiletest/src/main.rs
index a61b940398704..028483b7d95b5 100644
--- a/src/tools/compiletest/src/main.rs
+++ b/src/tools/compiletest/src/main.rs
@@ -449,15 +449,8 @@ pub fn test_opts(config: &Config) -> test::TestOpts {
 pub fn make_tests(config: &Config, tests: &mut Vec<test::TestDescAndFn>) {
     debug!("making tests from {:?}", config.src_base.display());
     let inputs = common_inputs_stamp(config);
-    collect_tests_from_dir(
-        config,
-        &config.src_base,
-        &config.src_base,
-        &PathBuf::new(),
-        &inputs,
-        tests,
-    )
-    .expect(&format!("Could not read tests from {}", config.src_base.display()));
+    collect_tests_from_dir(config, &config.src_base, &PathBuf::new(), &inputs, tests)
+        .expect(&format!("Could not read tests from {}", config.src_base.display()));
 }
 
 /// Returns a stamp constructed from input files common to all test cases.
@@ -494,7 +487,6 @@ fn common_inputs_stamp(config: &Config) -> Stamp {
 
 fn collect_tests_from_dir(
     config: &Config,
-    base: &Path,
     dir: &Path,
     relative_dir_path: &Path,
     inputs: &Stamp,
@@ -538,14 +530,7 @@ fn collect_tests_from_dir(
             let relative_file_path = relative_dir_path.join(file.file_name());
             if &file_name != "auxiliary" {
                 debug!("found directory: {:?}", file_path.display());
-                collect_tests_from_dir(
-                    config,
-                    base,
-                    &file_path,
-                    &relative_file_path,
-                    inputs,
-                    tests,
-                )?;
+                collect_tests_from_dir(config, &file_path, &relative_file_path, inputs, tests)?;
             }
         } else {
             debug!("found other file/directory: {:?}", file_path.display());

From c9a5a03ffda14aaf505015b3b866c5a9f3cc6bf4 Mon Sep 17 00:00:00 2001
From: Oliver Scherer <github35764891676564198441@oli-obk.de>
Date: Wed, 11 Mar 2020 11:49:00 +0100
Subject: [PATCH 2/2] Enable `--bless`ing of MIR dumps

---
 src/librustc_data_structures/sorted_map.rs    |   4 +-
 src/librustc_mir/interpret/memory.rs          |  96 ++----
 src/librustc_mir/util/pretty.rs               | 282 ++++++++++++++++--
 src/test/mir-opt/README.md                    |  42 ++-
 src/test/mir-opt/basic_assignment.rs          |  34 +--
 .../rustc.main.SimplifyCfg-initial.after.mir  |  90 ++++++
 .../mir-opt/const-promotion-extern-static.rs  |  66 +---
 .../rustc.BAR-promoted[0].ConstProp.after.mir |  26 ++
 .../rustc.BAR.PromoteTemps.diff               |  62 ++++
 .../rustc.FOO-promoted[0].ConstProp.after.mir |  26 ++
 .../rustc.FOO.PromoteTemps.diff               |  62 ++++
 src/test/mir-opt/const_allocation.rs          |   9 +
 .../32bit/rustc.main.ConstProp.after.mir      |  65 ++++
 .../64bit/rustc.main.ConstProp.after.mir      |  69 +++++
 src/test/mir-opt/const_allocation2.rs         |  11 +
 .../32bit/rustc.main.ConstProp.after.mir      |  64 ++++
 .../64bit/rustc.main.ConstProp.after.mir      |  67 +++++
 src/test/mir-opt/const_allocation3.rs         |  29 ++
 .../32bit/rustc.main.ConstProp.after.mir      |  58 ++++
 .../64bit/rustc.main.ConstProp.after.mir      |  59 ++++
 src/test/mir-opt/const_prop/cast.rs           |  46 +--
 .../const_prop/cast/rustc.main.ConstProp.diff |  48 +++
 src/tools/compiletest/src/runtest.rs          | 139 +++++++--
 23 files changed, 1198 insertions(+), 256 deletions(-)
 create mode 100644 src/test/mir-opt/basic_assignment/rustc.main.SimplifyCfg-initial.after.mir
 create mode 100644 src/test/mir-opt/const-promotion-extern-static/rustc.BAR-promoted[0].ConstProp.after.mir
 create mode 100644 src/test/mir-opt/const-promotion-extern-static/rustc.BAR.PromoteTemps.diff
 create mode 100644 src/test/mir-opt/const-promotion-extern-static/rustc.FOO-promoted[0].ConstProp.after.mir
 create mode 100644 src/test/mir-opt/const-promotion-extern-static/rustc.FOO.PromoteTemps.diff
 create mode 100644 src/test/mir-opt/const_allocation.rs
 create mode 100644 src/test/mir-opt/const_allocation/32bit/rustc.main.ConstProp.after.mir
 create mode 100644 src/test/mir-opt/const_allocation/64bit/rustc.main.ConstProp.after.mir
 create mode 100644 src/test/mir-opt/const_allocation2.rs
 create mode 100644 src/test/mir-opt/const_allocation2/32bit/rustc.main.ConstProp.after.mir
 create mode 100644 src/test/mir-opt/const_allocation2/64bit/rustc.main.ConstProp.after.mir
 create mode 100644 src/test/mir-opt/const_allocation3.rs
 create mode 100644 src/test/mir-opt/const_allocation3/32bit/rustc.main.ConstProp.after.mir
 create mode 100644 src/test/mir-opt/const_allocation3/64bit/rustc.main.ConstProp.after.mir
 create mode 100644 src/test/mir-opt/const_prop/cast/rustc.main.ConstProp.diff

diff --git a/src/librustc_data_structures/sorted_map.rs b/src/librustc_data_structures/sorted_map.rs
index 8c42b74b85aef..652268dcee884 100644
--- a/src/librustc_data_structures/sorted_map.rs
+++ b/src/librustc_data_structures/sorted_map.rs
@@ -110,13 +110,13 @@ impl<K: Ord, V> SortedMap<K, V> {
 
     /// Iterate over the keys, sorted
     #[inline]
-    pub fn keys(&self) -> impl Iterator<Item = &K> + ExactSizeIterator {
+    pub fn keys(&self) -> impl Iterator<Item = &K> + ExactSizeIterator + DoubleEndedIterator {
         self.data.iter().map(|&(ref k, _)| k)
     }
 
     /// Iterate over values, sorted by key
     #[inline]
-    pub fn values(&self) -> impl Iterator<Item = &V> + ExactSizeIterator {
+    pub fn values(&self) -> impl Iterator<Item = &V> + ExactSizeIterator + DoubleEndedIterator {
         self.data.iter().map(|&(_, ref v)| v)
     }
 
diff --git a/src/librustc_mir/interpret/memory.rs b/src/librustc_mir/interpret/memory.rs
index 8437399752e29..df66ab3f419da 100644
--- a/src/librustc_mir/interpret/memory.rs
+++ b/src/librustc_mir/interpret/memory.rs
@@ -649,58 +649,15 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
         &self,
         allocs_seen: &mut FxHashSet<AllocId>,
         allocs_to_print: &mut VecDeque<AllocId>,
-        mut msg: String,
         alloc: &Allocation<Tag, Extra>,
-        extra: String,
     ) {
-        use std::fmt::Write;
-
-        let prefix_len = msg.len();
-        let mut relocations = vec![];
-
-        for i in 0..alloc.size.bytes() {
-            let i = Size::from_bytes(i);
-            if let Some(&(_, target_id)) = alloc.relocations().get(&i) {
-                if allocs_seen.insert(target_id) {
-                    allocs_to_print.push_back(target_id);
-                }
-                relocations.push((i, target_id));
-            }
-            if alloc.undef_mask().is_range_defined(i, i + Size::from_bytes(1)).is_ok() {
-                // this `as usize` is fine, since `i` came from a `usize`
-                let i = i.bytes_usize();
-
-                // Checked definedness (and thus range) and relocations. This access also doesn't
-                // influence interpreter execution but is only for debugging.
-                let bytes = alloc.inspect_with_undef_and_ptr_outside_interpreter(i..i + 1);
-                write!(msg, "{:02x} ", bytes[0]).unwrap();
-            } else {
-                msg.push_str("__ ");
+        for &(_, (_, target_id)) in alloc.relocations().iter() {
+            if allocs_seen.insert(target_id) {
+                allocs_to_print.push_back(target_id);
             }
         }
-
-        eprintln!(
-            "{}({} bytes, alignment {}){}",
-            msg,
-            alloc.size.bytes(),
-            alloc.align.bytes(),
-            extra
-        );
-
-        if !relocations.is_empty() {
-            msg.clear();
-            write!(msg, "{:1$}", "", prefix_len).unwrap(); // Print spaces.
-            let mut pos = Size::ZERO;
-            let relocation_width = (self.pointer_size().bytes() - 1) * 3;
-            for (i, target_id) in relocations {
-                write!(msg, "{:1$}", "", ((i - pos) * 3).bytes_usize()).unwrap();
-                let target = format!("({})", target_id);
-                // this `as usize` is fine, since we can't print more chars than `usize::MAX`
-                write!(msg, "└{0:─^1$}┘ ", target, relocation_width as usize).unwrap();
-                pos = i + self.pointer_size();
-            }
-            eprintln!("{}", msg);
-        }
+        crate::util::pretty::write_allocation(self.tcx.tcx, alloc, &mut std::io::stderr(), "")
+            .unwrap();
     }
 
     /// Print a list of allocations and all allocations they point to, recursively.
@@ -713,45 +670,42 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
         let mut allocs_seen = FxHashSet::default();
 
         while let Some(id) = allocs_to_print.pop_front() {
-            let msg = format!("Alloc {:<5} ", format!("{}:", id));
+            eprint!("Alloc {:<5}: ", id);
+            fn msg<Tag, Extra>(alloc: &Allocation<Tag, Extra>, extra: &str) {
+                eprintln!(
+                    "({} bytes, alignment {}){}",
+                    alloc.size.bytes(),
+                    alloc.align.bytes(),
+                    extra
+                )
+            };
 
             // normal alloc?
             match self.alloc_map.get_or(id, || Err(())) {
                 Ok((kind, alloc)) => {
-                    let extra = match kind {
-                        MemoryKind::Stack => " (stack)".to_owned(),
-                        MemoryKind::Vtable => " (vtable)".to_owned(),
-                        MemoryKind::CallerLocation => " (caller_location)".to_owned(),
-                        MemoryKind::Machine(m) => format!(" ({:?})", m),
+                    match kind {
+                        MemoryKind::Stack => msg(alloc, " (stack)"),
+                        MemoryKind::Vtable => msg(alloc, " (vtable)"),
+                        MemoryKind::CallerLocation => msg(alloc, " (caller_location)"),
+                        MemoryKind::Machine(m) => msg(alloc, &format!(" ({:?})", m)),
                     };
-                    self.dump_alloc_helper(
-                        &mut allocs_seen,
-                        &mut allocs_to_print,
-                        msg,
-                        alloc,
-                        extra,
-                    );
+                    self.dump_alloc_helper(&mut allocs_seen, &mut allocs_to_print, alloc);
                 }
                 Err(()) => {
                     // global alloc?
                     match self.tcx.alloc_map.lock().get(id) {
                         Some(GlobalAlloc::Memory(alloc)) => {
-                            self.dump_alloc_helper(
-                                &mut allocs_seen,
-                                &mut allocs_to_print,
-                                msg,
-                                alloc,
-                                " (immutable)".to_owned(),
-                            );
+                            msg(alloc, " (immutable)");
+                            self.dump_alloc_helper(&mut allocs_seen, &mut allocs_to_print, alloc);
                         }
                         Some(GlobalAlloc::Function(func)) => {
-                            eprintln!("{} {}", msg, func);
+                            eprintln!("{}", func);
                         }
                         Some(GlobalAlloc::Static(did)) => {
-                            eprintln!("{} {:?}", msg, did);
+                            eprintln!("{:?}", did);
                         }
                         None => {
-                            eprintln!("{} (deallocated)", msg);
+                            eprintln!("(deallocated)");
                         }
                     }
                 }
diff --git a/src/librustc_mir/util/pretty.rs b/src/librustc_mir/util/pretty.rs
index 4c380d1141806..0368a73832d75 100644
--- a/src/librustc_mir/util/pretty.rs
+++ b/src/librustc_mir/util/pretty.rs
@@ -1,11 +1,14 @@
 use super::graphviz::write_mir_fn_graphviz;
 use crate::transform::MirSource;
+use either::Either;
+use rustc::mir::interpret::{read_target_uint, AllocId, Allocation, ConstValue, GlobalAlloc};
 use rustc::mir::visit::Visitor;
 use rustc::mir::*;
-use rustc::ty::{self, TyCtxt};
+use rustc::ty::{self, layout::Size, TyCtxt, TypeFoldable, TypeVisitor};
 use rustc_data_structures::fx::FxHashMap;
 use rustc_hir::def_id::{DefId, LOCAL_CRATE};
 use rustc_index::vec::Idx;
+use std::collections::BTreeSet;
 use std::fmt::Display;
 use std::fmt::Write as _;
 use std::fs;
@@ -77,20 +80,7 @@ pub fn dump_mir<'tcx, F>(
         return;
     }
 
-    let node_path = ty::print::with_forced_impl_filename_line(|| {
-        // see notes on #41697 below
-        tcx.def_path_str(source.def_id())
-    });
-    dump_matched_mir_node(
-        tcx,
-        pass_num,
-        pass_name,
-        &node_path,
-        disambiguator,
-        source,
-        body,
-        extra_data,
-    );
+    dump_matched_mir_node(tcx, pass_num, pass_name, disambiguator, source, body, extra_data);
 }
 
 pub fn dump_enabled<'tcx>(tcx: TyCtxt<'tcx>, pass_name: &str, source: MirSource<'tcx>) -> bool {
@@ -117,7 +107,6 @@ fn dump_matched_mir_node<'tcx, F>(
     tcx: TyCtxt<'tcx>,
     pass_num: Option<&dyn Display>,
     pass_name: &str,
-    node_path: &str,
     disambiguator: &dyn Display,
     source: MirSource<'tcx>,
     body: &Body<'tcx>,
@@ -127,10 +116,16 @@ fn dump_matched_mir_node<'tcx, F>(
 {
     let _: io::Result<()> = try {
         let mut file = create_dump_file(tcx, "mir", pass_num, pass_name, disambiguator, source)?;
-        writeln!(file, "// MIR for `{}`", node_path)?;
-        writeln!(file, "// source = {:?}", source)?;
-        writeln!(file, "// pass_name = {}", pass_name)?;
-        writeln!(file, "// disambiguator = {}", disambiguator)?;
+        let def_path = ty::print::with_forced_impl_filename_line(|| {
+            // see notes on #41697 above
+            tcx.def_path_str(source.def_id())
+        });
+        write!(file, "// MIR for `{}", def_path)?;
+        match source.promoted {
+            None => write!(file, "`")?,
+            Some(promoted) => write!(file, "::{:?}`", promoted)?,
+        }
+        writeln!(file, " {} {}", disambiguator, pass_name)?;
         if let Some(ref layout) = body.generator_layout {
             writeln!(file, "// generator_layout = {:?}", layout)?;
         }
@@ -276,6 +271,9 @@ where
     }
 
     writeln!(w, "}}")?;
+
+    write_allocations(tcx, body, w)?;
+
     Ok(())
 }
 
@@ -534,6 +532,250 @@ pub fn write_mir_intro<'tcx>(
     Ok(())
 }
 
+/// Find all `AllocId`s mentioned (recursively) in the MIR body and print their corresponding
+/// allocations.
+pub fn write_allocations<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    body: &Body<'_>,
+    w: &mut dyn Write,
+) -> io::Result<()> {
+    fn alloc_ids_from_alloc(alloc: &Allocation) -> impl DoubleEndedIterator<Item = AllocId> + '_ {
+        alloc.relocations().values().map(|(_, id)| *id)
+    }
+    fn alloc_ids_from_const(val: ConstValue<'_>) -> impl Iterator<Item = AllocId> + '_ {
+        match val {
+            ConstValue::Scalar(interpret::Scalar::Ptr(ptr)) => {
+                Either::Left(Either::Left(std::iter::once(ptr.alloc_id)))
+            }
+            ConstValue::Scalar(interpret::Scalar::Raw { .. }) => {
+                Either::Left(Either::Right(std::iter::empty()))
+            }
+            ConstValue::ByRef { alloc, .. } | ConstValue::Slice { data: alloc, .. } => {
+                Either::Right(alloc_ids_from_alloc(alloc))
+            }
+        }
+    }
+    struct CollectAllocIds(BTreeSet<AllocId>);
+    impl<'tcx> TypeVisitor<'tcx> for CollectAllocIds {
+        fn visit_const(&mut self, c: &'tcx ty::Const<'tcx>) -> bool {
+            if let ty::ConstKind::Value(val) = c.val {
+                self.0.extend(alloc_ids_from_const(val));
+            }
+            c.super_visit_with(self)
+        }
+    }
+    let mut visitor = CollectAllocIds(Default::default());
+    body.visit_with(&mut visitor);
+    let mut seen = visitor.0;
+    let mut todo: Vec<_> = seen.iter().copied().collect();
+    while let Some(id) = todo.pop() {
+        let mut write_header_and_allocation =
+            |w: &mut dyn Write, alloc: &Allocation| -> io::Result<()> {
+                write!(w, "size: {}, align: {})", alloc.size.bytes(), alloc.align.bytes())?;
+                if alloc.size == Size::ZERO {
+                    write!(w, " {{}}")?;
+                } else {
+                    writeln!(w, " {{")?;
+                    write_allocation(tcx, alloc, w, "    ")?;
+                    write!(w, "}}")?;
+                    // `.rev()` because we are popping them from the back of the `todo` vector.
+                    for id in alloc_ids_from_alloc(alloc).rev() {
+                        if seen.insert(id) {
+                            todo.push(id);
+                        }
+                    }
+                }
+                Ok(())
+            };
+        write!(w, "\n{}", id)?;
+        let alloc = tcx.alloc_map.lock().get(id);
+        match alloc {
+            // This can't really happen unless there are bugs, but it doesn't cost us anything to
+            // gracefully handle it and allow buggy rustc to be debugged via allocation printing.
+            None => write!(w, " (deallocated)")?,
+            Some(GlobalAlloc::Function(inst)) => write!(w, " (fn: {})", inst)?,
+            Some(GlobalAlloc::Static(did)) if !tcx.is_foreign_item(did) => {
+                match tcx.const_eval_poly(did) {
+                    Ok(ConstValue::ByRef { alloc, .. }) => {
+                        write!(w, " (static: {}, ", tcx.def_path_str(did))?;
+                        write_header_and_allocation(w, alloc)?;
+                    }
+                    Ok(_) => {
+                        span_bug!(tcx.def_span(did), " static item without `ByRef` initializer")
+                    }
+                    Err(_) => write!(
+                        w,
+                        " (static: {}, error during initializer evaluation)",
+                        tcx.def_path_str(did)
+                    )?,
+                }
+            }
+            Some(GlobalAlloc::Static(did)) => {
+                write!(w, " (extern static: {})", tcx.def_path_str(did))?
+            }
+            Some(GlobalAlloc::Memory(alloc)) => {
+                write!(w, " (")?;
+                write_header_and_allocation(w, alloc)?
+            }
+        }
+
+        writeln!(w)?;
+    }
+    Ok(())
+}
+
+fn write_allocation_endline(w: &mut dyn Write, ascii: &str) -> io::Result<()> {
+    for _ in 0..(BYTES_PER_LINE - ascii.chars().count()) {
+        write!(w, "   ")?;
+    }
+    writeln!(w, " │ {}", ascii)
+}
+
+/// Number of bytes to print per allocation hex dump line.
+const BYTES_PER_LINE: usize = 16;
+
+/// Prints the line start address and returns the new line start address.
+fn write_allocation_newline(
+    w: &mut dyn Write,
+    mut line_start: Size,
+    ascii: &str,
+    pos_width: usize,
+    prefix: &str,
+) -> io::Result<Size> {
+    write_allocation_endline(w, ascii)?;
+    line_start += Size::from_bytes(BYTES_PER_LINE);
+    write!(w, "{}0x{:02$x} │ ", prefix, line_start.bytes(), pos_width)?;
+    Ok(line_start)
+}
+
+/// Dumps the bytes of an allocation to the given writer. This also prints relocations instead of
+/// the raw bytes where applicable.
+/// The byte format is similar to how hex editors print bytes. Each line starts with the address of
+/// the start of the line, followed by all bytes in hex format (space separated).
+/// If the allocation is small enough to fit into a single line, no start address is given.
+/// After the hex dump, an ascii dump follows, replacing all unprintable characters (control
+/// characters or characters whose value is larger than 127) with a `.`
+///
+/// The `prefix` argument allows callers to add an arbitrary prefix before each line (even if there
+/// is only one line). Note that your prefix should contain a trailing space as the lines are
+/// printed directly after it.
+pub fn write_allocation<Tag, Extra>(
+    tcx: TyCtxt<'tcx>,
+    alloc: &Allocation<Tag, Extra>,
+    w: &mut dyn Write,
+    prefix: &str,
+) -> io::Result<()> {
+    let num_lines = alloc.size.bytes_usize().saturating_sub(BYTES_PER_LINE);
+    // Number of chars needed to represent all line numbers.
+    let pos_width = format!("{:x}", alloc.size.bytes()).len();
+
+    if num_lines > 0 {
+        write!(w, "{}0x{:02$x} │ ", prefix, 0, pos_width)?;
+    } else {
+        write!(w, "{}", prefix)?;
+    }
+
+    let mut i = Size::ZERO;
+    let mut line_start = Size::ZERO;
+
+    let ptr_size = tcx.data_layout.pointer_size;
+
+    let mut ascii = String::new();
+
+    let oversized_ptr = |target: &mut String, width| {
+        if target.len() > width {
+            write!(target, " ({} ptr bytes)", ptr_size.bytes()).unwrap();
+        }
+    };
+
+    while i < alloc.size {
+        // The line start already has a space. While we could remove that space from the line start
+        // printing and unconditionally print a space here, that would cause the single-line case
+        // to have a single space before it, which looks weird.
+        if i != line_start {
+            write!(w, " ")?;
+        }
+        if let Some(&(_, target_id)) = alloc.relocations().get(&i) {
+            // Memory with a relocation must be defined
+            let j = i.bytes_usize();
+            let offset =
+                alloc.inspect_with_undef_and_ptr_outside_interpreter(j..j + ptr_size.bytes_usize());
+            let offset = read_target_uint(tcx.data_layout.endian, offset).unwrap();
+            let relocation_width = |bytes| bytes * 3;
+            let mut target = format!("{}+{}", target_id, offset);
+            if ((i - line_start) + ptr_size).bytes_usize() > BYTES_PER_LINE {
+                // This branch handles the situation where a relocation starts in the current line
+                // but ends in the next one.
+                let remainder = Size::from_bytes(BYTES_PER_LINE) - (i - line_start);
+                let overflow = ptr_size - remainder;
+                let remainder_width = relocation_width(remainder.bytes_usize()) - 2;
+                let overflow_width = relocation_width(overflow.bytes_usize() - 1) + 1;
+                ascii.push('╾');
+                for _ in 0..remainder.bytes() - 1 {
+                    ascii.push('─');
+                }
+                if overflow_width > remainder_width && overflow_width >= target.len() {
+                    // The case where the relocation fits into the part in the next line
+                    write!(w, "╾{0:─^1$}", "", remainder_width)?;
+                    line_start =
+                        write_allocation_newline(w, line_start, &ascii, pos_width, prefix)?;
+                    ascii.clear();
+                    write!(w, "{0:─^1$}╼", target, overflow_width)?;
+                } else {
+                    oversized_ptr(&mut target, remainder_width);
+                    write!(w, "╾{0:─^1$}", target, remainder_width)?;
+                    line_start =
+                        write_allocation_newline(w, line_start, &ascii, pos_width, prefix)?;
+                    write!(w, "{0:─^1$}╼", "", overflow_width)?;
+                    ascii.clear();
+                }
+                for _ in 0..overflow.bytes() - 1 {
+                    ascii.push('─');
+                }
+                ascii.push('╼');
+                i += ptr_size;
+                continue;
+            } else {
+                // This branch handles a relocation that starts and ends in the current line.
+                let relocation_width = relocation_width(ptr_size.bytes_usize() - 1);
+                oversized_ptr(&mut target, relocation_width);
+                ascii.push('╾');
+                write!(w, "╾{0:─^1$}╼", target, relocation_width)?;
+                for _ in 0..ptr_size.bytes() - 2 {
+                    ascii.push('─');
+                }
+                ascii.push('╼');
+                i += ptr_size;
+            }
+        } else if alloc.undef_mask().is_range_defined(i, i + Size::from_bytes(1)).is_ok() {
+            let j = i.bytes_usize();
+
+            // Checked definedness (and thus range) and relocations. This access also doesn't
+            // influence interpreter execution but is only for debugging.
+            let c = alloc.inspect_with_undef_and_ptr_outside_interpreter(j..j + 1)[0];
+            write!(w, "{:02x}", c)?;
+            if c.is_ascii_control() || c >= 0x80 {
+                ascii.push('.');
+            } else {
+                ascii.push(char::from(c));
+            }
+            i += Size::from_bytes(1);
+        } else {
+            write!(w, "__")?;
+            ascii.push('░');
+            i += Size::from_bytes(1);
+        }
+        // Print a new line header if the next line still has some bytes to print.
+        if i == line_start + Size::from_bytes(BYTES_PER_LINE) && i != alloc.size {
+            line_start = write_allocation_newline(w, line_start, &ascii, pos_width, prefix)?;
+            ascii.clear();
+        }
+    }
+    write_allocation_endline(w, &ascii)?;
+
+    Ok(())
+}
+
 fn write_mir_sig(
     tcx: TyCtxt<'_>,
     src: MirSource<'tcx>,
diff --git a/src/test/mir-opt/README.md b/src/test/mir-opt/README.md
index ad4932b9fb945..a6f86d4e384ae 100644
--- a/src/test/mir-opt/README.md
+++ b/src/test/mir-opt/README.md
@@ -1,6 +1,46 @@
 This folder contains tests for MIR optimizations.
 
-The test format is:
+There are two test formats. One allows specifying a pattern to look for in the MIR, which also
+permits leaving placeholders, but requires you to manually change the pattern if anything changes.
+The other emits MIR to extra files that you can automatically update by specifying `--bless` on
+the command line (just like `ui` tests updating `.stderr` files).
+
+# `--bless`able test format
+
+By default 32 bit and 64 bit targets use the same dump files, which can be problematic in the
+presence of pointers in constants or other bit width dependent things. In that case you can add
+
+```
+// EMIT_MIR_FOR_EACH_BIT_WIDTH
+```
+
+to your test, causing separate files to be generated for 32bit and 64bit systems.
+
+## Emit a diff of the mir for a specific optimization
+
+This is what you want most often when you want to see how an optimization changes the MIR.
+
+```
+// EMIT_MIR $file_name_of_some_mir_dump.diff
+```
+
+## Emit mir after a specific optimization
+
+Use this if you are just interested in the final state after an optimization.
+
+```
+// EMIT_MIR $file_name_of_some_mir_dump.after.mir
+```
+
+## Emit mir before a specific optimization
+
+This exists mainly for completeness and is rarely useful.
+
+```
+// EMIT_MIR $file_name_of_some_mir_dump.before.mir
+```
+
+# Inline test format
 
 ```
 (arbitrary rust code)
diff --git a/src/test/mir-opt/basic_assignment.rs b/src/test/mir-opt/basic_assignment.rs
index ca0e9fa811a26..17141b6334c82 100644
--- a/src/test/mir-opt/basic_assignment.rs
+++ b/src/test/mir-opt/basic_assignment.rs
@@ -1,5 +1,7 @@
 // this tests move up progration, which is not yet implemented
 
+// EMIT_MIR rustc.main.SimplifyCfg-initial.after.mir
+
 // Check codegen for assignments (`a = b`) where the left-hand-side is
 // not yet initialized. Assignments tend to be absent in simple code,
 // so subtle breakage in them can leave a quite hard-to-find trail of
@@ -13,40 +15,10 @@ fn main() {
     // assignment:
     nodrop_y = nodrop_x;
 
-    let drop_x : Option<Box<u32>> = None;
+    let drop_x: Option<Box<u32>> = None;
     let drop_y;
 
     // Since the type of `drop_y` has drop, we generate a `replace`
     // terminator:
     drop_y = drop_x;
 }
-
-// END RUST SOURCE
-// START rustc.main.SimplifyCfg-initial.after.mir
-//    bb0: {
-//        StorageLive(_1);
-//        _1 = const false;
-//        FakeRead(ForLet, _1);
-//        StorageLive(_2);
-//        StorageLive(_3);
-//        _3 = _1;
-//        _2 = move _3;
-//        StorageDead(_3);
-//        StorageLive(_4);
-//        _4 = std::option::Option::<std::boxed::Box<u32>>::None;
-//        FakeRead(ForLet, _4);
-//        AscribeUserType(_4, o, UserTypeProjection { base: UserType(1), projs: [] });
-//        StorageLive(_5);
-//        StorageLive(_6);
-//        _6 = move _4;
-//        replace(_5 <- move _6) -> [return: bb2, unwind: bb5];
-//    }
-//    ...
-//    bb2: {
-//        drop(_6) -> [return: bb6, unwind: bb4];
-//    }
-//    ...
-//    bb5 (cleanup): {
-//        drop(_6) -> bb4;
-//    }
-// END rustc.main.SimplifyCfg-initial.after.mir
diff --git a/src/test/mir-opt/basic_assignment/rustc.main.SimplifyCfg-initial.after.mir b/src/test/mir-opt/basic_assignment/rustc.main.SimplifyCfg-initial.after.mir
new file mode 100644
index 0000000000000..a3f4573964c3d
--- /dev/null
+++ b/src/test/mir-opt/basic_assignment/rustc.main.SimplifyCfg-initial.after.mir
@@ -0,0 +1,90 @@
+// MIR for `main` after SimplifyCfg-initial
+
+| User Type Annotations
+| 0: Canonical { max_universe: U0, variables: [], value: Ty(std::option::Option<std::boxed::Box<u32>>) } at $DIR/basic_assignment.rs:18:17: 18:33
+| 1: Canonical { max_universe: U0, variables: [], value: Ty(std::option::Option<std::boxed::Box<u32>>) } at $DIR/basic_assignment.rs:18:17: 18:33
+|
+fn main() -> () {
+    let mut _0: ();                      // return place in scope 0 at $DIR/basic_assignment.rs:10:11: 10:11
+    let _1: bool;                        // in scope 0 at $DIR/basic_assignment.rs:11:9: 11:17
+    let mut _3: bool;                    // in scope 0 at $DIR/basic_assignment.rs:16:16: 16:24
+    let mut _6: std::option::Option<std::boxed::Box<u32>>; // in scope 0 at $DIR/basic_assignment.rs:23:14: 23:20
+    scope 1 {
+        debug nodrop_x => _1;            // in scope 1 at $DIR/basic_assignment.rs:11:9: 11:17
+        let _2: bool;                    // in scope 1 at $DIR/basic_assignment.rs:12:9: 12:17
+        scope 2 {
+            debug nodrop_y => _2;        // in scope 2 at $DIR/basic_assignment.rs:12:9: 12:17
+            let _4: std::option::Option<std::boxed::Box<u32>> as UserTypeProjection { base: UserType(0), projs: [] }; // in scope 2 at $DIR/basic_assignment.rs:18:9: 18:15
+            scope 3 {
+                debug drop_x => _4;      // in scope 3 at $DIR/basic_assignment.rs:18:9: 18:15
+                let _5: std::option::Option<std::boxed::Box<u32>>; // in scope 3 at $DIR/basic_assignment.rs:19:9: 19:15
+                scope 4 {
+                    debug drop_y => _5;  // in scope 4 at $DIR/basic_assignment.rs:19:9: 19:15
+                }
+            }
+        }
+    }
+
+    bb0: {
+        StorageLive(_1);                 // bb0[0]: scope 0 at $DIR/basic_assignment.rs:11:9: 11:17
+        _1 = const false;                // bb0[1]: scope 0 at $DIR/basic_assignment.rs:11:20: 11:25
+                                         // ty::Const
+                                         // + ty: bool
+                                         // + val: Value(Scalar(0x00))
+                                         // mir::Constant
+                                         // + span: $DIR/basic_assignment.rs:11:20: 11:25
+                                         // + literal: Const { ty: bool, val: Value(Scalar(0x00)) }
+        FakeRead(ForLet, _1);            // bb0[2]: scope 0 at $DIR/basic_assignment.rs:11:9: 11:17
+        StorageLive(_2);                 // bb0[3]: scope 1 at $DIR/basic_assignment.rs:12:9: 12:17
+        StorageLive(_3);                 // bb0[4]: scope 2 at $DIR/basic_assignment.rs:16:16: 16:24
+        _3 = _1;                         // bb0[5]: scope 2 at $DIR/basic_assignment.rs:16:16: 16:24
+        _2 = move _3;                    // bb0[6]: scope 2 at $DIR/basic_assignment.rs:16:5: 16:24
+        StorageDead(_3);                 // bb0[7]: scope 2 at $DIR/basic_assignment.rs:16:23: 16:24
+        StorageLive(_4);                 // bb0[8]: scope 2 at $DIR/basic_assignment.rs:18:9: 18:15
+        _4 = std::option::Option::<std::boxed::Box<u32>>::None; // bb0[9]: scope 2 at $DIR/basic_assignment.rs:18:36: 18:40
+        FakeRead(ForLet, _4);            // bb0[10]: scope 2 at $DIR/basic_assignment.rs:18:9: 18:15
+        AscribeUserType(_4, o, UserTypeProjection { base: UserType(1), projs: [] }); // bb0[11]: scope 2 at $DIR/basic_assignment.rs:18:17: 18:33
+        StorageLive(_5);                 // bb0[12]: scope 3 at $DIR/basic_assignment.rs:19:9: 19:15
+        StorageLive(_6);                 // bb0[13]: scope 4 at $DIR/basic_assignment.rs:23:14: 23:20
+        _6 = move _4;                    // bb0[14]: scope 4 at $DIR/basic_assignment.rs:23:14: 23:20
+        replace(_5 <- move _6) -> [return: bb2, unwind: bb5]; // bb0[15]: scope 4 at $DIR/basic_assignment.rs:23:5: 23:11
+    }
+
+    bb1 (cleanup): {
+        resume;                          // bb1[0]: scope 0 at $DIR/basic_assignment.rs:10:1: 24:2
+    }
+
+    bb2: {
+        drop(_6) -> [return: bb6, unwind: bb4]; // bb2[0]: scope 4 at $DIR/basic_assignment.rs:23:19: 23:20
+    }
+
+    bb3 (cleanup): {
+        drop(_4) -> bb1;                 // bb3[0]: scope 2 at $DIR/basic_assignment.rs:24:1: 24:2
+    }
+
+    bb4 (cleanup): {
+        drop(_5) -> bb3;                 // bb4[0]: scope 3 at $DIR/basic_assignment.rs:24:1: 24:2
+    }
+
+    bb5 (cleanup): {
+        drop(_6) -> bb4;                 // bb5[0]: scope 4 at $DIR/basic_assignment.rs:23:19: 23:20
+    }
+
+    bb6: {
+        StorageDead(_6);                 // bb6[0]: scope 4 at $DIR/basic_assignment.rs:23:19: 23:20
+        _0 = ();                         // bb6[1]: scope 0 at $DIR/basic_assignment.rs:10:11: 24:2
+        drop(_5) -> [return: bb7, unwind: bb3]; // bb6[2]: scope 3 at $DIR/basic_assignment.rs:24:1: 24:2
+    }
+
+    bb7: {
+        StorageDead(_5);                 // bb7[0]: scope 3 at $DIR/basic_assignment.rs:24:1: 24:2
+        drop(_4) -> [return: bb8, unwind: bb1]; // bb7[1]: scope 2 at $DIR/basic_assignment.rs:24:1: 24:2
+    }
+
+    bb8: {
+        StorageDead(_4);                 // bb8[0]: scope 2 at $DIR/basic_assignment.rs:24:1: 24:2
+        StorageDead(_2);                 // bb8[1]: scope 1 at $DIR/basic_assignment.rs:24:1: 24:2
+        StorageDead(_1);                 // bb8[2]: scope 0 at $DIR/basic_assignment.rs:24:1: 24:2
+        return;                          // bb8[3]: scope 0 at $DIR/basic_assignment.rs:24:2: 24:2
+    }
+}
diff --git a/src/test/mir-opt/const-promotion-extern-static.rs b/src/test/mir-opt/const-promotion-extern-static.rs
index c858a4c5ee7c6..c9d350a98fd9c 100644
--- a/src/test/mir-opt/const-promotion-extern-static.rs
+++ b/src/test/mir-opt/const-promotion-extern-static.rs
@@ -4,70 +4,12 @@ extern "C" {
 
 static Y: i32 = 42;
 
+// EMIT_MIR rustc.BAR.PromoteTemps.diff
+// EMIT_MIR rustc.BAR-promoted[0].ConstProp.after.mir
 static mut BAR: *const &i32 = [&Y].as_ptr();
 
+// EMIT_MIR rustc.FOO.PromoteTemps.diff
+// EMIT_MIR rustc.FOO-promoted[0].ConstProp.after.mir
 static mut FOO: *const &i32 = [unsafe { &X }].as_ptr();
 
 fn main() {}
-
-// END RUST SOURCE
-// START rustc.FOO.PromoteTemps.before.mir
-// bb0: {
-// ...
-//     _5 = const {alloc1+0: &i32};
-//     _4 = &(*_5);
-//     _3 = [move _4];
-//     _2 = &_3;
-//     _1 = move _2 as &[&i32] (Pointer(Unsize));
-//     _0 = const core::slice::<impl [&i32]>::as_ptr(move _1) -> [return: bb2, unwind: bb1];
-// }
-// ...
-// bb2: {
-//     StorageDead(_5);
-//     StorageDead(_3);
-//     return;
-// }
-// END rustc.FOO.PromoteTemps.before.mir
-// START rustc.BAR.PromoteTemps.before.mir
-// bb0: {
-// ...
-//     _5 = const {alloc0+0: &i32};
-//     _4 = &(*_5);
-//     _3 = [move _4];
-//     _2 = &_3;
-//     _1 = move _2 as &[&i32] (Pointer(Unsize));
-//     _0 = const core::slice::<impl [&i32]>::as_ptr(move _1) -> [return: bb2, unwind: bb1];
-// }
-// ...
-// bb2: {
-//     StorageDead(_5);
-//     StorageDead(_3);
-//     return;
-// }
-// END rustc.BAR.PromoteTemps.before.mir
-// START rustc.BAR.PromoteTemps.after.mir
-// bb0: {
-// ...
-//     _6 = const BAR::promoted[0];
-//     _2 = &(*_6);
-//     _1 = move _2 as &[&i32] (Pointer(Unsize));
-//     _0 = const core::slice::<impl [&i32]>::as_ptr(move _1) -> [return: bb2, unwind: bb1];
-// }
-// ...
-// bb2: {
-//     return;
-// }
-// END rustc.BAR.PromoteTemps.after.mir
-// START rustc.FOO.PromoteTemps.after.mir
-// bb0: {
-// ...
-//     _6 = const FOO::promoted[0];
-//     _2 = &(*_6);
-//     _1 = move _2 as &[&i32] (Pointer(Unsize));
-//     _0 = const core::slice::<impl [&i32]>::as_ptr(move _1) -> [return: bb2, unwind: bb1];
-// }
-// ...
-// bb2: {
-//     return;
-// }
-// END rustc.FOO.PromoteTemps.after.mir
diff --git a/src/test/mir-opt/const-promotion-extern-static/rustc.BAR-promoted[0].ConstProp.after.mir b/src/test/mir-opt/const-promotion-extern-static/rustc.BAR-promoted[0].ConstProp.after.mir
new file mode 100644
index 0000000000000..256018adaa89b
--- /dev/null
+++ b/src/test/mir-opt/const-promotion-extern-static/rustc.BAR-promoted[0].ConstProp.after.mir
@@ -0,0 +1,26 @@
+// MIR for `BAR::promoted[0]` after ConstProp
+
+promoted[0] in BAR: &[&i32; 1] = {
+    let mut _0: &[&i32; 1];              // return place in scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35
+    let mut _1: [&i32; 1];               // in scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35
+    let mut _2: &i32;                    // in scope 0 at $DIR/const-promotion-extern-static.rs:9:32: 9:34
+    let mut _3: &i32;                    // in scope 0 at $DIR/const-promotion-extern-static.rs:9:33: 9:34
+
+    bb0: {
+        _3 = const {alloc0+0: &i32};     // bb0[0]: scope 0 at $DIR/const-promotion-extern-static.rs:9:33: 9:34
+                                         // ty::Const
+                                         // + ty: &i32
+                                         // + val: Value(Scalar(alloc0+0))
+                                         // mir::Constant
+                                         // + span: $DIR/const-promotion-extern-static.rs:9:33: 9:34
+                                         // + literal: Const { ty: &i32, val: Value(Scalar(alloc0+0)) }
+        _2 = _3;                         // bb0[1]: scope 0 at $DIR/const-promotion-extern-static.rs:9:32: 9:34
+        _1 = [move _2];                  // bb0[2]: scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35
+        _0 = &_1;                        // bb0[3]: scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35
+        return;                          // bb0[4]: scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35
+    }
+}
+
+alloc0 (static: Y, size: 4, align: 4) {
+    2a 00 00 00                                     │ *...
+}
diff --git a/src/test/mir-opt/const-promotion-extern-static/rustc.BAR.PromoteTemps.diff b/src/test/mir-opt/const-promotion-extern-static/rustc.BAR.PromoteTemps.diff
new file mode 100644
index 0000000000000..bc5b114418e42
--- /dev/null
+++ b/src/test/mir-opt/const-promotion-extern-static/rustc.BAR.PromoteTemps.diff
@@ -0,0 +1,62 @@
+- // MIR for `BAR` before PromoteTemps
++ // MIR for `BAR` after PromoteTemps
+  
+  static mut BAR: *const &i32 = {
+      let mut _0: *const &i32;             // return place in scope 0 at $DIR/const-promotion-extern-static.rs:9:17: 9:28
+      let mut _1: &[&i32];                 // in scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35
+      let mut _2: &[&i32; 1];              // in scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35
+      let _3: [&i32; 1];                   // in scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35
+      let mut _4: &i32;                    // in scope 0 at $DIR/const-promotion-extern-static.rs:9:32: 9:34
+      let _5: &i32;                        // in scope 0 at $DIR/const-promotion-extern-static.rs:9:33: 9:34
++     let mut _6: &[&i32; 1];              // in scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35
+  
+      bb0: {
+          StorageLive(_1);                 // bb0[0]: scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35
+          StorageLive(_2);                 // bb0[1]: scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35
+-         StorageLive(_3);                 // bb0[2]: scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35
+-         StorageLive(_4);                 // bb0[3]: scope 0 at $DIR/const-promotion-extern-static.rs:9:32: 9:34
+-         StorageLive(_5);                 // bb0[4]: scope 0 at $DIR/const-promotion-extern-static.rs:9:33: 9:34
+-         _5 = const {alloc0+0: &i32};     // bb0[5]: scope 0 at $DIR/const-promotion-extern-static.rs:9:33: 9:34
++         _6 = const BAR::promoted[0];     // bb0[2]: scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35
+                                           // ty::Const
+-                                          // + ty: &i32
+-                                          // + val: Value(Scalar(alloc0+0))
++                                          // + ty: &[&i32; 1]
++                                          // + val: Unevaluated(DefId(0:6 ~ const_promotion_extern_static[317d]::BAR[0]), [], Some(promoted[0]))
+                                           // mir::Constant
+-                                          // + span: $DIR/const-promotion-extern-static.rs:9:33: 9:34
+-                                          // + literal: Const { ty: &i32, val: Value(Scalar(alloc0+0)) }
+-         _4 = &(*_5);                     // bb0[6]: scope 0 at $DIR/const-promotion-extern-static.rs:9:32: 9:34
+-         _3 = [move _4];                  // bb0[7]: scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35
+-         _2 = &_3;                        // bb0[8]: scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35
+-         _1 = move _2 as &[&i32] (Pointer(Unsize)); // bb0[9]: scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35
+-         _0 = const core::slice::<impl [&i32]>::as_ptr(move _1) -> [return: bb2, unwind: bb1]; // bb0[10]: scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:44
++                                          // + span: $DIR/const-promotion-extern-static.rs:9:31: 9:35
++                                          // + literal: Const { ty: &[&i32; 1], val: Unevaluated(DefId(0:6 ~ const_promotion_extern_static[317d]::BAR[0]), [], Some(promoted[0])) }
++         _2 = &(*_6);                     // bb0[3]: scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35
++         _1 = move _2 as &[&i32] (Pointer(Unsize)); // bb0[4]: scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:35
++         _0 = const core::slice::<impl [&i32]>::as_ptr(move _1) -> [return: bb2, unwind: bb1]; // bb0[5]: scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:44
+                                           // ty::Const
+                                           // + ty: for<'r> fn(&'r [&i32]) -> *const &i32 {core::slice::<impl [&i32]>::as_ptr}
+                                           // + val: Value(Scalar(<ZST>))
+                                           // mir::Constant
+                                           // + span: $DIR/const-promotion-extern-static.rs:9:36: 9:42
+                                           // + literal: Const { ty: for<'r> fn(&'r [&i32]) -> *const &i32 {core::slice::<impl [&i32]>::as_ptr}, val: Value(Scalar(<ZST>)) }
+      }
+  
+      bb1 (cleanup): {
+          resume;                          // bb1[0]: scope 0 at $DIR/const-promotion-extern-static.rs:9:1: 9:45
+      }
+  
+      bb2: {
+-         StorageDead(_5);                 // bb2[0]: scope 0 at $DIR/const-promotion-extern-static.rs:9:43: 9:44
+-         StorageDead(_3);                 // bb2[1]: scope 0 at $DIR/const-promotion-extern-static.rs:9:43: 9:44
+-         return;                          // bb2[2]: scope 0 at $DIR/const-promotion-extern-static.rs:9:1: 9:45
++         return;                          // bb2[0]: scope 0 at $DIR/const-promotion-extern-static.rs:9:1: 9:45
+      }
+- }
+- 
+- alloc0 (static: Y, size: 4, align: 4) {
+-     2a 00 00 00                                     │ *...
+  }
+  
diff --git a/src/test/mir-opt/const-promotion-extern-static/rustc.FOO-promoted[0].ConstProp.after.mir b/src/test/mir-opt/const-promotion-extern-static/rustc.FOO-promoted[0].ConstProp.after.mir
new file mode 100644
index 0000000000000..5bbc05a9cab7c
--- /dev/null
+++ b/src/test/mir-opt/const-promotion-extern-static/rustc.FOO-promoted[0].ConstProp.after.mir
@@ -0,0 +1,26 @@
+// MIR for `FOO::promoted[0]` after ConstProp
+
+promoted[0] in FOO: &[&i32; 1] = {
+    let mut _0: &[&i32; 1];              // return place in scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46
+    let mut _1: [&i32; 1];               // in scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46
+    let mut _2: &i32;                    // in scope 0 at $DIR/const-promotion-extern-static.rs:13:32: 13:45
+    let mut _3: &i32;                    // in scope 0 at $DIR/const-promotion-extern-static.rs:13:42: 13:43
+    scope 1 {
+    }
+
+    bb0: {
+        _3 = const {alloc2+0: &i32};     // bb0[0]: scope 0 at $DIR/const-promotion-extern-static.rs:13:42: 13:43
+                                         // ty::Const
+                                         // + ty: &i32
+                                         // + val: Value(Scalar(alloc2+0))
+                                         // mir::Constant
+                                         // + span: $DIR/const-promotion-extern-static.rs:13:42: 13:43
+                                         // + literal: Const { ty: &i32, val: Value(Scalar(alloc2+0)) }
+        _2 = _3;                         // bb0[1]: scope 0 at $DIR/const-promotion-extern-static.rs:13:41: 13:43
+        _1 = [move _2];                  // bb0[2]: scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46
+        _0 = &_1;                        // bb0[3]: scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46
+        return;                          // bb0[4]: scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46
+    }
+}
+
+alloc2 (extern static: X)
diff --git a/src/test/mir-opt/const-promotion-extern-static/rustc.FOO.PromoteTemps.diff b/src/test/mir-opt/const-promotion-extern-static/rustc.FOO.PromoteTemps.diff
new file mode 100644
index 0000000000000..7df1a47b2f497
--- /dev/null
+++ b/src/test/mir-opt/const-promotion-extern-static/rustc.FOO.PromoteTemps.diff
@@ -0,0 +1,62 @@
+- // MIR for `FOO` before PromoteTemps
++ // MIR for `FOO` after PromoteTemps
+  
+  static mut FOO: *const &i32 = {
+      let mut _0: *const &i32;             // return place in scope 0 at $DIR/const-promotion-extern-static.rs:13:17: 13:28
+      let mut _1: &[&i32];                 // in scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46
+      let mut _2: &[&i32; 1];              // in scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46
+      let _3: [&i32; 1];                   // in scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46
+      let mut _4: &i32;                    // in scope 0 at $DIR/const-promotion-extern-static.rs:13:32: 13:45
+      let _5: &i32;                        // in scope 0 at $DIR/const-promotion-extern-static.rs:13:42: 13:43
++     let mut _6: &[&i32; 1];              // in scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46
+      scope 1 {
+      }
+  
+      bb0: {
+          StorageLive(_1);                 // bb0[0]: scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46
+          StorageLive(_2);                 // bb0[1]: scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46
+-         StorageLive(_3);                 // bb0[2]: scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46
+-         StorageLive(_4);                 // bb0[3]: scope 0 at $DIR/const-promotion-extern-static.rs:13:32: 13:45
+-         StorageLive(_5);                 // bb0[4]: scope 1 at $DIR/const-promotion-extern-static.rs:13:42: 13:43
+-         _5 = const {alloc2+0: &i32};     // bb0[5]: scope 1 at $DIR/const-promotion-extern-static.rs:13:42: 13:43
++         _6 = const FOO::promoted[0];     // bb0[2]: scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46
+                                           // ty::Const
+-                                          // + ty: &i32
+-                                          // + val: Value(Scalar(alloc2+0))
++                                          // + ty: &[&i32; 1]
++                                          // + val: Unevaluated(DefId(0:7 ~ const_promotion_extern_static[317d]::FOO[0]), [], Some(promoted[0]))
+                                           // mir::Constant
+-                                          // + span: $DIR/const-promotion-extern-static.rs:13:42: 13:43
+-                                          // + literal: Const { ty: &i32, val: Value(Scalar(alloc2+0)) }
+-         _4 = &(*_5);                     // bb0[6]: scope 1 at $DIR/const-promotion-extern-static.rs:13:41: 13:43
+-         _3 = [move _4];                  // bb0[7]: scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46
+-         _2 = &_3;                        // bb0[8]: scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46
+-         _1 = move _2 as &[&i32] (Pointer(Unsize)); // bb0[9]: scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46
+-         _0 = const core::slice::<impl [&i32]>::as_ptr(move _1) -> [return: bb2, unwind: bb1]; // bb0[10]: scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:55
++                                          // + span: $DIR/const-promotion-extern-static.rs:13:31: 13:46
++                                          // + literal: Const { ty: &[&i32; 1], val: Unevaluated(DefId(0:7 ~ const_promotion_extern_static[317d]::FOO[0]), [], Some(promoted[0])) }
++         _2 = &(*_6);                     // bb0[3]: scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46
++         _1 = move _2 as &[&i32] (Pointer(Unsize)); // bb0[4]: scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:46
++         _0 = const core::slice::<impl [&i32]>::as_ptr(move _1) -> [return: bb2, unwind: bb1]; // bb0[5]: scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:55
+                                           // ty::Const
+                                           // + ty: for<'r> fn(&'r [&i32]) -> *const &i32 {core::slice::<impl [&i32]>::as_ptr}
+                                           // + val: Value(Scalar(<ZST>))
+                                           // mir::Constant
+                                           // + span: $DIR/const-promotion-extern-static.rs:13:47: 13:53
+                                           // + literal: Const { ty: for<'r> fn(&'r [&i32]) -> *const &i32 {core::slice::<impl [&i32]>::as_ptr}, val: Value(Scalar(<ZST>)) }
+      }
+  
+      bb1 (cleanup): {
+          resume;                          // bb1[0]: scope 0 at $DIR/const-promotion-extern-static.rs:13:1: 13:56
+      }
+  
+      bb2: {
+-         StorageDead(_5);                 // bb2[0]: scope 0 at $DIR/const-promotion-extern-static.rs:13:54: 13:55
+-         StorageDead(_3);                 // bb2[1]: scope 0 at $DIR/const-promotion-extern-static.rs:13:54: 13:55
+-         return;                          // bb2[2]: scope 0 at $DIR/const-promotion-extern-static.rs:13:1: 13:56
++         return;                          // bb2[0]: scope 0 at $DIR/const-promotion-extern-static.rs:13:1: 13:56
+      }
+  }
+- 
+- alloc2 (extern static: X)
+  
diff --git a/src/test/mir-opt/const_allocation.rs b/src/test/mir-opt/const_allocation.rs
new file mode 100644
index 0000000000000..aaf996ee8e1a7
--- /dev/null
+++ b/src/test/mir-opt/const_allocation.rs
@@ -0,0 +1,9 @@
+// EMIT_MIR_FOR_EACH_BIT_WIDTH
+
+static FOO: &[(Option<i32>, &[&str])] =
+    &[(None, &[]), (None, &["foo", "bar"]), (Some(42), &["meh", "mop", "möp"])];
+
+// EMIT_MIR rustc.main.ConstProp.after.mir
+fn main() {
+    FOO;
+}
diff --git a/src/test/mir-opt/const_allocation/32bit/rustc.main.ConstProp.after.mir b/src/test/mir-opt/const_allocation/32bit/rustc.main.ConstProp.after.mir
new file mode 100644
index 0000000000000..5880672450878
--- /dev/null
+++ b/src/test/mir-opt/const_allocation/32bit/rustc.main.ConstProp.after.mir
@@ -0,0 +1,65 @@
+// MIR for `main` after ConstProp
+
+fn main() -> () {
+    let mut _0: ();                      // return place in scope 0 at $DIR/const_allocation.rs:7:11: 7:11
+    let _1: &[(std::option::Option<i32>, &[&str])]; // in scope 0 at $DIR/const_allocation.rs:8:5: 8:8
+    let mut _2: &&[(std::option::Option<i32>, &[&str])]; // in scope 0 at $DIR/const_allocation.rs:8:5: 8:8
+
+    bb0: {
+        StorageLive(_1);                 // bb0[0]: scope 0 at $DIR/const_allocation.rs:8:5: 8:8
+        StorageLive(_2);                 // bb0[1]: scope 0 at $DIR/const_allocation.rs:8:5: 8:8
+        _2 = const {alloc0+0: &&[(std::option::Option<i32>, &[&str])]}; // bb0[2]: scope 0 at $DIR/const_allocation.rs:8:5: 8:8
+                                         // ty::Const
+                                         // + ty: &&[(std::option::Option<i32>, &[&str])]
+                                         // + val: Value(Scalar(alloc0+0))
+                                         // mir::Constant
+                                         // + span: $DIR/const_allocation.rs:8:5: 8:8
+                                         // + literal: Const { ty: &&[(std::option::Option<i32>, &[&str])], val: Value(Scalar(alloc0+0)) }
+        _1 = (*_2);                      // bb0[3]: scope 0 at $DIR/const_allocation.rs:8:5: 8:8
+        StorageDead(_2);                 // bb0[4]: scope 0 at $DIR/const_allocation.rs:8:8: 8:9
+        StorageDead(_1);                 // bb0[5]: scope 0 at $DIR/const_allocation.rs:8:8: 8:9
+        _0 = ();                         // bb0[6]: scope 0 at $DIR/const_allocation.rs:7:11: 9:2
+        return;                          // bb0[7]: scope 0 at $DIR/const_allocation.rs:9:2: 9:2
+    }
+}
+
+alloc0 (static: FOO, size: 8, align: 4) {
+    ╾alloc17+0╼ 03 00 00 00                         │ ╾──╼....
+}
+
+alloc17 (size: 48, align: 4) {
+    0x00 │ 00 00 00 00 __ __ __ __ ╾alloc4+0─╼ 00 00 00 00 │ ....░░░░╾──╼....
+    0x10 │ 00 00 00 00 __ __ __ __ ╾alloc8+0─╼ 02 00 00 00 │ ....░░░░╾──╼....
+    0x20 │ 01 00 00 00 2a 00 00 00 ╾alloc13+0╼ 03 00 00 00 │ ....*...╾──╼....
+}
+
+alloc4 (size: 0, align: 4) {}
+
+alloc8 (size: 16, align: 4) {
+    ╾alloc7+0─╼ 03 00 00 00 ╾alloc9+0─╼ 03 00 00 00 │ ╾──╼....╾──╼....
+}
+
+alloc7 (size: 3, align: 1) {
+    66 6f 6f                                        │ foo
+}
+
+alloc9 (size: 3, align: 1) {
+    62 61 72                                        │ bar
+}
+
+alloc13 (size: 24, align: 4) {
+    0x00 │ ╾alloc12+0╼ 03 00 00 00 ╾alloc14+0╼ 03 00 00 00 │ ╾──╼....╾──╼....
+    0x10 │ ╾alloc15+0╼ 04 00 00 00                         │ ╾──╼....
+}
+
+alloc12 (size: 3, align: 1) {
+    6d 65 68                                        │ meh
+}
+
+alloc14 (size: 3, align: 1) {
+    6d 6f 70                                        │ mop
+}
+
+alloc15 (size: 4, align: 1) {
+    6d c3 b6 70                                     │ m..p
+}
diff --git a/src/test/mir-opt/const_allocation/64bit/rustc.main.ConstProp.after.mir b/src/test/mir-opt/const_allocation/64bit/rustc.main.ConstProp.after.mir
new file mode 100644
index 0000000000000..b60d23aa3f117
--- /dev/null
+++ b/src/test/mir-opt/const_allocation/64bit/rustc.main.ConstProp.after.mir
@@ -0,0 +1,69 @@
+// MIR for `main` after ConstProp
+
+fn main() -> () {
+    let mut _0: ();                      // return place in scope 0 at $DIR/const_allocation.rs:7:11: 7:11
+    let _1: &[(std::option::Option<i32>, &[&str])]; // in scope 0 at $DIR/const_allocation.rs:8:5: 8:8
+    let mut _2: &&[(std::option::Option<i32>, &[&str])]; // in scope 0 at $DIR/const_allocation.rs:8:5: 8:8
+
+    bb0: {
+        StorageLive(_1);                 // bb0[0]: scope 0 at $DIR/const_allocation.rs:8:5: 8:8
+        StorageLive(_2);                 // bb0[1]: scope 0 at $DIR/const_allocation.rs:8:5: 8:8
+        _2 = const {alloc0+0: &&[(std::option::Option<i32>, &[&str])]}; // bb0[2]: scope 0 at $DIR/const_allocation.rs:8:5: 8:8
+                                         // ty::Const
+                                         // + ty: &&[(std::option::Option<i32>, &[&str])]
+                                         // + val: Value(Scalar(alloc0+0))
+                                         // mir::Constant
+                                         // + span: $DIR/const_allocation.rs:8:5: 8:8
+                                         // + literal: Const { ty: &&[(std::option::Option<i32>, &[&str])], val: Value(Scalar(alloc0+0)) }
+        _1 = (*_2);                      // bb0[3]: scope 0 at $DIR/const_allocation.rs:8:5: 8:8
+        StorageDead(_2);                 // bb0[4]: scope 0 at $DIR/const_allocation.rs:8:8: 8:9
+        StorageDead(_1);                 // bb0[5]: scope 0 at $DIR/const_allocation.rs:8:8: 8:9
+        _0 = ();                         // bb0[6]: scope 0 at $DIR/const_allocation.rs:7:11: 9:2
+        return;                          // bb0[7]: scope 0 at $DIR/const_allocation.rs:9:2: 9:2
+    }
+}
+
+alloc0 (static: FOO, size: 16, align: 8) {
+    ╾──────alloc17+0──────╼ 03 00 00 00 00 00 00 00 │ ╾──────╼........
+}
+
+alloc17 (size: 72, align: 8) {
+    0x00 │ 00 00 00 00 __ __ __ __ ╾──────alloc4+0───────╼ │ ....░░░░╾──────╼
+    0x10 │ 00 00 00 00 00 00 00 00 00 00 00 00 __ __ __ __ │ ............░░░░
+    0x20 │ ╾──────alloc8+0───────╼ 02 00 00 00 00 00 00 00 │ ╾──────╼........
+    0x30 │ 01 00 00 00 2a 00 00 00 ╾──────alloc13+0──────╼ │ ....*...╾──────╼
+    0x40 │ 03 00 00 00 00 00 00 00                         │ ........
+}
+
+alloc4 (size: 0, align: 8) {}
+
+alloc8 (size: 32, align: 8) {
+    0x00 │ ╾──────alloc7+0───────╼ 03 00 00 00 00 00 00 00 │ ╾──────╼........
+    0x10 │ ╾──────alloc9+0───────╼ 03 00 00 00 00 00 00 00 │ ╾──────╼........
+}
+
+alloc7 (size: 3, align: 1) {
+    66 6f 6f                                        │ foo
+}
+
+alloc9 (size: 3, align: 1) {
+    62 61 72                                        │ bar
+}
+
+alloc13 (size: 48, align: 8) {
+    0x00 │ ╾──────alloc12+0──────╼ 03 00 00 00 00 00 00 00 │ ╾──────╼........
+    0x10 │ ╾──────alloc14+0──────╼ 03 00 00 00 00 00 00 00 │ ╾──────╼........
+    0x20 │ ╾──────alloc15+0──────╼ 04 00 00 00 00 00 00 00 │ ╾──────╼........
+}
+
+alloc12 (size: 3, align: 1) {
+    6d 65 68                                        │ meh
+}
+
+alloc14 (size: 3, align: 1) {
+    6d 6f 70                                        │ mop
+}
+
+alloc15 (size: 4, align: 1) {
+    6d c3 b6 70                                     │ m..p
+}
diff --git a/src/test/mir-opt/const_allocation2.rs b/src/test/mir-opt/const_allocation2.rs
new file mode 100644
index 0000000000000..ca61b84c0bcad
--- /dev/null
+++ b/src/test/mir-opt/const_allocation2.rs
@@ -0,0 +1,11 @@
+// EMIT_MIR_FOR_EACH_BIT_WIDTH
+
+// EMIT_MIR rustc.main.ConstProp.after.mir
+fn main() {
+    FOO;
+}
+
+const BAR: [u8; 4] = [42, 69, 21, 111];
+
+static FOO: &[(Option<i32>, &[&u8])] =
+    &[(None, &[]), (None, &[&5, &6]), (Some(42), &[&BAR[3], &42, &BAR[2]])];
diff --git a/src/test/mir-opt/const_allocation2/32bit/rustc.main.ConstProp.after.mir b/src/test/mir-opt/const_allocation2/32bit/rustc.main.ConstProp.after.mir
new file mode 100644
index 0000000000000..6caecf1eceac0
--- /dev/null
+++ b/src/test/mir-opt/const_allocation2/32bit/rustc.main.ConstProp.after.mir
@@ -0,0 +1,64 @@
+// MIR for `main` after ConstProp
+
+fn main() -> () {
+    let mut _0: ();                      // return place in scope 0 at $DIR/const_allocation2.rs:4:11: 4:11
+    let _1: &[(std::option::Option<i32>, &[&u8])]; // in scope 0 at $DIR/const_allocation2.rs:5:5: 5:8
+    let mut _2: &&[(std::option::Option<i32>, &[&u8])]; // in scope 0 at $DIR/const_allocation2.rs:5:5: 5:8
+
+    bb0: {
+        StorageLive(_1);                 // bb0[0]: scope 0 at $DIR/const_allocation2.rs:5:5: 5:8
+        StorageLive(_2);                 // bb0[1]: scope 0 at $DIR/const_allocation2.rs:5:5: 5:8
+        _2 = const {alloc0+0: &&[(std::option::Option<i32>, &[&u8])]}; // bb0[2]: scope 0 at $DIR/const_allocation2.rs:5:5: 5:8
+                                         // ty::Const
+                                         // + ty: &&[(std::option::Option<i32>, &[&u8])]
+                                         // + val: Value(Scalar(alloc0+0))
+                                         // mir::Constant
+                                         // + span: $DIR/const_allocation2.rs:5:5: 5:8
+                                         // + literal: Const { ty: &&[(std::option::Option<i32>, &[&u8])], val: Value(Scalar(alloc0+0)) }
+        _1 = (*_2);                      // bb0[3]: scope 0 at $DIR/const_allocation2.rs:5:5: 5:8
+        StorageDead(_2);                 // bb0[4]: scope 0 at $DIR/const_allocation2.rs:5:8: 5:9
+        StorageDead(_1);                 // bb0[5]: scope 0 at $DIR/const_allocation2.rs:5:8: 5:9
+        _0 = ();                         // bb0[6]: scope 0 at $DIR/const_allocation2.rs:4:11: 6:2
+        return;                          // bb0[7]: scope 0 at $DIR/const_allocation2.rs:6:2: 6:2
+    }
+}
+
+alloc0 (static: FOO, size: 8, align: 4) {
+    ╾alloc24+0╼ 03 00 00 00                         │ ╾──╼....
+}
+
+alloc24 (size: 48, align: 4) {
+    0x00 │ 00 00 00 00 __ __ __ __ ╾alloc9+0─╼ 00 00 00 00 │ ....░░░░╾──╼....
+    0x10 │ 00 00 00 00 __ __ __ __ ╾alloc14+0╼ 02 00 00 00 │ ....░░░░╾──╼....
+    0x20 │ 01 00 00 00 2a 00 00 00 ╾alloc22+0╼ 03 00 00 00 │ ....*...╾──╼....
+}
+
+alloc9 (size: 0, align: 4) {}
+
+alloc14 (size: 8, align: 4) {
+    ╾alloc12+0╼ ╾alloc13+0╼                         │ ╾──╼╾──╼
+}
+
+alloc12 (size: 1, align: 1) {
+    05                                              │ .
+}
+
+alloc13 (size: 1, align: 1) {
+    06                                              │ .
+}
+
+alloc22 (size: 12, align: 4) {
+    ╾alloc18+3╼ ╾alloc19+0╼ ╾alloc21+2╼             │ ╾──╼╾──╼╾──╼
+}
+
+alloc18 (size: 4, align: 1) {
+    2a 45 15 6f                                     │ *E.o
+}
+
+alloc19 (size: 1, align: 1) {
+    2a                                              │ *
+}
+
+alloc21 (size: 4, align: 1) {
+    2a 45 15 6f                                     │ *E.o
+}
diff --git a/src/test/mir-opt/const_allocation2/64bit/rustc.main.ConstProp.after.mir b/src/test/mir-opt/const_allocation2/64bit/rustc.main.ConstProp.after.mir
new file mode 100644
index 0000000000000..52e7c2aec2b12
--- /dev/null
+++ b/src/test/mir-opt/const_allocation2/64bit/rustc.main.ConstProp.after.mir
@@ -0,0 +1,67 @@
+// MIR for `main` after ConstProp
+
+fn main() -> () {
+    let mut _0: ();                      // return place in scope 0 at $DIR/const_allocation2.rs:4:11: 4:11
+    let _1: &[(std::option::Option<i32>, &[&u8])]; // in scope 0 at $DIR/const_allocation2.rs:5:5: 5:8
+    let mut _2: &&[(std::option::Option<i32>, &[&u8])]; // in scope 0 at $DIR/const_allocation2.rs:5:5: 5:8
+
+    bb0: {
+        StorageLive(_1);                 // bb0[0]: scope 0 at $DIR/const_allocation2.rs:5:5: 5:8
+        StorageLive(_2);                 // bb0[1]: scope 0 at $DIR/const_allocation2.rs:5:5: 5:8
+        _2 = const {alloc0+0: &&[(std::option::Option<i32>, &[&u8])]}; // bb0[2]: scope 0 at $DIR/const_allocation2.rs:5:5: 5:8
+                                         // ty::Const
+                                         // + ty: &&[(std::option::Option<i32>, &[&u8])]
+                                         // + val: Value(Scalar(alloc0+0))
+                                         // mir::Constant
+                                         // + span: $DIR/const_allocation2.rs:5:5: 5:8
+                                         // + literal: Const { ty: &&[(std::option::Option<i32>, &[&u8])], val: Value(Scalar(alloc0+0)) }
+        _1 = (*_2);                      // bb0[3]: scope 0 at $DIR/const_allocation2.rs:5:5: 5:8
+        StorageDead(_2);                 // bb0[4]: scope 0 at $DIR/const_allocation2.rs:5:8: 5:9
+        StorageDead(_1);                 // bb0[5]: scope 0 at $DIR/const_allocation2.rs:5:8: 5:9
+        _0 = ();                         // bb0[6]: scope 0 at $DIR/const_allocation2.rs:4:11: 6:2
+        return;                          // bb0[7]: scope 0 at $DIR/const_allocation2.rs:6:2: 6:2
+    }
+}
+
+alloc0 (static: FOO, size: 16, align: 8) {
+    ╾──────alloc24+0──────╼ 03 00 00 00 00 00 00 00 │ ╾──────╼........
+}
+
+alloc24 (size: 72, align: 8) {
+    0x00 │ 00 00 00 00 __ __ __ __ ╾──────alloc9+0───────╼ │ ....░░░░╾──────╼
+    0x10 │ 00 00 00 00 00 00 00 00 00 00 00 00 __ __ __ __ │ ............░░░░
+    0x20 │ ╾──────alloc14+0──────╼ 02 00 00 00 00 00 00 00 │ ╾──────╼........
+    0x30 │ 01 00 00 00 2a 00 00 00 ╾──────alloc22+0──────╼ │ ....*...╾──────╼
+    0x40 │ 03 00 00 00 00 00 00 00                         │ ........
+}
+
+alloc9 (size: 0, align: 8) {}
+
+alloc14 (size: 16, align: 8) {
+    ╾──────alloc12+0──────╼ ╾──────alloc13+0──────╼ │ ╾──────╼╾──────╼
+}
+
+alloc12 (size: 1, align: 1) {
+    05                                              │ .
+}
+
+alloc13 (size: 1, align: 1) {
+    06                                              │ .
+}
+
+alloc22 (size: 24, align: 8) {
+    0x00 │ ╾──────alloc18+3──────╼ ╾──────alloc19+0──────╼ │ ╾──────╼╾──────╼
+    0x10 │ ╾──────alloc21+2──────╼                         │ ╾──────╼
+}
+
+alloc18 (size: 4, align: 1) {
+    2a 45 15 6f                                     │ *E.o
+}
+
+alloc19 (size: 1, align: 1) {
+    2a                                              │ *
+}
+
+alloc21 (size: 4, align: 1) {
+    2a 45 15 6f                                     │ *E.o
+}
diff --git a/src/test/mir-opt/const_allocation3.rs b/src/test/mir-opt/const_allocation3.rs
new file mode 100644
index 0000000000000..73bb58e1a9892
--- /dev/null
+++ b/src/test/mir-opt/const_allocation3.rs
@@ -0,0 +1,29 @@
+// EMIT_MIR_FOR_EACH_BIT_WIDTH
+
+// EMIT_MIR rustc.main.ConstProp.after.mir
+fn main() {
+    FOO;
+}
+
+#[repr(packed)]
+struct Packed {
+    a: [u8; 28],
+    b: &'static i32,
+    c: u32,
+    d: [u8; 102],
+    e: fn(),
+    f: u16,
+    g: &'static u8,
+    h: [u8; 20],
+}
+
+static FOO: &Packed = &Packed {
+    a: [0xAB; 28],
+    b: &42,
+    c: 0xABCD_EF01,
+    d: [0; 102],
+    e: main,
+    f: 0,
+    g: &[0; 100][99],
+    h: [0; 20],
+};
diff --git a/src/test/mir-opt/const_allocation3/32bit/rustc.main.ConstProp.after.mir b/src/test/mir-opt/const_allocation3/32bit/rustc.main.ConstProp.after.mir
new file mode 100644
index 0000000000000..4fc2c4c3fb3dd
--- /dev/null
+++ b/src/test/mir-opt/const_allocation3/32bit/rustc.main.ConstProp.after.mir
@@ -0,0 +1,58 @@
+// MIR for `main` after ConstProp
+
+fn main() -> () {
+    let mut _0: ();                      // return place in scope 0 at $DIR/const_allocation3.rs:4:11: 4:11
+    let _1: &Packed;                     // in scope 0 at $DIR/const_allocation3.rs:5:5: 5:8
+    let mut _2: &&Packed;                // in scope 0 at $DIR/const_allocation3.rs:5:5: 5:8
+
+    bb0: {
+        StorageLive(_1);                 // bb0[0]: scope 0 at $DIR/const_allocation3.rs:5:5: 5:8
+        StorageLive(_2);                 // bb0[1]: scope 0 at $DIR/const_allocation3.rs:5:5: 5:8
+        _2 = const {alloc0+0: &&Packed}; // bb0[2]: scope 0 at $DIR/const_allocation3.rs:5:5: 5:8
+                                         // ty::Const
+                                         // + ty: &&Packed
+                                         // + val: Value(Scalar(alloc0+0))
+                                         // mir::Constant
+                                         // + span: $DIR/const_allocation3.rs:5:5: 5:8
+                                         // + literal: Const { ty: &&Packed, val: Value(Scalar(alloc0+0)) }
+        _1 = (*_2);                      // bb0[3]: scope 0 at $DIR/const_allocation3.rs:5:5: 5:8
+        StorageDead(_2);                 // bb0[4]: scope 0 at $DIR/const_allocation3.rs:5:8: 5:9
+        StorageDead(_1);                 // bb0[5]: scope 0 at $DIR/const_allocation3.rs:5:8: 5:9
+        _0 = ();                         // bb0[6]: scope 0 at $DIR/const_allocation3.rs:4:11: 6:2
+        return;                          // bb0[7]: scope 0 at $DIR/const_allocation3.rs:6:2: 6:2
+    }
+}
+
+alloc0 (static: FOO, size: 4, align: 4) {
+    ╾alloc10+0╼                                     │ ╾──╼
+}
+
+alloc10 (size: 168, align: 1) {
+    0x00 │ ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab │ ................
+    0x10 │ ab ab ab ab ab ab ab ab ab ab ab ab ╾alloc5+0─╼ │ ............╾──╼
+    0x20 │ 01 ef cd ab 00 00 00 00 00 00 00 00 00 00 00 00 │ ................
+    0x30 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................
+    0x40 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................
+    0x50 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................
+    0x60 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................
+    0x70 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................
+    0x80 │ 00 00 00 00 00 00 00 00 00 00 ╾alloc7+0─╼ 00 00 │ ..........╾──╼..
+    0x90 │ ╾alloc8+99╼ 00 00 00 00 00 00 00 00 00 00 00 00 │ ╾──╼............
+    0xa0 │ 00 00 00 00 00 00 00 00                         │ ........
+}
+
+alloc5 (size: 4, align: 4) {
+    2a 00 00 00                                     │ *...
+}
+
+alloc7 (fn: main)
+
+alloc8 (size: 100, align: 1) {
+    0x00 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................
+    0x10 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................
+    0x20 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................
+    0x30 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................
+    0x40 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................
+    0x50 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................
+    0x60 │ 00 00 00 00                                     │ ....
+}
diff --git a/src/test/mir-opt/const_allocation3/64bit/rustc.main.ConstProp.after.mir b/src/test/mir-opt/const_allocation3/64bit/rustc.main.ConstProp.after.mir
new file mode 100644
index 0000000000000..ae5ebe7043706
--- /dev/null
+++ b/src/test/mir-opt/const_allocation3/64bit/rustc.main.ConstProp.after.mir
@@ -0,0 +1,59 @@
+// MIR for `main` after ConstProp
+
+fn main() -> () {
+    let mut _0: ();                      // return place in scope 0 at $DIR/const_allocation3.rs:4:11: 4:11
+    let _1: &Packed;                     // in scope 0 at $DIR/const_allocation3.rs:5:5: 5:8
+    let mut _2: &&Packed;                // in scope 0 at $DIR/const_allocation3.rs:5:5: 5:8
+
+    bb0: {
+        StorageLive(_1);                 // bb0[0]: scope 0 at $DIR/const_allocation3.rs:5:5: 5:8
+        StorageLive(_2);                 // bb0[1]: scope 0 at $DIR/const_allocation3.rs:5:5: 5:8
+        _2 = const {alloc0+0: &&Packed}; // bb0[2]: scope 0 at $DIR/const_allocation3.rs:5:5: 5:8
+                                         // ty::Const
+                                         // + ty: &&Packed
+                                         // + val: Value(Scalar(alloc0+0))
+                                         // mir::Constant
+                                         // + span: $DIR/const_allocation3.rs:5:5: 5:8
+                                         // + literal: Const { ty: &&Packed, val: Value(Scalar(alloc0+0)) }
+        _1 = (*_2);                      // bb0[3]: scope 0 at $DIR/const_allocation3.rs:5:5: 5:8
+        StorageDead(_2);                 // bb0[4]: scope 0 at $DIR/const_allocation3.rs:5:8: 5:9
+        StorageDead(_1);                 // bb0[5]: scope 0 at $DIR/const_allocation3.rs:5:8: 5:9
+        _0 = ();                         // bb0[6]: scope 0 at $DIR/const_allocation3.rs:4:11: 6:2
+        return;                          // bb0[7]: scope 0 at $DIR/const_allocation3.rs:6:2: 6:2
+    }
+}
+
+alloc0 (static: FOO, size: 8, align: 8) {
+    ╾──────alloc10+0──────╼                         │ ╾──────╼
+}
+
+alloc10 (size: 180, align: 1) {
+    0x00 │ ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab │ ................
+    0x10 │ ab ab ab ab ab ab ab ab ab ab ab ab ╾─alloc5+0─ │ ............╾───
+    0x20 │ ──────────╼ 01 ef cd ab 00 00 00 00 00 00 00 00 │ ───╼............
+    0x30 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................
+    0x40 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................
+    0x50 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................
+    0x60 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................
+    0x70 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................
+    0x80 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ╾──── │ ..............╾─
+    0x90 │ ────alloc7+0────╼ 00 00 ╾──────alloc8+99──────╼ │ ─────╼..╾──────╼
+    0xa0 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................
+    0xb0 │ 00 00 00 00                                     │ ....
+}
+
+alloc5 (size: 4, align: 4) {
+    2a 00 00 00                                     │ *...
+}
+
+alloc7 (fn: main)
+
+alloc8 (size: 100, align: 1) {
+    0x00 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................
+    0x10 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................
+    0x20 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................
+    0x30 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................
+    0x40 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................
+    0x50 │ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │ ................
+    0x60 │ 00 00 00 00                                     │ ....
+}
diff --git a/src/test/mir-opt/const_prop/cast.rs b/src/test/mir-opt/const_prop/cast.rs
index 9cfbfebdcc3df..2af5f32a66832 100644
--- a/src/test/mir-opt/const_prop/cast.rs
+++ b/src/test/mir-opt/const_prop/cast.rs
@@ -1,49 +1,7 @@
+// EMIT_MIR rustc.main.ConstProp.diff
+
 fn main() {
     let x = 42u8 as u32;
 
     let y = 42u32 as u8;
 }
-
-// END RUST SOURCE
-// START rustc.main.ConstProp.before.mir
-// let mut _0: ();
-// let _1: u32;
-// scope 1 {
-//   debug x => _1;
-//   let _2: u8;
-//   scope 2 {
-//     debug y => _2;
-//   }
-// }
-// bb0: {
-//   StorageLive(_1);
-//   _1 = const 42u8 as u32 (Misc);
-//   StorageLive(_2);
-//   _2 = const 42u32 as u8 (Misc);
-//   _0 = ();
-//   StorageDead(_2);
-//   StorageDead(_1);
-//   return;
-// }
-// END rustc.main.ConstProp.before.mir
-// START rustc.main.ConstProp.after.mir
-// let mut _0: ();
-// let _1: u32;
-// scope 1 {
-//   debug x => _1;
-//   let _2: u8;
-//   scope 2 {
-//     debug y => _2;
-//   }
-// }
-// bb0: {
-//   StorageLive(_1);
-//   _1 = const 42u32;
-//   StorageLive(_2);
-//   _2 = const 42u8;
-//   _0 = ();
-//   StorageDead(_2);
-//   StorageDead(_1);
-//   return;
-// }
-// END rustc.main.ConstProp.after.mir
diff --git a/src/test/mir-opt/const_prop/cast/rustc.main.ConstProp.diff b/src/test/mir-opt/const_prop/cast/rustc.main.ConstProp.diff
new file mode 100644
index 0000000000000..ee6a0e87b12fc
--- /dev/null
+++ b/src/test/mir-opt/const_prop/cast/rustc.main.ConstProp.diff
@@ -0,0 +1,48 @@
+- // MIR for `main` before ConstProp
++ // MIR for `main` after ConstProp
+  
+  fn main() -> () {
+      let mut _0: ();                      // return place in scope 0 at $DIR/cast.rs:3:11: 3:11
+      let _1: u32;                         // in scope 0 at $DIR/cast.rs:4:9: 4:10
+      scope 1 {
+          debug x => _1;                   // in scope 1 at $DIR/cast.rs:4:9: 4:10
+          let _2: u8;                      // in scope 1 at $DIR/cast.rs:6:9: 6:10
+          scope 2 {
+              debug y => _2;               // in scope 2 at $DIR/cast.rs:6:9: 6:10
+          }
+      }
+  
+      bb0: {
+          StorageLive(_1);                 // bb0[0]: scope 0 at $DIR/cast.rs:4:9: 4:10
+-         _1 = const 42u8 as u32 (Misc);   // bb0[1]: scope 0 at $DIR/cast.rs:4:13: 4:24
++         _1 = const 42u32;                // bb0[1]: scope 0 at $DIR/cast.rs:4:13: 4:24
+                                           // ty::Const
+-                                          // + ty: u8
+-                                          // + val: Value(Scalar(0x2a))
+-                                          // mir::Constant
+-                                          // + span: $DIR/cast.rs:4:13: 4:17
+-                                          // + literal: Const { ty: u8, val: Value(Scalar(0x2a)) }
+-         StorageLive(_2);                 // bb0[2]: scope 1 at $DIR/cast.rs:6:9: 6:10
+-         _2 = const 42u32 as u8 (Misc);   // bb0[3]: scope 1 at $DIR/cast.rs:6:13: 6:24
+-                                          // ty::Const
+                                           // + ty: u32
+                                           // + val: Value(Scalar(0x0000002a))
+                                           // mir::Constant
+-                                          // + span: $DIR/cast.rs:6:13: 6:18
++                                          // + span: $DIR/cast.rs:4:13: 4:24
+                                           // + literal: Const { ty: u32, val: Value(Scalar(0x0000002a)) }
++         StorageLive(_2);                 // bb0[2]: scope 1 at $DIR/cast.rs:6:9: 6:10
++         _2 = const 42u8;                 // bb0[3]: scope 1 at $DIR/cast.rs:6:13: 6:24
++                                          // ty::Const
++                                          // + ty: u8
++                                          // + val: Value(Scalar(0x2a))
++                                          // mir::Constant
++                                          // + span: $DIR/cast.rs:6:13: 6:24
++                                          // + literal: Const { ty: u8, val: Value(Scalar(0x2a)) }
+          _0 = ();                         // bb0[4]: scope 0 at $DIR/cast.rs:3:11: 7:2
+          StorageDead(_2);                 // bb0[5]: scope 1 at $DIR/cast.rs:7:1: 7:2
+          StorageDead(_1);                 // bb0[6]: scope 0 at $DIR/cast.rs:7:1: 7:2
+          return;                          // bb0[7]: scope 0 at $DIR/cast.rs:7:2: 7:2
+      }
+  }
+  
diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs
index 4508f5b7f95f4..0ee016f33dd88 100644
--- a/src/tools/compiletest/src/runtest.rs
+++ b/src/tools/compiletest/src/runtest.rs
@@ -11,6 +11,7 @@ use crate::common::{UI_RUN_STDERR, UI_RUN_STDOUT};
 use crate::errors::{self, Error, ErrorKind};
 use crate::header::TestProps;
 use crate::json;
+use crate::util::get_pointer_width;
 use crate::util::{logv, PathBufExt};
 use diff;
 use regex::{Captures, Regex};
@@ -178,6 +179,33 @@ pub fn make_diff(expected: &str, actual: &str, context_size: usize) -> Vec<Misma
     results
 }
 
+fn print_diff(expected: &str, actual: &str, context_size: usize) {
+    write_diff(expected, actual, context_size, std::io::stdout());
+}
+
+fn write_diff(expected: &str, actual: &str, context_size: usize, mut dest: impl io::Write) {
+    let diff_results = make_diff(expected, actual, context_size);
+    for result in diff_results {
+        let mut line_number = result.line_number;
+        for line in result.lines {
+            match line {
+                DiffLine::Expected(e) => {
+                    writeln!(dest, "-\t{}", e).unwrap();
+                    line_number += 1;
+                }
+                DiffLine::Context(c) => {
+                    writeln!(dest, "{}\t{}", line_number, c).unwrap();
+                    line_number += 1;
+                }
+                DiffLine::Resulting(r) => {
+                    writeln!(dest, "+\t{}", r).unwrap();
+                }
+            }
+        }
+        writeln!(dest).unwrap();
+    }
+}
+
 pub fn run(config: Config, testpaths: &TestPaths, revision: Option<&str>) {
     match &*config.target {
         "arm-linux-androideabi"
@@ -3040,6 +3068,89 @@ impl<'test> TestCx<'test> {
 
     fn check_mir_dump(&self) {
         let test_file_contents = fs::read_to_string(&self.testpaths.file).unwrap();
+
+        let mut test_dir = self.testpaths.file.with_extension("");
+
+        if test_file_contents.lines().any(|l| l == "// EMIT_MIR_FOR_EACH_BIT_WIDTH") {
+            test_dir.push(get_pointer_width(&self.config.target))
+        }
+
+        if self.config.bless {
+            let _ = std::fs::remove_dir_all(&test_dir);
+        }
+        for l in test_file_contents.lines() {
+            if l.starts_with("// EMIT_MIR ") {
+                let test_name = l.trim_start_matches("// EMIT_MIR ");
+                let expected_file = test_dir.join(test_name);
+
+                let dumped_string = if test_name.ends_with(".diff") {
+                    let test_name = test_name.trim_end_matches(".diff");
+                    let before = format!("{}.before.mir", test_name);
+                    let after = format!("{}.after.mir", test_name);
+                    let before = self.get_mir_dump_dir().join(before);
+                    let after = self.get_mir_dump_dir().join(after);
+                    debug!(
+                        "comparing the contents of: {} with {}",
+                        before.display(),
+                        after.display()
+                    );
+                    let before = fs::read_to_string(before).unwrap();
+                    let after = fs::read_to_string(after).unwrap();
+                    let before = self.normalize_output(&before, &[]);
+                    let after = self.normalize_output(&after, &[]);
+                    let mut dumped_string = String::new();
+                    for result in diff::lines(&before, &after) {
+                        use std::fmt::Write;
+                        match result {
+                            diff::Result::Left(s) => writeln!(dumped_string, "- {}", s).unwrap(),
+                            diff::Result::Right(s) => writeln!(dumped_string, "+ {}", s).unwrap(),
+                            diff::Result::Both(s, _) => writeln!(dumped_string, "  {}", s).unwrap(),
+                        }
+                    }
+                    dumped_string
+                } else {
+                    let mut output_file = PathBuf::new();
+                    output_file.push(self.get_mir_dump_dir());
+                    output_file.push(test_name);
+                    debug!(
+                        "comparing the contents of: {} with {}",
+                        output_file.display(),
+                        expected_file.display()
+                    );
+                    if !output_file.exists() {
+                        panic!(
+                            "Output file `{}` from test does not exist, available files are in `{}`",
+                            output_file.display(),
+                            output_file.parent().unwrap().display()
+                        );
+                    }
+                    self.check_mir_test_timestamp(test_name, &output_file);
+                    let dumped_string = fs::read_to_string(&output_file).unwrap();
+                    self.normalize_output(&dumped_string, &[])
+                };
+                if self.config.bless {
+                    let _ = std::fs::create_dir_all(&test_dir);
+                    let _ = std::fs::remove_file(&expected_file);
+                    std::fs::write(expected_file, dumped_string.as_bytes()).unwrap();
+                } else {
+                    if !expected_file.exists() {
+                        panic!(
+                            "Output file `{}` from test does not exist",
+                            expected_file.display()
+                        );
+                    }
+                    let expected_string = fs::read_to_string(&expected_file).unwrap();
+                    if dumped_string != expected_string {
+                        print_diff(&dumped_string, &expected_string, 3);
+                        panic!(
+                            "Actual MIR output differs from expected MIR output {}",
+                            expected_file.display()
+                        );
+                    }
+                }
+            }
+        }
+
         if let Some(idx) = test_file_contents.find("// END RUST SOURCE") {
             let (_, tests_text) = test_file_contents.split_at(idx + "// END_RUST SOURCE".len());
             let tests_text_str = String::from(tests_text);
@@ -3090,13 +3201,10 @@ impl<'test> TestCx<'test> {
         let mut output_file = PathBuf::new();
         output_file.push(self.get_mir_dump_dir());
         output_file.push(test_name);
-        debug!("comparing the contests of: {:?}", output_file);
+        debug!("comparing the contents of: {:?}", output_file);
         debug!("with: {:?}", expected_content);
         if !output_file.exists() {
-            panic!(
-                "Output file `{}` from test does not exist",
-                output_file.into_os_string().to_string_lossy()
-            );
+            panic!("Output file `{}` from test does not exist", output_file.display());
         }
         self.check_mir_test_timestamp(test_name, &output_file);
 
@@ -3356,26 +3464,7 @@ impl<'test> TestCx<'test> {
                 println!("normalized {}:\n{}\n", kind, actual);
             } else {
                 println!("diff of {}:\n", kind);
-                let diff_results = make_diff(expected, actual, 3);
-                for result in diff_results {
-                    let mut line_number = result.line_number;
-                    for line in result.lines {
-                        match line {
-                            DiffLine::Expected(e) => {
-                                println!("-\t{}", e);
-                                line_number += 1;
-                            }
-                            DiffLine::Context(c) => {
-                                println!("{}\t{}", line_number, c);
-                                line_number += 1;
-                            }
-                            DiffLine::Resulting(r) => {
-                                println!("+\t{}", r);
-                            }
-                        }
-                    }
-                    println!();
-                }
+                print_diff(expected, actual, 3);
             }
         }