[libc-commits] [libc] eea589f - [libc][math] Qualify log with constant evaluation support (#184745)

via libc-commits libc-commits at lists.llvm.org
Sat Mar 21 14:55:11 PDT 2026


Author: Muhammad Bassiouni
Date: 2026-03-21T23:55:05+02:00
New Revision: eea589f951e188be57860459f45763985bd2233e

URL: https://github.com/llvm/llvm-project/commit/eea589f951e188be57860459f45763985bd2233e
DIFF: https://github.com/llvm/llvm-project/commit/eea589f951e188be57860459f45763985bd2233e.diff

LOG: [libc][math] Qualify log with constant evaluation support (#184745)

Lay the ground for C++26 `constexpr` math functions:
- Introduce `LIBC_ENABLE_CONSTEXPR` macro switch to specify the desire
of `constexpr`-only code route.
- Introduce `LIBC_HAS_CONSTANT_EVALUATION` to indicate that we are using
`constexpr`-only code in all dependent functions.
- Introduce `LIBC_CONSTEXPR` macro qualifier to aid in altering the
signature of non-`constexpr` functions.

Note that non-`constexpr` qualified functions are caused by the
exploitation of non-`constexpr` compatible utils, resulting in
non-qualified dependent function, but it can be modified to be qualified
using other code routes.

If the function is `constexpr` compatible, then it's prohibited to use
`LIBC_CONSTEXPR` as a function qualifier. We only qualify it with
`constexpr` as usual.

`LIBC_CONSTEXPR` may or may not evaluate to `constexpr` depending on the
environment configurations, thus it's only used to modify the function
signature in constant evaluation context and remove the qualifier if
it's not desired (depending on provided configurations).

Possible side effects:
- Current qualified routes may or may not produce the desired ULP, this
is implementation dependent (function by function basis) and needs
further testing of the chosen code route.
- The shared tests in the current configuration can still compile with
unsupported compiler. I didn't want to raise compilation error with
unsupported compilers now, but we need to push compiler support with
newer versions for this one to work as intended.

Added: 
    

Modified: 
    libc/src/__support/CPP/type_traits/is_constant_evaluated.h
    libc/src/__support/FPUtil/PolyEval.h
    libc/src/__support/FPUtil/multiply_add.h
    libc/src/__support/macros/attributes.h
    libc/src/__support/macros/config.h
    libc/src/__support/math/log.h
    libc/src/__support/math/log_range_reduction.h
    libc/test/shared/shared_math_test.cpp

Removed: 
    


################################################################################
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/PolyEval.h b/libc/src/__support/FPUtil/PolyEval.h
index 4c38cabbb1737..58a3f4e24fdeb 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);
 }

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..6908c365e4776 100644
--- a/libc/src/__support/macros/attributes.h
+++ b/libc/src/__support/macros/attributes.h
@@ -29,6 +29,21 @@
 #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
+// 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 
diff erent 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..62bdf4e25431e 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,7 +804,7 @@ 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

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 341b35a7b7e0f..45b77df069658 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"
@@ -254,7 +270,6 @@ TEST(LlvmLibcSharedMathTest, AllDouble) {
   EXPECT_FP_EQ(0x0p+0f, LIBC_NAMESPACE::shared::ffma(0.0, 0.0, 0.0));
   EXPECT_FP_EQ(0.0, LIBC_NAMESPACE::shared::hypot(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));


        


More information about the libc-commits mailing list