[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