[Lldb-commits] [lldb] [lldb] Move demangling into a minimal D language plugin (PR #232929)
via lldb-commits
lldb-commits at lists.llvm.org
Fri Oct 16 13:47:35 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-lldb
Author: Adrian Prantl (adrian-prantl)
<details>
<summary>Changes</summary>
Currently Mangled demangles D names itself, since there is no D
language plugin to hand them to.
This patch adds a minimal one, like the Rust plugin, that recognizes D
source files, registers the D mangling scheme, and demangles its names
with the code that moves out of Mangled, which leaves Mangled without
a demangler of its own.
Assisted-by: Claude
Depends on https://github.com/llvm/llvm-project/pull/232927
---
Patch is 72.29 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/232929.diff
28 Files Affected:
- (modified) lldb/include/lldb/Core/Mangled.h (+61-32)
- (modified) lldb/include/lldb/Core/Module.h (+3-2)
- (modified) lldb/include/lldb/Symbol/Symtab.h (+3-3)
- (modified) lldb/include/lldb/Target/Language.h (+57)
- (modified) lldb/source/Core/Mangled.cpp (+180-199)
- (modified) lldb/source/Core/Module.cpp (+12)
- (modified) lldb/source/Plugins/Language/CMakeLists.txt (+2)
- (modified) lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp (+177-4)
- (modified) lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h (+7)
- (added) lldb/source/Plugins/Language/D/CMakeLists.txt (+11)
- (added) lldb/source/Plugins/Language/D/DLanguage.cpp (+81)
- (added) lldb/source/Plugins/Language/D/DLanguage.h (+44)
- (added) lldb/source/Plugins/Language/Rust/CMakeLists.txt (+11)
- (added) lldb/source/Plugins/Language/Rust/RustLanguage.cpp (+70)
- (added) lldb/source/Plugins/Language/Rust/RustLanguage.h (+44)
- (modified) lldb/source/Symbol/Symtab.cpp (+58-77)
- (modified) lldb/source/Target/Language.cpp (+24)
- (modified) lldb/unittests/Core/CMakeLists.txt (+2)
- (modified) lldb/unittests/Core/MangledTest.cpp (+57-1)
- (modified) lldb/unittests/Core/ModuleTest.cpp (+51)
- (modified) lldb/unittests/Language/CMakeLists.txt (+2)
- (added) lldb/unittests/Language/D/CMakeLists.txt (+6)
- (added) lldb/unittests/Language/D/DLanguageTest.cpp (+43)
- (added) lldb/unittests/Language/Rust/CMakeLists.txt (+6)
- (added) lldb/unittests/Language/Rust/RustLanguageTest.cpp (+35)
- (modified) lldb/unittests/Symbol/CMakeLists.txt (+1)
- (modified) lldb/unittests/Symbol/CallEdgeTest.cpp (+2-1)
- (modified) lldb/unittests/Symbol/TestTypeSystemClang.cpp (+2-1)
``````````diff
diff --git a/lldb/include/lldb/Core/Mangled.h b/lldb/include/lldb/Core/Mangled.h
index 622c0f82f49f77..b29dd1930d77a5 100644
--- a/lldb/include/lldb/Core/Mangled.h
+++ b/lldb/include/lldb/Core/Mangled.h
@@ -14,9 +14,11 @@
#include "lldb/lldb-enumerations.h"
#include "lldb/lldb-forward.h"
#include "lldb/lldb-types.h"
+#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/StringRef.h"
#include <cstddef>
+#include <cstdint>
#include <optional>
namespace lldb_private {
@@ -39,13 +41,41 @@ class Mangled {
ePreferDemangledWithoutArguments
};
- enum ManglingScheme {
+ /// The underlying type is fixed so that the schemes that language plugins
+ /// register, which have no enumerators, are valid values too.
+ enum ManglingScheme : uint8_t {
eManglingSchemeNone = 0,
- eManglingSchemeMSVC,
- eManglingSchemeItanium,
- eManglingSchemeRustV0,
- eManglingSchemeD,
eManglingSchemeSwift,
+ /// The first of the schemes that language plugins register with
+ /// RegisterManglingScheme().
+ eFirstRegisteredManglingScheme,
+ };
+
+ /// The number of mangling schemes, including all that can be registered.
+ static constexpr size_t kNumManglingSchemes = 16;
+
+ /// What a mangled function name says about the function's declaration.
+ enum class FunctionNameKind {
+ /// No enclosing context.
+ TopLevel,
+ /// A function in a namespace or module.
+ InNamespace,
+ /// A method.
+ Method,
+ /// A constructor or destructor, so its context is a type.
+ ConstructorOrDestructor,
+ /// A function or method in a context that the mangling doesn't classify.
+ Unknown,
+ };
+
+ /// The parts of a mangled function name that its symbol is indexed by.
+ struct FunctionNameParts {
+ /// If empty, the symbol is only indexed by its mangled name.
+ ConstString basename;
+ /// The enclosing context. Only needed for ConstructorOrDestructor and
+ /// Unknown.
+ ConstString decl_context;
+ FunctionNameKind kind = FunctionNameKind::Unknown;
};
/// Default constructor.
@@ -195,33 +225,6 @@ class Mangled {
/// if there is no mangled or demangled counterpart.
lldb::LanguageType GuessLanguage() const;
- /// Function signature for filtering mangled names.
- using SkipMangledNameFn = bool(llvm::StringRef, ManglingScheme);
-
- /// Get rich mangling information. This is optimized for batch processing
- /// while populating a name index. To get the pure demangled name string for
- /// a single entity, use GetDemangledName() instead.
- ///
- /// For names that match the Itanium mangling scheme, this uses LLVM's
- /// ItaniumPartialDemangler. All other names fall back to LLDB's builtin
- /// parser currently.
- ///
- /// This function is thread-safe when used with different \a context
- /// instances in different threads.
- ///
- /// \param[in] context
- /// The context for this function. A single instance can be stack-
- /// allocated in the caller's frame and used for multiple calls.
- ///
- /// \param[in] skip_mangled_name
- /// A filtering function for skipping entities based on name and mangling
- /// scheme. This can be null if unused.
- ///
- /// \return
- /// True on success, false otherwise.
- bool GetRichManglingInfo(RichManglingContext &context,
- SkipMangledNameFn *skip_mangled_name);
-
/// Try to identify the mangling scheme used.
/// \param[in] name
/// The name we are attempting to identify the mangling scheme for.
@@ -233,6 +236,32 @@ class Mangled {
static bool IsMangledName(llvm::StringRef name);
+ /// Returns true if \p name, which starts with one of the prefixes of a
+ /// mangling scheme, is a name of that scheme.
+ using ManglingSchemeMatcher = bool (*)(llvm::StringRef name);
+
+ /// Register a mangling scheme whose names start with one of \p prefixes,
+ /// for the language plugin of \p language to demangle and parse. If the
+ /// prefixes alone don't tell the names of the scheme apart, \p matches
+ /// checks the rest of each name. Language plugins call this when they are
+ /// initialized. Registering the same scheme again returns the same scheme.
+ ///
+ /// \return
+ /// The new scheme, or eManglingSchemeNone if there is no room for
+ /// another one.
+ static ManglingScheme
+ RegisterManglingScheme(llvm::ArrayRef<llvm::StringRef> prefixes,
+ lldb::LanguageType language,
+ ManglingSchemeMatcher matches = nullptr);
+
+ /// Stop recognizing the names of \p scheme, a scheme returned by
+ /// RegisterManglingScheme().
+ static void UnregisterManglingScheme(ManglingScheme scheme);
+
+ /// Returns the language that registered \p scheme, or eLanguageTypeUnknown
+ /// if no language did.
+ static lldb::LanguageType GetLanguageForManglingScheme(ManglingScheme scheme);
+
/// Decode a serialized version of this object from data.
///
/// \param data
diff --git a/lldb/include/lldb/Core/Module.h b/lldb/include/lldb/Core/Module.h
index e71a29e3e413b9..b4586e9a420e2d 100644
--- a/lldb/include/lldb/Core/Module.h
+++ b/lldb/include/lldb/Core/Module.h
@@ -954,8 +954,9 @@ class Module : public std::enable_shared_from_this<Module>,
/// \param[in] lang_type
/// The language to create lookups for. If eLanguageTypeUnknown is
/// passed, creates one LookupInfo for each language plugin currently
- /// available in LLDB. If a specific language is provided, creates only
- /// a single LookupInfo for that language.
+ /// available in LLDB, except for full-name lookups that would repeat
+ /// the lookup of another language. If a specific language is provided,
+ /// creates only a single LookupInfo for that language.
///
/// \param[in] lookup_name_override
/// Manually override the name used for lookup. This parameter is
diff --git a/lldb/include/lldb/Symbol/Symtab.h b/lldb/include/lldb/Symbol/Symtab.h
index 224de9e775859f..ea5e2d8044402b 100644
--- a/lldb/include/lldb/Symbol/Symtab.h
+++ b/lldb/include/lldb/Symbol/Symtab.h
@@ -346,9 +346,9 @@ class Symtab {
SymbolContextList &sc_list);
void RegisterMangledNameEntry(
- uint32_t value, std::set<const char *> &class_contexts,
- std::vector<std::pair<NameToIndexMap::Entry, const char *>> &backlog,
- RichManglingContext &rmc);
+ uint32_t value, const Mangled::FunctionNameParts &parts,
+ std::set<const char *> &class_contexts,
+ std::vector<std::pair<NameToIndexMap::Entry, const char *>> &backlog);
void RegisterBacklogEntry(const NameToIndexMap::Entry &entry,
const char *decl_context,
diff --git a/lldb/include/lldb/Target/Language.h b/lldb/include/lldb/Target/Language.h
index c08994cc32e9fd..bd0eb7490346e7 100644
--- a/lldb/include/lldb/Target/Language.h
+++ b/lldb/include/lldb/Target/Language.h
@@ -12,11 +12,14 @@
#include <functional>
#include <memory>
+#include <optional>
#include <set>
+#include <string>
#include <vector>
#include "lldb/Core/FormatEntity.h"
#include "lldb/Core/Highlighter.h"
+#include "lldb/Core/Mangled.h"
#include "lldb/Core/PluginInterface.h"
#include "lldb/DataFormatters/DumpValueObjectOptions.h"
#include "lldb/DataFormatters/FormatClasses.h"
@@ -167,6 +170,10 @@ class Language : public PluginInterface {
static Language *FindPlugin(lldb::LanguageType language,
llvm::StringRef file_path);
+ /// Returns the Language that registered \p scheme with
+ /// Mangled::RegisterManglingScheme(), or a nullptr if none did.
+ static Language *FindPluginForManglingScheme(Mangled::ManglingScheme scheme);
+
static llvm::Expected<lldb::LanguageType>
GetExceptionLanguageForLanguage(llvm::StringRef lang_name);
// return false from callback to stop iterating
@@ -324,6 +331,56 @@ class Language : public PluginInterface {
return false;
}
+ /// How to demangle a name in DemangleSymbol().
+ struct DemangleOptions {
+ /// Whether to compute the \c DemangledNameInfo too.
+ bool want_info = false;
+ };
+
+ /// A demangled name and, if requested, where its parts begin and end.
+ struct DemangledName {
+ /// Empty if the name couldn't be demangled.
+ std::string name;
+ /// Only computed if DemangleOptions::want_info is true.
+ std::optional<DemangledNameInfo> info;
+ };
+
+ /// Demangle \p mangled, a name mangled with \p scheme, a scheme that this
+ /// language registered with Mangled::RegisterManglingScheme().
+ virtual DemangledName DemangleSymbol(ConstString mangled,
+ Mangled::ManglingScheme scheme,
+ const DemangleOptions &options) const {
+ return {};
+ }
+
+ /// Parses mangled function names for the symbol table's name indexes.
+ ///
+ /// A symbol table uses one parser for all of its symbols, so a parser can
+ /// reuse state, such as a demangler, between names.
+ class MangledNameParser {
+ public:
+ virtual ~MangledNameParser() = default;
+
+ /// Parse \p mangled, a function name mangled with the scheme that this
+ /// parser was created for.
+ ///
+ /// \return
+ /// The parts to index the symbol by. If their basename is empty, the
+ /// symbol is only indexed by its mangled name. std::nullopt if this
+ /// isn't a function name the parser understands, so that the symbol is
+ /// indexed by its demangled name instead.
+ virtual std::optional<Mangled::FunctionNameParts>
+ Parse(const Mangled &mangled) = 0;
+ };
+
+ /// Returns a parser for the names mangled with \p scheme, a scheme that this
+ /// language registered, or a nullptr if the symbol table should index them
+ /// by their demangled names.
+ virtual std::unique_ptr<MangledNameParser>
+ CreateMangledNameParser(Mangled::ManglingScheme scheme) const {
+ return nullptr;
+ }
+
/// An individual data formatter may apply to several types and cross language
/// boundaries. Each of those languages may want to customize the display of
/// values of said types by appending proper prefix/suffix information in
diff --git a/lldb/source/Core/Mangled.cpp b/lldb/source/Core/Mangled.cpp
index 06aa1a97309c1b..b5d3e7bb775033 100644
--- a/lldb/source/Core/Mangled.cpp
+++ b/lldb/source/Core/Mangled.cpp
@@ -10,63 +10,182 @@
#include "lldb/Core/DataFileCache.h"
#include "lldb/Core/DemangledNameInfo.h"
-#include "lldb/Core/RichManglingContext.h"
#include "lldb/Target/Language.h"
#include "lldb/Utility/ConstString.h"
#include "lldb/Utility/DataEncoder.h"
-#include "lldb/Utility/LLDBLog.h"
-#include "lldb/Utility/Log.h"
#include "lldb/Utility/RegularExpression.h"
#include "lldb/Utility/Stream.h"
#include "lldb/lldb-enumerations.h"
-#include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringRef.h"
-#include "llvm/Demangle/Demangle.h"
#include "llvm/Support/Compiler.h"
+#include <array>
+#include <atomic>
#include <mutex>
#include <optional>
#include <string>
-#include <string_view>
#include <utility>
+#include <vector>
-#include <cstdlib>
-#include <cstring>
using namespace lldb_private;
#pragma mark Mangled
-bool Mangled::IsMangledName(llvm::StringRef name) {
- return Mangled::GetManglingScheme(name) != Mangled::eManglingSchemeNone;
+namespace {
+/// A mangling scheme that a language plugin registered.
+struct RegisteredManglingScheme {
+ std::vector<std::string> prefixes;
+ /// If set, a name that starts with one of the prefixes must also match.
+ Mangled::ManglingSchemeMatcher matches = nullptr;
+ lldb::LanguageType language = lldb::eLanguageTypeUnknown;
+ /// Schemes are never removed, so that GetManglingScheme() can read them
+ /// without a lock. Unregistered schemes are only marked inactive. No other
+ /// data is published through this flag, so it needs no memory ordering.
+ std::atomic<bool> active = false;
+};
+
+/// The mangling schemes that language plugins registered.
+class ManglingSchemeRegistry {
+public:
+ Mangled::ManglingScheme Register(llvm::ArrayRef<llvm::StringRef> prefixes,
+ lldb::LanguageType language,
+ Mangled::ManglingSchemeMatcher matches);
+ void Unregister(Mangled::ManglingScheme scheme);
+ lldb::LanguageType GetLanguage(Mangled::ManglingScheme scheme) const;
+ Mangled::ManglingScheme Find(llvm::StringRef name) const;
+
+private:
+ /// Serializes registrations.
+ std::mutex m_mutex;
+ std::array<RegisteredManglingScheme,
+ Mangled::kNumManglingSchemes -
+ Mangled::eFirstRegisteredManglingScheme>
+ m_schemes;
+ /// The number of initialized entries in m_schemes. Incrementing it publishes
+ /// a new entry to readers.
+ std::atomic<size_t> m_num_schemes = 0;
+};
+} // namespace
+
+static ManglingSchemeRegistry &GetManglingSchemeRegistry() {
+ // NOTE: INTENTIONAL LEAK due to global destructor chain.
+ static ManglingSchemeRegistry *g_registry = new ManglingSchemeRegistry();
+ return *g_registry;
}
-Mangled::ManglingScheme Mangled::GetManglingScheme(llvm::StringRef name) {
- if (name.empty())
- return Mangled::eManglingSchemeNone;
+static Mangled::ManglingScheme SchemeForIndex(size_t index) {
+ return static_cast<Mangled::ManglingScheme>(
+ Mangled::eFirstRegisteredManglingScheme + index);
+}
- if (name.starts_with("?"))
- return Mangled::eManglingSchemeMSVC;
+/// Returns true if a name could start with both one of \p prefixes and a
+/// prefix of one of the active \p schemes.
+[[maybe_unused]] static bool
+OverlapsActiveScheme(llvm::ArrayRef<llvm::StringRef> prefixes,
+ llvm::ArrayRef<RegisteredManglingScheme> schemes) {
+ for (const RegisteredManglingScheme &scheme : schemes) {
+ if (!scheme.active.load(std::memory_order_relaxed))
+ continue;
+ for (llvm::StringRef prefix : prefixes)
+ for (llvm::StringRef other : scheme.prefixes)
+ if (prefix.starts_with(other) || other.starts_with(prefix))
+ return true;
+ }
+ return false;
+}
+
+Mangled::ManglingScheme
+ManglingSchemeRegistry::Register(llvm::ArrayRef<llvm::StringRef> prefixes,
+ lldb::LanguageType language,
+ Mangled::ManglingSchemeMatcher matches) {
+ std::lock_guard<std::mutex> guard(m_mutex);
+ const size_t num_schemes = m_num_schemes.load(std::memory_order_relaxed);
+ size_t index = 0;
+ for (; index < num_schemes; ++index) {
+ const RegisteredManglingScheme &scheme = m_schemes[index];
+ if (scheme.language == language && scheme.matches == matches &&
+ llvm::equal(scheme.prefixes, prefixes))
+ break;
+ }
- if (name.starts_with("_R"))
- return Mangled::eManglingSchemeRustV0;
+ if (index == num_schemes) {
+ assert(index < m_schemes.size() && "too many mangling schemes");
+ if (index >= m_schemes.size())
+ return Mangled::eManglingSchemeNone;
+ assert(!OverlapsActiveScheme(
+ prefixes, llvm::ArrayRef(m_schemes.data(), num_schemes)) &&
+ "mangling scheme prefixes overlap");
+ m_schemes[index].prefixes.assign(prefixes.begin(), prefixes.end());
+ m_schemes[index].matches = matches;
+ m_schemes[index].language = language;
+ m_num_schemes.store(num_schemes + 1, std::memory_order_release);
+ }
+ m_schemes[index].active.store(true, std::memory_order_relaxed);
+ return SchemeForIndex(index);
+}
- if (name.starts_with("_D")) {
- // A dlang mangled name begins with `_D`, followed by a numeric length. One
- // known exception is the symbol `_Dmain`.
- // See `SymbolName` and `LName` in
- // https://dlang.org/spec/abi.html#name_mangling
- llvm::StringRef buf = name.drop_front(2);
- if (!buf.empty() && (llvm::isDigit(buf.front()) || name == "_Dmain"))
- return Mangled::eManglingSchemeD;
+void ManglingSchemeRegistry::Unregister(Mangled::ManglingScheme scheme) {
+ if (scheme < Mangled::eFirstRegisteredManglingScheme)
+ return;
+ size_t index = scheme - Mangled::eFirstRegisteredManglingScheme;
+ if (index < m_num_schemes.load(std::memory_order_acquire))
+ m_schemes[index].active.store(false, std::memory_order_relaxed);
+}
+
+lldb::LanguageType
+ManglingSchemeRegistry::GetLanguage(Mangled::ManglingScheme scheme) const {
+ if (scheme < Mangled::eFirstRegisteredManglingScheme)
+ return lldb::eLanguageTypeUnknown;
+ size_t index = scheme - Mangled::eFirstRegisteredManglingScheme;
+ if (index >= m_num_schemes.load(std::memory_order_acquire))
+ return lldb::eLanguageTypeUnknown;
+ return m_schemes[index].language;
+}
+
+Mangled::ManglingScheme
+ManglingSchemeRegistry::Find(llvm::StringRef name) const {
+ const size_t num_schemes = m_num_schemes.load(std::memory_order_acquire);
+ for (size_t index = 0; index < num_schemes; ++index) {
+ const RegisteredManglingScheme &scheme = m_schemes[index];
+ if (!scheme.active.load(std::memory_order_relaxed))
+ continue;
+ for (llvm::StringRef prefix : scheme.prefixes) {
+ if (!name.starts_with(prefix))
+ continue;
+ // Prefixes don't overlap, so no other scheme can match the name.
+ if (scheme.matches && !scheme.matches(name))
+ return Mangled::eManglingSchemeNone;
+ return SchemeForIndex(index);
+ }
}
+ return Mangled::eManglingSchemeNone;
+}
+
+Mangled::ManglingScheme
+Mangled::RegisterManglingScheme(llvm::ArrayRef<llvm::StringRef> prefixes,
+ lldb::LanguageType language,
+ ManglingSchemeMatcher matches) {
+ return GetManglingSchemeRegistry().Register(prefixes, language, matches);
+}
+
+void Mangled::UnregisterManglingScheme(ManglingScheme scheme) {
+ GetManglingSchemeRegistry().Unregister(scheme);
+}
- if (name.starts_with("_Z"))
- return Mangled::eManglingSchemeItanium;
+lldb::LanguageType
+Mangled::GetLanguageForManglingScheme(ManglingScheme scheme) {
+ return GetManglingSchemeRegistry().GetLanguage(scheme);
+}
- // ___Z is a clang extension of block invocations
- if (name.starts_with("___Z"))
- return Mangled::eManglingSchemeItanium;
+bool Mangled::IsMangledName(llvm::StringRef name) {
+ return Mangled::GetManglingScheme(name) != Mangled::eManglingSchemeNone;
+}
+
+Mangled::ManglingScheme Mangled::GetManglingScheme(llvm::StringRef name) {
+ if (name.empty())
+ return Mangled::eManglingSchemeNone;
// Swift's older style of mangling used "_T" as a mangling prefix. This can
// lead to false positives with other symbols that just so happen to start
@@ -88,7 +207,7 @@ Mangled::ManglingScheme Mangled::GetManglingScheme(llvm::StringRef name) {
name.starts_with("@__swiftmacro_"))
return Mangled::eManglingSchemeSwift;
- return Mangled::eManglingSchemeNone;
+ return GetManglingSchemeRegistry().Find(name);
}
Mangled::Mangled(ConstString s) : m_mangled(), m_demangled() {
@@ -136,148 +255,6 @@ void Mangled::SetValue(ConstString name) {
}
}
-// Local helpers for different demangling implementations.
-static char *GetMSVCDemangledStr(llvm::StringRef M) {
- char *demangled_cstr = llvm::microsoftDemangle(
- M, nullptr, nullptr,
- llvm::MSDemangleFlags(
- llvm::MSDF_NoAccessSpecifier | llvm::MSDF_NoCallingConvention |
- llvm::MSDF_NoMemberType | llvm::MSDF_NoVariableType));
-
- if (Log *log = GetLog(LLDBLog::Demangle)) {
- if (demangled_cstr && demangled_cstr[0])
- LLDB_LOGF(log, "demangled msvc: %s -> \"%s\"", M.data(), demangled_cstr);
- else
- LLDB_LOGF(log, "demangled msvc: %s -> error", M.data());
- }
-
- return demangled_cstr;
-}
-
-static std::pair<char *, DemangledNameInfo>
-GetItaniumDemangledStr(const char *M) {
- char *demangled_cstr = nullptr;
-
- DemangledNameInfo info;
- llvm::ItaniumPartialDemangler ipd;
- bool err = ipd.partialDemangle(M);
- if (!err) {
- // Default buffer and size (OutputBuffer will realloc in case it's too
- // small).
- size_t demangled_size = 80;
- demangled_cstr = static_cast<char *>(std::malloc(80));
-
- TrackingOutputBuffer OB(demangled_cstr, demangled_size);
- demangled_cstr = ipd.finishDemangle(&OB);
- info = std::move(OB.NameInfo);
-
- assert(demangled_cstr &&
- "finishDemangle must always succeed if partialDemangle ...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/232929
More information about the lldb-commits
mailing list