[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