Skip to content

Commit e9cd151

Browse files
committed
GVN: Do not introduce new dereferences or unify dereferences if they borrow from non-SSA locals
1 parent e312df7 commit e9cd151

29 files changed

+638
-354
lines changed

compiler/rustc_mir_transform/src/gvn.rs

Lines changed: 87 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ impl<'tcx> crate::MirPass<'tcx> for GVN {
135135
VnState::new(tcx, body, typing_env, &ssa, dominators, &body.local_decls, &arena);
136136

137137
for local in body.args_iter().filter(|&local| ssa.is_ssa(local)) {
138-
let opaque = state.new_opaque(body.local_decls[local].ty);
138+
let opaque = state.new_argument(body.local_decls[local].ty);
139139
state.assign(local, opaque);
140140
}
141141

@@ -198,8 +198,9 @@ enum AddressBase {
198198
enum Value<'a, 'tcx> {
199199
// Root values.
200200
/// Used to represent values we know nothing about.
201-
/// The `usize` is a counter incremented by `new_opaque`.
202201
Opaque(VnOpaque),
202+
/// The value is a argument.
203+
Argument(VnOpaque),
203204
/// Evaluated or unevaluated constant value.
204205
Constant {
205206
value: Const<'tcx>,
@@ -284,7 +285,7 @@ impl<'a, 'tcx> ValueSet<'a, 'tcx> {
284285
let value = value(VnOpaque);
285286

286287
debug_assert!(match value {
287-
Value::Opaque(_) | Value::Address { .. } => true,
288+
Value::Opaque(_) | Value::Argument(_) | Value::Address { .. } => true,
288289
Value::Constant { disambiguator, .. } => disambiguator.is_some(),
289290
_ => false,
290291
});
@@ -440,6 +441,13 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
440441
index
441442
}
442443

444+
#[instrument(level = "trace", skip(self), ret)]
445+
fn new_argument(&mut self, ty: Ty<'tcx>) -> VnIndex {
446+
let index = self.insert_unique(ty, Value::Argument);
447+
self.evaluated[index] = Some(None);
448+
index
449+
}
450+
443451
/// Create a new `Value::Address` distinct from all the others.
444452
#[instrument(level = "trace", skip(self), ret)]
445453
fn new_pointer(&mut self, place: Place<'tcx>, kind: AddressKind) -> Option<VnIndex> {
@@ -458,6 +466,10 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
458466
projection.next();
459467
AddressBase::Deref(base)
460468
} else {
469+
// Only propagate the pointer of the SSA local.
470+
if !self.ssa.is_ssa(place.local) {
471+
return None;
472+
}
461473
AddressBase::Local(place.local)
462474
};
463475
// Do not try evaluating inside `Index`, this has been done by `simplify_place_projection`.
@@ -526,10 +538,6 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
526538
self.insert(ty, Value::Aggregate(VariantIdx::ZERO, self.arena.alloc_slice(values)))
527539
}
528540

529-
fn insert_deref(&mut self, ty: Ty<'tcx>, value: VnIndex) -> VnIndex {
530-
self.insert(ty, Value::Projection(value, ProjectionElem::Deref))
531-
}
532-
533541
#[instrument(level = "trace", skip(self), ret)]
534542
fn eval_to_const_inner(&mut self, value: VnIndex) -> Option<OpTy<'tcx>> {
535543
use Value::*;
@@ -543,7 +551,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
543551
let op = match self.get(value) {
544552
_ if ty.is_zst() => ImmTy::uninit(ty).into(),
545553

546-
Opaque(_) => return None,
554+
Opaque(_) | Argument(_) => return None,
547555
// Keep runtime check constants as symbolic.
548556
RuntimeChecks(..) => return None,
549557

@@ -795,7 +803,13 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
795803

796804
// An immutable borrow `_x` always points to the same value for the
797805
// lifetime of the borrow, so we can merge all instances of `*_x`.
798-
return Some((projection_ty, self.insert_deref(projection_ty.ty, value)));
806+
return Some((
807+
projection_ty,
808+
self.insert(
809+
projection_ty.ty,
810+
Value::Projection(value, ProjectionElem::Deref),
811+
),
812+
));
799813
} else {
800814
return None;
801815
}
@@ -1119,6 +1133,42 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
11191133
}
11201134
}
11211135

1136+
// We can introduce a new dereference if the source value cannot be changed in the body.
1137+
let mut copy_root = copy_from_local_value;
1138+
loop {
1139+
match self.get(copy_root) {
1140+
Value::Projection(base, _) => {
1141+
copy_root = base;
1142+
}
1143+
Value::Address {
1144+
base,
1145+
projection,
1146+
kind: AddressKind::Ref(BorrowKind::Shared),
1147+
..
1148+
} if projection.iter().all(ProjectionElem::is_stable_offset) => match base {
1149+
AddressBase::Local(local) => {
1150+
break;
1151+
}
1152+
AddressBase::Deref(index) => {
1153+
copy_root = index;
1154+
}
1155+
},
1156+
Value::Argument(_) if !self.ty(copy_root).is_mutable_ptr() => {
1157+
break;
1158+
}
1159+
Value::Opaque(_) => {
1160+
let ty = self.ty(copy_root);
1161+
if ty.is_fn() || !ty.is_any_ptr() {
1162+
break;
1163+
}
1164+
return None;
1165+
}
1166+
_ => {
1167+
return None;
1168+
}
1169+
}
1170+
}
1171+
11221172
// Both must be variants of the same type.
11231173
if self.ty(copy_from_local_value) == ty { Some(copy_from_local_value) } else { None }
11241174
}
@@ -1208,12 +1258,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
12081258
}
12091259

