[llvm] [ORC] Rename ConnectionSpec's fd transport to socket:adopt (PR #225075)

Lang Hames via llvm-commits llvm-commits at lists.llvm.org
Mon Sep 21 06:27:05 PDT 2026


https://github.com/lhames updated https://github.com/llvm/llvm-project/pull/225075

>From 787142282742ee76cd63ef20296047c611992ced Mon Sep 17 00:00:00 2001
From: Lang Hames <lhames at gmail.com>
Date: Mon, 21 Sep 2026 22:31:49 +1000
Subject: [PATCH 1/2] [ORC] Rename ConnectionSpec's fd transport to
 socket:adopt

A ConnectionSpec's transport field says what kind of thing the
descriptor names, and "fd" named a representation rather than a kind. It
also left no room for a pipe transport, which takes two descriptors and
a different server.

The transport becomes "socket", and adopting a handed-over handle
becomes an explicit "adopt" action rather than the absence of one. So
"tcp" names an endpoint to reach while "socket" names a handle already
held, and a spec states how the process came by its channel -- worth
spelling out, since "adopt", "connect" and "listen" do not carry the
same trust.

llvm-jitlink-executor takes "socket:adopt=<fd>" in place of "fd=<fd>"
and rejects any other action for that transport. The spec is an internal
handoff from llvm-jitlink in every in-tree use, so nothing else needed
updating. The class comment is rewritten around the same split and stops
describing transports that do not exist yet.

Matches the scheme the ORC runtime uses for its connector registry.
---
 .../Orc/Shared/ConnectionSpec.h               | 36 +++++++++----------
 .../llvm-jitlink-executor.cpp                 | 14 +++++---
 llvm/tools/llvm-jitlink/llvm-jitlink.cpp      |  2 +-
 .../Orc/ConnectionSpecTest.cpp                | 26 ++++++++++----
 4 files changed, 48 insertions(+), 30 deletions(-)

diff --git a/llvm/include/llvm/ExecutionEngine/Orc/Shared/ConnectionSpec.h b/llvm/include/llvm/ExecutionEngine/Orc/Shared/ConnectionSpec.h
index 9c5243c2b4ebf..99bead919127c 100644
--- a/llvm/include/llvm/ExecutionEngine/Orc/Shared/ConnectionSpec.h
+++ b/llvm/include/llvm/ExecutionEngine/Orc/Shared/ConnectionSpec.h
@@ -7,7 +7,7 @@
 //===----------------------------------------------------------------------===//
 //
 // A ConnectionSpec describes one connection a process should establish with
-// its peer, e.g. "tcp:connect=localhost:20000" or "fd=3".
+// its peer, e.g. "tcp:connect=localhost:20000" or "socket:adopt=3".
 //
 //===----------------------------------------------------------------------===//
 
