[Lldb-commits] [clang] [lldb] [llvm] [WIP][lldb][Expression] More reliable function call resolution (PR #114529)

Michael Buch via lldb-commits lldb-commits at lists.llvm.org
Fri Nov 1 04:09:21 PDT 2024


https://github.com/Michael137 created https://github.com/llvm/llvm-project/pull/114529

Naive implementation of all the parts of following RFC:
https://discourse.llvm.org/t/rfc-lldb-handling-abi-tagged-constructors-destructors-in-expression-evaluator/82816

Main changes:
1. Instead of relying on linkage names to resolve function symbols, encode the exact function DIE and module in the `AsmLabelAttr`. Currently we just put the `Module` pointer value into the label, which may be not sufficient if those could disappear under the expression evaluator's feet.
2. Teach the LLDB symbol resolve about (1)
3. Introduce new Clang attribute to allow specifying multiple `asm` labels for ctors/dtors (one for each variant)
4. Attach the new attribute in (3), where the mangled names use the format from (1). To determine which variant a DIE corresponds to we add a new API to the `ItaniumPartialDemangler` (though could be made into a DWARF attribute for quicker determination).

>From 6b3b5ef0e50dd2e575922c47696ccffcd71460ea Mon Sep 17 00:00:00 2001
From: Michael Buch <michaelbuch12 at gmail.com>
Date: Sun, 20 Oct 2024 11:35:15 +0100
Subject: [PATCH] [WIP][lldb][Expression] More reliable function call
 resolution

Implements all the parts of following RFC:
https://discourse.llvm.org/t/rfc-lldb-handling-abi-tagged-constructors-destructors-in-expression-evaluator/82816

Main changes:
1. Instead of relying on linkage names to resolve function symbols,
   encode the exact function DIE and module in the `AsmLabelAttr`
2. Teach the LLDB symbol resolve about (1)
3. Introduce new Clang attribute to allow specifying multiple `asm`
   labels for ctors/dtors (one for each variant)
4. Attach the new attribute in (3), where the mangled names use the
   format from (1). To determine which variant a DIE corresponds to we
add a new API to the `ItaniumPartialDemangler` (though could be made
into a DWARF attribute for quicker determination).
---
 clang/include/clang/Basic/Attr.td             |  8 ++
 clang/include/clang/Basic/AttrDocs.td         |  5 ++
 clang/lib/AST/Mangle.cpp                      | 65 ++++++++++++++-
 clang/lib/Sema/SemaDeclAttr.cpp               | 22 +++++
 lldb/source/Expression/IRExecutionUnit.cpp    | 36 +++++++++
 .../SymbolFile/DWARF/DWARFASTParserClang.cpp  | 80 +++++++++++++++----
 .../TypeSystem/Clang/TypeSystemClang.cpp      | 15 +++-
 .../TypeSystem/Clang/TypeSystemClang.h        |  3 +-
 lldb/test/Shell/Expr/TestAbiTagCtorsDtors.cpp | 28 +++++++
 llvm/include/llvm/Demangle/Demangle.h         |  3 +
 llvm/include/llvm/Demangle/ItaniumDemangle.h  |  2 +
 llvm/lib/Demangle/ItaniumDemangle.cpp         | 18 +++--
 12 files changed, 260 insertions(+), 25 deletions(-)
 create mode 100644 lldb/test/Shell/Expr/TestAbiTagCtorsDtors.cpp

diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index 70fad60d4edbb5..407eece2a728a2 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -784,6 +784,14 @@ def AbiTag : Attr {
   let Documentation = [AbiTagsDocs];
 }
 
+def StructorMangledNames : Attr {
+  let Spellings = [Clang<"structor_names">];
+  let Args = [VariadicStringArgument<"MangledNames">];
+  let Subjects = SubjectList<[Function], ErrorDiag>;
+  let Documentation = [StructorMangledNamesDocs];
+}
+
+
 def AddressSpace : TypeAttr {
   let Spellings = [Clang<"address_space">];
   let Args = [IntArgument<"AddressSpace">];
diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td
index 546e5100b79dd9..2b886aecd193de 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -3568,6 +3568,11 @@ manipulating bits of the enumerator when issuing warnings.
   }];
 }
 
