Skip to content

Commit d7e2650

Browse files
committed
miri: avoid a bunch of casts by offering usized-based field indexing
1 parent cd15b65 commit d7e2650

File tree

10 files changed

+106
-68
lines changed

10 files changed

+106
-68
lines changed

src/librustc_mir/const_eval/mod.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
// Not in interpret to make sure we do not use private implementation details
22

3+
use std::convert::TryFrom;
4+
35
use rustc::mir;
46
use rustc::ty::layout::VariantIdx;
57
use rustc::ty::{self, TyCtxt};
@@ -37,7 +39,7 @@ pub(crate) fn const_field<'tcx>(
3739
Some(variant) => ecx.operand_downcast(op, variant).unwrap(),
3840
};
3941
// then project
40-
let field = ecx.operand_field(down, field.index() as u64).unwrap();
42+
let field = ecx.operand_field(down, field.index()).unwrap();
4143
// and finally move back to the const world, always normalizing because
4244
// this is not called for statics.
4345
op_to_const(&ecx, field)
@@ -68,10 +70,11 @@ pub(crate) fn destructure_const<'tcx>(
6870

6971
let variant = ecx.read_discriminant(op).unwrap().1;
7072

73+
// We go to `usize` as we cannot allocate anything bigger anyway.
7174
let field_count = match val.ty.kind {
72-
ty::Array(_, len) => len.eval_usize(tcx, param_env),
73-
ty::Adt(def, _) => def.variants[variant].fields.len() as u64,
74-
ty::Tuple(substs) => substs.len() as u64,
75+
ty::Array(_, len) => usize::try_from(len.eval_usize(tcx, param_env)).unwrap(),
76+
ty::Adt(def, _) => def.variants[variant].fields.len(),
77+
ty::Tuple(substs) => substs.len(),
7578
_ => bug!("cannot destructure constant {:?}", val),
7679
};
7780

src/librustc_mir/interpret/cast.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -320,11 +320,11 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
320320
// Example: `Arc<T>` -> `Arc<Trait>`
321321
// here we need to increase the size of every &T thin ptr field to a fat ptr
322322
for i in 0..src.layout.fields.count() {
323-
let dst_field = self.place_field(dest, i as u64)?;
323+
let dst_field = self.place_field(dest, i)?;
324324
if dst_field.layout.is_zst() {
325325
continue;
326326
}
327-
let src_field = self.operand_field(src, i as u64)?;
327+
let src_field = self.operand_field(src, i)?;
328328
if src_field.layout.ty == dst_field.layout.ty {
329329
self.copy_op(src_field, dst_field)?;
330330
} else {

src/librustc_mir/interpret/intrinsics.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -350,8 +350,8 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
350350
);
351351

352352
for i in 0..len {
353-
let place = self.place_field(dest, i)?;
354-
let value = if i == index { elem } else { self.operand_field(input, i)? };
353+
let place = self.place_index(dest, i)?;
354+
let value = if i == index { elem } else { self.operand_index(input, i)? };
355355
self.copy_op(value, place)?;
356356
}
357357
}
@@ -370,7 +370,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
370370
"Return type `{}` must match vector element type `{}`",
371371
dest.layout.ty, e_ty
372372
);
373-
self.copy_op(self.operand_field(args[0], index)?, dest)?;
373+
self.copy_op(self.operand_index(args[0], index)?, dest)?;
374374
}
375375
_ => return Ok(false),
376376
}

