8cc0295babb77e22823157439b40542c89035b53
[alexxy/gromacs.git] / src / gromacs / simd / tests / bootstrap_loadstore.cpp
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 #include "gmxpre.h"
36
37 #include "config.h"
38
39 /*! \internal \file
40  * \brief
41  * Separate test of SIMD load/store, before we use them in the SIMD test classes.
42  *
43  * Simple tests without using any classes/utilities, so we can use load/store
44  * functions inside our test utilities after this has passed.
45  *
46  * This file tests:
47  *
48  * - gmx_simd_align_r(),gmx_simd_align_i(),gmx_simd4_align_r(),
49  * - gmx_simd_load_r(),gmx_simd_store_r(),gmx_simd_loadu_r(),gmx_simd_storeu_r()
50  * - gmx_simd_load_i(),gmx_simd_store_i(), gmx_simd_loadu_i(),gmx_simd_storeu_i()
51  * - gmx_simd4_load_r(),gmx_simd4_store_r(), gmx_simd4_loadu_r(),gmx_simd4_storeu_r()
52  *
53  * \author Erik Lindahl <erik.lindahl@scilifelab.se>
54  * \ingroup module_simd
55  */
56
57 #include <gtest/gtest.h>
58
59 #include "gromacs/simd/simd.h"
60 #include "gromacs/utility/real.h"
61
62 namespace
63 {
64
65 /*! \cond internal */
66 /*! \addtogroup module_simd */
67 /*! \{ */
68
69 TEST(SimdBootstrapTest, gmxSimdAlign)
70 {
71 #ifdef GMX_SIMD_HAVE_REAL
72     real rdata[GMX_SIMD_REAL_WIDTH*2];
73     for (int i = 0; i < GMX_SIMD_REAL_WIDTH; i++)
74     {
75         EXPECT_EQ(((size_t)gmx_simd_align_r(&rdata[i]) & (GMX_SIMD_REAL_WIDTH*sizeof(real)-1)), (size_t)0);
76     }
77 #endif
78 #ifdef GMX_SIMD_HAVE_INT32
79     int idata[GMX_SIMD_INT32_WIDTH*2];
80     for (int i = 0; i < GMX_SIMD_INT32_WIDTH; i++)
81     {
82         EXPECT_EQ(((size_t)gmx_simd_align_i(&idata[i]) & (GMX_SIMD_INT32_WIDTH*sizeof(int)-1)), (size_t)0);
83     }
84 #endif
85 }
86
87 /*! \brief Generic routine to test load & store of SIMD, and check for side effects.
88  *
89  * The tests for load, store, unaligned load and unaligned store both for
90  * real and int are pretty much similar, so we use a template function with
91  * additional function pointers for the actual load/store calls. This would
92  * be more hacking to turn into a class, since the SIMD functionality uses
93  * macros rather than functions that can be overloaded.
94  */
95 template <typename T, typename TSimd> void
96 simdLoadStoreTester(TSimd simdLoadFn(T* mem), void simdStoreFn(T* mem, TSimd),
97                     T * simdAlignFn(T *mem),
98                     const int loadOffset, const int storeOffset, const int simdWidth)
99 {
100     /* We want simdWidth elements before the data to check we are not polluting
101      * memory. Then we need 2*simdWidth storage to be able to extract an aligned
102      * pointer, another simdWidth elements so we can create (deliberately)
103      * offset un-aligned pointers, and finally simdWidth elements at the end
104      * to test we are not polluting memory there either. Sum=5*simdWidth!
105      */
106     std::vector<T>   src(simdWidth*5);
107     std::vector<T>   dst(simdWidth*5);
108     // Make sure we have memory to check both before and after the test pointers
109     T *              pCopySrc = simdAlignFn(&src[0]) + simdWidth + loadOffset;
110     T *              pCopyDst = simdAlignFn(&dst[0]) + simdWidth + storeOffset;
111     int              i;
112
113     for (i = 0; i < simdWidth*5; i++)
114     {
115         src[i] =  1+i;
116         dst[i] = -1-i;
117     }
118
119     simdStoreFn(pCopyDst, simdLoadFn(pCopySrc));
120
121     for (i = 0; i < simdWidth; i++)
122     {
123         EXPECT_EQ(pCopySrc[i], pCopyDst[i]) << "SIMD load or store not moving data correctly for element " << i;
124     }
125
126     for (i = 0; i < simdWidth*5; i++)
127     {
128         EXPECT_EQ(src[i], (T)(1+i)) << "Side effect on source memory, i = " << i;
129         if (&dst[0]+i < pCopyDst || &dst[0]+i >= pCopyDst+simdWidth)
130         {
131             EXPECT_EQ(dst[i], (T)(-1-i)) << "Side effect on destination memory, i = " << i;
132         }
133     }
134 }
135
136 #ifdef GMX_SIMD_HAVE_REAL
137 //! Wrapper for SIMD macro to load aligned floating-point data.
138 gmx_simd_real_t wrapperSimdLoadR(real *m)
139 {
140     return gmx_simd_load_r(m);
141 }
142 //! Wrapper for SIMD macro to store to aligned floating-point data.
143 void            wrapperSimdStoreR(real *m, gmx_simd_real_t s)
144 {
145     gmx_simd_store_r(m, s);
146 }
147
148 TEST(SimdBootstrapTest, gmxSimdLoadStoreR)
149 {
150     simdLoadStoreTester(wrapperSimdLoadR, wrapperSimdStoreR, gmx_simd_align_r, 0, 0, GMX_SIMD_REAL_WIDTH);
151 }
152
153 #    ifdef GMX_SIMD_HAVE_LOADU
154 //! Wrapper for SIMD macro to load unaligned floating-point data.
155 gmx_simd_real_t WrapperSimdLoadUR(real *m)
156 {
157     return gmx_simd_loadu_r(m);
158 }
159
160 TEST(SimdBootstrapTest, gmxSimdLoadUR)
161 {
162     for (int i = 0; i < GMX_SIMD_REAL_WIDTH; i++)
163     {
164         simdLoadStoreTester(WrapperSimdLoadUR, wrapperSimdStoreR, gmx_simd_align_r, i, 0, GMX_SIMD_REAL_WIDTH);
165     }
166 }
167 #    endif
168
169 #    ifdef GMX_SIMD_HAVE_STOREU
170 //! Wrapper for SIMD macro to store to unaligned floating-point data.
171 void WrapperSimdStoreUR(real *m, gmx_simd_real_t s)
172 {
173     gmx_simd_storeu_r(m, s);
174 }
175
176 TEST(SimdBootstrapTest, gmxSimdStoreUR)
177 {
178     for (int i = 0; i < GMX_SIMD_REAL_WIDTH; i++)
179     {
180         simdLoadStoreTester(wrapperSimdLoadR, WrapperSimdStoreUR, gmx_simd_align_r, 0, i, GMX_SIMD_REAL_WIDTH);
181     }
182 }
183 #    endif
184 #endif
185
186 #ifdef GMX_SIMD_HAVE_INT32
187 // Tests for gmx_simd_int32_t load & store operations
188
189 //! Wrapper for SIMD macro to load aligned integer data.
190 gmx_simd_int32_t wrapperSimdLoadI(int *m)
191 {
192     return gmx_simd_load_i(m);
193 }
194 //! Wrapper for SIMD macro to store to aligned integer data.
195 void             wrapperSimdStoreI(int *m, gmx_simd_int32_t s)
196 {
197     gmx_simd_store_i(m, s);
198 }
199
200 TEST(SimdBootstrapTest, gmxSimdLoadStoreI)
201 {
202     simdLoadStoreTester(wrapperSimdLoadI, wrapperSimdStoreI, gmx_simd_align_i, 0, 0, GMX_SIMD_INT32_WIDTH);
203 }
204
205 #    ifdef GMX_SIMD_HAVE_LOADU
206 //! Wrapper for SIMD macro to load unaligned integer data.
207 gmx_simd_int32_t wrapperSimdLoadUI(int *m)
208 {
209     return gmx_simd_loadu_i(m);
210 }
211
212 TEST(SimdBootstrapTest, gmxSimdLoadUI)
213 {
214     for (int i = 0; i < GMX_SIMD_INT32_WIDTH; i++)
215     {
216         simdLoadStoreTester(wrapperSimdLoadUI, wrapperSimdStoreI, gmx_simd_align_i, i, 0, GMX_SIMD_INT32_WIDTH);
217     }
218 }
219 #    endif
220
221 #    ifdef GMX_SIMD_HAVE_STOREU
222 //! Wrapper for SIMD macro to store to unaligned integer data.
223 void wrapperSimdStoreUI(int *m, gmx_simd_int32_t s)
224 {
225     gmx_simd_storeu_i(m, s);
226 }
227
228 TEST(SimdBootstrapTest, gmxSimdStoreUI)
229 {
230     for (int i = 0; i < GMX_SIMD_INT32_WIDTH; i++)
231     {
232         simdLoadStoreTester(wrapperSimdLoadI, wrapperSimdStoreUI, gmx_simd_align_i, 0, i, GMX_SIMD_INT32_WIDTH);
233     }
234 }
235 #    endif
236 #endif
237
238 #ifdef GMX_SIMD4_HAVE_REAL
239 /* Tests for gmx_simd4_real_t load & store operations. Define wrapper functions
240  * for the SIMD instructions that are typically implemented as macros.
241  */
242
243 /*! \brief Separate load/store tester function for SIMD4.
244  *
245  * Due to the way SIMD variables
246  * are implemented as deep internal data, some compilers treat them as
247  * float/double with special prefixes. Unfortunately, this means that some C++
248  * compilers think an 8-wide normal real SIMD and a 4-wide SIMD4 real type
249  * cannot be overloaded (e.g. with gcc using 256-bit AVX single precision).
250  */
251 template <typename T, typename TSimd> void
252 simd4LoadStoreTester(TSimd simd4LoadFn(T* mem), void simd4StoreFn(T* mem, TSimd),
253                      T * simd4AlignFn(T *mem),
254                      const int loadOffset, const int storeOffset)
255 {
256     /* We want simdWidth elements before the data to check we are not polluting
257      * memory. Then we need 2*simdWidth storage to be able to extract an aligned
258      * pointer, another simdWidth elements so we can create (deliberately)
259      * offset un-aligned pointers, and finally simdWidth elements at the end
260      * to test we are not polluting memory there either. Sum=5*simdWidth!
261      */
262     T         src[GMX_SIMD4_WIDTH*5];
263     T         dst[GMX_SIMD4_WIDTH*5];
264     // Make sure we have memory to check both before and after the test pointers
265     T *       pCopySrc = simd4AlignFn(src) + GMX_SIMD4_WIDTH + loadOffset;
266     T *       pCopyDst = simd4AlignFn(dst) + GMX_SIMD4_WIDTH + storeOffset;
267     int       i;
268
269     for (i = 0; i < GMX_SIMD4_WIDTH*5; i++)
270     {
271         src[i] =  1+i;
272         dst[i] = -1-i;
273     }
274
275     simd4StoreFn(pCopyDst, simd4LoadFn(pCopySrc));
276
277     for (i = 0; i < GMX_SIMD4_WIDTH; i++)
278     {
279         EXPECT_EQ(pCopySrc[i], pCopyDst[i]) << "SIMD4 load or store not moving data correctly for element " << i;
280     }
281
282     for (i = 0; i < GMX_SIMD4_WIDTH*5; i++)
283     {
284         EXPECT_EQ(src[i], (T)(1+i)) << "Side effect on source memory, i = " << i;
285         if (dst+i < pCopyDst || dst+i >= pCopyDst+GMX_SIMD4_WIDTH)
286         {
287             EXPECT_EQ(dst[i], (T)(-1-i)) << "Side effect on destination memory, i = " << i;
288         }
289     }
290 }
291
292 //! Wrapper for SIMD4 macro to load aligned floating-point data.
293 gmx_simd4_real_t wrapperSimd4LoadR(real *m)
294 {
295     return gmx_simd4_load_r(m);
296 }
297 //! Wrapper for SIMD4 macro to store to aligned floating-point data.
298 void             wrapperSimd4StoreR(real *m, gmx_simd4_real_t s)
299 {
300     gmx_simd4_store_r(m, s);
301 }
302
303 TEST(SimdBootstrapTest, gmxSimd4LoadStoreR)
304 {
305     simd4LoadStoreTester(wrapperSimd4LoadR, wrapperSimd4StoreR, gmx_simd4_align_r, 0, 0);
306 }
307
308 #    ifdef GMX_SIMD_HAVE_LOADU
309 //! Wrapper for SIMD4 macro to load unaligned floating-point data.
310 gmx_simd4_real_t WrapperSimd4LoadUR(real *m)
311 {
312     return gmx_simd4_loadu_r(m);
313 }
314
315 TEST(SimdBootstrapTest, gmxSimd4LoadUR)
316 {
317     for (int i = 0; i < GMX_SIMD4_WIDTH; i++)
318     {
319         simd4LoadStoreTester(WrapperSimd4LoadUR, wrapperSimd4StoreR, gmx_simd4_align_r, i, 0);
320     }
321 }
322 #    endif
323
324 #    ifdef GMX_SIMD_HAVE_STOREU
325 //! Wrapper for SIMD4 macro to store to unaligned floating-point data.
326 void WrapperSimd4StoreUR(real *m, gmx_simd4_real_t s)
327 {
328     gmx_simd4_storeu_r(m, s);
329 }
330
331 TEST(SimdBootstrapTest, gmxSimd4StoreUR)
332 {
333     for (int i = 0; i < GMX_SIMD4_WIDTH; i++)
334     {
335         simd4LoadStoreTester(wrapperSimd4LoadR, WrapperSimd4StoreUR, gmx_simd4_align_r, 0, i);
336     }
337 }
338 #    endif
339 #endif
340
341 /*! \} */
342 /*! \endcond */
343
344 } // namespace