diff --git a/llvm/include/llvm/ADT/bit.h b/llvm/include/llvm/ADT/bit.h --- a/llvm/include/llvm/ADT/bit.h +++ b/llvm/include/llvm/ADT/bit.h @@ -20,6 +20,10 @@ #include #include +#if defined(_MSC_VER) && !defined(_DEBUG) +#include // 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 @@ return to; } +/// Reverses the bytes in the given integer value V. +template >> +[[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(UV); + uint32_t Lo = llvm::byteswap(UV >> 32); + return (Hi << 32) | Lo; +#endif + } else { + static_assert(!sizeof(T *), "Don't know how to handle the given type."); + return 0; + } +} + template >> [[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 --- a/llvm/unittests/ADT/BitTest.cpp +++ b/llvm/unittests/ADT/BitTest.cpp @@ -31,6 +31,84 @@ llvm::bit_cast(llvm::bit_cast(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(value); + EXPECT_EQ(original_uint8, llvm::byteswap(llvm::byteswap(original_uint8))); + + uint16_t original_uint16 = static_cast(value); + EXPECT_EQ(original_uint16, llvm::byteswap(llvm::byteswap(original_uint16))); + + uint32_t original_uint32 = static_cast(value); + EXPECT_EQ(original_uint32, llvm::byteswap(llvm::byteswap(original_uint32))); + + uint64_t original_uint64 = static_cast(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(value); + EXPECT_EQ(original_int8, llvm::byteswap(llvm::byteswap(original_int8))); + + int16_t original_int16 = static_cast(value); + EXPECT_EQ(original_int16, llvm::byteswap(llvm::byteswap(original_int16))); + + int32_t original_int32 = static_cast(value); + EXPECT_EQ(original_int32, llvm::byteswap(llvm::byteswap(original_int32))); + + int64_t original_int64 = static_cast(value); + EXPECT_EQ(original_int64, llvm::byteswap(llvm::byteswap(original_int64))); + + // Test other sign. + value *= -1; + + original_int8 = static_cast(value); + EXPECT_EQ(original_int8, llvm::byteswap(llvm::byteswap(original_int8))); + + original_int16 = static_cast(value); + EXPECT_EQ(original_int16, llvm::byteswap(llvm::byteswap(original_int16))); + + original_int32 = static_cast(value); + EXPECT_EQ(original_int32, llvm::byteswap(llvm::byteswap(original_int32))); + + original_int64 = static_cast(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));