poulpy/spqlios/lib/spqlios/reim/reim_fft_avx2.c
spqlios/lib/spqlios/reim/reim_fft_avx2.cRaw
1#include "immintrin.h"
2#include "reim_fft_internal.h"
3#include "reim_fft_private.h"
4
5__always_inline void reim_twiddle_fft_avx2_fma(uint32_t h, double* re, double* im, double om[2]) {
6 const __m128d omx = _mm_load_pd(om);
7 const __m256d omra = _mm256_set_m128d(omx, omx);
8 const __m256d omi = _mm256_unpackhi_pd(omra, omra);
9 const __m256d omr = _mm256_unpacklo_pd(omra, omra);
10 double* r0 = re;
11 double* r1 = re + h;
12 double* i0 = im;
13 double* i1 = im + h;
14 for (uint32_t i=0; i<h; i+=4) {
15 __m256d ur0 = _mm256_loadu_pd(r0+i);
16 __m256d ur1 = _mm256_loadu_pd(r1+i);
17 __m256d ui0 = _mm256_loadu_pd(i0+i);
18 __m256d ui1 = _mm256_loadu_pd(i1+i);
19 __m256d tra = _mm256_mul_pd(omi, ui1);
20 __m256d tia = _mm256_mul_pd(omi, ur1);
21 tra = _mm256_fmsub_pd(omr, ur1, tra);
22 tia = _mm256_fmadd_pd(omr, ui1, tia);
23 ur1 = _mm256_sub_pd(ur0, tra);
24 ui1 = _mm256_sub_pd(ui0, tia);
25 ur0 = _mm256_add_pd(ur0, tra);
26 ui0 = _mm256_add_pd(ui0, tia);
27 _mm256_storeu_pd(r0+i, ur0);
28 _mm256_storeu_pd(r1+i, ur1);
29 _mm256_storeu_pd(i0+i, ui0);
30 _mm256_storeu_pd(i1+i, ui1);
31 }
32}
33
34__always_inline void reim_bitwiddle_fft_avx2_fma(uint32_t h, double* re, double* im, double om[4]) {
35 double* const r0 = re;
36 double* const r1 = re + h;
37 double* const r2 = re + 2*h;
38 double* const r3 = re + 3*h;
39 double* const i0 = im;
40 double* const i1 = im + h;
41 double* const i2 = im + 2*h;
42 double* const i3 = im + 3*h;
43 const __m256d om0 = _mm256_loadu_pd(om);
44 const __m256d omb = _mm256_permute2f128_pd(om0,om0,0x11);
45 const __m256d oma = _mm256_permute2f128_pd(om0,om0,0x00);
46 const __m256d omai = _mm256_unpackhi_pd(oma, oma);
47 const __m256d omar = _mm256_unpacklo_pd(oma, oma);
48 const __m256d ombi = _mm256_unpackhi_pd(omb, omb);
49 const __m256d ombr = _mm256_unpacklo_pd(omb, omb);
50 for (uint32_t i=0; i<h; i+=4) {
51 __m256d ur0 = _mm256_loadu_pd(r0+i);
52 __m256d ur1 = _mm256_loadu_pd(r1+i);
53 __m256d ur2 = _mm256_loadu_pd(r2+i);
54 __m256d ur3 = _mm256_loadu_pd(r3+i);
55 __m256d ui0 = _mm256_loadu_pd(i0+i);
56 __m256d ui1 = _mm256_loadu_pd(i1+i);
57 __m256d ui2 = _mm256_loadu_pd(i2+i);
58 __m256d ui3 = _mm256_loadu_pd(i3+i);
59 //------ twiddles 1
60 __m256d tra = _mm256_mul_pd(omai, ui2);
61 __m256d trb = _mm256_mul_pd(omai, ui3);
62 __m256d tia = _mm256_mul_pd(omai, ur2);
63 __m256d tib = _mm256_mul_pd(omai, ur3);
64 tra = _mm256_fmsub_pd(omar, ur2, tra);
65 trb = _mm256_fmsub_pd(omar, ur3, trb);
66 tia = _mm256_fmadd_pd(omar, ui2, tia);
67 tib = _mm256_fmadd_pd(omar, ui3, tib);
68 ur2 = _mm256_sub_pd(ur0, tra);
69 ur3 = _mm256_sub_pd(ur1, trb);
70 ui2 = _mm256_sub_pd(ui0, tia);
71 ui3 = _mm256_sub_pd(ui1, tib);
72 ur0 = _mm256_add_pd(ur0, tra);
73 ur1 = _mm256_add_pd(ur1, trb);
74 ui0 = _mm256_add_pd(ui0, tia);
75 ui1 = _mm256_add_pd(ui1, tib);
76 //------ twiddles 1
77 tra = _mm256_mul_pd(ombi, ui1);
78 trb = _mm256_mul_pd(ombr, ui3); //ii
79 tia = _mm256_mul_pd(ombi, ur1);
80 tib = _mm256_mul_pd(ombr, ur3); //ri
81 tra = _mm256_fmsub_pd(ombr, ur1, tra);
82 trb = _mm256_fmadd_pd(ombi, ur3, trb); //-rr+ii
83 tia = _mm256_fmadd_pd(ombr, ui1, tia);
84 tib = _mm256_fmsub_pd(ombi, ui3, tib); //-ir-ri
85 ur1 = _mm256_sub_pd(ur0, tra);
86 ur3 = _mm256_add_pd(ur2, trb);
87 ui1 = _mm256_sub_pd(ui0, tia);
88 ui3 = _mm256_add_pd(ui2, tib);
89 ur0 = _mm256_add_pd(ur0, tra);
90 ur2 = _mm256_sub_pd(ur2, trb);
91 ui0 = _mm256_add_pd(ui0, tia);
92 ui2 = _mm256_sub_pd(ui2, tib);
93 ///---
94 _mm256_storeu_pd(r0+i, ur0);
95 _mm256_storeu_pd(r1+i, ur1);
96 _mm256_storeu_pd(r2+i, ur2);
97 _mm256_storeu_pd(r3+i, ur3);
98 _mm256_storeu_pd(i0+i, ui0);
99 _mm256_storeu_pd(i1+i, ui1);
100 _mm256_storeu_pd(i2+i, ui2);
101 _mm256_storeu_pd(i3+i, ui3);
102 }
103}
104
105void reim_fft_bfs_16_avx2_fma(uint32_t m, double* re, double* im, double** omg) {
106 uint32_t log2m = _mm_popcnt_u32(m-1); //log2(m);
107 uint32_t mm = m;
108 if ((log2m & 1) != 0) {
109 uint32_t h = mm >> 1;
110 // do the first twiddle iteration normally
111 reim_twiddle_fft_avx2_fma(h, re, im, *omg);
112 *omg += 2;
113 mm = h;
114 }
115 while (mm > 16) {
116 uint32_t h = mm >> 2;
117 for (uint32_t off = 0; off < m; off += mm) {
118 reim_bitwiddle_fft_avx2_fma(h, re + off, im + off, *omg);
119 *omg += 4;
120 }
121 mm = h;
122 }
123 if (mm!=16) abort(); // bug!
124 for (uint32_t off = 0; off < m; off += 16) {
125 reim_fft16_avx_fma(re+off, im+off, *omg);
126 *omg += 16;
127 }
128}
129
130void reim_fft_rec_16_avx2_fma(uint32_t m, double* re, double* im, double** omg) {
131 if (m <= 2048) return reim_fft_bfs_16_avx2_fma(m, re, im, omg);
132 const uint32_t h = m / 2;
133 reim_twiddle_fft_avx2_fma(h, re, im, *omg);
134 *omg += 2;
135 reim_fft_rec_16_avx2_fma(h, re, im, omg);
136 reim_fft_rec_16_avx2_fma(h, re + h, im + h, omg);
137}
138
139void reim_fft_avx2_fma(const REIM_FFT_PRECOMP* precomp, double* dat) {
140 const int32_t m = precomp->m;
141 double* omg = precomp->powomegas;
142 double* re = dat;
143 double* im = dat+m;
144 if (m <= 16) {
145 switch (m) {
146 case 1:
147 return;
148 case 2:
149 return reim_fft2_ref(re, im, omg);
150 case 4:
151 return reim_fft4_avx_fma(re, im, omg);
152 case 8:
153 return reim_fft8_avx_fma(re, im, omg);
154 case 16:
155 return reim_fft16_avx_fma(re, im, omg);
156 default:
157 abort(); // m is not a power of 2
158 }
159 }
160 if (m <= 2048) return reim_fft_bfs_16_avx2_fma(m, re, im, &omg);
161 return reim_fft_rec_16_avx2_fma(m, re, im, &omg);
162}