@@ -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 ) ]
168176enum 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 ,
@@ -307,16 +318,38 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
307318
308319 /// Create a new `Value::Address` distinct from all the others.
309320 #[ instrument( level = "trace" , skip( self ) , ret) ]
310- fn new_pointer ( & mut self , place : Place < ' tcx > , kind : AddressKind ) -> VnIndex {
321+ fn new_pointer (
322+ & mut self ,
323+ place : Place < ' tcx > ,
324+ kind : AddressKind ,
325+ location : Location ,
326+ ) -> Option < VnIndex > {
311327 let pty = place. ty ( self . local_decls , self . tcx ) . ty ;
312328 let ty = match kind {
313329 AddressKind :: Ref ( bk) => {
314330 Ty :: new_ref ( self . tcx , self . tcx . lifetimes . re_erased , pty, bk. to_mutbl_lossy ( ) )
315331 }
316332 AddressKind :: Address ( mutbl) => Ty :: new_ptr ( self . tcx , pty, mutbl. to_mutbl_lossy ( ) ) ,
317333 } ;
318- let value = Value :: Address { place, kind, provenance : self . next_opaque ( ) } ;
319- self . insert ( ty, value)
334+
335+ let mut projection = place. projection . iter ( ) ;
336+ let base = if place. is_indirect_first_projection ( ) {
337+ let base = self . locals [ place. local ] ?;
338+ // Skip the initial `Deref`.
339+ projection. next ( ) ;
340+ AddressBase :: Deref ( base)
341+ } else {
342+ AddressBase :: Local ( place. local )
343+ } ;
344+ // Do not try evaluating inside `Index`, this has been done by `simplify_place_value`.
345+ let projection = self
346+ . arena
347+ . try_alloc_from_iter (
348+ projection. map ( |proj| proj. try_map ( |value| self . locals [ value] , |ty| ty) . ok_or ( ( ) ) ) ,
349+ )
350+ . ok ( ) ?;
351+ let value = Value :: Address { base, projection, kind, provenance : self . next_opaque ( ) } ;
352+ Some ( self . insert ( ty, value) )
320353 }
321354
322355 #[ inline]
@@ -457,14 +490,15 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
457490 let elem = elem. try_map ( |_| None , |( ) | ty. ty ) ?;
458491 self . ecx . project ( base, elem) . discard_err ( ) ?
459492 }
460- Address { place, kind : _, provenance : _ } => {
461- if !place. is_indirect_first_projection ( ) {
462- return None ;
463- }
464- let local = self . locals [ place. local ] ?;
465- let pointer = self . evaluated [ local] . as_ref ( ) ?;
493+ Address { base, projection, .. } => {
494+ debug_assert ! ( !projection. contains( & ProjectionElem :: Deref ) ) ;
495+ let pointer = match base {
496+ AddressBase :: Deref ( pointer) => self . evaluated [ pointer] . as_ref ( ) ?,
497+ // We have no stack to point to.
498+ AddressBase :: Local ( _) => return None ,
499+ } ;
466500 let mut mplace = self . ecx . deref_pointer ( pointer) . discard_err ( ) ?;
467- for elem in place . projection . iter ( ) . skip ( 1 ) {
501+ for elem in projection {
468502 // `Index` by constants should have been replaced by `ConstantIndex` by
469503 // `simplify_place_projection`.
470504 let elem = elem. try_map ( |_| None , |ty| ty) ?;
@@ -583,19 +617,51 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
583617 Some ( op)
584618 }
585619
620+ /// Represent the *value* we obtain by dereferencing an `Address` value.
621+ #[ instrument( level = "trace" , skip( self ) , ret) ]
622+ fn dereference_address (
623+ & mut self ,
624+ base : AddressBase ,
625+ projection : & [ ProjectionElem < VnIndex , Ty < ' tcx > > ] ,
626+ ) -> Option < VnIndex > {
627+ let ( mut place_ty, mut value) = match base {
628+ // The base is a local, so we take the local's value and project from it.
629+ AddressBase :: Local ( local) => {
630+ let local = self . locals [ local] ?;
631+ let place_ty = PlaceTy :: from_ty ( self . ty ( local) ) ;
632+ ( place_ty, local)
633+ }
634+ // The base is a pointer's deref, so we introduce the implicit deref.
635+ AddressBase :: Deref ( reborrow) => {
636+ let place_ty = PlaceTy :: from_ty ( self . ty ( reborrow) ) ;
637+ self . project ( place_ty, reborrow, ProjectionElem :: Deref ) ?
638+ }
639+ } ;
640+ for & proj in projection {
641+ ( place_ty, value) = self . project ( place_ty, value, proj) ?;
642+ }
643+ Some ( value)
644+ }
645+
646+ #[ instrument( level = "trace" , skip( self ) , ret) ]
586647 fn project (
587648 & mut self ,
588649 place_ty : PlaceTy < ' tcx > ,
589650 value : VnIndex ,
590- proj : PlaceElem < ' tcx > ,
591- from_non_ssa_index : & mut bool ,
651+ proj : ProjectionElem < VnIndex , Ty < ' tcx > > ,
592652 ) -> Option < ( PlaceTy < ' tcx > , VnIndex ) > {
593653 let projection_ty = place_ty. projection_ty ( self . tcx , proj) ;
594654 let proj = match proj {
595655 ProjectionElem :: Deref => {
596656 if let Some ( Mutability :: Not ) = place_ty. ty . ref_mutability ( )
597657 && projection_ty. ty . is_freeze ( self . tcx , self . typing_env ( ) )
598658 {
659+ if let Value :: Address { base, projection, .. } = self . get ( value)
660+ && let Some ( value) = self . dereference_address ( base, projection)
661+ {
662+ return Some ( ( projection_ty, value) ) ;
663+ }
664+
599665 // An immutable borrow `_x` always points to the same value for the
600666 // lifetime of the borrow, so we can merge all instances of `*_x`.
601667 return Some ( ( projection_ty, self . insert_deref ( projection_ty. ty , value) ) ) ;
@@ -632,10 +698,8 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
632698 }
633699 ProjectionElem :: Index ( idx) => {
634700 if let Value :: Repeat ( inner, _) = self . get ( value) {
635- * from_non_ssa_index |= self . locals [ idx] . is_none ( ) ;
636701 return Some ( ( projection_ty, inner) ) ;
637702 }
638- let idx = self . locals [ idx] ?;
639703 ProjectionElem :: Index ( idx)
640704 }
641705 ProjectionElem :: ConstantIndex { offset, min_length, from_end } => {
@@ -714,74 +778,71 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
714778 /// Represent the *value* which would be read from `place`, and point `place` to a preexisting
715779 /// place with the same value (if that already exists).
716780 #[ instrument( level = "trace" , skip( self ) , ret) ]
717- fn simplify_place_value (
781+ fn compute_place_value (
718782 & mut self ,
719- place : & mut Place < ' tcx > ,
783+ place : Place < ' tcx > ,
720784 location : Location ,
721- ) -> Option < VnIndex > {
722- self . simplify_place_projection ( place, location) ;
723-
785+ ) -> Result < VnIndex , PlaceRef < ' tcx > > {
724786 // Invariant: `place` and `place_ref` point to the same value, even if they point to
725787 // different memory locations.
726788 let mut place_ref = place. as_ref ( ) ;
727789
728790 // Invariant: `value` holds the value up-to the `index`th projection excluded.
729- let mut value = self . locals [ place. local ] ? ;
791+ let Some ( mut value) = self . locals [ place. local ] else { return Err ( place_ref ) } ;
730792 // Invariant: `value` has type `place_ty`, with optional downcast variant if needed.
731793 let mut place_ty = PlaceTy :: from_ty ( self . local_decls [ place. local ] . ty ) ;
732- let mut from_non_ssa_index = false ;
733794 for ( index, proj) in place. projection . iter ( ) . enumerate ( ) {
734- if let Value :: Projection ( pointer, ProjectionElem :: Deref ) = self . get ( value)
735- && let Value :: Address { place : mut pointee, kind, .. } = self . get ( pointer)
736- && let AddressKind :: Ref ( BorrowKind :: Shared ) = kind
737- && let Some ( v) = self . simplify_place_value ( & mut pointee, location)
738- {
739- value = v;
740- // `pointee` holds a `Place`, so `ProjectionElem::Index` holds a `Local`.
741- // That local is SSA, but we otherwise have no guarantee on that local's value at
742- // the current location compared to its value where `pointee` was borrowed.
743- if pointee. projection . iter ( ) . all ( |elem| !matches ! ( elem, ProjectionElem :: Index ( _) ) ) {
744- place_ref =
745- pointee. project_deeper ( & place. projection [ index..] , self . tcx ) . as_ref ( ) ;
746- }
747- }
748795 if let Some ( local) = self . try_as_local ( value, location) {
749796 // Both `local` and `Place { local: place.local, projection: projection[..index] }`
750797 // hold the same value. Therefore, following place holds the value in the original
751798 // `place`.
752799 place_ref = PlaceRef { local, projection : & place. projection [ index..] } ;
753800 }
754801
755- ( place_ty, value) = self . project ( place_ty, value, proj, & mut from_non_ssa_index) ?;
802+ let Some ( proj) = proj. try_map ( |value| self . locals [ value] , |ty| ty) else {
803+ return Err ( place_ref) ;
804+ } ;
805+ let Some ( ty_and_value) = self . project ( place_ty, value, proj) else {
806+ return Err ( place_ref) ;
807+ } ;
808+ ( place_ty, value) = ty_and_value;
756809 }
757810
758- if let Value :: Projection ( pointer, ProjectionElem :: Deref ) = self . get ( value)
759- && let Value :: Address { place : mut pointee, kind, .. } = self . get ( pointer)
760- && let AddressKind :: Ref ( BorrowKind :: Shared ) = kind
761- && let Some ( v) = self . simplify_place_value ( & mut pointee, location)
762- {
763- value = v;
764- // `pointee` holds a `Place`, so `ProjectionElem::Index` holds a `Local`.
765- // That local is SSA, but we otherwise have no guarantee on that local's value at
766- // the current location compared to its value where `pointee` was borrowed.
767- if pointee. projection . iter ( ) . all ( |elem| !matches ! ( elem, ProjectionElem :: Index ( _) ) ) {
768- place_ref = pointee. project_deeper ( & [ ] , self . tcx ) . as_ref ( ) ;
769- }
770- }
771- if let Some ( new_local) = self . try_as_local ( value, location) {
772- place_ref = PlaceRef { local : new_local, projection : & [ ] } ;
773- } else if from_non_ssa_index {
774- // If access to non-SSA locals is unavoidable, bail out.
775- return None ;
776- }
811+ Ok ( value)
812+ }
777813
778- if place_ref. local != place. local || place_ref. projection . len ( ) < place. projection . len ( ) {
779- // By the invariant on `place_ref`.
780- * place = place_ref. project_deeper ( & [ ] , self . tcx ) ;
781- self . reused_locals . insert ( place_ref. local ) ;
782- }
814+ /// Represent the *value* which would be read from `place`, and point `place` to a preexisting
815+ /// place with the same value (if that already exists).
816+ #[ instrument( level = "trace" , skip( self ) , ret) ]
817+ fn simplify_place_value (
818+ & mut self ,
819+ place : & mut Place < ' tcx > ,
820+ location : Location ,
821+ ) -> Option < VnIndex > {
822+ self . simplify_place_projection ( place, location) ;
783823
784- Some ( value)
824+ match self . compute_place_value ( * place, location) {
825+ Ok ( value) => {
826+ if let Some ( new_place) = self . try_as_place ( value, location, true )
827+ && ( new_place. local != place. local
828+ || new_place. projection . len ( ) < place. projection . len ( ) )
829+ {
830+ * place = new_place;
831+ self . reused_locals . insert ( new_place. local ) ;
832+ }
833+ Some ( value)
834+ }
835+ Err ( place_ref) => {
836+ if place_ref. local != place. local
837+ || place_ref. projection . len ( ) < place. projection . len ( )
838+ {
839+ // By the invariant on `place_ref`.
840+ * place = place_ref. project_deeper ( & [ ] , self . tcx ) ;
841+ self . reused_locals . insert ( place_ref. local ) ;
842+ }
843+ None
844+ }
845+ }
785846 }
786847
787848 #[ instrument( level = "trace" , skip( self ) , ret) ]
@@ -828,11 +889,11 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
828889 Rvalue :: Aggregate ( ..) => return self . simplify_aggregate ( lhs, rvalue, location) ,
829890 Rvalue :: Ref ( _, borrow_kind, ref mut place) => {
830891 self . simplify_place_projection ( place, location) ;
831- return Some ( self . new_pointer ( * place, AddressKind :: Ref ( borrow_kind) ) ) ;
892+ return self . new_pointer ( * place, AddressKind :: Ref ( borrow_kind) , location ) ;
832893 }
833894 Rvalue :: RawPtr ( mutbl, ref mut place) => {
834895 self . simplify_place_projection ( place, location) ;
835- return Some ( self . new_pointer ( * place, AddressKind :: Address ( mutbl) ) ) ;
896+ return self . new_pointer ( * place, AddressKind :: Address ( mutbl) , location ) ;
836897 }
837898 Rvalue :: WrapUnsafeBinder ( ref mut op, _) => {
838899 let value = self . simplify_operand ( op, location) ?;
@@ -1072,12 +1133,10 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
10721133 }
10731134
10741135 // `&mut *p`, `&raw *p`, etc don't change metadata.
1075- Value :: Address { place, kind : _, provenance : _ }
1076- if let PlaceRef { local, projection : [ PlaceElem :: Deref ] } =
1077- place. as_ref ( )
1078- && let Some ( local_index) = self . locals [ local] =>
1136+ Value :: Address { base : AddressBase :: Deref ( reborrowed) , projection, .. }
1137+ if projection. is_empty ( ) =>
10791138 {
1080- local_index
1139+ reborrowed
10811140 }
10821141
10831142 _ => break ,
0 commit comments