[llvm] [ORC] Add ConnectionSpec for connection string parsing (PR #224000)

Lang Hames via llvm-commits llvm-commits at lists.llvm.org
Wed Sep 16 05:47:59 PDT 2026


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

A ConnectionSpec is the parsed form of a string describing one connection a process should establish with its peer:

  <transport>[:<action>]=<descriptor>

E.g. "fd=3" or "tcp:connect=localhost:20000". The parser only checks punctuation: transport and action names are opaque tokens, and the descriptor's syntax is left to the transport, which lets a descriptor contain ':' and '=' unescaped (e.g. "tcp:listen=[::1]:0").

This will be used by llvm-jitlink and llvm-jitlink-executor to generalize out-of-process executor connection setup beyond the current fd/TCP-only options.

>From ab70f81540cd0cada874e4734518f3958c100d84 Mon Sep 17 00:00:00 2001
From: Lang Hames <lhames at apple.com>
Date: Mon, 14 Sep 2026 20:49:48 +1000
Subject: [PATCH] [ORC] Add ConnectionSpec for connection string parsing

A ConnectionSpec is the parsed form of a string describing one
connection a process should establish with its peer:

  <transport>[:<action>]=<descriptor>

E.g. "fd=3" or "tcp:connect=localhost:20000". The parser only checks
punctuation: transport and action names are opaque tokens, and the
descriptor's syntax is left to the transport, which lets a descriptor
contain ':' and '=' unescaped (e.g. "tcp:listen=[::1]:0").

This will be used by llvm-jitlink and llvm-jitlink-executor to
generalize out-of-process executor connection setup beyond the current
fd/TCP-only options.
---
 .../Orc/Shared/ConnectionSpec.h               |  74 ++++++++++++
 .../ExecutionEngine/Orc/Shared/CMakeLists.txt |   1 +
 .../Orc/Shared/ConnectionSpec.cpp             |  38 +++++++
 .../ExecutionEngine/Orc/CMakeLists.txt        |   1 +
 .../Orc/ConnectionSpecTest.cpp                | 106 ++++++++++++++++++
 5 files changed, 220 insertions(+)
 create mode 100644 llvm/include/llvm/ExecutionEngine/Orc/Shared/ConnectionSpec.h
 create mode 100644 llvm/lib/ExecutionEngine/Orc/Shared/ConnectionSpec.cpp
 create mode 100644 llvm/unittests/ExecutionEngine/Orc/ConnectionSpecTest.cpp

diff --git a/llvm/include/llvm/ExecutionEngine/Orc/Shared/ConnectionSpec.h b/llvm/include/llvm/ExecutionEngine/Orc/Shared/ConnectionSpec.h
new file mode 100644
index 0000000000000..8cdeececef5c5
--- /dev/null
+++ b/llvm/include/llvm/ExecutionEngine/Orc/Shared/ConnectionSpec.h
@@ -0,0 +1,74 @@
+//===- ConnectionSpec.h - Connection spec parsing ---------------*- 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
+//
+//===----------------------------------------------------------------------===//
+//
+// A ConnectionSpec describes one connection a process should establish with
+// its peer, e.g. "tcp:connect=localhost:20000" or "fd=3".
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_EXECUTIONENGINE_ORC_SHARED_CONNECTIONSPEC_H
+#define LLVM_EXECUTIONENGINE_ORC_SHARED_CONNECTIONSPEC_H
+
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Compiler.h"
+#include "llvm/Support/Error.h"
+#include <string>
+
+namespace llvm::orc {
+
+/// The parsed form of a string describing one connection a process should
+/// establish with its peer:
+///
+///   <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.
+///
+/// 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>".
+///
+/// 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").
+///
+/// All three fields are preserved verbatim, so callers matching a transport
+/// or action name against a known set do so case-sensitively.
+class ConnectionSpec {
+public:
+  /// Parses Spec as <transport>[:<action>]=<descriptor>.
+  LLVM_ABI static Expected<ConnectionSpec> parse(StringRef Spec);
+
+  /// 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.
+  StringRef getAction() const { return Action; }
+
+  /// The transport-specific address. May be empty (e.g. "fd=").
+  StringRef getDescriptor() const { return Descriptor; }
+
+private:
+  ConnectionSpec(std::string Transport, std::string Action,
+                 std::string Descriptor)
+      : Transport(std::move(Transport)), Action(std::move(Action)),
+        Descriptor(std::move(Descriptor)) {}
+
+  std::string Transport;
+  std::string Action;
+  std::string Descriptor;
+};
+
+} // namespace llvm::orc
+
+#endif // LLVM_EXECUTIONENGINE_ORC_SHARED_CONNECTIONSPEC_H
diff --git a/llvm/lib/ExecutionEngine/Orc/Shared/CMakeLists.txt b/llvm/lib/ExecutionEngine/Orc/Shared/CMakeLists.txt
index 717fc21826aba..1cb31d6a18b3a 100644
--- a/llvm/lib/ExecutionEngine/Orc/Shared/CMakeLists.txt
+++ b/llvm/lib/ExecutionEngine/Orc/Shared/CMakeLists.txt
@@ -1,5 +1,6 @@
 add_llvm_component_library(LLVMOrcShared
   AllocationActions.cpp
+  ConnectionSpec.cpp
   MachOObjectFormat.cpp
   ObjectFormats.cpp
   OrcError.cpp
diff --git a/llvm/lib/ExecutionEngine/Orc/Shared/ConnectionSpec.cpp b/llvm/lib/ExecutionEngine/Orc/Shared/ConnectionSpec.cpp
new file mode 100644
index 0000000000000..0e0935a9b2701
--- /dev/null
+++ b/llvm/lib/ExecutionEngine/Orc/Shared/ConnectionSpec.cpp
@@ -0,0 +1,38 @@
+//===- ConnectionSpec.cpp - Connection spec parsing -----------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ExecutionEngine/Orc/Shared/ConnectionSpec.h"
+
+using namespace llvm;
+using namespace llvm::orc;
+
+static Error makeConnectionSpecError(StringRef Spec, const Twine &Msg) {
+  return make_error<StringError>("in connection spec '" + Spec.str() +
+                                     "': " + Msg,
+                                 inconvertibleErrorCode());
+}
+
+Expected<ConnectionSpec> ConnectionSpec::parse(StringRef Spec) {
+  if (!Spec.contains('='))
+    return makeConnectionSpecError(
+        Spec, "expected '<transport>[:<action>]=<descriptor>', but found "
+              "no '='");
+
+  auto [LHS, Descriptor] = Spec.split('=');
+  auto [Transport, Action] = LHS.split(':');
+
+  if (Transport.empty())
+    return makeConnectionSpecError(Spec, "empty transport name");
+
+  // An action is optional, but writing the ':' that introduces one and then
+  // omitting it is an error.
+  if (LHS.contains(':') && Action.empty())
+    return makeConnectionSpecError(Spec, "empty action name");
+
+  return ConnectionSpec(Transport.str(), Action.str(), Descriptor.str());
+}
diff --git a/llvm/unittests/ExecutionEngine/Orc/CMakeLists.txt b/llvm/unittests/ExecutionEngine/Orc/CMakeLists.txt
index db2b50e05d07b..e69c558f08c25 100644
--- a/llvm/unittests/ExecutionEngine/Orc/CMakeLists.txt
+++ b/llvm/unittests/ExecutionEngine/Orc/CMakeLists.txt
@@ -19,6 +19,7 @@ set(LLVM_LINK_COMPONENTS
 
 add_llvm_unittest(OrcJITTests
   COFFAutoImportGeneratorTest.cpp
+  ConnectionSpecTest.cpp
   CoreAPIsTest.cpp
   ExecutorAddressTest.cpp
   ExecutionSessionWrapperFunctionCallsTest.cpp
diff --git a/llvm/unittests/ExecutionEngine/Orc/ConnectionSpecTest.cpp b/llvm/unittests/ExecutionEngine/Orc/ConnectionSpecTest.cpp
new file mode 100644
index 0000000000000..adb460b221e9c
--- /dev/null
+++ b/llvm/unittests/ExecutionEngine/Orc/ConnectionSpecTest.cpp
@@ -0,0 +1,106 @@
+//===- ConnectionSpecTest.cpp - Unit tests for ConnectionSpec -------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ExecutionEngine/Orc/Shared/ConnectionSpec.h"
+#include "llvm/Testing/Support/Error.h"
+#include "gtest/gtest.h"
+
+using namespace llvm;
+using namespace llvm::orc;
+
+namespace {
+
+TEST(ConnectionSpecTest, TransportAndDescriptorOnly) {
+  auto Spec = ConnectionSpec::parse("fd=3");
+  ASSERT_THAT_EXPECTED(Spec, Succeeded());
+  EXPECT_EQ(Spec->getTransport(), "fd");
+  EXPECT_EQ(Spec->getAction(), "");
+  EXPECT_EQ(Spec->getDescriptor(), "3");
+}
+
+TEST(ConnectionSpecTest, TransportActionAndDescriptor) {
+  auto Spec = ConnectionSpec::parse("tcp:connect=localhost:20000");
+  ASSERT_THAT_EXPECTED(Spec, Succeeded());
+  EXPECT_EQ(Spec->getTransport(), "tcp");
+  EXPECT_EQ(Spec->getAction(), "connect");
+  EXPECT_EQ(Spec->getDescriptor(), "localhost:20000");
+}
+
+TEST(ConnectionSpecTest, EmptyDescriptorIsLegal) {
+  auto Spec = ConnectionSpec::parse("fd=");
+  ASSERT_THAT_EXPECTED(Spec, Succeeded());
+  EXPECT_EQ(Spec->getTransport(), "fd");
+  EXPECT_EQ(Spec->getAction(), "");
+  EXPECT_EQ(Spec->getDescriptor(), "");
+}
+
+TEST(ConnectionSpecTest, ColonInDescriptorAfterAction) {
+  // The ':' search must be confined to the text before the first '=', so a
+  // descriptor may itself contain ':' unescaped.
+  auto Spec = ConnectionSpec::parse("tcp:listen=[::1]:0");
+  ASSERT_THAT_EXPECTED(Spec, Succeeded());
+  EXPECT_EQ(Spec->getTransport(), "tcp");
+  EXPECT_EQ(Spec->getAction(), "listen");
+  EXPECT_EQ(Spec->getDescriptor(), "[::1]:0");
+}
+
+TEST(ConnectionSpecTest, EqualsInDescriptor) {
+  // Only the *first* '=' delimits transport/action from the descriptor, so a
+  // descriptor may itself contain '=' unescaped.
+  auto Spec = ConnectionSpec::parse("unix:listen=/tmp/a=b.sock");
+  ASSERT_THAT_EXPECTED(Spec, Succeeded());
+  EXPECT_EQ(Spec->getTransport(), "unix");
+  EXPECT_EQ(Spec->getAction(), "listen");
+  EXPECT_EQ(Spec->getDescriptor(), "/tmp/a=b.sock");
+}
+
+TEST(ConnectionSpecTest, ColonInActionIsLegal) {
+  // Only the *first* ':' before the '=' delimits the transport from the
+  // action; the action is an opaque token, so the parser passes any
+  // remaining ':' through rather than rejecting it. Transports are free to
+  // reject actions they don't recognize.
+  auto Spec = ConnectionSpec::parse("tcp::listen=[::1]:0");
+  ASSERT_THAT_EXPECTED(Spec, Succeeded());
+  EXPECT_EQ(Spec->getTransport(), "tcp");
+  EXPECT_EQ(Spec->getAction(), ":listen");
+  EXPECT_EQ(Spec->getDescriptor(), "[::1]:0");
+}
+
+TEST(ConnectionSpecTest, NamesArePreservedVerbatim) {
+  // The parser does not case-normalize: callers match case-sensitively.
+  auto Spec = ConnectionSpec::parse("TCP:Connect=localhost:20000");
+  ASSERT_THAT_EXPECTED(Spec, Succeeded());
+  EXPECT_EQ(Spec->getTransport(), "TCP");
+  EXPECT_EQ(Spec->getAction(), "Connect");
+}
+
+TEST(ConnectionSpecTest, MissingEqualsIsError) {
+  EXPECT_THAT_EXPECTED(ConnectionSpec::parse("tcp:connect"), Failed());
+}
+
+TEST(ConnectionSpecTest, EmptySpecIsError) {
+  EXPECT_THAT_EXPECTED(ConnectionSpec::parse(""), Failed());
+}
+
+TEST(ConnectionSpecTest, EmptyTransportIsError) {
+  EXPECT_THAT_EXPECTED(ConnectionSpec::parse("=3"), Failed());
+}
+
+TEST(ConnectionSpecTest, DescriptorOnlyIsError) {
+  EXPECT_THAT_EXPECTED(ConnectionSpec::parse("="), Failed());
+}
+
+TEST(ConnectionSpecTest, EmptyActionIsError) {
+  EXPECT_THAT_EXPECTED(ConnectionSpec::parse("tcp:=localhost:20000"), Failed());
+}
+
+TEST(ConnectionSpecTest, EmptyTransportAndActionIsError) {
+  EXPECT_THAT_EXPECTED(ConnectionSpec::parse(":=3"), Failed());
+}
+
+} // namespace



More information about the llvm-commits mailing list