@@ -163,6 +163,14 @@ enum AddressKind {
163163 Address ( RawPtrKind ) ,
164164}
165165
166+ #[ derive( Copy , Clone , Debug , PartialEq , Eq , Hash ) ]
167+ enum AddressBase {
168+ /// This address is based on this local.
169+ Local ( Local ) ,
170+ /// This address is based on the deref of this pointer.
171+ Deref ( VnIndex ) ,
172+ }
173+
166174#[ derive( Copy , Clone , Debug , PartialEq , Eq , Hash ) ]
167175enum Value < ' a , ' tcx > {
168176 // Root values.
@@ -191,7 +199,10 @@ enum Value<'a, 'tcx> {
191199 Repeat ( VnIndex , ty:: Const < ' tcx > ) ,
192200 /// The address of a place.
193201 Address {
194- place : Place < ' tcx > ,
202+ base : AddressBase ,
203+ // We do not use a plain `Place` as we want to be able to reason about indices.
204+ // This does not contain any `Deref` projection.
205+ projection : & ' a [ ProjectionElem < VnIndex , Ty < ' tcx > > ] ,
195206 kind : AddressKind ,
196207 /// Give each borrow and pointer a different provenance, so we don't merge them.
197208 provenance : usize ,
@@ -308,16 +319,38 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
308319
309320 /// Create a new `Value::Address` distinct from all the others.
310321 #[ instrument( level = "trace" , skip( self ) , ret) ]
311- fn new_pointer ( & mut self , place : Place < ' tcx > , kind : AddressKind ) -> VnIndex {
322+ fn new_pointer (
323+ & mut self ,
324+ place : Place < ' tcx > ,
325+ kind : AddressKind ,
326+ location : Location ,
327+ ) -> Option < VnIndex > {
312328 let pty = place. ty ( self . local_decls , self . tcx ) . ty ;
313329 let ty = match kind {
314330 AddressKind :: Ref ( bk) => {
315331 Ty :: new_ref ( self . tcx , self . tcx . lifetimes . re_erased , pty, bk. to_mutbl_lossy ( ) )
316332 }
317333 AddressKind :: Address ( mutbl) => Ty :: new_ptr ( self . tcx , pty, mutbl. to_mutbl_lossy ( ) ) ,
318334 } ;
319- let value = Value :: Address { place, kind, provenance : self . next_opaque ( ) } ;
320- self . insert ( ty, value)
335+
336+ let mut projection = place. projection . iter ( ) ;
337+ let base = if place. is_indirect_first_projection ( ) {
338+ let base = self . locals [ place. local ] ?;
339+ // Skip the initial `Deref`.
340+ projection. next ( ) ;
341+ AddressBase :: Deref ( base)
342+ } else {
343+ AddressBase :: Local ( place. local )
344+ } ;
345+ // Do not try evaluating inside `Index`, this has been done by `simplify_place_value`.
346+ let projection = self
347+ . arena
348+ . try_alloc_from_iter (
349+ projection. map ( |proj| proj. try_map ( |value| self . locals [ value] , |ty| ty) . ok_or ( ( ) ) ) ,
350+ )
351+ . ok ( ) ?;
352+ let value = Value :: Address { base, projection, kind, provenance : self . next_opaque ( ) } ;
353+ Some ( self . insert ( ty, value) )
321354 }
322355
323356 #[ inline]
@@ -458,14 +491,15 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
458491 let elem = elem. try_map ( |_| None , |( ) | ty. ty ) ?;
459492 self . ecx . project ( base, elem) . discard_err ( ) ?
460493 }
461- Address { place, kind : _, provenance : _ } => {
462- if !place. is_indirect_first_projection ( ) {
463- return None ;
464- }
465- let local = self . locals [ place. local ] ?;
466- let pointer = self . evaluated [ local] . as_ref ( ) ?;
494+ Address { base, projection, .. } => {
495+ debug_assert ! ( !projection. contains( & ProjectionElem :: Deref ) ) ;
496+ let pointer = match base {
497+ AddressBase :: Deref ( pointer) => self . evaluated [ pointer] . as_ref ( ) ?,
498+ // We have no stack to point to.
499+ AddressBase :: Local ( _) => return None ,
500+ } ;
467501 let mut mplace = self . ecx . deref_pointer ( pointer) . discard_err ( ) ?;
468- for elem in place . projection . iter ( ) . skip ( 1 ) {
502+ for elem in projection {
469503 // `Index` by constants should have been replaces by `ConstantIndex` by
470504 // `simplify_place_projection`.
471505 let elem = elem. try_map ( |_| None , |ty| ty) ?;
@@ -589,19 +623,51 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
589623 Some ( op)
590624 }
591625
626+ /// Represent the *value* we obtain by dereferencing an `Address` value.
627+ #[ instrument( level = "trace" , skip( self ) , ret) ]
628+ fn dereference_address (
629+ & mut self ,
630+ base : AddressBase ,
631+ projection : & [ ProjectionElem < VnIndex , Ty < ' tcx > > ] ,
632+ ) -> Option < VnIndex > {
633+ let ( mut place_ty, mut value) = match base {
634+ // The base is a local, so we take the local's value and project from it.
635+ AddressBase :: Local ( local) => {
636+ let local = self . locals [ local] ?;
637+ let place_ty = PlaceTy :: from_ty ( self . ty ( local) ) ;
638+ ( place_ty, local)
639+ }
640+ // The base is a pointer's deref, so we introduce the implicit deref.
641+ AddressBase :: Deref ( reborrow) => {
642+ let place_ty = PlaceTy :: from_ty ( self . ty ( reborrow) ) ;
643+ self . project ( place_ty, reborrow, ProjectionElem :: Deref ) ?
644+ }
645+ } ;
646+ for & proj in projection {
647+ ( place_ty, value) = self . project ( place_ty, value, proj) ?;
648+ }
649+ Some ( value)
650+ }
651+
652+ #[ instrument( level = "trace" , skip( self ) , ret) ]
592653 fn project (
593654 & mut self ,
594655 place_ty : PlaceTy < ' tcx > ,
595656 value : VnIndex ,
596- proj : PlaceElem < ' tcx > ,
597- from_non_ssa_index : & mut bool ,
657+ proj : ProjectionElem < VnIndex , Ty < ' tcx > > ,
598658 ) -> Option < ( PlaceTy < ' tcx > , VnIndex ) > {
599659 let projection_ty = place_ty. projection_ty ( self . tcx , proj) ;
600660 let proj = match proj {
601661 ProjectionElem :: Deref => {
602662 if let Some ( Mutability :: Not ) = place_ty. ty . ref_mutability ( )
603663 && projection_ty. ty . is_freeze ( self . tcx , self . typing_env ( ) )
604664 {
665+ if let Value :: Address { base, projection, .. } = self . get ( value)
666+ && let Some ( value) = self . dereference_address ( base, projection)
667+ {
668+ return Some ( ( projection_ty, value) ) ;
669+ }
670+
605671 // An immutable borrow `_x` always points to the same value for the
606672 // lifetime of the borrow, so we can merge all instances of `*_x`.
607673 return Some ( ( projection_ty, self . insert_deref ( projection_ty. ty , value) ) ) ;
@@ -638,10 +704,8 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
638704 }
639705 ProjectionElem :: Index ( idx) => {
640706 if let Value :: Repeat ( inner, _) = self . get ( value) {
641- * from_non_ssa_index |= self . locals [ idx] . is_none ( ) ;
642707 return Some ( ( projection_ty, inner) ) ;
643708 }
644- let idx = self . locals [ idx] ?;
645709 ProjectionElem :: Index ( idx)
646710 }
647711 ProjectionElem :: ConstantIndex { offset, min_length, from_end } => {
@@ -720,63 +784,71 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
720784 /// Represent the *value* which would be read from `place`, and point `place` to a preexisting
721785 /// place with the same value (if that already exists).
722786 #[ instrument( level = "trace" , skip( self ) , ret) ]
723- fn simplify_place_value (
787+ fn compute_place_value (
724788 & mut self ,
725- place : & mut Place < ' tcx > ,
789+ place : Place < ' tcx > ,
726790 location : Location ,
727- ) -> Option < VnIndex > {
728- self . simplify_place_projection ( place, location) ;
729-
791+ ) -> Result < VnIndex , PlaceRef < ' tcx > > {
730792 // Invariant: `place` and `place_ref` point to the same value, even if they point to
731793 // different memory locations.
732794 let mut place_ref = place. as_ref ( ) ;
733795
734796 // Invariant: `value` holds the value up-to the `index`th projection excluded.
735- let mut value = self . locals [ place. local ] ? ;
797+ let Some ( mut value) = self . locals [ place. local ] else { return Err ( place_ref ) } ;
736798 // Invariant: `value` has type `place_ty`, with optional downcast variant if needed.
737799 let mut place_ty = PlaceTy :: from_ty ( self . local_decls [ place. local ] . ty ) ;
738- let mut from_non_ssa_index = false ;
739800 for ( index, proj) in place. projection . iter ( ) . enumerate ( ) {
740- if let Value :: Projection ( pointer, ProjectionElem :: Deref ) = self . get ( value)
741- && let Value :: Address { place : mut pointee, kind, .. } = self . get ( pointer)
742- && let AddressKind :: Ref ( BorrowKind :: Shared ) = kind
743- && let Some ( v) = self . simplify_place_value ( & mut pointee, location)
744- {
745- value = v;
746- place_ref = pointee. project_deeper ( & place. projection [ index..] , self . tcx ) . as_ref ( ) ;
747- }
748801 if let Some ( local) = self . try_as_local ( value, location) {
749802 // Both `local` and `Place { local: place.local, projection: projection[..index] }`
750803 // hold the same value. Therefore, following place holds the value in the original
751804 // `place`.
752805 place_ref = PlaceRef { local, projection : & place. projection [ index..] } ;
753806 }
754807
755- ( place_ty, value) = self . project ( place_ty, value, proj, & mut from_non_ssa_index) ?;
808+ let Some ( proj) = proj. try_map ( |value| self . locals [ value] , |ty| ty) else {
809+ return Err ( place_ref) ;
810+ } ;
811+ let Some ( ty_and_value) = self . project ( place_ty, value, proj) else {
812+ return Err ( place_ref) ;
813+ } ;
814+ ( place_ty, value) = ty_and_value;
756815 }
757816
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- place_ref = pointee. project_deeper ( & [ ] , self . tcx ) . as_ref ( ) ;
765- }
766- if let Some ( new_local) = self . try_as_local ( value, location) {
767- place_ref = PlaceRef { local : new_local, projection : & [ ] } ;
768- } else if from_non_ssa_index {
769- // If access to non-SSA locals is unavoidable, bail out.
770- return None ;
771- }
817+ Ok ( value)
818+ }
772819
773- if place_ref. local != place. local || place_ref. projection . len ( ) < place. projection . len ( ) {
774- // By the invariant on `place_ref`.
775- * place = place_ref. project_deeper ( & [ ] , self . tcx ) ;
776- self . reused_locals . insert ( place_ref. local ) ;
777- }
820+ /// Represent the *value* which would be read from `place`, and point `place` to a preexisting
821+ /// place with the same value (if that already exists).
822+ #[ instrument( level = "trace" , skip( self ) , ret) ]
823+ fn simplify_place_value (
824+ & mut self ,
825+ place : & mut Place < ' tcx > ,
826+ location : Location ,
827+ ) -> Option < VnIndex > {
828+ self . simplify_place_projection ( place, location) ;
778829
779- Some ( value)
830+ match self . compute_place_value ( * place, location) {
831+ Ok ( value) => {
832+ if let Some ( new_place) = self . try_as_place ( value, location, true )
833+ && ( new_place. local != place. local
834+ || new_place. projection . len ( ) < place. projection . len ( ) )
835+ {
836+ * place = new_place;
837+ self . reused_locals . insert ( new_place. local ) ;
838+ }
839+ Some ( value)
840+ }
841+ Err ( place_ref) => {
842+ if place_ref. local != place. local
843+ || place_ref. projection . len ( ) < place. projection . len ( )
844+ {
845+ // By the invariant on `place_ref`.
846+ * place = place_ref. project_deeper ( & [ ] , self . tcx ) ;
847+ self . reused_locals . insert ( place_ref. local ) ;
848+ }
849+ None
850+ }
851+ }
780852 }
781853
782854 #[ instrument( level = "trace" , skip( self ) , ret) ]
@@ -823,11 +895,11 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
823895 Rvalue :: Aggregate ( ..) => return self . simplify_aggregate ( lhs, rvalue, location) ,
824896 Rvalue :: Ref ( _, borrow_kind, ref mut place) => {
825897 self . simplify_place_projection ( place, location) ;
826- return Some ( self . new_pointer ( * place, AddressKind :: Ref ( borrow_kind) ) ) ;
898+ return self . new_pointer ( * place, AddressKind :: Ref ( borrow_kind) , location ) ;
827899 }
828900 Rvalue :: RawPtr ( mutbl, ref mut place) => {
829901 self . simplify_place_projection ( place, location) ;
830- return Some ( self . new_pointer ( * place, AddressKind :: Address ( mutbl) ) ) ;
902+ return self . new_pointer ( * place, AddressKind :: Address ( mutbl) , location ) ;
831903 }
832904 Rvalue :: WrapUnsafeBinder ( ref mut op, _) => {
833905 let value = self . simplify_operand ( op, location) ?;
@@ -1069,12 +1141,10 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
10691141 }
10701142
10711143 // `&mut *p`, `&raw *p`, etc don't change metadata.
1072- Value :: Address { place, kind : _, provenance : _ }
1073- if let PlaceRef { local, projection : [ PlaceElem :: Deref ] } =
1074- place. as_ref ( )
1075- && let Some ( local_index) = self . locals [ local] =>
1144+ Value :: Address { base : AddressBase :: Deref ( reborrowed) , projection, .. }
1145+ if projection. is_empty ( ) =>
10761146 {
1077- arg_index = local_index ;
1147+ arg_index = reborrowed ;
10781148 was_updated = true ;
10791149 continue ;
10801150 }
@@ -1420,9 +1490,9 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
14201490
14211491 // The length information is stored in the wide pointer.
14221492 // Reborrowing copies length information from one pointer to the other.
1423- while let Value :: Address { place : borrowed, .. } = self . get ( inner )
1424- && let [ PlaceElem :: Deref ] = borrowed . projection [ .. ]
1425- && let Some ( borrowed ) = self . locals [ borrowed . local ]
1493+ while let Value :: Address { base : AddressBase :: Deref ( borrowed) , projection , .. } =
1494+ self . get ( inner )
1495+ && projection . is_empty ( )
14261496 {
14271497 inner = borrowed;
14281498 }
0 commit comments