1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-11-22 10:42:39 +01:00

[ORC] Port WrapperFunctionUtils and SimplePackedSerialization from ORC runtime.

Replace the existing WrapperFunctionResult type in
llvm/include/ExecutionEngine/Orc/Shared/TargetProcessControlTypes.h with a
version adapted from the ORC runtime's implementation.

Also introduce the SimplePackedSerialization scheme (also adapted from the ORC
runtime's implementation) for wrapper functions to avoid manual serialization
and deserialization for calls to runtime functions involving common types.
This commit is contained in:
Lang Hames 2021-06-15 20:26:51 +10:00
parent 8521aa2a65
commit e11b1aca83
16 changed files with 1299 additions and 260 deletions

View File

@ -354,7 +354,7 @@ public:
return Result; return Result;
} }
Expected<tpctypes::WrapperFunctionResult> Expected<shared::WrapperFunctionResult>
runWrapper(JITTargetAddress WrapperFnAddr, runWrapper(JITTargetAddress WrapperFnAddr,
ArrayRef<uint8_t> ArgBuffer) override { ArrayRef<uint8_t> ArgBuffer) override {
DEBUG_WITH_TYPE("orc", { DEBUG_WITH_TYPE("orc", {
@ -364,7 +364,6 @@ public:
}); });
auto Result = auto Result =
EP.template callB<orcrpctpc::RunWrapper>(WrapperFnAddr, ArgBuffer); EP.template callB<orcrpctpc::RunWrapper>(WrapperFnAddr, ArgBuffer);
// dbgs() << "Returned from runWrapper...\n";
return Result; return Result;
} }

View File

@ -0,0 +1,565 @@
//===---- SimplePackedSerialization.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
//
//===----------------------------------------------------------------------===//
//
// The behavior of the utilities in this header must be synchronized with the
// behavior of the utilities in
// compiler-rt/lib/orc/simple_packed_serialization.h.
//
// The Simple Packed Serialization (SPS) utilities are used to generate
// argument and return buffers for wrapper functions using the following
// serialization scheme:
//
// Primitives (signed types should be two's complement):
// bool, char, int8_t, uint8_t -- 8-bit (0=false, 1=true)
// int16_t, uint16_t -- 16-bit little endian
// int32_t, uint32_t -- 32-bit little endian
// int64_t, int64_t -- 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 LLVM_EXECUTIONENGINE_ORC_SHARED_SIMPLEPACKEDSERIALIZATION_H
#define LLVM_EXECUTIONENGINE_ORC_SHARED_SIMPLEPACKEDSERIALIZATION_H
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/SwapByteOrder.h"
#include <string>
#include <tuple>
#include <type_traits>
#include <utility>
#include <vector>
namespace llvm {
namespace orc {
namespace shared {
/// 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 (sys::IsBigEndianHost)
sys::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 (sys::IsBigEndianHost)
sys::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 target addresseses.
class SPSTargetAddress {};
template <>
class SPSSerializationTraits<SPSTargetAddress, uint64_t>
: public SPSSerializationTraits<uint64_t, uint64_t> {};
/// 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 StringRefs.
///
/// Serialization is as for regular strings. Deserialization points directly
/// into the blob.
template <> class SPSSerializationTraits<SPSString, StringRef> {
public:
static size_t size(const StringRef &S) {
return SPSArgList<uint64_t>::size(static_cast<uint64_t>(S.size())) +
S.size();
}
static bool serialize(SPSOutputBuffer &OB, StringRef 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, StringRef &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, inconvertibleErrorCode());
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, inconvertibleErrorCode());
}
} // 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 shared
} // end namespace orc
} // end namespace llvm
#endif // LLVM_EXECUTIONENGINE_ORC_SHARED_SIMPLEPACKEDSERIALIZATION_H

View File

@ -61,103 +61,6 @@ using DylibHandle = JITTargetAddress;
using LookupResult = std::vector<JITTargetAddress>; using LookupResult = std::vector<JITTargetAddress>;
/// Either a uint8_t array or a uint8_t*.
union CWrapperFunctionResultData {
uint8_t Value[8];
uint8_t *ValuePtr;
};
/// C ABI compatible wrapper function result.
///
/// This can be safely returned from extern "C" functions, but should be used
/// to construct a WrapperFunctionResult for safety.
struct CWrapperFunctionResult {
uint64_t Size;
CWrapperFunctionResultData Data;
void (*Destroy)(CWrapperFunctionResultData Data, uint64_t Size);
};
/// C++ wrapper function result: Same as CWrapperFunctionResult but
/// auto-releases memory.
class WrapperFunctionResult {
public:
/// Create a default WrapperFunctionResult.
WrapperFunctionResult() { zeroInit(R); }
/// Create a WrapperFunctionResult from a CWrapperFunctionResult. This
/// instance takes ownership of the result object and will automatically
/// call the Destroy member upon destruction.
WrapperFunctionResult(CWrapperFunctionResult R) : R(R) {}
WrapperFunctionResult(const WrapperFunctionResult &) = delete;
WrapperFunctionResult &operator=(const WrapperFunctionResult &) = delete;
WrapperFunctionResult(WrapperFunctionResult &&Other) {
zeroInit(R);
std::swap(R, Other.R);
}
WrapperFunctionResult &operator=(WrapperFunctionResult &&Other) {
CWrapperFunctionResult Tmp;
zeroInit(Tmp);
std::swap(Tmp, Other.R);
std::swap(R, Tmp);
return *this;
}
~WrapperFunctionResult() {
if (R.Destroy)
R.Destroy(R.Data, R.Size);
}
/// Relinquish ownership of and return the CWrapperFunctionResult.
CWrapperFunctionResult release() {
CWrapperFunctionResult Tmp;
zeroInit(Tmp);
std::swap(R, Tmp);
return Tmp;
}
/// Get an ArrayRef covering the data in the result.
ArrayRef<uint8_t> getData() const {
if (R.Size <= 8)
return ArrayRef<uint8_t>(R.Data.Value, R.Size);
return ArrayRef<uint8_t>(R.Data.ValuePtr, R.Size);
}
/// Create a WrapperFunctionResult from the given integer, provided its
/// size is no greater than 64 bits.
template <typename T,
typename _ = std::enable_if_t<std::is_integral<T>::value &&
sizeof(T) <= sizeof(uint64_t)>>
static WrapperFunctionResult from(T Value) {
CWrapperFunctionResult R;
R.Size = sizeof(T);
memcpy(&R.Data.Value, Value, R.Size);
R.Destroy = nullptr;
return R;
}
/// Create a WrapperFunctionResult from the given string.
static WrapperFunctionResult from(StringRef S);
/// Always free Data.ValuePtr by calling free on it.
static void destroyWithFree(CWrapperFunctionResultData Data, uint64_t Size);
/// Always free Data.ValuePtr by calling delete[] on it.
static void destroyWithDeleteArray(CWrapperFunctionResultData Data,
uint64_t Size);
private:
static void zeroInit(CWrapperFunctionResult &R) {
R.Size = 0;
R.Data.ValuePtr = nullptr;
R.Destroy = nullptr;
}
CWrapperFunctionResult R;
};
} // end namespace tpctypes } // end namespace tpctypes
} // end namespace orc } // end namespace orc
} // end namespace llvm } // end namespace llvm

