Skip to content

Commit 13c3873

Browse files
committed
Auto merge of #150485 - dianqk:gvn-ssa-borrow, r=cjgillot
GVN: Only propagate borrows from SSA locals Fixes #141313. This is a more principled fix than #147886. Using a reference that is not a borrowing of an SSA local at a new location may be UB. The PR has two major changes. The first one, when introducing a new dereference at a new location, is that the reference must point to an SSA local or be an immutable argument. `dereference_address` has handled SSA locals. The second one, if we cannot guard to the reference point to an SSA local in `visit_assign`, we have to rewrite the value to opaque. This avoids unifying the following dereferences that also are references: ```rust let b: &T = *a; // ... `a` is allowed to be modified. `c` and `b` have different borrowing lifetime. // Unifying them will extend the lifetime of `b`. let c: &T = *a; ``` See also #130853. This still allows unifying non-reference dereferences: ```rust let a: &T = ...; let b: T = *a; // ... a is NOT allowed to be modified. let c: T = *a; ``` r? @cjgillot
2 parents 08a4ce5 + 9c029d2 commit 13c3873

File tree

33 files changed

+909
-353
lines changed

33 files changed

+909
-353
lines changed

compiler/rustc_middle/src/mir/terminator.rs

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -710,28 +710,6 @@ impl<'tcx> TerminatorKind<'tcx> {
710710
_ => None,
711711
}
712712
}
713-
714-
/// Returns true if the terminator can write to memory.
715-
pub fn can_write_to_memory(&self) -> bool {
716-
match self {
717-
TerminatorKind::Goto { .. }
718-
| TerminatorKind::SwitchInt { .. }
719-
| TerminatorKind::UnwindResume
720-
| TerminatorKind::UnwindTerminate(_)
721-
| TerminatorKind::Return
722-
| TerminatorKind::Assert { .. }
723-
| TerminatorKind::CoroutineDrop
724-
| TerminatorKind::FalseEdge { .. }
725-
| TerminatorKind::FalseUnwind { .. }
726-
| TerminatorKind::Unreachable => false,
727-
TerminatorKind::Call { .. }
728-
| TerminatorKind::Drop { .. }
729-
| TerminatorKind::TailCall { .. }
730-
// Yield writes to the resume_arg place.
731-
| TerminatorKind::Yield { .. }
732-
| TerminatorKind::InlineAsm { .. } => true,
733-
}
734-
}
735713
}
736714

737715
#[derive(Copy, Clone, Debug)]

compiler/rustc_mir_transform/src/gvn.rs

