about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2023-09-08 15:32:54 +0000
committerbors <bors@rust-lang.org>2023-09-08 15:32:54 +0000
commit3cd97ed3c3efe11bf6b63d23dce2515238b78a57 (patch)
tree4a711661724c3d0deda8a382723eb8a1df0d165e
parent309af3442a1808888e3ceb2eacccbf4140eba1e0 (diff)
parentb7a925a7ce37c5163ce81abec0fdf8d848d34ae5 (diff)
downloadrust-3cd97ed3c3efe11bf6b63d23dce2515238b78a57.tar.gz
rust-3cd97ed3c3efe11bf6b63d23dce2515238b78a57.zip
Auto merge of #115612 - cjgillot:const-prop-int, r=oli-obk
Improvements to dataflow const-prop

Partially cherry-picked from https://github.com/rust-lang/rust/pull/110719

r? `@oli-obk`
cc `@jachris`
-rw-r--r--compiler/rustc_middle/src/mir/mod.rs9
-rw-r--r--compiler/rustc_middle/src/ty/consts/int.rs5
-rw-r--r--compiler/rustc_mir_dataflow/src/value_analysis.rs109
-rw-r--r--compiler/rustc_mir_transform/src/dataflow_const_prop.rs323
-rw-r--r--tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.ConstProp.32bit.panic-abort.diff6
-rw-r--r--tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.ConstProp.32bit.panic-unwind.diff6
-rw-r--r--tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.ConstProp.64bit.panic-abort.diff6
-rw-r--r--tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.ConstProp.64bit.panic-unwind.diff6
-rw-r--r--tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.rs3
-rw-r--r--tests/mir-opt/const_prop/large_array_index.rs1
-rw-r--r--tests/mir-opt/const_prop/repeat.rs1
-rw-r--r--tests/mir-opt/dataflow-const-prop/array_index.main.DataflowConstProp.32bit.panic-abort.diff39
-rw-r--r--tests/mir-opt/dataflow-const-prop/array_index.main.DataflowConstProp.32bit.panic-unwind.diff39
-rw-r--r--tests/mir-opt/dataflow-const-prop/array_index.main.DataflowConstProp.64bit.panic-abort.diff39
-rw-r--r--tests/mir-opt/dataflow-const-prop/array_index.main.DataflowConstProp.64bit.panic-unwind.diff39
-rw-r--r--tests/mir-opt/dataflow-const-prop/array_index.rs8
-rw-r--r--tests/mir-opt/dataflow-const-prop/boolean_identities.rs10
-rw-r--r--tests/mir-opt/dataflow-const-prop/boolean_identities.test.DataflowConstProp.diff33
-rw-r--r--tests/mir-opt/dataflow-const-prop/large_array_index.main.DataflowConstProp.32bit.panic-abort.diff39
-rw-r--r--tests/mir-opt/dataflow-const-prop/large_array_index.main.DataflowConstProp.32bit.panic-unwind.diff39
-rw-r--r--tests/mir-opt/dataflow-const-prop/large_array_index.main.DataflowConstProp.64bit.panic-abort.diff39
-rw-r--r--tests/mir-opt/dataflow-const-prop/large_array_index.main.DataflowConstProp.64bit.panic-unwind.diff39
-rw-r--r--tests/mir-opt/dataflow-const-prop/large_array_index.rs9
-rw-r--r--tests/mir-opt/dataflow-const-prop/mult_by_zero.rs10
-rw-r--r--tests/mir-opt/dataflow-const-prop/mult_by_zero.test.DataflowConstProp.diff18
-rw-r--r--tests/mir-opt/dataflow-const-prop/offset_of.concrete.DataflowConstProp.panic-abort.diff76
-rw-r--r--tests/mir-opt/dataflow-const-prop/offset_of.concrete.DataflowConstProp.panic-unwind.diff76
-rw-r--r--tests/mir-opt/dataflow-const-prop/offset_of.generic.DataflowConstProp.panic-abort.diff72
-rw-r--r--tests/mir-opt/dataflow-const-prop/offset_of.generic.DataflowConstProp.panic-unwind.diff72
-rw-r--r--tests/mir-opt/dataflow-const-prop/offset_of.rs49
-rw-r--r--tests/mir-opt/dataflow-const-prop/repeat.main.DataflowConstProp.32bit.panic-abort.diff43
-rw-r--r--tests/mir-opt/dataflow-const-prop/repeat.main.DataflowConstProp.32bit.panic-unwind.diff43
-rw-r--r--tests/mir-opt/dataflow-const-prop/repeat.main.DataflowConstProp.64bit.panic-abort.diff43
-rw-r--r--tests/mir-opt/dataflow-const-prop/repeat.main.DataflowConstProp.64bit.panic-unwind.diff43
-rw-r--r--tests/mir-opt/dataflow-const-prop/repeat.rs8
-rw-r--r--tests/mir-opt/dataflow-const-prop/slice_len.main.DataflowConstProp.32bit.panic-abort.diff76
-rw-r--r--tests/mir-opt/dataflow-const-prop/slice_len.main.DataflowConstProp.32bit.panic-unwind.diff76
-rw-r--r--tests/mir-opt/dataflow-const-prop/slice_len.main.DataflowConstProp.64bit.panic-abort.diff76
-rw-r--r--tests/mir-opt/dataflow-const-prop/slice_len.main.DataflowConstProp.64bit.panic-unwind.diff76
-rw-r--r--tests/mir-opt/dataflow-const-prop/slice_len.rs12
-rw-r--r--tests/mir-opt/dataflow-const-prop/transmute.from_char.DataflowConstProp.32bit.diff15
-rw-r--r--tests/mir-opt/dataflow-const-prop/transmute.from_char.DataflowConstProp.64bit.diff15
-rw-r--r--tests/mir-opt/dataflow-const-prop/transmute.invalid_bool.DataflowConstProp.32bit.diff15
-rw-r--r--tests/mir-opt/dataflow-const-prop/transmute.invalid_bool.DataflowConstProp.64bit.diff15
-rw-r--r--tests/mir-opt/dataflow-const-prop/transmute.invalid_char.DataflowConstProp.32bit.diff15
-rw-r--r--tests/mir-opt/dataflow-const-prop/transmute.invalid_char.DataflowConstProp.64bit.diff15
-rw-r--r--tests/mir-opt/dataflow-const-prop/transmute.less_as_i8.DataflowConstProp.32bit.diff18
-rw-r--r--tests/mir-opt/dataflow-const-prop/transmute.less_as_i8.DataflowConstProp.64bit.diff18
-rw-r--r--tests/mir-opt/dataflow-const-prop/transmute.rs63
-rw-r--r--tests/mir-opt/dataflow-const-prop/transmute.undef_union_as_integer.DataflowConstProp.32bit.diff22
-rw-r--r--tests/mir-opt/dataflow-const-prop/transmute.undef_union_as_integer.DataflowConstProp.64bit.diff22
-rw-r--r--tests/mir-opt/dataflow-const-prop/transmute.unreachable_box.DataflowConstProp.32bit.diff20
-rw-r--r--tests/mir-opt/dataflow-const-prop/transmute.unreachable_box.DataflowConstProp.64bit.diff20
-rw-r--r--tests/mir-opt/dataflow-const-prop/transmute.unreachable_direct.DataflowConstProp.32bit.diff22
-rw-r--r--tests/mir-opt/dataflow-const-prop/transmute.unreachable_direct.DataflowConstProp.64bit.diff22
-rw-r--r--tests/mir-opt/dataflow-const-prop/transmute.unreachable_mut.DataflowConstProp.32bit.diff24
-rw-r--r--tests/mir-opt/dataflow-const-prop/transmute.unreachable_mut.DataflowConstProp.64bit.diff24
-rw-r--r--tests/mir-opt/dataflow-const-prop/transmute.unreachable_ref.DataflowConstProp.32bit.diff20
-rw-r--r--tests/mir-opt/dataflow-const-prop/transmute.unreachable_ref.DataflowConstProp.64bit.diff20
-rw-r--r--tests/mir-opt/dataflow-const-prop/transmute.valid_char.DataflowConstProp.32bit.diff15
-rw-r--r--tests/mir-opt/dataflow-const-prop/transmute.valid_char.DataflowConstProp.64bit.diff15
-rw-r--r--tests/mir-opt/issue_76432.test.SimplifyComparisonIntegral.panic-abort.diff8
-rw-r--r--tests/mir-opt/issue_76432.test.SimplifyComparisonIntegral.panic-unwind.diff8
63 files changed, 1979 insertions, 188 deletions
diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs
index 239d20ae1b0..c78b1e0e2f4 100644
--- a/compiler/rustc_middle/src/mir/mod.rs
+++ b/compiler/rustc_middle/src/mir/mod.rs
@@ -1737,6 +1737,13 @@ impl<'tcx> PlaceRef<'tcx> {
     }
 }
 
+impl From<Local> for PlaceRef<'_> {
+    #[inline]
+    fn from(local: Local) -> Self {
+        PlaceRef { local, projection: &[] }
+    }
+}
+
 impl Debug for Place<'_> {
     fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
         self.as_ref().fmt(fmt)
