154 lines
5.5 KiB
C++
154 lines
5.5 KiB
C++
//===------ CallViaEPC.h - Call wrapper functions via EPC -------*- 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// Call executor functions with common signatures via
|
|
// ExecutorProcessControl::callWrapperAsync.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#ifndef LLVM_EXECUTIONENGINE_ORC_CALLVIAEPC_H
|
|
#define LLVM_EXECUTIONENGINE_ORC_CALLVIAEPC_H
|
|
|
|
#include "llvm/ExecutionEngine/Orc/CallableTraitsHelper.h"
|
|
#include "llvm/ExecutionEngine/Orc/ExecutorProcessControl.h"
|
|
#include "llvm/Support/Error.h"
|
|
#include "llvm/Support/MSVCErrorWorkarounds.h"
|
|
|
|
#include <type_traits>
|
|
|
|
namespace llvm::orc {
|
|
|
|
namespace detail {
|
|
|
|
template <typename HandlerArgT> struct CallViaEPCRetValueTraits;
|
|
|
|
template <typename RetT> struct CallViaEPCRetValueTraits<Expected<RetT>> {
|
|
using value_type = RetT;
|
|
};
|
|
|
|
template <> struct CallViaEPCRetValueTraits<Error> {
|
|
using value_type = void;
|
|
};
|
|
|
|
template <typename RetT> struct CallViaEPCRetValueTraits<MSVCPExpected<RetT>> {
|
|
using value_type = RetT;
|
|
};
|
|
|
|
template <> struct CallViaEPCRetValueTraits<MSVCPError> {
|
|
using value_type = void;
|
|
};
|
|
|
|
// Helper to extract the argument type from a handler callable.
|
|
template <typename HandlerT> struct CallViaEPCHandlerTraits {
|
|
using ArgInfo = CallableArgInfo<HandlerT>;
|
|
using ArgsTuple = typename ArgInfo::ArgsTupleType;
|
|
static_assert(std::tuple_size_v<ArgsTuple> == 1,
|
|
"Handler must take exactly one argument");
|
|
using HandlerArgT = std::tuple_element_t<0, ArgsTuple>;
|
|
using RetT = typename CallViaEPCRetValueTraits<
|
|
std::remove_cv_t<std::remove_reference_t<HandlerArgT>>>::value_type;
|
|
};
|
|
|
|
} // namespace detail
|
|
|
|
/// Call a wrapper function via EPC asynchronously.
|
|
template <typename HandlerFn, typename Serializer, typename... ArgTs>
|
|
std::enable_if_t<std::is_invocable_v<HandlerFn, Error>>
|
|
callViaEPC(HandlerFn &&H, ExecutorProcessControl &EPC, Serializer S,
|
|
ExecutorSymbolDef Fn, ArgTs &&...Args) {
|
|
using RetT = typename detail::CallViaEPCHandlerTraits<HandlerFn>::RetT;
|
|
|
|
if (auto ArgBytes = S.serialize(std::forward<ArgTs>(Args)...))
|
|
EPC.callWrapperAsync(
|
|
Fn.getAddress(),
|
|
[S = std::move(S), H = std::forward<HandlerFn>(H)](
|
|
shared::WrapperFunctionBuffer R) mutable {
|
|
if (const char *ErrMsg = R.getOutOfBandError())
|
|
H(make_error<StringError>(ErrMsg, inconvertibleErrorCode()));
|
|
else
|
|
H(S.template deserialize<RetT>(std::move(R)));
|
|
},
|
|
{ArgBytes->data(), ArgBytes->size()});
|
|
else
|
|
H(ArgBytes.takeError());
|
|
}
|
|
|
|
/// Call a wrapper function via EPC synchronously using the given promise.
|
|
///
|
|
/// This performs a blocking call by making an asynchronous call to set the
|
|
/// promise and waiting on a future.
|
|
///
|
|
/// Blocking calls should only be used for convenience by ORC clients, never
|
|
/// internally.
|
|
template <typename PromiseT, typename Serializer, typename... ArgTs>
|
|
std::enable_if_t<!std::is_invocable_v<PromiseT, Error>,
|
|
decltype(std::declval<PromiseT>().get_future().get())>
|
|
callViaEPC(PromiseT &&P, ExecutorProcessControl &EPC, Serializer S,
|
|
ExecutorSymbolDef Fn, ArgTs &&...Args) {
|
|
auto F = P.get_future();
|
|
using RetT = decltype(F.get());
|
|
callViaEPC([P = std::move(P)](RetT R) mutable { P.set_value(std::move(R)); },
|
|
EPC, std::move(S), std::move(Fn), std::forward<ArgTs>(Args)...);
|
|
return F.get();
|
|
}
|
|
|
|
/// Encapsulates calls via EPC to any function that's compatible with the given
|
|
/// serialization scheme.
|
|
template <typename Serializer> class EPCCaller {
|
|
public:
|
|
EPCCaller(ExecutorProcessControl &EPC, Serializer &&S)
|
|
: EPC(EPC), S(std::move(S)) {}
|
|
|
|
// TODO: Add an ExecutionSession constructor once ExecutionSession has been
|
|
// moved to its own header.
|
|
|
|
// Make a call to the given function using callViaEPC.
|
|
//
|
|
// The PromiseOrHandlerT value is forwarded. Its type will determine both the
|
|
// return value type and the dispatch method (asynchronous vs synchronous).
|
|
template <typename PromiseOrHandlerT, typename... ArgTs>
|
|
decltype(auto) operator()(PromiseOrHandlerT &&R, ExecutorSymbolDef Fn,
|
|
ArgTs &&...Args) {
|
|
return callViaEPC(std::forward<PromiseOrHandlerT>(R), EPC, S, Fn,
|
|
std::forward<ArgTs>(Args)...);
|
|
}
|
|
|
|
private:
|
|
ExecutorProcessControl &EPC;
|
|
Serializer S;
|
|
};
|
|
|
|
/// Encapsulates calls via EPC to a specific function, using the given
|
|
/// serialization scheme.
|
|
template <typename Serializer> class EPCCall {
|
|
public:
|
|
EPCCall(ExecutorProcessControl &EPC, Serializer &&S, ExecutorSymbolDef Fn)
|
|
: Caller(EPC, std::move(S)), Fn(std::move(Fn)) {}
|
|
|
|
// TODO: Add an ExecutionSession constructor once ExecutionSession has been
|
|
// moved to its own header.
|
|
|
|
// Make a call using callViaEPC.
|
|
//
|
|
// The PromiseOrHandlerT value is forwarded. Its type will determine both the
|
|
// return value type and the dispatch method (asynchronous vs synchronous).
|
|
template <typename PromiseOrHandlerT, typename... ArgTs>
|
|
decltype(auto) operator()(PromiseOrHandlerT &&R, ArgTs &&...Args) {
|
|
return Caller(std::forward<PromiseOrHandlerT>(R), Fn,
|
|
std::forward<ArgTs>(Args)...);
|
|
}
|
|
|
|
private:
|
|
EPCCaller<Serializer> Caller;
|
|
ExecutorSymbolDef Fn;
|
|
};
|
|
|
|
} // namespace llvm::orc
|
|
|
|
#endif // LLVM_EXECUTIONENGINE_ORC_CALLVIAEPC_H
|