@@ -95,7 +95,7 @@ use rustc_const_eval::interpret::{
95
95
ImmTy , Immediate , InterpCx , MemPlaceMeta , MemoryKind , OpTy , Projectable , Scalar ,
96
96
intern_const_alloc_for_constprop,
97
97
} ;
98
- use rustc_data_structures:: fx:: { FxIndexSet , MutableValues } ;
98
+ use rustc_data_structures:: fx:: { FxHashMap , FxIndexSet , MutableValues } ;
99
99
use rustc_data_structures:: graph:: dominators:: Dominators ;
100
100
use rustc_hir:: def:: DefKind ;
101
101
use rustc_index:: bit_set:: DenseBitSet ;
@@ -233,8 +233,11 @@ struct VnState<'body, 'a, 'tcx> {
233
233
/// Value stored in each local.
234
234
locals : IndexVec < Local , Option < VnIndex > > ,
235
235
/// Locals that are assigned that value.
236
- // This vector does not hold all the values of `VnIndex` that we create.
237
- rev_locals : IndexVec < VnIndex , SmallVec < [ Local ; 1 ] > > ,
236
+ // This vector holds the locals that are SSA.
237
+ rev_locals_ssa : IndexVec < VnIndex , SmallVec < [ Local ; 1 ] > > ,
238
+ // This map holds the locals that are not SSA. This map is cleared at the end of each block.
239
+ // Therefore, we do not need a location, the local always appears before the current location.
240
+ rev_locals_non_ssa : FxHashMap < VnIndex , SmallVec < [ Local ; 1 ] > > ,
238
241
values : FxIndexSet < ( Value < ' a , ' tcx > , Ty < ' tcx > ) > ,
239
242
/// Values evaluated as constants if possible.
240
243
evaluated : IndexVec < VnIndex , Option < OpTy < ' tcx > > > ,
@@ -270,8 +273,9 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
270
273
ecx : InterpCx :: new ( tcx, DUMMY_SP , typing_env, DummyMachine ) ,
271
274
local_decls,
272
275
is_coroutine : body. coroutine . is_some ( ) ,
273
- locals : IndexVec :: from_elem ( None , local_decls) ,
274
- rev_locals : IndexVec :: with_capacity ( num_values) ,
276
+ locals : IndexVec :: from_elem ( None , & body. local_decls ) ,
277
+ rev_locals_ssa : IndexVec :: with_capacity ( num_values) ,
278
+ rev_locals_non_ssa : FxHashMap :: default ( ) ,
275
279
values : FxIndexSet :: with_capacity_and_hasher ( num_values, Default :: default ( ) ) ,
276
280
evaluated : IndexVec :: with_capacity ( num_values) ,
277
281
next_opaque : 1 ,
@@ -296,7 +300,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
296
300
let evaluated = self . eval_to_const ( index) ;
297
301
let _index = self . evaluated . push ( evaluated) ;
298
302
debug_assert_eq ! ( index, _index) ;
299
- let _index = self . rev_locals . push ( SmallVec :: new ( ) ) ;
303
+ let _index = self . rev_locals_ssa . push ( Default :: default ( ) ) ;
300
304
debug_assert_eq ! ( index, _index) ;
301
305
}
302
306
index
@@ -334,7 +338,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
334
338
335
339
let mut projection = place. projection . iter ( ) ;
336
340
let base = if place. is_indirect_first_projection ( ) {
337
- let base = self . locals [ place. local ] ? ;
341
+ let base = self . local ( place. local ) ;
338
342
// Skip the initial `Deref`.
339
343
projection. next ( ) ;
340
344
AddressBase :: Deref ( base)
@@ -345,7 +349,8 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
345
349
let projection = self
346
350
. arena
347
351
. try_alloc_from_iter (
348
- projection. map ( |proj| proj. try_map ( |value| self . locals [ value] , |ty| ty) . ok_or ( ( ) ) ) ,
352
+ projection
353
+ . map ( |proj| proj. try_map ( |value| Some ( self . local ( value) ) , |ty| ty) . ok_or ( ( ) ) ) ,
349
354
)
350
355
. ok ( ) ?;
351
356
let value = Value :: Address { base, projection, kind, provenance : self . next_opaque ( ) } ;
@@ -362,12 +367,49 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
362
367
self . values . get_index ( index. as_usize ( ) ) . unwrap ( ) . 1
363
368
}
364
369
365
- /// Record that `local` is assigned `value`. `local` must be SSA.
370
+ /// Record that `local` is assigned `value`.
366
371
#[ instrument( level = "trace" , skip( self ) ) ]
367
372
fn assign ( & mut self , local : Local , value : VnIndex ) {
368
- debug_assert ! ( self . ssa. is_ssa( local) ) ;
369
373
self . locals [ local] = Some ( value) ;
370
- self . rev_locals [ value] . push ( local) ;
374
+ if self . ssa . is_ssa ( local) {
375
+ self . rev_locals_ssa [ value] . push ( local) ;
376
+ } else {
377
+ self . rev_locals_non_ssa . entry ( value) . or_default ( ) . push ( local) ;
378
+ }
379
+ }
380
+
381
+ /// Return the value assigned to a local, or assign an opaque value and return it.
382
+ #[ instrument( level = "trace" , skip( self ) , ret) ]
383
+ fn local ( & mut self , local : Local ) -> VnIndex {
384
+ if let Some ( value) = self . locals [ local] {
385
+ return value;
386
+ }
387
+ let value = self . new_opaque ( self . local_decls [ local] . ty ) ;
388
+ self . locals [ local] = Some ( value) ;
389
+ self . rev_locals_non_ssa . entry ( value) . or_default ( ) . push ( local) ;
390
+ value
391
+ }
392
+
393
+ #[ instrument( level = "trace" , skip( self ) ) ]
394
+ fn discard_place ( & mut self , place : Place < ' tcx > ) {
395
+ let discard_local = |this : & mut Self , local| {
396
+ if this. ssa . is_ssa ( local) {
397
+ return ;
398
+ }
399
+ if let Some ( value) = this. locals [ local] . take ( ) {
400
+ this. rev_locals_non_ssa . entry ( value) . or_default ( ) . retain ( |l| * l != local) ;
401
+ }
402
+ } ;
403
+ if place. is_indirect_first_projection ( ) {
404
+ // Non-local mutation maybe invalidate deref.
405
+ self . invalidate_derefs ( ) ;
406
+ // Remove stored value from borrowed locals.
407
+ for local in self . ssa . borrowed_locals ( ) . iter ( ) {
408
+ discard_local ( self , local) ;
409
+ }
410
+ } else {
411
+ discard_local ( self , place. local ) ;
412
+ }
371
413
}
372
414
373
415
fn insert_constant ( & mut self , value : Const < ' tcx > ) -> VnIndex {
@@ -627,7 +669,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
627
669
let ( mut place_ty, mut value) = match base {
628
670
// The base is a local, so we take the local's value and project from it.
629
671
AddressBase :: Local ( local) => {
630
- let local = self . locals [ local] ? ;
672
+ let local = self . local ( local ) ;
631
673
let place_ty = PlaceTy :: from_ty ( self . ty ( local) ) ;
632
674
( place_ty, local)
633
675
}
@@ -733,7 +775,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
733
775
// If the projection is indirect, we treat the local as a value, so can replace it with
734
776
// another local.
735
777
if place. is_indirect_first_projection ( )
736
- && let Some ( base) = self . locals [ place. local ]
778
+ && let base = self . local ( place. local )
737
779
&& let Some ( new_local) = self . try_as_local ( base, location)
738
780
&& place. local != new_local
739
781
{
@@ -745,9 +787,8 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
745
787
746
788
for i in 0 ..projection. len ( ) {
747
789
let elem = projection[ i] ;
748
- if let ProjectionElem :: Index ( idx_local) = elem
749
- && let Some ( idx) = self . locals [ idx_local]
750
- {
790
+ if let ProjectionElem :: Index ( idx_local) = elem {
791
+ let idx = self . local ( idx_local) ;
751
792
if let Some ( offset) = self . evaluated [ idx] . as_ref ( )
752
793
&& let Some ( offset) = self . ecx . read_target_usize ( offset) . discard_err ( )
753
794
&& let Some ( min_length) = offset. checked_add ( 1 )
@@ -783,7 +824,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
783
824
let mut place_ref = place. as_ref ( ) ;
784
825
785
826
// Invariant: `value` holds the value up-to the `index`th projection excluded.
786
- let Some ( mut value) = self . locals [ place. local ] else { return Err ( place_ref ) } ;
827
+ let mut value = self . local ( place. local ) ;
787
828
// Invariant: `value` has type `place_ty`, with optional downcast variant if needed.
788
829
let mut place_ty = PlaceTy :: from_ty ( self . local_decls [ place. local ] . ty ) ;
789
830
for ( index, proj) in place. projection . iter ( ) . enumerate ( ) {
@@ -794,7 +835,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
794
835
place_ref = PlaceRef { local, projection : & place. projection [ index..] } ;
795
836
}
796
837
797
- let Some ( proj) = proj. try_map ( |value| self . locals [ value] , |ty| ty) else {
838
+ let Some ( proj) = proj. try_map ( |value| Some ( self . local ( value) ) , |ty| ty) else {
798
839
return Err ( place_ref) ;
799
840
} ;
800
841
let Some ( ty_and_value) = self . project ( place_ty, value, proj) else {
@@ -1677,11 +1718,17 @@ impl<'tcx> VnState<'_, '_, 'tcx> {
1677
1718
/// If there is a local which is assigned `index`, and its assignment strictly dominates `loc`,
1678
1719
/// return it. If you used this local, add it to `reused_locals` to remove storage statements.
1679
1720
fn try_as_local ( & mut self , index : VnIndex , loc : Location ) -> Option < Local > {
1680
- let other = self . rev_locals . get ( index) ?;
1681
- other
1682
- . iter ( )
1683
- . find ( |& & other| self . ssa . assignment_dominates ( & self . dominators , other, loc) )
1684
- . copied ( )
1721
+ if let Some ( ssa) = self . rev_locals_ssa . get ( index)
1722
+ && let Some ( other) = ssa
1723
+ . iter ( )
1724
+ . find ( |& & other| self . ssa . assignment_dominates ( & self . dominators , other, loc) )
1725
+ {
1726
+ Some ( * other)
1727
+ } else if let Some ( non_ssa) = self . rev_locals_non_ssa . get ( & index) {
1728
+ non_ssa. first ( ) . copied ( )
1729
+ } else {
1730
+ None
1731
+ }
1685
1732
}
1686
1733
}
1687
1734
@@ -1690,11 +1737,20 @@ impl<'tcx> MutVisitor<'tcx> for VnState<'_, '_, 'tcx> {
1690
1737
self . tcx
1691
1738
}
1692
1739
1740
+ fn visit_basic_block_data ( & mut self , block : BasicBlock , bbdata : & mut BasicBlockData < ' tcx > ) {
1741
+ self . rev_locals_non_ssa . clear ( ) ;
1742
+ for local in self . locals . indices ( ) {
1743
+ if !self . ssa . is_ssa ( local) {
1744
+ self . locals [ local] = None ;
1745
+ }
1746
+ }
1747
+ self . super_basic_block_data ( block, bbdata) ;
1748
+ }
1749
+
1693
1750
fn visit_place ( & mut self , place : & mut Place < ' tcx > , context : PlaceContext , location : Location ) {
1694
1751
self . simplify_place_projection ( place, location) ;
1695
- if context. is_mutating_use ( ) && place. is_indirect ( ) {
1696
- // Non-local mutation maybe invalidate deref.
1697
- self . invalidate_derefs ( ) ;
1752
+ if context. is_mutating_use ( ) {
1753
+ self . discard_place ( * place) ;
1698
1754
}
1699
1755
self . super_place ( place, context, location) ;
1700
1756
}
@@ -1733,13 +1789,9 @@ impl<'tcx> MutVisitor<'tcx> for VnState<'_, '_, 'tcx> {
1733
1789
}
1734
1790
}
1735
1791
1736
- if lhs. is_indirect ( ) {
1737
- // Non-local mutation maybe invalidate deref.
1738
- self . invalidate_derefs ( ) ;
1739
- }
1792
+ self . discard_place ( * lhs) ;
1740
1793
1741
1794
if let Some ( local) = lhs. as_local ( )
1742
- && self . ssa . is_ssa ( local)
1743
1795
&& let rvalue_ty = rvalue. ty ( self . local_decls , self . tcx )
1744
1796
// FIXME(#112651) `rvalue` may have a subtype to `local`. We can only mark
1745
1797
// `local` as reusable if we have an exact type match.
@@ -1751,14 +1803,13 @@ impl<'tcx> MutVisitor<'tcx> for VnState<'_, '_, 'tcx> {
1751
1803
}
1752
1804
1753
1805
fn visit_terminator ( & mut self , terminator : & mut Terminator < ' tcx > , location : Location ) {
1754
- if let Terminator { kind : TerminatorKind :: Call { destination, .. } , .. } = terminator {
1755
- if let Some ( local) = destination. as_local ( )
1756
- && self . ssa . is_ssa ( local)
1757
- {
1758
- let ty = self . local_decls [ local] . ty ;
1759
- let opaque = self . new_opaque ( ty) ;
1760
- self . assign ( local, opaque) ;
1761
- }
1806
+ self . super_terminator ( terminator, location) ;
1807
+ if let Terminator { kind : TerminatorKind :: Call { destination, .. } , .. } = terminator
1808
+ && let Some ( local) = destination. as_local ( )
1809
+ {
1810
+ let ty = self . local_decls [ local] . ty ;
1811
+ let opaque = self . new_opaque ( ty) ;
1812
+ self . assign ( local, opaque) ;
1762
1813
}
1763
1814
// Function calls and ASM may invalidate (nested) derefs. We must handle them carefully.
1764
1815
// Currently, only preserving derefs for trivial terminators like SwitchInt and Goto.
@@ -1769,7 +1820,6 @@ impl<'tcx> MutVisitor<'tcx> for VnState<'_, '_, 'tcx> {
1769
1820
if !safe_to_preserve_derefs {
1770
1821
self . invalidate_derefs ( ) ;
1771
1822
}
1772
- self . super_terminator ( terminator, location) ;
1773
1823
}
1774
1824
}
1775
1825
0 commit comments