[clang] [llvm] [CloneFunction][DebugInfo] Avoid cloning DILocalVariables of inlined functions (PR #75385)

Vladislav Dzhidzhoev via cfe-commits cfe-commits at lists.llvm.org
Thu Jan 11 08:07:38 PST 2024


https://github.com/dzhidzhoev updated https://github.com/llvm/llvm-project/pull/75385

>From 2953d4adcb36c28d12a3e3f8febf0995ef22c0d9 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] [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.

Additionally, if DISubprogram is not cloned in CloneFunctionInto(),
cloning of its DILocalVariables is avoided.
Otherwise we get duplicated DILocalVariables not tracked in their
subprogram's retainedNodes, that crash LTO with Chromium.

Reviewed By: jmmartinez
Authored-by: Kristina Bessonova <kbessonova at accesssoftek.com>
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              |  11 +-
 llvm/lib/Bitcode/Reader/MetadataLoader.cpp    | 111 +++--
 .../CodeGen/AsmPrinter/DwarfCompileUnit.cpp   |  60 ++-
 .../lib/CodeGen/AsmPrinter/DwarfCompileUnit.h |  16 +-
 llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp    |  13 +-
 llvm/lib/IR/DIBuilder.cpp                     |  39 +-
 llvm/lib/IR/DebugInfo.cpp                     |  14 +-
 llvm/lib/IR/Verifier.cpp                      |   6 +-
 llvm/lib/Transforms/Utils/CloneFunction.cpp   |  15 +-
 llvm/test/Bitcode/clone-local-types.ll        |  46 ++
 llvm/test/Bitcode/upgrade-cu-locals.ll        |  68 +--
 llvm/test/Bitcode/upgrade-cu-locals.ll.bc     | Bin 2688 -> 2780 bytes
 .../DebugInfo/Generic/inlined-local-type.ll   | 128 ++++++
 .../Generic/lexical-block-retained-types.ll   |  55 +++
 .../DebugInfo/Generic/lexical-block-types.ll  | 425 ++++++++++++++++++
 .../Generic/verifier-invalid-disubprogram.ll  |   2 +-
 .../X86/local-type-as-template-parameter.ll   | 161 +++++++
 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          | 105 +++++
 31 files changed, 1266 insertions(+), 199 deletions(-)
 create mode 100644 llvm/test/Bitcode/clone-local-types.ll
 create mode 100644 llvm/test/DebugInfo/Generic/inlined-local-type.ll
 create mode 100644 llvm/test/DebugInfo/Generic/lexical-block-retained-types.ll
 create mode 100644 llvm/test/DebugInfo/Generic/lexical-block-types.ll
 create mode 100644 llvm/test/DebugInfo/X86/local-type-as-template-parameter.ll
 rename llvm/test/DebugInfo/{Generic => X86}/split-dwarf-local-import.ll (98%)
 rename llvm/test/DebugInfo/{Generic => X86}/split-dwarf-local-import2.ll (98%)
 rename llvm/test/DebugInfo/{Generic => X86}/split-dwarf-local-import3.ll (100%)

diff --git a/clang/test/CodeGen/debug-info-codeview-unnamed.c b/clang/test/CodeGen/debug-info-codeview-unnamed.c
index bd2a7543e56b2b..16ffb3682236f1 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 3e9f7b07658e36..31d608d92a06b4 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 023cac159faa4b..5b01c6dbb39414 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 9f2c044843d0f0..7c0bf8ccb03842 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 61b3c7c0526c8e..c91cf83c0405fe 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 b4c79936ab33e6..9602ac1b024973 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 6b9c9a243decd1..122e4aa62ea7df 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 eecbac6520ac97..3d659e7bfd004a 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 edec161b397155..166cd664aec1de 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 36ef77f9505bc1..684bc7b026ff0b 100644
--- a/llvm/include/llvm/IR/DebugInfo.h
+++ b/llvm/include/llvm/IR/DebugInfo.h
@@ -104,7 +104,7 @@ class DebugInfoFinder {
   void processInstruction(const Module &M, const Instruction &I);
 
   /// Process a DILocalVariable.
-  void processVariable(const Module &M, const DILocalVariable *DVI);
+  void processVariable(DILocalVariable *DVI);
   /// Process debug info location.
   void processLocation(const Module &M, const DILocation *Loc);
   // Process a DPValue, much like a DbgVariableIntrinsic.
@@ -120,6 +120,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);
@@ -134,6 +135,8 @@ class DebugInfoFinder {
       SmallVectorImpl<DIGlobalVariableExpression *>::const_iterator;
   using type_iterator = SmallVectorImpl<DIType *>::const_iterator;
   using scope_iterator = SmallVectorImpl<DIScope *>::const_iterator;
+  using local_variable_iterator =
+      SmallVectorImpl<DILocalVariable *>::const_iterator;
 
   iterator_range<compile_unit_iterator> compile_units() const {
     return make_range(CUs.begin(), CUs.end());
@@ -147,6 +150,10 @@ class DebugInfoFinder {
     return make_range(GVs.begin(), GVs.end());
   }
 
+  iterator_range<local_variable_iterator> local_variables() const {
+    return make_range(LVs.begin(), LVs.end());
+  }
+
   iterator_range<type_iterator> types() const {
     return make_range(TYs.begin(), TYs.end());
   }
@@ -157,6 +164,7 @@ class DebugInfoFinder {
 
   unsigned compile_unit_count() const { return CUs.size(); }
   unsigned global_variable_count() const { return GVs.size(); }
+  unsigned local_variable_count() const { return LVs.size(); }
   unsigned subprogram_count() const { return SPs.size(); }
   unsigned type_count() const { return TYs.size(); }
   unsigned scope_count() const { return Scopes.size(); }
@@ -165,6 +173,7 @@ class DebugInfoFinder {
   SmallVector<DICompileUnit *, 8> CUs;
   SmallVector<DISubprogram *, 8> SPs;
   SmallVector<DIGlobalVariableExpression *, 8> GVs;
+  SmallVector<DILocalVariable *, 8> LVs;
   SmallVector<DIType *, 8> TYs;
   SmallVector<DIScope *, 8> Scopes;
   SmallPtrSet<const MDNode *, 32> NodesSeen;
diff --git a/llvm/lib/Bitcode/Reader/MetadataLoader.cpp b/llvm/lib/Bitcode/Reader/MetadataLoader.cpp
index 910e97489dbbe0..d118695b431c5a 100644
--- a/llvm/lib/Bitcode/Reader/MetadataLoader.cpp
+++ b/llvm/lib/Bitcode/Reader/MetadataLoader.cpp
@@ -547,6 +547,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) {
@@ -554,48 +556,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));
         }
       }
     }
