[compiler-rt] 49f4a58 - [ORC-RT] Split Simple-Packed-Serialization code into its own header.

Lang Hames via llvm-commits llvm-commits at lists.llvm.org
Sat Jun 12 18:07:37 PDT 2021


Author: Lang Hames
Date: 2021-06-13T10:17:13+10:00
New Revision: 49f4a58d53c72abee6da3443028fff6bb2d8fe35

URL: https://github.com/llvm/llvm-project/commit/49f4a58d53c72abee6da3443028fff6bb2d8fe35
DIFF: https://github.com/llvm/llvm-project/commit/49f4a58d53c72abee6da3443028fff6bb2d8fe35.diff

LOG: [ORC-RT] Split Simple-Packed-Serialization code into its own header.

This will simplify integration of this code into LLVM -- The
Simple-Packed-Serialization code can be copied near-verbatim, but
WrapperFunctionResult will require more adaptation.

Added: 
    compiler-rt/lib/orc/simple_packed_serialization.h
    compiler-rt/lib/orc/unittests/simple_packed_serialization_test.cpp

Modified: 
    compiler-rt/lib/orc/CMakeLists.txt
    compiler-rt/lib/orc/common.h
    compiler-rt/lib/orc/unittests/CMakeLists.txt
    compiler-rt/lib/orc/unittests/wrapper_function_utils_test.cpp
    compiler-rt/lib/orc/wrapper_function_utils.h

Removed: 
    


################################################################################
diff  --git a/compiler-rt/lib/orc/CMakeLists.txt b/compiler-rt/lib/orc/CMakeLists.txt
index a94a68a724368..417033a206216 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 bc64366a477a7..62ff12a365e61 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 <type_traits>
 
 /// 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 0000000000000..a6e90f506d7ce
