[libc-commits] [libc] [libc] Keep the compiler from folding the inputs of floating point tests (PR #236613)
via libc-commits
libc-commits at lists.llvm.org
Wed Oct 28 18:16:43 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-libc
Author: Mike Lothian (FireBurn)
<details>
<summary>Changes</summary>
With LTO the function under test is inlined into the test, and a call on a constant is then evaluated at compile time. That skips what the test is there to check: the exception flag for a signaling NaN, the rounding mode that gives an exact fma cancellation its sign, and the flush and denormals-are-zero modes set through MXCSR. coshf, cbrtf, log2f, exp2f, fma and fmaf failed this way when built with -flto=thin
Pass those inputs through an empty asm, so they are run time values. sNaN, min_denormal and max_denormal in FPTest become members rather than static constexpr, as the same names in DECLARE_SPECIAL_CONSTANTS already are. Nothing used them as constants
It might be worth an occasional LTO build among the bots, since these only show up there
AI disclosure: this patch was written with Claude Code. I reviewed the change and built the libc tests with -O3 -march=native -flto=thin; the six tests above failed before and pass after, and the rest of check-libc still passes in that configuration
Assisted-by: Claude
---
Full diff: https://github.com/llvm/llvm-project/pull/236613.diff
2 Files Affected:
- (modified) libc/test/UnitTest/FPMatcher.h (+19-6)
- (modified) libc/test/src/math/smoke/FmaTest.h (+17-11)
``````````diff
diff --git a/libc/test/UnitTest/FPMatcher.h b/libc/test/UnitTest/FPMatcher.h
index 314d8f4f1ef1e8..ab3d24ab551df4 100644
--- a/libc/test/UnitTest/FPMatcher.h
+++ b/libc/test/UnitTest/FPMatcher.h
@@ -175,6 +175,16 @@ CFPMatcher<T, C> getMatcherComplex(T expectedValue) {
return CFPMatcher<T, C>(expectedValue);
}
+// Returns its argument as a value the compiler cannot see through. A call on a
+// constant that is inlined, as it is under LTO, is otherwise folded at compile
+// time, which skips the exception flags, the rounding mode and the flush or
+// denormals-are-zero mode that tests of floating point functions are there to
+// check.
+template <typename T> LIBC_INLINE T opaque(T value) {
+ __asm__ volatile("" : "+m"(value));
+ return value;
+}
+
template <typename T> struct FPTest : public ErrnoCheckingTest {
using FPBits = LIBC_NAMESPACE::fputil::FPBits<T>;
using StorageType = typename FPBits::StorageType;
@@ -184,14 +194,14 @@ template <typename T> struct FPTest : public ErrnoCheckingTest {
static constexpr T neg_zero = FPBits::zero(Sign::NEG).get_val();
static constexpr T aNaN = FPBits::quiet_nan(Sign::POS).get_val();
static constexpr T neg_aNaN = FPBits::quiet_nan(Sign::NEG).get_val();
- static constexpr T sNaN = FPBits::signaling_nan().get_val();
+ const T sNaN = opaque(FPBits::signaling_nan().get_val());
static constexpr T inf = FPBits::inf(Sign::POS).get_val();
static constexpr T neg_inf = FPBits::inf(Sign::NEG).get_val();
static constexpr T min_normal = FPBits::min_normal().get_val();
static constexpr T max_normal = FPBits::max_normal(Sign::POS).get_val();
static constexpr T neg_max_normal = FPBits::max_normal(Sign::NEG).get_val();
- static constexpr T min_denormal = FPBits::min_subnormal().get_val();
- static constexpr T max_denormal = FPBits::max_subnormal().get_val();
+ const T min_denormal = opaque(FPBits::min_subnormal().get_val());
+ const T max_denormal = opaque(FPBits::max_subnormal().get_val());
#ifdef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
static constexpr int N_ROUNDING_MODES = 1;
@@ -254,16 +264,19 @@ struct ModifyMXCSR {
const T neg_zero = FPBits::zero(Sign::NEG).get_val(); \
const T aNaN = FPBits::quiet_nan(Sign::POS).get_val(); \
const T neg_aNaN = FPBits::quiet_nan(Sign::NEG).get_val(); \
- const T sNaN = FPBits::signaling_nan(Sign::POS).get_val(); \
+ const T sNaN = LIBC_NAMESPACE::testing::opaque( \
+ FPBits::signaling_nan(Sign::POS).get_val()); \
const T neg_sNaN = FPBits::signaling_nan(Sign::NEG).get_val(); \
const T inf = FPBits::inf(Sign::POS).get_val(); \
const T neg_inf = FPBits::inf(Sign::NEG).get_val(); \
const T min_normal = FPBits::min_normal().get_val(); \
const T max_normal = FPBits::max_normal(Sign::POS).get_val(); \
const T neg_max_normal = FPBits::max_normal(Sign::NEG).get_val(); \
- const T min_denormal = FPBits::min_subnormal(Sign::POS).get_val(); \
+ const T min_denormal = LIBC_NAMESPACE::testing::opaque( \
+ FPBits::min_subnormal(Sign::POS).get_val()); \
const T neg_min_denormal = FPBits::min_subnormal(Sign::NEG).get_val(); \
- const T max_denormal = FPBits::max_subnormal().get_val(); \
+ const T max_denormal = \
+ LIBC_NAMESPACE::testing::opaque(FPBits::max_subnormal().get_val()); \
static constexpr int UNKNOWN_MATH_ROUNDING_DIRECTION = 99; \
static constexpr LIBC_NAMESPACE::cpp::array<int, 6> \
MATH_ROUNDING_DIRECTIONS_INCLUDING_UNKNOWN = { \
diff --git a/libc/test/src/math/smoke/FmaTest.h b/libc/test/src/math/smoke/FmaTest.h
index dfd8b7ffb3fce8..f34cc9184f7875 100644
--- a/libc/test/src/math/smoke/FmaTest.h
+++ b/libc/test/src/math/smoke/FmaTest.h
@@ -90,24 +90,30 @@ class FmaTestTemplate : public LIBC_NAMESPACE::testing::FEnvSafeTest {
EXPECT_FP_EQ_ALL_ROUNDING(func(InType(-0.25), in_z, in_z),
func(InType(1.75), in_z, -in_z));
- // Exact cancellation.
+ // Exact cancellation. The rounding mode decides the sign of the zero, so
+ // the inputs must not be known to the compiler.
+ auto exact = [&](InType x, InType y, InType z) {
+ return func(LIBC_NAMESPACE::testing::opaque(x),
+ LIBC_NAMESPACE::testing::opaque(y),
+ LIBC_NAMESPACE::testing::opaque(z));
+ };
EXPECT_FP_EQ_ROUNDING_NEAREST(
- out.zero, func(InType(3.0), InType(5.0), InType(-15.0)));
- EXPECT_FP_EQ_ROUNDING_UPWARD(out.zero,
- func(InType(3.0), InType(5.0), InType(-15.0)));
+ out.zero, exact(InType(3.0), InType(5.0), InType(-15.0)));
+ EXPECT_FP_EQ_ROUNDING_UPWARD(
+ out.zero, exact(InType(3.0), InType(5.0), InType(-15.0)));
EXPECT_FP_EQ_ROUNDING_TOWARD_ZERO(
- out.zero, func(InType(3.0), InType(5.0), InType(-15.0)));
+ out.zero, exact(InType(3.0), InType(5.0), InType(-15.0)));
EXPECT_FP_EQ_ROUNDING_DOWNWARD(
- out.neg_zero, func(InType(3.0), InType(5.0), InType(-15.0)));
+ out.neg_zero, exact(InType(3.0), InType(5.0), InType(-15.0)));
EXPECT_FP_EQ_ROUNDING_NEAREST(
- out.zero, func(InType(-3.0), InType(5.0), InType(15.0)));
- EXPECT_FP_EQ_ROUNDING_UPWARD(out.zero,
- func(InType(-3.0), InType(5.0), InType(15.0)));
+ out.zero, exact(InType(-3.0), InType(5.0), InType(15.0)));
+ EXPECT_FP_EQ_ROUNDING_UPWARD(
+ out.zero, exact(InType(-3.0), InType(5.0), InType(15.0)));
EXPECT_FP_EQ_ROUNDING_TOWARD_ZERO(
- out.zero, func(InType(-3.0), InType(5.0), InType(15.0)));
+ out.zero, exact(InType(-3.0), InType(5.0), InType(15.0)));
EXPECT_FP_EQ_ROUNDING_DOWNWARD(
- out.neg_zero, func(InType(-3.0), InType(5.0), InType(15.0)));
+ out.neg_zero, exact(InType(-3.0), InType(5.0), InType(15.0)));
}
};
``````````
</details>
https://github.com/llvm/llvm-project/pull/236613
More information about the libc-commits
mailing list