[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 22:27:30 PDT 2026


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

>From 23de6a727feac70a61db702de19e5c2cc90812b5 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/4] [ORC] Add SymbolNameSpec, hoist name mangling into
 Shared.

Introduce two Shared, ExecutionSession-independent pieces:

 - SymbolNameSpec (Shared/SymbolNameSpec.h): a StringRef paired with a
   SymbolNameKind (Verbatim, Linker, IR, C) describing the naming level a
   symbol name is expressed in. A lightweight, dependency-free value type, so
   it can describe interface-name constants in Shared/ headers.

 - Mangler (Shared/Mangler.h, Shared/Mangler.cpp): the linker name-mangling
   logic factored out of MangleAndInterner. It holds only a ManglingMode (no
   ExecutionSession or SymbolStringPool), so names can be mangled on the
   executor side as well as the controller side. It exposes withMangledNameDo
   (which avoids allocating when mangling is a no-op) and a mangledCopy
   convenience, both dispatching on the spec's SymbolNameKind.

MangleAndInterner becomes a thin wrapper: it derives a ManglingMode (from an
ABIName / ManglingMode / DataLayout), holds a Mangler, and interns whatever the
Mangler produces. Behavior is unchanged.

This lets lookup utilities eventually accept C / IR source names and mangle
them to linker level on the caller's behalf, rather than requiring pre-mangled
names. The MangleAndInternerTest unit tests are renamed to ManglerTest and now
exercise Mangler directly; MangleAndInterner is left untested since it is
slated for removal.
---
 .../llvm/ExecutionEngine/Orc/Mangling.h       | 42 ++++----
 .../llvm/ExecutionEngine/Orc/Shared/Mangler.h | 95 +++++++++++++++++++
 .../Orc/Shared/SymbolNameSpec.h               | 68 +++++++++++++
 llvm/lib/ExecutionEngine/Orc/LLJIT.cpp        |  2 +-
 llvm/lib/ExecutionEngine/Orc/Mangling.cpp     | 79 ---------------
 .../ExecutionEngine/Orc/Shared/CMakeLists.txt |  1 +
 .../ExecutionEngine/Orc/Shared/Mangler.cpp    | 53 +++++++++++
 .../ExecutionEngine/Orc/CMakeLists.txt        |  2 +-
 .../Orc/MangleAndInternerTest.cpp             | 87 -----------------
 .../ExecutionEngine/Orc/ManglerTest.cpp       | 87 +++++++++++++++++
 10 files changed, 329 insertions(+), 187 deletions(-)
 create mode 100644 llvm/include/llvm/ExecutionEngine/Orc/Shared/Mangler.h
 create mode 100644 llvm/include/llvm/ExecutionEngine/Orc/Shared/SymbolNameSpec.h
 create mode 100644 llvm/lib/ExecutionEngine/Orc/Shared/Mangler.cpp
 delete mode 100644 llvm/unittests/ExecutionEngine/Orc/MangleAndInternerTest.cpp
 create mode 100644 llvm/unittests/ExecutionEngine/Orc/ManglerTest.cpp

diff --git a/llvm/include/llvm/ExecutionEngine/Orc/Mangling.h b/llvm/include/llvm/ExecutionEngine/Orc/Mangling.h
index 650ba1fe8cc54..5fd664008e23d 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/Mangler.h"
 #include "llvm/ExecutionEngine/Orc/ThreadSafeModule.h"
 #include "llvm/IR/Module.h"
 #include "llvm/Support/Compiler.h"
@@ -26,30 +27,33 @@ namespace llvm::orc {
 /// ExecutionSession.
 class MangleAndInterner {
 public:
-  enum class ManglingMode {
-    None,
-    ELF,
-    MachO,
-    WinCOFF,
-    WinCOFFX86,
-    GOFF,
-    Mips,
-    XCOFF
-  };
+  using ManglingMode = Mangler::ManglingMode;
+
+  MangleAndInterner(ExecutionSession &ES, Mangler M)
+      : ES(ES), M(std::move(M)) {}
+  MangleAndInterner(ExecutionSession &ES, ManglingMode Mode)
+      : ES(ES), M(Mode) {}
+  MangleAndInterner(ExecutionSession &ES, StringRef ABIName = "")
+      : ES(ES), M(ES.getTargetTriple(), ABIName) {}
+  MangleAndInterner(ExecutionSession &ES, const DataLayout &DL)
+      : ES(ES), M(DL.getStringRepresentation()) {}
+
+  const Mangler &mangler() const { return M; }
+
+  SymbolStringPtr operator()(const SymbolNameSpec &NameSpec) {
+    return M.withMangledNameDo(
+        [this](StringRef MangledName) { return ES.intern(MangledName); },
+        NameSpec);
+  }
 
-  LLVM_ABI MangleAndInterner(ExecutionSession &ES, StringRef ABIName = "");
-  LLVM_ABI MangleAndInterner(ExecutionSession &ES, ManglingMode Mode);
-  LLVM_ABI MangleAndInterner(ExecutionSession &ES, const DataLayout &DL);
-  LLVM_ABI SymbolStringPtr operator()(StringRef Name);
+  SymbolStringPtr operator()(StringRef Name) {
+    return (*this)(SymbolNameSpec(Name, SymbolNameKind::IR));
+  }
 
 private:
-  static ManglingMode fromDataLayoutStr(StringRef DLStr);
-  static ManglingMode fromTriple(const Triple &TT, StringRef ABIName);
-  static ManglingMode fromDataLayout(const DataLayout &DL);
-  bool doNotMangleLeadingQuestionMark() const;
 
   ExecutionSession &ES;
-  ManglingMode Mode;
+  Mangler M;
 };
 
 /// Maps IR global values to their linker symbol names / flags.
diff --git a/llvm/include/llvm/ExecutionEngine/Orc/Shared/Mangler.h b/llvm/include/llvm/ExecutionEngine/Orc/Shared/Mangler.h
new file mode 100644
index 0000000000000..c5075214a9155
--- /dev/null
+++ b/llvm/include/llvm/ExecutionEngine/Orc/Shared/Mangler.h
@@ -0,0 +1,95 @@
+//===------- Mangler.h -- Linker name mangling for ORC ---------*- 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
+//
+//===----------------------------------------------------------------------===//
+//
+// Linker name mangling for ORC.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_EXECUTIONENGINE_ORC_SHARED_MANGLER_H
+#define LLVM_EXECUTIONENGINE_ORC_SHARED_MANGLER_H
+
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ExecutionEngine/Orc/Shared/SymbolNameSpec.h"
+#include "llvm/Support/Compiler.h"
+
+namespace llvm {
+
+class Triple;
+
+namespace orc {
+
+/// Applies linker name-mangling for a target.
+class Mangler {
+public:
+  /// The linker name-mangling scheme for a target, determined by its object
+  /// format. This captures the platform decoration applied to symbol names
+  /// (e.g. a leading '_' on MachO), independently of any ExecutionSession.
+  enum class ManglingMode {
+    None,
+    ELF,
+    MachO,
+    WinCOFF,
+    WinCOFFX86,
+    GOFF,
+    Mips,
+    XCOFF
+  };
+
+  explicit Mangler(ManglingMode Mode) : Mode(Mode) {}
+  explicit Mangler(StringRef DLStr) : Mode(fromDataLayoutStr(DLStr)) {}
+  explicit Mangler(const Triple &TT, StringRef ABIName = "")
+      : Mode(fromTriple(TT, ABIName)) {}
+
+  /// Calls the given callback with the mangled version of Name as a StringRef.
+  /// The mangled name is only valid for the duration of the callback and must
+  /// not escape. This allows withMangledNameDo to avoid allocations when
+  /// mangling is a no-op.
+  template <typename HandlerFn>
+  decltype(auto) withMangledNameDo(HandlerFn &&H,
+                                   const SymbolNameSpec &Name) const {
+    if (Name.getKind() == SymbolNameKind::Verbatim ||
+        Name.getKind() == SymbolNameKind::Linker)
+      return H(Name.getName());
+
+    if (Name.getName().empty())
+      return H(Name.getName());
+
+    if (Name.getName()[0] == '\1')
+      return H(Name.getName().substr(1));
+
+    if (Name.getName()[0] == '?' && doNotMangleLeadingQuestionMark())
+      return H(Name.getName());
+
+    if (Mode == ManglingMode::MachO || Mode == ManglingMode::WinCOFFX86)
+      return H(StringRef(("_" + Name.getName()).str()));
+
+    return H(Name.getName());
+  }
+
+  /// Construct a mangled version of the given name as a std::string.
+  /// This always produces a copy, even for no-op manglings. Prefer
+  /// withMangledNameDo in any performance-sensitive context.
+  std::string mangledCopy(const SymbolNameSpec &Name) const {
+    return withMangledNameDo(
+        [](StringRef MangledName) { return MangledName.str(); }, Name);
+  }
+
+private:
+  LLVM_ABI static ManglingMode fromDataLayoutStr(StringRef DLStr);
+  LLVM_ABI static ManglingMode fromTriple(const Triple &TT, StringRef ABIName);
+  bool doNotMangleLeadingQuestionMark() const {
+    return Mode == ManglingMode::WinCOFF || Mode == ManglingMode::WinCOFFX86;
+  }
+
+  ManglingMode Mode;
+};
+
+} // namespace orc
+} // namespace llvm
+
+#endif // LLVM_EXECUTIONENGINE_ORC_SHARED_MANGLING_H
diff --git a/llvm/include/llvm/ExecutionEngine/Orc/Shared/SymbolNameSpec.h b/llvm/include/llvm/ExecutionEngine/Orc/Shared/SymbolNameSpec.h
new file mode 100644
index 0000000000000..c52d4e1f770af
--- /dev/null
+++ b/llvm/include/llvm/ExecutionEngine/Orc/Shared/SymbolNameSpec.h
@@ -0,0 +1,68 @@
+//===- 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 symbol name paired with the naming level it is expressed in.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_EXECUTIONENGINE_ORC_SHARED_SYMBOLNAMESPEC_H
+#define LLVM_EXECUTIONENGINE_ORC_SHARED_SYMBOLNAMESPEC_H
+
+#include "llvm/ADT/StringRef.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 (SymbolNameKind) it is
+/// expressed in, so that a Mangler / MangleAndInterner can mangle it to linker
+/// level rather than requiring callers to pre-mangle.
+///
+/// The name is not copied: a SymbolNameSpec must not outlive the string it
+/// refers to.
+///
+/// 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:
+  constexpr SymbolNameSpec(StringRef Name,
+                           SymbolNameKind Kind = SymbolNameKind::Verbatim)
+      : Name(Name), Kind(Kind) {}
+
+  static constexpr SymbolNameSpec verbatim(StringRef Name) {
+    return {Name, SymbolNameKind::Verbatim};
+  }
+  static constexpr SymbolNameSpec linker(StringRef Name) {
+    return {Name, SymbolNameKind::Linker};
+  }
+  static constexpr SymbolNameSpec ir(StringRef Name) {
+    return {Name, SymbolNameKind::IR};
+  }
+  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;
+};
+
+} // namespace llvm::orc
+
+#endif // LLVM_EXECUTIONENGINE_ORC_SHARED_SYMBOLNAMESPEC_H
diff --git a/llvm/lib/ExecutionEngine/Orc/LLJIT.cpp b/llvm/lib/ExecutionEngine/Orc/LLJIT.cpp
index 04d24d1d30497..938c8e076eb07 100644
--- a/llvm/lib/ExecutionEngine/Orc/LLJIT.cpp
+++ b/llvm/lib/ExecutionEngine/Orc/LLJIT.cpp
@@ -1105,7 +1105,7 @@ std::string LLJIT::mangle(StringRef UnmangledName) const {
   std::string MangledName;
   {
     raw_string_ostream MangledNameStream(MangledName);
-    Mangler::getNameWithPrefix(MangledNameStream, UnmangledName, DL);
+    llvm::Mangler::getNameWithPrefix(MangledNameStream, UnmangledName, DL);
   }
   return MangledName;
 }