View File

@ -0,0 +1,426 @@
//===- WrapperFunctionUtils.h - Utilities for wrapper functions -*- 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
//
//===----------------------------------------------------------------------===//
//
// A buffer for serialized results.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_EXECUTIONENGINE_ORC_WRAPPERFUNCTIONUTILS_H
#define LLVM_EXECUTIONENGINE_ORC_WRAPPERFUNCTIONUTILS_H
#include "llvm/ExecutionEngine/Orc/Shared/SimplePackedSerialization.h"
#include "llvm/Support/Error.h"
#include <type_traits>
namespace llvm {
namespace orc {
namespace shared {
namespace detail {
// DO NOT USE DIRECTLY.
// Must be kept in-sync with compiler-rt/lib/orc/c-api.h.
union CWrapperFunctionResultDataUnion {
const char *ValuePtr;
char Value[sizeof(ValuePtr)];
};
// DO NOT USE DIRECTLY.
// Must be kept in-sync with compiler-rt/lib/orc/c-api.h.
typedef struct {
CWrapperFunctionResultDataUnion Data;
size_t Size;
} CWrapperFunctionResult;
} // end namespace detail
/// C++ wrapper function result: Same as CWrapperFunctionResult but
/// auto-releases memory.
class WrapperFunctionResult {
public:
/// Create a default WrapperFunctionResult.
WrapperFunctionResult() { init(R); }
/// Create a WrapperFunctionResult by taking ownership of a
/// detail::CWrapperFunctionResult.
///
/// Warning: This should only be used by clients writing wrapper-function
/// caller utilities (like TargetProcessControl).
WrapperFunctionResult(detail::CWrapperFunctionResult R) : R(R) {
// Reset R.
init(R);
}
WrapperFunctionResult(const WrapperFunctionResult &) = delete;
WrapperFunctionResult &operator=(const WrapperFunctionResult &) = delete;
WrapperFunctionResult(WrapperFunctionResult &&Other) {
init(R);
std::swap(R, Other.R);
}
WrapperFunctionResult &operator=(WrapperFunctionResult &&Other) {
WrapperFunctionResult Tmp(std::move(Other));
std::swap(R, Tmp.R);
return *this;
}
~WrapperFunctionResult() {
if ((R.Size > sizeof(R.Data.Value)) ||
(R.Size == 0 && R.Data.ValuePtr != nullptr))
free((void *)R.Data.ValuePtr);
}
/// Release ownership of the contained detail::CWrapperFunctionResult.
/// Warning: Do not use -- this method will be removed in the future. It only
/// exists to temporarily support some code that will eventually be moved to
/// the ORC runtime.
detail::CWrapperFunctionResult release() {
detail::CWrapperFunctionResult Tmp;
init(Tmp);
std::swap(R, Tmp);
return Tmp;
}
/// Get a pointer to the data contained in this instance.
const char *data() const {
assert((R.Size != 0 || R.Data.ValuePtr == nullptr) &&
"Cannot get data for out-of-band error value");
return R.Size > sizeof(R.Data.Value) ? R.Data.ValuePtr : R.Data.Value;
}
/// Returns the size of the data contained in this instance.
size_t size() const {
assert((R.Size != 0 || R.Data.ValuePtr == nullptr) &&
"Cannot get data for out-of-band error value");
return R.Size;
}
/// Returns true if this value is equivalent to a default-constructed
/// WrapperFunctionResult.
bool empty() const { return R.Size == 0 && R.Data.ValuePtr == nullptr; }
/// Create a WrapperFunctionResult with the given size and return a pointer
/// to the underlying memory.
static char *allocate(WrapperFunctionResult &WFR, size_t Size) {
// Reset.
WFR = WrapperFunctionResult();
WFR.R.Size = Size;
char *DataPtr;
if (WFR.R.Size > sizeof(WFR.R.Data.Value)) {
DataPtr = (char *)malloc(WFR.R.Size);
WFR.R.Data.ValuePtr = DataPtr;
} else
DataPtr = WFR.R.Data.Value;
return DataPtr;
}
/// Copy from the given char range.
static WrapperFunctionResult copyFrom(const char *Source, size_t Size) {
WrapperFunctionResult WFR;
char *DataPtr = allocate(WFR, Size);
memcpy(DataPtr, Source, Size);
return WFR;
}
/// Copy from the given null-terminated string (includes the null-terminator).
static WrapperFunctionResult copyFrom(const char *Source) {
return copyFrom(Source, strlen(Source) + 1);
}
/// Copy from the given std::string (includes the null terminator).
static WrapperFunctionResult copyFrom(const std::string &Source) {
return copyFrom(Source.c_str());
}
/// Create an out-of-band error by copying the given string.
static WrapperFunctionResult createOutOfBandError(const char *Msg) {
// Reset.
WrapperFunctionResult WFR;
char *Tmp = (char *)malloc(strlen(Msg) + 1);
strcpy(Tmp, Msg);
WFR.R.Data.ValuePtr = Tmp;
return WFR;
}
/// 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 {
return R.Size == 0 ? R.Data.ValuePtr : nullptr;
}
private:
static void init(detail::CWrapperFunctionResult &R) {
R.Data.ValuePtr = nullptr;
R.Size = 0;
}
detail::CWrapperFunctionResult R;
};
namespace detail {
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",
inconvertibleErrorCode());
return Result;
}
template <typename RetT> class WrapperFunctionHandlerCaller {
public:
template <typename HandlerT, typename ArgTupleT, std::size_t... I>
static decltype(auto) call(HandlerT &&H, ArgTupleT &Args,
std::index_sequence<I...>) {
return std::forward<HandlerT>(H)(std::get<I>(Args)...);
}
};
template <> class WrapperFunctionHandlerCaller<void> {
public:
template <typename HandlerT, typename ArgTupleT, std::size_t... I>
static SPSEmpty call(HandlerT &&H, ArgTupleT &Args,
std::index_sequence<I...>) {
std::forward<HandlerT>(H)(std::get<I>(Args)...);
return SPSEmpty();
}
};
template <typename WrapperFunctionImplT,
template <typename> class ResultSerializer, typename... SPSTagTs>
class WrapperFunctionHandlerHelper
: public WrapperFunctionHandlerHelper<
decltype(&std::remove_reference_t<WrapperFunctionImplT>::operator()),
ResultSerializer, SPSTagTs...> {};
template <typename RetT, typename... ArgTs,
template <typename> class ResultSerializer, typename... SPSTagTs>
class WrapperFunctionHandlerHelper<RetT(ArgTs...), ResultSerializer,
SPSTagTs...> {
public:
using ArgTuple = std::tuple<std::decay_t<ArgTs>...>;
using ArgIndices = std::make_index_sequence<std::tuple_size<ArgTuple>::value>;
template <typename HandlerT>
static WrapperFunctionResult apply(HandlerT &&H, const char *ArgData,
size_t ArgSize) {
ArgTuple Args;
if (!deserialize(ArgData, ArgSize, Args, ArgIndices{}))
return WrapperFunctionResult::createOutOfBandError(
"Could not deserialize arguments for wrapper function call");
auto HandlerResult = WrapperFunctionHandlerCaller<RetT>::call(
std::forward<HandlerT>(H), Args, ArgIndices{});
if (auto Result = ResultSerializer<decltype(HandlerResult)>::serialize(
std::move(HandlerResult)))
return std::move(*Result);
else
return WrapperFunctionResult::createOutOfBandError(
toString(Result.takeError()));
}
private:
template <std::size_t... I>
static bool deserialize(const char *ArgData, size_t ArgSize, ArgTuple &Args,
std::index_sequence<I...>) {
SPSInputBuffer IB(ArgData, ArgSize);
return SPSArgList<SPSTagTs...>::deserialize(IB, std::get<I>(Args)...);
}
};
// Map function references to function types.
template <typename RetT, typename... ArgTs,
template <typename> class ResultSerializer, typename... SPSTagTs>
class WrapperFunctionHandlerHelper<RetT (&)(ArgTs...), ResultSerializer,
SPSTagTs...>
: public WrapperFunctionHandlerHelper<RetT(ArgTs...), ResultSerializer,
SPSTagTs...> {};
// Map non-const member function types to function types.
template <typename ClassT, typename RetT, typename... ArgTs,
template <typename> class ResultSerializer, typename... SPSTagTs>
class WrapperFunctionHandlerHelper<RetT (ClassT::*)(ArgTs...), ResultSerializer,
SPSTagTs...>
: public WrapperFunctionHandlerHelper<RetT(ArgTs...), ResultSerializer,
SPSTagTs...> {};
// Map const member function types to function types.
template <typename ClassT, typename RetT, typename... ArgTs,
template <typename> class ResultSerializer, typename... SPSTagTs>
class WrapperFunctionHandlerHelper<RetT (ClassT::*)(ArgTs...) const,
ResultSerializer, SPSTagTs...>
: public WrapperFunctionHandlerHelper<RetT(ArgTs...), ResultSerializer,
SPSTagTs...> {};
template <typename SPSRetTagT, typename RetT> class ResultSerializer {
public:
static Expected<WrapperFunctionResult> serialize(RetT Result) {
return serializeViaSPSToWrapperFunctionResult<SPSArgList<SPSRetTagT>>(
Result);
}
};
template <typename SPSRetTagT> class ResultSerializer<SPSRetTagT, Error> {
public:
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 Expected<WrapperFunctionResult> serialize(Expected<T> E) {
return serializeViaSPSToWrapperFunctionResult<SPSArgList<SPSRetTagT>>(
toSPSSerializable(std::move(E)));
}
};
template <typename SPSRetTagT, typename RetT> class ResultDeserializer {
public:
static void makeSafe(RetT &Result) {}
static Error deserialize(RetT &Result, const char *ArgData, size_t ArgSize) {
SPSInputBuffer IB(ArgData, ArgSize);
if (!SPSArgList<SPSRetTagT>::deserialize(IB, Result))
return make_error<StringError>(
"Error deserializing return value from blob in call",
inconvertibleErrorCode());
return Error::success();
}
};
template <> class ResultDeserializer<SPSError, Error> {
public:
static void makeSafe(Error &Err) { cantFail(std::move(Err)); }
static Error deserialize(Error &Err, const char *ArgData, size_t ArgSize) {
SPSInputBuffer IB(ArgData, ArgSize);
SPSSerializableError BSE;
if (!SPSArgList<SPSError>::deserialize(IB, BSE))
return make_error<StringError>(
"Error deserializing return value from blob in call",
inconvertibleErrorCode());
Err = fromSPSSerializable(std::move(BSE));
return Error::success();
}
};
template <typename SPSTagT, typename T>
class ResultDeserializer<SPSExpected<SPSTagT>, Expected<T>> {
public:
static void makeSafe(Expected<T> &E) { cantFail(E.takeError()); }
static Error deserialize(Expected<T> &E, const char *ArgData,
size_t ArgSize) {
SPSInputBuffer IB(ArgData, ArgSize);
SPSSerializableExpected<T> BSE;
if (!SPSArgList<SPSExpected<SPSTagT>>::deserialize(IB, BSE))
return make_error<StringError>(
"Error deserializing return value from blob in call",
inconvertibleErrorCode());
E = fromSPSSerializable(std::move(BSE));
return Error::success();
}
};
} // end namespace detail
template <typename SPSSignature> class WrapperFunction;
template <typename SPSRetTagT, typename... SPSTagTs>
class WrapperFunction<SPSRetTagT(SPSTagTs...)> {
private:
template <typename RetT>
using ResultSerializer = detail::ResultSerializer<SPSRetTagT, RetT>;
public:
/// Call a wrapper function. Callere should be callable as
/// WrapperFunctionResult Fn(const char *ArgData, size_t ArgSize);
template <typename CallerFn, typename RetT, typename... ArgTs>
static Error call(const CallerFn &Caller, RetT &Result,
const ArgTs &...Args) {
// RetT might be an Error or Expected value. Set the checked flag now:
// we don't want the user to have to check the unused result if this
// operation fails.
detail::ResultDeserializer<SPSRetTagT, RetT>::makeSafe(Result);
auto ArgBuffer =
detail::serializeViaSPSToWrapperFunctionResult<SPSArgList<SPSTagTs...>>(
Args...);
if (!ArgBuffer)
return ArgBuffer.takeError();
WrapperFunctionResult ResultBuffer =
Caller(ArgBuffer->data(), ArgBuffer->size());
if (auto ErrMsg = ResultBuffer.getOutOfBandError())
return make_error<StringError>(ErrMsg, inconvertibleErrorCode());
return detail::ResultDeserializer<SPSRetTagT, RetT>::deserialize(
Result, ResultBuffer.data(), ResultBuffer.size());
}
/// Handle a call to a wrapper function.
template <typename HandlerT>
static WrapperFunctionResult handle(const char *ArgData, size_t ArgSize,
HandlerT &&Handler) {
using WFHH =
detail::WrapperFunctionHandlerHelper<HandlerT, ResultSerializer,
SPSTagTs...>;
return WFHH::apply(std::forward<HandlerT>(Handler), ArgData, ArgSize);
}
private:
template <typename T> static const T &makeSerializable(const T &Value) {
return Value;
}
static detail::SPSSerializableError makeSerializable(Error Err) {
return detail::toSPSSerializable(std::move(Err));
}
template <typename T>
static detail::SPSSerializableExpected<T> makeSerializable(Expected<T> E) {
return detail::toSPSSerializable(std::move(E));
}
};
template <typename... SPSTagTs>
class WrapperFunction<void(SPSTagTs...)>
: private WrapperFunction<SPSEmpty(SPSTagTs...)> {
public:
template <typename... ArgTs>
static Error call(const void *FnTag, const ArgTs &...Args) {
SPSEmpty BE;
return WrapperFunction<SPSEmpty(SPSTagTs...)>::call(FnTag, BE, Args...);
}
using WrapperFunction<SPSEmpty(SPSTagTs...)>::handle;
};
} // end namespace shared
} // end namespace orc
} // end namespace llvm
#endif // LLVM_EXECUTIONENGINE_ORC_WRAPPERFUNCTIONUTILS_H

