|
14 | 14 | #include "llvm/Support/raw_ostream.h"
|
15 | 15 | #include <cmath>
|
16 | 16 |
|
17 |
| -static bool isDenorm(float F) { return std::fpclassify(F) == FP_SUBNORMAL; } |
| 17 | +// limited to float, double, and long double |
| 18 | +template <typename T> static bool isDenorm(T F) { |
| 19 | + return std::fpclassify(F) == FP_SUBNORMAL; |
| 20 | +} |
18 | 21 |
|
19 | 22 | static bool isFloat16NAN(uint16_t Val) {
|
20 | 23 | return (Val & 0x7c00) == 0x7c00 && (Val & 0x03ff) != 0;
|
21 | 24 | }
|
22 | 25 |
|
| 26 | +static bool compareDoubleULP(const double &FSrc, const double &FRef, |
| 27 | + unsigned ULPTolerance, |
| 28 | + offloadtest::DenormMode DM) { |
| 29 | + if (FSrc == FRef) |
| 30 | + return true; |
| 31 | + if (std::isnan(FSrc) || std::isnan(FRef)) |
| 32 | + return std::isnan(FRef) && std::isnan(FSrc); |
| 33 | + if (DM == offloadtest::DenormMode::Any) { |
| 34 | + // If denorm expected, output can be sign preserved zero. Otherwise output |
| 35 | + // should pass the regular ulp testing. |
| 36 | + if (isDenorm(FRef) && FSrc == 0 && std::signbit(FSrc) == std::signbit(FRef)) |
| 37 | + return true; |
| 38 | + } |
| 39 | + // For FTZ or Preserve mode, we should get the expected number within |
| 40 | + // ULPTolerance for any operations. |
| 41 | + const int64_t Diff = *((const uint64_t *)&FSrc) - *((const uint64_t *)&FRef); |
| 42 | + const uint64_t AbsDiff = Diff < 0 ? -Diff : Diff; |
| 43 | + return AbsDiff <= ULPTolerance; |
| 44 | +} |
| 45 | + |
23 | 46 | static bool compareFloatULP(const float &FSrc, const float &FRef,
|
24 | 47 | unsigned ULPTolerance, offloadtest::DenormMode DM) {
|
25 | 48 | if (FSrc == FRef)
|
@@ -61,40 +84,54 @@ static bool testBufferExact(offloadtest::Buffer *B1, offloadtest::Buffer *B2) {
|
61 | 84 | return true;
|
62 | 85 | }
|
63 | 86 |
|
| 87 | +template <typename T> |
| 88 | +static bool testAll(std::function<bool(const T &, const T &)> ComparisonFn, |
| 89 | + llvm::ArrayRef<T> Arr1, llvm::ArrayRef<T> Arr2) { |
| 90 | + if (Arr1.size() != Arr2.size()) |
| 91 | + return false; |
| 92 | + |
| 93 | + for (size_t I = 0, E = Arr1.size(); I < E; ++I) { |
| 94 | + if (!ComparisonFn(Arr1[I], Arr2[I])) |
| 95 | + return false; |
| 96 | + } |
| 97 | + return true; |
| 98 | +} |
| 99 | + |
64 | 100 | static bool testBufferFuzzy(offloadtest::Buffer *B1, offloadtest::Buffer *B2,
|
65 | 101 | unsigned ULPT, offloadtest::DenormMode DM) {
|
| 102 | + assert(B1->Format == B2->Format && "Buffer types must be the same"); |
66 | 103 | switch (B1->Format) {
|
| 104 | + case offloadtest::DataFormat::Float64: { |
| 105 | + const llvm::ArrayRef<double> Arr1( |
| 106 | + reinterpret_cast<double *>(B1->Data.get()), B1->Size / sizeof(double)); |
| 107 | + const llvm::ArrayRef<double> Arr2( |
| 108 | + reinterpret_cast<double *>(B2->Data.get()), B2->Size / sizeof(double)); |
| 109 | + auto Fn = [ULPT, DM](const double &FS, const double &FR) { |
| 110 | + return compareDoubleULP(FS, FR, ULPT, DM); |
| 111 | + }; |
| 112 | + return testAll<double>(Fn, Arr1, Arr2); |
| 113 | + } |
67 | 114 | case offloadtest::DataFormat::Float32: {
|
68 |
| - if (B1->Size != B2->Size) |
69 |
| - return false; |
70 | 115 | const llvm::ArrayRef<float> Arr1(reinterpret_cast<float *>(B1->Data.get()),
|
71 | 116 | B1->Size / sizeof(float));
|
72 |
| - assert(B2->Format == offloadtest::DataFormat::Float32 && |
73 |
| - "Buffer types must be the same"); |
74 | 117 | const llvm::ArrayRef<float> Arr2(reinterpret_cast<float *>(B2->Data.get()),
|
75 | 118 | B2->Size / sizeof(float));
|
76 |
| - for (unsigned I = 0, E = Arr1.size(); I < E; ++I) { |
77 |
| - if (!compareFloatULP(Arr1[I], Arr2[I], ULPT, DM)) |
78 |
| - return false; |
79 |
| - } |
80 |
| - return true; |
| 119 | + auto Fn = [ULPT, DM](const float &FS, const float &FR) { |
| 120 | + return compareFloatULP(FS, FR, ULPT, DM); |
| 121 | + }; |
| 122 | + return testAll<float>(Fn, Arr1, Arr2); |
81 | 123 | }
|
82 | 124 | case offloadtest::DataFormat::Float16: {
|
83 |
| - if (B1->Size != B2->Size) |
84 |
| - return false; |
85 | 125 | const llvm::ArrayRef<uint16_t> Arr1(
|
86 | 126 | reinterpret_cast<uint16_t *>(B1->Data.get()),
|
87 | 127 | B1->Size / sizeof(uint16_t));
|
88 |
| - assert(B2->Format == offloadtest::DataFormat::Float16 && |
89 |
| - "Buffer types must be the same"); |
90 | 128 | const llvm::ArrayRef<uint16_t> Arr2(
|
91 | 129 | reinterpret_cast<uint16_t *>(B2->Data.get()),
|
92 | 130 | B2->Size / sizeof(uint16_t));
|
93 |
| - for (unsigned I = 0, E = Arr1.size(); I < E; ++I) { |
94 |
| - if (!compareFloat16ULP(Arr1[I], Arr2[I], ULPT)) |
95 |
| - return false; |
96 |
| - } |
97 |
| - return true; |
| 131 | + auto Fn = [ULPT](const uint16_t &FS, const uint16_t &FR) { |
| 132 | + return compareFloat16ULP(FS, FR, ULPT); |
| 133 | + }; |
| 134 | + return testAll<uint16_t>(Fn, Arr1, Arr2); |
98 | 135 | }
|
99 | 136 | default:
|
100 | 137 | llvm_unreachable("Only float types are supported by the fuzzy test.");
|
|
0 commit comments