Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add const-eval support for SIMD types, insert, and extract #64738

Merged
merged 12 commits into from
Sep 25, 2019
20 changes: 19 additions & 1 deletion src/librustc_mir/interpret/intrinsics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,25 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
"transmute" => {
self.copy_op_transmute(args[0], dest)?;
}

"simd_insert" => {
let mut vector = self.read_vector(args[0])?;
oli-obk marked this conversation as resolved.
Show resolved Hide resolved
let index = self.read_scalar(args[1])?.to_u32()? as usize;
let scalar = self.read_immediate(args[2])?;
if vector[index].layout.size == scalar.layout.size {
vector[index] = scalar;
} else {
throw_ub_format!(
gnzlbg marked this conversation as resolved.
Show resolved Hide resolved
"Inserting `{:?}` with size `{}` to a vector element place of size `{}`",
scalar, scalar.layout.size.bytes(), vector[index].layout.size.bytes()
);
}
self.write_vector(vector, dest)?;
}
"simd_extract" => {
let index = self.read_scalar(args[1])?.to_u32()? as _;
let scalar = self.read_immediate(self.operand_field(args[0], index)?)?;
self.write_immediate(*scalar, dest)?;
}
_ => return Ok(false),
}

Expand Down
15 changes: 15 additions & 0 deletions src/librustc_mir/interpret/operand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,21 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
}
}

/// Read vector from operand `op`
pub fn read_vector(&self, op: OpTy<'tcx, M::PointerTag>)
-> InterpResult<'tcx, Vec<ImmTy<'tcx, M::PointerTag>>> {
if let layout::Abi::Vector { count, .. } = op.layout.abi {
assert_ne!(count, 0);
let mut scalars = Vec::new();
for index in 0..count {
scalars.push(self.read_immediate(self.operand_field(op, index as _)?)?);
}
Ok(scalars)
} else {
bug!("type is not a vector: {:?}, abi: {:?}", op.layout.ty, op.layout.abi);
}
}

/// Read a scalar from a place
pub fn read_scalar(
&self,
Expand Down
34 changes: 34 additions & 0 deletions src/librustc_mir/interpret/place.rs
Original file line number Diff line number Diff line change
Expand Up @@ -696,6 +696,40 @@ where
Ok(())
}

/// Writes the `scalar` to the `index`-th element of the `vector`.
pub fn write_scalar_to_vector(
&mut self,
scalar: ImmTy<'tcx, M::PointerTag>,
vector: PlaceTy<'tcx, M::PointerTag>,
index: usize,
) -> InterpResult<'tcx> {
let index = index as u64;
let place = self.place_field(vector, index)?;
self.write_immediate(*scalar, place)?;
Ok(())
}

/// Writes the `scalars` to the `vector`.
pub fn write_vector(
&mut self,
scalars: Vec<ImmTy<'tcx, M::PointerTag>>,
vector: PlaceTy<'tcx, M::PointerTag>,
) -> InterpResult<'tcx> {
assert_ne!(scalars.len(), 0);
match vector.layout.ty.sty {
ty::Adt(def, ..) if def.repr.simd() => {
let tcx = &*self.tcx;
let count = vector.layout.ty.simd_size(*tcx);
assert_eq!(count, scalars.len());
for index in 0..scalars.len() {
self.write_scalar_to_vector(scalars[index], vector, index)?;
}
}
_ => bug!("not a vector"),
}
Ok(())
}

