[clang] [DebugMetadata][DwarfDebug] Clone uniqued function-local types after metadata loading. (PR #68986)

Vladislav Dzhidzhoev via cfe-commits cfe-commits at lists.llvm.org
Fri Oct 13 06:02:12 PDT 2023


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

- [DebugMetadata][DwarfDebug] Support function-local types in lexical block scopes (4/7)
- Clone function-local types after metadata loading.

This is a follow-up for https://reviews.llvm.org/D144006, fixing a crash reported in Chromium (https://reviews.llvm.org/D144006#4651955).

The very first commit is added for convenience, as it has already been accepted.
The issue with it is that when DIType nodes are loaded, they get deduplicated as ODR-uniqued. When two modules get merged by LTO, DISubprogram nodes for the same function from different modules are not uniqued, since they are marked as distinct, whereas their retained type are, which can violate the ownership relationship betweeen DISubprogram and its function-local retained type.
It is fixed by copying a function-local type referenced by one DISubprogram through retainedNodes and referencing another in its scope field. The scope of copy gets changed correspondingly.

This is meant to be committed along with https://reviews.llvm.org/D144006.


>From d61f109dcb92db45fa8f6ce7503409edcbfbce63 Mon Sep 17 00:00:00 2001
From: Vladislav Dzhidzhoev <vdzhidzhoev at accesssoftek.com>
Date: Tue, 18 Jul 2023 14:22:46 +0200
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

Differential Revision: https://reviews.llvm.org/D144006
---
 .../CodeGen/debug-info-codeview-unnamed.c     |  16 +-
 clang/test/CodeGen/debug-info-unused-types.c  |  16 +-
 .../test/CodeGen/debug-info-unused-types.cpp  |  14 +-
 clang/test/CodeGenCXX/debug-info-access.cpp   |   2 +-
 .../CodeGenCXX/debug-info-anon-union-vars.cpp |  12 +-
 .../debug-info-codeview-unnamed.cpp           | 110 +++--
 .../debug-info-gline-tables-only-codeview.cpp |   4 +-
 clang/test/CodeGenCXX/debug-lambda-this.cpp   |   4 +-
 llvm/include/llvm/IR/DIBuilder.h              |   6 +-
 llvm/include/llvm/IR/DebugInfo.h              |   1 +
 llvm/lib/Bitcode/Reader/MetadataLoader.cpp    |  86 ++--
 .../CodeGen/AsmPrinter/DwarfCompileUnit.cpp   |  60 ++-
 .../lib/CodeGen/AsmPrinter/DwarfCompileUnit.h |  16 +-
 llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp    |  13 +-
 llvm/lib/IR/DIBuilder.cpp                     |  37 +-
 llvm/lib/IR/DebugInfo.cpp                     |  11 +-
 llvm/lib/IR/Verifier.cpp                      |   6 +-
 llvm/lib/Transforms/Utils/CloneFunction.cpp   |   7 +-
 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 +++++++
 llvm/test/DebugInfo/X86/set.ll                |   4 +-
 .../split-dwarf-local-import.ll               |   1 -
 .../split-dwarf-local-import2.ll              |   1 -
 .../split-dwarf-local-import3.ll              |   0
 .../Transforms/Utils/CloningTest.cpp          |  93 ++++
 30 files changed, 1165 insertions(+), 194 deletions(-)
 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/CodeGen/debug-info-codeview-unnamed.c b/clang/test/CodeGen/debug-info-codeview-unnamed.c
index bd2a7543e56b2ba..16ffb3682236f18 100644
--- a/clang/test/CodeGen/debug-info-codeview-unnamed.c
+++ b/clang/test/CodeGen/debug-info-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/CodeGen/debug-info-unused-types.c b/clang/test/CodeGen/debug-info-unused-types.c
index 3e9f7b07658e36e..31d608d92a06b41 100644
--- a/clang/test/CodeGen/debug-info-unused-types.c
+++ b/clang/test/CodeGen/debug-info-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/CodeGen/debug-info-unused-types.cpp b/clang/test/CodeGen/debug-info-unused-types.cpp
index 023cac159faa4b4..5b01c6dbb394149 100644
--- a/clang/test/CodeGen/debug-info-unused-types.cpp
+++ b/clang/test/CodeGen/debug-info-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/clang/test/CodeGenCXX/debug-info-access.cpp b/clang/test/CodeGenCXX/debug-info-access.cpp
index 9f2c044843d0f0f..7c0bf8ccb038428 100644
--- a/clang/test/CodeGenCXX/debug-info-access.cpp
+++ b/clang/test/CodeGenCXX/debug-info-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/CodeGenCXX/debug-info-anon-union-vars.cpp b/clang/test/CodeGenCXX/debug-info-anon-union-vars.cpp
index 61b3c7c0526c8ef..c91cf83c0405fec 100644
--- a/clang/test/CodeGenCXX/debug-info-anon-union-vars.cpp
+++ b/clang/test/CodeGenCXX/debug-info-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/CodeGenCXX/debug-info-codeview-unnamed.cpp b/clang/test/CodeGenCXX/debug-info-codeview-unnamed.cpp
index b4c79936ab33e64..9602ac1b0249730 100644
--- a/clang/test/CodeGenCXX/debug-info-codeview-unnamed.cpp
+++ b/clang/test/CodeGenCXX/debug-info-codeview-unnamed.cpp
@@ -3,6 +3,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.
@@ -10,21 +64,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:      )
 
 
@@ -36,21 +80,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:      )
 
 
@@ -61,21 +95,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:      )
 
 
@@ -87,21 +111,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/CodeGenCXX/debug-info-gline-tables-only-codeview.cpp b/clang/test/CodeGenCXX/debug-info-gline-tables-only-codeview.cpp
index 6b9c9a243decd1e..122e4aa62ea7dfc 100644
--- a/clang/test/CodeGenCXX/debug-info-gline-tables-only-codeview.cpp
+++ b/clang/test/CodeGenCXX/debug-info-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/CodeGenCXX/debug-lambda-this.cpp b/clang/test/CodeGenCXX/debug-lambda-this.cpp
index eecbac6520ac97d..3d659e7bfd004a1 100644
--- a/clang/test/CodeGenCXX/debug-lambda-this.cpp
+++ b/clang/test/CodeGenCXX/debug-lambda-this.cpp
@@ -13,10 +13,10 @@ int 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/llvm/include/llvm/IR/DIBuilder.h b/llvm/include/llvm/IR/DIBuilder.h
index ecd6dd7b0a4f822..0aa180aec6d8a0f 100644
--- a/llvm/include/llvm/IR/DIBuilder.h
+++ b/llvm/include/llvm/IR/DIBuilder.h
@@ -49,7 +49,7 @@ namespace llvm {
     Function *LabelFn;       ///< llvm.dbg.label
     Function *AssignFn;      ///< llvm.dbg.assign
 
-    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/include/llvm/IR/DebugInfo.h b/llvm/include/llvm/IR/DebugInfo.h
index 92beebed8ad51df..2c1fc6fb965fffa 100644
--- a/llvm/include/llvm/IR/DebugInfo.h
+++ b/llvm/include/llvm/IR/DebugInfo.h
@@ -114,6 +114,7 @@ class DebugInfoFinder {
   void processCompileUnit(DICompileUnit *CU);
   void processScope(DIScope *Scope);
   void processType(DIType *DT);
+  void processLocalVariable(DILocalVariable *DV);
   bool addCompileUnit(DICompileUnit *CU);
   bool addGlobalVariable(DIGlobalVariableExpression *DIG);
   bool addScope(DIScope *Scope);
diff --git a/llvm/lib/Bitcode/Reader/MetadataLoader.cpp b/llvm/lib/Bitcode/Reader/MetadataLoader.cpp
index 1e9ed5fcaa581b3..c87322ab634d498 100644
--- a/llvm/lib/Bitcode/Reader/MetadataLoader.cpp
+++ b/llvm/lib/Bitcode/Reader/MetadataLoader.cpp
@@ -548,6 +548,8 @@ class MetadataLoader::MetadataLoaderImpl {
 
   /// Move local imports from DICompileUnit's 'imports' field to
   /// DISubprogram's retainedNodes.
+  /// Move fucntion-local enums from DICompileUnit's enums
+  /// to DISubprogram's retainedNodes.
   void upgradeCULocals() {
     if (NamedMDNode *CUNodes = TheModule.getNamedMetadata("llvm.dbg.cu")) {
       for (unsigned I = 0, E = CUNodes->getNumOperands(); I != E; ++I) {
@@ -555,48 +557,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 (dyn_cast_or_null<DILocalScope>(IE->getScope())) {
-              EntitiesToRemove.insert(IE);
-            }
+            if (dyn_cast_or_null<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 (dyn_cast_or_null<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));
         }
       }
     }
diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp
index 42bf755737506b4..b14d1dd58367bf2 100644
--- a/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp
@@ -592,10 +592,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);
 }
 
@@ -715,24 +714,39 @@ 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;
+  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;
   }
 
   attachRangesOrLowHighPC(*ScopeDIE, Scope->getRanges());
