Auto merge of #115764 - RalfJung:const-by-ref-alloc-id, r=oli-obk

some ConstValue refactoring

In particular, use AllocId instead of Allocation in ConstValue::ByRef. This helps avoid redundant AllocIds when a  `ByRef` constant gets put back into the interpreter.

r? `@oli-obk`

Fixes https://github.com/rust-lang/rust/issues/105536
This commit is contained in:
bors 2023-09-14 10:04:45 +00:00
commit d97e04fbfc
57 changed files with 422 additions and 220 deletions

View File

@ -3,7 +3,7 @@
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
use rustc_middle::mir::interpret::{
read_target_uint, AllocId, ConstAllocation, ConstValue, ErrorHandled, GlobalAlloc, Scalar,
read_target_uint, AllocId, ConstValue, ErrorHandled, GlobalAlloc, Scalar,
};
use cranelift_module::*;
@ -116,7 +116,7 @@ pub(crate) fn codegen_const_value<'tcx>(
}
match const_val {
ConstValue::ZeroSized => unreachable!(), // we already handles ZST above
ConstValue::ZeroSized => unreachable!(), // we already handled ZST above
ConstValue::Scalar(x) => match x {
Scalar::Int(int) => {
if fx.clif_type(layout.ty).is_some() {
@ -200,13 +200,14 @@ pub(crate) fn codegen_const_value<'tcx>(
CValue::by_val(val, layout)
}
},
ConstValue::ByRef { alloc, offset } => CValue::by_ref(
pointer_for_allocation(fx, alloc)
ConstValue::Indirect { alloc_id, offset } => CValue::by_ref(
pointer_for_allocation(fx, alloc_id)
.offset_i64(fx, i64::try_from(offset.bytes()).unwrap()),
layout,
),
ConstValue::Slice { data, start, end } => {
let ptr = pointer_for_allocation(fx, data)
let alloc_id = fx.tcx.reserve_and_set_memory_alloc(data);
let ptr = pointer_for_allocation(fx, alloc_id)
.offset_i64(fx, i64::try_from(start).unwrap())
.get_addr(fx);
let len = fx
@ -220,9 +221,9 @@ pub(crate) fn codegen_const_value<'tcx>(
fn pointer_for_allocation<'tcx>(
fx: &mut FunctionCx<'_, '_, 'tcx>,
alloc: ConstAllocation<'tcx>,
alloc_id: AllocId,
) -> crate::pointer::Pointer {
let alloc_id = fx.tcx.create_memory_alloc(alloc);
let alloc = fx.tcx.global_alloc(alloc_id).unwrap_memory();
let data_id = data_id_for_alloc_id(
&mut fx.constants_cx,
&mut *fx.module,
@ -353,6 +354,7 @@ fn define_all_allocs(tcx: TyCtxt<'_>, module: &mut dyn Module, cx: &mut Constant
unreachable!()
}
};
// FIXME: should we have a cache so we don't do this multiple times for the same `ConstAllocation`?
let data_id = *cx.anon_allocs.entry(alloc_id).or_insert_with(|| {
module.declare_anonymous_data(alloc.inner().mutability.is_mut(), false).unwrap()
});

View File

@ -172,7 +172,8 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>(
.expect("simd_shuffle idx not const");
let idx_bytes = match idx_const {
ConstValue::ByRef { alloc, offset } => {
ConstValue::Indirect { alloc_id, offset } => {
let alloc = fx.tcx.global_alloc(alloc_id).unwrap_memory();
let size = Size::from_bytes(
4 * ret_lane_count, /* size_of([u32; ret_lane_count]) */
);

View File

@ -105,7 +105,10 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
bug!("from_const: invalid ScalarPair layout: {:#?}", layout);
};
let a = Scalar::from_pointer(
Pointer::new(bx.tcx().create_memory_alloc(data), Size::from_bytes(start)),
Pointer::new(
bx.tcx().reserve_and_set_memory_alloc(data),
Size::from_bytes(start),
),
&bx.tcx(),
);
let a_llval = bx.scalar_to_backend(
@ -116,7 +119,8 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
let b_llval = bx.const_usize((end - start) as u64);
OperandValue::Pair(a_llval, b_llval)
}
ConstValue::ByRef { alloc, offset } => {
ConstValue::Indirect { alloc_id, offset } => {
let alloc = bx.tcx().global_alloc(alloc_id).unwrap_memory();
return Self::from_const_alloc(bx, layout, alloc, offset);
}
};
@ -182,6 +186,8 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
_ if layout.is_zst() => OperandRef::zero_sized(layout),
_ => {
// Neither a scalar nor scalar pair. Load from a place
// FIXME: should we cache `const_data_from_alloc` to avoid repeating this for the
// same `ConstAllocation`?
let init = bx.const_data_from_alloc(alloc);
let base_addr = bx.static_addr_of(init, alloc_align, None);

View File

@ -18,9 +18,9 @@ use super::{CanAccessStatics, CompileTimeEvalContext, CompileTimeInterpreter};
use crate::errors;
use crate::interpret::eval_nullary_intrinsic;
use crate::interpret::{
intern_const_alloc_recursive, Allocation, ConstAlloc, ConstValue, CtfeValidationMode, GlobalId,
Immediate, InternKind, InterpCx, InterpError, InterpResult, MPlaceTy, MemoryKind, OpTy,
RefTracking, StackPopCleanup,
intern_const_alloc_recursive, ConstAlloc, ConstValue, CtfeValidationMode, GlobalId, Immediate,
InternKind, InterpCx, InterpError, InterpResult, MPlaceTy, MemoryKind, OpTy, RefTracking,
StackPopCleanup,
};
// Returns a pointer to where the result lives
@ -105,91 +105,68 @@ pub(super) fn mk_eval_cx<'mir, 'tcx>(
)
}
/// This function converts an interpreter value into a constant that is meant for use in the
/// type system.
/// This function converts an interpreter value into a MIR constant.
#[instrument(skip(ecx), level = "debug")]
pub(super) fn op_to_const<'tcx>(
ecx: &CompileTimeEvalContext<'_, 'tcx>,
op: &OpTy<'tcx>,
) -> ConstValue<'tcx> {
// We do not have value optimizations for everything.
// Only scalars and slices, since they are very common.
// Note that further down we turn scalars of uninitialized bits back to `ByRef`. These can result
// from scalar unions that are initialized with one of their zero sized variants. We could
// instead allow `ConstValue::Scalar` to store `ScalarMaybeUninit`, but that would affect all
// the usual cases of extracting e.g. a `usize`, without there being a real use case for the
// `Undef` situation.
let try_as_immediate = match op.layout.abi {
// Handle ZST consistently and early.
if op.layout.is_zst() {
return ConstValue::ZeroSized;
}
// All scalar types should be stored as `ConstValue::Scalar`. This is needed to make
// `ConstValue::try_to_scalar` efficient; we want that to work for *all* constants of scalar
// type (it's used throughout the compiler and having it work just on literals is not enough)
// and we want it to be fast (i.e., don't go to an `Allocation` and reconstruct the `Scalar`
// from its byte-serialized form).
let force_as_immediate = match op.layout.abi {
Abi::Scalar(abi::Scalar::Initialized { .. }) => true,
Abi::ScalarPair(..) => match op.layout.ty.kind() {
ty::Ref(_, inner, _) => match *inner.kind() {
ty::Slice(elem) => elem == ecx.tcx.types.u8,
ty::Str => true,
_ => false,
},
_ => false,
},
// We don't *force* `ConstValue::Slice` for `ScalarPair`. This has the advantage that if the
// input `op` is a place, then turning it into a `ConstValue` and back into a `OpTy` will
// not have to generate any duplicate allocations (we preserve the original `AllocId` in
// `ConstValue::Indirect`). It means accessing the contents of a slice can be slow (since
// they can be stored as `ConstValue::Indirect`), but that's not relevant since we barely
// ever have to do this. (`try_get_slice_bytes_for_diagnostics` exists to provide this
// functionality.)
_ => false,
};
let immediate = if try_as_immediate {
let immediate = if force_as_immediate {
Right(ecx.read_immediate(op).expect("normalization works on validated constants"))
} else {
// It is guaranteed that any non-slice scalar pair is actually ByRef here.
// When we come back from raw const eval, we are always by-ref. The only way our op here is
// by-val is if we are in destructure_mir_constant, i.e., if this is (a field of) something that we
// "tried to make immediate" before. We wouldn't do that for non-slice scalar pairs or
// structs containing such.
op.as_mplace_or_imm()
};
debug!(?immediate);
// We know `offset` is relative to the allocation, so we can use `into_parts`.
let to_const_value = |mplace: &MPlaceTy<'_>| {
debug!("to_const_value(mplace: {:?})", mplace);
match mplace.ptr().into_parts() {
(Some(alloc_id), offset) => {
let alloc = ecx.tcx.global_alloc(alloc_id).unwrap_memory();
ConstValue::ByRef { alloc, offset }
}
(None, offset) => {
assert!(mplace.layout.is_zst());
assert_eq!(
offset.bytes() % mplace.layout.align.abi.bytes(),
0,
"this MPlaceTy must come from a validated constant, thus we can assume the \
alignment is correct",
);
ConstValue::ZeroSized
}
}
};
match immediate {
Left(ref mplace) => to_const_value(mplace),
// see comment on `let try_as_immediate` above
Left(ref mplace) => {
// We know `offset` is relative to the allocation, so we can use `into_parts`.
let (alloc_id, offset) = mplace.ptr().into_parts();
let alloc_id = alloc_id.expect("cannot have `fake` place fot non-ZST type");
ConstValue::Indirect { alloc_id, offset }
}
// see comment on `let force_as_immediate` above
Right(imm) => match *imm {
_ if imm.layout.is_zst() => ConstValue::ZeroSized,
Immediate::Scalar(x) => ConstValue::Scalar(x),
Immediate::ScalarPair(a, b) => {
debug!("ScalarPair(a: {:?}, b: {:?})", a, b);
// FIXME: assert that this has an appropriate type.
// Currently we actually get here for non-[u8] slices during valtree construction!
let msg = "`op_to_const` on an immediate scalar pair must only be used on slice references to actually allocated memory";
// We know `offset` is relative to the allocation, so we can use `into_parts`.
let (data, start) = match a.to_pointer(ecx).unwrap().into_parts() {
(Some(alloc_id), offset) => {
(ecx.tcx.global_alloc(alloc_id).unwrap_memory(), offset.bytes())
}
(None, _offset) => (
ecx.tcx.mk_const_alloc(Allocation::from_bytes_byte_aligned_immutable(
b"" as &[u8],
)),
0,
),
};
let len = b.to_target_usize(ecx).unwrap();
let start = start.try_into().unwrap();
// We use `ConstValue::Slice` so that we don't have to generate an allocation for
// `ConstValue::Indirect` here.
let (alloc_id, offset) = a.to_pointer(ecx).expect(msg).into_parts();
let alloc_id = alloc_id.expect(msg);
let data = ecx.tcx.global_alloc(alloc_id).unwrap_memory();
let start = offset.bytes_usize();
let len = b.to_target_usize(ecx).expect(msg);
let len: usize = len.try_into().unwrap();
ConstValue::Slice { data, start, end: start + len }
}
Immediate::Uninit => to_const_value(&op.assert_mem_place()),
Immediate::Uninit => bug!("`Uninit` is not a valid value for {}", op.layout.ty),
},
}
}

View File

@ -84,7 +84,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
)
.ok_or_else(|| err_inval!(TooGeneric))?;
let fn_ptr = self.create_fn_alloc_ptr(FnVal::Instance(instance));
let fn_ptr = self.fn_ptr(FnVal::Instance(instance));
self.write_pointer(fn_ptr, dest)?;
}
_ => span_bug!(self.cur_span(), "reify fn pointer on {:?}", src.layout.ty),
@ -116,7 +116,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
ty::ClosureKind::FnOnce,
)
.ok_or_else(|| err_inval!(TooGeneric))?;
let fn_ptr = self.create_fn_alloc_ptr(FnVal::Instance(instance));
let fn_ptr = self.fn_ptr(FnVal::Instance(instance));
self.write_pointer(fn_ptr, dest)?;
}
_ => span_bug!(self.cur_span(), "closure fn pointer on {:?}", src.layout.ty),

View File

@ -24,8 +24,8 @@ use rustc_middle::ty::{self, layout::TyAndLayout, Ty};
use rustc_ast::Mutability;
use super::{
AllocId, Allocation, ConstAllocation, InterpCx, MPlaceTy, Machine, MemoryKind, PlaceTy,
Projectable, ValueVisitor,
AllocId, Allocation, InterpCx, MPlaceTy, Machine, MemoryKind, PlaceTy, Projectable,
ValueVisitor,
};
use crate::const_eval;
use crate::errors::{DanglingPtrInFinal, UnsupportedUntypedPointer};
@ -455,7 +455,7 @@ impl<'mir, 'tcx: 'mir, M: super::intern::CompileTimeMachine<'mir, 'tcx, !>>
{
/// A helper function that allocates memory for the layout given and gives you access to mutate
/// it. Once your own mutation code is done, the backing `Allocation` is removed from the
/// current `Memory` and returned.
/// current `Memory` and interned as read-only into the global memory.
pub fn intern_with_temp_alloc(
&mut self,
layout: TyAndLayout<'tcx>,
@ -463,11 +463,15 @@ impl<'mir, 'tcx: 'mir, M: super::intern::CompileTimeMachine<'mir, 'tcx, !>>
&mut InterpCx<'mir, 'tcx, M>,
&PlaceTy<'tcx, M::Provenance>,
) -> InterpResult<'tcx, ()>,
) -> InterpResult<'tcx, ConstAllocation<'tcx>> {
) -> InterpResult<'tcx, AllocId> {
// `allocate` picks a fresh AllocId that we will associate with its data below.
let dest = self.allocate(layout, MemoryKind::Stack)?;
f(self, &dest.clone().into())?;
let mut alloc = self.memory.alloc_map.remove(&dest.ptr().provenance.unwrap()).unwrap().1;
alloc.mutability = Mutability::Not;
Ok(self.tcx.mk_const_alloc(alloc))
let alloc = self.tcx.mk_const_alloc(alloc);
let alloc_id = dest.ptr().provenance.unwrap(); // this was just allocated, it must have provenance
self.tcx.set_alloc_id_memory(alloc_id, alloc);
Ok(alloc_id)
}
}