Lines changed: 44 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -129,24 +129,18 @@ impl<'tcx> crate::MirPass<'tcx> for GVN {
129129
let ssa = SsaLocals::new(tcx, body, typing_env);
130130
// Clone dominators because we need them while mutating the body.
131131
let dominators = body.basic_blocks.dominators().clone();
132-
let maybe_loop_headers = loops::maybe_loop_headers(body);
133132

134133
let arena = DroplessArena::default();
135134
let mut state =
136135
VnState::new(tcx, body, typing_env, &ssa, dominators, &body.local_decls, &arena);
137136

138137
for local in body.args_iter().filter(|&local| ssa.is_ssa(local)) {
139-
let opaque = state.new_opaque(body.local_decls[local].ty);
138+
let opaque = state.new_argument(body.local_decls[local].ty);
140139
state.assign(local, opaque);
141140
}
142141

143142
let reverse_postorder = body.basic_blocks.reverse_postorder().to_vec();
144143
for bb in reverse_postorder {
145-
// N.B. With loops, reverse postorder cannot produce a valid topological order.
146-
// A statement or terminator from inside the loop, that is not processed yet, may have performed an indirect write.
147-
if maybe_loop_headers.contains(bb) {
148-
state.invalidate_derefs();
149-
}
150144
let data = &mut body.basic_blocks.as_mut_preserves_cfg()[bb];
151145
state.visit_basic_block_data(bb, data);
152146
}
@@ -204,8 +198,9 @@ enum AddressBase {
204198
enum Value<'a, 'tcx> {
205199
// Root values.
206200
/// Used to represent values we know nothing about.
207-
/// The `usize` is a counter incremented by `new_opaque`.
208201
Opaque(VnOpaque),
202+
/// The value is a argument.
203+
Argument(VnOpaque),
209204
/// Evaluated or unevaluated constant value.
210205
Constant {
211206
value: Const<'tcx>,
@@ -290,7 +285,7 @@ impl<'a, 'tcx> ValueSet<'a, 'tcx> {
290285
let value = value(VnOpaque);
291286

292287
debug_assert!(match value {
293-
Value::Opaque(_) | Value::Address { .. } => true,
288+
Value::Opaque(_) | Value::Argument(_) | Value::Address { .. } => true,
294289
Value::Constant { disambiguator, .. } => disambiguator.is_some(),
295290
_ => false,
296291
});
@@ -350,12 +345,6 @@ impl<'a, 'tcx> ValueSet<'a, 'tcx> {
350345
fn ty(&self, index: VnIndex) -> Ty<'tcx> {
351346
self.types[index]
352347
}
353-
354-
/// Replace the value associated with `index` with an opaque value.
355-
#[inline]
356-
fn forget(&mut self, index: VnIndex) {
357-
self.values[index] = Value::Opaque(VnOpaque);
358-
}
359348
}
360349

361350
struct VnState<'body, 'a, 'tcx> {
@@ -374,8 +363,6 @@ struct VnState<'body, 'a, 'tcx> {
374363
/// - `Some(None)` are values for which computation has failed;
375364
/// - `Some(Some(op))` are successful computations.
376365
evaluated: IndexVec<VnIndex, Option<Option<&'a OpTy<'tcx>>>>,
377-
/// Cache the deref values.
378-
derefs: Vec<VnIndex>,
379366
ssa: &'body SsaLocals,
380367
dominators: Dominators<BasicBlock>,
381368
reused_locals: DenseBitSet<Local>,
@@ -408,7 +395,6 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
408395
rev_locals: IndexVec::with_capacity(num_values),
409396
values: ValueSet::new(num_values),
410397
evaluated: IndexVec::with_capacity(num_values),
411-
derefs: Vec::new(),
412398
ssa,
413399
dominators,
414400
reused_locals: DenseBitSet::new_empty(local_decls.len()),
@@ -455,6 +441,13 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
455441
index
456442
}
457443

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+
458451
/// Create a new `Value::Address` distinct from all the others.
459452
#[instrument(level = "trace", skip(self), ret)]
460453
fn new_pointer(&mut self, place: Place<'tcx>, kind: AddressKind) -> Option<VnIndex> {
@@ -472,8 +465,11 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
472465
// Skip the initial `Deref`.
473466
projection.next();
474467
AddressBase::Deref(base)
475-
} else {
468+
} else if self.ssa.is_ssa(place.local) {
469+
// Only propagate the pointer of the SSA local.
476470
AddressBase::Local(place.local)
471+
} else {
472+
return None;
477473
};
478474
// Do not try evaluating inside `Index`, this has been done by `simplify_place_projection`.
479475
let projection =
@@ -541,18 +537,6 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
541537
self.insert(ty, Value::Aggregate(VariantIdx::ZERO, self.arena.alloc_slice(values)))
542538
}
543539

544-
fn insert_deref(&mut self, ty: Ty<'tcx>, value: VnIndex) -> VnIndex {
545-
let value = self.insert(ty, Value::Projection(value, ProjectionElem::Deref));
546-
self.derefs.push(value);
547-
value
548-
}
549-
550-
fn invalidate_derefs(&mut self) {
551-
for deref in std::mem::take(&mut self.derefs) {
552-
self.values.forget(deref);
553-
}
554-
}
555-
556540
#[instrument(level = "trace", skip(self), ret)]
557541
fn eval_to_const_inner(&mut self, value: VnIndex) -> Option<OpTy<'tcx>> {
558542
use Value::*;
@@ -566,7 +550,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
566550
let op = match self.get(value) {
567551
_ if ty.is_zst() => ImmTy::uninit(ty).into(),
568552

569-
Opaque(_) => return None,
553+
Opaque(_) | Argument(_) => return None,
570554
// Keep runtime check constants as symbolic.
571555
RuntimeChecks(..) => return None,
572556

@@ -815,10 +799,24 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
815799
{
816800
return Some((projection_ty, value));
817801
}
802+
// DO NOT reason the pointer value.
803+
// We cannot unify two pointers that dereference same local, because they may
804+
// have different lifetimes.
805+
// ```
806+
// let b: &T = *a;
807+
// ... `a` is allowed to be modified. `c` and `b` have different borrowing lifetime.
808+
// Unifying them will extend the lifetime of `b`.
809+
// let c: &T = *a;
810+
// ```
811+
if projection_ty.ty.is_ref() {
812+
return None;
813+
}
818814

819815
// An immutable borrow `_x` always points to the same value for the
820816
// lifetime of the borrow, so we can merge all instances of `*_x`.
821-
return Some((projection_ty, self.insert_deref(projection_ty.ty, value)));
817+
let deref = self
818+
.insert(projection_ty.ty, Value::Projection(value, ProjectionElem::Deref));
819+
return Some((projection_ty, deref));
822820
} else {
823821
return None;
824822
}
@@ -1037,7 +1035,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
10371035
let op = self.simplify_operand(op, location)?;
10381036
Value::Repeat(op, amount)
10391037
}
1040-
Rvalue::Aggregate(..) => return self.simplify_aggregate(lhs, rvalue, location),
1038+
Rvalue::Aggregate(..) => return self.simplify_aggregate(rvalue, location),
10411039
Rvalue::Ref(_, borrow_kind, ref mut place) => {
10421040
self.simplify_place_projection(place, location);
10431041
return self.new_pointer(*place, AddressKind::Ref(borrow_kind));
@@ -1148,7 +1146,6 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
11481146

11491147
fn simplify_aggregate(
11501148
&mut self,
1151-
lhs: &Place<'tcx>,
11521149
rvalue: &mut Rvalue<'tcx>,
11531150
location: Location,
11541151
) -> Option<VnIndex> {
@@ -1231,12 +1228,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
12311228
}
12321229

12331230
if let Some(value) = self.simplify_aggregate_to_copy(ty, variant_index, &fields) {
1234-
// Allow introducing places with non-constant offsets, as those are still better than
1235-
// reconstructing an aggregate. But avoid creating `*a = copy (*b)`, as they might be
1236-
// aliases resulting in overlapping assignments.
1237-
let allow_complex_projection =
1238-
lhs.projection[..].iter().all(PlaceElem::is_stable_offset);
1239-
if let Some(place) = self.try_as_place(value, location, allow_complex_projection) {
1231+
if let Some(place) = self.try_as_place(value, location, true) {
12401232
self.reused_locals.insert(place.local);
12411233
*rvalue = Rvalue::Use(Operand::Copy(place));
12421234
}
@@ -1890,6 +1882,17 @@ impl<'tcx> VnState<'_, '_, 'tcx> {
18901882
&& (allow_complex_projection || proj.is_stable_offset())
18911883
&& let Some(proj) = self.try_as_place_elem(self.ty(index), proj, loc)
18921884
{
1885+
if proj == PlaceElem::Deref {
1886+
// We can introduce a new dereference if the source value cannot be changed in the body.
1887+
// Dereferencing an immutable argument always gives the same value in the body.
1888+
match self.get(pointer) {
1889+
Value::Argument(_)
1890+
if let Some(Mutability::Not) = self.ty(pointer).ref_mutability() => {}
1891+
_ => {
1892+
return None;
1893+
}
1894+
}
1895+
}
18931896
projection.push(proj);
18941897
index = pointer;
18951898
} else {
@@ -1916,10 +1919,6 @@ impl<'tcx> MutVisitor<'tcx> for VnState<'_, '_, 'tcx> {
19161919

19171920
fn visit_place(&mut self, place: &mut Place<'tcx>, context: PlaceContext, location: Location) {
19181921
self.simplify_place_projection(place, location);
1919-
if context.is_mutating_use() && place.is_indirect() {
1920-
// Non-local mutation maybe invalidate deref.
1921-
self.invalidate_derefs();
1922-
}
19231922
self.super_place(place, context, location);
19241923
}
19251924

@@ -1949,11 +1948,6 @@ impl<'tcx> MutVisitor<'tcx> for VnState<'_, '_, 'tcx> {
19491948
}
19501949
}
19511950

1952-
if lhs.is_indirect() {
1953-
// Non-local mutation maybe invalidate deref.
1954-
self.invalidate_derefs();
1955-
}
1956-
19571951
if let Some(local) = lhs.as_local()
19581952
&& self.ssa.is_ssa(local)
19591953
&& let rvalue_ty = rvalue.ty(self.local_decls, self.tcx)
@@ -1976,10 +1970,6 @@ impl<'tcx> MutVisitor<'tcx> for VnState<'_, '_, 'tcx> {
19761970
self.assign(local, opaque);
19771971
}
19781972
}
1979-
// Terminators that can write to memory may invalidate (nested) derefs.
1980-
if terminator.kind.can_write_to_memory() {
1981-
self.invalidate_derefs();
1982-
}
19831973
self.super_terminator(terminator, location);
19841974
}
19851975
}

