-
Notifications
You must be signed in to change notification settings - Fork 353
[UnitTest] Add test for fmax reductions without fast-math. #266
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,274 @@ | ||
#include <algorithm> | ||
#include <functional> | ||
#include <iostream> | ||
#include <limits> | ||
#include <memory> | ||
#include <stdint.h> | ||
|
||
#include "common.h" | ||
|
||
static bool isEqual(float A, float B) { | ||
if (std::isnan(A) || std::isnan(B)) | ||
return std::isnan(A) && std::isnan(B); | ||
|
||
if (A == 0.0f) | ||
return B == 0.0f && std::signbit(A) == std::signbit(B); | ||
|
||
return A == B; | ||
} | ||
|
||
template <typename Ty> using Fn1Ty = std::function<Ty(Ty *, unsigned)>; | ||
|
||
template <typename Ty> | ||
static void check(Fn1Ty<Ty> ScalarFn, Fn1Ty<Ty> VectorFn, float *Src, | ||
unsigned N, const char *Type) { | ||
auto Reference = ScalarFn(Src, N); | ||
auto ToCheck = VectorFn(Src, N); | ||
if (!isEqual(Reference, ToCheck)) { | ||
std::cerr << "Miscompare " << Type << ": " << Reference << " != " << ToCheck | ||
<< "\n"; | ||
exit(1); | ||
} | ||
} | ||
|
||
template <typename Ty> | ||
static void checkVectorFunction(Fn1Ty<Ty> ScalarFn, Fn1Ty<Ty> VectorFn, | ||
const char *Name) { | ||
std::cout << "Checking " << Name << "\n"; | ||
|
||
unsigned N = 1024; | ||
std::unique_ptr<Ty[]> Src1(new Ty[N]); | ||
init_data(Src1, N); | ||
|
||
// Check with random inputs. | ||
|
||
// Check with sorted inputs. | ||
std::sort(&Src1[0], &Src1[N]); | ||
check(ScalarFn, VectorFn, &Src1[0], N, "sorted"); | ||
|
||
// Check with reverse sorted inputs. | ||
std::reverse(&Src1[0], &Src1[N]); | ||
check(ScalarFn, VectorFn, &Src1[0], N, "reverse-sorted"); | ||
|
||
// Check with all max values. | ||
for (unsigned I = 0; I != N; ++I) | ||
Src1[I] = std::numeric_limits<Ty>::max(); | ||
check(ScalarFn, VectorFn, &Src1[0], N, "all-max"); | ||
|
||
// Check with all min values. | ||
for (unsigned I = 0; I != N; ++I) | ||
Src1[I] = std::numeric_limits<Ty>::min(); | ||
check(ScalarFn, VectorFn, &Src1[0], N, "all-min"); | ||
|
||
// Check with various denormals. | ||
Src1[0] = std::numeric_limits<Ty>::denorm_min(); | ||
for (unsigned I = 1; I != N; ++I) | ||
Src1[I] = std::numeric_limits<Ty>::denorm_min() / I; | ||
check(ScalarFn, VectorFn, &Src1[0], N, "denormals"); | ||
|
||
// Check with inputs all zero. | ||
for (unsigned I = 0; I != N; ++I) | ||
Src1[I] = 0.0; | ||
check(ScalarFn, VectorFn, &Src1[0], N, "all-zeros"); | ||
|
||
// Check with NaN at different indices. | ||
for (unsigned NaNIdx = 3; NaNIdx != 32; NaNIdx++) { | ||
for (unsigned I = 0; I != N; ++I) | ||
Src1[I] = 100; | ||
Src1[NaNIdx] = std::numeric_limits<Ty>::quiet_NaN(); | ||
|
||
check(ScalarFn, VectorFn, &Src1[0], N, "NaN"); | ||
} | ||
|
||
// Check with multiple signed-zeros at different positions. | ||
for (unsigned Idx = 0; Idx != 64; ++Idx) { | ||
for (unsigned I = 0; I != N; ++I) | ||
Src1[I] = -1.0; | ||
|
||
for (unsigned Offset = 1; Offset != 32; ++Offset) { | ||
Src1[Idx] = -0.0; | ||
Src1[Idx + Offset] = +0.0; | ||
|
||
check(ScalarFn, VectorFn, &Src1[0], N, "signed-zeros"); | ||
} | ||
} | ||
|
||
for (unsigned Idx = 0; Idx != 64; ++Idx) { | ||
for (unsigned I = 0; I != N; ++I) | ||
Src1[I] = -1.0; | ||
|
||
for (unsigned Offset = 1; Offset != 32; ++Offset) { | ||
Src1[Idx] = +0.0; | ||
Src1[Idx + Offset] = -0.0; | ||
|
||
check(ScalarFn, VectorFn, &Src1[0], N, "signed-zeros"); | ||
} | ||
} | ||
|
||
// Check with max value at all possible indices. | ||
for (unsigned Idx = 0; Idx != N; ++Idx) { | ||
for (unsigned I = 0; I != N; ++I) | ||
Src1[I] = I; | ||
|
||
Src1[Idx] = N + 1; | ||
|
||
check(ScalarFn, VectorFn, &Src1[0], N, "full"); | ||
|
||
for (unsigned Offset = 1; Offset != 16; ++Offset) { | ||
if (Idx + Offset < N) | ||
Src1[Idx + Offset] = N + 1; | ||
|
||
check(ScalarFn, VectorFn, &Src1[0], N, "full"); | ||
} | ||
} | ||
|
||
// Check with NaN value at all possible indices. | ||
for (unsigned Idx = 0; Idx != N; ++Idx) { | ||
for (unsigned I = 0; I != N; ++I) | ||
Src1[I] = I; | ||
|
||
Src1[Idx] = std::numeric_limits<float>::quiet_NaN(); | ||
check(ScalarFn, VectorFn, &Src1[0], N, "full-with-nan"); | ||
|
||
// Check with multiple NaNs at different offsets. | ||
for (unsigned Offset = 1; Offset != 16; ++Offset) { | ||
if (Idx + Offset < N) | ||
Src1[Idx + Offset] = std::numeric_limits<float>::quiet_NaN(); | ||
|
||
check(ScalarFn, VectorFn, &Src1[0], N, "full-with-multiple-nan"); | ||
} | ||
} | ||
|
||
// Check with multiple infinity values at different positions. | ||
for (unsigned Idx = 0; Idx != 64; ++Idx) { | ||
for (unsigned I = 0; I != N; ++I) | ||
Src1[I] = -1.0; | ||
|
||
for (unsigned Offset = 1; Offset != 16; ++Offset) { | ||
Src1[Idx] = -std::numeric_limits<float>::infinity(); | ||
Src1[Idx + Offset] = std::numeric_limits<float>::infinity(); | ||
|
||
check(ScalarFn, VectorFn, &Src1[0], N, "infinity"); | ||
} | ||
} | ||
} | ||
|
||
int main(void) { | ||
rng = std::mt19937(15); | ||
|
||
{ | ||
DEFINE_SCALAR_AND_VECTOR_FN1_TYPE( | ||
float Max = -2.0;, for (unsigned I = 0; I < 1024; | ||
I++) { Max = std::fmax(Max, A[I]); } return Max; | ||
, float); | ||
checkVectorFunction<float>(ScalarFn, VectorFn, "fmaxnum_start_neg_2"); | ||
} | ||
{ | ||
DEFINE_SCALAR_AND_VECTOR_FN1_TYPE( | ||
float Max = std::numeric_limits<float>::min(); | ||
, for (unsigned I = 0; I < 1024; | ||
I++) { Max = std::fmax(Max, A[I]); } return Max; | ||
, float); | ||
checkVectorFunction<float>(ScalarFn, VectorFn, "fmaxnum_start_min"); | ||
} | ||
{ | ||
DEFINE_SCALAR_AND_VECTOR_FN1_TYPE( | ||
float Max = std::numeric_limits<float>::denorm_min(); | ||
, for (unsigned I = 0; I < 1024; | ||
I++) { Max = std::fmax(Max, A[I]); } return Max; | ||
, float); | ||
checkVectorFunction<float>(ScalarFn, VectorFn, "fmaxnum_start_denorm_min"); | ||
} | ||
{ | ||
DEFINE_SCALAR_AND_VECTOR_FN1_TYPE( | ||
float Max = std::numeric_limits<float>::quiet_NaN(); | ||
, for (unsigned I = 0; I < 1024; | ||
I++) { Max = std::fmax(Max, A[I]); } return Max; | ||
, float); | ||
checkVectorFunction<float>(ScalarFn, VectorFn, "fmaxnum_start_is_nan"); | ||
} | ||
|
||
{ | ||
DEFINE_SCALAR_AND_VECTOR_FN1_TYPE( | ||
float Max = -2.0; | ||
, for (unsigned I = 0; I < 1024; | ||
I++) { Max = A[I] > Max ? A[I] : Max; } return Max; | ||
, float); | ||
checkVectorFunction<float>(ScalarFn, VectorFn, "fmax_strict_start_neg_2"); | ||
} | ||
{ | ||
DEFINE_SCALAR_AND_VECTOR_FN1_TYPE( | ||
float Max = std::numeric_limits<float>::min(); | ||
, for (unsigned I = 0; I < 1024; | ||
I++) { Max = A[I] > Max ? A[I] : Max; } return Max; | ||
, float); | ||
checkVectorFunction<float>(ScalarFn, VectorFn, "fmax_strict_start_min"); | ||
} | ||
{ | ||
DEFINE_SCALAR_AND_VECTOR_FN1_TYPE( | ||
float Max = std::numeric_limits<float>::denorm_min(); | ||
, for (unsigned I = 0; I < 1025; | ||
I++) { Max = A[I] > Max ? A[I] : Max; } return Max; | ||
, float); | ||
checkVectorFunction<float>(ScalarFn, VectorFn, | ||
"fmax_strict_start_denorm_min"); | ||
} | ||
{ | ||
DEFINE_SCALAR_AND_VECTOR_FN1_TYPE( | ||
float Max = std::numeric_limits<float>::quiet_NaN(); | ||
, for (unsigned I = 0; I < 1025; | ||
I++) { Max = A[I] > Max ? A[I] : Max; } return Max; | ||
, float); | ||
checkVectorFunction<float>(ScalarFn, VectorFn, "fmax_strict_start_nan"); | ||
} | ||
|
||
{ | ||
DEFINE_SCALAR_AND_VECTOR_FN1_TYPE( | ||
float Max = -2.0; | ||
, for (unsigned I = 0; I < 1024; | ||
I++) { Max = Max >= A[I] ? Max : A[I]; } return Max; | ||
, float); | ||
checkVectorFunction<float>(ScalarFn, VectorFn, | ||
"fmax_non_strict_start_neg_2"); | ||
} | ||
|
||
{ | ||
DEFINE_SCALAR_AND_VECTOR_FN1_TYPE( | ||
float Max = -2.0; | ||
, for (unsigned I = 0; I < 1024; | ||
I++) { Max = Max > A[I] ? Max : A[I]; } return Max; | ||
, float); | ||
checkVectorFunction<float>(ScalarFn, VectorFn, | ||
"fmax_cmp_max_gt_start_neg_2"); | ||
} | ||
|
||
{ | ||
DEFINE_SCALAR_AND_VECTOR_FN1_TYPE( | ||
float Max = -2.0; | ||
, for (unsigned I = 0; I < 1024; | ||
I++) { Max = Max < A[I] ? A[I] : Max; } return Max; | ||
, float); | ||
checkVectorFunction<float>(ScalarFn, VectorFn, | ||
"fmax_cmp_max_lt_start_neg_2"); | ||
} | ||
{ | ||
DEFINE_SCALAR_AND_VECTOR_FN1_TYPE( | ||
float Max = std::numeric_limits<float>::denorm_min(); | ||
, for (unsigned I = 0; I < 1024; | ||
I++) { Max = Max < A[I] ? A[I] : Max; } return Max; | ||
, float); | ||
checkVectorFunction<float>(ScalarFn, VectorFn, | ||
"fmax_cmp_max_lt_start_denorm_min"); | ||
} | ||
{ | ||
DEFINE_SCALAR_AND_VECTOR_FN1_TYPE( | ||
float Max = std::numeric_limits<float>::quiet_NaN(); | ||
, for (unsigned I = 0; I < 1024; | ||
I++) { Max = Max < A[I] ? A[I] : Max; } return Max; | ||
, float); | ||
checkVectorFunction<float>(ScalarFn, VectorFn, | ||
"fmax_cmp_max_lt_start_neg_nan"); | ||
} | ||
|
||
return 0; | ||
} |
14 changes: 14 additions & 0 deletions
14
SingleSource/UnitTests/Vectorizer/fmax-reduction.cpp.reference_output
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
Checking fmaxnum_start_neg_2 | ||
Checking fmaxnum_start_min | ||
Checking fmaxnum_start_denorm_min | ||
Checking fmaxnum_start_is_nan | ||
Checking fmax_strict_start_neg_2 | ||
Checking fmax_strict_start_min | ||
Checking fmax_strict_start_denorm_min | ||
Checking fmax_strict_start_nan | ||
Checking fmax_non_strict_start_neg_2 | ||
Checking fmax_cmp_max_gt_start_neg_2 | ||
Checking fmax_cmp_max_lt_start_neg_2 | ||
Checking fmax_cmp_max_lt_start_denorm_min | ||
Checking fmax_cmp_max_lt_start_neg_nan | ||
exit 0 |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be good to test signaling nans, but it will be busted all over the place
Test some denormals?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added some tests with denormals both as start value and in the inputs, thanks
At least for AArch64, the tests also pass when replacing all quiet nans with signaling nans with llvm/llvm-project#148239, as it just checks if it matches the behavior of the scalar loop.
But in general it may be a bit risky to check signaling NaNs, as the behavior may not be 100% consistent across platforms?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It should be, but the state is in flux and has never been consistent