return Result;
}
- Expected<tpctypes::WrapperFunctionResult>
+ Expected<shared::WrapperFunctionResult>
runWrapper(JITTargetAddress WrapperFnAddr,
ArrayRef<uint8_t> ArgBuffer) override {
DEBUG_WITH_TYPE("orc", {
});
auto Result =
EP.template callB<orcrpctpc::RunWrapper>(WrapperFnAddr, ArgBuffer);
- // dbgs() << "Returned from runWrapper...\n";
return Result;
}
--- /dev/null
+//===---- 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
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 orc
} // end namespace llvm
--- /dev/null
+//===- 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
#ifndef 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>
-extern "C" llvm::orc::tpctypes::CWrapperFunctionResult
-llvm_orc_registerJITLoaderGDBWrapper(uint8_t *Data, uint64_t Size);
+extern "C" llvm::orc::shared::detail::CWrapperFunctionResult
+llvm_orc_registerJITLoaderGDBWrapper(const char *Data, uint64_t Size);
#endif // LLVM_EXECUTIONENGINE_ORC_TARGETPROCESS_JITLOADERGDB_H
#include "llvm/ExecutionEngine/Orc/Shared/RPCUtils.h"
#include "llvm/ExecutionEngine/Orc/Shared/RawByteChannel.h"
#include "llvm/ExecutionEngine/Orc/Shared/TargetProcessControlTypes.h"
+#include "llvm/ExecutionEngine/Orc/Shared/WrapperFunctionUtils.h"
#include "llvm/ExecutionEngine/Orc/TargetProcess/TargetExecutionUtils.h"
#include "llvm/Support/DynamicLibrary.h"
#include "llvm/Support/FormatVariadic.h"
static const char *getName() { return "ReleaseOrFinalizeMemRequestElement"; }
};
-template <> class SerializationTypeName<tpctypes::WrapperFunctionResult> {
+template <> class SerializationTypeName<shared::WrapperFunctionResult> {
public:
static const char *getName() { return "WrapperFunctionResult"; }
};
template <typename ChannelT>
class SerializationTraits<
- ChannelT, tpctypes::WrapperFunctionResult, tpctypes::WrapperFunctionResult,
+ ChannelT, shared::WrapperFunctionResult, shared::WrapperFunctionResult,
std::enable_if_t<std::is_base_of<RawByteChannel, ChannelT>::value>> {
public:
- static Error serialize(ChannelT &C,
- const tpctypes::WrapperFunctionResult &E) {
- auto Data = E.getData();
- if (auto Err = serializeSeq(C, static_cast<uint64_t>(Data.size())))
+ static Error serialize(ChannelT &C, const shared::WrapperFunctionResult &E) {
+ if (auto Err = serializeSeq(C, static_cast<uint64_t>(E.size())))
return Err;
- if (Data.size() == 0)
+ if (E.size() == 0)
return Error::success();
- return C.appendBytes(reinterpret_cast<const char *>(Data.data()),
- Data.size());
+ return C.appendBytes(E.data(), E.size());
}
- static Error deserialize(ChannelT &C, tpctypes::WrapperFunctionResult &E) {
- tpctypes::CWrapperFunctionResult R;
-
- R.Size = 0;
- R.Data.ValuePtr = nullptr;
- R.Destroy = nullptr;
+ static Error deserialize(ChannelT &C, shared::WrapperFunctionResult &E) {
- if (auto Err = deserializeSeq(C, R.Size))
- 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;
+ uint64_t Size;
+ if (auto Err = deserializeSeq(C, Size))
return Err;
- }
- E = tpctypes::WrapperFunctionResult(R);
- return Error::success();
+ char *DataPtr = shared::WrapperFunctionResult::allocate(E, Size);
+ return C.readBytes(DataPtr, E.size());
}
};
class RunWrapper
: public shared::RPCFunction<RunWrapper,
- tpctypes::WrapperFunctionResult(
+ shared::WrapperFunctionResult(
JITTargetAddress, std::vector<uint8_t>)> {
public:
static const char *getName() { return "RunWrapper"; }
ProgramNameOverride);
}
- tpctypes::WrapperFunctionResult
+ shared::WrapperFunctionResult
runWrapper(JITTargetAddress WrapperFnAddr,
const std::vector<uint8_t> &ArgBuffer) {
- using WrapperFnTy = tpctypes::CWrapperFunctionResult (*)(
- const uint8_t *Data, uint64_t Size);
+ using WrapperFnTy = shared::detail::CWrapperFunctionResult (*)(
+ const char *Data, uint64_t Size);
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; }
// Support for dynamically registering and deregistering eh-frame sections
// in-process via libunwind.
//
+// FIXME: The functionality in this file should be moved to the ORC runtime.
+//
//===----------------------------------------------------------------------===//
#ifndef 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"
namespace llvm {
} // end namespace orc
} // end namespace llvm
-extern "C" llvm::orc::tpctypes::CWrapperFunctionResult
-llvm_orc_registerEHFrameSectionWrapper(uint8_t *Data, uint64_t Size);
+extern "C" llvm::orc::shared::detail::CWrapperFunctionResult
+llvm_orc_registerEHFrameSectionWrapper(const char *Data, uint64_t Size);
-extern "C" llvm::orc::tpctypes::CWrapperFunctionResult
-llvm_orc_deregisterEHFrameSectionWrapper(uint8_t *Data, uint64_t Size);
+extern "C" llvm::orc::shared::detail::CWrapperFunctionResult
+llvm_orc_deregisterEHFrameSectionWrapper(const char *Data, uint64_t Size);
#endif // LLVM_EXECUTIONENGINE_ORC_TARGETPROCESS_REGISTEREHFRAMES_H
#include "llvm/ExecutionEngine/JITLink/JITLinkMemoryManager.h"
#include "llvm/ExecutionEngine/Orc/Core.h"
#include "llvm/ExecutionEngine/Orc/Shared/TargetProcessControlTypes.h"
+#include "llvm/ExecutionEngine/Orc/Shared/WrapperFunctionUtils.h"
#include "llvm/Support/DynamicLibrary.h"
#include "llvm/Support/MSVCErrorWorkarounds.h"
virtual Expected<int32_t> runAsMain(JITTargetAddress MainFnAddr,
ArrayRef<std::string> Args) = 0;
- /// Run a wrapper function with signature:
+ /// Run a wrapper function in the executor.
///
/// \code{.cpp}
/// CWrapperFunctionResult fn(uint8_t *Data, uint64_t Size);
/// \endcode{.cpp}
///
- virtual Expected<tpctypes::WrapperFunctionResult>
+ virtual Expected<shared::WrapperFunctionResult>
runWrapper(JITTargetAddress WrapperFnAddr, ArrayRef<uint8_t> ArgBuffer) = 0;
/// Disconnect from the target process.
Expected<int32_t> runAsMain(JITTargetAddress MainFnAddr,
ArrayRef<std::string> Args) override;
- Expected<tpctypes::WrapperFunctionResult>
+ Expected<shared::WrapperFunctionResult>
runWrapper(JITTargetAddress WrapperFnAddr,
ArrayRef<uint8_t> ArgBuffer) override;
add_llvm_component_library(LLVMOrcShared
OrcError.cpp
RPCError.cpp
- TargetProcessControlTypes.cpp
ADDITIONAL_HEADER_DIRS
${LLVM_MAIN_INCLUDE_DIR}/llvm/ExecutionEngine/Orc
+++ /dev/null
-//===---------- 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
// Serialize rendezvous with the debugger as well as access to shared data.
ManagedStatic<std::mutex> JITDebugLock;
-static std::pair<const char *, uint64_t> readDebugObjectInfo(uint8_t *ArgData,
- uint64_t ArgSize) {
- 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();
-
+// Register debug object, return error message or null for success.
+static void registerJITLoaderGDBImpl(JITTargetAddress Addr, uint64_t Size) {
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;
std::lock_guard<std::mutex> Lock(*JITDebugLock);
// Run into the rendezvous breakpoint.
__jit_debug_descriptor.action_flag = JIT_REGISTER_FN;
__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();
}
using namespace llvm;
using namespace llvm::orc;
-using namespace llvm::orc::tpctypes;
+using namespace llvm::orc::shared;
namespace llvm {
namespace orc {
} // end namespace orc
} // end namespace llvm
-extern "C" CWrapperFunctionResult
-llvm_orc_registerEHFrameSectionWrapper(uint8_t *Data, uint64_t Size) {
- if (Size != sizeof(uint64_t) + sizeof(uint64_t))
- 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));
- }
+static Error registerEHFrameWrapper(JITTargetAddress Addr, uint64_t Size) {
+ return llvm::orc::registerEHFrameSection(
+ jitTargetAddressToPointer<const void *>(Addr), Size);
+}
- if (auto Err = registerEHFrameSection(
- jitTargetAddressToPointer<void *>(EHFrameSectionAddr),
- EHFrameSectionSize)) {
- auto ErrMsg = toString(std::move(Err));
- return WrapperFunctionResult::from(ErrMsg).release();
- }
- return WrapperFunctionResult().release();
+static Error deregisterEHFrameWrapper(JITTargetAddress Addr, uint64_t Size) {
+ return llvm::orc::deregisterEHFrameSection(
+ jitTargetAddressToPointer<const void *>(Addr), Size);
}
-extern "C" CWrapperFunctionResult
-llvm_orc_deregisterEHFrameSectionWrapper(uint8_t *Data, uint64_t Size) {
- if (Size != sizeof(uint64_t) + sizeof(uint64_t))
- 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));
- }
+extern "C" orc::shared::detail::CWrapperFunctionResult
+llvm_orc_registerEHFrameSectionWrapper(const char *Data, uint64_t Size) {
+ return WrapperFunction<SPSError(SPSTargetAddress, uint64_t)>::handle(
+ Data, Size, registerEHFrameWrapper)
+ .release();
+}
- if (auto Err = deregisterEHFrameSection(
- jitTargetAddressToPointer<void *>(EHFrameSectionAddr),
- EHFrameSectionSize)) {
- auto ErrMsg = toString(std::move(Err));
- return WrapperFunctionResult::from(ErrMsg).release();
- }
- return WrapperFunctionResult().release();
+extern "C" orc::shared::detail::CWrapperFunctionResult
+llvm_orc_deregisterEHFrameSectionWrapper(const char *Data, uint64_t Size) {
+ return WrapperFunction<SPSError(SPSTargetAddress, uint64_t)>::handle(
+ Data, Size, deregisterEHFrameWrapper)
+ .release();
}
return orc::runAsMain(jitTargetAddressToFunction<MainTy>(MainFnAddr), Args);
}
-Expected<tpctypes::WrapperFunctionResult>
+Expected<shared::WrapperFunctionResult>
SelfTargetProcessControl::runWrapper(JITTargetAddress WrapperFnAddr,
ArrayRef<uint8_t> ArgBuffer) {
- using WrapperFnTy =
- tpctypes::CWrapperFunctionResult (*)(const uint8_t *Data, uint64_t Size);
+ using WrapperFnTy = shared::detail::CWrapperFunctionResult (*)(
+ const char *Data, uint64_t Size);
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(); }
ResourceTrackerTest.cpp
RPCUtilsTest.cpp
RTDyldObjectLinkingLayerTest.cpp
+ SimplePackedSerializationTest.cpp
SymbolStringPoolTest.cpp
ThreadSafeModuleTest.cpp
+ WrapperFunctionUtilsTest.cpp
)
target_link_libraries(OrcJITTests PRIVATE
--- /dev/null
+//===-------- 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);
+}
--- /dev/null
+//===----- 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);
+}