makepad/tools/run/fixture/diff/simd_generic.rs

166 lines
7.1 KiB
Rust

// diff_fast corpus program: rustc's generic SIMD intrinsics (core::intrinsics::simd) on #[repr(simd)] types of
// every lane type and widths 8..32 bytes, edge and pseudo-random lanes; every result line is `D ...`.
// (rustc side: nightly features through RUSTC_BOOTSTRAP=1, set by diff_fast.sh for programs with #![feature].)
// NaN results print canonically (Rust leaves NaN payloads unspecified); minimum/maximum_number_nsz print
// zeros without sign (either zero may be returned).
#![feature(repr_simd, core_intrinsics)]
#![allow(non_camel_case_types, internal_features)]
use core::intrinsics::simd::*;
macro_rules! d { ($($a:tt)*) => { println!("D {}", format!($($a)*)) } }
fn rounds(f: fn()) {
let ms: u64 = std::env::var("DIFF_SLEEP_MS").ok().and_then(|v| v.parse().ok()).unwrap_or(0);
for r in 0..4 { f(); if r < 3 && ms > 0 { std::thread::sleep(std::time::Duration::from_millis(ms)); } }
}
#[test]
fn run() { rounds(work); }
struct Rng(u64);
impl Rng {
fn next(&mut self) -> u64 {
self.0 ^= self.0 << 13;
self.0 ^= self.0 >> 7;
self.0 ^= self.0 << 17;
self.0
}
}
fn mix(h: &mut u64, x: u64) {
*h = (*h ^ x).wrapping_mul(0x100000001b3).rotate_left(7);
}
const EDGE: [u64; 12] = [0, 1, 2, 7, u64::MAX, 0x8000000000000000, 0x7fffffffffffffff, 0x80, 0x7f, 0x8000, 0x7fff, 0x80000000];
macro_rules! ints {
($f:ident, $v:ident, $m:ident, $e:ty, $n:expr) => {
#[repr(simd)]
#[derive(Copy, Clone)]
struct $v([$e; $n]);
#[repr(simd)]
#[derive(Copy, Clone)]
struct $m([$e; $n]);
#[inline(never)]
fn $f(rng: &mut Rng) {
let bits = <$e>::BITS as u64;
let mut hs = [0u64; 20];
for _ in 0..64 {
let mut a = [0 as $e; $n];
let mut b = [0 as $e; $n];
let mut s = [0 as $e; $n];
for k in 0..$n {
let r = rng.next();
a[k] = (if r % 3 == 0 { rng.next() } else { EDGE[(r >> 8) as usize % EDGE.len()] }) as $e;
let r = rng.next();
b[k] = (if r % 3 == 0 { rng.next() } else { EDGE[(r >> 8) as usize % EDGE.len()] }) as $e;
s[k] = (rng.next() % bits) as $e;
}
let (va, vb, vs) = ($v(a), $v(b), $v(s));
let r: [$v; 13] = unsafe {
[
simd_add(va, vb), simd_sub(va, vb), simd_mul(va, vb), simd_and(va, vb), simd_or(va, vb),
simd_xor(va, vb), simd_shl(va, vs), simd_shr(va, vs), simd_sub(simd_xor(va, va), va),
simd_saturating_add(va, vb), simd_saturating_sub(va, vb),
simd_select(simd_lt::<$v, $m>(va, vb), va, vb), simd_select(simd_ge::<$v, $m>(va, vb), vb, va),
]
};
let m: [$m; 6] = unsafe { [simd_eq(va, vb), simd_ne(va, vb), simd_lt(va, vb), simd_le(va, vb), simd_gt(va, vb), simd_ge(va, vb)] };
for (i, v) in r.iter().enumerate() {
let l: [$e; $n] = unsafe { core::mem::transmute(*v) };
for x in l { mix(&mut hs[i], x as u64); }
}
for (i, v) in m.iter().enumerate() {
let l: [$e; $n] = unsafe { core::mem::transmute(*v) };
for x in l { mix(&mut hs[13 + i], x as u64); }
}
}
d!("{} {:016x?}", stringify!($v), hs);
}
};
}
macro_rules! floats {
($f:ident, $v:ident, $m:ident, $e:ty, $mi:ty, $n:expr) => {
#[repr(simd)]
#[derive(Copy, Clone)]
struct $v([$e; $n]);
#[repr(simd)]
#[derive(Copy, Clone)]
struct $m([$mi; $n]);
#[inline(never)]
fn $f(rng: &mut Rng) {
let edge: [$e; 16] = [0.0, -0.0, 1.0, -1.0, 0.5, 1.5, 2.5, -2.5, <$e>::INFINITY, <$e>::NEG_INFINITY, <$e>::NAN, <$e>::MAX, <$e>::MIN_POSITIVE, 1e-40 as $e, 0.49999997, 3.0e9];
let canon = |x: $e, nsz: bool| -> u64 {
if x.is_nan() { 1 } else if nsz && x == 0.0 { 0 } else { x.to_bits() as u64 }
};
let mut hs = [0u64; 21];
for _ in 0..64 {
let mut a = [0.0 as $e; $n];
let mut b = [0.0 as $e; $n];
for k in 0..$n {
let r = rng.next();
a[k] = if r % 4 == 0 { (rng.next() as i64 as $e) * 1e-12 } else { edge[(r >> 8) as usize % 16] };
let r = rng.next();
b[k] = if r % 4 == 0 { (rng.next() as i64 as $e) * 1e-15 } else { edge[(r >> 8) as usize % 16] };
}
let (va, vb) = ($v(a), $v(b));
let r: [$v; 13] = unsafe {
[
simd_add(va, vb), simd_sub(va, vb), simd_mul(va, vb), simd_div(va, vb), simd_neg(va), simd_fabs(va),
simd_fsqrt(va), simd_floor(va), simd_ceil(va), simd_trunc(va), simd_round(va),
simd_select(simd_lt::<$v, $m>(va, vb), va, vb), simd_select(simd_ne::<$v, $m>(va, vb), vb, va),
]
};
let mm: [$v; 2] = unsafe { [simd_minimum_number_nsz(va, vb), simd_maximum_number_nsz(va, vb)] };
let m: [$m; 6] = unsafe { [simd_eq(va, vb), simd_ne(va, vb), simd_lt(va, vb), simd_le(va, vb), simd_gt(va, vb), simd_ge(va, vb)] };
for (i, v) in r.iter().enumerate() {
let l: [$e; $n] = unsafe { core::mem::transmute(*v) };
for x in l { mix(&mut hs[i], canon(x, false)); }
}
for (i, v) in mm.iter().enumerate() {
let l: [$e; $n] = unsafe { core::mem::transmute(*v) };
for x in l { mix(&mut hs[13 + i], canon(x, true)); }
}
for (i, v) in m.iter().enumerate() {
let l: [$mi; $n] = unsafe { core::mem::transmute(*v) };
for x in l { mix(&mut hs[15 + i], x as u64); }
}
}
d!("{} {:016x?}", stringify!($v), hs);
}
};
}
ints!(t_i8x16, i8x16, m8x16, i8, 16);
ints!(t_u8x16, u8x16, mu8x16, u8, 16);
ints!(t_u8x8, u8x8, mu8x8, u8, 8);
ints!(t_i16x8, i16x8, m16x8, i16, 8);
ints!(t_u16x16, u16x16, mu16x16, u16, 16);
ints!(t_i32x4, i32x4, m32x4, i32, 4);
ints!(t_u32x2, u32x2, mu32x2, u32, 2);
ints!(t_u32x8, u32x8, mu32x8, u32, 8);
ints!(t_i64x2, i64x2, m64x2, i64, 2);
ints!(t_u64x4, u64x4, mu64x4, u64, 4);
floats!(t_f32x4, f32x4, mf32x4, f32, i32, 4);
floats!(t_f32x2, f32x2, mf32x2, f32, i32, 2);
floats!(t_f32x8, f32x8, mf32x8, f32, i32, 8);
floats!(t_f64x2, f64x2, mf64x2, f64, i64, 2);
floats!(t_f64x4, f64x4, mf64x4, f64, i64, 4);
fn work() {
let mut rng = Rng(0x2545_f491_4f6c_dd1d);
t_i8x16(&mut rng);
t_u8x16(&mut rng);
t_u8x8(&mut rng);
t_i16x8(&mut rng);
t_u16x16(&mut rng);
t_i32x4(&mut rng);
t_u32x2(&mut rng);
t_u32x8(&mut rng);
t_i64x2(&mut rng);
t_u64x4(&mut rng);
t_f32x4(&mut rng);
t_f32x2(&mut rng);
t_f32x8(&mut rng);
t_f64x2(&mut rng);
t_f64x4(&mut rng);
}