51dd883ed2c10a46fb5f7dce8c5c9d91060959fb
[alexxy/gromacs.git] / src / gromacs / mdlib / nbnxn_kernels / nbnxn_kernel_simd_utils_x86_256s.h
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 2012,2013,2014, by the GROMACS development team, led by
5  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
6  * and including many others, as listed in the AUTHORS file in the
7  * top-level source directory and at http://www.gromacs.org.
8  *
9  * GROMACS is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public License
11  * as published by the Free Software Foundation; either version 2.1
12  * of the License, or (at your option) any later version.
13  *
14  * GROMACS is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with GROMACS; if not, see
21  * http://www.gnu.org/licenses, or write to the Free Software Foundation,
22  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
23  *
24  * If you want to redistribute modifications to GROMACS, please
25  * consider that scientific software is very special. Version
26  * control is crucial - bugs must be traceable. We will be happy to
27  * consider code for inclusion in the official distribution, but
28  * derived work must not be called official GROMACS. Details are found
29  * in the README & COPYING files - if they are missing, get the
30  * official version at http://www.gromacs.org.
31  *
32  * To help us fund GROMACS development, we humbly ask that you cite
33  * the research papers on the package. Check out http://www.gromacs.org.
34  */
35 #ifndef _nbnxn_kernel_simd_utils_x86_256s_h_
36 #define _nbnxn_kernel_simd_utils_x86_256s_h_
37
38 /* This files contains all functions/macros for the SIMD kernels
39  * which have explicit dependencies on the j-cluster size and/or SIMD-width.
40  * The functionality which depends on the j-cluster size is:
41  *   LJ-parameter lookup
42  *   force table lookup
43  *   energy group pair energy storage
44  */
45
46
47 #ifdef GMX_NBNXN_SIMD_2XNN
48 /* Half-width operations are required for the 2xnn kernels */
49
50 /* Half-width SIMD real type */
51 #define gmx_mm_hpr  __m128
52
53 /* Half-width SIMD operations */
54 /* Load reals at half-width aligned pointer b into half-width SIMD register a */
55 #define gmx_load_hpr(a, b)    *(a) = _mm_load_ps(b)
56 /* Set all entries in half-width SIMD register *a to b */
57 #define gmx_set1_hpr(a, b)   *(a) = _mm_set1_ps(b)
58 /* Load one real at b and one real at b+1 into halves of a, respectively */
59 #define gmx_load1p1_pr(a, b)  *(a) = _mm256_insertf128_ps(_mm256_castps128_ps256(_mm_load1_ps(b)), _mm_load1_ps(b+1), 0x1)
60 /* To half-width SIMD register b into half width aligned memory a */
61 #define gmx_store_hpr(a, b)          _mm_store_ps(a, b)
62 #define gmx_add_hpr                  _mm_add_ps
63 #define gmx_sub_hpr                  _mm_sub_ps
64
65 /* Sum over 4 half SIMD registers */
66 static __m128 gmx_simdcall gmx_sum4_hpr(__m256 x, __m256 y)
67 {
68     __m256 sum;
69
70     sum = _mm256_add_ps(x, y);
71     return _mm_add_ps(_mm256_castps256_ps128(sum), _mm256_extractf128_ps(sum, 0x1));
72 }
73
74 /* Load reals at half-width aligned pointer b into two halves of a */
75 static gmx_inline void
76 gmx_loaddh_pr(gmx_simd_real_t *a, const real *b)
77 {
78     __m128 tmp;
79     tmp = _mm_load_ps(b);
80     *a  = _mm256_insertf128_ps(_mm256_castps128_ps256(tmp), tmp, 0x1);
81 }
82
83 static gmx_inline void gmx_simdcall
84 gmx_pr_to_2hpr(gmx_simd_real_t a, gmx_mm_hpr *b, gmx_mm_hpr *c)
85 {
86     *b = _mm256_extractf128_ps(a, 0);
87     *c = _mm256_extractf128_ps(a, 1);
88 }
89
90 /* Store half width SIMD registers a and b in full width register *c */
91 static gmx_inline void gmx_simdcall
92 gmx_2hpr_to_pr(gmx_mm_hpr a, gmx_mm_hpr b, gmx_simd_real_t *c)
93 {
94     *c = _mm256_insertf128_ps(_mm256_castps128_ps256(a), b, 0x1);
95 }
96
97 #endif /* GMX_NBNXN_SIMD_2XNN */
98
99 /* Collect element 0 and 1 of the 4 inputs to out0 and out1, respectively */
100 static gmx_inline void gmx_simdcall
101 gmx_shuffle_4_ps_fil01_to_2_ps(__m128 in0, __m128 in1, __m128 in2, __m128 in3,
102                                __m128 *out0, __m128 *out1)
103 {
104     __m128 _c01, _c23;
105
106     _c01  = _mm_movelh_ps(in0, in1);
107     _c23  = _mm_movelh_ps(in2, in3);
108     *out0 = _mm_shuffle_ps(_c01, _c23, _MM_SHUFFLE(2, 0, 2, 0));
109     *out1 = _mm_shuffle_ps(_c01, _c23, _MM_SHUFFLE(3, 1, 3, 1));
110 }
111
112 /* Collect element 2 of the 4 inputs to out */
113 static gmx_inline __m128 gmx_simdcall
114 gmx_shuffle_4_ps_fil2_to_1_ps(__m128 in0, __m128 in1, __m128 in2, __m128 in3)
115 {
116     __m128 _c01, _c23;
117
118     _c01 = _mm_shuffle_ps(in0, in1, _MM_SHUFFLE(3, 2, 3, 2));
119     _c23 = _mm_shuffle_ps(in2, in3, _MM_SHUFFLE(3, 2, 3, 2));
120
121     return _mm_shuffle_ps(_c01, _c23, _MM_SHUFFLE(2, 0, 2, 0));
122 }
123
124 /* Sum the elements within each input register and return the sums */
125 static gmx_inline __m128 gmx_simdcall
126 gmx_mm_transpose_sum4_pr(__m256 in0, __m256 in1,
127                          __m256 in2, __m256 in3)
128 {
129     in0 = _mm256_hadd_ps(in0, in1);
130     in2 = _mm256_hadd_ps(in2, in3);
131     in1 = _mm256_hadd_ps(in0, in2);
132
133     return _mm_add_ps(_mm256_castps256_ps128(in1),
134                       _mm256_extractf128_ps(in1, 1));
135 }
136
137 /* Sum the elements of halfs of each input register and return the sums */
138 static gmx_inline __m128 gmx_simdcall
139 gmx_mm_transpose_sum4h_pr(__m256 in0, __m256 in2)
140 {
141     in0 = _mm256_hadd_ps(in0, _mm256_setzero_ps());
142     in2 = _mm256_hadd_ps(in2, _mm256_setzero_ps());
143     in0 = _mm256_hadd_ps(in0, in2);
144     in2 = _mm256_permute_ps(in0, _MM_SHUFFLE(2, 3, 0, 1));
145
146     return _mm_add_ps(_mm256_castps256_ps128(in0), _mm256_extractf128_ps(in2, 1));
147 }
148
149 /* Put two 128-bit 4-float registers into one 256-bit 8-float register */
150 static gmx_inline __m256 gmx_simdcall
151 gmx_2_mm_to_m256(__m128 in0, __m128 in1)
152 {
153     return _mm256_insertf128_ps(_mm256_castps128_ps256(in0), in1, 1);
154 }
155
156 #if UNROLLJ == 8
157 static gmx_inline void
158 load_lj_pair_params(const real *nbfp, const int *type, int aj,
159                     __m256 *c6_S, __m256 *c12_S)
160 {
161     __m128 clj_S[UNROLLJ], c6t_S[2], c12t_S[2];
162     int    p;
163
164     for (p = 0; p < UNROLLJ; p++)
165     {
166         /* Here we load 4 aligned floats, but we need just 2 */
167         clj_S[p] = _mm_load_ps(nbfp+type[aj+p]*nbfp_stride);
168     }
169     gmx_shuffle_4_ps_fil01_to_2_ps(clj_S[0], clj_S[1], clj_S[2], clj_S[3],
170                                    &c6t_S[0], &c12t_S[0]);
171     gmx_shuffle_4_ps_fil01_to_2_ps(clj_S[4], clj_S[5], clj_S[6], clj_S[7],
172                                    &c6t_S[1], &c12t_S[1]);
173
174     *c6_S  = gmx_2_mm_to_m256(c6t_S[0], c6t_S[1]);
175     *c12_S = gmx_2_mm_to_m256(c12t_S[0], c12t_S[1]);
176 }
177 #endif
178
179 #if UNROLLJ == 4
180 static gmx_inline void
181 load_lj_pair_params2(const real *nbfp0, const real *nbfp1,
182                      const int *type, int aj,
183                      __m256 *c6_S, __m256 *c12_S)
184 {
185     __m128 clj_S0[UNROLLJ], clj_S1[UNROLLJ], c6t_S[2], c12t_S[2];
186     int    p;
187
188     for (p = 0; p < UNROLLJ; p++)
189     {
190         /* Here we load 4 aligned floats, but we need just 2 */
191         clj_S0[p] = _mm_load_ps(nbfp0+type[aj+p]*nbfp_stride);
192     }
193     for (p = 0; p < UNROLLJ; p++)
194     {
195         /* Here we load 4 aligned floats, but we need just 2 */
196         clj_S1[p] = _mm_load_ps(nbfp1+type[aj+p]*nbfp_stride);
197     }
198     gmx_shuffle_4_ps_fil01_to_2_ps(clj_S0[0], clj_S0[1], clj_S0[2], clj_S0[3],
199                                    &c6t_S[0], &c12t_S[0]);
200     gmx_shuffle_4_ps_fil01_to_2_ps(clj_S1[0], clj_S1[1], clj_S1[2], clj_S1[3],
201                                    &c6t_S[1], &c12t_S[1]);
202
203     *c6_S  = gmx_2_mm_to_m256(c6t_S[0], c6t_S[1]);
204     *c12_S = gmx_2_mm_to_m256(c12t_S[0], c12t_S[1]);
205 }
206 #endif
207
208
209 /* The load_table functions below are performance critical.
210  * The routines issue UNROLLI*UNROLLJ _mm_load_ps calls.
211  * As these all have latencies, scheduling is crucial.
212  * The Intel compilers and CPUs seem to do a good job at this.
213  * But AMD CPUs perform significantly worse with gcc than with icc.
214  * Performance is improved a bit by using the extract function UNROLLJ times,
215  * instead of doing an _mm_store_si128 for every i-particle.
216  * This is only faster when we use FDV0 formatted tables, where we also need
217  * to multiple the index by 4, which can be done by a SIMD bit shift.
218  * With single precision AVX, 8 extracts are much slower than 1 store.
219  * Because of this, the load_table_f function always takes the ti
220  * parameter, which should contain a buffer that is aligned with
221  * prepare_table_load_buffer(), but it is only used with full-width
222  * AVX_256. */
223
224 static gmx_inline void gmx_simdcall
225 load_table_f(const real *tab_coul_FDV0, gmx_simd_int32_t ti_S, int *ti,
226              __m256 *ctab0_S, __m256 *ctab1_S)
227 {
228     __m128 ctab_S[8], ctabt_S[4];
229     int    j;
230
231     /* Bit shifting would be faster, but AVX doesn't support that */
232     _mm256_store_si256((__m256i *)ti, ti_S);
233     for (j = 0; j < 8; j++)
234     {
235         ctab_S[j] = _mm_load_ps(tab_coul_FDV0+ti[j]*4);
236     }
237     gmx_shuffle_4_ps_fil01_to_2_ps(ctab_S[0], ctab_S[1], ctab_S[2], ctab_S[3],
238                                    &ctabt_S[0], &ctabt_S[2]);
239     gmx_shuffle_4_ps_fil01_to_2_ps(ctab_S[4], ctab_S[5], ctab_S[6], ctab_S[7],
240                                    &ctabt_S[1], &ctabt_S[3]);
241
242     *ctab0_S = gmx_2_mm_to_m256(ctabt_S[0], ctabt_S[1]);
243     *ctab1_S = gmx_2_mm_to_m256(ctabt_S[2], ctabt_S[3]);
244 }
245
246 static gmx_inline void gmx_simdcall
247 load_table_f_v(const real *tab_coul_FDV0, gmx_simd_int32_t ti_S, int *ti,
248                __m256 *ctab0_S, __m256 *ctab1_S, __m256 *ctabv_S)
249 {
250     __m128 ctab_S[8], ctabt_S[4], ctabvt_S[2];
251     int    j;
252
253     /* Bit shifting would be faster, but AVX doesn't support that */
254     _mm256_store_si256((__m256i *)ti, ti_S);
255     for (j = 0; j < 8; j++)
256     {
257         ctab_S[j] = _mm_load_ps(tab_coul_FDV0+ti[j]*4);
258     }
259     gmx_shuffle_4_ps_fil01_to_2_ps(ctab_S[0], ctab_S[1], ctab_S[2], ctab_S[3],
260                                    &ctabt_S[0], &ctabt_S[2]);
261     gmx_shuffle_4_ps_fil01_to_2_ps(ctab_S[4], ctab_S[5], ctab_S[6], ctab_S[7],
262                                    &ctabt_S[1], &ctabt_S[3]);
263
264     *ctab0_S = gmx_2_mm_to_m256(ctabt_S[0], ctabt_S[1]);
265     *ctab1_S = gmx_2_mm_to_m256(ctabt_S[2], ctabt_S[3]);
266
267     ctabvt_S[0] = gmx_shuffle_4_ps_fil2_to_1_ps(ctab_S[0], ctab_S[1],
268                                                 ctab_S[2], ctab_S[3]);
269     ctabvt_S[1] = gmx_shuffle_4_ps_fil2_to_1_ps(ctab_S[4], ctab_S[5],
270                                                 ctab_S[6], ctab_S[7]);
271
272     *ctabv_S = gmx_2_mm_to_m256(ctabvt_S[0], ctabvt_S[1]);
273 }
274
275 #ifdef GMX_SIMD_HAVE_FINT32_LOGICAL
276
277 typedef gmx_simd_int32_t gmx_exclfilter;
278 static const int filter_stride = GMX_SIMD_INT32_WIDTH/GMX_SIMD_REAL_WIDTH;
279
280 static gmx_inline gmx_exclfilter gmx_simdcall
281 gmx_load1_exclfilter(int e)
282 {
283     return _mm256_set1_epi32(e);
284 }
285
286 static gmx_inline gmx_exclfilter gmx_simdcall
287 gmx_load_exclusion_filter(const unsigned *i)
288 {
289     return gmx_simd_load_i(i);
290 }
291
292 static gmx_inline gmx_simd_bool_t gmx_simdcall
293 gmx_checkbitmask_pb(gmx_exclfilter m0, gmx_exclfilter m1)
294 {
295     return _mm256_castsi256_ps(_mm256_cmpeq_epi32(_mm256_andnot_si256(m0, m1), _mm256_setzero_si256()));
296 }
297
298 #else /* GMX_SIMD_HAVE_FINT32_LOGICAL */
299
300 /* No integer support, use a real to store the exclusion bits */
301 typedef gmx_simd_real_t gmx_exclfilter;
302 static const int filter_stride = 1;
303
304 static gmx_inline gmx_exclfilter gmx_simdcall
305 gmx_load1_exclfilter(int e)
306 {
307     return _mm256_castsi256_ps(_mm256_set1_epi32(e));
308 }
309
310 static gmx_inline gmx_exclfilter gmx_simdcall
311 gmx_load_exclusion_filter(const unsigned *i)
312 {
313     return gmx_simd_load_r((real *) (i));
314 }
315
316 static gmx_inline gmx_simd_bool_t gmx_simdcall
317 gmx_checkbitmask_pb(gmx_exclfilter m0, gmx_exclfilter m1)
318 {
319     return _mm256_cmp_ps(_mm256_cvtepi32_ps(_mm256_castps_si256(_mm256_and_ps(m0, m1))), _mm256_setzero_ps(), 0x0c);
320 }
321
322 #endif /* GMX_SIMD_HAVE_FINT32_LOGICAL */
323
324 #endif /* _nbnxn_kernel_simd_utils_x86_s256s_h_ */