Commit 33b7fa2d authored by Yedidya Feldblum's avatar Yedidya Feldblum Committed by Facebook Github Bot

Smaller implementations for findFirstSet, findLastSet

Summary: [Folly] Smaller implementations for `findFirstSet`, `findLastSet`.

Reviewed By: nbronson

Differential Revision: D8471816

fbshipit-source-id: 699af98d404e6dd76df02e344d09c850abc1c32f
parent e0122b40
...@@ -60,159 +60,118 @@ ...@@ -60,159 +60,118 @@
#include <limits> #include <limits>
#include <type_traits> #include <type_traits>
#include <folly/ConstexprMath.h>
#include <folly/Portability.h> #include <folly/Portability.h>
#include <folly/Utility.h>
#include <folly/lang/Assume.h> #include <folly/lang/Assume.h>
#include <folly/portability/Builtins.h> #include <folly/portability/Builtins.h>
namespace folly { namespace folly {
// Generate overloads for findFirstSet as wrappers around namespace detail {
// appropriate ffs, ffsl, ffsll gcc builtins template <typename Dst, typename Src>
template <class T> constexpr std::make_signed_t<Dst> bits_to_signed(Src const s) {
inline FOLLY_INTRINSIC_CONSTEXPR static_assert(std::is_signed<Dst>::value, "unsigned type");
typename std::enable_if< return to_signed(static_cast<std::make_unsigned_t<Dst>>(to_unsigned(s)));
(std::is_integral<T>::value &&
std::is_unsigned<T>::value &&
sizeof(T) <= sizeof(unsigned int)),
unsigned int>::type
findFirstSet(T x) {
return static_cast<unsigned int>(__builtin_ffs(static_cast<int>(x)));
}
template <class T>
inline FOLLY_INTRINSIC_CONSTEXPR
typename std::enable_if<
(std::is_integral<T>::value &&
std::is_unsigned<T>::value &&
sizeof(T) > sizeof(unsigned int) &&
sizeof(T) <= sizeof(unsigned long)),
unsigned int>::type
findFirstSet(T x) {
return static_cast<unsigned int>(__builtin_ffsl(static_cast<long>(x)));
}
template <class T>
inline FOLLY_INTRINSIC_CONSTEXPR
typename std::enable_if<
(std::is_integral<T>::value &&
std::is_unsigned<T>::value &&
sizeof(T) > sizeof(unsigned long) &&
sizeof(T) <= sizeof(unsigned long long)),
unsigned int>::type
findFirstSet(T x) {
return static_cast<unsigned int>(__builtin_ffsll(static_cast<long long>(x)));
} }
template <typename Dst, typename Src>
template <class T> constexpr std::make_unsigned_t<Dst> bits_to_unsigned(Src const s) {
inline FOLLY_INTRINSIC_CONSTEXPR static_assert(std::is_unsigned<Dst>::value, "signed type");
typename std::enable_if< return static_cast<Dst>(to_unsigned(s));
(std::is_integral<T>::value && std::is_signed<T>::value),
unsigned int>::type
findFirstSet(T x) {
// Note that conversion from a signed type to the corresponding unsigned
// type is technically implementation-defined, but will likely work
// on any impementation that uses two's complement.
return findFirstSet(static_cast<typename std::make_unsigned<T>::type>(x));
}
// findLastSet: return the 1-based index of the highest bit set
// for x > 0, findLastSet(x) == 1 + floor(log2(x))
template <class T>
inline FOLLY_INTRINSIC_CONSTEXPR
typename std::enable_if<
(std::is_integral<T>::value &&
std::is_unsigned<T>::value &&
sizeof(T) <= sizeof(unsigned int)),
unsigned int>::type
findLastSet(T x) {
// If X is a power of two X - Y = ((X - 1) ^ Y) + 1. Doing this transformation
// allows GCC to remove its own xor that it adds to implement clz using bsr
return x ? ((8 * sizeof(unsigned int) - 1) ^ __builtin_clz(x)) + 1 : 0;
} }
} // namespace detail
template <class T> /// findFirstSet
inline FOLLY_INTRINSIC_CONSTEXPR ///
typename std::enable_if< /// Return the 1-based index of the least significant bit which is set.
(std::is_integral<T>::value && /// For x > 0, the exponent in the largest power of two which does not divide x.
std::is_unsigned<T>::value && template <typename T>
sizeof(T) > sizeof(unsigned int) && inline FOLLY_INTRINSIC_CONSTEXPR unsigned int findFirstSet(T const v) {
sizeof(T) <= sizeof(unsigned long)), using S0 = int;
unsigned int>::type using S1 = long int;
findLastSet(T x) { using S2 = long long int;
return x ? ((8 * sizeof(unsigned long) - 1) ^ __builtin_clzl(x)) + 1 : 0; using detail::bits_to_signed;
static_assert(sizeof(T) <= sizeof(S2), "over-sized type");
static_assert(std::is_integral<T>::value, "non-integral type");
static_assert(!std::is_same<T, bool>::value, "bool type");
// clang-format off
return static_cast<unsigned int>(
sizeof(T) <= sizeof(S0) ? __builtin_ffs(bits_to_signed<S0>(v)) :
sizeof(T) <= sizeof(S1) ? __builtin_ffsl(bits_to_signed<S1>(v)) :
sizeof(T) <= sizeof(S2) ? __builtin_ffsll(bits_to_signed<S2>(v)) :
0);
// clang-format on
} }
template <class T> /// findLastSet
inline FOLLY_INTRINSIC_CONSTEXPR ///
typename std::enable_if< /// Return the 1-based index of the most significant bit which is set.
(std::is_integral<T>::value && /// For x > 0, findLastSet(x) == 1 + floor(log2(x)).
std::is_unsigned<T>::value && template <typename T>
sizeof(T) > sizeof(unsigned long) && inline FOLLY_INTRINSIC_CONSTEXPR unsigned int findLastSet(T const v) {
sizeof(T) <= sizeof(unsigned long long)), using U0 = unsigned int;
unsigned int>::type using U1 = unsigned long int;
findLastSet(T x) { using U2 = unsigned long long int;
return x ? ((8 * sizeof(unsigned long long) - 1) ^ __builtin_clzll(x)) + 1 using detail::bits_to_unsigned;
: 0; static_assert(sizeof(T) <= sizeof(U2), "over-sized type");
static_assert(std::is_integral<T>::value, "non-integral type");
static_assert(!std::is_same<T, bool>::value, "bool type");
// If X is a power of two X - Y = 1 + ((X - 1) ^ Y). Doing this transformation
// allows GCC to remove its own xor that it adds to implement clz using bsr.
// clang-format off
using size = index_constant<constexpr_max(sizeof(T), sizeof(U0))>;
return v ? 1u + ((8u * size{} - 1u) ^ static_cast<unsigned int>(
sizeof(T) <= sizeof(U0) ? __builtin_clz(bits_to_unsigned<U1>(v)) :
sizeof(T) <= sizeof(U1) ? __builtin_clzl(bits_to_unsigned<U1>(v)) :
sizeof(T) <= sizeof(U2) ? __builtin_clzll(bits_to_unsigned<U2>(v)) :
0)) : 0u;
// clang-format on
} }
template <class T> /// popcount
inline FOLLY_INTRINSIC_CONSTEXPR ///
typename std::enable_if< /// Returns the number of bits which are set.
(std::is_integral<T>::value && template <typename T>
std::is_signed<T>::value), inline FOLLY_INTRINSIC_CONSTEXPR unsigned int popcount(T const v) {
unsigned int>::type using U0 = unsigned int;
findLastSet(T x) { using U1 = unsigned long int;
return findLastSet(static_cast<typename std::make_unsigned<T>::type>(x)); using U2 = unsigned long long int;
using detail::bits_to_unsigned;
static_assert(sizeof(T) <= sizeof(U2), "over-sized type");
static_assert(std::is_integral<T>::value, "non-integral type");
static_assert(!std::is_same<T, bool>::value, "bool type");
// clang-format off
return static_cast<unsigned int>(
sizeof(T) <= sizeof(U0) ? __builtin_popcount(bits_to_unsigned<U1>(v)) :
sizeof(T) <= sizeof(U1) ? __builtin_popcountl(bits_to_unsigned<U1>(v)) :
sizeof(T) <= sizeof(U2) ? __builtin_popcountll(bits_to_unsigned<U2>(v)) :
0);
// clang-format on
} }
template <class T> template <class T>
inline FOLLY_INTRINSIC_CONSTEXPR inline FOLLY_INTRINSIC_CONSTEXPR T nextPowTwo(T const v) {
typename std::enable_if< static_assert(std::is_unsigned<T>::value, "signed type");
std::is_integral<T>::value && std::is_unsigned<T>::value, return v ? (T(1) << findLastSet(v - 1)) : T(1);
T>::type
nextPowTwo(T v) {
return v ? (T(1) << findLastSet(v - 1)) : 1;
} }
template <class T> template <class T>
inline FOLLY_INTRINSIC_CONSTEXPR typename std:: inline FOLLY_INTRINSIC_CONSTEXPR T prevPowTwo(T const v) {
enable_if<std::is_integral<T>::value && std::is_unsigned<T>::value, T>::type static_assert(std::is_unsigned<T>::value, "signed type");
prevPowTwo(T v) { return v ? (T(1) << (findLastSet(v) - 1)) : T(0);
return v ? (T(1) << (findLastSet(v) - 1)) : 0;
} }
template <class T> template <class T>
inline constexpr typename std::enable_if< inline constexpr bool isPowTwo(T const v) {
std::is_integral<T>::value && std::is_unsigned<T>::value, static_assert(std::is_integral<T>::value, "non-integral type");
bool>::type static_assert(std::is_unsigned<T>::value, "signed type");
isPowTwo(T v) { static_assert(!std::is_same<T, bool>::value, "bool type");
return (v != 0) && !(v & (v - 1)); return (v != 0) && !(v & (v - 1));
} }
/**
* Population count
*/
template <class T>
inline typename std::enable_if<
(std::is_integral<T>::value &&
std::is_unsigned<T>::value &&
sizeof(T) <= sizeof(unsigned int)),
size_t>::type
popcount(T x) {
return size_t(__builtin_popcount(x));
}
template <class T>
inline typename std::enable_if<
(std::is_integral<T>::value &&
std::is_unsigned<T>::value &&
sizeof(T) > sizeof(unsigned int) &&
sizeof(T) <= sizeof(unsigned long long)),
size_t>::type
popcount(T x) {
return size_t(__builtin_popcountll(x));
}
/** /**
* Endianness detection and manipulation primitives. * Endianness detection and manipulation primitives.
*/ */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment