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 compate 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
+
1
31
use rustc_data_structures:: fx:: { FxHashMap , FxIndexSet } ;
2
32
use rustc_data_structures:: graph:: dominators:: Dominators ;
3
33
use rustc_index:: bit_set:: BitSet ;
@@ -11,7 +41,6 @@ use rustc_target::abi::{VariantIdx, FIRST_VARIANT};
11
41
use crate :: ssa:: SsaLocals ;
12
42
use crate :: MirPass ;
13
43
14
- /// Global value numbering.
15
44
pub struct GVN ;
16
45
17
46
impl < ' tcx > MirPass < ' tcx > for GVN {
@@ -65,6 +94,9 @@ fn propagate_ssa<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
65
94
replacer. visit_basic_block_data ( bb, data) ;
66
95
}
67
96
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.
68
100
StorageRemover { tcx, reused_locals : replacer. reused_locals } . visit_body_preserves_cfg ( body) ;
69
101
70
102
if any_replacement {
@@ -154,6 +186,8 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
154
186
VnIndex :: from_usize ( index)
155
187
}
156
188
189
+ /// Create a new `Value` for which we have no information at all, except that it is distinct
190
+ /// from all the others.
157
191
#[ instrument( level = "trace" , skip( self ) , ret) ]
158
192
fn new_opaque ( & mut self ) -> Option < VnIndex > {
159
193
let next_opaque = self . next_opaque . as_mut ( ) ?;
@@ -162,6 +196,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
162
196
Some ( self . insert ( value) )
163
197
}
164
198
199
+ /// Create a new `Value::Address` distinct from all the others.
165
200
#[ instrument( level = "trace" , skip( self ) , ret) ]
166
201
fn new_pointer ( & mut self , place : Place < ' tcx > ) -> Option < VnIndex > {
167
202
let next_opaque = self . next_opaque . as_mut ( ) ?;
@@ -174,12 +209,14 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
174
209
self . values . get_index ( index. as_usize ( ) ) . unwrap ( )
175
210
}
176
211
212
+ /// Record that `local` is assigned `value`. `local` must be SSA.
177
213
#[ instrument( level = "trace" , skip( self ) ) ]
178
214
fn assign ( & mut self , local : Local , value : VnIndex ) {
179
215
self . locals [ local] = Some ( value) ;
180
216
self . rev_locals . entry ( value) . or_default ( ) . push ( local) ;
181
217
}
182
218
219
+ /// Represent the *value* which would be read from `place`.
183
220
#[ instrument( level = "trace" , skip( self ) , ret) ]
184
221
fn insert_place ( & mut self , place : Place < ' tcx > ) -> Option < VnIndex > {
185
222
let mut value = self . locals [ place. local ] ?;
@@ -308,11 +345,14 @@ struct Replacer<'a, 'tcx> {
308
345
ssa : SsaLocals ,
309
346
dominators : Dominators < BasicBlock > ,
310
347
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.
311
350
reused_locals : BitSet < Local > ,
312
351
any_replacement : & ' a mut bool ,
313
352
}
314
353
315
354
impl < ' tcx > Replacer < ' _ , ' tcx > {
355
+ /// If `index` is a `Value::Constant`, return the `Constant` to be put in the MIR.
316
356
fn try_as_constant ( & mut self , index : VnIndex ) -> Option < Constant < ' tcx > > {
317
357
if let Value :: Constant ( literal) = self . state . get ( index) {
318
358
Some ( Constant { span : rustc_span:: DUMMY_SP , user_ty : None , literal : literal. clone ( ) } )
@@ -321,6 +361,8 @@ impl<'tcx> Replacer<'_, 'tcx> {
321
361
}
322
362
}
323
363
364
+ /// If there is a local which is assigned `index`, and its assignment strictly dominates `loc`,
365
+ /// return it.
324
366
fn try_as_local ( & mut self , index : VnIndex , loc : Location ) -> Option < Local > {
325
367
let other = self . state . rev_locals . get ( & index) ?;
326
368
other
0 commit comments