[llvm] 1a09ac5 - [ORC] Add CallViaEPC and CallSPSViaEPC utilities. (#170464)

via llvm-commits llvm-commits at lists.llvm.org
Wed Dec 3 16:17:31 PST 2025


Author: Lang Hames
Date: 2025-12-04T11:17:27+11:00
New Revision: 1a09ac584cee84eb79c0bc4f3b25c1c6e206078f

URL: https://github.com/llvm/llvm-project/commit/1a09ac584cee84eb79c0bc4f3b25c1c6e206078f
DIFF: https://github.com/llvm/llvm-project/commit/1a09ac584cee84eb79c0bc4f3b25c1c6e206078f.diff

LOG: [ORC] Add CallViaEPC and CallSPSViaEPC utilities. (#170464)

These utilities simplify making typed async calls via
ExecutorProcessControl::callWrapperAsync.

CallViaEPC.h provides utilities for making typed async calls using a
given Serializer to serialize arguments and deserialize results.

callViaEPC takes a result handler function object (accepting
Expected<T>), an EPC reference, a Serializer, a target function address,
and arguments. The return type T is inferred from the handler's argument
type using CallableTraitsHelper.

EPCCaller wraps an ExecutorProcessControl& and Serializer, simplifying
repeated calls with the same serialization.

EPCCall wraps an EPCCaller and target function address, simplifying
repeated calls to a specific wrapper function.

CallSPSViaEPC.h provides utilities for using CallViaEPC with SPS
serialization.

SPSCallSerializer is a CallViaEPC-compatible serializer using SPS.

SPSEPCCaller takes an SPS function signature as its template argument
and applies SPS serialization to arguments and return values.

SPSEPCCall wraps an SPSCaller and a target function address, simplifying
repeated calls to a specific SPS wrapper function.

Added: 
    llvm/include/llvm/ExecutionEngine/Orc/CallSPSViaEPC.h
    llvm/include/llvm/ExecutionEngine/Orc/CallViaEPC.h
    llvm/unittests/ExecutionEngine/Orc/CallSPSViaEPCTest.cpp

Modified: 
    llvm/unittests/ExecutionEngine/Orc/CMakeLists.txt

Removed: 
    


################################################################################
diff  --git a/llvm/include/llvm/ExecutionEngine/Orc/CallSPSViaEPC.h b/llvm/include/llvm/ExecutionEngine/Orc/CallSPSViaEPC.h
new file mode 100644
index 0000000000000..978c9be06ad98
--- /dev/null
+++ b/llvm/include/llvm/ExecutionEngine/Orc/CallSPSViaEPC.h
@@ -0,0 +1,79 @@
+//===---- CallSPSViaEPC.h - EPCCalls using SPS 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
+//
+//===----------------------------------------------------------------------===//
+//
+// EPCCalls using SimplePackedSerialization.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_EXECUTIONENGINE_ORC_CALLSPSVIAEPC_H
+#define LLVM_EXECUTIONENGINE_ORC_CALLSPSVIAEPC_H
+
+#include "llvm/ExecutionEngine/Orc/CallViaEPC.h"
+#include "llvm/ExecutionEngine/Orc/CallableTraitsHelper.h"
+#include "llvm/ExecutionEngine/Orc/Shared/SimplePackedSerialization.h"
+
+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 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());
+    if (!ArgSerialization::serialize(OB, Args...))
+      return make_error<StringError>("Could not serialize arguments",
+                                     inconvertibleErrorCode());
+    return std::move(Buffer);
+  }
+
+  template <typename RetT>
+  Expected<RetT> deserialize(shared::WrapperFunctionResult ResultBytes) {
+    using RetDeserialization =
+        typename SPSCallSerialization<SPSSig>::RetSerialization;
+    shared::SPSInputBuffer IB(ResultBytes.data(), ResultBytes.size());
+    RetT ReturnValue;
+    if (!RetDeserialization::deserialize(IB, ReturnValue))
+      return make_error<StringError>("Could not deserialize return value",
+                                     inconvertibleErrorCode());
+    return ReturnValue;
+  }
+};
+
+template <typename SPSSig>
+class SPSEPCCaller : public EPCCaller<SPSCallSerializer<SPSSig>> {
+public:
+  SPSEPCCaller(ExecutorProcessControl &EPC)
+      : EPCCaller<SPSCallSerializer<SPSSig>>(EPC, SPSCallSerializer<SPSSig>()) {
+  }
+};
+
+template <typename SPSSig>
+class SPSEPCCall : public EPCCall<SPSCallSerializer<SPSSig>> {
+public:
+  SPSEPCCall(ExecutorProcessControl &EPC, ExecutorSymbolDef Fn)
+      : EPCCall<SPSCallSerializer<SPSSig>>(EPC, SPSCallSerializer<SPSSig>(),
+                                           std::move(Fn)) {}
+};
+
+} // namespace llvm::orc
+
+#endif // LLVM_EXECUTIONENGINE_ORC_CALLSPSVIAEPC_H

