Rollup merge of #90999 - RalfJung:miri_simd, r=oli-obk

fix CTFE/Miri simd_insert/extract on array-style repr(simd) types

The changed test would previously fail since `place_index` would just return the only field of `f32x4`, i.e., the array -- rather than *indexing into* the array which is what we have to do.

The new helper methods will also be needed for https://github.com/rust-lang/miri/issues/1912.

r? ``````@oli-obk``````
This commit is contained in:
Matthias Krüger 2021-11-20 01:09:41 +01:00 committed by GitHub
commit cf69f9e220
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 76 additions and 38 deletions

View file

@ -419,48 +419,33 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
sym::simd_insert => {
let index = u64::from(self.read_scalar(&args[1])?.to_u32()?);
let elem = &args[2];
let input = &args[0];
let (len, e_ty) = input.layout.ty.simd_size_and_type(*self.tcx);
let (input, input_len) = self.operand_to_simd(&args[0])?;
let (dest, dest_len) = self.place_to_simd(dest)?;
assert_eq!(input_len, dest_len, "Return vector length must match input length");
assert!(
index < len,
"Index `{}` must be in bounds of vector type `{}`: `[0, {})`",
index < dest_len,
"Index `{}` must be in bounds of vector with length {}`",
index,
e_ty,
len
);
assert_eq!(
input.layout, dest.layout,
"Return type `{}` must match vector type `{}`",
dest.layout.ty, input.layout.ty
);
assert_eq!(
elem.layout.ty, e_ty,
"Scalar element type `{}` must match vector element type `{}`",
elem.layout.ty, e_ty
dest_len
);
for i in 0..len {
let place = self.place_index(dest, i)?;
let value = if i == index { *elem } else { self.operand_index(input, i)? };
self.copy_op(&value, &place)?;
for i in 0..dest_len {
let place = self.mplace_index(&dest, i)?;
let value =
if i == index { *elem } else { self.mplace_index(&input, i)?.into() };
self.copy_op(&value, &place.into())?;
}
}
sym::simd_extract => {
let index = u64::from(self.read_scalar(&args[1])?.to_u32()?);
let (len, e_ty) = args[0].layout.ty.simd_size_and_type(*self.tcx);
let (input, input_len) = self.operand_to_simd(&args[0])?;
assert!(
index < len,
"index `{}` is out-of-bounds of vector type `{}` with length `{}`",
index < input_len,
"index `{}` must be in bounds of vector with length `{}`",
index,
e_ty,
len
input_len
);
assert_eq!(
e_ty, dest.layout.ty,
"Return type `{}` must match vector element type `{}`",
dest.layout.ty, e_ty
);
self.copy_op(&self.operand_index(&args[0], index)?, dest)?;
self.copy_op(&self.mplace_index(&input, index)?.into(), dest)?;
}
sym::likely | sym::unlikely | sym::black_box => {
// These just return their argument

View file

@ -437,6 +437,18 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
})
}
/// Converts a repr(simd) operand into an operand where `place_index` accesses the SIMD elements.
/// Also returns the number of elements.
pub fn operand_to_simd(
&self,
base: &OpTy<'tcx, M::PointerTag>,
) -> InterpResult<'tcx, (MPlaceTy<'tcx, M::PointerTag>, u64)> {
// Basically we just transmute this place into an array following simd_size_and_type.
// This only works in memory, but repr(simd) types should never be immediates anyway.
assert!(base.layout.ty.is_simd());
self.mplace_to_simd(&base.assert_mem_place())
}
/// Read from a local. Will not actually access the local if reading from a ZST.
/// Will not access memory, instead an indirect `Operand` is returned.
///

View file

@ -200,7 +200,7 @@ impl<'tcx, Tag: Provenance> MPlaceTy<'tcx, Tag> {
}
} else {
// Go through the layout. There are lots of types that support a length,
// e.g., SIMD types.
// e.g., SIMD types. (But not all repr(simd) types even have FieldsShape::Array!)
match self.layout.fields {
FieldsShape::Array { count, .. } => Ok(count),
_ => bug!("len not supported on sized type {:?}", self.layout.ty),
@ -533,6 +533,22 @@ where
})
}
/// Converts a repr(simd) place into a place where `place_index` accesses the SIMD elements.
/// Also returns the number of elements.
pub fn mplace_to_simd(
&self,
base: &MPlaceTy<'tcx, M::PointerTag>,
) -> InterpResult<'tcx, (MPlaceTy<'tcx, M::PointerTag>, u64)> {
// Basically we just transmute this place into an array following simd_size_and_type.
// (Transmuting is okay since this is an in-memory place. We also double-check the size
// stays the same.)
let (len, e_ty) = base.layout.ty.simd_size_and_type(*self.tcx);
let array = self.tcx.mk_array(e_ty, len);
let layout = self.layout_of(array)?;
assert_eq!(layout.size, base.layout.size);
Ok((MPlaceTy { layout, ..*base }, len))
}
/// Gets the place of a field inside the place, and also the field's type.
/// Just a convenience function, but used quite a bit.
/// This is the only projection that might have a side-effect: We cannot project
@ -594,6 +610,16 @@ where
})
}
/// Converts a repr(simd) place into a place where `place_index` accesses the SIMD elements.
/// Also returns the number of elements.
pub fn place_to_simd(
&mut self,
base: &PlaceTy<'tcx, M::PointerTag>,
) -> InterpResult<'tcx, (MPlaceTy<'tcx, M::PointerTag>, u64)> {
let mplace = self.force_allocation(base)?;
self.mplace_to_simd(&mplace)
}
/// Computes a place. You should only use this if you intend to write into this
/// place; for reading, a more efficient alternative is `eval_place_for_read`.
pub fn eval_place(