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