[libc] Add endianness support
Add endianness detection support. This will be useful to implement `memcmp`.
Differential Revision: https://reviews.llvm.org/D100571
GitOrigin-RevId: 7e075ad0b261236dd0a01f0b5e01f3221b0700d7
diff --git a/src/__support/CMakeLists.txt b/src/__support/CMakeLists.txt
index b20e8bc..4206d74 100644
--- a/src/__support/CMakeLists.txt
+++ b/src/__support/CMakeLists.txt
@@ -2,6 +2,7 @@
common
HDRS
common.h
+ endian.h
sanitizer.h
)
diff --git a/src/__support/endian.h b/src/__support/endian.h
new file mode 100644
index 0000000..e1d52ca
--- /dev/null
+++ b/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 <stdint.h>
+
+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 <unsigned ORDER> struct Endian {
+ static constexpr const bool isLittle = ORDER == __ORDER_LITTLE_ENDIAN__;
+ static constexpr const bool isBig = ORDER == __ORDER_BIG_ENDIAN__;
+ template <typename T> static T ToBigEndian(T value);
+ template <typename T> static T ToLittleEndian(T value);
+};
+
+// Little Endian specializations
+template <>
+template <>
+inline uint8_t
+Endian<__ORDER_LITTLE_ENDIAN__>::ToBigEndian<uint8_t>(uint8_t v) {
+ return v;
+}
+template <>
+template <>
+inline uint8_t
+Endian<__ORDER_LITTLE_ENDIAN__>::ToLittleEndian<uint8_t>(uint8_t v) {
+ return v;
+}
+template <>
+template <>
+inline uint16_t
+Endian<__ORDER_LITTLE_ENDIAN__>::ToBigEndian<uint16_t>(uint16_t v) {
+ return __builtin_bswap16(v);
+}
+template <>
+template <>
+inline uint16_t
+Endian<__ORDER_LITTLE_ENDIAN__>::ToLittleEndian<uint16_t>(uint16_t v) {
+ return v;
+}
+template <>
+template <>
+inline uint32_t
+Endian<__ORDER_LITTLE_ENDIAN__>::ToBigEndian<uint32_t>(uint32_t v) {
+ return __builtin_bswap32(v);
+}
+template <>
+template <>
+inline uint32_t
+Endian<__ORDER_LITTLE_ENDIAN__>::ToLittleEndian<uint32_t>(uint32_t v) {
+ return v;
+}
+template <>
+template <>
+inline uint64_t
+Endian<__ORDER_LITTLE_ENDIAN__>::ToBigEndian<uint64_t>(uint64_t v) {
+ return __builtin_bswap64(v);
+}
+template <>
+template <>
+inline uint64_t
+Endian<__ORDER_LITTLE_ENDIAN__>::ToLittleEndian<uint64_t>(uint64_t v) {
+ return v;
+}
+
+// Big Endian specializations
+template <>
+template <>
+inline uint8_t Endian<__ORDER_BIG_ENDIAN__>::ToBigEndian<uint8_t>(uint8_t v) {
+ return v;
+}
+template <>
+template <>
+inline uint8_t
+Endian<__ORDER_BIG_ENDIAN__>::ToLittleEndian<uint8_t>(uint8_t v) {
+ return v;
+}
+template <>
+template <>
+inline uint16_t
+Endian<__ORDER_BIG_ENDIAN__>::ToBigEndian<uint16_t>(uint16_t v) {
+ return v;
+}
+template <>
+template <>
+inline uint16_t
+Endian<__ORDER_BIG_ENDIAN__>::ToLittleEndian<uint16_t>(uint16_t v) {
+ return __builtin_bswap16(v);
+}
+template <>
+template <>
+inline uint32_t
+Endian<__ORDER_BIG_ENDIAN__>::ToBigEndian<uint32_t>(uint32_t v) {
+ return v;
+}
+template <>
+template <>
+inline uint32_t
+Endian<__ORDER_BIG_ENDIAN__>::ToLittleEndian<uint32_t>(uint32_t v) {
+ return __builtin_bswap32(v);
+}
+template <>
+template <>
+inline uint64_t
+Endian<__ORDER_BIG_ENDIAN__>::ToBigEndian<uint64_t>(uint64_t v) {
+ return v;
+}
+template <>
+template <>
+inline uint64_t
+Endian<__ORDER_BIG_ENDIAN__>::ToLittleEndian<uint64_t>(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/test/src/CMakeLists.txt b/test/src/CMakeLists.txt
index 1d7c470..d4689d9 100644
--- a/test/src/CMakeLists.txt
+++ b/test/src/CMakeLists.txt
@@ -1,3 +1,4 @@
+add_subdirectory(__support)
add_subdirectory(ctype)
add_subdirectory(errno)
add_subdirectory(fenv)
diff --git a/test/src/__support/CMakeLists.txt b/test/src/__support/CMakeLists.txt
new file mode 100644
index 0000000..813e413
--- /dev/null
+++ b/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/test/src/__support/endian_test.cpp b/test/src/__support/endian_test.cpp
new file mode 100644
index 0000000..89e2be2
--- /dev/null
+++ b/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 <typename T> 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