From 76f5bc6a9ffcd0b3255b5266cc963635a12f6016 Mon Sep 17 00:00:00 2001
From: Camille GILLOT <gillot.camille@gmail.com>
Date: Thu, 27 Jun 2024 14:51:58 +0000
Subject: [PATCH 1/4] Create mapped places upon seeing them in the body.

---
 .../rustc_mir_dataflow/src/value_analysis.rs  | 239 +++++++++---------
 .../src/dataflow_const_prop.rs                |  20 +-
 .../rustc_mir_transform/src/jump_threading.rs |   2 +-
 tests/coverage/try_error_result.cov-map       | 199 ++++++++-------
 ...pr_transparent.main.DataflowConstProp.diff |   6 +-
 .../dataflow-const-prop/repr_transparent.rs   |   2 +-
 .../struct.main.DataflowConstProp.32bit.diff  |  29 +--
 .../struct.main.DataflowConstProp.64bit.diff  |  29 +--
 tests/mir-opt/dataflow-const-prop/struct.rs   |   6 +-
 .../tuple.main.DataflowConstProp.32bit.diff   |   4 +-
 .../tuple.main.DataflowConstProp.64bit.diff   |   4 +-
 tests/mir-opt/dataflow-const-prop/tuple.rs    |   3 +-
 12 files changed, 271 insertions(+), 272 deletions(-)

diff --git a/compiler/rustc_mir_dataflow/src/value_analysis.rs b/compiler/rustc_mir_dataflow/src/value_analysis.rs
index 1582c2e8a9061..f21a241914442 100644
--- a/compiler/rustc_mir_dataflow/src/value_analysis.rs
+++ b/compiler/rustc_mir_dataflow/src/value_analysis.rs
@@ -32,15 +32,16 @@
 //! Because of that, we can assume that the only way to change the value behind a tracked place is
 //! by direct assignment.
 
-use std::collections::VecDeque;
 use std::fmt::{Debug, Formatter};
 use std::ops::Range;
 
+use rustc_data_structures::captures::Captures;
 use rustc_data_structures::fx::{FxHashMap, StdEntry};
 use rustc_data_structures::stack::ensure_sufficient_stack;
 use rustc_index::bit_set::BitSet;
 use rustc_index::IndexVec;
 use rustc_middle::bug;
+use rustc_middle::mir::tcx::PlaceTy;
 use rustc_middle::mir::visit::{MutatingUseContext, PlaceContext, Visitor};
 use rustc_middle::mir::*;
 use rustc_middle::ty::{self, Ty, TyCtxt};
@@ -58,7 +59,7 @@ pub trait ValueAnalysis<'tcx> {
 
     const NAME: &'static str;
 
