|
| 1 | +//! `UnsignedIntegerVector::abs_diff` correctness. |
| 2 | +//! |
| 3 | +//! `a.abs_diff(b)` is the per-lane unsigned `|a - b|`, computed branchlessly as |
| 4 | +//! `(a -| b) | (b -| a)` with saturating subtraction. Verified against Rust's |
| 5 | +//! `uN::abs_diff` oracle over all ordered pairs of a probe set (covering equal, |
| 6 | +//! a<b, a>b, and the 0/MAX corners). The default composes `saturating_sub`, |
| 7 | +//! which is natively overridden per backend, so this is exercised across scalar |
| 8 | +//! and x86 v1/v2/v3. |
| 9 | +
|
| 10 | +use thermite::backend::scalar::Scalar; |
| 11 | +use thermite::prelude::*; |
| 12 | + |
| 13 | +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] |
| 14 | +use thermite::backend::{x86_v1::X86V1, x86_v2::X86V2, x86_v3::X86V3}; |
| 15 | + |
| 16 | +macro_rules! check { |
| 17 | + ($name:ident, $vty:ty, $elem:ty, $n:literal) => { |
| 18 | + #[test] |
| 19 | + fn $name() { |
| 20 | + type V = $vty; |
| 21 | + const N: usize = $n; |
| 22 | + |
| 23 | + let max = <$elem>::MAX; |
| 24 | + let probes: [$elem; 8] = [0, 1, 2, 127, 128, max / 2, max - 1, max]; |
| 25 | + let p = probes.len(); |
| 26 | + |
| 27 | + // Every ordered (probe, rotated-probe) pair across the lanes. |
| 28 | + for shift in 0..p { |
| 29 | + let mut da = [0 as $elem; N]; |
| 30 | + let mut db = [0 as $elem; N]; |
| 31 | + for i in 0..N { |
| 32 | + da[i] = probes[i % p]; |
| 33 | + db[i] = probes[(i + shift) % p]; |
| 34 | + } |
| 35 | + let got = V::new(da).abs_diff(V::new(db)); |
| 36 | + |
| 37 | + for i in 0..N { |
| 38 | + assert_eq!( |
| 39 | + got.as_slice()[i], |
| 40 | + da[i].abs_diff(db[i]), |
| 41 | + "abs_diff a={} b={} lane={}", |
| 42 | + da[i], db[i], i |
| 43 | + ); |
| 44 | + } |
| 45 | + } |
| 46 | + } |
| 47 | + }; |
| 48 | +} |
| 49 | + |
| 50 | +macro_rules! suite { |
| 51 | + ($modname:ident, $backend:ty) => { |
| 52 | + mod $modname { |
| 53 | + use super::*; |
| 54 | + check!(u8x16, thermite::simd::u8x16<$backend>, u8, 16); |
| 55 | + check!(u16x8, thermite::simd::u16x8<$backend>, u16, 8); |
| 56 | + check!(u16x16, thermite::simd::u16x16<$backend>, u16, 16); |
| 57 | + check!(u32x4, thermite::simd::u32x4<$backend>, u32, 4); |
| 58 | + check!(u32x8, thermite::simd::u32x8<$backend>, u32, 8); |
| 59 | + check!(u64x2, thermite::simd::u64x2<$backend>, u64, 2); |
| 60 | + check!(u64x4, thermite::simd::u64x4<$backend>, u64, 4); |
| 61 | + } |
| 62 | + }; |
| 63 | +} |
| 64 | + |
| 65 | +suite!(scalar, Scalar); |
| 66 | + |
| 67 | +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] |
| 68 | +suite!(v1, X86V1); |
| 69 | +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] |
| 70 | +suite!(v2, X86V2); |
| 71 | +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] |
| 72 | +suite!(v3, X86V3); |
0 commit comments