/// Write an `Immediate` to memory.
#[inline(always)]
pub fn write_immediate_to_mplace(
Expand Down
4 changes: 3 additions & 1 deletion src/librustc_mir/interpret/terminator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,9 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {

match instance.def {
ty::InstanceDef::Intrinsic(..) => {
if caller_abi != Abi::RustIntrinsic {
if let Abi::RustIntrinsic | Abi::PlatformIntrinsic = caller_abi {
gnzlbg marked this conversation as resolved.
Show resolved Hide resolved
// ok
} else {
throw_unsup!(FunctionAbiMismatch(caller_abi, Abi::RustIntrinsic))
}
// The intrinsic itself cannot diverge, so if we got here without a return
Expand Down
24 changes: 24 additions & 0 deletions src/test/ui/consts/const-eval/const_eval-simd.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// run-pass
// compile-flags: -Zunleash-the-miri-inside-of-you
#![feature(repr_simd)]
#![feature(platform_intrinsics)]
#![allow(non_camel_case_types)]

#[repr(simd)] struct i8x1(i8);

extern "platform-intrinsic" {
fn simd_insert<T, U>(x: T, idx: u32, val: U) -> T;
fn simd_extract<T, U>(x: T, idx: u32) -> U;
}

const fn foo(x: i8x1) -> i8 {
unsafe { simd_insert(x, 0_u32, 42_i8) }.0
}

fn main() {
const V: i8x1 = i8x1(13);
const X: i8 = foo(V);
const Y: i8 = unsafe { simd_extract(V, 0) };
assert_eq!(X, 42);
assert_eq!(Y, 13);
}
48 changes: 48 additions & 0 deletions src/test/ui/consts/const-eval/const_eval-simd.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
warning: skipping const checks
--> $DIR/const_eval-simd.rs:22:5
|
LL | assert_eq!(X, 42);
| ^^^^^^^^^^^^^^^^^^
|
= note: this warning originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)

warning: skipping const checks
--> $DIR/const_eval-simd.rs:22:5
|
LL | assert_eq!(X, 42);
| ^^^^^^^^^^^^^^^^^^
|
= note: this warning originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)

warning: skipping const checks
--> $DIR/const_eval-simd.rs:22:5
|
LL | assert_eq!(X, 42);
| ^^^^^^^^^^^^^^^^^^
|
= note: this warning originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)

warning: skipping const checks
--> $DIR/const_eval-simd.rs:23:5
|
LL | assert_eq!(Y, 13);
| ^^^^^^^^^^^^^^^^^^
|
= note: this warning originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)

warning: skipping const checks
--> $DIR/const_eval-simd.rs:23:5
|
LL | assert_eq!(Y, 13);
| ^^^^^^^^^^^^^^^^^^
|
= note: this warning originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)

warning: skipping const checks
--> $DIR/const_eval-simd.rs:23:5
|
LL | assert_eq!(Y, 13);
| ^^^^^^^^^^^^^^^^^^
|
= note: this warning originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)

20 changes: 20 additions & 0 deletions src/test/ui/consts/const-eval/const_eval-simd_fail.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// compile-flags: -Zunleash-the-miri-inside-of-you
#![feature(repr_simd)]
#![feature(platform_intrinsics)]
#![allow(non_camel_case_types)]

#[repr(simd)] struct i8x1(i8);

extern "platform-intrinsic" {
fn simd_insert<T, U>(x: T, idx: u32, val: U) -> T;
}

const fn foo(x: i8x1) -> i8 {
// 42 is a i16 that does not fit in a i8
unsafe { simd_insert(x, 0_u32, 42_i16) }.0 //~ ERROR
}

fn main() {
const V: i8x1 = i8x1(13);
const X: i8 = foo(V);
}
16 changes: 16 additions & 0 deletions src/test/ui/consts/const-eval/const_eval-simd_fail.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
error: any use of this value will cause an error
--> $DIR/const_eval-simd_fail.rs:14:14
|
LL | unsafe { simd_insert(x, 0_u32, 42_i16) }.0
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| |
| Inserting `i16` with size `2` to a vector element place of size `1`
| inside call to `foo` at $DIR/const_eval-simd_fail.rs:19:19
...
LL | const X: i8 = foo(V);
| ---------------------
|
= note: `#[deny(const_err)]` on by default

error: aborting due to previous error