implemented plain-C SIMD macros for reference
[alexxy/gromacs.git] / src / mdlib / nbnxn_kernels / nbnxn_kernel_simd_utils_x86_256d.h
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
5  * Copyright (c) 2001-2012, The GROMACS Development Team
6  * Copyright (c) 2012,2013, by the GROMACS development team, led by
7  * David van der Spoel, Berk Hess, Erik Lindahl, and including many
8  * others, as listed in the AUTHORS file in the top-level source
9  * directory and at http://www.gromacs.org.
10  *
11  * GROMACS is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU Lesser General Public License
13  * as published by the Free Software Foundation; either version 2.1
14  * of the License, or (at your option) any later version.
15  *
16  * GROMACS is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19  * Lesser General Public License for more details.
20  *
21  * You should have received a copy of the GNU Lesser General Public
22  * License along with GROMACS; if not, see
23  * http://www.gnu.org/licenses, or write to the Free Software Foundation,
24  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
25  *
26  * If you want to redistribute modifications to GROMACS, please
27  * consider that scientific software is very special. Version
28  * control is crucial - bugs must be traceable. We will be happy to
29  * consider code for inclusion in the official distribution, but
30  * derived work must not be called official GROMACS. Details are found
31  * in the README & COPYING files - if they are missing, get the
32  * official version at http://www.gromacs.org.
33  *
34  * To help us fund GROMACS development, we humbly ask that you cite
35  * the research papers on the package. Check out http://www.gromacs.org.
36  */
37 #ifndef _nbnxn_kernel_simd_utils_x86_256d_h_
38 #define _nbnxn_kernel_simd_utils_x86_256d_h_
39
40 /* This files contains all functions/macros for the SIMD kernels
41  * which have explicit dependencies on the j-cluster size and/or SIMD-width.
42  * The functionality which depends on the j-cluster size is:
43  *   LJ-parameter lookup
44  *   force table lookup
45  *   energy group pair energy storage
46  */
47
48 /* Transpose 2 double precision registers */
49 static gmx_inline void
50 gmx_mm_transpose2_op_pd(__m128d in0, __m128d in1,
51                         __m128d *out0, __m128d *out1)
52 {
53     *out0 = _mm_unpacklo_pd(in0, in1);
54     *out1 = _mm_unpackhi_pd(in0, in1);
55 }
56
57 /* Sum the elements within each input register and store the sums in out */
58 static gmx_inline __m256d
59 gmx_mm_transpose_sum4_pr(__m256d in0, __m256d in1,
60                          __m256d in2, __m256d in3)
61 {
62     in0 = _mm256_hadd_pd(in0, in1);
63     in2 = _mm256_hadd_pd(in2, in3);
64
65     return _mm256_add_pd(_mm256_permute2f128_pd(in0, in2, 0x20), _mm256_permute2f128_pd(in0, in2, 0x31)); 
66 }
67
68 static gmx_inline __m256
69 gmx_mm256_invsqrt_ps_single(__m256 x)
70 {
71     const __m256 half  = _mm256_set_ps(0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5);
72     const __m256 three = _mm256_set_ps(3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0);
73
74     __m256       lu = _mm256_rsqrt_ps(x);
75
76     return _mm256_mul_ps(half, _mm256_mul_ps(_mm256_sub_ps(three, _mm256_mul_ps(_mm256_mul_ps(lu, lu), x)), lu));
77 }
78
79 /* Put two 128-bit 4-float registers into one 256-bit 8-float register */
80 static gmx_inline __m256
81 gmx_2_m128_to_m256(__m128 in0, __m128 in1)
82 {
83     return _mm256_insertf128_ps(_mm256_castps128_ps256(in0), in1, 1);
84 }
85
86 /* Put two 128-bit 2-double registers into one 256-bit 4-double register */
87 static gmx_inline __m256d
88 gmx_2_m128d_to_m256d(__m128d in0, __m128d in1)
89 {
90     return _mm256_insertf128_pd(_mm256_castpd128_pd256(in0), in1, 1);
91 }
92
93 /* Do 2 double precision invsqrt operations.
94  * Doing the SIMD rsqrt and the first Newton Raphson iteration
95  * in single precision gives full double precision accuracy.
96  */
97 static gmx_inline void
98 gmx_mm_invsqrt2_pd(__m256d in0, __m256d in1,
99                    __m256d *out0, __m256d *out1)
100 {
101     const __m256d half  = _mm256_set1_pd(0.5);
102     const __m256d three = _mm256_set1_pd(3.0);
103     __m256        s, ir;
104     __m256d       lu0, lu1;
105
106     s    =  gmx_2_m128_to_m256(_mm256_cvtpd_ps(in0), _mm256_cvtpd_ps(in1));
107     ir    = gmx_mm256_invsqrt_ps_single(s);
108     lu0   = _mm256_cvtps_pd(_mm256_castps256_ps128(ir));
109     lu1   = _mm256_cvtps_pd(_mm256_extractf128_ps(ir, 1));
110     *out0 = _mm256_mul_pd(half, _mm256_mul_pd(_mm256_sub_pd(three, _mm256_mul_pd(_mm256_mul_pd(lu0, lu0), in0)), lu0));
111     *out1 = _mm256_mul_pd(half, _mm256_mul_pd(_mm256_sub_pd(three, _mm256_mul_pd(_mm256_mul_pd(lu1, lu1), in1)), lu1));
112 }
113
114 static gmx_inline void
115 load_lj_pair_params(const real *nbfp, const int *type, int aj,
116                     __m256d *c6_S, __m256d *c12_S)
117 {
118     __m128d clj_S[UNROLLJ], c6t_S[2], c12t_S[2];
119     int     p;
120
121     for (p = 0; p < UNROLLJ; p++)
122     {
123         clj_S[p] = _mm_load_pd(nbfp+type[aj+p]*NBFP_STRIDE);
124     }
125     gmx_mm_transpose2_op_pd(clj_S[0], clj_S[1], &c6t_S[0], &c12t_S[0]);
126     gmx_mm_transpose2_op_pd(clj_S[2], clj_S[3], &c6t_S[1], &c12t_S[1]);
127     *c6_S  = gmx_2_m128d_to_m256d(c6t_S[0], c6t_S[1]);
128     *c12_S = gmx_2_m128d_to_m256d(c12t_S[0], c12t_S[1]);
129 }
130
131 static gmx_inline void
132 load_table_f(const real *tab_coul_F, __m128i ti_S, int *ti,
133              __m256d *ctab0_S, __m256d *ctab1_S)
134 {
135     __m128d ctab_S[4], tr_S[4];
136     int     j;
137
138     _mm_store_si128((__m128i *)ti, ti_S);
139     for (j = 0; j < 4; j++)
140     {
141         ctab_S[j] = _mm_loadu_pd(tab_coul_F+ti[j]);
142     }
143     /* Shuffle the force table entries to a convenient order */
144     gmx_mm_transpose2_op_pd(ctab_S[0], ctab_S[1], &tr_S[0], &tr_S[1]);
145     gmx_mm_transpose2_op_pd(ctab_S[2], ctab_S[3], &tr_S[2], &tr_S[3]);
146     *ctab0_S = gmx_2_m128d_to_m256d(tr_S[0], tr_S[2]);
147     *ctab1_S = gmx_2_m128d_to_m256d(tr_S[1], tr_S[3]);
148     /* The second force table entry should contain the difference */
149     *ctab1_S = _mm256_sub_pd(*ctab1_S, *ctab0_S);
150 }
151
152 static gmx_inline void
153 load_table_f_v(const real *tab_coul_F, const real *tab_coul_V,
154                __m128i ti_S, int *ti,
155                __m256d *ctab0_S, __m256d *ctab1_S, __m256d *ctabv_S)
156 {
157     __m128d ctab_S[8], tr_S[4];
158     int     j;
159
160     _mm_store_si128((__m128i *)ti, ti_S);
161     for (j = 0; j < 4; j++)
162     {
163         ctab_S[j] = _mm_loadu_pd(tab_coul_F+ti[j]);
164     }
165     /* Shuffle the force table entries to a convenient order */
166     gmx_mm_transpose2_op_pd(ctab_S[0], ctab_S[1], &tr_S[0], &tr_S[1]);
167     gmx_mm_transpose2_op_pd(ctab_S[2], ctab_S[3], &tr_S[2], &tr_S[3]);
168     *ctab0_S = gmx_2_m128d_to_m256d(tr_S[0], tr_S[2]);
169     *ctab1_S = gmx_2_m128d_to_m256d(tr_S[1], tr_S[3]);
170     /* The second force table entry should contain the difference */
171     *ctab1_S = _mm256_sub_pd(*ctab1_S, *ctab0_S);
172
173     for (j = 0; j < 4; j++)
174     {
175         ctab_S[4+j] = _mm_loadu_pd(tab_coul_V+ti[j]);
176     }
177     /* Shuffle the energy table entries to a single register */
178     *ctabv_S = gmx_2_m128d_to_m256d(_mm_shuffle_pd(ctab_S[4], ctab_S[5], _MM_SHUFFLE2(0, 0)), _mm_shuffle_pd(ctab_S[6], ctab_S[7], _MM_SHUFFLE2(0, 0)));
179 }
180
181 #endif /* _nbnxn_kernel_simd_utils_x86_s256d_h_ */