@@ -131,11 +131,6 @@ impl<'tcx> crate::MirPass<'tcx> for GVN {
131131 let mut state =
132132 VnState :: new ( tcx, body, typing_env, & ssa, dominators, & body. local_decls , & arena) ;
133133
134- for local in body. args_iter ( ) . filter ( |& local| ssa. is_ssa ( local) ) {
135- let opaque = state. new_opaque ( body. local_decls [ local] . ty ) ;
136- state. assign ( local, opaque) ;
137- }
138-
139134 let reverse_postorder = body. basic_blocks . reverse_postorder ( ) . to_vec ( ) ;
140135 for bb in reverse_postorder {
141136 let data = & mut body. basic_blocks . as_mut_preserves_cfg ( ) [ bb] ;
@@ -232,10 +227,10 @@ struct VnState<'body, 'a, 'tcx> {
232227 local_decls : & ' body LocalDecls < ' tcx > ,
233228 is_coroutine : bool ,
234229 /// Value stored in each local.
235- locals : IndexVec < Local , Option < VnIndex > > ,
230+ locals : IndexVec < Local , VnIndex > ,
236231 /// Locals that are assigned that value.
237232 // This vector does not hold all the values of `VnIndex` that we create.
238- rev_locals : IndexVec < VnIndex , SmallVec < [ Local ; 1 ] > > ,
233+ rev_locals : IndexVec < VnIndex , SmallVec < [ ( Local , Location ) ; 1 ] > > ,
239234 values : FxIndexSet < ( Value < ' a , ' tcx > , Ty < ' tcx > ) > ,
240235 /// Values evaluated as constants if possible.
241236 evaluated : IndexVec < VnIndex , Option < OpTy < ' tcx > > > ,
@@ -266,12 +261,12 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
266261 let num_values =
267262 2 * body. basic_blocks . iter ( ) . map ( |bbdata| bbdata. statements . len ( ) ) . sum :: < usize > ( )
268263 + 4 * body. basic_blocks . len ( ) ;
269- VnState {
264+ let mut this = VnState {
270265 tcx,
271266 ecx : InterpCx :: new ( tcx, DUMMY_SP , typing_env, DummyMachine ) ,
272267 local_decls,
273268 is_coroutine : body. coroutine . is_some ( ) ,
274- locals : IndexVec :: from_elem ( None , local_decls) ,
269+ locals : IndexVec :: with_capacity ( body . local_decls . len ( ) ) ,
275270 rev_locals : IndexVec :: with_capacity ( num_values) ,
276271 values : FxIndexSet :: with_capacity_and_hasher ( num_values, Default :: default ( ) ) ,
277272 evaluated : IndexVec :: with_capacity ( num_values) ,
@@ -281,7 +276,14 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
281276 dominators,
282277 reused_locals : DenseBitSet :: new_empty ( local_decls. len ( ) ) ,
283278 arena,
279+ } ;
280+ let init_loc = Location { block : START_BLOCK , statement_index : 0 } ;
281+ for decl in body. local_decls . iter ( ) {
282+ let value = this. new_opaque ( decl. ty ) ;
283+ let local = this. locals . push ( value) ;
284+ this. rev_locals [ value] . push ( ( local, init_loc) ) ;
284285 }
286+ this
285287 }
286288
287289 fn typing_env ( & self ) -> ty:: TypingEnv < ' tcx > {
@@ -335,7 +337,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
335337
336338 let mut projection = place. projection . iter ( ) ;
337339 let base = if place. is_indirect_first_projection ( ) {
338- let base = self . locals [ place. local ] ? ;
340+ let base = self . locals [ place. local ] ;
339341 // Skip the initial `Deref`.
340342 projection. next ( ) ;
341343 AddressBase :: Deref ( base)
@@ -346,7 +348,8 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
346348 let projection = self
347349 . arena
348350 . try_alloc_from_iter (
349- projection. map ( |proj| proj. try_map ( |value| self . locals [ value] , |ty| ty) . ok_or ( ( ) ) ) ,
351+ projection
352+ . map ( |proj| proj. try_map ( |value| Some ( self . locals [ value] ) , |ty| ty) . ok_or ( ( ) ) ) ,
350353 )
351354 . ok ( ) ?;
352355 let value = Value :: Address { base, projection, kind, provenance : self . next_opaque ( ) } ;
@@ -363,12 +366,39 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
363366 self . values . get_index ( index. as_usize ( ) ) . unwrap ( ) . 1
364367 }
365368
366- /// Record that `local` is assigned `value`. `local` must be SSA.
369+ /// Record that `local` is assigned `value`.
367370 #[ instrument( level = "trace" , skip( self ) ) ]
368- fn assign ( & mut self , local : Local , value : VnIndex ) {
369- debug_assert ! ( self . ssa. is_ssa( local) ) ;
370- self . locals [ local] = Some ( value) ;
371- self . rev_locals [ value] . push ( local) ;
371+ fn assign ( & mut self , local : Local , value : VnIndex , loc : Location ) {
372+ self . locals [ local] = value;
373+ self . rev_locals [ value] . push ( ( local, loc) ) ;
374+ }
375+
376+ #[ instrument( level = "trace" , skip( self ) ) ]
377+ fn discard_place ( & mut self , place : Place < ' tcx > ) {
378+ let discard_local = |this : & mut Self , local| {
379+ if this. ssa . is_ssa ( local) {
380+ return ;
381+ }
382+ let value = this. locals [ local] ;
383+ this. rev_locals [ value] . retain ( |( l, _) | * l != local) ;
384+ this. locals [ local] = this. new_opaque ( this. ty ( value) ) ;
385+ #[ cfg( debug_assertions) ]
386+ for local_vec in this. rev_locals . iter ( ) {
387+ for ( other, _) in local_vec {
388+ debug_assert_ne ! ( * other, local) ;
389+ }
390+ }
391+ } ;
392+ if place. is_indirect_first_projection ( ) {
393+ // Non-local mutation maybe invalidate deref.
394+ self . invalidate_derefs ( ) ;
395+ // Remove stored value from borrowed locals.
396+ for local in self . ssa . borrowed_locals ( ) . iter ( ) {
397+ discard_local ( self , local) ;
398+ }
399+ } else {
400+ discard_local ( self , place. local ) ;
401+ }
372402 }
373403
374404 fn insert_constant ( & mut self , value : Const < ' tcx > ) -> VnIndex {
@@ -633,7 +663,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
633663 let ( mut place_ty, mut value) = match base {
634664 // The base is a local, so we take the local's value and project from it.
635665 AddressBase :: Local ( local) => {
636- let local = self . locals [ local] ? ;
666+ let local = self . locals [ local] ;
637667 let place_ty = PlaceTy :: from_ty ( self . ty ( local) ) ;
638668 ( place_ty, local)
639669 }
@@ -744,7 +774,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
744774 // If the projection is indirect, we treat the local as a value, so can replace it with
745775 // another local.
746776 if place. is_indirect_first_projection ( )
747- && let Some ( base) = self . locals [ place. local ]
777+ && let base = self . locals [ place. local ]
748778 && let Some ( new_local) = self . try_as_local ( base, location)
749779 && place. local != new_local
750780 {
@@ -756,9 +786,8 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
756786
757787 for i in 0 ..projection. len ( ) {
758788 let elem = projection[ i] ;
759- if let ProjectionElem :: Index ( idx_local) = elem
760- && let Some ( idx) = self . locals [ idx_local]
761- {
789+ if let ProjectionElem :: Index ( idx_local) = elem {
790+ let idx = self . locals [ idx_local] ;
762791 if let Some ( offset) = self . evaluated [ idx] . as_ref ( )
763792 && let Some ( offset) = self . ecx . read_target_usize ( offset) . discard_err ( )
764793 && let Some ( min_length) = offset. checked_add ( 1 )
@@ -794,7 +823,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
794823 let mut place_ref = place. as_ref ( ) ;
795824
796825 // Invariant: `value` holds the value up-to the `index`th projection excluded.
797- let Some ( mut value) = self . locals [ place. local ] else { return Err ( place_ref ) } ;
826+ let mut value = self . locals [ place. local ] ;
798827 // Invariant: `value` has type `place_ty`, with optional downcast variant if needed.
799828 let mut place_ty = PlaceTy :: from_ty ( self . local_decls [ place. local ] . ty ) ;
800829 for ( index, proj) in place. projection . iter ( ) . enumerate ( ) {
@@ -805,7 +834,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
805834 place_ref = PlaceRef { local, projection : & place. projection [ index..] } ;
806835 }
807836
808- let Some ( proj) = proj. try_map ( |value| self . locals [ value] , |ty| ty) else {
837+ let Some ( proj) = proj. try_map ( |value| Some ( self . locals [ value] ) , |ty| ty) else {
809838 return Err ( place_ref) ;
810839 } ;
811840 let Some ( ty_and_value) = self . project ( place_ty, value, proj) else {
@@ -1017,6 +1046,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
10171046 None
10181047 }
10191048
1049+ #[ instrument( level = "trace" , skip( self ) , ret) ]
10201050 fn simplify_aggregate (
10211051 & mut self ,
10221052 lhs : & Place < ' tcx > ,
@@ -1661,6 +1691,7 @@ fn op_to_prop_const<'tcx>(
16611691impl < ' tcx > VnState < ' _ , ' _ , ' tcx > {
16621692 /// If either [`Self::try_as_constant`] as [`Self::try_as_place`] succeeds,
16631693 /// returns that result as an [`Operand`].
1694+ #[ instrument( level = "trace" , skip( self ) , ret) ]
16641695 fn try_as_operand ( & mut self , index : VnIndex , location : Location ) -> Option < Operand < ' tcx > > {
16651696 if let Some ( const_) = self . try_as_constant ( index) {
16661697 Some ( Operand :: Constant ( Box :: new ( const_) ) )
@@ -1673,6 +1704,7 @@ impl<'tcx> VnState<'_, '_, 'tcx> {
16731704 }
16741705
16751706 /// If `index` is a `Value::Constant`, return the `Constant` to be put in the MIR.
1707+ #[ instrument( level = "trace" , skip( self ) , ret) ]
16761708 fn try_as_constant ( & mut self , index : VnIndex ) -> Option < ConstOperand < ' tcx > > {
16771709 // This was already constant in MIR, do not change it. If the constant is not
16781710 // deterministic, adding an additional mention of it in MIR will not give the same value as
@@ -1730,12 +1762,17 @@ impl<'tcx> VnState<'_, '_, 'tcx> {
17301762
17311763 /// If there is a local which is assigned `index`, and its assignment strictly dominates `loc`,
17321764 /// return it. If you used this local, add it to `reused_locals` to remove storage statements.
1765+ #[ instrument( level = "trace" , skip( self ) , ret) ]
17331766 fn try_as_local ( & mut self , index : VnIndex , loc : Location ) -> Option < Local > {
17341767 let other = self . rev_locals . get ( index) ?;
17351768 other
17361769 . iter ( )
1737- . find ( |& & other| self . ssa . assignment_dominates ( & self . dominators , other, loc) )
1738- . copied ( )
1770+ . find ( |& & ( other, assign_loc) | {
1771+ self . ssa . assignment_dominates ( & self . dominators , other, loc)
1772+ || ( assign_loc. block == loc. block
1773+ && assign_loc. statement_index < loc. statement_index )
1774+ } )
1775+ . map ( |& ( other, _) | other)
17391776 }
17401777}
17411778
@@ -1744,11 +1781,25 @@ impl<'tcx> MutVisitor<'tcx> for VnState<'_, '_, 'tcx> {
17441781 self . tcx
17451782 }
17461783
1784+ fn visit_basic_block_data ( & mut self , block : BasicBlock , bbdata : & mut BasicBlockData < ' tcx > ) {
1785+ for local_set in self . rev_locals . iter_mut ( ) {
1786+ local_set. retain ( |( local, _) | self . ssa . is_ssa ( * local) ) ;
1787+ }
1788+ for local in self . locals . indices ( ) {
1789+ if !self . ssa . is_ssa ( local) {
1790+ let current = self . locals [ local] ;
1791+ let new = self . new_opaque ( self . ty ( current) ) ;
1792+ self . locals [ local] = new;
1793+ self . rev_locals [ new] . push ( ( local, Location { block, statement_index : 0 } ) ) ;
1794+ }
1795+ }
1796+ self . super_basic_block_data ( block, bbdata) ;
1797+ }
1798+
17471799 fn visit_place ( & mut self , place : & mut Place < ' tcx > , context : PlaceContext , location : Location ) {
17481800 self . simplify_place_projection ( place, location) ;
1749- if context. is_mutating_use ( ) && place. is_indirect ( ) {
1750- // Non-local mutation maybe invalidate deref.
1751- self . invalidate_derefs ( ) ;
1801+ if context. is_mutating_use ( ) {
1802+ self . discard_place ( * place) ;
17521803 }
17531804 self . super_place ( place, context, location) ;
17541805 }
@@ -1779,32 +1830,27 @@ impl<'tcx> MutVisitor<'tcx> for VnState<'_, '_, 'tcx> {
17791830 }
17801831 }
17811832
1782- if lhs. is_indirect ( ) {
1783- // Non-local mutation maybe invalidate deref.
1784- self . invalidate_derefs ( ) ;
1785- }
1833+ self . discard_place ( * lhs) ;
17861834
17871835 if let Some ( local) = lhs. as_local ( )
1788- && self . ssa . is_ssa ( local)
17891836 && let rvalue_ty = rvalue. ty ( self . local_decls , self . tcx )
17901837 // FIXME(#112651) `rvalue` may have a subtype to `local`. We can only mark
17911838 // `local` as reusable if we have an exact type match.
17921839 && self . local_decls [ local] . ty == rvalue_ty
17931840 {
17941841 let value = value. unwrap_or_else ( || self . new_opaque ( rvalue_ty) ) ;
1795- self . assign ( local, value) ;
1842+ self . assign ( local, value, location ) ;
17961843 }
17971844 }
17981845
17991846 fn visit_terminator ( & mut self , terminator : & mut Terminator < ' tcx > , location : Location ) {
1800- if let Terminator { kind : TerminatorKind :: Call { destination, .. } , .. } = terminator {
1801- if let Some ( local) = destination. as_local ( )
1802- && self . ssa . is_ssa ( local)
1803- {
1804- let ty = self . local_decls [ local] . ty ;
1805- let opaque = self . new_opaque ( ty) ;
1806- self . assign ( local, opaque) ;
1807- }
1847+ self . super_terminator ( terminator, location) ;
1848+ if let Terminator { kind : TerminatorKind :: Call { destination, .. } , .. } = terminator
1849+ && let Some ( local) = destination. as_local ( )
1850+ {
1851+ let ty = self . local_decls [ local] . ty ;
1852+ let opaque = self . new_opaque ( ty) ;
1853+ self . assign ( local, opaque, location) ;
18081854 }
18091855 // Function calls and ASM may invalidate (nested) derefs. We must handle them carefully.
18101856 // Currently, only preserving derefs for trivial terminators like SwitchInt and Goto.
@@ -1815,7 +1861,6 @@ impl<'tcx> MutVisitor<'tcx> for VnState<'_, '_, 'tcx> {
18151861 if !safe_to_preserve_derefs {
18161862 self . invalidate_derefs ( ) ;
18171863 }
1818- self . super_terminator ( terminator, location) ;
18191864 }
18201865}
18211866
0 commit comments