diff --git a/compiler-rt/lib/orc/CMakeLists.txt b/compiler-rt/lib/orc/CMakeLists.txt index a94a68a72436..417033a20621 100644 --- a/compiler-rt/lib/orc/CMakeLists.txt +++ b/compiler-rt/lib/orc/CMakeLists.txt @@ -19,6 +19,7 @@ set(ORC_IMPL_HEADERS endianness.h error.h extensible_rtti.h + simple_packed_serialization.h stl_extras.h wrapper_function_utils.h ) diff --git a/compiler-rt/lib/orc/common.h b/compiler-rt/lib/orc/common.h index bc64366a477a..62ff12a365e6 100644 --- a/compiler-rt/lib/orc/common.h +++ b/compiler-rt/lib/orc/common.h @@ -14,6 +14,7 @@ #define ORC_RT_COMMON_H #include "c_api.h" +#include /// Opaque struct for external symbols. struct __orc_rt_Opaque {}; diff --git a/compiler-rt/lib/orc/simple_packed_serialization.h b/compiler-rt/lib/orc/simple_packed_serialization.h new file mode 100644 index 000000000000..a6e90f506d7c --- /dev/null +++ b/compiler-rt/lib/orc/simple_packed_serialization.h @@ -0,0 +1,556 @@ +//===--- simple_packed_serialization.h - simple serialization ---*- 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 +// +//===----------------------------------------------------------------------===// +// +// This file is a part of the ORC runtime support library. +// +// The behavior of the utilities in this header must be synchronized with the +// behavior of the utilities in +// llvm/ExecutionEngine/Orc/Shared/WrapperFunctionUtils.h. +// +// The Simple Packed Serialization (SPS) utilities are used to generate +// argument and return buffers for wrapper functions using the following +// serialization scheme: +// +// Primitives: +// bool, char, int8_t, uint8_t -- Two's complement 8-bit (0=false, 1=true) +// int16_t, uint16_t -- Two's complement 16-bit little endian +// int32_t, uint32_t -- Two's complement 32-bit little endian +// int64_t, int64_t -- Two's complement 64-bit little endian +// +// Sequence: +// Serialized as the sequence length (as a uint64_t) followed by the +// serialization of each of the elements without padding. +// +// Tuple: +// Serialized as each of the element types from T1 to TN without padding. +// +//===----------------------------------------------------------------------===// + +#ifndef ORC_RT_SIMPLE_PACKED_SERIALIZATION_H +#define ORC_RT_SIMPLE_PACKED_SERIALIZATION_H + +#include "adt.h" +#include "endianness.h" +#include "error.h" +#include "stl_extras.h" + +#include +#include +#include +#include +#include + +namespace __orc_rt { + +/// Output char buffer with overflow check. +class SPSOutputBuffer { +public: + SPSOutputBuffer(char *Buffer, size_t Remaining) + : Buffer(Buffer), Remaining(Remaining) {} + bool write(const char *Data, size_t Size) { + if (Size > Remaining) + return false; + memcpy(Buffer, Data, Size); + Buffer += Size; + Remaining -= Size; + return true; + } + +private: + char *Buffer = nullptr; + size_t Remaining = 0; +}; + +/// Input char buffer with underflow check. +class SPSInputBuffer { +public: + SPSInputBuffer() = default; + SPSInputBuffer(const char *Buffer, size_t Remaining) + : Buffer(Buffer), Remaining(Remaining) {} + bool read(char *Data, size_t Size) { + if (Size > Remaining) + return false; + memcpy(Data, Buffer, Size); + Buffer += Size; + Remaining -= Size; + return true; + } + + const char *data() const { return Buffer; } + bool skip(size_t Size) { + if (Size > Remaining) + return false; + Remaining -= Size; + return true; + } + +private: + const char *Buffer = nullptr; + size_t Remaining = 0; +}; + +/// Specialize to describe how to serialize/deserialize to/from the given +/// concrete type. +template +class SPSSerializationTraits; + +/// A utility class for serializing to a blob from a variadic list. +template class SPSArgList; + +// Empty list specialization for SPSArgList. +template <> class SPSArgList<> { +public: + static size_t size() { return 0; } + + static bool serialize(SPSOutputBuffer &OB) { return true; } + static bool deserialize(SPSInputBuffer &IB) { return true; } +}; + +// Non-empty list specialization for SPSArgList. +template +class SPSArgList { +public: + template + static size_t size(const ArgT &Arg, const ArgTs &...Args) { + return SPSSerializationTraits::size(Arg) + + SPSArgList::size(Args...); + } + + template + static bool serialize(SPSOutputBuffer &OB, const ArgT &Arg, + const ArgTs &...Args) { + return SPSSerializationTraits::serialize(OB, Arg) && + SPSArgList::serialize(OB, Args...); + } + + template + static bool deserialize(SPSInputBuffer &IB, ArgT &Arg, ArgTs &...Args) { + return SPSSerializationTraits::deserialize(IB, Arg) && + SPSArgList::deserialize(IB, Args...); + } +}; + +/// SPS serialization for integral types, bool, and char. +template +class SPSSerializationTraits< + SPSTagT, SPSTagT, + std::enable_if_t::value || + std::is_same::value || + std::is_same::value || + std::is_same::value || + std::is_same::value || + std::is_same::value || + std::is_same::value || + std::is_same::value || + std::is_same::value || + std::is_same::value>> { +public: + static size_t size(const SPSTagT &Value) { return sizeof(SPSTagT); } + + static bool serialize(SPSOutputBuffer &OB, const SPSTagT &Value) { + SPSTagT Tmp = Value; + if (IsBigEndianHost) + swapByteOrder(Tmp); + return OB.write(reinterpret_cast(&Tmp), sizeof(Tmp)); + } + + static bool deserialize(SPSInputBuffer &IB, SPSTagT &Value) { + SPSTagT Tmp; + if (!IB.read(reinterpret_cast(&Tmp), sizeof(Tmp))) + return false; + if (IsBigEndianHost) + swapByteOrder(Tmp); + Value = Tmp; + return true; + } +}; + +// Any empty placeholder suitable as a substitute for void when deserializing +class SPSEmpty {}; + +/// SPS tag type for target addresses. +/// +/// SPSTagTargetAddresses should be serialized as a uint64_t value. +class SPSTagTargetAddress; + +template <> +class SPSSerializationTraits + : public SPSSerializationTraits {}; + +/// SPS tag type for tuples. +/// +/// A blob tuple should be serialized by serializing each of the elements in +/// sequence. +template class SPSTuple { +public: + /// Convenience typedef of the corresponding arg list. + typedef SPSArgList AsArgList; +}; + +/// SPS tag type for sequences. +/// +/// SPSSequences should be serialized as a uint64_t sequence length, +/// followed by the serialization of each of the elements. +template class SPSSequence; + +/// SPS tag type for strings, which are equivalent to sequences of chars. +using SPSString = SPSSequence; + +/// SPS tag type for maps. +/// +/// SPS maps are just sequences of (Key, Value) tuples. +template +using SPSMap = SPSSequence>; + +/// Serialization for SPSEmpty type. +template <> class SPSSerializationTraits { +public: + static size_t size(const SPSEmpty &EP) { return 0; } + static bool serialize(SPSOutputBuffer &OB, const SPSEmpty &BE) { + return true; + } + static bool deserialize(SPSInputBuffer &IB, SPSEmpty &BE) { return true; } +}; + +/// Specialize this to implement 'trivial' sequence serialization for +/// a concrete sequence type. +/// +/// Trivial sequence serialization uses the sequence's 'size' member to get the +/// length of the sequence, and uses a range-based for loop to iterate over the +/// elements. +/// +/// Specializing this template class means that you do not need to provide a +/// specialization of SPSSerializationTraits for your type. +template +class TrivialSPSSequenceSerialization { +public: + static constexpr bool available = false; +}; + +/// Specialize this to implement 'trivial' sequence deserialization for +/// a concrete sequence type. +/// +/// Trivial deserialization calls a static 'reserve(SequenceT&)' method on your +/// specialization (you must implement this) to reserve space, and then calls +/// a static 'append(SequenceT&, ElementT&) method to append each of the +/// deserialized elements. +/// +/// Specializing this template class means that you do not need to provide a +/// specialization of SPSSerializationTraits for your type. +template +class TrivialSPSSequenceDeserialization { +public: + static constexpr bool available = false; +}; + +/// Trivial std::string -> SPSSequence serialization. +template <> class TrivialSPSSequenceSerialization { +public: + static constexpr bool available = true; +}; + +/// Trivial SPSSequence -> std::string deserialization. +template <> class TrivialSPSSequenceDeserialization { +public: + static constexpr bool available = true; + + using element_type = char; + + static void reserve(std::string &S, uint64_t Size) { S.reserve(Size); } + static bool append(std::string &S, char C) { + S.push_back(C); + return true; + } +}; + +/// Trivial std::vector -> SPSSequence serialization. +template +class TrivialSPSSequenceSerialization> { +public: + static constexpr bool available = true; +}; + +/// Trivial SPSSequence -> std::vector deserialization. +template +class TrivialSPSSequenceDeserialization> { +public: + static constexpr bool available = true; + + using element_type = typename std::vector::value_type; + + static void reserve(std::vector &V, uint64_t Size) { V.reserve(Size); } + static bool append(std::vector &V, T E) { + V.push_back(std::move(E)); + return true; + } +}; + +/// 'Trivial' sequence serialization: Sequence is serialized as a uint64_t size +/// followed by a for-earch loop over the elements of the sequence to serialize +/// each of them. +template +class SPSSerializationTraits, SequenceT, + std::enable_if_t::available>> { +public: + static size_t size(const SequenceT &S) { + size_t Size = SPSArgList::size(static_cast(S.size())); + for (const auto &E : S) + Size += SPSArgList::size(E); + return Size; + } + + static bool serialize(SPSOutputBuffer &OB, const SequenceT &S) { + if (!SPSArgList::serialize(OB, static_cast(S.size()))) + return false; + for (const auto &E : S) + if (!SPSArgList::serialize(OB, E)) + return false; + return true; + } + + static bool deserialize(SPSInputBuffer &IB, SequenceT &S) { + using TBSD = TrivialSPSSequenceDeserialization; + uint64_t Size; + if (!SPSArgList::deserialize(IB, Size)) + return false; + TBSD::reserve(S, Size); + for (size_t I = 0; I != Size; ++I) { + typename TBSD::element_type E; + if (!SPSArgList::deserialize(IB, E)) + return false; + if (!TBSD::append(S, std::move(E))) + return false; + } + return true; + } +}; + +/// SPSTuple serialization for std::pair. +template +class SPSSerializationTraits, std::pair> { +public: + static size_t size(const std::pair &P) { + return SPSArgList::size(P.first) + + SPSArgList::size(P.second); + } + + static bool serialize(SPSOutputBuffer &OB, const std::pair &P) { + return SPSArgList::serialize(OB, P.first) && + SPSArgList::serialize(OB, P.second); + } + + static bool deserialize(SPSInputBuffer &IB, std::pair &P) { + return SPSArgList::deserialize(IB, P.first) && + SPSArgList::deserialize(IB, P.second); + } +}; + +/// Serialization for string_views. +/// +/// Serialization is as for regular strings. Deserialization points directly +/// into the blob. +template <> class SPSSerializationTraits { +public: + static size_t size(const __orc_rt::string_view &S) { + return SPSArgList::size(static_cast(S.size())) + + S.size(); + } + + static bool serialize(SPSOutputBuffer &OB, const __orc_rt::string_view &S) { + if (!SPSArgList::serialize(OB, static_cast(S.size()))) + return false; + return OB.write(S.data(), S.size()); + } + + static bool deserialize(SPSInputBuffer &IB, __orc_rt::string_view &S) { + const char *Data = nullptr; + uint64_t Size; + if (!SPSArgList::deserialize(IB, Size)) + return false; + Data = IB.data(); + if (!IB.skip(Size)) + return false; + S = {Data, Size}; + return true; + } +}; + +/// SPS tag type for errors. +class SPSError; + +/// SPS tag type for expecteds, which are either a T or a string representing +/// an error. +template class SPSExpected; + +namespace detail { + +/// Helper type for serializing Errors. +/// +/// llvm::Errors are move-only, and not inspectable except by consuming them. +/// This makes them unsuitable for direct serialization via +/// SPSSerializationTraits, which needs to inspect values twice (once to +/// determine the amount of space to reserve, and then again to serialize). +/// +/// The SPSSerializableError type is a helper that can be +/// constructed from an llvm::Error, but inspected more than once. +struct SPSSerializableError { + bool HasError = false; + std::string ErrMsg; +}; + +/// Helper type for serializing Expecteds. +/// +/// See SPSSerializableError for more details. +/// +// FIXME: Use std::variant for storage once we have c++17. +template struct SPSSerializableExpected { + bool HasValue = false; + T Value{}; + std::string ErrMsg; +}; + +inline SPSSerializableError toSPSSerializable(Error Err) { + if (Err) + return {true, toString(std::move(Err))}; + return {false, {}}; +} + +inline Error fromSPSSerializable(SPSSerializableError BSE) { + if (BSE.HasError) + return make_error(BSE.ErrMsg); + return Error::success(); +} + +template +SPSSerializableExpected toSPSSerializable(Expected E) { + if (E) + return {true, std::move(*E), {}}; + else + return {false, {}, toString(E.takeError())}; +} + +template +Expected fromSPSSerializable(SPSSerializableExpected BSE) { + if (BSE.HasValue) + return std::move(BSE.Value); + else + return make_error(BSE.ErrMsg); +} + +} // end namespace detail + +/// Serialize to a SPSError from a detail::SPSSerializableError. +template <> +class SPSSerializationTraits { +public: + static size_t size(const detail::SPSSerializableError &BSE) { + size_t Size = SPSArgList::size(BSE.HasError); + if (BSE.HasError) + Size += SPSArgList::size(BSE.ErrMsg); + return Size; + } + + static bool serialize(SPSOutputBuffer &OB, + const detail::SPSSerializableError &BSE) { + if (!SPSArgList::serialize(OB, BSE.HasError)) + return false; + if (BSE.HasError) + if (!SPSArgList::serialize(OB, BSE.ErrMsg)) + return false; + return true; + } + + static bool deserialize(SPSInputBuffer &IB, + detail::SPSSerializableError &BSE) { + if (!SPSArgList::deserialize(IB, BSE.HasError)) + return false; + + if (!BSE.HasError) + return true; + + return SPSArgList::deserialize(IB, BSE.ErrMsg); + } +}; + +/// Serialize to a SPSExpected from a +/// detail::SPSSerializableExpected. +template +class SPSSerializationTraits, + detail::SPSSerializableExpected> { +public: + static size_t size(const detail::SPSSerializableExpected &BSE) { + size_t Size = SPSArgList::size(BSE.HasValue); + if (BSE.HasValue) + Size += SPSArgList::size(BSE.Value); + else + Size += SPSArgList::size(BSE.ErrMsg); + return Size; + } + + static bool serialize(SPSOutputBuffer &OB, + const detail::SPSSerializableExpected &BSE) { + if (!SPSArgList::serialize(OB, BSE.HasValue)) + return false; + + if (BSE.HasValue) + return SPSArgList::serialize(OB, BSE.Value); + + return SPSArgList::serialize(OB, BSE.ErrMsg); + } + + static bool deserialize(SPSInputBuffer &IB, + detail::SPSSerializableExpected &BSE) { + if (!SPSArgList::deserialize(IB, BSE.HasValue)) + return false; + + if (BSE.HasValue) + return SPSArgList::deserialize(IB, BSE.Value); + + return SPSArgList::deserialize(IB, BSE.ErrMsg); + } +}; + +/// Serialize to a SPSExpected from a detail::SPSSerializableError. +template +class SPSSerializationTraits, + detail::SPSSerializableError> { +public: + static size_t size(const detail::SPSSerializableError &BSE) { + assert(BSE.HasError && "Cannot serialize expected from a success value"); + return SPSArgList::size(false) + + SPSArgList::size(BSE.ErrMsg); + } + + static bool serialize(SPSOutputBuffer &OB, + const detail::SPSSerializableError &BSE) { + assert(BSE.HasError && "Cannot serialize expected from a success value"); + if (!SPSArgList::serialize(OB, false)) + return false; + return SPSArgList::serialize(OB, BSE.ErrMsg); + } +}; + +/// Serialize to a SPSExpected from a T. +template +class SPSSerializationTraits, T> { +public: + static size_t size(const T &Value) { + return SPSArgList::size(true) + SPSArgList::size(Value); + } + + static bool serialize(SPSOutputBuffer &OB, const T &Value) { + if (!SPSArgList::serialize(OB, true)) + return false; + return SPSArgList::serialize(Value); + } +}; + +} // end namespace __orc_rt + +#endif // ORC_RT_SIMPLE_PACKED_SERIALIZATION_H diff --git a/compiler-rt/lib/orc/unittests/CMakeLists.txt b/compiler-rt/lib/orc/unittests/CMakeLists.txt index 0c84cd594451..fbbf7faec1e9 100644 --- a/compiler-rt/lib/orc/unittests/CMakeLists.txt +++ b/compiler-rt/lib/orc/unittests/CMakeLists.txt @@ -88,6 +88,7 @@ set(UNITTEST_SOURCES orc_unit_test_main.cpp stl_extras_test.cpp wrapper_function_utils_test.cpp + simple_packed_serialization_test.cpp ) if (COMPILER_RT_CAN_EXECUTE_TESTS) diff --git a/compiler-rt/lib/orc/unittests/simple_packed_serialization_test.cpp b/compiler-rt/lib/orc/unittests/simple_packed_serialization_test.cpp new file mode 100644 index 000000000000..2e6d43d1fb9a --- /dev/null +++ b/compiler-rt/lib/orc/unittests/simple_packed_serialization_test.cpp @@ -0,0 +1,165 @@ +//===-- simple_packed_serialization_test.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 +// +//===----------------------------------------------------------------------===// +// +// This file is a part of the ORC runtime. +// +//===----------------------------------------------------------------------===// + +#include "simple_packed_serialization.h" +#include "gtest/gtest.h" + +#include + +using namespace __orc_rt; + +TEST(SimplePackedSerializationTest, SPSOutputBuffer) { + constexpr unsigned NumBytes = 8; + char Buffer[NumBytes]; + char Zero = 0; + SPSOutputBuffer OB(Buffer, NumBytes); + + // Expect that we can write NumBytes of content. + for (unsigned I = 0; I != NumBytes; ++I) { + char C = I; + EXPECT_TRUE(OB.write(&C, 1)); + } + + // Expect an error when we attempt to write an extra byte. + EXPECT_FALSE(OB.write(&Zero, 1)); + + // Check that the buffer contains the expected content. + for (unsigned I = 0; I != NumBytes; ++I) + EXPECT_EQ(Buffer[I], (char)I); +} + +TEST(SimplePackedSerializationTest, SPSInputBuffer) { + char Buffer[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}; + SPSInputBuffer IB(Buffer, sizeof(Buffer)); + + char C; + for (unsigned I = 0; I != sizeof(Buffer); ++I) { + EXPECT_TRUE(IB.read(&C, 1)); + EXPECT_EQ(C, (char)I); + } + + EXPECT_FALSE(IB.read(&C, 1)); +} + +template +static void blobSerializationRoundTrip(const T &Value) { + using BST = SPSSerializationTraits; + + size_t Size = BST::size(Value); + auto Buffer = std::make_unique(Size); + SPSOutputBuffer OB(Buffer.get(), Size); + + EXPECT_TRUE(BST::serialize(OB, Value)); + + SPSInputBuffer IB(Buffer.get(), Size); + + T DSValue; + EXPECT_TRUE(BST::deserialize(IB, DSValue)); + + EXPECT_EQ(Value, DSValue) + << "Incorrect value after serialization/deserialization round-trip"; +} + +template static void testFixedIntegralTypeSerialization() { + blobSerializationRoundTrip(0); + blobSerializationRoundTrip(static_cast(1)); + if (std::is_signed::value) { + blobSerializationRoundTrip(static_cast(-1)); + blobSerializationRoundTrip(std::numeric_limits::min()); + } + blobSerializationRoundTrip(std::numeric_limits::max()); +} + +TEST(SimplePackedSerializationTest, BoolSerialization) { + blobSerializationRoundTrip(true); + blobSerializationRoundTrip(false); +} + +TEST(SimplePackedSerializationTest, CharSerialization) { + blobSerializationRoundTrip((char)0x00); + blobSerializationRoundTrip((char)0xAA); + blobSerializationRoundTrip((char)0xFF); +} + +TEST(SimplePackedSerializationTest, Int8Serialization) { + testFixedIntegralTypeSerialization(); +} + +TEST(SimplePackedSerializationTest, UInt8Serialization) { + testFixedIntegralTypeSerialization(); +} + +TEST(SimplePackedSerializationTest, Int16Serialization) { + testFixedIntegralTypeSerialization(); +} + +TEST(SimplePackedSerializationTest, UInt16Serialization) { + testFixedIntegralTypeSerialization(); +} + +TEST(SimplePackedSerializationTest, Int32Serialization) { + testFixedIntegralTypeSerialization(); +} + +TEST(SimplePackedSerializationTest, UInt32Serialization) { + testFixedIntegralTypeSerialization(); +} + +TEST(SimplePackedSerializationTest, Int64Serialization) { + testFixedIntegralTypeSerialization(); +} + +TEST(SimplePackedSerializationTest, UInt64Serialization) { + testFixedIntegralTypeSerialization(); +} + +TEST(SimplePackedSerializationTest, SequenceSerialization) { + std::vector V({1, 2, -47, 139}); + blobSerializationRoundTrip, std::vector>(V); +} + +TEST(SimplePackedSerializationTest, StringViewCharSequenceSerialization) { + const char *HW = "Hello, world!"; + blobSerializationRoundTrip(string_view(HW)); +} + +TEST(SimplePackedSerializationTest, StdPairSerialization) { + std::pair P(42, "foo"); + blobSerializationRoundTrip, + std::pair>(P); +} + +TEST(SimplePackedSerializationTest, ArgListSerialization) { + using BAL = SPSArgList; + + bool Arg1 = true; + int32_t Arg2 = 42; + std::string Arg3 = "foo"; + + size_t Size = BAL::size(Arg1, Arg2, Arg3); + auto Buffer = std::make_unique(Size); + SPSOutputBuffer OB(Buffer.get(), Size); + + EXPECT_TRUE(BAL::serialize(OB, Arg1, Arg2, Arg3)); + + SPSInputBuffer IB(Buffer.get(), Size); + + bool ArgOut1; + int32_t ArgOut2; + std::string ArgOut3; + + EXPECT_TRUE(BAL::deserialize(IB, ArgOut1, ArgOut2, ArgOut3)); + + EXPECT_EQ(Arg1, ArgOut1); + EXPECT_EQ(Arg2, ArgOut2); + EXPECT_EQ(Arg3, ArgOut3); +} diff --git a/compiler-rt/lib/orc/unittests/wrapper_function_utils_test.cpp b/compiler-rt/lib/orc/unittests/wrapper_function_utils_test.cpp index 936e5210eeef..c1f6896ac05a 100644 --- a/compiler-rt/lib/orc/unittests/wrapper_function_utils_test.cpp +++ b/compiler-rt/lib/orc/unittests/wrapper_function_utils_test.cpp @@ -68,153 +68,6 @@ TEST(WrapperFunctionUtilsTest, WrapperFunctionResultFromOutOfBandError) { EXPECT_TRUE(strcmp(R.getOutOfBandError(), TestString) == 0); } -TEST(WrapperFunctionUtilsTest, SPSOutputBuffer) { - constexpr unsigned NumBytes = 8; - char Buffer[NumBytes]; - char Zero = 0; - SPSOutputBuffer OB(Buffer, NumBytes); - - // Expect that we can write NumBytes of content. - for (unsigned I = 0; I != NumBytes; ++I) { - char C = I; - EXPECT_TRUE(OB.write(&C, 1)); - } - - // Expect an error when we attempt to write an extra byte. - EXPECT_FALSE(OB.write(&Zero, 1)); - - // Check that the buffer contains the expected content. - for (unsigned I = 0; I != NumBytes; ++I) - EXPECT_EQ(Buffer[I], (char)I); -} - -TEST(WrapperFunctionUtilsTest, SPSInputBuffer) { - char Buffer[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}; - SPSInputBuffer IB(Buffer, sizeof(Buffer)); - - char C; - for (unsigned I = 0; I != sizeof(Buffer); ++I) { - EXPECT_TRUE(IB.read(&C, 1)); - EXPECT_EQ(C, (char)I); - } - - EXPECT_FALSE(IB.read(&C, 1)); -} - -template -static void blobSerializationRoundTrip(const T &Value) { - using BST = SPSSerializationTraits; - - size_t Size = BST::size(Value); - auto Buffer = std::make_unique(Size); - SPSOutputBuffer OB(Buffer.get(), Size); - - EXPECT_TRUE(BST::serialize(OB, Value)); - - SPSInputBuffer IB(Buffer.get(), Size); - - T DSValue; - EXPECT_TRUE(BST::deserialize(IB, DSValue)); - - EXPECT_EQ(Value, DSValue) - << "Incorrect value after serialization/deserialization round-trip"; -} - -template static void testFixedIntegralTypeSerialization() { - blobSerializationRoundTrip(0); - blobSerializationRoundTrip(static_cast(1)); - if (std::is_signed::value) { - blobSerializationRoundTrip(static_cast(-1)); - blobSerializationRoundTrip(std::numeric_limits::min()); - } - blobSerializationRoundTrip(std::numeric_limits::max()); -} - -TEST(WrapperFunctionUtilsTest, BoolSerialization) { - blobSerializationRoundTrip(true); - blobSerializationRoundTrip(false); -} - -TEST(WrapperFunctionUtilsTest, CharSerialization) { - blobSerializationRoundTrip((char)0x00); - blobSerializationRoundTrip((char)0xAA); - blobSerializationRoundTrip((char)0xFF); -} - -TEST(WrapperFunctionUtilsTest, Int8Serialization) { - testFixedIntegralTypeSerialization(); -} - -TEST(WrapperFunctionUtilsTest, UInt8Serialization) { - testFixedIntegralTypeSerialization(); -} - -TEST(WrapperFunctionUtilsTest, Int16Serialization) { - testFixedIntegralTypeSerialization(); -} - -TEST(WrapperFunctionUtilsTest, UInt16Serialization) { - testFixedIntegralTypeSerialization(); -} - -TEST(WrapperFunctionUtilsTest, Int32Serialization) { - testFixedIntegralTypeSerialization(); -} - -TEST(WrapperFunctionUtilsTest, UInt32Serialization) { - testFixedIntegralTypeSerialization(); -} - -TEST(WrapperFunctionUtilsTest, Int64Serialization) { - testFixedIntegralTypeSerialization(); -} - -TEST(WrapperFunctionUtilsTest, UInt64Serialization) { - testFixedIntegralTypeSerialization(); -} - -TEST(WrapperFunctionUtilsTest, SequenceSerialization) { - std::vector V({1, 2, -47, 139}); - blobSerializationRoundTrip, std::vector>(V); -} - -TEST(WrapperFunctionUtilsTest, StringViewCharSequenceSerialization) { - const char *HW = "Hello, world!"; - blobSerializationRoundTrip(string_view(HW)); -} - -TEST(WrapperFunctionUtilsTest, StdPairSerialization) { - std::pair P(42, "foo"); - blobSerializationRoundTrip, - std::pair>(P); -} - -TEST(WrapperFunctionUtilsTest, ArgListSerialization) { - using BAL = SPSArgList; - - bool Arg1 = true; - int32_t Arg2 = 42; - std::string Arg3 = "foo"; - - size_t Size = BAL::size(Arg1, Arg2, Arg3); - auto Buffer = std::make_unique(Size); - SPSOutputBuffer OB(Buffer.get(), Size); - - EXPECT_TRUE(BAL::serialize(OB, Arg1, Arg2, Arg3)); - - SPSInputBuffer IB(Buffer.get(), Size); - - bool ArgOut1; - int32_t ArgOut2; - std::string ArgOut3; - - EXPECT_TRUE(BAL::deserialize(IB, ArgOut1, ArgOut2, ArgOut3)); - - EXPECT_EQ(Arg1, ArgOut1); - EXPECT_EQ(Arg2, ArgOut2); - EXPECT_EQ(Arg3, ArgOut3); -} - static __orc_rt_CWrapperFunctionResult addWrapper(const char *ArgData, size_t ArgSize) { return WrapperFunction::handle( diff --git a/compiler-rt/lib/orc/wrapper_function_utils.h b/compiler-rt/lib/orc/wrapper_function_utils.h index de82eaf3f7fe..416429fe439b 100644 --- a/compiler-rt/lib/orc/wrapper_function_utils.h +++ b/compiler-rt/lib/orc/wrapper_function_utils.h @@ -8,44 +8,16 @@ // // This file is a part of the ORC runtime support library. // -// The behavior of the utilities in this header must be synchronized with the -// behavior of the utilities in -// llvm/ExecutionEngine/Orc/Shared/WrapperFunctionUtils.h. -// -// The Simple Packed Serialization (SPS) utilities are used to generate -// argument and return buffers for wrapper functions using the following -// serialization scheme: -// -// Primitives: -// bool, char, int8_t, uint8_t -- Two's complement 8-bit (0=false, 1=true) -// int16_t, uint16_t -- Two's complement 16-bit little endian -// int32_t, uint32_t -- Two's complement 32-bit little endian -// int64_t, int64_t -- Two's complement 64-bit little endian -// -// Sequence: -// Serialized as the sequence length (as a uint64_t) followed by the -// serialization of each of the elements without padding. -// -// Tuple: -// Serialized as each of the element types from T1 to TN without padding. -// //===----------------------------------------------------------------------===// #ifndef ORC_RT_WRAPPER_FUNCTION_UTILS_H #define ORC_RT_WRAPPER_FUNCTION_UTILS_H -#include "adt.h" #include "c_api.h" #include "common.h" -#include "endianness.h" #include "error.h" -#include "stl_extras.h" - -#include -#include +#include "simple_packed_serialization.h" #include -#include -#include namespace __orc_rt { @@ -126,6 +98,11 @@ public: return __orc_rt_CreateCWrapperFunctionResultFromOutOfBandError(Msg); } + /// Create an out-of-band error by copying the given string. + static WrapperFunctionResult createOutOfBandError(const std::string &Msg) { + return createOutOfBandError(Msg.c_str()); + } + /// If this value is an out-of-band error then this returns the error message, /// otherwise returns nullptr. const char *getOutOfBandError() const { @@ -136,537 +113,21 @@ private: __orc_rt_CWrapperFunctionResult R; }; -/// Output char buffer with overflow check. -class SPSOutputBuffer { -public: - SPSOutputBuffer(char *Buffer, size_t Remaining) - : Buffer(Buffer), Remaining(Remaining) {} - bool write(const char *Data, size_t Size) { - if (Size > Remaining) - return false; - memcpy(Buffer, Data, Size); - Buffer += Size; - Remaining -= Size; - return true; - } - -private: - char *Buffer = nullptr; - size_t Remaining = 0; -}; - -/// Input char buffer with underflow check. -class SPSInputBuffer { -public: - SPSInputBuffer() = default; - SPSInputBuffer(const char *Buffer, size_t Remaining) - : Buffer(Buffer), Remaining(Remaining) {} - bool read(char *Data, size_t Size) { - if (Size > Remaining) - return false; - memcpy(Data, Buffer, Size); - Buffer += Size; - Remaining -= Size; - return true; - } - - const char *data() const { return Buffer; } - bool skip(size_t Size) { - if (Size > Remaining) - return false; - Remaining -= Size; - return true; - } - -private: - const char *Buffer = nullptr; - size_t Remaining = 0; -}; - -/// Specialize to describe how to serialize/deserialize to/from the given -/// concrete type. -template -class SPSSerializationTraits; - -/// A utility class for serializing to a blob from a variadic list. -template class SPSArgList; - -// Empty list specialization for SPSArgList. -template <> class SPSArgList<> { -public: - static size_t size() { return 0; } - - static bool serialize(SPSOutputBuffer &OB) { return true; } - static bool deserialize(SPSInputBuffer &IB) { return true; } - - static bool toWrapperFunctionResult(WrapperFunctionResult &R) { - R = WrapperFunctionResult(); - return true; - } -}; - -// Non-empty list specialization for SPSArgList. -template -class SPSArgList { -public: - template - static size_t size(const ArgT &Arg, const ArgTs &...Args) { - return SPSSerializationTraits::size(Arg) + - SPSArgList::size(Args...); - } - - template - static bool serialize(SPSOutputBuffer &OB, const ArgT &Arg, - const ArgTs &...Args) { - return SPSSerializationTraits::serialize(OB, Arg) && - SPSArgList::serialize(OB, Args...); - } - - template - static bool deserialize(SPSInputBuffer &IB, ArgT &Arg, ArgTs &...Args) { - return SPSSerializationTraits::deserialize(IB, Arg) && - SPSArgList::deserialize(IB, Args...); - } - - template - static bool toWrapperFunctionResult(WrapperFunctionResult &R, - const ArgTs &...Args) { - WrapperFunctionResult TR; - char *DataPtr = WrapperFunctionResult::allocate(TR, size(Args...)); - - SPSOutputBuffer OB(DataPtr, TR.size()); - if (!serialize(OB, Args...)) - return false; - - R = std::move(TR); - return true; - } - - template - static bool fromBuffer(const char *Data, size_t Size, ArgTs &...Args) { - SPSInputBuffer IB(Data, Size); - return deserialize(IB, Args...); - } -}; - -/// SPS serialization for integral types, bool, and char. -template -class SPSSerializationTraits< - SPSTagT, SPSTagT, - std::enable_if_t::value || - std::is_same::value || - std::is_same::value || - std::is_same::value || - std::is_same::value || - std::is_same::value || - std::is_same::value || - std::is_same::value || - std::is_same::value || - std::is_same::value>> { -public: - static size_t size(const SPSTagT &Value) { return sizeof(SPSTagT); } - - static bool serialize(SPSOutputBuffer &OB, const SPSTagT &Value) { - SPSTagT Tmp = Value; - if (IsBigEndianHost) - swapByteOrder(Tmp); - return OB.write(reinterpret_cast(&Tmp), sizeof(Tmp)); - } - - static bool deserialize(SPSInputBuffer &IB, SPSTagT &Value) { - SPSTagT Tmp; - if (!IB.read(reinterpret_cast(&Tmp), sizeof(Tmp))) - return false; - if (IsBigEndianHost) - swapByteOrder(Tmp); - Value = Tmp; - return true; - } -}; - -// Any empty placeholder suitable as a substitute for void when deserializing -class SPSEmpty {}; - -/// SPS tag type for target addresses. -/// -/// SPSTagTargetAddresses should be serialized as a uint64_t value. -class SPSTagTargetAddress; - -template <> -class SPSSerializationTraits - : public SPSSerializationTraits {}; - -/// SPS tag type for tuples. -/// -/// A blob tuple should be serialized by serializing each of the elements in -/// sequence. -template class SPSTuple { -public: - /// Convenience typedef of the corresponding arg list. - typedef SPSArgList AsArgList; -}; - -/// SPS tag type for sequences. -/// -/// SPSSequences should be serialized as a uint64_t sequence length, -/// followed by the serialization of each of the elements. -template class SPSSequence; - -/// SPS tag type for strings, which are equivalent to sequences of chars. -using SPSString = SPSSequence; - -/// SPS tag type for maps. -/// -/// SPS maps are just sequences of (Key, Value) tuples. -template -using SPSMap = SPSSequence>; - -/// Serialization for SPSEmpty type. -template <> class SPSSerializationTraits { -public: - static size_t size(const SPSEmpty &EP) { return 0; } - static bool serialize(SPSOutputBuffer &OB, const SPSEmpty &BE) { - return true; - } - static bool deserialize(SPSInputBuffer &IB, SPSEmpty &BE) { return true; } -}; - -/// Specialize this to implement 'trivial' sequence serialization for -/// a concrete sequence type. -/// -/// Trivial sequence serialization uses the sequence's 'size' member to get the -/// length of the sequence, and uses a range-based for loop to iterate over the -/// elements. -/// -/// Specializing this template class means that you do not need to provide a -/// specialization of SPSSerializationTraits for your type. -template -class TrivialSPSSequenceSerialization { -public: - static constexpr bool available = false; -}; - -/// Specialize this to implement 'trivial' sequence deserialization for -/// a concrete sequence type. -/// -/// Trivial deserialization calls a static 'reserve(SequenceT&)' method on your -/// specialization (you must implement this) to reserve space, and then calls -/// a static 'append(SequenceT&, ElementT&) method to append each of the -/// deserialized elements. -/// -/// Specializing this template class means that you do not need to provide a -/// specialization of SPSSerializationTraits for your type. -template -class TrivialSPSSequenceDeserialization { -public: - static constexpr bool available = false; -}; - -/// Trivial std::string -> SPSSequence serialization. -template <> class TrivialSPSSequenceSerialization { -public: - static constexpr bool available = true; -}; - -/// Trivial SPSSequence -> std::string deserialization. -template <> class TrivialSPSSequenceDeserialization { -public: - static constexpr bool available = true; - - using element_type = char; - - static void reserve(std::string &S, uint64_t Size) { S.reserve(Size); } - static bool append(std::string &S, char C) { - S.push_back(C); - return true; - } -}; - -/// Trivial std::vector -> SPSSequence serialization. -template -class TrivialSPSSequenceSerialization> { -public: - static constexpr bool available = true; -}; - -/// Trivial SPSSequence -> std::vector deserialization. -template -class TrivialSPSSequenceDeserialization> { -public: - static constexpr bool available = true; - - using element_type = typename std::vector::value_type; - - static void reserve(std::vector &V, uint64_t Size) { V.reserve(Size); } - static bool append(std::vector &V, T E) { - V.push_back(std::move(E)); - return true; - } -}; - -/// 'Trivial' sequence serialization: Sequence is serialized as a uint64_t size -/// followed by a for-earch loop over the elements of the sequence to serialize -/// each of them. -template -class SPSSerializationTraits, SequenceT, - std::enable_if_t::available>> { -public: - static size_t size(const SequenceT &S) { - size_t Size = SPSArgList::size(static_cast(S.size())); - for (const auto &E : S) - Size += SPSArgList::size(E); - return Size; - } - - static bool serialize(SPSOutputBuffer &OB, const SequenceT &S) { - if (!SPSArgList::serialize(OB, static_cast(S.size()))) - return false; - for (const auto &E : S) - if (!SPSArgList::serialize(OB, E)) - return false; - return true; - } - - static bool deserialize(SPSInputBuffer &IB, SequenceT &S) { - using TBSD = TrivialSPSSequenceDeserialization; - uint64_t Size; - if (!SPSArgList::deserialize(IB, Size)) - return false; - TBSD::reserve(S, Size); - for (size_t I = 0; I != Size; ++I) { - typename TBSD::element_type E; - if (!SPSArgList::deserialize(IB, E)) - return false; - if (!TBSD::append(S, std::move(E))) - return false; - } - return true; - } -}; - -/// SPSTuple serialization for std::pair. -template -class SPSSerializationTraits, std::pair> { -public: - static size_t size(const std::pair &P) { - return SPSArgList::size(P.first) + - SPSArgList::size(P.second); - } - - static bool serialize(SPSOutputBuffer &OB, const std::pair &P) { - return SPSArgList::serialize(OB, P.first) && - SPSArgList::serialize(OB, P.second); - } - - static bool deserialize(SPSInputBuffer &IB, std::pair &P) { - return SPSArgList::deserialize(IB, P.first) && - SPSArgList::deserialize(IB, P.second); - } -}; - -/// Serialization for string_views. -/// -/// Serialization is as for regular strings. Deserialization points directly -/// into the blob. -template <> class SPSSerializationTraits { -public: - static size_t size(const __orc_rt::string_view &S) { - return SPSArgList::size(static_cast(S.size())) + - S.size(); - } - - static bool serialize(SPSOutputBuffer &OB, const __orc_rt::string_view &S) { - if (!SPSArgList::serialize(OB, static_cast(S.size()))) - return false; - return OB.write(S.data(), S.size()); - } - - static bool deserialize(SPSInputBuffer &IB, __orc_rt::string_view &S) { - const char *Data = nullptr; - uint64_t Size; - if (!SPSArgList::deserialize(IB, Size)) - return false; - Data = IB.data(); - if (!IB.skip(Size)) - return false; - S = {Data, Size}; - return true; - } -}; - -/// SPS tag type for errors. -class SPSError; - -/// SPS tag type for expecteds, which are either a T or a string representing -/// an error. -template class SPSExpected; - namespace detail { -/// Helper type for serializing Errors. -/// -/// llvm::Errors are move-only, and not inspectable except by consuming them. -/// This makes them unsuitable for direct serialization via -/// SPSSerializationTraits, which needs to inspect values twice (once to -/// determine the amount of space to reserve, and then again to serialize). -/// -/// The WrapperFunctionSerializableError type is a helper that can be -/// constructed from an llvm::Error, but inspected more than once. -struct SPSSerializableError { - bool HasError = false; - std::string ErrMsg; -}; - -/// Helper type for serializing Expecteds. -/// -/// See SPSSerializableError for more details. -/// -// FIXME: Use std::variant for storage once we have c++17. -template struct SPSSerializableExpected { - bool HasValue = false; - T Value{}; - std::string ErrMsg; -}; - -inline SPSSerializableError toSPSSerializable(Error Err) { - if (Err) - return {true, toString(std::move(Err))}; - return {false, {}}; +template +Expected +serializeViaSPSToWrapperFunctionResult(const ArgTs &...Args) { + WrapperFunctionResult Result; + char *DataPtr = + WrapperFunctionResult::allocate(Result, SPSArgListT::size(Args...)); + SPSOutputBuffer OB(DataPtr, Result.size()); + if (!SPSArgListT::serialize(OB, Args...)) + return make_error( + "Error serializing arguments to blob in call"); + return Result; } -inline Error fromSPSSerializable(SPSSerializableError BSE) { - if (BSE.HasError) - return make_error(BSE.ErrMsg); - return Error::success(); -} - -template -SPSSerializableExpected toSPSSerializable(Expected E) { - if (E) - return {true, std::move(*E), {}}; - else - return {false, {}, toString(E.takeError())}; -} - -template -Expected fromSPSSerializable(SPSSerializableExpected BSE) { - if (BSE.HasValue) - return std::move(BSE.Value); - else - return make_error(BSE.ErrMsg); -} - -} // end namespace detail - -/// Serialize to a SPSError from a detail::SPSSerializableError. -template <> -class SPSSerializationTraits { -public: - static size_t size(const detail::SPSSerializableError &BSE) { - size_t Size = SPSArgList::size(BSE.HasError); - if (BSE.HasError) - Size += SPSArgList::size(BSE.ErrMsg); - return Size; - } - - static bool serialize(SPSOutputBuffer &OB, - const detail::SPSSerializableError &BSE) { - if (!SPSArgList::serialize(OB, BSE.HasError)) - return false; - if (BSE.HasError) - if (!SPSArgList::serialize(OB, BSE.ErrMsg)) - return false; - return true; - } - - static bool deserialize(SPSInputBuffer &IB, - detail::SPSSerializableError &BSE) { - if (!SPSArgList::deserialize(IB, BSE.HasError)) - return false; - - if (!BSE.HasError) - return true; - - return SPSArgList::deserialize(IB, BSE.ErrMsg); - } -}; - -/// Serialize to a SPSExpected from a -/// detail::SPSSerializableExpected. -template -class SPSSerializationTraits, - detail::SPSSerializableExpected> { -public: - static size_t size(const detail::SPSSerializableExpected &BSE) { - size_t Size = SPSArgList::size(BSE.HasValue); - if (BSE.HasValue) - Size += SPSArgList::size(BSE.Value); - else - Size += SPSArgList::size(BSE.ErrMsg); - return Size; - } - - static bool serialize(SPSOutputBuffer &OB, - const detail::SPSSerializableExpected &BSE) { - if (!SPSArgList::serialize(OB, BSE.HasValue)) - return false; - - if (BSE.HasValue) - return SPSArgList::serialize(OB, BSE.Value); - - return SPSArgList::serialize(OB, BSE.ErrMsg); - } - - static bool deserialize(SPSInputBuffer &IB, - detail::SPSSerializableExpected &BSE) { - if (!SPSArgList::deserialize(IB, BSE.HasValue)) - return false; - - if (BSE.HasValue) - return SPSArgList::deserialize(IB, BSE.Value); - - return SPSArgList::deserialize(IB, BSE.ErrMsg); - } -}; - -/// Serialize to a SPSExpected from a detail::SPSSerializableError. -template -class SPSSerializationTraits, - detail::SPSSerializableError> { -public: - static size_t size(const detail::SPSSerializableError &BSE) { - assert(BSE.HasError && "Cannot serialize expected from a success value"); - return SPSArgList::size(false) + - SPSArgList::size(BSE.ErrMsg); - } - - static bool serialize(SPSOutputBuffer &OB, - const detail::SPSSerializableError &BSE) { - assert(BSE.HasError && "Cannot serialize expected from a success value"); - if (!SPSArgList::serialize(OB, false)) - return false; - return SPSArgList::serialize(OB, BSE.ErrMsg); - } -}; - -/// Serialize to a SPSExpected from a T. -template -class SPSSerializationTraits, T> { -public: - static size_t size(const T &Value) { - return SPSArgList::size(true) + SPSArgList::size(Value); - } - - static bool serialize(SPSOutputBuffer &OB, const T &Value) { - if (!SPSArgList::serialize(OB, true)) - return false; - return SPSArgList::serialize(Value); - } -}; - -namespace detail { - template class ResultSerializer, typename... SPSTagTs> class WrapperFunctionHandlerHelper @@ -690,8 +151,12 @@ public: return WrapperFunctionResult::createOutOfBandError( "Could not deserialize arguments for wrapper function call"); - return ResultSerializer::serialize( - call(std::forward(H), Args, ArgIndices{})); + if (auto Result = ResultSerializer::serialize( + call(std::forward(H), Args, ArgIndices{}))) + return std::move(*Result); + else + return WrapperFunctionResult::createOutOfBandError( + toString(Result.takeError())); } private: @@ -735,37 +200,26 @@ class WrapperFunctionHandlerHelper class ResultSerializer { public: - static WrapperFunctionResult serialize(RetT Result) { - WrapperFunctionResult R; - if (!SPSArgList::toWrapperFunctionResult(R, Result)) - return WrapperFunctionResult::createOutOfBandError( - "Could not serialize return value from wrapper function"); - return R; + static Expected serialize(RetT Result) { + return serializeViaSPSToWrapperFunctionResult>( + Result); } }; template class ResultSerializer { public: - static WrapperFunctionResult serialize(Error Err) { - WrapperFunctionResult R; - if (!SPSArgList::toWrapperFunctionResult( - R, toSPSSerializable(std::move(Err)))) - return WrapperFunctionResult::createOutOfBandError( - "Could not serialize return value from wrapper function"); - return R; + static Expected serialize(Error Err) { + return serializeViaSPSToWrapperFunctionResult>( + toSPSSerializable(std::move(Err))); } }; template class ResultSerializer> { public: - static WrapperFunctionResult serialize(Expected E) { - WrapperFunctionResult R; - if (!SPSArgList::toWrapperFunctionResult( - R, toSPSSerializable(std::move(E)))) - return WrapperFunctionResult::createOutOfBandError( - "Could not serialize return value from wrapper function"); - return R; + static Expected serialize(Expected E) { + return serializeViaSPSToWrapperFunctionResult>( + toSPSSerializable(std::move(E))); } }; @@ -838,12 +292,15 @@ public: if (ORC_RT_UNLIKELY(!&__orc_rt_jit_dispatch_ctx)) return make_error("__orc_jtjit_dispatch not set"); - WrapperFunctionResult ArgBuffer; - if (!SPSArgList::toWrapperFunctionResult(ArgBuffer, Args...)) - return make_error( - "Error serializing arguments to blob in call"); - WrapperFunctionResult ResultBuffer = __orc_rt_jit_dispatch( - &__orc_rt_jit_dispatch_ctx, FnTag, ArgBuffer.data(), ArgBuffer.size()); + auto ArgBuffer = + detail::serializeViaSPSToWrapperFunctionResult>( + Args...); + if (!ArgBuffer) + return ArgBuffer.takeError(); + + WrapperFunctionResult ResultBuffer = + __orc_rt_jit_dispatch(&__orc_rt_jit_dispatch_ctx, FnTag, + ArgBuffer->data(), ArgBuffer->size()); if (auto ErrMsg = ResultBuffer.getOutOfBandError()) return make_error(ErrMsg);