diff --git a/llvm/lib/ExecutionEngine/Orc/Mangling.cpp b/llvm/lib/ExecutionEngine/Orc/Mangling.cpp
index ec95b56ef0e95..2fe78d615a5b5 100644
--- a/llvm/lib/ExecutionEngine/Orc/Mangling.cpp
+++ b/llvm/lib/ExecutionEngine/Orc/Mangling.cpp
@@ -7,92 +7,13 @@
 //===----------------------------------------------------------------------===//
 
 #include "llvm/ExecutionEngine/Orc/Mangling.h"
-#include "llvm/ADT/StringExtras.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/IR/Constants.h"
-#include "llvm/IR/Mangler.h"
-#include "llvm/Support/ErrorHandling.h"
 
 #define DEBUG_TYPE "orc"
 
 namespace llvm::orc {
 
-MangleAndInterner::MangleAndInterner(ExecutionSession &ES, StringRef ABIName)
-    : ES(ES), Mode(fromTriple(ES.getTargetTriple(), ABIName)) {}
-
-MangleAndInterner::MangleAndInterner(ExecutionSession &ES, ManglingMode Mode)
-    : ES(ES), Mode(Mode) {}
-
-MangleAndInterner::MangleAndInterner(ExecutionSession &ES, const DataLayout &DL)
-    : ES(ES), Mode(fromDataLayout(DL)) {}
-
-// TODO: The prefixing rules below, and the mangling-mode derivation in
-// fromDataLayoutStr, duplicate logic that already lives in llvm::Mangler
-// (getNameWithPrefix) and DataLayout (ManglingModeT and its "m:" spec
-// parsing). They are re-implemented here only because those APIs require a
-// full DataLayout, which this class is meant to work without. We should
-// refactor to have one copy of this code, probably best defined in
-// TargetParser, shared between all users.
-SymbolStringPtr MangleAndInterner::operator()(StringRef Name) {
-  if (Name.empty())
-    return ES.intern(Name);
-
-  if (Name.front() == '\1')
-    return ES.intern(Name.substr(1));
-
-  if (Name[0] == '?' && doNotMangleLeadingQuestionMark())
-    return ES.intern(Name);
-
-  if (Mode == ManglingMode::MachO || Mode == ManglingMode::WinCOFFX86)
-    return ES.intern(("_" + Name).str());
-
-  return ES.intern(Name);
-}
-
-MangleAndInterner::ManglingMode
-MangleAndInterner::fromDataLayoutStr(StringRef DLStr) {
-  for (StringRef Spec : split(DLStr, '-')) {
-    if (!Spec.starts_with("m:"))
-      continue;
-    auto ModeStr = Spec.drop_front(2);
-    assert(ModeStr.size() == 1 &&
-           "invalid data layout string from Triple::computeDataLayout");
-    switch (ModeStr[0]) {
-    case 'e':
-      return ManglingMode::ELF;
-    case 'l':
-      return ManglingMode::GOFF;
-    case 'o':
-      return ManglingMode::MachO;
-    case 'm':
-      return ManglingMode::Mips;
-    case 'w':
-      return ManglingMode::WinCOFF;
-    case 'x':
-      return ManglingMode::WinCOFFX86;
-    case 'a':
-      return ManglingMode::XCOFF;
-    default:
-      llvm_unreachable("Invalid mangling mode from Triple::computeDataLayout");
-    }
-  }
-  return ManglingMode::None;
-}
-
-MangleAndInterner::ManglingMode
-MangleAndInterner::fromTriple(const Triple &TT, StringRef ABIName) {
-  return fromDataLayoutStr(TT.computeDataLayout(ABIName));
-}
-
-MangleAndInterner::ManglingMode
-MangleAndInterner::fromDataLayout(const DataLayout &DL) {
-  return fromDataLayoutStr(DL.getStringRepresentation());
-}
-
-bool MangleAndInterner::doNotMangleLeadingQuestionMark() const {
-  return Mode == ManglingMode::WinCOFF || Mode == ManglingMode::WinCOFFX86;
-}
-
 void IRSymbolMapper::add(ExecutionSession &ES, const ManglingOptions &MO,
                          ArrayRef<GlobalValue *> GVs,
                          SymbolFlagsMap &SymbolFlags,
diff --git a/llvm/lib/ExecutionEngine/Orc/Shared/CMakeLists.txt b/llvm/lib/ExecutionEngine/Orc/Shared/CMakeLists.txt
index 717fc21826aba..33a8a41df1e0f 100644
--- a/llvm/lib/ExecutionEngine/Orc/Shared/CMakeLists.txt
+++ b/llvm/lib/ExecutionEngine/Orc/Shared/CMakeLists.txt
@@ -1,6 +1,7 @@
 add_llvm_component_library(LLVMOrcShared
   AllocationActions.cpp
   MachOObjectFormat.cpp
+  Mangler.cpp
   ObjectFormats.cpp
   OrcError.cpp
   OrcRTBridge.cpp
diff --git a/llvm/lib/ExecutionEngine/Orc/Shared/Mangler.cpp b/llvm/lib/ExecutionEngine/Orc/Shared/Mangler.cpp
new file mode 100644
index 0000000000000..e66aa2bcdc677
--- /dev/null
+++ b/llvm/lib/ExecutionEngine/Orc/Shared/Mangler.cpp
@@ -0,0 +1,53 @@
+//===------------- Mangler.cpp -- Linker name mangling for ORC ------------===//
+//
+// 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/Mangler.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/TargetParser/Triple.h"
+
+#include <cassert>
+
+#define DEBUG_TYPE "orc"
+
+namespace llvm::orc {
+
+Mangler::ManglingMode Mangler::fromDataLayoutStr(StringRef DLStr) {
+  for (StringRef Spec : split(DLStr, '-')) {
+    if (!Spec.starts_with("m:"))
+      continue;
+    auto ModeStr = Spec.drop_front(2);
+    assert(ModeStr.size() == 1 &&
+           "invalid data layout string from Triple::computeDataLayout");
+    switch (ModeStr[0]) {
+    case 'e':
+      return ManglingMode::ELF;
+    case 'l':
+      return ManglingMode::GOFF;
+    case 'o':
+      return ManglingMode::MachO;
+    case 'm':
+      return ManglingMode::Mips;
+    case 'w':
+      return ManglingMode::WinCOFF;
+    case 'x':
+      return ManglingMode::WinCOFFX86;
+    case 'a':
+      return ManglingMode::XCOFF;
+    default:
+      llvm_unreachable("Invalid mangling mode from Triple::computeDataLayout");
+    }
+  }
+  return ManglingMode::None;
+}
+
+Mangler::ManglingMode Mangler::fromTriple(const Triple &TT, StringRef ABIName) {
+  return fromDataLayoutStr(TT.computeDataLayout(ABIName));
+}
+
+} // namespace llvm::orc
diff --git a/llvm/unittests/ExecutionEngine/Orc/CMakeLists.txt b/llvm/unittests/ExecutionEngine/Orc/CMakeLists.txt
index a9271bac9d1c7..7e5f3f2192011 100644
--- a/llvm/unittests/ExecutionEngine/Orc/CMakeLists.txt
+++ b/llvm/unittests/ExecutionEngine/Orc/CMakeLists.txt
@@ -33,7 +33,7 @@ add_llvm_unittest(OrcJITTests
   LinkGraphLinkingLayerTest.cpp
   LookupAndApplyTest.cpp
   MachOBuilderTest.cpp
-  MangleAndInternerTest.cpp
+  ManglerTest.cpp
   MachOPlatformTest.cpp
   MapperJITLinkMemoryManagerTest.cpp
   MemoryFlagsTest.cpp
diff --git a/llvm/unittests/ExecutionEngine/Orc/MangleAndInternerTest.cpp b/llvm/unittests/ExecutionEngine/Orc/MangleAndInternerTest.cpp
deleted file mode 100644
index a047985ad1d3b..0000000000000
--- a/llvm/unittests/ExecutionEngine/Orc/MangleAndInternerTest.cpp
+++ /dev/null
@@ -1,87 +0,0 @@
-//===---- MangleAndInternerTest.cpp - Unit tests for MangleAndInterner ----===//
-//
-// 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/Mangling.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()));
-}
-
-struct ManglingCase {
-  StringRef Triple;
-  StringRef Input;
-  StringRef Expected;
-};
-
-} // namespace
-
-TEST(MangleAndInternerTest, FromTripleAcrossFormats) {
-  static const ManglingCase Cases[] = {
-      // ELF: no prefix.
-      {"x86_64-unknown-linux-gnu", "foo", "foo"},
-      // MachO: leading underscore.
-      {"x86_64-apple-darwin", "foo", "_foo"},
-      {"arm64-apple-darwin", "foo", "_foo"},
-      // Windows COFF, x86_64: no prefix.
-      {"x86_64-pc-windows-msvc", "foo", "foo"},
-      // Windows COFF, x86 (32-bit): leading underscore.
-      {"i686-pc-windows-msvc", "foo", "_foo"},
-      // AIX XCOFF: no prefix.
-      {"powerpc64-ibm-aix", "foo", "foo"},
-      // z/OS GOFF: no prefix.
-      {"s390x-ibm-zos", "foo", "foo"},
-      // MIPS O32: no prefix.
-      {"mipsel-unknown-linux-gnu", "foo", "foo"},
-  };
-
-  for (const auto &C : Cases) {
-    SCOPED_TRACE(C.Triple);
-    ExecutionSession ES = makeES(C.Triple);
-    MangleAndInterner Mangle(ES);
-    EXPECT_EQ(*Mangle(C.Input), C.Expected);
-    cantFail(ES.endSession());
-  }
-}
-
-TEST(MangleAndInternerTest, DoNotMangleLeadingBackslash1) {
-  ExecutionSession ES = makeES("x86_64-apple-darwin");
-  MangleAndInterner Mangle(ES);
-  EXPECT_EQ(*Mangle("\1foo"), "foo");
-  cantFail(ES.endSession());
-}
-
-TEST(MangleAndInternerTest, WindowsQuestionMarkNotMangled) {
-  ExecutionSession ES = makeES("x86_64-pc-windows-msvc");
-  MangleAndInterner Mangle(ES);
-  EXPECT_EQ(*Mangle("?foo@@YAHXZ"), "?foo@@YAHXZ");
-  cantFail(ES.endSession());
-}
-
-TEST(MangleAndInternerTest, MachOQuestionMarkIsMangled) {
-  // MachO has no question-mark suppression: gets the usual '_' prefix.
-  ExecutionSession ES = makeES("x86_64-apple-darwin");
-  MangleAndInterner Mangle(ES);
-  EXPECT_EQ(*Mangle("?foo"), "_?foo");
-  cantFail(ES.endSession());
-}
-
-TEST(MangleAndInternerTest, ExplicitManglingMode) {
-  ExecutionSession ES = makeES("x86_64-unknown-linux-gnu");
-  MangleAndInterner Mangle(ES, MangleAndInterner::ManglingMode::MachO);
-  EXPECT_EQ(*Mangle("foo"), "_foo");
-  cantFail(ES.endSession());
-}
diff --git a/llvm/unittests/ExecutionEngine/Orc/ManglerTest.cpp b/llvm/unittests/ExecutionEngine/Orc/ManglerTest.cpp
new file mode 100644
index 0000000000000..41ec22c432021
--- /dev/null
+++ b/llvm/unittests/ExecutionEngine/Orc/ManglerTest.cpp
@@ -0,0 +1,87 @@
+//===-------------- ManglerTest.cpp - Unit tests for Mangler --------------===//
+//
+// 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/Mangler.h"
+#include "llvm/ExecutionEngine/Orc/Shared/SymbolNameSpec.h"
+
+#include "OrcTestCommon.h"
+
+using namespace llvm;
+using namespace llvm::orc;
+
+namespace {
+
+struct ManglingCase {
+  StringRef Triple;
+  StringRef Input;
+  StringRef Expected;
+};
+
+} // namespace
+
+TEST(ManglerTest, FromTripleAcrossFormats) {
+  static const ManglingCase Cases[] = {
+      // ELF: no prefix.
+      {"x86_64-unknown-linux-gnu", "foo", "foo"},
+      // MachO: leading underscore.
+      {"x86_64-apple-darwin", "foo", "_foo"},
+      {"arm64-apple-darwin", "foo", "_foo"},
+      // Windows COFF, x86_64: no prefix.
+      {"x86_64-pc-windows-msvc", "foo", "foo"},
+      // Windows COFF, x86 (32-bit): leading underscore.
+      {"i686-pc-windows-msvc", "foo", "_foo"},
+      // AIX XCOFF: no prefix.
+      {"powerpc64-ibm-aix", "foo", "foo"},
+      // z/OS GOFF: no prefix.
+      {"s390x-ibm-zos", "foo", "foo"},
+      // MIPS O32: no prefix.
+      {"mipsel-unknown-linux-gnu", "foo", "foo"},
+  };
+
+  for (const auto &C : Cases) {
+    SCOPED_TRACE(C.Triple);
+    Mangler Mangle(Triple(C.Triple));
+    EXPECT_EQ(Mangle.mangledCopy(SymbolNameSpec::ir(C.Input)), C.Expected);
+  }
+}
+
+TEST(ManglerTest, DoNotMangleLeadingBackslash1) {
+  Mangler Mangle(Triple("x86_64-apple-darwin"));
+  EXPECT_EQ(Mangle.mangledCopy(SymbolNameSpec::ir("\1foo")), "foo");
+}
+
+TEST(ManglerTest, WindowsQuestionMarkNotMangled) {
+  Mangler Mangle(Triple("x86_64-pc-windows-msvc"));
+  EXPECT_EQ(Mangle.mangledCopy(SymbolNameSpec::ir("?foo@@YAHXZ")),
+            "?foo@@YAHXZ");
+}
+
+TEST(ManglerTest, MachOQuestionMarkIsMangled) {
+  // MachO has no question-mark suppression: gets the usual '_' prefix.
+  Mangler Mangle(Triple("x86_64-apple-darwin"));
+  EXPECT_EQ(Mangle.mangledCopy(SymbolNameSpec::ir("?foo")), "_?foo");
+}
+
+TEST(ManglerTest, ExplicitManglingMode) {
+  Mangler Mangle(Mangler::ManglingMode::MachO);
+  EXPECT_EQ(Mangle.mangledCopy(SymbolNameSpec::ir("foo")), "_foo");
+}
+
+// Use a MachO triple so that mangling ("_" prefix) is observably different from
+// interning verbatim.
+TEST(ManglerTest, SymbolNameSpecKindDispatch) {
+  Mangler Mangle(Triple("x86_64-apple-darwin"));
+
+  // Verbatim and Linker are interned unmodified.
+  EXPECT_EQ(Mangle.mangledCopy(SymbolNameSpec::verbatim("foo")), "foo");
+  EXPECT_EQ(Mangle.mangledCopy(SymbolNameSpec::linker("foo")), "foo");
+
+  // IR and C are mangled (leading "_" on MachO).
+  EXPECT_EQ(Mangle.mangledCopy(SymbolNameSpec::ir("foo")), "_foo");
+  EXPECT_EQ(Mangle.mangledCopy(SymbolNameSpec::c("foo")), "_foo");
+}

>From 9cab371571f3390908ea8d2c45c4d26927b8ac89 Mon Sep 17 00:00:00 2001
From: Lang Hames <lhames at gmail.com>
Date: Tue, 8 Sep 2026 12:01:31 +1000
Subject: [PATCH 2/4] clang-format

---
 llvm/include/llvm/ExecutionEngine/Orc/Mangling.h | 1 -
 1 file changed, 1 deletion(-)

diff --git a/llvm/include/llvm/ExecutionEngine/Orc/Mangling.h b/llvm/include/llvm/ExecutionEngine/Orc/Mangling.h
index 5fd664008e23d..46c1b926def7d 100644
--- a/llvm/include/llvm/ExecutionEngine/Orc/Mangling.h
+++ b/llvm/include/llvm/ExecutionEngine/Orc/Mangling.h
@@ -51,7 +51,6 @@ class MangleAndInterner {
   }
 
 private:
-
   ExecutionSession &ES;
   Mangler M;
 };

>From 66a75b844dcbcb20317509549b9641555dfbed3c Mon Sep 17 00:00:00 2001
From: Lang Hames <lhames at gmail.com>
Date: Tue, 8 Sep 2026 12:41:24 +1000
Subject: [PATCH 3/4] Use SmallString for mangling

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

diff --git a/llvm/include/llvm/ExecutionEngine/Orc/Shared/Mangler.h b/llvm/include/llvm/ExecutionEngine/Orc/Shared/Mangler.h
index c5075214a9155..f711f1d777ae6 100644
--- a/llvm/include/llvm/ExecutionEngine/Orc/Shared/Mangler.h
+++ b/llvm/include/llvm/ExecutionEngine/Orc/Shared/Mangler.h
@@ -13,6 +13,7 @@
 #ifndef LLVM_EXECUTIONENGINE_ORC_SHARED_MANGLER_H
 #define LLVM_EXECUTIONENGINE_ORC_SHARED_MANGLER_H
 
+#include "llvm/ADT/SmallString.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/ExecutionEngine/Orc/Shared/SymbolNameSpec.h"
 #include "llvm/Support/Compiler.h"
@@ -65,8 +66,11 @@ class Mangler {
     if (Name.getName()[0] == '?' && doNotMangleLeadingQuestionMark())
       return H(Name.getName());
 
-    if (Mode == ManglingMode::MachO || Mode == ManglingMode::WinCOFFX86)
-      return H(StringRef(("_" + Name.getName()).str()));
+    if (Mode == ManglingMode::MachO || Mode == ManglingMode::WinCOFFX86) {
+      SmallString<1024> MangledName;
+      MangledName.append({StringRef("_"), Name.getName()});
+      return H(StringRef(MangledName));
+    }
 
     return H(Name.getName());
   }

>From b334554f5ec16116bb723cd2b5a7e94ae8904d68 Mon Sep 17 00:00:00 2001
From: Lang Hames <lhames at gmail.com>
Date: Tue, 8 Sep 2026 15:26:49 +1000
Subject: [PATCH 4/4] Simplify mode enum name, rename namespec arg

---
 .../llvm/ExecutionEngine/Orc/Mangling.h       |  2 +-
 .../llvm/ExecutionEngine/Orc/Shared/Mangler.h | 61 ++++++++-----------
 .../ExecutionEngine/Orc/Shared/Mangler.cpp    | 20 +++---
 .../ExecutionEngine/Orc/ManglerTest.cpp       |  2 +-
 4 files changed, 38 insertions(+), 47 deletions(-)

diff --git a/llvm/include/llvm/ExecutionEngine/Orc/Mangling.h b/llvm/include/llvm/ExecutionEngine/Orc/Mangling.h
index 46c1b926def7d..f592608ef1557 100644
--- a/llvm/include/llvm/ExecutionEngine/Orc/Mangling.h
+++ b/llvm/include/llvm/ExecutionEngine/Orc/Mangling.h
@@ -27,7 +27,7 @@ namespace llvm::orc {
 /// ExecutionSession.
 class MangleAndInterner {
 public:
-  using ManglingMode = Mangler::ManglingMode;
+  using ManglingMode = Mangler::Mode;
 
   MangleAndInterner(ExecutionSession &ES, Mangler M)
       : ES(ES), M(std::move(M)) {}
diff --git a/llvm/include/llvm/ExecutionEngine/Orc/Shared/Mangler.h b/llvm/include/llvm/ExecutionEngine/Orc/Shared/Mangler.h
index f711f1d777ae6..e9a2f268fbaff 100644
--- a/llvm/include/llvm/ExecutionEngine/Orc/Shared/Mangler.h
+++ b/llvm/include/llvm/ExecutionEngine/Orc/Shared/Mangler.h
@@ -30,49 +30,40 @@ class Mangler {
   /// The linker name-mangling scheme for a target, determined by its object
   /// format. This captures the platform decoration applied to symbol names
   /// (e.g. a leading '_' on MachO), independently of any ExecutionSession.
-  enum class ManglingMode {
-    None,
-    ELF,
-    MachO,
-    WinCOFF,
-    WinCOFFX86,
-    GOFF,
-    Mips,
-    XCOFF
-  };
-
-  explicit Mangler(ManglingMode Mode) : Mode(Mode) {}
-  explicit Mangler(StringRef DLStr) : Mode(fromDataLayoutStr(DLStr)) {}
+  enum class Mode { None, ELF, MachO, WinCOFF, WinCOFFX86, GOFF, Mips, XCOFF };
+
+  explicit Mangler(Mode MM) : MM(MM) {}
+  explicit Mangler(StringRef DLStr) : MM(fromDataLayoutStr(DLStr)) {}
   explicit Mangler(const Triple &TT, StringRef ABIName = "")
-      : Mode(fromTriple(TT, ABIName)) {}
+      : MM(fromTriple(TT, ABIName)) {}
 
-  /// Calls the given callback with the mangled version of Name as a StringRef.
-  /// The mangled name is only valid for the duration of the callback and must
-  /// not escape. This allows withMangledNameDo to avoid allocations when
-  /// mangling is a no-op.
+  /// Calls the given callback with the mangled version of NameSpec as a
+  /// StringRef. The mangled name is only valid for the duration of the callback
+  /// and must not escape. This allows withMangledNameDo to avoid allocations
+  /// when mangling is a no-op.
   template <typename HandlerFn>
   decltype(auto) withMangledNameDo(HandlerFn &&H,
-                                   const SymbolNameSpec &Name) const {
-    if (Name.getKind() == SymbolNameKind::Verbatim ||
-        Name.getKind() == SymbolNameKind::Linker)
-      return H(Name.getName());
+                                   const SymbolNameSpec &NameSpec) const {
+    if (NameSpec.getKind() == SymbolNameKind::Verbatim ||
+        NameSpec.getKind() == SymbolNameKind::Linker)
+      return H(NameSpec.getName());
 
-    if (Name.getName().empty())
-      return H(Name.getName());
+    if (NameSpec.getName().empty())
+      return H(NameSpec.getName());
 
-    if (Name.getName()[0] == '\1')
-      return H(Name.getName().substr(1));
+    if (NameSpec.getName()[0] == '\1')
+      return H(NameSpec.getName().substr(1));
 
-    if (Name.getName()[0] == '?' && doNotMangleLeadingQuestionMark())
-      return H(Name.getName());
+    if (NameSpec.getName()[0] == '?' && doNotMangleLeadingQuestionMark())
+      return H(NameSpec.getName());
 
-    if (Mode == ManglingMode::MachO || Mode == ManglingMode::WinCOFFX86) {
+    if (MM == Mode::MachO || MM == Mode::WinCOFFX86) {
       SmallString<1024> MangledName;
-      MangledName.append({StringRef("_"), Name.getName()});
+      MangledName.append({StringRef("_"), NameSpec.getName()});
       return H(StringRef(MangledName));
     }
 
-    return H(Name.getName());
+    return H(NameSpec.getName());
   }
 
   /// Construct a mangled version of the given name as a std::string.
@@ -84,13 +75,13 @@ class Mangler {
   }
 
 private:
-  LLVM_ABI static ManglingMode fromDataLayoutStr(StringRef DLStr);
-  LLVM_ABI static ManglingMode fromTriple(const Triple &TT, StringRef ABIName);
+  LLVM_ABI static Mode fromDataLayoutStr(StringRef DLStr);
+  LLVM_ABI static Mode fromTriple(const Triple &TT, StringRef ABIName);
   bool doNotMangleLeadingQuestionMark() const {
-    return Mode == ManglingMode::WinCOFF || Mode == ManglingMode::WinCOFFX86;
+    return MM == Mode::WinCOFF || MM == Mode::WinCOFFX86;
   }
 
-  ManglingMode Mode;
+  Mode MM;
 };
 
 } // namespace orc
diff --git a/llvm/lib/ExecutionEngine/Orc/Shared/Mangler.cpp b/llvm/lib/ExecutionEngine/Orc/Shared/Mangler.cpp
index e66aa2bcdc677..824b3d7053ddd 100644
--- a/llvm/lib/ExecutionEngine/Orc/Shared/Mangler.cpp
+++ b/llvm/lib/ExecutionEngine/Orc/Shared/Mangler.cpp
@@ -17,7 +17,7 @@
 
 namespace llvm::orc {
 
-Mangler::ManglingMode Mangler::fromDataLayoutStr(StringRef DLStr) {
+Mangler::Mode Mangler::fromDataLayoutStr(StringRef DLStr) {
   for (StringRef Spec : split(DLStr, '-')) {
     if (!Spec.starts_with("m:"))
       continue;
@@ -26,27 +26,27 @@ Mangler::ManglingMode Mangler::fromDataLayoutStr(StringRef DLStr) {
            "invalid data layout string from Triple::computeDataLayout");
     switch (ModeStr[0]) {
     case 'e':
-      return ManglingMode::ELF;
+      return Mode::ELF;
     case 'l':
-      return ManglingMode::GOFF;
+      return Mode::GOFF;
     case 'o':
-      return ManglingMode::MachO;
+      return Mode::MachO;
     case 'm':
-      return ManglingMode::Mips;
+      return Mode::Mips;
     case 'w':
-      return ManglingMode::WinCOFF;
+      return Mode::WinCOFF;
     case 'x':
-      return ManglingMode::WinCOFFX86;
+      return Mode::WinCOFFX86;
     case 'a':
-      return ManglingMode::XCOFF;
+      return Mode::XCOFF;
     default:
       llvm_unreachable("Invalid mangling mode from Triple::computeDataLayout");
     }
   }
-  return ManglingMode::None;
+  return Mode::None;
 }
 
-Mangler::ManglingMode Mangler::fromTriple(const Triple &TT, StringRef ABIName) {
+Mangler::Mode Mangler::fromTriple(const Triple &TT, StringRef ABIName) {
   return fromDataLayoutStr(TT.computeDataLayout(ABIName));
 }
 
diff --git a/llvm/unittests/ExecutionEngine/Orc/ManglerTest.cpp b/llvm/unittests/ExecutionEngine/Orc/ManglerTest.cpp
index 41ec22c432021..ed0aff788cfd0 100644
--- a/llvm/unittests/ExecutionEngine/Orc/ManglerTest.cpp
+++ b/llvm/unittests/ExecutionEngine/Orc/ManglerTest.cpp
@@ -68,7 +68,7 @@ TEST(ManglerTest, MachOQuestionMarkIsMangled) {
 }
 
 TEST(ManglerTest, ExplicitManglingMode) {
-  Mangler Mangle(Mangler::ManglingMode::MachO);
+  Mangler Mangle(Mangler::Mode::MachO);
   EXPECT_EQ(Mangle.mangledCopy(SymbolNameSpec::ir("foo")), "_foo");
 }
 



More information about the llvm-commits mailing list