SYCL: Avoid using no_init read accessor in rocFFT
[alexxy/gromacs.git] / src / gromacs / simd / tests / simd.h
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
5  * Copyright (c) 2019,2020, by the GROMACS development team, led by
6  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
7  * and including many others, as listed in the AUTHORS file in the
8  * top-level source directory and at http://www.gromacs.org.
9  *
10  * GROMACS is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Lesser General Public License
12  * as published by the Free Software Foundation; either version 2.1
13  * of the License, or (at your option) any later version.
14  *
15  * GROMACS is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public
21  * License along with GROMACS; if not, see
22  * http://www.gnu.org/licenses, or write to the Free Software Foundation,
23  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
24  *
25  * If you want to redistribute modifications to GROMACS, please
26  * consider that scientific software is very special. Version
27  * control is crucial - bugs must be traceable. We will be happy to
28  * consider code for inclusion in the official distribution, but
29  * derived work must not be called official GROMACS. Details are found
30  * in the README & COPYING files - if they are missing, get the
31  * official version at http://www.gromacs.org.
32  *
33  * To help us fund GROMACS development, we humbly ask that you cite
34  * the research papers on the package. Check out http://www.gromacs.org.
35  */
36 #ifndef GMX_SIMD_TESTS_SIMD_H
37 #define GMX_SIMD_TESTS_SIMD_H
38
39 /*! \internal \file
40  * \brief
41  * Declares fixture for testing of normal SIMD (not SIMD4) functionality.
42  *
43  * The SIMD tests are both simple and complicated. The actual testing logic
44  * is \a very straightforward since we just need to test single values against
45  * the math library, and for some math functions we need to do it in a loop.
46  * This could have been achieved in minutes with the default Google Test tools,
47  * if it wasn't for the problem that we cannot access or compare SIMD contents
48  * directly without using lots of other SIMD functionality. For this reason
49  * we have separate the basic testing of load/store operations into a separate
50  * bootstrapping test. Once this works, we use a set of utility routines to
51  * convert SIMD contents to/from std:vector<> and perform the rest of the tests,
52  * which then can farmed out to the base class SimdBaseTest that is common
53  * to SIMD and SIMD4.
54  *
55  * Another complication is that the width of the SIMD implementation will
56  * depend on the hardware and precision. For some simple operations it is
57  * sufficient to set all SIMD elements to the same value, and check that the
58  * result is present in all elements. However, for a few more complex
59  * instructions that might rely on shuffling under-the-hood it is important
60  * that we can test operations with different elements. We achieve this by
61  * having test code that can initialize a SIMD variable from an std::vector
62  * of arbitrary length; the vector is simply repeated to fill all elements in
63  * the SIMD variable. We also have similar routines to compare a SIMD result
64  * with values in a vector, which returns true iff all elements match.
65  *
66  * This way we can write simple tests that use different values for all SIMD
67  * elements. Personally I like using vectors of length 3, since this means
68  * there are no simple repeated patterns in low/high halves of SIMD variables
69  * that are 2,4,8,or 16 elements wide, and we still don't have to care about
70  * the exact SIMD width of the underlying implementation.
71  *
72  * Note that this utility uses a few SIMD load/store instructions internally -
73  * those have been tested separately in the bootstrap_loadstore.cpp file.
74  *
75  * \author Erik Lindahl <erik.lindahl@scilifelab.se>
76  * \ingroup module_simd
77  */
78 #include <vector>
79
80 #include <gtest/gtest.h>
81
82 #include "gromacs/simd/simd.h"
83
84 #include "base.h"
85 #include "data.h"
86
87 #if GMX_SIMD
88
89 namespace gmx
90 {
91 namespace test
92 {
93
94
95 /*! \cond internal */
96 /*! \addtogroup module_simd */
97 /*! \{ */
98
99 /* Unfortunately we cannot keep static SIMD constants in the test fixture class.
100  * The problem is that SIMD memory need to be aligned, and in particular
101  * this applies to automatic storage of variables in classes. For SSE registers
102  * this means 16-byte alignment (which seems to work), but AVX requires 32-bit
103  * alignment. At least both gcc-4.7.3 and Apple clang-5.0 (OS X 10.9) fail to
104  * align these variables when they are stored as data in a class.
105  *
106  * In theory we could set some of these on-the-fly e.g. with setSimdFrom3R()
107  * instead (although that would mean repeating code between tests), but many of
108  * the constants depend on the current precision not to mention they
109  * occasionally have many digits that need to be exactly right, and keeping
110  * them in a single place makes sure they are consistent.
111  */
112 #    if GMX_SIMD_HAVE_REAL
113 extern const SimdReal rSimd_c0c1c2; //!< c0,c1,c2 repeated
114 extern const SimdReal rSimd_c3c4c5; //!< c3,c4,c5 repeated
115 extern const SimdReal rSimd_c6c7c8; //!< c6,c7,c8 repeated
116 extern const SimdReal rSimd_c3c0c4; //!< c3,c0,c4 repeated
117 extern const SimdReal rSimd_c4c6c8; //!< c4,c6,c8 repeated
118 extern const SimdReal rSimd_c7c2c3; //!< c7,c2,c3 repeated
119 extern const SimdReal rSimd_m0m1m2; //!< -c0,-c1,-c2 repeated
120 extern const SimdReal rSimd_m3m0m4; //!< -c3,-c0,-c4 repeated
121
122 extern const SimdReal rSimd_2p25;  //!< Value that rounds down.
123 extern const SimdReal rSimd_3p25;  //!< Value that rounds down.
124 extern const SimdReal rSimd_3p75;  //!< Value that rounds up.
125 extern const SimdReal rSimd_m2p25; //!< Negative value that rounds up.
126 extern const SimdReal rSimd_m3p25; //!< Negative value that rounds up.
127 extern const SimdReal rSimd_m3p75; //!< Negative value that rounds down.
128 //! Three large floating-point values whose exponents are >32.
129 extern const SimdReal rSimd_Exp;
130
131 #        if GMX_SIMD_HAVE_LOGICAL
132 extern const SimdReal rSimd_logicalA;         //!< Bit pattern to test logical ops
133 extern const SimdReal rSimd_logicalB;         //!< Bit pattern to test logical ops
134 extern const SimdReal rSimd_logicalResultOr;  //!< Result or bitwise 'or' of A and B
135 extern const SimdReal rSimd_logicalResultAnd; //!< Result or bitwise 'and' of A and B
136 #        endif                                // GMX_SIMD_HAVE_LOGICAL
137
138 #        if GMX_SIMD_HAVE_DOUBLE && GMX_DOUBLE
139 // Make sure we also test exponents outside single precision when we use double
140 extern const SimdReal rSimd_ExpDouble1;
141 extern const SimdReal rSimd_ExpDouble2;
142 #        endif
143 // Magic FP numbers corresponding to specific bit patterns
144 extern const SimdReal rSimd_Bits1; //!< Pattern F0 repeated to fill single/double.
145 extern const SimdReal rSimd_Bits2; //!< Pattern CC repeated to fill single/double.
146 extern const SimdReal rSimd_Bits3; //!< Pattern C0 repeated to fill single/double.
147 extern const SimdReal rSimd_Bits4; //!< Pattern 0C repeated to fill single/double.
148 extern const SimdReal rSimd_Bits5; //!< Pattern FC repeated to fill single/double.
149 extern const SimdReal rSimd_Bits6; //!< Pattern 3C repeated to fill single/double.
150 #    endif                         // GMX_SIMD_HAVE_REAL
151 #    if GMX_SIMD_HAVE_INT32_ARITHMETICS
152 extern const SimdInt32 iSimd_1_2_3;    //!< Three generic ints.
153 extern const SimdInt32 iSimd_4_5_6;    //!< Three generic ints.
154 extern const SimdInt32 iSimd_7_8_9;    //!< Three generic ints.
155 extern const SimdInt32 iSimd_5_7_9;    //!< iSimd_1_2_3 + iSimd_4_5_6.
156 extern const SimdInt32 iSimd_1M_2M_3M; //!< Term1 for 32bit add/sub.
157 extern const SimdInt32 iSimd_4M_5M_6M; //!< Term2 for 32bit add/sub.
158 extern const SimdInt32 iSimd_5M_7M_9M; //!< iSimd_1M_2M_3M + iSimd_4M_5M_6M.
159 #    endif
160 #    if GMX_SIMD_HAVE_INT32_LOGICAL
161 extern const SimdInt32 iSimd_0xF0F0F0F0; //!< Bitpattern to test integer logical operations.
162 extern const SimdInt32 iSimd_0xCCCCCCCC; //!< Bitpattern to test integer logical operations.
163 #    endif
164
165
166 /*! \internal
167  * \brief
168  * Test fixture for SIMD tests.
169  *
170  * This is a very simple test fixture that basically just takes the common
171  * SIMD/SIMD4 functionality from SimdBaseTest and creates wrapper routines
172  * specific for normal SIMD functionality.
173  */
174 class SimdTest : public SimdBaseTest
175 {
176 public:
177 #    if GMX_SIMD_HAVE_REAL
178     /*! \brief Compare two real SIMD variables for approximate equality.
179      *
180      * This is an internal implementation routine. YOu should always use
181      * GMX_EXPECT_SIMD_REAL_NEAR() instead.
182      *
183      * This routine is designed according to the Google test specs, so the char
184      * strings will describe the arguments to the macro.
185      *
186      * The comparison is applied to each element, and it returns true if each element
187      * in the SIMD test variable is within the class tolerances of the corresponding
188      * reference element.
189      */
190
191
192     ::testing::AssertionResult
193     compareSimdRealUlp(const char* refExpr, const char* tstExpr, SimdReal ref, SimdReal tst);
194
195     /*! \brief Compare two real SIMD variables for exact equality.
196      *
197      * This is an internal implementation routine. YOu should always use
198      * GMX_EXPECT_SIMD_REAL_NEAR() instead.
199      *
200      * This routine is designed according to the Google test specs, so the char
201      * strings will describe the arguments to the macro.
202      *
203      * The comparison is applied to each element, and it returns true if each element
204      * in the SIMD test variable is within the class tolerances of the corresponding
205      * reference element.
206      */
207     ::testing::AssertionResult compareSimdEq(const char* refExpr, const char* tstExpr, SimdReal ref, SimdReal tst);
208
209     /*! \brief Compare two 32-bit integer SIMD variables.
210      *
211      * This is an internal implementation routine. YOu should always use
212      * GMX_EXPECT_SIMD_INT_EQ() instead.
213      *
214      * This routine is designed according to the Google test specs, so the char
215      * strings will describe the arguments to the macro, while the SIMD and
216      * tolerance arguments are used to decide if the values are approximately equal.
217      *
218      * The comparison is applied to each element, and it returns true if each element
219      * in the SIMD variable tst is identical to the corresponding reference element.
220      */
221     ::testing::AssertionResult compareSimdEq(const char* refExpr, const char* tstExpr, SimdInt32 ref, SimdInt32 tst);
222 #    endif
223 };
224
225 #    if GMX_SIMD_HAVE_REAL
226 /*! \brief Convert SIMD real to std::vector<real>.
227  *
228  * The returned vector will have the same length as the SIMD width.
229  */
230 std::vector<real> simdReal2Vector(SimdReal simd);
231
232 /*! \brief Return floating-point SIMD value from std::vector<real>.
233  *
234  * If the vector is longer than SIMD width, only the first elements will be used.
235  * If it is shorter, the contents will be repeated to fill the SIMD register.
236  */
237 SimdReal vector2SimdReal(const std::vector<real>& v);
238
239 /*! \brief Set SIMD register contents from three real values.
240  *
241  * Our reason for using three values is that 3 is not a factor in any known
242  * SIMD width, so this way there will not be any simple repeated patterns e.g.
243  * between the low/high 64/128/256 bits in the SIMD register, which could hide bugs.
244  */
245 SimdReal setSimdRealFrom3R(real r0, real r1, real r2);
246
247 /*! \brief Set SIMD register contents from single real value.
248  *
249  * All elements is set from the given value. This is effectively the same
250  * operation as simdSet1(), but is implemented using only load/store
251  * operations that have been tested separately in the bootstrapping tests.
252  */
253 SimdReal setSimdRealFrom1R(real value);
254
255 /*! \brief Test if a SIMD real is bitwise identical to reference SIMD value. */
256 #        define GMX_EXPECT_SIMD_REAL_EQ(ref, tst) EXPECT_PRED_FORMAT2(compareSimdEq, ref, tst)
257
258 /*! \brief Test if a SIMD is bitwise identical to reference SIMD value. */
259 #        define GMX_EXPECT_SIMD_EQ(ref, tst) EXPECT_PRED_FORMAT2(compareSimdEq, ref, tst)
260
261 /*! \brief Test if a SIMD real is within tolerance of reference SIMD value. */
262 #        define GMX_EXPECT_SIMD_REAL_NEAR(ref, tst) \
263             EXPECT_PRED_FORMAT2(compareSimdRealUlp, ref, tst)
264
265 /*! \brief Convert SIMD integer to std::vector<int>.
266  *
267  * The returned vector will have the same length as the SIMD width.
268  */
269 std::vector<std::int32_t> simdInt2Vector(SimdInt32 simd);
270
271 /*! \brief Return 32-bit integer SIMD value from std::vector<int>.
272  *
273  * If the vector is longer than SIMD width, only the first elements will be used.
274  * If it is shorter, the contents will be repeated to fill the SIMD register.
275  */
276 SimdInt32 vector2SimdInt(const std::vector<std::int32_t>& v);
277
278 /*! \brief Set SIMD register contents from three int values.
279  *
280  * Our reason for using three values is that 3 is not a factor in any known
281  * SIMD width, so this way there will not be any simple repeated patterns e.g.
282  * between the low/high 64/128/256 bits in the SIMD register, which could hide bugs.
283  */
284 SimdInt32 setSimdIntFrom3I(int i0, int i1, int i2);
285
286 /*! \brief Set SIMD register contents from single integer value.
287  *
288  * All elements is set from the given value. This is effectively the same
289  * operation as simdSet1I(), but is implemented using only load/store
290  * operations that have been tested separately in the bootstrapping tests.
291  */
292 SimdInt32 setSimdIntFrom1I(int value);
293
294 /*! \brief Macro that checks SIMD integer expression against SIMD or reference int.
295  *
296  * If the reference argument is a scalar integer it will be expanded into
297  * the width of the SIMD register and tested against all elements.
298  */
299 #        define GMX_EXPECT_SIMD_INT_EQ(ref, tst) EXPECT_PRED_FORMAT2(compareSimdEq, ref, tst)
300
301 #    endif // GMX_SIMD_HAVE_REAL
302
303 /*! \} */
304 /*! \endcond */
305
306 } // namespace test
307 } // namespace gmx
308
309 #endif // GMX_SIMD
310
311 #endif // GMX_SIMD_TESTS_SIMD_H