Skip to content

Commit eed7ec5

Browse files
committed
[libc][math] Add test and fix atan2f crashing when flush-denorm-to-zero (FTZ)
and denorm-as-zero (DAZ) modes are set.
1 parent d989c24 commit eed7ec5

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
@@ -15,6 +15,7 @@
1515
#include "src/__support/FPUtil/FPBits.h"
1616
#include "src/__support/FPUtil/fpbits_str.h"
1717
#include "src/__support/macros/config.h"
18+
#include "src/__support/macros/properties/architectures.h"
1819
#include "test/UnitTest/RoundingModeUtils.h"
1920
#include "test/UnitTest/StringUtils.h"
2021
#include "test/UnitTest/Test.h"
@@ -175,6 +176,31 @@ template <typename T> struct FPTest : public Test {
175176
};
176177
};
177178

179+
// Add facility to test Flush-Denormal-To-Zero (FTZ) and Denormal-As-Zero (DAZ)
180+
// modes.
181+
// These tests to ensure that our implementations will not crash under these
182+
// modes.
183+
#if defined(LIBC_TARGET_ARCH_IS_X86_64) && __has_builtin(__builtin_ia32_stmxcsr)
184+
185+
#define LIBC_TEST_FTZ_DAZ
186+
187+
static constexpr unsigned FTZ = 0x8000; // Flush denormal to zero
188+
static constexpr unsigned DAZ = 0x0040; // Denormal as zero
189+
190+
struct ModifyMXCSR {
191+
ModifyMXCSR(unsigned flags) {
192+
old_mxcsr = __builtin_ia32_stmxcsr();
193+
__builtin_ia32_ldmxcsr(old_mxcsr | flags);
194+
}
195+
196+
~ModifyMXCSR() { __builtin_ia32_ldmxcsr(old_mxcsr); }
197+
198+
private:
199+
unsigned old_mxcsr;
200+
};
201+
202+
#endif
203+
178204
} // namespace testing
179205
} // namespace LIBC_NAMESPACE_DECL
180206

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)