#![cfg(target_feature = "simd128")]
use crate::simd::utils::ComputeVector;
use core::arch::wasm32::{u32x4_shuffle, u64x2_shuffle, u64x2_splat, v128};
use wide::{f32x4, f64x2, f64x4, i32x4, i64x2, i64x4, u32x4, u64x2, u64x4};
pub(crate) const fn first(index: usize) -> usize { index & 1 }
pub(crate) const fn second(index: usize) -> usize { 2 + (index & 1) }
macro_rules! define_conversions {
($ty:ty, $reg:ident, $val:ident) => {
#[inline(always)]
fn $reg(v: $ty) -> v128 { v.into() }
#[inline(always)]
fn $val(v: v128) -> $ty { v.into() }
};
}
define_conversions!(f32x4, f32_reg, f32_val);
define_conversions!(i32x4, i32_reg, i32_val);
define_conversions!(u32x4, u32_reg, u32_val);
define_conversions!(f64x2, f64_reg, f64_val);
define_conversions!(i64x2, i64_reg, i64_val);
define_conversions!(u64x2, u64_reg, u64_val);
macro_rules! define_split_join {
($x4:ty, $x2:ty, $split:ident, $join:ident, $reg:ident, $val:ident) => {
#[inline(always)]
fn $split(v: $x4) -> [v128; 2] {
let [lo, hi] = unsafe { core::mem::transmute::<$x4, [$x2; 2]>(v) };
[$reg(lo), $reg(hi)]
}
#[inline(always)]
fn $join(lo: v128, hi: v128) -> $x4 {
unsafe { core::mem::transmute::<[$x2; 2], $x4>([$val(lo), $val(hi)]) }
}
};
}
define_split_join!(f64x4, f64x2, f64_split, f64_join, f64_reg, f64_val);
define_split_join!(i64x4, i64x2, i64_split, i64_join, i64_reg, i64_val);
define_split_join!(u64x4, u64x2, u64_split, u64_join, u64_reg, u64_val);
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 S0: usize, const S1: usize>(a: Self) -> Self::Vector2;
fn swizzle4<const I0: usize, const I1: usize, const I2: usize, const I3: usize, const S0: usize, const S1: usize, const S2: usize, const S3: usize>(a: Self) -> Self::Vector4;
}
#[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 S0: usize, const S1: usize>(a: Self, b: Self) -> Self::Vector2;
fn swizzle_concat4<const I0: usize, const I1: usize, const I2: usize, const I3: usize, const S0: usize, const S1: usize, const S2: usize, const S3: usize>(a: Self, b: Self) -> Self::Vector4;
}
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 S0: usize, const S1: usize>(a: Self) -> Self::Vector2 {
let h = $split(a);
$val(u64x2_shuffle::<S0, S1>(h[I0 >> 1], h[I1 >> 1]))
}
#[inline(always)]
fn swizzle4<const I0: usize, const I1: usize, const I2: usize, const I3: usize, const S0: usize, const S1: usize, const S2: usize, const S3: usize>(a: Self) -> Self::Vector4 {
let h = $split(a);
$join(
u64x2_shuffle::<S0, S1>(h[I0 >> 1], h[I1 >> 1]),
u64x2_shuffle::<S2, S3>(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 S0: usize, const S1: usize>(a: Self, b: Self) -> Self::Vector2 {
let (a, b) = ($split(a), $split(b));
let h = [a[0], a[1], b[0], b[1]];
$val(u64x2_shuffle::<S0, S1>(h[I0 >> 1], h[I1 >> 1]))
}
#[inline(always)]
fn swizzle_concat4<const I0: usize, const I1: usize, const I2: usize, const I3: usize, const S0: usize, const S1: usize, const S2: usize, const S3: usize>(a: Self, b: Self) -> Self::Vector4 {
let (a, b) = ($split(a), $split(b));
let h = [a[0], a[1], b[0], b[1]];
$join(
u64x2_shuffle::<S0, S1>(h[I0 >> 1], h[I1 >> 1]),
u64x2_shuffle::<S2, S3>(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 { $join($reg(a), u64x2_splat(0)) }
#[inline(always)]
fn swizzle2<const I0: usize, const I1: usize, const S0: usize, const S1: usize>(a: Self) -> Self::Vector2 {
assert_lower_half!(I0, I1);
let a = $reg(a);
$val(u64x2_shuffle::<S0, S1>(a, a))
}
#[inline(always)]
fn swizzle4<const I0: usize, const I1: usize, const I2: usize, const I3: usize, const S0: usize, const S1: usize, const S2: usize, const S3: usize>(a: Self) -> Self::Vector4 {
assert_lower_half!(I0, I1, I2, I3);
let a = $reg(a);
$join(u64x2_shuffle::<S0, S1>(a, a), u64x2_shuffle::<S2, S3>(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_32bit {
($self:ty, $reg:ident, $val:ident) => {
#[rustfmt::skip]
impl Swizzle for $self {
#[inline(always)]
fn __xy(a: Self) -> Self::Vector2 { a }
#[inline(always)]
fn __widen(a: Self) -> Self::Vector4 { a }
#[inline(always)]
fn swizzle2<const I0: usize, const I1: usize, const S0: usize, const S1: usize>(a: Self) -> Self::Vector2 {
$val(u32x4_shuffle::<I0, I1, I0, I1>($reg(a), $reg(a)))
}
#[inline(always)]
fn swizzle4<const I0: usize, const I1: usize, const I2: usize, const I3: usize, const S0: usize, const S1: usize, const S2: usize, const S3: usize>(a: Self) -> Self::Vector4 {
$val(u32x4_shuffle::<I0, I1, I2, I3>($reg(a), $reg(a)))
}
}
#[rustfmt::skip]
impl SwizzleConcat for $self {
#[inline(always)]
fn concat_4(a: Self::Vector2, b: Self::Vector2) -> Self {
$val(u32x4_shuffle::<0, 1, 4, 5>($reg(a), $reg(b)))
}
#[inline(always)]
fn swizzle_concat2<const I0: usize, const I1: usize, const S0: usize, const S1: usize>(a: Self, b: Self) -> Self::Vector2 {
$val(u32x4_shuffle::<I0, I1, I0, I1>($reg(a), $reg(b)))
}
#[inline(always)]
fn swizzle_concat4<const I0: usize, const I1: usize, const I2: usize, const I3: usize, const S0: usize, const S1: usize, const S2: usize, const S3: usize>(a: Self, b: Self) -> Self::Vector4 {
$val(u32x4_shuffle::<I0, I1, I2, I3>($reg(a), $reg(b)))
}
}
};
}
impl_swizzle_32bit!(f32x4, f32_reg, f32_val);
impl_swizzle_32bit!(i32x4, i32_reg, i32_val);
impl_swizzle_32bit!(u32x4, u32_reg, u32_val);
macro_rules! swizzle4 {
($a:expr, [0, 1]) => {
$crate::simd::swizzle_wasm::Swizzle::__xy($a)
};
($a:expr, [0, 1, _, _]) => {
$crate::simd::swizzle_wasm::Swizzle::__widen($a)
};
($a:expr, [0, 1, 2, _]) => {
$crate::simd::swizzle_wasm::Swizzle::__widen($a)
};
($a:expr, [0, 1, 2, 3]) => {
$crate::simd::swizzle_wasm::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_wasm::Swizzle::swizzle2::<
{ $crate::simd::utils::validate_lane4!($i0) },
{ $crate::simd::utils::validate_lane4!($i1) },
{ $crate::simd::swizzle_wasm::first($i0) },
{ $crate::simd::swizzle_wasm::second($i1) },
>($a)
};
($a:expr, [$i0:tt, $i1:tt, _, _]) => {
$crate::simd::swizzle_wasm::swizzle4!($a, [$i0, $i1, $i0, $i1])
};
($a:expr, [$i0:tt, $i1:tt, $i2:tt]) => {
$crate::simd::swizzle_wasm::swizzle4!($a, [$i0, $i1, $i2, _])
};
($a:expr, [$i0:tt, $i1:tt, $i2:tt, _]) => {
$crate::simd::utils::complete_swizzle4!([$crate::simd::swizzle_wasm::swizzle4], ($a), [
$i0,
$i1,
$i2,
_
])
};
($a:expr, [$i0:tt, $i1:tt, $i2:tt, $i3:tt]) => {
$crate::simd::swizzle_wasm::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_wasm::first($i0) },
{ $crate::simd::swizzle_wasm::second($i1) },
{ $crate::simd::swizzle_wasm::first($i2) },
{ $crate::simd::swizzle_wasm::second($i3) },
>($a)
};
($a:expr, $b:expr, @concat) => {
$crate::simd::swizzle_wasm::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_wasm::SwizzleConcat::swizzle_concat2::<
{ $crate::simd::utils::validate_lane8!($i0) },
{ $crate::simd::utils::validate_lane8!($i1) },
{ $crate::simd::swizzle_wasm::first($i0) },
{ $crate::simd::swizzle_wasm::second($i1) },
>($a, $b)
};
($a:expr, $b:expr, [$i0:tt, $i1:tt, _, _]) => {
$crate::simd::swizzle_wasm::swizzle4!($a, $b, [$i0, $i1, $i0, $i1])
};
($a:expr, $b:expr, [$i0:tt, $i1:tt, $i2:tt]) => {
$crate::simd::swizzle_wasm::swizzle4!($a, $b, [$i0, $i1, $i2, _])
};
($a:expr, $b:expr, [$i0:tt, $i1:tt, $i2:tt, _]) => {
$crate::simd::utils::complete_swizzle4!([$crate::simd::swizzle_wasm::swizzle4], ($a, $b), [
$i0,
$i1,
$i2,
_
])
};
($a:expr, $b:expr, [$i0:tt, $i1:tt, $i2:tt, $i3:tt]) => {
$crate::simd::swizzle_wasm::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_wasm::first($i0) },
{ $crate::simd::swizzle_wasm::second($i1) },
{ $crate::simd::swizzle_wasm::first($i2) },
{ $crate::simd::swizzle_wasm::second($i3) },
>($a, $b)
};
}
pub(crate) use swizzle4;
#[cfg(test)]
mod tests {
use super::*;
macro_rules! check_4lane_64bit {
($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_64bit!(f64x4_lanes, f64, f64x4::new, f64x4::to_array, f64x2::to_array);
check_4lane_64bit!(i64x4_lanes, i64, i64x4::new, i64x4::to_array, i64x2::to_array);
check_4lane_64bit!(u64x4_lanes, u64, u64x4::new, u64x4::to_array, u64x2::to_array);
macro_rules! check_2lane_64bit {
($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_64bit!(f64x2_lanes, f64, f64x2::new, f64x2::to_array, f64x4::to_array);
check_2lane_64bit!(i64x2_lanes, i64, i64x2::new, i64x2::to_array, i64x4::to_array);
check_2lane_64bit!(u64x2_lanes, u64, u64x2::new, u64x2::to_array, u64x4::to_array);
macro_rules! check_32bit {
($name:ident, $s:ty, $new:expr, $read:expr) => {
#[test]
#[rustfmt::skip]
fn $name() {
let new = $new;
let read = $read;
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!(read(swizzle4!(a, [3, 1, 2, 0])), [13 as $s, 11 as $s, 12 as $s, 10 as $s]);
assert_eq!(read(swizzle4!(a, b, [7, 0, 5, 2])), [23 as $s, 10 as $s, 21 as $s, 12 as $s]);
assert_eq!(read(swizzle4!(a, b, [0, 4, 1, 5])), [10 as $s, 20 as $s, 11 as $s, 21 as $s]);
assert_eq!(read(swizzle4!(a, [0, 2, 1, 3])), [10 as $s, 12 as $s, 11 as $s, 13 as $s]);
assert_eq!(read(swizzle4!(a, [2, 3, 0, 1])), [12 as $s, 13 as $s, 10 as $s, 11 as $s]);
assert_eq!(read(swizzle4!(a, b, [0, 1, 4, 5])), [10 as $s, 11 as $s, 20 as $s, 21 as $s]);
assert_eq!(read(swizzle4!(a, [1, 1, 1, 1])), [11 as $s, 11 as $s, 11 as $s, 11 as $s]);
assert_eq!(read(swizzle4!(a, [0, 1, 2, 3])), [10 as $s, 11 as $s, 12 as $s, 13 as $s]);
assert_eq!(read(swizzle4!(a, [0, 1, 2, _]))[..3], [10 as $s, 11 as $s, 12 as $s]);
assert_eq!(read(swizzle4!(a, [0, 1, 2]))[..3], [10 as $s, 11 as $s, 12 as $s]);
assert_eq!(read(swizzle4!(a, [0, 1, _, _]))[..2], [10 as $s, 11 as $s]);
assert_eq!(read(swizzle4!(a, [0, 1]))[..2], [10 as $s, 11 as $s]);
assert_eq!(read(swizzle4!(a, [3, 1])), [13 as $s, 11 as $s, 13 as $s, 11 as $s]);
assert_eq!(read(swizzle4!(a, b, [4, 1])), [20 as $s, 11 as $s, 20 as $s, 11 as $s]);
}
};
}
check_32bit!(f32x4_lanes, f32, f32x4::new, f32x4::to_array);
check_32bit!(i32x4_lanes, i32, i32x4::new, i32x4::to_array);
check_32bit!(u32x4_lanes, u32, u32x4::new, u32x4::to_array);
}