@@ -1686,15 +1700,21 @@ 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 && getAbstractScopeDIEs().count(LB))
     return getAbstractScopeDIEs()[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) {
@@ -1702,7 +1722,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 453dcc1c90bbf49..5b68f60ad2fa377 100644
--- a/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.h
+++ b/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.h
@@ -234,14 +234,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);
@@ -260,6 +255,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);
+
   /// Construct a DIE for this subprogram scope.
   DIE &constructSubprogramScopeDIE(const DISubprogram *Sub,
                                    LexicalScope *Scope);
diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
index ee2ab71ad28e47f..4aa605f18192513 100644
--- a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
@@ -1211,12 +1211,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);
@@ -1410,9 +1411,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!");
     }
@@ -1510,6 +1515,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 1ce8c17f8a880f6..d3e618b638fbe2f 100644
--- a/llvm/lib/IR/DIBuilder.cpp
+++ b/llvm/lib/IR/DIBuilder.cpp
@@ -31,7 +31,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())
@@ -68,10 +68,10 @@ void DIBuilder::finalize() {
     return;
   }
 
-  if (!AllEnumTypes.empty())
+  if (!EnumTypes.empty())
     CUNode->replaceEnumTypes(MDTuple::get(
-        VMContext, SmallVector<Metadata *, 16>(AllEnumTypes.begin(),
-                                               AllEnumTypes.end())));
+        VMContext, SmallVector<Metadata *, 16>(EnumTypes.begin(),
+                                               EnumTypes.end())));
 
   SmallVector<Metadata *, 16> RetainValues;
   // Declarations and definitions of the same type may be retained. Some
