#![cfg(all(target_arch = "aarch64", target_feature = "neon"))]
use super::utils::{ComputeVector, f32x2, i32x2, u32x2};
use core::arch::aarch64::*;
use wide::{f32x4, f64x2, f64x4, i32x4, i64x2, i64x4, u32x4, u64x2, u64x4};
pub(crate) const fn pd(l0: usize, l1: usize) -> i32 { ((l0 & 1) | (l1 & 1) << 1) as i32 }
pub(crate) trait Shuffle<const MASK: i32> {
fn shuffle(a: Self, b: Self) -> Self;
}
macro_rules! impl_shuffle {
($mask:literal => $call:ident $(::<$($n:literal),+>)?) => {
impl Shuffle<$mask> for float64x2_t {
#[inline(always)]
fn shuffle(a: Self, b: Self) -> Self {
unsafe { $call$(::<$($n),+>)?(a, b) }
}
}
};
}
impl_shuffle!(0b00 => vzip1q_f64);
impl_shuffle!(0b01 => vextq_f64::<1>);
impl_shuffle!(0b10 => vcopyq_laneq_f64::<1, 1>);
impl_shuffle!(0b11 => vzip2q_f64);
#[inline(always)]
unsafe fn same_f64(v: float64x2_t) -> float64x2_t { v }
macro_rules! define_64bit_conversions {
($x4:ty, $x2:ty, $split:ident, $join:ident, $reg:ident, $val:ident, $to:ident, $from:ident) => {
#[inline(always)]
fn $reg(v: $x2) -> float64x2_t {
unsafe { $to(v.into()) }
}
#[inline(always)]
fn $val(v: float64x2_t) -> $x2 {
unsafe { $from(v) }.into()
}
#[inline(always)]
fn $split(v: $x4) -> [float64x2_t; 2] {
let [lo, hi] = unsafe { core::mem::transmute::<$x4, [$x2; 2]>(v) };
[$reg(lo), $reg(hi)]
}
#[inline(always)]
fn $join(lo: float64x2_t, hi: float64x2_t) -> $x4 {
unsafe { core::mem::transmute::<[$x2; 2], $x4>([$val(lo), $val(hi)]) }
}
};
}
define_64bit_conversions!(f64x4, f64x2, f64_split, f64_join, f64_reg, f64_val, same_f64, same_f64);
define_64bit_conversions!(
i64x4,
i64x2,
i64_split,
i64_join,
i64_reg,
i64_val,
vreinterpretq_f64_s64,
vreinterpretq_s64_f64
);
define_64bit_conversions!(
u64x4,
u64x2,
u64_split,
u64_join,
u64_reg,
u64_val,
vreinterpretq_f64_u64,
vreinterpretq_u64_f64
);
macro_rules! define_32bit_conversions {
(
$x4:ty, $x2:ty, $bytes4:ident, $value4:ident, $bytes2:ident, $value2:ident,
$to4:ident, $from4:ident, $to2:ident, $from2:ident
) => {
#[inline(always)]
fn $bytes4(v: $x4) -> uint8x16_t {
unsafe { $to4(v.into()) }
}
#[inline(always)]
fn $value4(v: uint8x16_t) -> $x4 {
unsafe { $from4(v) }.into()
}
#[inline(always)]
fn $bytes2(v: $x2) -> uint8x16_t {
unsafe { vcombine_u8($to2(v.0), vdup_n_u8(0)) }
}
#[inline(always)]
fn $value2(v: uint8x16_t) -> $x2 {
unsafe { $from2(vget_low_u8(v)) }.into()
}
};
}
define_32bit_conversions!(
f32x4,
f32x2,
f32_bytes4,
f32_value4,
f32_bytes2,
f32_value2,
vreinterpretq_u8_f32,
vreinterpretq_f32_u8,
vreinterpret_u8_f32,
vreinterpret_f32_u8
);
define_32bit_conversions!(
i32x4,
i32x2,
i32_bytes4,
i32_value4,
i32_bytes2,
i32_value2,
vreinterpretq_u8_s32,
vreinterpretq_s32_u8,
vreinterpret_u8_s32,
vreinterpret_s32_u8
);
define_32bit_conversions!(
u32x4,
u32x2,
u32_bytes4,
u32_value4,
u32_bytes2,
u32_value2,
vreinterpretq_u8_u32,
vreinterpretq_u32_u8,
vreinterpret_u8_u32,
vreinterpret_u32_u8
);
macro_rules! define_32bit_halves {
($x4:ty, $x2:ty, $narrow:ident, $widen:ident, $concat:ident, $low:ident, $combine:ident, $dup:ident, $zero:expr) => {
#[inline(always)]
fn $narrow(v: $x4) -> $x2 {
unsafe { $low(v.into()) }.into()
}
#[inline(always)]
fn $widen(v: $x2) -> $x4 {
unsafe { $combine(v.0, $dup($zero)) }.into()
}
#[inline(always)]
fn $concat(a: $x2, b: $x2) -> $x4 {
unsafe { $combine(a.0, b.0) }.into()
}
};
}
define_32bit_halves!(
f32x4,
f32x2,
f32_narrow,
f32_widen,
f32_concat,
vget_low_f32,
vcombine_f32,
vdup_n_f32,
0.
);
define_32bit_halves!(
i32x4,
i32x2,
i32_narrow,
i32_widen,
i32_concat,
vget_low_s32,
vcombine_s32,
vdup_n_s32,
0
);
define_32bit_halves!(
u32x4,
u32x2,
u32_narrow,
u32_widen,
u32_concat,
vget_low_u32,
vcombine_u32,
vdup_n_u32,
0
);
#[inline(always)]
const fn byte_table<const I0: usize, const I1: usize, const I2: usize, const I3: usize>() -> [u8; 16]
{
let lanes = [I0, I1, I2, I3];
let mut table = [0u8; 16];
let mut lane = 0;
while lane < 4 {
let mut byte = 0;
while byte < 4 {
table[lane * 4 + byte] = (lanes[lane] * 4 + byte) as u8;
byte += 1;
}
lane += 1;
}
table
}
#[inline(always)]
fn tbl1_16<const I0: usize, const I1: usize, const I2: usize, const I3: usize>(
a: uint8x16_t,
) -> uint8x16_t {
let idx = byte_table::<I0, I1, I2, I3>();
unsafe { vqtbl1q_u8(a, vld1q_u8(idx.as_ptr())) }
}
#[inline(always)]
fn tbl2_16<const I0: usize, const I1: usize, const I2: usize, const I3: usize>(
a: uint8x16_t,
b: uint8x16_t,
) -> uint8x16_t {
let idx = byte_table::<I0, I1, I2, I3>();
unsafe { vqtbl2q_u8(uint8x16x2_t(a, b), vld1q_u8(idx.as_ptr())) }
}
macro_rules! assert_lower_half {
($($i:ident),+) => {
const {
assert!(
$($i & 2 == 0 &&)+ true,
"a two-lane operand has no upper half; each lane index must select lane 0 or 1 of \
its own operand",
)
}
};
}
#[rustfmt::skip]
pub(crate) trait Swizzle: ComputeVector {
fn __xy(a: Self) -> Self::Vector2;
fn __widen(a: Self) -> Self::Vector4;
fn swizzle2<const I0: usize, const I1: usize, const PD: i32>(a: Self) -> Self::Vector2
where float64x2_t: Shuffle<PD>;
fn swizzle4<const I0: usize, const I1: usize, const I2: usize, const I3: usize, const PD_LO: i32, const PD_HI: i32>(a: Self) -> Self::Vector4
where float64x2_t: Shuffle<PD_LO> + Shuffle<PD_HI>;
}
#[rustfmt::skip]
pub(crate) trait SwizzleConcat: Swizzle {
fn concat_4(a: Self::Vector2, b: Self::Vector2) -> Self;
fn swizzle_concat2<const I0: usize, const I1: usize, const PD: i32>(a: Self, b: Self) -> Self::Vector2
where float64x2_t: Shuffle<PD>;
fn swizzle_concat4<const I0: usize, const I1: usize, const I2: usize, const I3: usize, const PD_LO: i32, const PD_HI: i32>(a: Self, b: Self) -> Self::Vector4
where float64x2_t: Shuffle<PD_LO> + Shuffle<PD_HI>;
}
macro_rules! impl_swizzle_concat_64bit {
($self:ty, $split:ident, $join:ident, $reg:ident, $val:ident) => {
#[rustfmt::skip]
impl Swizzle for $self {
#[inline(always)]
fn __xy(a: Self) -> Self::Vector2 { $val($split(a)[0]) }
#[inline(always)]
fn __widen(a: Self) -> Self::Vector4 { a }
#[inline(always)]
fn swizzle2<const I0: usize, const I1: usize, const PD: i32>(a: Self) -> Self::Vector2
where float64x2_t: Shuffle<PD>
{
let h = $split(a);
$val(<float64x2_t as Shuffle<PD>>::shuffle(h[I0 >> 1], h[I1 >> 1]))
}
#[inline(always)]
fn swizzle4<const I0: usize, const I1: usize, const I2: usize, const I3: usize, const PD_LO: i32, const PD_HI: i32>(a: Self) -> Self::Vector4
where float64x2_t: Shuffle<PD_LO> + Shuffle<PD_HI>
{
let h = $split(a);
$join(
<float64x2_t as Shuffle<PD_LO>>::shuffle(h[I0 >> 1], h[I1 >> 1]),
<float64x2_t as Shuffle<PD_HI>>::shuffle(h[I2 >> 1], h[I3 >> 1]),
)
}
}
#[rustfmt::skip]
impl SwizzleConcat for $self {
#[inline(always)]
fn concat_4(a: Self::Vector2, b: Self::Vector2) -> Self { $join($reg(a), $reg(b)) }
#[inline(always)]
fn swizzle_concat2<const I0: usize, const I1: usize, const PD: i32>(a: Self, b: Self) -> Self::Vector2
where float64x2_t: Shuffle<PD>
{
let (a, b) = ($split(a), $split(b));
let h = [a[0], a[1], b[0], b[1]];
$val(<float64x2_t as Shuffle<PD>>::shuffle(h[I0 >> 1], h[I1 >> 1]))
}
#[inline(always)]
fn swizzle_concat4<const I0: usize, const I1: usize, const I2: usize, const I3: usize, const PD_LO: i32, const PD_HI: i32>(a: Self, b: Self) -> Self::Vector4
where float64x2_t: Shuffle<PD_LO> + Shuffle<PD_HI>
{
let (a, b) = ($split(a), $split(b));
let h = [a[0], a[1], b[0], b[1]];
$join(
<float64x2_t as Shuffle<PD_LO>>::shuffle(h[I0 >> 1], h[I1 >> 1]),
<float64x2_t as Shuffle<PD_HI>>::shuffle(h[I2 >> 1], h[I3 >> 1]),
)
}
}
};
}
impl_swizzle_concat_64bit!(f64x4, f64_split, f64_join, f64_reg, f64_val);
impl_swizzle_concat_64bit!(i64x4, i64_split, i64_join, i64_reg, i64_val);
impl_swizzle_concat_64bit!(u64x4, u64_split, u64_join, u64_reg, u64_val);
macro_rules! impl_swizzle_64bit {
($self:ty, $reg:ident, $val:ident, $join:ident) => {
#[rustfmt::skip]
impl Swizzle for $self {
#[inline(always)]
fn __xy(a: Self) -> Self::Vector2 { a }
#[inline(always)]
fn __widen(a: Self) -> Self::Vector4 {
let zero = unsafe { vdupq_n_f64(0.) };
$join($reg(a), zero)
}
#[inline(always)]
fn swizzle2<const I0: usize, const I1: usize, const PD: i32>(a: Self) -> Self::Vector2
where float64x2_t: Shuffle<PD>
{
assert_lower_half!(I0, I1);
let a = $reg(a);
$val(<float64x2_t as Shuffle<PD>>::shuffle(a, a))
}
#[inline(always)]
fn swizzle4<const I0: usize, const I1: usize, const I2: usize, const I3: usize, const PD_LO: i32, const PD_HI: i32>(a: Self) -> Self::Vector4
where float64x2_t: Shuffle<PD_LO> + Shuffle<PD_HI>
{
assert_lower_half!(I0, I1, I2, I3);
let a = $reg(a);
$join(
<float64x2_t as Shuffle<PD_LO>>::shuffle(a, a),
<float64x2_t as Shuffle<PD_HI>>::shuffle(a, a),
)
}
}
};
}
impl_swizzle_64bit!(f64x2, f64_reg, f64_val, f64_join);
impl_swizzle_64bit!(i64x2, i64_reg, i64_val, i64_join);
impl_swizzle_64bit!(u64x2, u64_reg, u64_val, u64_join);
macro_rules! impl_swizzle_concat_32bit {
($self:ty, $bytes:ident, $value4:ident, $value2:ident, $narrow:ident, $concat:ident) => {
#[rustfmt::skip]
impl Swizzle for $self {
#[inline(always)]
fn __xy(a: Self) -> Self::Vector2 { $narrow(a) }
#[inline(always)]
fn __widen(a: Self) -> Self::Vector4 { a }
#[inline(always)]
fn swizzle2<const I0: usize, const I1: usize, const PD: i32>(a: Self) -> Self::Vector2
where float64x2_t: Shuffle<PD>
{
$value2(tbl1_16::<I0, I1, 0, 0>($bytes(a)))
}
#[inline(always)]
fn swizzle4<const I0: usize, const I1: usize, const I2: usize, const I3: usize, const PD_LO: i32, const PD_HI: i32>(a: Self) -> Self::Vector4
where float64x2_t: Shuffle<PD_LO> + Shuffle<PD_HI>
{
$value4(tbl1_16::<I0, I1, I2, I3>($bytes(a)))
}
}
#[rustfmt::skip]
impl SwizzleConcat for $self {
#[inline(always)]
fn concat_4(a: Self::Vector2, b: Self::Vector2) -> Self { $concat(a, b) }
#[inline(always)]
fn swizzle_concat2<const I0: usize, const I1: usize, const PD: i32>(a: Self, b: Self) -> Self::Vector2
where float64x2_t: Shuffle<PD>
{
$value2(tbl2_16::<I0, I1, 0, 0>($bytes(a), $bytes(b)))
}
#[inline(always)]
fn swizzle_concat4<const I0: usize, const I1: usize, const I2: usize, const I3: usize, const PD_LO: i32, const PD_HI: i32>(a: Self, b: Self) -> Self::Vector4
where float64x2_t: Shuffle<PD_LO> + Shuffle<PD_HI>
{
$value4(tbl2_16::<I0, I1, I2, I3>($bytes(a), $bytes(b)))
}
}
};
}
impl_swizzle_concat_32bit!(f32x4, f32_bytes4, f32_value4, f32_value2, f32_narrow, f32_concat);
impl_swizzle_concat_32bit!(i32x4, i32_bytes4, i32_value4, i32_value2, i32_narrow, i32_concat);
impl_swizzle_concat_32bit!(u32x4, u32_bytes4, u32_value4, u32_value2, u32_narrow, u32_concat);
macro_rules! impl_swizzle_32bit {
($self:ty, $bytes:ident, $value4:ident, $value2:ident, $widen:ident) => {
#[rustfmt::skip]
impl Swizzle for $self {
#[inline(always)]
fn __xy(a: Self) -> Self::Vector2 { a }
#[inline(always)]
fn __widen(a: Self) -> Self::Vector4 { $widen(a) }
#[inline(always)]
fn swizzle2<const I0: usize, const I1: usize, const PD: i32>(a: Self) -> Self::Vector2
where float64x2_t: Shuffle<PD>
{
$value2(tbl1_16::<I0, I1, 0, 0>($bytes(a)))
}
#[inline(always)]
fn swizzle4<const I0: usize, const I1: usize, const I2: usize, const I3: usize, const PD_LO: i32, const PD_HI: i32>(a: Self) -> Self::Vector4
where float64x2_t: Shuffle<PD_LO> + Shuffle<PD_HI>
{
$value4(tbl1_16::<I0, I1, I2, I3>($bytes(a)))
}
}
};
}
impl_swizzle_32bit!(f32x2, f32_bytes2, f32_value4, f32_value2, f32_widen);
impl_swizzle_32bit!(i32x2, i32_bytes2, i32_value4, i32_value2, i32_widen);
impl_swizzle_32bit!(u32x2, u32_bytes2, u32_value4, u32_value2, u32_widen);
macro_rules! swizzle4 {
($a:expr, [0, 1]) => {
$crate::simd::swizzle_arm::Swizzle::__xy($a)
};
($a:expr, [0, 1, _, _]) => {
$crate::simd::swizzle_arm::Swizzle::__widen($a)
};
($a:expr, [0, 1, 2, _]) => {
$crate::simd::swizzle_arm::Swizzle::__widen($a)
};
($a:expr, [0, 1, 2, 3]) => {
$crate::simd::swizzle_arm::Swizzle::__widen($a)
};
($a:expr, [$i0:tt]) => {
compile_error!(
"a swizzle produces at least two lanes; a single index selects a scalar, not a vector"
)
};
($a:expr, [$i0:tt, _, _, _]) => {
compile_error!(
"only the first lane is given; the other three cannot be inferred, so spell them out"
)
};
($a:expr, [$i0:tt, $i1:tt]) => {
$crate::simd::swizzle_arm::Swizzle::swizzle2::<
{ $crate::simd::utils::validate_lane4!($i0) },
{ $crate::simd::utils::validate_lane4!($i1) },
{ $crate::simd::swizzle_arm::pd($i0, $i1) },
>($a)
};
($a:expr, [$i0:tt, $i1:tt, _, _]) => {
$crate::simd::swizzle_arm::swizzle4!($a, [$i0, $i1, $i0, $i1])
};
($a:expr, [$i0:tt, $i1:tt, $i2:tt]) => {
$crate::simd::swizzle_arm::swizzle4!($a, [$i0, $i1, $i2, _])
};
($a:expr, [$i0:tt, $i1:tt, $i2:tt, _]) => {
$crate::simd::utils::complete_swizzle4!([$crate::simd::swizzle_arm::swizzle4], ($a), [
$i0,
$i1,
$i2,
_
])
};
($a:expr, [$i0:tt, $i1:tt, $i2:tt, $i3:tt]) => {
$crate::simd::swizzle_arm::Swizzle::swizzle4::<
{ $crate::simd::utils::validate_lane4!($i0) },
{ $crate::simd::utils::validate_lane4!($i1) },
{ $crate::simd::utils::validate_lane4!($i2) },
{ $crate::simd::utils::validate_lane4!($i3) },
{ $crate::simd::swizzle_arm::pd($i0, $i1) },
{ $crate::simd::swizzle_arm::pd($i2, $i3) },
>($a)
};
($a:expr, $b:expr, @concat) => {
$crate::simd::swizzle_arm::SwizzleConcat::concat_4($a, $b)
};
($a:expr, $b:expr, [$i0:tt]) => {
compile_error!(
"a swizzle produces at least two lanes; a single index selects a scalar, not a vector"
)
};
($a:expr, $b:expr, [$i0:tt, _, _, _]) => {
compile_error!(
"only the first lane is given; the other three cannot be inferred, so spell them out"
)
};
($a:expr, $b:expr, [$i0:tt, $i1:tt]) => {
$crate::simd::swizzle_arm::SwizzleConcat::swizzle_concat2::<
{ $crate::simd::utils::validate_lane8!($i0) },
{ $crate::simd::utils::validate_lane8!($i1) },
{ $crate::simd::swizzle_arm::pd($i0, $i1) },
>($a, $b)
};
($a:expr, $b:expr, [$i0:tt, $i1:tt, _, _]) => {
$crate::simd::swizzle_arm::swizzle4!($a, $b, [$i0, $i1, $i0, $i1])
};
($a:expr, $b:expr, [$i0:tt, $i1:tt, $i2:tt]) => {
$crate::simd::swizzle_arm::swizzle4!($a, $b, [$i0, $i1, $i2, _])
};
($a:expr, $b:expr, [$i0:tt, $i1:tt, $i2:tt, _]) => {
$crate::simd::utils::complete_swizzle4!([$crate::simd::swizzle_arm::swizzle4], ($a, $b), [
$i0,
$i1,
$i2,
_
])
};
($a:expr, $b:expr, [$i0:tt, $i1:tt, $i2:tt, $i3:tt]) => {
$crate::simd::swizzle_arm::SwizzleConcat::swizzle_concat4::<
{ $crate::simd::utils::validate_lane8!($i0) },
{ $crate::simd::utils::validate_lane8!($i1) },
{ $crate::simd::utils::validate_lane8!($i2) },
{ $crate::simd::utils::validate_lane8!($i3) },
{ $crate::simd::swizzle_arm::pd($i0, $i1) },
{ $crate::simd::swizzle_arm::pd($i2, $i3) },
>($a, $b)
};
}
pub(crate) use swizzle4;
#[cfg(test)]
mod tests {
use super::*;
macro_rules! define_2lane_helpers {
($ty:ty, $scalar:ty, $new:ident, $read:ident, $load:ident, $store:ident) => {
fn $new(v: [$scalar; 2]) -> $ty {
unsafe { $load(v.as_ptr()) }.into()
}
fn $read(v: $ty) -> [$scalar; 2] {
let mut out = [0 as $scalar; 2];
unsafe { $store(out.as_mut_ptr(), v.0) };
out
}
};
}
define_2lane_helpers!(f32x2, f32, f32x2_new, f32x2_read, vld1_f32, vst1_f32);
define_2lane_helpers!(i32x2, i32, i32x2_new, i32x2_read, vld1_s32, vst1_s32);
define_2lane_helpers!(u32x2, u32, u32x2_new, u32x2_read, vld1_u32, vst1_u32);
macro_rules! check_4lane {
($name:ident, $s:ty, $new:expr, $read4:expr, $read2:expr) => {
#[test]
#[rustfmt::skip]
fn $name() {
let new = $new;
let read4 = $read4;
let read2 = $read2;
let a = new([10 as $s, 11 as $s, 12 as $s, 13 as $s]);
let b = new([20 as $s, 21 as $s, 22 as $s, 23 as $s]);
assert_eq!(read4(swizzle4!(a, [0, 1, 2, 3])), [10 as $s, 11 as $s, 12 as $s, 13 as $s]);
assert_eq!(read4(swizzle4!(a, [0, 1, 2, _]))[..3], [10 as $s, 11 as $s, 12 as $s]);
assert_eq!(read4(swizzle4!(a, [0, 1, 2]))[..3], [10 as $s, 11 as $s, 12 as $s]);
assert_eq!(read4(swizzle4!(a, [0, 1, _, _]))[..2], [10 as $s, 11 as $s]);
assert_eq!(read2(swizzle4!(a, [0, 1]))[..2], [10 as $s, 11 as $s]);
assert_eq!(read4(swizzle4!(a, [3, 1, 2, 0])), [13 as $s, 11 as $s, 12 as $s, 10 as $s]);
assert_eq!(read4(swizzle4!(a, b, [7, 0, 5, 2])), [23 as $s, 10 as $s, 21 as $s, 12 as $s]);
assert_eq!(read4(swizzle4!(a, b, [0, 4, 1, 5])), [10 as $s, 20 as $s, 11 as $s, 21 as $s]);
assert_eq!(read4(swizzle4!(a, [0, 2, 1, 3])), [10 as $s, 12 as $s, 11 as $s, 13 as $s]);
assert_eq!(read4(swizzle4!(a, [2, 3, 0, 1])), [12 as $s, 13 as $s, 10 as $s, 11 as $s]);
assert_eq!(read4(swizzle4!(a, b, [0, 1, 4, 5])), [10 as $s, 11 as $s, 20 as $s, 21 as $s]);
assert_eq!(read4(swizzle4!(a, [1, 1, 1, 1])), [11 as $s, 11 as $s, 11 as $s, 11 as $s]);
assert_eq!(
read4(swizzle4!(swizzle4!(a, [0, 1]), swizzle4!(b, [0, 1]), @concat)),
[10 as $s, 11 as $s, 20 as $s, 21 as $s],
);
assert_eq!(read2(swizzle4!(a, [3, 1])), [13 as $s, 11 as $s]);
assert_eq!(read2(swizzle4!(a, b, [4, 1])), [20 as $s, 11 as $s]);
assert_eq!(read2(swizzle4!(a, b, [2, 6])), [12 as $s, 22 as $s]);
}
};
}
check_4lane!(f64x4_lanes, f64, f64x4::new, f64x4::to_array, f64x2::to_array);
check_4lane!(i64x4_lanes, i64, i64x4::new, i64x4::to_array, i64x2::to_array);
check_4lane!(u64x4_lanes, u64, u64x4::new, u64x4::to_array, u64x2::to_array);
check_4lane!(f32x4_lanes, f32, f32x4::new, f32x4::to_array, f32x2_read);
check_4lane!(i32x4_lanes, i32, i32x4::new, i32x4::to_array, i32x2_read);
check_4lane!(u32x4_lanes, u32, u32x4::new, u32x4::to_array, u32x2_read);
macro_rules! check_2lane {
($name:ident, $s:ty, $new:expr, $read2:expr, $read4:expr) => {
#[test]
#[rustfmt::skip]
fn $name() {
let new = $new;
let read2 = $read2;
let read4 = $read4;
let a = new([10 as $s, 11 as $s]);
assert_eq!(read2(swizzle4!(a, [1, 0])), [11 as $s, 10 as $s]);
assert_eq!(read2(swizzle4!(a, [0, 0])), [10 as $s, 10 as $s]);
assert_eq!(read4(swizzle4!(a, [1, 0, 0, 1])), [11 as $s, 10 as $s, 10 as $s, 11 as $s]);
}
};
}
check_2lane!(f64x2_lanes, f64, f64x2::new, f64x2::to_array, f64x4::to_array);
check_2lane!(i64x2_lanes, i64, i64x2::new, i64x2::to_array, i64x4::to_array);
check_2lane!(u64x2_lanes, u64, u64x2::new, u64x2::to_array, u64x4::to_array);
check_2lane!(f32x2_lanes, f32, f32x2_new, f32x2_read, f32x4::to_array);
check_2lane!(i32x2_lanes, i32, i32x2_new, i32x2_read, i32x4::to_array);
check_2lane!(u32x2_lanes, u32, u32x2_new, u32x2_read, u32x4::to_array);
}