1+ //! Global value numbering.
2+ //!
3+ //! MIR may contain repeated and/or redundant computations. The objective of this pass is to detect
4+ //! such redunancies and re-use the already-computed result when possible.
5+ //!
6+ //! In a first pass, we compute a symbolic representation of values that are assigned to SSA
7+ //! locals. This symbolic representation is defined by the `Value` enum. Each produced instance of
8+ //! `Value` is interned as a `VnIndex`, which allows to cheaply compute identical values.
9+ //!
10+ //! From those assignments, we construct a mapping `VnIndex -> Vec<(Local, Location)>` of available
11+ //! values, the locals in which they are stored, and a the assignment location.
12+ //!
13+ //! In a second pass, we traverse all (non SSA) assignments `x = rvalue` and operands. For each
14+ //! one, we compute the `VnIndex` of the rvalue. If this `VnIndex` is associated to a constant, we
15+ //! replace the rvalue/operand by that constant. Otherwise, if there is an SSA local `y`
16+ //! associated to this `VnIndex`, and if its definition location strictly dominates the assignment
17+ //! to `x`, we replace the assignment by `x = y`.
18+ //!
19+ //! By opportunity, this pass simplifies some `Rvalue`s based on the accumulated knowledge.
20+ //!
21+ //! # Handling of references
22+ //!
23+ //! We handle references by assigning a different "provenance" index to each Ref/AddressOf rvalue.
24+ //! This ensure that we do not spuriously merge borrows that should not be merged. Meanwhile, we
25+ //! consider all the derefs of an immutable reference to a freeze type to give the same value:
26+ //! ```ignore (MIR)
27+ //! _a = *_b // _b is &Freeze
28+ //! _c = *_b // replaced by _c = _a
29+ //! ```
30+
131use rustc_data_structures:: fx:: { FxHashMap , FxIndexSet } ;
232use rustc_data_structures:: graph:: dominators:: Dominators ;
333use rustc_index:: bit_set:: BitSet ;
@@ -11,7 +41,6 @@ use rustc_target::abi::{VariantIdx, FIRST_VARIANT};
1141use crate :: ssa:: SsaLocals ;
1242use crate :: MirPass ;
1343
14- /// Global value numbering.
1544pub struct GVN ;
1645
1746impl < ' tcx > MirPass < ' tcx > for GVN {
@@ -65,6 +94,9 @@ fn propagate_ssa<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
6594 replacer. visit_basic_block_data ( bb, data) ;
6695 }
6796
97+ // For each local that is reused (`y` above), we remove its storage statements do avoid any
98+ // difficulty. Those locals are SSA, so should be easy to optimize by LLVM without storage
99+ // statements.
68100 StorageRemover { tcx, reused_locals : replacer. reused_locals } . visit_body_preserves_cfg ( body) ;
69101
70102 if any_replacement {
@@ -154,6 +186,8 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
154186 VnIndex :: from_usize ( index)
155187 }
156188
189+ /// Create a new `Value` for which we have no information at all, except that it is distinct
190+ /// from all the others.
157191 #[ instrument( level = "trace" , skip( self ) , ret) ]
158192 fn new_opaque ( & mut self ) -> Option < VnIndex > {
159193 let next_opaque = self . next_opaque . as_mut ( ) ?;
@@ -162,6 +196,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
162196 Some ( self . insert ( value) )
163197 }
164198
199+ /// Create a new `Value::Address` distinct from all the others.
165200 #[ instrument( level = "trace" , skip( self ) , ret) ]
166201 fn new_pointer ( & mut self , place : Place < ' tcx > ) -> Option < VnIndex > {
167202 let next_opaque = self . next_opaque . as_mut ( ) ?;
@@ -174,12 +209,14 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
174209 self . values . get_index ( index. as_usize ( ) ) . unwrap ( )
175210 }
176211
212+ /// Record that `local` is assigned `value`. `local` must be SSA.
177213 #[ instrument( level = "trace" , skip( self ) ) ]
178214 fn assign ( & mut self , local : Local , value : VnIndex ) {
179215 self . locals [ local] = Some ( value) ;
180216 self . rev_locals . entry ( value) . or_default ( ) . push ( local) ;
181217 }
182218
219+ /// Represent the *value* which would be read from `place`.
183220 #[ instrument( level = "trace" , skip( self ) , ret) ]
184221 fn insert_place ( & mut self , place : Place < ' tcx > ) -> Option < VnIndex > {
185222 let mut value = self . locals [ place. local ] ?;
@@ -308,11 +345,14 @@ struct Replacer<'a, 'tcx> {
308345 ssa : SsaLocals ,
309346 dominators : Dominators < BasicBlock > ,
310347 state : VnState < ' a , ' tcx > ,
348+ /// Set of locals that are reused, and for which we should remove storage statements to avoid a
349+ /// use-after-StorageDead.
311350 reused_locals : BitSet < Local > ,
312351 any_replacement : & ' a mut bool ,
313352}
314353
315354impl < ' tcx > Replacer < ' _ , ' tcx > {
355+ /// If `index` is a `Value::Constant`, return the `Constant` to be put in the MIR.
316356 fn try_as_constant ( & mut self , index : VnIndex ) -> Option < ConstOperand < ' tcx > > {
317357 if let Value :: Constant ( const_) = self . state . get ( index) {
318358 Some ( ConstOperand { span : rustc_span:: DUMMY_SP , user_ty : None , const_ : const_. clone ( ) } )
@@ -321,6 +361,8 @@ impl<'tcx> Replacer<'_, 'tcx> {
321361 }
322362 }
323363
364+ /// If there is a local which is assigned `index`, and its assignment strictly dominates `loc`,
365+ /// return it.
324366 fn try_as_local ( & mut self , index : VnIndex , loc : Location ) -> Option < Local > {
325367 let other = self . state . rev_locals . get ( & index) ?;
326368 other
0 commit comments