From d2e5a3d131bd5a4ac92c0e6cfd3c49b5b6d44ab6 Mon Sep 17 00:00:00 2001 From: dianqk Date: Sun, 18 May 2025 17:04:49 +0800 Subject: [PATCH] gvn: avoid creating overlapping assignments --- compiler/rustc_mir_transform/src/gvn.rs | 19 ++++++---- .../gvn_overlapping.overlapping.GVN.diff | 18 ++++++++++ tests/mir-opt/gvn_overlapping.rs | 36 +++++++++++++++++++ 3 files changed, 67 insertions(+), 6 deletions(-) create mode 100644 tests/mir-opt/gvn_overlapping.overlapping.GVN.diff create mode 100644 tests/mir-opt/gvn_overlapping.rs diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index 8b8d1efbbd2e0..209e818e9e32d 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 { @@ -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 { @@ -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 0000000000000..fcabcdbcfef2c --- /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 0000000000000..99113445e683d --- /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), +}