AK: Add BuiltinWrappers.h

The goal of this file is to enable C++ overloaded functions for
standard builtin functions that we use. It contains fallback
implementations for systems that do not have the builtins available.
This commit is contained in:
Nick Johnson 2021-12-12 16:52:57 -06:00 committed by Andreas Kling
parent 01eefc344a
commit 548529ace4
Notes: sideshowbarker 2024-07-17 22:38:31 +09:00
3 changed files with 165 additions and 0 deletions

110
AK/BuiltinWrappers.h Normal file
View file

@ -0,0 +1,110 @@
/*
* Copyright (c) 2021, Nick Johnson <sylvyrfysh@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include "Concepts.h"
#include "Platform.h"
template<Unsigned IntType>
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<Unsigned IntType>
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<Unsigned IntType>
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<Unsigned IntType>
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<Unsigned IntType>
inline constexpr int count_leading_zeroes_safe(IntType value)
{
if (value == 0)
return 8 * sizeof(IntType);
return count_leading_zeroes(value);
}

View file

@ -10,6 +10,7 @@ set(AK_TEST_SOURCES
TestBinarySearch.cpp
TestBitCast.cpp
TestBitmap.cpp
TestBuiltinWrappers.cpp
TestByteBuffer.cpp
TestCharacterTypes.cpp
TestChecked.cpp

View file

@ -0,0 +1,54 @@
/*
* Copyright (c) 2018-2020, Nick Johnson <sylvyrfysh@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibTest/TestCase.h>
#include <AK/BuiltinWrappers.h>
#include <AK/Types.h>
TEST_CASE(wrapped_popcount)
{
EXPECT_EQ(popcount(NumericLimits<u8>::max()), 8);
EXPECT_EQ(popcount(NumericLimits<u16>::max()), 16);
EXPECT_EQ(popcount(NumericLimits<u32>::max()), 32);
EXPECT_EQ(popcount(NumericLimits<u64>::max()), 64);
EXPECT_EQ(popcount(NumericLimits<size_t>::max()), static_cast<int>(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<u8>::max()), 0);
EXPECT_EQ(count_leading_zeroes(static_cast<u8>(0x20)), 2);
EXPECT_EQ(count_leading_zeroes_safe(static_cast<u8>(0)), 8);
EXPECT_EQ(count_leading_zeroes(NumericLimits<u16>::max()), 0);
EXPECT_EQ(count_leading_zeroes(static_cast<u16>(0x20)), 10);
EXPECT_EQ(count_leading_zeroes_safe(static_cast<u16>(0)), 16);
EXPECT_EQ(count_leading_zeroes(NumericLimits<u32>::max()), 0);
EXPECT_EQ(count_leading_zeroes(static_cast<u32>(0x20)), 26);
EXPECT_EQ(count_leading_zeroes_safe(static_cast<u32>(0)), 32);
EXPECT_EQ(count_leading_zeroes(NumericLimits<u64>::max()), 0);
}
TEST_CASE(wrapped_count_trailing_zeroes)
{
EXPECT_EQ(count_trailing_zeroes(NumericLimits<u8>::max()), 0);
EXPECT_EQ(count_trailing_zeroes(static_cast<u8>(1)), 0);
EXPECT_EQ(count_trailing_zeroes(static_cast<u8>(2)), 1);
EXPECT_EQ(count_trailing_zeroes_safe(static_cast<u8>(0)), 8);
EXPECT_EQ(count_trailing_zeroes(NumericLimits<u16>::max()), 0);
EXPECT_EQ(count_trailing_zeroes(static_cast<u16>(1)), 0);
EXPECT_EQ(count_trailing_zeroes(static_cast<u16>(2)), 1);
EXPECT_EQ(count_trailing_zeroes_safe(static_cast<u16>(0)), 16);
EXPECT_EQ(count_trailing_zeroes(NumericLimits<u32>::max()), 0);
EXPECT_EQ(count_trailing_zeroes(static_cast<u32>(1)), 0);
EXPECT_EQ(count_trailing_zeroes(static_cast<u32>(2)), 1);
EXPECT_EQ(count_trailing_zeroes_safe(static_cast<u32>(0)), 32);
EXPECT_EQ(count_trailing_zeroes(NumericLimits<u64>::max()), 0);
EXPECT_EQ(count_trailing_zeroes(static_cast<u64>(1)), 0);
EXPECT_EQ(count_trailing_zeroes(static_cast<u64>(2)), 1);
}