[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