Skip to content

Commit efb5f98

Browse files
Auto merge of #147886 - dianqk:gvn-new-deref-arg, r=<try>
GVN: Only introduce new derefs if the immutable borrow is always valid.
2 parents c6efb90 + c0cb50a commit efb5f98

30 files changed

+364
-185
lines changed

compiler/rustc_mir_transform/src/gvn.rs

Lines changed: 65 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -136,8 +136,8 @@ impl<'tcx> crate::MirPass<'tcx> for GVN {
136136
VnState::new(tcx, body, typing_env, &ssa, dominators, &body.local_decls, &arena);
137137

138138
for local in body.args_iter().filter(|&local| ssa.is_ssa(local)) {
139-
let opaque = state.new_opaque(body.local_decls[local].ty);
140-
state.assign(local, opaque);
139+
let argument = state.insert_parameter(body.local_decls[local].ty);
140+
state.assign(local, argument);
141141
}
142142

143143
let reverse_postorder = body.basic_blocks.reverse_postorder().to_vec();
@@ -204,8 +204,9 @@ enum AddressBase {
204204
enum Value<'a, 'tcx> {
205205
// Root values.
206206
/// Used to represent values we know nothing about.
207-
/// The `usize` is a counter incremented by `new_opaque`.
208207
Opaque(VnOpaque),
208+
/// Used to represent values we know nothing except that it is a function parameter.
209+
Parameter(VnOpaque),
209210
/// Evaluated or unevaluated constant value.
210211
Constant {
211212
value: Const<'tcx>,
@@ -290,7 +291,7 @@ impl<'a, 'tcx> ValueSet<'a, 'tcx> {
290291
let value = value(VnOpaque);
291292

292293
debug_assert!(match value {
293-
Value::Opaque(_) | Value::Address { .. } => true,
294+
Value::Opaque(_) | Value::Parameter(_) | Value::Address { .. } => true,
294295
Value::Constant { disambiguator, .. } => disambiguator.is_some(),
295296
_ => false,
296297
});
@@ -308,7 +309,7 @@ impl<'a, 'tcx> ValueSet<'a, 'tcx> {
308309
#[allow(rustc::pass_by_value)] // closures take `&VnIndex`
309310
fn insert(&mut self, ty: Ty<'tcx>, value: Value<'a, 'tcx>) -> (VnIndex, bool) {
310311
debug_assert!(match value {
311-
Value::Opaque(_) | Value::Address { .. } => false,
312+
Value::Opaque(_) | Value::Parameter(_) | Value::Address { .. } => false,
312313
Value::Constant { disambiguator, .. } => disambiguator.is_none(),
313314
_ => true,
314315
});
@@ -420,6 +421,20 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
420421
self.ecx.typing_env()
421422
}
422423

424+
fn insert_unique(
425+
&mut self,
426+
ty: Ty<'tcx>,
427+
evaluated: bool,
428+
value: impl FnOnce(VnOpaque) -> Value<'a, 'tcx>,
429+
) -> VnIndex {
430+
let index = self.values.insert_unique(ty, value);
431+
let _index = self.evaluated.push(if evaluated { Some(None) } else { None });
432+
debug_assert_eq!(index, _index);
433+
let _index = self.rev_locals.push(SmallVec::new());
434+
debug_assert_eq!(index, _index);
435+
index
436+
}
437+
423438
#[instrument(level = "trace", skip(self), ret)]
424439
fn insert(&mut self, ty: Ty<'tcx>, value: Value<'a, 'tcx>) -> VnIndex {
425440
let (index, new) = self.values.insert(ty, value);
@@ -437,12 +452,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
437452
/// from all the others.
438453
#[instrument(level = "trace", skip(self), ret)]
439454
fn new_opaque(&mut self, ty: Ty<'tcx>) -> VnIndex {
440-
let index = self.values.insert_unique(ty, Value::Opaque);
441-
let _index = self.evaluated.push(Some(None));
442-
debug_assert_eq!(index, _index);
443-
let _index = self.rev_locals.push(SmallVec::new());
444-
debug_assert_eq!(index, _index);
445-
index
455+
self.insert_unique(ty, true, Value::Opaque)
446456
}
447457

448458
/// Create a new `Value::Address` distinct from all the others.
@@ -470,42 +480,30 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
470480
projection.map(|proj| proj.try_map(|index| self.locals[index], |ty| ty).ok_or(()));
471481
let projection = self.arena.try_alloc_from_iter(projection).ok()?;
472482

473-
let index = self.values.insert_unique(ty, |provenance| Value::Address {
483+
let index = self.insert_unique(ty, false, |provenance| Value::Address {
474484
base,
475485
projection,
476486
kind,
477487
provenance,
478488
});
479-
let _index = self.evaluated.push(None);
480-
debug_assert_eq!(index, _index);
481-
let _index = self.rev_locals.push(SmallVec::new());
482-
debug_assert_eq!(index, _index);
483489

484490
Some(index)
485491
}
486492

487493
#[instrument(level = "trace", skip(self), ret)]
488494
fn insert_constant(&mut self, value: Const<'tcx>) -> VnIndex {
489-
let (index, new) = if value.is_deterministic() {
495+
if value.is_deterministic() {
490496
// The constant is deterministic, no need to disambiguate.
491497
let constant = Value::Constant { value, disambiguator: None };
492-
self.values.insert(value.ty(), constant)
498+
self.insert(value.ty(), constant)
493499
} else {
494500
// Multiple mentions of this constant will yield different values,
495501
// so assign a different `disambiguator` to ensure they do not get the same `VnIndex`.
496-
let index = self.values.insert_unique(value.ty(), |disambiguator| Value::Constant {
502+
self.insert_unique(value.ty(), false, |disambiguator| Value::Constant {
497503
value,
498504
disambiguator: Some(disambiguator),
499-
});
500-
(index, true)
501-
};
502-
if new {
503-
let _index = self.evaluated.push(None);
504-
debug_assert_eq!(index, _index);
505-
let _index = self.rev_locals.push(SmallVec::new());
506-
debug_assert_eq!(index, _index);
505+
})
507506
}
508-
index
509507
}
510508

511509
#[inline]
@@ -540,13 +538,20 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
540538
self.insert(ty, Value::Constant { value, disambiguator: None })
541539
}
542540

541+
fn insert_parameter(&mut self, ty: Ty<'tcx>) -> VnIndex {
542+
self.insert_unique(ty, true, Value::Parameter)
543+
}
544+
543545
fn insert_tuple(&mut self, ty: Ty<'tcx>, values: &[VnIndex]) -> VnIndex {
544546
self.insert(ty, Value::Aggregate(VariantIdx::ZERO, self.arena.alloc_slice(values)))
545547
}
546548

547-
fn insert_deref(&mut self, ty: Ty<'tcx>, value: VnIndex) -> VnIndex {
549+
fn insert_deref(&mut self, ty: Ty<'tcx>, value: VnIndex, always_valid: bool) -> VnIndex {
548550
let value = self.insert(ty, Value::Projection(value, ProjectionElem::Deref));
549-
self.derefs.push(value);
551+
// If the borrow lifetime is the whole body, we don't need to invalidate it.
552+
if !always_valid {
553+
self.derefs.push(value);
554+
}
550555
value
551556
}
552557

@@ -569,7 +574,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
569574
let op = match self.get(value) {
570575
_ if ty.is_zst() => ImmTy::uninit(ty).into(),
571576

572-
Opaque(_) => return None,
577+
Opaque(_) | Parameter(_) => return None,
573578
// Do not bother evaluating repeat expressions. This would uselessly consume memory.
574579
Repeat(..) => return None,
575580

@@ -817,15 +822,38 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
817822
if let Some(Mutability::Not) = place_ty.ty.ref_mutability()
818823
&& projection_ty.ty.is_freeze(self.tcx, self.typing_env())
819824
{
820-
if let Value::Address { base, projection, .. } = self.get(value)
821-
&& let Some(value) = self.dereference_address(base, projection)
822-
{
825+
if let Value::Address { base, projection, .. } = self.get(value) {
826+
// Bail out if the address cannot be dereferenced.
827+
let value = self.dereference_address(base, projection)?;
823828
return Some((projection_ty, value));
824829
}
825830

826-
// An immutable borrow `_x` always points to the same value for the
827-
// lifetime of the borrow, so we can merge all instances of `*_x`.
828-
return Some((projection_ty, self.insert_deref(projection_ty.ty, value)));
831+
if let Value::Parameter(_) | Value::Constant { .. } = self.get(value) {
832+
// An immutable borrow `_x` that is an parameter or a constant always points to the same value for the
833+
// lifetime of the borrow, so we can merge all instances of `*_x`.
834+
// The lifetime here is unrelated to storage markers, but rather does not violate the Stacked Borrows rules.
835+
return Some((
836+
projection_ty,
837+
self.insert_deref(projection_ty.ty, value, true),
838+
));
839+
}
840+
841+
// FIXME: We could introduce new deref that the immutable borrow is not valid in the whole body,
842+
// but we must invalidate it when out of the immutable borrow lifetime.
843+
// Known related issues:
844+
// - https://github.com/rust-lang/rust/issues/130853 (Fixed by invalidating)
845+
// - https://github.com/rust-lang/rust/issues/132353 (Fixed by invalidating)
846+
// - https://github.com/rust-lang/rust/issues/141038 (Not fixed well)
847+
// - https://github.com/rust-lang/rust/issues/141251 (Fixed by #144477)
848+
// - https://github.com/rust-lang/rust/issues/141313
849+
// - https://github.com/rust-lang/rust/pull/147607 (Fixed by invalidating)
850+
if self.tcx.sess.opts.unstable_opts.unsound_mir_opts {
851+
return Some((
852+
projection_ty,
853+
self.insert_deref(projection_ty.ty, value, false),
854+
));
855+
}
856+
return None;
829857
} else {
830858
return None;
831859
}

tests/mir-opt/const_prop/ref_deref_project.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
// This does not currently propagate (#67862)
21
//@ test-mir-pass: GVN
32

3+
// Checks that GVN knows a is 5.
44
// EMIT_MIR ref_deref_project.main.GVN.diff
55
fn main() {
66
// CHECK-LABEL: fn main(

tests/mir-opt/const_prop/slice_len.main.GVN.32bit.panic-abort.diff

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040

4141
bb1: {
4242
- _1 = copy (*_2)[_6];
43-
+ _1 = const 2_u32;
43+
+ _1 = copy (*_2)[1 of 2];
4444
StorageDead(_6);
4545
StorageDead(_4);
4646
StorageDead(_2);

tests/mir-opt/const_prop/slice_len.main.GVN.32bit.panic-unwind.diff

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040

4141
bb1: {
4242
- _1 = copy (*_2)[_6];
43-
+ _1 = const 2_u32;
43+
+ _1 = copy (*_2)[1 of 2];
4444
StorageDead(_6);
4545
StorageDead(_4);
4646
StorageDead(_2);

tests/mir-opt/const_prop/slice_len.main.GVN.64bit.panic-abort.diff

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040

4141
bb1: {
4242
- _1 = copy (*_2)[_6];
43-
+ _1 = const 2_u32;
43+
+ _1 = copy (*_2)[1 of 2];
4444
StorageDead(_6);
4545
StorageDead(_4);
4646
StorageDead(_2);

tests/mir-opt/const_prop/slice_len.main.GVN.64bit.panic-unwind.diff

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040

4141
bb1: {
4242
- _1 = copy (*_2)[_6];
43-
+ _1 = const 2_u32;
43+
+ _1 = copy (*_2)[1 of 2];
4444
StorageDead(_6);
4545
StorageDead(_4);
4646
StorageDead(_2);

tests/mir-opt/const_prop/slice_len.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,13 @@
33
// EMIT_MIR_FOR_EACH_PANIC_STRATEGY
44
// EMIT_MIR_FOR_EACH_BIT_WIDTH
55

6+
// FIXME: GVN should be able to know `a` is 2;
67
// EMIT_MIR slice_len.main.GVN.diff
78
fn main() {
89
// CHECK-LABEL: fn main(
910
// CHECK: debug a => [[a:_.*]];
1011
// CHECK: [[slice:_.*]] = copy {{.*}} as &[u32] (PointerCoercion(Unsize, AsCast));
1112
// CHECK: assert(const true,
12-
// CHECK: [[a]] = const 2_u32;
13+
// CHECK: [[a]] = copy (*[[slice]])[1 of 2];
1314
let a = (&[1u32, 2, 3] as &[u32])[1];
1415
}

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
@@ -72,8 +72,7 @@
7272

7373
bb0: {
7474
StorageLive(_1);
75-
- StorageLive(_2);
76-
+ nop;
75+
StorageLive(_2);
7776
StorageLive(_3);
7877
StorageLive(_4);
7978
- _4 = ();
@@ -134,12 +133,10 @@
134133
StorageDead(_4);
135134
_2 = &_3;
136135
_1 = &(*_2);
137-
- StorageDead(_2);
136+
StorageDead(_2);
138137
- StorageLive(_5);
139-
- _10 = copy (*_1);
140-
+ nop;
141138
+ nop;
142-
+ _10 = copy (*_2);
139+
_10 = copy (*_1);
143140
_11 = copy ((_10.0: std::ptr::Unique<()>).0: std::ptr::NonNull<()>) as *const () (Transmute);
144141
_5 = &raw const (*_11);
145142
- 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
@@ -72,8 +72,7 @@
7272

7373
bb0: {
7474
StorageLive(_1);
75-
- StorageLive(_2);
76-
+ nop;
75+
StorageLive(_2);
7776
StorageLive(_3);
7877
StorageLive(_4);
7978
- _4 = ();
@@ -134,12 +133,10 @@
134133
StorageDead(_4);
135134
_2 = &_3;
136135
_1 = &(*_2);
137-
- StorageDead(_2);
136+
StorageDead(_2);
138137
- StorageLive(_5);
139-
- _10 = copy (*_1);
140-
+ nop;
141138
+ nop;
142-
+ _10 = copy (*_2);
139+
_10 = copy (*_1);
143140
_11 = copy ((_10.0: std::ptr::Unique<()>).0: std::ptr::NonNull<()>) as *const () (Transmute);
144141
_5 = &raw const (*_11);
145142
- StorageLive(_6);

0 commit comments

Comments
 (0)