1use core::arch::x86_64::*;
2use num_complex::Complex;
3
4use crate::{common::FftNum, FftDirection};
5
6use crate::array_utils::DoubleBuf;
7use crate::twiddles;
8use crate::{Direction, Fft, Length};
9
10use super::sse_common::{assert_f32, assert_f64};
11use super::sse_utils::*;
12use super::sse_vector::{SseArrayMut, SseVector};
13
14#[inline(always)]
15unsafe fn pack_32(a: Complex<f32>, b: Complex<f32>) -> __m128 {
16 _mm_set_ps(b.im, b.re, a.im, a.re)
17}
18#[inline(always)]
19unsafe fn pack_64(a: Complex<f64>) -> __m128d {
20 _mm_set_pd(a.im, a.re)
21}
22
23#[allow(unused)]
24macro_rules! boilerplate_fft_sse_f32_butterfly {
25 ($struct_name:ident, $len:expr, $direction_fn:expr) => {
26 impl<T: FftNum> Fft<T> for $struct_name<T> {
27 fn process_immutable_with_scratch(
28 &self,
29 input: &[Complex<T>],
30 output: &mut [Complex<T>],
31 _scratch: &mut [Complex<T>],
32 ) {
33 unsafe {
34 let simd_input = crate::array_utils::workaround_transmute(input);
35 let simd_output = crate::array_utils::workaround_transmute_mut(output);
36 super::sse_common::sse_fft_helper_immut_unroll2x(
37 simd_input,
38 simd_output,
39 self.len(),
40 |input, output| {
41 self.perform_parallel_fft_contiguous(DoubleBuf { input, output })
42 },
43 |input, output| self.perform_fft_contiguous(DoubleBuf { input, output }),
44 );
45 }
46 }
47 fn process_outofplace_with_scratch(
48 &self,
49 input: &mut [Complex<T>],
50 output: &mut [Complex<T>],
51 _scratch: &mut [Complex<T>],
52 ) {
53 unsafe {
54 let simd_input = crate::array_utils::workaround_transmute_mut(input);
55 let simd_output = crate::array_utils::workaround_transmute_mut(output);
56 super::sse_common::sse_fft_helper_outofplace_unroll2x(
57 simd_input,
58 simd_output,
59 self.len(),
60 |input, output| {
61 self.perform_parallel_fft_contiguous(DoubleBuf { input, output })
62 },
63 |input, output| self.perform_fft_contiguous(DoubleBuf { input, output }),
64 );
65 }
66 }
67 fn process_with_scratch(&self, buffer: &mut [Complex<T>], _scratch: &mut [Complex<T>]) {
68 unsafe {
69 let simd_buffer = crate::array_utils::workaround_transmute_mut(buffer);
70 super::sse_common::sse_fft_helper_inplace_unroll2x(
71 simd_buffer,
72 self.len(),
73 |chunk| self.perform_parallel_fft_contiguous(chunk),
74 |chunk| self.perform_fft_contiguous(chunk),
75 )
76 }
77 }
78 #[inline(always)]
79 fn get_inplace_scratch_len(&self) -> usize {
80 0
81 }
82 #[inline(always)]
83 fn get_outofplace_scratch_len(&self) -> usize {
84 0
85 }
86 #[inline(always)]
87 fn get_immutable_scratch_len(&self) -> usize {
88 0
89 }
90 }
91 impl<T> Length for $struct_name<T> {
92 #[inline(always)]
93 fn len(&self) -> usize {
94 $len
95 }
96 }
97 impl<T> Direction for $struct_name<T> {
98 #[inline(always)]
99 fn fft_direction(&self) -> FftDirection {
100 $direction_fn(self)
101 }
102 }
103 };
104}
105
106macro_rules! boilerplate_fft_sse_f32_butterfly_noparallel {
107 ($struct_name:ident, $len:expr, $direction_fn:expr) => {
108 impl<T: FftNum> Fft<T> for $struct_name<T> {
109 fn process_immutable_with_scratch(
110 &self,
111 input: &[Complex<T>],
112 output: &mut [Complex<T>],
113 _scratch: &mut [Complex<T>],
114 ) {
115 unsafe {
116 let simd_input = crate::array_utils::workaround_transmute(input);
117 let simd_output = crate::array_utils::workaround_transmute_mut(output);
118 super::sse_common::sse_fft_helper_immut(
119 simd_input,
120 simd_output,
121 &mut [],
122 self.len(),
123 0,
124 |input, output, _| self.perform_fft_contiguous(DoubleBuf { input, output }),
125 );
126 }
127 }
128 fn process_outofplace_with_scratch(
129 &self,
130 input: &mut [Complex<T>],
131 output: &mut [Complex<T>],
132 _scratch: &mut [Complex<T>],
133 ) {
134 unsafe {
135 let simd_input = crate::array_utils::workaround_transmute_mut(input);
136 let simd_output = crate::array_utils::workaround_transmute_mut(output);
137 super::sse_common::sse_fft_helper_outofplace(
138 simd_input,
139 simd_output,
140 &mut [],
141 self.len(),
142 0,
143 |input, output, _| self.perform_fft_contiguous(DoubleBuf { input, output }),
144 );
145 }
146 }
147 fn process_with_scratch(&self, buffer: &mut [Complex<T>], _scratch: &mut [Complex<T>]) {
148 unsafe {
149 let simd_buffer = crate::array_utils::workaround_transmute_mut(buffer);
150 super::sse_common::sse_fft_helper_inplace(
151 simd_buffer,
152 &mut [],
153 self.len(),
154 0,
155 |chunk, _| self.perform_fft_contiguous(chunk),
156 )
157 }
158 }
159 #[inline(always)]
160 fn get_inplace_scratch_len(&self) -> usize {
161 0
162 }
163 #[inline(always)]
164 fn get_outofplace_scratch_len(&self) -> usize {
165 0
166 }
167 #[inline(always)]
168 fn get_immutable_scratch_len(&self) -> usize {
169 0
170 }
171 }
172 impl<T> Length for $struct_name<T> {
173 #[inline(always)]
174 fn len(&self) -> usize {
175 $len
176 }
177 }
178 impl<T> Direction for $struct_name<T> {
179 #[inline(always)]
180 fn fft_direction(&self) -> FftDirection {
181 $direction_fn(self)
182 }
183 }
184 };
185}
186
187macro_rules! boilerplate_fft_sse_f64_butterfly {
188 ($struct_name:ident, $len:expr, $direction_fn:expr) => {
189 impl<T: FftNum> Fft<T> for $struct_name<T> {
190 fn process_immutable_with_scratch(
191 &self,
192 input: &[Complex<T>],
193 output: &mut [Complex<T>],
194 _scratch: &mut [Complex<T>],
195 ) {
196 unsafe {
197 let simd_input = crate::array_utils::workaround_transmute(input);
198 let simd_output = crate::array_utils::workaround_transmute_mut(output);
199 super::sse_common::sse_fft_helper_immut(
200 simd_input,
201 simd_output,
202 &mut [],
203 self.len(),
204 0,
205 |input, output, _| self.perform_fft_contiguous(DoubleBuf { input, output }),
206 );
207 }
208 }
209 fn process_outofplace_with_scratch(
210 &self,
211 input: &mut [Complex<T>],
212 output: &mut [Complex<T>],
213 _scratch: &mut [Complex<T>],
214 ) {
215 unsafe {
216 let simd_input = crate::array_utils::workaround_transmute_mut(input);
217 let simd_output = crate::array_utils::workaround_transmute_mut(output);
218 super::sse_common::sse_fft_helper_outofplace(
219 simd_input,
220 simd_output,
221 &mut [],
222 self.len(),
223 0,
224 |input, output, _| self.perform_fft_contiguous(DoubleBuf { input, output }),
225 );
226 }
227 }
228 fn process_with_scratch(&self, buffer: &mut [Complex<T>], _scratch: &mut [Complex<T>]) {
229 unsafe {
230 let simd_buffer = crate::array_utils::workaround_transmute_mut(buffer);
231 super::sse_common::sse_fft_helper_inplace(
232 simd_buffer,
233 &mut [],
234 self.len(),
235 0,
236 |chunk, _| self.perform_fft_contiguous(chunk),
237 )
238 }
239 }
240 #[inline(always)]
241 fn get_inplace_scratch_len(&self) -> usize {
242 0
243 }
244 #[inline(always)]
245 fn get_outofplace_scratch_len(&self) -> usize {
246 0
247 }
248 #[inline(always)]
249 fn get_immutable_scratch_len(&self) -> usize {
250 0
251 }
252 }
253 impl<T> Length for $struct_name<T> {
254 #[inline(always)]
255 fn len(&self) -> usize {
256 $len
257 }
258 }
259 impl<T> Direction for $struct_name<T> {
260 #[inline(always)]
261 fn fft_direction(&self) -> FftDirection {
262 $direction_fn(self)
263 }
264 }
265 };
266}
267
268pub struct SseF32Butterfly1<T> {
276 direction: FftDirection,
277 _phantom: std::marker::PhantomData<T>,
278}
279
280boilerplate_fft_sse_f32_butterfly!(SseF32Butterfly1, 1, |this: &SseF32Butterfly1<_>| this
281 .direction);
282impl<T: FftNum> SseF32Butterfly1<T> {
283 #[inline(always)]
284 pub fn new(direction: FftDirection) -> Self {
285 assert_f32::<T>();
286 Self {
287 direction,
288 _phantom: std::marker::PhantomData,
289 }
290 }
291 #[inline(always)]
292 pub(crate) unsafe fn perform_fft_contiguous(&self, mut buffer: impl SseArrayMut<f32>) {
293 let value = buffer.load_partial_lo_complex(0);
294 buffer.store_partial_lo_complex(value, 0);
295 }
296
297 #[inline(always)]
298 pub(crate) unsafe fn perform_parallel_fft_contiguous(&self, mut buffer: impl SseArrayMut<f32>) {
299 let value = buffer.load_complex(0);
300 buffer.store_complex(value, 0);
301 }
302}
303
304pub struct SseF64Butterfly1<T> {
312 direction: FftDirection,
313 _phantom: std::marker::PhantomData<T>,
314}
315
316boilerplate_fft_sse_f64_butterfly!(SseF64Butterfly1, 1, |this: &SseF64Butterfly1<_>| this
317 .direction);
318impl<T: FftNum> SseF64Butterfly1<T> {
319 #[inline(always)]
320 pub fn new(direction: FftDirection) -> Self {
321 assert_f64::<T>();
322 Self {
323 direction,
324 _phantom: std::marker::PhantomData,
325 }
326 }
327 #[inline(always)]
328 pub(crate) unsafe fn perform_fft_contiguous(&self, mut buffer: impl SseArrayMut<f64>) {
329 let value = buffer.load_complex(0);
330 buffer.store_complex(value, 0);
331 }
332}
333
334pub struct SseF32Butterfly2<T> {
342 direction: FftDirection,
343 _phantom: std::marker::PhantomData<T>,
344}
345
346boilerplate_fft_sse_f32_butterfly!(SseF32Butterfly2, 2, |this: &SseF32Butterfly2<_>| this
347 .direction);
348impl<T: FftNum> SseF32Butterfly2<T> {
349 #[inline(always)]
350 pub fn new(direction: FftDirection) -> Self {
351 assert_f32::<T>();
352 Self {
353 direction,
354 _phantom: std::marker::PhantomData,
355 }
356 }
357 #[inline(always)]
358 pub(crate) unsafe fn perform_fft_contiguous(&self, mut buffer: impl SseArrayMut<f32>) {
359 let values = buffer.load_complex(0);
360
361 let temp = self.perform_fft_direct(values);
362
363 buffer.store_complex(temp, 0);
364 }
365
366 #[inline(always)]
367 pub(crate) unsafe fn perform_parallel_fft_contiguous(&self, mut buffer: impl SseArrayMut<f32>) {
368 let values_a = buffer.load_complex(0);
369 let values_b = buffer.load_complex(2);
370
371 let out = self.perform_parallel_fft_direct(values_a, values_b);
372
373 let [out02, out13] = transpose_complex_2x2_f32(out[0], out[1]);
374
375 buffer.store_complex(out02, 0);
376 buffer.store_complex(out13, 2);
377 }
378
379 #[inline(always)]
382 pub(crate) unsafe fn perform_fft_direct(&self, values: __m128) -> __m128 {
383 solo_fft2_f32(values)
384 }
385
386 #[inline(always)]
389 pub(crate) unsafe fn perform_parallel_fft_direct(
390 &self,
391 values_x: __m128,
392 values_y: __m128,
393 ) -> [__m128; 2] {
394 parallel_fft2_contiguous_f32(values_x, values_y)
395 }
396}
397
398#[inline(always)]
401pub(crate) unsafe fn parallel_fft2_interleaved_f32(val02: __m128, val13: __m128) -> [__m128; 2] {
402 let temp0 = _mm_add_ps(val02, val13);
403 let temp1 = _mm_sub_ps(val02, val13);
404 [temp0, temp1]
405}
406
407#[inline(always)]
410unsafe fn parallel_fft2_contiguous_f32(left: __m128, right: __m128) -> [__m128; 2] {
411 let [temp02, temp13] = transpose_complex_2x2_f32(left, right);
412 parallel_fft2_interleaved_f32(temp02, temp13)
413}
414
415#[inline(always)]
418unsafe fn solo_fft2_f32(values: __m128) -> __m128 {
419 let temp = reverse_complex_elements_f32(values);
420 let temp2 = negate_hi_f32(values);
421 _mm_add_ps(temp2, temp)
422}
423
424pub struct SseF64Butterfly2<T> {
432 direction: FftDirection,
433 _phantom: std::marker::PhantomData<T>,
434}
435
436boilerplate_fft_sse_f64_butterfly!(SseF64Butterfly2, 2, |this: &SseF64Butterfly2<_>| this
437 .direction);
438impl<T: FftNum> SseF64Butterfly2<T> {
439 #[inline(always)]
440 pub fn new(direction: FftDirection) -> Self {
441 assert_f64::<T>();
442 Self {
443 direction,
444 _phantom: std::marker::PhantomData,
445 }
446 }
447
448 #[inline(always)]
449 pub(crate) unsafe fn perform_fft_contiguous(&self, mut buffer: impl SseArrayMut<f64>) {
450 let value0 = buffer.load_complex(0);
451 let value1 = buffer.load_complex(1);
452
453 let out = self.perform_fft_direct(value0, value1);
454
455 buffer.store_complex(out[0], 0);
456 buffer.store_complex(out[1], 1);
457 }
458
459 #[inline(always)]
460 pub(crate) unsafe fn perform_fft_direct(
461 &self,
462 value0: __m128d,
463 value1: __m128d,
464 ) -> [__m128d; 2] {
465 solo_fft2_f64(value0, value1)
466 }
467}
468
469#[inline(always)]
470pub(crate) unsafe fn solo_fft2_f64(left: __m128d, right: __m128d) -> [__m128d; 2] {
471 let temp0 = _mm_add_pd(left, right);
472 let temp1 = _mm_sub_pd(left, right);
473 [temp0, temp1]
474}
475
476pub struct SseF32Butterfly3<T> {
484 direction: FftDirection,
485 _phantom: std::marker::PhantomData<T>,
486 rotate: Rotate90F32,
487 twiddle: __m128,
488 twiddle1re: __m128,
489 twiddle1im: __m128,
490}
491
492boilerplate_fft_sse_f32_butterfly!(SseF32Butterfly3, 3, |this: &SseF32Butterfly3<_>| this
493 .direction);
494impl<T: FftNum> SseF32Butterfly3<T> {
495 #[inline(always)]
496 pub fn new(direction: FftDirection) -> Self {
497 assert_f32::<T>();
498 let rotate = Rotate90F32::new(true);
499 let tw1: Complex<f32> = twiddles::compute_twiddle(1, 3, direction);
500 let twiddle = unsafe { _mm_set_ps(-tw1.im, -tw1.im, tw1.re, tw1.re) };
501 let twiddle1re = unsafe { _mm_set_ps(tw1.re, tw1.re, tw1.re, tw1.re) };
502 let twiddle1im = unsafe { _mm_set_ps(tw1.im, tw1.im, tw1.im, tw1.im) };
503 Self {
504 direction,
505 _phantom: std::marker::PhantomData,
506 rotate,
507 twiddle,
508 twiddle1re,
509 twiddle1im,
510 }
511 }
512 #[inline(always)]
513 pub(crate) unsafe fn perform_fft_contiguous(&self, mut buffer: impl SseArrayMut<f32>) {
514 let value0x = buffer.load_partial_lo_complex(0);
515 let value12 = buffer.load_complex(1);
516
517 let out = self.perform_fft_direct(value0x, value12);
518
519 buffer.store_partial_lo_complex(out[0], 0);
520 buffer.store_complex(out[1], 1);
521 }
522
523 #[inline(always)]
524 pub(crate) unsafe fn perform_parallel_fft_contiguous(&self, mut buffer: impl SseArrayMut<f32>) {
525 let valuea0a1 = buffer.load_complex(0);
526 let valuea2b0 = buffer.load_complex(2);
527 let valueb1b2 = buffer.load_complex(4);
528
529 let value0 = extract_lo_hi_f32(valuea0a1, valuea2b0);
530 let value1 = extract_hi_lo_f32(valuea0a1, valueb1b2);
531 let value2 = extract_lo_hi_f32(valuea2b0, valueb1b2);
532
533 let out = self.perform_parallel_fft_direct(value0, value1, value2);
534
535 let out0 = extract_lo_lo_f32(out[0], out[1]);
536 let out1 = extract_lo_hi_f32(out[2], out[0]);
537 let out2 = extract_hi_hi_f32(out[1], out[2]);
538
539 buffer.store_complex(out0, 0);
540 buffer.store_complex(out1, 2);
541 buffer.store_complex(out2, 4);
542 }
543
544 #[inline(always)]
548 pub(crate) unsafe fn perform_fft_direct(
549 &self,
550 value0x: __m128,
551 value12: __m128,
552 ) -> [__m128; 2] {
553 let rev12 = negate_hi_f32(reverse_complex_elements_f32(value12));
555 let temp12pn = self.rotate.rotate_hi(_mm_add_ps(value12, rev12));
556 let twiddled = _mm_mul_ps(temp12pn, self.twiddle);
557 let temp = _mm_add_ps(value0x, twiddled);
558 let out12 = solo_fft2_f32(temp);
559 let out0x = _mm_add_ps(value0x, temp12pn);
560 [out0x, out12]
561 }
562
563 #[inline(always)]
566 pub(crate) unsafe fn perform_parallel_fft_direct(
567 &self,
568 value0: __m128,
569 value1: __m128,
570 value2: __m128,
571 ) -> [__m128; 3] {
572 let x12p = _mm_add_ps(value1, value2);
574 let x12n = _mm_sub_ps(value1, value2);
575 let sum = _mm_add_ps(value0, x12p);
576
577 let temp_a = _mm_mul_ps(self.twiddle1re, x12p);
578 let temp_a = _mm_add_ps(temp_a, value0);
579
580 let n_rot = self.rotate.rotate_both(x12n);
581 let temp_b = _mm_mul_ps(self.twiddle1im, n_rot);
582
583 let x1 = _mm_add_ps(temp_a, temp_b);
584 let x2 = _mm_sub_ps(temp_a, temp_b);
585 [sum, x1, x2]
586 }
587}
588
589pub struct SseF64Butterfly3<T> {
597 direction: FftDirection,
598 _phantom: std::marker::PhantomData<T>,
599 rotate: Rotate90F64,
600 twiddle1re: __m128d,
601 twiddle1im: __m128d,
602}
603
604boilerplate_fft_sse_f64_butterfly!(SseF64Butterfly3, 3, |this: &SseF64Butterfly3<_>| this
605 .direction);
606impl<T: FftNum> SseF64Butterfly3<T> {
607 #[inline(always)]
608 pub fn new(direction: FftDirection) -> Self {
609 assert_f64::<T>();
610 let rotate = Rotate90F64::new(true);
611 let tw1: Complex<f64> = twiddles::compute_twiddle(1, 3, direction);
612 let twiddle1re = unsafe { _mm_set_pd(tw1.re, tw1.re) };
613 let twiddle1im = unsafe { _mm_set_pd(tw1.im, tw1.im) };
614
615 Self {
616 direction,
617 _phantom: std::marker::PhantomData,
618 rotate,
619 twiddle1re,
620 twiddle1im,
621 }
622 }
623
624 #[inline(always)]
625 pub(crate) unsafe fn perform_fft_contiguous(&self, mut buffer: impl SseArrayMut<f64>) {
626 let value0 = buffer.load_complex(0);
627 let value1 = buffer.load_complex(1);
628 let value2 = buffer.load_complex(2);
629
630 let out = self.perform_fft_direct(value0, value1, value2);
631
632 buffer.store_complex(out[0], 0);
633 buffer.store_complex(out[1], 1);
634 buffer.store_complex(out[2], 2);
635 }
636
637 #[inline(always)]
640 pub(crate) unsafe fn perform_fft_direct(
641 &self,
642 value0: __m128d,
643 value1: __m128d,
644 value2: __m128d,
645 ) -> [__m128d; 3] {
646 let x12p = _mm_add_pd(value1, value2);
648 let x12n = _mm_sub_pd(value1, value2);
649 let sum = _mm_add_pd(value0, x12p);
650
651 let temp_a = _mm_mul_pd(self.twiddle1re, x12p);
652 let temp_a = _mm_add_pd(temp_a, value0);
653
654 let n_rot = self.rotate.rotate(x12n);
655 let temp_b = _mm_mul_pd(self.twiddle1im, n_rot);
656
657 let x1 = _mm_add_pd(temp_a, temp_b);
658 let x2 = _mm_sub_pd(temp_a, temp_b);
659 [sum, x1, x2]
660 }
661}
662
663pub struct SseF32Butterfly4<T> {
671 direction: FftDirection,
672 _phantom: std::marker::PhantomData<T>,
673 rotate: Rotate90F32,
674}
675
676boilerplate_fft_sse_f32_butterfly!(SseF32Butterfly4, 4, |this: &SseF32Butterfly4<_>| this
677 .direction);
678impl<T: FftNum> SseF32Butterfly4<T> {
679 #[inline(always)]
680 pub fn new(direction: FftDirection) -> Self {
681 assert_f32::<T>();
682 let rotate = if direction == FftDirection::Inverse {
683 Rotate90F32::new(true)
684 } else {
685 Rotate90F32::new(false)
686 };
687 Self {
688 direction,
689 _phantom: std::marker::PhantomData,
690 rotate,
691 }
692 }
693 #[inline(always)]
694 pub(crate) unsafe fn perform_fft_contiguous(&self, mut buffer: impl SseArrayMut<f32>) {
695 let value01 = buffer.load_complex(0);
696 let value23 = buffer.load_complex(2);
697
698 let out = self.perform_fft_direct(value01, value23);
699
700 buffer.store_complex(out[0], 0);
701 buffer.store_complex(out[1], 2);
702 }
703
704 #[inline(always)]
705 pub(crate) unsafe fn perform_parallel_fft_contiguous(&self, mut buffer: impl SseArrayMut<f32>) {
706 let value01a = buffer.load_complex(0);
707 let value23a = buffer.load_complex(2);
708 let value01b = buffer.load_complex(4);
709 let value23b = buffer.load_complex(6);
710
711 let [value0ab, value1ab] = transpose_complex_2x2_f32(value01a, value01b);
712 let [value2ab, value3ab] = transpose_complex_2x2_f32(value23a, value23b);
713
714 let out = self.perform_parallel_fft_direct([value0ab, value1ab, value2ab, value3ab]);
715
716 let [out0, out1] = transpose_complex_2x2_f32(out[0], out[1]);
717 let [out2, out3] = transpose_complex_2x2_f32(out[2], out[3]);
718
719 buffer.store_complex(out0, 0);
720 buffer.store_complex(out1, 4);
721 buffer.store_complex(out2, 2);
722 buffer.store_complex(out3, 6);
723 }
724
725 #[inline(always)]
728 pub(crate) unsafe fn perform_fft_direct(
729 &self,
730 value01: __m128,
731 value23: __m128,
732 ) -> [__m128; 2] {
733 let mut temp = parallel_fft2_interleaved_f32(value01, value23);
740
741 temp[1] = self.rotate.rotate_hi(temp[1]);
743
744 parallel_fft2_contiguous_f32(temp[0], temp[1])
750 }
751
752 #[inline(always)]
753 pub(crate) unsafe fn perform_parallel_fft_direct(&self, values: [__m128; 4]) -> [__m128; 4] {
754 let temp0 = parallel_fft2_interleaved_f32(values[0], values[2]);
761 let mut temp1 = parallel_fft2_interleaved_f32(values[1], values[3]);
762
763 temp1[1] = self.rotate.rotate_both(temp1[1]);
765
766 let out0 = parallel_fft2_interleaved_f32(temp0[0], temp1[0]);
770 let out2 = parallel_fft2_interleaved_f32(temp0[1], temp1[1]);
771
772 [out0[0], out2[0], out0[1], out2[1]]
774 }
775}
776
777pub struct SseF64Butterfly4<T> {
785 direction: FftDirection,
786 _phantom: std::marker::PhantomData<T>,
787 rotate: Rotate90F64,
788}
789
790boilerplate_fft_sse_f64_butterfly!(SseF64Butterfly4, 4, |this: &SseF64Butterfly4<_>| this
791 .direction);
792impl<T: FftNum> SseF64Butterfly4<T> {
793 #[inline(always)]
794 pub fn new(direction: FftDirection) -> Self {
795 assert_f64::<T>();
796 let rotate = if direction == FftDirection::Inverse {
797 Rotate90F64::new(true)
798 } else {
799 Rotate90F64::new(false)
800 };
801
802 Self {
803 direction,
804 _phantom: std::marker::PhantomData,
805 rotate,
806 }
807 }
808
809 #[inline(always)]
810 pub(crate) unsafe fn perform_fft_contiguous(&self, mut buffer: impl SseArrayMut<f64>) {
811 let value0 = buffer.load_complex(0);
812 let value1 = buffer.load_complex(1);
813 let value2 = buffer.load_complex(2);
814 let value3 = buffer.load_complex(3);
815
816 let out = self.perform_fft_direct([value0, value1, value2, value3]);
817
818 buffer.store_complex(out[0], 0);
819 buffer.store_complex(out[1], 1);
820 buffer.store_complex(out[2], 2);
821 buffer.store_complex(out[3], 3);
822 }
823
824 #[inline(always)]
825 pub(crate) unsafe fn perform_fft_direct(&self, values: [__m128d; 4]) -> [__m128d; 4] {
826 let temp0 = solo_fft2_f64(values[0], values[2]);
833 let mut temp1 = solo_fft2_f64(values[1], values[3]);
834
835 temp1[1] = self.rotate.rotate(temp1[1]);
837
838 let out0 = solo_fft2_f64(temp0[0], temp1[0]);
842 let out2 = solo_fft2_f64(temp0[1], temp1[1]);
843
844 [out0[0], out2[0], out0[1], out2[1]]
846 }
847}
848
849pub struct SseF32Butterfly5<T> {
857 direction: FftDirection,
858 _phantom: std::marker::PhantomData<T>,
859 rotate: Rotate90F32,
860 twiddle12re: __m128,
861 twiddle21re: __m128,
862 twiddle12im: __m128,
863 twiddle21im: __m128,
864 twiddle1re: __m128,
865 twiddle1im: __m128,
866 twiddle2re: __m128,
867 twiddle2im: __m128,
868}
869
870boilerplate_fft_sse_f32_butterfly!(SseF32Butterfly5, 5, |this: &SseF32Butterfly5<_>| this
871 .direction);
872impl<T: FftNum> SseF32Butterfly5<T> {
873 #[inline(always)]
874 pub fn new(direction: FftDirection) -> Self {
875 assert_f32::<T>();
876 let rotate = Rotate90F32::new(true);
877 let tw1: Complex<f32> = twiddles::compute_twiddle(1, 5, direction);
878 let tw2: Complex<f32> = twiddles::compute_twiddle(2, 5, direction);
879 let twiddle12re = unsafe { _mm_set_ps(tw2.re, tw2.re, tw1.re, tw1.re) };
880 let twiddle21re = unsafe { _mm_set_ps(tw1.re, tw1.re, tw2.re, tw2.re) };
881 let twiddle12im = unsafe { _mm_set_ps(tw2.im, tw2.im, tw1.im, tw1.im) };
882 let twiddle21im = unsafe { _mm_set_ps(-tw1.im, -tw1.im, tw2.im, tw2.im) };
883 let twiddle1re = unsafe { _mm_set_ps(tw1.re, tw1.re, tw1.re, tw1.re) };
884 let twiddle1im = unsafe { _mm_set_ps(tw1.im, tw1.im, tw1.im, tw1.im) };
885 let twiddle2re = unsafe { _mm_set_ps(tw2.re, tw2.re, tw2.re, tw2.re) };
886 let twiddle2im = unsafe { _mm_set_ps(tw2.im, tw2.im, tw2.im, tw2.im) };
887
888 Self {
889 direction,
890 _phantom: std::marker::PhantomData,
891 rotate,
892 twiddle12re,
893 twiddle21re,
894 twiddle12im,
895 twiddle21im,
896 twiddle1re,
897 twiddle1im,
898 twiddle2re,
899 twiddle2im,
900 }
901 }
902 #[inline(always)]
903 pub(crate) unsafe fn perform_fft_contiguous(&self, mut buffer: impl SseArrayMut<f32>) {
904 let value00 = buffer.load1_complex(0);
905 let value12 = buffer.load_complex(1);
906 let value34 = buffer.load_complex(3);
907
908 let out = self.perform_fft_direct(value00, value12, value34);
909
910 buffer.store_partial_lo_complex(out[0], 0);
911 buffer.store_complex(out[1], 1);
912 buffer.store_complex(out[2], 3);
913 }
914
915 #[inline(always)]
916 pub(crate) unsafe fn perform_parallel_fft_contiguous(&self, mut buffer: impl SseArrayMut<f32>) {
917 let input_packed = read_complex_to_array!(buffer, {0, 2, 4 ,6, 8});
918
919 let value0 = extract_lo_hi_f32(input_packed[0], input_packed[2]);
920 let value1 = extract_hi_lo_f32(input_packed[0], input_packed[3]);
921 let value2 = extract_lo_hi_f32(input_packed[1], input_packed[3]);
922 let value3 = extract_hi_lo_f32(input_packed[1], input_packed[4]);
923 let value4 = extract_lo_hi_f32(input_packed[2], input_packed[4]);
924
925 let out = self.perform_parallel_fft_direct(value0, value1, value2, value3, value4);
926
927 let out_packed = [
928 extract_lo_lo_f32(out[0], out[1]),
929 extract_lo_lo_f32(out[2], out[3]),
930 extract_lo_hi_f32(out[4], out[0]),
931 extract_hi_hi_f32(out[1], out[2]),
932 extract_hi_hi_f32(out[3], out[4]),
933 ];
934
935 write_complex_to_array_strided!(out_packed, buffer, 2, {0, 1, 2, 3, 4});
936 }
937
938 #[inline(always)]
942 pub(crate) unsafe fn perform_fft_direct(
943 &self,
944 value00: __m128,
945 value12: __m128,
946 value34: __m128,
947 ) -> [__m128; 3] {
948 let temp43 = reverse_complex_elements_f32(value34);
950 let x1423p = _mm_add_ps(value12, temp43);
951 let x1423n = _mm_sub_ps(value12, temp43);
952
953 let x1414p = duplicate_lo_f32(x1423p);
954 let x2323p = duplicate_hi_f32(x1423p);
955 let x1414n = duplicate_lo_f32(x1423n);
956 let x2323n = duplicate_hi_f32(x1423n);
957
958 let temp_a1 = _mm_mul_ps(self.twiddle12re, x1414p);
959 let temp_a2 = _mm_mul_ps(self.twiddle21re, x2323p);
960
961 let temp_b1 = _mm_mul_ps(self.twiddle12im, x1414n);
962 let temp_b2 = _mm_mul_ps(self.twiddle21im, x2323n);
963
964 let temp_a = _mm_add_ps(temp_a1, temp_a2);
965 let temp_a = _mm_add_ps(value00, temp_a);
966
967 let temp_b = _mm_add_ps(temp_b1, temp_b2);
968
969 let b_rot = self.rotate.rotate_both(temp_b);
970
971 let x00 = _mm_add_ps(value00, _mm_add_ps(x1414p, x2323p));
972
973 let x12 = _mm_add_ps(temp_a, b_rot);
974 let x34 = reverse_complex_elements_f32(_mm_sub_ps(temp_a, b_rot));
975 [x00, x12, x34]
976 }
977
978 #[inline(always)]
981 pub(crate) unsafe fn perform_parallel_fft_direct(
982 &self,
983 value0: __m128,
984 value1: __m128,
985 value2: __m128,
986 value3: __m128,
987 value4: __m128,
988 ) -> [__m128; 5] {
989 let x14p = _mm_add_ps(value1, value4);
991 let x14n = _mm_sub_ps(value1, value4);
992 let x23p = _mm_add_ps(value2, value3);
993 let x23n = _mm_sub_ps(value2, value3);
994
995 let temp_a1_1 = _mm_mul_ps(self.twiddle1re, x14p);
996 let temp_a1_2 = _mm_mul_ps(self.twiddle2re, x23p);
997 let temp_b1_1 = _mm_mul_ps(self.twiddle1im, x14n);
998 let temp_b1_2 = _mm_mul_ps(self.twiddle2im, x23n);
999 let temp_a2_1 = _mm_mul_ps(self.twiddle1re, x23p);
1000 let temp_a2_2 = _mm_mul_ps(self.twiddle2re, x14p);
1001 let temp_b2_1 = _mm_mul_ps(self.twiddle2im, x14n);
1002 let temp_b2_2 = _mm_mul_ps(self.twiddle1im, x23n);
1003
1004 let temp_a1 = _mm_add_ps(value0, _mm_add_ps(temp_a1_1, temp_a1_2));
1005 let temp_b1 = _mm_add_ps(temp_b1_1, temp_b1_2);
1006 let temp_a2 = _mm_add_ps(value0, _mm_add_ps(temp_a2_1, temp_a2_2));
1007 let temp_b2 = _mm_sub_ps(temp_b2_1, temp_b2_2);
1008
1009 [
1010 _mm_add_ps(value0, _mm_add_ps(x14p, x23p)),
1011 _mm_add_ps(temp_a1, self.rotate.rotate_both(temp_b1)),
1012 _mm_add_ps(temp_a2, self.rotate.rotate_both(temp_b2)),
1013 _mm_sub_ps(temp_a2, self.rotate.rotate_both(temp_b2)),
1014 _mm_sub_ps(temp_a1, self.rotate.rotate_both(temp_b1)),
1015 ]
1016 }
1017}
1018
1019pub struct SseF64Butterfly5<T> {
1027 direction: FftDirection,
1028 _phantom: std::marker::PhantomData<T>,
1029 rotate: Rotate90F64,
1030 twiddle1re: __m128d,
1031 twiddle1im: __m128d,
1032 twiddle2re: __m128d,
1033 twiddle2im: __m128d,
1034}
1035
1036boilerplate_fft_sse_f64_butterfly!(SseF64Butterfly5, 5, |this: &SseF64Butterfly5<_>| this
1037 .direction);
1038impl<T: FftNum> SseF64Butterfly5<T> {
1039 #[inline(always)]
1040 pub fn new(direction: FftDirection) -> Self {
1041 assert_f64::<T>();
1042 let rotate = Rotate90F64::new(true);
1043 let tw1: Complex<f64> = twiddles::compute_twiddle(1, 5, direction);
1044 let tw2: Complex<f64> = twiddles::compute_twiddle(2, 5, direction);
1045 let twiddle1re = unsafe { _mm_set_pd(tw1.re, tw1.re) };
1046 let twiddle1im = unsafe { _mm_set_pd(tw1.im, tw1.im) };
1047 let twiddle2re = unsafe { _mm_set_pd(tw2.re, tw2.re) };
1048 let twiddle2im = unsafe { _mm_set_pd(tw2.im, tw2.im) };
1049
1050 Self {
1051 direction,
1052 _phantom: std::marker::PhantomData,
1053 rotate,
1054 twiddle1re,
1055 twiddle1im,
1056 twiddle2re,
1057 twiddle2im,
1058 }
1059 }
1060
1061 #[inline(always)]
1062 pub(crate) unsafe fn perform_fft_contiguous(&self, mut buffer: impl SseArrayMut<f64>) {
1063 let value0 = buffer.load_complex(0);
1064 let value1 = buffer.load_complex(1);
1065 let value2 = buffer.load_complex(2);
1066 let value3 = buffer.load_complex(3);
1067 let value4 = buffer.load_complex(4);
1068
1069 let out = self.perform_fft_direct(value0, value1, value2, value3, value4);
1070
1071 buffer.store_complex(out[0], 0);
1072 buffer.store_complex(out[1], 1);
1073 buffer.store_complex(out[2], 2);
1074 buffer.store_complex(out[3], 3);
1075 buffer.store_complex(out[4], 4);
1076 }
1077
1078 #[inline(always)]
1081 pub(crate) unsafe fn perform_fft_direct(
1082 &self,
1083 value0: __m128d,
1084 value1: __m128d,
1085 value2: __m128d,
1086 value3: __m128d,
1087 value4: __m128d,
1088 ) -> [__m128d; 5] {
1089 let x14p = _mm_add_pd(value1, value4);
1091 let x14n = _mm_sub_pd(value1, value4);
1092 let x23p = _mm_add_pd(value2, value3);
1093 let x23n = _mm_sub_pd(value2, value3);
1094
1095 let temp_a1_1 = _mm_mul_pd(self.twiddle1re, x14p);
1096 let temp_a1_2 = _mm_mul_pd(self.twiddle2re, x23p);
1097 let temp_a2_1 = _mm_mul_pd(self.twiddle2re, x14p);
1098 let temp_a2_2 = _mm_mul_pd(self.twiddle1re, x23p);
1099
1100 let temp_b1_1 = _mm_mul_pd(self.twiddle1im, x14n);
1101 let temp_b1_2 = _mm_mul_pd(self.twiddle2im, x23n);
1102 let temp_b2_1 = _mm_mul_pd(self.twiddle2im, x14n);
1103 let temp_b2_2 = _mm_mul_pd(self.twiddle1im, x23n);
1104
1105 let temp_a1 = _mm_add_pd(value0, _mm_add_pd(temp_a1_1, temp_a1_2));
1106 let temp_a2 = _mm_add_pd(value0, _mm_add_pd(temp_a2_1, temp_a2_2));
1107
1108 let temp_b1 = _mm_add_pd(temp_b1_1, temp_b1_2);
1109 let temp_b2 = _mm_sub_pd(temp_b2_1, temp_b2_2);
1110
1111 let temp_b1_rot = self.rotate.rotate(temp_b1);
1112 let temp_b2_rot = self.rotate.rotate(temp_b2);
1113 [
1114 _mm_add_pd(value0, _mm_add_pd(x14p, x23p)),
1115 _mm_add_pd(temp_a1, temp_b1_rot),
1116 _mm_add_pd(temp_a2, temp_b2_rot),
1117 _mm_sub_pd(temp_a2, temp_b2_rot),
1118 _mm_sub_pd(temp_a1, temp_b1_rot),
1119 ]
1120 }
1121}
1122
1123pub struct SseF32Butterfly6<T> {
1131 direction: FftDirection,
1132 _phantom: std::marker::PhantomData<T>,
1133 bf3: SseF32Butterfly3<T>,
1134}
1135
1136boilerplate_fft_sse_f32_butterfly!(SseF32Butterfly6, 6, |this: &SseF32Butterfly6<_>| this
1137 .direction);
1138impl<T: FftNum> SseF32Butterfly6<T> {
1139 #[inline(always)]
1140 pub fn new(direction: FftDirection) -> Self {
1141 assert_f32::<T>();
1142 let bf3 = SseF32Butterfly3::new(direction);
1143
1144 Self {
1145 direction,
1146 _phantom: std::marker::PhantomData,
1147 bf3,
1148 }
1149 }
1150
1151 #[inline(always)]
1152 pub(crate) unsafe fn perform_fft_contiguous(&self, mut buffer: impl SseArrayMut<f32>) {
1153 let value01 = buffer.load_complex(0);
1154 let value23 = buffer.load_complex(2);
1155 let value45 = buffer.load_complex(4);
1156
1157 let out = self.perform_fft_direct(value01, value23, value45);
1158
1159 buffer.store_complex(out[0], 0);
1160 buffer.store_complex(out[1], 2);
1161 buffer.store_complex(out[2], 4);
1162 }
1163
1164 #[inline(always)]
1165 pub(crate) unsafe fn perform_parallel_fft_contiguous(&self, mut buffer: impl SseArrayMut<f32>) {
1166 let input_packed = read_complex_to_array!(buffer, {0, 2, 4, 6, 8, 10});
1167
1168 let values = interleave_complex_f32!(input_packed, 3, {0, 1, 2});
1169
1170 let out = self.perform_parallel_fft_direct(
1171 values[0], values[1], values[2], values[3], values[4], values[5],
1172 );
1173
1174 let out_sorted = separate_interleaved_complex_f32!(out, {0, 2, 4});
1175 write_complex_to_array_strided!(out_sorted, buffer, 2, {0, 1, 2, 3, 4, 5});
1176 }
1177
1178 #[inline(always)]
1179 pub(crate) unsafe fn perform_fft_direct(
1180 &self,
1181 value01: __m128,
1182 value23: __m128,
1183 value45: __m128,
1184 ) -> [__m128; 3] {
1185 let reord0 = extract_lo_hi_f32(value01, value23);
1189 let reord1 = extract_lo_hi_f32(value23, value45);
1190 let reord2 = extract_lo_hi_f32(value45, value01);
1191
1192 let mid = self.bf3.perform_parallel_fft_direct(reord0, reord1, reord2);
1193
1194 let [output0, output1] = parallel_fft2_contiguous_f32(mid[0], mid[1]);
1198 let output2 = solo_fft2_f32(mid[2]);
1199
1200 [
1202 extract_lo_hi_f32(output0, output1),
1203 extract_lo_lo_f32(output2, output1),
1204 extract_hi_hi_f32(output0, output2),
1205 ]
1206 }
1207
1208 #[inline(always)]
1209 pub(crate) unsafe fn perform_parallel_fft_direct(
1210 &self,
1211 value0: __m128,
1212 value1: __m128,
1213 value2: __m128,
1214 value3: __m128,
1215 value4: __m128,
1216 value5: __m128,
1217 ) -> [__m128; 6] {
1218 let mid0 = self.bf3.perform_parallel_fft_direct(value0, value2, value4);
1222 let mid1 = self.bf3.perform_parallel_fft_direct(value3, value5, value1);
1223
1224 let [output0, output1] = parallel_fft2_interleaved_f32(mid0[0], mid1[0]);
1228 let [output2, output3] = parallel_fft2_interleaved_f32(mid0[1], mid1[1]);
1229 let [output4, output5] = parallel_fft2_interleaved_f32(mid0[2], mid1[2]);
1230
1231 [output0, output3, output4, output1, output2, output5]
1233 }
1234}
1235
1236pub struct SseF64Butterfly6<T> {
1244 _phantom: std::marker::PhantomData<T>,
1245 bf3: SseF64Butterfly3<T>,
1246}
1247
1248boilerplate_fft_sse_f64_butterfly!(SseF64Butterfly6, 6, |this: &SseF64Butterfly6<_>| this
1249 .bf3
1250 .direction);
1251impl<T: FftNum> SseF64Butterfly6<T> {
1252 #[inline(always)]
1253 pub fn new(direction: FftDirection) -> Self {
1254 assert_f64::<T>();
1255 let bf3 = SseF64Butterfly3::new(direction);
1256
1257 Self {
1258 _phantom: std::marker::PhantomData,
1259 bf3,
1260 }
1261 }
1262
1263 #[inline(always)]
1264 pub(crate) unsafe fn perform_fft_contiguous(&self, mut buffer: impl SseArrayMut<f64>) {
1265 let value0 = buffer.load_complex(0);
1266 let value1 = buffer.load_complex(1);
1267 let value2 = buffer.load_complex(2);
1268 let value3 = buffer.load_complex(3);
1269 let value4 = buffer.load_complex(4);
1270 let value5 = buffer.load_complex(5);
1271
1272 let out = self.perform_fft_direct([value0, value1, value2, value3, value4, value5]);
1273
1274 buffer.store_complex(out[0], 0);
1275 buffer.store_complex(out[1], 1);
1276 buffer.store_complex(out[2], 2);
1277 buffer.store_complex(out[3], 3);
1278 buffer.store_complex(out[4], 4);
1279 buffer.store_complex(out[5], 5);
1280 }
1281
1282 #[inline(always)]
1283 pub(crate) unsafe fn perform_fft_direct(&self, values: [__m128d; 6]) -> [__m128d; 6] {
1284 let mid0 = self.bf3.perform_fft_direct(values[0], values[2], values[4]);
1288 let mid1 = self.bf3.perform_fft_direct(values[3], values[5], values[1]);
1289
1290 let [output0, output1] = solo_fft2_f64(mid0[0], mid1[0]);
1294 let [output2, output3] = solo_fft2_f64(mid0[1], mid1[1]);
1295 let [output4, output5] = solo_fft2_f64(mid0[2], mid1[2]);
1296
1297 [output0, output3, output4, output1, output2, output5]
1299 }
1300}
1301
1302pub struct SseF32Butterfly8<T> {
1310 root2: __m128,
1311 root2_dual: __m128,
1312 direction: FftDirection,
1313 bf4: SseF32Butterfly4<T>,
1314 rotate90: Rotate90F32,
1315}
1316
1317boilerplate_fft_sse_f32_butterfly!(SseF32Butterfly8, 8, |this: &SseF32Butterfly8<_>| this
1318 .direction);
1319impl<T: FftNum> SseF32Butterfly8<T> {
1320 #[inline(always)]
1321 pub fn new(direction: FftDirection) -> Self {
1322 assert_f32::<T>();
1323 let bf4 = SseF32Butterfly4::new(direction);
1324 let root2 = unsafe { _mm_set_ps(0.5f32.sqrt(), 0.5f32.sqrt(), 1.0, 1.0) };
1325 let root2_dual = unsafe { _mm_load1_ps(&0.5f32.sqrt()) };
1326 let rotate90 = if direction == FftDirection::Inverse {
1327 Rotate90F32::new(true)
1328 } else {
1329 Rotate90F32::new(false)
1330 };
1331 Self {
1332 root2,
1333 root2_dual,
1334 direction,
1335 bf4,
1336 rotate90,
1337 }
1338 }
1339
1340 #[inline(always)]
1341 unsafe fn perform_fft_contiguous(&self, mut buffer: impl SseArrayMut<f32>) {
1342 let input_packed = read_complex_to_array!(buffer, {0, 2, 4, 6});
1343
1344 let out = self.perform_fft_direct(input_packed);
1345
1346 write_complex_to_array_strided!(out, buffer, 2, {0,1,2,3});
1347 }
1348
1349 #[inline(always)]
1350 pub(crate) unsafe fn perform_parallel_fft_contiguous(&self, mut buffer: impl SseArrayMut<f32>) {
1351 let input_packed = read_complex_to_array!(buffer, {0, 2, 4, 6, 8, 10, 12, 14});
1352
1353 let values = interleave_complex_f32!(input_packed, 4, {0, 1, 2, 3});
1354
1355 let out = self.perform_parallel_fft_direct(values);
1356
1357 let out_sorted = separate_interleaved_complex_f32!(out, {0, 2, 4, 6});
1358
1359 write_complex_to_array_strided!(out_sorted, buffer, 2, {0,1,2,3,4,5,6,7});
1360 }
1361
1362 #[inline(always)]
1363 unsafe fn perform_fft_direct(&self, values: [__m128; 4]) -> [__m128; 4] {
1364 let [in02, in13] = transpose_complex_2x2_f32(values[0], values[1]);
1367 let [in46, in57] = transpose_complex_2x2_f32(values[2], values[3]);
1368
1369 let val0 = self.bf4.perform_fft_direct(in02, in46);
1371 let mut val2 = self.bf4.perform_fft_direct(in13, in57);
1372
1373 let val2b = self.rotate90.rotate_hi(val2[0]);
1375 let val2c = _mm_add_ps(val2b, val2[0]);
1376 let val2d = _mm_mul_ps(val2c, self.root2);
1377 val2[0] = extract_lo_hi_f32(val2[0], val2d);
1378
1379 let val3b = self.rotate90.rotate_both(val2[1]);
1380 let val3c = _mm_sub_ps(val3b, val2[1]);
1381 let val3d = _mm_mul_ps(val3c, self.root2);
1382 val2[1] = extract_lo_hi_f32(val3b, val3d);
1383
1384 let out0 = parallel_fft2_interleaved_f32(val0[0], val2[0]);
1388 let out1 = parallel_fft2_interleaved_f32(val0[1], val2[1]);
1389
1390 [out0[0], out1[0], out0[1], out1[1]]
1392 }
1393
1394 #[inline(always)]
1395 unsafe fn perform_parallel_fft_direct(&self, values: [__m128; 8]) -> [__m128; 8] {
1396 let val03 = self
1401 .bf4
1402 .perform_parallel_fft_direct([values[0], values[2], values[4], values[6]]);
1403 let mut val47 = self
1404 .bf4
1405 .perform_parallel_fft_direct([values[1], values[3], values[5], values[7]]);
1406
1407 let val5b = self.rotate90.rotate_both(val47[1]);
1409 let val5c = _mm_add_ps(val5b, val47[1]);
1410 val47[1] = _mm_mul_ps(val5c, self.root2_dual);
1411 val47[2] = self.rotate90.rotate_both(val47[2]);
1412 let val7b = self.rotate90.rotate_both(val47[3]);
1413 let val7c = _mm_sub_ps(val7b, val47[3]);
1414 val47[3] = _mm_mul_ps(val7c, self.root2_dual);
1415
1416 let out0 = parallel_fft2_interleaved_f32(val03[0], val47[0]);
1420 let out1 = parallel_fft2_interleaved_f32(val03[1], val47[1]);
1421 let out2 = parallel_fft2_interleaved_f32(val03[2], val47[2]);
1422 let out3 = parallel_fft2_interleaved_f32(val03[3], val47[3]);
1423
1424 [
1426 out0[0], out1[0], out2[0], out3[0], out0[1], out1[1], out2[1], out3[1],
1427 ]
1428 }
1429}
1430
1431pub struct SseF64Butterfly8<T> {
1439 bf4: SseF64Butterfly4<T>,
1440}
1441
1442boilerplate_fft_sse_f64_butterfly!(SseF64Butterfly8, 8, |this: &SseF64Butterfly8<_>| this
1443 .bf4
1444 .direction);
1445impl<T: FftNum> SseF64Butterfly8<T> {
1446 #[inline(always)]
1447 pub fn new(direction: FftDirection) -> Self {
1448 assert_f64::<T>();
1449 Self {
1450 bf4: SseF64Butterfly4::new(direction),
1451 }
1452 }
1453
1454 #[inline(always)]
1455 unsafe fn perform_fft_contiguous(&self, mut buffer: impl SseArrayMut<f64>) {
1456 let values = read_complex_to_array!(buffer, {0, 1, 2, 3, 4, 5, 6, 7});
1457
1458 let out = self.perform_fft_direct(values);
1459
1460 write_complex_to_array!(out, buffer, {0, 1, 2, 3, 4, 5, 6, 7});
1461 }
1462
1463 #[inline(always)]
1464 unsafe fn perform_fft_direct(&self, values: [__m128d; 8]) -> [__m128d; 8] {
1465 let val03 = self
1470 .bf4
1471 .perform_fft_direct([values[0], values[2], values[4], values[6]]);
1472 let mut val47 = self
1473 .bf4
1474 .perform_fft_direct([values[1], values[3], values[5], values[7]]);
1475
1476 val47[1] = self.bf4.rotate.rotate_45(val47[1]);
1478 val47[2] = self.bf4.rotate.rotate(val47[2]);
1479 val47[3] = self.bf4.rotate.rotate_135(val47[3]);
1480
1481 let out0 = solo_fft2_f64(val03[0], val47[0]);
1485 let out1 = solo_fft2_f64(val03[1], val47[1]);
1486 let out2 = solo_fft2_f64(val03[2], val47[2]);
1487 let out3 = solo_fft2_f64(val03[3], val47[3]);
1488
1489 [
1491 out0[0], out1[0], out2[0], out3[0], out0[1], out1[1], out2[1], out3[1],
1492 ]
1493 }
1494}
1495
1496pub struct SseF32Butterfly9<T> {
1503 direction: FftDirection,
1504 _phantom: std::marker::PhantomData<T>,
1505 bf3: SseF32Butterfly3<T>,
1506 twiddle1: __m128,
1507 twiddle2: __m128,
1508 twiddle4: __m128,
1509}
1510
1511boilerplate_fft_sse_f32_butterfly!(SseF32Butterfly9, 9, |this: &SseF32Butterfly9<_>| this
1512 .direction);
1513impl<T: FftNum> SseF32Butterfly9<T> {
1514 #[inline(always)]
1515 pub fn new(direction: FftDirection) -> Self {
1516 assert_f32::<T>();
1517 let bf3 = SseF32Butterfly3::new(direction);
1518 let tw1: Complex<f32> = twiddles::compute_twiddle(1, 9, direction);
1519 let tw2: Complex<f32> = twiddles::compute_twiddle(2, 9, direction);
1520 let tw4: Complex<f32> = twiddles::compute_twiddle(4, 9, direction);
1521 let twiddle1 = unsafe { _mm_set_ps(tw1.im, tw1.re, tw1.im, tw1.re) };
1522 let twiddle2 = unsafe { _mm_set_ps(tw2.im, tw2.re, tw2.im, tw2.re) };
1523 let twiddle4 = unsafe { _mm_set_ps(tw4.im, tw4.re, tw4.im, tw4.re) };
1524 Self {
1525 direction,
1526 _phantom: std::marker::PhantomData,
1527 bf3,
1528 twiddle1,
1529 twiddle2,
1530 twiddle4,
1531 }
1532 }
1533
1534 #[inline(always)]
1535 pub(crate) unsafe fn perform_fft_contiguous(&self, mut buffer: impl SseArrayMut<f32>) {
1536 let values = read_partial1_complex_to_array!(buffer, {0,1,2,3,4,5,6,7,8});
1538
1539 let out = self.perform_parallel_fft_direct(values);
1540
1541 for n in 0..9 {
1542 buffer.store_partial_lo_complex(out[n], n);
1543 }
1544 }
1545
1546 #[inline(always)]
1547 pub(crate) unsafe fn perform_parallel_fft_contiguous(&self, mut buffer: impl SseArrayMut<f32>) {
1548 let input_packed = read_complex_to_array!(buffer, {0, 2, 4, 6, 8, 10, 12, 14, 16});
1549
1550 let values = [
1551 extract_lo_hi_f32(input_packed[0], input_packed[4]),
1552 extract_hi_lo_f32(input_packed[0], input_packed[5]),
1553 extract_lo_hi_f32(input_packed[1], input_packed[5]),
1554 extract_hi_lo_f32(input_packed[1], input_packed[6]),
1555 extract_lo_hi_f32(input_packed[2], input_packed[6]),
1556 extract_hi_lo_f32(input_packed[2], input_packed[7]),
1557 extract_lo_hi_f32(input_packed[3], input_packed[7]),
1558 extract_hi_lo_f32(input_packed[3], input_packed[8]),
1559 extract_lo_hi_f32(input_packed[4], input_packed[8]),
1560 ];
1561
1562 let out = self.perform_parallel_fft_direct(values);
1563
1564 let out_packed = [
1565 extract_lo_lo_f32(out[0], out[1]),
1566 extract_lo_lo_f32(out[2], out[3]),
1567 extract_lo_lo_f32(out[4], out[5]),
1568 extract_lo_lo_f32(out[6], out[7]),
1569 extract_lo_hi_f32(out[8], out[0]),
1570 extract_hi_hi_f32(out[1], out[2]),
1571 extract_hi_hi_f32(out[3], out[4]),
1572 extract_hi_hi_f32(out[5], out[6]),
1573 extract_hi_hi_f32(out[7], out[8]),
1574 ];
1575
1576 write_complex_to_array_strided!(out_packed, buffer, 2, {0,1,2,3,4,5,6,7,8});
1577 }
1578
1579 #[inline(always)]
1580 pub(crate) unsafe fn perform_parallel_fft_direct(&self, values: [__m128; 9]) -> [__m128; 9] {
1581 let mid0 = self
1585 .bf3
1586 .perform_parallel_fft_direct(values[0], values[3], values[6]);
1587 let mut mid1 = self
1588 .bf3
1589 .perform_parallel_fft_direct(values[1], values[4], values[7]);
1590 let mut mid2 = self
1591 .bf3
1592 .perform_parallel_fft_direct(values[2], values[5], values[8]);
1593
1594 mid1[1] = SseVector::mul_complex(self.twiddle1, mid1[1]);
1596 mid1[2] = SseVector::mul_complex(self.twiddle2, mid1[2]);
1597 mid2[1] = SseVector::mul_complex(self.twiddle2, mid2[1]);
1598 mid2[2] = SseVector::mul_complex(self.twiddle4, mid2[2]);
1599
1600 let [output0, output1, output2] = self
1601 .bf3
1602 .perform_parallel_fft_direct(mid0[0], mid1[0], mid2[0]);
1603 let [output3, output4, output5] = self
1604 .bf3
1605 .perform_parallel_fft_direct(mid0[1], mid1[1], mid2[1]);
1606 let [output6, output7, output8] = self
1607 .bf3
1608 .perform_parallel_fft_direct(mid0[2], mid1[2], mid2[2]);
1609
1610 [
1611 output0, output3, output6, output1, output4, output7, output2, output5, output8,
1612 ]
1613 }
1614}
1615
1616pub struct SseF64Butterfly9<T> {
1624 _phantom: std::marker::PhantomData<T>,
1625 bf3: SseF64Butterfly3<T>,
1626 twiddle1: __m128d,
1627 twiddle2: __m128d,
1628 twiddle4: __m128d,
1629}
1630
1631boilerplate_fft_sse_f64_butterfly!(SseF64Butterfly9, 9, |this: &SseF64Butterfly9<_>| this
1632 .bf3
1633 .direction);
1634impl<T: FftNum> SseF64Butterfly9<T> {
1635 #[inline(always)]
1636 pub fn new(direction: FftDirection) -> Self {
1637 assert_f64::<T>();
1638 let bf3 = SseF64Butterfly3::new(direction);
1639 let tw1: Complex<f64> = twiddles::compute_twiddle(1, 9, direction);
1640 let tw2: Complex<f64> = twiddles::compute_twiddle(2, 9, direction);
1641 let tw4: Complex<f64> = twiddles::compute_twiddle(4, 9, direction);
1642 let twiddle1 = unsafe { _mm_set_pd(tw1.im, tw1.re) };
1643 let twiddle2 = unsafe { _mm_set_pd(tw2.im, tw2.re) };
1644 let twiddle4 = unsafe { _mm_set_pd(tw4.im, tw4.re) };
1645 Self {
1646 _phantom: std::marker::PhantomData,
1647 bf3,
1648 twiddle1,
1649 twiddle2,
1650 twiddle4,
1651 }
1652 }
1653
1654 #[inline(always)]
1655 pub(crate) unsafe fn perform_fft_contiguous(&self, mut buffer: impl SseArrayMut<f64>) {
1656 let values = read_complex_to_array!(buffer, {0, 1, 2, 3, 4, 5, 6, 7, 8});
1657
1658 let out = self.perform_fft_direct(values);
1659
1660 write_complex_to_array!(out, buffer, {0, 1, 2, 3, 4, 5, 6, 7, 8});
1661 }
1662
1663 #[inline(always)]
1664 pub(crate) unsafe fn perform_fft_direct(&self, values: [__m128d; 9]) -> [__m128d; 9] {
1665 let mid0 = self.bf3.perform_fft_direct(values[0], values[3], values[6]);
1669 let mut mid1 = self.bf3.perform_fft_direct(values[1], values[4], values[7]);
1670 let mut mid2 = self.bf3.perform_fft_direct(values[2], values[5], values[8]);
1671
1672 mid1[1] = SseVector::mul_complex(self.twiddle1, mid1[1]);
1674 mid1[2] = SseVector::mul_complex(self.twiddle2, mid1[2]);
1675 mid2[1] = SseVector::mul_complex(self.twiddle2, mid2[1]);
1676 mid2[2] = SseVector::mul_complex(self.twiddle4, mid2[2]);
1677
1678 let [output0, output1, output2] = self.bf3.perform_fft_direct(mid0[0], mid1[0], mid2[0]);
1679 let [output3, output4, output5] = self.bf3.perform_fft_direct(mid0[1], mid1[1], mid2[1]);
1680 let [output6, output7, output8] = self.bf3.perform_fft_direct(mid0[2], mid1[2], mid2[2]);
1681
1682 [
1683 output0, output3, output6, output1, output4, output7, output2, output5, output8,
1684 ]
1685 }
1686}
1687
1688pub struct SseF32Butterfly10<T> {
1696 direction: FftDirection,
1697 _phantom: std::marker::PhantomData<T>,
1698 bf5: SseF32Butterfly5<T>,
1699}
1700
1701boilerplate_fft_sse_f32_butterfly!(SseF32Butterfly10, 10, |this: &SseF32Butterfly10<_>| this
1702 .direction);
1703impl<T: FftNum> SseF32Butterfly10<T> {
1704 #[inline(always)]
1705 pub fn new(direction: FftDirection) -> Self {
1706 assert_f32::<T>();
1707 let bf5 = SseF32Butterfly5::new(direction);
1708 Self {
1709 direction,
1710 _phantom: std::marker::PhantomData,
1711 bf5,
1712 }
1713 }
1714
1715 #[inline(always)]
1716 unsafe fn perform_fft_contiguous(&self, mut buffer: impl SseArrayMut<f32>) {
1717 let input_packed = read_complex_to_array!(buffer, {0, 2, 4, 6, 8});
1718
1719 let out = self.perform_fft_direct(input_packed);
1720
1721 write_complex_to_array_strided!(out, buffer, 2, {0,1,2,3,4});
1722 }
1723
1724 #[inline(always)]
1725 pub(crate) unsafe fn perform_parallel_fft_contiguous(&self, mut buffer: impl SseArrayMut<f32>) {
1726 let input_packed = read_complex_to_array!(buffer, {0, 2, 4, 6, 8, 10, 12, 14, 16, 18});
1727
1728 let values = interleave_complex_f32!(input_packed, 5, {0, 1, 2, 3, 4});
1729
1730 let out = self.perform_parallel_fft_direct(values);
1731
1732 let out_sorted = separate_interleaved_complex_f32!(out, {0, 2, 4, 6, 8});
1733
1734 write_complex_to_array_strided!(out_sorted, buffer, 2, {0,1,2,3,4,5,6,7,8,9});
1735 }
1736
1737 #[inline(always)]
1738 pub(crate) unsafe fn perform_fft_direct(&self, values: [__m128; 5]) -> [__m128; 5] {
1739 let reord0 = extract_lo_hi_f32(values[0], values[2]);
1742 let reord1 = extract_lo_hi_f32(values[1], values[3]);
1743 let reord2 = extract_lo_hi_f32(values[2], values[4]);
1744 let reord3 = extract_lo_hi_f32(values[3], values[0]);
1745 let reord4 = extract_lo_hi_f32(values[4], values[1]);
1746
1747 let mids = self
1749 .bf5
1750 .perform_parallel_fft_direct(reord0, reord1, reord2, reord3, reord4);
1751
1752 let [temp01, temp23] = parallel_fft2_contiguous_f32(mids[0], mids[1]);
1756 let [temp45, temp67] = parallel_fft2_contiguous_f32(mids[2], mids[3]);
1757 let temp89 = solo_fft2_f32(mids[4]);
1758
1759 let out01 = extract_lo_hi_f32(temp01, temp23);
1761 let out23 = extract_lo_hi_f32(temp45, temp67);
1762 let out45 = extract_lo_lo_f32(temp89, temp23);
1763 let out67 = extract_hi_lo_f32(temp01, temp67);
1764 let out89 = extract_hi_hi_f32(temp45, temp89);
1765
1766 [out01, out23, out45, out67, out89]
1767 }
1768
1769 #[inline(always)]
1770 pub(crate) unsafe fn perform_parallel_fft_direct(&self, values: [__m128; 10]) -> [__m128; 10] {
1771 let mid0 = self
1775 .bf5
1776 .perform_parallel_fft_direct(values[0], values[2], values[4], values[6], values[8]);
1777 let mid1 = self
1778 .bf5
1779 .perform_parallel_fft_direct(values[5], values[7], values[9], values[1], values[3]);
1780
1781 let [output0, output1] = parallel_fft2_interleaved_f32(mid0[0], mid1[0]);
1785 let [output2, output3] = parallel_fft2_interleaved_f32(mid0[1], mid1[1]);
1786 let [output4, output5] = parallel_fft2_interleaved_f32(mid0[2], mid1[2]);
1787 let [output6, output7] = parallel_fft2_interleaved_f32(mid0[3], mid1[3]);
1788 let [output8, output9] = parallel_fft2_interleaved_f32(mid0[4], mid1[4]);
1789
1790 [
1792 output0, output3, output4, output7, output8, output1, output2, output5, output6,
1793 output9,
1794 ]
1795 }
1796}
1797
1798pub struct SseF64Butterfly10<T> {
1806 _phantom: std::marker::PhantomData<T>,
1807 bf2: SseF64Butterfly2<T>,
1808 bf5: SseF64Butterfly5<T>,
1809}
1810
1811boilerplate_fft_sse_f64_butterfly!(SseF64Butterfly10, 10, |this: &SseF64Butterfly10<_>| this
1812 .bf5
1813 .direction);
1814impl<T: FftNum> SseF64Butterfly10<T> {
1815 #[inline(always)]
1816 pub fn new(direction: FftDirection) -> Self {
1817 assert_f64::<T>();
1818 let bf2 = SseF64Butterfly2::new(direction);
1819 let bf5 = SseF64Butterfly5::new(direction);
1820 Self {
1821 _phantom: std::marker::PhantomData,
1822 bf2,
1823 bf5,
1824 }
1825 }
1826
1827 #[inline(always)]
1828 pub(crate) unsafe fn perform_fft_contiguous(&self, mut buffer: impl SseArrayMut<f64>) {
1829 let values = read_complex_to_array!(buffer, {0, 1, 2, 3, 4, 5, 6, 7, 8, 9});
1830
1831 let out = self.perform_fft_direct(values);
1832
1833 write_complex_to_array!(out, buffer, {0, 1, 2, 3, 4, 5, 6, 7, 8, 9});
1834 }
1835
1836 #[inline(always)]
1837 pub(crate) unsafe fn perform_fft_direct(&self, values: [__m128d; 10]) -> [__m128d; 10] {
1838 let mid0 = self
1842 .bf5
1843 .perform_fft_direct(values[0], values[2], values[4], values[6], values[8]);
1844 let mid1 = self
1845 .bf5
1846 .perform_fft_direct(values[5], values[7], values[9], values[1], values[3]);
1847
1848 let [output0, output1] = self.bf2.perform_fft_direct(mid0[0], mid1[0]);
1852 let [output2, output3] = self.bf2.perform_fft_direct(mid0[1], mid1[1]);
1853 let [output4, output5] = self.bf2.perform_fft_direct(mid0[2], mid1[2]);
1854 let [output6, output7] = self.bf2.perform_fft_direct(mid0[3], mid1[3]);
1855 let [output8, output9] = self.bf2.perform_fft_direct(mid0[4], mid1[4]);
1856
1857 [
1859 output0, output3, output4, output7, output8, output1, output2, output5, output6,
1860 output9,
1861 ]
1862 }
1863}
1864
1865pub struct SseF32Butterfly12<T> {
1873 direction: FftDirection,
1874 _phantom: std::marker::PhantomData<T>,
1875 bf3: SseF32Butterfly3<T>,
1876 bf4: SseF32Butterfly4<T>,
1877}
1878
1879boilerplate_fft_sse_f32_butterfly!(SseF32Butterfly12, 12, |this: &SseF32Butterfly12<_>| this
1880 .direction);
1881impl<T: FftNum> SseF32Butterfly12<T> {
1882 #[inline(always)]
1883 pub fn new(direction: FftDirection) -> Self {
1884 assert_f32::<T>();
1885 let bf3 = SseF32Butterfly3::new(direction);
1886 let bf4 = SseF32Butterfly4::new(direction);
1887 Self {
1888 direction,
1889 _phantom: std::marker::PhantomData,
1890 bf3,
1891 bf4,
1892 }
1893 }
1894
1895 #[inline(always)]
1896 unsafe fn perform_fft_contiguous(&self, mut buffer: impl SseArrayMut<f32>) {
1897 let input_packed = read_complex_to_array!(buffer, {0, 2, 4, 6, 8, 10 });
1898
1899 let out = self.perform_fft_direct(input_packed);
1900
1901 write_complex_to_array_strided!(out, buffer, 2, {0,1,2,3,4,5});
1902 }
1903
1904 #[inline(always)]
1905 pub(crate) unsafe fn perform_parallel_fft_contiguous(&self, mut buffer: impl SseArrayMut<f32>) {
1906 let input_packed =
1907 read_complex_to_array!(buffer, {0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22});
1908
1909 let values = interleave_complex_f32!(input_packed, 6, {0, 1, 2, 3, 4, 5});
1910
1911 let out = self.perform_parallel_fft_direct(values);
1912
1913 let out_sorted = separate_interleaved_complex_f32!(out, {0, 2, 4, 6, 8, 10});
1914
1915 write_complex_to_array_strided!(out_sorted, buffer, 2, {0,1,2,3,4,5,6,7,8,9, 10, 11});
1916 }
1917
1918 #[inline(always)]
1919 pub(crate) unsafe fn perform_fft_direct(&self, values: [__m128; 6]) -> [__m128; 6] {
1920 let packed03 = extract_lo_hi_f32(values[0], values[1]);
1924 let packed47 = extract_lo_hi_f32(values[2], values[3]);
1925 let packed69 = extract_lo_hi_f32(values[3], values[4]);
1926 let packed101 = extract_lo_hi_f32(values[5], values[0]);
1927 let packed811 = extract_lo_hi_f32(values[4], values[5]);
1928 let packed25 = extract_lo_hi_f32(values[1], values[2]);
1929
1930 let mid0 = self.bf4.perform_fft_direct(packed03, packed69);
1932 let mid1 = self.bf4.perform_fft_direct(packed47, packed101);
1933 let mid2 = self.bf4.perform_fft_direct(packed811, packed25);
1934
1935 let [temp03, temp14, temp25] = self
1939 .bf3
1940 .perform_parallel_fft_direct(mid0[0], mid1[0], mid2[0]);
1941 let [temp69, temp710, temp811] = self
1942 .bf3
1943 .perform_parallel_fft_direct(mid0[1], mid1[1], mid2[1]);
1944
1945 [
1947 extract_lo_hi_f32(temp03, temp14),
1948 extract_lo_hi_f32(temp811, temp69),
1949 extract_lo_hi_f32(temp14, temp25),
1950 extract_lo_hi_f32(temp69, temp710),
1951 extract_lo_hi_f32(temp25, temp03),
1952 extract_lo_hi_f32(temp710, temp811),
1953 ]
1954 }
1955
1956 #[inline(always)]
1957 pub(crate) unsafe fn perform_parallel_fft_direct(&self, values: [__m128; 12]) -> [__m128; 12] {
1958 let mid0 = self
1962 .bf4
1963 .perform_parallel_fft_direct([values[0], values[3], values[6], values[9]]);
1964 let mid1 = self
1965 .bf4
1966 .perform_parallel_fft_direct([values[4], values[7], values[10], values[1]]);
1967 let mid2 = self
1968 .bf4
1969 .perform_parallel_fft_direct([values[8], values[11], values[2], values[5]]);
1970
1971 let [output0, output1, output2] = self
1975 .bf3
1976 .perform_parallel_fft_direct(mid0[0], mid1[0], mid2[0]);
1977 let [output3, output4, output5] = self
1978 .bf3
1979 .perform_parallel_fft_direct(mid0[1], mid1[1], mid2[1]);
1980 let [output6, output7, output8] = self
1981 .bf3
1982 .perform_parallel_fft_direct(mid0[2], mid1[2], mid2[2]);
1983 let [output9, output10, output11] = self
1984 .bf3
1985 .perform_parallel_fft_direct(mid0[3], mid1[3], mid2[3]);
1986
1987 [
1989 output0, output4, output8, output9, output1, output5, output6, output10, output2,
1990 output3, output7, output11,
1991 ]
1992 }
1993}
1994
1995pub struct SseF64Butterfly12<T> {
2003 _phantom: std::marker::PhantomData<T>,
2004 bf3: SseF64Butterfly3<T>,
2005 bf4: SseF64Butterfly4<T>,
2006}
2007
2008boilerplate_fft_sse_f64_butterfly!(SseF64Butterfly12, 12, |this: &SseF64Butterfly12<_>| this
2009 .bf3
2010 .direction);
2011impl<T: FftNum> SseF64Butterfly12<T> {
2012 #[inline(always)]
2013 pub fn new(direction: FftDirection) -> Self {
2014 assert_f64::<T>();
2015 let bf3 = SseF64Butterfly3::new(direction);
2016 let bf4 = SseF64Butterfly4::new(direction);
2017 Self {
2018 _phantom: std::marker::PhantomData,
2019 bf3,
2020 bf4,
2021 }
2022 }
2023
2024 #[inline(always)]
2025 pub(crate) unsafe fn perform_fft_contiguous(&self, mut buffer: impl SseArrayMut<f64>) {
2026 let values = read_complex_to_array!(buffer, {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11});
2027
2028 let out = self.perform_fft_direct(values);
2029
2030 write_complex_to_array!(out, buffer, {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11});
2031 }
2032
2033 #[inline(always)]
2034 pub(crate) unsafe fn perform_fft_direct(&self, values: [__m128d; 12]) -> [__m128d; 12] {
2035 let mid0 = self
2039 .bf4
2040 .perform_fft_direct([values[0], values[3], values[6], values[9]]);
2041 let mid1 = self
2042 .bf4
2043 .perform_fft_direct([values[4], values[7], values[10], values[1]]);
2044 let mid2 = self
2045 .bf4
2046 .perform_fft_direct([values[8], values[11], values[2], values[5]]);
2047
2048 let [output0, output1, output2] = self.bf3.perform_fft_direct(mid0[0], mid1[0], mid2[0]);
2052 let [output3, output4, output5] = self.bf3.perform_fft_direct(mid0[1], mid1[1], mid2[1]);
2053 let [output6, output7, output8] = self.bf3.perform_fft_direct(mid0[2], mid1[2], mid2[2]);
2054 let [output9, output10, output11] = self.bf3.perform_fft_direct(mid0[3], mid1[3], mid2[3]);
2055
2056 [
2057 output0, output4, output8, output9, output1, output5, output6, output10, output2,
2058 output3, output7, output11,
2059 ]
2060 }
2061}
2062
2063pub struct SseF32Butterfly15<T> {
2070 direction: FftDirection,
2071 _phantom: std::marker::PhantomData<T>,
2072 bf3: SseF32Butterfly3<T>,
2073 bf5: SseF32Butterfly5<T>,
2074}
2075
2076boilerplate_fft_sse_f32_butterfly!(SseF32Butterfly15, 15, |this: &SseF32Butterfly15<_>| this
2077 .direction);
2078impl<T: FftNum> SseF32Butterfly15<T> {
2079 #[inline(always)]
2080 pub fn new(direction: FftDirection) -> Self {
2081 assert_f32::<T>();
2082 let bf3 = SseF32Butterfly3::new(direction);
2083 let bf5 = SseF32Butterfly5::new(direction);
2084 Self {
2085 direction,
2086 _phantom: std::marker::PhantomData,
2087 bf3,
2088 bf5,
2089 }
2090 }
2091
2092 #[inline(always)]
2093 pub(crate) unsafe fn perform_fft_contiguous(&self, mut buffer: impl SseArrayMut<f32>) {
2094 let values = read_partial1_complex_to_array!(buffer, {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14});
2096
2097 let out = self.perform_parallel_fft_direct(values);
2098
2099 for n in 0..15 {
2100 buffer.store_partial_lo_complex(out[n], n);
2101 }
2102 }
2103
2104 #[inline(always)]
2105 pub(crate) unsafe fn perform_parallel_fft_contiguous(&self, mut buffer: impl SseArrayMut<f32>) {
2106 let input_packed =
2107 read_complex_to_array!(buffer, {0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28});
2108
2109 let values = [
2110 extract_lo_hi_f32(input_packed[0], input_packed[7]),
2111 extract_hi_lo_f32(input_packed[0], input_packed[8]),
2112 extract_lo_hi_f32(input_packed[1], input_packed[8]),
2113 extract_hi_lo_f32(input_packed[1], input_packed[9]),
2114 extract_lo_hi_f32(input_packed[2], input_packed[9]),
2115 extract_hi_lo_f32(input_packed[2], input_packed[10]),
2116 extract_lo_hi_f32(input_packed[3], input_packed[10]),
2117 extract_hi_lo_f32(input_packed[3], input_packed[11]),
2118 extract_lo_hi_f32(input_packed[4], input_packed[11]),
2119 extract_hi_lo_f32(input_packed[4], input_packed[12]),
2120 extract_lo_hi_f32(input_packed[5], input_packed[12]),
2121 extract_hi_lo_f32(input_packed[5], input_packed[13]),
2122 extract_lo_hi_f32(input_packed[6], input_packed[13]),
2123 extract_hi_lo_f32(input_packed[6], input_packed[14]),
2124 extract_lo_hi_f32(input_packed[7], input_packed[14]),
2125 ];
2126
2127 let out = self.perform_parallel_fft_direct(values);
2128
2129 let out_packed = [
2130 extract_lo_lo_f32(out[0], out[1]),
2131 extract_lo_lo_f32(out[2], out[3]),
2132 extract_lo_lo_f32(out[4], out[5]),
2133 extract_lo_lo_f32(out[6], out[7]),
2134 extract_lo_lo_f32(out[8], out[9]),
2135 extract_lo_lo_f32(out[10], out[11]),
2136 extract_lo_lo_f32(out[12], out[13]),
2137 extract_lo_hi_f32(out[14], out[0]),
2138 extract_hi_hi_f32(out[1], out[2]),
2139 extract_hi_hi_f32(out[3], out[4]),
2140 extract_hi_hi_f32(out[5], out[6]),
2141 extract_hi_hi_f32(out[7], out[8]),
2142 extract_hi_hi_f32(out[9], out[10]),
2143 extract_hi_hi_f32(out[11], out[12]),
2144 extract_hi_hi_f32(out[13], out[14]),
2145 ];
2146
2147 write_complex_to_array_strided!(out_packed, buffer, 2, {0,1,2,3,4,5,6,7,8,9, 10, 11, 12, 13, 14});
2148 }
2149
2150 #[inline(always)]
2151 pub(crate) unsafe fn perform_parallel_fft_direct(&self, values: [__m128; 15]) -> [__m128; 15] {
2152 let mid0 = self
2156 .bf5
2157 .perform_parallel_fft_direct(values[0], values[3], values[6], values[9], values[12]);
2158 let mid1 = self
2159 .bf5
2160 .perform_parallel_fft_direct(values[5], values[8], values[11], values[14], values[2]);
2161 let mid2 = self
2162 .bf5
2163 .perform_parallel_fft_direct(values[10], values[13], values[1], values[4], values[7]);
2164
2165 let [output0, output1, output2] = self
2169 .bf3
2170 .perform_parallel_fft_direct(mid0[0], mid1[0], mid2[0]);
2171 let [output3, output4, output5] = self
2172 .bf3
2173 .perform_parallel_fft_direct(mid0[1], mid1[1], mid2[1]);
2174 let [output6, output7, output8] = self
2175 .bf3
2176 .perform_parallel_fft_direct(mid0[2], mid1[2], mid2[2]);
2177 let [output9, output10, output11] = self
2178 .bf3
2179 .perform_parallel_fft_direct(mid0[3], mid1[3], mid2[3]);
2180 let [output12, output13, output14] = self
2181 .bf3
2182 .perform_parallel_fft_direct(mid0[4], mid1[4], mid2[4]);
2183
2184 [
2185 output0, output4, output8, output9, output13, output2, output3, output7, output11,
2186 output12, output1, output5, output6, output10, output14,
2187 ]
2188 }
2189}
2190
2191pub struct SseF64Butterfly15<T> {
2199 _phantom: std::marker::PhantomData<T>,
2200 bf3: SseF64Butterfly3<T>,
2201 bf5: SseF64Butterfly5<T>,
2202}
2203
2204boilerplate_fft_sse_f64_butterfly!(SseF64Butterfly15, 15, |this: &SseF64Butterfly15<_>| this
2205 .bf3
2206 .direction);
2207impl<T: FftNum> SseF64Butterfly15<T> {
2208 #[inline(always)]
2209 pub fn new(direction: FftDirection) -> Self {
2210 assert_f64::<T>();
2211 let bf3 = SseF64Butterfly3::new(direction);
2212 let bf5 = SseF64Butterfly5::new(direction);
2213 Self {
2214 _phantom: std::marker::PhantomData,
2215 bf3,
2216 bf5,
2217 }
2218 }
2219
2220 #[inline(always)]
2221 pub(crate) unsafe fn perform_fft_contiguous(&self, mut buffer: impl SseArrayMut<f64>) {
2222 let values =
2223 read_complex_to_array!(buffer, {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14});
2224
2225 let out = self.perform_fft_direct(values);
2226
2227 write_complex_to_array!(out, buffer, {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14});
2228 }
2229
2230 #[inline(always)]
2231 pub(crate) unsafe fn perform_fft_direct(&self, values: [__m128d; 15]) -> [__m128d; 15] {
2232 let mid0 = self
2236 .bf5
2237 .perform_fft_direct(values[0], values[3], values[6], values[9], values[12]);
2238 let mid1 = self
2239 .bf5
2240 .perform_fft_direct(values[5], values[8], values[11], values[14], values[2]);
2241 let mid2 = self
2242 .bf5
2243 .perform_fft_direct(values[10], values[13], values[1], values[4], values[7]);
2244
2245 let [output0, output1, output2] = self.bf3.perform_fft_direct(mid0[0], mid1[0], mid2[0]);
2249 let [output3, output4, output5] = self.bf3.perform_fft_direct(mid0[1], mid1[1], mid2[1]);
2250 let [output6, output7, output8] = self.bf3.perform_fft_direct(mid0[2], mid1[2], mid2[2]);
2251 let [output9, output10, output11] = self.bf3.perform_fft_direct(mid0[3], mid1[3], mid2[3]);
2252 let [output12, output13, output14] = self.bf3.perform_fft_direct(mid0[4], mid1[4], mid2[4]);
2253
2254 [
2255 output0, output4, output8, output9, output13, output2, output3, output7, output11,
2256 output12, output1, output5, output6, output10, output14,
2257 ]
2258 }
2259}
2260
2261pub struct SseF32Butterfly16<T> {
2269 bf4: SseF32Butterfly4<T>,
2270 twiddles_packed: [__m128; 6],
2271 twiddle1: __m128,
2272 twiddle3: __m128,
2273 twiddle9: __m128,
2274}
2275
2276boilerplate_fft_sse_f32_butterfly_noparallel!(SseF32Butterfly16, 16, |this: &SseF32Butterfly16<
2277 _,
2278>| this.bf4.direction);
2279impl<T: FftNum> SseF32Butterfly16<T> {
2280 pub fn new(direction: FftDirection) -> Self {
2281 assert_f32::<T>();
2282 let tw0: Complex<f32> = Complex { re: 1.0, im: 0.0 };
2283 let tw1: Complex<f32> = twiddles::compute_twiddle(1, 16, direction);
2284 let tw2: Complex<f32> = twiddles::compute_twiddle(2, 16, direction);
2285 let tw3: Complex<f32> = twiddles::compute_twiddle(3, 16, direction);
2286 let tw4: Complex<f32> = twiddles::compute_twiddle(4, 16, direction);
2287 let tw6: Complex<f32> = twiddles::compute_twiddle(6, 16, direction);
2288 let tw9: Complex<f32> = twiddles::compute_twiddle(9, 16, direction);
2289
2290 unsafe {
2291 Self {
2292 bf4: SseF32Butterfly4::new(direction),
2293 twiddles_packed: [
2294 pack_32(tw0, tw1),
2295 pack_32(tw0, tw2),
2296 pack_32(tw0, tw3),
2297 pack_32(tw2, tw3),
2298 pack_32(tw4, tw6),
2299 pack_32(tw6, tw9),
2300 ],
2301 twiddle1: pack_32(tw1, tw1),
2302 twiddle3: pack_32(tw3, tw3),
2303 twiddle9: pack_32(tw9, tw9),
2304 }
2305 }
2306 }
2307
2308 #[inline(always)]
2309 unsafe fn perform_fft_contiguous(&self, mut buffer: impl SseArrayMut<f32>) {
2310 let load = |i| {
2318 [
2319 buffer.load_complex(i),
2320 buffer.load_complex(i + 4),
2321 buffer.load_complex(i + 8),
2322 buffer.load_complex(i + 12),
2323 ]
2324 };
2325
2326 let mut tmp0 = self.bf4.perform_parallel_fft_direct(load(0));
2328 tmp0[1] = SseVector::mul_complex(tmp0[1], self.twiddles_packed[0]);
2329 tmp0[2] = SseVector::mul_complex(tmp0[2], self.twiddles_packed[1]);
2330 tmp0[3] = SseVector::mul_complex(tmp0[3], self.twiddles_packed[2]);
2331 let [mid0, mid1] = transpose_complex_2x2_f32(tmp0[0], tmp0[1]);
2332 let [mid4, mid5] = transpose_complex_2x2_f32(tmp0[2], tmp0[3]);
2333
2334 let mut tmp1 = self.bf4.perform_parallel_fft_direct(load(2));
2335 tmp1[1] = SseVector::mul_complex(tmp1[1], self.twiddles_packed[3]);
2336 tmp1[2] = SseVector::mul_complex(tmp1[2], self.twiddles_packed[4]);
2337 tmp1[3] = SseVector::mul_complex(tmp1[3], self.twiddles_packed[5]);
2338 let [mid2, mid3] = transpose_complex_2x2_f32(tmp1[0], tmp1[1]);
2339 let [mid6, mid7] = transpose_complex_2x2_f32(tmp1[2], tmp1[3]);
2340
2341 let mut store = |i: usize, vectors: [__m128; 4]| {
2343 buffer.store_complex(vectors[0], i + 0);
2344 buffer.store_complex(vectors[1], i + 4);
2345 buffer.store_complex(vectors[2], i + 8);
2346 buffer.store_complex(vectors[3], i + 12);
2347 };
2348 let out0 = self
2350 .bf4
2351 .perform_parallel_fft_direct([mid0, mid1, mid2, mid3]);
2352 store(0, out0);
2353
2354 let out1 = self
2355 .bf4
2356 .perform_parallel_fft_direct([mid4, mid5, mid6, mid7]);
2357 store(2, out1);
2358 }
2359
2360 #[allow(unused)]
2362 pub(crate) unsafe fn perform_parallel_fft_contiguous(&self, mut buffer: impl SseArrayMut<f32>) {
2363 let load = |i: usize| {
2371 let [a0, a1] =
2372 transpose_complex_2x2_f32(buffer.load_complex(i + 0), buffer.load_complex(i + 16));
2373 let [b0, b1] =
2374 transpose_complex_2x2_f32(buffer.load_complex(i + 4), buffer.load_complex(i + 20));
2375 let [c0, c1] =
2376 transpose_complex_2x2_f32(buffer.load_complex(i + 8), buffer.load_complex(i + 24));
2377 let [d0, d1] =
2378 transpose_complex_2x2_f32(buffer.load_complex(i + 12), buffer.load_complex(i + 28));
2379 [[a0, b0, c0, d0], [a1, b1, c1, d1]]
2380 };
2381
2382 let [in2, in3] = load(2);
2384 let mut tmp2 = self.bf4.perform_parallel_fft_direct(in2);
2385 let mut tmp3 = self.bf4.perform_parallel_fft_direct(in3);
2386 tmp2[1] = self.bf4.rotate.rotate_both_45(tmp2[1]);
2387 tmp2[2] = self.bf4.rotate.rotate_both(tmp2[2]);
2388 tmp2[3] = self.bf4.rotate.rotate_both_135(tmp2[3]);
2389 tmp3[1] = SseVector::mul_complex(tmp3[1], self.twiddle3);
2390 tmp3[2] = self.bf4.rotate.rotate_both_135(tmp3[2]);
2391 tmp3[3] = SseVector::mul_complex(tmp3[3], self.twiddle9);
2392
2393 let [in0, in1] = load(0);
2395 let tmp0 = self.bf4.perform_parallel_fft_direct(in0);
2396 let mut tmp1 = self.bf4.perform_parallel_fft_direct(in1);
2397 tmp1[1] = SseVector::mul_complex(tmp1[1], self.twiddle1);
2398 tmp1[2] = self.bf4.rotate.rotate_both_45(tmp1[2]);
2399 tmp1[3] = SseVector::mul_complex(tmp1[3], self.twiddle3);
2400
2401 let mut store = |i, values_a: [__m128; 4], values_b: [__m128; 4]| {
2403 for n in 0..4 {
2404 let [a, b] = transpose_complex_2x2_f32(values_a[n], values_b[n]);
2405 buffer.store_complex(a, i + n * 4);
2406 buffer.store_complex(b, i + n * 4 + 16);
2407 }
2408 };
2409 let out0 = self
2411 .bf4
2412 .perform_parallel_fft_direct([tmp0[0], tmp1[0], tmp2[0], tmp3[0]]);
2413 let out1 = self
2414 .bf4
2415 .perform_parallel_fft_direct([tmp0[1], tmp1[1], tmp2[1], tmp3[1]]);
2416 store(0, out0, out1);
2417
2418 let out2 = self
2419 .bf4
2420 .perform_parallel_fft_direct([tmp0[2], tmp1[2], tmp2[2], tmp3[2]]);
2421 let out3 = self
2422 .bf4
2423 .perform_parallel_fft_direct([tmp0[3], tmp1[3], tmp2[3], tmp3[3]]);
2424 store(2, out2, out3);
2425 }
2426}
2427pub struct SseF64Butterfly16<T> {
2435 bf4: SseF64Butterfly4<T>,
2436 twiddle1: __m128d,
2437 twiddle3: __m128d,
2438 twiddle9: __m128d,
2439}
2440
2441boilerplate_fft_sse_f64_butterfly!(SseF64Butterfly16, 16, |this: &SseF64Butterfly16<_>| this
2442 .bf4
2443 .direction);
2444impl<T: FftNum> SseF64Butterfly16<T> {
2445 #[inline(always)]
2446 pub fn new(direction: FftDirection) -> Self {
2447 assert_f64::<T>();
2448 let tw1: Complex<f64> = twiddles::compute_twiddle(1, 16, direction);
2449 let tw3: Complex<f64> = twiddles::compute_twiddle(3, 16, direction);
2450 let tw9: Complex<f64> = twiddles::compute_twiddle(9, 16, direction);
2451
2452 unsafe {
2453 Self {
2454 bf4: SseF64Butterfly4::new(direction),
2455 twiddle1: pack_64(tw1),
2456 twiddle3: pack_64(tw3),
2457 twiddle9: pack_64(tw9),
2458 }
2459 }
2460 }
2461
2462 #[inline(always)]
2463 unsafe fn perform_fft_contiguous(&self, mut buffer: impl SseArrayMut<f64>) {
2464 let load = |i| {
2472 [
2473 buffer.load_complex(i),
2474 buffer.load_complex(i + 4),
2475 buffer.load_complex(i + 8),
2476 buffer.load_complex(i + 12),
2477 ]
2478 };
2479
2480 let mut tmp1 = self.bf4.perform_fft_direct(load(1));
2482 tmp1[1] = SseVector::mul_complex(tmp1[1], self.twiddle1);
2483 tmp1[2] = self.bf4.rotate.rotate_45(tmp1[2]);
2484 tmp1[3] = SseVector::mul_complex(tmp1[3], self.twiddle3);
2485
2486 let mut tmp3 = self.bf4.perform_fft_direct(load(3));
2487 tmp3[1] = SseVector::mul_complex(tmp3[1], self.twiddle3);
2488 tmp3[2] = self.bf4.rotate.rotate_135(tmp3[2]);
2489 tmp3[3] = SseVector::mul_complex(tmp3[3], self.twiddle9);
2490
2491 let mut tmp2 = self.bf4.perform_fft_direct(load(2));
2492 tmp2[1] = self.bf4.rotate.rotate_45(tmp2[1]);
2493 tmp2[2] = self.bf4.rotate.rotate(tmp2[2]);
2494 tmp2[3] = self.bf4.rotate.rotate_135(tmp2[3]);
2495
2496 let tmp0 = self.bf4.perform_fft_direct(load(0));
2498
2499 let mut store = |i: usize, vectors: [__m128d; 4]| {
2501 buffer.store_complex(vectors[0], i + 0);
2502 buffer.store_complex(vectors[1], i + 4);
2503 buffer.store_complex(vectors[2], i + 8);
2504 buffer.store_complex(vectors[3], i + 12);
2505 };
2506
2507 let out0 = self
2509 .bf4
2510 .perform_fft_direct([tmp0[0], tmp1[0], tmp2[0], tmp3[0]]);
2511 store(0, out0);
2512
2513 let out1 = self
2514 .bf4
2515 .perform_fft_direct([tmp0[1], tmp1[1], tmp2[1], tmp3[1]]);
2516 store(1, out1);
2517
2518 let out2 = self
2519 .bf4
2520 .perform_fft_direct([tmp0[2], tmp1[2], tmp2[2], tmp3[2]]);
2521 store(2, out2);
2522
2523 let out3 = self
2524 .bf4
2525 .perform_fft_direct([tmp0[3], tmp1[3], tmp2[3], tmp3[3]]);
2526 store(3, out3);
2527 }
2528}
2529
2530pub struct SseF32Butterfly24<T> {
2538 bf4: SseF32Butterfly4<T>,
2539 bf6: SseF32Butterfly6<T>,
2540 twiddles_packed: [__m128; 9],
2541 twiddle1: __m128,
2542 twiddle2: __m128,
2543 twiddle4: __m128,
2544 twiddle5: __m128,
2545 twiddle8: __m128,
2546 twiddle10: __m128,
2547}
2548
2549boilerplate_fft_sse_f32_butterfly!(SseF32Butterfly24, 24, |this: &SseF32Butterfly24<_>| this
2550 .bf4
2551 .direction);
2552impl<T: FftNum> SseF32Butterfly24<T> {
2553 #[inline(always)]
2554 pub fn new(direction: FftDirection) -> Self {
2555 assert_f32::<T>();
2556 let tw0: Complex<f32> = Complex { re: 1.0, im: 0.0 };
2557 let tw1: Complex<f32> = twiddles::compute_twiddle(1, 24, direction);
2558 let tw2: Complex<f32> = twiddles::compute_twiddle(2, 24, direction);
2559 let tw3: Complex<f32> = twiddles::compute_twiddle(3, 24, direction);
2560 let tw4: Complex<f32> = twiddles::compute_twiddle(4, 24, direction);
2561 let tw5: Complex<f32> = twiddles::compute_twiddle(5, 24, direction);
2562 let tw6: Complex<f32> = twiddles::compute_twiddle(6, 24, direction);
2563 let tw8: Complex<f32> = twiddles::compute_twiddle(8, 24, direction);
2564 let tw9: Complex<f32> = twiddles::compute_twiddle(9, 24, direction);
2565 let tw10: Complex<f32> = twiddles::compute_twiddle(10, 24, direction);
2566 let tw12: Complex<f32> = twiddles::compute_twiddle(12, 24, direction);
2567 let tw15: Complex<f32> = twiddles::compute_twiddle(15, 24, direction);
2568 unsafe {
2569 Self {
2570 bf4: SseF32Butterfly4::new(direction),
2571 bf6: SseF32Butterfly6::new(direction),
2572 twiddles_packed: [
2573 pack_32(tw0, tw1),
2574 pack_32(tw0, tw2),
2575 pack_32(tw0, tw3),
2576 pack_32(tw2, tw3),
2577 pack_32(tw4, tw6),
2578 pack_32(tw6, tw9),
2579 pack_32(tw4, tw5),
2580 pack_32(tw8, tw10),
2581 pack_32(tw12, tw15),
2582 ],
2583 twiddle1: pack_32(tw1, tw1),
2584 twiddle2: pack_32(tw2, tw2),
2585 twiddle4: pack_32(tw4, tw4),
2586 twiddle5: pack_32(tw5, tw5),
2587 twiddle8: pack_32(tw8, tw8),
2588 twiddle10: pack_32(tw10, tw10),
2589 }
2590 }
2591 }
2592
2593 #[inline(always)]
2594 unsafe fn perform_fft_contiguous(&self, mut buffer: impl SseArrayMut<f32>) {
2595 let load = |i| {
2603 [
2604 buffer.load_complex(i),
2605 buffer.load_complex(i + 6),
2606 buffer.load_complex(i + 12),
2607 buffer.load_complex(i + 18),
2608 ]
2609 };
2610
2611 let mut tmp1 = self.bf4.perform_parallel_fft_direct(load(2));
2613 tmp1[1] = SseVector::mul_complex(tmp1[1], self.twiddles_packed[3]);
2614 tmp1[2] = SseVector::mul_complex(tmp1[2], self.twiddles_packed[4]);
2615 tmp1[3] = SseVector::mul_complex(tmp1[3], self.twiddles_packed[5]);
2616 let [mid2, mid3] = transpose_complex_2x2_f32(tmp1[0], tmp1[1]);
2617 let [mid8, mid9] = transpose_complex_2x2_f32(tmp1[2], tmp1[3]);
2618
2619 let mut tmp2 = self.bf4.perform_parallel_fft_direct(load(4));
2620 tmp2[1] = SseVector::mul_complex(tmp2[1], self.twiddles_packed[6]);
2621 tmp2[2] = SseVector::mul_complex(tmp2[2], self.twiddles_packed[7]);
2622 tmp2[3] = SseVector::mul_complex(tmp2[3], self.twiddles_packed[8]);
2623 let [mid4, mid5] = transpose_complex_2x2_f32(tmp2[0], tmp2[1]);
2624 let [mid10, mid11] = transpose_complex_2x2_f32(tmp2[2], tmp2[3]);
2625
2626 let mut tmp0 = self.bf4.perform_parallel_fft_direct(load(0));
2627 tmp0[1] = SseVector::mul_complex(tmp0[1], self.twiddles_packed[0]);
2628 tmp0[2] = SseVector::mul_complex(tmp0[2], self.twiddles_packed[1]);
2629 tmp0[3] = SseVector::mul_complex(tmp0[3], self.twiddles_packed[2]);
2630 let [mid0, mid1] = transpose_complex_2x2_f32(tmp0[0], tmp0[1]);
2631 let [mid6, mid7] = transpose_complex_2x2_f32(tmp0[2], tmp0[3]);
2632
2633 let mut store = |i, vectors: [__m128; 6]| {
2635 buffer.store_complex(vectors[0], i);
2636 buffer.store_complex(vectors[1], i + 4);
2637 buffer.store_complex(vectors[2], i + 8);
2638 buffer.store_complex(vectors[3], i + 12);
2639 buffer.store_complex(vectors[4], i + 16);
2640 buffer.store_complex(vectors[5], i + 20);
2641 };
2642
2643 let out0 = self
2645 .bf6
2646 .perform_parallel_fft_direct(mid0, mid1, mid2, mid3, mid4, mid5);
2647 store(0, out0);
2648
2649 let out1 = self
2650 .bf6
2651 .perform_parallel_fft_direct(mid6, mid7, mid8, mid9, mid10, mid11);
2652 store(2, out1);
2653 }
2654
2655 #[inline(always)]
2656 pub(crate) unsafe fn perform_parallel_fft_contiguous(&self, mut buffer: impl SseArrayMut<f32>) {
2657 let load = |i: usize| {
2665 let [a0, a1] =
2666 transpose_complex_2x2_f32(buffer.load_complex(i + 0), buffer.load_complex(i + 24));
2667 let [b0, b1] =
2668 transpose_complex_2x2_f32(buffer.load_complex(i + 6), buffer.load_complex(i + 30));
2669 let [c0, c1] =
2670 transpose_complex_2x2_f32(buffer.load_complex(i + 12), buffer.load_complex(i + 36));
2671 let [d0, d1] =
2672 transpose_complex_2x2_f32(buffer.load_complex(i + 18), buffer.load_complex(i + 42));
2673 [[a0, b0, c0, d0], [a1, b1, c1, d1]]
2674 };
2675
2676 let [in0, in1] = load(0);
2678 let tmp0 = self.bf4.perform_parallel_fft_direct(in0);
2679 let mut tmp1 = self.bf4.perform_parallel_fft_direct(in1);
2680 tmp1[1] = SseVector::mul_complex(tmp1[1], self.twiddle1);
2681 tmp1[2] = SseVector::mul_complex(tmp1[2], self.twiddle2);
2682 tmp1[3] = self.bf4.rotate.rotate_both_45(tmp1[3]);
2683
2684 let [in2, in3] = load(2);
2685 let mut tmp2 = self.bf4.perform_parallel_fft_direct(in2);
2686 let mut tmp3 = self.bf4.perform_parallel_fft_direct(in3);
2687 tmp2[1] = SseVector::mul_complex(tmp2[1], self.twiddle2);
2688 tmp2[2] = SseVector::mul_complex(tmp2[2], self.twiddle4);
2689 tmp2[3] = self.bf4.rotate.rotate_both(tmp2[3]);
2690 tmp3[1] = self.bf4.rotate.rotate_both_45(tmp3[1]);
2691 tmp3[2] = self.bf4.rotate.rotate_both(tmp3[2]);
2692 tmp3[3] = self.bf4.rotate.rotate_both_135(tmp3[3]);
2693
2694 let [in4, in5] = load(4);
2695 let mut tmp4 = self.bf4.perform_parallel_fft_direct(in4);
2696 let mut tmp5 = self.bf4.perform_parallel_fft_direct(in5);
2697 tmp4[1] = SseVector::mul_complex(tmp4[1], self.twiddle4);
2698 tmp4[2] = SseVector::mul_complex(tmp4[2], self.twiddle8);
2699 tmp4[3] = SseVector::neg(tmp4[3]);
2700 tmp5[1] = SseVector::mul_complex(tmp5[1], self.twiddle5);
2701 tmp5[2] = SseVector::mul_complex(tmp5[2], self.twiddle10);
2702 tmp5[3] = self.bf4.rotate.rotate_both_225(tmp5[3]);
2703
2704 let mut store = |i, vectors_a: [__m128; 6], vectors_b: [__m128; 6]| {
2706 for n in 0..6 {
2707 let [a, b] = transpose_complex_2x2_f32(vectors_a[n], vectors_b[n]);
2708 buffer.store_complex(a, i + n * 4);
2709 buffer.store_complex(b, i + n * 4 + 24);
2710 }
2711 };
2712
2713 let out0 = self
2715 .bf6
2716 .perform_parallel_fft_direct(tmp0[0], tmp1[0], tmp2[0], tmp3[0], tmp4[0], tmp5[0]);
2717 let out1 = self
2718 .bf6
2719 .perform_parallel_fft_direct(tmp0[1], tmp1[1], tmp2[1], tmp3[1], tmp4[1], tmp5[1]);
2720 store(0, out0, out1);
2721
2722 let out2 = self
2723 .bf6
2724 .perform_parallel_fft_direct(tmp0[2], tmp1[2], tmp2[2], tmp3[2], tmp4[2], tmp5[2]);
2725 let out3 = self
2726 .bf6
2727 .perform_parallel_fft_direct(tmp0[3], tmp1[3], tmp2[3], tmp3[3], tmp4[3], tmp5[3]);
2728 store(2, out2, out3);
2729 }
2730}
2731
2732pub struct SseF64Butterfly24<T> {
2740 bf4: SseF64Butterfly4<T>,
2741 bf6: SseF64Butterfly6<T>,
2742 twiddle1: __m128d,
2743 twiddle2: __m128d,
2744 twiddle4: __m128d,
2745 twiddle5: __m128d,
2746 twiddle8: __m128d,
2747 twiddle10: __m128d,
2748}
2749
2750boilerplate_fft_sse_f64_butterfly!(SseF64Butterfly24, 24, |this: &SseF64Butterfly24<_>| this
2751 .bf4
2752 .direction);
2753impl<T: FftNum> SseF64Butterfly24<T> {
2754 #[inline(always)]
2755 pub fn new(direction: FftDirection) -> Self {
2756 assert_f64::<T>();
2757 let tw1: Complex<f64> = twiddles::compute_twiddle(1, 24, direction);
2758 let tw2: Complex<f64> = twiddles::compute_twiddle(2, 24, direction);
2759 let tw4: Complex<f64> = twiddles::compute_twiddle(4, 24, direction);
2760 let tw5: Complex<f64> = twiddles::compute_twiddle(5, 24, direction);
2761 let tw8: Complex<f64> = twiddles::compute_twiddle(8, 24, direction);
2762 let tw10: Complex<f64> = twiddles::compute_twiddle(10, 24, direction);
2763
2764 unsafe {
2765 Self {
2766 bf4: SseF64Butterfly4::new(direction),
2767 bf6: SseF64Butterfly6::new(direction),
2768 twiddle1: pack_64(tw1),
2769 twiddle2: pack_64(tw2),
2770 twiddle4: pack_64(tw4),
2771 twiddle5: pack_64(tw5),
2772 twiddle8: pack_64(tw8),
2773 twiddle10: pack_64(tw10),
2774 }
2775 }
2776 }
2777
2778 #[inline(always)]
2779 unsafe fn perform_fft_contiguous(&self, mut buffer: impl SseArrayMut<f64>) {
2780 let load = |i| {
2788 [
2789 buffer.load_complex(i),
2790 buffer.load_complex(i + 6),
2791 buffer.load_complex(i + 12),
2792 buffer.load_complex(i + 18),
2793 ]
2794 };
2795
2796 let mut tmp1 = self.bf4.perform_fft_direct(load(1));
2798 tmp1[1] = SseVector::mul_complex(tmp1[1], self.twiddle1);
2799 tmp1[2] = SseVector::mul_complex(tmp1[2], self.twiddle2);
2800 tmp1[3] = self.bf4.rotate.rotate_45(tmp1[3]);
2801
2802 let mut tmp2 = self.bf4.perform_fft_direct(load(2));
2803 tmp2[1] = SseVector::mul_complex(tmp2[1], self.twiddle2);
2804 tmp2[2] = SseVector::mul_complex(tmp2[2], self.twiddle4);
2805 tmp2[3] = self.bf4.rotate.rotate(tmp2[3]);
2806
2807 let mut tmp4 = self.bf4.perform_fft_direct(load(4));
2808 tmp4[1] = SseVector::mul_complex(tmp4[1], self.twiddle4);
2809 tmp4[2] = SseVector::mul_complex(tmp4[2], self.twiddle8);
2810 tmp4[3] = SseVector::neg(tmp4[3]);
2811
2812 let mut tmp5 = self.bf4.perform_fft_direct(load(5));
2813 tmp5[1] = SseVector::mul_complex(tmp5[1], self.twiddle5);
2814 tmp5[2] = SseVector::mul_complex(tmp5[2], self.twiddle10);
2815 tmp5[3] = self.bf4.rotate.rotate_225(tmp5[3]);
2816
2817 let mut tmp3 = self.bf4.perform_fft_direct(load(3));
2818 tmp3[1] = self.bf4.rotate.rotate_45(tmp3[1]);
2819 tmp3[2] = self.bf4.rotate.rotate(tmp3[2]);
2820 tmp3[3] = self.bf4.rotate.rotate_135(tmp3[3]);
2821
2822 let tmp0 = self.bf4.perform_fft_direct(load(0));
2824
2825 let mut store = |i, vectors: [__m128d; 6]| {
2827 buffer.store_complex(vectors[0], i);
2828 buffer.store_complex(vectors[1], i + 4);
2829 buffer.store_complex(vectors[2], i + 8);
2830 buffer.store_complex(vectors[3], i + 12);
2831 buffer.store_complex(vectors[4], i + 16);
2832 buffer.store_complex(vectors[5], i + 20);
2833 };
2834
2835 let out0 = self
2837 .bf6
2838 .perform_fft_direct([tmp0[0], tmp1[0], tmp2[0], tmp3[0], tmp4[0], tmp5[0]]);
2839 store(0, out0);
2840
2841 let out1 = self
2842 .bf6
2843 .perform_fft_direct([tmp0[1], tmp1[1], tmp2[1], tmp3[1], tmp4[1], tmp5[1]]);
2844 store(1, out1);
2845
2846 let out2 = self
2847 .bf6
2848 .perform_fft_direct([tmp0[2], tmp1[2], tmp2[2], tmp3[2], tmp4[2], tmp5[2]]);
2849 store(2, out2);
2850
2851 let out3 = self
2852 .bf6
2853 .perform_fft_direct([tmp0[3], tmp1[3], tmp2[3], tmp3[3], tmp4[3], tmp5[3]]);
2854 store(3, out3);
2855 }
2856}
2857
2858pub struct SseF32Butterfly32<T> {
2866 bf8: SseF32Butterfly8<T>,
2867 twiddles_packed: [__m128; 12],
2868 twiddle1: __m128,
2869 twiddle2: __m128,
2870 twiddle3: __m128,
2871 twiddle5: __m128,
2872 twiddle6: __m128,
2873 twiddle7: __m128,
2874 twiddle9: __m128,
2875 twiddle10: __m128,
2876 twiddle14: __m128,
2877 twiddle15: __m128,
2878 twiddle18: __m128,
2879 twiddle21: __m128,
2880}
2881
2882boilerplate_fft_sse_f32_butterfly!(SseF32Butterfly32, 32, |this: &SseF32Butterfly32<_>| this
2883 .bf8
2884 .bf4
2885 .direction);
2886impl<T: FftNum> SseF32Butterfly32<T> {
2887 #[inline(always)]
2888 pub fn new(direction: FftDirection) -> Self {
2889 assert_f32::<T>();
2890 let tw0: Complex<f32> = Complex { re: 1.0, im: 0.0 };
2891 let tw1: Complex<f32> = twiddles::compute_twiddle(1, 32, direction);
2892 let tw2: Complex<f32> = twiddles::compute_twiddle(2, 32, direction);
2893 let tw3: Complex<f32> = twiddles::compute_twiddle(3, 32, direction);
2894 let tw4: Complex<f32> = twiddles::compute_twiddle(4, 32, direction);
2895 let tw5: Complex<f32> = twiddles::compute_twiddle(5, 32, direction);
2896 let tw6: Complex<f32> = twiddles::compute_twiddle(6, 32, direction);
2897 let tw7: Complex<f32> = twiddles::compute_twiddle(7, 32, direction);
2898 let tw8: Complex<f32> = twiddles::compute_twiddle(8, 32, direction);
2899 let tw9: Complex<f32> = twiddles::compute_twiddle(9, 32, direction);
2900 let tw10: Complex<f32> = twiddles::compute_twiddle(10, 32, direction);
2901 let tw12: Complex<f32> = twiddles::compute_twiddle(12, 32, direction);
2902 let tw14: Complex<f32> = twiddles::compute_twiddle(14, 32, direction);
2903 let tw15: Complex<f32> = twiddles::compute_twiddle(15, 32, direction);
2904 let tw18: Complex<f32> = twiddles::compute_twiddle(18, 32, direction);
2905 let tw21: Complex<f32> = twiddles::compute_twiddle(21, 32, direction);
2906 unsafe {
2907 Self {
2908 bf8: SseF32Butterfly8::new(direction),
2909 twiddles_packed: [
2910 pack_32(tw0, tw1),
2911 pack_32(tw0, tw2),
2912 pack_32(tw0, tw3),
2913 pack_32(tw2, tw3),
2914 pack_32(tw4, tw6),
2915 pack_32(tw6, tw9),
2916 pack_32(tw4, tw5),
2917 pack_32(tw8, tw10),
2918 pack_32(tw12, tw15),
2919 pack_32(tw6, tw7),
2920 pack_32(tw12, tw14),
2921 pack_32(tw18, tw21),
2922 ],
2923 twiddle1: pack_32(tw1, tw1),
2924 twiddle2: pack_32(tw2, tw2),
2925 twiddle3: pack_32(tw3, tw3),
2926 twiddle5: pack_32(tw5, tw5),
2927 twiddle6: pack_32(tw6, tw6),
2928 twiddle7: pack_32(tw7, tw7),
2929 twiddle9: pack_32(tw9, tw9),
2930 twiddle10: pack_32(tw10, tw10),
2931 twiddle14: pack_32(tw14, tw14),
2932 twiddle15: pack_32(tw15, tw15),
2933 twiddle18: pack_32(tw18, tw18),
2934 twiddle21: pack_32(tw21, tw21),
2935 }
2936 }
2937 }
2938
2939 #[inline(always)]
2940 unsafe fn perform_fft_contiguous(&self, mut buffer: impl SseArrayMut<f32>) {
2941 let load = |i| {
2949 [
2950 buffer.load_complex(i),
2951 buffer.load_complex(i + 8),
2952 buffer.load_complex(i + 16),
2953 buffer.load_complex(i + 24),
2954 ]
2955 };
2956
2957 let mut tmp0 = self.bf8.bf4.perform_parallel_fft_direct(load(0));
2959 tmp0[1] = SseVector::mul_complex(tmp0[1], self.twiddles_packed[0]);
2960 tmp0[2] = SseVector::mul_complex(tmp0[2], self.twiddles_packed[1]);
2961 tmp0[3] = SseVector::mul_complex(tmp0[3], self.twiddles_packed[2]);
2962 let [mid0, mid1] = transpose_complex_2x2_f32(tmp0[0], tmp0[1]);
2963 let [mid8, mid9] = transpose_complex_2x2_f32(tmp0[2], tmp0[3]);
2964
2965 let mut tmp1 = self.bf8.bf4.perform_parallel_fft_direct(load(2));
2966 tmp1[1] = SseVector::mul_complex(tmp1[1], self.twiddles_packed[3]);
2967 tmp1[2] = SseVector::mul_complex(tmp1[2], self.twiddles_packed[4]);
2968 tmp1[3] = SseVector::mul_complex(tmp1[3], self.twiddles_packed[5]);
2969 let [mid2, mid3] = transpose_complex_2x2_f32(tmp1[0], tmp1[1]);
2970 let [mid10, mid11] = transpose_complex_2x2_f32(tmp1[2], tmp1[3]);
2971
2972 let mut tmp2 = self.bf8.bf4.perform_parallel_fft_direct(load(4));
2973 tmp2[1] = SseVector::mul_complex(tmp2[1], self.twiddles_packed[6]);
2974 tmp2[2] = SseVector::mul_complex(tmp2[2], self.twiddles_packed[7]);
2975 tmp2[3] = SseVector::mul_complex(tmp2[3], self.twiddles_packed[8]);
2976 let [mid4, mid5] = transpose_complex_2x2_f32(tmp2[0], tmp2[1]);
2977 let [mid12, mid13] = transpose_complex_2x2_f32(tmp2[2], tmp2[3]);
2978
2979 let mut tmp3 = self.bf8.bf4.perform_parallel_fft_direct(load(6));
2980 tmp3[1] = SseVector::mul_complex(tmp3[1], self.twiddles_packed[9]);
2981 tmp3[2] = SseVector::mul_complex(tmp3[2], self.twiddles_packed[10]);
2982 tmp3[3] = SseVector::mul_complex(tmp3[3], self.twiddles_packed[11]);
2983 let [mid6, mid7] = transpose_complex_2x2_f32(tmp3[0], tmp3[1]);
2984 let [mid14, mid15] = transpose_complex_2x2_f32(tmp3[2], tmp3[3]);
2985
2986 let mut store = |i, vectors: [__m128; 8]| {
2988 buffer.store_complex(vectors[0], i);
2989 buffer.store_complex(vectors[1], i + 4);
2990 buffer.store_complex(vectors[2], i + 8);
2991 buffer.store_complex(vectors[3], i + 12);
2992 buffer.store_complex(vectors[4], i + 16);
2993 buffer.store_complex(vectors[5], i + 20);
2994 buffer.store_complex(vectors[6], i + 24);
2995 buffer.store_complex(vectors[7], i + 28);
2996 };
2997
2998 let out0 = self
3000 .bf8
3001 .perform_parallel_fft_direct([mid0, mid1, mid2, mid3, mid4, mid5, mid6, mid7]);
3002 store(0, out0);
3003
3004 let out1 = self
3005 .bf8
3006 .perform_parallel_fft_direct([mid8, mid9, mid10, mid11, mid12, mid13, mid14, mid15]);
3007 store(2, out1);
3008 }
3009
3010 #[inline(always)]
3011 pub(crate) unsafe fn perform_parallel_fft_contiguous(&self, mut buffer: impl SseArrayMut<f32>) {
3012 let load = |i: usize| {
3020 let [a0, a1] =
3021 transpose_complex_2x2_f32(buffer.load_complex(i + 0), buffer.load_complex(i + 32));
3022 let [b0, b1] =
3023 transpose_complex_2x2_f32(buffer.load_complex(i + 8), buffer.load_complex(i + 40));
3024 let [c0, c1] =
3025 transpose_complex_2x2_f32(buffer.load_complex(i + 16), buffer.load_complex(i + 48));
3026 let [d0, d1] =
3027 transpose_complex_2x2_f32(buffer.load_complex(i + 24), buffer.load_complex(i + 56));
3028 [[a0, b0, c0, d0], [a1, b1, c1, d1]]
3029 };
3030
3031 let [in0, in1] = load(0);
3033 let tmp0 = self.bf8.bf4.perform_parallel_fft_direct(in0);
3034 let mut tmp1 = self.bf8.bf4.perform_parallel_fft_direct(in1);
3035 tmp1[1] = SseVector::mul_complex(tmp1[1], self.twiddle1);
3036 tmp1[2] = SseVector::mul_complex(tmp1[2], self.twiddle2);
3037 tmp1[3] = SseVector::mul_complex(tmp1[3], self.twiddle3);
3038
3039 let [in2, in3] = load(2);
3040 let mut tmp2 = self.bf8.bf4.perform_parallel_fft_direct(in2);
3041 let mut tmp3 = self.bf8.bf4.perform_parallel_fft_direct(in3);
3042 tmp2[1] = SseVector::mul_complex(tmp2[1], self.twiddle2);
3043 tmp2[2] = self.bf8.bf4.rotate.rotate_both_45(tmp2[2]);
3044 tmp2[3] = SseVector::mul_complex(tmp2[3], self.twiddle6);
3045 tmp3[1] = SseVector::mul_complex(tmp3[1], self.twiddle3);
3046 tmp3[2] = SseVector::mul_complex(tmp3[2], self.twiddle6);
3047 tmp3[3] = SseVector::mul_complex(tmp3[3], self.twiddle9);
3048
3049 let [in4, in5] = load(4);
3050 let mut tmp4 = self.bf8.bf4.perform_parallel_fft_direct(in4);
3051 let mut tmp5 = self.bf8.bf4.perform_parallel_fft_direct(in5);
3052 tmp4[1] = self.bf8.bf4.rotate.rotate_both_45(tmp4[1]);
3053 tmp4[2] = self.bf8.bf4.rotate.rotate_both(tmp4[2]);
3054 tmp4[3] = self.bf8.bf4.rotate.rotate_both_135(tmp4[3]);
3055 tmp5[1] = SseVector::mul_complex(tmp5[1], self.twiddle5);
3056 tmp5[2] = SseVector::mul_complex(tmp5[2], self.twiddle10);
3057 tmp5[3] = SseVector::mul_complex(tmp5[3], self.twiddle15);
3058
3059 let [in6, in7] = load(6);
3060 let mut tmp6 = self.bf8.bf4.perform_parallel_fft_direct(in6);
3061 let mut tmp7 = self.bf8.bf4.perform_parallel_fft_direct(in7);
3062 tmp6[1] = SseVector::mul_complex(tmp6[1], self.twiddle6);
3063 tmp6[2] = self.bf8.bf4.rotate.rotate_both_135(tmp6[2]);
3064 tmp6[3] = SseVector::mul_complex(tmp6[3], self.twiddle18);
3065 tmp7[1] = SseVector::mul_complex(tmp7[1], self.twiddle7);
3066 tmp7[2] = SseVector::mul_complex(tmp7[2], self.twiddle14);
3067 tmp7[3] = SseVector::mul_complex(tmp7[3], self.twiddle21);
3068
3069 let mut store = |i, vectors_a: [__m128; 8], vectors_b: [__m128; 8]| {
3071 for n in 0..8 {
3072 let [a, b] = transpose_complex_2x2_f32(vectors_a[n], vectors_b[n]);
3073 buffer.store_complex(a, i + n * 4);
3074 buffer.store_complex(b, i + n * 4 + 32);
3075 }
3076 };
3077
3078 let out0 = self.bf8.perform_parallel_fft_direct([
3080 tmp0[0], tmp1[0], tmp2[0], tmp3[0], tmp4[0], tmp5[0], tmp6[0], tmp7[0],
3081 ]);
3082 let out1 = self.bf8.perform_parallel_fft_direct([
3083 tmp0[1], tmp1[1], tmp2[1], tmp3[1], tmp4[1], tmp5[1], tmp6[1], tmp7[1],
3084 ]);
3085 store(0, out0, out1);
3086
3087 let out2 = self.bf8.perform_parallel_fft_direct([
3088 tmp0[2], tmp1[2], tmp2[2], tmp3[2], tmp4[2], tmp5[2], tmp6[2], tmp7[2],
3089 ]);
3090 let out3 = self.bf8.perform_parallel_fft_direct([
3091 tmp0[3], tmp1[3], tmp2[3], tmp3[3], tmp4[3], tmp5[3], tmp6[3], tmp7[3],
3092 ]);
3093 store(2, out2, out3);
3094 }
3095}
3096
3097pub struct SseF64Butterfly32<T> {
3105 bf8: SseF64Butterfly8<T>,
3106 twiddle1: __m128d,
3107 twiddle2: __m128d,
3108 twiddle3: __m128d,
3109 twiddle5: __m128d,
3110 twiddle6: __m128d,
3111 twiddle7: __m128d,
3112 twiddle9: __m128d,
3113 twiddle10: __m128d,
3114 twiddle14: __m128d,
3115 twiddle15: __m128d,
3116 twiddle18: __m128d,
3117 twiddle21: __m128d,
3118}
3119
3120boilerplate_fft_sse_f64_butterfly!(SseF64Butterfly32, 32, |this: &SseF64Butterfly32<_>| this
3121 .bf8
3122 .bf4
3123 .direction);
3124impl<T: FftNum> SseF64Butterfly32<T> {
3125 #[inline(always)]
3126 pub fn new(direction: FftDirection) -> Self {
3127 assert_f64::<T>();
3128 let tw1: Complex<f64> = twiddles::compute_twiddle(1, 32, direction);
3129 let tw2: Complex<f64> = twiddles::compute_twiddle(2, 32, direction);
3130 let tw3: Complex<f64> = twiddles::compute_twiddle(3, 32, direction);
3131 let tw5: Complex<f64> = twiddles::compute_twiddle(5, 32, direction);
3132 let tw6: Complex<f64> = twiddles::compute_twiddle(6, 32, direction);
3133 let tw7: Complex<f64> = twiddles::compute_twiddle(7, 32, direction);
3134 let tw9: Complex<f64> = twiddles::compute_twiddle(9, 32, direction);
3135 let tw10: Complex<f64> = twiddles::compute_twiddle(10, 32, direction);
3136 let tw14: Complex<f64> = twiddles::compute_twiddle(14, 32, direction);
3137 let tw15: Complex<f64> = twiddles::compute_twiddle(15, 32, direction);
3138 let tw18: Complex<f64> = twiddles::compute_twiddle(18, 32, direction);
3139 let tw21: Complex<f64> = twiddles::compute_twiddle(21, 32, direction);
3140
3141 unsafe {
3142 Self {
3143 bf8: SseF64Butterfly8::new(direction),
3144 twiddle1: pack_64(tw1),
3145 twiddle2: pack_64(tw2),
3146 twiddle3: pack_64(tw3),
3147 twiddle5: pack_64(tw5),
3148 twiddle6: pack_64(tw6),
3149 twiddle7: pack_64(tw7),
3150 twiddle9: pack_64(tw9),
3151 twiddle10: pack_64(tw10),
3152 twiddle14: pack_64(tw14),
3153 twiddle15: pack_64(tw15),
3154 twiddle18: pack_64(tw18),
3155 twiddle21: pack_64(tw21),
3156 }
3157 }
3158 }
3159
3160 #[inline(always)]
3161 unsafe fn perform_fft_contiguous(&self, mut buffer: impl SseArrayMut<f64>) {
3162 let load = |i| {
3170 [
3171 buffer.load_complex(i),
3172 buffer.load_complex(i + 8),
3173 buffer.load_complex(i + 16),
3174 buffer.load_complex(i + 24),
3175 ]
3176 };
3177
3178 let mut tmp1 = self.bf8.bf4.perform_fft_direct(load(1));
3180 tmp1[1] = SseVector::mul_complex(tmp1[1], self.twiddle1);
3181 tmp1[2] = SseVector::mul_complex(tmp1[2], self.twiddle2);
3182 tmp1[3] = SseVector::mul_complex(tmp1[3], self.twiddle3);
3183
3184 let mut tmp2 = self.bf8.bf4.perform_fft_direct(load(2));
3185 tmp2[1] = SseVector::mul_complex(tmp2[1], self.twiddle2);
3186 tmp2[2] = self.bf8.bf4.rotate.rotate_45(tmp2[2]);
3187 tmp2[3] = SseVector::mul_complex(tmp2[3], self.twiddle6);
3188
3189 let mut tmp3 = self.bf8.bf4.perform_fft_direct(load(3));
3190 tmp3[1] = SseVector::mul_complex(tmp3[1], self.twiddle3);
3191 tmp3[2] = SseVector::mul_complex(tmp3[2], self.twiddle6);
3192 tmp3[3] = SseVector::mul_complex(tmp3[3], self.twiddle9);
3193
3194 let mut tmp5 = self.bf8.bf4.perform_fft_direct(load(5));
3195 tmp5[1] = SseVector::mul_complex(tmp5[1], self.twiddle5);
3196 tmp5[2] = SseVector::mul_complex(tmp5[2], self.twiddle10);
3197 tmp5[3] = SseVector::mul_complex(tmp5[3], self.twiddle15);
3198
3199 let mut tmp6 = self.bf8.bf4.perform_fft_direct(load(6));
3200 tmp6[1] = SseVector::mul_complex(tmp6[1], self.twiddle6);
3201 tmp6[2] = self.bf8.bf4.rotate.rotate_135(tmp6[2]);
3202 tmp6[3] = SseVector::mul_complex(tmp6[3], self.twiddle18);
3203
3204 let mut tmp7 = self.bf8.bf4.perform_fft_direct(load(7));
3205 tmp7[1] = SseVector::mul_complex(tmp7[1], self.twiddle7);
3206 tmp7[2] = SseVector::mul_complex(tmp7[2], self.twiddle14);
3207 tmp7[3] = SseVector::mul_complex(tmp7[3], self.twiddle21);
3208
3209 let mut tmp4 = self.bf8.bf4.perform_fft_direct(load(4));
3210 tmp4[1] = self.bf8.bf4.rotate.rotate_45(tmp4[1]);
3211 tmp4[2] = self.bf8.bf4.rotate.rotate(tmp4[2]);
3212 tmp4[3] = self.bf8.bf4.rotate.rotate_135(tmp4[3]);
3213
3214 let tmp0 = self.bf8.bf4.perform_fft_direct(load(0));
3216
3217 let mut store = |i, vectors: [__m128d; 8]| {
3219 buffer.store_complex(vectors[0], i);
3220 buffer.store_complex(vectors[1], i + 4);
3221 buffer.store_complex(vectors[2], i + 8);
3222 buffer.store_complex(vectors[3], i + 12);
3223 buffer.store_complex(vectors[4], i + 16);
3224 buffer.store_complex(vectors[5], i + 20);
3225 buffer.store_complex(vectors[6], i + 24);
3226 buffer.store_complex(vectors[7], i + 28);
3227 };
3228
3229 let out0 = self.bf8.perform_fft_direct([
3231 tmp0[0], tmp1[0], tmp2[0], tmp3[0], tmp4[0], tmp5[0], tmp6[0], tmp7[0],
3232 ]);
3233 store(0, out0);
3234
3235 let out1 = self.bf8.perform_fft_direct([
3236 tmp0[1], tmp1[1], tmp2[1], tmp3[1], tmp4[1], tmp5[1], tmp6[1], tmp7[1],
3237 ]);
3238 store(1, out1);
3239
3240 let out2 = self.bf8.perform_fft_direct([
3241 tmp0[2], tmp1[2], tmp2[2], tmp3[2], tmp4[2], tmp5[2], tmp6[2], tmp7[2],
3242 ]);
3243 store(2, out2);
3244
3245 let out3 = self.bf8.perform_fft_direct([
3246 tmp0[3], tmp1[3], tmp2[3], tmp3[3], tmp4[3], tmp5[3], tmp6[3], tmp7[3],
3247 ]);
3248 store(3, out3);
3249 }
3250}
3251
3252#[cfg(test)]
3253mod unit_tests {
3254 use super::*;
3255 use crate::{
3256 algorithm::Dft,
3257 test_utils::{check_fft_algorithm, compare_vectors},
3258 };
3259
3260 macro_rules! test_butterfly_32_func {
3263 ($test_name:ident, $struct_name:ident, $size:expr) => {
3264 #[test]
3265 fn $test_name() {
3266 let butterfly = $struct_name::new(FftDirection::Forward);
3267 check_fft_algorithm::<f32>(&butterfly, $size, FftDirection::Forward);
3268
3269 let butterfly_direction = $struct_name::new(FftDirection::Inverse);
3270 check_fft_algorithm::<f32>(&butterfly_direction, $size, FftDirection::Inverse);
3271 }
3272 };
3273 }
3274 test_butterfly_32_func!(test_ssef32_butterfly1, SseF32Butterfly1, 1);
3275 test_butterfly_32_func!(test_ssef32_butterfly2, SseF32Butterfly2, 2);
3276 test_butterfly_32_func!(test_ssef32_butterfly3, SseF32Butterfly3, 3);
3277 test_butterfly_32_func!(test_ssef32_butterfly4, SseF32Butterfly4, 4);
3278 test_butterfly_32_func!(test_ssef32_butterfly5, SseF32Butterfly5, 5);
3279 test_butterfly_32_func!(test_ssef32_butterfly6, SseF32Butterfly6, 6);
3280 test_butterfly_32_func!(test_ssef32_butterfly8, SseF32Butterfly8, 8);
3281 test_butterfly_32_func!(test_ssef32_butterfly9, SseF32Butterfly9, 9);
3282 test_butterfly_32_func!(test_ssef32_butterfly10, SseF32Butterfly10, 10);
3283 test_butterfly_32_func!(test_ssef32_butterfly12, SseF32Butterfly12, 12);
3284 test_butterfly_32_func!(test_ssef32_butterfly15, SseF32Butterfly15, 15);
3285 test_butterfly_32_func!(test_ssef32_butterfly16, SseF32Butterfly16, 16);
3286 test_butterfly_32_func!(test_ssef32_butterfly24, SseF32Butterfly24, 24);
3287 test_butterfly_32_func!(test_ssef32_butterfly32, SseF32Butterfly32, 32);
3288
3289 macro_rules! test_butterfly_64_func {
3292 ($test_name:ident, $struct_name:ident, $size:expr) => {
3293 #[test]
3294 fn $test_name() {
3295 let butterfly = $struct_name::new(FftDirection::Forward);
3296 check_fft_algorithm::<f64>(&butterfly, $size, FftDirection::Forward);
3297
3298 let butterfly_direction = $struct_name::new(FftDirection::Inverse);
3299 check_fft_algorithm::<f64>(&butterfly_direction, $size, FftDirection::Inverse);
3300 }
3301 };
3302 }
3303 test_butterfly_64_func!(test_ssef64_butterfly1, SseF64Butterfly1, 1);
3304 test_butterfly_64_func!(test_ssef64_butterfly2, SseF64Butterfly2, 2);
3305 test_butterfly_64_func!(test_ssef64_butterfly3, SseF64Butterfly3, 3);
3306 test_butterfly_64_func!(test_ssef64_butterfly4, SseF64Butterfly4, 4);
3307 test_butterfly_64_func!(test_ssef64_butterfly5, SseF64Butterfly5, 5);
3308 test_butterfly_64_func!(test_ssef64_butterfly6, SseF64Butterfly6, 6);
3309 test_butterfly_64_func!(test_ssef64_butterfly8, SseF64Butterfly8, 8);
3310 test_butterfly_64_func!(test_ssef64_butterfly9, SseF64Butterfly9, 9);
3311 test_butterfly_64_func!(test_ssef64_butterfly10, SseF64Butterfly10, 10);
3312 test_butterfly_64_func!(test_ssef64_butterfly12, SseF64Butterfly12, 12);
3313 test_butterfly_64_func!(test_ssef64_butterfly15, SseF64Butterfly15, 15);
3314 test_butterfly_64_func!(test_ssef64_butterfly16, SseF64Butterfly16, 16);
3315 test_butterfly_64_func!(test_ssef64_butterfly24, SseF64Butterfly24, 24);
3316 test_butterfly_64_func!(test_ssef64_butterfly32, SseF64Butterfly32, 32);
3317
3318 #[test]
3319 fn test_parallel_fft4_32() {
3320 unsafe {
3321 let val_a1 = Complex::<f32>::new(1.0, 2.5);
3322 let val_a2 = Complex::<f32>::new(3.2, 4.2);
3323 let val_a3 = Complex::<f32>::new(5.6, 6.2);
3324 let val_a4 = Complex::<f32>::new(7.4, 8.3);
3325
3326 let val_b1 = Complex::<f32>::new(6.0, 24.5);
3327 let val_b2 = Complex::<f32>::new(4.2, 34.2);
3328 let val_b3 = Complex::<f32>::new(9.6, 61.2);
3329 let val_b4 = Complex::<f32>::new(17.4, 81.3);
3330
3331 let p1 = _mm_set_ps(val_b1.im, val_b1.re, val_a1.im, val_a1.re);
3332 let p2 = _mm_set_ps(val_b2.im, val_b2.re, val_a2.im, val_a2.re);
3333 let p3 = _mm_set_ps(val_b3.im, val_b3.re, val_a3.im, val_a3.re);
3334 let p4 = _mm_set_ps(val_b4.im, val_b4.re, val_a4.im, val_a4.re);
3335
3336 let mut val_a = vec![val_a1, val_a2, val_a3, val_a4];
3337 let mut val_b = vec![val_b1, val_b2, val_b3, val_b4];
3338
3339 let dft = Dft::new(4, FftDirection::Forward);
3340
3341 let bf4 = SseF32Butterfly4::<f32>::new(FftDirection::Forward);
3342
3343 dft.process(&mut val_a);
3344 dft.process(&mut val_b);
3345 let res_both = bf4.perform_parallel_fft_direct([p1, p2, p3, p4]);
3346
3347 let res = std::mem::transmute::<[__m128; 4], [Complex<f32>; 8]>(res_both);
3348 let sse_res_a = [res[0], res[2], res[4], res[6]];
3349 let sse_res_b = [res[1], res[3], res[5], res[7]];
3350 assert!(compare_vectors(&val_a, &sse_res_a));
3351 assert!(compare_vectors(&val_b, &sse_res_b));
3352 }
3353 }
3354
3355 #[test]
3356 fn test_pack() {
3357 unsafe {
3358 let nbr2 = _mm_set_ps(8.0, 7.0, 6.0, 5.0);
3359 let nbr1 = _mm_set_ps(4.0, 3.0, 2.0, 1.0);
3360 let first = extract_lo_lo_f32(nbr1, nbr2);
3361 let second = extract_hi_hi_f32(nbr1, nbr2);
3362 let first = std::mem::transmute::<__m128, [Complex<f32>; 2]>(first);
3363 let second = std::mem::transmute::<__m128, [Complex<f32>; 2]>(second);
3364 let first_expected = [Complex::new(1.0, 2.0), Complex::new(5.0, 6.0)];
3365 let second_expected = [Complex::new(3.0, 4.0), Complex::new(7.0, 8.0)];
3366 assert_eq!(first, first_expected);
3367 assert_eq!(second, second_expected);
3368 }
3369 }
3370}