Skip to content

Commit 4712cc3

Browse files
cjgillotRalfJung
andcommitted
Move methods to gvn.rs.
Elaborate comments. Co-authored-by: Ralf Jung <post@ralfj.de>
1 parent d35719b commit 4712cc3

2 files changed

Lines changed: 70 additions & 69 deletions

File tree

compiler/rustc_middle/src/mir/consts.rs

Lines changed: 0 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -181,26 +181,6 @@ impl ConstValue {
181181
Some(data.inner().inspect_with_uninit_and_ptr_outside_interpreter(start..end))
182182
}
183183

184-
/// Check if a constant may contain provenance information. This is used by MIR opts.
185-
/// Can return `true` even if there is no provenance.
186-
pub fn may_have_provenance(&self, tcx: TyCtxt<'_>, size: Size) -> bool {
187-
match *self {
188-
ConstValue::ZeroSized | ConstValue::Scalar(Scalar::Int(_)) => return false,
189-
ConstValue::Scalar(Scalar::Ptr(..)) => return true,
190-
// It's hard to find out the part of the allocation we point to;
191-
// just conservatively check everything.
192-
ConstValue::Slice { alloc_id, meta: _ } => {
193-
!tcx.global_alloc(alloc_id).unwrap_memory().inner().provenance().ptrs().is_empty()
194-
}
195-
ConstValue::Indirect { alloc_id, offset } => !tcx
196-
.global_alloc(alloc_id)
197-
.unwrap_memory()
198-
.inner()
199-
.provenance()
200-
.range_empty(AllocRange::from(offset..offset + size), &tcx),
201-
}
202-
}
203-
204184
/// Check if a constant only contains uninitialized bytes.
205185
pub fn all_bytes_uninit(&self, tcx: TyCtxt<'_>) -> bool {
206186
let ConstValue::Indirect { alloc_id, .. } = self else {
@@ -474,39 +454,6 @@ impl<'tcx> Const<'tcx> {
474454
let val = ConstValue::Scalar(s);
475455
Self::Val(val, ty)
476456
}
477-
478-
/// Return true if any evaluation of this constant always returns the same value,
479-
/// taking into account even pointer identity tests.
480-
pub fn is_deterministic(&self) -> bool {
481-
// Primitive types cannot contain provenance and always have the same value.
482-
if self.ty().is_primitive() {
483-
return true;
484-
}
485-
486-
// Some constants may generate fresh allocations for pointers they contain,
487-
// so using the same constant twice can yield two different results.
488-
// Notably, valtrees purposefully generate new allocations.
489-
match self {
490-
Const::Ty(_, c) => match c.kind() {
491-
ty::ConstKind::Param(..) => true,
492-
// A valtree may be a reference. Valtree references correspond to a
493-
// different allocation each time they are evaluated. Valtrees for primitive
494-
// types are fine though.
495-
ty::ConstKind::Value(..)
496-
| ty::ConstKind::Expr(..)
497-
| ty::ConstKind::Unevaluated(..)
498-
// This can happen if evaluation of a constant failed. The result does not matter
499-
// much since compilation is doomed.
500-
| ty::ConstKind::Error(..) => false,
501-
// Should not appear in runtime MIR.
502-
ty::ConstKind::Infer(..)
503-
| ty::ConstKind::Bound(..)
504-
| ty::ConstKind::Placeholder(..) => bug!(),
505-
},
506-
Const::Unevaluated(..) => false,
507-
Const::Val(..) => true,
508-
}
509-
}
510457
}
511458

512459
/// An unevaluated (potentially generic) constant used in MIR.

compiler/rustc_mir_transform/src/gvn.rs

Lines changed: 70 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -61,10 +61,8 @@
6161
//! The evaluated form is inserted in `evaluated` as an `OpTy` or `None` if evaluation failed.
6262
//!
6363
//! The difficulty is non-deterministic evaluation of MIR constants. Some `Const` can have
64-
//! different runtime values each time they are evaluated. This is the case with
65-
//! `Const::Slice` which have a new pointer each time they are evaluated, and constants that
66-
//! contain a fn pointer (`AllocId` pointing to a `GlobalAlloc::Function`) pointing to a different
67-
//! symbol in each codegen unit.
64+
//! different runtime values each time they are evaluated. This happens with valtrees that
65+
//! generate a new allocation each time they are used. This is checked by `is_deterministic`.
6866
//!
6967
//! Meanwhile, we want to be able to read indirect constants. For instance:
7068
//! ```
@@ -81,8 +79,12 @@
8179
//! may be non-deterministic. When that happens, we assign a disambiguator to ensure that we do not
8280
//! merge the constants. See `duplicate_slice` test in `gvn.rs`.
8381
//!
84-
//! Second, when writing constants in MIR, we do not write `Const::Slice` or `Const`
85-
//! that contain `AllocId`s.
82+
//! Conversely, some constants cannot cross function boundaries, which could happen because of
83+
//! inlining. For instance, constants that contain a fn pointer (`AllocId` pointing to a
84+
//! `GlobalAlloc::Function`) point to a different symbol in each codegen unit. To avoid this,
85+
//! when writing constants in MIR, we do not write `Const`s that contain `AllocId`s. This is
86+
//! checked by `may_have_provenance`. See <https://github.com/rust-lang/rust/issues/128775> for
87+
//! more information.
8688
8789
use std::borrow::Cow;
8890
use std::hash::{Hash, Hasher};
@@ -103,7 +105,7 @@ use rustc_hir::def::DefKind;
103105
use rustc_index::bit_set::DenseBitSet;
104106
use rustc_index::{IndexVec, newtype_index};
105107
use rustc_middle::bug;
106-
use rustc_middle::mir::interpret::GlobalAlloc;
108+
use rustc_middle::mir::interpret::{AllocRange, GlobalAlloc};
107109
use rustc_middle::mir::visit::*;
108110
use rustc_middle::mir::*;
109111
use rustc_middle::ty::layout::HasTypingEnv;
@@ -487,7 +489,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
487489

488490
#[instrument(level = "trace", skip(self), ret)]
489491
fn insert_constant(&mut self, value: Const<'tcx>) -> VnIndex {
490-
if value.is_deterministic() {
492+
if is_deterministic(value) {
491493
// The constant is deterministic, no need to disambiguate.
492494
let constant = Value::Constant { value, disambiguator: None };
493495
self.insert(value.ty(), constant)
@@ -522,14 +524,14 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
522524
fn insert_bool(&mut self, flag: bool) -> VnIndex {
523525
// Booleans are deterministic.
524526
let value = Const::from_bool(self.tcx, flag);
525-
debug_assert!(value.is_deterministic());
527+
debug_assert!(is_deterministic(value));
526528
self.insert(self.tcx.types.bool, Value::Constant { value, disambiguator: None })
527529
}
528530

529531
fn insert_scalar(&mut self, ty: Ty<'tcx>, scalar: Scalar) -> VnIndex {
530532
// Scalars are deterministic.
531533
let value = Const::from_scalar(self.tcx, scalar, ty);
532-
debug_assert!(value.is_deterministic());
534+
debug_assert!(is_deterministic(value));
533535
self.insert(ty, Value::Constant { value, disambiguator: None })
534536
}
535537

@@ -1729,6 +1731,49 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
17291731
}
17301732
}
17311733

1734+
/// Return true if any evaluation of this constant in the same MIR body
1735+
/// always returns the same value, taking into account even pointer identity tests.
1736+
///
1737+
/// In other words, this answers: is "cloning" the `Const` ok?
1738+
///
1739+
/// This returns `false` for constants that synthesize new `AllocId` when they are instantiated.
1740+
/// It is `true` for anything else, since a given `AllocId` *does* have a unique runtime value
1741+
/// within the scope of a single MIR body.
1742+
fn is_deterministic(c: Const<'_>) -> bool {
1743+
// Primitive types cannot contain provenance and always have the same value.
1744+
if c.ty().is_primitive() {
1745+
return true;
1746+
}
1747+
1748+
match c {
1749+
// Some constants may generate fresh allocations for pointers they contain,
1750+
// so using the same constant twice can yield two different results.
1751+
// Notably, valtrees purposefully generate new allocations.
1752+
Const::Ty(..) => false,
1753+
// We do not know the contents, so don't attempt to do anything clever.
1754+
Const::Unevaluated(..) => false,
1755+
// When an evaluated constant contains provenance, it is encoded as an `AllocId`.
1756+
// Cloning the constant will reuse the same `AllocId`. If this is in the same MIR
1757+
// body, this same `AllocId` will result in the same pointer in codegen.
1758+
Const::Val(..) => true,
1759+
}
1760+
}
1761+
1762+
/// Check if a constant may contain provenance information.
1763+
/// Can return `true` even if there is no provenance.
1764+
fn may_have_provenance(tcx: TyCtxt<'_>, value: ConstValue, size: Size) -> bool {
1765+
match value {
1766+
ConstValue::ZeroSized | ConstValue::Scalar(Scalar::Int(_)) => return false,
1767+
ConstValue::Scalar(Scalar::Ptr(..)) | ConstValue::Slice { .. } => return true,
1768+
ConstValue::Indirect { alloc_id, offset } => !tcx
1769+
.global_alloc(alloc_id)
1770+
.unwrap_memory()
1771+
.inner()
1772+
.provenance()
1773+
.range_empty(AllocRange::from(offset..offset + size), &tcx),
1774+
}
1775+
}
1776+
17321777
fn op_to_prop_const<'tcx>(
17331778
ecx: &mut InterpCx<'tcx, DummyMachine>,
17341779
op: &OpTy<'tcx>,
@@ -1760,7 +1805,7 @@ fn op_to_prop_const<'tcx>(
17601805
if !scalar.try_to_scalar_int().is_ok() {
17611806
// Check that we do not leak a pointer.
17621807
// Those pointers may lose part of their identity in codegen.
1763-
// FIXME: remove this hack once https://github.com/rust-lang/rust/issues/79738 is fixed.
1808+
// FIXME: remove this hack once https://github.com/rust-lang/rust/issues/128775 is fixed.
17641809
return None;
17651810
}
17661811
return Some(ConstValue::Scalar(scalar));
@@ -1772,7 +1817,7 @@ fn op_to_prop_const<'tcx>(
17721817
let (size, _align) = ecx.size_and_align_of_val(&mplace).discard_err()??;
17731818

17741819
// Do not try interning a value that contains provenance.
1775-
// Due to https://github.com/rust-lang/rust/issues/79738, doing so could lead to bugs.
1820+
// Due to https://github.com/rust-lang/rust/issues/128775, doing so could lead to bugs.
17761821
// FIXME: remove this hack once that issue is fixed.
17771822
let alloc_ref = ecx.get_ptr_alloc(mplace.ptr(), size).discard_err()??;
17781823
if alloc_ref.has_provenance() {
@@ -1803,8 +1848,17 @@ fn op_to_prop_const<'tcx>(
18031848

18041849
// Check that we do not leak a pointer.
18051850
// Those pointers may lose part of their identity in codegen.
1806-
// FIXME: remove this hack once https://github.com/rust-lang/rust/issues/79738 is fixed.
1807-
if ecx.tcx.global_alloc(alloc_id).unwrap_memory().inner().provenance().ptrs().is_empty() {
1851+
// FIXME: remove this hack once https://github.com/rust-lang/rust/issues/128775 is fixed.
1852+
if ecx
1853+
.tcx
1854+
.global_alloc(alloc_id)
1855+
.unwrap_memory()
1856+
.inner()
1857+
.provenance()
1858+
.provenances()
1859+
.next()
1860+
.is_none()
1861+
{
18081862
return Some(value);
18091863
}
18101864

@@ -1861,8 +1915,8 @@ impl<'tcx> VnState<'_, '_, 'tcx> {
18611915

18621916
// Check that we do not leak a pointer.
18631917
// Those pointers may lose part of their identity in codegen.
1864-
// FIXME: remove this hack once https://github.com/rust-lang/rust/issues/79738 is fixed.
1865-
assert!(!value.may_have_provenance(self.tcx, op.layout.size));
1918+
// FIXME: remove this hack once https://github.com/rust-lang/rust/issues/128775 is fixed.
1919+
assert!(!may_have_provenance(self.tcx, value, op.layout.size));
18661920

18671921
Some(Const::Val(value, op.layout.ty))
18681922
}

0 commit comments

Comments
 (0)