1 //! Functions concerning immediate values and operands, and reading from operands.
2 //! All high-level functions to read from memory work on operands as sources.
4 use std
::assert_matches
::assert_matches
;
6 use either
::{Either, Left, Right}
;
8 use rustc_hir
::def
::Namespace
;
9 use rustc_middle
::ty
::layout
::{LayoutOf, TyAndLayout}
;
10 use rustc_middle
::ty
::print
::{FmtPrinter, PrettyPrinter}
;
11 use rustc_middle
::ty
::{ConstInt, Ty, TyCtxt}
;
12 use rustc_middle
::{mir, ty}
;
13 use rustc_target
::abi
::{self, Abi, HasDataLayout, Size}
;
16 alloc_range
, from_known_layout
, mir_assign_valid_types
, AllocId
, Frame
, InterpCx
, InterpResult
,
17 MPlaceTy
, Machine
, MemPlace
, MemPlaceMeta
, OffsetMode
, PlaceTy
, Pointer
, Projectable
,
21 /// An `Immediate` represents a single immediate self-contained Rust value.
23 /// For optimization of a few very common cases, there is also a representation for a pair of
24 /// primitive values (`ScalarPair`). It allows Miri to avoid making allocations for checked binary
25 /// operations and wide pointers. This idea was taken from rustc's codegen.
26 /// In particular, thanks to `ScalarPair`, arithmetic operations and casts can be entirely
27 /// defined on `Immediate`, and do not have to work with a `Place`.
28 #[derive(Copy, Clone, Debug)]
29 pub enum Immediate
<Prov
: Provenance
= AllocId
> {
30 /// A single scalar value (must have *initialized* `Scalar` ABI).
32 /// A pair of two scalar value (must have `ScalarPair` ABI where both fields are
33 /// `Scalar::Initialized`).
34 ScalarPair(Scalar
<Prov
>, Scalar
<Prov
>),
35 /// A value of fully uninitialized memory. Can have arbitrary size and layout, but must be sized.
39 impl<Prov
: Provenance
> From
<Scalar
<Prov
>> for Immediate
<Prov
> {
41 fn from(val
: Scalar
<Prov
>) -> Self {
42 Immediate
::Scalar(val
)
46 impl<Prov
: Provenance
> Immediate
<Prov
> {
47 pub fn new_pointer_with_meta(
48 ptr
: Pointer
<Option
<Prov
>>,
49 meta
: MemPlaceMeta
<Prov
>,
50 cx
: &impl HasDataLayout
,
52 let ptr
= Scalar
::from_maybe_pointer(ptr
, cx
);
54 MemPlaceMeta
::None
=> Immediate
::from(ptr
),
55 MemPlaceMeta
::Meta(meta
) => Immediate
::ScalarPair(ptr
, meta
),
59 pub fn new_slice(ptr
: Pointer
<Option
<Prov
>>, len
: u64, cx
: &impl HasDataLayout
) -> Self {
60 Immediate
::ScalarPair(
61 Scalar
::from_maybe_pointer(ptr
, cx
),
62 Scalar
::from_target_usize(len
, cx
),
67 val
: Pointer
<Option
<Prov
>>,
68 vtable
: Pointer
<Option
<Prov
>>,
69 cx
: &impl HasDataLayout
,
71 Immediate
::ScalarPair(
72 Scalar
::from_maybe_pointer(val
, cx
),
73 Scalar
::from_maybe_pointer(vtable
, cx
),
78 #[cfg_attr(debug_assertions, track_caller)] // only in debug builds due to perf (see #98980)
79 pub fn to_scalar(self) -> Scalar
<Prov
> {
81 Immediate
::Scalar(val
) => val
,
82 Immediate
::ScalarPair(..) => bug
!("Got a scalar pair where a scalar was expected"),
83 Immediate
::Uninit
=> bug
!("Got uninit where a scalar was expected"),
88 #[cfg_attr(debug_assertions, track_caller)] // only in debug builds due to perf (see #98980)
89 pub fn to_scalar_pair(self) -> (Scalar
<Prov
>, Scalar
<Prov
>) {
91 Immediate
::ScalarPair(val1
, val2
) => (val1
, val2
),
92 Immediate
::Scalar(..) => bug
!("Got a scalar where a scalar pair was expected"),
93 Immediate
::Uninit
=> bug
!("Got uninit where a scalar pair was expected"),
98 // ScalarPair needs a type to interpret, so we often have an immediate and a type together
99 // as input for binary and cast operations.
101 pub struct ImmTy
<'tcx
, Prov
: Provenance
= AllocId
> {
102 imm
: Immediate
<Prov
>,
103 pub layout
: TyAndLayout
<'tcx
>,
106 impl<Prov
: Provenance
> std
::fmt
::Display
for ImmTy
<'_
, Prov
> {
107 fn fmt(&self, f
: &mut std
::fmt
::Formatter
<'_
>) -> std
::fmt
::Result
{
108 /// Helper function for printing a scalar to a FmtPrinter
109 fn p
<'a
, 'tcx
, Prov
: Provenance
>(
110 cx
: &mut FmtPrinter
<'a
, 'tcx
>,
113 ) -> Result
<(), std
::fmt
::Error
> {
115 Scalar
::Int(int
) => cx
.pretty_print_const_scalar_int(int
, ty
, true),
116 Scalar
::Ptr(ptr
, _sz
) => {
117 // Just print the ptr value. `pretty_print_const_scalar_ptr` would also try to
118 // print what is points to, which would fail since it has no access to the local
120 cx
.pretty_print_const_pointer(ptr
, ty
)
124 ty
::tls
::with(|tcx
| {
126 Immediate
::Scalar(s
) => {
127 if let Some(ty
) = tcx
.lift(self.layout
.ty
) {
129 FmtPrinter
::print_string(tcx
, Namespace
::ValueNS
, |cx
| p(cx
, s
, ty
))?
;
133 write
!(f
, "{:x}: {}", s
, self.layout
.ty
)
135 Immediate
::ScalarPair(a
, b
) => {
136 // FIXME(oli-obk): at least print tuples and slices nicely
137 write
!(f
, "({:x}, {:x}): {}", a
, b
, self.layout
.ty
)
139 Immediate
::Uninit
=> {
140 write
!(f
, "uninit: {}", self.layout
.ty
)
147 impl<Prov
: Provenance
> std
::fmt
::Debug
for ImmTy
<'_
, Prov
> {
148 fn fmt(&self, f
: &mut std
::fmt
::Formatter
<'_
>) -> std
::fmt
::Result
{
149 // Printing `layout` results in too much noise; just print a nice version of the type.
150 f
.debug_struct("ImmTy")
151 .field("imm", &self.imm
)
152 .field("ty", &format_args
!("{}", self.layout
.ty
))
157 impl<'tcx
, Prov
: Provenance
> std
::ops
::Deref
for ImmTy
<'tcx
, Prov
> {
158 type Target
= Immediate
<Prov
>;
160 fn deref(&self) -> &Immediate
<Prov
> {
165 impl<'tcx
, Prov
: Provenance
> ImmTy
<'tcx
, Prov
> {
167 pub fn from_scalar(val
: Scalar
<Prov
>, layout
: TyAndLayout
<'tcx
>) -> Self {
168 debug_assert
!(layout
.abi
.is_scalar(), "`ImmTy::from_scalar` on non-scalar layout");
169 ImmTy { imm: val.into(), layout }
173 pub fn from_scalar_pair(a
: Scalar
<Prov
>, b
: Scalar
<Prov
>, layout
: TyAndLayout
<'tcx
>) -> Self {
175 matches
!(layout
.abi
, Abi
::ScalarPair(..)),
176 "`ImmTy::from_scalar_pair` on non-scalar-pair layout"
178 let imm
= Immediate
::ScalarPair(a
, b
);
179 ImmTy { imm, layout }
183 pub fn from_immediate(imm
: Immediate
<Prov
>, layout
: TyAndLayout
<'tcx
>) -> Self {
185 match (imm
, layout
.abi
) {
186 (Immediate
::Scalar(..), Abi
::Scalar(..)) => true,
187 (Immediate
::ScalarPair(..), Abi
::ScalarPair(..)) => true,
188 (Immediate
::Uninit
, _
) if layout
.is_sized() => true,
191 "immediate {imm:?} does not fit to layout {layout:?}",
193 ImmTy { imm, layout }
197 pub fn uninit(layout
: TyAndLayout
<'tcx
>) -> Self {
198 debug_assert
!(layout
.is_sized(), "immediates must be sized");
199 ImmTy { imm: Immediate::Uninit, layout }
203 pub fn try_from_uint(i
: impl Into
<u128
>, layout
: TyAndLayout
<'tcx
>) -> Option
<Self> {
204 Some(Self::from_scalar(Scalar
::try_from_uint(i
, layout
.size
)?
, layout
))
207 pub fn from_uint(i
: impl Into
<u128
>, layout
: TyAndLayout
<'tcx
>) -> Self {
208 Self::from_scalar(Scalar
::from_uint(i
, layout
.size
), layout
)
212 pub fn try_from_int(i
: impl Into
<i128
>, layout
: TyAndLayout
<'tcx
>) -> Option
<Self> {
213 Some(Self::from_scalar(Scalar
::try_from_int(i
, layout
.size
)?
, layout
))
217 pub fn from_int(i
: impl Into
<i128
>, layout
: TyAndLayout
<'tcx
>) -> Self {
218 Self::from_scalar(Scalar
::from_int(i
, layout
.size
), layout
)
222 pub fn from_bool(b
: bool
, tcx
: TyCtxt
<'tcx
>) -> Self {
223 let layout
= tcx
.layout_of(ty
::ParamEnv
::reveal_all().and(tcx
.types
.bool
)).unwrap();
224 Self::from_scalar(Scalar
::from_bool(b
), layout
)
228 pub fn to_const_int(self) -> ConstInt
{
229 assert
!(self.layout
.ty
.is_integral());
230 let int
= self.to_scalar().assert_int();
231 ConstInt
::new(int
, self.layout
.ty
.is_signed(), self.layout
.ty
.is_ptr_sized_integral())
234 /// Compute the "sub-immediate" that is located within the `base` at the given offset with the
236 // Not called `offset` to avoid confusion with the trait method.
237 fn offset_(&self, offset
: Size
, layout
: TyAndLayout
<'tcx
>, cx
: &impl HasDataLayout
) -> Self {
238 debug_assert
!(layout
.is_sized(), "unsized immediates are not a thing");
239 // `ImmTy` have already been checked to be in-bounds, so we can just check directly if this
240 // remains in-bounds. This cannot actually be violated since projections are type-checked
241 // and bounds-checked.
243 offset
+ layout
.size
<= self.layout
.size
,
244 "attempting to project to field at offset {} with size {} into immediate with layout {:#?}",
249 // This makes several assumptions about what layouts we will encounter; we match what
250 // codegen does as good as we can (see `extract_field` in `rustc_codegen_ssa/src/mir/operand.rs`).
251 let inner_val
: Immediate
<_
> = match (**self, self.layout
.abi
) {
252 // if the entire value is uninit, then so is the field (can happen in ConstProp)
253 (Immediate
::Uninit
, _
) => Immediate
::Uninit
,
254 // the field contains no information, can be left uninit
255 // (Scalar/ScalarPair can contain even aligned ZST, not just 1-ZST)
256 _
if layout
.is_zst() => Immediate
::Uninit
,
257 // some fieldless enum variants can have non-zero size but still `Aggregate` ABI... try
258 // to detect those here and also give them no data
259 _
if matches
!(layout
.abi
, Abi
::Aggregate { .. }
)
260 && matches
!(&layout
.fields
, abi
::FieldsShape
::Arbitrary { offsets, .. }
if offsets
.len() == 0) =>
264 // the field covers the entire type
265 _
if layout
.size
== self.layout
.size
=> {
266 assert_eq
!(offset
.bytes(), 0);
268 match (self.layout
.abi
, layout
.abi
) {
269 (Abi
::Scalar(..), Abi
::Scalar(..)) => true,
270 (Abi
::ScalarPair(..), Abi
::ScalarPair(..)) => true,
273 "cannot project into {} immediate with equally-sized field {}\nouter ABI: {:#?}\nfield ABI: {:#?}",
281 // extract fields from types with `ScalarPair` ABI
282 (Immediate
::ScalarPair(a_val
, b_val
), Abi
::ScalarPair(a
, b
)) => {
283 assert
!(matches
!(layout
.abi
, Abi
::Scalar(..)));
284 Immediate
::from(if offset
.bytes() == 0 {
285 debug_assert_eq
!(layout
.size
, a
.size(cx
));
288 debug_assert_eq
!(offset
, a
.size(cx
).align_to(b
.align(cx
).abi
));
289 debug_assert_eq
!(layout
.size
, b
.size(cx
));
293 // everything else is a bug
294 _
=> bug
!("invalid field access on immediate {}, layout {:#?}", self, self.layout
),
297 ImmTy
::from_immediate(inner_val
, layout
)
301 impl<'tcx
, Prov
: Provenance
> Projectable
<'tcx
, Prov
> for ImmTy
<'tcx
, Prov
> {
303 fn layout(&self) -> TyAndLayout
<'tcx
> {
308 fn meta(&self) -> MemPlaceMeta
<Prov
> {
309 debug_assert
!(self.layout
.is_sized()); // unsized ImmTy can only exist temporarily and should never reach this here
313 fn offset_with_meta
<'mir
, M
: Machine
<'mir
, 'tcx
, Provenance
= Prov
>>(
317 meta
: MemPlaceMeta
<Prov
>,
318 layout
: TyAndLayout
<'tcx
>,
319 ecx
: &InterpCx
<'mir
, 'tcx
, M
>,
320 ) -> InterpResult
<'tcx
, Self> {
321 assert_matches
!(meta
, MemPlaceMeta
::None
); // we can't store this anywhere anyway
322 Ok(self.offset_(offset
, layout
, ecx
))
325 fn to_op
<'mir
, M
: Machine
<'mir
, 'tcx
, Provenance
= Prov
>>(
327 _ecx
: &InterpCx
<'mir
, 'tcx
, M
>,
328 ) -> InterpResult
<'tcx
, OpTy
<'tcx
, M
::Provenance
>> {
329 Ok(self.clone().into())
333 /// An `Operand` is the result of computing a `mir::Operand`. It can be immediate,
334 /// or still in memory. The latter is an optimization, to delay reading that chunk of
335 /// memory and to avoid having to store arbitrary-sized data here.
336 #[derive(Copy, Clone, Debug)]
337 pub(super) enum Operand
<Prov
: Provenance
= AllocId
> {
338 Immediate(Immediate
<Prov
>),
339 Indirect(MemPlace
<Prov
>),
343 pub struct OpTy
<'tcx
, Prov
: Provenance
= AllocId
> {
344 op
: Operand
<Prov
>, // Keep this private; it helps enforce invariants.
345 pub layout
: TyAndLayout
<'tcx
>,
348 impl<Prov
: Provenance
> std
::fmt
::Debug
for OpTy
<'_
, Prov
> {
349 fn fmt(&self, f
: &mut std
::fmt
::Formatter
<'_
>) -> std
::fmt
::Result
{
350 // Printing `layout` results in too much noise; just print a nice version of the type.
351 f
.debug_struct("OpTy")
352 .field("op", &self.op
)
353 .field("ty", &format_args
!("{}", self.layout
.ty
))
358 impl<'tcx
, Prov
: Provenance
> From
<ImmTy
<'tcx
, Prov
>> for OpTy
<'tcx
, Prov
> {
360 fn from(val
: ImmTy
<'tcx
, Prov
>) -> Self {
361 OpTy { op: Operand::Immediate(val.imm), layout: val.layout }
365 impl<'tcx
, Prov
: Provenance
> From
<MPlaceTy
<'tcx
, Prov
>> for OpTy
<'tcx
, Prov
> {
367 fn from(mplace
: MPlaceTy
<'tcx
, Prov
>) -> Self {
368 OpTy { op: Operand::Indirect(*mplace.mplace()), layout: mplace.layout }
372 impl<'tcx
, Prov
: Provenance
> OpTy
<'tcx
, Prov
> {
374 pub(super) fn op(&self) -> &Operand
<Prov
> {
379 impl<'tcx
, Prov
: Provenance
> Projectable
<'tcx
, Prov
> for OpTy
<'tcx
, Prov
> {
381 fn layout(&self) -> TyAndLayout
<'tcx
> {
386 fn meta(&self) -> MemPlaceMeta
<Prov
> {
387 match self.as_mplace_or_imm() {
388 Left(mplace
) => mplace
.meta(),
390 debug_assert
!(self.layout
.is_sized(), "unsized immediates are not a thing");
396 fn offset_with_meta
<'mir
, M
: Machine
<'mir
, 'tcx
, Provenance
= Prov
>>(
400 meta
: MemPlaceMeta
<Prov
>,
401 layout
: TyAndLayout
<'tcx
>,
402 ecx
: &InterpCx
<'mir
, 'tcx
, M
>,
403 ) -> InterpResult
<'tcx
, Self> {
404 match self.as_mplace_or_imm() {
405 Left(mplace
) => Ok(mplace
.offset_with_meta(offset
, mode
, meta
, layout
, ecx
)?
.into()),
407 assert_matches
!(meta
, MemPlaceMeta
::None
); // no place to store metadata here
408 // Every part of an uninit is uninit.
409 Ok(imm
.offset_(offset
, layout
, ecx
).into())
414 fn to_op
<'mir
, M
: Machine
<'mir
, 'tcx
, Provenance
= Prov
>>(
416 _ecx
: &InterpCx
<'mir
, 'tcx
, M
>,
417 ) -> InterpResult
<'tcx
, OpTy
<'tcx
, M
::Provenance
>> {
422 /// The `Readable` trait describes interpreter values that one can read from.
423 pub trait Readable
<'tcx
, Prov
: Provenance
>: Projectable
<'tcx
, Prov
> {
424 fn as_mplace_or_imm(&self) -> Either
<MPlaceTy
<'tcx
, Prov
>, ImmTy
<'tcx
, Prov
>>;
427 impl<'tcx
, Prov
: Provenance
> Readable
<'tcx
, Prov
> for OpTy
<'tcx
, Prov
> {
429 fn as_mplace_or_imm(&self) -> Either
<MPlaceTy
<'tcx
, Prov
>, ImmTy
<'tcx
, Prov
>> {
430 self.as_mplace_or_imm()
434 impl<'tcx
, Prov
: Provenance
> Readable
<'tcx
, Prov
> for MPlaceTy
<'tcx
, Prov
> {
436 fn as_mplace_or_imm(&self) -> Either
<MPlaceTy
<'tcx
, Prov
>, ImmTy
<'tcx
, Prov
>> {
441 impl<'tcx
, Prov
: Provenance
> Readable
<'tcx
, Prov
> for ImmTy
<'tcx
, Prov
> {
443 fn as_mplace_or_imm(&self) -> Either
<MPlaceTy
<'tcx
, Prov
>, ImmTy
<'tcx
, Prov
>> {
448 impl<'mir
, 'tcx
: 'mir
, M
: Machine
<'mir
, 'tcx
>> InterpCx
<'mir
, 'tcx
, M
> {
449 /// Try reading an immediate in memory; this is interesting particularly for `ScalarPair`.
450 /// Returns `None` if the layout does not permit loading this as a value.
452 /// This is an internal function; call `read_immediate` instead.
453 fn read_immediate_from_mplace_raw(
455 mplace
: &MPlaceTy
<'tcx
, M
::Provenance
>,
456 ) -> InterpResult
<'tcx
, Option
<ImmTy
<'tcx
, M
::Provenance
>>> {
457 if mplace
.layout
.is_unsized() {
458 // Don't touch unsized
462 let Some(alloc
) = self.get_place_alloc(mplace
)?
else {
463 // zero-sized type can be left uninit
464 return Ok(Some(ImmTy
::uninit(mplace
.layout
)));
467 // It may seem like all types with `Scalar` or `ScalarPair` ABI are fair game at this point.
468 // However, `MaybeUninit<u64>` is considered a `Scalar` as far as its layout is concerned --
469 // and yet cannot be represented by an interpreter `Scalar`, since we have to handle the
470 // case where some of the bytes are initialized and others are not. So, we need an extra
471 // check that walks over the type of `mplace` to make sure it is truly correct to treat this
472 // like a `Scalar` (or `ScalarPair`).
473 Ok(match mplace
.layout
.abi
{
474 Abi
::Scalar(abi
::Scalar
::Initialized { value: s, .. }
) => {
475 let size
= s
.size(self);
476 assert_eq
!(size
, mplace
.layout
.size
, "abi::Scalar size does not match layout size");
477 let scalar
= alloc
.read_scalar(
478 alloc_range(Size
::ZERO
, size
),
479 /*read_provenance*/ matches
!(s
, abi
::Pointer(_
)),
481 Some(ImmTy
::from_scalar(scalar
, mplace
.layout
))
484 abi
::Scalar
::Initialized { value: a, .. }
,
485 abi
::Scalar
::Initialized { value: b, .. }
,
487 // We checked `ptr_align` above, so all fields will have the alignment they need.
488 // We would anyway check against `ptr_align.restrict_for_offset(b_offset)`,
489 // which `ptr.offset(b_offset)` cannot possibly fail to satisfy.
490 let (a_size
, b_size
) = (a
.size(self), b
.size(self));
491 let b_offset
= a_size
.align_to(b
.align(self).abi
);
492 assert
!(b_offset
.bytes() > 0); // in `operand_field` we use the offset to tell apart the fields
493 let a_val
= alloc
.read_scalar(
494 alloc_range(Size
::ZERO
, a_size
),
495 /*read_provenance*/ matches
!(a
, abi
::Pointer(_
)),
497 let b_val
= alloc
.read_scalar(
498 alloc_range(b_offset
, b_size
),
499 /*read_provenance*/ matches
!(b
, abi
::Pointer(_
)),
501 Some(ImmTy
::from_immediate(Immediate
::ScalarPair(a_val
, b_val
), mplace
.layout
))
504 // Neither a scalar nor scalar pair.
510 /// Try returning an immediate for the operand. If the layout does not permit loading this as an
511 /// immediate, return where in memory we can find the data.
512 /// Note that for a given layout, this operation will either always return Left or Right!
513 /// succeed! Whether it returns Left depends on whether the layout can be represented
514 /// in an `Immediate`, not on which data is stored there currently.
516 /// This is an internal function that should not usually be used; call `read_immediate` instead.
517 /// ConstProp needs it, though.
518 pub fn read_immediate_raw(
520 src
: &impl Readable
<'tcx
, M
::Provenance
>,
521 ) -> InterpResult
<'tcx
, Either
<MPlaceTy
<'tcx
, M
::Provenance
>, ImmTy
<'tcx
, M
::Provenance
>>> {
522 Ok(match src
.as_mplace_or_imm() {
523 Left(ref mplace
) => {
524 if let Some(val
) = self.read_immediate_from_mplace_raw(mplace
)?
{
530 Right(val
) => Right(val
),
534 /// Read an immediate from a place, asserting that that is possible with the given layout.
536 /// If this succeeds, the `ImmTy` is never `Uninit`.
538 pub fn read_immediate(
540 op
: &impl Readable
<'tcx
, M
::Provenance
>,
541 ) -> InterpResult
<'tcx
, ImmTy
<'tcx
, M
::Provenance
>> {
544 Abi
::Scalar(abi
::Scalar
::Initialized { .. }
)
545 | Abi
::ScalarPair(abi
::Scalar
::Initialized { .. }
, abi
::Scalar
::Initialized { .. }
)
547 span_bug
!(self.cur_span(), "primitive read not possible for type: {}", op
.layout().ty
);
549 let imm
= self.read_immediate_raw(op
)?
.right().unwrap();
550 if matches
!(*imm
, Immediate
::Uninit
) {
551 throw_ub
!(InvalidUninitBytes(None
));
556 /// Read a scalar from a place
559 op
: &impl Readable
<'tcx
, M
::Provenance
>,
560 ) -> InterpResult
<'tcx
, Scalar
<M
::Provenance
>> {
561 Ok(self.read_immediate(op
)?
.to_scalar())
564 // Pointer-sized reads are fairly common and need target layout access, so we wrap them in
565 // convenience functions.
567 /// Read a pointer from a place.
570 op
: &impl Readable
<'tcx
, M
::Provenance
>,
571 ) -> InterpResult
<'tcx
, Pointer
<Option
<M
::Provenance
>>> {
572 self.read_scalar(op
)?
.to_pointer(self)
574 /// Read a pointer-sized unsigned integer from a place.
575 pub fn read_target_usize(
577 op
: &impl Readable
<'tcx
, M
::Provenance
>,
578 ) -> InterpResult
<'tcx
, u64> {
579 self.read_scalar(op
)?
.to_target_usize(self)
581 /// Read a pointer-sized signed integer from a place.
582 pub fn read_target_isize(
584 op
: &impl Readable
<'tcx
, M
::Provenance
>,
585 ) -> InterpResult
<'tcx
, i64> {
586 self.read_scalar(op
)?
.to_target_isize(self)
589 /// Turn the wide MPlace into a string (must already be dereferenced!)
590 pub fn read_str(&self, mplace
: &MPlaceTy
<'tcx
, M
::Provenance
>) -> InterpResult
<'tcx
, &str> {
591 let len
= mplace
.len(self)?
;
592 let bytes
= self.read_bytes_ptr_strip_provenance(mplace
.ptr(), Size
::from_bytes(len
))?
;
593 let str = std
::str::from_utf8(bytes
).map_err(|err
| err_ub
!(InvalidStr(err
)))?
;
597 /// Converts a repr(simd) operand into an operand where `place_index` accesses the SIMD elements.
598 /// Also returns the number of elements.
600 /// Can (but does not always) trigger UB if `op` is uninitialized.
601 pub fn operand_to_simd(
603 op
: &OpTy
<'tcx
, M
::Provenance
>,
604 ) -> InterpResult
<'tcx
, (MPlaceTy
<'tcx
, M
::Provenance
>, u64)> {
605 // Basically we just transmute this place into an array following simd_size_and_type.
606 // This only works in memory, but repr(simd) types should never be immediates anyway.
607 assert
!(op
.layout
.ty
.is_simd());
608 match op
.as_mplace_or_imm() {
609 Left(mplace
) => self.mplace_to_simd(&mplace
),
610 Right(imm
) => match *imm
{
611 Immediate
::Uninit
=> {
612 throw_ub
!(InvalidUninitBytes(None
))
614 Immediate
::Scalar(..) | Immediate
::ScalarPair(..) => {
615 bug
!("arrays/slices can never have Scalar/ScalarPair layout")
621 /// Read from a local.
622 /// Will not access memory, instead an indirect `Operand` is returned.
624 /// This is public because it is used by [priroda](https://github.com/oli-obk/priroda) to get an
625 /// OpTy from a local.
628 frame
: &Frame
<'mir
, 'tcx
, M
::Provenance
, M
::FrameExtra
>,
630 layout
: Option
<TyAndLayout
<'tcx
>>,
631 ) -> InterpResult
<'tcx
, OpTy
<'tcx
, M
::Provenance
>> {
632 let layout
= self.layout_of_local(frame
, local
, layout
)?
;
633 let op
= *frame
.locals
[local
].access()?
;
634 if matches
!(op
, Operand
::Immediate(_
)) {
635 if layout
.is_unsized() {
636 // ConstProp marks *all* locals as `Immediate::Uninit` since it cannot
637 // efficiently check whether they are sized. We have to catch that case here.
638 throw_inval
!(ConstPropNonsense
);
641 Ok(OpTy { op, layout }
)
644 /// Every place can be read from, so we can turn them into an operand.
645 /// This will definitely return `Indirect` if the place is a `Ptr`, i.e., this
646 /// will never actually read from memory.
649 place
: &PlaceTy
<'tcx
, M
::Provenance
>,
650 ) -> InterpResult
<'tcx
, OpTy
<'tcx
, M
::Provenance
>> {
651 match place
.as_mplace_or_local() {
652 Left(mplace
) => Ok(mplace
.into()),
653 Right((frame
, local
, offset
)) => {
654 debug_assert
!(place
.layout
.is_sized()); // only sized locals can ever be `Place::Local`.
655 let base
= self.local_to_op(&self.stack()[frame
], local
, None
)?
;
657 Some(offset
) => base
.offset(offset
, place
.layout
, self)?
,
659 // In the common case this hasn't been projected.
660 debug_assert_eq
!(place
.layout
, base
.layout
);
668 /// Evaluate a place with the goal of reading from it. This lets us sometimes
669 /// avoid allocations.
670 pub fn eval_place_to_op(
672 mir_place
: mir
::Place
<'tcx
>,
673 layout
: Option
<TyAndLayout
<'tcx
>>,
674 ) -> InterpResult
<'tcx
, OpTy
<'tcx
, M
::Provenance
>> {
675 // Do not use the layout passed in as argument if the base we are looking at
676 // here is not the entire place.
677 let layout
= if mir_place
.projection
.is_empty() { layout }
else { None }
;
679 let mut op
= self.local_to_op(self.frame(), mir_place
.local
, layout
)?
;
680 // Using `try_fold` turned out to be bad for performance, hence the loop.
681 for elem
in mir_place
.projection
.iter() {
682 op
= self.project(&op
, elem
)?
685 trace
!("eval_place_to_op: got {:?}", op
);
686 // Sanity-check the type we ended up with.
687 if cfg
!(debug_assertions
) {
688 let normalized_place_ty
= self.subst_from_current_frame_and_normalize_erasing_regions(
689 mir_place
.ty(&self.frame().body
.local_decls
, *self.tcx
).ty
,
691 if !mir_assign_valid_types(
694 self.layout_of(normalized_place_ty
)?
,
699 "eval_place of a MIR place with type {} produced an interpreter operand with type {}",
708 /// Evaluate the operand, returning a place where you can then find the data.
709 /// If you already know the layout, you can save two table lookups
710 /// by passing it in here.
714 mir_op
: &mir
::Operand
<'tcx
>,
715 layout
: Option
<TyAndLayout
<'tcx
>>,
716 ) -> InterpResult
<'tcx
, OpTy
<'tcx
, M
::Provenance
>> {
717 use rustc_middle
::mir
::Operand
::*;
718 let op
= match mir_op
{
719 // FIXME: do some more logic on `move` to invalidate the old location
720 &Copy(place
) | &Move(place
) => self.eval_place_to_op(place
, layout
)?
,
722 Constant(constant
) => {
724 self.subst_from_current_frame_and_normalize_erasing_regions(constant
.const_
)?
;
726 // This can still fail:
727 // * During ConstProp, with `TooGeneric` or since the `required_consts` were not all
729 // * During CTFE, since promoteds in `const`/`static` initializer bodies can fail.
730 self.eval_mir_constant(&c
, Some(constant
.span
), layout
)?
733 trace
!("{:?}: {:?}", mir_op
, op
);
737 pub(crate) fn const_val_to_op(
739 val_val
: mir
::ConstValue
<'tcx
>,
741 layout
: Option
<TyAndLayout
<'tcx
>>,
742 ) -> InterpResult
<'tcx
, OpTy
<'tcx
, M
::Provenance
>> {
743 // Other cases need layout.
744 let adjust_scalar
= |scalar
| -> InterpResult
<'tcx
, _
> {
746 Scalar
::Ptr(ptr
, size
) => Scalar
::Ptr(self.global_base_pointer(ptr
)?
, size
),
747 Scalar
::Int(int
) => Scalar
::Int(int
),
750 let layout
= from_known_layout(self.tcx
, self.param_env
, layout
, || self.layout_of(ty
))?
;
751 let imm
= match val_val
{
752 mir
::ConstValue
::Indirect { alloc_id, offset }
=> {
753 // We rely on mutability being set correctly in that allocation to prevent writes
754 // where none should happen.
755 let ptr
= self.global_base_pointer(Pointer
::new(alloc_id
, offset
))?
;
756 return Ok(self.ptr_to_mplace(ptr
.into(), layout
).into());
758 mir
::ConstValue
::Scalar(x
) => adjust_scalar(x
)?
.into(),
759 mir
::ConstValue
::ZeroSized
=> Immediate
::Uninit
,
760 mir
::ConstValue
::Slice { data, meta }
=> {
761 // We rely on mutability being set correctly in `data` to prevent writes
762 // where none should happen.
763 let ptr
= Pointer
::new(self.tcx
.reserve_and_set_memory_alloc(data
), Size
::ZERO
);
764 Immediate
::new_slice(self.global_base_pointer(ptr
)?
.into(), meta
, self)
767 Ok(OpTy { op: Operand::Immediate(imm), layout }
)
771 // Some nodes are used a lot. Make sure they don't unintentionally get bigger.
772 #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
775 use rustc_data_structures
::static_assert_size
;
776 // tidy-alphabetical-start
777 static_assert_size
!(Immediate
, 48);
778 static_assert_size
!(ImmTy
<'_
>, 64);
779 static_assert_size
!(Operand
, 56);
780 static_assert_size
!(OpTy
<'_
>, 72);
781 // tidy-alphabetical-end