+def StructorMangledNamesDocs : Documentation {
+  let Category = DocCatDecl;
+  let Content = [{ TODO }];
+}
+
 def AsmLabelDocs : Documentation {
   let Category = DocCatDecl;
   let Content = [{
diff --git a/clang/lib/AST/Mangle.cpp b/clang/lib/AST/Mangle.cpp
index 4875e8537b3c11..c784fca053062c 100644
--- a/clang/lib/AST/Mangle.cpp
+++ b/clang/lib/AST/Mangle.cpp
@@ -9,19 +9,20 @@
 // Implements generic name mangling support for blocks and Objective-C.
 //
 //===----------------------------------------------------------------------===//
-#include "clang/AST/Attr.h"
+#include "clang/AST/Mangle.h"
 #include "clang/AST/ASTContext.h"
+#include "clang/AST/Attr.h"
 #include "clang/AST/Decl.h"
 #include "clang/AST/DeclCXX.h"
 #include "clang/AST/DeclObjC.h"
 #include "clang/AST/DeclTemplate.h"
 #include "clang/AST/ExprCXX.h"
-#include "clang/AST/Mangle.h"
 #include "clang/AST/VTableBuilder.h"
 #include "clang/Basic/ABI.h"
 #include "clang/Basic/SourceManager.h"
 #include "clang/Basic/TargetInfo.h"
 #include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/StringSwitch.h"
 #include "llvm/IR/DataLayout.h"
 #include "llvm/IR/Mangler.h"
 #include "llvm/Support/ErrorHandling.h"
@@ -126,7 +127,7 @@ bool MangleContext::shouldMangleDeclName(const NamedDecl *D) {
 
   // Any decl can be declared with __asm("foo") on it, and this takes precedence
   // over all other naming in the .o file.
-  if (D->hasAttr<AsmLabelAttr>())
+  if (D->hasAttr<AsmLabelAttr>() || D->hasAttr<StructorMangledNamesAttr>())
     return true;
 
   // Declarations that don't have identifier names always need to be mangled.
@@ -140,6 +141,64 @@ void MangleContext::mangleName(GlobalDecl GD, raw_ostream &Out) {
   const ASTContext &ASTContext = getASTContext();
   const NamedDecl *D = cast<NamedDecl>(GD.getDecl());
 
+  if (const StructorMangledNamesAttr *SMA =
+          D->getAttr<StructorMangledNamesAttr>()) {
+    CXXConstructorDecl const *Ctor = dyn_cast<CXXConstructorDecl>(D);
+    CXXDestructorDecl const *Dtor = dyn_cast<CXXDestructorDecl>(D);
+    assert(Ctor || Dtor);
+    enum CtorDtor {
+      None = -1,
+      Deleting = 0,
+      Base,
+      Complete,
+      Allocating
+    } CtorDtorVariant = None;
+
+    if (Dtor) {
+      switch (GD.getDtorType()) {
+      case Dtor_Complete:
+        CtorDtorVariant = Complete;
+        break;
+      case Dtor_Base:
+        CtorDtorVariant = Base;
+        break;
+      case Dtor_Deleting:
+      case Dtor_Comdat:
+        llvm_unreachable("");
+      }
+    } else if (Ctor) {
+      switch (GD.getCtorType()) {
+      case Ctor_Complete:
+        CtorDtorVariant = Complete;
+        break;
+      case Ctor_Base:
+        CtorDtorVariant = Base;
+        break;
+      case Ctor_DefaultClosure:
+      case Ctor_CopyingClosure:
+      case Ctor_Comdat:
+        llvm_unreachable("");
+      }
+    }
+
+    llvm::DenseMap<CtorDtor, llvm::StringRef> names;
+    for (auto name : SMA->mangledNames()) {
+      auto [structor_variant, mangled_name] = name.split(':');
+      auto variant = llvm::StringSwitch<CtorDtor>(structor_variant)
+                         .Case("0", CtorDtor::Deleting)
+                         .Case("1", CtorDtor::Base)
+                         .Case("2", CtorDtor::Complete)
+                         .Case("3", CtorDtor::Complete)
+                         .Default(CtorDtor::None);
+      names[variant] = mangled_name;
+    }
+
+    assert(CtorDtorVariant != CtorDtor::None);
+
+    Out << names[CtorDtorVariant];
+    return;
+  }
+
   // Any decl can be declared with __asm("foo") on it, and this takes precedence
   // over all other naming in the .o file.
   if (const AsmLabelAttr *ALA = D->getAttr<AsmLabelAttr>()) {
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index 14cc51cf89665a..fdf66be8ac3987 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -5539,6 +5539,25 @@ static void handleMSConstexprAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
   D->addAttr(::new (S.Context) MSConstexprAttr(S.Context, AL));
 }
 
+static void handleStructorNamesAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
+  SmallVector<StringRef, 4> Tags;
+  for (unsigned I = 0, E = AL.getNumArgs(); I != E; ++I) {
+    StringRef Tag;
+    if (!S.checkStringLiteralArgumentAttr(AL, I, Tag))
+      return;
+    Tags.push_back(Tag);
+  }
+
+  if (!AL.checkAtLeastNumArgs(S, 1))
+    return;
+
+  // Store tags without duplicates.
+  Tags.erase(std::unique(Tags.begin(), Tags.end()), Tags.end());
+
+  D->addAttr(::new (S.Context) StructorMangledNamesAttr(
+      S.Context, AL, Tags.data(), Tags.size()));
+}
+
 static void handleAbiTagAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
   SmallVector<StringRef, 4> Tags;
   for (unsigned I = 0, E = AL.getNumArgs(); I != E; ++I) {
@@ -6983,6 +7002,9 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL,
     S.HLSL().handleParamModifierAttr(D, AL);
     break;
 
+  case ParsedAttr::AT_StructorMangledNames:
+    handleStructorNamesAttr(S, D, AL);
+    break;
   case ParsedAttr::AT_AbiTag:
     handleAbiTagAttr(S, D, AL);
     break;
diff --git a/lldb/source/Expression/IRExecutionUnit.cpp b/lldb/source/Expression/IRExecutionUnit.cpp
index 15ca2ddbbae046..b11085d921c419 100644
--- a/lldb/source/Expression/IRExecutionUnit.cpp
+++ b/lldb/source/Expression/IRExecutionUnit.cpp
@@ -16,6 +16,8 @@
 #include "llvm/Support/SourceMgr.h"
 #include "llvm/Support/raw_ostream.h"
 
+#include "Plugins/SymbolFile/DWARF/DWARFBaseDIE.h"
+#include "Plugins/SymbolFile/DWARF/SymbolFileDWARF.h"
 #include "lldb/Core/Debugger.h"
 #include "lldb/Core/Disassembler.h"
 #include "lldb/Core/Module.h"
@@ -37,6 +39,8 @@
 #include "lldb/Utility/LLDBLog.h"
 #include "lldb/Utility/Log.h"
 
+#include <cstddef>
+#include <cstdint>
 #include <optional>
 
 using namespace lldb_private;
@@ -781,6 +785,38 @@ IRExecutionUnit::FindInSymbols(const std::vector<ConstString> &names,
   function_options.include_inlines = false;
 
   for (const ConstString &name : names) {
+    auto ref = name.GetStringRef();
+    if (ref.consume_front("$__lldb_func_")) {
+      uintptr_t module_ptr;
+      if (ref.consumeInteger(0, module_ptr))
+        return LLDB_INVALID_ADDRESS;
+
+      auto *mod = (lldb_private::Module *)module_ptr;
+      auto *sym = mod->GetSymbolFile();
+      assert(mod && sym);
+
+      if (!ref.consume_front(":"))
+        return LLDB_INVALID_ADDRESS;
+
+      lldb::user_id_t die_id;
+      if (ref.consumeInteger(10, die_id))
+        return LLDB_INVALID_ADDRESS;
+
+      auto *dwarf = llvm::dyn_cast<plugin::dwarf::SymbolFileDWARF>(sym);
+      if (!dwarf)
+        return LLDB_INVALID_ADDRESS;
+
+      auto die = dwarf->GetDIE(die_id);
+      Module::LookupInfo lookup_info(
+          ConstString(die.GetMangledName()),
+          lldb::FunctionNameType::eFunctionNameTypeAny,
+          lldb::LanguageType::eLanguageTypeC_plus_plus);
+      SymbolContextList sc_list;
+      dwarf->FindFunctions(lookup_info, {}, false, sc_list);
+      if (auto load_addr = resolver.Resolve(sc_list))
+        return *load_addr;
+    }
+
     if (sc.module_sp) {
       SymbolContextList sc_list;
       sc.module_sp->FindFunctions(name, CompilerDeclContext(),
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp
index 70540fe7fada68..792c58a12b464f 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp
+++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp
@@ -45,6 +45,7 @@
 #include "clang/AST/Type.h"
 #include "llvm/ADT/StringExtras.h"
 #include "llvm/Demangle/Demangle.h"
+#include "llvm/Support/FormatVariadic.h"
 
 #include <map>
 #include <memory>
@@ -1040,6 +1041,62 @@ bool DWARFASTParserClang::ParseObjCMethod(
   return true;
 }
 
+static bool IsStructorDIE(DWARFDIE const &die, DWARFDIE const &parent_die) {
+  llvm::StringRef name = die.GetName();
+  llvm::StringRef parent_name = parent_die.GetName();
+
+  name.consume_front("~");
+  parent_name = parent_name.substr(0, parent_name.find('<'));
+
+  return name == parent_name;
+}
+
+/// Given a DIE with an external definition (and thus no linkage name)
+/// find the definitions by lookup into the DWARF name index.
+/// We check the DW_AT_specification for each DIE in the index with
+/// the same name as the specified 'die' until we find one that references
+/// 'die'. Then return that linkage name. If no such DIE is found in the index,
+/// returns nullptr.
+static std::vector<std::string> FindStructorNames(DWARFDIE die) {
+  auto *dwarf = die.GetDWARF();
+  assert(dwarf);
+
+  ConstString func_name(die.GetName());
+  assert(func_name);
+
+  SymbolContextList sc_list;
+  Module::LookupInfo lookup_info(func_name,
+                                 FunctionNameType::eFunctionNameTypeMethod |
+                                     FunctionNameType::eFunctionNameTypeFull,
+                                 LanguageType::eLanguageTypeUnknown);
+  dwarf->FindFunctions(lookup_info, {}, true, sc_list);
+
+  std::vector<std::string> structor_names;
+  for (auto const &sc : sc_list.SymbolContexts()) {
+    if (auto *func = sc.function) {
+      auto func_die = dwarf->GetDIE(func->GetID());
+      if (!func_die.IsValid())
+        continue;
+
+      auto spec_die =
+          func_die.GetAttributeValueAsReferenceDIE(DW_AT_specification);
+      if (spec_die.IsValid() && spec_die == die) {
+        llvm::ItaniumPartialDemangler D;
+        const bool success = !D.partialDemangle(func_die.GetMangledName());
+        assert(success);
+        const auto maybe_structor_kind = D.getCtorDtorVariant();
+        assert(maybe_structor_kind);
+
+        structor_names.push_back(
+            llvm::formatv("{0}:$__lldb_func_{1}:{2}", *maybe_structor_kind,
+                          func_die.GetModule().get(), die.GetID()));
+      }
+    }
+  }
+
+  return structor_names;
+}
+
 std::pair<bool, TypeSP> DWARFASTParserClang::ParseCXXMethod(
     const DWARFDIE &die, CompilerType clang_type,
     const ParsedDWARFTypeAttributes &attrs, const DWARFDIE &decl_ctx_die,
@@ -1140,11 +1197,15 @@ std::pair<bool, TypeSP> DWARFASTParserClang::ParseCXXMethod(
   const auto accessibility =
       attrs.accessibility == eAccessNone ? eAccessPublic : attrs.accessibility;
 
+  std::vector<std::string> structor_names;
+  if (IsStructorDIE(die, decl_ctx_die))
+    structor_names = FindStructorNames(die);
+
   clang::CXXMethodDecl *cxx_method_decl = m_ast.AddMethodToCXXRecordType(
       class_opaque_type.GetOpaqueQualType(), attrs.name.GetCString(),
       attrs.mangled_name, clang_type, accessibility, attrs.is_virtual,
       is_static, attrs.is_inline, attrs.is_explicit, is_attr_used,
-      attrs.is_artificial);
+      attrs.is_artificial, structor_names);
 
   if (cxx_method_decl) {
     LinkDeclContextToDIE(cxx_method_decl, die);
@@ -1330,19 +1391,10 @@ DWARFASTParserClang::ParseSubroutine(const DWARFDIE &die,
         lldbassert(function_decl);
 
         if (function_decl) {
-          // Attach an asm(<mangled_name>) label to the FunctionDecl.
-          // This ensures that clang::CodeGen emits function calls
-          // using symbols that are mangled according to the DW_AT_linkage_name.
-          // If we didn't do this, the external symbols wouldn't exactly
-          // match the mangled name LLDB knows about and the IRExecutionUnit
-          // would have to fall back to searching object files for
-          // approximately matching function names. The motivating
-          // example is generating calls to ABI-tagged template functions.
-          // This is done separately for member functions in
-          // AddMethodToCXXRecordType.
-          if (attrs.mangled_name)
-            function_decl->addAttr(clang::AsmLabelAttr::CreateImplicit(
-                m_ast.getASTContext(), attrs.mangled_name, /*literal=*/false));
+          auto ident = llvm::formatv("$__lldb_func_{0}:{1}",
+                                     die.GetModule().get(), die.GetID());
+          function_decl->addAttr(clang::AsmLabelAttr::CreateImplicit(
+              m_ast.getASTContext(), ident.str(), /*literal=*/false));
 
           LinkDeclContextToDIE(function_decl, die);
 
diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
index b0f49ebf2d2cbb..549b810490a590 100644
--- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
+++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
@@ -10,6 +10,7 @@
 
 #include "clang/AST/DeclBase.h"
 #include "clang/AST/ExprCXX.h"
+#include "llvm/ADT/SmallVector.h"
 #include "llvm/Support/Casting.h"
 #include "llvm/Support/FormatAdapters.h"
 #include "llvm/Support/FormatVariadic.h"
@@ -7735,7 +7736,8 @@ clang::CXXMethodDecl *TypeSystemClang::AddMethodToCXXRecordType(
     lldb::opaque_compiler_type_t type, llvm::StringRef name,
     const char *mangled_name, const CompilerType &method_clang_type,
     lldb::AccessType access, bool is_virtual, bool is_static, bool is_inline,
-    bool is_explicit, bool is_attr_used, bool is_artificial) {
+    bool is_explicit, bool is_attr_used, bool is_artificial,
+    std::vector<std::string> const &structor_names) {
   if (!type || !method_clang_type.IsValid() || name.empty())
     return nullptr;
 
@@ -7788,6 +7790,11 @@ clang::CXXMethodDecl *TypeSystemClang::AddMethodToCXXRecordType(
     cxx_dtor_decl->setImplicit(is_artificial);
     cxx_dtor_decl->setInlineSpecified(is_inline);
     cxx_dtor_decl->setConstexprKind(ConstexprSpecKind::Unspecified);
+    if (!structor_names.empty()) {
+      auto names = llvm::to_vector_of<llvm::StringRef>(structor_names);
+      cxx_dtor_decl->addAttr(clang::StructorMangledNamesAttr::CreateImplicit(
+          getASTContext(), names.data(), names.size()));
+    }
     cxx_method_decl = cxx_dtor_decl;
   } else if (decl_name == cxx_record_decl->getDeclName()) {
     cxx_ctor_decl = clang::CXXConstructorDecl::CreateDeserialized(
@@ -7802,6 +7809,12 @@ clang::CXXMethodDecl *TypeSystemClang::AddMethodToCXXRecordType(
     cxx_ctor_decl->setConstexprKind(ConstexprSpecKind::Unspecified);
     cxx_ctor_decl->setNumCtorInitializers(0);
     cxx_ctor_decl->setExplicitSpecifier(explicit_spec);
+    if (!structor_names.empty()) {
+      auto names = llvm::to_vector_of<llvm::StringRef>(structor_names);
+      cxx_ctor_decl->addAttr(clang::StructorMangledNamesAttr::CreateImplicit(
+          getASTContext(), names.data(), names.size()));
+    }
+
     cxx_method_decl = cxx_ctor_decl;
   } else {
     clang::StorageClass SC = is_static ? clang::SC_Static : clang::SC_None;
diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h
index e39aedec7e3902..f5ab4dde8ed58c 100644
--- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h
+++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h
@@ -980,7 +980,8 @@ class TypeSystemClang : public TypeSystem {
       lldb::opaque_compiler_type_t type, llvm::StringRef name,
       const char *mangled_name, const CompilerType &method_type,
       lldb::AccessType access, bool is_virtual, bool is_static, bool is_inline,
-      bool is_explicit, bool is_attr_used, bool is_artificial);
+      bool is_explicit, bool is_attr_used, bool is_artificial,
+      std::vector<std::string> const &structor_names = {});
 
   void AddMethodOverridesForCXXRecordType(lldb::opaque_compiler_type_t type);
 
diff --git a/lldb/test/Shell/Expr/TestAbiTagCtorsDtors.cpp b/lldb/test/Shell/Expr/TestAbiTagCtorsDtors.cpp
new file mode 100644
index 00000000000000..49de90ee64dea6
--- /dev/null
+++ b/lldb/test/Shell/Expr/TestAbiTagCtorsDtors.cpp
@@ -0,0 +1,28 @@
+// Tests that we can call abi-tagged constructors/destructors.
+
+// RUN: %build %s -o %t
+// RUN: %lldb %t -o run \
+// RUN:          -o "expression sinkTagged(getTagged())" \
+// RUN:          -o "expression Tagged()" \
+// RUN:          -o exit | FileCheck %s
+
+// CHECK: expression sinkTagged(getTagged())
+// CHECK: expression Tagged()
+
+struct Tagged {
+  [[gnu::abi_tag("Test", "CtorTag")]] [[gnu::abi_tag("v1")]] Tagged() = default;
+  [[gnu::abi_tag("Test", "DtorTag")]] [[gnu::abi_tag("v1")]] ~Tagged() {}
+
+  int mem = 15;
+};
+
+Tagged getTagged() { return Tagged(); }
+void sinkTagged(Tagged t) {}
+
+int main() {
+  Tagged t;
+
+  // TODO: is there a more reliable way of triggering destructor call?
+  sinkTagged(getTagged());
+  __builtin_debugtrap();
+}
diff --git a/llvm/include/llvm/Demangle/Demangle.h b/llvm/include/llvm/Demangle/Demangle.h
index fe129603c0785d..103b60d6c343ce 100644
--- a/llvm/include/llvm/Demangle/Demangle.h
+++ b/llvm/include/llvm/Demangle/Demangle.h
@@ -10,6 +10,7 @@
 #define LLVM_DEMANGLE_DEMANGLE_H
 
 #include <cstddef>
+#include <optional>
 #include <string>
 #include <string_view>
 
@@ -108,6 +109,8 @@ struct ItaniumPartialDemangler {
   /// the function is a non-static member function.
   bool hasFunctionQualifiers() const;
 
+  std::optional<int> getCtorDtorVariant() const;
+
   /// If this symbol describes a constructor or destructor.
   bool isCtorOrDtor() const;
 
diff --git a/llvm/include/llvm/Demangle/ItaniumDemangle.h b/llvm/include/llvm/Demangle/ItaniumDemangle.h
index 0af0224bc83fa8..39389132430ab0 100644
--- a/llvm/include/llvm/Demangle/ItaniumDemangle.h
+++ b/llvm/include/llvm/Demangle/ItaniumDemangle.h
@@ -1728,6 +1728,8 @@ class CtorDtorName final : public Node {
 
   template<typename Fn> void match(Fn F) const { F(Basename, IsDtor, Variant); }
 
+  int getVariant() const { return Variant; }
+
   void printLeft(OutputBuffer &OB) const override {
     if (IsDtor)
       OB += "~";
diff --git a/llvm/lib/Demangle/ItaniumDemangle.cpp b/llvm/lib/Demangle/ItaniumDemangle.cpp
index 5c21b06a1d0955..786985a141b022 100644
--- a/llvm/lib/Demangle/ItaniumDemangle.cpp
+++ b/llvm/lib/Demangle/ItaniumDemangle.cpp
@@ -20,6 +20,7 @@
 #include <cstring>
 #include <exception>
 #include <functional>
+#include <optional>
 #include <utility>
 
 using namespace llvm;
@@ -548,15 +549,16 @@ bool ItaniumPartialDemangler::hasFunctionQualifiers() const {
   return E->getCVQuals() != QualNone || E->getRefQual() != FrefQualNone;
 }
 
-bool ItaniumPartialDemangler::isCtorOrDtor() const {
+std::optional<int> ItaniumPartialDemangler::getCtorDtorVariant() const {
   const Node *N = static_cast<const Node *>(RootNode);
   while (N) {
     switch (N->getKind()) {
     default:
-      return false;
-    case Node::KCtorDtorName:
-      return true;
-
+      return std::nullopt;
+    case Node::KCtorDtorName: {
+      auto const *StructorNode = static_cast<const CtorDtorName *>(N);
+      return StructorNode->getVariant();
+    }
     case Node::KAbiTagAttr:
       N = static_cast<const AbiTagAttr *>(N)->Base;
       break;
@@ -577,7 +579,11 @@ bool ItaniumPartialDemangler::isCtorOrDtor() const {
       break;
     }
   }
-  return false;
+  return std::nullopt;
+}
+
+bool ItaniumPartialDemangler::isCtorOrDtor() const {
+  return getCtorDtorVariant().has_value();
 }
 
 bool ItaniumPartialDemangler::isFunction() const {



More information about the lldb-commits mailing list