diff --git a/AK/BuiltinWrappers.h b/AK/BuiltinWrappers.h new file mode 100644 index 00000000000..79f3a61a451 --- /dev/null +++ b/AK/BuiltinWrappers.h @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2021, Nick Johnson + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include "Concepts.h" +#include "Platform.h" + +template +inline constexpr int popcount(IntType value) +{ +#if defined(__GNUC__) || defined(__clang__) + static_assert(sizeof(IntType) <= sizeof(unsigned long long)); + if constexpr (sizeof(IntType) <= sizeof(unsigned int)) + return __builtin_popcount(value); + if constexpr (sizeof(IntType) == sizeof(unsigned long)) + return __builtin_popcountl(value); + if constexpr (sizeof(IntType) == sizeof(unsigned long long)) + return __builtin_popcountll(value); + VERIFY_NOT_REACHED(); +#else + int ones = 0; + for (size_t i = 0; i < 8 * sizeof(IntType); ++i) { + if ((val >> i) & 1) { + ++ones; + } + } + return ones; +#endif +} + +// The function will return the number of trailing zeroes in the type. If +// the given number if zero, this function may contain undefined +// behavior, or it may return the number of bits in the number. If +// this function can be called with zero, the use of +// count_trailing_zeroes_safe is preferred. +template +inline constexpr int count_trailing_zeroes(IntType value) +{ +#if defined(__GNUC__) || defined(__clang__) + static_assert(sizeof(IntType) <= sizeof(unsigned long long)); + if constexpr (sizeof(IntType) <= sizeof(unsigned int)) + return __builtin_ctz(value); + if constexpr (sizeof(IntType) == sizeof(unsigned long)) + return __builtin_ctzl(value); + if constexpr (sizeof(IntType) == sizeof(unsigned long long)) + return __builtin_ctzll(value); + VERIFY_NOT_REACHED(); +#else + for (size_t i = 0; i < 8 * sizeof(IntType); ++i) { + if ((val >> i) & 1) { + return i; + } + } + return 8 * sizeof(IntType); +#endif +} + +// The function will return the number of trailing zeroes in the type. If +// the given number is zero, this function will return the number of bits +// bits in the IntType. +template +inline constexpr int count_trailing_zeroes_safe(IntType value) +{ + if (value == 0) + return 8 * sizeof(IntType); + return count_trailing_zeroes(value); +} + +// The function will return the number of leading zeroes in the type. If +// the given number if zero, this function may contain undefined +// behavior, or it may return the number of bits in the number. If +// this function can be called with zero, the use of +// count_leading_zeroes_safe is preferred. +template +inline constexpr int count_leading_zeroes(IntType value) +{ +#if defined(__GNUC__) || defined(__clang__) + static_assert(sizeof(IntType) <= sizeof(unsigned long long)); + if constexpr (sizeof(IntType) <= sizeof(unsigned int)) + return __builtin_clz(value) - (32 - (8 * sizeof(IntType))); + if constexpr (sizeof(IntType) == sizeof(unsigned long)) + return __builtin_clzl(value); + if constexpr (sizeof(IntType) == sizeof(unsigned long long)) + return __builtin_clzll(value); + VERIFY_NOT_REACHED(); +#else + // Wrap around, catch going past zero by noticing that i is greater than the number of bits in the number + for (size_t i = (8 * sizeof(IntType)) - 1; i < 8 * sizeof(IntType); --i) { + if ((val >> i) & 1) { + return i; + } + } + return 8 * sizeof(IntType); +#endif +} + +// The function will return the number of leading zeroes in the type. If +// the given number is zero, this function will return the number of bits +// in the IntType. +template +inline constexpr int count_leading_zeroes_safe(IntType value) +{ + if (value == 0) + return 8 * sizeof(IntType); + return count_leading_zeroes(value); +} diff --git a/Tests/AK/CMakeLists.txt b/Tests/AK/CMakeLists.txt index f1cd7d0129f..e1dc8eb0763 100644 --- a/Tests/AK/CMakeLists.txt +++ b/Tests/AK/CMakeLists.txt @@ -10,6 +10,7 @@ set(AK_TEST_SOURCES TestBinarySearch.cpp TestBitCast.cpp TestBitmap.cpp + TestBuiltinWrappers.cpp TestByteBuffer.cpp TestCharacterTypes.cpp TestChecked.cpp diff --git a/Tests/AK/TestBuiltinWrappers.cpp b/Tests/AK/TestBuiltinWrappers.cpp new file mode 100644 index 00000000000..87b0769b7cd --- /dev/null +++ b/Tests/AK/TestBuiltinWrappers.cpp @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2018-2020, Nick Johnson + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include + +#include +#include + +TEST_CASE(wrapped_popcount) +{ + EXPECT_EQ(popcount(NumericLimits::max()), 8); + EXPECT_EQ(popcount(NumericLimits::max()), 16); + EXPECT_EQ(popcount(NumericLimits::max()), 32); + EXPECT_EQ(popcount(NumericLimits::max()), 64); + EXPECT_EQ(popcount(NumericLimits::max()), static_cast(8 * sizeof(size_t))); + EXPECT_EQ(popcount(0u), 0); + EXPECT_EQ(popcount(0b01010101ULL), 4); +} + +TEST_CASE(wrapped_count_leading_zeroes) +{ + EXPECT_EQ(count_leading_zeroes(NumericLimits::max()), 0); + EXPECT_EQ(count_leading_zeroes(static_cast(0x20)), 2); + EXPECT_EQ(count_leading_zeroes_safe(static_cast(0)), 8); + EXPECT_EQ(count_leading_zeroes(NumericLimits::max()), 0); + EXPECT_EQ(count_leading_zeroes(static_cast(0x20)), 10); + EXPECT_EQ(count_leading_zeroes_safe(static_cast(0)), 16); + EXPECT_EQ(count_leading_zeroes(NumericLimits::max()), 0); + EXPECT_EQ(count_leading_zeroes(static_cast(0x20)), 26); + EXPECT_EQ(count_leading_zeroes_safe(static_cast(0)), 32); + EXPECT_EQ(count_leading_zeroes(NumericLimits::max()), 0); +} + +TEST_CASE(wrapped_count_trailing_zeroes) +{ + EXPECT_EQ(count_trailing_zeroes(NumericLimits::max()), 0); + EXPECT_EQ(count_trailing_zeroes(static_cast(1)), 0); + EXPECT_EQ(count_trailing_zeroes(static_cast(2)), 1); + EXPECT_EQ(count_trailing_zeroes_safe(static_cast(0)), 8); + EXPECT_EQ(count_trailing_zeroes(NumericLimits::max()), 0); + EXPECT_EQ(count_trailing_zeroes(static_cast(1)), 0); + EXPECT_EQ(count_trailing_zeroes(static_cast(2)), 1); + EXPECT_EQ(count_trailing_zeroes_safe(static_cast(0)), 16); + EXPECT_EQ(count_trailing_zeroes(NumericLimits::max()), 0); + EXPECT_EQ(count_trailing_zeroes(static_cast(1)), 0); + EXPECT_EQ(count_trailing_zeroes(static_cast(2)), 1); + EXPECT_EQ(count_trailing_zeroes_safe(static_cast(0)), 32); + EXPECT_EQ(count_trailing_zeroes(NumericLimits::max()), 0); + EXPECT_EQ(count_trailing_zeroes(static_cast(1)), 0); + EXPECT_EQ(count_trailing_zeroes(static_cast(2)), 1); +}