[libc][math] Add test and fix atan2f crashing when flush-denorm-to-zero (FTZ) and denorm-as-zero (DAZ) modes are set. (#112828)

This commit is contained in:
lntue
2024-10-18 14:56:23 -04:00
committed by GitHub
parent caa9e41814
commit 952dafb08e
3 changed files with 72 additions and 5 deletions

View File

@@ -246,12 +246,18 @@ LLVM_LIBC_FUNCTION(float, atan2f, (float y, float x)) {
uint32_t y_abs = y_bits.uintval();
uint32_t max_abs = x_abs > y_abs ? x_abs : y_abs;
uint32_t min_abs = x_abs <= y_abs ? x_abs : y_abs;
float num_f = FPBits(min_abs).get_val();
float den_f = FPBits(max_abs).get_val();
double num_d = static_cast<double>(num_f);
double den_d = static_cast<double>(den_f);
if (LIBC_UNLIKELY(max_abs >= 0x7f80'0000U || min_abs == 0U)) {
if (LIBC_UNLIKELY(max_abs >= 0x7f80'0000U || num_d == 0.0)) {
if (x_bits.is_nan() || y_bits.is_nan())
return FPBits::quiet_nan().get_val();
size_t x_except = x_abs == 0 ? 0 : (x_abs == 0x7f80'0000 ? 2 : 1);
size_t y_except = y_abs == 0 ? 0 : (y_abs == 0x7f80'0000 ? 2 : 1);
double x_d = static_cast<double>(x);
double y_d = static_cast<double>(y);
size_t x_except = (x_d == 0.0) ? 0 : (x_abs == 0x7f80'0000 ? 2 : 1);
size_t y_except = (y_d == 0.0) ? 0 : (y_abs == 0x7f80'0000 ? 2 : 1);
// Exceptional cases:
// EXCEPT[y_except][x_except][x_is_neg]
@@ -275,8 +281,6 @@ LLVM_LIBC_FUNCTION(float, atan2f, (float y, float x)) {
bool recip = x_abs < y_abs;
double final_sign = IS_NEG[(x_sign != y_sign) != recip];
fputil::DoubleDouble const_term = CONST_ADJ[x_sign][y_sign][recip];
double num_d = static_cast<double>(FPBits(min_abs).get_val());
double den_d = static_cast<double>(FPBits(max_abs).get_val());
double q_d = num_d / den_d;
double k_d = fputil::nearest_integer(q_d * 0x1.0p4f);

View File

@@ -16,6 +16,7 @@
#include "src/__support/FPUtil/FPBits.h"
#include "src/__support/FPUtil/fpbits_str.h"
#include "src/__support/macros/config.h"
#include "src/__support/macros/properties/architectures.h"
#include "test/UnitTest/RoundingModeUtils.h"
#include "test/UnitTest/StringUtils.h"
#include "test/UnitTest/Test.h"
@@ -192,6 +193,31 @@ template <typename T> struct FPTest : public Test {
};
};
// Add facility to test Flush-Denormal-To-Zero (FTZ) and Denormal-As-Zero (DAZ)
// modes.
// These tests to ensure that our implementations will not crash under these
// modes.
#if defined(LIBC_TARGET_ARCH_IS_X86_64) && __has_builtin(__builtin_ia32_stmxcsr)
#define LIBC_TEST_FTZ_DAZ
static constexpr unsigned FTZ = 0x8000; // Flush denormal to zero
static constexpr unsigned DAZ = 0x0040; // Denormal as zero
struct ModifyMXCSR {
ModifyMXCSR(unsigned flags) {
old_mxcsr = __builtin_ia32_stmxcsr();
__builtin_ia32_ldmxcsr(old_mxcsr | flags);
}
~ModifyMXCSR() { __builtin_ia32_ldmxcsr(old_mxcsr); }
private:
unsigned old_mxcsr;
};
#endif
} // namespace testing
} // namespace LIBC_NAMESPACE_DECL

View File

@@ -58,3 +58,40 @@ TEST_F(LlvmLibcAtan2fTest, SpecialNumbers) {
// EXPECT_FP_EXCEPTION(0);
EXPECT_MATH_ERRNO(0);
}
#ifdef LIBC_TEST_FTZ_DAZ
using namespace LIBC_NAMESPACE::testing;
TEST_F(LlvmLibcAtan2fTest, FTZMode) {
ModifyMXCSR mxcsr(FTZ);
EXPECT_FP_EQ(0x1.921fb6p-1f,
LIBC_NAMESPACE::atan2f(min_denormal, min_denormal));
EXPECT_FP_EQ(0x1.000002p-23f,
LIBC_NAMESPACE::atan2f(min_denormal, max_denormal));
EXPECT_FP_EQ(0x1.921fb4p0f,
LIBC_NAMESPACE::atan2f(max_denormal, min_denormal));
EXPECT_FP_EQ(0x1.921fb6p-1f,
LIBC_NAMESPACE::atan2f(max_denormal, max_denormal));
}
TEST_F(LlvmLibcAtan2fTest, DAZMode) {
ModifyMXCSR mxcsr(DAZ);
EXPECT_FP_EQ(0.0f, LIBC_NAMESPACE::atan2f(min_denormal, min_denormal));
EXPECT_FP_EQ(0.0f, LIBC_NAMESPACE::atan2f(min_denormal, max_denormal));
EXPECT_FP_EQ(0.0f, LIBC_NAMESPACE::atan2f(max_denormal, min_denormal));
EXPECT_FP_EQ(0.0f, LIBC_NAMESPACE::atan2f(max_denormal, max_denormal));
}
TEST_F(LlvmLibcAtan2fTest, FTZDAZMode) {
ModifyMXCSR mxcsr(FTZ | DAZ);
EXPECT_FP_EQ(0.0f, LIBC_NAMESPACE::atan2f(min_denormal, min_denormal));
EXPECT_FP_EQ(0.0f, LIBC_NAMESPACE::atan2f(min_denormal, max_denormal));
EXPECT_FP_EQ(0.0f, LIBC_NAMESPACE::atan2f(max_denormal, min_denormal));
EXPECT_FP_EQ(0.0f, LIBC_NAMESPACE::atan2f(max_denormal, max_denormal));
}
#endif