[llvm] [orc-rt] Add method-wrapper utils for use with WrapperFunction::handle. (PR #162035)
Lang Hames via llvm-commits
llvm-commits at lists.llvm.org
Sun Oct 5 19:03:29 PDT 2025
https://github.com/lhames created https://github.com/llvm/llvm-project/pull/162035
WrapperFunction::handleWithAsyncMethod can be used to wrap asynchronous methods (void methods whose first arguments are Return callbacks) for use with WrapperFunction::handle.
WrapperFunction::handleWithSyncMethod can be used to wrap regular (non-asynchronous) methods for use with WrapperFunction::handle.
Both variants return function objects that take a Return callback as their first argument, and an ExecutorAddr representing the address of the instance to call the object on. For asynchronous methods the resulting function object (AsyncMethod<method-ptr>) forwards the Return callback through to the method. For synchronous methods the method is called and the result passed to the Return callback.
>From 3ecc9eaf8b08e4f21f8a24aa7e1b6df191ff84a8 Mon Sep 17 00:00:00 2001
From: Lang Hames <lhames at gmail.com>
Date: Mon, 6 Oct 2025 11:56:19 +1100
Subject: [PATCH] [orc-rt] Add method-wrapper utils for use with
WrapperFunction::handle.
WrapperFunction::handleWithAsyncMethod can be used to wrap asynchronous methods
(void methods whose first arguments are Return callbacks) for use with
WrapperFunction::handle.
WrapperFunction::handleWithSyncMethod can be used to wrap regular
(non-asynchronous) methods for use with WrapperFunction::handle.
Both variants return function objects that take a Return callback as their
first argument, and an ExecutorAddr representing the address of the instance to
call the object on. For asynchronous methods the resulting function object
(AsyncMethod<method-ptr>) forwards the Return callback through to the method.
For synchronous methods the method is called and the result passed to the
Return callback.
---
orc-rt/include/orc-rt/WrapperFunction.h | 124 ++++++++++++++++++++
orc-rt/unittests/SPSWrapperFunctionTest.cpp | 50 ++++++++
2 files changed, 174 insertions(+)
diff --git a/orc-rt/include/orc-rt/WrapperFunction.h b/orc-rt/include/orc-rt/WrapperFunction.h
index 47e770f0bfbf7..c43f1c5b4a753 100644
--- a/orc-rt/include/orc-rt/WrapperFunction.h
+++ b/orc-rt/include/orc-rt/WrapperFunction.h
@@ -16,7 +16,9 @@
#include "orc-rt-c/WrapperFunction.h"
#include "orc-rt/CallableTraitsHelper.h"
#include "orc-rt/Error.h"
+#include "orc-rt/ExecutorAddress.h"
#include "orc-rt/bind.h"
+#include "orc-rt/move_only_function.h"
#include <utility>
@@ -205,6 +207,128 @@ struct ResultDeserializer<std::tuple<Error>, Serializer> {
/// wrapper functions in C++.
struct WrapperFunction {
+ /// Wraps an asynchronous method (a method returning void, and taking a
+ /// return callback as its first argument) for use with
+ /// WrapperFunction::handle.
+ ///
+ /// AsyncMethod's call operator takes an ExecutorAddr as its second argument,
+ /// casts it to a ClassT*, and then calls the wrapped method on that pointer,
+ /// forwarding the return callback and any subsequent arguments (after the
+ /// second argument representing the object address).
+ ///
+ /// This utility removes some of the boilerplate from writing wrappers for
+ /// method calls.
+ template <typename ClassT, typename ReturnT, typename... ArgTs>
+ struct AsyncMethod {
+ AsyncMethod(void (ClassT::*M)(ReturnT, ArgTs...)) : M(M) {}
+ void operator()(ReturnT &&Return, ExecutorAddr Obj, ArgTs &&...Args) {
+ (Obj.toPtr<ClassT *>()->*M)(std::forward<ReturnT>(Return),
+ std::forward<ArgTs>(Args)...);
+ }
+
+ private:
+ void (ClassT::*M)(ReturnT, ArgTs...);
+ };
+
+ /// Create an AsyncMethod wrapper for the given method pointer. The given
+ /// method should be asynchronous: returning void, and taking a return
+ /// callback as its first argument.
+ ///
+ /// The handWithAsyncMethod function can be used to remove some of the
+ /// boilerplate from writing wrappers for method calls:
+ ///
+ /// @code{.cpp}
+ /// class MyClass {
+ /// public:
+ /// void myMethod(move_only_function<void(std::string)> Return,
+ // uint32_t X, bool Y) { ... }
+ /// };
+ ///
+ /// // SPS Method signature -- note MyClass object address as first
+ /// // argument.
+ /// using SPSMyMethodWrapperSignature =
+ /// SPSString(SPSExecutorAddr, uint32_t, bool);
+ ///
+ ///
+ /// static void adder_add_async_sps_wrapper(
+ /// orc_rt_SessionRef Session, void *CallCtx,
+ /// orc_rt_WrapperFunctionReturn Return,
+ /// orc_rt_WrapperFunctionBuffer ArgBytes) {
+ /// using SPSSig = SPSString(SPSExecutorAddr, int32_t, bool);
+ /// SPSWrapperFunction<SPSSig>::handle(
+ /// Session, CallCtx, Return, ArgBytes,
+ /// WrapperFunction::handleWithAsyncMethod(&MyClass::myMethod));
+ /// }
+ /// @endcode
+ ///
+ template <typename ClassT, typename ReturnT, typename... ArgTs>
+ static AsyncMethod<ClassT, ReturnT, ArgTs...>
+ handleWithAsyncMethod(void (ClassT::*M)(ReturnT, ArgTs...)) {
+ return AsyncMethod<ClassT, ReturnT, ArgTs...>(M);
+ }
+
+ /// Wraps a synchronous method (an ordinary method that returns its result,
+ /// as opposed to an asynchronous method, see AsyncMethod) for use with
+ /// WrapperFunction::handle.
+ ///
+ /// SyncMethod's call operator takes a return callback as its first argument
+ /// and an ExecutorAddr as its second argument. The ExecutorAddr argument is
+ /// cast to a ClassT*, and then called passing the subsequent arguments
+ /// (after the second argument representing the object address). The Return
+ /// callback is then called on the value returned from the method.
+ ///
+ /// This utility removes some of the boilerplate from writing wrappers for
+ /// method calls.
+ template <typename ClassT, typename RetT, typename... ArgTs>
+ class SyncMethod {
+ public:
+ SyncMethod(RetT (ClassT::*M)(ArgTs...)) : M(M) {}
+
+ void operator()(move_only_function<void(RetT)> Return, ExecutorAddr Obj,
+ ArgTs &&...Args) {
+ Return((Obj.toPtr<ClassT *>()->*M)(std::forward<ArgTs>(Args)...));
+ }
+
+ private:
+ RetT (ClassT::*M)(ArgTs...);
+ };
+
+ /// Create an SyncMethod wrapper for the given method pointer. The given
+ /// method should be synchronous, i.e. returning its result (as opposed to
+ /// asynchronous, see AsyncMethod).
+ ///
+ /// The handWithAsyncMethod function can be used to remove some of the
+ /// boilerplate from writing wrappers for method calls:
+ ///
+ /// @code{.cpp}
+ /// class MyClass {
+ /// public:
+ /// std::string myMethod(uint32_t X, bool Y) { ... }
+ /// };
+ ///
+ /// // SPS Method signature -- note MyClass object address as first
+ /// // argument.
+ /// using SPSMyMethodWrapperSignature =
+ /// SPSString(SPSExecutorAddr, uint32_t, bool);
+ ///
+ ///
+ /// static void adder_add_sync_sps_wrapper(
+ /// orc_rt_SessionRef Session, void *CallCtx,
+ /// orc_rt_WrapperFunctionReturn Return,
+ /// orc_rt_WrapperFunctionBuffer ArgBytes) {
+ /// using SPSSig = SPSString(SPSExecutorAddr, int32_t, bool);
+ /// SPSWrapperFunction<SPSSig>::handle(
+ /// Session, CallCtx, Return, ArgBytes,
+ /// WrapperFunction::handleWithSyncMethod(&Adder::addSync));
+ /// }
+ /// @endcode
+ ///
+ template <typename ClassT, typename RetT, typename... ArgTs>
+ static SyncMethod<ClassT, RetT, ArgTs...>
+ handleWithSyncMethod(RetT (ClassT::*M)(ArgTs...)) {
+ return SyncMethod<ClassT, RetT, ArgTs...>(M);
+ }
+
/// Make a call to a wrapper function.
///
/// This utility serializes and deserializes arguments and return values
diff --git a/orc-rt/unittests/SPSWrapperFunctionTest.cpp b/orc-rt/unittests/SPSWrapperFunctionTest.cpp
index 32aaa61639dbb..e010e2a067adf 100644
--- a/orc-rt/unittests/SPSWrapperFunctionTest.cpp
+++ b/orc-rt/unittests/SPSWrapperFunctionTest.cpp
@@ -297,3 +297,53 @@ TEST(SPSWrapperFunctionUtilsTest, TestHandlerWithReferences) {
EXPECT_EQ(OpCounter<3>::moves(), 1U);
EXPECT_EQ(OpCounter<3>::copies(), 0U);
}
+
+namespace {
+class Adder {
+public:
+ int32_t addSync(int32_t X, int32_t Y) { return X + Y; }
+ void addAsync(move_only_function<void(int32_t)> Return, int32_t X,
+ int32_t Y) {
+ Return(addSync(X, Y));
+ }
+};
+} // anonymous namespace
+
+static void adder_add_async_sps_wrapper(orc_rt_SessionRef Session,
+ void *CallCtx,
+ orc_rt_WrapperFunctionReturn Return,
+ orc_rt_WrapperFunctionBuffer ArgBytes) {
+ SPSWrapperFunction<int32_t(SPSExecutorAddr, int32_t, int32_t)>::handle(
+ Session, CallCtx, Return, ArgBytes,
+ WrapperFunction::handleWithAsyncMethod(&Adder::addAsync));
+}
+
+TEST(SPSWrapperFunctionUtilsTest, HandleWtihAsyncMethod) {
+ auto A = std::make_unique<Adder>();
+ int32_t Result = 0;
+ SPSWrapperFunction<int32_t(SPSExecutorAddr, int32_t, int32_t)>::call(
+ DirectCaller(nullptr, adder_add_async_sps_wrapper),
+ [&](Expected<int32_t> R) { Result = cantFail(std::move(R)); },
+ ExecutorAddr::fromPtr(A.get()), 41, 1);
+
+ EXPECT_EQ(Result, 42);
+}
+
+static void adder_add_sync_sps_wrapper(orc_rt_SessionRef Session, void *CallCtx,
+ orc_rt_WrapperFunctionReturn Return,
+ orc_rt_WrapperFunctionBuffer ArgBytes) {
+ SPSWrapperFunction<int32_t(SPSExecutorAddr, int32_t, int32_t)>::handle(
+ Session, CallCtx, Return, ArgBytes,
+ WrapperFunction::handleWithSyncMethod(&Adder::addSync));
+}
+
+TEST(SPSWrapperFunctionUtilsTest, HandleWithSyncMethod) {
+ auto A = std::make_unique<Adder>();
+ int32_t Result = 0;
+ SPSWrapperFunction<int32_t(SPSExecutorAddr, int32_t, int32_t)>::call(
+ DirectCaller(nullptr, adder_add_sync_sps_wrapper),
+ [&](Expected<int32_t> R) { Result = cantFail(std::move(R)); },
+ ExecutorAddr::fromPtr(A.get()), 41, 1);
+
+ EXPECT_EQ(Result, 42);
+}
More information about the llvm-commits
mailing list