@@ -711,6 +731,29 @@ class MetadataLoader::MetadataLoaderImpl {
       upgradeCULocals();
   }
 
+  void cloneLocalTypes() {
+    for (unsigned I = 0; I < MetadataList.size(); ++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:
@@ -1086,6 +1129,7 @@ Error MetadataLoader::MetadataLoaderImpl::parseMetadata(bool ModuleLevel) {
       // placeholders, that we flush here.
       resolveForwardRefsAndPlaceholders(Placeholders);
       upgradeDebugInfo(ModuleLevel);
+      cloneLocalTypes();
       // Return at the beginning of the block, since it is easy to skip it
       // entirely from there.
       Stream.ReadBlockEnd(); // Pop the abbrev block context.
@@ -1117,6 +1161,7 @@ Error MetadataLoader::MetadataLoaderImpl::parseMetadata(bool ModuleLevel) {
     case BitstreamEntry::EndBlock:
       resolveForwardRefsAndPlaceholders(Placeholders);
       upgradeDebugInfo(ModuleLevel);
+      cloneLocalTypes();
       return Error::success();
     case BitstreamEntry::Record:
       // The interesting case.
diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp
index 14f2a363f9be61..f40d0a13a44002 100644
--- a/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp
@@ -594,10 +594,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);
 }
 
@@ -718,24 +717,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());
@@ -1698,15 +1712,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) {
@@ -1714,7 +1734,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 dc772bb459c956..adfddf5062fa18 100644
--- a/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.h
+++ b/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.h
@@ -227,14 +227,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);
@@ -253,6 +248,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 41afbea4561433..2d00b9f3ccd6e2 100644
--- a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
@@ -1214,12 +1214,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);
@@ -1417,9 +1418,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!");
     }
