[llvm] [ORC] Add void function support to CallViaEPC, CallSPSViaEPC. (PR #170800)

Lang Hames via llvm-commits llvm-commits at lists.llvm.org
Thu Dec 4 21:11:09 PST 2025


https://github.com/lhames created https://github.com/llvm/llvm-project/pull/170800

Adds support for calling void functions. Calls to void functions return Error to capture any IPC/RPC failure.

>From e0989ef495c837343e4917f2a83389a672d8cfc7 Mon Sep 17 00:00:00 2001
From: Lang Hames <lhames at gmail.com>
Date: Fri, 5 Dec 2025 07:53:08 +1100
Subject: [PATCH] [ORC] Add void function support to CallViaEPC, CallSPSViaEPC.

Adds support for calling void functions. Calls to void functions return Error
to capture any IPC/RPC failure.
---
 .../llvm/ExecutionEngine/Orc/CallSPSViaEPC.h  | 44 +++++++++++++------
 .../llvm/ExecutionEngine/Orc/CallViaEPC.h     | 30 ++++++++++---
 .../ExecutionEngine/Orc/CallSPSViaEPCTest.cpp | 26 +++++++++++
 3 files changed, 80 insertions(+), 20 deletions(-)

diff --git a/llvm/include/llvm/ExecutionEngine/Orc/CallSPSViaEPC.h b/llvm/include/llvm/ExecutionEngine/Orc/CallSPSViaEPC.h
index 978c9be06ad98..768d0b3ec1000 100644
--- a/llvm/include/llvm/ExecutionEngine/Orc/CallSPSViaEPC.h
+++ b/llvm/include/llvm/ExecutionEngine/Orc/CallSPSViaEPC.h
@@ -20,22 +20,13 @@
 namespace llvm::orc {
 
 namespace detail {
-template <typename RetT, typename... ArgTs> struct SPSCallSerializationImpl {
-  using RetSerialization = shared::SPSArgList<RetT>;
-  using ArgSerialization = shared::SPSArgList<ArgTs...>;
-};
-} // namespace detail
-
-template <typename SPSSig>
-struct SPSCallSerialization
-    : public CallableTraitsHelper<detail::SPSCallSerializationImpl, SPSSig> {};
+template <typename SPSRetT, typename... SPSArgTs>
+struct SPSCallSerializationImpl {
+  using RetSerialization = shared::SPSArgList<SPSRetT>;
+  using ArgSerialization = shared::SPSArgList<SPSArgTs...>;
 
-template <typename SPSSig> class SPSCallSerializer {
-public:
   template <typename... ArgTs>
   Expected<shared::WrapperFunctionResult> serialize(ArgTs &&...Args) {
-    using ArgSerialization =
-        typename SPSCallSerialization<SPSSig>::ArgSerialization;
     auto Buffer = shared::WrapperFunctionResult::allocate(
         ArgSerialization::size(Args...));
     shared::SPSOutputBuffer OB(Buffer.data(), Buffer.size());
@@ -44,11 +35,22 @@ template <typename SPSSig> class SPSCallSerializer {
                                      inconvertibleErrorCode());
     return std::move(Buffer);
   }
+};
+
+template <typename SPSSig>
+struct SPSCallSerialization
+    : public CallableTraitsHelper<detail::SPSCallSerializationImpl, SPSSig> {};
+
+} // namespace detail
+
+/// SPS serialization for non-void calls.
+template <typename SPSSig>
+struct SPSCallSerializer : public detail::SPSCallSerialization<SPSSig> {
 
   template <typename RetT>
   Expected<RetT> deserialize(shared::WrapperFunctionResult ResultBytes) {
     using RetDeserialization =
-        typename SPSCallSerialization<SPSSig>::RetSerialization;
+        typename detail::SPSCallSerialization<SPSSig>::RetSerialization;
     shared::SPSInputBuffer IB(ResultBytes.data(), ResultBytes.size());
     RetT ReturnValue;
     if (!RetDeserialization::deserialize(IB, ReturnValue))
@@ -58,6 +60,20 @@ template <typename SPSSig> class SPSCallSerializer {
   }
 };
 
+/// SPS serialization for void calls.
+template <typename... SPSArgTs>
+struct SPSCallSerializer<void(SPSArgTs...)>
+    : public detail::SPSCallSerialization<void(SPSArgTs...)> {
+  template <typename RetT>
+  std::enable_if_t<std::is_void_v<RetT>, Error>
+  deserialize(shared::WrapperFunctionResult ResultBytes) {
+    if (!ResultBytes.empty())
+      return make_error<StringError>("Could not deserialize return value",
+                                     inconvertibleErrorCode());
+    return Error::success();
+  }
+};
+
 template <typename SPSSig>
 class SPSEPCCaller : public EPCCaller<SPSCallSerializer<SPSSig>> {
 public:
diff --git a/llvm/include/llvm/ExecutionEngine/Orc/CallViaEPC.h b/llvm/include/llvm/ExecutionEngine/Orc/CallViaEPC.h
index 1ec422f9da716..b825d82e1d5fd 100644
--- a/llvm/include/llvm/ExecutionEngine/Orc/CallViaEPC.h
+++ b/llvm/include/llvm/ExecutionEngine/Orc/CallViaEPC.h
@@ -25,15 +25,33 @@ namespace llvm::orc {
 
 namespace detail {
 
-// Helper to extract the Expected<T> argument type from a handler callable.
-template <typename HandlerT> struct HandlerTraits {
+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 ExpectedArgType = std::tuple_element_t<0, ArgsTuple>;
-  using RetT = typename std::remove_cv_t<
-      std::remove_reference_t<ExpectedArgType>>::value_type;
+  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
@@ -43,7 +61,7 @@ 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::HandlerTraits<HandlerFn>::RetT;
+  using RetT = typename detail::CallViaEPCHandlerTraits<HandlerFn>::RetT;
 
   if (auto ArgBytes = S.serialize(std::forward<ArgTs>(Args)...))
     EPC.callWrapperAsync(
diff --git a/llvm/unittests/ExecutionEngine/Orc/CallSPSViaEPCTest.cpp b/llvm/unittests/ExecutionEngine/Orc/CallSPSViaEPCTest.cpp
index 25764bbe0d7e1..7e3d468ac8552 100644
--- a/llvm/unittests/ExecutionEngine/Orc/CallSPSViaEPCTest.cpp
+++ b/llvm/unittests/ExecutionEngine/Orc/CallSPSViaEPCTest.cpp
@@ -19,6 +19,10 @@ using namespace llvm;
 using namespace llvm::orc;
 using namespace llvm::orc::shared;
 
+static CWrapperFunctionResult voidWrapper(const char *ArgData, size_t ArgSize) {
+  return WrapperFunction<void()>::handle(ArgData, ArgSize, []() {}).release();
+}
+
 static CWrapperFunctionResult mainWrapper(const char *ArgData, size_t ArgSize) {
   return WrapperFunction<int32_t(SPSSequence<SPSString>)>::handle(
              ArgData, ArgSize,
@@ -28,6 +32,28 @@ static CWrapperFunctionResult mainWrapper(const char *ArgData, size_t ArgSize) {
       .release();
 }
 
+TEST(CallSPSViaEPCTest, CallVoidViaCallerAsync) {
+  auto EPC = cantFail(SelfExecutorProcessControl::Create());
+  SPSEPCCaller<void()> C(*EPC);
+
+  Error Err = Error::success();
+  {
+    ErrorAsOutParameter _(Err);
+    C([&](Error E) { Err = std::move(E); },
+      ExecutorSymbolDef::fromPtr(voidWrapper));
+  }
+  EXPECT_THAT_ERROR(std::move(Err), Succeeded());
+}
+
+TEST(CallSPSViaEPCTest, CallVoidViaCallerSync) {
+  auto EPC = cantFail(SelfExecutorProcessControl::Create());
+  SPSEPCCaller<void()> C(*EPC);
+
+  Error Err =
+      C(std::promise<MSVCPError>(), ExecutorSymbolDef::fromPtr(voidWrapper));
+  EXPECT_THAT_ERROR(std::move(Err), Succeeded());
+}
+
 TEST(CallSPSViaEPCTest, CallMainViaCallerAsync) {
   auto EPC = cantFail(SelfExecutorProcessControl::Create());
   SPSEPCCaller<int32_t(SPSSequence<SPSString>)> C(*EPC);



More information about the llvm-commits mailing list