use crate::field::fan_paar::{FanPaar16, FanPaar32, FanPaar64, fp32, fp64};
use crate::kernel::scalar;
use crate::kernel::tables::FpTowerTables;
#[cfg(target_arch = "x86")]
use core::arch::x86::*;
#[cfg(target_arch = "x86_64")]
use core::arch::x86_64::*;
use super::gf16::{nibble_avx2, nibble_ssse3, scale_avx2, scale_ssse3};
use super::gf32::{SWAP2, load_mask};
use super::gf64::SWAP4;
#[allow(clippy::used_underscore_binding)]
#[archmage::arcane(import_intrinsics)]
pub fn mul_add_avx2(
_token: archmage::X64V3Token,
dst: &mut [u8],
tables: &FpTowerTables,
src: &[u8],
) {
assert_eq!(
dst.len(),
src.len(),
"fan_paar::mul_add_avx2: dst is {} bytes but src is {} bytes",
dst.len(),
src.len(),
);
assert!(
dst.len().is_multiple_of(2),
"fan_paar::mul_add_avx2: buffer of {} bytes is not a whole number of 2-byte elements",
dst.len(),
);
let vectors = nibble_avx2(tables);
let (lanes, dst_rest) = dst.as_chunks_mut::<32>();
let (src_lanes, src_rest) = src.as_chunks::<32>();
for (dst_lane, src_lane) in lanes.iter_mut().zip(src_lanes) {
let x = _mm256_loadu_si256(src_lane);
let d = _mm256_loadu_si256(&*dst_lane);
let scaled = scale_avx2(x, &vectors);
_mm256_storeu_si256(dst_lane, _mm256_xor_si256(d, scaled));
}
let (narrow, dst_tail) = dst_rest.as_chunks_mut::<16>();
let (src_narrow, src_tail) = src_rest.as_chunks::<16>();
if let Some(dst_lane) = narrow.first_mut() {
let table = nibble_ssse3(tables);
let x = _mm_loadu_si128(&src_narrow[0]);
let d = _mm_loadu_si128(&*dst_lane);
let scaled = scale_ssse3(x, &table);
_mm_storeu_si128(dst_lane, _mm_xor_si128(d, scaled));
}
scalar::mul_add::<FanPaar16>(dst_tail, tables.coeff, src_tail);
}
#[allow(clippy::used_underscore_binding)]
#[archmage::arcane(import_intrinsics)]
pub fn mul_assign_avx2(_token: archmage::X64V3Token, dst: &mut [u8], tables: &FpTowerTables) {
assert!(
dst.len().is_multiple_of(2),
"fan_paar::mul_assign_avx2: buffer of {} bytes is not a whole number of 2-byte elements",
dst.len(),
);
let vectors = nibble_avx2(tables);
let (lanes, dst_rest) = dst.as_chunks_mut::<32>();
for dst_lane in lanes.iter_mut() {
let x = _mm256_loadu_si256(&*dst_lane);
_mm256_storeu_si256(dst_lane, scale_avx2(x, &vectors));
}
let (narrow, dst_tail) = dst_rest.as_chunks_mut::<16>();
if let Some(dst_lane) = narrow.first_mut() {
let table = nibble_ssse3(tables);
let x = _mm_loadu_si128(&*dst_lane);
_mm_storeu_si128(dst_lane, scale_ssse3(x, &table));
}
scalar::mul_assign::<FanPaar16>(dst_tail, tables.coeff);
}
#[allow(clippy::used_underscore_binding)]
#[archmage::arcane(import_intrinsics)]
pub fn mul_into_avx2(
_token: archmage::X64V3Token,
dst: &mut [u8],
tables: &FpTowerTables,
src: &[u8],
) {
assert_eq!(
dst.len(),
src.len(),
"fan_paar::mul_into_avx2: dst is {} bytes but src is {} bytes",
dst.len(),
src.len(),
);
assert!(
dst.len().is_multiple_of(2),
"fan_paar::mul_into_avx2: buffer of {} bytes is not a whole number of 2-byte elements",
dst.len(),
);
let vectors = nibble_avx2(tables);
let (lanes, dst_rest) = dst.as_chunks_mut::<32>();
let (src_lanes, src_rest) = src.as_chunks::<32>();
for (dst_lane, src_lane) in lanes.iter_mut().zip(src_lanes) {
let x = _mm256_loadu_si256(src_lane);
_mm256_storeu_si256(dst_lane, scale_avx2(x, &vectors));
}
let (narrow, dst_tail) = dst_rest.as_chunks_mut::<16>();
let (src_narrow, src_tail) = src_rest.as_chunks::<16>();
if let Some(dst_lane) = narrow.first_mut() {
let table = nibble_ssse3(tables);
let x = _mm_loadu_si128(&src_narrow[0]);
_mm_storeu_si128(dst_lane, scale_ssse3(x, &table));
}
dst_tail.copy_from_slice(src_tail);
scalar::mul_assign::<FanPaar16>(dst_tail, tables.coeff);
}
#[allow(clippy::used_underscore_binding)]
#[archmage::arcane(import_intrinsics)]
pub fn mul_add_ssse3(
_token: archmage::X64V2Token,
dst: &mut [u8],
tables: &FpTowerTables,
src: &[u8],
) {
assert_eq!(
dst.len(),
src.len(),
"fan_paar::mul_add_ssse3: dst is {} bytes but src is {} bytes",
dst.len(),
src.len(),
);
assert!(
dst.len().is_multiple_of(2),
"fan_paar::mul_add_ssse3: buffer of {} bytes is not a whole number of 2-byte elements",
dst.len(),
);
let vectors = nibble_ssse3(tables);
let (lanes, dst_tail) = dst.as_chunks_mut::<16>();
let (src_lanes, src_tail) = src.as_chunks::<16>();
for (dst_lane, src_lane) in lanes.iter_mut().zip(src_lanes) {
let x = _mm_loadu_si128(src_lane);
let d = _mm_loadu_si128(&*dst_lane);
let scaled = scale_ssse3(x, &vectors);
_mm_storeu_si128(dst_lane, _mm_xor_si128(d, scaled));
}
scalar::mul_add::<FanPaar16>(dst_tail, tables.coeff, src_tail);
}
#[allow(clippy::used_underscore_binding)]
#[archmage::arcane(import_intrinsics)]
pub fn mul_assign_ssse3(_token: archmage::X64V2Token, dst: &mut [u8], tables: &FpTowerTables) {
assert!(
dst.len().is_multiple_of(2),
"fan_paar::mul_assign_ssse3: buffer of {} bytes is not a whole number of 2-byte elements",
dst.len(),
);
let vectors = nibble_ssse3(tables);
let (lanes, dst_tail) = dst.as_chunks_mut::<16>();
for dst_lane in lanes.iter_mut() {
let x = _mm_loadu_si128(&*dst_lane);
_mm_storeu_si128(dst_lane, scale_ssse3(x, &vectors));
}
scalar::mul_assign::<FanPaar16>(dst_tail, tables.coeff);
}
#[allow(clippy::used_underscore_binding)]
#[archmage::arcane(import_intrinsics)]
pub fn mul_into_ssse3(
_token: archmage::X64V2Token,
dst: &mut [u8],
tables: &FpTowerTables,
src: &[u8],
) {
assert_eq!(
dst.len(),
src.len(),
"fan_paar::mul_into_ssse3: dst is {} bytes but src is {} bytes",
dst.len(),
src.len(),
);
assert!(
dst.len().is_multiple_of(2),
"fan_paar::mul_into_ssse3: buffer of {} bytes is not a whole number of 2-byte elements",
dst.len(),
);
let vectors = nibble_ssse3(tables);
let (lanes, dst_tail) = dst.as_chunks_mut::<16>();
let (src_lanes, src_tail) = src.as_chunks::<16>();
for (dst_lane, src_lane) in lanes.iter_mut().zip(src_lanes) {
let x = _mm_loadu_si128(src_lane);
_mm_storeu_si128(dst_lane, scale_ssse3(x, &vectors));
}
dst_tail.copy_from_slice(src_tail);
scalar::mul_assign::<FanPaar16>(dst_tail, tables.coeff);
}
struct Fp32Lanes {
a: super::gf16::NibbleAvx2,
b: super::gf16::NibbleAvx2,
c: super::gf16::NibbleAvx2,
even: __m256i,
swap2: __m256i,
}
#[archmage::rite(v3)]
fn fp32_lanes(coeff: fp32::Elem) -> Fp32Lanes {
let (c0, c1) = coeff.to_components();
let a_coeff = c0;
let b_coeff = c0.add(c1.mul_alpha());
let c_coeff = c1;
Fp32Lanes {
a: nibble_avx2(&FpTowerTables::new(a_coeff)),
b: nibble_avx2(&FpTowerTables::new(b_coeff)),
c: nibble_avx2(&FpTowerTables::new(c_coeff)),
even: _mm256_set1_epi32(i32::from_ne_bytes(0x0000_ffffu32.to_ne_bytes())),
swap2: load_mask(&SWAP2),
}
}
#[archmage::rite(v3)]
fn scale_fp32_avx2(x: __m256i, lanes: &Fp32Lanes) -> __m256i {
let xa = scale_avx2(x, &lanes.a);
let xb = scale_avx2(x, &lanes.b);
let same = _mm256_xor_si256(
_mm256_and_si256(xa, lanes.even),
_mm256_andnot_si256(lanes.even, xb),
);
let cross = scale_avx2(_mm256_shuffle_epi8(x, lanes.swap2), &lanes.c);
_mm256_xor_si256(same, cross)
}
#[allow(clippy::used_underscore_binding)]
#[archmage::arcane(import_intrinsics)]
pub fn mul_add_fp32_avx2(
_token: archmage::X64V3Token,
dst: &mut [u8],
coeff: fp32::Elem,
src: &[u8],
) {
assert_eq!(
dst.len(),
src.len(),
"fan_paar::mul_add_fp32_avx2: dst is {} bytes but src is {} bytes",
dst.len(),
src.len(),
);
assert!(
dst.len().is_multiple_of(4),
"fan_paar::mul_add_fp32_avx2: buffer of {} bytes is not a whole number of 4-byte elements",
dst.len(),
);
let l = fp32_lanes(coeff);
let (lanes, dst_tail) = dst.as_chunks_mut::<32>();
let (src_lanes, src_tail) = src.as_chunks::<32>();
for (dst_lane, src_lane) in lanes.iter_mut().zip(src_lanes) {
let x = _mm256_loadu_si256(src_lane);
let d = _mm256_loadu_si256(&*dst_lane);
let r = scale_fp32_avx2(x, &l);
_mm256_storeu_si256(dst_lane, _mm256_xor_si256(d, r));
}
scalar::mul_add::<FanPaar32>(dst_tail, coeff, src_tail);
}
#[allow(clippy::used_underscore_binding)]
#[archmage::arcane(import_intrinsics)]
pub fn mul_assign_fp32_avx2(_token: archmage::X64V3Token, dst: &mut [u8], coeff: fp32::Elem) {
assert!(
dst.len().is_multiple_of(4),
"fan_paar::mul_assign_fp32_avx2: buffer of {} bytes is not a whole number of 4-byte elements",
dst.len(),
);
let l = fp32_lanes(coeff);
let (lanes, dst_tail) = dst.as_chunks_mut::<32>();
for dst_lane in lanes.iter_mut() {
let x = _mm256_loadu_si256(&*dst_lane);
_mm256_storeu_si256(dst_lane, scale_fp32_avx2(x, &l));
}
scalar::mul_assign::<FanPaar32>(dst_tail, coeff);
}
#[allow(clippy::used_underscore_binding)]
#[archmage::arcane(import_intrinsics)]
pub fn mul_into_fp32_avx2(
_token: archmage::X64V3Token,
dst: &mut [u8],
coeff: fp32::Elem,
src: &[u8],
) {
assert_eq!(
dst.len(),
src.len(),
"fan_paar::mul_into_fp32_avx2: dst is {} bytes but src is {} bytes",
dst.len(),
src.len(),
);
assert!(
dst.len().is_multiple_of(4),
"fan_paar::mul_into_fp32_avx2: buffer of {} bytes is not a whole number of 4-byte elements",
dst.len(),
);
let l = fp32_lanes(coeff);
let (lanes, dst_tail) = dst.as_chunks_mut::<32>();
let (src_lanes, src_tail) = src.as_chunks::<32>();
for (dst_lane, src_lane) in lanes.iter_mut().zip(src_lanes) {
let x = _mm256_loadu_si256(src_lane);
_mm256_storeu_si256(dst_lane, scale_fp32_avx2(x, &l));
}
dst_tail.copy_from_slice(src_tail);
scalar::mul_assign::<FanPaar32>(dst_tail, coeff);
}
struct Fp64Lanes {
a: Fp32Lanes,
b: Fp32Lanes,
c: Fp32Lanes,
even: __m256i,
swap4: __m256i,
}
#[archmage::rite(v3)]
fn fp64_lanes(coeff: fp64::Elem) -> Fp64Lanes {
let (c0, c1) = coeff.to_components();
let a_coeff = c0;
let b_coeff = c0.add(c1.mul_alpha());
let c_coeff = c1;
Fp64Lanes {
a: fp32_lanes(a_coeff),
b: fp32_lanes(b_coeff),
c: fp32_lanes(c_coeff),
even: _mm256_set1_epi64x(i64::from_ne_bytes(0x0000_0000_ffff_ffffu64.to_ne_bytes())),
swap4: load_mask(&SWAP4),
}
}
#[archmage::rite(v3)]
fn scale_fp64_avx2(x: __m256i, lanes: &Fp64Lanes) -> __m256i {
let xa = scale_fp32_avx2(x, &lanes.a);
let xb = scale_fp32_avx2(x, &lanes.b);
let same = _mm256_xor_si256(
_mm256_and_si256(xa, lanes.even),
_mm256_andnot_si256(lanes.even, xb),
);
let cross = scale_fp32_avx2(_mm256_shuffle_epi8(x, lanes.swap4), &lanes.c);
_mm256_xor_si256(same, cross)
}
#[allow(clippy::used_underscore_binding)]
#[archmage::arcane(import_intrinsics)]
pub fn mul_add_fp64_avx2(
_token: archmage::X64V3Token,
dst: &mut [u8],
coeff: fp64::Elem,
src: &[u8],
) {
assert_eq!(
dst.len(),
src.len(),
"fan_paar::mul_add_fp64_avx2: dst is {} bytes but src is {} bytes",
dst.len(),
src.len(),
);
assert!(
dst.len().is_multiple_of(8),
"fan_paar::mul_add_fp64_avx2: buffer of {} bytes is not a whole number of 8-byte elements",
dst.len(),
);
let l = fp64_lanes(coeff);
let (lanes, dst_tail) = dst.as_chunks_mut::<32>();
let (src_lanes, src_tail) = src.as_chunks::<32>();
for (dst_lane, src_lane) in lanes.iter_mut().zip(src_lanes) {
let x = _mm256_loadu_si256(src_lane);
let d = _mm256_loadu_si256(&*dst_lane);
let r = scale_fp64_avx2(x, &l);
_mm256_storeu_si256(dst_lane, _mm256_xor_si256(d, r));
}
scalar::mul_add::<FanPaar64>(dst_tail, coeff, src_tail);
}
#[allow(clippy::used_underscore_binding)]
#[archmage::arcane(import_intrinsics)]
pub fn mul_assign_fp64_avx2(_token: archmage::X64V3Token, dst: &mut [u8], coeff: fp64::Elem) {
assert!(
dst.len().is_multiple_of(8),
"fan_paar::mul_assign_fp64_avx2: buffer of {} bytes is not a whole number of 8-byte elements",
dst.len(),
);
let l = fp64_lanes(coeff);
let (lanes, dst_tail) = dst.as_chunks_mut::<32>();
for dst_lane in lanes.iter_mut() {
let x = _mm256_loadu_si256(&*dst_lane);
_mm256_storeu_si256(dst_lane, scale_fp64_avx2(x, &l));
}
scalar::mul_assign::<FanPaar64>(dst_tail, coeff);
}
#[allow(clippy::used_underscore_binding)]
#[archmage::arcane(import_intrinsics)]
pub fn mul_into_fp64_avx2(
_token: archmage::X64V3Token,
dst: &mut [u8],
coeff: fp64::Elem,
src: &[u8],
) {
assert_eq!(
dst.len(),
src.len(),
"fan_paar::mul_into_fp64_avx2: dst is {} bytes but src is {} bytes",
dst.len(),
src.len(),
);
assert!(
dst.len().is_multiple_of(8),
"fan_paar::mul_into_fp64_avx2: buffer of {} bytes is not a whole number of 8-byte elements",
dst.len(),
);
let l = fp64_lanes(coeff);
let (lanes, dst_tail) = dst.as_chunks_mut::<32>();
let (src_lanes, src_tail) = src.as_chunks::<32>();
for (dst_lane, src_lane) in lanes.iter_mut().zip(src_lanes) {
let x = _mm256_loadu_si256(src_lane);
_mm256_storeu_si256(dst_lane, scale_fp64_avx2(x, &l));
}
dst_tail.copy_from_slice(src_tail);
scalar::mul_assign::<FanPaar64>(dst_tail, coeff);
}