Skip to content

Commit 952dafb

Browse files
authored
[libc][math] Add test and fix atan2f crashing when flush-denorm-to-zero (FTZ) and denorm-as-zero (DAZ) modes are set. (#112828)
1 parent caa9e41 commit 952dafb

File tree

3 files changed

+72
-5
lines changed

3 files changed

+72
-5
lines changed

libc/src/math/generic/atan2f.cpp

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -246,12 +246,18 @@ LLVM_LIBC_FUNCTION(float, atan2f, (float y, float x)) {
246246
uint32_t y_abs = y_bits.uintval();
247247
uint32_t max_abs = x_abs > y_abs ? x_abs : y_abs;
248248
uint32_t min_abs = x_abs <= y_abs ? x_abs : y_abs;
249+
float num_f = FPBits(min_abs).get_val();
250+
float den_f = FPBits(max_abs).get_val();
251+
double num_d = static_cast<double>(num_f);
252+
double den_d = static_cast<double>(den_f);
249253

250-
if (LIBC_UNLIKELY(max_abs >= 0x7f80'0000U || min_abs == 0U)) {
254+
if (LIBC_UNLIKELY(max_abs >= 0x7f80'0000U || num_d == 0.0)) {
251255
if (x_bits.is_nan() || y_bits.is_nan())
252256
return FPBits::quiet_nan().get_val();
253-
size_t x_except = x_abs == 0 ? 0 : (x_abs == 0x7f80'0000 ? 2 : 1);
254-
size_t y_except = y_abs == 0 ? 0 : (y_abs == 0x7f80'0000 ? 2 : 1);
257+
double x_d = static_cast<double>(x);
258+
double y_d = static_cast<double>(y);
259+
size_t x_except = (x_d == 0.0) ? 0 : (x_abs == 0x7f80'0000 ? 2 : 1);
260+
size_t y_except = (y_d == 0.0) ? 0 : (y_abs == 0x7f80'0000 ? 2 : 1);
255261

256262
// Exceptional cases:
257263
// EXCEPT[y_except][x_except][x_is_neg]
@@ -275,8 +281,6 @@ LLVM_LIBC_FUNCTION(float, atan2f, (float y, float x)) {
275281
bool recip = x_abs < y_abs;
276282
double final_sign = IS_NEG[(x_sign != y_sign) != recip];
277283
fputil::DoubleDouble const_term = CONST_ADJ[x_sign][y_sign][recip];
278-
double num_d = static_cast<double>(FPBits(min_abs).get_val());
279-
double den_d = static_cast<double>(FPBits(max_abs).get_val());
280284
double q_d = num_d / den_d;
281285

282286
double k_d = fputil::nearest_integer(q_d * 0x1.0p4f);

libc/test/UnitTest/FPMatcher.h

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include "src/__support/FPUtil/FPBits.h"
1717
#include "src/__support/FPUtil/fpbits_str.h"
1818
#include "src/__support/macros/config.h"
19+
#include "src/__support/macros/properties/architectures.h"
1920
#include "test/UnitTest/RoundingModeUtils.h"
2021
#include "test/UnitTest/StringUtils.h"
2122
#include "test/UnitTest/Test.h"
@@ -192,6 +193,31 @@ template <typename T> struct FPTest : public Test {
192193
};
193194
};
194195

196+
// Add facility to test Flush-Denormal-To-Zero (FTZ) and Denormal-As-Zero (DAZ)
197+
// modes.
198+
// These tests to ensure that our implementations will not crash under these
199+
// modes.
200+
#if defined(LIBC_TARGET_ARCH_IS_X86_64) && __has_builtin(__builtin_ia32_stmxcsr)
201+
202+
#define LIBC_TEST_FTZ_DAZ
203+
204+
static constexpr unsigned FTZ = 0x8000; // Flush denormal to zero
205+
static constexpr unsigned DAZ = 0x0040; // Denormal as zero
206+
207+
struct ModifyMXCSR {
208+
ModifyMXCSR(unsigned flags) {
209+
old_mxcsr = __builtin_ia32_stmxcsr();
210+
__builtin_ia32_ldmxcsr(old_mxcsr | flags);
211+
}
212+
213+
~ModifyMXCSR() { __builtin_ia32_ldmxcsr(old_mxcsr); }
214+
215+
private:
216+
unsigned old_mxcsr;
217+
};
218+
219+
#endif
220+
195221
} // namespace testing
196222
} // namespace LIBC_NAMESPACE_DECL
197223

libc/test/src/math/smoke/atan2f_test.cpp

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,3 +58,40 @@ TEST_F(LlvmLibcAtan2fTest, SpecialNumbers) {
5858
// EXPECT_FP_EXCEPTION(0);
5959
EXPECT_MATH_ERRNO(0);
6060
}
61+
62+
#ifdef LIBC_TEST_FTZ_DAZ
63+
64+
using namespace LIBC_NAMESPACE::testing;
65+
66+
TEST_F(LlvmLibcAtan2fTest, FTZMode) {
67+
ModifyMXCSR mxcsr(FTZ);
68+
69+
EXPECT_FP_EQ(0x1.921fb6p-1f,
70+
LIBC_NAMESPACE::atan2f(min_denormal, min_denormal));
71+
EXPECT_FP_EQ(0x1.000002p-23f,
72+
LIBC_NAMESPACE::atan2f(min_denormal, max_denormal));
73+
EXPECT_FP_EQ(0x1.921fb4p0f,
74+
LIBC_NAMESPACE::atan2f(max_denormal, min_denormal));
75+
EXPECT_FP_EQ(0x1.921fb6p-1f,
76+
LIBC_NAMESPACE::atan2f(max_denormal, max_denormal));
77+
}
78+
79+
TEST_F(LlvmLibcAtan2fTest, DAZMode) {
80+
ModifyMXCSR mxcsr(DAZ);
81+
82+
EXPECT_FP_EQ(0.0f, LIBC_NAMESPACE::atan2f(min_denormal, min_denormal));
83+
EXPECT_FP_EQ(0.0f, LIBC_NAMESPACE::atan2f(min_denormal, max_denormal));
84+
EXPECT_FP_EQ(0.0f, LIBC_NAMESPACE::atan2f(max_denormal, min_denormal));
85+
EXPECT_FP_EQ(0.0f, LIBC_NAMESPACE::atan2f(max_denormal, max_denormal));
86+
}
87+
88+
TEST_F(LlvmLibcAtan2fTest, FTZDAZMode) {
89+
ModifyMXCSR mxcsr(FTZ | DAZ);
90+
91+
EXPECT_FP_EQ(0.0f, LIBC_NAMESPACE::atan2f(min_denormal, min_denormal));
92+
EXPECT_FP_EQ(0.0f, LIBC_NAMESPACE::atan2f(min_denormal, max_denormal));
93+
EXPECT_FP_EQ(0.0f, LIBC_NAMESPACE::atan2f(max_denormal, min_denormal));
94+
EXPECT_FP_EQ(0.0f, LIBC_NAMESPACE::atan2f(max_denormal, max_denormal));
95+
}
96+
97+
#endif

0 commit comments

Comments
 (0)