1
1
//! Functions concerning immediate values and operands, and reading from operands.
2
2
//! All high-level functions to read from memory work on operands as sources.
3
3
4
- use std::convert::TryFrom;
5
4
use std::fmt::Write;
6
5
7
6
use rustc_hir::def::Namespace;
@@ -15,7 +14,7 @@ use rustc_target::abi::{VariantIdx, Variants};
15
14
16
15
use super::{
17
16
alloc_range, from_known_layout, mir_assign_valid_types, AllocId, ConstValue, Frame, GlobalId,
18
- InterpCx, InterpResult, MPlaceTy, Machine, MemPlace, Place, PlaceTy, Pointer,
17
+ InterpCx, InterpResult, MPlaceTy, Machine, MemPlace, MemPlaceMeta, Place, PlaceTy, Pointer,
19
18
PointerArithmetic, Provenance, Scalar, ScalarMaybeUninit,
20
19
};
21
20
@@ -253,6 +252,11 @@ impl<'tcx, Tag: Provenance> ImmTy<'tcx, Tag> {
253
252
ImmTy { imm, layout }
254
253
}
255
254
255
+ #[inline]
256
+ pub fn uninit(layout: TyAndLayout<'tcx>) -> Self {
257
+ ImmTy { imm: Immediate::Uninit, layout }
258
+ }
259
+
256
260
#[inline]
257
261
pub fn try_from_uint(i: impl Into<u128>, layout: TyAndLayout<'tcx>) -> Option<Self> {
258
262
Some(Self::from_scalar(Scalar::try_from_uint(i, layout.size)?, layout))
@@ -280,6 +284,41 @@ impl<'tcx, Tag: Provenance> ImmTy<'tcx, Tag> {
280
284
}
281
285
}
282
286
287
+ impl<'tcx, Tag: Provenance> OpTy<'tcx, Tag> {
288
+ pub fn len(&self, cx: &impl HasDataLayout) -> InterpResult<'tcx, u64> {
289
+ if self.layout.is_unsized() {
290
+ // There are no unsized immediates.
291
+ self.assert_mem_place().len(cx)
292
+ } else {
293
+ match self.layout.fields {
294
+ abi::FieldsShape::Array { count, .. } => Ok(count),
295
+ _ => bug!("len not supported on sized type {:?}", self.layout.ty),
296
+ }
297
+ }
298
+ }
299
+
300
+ pub fn offset(
301
+ &self,
302
+ offset: Size,
303
+ meta: MemPlaceMeta<Tag>,
304
+ layout: TyAndLayout<'tcx>,
305
+ cx: &impl HasDataLayout,
306
+ ) -> InterpResult<'tcx, Self> {
307
+ match self.try_as_mplace() {
308
+ Ok(mplace) => Ok(mplace.offset(offset, meta, layout, cx)?.into()),
309
+ Err(imm) => {
310
+ assert!(
311
+ matches!(*imm, Immediate::Uninit),
312
+ "Scalar/ScalarPair cannot be offset into"
313
+ );
314
+ assert!(!meta.has_meta()); // no place to store metadata here
315
+ // Every part of an uninit is uninit.
316
+ Ok(ImmTy::uninit(layout).into())
317
+ }
318
+ }
319
+ }
320
+ }
321
+
283
322
impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
284
323
/// Try reading an immediate in memory; this is interesting particularly for `ScalarPair`.
285
324
/// Returns `None` if the layout does not permit loading this as a value.
@@ -296,11 +335,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
296
335
}
297
336
298
337
let Some(alloc) = self.get_place_alloc(mplace)? else {
299
- return Ok(Some(ImmTy {
300
- // zero-sized type can be left uninit
301
- imm: Immediate::Uninit,
302
- layout: mplace.layout,
303
- }));
338
+ // zero-sized type can be left uninit
339
+ return Ok(Some(ImmTy::uninit(mplace.layout)));
304
340
};
305
341
306
342
// It may seem like all types with `Scalar` or `ScalarPair` ABI are fair game at this point.
@@ -367,6 +403,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
367
403
/// This flag exists only for validity checking.
368
404
///
369
405
/// This is an internal function that should not usually be used; call `read_immediate` instead.
406
+ /// ConstProp needs it, though.
370
407
pub fn read_immediate_raw(
371
408
&self,
372
409
src: &OpTy<'tcx, M::PointerTag>,
@@ -421,123 +458,28 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
421
458
Ok(str)
422
459
}
423
460
424
- /// Projection functions
425
- pub fn operand_field(
426
- &self,
427
- op: &OpTy<'tcx, M::PointerTag>,
428
- field: usize,
429
- ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> {
430
- let base = match op.try_as_mplace() {
431
- Ok(ref mplace) => {
432
- // We can reuse the mplace field computation logic for indirect operands.
433
- let field = self.mplace_field(mplace, field)?;
434
- return Ok(field.into());
435
- }
436
- Err(value) => value,
437
- };
438
-
439
- let field_layout = base.layout.field(self, field);
440
- let offset = base.layout.fields.offset(field);
441
- // This makes several assumptions about what layouts we will encounter; we match what
442
- // codegen does as good as we can (see `extract_field` in `rustc_codegen_ssa/src/mir/operand.rs`).
443
- let field_val: Immediate<_> = match (*base, base.layout.abi) {
444
- // the field contains no information, can be left uninit
445
- _ if field_layout.is_zst() => Immediate::Uninit,
446
- // the field covers the entire type
447
- _ if field_layout.size == base.layout.size => {
448
- assert!(match (base.layout.abi, field_layout.abi) {
449
- (Abi::Scalar(..), Abi::Scalar(..)) => true,
450
- (Abi::ScalarPair(..), Abi::ScalarPair(..)) => true,
451
- _ => false,
452
- });
453
- assert!(offset.bytes() == 0);
454
- *base
455
- }
456
- // extract fields from types with `ScalarPair` ABI
457
- (Immediate::ScalarPair(a_val, b_val), Abi::ScalarPair(a, b)) => {
458
- assert!(matches!(field_layout.abi, Abi::Scalar(..)));
459
- Immediate::from(if offset.bytes() == 0 {
460
- debug_assert_eq!(field_layout.size, a.size(self));
461
- a_val
462
- } else {
463
- debug_assert_eq!(offset, a.size(self).align_to(b.align(self).abi));
464
- debug_assert_eq!(field_layout.size, b.size(self));
465
- b_val
466
- })
467
- }
468
- _ => span_bug!(
469
- self.cur_span(),
470
- "invalid field access on immediate {}, layout {:#?}",
471
- base,
472
- base.layout
473
- ),
474
- };
475
-
476
- Ok(OpTy { op: Operand::Immediate(field_val), layout: field_layout, align: None })
477
- }
478
-
479
- pub fn operand_index(
480
- &self,
481
- op: &OpTy<'tcx, M::PointerTag>,
482
- index: u64,
483
- ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> {
484
- if let Ok(index) = usize::try_from(index) {
485
- // We can just treat this as a field.
486
- self.operand_field(op, index)
487
- } else {
488
- // Indexing into a big array. This must be an mplace.
489
- let mplace = op.assert_mem_place();
490
- Ok(self.mplace_index(&mplace, index)?.into())
491
- }
492
- }
493
-
494
- pub fn operand_downcast(
495
- &self,
496
- op: &OpTy<'tcx, M::PointerTag>,
497
- variant: VariantIdx,
498
- ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> {
499
- Ok(match op.try_as_mplace() {
500
- Ok(ref mplace) => self.mplace_downcast(mplace, variant)?.into(),
501
- Err(..) => {
502
- // Downcasts only change the layout.
503
- // (In particular, no check about whether this is even the active variant -- that's by design,
504
- // see https://github.com/rust-lang/rust/issues/93688#issuecomment-1032929496.)
505
- let layout = op.layout.for_variant(self, variant);
506
- OpTy { layout, ..*op }
507
- }
508
- })
509
- }
510
-
511
- #[instrument(skip(self), level = "debug")]
512
- pub fn operand_projection(
513
- &self,
514
- base: &OpTy<'tcx, M::PointerTag>,
515
- proj_elem: mir::PlaceElem<'tcx>,
516
- ) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> {
517
- use rustc_middle::mir::ProjectionElem::*;
518
- Ok(match proj_elem {
519
- Field(field, _) => self.operand_field(base, field.index())?,
520
- Downcast(_, variant) => self.operand_downcast(base, variant)?,
521
- Deref => self.deref_operand(base)?.into(),
522
- Subslice { .. } | ConstantIndex { .. } | Index(_) => {
523
- // The rest should only occur as mplace, we do not use Immediates for types
524
- // allowing such operations. This matches place_projection forcing an allocation.
525
- let mplace = base.assert_mem_place();
526
- self.mplace_projection(&mplace, proj_elem)?.into()
527
- }
528
- })
529
- }
530
-
531
461
/// Converts a repr(simd) operand into an operand where `place_index` accesses the SIMD elements.
532
462
/// Also returns the number of elements.
463
+ ///
464
+ /// Can (but does not always) trigger UB if `op` is uninitialized.
533
465
pub fn operand_to_simd(
534
466
&self,
535
- base : &OpTy<'tcx, M::PointerTag>,
467
+ op : &OpTy<'tcx, M::PointerTag>,
536
468
) -> InterpResult<'tcx, (MPlaceTy<'tcx, M::PointerTag>, u64)> {
537
469
// Basically we just transmute this place into an array following simd_size_and_type.
538
470
// This only works in memory, but repr(simd) types should never be immediates anyway.
539
- assert!(base.layout.ty.is_simd());
540
- self.mplace_to_simd(&base.assert_mem_place())
471
+ assert!(op.layout.ty.is_simd());
472
+ match op.try_as_mplace() {
473
+ Ok(mplace) => self.mplace_to_simd(&mplace),
474
+ Err(imm) => match *imm {
475
+ Immediate::Uninit => {
476
+ throw_ub!(InvalidUninitBytes(None))
477
+ }
478
+ Immediate::Scalar(..) | Immediate::ScalarPair(..) => {
479
+ bug!("arrays/slices can never have Scalar/ScalarPair layout")
480
+ }
481
+ },
482
+ }
541
483
}
542
484
543
485
/// Read from a local. Will not actually access the local if reading from a ZST.
@@ -582,30 +524,34 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
582
524
/// avoid allocations.
583
525
pub fn eval_place_to_op(
584
526
&self,
585
- place : mir::Place<'tcx>,
527
+ mir_place : mir::Place<'tcx>,
586
528
layout: Option<TyAndLayout<'tcx>>,
587
529
) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> {
588
530
// Do not use the layout passed in as argument if the base we are looking at
589
531
// here is not the entire place.
590
- let layout = if place .projection.is_empty() { layout } else { None };
532
+ let layout = if mir_place .projection.is_empty() { layout } else { None };
591
533
592
- let base_op = self.local_to_op(self.frame(), place.local, layout)?;
593
-
594
- let op = place
595
- .projection
596
- .iter()
597
- .try_fold(base_op, |op, elem| self.operand_projection(&op, elem))?;
534
+ let mut op = self.local_to_op(self.frame(), mir_place.local, layout)?;
535
+ // Using `try_fold` turned out to be bad for performance, hence the loop.
536
+ for elem in mir_place.projection.iter() {
537
+ op = self.operand_projection(&op, elem)?
538
+ }
598
539
599
540
trace!("eval_place_to_op: got {:?}", *op);
600
541
// Sanity-check the type we ended up with.
601
- debug_assert!(mir_assign_valid_types(
602
- *self.tcx,
603
- self.param_env,
604
- self.layout_of(self.subst_from_current_frame_and_normalize_erasing_regions(
605
- place.ty(&self.frame().body.local_decls, *self.tcx).ty
606
- )?)?,
607
- op.layout,
608
- ));
542
+ debug_assert!(
543
+ mir_assign_valid_types(
544
+ *self.tcx,
545
+ self.param_env,
546
+ self.layout_of(self.subst_from_current_frame_and_normalize_erasing_regions(
547
+ mir_place.ty(&self.frame().body.local_decls, *self.tcx).ty
548
+ )?)?,
549
+ op.layout,
550
+ ),
551
+ "eval_place of a MIR place with type {:?} produced an interpreter operand with type {:?}",
552
+ mir_place.ty(&self.frame().body.local_decls, *self.tcx).ty,
553
+ op.layout.ty,
554
+ );
609
555
Ok(op)
610
556
}
611
557
0 commit comments