[clang] [llvm] [DebugInfo] Place local ODR-uniqued types in decl DISubprograms (PR #119001)
Jeremy Morse via cfe-commits
cfe-commits at lists.llvm.org
Fri Dec 6 09:21:30 PST 2024
https://github.com/jmorse created https://github.com/llvm/llvm-project/pull/119001
This is a reapplication and fix for the code in #75385 , support for function-local types in debug-info. The first commit is a reapplication of that code (plus some rebasing-maintenance), the second commit shifts a bit of debug-info metadata in the type hierarchy to make it safer when being unique'd. The root problem in #75385 seems to be that multiple copies of `DICompositeTypes` in LTO contexts, where multiple definitions of functions appear in debug-info, get unique'd and break various links between types and subprograms. Second commit message follows:
~
There are two flavours of DISubprogram: declarations, which are unique'd
and abstractly describe the function in question. There are also definition
DISubprograms which correspond to real instances, some of which are
inlined, duplicated across translation units in LTO, or otherwise can have
multiple instances.
Given that LLVM sometimes force-uniques types by their ODR-name, see the
enableDebugTypeODRUniquing feature, we shouldn't place types that might be
unique'd into duplicated contexts like definition DISubprograms. Instead,
place them into the declaration.
This slightly bends the existing approach where only functions that have a
separate declaratrion to their definition get a declaration-DISubprogram. A
single function in a translation unit might now get a declaration where it
didn't before, if it contains an ODR-unique'd type declaration. This seems
reasonable given that the LLVM idea of a declaration doesn't have to
exactly match source-language ideas.
The added cpp test checks that such ORD-unique'd types are detected and
placed in the declaration DISubprogram, creating one if necessary. The IR
test ensures that nothing changes after a round-trip with
enableDebugTypeODRUniquing turned on. Changes to the codeview
test are because the order of metadata changes a little.
>From ab44725ac4efa8c37bb418b0a53439dc1de4526d Mon Sep 17 00:00:00 2001
From: Jeremy Morse <jeremy.morse at sony.com>
Date: Fri, 6 Dec 2024 16:42:28 +0000
Subject: [PATCH 1/2] [DebugMetadata][DwarfDebug] Support function-local types
in lexical block scopes (4/7)
RFC https://discourse.llvm.org/t/rfc-dwarfdebug-fix-and-improve-handling-imported-entities-types-and-static-local-in-subprogram-and-lexical-block-scopes/68544
Similar to imported declarations, the patch tracks function-local types in
DISubprogram's 'retainedNodes' field. DwarfDebug is adjusted in accordance with
the aforementioned metadata change and provided a support of function-local
types scoped within a lexical block.
The patch assumes that DICompileUnit's 'enums field' no longer tracks local
types and DwarfDebug would assert if any locally-scoped types get placed there.
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
(cherry picked from commit 2953d4adcb36c28d12a3e3f8febf0995ef22c0d9)
Signed-off-by: Jeremy Morse <jeremy.morse at sony.com>
Conflicts:
llvm/lib/IR/DIBuilder.cpp
llvm/lib/IR/DebugInfo.cpp
llvm/lib/Transforms/Utils/CloneFunction.cpp
---
.../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 | 33 +-
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, 1263 insertions(+), 196 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 0df6e1a0419bb3..7b88f53a92e421 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 9fcb1c68d7efa7..2d6d8a56e97e31 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 cb1150c269a1da..8ec3f1636ac0f9 100644
--- a/llvm/include/llvm/IR/DIBuilder.h
+++ b/llvm/include/llvm/IR/DIBuilder.h
@@ -52,7 +52,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;
@@ -67,8 +67,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 73f45c3769be44..c83a5b2b3a5575 100644
--- a/llvm/include/llvm/IR/DebugInfo.h
+++ b/llvm/include/llvm/IR/DebugInfo.h
@@ -110,7 +110,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 DbgRecord (e.g, treat a DbgVariableRecord like a
@@ -127,6 +127,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);
@@ -141,6 +142,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());
@@ -154,6 +157,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());
}
@@ -164,6 +171,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(); }
@@ -172,6 +180,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 1caa8480efc518..5d20172b17e32e 100644
--- a/llvm/lib/Bitcode/Reader/MetadataLoader.cpp
+++ b/llvm/lib/Bitcode/Reader/MetadataLoader.cpp
@@ -546,6 +546,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 (MDNode *N : CUNodes->operands()) {
@@ -553,48 +555,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));
}
}
}
@@ -718,6 +738,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:
@@ -1093,6 +1136,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.
@@ -1124,6 +1168,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 2f96366b78e97d..9dfa833bb4dd79 100644
--- a/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp
@@ -622,10 +622,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);
}
@@ -746,24 +745,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());
@@ -1744,15 +1758,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) {
@@ -1760,7 +1780,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 104039db03c7ca..6665af966b9771 100644
--- a/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.h
+++ b/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.h
@@ -229,14 +229,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);
@@ -255,6 +250,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,
MCSymbol *LineTableSym);
diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
index affc72b5950fd9..0df7783f5d0afe 100644
--- a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
@@ -1215,12 +1215,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);
@@ -1420,9 +1421,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!");
}
@@ -1520,6 +1525,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 3af397bfc9ad19..c6215951e7a7e6 100644
--- a/llvm/lib/IR/DIBuilder.cpp
+++ b/llvm/lib/IR/DIBuilder.cpp
@@ -30,7 +30,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())
@@ -67,10 +67,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
@@ -361,10 +361,13 @@ DIBuilder::createTemplateAlias(DIType *Ty, StringRef Name, DIFile *File,
unsigned LineNo, DIScope *Context,
DINodeArray TParams, uint32_t AlignInBits,
DINode::DIFlags Flags, DINodeArray Annotations) {
- return DIDerivedType::get(VMContext, dwarf::DW_TAG_template_alias, Name, File,
+ auto *T = DIDerivedType::get(VMContext, dwarf::DW_TAG_template_alias, Name, File,
LineNo, getNonCompileUnitScope(Context), Ty, 0,
AlignInBits, 0, std::nullopt, std::nullopt, Flags,
TParams.get(), Annotations);
+ if (isa_and_nonnull<DILocalScope>(Context))
+ getSubprogramNodesTrackingVector(Context).emplace_back(T);
+ return T;
}
DIDerivedType *DIBuilder::createFriend(DIType *Ty, DIType *FriendTy) {
@@ -514,6 +517,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;
}
@@ -530,6 +535,8 @@ DICompositeType *DIBuilder::createStructType(
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, Specification,
NumExtraInhabitants);
trackIfUnresolved(R);
+ if (isa_and_nonnull<DILocalScope>(Context))
+ getSubprogramNodesTrackingVector(Context).emplace_back(R);
return R;
}
@@ -542,6 +549,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;
}
@@ -576,7 +585,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;
}
@@ -657,7 +669,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; }
@@ -674,6 +687,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;
}
@@ -690,6 +705,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 e5b45e0082a823..d1c23820c81586 100644
--- a/llvm/lib/IR/DebugInfo.cpp
+++ b/llvm/lib/IR/DebugInfo.cpp
@@ -256,7 +256,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());
@@ -274,7 +274,7 @@ void DebugInfoFinder::processLocation(const Module &M, const DILocation *Loc) {
void DebugInfoFinder::processDbgRecord(const Module &M, const DbgRecord &DR) {
if (const DbgVariableRecord *DVR = dyn_cast<const DbgVariableRecord>(&DR))
- processVariable(M, DVR->getVariable());
+ processVariable(DVR->getVariable());
processLocation(M, DR.getDebugLoc().get());
}
@@ -349,12 +349,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 67834631b3c50d..6063a9611fb24a 100644
--- a/llvm/lib/IR/Verifier.cpp
+++ b/llvm/lib/IR/Verifier.cpp
@@ -1467,9 +1467,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 cb6a4e34c226e5..58a59be09530ed 100644
--- a/llvm/lib/Transforms/Utils/CloneFunction.cpp
+++ b/llvm/lib/Transforms/Utils/CloneFunction.cpp
@@ -263,12 +263,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);
@@ -280,7 +288,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=
zSie0NwAk3VXnE9Y+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 16a17edccc33e2..8a565019eddfd1 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 96e863b257c95e..fc45d1eead2bab 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)
>From a7a3f59f3caae556c6613056f634b504d5052964 Mon Sep 17 00:00:00 2001
From: Jeremy Morse <jeremy.morse at sony.com>
Date: Thu, 5 Dec 2024 10:30:17 +0000
Subject: [PATCH 2/2] [DebugInfo] Place local ODR-uniqued types in decl
DISubprograms
There are two flavours of DISubprogram: declarations, which are unique'd
and abstractly describe the function in question. There are also definition
DISubprograms which correspond to real instances, some of which are
inlined, duplicated across translation units in LTO, or otherwise can have
multiple instances.
Given that LLVM sometimes force-uniques types by their ODR-name, see the
enableDebugTypeODRUniquing feature, we shouldn't place types that might be
unique'd into duplicated contexts like definition DISubprograms. Instead,
place them into the declaration.
This slightly bends the existing approach where only functions that have a
separate declaratrion to their definition get a declaration-DISubprogram. A
single function in a translation unit might now get a declaration where it
didn't before, if it contains an ODR-unique'd type declaration. This seems
reasonable given that the LLVM idea of a declaration doesn't have to
exactly match source-language ideas.
The added cpp test checks that such ORD-unique'd types are detected and
placed in the declaration DISubprogram, creating one if necessary. The IR
test ensures that nothing changes after a round-trip with
enableDebugTypeODRUniquing turned on.
---
clang/lib/CodeGen/CGDebugInfo.cpp | 55 ++++++++
clang/lib/CodeGen/CGDebugInfo.h | 2 +
.../debug-info-codeview-unnamed.cpp | 61 ++++-----
.../CodeGenCXX/debug-info-local-types.cpp | 79 +++++++++++
.../DebugInfo/local-odr-types-hiearchy.ll | 124 ++++++++++++++++++
5 files changed, 288 insertions(+), 33 deletions(-)
create mode 100644 clang/test/CodeGenCXX/debug-info-local-types.cpp
create mode 100644 llvm/test/DebugInfo/local-odr-types-hiearchy.ll
diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp
index 60f32f76109e9a..e56aef662a571d 100644
--- a/clang/lib/CodeGen/CGDebugInfo.cpp
+++ b/clang/lib/CodeGen/CGDebugInfo.cpp
@@ -1235,6 +1235,7 @@ CGDebugInfo::getOrCreateRecordFwdDecl(const RecordType *Ty,
// Don't include a linkage name in line tables only.
if (CGM.getCodeGenOpts().hasReducedDebugInfo())
Identifier = getTypeIdentifier(Ty, CGM, TheCU);
+ Ctx = PickCompositeTypeScope(Ctx, Identifier);
llvm::DICompositeType *RetTy = DBuilder.createReplaceableCompositeType(
getTagForRecord(RD), RDName, Ctx, DefUnit, Line, 0, Size, Align, Flags,
Identifier);
@@ -3534,6 +3535,7 @@ llvm::DIType *CGDebugInfo::CreateEnumType(const EnumType *Ty) {
// FwdDecl with the second and then replace the second with
// complete type.
llvm::DIScope *EDContext = getDeclContextDescriptor(ED);
+ EDContext = PickCompositeTypeScope(EDContext, Identifier);
llvm::DIFile *DefUnit = getOrCreateFile(ED->getLocation());
llvm::TempDIScope TmpContext(DBuilder.createReplaceableCompositeType(
llvm::dwarf::DW_TAG_enumeration_type, "", TheCU, DefUnit, 0));
@@ -3901,6 +3903,58 @@ CGDebugInfo::getOrCreateLimitedType(const RecordType *Ty) {
return Res;
}
+llvm::DIScope *
+CGDebugInfo::PickCompositeTypeScope(llvm::DIScope *S, StringRef Identifier) {
+ using llvm::DISubprogram;
+
+ // Only adjust the scope for composite types placed into functions.
+ if (!isa<DISubprogram>(S))
+ return S;
+
+ // We must adjust the scope if the ODR-name of the type is set.
+ if (Identifier.empty())
+ return S;
+
+ // This type has an ODR-name, and might be de-duplicated during LTO. It needs
+ // to be placed in the unique declaration of the function, not a (potentially
+ // duplicated) definition.
+ DISubprogram *SP = cast<DISubprogram>(S);
+ if (DISubprogram *Decl = SP->getDeclaration())
+ return Decl;
+
+ // There is no declaration -- we must produce one and retrofit it to the
+ // existing definition. Assume that we can just harvest the existing
+ // information, clear the definition flag and set as decl.
+ DISubprogram::DISPFlags SPFlags = SP->getSPFlags();
+ SPFlags &= ~DISubprogram::SPFlagDefinition;
+
+ llvm::DINode::DIFlags Flags = SP->getFlags();
+ Flags &= ~llvm::DINode::FlagAllCallsDescribed;
+
+#ifdef EXPENSIVE_CHECKS
+ // If we're looking to be really rigorous and avoid a hard-to-debug mishap,
+ // make sure that there aren't any function definitions in the scope chain.
+ llvm::DIScope *ToCheck = SP->getScope();
+ do {
+ // We should terminate at a DIFile rather than a DICompileUnit -- we're
+ // not fully unique across LTO otherwise.
+ assert(!isa<llvm::DICompileUnit>(ToCheck));
+ if (auto *DISP = dyn_cast<DISubprogram>(ToCheck))
+ assert(!(DISP->getSPFlags() & DISubprogram::SPFlagDefinition));
+ ToCheck = ToCheck->getScope();
+ } while (ToCheck);
+#endif
+
+ DISubprogram *DeclSP = DBuilder.createFunction(
+ SP->getScope(), SP->getName(), SP->getLinkageName(), SP->getFile(),
+ SP->getLine(), SP->getType(), SP->getScopeLine(),
+ Flags, SPFlags, SP->getTemplateParams(), nullptr, nullptr,
+ SP->getAnnotations());
+
+ SP->replaceDeclaration(DeclSP);
+ return DeclSP;
+}
+
// TODO: Currently used for context chains when limiting debug info.
llvm::DICompositeType *CGDebugInfo::CreateLimitedType(const RecordType *Ty) {
RecordDecl *RD = Ty->getDecl();
@@ -3938,6 +3992,7 @@ llvm::DICompositeType *CGDebugInfo::CreateLimitedType(const RecordType *Ty) {
auto Align = getTypeAlignIfRequired(Ty, CGM.getContext());
SmallString<256> Identifier = getTypeIdentifier(Ty, CGM, TheCU);
+ RDContext = PickCompositeTypeScope(RDContext, Identifier);
// Explicitly record the calling convention and export symbols for C++
// records.
diff --git a/clang/lib/CodeGen/CGDebugInfo.h b/clang/lib/CodeGen/CGDebugInfo.h
index 3fd0237a1c61dd..71c84c45986c78 100644
--- a/clang/lib/CodeGen/CGDebugInfo.h
+++ b/clang/lib/CodeGen/CGDebugInfo.h
@@ -486,6 +486,8 @@ class CGDebugInfo {
void EmitFunctionDecl(GlobalDecl GD, SourceLocation Loc,
QualType FnType, llvm::Function *Fn = nullptr);
+ llvm::DIScope *PickCompositeTypeScope(llvm::DIScope *S, StringRef Identifier);
+
/// Emit debug info for an extern function being called.
/// This is needed for call site debug info.
void EmitFuncDeclForCallSite(llvm::CallBase *CallOrInvoke,
diff --git a/clang/test/CodeGenCXX/debug-info-codeview-unnamed.cpp b/clang/test/CodeGenCXX/debug-info-codeview-unnamed.cpp
index 2d6d8a56e97e31..06767370483790 100644
--- a/clang/test/CodeGenCXX/debug-info-codeview-unnamed.cpp
+++ b/clang/test/CodeGenCXX/debug-info-codeview-unnamed.cpp
@@ -8,13 +8,6 @@ int main(int argc, char* argv[], char* arge[]) {
// 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(
@@ -23,12 +16,6 @@ int main(int argc, char* argv[], char* arge[]) {
// 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(
@@ -36,12 +23,6 @@ int main(int argc, char* argv[], char* arge[]) {
// 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(
@@ -49,13 +30,6 @@ int main(int argc, char* argv[], char* arge[]) {
// 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
@@ -68,9 +42,14 @@ int main(int argc, char* argv[], char* arge[]) {
// LINUX-SAME: )
//
// MSVC: !{{[0-9]+}} = !DILocalVariable(name: "one"
- // MSVC-SAME: type: [[TYPE_OF_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: )
-
// In CodeView, the LF_POINTER entry for "ptr2unnamed" refers to the forward
// reference of the unnamed struct. Visual Studio requires a unique
@@ -84,9 +63,14 @@ int main(int argc, char* argv[], char* arge[]) {
// LINUX-SAME: )
//
// MSVC: !{{[0-9]+}} = !DILocalVariable(name: "two"
- // MSVC-SAME: type: [[TYPE_OF_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: )
-
// In DWARF, named structures which are not externally visibile do not
// require an identifier. In CodeView, named structures are given an
@@ -99,9 +83,14 @@ int main(int argc, char* argv[], char* arge[]) {
// LINUX-SAME: )
//
// MSVC: !{{[0-9]+}} = !DILocalVariable(name: "three"
- // MSVC-SAME: type: [[TYPE_OF_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: )
-
// In CodeView, the LF_MFUNCTION entry for the lambda "operator()" routine
// refers to the forward reference of the unnamed LF_CLASS for the lambda.
@@ -115,7 +104,13 @@ int main(int argc, char* argv[], char* arge[]) {
// LINUX-SAME: )
//
// MSVC: !{{[0-9]+}} = !DILocalVariable(name: "four"
- // MSVC-SAME: type: [[TYPE_OF_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: )
return 0;
diff --git a/clang/test/CodeGenCXX/debug-info-local-types.cpp b/clang/test/CodeGenCXX/debug-info-local-types.cpp
new file mode 100644
index 00000000000000..9a249fbf388312
--- /dev/null
+++ b/clang/test/CodeGenCXX/debug-info-local-types.cpp
@@ -0,0 +1,79 @@
+// RUN: %clang_cc1 -triple %itanium_abi_triple %s -o - -O0 -emit-llvm \
+// RUN: -disable-llvm-passes -debug-info-kind=limited | FileCheck %s
+//
+// Test that types declared inside functions, that receive an "identifier"
+// field used for ODR-uniquing, are placed inside the declaration DISubprogram
+// for the function rather than the definition DISubprogram. This avoids
+// later problems with distinct types in distinct DISubprograms being
+// inadvertantly unique'd; see github PR 75385.
+//
+// NB: The types below are marked distinct, but other portions of LLVM
+// force-unique them at a later date, see the enableDebugTypeODRUniquing
+// feature. Clang doesn't enable that itself; instead this test ensures a safe
+// representation of the types is produced.
+//
+// The check-lines below are not strictly in order of hierachy, so here's a
+// diagram of what's desired:
+//
+// DIFile
+// |
+// Decl-DISubprogram "foo"
+// / \
+// / \
+// Def-DISubprogram "foo" DICompositeType "bar"
+// |
+// |
+// Decl-DISubprogram "get_a"
+// / |
+// / |
+// Def-DISubprogram "get_a" DICompositeType "baz"
+// |
+// |
+// {Def,Decl}-DISubprogram "get_b"
+
+// CHECK: ![[FILENUM:[0-9]+]] = !DIFile(filename: "{{.*}}debug-info-local-types.cpp",
+
+// CHECK: ![[BARSTRUCT:[0-9]+]] = distinct !DICompositeType(tag: DW_TAG_class_type, name: "bar", scope: ![[FOOFUNC:[0-9]+]], file: ![[FILENUM]],
+// CHECK-SAME: identifier: "_ZTSZ3foovE3bar")
+
+// CHECK: ![[FOOFUNC]] = !DISubprogram(name: "foo", linkageName: "_Z3foov", scope: ![[FILENUM]], file: ![[FILENUM]],
+//// Test to ensure that this is _not_ a definition, therefore a decl.
+// CHECK-SAME: spFlags: 0)
+
+// CHECK: ![[GETADECL:[0-9]+]] = !DISubprogram(name: "get_a", scope: ![[BARSTRUCT]], file: ![[FILENUM]],
+//// Test to ensure that this is _not_ a definition, therefore a decl.
+// CHECK-SAME: spFlags: 0)
+
+// CHECK: ![[BAZSTRUCT:[0-9]+]] = distinct !DICompositeType(tag: DW_TAG_class_type, name: "baz", scope: ![[GETADECL]], file: ![[FILENUM]],
+// CHECK-SAME: identifier: "_ZTSZZ3foovEN3bar5get_aEvE3baz")
+// CHECK: distinct !DISubprogram(name: "get_b",
+// CHECK-SAME: scope: ![[BAZSTRUCT]], file: ![[FILENUM]],
+
+inline int foo() {
+ class bar {
+ private:
+ int a = 0;
+ public:
+ int get_a() {
+ class baz {
+ private:
+ int b = 0;
+ public:
+ int get_b() {
+ return b;
+ }
+ };
+
+ static baz xyzzy;
+ return a + xyzzy.get_b();
+ }
+ };
+
+ static bar baz;
+ return baz.get_a();
+}
+
+int a() {
+ return foo();
+}
+
diff --git a/llvm/test/DebugInfo/local-odr-types-hiearchy.ll b/llvm/test/DebugInfo/local-odr-types-hiearchy.ll
new file mode 100644
index 00000000000000..8d73e85643f434
--- /dev/null
+++ b/llvm/test/DebugInfo/local-odr-types-hiearchy.ll
@@ -0,0 +1,124 @@
+; RUN: opt %s -o - -S | FileCheck %s
+
+; Paired with clang/test/CodeGenCXX/debug-info-local-types.cpp, this round-trips
+; debug-info metadata for types that are ODR-uniqued, to ensure that the type
+; hierachy does not change. See the enableDebugTypeODRUniquing feature: types
+; that have the "identifier" field set will be unique'd based on their name,
+; even if the "distinct" flag is set. Clang doesn't enable that itself, but opt
+; does, therefore we pass the metadata through opt to check it doesn't change
+; the type hiearchy.
+;
+; The check-lines below are not strictly in order of hierachy, so here's a
+; diagram of what's desired:
+;
+; DIFile
+; |
+; Decl-DISubprogram "foo"
+; / \
+; / \
+; Def-DISubprogram "foo" DICompositeType "bar"
+; |
+; |
+; Decl-DISubprogram "get_a"
+; / |
+; / |
+; Def-DISubprogram "get_a" DICompositeType "baz"
+; |
+; |
+; {Def,Decl}-DISubprogram "get_b"
+;
+; The declaration DISubprograms are unique'd, and the DICompositeTypes should
+; be in those scopes rather than the definition DISubprograms.
+
+; CHECK: ![[FILENUM:[0-9]+]] = !DIFile(filename: "{{.*}}debug-info-local-types.cpp",
+
+; CHECK: ![[BARSTRUCT:[0-9]+]] = distinct !DICompositeType(tag: DW_TAG_class_type, name: "bar", scope: ![[FOOFUNC:[0-9]+]], file: ![[FILENUM]],
+; CHECK-SAME: identifier: "_ZTSZ3foovE3bar")
+
+; CHECK: ![[FOOFUNC]] = !DISubprogram(name: "foo", linkageName: "_Z3foov", scope: ![[FILENUM]], file: ![[FILENUM]],
+;; Test to ensure that this is _not_ a definition, therefore a decl.
+; CHECK-SAME: spFlags: 0)
+
+; CHECK: ![[GETADECL:[0-9]+]] = !DISubprogram(name: "get_a", scope: ![[BARSTRUCT]], file: ![[FILENUM]],
+;; Test to ensure that this is _not_ a definition, therefore a decl.
+; CHECK-SAME: spFlags: 0)
+
+; CHECK: ![[BAZSTRUCT:[0-9]+]] = distinct !DICompositeType(tag: DW_TAG_class_type, name: "baz", scope: ![[GETADECL]], file: ![[FILENUM]],
+; CHECK-SAME: identifier: "_ZTSZZ3foovEN3bar5get_aEvE3baz")
+; CHECK: distinct !DISubprogram(name: "get_b",
+; CHECK-SAME: scope: ![[BAZSTRUCT]], file: ![[FILENUM]],
+
+%class.bar = type { i32 }
+%class.baz = type { i32 }
+
+$_Z3foov = comdat any
+
+$_ZZ3foovEN3bar5get_aEv = comdat any
+
+$_ZZZ3foovEN3bar5get_aEvEN3baz5get_bEv = comdat any
+
+$_ZZ3foovE3baz = comdat any
+
+$_ZZZ3foovEN3bar5get_aEvE5xyzzy = comdat any
+
+ at _ZZ3foovE3baz = linkonce_odr global %class.bar zeroinitializer, comdat, align 4, !dbg !0
+ at _ZZZ3foovEN3bar5get_aEvE5xyzzy = linkonce_odr global %class.baz zeroinitializer, comdat, align 4, !dbg !10
+
+define dso_local noundef i32 @_Z1av() !dbg !32 {
+entry:
+ unreachable
+}
+
+define linkonce_odr noundef i32 @_Z3foov() comdat !dbg !2 {
+entry:
+ unreachable
+}
+
+define linkonce_odr noundef i32 @_ZZ3foovEN3bar5get_aEv(ptr noundef nonnull align 4 dereferenceable(4) %this) comdat align 2 !dbg !12 {
+entry:
+ unreachable
+}
+
+define linkonce_odr noundef i32 @_ZZZ3foovEN3bar5get_aEvEN3baz5get_bEv(ptr noundef nonnull align 4 dereferenceable(4) %this) comdat align 2 !dbg !33 {
+entry:
+ unreachable
+}
+
+!llvm.dbg.cu = !{!7}
+!llvm.module.flags = !{!29, !30}
+!llvm.ident = !{!31}
+
+!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression())
+!1 = distinct !DIGlobalVariable(name: "baz", scope: !2, file: !3, line: 71, type: !13, isLocal: false, isDefinition: true)
+!2 = distinct !DISubprogram(name: "foo", linkageName: "_Z3foov", scope: !3, file: !3, line: 51, type: !4, scopeLine: 51, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !7, declaration: !14)
+!3 = !DIFile(filename: "debug-info-local-types.cpp", directory: ".")
+!4 = !DISubroutineType(types: !5)
+!5 = !{!6}
+!6 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!7 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !8, producer: "clang", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, globals: !9, splitDebugInlining: false, nameTableKind: None)
+!8 = !DIFile(filename: "<stdin>", directory: ".")
+!9 = !{!0, !10}
+!10 = !DIGlobalVariableExpression(var: !11, expr: !DIExpression())
+!11 = distinct !DIGlobalVariable(name: "xyzzy", scope: !12, file: !3, line: 66, type: !22, isLocal: false, isDefinition: true)
+!12 = distinct !DISubprogram(name: "get_a", linkageName: "_ZZ3foovEN3bar5get_aEv", scope: !13, file: !3, line: 56, type: !18, scopeLine: 56, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !7, declaration: !17, retainedNodes: !21)
+!13 = distinct !DICompositeType(tag: DW_TAG_class_type, name: "bar", scope: !14, file: !3, line: 52, size: 32, flags: DIFlagTypePassByValue | DIFlagNonTrivial, elements: !15, identifier: "_ZTSZ3foovE3bar")
+!14 = !DISubprogram(name: "foo", linkageName: "_Z3foov", scope: !3, file: !3, line: 51, type: !4, scopeLine: 51, flags: DIFlagPrototyped, spFlags: 0)
+!15 = !{!16, !17}
+!16 = !DIDerivedType(tag: DW_TAG_member, name: "a", scope: !13, file: !3, line: 54, baseType: !6, size: 32)
+!17 = !DISubprogram(name: "get_a", scope: !13, file: !3, line: 56, type: !18, scopeLine: 56, flags: DIFlagPublic | DIFlagPrototyped, spFlags: 0)
+!18 = !DISubroutineType(types: !19)
+!19 = !{!6, !20}
+!20 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64, flags: DIFlagArtificial | DIFlagObjectPointer)
+!21 = !{}
+!22 = distinct !DICompositeType(tag: DW_TAG_class_type, name: "baz", scope: !17, file: !3, line: 57, size: 32, flags: DIFlagTypePassByValue | DIFlagNonTrivial, elements: !23, identifier: "_ZTSZZ3foovEN3bar5get_aEvE3baz")
+!23 = !{!24, !25}
+!24 = !DIDerivedType(tag: DW_TAG_member, name: "b", scope: !22, file: !3, line: 59, baseType: !6, size: 32)
+!25 = !DISubprogram(name: "get_b", scope: !22, file: !3, line: 61, type: !26, scopeLine: 61, flags: DIFlagPublic | DIFlagPrototyped, spFlags: 0)
+!26 = !DISubroutineType(types: !27)
+!27 = !{!6, !28}
+!28 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64, flags: DIFlagArtificial | DIFlagObjectPointer)
+!29 = !{i32 2, !"Debug Info Version", i32 3}
+!30 = !{i32 1, !"wchar_size", i32 4}
+!31 = !{!"clang"}
+!32 = distinct !DISubprogram(name: "a", linkageName: "_Z1av", scope: !3, file: !3, line: 75, type: !4, scopeLine: 75, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !7)
+!33 = distinct !DISubprogram(name: "get_b", linkageName: "_ZZZ3foovEN3bar5get_aEvEN3baz5get_bEv", scope: !22, file: !3, line: 61, type: !26, scopeLine: 61, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !7, declaration: !25, retainedNodes: !21)
More information about the cfe-commits
mailing list