@@ -1517,6 +1522,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 62efaba025344b..4125ab6a5d8b5b 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())
-    CUNode->replaceEnumTypes(MDTuple::get(
-        VMContext, SmallVector<Metadata *, 16>(AllEnumTypes.begin(),
-                                               AllEnumTypes.end())));
+  if (!EnumTypes.empty())
+    CUNode->replaceEnumTypes(
+        MDTuple::get(VMContext, SmallVector<Metadata *, 16>(EnumTypes.begin(),
+                                                            EnumTypes.end())));
 
   SmallVector<Metadata *, 16> RetainValues;
   // Declarations and definitions of the same type may be retained. Some
@@ -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, RunTimeLang, 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;
 }
 
@@ -546,7 +555,10 @@ DIBuilder::createEnumerationType(DIScope *Scope, StringRef Name, DIFile *File,
       getNonCompileUnitScope(Scope), UnderlyingType, SizeInBits, AlignInBits, 0,
       IsScoped ? DINode::FlagEnumClass : DINode::FlagZero, Elements,
       RunTimeLang, 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;
 }
@@ -627,7 +639,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; }
@@ -644,6 +657,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;
 }
 
@@ -660,6 +675,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 c6dc42e8ac88c6..316197aba727fd 100644
--- a/llvm/lib/IR/DebugInfo.cpp
+++ b/llvm/lib/IR/DebugInfo.cpp
@@ -193,7 +193,7 @@ void DebugInfoFinder::processCompileUnit(DICompileUnit *CU) {
 void DebugInfoFinder::processInstruction(const Module &M,
                                          const Instruction &I) {
   if (auto *DVI = dyn_cast<DbgVariableIntrinsic>(&I))
-    processVariable(M, DVI->getVariable());
+    processVariable(DVI->getVariable());
 
   if (auto DbgLoc = I.getDebugLoc())
     processLocation(M, DbgLoc.get());
@@ -210,7 +210,7 @@ void DebugInfoFinder::processLocation(const Module &M, const DILocation *Loc) {
 }
 
 void DebugInfoFinder::processDPValue(const Module &M, const DPValue &DPV) {
-  processVariable(M, DPV.getVariable());
+  processVariable(DPV.getVariable());
   processLocation(M, DPV.getDebugLoc().get());
 }
 
@@ -285,12 +285,18 @@ void DebugInfoFinder::processSubprogram(DISubprogram *SP) {
       processType(TVal->getType());
     }
   }
+
+  for (auto *N : SP->getRetainedNodes()) {
+    if (auto *Var = dyn_cast_or_null<DILocalVariable>(N)) {
+      processVariable(Var);
+    }
+  }
 }
 
