[libc-commits] [libc] [libc][math] Qualify log with constant evaluation support (PR #184745)
Muhammad Bassiouni via libc-commits
libc-commits at lists.llvm.org
Fri Mar 20 02:58:43 PDT 2026
https://github.com/bassiounix updated https://github.com/llvm/llvm-project/pull/184745
>From 6c30f8301415d500a065e83a5ee503c3f88afe5c Mon Sep 17 00:00:00 2001
From: bassiounix <muhammad.m.bassiouni at gmail.com>
Date: Thu, 5 Mar 2026 07:48:03 +0200
Subject: [PATCH 1/5] [libc][math] Qualify log with constant evaluation support
---
.../CPP/type_traits/is_constant_evaluated.h | 4 ++++
libc/src/__support/FPUtil/multiply_add.h | 4 ++--
libc/src/__support/macros/attributes.h | 14 ++++++++++++++
libc/src/__support/macros/config.h | 10 ++++++++++
libc/src/__support/math/log.h | 10 +++++-----
libc/src/__support/math/log_range_reduction.h | 4 ++--
libc/test/shared/shared_math_test.cpp | 17 ++++++++++++++++-
7 files changed, 53 insertions(+), 10 deletions(-)
diff --git a/libc/src/__support/CPP/type_traits/is_constant_evaluated.h b/libc/src/__support/CPP/type_traits/is_constant_evaluated.h
index 0bb2d0806cb0d..65adbc1ae0ed9 100644
--- a/libc/src/__support/CPP/type_traits/is_constant_evaluated.h
+++ b/libc/src/__support/CPP/type_traits/is_constant_evaluated.h
@@ -15,7 +15,11 @@ namespace LIBC_NAMESPACE_DECL {
namespace cpp {
LIBC_INLINE constexpr bool is_constant_evaluated() {
+#if __has_builtin(__builtin_is_constant_evaluated)
return __builtin_is_constant_evaluated();
+#else
+ return false;
+#endif
}
} // namespace cpp
diff --git a/libc/src/__support/FPUtil/multiply_add.h b/libc/src/__support/FPUtil/multiply_add.h
index 1469326c9ba3e..f6c6ee0754ea8 100644
--- a/libc/src/__support/FPUtil/multiply_add.h
+++ b/libc/src/__support/FPUtil/multiply_add.h
@@ -29,7 +29,7 @@ multiply_add(const T &x, const T &y, const T &z) {
}
template <typename T>
-LIBC_INLINE static constexpr cpp::enable_if_t<(sizeof(T) <= sizeof(void *)), T>
+LIBC_INLINE constexpr cpp::enable_if_t<(sizeof(T) <= sizeof(void *)), T>
multiply_add(T x, T y, T z) {
return x * y + z;
}
@@ -37,7 +37,7 @@ multiply_add(T x, T y, T z) {
} // namespace fputil
} // namespace LIBC_NAMESPACE_DECL
-#if defined(LIBC_TARGET_CPU_HAS_FMA)
+#if defined(LIBC_TARGET_CPU_HAS_FMA) && !defined(LIBC_HAS_CONSTANT_EVALUATION)
// FMA instructions are available.
// We use builtins directly instead of including FMA.h to avoid a circular
diff --git a/libc/src/__support/macros/attributes.h b/libc/src/__support/macros/attributes.h
index d5ff028634940..11591c20bd76e 100644
--- a/libc/src/__support/macros/attributes.h
+++ b/libc/src/__support/macros/attributes.h
@@ -29,6 +29,20 @@
#define LIBC_INLINE_ASM __asm__ __volatile__
#define LIBC_UNUSED __attribute__((unused))
+// The reason for indirection is GCC is known to fail with constexpr qualified
+// functions that doesn't produce constant expression. This avoids it by using
+// LIBC_ENABLE_CONSTEXPR as a flag to control whether the function should be
+// constexpr qualified or not.
+#if LIBC_ENABLE_CONSTEXPR && \
+ (__has_builtin(__builtin_is_constant_evaluated) || \
+ (defined(LIBC_COMPILER_IS_GCC) && (LIBC_COMPILER_GCC_VER >= 900)) || \
+ (defined(LIBC_COMPILER_IS_CLANG) && LIBC_COMPILER_CLANG_VER >= 1100))
+#define LIBC_HAS_CONSTANT_EVALUATION
+#define LIBC_CONSTEXPR constexpr
+#else
+#define LIBC_CONSTEXPR
+#endif
+
// Uses the platform specific specialization
#define LIBC_THREAD_MODE_PLATFORM 0
diff --git a/libc/src/__support/macros/config.h b/libc/src/__support/macros/config.h
index b06a890c9c13c..a876a39069b01 100644
--- a/libc/src/__support/macros/config.h
+++ b/libc/src/__support/macros/config.h
@@ -66,4 +66,14 @@
#define LIBC_NAMESPACE_DECL LIBC_NAMESPACE
#endif
+// IMPORTANT (USE WITH CAUTION): This macro is intended to be used at the top of
+// the file and set to 1. It alters the signatures of some functions to have
+// constexpr qualifier and forces the use of constexpr-compatible
+// implementation, which might be a completely different code path or
+// instructions. Some of these functions exploit platform-specific non-constexpr
+// implementations to achieve certain goals, thus it is disabled by default.
+#ifndef LIBC_ENABLE_CONSTEXPR
+#define LIBC_ENABLE_CONSTEXPR 0
+#endif
+
#endif // LLVM_LIBC_SRC___SUPPORT_MACROS_CONFIG_H
diff --git a/libc/src/__support/math/log.h b/libc/src/__support/math/log.h
index f7b5767899ccc..1584134c98ba2 100644
--- a/libc/src/__support/math/log.h
+++ b/libc/src/__support/math/log.h
@@ -721,8 +721,7 @@ LIBC_INLINE_VAR constexpr Float128 BIG_COEFFS[3]{
// Reuse the output of the fast pass range reduction.
// -2^-8 <= m_x < 2^-7
#ifndef LIBC_MATH_HAS_SKIP_ACCURATE_PASS
-LIBC_INLINE double log_accurate(int e_x, int index, double m_x) {
-
+LIBC_INLINE constexpr double log_accurate(int e_x, int index, double m_x) {
Float128 e_x_f128(static_cast<float>(e_x));
Float128 sum = fputil::quick_mul(LOG_2, e_x_f128);
sum = fputil::quick_add(sum, LOG_TABLE.step_1[index]);
@@ -744,7 +743,7 @@ LIBC_INLINE double log_accurate(int e_x, int index, double m_x) {
} // namespace log_internal
-LIBC_INLINE double log(double x) {
+LIBC_INLINE LIBC_CONSTEXPR double log(double x) {
using namespace log_internal;
using FPBits_t = typename fputil::FPBits<double>;
@@ -805,11 +804,12 @@ LIBC_INLINE double log(double x) {
uint64_t x_m = (x_u & 0x000F'FFFF'FFFF'FFFFULL) | 0x3FF0'0000'0000'0000ULL;
double m = FPBits_t(x_m).get_val();
- double u, u_sq;
+ double u{}, u_sq{};
fputil::DoubleDouble r1;
// Perform exact range reduction
-#ifdef LIBC_TARGET_CPU_HAS_FMA_DOUBLE
+#if defined(LIBC_TARGET_CPU_HAS_FMA_DOUBLE) && \
+ !defined(LIBC_HAS_CONSTANT_EVALUATION)
u = fputil::multiply_add(r, m, -1.0); // exact
#else
uint64_t c_m = x_m & 0x3FFF'E000'0000'0000ULL;
diff --git a/libc/src/__support/math/log_range_reduction.h b/libc/src/__support/math/log_range_reduction.h
index 49cce322fdddf..5823cc8013e4d 100644
--- a/libc/src/__support/math/log_range_reduction.h
+++ b/libc/src/__support/math/log_range_reduction.h
@@ -15,7 +15,6 @@
#include "src/__support/uint128.h"
namespace LIBC_NAMESPACE_DECL {
-
namespace math {
namespace log_range_reduction_internal {
@@ -36,7 +35,7 @@ struct LogRR {
// sum: adding -log(r1) - log(r2) - log(r3) - log(r4) to the resulted sum.
// return value: the reduced argument v satisfying:
// -0x1.0002143p-29 <= v < 0x1p-29, and ulp(v) >= 2^(-125).
-LIBC_INLINE fputil::DyadicFloat<128>
+LIBC_INLINE constexpr fputil::DyadicFloat<128>
log_range_reduction(double m_x, const LogRR &log_table,
fputil::DyadicFloat<128> &sum) {
using namespace common_constants_internal;
@@ -91,6 +90,7 @@ log_range_reduction(double m_x, const LogRR &log_table,
MType({static_cast<uint64_t>(vv4),
static_cast<uint64_t>(vv4 >> 64)}));
}
+
} // namespace log_range_reduction_internal
} // namespace math
} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/test/shared/shared_math_test.cpp b/libc/test/shared/shared_math_test.cpp
index 460449e4fcb2e..793c06c2a7c69 100644
--- a/libc/test/shared/shared_math_test.cpp
+++ b/libc/test/shared/shared_math_test.cpp
@@ -6,6 +6,22 @@
//
//===----------------------------------------------------------------------===//
+#define LIBC_ENABLE_CONSTEXPR 1
+
+#include "shared/math/log.h"
+
+#ifdef LIBC_HAS_CONSTANT_EVALUATION
+
+//===-- Double Tests ------------------------------------------------------===//
+
+static_assert(0x0p+0 == LIBC_NAMESPACE::shared::log(1.0));
+
+//===----------------------------------------------------------------------===//
+
+#endif // LIBC_HAS_CONSTANT_EVALUATION
+
+#undef LIBC_ENABLE_CONSTEXPR
+
#include "shared/math.h"
#include "test/UnitTest/FPMatcher.h"
#include "test/UnitTest/Test.h"
@@ -246,7 +262,6 @@ TEST(LlvmLibcSharedMathTest, AllDouble) {
EXPECT_FP_EQ(0x0p+0, LIBC_NAMESPACE::shared::expm1(0.0));
EXPECT_FP_EQ(0x0p+0f, LIBC_NAMESPACE::shared::ffma(0.0, 0.0, 0.0));
EXPECT_FP_EQ(0x0p+0, LIBC_NAMESPACE::shared::fsqrt(0.0));
- EXPECT_FP_EQ(0x0p+0, LIBC_NAMESPACE::shared::log(1.0));
EXPECT_FP_EQ(0x0p+0, LIBC_NAMESPACE::shared::log10(1.0));
EXPECT_FP_EQ(0x0p+0, LIBC_NAMESPACE::shared::log1p(0.0));
EXPECT_FP_EQ(0x0p+0, LIBC_NAMESPACE::shared::log2(1.0));
>From cbc16ab19f52df7910b511998e7b0ef166483e97 Mon Sep 17 00:00:00 2001
From: bassiounix <muhammad.m.bassiouni at gmail.com>
Date: Fri, 6 Mar 2026 01:53:22 +0200
Subject: [PATCH 2/5] remove unnecessary check
---
libc/src/__support/math/log.h | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/libc/src/__support/math/log.h b/libc/src/__support/math/log.h
index 1584134c98ba2..5cbd771cb86c6 100644
--- a/libc/src/__support/math/log.h
+++ b/libc/src/__support/math/log.h
@@ -808,8 +808,7 @@ LIBC_INLINE LIBC_CONSTEXPR double log(double x) {
fputil::DoubleDouble r1;
// Perform exact range reduction
-#if defined(LIBC_TARGET_CPU_HAS_FMA_DOUBLE) && \
- !defined(LIBC_HAS_CONSTANT_EVALUATION)
+#if defined(LIBC_TARGET_CPU_HAS_FMA_DOUBLE)
u = fputil::multiply_add(r, m, -1.0); // exact
#else
uint64_t c_m = x_m & 0x3FFF'E000'0000'0000ULL;
>From bd4cb407ec41a6b5b7a4b0a017b8c7aa67403c60 Mon Sep 17 00:00:00 2001
From: bassiounix <muhammad.m.bassiouni at gmail.com>
Date: Fri, 6 Mar 2026 01:55:57 +0200
Subject: [PATCH 3/5] revert to ifdef
---
libc/src/__support/math/log.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/libc/src/__support/math/log.h b/libc/src/__support/math/log.h
index 5cbd771cb86c6..62bdf4e25431e 100644
--- a/libc/src/__support/math/log.h
+++ b/libc/src/__support/math/log.h
@@ -808,7 +808,7 @@ LIBC_INLINE LIBC_CONSTEXPR double log(double x) {
fputil::DoubleDouble r1;
// Perform exact range reduction
-#if defined(LIBC_TARGET_CPU_HAS_FMA_DOUBLE)
+#ifdef LIBC_TARGET_CPU_HAS_FMA_DOUBLE
u = fputil::multiply_add(r, m, -1.0); // exact
#else
uint64_t c_m = x_m & 0x3FFF'E000'0000'0000ULL;
>From 1fe8493fe194936aec19193c4e04284e812ffd96 Mon Sep 17 00:00:00 2001
From: bassiounix <muhammad.m.bassiouni at gmail.com>
Date: Fri, 13 Mar 2026 20:33:53 +0200
Subject: [PATCH 4/5] add todo
---
libc/src/__support/macros/attributes.h | 1 +
1 file changed, 1 insertion(+)
diff --git a/libc/src/__support/macros/attributes.h b/libc/src/__support/macros/attributes.h
index 11591c20bd76e..6908c365e4776 100644
--- a/libc/src/__support/macros/attributes.h
+++ b/libc/src/__support/macros/attributes.h
@@ -29,6 +29,7 @@
#define LIBC_INLINE_ASM __asm__ __volatile__
#define LIBC_UNUSED __attribute__((unused))
+// TODO: Remove the macro once Clang/LLVM bump their minimum compilers' version.
// The reason for indirection is GCC is known to fail with constexpr qualified
// functions that doesn't produce constant expression. This avoids it by using
// LIBC_ENABLE_CONSTEXPR as a flag to control whether the function should be
>From 2b74b14fff6383bc8aa3e1166277e084b3181c2c Mon Sep 17 00:00:00 2001
From: bassiounix <muhammad.m.bassiouni at gmail.com>
Date: Fri, 20 Mar 2026 11:58:13 +0200
Subject: [PATCH 5/5] misc
---
libc/src/__support/FPUtil/PolyEval.h | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/libc/src/__support/FPUtil/PolyEval.h b/libc/src/__support/FPUtil/PolyEval.h
index 7bec4e30a9960..3ba37f1df5823 100644
--- a/libc/src/__support/FPUtil/PolyEval.h
+++ b/libc/src/__support/FPUtil/PolyEval.h
@@ -25,14 +25,14 @@ namespace LIBC_NAMESPACE_DECL {
namespace fputil {
template <typename T>
-LIBC_INLINE cpp::enable_if_t<(sizeof(T) > sizeof(void *)), T>
+LIBC_INLINE constexpr cpp::enable_if_t<(sizeof(T) > sizeof(void *)), T>
polyeval(const T &, const T &a0) {
return a0;
}
template <typename T>
-LIBC_INLINE cpp::enable_if_t<(sizeof(T) <= sizeof(void *)), T> polyeval(T,
- T a0) {
+LIBC_INLINE constexpr cpp::enable_if_t<(sizeof(T) <= sizeof(void *)), T>
+polyeval(T, T a0) {
return a0;
}
@@ -43,7 +43,7 @@ polyeval(const T &x, const T &a0, const Ts &...a) {
}
template <typename T, typename... Ts>
-LIBC_INLINE cpp::enable_if_t<(sizeof(T) <= sizeof(void *)), T>
+LIBC_INLINE LIBC_CONSTEXPR cpp::enable_if_t<(sizeof(T) <= sizeof(void *)), T>
polyeval(T x, T a0, Ts... a) {
return multiply_add(x, polyeval(x, a...), a0);
}
More information about the libc-commits
mailing list