--- /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<T>:
+//   Serialized as the sequence length (as a uint64_t) followed by the
+//   serialization of each of the elements without padding.
+//
+// Tuple<T1, ..., TN>:
+//   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 <string>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+#include <vector>
+
+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 <typename SPSTagT, typename ConcreteT, typename _ = void>
+class SPSSerializationTraits;
+
+/// A utility class for serializing to a blob from a variadic list.
+template <typename... ArgTs> 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 <typename SPSTagT, typename... SPSTagTs>
+class SPSArgList<SPSTagT, SPSTagTs...> {
+public:
+  template <typename ArgT, typename... ArgTs>
+  static size_t size(const ArgT &Arg, const ArgTs &...Args) {
+    return SPSSerializationTraits<SPSTagT, ArgT>::size(Arg) +
+           SPSArgList<SPSTagTs...>::size(Args...);
+  }
+
+  template <typename ArgT, typename... ArgTs>
+  static bool serialize(SPSOutputBuffer &OB, const ArgT &Arg,
+                        const ArgTs &...Args) {
+    return SPSSerializationTraits<SPSTagT, ArgT>::serialize(OB, Arg) &&
+           SPSArgList<SPSTagTs...>::serialize(OB, Args...);
+  }
+
+  template <typename ArgT, typename... ArgTs>
+  static bool deserialize(SPSInputBuffer &IB, ArgT &Arg, ArgTs &...Args) {
+    return SPSSerializationTraits<SPSTagT, ArgT>::deserialize(IB, Arg) &&
+           SPSArgList<SPSTagTs...>::deserialize(IB, Args...);
+  }
+};
+
+/// SPS serialization for integral types, bool, and char.
+template <typename SPSTagT>
+class SPSSerializationTraits<
+    SPSTagT, SPSTagT,
+    std::enable_if_t<std::is_same<SPSTagT, bool>::value ||
+                     std::is_same<SPSTagT, char>::value ||
+                     std::is_same<SPSTagT, int8_t>::value ||
+                     std::is_same<SPSTagT, int16_t>::value ||
+                     std::is_same<SPSTagT, int32_t>::value ||
+                     std::is_same<SPSTagT, int64_t>::value ||
+                     std::is_same<SPSTagT, uint8_t>::value ||
+                     std::is_same<SPSTagT, uint16_t>::value ||
+                     std::is_same<SPSTagT, uint32_t>::value ||
+                     std::is_same<SPSTagT, uint64_t>::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<const char *>(&Tmp), sizeof(Tmp));
+  }
+
+  static bool deserialize(SPSInputBuffer &IB, SPSTagT &Value) {
+    SPSTagT Tmp;
+    if (!IB.read(reinterpret_cast<char *>(&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<SPSTagTargetAddress, uint64_t>
+    : public SPSSerializationTraits<uint64_t, uint64_t> {};
+
+/// SPS tag type for tuples.
+///
+/// A blob tuple should be serialized by serializing each of the elements in
+/// sequence.
+template <typename... SPSTagTs> class SPSTuple {
+public:
+  /// Convenience typedef of the corresponding arg list.
+  typedef SPSArgList<SPSTagTs...> 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 <typename SPSElementTagT> class SPSSequence;
+
+/// SPS tag type for strings, which are equivalent to sequences of chars.
+using SPSString = SPSSequence<char>;
+
+/// SPS tag type for maps.
+///
+/// SPS maps are just sequences of (Key, Value) tuples.
+template <typename SPSTagT1, typename SPSTagT2>
+using SPSMap = SPSSequence<SPSTuple<SPSTagT1, SPSTagT2>>;
+
+/// Serialization for SPSEmpty type.
+template <> class SPSSerializationTraits<SPSEmpty, SPSEmpty> {
+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 <typename SPSElementTagT, typename ConcreteSequenceT>
+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 <typename SPSElementTagT, typename ConcreteSequenceT>
+class TrivialSPSSequenceDeserialization {
+public:
+  static constexpr bool available = false;
+};
+
+/// Trivial std::string -> SPSSequence<char> serialization.
+template <> class TrivialSPSSequenceSerialization<char, std::string> {
+public:
+  static constexpr bool available = true;
+};
+
+/// Trivial SPSSequence<char> -> std::string deserialization.
+template <> class TrivialSPSSequenceDeserialization<char, std::string> {
+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<T> -> SPSSequence<SPSElementTagT> serialization.
+template <typename SPSElementTagT, typename T>
+class TrivialSPSSequenceSerialization<SPSElementTagT, std::vector<T>> {
+public:
+  static constexpr bool available = true;
+};
+
+/// Trivial SPSSequence<SPSElementTagT> -> std::vector<T> deserialization.
+template <typename SPSElementTagT, typename T>
+class TrivialSPSSequenceDeserialization<SPSElementTagT, std::vector<T>> {
+public:
+  static constexpr bool available = true;
+
+  using element_type = typename std::vector<T>::value_type;
+
+  static void reserve(std::vector<T> &V, uint64_t Size) { V.reserve(Size); }
+  static bool append(std::vector<T> &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 <typename SPSElementTagT, typename SequenceT>
+class SPSSerializationTraits<SPSSequence<SPSElementTagT>, SequenceT,
+                             std::enable_if_t<TrivialSPSSequenceSerialization<
+                                 SPSElementTagT, SequenceT>::available>> {
+public:
+  static size_t size(const SequenceT &S) {
+    size_t Size = SPSArgList<uint64_t>::size(static_cast<uint64_t>(S.size()));
+    for (const auto &E : S)
+      Size += SPSArgList<SPSElementTagT>::size(E);
+    return Size;
+  }
+
+  static bool serialize(SPSOutputBuffer &OB, const SequenceT &S) {
+    if (!SPSArgList<uint64_t>::serialize(OB, static_cast<uint64_t>(S.size())))
+      return false;
+    for (const auto &E : S)
+      if (!SPSArgList<SPSElementTagT>::serialize(OB, E))
+        return false;
+    return true;
+  }
+
+  static bool deserialize(SPSInputBuffer &IB, SequenceT &S) {
+    using TBSD = TrivialSPSSequenceDeserialization<SPSElementTagT, SequenceT>;
+    uint64_t Size;
+    if (!SPSArgList<uint64_t>::deserialize(IB, Size))
+      return false;
+    TBSD::reserve(S, Size);
+    for (size_t I = 0; I != Size; ++I) {
+      typename TBSD::element_type E;
+      if (!SPSArgList<SPSElementTagT>::deserialize(IB, E))
+        return false;
+      if (!TBSD::append(S, std::move(E)))
+        return false;
+    }
+    return true;
+  }
+};
+
+/// SPSTuple serialization for std::pair.
+template <typename SPSTagT1, typename SPSTagT2, typename T1, typename T2>
+class SPSSerializationTraits<SPSTuple<SPSTagT1, SPSTagT2>, std::pair<T1, T2>> {
+public:
+  static size_t size(const std::pair<T1, T2> &P) {
+    return SPSArgList<SPSTagT1>::size(P.first) +
+           SPSArgList<SPSTagT2>::size(P.second);
+  }
+
+  static bool serialize(SPSOutputBuffer &OB, const std::pair<T1, T2> &P) {
+    return SPSArgList<SPSTagT1>::serialize(OB, P.first) &&
+           SPSArgList<SPSTagT2>::serialize(OB, P.second);
+  }
+
+  static bool deserialize(SPSInputBuffer &IB, std::pair<T1, T2> &P) {
+    return SPSArgList<SPSTagT1>::deserialize(IB, P.first) &&
+           SPSArgList<SPSTagT2>::deserialize(IB, P.second);
+  }
+};
+
+/// Serialization for string_views.
+///
+/// Serialization is as for regular strings. Deserialization points directly
+/// into the blob.
+template <> class SPSSerializationTraits<SPSString, __orc_rt::string_view> {
+public:
+  static size_t size(const __orc_rt::string_view &S) {
+    return SPSArgList<uint64_t>::size(static_cast<uint64_t>(S.size())) +
+           S.size();
+  }
+
+  static bool serialize(SPSOutputBuffer &OB, const __orc_rt::string_view &S) {
+    if (!SPSArgList<uint64_t>::serialize(OB, static_cast<uint64_t>(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<uint64_t>::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 <typename SPSTagT> 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 Expected<T>s.
+///
+/// See SPSSerializableError for more details.
+///
+// FIXME: Use std::variant for storage once we have c++17.
+template <typename T> 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<StringError>(BSE.ErrMsg);
+  return Error::success();
+}
+
+template <typename T>
+SPSSerializableExpected<T> toSPSSerializable(Expected<T> E) {
+  if (E)
+    return {true, std::move(*E), {}};
+  else
+    return {false, {}, toString(E.takeError())};
+}
+
+template <typename T>
+Expected<T> fromSPSSerializable(SPSSerializableExpected<T> BSE) {
+  if (BSE.HasValue)
+    return std::move(BSE.Value);
+  else
+    return make_error<StringError>(BSE.ErrMsg);
+}
+
+} // end namespace detail
+
+/// Serialize to a SPSError from a detail::SPSSerializableError.
+template <>
+class SPSSerializationTraits<SPSError, detail::SPSSerializableError> {
+public:
+  static size_t size(const detail::SPSSerializableError &BSE) {
+    size_t Size = SPSArgList<bool>::size(BSE.HasError);
+    if (BSE.HasError)
+      Size += SPSArgList<SPSString>::size(BSE.ErrMsg);
+    return Size;
+  }
+
+  static bool serialize(SPSOutputBuffer &OB,
+                        const detail::SPSSerializableError &BSE) {
+    if (!SPSArgList<bool>::serialize(OB, BSE.HasError))
+      return false;
+    if (BSE.HasError)
+      if (!SPSArgList<SPSString>::serialize(OB, BSE.ErrMsg))
+        return false;
+    return true;
+  }
+
+  static bool deserialize(SPSInputBuffer &IB,
+                          detail::SPSSerializableError &BSE) {
+    if (!SPSArgList<bool>::deserialize(IB, BSE.HasError))
+      return false;
+
+    if (!BSE.HasError)
+      return true;
+
+    return SPSArgList<SPSString>::deserialize(IB, BSE.ErrMsg);
+  }
+};
+
+/// Serialize to a SPSExpected<SPSTagT> from a
+/// detail::SPSSerializableExpected<T>.
+template <typename SPSTagT, typename T>
+class SPSSerializationTraits<SPSExpected<SPSTagT>,
+                             detail::SPSSerializableExpected<T>> {
+public:
+  static size_t size(const detail::SPSSerializableExpected<T> &BSE) {
+    size_t Size = SPSArgList<bool>::size(BSE.HasValue);
+    if (BSE.HasValue)
+      Size += SPSArgList<SPSTagT>::size(BSE.Value);
+    else
+      Size += SPSArgList<SPSString>::size(BSE.ErrMsg);
+    return Size;
+  }
+
+  static bool serialize(SPSOutputBuffer &OB,
+                        const detail::SPSSerializableExpected<T> &BSE) {
+    if (!SPSArgList<bool>::serialize(OB, BSE.HasValue))
+      return false;
+
+    if (BSE.HasValue)
+      return SPSArgList<SPSTagT>::serialize(OB, BSE.Value);
+
+    return SPSArgList<SPSString>::serialize(OB, BSE.ErrMsg);
+  }
+
+  static bool deserialize(SPSInputBuffer &IB,
+                          detail::SPSSerializableExpected<T> &BSE) {
+    if (!SPSArgList<bool>::deserialize(IB, BSE.HasValue))
+      return false;
+
+    if (BSE.HasValue)
+      return SPSArgList<SPSTagT>::deserialize(IB, BSE.Value);
+
+    return SPSArgList<SPSString>::deserialize(IB, BSE.ErrMsg);
+  }
+};
+
+/// Serialize to a SPSExpected<SPSTagT> from a detail::SPSSerializableError.
+template <typename SPSTagT>
+class SPSSerializationTraits<SPSExpected<SPSTagT>,
+                             detail::SPSSerializableError> {
+public:
+  static size_t size(const detail::SPSSerializableError &BSE) {
+    assert(BSE.HasError && "Cannot serialize expected from a success value");
+    return SPSArgList<bool>::size(false) +
+           SPSArgList<SPSString>::size(BSE.ErrMsg);
+  }
+
+  static bool serialize(SPSOutputBuffer &OB,
+                        const detail::SPSSerializableError &BSE) {
+    assert(BSE.HasError && "Cannot serialize expected from a success value");
+    if (!SPSArgList<bool>::serialize(OB, false))
+      return false;
+    return SPSArgList<SPSString>::serialize(OB, BSE.ErrMsg);
+  }
+};
+
+/// Serialize to a SPSExpected<SPSTagT> from a T.
+template <typename SPSTagT, typename T>
+class SPSSerializationTraits<SPSExpected<SPSTagT>, T> {
+public:
+  static size_t size(const T &Value) {
+    return SPSArgList<bool>::size(true) + SPSArgList<SPSTagT>::size(Value);
+  }
+
+  static bool serialize(SPSOutputBuffer &OB, const T &Value) {
+    if (!SPSArgList<bool>::serialize(OB, true))
+      return false;
+    return SPSArgList<SPSTagT>::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 0c84cd594451a..fbbf7faec1e9e 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 0000000000000..2e6d43d1fb9ab
--- /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 <stdio.h>
+
+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 <typename SPSTagT, typename T>
+static void blobSerializationRoundTrip(const T &Value) {
+  using BST = SPSSerializationTraits<SPSTagT, T>;
+
+  size_t Size = BST::size(Value);
+  auto Buffer = std::make_unique<char[]>(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 <typename T> static void testFixedIntegralTypeSerialization() {
+  blobSerializationRoundTrip<T, T>(0);
+  blobSerializationRoundTrip<T, T>(static_cast<T>(1));
+  if (std::is_signed<T>::value) {
+    blobSerializationRoundTrip<T, T>(static_cast<T>(-1));
+    blobSerializationRoundTrip<T, T>(std::numeric_limits<T>::min());
+  }
+  blobSerializationRoundTrip<T, T>(std::numeric_limits<T>::max());
+}
+
+TEST(SimplePackedSerializationTest, BoolSerialization) {
+  blobSerializationRoundTrip<bool, bool>(true);
+  blobSerializationRoundTrip<bool, bool>(false);
+}
+
+TEST(SimplePackedSerializationTest, CharSerialization) {
+  blobSerializationRoundTrip<char, char>((char)0x00);
+  blobSerializationRoundTrip<char, char>((char)0xAA);
+  blobSerializationRoundTrip<char, char>((char)0xFF);
+}
+
+TEST(SimplePackedSerializationTest, Int8Serialization) {
+  testFixedIntegralTypeSerialization<int8_t>();
+}
+
+TEST(SimplePackedSerializationTest, UInt8Serialization) {
+  testFixedIntegralTypeSerialization<uint8_t>();
+}
+
+TEST(SimplePackedSerializationTest, Int16Serialization) {
+  testFixedIntegralTypeSerialization<int16_t>();
+}
+
+TEST(SimplePackedSerializationTest, UInt16Serialization) {
+  testFixedIntegralTypeSerialization<uint16_t>();
+}
+
+TEST(SimplePackedSerializationTest, Int32Serialization) {
+  testFixedIntegralTypeSerialization<int32_t>();
+}
+
+TEST(SimplePackedSerializationTest, UInt32Serialization) {
+  testFixedIntegralTypeSerialization<uint32_t>();
+}
+
+TEST(SimplePackedSerializationTest, Int64Serialization) {
+  testFixedIntegralTypeSerialization<int64_t>();
+}
+
+TEST(SimplePackedSerializationTest, UInt64Serialization) {
+  testFixedIntegralTypeSerialization<uint64_t>();
+}
+
+TEST(SimplePackedSerializationTest, SequenceSerialization) {
+  std::vector<int32_t> V({1, 2, -47, 139});
+  blobSerializationRoundTrip<SPSSequence<int32_t>, std::vector<int32_t>>(V);
+}
+
+TEST(SimplePackedSerializationTest, StringViewCharSequenceSerialization) {
+  const char *HW = "Hello, world!";
+  blobSerializationRoundTrip<SPSString, string_view>(string_view(HW));
+}
+
+TEST(SimplePackedSerializationTest, StdPairSerialization) {
+  std::pair<int32_t, std::string> P(42, "foo");
+  blobSerializationRoundTrip<SPSTuple<int32_t, SPSString>,
+                             std::pair<int32_t, std::string>>(P);
+}
+
+TEST(SimplePackedSerializationTest, ArgListSerialization) {
+  using BAL = SPSArgList<bool, int32_t, SPSString>;
+
+  bool Arg1 = true;
+  int32_t Arg2 = 42;
+  std::string Arg3 = "foo";
+
+  size_t Size = BAL::size(Arg1, Arg2, Arg3);
+  auto Buffer = std::make_unique<char[]>(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 936e5210eeefa..c1f6896ac05aa 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 <typename SPSTagT, typename T>
-static void blobSerializationRoundTrip(const T &Value) {
-  using BST = SPSSerializationTraits<SPSTagT, T>;
-
-  size_t Size = BST::size(Value);
-  auto Buffer = std::make_unique<char[]>(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 <typename T> static void testFixedIntegralTypeSerialization() {
-  blobSerializationRoundTrip<T, T>(0);
-  blobSerializationRoundTrip<T, T>(static_cast<T>(1));
-  if (std::is_signed<T>::value) {
-    blobSerializationRoundTrip<T, T>(static_cast<T>(-1));
-    blobSerializationRoundTrip<T, T>(std::numeric_limits<T>::min());
-  }
-  blobSerializationRoundTrip<T, T>(std::numeric_limits<T>::max());
-}
-
-TEST(WrapperFunctionUtilsTest, BoolSerialization) {
-  blobSerializationRoundTrip<bool, bool>(true);
-  blobSerializationRoundTrip<bool, bool>(false);
-}
-
-TEST(WrapperFunctionUtilsTest, CharSerialization) {
-  blobSerializationRoundTrip<char, char>((char)0x00);
-  blobSerializationRoundTrip<char, char>((char)0xAA);
-  blobSerializationRoundTrip<char, char>((char)0xFF);
-}
-
-TEST(WrapperFunctionUtilsTest, Int8Serialization) {
-  testFixedIntegralTypeSerialization<int8_t>();
-}
-
-TEST(WrapperFunctionUtilsTest, UInt8Serialization) {
-  testFixedIntegralTypeSerialization<uint8_t>();
-}
-
-TEST(WrapperFunctionUtilsTest, Int16Serialization) {
-  testFixedIntegralTypeSerialization<int16_t>();
-}
-
-TEST(WrapperFunctionUtilsTest, UInt16Serialization) {
-  testFixedIntegralTypeSerialization<uint16_t>();
-}
-
-TEST(WrapperFunctionUtilsTest, Int32Serialization) {
-  testFixedIntegralTypeSerialization<int32_t>();
-}
-
-TEST(WrapperFunctionUtilsTest, UInt32Serialization) {
-  testFixedIntegralTypeSerialization<uint32_t>();
-}
-
-TEST(WrapperFunctionUtilsTest, Int64Serialization) {
-  testFixedIntegralTypeSerialization<int64_t>();
-}
-
-TEST(WrapperFunctionUtilsTest, UInt64Serialization) {
-  testFixedIntegralTypeSerialization<uint64_t>();
-}
-
-TEST(WrapperFunctionUtilsTest, SequenceSerialization) {
-  std::vector<int32_t> V({1, 2, -47, 139});
-  blobSerializationRoundTrip<SPSSequence<int32_t>, std::vector<int32_t>>(V);
-}
-
-TEST(WrapperFunctionUtilsTest, StringViewCharSequenceSerialization) {
-  const char *HW = "Hello, world!";
-  blobSerializationRoundTrip<SPSString, string_view>(string_view(HW));
-}
-
-TEST(WrapperFunctionUtilsTest, StdPairSerialization) {
-  std::pair<int32_t, std::string> P(42, "foo");
-  blobSerializationRoundTrip<SPSTuple<int32_t, SPSString>,
-                             std::pair<int32_t, std::string>>(P);
-}
-
-TEST(WrapperFunctionUtilsTest, ArgListSerialization) {
-  using BAL = SPSArgList<bool, int32_t, SPSString>;
-
-  bool Arg1 = true;
-  int32_t Arg2 = 42;
-  std::string Arg3 = "foo";
-
-  size_t Size = BAL::size(Arg1, Arg2, Arg3);
-  auto Buffer = std::make_unique<char[]>(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<int32_t(int32_t, int32_t)>::handle(

diff  --git a/compiler-rt/lib/orc/wrapper_function_utils.h b/compiler-rt/lib/orc/wrapper_function_utils.h
index de82eaf3f7fe6..416429fe439b0 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<T>:
-//   Serialized as the sequence length (as a uint64_t) followed by the
-//   serialization of each of the elements without padding.
-//
-// Tuple<T1, ..., TN>:
-//   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 <string>
-#include <tuple>
+#include "simple_packed_serialization.h"
 #include <type_traits>
-#include <utility>
-#include <vector>
 
 namespace __orc_rt {
 
@@ -126,6 +98,11 @@ class WrapperFunctionResult {
     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 @@ class WrapperFunctionResult {
   __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 <typename SPSTagT, typename ConcreteT, typename _ = void>
-class SPSSerializationTraits;
-
-/// A utility class for serializing to a blob from a variadic list.
-template <typename... ArgTs> 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 <typename SPSTagT, typename... SPSTagTs>
-class SPSArgList<SPSTagT, SPSTagTs...> {
-public:
-  template <typename ArgT, typename... ArgTs>
-  static size_t size(const ArgT &Arg, const ArgTs &...Args) {
-    return SPSSerializationTraits<SPSTagT, ArgT>::size(Arg) +
-           SPSArgList<SPSTagTs...>::size(Args...);
-  }
-
-  template <typename ArgT, typename... ArgTs>
-  static bool serialize(SPSOutputBuffer &OB, const ArgT &Arg,
-                        const ArgTs &...Args) {
-    return SPSSerializationTraits<SPSTagT, ArgT>::serialize(OB, Arg) &&
-           SPSArgList<SPSTagTs...>::serialize(OB, Args...);
-  }
-
-  template <typename ArgT, typename... ArgTs>
-  static bool deserialize(SPSInputBuffer &IB, ArgT &Arg, ArgTs &...Args) {
-    return SPSSerializationTraits<SPSTagT, ArgT>::deserialize(IB, Arg) &&
-           SPSArgList<SPSTagTs...>::deserialize(IB, Args...);
-  }
-
-  template <typename... ArgTs>
-  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 <typename... ArgTs>
-  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 <typename SPSTagT>
-class SPSSerializationTraits<
-    SPSTagT, SPSTagT,
-    std::enable_if_t<std::is_same<SPSTagT, bool>::value ||
-                     std::is_same<SPSTagT, char>::value ||
-                     std::is_same<SPSTagT, int8_t>::value ||
-                     std::is_same<SPSTagT, int16_t>::value ||
-                     std::is_same<SPSTagT, int32_t>::value ||
-                     std::is_same<SPSTagT, int64_t>::value ||
-                     std::is_same<SPSTagT, uint8_t>::value ||
-                     std::is_same<SPSTagT, uint16_t>::value ||
-                     std::is_same<SPSTagT, uint32_t>::value ||
-                     std::is_same<SPSTagT, uint64_t>::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<const char *>(&Tmp), sizeof(Tmp));
-  }
-
-  static bool deserialize(SPSInputBuffer &IB, SPSTagT &Value) {
-    SPSTagT Tmp;
-    if (!IB.read(reinterpret_cast<char *>(&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<SPSTagTargetAddress, uint64_t>
-    : public SPSSerializationTraits<uint64_t, uint64_t> {};
-
-/// SPS tag type for tuples.
-///
-/// A blob tuple should be serialized by serializing each of the elements in
-/// sequence.
-template <typename... SPSTagTs> class SPSTuple {
-public:
-  /// Convenience typedef of the corresponding arg list.
-  typedef SPSArgList<SPSTagTs...> 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 <typename SPSElementTagT> class SPSSequence;
-
-/// SPS tag type for strings, which are equivalent to sequences of chars.
-using SPSString = SPSSequence<char>;
-
-/// SPS tag type for maps.
-///
-/// SPS maps are just sequences of (Key, Value) tuples.
-template <typename SPSTagT1, typename SPSTagT2>
-using SPSMap = SPSSequence<SPSTuple<SPSTagT1, SPSTagT2>>;
-
-/// Serialization for SPSEmpty type.
-template <> class SPSSerializationTraits<SPSEmpty, SPSEmpty> {
-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 <typename SPSElementTagT, typename ConcreteSequenceT>
-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 <typename SPSElementTagT, typename ConcreteSequenceT>
-class TrivialSPSSequenceDeserialization {
-public:
-  static constexpr bool available = false;
-};
-
-/// Trivial std::string -> SPSSequence<char> serialization.
-template <> class TrivialSPSSequenceSerialization<char, std::string> {
-public:
-  static constexpr bool available = true;
-};
-
-/// Trivial SPSSequence<char> -> std::string deserialization.
-template <> class TrivialSPSSequenceDeserialization<char, std::string> {
-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<T> -> SPSSequence<SPSElementTagT> serialization.
-template <typename SPSElementTagT, typename T>
-class TrivialSPSSequenceSerialization<SPSElementTagT, std::vector<T>> {
-public:
-  static constexpr bool available = true;
-};
-
-/// Trivial SPSSequence<SPSElementTagT> -> std::vector<T> deserialization.
-template <typename SPSElementTagT, typename T>
-class TrivialSPSSequenceDeserialization<SPSElementTagT, std::vector<T>> {
-public:
-  static constexpr bool available = true;
-
-  using element_type = typename std::vector<T>::value_type;
-
-  static void reserve(std::vector<T> &V, uint64_t Size) { V.reserve(Size); }
-  static bool append(std::vector<T> &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 <typename SPSElementTagT, typename SequenceT>
-class SPSSerializationTraits<SPSSequence<SPSElementTagT>, SequenceT,
-                             std::enable_if_t<TrivialSPSSequenceSerialization<
-                                 SPSElementTagT, SequenceT>::available>> {
-public:
-  static size_t size(const SequenceT &S) {
-    size_t Size = SPSArgList<uint64_t>::size(static_cast<uint64_t>(S.size()));
-    for (const auto &E : S)
-      Size += SPSArgList<SPSElementTagT>::size(E);
-    return Size;
-  }
-
-  static bool serialize(SPSOutputBuffer &OB, const SequenceT &S) {
-    if (!SPSArgList<uint64_t>::serialize(OB, static_cast<uint64_t>(S.size())))
-      return false;
-    for (const auto &E : S)
-      if (!SPSArgList<SPSElementTagT>::serialize(OB, E))
-        return false;
-    return true;
-  }
-
-  static bool deserialize(SPSInputBuffer &IB, SequenceT &S) {
-    using TBSD = TrivialSPSSequenceDeserialization<SPSElementTagT, SequenceT>;
-    uint64_t Size;
-    if (!SPSArgList<uint64_t>::deserialize(IB, Size))
-      return false;
-    TBSD::reserve(S, Size);
-    for (size_t I = 0; I != Size; ++I) {
-      typename TBSD::element_type E;
-      if (!SPSArgList<SPSElementTagT>::deserialize(IB, E))
-        return false;
-      if (!TBSD::append(S, std::move(E)))
-        return false;
-    }
-    return true;
-  }
-};
-
-/// SPSTuple serialization for std::pair.
-template <typename SPSTagT1, typename SPSTagT2, typename T1, typename T2>
-class SPSSerializationTraits<SPSTuple<SPSTagT1, SPSTagT2>, std::pair<T1, T2>> {
-public:
-  static size_t size(const std::pair<T1, T2> &P) {
-    return SPSArgList<SPSTagT1>::size(P.first) +
-           SPSArgList<SPSTagT2>::size(P.second);
-  }
-
-  static bool serialize(SPSOutputBuffer &OB, const std::pair<T1, T2> &P) {
-    return SPSArgList<SPSTagT1>::serialize(OB, P.first) &&
-           SPSArgList<SPSTagT2>::serialize(OB, P.second);
-  }
-
-  static bool deserialize(SPSInputBuffer &IB, std::pair<T1, T2> &P) {
-    return SPSArgList<SPSTagT1>::deserialize(IB, P.first) &&
-           SPSArgList<SPSTagT2>::deserialize(IB, P.second);
-  }
-};
-
-/// Serialization for string_views.
-///
-/// Serialization is as for regular strings. Deserialization points directly
-/// into the blob.
-template <> class SPSSerializationTraits<SPSString, __orc_rt::string_view> {
-public:
-  static size_t size(const __orc_rt::string_view &S) {
-    return SPSArgList<uint64_t>::size(static_cast<uint64_t>(S.size())) +
-           S.size();
-  }
-
-  static bool serialize(SPSOutputBuffer &OB, const __orc_rt::string_view &S) {
-    if (!SPSArgList<uint64_t>::serialize(OB, static_cast<uint64_t>(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<uint64_t>::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 <typename SPSTagT> 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 Expected<T>s.
-///
-/// See SPSSerializableError for more details.
-///
-// FIXME: Use std::variant for storage once we have c++17.
-template <typename T> 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 <typename SPSArgListT, typename... ArgTs>
+Expected<WrapperFunctionResult>
+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<StringError>(
+        "Error serializing arguments to blob in call");
+  return Result;
 }
 
-inline Error fromSPSSerializable(SPSSerializableError BSE) {
-  if (BSE.HasError)
-    return make_error<StringError>(BSE.ErrMsg);
-  return Error::success();
-}
-
-template <typename T>
-SPSSerializableExpected<T> toSPSSerializable(Expected<T> E) {
-  if (E)
-    return {true, std::move(*E), {}};
-  else
-    return {false, {}, toString(E.takeError())};
-}
-
-template <typename T>
-Expected<T> fromSPSSerializable(SPSSerializableExpected<T> BSE) {
-  if (BSE.HasValue)
-    return std::move(BSE.Value);
-  else
-    return make_error<StringError>(BSE.ErrMsg);
-}
-
-} // end namespace detail
-
-/// Serialize to a SPSError from a detail::SPSSerializableError.
-template <>
-class SPSSerializationTraits<SPSError, detail::SPSSerializableError> {
-public:
-  static size_t size(const detail::SPSSerializableError &BSE) {
-    size_t Size = SPSArgList<bool>::size(BSE.HasError);
-    if (BSE.HasError)
-      Size += SPSArgList<SPSString>::size(BSE.ErrMsg);
-    return Size;
-  }
-
-  static bool serialize(SPSOutputBuffer &OB,
-                        const detail::SPSSerializableError &BSE) {
-    if (!SPSArgList<bool>::serialize(OB, BSE.HasError))
-      return false;
-    if (BSE.HasError)
-      if (!SPSArgList<SPSString>::serialize(OB, BSE.ErrMsg))
-        return false;
-    return true;
-  }
-
-  static bool deserialize(SPSInputBuffer &IB,
-                          detail::SPSSerializableError &BSE) {
-    if (!SPSArgList<bool>::deserialize(IB, BSE.HasError))
-      return false;
-
-    if (!BSE.HasError)
-      return true;
-
-    return SPSArgList<SPSString>::deserialize(IB, BSE.ErrMsg);
-  }
-};
-
-/// Serialize to a SPSExpected<SPSTagT> from a
-/// detail::SPSSerializableExpected<T>.
-template <typename SPSTagT, typename T>
-class SPSSerializationTraits<SPSExpected<SPSTagT>,
-                             detail::SPSSerializableExpected<T>> {
-public:
-  static size_t size(const detail::SPSSerializableExpected<T> &BSE) {
-    size_t Size = SPSArgList<bool>::size(BSE.HasValue);
-    if (BSE.HasValue)
-      Size += SPSArgList<SPSTagT>::size(BSE.Value);
-    else
-      Size += SPSArgList<SPSString>::size(BSE.ErrMsg);
-    return Size;
-  }
-
-  static bool serialize(SPSOutputBuffer &OB,
-                        const detail::SPSSerializableExpected<T> &BSE) {
-    if (!SPSArgList<bool>::serialize(OB, BSE.HasValue))
-      return false;
-
-    if (BSE.HasValue)
-      return SPSArgList<SPSTagT>::serialize(OB, BSE.Value);
-
-    return SPSArgList<SPSString>::serialize(OB, BSE.ErrMsg);
-  }
-
-  static bool deserialize(SPSInputBuffer &IB,
-                          detail::SPSSerializableExpected<T> &BSE) {
-    if (!SPSArgList<bool>::deserialize(IB, BSE.HasValue))
-      return false;
-
-    if (BSE.HasValue)
-      return SPSArgList<SPSTagT>::deserialize(IB, BSE.Value);
-
-    return SPSArgList<SPSString>::deserialize(IB, BSE.ErrMsg);
-  }
-};
-
-/// Serialize to a SPSExpected<SPSTagT> from a detail::SPSSerializableError.
-template <typename SPSTagT>
-class SPSSerializationTraits<SPSExpected<SPSTagT>,
-                             detail::SPSSerializableError> {
-public:
-  static size_t size(const detail::SPSSerializableError &BSE) {
-    assert(BSE.HasError && "Cannot serialize expected from a success value");
-    return SPSArgList<bool>::size(false) +
-           SPSArgList<SPSString>::size(BSE.ErrMsg);
-  }
-
-  static bool serialize(SPSOutputBuffer &OB,
-                        const detail::SPSSerializableError &BSE) {
-    assert(BSE.HasError && "Cannot serialize expected from a success value");
-    if (!SPSArgList<bool>::serialize(OB, false))
-      return false;
-    return SPSArgList<SPSString>::serialize(OB, BSE.ErrMsg);
-  }
-};
-
-/// Serialize to a SPSExpected<SPSTagT> from a T.
-template <typename SPSTagT, typename T>
-class SPSSerializationTraits<SPSExpected<SPSTagT>, T> {
-public:
-  static size_t size(const T &Value) {
-    return SPSArgList<bool>::size(true) + SPSArgList<SPSTagT>::size(Value);
-  }
-
-  static bool serialize(SPSOutputBuffer &OB, const T &Value) {
-    if (!SPSArgList<bool>::serialize(OB, true))
-      return false;
-    return SPSArgList<SPSTagT>::serialize(Value);
-  }
-};
-
-namespace detail {
-
 template <typename WrapperFunctionImplT,
           template <typename> class ResultSerializer, typename... SPSTagTs>
 class WrapperFunctionHandlerHelper
@@ -690,8 +151,12 @@ class WrapperFunctionHandlerHelper<RetT(ArgTs...), ResultSerializer,
       return WrapperFunctionResult::createOutOfBandError(
           "Could not deserialize arguments for wrapper function call");
 
-    return ResultSerializer<RetT>::serialize(
-        call(std::forward<HandlerT>(H), Args, ArgIndices{}));
+    if (auto Result = ResultSerializer<RetT>::serialize(
+            call(std::forward<HandlerT>(H), Args, ArgIndices{})))
+      return std::move(*Result);
+    else
+      return WrapperFunctionResult::createOutOfBandError(
+          toString(Result.takeError()));
   }
 
 private:
@@ -735,37 +200,26 @@ class WrapperFunctionHandlerHelper<RetT (ClassT::*)(ArgTs...) const,
 
 template <typename SPSRetTagT, typename RetT> class ResultSerializer {
 public:
-  static WrapperFunctionResult serialize(RetT Result) {
-    WrapperFunctionResult R;
-    if (!SPSArgList<SPSRetTagT>::toWrapperFunctionResult(R, Result))
-      return WrapperFunctionResult::createOutOfBandError(
-          "Could not serialize return value from wrapper function");
-    return R;
+  static Expected<WrapperFunctionResult> serialize(RetT Result) {
+    return serializeViaSPSToWrapperFunctionResult<SPSArgList<SPSRetTagT>>(
+        Result);
   }
 };
 
 template <typename SPSRetTagT> class ResultSerializer<SPSRetTagT, Error> {
 public:
-  static WrapperFunctionResult serialize(Error Err) {
-    WrapperFunctionResult R;
-    if (!SPSArgList<SPSRetTagT>::toWrapperFunctionResult(
-            R, toSPSSerializable(std::move(Err))))
-      return WrapperFunctionResult::createOutOfBandError(
-          "Could not serialize return value from wrapper function");
-    return R;
+  static Expected<WrapperFunctionResult> serialize(Error Err) {
+    return serializeViaSPSToWrapperFunctionResult<SPSArgList<SPSRetTagT>>(
+        toSPSSerializable(std::move(Err)));
   }
 };
 
 template <typename SPSRetTagT, typename T>
 class ResultSerializer<SPSRetTagT, Expected<T>> {
 public:
-  static WrapperFunctionResult serialize(Expected<T> E) {
-    WrapperFunctionResult R;
-    if (!SPSArgList<SPSRetTagT>::toWrapperFunctionResult(
-            R, toSPSSerializable(std::move(E))))
-      return WrapperFunctionResult::createOutOfBandError(
-          "Could not serialize return value from wrapper function");
-    return R;
+  static Expected<WrapperFunctionResult> serialize(Expected<T> E) {
+    return serializeViaSPSToWrapperFunctionResult<SPSArgList<SPSRetTagT>>(
+        toSPSSerializable(std::move(E)));
   }
 };
 
@@ -838,12 +292,15 @@ class WrapperFunction<SPSRetTagT(SPSTagTs...)> {
     if (ORC_RT_UNLIKELY(!&__orc_rt_jit_dispatch_ctx))
       return make_error<StringError>("__orc_jtjit_dispatch not set");
 
-    WrapperFunctionResult ArgBuffer;
-    if (!SPSArgList<SPSTagTs...>::toWrapperFunctionResult(ArgBuffer, Args...))
-      return make_error<StringError>(
-          "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<SPSArgList<SPSTagTs...>>(
+            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<StringError>(ErrMsg);
 


        


More information about the llvm-commits mailing list