122 lines
3.8 KiB
Rust
122 lines
3.8 KiB
Rust
// run-pass
|
|
#![allow(unused_must_use)]
|
|
// ignore-emscripten FIXME(#45351) hits an LLVM assert
|
|
|
|
#![feature(repr_simd, platform_intrinsics, concat_idents, test)]
|
|
#![allow(non_camel_case_types)]
|
|
|
|
extern crate test;
|
|
|
|
#[repr(simd)]
|
|
#[derive(PartialEq, Debug)]
|
|
struct i32x4(i32, i32, i32, i32);
|
|
#[repr(simd)]
|
|
#[derive(PartialEq, Debug)]
|
|
struct i8x4(i8, i8, i8, i8);
|
|
|
|
#[repr(simd)]
|
|
#[derive(PartialEq, Debug)]
|
|
struct u32x4(u32, u32, u32, u32);
|
|
#[repr(simd)]
|
|
#[derive(PartialEq, Debug)]
|
|
struct u8x4(u8, u8, u8, u8);
|
|
|
|
#[repr(simd)]
|
|
#[derive(PartialEq, Debug)]
|
|
struct f32x4(f32, f32, f32, f32);
|
|
|
|
#[repr(simd)]
|
|
#[derive(PartialEq, Debug)]
|
|
struct f64x4(f64, f64, f64, f64);
|
|
|
|
|
|
extern "platform-intrinsic" {
|
|
fn simd_cast<T, U>(x: T) -> U;
|
|
}
|
|
|
|
const A: i32 = -1234567;
|
|
const B: i32 = 12345678;
|
|
const C: i32 = -123456789;
|
|
const D: i32 = 1234567890;
|
|
|
|
trait Foo {
|
|
fn is_float() -> bool { false }
|
|
fn in_range(x: i32) -> bool;
|
|
}
|
|
impl Foo for i32 {
|
|
fn in_range(_: i32) -> bool { true }
|
|
}
|
|
impl Foo for i8 {
|
|
fn in_range(x: i32) -> bool { -128 <= x && x < 128 }
|
|
}
|
|
impl Foo for u32 {
|
|
fn in_range(x: i32) -> bool { 0 <= x }
|
|
}
|
|
impl Foo for u8 {
|
|
fn in_range(x: i32) -> bool { 0 <= x && x < 128 }
|
|
}
|
|
impl Foo for f32 {
|
|
fn is_float() -> bool { true }
|
|
fn in_range(_: i32) -> bool { true }
|
|
}
|
|
impl Foo for f64 {
|
|
fn is_float() -> bool { true }
|
|
fn in_range(_: i32) -> bool { true }
|
|
}
|
|
|
|
fn main() {
|
|
macro_rules! test {
|
|
($from: ident, $to: ident) => {{
|
|
// force the casts to actually happen, or else LLVM/rustc
|
|
// may fold them and get slightly different results.
|
|
let (a, b, c, d) = test::black_box((A as $from, B as $from, C as $from, D as $from));
|
|
// the SIMD vectors are all FOOx4, so we can concat_idents
|
|
// so we don't have to pass in the extra args to the macro
|
|
let mut from = simd_cast(concat_idents!($from, x4)(a, b, c, d));
|
|
let mut to = concat_idents!($to, x4)(a as $to,
|
|
b as $to,
|
|
c as $to,
|
|
d as $to);
|
|
// assist type inference, it needs to know what `from` is
|
|
// for the `if` statements.
|
|
to == from;
|
|
|
|
// there are platform differences for some out of range
|
|
// casts, so we just normalize such things: it's OK for
|
|
// "invalid" calculations to result in nonsense answers.
|
|
// (e.g., negative float to unsigned integer goes through a
|
|
// library routine on the default i686 platforms, and the
|
|
// implementation of that routine differs on e.g., Linux
|
|
// vs. macOS, resulting in different answers.)
|
|
if $from::is_float() {
|
|
if !$to::in_range(A) { from.0 = 0 as $to; to.0 = 0 as $to; }
|
|
if !$to::in_range(B) { from.1 = 0 as $to; to.1 = 0 as $to; }
|
|
if !$to::in_range(C) { from.2 = 0 as $to; to.2 = 0 as $to; }
|
|
if !$to::in_range(D) { from.3 = 0 as $to; to.3 = 0 as $to; }
|
|
}
|
|
|
|
assert!(to == from,
|
|
"{} -> {} ({:?} != {:?})", stringify!($from), stringify!($to),
|
|
from, to);
|
|
}}
|
|
}
|
|
macro_rules! tests {
|
|
(: $($to: ident),*) => { () };
|
|
// repeating the list twice is easier than writing a cartesian
|
|
// product macro
|
|
($from: ident $(, $from_: ident)*: $($to: ident),*) => {
|
|
fn $from() { unsafe { $( test!($from, $to); )* } }
|
|
tests!($($from_),*: $($to),*)
|
|
};
|
|
($($types: ident),*) => {{
|
|
tests!($($types),* : $($types),*);
|
|
$($types();)*
|
|
}}
|
|
}
|
|
|
|
// test various combinations, including truncation,
|
|
// signed/unsigned extension, and floating point casts.
|
|
tests!(i32, i8, u32, u8, f32);
|
|
tests!(i32, u32, f32, f64)
|
|
}
|