tests/mir-opt/dont_reset_cast_kind_without_updating_operand.test.GVN.32bit.panic-abort.diff

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,7 @@
7575

7676
bb0: {
7777
StorageLive(_1);
78-
- StorageLive(_2);
79-
+ nop;
78+
StorageLive(_2);
8079
StorageLive(_3);
8180
StorageLive(_4);
8281
- _4 = ();
@@ -144,12 +143,10 @@
144143
StorageDead(_4);
145144
_2 = &_3;
146145
_1 = &(*_2);
147-
- StorageDead(_2);
146+
StorageDead(_2);
148147
- StorageLive(_5);
149-
- _10 = copy (*_1);
150-
+ nop;
151148
+ nop;
152-
+ _10 = copy (*_2);
149+
_10 = copy (*_1);
153150
_11 = copy ((_10.0: std::ptr::Unique<()>).0: std::ptr::NonNull<()>) as *const () (Transmute);
154151
_5 = &raw const (*_11);
155152
- StorageLive(_6);

tests/mir-opt/dont_reset_cast_kind_without_updating_operand.test.GVN.32bit.panic-unwind.diff

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,7 @@
3737

3838
bb0: {
3939
StorageLive(_1);
40-
- StorageLive(_2);
41-
+ nop;
40+
StorageLive(_2);
4241
StorageLive(_3);
4342
StorageLive(_4);
4443
- _4 = ();
@@ -51,12 +50,10 @@
5150
StorageDead(_4);
5251
_2 = &_3;
5352
_1 = &(*_2);
54-
- StorageDead(_2);
53+
StorageDead(_2);
5554
- StorageLive(_5);
56-
- _10 = copy (*_1);
57-
+ nop;
5855
+ nop;
59-
+ _10 = copy (*_2);
56+
_10 = copy (*_1);
6057
_11 = copy ((_10.0: std::ptr::Unique<()>).0: std::ptr::NonNull<()>) as *const () (Transmute);
6158
_5 = &raw const (*_11);
6259
- StorageLive(_6);

tests/mir-opt/dont_reset_cast_kind_without_updating_operand.test.GVN.64bit.panic-abort.diff

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,7 @@
7575

7676
bb0: {
7777
StorageLive(_1);
78-
- StorageLive(_2);
79-
+ nop;
78+
StorageLive(_2);
8079
StorageLive(_3);
8180
StorageLive(_4);
8281
- _4 = ();
@@ -144,12 +143,10 @@
144143
StorageDead(_4);
145144
_2 = &_3;
146145
_1 = &(*_2);
147-
- StorageDead(_2);
146+
StorageDead(_2);
148147
- StorageLive(_5);
149-
- _10 = copy (*_1);
150-
+ nop;
151148
+ nop;
152-
+ _10 = copy (*_2);
149+
_10 = copy (*_1);
153150
_11 = copy ((_10.0: std::ptr::Unique<()>).0: std::ptr::NonNull<()>) as *const () (Transmute);
154151
_5 = &raw const (*_11);
155152
- StorageLive(_6);

tests/mir-opt/dont_reset_cast_kind_without_updating_operand.test.GVN.64bit.panic-unwind.diff

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,7 @@
3737

3838
bb0: {
3939
StorageLive(_1);
40-
- StorageLive(_2);
41-
+ nop;
40+
StorageLive(_2);
4241
StorageLive(_3);
4342
StorageLive(_4);
4443
- _4 = ();
@@ -51,12 +50,10 @@
5150
StorageDead(_4);
5251
_2 = &_3;
5352
_1 = &(*_2);
54-
- StorageDead(_2);
53+
StorageDead(_2);
5554
- StorageLive(_5);
56-
- _10 = copy (*_1);
57-
+ nop;
5855
+ nop;
59-
+ _10 = copy (*_2);
56+
_10 = copy (*_1);
6057
_11 = copy ((_10.0: std::ptr::Unique<()>).0: std::ptr::NonNull<()>) as *const () (Transmute);
6158
_5 = &raw const (*_11);
6259
- StorageLive(_6);

0 commit comments

Comments
 (0)