[libc-commits] [libc] [libc][math] Qualify log with constant evaluation support (PR #184745)
via libc-commits
libc-commits at lists.llvm.org
Wed Mar 4 22:34:11 PST 2026
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-libc
Author: Muhammad Bassiouni (bassiounix)
<details>
<summary>Changes</summary>
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.
I started with `log` as `constexpr log` variant is needed in `<wctype.h>` conversion functions inside the perfect hash table.
---
Full diff: https://github.com/llvm/llvm-project/pull/184745.diff
7 Files Affected:
- (modified) libc/src/__support/CPP/type_traits/is_constant_evaluated.h (+4)
- (modified) libc/src/__support/FPUtil/multiply_add.h (+2-2)
- (modified) libc/src/__support/macros/attributes.h (+14)
- (modified) libc/src/__support/macros/config.h (+10)
- (modified) libc/src/__support/math/log.h (+5-5)
- (modified) libc/src/__support/math/log_range_reduction.h (+2-2)
- (modified) libc/test/shared/shared_math_test.cpp (+16-1)
``````````diff
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));
``````````
</details>
https://github.com/llvm/llvm-project/pull/184745
More information about the libc-commits
mailing list