about summary refs log tree commit diff
diff options
context:
space:
mode:
authordianqk <dianqk@dianqk.net>2025-05-18 17:04:49 +0800
committerdianqk <dianqk@dianqk.net>2025-05-18 18:42:00 +0800
commitd2e5a3d131bd5a4ac92c0e6cfd3c49b5b6d44ab6 (patch)
tree9165e560cd2016806241fc4186ff2e9b21b7af29
parentac17c3486c6fdfbb0c3c18b99f3d8dfbff625d29 (diff)
downloadrust-d2e5a3d131bd5a4ac92c0e6cfd3c49b5b6d44ab6.tar.gz
rust-d2e5a3d131bd5a4ac92c0e6cfd3c49b5b6d44ab6.zip
gvn: avoid creating overlapping assignments
-rw-r--r--compiler/rustc_mir_transform/src/gvn.rs19
-rw-r--r--tests/mir-opt/gvn_overlapping.overlapping.GVN.diff18
-rw-r--r--tests/mir-opt/gvn_overlapping.rs36
3 files changed, 67 insertions, 6 deletions
diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs
index 8b8d1efbbd2..209e818e9e3 100644
--- a/compiler/rustc_mir_transform/src/gvn.rs
+++ b/compiler/rustc_mir_transform/src/gvn.rs
@@ -836,6 +836,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
     #[instrument(level = "trace", skip(self), ret)]
     fn simplify_rvalue(
         &mut self,
+        lhs: &Place<'tcx>,
         rvalue: &mut Rvalue<'tcx>,
         location: Location,
     ) -> Option<VnIndex> {
@@ -855,7 +856,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
                 Value::Repeat(op, amount)
             }
             Rvalue::NullaryOp(op, ty) => Value::NullaryOp(op, ty),
-            Rvalue::Aggregate(..) => return self.simplify_aggregate(rvalue, location),
+            Rvalue::Aggregate(..) => return self.simplify_aggregate(lhs, rvalue, location),
             Rvalue::Ref(_, borrow_kind, ref mut place) => {
                 self.simplify_place_projection(place, location);
                 return Some(self.new_pointer(*place, AddressKind::Ref(borrow_kind)));
@@ -943,6 +944,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
 
     fn simplify_aggregate_to_copy(
         &mut self,
+        lhs: &Place<'tcx>,
         rvalue: &mut Rvalue<'tcx>,
         location: Location,
         fields: &[VnIndex],
@@ -982,12 +984,16 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
 
         // Allow introducing places with non-constant offsets, as those are still better than
         // reconstructing an aggregate.
-        if let Some(place) = self.try_as_place(copy_from_local_value, location, true) {
-            if rvalue.ty(self.local_decls, self.tcx) == place.ty(self.local_decls, self.tcx).ty {
+        if let Some(place) = self.try_as_place(copy_from_local_value, location, true)
+            && rvalue.ty(self.local_decls, self.tcx) == place.ty(self.local_decls, self.tcx).ty
+        {
+            // Avoid creating `*a = copy (*b)`, as they might be aliases resulting in overlapping assignments.
+            // FIXME: This also avoids any kind of projection, not just derefs. We can add allowed projections.
+            if lhs.as_local().is_some() {
                 self.reused_locals.insert(place.local);
                 *rvalue = Rvalue::Use(Operand::Copy(place));
-                return Some(copy_from_local_value);
             }
+            return Some(copy_from_local_value);
         }
 
         None
@@ -995,6 +1001,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
 
     fn simplify_aggregate(
         &mut self,
+        lhs: &Place<'tcx>,
         rvalue: &mut Rvalue<'tcx>,
         location: Location,
     ) -> Option<VnIndex> {
@@ -1090,7 +1097,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
 
         if let AggregateTy::Def(_, _) = ty
             && let Some(value) =
-                self.simplify_aggregate_to_copy(rvalue, location, &fields, variant_index)
+                self.simplify_aggregate_to_copy(lhs, rvalue, location, &fields, variant_index)
         {
             return Some(value);
         }
@@ -1765,7 +1772,7 @@ impl<'tcx> MutVisitor<'tcx> for VnState<'_, 'tcx> {
         if let StatementKind::Assign(box (ref mut lhs, ref mut rvalue)) = stmt.kind {
             self.simplify_place_projection(lhs, location);
 
-            let value = self.simplify_rvalue(rvalue, location);
+            let value = self.simplify_rvalue(lhs, rvalue, location);
             let value = if let Some(local) = lhs.as_local()
                 && self.ssa.is_ssa(local)
                 // FIXME(#112651) `rvalue` may have a subtype to `local`. We can only mark
diff --git a/tests/mir-opt/gvn_overlapping.overlapping.GVN.diff b/tests/mir-opt/gvn_overlapping.overlapping.GVN.diff
new file mode 100644
index 00000000000..fcabcdbcfef
--- /dev/null
+++ b/tests/mir-opt/gvn_overlapping.overlapping.GVN.diff
@@ -0,0 +1,18 @@
+- // MIR for `overlapping` before GVN
++ // MIR for `overlapping` after GVN
+  
+  fn overlapping(_1: Adt) -> () {
+      let mut _0: ();
+      let mut _2: *mut Adt;
+      let mut _3: u32;
+      let mut _4: &Adt;
+  
+      bb0: {
+          _2 = &raw mut _1;
+          _4 = &(*_2);
+          _3 = copy (((*_4) as variant#1).0: u32);
+          (*_2) = Adt::Some(copy _3);
+          return;
+      }
+  }
+  
diff --git a/tests/mir-opt/gvn_overlapping.rs b/tests/mir-opt/gvn_overlapping.rs
new file mode 100644
index 00000000000..99113445e68
--- /dev/null
+++ b/tests/mir-opt/gvn_overlapping.rs
@@ -0,0 +1,36 @@
+//@ test-mir-pass: GVN
+
+#![feature(custom_mir, core_intrinsics)]
+
+// Check that we do not create overlapping assignments.
+
+use std::intrinsics::mir::*;
+
+// EMIT_MIR gvn_overlapping.overlapping.GVN.diff
+#[custom_mir(dialect = "runtime")]
+fn overlapping(_17: Adt) {
+    // CHECK-LABEL: fn overlapping(
+    // CHECK: let mut [[PTR:.*]]: *mut Adt;
+    // CHECK: (*[[PTR]]) = Adt::Some(copy {{.*}});
+    mir! {
+        let _33: *mut Adt;
+        let _48: u32;
+        let _73: &Adt;
+        {
+            _33 = core::ptr::addr_of_mut!(_17);
+            _73 = &(*_33);
+            _48 = Field(Variant((*_73), 1), 0);
+            (*_33) = Adt::Some(_48);
+            Return()
+        }
+    }
+}
+
+fn main() {
+    overlapping(Adt::Some(0));
+}
+
+enum Adt {
+    None,
+    Some(u32),
+}