[llvm] [orc-rt] Add Proxy, a protocol-agnostic controller-call handle (PR #217792)

Lang Hames via llvm-commits llvm-commits at lists.llvm.org
Thu Aug 20 17:19:50 PDT 2026


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

Introduce Proxy<RetT(ArgTs...)>, a typed handle for invoking a controller-side operation from the executor. A Proxy abstracts over how a call reaches the controller: it holds an opaque callee tag and a dispatch function (supplied by a per-protocol spec) and forwards calls through the Session.

This is a cut-down port of llvm/ExecutionEngine/Orc/Proxy.h, with two deliberate differences:

 - The callee is identified by an opaque tag (const void *, typically the address of a controller-side global) rather than an ExecutorAddr, since the executor->controller direction dispatches by tag.

 - Only the asynchronous (OnComplete) call operator is provided. The blocking convenience operator is omitted: the executor may be single-threaded or freestanding and cannot rely on std::promise/future or on blocking a dispatch thread.

Add ProxyTest covering default construction, the void->Error and T->Expected<T> return mappings, and argument/tag forwarding.

>From 321e95850ec13f82ed8c1ffba5c39dca50a21c69 Mon Sep 17 00:00:00 2001
From: Lang Hames <lhames at gmail.com>
Date: Fri, 21 Aug 2026 08:47:46 +1000
Subject: [PATCH] [orc-rt] Add Proxy, a protocol-agnostic controller-call
 handle

Introduce Proxy<RetT(ArgTs...)>, a typed handle for invoking a
controller-side operation from the executor. A Proxy abstracts
over how a call reaches the controller: it holds an opaque callee
tag and a dispatch function (supplied by a per-protocol spec) and
forwards calls through the Session.

This is a cut-down port of llvm/ExecutionEngine/Orc/Proxy.h, with
two deliberate differences:

 - The callee is identified by an opaque tag (const void *,
   typically the address of a controller-side global) rather than
   an ExecutorAddr, since the executor->controller direction
   dispatches by tag.

 - Only the asynchronous (OnComplete) call operator is provided.
   The blocking convenience operator is omitted: the executor may
   be single-threaded or freestanding and cannot rely on
   std::promise/future or on blocking a dispatch thread.

Add ProxyTest covering default construction, the void->Error and
T->Expected<T> return mappings, and argument/tag forwarding.
---
 orc-rt/include/CMakeLists.txt   |  1 +
 orc-rt/include/orc-rt/Proxy.h   | 98 +++++++++++++++++++++++++++++++++
 orc-rt/test/unit/CMakeLists.txt |  1 +
 orc-rt/test/unit/ProxyTest.cpp  | 94 +++++++++++++++++++++++++++++++
 4 files changed, 194 insertions(+)
 create mode 100644 orc-rt/include/orc-rt/Proxy.h
 create mode 100644 orc-rt/test/unit/ProxyTest.cpp

diff --git a/orc-rt/include/CMakeLists.txt b/orc-rt/include/CMakeLists.txt
index 40c16d681e783..3a63f972549a2 100644
--- a/orc-rt/include/CMakeLists.txt
+++ b/orc-rt/include/CMakeLists.txt
@@ -20,6 +20,7 @@ set(ORC_RT_HEADERS
     orc-rt/Math.h
     orc-rt/MemoryFlags.h
     orc-rt/NativeDylibManager.h
+    orc-rt/Proxy.h
     orc-rt/QueueingRunner.h
     orc-rt/RTTI.h
     orc-rt/SPSAllocAction.h
diff --git a/orc-rt/include/orc-rt/Proxy.h b/orc-rt/include/orc-rt/Proxy.h
new file mode 100644
index 0000000000000..2b4b8dde0de2c
--- /dev/null
+++ b/orc-rt/include/orc-rt/Proxy.h
@@ -0,0 +1,98 @@
+//===------ Proxy.h - Protocol-agnostic controller call APIs ----*- 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
+//
+//===----------------------------------------------------------------------===//
+//
+// Protocol-agnostic interface for invoking a controller-side operation from the
+// executor. A Proxy abstracts over how a call reaches the controller, so
+// callers can be written once regardless of the underlying transport or
+// serialization.
+//
+// The callee is named by an opaque tag (typically the address of a
+// controller-side global). A Proxy's dispatch function -- supplied by a spec
+// for some concrete protocol -- routes the call through the Session (e.g. via
+// callController). Named proxies for specific operation families, and their
+// specs, live alongside the utilities that use them.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef ORC_RT_PROXY_H
+#define ORC_RT_PROXY_H
+
+#include "orc-rt/Error.h"
+#include "orc-rt/move_only_function.h"
+
+#include <cassert>
+#include <utility>
+
+namespace orc_rt {
+
+class Session;
+
+class ProxyBase {
+public:
+  ProxyBase() = default;
+  explicit ProxyBase(const void *CalleeTag) : CalleeTag(CalleeTag) {}
+
+  /// Returns the callee tag.
+  const void *calleeTag() const { return CalleeTag; }
+
+  /// Evaluates to true if the callee is non-null.
+  explicit operator bool() const { return !!CalleeTag; }
+
+private:
+  const void *CalleeTag = nullptr;
+};
+
+template <typename FnT> class Proxy;
+
+namespace detail {
+
+template <typename T> struct ProxyErrorRet {
+  using type = Expected<T>;
+};
+template <> struct ProxyErrorRet<void> {
+  using type = Error;
+};
+template <> struct ProxyErrorRet<Error> {
+  using type = Error;
+};
+template <typename T> struct ProxyErrorRet<Expected<T>> {
+  using type = Expected<T>;
+};
+
+} // namespace detail
+
+template <typename RetT, typename... ArgTs>
+class Proxy<RetT(ArgTs...)> : public ProxyBase {
+public:
+  using FnType = RetT(ArgTs...);
+
+  using CalleeRetT = RetT;
+
+  using ErrorRetT = typename detail::ProxyErrorRet<RetT>::type;
+
+  using DispatchFn = void (*)(move_only_function<void(ErrorRetT)> OnComplete,
+                              Session &S, const void *CalleeTag,
+                              const ArgTs &...Args);
+
+  Proxy() = default;
+  Proxy(DispatchFn Dispatch, const void *CalleeTag)
+      : ProxyBase(CalleeTag), Dispatch(Dispatch) {}
+
+  void operator()(move_only_function<void(ErrorRetT)> OnComplete, Session &S,
+                  const ArgTs &...Args) const {
+    assert(Dispatch && "Proxy's Dispatch member is not set");
+    Dispatch(std::move(OnComplete), S, calleeTag(), Args...);
+  }
+
+private:
+  DispatchFn Dispatch = nullptr;
+};
+
+} // namespace orc_rt
+
+#endif // ORC_RT_PROXY_H
diff --git a/orc-rt/test/unit/CMakeLists.txt b/orc-rt/test/unit/CMakeLists.txt
index 1916000506e31..ecbb35af8b880 100644
--- a/orc-rt/test/unit/CMakeLists.txt
+++ b/orc-rt/test/unit/CMakeLists.txt
@@ -37,6 +37,7 @@ add_orc_rt_unittest(CoreTests
   MemoryFlagsTest.cpp
   NativeDylibManagerSPSCITest.cpp
   NativeDylibManagerTest.cpp
+  ProxyTest.cpp
   QueueingRunnerTest.cpp
   RTTITest.cpp
   SPSAllocActionTest.cpp
diff --git a/orc-rt/test/unit/ProxyTest.cpp b/orc-rt/test/unit/ProxyTest.cpp
new file mode 100644
index 0000000000000..3f69ffdb01790
--- /dev/null
+++ b/orc-rt/test/unit/ProxyTest.cpp
@@ -0,0 +1,94 @@
+//===- ProxyTest.cpp ------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// Tests for orc-rt's Proxy.h APIs.
+//
+//===----------------------------------------------------------------------===//
+
+#include "orc-rt/Proxy.h"
+
+#include "CommonTestUtils.h"
+
+#include "gtest/gtest.h"
+
+#include <optional>
+#include <utility>
+
+using namespace orc_rt;
+
+namespace {
+
+// Reports the callee's void result (success), exercising the void -> Error
+// return mapping.
+void voidDispatch(move_only_function<void(Error)> OnComplete, Session &,
+                  const void *) {
+  OnComplete(Error::success());
+}
+
+// Returns its argument plus one, exercising argument forwarding and the
+// T -> Expected<T> return mapping.
+void addOneDispatch(move_only_function<void(Expected<int>)> OnComplete,
+                    Session &, const void *, const int &X) {
+  OnComplete(X + 1);
+}
+
+// Returns the callee tag it was handed, exercising tag forwarding through
+// operator().
+void returnTagDispatch(
+    move_only_function<void(Expected<const void *>)> OnComplete, Session &,
+    const void *Tag) {
+  OnComplete(Tag);
+}
+
+} // namespace
+
+TEST(ProxyTest, DefaultConstructedProxyIsNull) {
+  Proxy<void()> P;
+  EXPECT_FALSE(P);
+}
+
+TEST(ProxyTest, DispatchReportsVoidResult) {
+  Session S(mockExecutorProcessInfo(), noDispatch, noErrors);
+
+  int Tag = 0;
+  Proxy<void()> P(voidDispatch, &Tag);
+  EXPECT_TRUE(P);
+
+  bool Completed = false;
+  P(
+      [&](Error Err) {
+        cantFail(std::move(Err));
+        Completed = true;
+      },
+      S);
+  EXPECT_TRUE(Completed);
+}
+
+TEST(ProxyTest, DispatchForwardsArgsAndResult) {
+  Session S(mockExecutorProcessInfo(), noDispatch, noErrors);
+
+  int Tag = 0;
+  Proxy<int(int)> P(addOneDispatch, &Tag);
+
+  std::optional<int> Result;
+  P([&](Expected<int> R) { Result = cantFail(std::move(R)); }, S, 42);
+  ASSERT_TRUE(Result.has_value());
+  EXPECT_EQ(*Result, 43);
+}
+
+TEST(ProxyTest, DispatchForwardsCalleeTag) {
+  Session S(mockExecutorProcessInfo(), noDispatch, noErrors);
+
+  int Tag = 0;
+  Proxy<const void *()> P(returnTagDispatch, &Tag);
+
+  std::optional<const void *> Result;
+  P([&](Expected<const void *> R) { Result = cantFail(std::move(R)); }, S);
+  ASSERT_TRUE(Result.has_value());
+  EXPECT_EQ(*Result, &Tag);
+}



More information about the llvm-commits mailing list