Skip to content

Commit 3d96e54

Browse files
committed
Use a VnIndex in Address projection.
1 parent 1180278 commit 3d96e54

File tree

7 files changed

+171
-79
lines changed

7 files changed

+171
-79
lines changed

compiler/rustc_mir_transform/src/gvn.rs

Lines changed: 129 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,14 @@ enum AddressKind {
186186
Address(RawPtrKind),
187187
}
188188

189+
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
190+
enum AddressBase {
191+
/// This address is based on this local.
192+
Local(Local),
193+
/// This address is based on the deref of this pointer.
194+
Deref(VnIndex),
195+
}
196+
189197
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
190198
enum Value<'a, 'tcx> {
191199
// Root values.
@@ -216,7 +224,10 @@ enum Value<'a, 'tcx> {
216224
Repeat(VnIndex, ty::Const<'tcx>),
217225
/// The address of a place.
218226
Address {
219-
place: Place<'tcx>,
227+
base: AddressBase,
228+
// We do not use a plain `Place` as we want to be able to reason about indices.
229+
// This does not contain any `Deref` projection.
230+
projection: &'a [ProjectionElem<VnIndex, Ty<'tcx>>],
220231
kind: AddressKind,
221232
/// Give each borrow and pointer a different provenance, so we don't merge them.
222233
provenance: VnOpaque,
@@ -426,22 +437,42 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
426437

427438
/// Create a new `Value::Address` distinct from all the others.
428439
#[instrument(level = "trace", skip(self), ret)]
429-
fn new_pointer(&mut self, place: Place<'tcx>, kind: AddressKind) -> VnIndex {
440+
fn new_pointer(&mut self, place: Place<'tcx>, kind: AddressKind) -> Option<VnIndex> {
430441
let pty = place.ty(self.local_decls, self.tcx).ty;
431442
let ty = match kind {
432443
AddressKind::Ref(bk) => {
433444
Ty::new_ref(self.tcx, self.tcx.lifetimes.re_erased, pty, bk.to_mutbl_lossy())
434445
}
435446
AddressKind::Address(mutbl) => Ty::new_ptr(self.tcx, pty, mutbl.to_mutbl_lossy()),
436447
};
437-
let index =
438-
self.values.insert_unique(ty, |provenance| Value::Address { place, kind, provenance });
448+
449+
let mut projection = place.projection.iter();
450+
let base = if place.is_indirect_first_projection() {
451+
let base = self.locals[place.local]?;
452+
// Skip the initial `Deref`.
453+
projection.next();
454+
AddressBase::Deref(base)
455+
} else {
456+
AddressBase::Local(place.local)
457+
};
458+
// Do not try evaluating inside `Index`, this has been done by `simplify_place_projection`.
459+
let projection =
460+
projection.map(|proj| proj.try_map(|index| self.locals[index], |ty| ty).ok_or(()));
461+
let projection = self.arena.try_alloc_from_iter(projection).ok()?;
462+
463+
let index = self.values.insert_unique(ty, |provenance| Value::Address {
464+
base,
465+
projection,
466+
kind,
467+
provenance,
468+
});
439469
let evaluated = self.eval_to_const(index);
440470
let _index = self.evaluated.push(evaluated);
441471
debug_assert_eq!(index, _index);
442472
let _index = self.rev_locals.push(SmallVec::new());
443473
debug_assert_eq!(index, _index);
444-
index
474+
475+
Some(index)
445476
}
446477

447478
#[instrument(level = "trace", skip(self), ret)]
@@ -591,14 +622,15 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
591622
let elem = elem.try_map(|_| None, |()| ty.ty)?;
592623
self.ecx.project(base, elem).discard_err()?
593624
}
594-
Address { place, kind: _, provenance: _ } => {
595-
if !place.is_indirect_first_projection() {
596-
return None;
597-
}
598-
let local = self.locals[place.local]?;
599-
let pointer = self.evaluated[local].as_ref()?;
625+
Address { base, projection, .. } => {
626+
debug_assert!(!projection.contains(&ProjectionElem::Deref));
627+
let pointer = match base {
628+
AddressBase::Deref(pointer) => self.evaluated[pointer].as_ref()?,
629+
// We have no stack to point to.
630+
AddressBase::Local(_) => return None,
631+
};
600632
let mut mplace = self.ecx.deref_pointer(pointer).discard_err()?;
601-
for elem in place.projection.iter().skip(1) {
633+
for elem in projection {
602634
// `Index` by constants should have been replaced by `ConstantIndex` by
603635
// `simplify_place_projection`.
604636
let elem = elem.try_map(|_| None, |ty| ty)?;
@@ -717,19 +749,51 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
717749
Some(op)
718750
}
719751

752+
/// Represent the *value* we obtain by dereferencing an `Address` value.
753+
#[instrument(level = "trace", skip(self), ret)]
754+
fn dereference_address(
755+
&mut self,
756+
base: AddressBase,
757+
projection: &[ProjectionElem<VnIndex, Ty<'tcx>>],
758+
) -> Option<VnIndex> {
759+
let (mut place_ty, mut value) = match base {
760+
// The base is a local, so we take the local's value and project from it.
761+
AddressBase::Local(local) => {
762+
let local = self.locals[local]?;
763+
let place_ty = PlaceTy::from_ty(self.ty(local));
764+
(place_ty, local)
765+
}
766+
// The base is a pointer's deref, so we introduce the implicit deref.
767+
AddressBase::Deref(reborrow) => {
768+
let place_ty = PlaceTy::from_ty(self.ty(reborrow));
769+
self.project(place_ty, reborrow, ProjectionElem::Deref)?
770+
}
771+
};
772+
for &proj in projection {
773+
(place_ty, value) = self.project(place_ty, value, proj)?;
774+
}
775+
Some(value)
776+
}
777+
778+
#[instrument(level = "trace", skip(self), ret)]
720779
fn project(
721780
&mut self,
722781
place_ty: PlaceTy<'tcx>,
723782
value: VnIndex,
724-
proj: PlaceElem<'tcx>,
725-
from_non_ssa_index: &mut bool,
783+
proj: ProjectionElem<VnIndex, Ty<'tcx>>,
726784
) -> Option<(PlaceTy<'tcx>, VnIndex)> {
727785
let projection_ty = place_ty.projection_ty(self.tcx, proj);
728786
let proj = match proj {
729787
ProjectionElem::Deref => {
730788
if let Some(Mutability::Not) = place_ty.ty.ref_mutability()
731789
&& projection_ty.ty.is_freeze(self.tcx, self.typing_env())
732790
{
791+
if let Value::Address { base, projection, .. } = self.get(value)
792+
&& let Some(value) = self.dereference_address(base, projection)
793+
{
794+
return Some((projection_ty, value));
795+
}
796+
733797
// An immutable borrow `_x` always points to the same value for the
734798
// lifetime of the borrow, so we can merge all instances of `*_x`.
735799
return Some((projection_ty, self.insert_deref(projection_ty.ty, value)));
@@ -766,10 +830,8 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
766830
}
767831
ProjectionElem::Index(idx) => {
768832
if let Value::Repeat(inner, _) = self.get(value) {
769-
*from_non_ssa_index |= self.locals[idx].is_none();
770833
return Some((projection_ty, inner));
771834
}
772-
let idx = self.locals[idx]?;
773835
ProjectionElem::Index(idx)
774836
}
775837
ProjectionElem::ConstantIndex { offset, min_length, from_end } => {
@@ -844,77 +906,74 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
844906
trace!(?place);
845907
}
846908

847-
/// Represent the *value* which would be read from `place`, and point `place` to a preexisting
848-
/// place with the same value (if that already exists).
909+
/// Represent the *value* which would be read from `place`. If we succeed, return it.
910+
/// If we fail, return a `PlaceRef` that contains the same value.
849911
#[instrument(level = "trace", skip(self), ret)]
850-
fn simplify_place_value(
912+
fn compute_place_value(
851913
&mut self,
852-
place: &mut Place<'tcx>,
914+
place: Place<'tcx>,
853915
location: Location,
854-
) -> Option<VnIndex> {
855-
self.simplify_place_projection(place, location);
856-
916+
) -> Result<VnIndex, PlaceRef<'tcx>> {
857917
// Invariant: `place` and `place_ref` point to the same value, even if they point to
858918
// different memory locations.
859919
let mut place_ref = place.as_ref();
860920

861921
// Invariant: `value` holds the value up-to the `index`th projection excluded.
862-
let mut value = self.locals[place.local]?;
922+
let Some(mut value) = self.locals[place.local] else { return Err(place_ref) };
863923
// Invariant: `value` has type `place_ty`, with optional downcast variant if needed.
864924
let mut place_ty = PlaceTy::from_ty(self.local_decls[place.local].ty);
865-
let mut from_non_ssa_index = false;
866925
for (index, proj) in place.projection.iter().enumerate() {
867-
if let Value::Projection(pointer, ProjectionElem::Deref) = self.get(value)
868-
&& let Value::Address { place: mut pointee, kind, .. } = self.get(pointer)
869-
&& let AddressKind::Ref(BorrowKind::Shared) = kind
870-
&& let Some(v) = self.simplify_place_value(&mut pointee, location)
871-
{
872-
value = v;
873-
// `pointee` holds a `Place`, so `ProjectionElem::Index` holds a `Local`.
874-
// That local is SSA, but we otherwise have no guarantee on that local's value at
875-
// the current location compared to its value where `pointee` was borrowed.
876-
if pointee.projection.iter().all(|elem| !matches!(elem, ProjectionElem::Index(_))) {
877-
place_ref =
878-
pointee.project_deeper(&place.projection[index..], self.tcx).as_ref();
879-
}
880-
}
881926
if let Some(local) = self.try_as_local(value, location) {
882927
// Both `local` and `Place { local: place.local, projection: projection[..index] }`
883928
// hold the same value. Therefore, following place holds the value in the original
884929
// `place`.
885930
place_ref = PlaceRef { local, projection: &place.projection[index..] };
886931
}
887932

888-
(place_ty, value) = self.project(place_ty, value, proj, &mut from_non_ssa_index)?;
933+
let Some(proj) = proj.try_map(|value| self.locals[value], |ty| ty) else {
934+
return Err(place_ref);
935+
};
936+
let Some(ty_and_value) = self.project(place_ty, value, proj) else {
937+
return Err(place_ref);
938+
};
939+
(place_ty, value) = ty_and_value;
889940
}
890941

891-
if let Value::Projection(pointer, ProjectionElem::Deref) = self.get(value)
892-
&& let Value::Address { place: mut pointee, kind, .. } = self.get(pointer)
893-
&& let AddressKind::Ref(BorrowKind::Shared) = kind
894-
&& let Some(v) = self.simplify_place_value(&mut pointee, location)
895-
{
896-
value = v;
897-
// `pointee` holds a `Place`, so `ProjectionElem::Index` holds a `Local`.
898-
// That local is SSA, but we otherwise have no guarantee on that local's value at
899-
// the current location compared to its value where `pointee` was borrowed.
900-
if pointee.projection.iter().all(|elem| !matches!(elem, ProjectionElem::Index(_))) {
901-
place_ref = pointee.project_deeper(&[], self.tcx).as_ref();
902-
}
903-
}
904-
if let Some(new_local) = self.try_as_local(value, location) {
905-
place_ref = PlaceRef { local: new_local, projection: &[] };
906-
} else if from_non_ssa_index {
907-
// If access to non-SSA locals is unavoidable, bail out.
908-
return None;
909-
}
942+
Ok(value)
943+
}
910944

911-
if place_ref.local != place.local || place_ref.projection.len() < place.projection.len() {
912-
// By the invariant on `place_ref`.
913-
*place = place_ref.project_deeper(&[], self.tcx);
914-
self.reused_locals.insert(place_ref.local);
915-
}
945+
/// Represent the *value* which would be read from `place`, and point `place` to a preexisting
946+
/// place with the same value (if that already exists).
947+
#[instrument(level = "trace", skip(self), ret)]
948+
fn simplify_place_value(
949+
&mut self,
950+
place: &mut Place<'tcx>,
951+
location: Location,
952+
) -> Option<VnIndex> {
953+
self.simplify_place_projection(place, location);
916954

917-
Some(value)
955+
match self.compute_place_value(*place, location) {
956+
Ok(value) => {
957+
if let Some(new_place) = self.try_as_place(value, location, true)
958+
&& (new_place.local != place.local
959+
|| new_place.projection.len() < place.projection.len())
960+
{
961+
*place = new_place;
962+
self.reused_locals.insert(new_place.local);
963+
}
964+
Some(value)
965+
}
966+
Err(place_ref) => {
967+
if place_ref.local != place.local
968+
|| place_ref.projection.len() < place.projection.len()
969+
{
970+
// By the invariant on `place_ref`.
971+
*place = place_ref.project_deeper(&[], self.tcx);
972+
self.reused_locals.insert(place_ref.local);
973+
}
974+
None
975+
}
976+
}
918977
}
919978

920979
#[instrument(level = "trace", skip(self), ret)]
@@ -961,11 +1020,11 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
9611020
Rvalue::Aggregate(..) => return self.simplify_aggregate(lhs, rvalue, location),
9621021
Rvalue::Ref(_, borrow_kind, ref mut place) => {
9631022
self.simplify_place_projection(place, location);
964-
return Some(self.new_pointer(*place, AddressKind::Ref(borrow_kind)));
1023+
return self.new_pointer(*place, AddressKind::Ref(borrow_kind));
9651024
}
9661025
Rvalue::RawPtr(mutbl, ref mut place) => {
9671026
self.simplify_place_projection(place, location);
968-
return Some(self.new_pointer(*place, AddressKind::Address(mutbl)));
1027+
return self.new_pointer(*place, AddressKind::Address(mutbl));
9691028
}
9701029
Rvalue::WrapUnsafeBinder(ref mut op, _) => {
9711030
let value = self.simplify_operand(op, location)?;
@@ -1205,12 +1264,10 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
12051264
}
12061265

12071266
// `&mut *p`, `&raw *p`, etc don't change metadata.
1208-
Value::Address { place, kind: _, provenance: _ }
1209-
if let PlaceRef { local, projection: [PlaceElem::Deref] } =
1210-
place.as_ref()
1211-
&& let Some(local_index) = self.locals[local] =>
1267+
Value::Address { base: AddressBase::Deref(reborrowed), projection, .. }
1268+
if projection.is_empty() =>
12121269
{
1213-
local_index
1270+
reborrowed
12141271
}
12151272

12161273
_ => break,

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,8 @@
4343
+ nop;
4444
StorageLive(_8);
4545
StorageLive(_9);
46-
_9 = copy (*_3);
46+
- _9 = copy (*_3);
47+
+ _9 = copy _1[_4];
4748
_8 = opaque::<u8>(move _9) -> [return: bb2, unwind unreachable];
4849
}
4950

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,8 @@
4343
+ nop;
4444
StorageLive(_8);
4545
StorageLive(_9);
46-
_9 = copy (*_3);
46+
- _9 = copy (*_3);
47+
+ _9 = copy _1[_4];
4748
_8 = opaque::<u8>(move _9) -> [return: bb2, unwind continue];
4849
}
4950

tests/mir-opt/gvn.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1065,8 +1065,8 @@ fn dereference_indexing(array: [u8; 2], index: usize) {
10651065
&array[i]
10661066
};
10671067

1068-
// CHECK-NOT: [{{.*}}]
1069-
// CHECK: [[tmp:_.*]] = copy (*[[a]]);
1068+
// CHECK-NOT: StorageDead([[i]]);
1069+
// CHECK: [[tmp:_.*]] = copy _1[[[i]]];
10701070
// CHECK: opaque::<u8>(move [[tmp]])
10711071
opaque(*a);
10721072
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
- // MIR for `index_place` before GVN
2+
+ // MIR for `index_place` after GVN
3+
4+
fn index_place(_1: usize, _2: usize, _3: [i32; 5]) -> i32 {
5+
let mut _0: i32;
6+
let mut _4: &i32;
7+
8+
bb0: {
9+
_4 = &_3[_1];
10+
_1 = copy _2;
11+
_0 = copy (*_4);
12+
return;
13+
}
14+
}
15+

tests/mir-opt/gvn_repeat.repeat_local.GVN.diff

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,7 @@
1010
_4 = [copy _3; 5];
1111
_5 = &_4[_1];
1212
_1 = copy _2;
13-
- _0 = copy (*_5);
14-
+ _0 = copy _3;
13+
_0 = copy (*_5);
1514
return;
1615
}
1716
}

0 commit comments

Comments
 (0)