[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