From c8adbd7a8b469355d40e381bd4597a24ef651313 Mon Sep 17 00:00:00 2001 From: Lang Hames Date: Fri, 7 Nov 2025 18:25:04 +1100 Subject: [PATCH] [orc-rt] Add endian_read/write operations. (#166892) The endian_read and endian_write operations read and write unsigned integers stored in a given endianness. --- orc-rt/include/orc-rt/Endian.h | 44 ++++++++++++++ orc-rt/unittests/CMakeLists.txt | 1 + orc-rt/unittests/EndianTest.cpp | 100 ++++++++++++++++++++++++++++++++ 3 files changed, 145 insertions(+) create mode 100644 orc-rt/include/orc-rt/Endian.h create mode 100644 orc-rt/unittests/EndianTest.cpp diff --git a/orc-rt/include/orc-rt/Endian.h b/orc-rt/include/orc-rt/Endian.h new file mode 100644 index 000000000000..538eb3f23d0f --- /dev/null +++ b/orc-rt/include/orc-rt/Endian.h @@ -0,0 +1,44 @@ +//===----- Endian.h - Endianness helpers for the ORC runtime ----*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// Endianness helper functions for the ORC runtime. +// +//===----------------------------------------------------------------------===// + +#ifndef ORC_RT_ENDIAN_H +#define ORC_RT_ENDIAN_H + +#include "bit.h" +#include +#include + +namespace orc_rt { + +/// Read a value with the given endianness from memory. +template +[[nodiscard]] inline std::enable_if_t, T> +endian_read(const void *Src, orc_rt::endian E) noexcept { + T Val; + memcpy(&Val, Src, sizeof(T)); + if (E != orc_rt::endian::native) + Val = orc_rt::byteswap(Val); + return Val; +} + +/// Write a value with the given endianness to memory. +template +inline std::enable_if_t> +endian_write(void *Dst, T Val, orc_rt::endian E) noexcept { + if (E != orc_rt::endian::native) + Val = orc_rt::byteswap(Val); + memcpy(Dst, &Val, sizeof(T)); +} + +} // namespace orc_rt + +#endif // ORC_RT_ENDIAN_H diff --git a/orc-rt/unittests/CMakeLists.txt b/orc-rt/unittests/CMakeLists.txt index 2928fc862b8d..7b943e803944 100644 --- a/orc-rt/unittests/CMakeLists.txt +++ b/orc-rt/unittests/CMakeLists.txt @@ -15,6 +15,7 @@ add_orc_rt_unittest(CoreTests AllocActionTest.cpp BitmaskEnumTest.cpp CallableTraitsHelperTest.cpp + EndianTest.cpp ErrorTest.cpp ExecutorAddressTest.cpp IntervalMapTest.cpp diff --git a/orc-rt/unittests/EndianTest.cpp b/orc-rt/unittests/EndianTest.cpp new file mode 100644 index 000000000000..74dc6ef620bc --- /dev/null +++ b/orc-rt/unittests/EndianTest.cpp @@ -0,0 +1,100 @@ +//===- EndianTest.cpp -----------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// Tests for orc-rt's Endian.h APIs. +// +//===----------------------------------------------------------------------===// + +#include "orc-rt/Endian.h" +#include "gtest/gtest.h" + +#include +#include + +using namespace orc_rt; + +template static void endianRead(T Value, endian E) { + char Buffer[sizeof(T)]; + memcpy(Buffer, &Value, sizeof(T)); + + if (E != endian::native) + std::reverse(Buffer, Buffer + sizeof(T)); + + T NewVal = endian_read(Buffer, E); + EXPECT_EQ(NewVal, Value); +} + +template static void endianWrite(T Value, endian E) { + char Buffer[sizeof(T)]; + + endian_write(Buffer, Value, E); + + if (E != endian::native) + std::reverse(Buffer, Buffer + sizeof(T)); + + T NewVal; + memcpy(&NewVal, Buffer, sizeof(T)); + + EXPECT_EQ(NewVal, Value); +} + +template static void endianReadAndWrite(T Value, endian E) { + endianRead(Value, E); + endianWrite(Value, E); +} + +template static void bothEndiansReadAndWrite(T Value) { + endianReadAndWrite(Value, endian::little); + endianReadAndWrite(Value, endian::big); +} + +// Rotate the given bit pattern through all valid rotations for T, testing that +// the given operation works for the given pattern. +template +void forAllRotatedValues(Op O, T InitialValue) { + T V = InitialValue; + for (size_t I = 0; I != CHAR_BIT * sizeof(T); ++I) { + O(V); + V = llvm::rotl(V, 1); + } +} + +template +void forAllShiftedValues(Op O, T InitialValue) { + T V = InitialValue; + constexpr T TopValueBit = 1 << (std::numeric_limits::digits - 1); + for (size_t I = 0; I != CHAR_BIT * sizeof(T); ++I) { + O(V); + if (V & TopValueBit) + break; + V << 1; + } +} + +TEST(EndianTest, ReadWrite) { + bothEndiansReadAndWrite(0); + bothEndiansReadAndWrite(0xff); + forAllRotatedValues(bothEndiansReadAndWrite, uint8_t(1)); + forAllRotatedValues(bothEndiansReadAndWrite, uint8_t(0x5A)); + + bothEndiansReadAndWrite(0); + bothEndiansReadAndWrite(0xffff); + forAllRotatedValues(bothEndiansReadAndWrite, uint16_t(1)); + forAllRotatedValues(bothEndiansReadAndWrite, uint16_t(0x5A5A)); + + bothEndiansReadAndWrite(0); + bothEndiansReadAndWrite(0xffffffff); + forAllRotatedValues(bothEndiansReadAndWrite, uint32_t(1)); + forAllRotatedValues(bothEndiansReadAndWrite, uint32_t(0x5A5A5A5A)); + + bothEndiansReadAndWrite(0); + bothEndiansReadAndWrite(0xffffffffffffffff); + forAllRotatedValues(bothEndiansReadAndWrite, uint64_t(1)); + forAllRotatedValues(bothEndiansReadAndWrite, + uint64_t(0x5A5A5A5A5A5A5A5A)); +}