View File

@ -13,10 +13,10 @@
#ifndef LLVM_EXECUTIONENGINE_ORC_TARGETPROCESS_JITLOADERGDB_H #ifndef LLVM_EXECUTIONENGINE_ORC_TARGETPROCESS_JITLOADERGDB_H
#define LLVM_EXECUTIONENGINE_ORC_TARGETPROCESS_JITLOADERGDB_H #define LLVM_EXECUTIONENGINE_ORC_TARGETPROCESS_JITLOADERGDB_H
#include "llvm/ExecutionEngine/Orc/Shared/TargetProcessControlTypes.h" #include "llvm/ExecutionEngine/Orc/Shared/WrapperFunctionUtils.h"
#include <cstdint> #include <cstdint>
extern "C" llvm::orc::tpctypes::CWrapperFunctionResult extern "C" llvm::orc::shared::detail::CWrapperFunctionResult
llvm_orc_registerJITLoaderGDBWrapper(uint8_t *Data, uint64_t Size); llvm_orc_registerJITLoaderGDBWrapper(const char *Data, uint64_t Size);
#endif // LLVM_EXECUTIONENGINE_ORC_TARGETPROCESS_JITLOADERGDB_H #endif // LLVM_EXECUTIONENGINE_ORC_TARGETPROCESS_JITLOADERGDB_H

