https://github.com/Michael137 updated 
https://github.com/llvm/llvm-project/pull/149827

>From d55e41fa03d09b2ddfc9484c4a70a7d21ed9a994 Mon Sep 17 00:00:00 2001
From: Michael Buch <michaelbuc...@gmail.com>
Date: Mon, 18 Aug 2025 15:12:45 +0100
Subject: [PATCH 1/5] [llvm][DebugInfo] Support DW_AT_linkage_names that are
 different between declaration and definition

(cherry picked from commit 62641a7cc6b439c747be0a9ae91b9b266d67816e)
---
 llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp     |  7 +-
 .../structor-declaration-linkage-names.ll     | 68 +++++++++++++++++++
 2 files changed, 70 insertions(+), 5 deletions(-)
 create mode 100644 
llvm/test/DebugInfo/Generic/structor-declaration-linkage-names.ll

diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp 
b/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp
index b03fac2d22a52..4904ad03199c7 100644
--- a/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp
@@ -1403,11 +1403,8 @@ bool 
DwarfUnit::applySubprogramDefinitionAttributes(const DISubprogram *SP,
 
   // Add the linkage name if we have one and it isn't in the Decl.
   StringRef LinkageName = SP->getLinkageName();
-  assert(((LinkageName.empty() || DeclLinkageName.empty()) ||
-          LinkageName == DeclLinkageName) &&
-         "decl has a linkage name and it is different");
-  if (DeclLinkageName.empty() &&
-      // Always emit it for abstract subprograms.
+  // Always emit linkage name for abstract subprograms.
+  if (DeclLinkageName != LinkageName &&
       (DD->useAllLinkageNames() || DU->getAbstractScopeDIEs().lookup(SP)))
     addLinkageName(SPDie, LinkageName);
 
diff --git a/llvm/test/DebugInfo/Generic/structor-declaration-linkage-names.ll 
b/llvm/test/DebugInfo/Generic/structor-declaration-linkage-names.ll
new file mode 100644
index 0000000000000..9b1f2a5b2a186
--- /dev/null
+++ b/llvm/test/DebugInfo/Generic/structor-declaration-linkage-names.ll
@@ -0,0 +1,68 @@
+; RUN: %llc_dwarf < %s -filetype=obj | llvm-dwarfdump -debug-info - | 
FileCheck %s
+
+; Make sure we attach DW_AT_linkage_name on function declarations but only
+; attach it on definitions if the value is different than on the declaration.
+
+target triple = "arm64-apple-macosx"
+
+define void @_Z11SameLinkagev() !dbg !4 {
+entry:
+  ret void
+}
+
+; CHECK:     DW_AT_linkage_name ("_Z11SameLinkagev")
+; CHECK:     DW_AT_declaration (true)
+; CHECK-NOT: DW_AT_linkage_name ("_Z11SameLinkagev")
+
+define void @_Z11DiffLinkagev() !dbg !8 {
+entry:
+  ret void
+}
+
+; CHECK: DW_AT_linkage_name ("SomeName")
+; CHECK: DW_AT_declaration (true)
+; CHECK: DW_AT_linkage_name ("_Z11DiffLinkagev")
+
+define void @_Z15EmptyDefLinkagev() !dbg !10 {
+entry:
+  ret void
+}
+
+; CHECK:     DW_AT_linkage_name ("_Z15EmptyDefLinkagev")
+; CHECK:     DW_AT_declaration (true)
+; CHECK-NOT: DW_AT_linkage_name
+
+define void @_Z16EmptyDeclLinkagev() !dbg !12 {
+entry:
+  ret void
+}
+
+; CHECK: DW_AT_declaration (true)
+; CHECK: DW_AT_linkage_name ("_Z16EmptyDeclLinkagev")
+
+define void @_Z13EmptyLinkagesv() !dbg !14 {
+entry:
+  ret void
+}
+
+; CHECK-NOT: DW_AT_linkage_name
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!2, !3}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, 
producer: "clang", isOptimized: false, runtimeVersion: 0, emissionKind: 
FullDebug, splitDebugInlining: false, nameTableKind: Apple, sysroot: "/")
+!1 = !DIFile(filename: "foo.cpp", directory: "/tmp")
+!2 = !{i32 7, !"Dwarf Version", i32 5}
+!3 = !{i32 2, !"Debug Info Version", i32 3}
+!4 = distinct !DISubprogram(name: "SameLinkage", linkageName: 
"_Z11SameLinkagev", scope: !1, file: !1, line: 3, type: !5, scopeLine: 3, 
flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, declaration: !7)
+!5 = !DISubroutineType(types: !6)
+!6 = !{null}
+!7 = !DISubprogram(name: "SameLinkage", linkageName: "_Z11SameLinkagev", 
scope: !1, file: !1, line: 3, type: !5, scopeLine: 3, flags: DIFlagPrototyped, 
spFlags: 0)
+!8 = distinct !DISubprogram(name: "DiffLinkage", linkageName: 
"_Z11DiffLinkagev", scope: !1, file: !1, line: 5, type: !5, scopeLine: 5, 
flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, declaration: !9)
+!9 = !DISubprogram(name: "DiffLinkage", linkageName: "SomeName", scope: !1, 
file: !1, line: 3, type: !5, scopeLine: 3, flags: DIFlagPrototyped, spFlags: 0)
+!10 = distinct !DISubprogram(name: "EmptyDefLinkage", linkageName: "", scope: 
!1, file: !1, line: 5, type: !5, scopeLine: 5, flags: DIFlagPrototyped, 
spFlags: DISPFlagDefinition, unit: !0, declaration: !11)
+!11 = !DISubprogram(name: "EmptyDefLinkage", linkageName: 
"_Z15EmptyDefLinkagev", scope: !1, file: !1, line: 3, type: !5, scopeLine: 3, 
flags: DIFlagPrototyped, spFlags: 0)
+!12 = distinct !DISubprogram(name: "EmptyDeclLinkage", linkageName: 
"_Z16EmptyDeclLinkagev", scope: !1, file: !1, line: 5, type: !5, scopeLine: 5, 
flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, declaration: 
!13)
+!13 = !DISubprogram(name: "EmptyDeclLinkage", linkageName: "", scope: !1, 
file: !1, line: 3, type: !5, scopeLine: 3, flags: DIFlagPrototyped, spFlags: 0)
+!14 = distinct !DISubprogram(name: "EmptyLinkages", linkageName: "", scope: 
!1, file: !1, line: 5, type: !5, scopeLine: 5, flags: DIFlagPrototyped, 
spFlags: DISPFlagDefinition, unit: !0, declaration: !15)
+!15 = !DISubprogram(name: "EmptyLinkages", linkageName: "", scope: !1, file: 
!1, line: 3, type: !5, scopeLine: 3, flags: DIFlagPrototyped, spFlags: 0)

>From 11ee2ee8b27a3d7e4325b0f37d44f091f49db04e Mon Sep 17 00:00:00 2001
From: Michael Buch <michaelbuc...@gmail.com>
Date: Mon, 18 Aug 2025 15:14:40 +0100
Subject: [PATCH 2/5] [clang][DebugInfo] Emit unified (Itanium) mangled name to
 structor declarations

---
 clang/include/clang/Basic/ABI.h               | 10 ++-
 clang/include/clang/Basic/DebugOptions.def    |  6 ++
 clang/include/clang/Driver/Options.td         | 12 +++
 clang/lib/AST/ItaniumMangle.cpp               | 10 +++
 clang/lib/AST/MicrosoftMangle.cpp             |  2 +
 clang/lib/CodeGen/CGClass.cpp                 |  2 +
 clang/lib/CodeGen/CGDebugInfo.cpp             | 35 ++++++--
 clang/lib/CodeGen/CGDebugInfo.h               |  4 +
 clang/lib/CodeGen/ItaniumCXXABI.cpp           |  5 ++
 clang/lib/CodeGen/MicrosoftCXXABI.cpp         |  4 +
 clang/lib/Driver/ToolChains/Clang.cpp         |  4 +
 clang/test/CodeGenObjCXX/debug-info-cyclic.mm |  5 +-
 clang/test/DebugInfo/CXX/artificial-arg.cpp   |  3 +-
 .../CXX/debug-info-structor-linkage-names.cpp | 89 +++++++++++++++++++
 14 files changed, 178 insertions(+), 13 deletions(-)
 create mode 100644 
clang/test/DebugInfo/CXX/debug-info-structor-linkage-names.cpp

diff --git a/clang/include/clang/Basic/ABI.h b/clang/include/clang/Basic/ABI.h
index 231bad799a42c..8279529c316cf 100644
--- a/clang/include/clang/Basic/ABI.h
+++ b/clang/include/clang/Basic/ABI.h
@@ -27,14 +27,16 @@ enum CXXCtorType {
   Ctor_Comdat,         ///< The COMDAT used for ctors
   Ctor_CopyingClosure, ///< Copying closure variant of a ctor
   Ctor_DefaultClosure, ///< Default closure variant of a ctor
+  Ctor_Unified,        ///< GCC-style unified dtor
 };
 
 /// C++ destructor types.
 enum CXXDtorType {
-    Dtor_Deleting, ///< Deleting dtor
-    Dtor_Complete, ///< Complete object dtor
-    Dtor_Base,     ///< Base object dtor
-    Dtor_Comdat    ///< The COMDAT used for dtors
+  Dtor_Deleting, ///< Deleting dtor
+  Dtor_Complete, ///< Complete object dtor
+  Dtor_Base,     ///< Base object dtor
+  Dtor_Comdat,   ///< The COMDAT used for dtors
+  Dtor_Unified,  ///< GCC-style unified dtor
 };
 
 } // end namespace clang
diff --git a/clang/include/clang/Basic/DebugOptions.def 
b/clang/include/clang/Basic/DebugOptions.def
index c6e736e92744c..a768b12fa4e0d 100644
--- a/clang/include/clang/Basic/DebugOptions.def
+++ b/clang/include/clang/Basic/DebugOptions.def
@@ -125,6 +125,12 @@ DEBUGOPT(DebugNameTable, 2, 0, Compatible)
 /// Whether to use DWARF base address specifiers in .debug_ranges.
 DEBUGOPT(DebugRangesBaseAddress, 1, 0, Compatible)
 
+/// Whether to add linkage names to constructor/destructor declarations.
+/// This is an escape hatch for cases where attaching the additional linkage
+/// names would increase debug-info size (particularly the .debug_str section)
+/// too much.
+DEBUGOPT(DebugStructorDeclLinkageNames, 1, 0, Benign)
+
 /// Whether to embed source in DWARF debug line section.
 DEBUGOPT(EmbedSource, 1, 0, Compatible)
 
diff --git a/clang/include/clang/Driver/Options.td 
b/clang/include/clang/Driver/Options.td
index 9cfb1bbcac5c3..f701c4d9e485f 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -4767,6 +4767,18 @@ def gembed_source : Flag<["-"], "gembed-source">, 
Group<g_flags_Group>,
 def gno_embed_source : Flag<["-"], "gno-embed-source">, Group<g_flags_Group>,
     Flags<[NoXarchOption]>,
     HelpText<"Restore the default behavior of not embedding source text in 
DWARF debug sections">;
+defm structor_decl_linkage_names
+    : BoolGOption<"structor-decl-linkage-names",
+                  CodeGenOpts<"DebugStructorDeclLinkageNames">, DefaultTrue,
+                  NegFlag<SetFalse>,
+                  PosFlag<SetTrue, [], [],
+                          "Attach linkage names to C++ constructor/destructor "
+                          "declarations in DWARF."
+                          "Implies -g.">,
+                  BothFlags<[], [ClangOption, CLOption, CC1Option]>>,
+                  DocBrief<[{On some ABIs (e.g., Itanium), constructors and 
destructors may have multiple variants. Historically, when generating DWARF, 
Clang did not attach ``DW_AT_linkage_name``s to structor DIEs because there 
were multiple possible manglings (depending on the structor variant) that could 
be used. With ``-gstructor-decl-linkage-names``, for ABIs with structor 
variants, we attach a "unified" mangled name to structor declarations DIEs 
which debuggers can use to look up all the definitions for a structor 
declaration. E.g., a "unified" mangled name ``_ZN3FooC4Ev`` may have multiple 
definitions associated with it such as ``_ZN3FooC1Ev`` and ``_ZN3FooC2Ev``.
+
+Enabling this flag results in a better interactive debugging experience (both 
GDB and LLDB have support for understanding these "unified" linkage names). 
However, it comes with a significant increase in debug-info size (particularly 
the `.debug_str` section). As an escape hatch, users can disable this feature 
using ``-gno-structor-decl-linkage-names``.}]>;
 defm key_instructions : BoolGOption<"key-instructions",
     CodeGenOpts<"DebugKeyInstructions">, DefaultFalse,
     NegFlag<SetFalse>, PosFlag<SetTrue, [], [],
diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp
index 112678fb2714a..f6ff9861508a2 100644
--- a/clang/lib/AST/ItaniumMangle.cpp
+++ b/clang/lib/AST/ItaniumMangle.cpp
@@ -6029,6 +6029,8 @@ void CXXNameMangler::mangleCXXCtorType(CXXCtorType T,
   //                  ::= CI2 <type> # base inheriting constructor
   //
   // In addition, C5 is a comdat name with C1 and C2 in it.
+  // C4 represents a ctor declaration and is used by debuggers to look up
+  // the various ctor variants.
   Out << 'C';
   if (InheritedFrom)
     Out << 'I';
@@ -6039,6 +6041,9 @@ void CXXNameMangler::mangleCXXCtorType(CXXCtorType T,
   case Ctor_Base:
     Out << '2';
     break;
+  case Ctor_Unified:
+    Out << '4';
+    break;
   case Ctor_Comdat:
     Out << '5';
     break;
@@ -6056,6 +6061,8 @@ void CXXNameMangler::mangleCXXDtorType(CXXDtorType T) {
   //                  ::= D2  # base object destructor
   //
   // In addition, D5 is a comdat name with D1, D2 and, if virtual, D0 in it.
+  // D4 represents a dtor declaration and is used by debuggers to look up
+  // the various dtor variants.
   switch (T) {
   case Dtor_Deleting:
     Out << "D0";
@@ -6066,6 +6073,9 @@ void CXXNameMangler::mangleCXXDtorType(CXXDtorType T) {
   case Dtor_Base:
     Out << "D2";
     break;
+  case Dtor_Unified:
+    Out << "D4";
+    break;
   case Dtor_Comdat:
     Out << "D5";
     break;
diff --git a/clang/lib/AST/MicrosoftMangle.cpp 
b/clang/lib/AST/MicrosoftMangle.cpp
index fc79ab1de24ff..d214db76e419b 100644
--- a/clang/lib/AST/MicrosoftMangle.cpp
+++ b/clang/lib/AST/MicrosoftMangle.cpp
@@ -1496,6 +1496,8 @@ void 
MicrosoftCXXNameMangler::mangleCXXDtorType(CXXDtorType T) {
   // it.
   case Dtor_Comdat:
     llvm_unreachable("not expecting a COMDAT");
+  case Dtor_Unified:
+    llvm_unreachable("not expecting a unified dtor type");
   }
   llvm_unreachable("Unsupported dtor type?");
 }
diff --git a/clang/lib/CodeGen/CGClass.cpp b/clang/lib/CodeGen/CGClass.cpp
index e9a92ae0f01cb..ba587943384a6 100644
--- a/clang/lib/CodeGen/CGClass.cpp
+++ b/clang/lib/CodeGen/CGClass.cpp
@@ -1502,6 +1502,8 @@ void CodeGenFunction::EmitDestructorBody(FunctionArgList 
&Args) {
   // we'd introduce *two* handler blocks.  In the Microsoft ABI, we
   // always delegate because we might not have a definition in this TU.
   switch (DtorType) {
+  case Dtor_Unified:
+    llvm_unreachable("not expecting a unified dtor");
   case Dtor_Comdat: llvm_unreachable("not expecting a COMDAT");
   case Dtor_Deleting: llvm_unreachable("already handled deleting case");
 
diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp 
b/clang/lib/CodeGen/CGDebugInfo.cpp
index 994bdbdae860f..2ad19e9581c12 100644
--- a/clang/lib/CodeGen/CGDebugInfo.cpp
+++ b/clang/lib/CodeGen/CGDebugInfo.cpp
@@ -2177,24 +2177,47 @@ static bool isFunctionLocalClass(const CXXRecordDecl 
*RD) {
   return false;
 }
 
+llvm::StringRef
+CGDebugInfo::GetMethodLinkageName(const CXXMethodDecl *Method) const {
+  assert(Method);
+
+  const bool IsCtorOrDtor =
+      isa<CXXConstructorDecl>(Method) || isa<CXXDestructorDecl>(Method);
+
+  if (IsCtorOrDtor && !CGM.getCodeGenOpts().DebugStructorDeclLinkageNames)
+    return {};
+
+  // In some ABIs (particularly Itanium) a single ctor/dtor
+  // corresponds to multiple functions. Attach a "unified"
+  // linkage name for those (which is the convention GCC uses).
+  // Otherwise, attach no linkage name.
+  if (IsCtorOrDtor && !CGM.getTarget().getCXXABI().hasConstructorVariants())
+    return {};
+
+  if (const auto *Ctor = llvm::dyn_cast<CXXConstructorDecl>(Method))
+    return CGM.getMangledName(GlobalDecl(Ctor, CXXCtorType::Ctor_Unified));
+
+  if (const auto *Dtor = llvm::dyn_cast<CXXDestructorDecl>(Method))
+    return CGM.getMangledName(GlobalDecl(Dtor, CXXDtorType::Dtor_Unified));
+
+  return CGM.getMangledName(Method);
+}
+
 llvm::DISubprogram *CGDebugInfo::CreateCXXMemberFunction(
     const CXXMethodDecl *Method, llvm::DIFile *Unit, llvm::DIType *RecordTy) {
-  bool IsCtorOrDtor =
-      isa<CXXConstructorDecl>(Method) || isa<CXXDestructorDecl>(Method);
+  assert(Method);
 
   StringRef MethodName = getFunctionName(Method);
   llvm::DISubroutineType *MethodTy = getOrCreateMethodType(Method, Unit);
 
-  // Since a single ctor/dtor corresponds to multiple functions, it doesn't
-  // make sense to give a single ctor/dtor a linkage name.
   StringRef MethodLinkageName;
   // FIXME: 'isFunctionLocalClass' seems like an arbitrary/unintentional
   // property to use here. It may've been intended to model "is non-external
   // type" but misses cases of non-function-local but non-external classes such
   // as those in anonymous namespaces as well as the reverse - external types
   // that are function local, such as those in (non-local) inline functions.
-  if (!IsCtorOrDtor && !isFunctionLocalClass(Method->getParent()))
-    MethodLinkageName = CGM.getMangledName(Method);
+  if (!isFunctionLocalClass(Method->getParent()))
+    MethodLinkageName = GetMethodLinkageName(Method);
 
   // Get the location for the method.
   llvm::DIFile *MethodDefUnit = nullptr;
diff --git a/clang/lib/CodeGen/CGDebugInfo.h b/clang/lib/CodeGen/CGDebugInfo.h
index 497d3a6ab17b1..55c528031368d 100644
--- a/clang/lib/CodeGen/CGDebugInfo.h
+++ b/clang/lib/CodeGen/CGDebugInfo.h
@@ -899,6 +899,10 @@ class CGDebugInfo {
       std::memcpy(Data + A.size(), B.data(), B.size());
     return StringRef(Data, A.size() + B.size());
   }
+
+  /// If one exists, returns the linkage name of the specified \
+  /// (non-null) \c Method. Returns empty string otherwise.
+  llvm::StringRef GetMethodLinkageName(const CXXMethodDecl *Method) const;
 };
 
 /// A scoped helper to set the current debug location to the specified
diff --git a/clang/lib/CodeGen/ItaniumCXXABI.cpp 
b/clang/lib/CodeGen/ItaniumCXXABI.cpp
index 4ed3775f156c9..4e6907ba68acf 100644
--- a/clang/lib/CodeGen/ItaniumCXXABI.cpp
+++ b/clang/lib/CodeGen/ItaniumCXXABI.cpp
@@ -91,6 +91,8 @@ class ItaniumCXXABI : public CodeGen::CGCXXABI {
 
       case Dtor_Comdat:
         llvm_unreachable("emitting dtor comdat as function?");
+      case Dtor_Unified:
+        llvm_unreachable("emitting unified dtor as function?");
       }
       llvm_unreachable("bad dtor kind");
     }
@@ -108,6 +110,9 @@ class ItaniumCXXABI : public CodeGen::CGCXXABI {
 
       case Ctor_Comdat:
         llvm_unreachable("emitting ctor comdat as function?");
+
+      case Ctor_Unified:
+        llvm_unreachable("emitting unified ctor as function?");
       }
       llvm_unreachable("bad dtor kind");
     }
diff --git a/clang/lib/CodeGen/MicrosoftCXXABI.cpp 
b/clang/lib/CodeGen/MicrosoftCXXABI.cpp
index 88f0648660965..94190a149e859 100644
--- a/clang/lib/CodeGen/MicrosoftCXXABI.cpp
+++ b/clang/lib/CodeGen/MicrosoftCXXABI.cpp
@@ -77,6 +77,8 @@ class MicrosoftCXXABI : public CGCXXABI {
         return false;
 
       case Dtor_Comdat: llvm_unreachable("emitting dtor comdat as function?");
+      case Dtor_Unified:
+        llvm_unreachable("unexpected unified dtor type");
       }
       llvm_unreachable("bad dtor kind");
     }
@@ -1417,6 +1419,8 @@ llvm::GlobalValue::LinkageTypes 
MicrosoftCXXABI::getCXXDestructorLinkage(
     // and are emitted everywhere they are used. They are internal if the class
     // is internal.
     return llvm::GlobalValue::LinkOnceODRLinkage;
+  case Dtor_Unified:
+    llvm_unreachable("MS C++ ABI does not support unified dtors");
   case Dtor_Comdat:
     llvm_unreachable("MS C++ ABI does not support comdat dtors");
   }
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp 
b/clang/lib/Driver/ToolChains/Clang.cpp
index 29b7180df5cb5..10a00f268884e 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -4594,6 +4594,10 @@ renderDebugOptions(const ToolChain &TC, const Driver &D, 
const llvm::Triple &T,
                    options::OPT_gno_key_instructions, false))
     CmdArgs.push_back("-gkey-instructions");
 
+  if (!Args.hasFlag(options::OPT_gstructor_decl_linkage_names,
+                    options::OPT_gno_structor_decl_linkage_names, true))
+    CmdArgs.push_back("-gno-structor-decl-linkage-names");
+
   if (EmitCodeView) {
     CmdArgs.push_back("-gcodeview");
 
diff --git a/clang/test/CodeGenObjCXX/debug-info-cyclic.mm 
b/clang/test/CodeGenObjCXX/debug-info-cyclic.mm
index 2fb1611c904d0..a062b6ad50612 100644
--- a/clang/test/CodeGenObjCXX/debug-info-cyclic.mm
+++ b/clang/test/CodeGenObjCXX/debug-info-cyclic.mm
@@ -10,8 +10,9 @@
 // CHECK-SAME:                             identifier:
 // CHECK: ![[BMEMBERS]] = !{![[BB:[0-9]+]]}
   B(struct A *);
-// CHECK: ![[BB]] = !DISubprogram(name: "B", scope: ![[B]]
-// CHECK-SAME:                    line: [[@LINE-2]],
+// CHECK: ![[BB]] = !DISubprogram(name: "B",
+// CHECK-SAME:                    scope: ![[B]]
+// CHECK-SAME:                    line: [[@LINE-3]],
 // CHECK-SAME:                    type: ![[TY:[0-9]+]],
 // CHECK: ![[TY]] = !DISubroutineType(types: ![[ARGS:[0-9]+]])
 // CHECK: ![[ARGS]] = !{null, ![[THIS:[0-9]+]], !{{[^,]+}}}
diff --git a/clang/test/DebugInfo/CXX/artificial-arg.cpp 
b/clang/test/DebugInfo/CXX/artificial-arg.cpp
index a0cf131f83e15..21b8d047b3456 100644
--- a/clang/test/DebugInfo/CXX/artificial-arg.cpp
+++ b/clang/test/DebugInfo/CXX/artificial-arg.cpp
@@ -25,7 +25,8 @@ int main(int argc, char **argv) {
 // CHECK: ![[CLASSTYPE:.*]] = distinct !DICompositeType(tag: 
DW_TAG_class_type, name: "A",
 // CHECK-SAME:                                 identifier: "_ZTS1A"
 // CHECK: ![[ARTARG:.*]] = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: 
![[CLASSTYPE]],{{.*}} DIFlagArtificial
-// CHECK: !DISubprogram(name: "A", scope: ![[CLASSTYPE]]
+// CHECK: !DISubprogram(name: "A"
+// CHECK-SAME:          scope: ![[CLASSTYPE]]
 // CHECK-SAME:          line: 12
 // CHECK-SAME:          DIFlagPublic
 // CHECK: !DISubroutineType(types: [[FUNCTYPE:![0-9]*]])
diff --git a/clang/test/DebugInfo/CXX/debug-info-structor-linkage-names.cpp 
b/clang/test/DebugInfo/CXX/debug-info-structor-linkage-names.cpp
new file mode 100644
index 0000000000000..b7aac198c5180
--- /dev/null
+++ b/clang/test/DebugInfo/CXX/debug-info-structor-linkage-names.cpp
@@ -0,0 +1,89 @@
+// Tests that we emit unified constructor/destructor linkage names
+// for ABIs that support it.
+
+// Check that -gstructor-decl-linkage-names is the default.
+// RUN: %clang_cc1 -triple aarch64-apple-macosx -emit-llvm 
-debug-info-kind=standalone \
+// RUN:            %s -o - | FileCheck %s --check-prefixes=CHECK,ITANIUM
+//
+// Check with -gstructor-decl-linkage-names.
+// RUN: %clang_cc1 -triple aarch64-apple-macosx -emit-llvm 
-debug-info-kind=standalone \
+// RUN:            -gstructor-decl-linkage-names %s -o - | FileCheck %s 
--check-prefixes=CHECK,ITANIUM
+//
+// Check with -gno-structor-decl-linkage-names.
+// RUN: %clang_cc1 -triple aarch64-apple-macosx -emit-llvm 
-debug-info-kind=standalone \
+// RUN:            -gno-structor-decl-linkage-names %s -o - | FileCheck %s 
--check-prefixes=CHECK,DISABLE
+//
+// Check ABI without structor variants.
+// RUN: %clang_cc1 -triple x86_64-windows-msvc -emit-llvm 
-debug-info-kind=standalone \
+// RUN:            -gstructor-decl-linkage-names %s -o - | FileCheck %s 
--check-prefixes=CHECK,MSABI
+
+struct Base {
+  Base(int x);
+  ~Base();
+};
+
+Base::Base(int x) {}
+Base::~Base() {}
+
+// Check that we emit unified ctor/dtor (C4/D4) on Itanium but not for MS-ABI.
+
+// CHECK: ![[BASE_CTOR_DECL:[0-9]+]] = !DISubprogram(name: "Base"
+// MSABI-NOT:                                        linkageName:
+// DISABLE-NOT:                                      linkageName:
+// ITANIUM-SAME:                                     linkageName: 
"_ZN4BaseC4Ei"
+// CHECK-SAME:                                       spFlags: 0
+
+// CHECK: ![[BASE_DTOR_DECL:[0-9]+]] = !DISubprogram(name: "~Base"
+// MSABI-NOT:                                        linkageName:
+// DISABLE-NOT:                                      linkageName:
+// ITANIUM-SAME:                                     linkageName: 
"_ZN4BaseD4Ev"
+// CHECK-SAME:                                       spFlags: 0
+
+// Check that the ctor/dtor definitions have linkage names that aren't
+// the ones on the declaration.
+
+// CHECK: !DISubprogram(name: "Base"
+// MSABI-SAME:          linkageName:
+// ITANIUM-SAME:        linkageName: "_ZN4BaseC2Ei"
+// CHECK-SAME:          spFlags: DISPFlagDefinition
+// CHECK-SAME:          declaration: ![[BASE_CTOR_DECL]]
+
+// ITANIUM: !DISubprogram(name: "Base"
+// ITANIUM-SAME:          linkageName: "_ZN4BaseC1Ei"
+// ITANIUM-SAME:          spFlags: DISPFlagDefinition
+// ITANIUM-SAME:          declaration: ![[BASE_CTOR_DECL]]
+
+// CHECK: !DISubprogram(name: "~Base"
+// MSABI-SAME:          linkageName:
+// ITANIUM-SAME:        linkageName: "_ZN4BaseD2Ev"
+// CHECK-SAME:          spFlags: DISPFlagDefinition
+// CHECK-SAME:          declaration: ![[BASE_DTOR_DECL]]
+
+// ITANIUM: !DISubprogram(name: "~Base"
+// ITANIUM-SAME:          linkageName: "_ZN4BaseD1Ev"
+// ITANIUM-SAME:          spFlags: DISPFlagDefinition
+// ITANIUM-SAME:          declaration: ![[BASE_DTOR_DECL]]
+
+struct Derived : public Base {
+    using Base::Base;
+} d(5);
+
+// CHECK: !DISubprogram(name: "Base"
+// MSABI-SAME:          linkageName:
+// ITANIUM-SAME:        linkageName: "_ZN7DerivedCI14BaseEi"
+// CHECK-SAME:          spFlags: {{.*}}DISPFlagDefinition
+// CHECK-SAME:          declaration: ![[BASE_INHERIT_CTOR_DECL:[0-9]+]]
+
+// CHECK: [[BASE_INHERIT_CTOR_DECL]] = !DISubprogram(name: "Base"
+// MSABI-NOT:                                        linkageName:
+// DISABLE-NOT:                                      linkageName:
+// ITANIUM-SAME:                                     linkageName: 
"_ZN7DerivedCI44BaseEi"
+// CHECK-SAME                                        spFlags: 0
+
+// ITANIUM: !DISubprogram(name: "Base"
+// ITANIUM-SAME:          linkageName: "_ZN7DerivedCI24BaseEi"
+// ITANIUM-SAME:          spFlags: DISPFlagDefinition
+// ITANIUM-SAME:          declaration: ![[BASE_INHERIT_CTOR_DECL:[0-9]+]]
+
+// MSABI:   !DISubprogram(name: "~Derived"
+// DISABLE: !DISubprogram(name: "~Derived"

>From e547dfe194a2cee0646af15c8940f7f14d69f048 Mon Sep 17 00:00:00 2001
From: Michael Buch <michaelbuc...@gmail.com>
Date: Mon, 4 Aug 2025 14:20:45 +0100
Subject: [PATCH 3/5] [clang][Mangle] Inject structor type into mangled name
 when mangling for LLDB JIT expressions

This patch adds special handling for `AsmLabel`s created by LLDB. LLDB
uses `AsmLabel`s to encode information about a function declaration to
make it easier to locate function symbols when JITing C++ expressions.
For constructors/destructors LLDB doesn't know at the time of creating
the `AsmLabelAttr` which structor variant the expression evaluator will
need to call (this is decided when compiling the expression). So we make
the Clang mangler inject this information into our custom label when
we're JITting the expression.
---
 clang/lib/AST/Mangle.cpp         | 33 +++++++++++++-
 clang/unittests/AST/DeclTest.cpp | 75 ++++++++++++++++++++++++++++++++
 2 files changed, 107 insertions(+), 1 deletion(-)

diff --git a/clang/lib/AST/Mangle.cpp b/clang/lib/AST/Mangle.cpp
index 0bfb51c11f0a5..1131477fa7200 100644
--- a/clang/lib/AST/Mangle.cpp
+++ b/clang/lib/AST/Mangle.cpp
@@ -152,6 +152,33 @@ bool MangleContext::shouldMangleDeclName(const NamedDecl 
*D) {
   return shouldMangleCXXName(D);
 }
 
+static llvm::StringRef g_lldb_func_call_label_prefix = "$__lldb_func:";
+
+/// Given an LLDB function call label, this function prints the label
+/// into \c Out, together with the structor type of \c GD (if the
+/// decl is a constructor/destructor). LLDB knows how to handle mangled
+/// names with this encoding.
+///
+/// Example input label:
+///   $__lldb_func::123:456:~Foo
+///
+/// Example output:
+///   $__lldb_func:D1:123:456:~Foo
+///
+static void emitLLDBAsmLabel(llvm::StringRef label, GlobalDecl GD,
+                             llvm::raw_ostream &Out) {
+  assert(label.starts_with(g_lldb_func_call_label_prefix));
+
+  Out << g_lldb_func_call_label_prefix;
+
+  if (llvm::isa<clang::CXXConstructorDecl>(GD.getDecl()))
+    Out << "C" << GD.getCtorType();
+  else if (llvm::isa<clang::CXXDestructorDecl>(GD.getDecl()))
+    Out << "D" << GD.getDtorType();
+
+  Out << label.substr(g_lldb_func_call_label_prefix.size());
+}
+
 void MangleContext::mangleName(GlobalDecl GD, raw_ostream &Out) {
   const ASTContext &ASTContext = getASTContext();
   const NamedDecl *D = cast<NamedDecl>(GD.getDecl());
@@ -185,7 +212,11 @@ void MangleContext::mangleName(GlobalDecl GD, raw_ostream 
&Out) {
     if (!UserLabelPrefix.empty())
       Out << '\01'; // LLVM IR Marker for __asm("foo")
 
-    Out << ALA->getLabel();
+    if (ALA->getLabel().starts_with(g_lldb_func_call_label_prefix))
+      emitLLDBAsmLabel(ALA->getLabel(), GD, Out);
+    else
+      Out << ALA->getLabel();
+
     return;
   }
 
diff --git a/clang/unittests/AST/DeclTest.cpp b/clang/unittests/AST/DeclTest.cpp
index 6b443918ec137..4bd7886ef9b35 100644
--- a/clang/unittests/AST/DeclTest.cpp
+++ b/clang/unittests/AST/DeclTest.cpp
@@ -16,6 +16,7 @@
 #include "clang/AST/Mangle.h"
 #include "clang/ASTMatchers/ASTMatchFinder.h"
 #include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Basic/ABI.h"
 #include "clang/Basic/Diagnostic.h"
 #include "clang/Basic/LLVM.h"
 #include "clang/Basic/TargetInfo.h"
@@ -102,6 +103,80 @@ TEST(Decl, AsmLabelAttr) {
                      "foo");
 }
 
+TEST(Decl, AsmLabelAttr_LLDB) {
+  StringRef Code = R"(
+    struct S {
+      void f() {}
+      S() = default;
+      ~S() = default;
+    };
+  )";
+  auto AST =
+      tooling::buildASTFromCodeWithArgs(Code, {"-target", 
"i386-apple-darwin"});
+  ASTContext &Ctx = AST->getASTContext();
+  assert(Ctx.getTargetInfo().getUserLabelPrefix() == StringRef("_") &&
+         "Expected target to have a global prefix");
+  DiagnosticsEngine &Diags = AST->getDiagnostics();
+
+  const auto *DeclS =
+      selectFirst<CXXRecordDecl>("d", match(cxxRecordDecl().bind("d"), Ctx));
+
+  auto *DeclF = *DeclS->method_begin();
+  auto *Ctor = *DeclS->ctor_begin();
+  auto *Dtor = DeclS->getDestructor();
+
+  ASSERT_TRUE(DeclF);
+  ASSERT_TRUE(Ctor);
+  ASSERT_TRUE(Dtor);
+
+  DeclF->addAttr(AsmLabelAttr::Create(Ctx, "$__lldb_func::123:123:_Z1fv"));
+  Ctor->addAttr(AsmLabelAttr::Create(Ctx, "$__lldb_func::123:123:S"));
+  Dtor->addAttr(AsmLabelAttr::Create(Ctx, "$__lldb_func::123:123:~S"));
+
+  std::unique_ptr<ItaniumMangleContext> MC(
+      ItaniumMangleContext::create(Ctx, Diags));
+
+  {
+    std::string Mangled;
+    llvm::raw_string_ostream OS_Mangled(Mangled);
+    MC->mangleName(DeclF, OS_Mangled);
+
+    ASSERT_EQ(Mangled, "\x01$__lldb_func::123:123:_Z1fv");
+  };
+
+  {
+    std::string Mangled;
+    llvm::raw_string_ostream OS_Mangled(Mangled);
+    MC->mangleName(GlobalDecl(Ctor, CXXCtorType::Ctor_Complete), OS_Mangled);
+
+    ASSERT_EQ(Mangled, "\x01$__lldb_func:C0:123:123:S");
+  };
+
+  {
+    std::string Mangled;
+    llvm::raw_string_ostream OS_Mangled(Mangled);
+    MC->mangleName(GlobalDecl(Ctor, CXXCtorType::Ctor_Base), OS_Mangled);
+
+    ASSERT_EQ(Mangled, "\x01$__lldb_func:C1:123:123:S");
+  };
+
+  {
+    std::string Mangled;
+    llvm::raw_string_ostream OS_Mangled(Mangled);
+    MC->mangleName(GlobalDecl(Dtor, CXXDtorType::Dtor_Deleting), OS_Mangled);
+
+    ASSERT_EQ(Mangled, "\x01$__lldb_func:D0:123:123:~S");
+  };
+
+  {
+    std::string Mangled;
+    llvm::raw_string_ostream OS_Mangled(Mangled);
+    MC->mangleName(GlobalDecl(Dtor, CXXDtorType::Dtor_Base), OS_Mangled);
+
+    ASSERT_EQ(Mangled, "\x01$__lldb_func:D2:123:123:~S");
+  };
+}
+
 TEST(Decl, MangleDependentSizedArray) {
   StringRef Code = R"(
     template <int ...N>

>From 21fc56bffe2a9603173f43aba34746e5fd345d41 Mon Sep 17 00:00:00 2001
From: Michael Buch <michaelbuc...@gmail.com>
Date: Thu, 14 Aug 2025 10:14:24 +0100
Subject: [PATCH 4/5] [lldb][CPlusPlusLanguage] Expose ManglingSubstitutor as
 static helpers

Allows us to use the mangling substitution facilities in
CPlusPlusLanguage but also SymbolFileDWARF.
---
 .../Language/CPlusPlus/CPlusPlusLanguage.cpp  | 322 ++++++++++--------
 .../Language/CPlusPlus/CPlusPlusLanguage.h    |  13 +
 2 files changed, 201 insertions(+), 134 deletions(-)

diff --git a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp 
b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
index b4207439f5285..9d9880edd2454 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
+++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
@@ -604,126 +604,6 @@ bool CPlusPlusLanguage::ExtractContextAndIdentifier(
   return false;
 }
 
-namespace {
-class NodeAllocator {
-  llvm::BumpPtrAllocator Alloc;
-
-public:
-  void reset() { Alloc.Reset(); }
-
-  template <typename T, typename... Args> T *makeNode(Args &&...args) {
-    return new (Alloc.Allocate(sizeof(T), alignof(T)))
-        T(std::forward<Args>(args)...);
-  }
-
-  void *allocateNodeArray(size_t sz) {
-    return Alloc.Allocate(sizeof(llvm::itanium_demangle::Node *) * sz,
-                          alignof(llvm::itanium_demangle::Node *));
-  }
-};
-
-template <typename Derived>
-class ManglingSubstitutor
-    : public llvm::itanium_demangle::AbstractManglingParser<Derived,
-                                                            NodeAllocator> {
-  using Base =
-      llvm::itanium_demangle::AbstractManglingParser<Derived, NodeAllocator>;
-
-public:
-  ManglingSubstitutor() : Base(nullptr, nullptr) {}
-
-  template <typename... Ts>
-  ConstString substitute(llvm::StringRef Mangled, Ts &&...Vals) {
-    this->getDerived().reset(Mangled, std::forward<Ts>(Vals)...);
-    return substituteImpl(Mangled);
-  }
-
-protected:
-  void reset(llvm::StringRef Mangled) {
-    Base::reset(Mangled.begin(), Mangled.end());
-    Written = Mangled.begin();
-    Result.clear();
-    Substituted = false;
-  }
-
-  ConstString substituteImpl(llvm::StringRef Mangled) {
-    Log *log = GetLog(LLDBLog::Language);
-    if (this->parse() == nullptr) {
-      LLDB_LOG(log, "Failed to substitute mangling in {0}", Mangled);
-      return ConstString();
-    }
-    if (!Substituted)
-      return ConstString();
-
-    // Append any trailing unmodified input.
-    appendUnchangedInput();
-    LLDB_LOG(log, "Substituted mangling {0} -> {1}", Mangled, Result);
-    return ConstString(Result);
-  }
-
-  void trySubstitute(llvm::StringRef From, llvm::StringRef To) {
-    if (!llvm::StringRef(currentParserPos(), 
this->numLeft()).starts_with(From))
-      return;
-
-    // We found a match. Append unmodified input up to this point.
-    appendUnchangedInput();
-
-    // And then perform the replacement.
-    Result += To;
-    Written += From.size();
-    Substituted = true;
-  }
-
-private:
-  /// Input character until which we have constructed the respective output
-  /// already.
-  const char *Written = "";
-
-  llvm::SmallString<128> Result;
-
-  /// Whether we have performed any substitutions.
-  bool Substituted = false;
-
-  const char *currentParserPos() const { return this->First; }
-
-  void appendUnchangedInput() {
-    Result +=
-        llvm::StringRef(Written, std::distance(Written, currentParserPos()));
-    Written = currentParserPos();
-  }
-};
-
-/// Given a mangled function `Mangled`, replace all the primitive function type
-/// arguments of `Search` with type `Replace`.
-class TypeSubstitutor : public ManglingSubstitutor<TypeSubstitutor> {
-  llvm::StringRef Search;
-  llvm::StringRef Replace;
-
-public:
-  void reset(llvm::StringRef Mangled, llvm::StringRef Search,
-             llvm::StringRef Replace) {
-    ManglingSubstitutor::reset(Mangled);
-    this->Search = Search;
-    this->Replace = Replace;
-  }
-
-  llvm::itanium_demangle::Node *parseType() {
-    trySubstitute(Search, Replace);
-    return ManglingSubstitutor::parseType();
-  }
-};
-
-class CtorDtorSubstitutor : public ManglingSubstitutor<CtorDtorSubstitutor> {
-public:
-  llvm::itanium_demangle::Node *
-  parseCtorDtorName(llvm::itanium_demangle::Node *&SoFar, NameState *State) {
-    trySubstitute("C1", "C2");
-    trySubstitute("D1", "D2");
-    return ManglingSubstitutor::parseCtorDtorName(SoFar, State);
-  }
-};
-} // namespace
-
 std::vector<ConstString> CPlusPlusLanguage::GenerateAlternateFunctionManglings(
     const ConstString mangled_name) const {
   std::vector<ConstString> alternates;
@@ -751,29 +631,49 @@ std::vector<ConstString> 
CPlusPlusLanguage::GenerateAlternateFunctionManglings(
     alternates.push_back(ConstString(fixed_scratch));
   }
 
-  TypeSubstitutor TS;
+  auto *log = GetLog(LLDBLog::Language);
+
   // `char` is implementation defined as either `signed` or `unsigned`.  As a
   // result a char parameter has 3 possible manglings: 'c'-char, 'a'-signed
   // char, 'h'-unsigned char.  If we're looking for symbols with a signed char
   // parameter, try finding matches which have the general case 'c'.
-  if (ConstString char_fixup =
-          TS.substitute(mangled_name.GetStringRef(), "a", "c"))
-    alternates.push_back(char_fixup);
+  if (auto char_fixup_or_err =
+          SubstituteType_ItaniumMangle(mangled_name.GetStringRef(), "a", "c")) 
{
+    // LLDB_LOG(log, "Substituted mangling {0} -> {1}", Mangled, Result);
+    if (*char_fixup_or_err)
+      alternates.push_back(*char_fixup_or_err);
+  } else
+    LLDB_LOG_ERROR(log, char_fixup_or_err.takeError(),
+                   "Failed to substitute 'char' type mangling: {0}");
 
   // long long parameter mangling 'x', may actually just be a long 'l' argument
-  if (ConstString long_fixup =
-          TS.substitute(mangled_name.GetStringRef(), "x", "l"))
-    alternates.push_back(long_fixup);
+  if (auto long_fixup_or_err =
+          SubstituteType_ItaniumMangle(mangled_name.GetStringRef(), "x", "l")) 
{
+    if (*long_fixup_or_err)
+      alternates.push_back(*long_fixup_or_err);
+  } else
+    LLDB_LOG_ERROR(log, long_fixup_or_err.takeError(),
+                   "Failed to substitute 'long long' type mangling: {0}");
 
   // unsigned long long parameter mangling 'y', may actually just be unsigned
   // long 'm' argument
-  if (ConstString ulong_fixup =
-          TS.substitute(mangled_name.GetStringRef(), "y", "m"))
-    alternates.push_back(ulong_fixup);
-
-  if (ConstString ctor_fixup =
-          CtorDtorSubstitutor().substitute(mangled_name.GetStringRef()))
-    alternates.push_back(ctor_fixup);
+  if (auto ulong_fixup_or_err =
+          SubstituteType_ItaniumMangle(mangled_name.GetStringRef(), "y", "m")) 
{
+    if (*ulong_fixup_or_err)
+      alternates.push_back(*ulong_fixup_or_err);
+  } else
+    LLDB_LOG_ERROR(
+        log, ulong_fixup_or_err.takeError(),
+        "Failed to substitute 'unsigned long long' type mangling: {0}");
+
+  if (auto ctor_fixup_or_err = SubstituteStructorAliases_ItaniumMangle(
+          mangled_name.GetStringRef())) {
+    if (*ctor_fixup_or_err) {
+      alternates.push_back(*ctor_fixup_or_err);
+    }
+  } else
+    LLDB_LOG_ERROR(log, ctor_fixup_or_err.takeError(),
+                   "Failed to substitute structor alias manglings: {0}");
 
   return alternates;
 }
@@ -2442,6 +2342,160 @@ bool CPlusPlusLanguage::HandleFrameFormatVariable(
   }
 }
 
+namespace {
+class NodeAllocator {
+  llvm::BumpPtrAllocator Alloc;
+
+public:
+  void reset() { Alloc.Reset(); }
+
+  template <typename T, typename... Args> T *makeNode(Args &&...args) {
+    return new (Alloc.Allocate(sizeof(T), alignof(T)))
+        T(std::forward<Args>(args)...);
+  }
+
+  void *allocateNodeArray(size_t sz) {
+    return Alloc.Allocate(sizeof(llvm::itanium_demangle::Node *) * sz,
+                          alignof(llvm::itanium_demangle::Node *));
+  }
+};
+
+template <typename Derived>
+class ManglingSubstitutor
+    : public llvm::itanium_demangle::AbstractManglingParser<Derived,
+                                                            NodeAllocator> {
+  using Base =
+      llvm::itanium_demangle::AbstractManglingParser<Derived, NodeAllocator>;
+
+public:
+  ManglingSubstitutor() : Base(nullptr, nullptr) {}
+
+  template <typename... Ts>
+  llvm::Expected<ConstString> substitute(llvm::StringRef Mangled,
+                                         Ts &&...Vals) {
+    this->getDerived().reset(Mangled, std::forward<Ts>(Vals)...);
+    return substituteImpl(Mangled);
+  }
+
+protected:
+  void reset(llvm::StringRef Mangled) {
+    Base::reset(Mangled.begin(), Mangled.end());
+    Written = Mangled.begin();
+    Result.clear();
+    Substituted = false;
+  }
+
+  llvm::Expected<ConstString> substituteImpl(llvm::StringRef Mangled) {
+    if (this->parse() == nullptr)
+      return llvm::createStringError(
+          llvm::formatv("Failed to substitute mangling in {0}", Mangled));
+
+    if (!Substituted)
+      return ConstString();
+
+    // Append any trailing unmodified input.
+    appendUnchangedInput();
+    return ConstString(Result);
+  }
+
+  void trySubstitute(llvm::StringRef From, llvm::StringRef To) {
+    if (!llvm::StringRef(currentParserPos(), 
this->numLeft()).starts_with(From))
+      return;
+
+    // We found a match. Append unmodified input up to this point.
+    appendUnchangedInput();
+
+    // And then perform the replacement.
+    Result += To;
+    Written += From.size();
+    Substituted = true;
+  }
+
+private:
+  /// Input character until which we have constructed the respective output
+  /// already.
+  const char *Written = "";
+
+  llvm::SmallString<128> Result;
+
+  /// Whether we have performed any substitutions.
+  bool Substituted = false;
+
+  const char *currentParserPos() const { return this->First; }
+
+  void appendUnchangedInput() {
+    Result +=
+        llvm::StringRef(Written, std::distance(Written, currentParserPos()));
+    Written = currentParserPos();
+  }
+};
+
+/// Given a mangled function `Mangled`, replace all the primitive function type
+/// arguments of `Search` with type `Replace`.
+class TypeSubstitutor : public ManglingSubstitutor<TypeSubstitutor> {
+  llvm::StringRef Search;
+  llvm::StringRef Replace;
+
+public:
+  void reset(llvm::StringRef Mangled, llvm::StringRef Search,
+             llvm::StringRef Replace) {
+    ManglingSubstitutor::reset(Mangled);
+    this->Search = Search;
+    this->Replace = Replace;
+  }
+
+  llvm::itanium_demangle::Node *parseType() {
+    trySubstitute(Search, Replace);
+    return ManglingSubstitutor::parseType();
+  }
+};
+
+class CtorDtorSubstitutor : public ManglingSubstitutor<CtorDtorSubstitutor> {
+  llvm::StringRef Search;
+  llvm::StringRef Replace;
+
+public:
+  void reset(llvm::StringRef Mangled, llvm::StringRef Search,
+             llvm::StringRef Replace) {
+    ManglingSubstitutor::reset(Mangled);
+    this->Search = Search;
+    this->Replace = Replace;
+  }
+
+  void reset(llvm::StringRef Mangled) { ManglingSubstitutor::reset(Mangled); }
+
+  llvm::itanium_demangle::Node *
+  parseCtorDtorName(llvm::itanium_demangle::Node *&SoFar, NameState *State) {
+    if (!Search.empty() && !Replace.empty()) {
+      trySubstitute(Search, Replace);
+    } else {
+      trySubstitute("D1", "D2");
+      trySubstitute("C1", "C2");
+    }
+    return ManglingSubstitutor::parseCtorDtorName(SoFar, State);
+  }
+};
+} // namespace
+
+llvm::Expected<ConstString>
+CPlusPlusLanguage::SubstituteType_ItaniumMangle(llvm::StringRef mangled_name,
+                                                llvm::StringRef subst_from,
+                                                llvm::StringRef subst_to) {
+  return TypeSubstitutor().substitute(mangled_name, subst_from, subst_to);
+}
+
+llvm::Expected<ConstString> 
CPlusPlusLanguage::SubstituteStructor_ItaniumMangle(
+    llvm::StringRef mangled_name, llvm::StringRef subst_from,
+    llvm::StringRef subst_to) {
+  return CtorDtorSubstitutor().substitute(mangled_name, subst_from, subst_to);
+}
+
+llvm::Expected<ConstString>
+CPlusPlusLanguage::SubstituteStructorAliases_ItaniumMangle(
+    llvm::StringRef mangled_name) {
+  return CtorDtorSubstitutor().substitute(mangled_name);
+}
+
 #define LLDB_PROPERTIES_language_cplusplus
 #include "LanguageCPlusPlusProperties.inc"
 
diff --git a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h 
b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h
index 4a30299dd2658..67b28bf202c3a 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h
+++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.h
@@ -164,6 +164,19 @@ class CPlusPlusLanguage : public Language {
   ConstString FindBestAlternateFunctionMangledName(
       const Mangled mangled, const SymbolContext &sym_ctx) const override;
 
+  static llvm::Expected<ConstString>
+  SubstituteType_ItaniumMangle(llvm::StringRef mangled_name,
+                               llvm::StringRef subst_from,
+                               llvm::StringRef subst_to);
+
+  static llvm::Expected<ConstString>
+  SubstituteStructor_ItaniumMangle(llvm::StringRef mangled_name,
+                                   llvm::StringRef subst_from,
+                                   llvm::StringRef subst_to);
+
+  static llvm::Expected<ConstString>
+  SubstituteStructorAliases_ItaniumMangle(llvm::StringRef mangled_name);
+
   llvm::StringRef GetInstanceVariableName() override { return "this"; }
 
   FormatEntity::Entry GetFunctionNameFormat() const override;

>From 5b2b2d9973de87d65baf026e7500a6a43d3430ed Mon Sep 17 00:00:00 2001
From: Michael Buch <michaelbuc...@gmail.com>
Date: Mon, 4 Aug 2025 14:21:16 +0100
Subject: [PATCH 5/5] [lldb][Expression] Encode structor variant into
 FunctionCallLabel

This patch is an implementation of [this 
discussion](https://discourse.llvm.org/t/rfc-lldb-handling-abi-tagged-constructors-destructors-in-expression-evaluator/82816/7)
 about handling ABI-tagged structors during expression evaluation.

**Motivation**

LLDB encodes the mangled name of a `DW_TAG_subprogram` into `AsmLabel`s on 
function and method Clang AST nodes. This means that when calls to these 
functions get lowered into IR (when running JITted expressions), the address 
resolver can locate the appropriate symbol by mangled name (and it's guaranteed 
to find the symbol because we got the mangled name from debug-info, instead of 
letting Clang mangle it based on AST structure). However, we don't do this for 
`CXXConstructorDecl`s/`CXXDestructorDecl`s because these structor declarations 
in DWARF don't have a linkage name. This is because there can be multiple 
variants of a structor, each with a distinct mangling in the Itanium ABI. Each 
structor variant has its own definition `DW_TAG_subprogram`. So LLDB doesn't 
know which mangled name to put into the `AsmLabel`.

Currently this means using an ABI-tagged constructor in LLDB expressions won't 
work (see [this 
RFC](https://discourse.llvm.org/t/rfc-lldb-handling-abi-tagged-constructors-destructors-in-expression-evaluator/82816)
 for concrete examples).

**Proposed Solution**

The `FunctionCallLabel` encoding that we put into `AsmLabel`s already supports 
stuffing more info about a DIE into it. So this patch extends the 
`FunctionCallLabel` to contain an optional discriminator (a sequence of bytes) 
which the `SymbolFileDWARF` plugin interprets as the constructor/destructor 
variant of that DIE.

There's a few subtleties here:
1. At the point at which LLDB first constructs the label, it has no way of 
knowing (just by looking the debug-info declaration), which structor variant 
the expression evaluator is supposed to call. That's something that gets 
decided when compiling the expression. So we let the Clang mangler inject the 
correct structor variant into the `AsmLabel` during JITing. I adjusted the 
`AsmLabelAttr` mangling for this. An option would've been to create a new Clang 
attribute which behaved like an `AsmLabel` but with these special semantics for 
LLDB. My main concern there is that we'd have to adjust all the `AsmLabelAttr` 
checks around Clang to also now account for this new attribute.
2. The compiler is free to omit the `C1` variant of a constructor if the `C2` 
variant is sufficient. In that case it may alias `C1` to `C2`, leaving us with 
only the `C2` `DW_TAG_subprogram` in the object file. Linux is one of the 
platforms where this occurs. For those cases there's heuristic in 
`SymbolFileDWARF` where we pick `C2` if we asked for `C1` but it doesn't exist. 
This may not always be correct (if for some reason the compiler decided to drop 
`C1` for other reasons).
---
 lldb/include/lldb/Expression/Expression.h     |   8 +-
 lldb/include/lldb/Symbol/SymbolFile.h         |   4 +-
 lldb/source/Expression/Expression.cpp         |  29 ++--
 lldb/source/Expression/IRExecutionUnit.cpp    |   2 +-
 .../SymbolFile/DWARF/DWARFASTParserClang.cpp  |   6 +-
 .../SymbolFile/DWARF/SymbolFileDWARF.cpp      | 152 +++++++++++++++---
 .../SymbolFile/DWARF/SymbolFileDWARF.h        |   6 +-
 .../DWARF/SymbolFileDWARFDebugMap.cpp         |   4 +-
 .../DWARF/SymbolFileDWARFDebugMap.h           |   2 +-
 .../API/lang/cpp/abi_tag_structors/Makefile   |   3 +
 .../abi_tag_structors/TestAbiTagStructors.py  |  90 +++++++++++
 .../API/lang/cpp/abi_tag_structors/main.cpp   |  44 +++++
 .../TestExprDefinitionInDylib.py              |  76 ++++++++-
 .../lang/cpp/expr-definition-in-dylib/lib.cpp |   6 +-
 .../lang/cpp/expr-definition-in-dylib/lib.h   |  11 +-
 .../cpp/expr-definition-in-dylib/main.cpp     |  15 +-
 lldb/unittests/Expression/ExpressionTest.cpp  |  37 +++--
 lldb/unittests/Symbol/TestTypeSystemClang.cpp |  14 +-
 18 files changed, 426 insertions(+), 83 deletions(-)
 create mode 100644 lldb/test/API/lang/cpp/abi_tag_structors/Makefile
 create mode 100644 
lldb/test/API/lang/cpp/abi_tag_structors/TestAbiTagStructors.py
 create mode 100644 lldb/test/API/lang/cpp/abi_tag_structors/main.cpp

diff --git a/lldb/include/lldb/Expression/Expression.h 
b/lldb/include/lldb/Expression/Expression.h
index 20067f469895b..847226167d584 100644
--- a/lldb/include/lldb/Expression/Expression.h
+++ b/lldb/include/lldb/Expression/Expression.h
@@ -103,11 +103,15 @@ class Expression {
 ///
 /// The format being:
 ///
-///   <prefix>:<module uid>:<symbol uid>:<name>
+///   <prefix>:<discriminator>:<module uid>:<symbol uid>:<name>
 ///
 /// The label string needs to stay valid for the entire lifetime
 /// of this object.
 struct FunctionCallLabel {
+  /// Arbitrary string which language plugins can interpret for their
+  /// own needs.
+  llvm::StringRef discriminator;
+
   /// Unique identifier of the lldb_private::Module
   /// which contains the symbol identified by \c symbol_id.
   lldb::user_id_t module_id;
@@ -133,7 +137,7 @@ struct FunctionCallLabel {
   ///
   /// The representation roundtrips through \c fromString:
   /// \code{.cpp}
-  /// llvm::StringRef encoded = "$__lldb_func:0x0:0x0:_Z3foov";
+  /// llvm::StringRef encoded = "$__lldb_func:blah:0x0:0x0:_Z3foov";
   /// FunctionCallLabel label = *fromString(label);
   ///
   /// assert (label.toString() == encoded);
diff --git a/lldb/include/lldb/Symbol/SymbolFile.h 
b/lldb/include/lldb/Symbol/SymbolFile.h
index bbc615d9fdc38..a84f081b340eb 100644
--- a/lldb/include/lldb/Symbol/SymbolFile.h
+++ b/lldb/include/lldb/Symbol/SymbolFile.h
@@ -332,12 +332,12 @@ class SymbolFile : public PluginInterface {
   /// Resolves the function corresponding to the specified LLDB function
   /// call \c label.
   ///
-  /// \param[in] label The FunctionCallLabel to be resolved.
+  /// \param[in,out] label The FunctionCallLabel to be resolved.
   ///
   /// \returns An llvm::Error if the specified \c label couldn't be resolved.
   ///          Returns the resolved function (as a SymbolContext) otherwise.
   virtual llvm::Expected<SymbolContext>
-  ResolveFunctionCallLabel(const FunctionCallLabel &label) {
+  ResolveFunctionCallLabel(FunctionCallLabel &label) {
     return llvm::createStringError("Not implemented");
   }
 
diff --git a/lldb/source/Expression/Expression.cpp 
b/lldb/source/Expression/Expression.cpp
index 796851ff15ca3..16ecb1d7deef8 100644
--- a/lldb/source/Expression/Expression.cpp
+++ b/lldb/source/Expression/Expression.cpp
@@ -34,10 +34,10 @@ Expression::Expression(ExecutionContextScope &exe_scope)
 
 llvm::Expected<FunctionCallLabel>
 lldb_private::FunctionCallLabel::fromString(llvm::StringRef label) {
-  llvm::SmallVector<llvm::StringRef, 4> components;
-  label.split(components, ":", /*MaxSplit=*/3);
+  llvm::SmallVector<llvm::StringRef, 5> components;
+  label.split(components, ":", /*MaxSplit=*/4);
 
-  if (components.size() != 4)
+  if (components.size() != 5)
     return llvm::createStringError("malformed function call label.");
 
   if (components[0] != FunctionCallLabelPrefix)
@@ -45,8 +45,10 @@ lldb_private::FunctionCallLabel::fromString(llvm::StringRef 
label) {
         "expected function call label prefix '{0}' but found '{1}' instead.",
         FunctionCallLabelPrefix, components[0]));
 
-  llvm::StringRef module_label = components[1];
-  llvm::StringRef die_label = components[2];
+  llvm::StringRef discriminator = components[1];
+  llvm::StringRef module_label = components[2];
+  llvm::StringRef die_label = components[3];
+  llvm::StringRef lookup_name = components[4];
 
   lldb::user_id_t module_id = 0;
   if (!llvm::to_integer(module_label, module_id))
@@ -58,20 +60,23 @@ lldb_private::FunctionCallLabel::fromString(llvm::StringRef 
label) {
     return llvm::createStringError(
         llvm::formatv("failed to parse symbol ID from '{0}'.", die_label));
 
-  return FunctionCallLabel{/*.module_id=*/module_id,
+  return FunctionCallLabel{/*.discriminator=*/discriminator,
+                           /*.module_id=*/module_id,
                            /*.symbol_id=*/die_id,
-                           /*.lookup_name=*/components[3]};
+                           /*.lookup_name=*/lookup_name};
 }
 
 std::string lldb_private::FunctionCallLabel::toString() const {
-  return llvm::formatv("{0}:{1:x}:{2:x}:{3}", FunctionCallLabelPrefix,
-                       module_id, symbol_id, lookup_name)
+  return llvm::formatv("{0}:{1}:{2:x}:{3:x}:{4}", FunctionCallLabelPrefix,
+                       discriminator, module_id, symbol_id, lookup_name)
       .str();
 }
 
 void llvm::format_provider<FunctionCallLabel>::format(
     const FunctionCallLabel &label, raw_ostream &OS, StringRef Style) {
-  OS << llvm::formatv("FunctionCallLabel{ module_id: {0:x}, symbol_id: {1:x}, "
-                      "lookup_name: {2} }",
-                      label.module_id, label.symbol_id, label.lookup_name);
+  OS << llvm::formatv("FunctionCallLabel{ discriminator: {0}, module_id: "
+                      "{1:x}, symbol_id: {2:x}, "
+                      "lookup_name: {3} }",
+                      label.discriminator, label.module_id, label.symbol_id,
+                      label.lookup_name);
 }
diff --git a/lldb/source/Expression/IRExecutionUnit.cpp 
b/lldb/source/Expression/IRExecutionUnit.cpp
index d557084acb745..d3dd163edd034 100644
--- a/lldb/source/Expression/IRExecutionUnit.cpp
+++ b/lldb/source/Expression/IRExecutionUnit.cpp
@@ -778,7 +778,7 @@ class LoadAddressResolver {
 /// Returns address of the function referred to by the special function call
 /// label \c label.
 static llvm::Expected<lldb::addr_t>
-ResolveFunctionCallLabel(const FunctionCallLabel &label,
+ResolveFunctionCallLabel(FunctionCallLabel &label,
                          const lldb_private::SymbolContext &sc,
                          bool &symbol_was_missing_weak) {
   symbol_was_missing_weak = false;
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp 
b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp
index a429ea848b7f7..bc13e7875c4a6 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp
+++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp
@@ -252,7 +252,7 @@ static unsigned GetCXXMethodCVQuals(const DWARFDIE 
&subprogram,
 }
 
 static std::string MakeLLDBFuncAsmLabel(const DWARFDIE &die) {
-  char const *name = die.GetMangledName(/*substitute_name_allowed*/ false);
+  const char *name = die.GetMangledName(/*substitute_name_allowed*/ false);
   if (!name)
     return {};
 
@@ -286,7 +286,9 @@ static std::string MakeLLDBFuncAsmLabel(const DWARFDIE 
&die) {
   if (die_id == LLDB_INVALID_UID)
     return {};
 
-  return FunctionCallLabel{/*module_id=*/module_id,
+  // Note, discriminator is added by Clang during mangling.
+  return FunctionCallLabel{/*discriminator=*/{},
+                           /*module_id=*/module_id,
                            /*symbol_id=*/die_id,
                            /*.lookup_name=*/name}
       .toString();
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp 
b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
index 9958af26379b9..4b0e81bb9079f 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
+++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp
@@ -7,10 +7,13 @@
 
//===----------------------------------------------------------------------===//
 
 #include "SymbolFileDWARF.h"
+#include "clang/Basic/ABI.h"
 #include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/StringExtras.h"
 #include "llvm/DebugInfo/DWARF/DWARFAddressRange.h"
 #include "llvm/DebugInfo/DWARF/DWARFDebugLoc.h"
 #include "llvm/Support/Casting.h"
+#include "llvm/Support/Error.h"
 #include "llvm/Support/FileUtilities.h"
 #include "llvm/Support/FormatAdapters.h"
 #include "llvm/Support/Threading.h"
@@ -22,6 +25,7 @@
 #include "lldb/Core/Progress.h"
 #include "lldb/Core/Section.h"
 #include "lldb/Core/Value.h"
+#include "lldb/Expression/Expression.h"
 #include "lldb/Utility/ArchSpec.h"
 #include "lldb/Utility/LLDBLog.h"
 #include "lldb/Utility/RegularExpression.h"
@@ -78,6 +82,7 @@
 
 #include "llvm/DebugInfo/DWARF/DWARFContext.h"
 #include "llvm/DebugInfo/DWARF/DWARFDebugAbbrev.h"
+#include "llvm/Demangle/Demangle.h"
 #include "llvm/Support/FileSystem.h"
 #include "llvm/Support/FormatVariadic.h"
 
@@ -2483,34 +2488,131 @@ bool SymbolFileDWARF::ResolveFunction(const DWARFDIE 
&orig_die,
   return false;
 }
 
-DWARFDIE
-SymbolFileDWARF::FindFunctionDefinition(const FunctionCallLabel &label) {
-  DWARFDIE definition;
-  Module::LookupInfo info(ConstString(label.lookup_name),
-                          lldb::eFunctionNameTypeFull,
-                          lldb::eLanguageTypeUnknown);
-
-  m_index->GetFunctions(info, *this, {}, [&](DWARFDIE entry) {
-    if (entry.GetAttributeValueAsUnsigned(llvm::dwarf::DW_AT_declaration, 0))
-      return IterationAction::Continue;
+static llvm::StringRef ClangToItaniumCtorKind(clang::CXXCtorType kind) {
+  switch (kind) {
+  case clang::CXXCtorType::Ctor_Complete:
+    return "C1";
+  case clang::CXXCtorType::Ctor_Base:
+    return "C2";
+  case clang::CXXCtorType::Ctor_Unified:
+    return "C4";
+  case clang::CXXCtorType::Ctor_CopyingClosure:
+  case clang::CXXCtorType::Ctor_DefaultClosure:
+  case clang::CXXCtorType::Ctor_Comdat:
+    llvm_unreachable("Unexpected constructor kind.");
+  }
+}
 
-    // We don't check whether the specification DIE for this function
-    // corresponds to the declaration DIE because the declaration might be in
-    // a type-unit but the definition in the compile-unit (and it's
-    // specifcation would point to the declaration in the compile-unit). We
-    // rely on the mangled name within the module to be enough to find us the
-    // unique definition.
-    definition = entry;
-    return IterationAction::Stop;
-  });
+static llvm::StringRef ClangToItaniumDtorKind(clang::CXXDtorType kind) {
+  switch (kind) {
+  case clang::CXXDtorType::Dtor_Deleting:
+    return "D0";
+  case clang::CXXDtorType::Dtor_Complete:
+    return "D1";
+  case clang::CXXDtorType::Dtor_Base:
+    return "D2";
+  case clang::CXXDtorType::Dtor_Unified:
+    return "D4";
+  case clang::CXXDtorType::Dtor_Comdat:
+    llvm_unreachable("Unexpected destructor kind.");
+  }
+}
+
+static llvm::StringRef
+GetItaniumCtorDtorVariant(llvm::StringRef discriminator) {
+  const bool is_ctor = discriminator.consume_front("C");
+  if (!is_ctor && !discriminator.consume_front("D"))
+    return {};
+
+  uint64_t structor_kind;
+  if (!llvm::to_integer(discriminator, structor_kind))
+    return {};
+
+  if (is_ctor) {
+    if (structor_kind > clang::CXXCtorType::Ctor_Unified)
+      return {};
+
+    return ClangToItaniumCtorKind(
+        static_cast<clang::CXXCtorType>(structor_kind));
+  }
+
+  if (structor_kind > clang::CXXDtorType::Dtor_Unified)
+    return {};
+
+  return 
ClangToItaniumDtorKind(static_cast<clang::CXXDtorType>(structor_kind));
+}
+
+llvm::Expected<DWARFDIE>
+SymbolFileDWARF::FindFunctionDefinition(const FunctionCallLabel &label,
+                                        const DWARFDIE &declaration) {
+  auto do_lookup = [this](llvm::StringRef lookup_name) -> DWARFDIE {
+    DWARFDIE found;
+    Module::LookupInfo info(ConstString(lookup_name),
+                            lldb::eFunctionNameTypeFull,
+                            lldb::eLanguageTypeUnknown);
+
+    m_index->GetFunctions(info, *this, {}, [&](DWARFDIE entry) {
+      if (entry.GetAttributeValueAsUnsigned(llvm::dwarf::DW_AT_declaration, 0))
+        return IterationAction::Continue;
+
+      found = entry;
+      return IterationAction::Stop;
+    });
+
+    return found;
+  };
+
+  DWARFDIE definition = do_lookup(label.lookup_name);
+  if (definition.IsValid())
+    return definition;
+
+  // This is not a structor lookup. Nothing else to be done here.
+  if (label.discriminator.empty())
+    return llvm::createStringError(
+        "no definition DIE found in this SymbolFile");
+
+  // We're doing a structor lookup. Maybe we didn't find the structor variant
+  // because the complete object structor was aliased to the base object
+  // structor. Try finding the alias instead.
+  //
+  // TODO: there are other reasons for why a subprogram definition might be
+  // missing. Ideally DWARF would tell us more details about which structor
+  // variant a DIE corresponds to and whether it's an alias.
+  auto subst_or_err =
+      CPlusPlusLanguage::SubstituteStructorAliases_ItaniumMangle(
+          label.lookup_name);
+  if (!subst_or_err)
+    return subst_or_err.takeError();
+
+  definition = do_lookup(*subst_or_err);
+
+  if (!definition.IsValid())
+    return llvm::createStringError(
+        "failed to find definition DIE for structor alias in fallback lookup");
 
   return definition;
 }
 
 llvm::Expected<SymbolContext>
-SymbolFileDWARF::ResolveFunctionCallLabel(const FunctionCallLabel &label) {
+SymbolFileDWARF::ResolveFunctionCallLabel(FunctionCallLabel &label) {
   std::lock_guard<std::recursive_mutex> guard(GetModuleMutex());
 
+  if (!label.discriminator.empty()) {
+    llvm::StringRef from = label.discriminator[0] == 'C' ? "C4" : "D4";
+
+    llvm::StringRef variant = GetItaniumCtorDtorVariant(label.discriminator);
+    if (variant.empty())
+      return llvm::createStringError(
+          "failed to get Itanium variant for discriminator");
+
+    auto subst_or_err = CPlusPlusLanguage::SubstituteStructor_ItaniumMangle(
+        label.lookup_name, from, variant);
+    if (subst_or_err && *subst_or_err)
+      label.lookup_name = subst_or_err->GetStringRef();
+    else
+      llvm::consumeError(subst_or_err.takeError());
+  }
+
   DWARFDIE die = GetDIE(label.symbol_id);
   if (!die.IsValid())
     return llvm::createStringError(
@@ -2519,11 +2621,13 @@ SymbolFileDWARF::ResolveFunctionCallLabel(const 
FunctionCallLabel &label) {
   // Label was created using a declaration DIE. Need to fetch the definition
   // to resolve the function call.
   if (die.GetAttributeValueAsUnsigned(llvm::dwarf::DW_AT_declaration, 0)) {
-    auto definition = FindFunctionDefinition(label);
-    if (!definition)
-      return llvm::createStringError("failed to find definition DIE");
+    auto die_or_err = FindFunctionDefinition(label, die);
+    if (!die_or_err)
+      return llvm::joinErrors(
+          llvm::createStringError("failed to find definition DIE:"),
+          die_or_err.takeError());
 
-    die = std::move(definition);
+    die = std::move(*die_or_err);
   }
 
   SymbolContextList sc_list;
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h 
b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h
index d7db8a3c0869f..608130cd068ce 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h
+++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h
@@ -378,7 +378,9 @@ class SymbolFileDWARF : public SymbolFileCommon {
   /// SymbolFile.
   ///
   /// \returns A valid definition DIE on success.
-  DWARFDIE FindFunctionDefinition(const FunctionCallLabel &label);
+  llvm::Expected<DWARFDIE>
+  FindFunctionDefinition(const FunctionCallLabel &label,
+                         const DWARFDIE &declaration);
 
 protected:
   SymbolFileDWARF(const SymbolFileDWARF &) = delete;
@@ -445,7 +447,7 @@ class SymbolFileDWARF : public SymbolFileCommon {
                                         DIEArray &&variable_dies);
 
   llvm::Expected<SymbolContext>
-  ResolveFunctionCallLabel(const FunctionCallLabel &label) override;
+  ResolveFunctionCallLabel(FunctionCallLabel &label) override;
 
   // Given a die_offset, figure out the symbol context representing that die.
   bool ResolveFunction(const DWARFDIE &die, bool include_inlines,
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp 
b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp
index 9d7452a1988fa..8b8229a7020c5 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp
+++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp
@@ -1603,8 +1603,8 @@ void SymbolFileDWARFDebugMap::GetCompileOptions(
   });
 }
 
-llvm::Expected<SymbolContext> 
SymbolFileDWARFDebugMap::ResolveFunctionCallLabel(
-    const FunctionCallLabel &label) {
+llvm::Expected<SymbolContext>
+SymbolFileDWARFDebugMap::ResolveFunctionCallLabel(FunctionCallLabel &label) {
   const uint64_t oso_idx = GetOSOIndexFromUserID(label.symbol_id);
   SymbolFileDWARF *oso_dwarf = GetSymbolFileByOSOIndex(oso_idx);
   if (!oso_dwarf)
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h 
b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h
index e1f1df23951c6..bce1ed2671af0 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h
+++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h
@@ -145,7 +145,7 @@ class SymbolFileDWARFDebugMap : public SymbolFileCommon {
   GetCompileOptions(std::unordered_map<lldb::CompUnitSP, Args> &args) override;
 
   llvm::Expected<SymbolContext>
-  ResolveFunctionCallLabel(const FunctionCallLabel &label) override;
+  ResolveFunctionCallLabel(FunctionCallLabel &label) override;
 
 protected:
   enum { kHaveInitializedOSOs = (1 << 0), kNumFlags };
diff --git a/lldb/test/API/lang/cpp/abi_tag_structors/Makefile 
b/lldb/test/API/lang/cpp/abi_tag_structors/Makefile
new file mode 100644
index 0000000000000..99998b20bcb05
--- /dev/null
+++ b/lldb/test/API/lang/cpp/abi_tag_structors/Makefile
@@ -0,0 +1,3 @@
+CXX_SOURCES := main.cpp
+
+include Makefile.rules
diff --git a/lldb/test/API/lang/cpp/abi_tag_structors/TestAbiTagStructors.py 
b/lldb/test/API/lang/cpp/abi_tag_structors/TestAbiTagStructors.py
new file mode 100644
index 0000000000000..e01574eb257d9
--- /dev/null
+++ b/lldb/test/API/lang/cpp/abi_tag_structors/TestAbiTagStructors.py
@@ -0,0 +1,90 @@
+"""
+Test that we can call structors/destructors
+annotated (and thus mangled) with ABI tags.
+"""
+
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+
+class AbiTagStructorsTestCase(TestBase):
+    def test_with_structor_linkage_names(self):
+        self.build(dictionary={"CXXFLAGS_EXTRAS": 
"-gstructor-decl-linkage-names"})
+
+        lldbutil.run_to_source_breakpoint(
+            self, "Break here", lldb.SBFileSpec("main.cpp", False)
+        )
+
+        self.expect_expr(
+            "Tagged()",
+            result_type="Tagged",
+            result_children=[ValueCheck(name="x", value="15")],
+        )
+        self.expect_expr(
+            "Tagged(-17)",
+            result_type="Tagged",
+            result_children=[ValueCheck(name="x", value="-17")],
+        )
+        self.expect_expr("t1 = t2", result_type="Tagged")
+
+        self.expect("expr Tagged t3(t1)", error=False)
+        self.expect("expr t1.~Tagged()", error=False)
+
+        self.expect("expr t1.~Tagged()", error=False)
+
+        self.expect(
+            "expression -- struct $Derived : virtual public Tagged { int y; 
$Derived(int val) : Tagged(val) { y = x; } };",
+            error=False,
+        )
+        self.expect(
+            "expression -- struct $Derived2 : virtual public $Derived { int z; 
$Derived2() : $Derived(10) { z = y; } };",
+            error=False,
+        )
+        self.expect_expr(
+            "$Derived2 d; d",
+            result_type="$Derived2",
+            result_children=[
+                ValueCheck(
+                    name="$Derived",
+                    children=[
+                        ValueCheck(
+                            name="Tagged", children=[ValueCheck(name="x", 
value="15")]
+                        ),
+                        ValueCheck(name="y", value="15"),
+                    ],
+                ),
+                ValueCheck(name="z", value="15"),
+            ],
+        )
+
+        # Calls to deleting and base object destructor variants (D0 and D2 in 
Itanium ABI)
+        self.expect_expr(
+            "struct D : public HasVirtualDtor {}; D d; d.func()",
+            result_type="int",
+            result_value="10",
+        )
+
+    def test_no_structor_linkage_names(self):
+        """
+        Test that without linkage names on structor declarations we can't call
+        ABI-tagged structors.
+        """
+        self.build(dictionary={"CXXFLAGS_EXTRAS": 
"-gno-structor-decl-linkage-names"})
+
+        lldbutil.run_to_source_breakpoint(
+            self, "Break here", lldb.SBFileSpec("main.cpp", False)
+        )
+
+        self.expect("expression Tagged(17)", error=True)
+        self.expect("expr Tagged t3(t1)", error=True)
+        self.expect("expr t1.~Tagged()", error=True)
+
+        ## Calls to deleting and base object destructor variants (D0 and D2 in 
Itanium ABI)
+        self.expect(
+            "expression -- struct D : public HasVirtualDtor {}; D d; d.func()",
+            error=True,
+        )
+
+        self.expect("expression -- Derived d(16); d", error=True)
diff --git a/lldb/test/API/lang/cpp/abi_tag_structors/main.cpp 
b/lldb/test/API/lang/cpp/abi_tag_structors/main.cpp
new file mode 100644
index 0000000000000..a5de8ee7f3ed6
--- /dev/null
+++ b/lldb/test/API/lang/cpp/abi_tag_structors/main.cpp
@@ -0,0 +1,44 @@
+#include <cstdio>
+
+struct Tagged {
+  [[gnu::abi_tag("Default")]] Tagged() : x(15) { std::puts(__func__); }
+  [[gnu::abi_tag("Value")]] Tagged(int val) : x(val) { std::puts(__func__); }
+  [[gnu::abi_tag("Copy")]] Tagged(const Tagged &lhs) : x(lhs.x) {
+    std::puts(__func__);
+  }
+  [[gnu::abi_tag("CopyAssign")]] Tagged &operator=(const Tagged &) {
+    std::puts(__func__);
+    return *this;
+  }
+  [[gnu::abi_tag("Dtor")]] ~Tagged() { std::puts(__func__); }
+
+  int x;
+};
+
+struct Base {
+  virtual ~Base() { std::puts(__func__); }
+  virtual int func() { return 5; }
+};
+
+struct HasVirtualDtor : public Base {
+  int func() override { return 10; }
+
+  [[gnu::abi_tag("VirtualDtor")]] ~HasVirtualDtor() override {
+    std::puts(__func__);
+  }
+};
+
+int main() {
+  Tagged t;
+  Tagged t1(10);
+  Tagged t2(t1);
+  t1 = t2;
+
+  Base b;
+  HasVirtualDtor vdtor;
+  vdtor.func();
+
+  std::puts("Break here");
+
+  return 0;
+}
diff --git 
a/lldb/test/API/lang/cpp/expr-definition-in-dylib/TestExprDefinitionInDylib.py 
b/lldb/test/API/lang/cpp/expr-definition-in-dylib/TestExprDefinitionInDylib.py
index 70d7fd096c7cf..c0545c70c84ea 100644
--- 
a/lldb/test/API/lang/cpp/expr-definition-in-dylib/TestExprDefinitionInDylib.py
+++ 
b/lldb/test/API/lang/cpp/expr-definition-in-dylib/TestExprDefinitionInDylib.py
@@ -5,15 +5,14 @@
 
 
 class ExprDefinitionInDylibTestCase(TestBase):
-    NO_DEBUG_INFO_TESTCASE = True
 
     @skipIfWindows
-    def test(self):
+    def test_with_structor_linkage_names(self):
         """
         Tests that we can call functions whose definition
         is in a different LLDB module than it's declaration.
         """
-        self.build()
+        self.build(dictionary={"CXXFLAGS_EXTRAS": 
"-gstructor-decl-linkage-names"})
 
         target = self.dbg.CreateTarget(self.getBuildArtifact("a.out"))
         self.assertTrue(target, VALID_TARGET)
@@ -31,10 +30,71 @@ def test(self):
         )
 
         self.expect_expr("f.method()", result_value="-72", result_type="int")
-        self.expect_expr("Foo()", result_type="Foo")
 
-        # FIXME: mangled name lookup for ABI-tagged ctors fails because
-        # the debug-info AST doesn't have ABI-tag information.
-        self.expect(
-            "expr Bar()", error=True, substrs=["error: Couldn't look up 
symbols"]
+        self.expect_expr("Foo(10)", result_type="Foo")
+
+        self.expect_expr("Base()", result_type="Base")
+
+        self.expect_expr("Bar()", result_type="Bar")
+
+        # Test a more complex setup: expression that has a three bases:
+        # 1. definition is in local module
+        # 2. definition is in different module
+        # 3. definition is in expression context (and has it's own virtual 
base)
+        self.expect_expr(
+            "struct ExprBase : virtual Foo { int z; ExprBase() : Foo(11) { z = 
x; } }; struct Expr : virtual Local, virtual Foo, virtual ExprBase { int w; 
Expr() : Local(), Foo(12), ExprBase() { w = y; } }; Expr tmp; tmp",
+            result_type="Expr",
+            result_children=[
+                ValueCheck(
+                    name="Local",
+                    children=[
+                        ValueCheck(
+                            name="Foo", children=[ValueCheck(name="x", 
value="12")]
+                        ),
+                        ValueCheck(name="y", value="12"),
+                    ],
+                ),
+                ValueCheck(name="Foo", children=[ValueCheck(name="x", 
value="12")]),
+                ValueCheck(
+                    name="ExprBase",
+                    children=[
+                        ValueCheck(
+                            name="Foo", children=[ValueCheck(name="x", 
value="12")]
+                        ),
+                        ValueCheck(name="z", value="12"),
+                    ],
+                ),
+                ValueCheck(name="w", value="12"),
+            ],
         )
+
+    @skipIfWindows
+    def test_no_structor_linkage_names(self):
+        """
+        Tests that if structor declarations don't have linkage names, we can't
+        call ABI-tagged constructors. But non-tagged ones are fine.
+        """
+        self.build(dictionary={"CXXFLAGS_EXTRAS": 
"-gno-structor-decl-linkage-names"})
+
+        target = self.dbg.CreateTarget(self.getBuildArtifact("a.out"))
+        self.assertTrue(target, VALID_TARGET)
+
+        env = self.registerSharedLibrariesWithTarget(target, ["lib"])
+
+        breakpoint = lldbutil.run_break_set_by_file_and_line(
+            self, "main.cpp", line_number("main.cpp", "return")
+        )
+
+        process = target.LaunchSimple(None, env, 
self.get_process_working_directory())
+
+        self.assertIsNotNone(
+            lldbutil.get_one_thread_stopped_at_breakpoint_id(self.process(), 
breakpoint)
+        )
+
+        self.expect_expr("f.method()", result_value="-72", result_type="int")
+
+        self.expect_expr("Foo(10)", result_type="Foo")
+
+        self.expect("Base()", error=True)
+
+        self.expect("Bar()", error=True)
diff --git a/lldb/test/API/lang/cpp/expr-definition-in-dylib/lib.cpp 
b/lldb/test/API/lang/cpp/expr-definition-in-dylib/lib.cpp
index 1a08817f5cda1..aa3921ffe0b10 100644
--- a/lldb/test/API/lang/cpp/expr-definition-in-dylib/lib.cpp
+++ b/lldb/test/API/lang/cpp/expr-definition-in-dylib/lib.cpp
@@ -4,10 +4,14 @@
 
 int Foo::method() { return -72; }
 
-Foo::Foo() { std::puts(__func__); }
+Foo::Foo(int val) : x(val) { std::puts(__func__); }
 
 Foo::~Foo() { std::puts(__func__); }
 
 Bar::Bar() { std::puts(__func__); }
 
 Bar::~Bar() { std::puts(__func__); }
+
+Base::Base() { std::puts(__func__); }
+
+Base::~Base() { std::puts(__func__); }
diff --git a/lldb/test/API/lang/cpp/expr-definition-in-dylib/lib.h 
b/lldb/test/API/lang/cpp/expr-definition-in-dylib/lib.h
index 5ec227946cba0..513c9a0f9c87c 100644
--- a/lldb/test/API/lang/cpp/expr-definition-in-dylib/lib.h
+++ b/lldb/test/API/lang/cpp/expr-definition-in-dylib/lib.h
@@ -3,11 +3,18 @@
 
 struct Foo {
   int method();
-  Foo();
+  Foo(int val);
   ~Foo();
+
+  int x;
+};
+
+struct Base {
+  [[gnu::abi_tag("BaseCtor")]] Base();
+  [[gnu::abi_tag("BaseDtor")]] ~Base();
 };
 
-struct Bar {
+struct Bar : public Base {
   [[gnu::abi_tag("Ctor")]] Bar();
   [[gnu::abi_tag("Dtor")]] ~Bar();
 };
diff --git a/lldb/test/API/lang/cpp/expr-definition-in-dylib/main.cpp 
b/lldb/test/API/lang/cpp/expr-definition-in-dylib/main.cpp
index 4d6bece21ecac..ff43190b991cc 100644
--- a/lldb/test/API/lang/cpp/expr-definition-in-dylib/main.cpp
+++ b/lldb/test/API/lang/cpp/expr-definition-in-dylib/main.cpp
@@ -1,7 +1,18 @@
 #include "lib.h"
 
+struct Local : public virtual Foo {
+  Local();
+  ~Local();
+  int y;
+};
+
+Local::Local() : Foo(5) { y = x; }
+Local::~Local() {}
+
 int main() {
-  Foo f;
-  Bar b;
+  Foo f(5);
+  Base b1;
+  Bar b2;
+  Local l1;
   return f.method();
 }
diff --git a/lldb/unittests/Expression/ExpressionTest.cpp 
b/lldb/unittests/Expression/ExpressionTest.cpp
index 12f6dd515fd11..ceb567c28ab99 100644
--- a/lldb/unittests/Expression/ExpressionTest.cpp
+++ b/lldb/unittests/Expression/ExpressionTest.cpp
@@ -23,15 +23,15 @@ struct LabelTestCase {
 
 static LabelTestCase g_label_test_cases[] = {
     // Failure modes
-    {"bar:0x0:0x0:_Z3foov",
+    {"bar:blah:0x0:0x0:_Z3foov",
      {},
      {"expected function call label prefix '$__lldb_func' but found 'bar' "
       "instead."}},
-    {"$__lldb_func :0x0:0x0:_Z3foov",
+    {"$__lldb_func :blah:0x0:0x0:_Z3foov",
      {},
      {"expected function call label prefix '$__lldb_func' but found "
       "'$__lldb_func ' instead."}},
-    {"$__lldb_funcc:0x0:0x0:_Z3foov",
+    {"$__lldb_funcc:blah:0x0:0x0:_Z3foov",
      {},
      {"expected function call label prefix '$__lldb_func' but found "
       "'$__lldb_funcc' instead."}},
@@ -39,47 +39,52 @@ static LabelTestCase g_label_test_cases[] = {
     {"foo", {}, {"malformed function call label."}},
     {"$__lldb_func", {}, {"malformed function call label."}},
     {"$__lldb_func:", {}, {"malformed function call label."}},
-    {"$__lldb_func:0x0:0x0", {}, {"malformed function call label."}},
-    {"$__lldb_func:abc:0x0:_Z3foov",
+    {"$__lldb_func:blah", {}, {"malformed function call label."}},
+    {"$__lldb_func:blah:0x0", {}, {"malformed function call label."}},
+    {"$__lldb_func:111:0x0:0x0", {}, {"malformed function call label."}},
+    {"$__lldb_func:111:abc:0x0:_Z3foov",
      {},
      {"failed to parse module ID from 'abc'."}},
-    {"$__lldb_func:-1:0x0:_Z3foov",
+    {"$__lldb_func:111:-1:0x0:_Z3foov",
      {},
      {"failed to parse module ID from '-1'."}},
-    {"$__lldb_func:0x0invalid:0x0:_Z3foov",
+    {"$__lldb_func:111:0x0invalid:0x0:_Z3foov",
      {},
      {"failed to parse module ID from '0x0invalid'."}},
-    {"$__lldb_func:0x0 :0x0:_Z3foov",
+    {"$__lldb_func:111:0x0 :0x0:_Z3foov",
      {},
      {"failed to parse module ID from '0x0 '."}},
-    {"$__lldb_func:0x0:abc:_Z3foov",
+    {"$__lldb_func:blah:0x0:abc:_Z3foov",
      {},
      {"failed to parse symbol ID from 'abc'."}},
-    {"$__lldb_func:0x5:-1:_Z3foov",
+    {"$__lldb_func:blah:0x5:-1:_Z3foov",
      {},
      {"failed to parse symbol ID from '-1'."}},
-    {"$__lldb_func:0x5:0x0invalid:_Z3foov",
+    {"$__lldb_func:blah:0x5:0x0invalid:_Z3foov",
      {},
      {"failed to parse symbol ID from '0x0invalid'."}},
-    {"$__lldb_func:0x5:0x0 :_Z3foov",
+    {"$__lldb_func:blah:0x5:0x0 :_Z3foov",
      {},
      {"failed to parse symbol ID from '0x0 '."}},
-    {"$__lldb_func:0x0:0x0:_Z3foov",
+    {"$__lldb_func:blah:0x0:0x0:_Z3foov",
      {
+         /*.discriminator=*/"blah",
          /*.module_id=*/0x0,
          /*.symbol_id=*/0x0,
          /*.lookup_name=*/"_Z3foov",
      },
      {}},
-    {"$__lldb_func:0x0:0x0:abc:def:::a",
+    {"$__lldb_func::0x0:0x0:abc:def:::a",
      {
+         /*.discriminator=*/"",
          /*.module_id=*/0x0,
          /*.symbol_id=*/0x0,
          /*.lookup_name=*/"abc:def:::a",
      },
      {}},
-    {"$__lldb_func:0xd2:0xf0:$__lldb_func",
+    {"$__lldb_func:0x45:0xd2:0xf0:$__lldb_func",
      {
+         /*.discriminator=*/"0x45",
          /*.module_id=*/0xd2,
          /*.symbol_id=*/0xf0,
          /*.lookup_name=*/"$__lldb_func",
@@ -106,6 +111,7 @@ TEST_P(ExpressionTestFixture, FunctionCallLabel) {
   EXPECT_EQ(decoded_or_err->toString(), encoded);
   EXPECT_EQ(label_str, encoded);
 
+  EXPECT_EQ(decoded_or_err->discriminator, label.discriminator);
   EXPECT_EQ(decoded_or_err->module_id, label.module_id);
   EXPECT_EQ(decoded_or_err->symbol_id, label.symbol_id);
   EXPECT_EQ(decoded_or_err->lookup_name, label.lookup_name);
@@ -113,6 +119,7 @@ TEST_P(ExpressionTestFixture, FunctionCallLabel) {
   auto roundtrip_or_err = FunctionCallLabel::fromString(label_str);
   EXPECT_THAT_EXPECTED(roundtrip_or_err, llvm::Succeeded());
 
+  EXPECT_EQ(roundtrip_or_err->discriminator, label.discriminator);
   EXPECT_EQ(roundtrip_or_err->module_id, label.module_id);
   EXPECT_EQ(roundtrip_or_err->symbol_id, label.symbol_id);
   EXPECT_EQ(roundtrip_or_err->lookup_name, label.lookup_name);
diff --git a/lldb/unittests/Symbol/TestTypeSystemClang.cpp 
b/lldb/unittests/Symbol/TestTypeSystemClang.cpp
index b993b82612497..f673cceae00dd 100644
--- a/lldb/unittests/Symbol/TestTypeSystemClang.cpp
+++ b/lldb/unittests/Symbol/TestTypeSystemClang.cpp
@@ -1150,12 +1150,12 @@ TEST_F(TestTypeSystemClang, AsmLabel_CtorDtor) {
       is_explicit, is_attr_used, is_artificial);
 
   auto *ctor = m_ast->AddMethodToCXXRecordType(
-      t.GetOpaqueQualType(), "S", /*asm_label=*/"$__lldb_func:0x0:0x0:S",
+      t.GetOpaqueQualType(), "S", /*asm_label=*/"$__lldb_func::0x0:0x0:S",
       function_type, lldb::AccessType::eAccessPublic, is_virtual, is_static,
       is_inline, is_explicit, is_attr_used, is_artificial);
 
   auto *dtor = m_ast->AddMethodToCXXRecordType(
-      t.GetOpaqueQualType(), "~S", /*asm_label=*/"$__lldb_func:0x0:0x0:~S",
+      t.GetOpaqueQualType(), "~S", /*asm_label=*/"$__lldb_func::0x0:0x0:~S",
       function_type, lldb::AccessType::eAccessPublic, is_virtual, is_static,
       is_inline, is_explicit, is_attr_used, is_artificial);
 
@@ -1181,11 +1181,11 @@ TEST_F(TestTypeSystemClang, AsmLabel_CtorDtor) {
   EXPECT_STREQ(llvm::GlobalValue::dropLLVMManglingEscape(
                    m_ast->DeclGetMangledName(ctor).GetStringRef())
                    .data(),
-               "$__lldb_func:0x0:0x0:S");
+               "$__lldb_func:C0:0x0:0x0:S");
   EXPECT_STREQ(llvm::GlobalValue::dropLLVMManglingEscape(
                    m_ast->DeclGetMangledName(dtor).GetStringRef())
                    .data(),
-               "$__lldb_func:0x0:0x0:~S");
+               "$__lldb_func:D1:0x0:0x0:~S");
 }
 
 struct AsmLabelTestCase {
@@ -1215,10 +1215,10 @@ class TestTypeSystemClangAsmLabel
 };
 
 static AsmLabelTestCase g_asm_label_test_cases[] = {
-    {/*mangled=*/"$__lldb_func:0x0:0x0:_Z3foov",
+    {/*mangled=*/"$__lldb_func::0x0:0x0:_Z3foov",
      /*expected=*/"_Z3foov"},
-    {/*mangled=*/"$__lldb_func:0x0:0x0:foo",
-     /*expected=*/"$__lldb_func:0x0:0x0:foo"},
+    {/*mangled=*/"$__lldb_func::0x0:0x0:foo",
+     /*expected=*/"$__lldb_func::0x0:0x0:foo"},
     {/*mangled=*/"foo",
      /*expected=*/"foo"},
     {/*mangled=*/"_Z3foov",

_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to