@@ -336,10 +336,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, 0,
-                            AlignInBits, 0, std::nullopt, Flags, nullptr,
-                            Annotations);
+  auto *T =
+      DIDerivedType::get(VMContext, dwarf::DW_TAG_typedef, Name, File, LineNo,
+                         getNonCompileUnitScope(Context), Ty, 0, AlignInBits, 0,
+                         std::nullopt, Flags, nullptr, Annotations);
+  if (isa_and_nonnull<DILocalScope>(Context))
+    getSubprogramNodesTrackingVector(Context).emplace_back(T);
+  return T;
 }
 
 DIDerivedType *DIBuilder::createFriend(DIType *Ty, DIType *FriendTy) {
@@ -487,6 +490,8 @@ DICompositeType *DIBuilder::createClassType(
       OffsetInBits, Flags, Elements, 0, VTableHolder,
       cast_or_null<MDTuple>(TemplateParams), UniqueIdentifier);
   trackIfUnresolved(R);
+  if (isa_and_nonnull<DILocalScope>(Context))
+    getSubprogramNodesTrackingVector(Context).emplace_back(R);
   return R;
 }
 
@@ -500,6 +505,8 @@ DICompositeType *DIBuilder::createStructType(
       getNonCompileUnitScope(Context), DerivedFrom, SizeInBits, AlignInBits, 0,
       Flags, Elements, RunTimeLang, VTableHolder, nullptr, UniqueIdentifier);
   trackIfUnresolved(R);
+  if (isa_and_nonnull<DILocalScope>(Context))
+    getSubprogramNodesTrackingVector(Context).emplace_back(R);
   return R;
 }
 
@@ -512,6 +519,8 @@ DICompositeType *DIBuilder::createUnionType(
       getNonCompileUnitScope(Scope), nullptr, SizeInBits, AlignInBits, 0, Flags,
       Elements, RunTimeLang, nullptr, nullptr, UniqueIdentifier);
   trackIfUnresolved(R);
+  if (isa_and_nonnull<DILocalScope>(Scope))
+    getSubprogramNodesTrackingVector(Scope).emplace_back(R);
   return R;
 }
 
