Skip to main content

Module gvn

Module gvn 

Source
Expand description

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.

From those assignments, we construct a mapping VnIndex -> Vec<(Local, Location)> of available values, the locals in which they are stored, and the assignment location.

We traverse all assignments x = rvalue and operands.

For each SSA one, 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.

For each non-SSA 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 Rvalues based on the accumulated knowledge.

§Operational semantic

Operationally, this pass attempts to prove bitwise equality between locals. Given this MIR:

_a = some value // has VnIndex i
// some MIR
_b = some other value // also has VnIndex i

We consider it to be replaceable by:

_a = some value // has VnIndex i
// some MIR
_c = some other value // also has VnIndex i
assume(_a bitwise equal to _c) // follows from having the same VnIndex
_b = _a // follows from the `assume`

Which is simplifiable to:

_a = some value // has VnIndex i
// some MIR
_b = _a

§Handling of references

We handle references by assigning a different “provenance” index to each Ref/RawPtr 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:

_a = *_b // _b is &Freeze
_c = *_b // replaced by _c = _a

§Determinism of constant propagation

When registering a new Value, we attempt to opportunistically evaluate it as a constant. The evaluated form is inserted in evaluated as an OpTy or None if evaluation failed.

The difficulty is non-deterministic evaluation of MIR constants. Some Const can have different runtime values each time they are evaluated. This happens with valtrees that generate a new allocation each time they are used. This is checked by is_deterministic.

Meanwhile, we want to be able to read indirect constants. For instance:

static A: &'static &'static u8 = &&63;
fn foo() -> u8 {
    **A // We want to replace by 63.
}
fn bar() -> u8 {
    b"abc"[1] // We want to replace by 'b'.
}

The Value::Constant variant stores a possibly unevaluated constant. Evaluating that constant may be non-deterministic. When that happens, we assign a disambiguator to ensure that we do not merge the constants. See duplicate_slice test in gvn.rs.

Conversely, some constants cannot cross function boundaries, which could happen because of inlining. For instance, constants that contain a fn pointer (AllocId pointing to a GlobalAlloc::Function) point to a different symbol in each codegen unit. To avoid this, when writing constants in MIR, we do not write Consts that contain AllocIds. This is checked by may_have_provenance. See https://github.com/rust-lang/rust/issues/128775 for more information.

Structs§

GVN 🔒
StorageRemover 🔒
ValueSet 🔒
Stores and deduplicates pairs of (Value, Ty) into in VnIndex numbered values.
VnIndex 🔒
This represents a Value in the symbolic execution.
VnOpaque 🔒
Marker type to forbid hashing and comparing opaque values. This struct should only be constructed by ValueSet::insert_unique to ensure we use that method to create non-unifiable values. It will ICE if used in ValueSet::insert.
VnState 🔒

Enums§

AddressBase 🔒
AddressKind 🔒
Value 🔒

Functions§

is_deterministic 🔒
Return true if any evaluation of this constant in the same MIR body always returns the same value, taking into account even pointer identity tests.
may_have_provenance 🔒
Check if a constant may contain provenance information. Can return true even if there is no provenance.
op_to_prop_const 🔒