-void DebugInfoFinder::processVariable(const Module &M,
-                                      const DILocalVariable *DV) {
+void DebugInfoFinder::processVariable(DILocalVariable *DV) {
   if (!NodesSeen.insert(DV).second)
     return;
+  LVs.push_back(DV);
   processScope(DV->getScope());
   processType(DV->getType());
 }
diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp
index b6ad85b2d46e12..49241160456cb5 100644
--- a/llvm/lib/IR/Verifier.cpp
+++ b/llvm/lib/IR/Verifier.cpp
@@ -1411,9 +1411,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 c0f333364fa587..2547ac00d88e35 100644
--- a/llvm/lib/Transforms/Utils/CloneFunction.cpp
+++ b/llvm/lib/Transforms/Utils/CloneFunction.cpp
@@ -232,12 +232,20 @@ void llvm::CloneFunctionInto(Function *NewFunc, const Function *OldFunc,
     // Avoid cloning types, compile units, and (other) subprograms.
     SmallPtrSet<const DISubprogram *, 16> MappedToSelfSPs;
     for (DISubprogram *ISP : DIFinder->subprograms()) {
+      // Don't clone inlined subprograms.
       if (ISP != SPClonedWithinModule) {
         mapToSelfIfNew(ISP);
         MappedToSelfSPs.insert(ISP);
       }
     }
 
+    // Avoid cloning local variables of subprograms that won't be cloned.
+    for (DILocalVariable *DV : DIFinder->local_variables())
+      if (auto *S = dyn_cast_or_null<DILocalScope>(DV->getScope()))
+        if (DISubprogram *SP = S->getSubprogram())
+          if (MappedToSelfSPs.contains(SP))
+            mapToSelfIfNew(DV);
+
     // If a subprogram isn't going to be cloned skip its lexical blocks as well.
     for (DIScope *S : DIFinder->scopes()) {
       auto *LScope = dyn_cast<DILocalScope>(S);
@@ -249,7 +257,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/clone-local-types.ll b/llvm/test/Bitcode/clone-local-types.ll
new file mode 100644
index 00000000000000..d2607303ec610a
--- /dev/null
+++ b/llvm/test/Bitcode/clone-local-types.ll
@@ -0,0 +1,46 @@
+; RUN: llvm-as < %s -o %t0
+; RUN: llvm-dis %t0 -o - | FileCheck %s
+
+; Ensure that function-local types with the same ODR identifier belonging
+; to different subprograms are not deduplicated when a module is being loaded.
+
+; CHECK: [[CU:![0-9]+]] = distinct !DICompileUnit
+; CHECK: [[BAR:![0-9]+]] = distinct !DISubprogram(name: "bar", {{.*}}, retainedNodes: [[RN_BAR:![0-9]+]])
+; CHECK: [[RN_BAR]] = !{[[T2:![0-9]+]]}
+; CHECK: [[T2]] = !DICompositeType(tag: DW_TAG_class_type, {{.*}}, identifier: "local_type")
+; CHECK: [[FOO:![0-9]+]] = distinct !DISubprogram(name: "foo", {{.*}}, retainedNodes: [[RN_FOO:![0-9]+]])
+; CHECK: [[RN_FOO]] = !{[[T1:![0-9]+]]}
+; CHECK: [[T1]] = !DICompositeType(tag: DW_TAG_class_type, {{.*}}, identifier: "local_type")
+
+target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-unknown-linux-gnu"
+
+declare void @foo()
+
+define void @bar(ptr %this) !dbg !10 {
+  call void @foo(), !dbg !17
+  ret void, !dbg !13
+}
+
+!llvm.module.flags = !{!0, !1, !2, !3, !4, !5, !6, !7}
+!llvm.dbg.cu = !{!8}
+
+!0 = !{i32 2, !"SDK Version", [2 x i32] [i32 11, i32 4]}
+!1 = !{i32 7, !"Dwarf Version", i32 2}
+!2 = !{i32 2, !"Debug Info Version", i32 3}
+!3 = !{i32 1, !"wchar_size", i32 4}
+!4 = !{i32 7, !"openmp", i32 50}
+!5 = !{i32 7, !"openmp-device", i32 50}
+!6 = !{i32 7, !"PIC Level", i32 2}
+!7 = !{i32 7, !"frame-pointer", i32 2}
+!8 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !9, producer: "clang version 14.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None)
+!9 = !DIFile(filename: "tmp.cpp", directory: "/tmp/")
+!10 = distinct !DISubprogram(name: "bar", scope: !9, file: !9, line: 68, type: !12, scopeLine: 68, flags: DIFlagPrototyped, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition | DISPFlagOptimized, unit: !8, retainedNodes: !16)
+!11 = distinct !DISubprogram(name: "foo", scope: !9, file: !9, line: 68, type: !12, scopeLine: 68, flags: DIFlagPrototyped, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition | DISPFlagOptimized, unit: !8, retainedNodes: !16)
+!12 = !DISubroutineType(types: !15)
+!13 = !DILocation(line: 69, column: 18, scope: !10)
+!14 = !DICompositeType(tag: DW_TAG_class_type, scope: !11, file: !9, line: 210, size: 8, flags: DIFlagTypePassByValue, elements: !15, identifier: "local_type")
+!15 = !{}
+!16 = !{!14}
+!17 = !DILocation(line: 69, column: 18, scope: !11, inlinedAt: !18)
+!18 = !DILocation(line: 4, column: 1, scope: !10)
diff --git a/llvm/test/Bitcode/upgrade-cu-locals.ll b/llvm/test/Bitcode/upgrade-cu-locals.ll
index 9a590f0fc0774a..7052ab379bea47 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 00000000000000..a6f7d294de9284
--- /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 00000000000000..64de1323c98c6b
--- /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 00000000000000..aef59984351a70
--- /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 6d4d0e93d38f9d..54ce1c56c6b307 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 00000000000000..586574d3e1aaf5
--- /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 292c7c6e4a5773..7dbec4520ed6ce 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 1a41f3f348ca04..676ef23b679e83 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 0057f675f9b316..71f80992d1b04a 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 025771f07ce5d4..1f04663f1cfa74 100644
--- a/llvm/unittests/Transforms/Utils/CloningTest.cpp
+++ b/llvm/unittests/Transforms/Utils/CloningTest.cpp
@@ -801,6 +801,111 @@ TEST(CloneFunction, CloneFunctionWithSubprograms) {
   EXPECT_FALSE(verifyModule(*ImplModule, &errs()));
 }
 
+TEST(CloneFunction, CloneFunctionWithRetainedNodes) {
+  StringRef ImplAssembly = R"(
+    declare void @llvm.dbg.declare(metadata, metadata, metadata)
+
+    define void @test() !dbg !3 {
+      call void @llvm.dbg.declare(metadata i8* undef, metadata !5, metadata !DIExpression()), !dbg !7
+      call void @llvm.dbg.declare(metadata i8* undef, metadata !25, metadata !DIExpression()), !dbg !7
+      ret void
+    }
+
+    declare void @cloned()
+
+    !llvm.dbg.cu = !{!0}
+    !llvm.module.flags = !{!2}
+    !0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, enums: !{!14})
+    !1 = !DIFile(filename: "test.cpp",  directory: "")
+    !2 = !{i32 1, !"Debug Info Version", i32 3}
+    !3 = distinct !DISubprogram(name: "test", scope: !1, unit: !0, retainedNodes: !9)
+    !4 = distinct !DISubprogram(name: "inlined", scope: !1, unit: !0, retainedNodes: !{!5})
+    !5 = !DILocalVariable(name: "awaitables", scope: !4, type: !23)
+    !6 = distinct !DILexicalBlock(scope: !4, file: !1, line: 1)
+    !7 = !DILocation(line: 1, scope: !6, inlinedAt: !8)
+    !8 = !DILocation(line: 10, scope: !3)
+    !9 = !{!15, !17, !18, !23}
+    !14 = distinct !DICompositeType(tag: DW_TAG_enumeration_type, scope: !0, file: !1, line: 13, size: 200, elements: !{})
+    !15 = !DILocalVariable(name: "a", scope: !3)
+    !16 = distinct !DICompositeType(tag: DW_TAG_enumeration_type, scope: !3, file: !1, line: 13, size: 208, elements: !{})
+    !17 = !DIImportedEntity(tag: DW_TAG_imported_declaration, name: "imported_l", file: !1, line: 14, scope: !3, entity: !16)
+    !18 = !DILabel(scope: !3, name: "l", file: !1, line: 22)
+    !22 = !DIBasicType(name: "real", size: 32, align: 32, encoding: DW_ATE_float)
+    !23 = !DIDerivedType(name: "local_float", tag: DW_TAG_const_type, baseType: !22, scope: !3)
+    !float_type = !{!23}
+    !25 = !DILocalVariable(name: "inlined2", scope: !4, type: !23)
+    !inlined2 = !{!25}
+  )";
+
+  LLVMContext Context;
+  SMDiagnostic Error;
+
+  auto ImplModule = parseAssemblyString(ImplAssembly, Error, Context);
+  EXPECT_TRUE(ImplModule != nullptr);
+  auto *Func = ImplModule->getFunction("test");
+  EXPECT_TRUE(Func != nullptr);
+  auto *ClonedFunc = ImplModule->getFunction("cloned");
+  EXPECT_TRUE(ClonedFunc != nullptr);
+
+  EXPECT_FALSE(verifyModule(*ImplModule, &errs()));
+
+  ValueToValueMapTy VMap;
+  SmallVector<ReturnInst *, 8> Returns;
+  ClonedCodeInfo CCI;
+  CloneFunctionInto(ClonedFunc, Func, VMap,
+                    CloneFunctionChangeType::GlobalChanges, Returns, "", &CCI);
+
+  EXPECT_FALSE(verifyModule(*ImplModule, &errs()));
+
+  // Check that retained and local types are copied.
+  DISubprogram *FuncSP = Func->getSubprogram();
+  DISubprogram *ClonedSP = ClonedFunc->getSubprogram();
+  EXPECT_NE(FuncSP, nullptr);
+  EXPECT_NE(ClonedSP, nullptr);
+  EXPECT_EQ(FuncSP->getRetainedNodes().size(), 4u);
+  EXPECT_EQ(FuncSP->getRetainedNodes().size(),
+            ClonedSP->getRetainedNodes().size());
+  for (unsigned I = 0; I < FuncSP->getRetainedNodes().size(); ++I) {
+    auto *Node = FuncSP->getRetainedNodes()[I];
+    auto *Copy = ClonedSP->getRetainedNodes()[I];
+
+    // Check that the order of retainedNodes is preserved by
+    // checking that the corresponding node has the same name.
+    if (auto *Var = dyn_cast<DILocalVariable>(Node)) {
+      auto *VarCopy = dyn_cast<DILocalVariable>(Copy);
+      EXPECT_NE(VarCopy, nullptr);
+      EXPECT_EQ(Var->getName(), VarCopy->getName());
+    } else if (auto *Label = dyn_cast<DILabel>(Node)) {
+      auto *LabelCopy = dyn_cast<DILabel>(Copy);
+      EXPECT_NE(LabelCopy, nullptr);
+      EXPECT_EQ(Label->getName(), LabelCopy->getName());
+    } else if (auto *IE = dyn_cast<DIImportedEntity>(Node)) {
+      auto *IECopy = dyn_cast<DIImportedEntity>(Copy);
+      EXPECT_NE(IECopy, nullptr);
+      EXPECT_EQ(IE->getName(), IECopy->getName());
+    } else if (auto *Ty = dyn_cast<DIType>(Node)) {
+      auto *TyCopy = dyn_cast<DIType>(Copy);
+      EXPECT_NE(TyCopy, nullptr);
+      EXPECT_EQ(Ty->getName(), TyCopy->getName());
+    }
+
+    // Check that node was copied
+    EXPECT_NE(Node, Copy);
+  }
+
+  auto *FloatType = dyn_cast<DIType>(
+      ImplModule->getNamedMetadata("float_type")->getOperand(0));
+  EXPECT_EQ(FloatType->getName(), "local_float");
+  EXPECT_TRUE(VMap.MD().contains(FloatType));
+  EXPECT_NE(FloatType, VMap.MD()[FloatType]);
+
+  auto *Inlined2 = dyn_cast<DILocalVariable>(
+      ImplModule->getNamedMetadata("inlined2")->getOperand(0));
+  EXPECT_EQ(Inlined2->getName(), "inlined2");
+  EXPECT_TRUE(VMap.MD().contains(Inlined2));
+  EXPECT_EQ(Inlined2, VMap.MD()[Inlined2]);
+}
+
 TEST(CloneFunction, CloneFunctionWithInlinedSubprograms) {
   StringRef ImplAssembly = R"(
     declare void @llvm.dbg.declare(metadata, metadata, metadata)



More information about the cfe-commits mailing list