476 lines · c
1//===-- FPMatchers.h --------------------------------------------*- C++ -*-===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#ifndef LLVM_LIBC_TEST_UNITTEST_FPMATCHER_H10#define LLVM_LIBC_TEST_UNITTEST_FPMATCHER_H11 12#include "src/__support/CPP/array.h"13#include "src/__support/CPP/type_traits.h"14#include "src/__support/FPUtil/FEnvImpl.h"15#include "src/__support/FPUtil/FPBits.h"16#include "src/__support/FPUtil/fpbits_str.h"17#include "src/__support/libc_errno.h"18#include "src/__support/macros/config.h"19#include "src/__support/macros/optimization.h"20#include "src/__support/macros/properties/architectures.h"21#include "test/UnitTest/ErrnoCheckingTest.h"22#include "test/UnitTest/RoundingModeUtils.h"23#include "test/UnitTest/StringUtils.h"24#include "test/UnitTest/Test.h"25 26#include "hdr/math_macros.h"27 28using LIBC_NAMESPACE::Sign;29 30namespace LIBC_NAMESPACE_DECL {31namespace testing {32 33template <typename T, TestCond Condition> class FPMatcher : public Matcher<T> {34 static_assert(cpp::is_floating_point_v<T>,35 "FPMatcher can only be used with floating point values.");36 static_assert(Condition == TestCond::EQ || Condition == TestCond::NE,37 "Unsupported FPMatcher test condition.");38 39 T expected;40 T actual;41 42public:43 FPMatcher(T expectedValue) : expected(expectedValue) {}44 45 bool match(T actualValue) {46 actual = actualValue;47 fputil::FPBits<T> actualBits(actual), expectedBits(expected);48 if (Condition == TestCond::EQ)49 return (actualBits.is_nan() && expectedBits.is_nan()) ||50 (actualBits.uintval() == expectedBits.uintval());51 52 // If condition == TestCond::NE.53 if (actualBits.is_nan())54 return !expectedBits.is_nan();55 return expectedBits.is_nan() ||56 (actualBits.uintval() != expectedBits.uintval());57 }58 59 void explainError() override {60 tlog << "Expected floating point value: "61 << str(fputil::FPBits<T>(expected)) << '\n';62 tlog << "Actual floating point value: " << str(fputil::FPBits<T>(actual))63 << '\n';64 }65};66 67template <typename T, TestCond Condition> class CFPMatcher : public Matcher<T> {68 static_assert(69 cpp::is_complex_v<T>,70 "CFPMatcher can only be used with complex floating point values.");71 static_assert(Condition == TestCond::EQ || Condition == TestCond::NE,72 "Unsupported CFPMatcher test condition.");73 74 T expected;75 T actual;76 77public:78 CFPMatcher(T expectedValue) : expected(expectedValue) {}79 80 template <typename CFT> bool matchComplex() {81 CFT *actualCmplxPtr = reinterpret_cast<CFT *>(&actual);82 CFT *expectedCmplxPtr = reinterpret_cast<CFT *>(&expected);83 CFT actualReal = actualCmplxPtr[0];84 CFT actualImag = actualCmplxPtr[1];85 CFT expectedReal = expectedCmplxPtr[0];86 CFT expectedImag = expectedCmplxPtr[1];87 fputil::FPBits<CFT> actualRealBits(actualReal),88 expectedRealBits(expectedReal);89 fputil::FPBits<CFT> actualImagBits(actualImag),90 expectedImagBits(expectedImag);91 if (Condition == TestCond::EQ)92 return ((actualRealBits.is_nan() && expectedRealBits.is_nan()) ||93 (actualRealBits.uintval() == expectedRealBits.uintval())) &&94 ((actualImagBits.is_nan() && expectedImagBits.is_nan()) ||95 (actualImagBits.uintval() == expectedImagBits.uintval()));96 97 // If condition == TestCond::NE.98 if (actualRealBits.is_nan() && expectedRealBits.is_nan())99 return !expectedRealBits.is_nan() && !expectedImagBits.is_nan();100 if (actualRealBits.is_nan())101 return !expectedRealBits.is_nan();102 if (actualImagBits.is_nan())103 return !expectedImagBits.is_nan();104 return (expectedRealBits.is_nan() ||105 actualRealBits.uintval() != expectedRealBits.uintval()) &&106 (expectedImagBits.is_nan() ||107 actualImagBits.uintval() != expectedImagBits.uintval());108 }109 110 template <typename CFT> void explainErrorComplex() {111 CFT *actualCmplxPtr = reinterpret_cast<CFT *>(&actual);112 CFT *expectedCmplxPtr = reinterpret_cast<CFT *>(&expected);113 CFT actualReal = actualCmplxPtr[0];114 CFT actualImag = actualCmplxPtr[1];115 CFT expectedReal = expectedCmplxPtr[0];116 CFT expectedImag = expectedCmplxPtr[1];117 tlog << "Expected complex floating point value: "118 << str(fputil::FPBits<CFT>(expectedReal)) + " + " +119 str(fputil::FPBits<CFT>(expectedImag)) + "i"120 << '\n';121 tlog << "Actual complex floating point value: "122 << str(fputil::FPBits<CFT>(actualReal)) + " + " +123 str(fputil::FPBits<CFT>(actualImag)) + "i"124 << '\n';125 }126 127 bool match(T actualValue) {128 actual = actualValue;129#ifndef LIBC_COMPILER_IS_MSVC130 if constexpr (cpp::is_complex_type_same<T, _Complex float>())131 return matchComplex<float>();132 else if constexpr (cpp::is_complex_type_same<T, _Complex double>())133 return matchComplex<double>();134 else if constexpr (cpp::is_complex_type_same<T, _Complex long double>())135 return matchComplex<long double>();136#ifdef LIBC_TYPES_HAS_CFLOAT16137 else if constexpr (cpp::is_complex_type_same<T, cfloat16>())138 return matchComplex<float16>();139#endif // LIBC_TYPES_HAS_CFLOAT16140#ifdef LIBC_TYPES_HAS_CFLOAT128141 else if constexpr (cpp::is_complex_type_same<T, cfloat128>())142 return matchComplex<float128>();143#endif // LIBC_TYPES_HAS_CFLOAT128144#else // LIBC_COMPILER_IS_MSVC145 return true;146#endif // LIBC_COMPILER_IS_MSVC147 }148 149 void explainError() override {150#ifndef LIBC_COMPILER_IS_MSVC151 if constexpr (cpp::is_complex_type_same<T, _Complex float>())152 return explainErrorComplex<float>();153 else if constexpr (cpp::is_complex_type_same<T, _Complex double>())154 return explainErrorComplex<double>();155 else if constexpr (cpp::is_complex_type_same<T, _Complex long double>())156 return explainErrorComplex<long double>();157#ifdef LIBC_TYPES_HAS_CFLOAT16158 else if constexpr (cpp::is_complex_type_same<T, cfloat16>())159 return explainErrorComplex<float16>();160#endif // LIBC_TYPES_HAS_CFLOAT16161#ifdef LIBC_TYPES_HAS_CFLOAT128162 else if constexpr (cpp::is_complex_type_same<T, cfloat128>())163 return explainErrorComplex<float128>();164#endif // LIBC_TYPES_HAS_CFLOAT128165#endif // LIBC_COMPILER_IS_MSVC166 }167};168 169template <TestCond C, typename T> FPMatcher<T, C> getMatcher(T expectedValue) {170 return FPMatcher<T, C>(expectedValue);171}172 173template <TestCond C, typename T>174CFPMatcher<T, C> getMatcherComplex(T expectedValue) {175 return CFPMatcher<T, C>(expectedValue);176}177 178template <typename T> struct FPTest : public ErrnoCheckingTest {179 using FPBits = LIBC_NAMESPACE::fputil::FPBits<T>;180 using StorageType = typename FPBits::StorageType;181 static constexpr StorageType STORAGE_MAX =182 LIBC_NAMESPACE::cpp::numeric_limits<StorageType>::max();183 static constexpr T zero = FPBits::zero(Sign::POS).get_val();184 static constexpr T neg_zero = FPBits::zero(Sign::NEG).get_val();185 static constexpr T aNaN = FPBits::quiet_nan(Sign::POS).get_val();186 static constexpr T neg_aNaN = FPBits::quiet_nan(Sign::NEG).get_val();187 // TODO: make this static constexpr188 const T sNaN = FPBits::signaling_nan().get_val();189 static constexpr T inf = FPBits::inf(Sign::POS).get_val();190 static constexpr T neg_inf = FPBits::inf(Sign::NEG).get_val();191 static constexpr T min_normal = FPBits::min_normal().get_val();192 static constexpr T max_normal = FPBits::max_normal(Sign::POS).get_val();193 static constexpr T neg_max_normal = FPBits::max_normal(Sign::NEG).get_val();194 static constexpr T min_denormal = FPBits::min_subnormal().get_val();195 static constexpr T max_denormal = FPBits::max_subnormal().get_val();196 197 static constexpr int N_ROUNDING_MODES = 4;198 static constexpr fputil::testing::RoundingMode ROUNDING_MODES[4] = {199 fputil::testing::RoundingMode::Nearest,200 fputil::testing::RoundingMode::Upward,201 fputil::testing::RoundingMode::Downward,202 fputil::testing::RoundingMode::TowardZero,203 };204 205 void TearDown() override {206 // TODO (PR 135320): Remove this override once all FPTest instances are207 // updated to validate or ignore errno.208 libc_errno = 0;209 ErrnoCheckingTest::TearDown();210 }211};212 213// Add facility to test Flush-Denormal-To-Zero (FTZ) and Denormal-As-Zero (DAZ)214// modes.215// These tests to ensure that our implementations will not crash under these216// modes.217#if defined(LIBC_TARGET_ARCH_IS_X86_64) && __has_builtin(__builtin_ia32_stmxcsr)218 219#define LIBC_TEST_FTZ_DAZ220 221static constexpr unsigned FTZ = 0x8000; // Flush denormal to zero222static constexpr unsigned DAZ = 0x0040; // Denormal as zero223 224struct ModifyMXCSR {225 ModifyMXCSR(unsigned flags) {226 old_mxcsr = __builtin_ia32_stmxcsr();227 __builtin_ia32_ldmxcsr(old_mxcsr | flags);228 }229 230 ~ModifyMXCSR() { __builtin_ia32_ldmxcsr(old_mxcsr); }231 232private:233 unsigned old_mxcsr;234};235 236#endif237 238} // namespace testing239} // namespace LIBC_NAMESPACE_DECL240 241#define DECLARE_SPECIAL_CONSTANTS(T) \242 using FPBits = LIBC_NAMESPACE::fputil::FPBits<T>; \243 using StorageType = typename FPBits::StorageType; \244 \245 static constexpr StorageType STORAGE_MAX = \246 LIBC_NAMESPACE::cpp::numeric_limits<StorageType>::max(); \247 const T zero = FPBits::zero(Sign::POS).get_val(); \248 const T neg_zero = FPBits::zero(Sign::NEG).get_val(); \249 const T aNaN = FPBits::quiet_nan(Sign::POS).get_val(); \250 const T neg_aNaN = FPBits::quiet_nan(Sign::NEG).get_val(); \251 const T sNaN = FPBits::signaling_nan(Sign::POS).get_val(); \252 const T neg_sNaN = FPBits::signaling_nan(Sign::NEG).get_val(); \253 const T inf = FPBits::inf(Sign::POS).get_val(); \254 const T neg_inf = FPBits::inf(Sign::NEG).get_val(); \255 const T min_normal = FPBits::min_normal().get_val(); \256 const T max_normal = FPBits::max_normal(Sign::POS).get_val(); \257 const T neg_max_normal = FPBits::max_normal(Sign::NEG).get_val(); \258 const T min_denormal = FPBits::min_subnormal(Sign::POS).get_val(); \259 const T neg_min_denormal = FPBits::min_subnormal(Sign::NEG).get_val(); \260 const T max_denormal = FPBits::max_subnormal().get_val(); \261 static constexpr int UNKNOWN_MATH_ROUNDING_DIRECTION = 99; \262 static constexpr LIBC_NAMESPACE::cpp::array<int, 6> \263 MATH_ROUNDING_DIRECTIONS_INCLUDING_UNKNOWN = { \264 FP_INT_UPWARD, FP_INT_DOWNWARD, \265 FP_INT_TOWARDZERO, FP_INT_TONEARESTFROMZERO, \266 FP_INT_TONEAREST, UNKNOWN_MATH_ROUNDING_DIRECTION, \267 };268 269#define EXPECT_FP_EQ(expected, actual) \270 EXPECT_THAT(actual, LIBC_NAMESPACE::testing::getMatcher< \271 LIBC_NAMESPACE::testing::TestCond::EQ>(expected))272 273#define EXPECT_CFP_EQ(expected, actual) \274 EXPECT_THAT(actual, LIBC_NAMESPACE::testing::getMatcherComplex< \275 LIBC_NAMESPACE::testing::TestCond::EQ>(expected))276 277#define TEST_FP_EQ(expected, actual) \278 LIBC_NAMESPACE::testing::getMatcher<LIBC_NAMESPACE::testing::TestCond::EQ>( \279 expected) \280 .match(actual)281 282#define EXPECT_FP_IS_NAN(actual) EXPECT_TRUE((actual) != (actual))283 284#define ASSERT_FP_EQ(expected, actual) \285 ASSERT_THAT(actual, LIBC_NAMESPACE::testing::getMatcher< \286 LIBC_NAMESPACE::testing::TestCond::EQ>(expected))287 288#define EXPECT_FP_NE(expected, actual) \289 EXPECT_THAT(actual, LIBC_NAMESPACE::testing::getMatcher< \290 LIBC_NAMESPACE::testing::TestCond::NE>(expected))291 292#define ASSERT_FP_NE(expected, actual) \293 ASSERT_THAT(actual, LIBC_NAMESPACE::testing::getMatcher< \294 LIBC_NAMESPACE::testing::TestCond::NE>(expected))295 296#define EXPECT_MATH_ERRNO(expected) \297 do { \298 if ((LIBC_MATH & LIBC_MATH_NO_ERRNO) == 0) \299 if (math_errhandling & MATH_ERRNO) { \300 int actual = libc_errno; \301 libc_errno = 0; \302 EXPECT_EQ(actual, expected); \303 } \304 } while (0)305 306#define ASSERT_MATH_ERRNO(expected) \307 do { \308 if ((LIBC_MATH & LIBC_MATH_NO_ERRNO) == 0) \309 if (math_errhandling & MATH_ERRNO) { \310 int actual = libc_errno; \311 libc_errno = 0; \312 ASSERT_EQ(actual, expected); \313 } \314 } while (0)315 316#define EXPECT_FP_EXCEPTION(expected) \317 do { \318 if ((LIBC_MATH & LIBC_MATH_NO_EXCEPT) == 0) \319 if (math_errhandling & MATH_ERREXCEPT) { \320 EXPECT_EQ( \321 LIBC_NAMESPACE::fputil::test_except( \322 static_cast<int>(FE_ALL_EXCEPT)) & \323 ((expected) ? (expected) : static_cast<int>(FE_ALL_EXCEPT)), \324 (expected)); \325 } \326 } while (0)327 328#define ASSERT_FP_EXCEPTION(expected) \329 do { \330 if ((LIBC_MATH & LIBC_MATH_NO_EXCEPT) == 0) \331 if (math_errhandling & MATH_ERREXCEPT) { \332 ASSERT_EQ( \333 LIBC_NAMESPACE::fputil::test_except( \334 static_cast<int>(FE_ALL_EXCEPT)) & \335 ((expected) ? (expected) : static_cast<int>(FE_ALL_EXCEPT)), \336 (expected)); \337 } \338 } while (0)339 340#define EXPECT_FP_EQ_WITH_EXCEPTION(expected_val, actual_val, expected_except) \341 do { \342 LIBC_NAMESPACE::fputil::clear_except(static_cast<int>(FE_ALL_EXCEPT)); \343 EXPECT_FP_EQ(expected_val, actual_val); \344 EXPECT_FP_EXCEPTION(expected_except); \345 } while (0)346 347#define EXPECT_FP_IS_NAN_WITH_EXCEPTION(actual_val, expected_except) \348 do { \349 LIBC_NAMESPACE::fputil::clear_except(static_cast<int>(FE_ALL_EXCEPT)); \350 EXPECT_FP_IS_NAN(actual_val); \351 EXPECT_FP_EXCEPTION(expected_except); \352 } while (0)353 354#define EXPECT_FP_EQ_ROUNDING_MODE(expected, actual, rounding_mode) \355 do { \356 using namespace LIBC_NAMESPACE::fputil::testing; \357 ForceRoundingMode __r((rounding_mode)); \358 if (__r.success) { \359 EXPECT_FP_EQ((expected), (actual)); \360 } \361 } while (0)362 363#define EXPECT_FP_EQ_ROUNDING_NEAREST(expected, actual) \364 EXPECT_FP_EQ_ROUNDING_MODE((expected), (actual), RoundingMode::Nearest)365 366#define EXPECT_FP_EQ_ROUNDING_UPWARD(expected, actual) \367 EXPECT_FP_EQ_ROUNDING_MODE((expected), (actual), RoundingMode::Upward)368 369#define EXPECT_FP_EQ_ROUNDING_DOWNWARD(expected, actual) \370 EXPECT_FP_EQ_ROUNDING_MODE((expected), (actual), RoundingMode::Downward)371 372#define EXPECT_FP_EQ_ROUNDING_TOWARD_ZERO(expected, actual) \373 EXPECT_FP_EQ_ROUNDING_MODE((expected), (actual), RoundingMode::TowardZero)374 375#define EXPECT_FP_EQ_ALL_ROUNDING_1(expected, actual) \376 do { \377 EXPECT_FP_EQ_ROUNDING_NEAREST((expected), (actual)); \378 EXPECT_FP_EQ_ROUNDING_UPWARD((expected), (actual)); \379 EXPECT_FP_EQ_ROUNDING_DOWNWARD((expected), (actual)); \380 EXPECT_FP_EQ_ROUNDING_TOWARD_ZERO((expected), (actual)); \381 } while (0)382 383#define EXPECT_FP_EQ_ALL_ROUNDING_4(expected_nearest, expected_upward, \384 expected_downward, expected_toward_zero, \385 actual) \386 do { \387 EXPECT_FP_EQ_ROUNDING_NEAREST((expected_nearest), (actual)); \388 EXPECT_FP_EQ_ROUNDING_UPWARD((expected_upward), (actual)); \389 EXPECT_FP_EQ_ROUNDING_DOWNWARD((expected_downward), (actual)); \390 EXPECT_FP_EQ_ROUNDING_TOWARD_ZERO((expected_toward_zero), (actual)); \391 } while (0)392 393#define EXPECT_FP_EQ_ALL_ROUNDING_UNSUPPORTED(...) \394 static_assert(false, "Unsupported number of arguments")395 396#define EXPECT_FP_EQ_ALL_ROUNDING_GET_6TH_ARG(ARG1, ARG2, ARG3, ARG4, ARG5, \397 ARG6, ...) \398 ARG6399 400#define EXPECT_FP_EQ_ALL_ROUNDING_SELECTION(...) \401 EXPECT_FP_EQ_ALL_ROUNDING_GET_6TH_ARG( \402 __VA_ARGS__, EXPECT_FP_EQ_ALL_ROUNDING_4, \403 EXPECT_FP_EQ_ALL_ROUNDING_UNSUPPORTED, \404 EXPECT_FP_EQ_ALL_ROUNDING_UNSUPPORTED, EXPECT_FP_EQ_ALL_ROUNDING_1)405 406#define EXPECT_FP_EQ_ALL_ROUNDING(...) \407 EXPECT_FP_EQ_ALL_ROUNDING_SELECTION(__VA_ARGS__)(__VA_ARGS__)408 409#define ASSERT_FP_EQ_ROUNDING_MODE(expected, actual, rounding_mode) \410 do { \411 using namespace LIBC_NAMESPACE::fputil::testing; \412 ForceRoundingMode __r((rounding_mode)); \413 if (__r.success) { \414 ASSERT_FP_EQ((expected), (actual)); \415 } \416 } while (0)417 418#define ASSERT_FP_EQ_ROUNDING_NEAREST(expected, actual) \419 ASSERT_FP_EQ_ROUNDING_MODE((expected), (actual), RoundingMode::Nearest)420 421#define ASSERT_FP_EQ_ROUNDING_UPWARD(expected, actual) \422 ASSERT_FP_EQ_ROUNDING_MODE((expected), (actual), RoundingMode::Upward)423 424#define ASSERT_FP_EQ_ROUNDING_DOWNWARD(expected, actual) \425 ASSERT_FP_EQ_ROUNDING_MODE((expected), (actual), RoundingMode::Downward)426 427#define ASSERT_FP_EQ_ROUNDING_TOWARD_ZERO(expected, actual) \428 ASSERT_FP_EQ_ROUNDING_MODE((expected), (actual), RoundingMode::TowardZero)429 430#define EXPECT_FP_EQ_WITH_EXCEPTION_ROUNDING_MODE( \431 expected, actual, expected_except, rounding_mode) \432 do { \433 using namespace LIBC_NAMESPACE::fputil::testing; \434 ForceRoundingMode __r((rounding_mode)); \435 if (__r.success) { \436 LIBC_NAMESPACE::fputil::clear_except(static_cast<int>(FE_ALL_EXCEPT)); \437 EXPECT_FP_EQ((expected), (actual)); \438 EXPECT_FP_EXCEPTION(expected_except); \439 } \440 } while (0)441 442#define EXPECT_FP_EQ_WITH_EXCEPTION_ROUNDING_NEAREST(expected, actual, \443 expected_except) \444 EXPECT_FP_EQ_WITH_EXCEPTION_ROUNDING_MODE( \445 (expected), (actual), (expected_except), RoundingMode::Nearest)446 447#define EXPECT_FP_EQ_WITH_EXCEPTION_ROUNDING_UPWARD(expected, actual, \448 expected_except) \449 EXPECT_FP_EQ_WITH_EXCEPTION_ROUNDING_MODE( \450 (expected), (actual), (expected_except), RoundingMode::Upward)451 452#define EXPECT_FP_EQ_WITH_EXCEPTION_ROUNDING_DOWNWARD(expected, actual, \453 expected_except) \454 EXPECT_FP_EQ_WITH_EXCEPTION_ROUNDING_MODE( \455 (expected), (actual), (expected_except), RoundingMode::Downward)456 457#define EXPECT_FP_EQ_WITH_EXCEPTION_ROUNDING_TOWARD_ZERO(expected, actual, \458 expected_except) \459 EXPECT_FP_EQ_WITH_EXCEPTION_ROUNDING_MODE( \460 (expected), (actual), (expected_except), RoundingMode::TowardZero)461 462#define EXPECT_FP_EQ_WITH_EXCEPTION_ALL_ROUNDING(expected, actual, \463 expected_except) \464 do { \465 EXPECT_FP_EQ_WITH_EXCEPTION_ROUNDING_NEAREST((expected), (actual), \466 (expected_except)); \467 EXPECT_FP_EQ_WITH_EXCEPTION_ROUNDING_UPWARD((expected), (actual), \468 (expected_except)); \469 EXPECT_FP_EQ_WITH_EXCEPTION_ROUNDING_DOWNWARD((expected), (actual), \470 (expected_except)); \471 EXPECT_FP_EQ_WITH_EXCEPTION_ROUNDING_TOWARD_ZERO((expected), (actual), \472 (expected_except)); \473 } while (0)474 475#endif // LLVM_LIBC_TEST_UNITTEST_FPMATCHER_H476