From e15c6eb760dc8167303c8dba986707b3b84605f7 Mon Sep 17 00:00:00 2001 From: Wenju He Date: Wed, 6 Aug 2025 00:08:04 +0800 Subject: [PATCH] Fix 'fpclassify: ambiguous call' compile fail in MSVC 2022 (#2426) Similar to #2219, we see "'fpclassify': ambiguous call" error in test_conformance\basic\test_fpmath.cpp due to missing constexpr at https://github.com/KhronosGroup/OpenCL-CTS/blob/9265cbb2c274/test_conformance/basic/test_fpmath.cpp#L104 This PR fixes the issue by moving utility function isnan_fp in testHarness.h and use it. Note this PR doesn't modify use of isnan in many tests where only float/double values are checked. --- test_common/harness/mathHelpers.h | 35 +++++++++++++++ test_conformance/basic/test_explicit_s2v.cpp | 37 ++++++---------- test_conformance/basic/test_fpmath.cpp | 20 +-------- .../conversions/basic_test_conversions.cpp | 19 +------- .../binary_two_results_i_half.cpp | 4 +- .../math_brute_force/ternary_half.cpp | 44 +++++++++---------- .../unary_two_results_half.cpp | 14 +++--- test_conformance/math_brute_force/utility.h | 11 +---- .../relationals/test_comparisons_fp.cpp | 6 +-- test_conformance/select/util_select.cpp | 8 ++-- test_conformance/spirv_new/testBase.h | 1 + test_conformance/spirv_new/test_decorate.cpp | 2 +- 12 files changed, 94 insertions(+), 107 deletions(-) create mode 100644 test_common/harness/mathHelpers.h diff --git a/test_common/harness/mathHelpers.h b/test_common/harness/mathHelpers.h new file mode 100644 index 00000000..cdbf2bfe --- /dev/null +++ b/test_common/harness/mathHelpers.h @@ -0,0 +1,35 @@ +// +// Copyright (c) 2025 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#ifndef _mathHelpers_h +#define _mathHelpers_h + +#if defined(__APPLE__) +#include +#else +#include +#endif +#include + +template inline bool isnan_fp(const T &v) { return std::isnan(v); } + +template <> inline bool isnan_fp(const cl_half &v) +{ + uint16_t h_exp = (((cl_half)v) >> (CL_HALF_MANT_DIG - 1)) & 0x1F; + uint16_t h_mant = ((cl_half)v) & 0x3FF; + return (h_exp == 0x1F && h_mant != 0); +} + +#endif // _mathHelpers_h diff --git a/test_conformance/basic/test_explicit_s2v.cpp b/test_conformance/basic/test_explicit_s2v.cpp index a5ae452f..418e184a 100644 --- a/test_conformance/basic/test_explicit_s2v.cpp +++ b/test_conformance/basic/test_explicit_s2v.cpp @@ -14,7 +14,6 @@ // limitations under the License. // #include -using std::isnan; #include "harness/compat.h" #include @@ -26,6 +25,7 @@ using std::isnan; #include #include "harness/conversions.h" +#include "harness/mathHelpers.h" #include "harness/typeWrappers.h" extern cl_half_rounding_mode halfRoundingMode; @@ -102,16 +102,6 @@ const char * kernel_explicit_s2v_set[NUM_VEC_TYPES][NUM_VEC_TYPES][5] = { // clang-format on -bool IsHalfNaN(cl_half v) -{ - // Extract FP16 exponent and mantissa - uint16_t h_exp = (((cl_half)v) >> (CL_HALF_MANT_DIG - 1)) & 0x1F; - uint16_t h_mant = ((cl_half)v) & 0x3FF; - - // NaN test - return (h_exp == 0x1F && h_mant != 0); -} - static int test_explicit_s2v_function(cl_context context, cl_command_queue queue, cl_kernel kernel, ExplicitType srcType, unsigned int count, @@ -183,20 +173,21 @@ static int test_explicit_s2v_function(cl_context context, { bool isSrcNaN = (((srcType == kHalf) - && IsHalfNaN(*reinterpret_cast(inPtr))) + && isnan_fp(*reinterpret_cast(inPtr))) || ((srcType == kFloat) - && isnan(*reinterpret_cast(inPtr))) + && isnan_fp(*reinterpret_cast(inPtr))) || ((srcType == kDouble) - && isnan(*reinterpret_cast(inPtr)))); - bool isDestNaN = (((destType == kHalf) - && IsHalfNaN(*reinterpret_cast( - outPtr + destTypeSize * s))) - || ((destType == kFloat) - && isnan(*reinterpret_cast( - outPtr + destTypeSize * s))) - || ((destType == kDouble) - && isnan(*reinterpret_cast( - outPtr + destTypeSize * s)))); + && isnan_fp(*reinterpret_cast(inPtr)))); + bool isDestNaN = + (((destType == kHalf) + && isnan_fp(*reinterpret_cast( + outPtr + destTypeSize * s))) + || ((destType == kFloat) + && isnan_fp(*reinterpret_cast( + outPtr + destTypeSize * s))) + || ((destType == kDouble) + && isnan_fp(*reinterpret_cast( + outPtr + destTypeSize * s)))); if (isSrcNaN && isDestNaN) { diff --git a/test_conformance/basic/test_fpmath.cpp b/test_conformance/basic/test_fpmath.cpp index c39a2fec..f8f39ae0 100644 --- a/test_conformance/basic/test_fpmath.cpp +++ b/test_conformance/basic/test_fpmath.cpp @@ -14,6 +14,7 @@ // limitations under the License. // #include "harness/compat.h" +#include "harness/mathHelpers.h" #include "harness/rounding_mode.h" #include "harness/stringHelpers.h" @@ -57,16 +58,6 @@ template double toDouble(T val) return val; } -bool isHalfNan(cl_half v) -{ - // Extract FP16 exponent and mantissa - uint16_t h_exp = (v >> (CL_HALF_MANT_DIG - 1)) & 0x1F; - uint16_t h_mant = v & 0x3FF; - - // NaN test - return (h_exp == 0x1F && h_mant != 0); -} - cl_half half_plus(cl_half a, cl_half b) { return HFF(std::plus()(HTF(a), HTF(b))); @@ -101,14 +92,7 @@ int verify_fp(std::vector (&input)[2], std::vector &output, T r = test.ref(inA[i], inB[i]); bool both_nan = false; - if (std::is_same::value) - { - both_nan = isHalfNan(r) && isHalfNan(output[i]); - } - else if (std::is_floating_point::value) - { - both_nan = std::isnan(r) && std::isnan(output[i]); - } + both_nan = isnan_fp(r) && isnan_fp(output[i]); // If not both nan, check if the result is the same if (!both_nan && (r != output[i])) diff --git a/test_conformance/conversions/basic_test_conversions.cpp b/test_conformance/conversions/basic_test_conversions.cpp index 59d41e55..d4f6d366 100644 --- a/test_conformance/conversions/basic_test_conversions.cpp +++ b/test_conformance/conversions/basic_test_conversions.cpp @@ -13,6 +13,7 @@ // See the License for the specific language governing permissions and // limitations under the License. // +#include "harness/mathHelpers.h" #include "harness/testHarness.h" #include "harness/compat.h" #include "harness/ThreadPool.h" @@ -955,24 +956,6 @@ void MapResultValuesComplete(const std::unique_ptr &info) // destroyed automatically soon after we exit. } -template static bool isnan_fp(const T &v) -{ - if (std::is_same::value) - { - uint16_t h_exp = (((cl_half)v) >> (CL_HALF_MANT_DIG - 1)) & 0x1F; - uint16_t h_mant = ((cl_half)v) & 0x3FF; - return (h_exp == 0x1F && h_mant != 0); - } - else - { -#if !defined(_WIN32) - return std::isnan(v); -#else - return _isnan(v); -#endif - } -} - template void ZeroNanToIntCases(cl_uint count, void *mapped, Type outType, void *input) { diff --git a/test_conformance/math_brute_force/binary_two_results_i_half.cpp b/test_conformance/math_brute_force/binary_two_results_i_half.cpp index a2379431..0b48e33e 100644 --- a/test_conformance/math_brute_force/binary_two_results_i_half.cpp +++ b/test_conformance/math_brute_force/binary_two_results_i_half.cpp @@ -260,7 +260,7 @@ int TestFunc_HalfI_Half_Half(const Func *f, MTdata d, bool relaxedMode) if (t[j] == q[j] && t2[j] == q2[j]) continue; // Check for paired NaNs - if (IsHalfNaN(t[j]) && IsHalfNaN(q[j]) && t2[j] == q2[j]) + if (isnan_fp(t[j]) && isnan_fp(q[j]) && t2[j] == q2[j]) continue; cl_half test = ((cl_half *)q)[j]; @@ -282,7 +282,7 @@ int TestFunc_HalfI_Half_Half(const Func *f, MTdata d, bool relaxedMode) // then the standard either neglects to say what is returned // in iptr or leaves it undefined or implementation defined. int iptrUndefined = IsHalfInfinity(p[j]) || (HTF(p2[j]) == 0.0f) - || IsHalfNaN(p2[j]) || IsHalfNaN(p[j]); + || isnan_fp(p2[j]) || isnan_fp(p[j]); if (iptrUndefined) iErr = 0; int fail = !(fabsf(err) <= half_ulps && iErr == 0); diff --git a/test_conformance/math_brute_force/ternary_half.cpp b/test_conformance/math_brute_force/ternary_half.cpp index 843ceaa0..08c39900 100644 --- a/test_conformance/math_brute_force/ternary_half.cpp +++ b/test_conformance/math_brute_force/ternary_half.cpp @@ -274,10 +274,10 @@ int TestFunc_Half_Half_Half_Half(const Func *f, MTdata d, bool relaxedMode) if (skipNanInf) { if (overflow[j] || IsHalfInfinity(correct) - || IsHalfNaN(correct) || IsHalfInfinity(hp0[j]) - || IsHalfNaN(hp0[j]) || IsHalfInfinity(hp1[j]) - || IsHalfNaN(hp1[j]) || IsHalfInfinity(hp2[j]) - || IsHalfNaN(hp2[j])) + || isnan_fp(correct) || IsHalfInfinity(hp0[j]) + || isnan_fp(hp0[j]) || IsHalfInfinity(hp1[j]) + || isnan_fp(hp1[j]) || IsHalfInfinity(hp2[j]) + || isnan_fp(hp2[j])) continue; } @@ -318,9 +318,9 @@ int TestFunc_Half_Half_Half_Half(const Func *f, MTdata d, bool relaxedMode) // Note: no double rounding here. Reference // functions calculate in single precision. if (IsHalfInfinity(correct2) - || IsHalfNaN(correct2) + || isnan_fp(correct2) || IsHalfInfinity(correct3) - || IsHalfNaN(correct3)) + || isnan_fp(correct3)) continue; } @@ -381,13 +381,13 @@ int TestFunc_Half_Half_Half_Half(const Func *f, MTdata d, bool relaxedMode) // Note: no double rounding here. Reference // functions calculate in single precision. if (IsHalfInfinity(correct2) - || IsHalfNaN(correct2) + || isnan_fp(correct2) || IsHalfInfinity(correct3) - || IsHalfNaN(correct3) + || isnan_fp(correct3) || IsHalfInfinity(correct4) - || IsHalfNaN(correct4) + || isnan_fp(correct4) || IsHalfInfinity(correct5) - || IsHalfNaN(correct5)) + || isnan_fp(correct5)) continue; } @@ -474,13 +474,13 @@ int TestFunc_Half_Half_Half_Half(const Func *f, MTdata d, bool relaxedMode) // Note: no double rounding here. Reference // functions calculate in single precision. if (IsHalfInfinity(correct2) - || IsHalfNaN(correct2) + || isnan_fp(correct2) || IsHalfInfinity(correct3) - || IsHalfNaN(correct3) + || isnan_fp(correct3) || IsHalfInfinity(correct4) - || IsHalfNaN(correct4) + || isnan_fp(correct4) || IsHalfInfinity(correct5) - || IsHalfNaN(correct5)) + || isnan_fp(correct5)) continue; } @@ -551,9 +551,9 @@ int TestFunc_Half_Half_Half_Half(const Func *f, MTdata d, bool relaxedMode) // Note: no double rounding here. Reference // functions calculate in single precision. if (IsHalfInfinity(correct2) - || IsHalfNaN(correct2) + || isnan_fp(correct2) || IsHalfInfinity(correct3) - || IsHalfNaN(correct3)) + || isnan_fp(correct3)) continue; } @@ -613,13 +613,13 @@ int TestFunc_Half_Half_Half_Half(const Func *f, MTdata d, bool relaxedMode) // Note: no double rounding here. Reference // functions calculate in single precision. if (IsHalfInfinity(correct2) - || IsHalfNaN(correct2) + || isnan_fp(correct2) || IsHalfInfinity(correct3) - || IsHalfNaN(correct3) + || isnan_fp(correct3) || IsHalfInfinity(correct4) - || IsHalfNaN(correct4) + || isnan_fp(correct4) || IsHalfInfinity(correct5) - || IsHalfNaN(correct5)) + || isnan_fp(correct5)) continue; } @@ -689,9 +689,9 @@ int TestFunc_Half_Half_Half_Half(const Func *f, MTdata d, bool relaxedMode) // Note: no double rounding here. Reference // functions calculate in single precision. if (IsHalfInfinity(correct2) - || IsHalfNaN(correct2) + || isnan_fp(correct2) || IsHalfInfinity(correct3) - || IsHalfNaN(correct3)) + || isnan_fp(correct3)) continue; } diff --git a/test_conformance/math_brute_force/unary_two_results_half.cpp b/test_conformance/math_brute_force/unary_two_results_half.cpp index 683e1492..63398028 100644 --- a/test_conformance/math_brute_force/unary_two_results_half.cpp +++ b/test_conformance/math_brute_force/unary_two_results_half.cpp @@ -249,9 +249,9 @@ int TestFunc_Half2_Half(const Func *f, MTdata d, bool relaxedMode) if (skipNanInf && overflow[j]) continue; // Note: no double rounding here. Reference functions // calculate in single precision. - if (IsHalfInfinity(correct1) || IsHalfNaN(correct1) - || IsHalfInfinity(correct2) || IsHalfNaN(correct2) - || IsHalfInfinity(pIn[j]) || IsHalfNaN(pIn[j])) + if (IsHalfInfinity(correct1) || isnan_fp(correct1) + || IsHalfInfinity(correct2) || isnan_fp(correct2) + || IsHalfInfinity(pIn[j]) || isnan_fp(pIn[j])) continue; } @@ -320,13 +320,13 @@ int TestFunc_Half2_Half(const Func *f, MTdata d, bool relaxedMode) // Note: no double rounding here. Reference // functions calculate in single precision. if (IsHalfInfinity(correctp) - || IsHalfNaN(correctp) + || isnan_fp(correctp) || IsHalfInfinity(correctn) - || IsHalfNaN(correctn) + || isnan_fp(correctn) || IsHalfInfinity(correct2p) - || IsHalfNaN(correct2p) + || isnan_fp(correct2p) || IsHalfInfinity(correct2n) - || IsHalfNaN(correct2n)) + || isnan_fp(correct2n)) continue; } diff --git a/test_conformance/math_brute_force/utility.h b/test_conformance/math_brute_force/utility.h index a43f3a64..f5a30f86 100644 --- a/test_conformance/math_brute_force/utility.h +++ b/test_conformance/math_brute_force/utility.h @@ -19,6 +19,7 @@ #include "harness/compat.h" #include "harness/rounding_mode.h" #include "harness/fpcontrol.h" +#include "harness/mathHelpers.h" #include "harness/testHarness.h" #include "harness/ThreadPool.h" #include "harness/conversions.h" @@ -172,16 +173,6 @@ inline int IsFloatNaN(double x) return ((u.u & 0x7fffffffU) > 0x7F800000U); } -inline bool IsHalfNaN(const cl_half v) -{ - // Extract FP16 exponent and mantissa - uint16_t h_exp = (((cl_half)v) >> (CL_HALF_MANT_DIG - 1)) & 0x1F; - uint16_t h_mant = ((cl_half)v) & 0x3FF; - - // NaN test - return (h_exp == 0x1F && h_mant != 0); -} - inline bool IsHalfInfinity(const cl_half v) { // Extract FP16 exponent and mantissa diff --git a/test_conformance/relationals/test_comparisons_fp.cpp b/test_conformance/relationals/test_comparisons_fp.cpp index 66ab0729..79de562a 100644 --- a/test_conformance/relationals/test_comparisons_fp.cpp +++ b/test_conformance/relationals/test_comparisons_fp.cpp @@ -22,6 +22,7 @@ #include #include +#include "harness/mathHelpers.h" #include "harness/stringHelpers.h" #include @@ -368,9 +369,8 @@ int RelationalsFPTest::test_equiv_kernel(unsigned int vecSize, { if (gInfNanSupport == 0) { - float a = inDataA[i * vecSize + j]; - float b = inDataB[i * vecSize + j]; - if (isnan(a) || isnan(b)) + if (isnan_fp(inDataA[i * vecSize + j]) + || isnan_fp(inDataB[i * vecSize + j])) fail = 0; else fail = 1; diff --git a/test_conformance/select/util_select.cpp b/test_conformance/select/util_select.cpp index a685b7f6..71653a87 100644 --- a/test_conformance/select/util_select.cpp +++ b/test_conformance/select/util_select.cpp @@ -14,6 +14,8 @@ // limitations under the License. // #include "harness/errorHelpers.h" +#include "harness/mathHelpers.h" +#include "harness/testHarness.h" #include #include @@ -834,9 +836,9 @@ size_t check_half(const void *const test, const void *const correct, // Allow nans to be binary different for (i = 0; i < count; i++) { - float fcorrect = cl_half_to_float(c[i]); - float ftest = cl_half_to_float(t[i]); - if ((t[i] != c[i]) && !(isnan(fcorrect) && isnan(ftest))) + if ((t[i] != c[i]) + && !(isnan_fp(cl_half_to_float(c[i])) + && isnan_fp(cl_half_to_float(t[i])))) { log_error("\n(check_half) Error for vector size %zu found at " "0x%8.8zx (of 0x%8.8zx): " diff --git a/test_conformance/spirv_new/testBase.h b/test_conformance/spirv_new/testBase.h index 54fe15bd..5ea415a9 100644 --- a/test_conformance/spirv_new/testBase.h +++ b/test_conformance/spirv_new/testBase.h @@ -20,6 +20,7 @@ #define _testBase_h #include "harness/compat.h" +#include "harness/mathHelpers.h" #include "harness/rounding_mode.h" #include diff --git a/test_conformance/spirv_new/test_decorate.cpp b/test_conformance/spirv_new/test_decorate.cpp index fc9fc522..f9380611 100644 --- a/test_conformance/spirv_new/test_decorate.cpp +++ b/test_conformance/spirv_new/test_decorate.cpp @@ -231,7 +231,7 @@ static inline f = cl_half_to_float(cl_half_from_float(f, half_rounding)); To val = static_cast(std::min(std::max(f, loVal), hiVal)); - if (isnan(cl_half_to_float(rhs))) + if (isnan_fp(rhs)) { val = 0; }