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 <climits> // CHAR_BIT
21 #include <limits>
22 #include <cfloat>
23 #include <cmath> // fmodf
24 #include <cstring> // memcmp
25 #include <iostream>
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 ,
71 UINTOP_TABLE
72 #undef X
73 #define X(inst, op, isdiv, isshift) \
74 { STR(inst), NULL, NULL, test##inst, Subzero_::test##inst, isdiv } \
75 ,
76 SINTOP_TABLE
77 #undef X
78 #define X(mult_by) \
79 { \
80 "Mult-By-" STR(mult_by), testMultiplyBy##mult_by, \
81 Subzero_::testMultiplyBy##mult_by, NULL, NULL, false \
82 } \
83 , {"Mult-By-Neg-" STR(mult_by), testMultiplyByNeg##mult_by, \
84 Subzero_::testMultiplyByNeg##mult_by, NULL, NULL, false},
85 MULIMM_TABLE};
86 #undef X
87 const static size_t NumFuncs = sizeof(Funcs) / sizeof(*Funcs);
88
89 if (sizeof(TypeUnsigned) <= sizeof(uint32_t)) {
90 // This is the "normal" version of the loop nest, for 32-bit or
91 // narrower types.
92 for (size_t f = 0; f < NumFuncs; ++f) {
93 for (size_t i = 0; i < NumValues; ++i) {
94 for (size_t j = 0; j < NumValues; ++j) {
95 TypeUnsigned Value1 = Values[i];
96 TypeUnsigned Value2 = Values[j];
97 // Avoid HW divide-by-zero exception.
98 if (Funcs[f].ExcludeDivExceptions &&
99 inputsMayTriggerException<TypeSigned>(Value1, Value2))
100 continue;
101 ++TotalTests;
102 TypeUnsigned ResultSz, ResultLlc;
103 if (Funcs[f].FuncSzUnsigned) {
104 ResultSz = Funcs[f].FuncSzUnsigned(Value1, Value2);
105 ResultLlc = Funcs[f].FuncLlcUnsigned(Value1, Value2);
106 } else {
107 ResultSz = Funcs[f].FuncSzSigned(Value1, Value2);
108 ResultLlc = Funcs[f].FuncLlcSigned(Value1, Value2);
109 }
110 if (ResultSz == ResultLlc) {
111 ++Passes;
112 } else {
113 ++Failures;
114 std::cout << "test" << Funcs[f].Name
115 << (CHAR_BIT * sizeof(TypeUnsigned)) << "(" << Value1
116 << ", " << Value2 << "): sz=" << (unsigned)ResultSz
117 << " llc=" << (unsigned)ResultLlc << "\n";
118 }
119 }
120 }
121 }
122 } else {
123 // This is the 64-bit version. Test values are synthesized from
124 // the 32-bit values in Values[].
125 for (size_t f = 0; f < NumFuncs; ++f) {
126 for (size_t iLo = 0; iLo < NumValues; ++iLo) {
127 for (size_t iHi = 0; iHi < NumValues; ++iHi) {
128 for (size_t jLo = 0; jLo < NumValues; ++jLo) {
129 for (size_t jHi = 0; jHi < NumValues; ++jHi) {
130 TypeUnsigned Value1 =
131 (((TypeUnsigned)Values[iHi]) << 32) + Values[iLo];
132 TypeUnsigned Value2 =
133 (((TypeUnsigned)Values[jHi]) << 32) + Values[jLo];
134 if (Funcs[f].ExcludeDivExceptions &&
135 inputsMayTriggerException<TypeSigned>(Value1, Value2))
136 continue;
137 ++TotalTests;
138 TypeUnsigned ResultSz, ResultLlc;
139 if (Funcs[f].FuncSzUnsigned) {
140 ResultSz = Funcs[f].FuncSzUnsigned(Value1, Value2);
141 ResultLlc = Funcs[f].FuncLlcUnsigned(Value1, Value2);
142 } else {
143 ResultSz = Funcs[f].FuncSzSigned(Value1, Value2);
144 ResultLlc = Funcs[f].FuncLlcSigned(Value1, Value2);
145 }
146 if (ResultSz == ResultLlc) {
147 ++Passes;
148 } else {
149 ++Failures;
150 std::cout << "test" << Funcs[f].Name
151 << (CHAR_BIT * sizeof(TypeUnsigned)) << "(" << Value1
152 << ", " << Value2 << "): sz=" << (uint64)ResultSz
153 << " llc=" << (uint64)ResultLlc << "\n";
154 }
155 }
156 }
157 }
158 }
159 }
160 }
161 }
162
163 const static size_t MaxTestsPerFunc = 100000;
164
165 template <typename TypeUnsignedLabel, typename TypeSignedLabel>
testsVecInt(size_t & TotalTests,size_t & Passes,size_t & Failures)166 void testsVecInt(size_t &TotalTests, size_t &Passes, size_t &Failures) {
167 typedef typename Vectors<TypeUnsignedLabel>::Ty TypeUnsigned;
168 typedef typename Vectors<TypeSignedLabel>::Ty TypeSigned;
169 typedef typename Vectors<TypeUnsignedLabel>::ElementTy ElementTypeUnsigned;
170 typedef typename Vectors<TypeSignedLabel>::ElementTy ElementTypeSigned;
171
172 typedef TypeUnsigned (*FuncTypeUnsigned)(TypeUnsigned, TypeUnsigned);
173 typedef TypeSigned (*FuncTypeSigned)(TypeSigned, TypeSigned);
174 volatile unsigned Values[] = INT_VALUE_ARRAY;
175 const static size_t NumValues = sizeof(Values) / sizeof(*Values);
176 static struct {
177 // For functions that operate on unsigned values, the
178 // FuncLlcSigned and FuncSzSigned fields are NULL. For functions
179 // that operate on signed values, the FuncLlcUnsigned and
180 // FuncSzUnsigned fields are NULL.
181 const char *Name;
182 FuncTypeUnsigned FuncLlcUnsigned;
183 FuncTypeUnsigned FuncSzUnsigned;
184 FuncTypeSigned FuncLlcSigned;
185 FuncTypeSigned FuncSzSigned;
186 bool ExcludeDivExceptions; // for divide related tests
187 bool MaskShiftOperations; // for shift related tests
188 } Funcs[] = {
189 #define X(inst, op, isdiv, isshift) \
190 { STR(inst), test##inst, Subzero_::test##inst, NULL, NULL, isdiv, isshift } \
191 ,
192 UINTOP_TABLE
193 #undef X
194 #define X(inst, op, isdiv, isshift) \
195 { STR(inst), NULL, NULL, test##inst, Subzero_::test##inst, isdiv, isshift } \
196 ,
197 SINTOP_TABLE
198 #undef X
199 };
200 const static size_t NumFuncs = sizeof(Funcs) / sizeof(*Funcs);
201 const static size_t NumElementsInType = Vectors<TypeUnsigned>::NumElements;
202 for (size_t f = 0; f < NumFuncs; ++f) {
203 PRNG Index;
204 for (size_t i = 0; i < MaxTestsPerFunc; ++i) {
205 // Initialize the test vectors.
206 TypeUnsigned Value1, Value2;
207 for (size_t j = 0; j < NumElementsInType; ++j) {
208 ElementTypeUnsigned Element1 = Values[Index() % NumValues];
209 ElementTypeUnsigned Element2 = Values[Index() % NumValues];
210 if (Funcs[f].ExcludeDivExceptions &&
211 inputsMayTriggerException<ElementTypeSigned>(Element1, Element2))
212 continue;
213 if (Funcs[f].MaskShiftOperations)
214 Element2 &= CHAR_BIT * sizeof(ElementTypeUnsigned) - 1;
215 setElement(Value1, j, Element1);
216 setElement(Value2, j, Element2);
217 }
218 // Perform the test.
219 TypeUnsigned ResultSz, ResultLlc;
220 ++TotalTests;
221 if (Funcs[f].FuncSzUnsigned) {
222 ResultSz = Funcs[f].FuncSzUnsigned(Value1, Value2);
223 ResultLlc = Funcs[f].FuncLlcUnsigned(Value1, Value2);
224 } else {
225 ResultSz = Funcs[f].FuncSzSigned(Value1, Value2);
226 ResultLlc = Funcs[f].FuncLlcSigned(Value1, Value2);
227 }
228 if (!memcmp(&ResultSz, &ResultLlc, sizeof(ResultSz))) {
229 ++Passes;
230 } else {
231 ++Failures;
232 std::cout << "test" << Funcs[f].Name << "v" << NumElementsInType << "i"
233 << (CHAR_BIT * sizeof(ElementTypeUnsigned)) << "("
234 << vectAsString<TypeUnsignedLabel>(Value1) << ","
235 << vectAsString<TypeUnsignedLabel>(Value2)
236 << "): sz=" << vectAsString<TypeUnsignedLabel>(ResultSz)
237 << " llc=" << vectAsString<TypeUnsignedLabel>(ResultLlc)
238 << "\n";
239 }
240 }
241 }
242 }
243
244 template <typename Type>
testsFp(size_t & TotalTests,size_t & Passes,size_t & Failures)245 void testsFp(size_t &TotalTests, size_t &Passes, size_t &Failures) {
246 static const Type NegInf = -1.0 / 0.0;
247 static const Type PosInf = 1.0 / 0.0;
248 static const Type Nan = 0.0 / 0.0;
249 static const Type NegNan = -0.0 / 0.0;
250 volatile Type Values[] = FP_VALUE_ARRAY(NegInf, PosInf, NegNan, Nan);
251 const static size_t NumValues = sizeof(Values) / sizeof(*Values);
252 typedef Type (*FuncType)(Type, Type);
253 static struct {
254 const char *Name;
255 FuncType FuncLlc;
256 FuncType FuncSz;
257 } Funcs[] = {
258 #define X(inst, op, func) \
259 { STR(inst), (FuncType)test##inst, (FuncType)Subzero_::test##inst } \
260 ,
261 FPOP_TABLE
262 #undef X
263 };
264 const static size_t NumFuncs = sizeof(Funcs) / sizeof(*Funcs);
265
266 for (size_t f = 0; f < NumFuncs; ++f) {
267 for (size_t i = 0; i < NumValues; ++i) {
268 for (size_t j = 0; j < NumValues; ++j) {
269 Type Value1 = Values[i];
270 Type Value2 = Values[j];
271 ++TotalTests;
272 Type ResultSz = Funcs[f].FuncSz(Value1, Value2);
273 Type ResultLlc = Funcs[f].FuncLlc(Value1, Value2);
274 // Compare results using memcmp() in case they are both NaN.
275 if (!memcmp(&ResultSz, &ResultLlc, sizeof(Type))) {
276 ++Passes;
277 } else {
278 ++Failures;
279 std::cout << std::fixed << "test" << Funcs[f].Name
280 << (CHAR_BIT * sizeof(Type)) << "(" << Value1 << ", "
281 << Value2 << "): sz=" << ResultSz << " llc=" << ResultLlc
282 << "\n";
283 }
284 }
285 }
286 }
287 for (size_t i = 0; i < NumValues; ++i) {
288 Type Value = Values[i];
289 ++TotalTests;
290 Type ResultSz = Subzero_::mySqrt(Value);
291 Type ResultLlc = mySqrt(Value);
292 // Compare results using memcmp() in case they are both NaN.
293 if (!memcmp(&ResultSz, &ResultLlc, sizeof(Type))) {
294 ++Passes;
295 } else {
296 ++Failures;
297 std::cout << std::fixed << "test_sqrt" << (CHAR_BIT * sizeof(Type)) << "("
298 << Value << "): sz=" << ResultSz << " llc=" << ResultLlc
299 << "\n";
300 }
301 ++TotalTests;
302 ResultSz = Subzero_::myFabs(Value);
303 ResultLlc = myFabs(Value);
304 // Compare results using memcmp() in case they are both NaN.
305 if (!memcmp(&ResultSz, &ResultLlc, sizeof(Type))) {
306 ++Passes;
307 } else {
308 ++Failures;
309 std::cout << std::fixed << "test_fabs" << (CHAR_BIT * sizeof(Type)) << "("
310 << Value << "): sz=" << ResultSz << " llc=" << ResultLlc
311 << "\n";
312 }
313 }
314 }
315
testsVecFp(size_t & TotalTests,size_t & Passes,size_t & Failures)316 void testsVecFp(size_t &TotalTests, size_t &Passes, size_t &Failures) {
317 static const float NegInf = -1.0 / 0.0;
318 static const float PosInf = 1.0 / 0.0;
319 static const float Nan = 0.0 / 0.0;
320 static const float NegNan = -0.0 / 0.0;
321 volatile float Values[] = FP_VALUE_ARRAY(NegInf, PosInf, NegNan, Nan);
322 const static size_t NumValues = sizeof(Values) / sizeof(*Values);
323 typedef v4f32 (*FuncType)(v4f32, v4f32);
324 static struct {
325 const char *Name;
326 FuncType FuncLlc;
327 FuncType FuncSz;
328 } Funcs[] = {
329 #define X(inst, op, func) \
330 { STR(inst), (FuncType)test##inst, (FuncType)Subzero_::test##inst } \
331 ,
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