]> git.proxmox.com Git - rustc.git/blob - compiler/rustc_const_eval/src/interpret/operand.rs
New upstream version 1.75.0+dfsg1
[rustc.git] / compiler / rustc_const_eval / src / interpret / operand.rs
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.
3
4 use std::assert_matches::assert_matches;
5
6 use either::{Either, Left, Right};
7
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};
14
15 use super::{
16 alloc_range, from_known_layout, mir_assign_valid_types, AllocId, Frame, InterpCx, InterpResult,
17 MPlaceTy, Machine, MemPlace, MemPlaceMeta, OffsetMode, PlaceTy, Pointer, Projectable,
18 Provenance, Scalar,
19 };
20
21 /// An `Immediate` represents a single immediate self-contained Rust value.
22 ///
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).
31 Scalar(Scalar<Prov>),
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.
36 Uninit,
37 }
38
39 impl<Prov: Provenance> From<Scalar<Prov>> for Immediate<Prov> {
40 #[inline(always)]
41 fn from(val: Scalar<Prov>) -> Self {
42 Immediate::Scalar(val)
43 }
44 }
45
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,
51 ) -> Self {
52 let ptr = Scalar::from_maybe_pointer(ptr, cx);
53 match meta {
54 MemPlaceMeta::None => Immediate::from(ptr),
55 MemPlaceMeta::Meta(meta) => Immediate::ScalarPair(ptr, meta),
56 }
57 }
58
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),
63 )
64 }
65
66 pub fn new_dyn_trait(
67 val: Pointer<Option<Prov>>,
68 vtable: Pointer<Option<Prov>>,
69 cx: &impl HasDataLayout,
70 ) -> Self {
71 Immediate::ScalarPair(
72 Scalar::from_maybe_pointer(val, cx),
73 Scalar::from_maybe_pointer(vtable, cx),
74 )
75 }
76
77 #[inline]
78 #[cfg_attr(debug_assertions, track_caller)] // only in debug builds due to perf (see #98980)
79 pub fn to_scalar(self) -> Scalar<Prov> {
80 match self {
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"),
84 }
85 }
86
87 #[inline]
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>) {
90 match self {
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"),
94 }
95 }
96 }
97
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.
100 #[derive(Clone)]
101 pub struct ImmTy<'tcx, Prov: Provenance = AllocId> {
102 imm: Immediate<Prov>,
103 pub layout: TyAndLayout<'tcx>,
104 }
105
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>,
111 s: Scalar<Prov>,
112 ty: Ty<'tcx>,
113 ) -> Result<(), std::fmt::Error> {
114 match s {
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
119 // memory.
120 cx.pretty_print_const_pointer(ptr, ty)
121 }
122 }
123 }
124 ty::tls::with(|tcx| {
125 match self.imm {
126 Immediate::Scalar(s) => {
127 if let Some(ty) = tcx.lift(self.layout.ty) {
128 let s =
129 FmtPrinter::print_string(tcx, Namespace::ValueNS, |cx| p(cx, s, ty))?;
130 f.write_str(&s)?;
131 return Ok(());
132 }
133 write!(f, "{:x}: {}", s, self.layout.ty)
134 }
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)
138 }
139 Immediate::Uninit => {
140 write!(f, "uninit: {}", self.layout.ty)
141 }
142 }
143 })
144 }
145 }
146
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))
153 .finish()
154 }
155 }
156
157 impl<'tcx, Prov: Provenance> std::ops::Deref for ImmTy<'tcx, Prov> {
158 type Target = Immediate<Prov>;
159 #[inline(always)]
160 fn deref(&self) -> &Immediate<Prov> {
161 &self.imm
162 }
163 }
164
165 impl<'tcx, Prov: Provenance> ImmTy<'tcx, Prov> {
166 #[inline]
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 }
170 }
171
172 #[inline]
173 pub fn from_scalar_pair(a: Scalar<Prov>, b: Scalar<Prov>, layout: TyAndLayout<'tcx>) -> Self {
174 debug_assert!(
175 matches!(layout.abi, Abi::ScalarPair(..)),
176 "`ImmTy::from_scalar_pair` on non-scalar-pair layout"
177 );
178 let imm = Immediate::ScalarPair(a, b);
179 ImmTy { imm, layout }
180 }
181
182 #[inline(always)]
183 pub fn from_immediate(imm: Immediate<Prov>, layout: TyAndLayout<'tcx>) -> Self {
184 debug_assert!(
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,
189 _ => false,
190 },
191 "immediate {imm:?} does not fit to layout {layout:?}",
192 );
193 ImmTy { imm, layout }
194 }
195
196 #[inline]
197 pub fn uninit(layout: TyAndLayout<'tcx>) -> Self {
198 debug_assert!(layout.is_sized(), "immediates must be sized");
199 ImmTy { imm: Immediate::Uninit, layout }
200 }
201
202 #[inline]
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))
205 }
206 #[inline]
207 pub fn from_uint(i: impl Into<u128>, layout: TyAndLayout<'tcx>) -> Self {
208 Self::from_scalar(Scalar::from_uint(i, layout.size), layout)
209 }
210
211 #[inline]
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))
214 }
215
216 #[inline]
217 pub fn from_int(i: impl Into<i128>, layout: TyAndLayout<'tcx>) -> Self {
218 Self::from_scalar(Scalar::from_int(i, layout.size), layout)
219 }
220
221 #[inline]
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)
225 }
226
227 #[inline]
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())
232 }
233
234 /// Compute the "sub-immediate" that is located within the `base` at the given offset with the
235 /// given layout.
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.
242 assert!(
243 offset + layout.size <= self.layout.size,
244 "attempting to project to field at offset {} with size {} into immediate with layout {:#?}",
245 offset.bytes(),
246 layout.size.bytes(),
247 self.layout,
248 );
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) =>
261 {
262 Immediate::Uninit
263 }
264 // the field covers the entire type
265 _ if layout.size == self.layout.size => {
266 assert_eq!(offset.bytes(), 0);
267 assert!(
268 match (self.layout.abi, layout.abi) {
269 (Abi::Scalar(..), Abi::Scalar(..)) => true,
270 (Abi::ScalarPair(..), Abi::ScalarPair(..)) => true,
271 _ => false,
272 },
273 "cannot project into {} immediate with equally-sized field {}\nouter ABI: {:#?}\nfield ABI: {:#?}",
274 self.layout.ty,
275 layout.ty,
276 self.layout.abi,
277 layout.abi,
278 );
279 **self
280 }
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));
286 a_val
287 } else {
288 debug_assert_eq!(offset, a.size(cx).align_to(b.align(cx).abi));
289 debug_assert_eq!(layout.size, b.size(cx));
290 b_val
291 })
292 }
293 // everything else is a bug
294 _ => bug!("invalid field access on immediate {}, layout {:#?}", self, self.layout),
295 };
296
297 ImmTy::from_immediate(inner_val, layout)
298 }
299 }
300
301 impl<'tcx, Prov: Provenance> Projectable<'tcx, Prov> for ImmTy<'tcx, Prov> {
302 #[inline(always)]
303 fn layout(&self) -> TyAndLayout<'tcx> {
304 self.layout
305 }
306
307 #[inline(always)]
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
310 MemPlaceMeta::None
311 }
312
313 fn offset_with_meta<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>(
314 &self,
315 offset: Size,
316 _mode: OffsetMode,
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))
323 }
324
325 fn to_op<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>(
326 &self,
327 _ecx: &InterpCx<'mir, 'tcx, M>,
328 ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
329 Ok(self.clone().into())
330 }
331 }
332
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>),
340 }
341
342 #[derive(Clone)]
343 pub struct OpTy<'tcx, Prov: Provenance = AllocId> {
344 op: Operand<Prov>, // Keep this private; it helps enforce invariants.
345 pub layout: TyAndLayout<'tcx>,
346 }
347
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))
354 .finish()
355 }
356 }
357
358 impl<'tcx, Prov: Provenance> From<ImmTy<'tcx, Prov>> for OpTy<'tcx, Prov> {
359 #[inline(always)]
360 fn from(val: ImmTy<'tcx, Prov>) -> Self {
361 OpTy { op: Operand::Immediate(val.imm), layout: val.layout }
362 }
363 }
364
365 impl<'tcx, Prov: Provenance> From<MPlaceTy<'tcx, Prov>> for OpTy<'tcx, Prov> {
366 #[inline(always)]
367 fn from(mplace: MPlaceTy<'tcx, Prov>) -> Self {
368 OpTy { op: Operand::Indirect(*mplace.mplace()), layout: mplace.layout }
369 }
370 }
371
372 impl<'tcx, Prov: Provenance> OpTy<'tcx, Prov> {
373 #[inline(always)]
374 pub(super) fn op(&self) -> &Operand<Prov> {
375 &self.op
376 }
377 }
378
379 impl<'tcx, Prov: Provenance> Projectable<'tcx, Prov> for OpTy<'tcx, Prov> {
380 #[inline(always)]
381 fn layout(&self) -> TyAndLayout<'tcx> {
382 self.layout
383 }
384
385 #[inline]
386 fn meta(&self) -> MemPlaceMeta<Prov> {
387 match self.as_mplace_or_imm() {
388 Left(mplace) => mplace.meta(),
389 Right(_) => {
390 debug_assert!(self.layout.is_sized(), "unsized immediates are not a thing");
391 MemPlaceMeta::None
392 }
393 }
394 }
395
396 fn offset_with_meta<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>(
397 &self,
398 offset: Size,
399 mode: OffsetMode,
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()),
406 Right(imm) => {
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())
410 }
411 }
412 }
413
414 fn to_op<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>(
415 &self,
416 _ecx: &InterpCx<'mir, 'tcx, M>,
417 ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
418 Ok(self.clone())
419 }
420 }
421
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>>;
425 }
426
427 impl<'tcx, Prov: Provenance> Readable<'tcx, Prov> for OpTy<'tcx, Prov> {
428 #[inline(always)]
429 fn as_mplace_or_imm(&self) -> Either<MPlaceTy<'tcx, Prov>, ImmTy<'tcx, Prov>> {
430 self.as_mplace_or_imm()
431 }
432 }
433
434 impl<'tcx, Prov: Provenance> Readable<'tcx, Prov> for MPlaceTy<'tcx, Prov> {
435 #[inline(always)]
436 fn as_mplace_or_imm(&self) -> Either<MPlaceTy<'tcx, Prov>, ImmTy<'tcx, Prov>> {
437 Left(self.clone())
438 }
439 }
440
441 impl<'tcx, Prov: Provenance> Readable<'tcx, Prov> for ImmTy<'tcx, Prov> {
442 #[inline(always)]
443 fn as_mplace_or_imm(&self) -> Either<MPlaceTy<'tcx, Prov>, ImmTy<'tcx, Prov>> {
444 Right(self.clone())
445 }
446 }
447
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.
451 ///
452 /// This is an internal function; call `read_immediate` instead.
453 fn read_immediate_from_mplace_raw(
454 &self,
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
459 return Ok(None);
460 }
461
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)));
465 };
466
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(_)),
480 )?;
481 Some(ImmTy::from_scalar(scalar, mplace.layout))
482 }
483 Abi::ScalarPair(
484 abi::Scalar::Initialized { value: a, .. },
485 abi::Scalar::Initialized { value: b, .. },
486 ) => {
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(_)),
496 )?;
497 let b_val = alloc.read_scalar(
498 alloc_range(b_offset, b_size),
499 /*read_provenance*/ matches!(b, abi::Pointer(_)),
500 )?;
501 Some(ImmTy::from_immediate(Immediate::ScalarPair(a_val, b_val), mplace.layout))
502 }
503 _ => {
504 // Neither a scalar nor scalar pair.
505 None
506 }
507 })
508 }
509
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.
515 ///
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(
519 &self,
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)? {
525 Right(val)
526 } else {
527 Left(mplace.clone())
528 }
529 }
530 Right(val) => Right(val),
531 })
532 }
533
534 /// Read an immediate from a place, asserting that that is possible with the given layout.
535 ///
536 /// If this succeeds, the `ImmTy` is never `Uninit`.
537 #[inline(always)]
538 pub fn read_immediate(
539 &self,
540 op: &impl Readable<'tcx, M::Provenance>,
541 ) -> InterpResult<'tcx, ImmTy<'tcx, M::Provenance>> {
542 if !matches!(
543 op.layout().abi,
544 Abi::Scalar(abi::Scalar::Initialized { .. })
545 | Abi::ScalarPair(abi::Scalar::Initialized { .. }, abi::Scalar::Initialized { .. })
546 ) {
547 span_bug!(self.cur_span(), "primitive read not possible for type: {}", op.layout().ty);
548 }
549 let imm = self.read_immediate_raw(op)?.right().unwrap();
550 if matches!(*imm, Immediate::Uninit) {
551 throw_ub!(InvalidUninitBytes(None));
552 }
553 Ok(imm)
554 }
555
556 /// Read a scalar from a place
557 pub fn read_scalar(
558 &self,
559 op: &impl Readable<'tcx, M::Provenance>,
560 ) -> InterpResult<'tcx, Scalar<M::Provenance>> {
561 Ok(self.read_immediate(op)?.to_scalar())
562 }
563
564 // Pointer-sized reads are fairly common and need target layout access, so we wrap them in
565 // convenience functions.
566
567 /// Read a pointer from a place.
568 pub fn read_pointer(
569 &self,
570 op: &impl Readable<'tcx, M::Provenance>,
571 ) -> InterpResult<'tcx, Pointer<Option<M::Provenance>>> {
572 self.read_scalar(op)?.to_pointer(self)
573 }
574 /// Read a pointer-sized unsigned integer from a place.
575 pub fn read_target_usize(
576 &self,
577 op: &impl Readable<'tcx, M::Provenance>,
578 ) -> InterpResult<'tcx, u64> {
579 self.read_scalar(op)?.to_target_usize(self)
580 }
581 /// Read a pointer-sized signed integer from a place.
582 pub fn read_target_isize(
583 &self,
584 op: &impl Readable<'tcx, M::Provenance>,
585 ) -> InterpResult<'tcx, i64> {
586 self.read_scalar(op)?.to_target_isize(self)
587 }
588
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)))?;
594 Ok(str)
595 }
596
597 /// Converts a repr(simd) operand into an operand where `place_index` accesses the SIMD elements.
598 /// Also returns the number of elements.
599 ///
600 /// Can (but does not always) trigger UB if `op` is uninitialized.
601 pub fn operand_to_simd(
602 &self,
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))
613 }
614 Immediate::Scalar(..) | Immediate::ScalarPair(..) => {
615 bug!("arrays/slices can never have Scalar/ScalarPair layout")
616 }
617 },
618 }
619 }
620
621 /// Read from a local.
622 /// Will not access memory, instead an indirect `Operand` is returned.
623 ///
624 /// This is public because it is used by [priroda](https://github.com/oli-obk/priroda) to get an
625 /// OpTy from a local.
626 pub fn local_to_op(
627 &self,
628 frame: &Frame<'mir, 'tcx, M::Provenance, M::FrameExtra>,
629 local: mir::Local,
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);
639 }
640 }
641 Ok(OpTy { op, layout })
642 }
643
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.
647 pub fn place_to_op(
648 &self,
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)?;
656 Ok(match offset {
657 Some(offset) => base.offset(offset, place.layout, self)?,
658 None => {
659 // In the common case this hasn't been projected.
660 debug_assert_eq!(place.layout, base.layout);
661 base
662 }
663 })
664 }
665 }
666 }
667
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(
671 &self,
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 };
678
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)?
683 }
684
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,
690 )?;
691 if !mir_assign_valid_types(
692 *self.tcx,
693 self.param_env,
694 self.layout_of(normalized_place_ty)?,
695 op.layout,
696 ) {
697 span_bug!(
698 self.cur_span(),
699 "eval_place of a MIR place with type {} produced an interpreter operand with type {}",
700 normalized_place_ty,
701 op.layout.ty,
702 )
703 }
704 }
705 Ok(op)
706 }
707
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.
711 #[inline]
712 pub fn eval_operand(
713 &self,
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)?,
721
722 Constant(constant) => {
723 let c =
724 self.subst_from_current_frame_and_normalize_erasing_regions(constant.const_)?;
725
726 // This can still fail:
727 // * During ConstProp, with `TooGeneric` or since the `required_consts` were not all
728 // checked yet.
729 // * During CTFE, since promoteds in `const`/`static` initializer bodies can fail.
730 self.eval_mir_constant(&c, Some(constant.span), layout)?
731 }
732 };
733 trace!("{:?}: {:?}", mir_op, op);
734 Ok(op)
735 }
736
737 pub(crate) fn const_val_to_op(
738 &self,
739 val_val: mir::ConstValue<'tcx>,
740 ty: Ty<'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, _> {
745 Ok(match scalar {
746 Scalar::Ptr(ptr, size) => Scalar::Ptr(self.global_base_pointer(ptr)?, size),
747 Scalar::Int(int) => Scalar::Int(int),
748 })
749 };
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());
757 }
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)
765 }
766 };
767 Ok(OpTy { op: Operand::Immediate(imm), layout })
768 }
769 }
770
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"))]
773 mod size_asserts {
774 use super::*;
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
782 }