[llvm] [ORC] Add SymbolNameSpec for mangling-aware symbol names (PR #221635)

Lang Hames via llvm-commits llvm-commits at lists.llvm.org
Mon Sep 7 00:08:51 PDT 2026


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

>From e1ed1f777ae4667da1fa61c9874fcb9fe26346df Mon Sep 17 00:00:00 2001
From: Lang Hames <lhames at gmail.com>
Date: Mon, 7 Sep 2026 14:48:08 +1000
Subject: [PATCH 1/3] [ORC] Add SymbolNameSpec for mangling-aware symbol names

Introduce SymbolNameSpec, a pair of a StringRef and a SymbolNameKind
(Verbatim, Linker, C, IR) describing the scheme a symbol name is
expressed in. Its mangleAndIntern(ES) method interns the name, applying
platform mangling for C / IR names (e.g. "malloc" -> "_malloc" on MachO)
and interning Verbatim / Linker names as-is.

This lets lookup utilities such as recordAddr and recordProxy accept C /
IR source names directly, instead of requiring callers to pre-mangle to
linker-level by hand, which is easy to get wrong.

Linker is currently a synonym for Verbatim, and C for IR; the kinds are
kept distinct to document call-site intent.

Future commits will migrate recordAddr / recordProxy to SymbolNameSpec.
---
 .../llvm/ExecutionEngine/Orc/SymbolNameSpec.h | 85 +++++++++++++++++++
 .../ExecutionEngine/Orc/CMakeLists.txt        |  1 +
 .../Orc/SymbolNameSpecTest.cpp                | 43 ++++++++++
 3 files changed, 129 insertions(+)
 create mode 100644 llvm/include/llvm/ExecutionEngine/Orc/SymbolNameSpec.h
 create mode 100644 llvm/unittests/ExecutionEngine/Orc/SymbolNameSpecTest.cpp

diff --git a/llvm/include/llvm/ExecutionEngine/Orc/SymbolNameSpec.h b/llvm/include/llvm/ExecutionEngine/Orc/SymbolNameSpec.h
new file mode 100644
index 0000000000000..961f4837d788d
--- /dev/null
+++ b/llvm/include/llvm/ExecutionEngine/Orc/SymbolNameSpec.h
@@ -0,0 +1,85 @@
+//===- SymbolNameSpec.h - A symbol name plus its mangling kind --*- 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 SymbolNameSpec pairs a symbol name with the naming level it is expressed
+// in, so that lookup utilities can accept C / IR source names and mangle them
+// to linker level on the caller's behalf rather than requiring pre-mangled
+// names.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_EXECUTIONENGINE_ORC_SYMBOLNAMESPEC_H
+#define LLVM_EXECUTIONENGINE_ORC_SYMBOLNAMESPEC_H
+
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ExecutionEngine/Orc/Core.h"
+#include "llvm/ExecutionEngine/Orc/Mangling.h"
+#include "llvm/Support/ErrorHandling.h"
+
+namespace llvm::orc {
+
+/// The naming level a symbol name is expressed in, which determines how it is
+/// mangled before being interned for lookup.
+enum class SymbolNameKind {
+  Verbatim, // Use the name as given, with no mangling.
+  Linker,   // An already-decorated linker name; a synonym for Verbatim.
+  IR,       // An IR global name; mangled to linker level for the target.
+  C,        // A C source name; handled identically to IR.
+};
+
+/// A symbol name together with the naming level it is expressed in.
+///
+/// The name is not copied: a SymbolNameSpec must not outlive the string it
+/// refers to, and must remain valid until intern() is called on it.
+///
+/// SymbolNameSpec is implicitly constructible from a StringRef, defaulting to
+/// Verbatim, so that APIs taking a SymbolNameSpec stay drop-in replacements for
+/// ones that previously took an already-mangled StringRef.
+class SymbolNameSpec {
+public:
+  SymbolNameSpec(StringRef Name, SymbolNameKind Kind = SymbolNameKind::Verbatim)
+      : Name(Name), Kind(Kind) {}
+
+  static SymbolNameSpec verbatim(StringRef Name) {
+    return {Name, SymbolNameKind::Verbatim};
+  }
+  static SymbolNameSpec linker(StringRef Name) {
+    return {Name, SymbolNameKind::Linker};
+  }
+  static SymbolNameSpec ir(StringRef Name) {
+    return {Name, SymbolNameKind::IR};
+  }
+  static SymbolNameSpec c(StringRef Name) { return {Name, SymbolNameKind::C}; }
+
+  StringRef getName() const { return Name; }
+  SymbolNameKind getKind() const { return Kind; }
+
+  /// Mangle (if required by Kind) and intern this name in the context of ES.
+  ///
+  /// C / IR names are mangled for ES's target triple, not for any particular
+  /// JITDylib's platform.
+  SymbolStringPtr mangleAndIntern(ExecutionSession &ES) const {
+    switch (Kind) {
+    case SymbolNameKind::Verbatim:
+    case SymbolNameKind::Linker:
+      return ES.intern(Name);
+    case SymbolNameKind::IR:
+    case SymbolNameKind::C:
+      return MangleAndInterner(ES)(Name);
+    }
+    llvm_unreachable("Unknown SymbolNameKind");
+  }
+
+private:
+  StringRef Name;
+  SymbolNameKind Kind;
+};
+
+} // namespace llvm::orc
+
+#endif // LLVM_EXECUTIONENGINE_ORC_SYMBOLNAMESPEC_H
diff --git a/llvm/unittests/ExecutionEngine/Orc/CMakeLists.txt b/llvm/unittests/ExecutionEngine/Orc/CMakeLists.txt
index a9271bac9d1c7..47c9f332a37ce 100644
--- a/llvm/unittests/ExecutionEngine/Orc/CMakeLists.txt
+++ b/llvm/unittests/ExecutionEngine/Orc/CMakeLists.txt
@@ -50,6 +50,7 @@ add_llvm_unittest(OrcJITTests
   SimplePackedSerializationTest.cpp
   SPSProxySpecTest.cpp
   SymbolLookupSetTest.cpp
+  SymbolNameSpecTest.cpp
   SymbolStringPoolTest.cpp
   TaskDispatchTest.cpp
   ThreadSafeModuleTest.cpp
diff --git a/llvm/unittests/ExecutionEngine/Orc/SymbolNameSpecTest.cpp b/llvm/unittests/ExecutionEngine/Orc/SymbolNameSpecTest.cpp
new file mode 100644
index 0000000000000..ee221f2f4fdba
--- /dev/null
+++ b/llvm/unittests/ExecutionEngine/Orc/SymbolNameSpecTest.cpp
@@ -0,0 +1,43 @@
+//===------ SymbolNameSpecTest.cpp - Unit tests for SymbolNameSpec --------===//
+//
+// 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/SymbolNameSpec.h"
+#include "llvm/Support/Error.h"
+
+#include "OrcTestCommon.h"
+
+using namespace llvm;
+using namespace llvm::orc;
+
+namespace {
+
+ExecutionSession makeES(StringRef TT) {
+  return ExecutionSession(std::make_unique<UnsupportedExecutorProcessControl>(
+      nullptr, nullptr, TT.str()));
+}
+
+} // namespace
+
+// Use a MachO triple so that mangling ("_" prefix) is observably different from
+// interning verbatim.
+TEST(SymbolNameSpecTest, KindDispatch) {
+  ExecutionSession ES = makeES("x86_64-apple-darwin");
+
+  // Verbatim interns unmodified.
+  EXPECT_EQ(*SymbolNameSpec::verbatim("foo").mangleAndIntern(ES), "foo");
+
+  // Linker mangles as Verbatim (unmodified).
+  EXPECT_EQ(*SymbolNameSpec::linker("foo").mangleAndIntern(ES), "foo");
+
+  // IR mangles as C (leading "_" on MachO).
+  EXPECT_EQ(*SymbolNameSpec::ir("foo").mangleAndIntern(ES),
+            *SymbolNameSpec::c("foo").mangleAndIntern(ES));
+  EXPECT_EQ(*SymbolNameSpec::c("foo").mangleAndIntern(ES), "_foo");
+
+  cantFail(ES.endSession());
+}

>From ac2fa5cba111dbf10c968c427fb22bd2f5a1c5b7 Mon Sep 17 00:00:00 2001
From: Lang Hames <lhames at gmail.com>
Date: Mon, 7 Sep 2026 16:57:38 +1000
Subject: [PATCH 2/3] Move SymbolNameSpec up into Shared so that it can be used
 for constants.

---
 .../llvm/ExecutionEngine/Orc/Mangling.h       |  6 +++
 .../Orc/{ => Shared}/SymbolNameSpec.h         | 52 +++++++------------
 llvm/lib/ExecutionEngine/Orc/Mangling.cpp     | 12 +++++
 .../ExecutionEngine/Orc/CMakeLists.txt        |  1 -
 .../Orc/MangleAndInternerTest.cpp             | 18 +++++++
 .../Orc/SymbolNameSpecTest.cpp                | 43 ---------------
 6 files changed, 55 insertions(+), 77 deletions(-)
 rename llvm/include/llvm/ExecutionEngine/Orc/{ => Shared}/SymbolNameSpec.h (53%)
 delete mode 100644 llvm/unittests/ExecutionEngine/Orc/SymbolNameSpecTest.cpp

diff --git a/llvm/include/llvm/ExecutionEngine/Orc/Mangling.h b/llvm/include/llvm/ExecutionEngine/Orc/Mangling.h
index 650ba1fe8cc54..f2c9b5cb87adc 100644
--- a/llvm/include/llvm/ExecutionEngine/Orc/Mangling.h
+++ b/llvm/include/llvm/ExecutionEngine/Orc/Mangling.h
@@ -14,6 +14,7 @@
 #define LLVM_EXECUTIONENGINE_ORC_MANGLING_H
 
 #include "llvm/ExecutionEngine/Orc/Core.h"
+#include "llvm/ExecutionEngine/Orc/Shared/SymbolNameSpec.h"
 #include "llvm/ExecutionEngine/Orc/ThreadSafeModule.h"
 #include "llvm/IR/Module.h"
 #include "llvm/Support/Compiler.h"
@@ -42,6 +43,11 @@ class MangleAndInterner {
   LLVM_ABI MangleAndInterner(ExecutionSession &ES, const DataLayout &DL);
   LLVM_ABI SymbolStringPtr operator()(StringRef Name);
 
+  /// Mangle and intern according to the spec's SymbolNameKind: Verbatim and
+  /// Linker names are interned as-is; IR and C names are mangled as for the
+  /// StringRef overload.
+  LLVM_ABI SymbolStringPtr operator()(const SymbolNameSpec &Name);
+
 private:
   static ManglingMode fromDataLayoutStr(StringRef DLStr);
   static ManglingMode fromTriple(const Triple &TT, StringRef ABIName);
diff --git a/llvm/include/llvm/ExecutionEngine/Orc/SymbolNameSpec.h b/llvm/include/llvm/ExecutionEngine/Orc/Shared/SymbolNameSpec.h
similarity index 53%
rename from llvm/include/llvm/ExecutionEngine/Orc/SymbolNameSpec.h
rename to llvm/include/llvm/ExecutionEngine/Orc/Shared/SymbolNameSpec.h
index 961f4837d788d..3f220f2018f42 100644
--- a/llvm/include/llvm/ExecutionEngine/Orc/SymbolNameSpec.h
+++ b/llvm/include/llvm/ExecutionEngine/Orc/Shared/SymbolNameSpec.h
@@ -7,19 +7,18 @@
 //===----------------------------------------------------------------------===//
 //
 // A SymbolNameSpec pairs a symbol name with the naming level it is expressed
-// in, so that lookup utilities can accept C / IR source names and mangle them
-// to linker level on the caller's behalf rather than requiring pre-mangled
-// names.
+// in, so that names can be mangled to linker level on the caller's behalf
+// (see MangleAndInterner) rather than requiring callers to pre-mangle them.
+//
+// This is a lightweight, dependency-free value type so that it can be used to
+// describe interface-name constants in Shared/ headers.
 //
 //===----------------------------------------------------------------------===//
 
-#ifndef LLVM_EXECUTIONENGINE_ORC_SYMBOLNAMESPEC_H
-#define LLVM_EXECUTIONENGINE_ORC_SYMBOLNAMESPEC_H
+#ifndef LLVM_EXECUTIONENGINE_ORC_SHARED_SYMBOLNAMESPEC_H
+#define LLVM_EXECUTIONENGINE_ORC_SHARED_SYMBOLNAMESPEC_H
 
 #include "llvm/ADT/StringRef.h"
-#include "llvm/ExecutionEngine/Orc/Core.h"
-#include "llvm/ExecutionEngine/Orc/Mangling.h"
-#include "llvm/Support/ErrorHandling.h"
 
 namespace llvm::orc {
 
@@ -35,46 +34,33 @@ enum class SymbolNameKind {
 /// A symbol name together with the naming level it is expressed in.
 ///
 /// The name is not copied: a SymbolNameSpec must not outlive the string it
-/// refers to, and must remain valid until intern() is called on it.
+/// refers to.
 ///
 /// SymbolNameSpec is implicitly constructible from a StringRef, defaulting to
 /// Verbatim, so that APIs taking a SymbolNameSpec stay drop-in replacements for
 /// ones that previously took an already-mangled StringRef.
 class SymbolNameSpec {
 public:
-  SymbolNameSpec(StringRef Name, SymbolNameKind Kind = SymbolNameKind::Verbatim)
+  constexpr SymbolNameSpec(StringRef Name, SymbolNameKind Kind =
+                                               SymbolNameKind::Verbatim)
       : Name(Name), Kind(Kind) {}
 
-  static SymbolNameSpec verbatim(StringRef Name) {
+  static constexpr SymbolNameSpec verbatim(StringRef Name) {
     return {Name, SymbolNameKind::Verbatim};
   }
-  static SymbolNameSpec linker(StringRef Name) {
+  static constexpr SymbolNameSpec linker(StringRef Name) {
     return {Name, SymbolNameKind::Linker};
   }
-  static SymbolNameSpec ir(StringRef Name) {
+  static constexpr SymbolNameSpec ir(StringRef Name) {
     return {Name, SymbolNameKind::IR};
   }
-  static SymbolNameSpec c(StringRef Name) { return {Name, SymbolNameKind::C}; }
-
-  StringRef getName() const { return Name; }
-  SymbolNameKind getKind() const { return Kind; }
-
-  /// Mangle (if required by Kind) and intern this name in the context of ES.
-  ///
-  /// C / IR names are mangled for ES's target triple, not for any particular
-  /// JITDylib's platform.
-  SymbolStringPtr mangleAndIntern(ExecutionSession &ES) const {
-    switch (Kind) {
-    case SymbolNameKind::Verbatim:
-    case SymbolNameKind::Linker:
-      return ES.intern(Name);
-    case SymbolNameKind::IR:
-    case SymbolNameKind::C:
-      return MangleAndInterner(ES)(Name);
-    }
-    llvm_unreachable("Unknown SymbolNameKind");
+  static constexpr SymbolNameSpec c(StringRef Name) {
+    return {Name, SymbolNameKind::C};
   }
 
+  constexpr StringRef getName() const { return Name; }
+  constexpr SymbolNameKind getKind() const { return Kind; }
+
 private:
   StringRef Name;
   SymbolNameKind Kind;
@@ -82,4 +68,4 @@ class SymbolNameSpec {
 
 } // namespace llvm::orc
 
-#endif // LLVM_EXECUTIONENGINE_ORC_SYMBOLNAMESPEC_H
+#endif // LLVM_EXECUTIONENGINE_ORC_SHARED_SYMBOLNAMESPEC_H
diff --git a/llvm/lib/ExecutionEngine/Orc/Mangling.cpp b/llvm/lib/ExecutionEngine/Orc/Mangling.cpp
index ec95b56ef0e95..cffb2a961826c 100644
--- a/llvm/lib/ExecutionEngine/Orc/Mangling.cpp
+++ b/llvm/lib/ExecutionEngine/Orc/Mangling.cpp
@@ -49,6 +49,18 @@ SymbolStringPtr MangleAndInterner::operator()(StringRef Name) {
   return ES.intern(Name);
 }
 
+SymbolStringPtr MangleAndInterner::operator()(const SymbolNameSpec &Name) {
+  switch (Name.getKind()) {
+  case SymbolNameKind::Verbatim:
+  case SymbolNameKind::Linker:
+    return ES.intern(Name.getName());
+  case SymbolNameKind::IR:
+  case SymbolNameKind::C:
+    return (*this)(Name.getName());
+  }
+  llvm_unreachable("Unknown SymbolNameKind");
+}
+
 MangleAndInterner::ManglingMode
 MangleAndInterner::fromDataLayoutStr(StringRef DLStr) {
   for (StringRef Spec : split(DLStr, '-')) {
diff --git a/llvm/unittests/ExecutionEngine/Orc/CMakeLists.txt b/llvm/unittests/ExecutionEngine/Orc/CMakeLists.txt
index 47c9f332a37ce..a9271bac9d1c7 100644
--- a/llvm/unittests/ExecutionEngine/Orc/CMakeLists.txt
+++ b/llvm/unittests/ExecutionEngine/Orc/CMakeLists.txt
@@ -50,7 +50,6 @@ add_llvm_unittest(OrcJITTests
   SimplePackedSerializationTest.cpp
   SPSProxySpecTest.cpp
   SymbolLookupSetTest.cpp
-  SymbolNameSpecTest.cpp
   SymbolStringPoolTest.cpp
   TaskDispatchTest.cpp
   ThreadSafeModuleTest.cpp
diff --git a/llvm/unittests/ExecutionEngine/Orc/MangleAndInternerTest.cpp b/llvm/unittests/ExecutionEngine/Orc/MangleAndInternerTest.cpp
index a047985ad1d3b..d412bdf05f747 100644
--- a/llvm/unittests/ExecutionEngine/Orc/MangleAndInternerTest.cpp
+++ b/llvm/unittests/ExecutionEngine/Orc/MangleAndInternerTest.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "llvm/ExecutionEngine/Orc/Mangling.h"
+#include "llvm/ExecutionEngine/Orc/Shared/SymbolNameSpec.h"
 #include "llvm/Support/Error.h"
 
 #include "OrcTestCommon.h"
@@ -85,3 +86,20 @@ TEST(MangleAndInternerTest, ExplicitManglingMode) {
   EXPECT_EQ(*Mangle("foo"), "_foo");
   cantFail(ES.endSession());
 }
+
+// Use a MachO triple so that mangling ("_" prefix) is observably different from
+// interning verbatim.
+TEST(MangleAndInternerTest, SymbolNameSpecKindDispatch) {
+  ExecutionSession ES = makeES("x86_64-apple-darwin");
+  MangleAndInterner Mangle(ES);
+
+  // Verbatim and Linker are interned unmodified.
+  EXPECT_EQ(*Mangle(SymbolNameSpec::verbatim("foo")), "foo");
+  EXPECT_EQ(*Mangle(SymbolNameSpec::linker("foo")), "foo");
+
+  // IR and C are mangled (leading "_" on MachO).
+  EXPECT_EQ(*Mangle(SymbolNameSpec::ir("foo")), "_foo");
+  EXPECT_EQ(*Mangle(SymbolNameSpec::c("foo")), "_foo");
+
+  cantFail(ES.endSession());
+}
diff --git a/llvm/unittests/ExecutionEngine/Orc/SymbolNameSpecTest.cpp b/llvm/unittests/ExecutionEngine/Orc/SymbolNameSpecTest.cpp
deleted file mode 100644
index ee221f2f4fdba..0000000000000
--- a/llvm/unittests/ExecutionEngine/Orc/SymbolNameSpecTest.cpp
+++ /dev/null
@@ -1,43 +0,0 @@
-//===------ SymbolNameSpecTest.cpp - Unit tests for SymbolNameSpec --------===//
-//
-// 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/SymbolNameSpec.h"
-#include "llvm/Support/Error.h"
-
-#include "OrcTestCommon.h"
-
-using namespace llvm;
-using namespace llvm::orc;
-
-namespace {
-
-ExecutionSession makeES(StringRef TT) {
-  return ExecutionSession(std::make_unique<UnsupportedExecutorProcessControl>(
-      nullptr, nullptr, TT.str()));
-}
-
-} // namespace
-
-// Use a MachO triple so that mangling ("_" prefix) is observably different from
-// interning verbatim.
-TEST(SymbolNameSpecTest, KindDispatch) {
-  ExecutionSession ES = makeES("x86_64-apple-darwin");
-
-  // Verbatim interns unmodified.
-  EXPECT_EQ(*SymbolNameSpec::verbatim("foo").mangleAndIntern(ES), "foo");
-
-  // Linker mangles as Verbatim (unmodified).
-  EXPECT_EQ(*SymbolNameSpec::linker("foo").mangleAndIntern(ES), "foo");
-
-  // IR mangles as C (leading "_" on MachO).
-  EXPECT_EQ(*SymbolNameSpec::ir("foo").mangleAndIntern(ES),
-            *SymbolNameSpec::c("foo").mangleAndIntern(ES));
-  EXPECT_EQ(*SymbolNameSpec::c("foo").mangleAndIntern(ES), "_foo");
-
-  cantFail(ES.endSession());
-}

>From 56d8c495512d3869315d50c4648f807b0aa8eed3 Mon Sep 17 00:00:00 2001
From: Lang Hames <lhames at gmail.com>
Date: Mon, 7 Sep 2026 17:08:35 +1000
Subject: [PATCH 3/3] clang-format

---
 llvm/include/llvm/ExecutionEngine/Orc/Shared/SymbolNameSpec.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/llvm/include/llvm/ExecutionEngine/Orc/Shared/SymbolNameSpec.h b/llvm/include/llvm/ExecutionEngine/Orc/Shared/SymbolNameSpec.h
index 3f220f2018f42..239c5555a4563 100644
--- a/llvm/include/llvm/ExecutionEngine/Orc/Shared/SymbolNameSpec.h
+++ b/llvm/include/llvm/ExecutionEngine/Orc/Shared/SymbolNameSpec.h
@@ -41,8 +41,8 @@ enum class SymbolNameKind {
 /// ones that previously took an already-mangled StringRef.
 class SymbolNameSpec {
 public:
-  constexpr SymbolNameSpec(StringRef Name, SymbolNameKind Kind =
-                                               SymbolNameKind::Verbatim)
+  constexpr SymbolNameSpec(StringRef Name,
+                           SymbolNameKind Kind = SymbolNameKind::Verbatim)
       : Name(Name), Kind(Kind) {}
 
   static constexpr SymbolNameSpec verbatim(StringRef Name) {



More information about the llvm-commits mailing list