#[cfg(target_arch = "x86_64")]
#[inline]
pub(super) fn sse2_ok() -> bool {
std::is_x86_feature_detected!("sse2")
}
#[cfg(target_arch = "x86_64")]
#[inline]
pub(super) fn avx_ok() -> bool {
std::is_x86_feature_detected!("avx")
}
#[cfg(target_arch = "aarch64")]
#[target_feature(enable = "neon")]
#[allow(clippy::too_many_arguments)]
pub(super) unsafe fn butterfly4(
re: &mut [f32],
im: &mut [f32],
i: usize,
k: usize,
half: usize,
off: usize,
tw_re: &[f32],
tw_im: &[f32],
) {
use std::arch::aarch64::*;
let j = i + k + half;
unsafe {
let wr = vld1q_f32(tw_re.as_ptr().add(off + k));
let wi = vld1q_f32(tw_im.as_ptr().add(off + k));
let rj = vld1q_f32(re.as_ptr().add(j));
let ij = vld1q_f32(im.as_ptr().add(j));
let tr = vsubq_f32(vmulq_f32(wr, rj), vmulq_f32(wi, ij));
let ti = vaddq_f32(vmulq_f32(wr, ij), vmulq_f32(wi, rj));
let ur = vld1q_f32(re.as_ptr().add(i + k));
let ui = vld1q_f32(im.as_ptr().add(i + k));
vst1q_f32(re.as_mut_ptr().add(j), vsubq_f32(ur, tr));
vst1q_f32(im.as_mut_ptr().add(j), vsubq_f32(ui, ti));
vst1q_f32(re.as_mut_ptr().add(i + k), vaddq_f32(ur, tr));
vst1q_f32(im.as_mut_ptr().add(i + k), vaddq_f32(ui, ti));
}
}
#[cfg(target_arch = "x86_64")]
#[target_feature(enable = "sse2")]
#[allow(clippy::too_many_arguments)]
pub(super) unsafe fn butterfly4(
re: &mut [f32],
im: &mut [f32],
i: usize,
k: usize,
half: usize,
off: usize,
tw_re: &[f32],
tw_im: &[f32],
) {
use std::arch::x86_64::*;
let j = i + k + half;
unsafe {
let wr = _mm_loadu_ps(tw_re.as_ptr().add(off + k));
let wi = _mm_loadu_ps(tw_im.as_ptr().add(off + k));
let rj = _mm_loadu_ps(re.as_ptr().add(j));
let ij = _mm_loadu_ps(im.as_ptr().add(j));
let tr = _mm_sub_ps(_mm_mul_ps(wr, rj), _mm_mul_ps(wi, ij));
let ti = _mm_add_ps(_mm_mul_ps(wr, ij), _mm_mul_ps(wi, rj));
let ur = _mm_loadu_ps(re.as_ptr().add(i + k));
let ui = _mm_loadu_ps(im.as_ptr().add(i + k));
_mm_storeu_ps(re.as_mut_ptr().add(j), _mm_sub_ps(ur, tr));
_mm_storeu_ps(im.as_mut_ptr().add(j), _mm_sub_ps(ui, ti));
_mm_storeu_ps(re.as_mut_ptr().add(i + k), _mm_add_ps(ur, tr));
_mm_storeu_ps(im.as_mut_ptr().add(i + k), _mm_add_ps(ui, ti));
}
}
#[cfg(target_arch = "x86_64")]
#[target_feature(enable = "avx")]
#[allow(clippy::too_many_arguments)]
pub(super) unsafe fn butterfly8(
re: &mut [f32],
im: &mut [f32],
i: usize,
k: usize,
half: usize,
off: usize,
tw_re: &[f32],
tw_im: &[f32],
) {
use std::arch::x86_64::*;
let j = i + k + half;
unsafe {
let wr = _mm256_loadu_ps(tw_re.as_ptr().add(off + k));
let wi = _mm256_loadu_ps(tw_im.as_ptr().add(off + k));
let rj = _mm256_loadu_ps(re.as_ptr().add(j));
let ij = _mm256_loadu_ps(im.as_ptr().add(j));
let tr = _mm256_sub_ps(_mm256_mul_ps(wr, rj), _mm256_mul_ps(wi, ij));
let ti = _mm256_add_ps(_mm256_mul_ps(wr, ij), _mm256_mul_ps(wi, rj));
let ur = _mm256_loadu_ps(re.as_ptr().add(i + k));
let ui = _mm256_loadu_ps(im.as_ptr().add(i + k));
_mm256_storeu_ps(re.as_mut_ptr().add(j), _mm256_sub_ps(ur, tr));
_mm256_storeu_ps(im.as_mut_ptr().add(j), _mm256_sub_ps(ui, ti));
_mm256_storeu_ps(re.as_mut_ptr().add(i + k), _mm256_add_ps(ur, tr));
_mm256_storeu_ps(im.as_mut_ptr().add(i + k), _mm256_add_ps(ui, ti));
}
}
pub(super) fn bitrev_swap(re: &mut [f32], im: &mut [f32], bitrev: &[u16]) {
let n = re.len();
if im.len() != n || bitrev.len() != n {
for (i, &rev) in bitrev.iter().enumerate() {
let j = rev as usize;
if i < j {
re.swap(i, j);
im.swap(i, j);
}
}
return;
}
let rp = re.as_mut_ptr();
let ip = im.as_mut_ptr();
for (i, &rev) in bitrev.iter().enumerate() {
let j = rev as usize;
if i < j && j < n {
unsafe {
std::ptr::swap(rp.add(i), rp.add(j));
std::ptr::swap(ip.add(i), ip.add(j));
}
}
}
}
#[cfg(target_arch = "x86_64")]
#[target_feature(enable = "avx")]
pub(super) unsafe fn ifft_stages_avx(re: &mut [f32], im: &mut [f32], tw_re: &[f32], tw_im: &[f32]) {
let n = re.len();
let mut len = 2usize;
let mut off = 0usize;
while len <= n {
let half = len / 2;
let mut i = 0usize;
while i < n {
let mut k = 0usize;
while k + 8 <= half {
unsafe { butterfly8(re, im, i, k, half, off, tw_re, tw_im) };
k += 8;
}
while k + 4 <= half {
unsafe { butterfly4(re, im, i, k, half, off, tw_re, tw_im) };
k += 4;
}
while k < half {
unsafe {
let wr = *tw_re.get_unchecked(off + k);
let wi = *tw_im.get_unchecked(off + k);
let j = i + k + half;
let rj = *re.get_unchecked(j);
let ij = *im.get_unchecked(j);
let ur = *re.get_unchecked(i + k);
let ui = *im.get_unchecked(i + k);
let tr = wr * rj - wi * ij;
let ti = wr * ij + wi * rj;
*re.get_unchecked_mut(j) = ur - tr;
*im.get_unchecked_mut(j) = ui - ti;
*re.get_unchecked_mut(i + k) = ur + tr;
*im.get_unchecked_mut(i + k) = ui + ti;
}
k += 1;
}
i += len;
}
off += half;
len *= 2;
}
}
pub(super) fn apply_soa(
plan: &super::Plan,
spec: &[f32],
out: &mut [f32],
re: &mut [f32],
im: &mut [f32],
) {
#[cfg(target_arch = "x86_64")]
{
let n = plan.n;
let n2 = n / 2;
let n4 = n / 4;
if spec.len() == n2
&& out.len() == n
&& re.len() == n4
&& im.len() == n4
&& plan.pre.len() == n4
&& plan.post.len() == n4
{
unsafe { prerot_sse(spec, &plan.pre, re, im) };
super::ifft_soa(re, im, &plan.bitrev, &plan.tw_re, &plan.tw_im);
unsafe {
postrot_sse(&plan.post, re, im);
permute_unchecked(out, re, im, n);
}
return;
}
}
apply_soa_scalar(plan, spec, out, re, im);
}
fn apply_soa_scalar(
plan: &super::Plan,
spec: &[f32],
out: &mut [f32],
re: &mut [f32],
im: &mut [f32],
) {
let n = plan.n;
let n2 = n / 2;
let n4 = n / 4;
for k in 0..n4 {
let z = super::C::new(spec[n2 - 2 * k - 1], spec[2 * k]).mul(plan.pre[k]);
re[k] = z.re;
im[k] = z.im;
}
super::ifft_soa(re, im, &plan.bitrev, &plan.tw_re, &plan.tw_im);
for k in 0..n4 {
let z = super::C::new(re[k], im[k]).mul(plan.post[k]);
re[k] = z.re;
im[k] = z.im;
}
let scale = 2.0 / n as f32;
let half = n4 / 2;
for p in 0..half {
let i = p * 2;
out[i] = scale * im[half + p];
out[n2 + i] = scale * re[half + p];
}
for p in 0..half {
let i = p * 2 + 1;
out[i] = -scale * re[half - 1 - p];
out[n2 + i] = -scale * im[half - 1 - p];
}
for n_i in 0..n4 {
out[n2 - 1 - n_i] = -out[n_i];
out[n - 1 - n_i] = out[n2 + n_i];
}
}
#[cfg(target_arch = "x86_64")]
#[target_feature(enable = "sse2")]
unsafe fn prerot_sse(spec: &[f32], pre: &[super::C], re: &mut [f32], im: &mut [f32]) {
use std::arch::x86_64::{
_mm_add_ps, _mm_loadu_ps, _mm_mul_ps, _mm_setr_ps, _mm_shuffle_ps, _mm_storeu_ps,
_mm_sub_ps,
};
let n4 = pre.len();
let n2 = n4 * 2;
let pf = pre.as_ptr().cast::<f32>();
let mut k = 0usize;
while k + 4 <= n4 {
unsafe {
let ar = _mm_setr_ps(
*spec.get_unchecked(n2 - 2 * k - 1),
*spec.get_unchecked(n2 - 2 * (k + 1) - 1),
*spec.get_unchecked(n2 - 2 * (k + 2) - 1),
*spec.get_unchecked(n2 - 2 * (k + 3) - 1),
);
let ai = _mm_setr_ps(
*spec.get_unchecked(2 * k),
*spec.get_unchecked(2 * (k + 1)),
*spec.get_unchecked(2 * (k + 2)),
*spec.get_unchecked(2 * (k + 3)),
);
let p0 = _mm_loadu_ps(pf.add(2 * k));
let p1 = _mm_loadu_ps(pf.add(2 * k + 4));
let br = _mm_shuffle_ps(p0, p1, 0x88);
let bi = _mm_shuffle_ps(p0, p1, 0xDD);
let zr = _mm_sub_ps(_mm_mul_ps(ar, br), _mm_mul_ps(ai, bi));
let zi = _mm_add_ps(_mm_mul_ps(ar, bi), _mm_mul_ps(ai, br));
_mm_storeu_ps(re.as_mut_ptr().add(k), zr);
_mm_storeu_ps(im.as_mut_ptr().add(k), zi);
}
k += 4;
}
while k < n4 {
let z = super::C::new(spec[n2 - 2 * k - 1], spec[2 * k]).mul(pre[k]);
re[k] = z.re;
im[k] = z.im;
k += 1;
}
}
#[cfg(target_arch = "x86_64")]
#[target_feature(enable = "sse2")]
unsafe fn postrot_sse(post: &[super::C], re: &mut [f32], im: &mut [f32]) {
use std::arch::x86_64::{
_mm_add_ps, _mm_loadu_ps, _mm_mul_ps, _mm_shuffle_ps, _mm_storeu_ps, _mm_sub_ps,
};
let n4 = post.len();
let pf = post.as_ptr().cast::<f32>();
let mut k = 0usize;
while k + 4 <= n4 {
unsafe {
let ar = _mm_loadu_ps(re.as_ptr().add(k));
let ai = _mm_loadu_ps(im.as_ptr().add(k));
let p0 = _mm_loadu_ps(pf.add(2 * k));
let p1 = _mm_loadu_ps(pf.add(2 * k + 4));
let br = _mm_shuffle_ps(p0, p1, 0x88);
let bi = _mm_shuffle_ps(p0, p1, 0xDD);
let zr = _mm_sub_ps(_mm_mul_ps(ar, br), _mm_mul_ps(ai, bi));
let zi = _mm_add_ps(_mm_mul_ps(ar, bi), _mm_mul_ps(ai, br));
_mm_storeu_ps(re.as_mut_ptr().add(k), zr);
_mm_storeu_ps(im.as_mut_ptr().add(k), zi);
}
k += 4;
}
while k < n4 {
let z = super::C::new(re[k], im[k]).mul(post[k]);
re[k] = z.re;
im[k] = z.im;
k += 1;
}
}
#[cfg(target_arch = "x86_64")]
unsafe fn permute_unchecked(out: &mut [f32], re: &[f32], im: &[f32], n: usize) {
let n2 = n / 2;
let n4 = n / 4;
let half = n4 / 2;
let scale = 2.0 / n as f32;
let neg = -scale;
let op = out.as_mut_ptr();
let rp = re.as_ptr();
let ip = im.as_ptr();
for p in 0..half {
let i = p * 2;
unsafe {
*op.add(i) = scale * *ip.add(half + p);
*op.add(n2 + i) = scale * *rp.add(half + p);
}
}
for p in 0..half {
let i = p * 2 + 1;
unsafe {
*op.add(i) = neg * *rp.add(half - 1 - p);
*op.add(n2 + i) = neg * *ip.add(half - 1 - p);
}
}
for n_i in 0..n4 {
unsafe {
let a = *op.add(n_i);
let b = *op.add(n2 + n_i);
*op.add(n2 - 1 - n_i) = -a;
*op.add(n - 1 - n_i) = b;
}
}
}