@@ -26,22 +26,23 @@ namespace llvm::orc {
 ///
 ///   <transport>[:<action>]=<descriptor>
 ///
-/// E.g. "fd=3", "tcp:connect=localhost:20000", "tcp:listen=[::1]:0". Such
-/// strings typically reach a process as a command-line argument, but nothing
-/// in the grammar or the parser assumes that.
+///   transport   what kind of thing the descriptor names: "tcp" for a host:port
+///               endpoint, "socket" for a handle this process already holds.
+///   action      what this process does with it: "connect", "listen", or
+///               "adopt" for a handle it was handed.
+///   descriptor  the thing itself, in whatever syntax the transport defines.
 ///
-/// A spec describes what the process reading it does, so the two ends of one
-/// connection carry different specs: an executor told "tcp:listen=:0" pairs
-/// with a controller told "tcp:connect=<host>:<port>".
+/// E.g. "tcp:connect=localhost:20000", "tcp:listen=[::1]:0", "socket:adopt=3".
 ///
-/// The parser only checks punctuation: the transport and action names are
-/// opaque tokens, and the descriptor's syntax is entirely up to the
-/// transport. Splitting the action from the transport is confined to the
-/// text before the first '=', which lets a descriptor contain ':' and '='
-/// unescaped (e.g. "tcp:listen=[::1]:0", "unix:listen=/tmp/a=b.sock").
+/// A spec says what the process reading it does, so the two ends of one
+/// connection carry different specs: an executor told
+/// "tcp:connect=<host>:<port>" pairs with a controller told "tcp:listen=:0".
 ///
-/// All three fields are preserved verbatim, so callers matching a transport
-/// or action name against a known set do so case-sensitively.
+/// Parsing checks punctuation only: transport and action are opaque tokens, the
+/// descriptor's syntax belongs to the transport, and all three fields are kept
+/// verbatim -- so a caller matching a name against a known set does so
+/// case-sensitively. Only the text before the first '=' is searched for the
+/// ':', which lets a descriptor hold ':' and '=' unescaped.
 class ConnectionSpec {
 public:
   /// Parses Spec as <transport>[:<action>]=<descriptor>.
@@ -50,12 +51,11 @@ class ConnectionSpec {
   /// The transport name, e.g. "tcp". Never empty.
   StringRef getTransport() const { return Transport; }
 
-  /// The action name, e.g. "listen" or "connect". May be empty: direction is
-  /// degenerate for some transports (an inherited socket fd is already
-  /// connected), so single-mode transports omit it.
+  /// The action name, e.g. "connect" or "adopt". May be empty.
   StringRef getAction() const { return Action; }
 
-  /// The transport-specific address. May be empty (e.g. "fd=").
+  /// The thing the transport names, in whatever syntax that transport defines.
+  /// Opaque here: the parser neither splits nor validates it. May be empty.
   StringRef getDescriptor() const { return Descriptor; }
 
   /// Rebuilds the original connection string, e.g. "tcp:connect=host:port".
diff --git a/llvm/tools/llvm-jitlink/llvm-jitlink-executor/llvm-jitlink-executor.cpp b/llvm/tools/llvm-jitlink/llvm-jitlink-executor/llvm-jitlink-executor.cpp
index 526257cf16e0e..a2379c880ae20 100644
--- a/llvm/tools/llvm-jitlink/llvm-jitlink-executor/llvm-jitlink-executor.cpp
+++ b/llvm/tools/llvm-jitlink/llvm-jitlink-executor/llvm-jitlink-executor.cpp
@@ -51,7 +51,7 @@ void printErrorAndExit(Twine ErrMsg) {
   errs() << "error: " << ErrMsg.str() << "\n\n"
          << "Usage:\n"
          << "  llvm-jitlink-executor " << DebugOption
-         << "[test-jitloadergdb] fd=<sockfd> [args...]\n"
+         << "[test-jitloadergdb] socket:adopt=<sockfd> [args...]\n"
          << "  llvm-jitlink-executor " << DebugOption
          << "[test-jitloadergdb] tcp:connect=<host>:<port> [args...]\n"
          << "  llvm-jitlink-executor " << DebugOption
@@ -101,7 +101,13 @@ Expected<std::unique_ptr<SimpleRemoteEPCServer>> createServerWithFD(int FD) {
 }
 
 Expected<std::unique_ptr<SimpleRemoteEPCServer>>
-connectWithFD(const ConnectionSpec &CS) {
+connectWithSocket(const ConnectionSpec &CS) {
+  if (CS.getAction() != "adopt")
+    return make_error<StringError>(
+        "In " + CS.str() +
+            ", the socket transport supports only the \"adopt\" action",
+        inconvertibleErrorCode());
+
   int FD;
   if (CS.getDescriptor().getAsInteger(10, FD))
     return make_error<StringError>(
@@ -171,8 +177,8 @@ connectWithTCP(const ConnectionSpec &CS) {
 
 Expected<std::unique_ptr<SimpleRemoteEPCServer>>
 createServer(const ConnectionSpec &CS) {
-  if (CS.getTransport() == "fd")
-    return connectWithFD(CS);
+  if (CS.getTransport() == "socket")
+    return connectWithSocket(CS);
   if (CS.getTransport() == "tcp")
     return connectWithTCP(CS);
 
diff --git a/llvm/tools/llvm-jitlink/llvm-jitlink.cpp b/llvm/tools/llvm-jitlink/llvm-jitlink.cpp
index 62c5930ca0661..2929cd57eea7f 100644
--- a/llvm/tools/llvm-jitlink/llvm-jitlink.cpp
+++ b/llvm/tools/llvm-jitlink/llvm-jitlink.cpp
@@ -979,7 +979,7 @@ launchExecutorWithDefaultConnect() {
             inconvertibleErrorCode());
     }
 
-    std::string ConnSpec = "fd=";
+    std::string ConnSpec = "socket:adopt=";
     ConnSpec += std::to_string(Sockets[ChildSocket]);
     if (auto Err = launchExecutor({ConnSpec}))
       return std::move(Err);
diff --git a/llvm/unittests/ExecutionEngine/Orc/ConnectionSpecTest.cpp b/llvm/unittests/ExecutionEngine/Orc/ConnectionSpecTest.cpp
index ce2f5b1bbd6bd..22418f5a64cb3 100644
--- a/llvm/unittests/ExecutionEngine/Orc/ConnectionSpecTest.cpp
+++ b/llvm/unittests/ExecutionEngine/Orc/ConnectionSpecTest.cpp
@@ -16,12 +16,24 @@ using namespace llvm::orc;
 namespace {
 
 TEST(ConnectionSpecTest, TransportAndDescriptorOnly) {
-  auto Spec = ConnectionSpec::parse("fd=3");
+  // The grammar permits a missing action, though no transport defines one.
+  auto Spec = ConnectionSpec::parse("socket=3");
   ASSERT_THAT_EXPECTED(Spec, Succeeded());
-  EXPECT_EQ(Spec->getTransport(), "fd");
+  EXPECT_EQ(Spec->getTransport(), "socket");
   EXPECT_EQ(Spec->getAction(), "");
   EXPECT_EQ(Spec->getDescriptor(), "3");
-  EXPECT_EQ(Spec->str(), "fd=3");
+  EXPECT_EQ(Spec->str(), "socket=3");
+}
+
+TEST(ConnectionSpecTest, MultiPartDescriptor) {
+  // Descriptor syntax is the transport's business, so a comma-separated pair
+  // passes through untouched.
+  auto Spec = ConnectionSpec::parse("pipe:adopt=3,4");
+  ASSERT_THAT_EXPECTED(Spec, Succeeded());
+  EXPECT_EQ(Spec->getTransport(), "pipe");
+  EXPECT_EQ(Spec->getAction(), "adopt");
+  EXPECT_EQ(Spec->getDescriptor(), "3,4");
+  EXPECT_EQ(Spec->str(), "pipe:adopt=3,4");
 }
 
 TEST(ConnectionSpecTest, TransportActionAndDescriptor) {
@@ -34,12 +46,12 @@ TEST(ConnectionSpecTest, TransportActionAndDescriptor) {
 }
 
 TEST(ConnectionSpecTest, EmptyDescriptorIsLegal) {
-  auto Spec = ConnectionSpec::parse("fd=");
+  auto Spec = ConnectionSpec::parse("socket:adopt=");
   ASSERT_THAT_EXPECTED(Spec, Succeeded());
-  EXPECT_EQ(Spec->getTransport(), "fd");
-  EXPECT_EQ(Spec->getAction(), "");
+  EXPECT_EQ(Spec->getTransport(), "socket");
+  EXPECT_EQ(Spec->getAction(), "adopt");
   EXPECT_EQ(Spec->getDescriptor(), "");
-  EXPECT_EQ(Spec->str(), "fd=");
+  EXPECT_EQ(Spec->str(), "socket:adopt=");
 }
 
 TEST(ConnectionSpecTest, ColonInDescriptorAfterAction) {

>From fdca0abe43f2b6aaf808fe4e177b3efd4c503daa Mon Sep 17 00:00:00 2001
From: Lang Hames <lhames at gmail.com>
Date: Mon, 21 Sep 2026 23:26:39 +1000
Subject: [PATCH 2/2] fix LLJITWithRemoteDebugging example

---
 .../OrcV2Examples/LLJITWithRemoteDebugging/RemoteJITUtils.cpp   | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/examples/OrcV2Examples/LLJITWithRemoteDebugging/RemoteJITUtils.cpp b/llvm/examples/OrcV2Examples/LLJITWithRemoteDebugging/RemoteJITUtils.cpp
index 55b7e37c91d6b..265da50036777 100644
--- a/llvm/examples/OrcV2Examples/LLJITWithRemoteDebugging/RemoteJITUtils.cpp
+++ b/llvm/examples/OrcV2Examples/LLJITWithRemoteDebugging/RemoteJITUtils.cpp
@@ -89,7 +89,7 @@ launchLocalExecutor(StringRef ExecutablePath) {
 
     std::string ExecPath = ExecutablePath.str();
     std::string TestOutputFlag = "test-jitloadergdb";
-    std::string ConnSpec = "fd=" + std::to_string(Sockets[ChildSocket]);
+    std::string ConnSpec = "socket:adopt=" + std::to_string(Sockets[ChildSocket]);
     char *const Args[] = {ExecPath.data(), TestOutputFlag.data(),
                           ConnSpec.data(), nullptr};
     int RC = execvp(ExecutablePath.data(), Args);



More information about the llvm-commits mailing list