@@ -163,6 +163,14 @@ enum AddressKind {
163
163
Address ( RawPtrKind ) ,
164
164
}
165
165
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
+
166
174
#[ derive( Copy , Clone , Debug , PartialEq , Eq , Hash ) ]
167
175
enum Value < ' a , ' tcx > {
168
176
// Root values.
@@ -191,7 +199,10 @@ enum Value<'a, 'tcx> {
191
199
Repeat ( VnIndex , ty:: Const < ' tcx > ) ,
192
200
/// The address of a place.
193
201
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 > > ] ,
195
206
kind : AddressKind ,
196
207
/// Give each borrow and pointer a different provenance, so we don't merge them.
197
208
provenance : usize ,
@@ -308,16 +319,38 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
308
319
309
320
/// Create a new `Value::Address` distinct from all the others.
310
321
#[ 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 > {
312
328
let pty = place. ty ( self . local_decls , self . tcx ) . ty ;
313
329
let ty = match kind {
314
330
AddressKind :: Ref ( bk) => {
315
331
Ty :: new_ref ( self . tcx , self . tcx . lifetimes . re_erased , pty, bk. to_mutbl_lossy ( ) )
316
332
}
317
333
AddressKind :: Address ( mutbl) => Ty :: new_ptr ( self . tcx , pty, mutbl. to_mutbl_lossy ( ) ) ,
318
334
} ;
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) )
321
354
}
322
355
323
356
#[ inline]
@@ -458,14 +491,15 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
458
491
let elem = elem. try_map ( |_| None , |( ) | ty. ty ) ?;
459
492
self . ecx . project ( base, elem) . discard_err ( ) ?
460
493
}
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
+ } ;
467
501
let mut mplace = self . ecx . deref_pointer ( pointer) . discard_err ( ) ?;
468
- for elem in place . projection . iter ( ) . skip ( 1 ) {
502
+ for elem in projection {
469
503
// `Index` by constants should have been replaces by `ConstantIndex` by
470
504
// `simplify_place_projection`.
471
505
let elem = elem. try_map ( |_| None , |ty| ty) ?;
@@ -589,19 +623,51 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
589
623
Some ( op)
590
624
}
591
625
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) ]
592
653
fn project (
593
654
& mut self ,
594
655
place_ty : PlaceTy < ' tcx > ,
595
656
value : VnIndex ,
596
- proj : PlaceElem < ' tcx > ,
597
- from_non_ssa_index : & mut bool ,
657
+ proj : ProjectionElem < VnIndex , Ty < ' tcx > > ,
598
658
) -> Option < ( PlaceTy < ' tcx > , VnIndex ) > {
599
659
let projection_ty = place_ty. projection_ty ( self . tcx , proj) ;
600
660
let proj = match proj {
601
661
ProjectionElem :: Deref => {
602
662
if let Some ( Mutability :: Not ) = place_ty. ty . ref_mutability ( )
603
663
&& projection_ty. ty . is_freeze ( self . tcx , self . typing_env ( ) )
604
664
{
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
+
605
671
// An immutable borrow `_x` always points to the same value for the
606
672
// lifetime of the borrow, so we can merge all instances of `*_x`.
607
673
return Some ( ( projection_ty, self . insert_deref ( projection_ty. ty , value) ) ) ;
@@ -638,10 +704,8 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
638
704
}
639
705
ProjectionElem :: Index ( idx) => {
640
706
if let Value :: Repeat ( inner, _) = self . get ( value) {
641
- * from_non_ssa_index |= self . locals [ idx] . is_none ( ) ;
642
707
return Some ( ( projection_ty, inner) ) ;
643
708
}
644
- let idx = self . locals [ idx] ?;
645
709
ProjectionElem :: Index ( idx)
646
710
}
647
711
ProjectionElem :: ConstantIndex { offset, min_length, from_end } => {
@@ -720,63 +784,71 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
720
784
/// Represent the *value* which would be read from `place`, and point `place` to a preexisting
721
785
/// place with the same value (if that already exists).
722
786
#[ instrument( level = "trace" , skip( self ) , ret) ]
723
- fn simplify_place_value (
787
+ fn compute_place_value (
724
788
& mut self ,
725
- place : & mut Place < ' tcx > ,
789
+ place : Place < ' tcx > ,
726
790
location : Location ,
727
- ) -> Option < VnIndex > {
728
- self . simplify_place_projection ( place, location) ;
729
-
791
+ ) -> Result < VnIndex , PlaceRef < ' tcx > > {
730
792
// Invariant: `place` and `place_ref` point to the same value, even if they point to
731
793
// different memory locations.
732
794
let mut place_ref = place. as_ref ( ) ;
733
795
734
796
// 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 ) } ;
736
798
// Invariant: `value` has type `place_ty`, with optional downcast variant if needed.
737
799
let mut place_ty = PlaceTy :: from_ty ( self . local_decls [ place. local ] . ty ) ;
738
- let mut from_non_ssa_index = false ;
739
800
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
- }
748
801
if let Some ( local) = self . try_as_local ( value, location) {
749
802
// Both `local` and `Place { local: place.local, projection: projection[..index] }`
750
803
// hold the same value. Therefore, following place holds the value in the original
751
804
// `place`.
752
805
place_ref = PlaceRef { local, projection : & place. projection [ index..] } ;
753
806
}
754
807
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;
756
815
}
757
816
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
+ }
772
819
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) ;
778
829
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
+ }
780
852
}
781
853
782
854
#[ instrument( level = "trace" , skip( self ) , ret) ]
@@ -823,11 +895,11 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
823
895
Rvalue :: Aggregate ( ..) => return self . simplify_aggregate ( lhs, rvalue, location) ,
824
896
Rvalue :: Ref ( _, borrow_kind, ref mut place) => {
825
897
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 ) ;
827
899
}
828
900
Rvalue :: RawPtr ( mutbl, ref mut place) => {
829
901
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 ) ;
831
903
}
832
904
Rvalue :: WrapUnsafeBinder ( ref mut op, _) => {
833
905
let value = self . simplify_operand ( op, location) ?;
@@ -1069,12 +1141,10 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
1069
1141
}
1070
1142
1071
1143
// `&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 ( ) =>
1076
1146
{
1077
- arg_index = local_index ;
1147
+ arg_index = reborrowed ;
1078
1148
was_updated = true ;
1079
1149
continue ;
1080
1150
}
@@ -1420,9 +1490,9 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
1420
1490
1421
1491
// The length information is stored in the wide pointer.
1422
1492
// 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 ( )
1426
1496
{
1427
1497
inner = borrowed;
1428
1498
}
0 commit comments