mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-22 06:44:35 +00:00
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:
commit
d97e04fbfc
@ -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()
|
||||
});
|
||||
|
@ -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]) */
|
||||
);
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -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),
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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)]
|
||||
|
@ -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();
|
||||
|
@ -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(
|
||||
|
@ -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())
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -510,6 +510,7 @@ TrivialTypeTraversalAndLiftImpls! {
|
||||
::rustc_span::symbol::Ident,
|
||||
::rustc_errors::ErrorGuaranteed,
|
||||
interpret::Scalar,
|
||||
interpret::AllocId,
|
||||
rustc_target::abi::Size,
|
||||
ty::BoundVar,
|
||||
}
|
||||
|
@ -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))
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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,
|
||||
))
|
||||
}
|
||||
|
@ -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,
|
||||
),
|
||||
};
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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))
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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)?;
|
||||
|
@ -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)?;
|
||||
|
@ -24,5 +24,9 @@
|
||||
StorageDead(_1);
|
||||
return;
|
||||
}
|
||||
+ }
|
||||
+
|
||||
+ alloc3 (size: 8, align: 4) {
|
||||
+ 02 00 00 00 00 __ __ __ │ .....░░░
|
||||
}
|
||||
|
||||
|
@ -24,5 +24,9 @@
|
||||
StorageDead(_1);
|
||||
return;
|
||||
}
|
||||
+ }
|
||||
+
|
||||
+ alloc3 (size: 8, align: 4) {
|
||||
+ 02 00 00 00 00 __ __ __ │ .....░░░
|
||||
}
|
||||
|
||||
|
@ -29,5 +29,9 @@
|
||||
StorageDead(_1);
|
||||
return;
|
||||
}
|
||||
+ }
|
||||
+
|
||||
+ alloc3 (size: 2, align: 1) {
|
||||
+ 03 00 │ ..
|
||||
}
|
||||
|
||||
|
@ -29,5 +29,9 @@
|
||||
StorageDead(_1);
|
||||
return;
|
||||
}
|
||||
+ }
|
||||
+
|
||||
+ alloc3 (size: 2, align: 1) {
|
||||
+ 03 00 │ ..
|
||||
}
|
||||
|
||||
|
@ -35,5 +35,9 @@
|
||||
_0 = const ();
|
||||
return;
|
||||
}
|
||||
+ }
|
||||
+
|
||||
+ alloc3 (size: 2, align: 1) {
|
||||
+ 00 01 │ ..
|
||||
}
|
||||
|
||||
|
@ -35,5 +35,9 @@
|
||||
_0 = const ();
|
||||
return;
|
||||
}
|
||||
+ }
|
||||
+
|
||||
+ alloc3 (size: 2, align: 1) {
|
||||
+ 00 01 │ ..
|
||||
}
|
||||
|
||||
|
@ -18,5 +18,13 @@
|
||||
StorageDead(_2);
|
||||
return;
|
||||
}
|
||||
+ }
|
||||
+
|
||||
+ alloc8 (size: 2, align: 1) {
|
||||
+ 00 00 │ ..
|
||||
+ }
|
||||
+
|
||||
+ alloc7 (size: 2, align: 1) {
|
||||
+ 00 00 │ ..
|
||||
}
|
||||
|
||||
|
@ -18,5 +18,13 @@
|
||||
StorageDead(_2);
|
||||
return;
|
||||
}
|
||||
+ }
|
||||
+
|
||||
+ alloc8 (size: 2, align: 1) {
|
||||
+ 00 00 │ ..
|
||||
+ }
|
||||
+
|
||||
+ alloc7 (size: 2, align: 1) {
|
||||
+ 00 00 │ ..
|
||||
}
|
||||
|
||||
|
@ -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 │ ..
|
||||
}
|
||||
|
||||
|
@ -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 │ ..
|
||||
}
|
||||
|
||||
|
@ -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 │ *...+...
|
||||
}
|
||||
|
||||
|
@ -46,5 +46,9 @@
|
||||
StorageDead(_1);
|
||||
return;
|
||||
}
|
||||
+ }
|
||||
+
|
||||
+ alloc7 (size: 8, align: 4) {
|
||||
+ 01 00 00 00 02 00 00 00 │ ........
|
||||
}
|
||||
|
||||
|
@ -46,5 +46,9 @@
|
||||
StorageDead(_1);
|
||||
return;
|
||||
}
|
||||
+ }
|
||||
+
|
||||
+ alloc7 (size: 8, align: 4) {
|
||||
+ 01 00 00 00 02 00 00 00 │ ........
|
||||
}
|
||||
|
||||
|
@ -17,5 +17,9 @@
|
||||
+ _0 = const 4_u32;
|
||||
return;
|
||||
}
|
||||
+ }
|
||||
+
|
||||
+ alloc5 (size: 8, align: 4) {
|
||||
+ 04 00 00 00 00 __ __ __ │ .....░░░
|
||||
}
|
||||
|
||||
|
@ -17,5 +17,9 @@
|
||||
+ _0 = const 4_u32;
|
||||
return;
|
||||
}
|
||||
+ }
|
||||
+
|
||||
+ alloc5 (size: 8, align: 4) {
|
||||
+ 04 00 00 00 00 __ __ __ │ .....░░░
|
||||
}
|
||||
|
||||
|
@ -14,3 +14,7 @@ fn add() -> u32 {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
alloc5 (size: 8, align: 4) {
|
||||
04 00 00 00 00 __ __ __ │ .....░░░
|
||||
}
|
||||
|
@ -14,3 +14,7 @@ fn add() -> u32 {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
alloc5 (size: 8, align: 4) {
|
||||
04 00 00 00 00 __ __ __ │ .....░░░
|
||||
}
|
||||
|
@ -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 │ ........
|
||||
}
|
||||
|
||||
|
@ -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 │ ........
|
||||
}
|
||||
|
||||
|
@ -64,5 +64,9 @@
|
||||
StorageDead(_1);
|
||||
return;
|
||||
}
|
||||
+ }
|
||||
+
|
||||
+ alloc15 (size: 8, align: 4) {
|
||||
+ 02 00 00 00 05 20 00 00 │ ..... ..
|
||||
}
|
||||
|
||||
|
@ -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 │ ......... ......
|
||||
}
|
||||
|
||||
|
@ -64,5 +64,9 @@
|
||||
StorageDead(_1);
|
||||
return;
|
||||
}
|
||||
+ }
|
||||
+
|
||||
+ alloc14 (size: 8, align: 4) {
|
||||
+ 05 20 00 00 01 00 00 00 │ . ......
|
||||
}
|
||||
|
||||
|
@ -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 │ . ..............
|
||||
}
|
||||
|
||||
|
@ -55,5 +55,9 @@
|
||||
StorageDead(_1);
|
||||
return;
|
||||
}
|
||||
+ }
|
||||
+
|
||||
+ alloc5 (size: 8, align: 4) {
|
||||
+ 04 00 00 00 00 __ __ __ │ .....░░░
|
||||
}
|
||||
|
||||
|
@ -55,5 +55,9 @@
|
||||
StorageDead(_1);
|
||||
return;
|
||||
}
|
||||
+ }
|
||||
+
|
||||
+ alloc5 (size: 8, align: 4) {
|
||||
+ 04 00 00 00 00 __ __ __ │ .....░░░
|
||||
}
|
||||
|
||||
|
@ -55,5 +55,9 @@
|
||||
StorageDead(_1);
|
||||
return;
|
||||
}
|
||||
+ }
|
||||
+
|
||||
+ alloc5 (size: 8, align: 4) {
|
||||
+ 04 00 00 00 00 __ __ __ │ .....░░░
|
||||
}
|
||||
|
||||
|
@ -55,5 +55,9 @@
|
||||
StorageDead(_1);
|
||||
return;
|
||||
}
|
||||
+ }
|
||||
+
|
||||
+ alloc5 (size: 8, align: 4) {
|
||||
+ 04 00 00 00 00 __ __ __ │ .....░░░
|
||||
}
|
||||
|
||||
|
31
tests/ui/consts/issue-105536-const-val-roundtrip-ptr-eq.rs
Normal file
31
tests/ui/consts/issue-105536-const-val-roundtrip-ptr-eq.rs
Normal 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());
|
||||
}
|
Loading…
Reference in New Issue
Block a user