src/librustc_mir/interpret/operand.rs

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -351,7 +351,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
351351
pub fn operand_field(
352352
&self,
353353
op: OpTy<'tcx, M::PointerTag>,
354-
field: u64,
354+
field: usize,
355355
) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> {
356356
let base = match op.try_as_mplace(self) {
357357
Ok(mplace) => {
@@ -362,7 +362,6 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
362362
Err(value) => value,
363363
};
364364

365-
let field = field.try_into().unwrap();
366365
let field_layout = op.layout.field(self, field)?;
367366
if field_layout.is_zst() {
368367
let immediate = Scalar::zst().into();
@@ -384,6 +383,21 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
384383
Ok(OpTy { op: Operand::Immediate(immediate), layout: field_layout })
385384
}
386385

386+
pub fn operand_index(
387+
&self,
388+
op: OpTy<'tcx, M::PointerTag>,
389+
index: u64,
390+
) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> {
391+
if let Ok(index) = usize::try_from(index) {
392+
// We can just treat this as a field.
393+
self.operand_field(op, index)
394+
} else {
395+
// Indexing into a big array. This must be an mplace.
396+
let mplace = op.assert_mem_place(self);
397+
Ok(self.mplace_index(mplace, index)?.into())
398+
}
399+
}
400+
387401
pub fn operand_downcast(
388402
&self,
389403
op: OpTy<'tcx, M::PointerTag>,
@@ -406,7 +420,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
406420
) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> {
407421
use rustc::mir::ProjectionElem::*;
408422
Ok(match *proj_elem {
409-
Field(field, _) => self.operand_field(base, u64::try_from(field.index()).unwrap())?,
423+
Field(field, _) => self.operand_field(base, field.index())?,
410424
Downcast(_, variant) => self.operand_downcast(base, variant)?,
411425
Deref => self.deref_operand(base)?.into(),
412426
Subslice { .. } | ConstantIndex { .. } | Index(_) => {
@@ -593,7 +607,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
593607
};
594608

595609
// read raw discriminant value
596-
let discr_op = self.operand_field(rval, u64::try_from(discr_index).unwrap())?;
610+
let discr_op = self.operand_field(rval, discr_index)?;
597611
let discr_val = self.read_immediate(discr_op)?;
598612
let raw_discr = discr_val.to_scalar_or_undef();
599613
trace!("discr value: {:?}", raw_discr);

src/librustc_mir/interpret/place.rs

Lines changed: 54 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -386,43 +386,20 @@ where
386386
Ok(place)
387387
}
388388

389-
/// Offset a pointer to project to a field. Unlike `place_field`, this is always
390-
/// possible without allocating, so it can take `&self`. Also return the field's layout.
389+
/// Offset a pointer to project to a field of a struct/union. Unlike `place_field`, this is
390+
/// always possible without allocating, so it can take `&self`. Also return the field's layout.
391391
/// This supports both struct and array fields.
392+
///
393+
/// This also works for arrays, but then the `usize` index type is restricting.
394+
/// For indexing into arrays, use `mplace_index`.
392395
#[inline(always)]
393396
pub fn mplace_field(
394397
&self,
395398
base: MPlaceTy<'tcx, M::PointerTag>,
396-
field: u64,
399+
field: usize,
397400
) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> {
398-
// Not using the layout method because we want to compute on u64
399-
let (offset, field_layout) = match base.layout.fields {
400-
layout::FieldPlacement::Arbitrary { ref offsets, .. } => {
401-
let field = usize::try_from(field).unwrap();
402-
(offsets[field], base.layout.field(self, field)?)
403-
}
404-
layout::FieldPlacement::Array { stride, .. } => {
405-
let len = base.len(self)?;
406-
if field >= len {
407-
// This can only be reached in ConstProp and non-rustc-MIR.
408-
throw_ub!(BoundsCheckFailed { len, index: field });
409-
}
410-
// All fields have the same layout.
411-
(Size::mul(stride, field), base.layout.field(self, 9)?)
412-
}
413-
layout::FieldPlacement::Union(count) => {
414-
let field = usize::try_from(field).unwrap();
415-
assert!(
416-
field < count,
417-
"Tried to access field {} of union {:#?} with {} fields",
418-
field,
419-
base.layout,
420-
count
421-
);
422-
// Offset is always 0
423-
(Size::from_bytes(0), base.layout.field(self, field)?)
424-
}
425-
};
401+
let offset = base.layout.fields.offset(field);
402+
let field_layout = base.layout.field(self, field)?;
426403

427404
// Offset may need adjustment for unsized fields.
428405
let (meta, offset) = if field_layout.is_unsized() {
@@ -452,6 +429,32 @@ where
452429
base.offset(offset, meta, field_layout, self)
453430
}
454431

432+
/// Index into an array.
433+
#[inline(always)]
434+
pub fn mplace_index(
435+
&self,
436+
base: MPlaceTy<'tcx, M::PointerTag>,
437+
index: u64,
438+
) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> {
439+
// Not using the layout method because we want to compute on u64
440+
match base.layout.fields {
441+
layout::FieldPlacement::Array { stride, .. } => {
442+
let len = base.len(self)?;
443+
if index >= len {
444+
// This can only be reached in ConstProp and non-rustc-MIR.
445+
throw_ub!(BoundsCheckFailed { len, index });
446+
}
447+
let offset = Size::mul(stride, index);
448+
// All fields have the same layout.
449+
let field_layout = base.layout.field(self, 0)?;
450+
451+
assert!(!field_layout.is_unsized());
452+
base.offset(offset, MemPlaceMeta::None, field_layout, self)
453+
}
454+
_ => bug!("`mplace_index` called on non-array type {:?}", base.layout.ty),
455+
}
456+
}
457+
455458
// Iterates over all fields of an array. Much more efficient than doing the
456459
// same by repeatedly calling `mplace_array`.
457460
pub(super) fn mplace_array_fields(
@@ -528,16 +531,19 @@ where
528531
) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> {
529532
use rustc::mir::ProjectionElem::*;
530533
Ok(match *proj_elem {
531-
Field(field, _) => self.mplace_field(base, u64::try_from(field.index()).unwrap())?,
534+
Field(field, _) => self.mplace_field(base, field.index())?,
532535
Downcast(_, variant) => self.mplace_downcast(base, variant)?,
533536
Deref => self.deref_operand(base.into())?,
534537

535538
Index(local) => {
536539
let layout = self.layout_of(self.tcx.types.usize)?;
537540
let n = self.access_local(self.frame(), local, Some(layout))?;
538541
let n = self.read_scalar(n)?;
539-
let n = self.force_bits(n.not_undef()?, self.tcx.data_layout.pointer_size)?;
540-
self.mplace_field(base, u64::try_from(n).unwrap())?
542+
let n = u64::try_from(
543+
self.force_bits(n.not_undef()?, self.tcx.data_layout.pointer_size)?,
544+
)
545+
.unwrap();
546+
self.mplace_index(base, n)?
541547
}
542548

543549
ConstantIndex { offset, min_length, from_end } => {
@@ -555,7 +561,7 @@ where
555561
u64::from(offset)
556562
};
557563

558-
self.mplace_field(base, index)?
564+
self.mplace_index(base, index)?
559565
}
560566

561567
Subslice { from, to, from_end } => {
@@ -571,14 +577,23 @@ where
571577
pub fn place_field(
572578
&mut self,
573579
base: PlaceTy<'tcx, M::PointerTag>,
574-
field: u64,
580+
field: usize,
575581
) -> InterpResult<'tcx, PlaceTy<'tcx, M::PointerTag>> {
576582
// FIXME: We could try to be smarter and avoid allocation for fields that span the
577583
// entire place.
578584
let mplace = self.force_allocation(base)?;
579585
Ok(self.mplace_field(mplace, field)?.into())
580586
}
581587

588+
pub fn place_index(
589+
&mut self,
590+
base: PlaceTy<'tcx, M::PointerTag>,
591+
index: u64,
592+
) -> InterpResult<'tcx, PlaceTy<'tcx, M::PointerTag>> {
593+
let mplace = self.force_allocation(base)?;
594+
Ok(self.mplace_index(mplace, index)?.into())
595+
}
596+
582597
pub fn place_downcast(
583598
&self,
584599
base: PlaceTy<'tcx, M::PointerTag>,
@@ -604,7 +619,7 @@ where
604619
) -> InterpResult<'tcx, PlaceTy<'tcx, M::PointerTag>> {
605620
use rustc::mir::ProjectionElem::*;
606621
Ok(match *proj_elem {
607-
Field(field, _) => self.place_field(base, u64::try_from(field.index()).unwrap())?,
622+
Field(field, _) => self.place_field(base, field.index())?,
608623
Downcast(_, variant) => self.place_downcast(base, variant)?,
609624
Deref => self.deref_operand(self.place_to_op(base)?)?.into(),
610625
// For the other variants, we have to force an allocation.
@@ -1073,7 +1088,7 @@ where
10731088
let size = discr_layout.value.size(self);
10741089
let discr_val = truncate(discr_val, size);
10751090

1076-
let discr_dest = self.place_field(dest, u64::try_from(discr_index).unwrap())?;
1091+
let discr_dest = self.place_field(dest, discr_index)?;
10771092
self.write_scalar(Scalar::from_uint(discr_val, size), discr_dest)?;
10781093
}
10791094
layout::Variants::Multiple {
@@ -1104,7 +1119,7 @@ where
11041119
niche_start_val,
11051120
)?;
11061121
// Write result.
1107-
let niche_dest = self.place_field(dest, u64::try_from(discr_index).unwrap())?;
1122+
let niche_dest = self.place_field(dest, discr_index)?;
11081123
self.write_immediate(*discr_val, niche_dest)?;
11091124
}
11101125
}

src/librustc_mir/interpret/step.rs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22
//!
33
//! The main entry point is the `step` method.
44
5-
use std::convert::TryFrom;
6-
75
use rustc::mir;
86
use rustc::mir::interpret::{InterpResult, PointerArithmetic, Scalar};
97
use rustc::ty::layout::LayoutOf;
@@ -194,8 +192,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
194192
// Ignore zero-sized fields.
195193
if !op.layout.is_zst() {
196194
let field_index = active_field_index.unwrap_or(i);
197-
let field_dest =
198-
self.place_field(dest, u64::try_from(field_index).unwrap())?;
195+
let field_dest = self.place_field(dest, field_index)?;
199196
self.copy_op(op, field_dest)?;
200197
}
201198
}

src/librustc_mir/interpret/terminator.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -309,7 +309,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
309309
.map(|&a| Ok(a))
310310
.chain(
311311
(0..untuple_arg.layout.fields.count())
312-
.map(|i| self.operand_field(untuple_arg, i as u64)),
312+
.map(|i| self.operand_field(untuple_arg, i)),
313313
)
314314
.collect::<InterpResult<'_, Vec<OpTy<'tcx, M::PointerTag>>>>(
315315
)?,
@@ -332,7 +332,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
332332
if Some(local) == body.spread_arg {
333333
// Must be a tuple
334334
for i in 0..dest.layout.fields.count() {
335-
let dest = self.place_field(dest, i as u64)?;
335+
let dest = self.place_field(dest, i)?;
336336
self.pass_argument(rust_abi, &mut caller_iter, dest)?;
337337
}
338338
} else {

src/librustc_mir/interpret/traits.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use std::convert::TryFrom;
12
use std::ops::Mul;
23

34
use rustc::mir::interpret::{InterpResult, Pointer, PointerArithmetic, Scalar};
@@ -56,7 +57,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
5657
// `get_vtable` in `rust_codegen_llvm/meth.rs`.
5758
// /////////////////////////////////////////////////////////////////////////////////////////
5859
let vtable = self.memory.allocate(
59-
ptr_size * (3 + methods.len() as u64),
60+
Size::mul(ptr_size, u64::try_from(methods.len()).unwrap().checked_add(3).unwrap()),
6061
ptr_align,
6162
MemoryKind::Vtable,
6263
);
@@ -172,10 +173,10 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
172173
.expect("cannot be a ZST");
173174
let alloc = self.memory.get_raw(vtable.alloc_id)?;
174175
let size = alloc.read_ptr_sized(self, vtable.offset(pointer_size, self)?)?.not_undef()?;
175-
let size = self.force_bits(size, pointer_size)? as u64;
176+
let size = u64::try_from(self.force_bits(size, pointer_size)?).unwrap();
176177
let align =
177178
alloc.read_ptr_sized(self, vtable.offset(pointer_size * 2, self)?)?.not_undef()?;
178-
let align = self.force_bits(align, pointer_size)? as u64;
179+
let align = u64::try_from(self.force_bits(align, pointer_size)?).unwrap();
179180

180181
if size >= self.tcx.data_layout().obj_size_bound() {
181182
throw_ub_format!(

0 commit comments

Comments
 (0)