@@ -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 ) ]
190198enum 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 ,
0 commit comments