forked from OSchip/llvm-project
[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.
This commit is contained in:
parent
152c9871e6
commit
49f4a58d53
|
@ -19,6 +19,7 @@ set(ORC_IMPL_HEADERS
|
||||||
endianness.h
|
endianness.h
|
||||||
error.h
|
error.h
|
||||||
extensible_rtti.h
|
extensible_rtti.h
|
||||||
|
simple_packed_serialization.h
|
||||||
stl_extras.h
|
stl_extras.h
|
||||||
wrapper_function_utils.h
|
wrapper_function_utils.h
|
||||||
)
|
)
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
#define ORC_RT_COMMON_H
|
#define ORC_RT_COMMON_H
|
||||||
|
|
||||||
#include "c_api.h"
|
#include "c_api.h"
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
/// Opaque struct for external symbols.
|
/// Opaque struct for external symbols.
|
||||||
struct __orc_rt_Opaque {};
|
struct __orc_rt_Opaque {};
|
||||||
|
|
|
@ -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
|
|
@ -88,6 +88,7 @@ set(UNITTEST_SOURCES
|
||||||
orc_unit_test_main.cpp
|
orc_unit_test_main.cpp
|
||||||
stl_extras_test.cpp
|
stl_extras_test.cpp
|
||||||
wrapper_function_utils_test.cpp
|
wrapper_function_utils_test.cpp
|
||||||
|
simple_packed_serialization_test.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
if (COMPILER_RT_CAN_EXECUTE_TESTS)
|
if (COMPILER_RT_CAN_EXECUTE_TESTS)
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
|
@ -68,153 +68,6 @@ TEST(WrapperFunctionUtilsTest, WrapperFunctionResultFromOutOfBandError) {
|
||||||
EXPECT_TRUE(strcmp(R.getOutOfBandError(), TestString) == 0);
|
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,
|
static __orc_rt_CWrapperFunctionResult addWrapper(const char *ArgData,
|
||||||
size_t ArgSize) {
|
size_t ArgSize) {
|
||||||
return WrapperFunction<int32_t(int32_t, int32_t)>::handle(
|
return WrapperFunction<int32_t(int32_t, int32_t)>::handle(
|
||||||
|
|
|
@ -8,44 +8,16 @@
|
||||||
//
|
//
|
||||||
// This file is a part of the ORC runtime support library.
|
// 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
|
#ifndef ORC_RT_WRAPPER_FUNCTION_UTILS_H
|
||||||
#define ORC_RT_WRAPPER_FUNCTION_UTILS_H
|
#define ORC_RT_WRAPPER_FUNCTION_UTILS_H
|
||||||
|
|
||||||
#include "adt.h"
|
|
||||||
#include "c_api.h"
|
#include "c_api.h"
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "endianness.h"
|
|
||||||
#include "error.h"
|
#include "error.h"
|
||||||
#include "stl_extras.h"
|
#include "simple_packed_serialization.h"
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <tuple>
|
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <utility>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace __orc_rt {
|
namespace __orc_rt {
|
||||||
|
|
||||||
|
@ -126,6 +98,11 @@ public:
|
||||||
return __orc_rt_CreateCWrapperFunctionResultFromOutOfBandError(Msg);
|
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,
|
/// If this value is an out-of-band error then this returns the error message,
|
||||||
/// otherwise returns nullptr.
|
/// otherwise returns nullptr.
|
||||||
const char *getOutOfBandError() const {
|
const char *getOutOfBandError() const {
|
||||||
|
@ -136,537 +113,21 @@ private:
|
||||||
__orc_rt_CWrapperFunctionResult R;
|
__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 {
|
namespace detail {
|
||||||
|
|
||||||
/// Helper type for serializing Errors.
|
template <typename SPSArgListT, typename... ArgTs>
|
||||||
///
|
Expected<WrapperFunctionResult>
|
||||||
/// llvm::Errors are move-only, and not inspectable except by consuming them.
|
serializeViaSPSToWrapperFunctionResult(const ArgTs &...Args) {
|
||||||
/// This makes them unsuitable for direct serialization via
|
WrapperFunctionResult Result;
|
||||||
/// SPSSerializationTraits, which needs to inspect values twice (once to
|
char *DataPtr =
|
||||||
/// determine the amount of space to reserve, and then again to serialize).
|
WrapperFunctionResult::allocate(Result, SPSArgListT::size(Args...));
|
||||||
///
|
SPSOutputBuffer OB(DataPtr, Result.size());
|
||||||
/// The WrapperFunctionSerializableError type is a helper that can be
|
if (!SPSArgListT::serialize(OB, Args...))
|
||||||
/// constructed from an llvm::Error, but inspected more than once.
|
return make_error<StringError>(
|
||||||
struct SPSSerializableError {
|
"Error serializing arguments to blob in call");
|
||||||
bool HasError = false;
|
return Result;
|
||||||
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);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
namespace detail {
|
|
||||||
|
|
||||||
template <typename WrapperFunctionImplT,
|
template <typename WrapperFunctionImplT,
|
||||||
template <typename> class ResultSerializer, typename... SPSTagTs>
|
template <typename> class ResultSerializer, typename... SPSTagTs>
|
||||||
class WrapperFunctionHandlerHelper
|
class WrapperFunctionHandlerHelper
|
||||||
|
@ -690,8 +151,12 @@ public:
|
||||||
return WrapperFunctionResult::createOutOfBandError(
|
return WrapperFunctionResult::createOutOfBandError(
|
||||||
"Could not deserialize arguments for wrapper function call");
|
"Could not deserialize arguments for wrapper function call");
|
||||||
|
|
||||||
return ResultSerializer<RetT>::serialize(
|
if (auto Result = ResultSerializer<RetT>::serialize(
|
||||||
call(std::forward<HandlerT>(H), Args, ArgIndices{}));
|
call(std::forward<HandlerT>(H), Args, ArgIndices{})))
|
||||||
|
return std::move(*Result);
|
||||||
|
else
|
||||||
|
return WrapperFunctionResult::createOutOfBandError(
|
||||||
|
toString(Result.takeError()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -735,37 +200,26 @@ class WrapperFunctionHandlerHelper<RetT (ClassT::*)(ArgTs...) const,
|
||||||
|
|
||||||
template <typename SPSRetTagT, typename RetT> class ResultSerializer {
|
template <typename SPSRetTagT, typename RetT> class ResultSerializer {
|
||||||
public:
|
public:
|
||||||
static WrapperFunctionResult serialize(RetT Result) {
|
static Expected<WrapperFunctionResult> serialize(RetT Result) {
|
||||||
WrapperFunctionResult R;
|
return serializeViaSPSToWrapperFunctionResult<SPSArgList<SPSRetTagT>>(
|
||||||
if (!SPSArgList<SPSRetTagT>::toWrapperFunctionResult(R, Result))
|
Result);
|
||||||
return WrapperFunctionResult::createOutOfBandError(
|
|
||||||
"Could not serialize return value from wrapper function");
|
|
||||||
return R;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename SPSRetTagT> class ResultSerializer<SPSRetTagT, Error> {
|
template <typename SPSRetTagT> class ResultSerializer<SPSRetTagT, Error> {
|
||||||
public:
|
public:
|
||||||
static WrapperFunctionResult serialize(Error Err) {
|
static Expected<WrapperFunctionResult> serialize(Error Err) {
|
||||||
WrapperFunctionResult R;
|
return serializeViaSPSToWrapperFunctionResult<SPSArgList<SPSRetTagT>>(
|
||||||
if (!SPSArgList<SPSRetTagT>::toWrapperFunctionResult(
|
toSPSSerializable(std::move(Err)));
|
||||||
R, toSPSSerializable(std::move(Err))))
|
|
||||||
return WrapperFunctionResult::createOutOfBandError(
|
|
||||||
"Could not serialize return value from wrapper function");
|
|
||||||
return R;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename SPSRetTagT, typename T>
|
template <typename SPSRetTagT, typename T>
|
||||||
class ResultSerializer<SPSRetTagT, Expected<T>> {
|
class ResultSerializer<SPSRetTagT, Expected<T>> {
|
||||||
public:
|
public:
|
||||||
static WrapperFunctionResult serialize(Expected<T> E) {
|
static Expected<WrapperFunctionResult> serialize(Expected<T> E) {
|
||||||
WrapperFunctionResult R;
|
return serializeViaSPSToWrapperFunctionResult<SPSArgList<SPSRetTagT>>(
|
||||||
if (!SPSArgList<SPSRetTagT>::toWrapperFunctionResult(
|
toSPSSerializable(std::move(E)));
|
||||||
R, toSPSSerializable(std::move(E))))
|
|
||||||
return WrapperFunctionResult::createOutOfBandError(
|
|
||||||
"Could not serialize return value from wrapper function");
|
|
||||||
return R;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -838,12 +292,15 @@ public:
|
||||||
if (ORC_RT_UNLIKELY(!&__orc_rt_jit_dispatch_ctx))
|
if (ORC_RT_UNLIKELY(!&__orc_rt_jit_dispatch_ctx))
|
||||||
return make_error<StringError>("__orc_jtjit_dispatch not set");
|
return make_error<StringError>("__orc_jtjit_dispatch not set");
|
||||||
|
|
||||||
WrapperFunctionResult ArgBuffer;
|
auto ArgBuffer =
|
||||||
if (!SPSArgList<SPSTagTs...>::toWrapperFunctionResult(ArgBuffer, Args...))
|
detail::serializeViaSPSToWrapperFunctionResult<SPSArgList<SPSTagTs...>>(
|
||||||
return make_error<StringError>(
|
Args...);
|
||||||
"Error serializing arguments to blob in call");
|
if (!ArgBuffer)
|
||||||
WrapperFunctionResult ResultBuffer = __orc_rt_jit_dispatch(
|
return ArgBuffer.takeError();
|
||||||
&__orc_rt_jit_dispatch_ctx, FnTag, ArgBuffer.data(), ArgBuffer.size());
|
|
||||||
|
WrapperFunctionResult ResultBuffer =
|
||||||
|
__orc_rt_jit_dispatch(&__orc_rt_jit_dispatch_ctx, FnTag,
|
||||||
|
ArgBuffer->data(), ArgBuffer->size());
|
||||||
if (auto ErrMsg = ResultBuffer.getOutOfBandError())
|
if (auto ErrMsg = ResultBuffer.getOutOfBandError())
|
||||||
return make_error<StringError>(ErrMsg);
|
return make_error<StringError>(ErrMsg);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue