[llvm] bbbfd87 - [ORC] Remove callSPSWrapper, callSPSWrapperAsync, and callWrapper (#226893)
via llvm-commits
llvm-commits at lists.llvm.org
Mon Sep 28 00:28:34 PDT 2026
Author: Lang Hames
Date: 2026-09-28T17:28:27+10:00
New Revision: bbbfd872877d184d6a5bd0d2a5322c9fe8387648
URL: https://github.com/llvm/llvm-project/commit/bbbfd872877d184d6a5bd0d2a5322c9fe8387648
DIFF: https://github.com/llvm/llvm-project/commit/bbbfd872877d184d6a5bd0d2a5322c9fe8387648.diff
LOG: [ORC] Remove callSPSWrapper, callSPSWrapperAsync, and callWrapper (#226893)
All in-tree callers now use Proxy. Remove the SPS convenience call
methods from ExecutionSession and ExecutorProcessControl, along with the
unit tests that exercised them (Proxy dispatch is covered by
SPSProxySpecTest). Also remove the blocking callWrapper convenience
methods, which have no remaining users.
Add a "How to call functions in the executor" section to
llvm/docs/ORCv2.md describing Proxy, controller-interface descriptors
and sps::ProxySpec, how lookupAndApply and recordProxy resolve proxies,
and how to migrate from callSPSWrapper.
Clients calling these methods directly should migrate to Proxy; see "How
to call functions in the executor" in llvm/docs/ORCv2.md.
Added:
Modified:
llvm/docs/ORCv2.md
llvm/include/llvm/ExecutionEngine/Orc/Core.h
llvm/include/llvm/ExecutionEngine/Orc/ExecutorProcessControl.h
llvm/unittests/ExecutionEngine/Orc/ExecutionSessionWrapperFunctionCallsTest.cpp
Removed:
################################################################################
diff --git a/llvm/docs/ORCv2.md b/llvm/docs/ORCv2.md
index 39ddbc721d97e..a84056d973130 100644
--- a/llvm/docs/ORCv2.md
+++ b/llvm/docs/ORCv2.md
@@ -769,6 +769,100 @@ for (const auto &IRPath : IRPaths) {
}
```
+(CallingExecutorFunctions)=
+
+### How to call functions in the executor
+
+JIT'd code runs in an *executor* process, which may or may not be the same
+process as the JIT itself (the *controller*). The controller calls into the
+executor through *wrapper functions*: executor-side functions that take a
+serialized argument buffer and return a serialized result. ORC provides three
+pieces for making these calls:
+
+* A **controller-interface (CI) descriptor** is the contract between the two
+ sides. It is a struct naming the wrapper function (a `SymbolNameSpec`, which
+ records the name's mangling kind so that ORC can mangle it for the target)
+ and giving its Simple Packed Serialization (SPS) wire signature. The
+ descriptors for ORC's own services live in
+ `llvm/ExecutionEngine/Orc/Shared/SPSCI/`.
+* A **`Proxy<RetT(ArgTs...)>`** (`Proxy.h`) is a typed, protocol-agnostic
+ handle to one executor-side operation: a callee address plus a dispatch
+ function. Its call operator takes an `ExecutionSession` and the C++
+ arguments. Dispatch failures and the callee's own errors are reported on a
+ single channel: callees returning `void` or `Error` yield an `Error`, and
+ callees returning `T` or `Expected<T>` yield an `Expected<T>`.
+* An **`sps::ProxySpec<ProxyT, CI>`** (`SPSProxySpec.h`) binds a proxy type to a
+ CI descriptor. Its `dispatch` function serializes the arguments using
+ `CI::SPSSig`, calls the wrapper, and deserializes the result.
+
+For example, given an executor-side wrapper named `my_add_wrapper`:
+
+```c++
+// The contract: the wrapper's name and wire signature.
+struct AddCI {
+ static constexpr SymbolNameSpec Name = SymbolNameSpec::c("my_add_wrapper");
+ using SPSSig = int32_t(int32_t, int32_t);
+};
+
+// The controller-side handle, and its SPS binding.
+using AddProxy = Proxy<int32_t(int32_t, int32_t)>;
+using AddProxySpec = sps::ProxySpec<AddProxy, AddCI>;
+```
+
+Proxies are usually resolved with `lookupAndApply` (`LookupAndApply.h`) and
+`recordProxy` (`RecordProxy.h`), which look up the spec's name (mangled for the
+target) and construct the proxy over the resulting address:
+
+```c++
+AddProxy Add;
+if (auto Err = lookupAndApply(JD, {recordProxy<AddProxySpec>(&Add)}))
+ return Err;
+```
+
+All of the entries in a `lookupAndApply` list are resolved by a single lookup,
+so a service's proxies can be bound together with any data symbols it needs
+(use `recordAddr` for those). Passing
+`SymbolLookupFlags::WeaklyReferencedSymbol` leaves the proxy null (see
+`Proxy::operator bool`) if the symbol is absent, and passing a name as the
+second argument to `recordProxy` binds the same spec to a
diff erent
+implementation. If you already have the wrapper's address, construct the proxy
+directly: `AddProxy Add(AddProxySpec::dispatch, AddAddr);`.
+
+Proxies can be called synchronously or asynchronously:
+
+```c++
+// Blocking:
+Expected<int32_t> Sum = Add(ES, 1, 2);
+
+// Asynchronous:
+Add([](Expected<int32_t> Sum) { /* ... */ }, ES, 1, 2);
+```
+
+A blocking call blocks a controller thread, so prefer the asynchronous form
+where possible.
+
+`Proxy` supersedes the `callSPSWrapper` and `callSPSWrapperAsync` methods of
+`ExecutionSession` and `ExecutorProcessControl`. To migrate a call such as:
+
+```c++
+int32_t Sum;
+if (auto Err = ES.callSPSWrapper<int32_t(int32_t, int32_t)>(AddAddr, Sum, 1, 2))
+ return Err;
+```
+
+define a CI descriptor and proxy as above, then write:
+
+```c++
+AddProxy Add(AddProxySpec::dispatch, AddAddr);
+Expected<int32_t> Sum = Add(ES, 1, 2);
+```
+
+Asynchronous callbacks that took a serialization `Error` and the callee's
+result as separate arguments now take the single `Error` or `Expected<T>`
+described above. `CallProxies.h` and `CallProxiesSPS.h` are small in-tree
+examples of named proxies and their specs, and `SimpleMemoryMap.h` and
+`SimpleMemoryMapSPS.h` show how to bundle a service's proxies into a handle.
+
(ProcessAndLibrarySymbols)=
## How to Add Process and Library Symbols to JITDylibs
diff --git a/llvm/include/llvm/ExecutionEngine/Orc/Core.h b/llvm/include/llvm/ExecutionEngine/Orc/Core.h
index ea2f0f027839d..7754a554b35a6 100644
--- a/llvm/include/llvm/ExecutionEngine/Orc/Core.h
+++ b/llvm/include/llvm/ExecutionEngine/Orc/Core.h
@@ -1400,38 +1400,6 @@ class ExecutionSession {
ArgBuffer);
}
- /// Run a wrapper function in the executor. The wrapper function should be
- /// callable as:
- ///
- /// \code{.cpp}
- /// CWrapperFunctionBuffer fn(uint8_t *Data, uint64_t Size);
- /// \endcode{.cpp}
- shared::WrapperFunctionBuffer callWrapper(ExecutorAddr WrapperFnAddr,
- ArrayRef<char> ArgBuffer) {
- return EPC->callWrapper(WrapperFnAddr, ArgBuffer);
- }
-
- /// Run a wrapper function using SPS to serialize the arguments and
- /// deserialize the results.
- template <typename SPSSignature, typename SendResultT, typename... ArgTs>
- void callSPSWrapperAsync(ExecutorAddr WrapperFnAddr, SendResultT &&SendResult,
- const ArgTs &...Args) {
- EPC->callSPSWrapperAsync<SPSSignature, SendResultT, ArgTs...>(
- WrapperFnAddr, std::forward<SendResultT>(SendResult), Args...);
- }
-
- /// Run a wrapper function using SPS to serialize the arguments and
- /// deserialize the results.
- ///
- /// If SPSSignature is a non-void function signature then the second argument
- /// (the first in the Args list) should be a reference to a return value.
- template <typename SPSSignature, typename... WrapperCallArgTs>
- Error callSPSWrapper(ExecutorAddr WrapperFnAddr,
- WrapperCallArgTs &&...WrapperCallArgs) {
- return EPC->callSPSWrapper<SPSSignature, WrapperCallArgTs...>(
- WrapperFnAddr, std::forward<WrapperCallArgTs>(WrapperCallArgs)...);
- }
-
/// Wrap a handler that takes concrete argument types (and a sender for a
/// concrete return type) to produce an AsyncHandlerWrapperFunction. Uses SPS
/// to unpack the arguments and pack the result.
diff --git a/llvm/include/llvm/ExecutionEngine/Orc/ExecutorProcessControl.h b/llvm/include/llvm/ExecutionEngine/Orc/ExecutorProcessControl.h
index d69c71fe444b1..ec752db3722dc 100644
--- a/llvm/include/llvm/ExecutionEngine/Orc/ExecutorProcessControl.h
+++ b/llvm/include/llvm/ExecutionEngine/Orc/ExecutorProcessControl.h
@@ -222,65 +222,6 @@ class LLVM_ABI ExecutorProcessControl {
std::forward<FnT>(OnComplete), ArgBuffer);
}
- /// Run a wrapper function in the executor. The wrapper function should be
- /// callable as:
- ///
- /// \code{.cpp}
- /// CWrapperFunctionBuffer fn(uint8_t *Data, uint64_t Size);
- /// \endcode{.cpp}
- shared::WrapperFunctionBuffer callWrapper(ExecutorAddr WrapperFnAddr,
- ArrayRef<char> ArgBuffer) {
- std::promise<shared::WrapperFunctionBuffer> RP;
- auto RF = RP.get_future();
- callWrapperAsync(
- RunInPlace(), WrapperFnAddr,
- [&](shared::WrapperFunctionBuffer R) {
- RP.set_value(std::move(R));
- }, ArgBuffer);
- return RF.get();
- }
-
- /// Run a wrapper function using SPS to serialize the arguments and
- /// deserialize the results.
- template <typename SPSSignature, typename RunPolicyT, typename SendResultT,
- typename... ArgTs>
- void callSPSWrapperAsync(RunPolicyT &&Runner, ExecutorAddr WrapperFnAddr,
- SendResultT &&SendResult, const ArgTs &...Args) {
- shared::WrapperFunction<SPSSignature>::callAsync(
- [this, WrapperFnAddr, Runner = std::move(Runner)]
- (auto &&SendResult, const char *ArgData, size_t ArgSize) mutable {
- this->callWrapperAsync(std::move(Runner), WrapperFnAddr,
- std::move(SendResult),
- ArrayRef<char>(ArgData, ArgSize));
- },
- std::forward<SendResultT>(SendResult), Args...);
- }
-
- /// Run a wrapper function using SPS to serialize the arguments and
- /// deserialize the results.
- template <typename SPSSignature, typename SendResultT, typename... ArgTs>
- void callSPSWrapperAsync(ExecutorAddr WrapperFnAddr, SendResultT &&SendResult,
- const ArgTs &...Args) {
- callSPSWrapperAsync<SPSSignature>(RunAsTask(*D), WrapperFnAddr,
- std::forward<SendResultT>(SendResult),
- Args...);
- }
-
- /// Run a wrapper function using SPS to serialize the arguments and
- /// deserialize the results.
- ///
- /// If SPSSignature is a non-void function signature then the second argument
- /// (the first in the Args list) should be a reference to a return value.
- template <typename SPSSignature, typename... WrapperCallArgTs>
- Error callSPSWrapper(ExecutorAddr WrapperFnAddr,
- WrapperCallArgTs &&...WrapperCallArgs) {
- return shared::WrapperFunction<SPSSignature>::call(
- [this, WrapperFnAddr](const char *ArgData, size_t ArgSize) {
- return callWrapper(WrapperFnAddr, ArrayRef<char>(ArgData, ArgSize));
- },
- std::forward<WrapperCallArgTs>(WrapperCallArgs)...);
- }
-
/// Disconnect from the target process.
///
/// This should be called after the JIT session is shut down.
diff --git a/llvm/unittests/ExecutionEngine/Orc/ExecutionSessionWrapperFunctionCallsTest.cpp b/llvm/unittests/ExecutionEngine/Orc/ExecutionSessionWrapperFunctionCallsTest.cpp
index faab7f2df108b..e1e9ec2468a27 100644
--- a/llvm/unittests/ExecutionEngine/Orc/ExecutionSessionWrapperFunctionCallsTest.cpp
+++ b/llvm/unittests/ExecutionEngine/Orc/ExecutionSessionWrapperFunctionCallsTest.cpp
@@ -10,8 +10,6 @@
#include "llvm/ExecutionEngine/Orc/Core.h"
#include "llvm/ExecutionEngine/Orc/ExecutorProcessControl.h"
#include "llvm/ExecutionEngine/Orc/SelfExecutorProcessControl.h"
-#include "llvm/Support/MSVCErrorWorkarounds.h"
-#include "llvm/Testing/Support/Error.h"
#include "gtest/gtest.h"
#include <future>
@@ -20,62 +18,11 @@ using namespace llvm;
using namespace llvm::orc;
using namespace llvm::orc::shared;
-static CWrapperFunctionBuffer addWrapper(const char *ArgData, size_t ArgSize) {
- return WrapperFunction<int32_t(int32_t, int32_t)>::handle(
- ArgData, ArgSize, [](int32_t X, int32_t Y) { return X + Y; })
- .release();
-}
-
static void addAsyncWrapper(unique_function<void(int32_t)> SendResult,
int32_t X, int32_t Y) {
SendResult(X + Y);
}
-static CWrapperFunctionBuffer voidWrapper(const char *ArgData, size_t ArgSize) {
- return WrapperFunction<void()>::handle(ArgData, ArgSize, []() {}).release();
-}
-
-TEST(ExecutionSessionWrapperFunctionCalls, RunWrapperTemplate) {
- ExecutionSession ES(cantFail(SelfExecutorProcessControl::Create()));
-
- int32_t Result;
- EXPECT_THAT_ERROR(ES.callSPSWrapper<int32_t(int32_t, int32_t)>(
- ExecutorAddr::fromPtr(addWrapper), Result, 2, 3),
- Succeeded());
- EXPECT_EQ(Result, 5);
- cantFail(ES.endSession());
-}
-
-TEST(ExecutionSessionWrapperFunctionCalls, RunVoidWrapperAsyncTemplate) {
- ExecutionSession ES(cantFail(SelfExecutorProcessControl::Create()));
-
- std::promise<MSVCPError> RP;
- ES.callSPSWrapperAsync<void()>(ExecutorAddr::fromPtr(voidWrapper),
- [&](Error SerializationErr) {
- RP.set_value(std::move(SerializationErr));
- });
- Error Err = RP.get_future().get();
- EXPECT_THAT_ERROR(std::move(Err), Succeeded());
- cantFail(ES.endSession());
-}
-
-TEST(ExecutionSessionWrapperFunctionCalls, RunNonVoidWrapperAsyncTemplate) {
- ExecutionSession ES(cantFail(SelfExecutorProcessControl::Create()));
-
- std::promise<MSVCPExpected<int32_t>> RP;
- ES.callSPSWrapperAsync<int32_t(int32_t, int32_t)>(
- ExecutorAddr::fromPtr(addWrapper),
- [&](Error SerializationErr, int32_t R) {
- if (SerializationErr)
- RP.set_value(std::move(SerializationErr));
- RP.set_value(std::move(R));
- },
- 2, 3);
- Expected<int32_t> Result = RP.get_future().get();
- EXPECT_THAT_EXPECTED(Result, HasValue(5));
- cantFail(ES.endSession());
-}
-
TEST(ExecutionSessionWrapperFunctionCalls, RegisterAsyncHandlerAndRun) {
constexpr ExecutorAddr AddAsyncTagAddr(0x01);
More information about the llvm-commits
mailing list