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.layout.ty, 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
2 changes: 2 additions & 0 deletions src/librustc_mir/transform/qualify_consts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -557,6 +557,8 @@ impl Qualif for IsNotPromotable {
| "saturating_add"
| "saturating_sub"
| "transmute"
| "simd_insert"
| "simd_extract"
=> return true,

_ => {}
Expand Down
20 changes: 20 additions & 0 deletions src/test/ui/consts/const-eval/simd/insert_extract-fail.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#![feature(const_fn)]
#![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/simd/insert_extract-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/insert_extract-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/insert_extract-fail.rs:19:19
gnzlbg marked this conversation as resolved.
Show resolved Hide resolved
...
LL | const X: i8 = foo(V);
| ---------------------
|
= note: `#[deny(const_err)]` on by default

error: aborting due to previous error

53 changes: 53 additions & 0 deletions src/test/ui/consts/const-eval/simd/insert_extract.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// run-pass
#![feature(const_fn)]
#![feature(repr_simd)]
#![feature(platform_intrinsics)]
#![allow(non_camel_case_types)]

#[repr(simd)] struct i8x1(i8);
#[repr(simd)] struct u16x2(u16, u16);
#[repr(simd)] struct f32x3(f32, f32, f32);

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;
}

fn main() {
{
const U: i8x1 = i8x1(13);
const V: i8x1 = unsafe { simd_insert(U, 0_u32, 42_i8) };
const X0: i8 = V.0;
const Y0: i8 = unsafe { simd_extract(V, 0) };
assert_eq!(X0, 42);
assert_eq!(Y0, 42);
}
{
const U: u16x2 = u16x2(13, 14);
const V: u16x2 = unsafe { simd_insert(U, 1_u32, 42_u16) };
const X0: u16 = V.0;
const X1: u16 = V.1;
const Y0: u16 = unsafe { simd_extract(V, 0) };
const Y1: u16 = unsafe { simd_extract(V, 1) };
assert_eq!(X0, 13);
assert_eq!(X1, 42);
assert_eq!(Y0, 13);
assert_eq!(Y1, 42);
}
{
const U: f32x3 = f32x3(13., 14., 15.);
const V: f32x3 = unsafe { simd_insert(U, 1_u32, 42_f32) };
const X0: f32 = V.0;
const X1: f32 = V.1;
const X2: f32 = V.2;
const Y0: f32 = unsafe { simd_extract(V, 0) };
const Y1: f32 = unsafe { simd_extract(V, 1) };
const Y2: f32 = unsafe { simd_extract(V, 2) };
assert_eq!(X0, 13.);
assert_eq!(X1, 42.);
assert_eq!(X2, 15.);
assert_eq!(Y0, 13.);
assert_eq!(Y1, 42.);
assert_eq!(Y2, 15.);
}
}