poulpy/poulpy-backend/src/cpu_spqlios/spqlios-arithmetic/spqlios/cplx/cplx_fft_avx2_fma.c
poulpy-backend/src/cpu_spqlios/spqlios-arithmetic/spqlios/cplx/cplx_fft_avx2_fma.cRaw
1#include <immintrin.h>
2#include <string.h>
3
4#include "cplx_fft_internal.h"
5#include "cplx_fft_private.h"
6
7typedef double D4MEM[4];
8
9/**
10 * @brief complex fft via bfs strategy (for m between 2 and 8)
11 * @param dat the data to run the algorithm on
12 * @param omg precomputed tables (must have been filled with fill_omega)
13 * @param m ring dimension of the FFT (modulo X^m-i)
14 */
15void cplx_fft_avx2_fma_bfs_2(D4MEM* dat, const D4MEM** omg, uint32_t m) {
16 double* data = (double*)dat;
17 int32_t _2nblock = m >> 1; // = h in ref code
18 D4MEM* const finaldd = (D4MEM*)(data + 2 * m);
19 while (_2nblock >= 2) {
20 int32_t nblock = _2nblock >> 1; // =h/2 in ref code
21 D4MEM* dd = (D4MEM*)data;
22 do {
23 const __m256d om = _mm256_load_pd(*omg[0]);
24 const __m256d omim = _mm256_addsub_pd(_mm256_setzero_pd(), _mm256_unpackhi_pd(om, om));
25 const __m256d omre = _mm256_unpacklo_pd(om, om);
26 D4MEM* const ddend = (dd + nblock);
27 D4MEM* ddmid = ddend;
28 do {
29 const __m256d b = _mm256_loadu_pd(ddmid[0]);
30 const __m256d t1 = _mm256_mul_pd(b, omre);
31 const __m256d barb = _mm256_shuffle_pd(b, b, 5);
32 const __m256d t2 = _mm256_fmadd_pd(barb, omim, t1);
33 const __m256d a = _mm256_loadu_pd(dd[0]);
34 const __m256d newa = _mm256_add_pd(a, t2);
35 const __m256d newb = _mm256_sub_pd(a, t2);
36 _mm256_storeu_pd(dd[0], newa);
37 _mm256_storeu_pd(ddmid[0], newb);
38 dd += 1;
39 ddmid += 1;
40 } while (dd < ddend);
41 dd += nblock;
42 *omg += 1;
43 } while (dd < finaldd);
44 _2nblock >>= 1;
45 }
46 // last iteration when _2nblock == 1
47 {
48 D4MEM* dd = (D4MEM*)data;
49 do {
50 const __m256d om = _mm256_load_pd(*omg[0]);
51 const __m256d omre = _mm256_unpacklo_pd(om, om);
52 const __m256d omim = _mm256_unpackhi_pd(om, om);
53 const __m256d ab = _mm256_loadu_pd(dd[0]);
54 const __m256d bb = _mm256_permute4x64_pd(ab, 0b11101110);
55 const __m256d bbbar = _mm256_permute4x64_pd(ab, 0b10111011);
56 const __m256d t1 = _mm256_mul_pd(bbbar, omim);
57 const __m256d t2 = _mm256_fmaddsub_pd(bb, omre, t1);
58 const __m256d aa = _mm256_permute4x64_pd(ab, 0b01000100);
59 const __m256d newab = _mm256_add_pd(aa, t2);
60 _mm256_storeu_pd(dd[0], newab);
61 dd += 1;
62 *omg += 1;
63 } while (dd < finaldd);
64 }
65}
66
67__always_inline void cplx_twiddle_fft_avx2(int32_t h, D4MEM* data, const void* omg) {
68 const __m256d om = _mm256_loadu_pd(omg);
69 const __m256d omim = _mm256_unpackhi_pd(om, om);
70 const __m256d omre = _mm256_unpacklo_pd(om, om);
71 D4MEM* d0 = data;
72 D4MEM* const ddend = d0 + (h >> 1);
73 D4MEM* d1 = ddend;
74 do {
75 const __m256d b = _mm256_loadu_pd(d1[0]);
76 const __m256d barb = _mm256_shuffle_pd(b, b, 5);
77 const __m256d t1 = _mm256_mul_pd(barb, omim);
78 const __m256d t2 = _mm256_fmaddsub_pd(b, omre, t1);
79 const __m256d a = _mm256_loadu_pd(d0[0]);
80 const __m256d newa = _mm256_add_pd(a, t2);
81 const __m256d newb = _mm256_sub_pd(a, t2);
82 _mm256_storeu_pd(d0[0], newa);
83 _mm256_storeu_pd(d1[0], newb);
84 d0 += 1;
85 d1 += 1;
86 } while (d0 < ddend);
87}
88
89__always_inline void cplx_bitwiddle_fft_avx2(int32_t h, void* data, const void* powom) {
90 const __m256d omx = _mm256_loadu_pd(powom);
91 const __m256d oma = _mm256_permute2f128_pd(omx, omx, 0x00);
92 const __m256d omb = _mm256_permute2f128_pd(omx, omx, 0x11);
93 const __m256d omaim = _mm256_unpackhi_pd(oma, oma);
94 const __m256d omare = _mm256_unpacklo_pd(oma, oma);
95 const __m256d ombim = _mm256_unpackhi_pd(omb, omb);
96 const __m256d ombre = _mm256_unpacklo_pd(omb, omb);
97 D4MEM* d0 = (D4MEM*)data;
98 D4MEM* const ddend = d0 + (h >> 1);
99 D4MEM* d1 = ddend;
100 D4MEM* d2 = d0 + h;
101 D4MEM* d3 = d1 + h;
102 __m256d reg0, reg1, reg2, reg3, tmp0, tmp1;
103 do {
104 reg0 = _mm256_loadu_pd(d0[0]);
105 reg1 = _mm256_loadu_pd(d1[0]);
106 reg2 = _mm256_loadu_pd(d2[0]);
107 reg3 = _mm256_loadu_pd(d3[0]);
108 tmp0 = _mm256_shuffle_pd(reg2, reg2, 5);
109 tmp1 = _mm256_shuffle_pd(reg3, reg3, 5);
110 tmp0 = _mm256_mul_pd(tmp0, omaim);
111 tmp1 = _mm256_mul_pd(tmp1, omaim);
112 tmp0 = _mm256_fmaddsub_pd(reg2, omare, tmp0);
113 tmp1 = _mm256_fmaddsub_pd(reg3, omare, tmp1);
114 reg2 = _mm256_sub_pd(reg0, tmp0);
115 reg3 = _mm256_sub_pd(reg1, tmp1);
116 reg0 = _mm256_add_pd(reg0, tmp0);
117 reg1 = _mm256_add_pd(reg1, tmp1);
118 //--------------------------------------
119 tmp0 = _mm256_shuffle_pd(reg1, reg1, 5);
120 tmp1 = _mm256_shuffle_pd(reg3, reg3, 5);
121 tmp0 = _mm256_mul_pd(tmp0, ombim); //(r,i)
122 tmp1 = _mm256_mul_pd(tmp1, ombre); //(-i,r)
123 tmp0 = _mm256_fmaddsub_pd(reg1, ombre, tmp0);
124 tmp1 = _mm256_fmsubadd_pd(reg3, ombim, tmp1);
125 reg1 = _mm256_sub_pd(reg0, tmp0);
126 reg3 = _mm256_add_pd(reg2, tmp1);
127 reg0 = _mm256_add_pd(reg0, tmp0);
128 reg2 = _mm256_sub_pd(reg2, tmp1);
129 /////
130 _mm256_storeu_pd(d0[0], reg0);
131 _mm256_storeu_pd(d1[0], reg1);
132 _mm256_storeu_pd(d2[0], reg2);
133 _mm256_storeu_pd(d3[0], reg3);
134 d0 += 1;
135 d1 += 1;
136 d2 += 1;
137 d3 += 1;
138 } while (d0 < ddend);
139}
140
141/**
142 * @brief complex fft via bfs strategy (for m >= 16)
143 * @param dat the data to run the algorithm on
144 * @param omg precomputed tables (must have been filled with fill_omega)
145 * @param m ring dimension of the FFT (modulo X^m-i)
146 */
147void cplx_fft_avx2_fma_bfs_16(D4MEM* dat, const D4MEM** omg, uint32_t m) {
148 double* data = (double*)dat;
149 D4MEM* const finaldd = (D4MEM*)(data + 2 * m);
150 uint32_t mm = m;
151 uint32_t log2m = _mm_popcnt_u32(m - 1); // log2(m)
152 if (log2m % 2 == 1) {
153 uint32_t h = mm >> 1;
154 cplx_twiddle_fft_avx2(h, dat, **omg);
155 *omg += 1;
156 mm >>= 1;
157 }
158 while (mm > 16) {
159 uint32_t h = mm / 4;
160 for (CPLX* d = (CPLX*)data; d < (CPLX*)finaldd; d += mm) {
161 cplx_bitwiddle_fft_avx2(h, d, (CPLX*)*omg);
162 *omg += 1;
163 }
164 mm = h;
165 }
166 {
167 D4MEM* dd = (D4MEM*)data;
168 do {
169 cplx_fft16_avx_fma(dd, *omg);
170 dd += 8;
171 *omg += 4;
172 } while (dd < finaldd);
173 _mm256_zeroupper();
174 }
175 /*
176 int32_t _2nblock = m >> 1; // = h in ref code
177 while (_2nblock >= 16) {
178 int32_t nblock = _2nblock >> 1; // =h/2 in ref code
179 D4MEM* dd = (D4MEM*)data;
180 do {
181 const __m256d om = _mm256_load_pd(*omg[0]);
182 const __m256d omim = _mm256_addsub_pd(_mm256_setzero_pd(), _mm256_unpackhi_pd(om, om));
183 const __m256d omre = _mm256_unpacklo_pd(om, om);
184 D4MEM* const ddend = (dd + nblock);
185 D4MEM* ddmid = ddend;
186 do {
187 const __m256d b = _mm256_loadu_pd(ddmid[0]);
188 const __m256d t1 = _mm256_mul_pd(b, omre);
189 const __m256d barb = _mm256_shuffle_pd(b, b, 5);
190 const __m256d t2 = _mm256_fmadd_pd(barb, omim, t1);
191 const __m256d a = _mm256_loadu_pd(dd[0]);
192 const __m256d newa = _mm256_add_pd(a, t2);
193 const __m256d newb = _mm256_sub_pd(a, t2);
194 _mm256_storeu_pd(dd[0], newa);
195 _mm256_storeu_pd(ddmid[0], newb);
196 dd += 1;
197 ddmid += 1;
198 } while (dd < ddend);
199 dd += nblock;
200 *omg += 1;
201 } while (dd < finaldd);
202 _2nblock >>= 1;
203 }
204 // last iteration when _2nblock == 8
205 {
206 D4MEM* dd = (D4MEM*)data;
207 do {
208 cplx_fft16_avx_fma(dd, *omg);
209 dd += 8;
210 *omg += 4;
211 } while (dd < finaldd);
212 _mm256_zeroupper();
213 }
214 */
215}
216
217/**
218 * @brief complex fft via dfs recursion (for m >= 16)
219 * @param dat the data to run the algorithm on
220 * @param omg precomputed tables (must have been filled with fill_omega)
221 * @param m ring dimension of the FFT (modulo X^m-i)
222 */
223void cplx_fft_avx2_fma_rec_16(D4MEM* dat, const D4MEM** omg, uint32_t m) {
224 if (m <= 8) return cplx_fft_avx2_fma_bfs_2(dat, omg, m);
225 if (m <= 2048) return cplx_fft_avx2_fma_bfs_16(dat, omg, m);
226 double* data = (double*)dat;
227 int32_t _2nblock = m >> 1; // = h in ref code
228 int32_t nblock = _2nblock >> 1; // =h/2 in ref code
229 D4MEM* dd = (D4MEM*)data;
230 const __m256d om = _mm256_load_pd(*omg[0]);
231 const __m256d omim = _mm256_addsub_pd(_mm256_setzero_pd(), _mm256_unpackhi_pd(om, om));
232 const __m256d omre = _mm256_unpacklo_pd(om, om);
233 D4MEM* const ddend = (dd + nblock);
234 D4MEM* ddmid = ddend;
235 do {
236 const __m256d b = _mm256_loadu_pd(ddmid[0]);
237 const __m256d t1 = _mm256_mul_pd(b, omre);
238 const __m256d barb = _mm256_shuffle_pd(b, b, 5);
239 const __m256d t2 = _mm256_fmadd_pd(barb, omim, t1);
240 const __m256d a = _mm256_loadu_pd(dd[0]);
241 const __m256d newa = _mm256_add_pd(a, t2);
242 const __m256d newb = _mm256_sub_pd(a, t2);
243 _mm256_storeu_pd(dd[0], newa);
244 _mm256_storeu_pd(ddmid[0], newb);
245 dd += 1;
246 ddmid += 1;
247 } while (dd < ddend);
248 *omg += 1;
249 cplx_fft_avx2_fma_rec_16(dat, omg, _2nblock);
250 cplx_fft_avx2_fma_rec_16(ddend, omg, _2nblock);
251}
252
253/**
254 * @brief complex fft via best strategy (for m>=1)
255 * @param dat the data to run the algorithm on: m complex numbers
256 * @param omg precomputed tables (must have been filled with fill_omega)
257 * @param m ring dimension of the FFT (modulo X^m-i)
258 */
259EXPORT void cplx_fft_avx2_fma(const CPLX_FFT_PRECOMP* precomp, void* d) {
260 const uint32_t m = precomp->m;
261 const D4MEM* omg = (D4MEM*)precomp->powomegas;
262 if (m <= 1) return;
263 if (m <= 8) return cplx_fft_avx2_fma_bfs_2(d, &omg, m);
264 if (m <= 2048) return cplx_fft_avx2_fma_bfs_16(d, &omg, m);
265 cplx_fft_avx2_fma_rec_16(d, &omg, m);
266}