Selaa lähdekoodia

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.
Nick Johnson 3 vuotta sitten
vanhempi
commit
548529a
3 muutettua tiedostoa jossa 165 lisäystä ja 0 poistoa
  1. 110 0
      AK/BuiltinWrappers.h
  2. 1 0
      Tests/AK/CMakeLists.txt
  3. 54 0
      Tests/AK/TestBuiltinWrappers.cpp

+ 110 - 0
AK/BuiltinWrappers.h

@@ -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);
+}

+ 1 - 0
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

+ 54 - 0
Tests/AK/TestBuiltinWrappers.cpp

@@ -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);
+}