Skip to content

Commit c8327c0

Browse files
committed
Add documentation.
1 parent 4533756 commit c8327c0

File tree

1 file changed

+43
-1
lines changed
  • compiler/rustc_mir_transform/src

1 file changed

+43
-1
lines changed

compiler/rustc_mir_transform/src/gvn.rs

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,33 @@
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+
131
use rustc_data_structures::fx::{FxHashMap, FxIndexSet};
232
use rustc_data_structures::graph::dominators::Dominators;
333
use rustc_index::bit_set::BitSet;
@@ -11,7 +41,6 @@ use rustc_target::abi::{VariantIdx, FIRST_VARIANT};
1141
use crate::ssa::SsaLocals;
1242
use crate::MirPass;
1343

14-
/// Global value numbering.
1544
pub struct GVN;
1645

1746
impl<'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

315354
impl<'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<Constant<'tcx>> {
317357
if let Value::Constant(literal) = self.state.get(index) {
318358
Some(Constant { span: rustc_span::DUMMY_SP, user_ty: None, literal: literal.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

Comments
 (0)