blob: d9bbbe9c7490e1a725412dc40946a9de94e9923d [file] [log] [blame]
Jim Stichnoth5bc2b1d2014-05-22 13:38:48 -07001/* crosstest.py --test=test_arith.cpp --test=test_arith_frem.ll \
Jan Voungf37fbbe2014-07-09 16:13:13 -07002 --test=test_arith_sqrt.ll --driver=test_arith_main.cpp \
3 --prefix=Subzero_ --output=test_arith */
Jim Stichnoth5bc2b1d2014-05-22 13:38:48 -07004
5#include <stdint.h>
6
Matt Wala7fa22d82014-07-17 12:41:31 -07007#include <climits> // CHAR_BIT
8#include <limits>
Jim Stichnoth5bc2b1d2014-05-22 13:38:48 -07009#include <cfloat>
Matt Wala7fa22d82014-07-17 12:41:31 -070010#include <cmath> // fmodf
Jim Stichnoth5bc2b1d2014-05-22 13:38:48 -070011#include <cstring> // memcmp
12#include <iostream>
13
14// Include test_arith.h twice - once normally, and once within the
15// Subzero_ namespace, corresponding to the llc and Subzero translated
16// object files, respectively.
17#include "test_arith.h"
18namespace Subzero_ {
19#include "test_arith.h"
20}
21
Matt Wala7fa22d82014-07-17 12:41:31 -070022volatile unsigned Values[] = INT_VALUE_ARRAY;
Jim Stichnoth5bc2b1d2014-05-22 13:38:48 -070023const static size_t NumValues = sizeof(Values) / sizeof(*Values);
24
Matt Wala7fa22d82014-07-17 12:41:31 -070025template <class T> bool inputsMayTriggerException(T Value1, T Value2) {
26 // Avoid HW divide-by-zero exception.
27 if (Value2 == 0)
28 return true;
29 // Avoid HW overflow exception (on x86-32). TODO: adjust
30 // for other architecture.
31 if (Value1 == std::numeric_limits<T>::min() && Value2 == -1)
32 return true;
33 return false;
34}
35
Jim Stichnoth5bc2b1d2014-05-22 13:38:48 -070036template <typename TypeUnsigned, typename TypeSigned>
37void testsInt(size_t &TotalTests, size_t &Passes, size_t &Failures) {
38 typedef TypeUnsigned (*FuncTypeUnsigned)(TypeUnsigned, TypeUnsigned);
39 typedef TypeSigned (*FuncTypeSigned)(TypeSigned, TypeSigned);
40 static struct {
41 const char *Name;
42 FuncTypeUnsigned FuncLlc;
43 FuncTypeUnsigned FuncSz;
44 bool ExcludeDivExceptions; // for divide related tests
45 } Funcs[] = {
46#define X(inst, op, isdiv) \
47 { \
48 STR(inst), (FuncTypeUnsigned)test##inst, \
49 (FuncTypeUnsigned)Subzero_::test##inst, isdiv \
50 } \
51 ,
52 UINTOP_TABLE
53#undef X
54#define X(inst, op, isdiv) \
55 { \
56 STR(inst), (FuncTypeUnsigned)(FuncTypeSigned)test##inst, \
57 (FuncTypeUnsigned)(FuncTypeSigned)Subzero_::test##inst, isdiv \
58 } \
59 ,
Matt Wala7fa22d82014-07-17 12:41:31 -070060 SINTOP_TABLE
Jim Stichnoth5bc2b1d2014-05-22 13:38:48 -070061#undef X
Matt Wala7fa22d82014-07-17 12:41:31 -070062 };
Jim Stichnoth5bc2b1d2014-05-22 13:38:48 -070063 const static size_t NumFuncs = sizeof(Funcs) / sizeof(*Funcs);
64
65 if (sizeof(TypeUnsigned) <= sizeof(uint32_t)) {
66 // This is the "normal" version of the loop nest, for 32-bit or
67 // narrower types.
68 for (size_t f = 0; f < NumFuncs; ++f) {
69 for (size_t i = 0; i < NumValues; ++i) {
70 for (size_t j = 0; j < NumValues; ++j) {
71 TypeUnsigned Value1 = Values[i];
72 TypeUnsigned Value2 = Values[j];
73 // Avoid HW divide-by-zero exception.
Matt Wala7fa22d82014-07-17 12:41:31 -070074 if (Funcs[f].ExcludeDivExceptions &&
75 inputsMayTriggerException<TypeSigned>(Value1, Value2))
Jim Stichnoth5bc2b1d2014-05-22 13:38:48 -070076 continue;
77 ++TotalTests;
78 TypeUnsigned ResultSz = Funcs[f].FuncSz(Value1, Value2);
79 TypeUnsigned ResultLlc = Funcs[f].FuncLlc(Value1, Value2);
80 if (ResultSz == ResultLlc) {
81 ++Passes;
82 } else {
83 ++Failures;
Matt Wala7fa22d82014-07-17 12:41:31 -070084 std::cout << "test" << Funcs[f].Name
85 << (CHAR_BIT * sizeof(TypeUnsigned)) << "(" << Value1
86 << ", " << Value2 << "): sz=" << (unsigned)ResultSz
Jim Stichnoth5bc2b1d2014-05-22 13:38:48 -070087 << " llc=" << (unsigned)ResultLlc << std::endl;
88 }
89 }
90 }
91 }
92 } else {
93 // This is the 64-bit version. Test values are synthesized from
94 // the 32-bit values in Values[].
95 for (size_t f = 0; f < NumFuncs; ++f) {
96 for (size_t iLo = 0; iLo < NumValues; ++iLo) {
97 for (size_t iHi = 0; iHi < NumValues; ++iHi) {
98 for (size_t jLo = 0; jLo < NumValues; ++jLo) {
99 for (size_t jHi = 0; jHi < NumValues; ++jHi) {
100 TypeUnsigned Value1 =
101 (((TypeUnsigned)Values[iHi]) << 32) + Values[iLo];
102 TypeUnsigned Value2 =
103 (((TypeUnsigned)Values[jHi]) << 32) + Values[jLo];
Matt Wala7fa22d82014-07-17 12:41:31 -0700104 if (Funcs[f].ExcludeDivExceptions &&
105 inputsMayTriggerException<TypeSigned>(Value1, Value2))
Jim Stichnoth5bc2b1d2014-05-22 13:38:48 -0700106 continue;
107 ++TotalTests;
108 TypeUnsigned ResultSz = Funcs[f].FuncSz(Value1, Value2);
109 TypeUnsigned ResultLlc = Funcs[f].FuncLlc(Value1, Value2);
110 if (ResultSz == ResultLlc) {
111 ++Passes;
112 } else {
113 ++Failures;
114 std::cout << "test" << Funcs[f].Name
Matt Wala7fa22d82014-07-17 12:41:31 -0700115 << (CHAR_BIT * sizeof(TypeUnsigned)) << "(" << Value1
116 << ", " << Value2 << "): sz=" << (unsigned)ResultSz
Jim Stichnoth5bc2b1d2014-05-22 13:38:48 -0700117 << " llc=" << (unsigned)ResultLlc << std::endl;
118 }
119 }
120 }
121 }
122 }
123 }
124 }
125}
126
Matt Wala7fa22d82014-07-17 12:41:31 -0700127// Vectors are deterministically constructed by selecting elements from
128// a pool of scalar values based on a pseudorandom sequence. Testing
129// all possible combinations of scalar values from the value table is
130// not tractable.
131// TODO: Replace with a portable PRNG from C++11.
132class PRNG {
133public:
134 PRNG(uint32_t Seed = 1) : State(Seed) {}
135
136 uint32_t operator()() {
137 // Lewis, Goodman, and Miller (1969)
138 State = (16807 * State) % 2147483647;
139 return State;
140 }
141
142private:
143 uint32_t State;
144};
145
146const static size_t MaxTestsPerFunc = 100000;
147
148template <typename Type, typename ElementType, typename CastType>
149void outputVector(const Type Vect) {
150 const static size_t NumElementsInType = sizeof(Type) / sizeof(ElementType);
151 for (size_t i = 0; i < NumElementsInType; ++i) {
152 if (i > 0)
153 std::cout << ", ";
154 std::cout << (CastType) Vect[i];
155 }
156}
157
158template <typename TypeUnsigned, typename TypeSigned,
159 typename ElementTypeUnsigned, typename ElementTypeSigned>
160void testsVecInt(size_t &TotalTests, size_t &Passes, size_t &Failures) {
161 typedef TypeUnsigned (*FuncTypeUnsigned)(TypeUnsigned, TypeUnsigned);
162 typedef TypeSigned (*FuncTypeSigned)(TypeSigned, TypeSigned);
163 static struct {
164 const char *Name;
165 FuncTypeUnsigned FuncLlc;
166 FuncTypeUnsigned FuncSz;
167 bool ExcludeDivExceptions; // for divide related tests
168 } Funcs[] = {
169#define X(inst, op, isdiv) \
170 { \
171 STR(inst), (FuncTypeUnsigned)test##inst, \
172 (FuncTypeUnsigned)Subzero_::test##inst, isdiv \
173 } \
174 ,
175 UINTOP_TABLE
176#undef X
177#define X(inst, op, isdiv) \
178 { \
179 STR(inst), (FuncTypeUnsigned)(FuncTypeSigned)test##inst, \
180 (FuncTypeUnsigned)(FuncTypeSigned)Subzero_::test##inst, isdiv \
181 } \
182 ,
183 SINTOP_TABLE
184#undef X
185 };
186 const static size_t NumFuncs = sizeof(Funcs) / sizeof(*Funcs);
187 const static size_t NumElementsInType =
188 sizeof(TypeUnsigned) / sizeof(ElementTypeUnsigned);
189 for (size_t f = 0; f < NumFuncs; ++f) {
190 PRNG Index;
191 for (size_t i = 0; i < MaxTestsPerFunc; ++i) {
192 // Initialize the test vectors.
193 TypeUnsigned Value1, Value2;
194 for (size_t j = 0; j < NumElementsInType;) {
195 ElementTypeUnsigned Element1 = Values[Index() % NumElementsInType];
196 ElementTypeUnsigned Element2 = Values[Index() % NumElementsInType];
197 if (Funcs[f].ExcludeDivExceptions &&
198 inputsMayTriggerException<ElementTypeSigned>(Element1, Element2))
199 continue;
200 Value1[j] = Element1;
201 Value2[j] = Element2;
202 ++j;
203 }
204 // Perform the test.
205 TypeUnsigned ResultSz = Funcs[f].FuncSz(Value1, Value2);
206 TypeUnsigned ResultLlc = Funcs[f].FuncLlc(Value1, Value2);
207 ++TotalTests;
208 if (!memcmp(&ResultSz, &ResultLlc, sizeof(ResultSz))) {
209 ++Passes;
210 } else {
211 std::cout << "test" << Funcs[f].Name << "v" << NumElementsInType << "i"
212 << (CHAR_BIT * sizeof(ElementTypeUnsigned)) << "(";
213 outputVector<TypeUnsigned, ElementTypeUnsigned, unsigned>(Value1);
214 std::cout << ", ";
215 outputVector<TypeUnsigned, ElementTypeUnsigned, unsigned>(Value2);
216 std::cout << "): sz=";
217 outputVector<TypeUnsigned, ElementTypeUnsigned, unsigned>(ResultSz);
218 std::cout << " llc=";
219 outputVector<TypeUnsigned, ElementTypeUnsigned, unsigned>(ResultLlc);
220 std::cout << std::endl;
221 }
222 }
223 }
224}
225
Jim Stichnoth5bc2b1d2014-05-22 13:38:48 -0700226template <typename Type>
227void testsFp(size_t &TotalTests, size_t &Passes, size_t &Failures) {
228 static const Type NegInf = -1.0 / 0.0;
229 static const Type PosInf = 1.0 / 0.0;
230 static const Type Nan = 0.0 / 0.0;
Jan Voungf37fbbe2014-07-09 16:13:13 -0700231 static const Type NegNan = -0.0 / 0.0;
Matt Wala7fa22d82014-07-17 12:41:31 -0700232 volatile Type Values[] = FP_VALUE_ARRAY(NegInf, PosInf, NegNan, Nan);
Jim Stichnoth5bc2b1d2014-05-22 13:38:48 -0700233 const static size_t NumValues = sizeof(Values) / sizeof(*Values);
234 typedef Type (*FuncType)(Type, Type);
235 static struct {
236 const char *Name;
237 FuncType FuncLlc;
238 FuncType FuncSz;
239 } Funcs[] = {
240#define X(inst, op, func) \
241 { STR(inst), (FuncType)test##inst, (FuncType)Subzero_::test##inst } \
242 ,
243 FPOP_TABLE
244#undef X
Matt Wala7fa22d82014-07-17 12:41:31 -0700245 };
Jim Stichnoth5bc2b1d2014-05-22 13:38:48 -0700246 const static size_t NumFuncs = sizeof(Funcs) / sizeof(*Funcs);
247
248 for (size_t f = 0; f < NumFuncs; ++f) {
249 for (size_t i = 0; i < NumValues; ++i) {
250 for (size_t j = 0; j < NumValues; ++j) {
251 Type Value1 = Values[i];
252 Type Value2 = Values[j];
253 ++TotalTests;
254 Type ResultSz = Funcs[f].FuncSz(Value1, Value2);
255 Type ResultLlc = Funcs[f].FuncLlc(Value1, Value2);
256 // Compare results using memcmp() in case they are both NaN.
257 if (!memcmp(&ResultSz, &ResultLlc, sizeof(Type))) {
258 ++Passes;
259 } else {
260 ++Failures;
261 std::cout << std::fixed << "test" << Funcs[f].Name
Matt Wala7fa22d82014-07-17 12:41:31 -0700262 << (CHAR_BIT * sizeof(Type)) << "(" << Value1 << ", "
263 << Value2 << "): sz=" << ResultSz << " llc=" << ResultLlc
Jim Stichnoth5bc2b1d2014-05-22 13:38:48 -0700264 << std::endl;
265 }
266 }
267 }
268 }
Jan Voungf37fbbe2014-07-09 16:13:13 -0700269 for (size_t i = 0; i < NumValues; ++i) {
270 Type Value = Values[i];
271 ++TotalTests;
272 Type ResultSz = Subzero_::mySqrt(Value);
273 Type ResultLlc = mySqrt(Value);
274 // Compare results using memcmp() in case they are both NaN.
275 if (!memcmp(&ResultSz, &ResultLlc, sizeof(Type))) {
276 ++Passes;
277 } else {
278 ++Failures;
Matt Wala7fa22d82014-07-17 12:41:31 -0700279 std::cout << std::fixed << "test_sqrt" << (CHAR_BIT * sizeof(Type)) << "("
280 << Value << "): sz=" << ResultSz << " llc=" << ResultLlc
Jan Voungf37fbbe2014-07-09 16:13:13 -0700281 << std::endl;
282 }
283 }
Jim Stichnoth5bc2b1d2014-05-22 13:38:48 -0700284}
285
Matt Wala7fa22d82014-07-17 12:41:31 -0700286void testsVecFp(size_t &TotalTests, size_t &Passes, size_t &Failures) {
287 static const float NegInf = -1.0 / 0.0;
288 static const float PosInf = 1.0 / 0.0;
289 static const float Nan = 0.0 / 0.0;
290 static const float NegNan = -0.0 / 0.0;
291 volatile float Values[] = FP_VALUE_ARRAY(NegInf, PosInf, NegNan, Nan);
292 const static size_t NumValues = sizeof(Values) / sizeof(*Values);
293 typedef v4f32 (*FuncType)(v4f32, v4f32);
294 static struct {
295 const char *Name;
296 FuncType FuncLlc;
297 FuncType FuncSz;
298 } Funcs[] = {
299#define X(inst, op, func) \
300 { STR(inst), (FuncType)test##inst, (FuncType)Subzero_::test##inst } \
301 ,
302 FPOP_TABLE
303#undef X
304 };
305 const static size_t NumFuncs = sizeof(Funcs) / sizeof(*Funcs);
306 const static size_t NumElementsInType = 4;
307 for (size_t f = 0; f < NumFuncs; ++f) {
308 PRNG Index;
309 for (size_t i = 0; i < MaxTestsPerFunc; ++i) {
310 // Initialize the test vectors.
311 v4f32 Value1, Value2;
312 for (size_t j = 0; j < NumElementsInType; ++j) {
313 Value1[j] = Values[Index() % NumElementsInType];
314 Value2[j] = Values[Index() % NumElementsInType];
315 }
316 // Perform the test.
317 v4f32 ResultSz = Funcs[f].FuncSz(Value1, Value2);
318 v4f32 ResultLlc = Funcs[f].FuncLlc(Value1, Value2);
319 ++TotalTests;
320 if (!memcmp(&ResultSz, &ResultLlc, sizeof(ResultSz))) {
321 ++Passes;
322 } else {
323 ++Failures;
324 std::cout << std::fixed << "test" << Funcs[f].Name << "v4f32"
325 << "(";
326 outputVector<v4f32, float, float>(Value1);
327 std::cout << ", ";
328 outputVector<v4f32, float, float>(Value2);
329 std::cout << "): sz=";
330 outputVector<v4f32, float, float>(ResultSz);
331 std::cout << " llc=";
332 outputVector<v4f32, float, float>(ResultLlc);
333 std::cout << std::endl;
334 }
335 }
336 }
337}
338
Jim Stichnoth5bc2b1d2014-05-22 13:38:48 -0700339int main(int argc, char **argv) {
340 size_t TotalTests = 0;
341 size_t Passes = 0;
342 size_t Failures = 0;
343
344 testsInt<uint8_t, int8_t>(TotalTests, Passes, Failures);
345 testsInt<uint16_t, int16_t>(TotalTests, Passes, Failures);
346 testsInt<uint32_t, int32_t>(TotalTests, Passes, Failures);
347 testsInt<uint64_t, int64_t>(TotalTests, Passes, Failures);
Matt Wala7fa22d82014-07-17 12:41:31 -0700348 testsVecInt<v4ui32, v4si32, uint32_t, int32_t>(TotalTests, Passes, Failures);
349 testsVecInt<v8ui16, v8si16, uint16_t, int16_t>(TotalTests, Passes, Failures);
350 testsVecInt<v16ui8, v16si8, uint8_t, int8_t>(TotalTests, Passes, Failures);
Jim Stichnoth5bc2b1d2014-05-22 13:38:48 -0700351 testsFp<float>(TotalTests, Passes, Failures);
352 testsFp<double>(TotalTests, Passes, Failures);
Matt Wala7fa22d82014-07-17 12:41:31 -0700353 testsVecFp(TotalTests, Passes, Failures);
Jim Stichnoth5bc2b1d2014-05-22 13:38:48 -0700354
355 std::cout << "TotalTests=" << TotalTests << " Passes=" << Passes
356 << " Failures=" << Failures << "\n";
357 return Failures;
358}
Matt Wala7fa22d82014-07-17 12:41:31 -0700359
360extern "C" {
361// Subzero helpers
362 v4si32 Sz_shl_v4i32(v4si32 a, v4si32 b) { return a << b; }
363 v4si32 Sz_ashr_v4i32(v4si32 a, v4si32 b) { return a >> b; }
364 v4ui32 Sz_lshr_v4i32(v4ui32 a, v4ui32 b) { return a >> b; }
365 v4si32 Sz_sdiv_v4i32(v4si32 a, v4si32 b) { return a / b; }
366 v4ui32 Sz_udiv_v4i32(v4ui32 a, v4ui32 b) { return a / b; }
367 v4si32 Sz_srem_v4i32(v4si32 a, v4si32 b) { return a % b; }
368 v4ui32 Sz_urem_v4i32(v4ui32 a, v4ui32 b) { return a % b; }
369
370 v8si16 Sz_shl_v8i16(v8si16 a, v8si16 b) { return a << b; }
371 v8si16 Sz_ashr_v8i16(v8si16 a, v8si16 b) { return a >> b; }
372 v8ui16 Sz_lshr_v8i16(v8ui16 a, v8ui16 b) { return a >> b; }
373 v8si16 Sz_sdiv_v8i16(v8si16 a, v8si16 b) { return a / b; }
374 v8ui16 Sz_udiv_v8i16(v8ui16 a, v8ui16 b) { return a / b; }
375 v8si16 Sz_srem_v8i16(v8si16 a, v8si16 b) { return a % b; }
376 v8ui16 Sz_urem_v8i16(v8ui16 a, v8ui16 b) { return a % b; }
377
378 v16ui8 Sz_mul_v16i8(v16ui8 a, v16ui8 b) { return a * b; }
379 v16si8 Sz_shl_v16i8(v16si8 a, v16si8 b) { return a << b; }
380 v16si8 Sz_ashr_v16i8(v16si8 a, v16si8 b) { return a >> b; }
381 v16ui8 Sz_lshr_v16i8(v16ui8 a, v16ui8 b) { return a >> b; }
382 v16si8 Sz_sdiv_v16i8(v16si8 a, v16si8 b) { return a / b; }
383 v16ui8 Sz_udiv_v16i8(v16ui8 a, v16ui8 b) { return a / b; }
384 v16si8 Sz_srem_v16i8(v16si8 a, v16si8 b) { return a % b; }
385 v16ui8 Sz_urem_v16i8(v16ui8 a, v16ui8 b) { return a % b; }
386
387 v4f32 Sz_frem_v4f32(v4f32 a, v4f32 b) {
388 v4f32 Result;
389 for (int i = 0; i < 4; ++i)
390 Result[i] = fmodf(a[i], b[i]);
391 return Result;
392 }
393}