[llvm] 51454e0 - [ADT] Add llvm::byteswap to bit.h

Kazu Hirata via llvm-commits llvm-commits at lists.llvm.org
Sun Jan 22 09:29:40 PST 2023


Author: Kazu Hirata
Date: 2023-01-22T09:29:35-08:00
New Revision: 51454e05112abf6345625f8fff620facd62f2d03

URL: https://github.com/llvm/llvm-project/commit/51454e05112abf6345625f8fff620facd62f2d03
DIFF: https://github.com/llvm/llvm-project/commit/51454e05112abf6345625f8fff620facd62f2d03.diff

LOG: [ADT] Add llvm::byteswap to bit.h

This patch adds C++23-style byteswap to bit.h.

The implementation and tests are largely taken from
llvm/include/llvm/Support/SwapByteOrder.h and
llvm/unittests/Support/SwapByteOrderTest.cpp, respectively.

Differential Revision: https://reviews.llvm.org/D142274

Added: 
    

Modified: 
    llvm/include/llvm/ADT/bit.h
    llvm/unittests/ADT/BitTest.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/include/llvm/ADT/bit.h b/llvm/include/llvm/ADT/bit.h
index 8208440bb33c2..887dd519fa44a 100644
--- a/llvm/include/llvm/ADT/bit.h
+++ b/llvm/include/llvm/ADT/bit.h
@@ -20,6 +20,10 @@
 #include <limits>
 #include <type_traits>
 
+#if defined(_MSC_VER) && !defined(_DEBUG)
+#include <cstdlib>  // for _byteswap_{ushort,ulong,uint64}
+#endif
+
 #ifdef _MSC_VER
 // Declare these intrinsics manually rather including intrin.h. It's very
 // expensive, and bit.h is popular via MathExtras.h.
@@ -49,6 +53,52 @@ template <
   return to;
 }
 
+/// Reverses the bytes in the given integer value V.
+template <typename T, typename = std::enable_if_t<std::is_integral_v<T>>>
+[[nodiscard]] constexpr T byteswap(T V) noexcept {
+  if constexpr (sizeof(T) == 1) {
+    return V;
+  } else if constexpr (sizeof(T) == 2) {
+    uint16_t UV = V;
+#if defined(_MSC_VER) && !defined(_DEBUG)
+    // The DLL version of the runtime lacks these functions (bug!?), but in a
+    // release build they're replaced with BSWAP instructions anyway.
+    return _byteswap_ushort(UV);
+#else
+    uint16_t Hi = UV << 8;
+    uint16_t Lo = UV >> 8;
+    return Hi | Lo;
+#endif
+  } else if constexpr (sizeof(T) == 4) {
+    uint32_t UV = V;
+#if __has_builtin(__builtin_bswap32)
+    return __builtin_bswap32(UV);
+#elif defined(_MSC_VER) && !defined(_DEBUG)
+    return _byteswap_ulong(UV);
+#else
+    uint32_t Byte0 = UV & 0x000000FF;
+    uint32_t Byte1 = UV & 0x0000FF00;
+    uint32_t Byte2 = UV & 0x00FF0000;
+    uint32_t Byte3 = UV & 0xFF000000;
+    return (Byte0 << 24) | (Byte1 << 8) | (Byte2 >> 8) | (Byte3 >> 24);
+#endif
+  } else if constexpr (sizeof(T) == 8) {
+    uint64_t UV = V;
+#if __has_builtin(__builtin_bswap64)
+    return __builtin_bswap64(UV);
+#elif defined(_MSC_VER) && !defined(_DEBUG)
+    return _byteswap_uint64(UV);
+#else
+    uint64_t Hi = llvm::byteswap<uint32_t>(UV);
+    uint32_t Lo = llvm::byteswap<uint32_t>(UV >> 32);
+    return (Hi << 32) | Lo;
+#endif
+  } else {
+    static_assert(!sizeof(T *), "Don't know how to handle the given type.");
+    return 0;
+  }
+}
+
 template <typename T, typename = std::enable_if_t<std::is_unsigned_v<T>>>
 [[nodiscard]] constexpr inline bool has_single_bit(T Value) noexcept {
   return (Value != 0) && ((Value & (Value - 1)) == 0);

diff  --git a/llvm/unittests/ADT/BitTest.cpp b/llvm/unittests/ADT/BitTest.cpp
index 08ec31f230711..65d8680e2fb97 100644
--- a/llvm/unittests/ADT/BitTest.cpp
+++ b/llvm/unittests/ADT/BitTest.cpp
@@ -31,6 +31,84 @@ TEST(BitTest, BitCast) {
                    llvm::bit_cast<double>(llvm::bit_cast<uint64_t>(kValueF64)));
 }
 
