Skip to content

Commit d7a4b0a

Browse files
committed
Use a VnIndex in Address projection.
1 parent c08d467 commit d7a4b0a

File tree

7 files changed

+173
-79
lines changed

7 files changed

+173
-79
lines changed

compiler/rustc_mir_transform/src/gvn.rs

Lines changed: 131 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,14 @@ enum AddressKind {
164164
Address(RawPtrKind),
165165
}
166166

167+
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
168+
enum AddressBase {
169+
/// This address is based on this local.
170+
Local(Local),
171+
/// This address is based on the deref of this pointer.
172+
Deref(VnIndex),
173+
}
174+
167175
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
168176
enum Value<'a, 'tcx> {
169177
// Root values.
@@ -192,7 +200,10 @@ enum Value<'a, 'tcx> {
192200
Repeat(VnIndex, ty::Const<'tcx>),
193201
/// The address of a place.
194202
Address {
195-
place: Place<'tcx>,
203+
base: AddressBase,
204+
// We do not use a plain `Place` as we want to be able to reason about indices.
205+
// This does not contain any `Deref` projection.
206+
projection: &'a [ProjectionElem<VnIndex, Ty<'tcx>>],
196207
kind: AddressKind,
197208
/// Give each borrow and pointer a different provenance, so we don't merge them.
198209
provenance: usize,
@@ -309,16 +320,38 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
309320

310321
/// Create a new `Value::Address` distinct from all the others.
311322
#[instrument(level = "trace", skip(self), ret)]
312-
fn new_pointer(&mut self, place: Place<'tcx>, kind: AddressKind) -> VnIndex {
323+
fn new_pointer(
324+
&mut self,
325+
place: Place<'tcx>,
326+
kind: AddressKind,
327+
location: Location,
328+
) -> Option<VnIndex> {
313329
let pty = place.ty(self.local_decls, self.tcx).ty;
314330
let ty = match kind {
315331
AddressKind::Ref(bk) => {
316332
Ty::new_ref(self.tcx, self.tcx.lifetimes.re_erased, pty, bk.to_mutbl_lossy())
317333
}
318334
AddressKind::Address(mutbl) => Ty::new_ptr(self.tcx, pty, mutbl.to_mutbl_lossy()),
319335
};
320-
let value = Value::Address { place, kind, provenance: self.next_opaque() };
321-
self.insert(ty, value)
336+
337+
let mut projection = place.projection.iter();
338+
let base = if place.is_indirect_first_projection() {
339+
let base = self.locals[place.local]?;
340+
// Skip the initial `Deref`.
341+
projection.next();
342+
AddressBase::Deref(base)
343+
} else {
344+
AddressBase::Local(place.local)
345+
};
346+
// Do not try evaluating inside `Index`, this has been done by `simplify_place_value`.
347+
let projection = self
348+
.arena
349+
.try_alloc_from_iter(
350+
projection.map(|proj| proj.try_map(|value| self.locals[value], |ty| ty).ok_or(())),
351+
)
352+
.ok()?;
353+
let value = Value::Address { base, projection, kind, provenance: self.next_opaque() };
354+
Some(self.insert(ty, value))
322355
}
323356

324357
#[inline]
@@ -459,14 +492,15 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
459492
let elem = elem.try_map(|_| None, |()| ty.ty)?;
460493
self.ecx.project(base, elem).discard_err()?
461494
}
462-
Address { place, kind: _, provenance: _ } => {
463-
if !place.is_indirect_first_projection() {
464-
return None;
465-
}
466-
let local = self.locals[place.local]?;
467-
let pointer = self.evaluated[local].as_ref()?;
495+
Address { base, projection, .. } => {
496+
debug_assert!(!projection.contains(&ProjectionElem::Deref));
497+
let pointer = match base {
498+
AddressBase::Deref(pointer) => self.evaluated[pointer].as_ref()?,
499+
// We have no stack to point to.
500+
AddressBase::Local(_) => return None,
501+
};
468502
let mut mplace = self.ecx.deref_pointer(pointer).discard_err()?;
469-
for elem in place.projection.iter().skip(1) {
503+
for elem in projection {
470504
// `Index` by constants should have been replaced by `ConstantIndex` by
471505
// `simplify_place_projection`.
472506
let elem = elem.try_map(|_| None, |ty| ty)?;
@@ -590,19 +624,51 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
590624
Some(op)
591625
}
592626

627+
/// Represent the *value* we obtain by dereferencing an `Address` value.
628+
#[instrument(level = "trace", skip(self), ret)]
629+
fn dereference_address(
630+
&mut self,
631+
base: AddressBase,
632+
projection: &[ProjectionElem<VnIndex, Ty<'tcx>>],
633+
) -> Option<VnIndex> {
634+
let (mut place_ty, mut value) = match base {
635+
// The base is a local, so we take the local's value and project from it.
636+
AddressBase::Local(local) => {
637+
let local = self.locals[local]?;
638+
let place_ty = PlaceTy::from_ty(self.ty(local));
639+
(place_ty, local)
640+
}
641+
// The base is a pointer's deref, so we introduce the implicit deref.
642+
AddressBase::Deref(reborrow) => {
643+
let place_ty = PlaceTy::from_ty(self.ty(reborrow));
644+
self.project(place_ty, reborrow, ProjectionElem::Deref)?
645+
}
646+
};
647+
for &proj in projection {
648+
(place_ty, value) = self.project(place_ty, value, proj)?;
649+
}
650+
Some(value)
651+
}
652+
653+
#[instrument(level = "trace", skip(self), ret)]
593654
fn project(
594655
&mut self,
595656
place_ty: PlaceTy<'tcx>,
596657
value: VnIndex,
597-
proj: PlaceElem<'tcx>,
598-
from_non_ssa_index: &mut bool,
658+
proj: ProjectionElem<VnIndex, Ty<'tcx>>,
599659
) -> Option<(PlaceTy<'tcx>, VnIndex)> {
600660
let projection_ty = place_ty.projection_ty(self.tcx, proj);
601661
let proj = match proj {
602662
ProjectionElem::Deref => {
603663
if let Some(Mutability::Not) = place_ty.ty.ref_mutability()
604664
&& projection_ty.ty.is_freeze(self.tcx, self.typing_env())
605665
{
666+
if let Value::Address { base, projection, .. } = self.get(value)
667+
&& let Some(value) = self.dereference_address(base, projection)
668+
{
669+
return Some((projection_ty, value));
670+
}
671+
606672
// An immutable borrow `_x` always points to the same value for the
607673
// lifetime of the borrow, so we can merge all instances of `*_x`.
608674
return Some((projection_ty, self.insert_deref(projection_ty.ty, value)));
@@ -639,10 +705,8 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
639705
}
640706
ProjectionElem::Index(idx) => {
641707
if let Value::Repeat(inner, _) = self.get(value) {
642-
*from_non_ssa_index |= self.locals[idx].is_none();
643708
return Some((projection_ty, inner));
644709
}
645-
let idx = self.locals[idx]?;
646710
ProjectionElem::Index(idx)
647711
}
648712
ProjectionElem::ConstantIndex { offset, min_length, from_end } => {
@@ -721,74 +785,71 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
721785
/// Represent the *value* which would be read from `place`, and point `place` to a preexisting
722786
/// place with the same value (if that already exists).
723787
#[instrument(level = "trace", skip(self), ret)]
724-
fn simplify_place_value(
788+
fn compute_place_value(
725789
&mut self,
726-
place: &mut Place<'tcx>,
790+
place: Place<'tcx>,
727791
location: Location,
728-
) -> Option<VnIndex> {
729-
self.simplify_place_projection(place, location);
730-
792+
) -> Result<VnIndex, PlaceRef<'tcx>> {
731793
// Invariant: `place` and `place_ref` point to the same value, even if they point to
732794
// different memory locations.
733795
let mut place_ref = place.as_ref();
734796

735797
// Invariant: `value` holds the value up-to the `index`th projection excluded.
736-
let mut value = self.locals[place.local]?;
798+
let Some(mut value) = self.locals[place.local] else { return Err(place_ref) };
737799
// Invariant: `value` has type `place_ty`, with optional downcast variant if needed.
738800
let mut place_ty = PlaceTy::from_ty(self.local_decls[place.local].ty);
739-
let mut from_non_ssa_index = false;
740801
for (index, proj) in place.projection.iter().enumerate() {
741-
if let Value::Projection(pointer, ProjectionElem::Deref) = self.get(value)
742-
&& let Value::Address { place: mut pointee, kind, .. } = self.get(pointer)
743-
&& let AddressKind::Ref(BorrowKind::Shared) = kind
744-
&& let Some(v) = self.simplify_place_value(&mut pointee, location)
745-
{
746-
value = v;
747-
// `pointee` holds a `Place`, so `ProjectionElem::Index` holds a `Local`.
748-
// That local is SSA, but we otherwise have no guarantee on that local's value at
749-
// the current location compared to its value where `pointee` was borrowed.
750-
if pointee.projection.iter().all(|elem| !matches!(elem, ProjectionElem::Index(_))) {
751-
place_ref =
752-
pointee.project_deeper(&place.projection[index..], self.tcx).as_ref();
753-
}
754-
}
755802
if let Some(local) = self.try_as_local(value, location) {
756803
// Both `local` and `Place { local: place.local, projection: projection[..index] }`
757804
// hold the same value. Therefore, following place holds the value in the original
758805
// `place`.
759806
place_ref = PlaceRef { local, projection: &place.projection[index..] };
760807
}
761808

762-
(place_ty, value) = self.project(place_ty, value, proj, &mut from_non_ssa_index)?;
809+
let Some(proj) = proj.try_map(|value| self.locals[value], |ty| ty) else {
810+
return Err(place_ref);
811+
};
812+
let Some(ty_and_value) = self.project(place_ty, value, proj) else {
813+
return Err(place_ref);
814+
};
815+
(place_ty, value) = ty_and_value;
763816
}
764817

765-
if let Value::Projection(pointer, ProjectionElem::Deref) = self.get(value)
766-
&& let Value::Address { place: mut pointee, kind, .. } = self.get(pointer)
767-
&& let AddressKind::Ref(BorrowKind::Shared) = kind
768-
&& let Some(v) = self.simplify_place_value(&mut pointee, location)
769-
{
770-
value = v;
771-
// `pointee` holds a `Place`, so `ProjectionElem::Index` holds a `Local`.
772-
// That local is SSA, but we otherwise have no guarantee on that local's value at
773-
// the current location compared to its value where `pointee` was borrowed.
774-
if pointee.projection.iter().all(|elem| !matches!(elem, ProjectionElem::Index(_))) {
775-
place_ref = pointee.project_deeper(&[], self.tcx).as_ref();
776-
}
777-
}
778-
if let Some(new_local) = self.try_as_local(value, location) {
779-
place_ref = PlaceRef { local: new_local, projection: &[] };
780-
} else if from_non_ssa_index {
781-
// If access to non-SSA locals is unavoidable, bail out.
782-
return None;
783-
}
818+
Ok(value)
819+
}
784820

785-
if place_ref.local != place.local || place_ref.projection.len() < place.projection.len() {
786-
// By the invariant on `place_ref`.
787-
*place = place_ref.project_deeper(&[], self.tcx);
788-
self.reused_locals.insert(place_ref.local);
789-
}
821+
/// Represent the *value* which would be read from `place`, and point `place` to a preexisting
822+
/// place with the same value (if that already exists).
823+
#[instrument(level = "trace", skip(self), ret)]
824+
fn simplify_place_value(
825+
&mut self,
826+
place: &mut Place<'tcx>,
827+
location: Location,
828+
) -> Option<VnIndex> {
829+
self.simplify_place_projection(place, location);
790830

791-
Some(value)
831+
match self.compute_place_value(*place, location) {
832+
Ok(value) => {
833+
if let Some(new_place) = self.try_as_place(value, location, true)
834+
&& (new_place.local != place.local
835+
|| new_place.projection.len() < place.projection.len())
836+
{
837+
*place = new_place;
838+
self.reused_locals.insert(new_place.local);
839+
}
840+
Some(value)
841+
}
842+
Err(place_ref) => {
843+
if place_ref.local != place.local
844+
|| place_ref.projection.len() < place.projection.len()
845+
{
846+
// By the invariant on `place_ref`.
847+
*place = place_ref.project_deeper(&[], self.tcx);
848+
self.reused_locals.insert(place_ref.local);
849+
}
850+
None
851+
}
852+
}
792853
}
793854

794855
#[instrument(level = "trace", skip(self), ret)]
@@ -835,11 +896,11 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
835896
Rvalue::Aggregate(..) => return self.simplify_aggregate(lhs, rvalue, location),
836897
Rvalue::Ref(_, borrow_kind, ref mut place) => {
837898
self.simplify_place_projection(place, location);
838-
return Some(self.new_pointer(*place, AddressKind::Ref(borrow_kind)));
899+
return self.new_pointer(*place, AddressKind::Ref(borrow_kind), location);
839900
}
840901
Rvalue::RawPtr(mutbl, ref mut place) => {
841902
self.simplify_place_projection(place, location);
842-
return Some(self.new_pointer(*place, AddressKind::Address(mutbl)));
903+
return self.new_pointer(*place, AddressKind::Address(mutbl), location);
843904
}
844905
Rvalue::WrapUnsafeBinder(ref mut op, _) => {
845906
let value = self.simplify_operand(op, location)?;
@@ -1070,12 +1131,10 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
10701131
}
10711132

10721133
// `&mut *p`, `&raw *p`, etc don't change metadata.
1073-
Value::Address { place, kind: _, provenance: _ }
1074-
if let PlaceRef { local, projection: [PlaceElem::Deref] } =
1075-
place.as_ref()
1076-
&& let Some(local_index) = self.locals[local] =>
1134+
Value::Address { base: AddressBase::Deref(reborrowed), projection, .. }
1135+
if projection.is_empty() =>
10771136
{
1078-
arg_index = local_index;
1137+
arg_index = reborrowed;
10791138
was_updated = true;
10801139
continue;
10811140
}
@@ -1421,9 +1480,9 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
14211480

14221481
// The length information is stored in the wide pointer.
14231482
// Reborrowing copies length information from one pointer to the other.
1424-
while let Value::Address { place: borrowed, .. } = self.get(inner)
1425-
&& let [PlaceElem::Deref] = borrowed.projection[..]
1426-
&& let Some(borrowed) = self.locals[borrowed.local]
1483+
while let Value::Address { base: AddressBase::Deref(borrowed), projection, .. } =
1484+
self.get(inner)
1485+
&& projection.is_empty()
14271486
{
14281487
inner = borrowed;
14291488
}

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)