10
10
// ===----------------------------------------------------------------------===//
11
11
12
12
#include " Support/Check.h"
13
+ #include " llvm/ADT/APFloat.h"
14
+ #include " llvm/ADT/APInt.h"
13
15
#include " llvm/Support/Error.h"
14
16
#include " llvm/Support/raw_ostream.h"
15
17
#include < cmath>
@@ -18,6 +20,16 @@ constexpr uint16_t Float16BitSign = 0x8000;
18
20
constexpr uint16_t Float16BitExp = 0x7c00 ;
19
21
constexpr uint16_t Float16BitMantissa = 0x03ff ;
20
22
23
+ static float convertFloat16ToFloat (const uint16_t F) {
24
+ const llvm::APInt API (16 , F);
25
+ llvm::detail::IEEEFloat IEF (llvm::APFloat::IEEEhalf (), API);
26
+ bool LostInfo;
27
+ // rounding mode should not matter since we are up converting
28
+ IEF.convert (llvm::APFloat::IEEEsingle (),
29
+ llvm::APFloatBase::rmNearestTiesToEven, &LostInfo);
30
+ return IEF.convertToFloat ();
31
+ }
32
+
21
33
// limited to float, double, and long double
22
34
template <typename T> static bool isDenorm (T F) {
23
35
return std::fpclassify (F) == FP_SUBNORMAL;
@@ -28,6 +40,23 @@ static bool isFloat16NAN(uint16_t Val) {
28
40
(Val & Float16BitMantissa) != 0 ;
29
41
}
30
42
43
+ static bool compareDoubleEpsilon (const double &FSrc, const double &FRef,
44
+ double Epsilon, offloadtest::DenormMode DM) {
45
+ if (FSrc == FRef)
46
+ return true ;
47
+ if (std::isnan (FSrc) || std::isnan (FRef))
48
+ return std::isnan (FRef) && std::isnan (FSrc);
49
+ if (DM == offloadtest::DenormMode::Any) {
50
+ // If denorm expected, output can be sign preserved zero. Otherwise output
51
+ // should pass the regular ulp testing.
52
+ if (isDenorm (FRef) && FSrc == 0 && std::signbit (FSrc) == std::signbit (FRef))
53
+ return true ;
54
+ }
55
+ // For FTZ or Preserve mode, we should get the expected number within
56
+ // epsilon for any operation
57
+ return std::abs (FSrc - FRef) < Epsilon;
58
+ }
59
+
31
60
static bool compareDoubleULP (const double &FSrc, const double &FRef,
32
61
unsigned ULPTolerance,
33
62
offloadtest::DenormMode DM) {
@@ -48,6 +77,24 @@ static bool compareDoubleULP(const double &FSrc, const double &FRef,
48
77
return AbsDiff <= ULPTolerance;
49
78
}
50
79
80
+ static bool compareFloatEpsilon (const float &FSrc, const float &FRef,
81
+ float Epsilon, offloadtest::DenormMode DM) {
82
+ if (FSrc == FRef)
83
+ return true ;
84
+ if (std::isnan (FSrc) || std::isnan (FRef))
85
+ return std::isnan (FRef) && std::isnan (FSrc);
86
+ if (DM == offloadtest::DenormMode::Any) {
87
+ // If denorm expected, output can be sign preserved zero. Otherwise output
88
+ // should pass the regular ulp testing.
89
+ if (isDenorm (FRef) && FSrc == 0 && std::signbit (FSrc) == std::signbit (FRef))
90
+ return true ;
91
+ }
92
+
93
+ // For FTZ or Preserve mode, we should get the expected number within
94
+ // epsilon for any operation
95
+ return std::abs (FSrc - FRef) < Epsilon;
96
+ }
97
+
51
98
static bool compareFloatULP (const float &FSrc, const float &FRef,
52
99
unsigned ULPTolerance, offloadtest::DenormMode DM) {
53
100
if (FSrc == FRef)
@@ -67,6 +114,21 @@ static bool compareFloatULP(const float &FSrc, const float &FRef,
67
114
return AbsDiff <= ULPTolerance;
68
115
}
69
116
117
+ static bool compareFloat16Epsilon (const uint16_t &FSrc, const uint16_t &FRef,
118
+ float Epsilon) {
119
+ // Treat +0 and -0 as equal
120
+ if ((FSrc & ~Float16BitSign) == 0 && (FRef & ~Float16BitSign) == 0 )
121
+ return true ;
122
+ if (FSrc == FRef)
123
+ return true ;
124
+ if (isFloat16NAN (FSrc) || isFloat16NAN (FRef))
125
+ return isFloat16NAN (FRef) && isFloat16NAN (FSrc);
126
+
127
+ const float FSrc32 = convertFloat16ToFloat (FSrc);
128
+ const float FRef32 = convertFloat16ToFloat (FRef);
129
+ return std::abs (FSrc32 - FRef32) < Epsilon;
130
+ }
131
+
70
132
static bool compareFloat16ULP (const uint16_t &FSrc, const uint16_t &FRef,
71
133
unsigned ULPTolerance) {
72
134
// Treat +0 and -0 as equal
@@ -113,29 +175,25 @@ static bool testAll(std::function<bool(const T &, const T &)> ComparisonFn,
113
175
return true ;
114
176
}
115
177
116
- static bool testBufferFuzzy (offloadtest::Buffer *B1, offloadtest::Buffer *B2,
117
- unsigned ULPT, offloadtest::DenormMode DM) {
178
+ template <typename T>
179
+ static bool
180
+ testBufferFloat (std::function<bool (const T &, const T &)> ComparisonFn,
181
+ offloadtest::Buffer *B1, offloadtest::Buffer *B2) {
118
182
assert (B1->Format == B2->Format && " Buffer types must be the same" );
119
183
switch (B1->Format ) {
120
184
case offloadtest::DataFormat::Float64: {
121
185
const llvm::ArrayRef<double > Arr1 (
122
186
reinterpret_cast <double *>(B1->Data .get ()), B1->Size / sizeof (double ));
123
187
const llvm::ArrayRef<double > Arr2 (
124
188
reinterpret_cast <double *>(B2->Data .get ()), B2->Size / sizeof (double ));
125
- auto Fn = [ULPT, DM](const double &FS, const double &FR) {
126
- return compareDoubleULP (FS, FR, ULPT, DM);
127
- };
128
- return testAll<double >(Fn, Arr1, Arr2);
189
+ return testAll<double >(ComparisonFn, Arr1, Arr2);
129
190
}
130
191
case offloadtest::DataFormat::Float32: {
131
192
const llvm::ArrayRef<float > Arr1 (reinterpret_cast <float *>(B1->Data .get ()),
132
193
B1->Size / sizeof (float ));
133
194
const llvm::ArrayRef<float > Arr2 (reinterpret_cast <float *>(B2->Data .get ()),
134
195
B2->Size / sizeof (float ));
135
- auto Fn = [ULPT, DM](const float &FS, const float &FR) {
136
- return compareFloatULP (FS, FR, ULPT, DM);
137
- };
138
- return testAll<float >(Fn, Arr1, Arr2);
196
+ return testAll<float >(ComparisonFn, Arr1, Arr2);
139
197
}
140
198
case offloadtest::DataFormat::Float16: {
141
199
const llvm::ArrayRef<uint16_t > Arr1 (
@@ -144,10 +202,64 @@ static bool testBufferFuzzy(offloadtest::Buffer *B1, offloadtest::Buffer *B2,
144
202
const llvm::ArrayRef<uint16_t > Arr2 (
145
203
reinterpret_cast <uint16_t *>(B2->Data .get ()),
146
204
B2->Size / sizeof (uint16_t ));
205
+ return testAll<uint16_t >(ComparisonFn, Arr1, Arr2);
206
+ }
207
+ default :
208
+ llvm_unreachable (" Only float types are supported by the fuzzy test." );
209
+ }
210
+ return false ;
211
+ }
212
+
213
+ static bool testBufferFloatEpsilon (offloadtest::Buffer *B1,
214
+ offloadtest::Buffer *B2, double Epsilon,
215
+ offloadtest::DenormMode DM) {
216
+
217
+ switch (B1->Format ) {
218
+ case offloadtest::DataFormat::Float64: {
219
+ auto Fn = [Epsilon, DM](const double &FS, const double &FR) {
220
+ return compareDoubleEpsilon (FS, FR, Epsilon, DM);
221
+ };
222
+ return testBufferFloat<double >(Fn, B1, B2);
223
+ }
224
+ case offloadtest::DataFormat::Float32: {
225
+ auto Fn = [Epsilon, DM](const float &FS, const float &FR) {
226
+ return compareFloatEpsilon (FS, FR, (float )Epsilon, DM);
227
+ };
228
+ return testBufferFloat<float >(Fn, B1, B2);
229
+ }
230
+ case offloadtest::DataFormat::Float16: {
231
+ auto Fn = [Epsilon](const uint16_t &FS, const uint16_t &FR) {
232
+ return compareFloat16Epsilon (FS, FR, (float )Epsilon);
233
+ };
234
+ return testBufferFloat<uint16_t >(Fn, B1, B2);
235
+ }
236
+ default :
237
+ llvm_unreachable (" Only float types are supported by the fuzzy test." );
238
+ }
239
+ return false ;
240
+ }
241
+
242
+ static bool testBufferFloatULP (offloadtest::Buffer *B1, offloadtest::Buffer *B2,
243
+ unsigned ULPT, offloadtest::DenormMode DM) {
244
+
245
+ switch (B1->Format ) {
246
+ case offloadtest::DataFormat::Float64: {
247
+ auto Fn = [ULPT, DM](const double &FS, const double &FR) {
248
+ return compareDoubleULP (FS, FR, ULPT, DM);
249
+ };
250
+ return testBufferFloat<double >(Fn, B1, B2);
251
+ }
252
+ case offloadtest::DataFormat::Float32: {
253
+ auto Fn = [ULPT, DM](const float &FS, const float &FR) {
254
+ return compareFloatULP (FS, FR, ULPT, DM);
255
+ };
256
+ return testBufferFloat<float >(Fn, B1, B2);
257
+ }
258
+ case offloadtest::DataFormat::Float16: {
147
259
auto Fn = [ULPT](const uint16_t &FS, const uint16_t &FR) {
148
260
return compareFloat16ULP (FS, FR, ULPT);
149
261
};
150
- return testAll <uint16_t >(Fn, Arr1, Arr2 );
262
+ return testBufferFloat <uint16_t >(Fn, B1, B2 );
151
263
}
152
264
default :
153
265
llvm_unreachable (" Only float types are supported by the fuzzy test." );
@@ -160,26 +272,25 @@ llvm::Error verifyResult(offloadtest::Result R) {
160
272
case offloadtest::Rule::BufferExact: {
161
273
if (testBufferExact (R.ActualPtr , R.ExpectedPtr ))
162
274
return llvm::Error::success ();
163
- llvm::SmallString<256 > Str;
164
- llvm::raw_svector_ostream OS (Str);
165
- OS << " Test failed: " << R.Name << " \n Expected:\n " ;
166
- llvm::yaml::Output YAMLOS (OS);
167
- YAMLOS << *R.ExpectedPtr ;
168
- OS << " Got:\n " ;
169
- YAMLOS << *R.ActualPtr ;
170
- return llvm::createStringError (Str.c_str ());
171
- }
172
- case offloadtest::Rule::BufferFuzzy: {
173
- if (testBufferFuzzy (R.ActualPtr , R.ExpectedPtr , R.ULPT , R.DM ))
275
+ break ;
276
+ }
277
+ case offloadtest::Rule::BufferFloatULP: {
278
+ if (testBufferFloatULP (R.ActualPtr , R.ExpectedPtr , R.ULPT , R.DM ))
279
+ return llvm::Error::success ();
280
+ break ;
281
+ }
282
+ case offloadtest::Rule::BufferFloatEpsilon: {
283
+ if (testBufferFloatEpsilon (R.ActualPtr , R.ExpectedPtr , R.Epsilon , R.DM ))
174
284
return llvm::Error::success ();
175
- llvm::SmallString<256 > Str;
176
- llvm::raw_svector_ostream OS (Str);
177
- OS << " Test failed: " << R.Name << " \n Expected:\n " ;
178
- llvm::yaml::Output YAMLOS (OS);
179
- YAMLOS << *R.ExpectedPtr ;
180
- OS << " Got:\n " ;
181
- YAMLOS << *R.ActualPtr ;
182
- return llvm::createStringError (Str.c_str ());
285
+ break ;
183
286
}
184
287
}
288
+ llvm::SmallString<256 > Str;
289
+ llvm::raw_svector_ostream OS (Str);
290
+ OS << " Test failed: " << R.Name << " \n Expected:\n " ;
291
+ llvm::yaml::Output YAMLOS (OS);
292
+ YAMLOS << *R.ExpectedPtr ;
293
+ OS << " Got:\n " ;
294
+ YAMLOS << *R.ActualPtr ;
295
+ return llvm::createStringError (Str.c_str ());
185
296
}
0 commit comments