Add 32-bit ARM Neon SIMD support
[alexxy/gromacs.git] / src / gromacs / simd / impl_arm_neon / impl_arm_neon.h
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 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
36 #ifndef GMX_SIMD_IMPL_ARM_NEON_H
37 #define GMX_SIMD_IMPL_ARM_NEON_H
38
39 #include <math.h>
40
41 #include <arm_neon.h>
42
43 /* ARM 32-bit NEON SIMD instruction wrappers
44  *
45  * Please see documentation in gromacs/simd/simd.h for defines.
46  */
47
48 /* Capability definitions for ARM 32-bit NEON */
49 #define GMX_SIMD_HAVE_FLOAT
50 #undef  GMX_SIMD_HAVE_DOUBLE
51 #define GMX_SIMD_HAVE_HARDWARE
52 #define GMX_SIMD_HAVE_LOADU
53 #define GMX_SIMD_HAVE_STOREU
54 #define GMX_SIMD_HAVE_LOGICAL
55 #define GMX_SIMD_HAVE_FMA
56 #undef  GMX_SIMD_HAVE_FRACTION
57 #define GMX_SIMD_HAVE_FINT32
58 #define GMX_SIMD_HAVE_FINT32_EXTRACT
59 #define GMX_SIMD_HAVE_FINT32_LOGICAL
60 #define GMX_SIMD_HAVE_FINT32_ARITHMETICS
61 #undef  GMX_SIMD_HAVE_DINT32
62 #undef  GMX_SIMD_HAVE_DINT32_EXTRACT
63 #undef  GMX_SIMD_HAVE_DINT32_LOGICAL
64 #undef  GMX_SIMD_HAVE_DINT32_ARITHMETICS
65 #define GMX_SIMD4_HAVE_FLOAT
66 #undef  GMX_SIMD4_HAVE_DOUBLE
67
68 /* Implementation details */
69 #define GMX_SIMD_FLOAT_WIDTH         4
70 #undef  GMX_SIMD_DOUBLE_WIDTH
71 #define GMX_SIMD_FINT32_WIDTH        4
72 #undef  GMX_SIMD_DINT32_WIDTH
73 #define GMX_SIMD_RSQRT_BITS          8
74 #define GMX_SIMD_RCP_BITS            8
75
76 /****************************************************
77  *      SINGLE PRECISION SIMD IMPLEMENTATION        *
78  ****************************************************/
79 #define gmx_simd_float_t           float32x4_t
80 #define gmx_simd_load_f            vld1q_f32
81 #define gmx_simd_load1_f           vld1q_dup_f32
82 #define gmx_simd_set1_f            vdupq_n_f32
83 #define gmx_simd_store_f           vst1q_f32
84 #define gmx_simd_loadu_f           vld1q_f32
85 #define gmx_simd_storeu_f          vst1q_f32
86 #define gmx_simd_setzero_f()       vdupq_n_f32(0.0f)
87 #define gmx_simd_add_f             vaddq_f32
88 #define gmx_simd_sub_f             vsubq_f32
89 #define gmx_simd_mul_f             vmulq_f32
90 #ifdef __ARM_FEATURE_FMA
91 #    define gmx_simd_fmadd_f(a, b, c)  vfmaq_f32(c, b, a)
92 #    define gmx_simd_fmsub_f(a, b, c)  vnegq_f32(vfmsq_f32(c, b, a))
93 #    define gmx_simd_fnmadd_f(a, b, c) vfmaq_f32(c, b, a)
94 #    define gmx_simd_fnmsub_f(a, b, c) vnegq_f32(vfmaq_f32(c, b, a))
95 #else
96 #    define gmx_simd_fmadd_f(a, b, c)  vmlaq_f32(c, b, a)
97 #    define gmx_simd_fmsub_f(a, b, c)  vnegq_f32(vmlsq_f32(c, b, a))
98 #    define gmx_simd_fnmadd_f(a, b, c) vmlsq_f32(c, b, a)
99 #    define gmx_simd_fnmsub_f(a, b, c) vnegq_f32(vmlaq_f32(c, b, a))
100 #endif
101 #define gmx_simd_and_f(a, b)        vreinterpretq_f32_s32(vandq_s32(vreinterpretq_s32_f32(a), vreinterpretq_s32_f32(b)))
102 #define gmx_simd_andnot_f(a, b)     vreinterpretq_f32_s32(vbicq_s32(vreinterpretq_s32_f32(b), vreinterpretq_s32_f32(a)))
103 #define gmx_simd_or_f(a, b)         vreinterpretq_f32_s32(vorrq_s32(vreinterpretq_s32_f32(a), vreinterpretq_s32_f32(b)))
104 #define gmx_simd_xor_f(a, b)        vreinterpretq_f32_s32(veorq_s32(vreinterpretq_s32_f32(a), vreinterpretq_s32_f32(b)))
105 #define gmx_simd_rsqrt_f            vrsqrteq_f32
106 #define gmx_simd_rsqrt_iter_f(lu, x) vmulq_f32(lu, vrsqrtsq_f32(vmulq_f32(lu, lu), x))
107 #define gmx_simd_rcp_f              vrecpeq_f32
108 #define gmx_simd_rcp_iter_f(lu, x)   vmulq_f32(lu, vrecpsq_f32(lu, x))
109 #define gmx_simd_fabs_f(x)         vabsq_f32(x)
110 #define gmx_simd_fneg_f(x)         vnegq_f32(x)
111 #define gmx_simd_max_f             vmaxq_f32
112 #define gmx_simd_min_f             vminq_f32
113 #define gmx_simd_round_f(x)        gmx_simd_cvt_i2f(gmx_simd_cvt_f2i(x))
114 #define gmx_simd_trunc_f(x)        gmx_simd_cvt_i2f(gmx_simd_cvtt_f2i(x))
115 #define gmx_simd_fraction_f(x)     vsubq_f32(x, gmx_simd_trunc_f(x))
116 #define gmx_simd_get_exponent_f    gmx_simd_get_exponent_f_arm_neon
117 #define gmx_simd_get_mantissa_f    gmx_simd_get_mantissa_f_arm_neon
118 #define gmx_simd_set_exponent_f    gmx_simd_set_exponent_f_arm_neon
119 /* integer datatype corresponding to float: gmx_simd_fint32_t */
120 #define gmx_simd_fint32_t         int32x4_t
121 #define gmx_simd_load_fi(m)        vld1q_s32(m)
122 #define gmx_simd_set1_fi           vdupq_n_s32
123 #define gmx_simd_store_fi(m, x)    vst1q_s32(m, x)
124 #define gmx_simd_loadu_fi(m)       vld1q_s32(m)
125 #define gmx_simd_storeu_fi(m, x)   vst1q_s32(m, x)
126 #define gmx_simd_setzero_fi()      vdupq_n_s32(0)
127 #define gmx_simd_cvtt_f2i          vcvtq_s32_f32
128 #define gmx_simd_cvt_f2i(x)        vcvtq_s32_f32(gmx_simd_add_f(gmx_simd_or_f(gmx_simd_and_f(vdupq_n_f32(-0.0f), x), vdupq_n_f32(0.5f)), x))
129 #define gmx_simd_cvt_i2f           vcvtq_f32_s32
130 #define gmx_simd_extract_fi(x, i)  vgetq_lane_s32(x, i)
131 /* Integer logical ops on gmx_simd_fint32_t */
132 #define gmx_simd_slli_fi           vshlq_n_s32
133 #define gmx_simd_srli_fi           vshrq_n_s32
134 #define gmx_simd_and_fi            vandq_s32
135 #define gmx_simd_andnot_fi(a, b)   vbicq_s32(b, a)
136 #define gmx_simd_or_fi             vorrq_s32
137 #define gmx_simd_xor_fi            veorq_s32
138 /* Integer arithmetic ops on gmx_simd_fint32_t */
139 #define gmx_simd_add_fi            vaddq_s32
140 #define gmx_simd_sub_fi            vsubq_s32
141 #define gmx_simd_mul_fi            vmulq_s32
142 /* Boolean & comparison operations on gmx_simd_float_t */
143 #define gmx_simd_fbool_t           uint32x4_t
144 #define gmx_simd_cmpeq_f           vceqq_f32
145 #define gmx_simd_cmplt_f           vcltq_f32
146 #define gmx_simd_cmple_f           vcleq_f32
147 #define gmx_simd_and_fb            vandq_u32
148 #define gmx_simd_or_fb             vorrq_u32
149 #define gmx_simd_anytrue_fb        gmx_simd_anytrue_fb_arm_neon
150 #define gmx_simd_blendzero_f(a, sel)     vreinterpretq_f32_u32(vandq_u32(vreinterpretq_u32_f32(a), sel))
151 #define gmx_simd_blendnotzero_f(a, sel)  vreinterpretq_f32_u32(vbicq_u32(vreinterpretq_u32_f32(a), sel))
152 #define gmx_simd_blendv_f(a, b, sel)     vbslq_f32(sel, b, a)
153 #define gmx_simd_reduce_f(a)       gmx_simd_reduce_f_arm_neon(a)
154 /* Boolean & comparison operations on gmx_simd_fint32_t */
155 #define gmx_simd_fibool_t          uint32x4_t
156 #define gmx_simd_cmpeq_fi          vceqq_s32
157 #define gmx_simd_cmplt_fi          vcltq_s32
158 #define gmx_simd_and_fib           vandq_u32
159 #define gmx_simd_or_fib            vorrq_u32
160 #define gmx_simd_anytrue_fib       gmx_simd_anytrue_fb
161 #define gmx_simd_blendzero_fi(a, sel)     vandq_s32(a, vreinterpretq_s32_u32(sel))
162 #define gmx_simd_blendnotzero_fi(a, sel)  vbicq_s32(a, vreinterpretq_s32_u32(sel))
163 #define gmx_simd_blendv_fi(a, b, sel)     vbslq_s32(sel, b, a)
164 /* Conversions between different booleans */
165 #define gmx_simd_cvt_fb2fib(x)     (x)
166 #define gmx_simd_cvt_fib2fb(x)     (x)
167
168 /****************************************************
169  *     NO DOUBLE PRECISION SIMD AVAILABLE           *
170  ****************************************************/
171
172
173 /****************************************************
174  * SINGLE PRECISION IMPLEMENTATION HELPER FUNCTIONS *
175  ****************************************************/
176 static gmx_inline gmx_simd_float_t
177 gmx_simd_get_exponent_f_arm_neon(gmx_simd_float_t x)
178 {
179     const float32x4_t expmask    = vreinterpretq_f32_s32( vdupq_n_s32(0x7F800000) );
180     int32x4_t         iexp;
181
182     iexp = vreinterpretq_s32_f32(gmx_simd_and_f(x, expmask));
183     iexp = vsubq_s32(vshrq_n_s32(iexp, 23), vdupq_n_s32(127));
184     return vcvtq_f32_s32(iexp);
185 }
186
187
188 static gmx_inline gmx_simd_float_t
189 gmx_simd_get_mantissa_f_arm_neon(gmx_simd_float_t x)
190 {
191     const float32x4_t mantmask   = vreinterpretq_f32_s32( vdupq_n_s32(0x007FFFFF) );
192     const float32x4_t one        = vdupq_n_f32(1.0f);
193
194     /* Get mantissa */
195     x = gmx_simd_and_f(mantmask, x);
196     /* Reset zero (but correctly biased) exponent */
197     return gmx_simd_or_f(x, one);
198 }
199
200
201 static gmx_inline gmx_simd_float_t
202 gmx_simd_set_exponent_f_arm_neon(gmx_simd_float_t x)
203 {
204     int32x4_t  iexp = gmx_simd_cvt_f2i(x);
205
206     iexp = vshlq_n_s32(vaddq_s32(iexp, vdupq_n_s32(127)), 23);
207     return vreinterpretq_f32_s32(iexp);
208 }
209
210 static gmx_inline float
211 gmx_simd_reduce_f_arm_neon(gmx_simd_float_t a)
212 {
213     float32x4_t b = vextq_f32(a, a, 2);
214
215     a = vaddq_f32(a, b);
216     b = vextq_f32(a, a, 1);
217     a = vaddq_f32(a, b);
218     return vgetq_lane_f32(a, 0);
219 }
220
221 static gmx_inline int
222 gmx_simd_anytrue_fb_arm_neon(gmx_simd_fbool_t a)
223 {
224     uint32x4_t b = vextq_u32(a, a, 2);
225
226     a = gmx_simd_or_fb(a, b);
227     b = vextq_u32(a, a, 1);
228     a = gmx_simd_or_fb(a, b);
229     return (vgetq_lane_u32(a, 0) != 0);
230 }
231
232
233 /* ARM 32-bit Neon is already 4-wide in single, so just reuse float type for SIMD4 */
234 #define gmx_simd4_float_t                gmx_simd_float_t
235 #define gmx_simd4_load_f                 gmx_simd_load_f
236 #define gmx_simd4_load1_f                gmx_simd_load1_f
237 #define gmx_simd4_set1_f                 gmx_simd_set1_f
238 #define gmx_simd4_store_f                gmx_simd_store_f
239 #define gmx_simd4_loadu_f                gmx_simd_loadu_f
240 #define gmx_simd4_storeu_f               gmx_simd_storeu_f
241 #define gmx_simd4_setzero_f              gmx_simd_setzero_f
242 #define gmx_simd4_add_f                  gmx_simd_add_f
243 #define gmx_simd4_sub_f                  gmx_simd_sub_f
244 #define gmx_simd4_mul_f                  gmx_simd_mul_f
245 #define gmx_simd4_fmadd_f                gmx_simd_fmadd_f
246 #define gmx_simd4_fmsub_f                gmx_simd_fmsub_f
247 #define gmx_simd4_fnmadd_f               gmx_simd_fnmadd_f
248 #define gmx_simd4_fnmsub_f               gmx_simd_fnmsub_f
249 #define gmx_simd4_and_f                  gmx_simd_and_f
250 #define gmx_simd4_andnot_f               gmx_simd_andnot_f
251 #define gmx_simd4_or_f                   gmx_simd_or_f
252 #define gmx_simd4_xor_f                  gmx_simd_xor_f
253 #define gmx_simd4_rsqrt_f                gmx_simd_rsqrt_f
254 #define gmx_simd4_fabs_f                 gmx_simd_fabs_f
255 #define gmx_simd4_fneg_f                 gmx_simd_fneg_f
256 #define gmx_simd4_max_f                  gmx_simd_max_f
257 #define gmx_simd4_min_f                  gmx_simd_min_f
258 #define gmx_simd4_round_f                gmx_simd_round_f
259 #define gmx_simd4_trunc_f                gmx_simd_trunc_f
260 #define gmx_simd4_dotproduct3_f          gmx_simd4_dotproduct3_f_arm_neon
261 #define gmx_simd4_fbool_t                gmx_simd_fbool_t
262 #define gmx_simd4_cmpeq_f                gmx_simd_cmpeq_f
263 #define gmx_simd4_cmplt_f                gmx_simd_cmplt_f
264 #define gmx_simd4_cmple_f                gmx_simd_cmple_f
265 #define gmx_simd4_and_fb                 gmx_simd_and_fb
266 #define gmx_simd4_or_fb                  gmx_simd_or_fb
267 #define gmx_simd4_anytrue_fb             gmx_simd_anytrue_fb
268 #define gmx_simd4_blendzero_f            gmx_simd_blendzero_f
269 #define gmx_simd4_blendnotzero_f         gmx_simd_blendnotzero_f
270 #define gmx_simd4_blendv_f               gmx_simd_blendv_f
271 #define gmx_simd4_reduce_f               gmx_simd_reduce_f
272
273 /* SIMD4 Dotproduct helper function */
274 static gmx_inline float
275 gmx_simd4_dotproduct3_f_arm_neon(gmx_simd_float_t a, gmx_simd_float_t b)
276 {
277     gmx_simd_float_t  c;
278     c = gmx_simd_mul_f(a, b);
279     /* set 4th element to 0, then add all of them */
280     c = vsetq_lane_f32(0.0f, c, 3);
281     return gmx_simd_reduce_f_arm_neon(c);
282 }
283
284 #endif /* GMX_SIMD_IMPL_ARM_NEON_H */