[clang] [llvm] Reland "[DebugMetadata][DwarfDebug] Support function-local types in lexical block scopes (4/7)" (PR #165032)

Vladislav Dzhidzhoev via llvm-commits llvm-commits at lists.llvm.org
Fri Oct 24 12:39:51 PDT 2025


https://github.com/dzhidzhoev created https://github.com/llvm/llvm-project/pull/165032

This is an attempt to merge https://reviews.llvm.org/D144006 with LTO fix.

The last merge attempt was https://github.com/llvm/llvm-project/pull/75385.
The issue with it was investigated in https://github.com/llvm/llvm-project/pull/75385#issuecomment-2386684121.
If the same (in the sense of ODR identifier/ODR uniquing rules) local type is present in two modules, and these modules are linked together, the type gets uniqued. A DIType, that happens to be loaded first, survives linking, and the references on other types with the same ODR identifier from the modules loaded later are replaced with the references on the DIType loaded first. Since defintion subprograms, in scope of which these types are located, are not deduplicated, the linker output may contain multiple DISubprogram's having the same (uniqued) type in their retainedNodes lists.
Further compilation of such modules causes crashes.

To tackle that,
* previous solution to handle LTO linking with local types in retainedNodes is removed (cloneLocalTypes() function),
* for each loaded distinct (definition) DISubprogram, its retainedNodes list is scanned after loading, and DITypes with a scope of another subprogram are removed. If something from a Function corresponding to the DISubprogram references uniqued type, we rely on cross-CU links.

With this approach, clang builds without crashes in FullLTO (which is not the case for https://github.com/llvm/llvm-project/pull/75385).

Additionally:
* a check is added to Verifier to report about local types located in a wrong retainedNodes list,
* DIBuilder's methods for type creation are updated, as https://reviews.llvm.org/D144006 has gotten slightly out-of-date.

Commit https://github.com/llvm/llvm-project/pull/75385 and the new changes are separate commits to simplify review process.

>From 8a396cfcd05dd98cf76388ae574b1ad78e44eaca Mon Sep 17 00:00:00 2001
From: Jeremy Morse <jeremy.morse at sony.com>
Date: Fri, 6 Dec 2024 16:42:28 +0000
Subject: [PATCH 1/2] [DebugMetadata][DwarfDebug] Support function-local types
 in lexical block scopes (4/7)

RFC https://discourse.llvm.org/t/rfc-dwarfdebug-fix-and-improve-handling-imported-entities-types-and-static-local-in-subprogram-and-lexical-block-scopes/68544

Similar to imported declarations, the patch tracks function-local types in
DISubprogram's 'retainedNodes' field. DwarfDebug is adjusted in accordance with
the aforementioned metadata change and provided a support of function-local
types scoped within a lexical block.

The patch assumes that DICompileUnit's 'enums field' no longer tracks local
types and DwarfDebug would assert if any locally-scoped types get placed there.

Reviewed By: jmmartinez
Authored-by: Kristina Bessonova <kbessonova at accesssoftek.com>
Differential Revision: https://reviews.llvm.org/D144006

(cherry picked from commit 2953d4adcb36c28d12a3e3f8febf0995ef22c0d9)
Signed-off-by: Jeremy Morse <jeremy.morse at sony.com>

Conflicts:
	llvm/lib/IR/DIBuilder.cpp
	llvm/lib/IR/DebugInfo.cpp
        llvm/lib/Transforms/Utils/CloneFunction.cpp
---
 clang/test/DebugInfo/CXX/access.cpp           |   2 +-
 clang/test/DebugInfo/CXX/anon-union-vars.cpp  |  12 +-
 clang/test/DebugInfo/CXX/codeview-unnamed.cpp | 110 +++--
 .../CXX/gline-tables-only-codeview.cpp        |   4 +-
 clang/test/DebugInfo/CXX/lambda-this.cpp      |   4 +-
 .../test/DebugInfo/Generic/codeview-unnamed.c |  16 +-
 clang/test/DebugInfo/Generic/unused-types.c   |  16 +-
 clang/test/DebugInfo/Generic/unused-types.cpp |  14 +-
 llvm/include/llvm/IR/DIBuilder.h              |   6 +-
 llvm/lib/Bitcode/Reader/MetadataLoader.cpp    | 115 +++--
 .../CodeGen/AsmPrinter/DwarfCompileUnit.cpp   |  64 ++-
 .../lib/CodeGen/AsmPrinter/DwarfCompileUnit.h |  16 +-
 llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp    |  13 +-
 llvm/lib/IR/DIBuilder.cpp                     |  40 +-
 llvm/lib/IR/DebugInfo.cpp                     |   2 +
 llvm/lib/IR/Verifier.cpp                      |   6 +-
 llvm/lib/Transforms/Utils/CloneFunction.cpp   |  17 +-
 llvm/test/Bitcode/clone-local-types.ll        |  46 ++
 llvm/test/Bitcode/upgrade-cu-locals.ll        |  68 +--
 llvm/test/Bitcode/upgrade-cu-locals.ll.bc     | Bin 2688 -> 2780 bytes
 .../DebugInfo/Generic/inlined-local-type.ll   | 128 ++++++
 .../Generic/lexical-block-retained-types.ll   |  55 +++
 .../DebugInfo/Generic/lexical-block-types.ll  | 425 ++++++++++++++++++
 .../Generic/verifier-invalid-disubprogram.ll  |   2 +-
 .../X86/local-type-as-template-parameter.ll   | 161 +++++++
 .../split-dwarf-local-import.ll               |   1 -
 .../split-dwarf-local-import2.ll              |   1 -
 .../split-dwarf-local-import3.ll              |   0
 .../Transforms/Utils/CloningTest.cpp          | 105 +++++
 29 files changed, 1254 insertions(+), 195 deletions(-)
 create mode 100644 llvm/test/Bitcode/clone-local-types.ll
 create mode 100644 llvm/test/DebugInfo/Generic/inlined-local-type.ll
 create mode 100644 llvm/test/DebugInfo/Generic/lexical-block-retained-types.ll
 create mode 100644 llvm/test/DebugInfo/Generic/lexical-block-types.ll
 create mode 100644 llvm/test/DebugInfo/X86/local-type-as-template-parameter.ll
 rename llvm/test/DebugInfo/{Generic => X86}/split-dwarf-local-import.ll (98%)
 rename llvm/test/DebugInfo/{Generic => X86}/split-dwarf-local-import2.ll (98%)
 rename llvm/test/DebugInfo/{Generic => X86}/split-dwarf-local-import3.ll (100%)

diff --git a/clang/test/DebugInfo/CXX/access.cpp b/clang/test/DebugInfo/CXX/access.cpp
index 9f2c044843d0f..7c0bf8ccb0384 100644
--- a/clang/test/DebugInfo/CXX/access.cpp
+++ b/clang/test/DebugInfo/CXX/access.cpp
@@ -18,9 +18,9 @@ class B : public A {
   static int public_static;
 
 protected:
+  // CHECK-DAG: !DIDerivedType(tag: DW_TAG_typedef, name: "prot_using",{{.*}} line: [[@LINE+3]],{{.*}} flags: DIFlagProtected)
   // CHECK-DAG: !DIDerivedType(tag: DW_TAG_typedef, name: "prot_typedef",{{.*}} line: [[@LINE+1]],{{.*}} flags: DIFlagProtected)
   typedef int prot_typedef;
-  // CHECK-DAG: !DIDerivedType(tag: DW_TAG_typedef, name: "prot_using",{{.*}} line: [[@LINE+1]],{{.*}} flags: DIFlagProtected)
   using prot_using = prot_typedef;
   prot_using prot_member;
 
diff --git a/clang/test/DebugInfo/CXX/anon-union-vars.cpp b/clang/test/DebugInfo/CXX/anon-union-vars.cpp
index 3aca4e199ab8d..27e6c6850e013 100644
--- a/clang/test/DebugInfo/CXX/anon-union-vars.cpp
+++ b/clang/test/DebugInfo/CXX/anon-union-vars.cpp
@@ -51,13 +51,13 @@ void instantiate(int x) {
 // CHECK: !DIGlobalVariable(name: "b",{{.*}} file: [[FILE]], line: 6,{{.*}} isLocal: true, isDefinition: true
 // CHECK: !DIGlobalVariable(name: "result", {{.*}} isLocal: false, isDefinition: true
 // CHECK: !DIGlobalVariable(name: "value", {{.*}} isLocal: false, isDefinition: true
-// CHECK: !DILocalVariable(name: "i", {{.*}}, flags: DIFlagArtificial
-// CHECK: !DILocalVariable(name: "c", {{.*}}, flags: DIFlagArtificial
-// CHECK: !DILocalVariable(
-// CHECK-NOT: name:
-// CHECK: type: ![[UNION:[0-9]+]]
-// CHECK: ![[UNION]] = distinct !DICompositeType(tag: DW_TAG_union_type,
+// CHECK: ![[UNION:[0-9]+]] = distinct !DICompositeType(tag: DW_TAG_union_type,
 // CHECK-NOT: name:
 // CHECK: elements
 // CHECK: !DIDerivedType(tag: DW_TAG_member, name: "i", scope: ![[UNION]],
 // CHECK: !DIDerivedType(tag: DW_TAG_member, name: "c", scope: ![[UNION]],
+// CHECK: !DILocalVariable(name: "i", {{.*}}, flags: DIFlagArtificial
+// CHECK: !DILocalVariable(name: "c", {{.*}}, flags: DIFlagArtificial
+// CHECK: !DILocalVariable(
+// CHECK-NOT: name:
+// CHECK: type: ![[UNION]]
diff --git a/clang/test/DebugInfo/CXX/codeview-unnamed.cpp b/clang/test/DebugInfo/CXX/codeview-unnamed.cpp
index 30815bda020ea..b625a80313c38 100644
--- a/clang/test/DebugInfo/CXX/codeview-unnamed.cpp
+++ b/clang/test/DebugInfo/CXX/codeview-unnamed.cpp
@@ -4,6 +4,60 @@
 
 int main(int argc, char* argv[], char* arge[]) {
   //
+  // LINUX:      [[TYPE_OF_ONE:![0-9]+]] = distinct !DICompositeType(
+  // LINUX-SAME:     tag: DW_TAG_structure_type
+  // LINUX-NOT:      name:
+  // LINUX-NOT:      identifier:
+  // LINUX-SAME:     )
+  //
+  // MSVC:       [[TYPE_OF_ONE:![0-9]+]] = distinct !DICompositeType
+  // MSVC-SAME:      tag: DW_TAG_structure_type
+  // MSVC-SAME:      name: "<unnamed-type-one>"
+  // MSVC-SAME:      identifier: ".?AU<unnamed-type-one>@?1??main@@9@"
+  // MSVC-SAME:      )
+
+
+  //
+  // LINUX:      [[TYPE_OF_TWO:![0-9]+]] = distinct !DICompositeType(
+  // LINUX-SAME:     tag: DW_TAG_structure_type
+  // LINUX-NOT:      name:
+  // LINUX-NOT:      identifier:
+  // LINUX-SAME:     )
+  //
+  // MSVC:       [[TYPE_OF_TWO:![0-9]+]] = distinct !DICompositeType
+  // MSVC-SAME:      tag: DW_TAG_structure_type
+  // MSVC-SAME:      name: "<unnamed-type-two>"
+  // MSVC-SAME:      identifier: ".?AU<unnamed-type-two>@?2??main@@9@"
+  // MSVC-SAME:      )
+
+
+  //
+  // LINUX:      [[TYPE_OF_THREE:![0-9]+]] = distinct !DICompositeType(
+  // LINUX-SAME:     tag: DW_TAG_structure_type
+  // LINUX-SAME:     name: "named"
+  // LINUX-NOT:      identifier:
+  // LINUX-SAME:     )
+  //
+  // MSVC:       [[TYPE_OF_THREE:![0-9]+]] = distinct !DICompositeType
+  // MSVC-SAME:      tag: DW_TAG_structure_type
+  // MSVC-SAME:      name: "named"
+  // MSVC-SAME:      identifier: ".?AUnamed@?1??main@@9@"
+  // MSVC-SAME:      )
+
+  //
+  // LINUX:      [[TYPE_OF_FOUR:![0-9]+]] = distinct !DICompositeType(
+  // LINUX-SAME:     tag: DW_TAG_class_type
+  // LINUX-NOT:      name:
+  // LINUX-NOT:      identifier:
+  // LINUX-SAME:     )
+  //
+  // MSVC:       [[TYPE_OF_FOUR:![0-9]+]] = distinct !DICompositeType
+  // MSVC-SAME:      tag: DW_TAG_class_type
+  // MSVC-SAME:      name: "<lambda_0>"
+  // MSVC-SAME:      identifier: ".?AV<lambda_0>@?0??main@@9@"
+  // MSVC-SAME:      )
+
+
   // In CodeView, the LF_MFUNCTION entry for "bar()" refers to the forward
   // reference of the unnamed struct. Visual Studio requires a unique
   // identifier to match the LF_STRUCTURE forward reference to the definition.
@@ -11,21 +65,11 @@ int main(int argc, char* argv[], char* arge[]) {
   struct { void bar() {} } one;
   //
   // LINUX:      !{{[0-9]+}} = !DILocalVariable(name: "one"
-  // LINUX-SAME:     type: [[TYPE_OF_ONE:![0-9]+]]
-  // LINUX-SAME:     )
-  // LINUX:      [[TYPE_OF_ONE]] = distinct !DICompositeType(
-  // LINUX-SAME:     tag: DW_TAG_structure_type
-  // LINUX-NOT:      name:
-  // LINUX-NOT:      identifier:
+  // LINUX-SAME:     type: [[TYPE_OF_ONE]]
   // LINUX-SAME:     )
   //
   // MSVC:       !{{[0-9]+}} = !DILocalVariable(name: "one"
-  // MSVC-SAME:      type: [[TYPE_OF_ONE:![0-9]+]]
-  // MSVC-SAME:      )
-  // MSVC:       [[TYPE_OF_ONE]] = distinct !DICompositeType
-  // MSVC-SAME:      tag: DW_TAG_structure_type
-  // MSVC-SAME:      name: "<unnamed-type-one>"
-  // MSVC-SAME:      identifier: ".?AU<unnamed-type-one>@?1??main@@9@"
+  // MSVC-SAME:      type: [[TYPE_OF_ONE]]
   // MSVC-SAME:      )
 
 
@@ -37,21 +81,11 @@ int main(int argc, char* argv[], char* arge[]) {
   int decltype(two)::*ptr2unnamed = &decltype(two)::bar;
   //
   // LINUX:      !{{[0-9]+}} = !DILocalVariable(name: "two"
-  // LINUX-SAME:     type: [[TYPE_OF_TWO:![0-9]+]]
-  // LINUX-SAME:     )
-  // LINUX:      [[TYPE_OF_TWO]] = distinct !DICompositeType(
-  // LINUX-SAME:     tag: DW_TAG_structure_type
-  // LINUX-NOT:      name:
-  // LINUX-NOT:      identifier:
+  // LINUX-SAME:     type: [[TYPE_OF_TWO]]
   // LINUX-SAME:     )
   //
   // MSVC:       !{{[0-9]+}} = !DILocalVariable(name: "two"
-  // MSVC-SAME:      type: [[TYPE_OF_TWO:![0-9]+]]
-  // MSVC-SAME:      )
-  // MSVC:       [[TYPE_OF_TWO]] = distinct !DICompositeType
-  // MSVC-SAME:      tag: DW_TAG_structure_type
-  // MSVC-SAME:      name: "<unnamed-type-two>"
-  // MSVC-SAME:      identifier: ".?AU<unnamed-type-two>@?2??main@@9@"
+  // MSVC-SAME:      type: [[TYPE_OF_TWO]]
   // MSVC-SAME:      )
 
 
@@ -62,21 +96,11 @@ int main(int argc, char* argv[], char* arge[]) {
   struct named { int bar; int named::* p2mem; } three = { 42, &named::bar };
   //
   // LINUX:      !{{[0-9]+}} = !DILocalVariable(name: "three"
-  // LINUX-SAME:     type: [[TYPE_OF_THREE:![0-9]+]]
-  // LINUX-SAME:     )
-  // LINUX:      [[TYPE_OF_THREE]] = distinct !DICompositeType(
-  // LINUX-SAME:     tag: DW_TAG_structure_type
-  // LINUX-SAME:     name: "named"
-  // LINUX-NOT:      identifier:
+  // LINUX-SAME:     type: [[TYPE_OF_THREE]]
   // LINUX-SAME:     )
   //
   // MSVC:       !{{[0-9]+}} = !DILocalVariable(name: "three"
-  // MSVC-SAME:      type: [[TYPE_OF_THREE:![0-9]+]]
-  // MSVC-SAME:      )
-  // MSVC:       [[TYPE_OF_THREE]] = distinct !DICompositeType
-  // MSVC-SAME:      tag: DW_TAG_structure_type
-  // MSVC-SAME:      name: "named"
-  // MSVC-SAME:      identifier: ".?AUnamed@?1??main@@9@"
+  // MSVC-SAME:      type: [[TYPE_OF_THREE]]
   // MSVC-SAME:      )
 
 
@@ -88,21 +112,11 @@ int main(int argc, char* argv[], char* arge[]) {
   auto four = [argc](int i) -> int { return argc == i ? 1 : 0; };
   //
   // LINUX:      !{{[0-9]+}} = !DILocalVariable(name: "four"
-  // LINUX-SAME:     type: [[TYPE_OF_FOUR:![0-9]+]]
-  // LINUX-SAME:     )
-  // LINUX:      [[TYPE_OF_FOUR]] = distinct !DICompositeType(
-  // LINUX-SAME:     tag: DW_TAG_class_type
-  // LINUX-NOT:      name:
-  // LINUX-NOT:      identifier:
+  // LINUX-SAME:     type: [[TYPE_OF_FOUR]]
   // LINUX-SAME:     )
   //
   // MSVC:       !{{[0-9]+}} = !DILocalVariable(name: "four"
-  // MSVC-SAME:      type: [[TYPE_OF_FOUR:![0-9]+]]
-  // MSVC-SAME:      )
-  // MSVC:       [[TYPE_OF_FOUR]] = distinct !DICompositeType
-  // MSVC-SAME:      tag: DW_TAG_class_type
-  // MSVC-SAME:      name: "<lambda_0>"
-  // MSVC-SAME:      identifier: ".?AV<lambda_0>@?0??main@@9@"
+  // MSVC-SAME:      type: [[TYPE_OF_FOUR]]
   // MSVC-SAME:      )
 
   return 0;
diff --git a/clang/test/DebugInfo/CXX/gline-tables-only-codeview.cpp b/clang/test/DebugInfo/CXX/gline-tables-only-codeview.cpp
index 6b9c9a243decd..122e4aa62ea7d 100644
--- a/clang/test/DebugInfo/CXX/gline-tables-only-codeview.cpp
+++ b/clang/test/DebugInfo/CXX/gline-tables-only-codeview.cpp
@@ -51,9 +51,9 @@ void test() {
   // CHECK-SAME:                              name: "<lambda_2_1>",
   c.lambda_params();
 
-  // CHECK: !DISubprogram(name: "operator()", scope: ![[LAMBDA1:[0-9]+]],
-  // CHECK: ![[LAMBDA1]] = !DICompositeType(tag: DW_TAG_class_type,
+  // CHECK: ![[LAMBDA1:[0-9]+]] = !DICompositeType(tag: DW_TAG_class_type,
   // CHECK-SAME:                            name: "<lambda_1>",
   // CHECK-SAME:                            flags: DIFlagFwdDecl
+  // CHECK: !DISubprogram(name: "operator()", scope: ![[LAMBDA1]],
   c.lambda2();
 }
diff --git a/clang/test/DebugInfo/CXX/lambda-this.cpp b/clang/test/DebugInfo/CXX/lambda-this.cpp
index 019d09c48f858..1df210953ac2e 100644
--- a/clang/test/DebugInfo/CXX/lambda-this.cpp
+++ b/clang/test/DebugInfo/CXX/lambda-this.cpp
@@ -13,10 +13,10 @@ void D::d(int x) {
 }
 
 // CHECK: ![[D:[0-9]+]] = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "D",
-// CHECK: ![[POINTER:.*]] = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: ![[D]], size: 64)
 // CHECK: !DIDerivedType(tag: DW_TAG_member, name: "this",
 // CHECK-SAME:           line: 11
-// CHECK-SAME:           baseType: ![[POINTER]]
+// CHECK-SAME:           baseType: ![[POINTER:[0-9]+]]
 // CHECK-SAME:           size: 64
 // CHECK-NOT:            offset: 0
 // CHECK-SAME:           ){{$}}
+// CHECK: ![[POINTER]] = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: ![[D]], size: 64)
diff --git a/clang/test/DebugInfo/Generic/codeview-unnamed.c b/clang/test/DebugInfo/Generic/codeview-unnamed.c
index 0df6e1a0419bb..7b88f53a92e42 100644
--- a/clang/test/DebugInfo/Generic/codeview-unnamed.c
+++ b/clang/test/DebugInfo/Generic/codeview-unnamed.c
@@ -8,23 +8,23 @@ int main(int argc, char* argv[], char* arge[]) {
   //
   struct { int bar; } one = {42};
   //
-  // LINUX:      !{{[0-9]+}} = !DILocalVariable(name: "one"
-  // LINUX-SAME:     type: [[TYPE_OF_ONE:![0-9]+]]
-  // LINUX-SAME:     )
-  // LINUX:      [[TYPE_OF_ONE]] = distinct !DICompositeType(
+  // LINUX:      [[TYPE_OF_ONE:![0-9]+]] = distinct !DICompositeType(
   // LINUX-SAME:     tag: DW_TAG_structure_type
   // LINUX-NOT:      name:
   // LINUX-NOT:      identifier:
   // LINUX-SAME:     )
+  // LINUX:      !{{[0-9]+}} = !DILocalVariable(name: "one"
+  // LINUX-SAME:     type: [[TYPE_OF_ONE]]
+  // LINUX-SAME:     )
   //
-  // MSVC:       !{{[0-9]+}} = !DILocalVariable(name: "one"
-  // MSVC-SAME:      type: [[TYPE_OF_ONE:![0-9]+]]
-  // MSVC-SAME:      )
-  // MSVC:       [[TYPE_OF_ONE]] = distinct !DICompositeType
+  // MSVC:       [[TYPE_OF_ONE:![0-9]+]] = distinct !DICompositeType
   // MSVC-SAME:      tag: DW_TAG_structure_type
   // MSVC-NOT:       name:
   // MSVC-NOT:       identifier:
   // MSVC-SAME:      )
+  // MSVC:       !{{[0-9]+}} = !DILocalVariable(name: "one"
+  // MSVC-SAME:      type: [[TYPE_OF_ONE]]
+  // MSVC-SAME:      )
 
   return 0;
 }
diff --git a/clang/test/DebugInfo/Generic/unused-types.c b/clang/test/DebugInfo/Generic/unused-types.c
index 3e9f7b07658e3..31d608d92a06b 100644
--- a/clang/test/DebugInfo/Generic/unused-types.c
+++ b/clang/test/DebugInfo/Generic/unused-types.c
@@ -18,13 +18,15 @@ void quux(void) {
 // CHECK: !DICompileUnit{{.+}}retainedTypes: [[RETTYPES:![0-9]+]]
 // CHECK: [[TYPE0:![0-9]+]] = !DICompositeType(tag: DW_TAG_enumeration_type, name: "bar"
 // CHECK: [[TYPE1:![0-9]+]] = !DIEnumerator(name: "BAR"
-// CHECK: [[TYPE2:![0-9]+]] = !DICompositeType(tag: DW_TAG_enumeration_type, name: "z"
-// CHECK: [[TYPE3:![0-9]+]] = !DIEnumerator(name: "Z"
-// CHECK: [[RETTYPES]] = !{[[TYPE4:![0-9]+]], [[TYPE5:![0-9]+]], [[TYPE0]], [[TYPE6:![0-9]+]], {{![0-9]+}}, [[TYPE7:![0-9]+]], [[TYPE2]], [[TYPE8:![0-9]+]]}
-// CHECK: [[TYPE4]] = !DIDerivedType(tag: DW_TAG_typedef, name: "my_int"
-// CHECK: [[TYPE5]] = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "foo"
-// CHECK: [[TYPE6]] = distinct !DICompositeType(tag: DW_TAG_union_type, name: "baz"
-// CHECK: [[TYPE7]] = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "y"
+// CHECK: [[RETTYPES]] = !{[[TYPE2:![0-9]+]], [[TYPE3:![0-9]+]], [[TYPE0]], [[TYPE4:![0-9]+]], {{![0-9]+}}}
+// CHECK: [[TYPE2]] = !DIDerivedType(tag: DW_TAG_typedef, name: "my_int"
+// CHECK: [[TYPE3]] = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "foo"
+// CHECK: [[TYPE4]] = distinct !DICompositeType(tag: DW_TAG_union_type, name: "baz"
+// CHECK: [[SP:![0-9]+]] = distinct !DISubprogram(name: "quux", {{.*}}, retainedNodes: [[SPRETNODES:![0-9]+]]
+// CHECK: [[SPRETNODES]] = !{[[TYPE5:![0-9]+]], [[TYPE6:![0-9]+]], [[TYPE8:![0-9]+]]}
+// CHECK: [[TYPE5]] = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "y"
+// CHECK: [[TYPE6]] = !DICompositeType(tag: DW_TAG_enumeration_type, name: "z"
+// CHECK: [[TYPE7:![0-9]+]] = !DIEnumerator(name: "Z"
 // CHECK: [[TYPE8]] = distinct !DICompositeType(tag: DW_TAG_union_type, name: "w"
 
 // Check that debug info is not emitted for the typedef, struct, enum, and
diff --git a/clang/test/DebugInfo/Generic/unused-types.cpp b/clang/test/DebugInfo/Generic/unused-types.cpp
index 023cac159faa4..5b01c6dbb3941 100644
--- a/clang/test/DebugInfo/Generic/unused-types.cpp
+++ b/clang/test/DebugInfo/Generic/unused-types.cpp
@@ -13,12 +13,14 @@ void quux() {
 // CHECK: !DICompileUnit{{.+}}retainedTypes: [[RETTYPES:![0-9]+]]
 // CHECK: [[TYPE0:![0-9]+]] = !DICompositeType(tag: DW_TAG_enumeration_type, name: "baz"
 // CHECK: [[TYPE1:![0-9]+]] = !DIEnumerator(name: "BAZ"
-// CHECK: [[TYPE2:![0-9]+]] = !DICompositeType(tag: DW_TAG_enumeration_type, name: "z"
-// CHECK: [[TYPE3:![0-9]+]] = !DIEnumerator(name: "Z"
-// CHECK: [[RETTYPES]] = !{[[TYPE4:![0-9]+]], [[TYPE5:![0-9]+]], [[TYPE0]], {{![0-9]+}}, [[TYPE6:![0-9]+]], [[TYPE2]]}
-// CHECK: [[TYPE4]] = !DIDerivedType(tag: DW_TAG_typedef, name: "foo"
-// CHECK: [[TYPE5]] = distinct !DICompositeType(tag: DW_TAG_class_type, name: "bar"
-// CHECK: [[TYPE6]] = distinct !DICompositeType(tag: DW_TAG_class_type, name: "y"
+// CHECK: [[RETTYPES]] = !{[[TYPE2:![0-9]+]], [[TYPE3:![0-9]+]], [[TYPE0]], {{![0-9]+}}}
+// CHECK: [[TYPE2]] = !DIDerivedType(tag: DW_TAG_typedef, name: "foo"
+// CHECK: [[TYPE3]] = distinct !DICompositeType(tag: DW_TAG_class_type, name: "bar"
+// CHECK: [[SP:![0-9]+]] = distinct !DISubprogram(name: "quux", {{.*}}, retainedNodes: [[SPRETNODES:![0-9]+]]
+// CHECK: [[SPRETNODES]] = !{[[TYPE4:![0-9]+]], [[TYPE5:![0-9]+]]}
+// CHECK: [[TYPE4]] = distinct !DICompositeType(tag: DW_TAG_class_type, name: "y", scope: [[SP]]
+// CHECK: [[TYPE5]] = !DICompositeType(tag: DW_TAG_enumeration_type, name: "z", scope: [[SP]]
+// CHECK: [[TYPE6:![0-9]+]] = !DIEnumerator(name: "Z"
 
 // NODBG-NOT: !DI{{CompositeType|Enumerator|DerivedType}}
 
diff --git a/llvm/include/llvm/IR/DIBuilder.h b/llvm/include/llvm/IR/DIBuilder.h
index f3839c9694f34..ce714d0388323 100644
--- a/llvm/include/llvm/IR/DIBuilder.h
+++ b/llvm/include/llvm/IR/DIBuilder.h
@@ -49,7 +49,7 @@ namespace llvm {
 
     DICompileUnit *CUNode; ///< The one compile unit created by this DIBuiler.
 
-    SmallVector<TrackingMDNodeRef, 4> AllEnumTypes;
+    SmallVector<TrackingMDNodeRef, 4> EnumTypes;
     /// Track the RetainTypes, since they can be updated later on.
     SmallVector<TrackingMDNodeRef, 4> AllRetainTypes;
     SmallVector<DISubprogram *, 4> AllSubprograms;
@@ -64,8 +64,8 @@ namespace llvm {
     SmallVector<TrackingMDNodeRef, 4> UnresolvedNodes;
     bool AllowUnresolvedNodes;
 
-    /// Each subprogram's preserved local variables, labels and imported
-    /// entities.
+    /// Each subprogram's preserved local variables, labels, imported entities,
+    /// and types.
     ///
     /// Do not use a std::vector.  Some versions of libc++ apparently copy
     /// instead of move on grow operations, and TrackingMDRef is expensive to
diff --git a/llvm/lib/Bitcode/Reader/MetadataLoader.cpp b/llvm/lib/Bitcode/Reader/MetadataLoader.cpp
index ed0443f599a44..b9e1583f31462 100644
--- a/llvm/lib/Bitcode/Reader/MetadataLoader.cpp
+++ b/llvm/lib/Bitcode/Reader/MetadataLoader.cpp
@@ -116,6 +116,8 @@ class BitcodeReaderMetadataList {
         RefsUpperBound(std::min((size_t)std::numeric_limits<unsigned>::max(),
                                 RefsUpperBound)) {}
 
+  using const_iterator = SmallVector<TrackingMDRef, 1>::const_iterator;
+
   // vector compatibility methods
   unsigned size() const { return MetadataPtrs.size(); }
   void resize(unsigned N) { MetadataPtrs.resize(N); }
@@ -124,6 +126,8 @@ class BitcodeReaderMetadataList {
   Metadata *back() const { return MetadataPtrs.back(); }
   void pop_back() { MetadataPtrs.pop_back(); }
   bool empty() const { return MetadataPtrs.empty(); }
+  const_iterator begin() { return MetadataPtrs.begin(); }
+  const_iterator end() { return MetadataPtrs.end(); }
 
   Metadata *operator[](unsigned i) const { return MetadataPtrs[i]; }
 
@@ -538,6 +542,8 @@ class MetadataLoader::MetadataLoaderImpl {
 
   /// Move local imports from DICompileUnit's 'imports' field to
   /// DISubprogram's retainedNodes.
+  /// Move function-local enums from DICompileUnit's enums
+  /// to DISubprogram's retainedNodes.
   void upgradeCULocals() {
     if (NamedMDNode *CUNodes = TheModule.getNamedMetadata("llvm.dbg.cu")) {
       for (MDNode *N : CUNodes->operands()) {
@@ -545,48 +551,66 @@ class MetadataLoader::MetadataLoaderImpl {
         if (!CU)
           continue;
 
-        if (CU->getRawImportedEntities()) {
-          // Collect a set of imported entities to be moved.
-          SetVector<Metadata *> EntitiesToRemove;
+        SetVector<Metadata *> MetadataToRemove;
+        // Collect imported entities to be moved.
+        if (CU->getRawImportedEntities())
           for (Metadata *Op : CU->getImportedEntities()->operands()) {
             auto *IE = cast<DIImportedEntity>(Op);
-            if (isa_and_nonnull<DILocalScope>(IE->getScope())) {
-              EntitiesToRemove.insert(IE);
-            }
+            if (isa_and_nonnull<DILocalScope>(IE->getScope()))
+              MetadataToRemove.insert(IE);
+          }
+        // Collect enums to be moved.
+        if (CU->getRawEnumTypes())
+          for (Metadata *Op : CU->getEnumTypes()->operands()) {
+            auto *Enum = cast<DICompositeType>(Op);
+            if (isa_and_nonnull<DILocalScope>(Enum->getScope()))
+              MetadataToRemove.insert(Enum);
           }
 
-          if (!EntitiesToRemove.empty()) {
-            // Make a new list of CU's 'imports'.
-            SmallVector<Metadata *> NewImports;
-            for (Metadata *Op : CU->getImportedEntities()->operands()) {
-              if (!EntitiesToRemove.contains(cast<DIImportedEntity>(Op))) {
+        if (!MetadataToRemove.empty()) {
+          // Make a new list of CU's 'imports'.
+          SmallVector<Metadata *> NewImports;
+          if (CU->getRawImportedEntities())
+            for (Metadata *Op : CU->getImportedEntities()->operands())
+              if (!MetadataToRemove.contains(Op))
                 NewImports.push_back(Op);
-              }
-            }
 
-            // Find DISubprogram corresponding to each entity.
-            std::map<DISubprogram *, SmallVector<Metadata *>> SPToEntities;
-            for (auto *I : EntitiesToRemove) {
-              auto *Entity = cast<DIImportedEntity>(I);
-              if (auto *SP = findEnclosingSubprogram(
-                      cast<DILocalScope>(Entity->getScope()))) {
-                SPToEntities[SP].push_back(Entity);
-              }
-            }
+          // Make a new list of CU's 'enums'.
+          SmallVector<Metadata *> NewEnums;
+          if (CU->getRawEnumTypes())
+            for (Metadata *Op : CU->getEnumTypes()->operands())
+              if (!MetadataToRemove.contains(Op))
+                NewEnums.push_back(Op);
+
+          // Find DISubprogram corresponding to each entity.
+          std::map<DISubprogram *, SmallVector<Metadata *>> SPToEntities;
+          for (auto *I : MetadataToRemove) {
+            DILocalScope *Scope = nullptr;
+            if (auto *Entity = dyn_cast<DIImportedEntity>(I))
+              Scope = cast<DILocalScope>(Entity->getScope());
+            else if (auto *Enum = dyn_cast<DICompositeType>(I))
+              Scope = cast<DILocalScope>(Enum->getScope());
+
+            if (auto *SP = findEnclosingSubprogram(Scope))
+              SPToEntities[SP].push_back(I);
+          }
 
-            // Update DISubprograms' retainedNodes.
-            for (auto I = SPToEntities.begin(); I != SPToEntities.end(); ++I) {
-              auto *SP = I->first;
-              auto RetainedNodes = SP->getRetainedNodes();
-              SmallVector<Metadata *> MDs(RetainedNodes.begin(),
-                                          RetainedNodes.end());
-              MDs.append(I->second);
-              SP->replaceRetainedNodes(MDNode::get(Context, MDs));
-            }
+          // Update DISubprograms' retainedNodes.
+          for (auto I = SPToEntities.begin(); I != SPToEntities.end(); ++I) {
+            auto *SP = I->first;
+            auto RetainedNodes = SP->getRetainedNodes();
+            SmallVector<Metadata *> MDs(RetainedNodes.begin(),
+                                        RetainedNodes.end());
+            MDs.append(I->second);
+            SP->replaceRetainedNodes(MDNode::get(Context, MDs));
+          }
 
-            // Remove entities with local scope from CU.
+          // Remove entities with local scope from CU.
+          if (CU->getRawImportedEntities())
             CU->replaceImportedEntities(MDTuple::get(Context, NewImports));
-          }
+          // Remove enums with local scope from CU.
+          if (CU->getRawEnumTypes())
+            CU->replaceEnumTypes(MDTuple::get(Context, NewEnums));
         }
       }
     }
@@ -710,6 +734,29 @@ class MetadataLoader::MetadataLoaderImpl {
       upgradeCULocals();
   }
 
+  void cloneLocalTypes() {
+    for (Metadata *M : MetadataList) {
+      if (auto *SP = dyn_cast_or_null<DISubprogram>(M)) {
+        auto RetainedNodes = SP->getRetainedNodes();
+        SmallVector<Metadata *> MDs(RetainedNodes.begin(), RetainedNodes.end());
+        bool HasChanged = false;
+        for (auto &N : MDs)
+          if (auto *T = dyn_cast<DIType>(N))
+            if (auto *LS = dyn_cast_or_null<DILocalScope>(T->getScope()))
+              if (auto *Parent = findEnclosingSubprogram(LS))
+                if (Parent != SP) {
+                  HasChanged = true;
+                  auto NewT = T->clone();
+                  NewT->replaceOperandWith(1, SP);
+                  N = MDNode::replaceWithUniqued(std::move(NewT));
+                }
+
+        if (HasChanged)
+          SP->replaceRetainedNodes(MDNode::get(Context, MDs));
+      }
+    }
+  }
+
   void callMDTypeCallback(Metadata **Val, unsigned TypeID);
 
 public:
@@ -1086,6 +1133,7 @@ Error MetadataLoader::MetadataLoaderImpl::parseMetadata(bool ModuleLevel) {
       // placeholders, that we flush here.
       resolveForwardRefsAndPlaceholders(Placeholders);
       upgradeDebugInfo(ModuleLevel);
+      cloneLocalTypes();
       // Return at the beginning of the block, since it is easy to skip it
       // entirely from there.
       Stream.ReadBlockEnd(); // Pop the abbrev block context.
@@ -1117,6 +1165,7 @@ Error MetadataLoader::MetadataLoaderImpl::parseMetadata(bool ModuleLevel) {
     case BitstreamEntry::EndBlock:
       resolveForwardRefsAndPlaceholders(Placeholders);
       upgradeDebugInfo(ModuleLevel);
+      cloneLocalTypes();
       return Error::success();
     case BitstreamEntry::Record:
       // The interesting case.
diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp
index 518121e200190..00f41c873cb53 100644
--- a/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp
@@ -642,10 +642,9 @@ void DwarfCompileUnit::constructScopeDIE(LexicalScope *Scope,
     return;
 
   // Emit lexical blocks.
-  DIE *ScopeDIE = constructLexicalScopeDIE(Scope);
+  DIE *ScopeDIE = getOrCreateLexicalBlockDIE(Scope, ParentScopeDIE);
   assert(ScopeDIE && "Scope DIE should not be null.");
 
-  ParentScopeDIE.addChild(ScopeDIE);
   createAndAddScopeChildren(Scope, *ScopeDIE);
 }
 
@@ -766,26 +765,41 @@ DIE *DwarfCompileUnit::constructInlinedScopeDIE(LexicalScope *Scope,
   return ScopeDIE;
 }
 
-// Construct new DW_TAG_lexical_block for this scope and attach
-// DW_AT_low_pc/DW_AT_high_pc labels.
-DIE *DwarfCompileUnit::constructLexicalScopeDIE(LexicalScope *Scope) {
+DIE *DwarfCompileUnit::getOrCreateLexicalBlockDIE(LexicalScope *Scope,
+                                                  DIE &ParentScopeDIE) {
   if (DD->isLexicalScopeDIENull(Scope))
     return nullptr;
   const auto *DS = Scope->getScopeNode();
-
-  auto ScopeDIE = DIE::get(DIEValueAllocator, dwarf::DW_TAG_lexical_block);
-  if (Scope->isAbstractScope()) {
-    assert(!getAbstractScopeDIEs().count(DS) &&
-           "Abstract DIE for this scope exists!");
-    getAbstractScopeDIEs()[DS] = ScopeDIE;
-    return ScopeDIE;
+  DIE *ScopeDIE = nullptr;
+
+  // FIXME: We may have a concrete DIE for this scope already created.
+  // This may happen when we emit local variables for an abstract tree of
+  // an inlined function: if a local variable has a templated type with
+  // a function-local type as a template parameter. See PR55680 for details
+  // (see also llvm/test/DebugInfo/Generic/local-type-as-template-parameter.ll).
+  if (!Scope->isAbstractScope() && !Scope->getInlinedAt()) {
+    if (auto It = LexicalBlockDIEs.find(DS); It != LexicalBlockDIEs.end()) {
+      ScopeDIE = It->second;
+      assert(!ScopeDIE->findAttribute(dwarf::DW_AT_low_pc) &&
+             !ScopeDIE->findAttribute(dwarf::DW_AT_ranges));
+      assert(ScopeDIE->getParent() == &ParentScopeDIE);
+    }
   }
-  if (!Scope->getInlinedAt()) {
-    assert(!LexicalBlockDIEs.count(DS) &&
-           "Concrete out-of-line DIE for this scope exists!");
-    LexicalBlockDIEs[DS] = ScopeDIE;
-  } else {
-    InlinedLocalScopeDIEs[DS].push_back(ScopeDIE);
+  if (!ScopeDIE) {
+    ScopeDIE = DIE::get(DIEValueAllocator, dwarf::DW_TAG_lexical_block);
+    ParentScopeDIE.addChild(ScopeDIE);
+
+    if (Scope->isAbstractScope()) {
+      assert(!getAbstractScopeDIEs().count(DS) &&
+             "Abstract DIE for this scope exists!");
+      getAbstractScopeDIEs()[DS] = ScopeDIE;
+      return ScopeDIE;
+    }
+
+    if (!Scope->getInlinedAt())
+      LexicalBlockDIEs[DS] = ScopeDIE;
+    else
+      InlinedLocalScopeDIEs[DS].push_back(ScopeDIE);
   }
 
   attachRangesOrLowHighPC(*ScopeDIE, Scope->getRanges());
@@ -1800,7 +1814,7 @@ void DwarfCompileUnit::createBaseTypeDIEs() {
   }
 }
 
-DIE *DwarfCompileUnit::getLexicalBlockDIE(const DILexicalBlock *LB) {
+DIE *DwarfCompileUnit::getLocalContextDIE(const DILexicalBlock *LB) {
   // Assume if there is an abstract tree all the DIEs are already emitted.
   bool isAbstract = getAbstractScopeDIEs().count(LB->getSubprogram());
   if (isAbstract) {
@@ -1810,8 +1824,14 @@ DIE *DwarfCompileUnit::getLexicalBlockDIE(const DILexicalBlock *LB) {
   }
   assert(!isAbstract && "Missed lexical block DIE in abstract tree!");
 
-  // Return a concrete DIE if it exists or nullptr otherwise.
-  return LexicalBlockDIEs.lookup(LB);
+  // Check if we have a concrete DIE.
+  if (auto It = LexicalBlockDIEs.find(LB); It != LexicalBlockDIEs.end())
+    return It->second;
+
+  // If nothing available found, we cannot just create a new lexical block,
+  // because it isn't known where to put it into the DIE tree.
+  // So, we may only try to find the most close avaiable parent DIE.
+  return getOrCreateContextDIE(LB->getScope()->getNonLexicalBlockFileScope());
 }
 
 DIE *DwarfCompileUnit::getOrCreateContextDIE(const DIScope *Context) {
@@ -1819,7 +1839,7 @@ DIE *DwarfCompileUnit::getOrCreateContextDIE(const DIScope *Context) {
     if (auto *LFScope = dyn_cast<DILexicalBlockFile>(Context))
       Context = LFScope->getNonLexicalBlockFileScope();
     if (auto *LScope = dyn_cast<DILexicalBlock>(Context))
-      return getLexicalBlockDIE(LScope);
+      return getLocalContextDIE(LScope);
 
     // Otherwise the context must be a DISubprogram.
     auto *SPScope = cast<DISubprogram>(Context);
diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.h b/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.h
index a3bbc8364599d..a705039eb486b 100644
--- a/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.h
+++ b/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.h
@@ -251,14 +251,9 @@ class DwarfCompileUnit final : public DwarfUnit {
   /// DIE to represent this concrete inlined copy of the function.
   DIE *constructInlinedScopeDIE(LexicalScope *Scope, DIE &ParentScopeDIE);
 
-  /// Construct new DW_TAG_lexical_block for this scope and
-  /// attach DW_AT_low_pc/DW_AT_high_pc labels.
-  DIE *constructLexicalScopeDIE(LexicalScope *Scope);
-
-  /// Get a DIE for the given DILexicalBlock.
-  /// Note that this function assumes that the DIE has been already created
-  /// and it's an error, if it hasn't.
-  DIE *getLexicalBlockDIE(const DILexicalBlock *LB);
+  /// Get if available or create a new DW_TAG_lexical_block for the given
+  /// LexicalScope and attach DW_AT_low_pc/DW_AT_high_pc labels.
+  DIE *getOrCreateLexicalBlockDIE(LexicalScope *Scope, DIE &ParentDIE);
 
   /// Construct a DIE for the given DbgVariable.
   DIE *constructVariableDIE(DbgVariable &DV, bool Abstract = false);
@@ -277,6 +272,11 @@ class DwarfCompileUnit final : public DwarfUnit {
   /// This instance of 'getOrCreateContextDIE()' can handle DILocalScope.
   DIE *getOrCreateContextDIE(const DIScope *Ty) override;
 
+  /// Get DW_TAG_lexical_block for the given DILexicalBlock if available,
+  /// or the most close parent DIE, if no correspoding DW_TAG_lexical_block
+  /// exists.
+  DIE *getLocalContextDIE(const DILexicalBlock *LB);
+
   DIE *getOrCreateSubprogramDIE(const DISubprogram *SP, const Function *F,
                                 bool Minimal = false) override;
 
diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
index 567acf75d1b8d..3096622da1d5a 100644
--- a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
@@ -1245,12 +1245,13 @@ void DwarfDebug::beginModule(Module *M) {
         CU.getOrCreateGlobalVariableDIE(GV, sortGlobalExprs(GVMap[GV]));
     }
 
-    for (auto *Ty : CUNode->getEnumTypes())
+    for (auto *Ty : CUNode->getEnumTypes()) {
+      assert(!isa_and_nonnull<DILocalScope>(Ty->getScope()) &&
+             "Unexpected function-local entity in 'enums' CU field.");
       CU.getOrCreateTypeDIE(cast<DIType>(Ty));
+    }
 
     for (auto *Ty : CUNode->getRetainedTypes()) {
-      // The retained types array by design contains pointers to
-      // MDNodes rather than DIRefs. Unique them here.
       if (DIType *RT = dyn_cast<DIType>(Ty))
         // There is no point in force-emitting a forward declaration.
         CU.getOrCreateTypeDIE(RT);
@@ -1451,9 +1452,13 @@ void DwarfDebug::endModule() {
              "Unexpected function-local entity in 'imports' CU field.");
       CU->getOrCreateImportedEntityDIE(IE);
     }
+
+    // Emit function-local entities.
     for (const auto *D : CU->getDeferredLocalDecls()) {
       if (auto *IE = dyn_cast<DIImportedEntity>(D))
         CU->getOrCreateImportedEntityDIE(IE);
+      else if (auto *Ty = dyn_cast<DIType>(D))
+        CU->getOrCreateTypeDIE(Ty);
       else
         llvm_unreachable("Unexpected local retained node!");
     }
@@ -1551,6 +1556,8 @@ static const DILocalScope *getRetainedNodeScope(const MDNode *N) {
     S = L->getScope();
   else if (const auto *IE = dyn_cast<DIImportedEntity>(N))
     S = IE->getScope();
+  else if (const auto *T = dyn_cast<DIType>(N))
+    S = T->getScope();
   else
     llvm_unreachable("Unexpected retained node!");
 
diff --git a/llvm/lib/IR/DIBuilder.cpp b/llvm/lib/IR/DIBuilder.cpp
index 07a870f0630a5..49198ed7671dc 100644
--- a/llvm/lib/IR/DIBuilder.cpp
+++ b/llvm/lib/IR/DIBuilder.cpp
@@ -29,7 +29,7 @@ DIBuilder::DIBuilder(Module &m, bool AllowUnresolvedNodes, DICompileUnit *CU)
       AllowUnresolvedNodes(AllowUnresolvedNodes) {
   if (CUNode) {
     if (const auto &ETs = CUNode->getEnumTypes())
-      AllEnumTypes.assign(ETs.begin(), ETs.end());
+      EnumTypes.assign(ETs.begin(), ETs.end());
     if (const auto &RTs = CUNode->getRetainedTypes())
       AllRetainTypes.assign(RTs.begin(), RTs.end());
     if (const auto &GVs = CUNode->getGlobalVariables())
@@ -66,10 +66,10 @@ void DIBuilder::finalize() {
     return;
   }
 
-  if (!AllEnumTypes.empty())
-    CUNode->replaceEnumTypes(MDTuple::get(
-        VMContext, SmallVector<Metadata *, 16>(AllEnumTypes.begin(),
-                                               AllEnumTypes.end())));
+  if (!EnumTypes.empty())
+    CUNode->replaceEnumTypes(
+        MDTuple::get(VMContext, SmallVector<Metadata *, 16>(EnumTypes.begin(),
+                                                            EnumTypes.end())));
 
   SmallVector<Metadata *, 16> RetainValues;
   // Declarations and definitions of the same type may be retained. Some
@@ -388,10 +388,14 @@ DIBuilder::createTemplateAlias(DIType *Ty, StringRef Name, DIFile *File,
                                unsigned LineNo, DIScope *Context,
                                DINodeArray TParams, uint32_t AlignInBits,
                                DINode::DIFlags Flags, DINodeArray Annotations) {
-  return DIDerivedType::get(VMContext, dwarf::DW_TAG_template_alias, Name, File,
-                            LineNo, getNonCompileUnitScope(Context), Ty,
-                            (uint64_t)0, AlignInBits, (uint64_t)0, std::nullopt,
-                            std::nullopt, Flags, TParams.get(), Annotations);
+  auto *T =
+      DIDerivedType::get(VMContext, dwarf::DW_TAG_template_alias, Name, File,
+                         LineNo, getNonCompileUnitScope(Context), Ty,
+                         (uint64_t)0, AlignInBits, (uint64_t)0, std::nullopt,
+                         std::nullopt, Flags, TParams.get(), Annotations);
+  if (isa_and_nonnull<DILocalScope>(Context))
+    getSubprogramNodesTrackingVector(Context).emplace_back(T);
+  return T;
 }
 
 DIDerivedType *DIBuilder::createFriend(DIType *Ty, DIType *FriendTy) {
@@ -582,6 +586,8 @@ DICompositeType *DIBuilder::createClassType(
       OffsetInBits, Flags, Elements, RunTimeLang, /*EnumKind=*/std::nullopt,
       VTableHolder, cast_or_null<MDTuple>(TemplateParams), UniqueIdentifier);
   trackIfUnresolved(R);
+  if (isa_and_nonnull<DILocalScope>(Context))
+    getSubprogramNodesTrackingVector(Context).emplace_back(R);
   return R;
 }
 
@@ -614,6 +620,8 @@ DICompositeType *DIBuilder::createStructType(
       nullptr, UniqueIdentifier, nullptr, nullptr, nullptr, nullptr, nullptr,
       nullptr, Specification, NumExtraInhabitants);
   trackIfUnresolved(R);
+  if (isa_and_nonnull<DILocalScope>(Context))
+    getSubprogramNodesTrackingVector(Context).emplace_back(R);
   return R;
 }
 
@@ -627,6 +635,8 @@ DICompositeType *DIBuilder::createUnionType(
       Elements, RunTimeLang, /*EnumKind=*/std::nullopt, nullptr, nullptr,
       UniqueIdentifier);
   trackIfUnresolved(R);
+  if (isa_and_nonnull<DILocalScope>(Scope))
+    getSubprogramNodesTrackingVector(Scope).emplace_back(R);
   return R;
 }
 
@@ -661,7 +671,10 @@ DICompositeType *DIBuilder::createEnumerationType(
       getNonCompileUnitScope(Scope), UnderlyingType, SizeInBits, AlignInBits, 0,
       IsScoped ? DINode::FlagEnumClass : DINode::FlagZero, Elements,
       RunTimeLang, EnumKind, nullptr, nullptr, UniqueIdentifier);
-  AllEnumTypes.emplace_back(CTy);
+  if (isa_and_nonnull<DILocalScope>(Scope))
+    getSubprogramNodesTrackingVector(Scope).emplace_back(CTy);
+  else
+    EnumTypes.emplace_back(CTy);
   trackIfUnresolved(CTy);
   return CTy;
 }
@@ -768,7 +781,8 @@ void DIBuilder::retainType(DIScope *T) {
   assert((isa<DIType>(T) || (isa<DISubprogram>(T) &&
                              cast<DISubprogram>(T)->isDefinition() == false)) &&
          "Expected type or subprogram declaration");
-  AllRetainTypes.emplace_back(T);
+  if (!isa_and_nonnull<DILocalScope>(T->getScope()))
+    AllRetainTypes.emplace_back(T);
 }
 
 DIBasicType *DIBuilder::createUnspecifiedParameter() { return nullptr; }
@@ -784,6 +798,8 @@ DICompositeType *DIBuilder::createForwardDecl(
       SizeInBits, AlignInBits, 0, DINode::FlagFwdDecl, nullptr, RuntimeLang,
       /*EnumKind=*/EnumKind, nullptr, nullptr, UniqueIdentifier);
   trackIfUnresolved(RetTy);
+  if (isa_and_nonnull<DILocalScope>(Scope))
+    getSubprogramNodesTrackingVector(Scope).emplace_back(RetTy);
   return RetTy;
 }
 
@@ -800,6 +816,8 @@ DICompositeType *DIBuilder::createReplaceableCompositeType(
           nullptr, nullptr, Annotations)
           .release();
   trackIfUnresolved(RetTy);
+  if (isa_and_nonnull<DILocalScope>(Scope))
+    getSubprogramNodesTrackingVector(Scope).emplace_back(RetTy);
   return RetTy;
 }
 
diff --git a/llvm/lib/IR/DebugInfo.cpp b/llvm/lib/IR/DebugInfo.cpp
index 58836068a4929..7d2a932d773b2 100644
--- a/llvm/lib/IR/DebugInfo.cpp
+++ b/llvm/lib/IR/DebugInfo.cpp
@@ -312,6 +312,8 @@ void DebugInfoFinder::processSubprogram(DISubprogram *SP) {
       processVariable(Var);
     else if (auto *Import = dyn_cast_or_null<DIImportedEntity>(N))
       processImportedEntity(Import);
+    else if (auto *T = dyn_cast_or_null<DIType>(N))
+      processType(T);
   }
 }
 
diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp
index 7917712846990..3cea732c4e130 100644
--- a/llvm/lib/IR/Verifier.cpp
+++ b/llvm/lib/IR/Verifier.cpp
@@ -1564,9 +1564,9 @@ void Verifier::visitDISubprogram(const DISubprogram &N) {
     CheckDI(Node, "invalid retained nodes list", &N, RawNode);
     for (Metadata *Op : Node->operands()) {
       CheckDI(Op && (isa<DILocalVariable>(Op) || isa<DILabel>(Op) ||
-                     isa<DIImportedEntity>(Op)),
-              "invalid retained nodes, expected DILocalVariable, DILabel or "
-              "DIImportedEntity",
+                     isa<DIImportedEntity>(Op) || isa<DIType>(Op)),
+              "invalid retained nodes, expected DILocalVariable, DILabel, "
+              "DIImportedEntity or DIType",
               &N, Node, Op);
     }
   }
diff --git a/llvm/lib/Transforms/Utils/CloneFunction.cpp b/llvm/lib/Transforms/Utils/CloneFunction.cpp
index 32924e7b69fdc..4c733ae88251c 100644
--- a/llvm/lib/Transforms/Utils/CloneFunction.cpp
+++ b/llvm/lib/Transforms/Utils/CloneFunction.cpp
@@ -87,10 +87,23 @@ createIdentityMDPredicate(const Function &F, CloneFunctionChangeType Changes) {
   };
 
   return [=](const Metadata *MD) {
-    // Avoid cloning types, compile units, and (other) subprograms.
-    if (isa<DICompileUnit>(MD) || isa<DIType>(MD))
+    // Avoid cloning compile units.
+    if (isa<DICompileUnit>(MD))
       return true;
 
+    // Clone no types but local
+    if (auto *Type = dyn_cast<DIType>(MD)) {
+      if (SPClonedWithinModule == nullptr)
+        return true;
+
+      auto *LScope = dyn_cast_or_null<DILocalScope>(Type->getScope());
+      if (!LScope)
+        return true;
+
+      if (ShouldKeep(LScope->getSubprogram()))
+        return true;
+    }
+
     if (auto *SP = dyn_cast<DISubprogram>(MD))
       return ShouldKeep(SP);
 
diff --git a/llvm/test/Bitcode/clone-local-types.ll b/llvm/test/Bitcode/clone-local-types.ll
new file mode 100644
index 0000000000000..d2607303ec610
--- /dev/null
+++ b/llvm/test/Bitcode/clone-local-types.ll
@@ -0,0 +1,46 @@
+; RUN: llvm-as < %s -o %t0
+; RUN: llvm-dis %t0 -o - | FileCheck %s
+
+; Ensure that function-local types with the same ODR identifier belonging
+; to different subprograms are not deduplicated when a module is being loaded.
+
+; CHECK: [[CU:![0-9]+]] = distinct !DICompileUnit
+; CHECK: [[BAR:![0-9]+]] = distinct !DISubprogram(name: "bar", {{.*}}, retainedNodes: [[RN_BAR:![0-9]+]])
+; CHECK: [[RN_BAR]] = !{[[T2:![0-9]+]]}
+; CHECK: [[T2]] = !DICompositeType(tag: DW_TAG_class_type, {{.*}}, identifier: "local_type")
+; CHECK: [[FOO:![0-9]+]] = distinct !DISubprogram(name: "foo", {{.*}}, retainedNodes: [[RN_FOO:![0-9]+]])
+; CHECK: [[RN_FOO]] = !{[[T1:![0-9]+]]}
+; CHECK: [[T1]] = !DICompositeType(tag: DW_TAG_class_type, {{.*}}, identifier: "local_type")
+
+target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-unknown-linux-gnu"
+
+declare void @foo()
+
+define void @bar(ptr %this) !dbg !10 {
+  call void @foo(), !dbg !17
+  ret void, !dbg !13
+}
+
+!llvm.module.flags = !{!0, !1, !2, !3, !4, !5, !6, !7}
+!llvm.dbg.cu = !{!8}
+
+!0 = !{i32 2, !"SDK Version", [2 x i32] [i32 11, i32 4]}
+!1 = !{i32 7, !"Dwarf Version", i32 2}
+!2 = !{i32 2, !"Debug Info Version", i32 3}
+!3 = !{i32 1, !"wchar_size", i32 4}
+!4 = !{i32 7, !"openmp", i32 50}
+!5 = !{i32 7, !"openmp-device", i32 50}
+!6 = !{i32 7, !"PIC Level", i32 2}
+!7 = !{i32 7, !"frame-pointer", i32 2}
+!8 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !9, producer: "clang version 14.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None)
+!9 = !DIFile(filename: "tmp.cpp", directory: "/tmp/")
+!10 = distinct !DISubprogram(name: "bar", scope: !9, file: !9, line: 68, type: !12, scopeLine: 68, flags: DIFlagPrototyped, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition | DISPFlagOptimized, unit: !8, retainedNodes: !16)
+!11 = distinct !DISubprogram(name: "foo", scope: !9, file: !9, line: 68, type: !12, scopeLine: 68, flags: DIFlagPrototyped, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition | DISPFlagOptimized, unit: !8, retainedNodes: !16)
+!12 = !DISubroutineType(types: !15)
+!13 = !DILocation(line: 69, column: 18, scope: !10)
+!14 = !DICompositeType(tag: DW_TAG_class_type, scope: !11, file: !9, line: 210, size: 8, flags: DIFlagTypePassByValue, elements: !15, identifier: "local_type")
+!15 = !{}
+!16 = !{!14}
+!17 = !DILocation(line: 69, column: 18, scope: !11, inlinedAt: !18)
+!18 = !DILocation(line: 4, column: 1, scope: !10)
diff --git a/llvm/test/Bitcode/upgrade-cu-locals.ll b/llvm/test/Bitcode/upgrade-cu-locals.ll
index 9a590f0fc0774..7052ab379bea4 100644
--- a/llvm/test/Bitcode/upgrade-cu-locals.ll
+++ b/llvm/test/Bitcode/upgrade-cu-locals.ll
@@ -1,4 +1,4 @@
-; Test moving of local imports from DICompileUnit's 'imports' to DISubprogram's 'retainedNodes'
+; Test moving of local imports/enums from DICompileUnit to DISubprogram's 'retainedNodes'
 ;
 ; RUN: llvm-dis -o - %s.bc | FileCheck %s
 
@@ -31,31 +31,36 @@ attributes #0 = { nocallback nofree nosync nounwind speculatable willreturn memo
 ; CHECK: !4 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "t4"
 
 ; CHECK: !5 = !{}
-; CHECK: !6 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !7, producer: "clang version 14.0.0", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, imports: !5, nameTableKind: GNU)
-
-; CHECK: !14 = distinct !DISubprogram(name: "main", scope: !7, file: !7, line: 2, type: !15, scopeLine: 2, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !6, retainedNodes: !18)
-; CHECK: !18 = !{!19}
-; CHECK: !19 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !20, entity: !23,
-; CHECK: !20 = !DILexicalBlock(scope: !21, file: !7, line: 7, column: 35)
-; CHECK: !21 = !DILexicalBlock(scope: !22, file: !7, line: 7, column: 35)
-; CHECK: !22 = !DILexicalBlock(scope: !14, file: !7, line: 7, column: 35)
-; CHECK: !23 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "t5", scope: !20,
-
-; CHECK: !25 = distinct !DISubprogram(name: "f1", linkageName: "_Z2f1v", scope: !1, file: !1, line: 3, type: !26, scopeLine: 3, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !28)
-; CHECK: !28 = !{!29, !32, !34}
-; CHECK: !29 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !25, entity: !30,
-; CHECK: !30 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "t1",
-; CHECK: !32 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !25, entity: !33,
-; CHECK: !33 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "t2",
-; CHECK: !34 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !25, entity: !35,
-; CHECK: !35 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "t3",
-
-; CHECK: !40 = distinct !DISubprogram(name: "main2", scope: !7, file: !7, line: 10, type: !15, scopeLine: 10, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !6, retainedNodes: !41)
-; CHECK: !41 = !{!42, !44}
-; CHECK: !42 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !40, entity: !43,
-; CHECK: !43 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "t6"
-; CHECK: !44 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !40, entity: !45,
-; CHECK: !45 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "t7",
+; CHECK: !6 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !7, producer: "clang version 14.0.0", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !8, imports: !5, nameTableKind: GNU)
+; CHECK: !7 = !DIFile(filename: "b.cpp"
+; CHECK: !8 = !{!9}
+; CHECK: !9 = !DICompositeType(tag: DW_TAG_enumeration_type, name: "Enum2", scope: !6, file: !7, line: 4, size: 8, align: 8, elements: !5)
+
+; CHECK: !16 = distinct !DISubprogram(name: "main", scope: !7, file: !7, line: 2, type: !17, scopeLine: 2, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !6, retainedNodes: !20)
+; CHECK: !20 = !{!21}
+; CHECK: !21 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !22, entity: !25,
+; CHECK: !22 = !DILexicalBlock(scope: !23, file: !7, line: 7, column: 35)
+; CHECK: !23 = !DILexicalBlock(scope: !24, file: !7, line: 7, column: 35)
+; CHECK: !24 = !DILexicalBlock(scope: !16, file: !7, line: 7, column: 35)
+; CHECK: !25 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "t5", scope: !22,
+
+; CHECK: !27 = distinct !DISubprogram(name: "f1", linkageName: "_Z2f1v", scope: !1, file: !1, line: 3, type: !28, scopeLine: 3, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !30)
+; CHECK: !30 = !{!31, !34, !36}
+; CHECK: !31 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !27, entity: !32,
+; CHECK: !32 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "t1",
+; CHECK: !34 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !27, entity: !35,
+; CHECK: !35 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "t2",
+; CHECK: !36 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !27, entity: !37,
+; CHECK: !37 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "t3",
+
+; CHECK: !42 = distinct !DISubprogram(name: "main2", scope: !7, file: !7, line: 10, type: !17, scopeLine: 10, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !6, retainedNodes: !43)
+; CHECK: !43 = !{!44, !46, !48, !49}
+; CHECK: !44 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !42, entity: !45,
+; CHECK: !45 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "t6"
+; CHECK: !46 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !42, entity: !47,
+; CHECK: !47 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "t7",
+; CHECK: !48 = !DICompositeType(tag: DW_TAG_enumeration_type, name: "Enum", scope: !42, file: !7, line: 3, size: 8, align: 8, elements: !5)
+; CHECK: !49 = !DICompositeType(tag: DW_TAG_enumeration_type, name: "Enum3", scope: !42, file: !7, line: 5, size: 8, align: 8, elements: !5)
 
 
 !0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang version 14.0.0", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, imports: !2, nameTableKind: GNU)
@@ -82,7 +87,7 @@ attributes #0 = { nocallback nofree nosync nounwind speculatable willreturn memo
 ; Leave t4 in CU
 !14 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !0, entity: !15, file: !1, line: 3)
 !15 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "t4", scope: !0, file: !1, line: 1, size: 8, flags: DIFlagTypePassByValue, elements: !7, identifier: "_ZTSN2ns2t4E")
-!16 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !17, producer: "clang version 14.0.0", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, imports: !18, nameTableKind: GNU)
+!16 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !17, producer: "clang version 14.0.0", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, imports: !18, enums: !50, nameTableKind: GNU)
 !17 = !DIFile(filename: "b.cpp", directory: "/")
 !18 = !{!19, !28, !31}
 
@@ -116,3 +121,12 @@ attributes #0 = { nocallback nofree nosync nounwind speculatable willreturn memo
 !41 = distinct !DILocation(line: 3, column: 3, scope: !23)
 !42 = !DILocation(line: 3, column: 41, scope: !4, inlinedAt: !41)
 !43 = !DILocation(line: 4, column: 1, scope: !23)
+
+!50 = !{!51, !52, !53}
+; Move to main2
+!51 = !DICompositeType(tag: DW_TAG_enumeration_type, name: "Enum", scope: !29, file: !17, line: 3, size: 8, align: 8, elements: !7)
+; Leave in b.cpp's CU
+!52 = !DICompositeType(tag: DW_TAG_enumeration_type, name: "Enum2", scope: !16, file: !17, line: 4, size: 8, align: 8, elements: !7)
+; Move to main2
+!53 = !DICompositeType(tag: DW_TAG_enumeration_type, name: "Enum3", scope: !29, file: !17, line: 5, size: 8, align: 8, elements: !7)
+
diff --git a/llvm/test/Bitcode/upgrade-cu-locals.ll.bc b/llvm/test/Bitcode/upgrade-cu-locals.ll.bc
index 9d0ea8fd9a3704ea1fb3a92250500addc75f0587..f73d05668af5b16f9ee03e400e2877faab339a0d 100644
GIT binary patch
delta 1080
zcmYk5e`p(J7{{NxT<<RFwOop6V|rQMH7e;k%bOqVBKYItmQkU&t8>^v2T79^Yj3eX
zCa4ST^$4!hY-CFq`$I=`D*iELuKy_e=R!ISm`cGRL56hTA0ibxhWMA%_q{6H%X`oB
z-1ohE-p}*!zJ}+nr}PA4Tzv8e=ETMO&yU}{_3PDJt*yNt=um-!|A&k}H9J|6zN*x)
zz`pltPnw1RR8~Q?N^cBEwg8N+ilr9PDjEQ4=T<*p+z#fi@<&_{iTssC`dw0W77=EH
z*c_9SV-gA2gRx8VvGQFm-JQ(6b&5U_YNDso)49lW?sWONHl>_yJF*18yXh_f*wo~p
zm<*emHz?jF4h4_b4>b}uctVLpliLG87yzu1pRs>l{*5sRvWb=j0!DRwX=wDE!o5S^
zOf<4d(z>W#%y#_LCnWZe`V1s?y-g$s)sK#yRhD8G6zL2mOMNo{bO~2Hg=K}-PyV<3
zq#>kxqe*`rGx=)KWD%^aF+h`3j$?WG#v>f#G%w}o32^!&9A2suj-yWr%l!{I44%|a
zL4<$~0r4859CHsjn1Ia at 0;sw`ra%slionW?#uo{nWGL`pFMPtkj$s~}XJN(nVGSA#
z4zOq@#XFE-No&eR!r!V%R>}e1C})ip;CBXV%(E=PadK-*f}<Bi)~=kQK=3&0zR$P?
zFab!z?O}~vR!8z0DHMT0x@`|!-w+nJg$D{euBG5HOG<>AheRU;84oDiwJ=Y7|E^bU
zPf#aaZ~liW_I^`?Z3Zt9R(*qaScJ9SUwv_3Q<kDq+B#R0>fX~?T-W%k-X<>8UgL=j
zd}ekQW|i;f7Vq_tsi(X3-JsT;L+v-o1XsyBp4;T7(%n-D;dAR-CS<UohPl7N`l(K4
zr0Ys`qSe~^Wumbb`*GsCKMuR^K<6vz%<9a-OTmGKYrRRW?O0>w{Q6hxpZ$6M`h_o>
zw~J42>AA|JXZ&N&#J$xHv5k3on>Xg(ukv3b;h>Ln<a)b(XM#LwT?+4icV}nkh0TA6
zo&B4bdNC2MEc==)o>a+U<_nRGCQVoQRnT!plNqI)a{8Ff@)_sAV5(!c!vJ$=_ozlX
z<-P*D&c#bd?4=WAs{s;qIV!309p_*u{7g7Dd~zy26wOR#W081lI2)ci5Q#<(#PJXw
bjz`1MY<74kUf$<AES)45abZx`X{vt#JnkA(

delta 1011
zcmYjPT}&fY6h1R;?+nAR8K7ukpbT3~NOmQ+|Ckt^#_fv6bXy??4aQDuk;YOAbd5%0
zrv<}iQ#P<|%$oR6v-sc!%?9*sAEv1*sKCpvhU}79AB at J-1png8D&A>B at aE3>`OdxH
z`DW%Ebf_IG{nX^@Bi~TU>Tgesr^SEYyYtha=j~yWY!HBg<7MPOE-e79rviCYP}RO~
z{gy^qhXoMu+C9!tM`Yd7K=*rCbF3X;n<h+~OH`|YIw-tvv~}+*HOenH<t0qpa4y8=
zHS>ZdEU$I902B_4N`Kn>%1fgo0Y`Gqk at ou2o|DYupHkvUM?9G>Oq(ubP_5n(a4sm=
zSie0NwAk3&#7VXnE9Y+cR78YGZ0=kSKu|xb4S$DwriFb<1z>Yhhyjpv9A9-m_myaT
zL(g*3 at Li&q)A9VPEq8uL*&bj)mDsk61ggK}NN7uR?PWy%O-xFsCjs;lE_N)IMV>_D
zKJ<VL#yUlNktuhbP{;%YAPW0`^xgcQLag7?M(?gN<`w4afdQb^OCrcbyRM_~7ovJk
zNHh~b24E!gv^6YS+3S+7BYC33XFb+2(9pq;LKfhUKA1%l7^Y!o3AQ_nWw=G*URruh
zMutPA&9iE$Y~eK#oI*-qW-;T0g%$|!2;gj`Q;a5?4q3o2P at nm%-O&sQe%1rxYjsbU
z=Cxy{^=ua}TlJP3D30MR*uj5hdmoe^XJpi20TX^o;0UY4X?z)1s<H2RgJn{|6$#z<
zR;n`*f?W)!5|HZNntA7khsjPT59ziZB&B9$Lu%dtZpAW|tfCTdC6Y?b=`i<!I2Ng*
zNfkNTu9e33_iOjYx4+mM|MHjft$U at G=auN@>vPZ8&dgmG{g!t_+n1JiKi&Q4*X8vq
zpX}VuKUzy9i#hYNWpjYqoMKH>Ktt-x<rKPch!#4@&scBPKP}i01Gz9h`{B{i(UTAU
zA-4KAF_pZ}UMzRsq;W(hvxgQP(I6jBp>;^$Xi)Hp1+A%zQVaK+&J4<A;u~v5YD|{n
zlyG{H{@geu1nPyCNvpFYYBLlg1s69s5SfVLsK at JZx`(jG?sT~nm&;-I+LfWlT#gBk
cSBb^kZo4~HxXhgw;!~;V+4S6CBGKsbANlGKqW}N^

diff --git a/llvm/test/DebugInfo/Generic/inlined-local-type.ll b/llvm/test/DebugInfo/Generic/inlined-local-type.ll
new file mode 100644
index 0000000000000..a6f7d294de928
--- /dev/null
+++ b/llvm/test/DebugInfo/Generic/inlined-local-type.ll
@@ -0,0 +1,128 @@
+; RUN: %llc_dwarf -O0 -filetype=obj < %s | llvm-dwarfdump -debug-info - | FileCheck --implicit-check-not "{{DW_TAG|NULL}}" %s
+; REQUIRES: object-emission
+
+; inline __attribute__((always_inline))
+; int removed() { struct A {int i;}; struct A a; return a.i++; }
+;
+; __attribute__((always_inline))
+; int not_removed() { struct B {int i;}; struct B b; return b.i++; }
+;
+; int foo() { return removed() + not_removed(); }}
+
+; Ensure that function-local types have the correct subprogram parent even if
+; those subprograms are inlined.
+
+; CHECK: DW_TAG_compile_unit
+; CHECK:   DW_TAG_subprogram
+; CHECK:     DW_AT_abstract_origin	({{0x.*}} "not_removed")
+; CHECK:     DW_TAG_variable
+; CHECK:     NULL
+; CHECK:   DW_TAG_subprogram
+; CHECK:     DW_AT_name	("removed")
+; CHECK: [[A:0x.*]]: DW_TAG_structure_type
+; CHECK:       DW_AT_name	("A")
+; CHECK:       DW_TAG_member
+; CHECK:       NULL
+; CHECK:     DW_TAG_variable
+; CHECK:       DW_AT_type	([[A]] "A")
+; CHECK:     NULL
+; CHECK:   DW_TAG_base_type
+; CHECK:   DW_TAG_subprogram
+; CHECK:     DW_AT_name	("not_removed")
+; CHECK: [[B:0x.*]]: DW_TAG_structure_type
+; CHECK:       DW_AT_name	("B")
+; CHECK:       DW_TAG_member
+; CHECK:       NULL
+; CHECK:     DW_TAG_variable
+; CHECK:       DW_AT_type	([[B]] "B")
+; CHECK:     NULL
+; CHECK:   DW_TAG_subprogram
+; CHECK:     DW_TAG_inlined_subroutine
+; CHECK:       DW_TAG_variable
+; CHECK:       NULL
+; CHECK:     DW_TAG_inlined_subroutine
+; CHECK:       DW_TAG_variable
+; CHECK:       NULL
+; CHECK:     NULL
+; CHECK:   NULL
+
+%struct.B = type { i32 }
+%struct.A = type { i32 }
+
+define dso_local i32 @not_removed() !dbg !12 {
+  %1 = alloca %struct.B, align 4
+  call void @llvm.dbg.declare(metadata %struct.B* %1, metadata !18, metadata !DIExpression()), !dbg !22
+  %2 = getelementptr inbounds %struct.B, %struct.B* %1, i32 0, i32 0, !dbg !23
+  %3 = load i32, i32* %2, align 4, !dbg !24
+  %4 = add nsw i32 %3, 1, !dbg !24
+  store i32 %4, i32* %2, align 4, !dbg !24
+  ret i32 %3, !dbg !25
+}
+
+declare void @llvm.dbg.declare(metadata, metadata, metadata)
+
+define dso_local i32 @foo() !dbg !26 {
+  %1 = alloca %struct.A, align 4
+  %2 = alloca %struct.B, align 4
+  call void @llvm.dbg.declare(metadata %struct.A* %1, metadata !27, metadata !DIExpression()), !dbg !32
+  %3 = getelementptr inbounds %struct.A, %struct.A* %1, i32 0, i32 0, !dbg !34
+  %4 = load i32, i32* %3, align 4, !dbg !35
+  %5 = add nsw i32 %4, 1, !dbg !35
+  store i32 %5, i32* %3, align 4, !dbg !35
+  call void @llvm.dbg.declare(metadata %struct.B* %2, metadata !18, metadata !DIExpression()), !dbg !36
+  %6 = getelementptr inbounds %struct.B, %struct.B* %2, i32 0, i32 0, !dbg !38
+  %7 = load i32, i32* %6, align 4, !dbg !39
+  %8 = add nsw i32 %7, 1, !dbg !39
+  store i32 %8, i32* %6, align 4, !dbg !39
+  %9 = add nsw i32 %4, %7, !dbg !40
+  ret i32 %9, !dbg !41
+}
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!2, !3, !4, !5, !6, !7, !8, !9, !10}
+!llvm.ident = !{!11}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 14.0.0", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None)
+!1 = !DIFile(filename: "inlined-local-type.cpp", directory: "")
+!2 = !{i32 7, !"Dwarf Version", i32 4}
+!3 = !{i32 2, !"Debug Info Version", i32 3}
+!4 = !{i32 1, !"wchar_size", i32 4}
+!5 = !{i32 1, !"branch-target-enforcement", i32 0}
+!6 = !{i32 1, !"sign-return-address", i32 0}
+!7 = !{i32 1, !"sign-return-address-all", i32 0}
+!8 = !{i32 1, !"sign-return-address-with-bkey", i32 0}
+!9 = !{i32 7, !"uwtable", i32 1}
+!10 = !{i32 7, !"frame-pointer", i32 1}
+!11 = !{!"clang version 14.0.0"}
+!12 = distinct !DISubprogram(name: "not_removed", scope: !13, file: !13, line: 5, type: !14, scopeLine: 5, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !43)
+!13 = !DIFile(filename: "inlined-local-type.cpp", directory: "")
+!14 = !DISubroutineType(types: !15)
+!15 = !{!16}
+!16 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!17 = !{}
+!18 = !DILocalVariable(name: "b", scope: !12, file: !13, line: 5, type: !19)
+!19 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "B", scope: !12, file: !13, line: 5, size: 32, elements: !20)
+!20 = !{!21}
+!21 = !DIDerivedType(tag: DW_TAG_member, name: "i", scope: !19, file: !13, line: 5, baseType: !16, size: 32)
+!22 = !DILocation(line: 5, column: 49, scope: !12)
+!23 = !DILocation(line: 5, column: 61, scope: !12)
+!24 = !DILocation(line: 5, column: 62, scope: !12)
+!25 = !DILocation(line: 5, column: 52, scope: !12)
+!26 = distinct !DISubprogram(name: "foo", scope: !13, file: !13, line: 7, type: !14, scopeLine: 7, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !17)
+!27 = !DILocalVariable(name: "a", scope: !28, file: !13, line: 2, type: !29)
+!28 = distinct !DISubprogram(name: "removed", scope: !13, file: !13, line: 2, type: !14, scopeLine: 2, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !42)
+!29 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "A", scope: !28, file: !13, line: 2, size: 32, elements: !30)
+!30 = !{!31}
+!31 = !DIDerivedType(tag: DW_TAG_member, name: "i", scope: !29, file: !13, line: 2, baseType: !16, size: 32)
+!32 = !DILocation(line: 2, column: 45, scope: !28, inlinedAt: !33)
+!33 = distinct !DILocation(line: 7, column: 20, scope: !26)
+!34 = !DILocation(line: 2, column: 57, scope: !28, inlinedAt: !33)
+!35 = !DILocation(line: 2, column: 58, scope: !28, inlinedAt: !33)
+!36 = !DILocation(line: 5, column: 49, scope: !12, inlinedAt: !37)
+!37 = distinct !DILocation(line: 7, column: 32, scope: !26)
+!38 = !DILocation(line: 5, column: 61, scope: !12, inlinedAt: !37)
+!39 = !DILocation(line: 5, column: 62, scope: !12, inlinedAt: !37)
+!40 = !DILocation(line: 7, column: 30, scope: !26)
+!41 = !DILocation(line: 7, column: 13, scope: !26)
+!42 = !{!29}
+!43 = !{!19}
diff --git a/llvm/test/DebugInfo/Generic/lexical-block-retained-types.ll b/llvm/test/DebugInfo/Generic/lexical-block-retained-types.ll
new file mode 100644
index 0000000000000..64de1323c98c6
--- /dev/null
+++ b/llvm/test/DebugInfo/Generic/lexical-block-retained-types.ll
@@ -0,0 +1,55 @@
+; RUN: %llc_dwarf -O0 -filetype=obj < %s | llvm-dwarfdump - | FileCheck --implicit-check-not "{{DW_TAG|NULL}}" %s
+
+; Test that retained unused (unreferenced) types emission.
+
+; Compiled from
+; $ clang -cc1 -debug-info-kind=unused-types test.cpp -emit-llvm
+
+; void test_unused() {
+;   struct Y {};
+;   {
+;     struct X {};
+;   }
+; }
+
+; CHECK: DW_TAG_compile_unit
+; CHECK:   DW_TAG_subprogram
+; CHECK:     DW_AT_name	("test_unused")
+; CHECK:     DW_TAG_structure_type
+; CHECK:       DW_AT_name	("Y")
+
+; FIXME: here should be DW_TAG_lexical_block as a parent of structure 'X'.
+; But it's not possible to reliably emit a lexical block for which a LexicalScope
+; wasn't created, so we just fallback to the most close parent DIE
+; (see DwarfCompileUnit::getOrCreateLexicalBlockDIE() for details).
+
+; CHECK:     DW_TAG_structure_type
+; CHECK:       DW_AT_name	("X")
+; CHECK:     NULL
+; CHECK:   NULL
+
+define dso_local void @_Z11test_unusedv() !dbg !5 {
+entry:
+  ret void, !dbg !16
+}
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!13, !14}
+!llvm.ident = !{!15}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang version 15.0.0", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, retainedTypes: !2, splitDebugInlining: false, nameTableKind: None)
+!1 = !DIFile(filename: "<stdin>", directory: "/")
+!2 = !{!3, !10}
+!3 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "Y", scope: !5, file: !4, line: 2, size: 8, flags: DIFlagTypePassByValue, elements: !8)
+!4 = !DIFile(filename: "test.cpp", directory: "/")
+!5 = distinct !DISubprogram(name: "test_unused", linkageName: "_Z11test_unusedv", scope: !4, file: !4, line: 1, type: !6, scopeLine: 1, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !9)
+!6 = !DISubroutineType(types: !7)
+!7 = !{null}
+!8 = !{}
+!9 = !{!3, !10}
+!10 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "X", scope: !11, file: !4, line: 4, size: 8, flags: DIFlagTypePassByValue, elements: !8)
+!11 = distinct !DILexicalBlock(scope: !5, file: !4, line: 3, column: 3)
+!13 = !{i32 2, !"Debug Info Version", i32 3}
+!14 = !{i32 1, !"wchar_size", i32 4}
+!15 = !{!"clang version 15.0.0"}
+!16 = !DILocation(line: 6, column: 1, scope: !5)
diff --git a/llvm/test/DebugInfo/Generic/lexical-block-types.ll b/llvm/test/DebugInfo/Generic/lexical-block-types.ll
new file mode 100644
index 0000000000000..eea46b1b67d8c
--- /dev/null
+++ b/llvm/test/DebugInfo/Generic/lexical-block-types.ll
@@ -0,0 +1,425 @@
+; RUN: %llc_dwarf -O0 -filetype=obj < %s | llvm-dwarfdump -debug-info - | FileCheck --implicit-check-not "{{DW_TAG|NULL}}" %s
+; REQUIRES: object-emission
+
+; inline __attribute__((always_inline))
+; void removed() {
+;   struct A1 { int i; };
+;   typedef int Int1;
+;   {
+;     struct I1 { Int1 j; };
+;     struct C1 { typedef char Char1; Char1 c; };
+;     A1 a1; a1.i++;
+;     {
+;       I1 i1; i1.j++;
+;       C1 c1; c1.c++;
+;     }
+;   }
+; }
+;
+; __attribute__((always_inline))
+; void not_removed() {
+;   struct A2 { int i; };
+;   typedef int Int2;
+;   {
+;     struct I2 { Int2 j; };
+;     struct C2 { typedef char Char2; Char2 c; };
+;     A2 a2; a2.i++;
+;     {
+;       I2 i2; i2.j++;
+;       C2 c2; c2.c++;
+;     }
+;   }
+; }
+;
+; void foo() {
+;   struct A3 { int i; };
+;   typedef int Int3;
+;   {
+;     struct I3 { Int3 j; };
+;     {
+;       struct C3 { typedef char Char3; Char3 c; };
+;       A3 a3; a3.i++;
+;       {
+;         I3 i3; i3.j++;
+;         C3 c3; c3.c++;
+;       }
+;     }
+;   }
+;   removed();
+;   not_removed();
+; }
+;
+; CHECK: DW_TAG_compile_unit
+
+; Out-of-line definition of `not_removed()` shouldn't contain any debug info for types.
+; CHECK:   DW_TAG_subprogram
+; CHECK:     DW_AT_abstract_origin	{{.*}} "_Z11not_removedv"
+; CHECK:     DW_TAG_lexical_block
+; CHECK:       DW_TAG_variable
+; CHECK:         DW_AT_abstract_origin	{{.*}} "a2"
+; CHECK:       DW_TAG_lexical_block
+; CHECK:         DW_TAG_variable
+; CHECK:           DW_AT_abstract_origin	{{.*}} "i2"
+; CHECK:         DW_TAG_variable
+; CHECK:           DW_AT_abstract_origin	{{.*}} "c2"
+; CHECK:         NULL
+; CHECK:       NULL
+; CHECK:     NULL
+
+; Abstract definition of `removed()`.
+; CHECK:   DW_TAG_subprogram
+; CHECK:     DW_AT_name	("removed")
+; CHECK:     DW_AT_inline	(DW_INL_inlined)
+
+; I1 and C1 defined in the first lexical block, typedef Char1 is a child of C1.
+; CHECK:     DW_TAG_lexical_block
+; CHECK:       DW_TAG_variable
+; CHECK:         DW_AT_name	("a1")
+; CHECK:         DW_AT_type	{{.*}} "A1"
+; CHECK:       DW_TAG_lexical_block
+; CHECK:         DW_TAG_variable
+; CHECK:           DW_AT_type	{{.*}} "I1"
+; CHECK:         DW_TAG_variable
+; CHECK:           DW_AT_type	{{.*}} "C1"
+; CHECK:         NULL
+; CHECK:       DW_TAG_structure_type
+; CHECK:         DW_AT_name	("I1")
+; CHECK:         DW_TAG_member
+; CHECK:           DW_AT_type	{{.*}} "Int1"
+; CHECK:         NULL
+; CHECK:       DW_TAG_structure_type
+; CHECK:         DW_AT_name	("C1")
+; CHECK:         DW_TAG_member
+; CHECK:           DW_AT_type	{{.*}} "C1::Char1"
+; CHECK:         DW_TAG_typedef
+; CHECK:           DW_AT_name	("Char1")
+; CHECK:         NULL
+; CHECK:       NULL
+
+; A1 and typedef Int1 defined in the subprogram scope.
+; CHECK:     DW_TAG_structure_type
+; CHECK:       DW_AT_name	("A1")
+; CHECK:       DW_TAG_member
+; CHECK:       NULL
+; CHECK:     DW_TAG_typedef
+; CHECK:       DW_AT_name	("Int1")
+; CHECK:     NULL
+
+; CHECK:   DW_TAG_base_type
+; CHECK:   DW_TAG_base_type
+
+; Abstract definition of `not_removed()`.
+; CHECK:   DW_TAG_subprogram
+; CHECK:     DW_AT_name	("not_removed")
+; CHECK:     DW_AT_inline	(DW_INL_inlined)
+
+; I2 and C2 defined in the first lexical block, typedef Char2 is a child of C2.
+; CHECK:     DW_TAG_lexical_block
+; CHECK:       DW_TAG_variable
+; CHECK:         DW_AT_name	("a2")
+; CHECK:         DW_AT_type	{{.*}} "A2"
+; CHECK:       DW_TAG_lexical_block
+; CHECK:         DW_TAG_variable
+; CHECK:           DW_AT_name	("i2")
+; CHECK:           DW_AT_type	{{.*}} "I2"
+; CHECK:         DW_TAG_variable
+; CHECK:           DW_AT_name	("c2")
+; CHECK:           DW_AT_type	{{.*}} "C2"
+; CHECK:         NULL
+; CHECK:       DW_TAG_structure_type
+; CHECK:         DW_AT_name	("I2")
+; CHECK:         DW_TAG_member
+; CHECK:           DW_AT_type	{{.*}} "Int2"
+; CHECK:         NULL
+; CHECK:       DW_TAG_structure_type
+; CHECK:         DW_AT_name	("C2")
+; CHECK:         DW_TAG_member
+; CHECK:           DW_AT_type	{{.*}} "C2::Char2"
+; CHECK:         DW_TAG_typedef
+; CHECK:           DW_AT_name	("Char2")
+; CHECK:         NULL
+; CHECK:       NULL
+
+; A2 and typedef Int2 defined in subprogram scope.
+; CHECK:     DW_TAG_structure_type
+; CHECK:       DW_AT_name	("A2")
+; CHECK:       DW_TAG_member
+; CHECK:       NULL
+; CHECK:     DW_TAG_typedef
+; CHECK:       DW_AT_name	("Int2")
+; CHECK:     NULL
+
+; Definition of `foo()`.
+; CHECK:   DW_TAG_subprogram
+; CHECK:     DW_AT_name	("foo")
+
+; CHECK:     DW_TAG_lexical_block
+; CHECK:       DW_TAG_lexical_block
+; CHECK:         DW_TAG_variable
+; CHECK:           DW_AT_name	("a3")
+; CHECK:           DW_AT_type	{{.*}} "A3"
+; CHECK:         DW_TAG_lexical_block
+; CHECK:           DW_TAG_variable
+; CHECK:             DW_AT_name	("i3")
+; CHECK:             DW_AT_type	{{.*}} "I3"
+; CHECK:           DW_TAG_variable
+; CHECK:             DW_AT_name	("c3")
+; CHECK:             DW_AT_type	{{.*}} "C3"
+; CHECK:           NULL
+
+; C3 has the inner lexical block scope, typedef Char3 is a child of C3.
+; CHECK:         DW_TAG_structure_type
+; CHECK:           DW_AT_name	("C3")
+; CHECK:           DW_TAG_member
+; CHECK:             DW_AT_type	{{.*}} "C3::Char3"
+; CHECK:           DW_TAG_typedef
+; CHECK:             DW_AT_name	("Char3")
+; CHECK:           NULL
+; CHECK:         NULL
+
+; I3 has the outer lexical block scope.
+; CHECK:       DW_TAG_structure_type
+; CHECK:         DW_AT_name	("I3")
+; CHECK:         DW_TAG_member
+; CHECK:           DW_AT_type	{{.*}} "Int3"
+; CHECK:         NULL
+; CHECK:       NULL
+
+; CHECK:     DW_TAG_inlined_subroutine
+; CHECK:       DW_AT_abstract_origin	{{.*}} "_Z7removedv"
+; CHECK:       DW_TAG_lexical_block
+; CHECK:         DW_TAG_variable
+; CHECK:         DW_TAG_lexical_block
+; CHECK:           DW_TAG_variable
+; CHECK:           DW_TAG_variable
+; CHECK:           NULL
+; CHECK:         NULL
+; CHECK:       NULL
+
+; CHECK:     DW_TAG_inlined_subroutine
+; CHECK:       DW_AT_abstract_origin	{{.*}} "_Z11not_removedv"
+; CHECK:       DW_TAG_lexical_block
+; CHECK:         DW_TAG_variable
+; CHECK:         DW_TAG_lexical_block
+; CHECK:           DW_TAG_variable
+; CHECK:           DW_TAG_variable
+; CHECK:           NULL
+; CHECK:         NULL
+; CHECK:       NULL
+
+; A3 and Int3 defined within the subprogam scope.
+; CHECK:     DW_TAG_structure_type
+; CHECK:       DW_AT_name	("A3")
+; CHECK:       DW_TAG_member
+; CHECK:       NULL
+; CHECK:     DW_TAG_typedef
+; CHECK:       DW_AT_name	("Int3")
+; CHECK:     NULL
+; CHECK:   NULL
+
+%struct.A2 = type { i32 }
+%struct.I2 = type { i32 }
+%struct.C2 = type { i8 }
+%struct.A1 = type { i32 }
+%struct.I1 = type { i32 }
+%struct.C1 = type { i8 }
+%struct.A3 = type { i32 }
+%struct.I3 = type { i32 }
+%struct.C3 = type { i8 }
+
+define dso_local void @_Z11not_removedv() !dbg !8 {
+entry:
+  %a2 = alloca %struct.A2, align 4
+  %i2 = alloca %struct.I2, align 4
+  %c2 = alloca %struct.C2, align 1
+  call void @llvm.dbg.declare(metadata %struct.A2* %a2, metadata !12, metadata !DIExpression()), !dbg !18
+  %i = getelementptr inbounds %struct.A2, %struct.A2* %a2, i32 0, i32 0, !dbg !19
+  %0 = load i32, i32* %i, align 4, !dbg !20
+  %inc = add nsw i32 %0, 1, !dbg !20
+  store i32 %inc, i32* %i, align 4, !dbg !20
+  call void @llvm.dbg.declare(metadata %struct.I2* %i2, metadata !21, metadata !DIExpression()), !dbg !27
+  %j = getelementptr inbounds %struct.I2, %struct.I2* %i2, i32 0, i32 0, !dbg !28
+  %1 = load i32, i32* %j, align 4, !dbg !29
+  %inc1 = add nsw i32 %1, 1, !dbg !29
+  store i32 %inc1, i32* %j, align 4, !dbg !29
+  call void @llvm.dbg.declare(metadata %struct.C2* %c2, metadata !30, metadata !DIExpression()), !dbg !36
+  %c = getelementptr inbounds %struct.C2, %struct.C2* %c2, i32 0, i32 0, !dbg !37
+  %2 = load i8, i8* %c, align 1, !dbg !38
+  %inc2 = add i8 %2, 1, !dbg !38
+  store i8 %inc2, i8* %c, align 1, !dbg !38
+  ret void, !dbg !39
+}
+
+declare void @llvm.dbg.declare(metadata, metadata, metadata)
+
+define dso_local void @_Z3foov() !dbg !40 {
+entry:
+  %a1.i = alloca %struct.A1, align 4
+  %i1.i = alloca %struct.I1, align 4
+  %c1.i = alloca %struct.C1, align 1
+  %a2.i = alloca %struct.A2, align 4
+  %i2.i = alloca %struct.I2, align 4
+  %c2.i = alloca %struct.C2, align 1
+  %a3 = alloca %struct.A3, align 4
+  %i3 = alloca %struct.I3, align 4
+  %c3 = alloca %struct.C3, align 1
+  call void @llvm.dbg.declare(metadata %struct.A3* %a3, metadata !41, metadata !DIExpression()), !dbg !47
+  %i = getelementptr inbounds %struct.A3, %struct.A3* %a3, i32 0, i32 0, !dbg !48
+  %0 = load i32, i32* %i, align 4, !dbg !49
+  %inc = add nsw i32 %0, 1, !dbg !49
+  store i32 %inc, i32* %i, align 4, !dbg !49
+  call void @llvm.dbg.declare(metadata %struct.I3* %i3, metadata !50, metadata !DIExpression()), !dbg !56
+  %j = getelementptr inbounds %struct.I3, %struct.I3* %i3, i32 0, i32 0, !dbg !57
+  %1 = load i32, i32* %j, align 4, !dbg !58
+  %inc1 = add nsw i32 %1, 1, !dbg !58
+  store i32 %inc1, i32* %j, align 4, !dbg !58
+  call void @llvm.dbg.declare(metadata %struct.C3* %c3, metadata !59, metadata !DIExpression()), !dbg !64
+  %c = getelementptr inbounds %struct.C3, %struct.C3* %c3, i32 0, i32 0, !dbg !65
+  %2 = load i8, i8* %c, align 1, !dbg !66
+  %inc2 = add i8 %2, 1, !dbg !66
+  store i8 %inc2, i8* %c, align 1, !dbg !66
+  call void @llvm.dbg.declare(metadata %struct.A1* %a1.i, metadata !67, metadata !DIExpression()), !dbg !73
+  %i.i3 = getelementptr inbounds %struct.A1, %struct.A1* %a1.i, i32 0, i32 0, !dbg !75
+  %3 = load i32, i32* %i.i3, align 4, !dbg !76
+  %inc.i4 = add nsw i32 %3, 1, !dbg !76
+  store i32 %inc.i4, i32* %i.i3, align 4, !dbg !76
+  call void @llvm.dbg.declare(metadata %struct.I1* %i1.i, metadata !77, metadata !DIExpression()), !dbg !83
+  %j.i5 = getelementptr inbounds %struct.I1, %struct.I1* %i1.i, i32 0, i32 0, !dbg !84
+  %4 = load i32, i32* %j.i5, align 4, !dbg !85
+  %inc1.i6 = add nsw i32 %4, 1, !dbg !85
+  store i32 %inc1.i6, i32* %j.i5, align 4, !dbg !85
+  call void @llvm.dbg.declare(metadata %struct.C1* %c1.i, metadata !86, metadata !DIExpression()), !dbg !91
+  %c.i7 = getelementptr inbounds %struct.C1, %struct.C1* %c1.i, i32 0, i32 0, !dbg !92
+  %5 = load i8, i8* %c.i7, align 1, !dbg !93
+  %inc2.i8 = add i8 %5, 1, !dbg !93
+  store i8 %inc2.i8, i8* %c.i7, align 1, !dbg !93
+  call void @llvm.dbg.declare(metadata %struct.A2* %a2.i, metadata !12, metadata !DIExpression()), !dbg !94
+  %i.i = getelementptr inbounds %struct.A2, %struct.A2* %a2.i, i32 0, i32 0, !dbg !96
+  %6 = load i32, i32* %i.i, align 4, !dbg !97
+  %inc.i = add nsw i32 %6, 1, !dbg !97
+  store i32 %inc.i, i32* %i.i, align 4, !dbg !97
+  call void @llvm.dbg.declare(metadata %struct.I2* %i2.i, metadata !21, metadata !DIExpression()), !dbg !98
+  %j.i = getelementptr inbounds %struct.I2, %struct.I2* %i2.i, i32 0, i32 0, !dbg !99
+  %7 = load i32, i32* %j.i, align 4, !dbg !100
+  %inc1.i = add nsw i32 %7, 1, !dbg !100
+  store i32 %inc1.i, i32* %j.i, align 4, !dbg !100
+  call void @llvm.dbg.declare(metadata %struct.C2* %c2.i, metadata !30, metadata !DIExpression()), !dbg !101
+  %c.i = getelementptr inbounds %struct.C2, %struct.C2* %c2.i, i32 0, i32 0, !dbg !102
+  %8 = load i8, i8* %c.i, align 1, !dbg !103
+  %inc2.i = add i8 %8, 1, !dbg !103
+  store i8 %inc2.i, i8* %c.i, align 1, !dbg !103
+  ret void, !dbg !104
+}
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!2, !3, !4, !5, !6}
+!llvm.ident = !{!7}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang version 14.0.0", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None)
+!1 = !DIFile(filename: "test.cpp", directory: "/")
+!2 = !{i32 7, !"Dwarf Version", i32 4}
+!3 = !{i32 2, !"Debug Info Version", i32 3}
+!4 = !{i32 1, !"wchar_size", i32 4}
+!5 = !{i32 7, !"uwtable", i32 1}
+!6 = !{i32 7, !"frame-pointer", i32 2}
+!7 = !{!"clang version 14.0.0"}
+!8 = distinct !DISubprogram(name: "not_removed", linkageName: "_Z11not_removedv", scope: !1, file: !1, line: 17, type: !9, scopeLine: 17, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !105)
+!9 = !DISubroutineType(types: !10)
+!10 = !{null}
+!11 = !{}
+!12 = !DILocalVariable(name: "a2", scope: !13, file: !1, line: 23, type: !14)
+!13 = distinct !DILexicalBlock(scope: !8, file: !1, line: 20, column: 3)
+!14 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "A2", scope: !8, file: !1, line: 18, size: 32, flags: DIFlagTypePassByValue, elements: !15)
+!15 = !{!16}
+!16 = !DIDerivedType(tag: DW_TAG_member, name: "i", scope: !14, file: !1, line: 18, baseType: !17, size: 32)
+!17 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!18 = !DILocation(line: 23, column: 8, scope: !13)
+!19 = !DILocation(line: 23, column: 15, scope: !13)
+!20 = !DILocation(line: 23, column: 16, scope: !13)
+!21 = !DILocalVariable(name: "i2", scope: !22, file: !1, line: 25, type: !23)
+!22 = distinct !DILexicalBlock(scope: !13, file: !1, line: 24, column: 5)
+!23 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "I2", scope: !13, file: !1, line: 21, size: 32, flags: DIFlagTypePassByValue, elements: !24)
+!24 = !{!25}
+!25 = !DIDerivedType(tag: DW_TAG_member, name: "j", scope: !23, file: !1, line: 21, baseType: !26, size: 32)
+!26 = !DIDerivedType(tag: DW_TAG_typedef, name: "Int2", scope: !8, file: !1, line: 19, baseType: !17)
+!27 = !DILocation(line: 25, column: 10, scope: !22)
+!28 = !DILocation(line: 25, column: 17, scope: !22)
+!29 = !DILocation(line: 25, column: 18, scope: !22)
+!30 = !DILocalVariable(name: "c2", scope: !22, file: !1, line: 26, type: !31)
+!31 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "C2", scope: !13, file: !1, line: 22, size: 8, flags: DIFlagTypePassByValue, elements: !32)
+!32 = !{!33}
+!33 = !DIDerivedType(tag: DW_TAG_member, name: "c", scope: !31, file: !1, line: 22, baseType: !34, size: 8)
+!34 = !DIDerivedType(tag: DW_TAG_typedef, name: "Char2", scope: !31, file: !1, line: 22, baseType: !35)
+!35 = !DIBasicType(name: "char", size: 8, encoding: DW_ATE_signed_char)
+!36 = !DILocation(line: 26, column: 10, scope: !22)
+!37 = !DILocation(line: 26, column: 17, scope: !22)
+!38 = !DILocation(line: 26, column: 18, scope: !22)
+!39 = !DILocation(line: 29, column: 1, scope: !8)
+!40 = distinct !DISubprogram(name: "foo", linkageName: "_Z3foov", scope: !1, file: !1, line: 31, type: !9, scopeLine: 31, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !107)
+!41 = !DILocalVariable(name: "a3", scope: !42, file: !1, line: 38, type: !44)
+!42 = distinct !DILexicalBlock(scope: !43, file: !1, line: 36, column: 5)
+!43 = distinct !DILexicalBlock(scope: !40, file: !1, line: 34, column: 3)
+!44 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "A3", scope: !40, file: !1, line: 32, size: 32, flags: DIFlagTypePassByValue, elements: !45)
+!45 = !{!46}
+!46 = !DIDerivedType(tag: DW_TAG_member, name: "i", scope: !44, file: !1, line: 32, baseType: !17, size: 32)
+!47 = !DILocation(line: 38, column: 10, scope: !42)
+!48 = !DILocation(line: 38, column: 17, scope: !42)
+!49 = !DILocation(line: 38, column: 18, scope: !42)
+!50 = !DILocalVariable(name: "i3", scope: !51, file: !1, line: 40, type: !52)
+!51 = distinct !DILexicalBlock(scope: !42, file: !1, line: 39, column: 7)
+!52 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "I3", scope: !43, file: !1, line: 35, size: 32, flags: DIFlagTypePassByValue, elements: !53)
+!53 = !{!54}
+!54 = !DIDerivedType(tag: DW_TAG_member, name: "j", scope: !52, file: !1, line: 35, baseType: !55, size: 32)
+!55 = !DIDerivedType(tag: DW_TAG_typedef, name: "Int3", scope: !40, file: !1, line: 33, baseType: !17)
+!56 = !DILocation(line: 40, column: 12, scope: !51)
+!57 = !DILocation(line: 40, column: 19, scope: !51)
+!58 = !DILocation(line: 40, column: 20, scope: !51)
+!59 = !DILocalVariable(name: "c3", scope: !51, file: !1, line: 41, type: !60)
+!60 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "C3", scope: !42, file: !1, line: 37, size: 8, flags: DIFlagTypePassByValue, elements: !61)
+!61 = !{!62}
+!62 = !DIDerivedType(tag: DW_TAG_member, name: "c", scope: !60, file: !1, line: 37, baseType: !63, size: 8)
+!63 = !DIDerivedType(tag: DW_TAG_typedef, name: "Char3", scope: !60, file: !1, line: 37, baseType: !35)
+!64 = !DILocation(line: 41, column: 12, scope: !51)
+!65 = !DILocation(line: 41, column: 19, scope: !51)
+!66 = !DILocation(line: 41, column: 20, scope: !51)
+!67 = !DILocalVariable(name: "a1", scope: !68, file: !1, line: 8, type: !70)
+!68 = distinct !DILexicalBlock(scope: !69, file: !1, line: 5, column: 3)
+!69 = distinct !DISubprogram(name: "removed", linkageName: "_Z7removedv", scope: !1, file: !1, line: 2, type: !9, scopeLine: 2, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !110)
+!70 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "A1", scope: !69, file: !1, line: 3, size: 32, flags: DIFlagTypePassByValue, elements: !71, identifier: "_ZTSZ7removedvE2A1")
+!71 = !{!72}
+!72 = !DIDerivedType(tag: DW_TAG_member, name: "i", scope: !70, file: !1, line: 3, baseType: !17, size: 32)
+!73 = !DILocation(line: 8, column: 8, scope: !68, inlinedAt: !74)
+!74 = distinct !DILocation(line: 45, column: 3, scope: !40)
+!75 = !DILocation(line: 8, column: 15, scope: !68, inlinedAt: !74)
+!76 = !DILocation(line: 8, column: 16, scope: !68, inlinedAt: !74)
+!77 = !DILocalVariable(name: "i1", scope: !78, file: !1, line: 10, type: !79)
+!78 = distinct !DILexicalBlock(scope: !68, file: !1, line: 9, column: 5)
+!79 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "I1", scope: !68, file: !1, line: 6, size: 32, flags: DIFlagTypePassByValue, elements: !80, identifier: "_ZTSZ7removedvE2I1")
+!80 = !{!81}
+!81 = !DIDerivedType(tag: DW_TAG_member, name: "j", scope: !79, file: !1, line: 6, baseType: !82, size: 32)
+!82 = !DIDerivedType(tag: DW_TAG_typedef, name: "Int1", scope: !69, file: !1, line: 4, baseType: !17)
+!83 = !DILocation(line: 10, column: 10, scope: !78, inlinedAt: !74)
+!84 = !DILocation(line: 10, column: 17, scope: !78, inlinedAt: !74)
+!85 = !DILocation(line: 10, column: 18, scope: !78, inlinedAt: !74)
+!86 = !DILocalVariable(name: "c1", scope: !78, file: !1, line: 11, type: !87)
+!87 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "C1", scope: !68, file: !1, line: 7, size: 8, flags: DIFlagTypePassByValue, elements: !88, identifier: "_ZTSZ7removedvE2C1")
+!88 = !{!89}
+!89 = !DIDerivedType(tag: DW_TAG_member, name: "c", scope: !87, file: !1, line: 7, baseType: !90, size: 8)
+!90 = !DIDerivedType(tag: DW_TAG_typedef, name: "Char1", scope: !87, file: !1, line: 7, baseType: !35)
+!91 = !DILocation(line: 11, column: 10, scope: !78, inlinedAt: !74)
+!92 = !DILocation(line: 11, column: 17, scope: !78, inlinedAt: !74)
+!93 = !DILocation(line: 11, column: 18, scope: !78, inlinedAt: !74)
+!94 = !DILocation(line: 23, column: 8, scope: !13, inlinedAt: !95)
+!95 = distinct !DILocation(line: 46, column: 3, scope: !40)
+!96 = !DILocation(line: 23, column: 15, scope: !13, inlinedAt: !95)
+!97 = !DILocation(line: 23, column: 16, scope: !13, inlinedAt: !95)
+!98 = !DILocation(line: 25, column: 10, scope: !22, inlinedAt: !95)
+!99 = !DILocation(line: 25, column: 17, scope: !22, inlinedAt: !95)
+!100 = !DILocation(line: 25, column: 18, scope: !22, inlinedAt: !95)
+!101 = !DILocation(line: 26, column: 10, scope: !22, inlinedAt: !95)
+!102 = !DILocation(line: 26, column: 17, scope: !22, inlinedAt: !95)
+!103 = !DILocation(line: 26, column: 18, scope: !22, inlinedAt: !95)
+!104 = !DILocation(line: 47, column: 1, scope: !40)
+!105 = !{!14, !23, !26, !31}
+!107 = !{!44, !52, !55, !60}
+!110 = !{!70, !79, !82, !87}
diff --git a/llvm/test/DebugInfo/Generic/verifier-invalid-disubprogram.ll b/llvm/test/DebugInfo/Generic/verifier-invalid-disubprogram.ll
index 6d4d0e93d38f9..54ce1c56c6b30 100644
--- a/llvm/test/DebugInfo/Generic/verifier-invalid-disubprogram.ll
+++ b/llvm/test/DebugInfo/Generic/verifier-invalid-disubprogram.ll
@@ -38,7 +38,7 @@ define void @invalid_subprogram_declaration() !dbg !9 { ret void }
 define void @invalid_retained_nodes_list() !dbg !10 { ret void }
 !10 = distinct !DISubprogram(retainedNodes: !0)
 
-; CHECK: invalid retained nodes, expected DILocalVariable, DILabel or DIImportedEntity
+; CHECK: invalid retained nodes, expected DILocalVariable, DILabel, DIImportedEntity or DIType
 define void @invalid_retained_nodes_expected() !dbg !11 { ret void }
 !11 = distinct !DISubprogram(retainedNodes: !{!0})
 
diff --git a/llvm/test/DebugInfo/X86/local-type-as-template-parameter.ll b/llvm/test/DebugInfo/X86/local-type-as-template-parameter.ll
new file mode 100644
index 0000000000000..586574d3e1aaf
--- /dev/null
+++ b/llvm/test/DebugInfo/X86/local-type-as-template-parameter.ll
@@ -0,0 +1,161 @@
+; REQUIERES: system-linux
+; RUN: %llc_dwarf -mtriple=x86_64-linux -O0 -filetype=obj < %s              \
+; RUN:  | llvm-dwarfdump --show-children --name=foo - \
+; RUN:  | FileCheck --implicit-check-not "{{DW_TAG|NULL}}" %s
+
+; The test ensures that AsmPrinter doesn't crashed compiling this.
+; It also demostrates misplacement for a local type (see PR55680 for details).
+
+; The test compiled from:
+
+; template<typename T>
+; struct A {
+;   A(T &in) : a(in) {}
+;   T a;
+; };
+;
+; __attribute__((always_inline))
+; void foo() {
+;   struct B { int i; };
+;   B objB;
+;   A<B> objA(objB);
+; }
+;
+; int main() {
+;   foo();
+; }
+
+; Concrete out-of-line tree of foo().
+; CHECK: DW_TAG_subprogram
+; CHECK:   DW_AT_abstract_origin {{.*}} "_Z3foov"
+
+; FIXME: 'struct B' should be in the abstract tree below, not here.
+; CHECK:   DW_TAG_structure_type
+; CHECK:     DW_AT_name	("B")
+; CHECK:     DW_TAG_member
+; CHECK:     NULL
+;
+; CHECK:   DW_TAG_variable
+; CHECK:     DW_AT_abstract_origin {{.*}} "objB"
+; CHECK:   DW_TAG_variable
+; CHECK:     DW_AT_abstract_origin {{.*}} "objA"
+
+; CHECK:   NULL
+
+; Abstract tree of foo().
+; CHECK: DW_TAG_subprogram
+; CHECK:   DW_AT_name	("foo")
+; CHECK:   DW_AT_inline	(DW_INL_inlined)
+
+; CHECK:   DW_TAG_variable
+; CHECK:     DW_AT_name	("objB")
+; CHECK:   DW_TAG_variable
+; CHECK:     DW_AT_name	("objA")
+
+; CHECK:   NULL
+
+; CHECK: DW_TAG_inlined_subroutine
+; CHECK:   DW_AT_abstract_origin {{.*}} "_Z3foov"
+; CHECK:   DW_TAG_variable
+; CHECK:     DW_AT_abstract_origin {{.*}} "objB"
+; CHECK:   DW_TAG_variable
+; CHECK:     DW_AT_abstract_origin {{.*}} "objA"
+; CHECK:   NULL
+
+%struct.B = type { i32 }
+%struct.A = type { %struct.B }
+
+define dso_local void @_Z3foov() !dbg !7 {
+entry:
+  %objB = alloca %struct.B, align 4
+  %objA = alloca %struct.A, align 4
+  call void @llvm.dbg.declare(metadata ptr %objB, metadata !30, metadata !DIExpression()), !dbg !31
+  call void @llvm.dbg.declare(metadata ptr %objA, metadata !32, metadata !DIExpression()), !dbg !33
+  call void @_ZN1AIZ3foovE1BEC2ERS0_(ptr noundef nonnull align 4 dereferenceable(4) %objA, ptr noundef nonnull align 4 dereferenceable(4) %objB), !dbg !33
+  ret void, !dbg !34
+}
+
+declare void @llvm.dbg.declare(metadata, metadata, metadata)
+
+define internal void @_ZN1AIZ3foovE1BEC2ERS0_(ptr noundef nonnull align 4 dereferenceable(4) %this, ptr noundef nonnull align 4 dereferenceable(4) %in) unnamed_addr align 2 !dbg !35 {
+entry:
+  %this.addr = alloca ptr, align 8
+  %in.addr = alloca ptr, align 8
+  store ptr %this, ptr %this.addr, align 8
+  call void @llvm.dbg.declare(metadata ptr %this.addr, metadata !36, metadata !DIExpression()), !dbg !38
+  store ptr %in, ptr %in.addr, align 8
+  call void @llvm.dbg.declare(metadata ptr %in.addr, metadata !39, metadata !DIExpression()), !dbg !40
+  %this1 = load ptr, ptr %this.addr, align 8
+  %a = getelementptr inbounds %struct.A, ptr %this1, i32 0, i32 0, !dbg !41
+  %0 = load ptr, ptr %in.addr, align 8, !dbg !42
+  call void @llvm.memcpy.p0.p0.i64(ptr align 4 %a, ptr align 4 %0, i64 4, i1 false), !dbg !41
+  ret void, !dbg !43
+}
+
+define dso_local noundef i32 @main() !dbg !44 {
+entry:
+  %objB.i = alloca %struct.B, align 4
+  %objA.i = alloca %struct.A, align 4
+  call void @llvm.dbg.declare(metadata ptr %objB.i, metadata !30, metadata !DIExpression()), !dbg !47
+  call void @llvm.dbg.declare(metadata ptr %objA.i, metadata !32, metadata !DIExpression()), !dbg !49
+  call void @_ZN1AIZ3foovE1BEC2ERS0_(ptr noundef nonnull align 4 dereferenceable(4) %objA.i, ptr noundef nonnull align 4 dereferenceable(4) %objB.i), !dbg !49
+  ret i32 0, !dbg !50
+}
+
+declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg)
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!22, !23, !24, !25, !26, !27, !28}
+!llvm.ident = !{!29}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang version 15.0.0", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, retainedTypes: !2, splitDebugInlining: false, nameTableKind: None)
+!1 = !DIFile(filename: "test.cpp", directory: "/", checksumkind: CSK_MD5, checksum: "aec7fd397e86f8655ef7f4bb4233b849")
+!2 = !{!3}
+!3 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "A<B>", file: !1, line: 2, size: 32, flags: DIFlagTypePassByValue | DIFlagNonTrivial, elements: !4, templateParams: !20)
+!4 = !{!5, !15}
+!5 = !DIDerivedType(tag: DW_TAG_member, name: "a", scope: !3, file: !1, line: 4, baseType: !6, size: 32)
+!6 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "B", scope: !7, file: !1, line: 9, size: 32, flags: DIFlagTypePassByValue, elements: !12)
+!7 = distinct !DISubprogram(name: "foo", linkageName: "_Z3foov", scope: !1, file: !1, line: 8, type: !8, scopeLine: 8, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !11)
+!8 = !DISubroutineType(types: !9)
+!9 = !{null}
+!10 = !{}
+!11 = !{!6}
+!12 = !{!13}
+!13 = !DIDerivedType(tag: DW_TAG_member, name: "i", scope: !6, file: !1, line: 9, baseType: !14, size: 32)
+!14 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!15 = !DISubprogram(name: "A", scope: !3, file: !1, line: 3, type: !16, scopeLine: 3, flags: DIFlagPrototyped, spFlags: DISPFlagLocalToUnit)
+!16 = !DISubroutineType(types: !17)
+!17 = !{null, !18, !19}
+!18 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !3, size: 64, flags: DIFlagArtificial | DIFlagObjectPointer)
+!19 = !DIDerivedType(tag: DW_TAG_reference_type, baseType: !6, size: 64)
+!20 = !{!21}
+!21 = !DITemplateTypeParameter(name: "T", type: !6)
+!22 = !{i32 7, !"Dwarf Version", i32 5}
+!23 = !{i32 2, !"Debug Info Version", i32 3}
+!24 = !{i32 1, !"wchar_size", i32 4}
+!25 = !{i32 7, !"PIC Level", i32 2}
+!26 = !{i32 7, !"PIE Level", i32 2}
+!27 = !{i32 7, !"uwtable", i32 2}
+!28 = !{i32 7, !"frame-pointer", i32 2}
+!29 = !{!"clang version 15.0.0"}
+!30 = !DILocalVariable(name: "objB", scope: !7, file: !1, line: 10, type: !6)
+!31 = !DILocation(line: 10, column: 5, scope: !7)
+!32 = !DILocalVariable(name: "objA", scope: !7, file: !1, line: 11, type: !3)
+!33 = !DILocation(line: 11, column: 8, scope: !7)
+!34 = !DILocation(line: 12, column: 1, scope: !7)
+!35 = distinct !DISubprogram(name: "A", linkageName: "_ZN1AIZ3foovE1BEC2ERS0_", scope: !3, file: !1, line: 3, type: !16, scopeLine: 3, flags: DIFlagPrototyped, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition, unit: !0, declaration: !15, retainedNodes: !10)
+!36 = !DILocalVariable(name: "this", arg: 1, scope: !35, type: !37, flags: DIFlagArtificial | DIFlagObjectPointer)
+!37 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !3, size: 64)
+!38 = !DILocation(line: 0, scope: !35)
+!39 = !DILocalVariable(name: "in", arg: 2, scope: !35, file: !1, line: 3, type: !19)
+!40 = !DILocation(line: 3, column: 8, scope: !35)
+!41 = !DILocation(line: 3, column: 14, scope: !35)
+!42 = !DILocation(line: 3, column: 16, scope: !35)
+!43 = !DILocation(line: 3, column: 21, scope: !35)
+!44 = distinct !DISubprogram(name: "main", scope: !1, file: !1, line: 14, type: !45, scopeLine: 14, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !10)
+!45 = !DISubroutineType(types: !46)
+!46 = !{!14}
+!47 = !DILocation(line: 10, column: 5, scope: !7, inlinedAt: !48)
+!48 = distinct !DILocation(line: 15, column: 3, scope: !44)
+!49 = !DILocation(line: 11, column: 8, scope: !7, inlinedAt: !48)
+!50 = !DILocation(line: 16, column: 1, scope: !44)
diff --git a/llvm/test/DebugInfo/Generic/split-dwarf-local-import.ll b/llvm/test/DebugInfo/X86/split-dwarf-local-import.ll
similarity index 98%
rename from llvm/test/DebugInfo/Generic/split-dwarf-local-import.ll
rename to llvm/test/DebugInfo/X86/split-dwarf-local-import.ll
index 16a17edccc33e..8a565019eddfd 100644
--- a/llvm/test/DebugInfo/Generic/split-dwarf-local-import.ll
+++ b/llvm/test/DebugInfo/X86/split-dwarf-local-import.ll
@@ -1,4 +1,3 @@
-; REQUIRES: x86-registered-target
 ; RUN: %llc_dwarf -O1 -filetype=obj -split-dwarf-file=%t.dwo < %s | llvm-dwarfdump -debug-info - | FileCheck %s --implicit-check-not "{{DW_TAG|NULL}}"
 
 ; CHECK-LABEL: debug_info contents
diff --git a/llvm/test/DebugInfo/Generic/split-dwarf-local-import2.ll b/llvm/test/DebugInfo/X86/split-dwarf-local-import2.ll
similarity index 98%
rename from llvm/test/DebugInfo/Generic/split-dwarf-local-import2.ll
rename to llvm/test/DebugInfo/X86/split-dwarf-local-import2.ll
index 0057f675f9b31..71f80992d1b04 100644
--- a/llvm/test/DebugInfo/Generic/split-dwarf-local-import2.ll
+++ b/llvm/test/DebugInfo/X86/split-dwarf-local-import2.ll
@@ -1,4 +1,3 @@
-; REQUIRES: x86-registered-target
 ; RUN: %llc_dwarf -split-dwarf-file=%t.dwo < %s | FileCheck %s
 
 ; Ensure function-local DW_TAG_imported_declaration get skipped if its parent subprogram was not emitted.
diff --git a/llvm/test/DebugInfo/Generic/split-dwarf-local-import3.ll b/llvm/test/DebugInfo/X86/split-dwarf-local-import3.ll
similarity index 100%
rename from llvm/test/DebugInfo/Generic/split-dwarf-local-import3.ll
rename to llvm/test/DebugInfo/X86/split-dwarf-local-import3.ll
diff --git a/llvm/unittests/Transforms/Utils/CloningTest.cpp b/llvm/unittests/Transforms/Utils/CloningTest.cpp
index d990808d31fe2..cf587a5002bf9 100644
--- a/llvm/unittests/Transforms/Utils/CloningTest.cpp
+++ b/llvm/unittests/Transforms/Utils/CloningTest.cpp
@@ -808,6 +808,111 @@ TEST(CloneFunction, CloneFunctionWithSubprograms) {
   EXPECT_FALSE(verifyModule(*ImplModule, &errs()));
 }
 
+TEST(CloneFunction, CloneFunctionWithRetainedNodes) {
+  StringRef ImplAssembly = R"(
+    declare void @llvm.dbg.declare(metadata, metadata, metadata)
+
+    define void @test() !dbg !3 {
+      call void @llvm.dbg.declare(metadata i8* undef, metadata !5, metadata !DIExpression()), !dbg !7
+      call void @llvm.dbg.declare(metadata i8* undef, metadata !25, metadata !DIExpression()), !dbg !7
+      ret void
+    }
+
+    declare void @cloned()
+
+    !llvm.dbg.cu = !{!0}
+    !llvm.module.flags = !{!2}
+    !0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, enums: !{!14})
+    !1 = !DIFile(filename: "test.cpp",  directory: "")
+    !2 = !{i32 1, !"Debug Info Version", i32 3}
+    !3 = distinct !DISubprogram(name: "test", scope: !1, unit: !0, retainedNodes: !9)
+    !4 = distinct !DISubprogram(name: "inlined", scope: !1, unit: !0, retainedNodes: !{!5})
+    !5 = !DILocalVariable(name: "awaitables", scope: !4, type: !23)
+    !6 = distinct !DILexicalBlock(scope: !4, file: !1, line: 1)
+    !7 = !DILocation(line: 1, scope: !6, inlinedAt: !8)
+    !8 = !DILocation(line: 10, scope: !3)
+    !9 = !{!15, !17, !18, !23}
+    !14 = distinct !DICompositeType(tag: DW_TAG_enumeration_type, scope: !0, file: !1, line: 13, size: 200, elements: !{})
+    !15 = !DILocalVariable(name: "a", scope: !3)
+    !16 = distinct !DICompositeType(tag: DW_TAG_enumeration_type, scope: !3, file: !1, line: 13, size: 208, elements: !{})
+    !17 = !DIImportedEntity(tag: DW_TAG_imported_declaration, name: "imported_l", file: !1, line: 14, scope: !3, entity: !16)
+    !18 = !DILabel(scope: !3, name: "l", file: !1, line: 22)
+    !22 = !DIBasicType(name: "real", size: 32, align: 32, encoding: DW_ATE_float)
+    !23 = !DIDerivedType(name: "local_float", tag: DW_TAG_const_type, baseType: !22, scope: !3)
+    !float_type = !{!23}
+    !25 = !DILocalVariable(name: "inlined2", scope: !4, type: !23)
+    !inlined2 = !{!25}
+  )";
+
+  LLVMContext Context;
+  SMDiagnostic Error;
+
+  auto ImplModule = parseAssemblyString(ImplAssembly, Error, Context);
+  EXPECT_TRUE(ImplModule != nullptr);
+  auto *Func = ImplModule->getFunction("test");
+  EXPECT_TRUE(Func != nullptr);
+  auto *ClonedFunc = ImplModule->getFunction("cloned");
+  EXPECT_TRUE(ClonedFunc != nullptr);
+
+  EXPECT_FALSE(verifyModule(*ImplModule, &errs()));
+
+  ValueToValueMapTy VMap;
+  SmallVector<ReturnInst *, 8> Returns;
+  ClonedCodeInfo CCI;
+  CloneFunctionInto(ClonedFunc, Func, VMap,
+                    CloneFunctionChangeType::GlobalChanges, Returns, "", &CCI);
+
+  EXPECT_FALSE(verifyModule(*ImplModule, &errs()));
+
+  // Check that retained and local types are copied.
+  DISubprogram *FuncSP = Func->getSubprogram();
+  DISubprogram *ClonedSP = ClonedFunc->getSubprogram();
+  EXPECT_NE(FuncSP, nullptr);
+  EXPECT_NE(ClonedSP, nullptr);
+  EXPECT_EQ(FuncSP->getRetainedNodes().size(), 4u);
+  EXPECT_EQ(FuncSP->getRetainedNodes().size(),
+            ClonedSP->getRetainedNodes().size());
+  for (unsigned I = 0; I < FuncSP->getRetainedNodes().size(); ++I) {
+    auto *Node = FuncSP->getRetainedNodes()[I];
+    auto *Copy = ClonedSP->getRetainedNodes()[I];
+
+    // Check that the order of retainedNodes is preserved by
+    // checking that the corresponding node has the same name.
+    if (auto *Var = dyn_cast<DILocalVariable>(Node)) {
+      auto *VarCopy = dyn_cast<DILocalVariable>(Copy);
+      EXPECT_NE(VarCopy, nullptr);
+      EXPECT_EQ(Var->getName(), VarCopy->getName());
+    } else if (auto *Label = dyn_cast<DILabel>(Node)) {
+      auto *LabelCopy = dyn_cast<DILabel>(Copy);
+      EXPECT_NE(LabelCopy, nullptr);
+      EXPECT_EQ(Label->getName(), LabelCopy->getName());
+    } else if (auto *IE = dyn_cast<DIImportedEntity>(Node)) {
+      auto *IECopy = dyn_cast<DIImportedEntity>(Copy);
+      EXPECT_NE(IECopy, nullptr);
+      EXPECT_EQ(IE->getName(), IECopy->getName());
+    } else if (auto *Ty = dyn_cast<DIType>(Node)) {
+      auto *TyCopy = dyn_cast<DIType>(Copy);
+      EXPECT_NE(TyCopy, nullptr);
+      EXPECT_EQ(Ty->getName(), TyCopy->getName());
+    }
+
+    // Check that node was copied
+    EXPECT_NE(Node, Copy);
+  }
+
+  auto *FloatType = dyn_cast<DIType>(
+      ImplModule->getNamedMetadata("float_type")->getOperand(0));
+  EXPECT_EQ(FloatType->getName(), "local_float");
+  EXPECT_TRUE(VMap.MD().contains(FloatType));
+  EXPECT_NE(FloatType, VMap.MD()[FloatType]);
+
+  auto *Inlined2 = dyn_cast<DILocalVariable>(
+      ImplModule->getNamedMetadata("inlined2")->getOperand(0));
+  EXPECT_EQ(Inlined2->getName(), "inlined2");
+  EXPECT_TRUE(VMap.MD().contains(Inlined2));
+  EXPECT_EQ(Inlined2, VMap.MD()[Inlined2]);
+}
+
 TEST(CloneFunction, CloneFunctionWithInlinedSubprograms) {
   StringRef ImplAssembly = R"(
     declare void @llvm.dbg.declare(metadata, metadata, metadata)

>From b7b0791953fcdb317029e4f61d55e60fd0ad28e1 Mon Sep 17 00:00:00 2001
From: Vladislav Dzhidzhoev <vdzhidzhoev at accesssoftek.com>
Date: Wed, 15 Oct 2025 20:06:23 +0200
Subject: [PATCH 2/2] Reland "[DebugMetadata][DwarfDebug] Support
 function-local types in lexical block scopes (4/7)"

This is an attempt to merge https://reviews.llvm.org/D144006 with LTO
fix.

The last merge attempt was https://github.com/llvm/llvm-project/pull/75385.
The issue with it was investigated in
https://github.com/llvm/llvm-project/pull/75385#issuecomment-2386684121.
If the same (in the sense of ODR identifier/ODR uniquing rules) local type
is present in two modules, and these modules are linked together, the type
gets uniqued. A DIType, that happens to be loaded first, survives linking,
and the references on other types with the same ODR identifier from the modules
loaded later are replaced with the references on the DIType loaded first.
Since defintion subprograms, in scope of which these types are located,
are not deduplicated, the linker output may contain multiple
DISubprogram's having the same (uniqued) type in their retainedNodes
lists.
Further compilation of such modules causes crashes.

To tackle that,
* previous solution to handle LTO linking with local types in
retainedNodes is removed (cloneLocalTypes() function),
* for each loaded distinct (definition) DISubprogram, its retainedNodes
list is scanned after loading, and DITypes with a scope of another subprogram
are removed. If something from a Function corresponding to the
DISubprogram references uniqued type, we rely on cross-CU links.

With this approach, clang builds without crashes in FullLTO (which is not
the case for https://github.com/llvm/llvm-project/pull/75385).

Additionally:
* a check is added to Verifier to report about local types located
in a wrong retainedNodes list,
* DIBuilder's methods for type creation are updated, as https://reviews.llvm.org/D144006
has gotten slightly out-of-date.

Commit https://github.com/llvm/llvm-project/pull/75385 and the new changes
are placed in separate commits to simplify review process.
---
 clang/test/Analysis/analyzeOneFunction.cpp    |  4 +-
 .../DebugInfo/CXX/lambda-capture-packs.cpp    | 15 +++---
 llvm/include/llvm/AsmParser/LLParser.h        |  4 ++
 llvm/include/llvm/IR/DebugInfoMetadata.h      | 15 ++++++
 llvm/lib/AsmParser/LLParser.cpp               |  7 +++
 llvm/lib/Bitcode/Reader/MetadataLoader.cpp    | 33 ++++---------
 llvm/lib/IR/DIBuilder.cpp                     | 26 ++++++++---
 llvm/lib/IR/DebugInfoMetadata.cpp             | 39 ++++++++++++++++
 llvm/lib/IR/Verifier.cpp                      | 10 ++++
 ...one-local-types.ll => local-type-scope.ll} | 16 ++++---
 .../ARM/call-graph-section-assembly.ll        |  4 +-
 .../X86/call-graph-section-assembly.ll        |  4 +-
 .../Inputs/cleanup-retained-nodes.ll          | 17 +++++++
 .../DebugInfo/X86/cleanup-retained-nodes.ll   | 46 +++++++++++++++++++
 .../X86/llparser-cleanup-retained-nodes.ll    | 37 +++++++++++++++
 15 files changed, 224 insertions(+), 53 deletions(-)
 rename llvm/test/Bitcode/{clone-local-types.ll => local-type-scope.ll} (79%)
 create mode 100644 llvm/test/DebugInfo/Inputs/cleanup-retained-nodes.ll
 create mode 100644 llvm/test/DebugInfo/X86/cleanup-retained-nodes.ll
 create mode 100644 llvm/test/DebugInfo/X86/llparser-cleanup-retained-nodes.ll

diff --git a/clang/test/Analysis/analyzeOneFunction.cpp b/clang/test/Analysis/analyzeOneFunction.cpp
index 3a362dfd9a08c..e4c96eb35f06b 100644
--- a/clang/test/Analysis/analyzeOneFunction.cpp
+++ b/clang/test/Analysis/analyzeOneFunction.cpp
@@ -5,9 +5,9 @@
 // RUN:   -analyze-function="c:@S at Window@F at overloaded#I#"
 
 // RUN: %clang_extdef_map %s | FileCheck %s
-// CHECK:      27:c:@S at Window@F at overloaded#I#
+// CHECK:      27:c:@S at Window@F at overloaded#d#
 // CHECK-NEXT: 27:c:@S at Window@F at overloaded#C#
-// CHECK-NEXT: 27:c:@S at Window@F at overloaded#d#
+// CHECK-NEXT: 27:c:@S at Window@F at overloaded#I#
 
 void clang_analyzer_warnIfReached();
 
diff --git a/clang/test/DebugInfo/CXX/lambda-capture-packs.cpp b/clang/test/DebugInfo/CXX/lambda-capture-packs.cpp
index 021b85d4ce3a4..609a71c6ec015 100644
--- a/clang/test/DebugInfo/CXX/lambda-capture-packs.cpp
+++ b/clang/test/DebugInfo/CXX/lambda-capture-packs.cpp
@@ -2,14 +2,6 @@
 // RUN:   -debug-info-kind=standalone -std=c++26 %s -o - | FileCheck %s
 
 
-// CHECK: ![[PACK1:[0-9]+]] = distinct !DISubprogram(name: "capture_pack<int>"
-// CHECK: ![[PACK2:[0-9]+]] = distinct !DISubprogram(name: "capture_pack<int, int>"
-// CHECK: ![[PACK3:[0-9]+]] = distinct !DISubprogram(name: "capture_pack_and_locals<int>"
-// CHECK: ![[PACK4:[0-9]+]] = distinct !DISubprogram(name: "capture_pack_and_locals<int, int>"
-// CHECK: ![[PACK5:[0-9]+]] = distinct !DISubprogram(name: "capture_pack_and_this<int>"
-// CHECK: ![[PACK6:[0-9]+]] = distinct !DISubprogram(name: "capture_pack_and_this<int, int>"
-// CHECK: ![[PACK7:[0-9]+]] = distinct !DISubprogram(name: "capture_binding_and_param_pack<int, int>"
-
 template<typename... Args>
 auto capture_pack(Args... args) {
   return [args..., ...params = args] {
@@ -17,6 +9,7 @@ auto capture_pack(Args... args) {
   }();
 }
 
+// CHECK: ![[PACK1:[0-9]+]] = distinct !DISubprogram(name: "capture_pack<int>"
 // CHECK:      distinct !DICompositeType(tag: DW_TAG_class_type, scope: ![[PACK1]]
 // CHECK-SAME:                           elements: ![[PACK1_ELEMS:[0-9]+]]
 // CHECK-DAG:  ![[PACK1_ELEMS]] = !{![[PACK1_ARGS:[0-9]+]], ![[PACK1_PARAMS:[0-9]+]]}
@@ -24,6 +17,7 @@ auto capture_pack(Args... args) {
 // CHECK-DAG:  ![[PACK1_PARAMS]] = !DIDerivedType(tag: DW_TAG_member, name: "params"
 // CHECK-NOT:  DW_TAG_member
 
+// CHECK: ![[PACK2:[0-9]+]] = distinct !DISubprogram(name: "capture_pack<int, int>"
 // CHECK:      distinct !DICompositeType(tag: DW_TAG_class_type, scope: ![[PACK2]]
 // CHECK-SAME:                           elements: ![[PACK2_ELEMS:[0-9]+]]
 // CHECK:      ![[PACK2_ELEMS]] = !{![[PACK2_ARGS:[0-9]+]]
@@ -42,6 +36,7 @@ auto capture_pack_and_locals(int x, Args... args) {
   }();
 }
 
+// CHECK: ![[PACK3:[0-9]+]] = distinct !DISubprogram(name: "capture_pack_and_locals<int>"
 // CHECK:      distinct !DICompositeType(tag: DW_TAG_class_type, scope: ![[PACK3]]
 // CHECK-SAME:                           elements: ![[PACK3_ELEMS:[0-9]+]]
 // CHECK:      ![[PACK3_ELEMS]] = !{![[PACK3_ARGS:[0-9]+]]
@@ -55,6 +50,7 @@ auto capture_pack_and_locals(int x, Args... args) {
 // CHECK-DAG:  ![[PACK3_W]] = !DIDerivedType(tag: DW_TAG_member, name: "w"
 // CHECK-NOT:  DW_TAG_member
 
+// CHECK: ![[PACK4:[0-9]+]] = distinct !DISubprogram(name: "capture_pack_and_locals<int, int>"
 // CHECK:      distinct !DICompositeType(tag: DW_TAG_class_type, scope: ![[PACK4]]
 // CHECK-SAME:                           elements: ![[PACK4_ELEMS:[0-9]+]]
 // CHECK:      ![[PACK4_ELEMS]] = !{![[PACK4_ARGS:[0-9]+]]
@@ -90,6 +86,7 @@ struct Foo {
   int w = 10;
 } f;
 
+// CHECK: ![[PACK5:[0-9]+]] = distinct !DISubprogram(name: "capture_pack_and_this<int>"
 // CHECK:      distinct !DICompositeType(tag: DW_TAG_class_type, scope: ![[PACK5]]
 // CHECK-SAME:                           elements: ![[PACK5a_ELEMS:[0-9]+]]
 // CHECK:      ![[PACK5a_ELEMS]] = !{![[PACK5a_THIS:[0-9]+]]
@@ -120,6 +117,7 @@ struct Foo {
 // CHECK-DAG:  ![[PACK5c_THIS]] = !DIDerivedType(tag: DW_TAG_member, name: "this"
 // CHECK-NOT:  DW_TAG_member
 
+// CHECK: ![[PACK6:[0-9]+]] = distinct !DISubprogram(name: "capture_pack_and_this<int, int>"
 // CHECK:      distinct !DICompositeType(tag: DW_TAG_class_type, scope: ![[PACK6]]
 // CHECK-SAME:                           elements: ![[PACK6a_ELEMS:[0-9]+]]
 // CHECK:      ![[PACK6a_ELEMS]] = !{![[PACK6a_THIS:[0-9]+]]
@@ -168,6 +166,7 @@ auto capture_binding_and_param_pack(Args... args) {
   }();
 }
 
+// CHECK: ![[PACK7:[0-9]+]] = distinct !DISubprogram(name: "capture_binding_and_param_pack<int, int>"
 // CHECK: distinct !DICompositeType(tag: DW_TAG_structure_type, name: "C"
 // CHECK:      distinct !DICompositeType(tag: DW_TAG_class_type, scope: ![[PACK7]]
 // CHECK-SAME:                           elements: ![[PACK7_ELEMS:[0-9]+]]
diff --git a/llvm/include/llvm/AsmParser/LLParser.h b/llvm/include/llvm/AsmParser/LLParser.h
index 9eb31d7e0a451..ae568f8ead039 100644
--- a/llvm/include/llvm/AsmParser/LLParser.h
+++ b/llvm/include/llvm/AsmParser/LLParser.h
@@ -181,6 +181,10 @@ namespace llvm {
     /// Keeps track of source locations for Values, BasicBlocks, and Functions.
     AsmParserContext *ParserContext;
 
+    /// retainedNodes of these subprograms should be cleaned up from incorrectly
+    /// scoped local types.
+    SmallVector<DISubprogram *> NewDistinctSPs;
+
     /// Only the llvm-as tool may set this to false to bypass
     /// UpgradeDebuginfo so it can generate broken bitcode.
     bool UpgradeDebugInfo;
diff --git a/llvm/include/llvm/IR/DebugInfoMetadata.h b/llvm/include/llvm/IR/DebugInfoMetadata.h
index c626efc9daaa4..e16b08d122a65 100644
--- a/llvm/include/llvm/IR/DebugInfoMetadata.h
+++ b/llvm/include/llvm/IR/DebugInfoMetadata.h
@@ -2536,6 +2536,21 @@ class DISubprogram : public DILocalScope {
     replaceOperandWith(7, N.get());
   }
 
+  /// Remove types that do not belong to the subprogram's scope from
+  /// retainedNodes list.
+  void cleanupRetainedNodes();
+
+  /// When DebugTypeODRUniquing is enabled, after multiple modules are loaded,
+  /// some subprograms (that are from different compilation units, usually)
+  /// may have references to the same local type in their retainedNodes lists.
+  ///
+  /// Clean up such references.
+  template <typename RangeT>
+  static void cleanupRetainedNodes(const RangeT &NewDistinctSPs) {
+    for (DISubprogram *SP : NewDistinctSPs)
+      SP->cleanupRetainedNodes();
+  }
+
   /// Check if this subprogram describes the given function.
   ///
   /// FIXME: Should this be looking through bitcasts?
diff --git a/llvm/lib/AsmParser/LLParser.cpp b/llvm/lib/AsmParser/LLParser.cpp
index 5164cec33e6f5..3d06a6c1d0da9 100644
--- a/llvm/lib/AsmParser/LLParser.cpp
+++ b/llvm/lib/AsmParser/LLParser.cpp
@@ -54,6 +54,7 @@
 #include <cassert>
 #include <cstring>
 #include <optional>
+#include <utility>
 #include <vector>
 
 using namespace llvm;
@@ -428,6 +429,8 @@ bool LLParser::validateEndOfModule(bool UpgradeDebugInfo) {
       N.second->resolveCycles();
   }
 
+  DISubprogram::cleanupRetainedNodes(std::exchange(NewDistinctSPs, {}));
+
   for (auto *Inst : InstsWithTBAATag) {
     MDNode *MD = Inst->getMetadata(LLVMContext::MD_tbaa);
     // With incomplete IR, the tbaa metadata may have been dropped.
@@ -5987,6 +5990,10 @@ bool LLParser::parseDISubprogram(MDNode *&Result, bool IsDistinct) {
        thisAdjustment.Val, flags.Val, SPFlags, unit.Val, templateParams.Val,
        declaration.Val, retainedNodes.Val, thrownTypes.Val, annotations.Val,
        targetFuncName.Val, keyInstructions.Val));
+
+  if (IsDistinct)
+    NewDistinctSPs.push_back(cast<DISubprogram>(Result));
+
   return false;
 }
 
diff --git a/llvm/lib/Bitcode/Reader/MetadataLoader.cpp b/llvm/lib/Bitcode/Reader/MetadataLoader.cpp
index b9e1583f31462..b4ca5ef4eaf26 100644
--- a/llvm/lib/Bitcode/Reader/MetadataLoader.cpp
+++ b/llvm/lib/Bitcode/Reader/MetadataLoader.cpp
@@ -452,6 +452,10 @@ class MetadataLoader::MetadataLoaderImpl {
   /// metadata.
   SmallDenseMap<Function *, DISubprogram *, 16> FunctionsWithSPs;
 
+  /// retainedNodes of these subprograms should be cleaned up from incorrectly
+  /// scoped local types.
+  SmallVector<DISubprogram *> NewDistinctSPs;
+
   // Map the bitcode's custom MDKind ID to the Module's MDKind ID.
   DenseMap<unsigned, unsigned> MDKindMap;
 
@@ -732,29 +736,7 @@ class MetadataLoader::MetadataLoaderImpl {
     upgradeCUVariables();
     if (ModuleLevel)
       upgradeCULocals();
-  }
-
-  void cloneLocalTypes() {
-    for (Metadata *M : MetadataList) {
-      if (auto *SP = dyn_cast_or_null<DISubprogram>(M)) {
-        auto RetainedNodes = SP->getRetainedNodes();
-        SmallVector<Metadata *> MDs(RetainedNodes.begin(), RetainedNodes.end());
-        bool HasChanged = false;
-        for (auto &N : MDs)
-          if (auto *T = dyn_cast<DIType>(N))
-            if (auto *LS = dyn_cast_or_null<DILocalScope>(T->getScope()))
-              if (auto *Parent = findEnclosingSubprogram(LS))
-                if (Parent != SP) {
-                  HasChanged = true;
-                  auto NewT = T->clone();
-                  NewT->replaceOperandWith(1, SP);
-                  N = MDNode::replaceWithUniqued(std::move(NewT));
-                }
-
-        if (HasChanged)
-          SP->replaceRetainedNodes(MDNode::get(Context, MDs));
-      }
-    }
+    DISubprogram::cleanupRetainedNodes(std::exchange(NewDistinctSPs, {}));
   }
 
   void callMDTypeCallback(Metadata **Val, unsigned TypeID);
@@ -1133,7 +1115,6 @@ Error MetadataLoader::MetadataLoaderImpl::parseMetadata(bool ModuleLevel) {
       // placeholders, that we flush here.
       resolveForwardRefsAndPlaceholders(Placeholders);
       upgradeDebugInfo(ModuleLevel);
-      cloneLocalTypes();
       // Return at the beginning of the block, since it is easy to skip it
       // entirely from there.
       Stream.ReadBlockEnd(); // Pop the abbrev block context.
@@ -1165,7 +1146,6 @@ Error MetadataLoader::MetadataLoaderImpl::parseMetadata(bool ModuleLevel) {
     case BitstreamEntry::EndBlock:
       resolveForwardRefsAndPlaceholders(Placeholders);
       upgradeDebugInfo(ModuleLevel);
-      cloneLocalTypes();
       return Error::success();
     case BitstreamEntry::Record:
       // The interesting case.
@@ -2048,6 +2028,9 @@ Error MetadataLoader::MetadataLoaderImpl::parseOneMetadata(
     MetadataList.assignValue(SP, NextMetadataNo);
     NextMetadataNo++;
 
+    if (IsDistinct)
+      NewDistinctSPs.push_back(SP);
+
     // Upgrade sp->function mapping to function->sp mapping.
     if (HasFn) {
       if (auto *CMD = dyn_cast_or_null<ConstantAsMetadata>(CUorFn))
diff --git a/llvm/lib/IR/DIBuilder.cpp b/llvm/lib/IR/DIBuilder.cpp
index 49198ed7671dc..2ee021e92415a 100644
--- a/llvm/lib/IR/DIBuilder.cpp
+++ b/llvm/lib/IR/DIBuilder.cpp
@@ -377,10 +377,13 @@ DIDerivedType *DIBuilder::createTypedef(DIType *Ty, StringRef Name,
                                         DIScope *Context, uint32_t AlignInBits,
                                         DINode::DIFlags Flags,
                                         DINodeArray Annotations) {
-  return DIDerivedType::get(VMContext, dwarf::DW_TAG_typedef, Name, File,
-                            LineNo, getNonCompileUnitScope(Context), Ty,
-                            (uint64_t)0, AlignInBits, (uint64_t)0, std::nullopt,
-                            std::nullopt, Flags, nullptr, Annotations);
+  auto *T = DIDerivedType::get(
+      VMContext, dwarf::DW_TAG_typedef, Name, File, LineNo,
+      getNonCompileUnitScope(Context), Ty, (uint64_t)0, AlignInBits,
+      (uint64_t)0, std::nullopt, std::nullopt, Flags, nullptr, Annotations);
+  if (isa_and_nonnull<DILocalScope>(Context))
+    getSubprogramNodesTrackingVector(Context).emplace_back(T);
+  return T;
 }
 
 DIDerivedType *
@@ -604,6 +607,8 @@ DICompositeType *DIBuilder::createStructType(
       nullptr, UniqueIdentifier, nullptr, nullptr, nullptr, nullptr, nullptr,
       nullptr, Specification, NumExtraInhabitants);
   trackIfUnresolved(R);
+  if (isa_and_nonnull<DILocalScope>(Context))
+    getSubprogramNodesTrackingVector(Context).emplace_back(R);
   return R;
 }
 
@@ -688,6 +693,8 @@ DIDerivedType *DIBuilder::createSetType(DIScope *Scope, StringRef Name,
                                SizeInBits, AlignInBits, 0, std::nullopt,
                                std::nullopt, DINode::FlagZero);
   trackIfUnresolved(R);
+  if (isa_and_nonnull<DILocalScope>(Scope))
+    getSubprogramNodesTrackingVector(Scope).emplace_back(R);
   return R;
 }
 
@@ -723,6 +730,8 @@ DICompositeType *DIBuilder::createArrayType(
                               : (Metadata *)cast<DIVariable *>(RK),
       nullptr, nullptr, 0, BitStride);
   trackIfUnresolved(R);
+  if (isa_and_nonnull<DILocalScope>(Scope))
+    getSubprogramNodesTrackingVector(Scope).emplace_back(R);
   return R;
 }
 
@@ -877,9 +886,12 @@ DISubrangeType *DIBuilder::createSubrangeType(
     uint64_t SizeInBits, uint32_t AlignInBits, DINode::DIFlags Flags,
     DIType *Ty, Metadata *LowerBound, Metadata *UpperBound, Metadata *Stride,
     Metadata *Bias) {
-  return DISubrangeType::get(VMContext, Name, File, LineNo, Scope, SizeInBits,
-                             AlignInBits, Flags, Ty, LowerBound, UpperBound,
-                             Stride, Bias);
+  auto *T = DISubrangeType::get(VMContext, Name, File, LineNo, Scope,
+                                SizeInBits, AlignInBits, Flags, Ty, LowerBound,
+                                UpperBound, Stride, Bias);
+  if (isa_and_nonnull<DILocalScope>(Scope))
+    getSubprogramNodesTrackingVector(Scope).emplace_back(T);
+  return T;
 }
 
 static void checkGlobalVariableScope(DIScope *Context) {
diff --git a/llvm/lib/IR/DebugInfoMetadata.cpp b/llvm/lib/IR/DebugInfoMetadata.cpp
index e30df88e6b56b..5d268f16d38dd 100644
--- a/llvm/lib/IR/DebugInfoMetadata.cpp
+++ b/llvm/lib/IR/DebugInfoMetadata.cpp
@@ -1425,6 +1425,45 @@ bool DISubprogram::describes(const Function *F) const {
   assert(F && "Invalid function");
   return F->getSubprogram() == this;
 }
+
+void DISubprogram::cleanupRetainedNodes() {
+  // Checks if a metadata node from retainedTypes is a type not belonging to
+  // this subprogram.
+  auto IsAlienType = [this](DINode *N) {
+    auto *T = dyn_cast_or_null<DIType>(N);
+    if (!T)
+      return false;
+
+    DISubprogram *TypeSP = nullptr;
+    // The type might have been global in the previously loaded IR modules.
+    if (auto *LS = dyn_cast_or_null<DILocalScope>(T->getScope()))
+      TypeSP = LS->getSubprogram();
+
+    return this != TypeSP;
+  };
+
+  // As this is expected to be called during module loading, before
+  // stripping old or incorrect debug info, perform minimal sanity check.
+  if (!isa_and_present<MDTuple>(getRawRetainedNodes()))
+    return;
+
+  MDTuple *RetainedNodes = cast<MDTuple>(getRawRetainedNodes());
+  SmallVector<Metadata *> MDs;
+  MDs.reserve(RetainedNodes->getNumOperands());
+  for (const MDOperand &Node : RetainedNodes->operands()) {
+    // Ignore malformed retainedNodes.
+    if (Node && !isa<DINode>(Node))
+      return;
+
+    auto *N = cast_or_null<DINode>(Node);
+    if (!IsAlienType(N))
+      MDs.push_back(N);
+  }
+
+  if (MDs.size() != RetainedNodes->getNumOperands())
+    replaceRetainedNodes(MDNode::get(getContext(), MDs));
+}
+
 DILexicalBlockBase::DILexicalBlockBase(LLVMContext &C, unsigned ID,
                                        StorageType Storage,
                                        ArrayRef<Metadata *> Ops)
diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp
index 3cea732c4e130..dc29218516359 100644
--- a/llvm/lib/IR/Verifier.cpp
+++ b/llvm/lib/IR/Verifier.cpp
@@ -1568,6 +1568,16 @@ void Verifier::visitDISubprogram(const DISubprogram &N) {
               "invalid retained nodes, expected DILocalVariable, DILabel, "
               "DIImportedEntity or DIType",
               &N, Node, Op);
+      if (auto *T = dyn_cast<DIType>(Op)) {
+        auto *TypeScope = dyn_cast_or_null<DILocalScope>(T->getScope());
+        DISubprogram *TypeSP = TypeScope ? TypeScope->getSubprogram() : nullptr;
+        DICompileUnit *TypeSPUnit = TypeSP ? TypeSP->getUnit() : nullptr;
+        if (isa_and_nonnull<DILocalScope>(T->getScope()))
+          CheckDI(!TypeScope || TypeSP == &N,
+                  "invalid retained node, DIType should have the scope of "
+                  "DISubprogram",
+                  &N, N.getUnit(), Node, Op, TypeScope, TypeSP, TypeSPUnit);
+      }
     }
   }
   CheckDI(!hasConflictingReferenceFlags(N.getFlags()),
diff --git a/llvm/test/Bitcode/clone-local-types.ll b/llvm/test/Bitcode/local-type-scope.ll
similarity index 79%
rename from llvm/test/Bitcode/clone-local-types.ll
rename to llvm/test/Bitcode/local-type-scope.ll
index d2607303ec610..7652093409ac3 100644
--- a/llvm/test/Bitcode/clone-local-types.ll
+++ b/llvm/test/Bitcode/local-type-scope.ll
@@ -1,16 +1,18 @@
-; RUN: llvm-as < %s -o %t0
-; RUN: llvm-dis %t0 -o - | FileCheck %s
+; RUN: llvm-as --disable-verify < %s -o %t0
+; RUN: opt --passes=verify %t0 -o /dev/null
+; RUN: llvm-dis %t0 -o - | FileCheck %s --implicit-check-not=DICompositeType
 
-; Ensure that function-local types with the same ODR identifier belonging
-; to different subprograms are not deduplicated when a module is being loaded.
+; During module loading, if a local type appears in retainedNodes
+; field of multiple DISubprograms due to ODR-uniquing,
+; retainedNodes should be cleaned up, so that only one DISubprogram
+; will have this type in its retainedNodes.
 
 ; CHECK: [[CU:![0-9]+]] = distinct !DICompileUnit
 ; CHECK: [[BAR:![0-9]+]] = distinct !DISubprogram(name: "bar", {{.*}}, retainedNodes: [[RN_BAR:![0-9]+]])
-; CHECK: [[RN_BAR]] = !{[[T2:![0-9]+]]}
-; CHECK: [[T2]] = !DICompositeType(tag: DW_TAG_class_type, {{.*}}, identifier: "local_type")
+; CHECK: [[RN_BAR]] = !{}
 ; CHECK: [[FOO:![0-9]+]] = distinct !DISubprogram(name: "foo", {{.*}}, retainedNodes: [[RN_FOO:![0-9]+]])
 ; CHECK: [[RN_FOO]] = !{[[T1:![0-9]+]]}
-; CHECK: [[T1]] = !DICompositeType(tag: DW_TAG_class_type, {{.*}}, identifier: "local_type")
+; CHECK: [[T1]] = !DICompositeType(tag: DW_TAG_class_type, scope: [[FOO]], {{.*}}, identifier: "local_type")
 
 target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
 target triple = "x86_64-unknown-linux-gnu"
diff --git a/llvm/test/CodeGen/ARM/call-graph-section-assembly.ll b/llvm/test/CodeGen/ARM/call-graph-section-assembly.ll
index 3d3974ee6ba3b..483db480af7bb 100644
--- a/llvm/test/CodeGen/ARM/call-graph-section-assembly.ll
+++ b/llvm/test/CodeGen/ARM/call-graph-section-assembly.ll
@@ -49,9 +49,9 @@ entry:
 ;; Number of unique direct callees.
 ; CHECK-NEXT: .byte	  3
 ;; Direct callees.
-; CHECK-NEXT: .long	direct_foo
-; CHECK-NEXT: .long	direct_bar
 ; CHECK-NEXT: .long	direct_baz
+; CHECK-NEXT: .long	direct_bar
+; CHECK-NEXT: .long	direct_foo
 ;; Number of unique indirect target type IDs.
 ; CHECK-NEXT: .byte   3
 ;; Indirect type IDs.
diff --git a/llvm/test/CodeGen/X86/call-graph-section-assembly.ll b/llvm/test/CodeGen/X86/call-graph-section-assembly.ll
index cdbad668aec54..5f42442341b30 100644
--- a/llvm/test/CodeGen/X86/call-graph-section-assembly.ll
+++ b/llvm/test/CodeGen/X86/call-graph-section-assembly.ll
@@ -48,9 +48,9 @@ entry:
 ;; Number of unique direct callees.
 ; CHECK-NEXT: .byte	  3
 ;; Direct callees.
-; CHECK-NEXT: .quad	direct_foo
-; CHECK-NEXT: .quad	direct_bar
 ; CHECK-NEXT: .quad	direct_baz
+; CHECK-NEXT: .quad	direct_bar
+; CHECK-NEXT: .quad	direct_foo
 ;; Number of unique indirect target type IDs.
 ; CHECK-NEXT: .byte   3
 ;; Indirect type IDs.
diff --git a/llvm/test/DebugInfo/Inputs/cleanup-retained-nodes.ll b/llvm/test/DebugInfo/Inputs/cleanup-retained-nodes.ll
new file mode 100644
index 0000000000000..d2cca1bc4f8a2
--- /dev/null
+++ b/llvm/test/DebugInfo/Inputs/cleanup-retained-nodes.ll
@@ -0,0 +1,17 @@
+target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-unknown-linux-gnu"
+
+ at var = global i8 0, align 4, !dbg !7
+
+!llvm.module.flags = !{!0, !1, !2}
+!llvm.dbg.cu = !{!3}
+
+!0 = !{i32 7, !"Dwarf Version", i32 2}
+!1 = !{i32 2, !"Debug Info Version", i32 3}
+!2 = !{i32 1, !"wchar_size", i32 4}
+!3 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !5, producer: "clang", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None)
+!4 = !{}
+!5 = !DIFile(filename: "tmp2.cpp", directory: "/tmp/")
+!6 = !DICompositeType(tag: DW_TAG_class_type, scope: !3, file: !5, line: 212, size: 8, flags: DIFlagTypePassByValue, elements: !4, identifier: "type_global_in_another_module")
+!7 = !DIGlobalVariableExpression(var: !8, expr: !DIExpression())
+!8 = distinct !DIGlobalVariable(name: "var", scope: !3, file: !5, line: 1, type: !6, isLocal: false, isDefinition: true)
diff --git a/llvm/test/DebugInfo/X86/cleanup-retained-nodes.ll b/llvm/test/DebugInfo/X86/cleanup-retained-nodes.ll
new file mode 100644
index 0000000000000..f26faa14b9e9e
--- /dev/null
+++ b/llvm/test/DebugInfo/X86/cleanup-retained-nodes.ll
@@ -0,0 +1,46 @@
+; RUN: llvm-as %s -o %t.bc
+; RUN: llvm-as %p/../Inputs/cleanup-retained-nodes.ll -o %t.global.bc
+; RUN: llvm-link %t.global.bc %t.bc %t.bc -o %t.linked.bc
+; RUN: opt --passes=verify %t.linked.bc -o /dev/null
+; RUN: llvm-dis %t.linked.bc -o - | FileCheck %s --implicit-check-not=DICompositeType
+
+; During module loading, if a local type appears in retainedNodes
+; field of multiple DISubprograms due to ODR-uniquing,
+; retainedNodes should be cleaned up, so that only one DISubprogram
+; will have this type in its retainedNodes.
+
+; CHECK: distinct !DICompositeType(tag: DW_TAG_class_type, {{.*}}, identifier: "type_global_in_another_module")
+; CHECK: [[EMPTY:![0-9]+]] = !{}
+; CHECK: [[BAR1:![0-9]+]] = distinct !DISubprogram(name: "bar", {{.*}}, retainedNodes: [[RN_BAR1:![0-9]+]])
+; CHECK: [[RN_BAR1]] = !{[[T1:![0-9]+]], [[T1:![0-9]+]], [[T1:![0-9]+]], [[T2:![0-9]+]]}
+; CHECK: [[T1]] = distinct !DICompositeType(tag: DW_TAG_class_type, scope: [[BAR1]], {{.*}}, identifier: "local_type")
+; CHECK: [[T2]] = distinct !DICompositeType(tag: DW_TAG_class_type, scope: [[LB:![0-9]+]], {{.*}}, identifier: "local_type_in_block")
+; CHECK: [[LB]] = !DILexicalBlock(scope: [[BAR1]]
+; CHECK: {{![0-9]+}} = distinct !DISubprogram(name: "bar", {{.*}}, retainedNodes: [[EMPTY:![0-9]+]])
+
+target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-unknown-linux-gnu"
+
+ at llvm.used = appending global [1 x ptr] [ptr @bar]
+
+define internal void @bar(ptr %this) !dbg !10 {
+  ret void
+}
+
+!llvm.module.flags = !{!0, !1, !2}
+!llvm.dbg.cu = !{!8}
+
+!0 = !{i32 7, !"Dwarf Version", i32 2}
+!1 = !{i32 2, !"Debug Info Version", i32 3}
+!2 = !{i32 1, !"wchar_size", i32 4}
+!3 = !DICompositeType(tag: DW_TAG_class_type, scope: !10, file: !9, line: 212, size: 8, flags: DIFlagTypePassByValue, elements: !7, identifier: "type_global_in_another_module")
+!4 = !DICompositeType(tag: DW_TAG_class_type, scope: !5, file: !9, line: 211, size: 8, flags: DIFlagTypePassByValue, elements: !7, identifier: "local_type_in_block")
+!5 = !DILexicalBlock(scope: !10)
+; All repeating occurences of a uniqued type in retainedNodes must be checked.
+!6 = !{!12, !12, !12, !4, !3}
+!7 = !{}
+!8 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !9, producer: "clang", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None)
+!9 = !DIFile(filename: "tmp.cpp", directory: "/tmp/")
+!10 = distinct !DISubprogram(name: "bar", scope: !9, file: !9, line: 68, type: !11, scopeLine: 68, flags: DIFlagPrototyped, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition | DISPFlagOptimized, unit: !8, retainedNodes: !6)
+!11 = !DISubroutineType(types: !7)
+!12 = !DICompositeType(tag: DW_TAG_class_type, scope: !10, file: !9, line: 210, size: 8, flags: DIFlagTypePassByValue, elements: !7, identifier: "local_type")
diff --git a/llvm/test/DebugInfo/X86/llparser-cleanup-retained-nodes.ll b/llvm/test/DebugInfo/X86/llparser-cleanup-retained-nodes.ll
new file mode 100644
index 0000000000000..4e61633ad7604
--- /dev/null
+++ b/llvm/test/DebugInfo/X86/llparser-cleanup-retained-nodes.ll
@@ -0,0 +1,37 @@
+; RUN: llvm-link %s %s -S -o %t
+; RUN: opt --passes=verify %t -o /dev/null
+; RUN: FileCheck --input-file=%t %s --implicit-check-not=DICompositeType
+
+; During module loading, if a local type appears in retainedNodes
+; field of multiple DISubprograms due to ODR-uniquing,
+; LLParser should clean up retainedNodes, so that only one DISubprogram
+; will have this type in its retainedNodes.
+
+; CHECK: [[BAR1:![0-9]+]] = distinct !DISubprogram(name: "bar", {{.*}}, retainedNodes: [[RN_BAR1:![0-9]+]])
+; CHECK: [[EMPTY:![0-9]+]] = !{}
+; CHECK: [[RN_BAR1]] = !{[[T1:![0-9]+]]}
+; CHECK: [[T1]] = distinct !DICompositeType(tag: DW_TAG_class_type, scope: [[BAR1]], {{.*}}, identifier: "local_type")
+; CHECK: {{![0-9]+}} = distinct !DISubprogram(name: "bar", {{.*}}, retainedNodes: [[EMPTY:![0-9]+]])
+
+target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-unknown-linux-gnu"
+
+ at llvm.used = appending global [1 x ptr] [ptr @bar]
+
+define internal void @bar(ptr %this) !dbg !5 {
+  ret void
+}
+
+!llvm.module.flags = !{!0, !1, !2}
+!llvm.dbg.cu = !{!3}
+
+!0 = !{i32 1, !"wchar_size", i32 4}
+!1 = !{i32 7, !"Dwarf Version", i32 2}
+!2 = !{i32 2, !"Debug Info Version", i32 3}
+!3 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !4, producer: "clang version 14.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None)
+!4 = !DIFile(filename: "tmp.cpp", directory: "/tmp/")
+!5 = distinct !DISubprogram(name: "bar", scope: !4, file: !4, line: 68, type: !6, scopeLine: 68, flags: DIFlagPrototyped, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition | DISPFlagOptimized, unit: !3, retainedNodes: !9)
+!6 = !DISubroutineType(types: !8)
+!7 = !DICompositeType(tag: DW_TAG_class_type, scope: !5, file: !4, line: 210, size: 8, flags: DIFlagTypePassByValue, elements: !8, identifier: "local_type")
+!8 = !{}
+!9 = !{!7}



More information about the llvm-commits mailing list