-    fn map(&self) -> &Map;
+    fn map(&self) -> &Map<'tcx>;
 
     fn handle_statement(&self, statement: &Statement<'tcx>, state: &mut State<Self::Value>) {
         self.super_statement(statement, state)
@@ -523,12 +524,12 @@ impl<V: Clone + HasBottom> State<V> {
     }
 
     /// Assign `value` to all places that are contained in `place` or may alias one.
-    pub fn flood_with(&mut self, place: PlaceRef<'_>, map: &Map, value: V) {
+    pub fn flood_with(&mut self, place: PlaceRef<'_>, map: &Map<'_>, value: V) {
         self.flood_with_tail_elem(place, None, map, value)
     }
 
     /// Assign `TOP` to all places that are contained in `place` or may alias one.
-    pub fn flood(&mut self, place: PlaceRef<'_>, map: &Map)
+    pub fn flood(&mut self, place: PlaceRef<'_>, map: &Map<'_>)
     where
         V: HasTop,
     {
@@ -536,12 +537,12 @@ impl<V: Clone + HasBottom> State<V> {
     }
 
     /// Assign `value` to the discriminant of `place` and all places that may alias it.
-    fn flood_discr_with(&mut self, place: PlaceRef<'_>, map: &Map, value: V) {
+    fn flood_discr_with(&mut self, place: PlaceRef<'_>, map: &Map<'_>, value: V) {
         self.flood_with_tail_elem(place, Some(TrackElem::Discriminant), map, value)
     }
 
     /// Assign `TOP` to the discriminant of `place` and all places that may alias it.
-    pub fn flood_discr(&mut self, place: PlaceRef<'_>, map: &Map)
+    pub fn flood_discr(&mut self, place: PlaceRef<'_>, map: &Map<'_>)
     where
         V: HasTop,
     {
@@ -559,7 +560,7 @@ impl<V: Clone + HasBottom> State<V> {
         &mut self,
         place: PlaceRef<'_>,
         tail_elem: Option<TrackElem>,
-        map: &Map,
+        map: &Map<'_>,
         value: V,
     ) {
         let State::Reachable(values) = self else { return };
@@ -570,7 +571,7 @@ impl<V: Clone + HasBottom> State<V> {
     /// This does nothing if the place is not tracked.
     ///
     /// The target place must have been flooded before calling this method.
-    fn insert_idx(&mut self, target: PlaceIndex, result: ValueOrPlace<V>, map: &Map) {
+    fn insert_idx(&mut self, target: PlaceIndex, result: ValueOrPlace<V>, map: &Map<'_>) {
         match result {
             ValueOrPlace::Value(value) => self.insert_value_idx(target, value, map),
             ValueOrPlace::Place(source) => self.insert_place_idx(target, source, map),
@@ -581,7 +582,7 @@ impl<V: Clone + HasBottom> State<V> {
     /// This does nothing if the place is not tracked.
     ///
     /// The target place must have been flooded before calling this method.
-    pub fn insert_value_idx(&mut self, target: PlaceIndex, value: V, map: &Map) {
+    pub fn insert_value_idx(&mut self, target: PlaceIndex, value: V, map: &Map<'_>) {
         let State::Reachable(values) = self else { return };
         if let Some(value_index) = map.places[target].value_index {
             values.insert(value_index, value)
@@ -595,7 +596,7 @@ impl<V: Clone + HasBottom> State<V> {
     /// places that are non-overlapping or identical.
     ///
     /// The target place must have been flooded before calling this method.
-    pub fn insert_place_idx(&mut self, target: PlaceIndex, source: PlaceIndex, map: &Map) {
+    pub fn insert_place_idx(&mut self, target: PlaceIndex, source: PlaceIndex, map: &Map<'_>) {
         let State::Reachable(values) = self else { return };
 
         // If both places are tracked, we copy the value to the target.
@@ -616,7 +617,7 @@ impl<V: Clone + HasBottom> State<V> {
     }
 
     /// Helper method to interpret `target = result`.
-    pub fn assign(&mut self, target: PlaceRef<'_>, result: ValueOrPlace<V>, map: &Map)
+    pub fn assign(&mut self, target: PlaceRef<'_>, result: ValueOrPlace<V>, map: &Map<'_>)
     where
         V: HasTop,
     {
@@ -627,7 +628,7 @@ impl<V: Clone + HasBottom> State<V> {
     }
 
     /// Helper method for assignments to a discriminant.
-    pub fn assign_discr(&mut self, target: PlaceRef<'_>, result: ValueOrPlace<V>, map: &Map)
+    pub fn assign_discr(&mut self, target: PlaceRef<'_>, result: ValueOrPlace<V>, map: &Map<'_>)
     where
         V: HasTop,
     {
@@ -638,25 +639,25 @@ impl<V: Clone + HasBottom> State<V> {
     }
 
     /// Retrieve the value stored for a place, or `None` if it is not tracked.
-    pub fn try_get(&self, place: PlaceRef<'_>, map: &Map) -> Option<V> {
+    pub fn try_get(&self, place: PlaceRef<'_>, map: &Map<'_>) -> Option<V> {
         let place = map.find(place)?;
         self.try_get_idx(place, map)
     }
 
     /// Retrieve the discriminant stored for a place, or `None` if it is not tracked.
-    pub fn try_get_discr(&self, place: PlaceRef<'_>, map: &Map) -> Option<V> {
+    pub fn try_get_discr(&self, place: PlaceRef<'_>, map: &Map<'_>) -> Option<V> {
         let place = map.find_discr(place)?;
         self.try_get_idx(place, map)
     }
 
     /// Retrieve the slice length stored for a place, or `None` if it is not tracked.
-    pub fn try_get_len(&self, place: PlaceRef<'_>, map: &Map) -> Option<V> {
+    pub fn try_get_len(&self, place: PlaceRef<'_>, map: &Map<'_>) -> Option<V> {
         let place = map.find_len(place)?;
         self.try_get_idx(place, map)
     }
 
     /// Retrieve the value stored for a place index, or `None` if it is not tracked.
-    pub fn try_get_idx(&self, place: PlaceIndex, map: &Map) -> Option<V> {
+    pub fn try_get_idx(&self, place: PlaceIndex, map: &Map<'_>) -> Option<V> {
         match self {
             State::Reachable(values) => {
                 map.places[place].value_index.map(|v| values.get(v).clone())
@@ -668,7 +669,7 @@ impl<V: Clone + HasBottom> State<V> {
     /// Retrieve the value stored for a place, or ⊤ if it is not tracked.
     ///
     /// This method returns ⊥ if the place is tracked and the state is unreachable.
-    pub fn get(&self, place: PlaceRef<'_>, map: &Map) -> V
+    pub fn get(&self, place: PlaceRef<'_>, map: &Map<'_>) -> V
     where
         V: HasBottom + HasTop,
     {
@@ -682,7 +683,7 @@ impl<V: Clone + HasBottom> State<V> {
     /// Retrieve the value stored for a place, or ⊤ if it is not tracked.
     ///
     /// This method returns ⊥ the current state is unreachable.
-    pub fn get_discr(&self, place: PlaceRef<'_>, map: &Map) -> V
+    pub fn get_discr(&self, place: PlaceRef<'_>, map: &Map<'_>) -> V
     where
         V: HasBottom + HasTop,
     {
@@ -696,7 +697,7 @@ impl<V: Clone + HasBottom> State<V> {
     /// Retrieve the value stored for a place, or ⊤ if it is not tracked.
     ///
     /// This method returns ⊥ the current state is unreachable.
-    pub fn get_len(&self, place: PlaceRef<'_>, map: &Map) -> V
+    pub fn get_len(&self, place: PlaceRef<'_>, map: &Map<'_>) -> V
     where
         V: HasBottom + HasTop,
     {
@@ -710,7 +711,7 @@ impl<V: Clone + HasBottom> State<V> {
     /// Retrieve the value stored for a place index, or ⊤ if it is not tracked.
     ///
     /// This method returns ⊥ the current state is unreachable.
-    pub fn get_idx(&self, place: PlaceIndex, map: &Map) -> V
+    pub fn get_idx(&self, place: PlaceIndex, map: &Map<'_>) -> V
     where
         V: HasBottom + HasTop,
     {
@@ -746,25 +747,25 @@ impl<V: JoinSemiLattice + Clone + HasBottom> JoinSemiLattice for State<V> {
 /// - For iteration, every [`PlaceInfo`] contains an intrusive linked list of its children.
 /// - To directly get the child for a specific projection, there is a `projections` map.
 #[derive(Debug)]
-pub struct Map {
+pub struct Map<'tcx> {
     locals: IndexVec<Local, Option<PlaceIndex>>,
     projections: FxHashMap<(PlaceIndex, TrackElem), PlaceIndex>,
-    places: IndexVec<PlaceIndex, PlaceInfo>,
+    places: IndexVec<PlaceIndex, PlaceInfo<'tcx>>,
     value_count: usize,
     // The Range corresponds to a slice into `inner_values_buffer`.
     inner_values: IndexVec<PlaceIndex, Range<usize>>,
     inner_values_buffer: Vec<ValueIndex>,
 }
 
-impl Map {
+impl<'tcx> Map<'tcx> {
     /// 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 {
+    pub fn new(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, value_limit: Option<usize>) -> Self {
         let mut map = Self {
-            locals: IndexVec::new(),
+            locals: IndexVec::from_elem(None, &body.local_decls),
             projections: FxHashMap::default(),
             places: IndexVec::new(),
             value_count: 0,
@@ -778,18 +779,15 @@ impl Map {
     }
 
     /// Register all non-excluded places that have scalar layout.
-    fn register<'tcx>(
+    #[tracing::instrument(level = "trace", skip(self, tcx, body))]
+    fn register(
         &mut self,
         tcx: TyCtxt<'tcx>,
         body: &Body<'tcx>,
         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);
         for (local, decl) in body.local_decls.iter_enumerated() {
             if exclude.contains(local) {
                 continue;
@@ -797,16 +795,15 @@ impl Map {
 
             // Create a place for the local.
             debug_assert!(self.locals[local].is_none());
-            let place = self.places.push(PlaceInfo::new(None));
+            let place = self.places.push(PlaceInfo::new(decl.ty, None));
             self.locals[local] = Some(place);
-
-            // And push the eventual children places to the worklist.
-            self.register_children(tcx, param_env, place, decl.ty, &mut worklist);
         }
 
-        // `place.elem1.elem2` with type `ty`.
-        // `elem1` is either `Some(Variant(i))` or `None`.
-        while let Some((mut place, elem1, elem2, ty)) = worklist.pop_front() {
+        PlaceCollector { tcx, body, map: self }.visit_body(body);
+
+        // Create values for places whose type have scalar layout.
+        let param_env = tcx.param_env_reveal_all_normalized(body.source.def_id());
+        for place_info in self.places.iter_mut() {
             // The user requires a bound on the number of created values.
             if let Some(value_limit) = value_limit
                 && self.value_count >= value_limit
@@ -814,19 +811,18 @@ impl Map {
                 break;
             }
 
-            // Create a place for this projection.
-            for elem in [elem1, Some(elem2)].into_iter().flatten() {
-                place = *self.projections.entry((place, elem)).or_insert_with(|| {
-                    // Prepend new child to the linked list.
-                    let next = self.places.push(PlaceInfo::new(Some(elem)));
-                    self.places[next].next_sibling = self.places[place].first_child;
-                    self.places[place].first_child = Some(next);
-                    next
-                });
+            if let Ok(ty) = tcx.try_normalize_erasing_regions(param_env, place_info.ty) {
+                place_info.ty = ty;
             }
 
-            // And push the eventual children places to the worklist.
-            self.register_children(tcx, param_env, place, ty, &mut worklist);
+            // Allocate a value slot if it doesn't have one, and the user requested one.
+            assert!(place_info.value_index.is_none());
+            if let Ok(layout) = tcx.layout_of(param_env.and(place_info.ty))
+                && layout.abi.is_scalar()
+            {
+                place_info.value_index = Some(self.value_count.into());
+                self.value_count += 1;
+            }
         }
 
         // Pre-compute the tree of ValueIndex nested in each PlaceIndex.
@@ -852,68 +848,14 @@ impl Map {
         self.projections.retain(|_, child| !self.inner_values[*child].is_empty());
     }
 
-    /// Potentially register the (local, projection) place and its fields, recursively.
-    ///
-    /// Invariant: The projection must only contain trackable elements.
-    fn register_children<'tcx>(
-        &mut self,
-        tcx: TyCtxt<'tcx>,
-        param_env: ty::ParamEnv<'tcx>,
-        place: PlaceIndex,
-        ty: Ty<'tcx>,
-        worklist: &mut VecDeque<(PlaceIndex, Option<TrackElem>, TrackElem, Ty<'tcx>)>,
-    ) {
-        // Allocate a value slot if it doesn't have one, and the user requested one.
-        assert!(self.places[place].value_index.is_none());
-        if tcx.layout_of(param_env.and(ty)).is_ok_and(|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() {
-            // 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 ty::Ref(_, ref_ty, _) | ty::RawPtr(ref_ty, _) = ty.kind()
-            && let ty::Slice(..) = ref_ty.kind()
-            // The user may have written a predicate like `[T]: Sized` in their where clauses,
-            // which makes slices scalars.
-            && self.places[place].value_index.is_none()
-        {
-            // 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, param_env, |variant, field, ty| {
-            worklist.push_back((
-                place,
-                variant.map(TrackElem::Variant),
-                TrackElem::Field(field),
-                ty,
-            ))
-        });
+    #[tracing::instrument(level = "trace", skip(self), ret)]
+    fn register_place(&mut self, ty: Ty<'tcx>, base: PlaceIndex, elem: TrackElem) -> PlaceIndex {
+        *self.projections.entry((base, elem)).or_insert_with(|| {
+            let next = self.places.push(PlaceInfo::new(ty, Some(elem)));
+            self.places[next].next_sibling = self.places[base].first_child;
+            self.places[base].first_child = Some(next);
+            next
+        })
     }
 
     /// Precompute the list of values inside `root` and store it inside
@@ -934,7 +876,54 @@ impl Map {
         let end = self.inner_values_buffer.len();
         self.inner_values[root] = start..end;
     }
+}
 
+struct PlaceCollector<'a, 'b, 'tcx> {
+    tcx: TyCtxt<'tcx>,
+    body: &'b Body<'tcx>,
+    map: &'a mut Map<'tcx>,
+}
+
+impl<'tcx> Visitor<'tcx> for PlaceCollector<'_, '_, 'tcx> {
+    #[tracing::instrument(level = "trace", skip(self))]
+    fn visit_place(&mut self, place: &Place<'tcx>, ctxt: PlaceContext, _: Location) {
+        if !ctxt.is_use() {
+            return;
+        }
+
+        // Create a place for this projection.
+        let Some(mut place_index) = self.map.locals[place.local] else { return };
+        let mut ty = PlaceTy::from_ty(self.body.local_decls[place.local].ty);
+        tracing::trace!(?place_index, ?ty);
+
+        if let ty::Ref(_, ref_ty, _) | ty::RawPtr(ref_ty, _) = ty.ty.kind()
+            && let ty::Slice(..) = ref_ty.kind()
+        {
+            self.map.register_place(self.tcx.types.usize, place_index, TrackElem::DerefLen);
+        } else if ty.ty.is_enum() {
+            let discriminant_ty = ty.ty.discriminant_ty(self.tcx);
+            self.map.register_place(discriminant_ty, place_index, TrackElem::Discriminant);
+        }
+
+        for proj in place.projection {
+            let Ok(track_elem) = proj.try_into() else { return };
+            ty = ty.projection_ty(self.tcx, proj);
+            place_index = self.map.register_place(ty.ty, place_index, track_elem);
+            tracing::trace!(?proj, ?place_index, ?ty);
+
+            if let ty::Ref(_, ref_ty, _) | ty::RawPtr(ref_ty, _) = ty.ty.kind()
+                && let ty::Slice(..) = ref_ty.kind()
+            {
+                self.map.register_place(self.tcx.types.usize, place_index, TrackElem::DerefLen);
+            } else if ty.ty.is_enum() {
+                let discriminant_ty = ty.ty.discriminant_ty(self.tcx);
+                self.map.register_place(discriminant_ty, place_index, TrackElem::Discriminant);
+            }
+        }
+    }
+}
+
+impl<'tcx> Map<'tcx> {
     /// Applies a single projection element, yielding the corresponding child.
     pub fn apply(&self, place: PlaceIndex, elem: TrackElem) -> Option<PlaceIndex> {
         self.projections.get(&(place, elem)).copied()
@@ -974,7 +963,10 @@ impl Map {
     }
 
     /// Iterate over all direct children.
-    fn children(&self, parent: PlaceIndex) -> impl Iterator<Item = PlaceIndex> + '_ {
+    fn children(
+        &self,
+        parent: PlaceIndex,
+    ) -> impl Iterator<Item = PlaceIndex> + Captures<'_> + Captures<'tcx> {
         Children::new(self, parent)
     }
 
@@ -1081,7 +1073,10 @@ impl Map {
 /// Together, `first_child` and `next_sibling` form an intrusive linked list, which is used to
 /// model a tree structure (a replacement for a member like `children: Vec<PlaceIndex>`).
 #[derive(Debug)]
-struct PlaceInfo {
+struct PlaceInfo<'tcx> {
+    /// Type of the referenced place.
+    ty: Ty<'tcx>,
+
     /// We store a [`ValueIndex`] if and only if the placed is tracked by the analysis.
     value_index: Option<ValueIndex>,
 
@@ -1095,24 +1090,24 @@ struct PlaceInfo {
     next_sibling: Option<PlaceIndex>,
 }
 
-impl PlaceInfo {
-    fn new(proj_elem: Option<TrackElem>) -> Self {
-        Self { next_sibling: None, first_child: None, proj_elem, value_index: None }
+impl<'tcx> PlaceInfo<'tcx> {
+    fn new(ty: Ty<'tcx>, proj_elem: Option<TrackElem>) -> Self {
+        Self { ty, next_sibling: None, first_child: None, proj_elem, value_index: None }
     }
 }
 
-struct Children<'a> {
-    map: &'a Map,
+struct Children<'a, 'tcx> {
+    map: &'a Map<'tcx>,
     next: Option<PlaceIndex>,
 }
 
-impl<'a> Children<'a> {
-    fn new(map: &'a Map, parent: PlaceIndex) -> Self {
+impl<'a, 'tcx> Children<'a, 'tcx> {
+    fn new(map: &'a Map<'tcx>, parent: PlaceIndex) -> Self {
         Self { map, next: map.places[parent].first_child }
     }
 }
 
-impl<'a> Iterator for Children<'a> {
+impl Iterator for Children<'_, '_> {
     type Item = PlaceIndex;
 
     fn next(&mut self) -> Option<Self::Item> {
@@ -1261,7 +1256,7 @@ fn debug_with_context_rec<V: Debug + Eq + HasBottom>(
     place_str: &str,
     new: &StateData<V>,
     old: Option<&StateData<V>>,
-    map: &Map,
+    map: &Map<'_>,
     f: &mut Formatter<'_>,
 ) -> std::fmt::Result {
     if let Some(value) = map.places[place].value_index {
@@ -1305,7 +1300,7 @@ fn debug_with_context_rec<V: Debug + Eq + HasBottom>(
 fn debug_with_context<V: Debug + Eq + HasBottom>(
     new: &StateData<V>,
     old: Option<&StateData<V>>,
-    map: &Map,
+    map: &Map<'_>,
     f: &mut Formatter<'_>,
 ) -> std::fmt::Result {
     for (local, place) in map.locals.iter_enumerated() {
diff --git a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs
index 8b965f4d18e45..8303ef039d183 100644
--- a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs
+++ b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs
@@ -66,7 +66,7 @@ impl<'tcx> MirPass<'tcx> for DataflowConstProp {
 }
 
 struct ConstAnalysis<'a, 'tcx> {
-    map: Map,
+    map: Map<'tcx>,
     tcx: TyCtxt<'tcx>,
     local_decls: &'a LocalDecls<'tcx>,
     ecx: InterpCx<'tcx, DummyMachine>,
@@ -78,7 +78,7 @@ impl<'tcx> ValueAnalysis<'tcx> for ConstAnalysis<'_, 'tcx> {
 
     const NAME: &'static str = "ConstAnalysis";
 
-    fn map(&self) -> &Map {
+    fn map(&self) -> &Map<'tcx> {
         &self.map
     }
 
@@ -330,7 +330,7 @@ impl<'tcx> ValueAnalysis<'tcx> for ConstAnalysis<'_, 'tcx> {
 }
 
 impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> {
-    pub fn new(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, map: Map) -> Self {
+    pub fn new(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, map: Map<'tcx>) -> Self {
         let param_env = tcx.param_env_reveal_all_normalized(body.source.def_id());
         Self {
             map,
@@ -560,12 +560,13 @@ impl<'tcx, 'locals> Collector<'tcx, 'locals> {
         Self { patch: Patch::new(tcx), local_decls }
     }
 
+    #[instrument(level = "trace", skip(self, ecx, map), ret)]
     fn try_make_constant(
         &self,
         ecx: &mut InterpCx<'tcx, DummyMachine>,
         place: Place<'tcx>,
         state: &State<FlatSet<Scalar>>,
-        map: &Map,
+        map: &Map<'tcx>,
     ) -> Option<Const<'tcx>> {
         let ty = place.ty(self.local_decls, self.patch.tcx).ty;
         let layout = ecx.layout_of(ty).ok()?;
@@ -598,10 +599,11 @@ impl<'tcx, 'locals> Collector<'tcx, 'locals> {
     }
 }
 
+#[instrument(level = "trace", skip(map), ret)]
 fn propagatable_scalar(
     place: PlaceIndex,
     state: &State<FlatSet<Scalar>>,
-    map: &Map,
+    map: &Map<'_>,
 ) -> Option<Scalar> {
     if let FlatSet::Elem(value) = state.get_idx(place, map)
         && value.try_to_scalar_int().is_ok()
@@ -613,14 +615,14 @@ fn propagatable_scalar(
     }
 }
 
-#[instrument(level = "trace", skip(ecx, state, map))]
+#[instrument(level = "trace", skip(ecx, state, map), ret)]
 fn try_write_constant<'tcx>(
     ecx: &mut InterpCx<'tcx, DummyMachine>,
     dest: &PlaceTy<'tcx>,
     place: PlaceIndex,
     ty: Ty<'tcx>,
     state: &State<FlatSet<Scalar>>,
-    map: &Map,
+    map: &Map<'tcx>,
 ) -> InterpResult<'tcx> {
     let layout = ecx.layout_of(ty)?;
 
@@ -719,6 +721,7 @@ impl<'mir, 'tcx>
 {
     type FlowState = State<FlatSet<Scalar>>;
 
+    #[instrument(level = "trace", skip(self, results, statement))]
     fn visit_statement_before_primary_effect(
         &mut self,
         results: &mut Results<'tcx, ValueAnalysisWrapper<ConstAnalysis<'_, 'tcx>>>,
@@ -740,6 +743,7 @@ impl<'mir, 'tcx>
         }
     }
 
+    #[instrument(level = "trace", skip(self, results, statement))]
     fn visit_statement_after_primary_effect(
         &mut self,
         results: &mut Results<'tcx, ValueAnalysisWrapper<ConstAnalysis<'_, 'tcx>>>,
@@ -834,7 +838,7 @@ struct OperandCollector<'tcx, 'map, 'locals, 'a> {
     state: &'a State<FlatSet<Scalar>>,
     visitor: &'a mut Collector<'tcx, 'locals>,
     ecx: &'map mut InterpCx<'tcx, DummyMachine>,
-    map: &'map Map,
+    map: &'map Map<'tcx>,
 }
 
 impl<'tcx> Visitor<'tcx> for OperandCollector<'tcx, '_, '_, '_> {
diff --git a/compiler/rustc_mir_transform/src/jump_threading.rs b/compiler/rustc_mir_transform/src/jump_threading.rs
index 97ec0cb39ded3..2100f4b4a1af5 100644
--- a/compiler/rustc_mir_transform/src/jump_threading.rs
+++ b/compiler/rustc_mir_transform/src/jump_threading.rs
@@ -123,7 +123,7 @@ struct TOFinder<'tcx, 'a> {
     param_env: ty::ParamEnv<'tcx>,
     ecx: InterpCx<'tcx, DummyMachine>,
     body: &'a Body<'tcx>,
-    map: &'a Map,
+    map: &'a Map<'tcx>,
     loop_headers: &'a BitSet<BasicBlock>,
     /// We use an arena to avoid cloning the slices when cloning `state`.
     arena: &'a DroplessArena,
diff --git a/tests/coverage/try_error_result.cov-map b/tests/coverage/try_error_result.cov-map
index 9c18827d8e68b..11a99f5395ec8 100644
--- a/tests/coverage/try_error_result.cov-map
+++ b/tests/coverage/try_error_result.cov-map
@@ -81,101 +81,130 @@ Number of file 0 mappings: 11
     = (((c4 + Zero) + Zero) + c3)
 
 Function name: try_error_result::test2
-Raw bytes (280): 0x[01, 01, 24, 01, 07, 00, 09, 03, 0d, 41, 00, 1e, 00, 41, 00, 1e, 00, 41, 00, 4a, 00, 4e, 00, 52, 41, 03, 0d, 52, 41, 03, 0d, 4e, 00, 52, 41, 03, 0d, 4a, 00, 4e, 00, 52, 41, 03, 0d, 66, 00, 45, 00, 45, 00, 66, 00, 45, 00, 7a, 00, 4d, 00, 4d, 00, 7a, 00, 4d, 00, 83, 01, 0d, 87, 01, 00, 00, 8b, 01, 8f, 01, 00, 19, 00, 28, 01, 3d, 01, 03, 17, 03, 08, 09, 00, 0e, 52, 02, 09, 04, 1a, 41, 06, 0d, 00, 2f, 00, 00, 2f, 00, 30, 1e, 00, 31, 03, 35, 00, 04, 11, 00, 12, 1a, 02, 11, 04, 12, 00, 05, 11, 00, 14, 1a, 00, 17, 00, 41, 19, 00, 41, 00, 42, 00, 00, 43, 00, 5f, 00, 00, 5f, 00, 60, 00, 01, 0d, 00, 20, 00, 01, 11, 00, 14, 00, 00, 17, 00, 41, 00, 00, 41, 00, 42, 00, 00, 43, 00, 60, 00, 00, 60, 00, 61, 00, 01, 0d, 00, 20, 46, 04, 11, 00, 14, 4e, 00, 17, 00, 42, 00, 00, 42, 00, 43, 4a, 00, 44, 00, 61, 00, 00, 61, 00, 62, 46, 01, 0d, 00, 20, 62, 01, 11, 00, 14, 45, 00, 17, 01, 36, 00, 01, 36, 00, 37, 66, 01, 12, 00, 2f, 00, 00, 2f, 00, 30, 62, 01, 0d, 00, 20, 76, 01, 11, 00, 14, 4d, 00, 17, 01, 36, 00, 02, 11, 00, 12, 7a, 01, 12, 00, 2f, 00, 01, 11, 00, 12, 76, 02, 0d, 00, 20, 0d, 03, 05, 00, 0b, 7f, 01, 01, 00, 02]
+Raw bytes (358): 0x[01, 01, 3b, 01, 07, 05, 09, 03, 0d, 41, 11, 4a, 15, 41, 11, 42, 1d, 46, 19, 4a, 15, 41, 11, 4a, 15, 41, 11, 46, 19, 4a, 15, 41, 11, 42, 1d, 46, 19, 4a, 15, 41, 11, 5e, 25, 49, 21, 49, 21, 5e, 25, 49, 21, 8a, 01, 2d, 8e, 01, 29, 92, 01, 41, 03, 0d, 92, 01, 41, 03, 0d, 8e, 01, 29, 92, 01, 41, 03, 0d, 8a, 01, 2d, 8e, 01, 29, 92, 01, 41, 03, 0d, a6, 01, 35, 45, 31, 45, 31, a6, 01, 35, 45, 31, ba, 01, 3d, 4d, 39, 4d, 39, ba, 01, 3d, 4d, 39, c3, 01, 0d, c7, 01, db, 01, cb, 01, cf, 01, 11, 15, d3, 01, d7, 01, 19, 1d, 21, 25, df, 01, e3, 01, 29, 2d, e7, 01, eb, 01, 31, 35, 39, 3d, 28, 01, 3d, 01, 03, 17, 03, 08, 09, 00, 0e, 92, 01, 02, 09, 04, 1a, 41, 06, 0d, 00, 2f, 11, 00, 2f, 00, 30, 4a, 00, 31, 03, 35, 15, 04, 11, 00, 12, 46, 02, 11, 04, 12, 3e, 05, 11, 00, 14, 46, 00, 17, 00, 41, 19, 00, 41, 00, 42, 42, 00, 43, 00, 5f, 1d, 00, 5f, 00, 60, 3e, 01, 0d, 00, 20, 5a, 01, 11, 00, 14, 49, 00, 17, 00, 41, 21, 00, 41, 00, 42, 5e, 00, 43, 00, 60, 25, 00, 60, 00, 61, 5a, 01, 0d, 00, 20, 86, 01, 04, 11, 00, 14, 8e, 01, 00, 17, 00, 42, 29, 00, 42, 00, 43, 8a, 01, 00, 44, 00, 61, 2d, 00, 61, 00, 62, 86, 01, 01, 0d, 00, 20, a2, 01, 01, 11, 00, 14, 45, 00, 17, 01, 36, 31, 01, 36, 00, 37, a6, 01, 01, 12, 00, 2f, 35, 00, 2f, 00, 30, a2, 01, 01, 0d, 00, 20, b6, 01, 01, 11, 00, 14, 4d, 00, 17, 01, 36, 39, 02, 11, 00, 12, ba, 01, 01, 12, 00, 2f, 3d, 01, 11, 00, 12, b6, 01, 02, 0d, 00, 20, 0d, 03, 05, 00, 0b, bf, 01, 01, 01, 00, 02]
 Number of files: 1
 - file 0 => global file 1
-Number of expressions: 36
+Number of expressions: 59
 - expression 0 operands: lhs = Counter(0), rhs = Expression(1, Add)
-- expression 1 operands: lhs = Zero, rhs = Counter(2)
+- expression 1 operands: lhs = Counter(1), rhs = Counter(2)
 - expression 2 operands: lhs = Expression(0, Add), rhs = Counter(3)
-- expression 3 operands: lhs = Counter(16), rhs = Zero
-- expression 4 operands: lhs = Expression(7, Sub), rhs = Zero
-- expression 5 operands: lhs = Counter(16), rhs = Zero
-- expression 6 operands: lhs = Expression(7, Sub), rhs = Zero
-- expression 7 operands: lhs = Counter(16), rhs = Zero
-- expression 8 operands: lhs = Expression(18, Sub), rhs = Zero
-- expression 9 operands: lhs = Expression(19, Sub), rhs = Zero
-- expression 10 operands: lhs = Expression(20, Sub), rhs = Counter(16)
-- expression 11 operands: lhs = Expression(0, Add), rhs = Counter(3)
-- expression 12 operands: lhs = Expression(20, Sub), rhs = Counter(16)
-- expression 13 operands: lhs = Expression(0, Add), rhs = Counter(3)
-- expression 14 operands: lhs = Expression(19, Sub), rhs = Zero
-- expression 15 operands: lhs = Expression(20, Sub), rhs = Counter(16)
-- expression 16 operands: lhs = Expression(0, Add), rhs = Counter(3)
-- expression 17 operands: lhs = Expression(18, Sub), rhs = Zero
-- expression 18 operands: lhs = Expression(19, Sub), rhs = Zero
-- expression 19 operands: lhs = Expression(20, Sub), rhs = Counter(16)
-- expression 20 operands: lhs = Expression(0, Add), rhs = Counter(3)
-- expression 21 operands: lhs = Expression(25, Sub), rhs = Zero
-- expression 22 operands: lhs = Counter(17), rhs = Zero
-- expression 23 operands: lhs = Counter(17), rhs = Zero
-- expression 24 operands: lhs = Expression(25, Sub), rhs = Zero
-- expression 25 operands: lhs = Counter(17), rhs = Zero
-- expression 26 operands: lhs = Expression(30, Sub), rhs = Zero
-- expression 27 operands: lhs = Counter(19), rhs = Zero
-- expression 28 operands: lhs = Counter(19), rhs = Zero
-- expression 29 operands: lhs = Expression(30, Sub), rhs = Zero
-- expression 30 operands: lhs = Counter(19), rhs = Zero
-- expression 31 operands: lhs = Expression(32, Add), rhs = Counter(3)
-- expression 32 operands: lhs = Expression(33, Add), rhs = Zero
-- expression 33 operands: lhs = Zero, rhs = Expression(34, Add)
-- expression 34 operands: lhs = Expression(35, Add), rhs = Zero
-- expression 35 operands: lhs = Counter(6), rhs = Zero
+- expression 3 operands: lhs = Counter(16), rhs = Counter(4)
+- expression 4 operands: lhs = Expression(18, Sub), rhs = Counter(5)
+- expression 5 operands: lhs = Counter(16), rhs = Counter(4)
+- expression 6 operands: lhs = Expression(16, Sub), rhs = Counter(7)
+- expression 7 operands: lhs = Expression(17, Sub), rhs = Counter(6)
+- expression 8 operands: lhs = Expression(18, Sub), rhs = Counter(5)
+- expression 9 operands: lhs = Counter(16), rhs = Counter(4)
+- expression 10 operands: lhs = Expression(18, Sub), rhs = Counter(5)
+- expression 11 operands: lhs = Counter(16), rhs = Counter(4)
+- expression 12 operands: lhs = Expression(17, Sub), rhs = Counter(6)
+- expression 13 operands: lhs = Expression(18, Sub), rhs = Counter(5)
+- expression 14 operands: lhs = Counter(16), rhs = Counter(4)
+- expression 15 operands: lhs = Expression(16, Sub), rhs = Counter(7)
+- expression 16 operands: lhs = Expression(17, Sub), rhs = Counter(6)
+- expression 17 operands: lhs = Expression(18, Sub), rhs = Counter(5)
+- expression 18 operands: lhs = Counter(16), rhs = Counter(4)
+- expression 19 operands: lhs = Expression(23, Sub), rhs = Counter(9)
+- expression 20 operands: lhs = Counter(18), rhs = Counter(8)
+- expression 21 operands: lhs = Counter(18), rhs = Counter(8)
+- expression 22 operands: lhs = Expression(23, Sub), rhs = Counter(9)
+- expression 23 operands: lhs = Counter(18), rhs = Counter(8)
+- expression 24 operands: lhs = Expression(34, Sub), rhs = Counter(11)
+- expression 25 operands: lhs = Expression(35, Sub), rhs = Counter(10)
+- expression 26 operands: lhs = Expression(36, Sub), rhs = Counter(16)
+- expression 27 operands: lhs = Expression(0, Add), rhs = Counter(3)
+- expression 28 operands: lhs = Expression(36, Sub), rhs = Counter(16)
+- expression 29 operands: lhs = Expression(0, Add), rhs = Counter(3)
+- expression 30 operands: lhs = Expression(35, Sub), rhs = Counter(10)
+- expression 31 operands: lhs = Expression(36, Sub), rhs = Counter(16)
+- expression 32 operands: lhs = Expression(0, Add), rhs = Counter(3)
+- expression 33 operands: lhs = Expression(34, Sub), rhs = Counter(11)
+- expression 34 operands: lhs = Expression(35, Sub), rhs = Counter(10)
+- expression 35 operands: lhs = Expression(36, Sub), rhs = Counter(16)
+- expression 36 operands: lhs = Expression(0, Add), rhs = Counter(3)
+- expression 37 operands: lhs = Expression(41, Sub), rhs = Counter(13)
+- expression 38 operands: lhs = Counter(17), rhs = Counter(12)
+- expression 39 operands: lhs = Counter(17), rhs = Counter(12)
+- expression 40 operands: lhs = Expression(41, Sub), rhs = Counter(13)
+- expression 41 operands: lhs = Counter(17), rhs = Counter(12)
+- expression 42 operands: lhs = Expression(46, Sub), rhs = Counter(15)
+- expression 43 operands: lhs = Counter(19), rhs = Counter(14)
+- expression 44 operands: lhs = Counter(19), rhs = Counter(14)
+- expression 45 operands: lhs = Expression(46, Sub), rhs = Counter(15)
+- expression 46 operands: lhs = Counter(19), rhs = Counter(14)
+- expression 47 operands: lhs = Expression(48, Add), rhs = Counter(3)
+- expression 48 operands: lhs = Expression(49, Add), rhs = Expression(54, Add)
+- expression 49 operands: lhs = Expression(50, Add), rhs = Expression(51, Add)
+- expression 50 operands: lhs = Counter(4), rhs = Counter(5)
+- expression 51 operands: lhs = Expression(52, Add), rhs = Expression(53, Add)
+- expression 52 operands: lhs = Counter(6), rhs = Counter(7)
+- expression 53 operands: lhs = Counter(8), rhs = Counter(9)
+- expression 54 operands: lhs = Expression(55, Add), rhs = Expression(56, Add)
+- expression 55 operands: lhs = Counter(10), rhs = Counter(11)
+- expression 56 operands: lhs = Expression(57, Add), rhs = Expression(58, Add)
+- expression 57 operands: lhs = Counter(12), rhs = Counter(13)
+- expression 58 operands: lhs = Counter(14), rhs = Counter(15)
 Number of file 0 mappings: 40
 - Code(Counter(0)) at (prev + 61, 1) to (start + 3, 23)
 - Code(Expression(0, Add)) at (prev + 8, 9) to (start + 0, 14)
-    = (c0 + (Zero + c2))
-- Code(Expression(20, Sub)) at (prev + 2, 9) to (start + 4, 26)
-    = ((c0 + (Zero + c2)) - c3)
+    = (c0 + (c1 + c2))
+- Code(Expression(36, Sub)) at (prev + 2, 9) to (start + 4, 26)
+    = ((c0 + (c1 + c2)) - c3)
 - Code(Counter(16)) at (prev + 6, 13) to (start + 0, 47)
-- Code(Zero) at (prev + 0, 47) to (start + 0, 48)
-- Code(Expression(7, Sub)) at (prev + 0, 49) to (start + 3, 53)
-    = (c16 - Zero)
-- Code(Zero) at (prev + 4, 17) to (start + 0, 18)
-- Code(Expression(6, Sub)) at (prev + 2, 17) to (start + 4, 18)
-    = ((c16 - Zero) - Zero)
-- Code(Zero) at (prev + 5, 17) to (start + 0, 20)
-- Code(Expression(6, Sub)) at (prev + 0, 23) to (start + 0, 65)
-    = ((c16 - Zero) - Zero)
+- Code(Counter(4)) at (prev + 0, 47) to (start + 0, 48)
+- Code(Expression(18, Sub)) at (prev + 0, 49) to (start + 3, 53)
+    = (c16 - c4)
+- Code(Counter(5)) at (prev + 4, 17) to (start + 0, 18)
+- Code(Expression(17, Sub)) at (prev + 2, 17) to (start + 4, 18)
+    = ((c16 - c4) - c5)
+- Code(Expression(15, Sub)) at (prev + 5, 17) to (start + 0, 20)
+    = ((((c16 - c4) - c5) - c6) - c7)
+- Code(Expression(17, Sub)) at (prev + 0, 23) to (start + 0, 65)
+    = ((c16 - c4) - c5)
 - Code(Counter(6)) at (prev + 0, 65) to (start + 0, 66)
-- Code(Zero) at (prev + 0, 67) to (start + 0, 95)
-- Code(Zero) at (prev + 0, 95) to (start + 0, 96)
-- Code(Zero) at (prev + 1, 13) to (start + 0, 32)
-- Code(Zero) at (prev + 1, 17) to (start + 0, 20)
-- Code(Zero) at (prev + 0, 23) to (start + 0, 65)
-- Code(Zero) at (prev + 0, 65) to (start + 0, 66)
-- Code(Zero) at (prev + 0, 67) to (start + 0, 96)
-- Code(Zero) at (prev + 0, 96) to (start + 0, 97)
-- Code(Zero) at (prev + 1, 13) to (start + 0, 32)
-- Code(Expression(17, Sub)) at (prev + 4, 17) to (start + 0, 20)
-    = (((((c0 + (Zero + c2)) - c3) - c16) - Zero) - Zero)
-- Code(Expression(19, Sub)) at (prev + 0, 23) to (start + 0, 66)
-    = (((c0 + (Zero + c2)) - c3) - c16)
-- Code(Zero) at (prev + 0, 66) to (start + 0, 67)
-- Code(Expression(18, Sub)) at (prev + 0, 68) to (start + 0, 97)
-    = ((((c0 + (Zero + c2)) - c3) - c16) - Zero)
-- Code(Zero) at (prev + 0, 97) to (start + 0, 98)
-- Code(Expression(17, Sub)) at (prev + 1, 13) to (start + 0, 32)
-    = (((((c0 + (Zero + c2)) - c3) - c16) - Zero) - Zero)
-- Code(Expression(24, Sub)) at (prev + 1, 17) to (start + 0, 20)
-    = ((c17 - Zero) - Zero)
+- Code(Expression(16, Sub)) at (prev + 0, 67) to (start + 0, 95)
+    = (((c16 - c4) - c5) - c6)
+- Code(Counter(7)) at (prev + 0, 95) to (start + 0, 96)
+- Code(Expression(15, Sub)) at (prev + 1, 13) to (start + 0, 32)
+    = ((((c16 - c4) - c5) - c6) - c7)
+- Code(Expression(22, Sub)) at (prev + 1, 17) to (start + 0, 20)
+    = ((c18 - c8) - c9)
+- Code(Counter(18)) at (prev + 0, 23) to (start + 0, 65)
+- Code(Counter(8)) at (prev + 0, 65) to (start + 0, 66)
+- Code(Expression(23, Sub)) at (prev + 0, 67) to (start + 0, 96)
+    = (c18 - c8)
+- Code(Counter(9)) at (prev + 0, 96) to (start + 0, 97)
+- Code(Expression(22, Sub)) at (prev + 1, 13) to (start + 0, 32)
+    = ((c18 - c8) - c9)
+- Code(Expression(33, Sub)) at (prev + 4, 17) to (start + 0, 20)
+    = (((((c0 + (c1 + c2)) - c3) - c16) - c10) - c11)
+- Code(Expression(35, Sub)) at (prev + 0, 23) to (start + 0, 66)
+    = (((c0 + (c1 + c2)) - c3) - c16)
+- Code(Counter(10)) at (prev + 0, 66) to (start + 0, 67)
+- Code(Expression(34, Sub)) at (prev + 0, 68) to (start + 0, 97)
+    = ((((c0 + (c1 + c2)) - c3) - c16) - c10)
+- Code(Counter(11)) at (prev + 0, 97) to (start + 0, 98)
+- Code(Expression(33, Sub)) at (prev + 1, 13) to (start + 0, 32)
+    = (((((c0 + (c1 + c2)) - c3) - c16) - c10) - c11)
+- Code(Expression(40, Sub)) at (prev + 1, 17) to (start + 0, 20)
+    = ((c17 - c12) - c13)
 - Code(Counter(17)) at (prev + 0, 23) to (start + 1, 54)
-- Code(Zero) at (prev + 1, 54) to (start + 0, 55)
-- Code(Expression(25, Sub)) at (prev + 1, 18) to (start + 0, 47)
-    = (c17 - Zero)
-- Code(Zero) at (prev + 0, 47) to (start + 0, 48)
-- Code(Expression(24, Sub)) at (prev + 1, 13) to (start + 0, 32)
-    = ((c17 - Zero) - Zero)
-- Code(Expression(29, Sub)) at (prev + 1, 17) to (start + 0, 20)
-    = ((c19 - Zero) - Zero)
+- Code(Counter(12)) at (prev + 1, 54) to (start + 0, 55)
+- Code(Expression(41, Sub)) at (prev + 1, 18) to (start + 0, 47)
+    = (c17 - c12)
+- Code(Counter(13)) at (prev + 0, 47) to (start + 0, 48)
+- Code(Expression(40, Sub)) at (prev + 1, 13) to (start + 0, 32)
+    = ((c17 - c12) - c13)
+- Code(Expression(45, Sub)) at (prev + 1, 17) to (start + 0, 20)
+    = ((c19 - c14) - c15)
 - Code(Counter(19)) at (prev + 0, 23) to (start + 1, 54)
-- Code(Zero) at (prev + 2, 17) to (start + 0, 18)
-- Code(Expression(30, Sub)) at (prev + 1, 18) to (start + 0, 47)
-    = (c19 - Zero)
-- Code(Zero) at (prev + 1, 17) to (start + 0, 18)
-- Code(Expression(29, Sub)) at (prev + 2, 13) to (start + 0, 32)
-    = ((c19 - Zero) - Zero)
+- Code(Counter(14)) at (prev + 2, 17) to (start + 0, 18)
+- Code(Expression(46, Sub)) at (prev + 1, 18) to (start + 0, 47)
+    = (c19 - c14)
+- Code(Counter(15)) at (prev + 1, 17) to (start + 0, 18)
+- Code(Expression(45, Sub)) at (prev + 2, 13) to (start + 0, 32)
+    = ((c19 - c14) - c15)
 - Code(Counter(3)) at (prev + 3, 5) to (start + 0, 11)
-- Code(Expression(31, Add)) at (prev + 1, 1) to (start + 0, 2)
-    = (((Zero + ((c6 + Zero) + Zero)) + Zero) + c3)
+- Code(Expression(47, Add)) at (prev + 1, 1) to (start + 0, 2)
+    = ((((c4 + c5) + ((c6 + c7) + (c8 + c9))) + ((c10 + c11) + ((c12 + c13) + (c14 + c15)))) + c3)
 
diff --git a/tests/mir-opt/dataflow-const-prop/repr_transparent.main.DataflowConstProp.diff b/tests/mir-opt/dataflow-const-prop/repr_transparent.main.DataflowConstProp.diff
index 98bd40ab2c3da..a77eb4708f2f0 100644
--- a/tests/mir-opt/dataflow-const-prop/repr_transparent.main.DataflowConstProp.diff
+++ b/tests/mir-opt/dataflow-const-prop/repr_transparent.main.DataflowConstProp.diff
@@ -32,7 +32,7 @@
           StorageDead(_5);
           StorageDead(_4);
 -         _2 = I32(move _3);
-+         _2 = const I32(0_i32);
++         _2 = I32(const 0_i32);
           StorageDead(_3);
           _0 = const ();
           StorageDead(_2);
@@ -42,10 +42,6 @@
 + }
 + 
 + ALLOC0 (size: 4, align: 4) {
-+     00 00 00 00                                     │ ....
-+ }
-+ 
-+ ALLOC1 (size: 4, align: 4) {
 +     00 00 00 00                                     │ ....
   }
   
diff --git a/tests/mir-opt/dataflow-const-prop/repr_transparent.rs b/tests/mir-opt/dataflow-const-prop/repr_transparent.rs
index 6152724c98f66..6e3c48c81dc23 100644
--- a/tests/mir-opt/dataflow-const-prop/repr_transparent.rs
+++ b/tests/mir-opt/dataflow-const-prop/repr_transparent.rs
@@ -15,6 +15,6 @@ fn main() {
     // CHECK: [[x]] = const I32(0_i32);
     let x = I32(0);
 
-    // CHECK: [[y]] = const I32(0_i32);
+    // CHECK: [[y]] = I32(const 0_i32);
     let y = I32(x.0 + x.0);
 }
diff --git a/tests/mir-opt/dataflow-const-prop/struct.main.DataflowConstProp.32bit.diff b/tests/mir-opt/dataflow-const-prop/struct.main.DataflowConstProp.32bit.diff
index a6da1483c1ac5..5e89382ea8f46 100644
--- a/tests/mir-opt/dataflow-const-prop/struct.main.DataflowConstProp.32bit.diff
+++ b/tests/mir-opt/dataflow-const-prop/struct.main.DataflowConstProp.32bit.diff
@@ -106,8 +106,7 @@
 -         _7 = (_10.0: f32);
 +         _7 = const 4f32;
           StorageLive(_8);
--         _8 = (_10.1: std::option::Option<S>);
-+         _8 = const Option::<S>::Some(S(1_i32));
+          _8 = (_10.1: std::option::Option<S>);
           StorageLive(_9);
           _9 = (_10.2: &[f32]);
           StorageDead(_10);
@@ -157,8 +156,7 @@
 +         _23 = const 82f32;
           StorageLive(_24);
           _37 = deref_copy (*_26);
--         _24 = ((*_37).1: std::option::Option<S>);
-+         _24 = const Option::<S>::Some(S(35_i32));
+          _24 = ((*_37).1: std::option::Option<S>);
           StorageLive(_25);
           _38 = deref_copy (*_26);
           _25 = ((*_38).2: &[f32]);
@@ -168,12 +166,11 @@
 -         _28 = _23;
 +         _28 = const 82f32;
           StorageLive(_29);
--         _29 = _24;
-+         _29 = const Option::<S>::Some(S(35_i32));
+          _29 = _24;
           StorageLive(_30);
           _30 = _25;
 -         _27 = BigStruct(move _28, move _29, move _30);
-+         _27 = BigStruct(const 82f32, const Option::<S>::Some(S(35_i32)), move _30);
++         _27 = BigStruct(const 82f32, move _29, move _30);
           StorageDead(_30);
           StorageDead(_29);
           StorageDead(_28);
@@ -199,29 +196,21 @@
       }
   }
   
-+ ALLOC2 (size: 8, align: 4) { .. }
-+ 
-+ ALLOC3 (size: 8, align: 4) { .. }
-+ 
-+ ALLOC4 (size: 8, align: 4) { .. }
-+ 
-+ ALLOC5 (size: 8, align: 4) { .. }
-+ 
-+ ALLOC6 (size: 4, align: 4) { .. }
++ ALLOC2 (size: 4, align: 4) { .. }
 + 
   ALLOC1 (static: BIG_STAT, size: 4, align: 4) { .. }
   
 - ALLOC2 (size: 20, align: 4) { .. }
-+ ALLOC7 (size: 20, align: 4) { .. }
++ ALLOC3 (size: 20, align: 4) { .. }
   
 - ALLOC3 (size: 8, align: 4) { .. }
-+ ALLOC8 (size: 8, align: 4) { .. }
++ ALLOC4 (size: 8, align: 4) { .. }
   
   ALLOC0 (static: SMALL_STAT, size: 4, align: 4) { .. }
   
 - ALLOC4 (size: 20, align: 4) { .. }
-+ ALLOC9 (size: 20, align: 4) { .. }
++ ALLOC5 (size: 20, align: 4) { .. }
   
 - ALLOC5 (size: 4, align: 4) { .. }
-+ ALLOC10 (size: 4, align: 4) { .. }
++ ALLOC6 (size: 4, align: 4) { .. }
   
diff --git a/tests/mir-opt/dataflow-const-prop/struct.main.DataflowConstProp.64bit.diff b/tests/mir-opt/dataflow-const-prop/struct.main.DataflowConstProp.64bit.diff
index 7ca25e4429953..a707d7e5e76e4 100644
--- a/tests/mir-opt/dataflow-const-prop/struct.main.DataflowConstProp.64bit.diff
+++ b/tests/mir-opt/dataflow-const-prop/struct.main.DataflowConstProp.64bit.diff
@@ -106,8 +106,7 @@
 -         _7 = (_10.0: f32);
 +         _7 = const 4f32;
           StorageLive(_8);
--         _8 = (_10.1: std::option::Option<S>);
-+         _8 = const Option::<S>::Some(S(1_i32));
+          _8 = (_10.1: std::option::Option<S>);
           StorageLive(_9);
           _9 = (_10.2: &[f32]);
           StorageDead(_10);
@@ -157,8 +156,7 @@
 +         _23 = const 82f32;
           StorageLive(_24);
           _37 = deref_copy (*_26);
--         _24 = ((*_37).1: std::option::Option<S>);
-+         _24 = const Option::<S>::Some(S(35_i32));
+          _24 = ((*_37).1: std::option::Option<S>);
           StorageLive(_25);
           _38 = deref_copy (*_26);
           _25 = ((*_38).2: &[f32]);
@@ -168,12 +166,11 @@
 -         _28 = _23;
 +         _28 = const 82f32;
           StorageLive(_29);
--         _29 = _24;
-+         _29 = const Option::<S>::Some(S(35_i32));
+          _29 = _24;
           StorageLive(_30);
           _30 = _25;
 -         _27 = BigStruct(move _28, move _29, move _30);
-+         _27 = BigStruct(const 82f32, const Option::<S>::Some(S(35_i32)), move _30);
++         _27 = BigStruct(const 82f32, move _29, move _30);
           StorageDead(_30);
           StorageDead(_29);
           StorageDead(_28);
@@ -199,29 +196,21 @@
       }
   }
   
-+ ALLOC2 (size: 8, align: 4) { .. }
-+ 
-+ ALLOC3 (size: 8, align: 4) { .. }
-+ 
-+ ALLOC4 (size: 8, align: 4) { .. }
-+ 
-+ ALLOC5 (size: 8, align: 4) { .. }
-+ 
-+ ALLOC6 (size: 4, align: 4) { .. }
++ ALLOC2 (size: 4, align: 4) { .. }
 + 
   ALLOC1 (static: BIG_STAT, size: 8, align: 8) { .. }
   
 - ALLOC2 (size: 32, align: 8) { .. }
-+ ALLOC7 (size: 32, align: 8) { .. }
++ ALLOC3 (size: 32, align: 8) { .. }
   
 - ALLOC3 (size: 8, align: 4) { .. }
-+ ALLOC8 (size: 8, align: 4) { .. }
++ ALLOC4 (size: 8, align: 4) { .. }
   
   ALLOC0 (static: SMALL_STAT, size: 8, align: 8) { .. }
   
 - ALLOC4 (size: 32, align: 8) { .. }
-+ ALLOC9 (size: 32, align: 8) { .. }
++ ALLOC5 (size: 32, align: 8) { .. }
   
 - ALLOC5 (size: 4, align: 4) { .. }
-+ ALLOC10 (size: 4, align: 4) { .. }
++ ALLOC6 (size: 4, align: 4) { .. }
   
diff --git a/tests/mir-opt/dataflow-const-prop/struct.rs b/tests/mir-opt/dataflow-const-prop/struct.rs
index 4b160c3dab7e8..89ad1b870294e 100644
--- a/tests/mir-opt/dataflow-const-prop/struct.rs
+++ b/tests/mir-opt/dataflow-const-prop/struct.rs
@@ -46,7 +46,7 @@ fn main() {
     const SMALL_VAL: SmallStruct = SmallStruct(4., Some(S(1)), &[]);
 
     // CHECK: [[a1]] = const 4f32;
-    // CHECK: [[b1]] = const Option::<S>::Some(S(1_i32));
+    // CHECK: [[b1]] = ({{_.*}}.1: std::option::Option<S>);
     // CHECK: [[c1]] = ({{_.*}}.2: &[f32]);
     let SmallStruct(a1, b1, c1) = SMALL_VAL;
 
@@ -69,12 +69,12 @@ fn main() {
 
     static BIG_STAT: &BigStruct = &BigStruct(82., Some(S(35)), &[45., 72.]);
     // CHECK: [[a4]] = const 82f32;
-    // CHECK: [[b4]] = const Option::<S>::Some(S(35_i32));
+    // CHECK: [[b4]] = ((*{{_.*}}).1: std::option::Option<S>);
     // CHECK: [[c4]] = ((*{{_.*}}).2: &[f32]);
     let BigStruct(a4, b4, c4) = *BIG_STAT;
 
     // We arbitrarily limit the size of synthetized values to 4 pointers.
     // `BigStruct` can be read, but we will keep a MIR aggregate for this.
-    // CHECK: [[bs]] = BigStruct(const 82f32, const Option::<S>::Some(S(35_i32)), move {{_.*}});
+    // CHECK: [[bs]] = BigStruct(const 82f32, move {{.*}}, move {{_.*}});
     let bs = BigStruct(a4, b4, c4);
 }
diff --git a/tests/mir-opt/dataflow-const-prop/tuple.main.DataflowConstProp.32bit.diff b/tests/mir-opt/dataflow-const-prop/tuple.main.DataflowConstProp.32bit.diff
index e4031b65caafe..0e14676efdf71 100644
--- a/tests/mir-opt/dataflow-const-prop/tuple.main.DataflowConstProp.32bit.diff
+++ b/tests/mir-opt/dataflow-const-prop/tuple.main.DataflowConstProp.32bit.diff
@@ -81,7 +81,7 @@
 -         _14 = _6;
 -         _11 = (move _12, move _13, move _14);
 +         _14 = const 11_i32;
-+         _11 = (const 6_i32, const (2_i32, 3_i32), const 11_i32);
++         _11 = (const 6_i32, move _13, const 11_i32);
           StorageDead(_14);
           StorageDead(_13);
           StorageDead(_12);
@@ -99,6 +99,4 @@
 + ALLOC1 (size: 8, align: 4) { .. }
 + 
 + ALLOC2 (size: 8, align: 4) { .. }
-+ 
-+ ALLOC3 (size: 8, align: 4) { .. }
   
diff --git a/tests/mir-opt/dataflow-const-prop/tuple.main.DataflowConstProp.64bit.diff b/tests/mir-opt/dataflow-const-prop/tuple.main.DataflowConstProp.64bit.diff
index e4031b65caafe..0e14676efdf71 100644
--- a/tests/mir-opt/dataflow-const-prop/tuple.main.DataflowConstProp.64bit.diff
+++ b/tests/mir-opt/dataflow-const-prop/tuple.main.DataflowConstProp.64bit.diff
@@ -81,7 +81,7 @@
 -         _14 = _6;
 -         _11 = (move _12, move _13, move _14);
 +         _14 = const 11_i32;
-+         _11 = (const 6_i32, const (2_i32, 3_i32), const 11_i32);
++         _11 = (const 6_i32, move _13, const 11_i32);
           StorageDead(_14);
           StorageDead(_13);
           StorageDead(_12);
@@ -99,6 +99,4 @@
 + ALLOC1 (size: 8, align: 4) { .. }
 + 
 + ALLOC2 (size: 8, align: 4) { .. }
-+ 
-+ ALLOC3 (size: 8, align: 4) { .. }
   
diff --git a/tests/mir-opt/dataflow-const-prop/tuple.rs b/tests/mir-opt/dataflow-const-prop/tuple.rs
index 19b675770abe9..ed794ca658cea 100644
--- a/tests/mir-opt/dataflow-const-prop/tuple.rs
+++ b/tests/mir-opt/dataflow-const-prop/tuple.rs
@@ -23,6 +23,7 @@ fn main() {
     // CHECK: [[c]] = const 11_i32;
     let c = a.0 + a.1 + b;
 
-    // CHECK: [[d]] = (const 6_i32, const (2_i32, 3_i32), const 11_i32);
+    // CHECK: [[a2:_.*]] = const (2_i32, 3_i32);
+    // CHECK: [[d]] = (const 6_i32, move [[a2]], const 11_i32);
     let d = (b, a, c);
 }

From af876626b057bd4c1cf73897b8313f1d09ea6176 Mon Sep 17 00:00:00 2001
From: Camille GILLOT <gillot.camille@gmail.com>
Date: Fri, 28 Jun 2024 13:01:27 +0000
Subject: [PATCH 2/4] Add test for copying aggregates.

---
 .../aggregate_copy.foo.DataflowConstProp.diff | 55 +++++++++++++++++++
 .../dataflow-const-prop/aggregate_copy.rs     | 40 ++++++++++++++
 ...regate_copy.JumpThreading.panic-abort.diff | 55 +++++++++++++++++++
 ...egate_copy.JumpThreading.panic-unwind.diff | 55 +++++++++++++++++++
 tests/mir-opt/jump_threading.rs               | 16 ++++++
 5 files changed, 221 insertions(+)
 create mode 100644 tests/mir-opt/dataflow-const-prop/aggregate_copy.foo.DataflowConstProp.diff
 create mode 100644 tests/mir-opt/dataflow-const-prop/aggregate_copy.rs
 create mode 100644 tests/mir-opt/jump_threading.aggregate_copy.JumpThreading.panic-abort.diff
 create mode 100644 tests/mir-opt/jump_threading.aggregate_copy.JumpThreading.panic-unwind.diff

diff --git a/tests/mir-opt/dataflow-const-prop/aggregate_copy.foo.DataflowConstProp.diff b/tests/mir-opt/dataflow-const-prop/aggregate_copy.foo.DataflowConstProp.diff
new file mode 100644
index 0000000000000..19bbbad51230f
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/aggregate_copy.foo.DataflowConstProp.diff
@@ -0,0 +1,55 @@
+- // MIR for `foo` before DataflowConstProp
++ // MIR for `foo` after DataflowConstProp
+  
+  fn foo() -> u32 {
+      let mut _0: u32;
+      let _1: (u32, u32);
+      let mut _4: bool;
+      let mut _5: u32;
+      scope 1 {
+          debug a => _1;
+          let _2: (u32, u32);
+          scope 2 {
+              debug b => _2;
+              let _3: u32;
+              scope 3 {
+                  debug c => _3;
+              }
+          }
+      }
+  
+      bb0: {
+          StorageLive(_1);
+          _1 = const Foo;
+          StorageLive(_2);
+          _2 = _1;
+          StorageLive(_3);
+          _3 = (_2.1: u32);
+          StorageLive(_4);
+          StorageLive(_5);
+          _5 = _3;
+          _4 = Ge(move _5, const 2_u32);
+          switchInt(move _4) -> [0: bb2, otherwise: bb1];
+      }
+  
+      bb1: {
+          StorageDead(_5);
+          _0 = (_2.0: u32);
+          goto -> bb3;
+      }
+  
+      bb2: {
+          StorageDead(_5);
+          _0 = const 13_u32;
+          goto -> bb3;
+      }
+  
+      bb3: {
+          StorageDead(_4);
+          StorageDead(_3);
+          StorageDead(_2);
+          StorageDead(_1);
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/dataflow-const-prop/aggregate_copy.rs b/tests/mir-opt/dataflow-const-prop/aggregate_copy.rs
new file mode 100644
index 0000000000000..595f38132ee13
--- /dev/null
+++ b/tests/mir-opt/dataflow-const-prop/aggregate_copy.rs
@@ -0,0 +1,40 @@
+//! Verify that we manage to propagate the value of aggregate `a` even without directly mentioning
+//! the contained scalars.
+//@ test-mir-pass: DataflowConstProp
+
+const Foo: (u32, u32) = (5, 3);
+
+fn foo() -> u32 {
+    // CHECK-LABEL: fn foo(
+    // CHECK: debug a => [[a:_.*]];
+    // CHECK: debug b => [[b:_.*]];
+    // CHECK: debug c => [[c:_.*]];
+
+    // CHECK:bb0: {
+    // CHECK:    [[a]] = const Foo;
+    // CHECK:    [[b]] = [[a]];
+    // CHECK:    [[c]] = ([[b]].1: u32);
+    // CHECK:    switchInt(move {{_.*}}) -> [0: bb2, otherwise: bb1];
+
+    // CHECK:bb1: {
+    // CHECK:    _0 = ([[b]].0: u32);
+    // CHECK:    goto -> bb3;
+
+    // CHECK:bb2: {
+    // CHECK:    _0 = const 13_u32;
+    // CHECK:    goto -> bb3;
+
+    let a = Foo;
+    // This copies the struct in `a`. We want to ensure that we do track the contents of `a`
+    // because we need to read `b` later.
+    let b = a;
+    let c = b.1;
+    if c >= 2 { b.0 } else { 13 }
+}
+
+fn main() {
+    // CHECK-LABEL: fn main(
+    foo();
+}
+
+// EMIT_MIR aggregate_copy.foo.DataflowConstProp.diff
diff --git a/tests/mir-opt/jump_threading.aggregate_copy.JumpThreading.panic-abort.diff b/tests/mir-opt/jump_threading.aggregate_copy.JumpThreading.panic-abort.diff
new file mode 100644
index 0000000000000..ec62ba2feeaa6
--- /dev/null
+++ b/tests/mir-opt/jump_threading.aggregate_copy.JumpThreading.panic-abort.diff
@@ -0,0 +1,55 @@
+- // MIR for `aggregate_copy` before JumpThreading
++ // MIR for `aggregate_copy` after JumpThreading
+  
+  fn aggregate_copy() -> u32 {
+      let mut _0: u32;
+      let _1: (u32, u32);
+      let mut _4: bool;
+      let mut _5: u32;
+      scope 1 {
+          debug a => _1;
+          let _2: (u32, u32);
+          scope 2 {
+              debug b => _2;
+              let _3: u32;
+              scope 3 {
+                  debug c => _3;
+              }
+          }
+      }
+  
+      bb0: {
+          StorageLive(_1);
+          _1 = const aggregate_copy::Foo;
+          StorageLive(_2);
+          _2 = _1;
+          StorageLive(_3);
+          _3 = (_2.1: u32);
+          StorageLive(_4);
+          StorageLive(_5);
+          _5 = _3;
+          _4 = Eq(move _5, const 2_u32);
+          switchInt(move _4) -> [0: bb2, otherwise: bb1];
+      }
+  
+      bb1: {
+          StorageDead(_5);
+          _0 = (_2.0: u32);
+          goto -> bb3;
+      }
+  
+      bb2: {
+          StorageDead(_5);
+          _0 = const 13_u32;
+          goto -> bb3;
+      }
+  
+      bb3: {
+          StorageDead(_4);
+          StorageDead(_3);
+          StorageDead(_2);
+          StorageDead(_1);
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/jump_threading.aggregate_copy.JumpThreading.panic-unwind.diff b/tests/mir-opt/jump_threading.aggregate_copy.JumpThreading.panic-unwind.diff
new file mode 100644
index 0000000000000..ec62ba2feeaa6
--- /dev/null
+++ b/tests/mir-opt/jump_threading.aggregate_copy.JumpThreading.panic-unwind.diff
@@ -0,0 +1,55 @@
+- // MIR for `aggregate_copy` before JumpThreading
++ // MIR for `aggregate_copy` after JumpThreading
+  
+  fn aggregate_copy() -> u32 {
+      let mut _0: u32;
+      let _1: (u32, u32);
+      let mut _4: bool;
+      let mut _5: u32;
+      scope 1 {
+          debug a => _1;
+          let _2: (u32, u32);
+          scope 2 {
+              debug b => _2;
+              let _3: u32;
+              scope 3 {
+                  debug c => _3;
+              }
+          }
+      }
+  
+      bb0: {
+          StorageLive(_1);
+          _1 = const aggregate_copy::Foo;
+          StorageLive(_2);
+          _2 = _1;
+          StorageLive(_3);
+          _3 = (_2.1: u32);
+          StorageLive(_4);
+          StorageLive(_5);
+          _5 = _3;
+          _4 = Eq(move _5, const 2_u32);
+          switchInt(move _4) -> [0: bb2, otherwise: bb1];
+      }
+  
+      bb1: {
+          StorageDead(_5);
+          _0 = (_2.0: u32);
+          goto -> bb3;
+      }
+  
+      bb2: {
+          StorageDead(_5);
+          _0 = const 13_u32;
+          goto -> bb3;
+      }
+  
+      bb3: {
+          StorageDead(_4);
+          StorageDead(_3);
+          StorageDead(_2);
+          StorageDead(_1);
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/jump_threading.rs b/tests/mir-opt/jump_threading.rs
index b4c133716800c..59b5b2c6eea37 100644
--- a/tests/mir-opt/jump_threading.rs
+++ b/tests/mir-opt/jump_threading.rs
@@ -506,6 +506,21 @@ fn assume(a: u8, b: bool) -> u8 {
     }
 }
 
+/// Verify that jump threading succeeds seeing through copies of aggregates.
+fn aggregate_copy() -> u32 {
+    // CHECK-LABEL: fn aggregate_copy(
+    // CHECK: switchInt(
+
+    const Foo: (u32, u32) = (5, 3);
+
+    let a = Foo;
+    // This copies a tuple, we want to ensure that the threading condition on `b.1` propagates to a
+    // condition on `a.1`.
+    let b = a;
+    let c = b.1;
+    if c == 2 { b.0 } else { 13 }
+}
+
 fn main() {
     // CHECK-LABEL: fn main(
     too_complex(Ok(0));
@@ -534,3 +549,4 @@ fn main() {
 // EMIT_MIR jump_threading.disappearing_bb.JumpThreading.diff
 // EMIT_MIR jump_threading.aggregate.JumpThreading.diff
 // EMIT_MIR jump_threading.assume.JumpThreading.diff
+// EMIT_MIR jump_threading.aggregate_copy.JumpThreading.diff

From 7ac7f135e3938c55e2fccb194ece52519c1797d2 Mon Sep 17 00:00:00 2001
From: Camille GILLOT <gillot.camille@gmail.com>
Date: Fri, 28 Jun 2024 13:02:08 +0000
Subject: [PATCH 3/4] Propagate places through assignments.

---
 .../rustc_mir_dataflow/src/value_analysis.rs  | 119 ++++++++++++++++--
 .../aggregate_copy.foo.DataflowConstProp.diff |  22 +++-
 .../dataflow-const-prop/aggregate_copy.rs     |  10 +-
 ...pr_transparent.main.DataflowConstProp.diff |   6 +-
 .../dataflow-const-prop/repr_transparent.rs   |   2 +-
 ...te.less_as_i8.DataflowConstProp.32bit.diff |  14 ++-
 ...te.less_as_i8.DataflowConstProp.64bit.diff |  14 ++-
 .../tuple.main.DataflowConstProp.32bit.diff   |   4 +-
 .../tuple.main.DataflowConstProp.64bit.diff   |   4 +-
 tests/mir-opt/dataflow-const-prop/tuple.rs    |   3 +-
 ...regate_copy.JumpThreading.panic-abort.diff |   3 +-
 ...egate_copy.JumpThreading.panic-unwind.diff |   3 +-
 tests/mir-opt/jump_threading.rs               |   2 +-
 13 files changed, 173 insertions(+), 33 deletions(-)

diff --git a/compiler/rustc_mir_dataflow/src/value_analysis.rs b/compiler/rustc_mir_dataflow/src/value_analysis.rs
index f21a241914442..c9f5d38fe2c1c 100644
--- a/compiler/rustc_mir_dataflow/src/value_analysis.rs
+++ b/compiler/rustc_mir_dataflow/src/value_analysis.rs
@@ -36,7 +36,7 @@ use std::fmt::{Debug, Formatter};
 use std::ops::Range;
 
 use rustc_data_structures::captures::Captures;
-use rustc_data_structures::fx::{FxHashMap, StdEntry};
+use rustc_data_structures::fx::{FxHashMap, FxIndexSet, StdEntry};
 use rustc_data_structures::stack::ensure_sufficient_stack;
 use rustc_index::bit_set::BitSet;
 use rustc_index::IndexVec;
@@ -799,7 +799,52 @@ impl<'tcx> Map<'tcx> {
             self.locals[local] = Some(place);
         }
 
-        PlaceCollector { tcx, body, map: self }.visit_body(body);
+        // Collect syntactic places and assignments between them.
+        let mut collector =
+            PlaceCollector { tcx, body, map: self, assignments: Default::default() };
+        collector.visit_body(body);
+        let PlaceCollector { mut assignments, .. } = collector;
+
+        // Just collecting syntactic places is not enough. We may need to propagate this pattern:
+        //      _1 = (const 5u32, const 13i64);
+        //      _2 = _1;
+        //      _3 = (_2.0 as u32);
+        //
+        // `_1.0` does not appear, but we still need to track it. This is achieved by propagating
+        // projections from assignments. We recorded an assignment between `_2` and `_1`, so we
+        // want `_1` and `_2` to have the same sub-places.
+        //
+        // This is what this fixpoint loop does. While we are still creating places, run through
+        // all the assignments, and register places for children.
+        let mut num_places = 0;
+        while num_places < self.places.len() {
+            num_places = self.places.len();
+
+            for assign in 0.. {
+                let Some(&(lhs, rhs)) = assignments.get_index(assign) else { break };
+
+                // Mirror children from `lhs` in `rhs`.
+                let mut child = self.places[lhs].first_child;
+                while let Some(lhs_child) = child {
+                    let PlaceInfo { ty, proj_elem, next_sibling, .. } = self.places[lhs_child];
+                    let rhs_child =
+                        self.register_place(ty, rhs, proj_elem.expect("child is not a projection"));
+                    assignments.insert((lhs_child, rhs_child));
+                    child = next_sibling;
+                }
+
+                // Conversely, mirror children from `rhs` in `lhs`.
+                let mut child = self.places[rhs].first_child;
+                while let Some(rhs_child) = child {
+                    let PlaceInfo { ty, proj_elem, next_sibling, .. } = self.places[rhs_child];
+                    let lhs_child =
+                        self.register_place(ty, lhs, proj_elem.expect("child is not a projection"));
+                    assignments.insert((lhs_child, rhs_child));
+                    child = next_sibling;
+                }
+            }
+        }
+        drop(assignments);
 
         // Create values for places whose type have scalar layout.
         let param_env = tcx.param_env_reveal_all_normalized(body.source.def_id());
@@ -882,17 +927,14 @@ struct PlaceCollector<'a, 'b, 'tcx> {
     tcx: TyCtxt<'tcx>,
     body: &'b Body<'tcx>,
     map: &'a mut Map<'tcx>,
+    assignments: FxIndexSet<(PlaceIndex, PlaceIndex)>,
 }
 
-impl<'tcx> Visitor<'tcx> for PlaceCollector<'_, '_, 'tcx> {
+impl<'tcx> PlaceCollector<'_, '_, 'tcx> {
     #[tracing::instrument(level = "trace", skip(self))]
-    fn visit_place(&mut self, place: &Place<'tcx>, ctxt: PlaceContext, _: Location) {
-        if !ctxt.is_use() {
-            return;
-        }
-
+    fn register_place(&mut self, place: Place<'tcx>) -> Option<PlaceIndex> {
         // Create a place for this projection.
-        let Some(mut place_index) = self.map.locals[place.local] else { return };
+        let mut place_index = self.map.locals[place.local]?;
         let mut ty = PlaceTy::from_ty(self.body.local_decls[place.local].ty);
         tracing::trace!(?place_index, ?ty);
 
@@ -906,7 +948,7 @@ impl<'tcx> Visitor<'tcx> for PlaceCollector<'_, '_, 'tcx> {
         }
 
         for proj in place.projection {
-            let Ok(track_elem) = proj.try_into() else { return };
+            let track_elem = proj.try_into().ok()?;
             ty = ty.projection_ty(self.tcx, proj);
             place_index = self.map.register_place(ty.ty, place_index, track_elem);
             tracing::trace!(?proj, ?place_index, ?ty);
@@ -920,6 +962,63 @@ impl<'tcx> Visitor<'tcx> for PlaceCollector<'_, '_, 'tcx> {
                 self.map.register_place(discriminant_ty, place_index, TrackElem::Discriminant);
             }
         }
+
+        Some(place_index)
+    }
+}
+
+impl<'tcx> Visitor<'tcx> for PlaceCollector<'_, '_, 'tcx> {
+    #[tracing::instrument(level = "trace", skip(self))]
+    fn visit_place(&mut self, place: &Place<'tcx>, ctxt: PlaceContext, _: Location) {
+        if !ctxt.is_use() {
+            return;
+        }
+
+        self.register_place(*place);
+    }
+
+    fn visit_assign(&mut self, lhs: &Place<'tcx>, rhs: &Rvalue<'tcx>, location: Location) {
+        self.super_assign(lhs, rhs, location);
+
+        match rhs {
+            Rvalue::Use(Operand::Move(rhs) | Operand::Copy(rhs)) | Rvalue::CopyForDeref(rhs) => {
+                let Some(lhs) = self.register_place(*lhs) else { return };
+                let Some(rhs) = self.register_place(*rhs) else { return };
+                self.assignments.insert((lhs, rhs));
+            }
+            Rvalue::Aggregate(kind, fields) => {
+                let Some(mut lhs) = self.register_place(*lhs) else { return };
+                match **kind {
+                    // Do not propagate unions.
+                    AggregateKind::Adt(_, _, _, _, Some(_)) => return,
+                    AggregateKind::Adt(_, variant, _, _, None) => {
+                        let ty = self.map.places[lhs].ty;
+                        if ty.is_enum() {
+                            lhs = self.map.register_place(ty, lhs, TrackElem::Variant(variant));
+                        }
+                    }
+                    AggregateKind::RawPtr(..)
+                    | AggregateKind::Array(_)
+                    | AggregateKind::Tuple
+                    | AggregateKind::Closure(..)
+                    | AggregateKind::Coroutine(..)
+                    | AggregateKind::CoroutineClosure(..) => {}
+                }
+                for (index, field) in fields.iter_enumerated() {
+                    if let Some(rhs) = field.place()
+                        && let Some(rhs) = self.register_place(rhs)
+                    {
+                        let lhs = self.map.register_place(
+                            self.map.places[rhs].ty,
+                            lhs,
+                            TrackElem::Field(index),
+                        );
+                        self.assignments.insert((lhs, rhs));
+                    }
+                }
+            }
+            _ => {}
+        }
     }
 }
 
diff --git a/tests/mir-opt/dataflow-const-prop/aggregate_copy.foo.DataflowConstProp.diff b/tests/mir-opt/dataflow-const-prop/aggregate_copy.foo.DataflowConstProp.diff
index 19bbbad51230f..1aeaaff21dceb 100644
--- a/tests/mir-opt/dataflow-const-prop/aggregate_copy.foo.DataflowConstProp.diff
+++ b/tests/mir-opt/dataflow-const-prop/aggregate_copy.foo.DataflowConstProp.diff
@@ -22,19 +22,25 @@
           StorageLive(_1);
           _1 = const Foo;
           StorageLive(_2);
-          _2 = _1;
+-         _2 = _1;
++         _2 = const (5_u32, 3_u32);
           StorageLive(_3);
-          _3 = (_2.1: u32);
+-         _3 = (_2.1: u32);
++         _3 = const 3_u32;
           StorageLive(_4);
           StorageLive(_5);
-          _5 = _3;
-          _4 = Ge(move _5, const 2_u32);
-          switchInt(move _4) -> [0: bb2, otherwise: bb1];
+-         _5 = _3;
+-         _4 = Ge(move _5, const 2_u32);
+-         switchInt(move _4) -> [0: bb2, otherwise: bb1];
++         _5 = const 3_u32;
++         _4 = const true;
++         switchInt(const true) -> [0: bb2, otherwise: bb1];
       }
   
       bb1: {
           StorageDead(_5);
-          _0 = (_2.0: u32);
+-         _0 = (_2.0: u32);
++         _0 = const 5_u32;
           goto -> bb3;
       }
   
@@ -51,5 +57,9 @@
           StorageDead(_1);
           return;
       }
++ }
++ 
++ ALLOC0 (size: 8, align: 4) {
++     05 00 00 00 03 00 00 00                         │ ........
   }
   
diff --git a/tests/mir-opt/dataflow-const-prop/aggregate_copy.rs b/tests/mir-opt/dataflow-const-prop/aggregate_copy.rs
index 595f38132ee13..aca5f047222f5 100644
--- a/tests/mir-opt/dataflow-const-prop/aggregate_copy.rs
+++ b/tests/mir-opt/dataflow-const-prop/aggregate_copy.rs
@@ -12,12 +12,14 @@ fn foo() -> u32 {
 
     // CHECK:bb0: {
     // CHECK:    [[a]] = const Foo;
-    // CHECK:    [[b]] = [[a]];
-    // CHECK:    [[c]] = ([[b]].1: u32);
-    // CHECK:    switchInt(move {{_.*}}) -> [0: bb2, otherwise: bb1];
+    // CHECK:    [[b]] = const (5_u32, 3_u32);
+    // CHECK:    [[c]] = const 3_u32;
+    // CHECK:    {{_.*}} = const 3_u32;
+    // CHECK:    {{_.*}} = const true;
+    // CHECK:    switchInt(const true) -> [0: bb2, otherwise: bb1];
 
     // CHECK:bb1: {
-    // CHECK:    _0 = ([[b]].0: u32);
+    // CHECK:    _0 = const 5_u32;
     // CHECK:    goto -> bb3;
 
     // CHECK:bb2: {
diff --git a/tests/mir-opt/dataflow-const-prop/repr_transparent.main.DataflowConstProp.diff b/tests/mir-opt/dataflow-const-prop/repr_transparent.main.DataflowConstProp.diff
index a77eb4708f2f0..98bd40ab2c3da 100644
--- a/tests/mir-opt/dataflow-const-prop/repr_transparent.main.DataflowConstProp.diff
+++ b/tests/mir-opt/dataflow-const-prop/repr_transparent.main.DataflowConstProp.diff
@@ -32,7 +32,7 @@
           StorageDead(_5);
           StorageDead(_4);
 -         _2 = I32(move _3);
-+         _2 = I32(const 0_i32);
++         _2 = const I32(0_i32);
           StorageDead(_3);
           _0 = const ();
           StorageDead(_2);
@@ -42,6 +42,10 @@
 + }
 + 
 + ALLOC0 (size: 4, align: 4) {
++     00 00 00 00                                     │ ....
++ }
++ 
++ ALLOC1 (size: 4, align: 4) {
 +     00 00 00 00                                     │ ....
   }
   
diff --git a/tests/mir-opt/dataflow-const-prop/repr_transparent.rs b/tests/mir-opt/dataflow-const-prop/repr_transparent.rs
index 6e3c48c81dc23..6152724c98f66 100644
--- a/tests/mir-opt/dataflow-const-prop/repr_transparent.rs
+++ b/tests/mir-opt/dataflow-const-prop/repr_transparent.rs
@@ -15,6 +15,6 @@ fn main() {
     // CHECK: [[x]] = const I32(0_i32);
     let x = I32(0);
 
-    // CHECK: [[y]] = I32(const 0_i32);
+    // CHECK: [[y]] = const I32(0_i32);
     let y = I32(x.0 + x.0);
 }
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
index 44dd40174092a..a023243ad9dac 100644
--- 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
@@ -7,10 +7,20 @@
   
       bb0: {
           StorageLive(_1);
-          _1 = Less;
-          _0 = move _1 as i8 (Transmute);
+-         _1 = Less;
+-         _0 = move _1 as i8 (Transmute);
++         _1 = const Less;
++         _0 = const std::cmp::Ordering::Less as i8 (Transmute);
           StorageDead(_1);
           return;
       }
++ }
++ 
++ ALLOC0 (size: 1, align: 1) {
++     ff                                              │ .
++ }
++ 
++ ALLOC1 (size: 1, align: 1) {
++     ff                                              │ .
   }
   
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
index 44dd40174092a..a023243ad9dac 100644
--- 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
@@ -7,10 +7,20 @@
   
       bb0: {
           StorageLive(_1);
-          _1 = Less;
-          _0 = move _1 as i8 (Transmute);
+-         _1 = Less;
+-         _0 = move _1 as i8 (Transmute);
++         _1 = const Less;
++         _0 = const std::cmp::Ordering::Less as i8 (Transmute);
           StorageDead(_1);
           return;
       }
++ }
++ 
++ ALLOC0 (size: 1, align: 1) {
++     ff                                              │ .
++ }
++ 
++ ALLOC1 (size: 1, align: 1) {
++     ff                                              │ .
   }
   
diff --git a/tests/mir-opt/dataflow-const-prop/tuple.main.DataflowConstProp.32bit.diff b/tests/mir-opt/dataflow-const-prop/tuple.main.DataflowConstProp.32bit.diff
index 0e14676efdf71..e4031b65caafe 100644
--- a/tests/mir-opt/dataflow-const-prop/tuple.main.DataflowConstProp.32bit.diff
+++ b/tests/mir-opt/dataflow-const-prop/tuple.main.DataflowConstProp.32bit.diff
@@ -81,7 +81,7 @@
 -         _14 = _6;
 -         _11 = (move _12, move _13, move _14);
 +         _14 = const 11_i32;
-+         _11 = (const 6_i32, move _13, const 11_i32);
++         _11 = (const 6_i32, const (2_i32, 3_i32), const 11_i32);
           StorageDead(_14);
           StorageDead(_13);
           StorageDead(_12);
@@ -99,4 +99,6 @@
 + ALLOC1 (size: 8, align: 4) { .. }
 + 
 + ALLOC2 (size: 8, align: 4) { .. }
++ 
++ ALLOC3 (size: 8, align: 4) { .. }
   
diff --git a/tests/mir-opt/dataflow-const-prop/tuple.main.DataflowConstProp.64bit.diff b/tests/mir-opt/dataflow-const-prop/tuple.main.DataflowConstProp.64bit.diff
index 0e14676efdf71..e4031b65caafe 100644
--- a/tests/mir-opt/dataflow-const-prop/tuple.main.DataflowConstProp.64bit.diff
+++ b/tests/mir-opt/dataflow-const-prop/tuple.main.DataflowConstProp.64bit.diff
@@ -81,7 +81,7 @@
 -         _14 = _6;
 -         _11 = (move _12, move _13, move _14);
 +         _14 = const 11_i32;
-+         _11 = (const 6_i32, move _13, const 11_i32);
++         _11 = (const 6_i32, const (2_i32, 3_i32), const 11_i32);
           StorageDead(_14);
           StorageDead(_13);
           StorageDead(_12);
@@ -99,4 +99,6 @@
 + ALLOC1 (size: 8, align: 4) { .. }
 + 
 + ALLOC2 (size: 8, align: 4) { .. }
++ 
++ ALLOC3 (size: 8, align: 4) { .. }
   
diff --git a/tests/mir-opt/dataflow-const-prop/tuple.rs b/tests/mir-opt/dataflow-const-prop/tuple.rs
index ed794ca658cea..19b675770abe9 100644
--- a/tests/mir-opt/dataflow-const-prop/tuple.rs
+++ b/tests/mir-opt/dataflow-const-prop/tuple.rs
@@ -23,7 +23,6 @@ fn main() {
     // CHECK: [[c]] = const 11_i32;
     let c = a.0 + a.1 + b;
 
-    // CHECK: [[a2:_.*]] = const (2_i32, 3_i32);
-    // CHECK: [[d]] = (const 6_i32, move [[a2]], const 11_i32);
+    // CHECK: [[d]] = (const 6_i32, const (2_i32, 3_i32), const 11_i32);
     let d = (b, a, c);
 }
diff --git a/tests/mir-opt/jump_threading.aggregate_copy.JumpThreading.panic-abort.diff b/tests/mir-opt/jump_threading.aggregate_copy.JumpThreading.panic-abort.diff
index ec62ba2feeaa6..0c8e04a1e74dc 100644
--- a/tests/mir-opt/jump_threading.aggregate_copy.JumpThreading.panic-abort.diff
+++ b/tests/mir-opt/jump_threading.aggregate_copy.JumpThreading.panic-abort.diff
@@ -29,7 +29,8 @@
           StorageLive(_5);
           _5 = _3;
           _4 = Eq(move _5, const 2_u32);
-          switchInt(move _4) -> [0: bb2, otherwise: bb1];
+-         switchInt(move _4) -> [0: bb2, otherwise: bb1];
++         goto -> bb2;
       }
   
       bb1: {
diff --git a/tests/mir-opt/jump_threading.aggregate_copy.JumpThreading.panic-unwind.diff b/tests/mir-opt/jump_threading.aggregate_copy.JumpThreading.panic-unwind.diff
index ec62ba2feeaa6..0c8e04a1e74dc 100644
--- a/tests/mir-opt/jump_threading.aggregate_copy.JumpThreading.panic-unwind.diff
+++ b/tests/mir-opt/jump_threading.aggregate_copy.JumpThreading.panic-unwind.diff
@@ -29,7 +29,8 @@
           StorageLive(_5);
           _5 = _3;
           _4 = Eq(move _5, const 2_u32);
-          switchInt(move _4) -> [0: bb2, otherwise: bb1];
+-         switchInt(move _4) -> [0: bb2, otherwise: bb1];
++         goto -> bb2;
       }
   
       bb1: {
diff --git a/tests/mir-opt/jump_threading.rs b/tests/mir-opt/jump_threading.rs
index 59b5b2c6eea37..de290c1ef4473 100644
--- a/tests/mir-opt/jump_threading.rs
+++ b/tests/mir-opt/jump_threading.rs
@@ -509,7 +509,7 @@ fn assume(a: u8, b: bool) -> u8 {
 /// Verify that jump threading succeeds seeing through copies of aggregates.
 fn aggregate_copy() -> u32 {
     // CHECK-LABEL: fn aggregate_copy(
-    // CHECK: switchInt(
+    // CHECK-NOT: switchInt(
 
     const Foo: (u32, u32) = (5, 3);
 

From 6e3cff70948b2292467f2e3e1b14d14372956034 Mon Sep 17 00:00:00 2001
From: Camille GILLOT <gillot.camille@gmail.com>
Date: Sat, 6 Jul 2024 11:18:58 +0000
Subject: [PATCH 4/4] Bless coverage.

---
 tests/coverage/closure.cov-map | 16 +++++++++-------
 1 file changed, 9 insertions(+), 7 deletions(-)

diff --git a/tests/coverage/closure.cov-map b/tests/coverage/closure.cov-map
index f36ef7af7ac38..6edd35921fe95 100644
--- a/tests/coverage/closure.cov-map
+++ b/tests/coverage/closure.cov-map
@@ -31,16 +31,18 @@ Number of file 0 mappings: 24
     = (c0 - c1)
 - Code(Counter(0)) at (prev + 1, 5) to (start + 3, 2)
 
-Function name: closure::main::{closure#0} (unused)
-Raw bytes (24): 0x[01, 01, 00, 04, 00, 28, 05, 02, 14, 00, 02, 15, 02, 0a, 00, 02, 0a, 00, 0b, 00, 01, 09, 01, 06]
+Function name: closure::main::{closure#0}
+Raw bytes (26): 0x[01, 01, 01, 01, 05, 04, 01, 28, 05, 02, 14, 05, 02, 15, 02, 0a, 02, 02, 0a, 00, 0b, 01, 01, 09, 01, 06]
 Number of files: 1
 - file 0 => global file 1
-Number of expressions: 0
+Number of expressions: 1
+- expression 0 operands: lhs = Counter(0), rhs = Counter(1)
 Number of file 0 mappings: 4
-- Code(Zero) at (prev + 40, 5) to (start + 2, 20)
-- Code(Zero) at (prev + 2, 21) to (start + 2, 10)
-- Code(Zero) at (prev + 2, 10) to (start + 0, 11)
-- Code(Zero) at (prev + 1, 9) to (start + 1, 6)
+- Code(Counter(0)) at (prev + 40, 5) to (start + 2, 20)
+- Code(Counter(1)) at (prev + 2, 21) to (start + 2, 10)
+- Code(Expression(0, Sub)) at (prev + 2, 10) to (start + 0, 11)
+    = (c0 - c1)
+- Code(Counter(0)) at (prev + 1, 9) to (start + 1, 6)
 
 Function name: closure::main::{closure#10} (unused)
 Raw bytes (10): 0x[01, 01, 00, 01, 00, 9b, 01, 07, 00, 21]