12101260
if let Some(value) = self.simplify_aggregate_to_copy(ty, variant_index, &fields) {
1211-
// Allow introducing places with non-constant offsets, as those are still better than
1212-
// reconstructing an aggregate. But avoid creating `*a = copy (*b)`, as they might be
1213-
// aliases resulting in overlapping assignments.
1214-
let allow_complex_projection =
1215-
lhs.projection[..].iter().all(PlaceElem::is_stable_offset);
1216-
if let Some(place) = self.try_as_place(value, location, allow_complex_projection) {
1261+
if let Some(place) = self.try_as_place(value, location, true) {
12171262
self.reused_locals.insert(place.local);
12181263
*rvalue = Rvalue::Use(Operand::Copy(place));
12191264
}
@@ -1866,7 +1911,7 @@ impl<'tcx> MutVisitor<'tcx> for VnState<'_, '_, 'tcx> {
18661911
) {
18671912
self.simplify_place_projection(lhs, location);
18681913

1869-
let value = self.simplify_rvalue(lhs, rvalue, location);
1914+
let mut value = self.simplify_rvalue(lhs, rvalue, location);
18701915
if let Some(value) = value {
18711916
if let Some(const_) = self.try_as_constant(value) {
18721917
*rvalue = Rvalue::Use(Operand::Constant(Box::new(const_)));
@@ -1879,9 +1924,34 @@ impl<'tcx> MutVisitor<'tcx> for VnState<'_, '_, 'tcx> {
18791924
}
18801925
}
18811926

1927+
let rvalue_ty = rvalue.ty(self.local_decls, self.tcx);
1928+
// DO NOT reason the pointer value if it may point to a non-SSA local.
1929+
// For instance, we cannot unify two pointers that dereference same local, because they may
1930+
// have different lifetimes.
1931+
if rvalue_ty.is_ref()
1932+
&& let Some(index) = value
1933+
{
1934+
match self.get(index) {
1935+
Value::Opaque(_) | Value::Projection(..) => {
1936+
value = None;
1937+
}
1938+
Value::Constant { .. }
1939+
| Value::Address { .. }
1940+
| Value::Argument(_)
1941+
| Value::RawPtr { .. }
1942+
| Value::BinaryOp(..)
1943+
| Value::Cast { .. } => {}
1944+
Value::Aggregate(..)
1945+
| Value::Union(..)
1946+
| Value::Repeat(..)
1947+
| Value::Discriminant(..)
1948+
| Value::RuntimeChecks(..)
1949+
| Value::UnaryOp(..) => unreachable!(),
1950+
}
1951+
}
1952+
18821953
if let Some(local) = lhs.as_local()
18831954
&& self.ssa.is_ssa(local)
1884-
&& let rvalue_ty = rvalue.ty(self.local_decls, self.tcx)
18851955
// FIXME(#112651) `rvalue` may have a subtype to `local`. We can only mark
18861956
// `local` as reusable if we have an exact type match.
18871957
&& self.local_decls[local].ty == rvalue_ty
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
- // MIR for `dereference_reborrow` before GVN
2+
+ // MIR for `dereference_reborrow` after GVN
3+
4+
fn dereference_reborrow(_1: &mut u8) -> () {
5+
debug mut_a => _1;
6+
let mut _0: ();
7+
let _2: &u8;
8+
scope 1 {
9+
debug a => _2;
10+
let _3: u8;
11+
scope 2 {
12+
debug b => _3;
13+
let _4: u8;
14+
scope 3 {
15+
debug c => _4;
16+
}
17+
}
18+
}
19+
20+
bb0: {
21+
StorageLive(_2);
22+
_2 = &(*_1);
23+
- StorageLive(_3);
24+
+ nop;
25+
_3 = copy (*_2);
26+
StorageLive(_4);
27+
- _4 = copy (*_2);
28+
+ _4 = copy _3;
29+
_0 = const ();
30+
StorageDead(_4);
31+
- StorageDead(_3);
32+
+ nop;
33+
StorageDead(_2);
34+
return;
35+
}
36+
}
37+
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
- // MIR for `dereference_reborrow` before GVN
2+
+ // MIR for `dereference_reborrow` after GVN
3+
4+
fn dereference_reborrow(_1: &mut u8) -> () {
5+
debug mut_a => _1;
6+
let mut _0: ();
7+
let _2: &u8;
8+
scope 1 {
9+
debug a => _2;
10+
let _3: u8;
11+
scope 2 {
12+
debug b => _3;
13+
let _4: u8;
14+
scope 3 {
15+
debug c => _4;
16+
}
17+
}
18+
}
19+
20+
bb0: {
21+
StorageLive(_2);
22+
_2 = &(*_1);
23+
- StorageLive(_3);
24+
+ nop;
25+
_3 = copy (*_2);
26+
StorageLive(_4);
27+
- _4 = copy (*_2);
28+
+ _4 = copy _3;
29+
_0 = const ();
30+
StorageDead(_4);
31+
- StorageDead(_3);
32+
+ nop;
33+
StorageDead(_2);
34+
return;
35+
}
36+
}
37+

tests/mir-opt/gvn.dereferences.GVN.panic-abort.diff

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -107,18 +107,23 @@
107107
StorageLive(_18);
108108
_18 = &(*_1);
109109
StorageLive(_19);
110-
StorageLive(_20);
110+
- StorageLive(_20);
111+
+ nop;
111112
_20 = copy (*_18);
112-
_19 = opaque::<u32>(move _20) -> [return: bb7, unwind unreachable];
113+
- _19 = opaque::<u32>(move _20) -> [return: bb7, unwind unreachable];
114+
+ _19 = opaque::<u32>(copy _20) -> [return: bb7, unwind unreachable];
113115
}
114116

115117
bb7: {
116-
StorageDead(_20);
118+
- StorageDead(_20);
119+
+ nop;
117120
StorageDead(_19);
118121
StorageLive(_21);
119122
StorageLive(_22);
120-
_22 = copy (*_18);
121-
_21 = opaque::<u32>(move _22) -> [return: bb8, unwind unreachable];
123+
- _22 = copy (*_18);
124+
- _21 = opaque::<u32>(move _22) -> [return: bb8, unwind unreachable];
125+
+ _22 = copy _20;
126+
+ _21 = opaque::<u32>(copy _20) -> [return: bb8, unwind unreachable];
122127
}
123128

124129
bb8: {
@@ -152,18 +157,23 @@
152157
StorageDead(_28);
153158
StorageDead(_27);
154159
StorageLive(_29);
155-
StorageLive(_30);
160+
- StorageLive(_30);
161+
+ nop;
156162
_30 = copy ((*_3).0: u32);
157-
_29 = opaque::<u32>(move _30) -> [return: bb12, unwind unreachable];
163+
- _29 = opaque::<u32>(move _30) -> [return: bb12, unwind unreachable];
164+
+ _29 = opaque::<u32>(copy _30) -> [return: bb12, unwind unreachable];
158165
}
159166

160167
bb12: {
161-
StorageDead(_30);
168+
- StorageDead(_30);
169+
+ nop;
162170
StorageDead(_29);
163171
StorageLive(_31);
164172
StorageLive(_32);
165-
_32 = copy ((*_3).0: u32);
166-
_31 = opaque::<u32>(move _32) -> [return: bb13, unwind unreachable];
173+
- _32 = copy ((*_3).0: u32);
174+
- _31 = opaque::<u32>(move _32) -> [return: bb13, unwind unreachable];
175+
+ _32 = copy _30;
176+
+ _31 = opaque::<u32>(copy _30) -> [return: bb13, unwind unreachable];
167177
}
168178

169179
bb13: {

tests/mir-opt/gvn.dereferences.GVN.panic-unwind.diff

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -107,18 +107,23 @@
107107
StorageLive(_18);
108108
_18 = &(*_1);
109109
StorageLive(_19);
110-
StorageLive(_20);
110+
- StorageLive(_20);
111+
+ nop;
111112
_20 = copy (*_18);
112-
_19 = opaque::<u32>(move _20) -> [return: bb7, unwind continue];
113+
- _19 = opaque::<u32>(move _20) -> [return: bb7, unwind continue];
114+
+ _19 = opaque::<u32>(copy _20) -> [return: bb7, unwind continue];
113115
}
114116

115117
bb7: {
116-
StorageDead(_20);
118+
- StorageDead(_20);
119+
+ nop;
117120
StorageDead(_19);
118121
StorageLive(_21);
119122
StorageLive(_22);
120-
_22 = copy (*_18);
121-
_21 = opaque::<u32>(move _22) -> [return: bb8, unwind continue];
123+
- _22 = copy (*_18);
124+
- _21 = opaque::<u32>(move _22) -> [return: bb8, unwind continue];
125+
+ _22 = copy _20;
126+
+ _21 = opaque::<u32>(copy _20) -> [return: bb8, unwind continue];
122127
}
123128

124129
bb8: {
@@ -152,18 +157,23 @@
152157
StorageDead(_28);
153158
StorageDead(_27);
154159
StorageLive(_29);
155-
StorageLive(_30);
160+
- StorageLive(_30);
161+
+ nop;
156162
_30 = copy ((*_3).0: u32);
157-
_29 = opaque::<u32>(move _30) -> [return: bb12, unwind continue];
163+
- _29 = opaque::<u32>(move _30) -> [return: bb12, unwind continue];
164+
+ _29 = opaque::<u32>(copy _30) -> [return: bb12, unwind continue];
158165
}
159166

160167
bb12: {
161-
StorageDead(_30);
168+
- StorageDead(_30);
169+
+ nop;
162170
StorageDead(_29);
163171
StorageLive(_31);
164172
StorageLive(_32);
165-
_32 = copy ((*_3).0: u32);
166-
_31 = opaque::<u32>(move _32) -> [return: bb13, unwind continue];
173+
- _32 = copy ((*_3).0: u32);
174+
- _31 = opaque::<u32>(move _32) -> [return: bb13, unwind continue];
175+
+ _32 = copy _30;
176+
+ _31 = opaque::<u32>(copy _30) -> [return: bb13, unwind continue];
167177
}
168178

169179
bb13: {

0 commit comments

Comments
 (0)