View File

@ -17,6 +17,7 @@
#include "llvm/ExecutionEngine/Orc/Shared/RPCUtils.h" #include "llvm/ExecutionEngine/Orc/Shared/RPCUtils.h"
#include "llvm/ExecutionEngine/Orc/Shared/RawByteChannel.h" #include "llvm/ExecutionEngine/Orc/Shared/RawByteChannel.h"
#include "llvm/ExecutionEngine/Orc/Shared/TargetProcessControlTypes.h" #include "llvm/ExecutionEngine/Orc/Shared/TargetProcessControlTypes.h"
#include "llvm/ExecutionEngine/Orc/Shared/WrapperFunctionUtils.h"
#include "llvm/ExecutionEngine/Orc/TargetProcess/TargetExecutionUtils.h" #include "llvm/ExecutionEngine/Orc/TargetProcess/TargetExecutionUtils.h"
#include "llvm/Support/DynamicLibrary.h" #include "llvm/Support/DynamicLibrary.h"
#include "llvm/Support/FormatVariadic.h" #include "llvm/Support/FormatVariadic.h"
@ -135,7 +136,7 @@ public:
static const char *getName() { return "ReleaseOrFinalizeMemRequestElement"; } static const char *getName() { return "ReleaseOrFinalizeMemRequestElement"; }
}; };
template <> class SerializationTypeName<tpctypes::WrapperFunctionResult> { template <> class SerializationTypeName<shared::WrapperFunctionResult> {
public: public:
static const char *getName() { return "WrapperFunctionResult"; } static const char *getName() { return "WrapperFunctionResult"; }
}; };
@ -234,40 +235,25 @@ public:
template <typename ChannelT> template <typename ChannelT>
class SerializationTraits< class SerializationTraits<
ChannelT, tpctypes::WrapperFunctionResult, tpctypes::WrapperFunctionResult, ChannelT, shared::WrapperFunctionResult, shared::WrapperFunctionResult,
std::enable_if_t<std::is_base_of<RawByteChannel, ChannelT>::value>> { std::enable_if_t<std::is_base_of<RawByteChannel, ChannelT>::value>> {
public: public:
static Error serialize(ChannelT &C, static Error serialize(ChannelT &C, const shared::WrapperFunctionResult &E) {
const tpctypes::WrapperFunctionResult &E) { if (auto Err = serializeSeq(C, static_cast<uint64_t>(E.size())))
auto Data = E.getData();
if (auto Err = serializeSeq(C, static_cast<uint64_t>(Data.size())))
return Err; return Err;
if (Data.size() == 0) if (E.size() == 0)
return Error::success(); return Error::success();
return C.appendBytes(reinterpret_cast<const char *>(Data.data()), return C.appendBytes(E.data(), E.size());
Data.size());
} }
static Error deserialize(ChannelT &C, tpctypes::WrapperFunctionResult &E) { static Error deserialize(ChannelT &C, shared::WrapperFunctionResult &E) {
tpctypes::CWrapperFunctionResult R;
R.Size = 0; uint64_t Size;
R.Data.ValuePtr = nullptr; if (auto Err = deserializeSeq(C, Size))
R.Destroy = nullptr;
if (auto Err = deserializeSeq(C, R.Size))
return Err; return Err;
if (R.Size == 0)
return Error::success();
R.Data.ValuePtr = new uint8_t[R.Size];
if (auto Err =
C.readBytes(reinterpret_cast<char *>(R.Data.ValuePtr), R.Size)) {
R.Destroy = tpctypes::WrapperFunctionResult::destroyWithDeleteArray;
return Err;
}
E = tpctypes::WrapperFunctionResult(R); char *DataPtr = shared::WrapperFunctionResult::allocate(E, Size);
return Error::success(); return C.readBytes(DataPtr, E.size());
} }
}; };
@ -371,7 +357,7 @@ public:
class RunWrapper class RunWrapper
: public shared::RPCFunction<RunWrapper, : public shared::RPCFunction<RunWrapper,
tpctypes::WrapperFunctionResult( shared::WrapperFunctionResult(
JITTargetAddress, std::vector<uint8_t>)> { JITTargetAddress, std::vector<uint8_t>)> {
public: public:
static const char *getName() { return "RunWrapper"; } static const char *getName() { return "RunWrapper"; }
@ -594,13 +580,14 @@ private:
ProgramNameOverride); ProgramNameOverride);
} }
tpctypes::WrapperFunctionResult shared::WrapperFunctionResult
runWrapper(JITTargetAddress WrapperFnAddr, runWrapper(JITTargetAddress WrapperFnAddr,
const std::vector<uint8_t> &ArgBuffer) { const std::vector<uint8_t> &ArgBuffer) {
using WrapperFnTy = tpctypes::CWrapperFunctionResult (*)( using WrapperFnTy = shared::detail::CWrapperFunctionResult (*)(
const uint8_t *Data, uint64_t Size); const char *Data, uint64_t Size);
auto *WrapperFn = jitTargetAddressToFunction<WrapperFnTy>(WrapperFnAddr); auto *WrapperFn = jitTargetAddressToFunction<WrapperFnTy>(WrapperFnAddr);
return WrapperFn(ArgBuffer.data(), ArgBuffer.size()); return WrapperFn(reinterpret_cast<const char *>(ArgBuffer.data()),
ArgBuffer.size());
} }
void closeConnection() { Finished = true; } void closeConnection() { Finished = true; }

