@@ -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 ,
@@ -307,16 +318,30 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
307
318
308
319
/// Create a new `Value::Address` distinct from all the others.
309
320
#[ instrument( level = "trace" , skip( self ) , ret) ]
310
- fn new_pointer ( & mut self , place : Place < ' tcx > , kind : AddressKind ) -> VnIndex {
321
+ fn new_pointer ( & mut self , place : Place < ' tcx > , kind : AddressKind ) -> Option < VnIndex > {
311
322
let pty = place. ty ( self . local_decls , self . tcx ) . ty ;
312
323
let ty = match kind {
313
324
AddressKind :: Ref ( bk) => {
314
325
Ty :: new_ref ( self . tcx , self . tcx . lifetimes . re_erased , pty, bk. to_mutbl_lossy ( ) )
315
326
}
316
327
AddressKind :: Address ( mutbl) => Ty :: new_ptr ( self . tcx , pty, mutbl. to_mutbl_lossy ( ) ) ,
317
328
} ;
318
- let value = Value :: Address { place, kind, provenance : self . next_opaque ( ) } ;
319
- self . insert ( ty, value)
329
+
330
+ let mut projection = place. projection . iter ( ) ;
331
+ let base = if place. is_indirect_first_projection ( ) {
332
+ let base = self . locals [ place. local ] ?;
333
+ // Skip the initial `Deref`.
334
+ projection. next ( ) ;
335
+ AddressBase :: Deref ( base)
336
+ } else {
337
+ AddressBase :: Local ( place. local )
338
+ } ;
339
+ // Do not try evaluating inside `Index`, this has been done by `simplify_place_projection`.
340
+ let projection =
341
+ projection. map ( |proj| proj. try_map ( |index| self . locals [ index] , |ty| ty) . ok_or ( ( ) ) ) ;
342
+ let projection = self . arena . try_alloc_from_iter ( projection) . ok ( ) ?;
343
+ let value = Value :: Address { base, projection, kind, provenance : self . next_opaque ( ) } ;
344
+ Some ( self . insert ( ty, value) )
320
345
}
321
346
322
347
#[ inline]
@@ -457,14 +482,15 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
457
482
let elem = elem. try_map ( |_| None , |( ) | ty. ty ) ?;
458
483
self . ecx . project ( base, elem) . discard_err ( ) ?
459
484
}
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 ( ) ?;
485
+ Address { base, projection, .. } => {
486
+ debug_assert ! ( !projection. contains( & ProjectionElem :: Deref ) ) ;
487
+ let pointer = match base {
488
+ AddressBase :: Deref ( pointer) => self . evaluated [ pointer] . as_ref ( ) ?,
489
+ // We have no stack to point to.
490
+ AddressBase :: Local ( _) => return None ,
491
+ } ;
466
492
let mut mplace = self . ecx . deref_pointer ( pointer) . discard_err ( ) ?;
467
- for elem in place . projection . iter ( ) . skip ( 1 ) {
493
+ for elem in projection {
468
494
// `Index` by constants should have been replaced by `ConstantIndex` by
469
495
// `simplify_place_projection`.
470
496
let elem = elem. try_map ( |_| None , |ty| ty) ?;
@@ -583,19 +609,51 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
583
609
Some ( op)
584
610
}
585
611
612
+ /// Represent the *value* we obtain by dereferencing an `Address` value.
613
+ #[ instrument( level = "trace" , skip( self ) , ret) ]
614
+ fn dereference_address (
615
+ & mut self ,
616
+ base : AddressBase ,
617
+ projection : & [ ProjectionElem < VnIndex , Ty < ' tcx > > ] ,
618
+ ) -> Option < VnIndex > {
619
+ let ( mut place_ty, mut value) = match base {
620
+ // The base is a local, so we take the local's value and project from it.
621
+ AddressBase :: Local ( local) => {
622
+ let local = self . locals [ local] ?;
623
+ let place_ty = PlaceTy :: from_ty ( self . ty ( local) ) ;
624
+ ( place_ty, local)
625
+ }
626
+ // The base is a pointer's deref, so we introduce the implicit deref.
627
+ AddressBase :: Deref ( reborrow) => {
628
+ let place_ty = PlaceTy :: from_ty ( self . ty ( reborrow) ) ;
629
+ self . project ( place_ty, reborrow, ProjectionElem :: Deref ) ?
630
+ }
631
+ } ;
632
+ for & proj in projection {
633
+ ( place_ty, value) = self . project ( place_ty, value, proj) ?;
634
+ }
635
+ Some ( value)
636
+ }
637
+
638
+ #[ instrument( level = "trace" , skip( self ) , ret) ]
586
639
fn project (
587
640
& mut self ,
588
641
place_ty : PlaceTy < ' tcx > ,
589
642
value : VnIndex ,
590
- proj : PlaceElem < ' tcx > ,
591
- from_non_ssa_index : & mut bool ,
643
+ proj : ProjectionElem < VnIndex , Ty < ' tcx > > ,
592
644
) -> Option < ( PlaceTy < ' tcx > , VnIndex ) > {
593
645
let projection_ty = place_ty. projection_ty ( self . tcx , proj) ;
594
646
let proj = match proj {
595
647
ProjectionElem :: Deref => {
596
648
if let Some ( Mutability :: Not ) = place_ty. ty . ref_mutability ( )
597
649
&& projection_ty. ty . is_freeze ( self . tcx , self . typing_env ( ) )
598
650
{
651
+ if let Value :: Address { base, projection, .. } = self . get ( value)
652
+ && let Some ( value) = self . dereference_address ( base, projection)
653
+ {
654
+ return Some ( ( projection_ty, value) ) ;
655
+ }
656
+
599
657
// An immutable borrow `_x` always points to the same value for the
600
658
// lifetime of the borrow, so we can merge all instances of `*_x`.
601
659
return Some ( ( projection_ty, self . insert_deref ( projection_ty. ty , value) ) ) ;
@@ -632,10 +690,8 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
632
690
}
633
691
ProjectionElem :: Index ( idx) => {
634
692
if let Value :: Repeat ( inner, _) = self . get ( value) {
635
- * from_non_ssa_index |= self . locals [ idx] . is_none ( ) ;
636
693
return Some ( ( projection_ty, inner) ) ;
637
694
}
638
- let idx = self . locals [ idx] ?;
639
695
ProjectionElem :: Index ( idx)
640
696
}
641
697
ProjectionElem :: ConstantIndex { offset, min_length, from_end } => {
@@ -711,77 +767,74 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
711
767
trace ! ( ?place) ;
712
768
}
713
769
714
- /// Represent the *value* which would be read from `place`, and point `place` to a preexisting
715
- /// place with the same value (if that already exists) .
770
+ /// Represent the *value* which would be read from `place`. If we succeed, return it.
771
+ /// If we fail, return a `PlaceRef` that contains the same value .
716
772
#[ instrument( level = "trace" , skip( self ) , ret) ]
717
- fn simplify_place_value (
773
+ fn compute_place_value (
718
774
& mut self ,
719
- place : & mut Place < ' tcx > ,
775
+ place : Place < ' tcx > ,
720
776
location : Location ,
721
- ) -> Option < VnIndex > {
722
- self . simplify_place_projection ( place, location) ;
723
-
777
+ ) -> Result < VnIndex , PlaceRef < ' tcx > > {
724
778
// Invariant: `place` and `place_ref` point to the same value, even if they point to
725
779
// different memory locations.
726
780
let mut place_ref = place. as_ref ( ) ;
727
781
728
782
// Invariant: `value` holds the value up-to the `index`th projection excluded.
729
- let mut value = self . locals [ place. local ] ? ;
783
+ let Some ( mut value) = self . locals [ place. local ] else { return Err ( place_ref ) } ;
730
784
// Invariant: `value` has type `place_ty`, with optional downcast variant if needed.
731
785
let mut place_ty = PlaceTy :: from_ty ( self . local_decls [ place. local ] . ty ) ;
732
- let mut from_non_ssa_index = false ;
733
786
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
- }
748
787
if let Some ( local) = self . try_as_local ( value, location) {
749
788
// Both `local` and `Place { local: place.local, projection: projection[..index] }`
750
789
// hold the same value. Therefore, following place holds the value in the original
751
790
// `place`.
752
791
place_ref = PlaceRef { local, projection : & place. projection [ index..] } ;
753
792
}
754
793
755
- ( place_ty, value) = self . project ( place_ty, value, proj, & mut from_non_ssa_index) ?;
794
+ let Some ( proj) = proj. try_map ( |value| self . locals [ value] , |ty| ty) else {
795
+ return Err ( place_ref) ;
796
+ } ;
797
+ let Some ( ty_and_value) = self . project ( place_ty, value, proj) else {
798
+ return Err ( place_ref) ;
799
+ } ;
800
+ ( place_ty, value) = ty_and_value;
756
801
}
757
802
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
- }
803
+ Ok ( value)
804
+ }
777
805
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
- }
806
+ /// Represent the *value* which would be read from `place`, and point `place` to a preexisting
807
+ /// place with the same value (if that already exists).
808
+ #[ instrument( level = "trace" , skip( self ) , ret) ]
809
+ fn simplify_place_value (
810
+ & mut self ,
811
+ place : & mut Place < ' tcx > ,
812
+ location : Location ,
813
+ ) -> Option < VnIndex > {
814
+ self . simplify_place_projection ( place, location) ;
783
815
784
- Some ( value)
816
+ match self . compute_place_value ( * place, location) {
817
+ Ok ( value) => {
818
+ if let Some ( new_place) = self . try_as_place ( value, location, true )
819
+ && ( new_place. local != place. local
820
+ || new_place. projection . len ( ) < place. projection . len ( ) )
821
+ {
822
+ * place = new_place;
823
+ self . reused_locals . insert ( new_place. local ) ;
824
+ }
825
+ Some ( value)
826
+ }
827
+ Err ( place_ref) => {
828
+ if place_ref. local != place. local
829
+ || place_ref. projection . len ( ) < place. projection . len ( )
830
+ {
831
+ // By the invariant on `place_ref`.
832
+ * place = place_ref. project_deeper ( & [ ] , self . tcx ) ;
833
+ self . reused_locals . insert ( place_ref. local ) ;
834
+ }
835
+ None
836
+ }
837
+ }
785
838
}
786
839
787
840
#[ instrument( level = "trace" , skip( self ) , ret) ]
@@ -828,11 +881,11 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
828
881
Rvalue :: Aggregate ( ..) => return self . simplify_aggregate ( lhs, rvalue, location) ,
829
882
Rvalue :: Ref ( _, borrow_kind, ref mut place) => {
830
883
self . simplify_place_projection ( place, location) ;
831
- return Some ( self . new_pointer ( * place, AddressKind :: Ref ( borrow_kind) ) ) ;
884
+ return self . new_pointer ( * place, AddressKind :: Ref ( borrow_kind) ) ;
832
885
}
833
886
Rvalue :: RawPtr ( mutbl, ref mut place) => {
834
887
self . simplify_place_projection ( place, location) ;
835
- return Some ( self . new_pointer ( * place, AddressKind :: Address ( mutbl) ) ) ;
888
+ return self . new_pointer ( * place, AddressKind :: Address ( mutbl) ) ;
836
889
}
837
890
Rvalue :: WrapUnsafeBinder ( ref mut op, _) => {
838
891
let value = self . simplify_operand ( op, location) ?;
@@ -1072,12 +1125,10 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
1072
1125
}
1073
1126
1074
1127
// `&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] =>
1128
+ Value :: Address { base : AddressBase :: Deref ( reborrowed) , projection, .. }
1129
+ if projection. is_empty ( ) =>
1079
1130
{
1080
- local_index
1131
+ reborrowed
1081
1132
}
1082
1133
1083
1134
_ => break ,
0 commit comments