Auto merge of #116270 - cjgillot:gvn-aggregate, r=oli-obk,RalfJung
See through aggregates in GVN This PR is extracted from https://github.com/rust-lang/rust/pull/111344 The first 2 commit are cleanups to avoid repeated work. I propose to stop removing useless assignments as part of this pass, and let a later `SimplifyLocals` do it. This makes tests easier to read (among others). The next 3 commits add a constant folding mechanism to the GVN pass, presented in https://github.com/rust-lang/rust/pull/116012. ~This pass is designed to only use global allocations, to avoid any risk of accidental modification of the stored state.~ The following commits implement opportunistic simplifications, in particular: - projections of aggregates: `MyStruct { x: a }.x` gets replaced by `a`, works with enums too; - projections of arrays: `[a, b][0]` becomes `a`; - projections of repeat expressions: `[a; N][x]` becomes `a`; - transform arrays of equal operands into a repeat rvalue. Fixes https://github.com/rust-lang/miri/issues/3090 r? `@oli-obk`
This commit is contained in:
commit
83c9732e0c
51 changed files with 4333 additions and 2622 deletions
|
@ -172,6 +172,24 @@ impl<'tcx> ConstValue<'tcx> {
|
|||
let end = end.try_into().unwrap();
|
||||
Some(data.inner().inspect_with_uninit_and_ptr_outside_interpreter(start..end))
|
||||
}
|
||||
|
||||
/// Check if a constant may contain provenance information. This is used by MIR opts.
|
||||
/// Can return `true` even if there is no provenance.
|
||||
pub fn may_have_provenance(&self, tcx: TyCtxt<'tcx>, size: Size) -> bool {
|
||||
match *self {
|
||||
ConstValue::ZeroSized | ConstValue::Scalar(Scalar::Int(_)) => return false,
|
||||
ConstValue::Scalar(Scalar::Ptr(..)) => return true,
|
||||
// It's hard to find out the part of the allocation we point to;
|
||||
// just conservatively check everything.
|
||||
ConstValue::Slice { data, meta: _ } => !data.inner().provenance().ptrs().is_empty(),
|
||||
ConstValue::Indirect { alloc_id, offset } => !tcx
|
||||
.global_alloc(alloc_id)
|
||||
.unwrap_memory()
|
||||
.inner()
|
||||
.provenance()
|
||||
.range_empty(super::AllocRange::from(offset..offset + size), &tcx),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
@ -485,6 +503,38 @@ impl<'tcx> Const<'tcx> {
|
|||
_ => Self::Ty(c),
|
||||
}
|
||||
}
|
||||
|
||||
/// Return true if any evaluation of this constant always returns the same value,
|
||||
/// taking into account even pointer identity tests.
|
||||
pub fn is_deterministic(&self) -> bool {
|
||||
// Some constants may generate fresh allocations for pointers they contain,
|
||||
// so using the same constant twice can yield two different results:
|
||||
// - valtrees purposefully generate new allocations
|
||||
// - ConstValue::Slice also generate new allocations
|
||||
match self {
|
||||
Const::Ty(c) => match c.kind() {
|
||||
ty::ConstKind::Param(..) => true,
|
||||
// A valtree may be a reference. Valtree references correspond to a
|
||||
// different allocation each time they are evaluated. Valtrees for primitive
|
||||
// types are fine though.
|
||||
ty::ConstKind::Value(_) => c.ty().is_primitive(),
|
||||
ty::ConstKind::Unevaluated(..) | ty::ConstKind::Expr(..) => false,
|
||||
// Should not appear in runtime MIR.
|
||||
ty::ConstKind::Infer(..)
|
||||
| ty::ConstKind::Bound(..)
|
||||
| ty::ConstKind::Placeholder(..)
|
||||
| ty::ConstKind::Error(..) => bug!(),
|
||||
},
|
||||
Const::Unevaluated(..) => false,
|
||||
// If the same slice appears twice in the MIR, we cannot guarantee that we will
|
||||
// give the same `AllocId` to the data.
|
||||
Const::Val(ConstValue::Slice { .. }, _) => false,
|
||||
Const::Val(
|
||||
ConstValue::ZeroSized | ConstValue::Scalar(_) | ConstValue::Indirect { .. },
|
||||
_,
|
||||
) => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An unevaluated (potentially generic) constant used in MIR.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue