[libc][math] Qualify ceil functions to constexpr (#184948)

This commit is contained in:
Muhammad Bassiouni 2026-03-24 00:25:42 +02:00 committed by GitHub
parent 4f32ea35f5
commit b4f50cfd4a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 143 additions and 79 deletions

View File

@ -27,8 +27,9 @@ namespace cpp {
#endif
template <unsigned N>
LIBC_INLINE static void inline_copy(const char *from, char *to) {
#if __has_builtin(__builtin_memcpy_inline)
LIBC_INLINE LIBC_CONSTEXPR void inline_copy(const char *from, char *to) {
#if __has_builtin(__builtin_memcpy_inline) && \
!defined(LIBC_HAS_CONSTANT_EVALUATION)
__builtin_memcpy_inline(to, from, N);
#else
for (unsigned i = 0; i < N; ++i)
@ -46,7 +47,8 @@ LIBC_INLINE static constexpr cpp::enable_if_t<
cpp::is_trivially_copyable<From>::value,
To>
bit_cast(const From &from) {
#if __has_builtin(__builtin_bit_cast) || defined(LIBC_COMPILER_IS_MSVC)
#if __has_builtin(__builtin_bit_cast) || defined(LIBC_COMPILER_IS_MSVC) || \
defined(LIBC_HAS_CONSTANT_EVALUATION)
return __builtin_bit_cast(To, from);
#else
To to{};

View File

@ -22,7 +22,7 @@ namespace LIBC_NAMESPACE_DECL {
namespace fputil {
template <typename T, cpp::enable_if_t<cpp::is_floating_point_v<T>, int> = 0>
LIBC_INLINE T trunc(T x) {
LIBC_INLINE constexpr T trunc(T x) {
using StorageType = typename FPBits<T>::StorageType;
FPBits<T> bits(x);
@ -52,7 +52,7 @@ LIBC_INLINE T trunc(T x) {
}
template <typename T, cpp::enable_if_t<cpp::is_floating_point_v<T>, int> = 0>
LIBC_INLINE T ceil(T x) {
LIBC_INLINE constexpr T ceil(T x) {
using StorageType = typename FPBits<T>::StorageType;
FPBits<T> bits(x);
@ -95,7 +95,7 @@ LIBC_INLINE T ceil(T x) {
}
template <typename T, cpp::enable_if_t<cpp::is_floating_point_v<T>, int> = 0>
LIBC_INLINE T floor(T x) {
LIBC_INLINE constexpr T floor(T x) {
FPBits<T> bits(x);
if (bits.is_neg()) {
return -ceil(-x);
@ -105,7 +105,7 @@ LIBC_INLINE T floor(T x) {
}
template <typename T, cpp::enable_if_t<cpp::is_floating_point_v<T>, int> = 0>
LIBC_INLINE T round(T x) {
LIBC_INLINE constexpr T round(T x) {
using StorageType = typename FPBits<T>::StorageType;
FPBits<T> bits(x);
@ -244,7 +244,7 @@ round_using_specific_rounding_mode(T x, int rnd) {
}
template <typename T>
LIBC_INLINE cpp::enable_if_t<cpp::is_floating_point_v<T>, T>
LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_floating_point_v<T>, T>
round_using_current_rounding_mode(T x) {
int rounding_mode = quick_get_round();
@ -350,7 +350,7 @@ template <typename FloatType, typename IntType,
cpp::enable_if_t<cpp::is_floating_point_v<FloatType> &&
cpp::is_integral_v<IntType>,
int> = 0>
LIBC_INLINE IntType rounded_float_to_signed_integer(FloatType x) {
LIBC_INLINE constexpr IntType rounded_float_to_signed_integer(FloatType x) {
constexpr IntType INTEGER_MIN = (IntType(1) << (sizeof(IntType) * 8 - 1));
constexpr IntType INTEGER_MAX = -(INTEGER_MIN + 1);
FPBits<FloatType> bits(x);
@ -390,7 +390,7 @@ template <typename FloatType, typename IntType,
cpp::enable_if_t<cpp::is_floating_point_v<FloatType> &&
cpp::is_integral_v<IntType>,
int> = 0>
LIBC_INLINE IntType round_to_signed_integer(FloatType x) {
LIBC_INLINE constexpr IntType round_to_signed_integer(FloatType x) {
return internal::rounded_float_to_signed_integer<FloatType, IntType>(
round(x));
}
@ -399,7 +399,7 @@ template <typename FloatType, typename IntType,
cpp::enable_if_t<cpp::is_floating_point_v<FloatType> &&
cpp::is_integral_v<IntType>,
int> = 0>
LIBC_INLINE IntType
LIBC_INLINE constexpr IntType
round_to_signed_integer_using_current_rounding_mode(FloatType x) {
return internal::rounded_float_to_signed_integer<FloatType, IntType>(
round_using_current_rounding_mode(x));

View File

@ -68,27 +68,27 @@ struct BFloat16 {
return static_cast<T>(static_cast<float>(*this));
}
LIBC_INLINE bool operator==(BFloat16 other) const {
LIBC_INLINE constexpr bool operator==(BFloat16 other) const {
return fputil::equals(*this, other);
}
LIBC_INLINE bool operator!=(BFloat16 other) const {
LIBC_INLINE constexpr bool operator!=(BFloat16 other) const {
return !fputil::equals(*this, other);
}
LIBC_INLINE bool operator<(BFloat16 other) const {
LIBC_INLINE constexpr bool operator<(BFloat16 other) const {
return fputil::less_than(*this, other);
}
LIBC_INLINE bool operator<=(BFloat16 other) const {
LIBC_INLINE constexpr bool operator<=(BFloat16 other) const {
return fputil::less_than_or_equals(*this, other);
}
LIBC_INLINE bool operator>(BFloat16 other) const {
LIBC_INLINE constexpr bool operator>(BFloat16 other) const {
return fputil::greater_than(*this, other);
}
LIBC_INLINE bool operator>=(BFloat16 other) const {
LIBC_INLINE constexpr bool operator>=(BFloat16 other) const {
return fputil::greater_than_or_equals(*this, other);
}
@ -98,23 +98,23 @@ struct BFloat16 {
return result.get_val();
}
LIBC_INLINE BFloat16 operator+(BFloat16 other) const {
LIBC_INLINE constexpr BFloat16 operator+(BFloat16 other) const {
return fputil::generic::add<BFloat16>(*this, other);
}
LIBC_INLINE BFloat16 operator-(BFloat16 other) const {
LIBC_INLINE constexpr BFloat16 operator-(BFloat16 other) const {
return fputil::generic::sub<BFloat16>(*this, other);
}
LIBC_INLINE BFloat16 operator*(BFloat16 other) const {
LIBC_INLINE constexpr BFloat16 operator*(BFloat16 other) const {
return fputil::generic::mul<bfloat16>(*this, other);
}
LIBC_INLINE BFloat16 operator/(BFloat16 other) const {
LIBC_INLINE constexpr BFloat16 operator/(BFloat16 other) const {
return fputil::generic::div<bfloat16>(*this, other);
}
LIBC_INLINE BFloat16 &operator*=(const BFloat16 &other) {
LIBC_INLINE constexpr BFloat16 &operator*=(const BFloat16 &other) {
*this = *this * other;
return *this;
}

View File

@ -26,8 +26,8 @@ namespace fputil {
// (iii) -inf != +inf
// 3. Any comparison with NaN returns false
template <typename T>
LIBC_INLINE cpp::enable_if_t<cpp::is_floating_point_v<T>, bool> equals(T x,
T y) {
LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_floating_point_v<T>, bool>
equals(T x, T y) {
using FPBits = FPBits<T>;
FPBits x_bits(x);
FPBits y_bits(y);
@ -52,8 +52,8 @@ LIBC_INLINE cpp::enable_if_t<cpp::is_floating_point_v<T>, bool> equals(T x,
// 2. x < +inf (x != +inf)
// 3. Any comparison with NaN return false
template <typename T>
LIBC_INLINE cpp::enable_if_t<cpp::is_floating_point_v<T>, bool> less_than(T x,
T y) {
LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_floating_point_v<T>, bool>
less_than(T x, T y) {
using FPBits = FPBits<T>;
FPBits x_bits(x);
FPBits y_bits(y);
@ -87,7 +87,7 @@ LIBC_INLINE cpp::enable_if_t<cpp::is_floating_point_v<T>, bool> less_than(T x,
// Implements compareSignalingGreater predicate
// x < y => y > x
template <typename T>
LIBC_INLINE cpp::enable_if_t<cpp::is_floating_point_v<T>, bool>
LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_floating_point_v<T>, bool>
greater_than(T x, T y) {
return less_than(y, x);
}
@ -95,7 +95,7 @@ greater_than(T x, T y) {
// Implements compareSignalingLessEqual predicate
// x <= y => (x < y) || (x == y)
template <typename T>
LIBC_INLINE cpp::enable_if_t<cpp::is_floating_point_v<T>, bool>
LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_floating_point_v<T>, bool>
less_than_or_equals(T x, T y) {
return less_than(x, y) || equals(x, y);
}
@ -103,7 +103,7 @@ less_than_or_equals(T x, T y) {
// Implements compareSignalingGreaterEqual predicate
// x >= y => (x > y) || (x == y) => (y < x) || (x == y)
template <typename T>
LIBC_INLINE cpp::enable_if_t<cpp::is_floating_point_v<T>, bool>
LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_floating_point_v<T>, bool>
greater_than_or_equals(T x, T y) {
return less_than(y, x) || equals(x, y);
}

View File

@ -27,10 +27,10 @@ namespace LIBC_NAMESPACE_DECL {
namespace fputil::generic {
template <bool IsSub, typename OutType, typename InType>
LIBC_INLINE cpp::enable_if_t<cpp::is_floating_point_v<OutType> &&
cpp::is_floating_point_v<InType> &&
sizeof(OutType) <= sizeof(InType),
OutType>
LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_floating_point_v<OutType> &&
cpp::is_floating_point_v<InType> &&
sizeof(OutType) <= sizeof(InType),
OutType>
add_or_sub(InType x, InType y) {
using OutFPBits = FPBits<OutType>;
using OutStorageType = typename OutFPBits::StorageType;
@ -177,7 +177,7 @@ add_or_sub(InType x, InType y) {
InStorageType aligned_min_mant = static_cast<InStorageType>(
min_mant >> cpp::min(alignment, RESULT_MANTISSA_LEN));
bool aligned_min_mant_sticky;
bool aligned_min_mant_sticky{};
if (alignment <= GUARD_BITS_LEN)
aligned_min_mant_sticky = false;
@ -203,19 +203,19 @@ add_or_sub(InType x, InType y) {
}
template <typename OutType, typename InType>
LIBC_INLINE cpp::enable_if_t<cpp::is_floating_point_v<OutType> &&
cpp::is_floating_point_v<InType> &&
sizeof(OutType) <= sizeof(InType),
OutType>
LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_floating_point_v<OutType> &&
cpp::is_floating_point_v<InType> &&
sizeof(OutType) <= sizeof(InType),
OutType>
add(InType x, InType y) {
return add_or_sub</*IsSub=*/false, OutType>(x, y);
}
template <typename OutType, typename InType>
LIBC_INLINE cpp::enable_if_t<cpp::is_floating_point_v<OutType> &&
cpp::is_floating_point_v<InType> &&
sizeof(OutType) <= sizeof(InType),
OutType>
LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_floating_point_v<OutType> &&
cpp::is_floating_point_v<InType> &&
sizeof(OutType) <= sizeof(InType),
OutType>
sub(InType x, InType y) {
return add_or_sub</*IsSub=*/true, OutType>(x, y);
}

View File

@ -26,10 +26,10 @@ namespace LIBC_NAMESPACE_DECL {
namespace fputil::generic {
template <typename OutType, typename InType>
LIBC_INLINE cpp::enable_if_t<cpp::is_floating_point_v<OutType> &&
cpp::is_floating_point_v<InType> &&
sizeof(OutType) <= sizeof(InType),
OutType>
LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_floating_point_v<OutType> &&
cpp::is_floating_point_v<InType> &&
sizeof(OutType) <= sizeof(InType),
OutType>
div(InType x, InType y) {
using OutFPBits = FPBits<OutType>;
using OutStorageType = typename OutFPBits::StorageType;

View File

@ -25,10 +25,10 @@ namespace LIBC_NAMESPACE_DECL {
namespace fputil::generic {
template <typename OutType, typename InType>
LIBC_INLINE cpp::enable_if_t<cpp::is_floating_point_v<OutType> &&
cpp::is_floating_point_v<InType> &&
sizeof(OutType) <= sizeof(InType),
OutType>
LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_floating_point_v<OutType> &&
cpp::is_floating_point_v<InType> &&
sizeof(OutType) <= sizeof(InType),
OutType>
mul(InType x, InType y) {
using OutFPBits = FPBits<OutType>;
using OutStorageType = typename OutFPBits::StorageType;

View File

@ -80,7 +80,7 @@ LIBC_INLINE int quick_get_round() {
} // namespace generic
LIBC_INLINE static constexpr bool fenv_is_round_up() {
LIBC_INLINE constexpr bool fenv_is_round_up() {
if (cpp::is_constant_evaluated()) {
return false;
} else {
@ -88,7 +88,7 @@ LIBC_INLINE static constexpr bool fenv_is_round_up() {
}
}
LIBC_INLINE static constexpr bool fenv_is_round_down() {
LIBC_INLINE constexpr bool fenv_is_round_down() {
if (cpp::is_constant_evaluated()) {
return false;
} else {
@ -96,7 +96,7 @@ LIBC_INLINE static constexpr bool fenv_is_round_down() {
}
}
LIBC_INLINE static constexpr bool fenv_is_round_to_nearest() {
LIBC_INLINE constexpr bool fenv_is_round_to_nearest() {
if (cpp::is_constant_evaluated()) {
return true;
} else {
@ -104,7 +104,7 @@ LIBC_INLINE static constexpr bool fenv_is_round_to_nearest() {
}
}
LIBC_INLINE static constexpr bool fenv_is_round_to_zero() {
LIBC_INLINE constexpr bool fenv_is_round_to_zero() {
if (cpp::is_constant_evaluated()) {
return false;
} else {
@ -113,7 +113,7 @@ LIBC_INLINE static constexpr bool fenv_is_round_to_zero() {
}
// Quick free standing get rounding mode based on the above observations.
LIBC_INLINE static constexpr int quick_get_round() {
LIBC_INLINE constexpr int quick_get_round() {
if (cpp::is_constant_evaluated()) {
return FE_TONEAREST;
} else {

View File

@ -15,8 +15,9 @@
namespace LIBC_NAMESPACE_DECL {
namespace math {
LIBC_INLINE double ceil(double x) {
#ifdef __LIBC_USE_BUILTIN_CEIL_FLOOR_RINT_TRUNC
LIBC_INLINE LIBC_CONSTEXPR double ceil(double x) {
#if defined(__LIBC_USE_BUILTIN_CEIL_FLOOR_RINT_TRUNC) && \
!defined(LIBC_HAS_CONSTANT_EVALUATION)
return __builtin_ceil(x);
#else
return fputil::ceil(x);

View File

@ -16,7 +16,7 @@
namespace LIBC_NAMESPACE_DECL {
namespace math {
LIBC_INLINE bfloat16 ceilbf16(bfloat16 x) { return fputil::ceil(x); }
LIBC_INLINE constexpr bfloat16 ceilbf16(bfloat16 x) { return fputil::ceil(x); }
} // namespace math
} // namespace LIBC_NAMESPACE_DECL

View File

@ -15,8 +15,9 @@
namespace LIBC_NAMESPACE_DECL {
namespace math {
LIBC_INLINE float ceilf(float x) {
#ifdef __LIBC_USE_BUILTIN_CEIL_FLOOR_RINT_TRUNC
LIBC_INLINE LIBC_CONSTEXPR float ceilf(float x) {
#if defined(__LIBC_USE_BUILTIN_CEIL_FLOOR_RINT_TRUNC) && \
!defined(LIBC_HAS_CONSTANT_EVALUATION)
return __builtin_ceilf(x);
#else
return fputil::ceil(x);

View File

@ -19,7 +19,7 @@
namespace LIBC_NAMESPACE_DECL {
namespace math {
LIBC_INLINE float128 ceilf128(float128 x) { return fputil::ceil(x); }
LIBC_INLINE constexpr float128 ceilf128(float128 x) { return fputil::ceil(x); }
} // namespace math
} // namespace LIBC_NAMESPACE_DECL

View File

@ -21,9 +21,10 @@
namespace LIBC_NAMESPACE_DECL {
namespace math {
LIBC_INLINE float16 ceilf16(float16 x) {
LIBC_INLINE LIBC_CONSTEXPR float16 ceilf16(float16 x) {
#if defined(__LIBC_USE_BUILTIN_CEIL_FLOOR_RINT_TRUNC) && \
defined(LIBC_TARGET_CPU_HAS_FAST_FLOAT16_OPS)
defined(LIBC_TARGET_CPU_HAS_FAST_FLOAT16_OPS) && \
!defined(LIBC_HAS_CONSTANT_EVALUATION)
return fputil::cast<float16>(__builtin_ceilf(x));
#else
return fputil::ceil(x);

View File

@ -15,6 +15,7 @@
namespace LIBC_NAMESPACE_DECL {
namespace math {
// TODO(issue#185232): Mark as constexpr once the refactor is done.
LIBC_INLINE long double ceill(long double x) { return fputil::ceil(x); }
} // namespace math

View File

@ -256,3 +256,13 @@ add_fp_unittest(
libc.src.__support.math.tanpif
libc.src.__support.math.tanpif16
)
add_fp_unittest(
shared_math_constexpr_test
SUITE
libc-shared-tests
SRCS
shared_math_constexpr_test.cpp
DEPENDS
.shared_math_test
)

View File

@ -0,0 +1,63 @@
//===-- Unittests for shared math functions in constexpr context ----------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#define LIBC_ENABLE_CONSTEXPR 1
#include "shared/math.h"
//===----------------------------------------------------------------------===//
// Double Tests
//===----------------------------------------------------------------------===//
static_assert(0x0p+0 == LIBC_NAMESPACE::shared::ceil(0.0));
static_assert(0x0p+0 == LIBC_NAMESPACE::shared::log(1.0));
//===----------------------------------------------------------------------===//
// Float Tests
//===----------------------------------------------------------------------===//
static_assert(0x0p+0f == LIBC_NAMESPACE::shared::ceilf(0.0f));
//===----------------------------------------------------------------------===//
// Float16 Tests
//===----------------------------------------------------------------------===//
#ifdef LIBC_TYPES_HAS_FLOAT16
static_assert(0x0p+0f16 == LIBC_NAMESPACE::shared::ceilf16(0.0f16));
#endif // LIBC_TYPES_HAS_FLOAT16
//===----------------------------------------------------------------------===//
// Long Double Tests
//===----------------------------------------------------------------------===//
// TODO(issue#185232): Mark as constexpr once the refactor is done.
#if 0 // Temporarily disable long double tests
static_assert(0x0p+0L == LIBC_NAMESPACE::shared::ceill(0.0L));
#endif
//===----------------------------------------------------------------------===//
// Float128 Tests
//===----------------------------------------------------------------------===//
#ifdef LIBC_TYPES_HAS_FLOAT128
static_assert(float128(0x0p+0) ==
LIBC_NAMESPACE::shared::ceilf128(float128(0.0)));
#endif // LIBC_TYPES_HAS_FLOAT128
//===----------------------------------------------------------------------===//
// BFloat16 Tests
//===----------------------------------------------------------------------===//
static_assert(bfloat16(0x0p+0) ==
LIBC_NAMESPACE::shared::ceilbf16(bfloat16(0.0)));

View File

@ -6,22 +6,6 @@
//
//===----------------------------------------------------------------------===//
#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"
@ -270,6 +254,7 @@ 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));