View File

@ -555,7 +555,7 @@ pub macro compile_time_machine(<$mir: lifetime, $tcx: lifetime>) {
def_id: DefId,
) -> InterpResult<$tcx, Pointer> {
// Use the `AllocId` associated with the `DefId`. Any actual *access* will fail.
Ok(Pointer::new(ecx.tcx.create_static_alloc(def_id), Size::ZERO))
Ok(Pointer::new(ecx.tcx.reserve_and_set_static_alloc(def_id), Size::ZERO))
}
#[inline(always)]

View File

@ -176,12 +176,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
M::adjust_alloc_base_pointer(self, ptr)
}
pub fn create_fn_alloc_ptr(
&mut self,
fn_val: FnVal<'tcx, M::ExtraFnVal>,
) -> Pointer<M::Provenance> {
pub fn fn_ptr(&mut self, fn_val: FnVal<'tcx, M::ExtraFnVal>) -> Pointer<M::Provenance> {
let id = match fn_val {
FnVal::Instance(instance) => self.tcx.create_fn_alloc(instance),
FnVal::Instance(instance) => self.tcx.reserve_and_set_fn_alloc(instance),
FnVal::Other(extra) => {
// FIXME(RalfJung): Should we have a cache here?
let id = self.tcx.reserve_alloc_id();

View File

@ -756,11 +756,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
};
let layout = from_known_layout(self.tcx, self.param_env, layout, || self.layout_of(ty))?;
let op = match val_val {
ConstValue::ByRef { alloc, offset } => {
let id = self.tcx.create_memory_alloc(alloc);
ConstValue::Indirect { alloc_id, offset } => {
// We rely on mutability being set correctly in that allocation to prevent writes
// where none should happen.
let ptr = self.global_base_pointer(Pointer::new(id, offset))?;
let ptr = self.global_base_pointer(Pointer::new(alloc_id, offset))?;
Operand::Indirect(MemPlace::from_ptr(ptr.into()))
}
ConstValue::Scalar(x) => Operand::Immediate(adjust_scalar(x)?.into()),
@ -769,7 +768,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
// We rely on mutability being set correctly in `data` to prevent writes
// where none should happen.
let ptr = Pointer::new(
self.tcx.create_memory_alloc(data),
self.tcx.reserve_and_set_memory_alloc(data),
Size::from_bytes(start), // offset: `start`
);
Operand::Immediate(Immediate::new_slice(

View File

@ -27,7 +27,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
ensure_monomorphic_enough(*self.tcx, ty)?;
ensure_monomorphic_enough(*self.tcx, poly_trait_ref)?;
let vtable_symbolic_allocation = self.tcx.create_vtable_alloc(ty, poly_trait_ref);
let vtable_symbolic_allocation = self.tcx.reserve_and_set_vtable_alloc(ty, poly_trait_ref);
let vtable_ptr = self.global_base_pointer(Pointer::from(vtable_symbolic_allocation))?;
Ok(vtable_ptr.into())
}

View File

@ -149,7 +149,7 @@ pub use self::error::{
UnsupportedOpInfo, ValidationErrorInfo, ValidationErrorKind,
};
pub use self::value::{get_slice_bytes, ConstAlloc, ConstValue, Scalar};
pub use self::value::{ConstAlloc, ConstValue, Scalar};
pub use self::allocation::{
alloc_range, AllocBytes, AllocError, AllocRange, AllocResult, Allocation, ConstAllocation,
@ -389,7 +389,7 @@ impl<'s> AllocDecodingSession<'s> {
trace!("creating fn alloc ID");
let instance = ty::Instance::decode(decoder);
trace!("decoded fn alloc instance: {:?}", instance);
let alloc_id = decoder.interner().create_fn_alloc(instance);
let alloc_id = decoder.interner().reserve_and_set_fn_alloc(instance);
alloc_id
}
AllocDiscriminant::VTable => {
@ -399,7 +399,8 @@ impl<'s> AllocDecodingSession<'s> {
let poly_trait_ref =
<Option<ty::PolyExistentialTraitRef<'_>> as Decodable<D>>::decode(decoder);
trace!("decoded vtable alloc instance: {ty:?}, {poly_trait_ref:?}");
let alloc_id = decoder.interner().create_vtable_alloc(ty, poly_trait_ref);
let alloc_id =
decoder.interner().reserve_and_set_vtable_alloc(ty, poly_trait_ref);
alloc_id
}
AllocDiscriminant::Static => {
@ -407,7 +408,7 @@ impl<'s> AllocDecodingSession<'s> {
trace!("creating extern static alloc ID");
let did = <DefId as Decodable<D>>::decode(decoder);
trace!("decoded static def-ID: {:?}", did);
let alloc_id = decoder.interner().create_static_alloc(did);
let alloc_id = decoder.interner().reserve_and_set_static_alloc(did);
alloc_id
}
}
@ -544,13 +545,13 @@ impl<'tcx> TyCtxt<'tcx> {
/// Generates an `AllocId` for a static or return a cached one in case this function has been
/// called on the same static before.
pub fn create_static_alloc(self, static_id: DefId) -> AllocId {
pub fn reserve_and_set_static_alloc(self, static_id: DefId) -> AllocId {
self.reserve_and_set_dedup(GlobalAlloc::Static(static_id))
}
/// Generates an `AllocId` for a function. Depending on the function type,
/// this might get deduplicated or assigned a new ID each time.
pub fn create_fn_alloc(self, instance: Instance<'tcx>) -> AllocId {
pub fn reserve_and_set_fn_alloc(self, instance: Instance<'tcx>) -> AllocId {
// Functions cannot be identified by pointers, as asm-equal functions can get deduplicated
// by the linker (we set the "unnamed_addr" attribute for LLVM) and functions can be
// duplicated across crates.
@ -575,7 +576,7 @@ impl<'tcx> TyCtxt<'tcx> {
}
/// Generates an `AllocId` for a (symbolic, not-reified) vtable. Will get deduplicated.
pub fn create_vtable_alloc(
pub fn reserve_and_set_vtable_alloc(
self,
ty: Ty<'tcx>,
poly_trait_ref: Option<ty::PolyExistentialTraitRef<'tcx>>,
@ -588,7 +589,7 @@ impl<'tcx> TyCtxt<'tcx> {
/// Statics with identical content will still point to the same `Allocation`, i.e.,
/// their data will be deduplicated through `Allocation` interning -- but they
/// are different places in memory and as such need different IDs.
pub fn create_memory_alloc(self, mem: ConstAllocation<'tcx>) -> AllocId {
pub fn reserve_and_set_memory_alloc(self, mem: ConstAllocation<'tcx>) -> AllocId {
let id = self.reserve_alloc_id();
self.set_alloc_id_memory(id, mem);
id

View File

@ -9,10 +9,13 @@ use rustc_apfloat::{
use rustc_macros::HashStable;
use rustc_target::abi::{HasDataLayout, Size};
use crate::ty::{ParamEnv, ScalarInt, Ty, TyCtxt};
use crate::{
mir::interpret::alloc_range,
ty::{ParamEnv, ScalarInt, Ty, TyCtxt},
};
use super::{
AllocId, AllocRange, ConstAllocation, InterpResult, Pointer, PointerArithmetic, Provenance,
AllocId, ConstAllocation, InterpResult, Pointer, PointerArithmetic, Provenance,
ScalarSizeMismatch,
};
@ -30,22 +33,32 @@ pub struct ConstAlloc<'tcx> {
#[derive(Copy, Clone, Debug, Eq, PartialEq, TyEncodable, TyDecodable, Hash)]
#[derive(HashStable, Lift)]
pub enum ConstValue<'tcx> {
/// Used only for types with `layout::abi::Scalar` ABI.
/// Used for types with `layout::abi::Scalar` ABI.
///
/// Not using the enum `Value` to encode that this must not be `Uninit`.
Scalar(Scalar),
/// Only used for ZSTs.
/// Only for ZSTs.
ZeroSized,
/// Used only for `&[u8]` and `&str`
/// Used for `&[u8]` and `&str`.
///
/// This is worth an optimized representation since Rust has literals of these types.
/// Not having to indirect those through an `AllocId` (or two, if we used `Indirect`) has shown
/// measurable performance improvements on stress tests.
Slice { data: ConstAllocation<'tcx>, start: usize, end: usize },
/// A value not represented/representable by `Scalar` or `Slice`
ByRef {
/// The backing memory of the value, may contain more memory than needed for just the value
/// in order to share `ConstAllocation`s between values
alloc: ConstAllocation<'tcx>,
/// A value not representable by the other variants; needs to be stored in-memory.
///
/// Must *not* be used for scalars or ZST, but having `&str` or other slices in this variant is fine.
Indirect {
/// The backing memory of the value. May contain more memory than needed for just the value
/// if this points into some other larger ConstValue.
///
/// We use an `AllocId` here instead of a `ConstAllocation<'tcx>` to make sure that when a
/// raw constant (which is basically just an `AllocId`) is turned into a `ConstValue` and
/// back, we can preserve the original `AllocId`.
alloc_id: AllocId,
/// Offset into `alloc`
offset: Size,
},
@ -58,7 +71,7 @@ impl<'tcx> ConstValue<'tcx> {
#[inline]
pub fn try_to_scalar(&self) -> Option<Scalar<AllocId>> {
match *self {
ConstValue::ByRef { .. } | ConstValue::Slice { .. } | ConstValue::ZeroSized => None,
ConstValue::Indirect { .. } | ConstValue::Slice { .. } | ConstValue::ZeroSized => None,
ConstValue::Scalar(val) => Some(val),
}
}
@ -104,6 +117,54 @@ impl<'tcx> ConstValue<'tcx> {
pub fn from_target_usize(i: u64, cx: &impl HasDataLayout) -> Self {
ConstValue::Scalar(Scalar::from_target_usize(i, cx))
}
/// Must only be called on constants of type `&str` or `&[u8]`!
pub fn try_get_slice_bytes_for_diagnostics(&self, tcx: TyCtxt<'tcx>) -> Option<&'tcx [u8]> {
let (data, start, end) = match self {
ConstValue::Scalar(_) | ConstValue::ZeroSized => {
bug!("`try_get_slice_bytes` on non-slice constant")
}
&ConstValue::Slice { data, start, end } => (data, start, end),
&ConstValue::Indirect { alloc_id, offset } => {
// The reference itself is stored behind an indirection.
// Load the reference, and then load the actual slice contents.
let a = tcx.global_alloc(alloc_id).unwrap_memory().inner();
let ptr_size = tcx.data_layout.pointer_size;
if a.size() < offset + 2 * ptr_size {
// (partially) dangling reference
return None;
}
// Read the wide pointer components.
let ptr = a
.read_scalar(
&tcx,
alloc_range(offset, ptr_size),
/* read_provenance */ true,
)
.ok()?;
let ptr = ptr.to_pointer(&tcx).ok()?;
let len = a
.read_scalar(
&tcx,
alloc_range(offset + ptr_size, ptr_size),
/* read_provenance */ false,
)
.ok()?;
let len = len.to_target_usize(&tcx).ok()?;
let len: usize = len.try_into().ok()?;
if len == 0 {
return Some(&[]);
}
// Non-empty slice, must have memory. We know this is a relative pointer.
let (inner_alloc_id, offset) = ptr.into_parts();
let data = tcx.global_alloc(inner_alloc_id?).unwrap_memory();
(data, offset.bytes_usize(), offset.bytes_usize() + len)
}
};
// This is for diagnostics only, so we are okay to use `inspect_with_uninit_and_ptr_outside_interpreter`.
Some(data.inner().inspect_with_uninit_and_ptr_outside_interpreter(start..end))
}
}
/// A `Scalar` represents an immediate, primitive value existing outside of a
@ -505,18 +566,3 @@ impl<'tcx, Prov: Provenance> Scalar<Prov> {
Ok(Double::from_bits(self.to_u64()?.into()))
}
}
/// Gets the bytes of a constant slice value.
pub fn get_slice_bytes<'tcx>(cx: &impl HasDataLayout, val: ConstValue<'tcx>) -> &'tcx [u8] {
if let ConstValue::Slice { data, start, end } = val {
let len = end - start;
data.inner()
.get_bytes_strip_provenance(
cx,
AllocRange { start: Size::from_bytes(start), size: Size::from_bytes(len) },
)
.unwrap_or_else(|err| bug!("const slice is invalid: {:?}", err))
} else {
bug!("expected const slice, but found another const value");
}
}

View File

@ -2887,35 +2887,21 @@ fn pretty_print_const_value<'tcx>(
let u8_type = tcx.types.u8;
match (ct, ty.kind()) {
// Byte/string slices, printed as (byte) string literals.
(ConstValue::Slice { data, start, end }, ty::Ref(_, inner, _)) => {
match inner.kind() {
ty::Slice(t) => {
if *t == u8_type {
// The `inspect` here is okay since we checked the bounds, and `u8` carries
// no provenance (we have an active slice reference here). We don't use
// this result to affect interpreter execution.
let byte_str = data
.inner()
.inspect_with_uninit_and_ptr_outside_interpreter(start..end);
pretty_print_byte_str(fmt, byte_str)?;
return Ok(());
}
}
ty::Str => {
// The `inspect` here is okay since we checked the bounds, and `str` carries
// no provenance (we have an active `str` reference here). We don't use this
// result to affect interpreter execution.
let slice = data
.inner()
.inspect_with_uninit_and_ptr_outside_interpreter(start..end);
fmt.write_str(&format!("{:?}", String::from_utf8_lossy(slice)))?;
return Ok(());
}
_ => {}
(_, ty::Ref(_, inner_ty, _)) if matches!(inner_ty.kind(), ty::Str) => {
if let Some(data) = ct.try_get_slice_bytes_for_diagnostics(tcx) {
fmt.write_str(&format!("{:?}", String::from_utf8_lossy(data)))?;
return Ok(());
}
}
(ConstValue::ByRef { alloc, offset }, ty::Array(t, n)) if *t == u8_type => {
(_, ty::Ref(_, inner_ty, _)) if matches!(inner_ty.kind(), ty::Slice(t) if *t == u8_type) => {
if let Some(data) = ct.try_get_slice_bytes_for_diagnostics(tcx) {
pretty_print_byte_str(fmt, data)?;
return Ok(());
}
}
(ConstValue::Indirect { alloc_id, offset }, ty::Array(t, n)) if *t == u8_type => {
let n = n.try_to_target_usize(tcx).unwrap();
let alloc = tcx.global_alloc(alloc_id).unwrap_memory();
// cast is ok because we already checked for pointer size (32 or 64 bit) above
let range = AllocRange { start: offset, size: Size::from_bytes(n) };
let byte_str = alloc.inner().get_bytes_strip_provenance(&tcx, range).unwrap();

View File

@ -460,7 +460,7 @@ impl<'tcx> Visitor<'tcx> for ExtraComments<'tcx> {
ConstValue::ZeroSized => "<ZST>".to_string(),
ConstValue::Scalar(s) => format!("Scalar({s:?})"),
ConstValue::Slice { .. } => "Slice(..)".to_string(),
ConstValue::ByRef { .. } => "ByRef(..)".to_string(),
ConstValue::Indirect { .. } => "ByRef(..)".to_string(),
};
let fmt_valtree = |valtree: &ty::ValTree<'tcx>| match valtree {
@ -701,14 +701,18 @@ pub fn write_allocations<'tcx>(
fn alloc_ids_from_const_val(val: ConstValue<'_>) -> impl Iterator<Item = AllocId> + '_ {
match val {
ConstValue::Scalar(interpret::Scalar::Ptr(ptr, _)) => {
Either::Left(Either::Left(std::iter::once(ptr.provenance)))
Either::Left(std::iter::once(ptr.provenance))
}
ConstValue::Scalar(interpret::Scalar::Int { .. }) => {
Either::Left(Either::Right(std::iter::empty()))
ConstValue::Scalar(interpret::Scalar::Int { .. }) => Either::Right(std::iter::empty()),
ConstValue::ZeroSized => Either::Right(std::iter::empty()),
ConstValue::Slice { .. } => {
// `u8`/`str` slices, shouldn't contain pointers that we want to print.
Either::Right(std::iter::empty())
}
ConstValue::ZeroSized => Either::Left(Either::Right(std::iter::empty())),
ConstValue::ByRef { alloc, .. } | ConstValue::Slice { data: alloc, .. } => {
Either::Right(alloc_ids_from_alloc(alloc))
ConstValue::Indirect { alloc_id, .. } => {
// FIXME: we don't actually want to print all of these, since some are printed nicely directly as values inline in MIR.
// Really we'd want `pretty_print_const_value` to decide which allocations to print, instead of having a separate visitor.
Either::Left(std::iter::once(alloc_id))
}
}
}

View File

@ -647,7 +647,7 @@ impl<'tcx> TyCtxt<'tcx> {
// Create an allocation that just contains these bytes.
let alloc = interpret::Allocation::from_bytes_byte_aligned_immutable(bytes);
let alloc = self.mk_const_alloc(alloc);
self.create_memory_alloc(alloc)
self.reserve_and_set_memory_alloc(alloc)
}
/// Returns a range of the start/end indices specified with the

View File

@ -510,6 +510,7 @@ TrivialTypeTraversalAndLiftImpls! {
::rustc_span::symbol::Ident,
::rustc_errors::ErrorGuaranteed,
interpret::Scalar,
interpret::AllocId,
rustc_target::abi::Size,
ty::BoundVar,
}

View File

@ -84,7 +84,7 @@ pub(super) fn vtable_allocation_provider<'tcx>(
let scalar = match entry {
VtblEntry::MetadataDropInPlace => {
let instance = ty::Instance::resolve_drop_in_place(tcx, ty);
let fn_alloc_id = tcx.create_fn_alloc(instance);
let fn_alloc_id = tcx.reserve_and_set_fn_alloc(instance);
let fn_ptr = Pointer::from(fn_alloc_id);
Scalar::from_pointer(fn_ptr, &tcx)
}
@ -94,7 +94,7 @@ pub(super) fn vtable_allocation_provider<'tcx>(
VtblEntry::Method(instance) => {
// Prepare the fn ptr we write into the vtable.
let instance = instance.polymorphize(tcx);
let fn_alloc_id = tcx.create_fn_alloc(instance);
let fn_alloc_id = tcx.reserve_and_set_fn_alloc(instance);
let fn_ptr = Pointer::from(fn_alloc_id);
Scalar::from_pointer(fn_ptr, &tcx)
}
@ -112,5 +112,5 @@ pub(super) fn vtable_allocation_provider<'tcx>(
}
vtable.mutability = Mutability::Not;
tcx.create_memory_alloc(tcx.mk_const_alloc(vtable))
tcx.reserve_and_set_memory_alloc(tcx.mk_const_alloc(vtable))
}

View File

@ -950,7 +950,7 @@ impl<'tcx> Cx<'tcx> {
let kind = if self.tcx.is_thread_local_static(id) {
ExprKind::ThreadLocalRef(id)
} else {
let alloc_id = self.tcx.create_static_alloc(id);
let alloc_id = self.tcx.reserve_and_set_static_alloc(id);
ExprKind::StaticRef { alloc_id, ty, def_id: id }
};
ExprKind::Deref {

View File

@ -143,7 +143,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine<'mir, 'tcx>
#[inline(always)]
fn enforce_alignment(_ecx: &InterpCx<'mir, 'tcx, Self>) -> CheckAlignment {
// We do not check for alignment to avoid having to carry an `Align`
// in `ConstValue::ByRef`.
// in `ConstValue::Indirect`.
CheckAlignment::No
}
@ -535,7 +535,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
}
trace!("replacing {:?} with {:?}", place, value);
// FIXME> figure out what to do when read_immediate_raw fails
// FIXME: figure out what to do when read_immediate_raw fails
let imm = self.ecx.read_immediate_raw(&value).ok()?;
let Right(imm) = imm else { return None };
@ -544,7 +544,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
Some(ConstantKind::from_scalar(self.tcx, scalar, value.layout.ty))
}
Immediate::ScalarPair(l, r) if l.try_to_int().is_ok() && r.try_to_int().is_ok() => {
let alloc = self
let alloc_id = self
.ecx
.intern_with_temp_alloc(value.layout, |ecx, dest| {
ecx.write_immediate(*imm, dest)
@ -552,7 +552,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
.ok()?;
Some(ConstantKind::Val(
ConstValue::ByRef { alloc, offset: Size::ZERO },
ConstValue::Indirect { alloc_id, offset: Size::ZERO },
value.layout.ty,
))
}

View File

@ -114,7 +114,7 @@ impl EnumSizeOpt {
tcx.data_layout.ptr_sized_integer().align(&tcx.data_layout).abi,
Mutability::Not,
);
let alloc = tcx.create_memory_alloc(tcx.mk_const_alloc(alloc));
let alloc = tcx.reserve_and_set_memory_alloc(tcx.mk_const_alloc(alloc));
Some((*adt_def, num_discrs, *alloc_cache.entry(ty).or_insert(alloc)))
}
fn optim<'tcx>(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
@ -139,7 +139,6 @@ impl EnumSizeOpt {
let (adt_def, num_variants, alloc_id) =
self.candidate(tcx, param_env, ty, &mut alloc_cache)?;
let alloc = tcx.global_alloc(alloc_id).unwrap_memory();
let tmp_ty = Ty::new_array(tcx, tcx.types.usize, num_variants as u64);
@ -154,7 +153,7 @@ impl EnumSizeOpt {
span,
user_ty: None,
literal: ConstantKind::Val(
interpret::ConstValue::ByRef { alloc, offset: Size::ZERO },
interpret::ConstValue::Indirect { alloc_id, offset: Size::ZERO },
tmp_ty,
),
};

View File

@ -1470,8 +1470,9 @@ fn collect_const_value<'tcx>(
) {
match value {
ConstValue::Scalar(Scalar::Ptr(ptr, _size)) => collect_alloc(tcx, ptr.provenance, output),
ConstValue::Slice { data: alloc, start: _, end: _ } | ConstValue::ByRef { alloc, .. } => {
for &id in alloc.inner().provenance().ptrs().values() {
ConstValue::Indirect { alloc_id, .. } => collect_alloc(tcx, alloc_id, output),
ConstValue::Slice { data, start: _, end: _ } => {
for &id in data.inner().provenance().ptrs().values() {
collect_alloc(tcx, id, output);
}
}

View File

@ -45,7 +45,7 @@ pub fn new_allocation<'tcx>(
new_empty_allocation(align.abi)
}
ConstValue::Slice { data, start, end } => {
let alloc_id = tables.tcx.create_memory_alloc(data);
let alloc_id = tables.tcx.reserve_and_set_memory_alloc(data);
let ptr = Pointer::new(alloc_id, rustc_target::abi::Size::from_bytes(start));
let scalar_ptr = rustc_middle::mir::interpret::Scalar::from_pointer(ptr, &tables.tcx);
let scalar_len = rustc_middle::mir::interpret::Scalar::from_target_usize(
@ -72,7 +72,8 @@ pub fn new_allocation<'tcx>(
.unwrap();
allocation.stable(tables)
}
ConstValue::ByRef { alloc, offset } => {
ConstValue::Indirect { alloc_id, offset } => {
let alloc = tables.tcx.global_alloc(alloc_id).unwrap_memory();
let ty_size = tables
.tcx
.layout_of(rustc_middle::ty::ParamEnv::reveal_all().and(ty))

View File

@ -232,7 +232,7 @@ fn path_to_matched_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<Ve
cx.tcx.type_of(def_id).instantiate_identity(),
),
Res::Def(DefKind::Const, def_id) => match cx.tcx.const_eval_poly(def_id).ok()? {
ConstValue::ByRef { alloc, offset } if offset.bytes() == 0 => {
ConstValue::Indirect { alloc, offset } if offset.bytes() == 0 => {
read_mir_alloc_def_path(cx, alloc.inner(), cx.tcx.type_of(def_id).instantiate_identity())
},
_ => None,

View File

@ -671,47 +671,41 @@ pub fn miri_to_const<'tcx>(lcx: &LateContext<'tcx>, result: mir::ConstantKind<'t
ty::RawPtr(_) => Some(Constant::RawPtr(int.assert_bits(int.size()))),
_ => None,
},
mir::ConstantKind::Val(ConstValue::Slice { data, start, end }, _) => match result.ty().kind() {
ty::Ref(_, tam, _) => match tam.kind() {
ty::Str => String::from_utf8(
data.inner()
.inspect_with_uninit_and_ptr_outside_interpreter(start..end)
.to_owned(),
)
.ok()
.map(Constant::Str),
_ => None,
},
_ => None,
},
mir::ConstantKind::Val(ConstValue::ByRef { alloc, offset: _ }, _) => match result.ty().kind() {
ty::Adt(adt_def, _) if adt_def.is_struct() => Some(Constant::Adt(result)),
ty::Array(sub_type, len) => match sub_type.kind() {
ty::Float(FloatTy::F32) => match len.try_to_target_usize(lcx.tcx) {
Some(len) => alloc
.inner()
.inspect_with_uninit_and_ptr_outside_interpreter(0..(4 * usize::try_from(len).unwrap()))
.to_owned()
.array_chunks::<4>()
.map(|&chunk| Some(Constant::F32(f32::from_le_bytes(chunk))))
.collect::<Option<Vec<Constant<'tcx>>>>()
.map(Constant::Vec),
_ => None,
},
ty::Float(FloatTy::F64) => match len.try_to_target_usize(lcx.tcx) {
Some(len) => alloc
.inner()
.inspect_with_uninit_and_ptr_outside_interpreter(0..(8 * usize::try_from(len).unwrap()))
.to_owned()
.array_chunks::<8>()
.map(|&chunk| Some(Constant::F64(f64::from_le_bytes(chunk))))
.collect::<Option<Vec<Constant<'tcx>>>>()
.map(Constant::Vec),
mir::ConstantKind::Val(cv, _) if matches!(result.ty().kind(), ty::Ref(_, inner_ty, _) if matches!(inner_ty.kind(), ty::Str)) => {
let data = cv.try_get_slice_bytes_for_diagnostics(lcx.tcx)?;
String::from_utf8(data.to_owned()).ok().map(Constant::Str)
}
mir::ConstantKind::Val(ConstValue::Indirect { alloc_id, offset: _ }, _) => {
let alloc = lcx.tcx.global_alloc(alloc_id).unwrap_memory();
match result.ty().kind() {
ty::Adt(adt_def, _) if adt_def.is_struct() => Some(Constant::Adt(result)),
ty::Array(sub_type, len) => match sub_type.kind() {
ty::Float(FloatTy::F32) => match len.try_to_target_usize(lcx.tcx) {
Some(len) => alloc
.inner()
.inspect_with_uninit_and_ptr_outside_interpreter(0..(4 * usize::try_from(len).unwrap()))
.to_owned()
.array_chunks::<4>()
.map(|&chunk| Some(Constant::F32(f32::from_le_bytes(chunk))))
.collect::<Option<Vec<Constant<'tcx>>>>()
.map(Constant::Vec),
_ => None,
},
ty::Float(FloatTy::F64) => match len.try_to_target_usize(lcx.tcx) {
Some(len) => alloc
.inner()
.inspect_with_uninit_and_ptr_outside_interpreter(0..(8 * usize::try_from(len).unwrap()))
.to_owned()
.array_chunks::<8>()
.map(|&chunk| Some(Constant::F64(f64::from_le_bytes(chunk))))
.collect::<Option<Vec<Constant<'tcx>>>>()
.map(Constant::Vec),
_ => None,
},
_ => None,
},
_ => None,
},
_ => None,
}
},
_ => None,
}

View File

@ -382,7 +382,7 @@ pub fn create_ecx<'mir, 'tcx: 'mir>(
.unwrap()
.unwrap();
let main_ptr = ecx.create_fn_alloc_ptr(FnVal::Instance(entry_instance));
let main_ptr = ecx.fn_ptr(FnVal::Instance(entry_instance));
// Inlining of `DEFAULT` from
// https://github.com/rust-lang/rust/blob/master/compiler/rustc_session/src/config/sigpipe.rs.

View File

@ -711,7 +711,7 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> {
let layout = this.machine.layouts.const_raw_ptr;
let dlsym = Dlsym::from_str("signal".as_bytes(), &this.tcx.sess.target.os)?
.expect("`signal` must be an actual dlsym on android");
let ptr = this.create_fn_alloc_ptr(FnVal::Other(dlsym));
let ptr = this.fn_ptr(FnVal::Other(dlsym));
let val = ImmTy::from_scalar(Scalar::from_pointer(ptr, this), layout);
Self::alloc_extern_static(this, "signal", val)?;
// A couple zero-initialized pointer-sized extern statics.

View File

@ -63,7 +63,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
// to reconstruct the needed frame information in `handle_miri_resolve_frame`.
// Note that we never actually read or write anything from/to this pointer -
// all of the data is represented by the pointer value itself.
let fn_ptr = this.create_fn_alloc_ptr(FnVal::Instance(instance));
let fn_ptr = this.fn_ptr(FnVal::Instance(instance));
fn_ptr.wrapping_offset(Size::from_bytes(pos.0), this)
})
.collect();
@ -159,7 +159,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
// Reconstruct the original function pointer,
// which we pass to user code.
let fn_ptr = this.create_fn_alloc_ptr(FnVal::Instance(fn_instance));
let fn_ptr = this.fn_ptr(FnVal::Instance(fn_instance));
let num_fields = dest.layout.fields.count();

View File

@ -232,7 +232,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
let symbol = this.read_pointer(symbol)?;
let symbol_name = this.read_c_str(symbol)?;
if let Some(dlsym) = Dlsym::from_str(symbol_name, &this.tcx.sess.target.os)? {
let ptr = this.create_fn_alloc_ptr(FnVal::Other(dlsym));
let ptr = this.fn_ptr(FnVal::Other(dlsym));
this.write_pointer(ptr, dest)?;
} else {
this.write_null(dest)?;

View File

@ -335,7 +335,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
this.read_target_isize(hModule)?;
let name = this.read_c_str(this.read_pointer(lpProcName)?)?;
if let Some(dlsym) = Dlsym::from_str(name, &this.tcx.sess.target.os)? {
let ptr = this.create_fn_alloc_ptr(FnVal::Other(dlsym));
let ptr = this.fn_ptr(FnVal::Other(dlsym));
this.write_pointer(ptr, dest)?;
} else {
this.write_null(dest)?;

View File

@ -24,5 +24,9 @@
StorageDead(_1);
return;
}
+ }
+
+ alloc3 (size: 8, align: 4) {
+ 02 00 00 00 00 __ __ __ │ .....░░░
}

View File

@ -24,5 +24,9 @@
StorageDead(_1);
return;
}
+ }
+
+ alloc3 (size: 8, align: 4) {
+ 02 00 00 00 00 __ __ __ │ .....░░░
}

View File

@ -29,5 +29,9 @@
StorageDead(_1);
return;
}
+ }
+
+ alloc3 (size: 2, align: 1) {
+ 03 00 │ ..
}

View File

@ -29,5 +29,9 @@
StorageDead(_1);
return;
}
+ }
+
+ alloc3 (size: 2, align: 1) {
+ 03 00 │ ..
}

View File

@ -35,5 +35,9 @@
_0 = const ();
return;
}
+ }
+
+ alloc3 (size: 2, align: 1) {
+ 00 01 │ ..
}

View File

@ -35,5 +35,9 @@
_0 = const ();
return;
}
+ }
+
+ alloc3 (size: 2, align: 1) {
+ 00 01 │ ..
}

View File

@ -18,5 +18,13 @@
StorageDead(_2);
return;
}
+ }
+
+ alloc8 (size: 2, align: 1) {
+ 00 00 │ ..
+ }
+
+ alloc7 (size: 2, align: 1) {
+ 00 00 │ ..
}

View File

@ -18,5 +18,13 @@
StorageDead(_2);
return;
}
+ }
+
+ alloc8 (size: 2, align: 1) {
+ 00 00 │ ..
+ }
+
+ alloc7 (size: 2, align: 1) {
+ 00 00 │ ..
}

View File

@ -23,5 +23,17 @@
StorageDead(_2);
return;
}
+ }
+
+ alloc12 (size: 2, align: 1) {
+ 01 02 │ ..
+ }
+
+ alloc11 (size: 2, align: 1) {
+ 01 02 │ ..
+ }
+
+ alloc8 (size: 2, align: 1) {
+ 01 02 │ ..
}

View File

@ -23,5 +23,17 @@
StorageDead(_2);
return;
}
+ }
+
+ alloc12 (size: 2, align: 1) {
+ 01 02 │ ..
+ }
+
+ alloc11 (size: 2, align: 1) {
+ 01 02 │ ..
+ }
+
+ alloc8 (size: 2, align: 1) {
+ 01 02 │ ..
}

View File

@ -25,5 +25,13 @@
StorageDead(_1);
return;
}
+ }
+
+ alloc7 (size: 8, align: 4) {
+ 2a 00 00 00 63 00 00 00 │ *...c...
+ }
+
+ alloc5 (size: 8, align: 4) {
+ 2a 00 00 00 2b 00 00 00 │ *...+...
}

View File

@ -46,5 +46,9 @@
StorageDead(_1);
return;
}
+ }
+
+ alloc7 (size: 8, align: 4) {
+ 01 00 00 00 02 00 00 00 │ ........
}

View File

@ -46,5 +46,9 @@
StorageDead(_1);
return;
}
+ }
+
+ alloc7 (size: 8, align: 4) {
+ 01 00 00 00 02 00 00 00 │ ........
}

View File

@ -17,5 +17,9 @@
+ _0 = const 4_u32;
return;
}
+ }
+
+ alloc5 (size: 8, align: 4) {
+ 04 00 00 00 00 __ __ __ │ .....░░░
}

View File

@ -17,5 +17,9 @@
+ _0 = const 4_u32;
return;
}
+ }
+
+ alloc5 (size: 8, align: 4) {
+ 04 00 00 00 00 __ __ __ │ .....░░░
}

View File

@ -14,3 +14,7 @@ fn add() -> u32 {
return;
}
}
alloc5 (size: 8, align: 4) {
04 00 00 00 00 __ __ __ .....
}

View File

@ -14,3 +14,7 @@ fn add() -> u32 {
return;
}
}
alloc5 (size: 8, align: 4) {
04 00 00 00 00 __ __ __ .....
}

View File

@ -29,5 +29,17 @@
StorageDead(_1);
return;
}
+ }
+
+ alloc9 (size: 8, align: 4) {
+ 01 00 00 00 02 00 00 00 │ ........
+ }
+
+ alloc8 (size: 8, align: 4) {
+ 01 00 00 00 02 00 00 00 │ ........
+ }
+
+ alloc6 (size: 8, align: 4) {
+ 01 00 00 00 02 00 00 00 │ ........
}

