use crate::field::Elem;
use crate::field::binary::Binary;
use crate::field::binary::tower::{FanPaar16, FanPaar32, FanPaar64, Tower};
use crate::kernel::scalar;
use crate::kernel::tables::FpTowerTables;
use crate::kernel::x86::gf16::{nibble_avx2, nibble_ssse3, scale_avx2, scale_ssse3};
use crate::kernel::x86::gf32::{SWAP2, load_mask};
use crate::kernel::x86::gf64::SWAP4;
#[cfg(target_arch = "x86")]
use core::arch::x86::*;
#[cfg(target_arch = "x86_64")]
use core::arch::x86_64::*;
#[allow(clippy::used_underscore_binding)]
#[archmage::arcane(import_intrinsics)]
pub fn mul_add_avx2_fp16(
_token: archmage::X64V3Token,
dst: &mut [u8],
tables: &FpTowerTables,
src: &[u8],
) {
assert_eq!(
dst.len(),
src.len(),
"fan_paar::mul_add_avx2_fp16: dst is {} bytes but src is {} bytes",
dst.len(),
src.len(),
);
assert!(
dst.len().is_multiple_of(2),
"fan_paar::mul_add_avx2_fp16: 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::<Binary<16, Tower<FanPaar16>>>(dst_tail, tables.coeff, src_tail);
}
#[allow(clippy::used_underscore_binding)]
#[archmage::arcane(import_intrinsics)]
pub fn mul_assign_avx2_fp16(_token: archmage::X64V3Token, dst: &mut [u8], tables: &FpTowerTables) {
assert!(
dst.len().is_multiple_of(2),
"fan_paar::mul_assign_avx2_fp16: 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::<Binary<16, Tower<FanPaar16>>>(dst_tail, tables.coeff);
}
#[allow(clippy::used_underscore_binding)]
#[archmage::arcane(import_intrinsics)]
pub fn mul_into_avx2_fp16(
_token: archmage::X64V3Token,
dst: &mut [u8],
tables: &FpTowerTables,
src: &[u8],
) {
assert_eq!(
dst.len(),
src.len(),
"fan_paar::mul_into_avx2_fp16: dst is {} bytes but src is {} bytes",
dst.len(),
src.len(),
);
assert!(
dst.len().is_multiple_of(2),
"fan_paar::mul_into_avx2_fp16: 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::<Binary<16, Tower<FanPaar16>>>(dst_tail, tables.coeff);
}
struct Fp32Lanes {
a: crate::kernel::x86::gf16::NibbleAvx2,
b: crate::kernel::x86::gf16::NibbleAvx2,
c: crate::kernel::x86::gf16::NibbleAvx2,
even: __m256i,
swap2: __m256i,
}
#[archmage::rite(v3)]
fn fp32_lanes(coeff: Elem<Binary<32, Tower<FanPaar32>>>) -> 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(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_avx2_fp32(
_token: archmage::X64V3Token,
dst: &mut [u8],
coeff: Elem<Binary<32, Tower<FanPaar32>>>,
src: &[u8],
) {
assert_eq!(
dst.len(),
src.len(),
"fan_paar::mul_add_avx2_fp32: dst is {} bytes but src is {} bytes",
dst.len(),
src.len(),
);
assert!(
dst.len().is_multiple_of(4),
"fan_paar::mul_add_avx2_fp32: 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(x, &l);
_mm256_storeu_si256(dst_lane, _mm256_xor_si256(d, r));
}
scalar::mul_add::<Binary<32, Tower<FanPaar32>>>(dst_tail, coeff, src_tail);
}
#[allow(clippy::used_underscore_binding)]
#[archmage::arcane(import_intrinsics)]
pub fn mul_assign_avx2_fp32(
_token: archmage::X64V3Token,
dst: &mut [u8],
coeff: Elem<Binary<32, Tower<FanPaar32>>>,
) {
assert!(
dst.len().is_multiple_of(4),
"fan_paar::mul_assign_avx2_fp32: 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(x, &l));
}
scalar::mul_assign::<Binary<32, Tower<FanPaar32>>>(dst_tail, coeff);
}
#[allow(clippy::used_underscore_binding)]
#[archmage::arcane(import_intrinsics)]
pub fn mul_into_avx2_fp32(
_token: archmage::X64V3Token,
dst: &mut [u8],
coeff: Elem<Binary<32, Tower<FanPaar32>>>,
src: &[u8],
) {
assert_eq!(
dst.len(),
src.len(),
"fan_paar::mul_into_avx2_fp32: dst is {} bytes but src is {} bytes",
dst.len(),
src.len(),
);
assert!(
dst.len().is_multiple_of(4),
"fan_paar::mul_into_avx2_fp32: 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(x, &l));
}
dst_tail.copy_from_slice(src_tail);
scalar::mul_assign::<Binary<32, Tower<FanPaar32>>>(dst_tail, coeff);
}
struct Fp64Lanes {
a: Fp32Lanes,
b: Fp32Lanes,
c: Fp32Lanes,
even: __m256i,
swap4: __m256i,
}
#[archmage::rite(v3)]
fn fp64_lanes(coeff: Elem<Binary<64, Tower<FanPaar64>>>) -> 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(x: __m256i, lanes: &Fp64Lanes) -> __m256i {
let xa = scale_fp32(x, &lanes.a);
let xb = scale_fp32(x, &lanes.b);
let same = _mm256_xor_si256(
_mm256_and_si256(xa, lanes.even),
_mm256_andnot_si256(lanes.even, xb),
);
let cross = scale_fp32(_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_avx2_fp64(
_token: archmage::X64V3Token,
dst: &mut [u8],
coeff: Elem<Binary<64, Tower<FanPaar64>>>,
src: &[u8],
) {
assert_eq!(
dst.len(),
src.len(),
"fan_paar::mul_add_avx2_fp64: dst is {} bytes but src is {} bytes",
dst.len(),
src.len(),
);
assert!(
dst.len().is_multiple_of(8),
"fan_paar::mul_add_avx2_fp64: 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(x, &l);
_mm256_storeu_si256(dst_lane, _mm256_xor_si256(d, r));
}
scalar::mul_add::<Binary<64, Tower<FanPaar64>>>(dst_tail, coeff, src_tail);
}
#[allow(clippy::used_underscore_binding)]
#[archmage::arcane(import_intrinsics)]
pub fn mul_assign_avx2_fp64(
_token: archmage::X64V3Token,
dst: &mut [u8],
coeff: Elem<Binary<64, Tower<FanPaar64>>>,
) {
assert!(
dst.len().is_multiple_of(8),
"fan_paar::mul_assign_avx2_fp64: 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(x, &l));
}
scalar::mul_assign::<Binary<64, Tower<FanPaar64>>>(dst_tail, coeff);
}
#[allow(clippy::used_underscore_binding)]
#[archmage::arcane(import_intrinsics)]
pub fn mul_into_avx2_fp64(
_token: archmage::X64V3Token,
dst: &mut [u8],
coeff: Elem<Binary<64, Tower<FanPaar64>>>,
src: &[u8],
) {
assert_eq!(
dst.len(),
src.len(),
"fan_paar::mul_into_avx2_fp64: dst is {} bytes but src is {} bytes",
dst.len(),
src.len(),
);
assert!(
dst.len().is_multiple_of(8),
"fan_paar::mul_into_avx2_fp64: 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(x, &l));
}
dst_tail.copy_from_slice(src_tail);
scalar::mul_assign::<Binary<64, Tower<FanPaar64>>>(dst_tail, coeff);
}