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 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> 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> 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> 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 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 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