#[cfg(target_arch = "x86_64")]
mod x86_64;
use core::ops::*;
use num_traits::Float;
use num_traits::Num;
pub trait Simd<T: SimdElement>: Deref<Target = [T]> {}
pub unsafe trait SimdElement: Sized {
type Vector: Sized + Copy;
const VECTOR_LEN: usize;
fn is_available() -> bool;
unsafe fn load(src: *const Self) -> Self::Vector;
unsafe fn load_partial(src: *const Self, len: usize) -> Self::Vector;
unsafe fn store(dst: *mut Self, src: Self::Vector);
unsafe fn store_partial(dst: *mut Self, src: Self::Vector, len: usize);
unsafe fn set(value: Self) -> Self::Vector;
}
pub unsafe trait SimdNumElement: SimdElement + Num {
unsafe fn add(left: Self::Vector, right: Self::Vector) -> Self::Vector;
unsafe fn sub(left: Self::Vector, right: Self::Vector) -> Self::Vector;
unsafe fn mul(left: Self::Vector, right: Self::Vector) -> Self::Vector;
unsafe fn div(left: Self::Vector, right: Self::Vector) -> Self::Vector;
#[inline(always)]
unsafe fn fma(a: Self::Vector, b: Self::Vector, c: Self::Vector) -> Self::Vector {
unsafe { <Self as SimdNumElement>::add(<Self as SimdNumElement>::mul(a, b), c) }
}
#[inline(always)]
unsafe fn powi(x: Self::Vector, n: i32) -> Self::Vector {
let mut result = unsafe { Self::set(Self::one()) };
if n < 0 {
for _ in 0..-n {
result = unsafe { <Self as SimdNumElement>::div(result, x) };
}
}
if 0 < n {
for _ in 0..n {
result = unsafe { <Self as SimdNumElement>::mul(result, x) };
}
}
result
}
}
pub unsafe trait SimdFloatingElement: SimdNumElement + Float {
unsafe fn sqrt(x: Self::Vector) -> Self::Vector;
unsafe fn exp(x: Self::Vector) -> Self::Vector;
unsafe fn tanh(x: Self::Vector) -> Self::Vector;
}
pub unsafe trait SimdIntegerElement: SimdNumElement + Num {
unsafe fn and(left: Self::Vector, right: Self::Vector) -> Self::Vector;
unsafe fn or(left: Self::Vector, right: Self::Vector) -> Self::Vector;
unsafe fn not(left: Self::Vector) -> Self::Vector;
unsafe fn xor(left: Self::Vector, right: Self::Vector) -> Self::Vector;
}
#[repr(transparent)]
#[derive(Debug, PartialEq, Clone, Copy)]
pub struct SimdRef<'a, T: SimdElement>(&'a [T]);
impl<'a, T: SimdElement> SimdRef<'a, T> {
pub fn new(slice: &'a [T]) -> Self {
Self(slice)
}
}
impl<'a, T: SimdElement> Deref for SimdRef<'a, T> {
type Target = [T];
fn deref(&self) -> &[T] {
&self.0
}
}
impl<'a, T: SimdElement> Simd<T> for SimdRef<'a, T> {}
#[derive(Debug, PartialEq)]
#[repr(transparent)]
pub struct SimdMut<'a, T: SimdElement>(&'a mut [T]);
impl<'a, T: SimdElement> SimdMut<'a, T> {
pub fn new(slice: &'a mut [T]) -> Self {
Self(slice)
}
}
impl<'a, T: SimdNumElement> SimdMut<'a, T> {
pub fn powi(&mut self, n: i32) {
if !T::is_available() {
panic!("simd is not available");
}
let len = self.len();
let x = self.as_mut_ptr();
unsafe {
for i in 0..len / T::VECTOR_LEN {
let offset = i * T::VECTOR_LEN;
let x_vector = T::load(x.add(offset));
let result_vector = T::powi(x_vector, n);
T::store(x.add(offset), result_vector);
}
let remaining = len % T::VECTOR_LEN;
if remaining != 0 {
let offset = len - remaining;
let x_vector = T::load_partial(x.add(offset), remaining);
let result_vector = T::powi(x_vector, n);
T::store(x.add(offset), result_vector);
}
}
}
}
impl<'a, T: SimdFloatingElement> SimdMut<'a, T> {
pub fn sqrt(&mut self) {
if !T::is_available() {
panic!("simd is not available");
}
let len = self.len();
let x = self.as_mut_ptr();
unsafe {
for i in 0..len / T::VECTOR_LEN {
let offset = i * T::VECTOR_LEN;
let x_vector = T::load(x.add(offset));
let result_vector = <T as SimdFloatingElement>::sqrt(x_vector);
T::store(x.add(offset), result_vector);
}
let remaining = len % T::VECTOR_LEN;
if remaining != 0 {
let offset = len - remaining;
let x_vector = T::load_partial(x.add(offset), remaining);
let result_vector = <T as SimdFloatingElement>::sqrt(x_vector);
T::store(x.add(offset), result_vector);
}
}
}
pub fn exp(&mut self) {
if !T::is_available() {
panic!("simd is not available");
}
let len = self.len();
let x = self.as_mut_ptr();
unsafe {
for i in 0..len / T::VECTOR_LEN {
let offset = i * T::VECTOR_LEN;
let x_vector = T::load(x.add(offset));
let result_vector = <T as SimdFloatingElement>::exp(x_vector);
T::store(x.add(offset), result_vector);
}
let remaining = len % T::VECTOR_LEN;
if remaining != 0 {
let offset = len - remaining;
let x_vector = T::load_partial(x.add(offset), remaining);
let result_vector = <T as SimdFloatingElement>::exp(x_vector);
T::store(x.add(offset), result_vector);
}
}
}
pub fn tanh(&mut self) {
if !T::is_available() {
panic!("simd is not available");
}
let len = self.len();
let x = self.as_mut_ptr();
unsafe {
for i in 0..len / T::VECTOR_LEN {
let offset = i * T::VECTOR_LEN;
let x_vector = T::load(x.add(offset));
let result_vector = <T as SimdFloatingElement>::tanh(x_vector);
T::store(x.add(offset), result_vector);
}
let remaining = len % T::VECTOR_LEN;
if remaining != 0 {
let offset = len - remaining;
let x_vector = T::load_partial(x.add(offset), remaining);
let result_vector = <T as SimdFloatingElement>::tanh(x_vector);
T::store(x.add(offset), result_vector);
}
}
}
}
impl<'a, T: SimdElement + SimdNumElement, R: Simd<T>> AddAssign<&R> for SimdMut<'a, T> {
fn add_assign(&mut self, rhs: &R) {
if !T::is_available() {
panic!("simd is not available");
}
let len = self.len().min(rhs.len());
let left = self.as_mut_ptr();
let right = rhs.as_ptr();
unsafe {
for i in 0..len / T::VECTOR_LEN {
let offset = i * T::VECTOR_LEN;
let left_vector = T::load(left.add(offset));
let right_vector = T::load(right.add(offset));
let result_vector = <T as SimdNumElement>::add(left_vector, right_vector);
T::store(left.add(offset), result_vector);
}
let remaining = len % T::VECTOR_LEN;
if remaining != 0 {
let offset = len - remaining;
let left_vector = T::load_partial(left.add(offset), remaining);
let right_vector = T::load_partial(right.add(offset), remaining);
let result_vector = <T as SimdNumElement>::add(left_vector, right_vector);
T::store_partial(left.add(offset), result_vector, remaining);
}
}
}
}
impl<'a, T: SimdElement + SimdNumElement, R: Simd<T>> SubAssign<&R> for SimdMut<'a, T> {
fn sub_assign(&mut self, rhs: &R) {
let len = self.len().min(rhs.len());
let left = self.as_mut_ptr();
let right = rhs.as_ptr();
if !T::is_available() {
panic!("simd is not available");
}
unsafe {
for i in 0..len / T::VECTOR_LEN {
let offset = i * T::VECTOR_LEN;
let left_vector = T::load(left.add(offset));
let right_vector = T::load(right.add(offset));
let result_vector = <T as SimdNumElement>::sub(left_vector, right_vector);
T::store(left.add(offset), result_vector);
}
let remaining = len % T::VECTOR_LEN;
if remaining != 0 {
let offset = len - remaining;
let left_vector = T::load_partial(left.add(offset), remaining);
let right_vector = T::load_partial(right.add(offset), remaining);
let result_vector = <T as SimdNumElement>::sub(left_vector, right_vector);
T::store_partial(left.add(offset), result_vector, remaining);
}
}
}
}
impl<'a, T: SimdElement + SimdNumElement, R: Simd<T>> MulAssign<&R> for SimdMut<'a, T> {
fn mul_assign(&mut self, rhs: &R) {
let len = self.len().min(rhs.len());
let left = self.as_mut_ptr();
let right = rhs.as_ptr();
if !T::is_available() {
panic!("simd is not available");
}
unsafe {
for i in 0..len / T::VECTOR_LEN {
let offset = i * T::VECTOR_LEN;
let left_vector = T::load(left.add(offset));
let right_vector = T::load(right.add(offset));
let result_vector = <T as SimdNumElement>::mul(left_vector, right_vector);
T::store(left.add(offset), result_vector);
}
let remaining = len % T::VECTOR_LEN;
if remaining != 0 {
let offset = len - remaining;
let left_vector = T::load_partial(left.add(offset), remaining);
let right_vector = T::load_partial(right.add(offset), remaining);
let result_vector = <T as SimdNumElement>::mul(left_vector, right_vector);
T::store_partial(left.add(offset), result_vector, remaining);
}
}
}
}
impl<'a, T: SimdElement + SimdNumElement, R: Simd<T>> DivAssign<&R> for SimdMut<'a, T> {
fn div_assign(&mut self, rhs: &R) {
let len = self.len().min(rhs.len());
let left = self.as_mut_ptr();
let right = rhs.as_ptr();
if !T::is_available() {
panic!("simd is not available");
}
unsafe {
for i in 0..len / T::VECTOR_LEN {
let offset = i * T::VECTOR_LEN;
let left_vector = T::load(left.add(offset));
let right_vector = T::load(right.add(offset));
let result_vector = <T as SimdNumElement>::div(left_vector, right_vector);
T::store(left.add(offset), result_vector);
}
let remaining = len % T::VECTOR_LEN;
if remaining != 0 {
let offset = len - remaining;
let left_vector = T::load_partial(left.add(offset), remaining);
let right_vector = T::load_partial(right.add(offset), remaining);
let result_vector = <T as SimdNumElement>::div(left_vector, right_vector);
T::store_partial(left.add(offset), result_vector, remaining);
}
}
}
}
impl<'a, T: SimdElement> Deref for SimdMut<'a, T> {
type Target = [T];
fn deref(&self) -> &[T] {
&self.0
}
}
impl<'a, T: SimdElement> DerefMut for SimdMut<'a, T> {
fn deref_mut(&mut self) -> &mut [T] {
&mut self.0
}
}
impl<'a, T: SimdElement> Simd<T> for SimdMut<'a, T> {}