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