@@ -2317,7 +2324,7 @@ impl<'tcx> ConstantKind<'tcx> {
 
     #[inline]
     pub fn try_to_scalar_int(self) -> Option<ScalarInt> {
-        Some(self.try_to_scalar()?.assert_int())
+        self.try_to_scalar()?.try_to_int().ok()
     }
 
     #[inline]
diff --git a/compiler/rustc_middle/src/ty/consts/int.rs b/compiler/rustc_middle/src/ty/consts/int.rs
index b16163edf14..9d99344d5bd 100644
--- a/compiler/rustc_middle/src/ty/consts/int.rs
+++ b/compiler/rustc_middle/src/ty/consts/int.rs
@@ -227,6 +227,11 @@ impl ScalarInt {
     }
 
     #[inline]
+    pub fn try_from_target_usize(i: impl Into<u128>, tcx: TyCtxt<'_>) -> Option<Self> {
+        Self::try_from_uint(i, tcx.data_layout.pointer_size)
+    }
+
+    #[inline]
     pub fn assert_bits(self, target_size: Size) -> u128 {
         self.to_bits(target_size).unwrap_or_else(|size| {
             bug!("expected int of size {}, but got size {}", target_size.bytes(), size.bytes())
diff --git a/compiler/rustc_mir_dataflow/src/value_analysis.rs b/compiler/rustc_mir_dataflow/src/value_analysis.rs
index 6872ef5e985..299bf692307 100644
--- a/compiler/rustc_mir_dataflow/src/value_analysis.rs
+++ b/compiler/rustc_mir_dataflow/src/value_analysis.rs
@@ -581,6 +581,14 @@ impl<V: Clone + HasTop + HasBottom> State<V> {
         }
     }
 
+    /// Retrieve the value stored for a place, or ⊤ if it is not tracked.
+    pub fn get_len(&self, place: PlaceRef<'_>, map: &Map) -> V {
+        match map.find_len(place) {
+            Some(place) => self.get_idx(place, map),
+            None => V::TOP,
+        }
+    }
+
     /// Retrieve the value stored for a place index, or ⊤ if it is not tracked.
     pub fn get_idx(&self, place: PlaceIndex, map: &Map) -> V {
         match &self.0 {
@@ -626,45 +634,36 @@ pub struct Map {
 }
 
 impl Map {
-    fn new() -> Self {
-        Self {
+    /// Returns a map that only tracks places whose type has scalar layout.
+    ///
+    /// This is currently the only way to create a [`Map`]. The way in which the tracked places are
+    /// chosen is an implementation detail and may not be relied upon (other than that their type
+    /// are scalars).
+    pub fn new<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, value_limit: Option<usize>) -> Self {
+        let mut map = Self {
             locals: IndexVec::new(),
             projections: FxHashMap::default(),
             places: IndexVec::new(),
             value_count: 0,
             inner_values: IndexVec::new(),
             inner_values_buffer: Vec::new(),
-        }
-    }
-
-    /// Returns a map that only tracks places whose type passes the filter.
-    ///
-    /// This is currently the only way to create a [`Map`]. The way in which the tracked places are
-    /// chosen is an implementation detail and may not be relied upon (other than that their type
-    /// passes the filter).
-    pub fn from_filter<'tcx>(
-        tcx: TyCtxt<'tcx>,
-        body: &Body<'tcx>,
-        filter: impl Fn(Ty<'tcx>) -> bool,
-        value_limit: Option<usize>,
-    ) -> Self {
-        let mut map = Self::new();
+        };
         let exclude = excluded_locals(body);
-        map.register_with_filter(tcx, body, filter, exclude, value_limit);
+        map.register(tcx, body, exclude, value_limit);
         debug!("registered {} places ({} nodes in total)", map.value_count, map.places.len());
         map
     }
 
-    /// Register all non-excluded places that pass the filter.
-    fn register_with_filter<'tcx>(
+    /// Register all non-excluded places that have scalar layout.
+    fn register<'tcx>(
         &mut self,
         tcx: TyCtxt<'tcx>,
         body: &Body<'tcx>,
-        filter: impl Fn(Ty<'tcx>) -> bool,
         exclude: BitSet<Local>,
         value_limit: Option<usize>,
     ) {
         let mut worklist = VecDeque::with_capacity(value_limit.unwrap_or(body.local_decls.len()));
+        let param_env = tcx.param_env_reveal_all_normalized(body.source.def_id());
 
         // Start by constructing the places for each bare local.
         self.locals = IndexVec::from_elem(None, &body.local_decls);
@@ -679,7 +678,7 @@ impl Map {
             self.locals[local] = Some(place);
 
             // And push the eventual children places to the worklist.
-            self.register_children(tcx, place, decl.ty, &filter, &mut worklist);
+            self.register_children(tcx, param_env, place, decl.ty, &mut worklist);
         }
 
         // `place.elem1.elem2` with type `ty`.
@@ -702,7 +701,7 @@ impl Map {
             }
 
             // And push the eventual children places to the worklist.
-            self.register_children(tcx, place, ty, &filter, &mut worklist);
+            self.register_children(tcx, param_env, place, ty, &mut worklist);
         }
 
         // Pre-compute the tree of ValueIndex nested in each PlaceIndex.
@@ -732,42 +731,52 @@ impl Map {
     fn register_children<'tcx>(
         &mut self,
         tcx: TyCtxt<'tcx>,
+        param_env: ty::ParamEnv<'tcx>,
         place: PlaceIndex,
         ty: Ty<'tcx>,
-        filter: &impl Fn(Ty<'tcx>) -> bool,
         worklist: &mut VecDeque<(PlaceIndex, Option<TrackElem>, TrackElem, Ty<'tcx>)>,
     ) {
         // Allocate a value slot if it doesn't have one, and the user requested one.
-        if self.places[place].value_index.is_none() && filter(ty) {
+        assert!(self.places[place].value_index.is_none());
+        if tcx.layout_of(param_env.and(ty)).map_or(false, |layout| layout.abi.is_scalar()) {
             self.places[place].value_index = Some(self.value_count.into());
             self.value_count += 1;
         }
 
         // For enums, directly create the `Discriminant`, as that's their main use.
         if ty.is_enum() {
-            let discr_ty = ty.discriminant_ty(tcx);
-            if filter(discr_ty) {
-                let discr = *self
-                    .projections
-                    .entry((place, TrackElem::Discriminant))
-                    .or_insert_with(|| {
-                        // Prepend new child to the linked list.
-                        let next = self.places.push(PlaceInfo::new(Some(TrackElem::Discriminant)));
-                        self.places[next].next_sibling = self.places[place].first_child;
-                        self.places[place].first_child = Some(next);
-                        next
-                    });
-
-                // Allocate a value slot if it doesn't have one.
-                if self.places[discr].value_index.is_none() {
-                    self.places[discr].value_index = Some(self.value_count.into());
-                    self.value_count += 1;
-                }
-            }
+            // Prepend new child to the linked list.
+            let discr = self.places.push(PlaceInfo::new(Some(TrackElem::Discriminant)));
+            self.places[discr].next_sibling = self.places[place].first_child;
+            self.places[place].first_child = Some(discr);
+            let old = self.projections.insert((place, TrackElem::Discriminant), discr);
+            assert!(old.is_none());
+
+            // Allocate a value slot since it doesn't have one.
+            assert!(self.places[discr].value_index.is_none());
+            self.places[discr].value_index = Some(self.value_count.into());
+            self.value_count += 1;
+        }
+
+        if let Some(ref_ty) = ty.builtin_deref(true) && let ty::Slice(..) = ref_ty.ty.kind() {
+            assert!(self.places[place].value_index.is_none(), "slices are not scalars");
+
+            // Prepend new child to the linked list.
+            let len = self.places.push(PlaceInfo::new(Some(TrackElem::DerefLen)));
+            self.places[len].next_sibling = self.places[place].first_child;
+            self.places[place].first_child = Some(len);
+
+            let old = self.projections.insert((place, TrackElem::DerefLen), len);
+            assert!(old.is_none());
+
+            // Allocate a value slot since it doesn't have one.
+            assert!( self.places[len].value_index.is_none() );
+            self.places[len].value_index = Some(self.value_count.into());
+            self.value_count += 1;
         }
 
         // Recurse with all fields of this place.
-        iter_fields(ty, tcx, ty::ParamEnv::reveal_all(), |variant, field, ty| {
+        iter_fields(ty, tcx, param_env, |variant, field, ty| {
             worklist.push_back((
                 place,
                 variant.map(TrackElem::Variant),
@@ -834,6 +843,11 @@ impl Map {
         self.find_extra(place, [TrackElem::Discriminant])
     }
 
+    /// Locates the given place and applies `DerefLen`, if it exists in the tree.
+    pub fn find_len(&self, place: PlaceRef<'_>) -> Option<PlaceIndex> {
+        self.find_extra(place, [TrackElem::DerefLen])
+    }
+
     /// Iterate over all direct children.
     pub fn children(&self, parent: PlaceIndex) -> impl Iterator<Item = PlaceIndex> + '_ {
         Children::new(self, parent)
@@ -985,6 +999,8 @@ pub enum TrackElem {
     Field(FieldIdx),
     Variant(VariantIdx),
     Discriminant,
+    // Length of a slice.
+    DerefLen,
 }
 
 impl<V, T> TryFrom<ProjectionElem<V, T>> for TrackElem {
@@ -1124,6 +1140,9 @@ fn debug_with_context_rec<V: Debug + Eq>(
                     format!("{}.{}", place_str, field.index())
                 }
             }
+            TrackElem::DerefLen => {
+                format!("Len(*{})", place_str)
+            }
         };
         debug_with_context_rec(child, &child_place_str, new, old, map, f)?;
     }
diff --git a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs
index d4c9163b571..333a14be996 100644
--- a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs
+++ b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs
@@ -6,10 +6,10 @@ use rustc_const_eval::const_eval::CheckAlignment;
 use rustc_const_eval::interpret::{ConstValue, ImmTy, Immediate, InterpCx, Scalar};
 use rustc_data_structures::fx::FxHashMap;
 use rustc_hir::def::DefKind;
-use rustc_middle::mir::visit::{MutVisitor, Visitor};
+use rustc_middle::mir::visit::{MutVisitor, NonMutatingUseContext, PlaceContext, Visitor};
 use rustc_middle::mir::*;
 use rustc_middle::ty::layout::TyAndLayout;
-use rustc_middle::ty::{self, Ty, TyCtxt};
+use rustc_middle::ty::{self, ScalarInt, Ty, TyCtxt};
 use rustc_mir_dataflow::value_analysis::{
     Map, State, TrackElem, ValueAnalysis, ValueAnalysisWrapper, ValueOrPlace,
 };
@@ -50,7 +50,7 @@ impl<'tcx> MirPass<'tcx> for DataflowConstProp {
         let place_limit = if tcx.sess.mir_opt_level() < 4 { Some(PLACE_LIMIT) } else { None };
 
         // Decide which places to track during the analysis.
-        let map = Map::from_filter(tcx, body, Ty::is_scalar, place_limit);
+        let map = Map::new(tcx, body, place_limit);
 
         // Perform the actual dataflow analysis.
         let analysis = ConstAnalysis::new(tcx, body, map);
@@ -58,9 +58,13 @@ impl<'tcx> MirPass<'tcx> for DataflowConstProp {
             .in_scope(|| analysis.wrap().into_engine(tcx, body).iterate_to_fixpoint());
 
         // Collect results and patch the body afterwards.
-        let mut visitor = CollectAndPatch::new(tcx);
+        let mut visitor = CollectAndPatch::new(tcx, &body.local_decls);
         debug_span!("collect").in_scope(|| results.visit_reachable_with(body, &mut visitor));
-        debug_span!("patch").in_scope(|| visitor.visit_body(body));
+        debug_span!("patch").in_scope(|| {
+            for (block, bbdata) in body.basic_blocks.as_mut_preserves_cfg().iter_enumerated_mut() {
+                visitor.visit_basic_block_data(block, bbdata);
+            }
+        })
     }
 }
 
@@ -73,7 +77,7 @@ struct ConstAnalysis<'a, 'tcx> {
 }
 
 impl<'tcx> ValueAnalysis<'tcx> for ConstAnalysis<'_, 'tcx> {
-    type Value = FlatSet<ScalarTy<'tcx>>;
+    type Value = FlatSet<ScalarInt>;
 
     const NAME: &'static str = "ConstAnalysis";
 
@@ -172,9 +176,7 @@ impl<'tcx> ValueAnalysis<'tcx> for ConstAnalysis<'_, 'tcx> {
                     if let Some(overflow_target) = overflow_target {
                         let overflow = match overflow {
                             FlatSet::Top => FlatSet::Top,
-                            FlatSet::Elem(overflow) => {
-                                self.wrap_scalar(Scalar::from_bool(overflow), self.tcx.types.bool)
-                            }
+                            FlatSet::Elem(overflow) => FlatSet::Elem(overflow.into()),
                             FlatSet::Bottom => FlatSet::Bottom,
                         };
                         // We have flooded `target` earlier.
@@ -182,6 +184,23 @@ impl<'tcx> ValueAnalysis<'tcx> for ConstAnalysis<'_, 'tcx> {
                     }
                 }
             }
+            Rvalue::Cast(
+                CastKind::PointerCoercion(ty::adjustment::PointerCoercion::Unsize),
+                operand,
+                _,
+            ) => {
+                let pointer = self.handle_operand(operand, state);
+                state.assign(target.as_ref(), pointer, self.map());
+
+                if let Some(target_len) = self.map().find_len(target.as_ref())
+                    && let operand_ty = operand.ty(self.local_decls, self.tcx)
+                    && let Some(operand_ty) = operand_ty.builtin_deref(true)
+                    && let ty::Array(_, len) = operand_ty.ty.kind()
+                    && let Some(len) = ConstantKind::Ty(*len).eval(self.tcx, self.param_env).try_to_scalar_int()
+                {
+                    state.insert_value_idx(target_len, FlatSet::Elem(len), self.map());
+                }
+            }
             _ => self.super_assign(target, rvalue, state),
         }
     }
@@ -191,47 +210,77 @@ impl<'tcx> ValueAnalysis<'tcx> for ConstAnalysis<'_, 'tcx> {
         rvalue: &Rvalue<'tcx>,
         state: &mut State<Self::Value>,
     ) -> ValueOrPlace<Self::Value> {
-        match rvalue {
-            Rvalue::Cast(
-                kind @ (CastKind::IntToInt
-                | CastKind::FloatToInt
-                | CastKind::FloatToFloat
-                | CastKind::IntToFloat),
-                operand,
-                ty,
-            ) => match self.eval_operand(operand, state) {
-                FlatSet::Elem(op) => match kind {
-                    CastKind::IntToInt | CastKind::IntToFloat => {
-                        self.ecx.int_to_int_or_float(&op, *ty)
-                    }
-                    CastKind::FloatToInt | CastKind::FloatToFloat => {
-                        self.ecx.float_to_float_or_int(&op, *ty)
-                    }
-                    _ => unreachable!(),
+        let val = match rvalue {
+            Rvalue::Len(place) => {
+                let place_ty = place.ty(self.local_decls, self.tcx);
+                if let ty::Array(_, len) = place_ty.ty.kind() {
+                    ConstantKind::Ty(*len)
+                        .eval(self.tcx, self.param_env)
+                        .try_to_scalar_int()
+                        .map_or(FlatSet::Top, FlatSet::Elem)
+                } else if let [ProjectionElem::Deref] = place.projection[..] {
+                    state.get_len(place.local.into(), self.map())
+                } else {
+                    FlatSet::Top
                 }
-                .map(|result| ValueOrPlace::Value(self.wrap_immediate(result, *ty)))
-                .unwrap_or(ValueOrPlace::TOP),
-                _ => ValueOrPlace::TOP,
-            },
+            }
+            Rvalue::Cast(CastKind::IntToInt | CastKind::IntToFloat, operand, ty) => {
+                match self.eval_operand(operand, state) {
+                    FlatSet::Elem(op) => self
+                        .ecx
+                        .int_to_int_or_float(&op, *ty)
+                        .map_or(FlatSet::Top, |result| self.wrap_immediate(result)),
+                    FlatSet::Bottom => FlatSet::Bottom,
+                    FlatSet::Top => FlatSet::Top,
+                }
+            }
+            Rvalue::Cast(CastKind::FloatToInt | CastKind::FloatToFloat, operand, ty) => {
+                match self.eval_operand(operand, state) {
+                    FlatSet::Elem(op) => self
+                        .ecx
+                        .float_to_float_or_int(&op, *ty)
+                        .map_or(FlatSet::Top, |result| self.wrap_immediate(result)),
+                    FlatSet::Bottom => FlatSet::Bottom,
+                    FlatSet::Top => FlatSet::Top,
+                }
+            }
+            Rvalue::Cast(CastKind::Transmute, operand, _) => {
+                match self.eval_operand(operand, state) {
+                    FlatSet::Elem(op) => self.wrap_immediate(*op),
+                    FlatSet::Bottom => FlatSet::Bottom,
+                    FlatSet::Top => FlatSet::Top,
+                }
+            }
             Rvalue::BinaryOp(op, box (left, right)) => {
                 // Overflows must be ignored here.
                 let (val, _overflow) = self.binary_op(state, *op, left, right);
-                ValueOrPlace::Value(val)
+                val
             }
             Rvalue::UnaryOp(op, operand) => match self.eval_operand(operand, state) {
-                FlatSet::Elem(value) => self
-                    .ecx
-                    .unary_op(*op, &value)
-                    .map(|val| ValueOrPlace::Value(self.wrap_immty(val)))
-                    .unwrap_or(ValueOrPlace::Value(FlatSet::Top)),
-                FlatSet::Bottom => ValueOrPlace::Value(FlatSet::Bottom),
-                FlatSet::Top => ValueOrPlace::Value(FlatSet::Top),
+                FlatSet::Elem(value) => {
+                    self.ecx.unary_op(*op, &value).map_or(FlatSet::Top, |val| self.wrap_immty(val))
+                }
+                FlatSet::Bottom => FlatSet::Bottom,
+                FlatSet::Top => FlatSet::Top,
             },
-            Rvalue::Discriminant(place) => {
-                ValueOrPlace::Value(state.get_discr(place.as_ref(), self.map()))
+            Rvalue::NullaryOp(null_op, ty) => {
+                let Ok(layout) = self.tcx.layout_of(self.param_env.and(*ty)) else {
+                    return ValueOrPlace::Value(FlatSet::Top);
+                };
+                let val = match null_op {
+                    NullOp::SizeOf if layout.is_sized() => layout.size.bytes(),
+                    NullOp::AlignOf if layout.is_sized() => layout.align.abi.bytes(),
+                    NullOp::OffsetOf(fields) => layout
+                        .offset_of_subfield(&self.ecx, fields.iter().map(|f| f.index()))
+                        .bytes(),
+                    _ => return ValueOrPlace::Value(FlatSet::Top),
+                };
+                ScalarInt::try_from_target_usize(val, self.tcx).map_or(FlatSet::Top, FlatSet::Elem)
             }
-            _ => self.super_rvalue(rvalue, state),
-        }
+            Rvalue::Discriminant(place) => state.get_discr(place.as_ref(), self.map()),
+            _ => return self.super_rvalue(rvalue, state),
+        };
+        ValueOrPlace::Value(val)
     }
 
     fn handle_constant(
@@ -242,9 +291,8 @@ impl<'tcx> ValueAnalysis<'tcx> for ConstAnalysis<'_, 'tcx> {
         constant
             .literal
             .eval(self.tcx, self.param_env)
-            .try_to_scalar()
-            .map(|value| FlatSet::Elem(ScalarTy(value, constant.ty())))
-            .unwrap_or(FlatSet::Top)
+            .try_to_scalar_int()
+            .map_or(FlatSet::Top, FlatSet::Elem)
     }
 
     fn handle_switch_int<'mir>(
@@ -261,9 +309,8 @@ impl<'tcx> ValueAnalysis<'tcx> for ConstAnalysis<'_, 'tcx> {
             // We are branching on uninitialized data, this is UB, treat it as unreachable.
             // This allows the set of visited edges to grow monotonically with the lattice.
             FlatSet::Bottom => TerminatorEdges::None,
-            FlatSet::Elem(ScalarTy(scalar, _)) => {
-                let int = scalar.assert_int();
-                let choice = int.assert_bits(int.size());
+            FlatSet::Elem(scalar) => {
+                let choice = scalar.assert_bits(scalar.size());
                 TerminatorEdges::Single(targets.target_for_value(choice))
             }
             FlatSet::Top => TerminatorEdges::SwitchInt { discr, targets },
@@ -271,16 +318,6 @@ impl<'tcx> ValueAnalysis<'tcx> for ConstAnalysis<'_, 'tcx> {
     }
 }
 
-#[derive(Clone, PartialEq, Eq)]
-struct ScalarTy<'tcx>(Scalar, Ty<'tcx>);
-
-impl<'tcx> std::fmt::Debug for ScalarTy<'tcx> {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        // This is used for dataflow visualization, so we return something more concise.
-        std::fmt::Display::fmt(&ConstantKind::Val(ConstValue::Scalar(self.0), self.1), f)
-    }
-}
-
 impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> {
     pub fn new(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, map: Map) -> Self {
         let param_env = tcx.param_env_reveal_all_normalized(body.source.def_id());
@@ -295,32 +332,62 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> {
 
     fn binary_op(
         &self,
-        state: &mut State<FlatSet<ScalarTy<'tcx>>>,
+        state: &mut State<FlatSet<ScalarInt>>,
         op: BinOp,
         left: &Operand<'tcx>,
         right: &Operand<'tcx>,
-    ) -> (FlatSet<ScalarTy<'tcx>>, FlatSet<bool>) {
+    ) -> (FlatSet<ScalarInt>, FlatSet<bool>) {
         let left = self.eval_operand(left, state);
         let right = self.eval_operand(right, state);
+
         match (left, right) {
+            (FlatSet::Bottom, _) | (_, FlatSet::Bottom) => (FlatSet::Bottom, FlatSet::Bottom),
+            // Both sides are known, do the actual computation.
             (FlatSet::Elem(left), FlatSet::Elem(right)) => {
                 match self.ecx.overflowing_binary_op(op, &left, &right) {
-                    Ok((val, overflow, ty)) => (self.wrap_scalar(val, ty), FlatSet::Elem(overflow)),
+                    Ok((Scalar::Int(val), overflow, _)) => {
+                        (FlatSet::Elem(val), FlatSet::Elem(overflow))
+                    }
                     _ => (FlatSet::Top, FlatSet::Top),
                 }
             }
-            (FlatSet::Bottom, _) | (_, FlatSet::Bottom) => (FlatSet::Bottom, FlatSet::Bottom),
-            (_, _) => {
-                // Could attempt some algebraic simplifications here.
-                (FlatSet::Top, FlatSet::Top)
+            // Exactly one side is known, attempt some algebraic simplifications.
+            (FlatSet::Elem(const_arg), _) | (_, FlatSet::Elem(const_arg)) => {
+                let layout = const_arg.layout;
+                if !matches!(layout.abi, rustc_target::abi::Abi::Scalar(..)) {
+                    return (FlatSet::Top, FlatSet::Top);
+                }
+
+                let arg_scalar = const_arg.to_scalar();
+                let Ok(arg_scalar) = arg_scalar.try_to_int() else {
+                    return (FlatSet::Top, FlatSet::Top);
+                };
+                let Ok(arg_value) = arg_scalar.to_bits(layout.size) else {
+                    return (FlatSet::Top, FlatSet::Top);
+                };
+
+                match op {
+                    BinOp::BitAnd if arg_value == 0 => (FlatSet::Elem(arg_scalar), FlatSet::Bottom),
+                    BinOp::BitOr
+                        if arg_value == layout.size.truncate(u128::MAX)
+                            || (layout.ty.is_bool() && arg_value == 1) =>
+                    {
+                        (FlatSet::Elem(arg_scalar), FlatSet::Bottom)
+                    }
+                    BinOp::Mul if layout.ty.is_integral() && arg_value == 0 => {
+                        (FlatSet::Elem(arg_scalar), FlatSet::Elem(false))
+                    }
+                    _ => (FlatSet::Top, FlatSet::Top),
+                }
             }
+            (FlatSet::Top, FlatSet::Top) => (FlatSet::Top, FlatSet::Top),
         }
     }
 
     fn eval_operand(
         &self,
         op: &Operand<'tcx>,
-        state: &mut State<FlatSet<ScalarTy<'tcx>>>,
+        state: &mut State<FlatSet<ScalarInt>>,
     ) -> FlatSet<ImmTy<'tcx>> {
         let value = match self.handle_operand(op, state) {
             ValueOrPlace::Value(value) => value,
@@ -328,76 +395,76 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> {
         };
         match value {
             FlatSet::Top => FlatSet::Top,
-            FlatSet::Elem(ScalarTy(scalar, ty)) => self
-                .tcx
-                .layout_of(self.param_env.and(ty))
-                .map(|layout| FlatSet::Elem(ImmTy::from_scalar(scalar, layout)))
-                .unwrap_or(FlatSet::Top),
+            FlatSet::Elem(scalar) => {
+                let ty = op.ty(self.local_decls, self.tcx);
+                self.tcx
+                    .layout_of(self.param_env.and(ty))
+                    .map(|layout| FlatSet::Elem(ImmTy::from_scalar(scalar.into(), layout)))
+                    .unwrap_or(FlatSet::Top)
+            }
             FlatSet::Bottom => FlatSet::Bottom,
         }
     }
 
-    fn eval_discriminant(
-        &self,
-        enum_ty: Ty<'tcx>,
-        variant_index: VariantIdx,
-    ) -> Option<ScalarTy<'tcx>> {
+    fn eval_discriminant(&self, enum_ty: Ty<'tcx>, variant_index: VariantIdx) -> Option<ScalarInt> {
         if !enum_ty.is_enum() {
             return None;
         }
         let discr = enum_ty.discriminant_for_variant(self.tcx, variant_index)?;
         let discr_layout = self.tcx.layout_of(self.param_env.and(discr.ty)).ok()?;
-        let discr_value = Scalar::try_from_uint(discr.val, discr_layout.size)?;
-        Some(ScalarTy(discr_value, discr.ty))
-    }
-
-    fn wrap_scalar(&self, scalar: Scalar, ty: Ty<'tcx>) -> FlatSet<ScalarTy<'tcx>> {
-        FlatSet::Elem(ScalarTy(scalar, ty))
+        let discr_value = ScalarInt::try_from_uint(discr.val, discr_layout.size)?;
+        Some(discr_value)
     }
 
-    fn wrap_immediate(&self, imm: Immediate, ty: Ty<'tcx>) -> FlatSet<ScalarTy<'tcx>> {
+    fn wrap_immediate(&self, imm: Immediate) -> FlatSet<ScalarInt> {
         match imm {
-            Immediate::Scalar(scalar) => self.wrap_scalar(scalar, ty),
+            Immediate::Scalar(Scalar::Int(scalar)) => FlatSet::Elem(scalar),
             _ => FlatSet::Top,
         }
     }
 
-    fn wrap_immty(&self, val: ImmTy<'tcx>) -> FlatSet<ScalarTy<'tcx>> {
-        self.wrap_immediate(*val, val.layout.ty)
+    fn wrap_immty(&self, val: ImmTy<'tcx>) -> FlatSet<ScalarInt> {
+        self.wrap_immediate(*val)
     }
 }
 
-struct CollectAndPatch<'tcx> {
+struct CollectAndPatch<'tcx, 'locals> {
     tcx: TyCtxt<'tcx>,
+    local_decls: &'locals LocalDecls<'tcx>,
 
     /// For a given MIR location, this stores the values of the operands used by that location. In
     /// particular, this is before the effect, such that the operands of `_1 = _1 + _2` are
     /// properly captured. (This may become UB soon, but it is currently emitted even by safe code.)
-    before_effect: FxHashMap<(Location, Place<'tcx>), ScalarTy<'tcx>>,
+    before_effect: FxHashMap<(Location, Place<'tcx>), ScalarInt>,
 
     /// Stores the assigned values for assignments where the Rvalue is constant.
-    assignments: FxHashMap<Location, ScalarTy<'tcx>>,
+    assignments: FxHashMap<Location, ScalarInt>,
 }
 
-impl<'tcx> CollectAndPatch<'tcx> {
-    fn new(tcx: TyCtxt<'tcx>) -> Self {
-        Self { tcx, before_effect: FxHashMap::default(), assignments: FxHashMap::default() }
+impl<'tcx, 'locals> CollectAndPatch<'tcx, 'locals> {
+    fn new(tcx: TyCtxt<'tcx>, local_decls: &'locals LocalDecls<'tcx>) -> Self {
+        Self {
+            tcx,
+            local_decls,
+            before_effect: FxHashMap::default(),
+            assignments: FxHashMap::default(),
+        }
     }
 
-    fn make_operand(&self, scalar: ScalarTy<'tcx>) -> Operand<'tcx> {
+    fn make_operand(&self, scalar: ScalarInt, ty: Ty<'tcx>) -> Operand<'tcx> {
         Operand::Constant(Box::new(Constant {
             span: DUMMY_SP,
             user_ty: None,
-            literal: ConstantKind::Val(ConstValue::Scalar(scalar.0), scalar.1),
+            literal: ConstantKind::Val(ConstValue::Scalar(scalar.into()), ty),
         }))
     }
 }
 
 impl<'mir, 'tcx>
     ResultsVisitor<'mir, 'tcx, Results<'tcx, ValueAnalysisWrapper<ConstAnalysis<'_, 'tcx>>>>
-    for CollectAndPatch<'tcx>
+    for CollectAndPatch<'tcx, '_>
 {
-    type FlowState = State<FlatSet<ScalarTy<'tcx>>>;
+    type FlowState = State<FlatSet<ScalarInt>>;
 
     fn visit_statement_before_primary_effect(
         &mut self,
@@ -453,8 +520,8 @@ impl<'mir, 'tcx>
     }
 }
 
-impl<'tcx> MutVisitor<'tcx> for CollectAndPatch<'tcx> {
-    fn tcx<'a>(&'a self) -> TyCtxt<'tcx> {
+impl<'tcx> MutVisitor<'tcx> for CollectAndPatch<'tcx, '_> {
+    fn tcx(&self) -> TyCtxt<'tcx> {
         self.tcx
     }
 
@@ -462,7 +529,8 @@ impl<'tcx> MutVisitor<'tcx> for CollectAndPatch<'tcx> {
         if let Some(value) = self.assignments.get(&location) {
             match &mut statement.kind {
                 StatementKind::Assign(box (_, rvalue)) => {
-                    *rvalue = Rvalue::Use(self.make_operand(value.clone()));
+                    let ty = rvalue.ty(self.local_decls, self.tcx);
+                    *rvalue = Rvalue::Use(self.make_operand(*value, ty));
                 }
                 _ => bug!("found assignment info for non-assign statement"),
             }
@@ -475,33 +543,56 @@ impl<'tcx> MutVisitor<'tcx> for CollectAndPatch<'tcx> {
         match operand {
             Operand::Copy(place) | Operand::Move(place) => {
                 if let Some(value) = self.before_effect.get(&(location, *place)) {
-                    *operand = self.make_operand(value.clone());
+                    let ty = place.ty(self.local_decls, self.tcx).ty;
+                    *operand = self.make_operand(*value, ty);
+                } else if !place.projection.is_empty() {
+                    self.super_operand(operand, location)
                 }
             }
-            _ => (),
+            Operand::Constant(_) => {}
+        }
+    }
+
+    fn process_projection_elem(
+        &mut self,
+        elem: PlaceElem<'tcx>,
+        location: Location,
+    ) -> Option<PlaceElem<'tcx>> {
+        if let PlaceElem::Index(local) = elem
+            && let Some(value) = self.before_effect.get(&(location, local.into()))
+            && let Ok(offset) = value.try_to_target_usize(self.tcx)
+            && let Some(min_length) = offset.checked_add(1)
+        {
+            Some(PlaceElem::ConstantIndex { offset, min_length, from_end: false })
+        } else {
+            None
         }
     }
 }
 
-struct OperandCollector<'tcx, 'map, 'a> {
-    state: &'a State<FlatSet<ScalarTy<'tcx>>>,
-    visitor: &'a mut CollectAndPatch<'tcx>,
+struct OperandCollector<'tcx, 'map, 'locals, 'a> {
+    state: &'a State<FlatSet<ScalarInt>>,
+    visitor: &'a mut CollectAndPatch<'tcx, 'locals>,
     map: &'map Map,
 }
 
-impl<'tcx, 'map, 'a> Visitor<'tcx> for OperandCollector<'tcx, 'map, 'a> {
+impl<'tcx> Visitor<'tcx> for OperandCollector<'tcx, '_, '_, '_> {
     fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) {
-        match operand {
-            Operand::Copy(place) | Operand::Move(place) => {
-                match self.state.get(place.as_ref(), self.map) {
-                    FlatSet::Top => (),
-                    FlatSet::Elem(value) => {
-                        self.visitor.before_effect.insert((location, *place), value);
-                    }
-                    FlatSet::Bottom => (),
-                }
+        if let Some(place) = operand.place() {
+            if let FlatSet::Elem(value) = self.state.get(place.as_ref(), self.map) {
+                self.visitor.before_effect.insert((location, place), value);
+            } else if !place.projection.is_empty() {
+                // Try to propagate into `Index` projections.
+                self.super_operand(operand, location)
             }
-            _ => (),
+        }
+    }
+
+    fn visit_local(&mut self, local: Local, ctxt: PlaceContext, location: Location) {
+        if let PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy | NonMutatingUseContext::Move) = ctxt
+            && let FlatSet::Elem(value) = self.state.get(local.into(), self.map)
+        {
+            self.visitor.before_effect.insert((location, local.into()), value);
         }
     }
 }
@@ -572,7 +663,7 @@ impl<'mir, 'tcx: 'mir> rustc_const_eval::interpret::Machine<'mir, 'tcx> for Dumm
         _bin_op: BinOp,
         _left: &rustc_const_eval::interpret::ImmTy<'tcx, Self::Provenance>,
         _right: &rustc_const_eval::interpret::ImmTy<'tcx, Self::Provenance>,
-    ) -> interpret::InterpResult<'tcx, (interpret::Scalar<Self::Provenance>, bool, Ty<'tcx>)> {
+    ) -> interpret::InterpResult<'tcx, (Scalar<Self::Provenance>, bool, Ty<'tcx>)> {
         throw_unsup!(Unsupported("".into()))
     }
 
diff --git a/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.ConstProp.32bit.panic-abort.diff b/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.ConstProp.32bit.panic-abort.diff
index 55c774d555d..e443c8991f9 100644
--- a/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.ConstProp.32bit.panic-abort.diff
+++ b/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.ConstProp.32bit.panic-abort.diff
@@ -34,11 +34,11 @@
           StorageLive(_5);
           StorageLive(_6);
           _6 = const 3_usize;
-          _7 = const 3_usize;
+          _7 = Len((*_1));
 -         _8 = Lt(_6, _7);
 -         assert(move _8, "index out of bounds: the length is {} but the index is {}", move _7, _6) -> [success: bb1, unwind unreachable];
-+         _8 = const false;
-+         assert(const false, "index out of bounds: the length is {} but the index is {}", const 3_usize, const 3_usize) -> [success: bb1, unwind unreachable];
++         _8 = Lt(const 3_usize, _7);
++         assert(move _8, "index out of bounds: the length is {} but the index is {}", move _7, const 3_usize) -> [success: bb1, unwind unreachable];
       }
   
       bb1: {
diff --git a/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.ConstProp.32bit.panic-unwind.diff b/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.ConstProp.32bit.panic-unwind.diff
index dcab570ea77..592f43f4739 100644
--- a/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.ConstProp.32bit.panic-unwind.diff
+++ b/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.ConstProp.32bit.panic-unwind.diff
@@ -34,11 +34,11 @@
           StorageLive(_5);
           StorageLive(_6);
           _6 = const 3_usize;
-          _7 = const 3_usize;
+          _7 = Len((*_1));
 -         _8 = Lt(_6, _7);
 -         assert(move _8, "index out of bounds: the length is {} but the index is {}", move _7, _6) -> [success: bb1, unwind continue];
-+         _8 = const false;
-+         assert(const false, "index out of bounds: the length is {} but the index is {}", const 3_usize, const 3_usize) -> [success: bb1, unwind continue];
++         _8 = Lt(const 3_usize, _7);
++         assert(move _8, "index out of bounds: the length is {} but the index is {}", move _7, const 3_usize) -> [success: bb1, unwind continue];
       }
   
       bb1: {
diff --git a/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.ConstProp.64bit.panic-abort.diff b/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.ConstProp.64bit.panic-abort.diff
index 55c774d555d..e443c8991f9 100644
--- a/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.ConstProp.64bit.panic-abort.diff
+++ b/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.ConstProp.64bit.panic-abort.diff
@@ -34,11 +34,11 @@
           StorageLive(_5);
           StorageLive(_6);
           _6 = const 3_usize;
-          _7 = const 3_usize;
+          _7 = Len((*_1));
 -         _8 = Lt(_6, _7);
 -         assert(move _8, "index out of bounds: the length is {} but the index is {}", move _7, _6) -> [success: bb1, unwind unreachable];
-+         _8 = const false;
-+         assert(const false, "index out of bounds: the length is {} but the index is {}", const 3_usize, const 3_usize) -> [success: bb1, unwind unreachable];
++         _8 = Lt(const 3_usize, _7);
++         assert(move _8, "index out of bounds: the length is {} but the index is {}", move _7, const 3_usize) -> [success: bb1, unwind unreachable];
       }
   
       bb1: {
diff --git a/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.ConstProp.64bit.panic-unwind.diff b/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.ConstProp.64bit.panic-unwind.diff
index dcab570ea77..592f43f4739 100644
--- a/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.ConstProp.64bit.panic-unwind.diff
+++ b/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.ConstProp.64bit.panic-unwind.diff
@@ -34,11 +34,11 @@
           StorageLive(_5);
           StorageLive(_6);
           _6 = const 3_usize;
-          _7 = const 3_usize;
+          _7 = Len((*_1));
 -         _8 = Lt(_6, _7);
 -         assert(move _8, "index out of bounds: the length is {} but the index is {}", move _7, _6) -> [success: bb1, unwind continue];
-+         _8 = const false;
-+         assert(const false, "index out of bounds: the length is {} but the index is {}", const 3_usize, const 3_usize) -> [success: bb1, unwind continue];
++         _8 = Lt(const 3_usize, _7);
++         assert(move _8, "index out of bounds: the length is {} but the index is {}", move _7, const 3_usize) -> [success: bb1, unwind continue];
       }
   
       bb1: {
diff --git a/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.rs b/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.rs
index 7931c4f02ae..d6b1a93f304 100644
--- a/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.rs
+++ b/tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.rs
@@ -1,8 +1,7 @@
 // unit-test: ConstProp
 // EMIT_MIR_FOR_EACH_PANIC_STRATEGY
-// compile-flags: -Zmir-enable-passes=+NormalizeArrayLen
-
 // EMIT_MIR_FOR_EACH_BIT_WIDTH
+
 // EMIT_MIR bad_op_unsafe_oob_for_slices.main.ConstProp.diff
 #[allow(unconditional_panic)]
 fn main() {
diff --git a/tests/mir-opt/const_prop/large_array_index.rs b/tests/mir-opt/const_prop/large_array_index.rs
index 6c03fe9d9c2..d226bd54671 100644
--- a/tests/mir-opt/const_prop/large_array_index.rs
+++ b/tests/mir-opt/const_prop/large_array_index.rs
@@ -1,6 +1,5 @@
 // unit-test: ConstProp
 // EMIT_MIR_FOR_EACH_PANIC_STRATEGY
-// compile-flags: -Zmir-enable-passes=+NormalizeArrayLen
 // EMIT_MIR_FOR_EACH_BIT_WIDTH
 
 // EMIT_MIR large_array_index.main.ConstProp.diff
diff --git a/tests/mir-opt/const_prop/repeat.rs b/tests/mir-opt/const_prop/repeat.rs
index 21dba84af37..fb8b825ee36 100644
--- a/tests/mir-opt/const_prop/repeat.rs
+++ b/tests/mir-opt/const_prop/repeat.rs
@@ -1,6 +1,5 @@
 // unit-test: ConstProp
 // EMIT_MIR_FOR_EACH_PANIC_STRATEGY
-// compile-flags: -Zmir-enable-passes=+NormalizeArrayLen
 // EMIT_MIR_FOR_EACH_BIT_WIDTH
 
 // EMIT_MIR repeat.main.ConstProp.diff
diff --git a/tests/mir-opt/dataflow-const-prop/array_index.main.DataflowConstProp.32bit.panic-abort.diff b/tests/mir-opt/dataflow-const-prop/array_index.main.DataflowConstProp.32bit.panic-abort.diff
new file mode 100644
index 00000000000..212ddc5b154
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/array_index.main.DataflowConstProp.32bit.panic-abort.diff
@@ -0,0 +1,39 @@
+- // MIR for `main` before DataflowConstProp
++ // MIR for `main` after DataflowConstProp
+  
+  fn main() -> () {
+      let mut _0: ();
+      let _1: u32;
+      let mut _2: [u32; 4];
+      let _3: usize;
+      let mut _4: usize;
+      let mut _5: bool;
+      scope 1 {
+          debug x => _1;
+      }
+  
+      bb0: {
+          StorageLive(_1);
+          StorageLive(_2);
+          _2 = [const 0_u32, const 1_u32, const 2_u32, const 3_u32];
+          StorageLive(_3);
+          _3 = const 2_usize;
+-         _4 = Len(_2);
+-         _5 = Lt(_3, _4);
+-         assert(move _5, "index out of bounds: the length is {} but the index is {}", move _4, _3) -> [success: bb1, unwind unreachable];
++         _4 = const 4_usize;
++         _5 = const true;
++         assert(const true, "index out of bounds: the length is {} but the index is {}", const 4_usize, const 2_usize) -> [success: bb1, unwind unreachable];
+      }
+  
+      bb1: {
+-         _1 = _2[_3];
++         _1 = _2[2 of 3];
+          StorageDead(_3);
+          StorageDead(_2);
+          _0 = const ();
+          StorageDead(_1);
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/dataflow-const-prop/array_index.main.DataflowConstProp.32bit.panic-unwind.diff b/tests/mir-opt/dataflow-const-prop/array_index.main.DataflowConstProp.32bit.panic-unwind.diff
new file mode 100644
index 00000000000..5c53d4f4461
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/array_index.main.DataflowConstProp.32bit.panic-unwind.diff
@@ -0,0 +1,39 @@
+- // MIR for `main` before DataflowConstProp
++ // MIR for `main` after DataflowConstProp
+  
+  fn main() -> () {
+      let mut _0: ();
+      let _1: u32;
+      let mut _2: [u32; 4];
+      let _3: usize;
+      let mut _4: usize;
+      let mut _5: bool;
+      scope 1 {
+          debug x => _1;
+      }
+  
+      bb0: {
+          StorageLive(_1);
+          StorageLive(_2);
+          _2 = [const 0_u32, const 1_u32, const 2_u32, const 3_u32];
+          StorageLive(_3);
+          _3 = const 2_usize;
+-         _4 = Len(_2);
+-         _5 = Lt(_3, _4);
+-         assert(move _5, "index out of bounds: the length is {} but the index is {}", move _4, _3) -> [success: bb1, unwind continue];
++         _4 = const 4_usize;
++         _5 = const true;
++         assert(const true, "index out of bounds: the length is {} but the index is {}", const 4_usize, const 2_usize) -> [success: bb1, unwind continue];
+      }
+  
+      bb1: {
+-         _1 = _2[_3];
++         _1 = _2[2 of 3];
+          StorageDead(_3);
+          StorageDead(_2);
+          _0 = const ();
+          StorageDead(_1);
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/dataflow-const-prop/array_index.main.DataflowConstProp.64bit.panic-abort.diff b/tests/mir-opt/dataflow-const-prop/array_index.main.DataflowConstProp.64bit.panic-abort.diff
new file mode 100644
index 00000000000..212ddc5b154
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/array_index.main.DataflowConstProp.64bit.panic-abort.diff
@@ -0,0 +1,39 @@
+- // MIR for `main` before DataflowConstProp
++ // MIR for `main` after DataflowConstProp
+  
+  fn main() -> () {
+      let mut _0: ();
+      let _1: u32;
+      let mut _2: [u32; 4];
+      let _3: usize;
+      let mut _4: usize;
+      let mut _5: bool;
+      scope 1 {
+          debug x => _1;
+      }
+  
+      bb0: {
+          StorageLive(_1);
+          StorageLive(_2);
+          _2 = [const 0_u32, const 1_u32, const 2_u32, const 3_u32];
+          StorageLive(_3);
+          _3 = const 2_usize;
+-         _4 = Len(_2);
+-         _5 = Lt(_3, _4);
+-         assert(move _5, "index out of bounds: the length is {} but the index is {}", move _4, _3) -> [success: bb1, unwind unreachable];
++         _4 = const 4_usize;
++         _5 = const true;
++         assert(const true, "index out of bounds: the length is {} but the index is {}", const 4_usize, const 2_usize) -> [success: bb1, unwind unreachable];
+      }
+  
+      bb1: {
+-         _1 = _2[_3];
++         _1 = _2[2 of 3];
+          StorageDead(_3);
+          StorageDead(_2);
+          _0 = const ();
+          StorageDead(_1);
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/dataflow-const-prop/array_index.main.DataflowConstProp.64bit.panic-unwind.diff b/tests/mir-opt/dataflow-const-prop/array_index.main.DataflowConstProp.64bit.panic-unwind.diff
new file mode 100644
index 00000000000..5c53d4f4461
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/array_index.main.DataflowConstProp.64bit.panic-unwind.diff
@@ -0,0 +1,39 @@
+- // MIR for `main` before DataflowConstProp
++ // MIR for `main` after DataflowConstProp
+  
+  fn main() -> () {
+      let mut _0: ();
+      let _1: u32;
+      let mut _2: [u32; 4];
+      let _3: usize;
+      let mut _4: usize;
+      let mut _5: bool;
+      scope 1 {
+          debug x => _1;
+      }
+  
+      bb0: {
+          StorageLive(_1);
+          StorageLive(_2);
+          _2 = [const 0_u32, const 1_u32, const 2_u32, const 3_u32];
+          StorageLive(_3);
+          _3 = const 2_usize;
+-         _4 = Len(_2);
+-         _5 = Lt(_3, _4);
+-         assert(move _5, "index out of bounds: the length is {} but the index is {}", move _4, _3) -> [success: bb1, unwind continue];
++         _4 = const 4_usize;
++         _5 = const true;
++         assert(const true, "index out of bounds: the length is {} but the index is {}", const 4_usize, const 2_usize) -> [success: bb1, unwind continue];
+      }
+  
+      bb1: {
+-         _1 = _2[_3];
++         _1 = _2[2 of 3];
+          StorageDead(_3);
+          StorageDead(_2);
+          _0 = const ();
+          StorageDead(_1);
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/dataflow-const-prop/array_index.rs b/tests/mir-opt/dataflow-const-prop/array_index.rs
new file mode 100644
index 00000000000..ddb3646ca9b
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/array_index.rs
@@ -0,0 +1,8 @@
+// EMIT_MIR_FOR_EACH_PANIC_STRATEGY
+// unit-test: DataflowConstProp
+// EMIT_MIR_FOR_EACH_BIT_WIDTH
+
+// EMIT_MIR array_index.main.DataflowConstProp.diff
+fn main() {
+    let x: u32 = [0, 1, 2, 3][2];
+}
diff --git a/tests/mir-opt/dataflow-const-prop/boolean_identities.rs b/tests/mir-opt/dataflow-const-prop/boolean_identities.rs
new file mode 100644
index 00000000000..9e911e85b88
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/boolean_identities.rs
@@ -0,0 +1,10 @@
+// unit-test: DataflowConstProp
+
+// EMIT_MIR boolean_identities.test.DataflowConstProp.diff
+pub fn test(x: bool, y: bool) -> bool {
+    (y | true) & (x & false)
+}
+
+fn main() {
+    test(true, false);
+}
diff --git a/tests/mir-opt/dataflow-const-prop/boolean_identities.test.DataflowConstProp.diff b/tests/mir-opt/dataflow-const-prop/boolean_identities.test.DataflowConstProp.diff
new file mode 100644
index 00000000000..5440c38ce4b
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/boolean_identities.test.DataflowConstProp.diff
@@ -0,0 +1,33 @@
+- // MIR for `test` before DataflowConstProp
++ // MIR for `test` after DataflowConstProp
+  
+  fn test(_1: bool, _2: bool) -> bool {
+      debug x => _1;
+      debug y => _2;
+      let mut _0: bool;
+      let mut _3: bool;
+      let mut _4: bool;
+      let mut _5: bool;
+      let mut _6: bool;
+  
+      bb0: {
+          StorageLive(_3);
+          StorageLive(_4);
+          _4 = _2;
+-         _3 = BitOr(move _4, const true);
++         _3 = const true;
+          StorageDead(_4);
+          StorageLive(_5);
+          StorageLive(_6);
+          _6 = _1;
+-         _5 = BitAnd(move _6, const false);
++         _5 = const false;
+          StorageDead(_6);
+-         _0 = BitAnd(move _3, move _5);
++         _0 = const false;
+          StorageDead(_5);
+          StorageDead(_3);
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/dataflow-const-prop/large_array_index.main.DataflowConstProp.32bit.panic-abort.diff b/tests/mir-opt/dataflow-const-prop/large_array_index.main.DataflowConstProp.32bit.panic-abort.diff
new file mode 100644
index 00000000000..6c612d46725
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/large_array_index.main.DataflowConstProp.32bit.panic-abort.diff
@@ -0,0 +1,39 @@
+- // MIR for `main` before DataflowConstProp
++ // MIR for `main` after DataflowConstProp
+  
+  fn main() -> () {
+      let mut _0: ();
+      let _1: u8;
+      let mut _2: [u8; 5000];
+      let _3: usize;
+      let mut _4: usize;
+      let mut _5: bool;
+      scope 1 {
+          debug x => _1;
+      }
+  
+      bb0: {
+          StorageLive(_1);
+          StorageLive(_2);
+          _2 = [const 0_u8; 5000];
+          StorageLive(_3);
+          _3 = const 2_usize;
+-         _4 = Len(_2);
+-         _5 = Lt(_3, _4);
+-         assert(move _5, "index out of bounds: the length is {} but the index is {}", move _4, _3) -> [success: bb1, unwind unreachable];
++         _4 = const 5000_usize;
++         _5 = const true;
++         assert(const true, "index out of bounds: the length is {} but the index is {}", const 5000_usize, const 2_usize) -> [success: bb1, unwind unreachable];
+      }
+  
+      bb1: {
+-         _1 = _2[_3];
++         _1 = _2[2 of 3];
+          StorageDead(_3);
+          StorageDead(_2);
+          _0 = const ();
+          StorageDead(_1);
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/dataflow-const-prop/large_array_index.main.DataflowConstProp.32bit.panic-unwind.diff b/tests/mir-opt/dataflow-const-prop/large_array_index.main.DataflowConstProp.32bit.panic-unwind.diff
new file mode 100644
index 00000000000..87024da2628
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/large_array_index.main.DataflowConstProp.32bit.panic-unwind.diff
@@ -0,0 +1,39 @@
+- // MIR for `main` before DataflowConstProp
++ // MIR for `main` after DataflowConstProp
+  
+  fn main() -> () {
+      let mut _0: ();
+      let _1: u8;
+      let mut _2: [u8; 5000];
+      let _3: usize;
+      let mut _4: usize;
+      let mut _5: bool;
+      scope 1 {
+          debug x => _1;
+      }
+  
+      bb0: {
+          StorageLive(_1);
+          StorageLive(_2);
+          _2 = [const 0_u8; 5000];
+          StorageLive(_3);
+          _3 = const 2_usize;
+-         _4 = Len(_2);
+-         _5 = Lt(_3, _4);
+-         assert(move _5, "index out of bounds: the length is {} but the index is {}", move _4, _3) -> [success: bb1, unwind continue];
++         _4 = const 5000_usize;
++         _5 = const true;
++         assert(const true, "index out of bounds: the length is {} but the index is {}", const 5000_usize, const 2_usize) -> [success: bb1, unwind continue];
+      }
+  
+      bb1: {
+-         _1 = _2[_3];
++         _1 = _2[2 of 3];
+          StorageDead(_3);
+          StorageDead(_2);
+          _0 = const ();
+          StorageDead(_1);
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/dataflow-const-prop/large_array_index.main.DataflowConstProp.64bit.panic-abort.diff b/tests/mir-opt/dataflow-const-prop/large_array_index.main.DataflowConstProp.64bit.panic-abort.diff
new file mode 100644
index 00000000000..6c612d46725
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/large_array_index.main.DataflowConstProp.64bit.panic-abort.diff
@@ -0,0 +1,39 @@
+- // MIR for `main` before DataflowConstProp
++ // MIR for `main` after DataflowConstProp
+  
+  fn main() -> () {
+      let mut _0: ();
+      let _1: u8;
+      let mut _2: [u8; 5000];
+      let _3: usize;
+      let mut _4: usize;
+      let mut _5: bool;
+      scope 1 {
+          debug x => _1;
+      }
+  
+      bb0: {
+          StorageLive(_1);
+          StorageLive(_2);
+          _2 = [const 0_u8; 5000];
+          StorageLive(_3);
+          _3 = const 2_usize;
+-         _4 = Len(_2);
+-         _5 = Lt(_3, _4);
+-         assert(move _5, "index out of bounds: the length is {} but the index is {}", move _4, _3) -> [success: bb1, unwind unreachable];
++         _4 = const 5000_usize;
++         _5 = const true;
++         assert(const true, "index out of bounds: the length is {} but the index is {}", const 5000_usize, const 2_usize) -> [success: bb1, unwind unreachable];
+      }
+  
+      bb1: {
+-         _1 = _2[_3];
++         _1 = _2[2 of 3];
+          StorageDead(_3);
+          StorageDead(_2);
+          _0 = const ();
+          StorageDead(_1);
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/dataflow-const-prop/large_array_index.main.DataflowConstProp.64bit.panic-unwind.diff b/tests/mir-opt/dataflow-const-prop/large_array_index.main.DataflowConstProp.64bit.panic-unwind.diff
new file mode 100644
index 00000000000..87024da2628
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/large_array_index.main.DataflowConstProp.64bit.panic-unwind.diff
@@ -0,0 +1,39 @@
+- // MIR for `main` before DataflowConstProp
++ // MIR for `main` after DataflowConstProp
+  
+  fn main() -> () {
+      let mut _0: ();
+      let _1: u8;
+      let mut _2: [u8; 5000];
+      let _3: usize;
+      let mut _4: usize;
+      let mut _5: bool;
+      scope 1 {
+          debug x => _1;
+      }
+  
+      bb0: {
+          StorageLive(_1);
+          StorageLive(_2);
+          _2 = [const 0_u8; 5000];
+          StorageLive(_3);
+          _3 = const 2_usize;
+-         _4 = Len(_2);
+-         _5 = Lt(_3, _4);
+-         assert(move _5, "index out of bounds: the length is {} but the index is {}", move _4, _3) -> [success: bb1, unwind continue];
++         _4 = const 5000_usize;
++         _5 = const true;
++         assert(const true, "index out of bounds: the length is {} but the index is {}", const 5000_usize, const 2_usize) -> [success: bb1, unwind continue];
+      }
+  
+      bb1: {
+-         _1 = _2[_3];
++         _1 = _2[2 of 3];
+          StorageDead(_3);
+          StorageDead(_2);
+          _0 = const ();
+          StorageDead(_1);
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/dataflow-const-prop/large_array_index.rs b/tests/mir-opt/dataflow-const-prop/large_array_index.rs
new file mode 100644
index 00000000000..af13c7d1020
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/large_array_index.rs
@@ -0,0 +1,9 @@
+// unit-test: DataflowConstProp
+// EMIT_MIR_FOR_EACH_PANIC_STRATEGY
+// EMIT_MIR_FOR_EACH_BIT_WIDTH
+
+// EMIT_MIR large_array_index.main.DataflowConstProp.diff
+fn main() {
+    // check that we don't propagate this, because it's too large
+    let x: u8 = [0_u8; 5000][2];
+}
diff --git a/tests/mir-opt/dataflow-const-prop/mult_by_zero.rs b/tests/mir-opt/dataflow-const-prop/mult_by_zero.rs
new file mode 100644
index 00000000000..dbea1480445
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/mult_by_zero.rs
@@ -0,0 +1,10 @@
+// unit-test: DataflowConstProp
+
+// EMIT_MIR mult_by_zero.test.DataflowConstProp.diff
+fn test(x : i32) -> i32 {
+  x * 0
+}
+
+fn main() {
+    test(10);
+}
diff --git a/tests/mir-opt/dataflow-const-prop/mult_by_zero.test.DataflowConstProp.diff b/tests/mir-opt/dataflow-const-prop/mult_by_zero.test.DataflowConstProp.diff
new file mode 100644
index 00000000000..91bc10a562f
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/mult_by_zero.test.DataflowConstProp.diff
@@ -0,0 +1,18 @@
+- // MIR for `test` before DataflowConstProp
++ // MIR for `test` after DataflowConstProp
+  
+  fn test(_1: i32) -> i32 {
+      debug x => _1;
+      let mut _0: i32;
+      let mut _2: i32;
+  
+      bb0: {
+          StorageLive(_2);
+          _2 = _1;
+-         _0 = Mul(move _2, const 0_i32);
++         _0 = const 0_i32;
+          StorageDead(_2);
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/dataflow-const-prop/offset_of.concrete.DataflowConstProp.panic-abort.diff b/tests/mir-opt/dataflow-const-prop/offset_of.concrete.DataflowConstProp.panic-abort.diff
new file mode 100644
index 00000000000..c61414b6541
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/offset_of.concrete.DataflowConstProp.panic-abort.diff
@@ -0,0 +1,76 @@
+- // MIR for `concrete` before DataflowConstProp
++ // MIR for `concrete` after DataflowConstProp
+  
+  fn concrete() -> () {
+      let mut _0: ();
+      let _1: usize;
+      let mut _2: usize;
+      let mut _4: usize;
+      let mut _6: usize;
+      let mut _8: usize;
+      scope 1 {
+          debug x => _1;
+          let _3: usize;
+          scope 2 {
+              debug y => _3;
+              let _5: usize;
+              scope 3 {
+                  debug z0 => _5;
+                  let _7: usize;
+                  scope 4 {
+                      debug z1 => _7;
+                  }
+              }
+          }
+      }
+  
+      bb0: {
+          StorageLive(_1);
+          StorageLive(_2);
+-         _2 = OffsetOf(Alpha, [0]);
+-         _1 = must_use::<usize>(move _2) -> [return: bb1, unwind unreachable];
++         _2 = const 4_usize;
++         _1 = must_use::<usize>(const 4_usize) -> [return: bb1, unwind unreachable];
+      }
+  
+      bb1: {
+          StorageDead(_2);
+          StorageLive(_3);
+          StorageLive(_4);
+-         _4 = OffsetOf(Alpha, [1]);
+-         _3 = must_use::<usize>(move _4) -> [return: bb2, unwind unreachable];
++         _4 = const 0_usize;
++         _3 = must_use::<usize>(const 0_usize) -> [return: bb2, unwind unreachable];
+      }
+  
+      bb2: {
+          StorageDead(_4);
+          StorageLive(_5);
+          StorageLive(_6);
+-         _6 = OffsetOf(Alpha, [2, 0]);
+-         _5 = must_use::<usize>(move _6) -> [return: bb3, unwind unreachable];
++         _6 = const 2_usize;
++         _5 = must_use::<usize>(const 2_usize) -> [return: bb3, unwind unreachable];
+      }
+  
+      bb3: {
+          StorageDead(_6);
+          StorageLive(_7);
+          StorageLive(_8);
+-         _8 = OffsetOf(Alpha, [2, 1]);
+-         _7 = must_use::<usize>(move _8) -> [return: bb4, unwind unreachable];
++         _8 = const 3_usize;
++         _7 = must_use::<usize>(const 3_usize) -> [return: bb4, unwind unreachable];
+      }
+  
+      bb4: {
+          StorageDead(_8);
+          _0 = const ();
+          StorageDead(_7);
+          StorageDead(_5);
+          StorageDead(_3);
+          StorageDead(_1);
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/dataflow-const-prop/offset_of.concrete.DataflowConstProp.panic-unwind.diff b/tests/mir-opt/dataflow-const-prop/offset_of.concrete.DataflowConstProp.panic-unwind.diff
new file mode 100644
index 00000000000..0c3939a3456
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/offset_of.concrete.DataflowConstProp.panic-unwind.diff
@@ -0,0 +1,76 @@
+- // MIR for `concrete` before DataflowConstProp
++ // MIR for `concrete` after DataflowConstProp
+  
+  fn concrete() -> () {
+      let mut _0: ();
+      let _1: usize;
+      let mut _2: usize;
+      let mut _4: usize;
+      let mut _6: usize;
+      let mut _8: usize;
+      scope 1 {
+          debug x => _1;
+          let _3: usize;
+          scope 2 {
+              debug y => _3;
+              let _5: usize;
+              scope 3 {
+                  debug z0 => _5;
+                  let _7: usize;
+                  scope 4 {
+                      debug z1 => _7;
+                  }
+              }
+          }
+      }
+  
+      bb0: {
+          StorageLive(_1);
+          StorageLive(_2);
+-         _2 = OffsetOf(Alpha, [0]);
+-         _1 = must_use::<usize>(move _2) -> [return: bb1, unwind continue];
++         _2 = const 4_usize;
++         _1 = must_use::<usize>(const 4_usize) -> [return: bb1, unwind continue];
+      }
+  
+      bb1: {
+          StorageDead(_2);
+          StorageLive(_3);
+          StorageLive(_4);
+-         _4 = OffsetOf(Alpha, [1]);
+-         _3 = must_use::<usize>(move _4) -> [return: bb2, unwind continue];
++         _4 = const 0_usize;
++         _3 = must_use::<usize>(const 0_usize) -> [return: bb2, unwind continue];
+      }
+  
+      bb2: {
+          StorageDead(_4);
+          StorageLive(_5);
+          StorageLive(_6);
+-         _6 = OffsetOf(Alpha, [2, 0]);
+-         _5 = must_use::<usize>(move _6) -> [return: bb3, unwind continue];
++         _6 = const 2_usize;
++         _5 = must_use::<usize>(const 2_usize) -> [return: bb3, unwind continue];
+      }
+  
+      bb3: {
+          StorageDead(_6);
+          StorageLive(_7);
+          StorageLive(_8);
+-         _8 = OffsetOf(Alpha, [2, 1]);
+-         _7 = must_use::<usize>(move _8) -> [return: bb4, unwind continue];
++         _8 = const 3_usize;
++         _7 = must_use::<usize>(const 3_usize) -> [return: bb4, unwind continue];
+      }
+  
+      bb4: {
+          StorageDead(_8);
+          _0 = const ();
+          StorageDead(_7);
+          StorageDead(_5);
+          StorageDead(_3);
+          StorageDead(_1);
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/dataflow-const-prop/offset_of.generic.DataflowConstProp.panic-abort.diff b/tests/mir-opt/dataflow-const-prop/offset_of.generic.DataflowConstProp.panic-abort.diff
new file mode 100644
index 00000000000..d54d4687060
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/offset_of.generic.DataflowConstProp.panic-abort.diff
@@ -0,0 +1,72 @@
+- // MIR for `generic` before DataflowConstProp
++ // MIR for `generic` after DataflowConstProp
+  
+  fn generic() -> () {
+      let mut _0: ();
+      let _1: usize;
+      let mut _2: usize;
+      let mut _4: usize;
+      let mut _6: usize;
+      let mut _8: usize;
+      scope 1 {
+          debug gx => _1;
+          let _3: usize;
+          scope 2 {
+              debug gy => _3;
+              let _5: usize;
+              scope 3 {
+                  debug dx => _5;
+                  let _7: usize;
+                  scope 4 {
+                      debug dy => _7;
+                  }
+              }
+          }
+      }
+  
+      bb0: {
+          StorageLive(_1);
+          StorageLive(_2);
+          _2 = OffsetOf(Gamma<T>, [0]);
+          _1 = must_use::<usize>(move _2) -> [return: bb1, unwind unreachable];
+      }
+  
+      bb1: {
+          StorageDead(_2);
+          StorageLive(_3);
+          StorageLive(_4);
+          _4 = OffsetOf(Gamma<T>, [1]);
+          _3 = must_use::<usize>(move _4) -> [return: bb2, unwind unreachable];
+      }
+  
+      bb2: {
+          StorageDead(_4);
+          StorageLive(_5);
+          StorageLive(_6);
+-         _6 = OffsetOf(Delta<T>, [1]);
+-         _5 = must_use::<usize>(move _6) -> [return: bb3, unwind unreachable];
++         _6 = const 0_usize;
++         _5 = must_use::<usize>(const 0_usize) -> [return: bb3, unwind unreachable];
+      }
+  
+      bb3: {
+          StorageDead(_6);
+          StorageLive(_7);
+          StorageLive(_8);
+-         _8 = OffsetOf(Delta<T>, [2]);
+-         _7 = must_use::<usize>(move _8) -> [return: bb4, unwind unreachable];
++         _8 = const 2_usize;
++         _7 = must_use::<usize>(const 2_usize) -> [return: bb4, unwind unreachable];
+      }
+  
+      bb4: {
+          StorageDead(_8);
+          _0 = const ();
+          StorageDead(_7);
+          StorageDead(_5);
+          StorageDead(_3);
+          StorageDead(_1);
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/dataflow-const-prop/offset_of.generic.DataflowConstProp.panic-unwind.diff b/tests/mir-opt/dataflow-const-prop/offset_of.generic.DataflowConstProp.panic-unwind.diff
new file mode 100644
index 00000000000..6032a2274ef
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/offset_of.generic.DataflowConstProp.panic-unwind.diff
@@ -0,0 +1,72 @@
+- // MIR for `generic` before DataflowConstProp
++ // MIR for `generic` after DataflowConstProp
+  
+  fn generic() -> () {
+      let mut _0: ();
+      let _1: usize;
+      let mut _2: usize;
+      let mut _4: usize;
+      let mut _6: usize;
+      let mut _8: usize;
+      scope 1 {
+          debug gx => _1;
+          let _3: usize;
+          scope 2 {
+              debug gy => _3;
+              let _5: usize;
+              scope 3 {
+                  debug dx => _5;
+                  let _7: usize;
+                  scope 4 {
+                      debug dy => _7;
+                  }
+              }
+          }
+      }
+  
+      bb0: {
+          StorageLive(_1);
+          StorageLive(_2);
+          _2 = OffsetOf(Gamma<T>, [0]);
+          _1 = must_use::<usize>(move _2) -> [return: bb1, unwind continue];
+      }
+  
+      bb1: {
+          StorageDead(_2);
+          StorageLive(_3);
+          StorageLive(_4);
+          _4 = OffsetOf(Gamma<T>, [1]);
+          _3 = must_use::<usize>(move _4) -> [return: bb2, unwind continue];
+      }
+  
+      bb2: {
+          StorageDead(_4);
+          StorageLive(_5);
+          StorageLive(_6);
+-         _6 = OffsetOf(Delta<T>, [1]);
+-         _5 = must_use::<usize>(move _6) -> [return: bb3, unwind continue];
++         _6 = const 0_usize;
++         _5 = must_use::<usize>(const 0_usize) -> [return: bb3, unwind continue];
+      }
+  
+      bb3: {
+          StorageDead(_6);
+          StorageLive(_7);
+          StorageLive(_8);
+-         _8 = OffsetOf(Delta<T>, [2]);
+-         _7 = must_use::<usize>(move _8) -> [return: bb4, unwind continue];
++         _8 = const 2_usize;
++         _7 = must_use::<usize>(const 2_usize) -> [return: bb4, unwind continue];
+      }
+  
+      bb4: {
+          StorageDead(_8);
+          _0 = const ();
+          StorageDead(_7);
+          StorageDead(_5);
+          StorageDead(_3);
+          StorageDead(_1);
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/dataflow-const-prop/offset_of.rs b/tests/mir-opt/dataflow-const-prop/offset_of.rs
new file mode 100644
index 00000000000..ccc90790e52
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/offset_of.rs
@@ -0,0 +1,49 @@
+// unit-test: DataflowConstProp
+// EMIT_MIR_FOR_EACH_PANIC_STRATEGY
+
+#![feature(offset_of)]
+
+use std::marker::PhantomData;
+use std::mem::offset_of;
+
+struct Alpha {
+    x: u8,
+    y: u16,
+    z: Beta,
+}
+
+struct Beta(u8, u8);
+
+struct Gamma<T> {
+    x: u8,
+    y: u16,
+    _t: T,
+}
+
+#[repr(C)]
+struct Delta<T> {
+    _phantom: PhantomData<T>,
+    x: u8,
+    y: u16,
+}
+
+// EMIT_MIR offset_of.concrete.DataflowConstProp.diff
+fn concrete() {
+    let x = offset_of!(Alpha, x);
+    let y = offset_of!(Alpha, y);
+    let z0 = offset_of!(Alpha, z.0);
+    let z1 = offset_of!(Alpha, z.1);
+}
+
+// EMIT_MIR offset_of.generic.DataflowConstProp.diff
+fn generic<T>() {
+    let gx = offset_of!(Gamma<T>, x);
+    let gy = offset_of!(Gamma<T>, y);
+    let dx = offset_of!(Delta<T>, x);
+    let dy = offset_of!(Delta<T>, y);
+}
+
+fn main() {
+    concrete();
+    generic::<()>();
+}
diff --git a/tests/mir-opt/dataflow-const-prop/repeat.main.DataflowConstProp.32bit.panic-abort.diff b/tests/mir-opt/dataflow-const-prop/repeat.main.DataflowConstProp.32bit.panic-abort.diff
new file mode 100644
index 00000000000..a18ef6c9db7
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/repeat.main.DataflowConstProp.32bit.panic-abort.diff
@@ -0,0 +1,43 @@
+- // MIR for `main` before DataflowConstProp
++ // MIR for `main` after DataflowConstProp
+  
+  fn main() -> () {
+      let mut _0: ();
+      let _1: u32;
+      let mut _2: u32;
+      let mut _3: [u32; 8];
+      let _4: usize;
+      let mut _5: usize;
+      let mut _6: bool;
+      scope 1 {
+          debug x => _1;
+      }
+  
+      bb0: {
+          StorageLive(_1);
+          StorageLive(_2);
+          StorageLive(_3);
+          _3 = [const 42_u32; 8];
+          StorageLive(_4);
+          _4 = const 2_usize;
+-         _5 = Len(_3);
+-         _6 = Lt(_4, _5);
+-         assert(move _6, "index out of bounds: the length is {} but the index is {}", move _5, _4) -> [success: bb1, unwind unreachable];
++         _5 = const 8_usize;
++         _6 = const true;
++         assert(const true, "index out of bounds: the length is {} but the index is {}", const 8_usize, const 2_usize) -> [success: bb1, unwind unreachable];
+      }
+  
+      bb1: {
+-         _2 = _3[_4];
++         _2 = _3[2 of 3];
+          _1 = Add(move _2, const 0_u32);
+          StorageDead(_2);
+          StorageDead(_4);
+          StorageDead(_3);
+          _0 = const ();
+          StorageDead(_1);
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/dataflow-const-prop/repeat.main.DataflowConstProp.32bit.panic-unwind.diff b/tests/mir-opt/dataflow-const-prop/repeat.main.DataflowConstProp.32bit.panic-unwind.diff
new file mode 100644
index 00000000000..3356ef98b14
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/repeat.main.DataflowConstProp.32bit.panic-unwind.diff
@@ -0,0 +1,43 @@
+- // MIR for `main` before DataflowConstProp
++ // MIR for `main` after DataflowConstProp
+  
+  fn main() -> () {
+      let mut _0: ();
+      let _1: u32;
+      let mut _2: u32;
+      let mut _3: [u32; 8];
+      let _4: usize;
+      let mut _5: usize;
+      let mut _6: bool;
+      scope 1 {
+          debug x => _1;
+      }
+  
+      bb0: {
+          StorageLive(_1);
+          StorageLive(_2);
+          StorageLive(_3);
+          _3 = [const 42_u32; 8];
+          StorageLive(_4);
+          _4 = const 2_usize;
+-         _5 = Len(_3);
+-         _6 = Lt(_4, _5);
+-         assert(move _6, "index out of bounds: the length is {} but the index is {}", move _5, _4) -> [success: bb1, unwind continue];
++         _5 = const 8_usize;
++         _6 = const true;
++         assert(const true, "index out of bounds: the length is {} but the index is {}", const 8_usize, const 2_usize) -> [success: bb1, unwind continue];
+      }
+  
+      bb1: {
+-         _2 = _3[_4];
++         _2 = _3[2 of 3];
+          _1 = Add(move _2, const 0_u32);
+          StorageDead(_2);
+          StorageDead(_4);
+          StorageDead(_3);
+          _0 = const ();
+          StorageDead(_1);
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/dataflow-const-prop/repeat.main.DataflowConstProp.64bit.panic-abort.diff b/tests/mir-opt/dataflow-const-prop/repeat.main.DataflowConstProp.64bit.panic-abort.diff
new file mode 100644
index 00000000000..a18ef6c9db7
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/repeat.main.DataflowConstProp.64bit.panic-abort.diff
@@ -0,0 +1,43 @@
+- // MIR for `main` before DataflowConstProp
++ // MIR for `main` after DataflowConstProp
+  
+  fn main() -> () {
+      let mut _0: ();
+      let _1: u32;
+      let mut _2: u32;
+      let mut _3: [u32; 8];
+      let _4: usize;
+      let mut _5: usize;
+      let mut _6: bool;
+      scope 1 {
+          debug x => _1;
+      }
+  
+      bb0: {
+          StorageLive(_1);
+          StorageLive(_2);
+          StorageLive(_3);
+          _3 = [const 42_u32; 8];
+          StorageLive(_4);
+          _4 = const 2_usize;
+-         _5 = Len(_3);
+-         _6 = Lt(_4, _5);
+-         assert(move _6, "index out of bounds: the length is {} but the index is {}", move _5, _4) -> [success: bb1, unwind unreachable];
++         _5 = const 8_usize;
++         _6 = const true;
++         assert(const true, "index out of bounds: the length is {} but the index is {}", const 8_usize, const 2_usize) -> [success: bb1, unwind unreachable];
+      }
+  
+      bb1: {
+-         _2 = _3[_4];
++         _2 = _3[2 of 3];
+          _1 = Add(move _2, const 0_u32);
+          StorageDead(_2);
+          StorageDead(_4);
+          StorageDead(_3);
+          _0 = const ();
+          StorageDead(_1);
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/dataflow-const-prop/repeat.main.DataflowConstProp.64bit.panic-unwind.diff b/tests/mir-opt/dataflow-const-prop/repeat.main.DataflowConstProp.64bit.panic-unwind.diff
new file mode 100644
index 00000000000..3356ef98b14
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/repeat.main.DataflowConstProp.64bit.panic-unwind.diff
@@ -0,0 +1,43 @@
+- // MIR for `main` before DataflowConstProp
++ // MIR for `main` after DataflowConstProp
+  
+  fn main() -> () {
+      let mut _0: ();
+      let _1: u32;
+      let mut _2: u32;
+      let mut _3: [u32; 8];
+      let _4: usize;
+      let mut _5: usize;
+      let mut _6: bool;
+      scope 1 {
+          debug x => _1;
+      }
+  
+      bb0: {
+          StorageLive(_1);
+          StorageLive(_2);
+          StorageLive(_3);
+          _3 = [const 42_u32; 8];
+          StorageLive(_4);
+          _4 = const 2_usize;
+-         _5 = Len(_3);
+-         _6 = Lt(_4, _5);
+-         assert(move _6, "index out of bounds: the length is {} but the index is {}", move _5, _4) -> [success: bb1, unwind continue];
++         _5 = const 8_usize;
++         _6 = const true;
++         assert(const true, "index out of bounds: the length is {} but the index is {}", const 8_usize, const 2_usize) -> [success: bb1, unwind continue];
+      }
+  
+      bb1: {
+-         _2 = _3[_4];
++         _2 = _3[2 of 3];
+          _1 = Add(move _2, const 0_u32);
+          StorageDead(_2);
+          StorageDead(_4);
+          StorageDead(_3);
+          _0 = const ();
+          StorageDead(_1);
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/dataflow-const-prop/repeat.rs b/tests/mir-opt/dataflow-const-prop/repeat.rs
new file mode 100644
index 00000000000..9fa353e44c5
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/repeat.rs
@@ -0,0 +1,8 @@
+// unit-test: DataflowConstProp
+// EMIT_MIR_FOR_EACH_PANIC_STRATEGY
+// EMIT_MIR_FOR_EACH_BIT_WIDTH
+
+// EMIT_MIR repeat.main.DataflowConstProp.diff
+fn main() {
+    let x: u32 = [42; 8][2] + 0;
+}
diff --git a/tests/mir-opt/dataflow-const-prop/slice_len.main.DataflowConstProp.32bit.panic-abort.diff b/tests/mir-opt/dataflow-const-prop/slice_len.main.DataflowConstProp.32bit.panic-abort.diff
new file mode 100644
index 00000000000..be55d259dcf
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/slice_len.main.DataflowConstProp.32bit.panic-abort.diff
@@ -0,0 +1,76 @@
+- // MIR for `main` before DataflowConstProp
++ // MIR for `main` after DataflowConstProp
+  
+  fn main() -> () {
+      let mut _0: ();
+      let _1: u32;
+      let mut _2: &[u32];
+      let mut _3: &[u32; 3];
+      let _4: &[u32; 3];
+      let _5: [u32; 3];
+      let _6: usize;
+      let mut _7: usize;
+      let mut _8: bool;
+      let mut _10: &[u32];
+      let _11: usize;
+      let mut _12: usize;
+      let mut _13: bool;
+      let mut _14: &[u32; 3];
+      scope 1 {
+          debug local => _1;
+          let _9: u32;
+          scope 2 {
+              debug constant => _9;
+          }
+      }
+  
+      bb0: {
+          StorageLive(_1);
+          StorageLive(_2);
+          StorageLive(_3);
+          StorageLive(_4);
+          _14 = const _;
+          _4 = _14;
+          _3 = _4;
+          _2 = move _3 as &[u32] (PointerCoercion(Unsize));
+          StorageDead(_3);
+          StorageLive(_6);
+          _6 = const 1_usize;
+-         _7 = Len((*_2));
+-         _8 = Lt(_6, _7);
+-         assert(move _8, "index out of bounds: the length is {} but the index is {}", move _7, _6) -> [success: bb1, unwind unreachable];
++         _7 = const 3_usize;
++         _8 = const true;
++         assert(const true, "index out of bounds: the length is {} but the index is {}", const 3_usize, const 1_usize) -> [success: bb1, unwind unreachable];
+      }
+  
+      bb1: {
+-         _1 = (*_2)[_6];
++         _1 = (*_2)[1 of 2];
+          StorageDead(_6);
+          StorageDead(_4);
+          StorageDead(_2);
+          StorageLive(_9);
+          StorageLive(_10);
+          _10 = const _;
+          StorageLive(_11);
+          _11 = const 1_usize;
+          _12 = Len((*_10));
+-         _13 = Lt(_11, _12);
+-         assert(move _13, "index out of bounds: the length is {} but the index is {}", move _12, _11) -> [success: bb2, unwind unreachable];
++         _13 = Lt(const 1_usize, _12);
++         assert(move _13, "index out of bounds: the length is {} but the index is {}", move _12, const 1_usize) -> [success: bb2, unwind unreachable];
+      }
+  
+      bb2: {
+-         _9 = (*_10)[_11];
++         _9 = (*_10)[1 of 2];
+          StorageDead(_11);
+          StorageDead(_10);
+          _0 = const ();
+          StorageDead(_9);
+          StorageDead(_1);
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/dataflow-const-prop/slice_len.main.DataflowConstProp.32bit.panic-unwind.diff b/tests/mir-opt/dataflow-const-prop/slice_len.main.DataflowConstProp.32bit.panic-unwind.diff
new file mode 100644
index 00000000000..f9a6c509ac8
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/slice_len.main.DataflowConstProp.32bit.panic-unwind.diff
@@ -0,0 +1,76 @@
+- // MIR for `main` before DataflowConstProp
++ // MIR for `main` after DataflowConstProp
+  
+  fn main() -> () {
+      let mut _0: ();
+      let _1: u32;
+      let mut _2: &[u32];
+      let mut _3: &[u32; 3];
+      let _4: &[u32; 3];
+      let _5: [u32; 3];
+      let _6: usize;
+      let mut _7: usize;
+      let mut _8: bool;
+      let mut _10: &[u32];
+      let _11: usize;
+      let mut _12: usize;
+      let mut _13: bool;
+      let mut _14: &[u32; 3];
+      scope 1 {
+          debug local => _1;
+          let _9: u32;
+          scope 2 {
+              debug constant => _9;
+          }
+      }
+  
+      bb0: {
+          StorageLive(_1);
+          StorageLive(_2);
+          StorageLive(_3);
+          StorageLive(_4);
+          _14 = const _;
+          _4 = _14;
+          _3 = _4;
+          _2 = move _3 as &[u32] (PointerCoercion(Unsize));
+          StorageDead(_3);
+          StorageLive(_6);
+          _6 = const 1_usize;
+-         _7 = Len((*_2));
+-         _8 = Lt(_6, _7);
+-         assert(move _8, "index out of bounds: the length is {} but the index is {}", move _7, _6) -> [success: bb1, unwind continue];
++         _7 = const 3_usize;
++         _8 = const true;
++         assert(const true, "index out of bounds: the length is {} but the index is {}", const 3_usize, const 1_usize) -> [success: bb1, unwind continue];
+      }
+  
+      bb1: {
+-         _1 = (*_2)[_6];
++         _1 = (*_2)[1 of 2];
+          StorageDead(_6);
+          StorageDead(_4);
+          StorageDead(_2);
+          StorageLive(_9);
+          StorageLive(_10);
+          _10 = const _;
+          StorageLive(_11);
+          _11 = const 1_usize;
+          _12 = Len((*_10));
+-         _13 = Lt(_11, _12);
+-         assert(move _13, "index out of bounds: the length is {} but the index is {}", move _12, _11) -> [success: bb2, unwind continue];
++         _13 = Lt(const 1_usize, _12);
++         assert(move _13, "index out of bounds: the length is {} but the index is {}", move _12, const 1_usize) -> [success: bb2, unwind continue];
+      }
+  
+      bb2: {
+-         _9 = (*_10)[_11];
++         _9 = (*_10)[1 of 2];
+          StorageDead(_11);
+          StorageDead(_10);
+          _0 = const ();
+          StorageDead(_9);
+          StorageDead(_1);
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/dataflow-const-prop/slice_len.main.DataflowConstProp.64bit.panic-abort.diff b/tests/mir-opt/dataflow-const-prop/slice_len.main.DataflowConstProp.64bit.panic-abort.diff
new file mode 100644
index 00000000000..be55d259dcf
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/slice_len.main.DataflowConstProp.64bit.panic-abort.diff
@@ -0,0 +1,76 @@
+- // MIR for `main` before DataflowConstProp
++ // MIR for `main` after DataflowConstProp
+  
+  fn main() -> () {
+      let mut _0: ();
+      let _1: u32;
+      let mut _2: &[u32];
+      let mut _3: &[u32; 3];
+      let _4: &[u32; 3];
+      let _5: [u32; 3];
+      let _6: usize;
+      let mut _7: usize;
+      let mut _8: bool;
+      let mut _10: &[u32];
+      let _11: usize;
+      let mut _12: usize;
+      let mut _13: bool;
+      let mut _14: &[u32; 3];
+      scope 1 {
+          debug local => _1;
+          let _9: u32;
+          scope 2 {
+              debug constant => _9;
+          }
+      }
+  
+      bb0: {
+          StorageLive(_1);
+          StorageLive(_2);
+          StorageLive(_3);
+          StorageLive(_4);
+          _14 = const _;
+          _4 = _14;
+          _3 = _4;
+          _2 = move _3 as &[u32] (PointerCoercion(Unsize));
+          StorageDead(_3);
+          StorageLive(_6);
+          _6 = const 1_usize;
+-         _7 = Len((*_2));
+-         _8 = Lt(_6, _7);
+-         assert(move _8, "index out of bounds: the length is {} but the index is {}", move _7, _6) -> [success: bb1, unwind unreachable];
++         _7 = const 3_usize;
++         _8 = const true;
++         assert(const true, "index out of bounds: the length is {} but the index is {}", const 3_usize, const 1_usize) -> [success: bb1, unwind unreachable];
+      }
+  
+      bb1: {
+-         _1 = (*_2)[_6];
++         _1 = (*_2)[1 of 2];
+          StorageDead(_6);
+          StorageDead(_4);
+          StorageDead(_2);
+          StorageLive(_9);
+          StorageLive(_10);
+          _10 = const _;
+          StorageLive(_11);
+          _11 = const 1_usize;
+          _12 = Len((*_10));
+-         _13 = Lt(_11, _12);
+-         assert(move _13, "index out of bounds: the length is {} but the index is {}", move _12, _11) -> [success: bb2, unwind unreachable];
++         _13 = Lt(const 1_usize, _12);
++         assert(move _13, "index out of bounds: the length is {} but the index is {}", move _12, const 1_usize) -> [success: bb2, unwind unreachable];
+      }
+  
+      bb2: {
+-         _9 = (*_10)[_11];
++         _9 = (*_10)[1 of 2];
+          StorageDead(_11);
+          StorageDead(_10);
+          _0 = const ();
+          StorageDead(_9);
+          StorageDead(_1);
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/dataflow-const-prop/slice_len.main.DataflowConstProp.64bit.panic-unwind.diff b/tests/mir-opt/dataflow-const-prop/slice_len.main.DataflowConstProp.64bit.panic-unwind.diff
new file mode 100644
index 00000000000..f9a6c509ac8
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/slice_len.main.DataflowConstProp.64bit.panic-unwind.diff
@@ -0,0 +1,76 @@
+- // MIR for `main` before DataflowConstProp
++ // MIR for `main` after DataflowConstProp
+  
+  fn main() -> () {
+      let mut _0: ();
+      let _1: u32;
+      let mut _2: &[u32];
+      let mut _3: &[u32; 3];
+      let _4: &[u32; 3];
+      let _5: [u32; 3];
+      let _6: usize;
+      let mut _7: usize;
+      let mut _8: bool;
+      let mut _10: &[u32];
+      let _11: usize;
+      let mut _12: usize;
+      let mut _13: bool;
+      let mut _14: &[u32; 3];
+      scope 1 {
+          debug local => _1;
+          let _9: u32;
+          scope 2 {
+              debug constant => _9;
+          }
+      }
+  
+      bb0: {
+          StorageLive(_1);
+          StorageLive(_2);
+          StorageLive(_3);
+          StorageLive(_4);
+          _14 = const _;
+          _4 = _14;
+          _3 = _4;
+          _2 = move _3 as &[u32] (PointerCoercion(Unsize));
+          StorageDead(_3);
+          StorageLive(_6);
+          _6 = const 1_usize;
+-         _7 = Len((*_2));
+-         _8 = Lt(_6, _7);
+-         assert(move _8, "index out of bounds: the length is {} but the index is {}", move _7, _6) -> [success: bb1, unwind continue];
++         _7 = const 3_usize;
++         _8 = const true;
++         assert(const true, "index out of bounds: the length is {} but the index is {}", const 3_usize, const 1_usize) -> [success: bb1, unwind continue];
+      }
+  
+      bb1: {
+-         _1 = (*_2)[_6];
++         _1 = (*_2)[1 of 2];
+          StorageDead(_6);
+          StorageDead(_4);
+          StorageDead(_2);
+          StorageLive(_9);
+          StorageLive(_10);
+          _10 = const _;
+          StorageLive(_11);
+          _11 = const 1_usize;
+          _12 = Len((*_10));
+-         _13 = Lt(_11, _12);
+-         assert(move _13, "index out of bounds: the length is {} but the index is {}", move _12, _11) -> [success: bb2, unwind continue];
++         _13 = Lt(const 1_usize, _12);
++         assert(move _13, "index out of bounds: the length is {} but the index is {}", move _12, const 1_usize) -> [success: bb2, unwind continue];
+      }
+  
+      bb2: {
+-         _9 = (*_10)[_11];
++         _9 = (*_10)[1 of 2];
+          StorageDead(_11);
+          StorageDead(_10);
+          _0 = const ();
+          StorageDead(_9);
+          StorageDead(_1);
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/dataflow-const-prop/slice_len.rs b/tests/mir-opt/dataflow-const-prop/slice_len.rs
new file mode 100644
index 00000000000..41367e48497
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/slice_len.rs
@@ -0,0 +1,12 @@
+// EMIT_MIR_FOR_EACH_PANIC_STRATEGY
+// unit-test: DataflowConstProp
+// compile-flags: -Zmir-enable-passes=+InstSimplify
+// EMIT_MIR_FOR_EACH_BIT_WIDTH
+
+// EMIT_MIR slice_len.main.DataflowConstProp.diff
+fn main() {
+    let local = (&[1u32, 2, 3] as &[u32])[1];
+
+    const SLICE: &[u32] = &[1, 2, 3];
+    let constant = SLICE[1];
+}
diff --git a/tests/mir-opt/dataflow-const-prop/transmute.from_char.DataflowConstProp.32bit.diff b/tests/mir-opt/dataflow-const-prop/transmute.from_char.DataflowConstProp.32bit.diff
new file mode 100644
index 00000000000..52f096ac0e4
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/transmute.from_char.DataflowConstProp.32bit.diff
@@ -0,0 +1,15 @@
+- // MIR for `from_char` before DataflowConstProp
++ // MIR for `from_char` after DataflowConstProp
+  
+  fn from_char() -> i32 {
+      let mut _0: i32;
+      scope 1 {
+      }
+  
+      bb0: {
+-         _0 = const 'R' as i32 (Transmute);
++         _0 = const 82_i32;
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/dataflow-const-prop/transmute.from_char.DataflowConstProp.64bit.diff b/tests/mir-opt/dataflow-const-prop/transmute.from_char.DataflowConstProp.64bit.diff
new file mode 100644
index 00000000000..52f096ac0e4
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/transmute.from_char.DataflowConstProp.64bit.diff
@@ -0,0 +1,15 @@
+- // MIR for `from_char` before DataflowConstProp
++ // MIR for `from_char` after DataflowConstProp
+  
+  fn from_char() -> i32 {
+      let mut _0: i32;
+      scope 1 {
+      }
+  
+      bb0: {
+-         _0 = const 'R' as i32 (Transmute);
++         _0 = const 82_i32;
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/dataflow-const-prop/transmute.invalid_bool.DataflowConstProp.32bit.diff b/tests/mir-opt/dataflow-const-prop/transmute.invalid_bool.DataflowConstProp.32bit.diff
new file mode 100644
index 00000000000..3972eb209a1
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/transmute.invalid_bool.DataflowConstProp.32bit.diff
@@ -0,0 +1,15 @@
+- // MIR for `invalid_bool` before DataflowConstProp
++ // MIR for `invalid_bool` after DataflowConstProp
+  
+  fn invalid_bool() -> bool {
+      let mut _0: bool;
+      scope 1 {
+      }
+  
+      bb0: {
+-         _0 = const -1_i8 as bool (Transmute);
++         _0 = const {transmute(0xff): bool};
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/dataflow-const-prop/transmute.invalid_bool.DataflowConstProp.64bit.diff b/tests/mir-opt/dataflow-const-prop/transmute.invalid_bool.DataflowConstProp.64bit.diff
new file mode 100644
index 00000000000..3972eb209a1
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/transmute.invalid_bool.DataflowConstProp.64bit.diff
@@ -0,0 +1,15 @@
+- // MIR for `invalid_bool` before DataflowConstProp
++ // MIR for `invalid_bool` after DataflowConstProp
+  
+  fn invalid_bool() -> bool {
+      let mut _0: bool;
+      scope 1 {
+      }
+  
+      bb0: {
+-         _0 = const -1_i8 as bool (Transmute);
++         _0 = const {transmute(0xff): bool};
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/dataflow-const-prop/transmute.invalid_char.DataflowConstProp.32bit.diff b/tests/mir-opt/dataflow-const-prop/transmute.invalid_char.DataflowConstProp.32bit.diff
new file mode 100644
index 00000000000..837dabde42a
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/transmute.invalid_char.DataflowConstProp.32bit.diff
@@ -0,0 +1,15 @@
+- // MIR for `invalid_char` before DataflowConstProp
++ // MIR for `invalid_char` after DataflowConstProp
+  
+  fn invalid_char() -> char {
+      let mut _0: char;
+      scope 1 {
+      }
+  
+      bb0: {
+-         _0 = const _ as char (Transmute);
++         _0 = const {transmute(0x7fffffff): char};
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/dataflow-const-prop/transmute.invalid_char.DataflowConstProp.64bit.diff b/tests/mir-opt/dataflow-const-prop/transmute.invalid_char.DataflowConstProp.64bit.diff
new file mode 100644
index 00000000000..837dabde42a
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/transmute.invalid_char.DataflowConstProp.64bit.diff
@@ -0,0 +1,15 @@
+- // MIR for `invalid_char` before DataflowConstProp
++ // MIR for `invalid_char` after DataflowConstProp
+  
+  fn invalid_char() -> char {
+      let mut _0: char;
+      scope 1 {
+      }
+  
+      bb0: {
+-         _0 = const _ as char (Transmute);
++         _0 = const {transmute(0x7fffffff): char};
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/dataflow-const-prop/transmute.less_as_i8.DataflowConstProp.32bit.diff b/tests/mir-opt/dataflow-const-prop/transmute.less_as_i8.DataflowConstProp.32bit.diff
new file mode 100644
index 00000000000..6091e169e8e
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/transmute.less_as_i8.DataflowConstProp.32bit.diff
@@ -0,0 +1,18 @@
+- // MIR for `less_as_i8` before DataflowConstProp
++ // MIR for `less_as_i8` after DataflowConstProp
+  
+  fn less_as_i8() -> i8 {
+      let mut _0: i8;
+      let mut _1: std::cmp::Ordering;
+      scope 1 {
+      }
+  
+      bb0: {
+          StorageLive(_1);
+          _1 = Less;
+          _0 = move _1 as i8 (Transmute);
+          StorageDead(_1);
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/dataflow-const-prop/transmute.less_as_i8.DataflowConstProp.64bit.diff b/tests/mir-opt/dataflow-const-prop/transmute.less_as_i8.DataflowConstProp.64bit.diff
new file mode 100644
index 00000000000..6091e169e8e
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/transmute.less_as_i8.DataflowConstProp.64bit.diff
@@ -0,0 +1,18 @@
+- // MIR for `less_as_i8` before DataflowConstProp
++ // MIR for `less_as_i8` after DataflowConstProp
+  
+  fn less_as_i8() -> i8 {
+      let mut _0: i8;
+      let mut _1: std::cmp::Ordering;
+      scope 1 {
+      }
+  
+      bb0: {
+          StorageLive(_1);
+          _1 = Less;
+          _0 = move _1 as i8 (Transmute);
+          StorageDead(_1);
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/dataflow-const-prop/transmute.rs b/tests/mir-opt/dataflow-const-prop/transmute.rs
new file mode 100644
index 00000000000..c25e33ab0b6
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/transmute.rs
@@ -0,0 +1,63 @@
+// unit-test: DataflowConstProp
+// compile-flags: -O --crate-type=lib
+// ignore-endian-big
+// EMIT_MIR_FOR_EACH_BIT_WIDTH
+
+use std::mem::transmute;
+
+// EMIT_MIR transmute.less_as_i8.DataflowConstProp.diff
+pub fn less_as_i8() -> i8 {
+    unsafe { transmute(std::cmp::Ordering::Less) }
+}
+
+// EMIT_MIR transmute.from_char.DataflowConstProp.diff
+pub fn from_char() -> i32 {
+    unsafe { transmute('R') }
+}
+
+// EMIT_MIR transmute.valid_char.DataflowConstProp.diff
+pub fn valid_char() -> char {
+    unsafe { transmute(0x52_u32) }
+}
+
+// EMIT_MIR transmute.invalid_char.DataflowConstProp.diff
+pub unsafe fn invalid_char() -> char {
+    unsafe { transmute(i32::MAX) }
+}
+
+// EMIT_MIR transmute.invalid_bool.DataflowConstProp.diff
+pub unsafe fn invalid_bool() -> bool {
+    unsafe { transmute(-1_i8) }
+}
+
+// EMIT_MIR transmute.undef_union_as_integer.DataflowConstProp.diff
+pub unsafe fn undef_union_as_integer() -> u32 {
+    union Union32 { value: u32, unit: () }
+    unsafe { transmute(Union32 { unit: () }) }
+}
+
+// EMIT_MIR transmute.unreachable_direct.DataflowConstProp.diff
+pub unsafe fn unreachable_direct() -> ! {
+    let x: Never = unsafe { transmute(()) };
+    match x {}
+}
+
+// EMIT_MIR transmute.unreachable_ref.DataflowConstProp.diff
+pub unsafe fn unreachable_ref() -> ! {
+    let x: &Never = unsafe { transmute(1_usize) };
+    match *x {}
+}
+
+// EMIT_MIR transmute.unreachable_mut.DataflowConstProp.diff
+pub unsafe fn unreachable_mut() -> ! {
+    let x: &mut Never = unsafe { transmute(1_usize) };
+    match *x {}
+}
+
+// EMIT_MIR transmute.unreachable_box.DataflowConstProp.diff
+pub unsafe fn unreachable_box() -> ! {
+    let x: Box<Never> = unsafe { transmute(1_usize) };
+    match *x {}
+}
+
+enum Never {}
diff --git a/tests/mir-opt/dataflow-const-prop/transmute.undef_union_as_integer.DataflowConstProp.32bit.diff b/tests/mir-opt/dataflow-const-prop/transmute.undef_union_as_integer.DataflowConstProp.32bit.diff
new file mode 100644
index 00000000000..fc0634b1f8f
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/transmute.undef_union_as_integer.DataflowConstProp.32bit.diff
@@ -0,0 +1,22 @@
+- // MIR for `undef_union_as_integer` before DataflowConstProp
++ // MIR for `undef_union_as_integer` after DataflowConstProp
+  
+  fn undef_union_as_integer() -> u32 {
+      let mut _0: u32;
+      let mut _1: undef_union_as_integer::Union32;
+      let mut _2: ();
+      scope 1 {
+      }
+  
+      bb0: {
+          StorageLive(_1);
+          StorageLive(_2);
+          _2 = ();
+          _1 = Union32 { value: move _2 };
+          StorageDead(_2);
+          _0 = move _1 as u32 (Transmute);
+          StorageDead(_1);
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/dataflow-const-prop/transmute.undef_union_as_integer.DataflowConstProp.64bit.diff b/tests/mir-opt/dataflow-const-prop/transmute.undef_union_as_integer.DataflowConstProp.64bit.diff
new file mode 100644
index 00000000000..fc0634b1f8f
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/transmute.undef_union_as_integer.DataflowConstProp.64bit.diff
@@ -0,0 +1,22 @@
+- // MIR for `undef_union_as_integer` before DataflowConstProp
++ // MIR for `undef_union_as_integer` after DataflowConstProp
+  
+  fn undef_union_as_integer() -> u32 {
+      let mut _0: u32;
+      let mut _1: undef_union_as_integer::Union32;
+      let mut _2: ();
+      scope 1 {
+      }
+  
+      bb0: {
+          StorageLive(_1);
+          StorageLive(_2);
+          _2 = ();
+          _1 = Union32 { value: move _2 };
+          StorageDead(_2);
+          _0 = move _1 as u32 (Transmute);
+          StorageDead(_1);
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/dataflow-const-prop/transmute.unreachable_box.DataflowConstProp.32bit.diff b/tests/mir-opt/dataflow-const-prop/transmute.unreachable_box.DataflowConstProp.32bit.diff
new file mode 100644
index 00000000000..d0c298ba233
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/transmute.unreachable_box.DataflowConstProp.32bit.diff
@@ -0,0 +1,20 @@
+- // MIR for `unreachable_box` before DataflowConstProp
++ // MIR for `unreachable_box` after DataflowConstProp
+  
+  fn unreachable_box() -> ! {
+      let mut _0: !;
+      let _1: std::boxed::Box<Never>;
+      scope 1 {
+          debug x => _1;
+      }
+      scope 2 {
+      }
+  
+      bb0: {
+          StorageLive(_1);
+-         _1 = const 1_usize as std::boxed::Box<Never> (Transmute);
++         _1 = const Box::<Never>(Unique::<Never> {{ pointer: NonNull::<Never> {{ pointer: {0x1 as *const Never} }}, _marker: PhantomData::<Never> }}, std::alloc::Global);
+          unreachable;
+      }
+  }
+  
diff --git a/tests/mir-opt/dataflow-const-prop/transmute.unreachable_box.DataflowConstProp.64bit.diff b/tests/mir-opt/dataflow-const-prop/transmute.unreachable_box.DataflowConstProp.64bit.diff
new file mode 100644
index 00000000000..d0c298ba233
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/transmute.unreachable_box.DataflowConstProp.64bit.diff
@@ -0,0 +1,20 @@
+- // MIR for `unreachable_box` before DataflowConstProp
++ // MIR for `unreachable_box` after DataflowConstProp
+  
+  fn unreachable_box() -> ! {
+      let mut _0: !;
+      let _1: std::boxed::Box<Never>;
+      scope 1 {
+          debug x => _1;
+      }
+      scope 2 {
+      }
+  
+      bb0: {
+          StorageLive(_1);
+-         _1 = const 1_usize as std::boxed::Box<Never> (Transmute);
++         _1 = const Box::<Never>(Unique::<Never> {{ pointer: NonNull::<Never> {{ pointer: {0x1 as *const Never} }}, _marker: PhantomData::<Never> }}, std::alloc::Global);
+          unreachable;
+      }
+  }
+  
diff --git a/tests/mir-opt/dataflow-const-prop/transmute.unreachable_direct.DataflowConstProp.32bit.diff b/tests/mir-opt/dataflow-const-prop/transmute.unreachable_direct.DataflowConstProp.32bit.diff
new file mode 100644
index 00000000000..acbb5cd1bc7
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/transmute.unreachable_direct.DataflowConstProp.32bit.diff
@@ -0,0 +1,22 @@
+- // MIR for `unreachable_direct` before DataflowConstProp
++ // MIR for `unreachable_direct` after DataflowConstProp
+  
+  fn unreachable_direct() -> ! {
+      let mut _0: !;
+      let _1: Never;
+      let mut _2: ();
+      scope 1 {
+          debug x => _1;
+      }
+      scope 2 {
+      }
+  
+      bb0: {
+          StorageLive(_1);
+          StorageLive(_2);
+          _2 = ();
+          _1 = move _2 as Never (Transmute);
+          unreachable;
+      }
+  }
+  
diff --git a/tests/mir-opt/dataflow-const-prop/transmute.unreachable_direct.DataflowConstProp.64bit.diff b/tests/mir-opt/dataflow-const-prop/transmute.unreachable_direct.DataflowConstProp.64bit.diff
new file mode 100644
index 00000000000..acbb5cd1bc7
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/transmute.unreachable_direct.DataflowConstProp.64bit.diff
@@ -0,0 +1,22 @@
+- // MIR for `unreachable_direct` before DataflowConstProp
++ // MIR for `unreachable_direct` after DataflowConstProp
+  
+  fn unreachable_direct() -> ! {
+      let mut _0: !;
+      let _1: Never;
+      let mut _2: ();
+      scope 1 {
+          debug x => _1;
+      }
+      scope 2 {
+      }
+  
+      bb0: {
+          StorageLive(_1);
+          StorageLive(_2);
+          _2 = ();
+          _1 = move _2 as Never (Transmute);
+          unreachable;
+      }
+  }
+  
diff --git a/tests/mir-opt/dataflow-const-prop/transmute.unreachable_mut.DataflowConstProp.32bit.diff b/tests/mir-opt/dataflow-const-prop/transmute.unreachable_mut.DataflowConstProp.32bit.diff
new file mode 100644
index 00000000000..2ffaeea72db
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/transmute.unreachable_mut.DataflowConstProp.32bit.diff
@@ -0,0 +1,24 @@
+- // MIR for `unreachable_mut` before DataflowConstProp
++ // MIR for `unreachable_mut` after DataflowConstProp
+  
+  fn unreachable_mut() -> ! {
+      let mut _0: !;
+      let _1: &mut Never;
+      let mut _2: &mut Never;
+      scope 1 {
+          debug x => _1;
+      }
+      scope 2 {
+      }
+  
+      bb0: {
+          StorageLive(_1);
+          StorageLive(_2);
+-         _2 = const 1_usize as &mut Never (Transmute);
++         _2 = const {0x1 as &mut Never};
+          _1 = &mut (*_2);
+          StorageDead(_2);
+          unreachable;
+      }
+  }
+  
diff --git a/tests/mir-opt/dataflow-const-prop/transmute.unreachable_mut.DataflowConstProp.64bit.diff b/tests/mir-opt/dataflow-const-prop/transmute.unreachable_mut.DataflowConstProp.64bit.diff
new file mode 100644
index 00000000000..2ffaeea72db
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/transmute.unreachable_mut.DataflowConstProp.64bit.diff
@@ -0,0 +1,24 @@
+- // MIR for `unreachable_mut` before DataflowConstProp
++ // MIR for `unreachable_mut` after DataflowConstProp
+  
+  fn unreachable_mut() -> ! {
+      let mut _0: !;
+      let _1: &mut Never;
+      let mut _2: &mut Never;
+      scope 1 {
+          debug x => _1;
+      }
+      scope 2 {
+      }
+  
+      bb0: {
+          StorageLive(_1);
+          StorageLive(_2);
+-         _2 = const 1_usize as &mut Never (Transmute);
++         _2 = const {0x1 as &mut Never};
+          _1 = &mut (*_2);
+          StorageDead(_2);
+          unreachable;
+      }
+  }
+  
diff --git a/tests/mir-opt/dataflow-const-prop/transmute.unreachable_ref.DataflowConstProp.32bit.diff b/tests/mir-opt/dataflow-const-prop/transmute.unreachable_ref.DataflowConstProp.32bit.diff
new file mode 100644
index 00000000000..31fcaafc5bc
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/transmute.unreachable_ref.DataflowConstProp.32bit.diff
@@ -0,0 +1,20 @@
+- // MIR for `unreachable_ref` before DataflowConstProp
++ // MIR for `unreachable_ref` after DataflowConstProp
+  
+  fn unreachable_ref() -> ! {
+      let mut _0: !;
+      let _1: &Never;
+      scope 1 {
+          debug x => _1;
+      }
+      scope 2 {
+      }
+  
+      bb0: {
+          StorageLive(_1);
+-         _1 = const 1_usize as &Never (Transmute);
++         _1 = const {0x1 as &Never};
+          unreachable;
+      }
+  }
+  
diff --git a/tests/mir-opt/dataflow-const-prop/transmute.unreachable_ref.DataflowConstProp.64bit.diff b/tests/mir-opt/dataflow-const-prop/transmute.unreachable_ref.DataflowConstProp.64bit.diff
new file mode 100644
index 00000000000..31fcaafc5bc
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/transmute.unreachable_ref.DataflowConstProp.64bit.diff
@@ -0,0 +1,20 @@
+- // MIR for `unreachable_ref` before DataflowConstProp
++ // MIR for `unreachable_ref` after DataflowConstProp
+  
+  fn unreachable_ref() -> ! {
+      let mut _0: !;
+      let _1: &Never;
+      scope 1 {
+          debug x => _1;
+      }
+      scope 2 {
+      }
+  
+      bb0: {
+          StorageLive(_1);
+-         _1 = const 1_usize as &Never (Transmute);
++         _1 = const {0x1 as &Never};
+          unreachable;
+      }
+  }
+  
diff --git a/tests/mir-opt/dataflow-const-prop/transmute.valid_char.DataflowConstProp.32bit.diff b/tests/mir-opt/dataflow-const-prop/transmute.valid_char.DataflowConstProp.32bit.diff
new file mode 100644
index 00000000000..402ef754a64
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/transmute.valid_char.DataflowConstProp.32bit.diff
@@ -0,0 +1,15 @@
+- // MIR for `valid_char` before DataflowConstProp
++ // MIR for `valid_char` after DataflowConstProp
+  
+  fn valid_char() -> char {
+      let mut _0: char;
+      scope 1 {
+      }
+  
+      bb0: {
+-         _0 = const 82_u32 as char (Transmute);
++         _0 = const 'R';
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/dataflow-const-prop/transmute.valid_char.DataflowConstProp.64bit.diff b/tests/mir-opt/dataflow-const-prop/transmute.valid_char.DataflowConstProp.64bit.diff
new file mode 100644
index 00000000000..402ef754a64
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/transmute.valid_char.DataflowConstProp.64bit.diff
@@ -0,0 +1,15 @@
+- // MIR for `valid_char` before DataflowConstProp
++ // MIR for `valid_char` after DataflowConstProp
+  
+  fn valid_char() -> char {
+      let mut _0: char;
+      scope 1 {
+      }
+  
+      bb0: {
+-         _0 = const 82_u32 as char (Transmute);
++         _0 = const 'R';
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/issue_76432.test.SimplifyComparisonIntegral.panic-abort.diff b/tests/mir-opt/issue_76432.test.SimplifyComparisonIntegral.panic-abort.diff
index f61632728ba..9d8f272abea 100644
--- a/tests/mir-opt/issue_76432.test.SimplifyComparisonIntegral.panic-abort.diff
+++ b/tests/mir-opt/issue_76432.test.SimplifyComparisonIntegral.panic-abort.diff
@@ -33,12 +33,10 @@
           _3 = &_4;
           _2 = move _3 as &[T] (PointerCoercion(Unsize));
           StorageDead(_3);
-          _8 = Len((*_2));
+          _8 = const 3_usize;
           _9 = const 3_usize;
--         _10 = Eq(move _8, const 3_usize);
--         switchInt(move _10) -> [0: bb1, otherwise: bb2];
-+         nop;
-+         switchInt(move _8) -> [3: bb2, otherwise: bb1];
+          _10 = const true;
+          goto -> bb2;
       }
   
       bb1: {
diff --git a/tests/mir-opt/issue_76432.test.SimplifyComparisonIntegral.panic-unwind.diff b/tests/mir-opt/issue_76432.test.SimplifyComparisonIntegral.panic-unwind.diff
index f6c337be10f..738b0b1b3e5 100644
--- a/tests/mir-opt/issue_76432.test.SimplifyComparisonIntegral.panic-unwind.diff
+++ b/tests/mir-opt/issue_76432.test.SimplifyComparisonIntegral.panic-unwind.diff
@@ -33,12 +33,10 @@
           _3 = &_4;
           _2 = move _3 as &[T] (PointerCoercion(Unsize));
           StorageDead(_3);
-          _8 = Len((*_2));
+          _8 = const 3_usize;
           _9 = const 3_usize;
--         _10 = Eq(move _8, const 3_usize);
--         switchInt(move _10) -> [0: bb1, otherwise: bb2];
-+         nop;
-+         switchInt(move _8) -> [3: bb2, otherwise: bb1];
+          _10 = const true;
+          goto -> bb2;
       }
   
       bb1: {