View File

@ -29,5 +29,17 @@
StorageDead(_1);
return;
}
+ }
+
+ alloc9 (size: 8, align: 4) {
+ 01 00 00 00 02 00 00 00 │ ........
+ }
+
+ alloc8 (size: 8, align: 4) {
+ 01 00 00 00 02 00 00 00 │ ........
+ }
+
+ alloc6 (size: 8, align: 4) {
+ 01 00 00 00 02 00 00 00 │ ........
}

View File

@ -64,5 +64,9 @@
StorageDead(_1);
return;
}
+ }
+
+ alloc15 (size: 8, align: 4) {
+ 02 00 00 00 05 20 00 00 │ ..... ..
}

View File

@ -64,5 +64,9 @@
StorageDead(_1);
return;
}
+ }
+
+ alloc15 (size: 16, align: 8) {
+ 02 00 00 00 00 00 00 00 05 20 00 00 00 00 00 00 │ ......... ......
}

View File

@ -64,5 +64,9 @@
StorageDead(_1);
return;
}
+ }
+
+ alloc14 (size: 8, align: 4) {
+ 05 20 00 00 01 00 00 00 │ . ......
}

View File

@ -64,5 +64,9 @@
StorageDead(_1);
return;
}
+ }
+
+ alloc14 (size: 16, align: 8) {
+ 05 20 00 00 00 00 00 00 01 00 00 00 00 00 00 00 │ . ..............
}