@@ -544,7 +553,10 @@ DICompositeType *DIBuilder::createEnumerationType(
       getNonCompileUnitScope(Scope), UnderlyingType, SizeInBits, AlignInBits, 0,
       IsScoped ? DINode::FlagEnumClass : DINode::FlagZero, Elements, 0, 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;
 }
@@ -625,7 +637,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; }
@@ -642,6 +655,8 @@ DIBuilder::createForwardDecl(unsigned Tag, StringRef Name, DIScope *Scope,
       SizeInBits, AlignInBits, 0, DINode::FlagFwdDecl, nullptr, RuntimeLang,
       nullptr, nullptr, UniqueIdentifier);
   trackIfUnresolved(RetTy);
+  if (isa_and_nonnull<DILocalScope>(Scope))
+    getSubprogramNodesTrackingVector(Scope).emplace_back(RetTy);
   return RetTy;
 }
 
@@ -658,6 +673,8 @@ DICompositeType *DIBuilder::createReplaceableCompositeType(
           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 48b5501c55ba47d..f95388cd906840a 100644
--- a/llvm/lib/IR/DebugInfo.cpp
+++ b/llvm/lib/IR/DebugInfo.cpp
@@ -267,6 +267,12 @@ void DebugInfoFinder::processSubprogram(DISubprogram *SP) {
       processType(TVal->getType());
     }
   }
