#![cfg(all(target_arch = "x86_64", target_feature = "avx2"))]
use core::arch::x86_64::{
__m256, __m256d, _mm256_add_pd, _mm256_add_ps, _mm256_loadu_pd, _mm256_loadu_ps, _mm256_mul_pd,
_mm256_mul_ps, _mm256_set1_pd, _mm256_set1_ps, _mm256_storeu_pd, _mm256_storeu_ps,
_mm256_sub_pd, _mm256_sub_ps,
};
use super::{SimdF32x8, SimdF64x4};
#[derive(Clone, Copy)]
pub(crate) struct F64x4Avx2(__m256d);
#[allow(dead_code)] impl SimdF64x4 for F64x4Avx2 {
#[inline]
fn splat(x: f64) -> Self {
unsafe { F64x4Avx2(_mm256_set1_pd(x)) }
}
#[inline]
fn load_unaligned(src: &[f64; 4]) -> Self {
unsafe { F64x4Avx2(_mm256_loadu_pd(src.as_ptr())) }
}
#[inline]
fn store_unaligned(self, dst: &mut [f64; 4]) {
unsafe { _mm256_storeu_pd(dst.as_mut_ptr(), self.0) }
}
#[inline]
fn add(self, rhs: Self) -> Self {
unsafe { F64x4Avx2(_mm256_add_pd(self.0, rhs.0)) }
}
#[inline]
fn sub(self, rhs: Self) -> Self {
unsafe { F64x4Avx2(_mm256_sub_pd(self.0, rhs.0)) }
}
#[inline]
fn mul(self, rhs: Self) -> Self {
unsafe { F64x4Avx2(_mm256_mul_pd(self.0, rhs.0)) }
}
#[inline]
fn horizontal_sum(self) -> f64 {
let mut tmp = [0.0_f64; 4];
unsafe { _mm256_storeu_pd(tmp.as_mut_ptr(), self.0) };
((tmp[0] + tmp[1]) + tmp[2]) + tmp[3]
}
}
#[derive(Clone, Copy)]
pub(crate) struct F32x8Avx2(__m256);
#[allow(dead_code)]
impl SimdF32x8 for F32x8Avx2 {
#[inline]
fn splat(x: f32) -> Self {
unsafe { F32x8Avx2(_mm256_set1_ps(x)) }
}
#[inline]
fn load_unaligned(src: &[f32; 8]) -> Self {
unsafe { F32x8Avx2(_mm256_loadu_ps(src.as_ptr())) }
}
#[inline]
fn store_unaligned(self, dst: &mut [f32; 8]) {
unsafe { _mm256_storeu_ps(dst.as_mut_ptr(), self.0) }
}
#[inline]
fn add(self, rhs: Self) -> Self {
unsafe { F32x8Avx2(_mm256_add_ps(self.0, rhs.0)) }
}
#[inline]
fn sub(self, rhs: Self) -> Self {
unsafe { F32x8Avx2(_mm256_sub_ps(self.0, rhs.0)) }
}
#[inline]
fn mul(self, rhs: Self) -> Self {
unsafe { F32x8Avx2(_mm256_mul_ps(self.0, rhs.0)) }
}
#[inline]
fn horizontal_sum(self) -> f32 {
let mut tmp = [0.0_f32; 8];
unsafe { _mm256_storeu_ps(tmp.as_mut_ptr(), self.0) };
let lo = (tmp[0] + tmp[1]) + (tmp[2] + tmp[3]);
let hi = (tmp[4] + tmp[5]) + (tmp[6] + tmp[7]);
lo + hi
}
}
#[allow(clippy::too_many_arguments, clippy::similar_names)]
pub(crate) fn apply_g4_stencil_avx2_4nodes(
base: usize,
prev: &[f64],
ar3h: &[f64],
ar1h: &[f64],
al1h: &[f64],
al3h: &[f64],
dx_sq_inv: f64,
out: &mut [f64],
) {
unsafe {
let lm2 = _mm256_loadu_pd(prev.as_ptr().add(base - 2));
let lm1 = _mm256_loadu_pd(prev.as_ptr().add(base - 1));
let ctr = _mm256_loadu_pd(prev.as_ptr().add(base));
let rp1 = _mm256_loadu_pd(prev.as_ptr().add(base + 1));
let rp2 = _mm256_loadu_pd(prev.as_ptr().add(base + 2));
let v_ar3h = _mm256_loadu_pd(ar3h.as_ptr().add(base));
let v_ar1h = _mm256_loadu_pd(ar1h.as_ptr().add(base));
let v_al1h = _mm256_loadu_pd(al1h.as_ptr().add(base));
let v_al3h = _mm256_loadu_pd(al3h.as_ptr().add(base));
let result = avx2_g4_fluxes(
lm2, lm1, ctr, rp1, rp2, v_ar3h, v_ar1h, v_al1h, v_al3h, dx_sq_inv,
);
_mm256_storeu_pd(out.as_mut_ptr().add(base), result);
}
}
#[allow(clippy::too_many_arguments, clippy::similar_names)]
#[inline]
unsafe fn avx2_g4_fluxes(
lm2: __m256d,
lm1: __m256d,
ctr: __m256d,
rp1: __m256d,
rp2: __m256d,
v_ar3h: __m256d,
v_ar1h: __m256d,
v_al1h: __m256d,
v_al3h: __m256d,
dx_sq_inv: f64,
) -> __m256d {
let five = _mm256_set1_pd(5.0);
let neg_one = _mm256_set1_pd(-1.0);
let neg_five = _mm256_set1_pd(-5.0);
let one_over_4 = _mm256_set1_pd(0.25);
let one_over_12 = _mm256_set1_pd(1.0 / 12.0);
let dx_sq_inv_v = _mm256_set1_pd(dx_sq_inv);
let flux_right = _mm256_mul_pd(
_mm256_mul_pd(_mm256_mul_pd(five, v_ar1h), _mm256_sub_pd(rp1, ctr)),
one_over_4,
);
let flux_right_far = _mm256_mul_pd(
_mm256_mul_pd(_mm256_mul_pd(neg_one, v_ar3h), _mm256_sub_pd(rp2, rp1)),
one_over_12,
);
let flux_left = _mm256_mul_pd(
_mm256_mul_pd(_mm256_mul_pd(neg_five, v_al1h), _mm256_sub_pd(ctr, lm1)),
one_over_4,
);
let flux_left_far = _mm256_mul_pd(_mm256_mul_pd(v_al3h, _mm256_sub_pd(lm1, lm2)), one_over_12);
let sum = _mm256_add_pd(
_mm256_add_pd(_mm256_add_pd(flux_right_far, flux_right), flux_left),
flux_left_far,
);
_mm256_mul_pd(sum, dx_sq_inv_v)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn avx2_golden_vector() {
let data = [1.0_f64, 2.0, 3.0, 4.0];
let v = F64x4Avx2::load_unaligned(&data);
let two = F64x4Avx2::splat(2.0);
let result = two.mul(v).horizontal_sum();
#[allow(clippy::float_cmp)]
let exact_20 = 20.0_f64;
assert!(
result.to_bits() == exact_20.to_bits(),
"AVX2 golden vector failed: got {result}"
);
}
#[test]
fn avx2_f32_golden_vector() {
let data = [1.0_f32, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0];
let v = F32x8Avx2::load_unaligned(&data);
let two = F32x8Avx2::splat(2.0_f32);
let result = two.mul(v).horizontal_sum();
#[allow(clippy::float_cmp)]
let exact_72 = 72.0_f32;
assert!(
result.to_bits() == exact_72.to_bits(),
"AVX2 f32 golden vector failed: got {result}"
);
}
}