View File

@ -9,12 +9,14 @@
// Support for dynamically registering and deregistering eh-frame sections // Support for dynamically registering and deregistering eh-frame sections
// in-process via libunwind. // in-process via libunwind.
// //
// FIXME: The functionality in this file should be moved to the ORC runtime.
//
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
#ifndef LLVM_EXECUTIONENGINE_ORC_TARGETPROCESS_REGISTEREHFRAMES_H #ifndef LLVM_EXECUTIONENGINE_ORC_TARGETPROCESS_REGISTEREHFRAMES_H
#define LLVM_EXECUTIONENGINE_ORC_TARGETPROCESS_REGISTEREHFRAMES_H #define LLVM_EXECUTIONENGINE_ORC_TARGETPROCESS_REGISTEREHFRAMES_H
#include "llvm/ExecutionEngine/Orc/Shared/TargetProcessControlTypes.h" #include "llvm/ExecutionEngine/Orc/Shared/WrapperFunctionUtils.h"
#include "llvm/Support/Error.h" #include "llvm/Support/Error.h"
namespace llvm { namespace llvm {
@ -31,10 +33,10 @@ Error deregisterEHFrameSection(const void *EHFrameSectionAddr,
} // end namespace orc } // end namespace orc
} // end namespace llvm } // end namespace llvm
extern "C" llvm::orc::tpctypes::CWrapperFunctionResult extern "C" llvm::orc::shared::detail::CWrapperFunctionResult
llvm_orc_registerEHFrameSectionWrapper(uint8_t *Data, uint64_t Size); llvm_orc_registerEHFrameSectionWrapper(const char *Data, uint64_t Size);
extern "C" llvm::orc::tpctypes::CWrapperFunctionResult extern "C" llvm::orc::shared::detail::CWrapperFunctionResult
llvm_orc_deregisterEHFrameSectionWrapper(uint8_t *Data, uint64_t Size); llvm_orc_deregisterEHFrameSectionWrapper(const char *Data, uint64_t Size);
#endif // LLVM_EXECUTIONENGINE_ORC_TARGETPROCESS_REGISTEREHFRAMES_H #endif // LLVM_EXECUTIONENGINE_ORC_TARGETPROCESS_REGISTEREHFRAMES_H

View File

@ -19,6 +19,7 @@
#include "llvm/ExecutionEngine/JITLink/JITLinkMemoryManager.h" #include "llvm/ExecutionEngine/JITLink/JITLinkMemoryManager.h"
#include "llvm/ExecutionEngine/Orc/Core.h" #include "llvm/ExecutionEngine/Orc/Core.h"
#include "llvm/ExecutionEngine/Orc/Shared/TargetProcessControlTypes.h" #include "llvm/ExecutionEngine/Orc/Shared/TargetProcessControlTypes.h"
#include "llvm/ExecutionEngine/Orc/Shared/WrapperFunctionUtils.h"
#include "llvm/Support/DynamicLibrary.h" #include "llvm/Support/DynamicLibrary.h"
#include "llvm/Support/MSVCErrorWorkarounds.h" #include "llvm/Support/MSVCErrorWorkarounds.h"
@ -137,13 +138,13 @@ public:
virtual Expected<int32_t> runAsMain(JITTargetAddress MainFnAddr, virtual Expected<int32_t> runAsMain(JITTargetAddress MainFnAddr,
ArrayRef<std::string> Args) = 0; ArrayRef<std::string> Args) = 0;
/// Run a wrapper function with signature: /// Run a wrapper function in the executor.
/// ///
/// \code{.cpp} /// \code{.cpp}
/// CWrapperFunctionResult fn(uint8_t *Data, uint64_t Size); /// CWrapperFunctionResult fn(uint8_t *Data, uint64_t Size);
/// \endcode{.cpp} /// \endcode{.cpp}
/// ///
virtual Expected<tpctypes::WrapperFunctionResult> virtual Expected<shared::WrapperFunctionResult>
runWrapper(JITTargetAddress WrapperFnAddr, ArrayRef<uint8_t> ArgBuffer) = 0; runWrapper(JITTargetAddress WrapperFnAddr, ArrayRef<uint8_t> ArgBuffer) = 0;
/// Disconnect from the target process. /// Disconnect from the target process.
@ -185,7 +186,7 @@ public:
Expected<int32_t> runAsMain(JITTargetAddress MainFnAddr, Expected<int32_t> runAsMain(JITTargetAddress MainFnAddr,
ArrayRef<std::string> Args) override; ArrayRef<std::string> Args) override;
Expected<tpctypes::WrapperFunctionResult> Expected<shared::WrapperFunctionResult>
runWrapper(JITTargetAddress WrapperFnAddr, runWrapper(JITTargetAddress WrapperFnAddr,
ArrayRef<uint8_t> ArgBuffer) override; ArrayRef<uint8_t> ArgBuffer) override;

View File

@ -1,7 +1,6 @@
add_llvm_component_library(LLVMOrcShared add_llvm_component_library(LLVMOrcShared
OrcError.cpp OrcError.cpp
RPCError.cpp RPCError.cpp
TargetProcessControlTypes.cpp
ADDITIONAL_HEADER_DIRS ADDITIONAL_HEADER_DIRS
${LLVM_MAIN_INCLUDE_DIR}/llvm/ExecutionEngine/Orc ${LLVM_MAIN_INCLUDE_DIR}/llvm/ExecutionEngine/Orc

View File

@ -1,44 +0,0 @@
//===---------- TargetProcessControlTypes.cpp - Shared TPC types ----------===//
//
// 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
//
//===----------------------------------------------------------------------===//
//
// TargetProcessControl types.
//
//===----------------------------------------------------------------------===//
#include "llvm/ExecutionEngine/Orc/Shared/TargetProcessControlTypes.h"
namespace llvm {
namespace orc {
namespace tpctypes {
WrapperFunctionResult WrapperFunctionResult::from(StringRef S) {
CWrapperFunctionResult R;
zeroInit(R);
R.Size = S.size();
if (R.Size > sizeof(uint64_t)) {
R.Data.ValuePtr = new uint8_t[R.Size];
memcpy(R.Data.ValuePtr, S.data(), R.Size);
R.Destroy = destroyWithDeleteArray;
} else
memcpy(R.Data.Value, S.data(), R.Size);
return R;
}
void WrapperFunctionResult::destroyWithFree(CWrapperFunctionResultData Data,
uint64_t Size) {
free(Data.ValuePtr);
}
void WrapperFunctionResult::destroyWithDeleteArray(
CWrapperFunctionResultData Data, uint64_t Size) {
delete[] Data.ValuePtr;
}
} // end namespace tpctypes
} // end namespace orc
} // end namespace llvm

View File

@ -68,26 +68,11 @@ using namespace llvm;
// Serialize rendezvous with the debugger as well as access to shared data. // Serialize rendezvous with the debugger as well as access to shared data.
ManagedStatic<std::mutex> JITDebugLock; ManagedStatic<std::mutex> JITDebugLock;
static std::pair<const char *, uint64_t> readDebugObjectInfo(uint8_t *ArgData, // Register debug object, return error message or null for success.
uint64_t ArgSize) { static void registerJITLoaderGDBImpl(JITTargetAddress Addr, uint64_t Size) {
BinaryStreamReader ArgReader(ArrayRef<uint8_t>(ArgData, ArgSize),
support::endianness::big);
uint64_t Addr, Size;
cantFail(ArgReader.readInteger(Addr));
cantFail(ArgReader.readInteger(Size));
return std::make_pair(jitTargetAddressToPointer<const char *>(Addr), Size);
}
extern "C" orc::tpctypes::CWrapperFunctionResult
llvm_orc_registerJITLoaderGDBWrapper(uint8_t *Data, uint64_t Size) {
if (Size != sizeof(uint64_t) + sizeof(uint64_t))
return orc::tpctypes::WrapperFunctionResult::from(
"Invalid arguments to llvm_orc_registerJITLoaderGDBWrapper")
.release();
jit_code_entry *E = new jit_code_entry; jit_code_entry *E = new jit_code_entry;
std::tie(E->symfile_addr, E->symfile_size) = readDebugObjectInfo(Data, Size); E->symfile_addr = jitTargetAddressToPointer<const char *>(Addr);
E->symfile_size = Size;
E->prev_entry = nullptr; E->prev_entry = nullptr;
std::lock_guard<std::mutex> Lock(*JITDebugLock); std::lock_guard<std::mutex> Lock(*JITDebugLock);
@ -105,6 +90,12 @@ llvm_orc_registerJITLoaderGDBWrapper(uint8_t *Data, uint64_t Size) {
// Run into the rendezvous breakpoint. // Run into the rendezvous breakpoint.
__jit_debug_descriptor.action_flag = JIT_REGISTER_FN; __jit_debug_descriptor.action_flag = JIT_REGISTER_FN;
__jit_debug_register_code(); __jit_debug_register_code();
}
return orc::tpctypes::WrapperFunctionResult().release();
extern "C" orc::shared::detail::CWrapperFunctionResult
llvm_orc_registerJITLoaderGDBWrapper(const char *Data, uint64_t Size) {
using namespace orc::shared;
return WrapperFunction<void(SPSTargetAddress, uint64_t)>::handle(
Data, Size, registerJITLoaderGDBImpl)
.release();
} }

View File

@ -23,7 +23,7 @@
using namespace llvm; using namespace llvm;
using namespace llvm::orc; using namespace llvm::orc;
using namespace llvm::orc::tpctypes; using namespace llvm::orc::shared;
namespace llvm { namespace llvm {
namespace orc { namespace orc {
@ -155,54 +155,26 @@ Error deregisterEHFrameSection(const void *EHFrameSectionAddr,
} // end namespace orc } // end namespace orc
} // end namespace llvm } // end namespace llvm
extern "C" CWrapperFunctionResult static Error registerEHFrameWrapper(JITTargetAddress Addr, uint64_t Size) {
llvm_orc_registerEHFrameSectionWrapper(uint8_t *Data, uint64_t Size) { return llvm::orc::registerEHFrameSection(
if (Size != sizeof(uint64_t) + sizeof(uint64_t)) jitTargetAddressToPointer<const void *>(Addr), Size);
return WrapperFunctionResult::from(
"Invalid arguments to llvm_orc_registerEHFrameSectionWrapper")
.release();
uint64_t EHFrameSectionAddr;
uint64_t EHFrameSectionSize;
{
BinaryStreamReader ArgReader(ArrayRef<uint8_t>(Data, Size),
support::endianness::big);
cantFail(ArgReader.readInteger(EHFrameSectionAddr));
cantFail(ArgReader.readInteger(EHFrameSectionSize));
}
if (auto Err = registerEHFrameSection(
jitTargetAddressToPointer<void *>(EHFrameSectionAddr),
EHFrameSectionSize)) {
auto ErrMsg = toString(std::move(Err));
return WrapperFunctionResult::from(ErrMsg).release();
}
return WrapperFunctionResult().release();
} }
extern "C" CWrapperFunctionResult static Error deregisterEHFrameWrapper(JITTargetAddress Addr, uint64_t Size) {
llvm_orc_deregisterEHFrameSectionWrapper(uint8_t *Data, uint64_t Size) { return llvm::orc::deregisterEHFrameSection(
if (Size != sizeof(uint64_t) + sizeof(uint64_t)) jitTargetAddressToPointer<const void *>(Addr), Size);
return WrapperFunctionResult::from( }
"Invalid arguments to llvm_orc_registerEHFrameSectionWrapper")
.release(); extern "C" orc::shared::detail::CWrapperFunctionResult
llvm_orc_registerEHFrameSectionWrapper(const char *Data, uint64_t Size) {
uint64_t EHFrameSectionAddr; return WrapperFunction<SPSError(SPSTargetAddress, uint64_t)>::handle(
uint64_t EHFrameSectionSize; Data, Size, registerEHFrameWrapper)
.release();
{ }
BinaryStreamReader ArgReader(ArrayRef<uint8_t>(Data, Size),
support::endianness::big); extern "C" orc::shared::detail::CWrapperFunctionResult
cantFail(ArgReader.readInteger(EHFrameSectionAddr)); llvm_orc_deregisterEHFrameSectionWrapper(const char *Data, uint64_t Size) {
cantFail(ArgReader.readInteger(EHFrameSectionSize)); return WrapperFunction<SPSError(SPSTargetAddress, uint64_t)>::handle(
} Data, Size, deregisterEHFrameWrapper)
.release();
if (auto Err = deregisterEHFrameSection(
jitTargetAddressToPointer<void *>(EHFrameSectionAddr),
EHFrameSectionSize)) {
auto ErrMsg = toString(std::move(Err));
return WrapperFunctionResult::from(ErrMsg).release();
}
return WrapperFunctionResult().release();
} }

View File

@ -102,13 +102,14 @@ SelfTargetProcessControl::runAsMain(JITTargetAddress MainFnAddr,
return orc::runAsMain(jitTargetAddressToFunction<MainTy>(MainFnAddr), Args); return orc::runAsMain(jitTargetAddressToFunction<MainTy>(MainFnAddr), Args);
} }
Expected<tpctypes::WrapperFunctionResult> Expected<shared::WrapperFunctionResult>
SelfTargetProcessControl::runWrapper(JITTargetAddress WrapperFnAddr, SelfTargetProcessControl::runWrapper(JITTargetAddress WrapperFnAddr,
ArrayRef<uint8_t> ArgBuffer) { ArrayRef<uint8_t> ArgBuffer) {
using WrapperFnTy = using WrapperFnTy = shared::detail::CWrapperFunctionResult (*)(
tpctypes::CWrapperFunctionResult (*)(const uint8_t *Data, uint64_t Size); const char *Data, uint64_t Size);
auto *WrapperFn = jitTargetAddressToFunction<WrapperFnTy>(WrapperFnAddr); auto *WrapperFn = jitTargetAddressToFunction<WrapperFnTy>(WrapperFnAddr);
return WrapperFn(ArgBuffer.data(), ArgBuffer.size()); return WrapperFn(reinterpret_cast<const char *>(ArgBuffer.data()),
ArgBuffer.size());
} }
Error SelfTargetProcessControl::disconnect() { return Error::success(); } Error SelfTargetProcessControl::disconnect() { return Error::success(); }

View File

@ -26,8 +26,10 @@ add_llvm_unittest(OrcJITTests
ResourceTrackerTest.cpp ResourceTrackerTest.cpp
RPCUtilsTest.cpp RPCUtilsTest.cpp
RTDyldObjectLinkingLayerTest.cpp RTDyldObjectLinkingLayerTest.cpp
SimplePackedSerializationTest.cpp
SymbolStringPoolTest.cpp SymbolStringPoolTest.cpp
ThreadSafeModuleTest.cpp ThreadSafeModuleTest.cpp
WrapperFunctionUtilsTest.cpp
) )
target_link_libraries(OrcJITTests PRIVATE target_link_libraries(OrcJITTests PRIVATE

View File

@ -0,0 +1,160 @@
//===-------- SimplePackedSerializationTest.cpp - Test SPS scheme ---------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "llvm/ExecutionEngine/Orc/Shared/SimplePackedSerialization.h"
#include "gtest/gtest.h"
using namespace llvm;
using namespace llvm::orc::shared;
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, StringRef>(StringRef(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);
}

View File

@ -0,0 +1,75 @@
//===----- WrapperFunctionUtilsTest.cpp - Test Wrapper-Function utils -----===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "llvm/ExecutionEngine/Orc/Shared/WrapperFunctionUtils.h"
#include "gtest/gtest.h"
using namespace llvm;
using namespace llvm::orc::shared;
namespace {
constexpr const char *TestString = "test string";
} // end anonymous namespace
TEST(WrapperFunctionUtilsTest, DefaultWrapperFunctionResult) {
WrapperFunctionResult R;
EXPECT_TRUE(R.empty());
EXPECT_EQ(R.size(), 0U);
EXPECT_EQ(R.getOutOfBandError(), nullptr);
}
TEST(WrapperFunctionUtilsTest, WrapperFunctionResultFromRange) {
auto R = WrapperFunctionResult::copyFrom(TestString, strlen(TestString) + 1);
EXPECT_EQ(R.size(), strlen(TestString) + 1);
EXPECT_TRUE(strcmp(R.data(), TestString) == 0);
EXPECT_FALSE(R.empty());
EXPECT_EQ(R.getOutOfBandError(), nullptr);
}
TEST(WrapperFunctionUtilsTest, WrapperFunctionResultFromCString) {
auto R = WrapperFunctionResult::copyFrom(TestString);
EXPECT_EQ(R.size(), strlen(TestString) + 1);
EXPECT_TRUE(strcmp(R.data(), TestString) == 0);
EXPECT_FALSE(R.empty());
EXPECT_EQ(R.getOutOfBandError(), nullptr);
}
TEST(WrapperFunctionUtilsTest, WrapperFunctionResultFromStdString) {
auto R = WrapperFunctionResult::copyFrom(std::string(TestString));
EXPECT_EQ(R.size(), strlen(TestString) + 1);
EXPECT_TRUE(strcmp(R.data(), TestString) == 0);
EXPECT_FALSE(R.empty());
EXPECT_EQ(R.getOutOfBandError(), nullptr);
}
TEST(WrapperFunctionUtilsTest, WrapperFunctionResultFromOutOfBandError) {
auto R = WrapperFunctionResult::createOutOfBandError(TestString);
EXPECT_FALSE(R.empty());
EXPECT_TRUE(strcmp(R.getOutOfBandError(), TestString) == 0);
}
static WrapperFunctionResult voidNoopWrapper(const char *ArgData,
size_t ArgSize) {
return WrapperFunction<void()>::handle(ArgData, ArgSize, voidNoop);
}
static WrapperFunctionResult addWrapper(const char *ArgData, size_t ArgSize) {
return WrapperFunction<int32_t(int32_t, int32_t)>::handle(
ArgData, ArgSize, [](int32_t X, int32_t Y) -> int32_t { return X + Y; });
}
TEST(WrapperFunctionUtilsTest, WrapperFunctionCallVoidNoopAndHandle) {
EXPECT_FALSE(!!WrapperFunction<void()>::call((void *)&voidNoopWrapper));
}
TEST(WrapperFunctionUtilsTest, WrapperFunctionCallAndHandle) {
int32_t Result;
EXPECT_FALSE(!!WrapperFunction<int32_t(int32_t, int32_t)>::call(
addWrapper, Result, 1, 2));
EXPECT_EQ(Result, (int32_t)3);
}