[llvm] [ORC] Replace JIT-dispatch handlers with call-controller handlers (PR #228628)

Lang Hames via llvm-commits llvm-commits at lists.llvm.org
Fri Oct 2 18:04:39 PDT 2026


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

This is the executor-to-controller counterpart to Proxy: where Proxy provides a uniform way to call functions in the executor, call-controller handlers provide a uniform way for the executor to call handlers in the controller.

Handlers are now registered as CallControllerHandlerBindings, which decouples ExecutionSession's handler registration from SimplePackedSerialization. The new bindCallControllerHandlerSPS utility makes it easy to introduce a new SPS handler with a one-liner, using either a lambda:

  ES.registerCallControllerHandlers(
      JD, bindCallControllerHandlerSPS<int32_t(int32_t, int32_t)>(
              SymbolNameSpec::c("add_tag"),
              [](unique_function<void(int32_t)> Return,
                 int32_t X, int32_t Y) {
                Return(X + Y);
              }));

or a method:

  ES.registerCallControllerHandlers(
      JD, bindCallControllerHandlerSPS<SPSError(SPSExecutorAddr)>(
              SymbolNameSpec::c("foo_tag"), &FooInstance, &Foo::func));

Tag names are expressed as SymbolNameSpecs, so callers no longer need to pre-mangle or intern them.

This replaces ExecutionSession's JIT-dispatch handler API (JITDispatchHandlerAssociationMap, wrapAsyncWithSPS, registerJITDispatchHandlers, runJITDispatchHandler) with:

- CallControllerHandlerBinding, which associates a tag name and SymbolLookupFlags with a CallControllerHandlerFn.
- registerCallControllerHandlers, which takes a std::vector of bindings, plus a variadic convenience overload so that bindings can be constructed inline at the call site.
- bindCallControllerHandlerSPS (in the new BindCallControllerHandlerSPS.h header), which replaces wrapAsyncWithSPS.
- runCallControllerHandler, which replaces runJITDispatchHandler. Handlers now receive their argument bytes as a WrapperFunctionBuffer.

Tags are now looked up using each binding's lookup flags, which default to RequiredSymbol. Previously all tags were looked up as weakly referenced, and handlers for missing tags were silently dropped. A missing tag is now an error unless its binding is weakly referenced.

Since SymbolNameSpec takes care of mangling, MachOPlatform no longer needs the "__orc_rt_resolve_tag" -> "___orc_rt_resolve_tag" alias in standardLazyCompilationAliases, and it has been removed.

All in-tree clients have been updated. JITLinkLazyReexportsTest.Basics now defines __orc_rt_resolve_tag, which it previously relied on the weak lookup to tolerate. New unit tests cover tag mangling, missing required and weak tags, duplicate bindings, and already-registered tags.

Assisted-by: Claude

>From 646b09ba0ccdfb147af2033269c753f34289ee2a Mon Sep 17 00:00:00 2001
From: Lang Hames <lhames at gmail.com>
Date: Sat, 3 Oct 2026 10:50:49 +1000
Subject: [PATCH] [ORC] Replace JIT-dispatch handlers with call-controller
 handlers

This is the executor-to-controller counterpart to Proxy: where Proxy
provides a uniform way to call functions in the executor,
call-controller handlers provide a uniform way for the executor to call
handlers in the controller.

Handlers are now registered as CallControllerHandlerBindings, which
decouples ExecutionSession's handler registration from
SimplePackedSerialization. The new bindCallControllerHandlerSPS utility
makes it easy to introduce a new SPS handler with a one-liner, using
either a lambda:

  ES.registerCallControllerHandlers(
      JD, bindCallControllerHandlerSPS<int32_t(int32_t, int32_t)>(
              SymbolNameSpec::c("add_tag"),
              [](unique_function<void(int32_t)> Return,
                 int32_t X, int32_t Y) {
                Return(X + Y);
              }));

or a method:

  ES.registerCallControllerHandlers(
      JD, bindCallControllerHandlerSPS<SPSError(SPSExecutorAddr)>(
              SymbolNameSpec::c("foo_tag"), &FooInstance, &Foo::func));

Tag names are expressed as SymbolNameSpecs, so callers no longer need
to pre-mangle or intern them.

This replaces ExecutionSession's JIT-dispatch handler API
(JITDispatchHandlerAssociationMap, wrapAsyncWithSPS,
registerJITDispatchHandlers, runJITDispatchHandler) with:

- CallControllerHandlerBinding, which associates a tag name and
  SymbolLookupFlags with a CallControllerHandlerFn.
- registerCallControllerHandlers, which takes a std::vector of bindings,
  plus a variadic convenience overload so that bindings can be
  constructed inline at the call site.
- bindCallControllerHandlerSPS (in the new BindCallControllerHandlerSPS.h
  header), which replaces wrapAsyncWithSPS.
- runCallControllerHandler, which replaces runJITDispatchHandler.
  Handlers now receive their argument bytes as a WrapperFunctionBuffer.

Tags are now looked up using each binding's lookup flags, which default
to RequiredSymbol. Previously all tags were looked up as weakly
referenced, and handlers for missing tags were silently dropped. A
missing tag is now an error unless its binding is weakly referenced.

Since SymbolNameSpec takes care of mangling, MachOPlatform no longer
needs the "__orc_rt_resolve_tag" -> "___orc_rt_resolve_tag" alias in
standardLazyCompilationAliases, and it has been removed.

All in-tree clients have been updated. JITLinkLazyReexportsTest.Basics
now defines __orc_rt_resolve_tag, which it previously relied on the weak
lookup to tolerate. New unit tests cover tag mangling, missing required
and weak tags, duplicate bindings, and already-registered tags.

Assisted-by: Claude
---
 .../Orc/BindCallControllerHandlerSPS.h        |  60 ++++++++
 llvm/include/llvm/ExecutionEngine/Orc/Core.h  | 127 +++++++--------
 llvm/lib/ExecutionEngine/Orc/COFFPlatform.cpp |  19 +--
 llvm/lib/ExecutionEngine/Orc/Core.cpp         |  70 +++++----
 .../ExecutionEngine/Orc/ELFNixPlatform.cpp    |  19 ++-
 llvm/lib/ExecutionEngine/Orc/InProcessEPC.cpp |   2 +-
 .../lib/ExecutionEngine/Orc/LazyReexports.cpp |  15 +-
 .../lib/ExecutionEngine/Orc/MachOPlatform.cpp |  22 ++-
 .../ExecutionEngine/Orc/ReOptimizeLayer.cpp   |  10 +-
 .../Orc/SelfExecutorProcessControl.cpp        |   2 +-
 .../ExecutionEngine/Orc/SimpleRemoteEPC.cpp   |   2 +-
 ...ecutionSessionWrapperFunctionCallsTest.cpp | 144 +++++++++++++++++-
 .../ExecutionEngine/Orc/InProcessEPCTest.cpp  |  19 +--
 .../Orc/LazyCallThroughAndReexportsTest.cpp   |   5 +-
 14 files changed, 365 insertions(+), 151 deletions(-)
 create mode 100644 llvm/include/llvm/ExecutionEngine/Orc/BindCallControllerHandlerSPS.h

diff --git a/llvm/include/llvm/ExecutionEngine/Orc/BindCallControllerHandlerSPS.h b/llvm/include/llvm/ExecutionEngine/Orc/BindCallControllerHandlerSPS.h
new file mode 100644
index 000000000000000..9b5671356d53463
--- /dev/null
+++ b/llvm/include/llvm/ExecutionEngine/Orc/BindCallControllerHandlerSPS.h
@@ -0,0 +1,60 @@
+//===--------------- BindCallControllerHandlerSPS.h -------------*- 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
+//
+//===----------------------------------------------------------------------===//
+//
+// Convenience functions for building call-controller handlers using SPS
+// serialization / deserialization.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_EXECUTIONENGINE_ORC_BINDCALLCONTROLLERHANDLERSPS_H
+#define LLVM_EXECUTIONENGINE_ORC_BINDCALLCONTROLLERHANDLERSPS_H
+
+#include "llvm/ExecutionEngine/Orc/Core.h"
+#include "llvm/ExecutionEngine/Orc/Shared/SymbolNameSpec.h"
+#include "llvm/ExecutionEngine/Orc/Shared/WrapperFunctionUtils.h"
+#include "llvm/ExecutionEngine/Orc/SymbolLookupSet.h"
+
+namespace llvm::orc {
+
+/// Bind a call-controller handler that takes concrete argument types (and a
+/// sender for a concrete return type) to the tag with the given name. Uses SPS
+/// to unpack the arguments and pack the result.
+template <typename SPSSigT, typename HandlerT>
+ExecutionSession::CallControllerHandlerBinding bindCallControllerHandlerSPS(
+    SymbolNameSpec Name, HandlerT &&Handler,
+    SymbolLookupFlags LF = SymbolLookupFlags::RequiredSymbol) {
+  return ExecutionSession::CallControllerHandlerBinding(
+      Name,
+      [Handler = std::forward<HandlerT>(Handler)](
+          ExecutionSession::CallControllerReturnFn Return,
+          shared::WrapperFunctionBuffer ArgBytes) mutable {
+        shared::WrapperFunction<SPSSigT>::handleAsync(
+            ArgBytes.data(), ArgBytes.size(), std::move(Return), Handler);
+      },
+      LF);
+}
+
+/// Bind a class method as a call-controller handler. The method takes
+/// concrete argument types (and a sender for a concrete return type). Uses SPS
+/// to unpack the arguments and pack the result.
+template <typename SPSSigT, typename ClassT, typename... MethodArgTs>
+ExecutionSession::CallControllerHandlerBinding bindCallControllerHandlerSPS(
+    SymbolNameSpec Name, ClassT *Instance,
+    void (ClassT::*Method)(MethodArgTs...),
+    SymbolLookupFlags LF = SymbolLookupFlags::RequiredSymbol) {
+  return bindCallControllerHandlerSPS<SPSSigT>(
+      Name,
+      [Instance, Method](MethodArgTs &&...MethodArgs) {
+        (Instance->*Method)(std::forward<MethodArgTs>(MethodArgs)...);
+      },
+      LF);
+}
+
+} // namespace llvm::orc
+
+#endif // LLVM_EXECUTIONENGINE_ORC_BINDCALLCONTROLLERHANDLERSPS_H
diff --git a/llvm/include/llvm/ExecutionEngine/Orc/Core.h b/llvm/include/llvm/ExecutionEngine/Orc/Core.h
index 7754a554b35a61d..1e9f6cff0590959 100644
--- a/llvm/include/llvm/ExecutionEngine/Orc/Core.h
+++ b/llvm/include/llvm/ExecutionEngine/Orc/Core.h
@@ -24,6 +24,7 @@
 #include "llvm/ExecutionEngine/Orc/MaterializationUnit.h"
 #include "llvm/ExecutionEngine/Orc/Shared/ExecutorAddress.h"
 #include "llvm/ExecutionEngine/Orc/Shared/ExecutorSymbolDef.h"
+#include "llvm/ExecutionEngine/Orc/Shared/SymbolNameSpec.h"
 #include "llvm/ExecutionEngine/Orc/Shared/WrapperFunctionUtils.h"
 #include "llvm/ExecutionEngine/Orc/SymbolLookupSet.h"
 #include "llvm/ExecutionEngine/Orc/TaskDispatch.h"
@@ -1120,19 +1121,41 @@ class ExecutionSession {
   /// For reporting errors.
   using ErrorReporter = unique_function<void(Error)>;
 
-  /// Send a result to the remote.
-  using SendResultFunction = unique_function<void(shared::WrapperFunctionBuffer)>;
+  /// Function type for returning results from a call-controller handler.
+  using CallControllerReturnFn =
+      unique_function<void(shared::WrapperFunctionBuffer)>;
 
-  /// An asynchronous wrapper-function callable from the executor via
-  /// jit-dispatch.
-  using JITDispatchHandlerFunction = unique_function<void(
-      SendResultFunction SendResult,
-      const char *ArgData, size_t ArgSize)>;
+  /// A call-controller handler: handles calls from the executor made via the
+  /// ORC runtime's call-controller mechanism.
+  using CallControllerHandlerFn = unique_function<void(
+      CallControllerReturnFn, shared::WrapperFunctionBuffer)>;
 
-  /// A map associating tag names with asynchronous wrapper function
-  /// implementations in the JIT.
-  using JITDispatchHandlerAssociationMap =
-      DenseMap<SymbolStringPtr, JITDispatchHandlerFunction>;
+  /// Associates a call-controller handler with the name of the tag symbol that
+  /// the executor will use to call it, and the SymbolLookupFlags to use when
+  /// looking that tag up.
+  ///
+  /// The name is not copied (see SymbolNameSpec): the referenced string must
+  /// outlive the registerCallControllerHandlers call that the binding is
+  /// passed to. Ordinary string literals have static storage duration, so
+  /// they are always safe.
+  class CallControllerHandlerBinding {
+  public:
+    CallControllerHandlerBinding(
+        SymbolNameSpec Name, CallControllerHandlerFn Handler,
+        SymbolLookupFlags LF = SymbolLookupFlags::RequiredSymbol)
+        : Name(Name), Handler(std::move(Handler)), LF(LF) {}
+
+    SymbolNameSpec getName() const { return Name; }
+
+    CallControllerHandlerFn takeHandler() { return std::move(Handler); }
+
+    SymbolLookupFlags getLookupFlags() const { return LF; }
+
+  private:
+    SymbolNameSpec Name;
+    CallControllerHandlerFn Handler;
+    SymbolLookupFlags LF;
+  };
 
   /// Construct an ExecutionSession with the given ExecutorProcessControl
   /// object.
@@ -1400,57 +1423,41 @@ class ExecutionSession {
                           ArgBuffer);
   }
 
-  /// 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.
+  /// For each binding, look up its tag symbol in JD and register the binding's
+  /// call-controller handler for the tag's address. The handler becomes
+  /// callable from the executor via the ORC runtime's call-controller
+  /// mechanism, using the tag's address as the handler tag.
   ///
-  /// This function is intended to support easy construction of
-  /// AsyncHandlerWrapperFunctions that can be associated with a tag
-  /// (using registerJITDispatchHandler) and called from the executor.
-  template <typename SPSSignature, typename HandlerT>
-  static JITDispatchHandlerFunction wrapAsyncWithSPS(HandlerT &&H) {
-    return [H = std::forward<HandlerT>(H)](SendResultFunction SendResult,
-                                           const char *ArgData,
-                                           size_t ArgSize) mutable {
-      shared::WrapperFunction<SPSSignature>::handleAsync(
-          ArgData, ArgSize, std::move(SendResult), H);
-    };
-  }
-
-  /// Wrap a class method 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.
+  /// Tag names are mangled for the session's target triple, then looked up
+  /// in JD using LookupKind::Static and JITDylibLookupFlags::MatchAllSymbols
+  /// (hidden tags will be found), with each binding's SymbolLookupFlags. If a
+  /// weakly referenced tag is not found then its handler is dropped.
   ///
-  /// This function is intended to support easy construction of
-  /// AsyncHandlerWrapperFunctions that can be associated with a tag
-  /// (using registerJITDispatchHandler) and called from the executor.
-  template <typename SPSSignature, typename ClassT, typename... MethodArgTs>
-  static JITDispatchHandlerFunction
-  wrapAsyncWithSPS(ClassT *Instance, void (ClassT::*Method)(MethodArgTs...)) {
-    return wrapAsyncWithSPS<SPSSignature>(
-        [Instance, Method](MethodArgTs &&...MethodArgs) {
-          (Instance->*Method)(std::forward<MethodArgTs>(MethodArgs)...);
-        });
+  /// On failure no handlers are registered.
+  LLVM_ABI Error registerCallControllerHandlers(
+      JITDylib &JD, std::vector<CallControllerHandlerBinding> Hs);
+
+  /// Convenience overload of registerCallControllerHandlers that takes the
+  /// bindings as arguments, so that they can be constructed inline at the
+  /// call site.
+  template <typename... BindingTs>
+  std::enable_if_t<
+      (std::is_same_v<BindingTs, CallControllerHandlerBinding> && ...), Error>
+  registerCallControllerHandlers(JITDylib &JD, BindingTs &&...Hs) {
+    std::vector<CallControllerHandlerBinding> Bs;
+    Bs.reserve(sizeof...(Hs));
+    (Bs.push_back(std::move(Hs)), ...);
+    return registerCallControllerHandlers(JD, std::move(Bs));
   }
 
-  /// For each tag symbol name, associate the corresponding
-  /// AsyncHandlerWrapperFunction with the address of that symbol. The
-  /// handler becomes callable from the executor using the ORC runtime
-  /// __orc_rt_jit_dispatch function and the given tag.
-  ///
-  /// Tag symbols will be looked up in JD using LookupKind::Static,
-  /// JITDylibLookupFlags::MatchAllSymbols (hidden tags will be found), and
-  /// LookupFlags::WeaklyReferencedSymbol. Missing tag definitions will not
-  /// cause an error, the handler will simply be dropped.
-  LLVM_ABI Error registerJITDispatchHandlers(
-      JITDylib &JD, JITDispatchHandlerAssociationMap WFs);
-
-  /// Run a registered jit-side wrapper function.
+  /// Run the call-controller handler registered for the given tag address.
   /// This should be called by the ExecutorProcessControl instance in response
-  /// to incoming jit-dispatch requests from the executor.
-  LLVM_ABI void runJITDispatchHandler(SendResultFunction SendResult,
-                                      ExecutorAddr HandlerFnTagAddr,
-                                      shared::WrapperFunctionBuffer ArgBytes);
+  /// to calls from the executor made via the ORC runtime's call-controller
+  /// mechanism.
+  LLVM_ABI void
+  runCallControllerHandler(CallControllerReturnFn Return,
+                           ExecutorAddr HandlerFnTagAddr,
+                           shared::WrapperFunctionBuffer ArgBytes);
 
   /// Dump the state of all the JITDylibs in this session.
   LLVM_ABI void dump(raw_ostream &OS);
@@ -1582,9 +1589,9 @@ class ExecutionSession {
                         std::unique_ptr<MaterializationResponsibility>>>
       OutstandingMUs;
 
-  mutable std::mutex JITDispatchHandlersMutex;
-  DenseMap<ExecutorAddr, std::shared_ptr<JITDispatchHandlerFunction>>
-      JITDispatchHandlers;
+  mutable std::mutex CallControllerHandlersMutex;
+  DenseMap<ExecutorAddr, std::unique_ptr<CallControllerHandlerFn>>
+      CallControllerHandlers;
 };
 
 template <typename Func> Error ResourceTracker::withResourceKeyDo(Func &&F) {
diff --git a/llvm/lib/ExecutionEngine/Orc/COFFPlatform.cpp b/llvm/lib/ExecutionEngine/Orc/COFFPlatform.cpp
index 4f777e95224d451..35cf4879892819a 100644
--- a/llvm/lib/ExecutionEngine/Orc/COFFPlatform.cpp
+++ b/llvm/lib/ExecutionEngine/Orc/COFFPlatform.cpp
@@ -9,6 +9,7 @@
 #include "llvm/ExecutionEngine/Orc/COFFPlatform.h"
 #include "llvm/ExecutionEngine/Orc/Mangling.h"
 
+#include "llvm/ExecutionEngine/Orc/BindCallControllerHandlerSPS.h"
 #include "llvm/ExecutionEngine/Orc/COFF.h"
 #include "llvm/ExecutionEngine/Orc/CallProxiesSPS.h"
 #include "llvm/ExecutionEngine/Orc/DebugUtils.h"
@@ -653,20 +654,20 @@ void COFFPlatform::rt_lookupSymbol(SendSymbolAddressFn SendResult,
 }
 
 Error COFFPlatform::associateRuntimeSupportFunctions(JITDylib &PlatformJD) {
-  ExecutionSession::JITDispatchHandlerAssociationMap WFs;
-
   using LookupSymbolSPSSig =
       SPSExpected<SPSExecutorAddr>(SPSExecutorAddr, SPSString);
-  WFs[ES.intern("__orc_rt_coff_symbol_lookup_tag")] =
-      ES.wrapAsyncWithSPS<LookupSymbolSPSSig>(this,
-                                              &COFFPlatform::rt_lookupSymbol);
+
   using PushInitializersSPSSig =
       SPSExpected<SPSCOFFJITDylibDepInfoMap>(SPSExecutorAddr);
-  WFs[ES.intern("__orc_rt_coff_push_initializers_tag")] =
-      ES.wrapAsyncWithSPS<PushInitializersSPSSig>(
-          this, &COFFPlatform::rt_pushInitializers);
 
-  return ES.registerJITDispatchHandlers(PlatformJD, std::move(WFs));
+  return ES.registerCallControllerHandlers(
+      PlatformJD,
+      bindCallControllerHandlerSPS<LookupSymbolSPSSig>(
+          SymbolNameSpec::c("__orc_rt_coff_symbol_lookup_tag"), this,
+          &COFFPlatform::rt_lookupSymbol),
+      bindCallControllerHandlerSPS<PushInitializersSPSSig>(
+          SymbolNameSpec::c("__orc_rt_coff_push_initializers_tag"), this,
+          &COFFPlatform::rt_pushInitializers));
 }
 
 Error COFFPlatform::runBootstrapInitializers(JDBootstrapState &BState) {
diff --git a/llvm/lib/ExecutionEngine/Orc/Core.cpp b/llvm/lib/ExecutionEngine/Orc/Core.cpp
index e0bfe34ca8b4759..b026addd5b083e7 100644
--- a/llvm/lib/ExecutionEngine/Orc/Core.cpp
+++ b/llvm/lib/ExecutionEngine/Orc/Core.cpp
@@ -12,6 +12,7 @@
 #include "llvm/Config/llvm-config.h"
 #include "llvm/ExecutionEngine/Orc/AbsoluteSymbols.h"
 #include "llvm/ExecutionEngine/Orc/DebugUtils.h"
+#include "llvm/ExecutionEngine/Orc/Shared/Mangler.h"
 #include "llvm/ExecutionEngine/Orc/Shared/OrcError.h"
 #include "llvm/Support/FormatVariadic.h"
 #include "llvm/Support/MSVCErrorWorkarounds.h"
@@ -1856,64 +1857,81 @@ ExecutionSession::lookup(ArrayRef<JITDylib *> SearchOrder, StringRef Name,
   return lookup(SearchOrder, intern(Name), RequiredState);
 }
 
-Error ExecutionSession::registerJITDispatchHandlers(
-    JITDylib &JD, JITDispatchHandlerAssociationMap WFs) {
+Error ExecutionSession::registerCallControllerHandlers(
+    JITDylib &JD, std::vector<CallControllerHandlerBinding> Hs) {
+
+  // Mangle and intern the tag names, and take the handlers.
+  Mangler Mangle(getTargetTriple());
+  DenseMap<SymbolStringPtr, CallControllerHandlerFn> Handlers;
+  SymbolLookupSet LookupSet;
+  for (auto &H : Hs) {
+    auto TagName = Mangle.withMangledNameDo(
+        [&](StringRef N) { return intern(N); }, H.getName());
+    auto Handler = H.takeHandler();
+    assert(Handler && "CallControllerHandler implementation missing");
+    if (!Handlers.try_emplace(TagName, std::move(Handler)).second)
+      return make_error<StringError>(
+          "Duplicate call-controller handler binding for tag " + *TagName,
+          inconvertibleErrorCode());
+    LookupSet.add(std::move(TagName), H.getLookupFlags());
+  }
 
   auto TagSyms = lookup({{&JD, JITDylibLookupFlags::MatchAllSymbols}},
-                        SymbolLookupSet::fromMapKeys(
-                            WFs, SymbolLookupFlags::WeaklyReferencedSymbol));
+                        std::move(LookupSet));
   if (!TagSyms)
     return TagSyms.takeError();
 
   // Associate tag addresses with implementations.
-  std::lock_guard<std::mutex> Lock(JITDispatchHandlersMutex);
+  std::lock_guard<std::mutex> Lock(CallControllerHandlersMutex);
 
   // Check that no tags are being overwritten.
   for (auto &[TagName, TagSym] : *TagSyms) {
     auto TagAddr = TagSym.getAddress();
-    if (JITDispatchHandlers.count(TagAddr))
+    if (CallControllerHandlers.count(TagAddr))
       return make_error<StringError>("Tag " + formatv("{0:x}", TagAddr) +
                                          " (for " + *TagName +
                                          ") already registered",
                                      inconvertibleErrorCode());
   }
 
-  // At this point we're guaranteed to succeed. Install the handlers.
+  // At this point we're guaranteed to succeed. Install the handlers. Handlers
+  // for weakly referenced tags that weren't found are dropped.
   for (auto &[TagName, TagSym] : *TagSyms) {
     auto TagAddr = TagSym.getAddress();
-    auto I = WFs.find(TagName);
-    assert(I != WFs.end() && I->second &&
-           "JITDispatchHandler implementation missing");
-    JITDispatchHandlers[TagAddr] =
-        std::make_shared<JITDispatchHandlerFunction>(std::move(I->second));
+    auto I = Handlers.find(TagName);
+    assert(I != Handlers.end() && "No handler for tag in lookup result");
+    CallControllerHandlers[TagAddr] =
+        std::make_unique<CallControllerHandlerFn>(std::move(I->second));
     LLVM_DEBUG({
-      dbgs() << "Associated function tag \"" << *TagName << "\" ("
-             << formatv("{0:x}", TagAddr) << ") with handler\n";
+      dbgs() << "Registered call-controller handler for tag \"" << *TagName
+             << "\" (" << formatv("{0:x}", TagAddr) << ")\n";
     });
   }
 
   return Error::success();
 }
 
-void ExecutionSession::runJITDispatchHandler(
-    SendResultFunction SendResult, ExecutorAddr HandlerFnTagAddr,
+void ExecutionSession::runCallControllerHandler(
+    CallControllerReturnFn Return, ExecutorAddr HandlerFnTagAddr,
     shared::WrapperFunctionBuffer ArgBytes) {
 
-  std::shared_ptr<JITDispatchHandlerFunction> F;
+  // Handlers are never removed, so H remains valid after the lock is
+  // released.
+  CallControllerHandlerFn *H = nullptr;
   {
-    std::lock_guard<std::mutex> Lock(JITDispatchHandlersMutex);
-    auto I = JITDispatchHandlers.find(HandlerFnTagAddr);
-    if (I != JITDispatchHandlers.end())
-      F = I->second;
+    std::lock_guard<std::mutex> Lock(CallControllerHandlersMutex);
+    auto I = CallControllerHandlers.find(HandlerFnTagAddr);
+    if (I != CallControllerHandlers.end())
+      H = I->second.get();
   }
 
-  if (F)
-    (*F)(std::move(SendResult), ArgBytes.data(), ArgBytes.size());
-  else
-    SendResult(shared::WrapperFunctionBuffer::createOutOfBandError(
-        ("No function registered for tag " +
+  if (!H)
+    return Return(shared::WrapperFunctionBuffer::createOutOfBandError(
+        ("No call-controller handler registered for tag " +
          formatv("{0:x16}", HandlerFnTagAddr))
             .str()));
+
+  (*H)(std::move(Return), std::move(ArgBytes));
 }
 
 void ExecutionSession::dump(raw_ostream &OS) {
diff --git a/llvm/lib/ExecutionEngine/Orc/ELFNixPlatform.cpp b/llvm/lib/ExecutionEngine/Orc/ELFNixPlatform.cpp
index af80b2de80eec7f..5a2b5961d5afacc 100644
--- a/llvm/lib/ExecutionEngine/Orc/ELFNixPlatform.cpp
+++ b/llvm/lib/ExecutionEngine/Orc/ELFNixPlatform.cpp
@@ -15,6 +15,7 @@
 #include "llvm/ExecutionEngine/JITLink/ppc64.h"
 #include "llvm/ExecutionEngine/JITLink/systemz.h"
 #include "llvm/ExecutionEngine/JITLink/x86_64.h"
+#include "llvm/ExecutionEngine/Orc/BindCallControllerHandlerSPS.h"
 #include "llvm/ExecutionEngine/Orc/ExecutionUtils.h"
 #include "llvm/ExecutionEngine/Orc/SPSProxySpec.h"
 #include "llvm/ExecutionEngine/Orc/Shared/ObjectFormats.h"
@@ -477,21 +478,19 @@ ELFNixPlatform::ELFNixPlatform(
 }
 
 Error ELFNixPlatform::associateRuntimeSupportFunctions(JITDylib &PlatformJD) {
-  ExecutionSession::JITDispatchHandlerAssociationMap WFs;
-
   using RecordInitializersSPSSig =
       SPSExpected<SPSELFNixJITDylibDepInfoMap>(SPSExecutorAddr);
-  WFs[ES.intern("__orc_rt_elfnix_push_initializers_tag")] =
-      ES.wrapAsyncWithSPS<RecordInitializersSPSSig>(
-          this, &ELFNixPlatform::rt_recordInitializers);
-
   using LookupSymbolSPSSig =
       SPSExpected<SPSExecutorAddr>(SPSExecutorAddr, SPSString);
-  WFs[ES.intern("__orc_rt_elfnix_symbol_lookup_tag")] =
-      ES.wrapAsyncWithSPS<LookupSymbolSPSSig>(this,
-                                              &ELFNixPlatform::rt_lookupSymbol);
 
-  return ES.registerJITDispatchHandlers(PlatformJD, std::move(WFs));
+  return ES.registerCallControllerHandlers(
+      PlatformJD,
+      bindCallControllerHandlerSPS<RecordInitializersSPSSig>(
+          SymbolNameSpec::c("__orc_rt_elfnix_push_initializers_tag"), this,
+          &ELFNixPlatform::rt_recordInitializers),
+      bindCallControllerHandlerSPS<LookupSymbolSPSSig>(
+          SymbolNameSpec::c("__orc_rt_elfnix_symbol_lookup_tag"), this,
+          &ELFNixPlatform::rt_lookupSymbol));
 }
 
 void ELFNixPlatform::pushInitializersLoop(
diff --git a/llvm/lib/ExecutionEngine/Orc/InProcessEPC.cpp b/llvm/lib/ExecutionEngine/Orc/InProcessEPC.cpp
index 3f1a0621d8a31ab..ed61aa5ca231c19 100644
--- a/llvm/lib/ExecutionEngine/Orc/InProcessEPC.cpp
+++ b/llvm/lib/ExecutionEngine/Orc/InProcessEPC.cpp
@@ -206,7 +206,7 @@ void InProcessEPC::callJITDispatch(uint64_t CallId, void *HandlerTag,
   LLVM_DEBUG(dbgs() << "InProcessEPC: JIT-dispatch call id " << CallId << " to "
                     << HandlerTag << "\n");
 
-  getExecutionSession().runJITDispatchHandler(
+  getExecutionSession().runCallControllerHandler(
       [this, CallId](shared::WrapperFunctionBuffer ResultBytes) {
         LLVM_DEBUG(dbgs() << "InProcessEPC: Returning JIT-dispatch result for "
                              "call id "
diff --git a/llvm/lib/ExecutionEngine/Orc/LazyReexports.cpp b/llvm/lib/ExecutionEngine/Orc/LazyReexports.cpp
index fc9a6eaead5a359..89eaddbefa7d26a 100644
--- a/llvm/lib/ExecutionEngine/Orc/LazyReexports.cpp
+++ b/llvm/lib/ExecutionEngine/Orc/LazyReexports.cpp
@@ -8,6 +8,7 @@
 
 #include "llvm/ExecutionEngine/Orc/LazyReexports.h"
 
+#include "llvm/ExecutionEngine/Orc/BindCallControllerHandlerSPS.h"
 #include "llvm/ExecutionEngine/Orc/ObjectLinkingLayer.h"
 #include "llvm/ExecutionEngine/Orc/OrcABISupport.h"
 #include "llvm/ExecutionEngine/Orc/Shared/SimplePackedSerialization.h"
@@ -339,16 +340,12 @@ LazyReexportsManager::LazyReexportsManager(EmitTrampolinesFn EmitTrampolines,
       EmitTrampolines(std::move(EmitTrampolines)), RSMgr(RSMgr), L(L) {
 
   using namespace shared;
-
   ErrorAsOutParameter _(&Err);
-
-  ExecutionSession::JITDispatchHandlerAssociationMap WFs;
-
-  WFs[ES.intern("__orc_rt_resolve_tag")] =
-      ES.wrapAsyncWithSPS<SPSExpected<SPSExecutorSymbolDef>(SPSExecutorAddr)>(
-          this, &LazyReexportsManager::resolve);
-
-  Err = ES.registerJITDispatchHandlers(PlatformJD, std::move(WFs));
+  using ResolveSPSSig = SPSExpected<SPSExecutorSymbolDef>(SPSExecutorAddr);
+  Err = ES.registerCallControllerHandlers(
+      PlatformJD, bindCallControllerHandlerSPS<ResolveSPSSig>(
+                      SymbolNameSpec::c("__orc_rt_resolve_tag"), this,
+                      &LazyReexportsManager::resolve));
 }
 
 std::unique_ptr<MaterializationUnit>
diff --git a/llvm/lib/ExecutionEngine/Orc/MachOPlatform.cpp b/llvm/lib/ExecutionEngine/Orc/MachOPlatform.cpp
index 4f980eb4e54686d..a7f074a9c04bde4 100644
--- a/llvm/lib/ExecutionEngine/Orc/MachOPlatform.cpp
+++ b/llvm/lib/ExecutionEngine/Orc/MachOPlatform.cpp
@@ -14,6 +14,7 @@
 #include "llvm/ExecutionEngine/JITLink/MachO.h"
 #include "llvm/ExecutionEngine/JITLink/aarch64.h"
 #include "llvm/ExecutionEngine/JITLink/x86_64.h"
+#include "llvm/ExecutionEngine/Orc/BindCallControllerHandlerSPS.h"
 #include "llvm/ExecutionEngine/Orc/ExecutionUtils.h"
 #include "llvm/ExecutionEngine/Orc/MachOBuilder.h"
 #include "llvm/ExecutionEngine/Orc/SPSProxySpec.h"
@@ -445,8 +446,7 @@ ArrayRef<std::pair<const char *, const char *>>
 MachOPlatform::standardLazyCompilationAliases() {
   static const std::pair<const char *, const char *>
       StandardLazyCompilationAliases[] = {
-          {"__orc_rt_reenter", "__orc_rt_sysv_reenter"},
-          {"__orc_rt_resolve_tag", "___orc_rt_resolve_tag"}};
+          {"__orc_rt_reenter", "__orc_rt_sysv_reenter"}};
 
   return ArrayRef<std::pair<const char *, const char *>>(
       StandardLazyCompilationAliases);
@@ -625,21 +625,19 @@ MachOPlatform::MachOPlatform(
 }
 
 Error MachOPlatform::associateRuntimeSupportFunctions() {
-  ExecutionSession::JITDispatchHandlerAssociationMap WFs;
-
   using PushInitializersSPSSig =
       SPSExpected<SPSMachOJITDylibDepInfoMap>(SPSExecutorAddr);
-  WFs[ES.intern("___orc_rt_macho_push_initializers_tag")] =
-      ES.wrapAsyncWithSPS<PushInitializersSPSSig>(
-          this, &MachOPlatform::rt_pushInitializers);
-
   using PushSymbolsSPSSig =
       SPSError(SPSExecutorAddr, SPSSequence<SPSTuple<SPSString, bool>>);
-  WFs[ES.intern("___orc_rt_macho_push_symbols_tag")] =
-      ES.wrapAsyncWithSPS<PushSymbolsSPSSig>(this,
-                                             &MachOPlatform::rt_pushSymbols);
 
-  return ES.registerJITDispatchHandlers(PlatformJD, std::move(WFs));
+  return ES.registerCallControllerHandlers(
+      PlatformJD,
+      bindCallControllerHandlerSPS<PushInitializersSPSSig>(
+          SymbolNameSpec::c("__orc_rt_macho_push_initializers_tag"), this,
+          &MachOPlatform::rt_pushInitializers),
+      bindCallControllerHandlerSPS<PushSymbolsSPSSig>(
+          SymbolNameSpec::c("__orc_rt_macho_push_symbols_tag"), this,
+          &MachOPlatform::rt_pushSymbols));
 }
 
 void MachOPlatform::pushInitializersLoop(
diff --git a/llvm/lib/ExecutionEngine/Orc/ReOptimizeLayer.cpp b/llvm/lib/ExecutionEngine/Orc/ReOptimizeLayer.cpp
index 9348e2acf527cf6..cb73ee1a61e3303 100644
--- a/llvm/lib/ExecutionEngine/Orc/ReOptimizeLayer.cpp
+++ b/llvm/lib/ExecutionEngine/Orc/ReOptimizeLayer.cpp
@@ -1,4 +1,5 @@
 #include "llvm/ExecutionEngine/Orc/ReOptimizeLayer.h"
+#include "llvm/ExecutionEngine/Orc/BindCallControllerHandlerSPS.h"
 #include "llvm/ExecutionEngine/Orc/LookupAndApply.h"
 #include "llvm/ExecutionEngine/Orc/Mangling.h"
 #include "llvm/ExecutionEngine/Orc/Shared/OrcRTBridge.h"
@@ -128,12 +129,11 @@ Error ReOptimizeLayer::addOrcRTLiteSupport(JITDylib &PlatformJD,
 }
 
 Error ReOptimizeLayer::registerRuntimeFunctions(JITDylib &PlatformJD) {
-  ExecutionSession::JITDispatchHandlerAssociationMap WFs;
   using ReoptimizeSPSSig = shared::SPSError(uint64_t, uint32_t);
-  WFs[Mangle("__orc_rt_reoptimize_tag")] =
-      ES.wrapAsyncWithSPS<ReoptimizeSPSSig>(this,
-                                            &ReOptimizeLayer::rt_reoptimize);
-  return ES.registerJITDispatchHandlers(PlatformJD, std::move(WFs));
+  return ES.registerCallControllerHandlers(
+      PlatformJD, bindCallControllerHandlerSPS<ReoptimizeSPSSig>(
+                      SymbolNameSpec::c("__orc_rt_reoptimize_tag"), this,
+                      &ReOptimizeLayer::rt_reoptimize));
 }
 
 void ReOptimizeLayer::emit(std::unique_ptr<MaterializationResponsibility> R,
diff --git a/llvm/lib/ExecutionEngine/Orc/SelfExecutorProcessControl.cpp b/llvm/lib/ExecutionEngine/Orc/SelfExecutorProcessControl.cpp
index 1e5edc325dd6769..a8c47180c29e65c 100644
--- a/llvm/lib/ExecutionEngine/Orc/SelfExecutorProcessControl.cpp
+++ b/llvm/lib/ExecutionEngine/Orc/SelfExecutorProcessControl.cpp
@@ -135,7 +135,7 @@ SelfExecutorProcessControl::jitDispatchViaWrapperFunctionManager(
   auto ResultF = ResultP.get_future();
   static_cast<SelfExecutorProcessControl *>(Ctx)
       ->getExecutionSession()
-      .runJITDispatchHandler(
+      .runCallControllerHandler(
           [ResultP = std::move(ResultP)](
               shared::WrapperFunctionBuffer Result) mutable {
             ResultP.set_value(std::move(Result));
diff --git a/llvm/lib/ExecutionEngine/Orc/SimpleRemoteEPC.cpp b/llvm/lib/ExecutionEngine/Orc/SimpleRemoteEPC.cpp
index ce3cb371ef8d7c9..88df76b05dfab46 100644
--- a/llvm/lib/ExecutionEngine/Orc/SimpleRemoteEPC.cpp
+++ b/llvm/lib/ExecutionEngine/Orc/SimpleRemoteEPC.cpp
@@ -380,7 +380,7 @@ void SimpleRemoteEPC::handleCallWrapper(
   assert(ES && "No ExecutionSession attached");
   D->dispatch(makeGenericNamedTask(
       [this, RemoteSeqNo, TagAddr, ArgBytes = std::move(ArgBytes)]() mutable {
-        ES->runJITDispatchHandler(
+        ES->runCallControllerHandler(
             [this, RemoteSeqNo](shared::WrapperFunctionBuffer WFR) {
               auto [ResultTag, Payload] = encodeResultMessage(std::move(WFR));
               if (auto Err =
diff --git a/llvm/unittests/ExecutionEngine/Orc/ExecutionSessionWrapperFunctionCallsTest.cpp b/llvm/unittests/ExecutionEngine/Orc/ExecutionSessionWrapperFunctionCallsTest.cpp
index e1e9ec2468a276a..ce2180c053da811 100644
--- a/llvm/unittests/ExecutionEngine/Orc/ExecutionSessionWrapperFunctionCallsTest.cpp
+++ b/llvm/unittests/ExecutionEngine/Orc/ExecutionSessionWrapperFunctionCallsTest.cpp
@@ -6,10 +6,13 @@
 //
 //===----------------------------------------------------------------------===//
 
+#include "OrcTestCommon.h"
 #include "llvm/ExecutionEngine/Orc/AbsoluteSymbols.h"
+#include "llvm/ExecutionEngine/Orc/BindCallControllerHandlerSPS.h"
 #include "llvm/ExecutionEngine/Orc/Core.h"
 #include "llvm/ExecutionEngine/Orc/ExecutorProcessControl.h"
 #include "llvm/ExecutionEngine/Orc/SelfExecutorProcessControl.h"
+#include "llvm/Testing/Support/Error.h"
 #include "gtest/gtest.h"
 
 #include <future>
@@ -34,12 +37,9 @@ TEST(ExecutionSessionWrapperFunctionCalls, RegisterAsyncHandlerAndRun) {
   cantFail(JD.define(absoluteSymbols(
       {{AddAsyncTag, {AddAsyncTagAddr, JITSymbolFlags::Exported}}})));
 
-  ExecutionSession::JITDispatchHandlerAssociationMap Associations;
-
-  Associations[AddAsyncTag] =
-      ES.wrapAsyncWithSPS<int32_t(int32_t, int32_t)>(addAsyncWrapper);
-
-  cantFail(ES.registerJITDispatchHandlers(JD, std::move(Associations)));
+  cantFail(ES.registerCallControllerHandlers(
+      JD, bindCallControllerHandlerSPS<int32_t(int32_t, int32_t)>(
+              SymbolNameSpec::verbatim("addAsync_tag"), addAsyncWrapper)));
 
   std::promise<int32_t> RP;
   auto RF = RP.get_future();
@@ -50,7 +50,7 @@ TEST(ExecutionSessionWrapperFunctionCalls, RegisterAsyncHandlerAndRun) {
   SPSOutputBuffer OB(ArgBuffer.data(), ArgBuffer.size());
   EXPECT_TRUE(ArgSerialization::serialize(OB, 1, 2));
 
-  ES.runJITDispatchHandler(
+  ES.runCallControllerHandler(
       [&](WrapperFunctionBuffer ResultBuffer) {
         int32_t Result;
         SPSInputBuffer IB(ResultBuffer.data(), ResultBuffer.size());
@@ -63,3 +63,133 @@ TEST(ExecutionSessionWrapperFunctionCalls, RegisterAsyncHandlerAndRun) {
 
   cantFail(ES.endSession());
 }
+
+namespace {
+
+class CallControllerHandlerTest : public CoreAPIsBasedStandardTest {};
+
+} // namespace
+
+// Returns a binding whose handler sets Called when run.
+static ExecutionSession::CallControllerHandlerBinding
+recordCall(SymbolNameSpec Name, bool &Called,
+           SymbolLookupFlags LF = SymbolLookupFlags::RequiredSymbol) {
+  return ExecutionSession::CallControllerHandlerBinding(
+      Name,
+      [&Called](ExecutionSession::CallControllerReturnFn Return,
+                WrapperFunctionBuffer) {
+        Called = true;
+        Return(WrapperFunctionBuffer());
+      },
+      LF);
+}
+
+// Runs the handler registered for TagAddr in ES. Returns the out-of-band error
+// reported, or the empty string if there was none.
+static std::string run(ExecutionSession &ES, ExecutorAddr TagAddr) {
+  std::string Err;
+  ES.runCallControllerHandler(
+      [&](WrapperFunctionBuffer R) {
+        if (auto *Msg = R.getOutOfBandError())
+          Err = Msg;
+      },
+      TagAddr, WrapperFunctionBuffer());
+  return Err;
+}
+
+TEST_F(CallControllerHandlerTest, MissingRequiredTagFails) {
+  // A missing required tag should cause registration to fail, and no handlers
+  // (including those whose tags were found) should be registered.
+  cantFail(JD.define(absoluteSymbols({{Foo, FooSym}})));
+
+  bool FooCalled = false, BarCalled = false;
+  EXPECT_THAT_ERROR(ES.registerCallControllerHandlers(
+                        JD,
+                        recordCall(SymbolNameSpec::verbatim("foo"), FooCalled),
+                        recordCall(SymbolNameSpec::verbatim("bar"), BarCalled)),
+                    Failed());
+
+  EXPECT_NE(run(ES, FooAddr), "");
+  EXPECT_FALSE(FooCalled);
+}
+
+TEST_F(CallControllerHandlerTest, MissingWeakTagIsDropped) {
+  // A missing weakly referenced tag should not cause registration to fail.
+  // The handler for it is dropped, and other handlers are registered.
+  cantFail(JD.define(absoluteSymbols({{Foo, FooSym}})));
+
+  bool FooCalled = false, BarCalled = false;
+  EXPECT_THAT_ERROR(ES.registerCallControllerHandlers(
+                        JD,
+                        recordCall(SymbolNameSpec::verbatim("foo"), FooCalled),
+                        recordCall(SymbolNameSpec::verbatim("bar"), BarCalled,
+                                   SymbolLookupFlags::WeaklyReferencedSymbol)),
+                    Succeeded());
+
+  EXPECT_EQ(run(ES, FooAddr), "");
+  EXPECT_TRUE(FooCalled);
+  EXPECT_FALSE(BarCalled);
+}
+
+TEST_F(CallControllerHandlerTest, DuplicateBindingFails) {
+  // Two bindings for the same tag should cause registration to fail, with no
+  // handlers registered.
+  cantFail(JD.define(absoluteSymbols({{Foo, FooSym}})));
+
+  bool Called1 = false, Called2 = false;
+  EXPECT_THAT_ERROR(ES.registerCallControllerHandlers(
+                        JD,
+                        recordCall(SymbolNameSpec::verbatim("foo"), Called1),
+                        recordCall(SymbolNameSpec::verbatim("foo"), Called2)),
+                    Failed());
+
+  EXPECT_NE(run(ES, FooAddr), "");
+  EXPECT_FALSE(Called1);
+  EXPECT_FALSE(Called2);
+}
+
+TEST_F(CallControllerHandlerTest, AlreadyRegisteredTagFails) {
+  // Registering a handler for an already-registered tag should fail, leave
+  // the existing handler in place, and register none of the other handlers
+  // passed in the same call.
+  cantFail(JD.define(absoluteSymbols({{Foo, FooSym}, {Bar, BarSym}})));
+
+  bool FooCalled1 = false, FooCalled2 = false, BarCalled = false;
+  cantFail(ES.registerCallControllerHandlers(
+      JD, recordCall(SymbolNameSpec::verbatim("foo"), FooCalled1)));
+
+  EXPECT_THAT_ERROR(
+      ES.registerCallControllerHandlers(
+          JD, recordCall(SymbolNameSpec::verbatim("bar"), BarCalled),
+          recordCall(SymbolNameSpec::verbatim("foo"), FooCalled2)),
+      Failed());
+
+  EXPECT_NE(run(ES, BarAddr), "");
+  EXPECT_FALSE(BarCalled);
+
+  EXPECT_EQ(run(ES, FooAddr), "");
+  EXPECT_TRUE(FooCalled1);
+  EXPECT_FALSE(FooCalled2);
+}
+
+TEST(CallControllerHandlerManglingTest, TagNamesAreMangled) {
+  // Tag names should be mangled for the session's target: on MachO, the C
+  // name "foo_tag" should resolve to the linker name "_foo_tag".
+  ExecutionSession ES(std::make_unique<UnsupportedExecutorProcessControl>(
+      nullptr, nullptr, "arm64-apple-darwin"));
+  auto &JD = ES.createBareJITDylib("JD");
+
+  constexpr ExecutorAddr TagAddr(0x1);
+  cantFail(JD.define(absoluteSymbols(
+      {{ES.intern("_foo_tag"), {TagAddr, JITSymbolFlags::Exported}}})));
+
+  bool Called = false;
+  EXPECT_THAT_ERROR(ES.registerCallControllerHandlers(
+                        JD, recordCall(SymbolNameSpec::c("foo_tag"), Called)),
+                    Succeeded());
+
+  EXPECT_EQ(run(ES, TagAddr), "");
+  EXPECT_TRUE(Called);
+
+  cantFail(ES.endSession());
+}
diff --git a/llvm/unittests/ExecutionEngine/Orc/InProcessEPCTest.cpp b/llvm/unittests/ExecutionEngine/Orc/InProcessEPCTest.cpp
index 5fe91c6ff6346f9..7a61a7eb99af0f0 100644
--- a/llvm/unittests/ExecutionEngine/Orc/InProcessEPCTest.cpp
+++ b/llvm/unittests/ExecutionEngine/Orc/InProcessEPCTest.cpp
@@ -560,7 +560,7 @@ TEST(InProcessEPCTest, ReturnWrapperResultForInvalidCallIdIsReported) {
       << "Expected invalid-call-id report, got: " << CapturedErr;
 }
 
-TEST(InProcessEPCTest, JITDispatchSuccess) {
+TEST(InProcessEPCTest, CallControllerSuccess) {
   MockIPCA IPCA;
   MockBootstrapInfoAccess BIA;
   auto EPCExp = createIPEPC(IPCA, BIA);
@@ -573,12 +573,13 @@ TEST(InProcessEPCTest, JITDispatchSuccess) {
   cantFail(Fix.JD.define(
       absoluteSymbols({{Tag, {TagAddr, JITSymbolFlags::Exported}}})));
 
-  ExecutionSession::JITDispatchHandlerAssociationMap Assocs;
-  Assocs[Tag] = [](ExecutionSession::SendResultFunction SendResult,
-                   const char *ArgData, size_t ArgSize) {
-    SendResult(shared::WrapperFunctionBuffer::copyFrom(ArgData, ArgSize));
-  };
-  cantFail(Fix.ES.registerJITDispatchHandlers(Fix.JD, std::move(Assocs)));
+  cantFail(Fix.ES.registerCallControllerHandlers(
+      Fix.JD, ExecutionSession::CallControllerHandlerBinding(
+                  SymbolNameSpec::verbatim("echo_tag"),
+                  [](ExecutionSession::CallControllerReturnFn Return,
+                     shared::WrapperFunctionBuffer ArgBytes) {
+                    Return(std::move(ArgBytes));
+                  })));
 
   std::optional<std::string> Result;
   std::optional<uint64_t> RxCallId;
@@ -599,7 +600,7 @@ TEST(InProcessEPCTest, JITDispatchSuccess) {
   EXPECT_EQ(*RxCallId, *SentCallId);
 }
 
-TEST(InProcessEPCTest, JITDispatchUnknownHandler) {
+TEST(InProcessEPCTest, CallControllerUnknownHandler) {
   MockIPCA IPCA;
   MockBootstrapInfoAccess BIA;
   auto EPCExp = createIPEPC(IPCA, BIA);
@@ -623,7 +624,7 @@ TEST(InProcessEPCTest, JITDispatchUnknownHandler) {
       << "Expected ReturnJITDispatchResult to deliver an OOB error";
 }
 
-TEST(InProcessEPCTest, JITDispatchAfterDisconnectIsDropped) {
+TEST(InProcessEPCTest, CallControllerAfterDisconnectIsDropped) {
   MockIPCA IPCA;
   MockBootstrapInfoAccess BIA;
   auto EPCExp = createIPEPC(IPCA, BIA);
diff --git a/llvm/unittests/ExecutionEngine/Orc/LazyCallThroughAndReexportsTest.cpp b/llvm/unittests/ExecutionEngine/Orc/LazyCallThroughAndReexportsTest.cpp
index 1f2aa0e9ed79baa..23aa0aaae8c2566 100644
--- a/llvm/unittests/ExecutionEngine/Orc/LazyCallThroughAndReexportsTest.cpp
+++ b/llvm/unittests/ExecutionEngine/Orc/LazyCallThroughAndReexportsTest.cpp
@@ -77,6 +77,7 @@ TEST_F(LazyReexportsTest, BasicLocalCallThroughManagerOperation) {
 }
 
 static void *noReentry(void *) { abort(); }
+static char ResolveTag;
 
 TEST(JITLinkLazyReexportsTest, Basics) {
   OrcNativeTarget::initialize();
@@ -114,7 +115,9 @@ TEST(JITLinkLazyReexportsTest, Basics) {
   cantFail(JD.define(absoluteSymbols(
       {{ES.intern("__orc_rt_reentry"),
         {ExecutorAddr::fromPtr(&noReentry),
-         JITSymbolFlags::Exported | JITSymbolFlags::Callable}}})));
+         JITSymbolFlags::Exported | JITSymbolFlags::Callable}},
+       {(*J)->mangleAndIntern("__orc_rt_resolve_tag"),
+        {ExecutorAddr::fromPtr(&ResolveTag), JITSymbolFlags::Exported}}})));
 
   auto LRMgr = createJITLinkLazyReexportsManager(OLL, **RSMgr, JD);
   if (!LRMgr) {



More information about the llvm-commits mailing list