Work-around for ICC bug in simd-tests
[alexxy/gromacs.git] / src / gromacs / simd / tests / simd_floatingpoint_util.cpp
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 2015,2017,2019, 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 <numeric>
38
39 #include "gromacs/simd/simd.h"
40 #include "gromacs/utility/alignedallocator.h"
41 #include "gromacs/utility/basedefinitions.h"
42
43 #include "testutils/testasserts.h"
44
45 #include "simd.h"
46
47 namespace gmx
48 {
49 namespace test
50 {
51 namespace
52 {
53
54 /*! \cond internal */
55 /*! \addtogroup module_simd */
56 /*! \{ */
57
58 #if GMX_SIMD_HAVE_REAL
59
60 /*! \brief Test fixture for higher-level floating-point utility functions.
61  *
62  * Inherit from main SimdTest, add code to generate aligned memory and data.
63  */
64 class SimdFloatingpointUtilTest : public SimdTest
65 {
66     public:
67         SimdFloatingpointUtilTest()
68         {
69             // Resize vectors to get the amount of memory we need
70             integerMemory_.resize(GMX_SIMD_REAL_WIDTH);
71
72             // The total memory we allocate corresponds to two work arrays
73             // and 4 values each of GMX_SIMD_REAL_WIDTH.
74             realMemory_.resize(2*s_workMemSize_+4*GMX_SIMD_REAL_WIDTH);
75
76             offset_ = integerMemory_.data();
77             val0_   = realMemory_.data();
78             val1_   = val0_ + GMX_SIMD_REAL_WIDTH;
79             val2_   = val1_ + GMX_SIMD_REAL_WIDTH;
80             val3_   = val2_ + GMX_SIMD_REAL_WIDTH;
81             mem0_   = val3_ + GMX_SIMD_REAL_WIDTH;
82             mem1_   = mem0_ + s_workMemSize_;
83
84             // Set default values for offset and variables val0_ through val3_
85             // We cannot fill mem_ here since those values depend on the test.
86             for (int i = 0; i < GMX_SIMD_REAL_WIDTH; i++)
87             {
88                 // Use every third point to avoid a continguous access pattern
89                 offset_[i] = 3 * i;
90                 // Multiply numbers by 1+100*GMX_REAL_EPS ensures some low bits are
91                 // set too, so the tests make sure we read all bits correctly.
92                 val0_[i]   = (i      ) * (1.0 + 100*GMX_REAL_EPS);
93                 val1_[i]   = (i + 0.1) * (1.0 + 100*GMX_REAL_EPS);
94                 val2_[i]   = (i + 0.2) * (1.0 + 100*GMX_REAL_EPS);
95                 val3_[i]   = (i + 0.3) * (1.0 + 100*GMX_REAL_EPS);
96             }
97         }
98
99     protected:
100         //! \brief Size of memory work buffers
101         //
102         // To have a somewhat odd access pattern, we use every
103         // third entry, so the largest value of offset_[i] is 3*GMX_SIMD_REAL_WIDTH.
104         // Then we also allow alignments up to 16, which means the largest index in mem0_[]
105         // that we might access is 16*3*GMX_SIMD_REAL_WIDTH+3.
106         static const std::size_t                    s_workMemSize_ = 16*3*GMX_SIMD_REAL_WIDTH+4;
107
108         std::vector<int, AlignedAllocator<int> >    integerMemory_; //!< Aligned integer memory
109         std::vector<real, AlignedAllocator<real> >  realMemory_;    //!< Aligned real memory
110
111         int *   offset_;                                            //!< Pointer to offset indices, aligned memory
112         real *  val0_;                                              //!< Pointer to GMX_SIMD_REAL_WIDTH values, aligned
113         real *  val1_;                                              //!< Pointer to GMX_SIMD_REAL_WIDTH values, aligned
114         real *  val2_;                                              //!< Pointer to GMX_SIMD_REAL_WIDTH values, aligned
115         real *  val3_;                                              //!< Pointer to GMX_SIMD_REAL_WIDTH values, aligned
116
117         real *  mem0_;                                              //!< Pointer to aligned memory, s_workMemSize real values
118         real *  mem1_;                                              //!< Pointer to aligned memory, s_workMemSize real values
119 };
120
121
122
123 TEST_F(SimdFloatingpointUtilTest, gatherLoadTranspose4)
124 {
125     SimdReal        v0, v1, v2, v3;
126     SimdReal        ref0, ref1, ref2, ref3;
127     const int       nalign                 = 3;
128     int             alignmentList[nalign]  = { 4, 8, 12 };
129     int             i, j, align;
130
131     for (i = 0; i < nalign; i++)
132     {
133         align = alignmentList[i];
134         for (j = 0; j < GMX_SIMD_REAL_WIDTH; j++)
135         {
136             mem0_[align * offset_[j]    ] = val0_[j];
137             mem0_[align * offset_[j] + 1] = val1_[j];
138             mem0_[align * offset_[j] + 2] = val2_[j];
139             mem0_[align * offset_[j] + 3] = val3_[j];
140         }
141
142         ref0 = load<SimdReal>(val0_);
143         ref1 = load<SimdReal>(val1_);
144         ref2 = load<SimdReal>(val2_);
145         ref3 = load<SimdReal>(val3_);
146
147         if (align == 4)
148         {
149             gatherLoadTranspose<4>(mem0_, offset_, &v0, &v1, &v2, &v3);
150         }
151         else if (align == 8)
152         {
153             gatherLoadTranspose<8>(mem0_, offset_, &v0, &v1, &v2, &v3);
154         }
155         else if (align == 12)
156         {
157             gatherLoadTranspose<12>(mem0_, offset_, &v0, &v1, &v2, &v3);
158         }
159         else
160         {
161             FAIL();
162         }
163
164         GMX_EXPECT_SIMD_REAL_EQ(ref0, v0);
165         GMX_EXPECT_SIMD_REAL_EQ(ref1, v1);
166         GMX_EXPECT_SIMD_REAL_EQ(ref2, v2);
167         GMX_EXPECT_SIMD_REAL_EQ(ref3, v3);
168     }
169 }
170
171 TEST_F(SimdFloatingpointUtilTest, gatherLoadTranspose2)
172 {
173     SimdReal        v0, v1;
174     SimdReal        ref0, ref1;
175     const int       nalign                 = 3;
176     int             alignmentList[nalign]  = { 2, 4, c_simdBestPairAlignment };
177     int             i, j, align;
178
179     EXPECT_TRUE(c_simdBestPairAlignment <= GMX_SIMD_REAL_WIDTH);
180
181     for (i = 0; i < nalign; i++)
182     {
183         align = alignmentList[i];
184         for (j = 0; j < GMX_SIMD_REAL_WIDTH; j++)
185         {
186             mem0_[align * offset_[j]    ] = val0_[j];
187             mem0_[align * offset_[j] + 1] = val1_[j];
188         }
189
190         ref0 = load<SimdReal>(val0_);
191         ref1 = load<SimdReal>(val1_);
192
193         if (align == 2)
194         {
195             gatherLoadTranspose<2>(mem0_, offset_, &v0, &v1);
196         }
197         else if (align == 4)
198         {
199             gatherLoadTranspose<4>(mem0_, offset_, &v0, &v1);
200         }
201         else if (align == c_simdBestPairAlignment)
202         {
203             gatherLoadTranspose<c_simdBestPairAlignment>(mem0_, offset_, &v0, &v1);
204         }
205         else
206         {
207             FAIL();
208         }
209
210         GMX_EXPECT_SIMD_REAL_EQ(ref0, v0);
211         GMX_EXPECT_SIMD_REAL_EQ(ref1, v1);
212     }
213 }
214
215 TEST_F(SimdFloatingpointUtilTest, gatherLoadUTranspose3)
216 {
217     SimdReal        v0, v1, v2;
218     SimdReal        ref0, ref1, ref2;
219     const int       nalign                 = 2;
220     int             alignmentList[nalign]  = { 3, 4 };
221     int             i, j, align;
222
223     for (i = 0; i < nalign; i++)
224     {
225         align = alignmentList[i];
226         for (j = 0; j < GMX_SIMD_REAL_WIDTH; j++)
227         {
228             mem0_[align * offset_[j]    ] = val0_[j];
229             mem0_[align * offset_[j] + 1] = val1_[j];
230             mem0_[align * offset_[j] + 2] = val2_[j];
231         }
232
233         ref0 = load<SimdReal>(val0_);
234         ref1 = load<SimdReal>(val1_);
235         ref2 = load<SimdReal>(val2_);
236
237         if (align == 3)
238         {
239             gatherLoadUTranspose<3>(mem0_, offset_, &v0, &v1, &v2);
240         }
241         else if (align == 4)
242         {
243             gatherLoadUTranspose<4>(mem0_, offset_, &v0, &v1, &v2);
244         }
245         else
246         {
247             FAIL();
248         }
249
250         GMX_EXPECT_SIMD_REAL_EQ(ref0, v0);
251         GMX_EXPECT_SIMD_REAL_EQ(ref1, v1);
252         GMX_EXPECT_SIMD_REAL_EQ(ref2, v2);
253     }
254 }
255
256 TEST_F(SimdFloatingpointUtilTest, transposeScatterStoreU3)
257 {
258     SimdReal                          v0, v1, v2;
259     real                              refmem[s_workMemSize_];
260     const int                         nalign                 = 2;
261     int                               alignmentList[nalign]  = { 3, 4 };
262     int                               i, align;
263     FloatingPointTolerance            tolerance(defaultRealTolerance());
264
265     for (i = 0; i < nalign; i++)
266     {
267         align = alignmentList[i];
268
269         // Set test and reference memory to background value
270         for (std::size_t j = 0; j < s_workMemSize_; j++)
271         {
272             // Multiply by 1+100*eps to make sure low bits are also used
273             mem0_[j] = refmem[j] = (1000.0 + j) * (1.0 + 100*GMX_REAL_EPS);
274         }
275
276         for (std::size_t j = 0; j < GMX_SIMD_REAL_WIDTH; j++)
277         {
278             // set values in _reference_ memory (we will then test with mem0_, and compare)
279             refmem[align * offset_[j]    ] = val0_[j];
280             refmem[align * offset_[j] + 1] = val1_[j];
281             refmem[align * offset_[j] + 2] = val2_[j];
282         }
283
284         v0 = load<SimdReal>(val0_);
285         v1 = load<SimdReal>(val1_);
286         v2 = load<SimdReal>(val2_);
287
288         if (align == 3)
289         {
290             transposeScatterStoreU<3>(mem0_, offset_, v0, v1, v2);
291         }
292         else if (align == 4)
293         {
294             transposeScatterStoreU<4>(mem0_, offset_, v0, v1, v2);
295         }
296         else
297         {
298             FAIL();
299         }
300
301         for (std::size_t j = 0; j < s_workMemSize_; j++)
302         {
303             EXPECT_REAL_EQ_TOL(refmem[j], mem0_[j], tolerance);
304         }
305     }
306 }
307
308 TEST_F(SimdFloatingpointUtilTest, transposeScatterIncrU3)
309 {
310     SimdReal                          v0, v1, v2;
311     real                              refmem[s_workMemSize_];
312     const int                         nalign                 = 2;
313     int                               alignmentList[nalign]  = { 3, 4 };
314     int                               i, align;
315     FloatingPointTolerance            tolerance(defaultRealTolerance());
316
317     for (i = 0; i < nalign; i++)
318     {
319         align = alignmentList[i];
320
321         // Set test and reference memory to background value
322         for (std::size_t j = 0; j < s_workMemSize_; j++)
323         {
324             // Multiply by 1+100*eps to make sure low bits are also used
325             mem0_[j] = refmem[j] = (1000.0 + j) * (1.0 + 100*GMX_REAL_EPS);
326         }
327
328         for (std::size_t j = 0; j < GMX_SIMD_REAL_WIDTH; j++)
329         {
330             // Add values to _reference_ memory (we will then test with mem0_, and compare)
331             refmem[align * offset_[j]    ] += val0_[j];
332             refmem[align * offset_[j] + 1] += val1_[j];
333             refmem[align * offset_[j] + 2] += val2_[j];
334         }
335
336         v0 = load<SimdReal>(val0_);
337         v1 = load<SimdReal>(val1_);
338         v2 = load<SimdReal>(val2_);
339
340         if (align == 3)
341         {
342             transposeScatterIncrU<3>(mem0_, offset_, v0, v1, v2);
343         }
344         else if (align == 4)
345         {
346             transposeScatterIncrU<4>(mem0_, offset_, v0, v1, v2);
347         }
348         else
349         {
350             FAIL();
351         }
352
353         for (std::size_t j = 0; j < s_workMemSize_; j++)
354         {
355             EXPECT_REAL_EQ_TOL(refmem[j], mem0_[j], tolerance);
356         }
357     }
358 }
359
360 TEST_F(SimdFloatingpointUtilTest, transposeScatterIncrU3Overlapping)
361 {
362     SimdReal                          v0, v1, v2;
363     real                              refmem[s_workMemSize_];
364     FloatingPointTolerance            tolerance(defaultRealTolerance());
365
366     // Alter offset_ to make all entries point to the same (first) value, so all entries will overlap
367     for (std::size_t j = 0; j < GMX_SIMD_REAL_WIDTH; j++)
368     {
369         offset_[j] = 0;
370     }
371
372     // Set test and reference memory to background value
373     for (std::size_t j = 0; j < s_workMemSize_; j++)
374     {
375         // Multiply by 1+100*eps to make sure low bits are also used
376         mem0_[j] = refmem[j] = (1000.0 + j) * (1.0 + 100*GMX_REAL_EPS);
377     }
378
379     for (std::size_t j = 0; j < GMX_SIMD_REAL_WIDTH; j++)
380     {
381         // Add values to _reference_ memory (we will then test with mem0_, and compare)
382         refmem[3 * offset_[j]    ] += val0_[j];
383         refmem[3 * offset_[j] + 1] += val1_[j];
384         refmem[3 * offset_[j] + 2] += val2_[j];
385     }
386
387     v0 = load<SimdReal>(val0_);
388     v1 = load<SimdReal>(val1_);
389     v2 = load<SimdReal>(val2_);
390
391     transposeScatterIncrU<3>(mem0_, offset_, v0, v1, v2);
392
393     for (std::size_t j = 0; j < s_workMemSize_; j++)
394     {
395         EXPECT_REAL_EQ_TOL(refmem[j], mem0_[j], tolerance);
396     }
397 }
398
399 TEST_F(SimdFloatingpointUtilTest, transposeScatterDecrU3)
400 {
401     SimdReal                          v0, v1, v2;
402     real                              refmem[s_workMemSize_];
403     const int                         nalign                 = 2;
404     int                               alignmentList[nalign]  = { 3, 4 };
405     int                               i, align;
406     FloatingPointTolerance            tolerance(defaultRealTolerance());
407
408     for (i = 0; i < nalign; i++)
409     {
410         align = alignmentList[i];
411
412         // Set test and reference memory to background value
413         for (std::size_t j = 0; j < s_workMemSize_; j++)
414         {
415             // Multiply by 1+100*eps to make sure low bits are also used
416             mem0_[j] = refmem[j] = (1000.0 + j) * (1.0 + 100*GMX_REAL_EPS);
417         }
418
419         for (std::size_t j = 0; j < GMX_SIMD_REAL_WIDTH; j++)
420         {
421             // Subtract values from _reference_ memory (we will then test with mem0_, and compare)
422             refmem[align * offset_[j]    ] -= val0_[j];
423             refmem[align * offset_[j] + 1] -= val1_[j];
424             refmem[align * offset_[j] + 2] -= val2_[j];
425         }
426
427         v0 = load<SimdReal>(val0_);
428         v1 = load<SimdReal>(val1_);
429         v2 = load<SimdReal>(val2_);
430
431         if (align == 3)
432         {
433             transposeScatterDecrU<3>(mem0_, offset_, v0, v1, v2);
434         }
435         else if (align == 4)
436         {
437             transposeScatterDecrU<4>(mem0_, offset_, v0, v1, v2);
438         }
439         else
440         {
441             FAIL();
442         }
443
444         for (std::size_t j = 0; j < s_workMemSize_; j++)
445         {
446             EXPECT_REAL_EQ_TOL(refmem[j], mem0_[j], tolerance);
447         }
448     }
449 }
450
451 TEST_F(SimdFloatingpointUtilTest, transposeScatterDecrU3Overlapping)
452 {
453     SimdReal                          v0, v1, v2;
454     real                              refmem[s_workMemSize_];
455     FloatingPointTolerance            tolerance(defaultRealTolerance());
456
457     // Alter offset_ to make all entries point to the same (first) value, so all entries will overlap
458     for (std::size_t j = 0; j < GMX_SIMD_REAL_WIDTH; j++)
459     {
460         offset_[j] = 0;
461     }
462
463     // Set test and reference memory to background value
464     for (std::size_t j = 0; j < s_workMemSize_; j++)
465     {
466         // Multiply by 1+100*eps to make sure low bits are also used
467         mem0_[j] = refmem[j] = (1000.0 + j) * (1.0 + 100*GMX_REAL_EPS);
468     }
469
470 #ifdef __INTEL_COMPILER  //Bug in (at least) 19u1 and 18u5 (03424712)
471     #pragma novector
472 #endif
473     for (std::size_t j = 0; j < GMX_SIMD_REAL_WIDTH; j++)
474     {
475         // Subtract values from _reference_ memory (we will then test with mem0_, and compare)
476         refmem[3 * offset_[j]    ] -= val0_[j];
477         refmem[3 * offset_[j] + 1] -= val1_[j];
478         refmem[3 * offset_[j] + 2] -= val2_[j];
479     }
480
481     v0 = load<SimdReal>(val0_);
482     v1 = load<SimdReal>(val1_);
483     v2 = load<SimdReal>(val2_);
484
485     transposeScatterDecrU<3>(mem0_, offset_, v0, v1, v2);
486
487     for (std::size_t j = 0; j < s_workMemSize_; j++)
488     {
489         EXPECT_REAL_EQ_TOL(refmem[j], mem0_[j], tolerance);
490     }
491 }
492
493 TEST_F(SimdFloatingpointUtilTest, expandScalarsToTriplets)
494 {
495     SimdReal        vs, v0, v1, v2;
496     int             i;
497
498     for (i = 0; i < GMX_SIMD_REAL_WIDTH; i++)
499     {
500         mem0_[i] = i;
501     }
502
503     vs = load<SimdReal>(mem0_);
504
505     expandScalarsToTriplets(vs, &v0, &v1, &v2);
506
507     store(val0_, v0);
508     store(val1_, v1);
509     store(val2_, v2);
510
511     for (i = 0; i < GMX_SIMD_REAL_WIDTH; i++)
512     {
513         EXPECT_EQ(i / 3, val0_[i]);
514         EXPECT_EQ((i + GMX_SIMD_REAL_WIDTH) / 3, val1_[i]);
515         EXPECT_EQ((i + 2 * GMX_SIMD_REAL_WIDTH) / 3, val2_[i]);
516     }
517 }
518
519
520 TEST_F(SimdFloatingpointUtilTest, gatherLoadBySimdIntTranspose4)
521 {
522     SimdReal         v0, v1, v2, v3;
523     SimdReal         ref0, ref1, ref2, ref3;
524     SimdInt32        simdoffset;
525     const int        nalign                 = 3;
526     int              alignmentList[nalign]  = { 4, 8, 12 };
527     int              i, j, align;
528
529     for (i = 0; i < nalign; i++)
530     {
531         align = alignmentList[i];
532         for (j = 0; j < GMX_SIMD_REAL_WIDTH; j++)
533         {
534             mem0_[align * offset_[j]    ] = val0_[j];
535             mem0_[align * offset_[j] + 1] = val1_[j];
536             mem0_[align * offset_[j] + 2] = val2_[j];
537             mem0_[align * offset_[j] + 3] = val3_[j];
538         }
539
540         simdoffset = load<SimdInt32>(offset_);
541         ref0       = load<SimdReal>(val0_);
542         ref1       = load<SimdReal>(val1_);
543         ref2       = load<SimdReal>(val2_);
544         ref3       = load<SimdReal>(val3_);
545
546         if (align == 4)
547         {
548             gatherLoadBySimdIntTranspose<4>(mem0_, simdoffset, &v0, &v1, &v2, &v3);
549         }
550         else if (align == 8)
551         {
552             gatherLoadBySimdIntTranspose<8>(mem0_, simdoffset, &v0, &v1, &v2, &v3);
553         }
554         else if (align == 12)
555         {
556             gatherLoadBySimdIntTranspose<12>(mem0_, simdoffset, &v0, &v1, &v2, &v3);
557         }
558         else
559         {
560             FAIL();
561         }
562
563         GMX_EXPECT_SIMD_REAL_EQ(ref0, v0);
564         GMX_EXPECT_SIMD_REAL_EQ(ref1, v1);
565         GMX_EXPECT_SIMD_REAL_EQ(ref2, v2);
566         GMX_EXPECT_SIMD_REAL_EQ(ref3, v3);
567     }
568 }
569
570
571 TEST_F(SimdFloatingpointUtilTest, gatherLoadBySimdIntTranspose2)
572 {
573     SimdReal         v0, v1;
574     SimdReal         ref0, ref1;
575     SimdInt32        simdoffset;
576     const int        nalign                 = 3;
577     int              alignmentList[nalign]  = { 4, 8, 12 };
578     int              i, j, align;
579
580     for (i = 0; i < nalign; i++)
581     {
582         align = alignmentList[i];
583         for (j = 0; j < GMX_SIMD_REAL_WIDTH; j++)
584         {
585             mem0_[align * offset_[j]    ] = val0_[j];
586             mem0_[align * offset_[j] + 1] = val1_[j];
587         }
588
589         simdoffset = load<SimdInt32>(offset_);
590         ref0       = load<SimdReal>(val0_);
591         ref1       = load<SimdReal>(val1_);
592
593         if (align == 4)
594         {
595             gatherLoadBySimdIntTranspose<4>(mem0_, simdoffset, &v0, &v1);
596         }
597         else if (align == 8)
598         {
599             gatherLoadBySimdIntTranspose<8>(mem0_, simdoffset, &v0, &v1);
600         }
601         else if (align == 12)
602         {
603             gatherLoadBySimdIntTranspose<12>(mem0_, simdoffset, &v0, &v1);
604         }
605         else
606         {
607             FAIL();
608         }
609
610         GMX_EXPECT_SIMD_REAL_EQ(ref0, v0);
611         GMX_EXPECT_SIMD_REAL_EQ(ref1, v1);
612     }
613 }
614
615 #if GMX_SIMD_HAVE_GATHER_LOADU_BYSIMDINT_TRANSPOSE_REAL
616 TEST_F(SimdFloatingpointUtilTest, gatherLoadUBySimdIntTranspose2)
617 {
618     SimdReal         v0, v1;
619     SimdReal         ref0, ref1;
620     SimdInt32        simdoffset;
621     const int        nalign                 = 3;
622     int              alignmentList[nalign]  = { 1, 3, 5 };
623     int              i, j, align;
624
625     for (i = 0; i < nalign; i++)
626     {
627         align = alignmentList[i];
628         for (j = 0; j < GMX_SIMD_REAL_WIDTH; j++)
629         {
630             mem0_[align * offset_[j]    ] = val0_[j];
631             mem0_[align * offset_[j] + 1] = val1_[j];
632         }
633
634         simdoffset = load<SimdInt32>(offset_);
635         ref0       = load<SimdReal>(val0_);
636         ref1       = load<SimdReal>(val1_);
637
638         if (align == 1)
639         {
640             gatherLoadUBySimdIntTranspose<1>(mem0_, simdoffset, &v0, &v1);
641         }
642         else if (align == 3)
643         {
644             gatherLoadUBySimdIntTranspose<3>(mem0_, simdoffset, &v0, &v1);
645         }
646         else if (align == 5)
647         {
648             gatherLoadUBySimdIntTranspose<5>(mem0_, simdoffset, &v0, &v1);
649         }
650         else
651         {
652             FAIL();
653         }
654
655         GMX_EXPECT_SIMD_REAL_EQ(ref0, v0);
656         GMX_EXPECT_SIMD_REAL_EQ(ref1, v1);
657     }
658 }
659 #endif      // GMX_SIMD_HAVE_GATHER_LOADU_BYSIMDINT_TRANSPOSE_REAL
660
661 TEST_F(SimdFloatingpointUtilTest, reduceIncr4Sum)
662 {
663     int                               i;
664     SimdReal                          v0, v1, v2, v3;
665     real                              sum0, sum1, sum2, sum3, tstsum;
666     FloatingPointTolerance            tolerance(defaultRealTolerance());
667
668     v0 = load<SimdReal>(val0_);
669     v1 = load<SimdReal>(val1_);
670     v2 = load<SimdReal>(val2_);
671     v3 = load<SimdReal>(val3_);
672
673     sum0 = sum1 = sum2 = sum3 = 0;
674     for (i = 0; i < GMX_SIMD_REAL_WIDTH; i++)
675     {
676         sum0 += val0_[i];
677         sum1 += val1_[i];
678         sum2 += val2_[i];
679         sum3 += val3_[i];
680     }
681
682     // Just put some numbers in memory so we check the addition is correct
683     mem0_[0] = c0;
684     mem0_[1] = c1;
685     mem0_[2] = c2;
686     mem0_[3] = c3;
687
688     tstsum = reduceIncr4ReturnSum(mem0_, v0, v1, v2, v3);
689
690     EXPECT_REAL_EQ_TOL(c0 + sum0, mem0_[0], tolerance);
691     EXPECT_REAL_EQ_TOL(c1 + sum1, mem0_[1], tolerance);
692     EXPECT_REAL_EQ_TOL(c2 + sum2, mem0_[2], tolerance);
693     EXPECT_REAL_EQ_TOL(c3 + sum3, mem0_[3], tolerance);
694
695     EXPECT_REAL_EQ_TOL(sum0 + sum1 + sum2 + sum3, tstsum, tolerance);
696 }
697
698 #if GMX_SIMD_HAVE_HSIMD_UTIL_REAL
699
700 TEST_F(SimdFloatingpointUtilTest, loadDualHsimd)
701 {
702     SimdReal v0, v1;
703
704     // Point p to the upper half of val0_
705     real * p = val0_ + GMX_SIMD_REAL_WIDTH / 2;
706
707     v0 = load<SimdReal>(val0_);
708     v1 = loadDualHsimd(val0_, p);
709
710     GMX_EXPECT_SIMD_REAL_EQ(v0, v1);
711 }
712
713 TEST_F(SimdFloatingpointUtilTest, loadDuplicateHsimd)
714 {
715     SimdReal        v0, v1;
716     int             i;
717     // Point p to the upper half of val0_
718     real          * p = val0_ + GMX_SIMD_REAL_WIDTH / 2;
719     // Copy data so upper half is identical to lower
720     for (i = 0; i < GMX_SIMD_REAL_WIDTH / 2; i++)
721     {
722         p[i] = val0_[i];
723     }
724
725     v0 = load<SimdReal>(val0_);
726     v1 = loadDuplicateHsimd(val0_);
727
728     GMX_EXPECT_SIMD_REAL_EQ(v0, v1);
729 }
730
731
732 TEST_F(SimdFloatingpointUtilTest, loadU1DualHsimd)
733 {
734     SimdReal        v0, v1;
735     int             i;
736     real            data[2] = { 1, 2 };
737
738     // Point p to the upper half of val0_
739     real * p = val0_ + GMX_SIMD_REAL_WIDTH / 2;
740     // Set all low elements to data[0], an high to data[1]
741     for (i = 0; i < GMX_SIMD_REAL_WIDTH / 2; i++)
742     {
743         val0_[i] = data[0];
744         p[i]     = data[1];
745     }
746
747     v0 = load<SimdReal>(val0_);
748     v1 = loadU1DualHsimd(data);
749
750     GMX_EXPECT_SIMD_REAL_EQ(v0, v1);
751 }
752
753
754 TEST_F(SimdFloatingpointUtilTest, storeDualHsimd)
755 {
756     SimdReal        v0;
757     int             i;
758
759     // Point p to the upper half of val0_
760     real * p = val0_ + GMX_SIMD_REAL_WIDTH / 2;
761
762     v0 = load<SimdReal>(val2_);
763     storeDualHsimd(val0_, p, v0);
764
765     for (i = 0; i < GMX_SIMD_REAL_WIDTH; i++)
766     {
767         EXPECT_EQ(val2_[i], val0_[i]);
768     }
769 }
770
771 TEST_F(SimdFloatingpointUtilTest, incrDualHsimd)
772 {
773     real            reference[GMX_SIMD_REAL_WIDTH];
774     SimdReal        v0;
775
776     // Create reference values
777     for (std::size_t i = 0; i < GMX_SIMD_REAL_WIDTH; i++)
778     {
779         reference[i] = val0_[i] + val2_[i];
780     }
781
782     // Point p to the upper half of val0_
783     real * p = val0_ + GMX_SIMD_REAL_WIDTH / 2;
784
785     v0 = load<SimdReal>(val2_);
786     incrDualHsimd(val0_, p, v0);
787
788     for (std::size_t i = 0; i < GMX_SIMD_REAL_WIDTH; i++)
789     {
790         EXPECT_EQ(reference[i], val0_[i]);
791     }
792 }
793
794 TEST_F(SimdFloatingpointUtilTest, incrDualHsimdOverlapping)
795 {
796     real            reference[GMX_SIMD_REAL_WIDTH/2];
797     SimdReal        v0;
798
799     // Create reference values
800     for (std::size_t i = 0; i < GMX_SIMD_REAL_WIDTH/2; i++)
801     {
802         reference[i] = val0_[i] + val2_[i] + val2_[GMX_SIMD_REAL_WIDTH/2+i];
803     }
804
805     v0 = load<SimdReal>(val2_);
806     incrDualHsimd(val0_, val0_, v0);
807
808     for (std::size_t i = 0; i < GMX_SIMD_REAL_WIDTH/2; i++)
809     {
810         EXPECT_EQ(reference[i], val0_[i]);
811     }
812 }
813
814 TEST_F(SimdFloatingpointUtilTest, decrHsimd)
815 {
816     SimdReal                          v0;
817     real                              ref[GMX_SIMD_REAL_WIDTH / 2];
818     int                               i;
819     FloatingPointTolerance            tolerance(defaultRealTolerance());
820
821     // Point p to the upper half of val1_
822     real * p = val1_ + GMX_SIMD_REAL_WIDTH / 2;
823     for (i = 0; i < GMX_SIMD_REAL_WIDTH / 2; i++)
824     {
825         ref[i] = val0_[i] - ( val1_[i] + p[i] );
826     }
827
828     v0 = load<SimdReal>(val1_);
829     decrHsimd(val0_, v0);
830
831     for (i = 0; i < GMX_SIMD_REAL_WIDTH / 2; i++)
832     {
833         EXPECT_REAL_EQ_TOL(ref[i], val0_[i], tolerance);
834     }
835 }
836
837
838 TEST_F(SimdFloatingpointUtilTest, gatherLoadTranspose2Hsimd)
839 {
840     SimdReal        v0, v1;
841     SimdReal        ref0, ref1;
842
843     const int       nalign                 = 3;
844     int             alignmentList[nalign]  = { 2, 4, c_simdBestPairAlignment };
845     int             i, j, align;
846
847     for (i = 0; i < nalign; i++)
848     {
849         align = alignmentList[i];
850         for (j = 0; j < GMX_SIMD_REAL_WIDTH / 2; j++)
851         {
852             // Use mem0_ as base for lower half
853             mem0_[align * offset_[j]    ] = val0_[j];
854             mem0_[align * offset_[j] + 1] = val1_[j];
855             // Use mem1_ as base for upper half
856             mem1_[align * offset_[j]    ] = val0_[GMX_SIMD_REAL_WIDTH / 2 + j];
857             mem1_[align * offset_[j] + 1] = val1_[GMX_SIMD_REAL_WIDTH / 2 + j];
858
859         }
860
861         ref0 = load<SimdReal>(val0_);
862         ref1 = load<SimdReal>(val1_);
863
864         if (align == 2)
865         {
866             gatherLoadTransposeHsimd<2>(mem0_, mem1_, offset_, &v0, &v1);
867         }
868         else if (align == 4)
869         {
870             gatherLoadTransposeHsimd<4>(mem0_, mem1_, offset_, &v0, &v1);
871         }
872         else if (align == c_simdBestPairAlignment)
873         {
874             gatherLoadTransposeHsimd<c_simdBestPairAlignment>(mem0_, mem1_, offset_, &v0, &v1);
875         }
876         else
877         {
878             FAIL();
879         }
880
881         GMX_EXPECT_SIMD_REAL_EQ(ref0, v0);
882         GMX_EXPECT_SIMD_REAL_EQ(ref1, v1);
883     }
884 }
885
886
887 TEST_F(SimdFloatingpointUtilTest, reduceIncr4SumHsimd)
888 {
889     int                               i;
890     SimdReal                          v0, v1;
891     real                              sum0, sum1, sum2, sum3, tstsum;
892     FloatingPointTolerance            tolerance(defaultRealTolerance());
893
894     // Use the half-SIMD storage in memory val0_ and val1_.
895     v0 = load<SimdReal>(val0_);
896     v1 = load<SimdReal>(val1_);
897
898     sum0 = sum1 = sum2 = sum3 = 0;
899     for (i = 0; i < GMX_SIMD_REAL_WIDTH / 2; i++)
900     {
901         sum0 += val0_[i];
902         sum1 += val0_[GMX_SIMD_REAL_WIDTH / 2 + i];
903         sum2 += val1_[i];
904         sum3 += val1_[GMX_SIMD_REAL_WIDTH / 2 + i];
905     }
906
907     // Just put some numbers in memory so we check the addition is correct
908     mem0_[0] = c0;
909     mem0_[1] = c1;
910     mem0_[2] = c2;
911     mem0_[3] = c3;
912
913     tstsum = reduceIncr4ReturnSumHsimd(mem0_, v0, v1);
914
915     EXPECT_REAL_EQ_TOL(c0 + sum0, mem0_[0], tolerance);
916     EXPECT_REAL_EQ_TOL(c1 + sum1, mem0_[1], tolerance);
917     EXPECT_REAL_EQ_TOL(c2 + sum2, mem0_[2], tolerance);
918     EXPECT_REAL_EQ_TOL(c3 + sum3, mem0_[3], tolerance);
919
920     EXPECT_REAL_EQ_TOL(sum0 + sum1 + sum2 + sum3, tstsum, tolerance);
921 }
922
923 #endif      // GMX_SIMD_HAVE_HSIMD_UTIL_REAL
924
925 //Test Currently doesn't work for GMX_SIMD_REAL_WIDTH<4. Should be fixed by having GMX_EXPECT_SIMD_REAL_EQ which works for both Simd and Simd4
926 #if GMX_SIMD_HAVE_4NSIMD_UTIL_REAL && GMX_SIMD_REAL_WIDTH >= 4
927
928 TEST_F(SimdFloatingpointUtilTest, loadUNDuplicate4)
929 {
930     Simd4NReal      v0, v1;
931     int             i;
932     real            data[GMX_SIMD_REAL_WIDTH/4];
933     std::iota(data, data+GMX_SIMD_REAL_WIDTH/4, 1);
934
935 #if defined __ICC && __ICC == 1800 || defined __ICL && __ICL == 1800
936 #pragma novector /* Work-around for incorrect vectorization for AVX_512(_KNL) */
937 #endif
938     for (i = 0; i < GMX_SIMD_REAL_WIDTH / 4; i++)
939     {
940         val0_[i*4] = val0_[i*4+1] = val0_[i*4+2] = val0_[i*4+3] = data[i];
941     }
942
943     v0 = load<Simd4NReal>(val0_);
944     v1 = loadUNDuplicate4(data);
945
946     GMX_EXPECT_SIMD_REAL_EQ(v0, v1);
947 }
948
949 TEST_F(SimdFloatingpointUtilTest, load4DuplicateN)
950 {
951     Simd4NReal        v0, v1;
952     int               i;
953     real              data[4] = { 1, 2, 3, 4};
954
955     for (i = 0; i < GMX_SIMD_REAL_WIDTH / 4; i++)
956     {
957         val0_[i*4]   = data[0];
958         val0_[i*4+1] = data[1];
959         val0_[i*4+2] = data[2];
960         val0_[i*4+3] = data[3];
961     }
962
963     v0 = load<Simd4NReal>(val0_);
964     v1 = load4DuplicateN(val0_);
965
966     GMX_EXPECT_SIMD_REAL_EQ(v0, v1);
967 }
968
969 TEST_F(SimdFloatingpointUtilTest, loadU4NOffset)
970 {
971     constexpr int   offset  = 6; //non power of 2
972     constexpr int   dataLen = 4+offset*(GMX_SIMD_REAL_WIDTH/4-1);
973     real            data[dataLen];
974     std::iota(data, data+dataLen, 1);
975
976     for (int i = 0; i < GMX_SIMD_REAL_WIDTH / 4; i++)
977     {
978         val0_[i*4]   = data[0+offset*i];
979         val0_[i*4+1] = data[1+offset*i];
980         val0_[i*4+2] = data[2+offset*i];
981         val0_[i*4+3] = data[3+offset*i];
982     }
983
984     const Simd4NReal v0 = load<Simd4NReal>(val0_);
985     const Simd4NReal v1 = loadU4NOffset(data, offset);
986
987     GMX_EXPECT_SIMD_REAL_EQ(v0, v1);
988 }
989
990 #endif      // GMX_SIMD_HAVE_4NSIMD_UTIL_REAL
991
992 #endif      // GMX_SIMD_HAVE_REAL
993
994 /*! \} */
995 /*! \endcond */
996
997 }      // namespace
998 }      // namespace
999 }      // namespace