@@ -164,6 +164,14 @@ enum AddressKind {
164
164
Address ( RawPtrKind ) ,
165
165
}
166
166
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
+
167
175
#[ derive( Copy , Clone , Debug , PartialEq , Eq , Hash ) ]
168
176
enum Value < ' a , ' tcx > {
169
177
// Root values.
@@ -192,7 +200,10 @@ enum Value<'a, 'tcx> {
192
200
Repeat ( VnIndex , ty:: Const < ' tcx > ) ,
193
201
/// The address of a place.
194
202
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 > > ] ,
196
207
kind : AddressKind ,
197
208
/// Give each borrow and pointer a different provenance, so we don't merge them.
198
209
provenance : usize ,
@@ -309,16 +320,38 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
309
320
310
321
/// Create a new `Value::Address` distinct from all the others.
311
322
#[ 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 > {
313
329
let pty = place. ty ( self . local_decls , self . tcx ) . ty ;
314
330
let ty = match kind {
315
331
AddressKind :: Ref ( bk) => {
316
332
Ty :: new_ref ( self . tcx , self . tcx . lifetimes . re_erased , pty, bk. to_mutbl_lossy ( ) )
317
333
}
318
334
AddressKind :: Address ( mutbl) => Ty :: new_ptr ( self . tcx , pty, mutbl. to_mutbl_lossy ( ) ) ,
319
335
} ;
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) )
322
355
}
323
356
324
357
#[ inline]
@@ -459,14 +492,15 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
459
492
let elem = elem. try_map ( |_| None , |( ) | ty. ty ) ?;
460
493
self . ecx . project ( base, elem) . discard_err ( ) ?
461
494
}
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
+ } ;
468
502
let mut mplace = self . ecx . deref_pointer ( pointer) . discard_err ( ) ?;
469
- for elem in place . projection . iter ( ) . skip ( 1 ) {
503
+ for elem in projection {
470
504
// `Index` by constants should have been replaced by `ConstantIndex` by
471
505
// `simplify_place_projection`.
472
506
let elem = elem. try_map ( |_| None , |ty| ty) ?;
@@ -590,19 +624,51 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
590
624
Some ( op)
591
625
}
592
626
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) ]
593
654
fn project (
594
655
& mut self ,
595
656
place_ty : PlaceTy < ' tcx > ,
596
657
value : VnIndex ,
597
- proj : PlaceElem < ' tcx > ,
598
- from_non_ssa_index : & mut bool ,
658
+ proj : ProjectionElem < VnIndex , Ty < ' tcx > > ,
599
659
) -> Option < ( PlaceTy < ' tcx > , VnIndex ) > {
600
660
let projection_ty = place_ty. projection_ty ( self . tcx , proj) ;
601
661
let proj = match proj {
602
662
ProjectionElem :: Deref => {
603
663
if let Some ( Mutability :: Not ) = place_ty. ty . ref_mutability ( )
604
664
&& projection_ty. ty . is_freeze ( self . tcx , self . typing_env ( ) )
605
665
{
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
+
606
672
// An immutable borrow `_x` always points to the same value for the
607
673
// lifetime of the borrow, so we can merge all instances of `*_x`.
608
674
return Some ( ( projection_ty, self . insert_deref ( projection_ty. ty , value) ) ) ;
@@ -639,10 +705,8 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
639
705
}
640
706
ProjectionElem :: Index ( idx) => {
641
707
if let Value :: Repeat ( inner, _) = self . get ( value) {
642
- * from_non_ssa_index |= self . locals [ idx] . is_none ( ) ;
643
708
return Some ( ( projection_ty, inner) ) ;
644
709
}
645
- let idx = self . locals [ idx] ?;
646
710
ProjectionElem :: Index ( idx)
647
711
}
648
712
ProjectionElem :: ConstantIndex { offset, min_length, from_end } => {
@@ -721,74 +785,71 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
721
785
/// Represent the *value* which would be read from `place`, and point `place` to a preexisting
722
786
/// place with the same value (if that already exists).
723
787
#[ instrument( level = "trace" , skip( self ) , ret) ]
724
- fn simplify_place_value (
788
+ fn compute_place_value (
725
789
& mut self ,
726
- place : & mut Place < ' tcx > ,
790
+ place : Place < ' tcx > ,
727
791
location : Location ,
728
- ) -> Option < VnIndex > {
729
- self . simplify_place_projection ( place, location) ;
730
-
792
+ ) -> Result < VnIndex , PlaceRef < ' tcx > > {
731
793
// Invariant: `place` and `place_ref` point to the same value, even if they point to
732
794
// different memory locations.
733
795
let mut place_ref = place. as_ref ( ) ;
734
796
735
797
// 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 ) } ;
737
799
// Invariant: `value` has type `place_ty`, with optional downcast variant if needed.
738
800
let mut place_ty = PlaceTy :: from_ty ( self . local_decls [ place. local ] . ty ) ;
739
- let mut from_non_ssa_index = false ;
740
801
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
- }
755
802
if let Some ( local) = self . try_as_local ( value, location) {
756
803
// Both `local` and `Place { local: place.local, projection: projection[..index] }`
757
804
// hold the same value. Therefore, following place holds the value in the original
758
805
// `place`.
759
806
place_ref = PlaceRef { local, projection : & place. projection [ index..] } ;
760
807
}
761
808
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;
763
816
}
764
817
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
+ }
784
820
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) ;
790
830
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
+ }
792
853
}
793
854
794
855
#[ instrument( level = "trace" , skip( self ) , ret) ]
@@ -835,11 +896,11 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
835
896
Rvalue :: Aggregate ( ..) => return self . simplify_aggregate ( lhs, rvalue, location) ,
836
897
Rvalue :: Ref ( _, borrow_kind, ref mut place) => {
837
898
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 ) ;
839
900
}
840
901
Rvalue :: RawPtr ( mutbl, ref mut place) => {
841
902
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 ) ;
843
904
}
844
905
Rvalue :: WrapUnsafeBinder ( ref mut op, _) => {
845
906
let value = self . simplify_operand ( op, location) ?;
@@ -1070,12 +1131,10 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
1070
1131
}
1071
1132
1072
1133
// `&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 ( ) =>
1077
1136
{
1078
- arg_index = local_index ;
1137
+ arg_index = reborrowed ;
1079
1138
was_updated = true ;
1080
1139
continue ;
1081
1140
}
@@ -1421,9 +1480,9 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
1421
1480
1422
1481
// The length information is stored in the wide pointer.
1423
1482
// 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 ( )
1427
1486
{
1428
1487
inner = borrowed;
1429
1488
}
0 commit comments