Skip to content

Commit c8e1653

Browse files
committed
Move methods to gvn.rs.
1 parent 1d83adb commit c8e1653

File tree

2 files changed

+63
-51
lines changed

2 files changed

+63
-51
lines changed

compiler/rustc_middle/src/mir/consts.rs

Lines changed: 0 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -181,21 +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: Option<Size>) -> bool {
187-
match *self {
188-
ConstValue::ZeroSized | ConstValue::Scalar(Scalar::Int(_)) => return false,
189-
ConstValue::Scalar(Scalar::Ptr(..)) | ConstValue::Slice { .. } => return true,
190-
ConstValue::Indirect { alloc_id, offset } => {
191-
let allocation = tcx.global_alloc(alloc_id).unwrap_memory().inner();
192-
let end = if let Some(size) = size { offset + size } else { allocation.size() };
193-
let provenance_map = allocation.provenance();
194-
!provenance_map.range_empty(AllocRange::from(offset..end), &tcx)
195-
}
196-
}
197-
}
198-
199184
/// Check if a constant only contains uninitialized bytes.
200185
pub fn all_bytes_uninit(&self, tcx: TyCtxt<'_>) -> bool {
201186
let ConstValue::Indirect { alloc_id, .. } = self else {
@@ -484,30 +469,6 @@ impl<'tcx> Const<'tcx> {
484469
let val = ConstValue::Scalar(s);
485470
Self::Val(val, ty)
486471
}
487-
488-
/// Return true if any evaluation of this constant in the same MIR body
489-
/// always returns the same value, taking into account even pointer identity tests.
490-
///
491-
/// In other words, this answers: is "cloning" the mir::ConstOperand ok?
492-
pub fn is_deterministic(&self) -> bool {
493-
// Primitive types cannot contain provenance and always have the same value.
494-
if self.ty().is_primitive() {
495-
return true;
496-
}
497-
498-
match self {
499-
// Some constants may generate fresh allocations for pointers they contain,
500-
// so using the same constant twice can yield two different results.
501-
// Notably, valtrees purposefully generate new allocations.
502-
Const::Ty(..) => false,
503-
// We do not know the contents, so don't attempt to do anything clever.
504-
Const::Unevaluated(..) => false,
505-
// When an evaluated contant contains provenance, it is encoded as an `AllocId`.
506-
// Cloning the constant will reuse the same `AllocId`. If this is in the same MIR
507-
// body, this same `AllocId` will result in the same pointer in codegen.
508-
Const::Val(..) => true,
509-
}
510-
}
511472
}
512473

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

compiler/rustc_mir_transform/src/gvn.rs

Lines changed: 63 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -61,10 +61,10 @@
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 used to be the case with
65+
//! `ConstValue::Slice` which have a new pointer each time they are evaluated, and is still the
66+
//! case with valtrees that generate a new allocation each time they are used. This is checked by
67+
//! `is_deterministic`.
6868
//!
6969
//! Meanwhile, we want to be able to read indirect constants. For instance:
7070
//! ```
@@ -81,8 +81,11 @@
8181
//! may be non-deterministic. When that happens, we assign a disambiguator to ensure that we do not
8282
//! merge the constants. See `duplicate_slice` test in `gvn.rs`.
8383
//!
84-
//! Second, when writing constants in MIR, we do not write `Const::Slice` or `Const`
85-
//! that contain `AllocId`s.
84+
//! Conversely, some constants cannot cross crate boundaries, which could happen because of
85+
//! inlining. For instance, constants that contain a fn pointer (`AllocId` pointing to a
86+
//! `GlobalAlloc::Function`) point to a different symbol in each codegen unit. To avoid this,
87+
//! when writing constants in MIR, we do not write `Const`s that contain `AllocId`s. This is
88+
//! checked by `may_have_provenance`.
8689
8790
use std::borrow::Cow;
8891
use std::hash::{Hash, Hasher};
@@ -103,7 +106,7 @@ use rustc_hir::def::DefKind;
103106
use rustc_index::bit_set::DenseBitSet;
104107
use rustc_index::{IndexVec, newtype_index};
105108
use rustc_middle::bug;
106-
use rustc_middle::mir::interpret::GlobalAlloc;
109+
use rustc_middle::mir::interpret::{AllocRange, GlobalAlloc};
107110
use rustc_middle::mir::visit::*;
108111
use rustc_middle::mir::*;
109112
use rustc_middle::ty::layout::HasTypingEnv;
@@ -486,7 +489,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
486489

487490
#[instrument(level = "trace", skip(self), ret)]
488491
fn insert_constant(&mut self, value: Const<'tcx>) -> VnIndex {
489-
let (index, new) = if value.is_deterministic() {
492+
let (index, new) = if is_deterministic(value) {
490493
// The constant is deterministic, no need to disambiguate.
491494
let constant = Value::Constant { value, disambiguator: None };
492495
self.values.insert(value.ty(), constant)
@@ -529,14 +532,14 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
529532
fn insert_bool(&mut self, flag: bool) -> VnIndex {
530533
// Booleans are deterministic.
531534
let value = Const::from_bool(self.tcx, flag);
532-
debug_assert!(value.is_deterministic());
535+
debug_assert!(is_deterministic(value));
533536
self.insert(self.tcx.types.bool, Value::Constant { value, disambiguator: None })
534537
}
535538

536539
fn insert_scalar(&mut self, ty: Ty<'tcx>, scalar: Scalar) -> VnIndex {
537540
// Scalars are deterministic.
538541
let value = Const::from_scalar(self.tcx, scalar, ty);
539-
debug_assert!(value.is_deterministic());
542+
debug_assert!(is_deterministic(value));
540543
self.insert(ty, Value::Constant { value, disambiguator: None })
541544
}
542545

@@ -1692,6 +1695,45 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
16921695
}
16931696
}
16941697

1698+
/// Return true if any evaluation of this constant in the same MIR body
1699+
/// always returns the same value, taking into account even pointer identity tests.
1700+
///
1701+
/// In other words, this answers: is "cloning" the `Const` ok?
1702+
fn is_deterministic(c: Const<'_>) -> bool {
1703+
// Primitive types cannot contain provenance and always have the same value.
1704+
if c.ty().is_primitive() {
1705+
return true;
1706+
}
1707+
1708+
match c {
1709+
// Some constants may generate fresh allocations for pointers they contain,
1710+
// so using the same constant twice can yield two different results.
1711+
// Notably, valtrees purposefully generate new allocations.
1712+
Const::Ty(..) => false,
1713+
// We do not know the contents, so don't attempt to do anything clever.
1714+
Const::Unevaluated(..) => false,
1715+
// When an evaluated constant contains provenance, it is encoded as an `AllocId`.
1716+
// Cloning the constant will reuse the same `AllocId`. If this is in the same MIR
1717+
// body, this same `AllocId` will result in the same pointer in codegen.
1718+
Const::Val(..) => true,
1719+
}
1720+
}
1721+
1722+
/// Check if a constant may contain provenance information.
1723+
/// Can return `true` even if there is no provenance.
1724+
fn may_have_provenance(tcx: TyCtxt<'_>, value: ConstValue, size: Size) -> bool {
1725+
match value {
1726+
ConstValue::ZeroSized | ConstValue::Scalar(Scalar::Int(_)) => return false,
1727+
ConstValue::Scalar(Scalar::Ptr(..)) | ConstValue::Slice { .. } => return true,
1728+
ConstValue::Indirect { alloc_id, offset } => !tcx
1729+
.global_alloc(alloc_id)
1730+
.unwrap_memory()
1731+
.inner()
1732+
.provenance()
1733+
.range_empty(AllocRange::from(offset..offset + size), &tcx),
1734+
}
1735+
}
1736+
16951737
fn op_to_prop_const<'tcx>(
16961738
ecx: &mut InterpCx<'tcx, DummyMachine>,
16971739
op: &OpTy<'tcx>,
@@ -1767,7 +1809,16 @@ fn op_to_prop_const<'tcx>(
17671809
// Check that we do not leak a pointer.
17681810
// Those pointers may lose part of their identity in codegen.
17691811
// FIXME: remove this hack once https://github.com/rust-lang/rust/issues/79738 is fixed.
1770-
if ecx.tcx.global_alloc(alloc_id).unwrap_memory().inner().provenance().ptrs().is_empty() {
1812+
if ecx
1813+
.tcx
1814+
.global_alloc(alloc_id)
1815+
.unwrap_memory()
1816+
.inner()
1817+
.provenance()
1818+
.provenances()
1819+
.next()
1820+
.is_none()
1821+
{
17711822
return Some(value);
17721823
}
17731824

@@ -1823,7 +1874,7 @@ impl<'tcx> VnState<'_, '_, 'tcx> {
18231874
// Check that we do not leak a pointer.
18241875
// Those pointers may lose part of their identity in codegen.
18251876
// FIXME: remove this hack once https://github.com/rust-lang/rust/issues/79738 is fixed.
1826-
assert!(!value.may_have_provenance(self.tcx, Some(op.layout.size)));
1877+
assert!(!may_have_provenance(self.tcx, value, op.layout.size));
18271878

18281879
Some(Const::Val(value, op.layout.ty))
18291880
}

0 commit comments

Comments
 (0)