//===------ 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 namespace llvm::orc { namespace detail { template struct CallViaEPCRetValueTraits; template struct CallViaEPCRetValueTraits> { using value_type = RetT; }; template <> struct CallViaEPCRetValueTraits { using value_type = void; }; template struct CallViaEPCRetValueTraits> { using value_type = RetT; }; template <> struct CallViaEPCRetValueTraits { using value_type = void; }; // Helper to extract the argument type from a handler callable. template struct CallViaEPCHandlerTraits { using ArgInfo = CallableArgInfo; using ArgsTuple = typename ArgInfo::ArgsTupleType; static_assert(std::tuple_size_v == 1, "Handler must take exactly one argument"); using HandlerArgT = std::tuple_element_t<0, ArgsTuple>; using RetT = typename CallViaEPCRetValueTraits< std::remove_cv_t>>::value_type; }; } // namespace detail /// Call a wrapper function via EPC asynchronously. template std::enable_if_t> callViaEPC(HandlerFn &&H, ExecutorProcessControl &EPC, Serializer S, ExecutorSymbolDef Fn, ArgTs &&...Args) { using RetT = typename detail::CallViaEPCHandlerTraits::RetT; if (auto ArgBytes = S.serialize(std::forward(Args)...)) EPC.callWrapperAsync( Fn.getAddress(), [S = std::move(S), H = std::forward(H)]( shared::WrapperFunctionBuffer R) mutable { if (const char *ErrMsg = R.getOutOfBandError()) H(make_error(ErrMsg, inconvertibleErrorCode())); else H(S.template deserialize(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 std::enable_if_t, decltype(std::declval().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(Args)...); return F.get(); } /// Encapsulates calls via EPC to any function that's compatible with the given /// serialization scheme. template 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 decltype(auto) operator()(PromiseOrHandlerT &&R, ExecutorSymbolDef Fn, ArgTs &&...Args) { return callViaEPC(std::forward(R), EPC, S, Fn, std::forward(Args)...); } private: ExecutorProcessControl &EPC; Serializer S; }; /// Encapsulates calls via EPC to a specific function, using the given /// serialization scheme. template 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 decltype(auto) operator()(PromiseOrHandlerT &&R, ArgTs &&...Args) { return Caller(std::forward(R), Fn, std::forward(Args)...); } private: EPCCaller Caller; ExecutorSymbolDef Fn; }; } // namespace llvm::orc #endif // LLVM_EXECUTIONENGINE_ORC_CALLVIAEPC_H