diff --git a/libc/src/__support/CMakeLists.txt b/libc/src/__support/CMakeLists.txt --- a/libc/src/__support/CMakeLists.txt +++ b/libc/src/__support/CMakeLists.txt @@ -2,6 +2,7 @@ common HDRS common.h + endian.h sanitizer.h ) diff --git a/libc/src/__support/endian.h b/libc/src/__support/endian.h new file mode 100644 --- /dev/null +++ b/libc/src/__support/endian.h @@ -0,0 +1,142 @@ +//===-- Endianness support ------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIBC_SRC_SUPPORT_ENDIAN_H +#define LLVM_LIBC_SRC_SUPPORT_ENDIAN_H + +#include + +namespace __llvm_libc { + +// We rely on compiler preprocessor defines to allow for cross compilation. +#if !defined(__BYTE_ORDER__) || !defined(__ORDER_LITTLE_ENDIAN__) || \ + !defined(__ORDER_BIG_ENDIAN__) +#error "Missing preprocessor definitions for endianness detection." +#endif + +namespace internal { + +// Converts uint8_t, uint16_t, uint32_t, uint64_t to its big or little endian +// counterpart. +// We use explicit template specialization: +// - to prevent accidental integer promotion. +// - to prevent fallback in (unlikely) case of middle-endianness. + +template struct Endian { + static constexpr const bool isLittle = ORDER == __ORDER_LITTLE_ENDIAN__; + static constexpr const bool isBig = ORDER == __ORDER_BIG_ENDIAN__; + template static T ToBigEndian(T value); + template static T ToLittleEndian(T value); +}; + +// Little Endian specializations +template <> +template <> +inline uint8_t +Endian<__ORDER_LITTLE_ENDIAN__>::ToBigEndian(uint8_t v) { + return v; +} +template <> +template <> +inline uint8_t +Endian<__ORDER_LITTLE_ENDIAN__>::ToLittleEndian(uint8_t v) { + return v; +} +template <> +template <> +inline uint16_t +Endian<__ORDER_LITTLE_ENDIAN__>::ToBigEndian(uint16_t v) { + return __builtin_bswap16(v); +} +template <> +template <> +inline uint16_t +Endian<__ORDER_LITTLE_ENDIAN__>::ToLittleEndian(uint16_t v) { + return v; +} +template <> +template <> +inline uint32_t +Endian<__ORDER_LITTLE_ENDIAN__>::ToBigEndian(uint32_t v) { + return __builtin_bswap32(v); +} +template <> +template <> +inline uint32_t +Endian<__ORDER_LITTLE_ENDIAN__>::ToLittleEndian(uint32_t v) { + return v; +} +template <> +template <> +inline uint64_t +Endian<__ORDER_LITTLE_ENDIAN__>::ToBigEndian(uint64_t v) { + return __builtin_bswap64(v); +} +template <> +template <> +inline uint64_t +Endian<__ORDER_LITTLE_ENDIAN__>::ToLittleEndian(uint64_t v) { + return v; +} + +// Big Endian specializations +template <> +template <> +inline uint8_t Endian<__ORDER_BIG_ENDIAN__>::ToBigEndian(uint8_t v) { + return v; +} +template <> +template <> +inline uint8_t +Endian<__ORDER_BIG_ENDIAN__>::ToLittleEndian(uint8_t v) { + return v; +} +template <> +template <> +inline uint16_t +Endian<__ORDER_BIG_ENDIAN__>::ToBigEndian(uint16_t v) { + return v; +} +template <> +template <> +inline uint16_t +Endian<__ORDER_BIG_ENDIAN__>::ToLittleEndian(uint16_t v) { + return __builtin_bswap16(v); +} +template <> +template <> +inline uint32_t +Endian<__ORDER_BIG_ENDIAN__>::ToBigEndian(uint32_t v) { + return v; +} +template <> +template <> +inline uint32_t +Endian<__ORDER_BIG_ENDIAN__>::ToLittleEndian(uint32_t v) { + return __builtin_bswap32(v); +} +template <> +template <> +inline uint64_t +Endian<__ORDER_BIG_ENDIAN__>::ToBigEndian(uint64_t v) { + return v; +} +template <> +template <> +inline uint64_t +Endian<__ORDER_BIG_ENDIAN__>::ToLittleEndian(uint64_t v) { + return __builtin_bswap64(v); +} + +} // namespace internal + +using Endian = internal::Endian<__BYTE_ORDER__>; + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_SUPPORT_ENDIAN_H diff --git a/libc/test/src/CMakeLists.txt b/libc/test/src/CMakeLists.txt --- a/libc/test/src/CMakeLists.txt +++ b/libc/test/src/CMakeLists.txt @@ -1,3 +1,4 @@ +add_subdirectory(__support) add_subdirectory(ctype) add_subdirectory(errno) add_subdirectory(fenv) diff --git a/libc/test/src/__support/CMakeLists.txt b/libc/test/src/__support/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/libc/test/src/__support/CMakeLists.txt @@ -0,0 +1,11 @@ +add_libc_testsuite(libc_support_unittests) + +add_libc_unittest( + endian_test + SUITE + libc_support_unittests + SRCS + endian_test.cpp + DEPENDS + libc.src.__support.common +) diff --git a/libc/test/src/__support/endian_test.cpp b/libc/test/src/__support/endian_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/__support/endian_test.cpp @@ -0,0 +1,55 @@ +//===-- Unittests for endian ----------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "src/__support/endian.h" +#include "utils/UnitTest/Test.h" + +namespace __llvm_libc { + +struct LlvmLibcEndian : testing::Test { + template void check(const T original, const T swapped) { +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + EXPECT_EQ(Endian::ToLittleEndian(original), original); + EXPECT_EQ(Endian::ToBigEndian(original), swapped); +#endif +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + EXPECT_EQ(Endian::ToBigEndian(original), original); + EXPECT_EQ(Endian::ToLittleEndian(original), swapped); +#endif + } +}; + +TEST_F(LlvmLibcEndian, Field) { + EXPECT_EQ(Endian::isLittle, __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__); + EXPECT_EQ(Endian::isBig, __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__); +} + +TEST_F(LlvmLibcEndian, uint8_t) { + const auto original = uint8_t(0x12); + check(original, original); +} + +TEST_F(LlvmLibcEndian, uint16_t) { + const auto original = uint16_t(0x1234); + const auto swapped = __builtin_bswap16(original); + check(original, swapped); +} + +TEST_F(LlvmLibcEndian, uint32_t) { + const auto original = uint32_t(0x12345678); + const auto swapped = __builtin_bswap32(original); + check(original, swapped); +} + +TEST_F(LlvmLibcEndian, uint64_t) { + const auto original = uint64_t(0x123456789ABCDEF0); + const auto swapped = __builtin_bswap64(original); + check(original, swapped); +} + +} // namespace __llvm_libc