diff  --git a/llvm/include/llvm/ExecutionEngine/Orc/CallViaEPC.h b/llvm/include/llvm/ExecutionEngine/Orc/CallViaEPC.h
new file mode 100644
index 0000000000000..36acf05cb48e9
--- /dev/null
+++ b/llvm/include/llvm/ExecutionEngine/Orc/CallViaEPC.h
@@ -0,0 +1,102 @@
+//===------ 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"
+
+namespace llvm::orc {
+
+namespace detail {
+
+// Helper to extract the Expected<T> argument type from a handler callable.
+template <typename HandlerT> struct HandlerTraits {
+  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;
+};
+
+} // namespace detail
+
+/// Call a wrapper function via EPC.
+template <typename HandlerT, typename Serializer, typename... ArgTs>
+void callViaEPC(HandlerT &&H, ExecutorProcessControl &EPC, Serializer S,
+                ExecutorSymbolDef Fn, ArgTs &&...Args) {
+  using RetT = typename detail::HandlerTraits<HandlerT>::RetT;
+
+  if (auto ArgBytes = S.serialize(std::forward<ArgTs>(Args)...))
+    EPC.callWrapperAsync(
+        Fn.getAddress(),
+        [S = std::move(S), H = std::forward<HandlerT>(H)](
+            shared::WrapperFunctionResult 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());
+}
+
+/// 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.
+
+  // Async call version.
+  template <typename HandlerT, typename... ArgTs>
+  void operator()(HandlerT &&H, ExecutorSymbolDef Fn, ArgTs &&...Args) {
+    callViaEPC(std::forward<HandlerT>(H), 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.
+
+  template <typename HandlerT, typename... ArgTs>
+  void operator()(HandlerT &&H, ArgTs &&...Args) {
+    Caller(std::forward<HandlerT>(H), Fn, std::forward<ArgTs>(Args)...);
+  }
+
+private:
+  EPCCaller<Serializer> Caller;
+  ExecutorSymbolDef Fn;
+};
+
+} // namespace llvm::orc
+
+#endif // LLVM_EXECUTIONENGINE_ORC_CALLVIAEPC_H

diff  --git a/llvm/unittests/ExecutionEngine/Orc/CMakeLists.txt b/llvm/unittests/ExecutionEngine/Orc/CMakeLists.txt
index e5b5633c5a096..ea45a03130073 100644
--- a/llvm/unittests/ExecutionEngine/Orc/CMakeLists.txt
+++ b/llvm/unittests/ExecutionEngine/Orc/CMakeLists.txt
@@ -19,6 +19,7 @@ set(LLVM_LINK_COMPONENTS
 
 add_llvm_unittest(OrcJITTests
   CallableTraitsHelperTest.cpp
+  CallSPSViaEPCTest.cpp
   CoreAPIsTest.cpp
   ExecutorAddressTest.cpp
   ExecutionSessionWrapperFunctionCallsTest.cpp

diff  --git a/llvm/unittests/ExecutionEngine/Orc/CallSPSViaEPCTest.cpp b/llvm/unittests/ExecutionEngine/Orc/CallSPSViaEPCTest.cpp
new file mode 100644
index 0000000000000..6ad5835ed6e8f
--- /dev/null
+++ b/llvm/unittests/ExecutionEngine/Orc/CallSPSViaEPCTest.cpp
@@ -0,0 +1,90 @@
+//===----------- CallSPSViaEPC.cpp - Test CallSPSViaEPC.h APIs ------------===//
+//
+// 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/CallSPSViaEPC.h"
+#include "llvm/ExecutionEngine/Orc/SelfExecutorProcessControl.h"
+
+#include "llvm/Testing/Support/Error.h"
+
+#include "gtest/gtest.h"
+
+using namespace llvm;
+using namespace llvm::orc;
+using namespace llvm::orc::shared;
+
+static CWrapperFunctionResult mainWrapper(const char *ArgData, size_t ArgSize) {
+  return WrapperFunction<int32_t(SPSSequence<SPSString>)>::handle(
+             ArgData, ArgSize,
+             [](std::vector<std::string> Args) -> int32_t {
+               return Args.size();
+             })
+      .release();
+}
+
+TEST(CallSPSViaEPCTest, CallMainViaCaller) {
+  auto EPC = cantFail(SelfExecutorProcessControl::Create());
+  SPSEPCCaller<int32_t(SPSSequence<SPSString>)> C(*EPC);
+  std::vector<std::string> Args;
+
+  std::optional<Expected<int32_t>> R1;
+  C([&](Expected<int32_t> R) { R1 = std::move(R); },
+    ExecutorSymbolDef::fromPtr(mainWrapper), Args);
+  ASSERT_THAT_EXPECTED(*R1, Succeeded());
+  EXPECT_EQ(**R1, 0);
+
+  Args.push_back("foo");
+  std::optional<Expected<int32_t>> R2;
+  C([&](Expected<int32_t> R) { R2 = std::move(R); },
+    ExecutorSymbolDef::fromPtr(mainWrapper), Args);
+  ASSERT_THAT_EXPECTED(*R2, Succeeded());
+  EXPECT_EQ(**R2, 1);
+
+  Args.push_back("foo");
+  std::optional<Expected<int32_t>> R3;
+  C([&](Expected<int32_t> R) { R3 = std::move(R); },
+    ExecutorSymbolDef::fromPtr(mainWrapper), Args);
+  ASSERT_THAT_EXPECTED(*R3, Succeeded());
+  EXPECT_EQ(**R3, 2);
+
+  Args.clear();
+  std::optional<Expected<int32_t>> R4;
+  C([&](Expected<int32_t> R) { R4 = std::move(R); },
+    ExecutorSymbolDef::fromPtr(mainWrapper), Args);
+  ASSERT_THAT_EXPECTED(*R4, Succeeded());
+  EXPECT_EQ(**R4, 0);
+}
+
+TEST(CallSPSViaEPCTest, CallMainViaGenericCall) {
+  auto EPC = cantFail(SelfExecutorProcessControl::Create());
+  SPSEPCCall<int32_t(SPSSequence<SPSString>)> C(
+      *EPC, ExecutorSymbolDef::fromPtr(mainWrapper));
+  std::vector<std::string> Args;
+
+  std::optional<Expected<int32_t>> R1;
+  C([&](Expected<int32_t> R) { R1 = std::move(R); }, Args);
+  ASSERT_THAT_EXPECTED(*R1, Succeeded());
+  EXPECT_EQ(**R1, 0);
+
+  Args.push_back("foo");
+  std::optional<Expected<int32_t>> R2;
+  C([&](Expected<int32_t> R) { R2 = std::move(R); }, Args);
+  ASSERT_THAT_EXPECTED(*R2, Succeeded());
+  EXPECT_EQ(**R2, 1);
+
+  Args.push_back("foo");
+  std::optional<Expected<int32_t>> R3;
+  C([&](Expected<int32_t> R) { R3 = std::move(R); }, Args);
+  ASSERT_THAT_EXPECTED(*R3, Succeeded());
+  EXPECT_EQ(**R3, 2);
+
+  Args.clear();
+  std::optional<Expected<int32_t>> R4;
+  C([&](Expected<int32_t> R) { R4 = std::move(R); }, Args);
+  ASSERT_THAT_EXPECTED(*R4, Succeeded());
+  EXPECT_EQ(**R4, 0);
+}


        


More information about the llvm-commits mailing list