+// In these first two tests all of the original_uintx values are truncated
+// except for 64. We could avoid this, but there's really no point.
+
+TEST(BitTest, ByteSwapUnsignedRoundTrip) {
+  // The point of the bit twiddling of magic is to test with and without bits
+  // in every byte.
+  uint64_t value = 1;
+  for (std::size_t i = 0; i <= sizeof(value); ++i) {
+    uint8_t original_uint8 = static_cast<uint8_t>(value);
+    EXPECT_EQ(original_uint8, llvm::byteswap(llvm::byteswap(original_uint8)));
+
+    uint16_t original_uint16 = static_cast<uint16_t>(value);
+    EXPECT_EQ(original_uint16, llvm::byteswap(llvm::byteswap(original_uint16)));
+
+    uint32_t original_uint32 = static_cast<uint32_t>(value);
+    EXPECT_EQ(original_uint32, llvm::byteswap(llvm::byteswap(original_uint32)));
+
+    uint64_t original_uint64 = static_cast<uint64_t>(value);
+    EXPECT_EQ(original_uint64, llvm::byteswap(llvm::byteswap(original_uint64)));
+
+    value = (value << 8) | 0x55; // binary 0101 0101.
+  }
+}
+
+TEST(BitTest, ByteSwapSignedRoundTrip) {
+  // The point of the bit twiddling of magic is to test with and without bits
+  // in every byte.
+  uint64_t value = 1;
+  for (std::size_t i = 0; i <= sizeof(value); ++i) {
+    int8_t original_int8 = static_cast<int8_t>(value);
+    EXPECT_EQ(original_int8, llvm::byteswap(llvm::byteswap(original_int8)));
+
+    int16_t original_int16 = static_cast<int16_t>(value);
+    EXPECT_EQ(original_int16, llvm::byteswap(llvm::byteswap(original_int16)));
+
+    int32_t original_int32 = static_cast<int32_t>(value);
+    EXPECT_EQ(original_int32, llvm::byteswap(llvm::byteswap(original_int32)));
+
+    int64_t original_int64 = static_cast<int64_t>(value);
+    EXPECT_EQ(original_int64, llvm::byteswap(llvm::byteswap(original_int64)));
+
+    // Test other sign.
+    value *= -1;
+
+    original_int8 = static_cast<int8_t>(value);
+    EXPECT_EQ(original_int8, llvm::byteswap(llvm::byteswap(original_int8)));
+
+    original_int16 = static_cast<int16_t>(value);
+    EXPECT_EQ(original_int16, llvm::byteswap(llvm::byteswap(original_int16)));
+
+    original_int32 = static_cast<int32_t>(value);
+    EXPECT_EQ(original_int32, llvm::byteswap(llvm::byteswap(original_int32)));
+
+    original_int64 = static_cast<int64_t>(value);
+    EXPECT_EQ(original_int64, llvm::byteswap(llvm::byteswap(original_int64)));
+
+    // Return to normal sign and twiddle.
+    value *= -1;
+    value = (value << 8) | 0x55; // binary 0101 0101.
+  }
+}
+
+TEST(BitTest, ByteSwap) {
+  // Unsigned types.
+  EXPECT_EQ(uint8_t(0x11), llvm::byteswap(uint8_t(0x11)));
+  EXPECT_EQ(uint16_t(0x1122), llvm::byteswap(uint16_t(0x2211)));
+  EXPECT_EQ(uint32_t(0x11223344), llvm::byteswap(uint32_t(0x44332211)));
+  EXPECT_EQ(uint64_t(0x1122334455667788ULL),
+            llvm::byteswap(uint64_t(0x8877665544332211ULL)));
+
+  // Signed types.
+  EXPECT_EQ(int8_t(0x11), llvm::byteswap(int8_t(0x11)));
+  EXPECT_EQ(int16_t(0x1122), llvm::byteswap(int16_t(0x2211)));
+  EXPECT_EQ(int32_t(0x11223344), llvm::byteswap(int32_t(0x44332211)));
+  EXPECT_EQ(int64_t(0x1122334455667788LL),
+            llvm::byteswap(int64_t(0x8877665544332211LL)));
+}
+
 TEST(BitTest, HasSingleBit) {
   EXPECT_FALSE(llvm::has_single_bit(0U));
   EXPECT_FALSE(llvm::has_single_bit(0ULL));


        


More information about the llvm-commits mailing list