Skip to content

Commit 55b9b4d

Browse files
committed
Auto merge of #146289 - cjgillot:gvn-aggregate, r=dianqk
GVN: Allow reusing aggregates if LHS is not a simple local. This resolves a FIXME in the code. I don't see a reason not to allow this.
2 parents f13ef0d + 52f74a5 commit 55b9b4d

File tree

4 files changed

+90
-30
lines changed

4 files changed

+90
-30
lines changed

compiler/rustc_mir_transform/src/gvn.rs

Lines changed: 17 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -896,18 +896,13 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
896896

897897
fn simplify_aggregate_to_copy(
898898
&mut self,
899-
lhs: &Place<'tcx>,
900-
rvalue: &mut Rvalue<'tcx>,
901-
location: Location,
902-
fields: &[VnIndex],
899+
ty: Ty<'tcx>,
903900
variant_index: VariantIdx,
901+
fields: &[VnIndex],
904902
) -> Option<VnIndex> {
905-
let Some(&first_field) = fields.first() else {
906-
return None;
907-
};
908-
let Value::Projection(copy_from_value, _) = *self.get(first_field) else {
909-
return None;
910-
};
903+
let Some(&first_field) = fields.first() else { return None };
904+
let Value::Projection(copy_from_value, _) = *self.get(first_field) else { return None };
905+
911906
// All fields must correspond one-to-one and come from the same aggregate value.
912907
if fields.iter().enumerate().any(|(index, &v)| {
913908
if let Value::Projection(pointer, ProjectionElem::Field(from_index, _)) = *self.get(v)
@@ -934,21 +929,8 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
934929
}
935930
}
936931

937-
// Allow introducing places with non-constant offsets, as those are still better than
938-
// reconstructing an aggregate.
939-
if self.ty(copy_from_local_value) == rvalue.ty(self.local_decls, self.tcx)
940-
&& let Some(place) = self.try_as_place(copy_from_local_value, location, true)
941-
{
942-
// Avoid creating `*a = copy (*b)`, as they might be aliases resulting in overlapping assignments.
943-
// FIXME: This also avoids any kind of projection, not just derefs. We can add allowed projections.
944-
if lhs.as_local().is_some() {
945-
self.reused_locals.insert(place.local);
946-
*rvalue = Rvalue::Use(Operand::Copy(place));
947-
}
948-
return Some(copy_from_local_value);
949-
}
950-
951-
None
932+
// Both must be variants of the same type.
933+
if self.ty(copy_from_local_value) == ty { Some(copy_from_local_value) } else { None }
952934
}
953935

954936
fn simplify_aggregate(
@@ -1035,9 +1017,16 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
10351017
return Some(self.insert(ty, Value::Repeat(first, len)));
10361018
}
10371019

1038-
if let Some(value) =
1039-
self.simplify_aggregate_to_copy(lhs, rvalue, location, &fields, variant_index)
1040-
{
1020+
if let Some(value) = self.simplify_aggregate_to_copy(ty, variant_index, &fields) {
1021+
// Allow introducing places with non-constant offsets, as those are still better than
1022+
// reconstructing an aggregate. But avoid creating `*a = copy (*b)`, as they might be
1023+
// aliases resulting in overlapping assignments.
1024+
let allow_complex_projection =
1025+
lhs.projection[..].iter().all(PlaceElem::is_stable_offset);
1026+
if let Some(place) = self.try_as_place(value, location, allow_complex_projection) {
1027+
self.reused_locals.insert(place.local);
1028+
*rvalue = Rvalue::Use(Operand::Copy(place));
1029+
}
10411030
return Some(value);
10421031
}
10431032

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
- // MIR for `fields` before GVN
2+
+ // MIR for `fields` after GVN
3+
4+
fn fields(_1: (Adt, Adt)) -> () {
5+
let mut _0: ();
6+
let mut _2: u32;
7+
8+
bb0: {
9+
_2 = copy (((_1.0: Adt) as variant#1).0: u32);
10+
(_1.1: Adt) = Adt::Some(copy _2);
11+
return;
12+
}
13+
}
14+

tests/mir-opt/gvn_overlapping.rs

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,10 @@
22

33
#![feature(custom_mir, core_intrinsics)]
44

5-
// Check that we do not create overlapping assignments.
6-
75
use std::intrinsics::mir::*;
86

97
// EMIT_MIR gvn_overlapping.overlapping.GVN.diff
8+
/// Check that we do not create overlapping assignments.
109
#[custom_mir(dialect = "runtime")]
1110
fn overlapping(_17: Adt) {
1211
// CHECK-LABEL: fn overlapping(
@@ -26,6 +25,45 @@ fn overlapping(_17: Adt) {
2625
}
2726
}
2827

28+
// EMIT_MIR gvn_overlapping.stable_projection.GVN.diff
29+
/// Check that we allow dereferences in the RHS if the LHS is a stable projection.
30+
#[custom_mir(dialect = "runtime")]
31+
fn stable_projection(_1: (Adt,)) {
32+
// CHECK-LABEL: fn stable_projection(
33+
// CHECK: let mut _2: *mut Adt;
34+
// CHECK: let mut _4: &Adt;
35+
// CHECK: (_1.0: Adt) = copy (*_4);
36+
mir! {
37+
let _2: *mut Adt;
38+
let _3: u32;
39+
let _4: &Adt;
40+
{
41+
_2 = core::ptr::addr_of_mut!(_1.0);
42+
_4 = &(*_2);
43+
_3 = Field(Variant((*_4), 1), 0);
44+
_1.0 = Adt::Some(_3);
45+
Return()
46+
}
47+
}
48+
}
49+
50+
// EMIT_MIR gvn_overlapping.fields.GVN.diff
51+
/// Check that we do not create assignments between different fields of the same local.
52+
#[custom_mir(dialect = "runtime")]
53+
fn fields(_1: (Adt, Adt)) {
54+
// CHECK-LABEL: fn fields(
55+
// CHECK: _2 = copy (((_1.0: Adt) as variant#1).0: u32);
56+
// CHECK-NEXT: (_1.1: Adt) = Adt::Some(copy _2);
57+
mir! {
58+
let _2: u32;
59+
{
60+
_2 = Field(Variant(_1.0, 1), 0);
61+
_1.1 = Adt::Some(_2);
62+
Return()
63+
}
64+
}
65+
}
66+
2967
fn main() {
3068
overlapping(Adt::Some(0));
3169
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
- // MIR for `stable_projection` before GVN
2+
+ // MIR for `stable_projection` after GVN
3+
4+
fn stable_projection(_1: (Adt,)) -> () {
5+
let mut _0: ();
6+
let mut _2: *mut Adt;
7+
let mut _3: u32;
8+
let mut _4: &Adt;
9+
10+
bb0: {
11+
_2 = &raw mut (_1.0: Adt);
12+
_4 = &(*_2);
13+
_3 = copy (((*_4) as variant#1).0: u32);
14+
- (_1.0: Adt) = Adt::Some(copy _3);
15+
+ (_1.0: Adt) = copy (*_4);
16+
return;
17+
}
18+
}
19+

0 commit comments

Comments
 (0)