2 * This file is part of the GROMACS molecular simulation package.
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.
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.
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.
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.
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.
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.
35 #ifndef GMX_SIMD_TESTS_SIMD_H
36 #define GMX_SIMD_TESTS_SIMD_H
40 * Declares fixture for testing of normal SIMD (not SIMD4) functionality.
42 * The SIMD tests are both simple and complicated. The actual testing logic
43 * is \a very straightforward since we just need to test single values against
44 * the math library, and for some math functions we need to do it in a loop.
45 * This could have been achieved in minutes with the default Google Test tools,
46 * if it wasn't for the problem that we cannot access or compare SIMD contents
47 * directly without using lots of other SIMD functionality. For this reason
48 * we have separate the basic testing of load/store operations into a separate
49 * bootstrapping test. Once this works, we use a set of utility routines to
50 * convert SIMD contents to/from std:vector<> and perform the rest of the tests,
51 * which then can farmed out to the base class SimdBaseTest that is common
54 * Another complication is that the width of the SIMD implementation will
55 * depend on the hardware and precision. For some simple operations it is
56 * sufficient to set all SIMD elements to the same value, and check that the
57 * result is present in all elements. However, for a few more complex
58 * instructions that might rely on shuffling under-the-hood it is important
59 * that we can test operations with different elements. We achieve this by
60 * having test code that can initialize a SIMD variable from an std::vector
61 * of arbitrary length; the vector is simply repeated to fill all elements in
62 * the SIMD variable. We also have similar routines to compare a SIMD result
63 * with values in a vector, which returns true iff all elements match.
65 * This way we can write simple tests that use different values for all SIMD
66 * elements. Personally I like using vectors of length 3, since this means
67 * there are no simple repeated patterns in low/high halves of SIMD variables
68 * that are 2,4,8,or 16 elements wide, and we still don't have to care about
69 * the exact SIMD width of the underlying implementation.
71 * Note that this utility uses a few SIMD load/store instructions internally -
72 * those have been tested separately in the bootstrap_loadstore.cpp file.
74 * \author Erik Lindahl <erik.lindahl@scilifelab.se>
75 * \ingroup module_simd
78 #include <gtest/gtest.h>
79 #include "gromacs/simd/simd.h"
89 /*! \addtogroup module_simd */
92 /* Unfortunately we cannot keep static SIMD constants in the test fixture class.
93 * The problem is that SIMD memory need to be aligned, and in particular
94 * this applies to automatic storage of variables in classes. For SSE registers
95 * this means 16-byte alignment (which seems to work), but AVX requires 32-bit
96 * alignment. At least both gcc-4.7.3 and Apple clang-5.0 (OS X 10.9) fail to
97 * align these variables when they are stored as data in a class.
99 * In theory we could set some of these on-the-fly e.g. with setSimdFrom3R()
100 * instead (although that would mean repeating code between tests), but many of
101 * the constants depend on the current precision not to mention they
102 * occasionally have many digits that need to be exactly right, and keeping
103 * them in a single place makes sure they are consistent.
105 #ifdef GMX_SIMD_HAVE_REAL
106 extern const gmx_simd_real_t rSimd_1_2_3; //!< Generic (different) fp values.
107 extern const gmx_simd_real_t rSimd_4_5_6; //!< Generic (different) fp values.
108 extern const gmx_simd_real_t rSimd_7_8_9; //!< Generic (different) fp values.
109 extern const gmx_simd_real_t rSimd_5_7_9; //!< rSimd_1_2_3 + rSimd_4_5_6.
110 extern const gmx_simd_real_t rSimd_m1_m2_m3; //!< Generic negative floating-point values.
111 extern const gmx_simd_real_t rSimd_3_1_4; //!< Used to test min/max.
112 extern const gmx_simd_real_t rSimd_m3_m1_m4; //!< negative rSimd_3_1_4.
113 extern const gmx_simd_real_t rSimd_2p25; //!< Value that rounds down.
114 extern const gmx_simd_real_t rSimd_3p75; //!< Value that rounds up.
115 extern const gmx_simd_real_t rSimd_m2p25; //!< Negative value that rounds up.
116 extern const gmx_simd_real_t rSimd_m3p75; //!< Negative value that rounds down.
117 //! Three large floating-point values whose exponents are >32.
118 extern const gmx_simd_real_t rSimd_Exp;
119 # if (defined GMX_SIMD_HAVE_DOUBLE) && (defined GMX_DOUBLE)
120 // Make sure we also test exponents outside single precision when we use double
121 extern const gmx_simd_real_t rSimd_ExpDouble;
123 // Magic FP numbers corresponding to specific bit patterns
124 extern const gmx_simd_real_t rSimd_Bits1; //!< Pattern F0 repeated to fill single/double.
125 extern const gmx_simd_real_t rSimd_Bits2; //!< Pattern CC repeated to fill single/double.
126 extern const gmx_simd_real_t rSimd_Bits3; //!< Pattern C0 repeated to fill single/double.
127 extern const gmx_simd_real_t rSimd_Bits4; //!< Pattern 0C repeated to fill single/double.
128 extern const gmx_simd_real_t rSimd_Bits5; //!< Pattern FC repeated to fill single/double.
129 extern const gmx_simd_real_t rSimd_Bits6; //!< Pattern 3C repeated to fill single/double.
130 #endif // GMX_SIMD_HAVE_REAL
131 #ifdef GMX_SIMD_HAVE_INT32
132 extern const gmx_simd_int32_t iSimd_1_2_3; //!< Three generic ints.
133 extern const gmx_simd_int32_t iSimd_4_5_6; //!< Three generic ints.
134 extern const gmx_simd_int32_t iSimd_7_8_9; //!< Three generic ints.
135 extern const gmx_simd_int32_t iSimd_5_7_9; //!< iSimd_1_2_3 + iSimd_4_5_6.
136 extern const gmx_simd_int32_t iSimd_1M_2M_3M; //!< Term1 for 32bit add/sub.
137 extern const gmx_simd_int32_t iSimd_4M_5M_6M; //!< Term2 for 32bit add/sub.
138 extern const gmx_simd_int32_t iSimd_5M_7M_9M; //!< iSimd_1M_2M_3M + iSimd_4M_5M_6M.
139 extern const gmx_simd_int32_t iSimd_0xF0F0F0F0; //!< Bitpattern to test integer logical operations.
140 extern const gmx_simd_int32_t iSimd_0xCCCCCCCC; //!< Bitpattern to test integer logical operations.
141 #endif // GMX_SIMD_HAVE_INT32
144 /*! \brief Test fixture for SIMD tests.
146 * This is a very simple test fixture that basically just takes the common
147 * SIMD/SIMD4 functionality from SimdBaseTest and creates wrapper routines
148 * specific for normal SIMD functionality.
150 class SimdTest : public SimdBaseTest
153 #ifdef GMX_SIMD_HAVE_REAL
154 /*! \brief Compare two real SIMD variables for approximate equality.
156 * This is an internal implementation routine. YOu should always use
157 * GMX_EXPECT_SIMD_REAL_NEAR() instead.
159 * This routine is designed according to the Google test specs, so the char
160 * strings will describe the arguments to the macro.
162 * The comparison is applied to each element, and it returns true if each element
163 * in the SIMD test variable is within the class tolerances of the corresponding
166 ::testing::AssertionResult
167 compareSimdRealUlp(const char * refExpr, const char * tstExpr,
168 const gmx_simd_real_t ref, const gmx_simd_real_t tst);
170 /*! \brief Compare two real SIMD variables for exact equality.
172 * This is an internal implementation routine. YOu should always use
173 * GMX_EXPECT_SIMD_REAL_NEAR() instead.
175 * This routine is designed according to the Google test specs, so the char
176 * strings will describe the arguments to the macro.
178 * The comparison is applied to each element, and it returns true if each element
179 * in the SIMD test variable is within the class tolerances of the corresponding
182 ::testing::AssertionResult
183 compareSimdRealEq(const char * refExpr, const char * tstExpr,
184 const gmx_simd_real_t ref, const gmx_simd_real_t tst);
188 #ifdef GMX_SIMD_HAVE_INT32
189 /*! \brief Compare two 32-bit integer SIMD variables.
191 * This is an internal implementation routine. YOu should always use
192 * GMX_EXPECT_SIMD_INT_EQ() instead.
194 * This routine is designed according to the Google test specs, so the char
195 * strings will describe the arguments to the macro, while the SIMD and
196 * tolerance arguments are used to decide if the values are approximately equal.
198 * The comparison is applied to each element, and it returns true if each element
199 * in the SIMD variable tst is identical to the corresponding reference element.
201 ::testing::AssertionResult
202 compareSimdInt32(const char * refExpr, const char * tstExpr,
203 const gmx_simd_int32_t ref, const gmx_simd_int32_t tst);
207 #ifdef GMX_SIMD_HAVE_REAL
208 /*! \brief Convert SIMD real to std::vector<real>.
210 * The returned vector will have the same length as the SIMD width.
212 std::vector<real> simdReal2Vector(const gmx_simd_real_t simd);
214 /*! \brief Return floating-point SIMD value from std::vector<real>.
216 * If the vector is longer than SIMD width, only the first elements will be used.
217 * If it is shorter, the contents will be repeated to fill the SIMD register.
219 gmx_simd_real_t vector2SimdReal(const std::vector<real> &v);
221 /*! \brief Set SIMD register contents from three real values.
223 * Our reason for using three values is that 3 is not a factor in any known
224 * SIMD width, so this way there will not be any simple repeated patterns e.g.
225 * between the low/high 64/128/256 bits in the SIMD register, which could hide bugs.
227 gmx_simd_real_t setSimdRealFrom3R(real r0, real r1, real r2);
229 /*! \brief Set SIMD register contents from single real value.
231 * All elements is set from the given value. This is effectively the same
232 * operation as gmx_simd_set1_r(), but is implemented using only load/store
233 * operations that have been tested separately in the bootstrapping tests.
235 gmx_simd_real_t setSimdRealFrom1R(real value);
237 /*! \brief Test if a SIMD real is bitwise identical to reference SIMD value. */
238 #define GMX_EXPECT_SIMD_REAL_EQ(ref, tst) EXPECT_PRED_FORMAT2(compareSimdRealEq, ref, tst)
240 /*! \brief Test if a SIMD real is within tolerance of reference SIMD value. */
241 #define GMX_EXPECT_SIMD_REAL_NEAR(ref, tst) EXPECT_PRED_FORMAT2(compareSimdRealUlp, ref, tst)
243 #endif // GMX_SIMD_HAVE_REAL
245 #ifdef GMX_SIMD_HAVE_INT32
246 /*! \brief Convert SIMD integer to std::vector<int>.
248 * The returned vector will have the same length as the SIMD width.
250 std::vector<int> simdInt2Vector(const gmx_simd_int32_t simd);
252 /*! \brief Return 32-bit integer SIMD value from std::vector<int>.
254 * If the vector is longer than SIMD width, only the first elements will be used.
255 * If it is shorter, the contents will be repeated to fill the SIMD register.
257 gmx_simd_int32_t vector2SimdInt(const std::vector<int> &v);
259 /*! \brief Set SIMD register contents from three int values.
261 * Our reason for using three values is that 3 is not a factor in any known
262 * SIMD width, so this way there will not be any simple repeated patterns e.g.
263 * between the low/high 64/128/256 bits in the SIMD register, which could hide bugs.
265 gmx_simd_int32_t setSimdIntFrom3I(int i0, int i1, int i2);
267 /*! \brief Set SIMD register contents from single integer value.
269 * All elements is set from the given value. This is effectively the same
270 * operation as gmx_simd_set1_i(), but is implemented using only load/store
271 * operations that have been tested separately in the bootstrapping tests.
273 gmx_simd_int32_t setSimdIntFrom1I(int value);
275 /*! \brief Macro that checks SIMD integer expression against SIMD or reference int.
277 * If the reference argument is a scalar integer it will be expanded into
278 * the width of the SIMD register and tested against all elements.
280 #define GMX_EXPECT_SIMD_INT_EQ(ref, tst) EXPECT_PRED_FORMAT2(compareSimdInt32, ref, tst)
282 #endif // GMX_SIMD_HAVE_INT32
290 #endif // GMX_SIMD_TESTS_SIMD_H