[llvm] 6ebd9e1 - [ORC] Add VoidVoid and IntVoid Callers, with tests. (#213211)
via llvm-commits
llvm-commits at lists.llvm.org
Fri Jul 31 04:06:28 PDT 2026
Author: Lang Hames
Date: 2026-07-31T21:06:23+10:00
New Revision: 6ebd9e1a12235fdc42e3e7b157678e6711af9c62
URL: https://github.com/llvm/llvm-project/commit/6ebd9e1a12235fdc42e3e7b157678e6711af9c62
DIFF: https://github.com/llvm/llvm-project/commit/6ebd9e1a12235fdc42e3e7b157678e6711af9c62.diff
LOG: [ORC] Add VoidVoid and IntVoid Callers, with tests. (#213211)
Add rt::VoidVoidCaller (void()) and rt::IntVoidCaller (int64_t())
runtime-agnostic interfaces and their rt::sps implementations, targeting
the orc_rt_ci_sps_call_void_void and orc_rt_ci_sps_call_int_void
controller-interface wrappers. Both are experimental and may be removed.
Extend SPSCallersTest to cover the new callers. VoidVoidCaller is the
first caller instantiated with a void result type, which exposed a
latent bug in sps::Caller::callAsync: its result handler declared a
CalleeRetT parameter, ill-formed when CalleeRetT is void. It now
special-cases void via `if constexpr`.
Added:
Modified:
llvm/include/llvm/ExecutionEngine/Orc/RTBridge/Calls.h
llvm/include/llvm/ExecutionEngine/Orc/RTBridge/SPS/Calls.h
llvm/unittests/ExecutionEngine/Orc/SPSCallersTest.cpp
Removed:
################################################################################
diff --git a/llvm/include/llvm/ExecutionEngine/Orc/RTBridge/Calls.h b/llvm/include/llvm/ExecutionEngine/Orc/RTBridge/Calls.h
index 61eb6d54f4ebd..29cf268c71803 100644
--- a/llvm/include/llvm/ExecutionEngine/Orc/RTBridge/Calls.h
+++ b/llvm/include/llvm/ExecutionEngine/Orc/RTBridge/Calls.h
@@ -84,6 +84,29 @@ template <typename RetT, typename... ArgTs> class Caller<RetT(ArgTs...)> {
/// argument vector, and its int64_t result is returned.
class MainCaller : public Caller<int64_t(ArrayRef<std::string>)> {};
+/// Runtime-agnostic interface for running a void() function in the executor.
+///
+/// The function to run is given by its ExecutorAddr.
+///
+/// WARNING: This Caller is experimental and may be removed.
+class VoidVoidCaller : public Caller<void()> {};
+
+/// Runtime-agnostic interface for running an int32_t() function in the
+/// executor.
+///
+/// The function to run is given by its ExecutorAddr.
+///
+/// WARNING: This Caller is experimental and may be removed.
+class Int32VoidCaller : public Caller<int32_t()> {};
+
+/// Runtime-agnostic interface for running an int32_t(int32_t) function in the
+/// executor.
+///
+/// The function to run is given by its ExecutorAddr.
+///
+/// WARNING: This Caller is experimental and may be removed.
+class Int32Int32Caller : public Caller<int32_t(int32_t)> {};
+
} // namespace llvm::orc::rt
#endif // LLVM_EXECUTIONENGINE_ORC_RTBRIDGE_CALLS_H
diff --git a/llvm/include/llvm/ExecutionEngine/Orc/RTBridge/SPS/Calls.h b/llvm/include/llvm/ExecutionEngine/Orc/RTBridge/SPS/Calls.h
index 86e89acdee3ff..d11cc16a7b7e7 100644
--- a/llvm/include/llvm/ExecutionEngine/Orc/RTBridge/SPS/Calls.h
+++ b/llvm/include/llvm/ExecutionEngine/Orc/RTBridge/SPS/Calls.h
@@ -72,16 +72,27 @@ class Caller<BaseT, SPSSigT, CINameV, RetT(ArgTs...)> : public BaseT {
ExecutionSession &ES, ExecutorAddr CallerFnAddr,
ExecutorAddr FnAddr, const ArgTs &...Args) {
using namespace llvm::orc::shared;
- ES.callSPSWrapperAsync<SPSSigT>(
- CallerFnAddr,
- [OnComplete = std::move(OnComplete)](Error SerErr,
- CalleeRetT Result) mutable {
- if (SerErr)
- return OnComplete(std::move(SerErr));
- else
- return OnComplete(std::move(Result));
- },
- FnAddr, Args...);
+ if constexpr (std::is_void_v<CalleeRetT>) {
+ // Void result: the executor-side function produces no value, so the only
+ // thing to report is the dispatch error (success if the call ran).
+ ES.callSPSWrapperAsync<SPSSigT>(
+ CallerFnAddr,
+ [OnComplete = std::move(OnComplete)](Error SerErr) mutable {
+ OnComplete(std::move(SerErr));
+ },
+ FnAddr, Args...);
+ } else {
+ ES.callSPSWrapperAsync<SPSSigT>(
+ CallerFnAddr,
+ [OnComplete = std::move(OnComplete)](Error SerErr,
+ CalleeRetT Result) mutable {
+ if (SerErr)
+ return OnComplete(std::move(SerErr));
+ else
+ return OnComplete(std::move(Result));
+ },
+ FnAddr, Args...);
+ }
}
void operator()(unique_function<void(ErrorRetT)> OnComplete,
@@ -103,6 +114,29 @@ inline constexpr char CallMainCIName[] = "orc_rt_ci_sps_call_main";
/// (int(int argc, char *argv[])) in the executor.
using MainCaller = Caller<rt::MainCaller, CallMainSPSSig, CallMainCIName>;
+using CallVoidVoidSPSSig = void(shared::SPSExecutorAddr);
+inline constexpr char CallVoidVoidCIName[] = "orc_rt_ci_sps_call_void_void";
+/// SPS caller for rt::VoidVoidCaller: runs a void() function in the executor.
+/// WARNING: This Caller is experimental and may be removed.
+using VoidVoidCaller =
+ Caller<rt::VoidVoidCaller, CallVoidVoidSPSSig, CallVoidVoidCIName>;
+
+using CallInt32VoidSPSSig = int32_t(shared::SPSExecutorAddr);
+inline constexpr char CallInt32VoidCIName[] = "orc_rt_ci_sps_call_int32_void";
+/// SPS caller for rt::Int32VoidCaller: runs an int32_t() function in the
+/// executor.
+/// WARNING: This Caller is experimental and may be removed.
+using Int32VoidCaller =
+ Caller<rt::Int32VoidCaller, CallInt32VoidSPSSig, CallInt32VoidCIName>;
+
+using CallInt32Int32SPSSig = int32_t(shared::SPSExecutorAddr, int32_t);
+inline constexpr char CallInt32Int32CIName[] = "orc_rt_ci_sps_call_int32_int32";
+/// SPS caller for rt::Int32Int32Caller: runs an int32_t(int32_t) function in
+/// the executor.
+/// WARNING: This Caller is experimental and may be removed.
+using Int32Int32Caller =
+ Caller<rt::Int32Int32Caller, CallInt32Int32SPSSig, CallInt32Int32CIName>;
+
} // namespace llvm::orc::rt::sps
#endif // LLVM_EXECUTIONENGINE_ORC_RTBRIDGE_SPS_CALLS_H
diff --git a/llvm/unittests/ExecutionEngine/Orc/SPSCallersTest.cpp b/llvm/unittests/ExecutionEngine/Orc/SPSCallersTest.cpp
index 067fa6dbeb574..b2b41857cb976 100644
--- a/llvm/unittests/ExecutionEngine/Orc/SPSCallersTest.cpp
+++ b/llvm/unittests/ExecutionEngine/Orc/SPSCallersTest.cpp
@@ -23,7 +23,11 @@
using namespace llvm;
using namespace llvm::orc;
using namespace llvm::orc::shared;
+using llvm::orc::rt::sps::Int32Int32Caller;
+using llvm::orc::rt::sps::Int32VoidCaller;
using llvm::orc::rt::sps::MainCaller;
+using llvm::orc::rt::sps::VoidVoidCaller;
+
// Test "main" function. Returns argc plus the length of the first element of
// argv (if argv is non-empty). Does not inspect argv entries beyond the first.
static int testMain(int argc, char *argv[]) {
@@ -142,3 +146,113 @@ TEST(SPSCallersTest, CreateLooksUpCallMainInBootstrapJD) {
cantFail(ES.endSession());
}
+
+// Target for VoidVoidCaller: records that it ran via a counter (there is no
+// return value to observe).
+static int VoidVoidCallCount = 0;
+static void voidVoidTarget() { ++VoidVoidCallCount; }
+
+// Executor-side wrapper for VoidVoidCaller. Decodes the target address and
+// invokes it as a void() function.
+static CWrapperFunctionBuffer callVoidVoidWrapper(const char *ArgData,
+ size_t ArgSize) {
+ return WrapperFunction<void(SPSExecutorAddr)>::handle(
+ ArgData, ArgSize,
+ [](ExecutorAddr FnAddr) { FnAddr.toPtr<void()>()(); })
+ .release();
+}
+
+// Target for Int32Int32Caller: doubles its argument, so the forwarded value is
+// observable in the result.
+static int32_t int32Int32Target(int32_t X) { return X * 2; }
+
+// Executor-side wrapper for Int32Int32Caller. Decodes the target address and
+// the int32_t argument, invokes the target, and returns the result.
+static CWrapperFunctionBuffer callInt32Int32Wrapper(const char *ArgData,
+ size_t ArgSize) {
+ return WrapperFunction<int32_t(SPSExecutorAddr, int32_t)>::handle(
+ ArgData, ArgSize,
+ [](ExecutorAddr FnAddr, int32_t X) -> int32_t {
+ return FnAddr.toPtr<int32_t(int32_t)>()(X);
+ })
+ .release();
+}
+
+// Exercises the void-return path (ErrorRetT == Error) and the empty argument
+// pack, through both the synchronous and asynchronous call operators.
+TEST(SPSCallersTest, VoidVoidSyncAndAsync) {
+ ExecutionSession ES(cantFail(SelfExecutorProcessControl::Create()));
+
+ VoidVoidCaller Call(ES, ExecutorAddr::fromPtr(callVoidVoidWrapper));
+ ExecutorAddr TargetAddr = ExecutorAddr::fromPtr(voidVoidTarget);
+
+ // Synchronous: the call operator returns Error, not Expected<T>.
+ VoidVoidCallCount = 0;
+ EXPECT_THAT_ERROR(Call(TargetAddr), Succeeded());
+ EXPECT_EQ(VoidVoidCallCount, 1);
+
+ // Asynchronous: the result is delivered as an Error.
+ VoidVoidCallCount = 0;
+ std::promise<MSVCPError> P;
+ auto F = P.get_future();
+ Call([&](Error Err) { P.set_value(std::move(Err)); }, TargetAddr);
+ EXPECT_THAT_ERROR(Error(F.get()), Succeeded());
+ EXPECT_EQ(VoidVoidCallCount, 1);
+
+ cantFail(ES.endSession());
+}
+
+// Exercises a non-void caller with an argument (so argument forwarding through
+// the pack is covered), and the Create / bootstrap lookup path for a caller
+// other than MainCaller.
+TEST(SPSCallersTest, Int32Int32SyncAndCreate) {
+ ExecutionSession ES(cantFail(SelfExecutorProcessControl::Create()));
+
+ Int32Int32Caller Call(ES, ExecutorAddr::fromPtr(callInt32Int32Wrapper));
+ Expected<int32_t> RDirect = Call(ExecutorAddr::fromPtr(int32Int32Target), 21);
+ ASSERT_THAT_EXPECTED(RDirect, Succeeded());
+ EXPECT_EQ(*RDirect, 42); // 21 * 2.
+
+ auto &BootstrapJD = ES.getBootstrapJITDylib();
+ cantFail(BootstrapJD.define(
+ absoluteSymbols({{ES.intern(Int32Int32Caller::CIName),
+ {ExecutorAddr::fromPtr(callInt32Int32Wrapper),
+ JITSymbolFlags::Exported}}})));
+
+ Expected<Int32Int32Caller> CreatedCall = Int32Int32Caller::Create(ES);
+ ASSERT_THAT_EXPECTED(CreatedCall, Succeeded());
+ Expected<int32_t> RCreated =
+ (*CreatedCall)(ExecutorAddr::fromPtr(int32Int32Target), 21);
+ ASSERT_THAT_EXPECTED(RCreated, Succeeded());
+ EXPECT_EQ(*RCreated, 42); // 21 * 2.
+
+ cantFail(ES.endSession());
+}
+
+// Target for Int32VoidCaller.
+static int32_t int32VoidTarget() { return 42; }
+
+// Executor-side wrapper for Int32VoidCaller. Decodes the target address,
+// invokes it as an int32_t() function, and returns the result.
+static CWrapperFunctionBuffer callInt32VoidWrapper(const char *ArgData,
+ size_t ArgSize) {
+ return WrapperFunction<int32_t(SPSExecutorAddr)>::handle(
+ ArgData, ArgSize,
+ [](ExecutorAddr FnAddr) -> int32_t {
+ return FnAddr.toPtr<int32_t()>()();
+ })
+ .release();
+}
+
+// Exercises a non-void, zero-argument caller. (Void return and argument
+// forwarding are covered by VoidVoidCaller and Int32Int32Caller respectively.)
+TEST(SPSCallersTest, Int32VoidSync) {
+ ExecutionSession ES(cantFail(SelfExecutorProcessControl::Create()));
+
+ Int32VoidCaller Call(ES, ExecutorAddr::fromPtr(callInt32VoidWrapper));
+ Expected<int32_t> R = Call(ExecutorAddr::fromPtr(int32VoidTarget));
+ ASSERT_THAT_EXPECTED(R, Succeeded());
+ EXPECT_EQ(*R, 42);
+
+ cantFail(ES.endSession());
+}
More information about the llvm-commits
mailing list