+
+  for (auto *N : SP->getRetainedNodes()) {
+    if (auto *Var = dyn_cast<DILocalVariable>(N)) {
+      processLocalVariable(Var);
+    }
+  }
 }
 
 void DebugInfoFinder::processVariable(const Module &M,
@@ -275,7 +281,10 @@ void DebugInfoFinder::processVariable(const Module &M,
   if (!N)
     return;
 
-  auto *DV = dyn_cast<DILocalVariable>(N);
+  processLocalVariable(dyn_cast<DILocalVariable>(N));
+}
+
+void DebugInfoFinder::processLocalVariable(DILocalVariable *DV) {
   if (!DV)
     return;
 
diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp
index 188e4a4a658f330..1e9436f20d8243f 100644
--- a/llvm/lib/IR/Verifier.cpp
+++ b/llvm/lib/IR/Verifier.cpp
@@ -1404,9 +1404,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 d55208602b715f8..39ac48c99ca398d 100644
--- a/llvm/lib/Transforms/Utils/CloneFunction.cpp
+++ b/llvm/lib/Transforms/Utils/CloneFunction.cpp
@@ -244,7 +244,12 @@ void llvm::CloneFunctionInto(Function *NewFunc, const Function *OldFunc,
       mapToSelfIfNew(CU);
 
     for (DIType *Type : DIFinder->types())
-      mapToSelfIfNew(Type);
+      // Don't skip subprogram's local types.
+      if (!isa_and_present<DILocalScope>(Type->getScope()) ||
+          SPClonedWithinModule == nullptr ||
+          dyn_cast<DILocalScope>(Type->getScope())->getSubprogram() !=
+              SPClonedWithinModule)
+        mapToSelfIfNew(Type);
   } else {
     assert(!SPClonedWithinModule &&
            "Subprogram should be in DIFinder->subprogram_count()...");
diff --git a/llvm/test/Bitcode/upgrade-cu-locals.ll b/llvm/test/Bitcode/upgrade-cu-locals.ll
index 9a590f0fc0774a8..7052ab379bea473 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 000000000000000..a6f7d294de92845
--- /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 000000000000000..64de1323c98c6bb
--- /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 000000000000000..aef59984351a703
--- /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	{{.*}} "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	{{.*}} "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	{{.*}} "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 6d4d0e93d38f9d0..54ce1c56c6b307f 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 000000000000000..586574d3e1aaf57
--- /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/X86/set.ll b/llvm/test/DebugInfo/X86/set.ll
index 292c7c6e4a57730..7dbec4520ed6ce1 100644
--- a/llvm/test/DebugInfo/X86/set.ll
+++ b/llvm/test/DebugInfo/X86/set.ll
@@ -68,11 +68,11 @@ attributes #1 = { nofree nosync nounwind readnone speculatable willreturn }
 !llvm.module.flags = !{!18, !19, !20}
 
 !0 = !{!"versions- cm3: d5.10.0 llvm: 9.0"}
-!1 = distinct !DICompileUnit(language: DW_LANG_Modula3, file: !2, producer: "cm3", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !3)
+!1 = distinct !DICompileUnit(language: DW_LANG_Modula3, file: !2, producer: "cm3", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug)
 !2 = !DIFile(filename: "Main.m3", directory: "/home/cm3/settest/src")
 !3 = !{!4}
 !4 = !DICompositeType(tag: DW_TAG_enumeration_type, name: "Enum", scope: !5, file: !2, line: 11, size: 8, align: 8, elements: !9)
-!5 = distinct !DISubprogram(name: "Test", linkageName: "Main__Test", scope: !2, file: !2, line: 11, type: !6, scopeLine: 11, spFlags: DISPFlagDefinition, unit: !1, retainedNodes: !8)
+!5 = distinct !DISubprogram(name: "Test", linkageName: "Main__Test", scope: !2, file: !2, line: 11, type: !6, scopeLine: 11, spFlags: DISPFlagDefinition, unit: !1, retainedNodes: !3)
 !6 = !DISubroutineType(types: !7)
 !7 = !{null}
 !8 = !{}
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 1a41f3f348ca046..676ef23b679e830 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 0057f675f9b3169..71f80992d1b04a2 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 e083c75bf9c92d5..9f8225f09b910d7 100644
--- a/llvm/unittests/Transforms/Utils/CloningTest.cpp
+++ b/llvm/unittests/Transforms/Utils/CloningTest.cpp
@@ -801,6 +801,99 @@ 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
+      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}
+    !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}
+  )";
+
+  LLVMContext Context;
+  SMDiagnostic Error;
+
+  auto ImplModule = parseAssemblyString(ImplAssembly, Error, Context);
+  llvm::errs() << Error.getMessage() << Error.getLineNo() << "\n";
+  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(), 3u);
+  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());
+    }
+
+    // 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]);
+}
+
 TEST(CloneFunction, CloneFunctionWithInlinedSubprograms) {
   StringRef ImplAssembly = R"(
     declare void @llvm.dbg.declare(metadata, metadata, metadata)

>From 5012fac53513949a3e1987e0d6cc4695ac5dbd69 Mon Sep 17 00:00:00 2001
From: Vladislav Dzhidzhoev <vdzhidzhoev at accesssoftek.com>
Date: Thu, 5 Oct 2023 16:13:13 +0200
Subject: [PATCH 2/2] Clone function-local types after metadata loading.

---
 llvm/lib/Bitcode/Reader/MetadataLoader.cpp | 24 ++++++++++++
 llvm/test/Bitcode/clone-local-types.ll     | 43 ++++++++++++++++++++++
 2 files changed, 67 insertions(+)
 create mode 100644 llvm/test/Bitcode/clone-local-types.ll

diff --git a/llvm/lib/Bitcode/Reader/MetadataLoader.cpp b/llvm/lib/Bitcode/Reader/MetadataLoader.cpp
index c87322ab634d498..e4fcc20242e186d 100644
--- a/llvm/lib/Bitcode/Reader/MetadataLoader.cpp
+++ b/llvm/lib/Bitcode/Reader/MetadataLoader.cpp
@@ -731,6 +731,29 @@ class MetadataLoader::MetadataLoaderImpl {
     upgradeCULocals();
   }
 
+  void cloneLocalTypes() {
+    for (int I = MetadataList.size() - 1; I >= 0; --I) {
+      if (auto *SP = dyn_cast_or_null<DISubprogram>(MetadataList[I])) {
+        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:
@@ -1137,6 +1160,7 @@ Error MetadataLoader::MetadataLoaderImpl::parseMetadata(bool ModuleLevel) {
     case BitstreamEntry::EndBlock:
       resolveForwardRefsAndPlaceholders(Placeholders);
       upgradeDebugInfo();
+      cloneLocalTypes();
       return Error::success();
     case BitstreamEntry::Record:
       // The interesting case.
diff --git a/llvm/test/Bitcode/clone-local-types.ll b/llvm/test/Bitcode/clone-local-types.ll
new file mode 100644
index 000000000000000..6b0f21b91394de8
--- /dev/null
+++ b/llvm/test/Bitcode/clone-local-types.ll
@@ -0,0 +1,43 @@
+; RUN: llvm-as < %s -o %t0
+; RUN: llvm-dis %t0 -o - | FileCheck %s
+
+; 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)



More information about the cfe-commits mailing list