@@ -186,6 +186,14 @@ enum AddressKind {
186
186
Address ( RawPtrKind ) ,
187
187
}
188
188
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
+
189
197
#[ derive( Copy , Clone , Debug , PartialEq , Eq , Hash ) ]
190
198
enum Value < ' a , ' tcx > {
191
199
// Root values.
@@ -216,7 +224,10 @@ enum Value<'a, 'tcx> {
216
224
Repeat ( VnIndex , ty:: Const < ' tcx > ) ,
217
225
/// The address of a place.
218
226
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 > > ] ,
220
231
kind : AddressKind ,
221
232
/// Give each borrow and pointer a different provenance, so we don't merge them.
222
233
provenance : VnOpaque ,
@@ -426,22 +437,42 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
426
437
427
438
/// Create a new `Value::Address` distinct from all the others.
428
439
#[ 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 > {
430
441
let pty = place. ty ( self . local_decls , self . tcx ) . ty ;
431
442
let ty = match kind {
432
443
AddressKind :: Ref ( bk) => {
433
444
Ty :: new_ref ( self . tcx , self . tcx . lifetimes . re_erased , pty, bk. to_mutbl_lossy ( ) )
434
445
}
435
446
AddressKind :: Address ( mutbl) => Ty :: new_ptr ( self . tcx , pty, mutbl. to_mutbl_lossy ( ) ) ,
436
447
} ;
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
+ } ) ;
439
469
let evaluated = self . eval_to_const ( index) ;
440
470
let _index = self . evaluated . push ( evaluated) ;
441
471
debug_assert_eq ! ( index, _index) ;
442
472
let _index = self . rev_locals . push ( SmallVec :: new ( ) ) ;
443
473
debug_assert_eq ! ( index, _index) ;
444
- index
474
+
475
+ Some ( index)
445
476
}
446
477
447
478
#[ instrument( level = "trace" , skip( self ) , ret) ]
@@ -591,14 +622,15 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
591
622
let elem = elem. try_map ( |_| None , |( ) | ty. ty ) ?;
592
623
self . ecx . project ( base, elem) . discard_err ( ) ?
593
624
}
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
+ } ;
600
632
let mut mplace = self . ecx . deref_pointer ( pointer) . discard_err ( ) ?;
601
- for elem in place . projection . iter ( ) . skip ( 1 ) {
633
+ for elem in projection {
602
634
// `Index` by constants should have been replaced by `ConstantIndex` by
603
635
// `simplify_place_projection`.
604
636
let elem = elem. try_map ( |_| None , |ty| ty) ?;
@@ -717,19 +749,51 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
717
749
Some ( op)
718
750
}
719
751
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) ]
720
779
fn project (
721
780
& mut self ,
722
781
place_ty : PlaceTy < ' tcx > ,
723
782
value : VnIndex ,
724
- proj : PlaceElem < ' tcx > ,
725
- from_non_ssa_index : & mut bool ,
783
+ proj : ProjectionElem < VnIndex , Ty < ' tcx > > ,
726
784
) -> Option < ( PlaceTy < ' tcx > , VnIndex ) > {
727
785
let projection_ty = place_ty. projection_ty ( self . tcx , proj) ;
728
786
let proj = match proj {
729
787
ProjectionElem :: Deref => {
730
788
if let Some ( Mutability :: Not ) = place_ty. ty . ref_mutability ( )
731
789
&& projection_ty. ty . is_freeze ( self . tcx , self . typing_env ( ) )
732
790
{
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
+
733
797
// An immutable borrow `_x` always points to the same value for the
734
798
// lifetime of the borrow, so we can merge all instances of `*_x`.
735
799
return Some ( ( projection_ty, self . insert_deref ( projection_ty. ty , value) ) ) ;
@@ -766,10 +830,8 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
766
830
}
767
831
ProjectionElem :: Index ( idx) => {
768
832
if let Value :: Repeat ( inner, _) = self . get ( value) {
769
- * from_non_ssa_index |= self . locals [ idx] . is_none ( ) ;
770
833
return Some ( ( projection_ty, inner) ) ;
771
834
}
772
- let idx = self . locals [ idx] ?;
773
835
ProjectionElem :: Index ( idx)
774
836
}
775
837
ProjectionElem :: ConstantIndex { offset, min_length, from_end } => {
@@ -845,77 +907,74 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
845
907
trace ! ( ?place) ;
846
908
}
847
909
848
- /// Represent the *value* which would be read from `place`, and point `place` to a preexisting
849
- /// place with the same value (if that already exists) .
910
+ /// Represent the *value* which would be read from `place`. If we succeed, return it.
911
+ /// If we fail, return a `PlaceRef` that contains the same value .
850
912
#[ instrument( level = "trace" , skip( self ) , ret) ]
851
- fn simplify_place_value (
913
+ fn compute_place_value (
852
914
& mut self ,
853
- place : & mut Place < ' tcx > ,
915
+ place : Place < ' tcx > ,
854
916
location : Location ,
855
- ) -> Option < VnIndex > {
856
- self . simplify_place_projection ( place, location) ;
857
-
917
+ ) -> Result < VnIndex , PlaceRef < ' tcx > > {
858
918
// Invariant: `place` and `place_ref` point to the same value, even if they point to
859
919
// different memory locations.
860
920
let mut place_ref = place. as_ref ( ) ;
861
921
862
922
// Invariant: `value` holds the value up-to the `index`th projection excluded.
863
- let mut value = self . locals [ place. local ] ? ;
923
+ let Some ( mut value) = self . locals [ place. local ] else { return Err ( place_ref ) } ;
864
924
// Invariant: `value` has type `place_ty`, with optional downcast variant if needed.
865
925
let mut place_ty = PlaceTy :: from_ty ( self . local_decls [ place. local ] . ty ) ;
866
- let mut from_non_ssa_index = false ;
867
926
for ( index, proj) in place. projection . iter ( ) . enumerate ( ) {
868
- if let Value :: Projection ( pointer, ProjectionElem :: Deref ) = self . get ( value)
869
- && let Value :: Address { place : mut pointee, kind, .. } = self . get ( pointer)
870
- && let AddressKind :: Ref ( BorrowKind :: Shared ) = kind
871
- && let Some ( v) = self . simplify_place_value ( & mut pointee, location)
872
- {
873
- value = v;
874
- // `pointee` holds a `Place`, so `ProjectionElem::Index` holds a `Local`.
875
- // That local is SSA, but we otherwise have no guarantee on that local's value at
876
- // the current location compared to its value where `pointee` was borrowed.
877
- if pointee. projection . iter ( ) . all ( |elem| !matches ! ( elem, ProjectionElem :: Index ( _) ) ) {
878
- place_ref =
879
- pointee. project_deeper ( & place. projection [ index..] , self . tcx ) . as_ref ( ) ;
880
- }
881
- }
882
927
if let Some ( local) = self . try_as_local ( value, location) {
883
928
// Both `local` and `Place { local: place.local, projection: projection[..index] }`
884
929
// hold the same value. Therefore, following place holds the value in the original
885
930
// `place`.
886
931
place_ref = PlaceRef { local, projection : & place. projection [ index..] } ;
887
932
}
888
933
889
- ( place_ty, value) = self . project ( place_ty, value, proj, & mut from_non_ssa_index) ?;
934
+ let Some ( proj) = proj. try_map ( |value| self . locals [ value] , |ty| ty) else {
935
+ return Err ( place_ref) ;
936
+ } ;
937
+ let Some ( ty_and_value) = self . project ( place_ty, value, proj) else {
938
+ return Err ( place_ref) ;
939
+ } ;
940
+ ( place_ty, value) = ty_and_value;
890
941
}
891
942
892
- if let Value :: Projection ( pointer, ProjectionElem :: Deref ) = self . get ( value)
893
- && let Value :: Address { place : mut pointee, kind, .. } = self . get ( pointer)
894
- && let AddressKind :: Ref ( BorrowKind :: Shared ) = kind
895
- && let Some ( v) = self . simplify_place_value ( & mut pointee, location)
896
- {
897
- value = v;
898
- // `pointee` holds a `Place`, so `ProjectionElem::Index` holds a `Local`.
899
- // That local is SSA, but we otherwise have no guarantee on that local's value at
900
- // the current location compared to its value where `pointee` was borrowed.
901
- if pointee. projection . iter ( ) . all ( |elem| !matches ! ( elem, ProjectionElem :: Index ( _) ) ) {
902
- place_ref = pointee. project_deeper ( & [ ] , self . tcx ) . as_ref ( ) ;
903
- }
904
- }
905
- if let Some ( new_local) = self . try_as_local ( value, location) {
906
- place_ref = PlaceRef { local : new_local, projection : & [ ] } ;
907
- } else if from_non_ssa_index {
908
- // If access to non-SSA locals is unavoidable, bail out.
909
- return None ;
910
- }
943
+ Ok ( value)
944
+ }
911
945
912
- if place_ref. local != place. local || place_ref. projection . len ( ) < place. projection . len ( ) {
913
- // By the invariant on `place_ref`.
914
- * place = place_ref. project_deeper ( & [ ] , self . tcx ) ;
915
- self . reused_locals . insert ( place_ref. local ) ;
916
- }
946
+ /// Represent the *value* which would be read from `place`, and point `place` to a preexisting
947
+ /// place with the same value (if that already exists).
948
+ #[ instrument( level = "trace" , skip( self ) , ret) ]
949
+ fn simplify_place_value (
950
+ & mut self ,
951
+ place : & mut Place < ' tcx > ,
952
+ location : Location ,
953
+ ) -> Option < VnIndex > {
954
+ self . simplify_place_projection ( place, location) ;
917
955
918
- Some ( value)
956
+ match self . compute_place_value ( * place, location) {
957
+ Ok ( value) => {
958
+ if let Some ( new_place) = self . try_as_place ( value, location, true )
959
+ && ( new_place. local != place. local
960
+ || new_place. projection . len ( ) < place. projection . len ( ) )
961
+ {
962
+ * place = new_place;
963
+ self . reused_locals . insert ( new_place. local ) ;
964
+ }
965
+ Some ( value)
966
+ }
967
+ Err ( place_ref) => {
968
+ if place_ref. local != place. local
969
+ || place_ref. projection . len ( ) < place. projection . len ( )
970
+ {
971
+ // By the invariant on `place_ref`.
972
+ * place = place_ref. project_deeper ( & [ ] , self . tcx ) ;
973
+ self . reused_locals . insert ( place_ref. local ) ;
974
+ }
975
+ None
976
+ }
977
+ }
919
978
}
920
979
921
980
#[ instrument( level = "trace" , skip( self ) , ret) ]
@@ -962,11 +1021,11 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
962
1021
Rvalue :: Aggregate ( ..) => return self . simplify_aggregate ( lhs, rvalue, location) ,
963
1022
Rvalue :: Ref ( _, borrow_kind, ref mut place) => {
964
1023
self . simplify_place_projection ( place, location) ;
965
- return Some ( self . new_pointer ( * place, AddressKind :: Ref ( borrow_kind) ) ) ;
1024
+ return self . new_pointer ( * place, AddressKind :: Ref ( borrow_kind) ) ;
966
1025
}
967
1026
Rvalue :: RawPtr ( mutbl, ref mut place) => {
968
1027
self . simplify_place_projection ( place, location) ;
969
- return Some ( self . new_pointer ( * place, AddressKind :: Address ( mutbl) ) ) ;
1028
+ return self . new_pointer ( * place, AddressKind :: Address ( mutbl) ) ;
970
1029
}
971
1030
Rvalue :: WrapUnsafeBinder ( ref mut op, _) => {
972
1031
let value = self . simplify_operand ( op, location) ?;
@@ -1206,12 +1265,10 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
1206
1265
}
1207
1266
1208
1267
// `&mut *p`, `&raw *p`, etc don't change metadata.
1209
- Value :: Address { place, kind : _, provenance : _ }
1210
- if let PlaceRef { local, projection : [ PlaceElem :: Deref ] } =
1211
- place. as_ref ( )
1212
- && let Some ( local_index) = self . locals [ local] =>
1268
+ Value :: Address { base : AddressBase :: Deref ( reborrowed) , projection, .. }
1269
+ if projection. is_empty ( ) =>
1213
1270
{
1214
- local_index
1271
+ reborrowed
1215
1272
}
1216
1273
1217
1274
_ => break ,
0 commit comments