Add documentation.
This commit is contained in:
parent
8b848af325
commit
6fa55d0aff
@ -1,3 +1,33 @@
|
|||||||
|
//! Global value numbering.
|
||||||
|
//!
|
||||||
|
//! MIR may contain repeated and/or redundant computations. The objective of this pass is to detect
|
||||||
|
//! such redundancies and re-use the already-computed result when possible.
|
||||||
|
//!
|
||||||
|
//! In a first pass, we compute a symbolic representation of values that are assigned to SSA
|
||||||
|
//! locals. This symbolic representation is defined by the `Value` enum. Each produced instance of
|
||||||
|
//! `Value` is interned as a `VnIndex`, which allows us to cheaply compute identical values.
|
||||||
|
//!
|
||||||
|
//! From those assignments, we construct a mapping `VnIndex -> Vec<(Local, Location)>` of available
|
||||||
|
//! values, the locals in which they are stored, and a the assignment location.
|
||||||
|
//!
|
||||||
|
//! In a second pass, we traverse all (non SSA) assignments `x = rvalue` and operands. For each
|
||||||
|
//! one, we compute the `VnIndex` of the rvalue. If this `VnIndex` is associated to a constant, we
|
||||||
|
//! replace the rvalue/operand by that constant. Otherwise, if there is an SSA local `y`
|
||||||
|
//! associated to this `VnIndex`, and if its definition location strictly dominates the assignment
|
||||||
|
//! to `x`, we replace the assignment by `x = y`.
|
||||||
|
//!
|
||||||
|
//! By opportunity, this pass simplifies some `Rvalue`s based on the accumulated knowledge.
|
||||||
|
//!
|
||||||
|
//! # Handling of references
|
||||||
|
//!
|
||||||
|
//! We handle references by assigning a different "provenance" index to each Ref/AddressOf rvalue.
|
||||||
|
//! This ensure that we do not spuriously merge borrows that should not be merged. Meanwhile, we
|
||||||
|
//! consider all the derefs of an immutable reference to a freeze type to give the same value:
|
||||||
|
//! ```ignore (MIR)
|
||||||
|
//! _a = *_b // _b is &Freeze
|
||||||
|
//! _c = *_b // replaced by _c = _a
|
||||||
|
//! ```
|
||||||
|
|
||||||
use rustc_data_structures::fx::{FxHashMap, FxIndexSet};
|
use rustc_data_structures::fx::{FxHashMap, FxIndexSet};
|
||||||
use rustc_data_structures::graph::dominators::Dominators;
|
use rustc_data_structures::graph::dominators::Dominators;
|
||||||
use rustc_index::bit_set::BitSet;
|
use rustc_index::bit_set::BitSet;
|
||||||
@ -11,7 +41,6 @@
|
|||||||
use crate::ssa::SsaLocals;
|
use crate::ssa::SsaLocals;
|
||||||
use crate::MirPass;
|
use crate::MirPass;
|
||||||
|
|
||||||
/// Global value numbering.
|
|
||||||
pub struct GVN;
|
pub struct GVN;
|
||||||
|
|
||||||
impl<'tcx> MirPass<'tcx> for GVN {
|
impl<'tcx> MirPass<'tcx> for GVN {
|
||||||
@ -65,6 +94,9 @@ fn propagate_ssa<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
|
|||||||
replacer.visit_basic_block_data(bb, data);
|
replacer.visit_basic_block_data(bb, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// For each local that is reused (`y` above), we remove its storage statements do avoid any
|
||||||
|
// difficulty. Those locals are SSA, so should be easy to optimize by LLVM without storage
|
||||||
|
// statements.
|
||||||
StorageRemover { tcx, reused_locals: replacer.reused_locals }.visit_body_preserves_cfg(body);
|
StorageRemover { tcx, reused_locals: replacer.reused_locals }.visit_body_preserves_cfg(body);
|
||||||
|
|
||||||
if any_replacement {
|
if any_replacement {
|
||||||
@ -154,6 +186,8 @@ fn insert(&mut self, value: Value<'tcx>) -> VnIndex {
|
|||||||
VnIndex::from_usize(index)
|
VnIndex::from_usize(index)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a new `Value` for which we have no information at all, except that it is distinct
|
||||||
|
/// from all the others.
|
||||||
#[instrument(level = "trace", skip(self), ret)]
|
#[instrument(level = "trace", skip(self), ret)]
|
||||||
fn new_opaque(&mut self) -> Option<VnIndex> {
|
fn new_opaque(&mut self) -> Option<VnIndex> {
|
||||||
let next_opaque = self.next_opaque.as_mut()?;
|
let next_opaque = self.next_opaque.as_mut()?;
|
||||||
@ -162,6 +196,7 @@ fn new_opaque(&mut self) -> Option<VnIndex> {
|
|||||||
Some(self.insert(value))
|
Some(self.insert(value))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a new `Value::Address` distinct from all the others.
|
||||||
#[instrument(level = "trace", skip(self), ret)]
|
#[instrument(level = "trace", skip(self), ret)]
|
||||||
fn new_pointer(&mut self, place: Place<'tcx>) -> Option<VnIndex> {
|
fn new_pointer(&mut self, place: Place<'tcx>) -> Option<VnIndex> {
|
||||||
let next_opaque = self.next_opaque.as_mut()?;
|
let next_opaque = self.next_opaque.as_mut()?;
|
||||||
@ -174,12 +209,14 @@ fn get(&self, index: VnIndex) -> &Value<'tcx> {
|
|||||||
self.values.get_index(index.as_usize()).unwrap()
|
self.values.get_index(index.as_usize()).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Record that `local` is assigned `value`. `local` must be SSA.
|
||||||
#[instrument(level = "trace", skip(self))]
|
#[instrument(level = "trace", skip(self))]
|
||||||
fn assign(&mut self, local: Local, value: VnIndex) {
|
fn assign(&mut self, local: Local, value: VnIndex) {
|
||||||
self.locals[local] = Some(value);
|
self.locals[local] = Some(value);
|
||||||
self.rev_locals.entry(value).or_default().push(local);
|
self.rev_locals.entry(value).or_default().push(local);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Represent the *value* which would be read from `place`.
|
||||||
#[instrument(level = "trace", skip(self), ret)]
|
#[instrument(level = "trace", skip(self), ret)]
|
||||||
fn insert_place(&mut self, place: Place<'tcx>) -> Option<VnIndex> {
|
fn insert_place(&mut self, place: Place<'tcx>) -> Option<VnIndex> {
|
||||||
let mut value = self.locals[place.local]?;
|
let mut value = self.locals[place.local]?;
|
||||||
@ -308,11 +345,14 @@ struct Replacer<'a, 'tcx> {
|
|||||||
ssa: SsaLocals,
|
ssa: SsaLocals,
|
||||||
dominators: Dominators<BasicBlock>,
|
dominators: Dominators<BasicBlock>,
|
||||||
state: VnState<'a, 'tcx>,
|
state: VnState<'a, 'tcx>,
|
||||||
|
/// Set of locals that are reused, and for which we should remove storage statements to avoid a
|
||||||
|
/// use-after-StorageDead.
|
||||||
reused_locals: BitSet<Local>,
|
reused_locals: BitSet<Local>,
|
||||||
any_replacement: &'a mut bool,
|
any_replacement: &'a mut bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'tcx> Replacer<'_, 'tcx> {
|
impl<'tcx> Replacer<'_, 'tcx> {
|
||||||
|
/// If `index` is a `Value::Constant`, return the `Constant` to be put in the MIR.
|
||||||
fn try_as_constant(&mut self, index: VnIndex) -> Option<ConstOperand<'tcx>> {
|
fn try_as_constant(&mut self, index: VnIndex) -> Option<ConstOperand<'tcx>> {
|
||||||
if let Value::Constant(const_) = self.state.get(index) {
|
if let Value::Constant(const_) = self.state.get(index) {
|
||||||
Some(ConstOperand { span: rustc_span::DUMMY_SP, user_ty: None, const_: const_.clone() })
|
Some(ConstOperand { span: rustc_span::DUMMY_SP, user_ty: None, const_: const_.clone() })
|
||||||
@ -321,6 +361,8 @@ fn try_as_constant(&mut self, index: VnIndex) -> Option<ConstOperand<'tcx>> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// If there is a local which is assigned `index`, and its assignment strictly dominates `loc`,
|
||||||
|
/// return it.
|
||||||
fn try_as_local(&mut self, index: VnIndex, loc: Location) -> Option<Local> {
|
fn try_as_local(&mut self, index: VnIndex, loc: Location) -> Option<Local> {
|
||||||
let other = self.state.rev_locals.get(&index)?;
|
let other = self.state.rev_locals.get(&index)?;
|
||||||
other
|
other
|
||||||
|
Loading…
Reference in New Issue
Block a user