loader: avoid UB when doing count trailing zeros

This commit is contained in:
Michael Scire 2021-04-30 10:03:54 -07:00
parent 15381409dc
commit dbcb1e1564
2 changed files with 49 additions and 10 deletions

View file

@ -175,20 +175,59 @@ namespace ams::util {
} else { } else {
using U = typename std::make_unsigned<T>::type; using U = typename std::make_unsigned<T>::type;
const U u = static_cast<U>(x); const U u = static_cast<U>(x);
if constexpr (std::is_same<U, unsigned long long>::value) {
return __builtin_clzll(u); if (u != 0) {
} else if constexpr (std::is_same<U, unsigned long>::value) { if constexpr (std::is_same<U, unsigned long long>::value) {
return __builtin_clzl(u); return __builtin_clzll(u);
} else if constexpr(std::is_same<U, unsigned int>::value) { } else if constexpr (std::is_same<U, unsigned long>::value) {
return __builtin_clz(u); return __builtin_clzl(u);
} else if constexpr(std::is_same<U, unsigned int>::value) {
return __builtin_clz(u);
} else {
static_assert(sizeof(U) < sizeof(unsigned int));
constexpr size_t BitDiff = BITSIZEOF(unsigned int) - BITSIZEOF(U);
return __builtin_clz(static_cast<unsigned int>(u)) - BitDiff;
}
} else { } else {
static_assert(sizeof(U) < sizeof(unsigned int)); return BITSIZEOF(U);
constexpr size_t BitDiff = BITSIZEOF(unsigned int) - BITSIZEOF(U);
return __builtin_clz(static_cast<unsigned int>(u)) - BitDiff;
} }
} }
} }
static_assert(CountLeadingZeros(~static_cast<u64>(0)) == 0);
static_assert(CountLeadingZeros(static_cast<u64>(0)) == BITSIZEOF(u64));
template<typename T> requires std::integral<T>
constexpr ALWAYS_INLINE int CountTrailingZeros(T x) {
if (std::is_constant_evaluated()) {
auto count = 0;
for (size_t i = 0; i < BITSIZEOF(T) && (x & 1) == 0; ++i) {
++count;
}
return count;
} else {
using U = typename std::make_unsigned<T>::type;
const U u = static_cast<U>(x);
if (u != 0) {
if constexpr (std::is_same<U, unsigned long long>::value) {
return __builtin_ctzll(u);
} else if constexpr (std::is_same<U, unsigned long>::value) {
return __builtin_ctzl(u);
} else if constexpr(std::is_same<U, unsigned int>::value) {
return __builtin_ctz(u);
} else {
static_assert(sizeof(U) < sizeof(unsigned int));
return __builtin_ctz(static_cast<unsigned int>(u));
}
} else {
return BITSIZEOF(U);
}
}
}
static_assert(CountTrailingZeros(~static_cast<u64>(0)) == 0);
static_assert(CountTrailingZeros(static_cast<u64>(0)) == BITSIZEOF(u64));
template<typename T> requires std::integral<T> template<typename T> requires std::integral<T>
constexpr ALWAYS_INLINE bool IsPowerOfTwo(T x) { constexpr ALWAYS_INLINE bool IsPowerOfTwo(T x) {
return x > 0 && ResetLeastSignificantOneBit(x) == 0; return x > 0 && ResetLeastSignificantOneBit(x) == 0;

View file

@ -44,7 +44,7 @@ namespace ams::ldr::caps {
constexpr ALWAYS_INLINE typename name::Type Get##name() const { return this->Get<name>(); } constexpr ALWAYS_INLINE typename name::Type Get##name() const { return this->Get<name>(); }
constexpr ALWAYS_INLINE CapabilityId GetCapabilityId(util::BitPack32 cap) { constexpr ALWAYS_INLINE CapabilityId GetCapabilityId(util::BitPack32 cap) {
return static_cast<CapabilityId>(__builtin_ctz(~cap.value)); return static_cast<CapabilityId>(util::CountTrailingZeros<u32>(~cap.value));
} }
constexpr inline util::BitPack32 EmptyCapability = {~u32{}}; constexpr inline util::BitPack32 EmptyCapability = {~u32{}};