View File

@ -55,5 +55,9 @@
StorageDead(_1);
return;
}
+ }
+
+ alloc5 (size: 8, align: 4) {
+ 04 00 00 00 00 __ __ __ │ .....░░░
}

View File

@ -55,5 +55,9 @@
StorageDead(_1);
return;
}
+ }
+
+ alloc5 (size: 8, align: 4) {
+ 04 00 00 00 00 __ __ __ │ .....░░░
}

View File

@ -55,5 +55,9 @@
StorageDead(_1);
return;
}
+ }
+
+ alloc5 (size: 8, align: 4) {
+ 04 00 00 00 00 __ __ __ │ .....░░░
}

View File

@ -55,5 +55,9 @@
StorageDead(_1);
return;
}
+ }
+
+ alloc5 (size: 8, align: 4) {
+ 04 00 00 00 00 __ __ __ │ .....░░░
}

View File

@ -0,0 +1,31 @@
// run-pass
// This does not reflect a stable guarantee (we guarantee very little for equality of pointers
// around `const`), but it would be good to understand what is happening if these assertions ever
// fail.
use std::ptr::NonNull;
use std::slice::from_raw_parts;
const PTR_U8: *const u8 = NonNull::dangling().as_ptr();
const CONST_U8_REF: &[u8] = unsafe { from_raw_parts(PTR_U8, 0) };
const CONST_U8_PTR: *const u8 = unsafe { from_raw_parts(PTR_U8, 0).as_ptr() };
static STATIC_U8_REF: &[u8] = unsafe { from_raw_parts(PTR_U8, 0) };
const PTR_U16: *const u16 = NonNull::dangling().as_ptr();
const CONST_U16_REF: &[u16] = unsafe { from_raw_parts(PTR_U16, 0) };
const fn const_u8_fn() -> &'static [u8] {
unsafe { from_raw_parts(PTR_U8, 0) }
}
fn main() {
let ptr_u8 = unsafe { from_raw_parts(PTR_U8, 0) }.as_ptr();
let ptr_u16 = unsafe { from_raw_parts(PTR_U16, 0) }.as_ptr();
assert_eq!(ptr_u8, PTR_U8);
assert_eq!(ptr_u8, CONST_U8_PTR);
assert_eq!(ptr_u8, const_u8_fn().as_ptr());
assert_eq!(ptr_u8, STATIC_U8_REF.as_ptr());
assert_eq!(ptr_u16, CONST_U16_REF.as_ptr());
assert_eq!(ptr_u8, CONST_U8_REF.as_ptr());
}