Skip to content

Make more math&bit functions constexpr, NFC #145856

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions llvm/include/llvm/ADT/bit.h
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ template <typename T, typename = std::enable_if_t<std::is_unsigned_v<T>>>
/// Only unsigned integral types are allowed.
///
/// Returns std::numeric_limits<T>::digits on an input of 0.
template <typename T> [[nodiscard]] int countr_zero(T Val) {
template <typename T> [[nodiscard]] constexpr int countr_zero(T Val) {
static_assert(std::is_unsigned_v<T>,
"Only unsigned integral types are allowed.");
if (!Val)
Expand Down Expand Up @@ -200,7 +200,7 @@ template <typename T> [[nodiscard]] int countr_zero(T Val) {
/// Only unsigned integral types are allowed.
///
/// Returns std::numeric_limits<T>::digits on an input of 0.
template <typename T> [[nodiscard]] int countl_zero(T Val) {
template <typename T> [[nodiscard]] constexpr int countl_zero(T Val) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if you are aware that not all code paths here (and some other functions) are actually available in constant expressions ? Like for MSVC it uses _BitScanReverse which itself isn't constexpr.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1, this could be unit-tested

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for pointing it out! I didn’t check all the code paths — I only checked at the __builtin_clz version.

static_assert(std::is_unsigned_v<T>,
"Only unsigned integral types are allowed.");
if (!Val)
Expand Down Expand Up @@ -244,7 +244,7 @@ template <typename T> [[nodiscard]] int countl_zero(T Val) {
/// Only unsigned integral types are allowed.
///
/// Returns std::numeric_limits<T>::digits on an input of all ones.
template <typename T> [[nodiscard]] int countl_one(T Value) {
template <typename T> [[nodiscard]] constexpr int countl_one(T Value) {
static_assert(std::is_unsigned_v<T>,
"Only unsigned integral types are allowed.");
return llvm::countl_zero<T>(~Value);
Expand All @@ -257,7 +257,7 @@ template <typename T> [[nodiscard]] int countl_one(T Value) {
/// Only unsigned integral types are allowed.
///
/// Returns std::numeric_limits<T>::digits on an input of all ones.
template <typename T> [[nodiscard]] int countr_one(T Value) {
template <typename T> [[nodiscard]] constexpr int countr_one(T Value) {
static_assert(std::is_unsigned_v<T>,
"Only unsigned integral types are allowed.");
return llvm::countr_zero<T>(~Value);
Expand All @@ -267,7 +267,7 @@ template <typename T> [[nodiscard]] int countr_one(T Value) {
/// Returns 0 otherwise.
///
/// Ex. bit_width(5) == 3.
template <typename T> [[nodiscard]] int bit_width(T Value) {
template <typename T> [[nodiscard]] constexpr int bit_width(T Value) {
static_assert(std::is_unsigned_v<T>,
"Only unsigned integral types are allowed.");
return std::numeric_limits<T>::digits - llvm::countl_zero(Value);
Expand All @@ -277,7 +277,7 @@ template <typename T> [[nodiscard]] int bit_width(T Value) {
/// nonzero. Returns 0 otherwise.
///
/// Ex. bit_floor(5) == 4.
template <typename T> [[nodiscard]] T bit_floor(T Value) {
template <typename T> [[nodiscard]] constexpr T bit_floor(T Value) {
static_assert(std::is_unsigned_v<T>,
"Only unsigned integral types are allowed.");
if (!Value)
Expand All @@ -292,7 +292,7 @@ template <typename T> [[nodiscard]] T bit_floor(T Value) {
///
/// The return value is undefined if the input is larger than the largest power
/// of two representable in T.
template <typename T> [[nodiscard]] T bit_ceil(T Value) {
template <typename T> [[nodiscard]] constexpr T bit_ceil(T Value) {
static_assert(std::is_unsigned_v<T>,
"Only unsigned integral types are allowed.");
if (Value < 2)
Expand All @@ -304,7 +304,7 @@ template <typename T> [[nodiscard]] T bit_ceil(T Value) {
/// Ex. popcount(0xF000F000) = 8
/// Returns 0 if the word is zero.
template <typename T, typename = std::enable_if_t<std::is_unsigned_v<T>>>
[[nodiscard]] inline int popcount(T Value) noexcept {
[[nodiscard]] inline constexpr int popcount(T Value) noexcept {
if constexpr (sizeof(T) <= 4) {
#if defined(__GNUC__)
return (int)__builtin_popcount(Value);
Expand Down
47 changes: 25 additions & 22 deletions llvm/include/llvm/Support/MathExtras.h
Original file line number Diff line number Diff line change
Expand Up @@ -303,8 +303,8 @@ constexpr bool isPowerOf2_64(uint64_t Value) {
/// If true, \p MaskIdx will specify the index of the lowest set bit and \p
/// MaskLen is updated to specify the length of the mask, else neither are
/// updated.
inline bool isShiftedMask_32(uint32_t Value, unsigned &MaskIdx,
unsigned &MaskLen) {
inline constexpr bool isShiftedMask_32(uint32_t Value, unsigned &MaskIdx,
unsigned &MaskLen) {
if (!isShiftedMask_32(Value))
return false;
MaskIdx = llvm::countr_zero(Value);
Expand All @@ -316,8 +316,8 @@ inline bool isShiftedMask_32(uint32_t Value, unsigned &MaskIdx,
/// remainder zero (64 bit version.) If true, \p MaskIdx will specify the index
/// of the lowest set bit and \p MaskLen is updated to specify the length of the
/// mask, else neither are updated.
inline bool isShiftedMask_64(uint64_t Value, unsigned &MaskIdx,
unsigned &MaskLen) {
inline constexpr bool isShiftedMask_64(uint64_t Value, unsigned &MaskIdx,
unsigned &MaskLen) {
if (!isShiftedMask_64(Value))
return false;
MaskIdx = llvm::countr_zero(Value);
Expand All @@ -337,26 +337,26 @@ template <> constexpr size_t CTLog2<1>() { return 0; }
/// Return the floor log base 2 of the specified value, -1 if the value is zero.
/// (32 bit edition.)
/// Ex. Log2_32(32) == 5, Log2_32(1) == 0, Log2_32(0) == -1, Log2_32(6) == 2
inline unsigned Log2_32(uint32_t Value) {
inline constexpr unsigned Log2_32(uint32_t Value) {
return 31 - llvm::countl_zero(Value);
}

/// Return the floor log base 2 of the specified value, -1 if the value is zero.
/// (64 bit edition.)
inline unsigned Log2_64(uint64_t Value) {
inline constexpr unsigned Log2_64(uint64_t Value) {
return 63 - llvm::countl_zero(Value);
}

/// Return the ceil log base 2 of the specified value, 32 if the value is zero.
/// (32 bit edition).
/// Ex. Log2_32_Ceil(32) == 5, Log2_32_Ceil(1) == 0, Log2_32_Ceil(6) == 3
inline unsigned Log2_32_Ceil(uint32_t Value) {
inline constexpr unsigned Log2_32_Ceil(uint32_t Value) {
return 32 - llvm::countl_zero(Value - 1);
}

/// Return the ceil log base 2 of the specified value, 64 if the value is zero.
/// (64 bit edition.)
inline unsigned Log2_64_Ceil(uint64_t Value) {
inline constexpr unsigned Log2_64_Ceil(uint64_t Value) {
return 64 - llvm::countl_zero(Value - 1);
}

Expand Down Expand Up @@ -391,7 +391,7 @@ constexpr uint64_t NextPowerOf2(uint64_t A) {

/// Returns the power of two which is greater than or equal to the given value.
/// Essentially, it is a ceil operation across the domain of powers of two.
inline uint64_t PowerOf2Ceil(uint64_t A) {
inline constexpr uint64_t PowerOf2Ceil(uint64_t A) {
if (!A || A > UINT64_MAX / 2)
return 0;
return UINT64_C(1) << Log2_64_Ceil(A);
Expand Down Expand Up @@ -569,7 +569,7 @@ template <unsigned B> constexpr int32_t SignExtend32(uint32_t X) {

/// Sign-extend the number in the bottom B bits of X to a 32-bit integer.
/// Requires B <= 32.
inline int32_t SignExtend32(uint32_t X, unsigned B) {
inline constexpr int32_t SignExtend32(uint32_t X, unsigned B) {
assert(B <= 32 && "Bit width out of range.");
if (B == 0)
return 0;
Expand All @@ -587,7 +587,7 @@ template <unsigned B> constexpr int64_t SignExtend64(uint64_t x) {

/// Sign-extend the number in the bottom B bits of X to a 64-bit integer.
/// Requires B <= 64.
inline int64_t SignExtend64(uint64_t X, unsigned B) {
inline constexpr int64_t SignExtend64(uint64_t X, unsigned B) {
assert(B <= 64 && "Bit width out of range.");
if (B == 0)
return 0;
Expand All @@ -614,9 +614,9 @@ constexpr T AbsoluteDifference(U X, V Y) {
/// maximum representable value of T on overflow. ResultOverflowed indicates if
/// the result is larger than the maximum representable value of type T.
template <typename T>
std::enable_if_t<std::is_unsigned_v<T>, T>
constexpr std::enable_if_t<std::is_unsigned_v<T>, T>
SaturatingAdd(T X, T Y, bool *ResultOverflowed = nullptr) {
bool Dummy;
bool Dummy = 0;
bool &Overflowed = ResultOverflowed ? *ResultOverflowed : Dummy;
// Hacker's Delight, p. 29
T Z = X + Y;
Expand All @@ -630,8 +630,8 @@ SaturatingAdd(T X, T Y, bool *ResultOverflowed = nullptr) {
/// Add multiple unsigned integers of type T. Clamp the result to the
/// maximum representable value of T on overflow.
template <class T, class... Ts>
std::enable_if_t<std::is_unsigned_v<T>, T> SaturatingAdd(T X, T Y, T Z,
Ts... Args) {
constexpr std::enable_if_t<std::is_unsigned_v<T>, T>
SaturatingAdd(T X, T Y, T Z, Ts... Args) {
bool Overflowed = false;
T XY = SaturatingAdd(X, Y, &Overflowed);
if (Overflowed)
Expand All @@ -643,9 +643,9 @@ std::enable_if_t<std::is_unsigned_v<T>, T> SaturatingAdd(T X, T Y, T Z,
/// maximum representable value of T on overflow. ResultOverflowed indicates if
/// the result is larger than the maximum representable value of type T.
template <typename T>
std::enable_if_t<std::is_unsigned_v<T>, T>
constexpr std::enable_if_t<std::is_unsigned_v<T>, T>
SaturatingMultiply(T X, T Y, bool *ResultOverflowed = nullptr) {
bool Dummy;
bool Dummy = 0;
bool &Overflowed = ResultOverflowed ? *ResultOverflowed : Dummy;

// Hacker's Delight, p. 30 has a different algorithm, but we don't use that
Expand Down Expand Up @@ -689,9 +689,9 @@ SaturatingMultiply(T X, T Y, bool *ResultOverflowed = nullptr) {
/// overflow. ResultOverflowed indicates if the result is larger than the
/// maximum representable value of type T.
template <typename T>
std::enable_if_t<std::is_unsigned_v<T>, T>
constexpr std::enable_if_t<std::is_unsigned_v<T>, T>
SaturatingMultiplyAdd(T X, T Y, T A, bool *ResultOverflowed = nullptr) {
bool Dummy;
bool Dummy = 0;
bool &Overflowed = ResultOverflowed ? *ResultOverflowed : Dummy;

T Product = SaturatingMultiply(X, Y, &Overflowed);
Expand All @@ -707,7 +707,8 @@ LLVM_ABI extern const float huge_valf;
/// Add two signed integers, computing the two's complement truncated result,
/// returning true if overflow occurred.
template <typename T>
std::enable_if_t<std::is_signed_v<T>, T> AddOverflow(T X, T Y, T &Result) {
constexpr std::enable_if_t<std::is_signed_v<T>, T> AddOverflow(T X, T Y,
T &Result) {
#if __has_builtin(__builtin_add_overflow)
return __builtin_add_overflow(X, Y, &Result);
#else
Expand All @@ -733,7 +734,8 @@ std::enable_if_t<std::is_signed_v<T>, T> AddOverflow(T X, T Y, T &Result) {
/// Subtract two signed integers, computing the two's complement truncated
/// result, returning true if an overflow occurred.
template <typename T>
std::enable_if_t<std::is_signed_v<T>, T> SubOverflow(T X, T Y, T &Result) {
constexpr std::enable_if_t<std::is_signed_v<T>, T> SubOverflow(T X, T Y,
T &Result) {
#if __has_builtin(__builtin_sub_overflow)
return __builtin_sub_overflow(X, Y, &Result);
#else
Expand All @@ -759,7 +761,8 @@ std::enable_if_t<std::is_signed_v<T>, T> SubOverflow(T X, T Y, T &Result) {
/// Multiply two signed integers, computing the two's complement truncated
/// result, returning true if an overflow occurred.
template <typename T>
std::enable_if_t<std::is_signed_v<T>, T> MulOverflow(T X, T Y, T &Result) {
constexpr std::enable_if_t<std::is_signed_v<T>, T> MulOverflow(T X, T Y,
T &Result) {
#if __has_builtin(__builtin_mul_overflow)
return __builtin_mul_overflow(X, Y, &Result);
#else
Expand Down