-
Notifications
You must be signed in to change notification settings - Fork 13.4k
some ConstValue refactoring #115764
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
some ConstValue refactoring #115764
Changes from all commits
551f481
0f8908d
430c386
7aa44ee
0a6e263
b18c0a8
04a4df5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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, | ||
}, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is really the key change, no longer forcing a |
||
// 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), | ||
}, | ||
} | ||
} | ||
|
Uh oh!
There was an error while loading. Please reload this page.