1 //===- subzero/crosstest/test_arith_main.cpp - Driver for tests -----------===//
2 //
3 // The Subzero Code Generator
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 // Driver for crosstesting arithmetic operations
11 //
12 //===----------------------------------------------------------------------===//
13
14 /* crosstest.py --test=test_arith.cpp --test=test_arith_frem.ll \
15 --test=test_arith_sqrt.ll --driver=test_arith_main.cpp \
16 --prefix=Subzero_ --output=test_arith */
17
18 #include <stdint.h>
19
20 #include <cfloat>
21 #include <climits> // CHAR_BIT
22 #include <cmath> // fmodf
23 #include <cstring> // memcmp
24 #include <iostream>
25 #include <limits>
26
27 // Include test_arith.h twice - once normally, and once within the
28 // Subzero_ namespace, corresponding to the llc and Subzero translated
29 // object files, respectively.
30 #include "test_arith.h"
31
32 namespace Subzero_ {
33 #include "test_arith.h"
34 }
35
36 #include "insertelement.h"
37 #include "xdefs.h"
38
inputsMayTriggerException(T Value1,T Value2)39 template <class T> bool inputsMayTriggerException(T Value1, T Value2) {
40 // Avoid HW divide-by-zero exception.
41 if (Value2 == 0)
42 return true;
43 // Avoid HW overflow exception (on x86-32). TODO: adjust
44 // for other architecture.
45 if (Value1 == std::numeric_limits<T>::min() && Value2 == -1)
46 return true;
47 return false;
48 }
49
50 template <typename TypeUnsigned, typename TypeSigned>
testsInt(size_t & TotalTests,size_t & Passes,size_t & Failures)51 void testsInt(size_t &TotalTests, size_t &Passes, size_t &Failures) {
52 typedef TypeUnsigned (*FuncTypeUnsigned)(TypeUnsigned, TypeUnsigned);
53 typedef TypeSigned (*FuncTypeSigned)(TypeSigned, TypeSigned);
54 volatile unsigned Values[] = INT_VALUE_ARRAY;
55 const static size_t NumValues = sizeof(Values) / sizeof(*Values);
56 static struct {
57 // For functions that operate on unsigned values, the
58 // FuncLlcSigned and FuncSzSigned fields are NULL. For functions
59 // that operate on signed values, the FuncLlcUnsigned and
60 // FuncSzUnsigned fields are NULL.
61 const char *Name;
62 FuncTypeUnsigned FuncLlcUnsigned;
63 FuncTypeUnsigned FuncSzUnsigned;
64 FuncTypeSigned FuncLlcSigned;
65 FuncTypeSigned FuncSzSigned;
66 bool ExcludeDivExceptions; // for divide related tests
67 } Funcs[] = {
68 #define X(inst, op, isdiv, isshift) \
69 {STR(inst), test##inst, Subzero_::test##inst, NULL, NULL, isdiv},
70 UINTOP_TABLE
71 #undef X
72 #define X(inst, op, isdiv, isshift) \
73 {STR(inst), NULL, NULL, test##inst, Subzero_::test##inst, isdiv},
74 SINTOP_TABLE
75 #undef X
76 #define X(mult_by) \
77 {"Mult-By-" STR(mult_by), \
78 testMultiplyBy##mult_by, \
79 Subzero_::testMultiplyBy##mult_by, \
80 NULL, \
81 NULL, \
82 false}, \
83 {"Mult-By-Neg-" STR(mult_by), \
84 testMultiplyByNeg##mult_by, \
85 Subzero_::testMultiplyByNeg##mult_by, \
86 NULL, \
87 NULL, \
88 false},
89 MULIMM_TABLE};
90 #undef X
91 const static size_t NumFuncs = sizeof(Funcs) / sizeof(*Funcs);
92
93 if (sizeof(TypeUnsigned) <= sizeof(uint32_t)) {
94 // This is the "normal" version of the loop nest, for 32-bit or
95 // narrower types.
96 for (size_t f = 0; f < NumFuncs; ++f) {
97 for (size_t i = 0; i < NumValues; ++i) {
98 for (size_t j = 0; j < NumValues; ++j) {
99 TypeUnsigned Value1 = Values[i];
100 TypeUnsigned Value2 = Values[j];
101 // Avoid HW divide-by-zero exception.
102 if (Funcs[f].ExcludeDivExceptions &&
103 inputsMayTriggerException<TypeSigned>(Value1, Value2))
104 continue;
105 ++TotalTests;
106 TypeUnsigned ResultSz, ResultLlc;
107 if (Funcs[f].FuncSzUnsigned) {
108 ResultSz = Funcs[f].FuncSzUnsigned(Value1, Value2);
109 ResultLlc = Funcs[f].FuncLlcUnsigned(Value1, Value2);
110 } else {
111 ResultSz = Funcs[f].FuncSzSigned(Value1, Value2);
112 ResultLlc = Funcs[f].FuncLlcSigned(Value1, Value2);
113 }
114 if (ResultSz == ResultLlc) {
115 ++Passes;
116 } else {
117 ++Failures;
118 std::cout << "test" << Funcs[f].Name
119 << (CHAR_BIT * sizeof(TypeUnsigned)) << "(" << Value1
120 << ", " << Value2 << "): sz=" << (unsigned)ResultSz
121 << " llc=" << (unsigned)ResultLlc << "\n";
122 }
123 }
124 }
125 }
126 } else {
127 // This is the 64-bit version. Test values are synthesized from
128 // the 32-bit values in Values[].
129 for (size_t f = 0; f < NumFuncs; ++f) {
130 for (size_t iLo = 0; iLo < NumValues; ++iLo) {
131 for (size_t iHi = 0; iHi < NumValues; ++iHi) {
132 for (size_t jLo = 0; jLo < NumValues; ++jLo) {
133 for (size_t jHi = 0; jHi < NumValues; ++jHi) {
134 TypeUnsigned Value1 =
135 (((TypeUnsigned)Values[iHi]) << 32) + Values[iLo];
136 TypeUnsigned Value2 =
137 (((TypeUnsigned)Values[jHi]) << 32) + Values[jLo];
138 if (Funcs[f].ExcludeDivExceptions &&
139 inputsMayTriggerException<TypeSigned>(Value1, Value2))
140 continue;
141 ++TotalTests;
142 TypeUnsigned ResultSz, ResultLlc;
143 if (Funcs[f].FuncSzUnsigned) {
144 ResultSz = Funcs[f].FuncSzUnsigned(Value1, Value2);
145 ResultLlc = Funcs[f].FuncLlcUnsigned(Value1, Value2);
146 } else {
147 ResultSz = Funcs[f].FuncSzSigned(Value1, Value2);
148 ResultLlc = Funcs[f].FuncLlcSigned(Value1, Value2);
149 }
150 if (ResultSz == ResultLlc) {
151 ++Passes;
152 } else {
153 ++Failures;
154 std::cout << "test" << Funcs[f].Name
155 << (CHAR_BIT * sizeof(TypeUnsigned)) << "(" << Value1
156 << ", " << Value2 << "): sz=" << (uint64)ResultSz
157 << " llc=" << (uint64)ResultLlc << "\n";
158 }
159 }
160 }
161 }
162 }
163 }
164 }
165 }
166
167 const static size_t MaxTestsPerFunc = 100000;
168
169 template <typename TypeUnsignedLabel, typename TypeSignedLabel>
testsVecInt(size_t & TotalTests,size_t & Passes,size_t & Failures)170 void testsVecInt(size_t &TotalTests, size_t &Passes, size_t &Failures) {
171 typedef typename Vectors<TypeUnsignedLabel>::Ty TypeUnsigned;
172 typedef typename Vectors<TypeSignedLabel>::Ty TypeSigned;
173 typedef typename Vectors<TypeUnsignedLabel>::ElementTy ElementTypeUnsigned;
174 typedef typename Vectors<TypeSignedLabel>::ElementTy ElementTypeSigned;
175
176 typedef TypeUnsigned (*FuncTypeUnsigned)(TypeUnsigned, TypeUnsigned);
177 typedef TypeSigned (*FuncTypeSigned)(TypeSigned, TypeSigned);
178 volatile unsigned Values[] = INT_VALUE_ARRAY;
179 const static size_t NumValues = sizeof(Values) / sizeof(*Values);
180 static struct {
181 // For functions that operate on unsigned values, the
182 // FuncLlcSigned and FuncSzSigned fields are NULL. For functions
183 // that operate on signed values, the FuncLlcUnsigned and
184 // FuncSzUnsigned fields are NULL.
185 const char *Name;
186 FuncTypeUnsigned FuncLlcUnsigned;
187 FuncTypeUnsigned FuncSzUnsigned;
188 FuncTypeSigned FuncLlcSigned;
189 FuncTypeSigned FuncSzSigned;
190 bool ExcludeDivExceptions; // for divide related tests
191 bool MaskShiftOperations; // for shift related tests
192 } Funcs[] = {
193 #define X(inst, op, isdiv, isshift) \
194 {STR(inst), test##inst, Subzero_::test##inst, NULL, NULL, isdiv, isshift},
195 UINTOP_TABLE
196 #undef X
197 #define X(inst, op, isdiv, isshift) \
198 {STR(inst), NULL, NULL, test##inst, Subzero_::test##inst, isdiv, isshift},
199 SINTOP_TABLE
200 #undef X
201 };
202 const static size_t NumFuncs = sizeof(Funcs) / sizeof(*Funcs);
203 const static size_t NumElementsInType = Vectors<TypeUnsigned>::NumElements;
204 for (size_t f = 0; f < NumFuncs; ++f) {
205 PRNG Index;
206 for (size_t i = 0; i < MaxTestsPerFunc; ++i) {
207 // Initialize the test vectors.
208 TypeUnsigned Value1, Value2;
209 for (size_t j = 0; j < NumElementsInType; ++j) {
210 ElementTypeUnsigned Element1 = Values[Index() % NumValues];
211 ElementTypeUnsigned Element2 = Values[Index() % NumValues];
212 if (Funcs[f].ExcludeDivExceptions &&
213 inputsMayTriggerException<ElementTypeSigned>(Element1, Element2))
214 continue;
215 if (Funcs[f].MaskShiftOperations)
216 Element2 &= CHAR_BIT * sizeof(ElementTypeUnsigned) - 1;
217 setElement(Value1, j, Element1);
218 setElement(Value2, j, Element2);
219 }
220 // Perform the test.
221 TypeUnsigned ResultSz, ResultLlc;
222 ++TotalTests;
223 if (Funcs[f].FuncSzUnsigned) {
224 ResultSz = Funcs[f].FuncSzUnsigned(Value1, Value2);
225 ResultLlc = Funcs[f].FuncLlcUnsigned(Value1, Value2);
226 } else {
227 ResultSz = Funcs[f].FuncSzSigned(Value1, Value2);
228 ResultLlc = Funcs[f].FuncLlcSigned(Value1, Value2);
229 }
230 if (!memcmp(&ResultSz, &ResultLlc, sizeof(ResultSz))) {
231 ++Passes;
232 } else {
233 ++Failures;
234 std::cout << "test" << Funcs[f].Name << "v" << NumElementsInType << "i"
235 << (CHAR_BIT * sizeof(ElementTypeUnsigned)) << "("
236 << vectAsString<TypeUnsignedLabel>(Value1) << ","
237 << vectAsString<TypeUnsignedLabel>(Value2)
238 << "): sz=" << vectAsString<TypeUnsignedLabel>(ResultSz)
239 << " llc=" << vectAsString<TypeUnsignedLabel>(ResultLlc)
240 << "\n";
241 }
242 }
243 }
244 }
245
246 template <typename Type>
testsFp(size_t & TotalTests,size_t & Passes,size_t & Failures)247 void testsFp(size_t &TotalTests, size_t &Passes, size_t &Failures) {
248 static const Type NegInf = -1.0 / 0.0;
249 static const Type PosInf = 1.0 / 0.0;
250 static const Type Nan = 0.0 / 0.0;
251 static const Type NegNan = -0.0 / 0.0;
252 volatile Type Values[] = FP_VALUE_ARRAY(NegInf, PosInf, NegNan, Nan);
253 const static size_t NumValues = sizeof(Values) / sizeof(*Values);
254 typedef Type (*FuncType)(Type, Type);
255 static struct {
256 const char *Name;
257 FuncType FuncLlc;
258 FuncType FuncSz;
259 } Funcs[] = {
260 #define X(inst, op, func) \
261 {STR(inst), (FuncType)test##inst, (FuncType)Subzero_::test##inst},
262 FPOP_TABLE
263 #undef X
264 };
265 const static size_t NumFuncs = sizeof(Funcs) / sizeof(*Funcs);
266
267 for (size_t f = 0; f < NumFuncs; ++f) {
268 for (size_t i = 0; i < NumValues; ++i) {
269 for (size_t j = 0; j < NumValues; ++j) {
270 Type Value1 = Values[i];
271 Type Value2 = Values[j];
272 ++TotalTests;
273 Type ResultSz = Funcs[f].FuncSz(Value1, Value2);
274 Type ResultLlc = Funcs[f].FuncLlc(Value1, Value2);
275 // Compare results using memcmp() in case they are both NaN.
276 if (!memcmp(&ResultSz, &ResultLlc, sizeof(Type))) {
277 ++Passes;
278 } else {
279 ++Failures;
280 std::cout << std::fixed << "test" << Funcs[f].Name
281 << (CHAR_BIT * sizeof(Type)) << "(" << Value1 << ", "
282 << Value2 << "): sz=" << ResultSz << " llc=" << ResultLlc
283 << "\n";
284 }
285 }
286 }
287 }
288 for (size_t i = 0; i < NumValues; ++i) {
289 Type Value = Values[i];
290 ++TotalTests;
291 Type ResultSz = Subzero_::mySqrt(Value);
292 Type ResultLlc = mySqrt(Value);
293 // Compare results using memcmp() in case they are both NaN.
294 if (!memcmp(&ResultSz, &ResultLlc, sizeof(Type))) {
295 ++Passes;
296 } else {
297 ++Failures;
298 std::cout << std::fixed << "test_sqrt" << (CHAR_BIT * sizeof(Type)) << "("
299 << Value << "): sz=" << ResultSz << " llc=" << ResultLlc
300 << "\n";
301 }
302 ++TotalTests;
303 ResultSz = Subzero_::myFabs(Value);
304 ResultLlc = myFabs(Value);
305 // Compare results using memcmp() in case they are both NaN.
306 if (!memcmp(&ResultSz, &ResultLlc, sizeof(Type))) {
307 ++Passes;
308 } else {
309 ++Failures;
310 std::cout << std::fixed << "test_fabs" << (CHAR_BIT * sizeof(Type)) << "("
311 << Value << "): sz=" << ResultSz << " llc=" << ResultLlc
312 << "\n";
313 }
314 }
315 }
316
testsVecFp(size_t & TotalTests,size_t & Passes,size_t & Failures)317 void testsVecFp(size_t &TotalTests, size_t &Passes, size_t &Failures) {
318 static const float NegInf = -1.0 / 0.0;
319 static const float PosInf = 1.0 / 0.0;
320 static const float Nan = 0.0 / 0.0;
321 static const float NegNan = -0.0 / 0.0;
322 volatile float Values[] = FP_VALUE_ARRAY(NegInf, PosInf, NegNan, Nan);
323 const static size_t NumValues = sizeof(Values) / sizeof(*Values);
324 typedef v4f32 (*FuncType)(v4f32, v4f32);
325 static struct {
326 const char *Name;
327 FuncType FuncLlc;
328 FuncType FuncSz;
329 } Funcs[] = {
330 #define X(inst, op, func) \
331 {STR(inst), (FuncType)test##inst, (FuncType)Subzero_::test##inst},
332 FPOP_TABLE
333 #undef X
334 };
335 const static size_t NumFuncs = sizeof(Funcs) / sizeof(*Funcs);
336 const static size_t NumElementsInType = 4;
337 for (size_t f = 0; f < NumFuncs; ++f) {
338 PRNG Index;
339 for (size_t i = 0; i < MaxTestsPerFunc; ++i) {
340 // Initialize the test vectors.
341 v4f32 Value1, Value2;
342 for (size_t j = 0; j < NumElementsInType; ++j) {
343 setElement(Value1, j, Values[Index() % NumValues]);
344 setElement(Value2, j, Values[Index() % NumValues]);
345 }
346 // Perform the test.
347 v4f32 ResultSz = Funcs[f].FuncSz(Value1, Value2);
348 v4f32 ResultLlc = Funcs[f].FuncLlc(Value1, Value2);
349 ++TotalTests;
350 if (!memcmp(&ResultSz, &ResultLlc, sizeof(ResultSz))) {
351 ++Passes;
352 } else {
353 ++Failures;
354 std::cout << "test" << Funcs[f].Name << "v4f32"
355 << "(" << vectAsString<v4f32>(Value1) << ","
356 << vectAsString<v4f32>(Value2)
357 << "): sz=" << vectAsString<v4f32>(ResultSz) << " llc"
358 << vectAsString<v4f32>(ResultLlc) << "\n";
359 }
360 // Special case for unary fabs operation. Use Value1, ignore Value2.
361 ResultSz = Subzero_::myFabs(Value1);
362 ResultLlc = myFabs(Value1);
363 ++TotalTests;
364 if (!memcmp(&ResultSz, &ResultLlc, sizeof(ResultSz))) {
365 ++Passes;
366 } else {
367 ++Failures;
368 std::cout << "test_fabs_v4f32"
369 << "(" << vectAsString<v4f32>(Value1)
370 << "): sz=" << vectAsString<v4f32>(ResultSz) << " llc"
371 << vectAsString<v4f32>(ResultLlc) << "\n";
372 }
373 }
374 }
375 }
376
main(int argc,char * argv[])377 int main(int argc, char *argv[]) {
378 size_t TotalTests = 0;
379 size_t Passes = 0;
380 size_t Failures = 0;
381
382 testsInt<bool, bool>(TotalTests, Passes, Failures);
383 testsInt<uint8_t, myint8_t>(TotalTests, Passes, Failures);
384 testsInt<uint16_t, int16_t>(TotalTests, Passes, Failures);
385 testsInt<uint32_t, int32_t>(TotalTests, Passes, Failures);
386 testsInt<uint64, int64>(TotalTests, Passes, Failures);
387 testsVecInt<v4ui32, v4si32>(TotalTests, Passes, Failures);
388 testsVecInt<v8ui16, v8si16>(TotalTests, Passes, Failures);
389 testsVecInt<v16ui8, v16si8>(TotalTests, Passes, Failures);
390 testsFp<float>(TotalTests, Passes, Failures);
391 testsFp<double>(TotalTests, Passes, Failures);
392 testsVecFp(TotalTests, Passes, Failures);
393
394 std::cout << "TotalTests=" << TotalTests << " Passes=" << Passes
395 << " Failures=" << Failures << "\n";
396 return Failures;
397 }
398