aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--clang/lib/CodeGen/CGVTables.cpp33
-rw-r--r--clang/test/CodeGenCXX/tmp-md-nodes3.cpp53
-rw-r--r--clang/test/DebugInfo/CXX/anon-union-vars.cpp12
-rw-r--r--clang/test/DebugInfo/CXX/codeview-unnamed.cpp110
-rw-r--r--clang/test/DebugInfo/CXX/gline-tables-only-codeview.cpp4
-rw-r--r--clang/test/DebugInfo/CXX/lambda-capture-packs.cpp15
-rw-r--r--clang/test/DebugInfo/CXX/lambda-this.cpp4
-rw-r--r--clang/test/DebugInfo/Generic/codeview-unnamed.c16
-rw-r--r--clang/test/DebugInfo/Generic/unused-types.c16
-rw-r--r--clang/test/DebugInfo/Generic/unused-types.cpp14
-rw-r--r--llvm/include/llvm/AsmParser/LLParser.h4
-rw-r--r--llvm/include/llvm/IR/DIBuilder.h6
-rw-r--r--llvm/include/llvm/IR/DebugInfoMetadata.h72
-rw-r--r--llvm/lib/AsmParser/LLParser.cpp7
-rw-r--r--llvm/lib/Bitcode/Reader/MetadataLoader.cpp178
-rw-r--r--llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp24
-rw-r--r--llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.h16
-rw-r--r--llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp11
-rw-r--r--llvm/lib/IR/DIBuilder.cpp66
-rw-r--r--llvm/lib/IR/DebugInfo.cpp6
-rw-r--r--llvm/lib/IR/DebugInfoMetadata.cpp61
-rw-r--r--llvm/lib/IR/Verifier.cpp17
-rw-r--r--llvm/lib/Transforms/Utils/CloneFunction.cpp27
-rw-r--r--llvm/test/Bitcode/upgrade-cu-locals.ll70
-rw-r--r--llvm/test/Bitcode/upgrade-cu-locals.ll.bcbin2688 -> 2780 bytes
-rw-r--r--llvm/test/DebugInfo/Generic/inlined-local-type.ll127
-rw-r--r--llvm/test/DebugInfo/Generic/lexical-block-retained-types.ll72
-rw-r--r--llvm/test/DebugInfo/Generic/lexical-block-types.ll424
-rw-r--r--llvm/test/DebugInfo/Generic/verifier-invalid-disubprogram.ll2
-rw-r--r--llvm/test/DebugInfo/Inputs/cleanup-retained-nodes.ll17
-rw-r--r--llvm/test/DebugInfo/X86/cleanup-retained-nodes.ll45
-rw-r--r--llvm/test/DebugInfo/X86/llparser-cleanup-retained-nodes.ll35
-rw-r--r--llvm/test/DebugInfo/X86/local-type-as-template-parameter.ll161
-rw-r--r--llvm/test/Transforms/FunctionImport/Inputs/funcimport-debug-retained-nodes.ll32
-rw-r--r--llvm/test/Transforms/FunctionImport/funcimport-debug-retained-nodes.ll118
-rw-r--r--llvm/unittests/Transforms/Utils/CloningTest.cpp139
36 files changed, 1763 insertions, 251 deletions
diff --git a/clang/lib/CodeGen/CGVTables.cpp b/clang/lib/CodeGen/CGVTables.cpp
index 46f0019..3891697 100644
--- a/clang/lib/CodeGen/CGVTables.cpp
+++ b/clang/lib/CodeGen/CGVTables.cpp
@@ -140,19 +140,32 @@ static void resolveTopLevelMetadata(llvm::Function *Fn,
// Find all llvm.dbg.declare intrinsics and resolve the DILocalVariable nodes
// they are referencing.
+ //
+ // DIDerivedTypes referring to incomplete Clang types, or
+ // LLVM enumeration types representing complete enums with no definition
+ // may be still unresolved. As they can't be cloned, keep references
+ // to the types from the base subprogram.
+ // FIXME: As a result, variables of cloned subprogram may refer to local types
+ // from base subprogram. In such case, type locality information is damaged.
+ // Find a way to enable cloning of all local types.
+ auto PrepareVariableMapping = [&VMap](llvm::DILocalVariable *DILocal) {
+ if (DILocal->isResolved())
+ return;
+
+ if (llvm::DIType *Ty = DILocal->getType(); Ty && !Ty->isResolved())
+ VMap.MD()[Ty].reset(Ty);
+
+ DILocal->resolve();
+ };
+
for (auto &BB : *Fn) {
for (auto &I : BB) {
for (llvm::DbgVariableRecord &DVR :
- llvm::filterDbgVars(I.getDbgRecordRange())) {
- auto *DILocal = DVR.getVariable();
- if (!DILocal->isResolved())
- DILocal->resolve();
- }
- if (auto *DII = dyn_cast<llvm::DbgVariableIntrinsic>(&I)) {
- auto *DILocal = DII->getVariable();
- if (!DILocal->isResolved())
- DILocal->resolve();
- }
+ llvm::filterDbgVars(I.getDbgRecordRange()))
+ PrepareVariableMapping(DVR.getVariable());
+
+ if (auto *DII = dyn_cast<llvm::DbgVariableIntrinsic>(&I))
+ PrepareVariableMapping(DII->getVariable());
}
}
}
diff --git a/clang/test/CodeGenCXX/tmp-md-nodes3.cpp b/clang/test/CodeGenCXX/tmp-md-nodes3.cpp
new file mode 100644
index 0000000..4656f64
--- /dev/null
+++ b/clang/test/CodeGenCXX/tmp-md-nodes3.cpp
@@ -0,0 +1,53 @@
+// REQUIRES: asserts, riscv-registered-target
+// Should trigger GenerateVarArgsThunk.
+// RUN: %clang_cc1 -O0 -triple riscv64-linux-gnu -debug-info-kind=limited -emit-llvm %s -o - | \
+// RUN: FileCheck %s
+
+// RUN: %clang_cc1 -O0 -triple riscv64-linux-gnu -debug-info-kind=limited -emit-obj %s -o - | \
+// RUN: llvm-dwarfdump --verify -
+
+// This test checks that clang doesn't crash when creating a varargs thunk
+// by cloning a function which DILocalVariable types are unresolved at cloning
+// time.
+// In such case, as a workaround, instead of cloning unresolved types for
+// the thunk, clang produces thunk DILocalVariables that refer to local
+// types from the original DISubprogram.
+
+typedef signed char __int8_t;
+typedef int BOOL;
+class CMsgAgent;
+
+class CFs {
+public:
+ typedef enum {} CACHE_HINT;
+ virtual BOOL ReqCacheHint( CMsgAgent* p_ma, CACHE_HINT hint, ... ) ;
+};
+
+typedef struct {} _Lldiv_t;
+
+class CBdVfs {
+public:
+ virtual ~CBdVfs( ) {}
+};
+
+class CBdVfsImpl : public CBdVfs, public CFs {
+ BOOL ReqCacheHint( CMsgAgent* p_ma, CACHE_HINT hint, ... );
+};
+
+BOOL CBdVfsImpl::ReqCacheHint( CMsgAgent* p_ma, CACHE_HINT hint, ... ) {
+ // Complete enum with no defintion. Clang allows to declare variables of
+ // such type.
+ enum class E : int;
+ E enum_var;
+ // Structure has forward declaration. DIDerivedType which is a pointer
+ // to it is unresolved during debug info generation for the function.
+ struct LocalStruct;
+ LocalStruct *struct_var;
+
+ struct GlobalStruct {};
+
+ return true;
+}
+
+// Check that thunk is emitted.
+// CHECK: define {{.*}} @_ZThn{{[48]}}_N10CBdVfsImpl12ReqCacheHintEP9CMsgAgentN3CFs10CACHE_HINTEz(
diff --git a/clang/test/DebugInfo/CXX/anon-union-vars.cpp b/clang/test/DebugInfo/CXX/anon-union-vars.cpp
index 3aca4e1..547d31c 100644
--- a/clang/test/DebugInfo/CXX/anon-union-vars.cpp
+++ b/clang/test/DebugInfo/CXX/anon-union-vars.cpp
@@ -51,13 +51,13 @@ void instantiate(int x) {
// CHECK: !DIGlobalVariable(name: "b",{{.*}} file: [[FILE]], line: 6,{{.*}} isLocal: true, isDefinition: true
// CHECK: !DIGlobalVariable(name: "result", {{.*}} isLocal: false, isDefinition: true
// CHECK: !DIGlobalVariable(name: "value", {{.*}} isLocal: false, isDefinition: true
-// CHECK: !DILocalVariable(name: "i", {{.*}}, flags: DIFlagArtificial
-// CHECK: !DILocalVariable(name: "c", {{.*}}, flags: DIFlagArtificial
-// CHECK: !DILocalVariable(
-// CHECK-NOT: name:
-// CHECK: type: ![[UNION:[0-9]+]]
-// CHECK: ![[UNION]] = distinct !DICompositeType(tag: DW_TAG_union_type,
+// CHECK: ![[UNION:[0-9]+]] = distinct !DICompositeType(tag: DW_TAG_union_type,
// CHECK-NOT: name:
// CHECK: elements
// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "i", scope: ![[UNION]],
// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "c", scope: ![[UNION]],
+// CHECK: !DILocalVariable(name: "i", {{.*}}, flags: DIFlagArtificial
+// CHECK: !DILocalVariable(name: "c", {{.*}}, flags: DIFlagArtificial
+// CHECK: !DILocalVariable(
+// CHECK-NOT: name:
+// CHECK-SAME: type: ![[UNION]]
diff --git a/clang/test/DebugInfo/CXX/codeview-unnamed.cpp b/clang/test/DebugInfo/CXX/codeview-unnamed.cpp
index 30815bd..b625a80 100644
--- a/clang/test/DebugInfo/CXX/codeview-unnamed.cpp
+++ b/clang/test/DebugInfo/CXX/codeview-unnamed.cpp
@@ -4,6 +4,60 @@
int main(int argc, char* argv[], char* arge[]) {
//
+ // LINUX: [[TYPE_OF_ONE:![0-9]+]] = distinct !DICompositeType(
+ // LINUX-SAME: tag: DW_TAG_structure_type
+ // LINUX-NOT: name:
+ // LINUX-NOT: identifier:
+ // LINUX-SAME: )
+ //
+ // MSVC: [[TYPE_OF_ONE:![0-9]+]] = distinct !DICompositeType
+ // MSVC-SAME: tag: DW_TAG_structure_type
+ // MSVC-SAME: name: "<unnamed-type-one>"
+ // MSVC-SAME: identifier: ".?AU<unnamed-type-one>@?1??main@@9@"
+ // MSVC-SAME: )
+
+
+ //
+ // LINUX: [[TYPE_OF_TWO:![0-9]+]] = distinct !DICompositeType(
+ // LINUX-SAME: tag: DW_TAG_structure_type
+ // LINUX-NOT: name:
+ // LINUX-NOT: identifier:
+ // LINUX-SAME: )
+ //
+ // MSVC: [[TYPE_OF_TWO:![0-9]+]] = distinct !DICompositeType
+ // MSVC-SAME: tag: DW_TAG_structure_type
+ // MSVC-SAME: name: "<unnamed-type-two>"
+ // MSVC-SAME: identifier: ".?AU<unnamed-type-two>@?2??main@@9@"
+ // MSVC-SAME: )
+
+
+ //
+ // LINUX: [[TYPE_OF_THREE:![0-9]+]] = distinct !DICompositeType(
+ // LINUX-SAME: tag: DW_TAG_structure_type
+ // LINUX-SAME: name: "named"
+ // LINUX-NOT: identifier:
+ // LINUX-SAME: )
+ //
+ // MSVC: [[TYPE_OF_THREE:![0-9]+]] = distinct !DICompositeType
+ // MSVC-SAME: tag: DW_TAG_structure_type
+ // MSVC-SAME: name: "named"
+ // MSVC-SAME: identifier: ".?AUnamed@?1??main@@9@"
+ // MSVC-SAME: )
+
+ //
+ // LINUX: [[TYPE_OF_FOUR:![0-9]+]] = distinct !DICompositeType(
+ // LINUX-SAME: tag: DW_TAG_class_type
+ // LINUX-NOT: name:
+ // LINUX-NOT: identifier:
+ // LINUX-SAME: )
+ //
+ // MSVC: [[TYPE_OF_FOUR:![0-9]+]] = distinct !DICompositeType
+ // MSVC-SAME: tag: DW_TAG_class_type
+ // MSVC-SAME: name: "<lambda_0>"
+ // MSVC-SAME: identifier: ".?AV<lambda_0>@?0??main@@9@"
+ // MSVC-SAME: )
+
+
// In CodeView, the LF_MFUNCTION entry for "bar()" refers to the forward
// reference of the unnamed struct. Visual Studio requires a unique
// identifier to match the LF_STRUCTURE forward reference to the definition.
@@ -11,21 +65,11 @@ int main(int argc, char* argv[], char* arge[]) {
struct { void bar() {} } one;
//
// LINUX: !{{[0-9]+}} = !DILocalVariable(name: "one"
- // LINUX-SAME: type: [[TYPE_OF_ONE:![0-9]+]]
- // LINUX-SAME: )
- // LINUX: [[TYPE_OF_ONE]] = distinct !DICompositeType(
- // LINUX-SAME: tag: DW_TAG_structure_type
- // LINUX-NOT: name:
- // LINUX-NOT: identifier:
+ // LINUX-SAME: type: [[TYPE_OF_ONE]]
// LINUX-SAME: )
//
// MSVC: !{{[0-9]+}} = !DILocalVariable(name: "one"
- // MSVC-SAME: type: [[TYPE_OF_ONE:![0-9]+]]
- // MSVC-SAME: )
- // MSVC: [[TYPE_OF_ONE]] = distinct !DICompositeType
- // MSVC-SAME: tag: DW_TAG_structure_type
- // MSVC-SAME: name: "<unnamed-type-one>"
- // MSVC-SAME: identifier: ".?AU<unnamed-type-one>@?1??main@@9@"
+ // MSVC-SAME: type: [[TYPE_OF_ONE]]
// MSVC-SAME: )
@@ -37,21 +81,11 @@ int main(int argc, char* argv[], char* arge[]) {
int decltype(two)::*ptr2unnamed = &decltype(two)::bar;
//
// LINUX: !{{[0-9]+}} = !DILocalVariable(name: "two"
- // LINUX-SAME: type: [[TYPE_OF_TWO:![0-9]+]]
- // LINUX-SAME: )
- // LINUX: [[TYPE_OF_TWO]] = distinct !DICompositeType(
- // LINUX-SAME: tag: DW_TAG_structure_type
- // LINUX-NOT: name:
- // LINUX-NOT: identifier:
+ // LINUX-SAME: type: [[TYPE_OF_TWO]]
// LINUX-SAME: )
//
// MSVC: !{{[0-9]+}} = !DILocalVariable(name: "two"
- // MSVC-SAME: type: [[TYPE_OF_TWO:![0-9]+]]
- // MSVC-SAME: )
- // MSVC: [[TYPE_OF_TWO]] = distinct !DICompositeType
- // MSVC-SAME: tag: DW_TAG_structure_type
- // MSVC-SAME: name: "<unnamed-type-two>"
- // MSVC-SAME: identifier: ".?AU<unnamed-type-two>@?2??main@@9@"
+ // MSVC-SAME: type: [[TYPE_OF_TWO]]
// MSVC-SAME: )
@@ -62,21 +96,11 @@ int main(int argc, char* argv[], char* arge[]) {
struct named { int bar; int named::* p2mem; } three = { 42, &named::bar };
//
// LINUX: !{{[0-9]+}} = !DILocalVariable(name: "three"
- // LINUX-SAME: type: [[TYPE_OF_THREE:![0-9]+]]
- // LINUX-SAME: )
- // LINUX: [[TYPE_OF_THREE]] = distinct !DICompositeType(
- // LINUX-SAME: tag: DW_TAG_structure_type
- // LINUX-SAME: name: "named"
- // LINUX-NOT: identifier:
+ // LINUX-SAME: type: [[TYPE_OF_THREE]]
// LINUX-SAME: )
//
// MSVC: !{{[0-9]+}} = !DILocalVariable(name: "three"
- // MSVC-SAME: type: [[TYPE_OF_THREE:![0-9]+]]
- // MSVC-SAME: )
- // MSVC: [[TYPE_OF_THREE]] = distinct !DICompositeType
- // MSVC-SAME: tag: DW_TAG_structure_type
- // MSVC-SAME: name: "named"
- // MSVC-SAME: identifier: ".?AUnamed@?1??main@@9@"
+ // MSVC-SAME: type: [[TYPE_OF_THREE]]
// MSVC-SAME: )
@@ -88,21 +112,11 @@ int main(int argc, char* argv[], char* arge[]) {
auto four = [argc](int i) -> int { return argc == i ? 1 : 0; };
//
// LINUX: !{{[0-9]+}} = !DILocalVariable(name: "four"
- // LINUX-SAME: type: [[TYPE_OF_FOUR:![0-9]+]]
- // LINUX-SAME: )
- // LINUX: [[TYPE_OF_FOUR]] = distinct !DICompositeType(
- // LINUX-SAME: tag: DW_TAG_class_type
- // LINUX-NOT: name:
- // LINUX-NOT: identifier:
+ // LINUX-SAME: type: [[TYPE_OF_FOUR]]
// LINUX-SAME: )
//
// MSVC: !{{[0-9]+}} = !DILocalVariable(name: "four"
- // MSVC-SAME: type: [[TYPE_OF_FOUR:![0-9]+]]
- // MSVC-SAME: )
- // MSVC: [[TYPE_OF_FOUR]] = distinct !DICompositeType
- // MSVC-SAME: tag: DW_TAG_class_type
- // MSVC-SAME: name: "<lambda_0>"
- // MSVC-SAME: identifier: ".?AV<lambda_0>@?0??main@@9@"
+ // MSVC-SAME: type: [[TYPE_OF_FOUR]]
// MSVC-SAME: )
return 0;
diff --git a/clang/test/DebugInfo/CXX/gline-tables-only-codeview.cpp b/clang/test/DebugInfo/CXX/gline-tables-only-codeview.cpp
index 6b9c9a2..122e4aa 100644
--- a/clang/test/DebugInfo/CXX/gline-tables-only-codeview.cpp
+++ b/clang/test/DebugInfo/CXX/gline-tables-only-codeview.cpp
@@ -51,9 +51,9 @@ void test() {
// CHECK-SAME: name: "<lambda_2_1>",
c.lambda_params();
- // CHECK: !DISubprogram(name: "operator()", scope: ![[LAMBDA1:[0-9]+]],
- // CHECK: ![[LAMBDA1]] = !DICompositeType(tag: DW_TAG_class_type,
+ // CHECK: ![[LAMBDA1:[0-9]+]] = !DICompositeType(tag: DW_TAG_class_type,
// CHECK-SAME: name: "<lambda_1>",
// CHECK-SAME: flags: DIFlagFwdDecl
+ // CHECK: !DISubprogram(name: "operator()", scope: ![[LAMBDA1]],
c.lambda2();
}
diff --git a/clang/test/DebugInfo/CXX/lambda-capture-packs.cpp b/clang/test/DebugInfo/CXX/lambda-capture-packs.cpp
index 021b85d..609a71c 100644
--- a/clang/test/DebugInfo/CXX/lambda-capture-packs.cpp
+++ b/clang/test/DebugInfo/CXX/lambda-capture-packs.cpp
@@ -2,14 +2,6 @@
// RUN: -debug-info-kind=standalone -std=c++26 %s -o - | FileCheck %s
-// CHECK: ![[PACK1:[0-9]+]] = distinct !DISubprogram(name: "capture_pack<int>"
-// CHECK: ![[PACK2:[0-9]+]] = distinct !DISubprogram(name: "capture_pack<int, int>"
-// CHECK: ![[PACK3:[0-9]+]] = distinct !DISubprogram(name: "capture_pack_and_locals<int>"
-// CHECK: ![[PACK4:[0-9]+]] = distinct !DISubprogram(name: "capture_pack_and_locals<int, int>"
-// CHECK: ![[PACK5:[0-9]+]] = distinct !DISubprogram(name: "capture_pack_and_this<int>"
-// CHECK: ![[PACK6:[0-9]+]] = distinct !DISubprogram(name: "capture_pack_and_this<int, int>"
-// CHECK: ![[PACK7:[0-9]+]] = distinct !DISubprogram(name: "capture_binding_and_param_pack<int, int>"
-
template<typename... Args>
auto capture_pack(Args... args) {
return [args..., ...params = args] {
@@ -17,6 +9,7 @@ auto capture_pack(Args... args) {
}();
}
+// CHECK: ![[PACK1:[0-9]+]] = distinct !DISubprogram(name: "capture_pack<int>"
// CHECK: distinct !DICompositeType(tag: DW_TAG_class_type, scope: ![[PACK1]]
// CHECK-SAME: elements: ![[PACK1_ELEMS:[0-9]+]]
// CHECK-DAG: ![[PACK1_ELEMS]] = !{![[PACK1_ARGS:[0-9]+]], ![[PACK1_PARAMS:[0-9]+]]}
@@ -24,6 +17,7 @@ auto capture_pack(Args... args) {
// CHECK-DAG: ![[PACK1_PARAMS]] = !DIDerivedType(tag: DW_TAG_member, name: "params"
// CHECK-NOT: DW_TAG_member
+// CHECK: ![[PACK2:[0-9]+]] = distinct !DISubprogram(name: "capture_pack<int, int>"
// CHECK: distinct !DICompositeType(tag: DW_TAG_class_type, scope: ![[PACK2]]
// CHECK-SAME: elements: ![[PACK2_ELEMS:[0-9]+]]
// CHECK: ![[PACK2_ELEMS]] = !{![[PACK2_ARGS:[0-9]+]]
@@ -42,6 +36,7 @@ auto capture_pack_and_locals(int x, Args... args) {
}();
}
+// CHECK: ![[PACK3:[0-9]+]] = distinct !DISubprogram(name: "capture_pack_and_locals<int>"
// CHECK: distinct !DICompositeType(tag: DW_TAG_class_type, scope: ![[PACK3]]
// CHECK-SAME: elements: ![[PACK3_ELEMS:[0-9]+]]
// CHECK: ![[PACK3_ELEMS]] = !{![[PACK3_ARGS:[0-9]+]]
@@ -55,6 +50,7 @@ auto capture_pack_and_locals(int x, Args... args) {
// CHECK-DAG: ![[PACK3_W]] = !DIDerivedType(tag: DW_TAG_member, name: "w"
// CHECK-NOT: DW_TAG_member
+// CHECK: ![[PACK4:[0-9]+]] = distinct !DISubprogram(name: "capture_pack_and_locals<int, int>"
// CHECK: distinct !DICompositeType(tag: DW_TAG_class_type, scope: ![[PACK4]]
// CHECK-SAME: elements: ![[PACK4_ELEMS:[0-9]+]]
// CHECK: ![[PACK4_ELEMS]] = !{![[PACK4_ARGS:[0-9]+]]
@@ -90,6 +86,7 @@ struct Foo {
int w = 10;
} f;
+// CHECK: ![[PACK5:[0-9]+]] = distinct !DISubprogram(name: "capture_pack_and_this<int>"
// CHECK: distinct !DICompositeType(tag: DW_TAG_class_type, scope: ![[PACK5]]
// CHECK-SAME: elements: ![[PACK5a_ELEMS:[0-9]+]]
// CHECK: ![[PACK5a_ELEMS]] = !{![[PACK5a_THIS:[0-9]+]]
@@ -120,6 +117,7 @@ struct Foo {
// CHECK-DAG: ![[PACK5c_THIS]] = !DIDerivedType(tag: DW_TAG_member, name: "this"
// CHECK-NOT: DW_TAG_member
+// CHECK: ![[PACK6:[0-9]+]] = distinct !DISubprogram(name: "capture_pack_and_this<int, int>"
// CHECK: distinct !DICompositeType(tag: DW_TAG_class_type, scope: ![[PACK6]]
// CHECK-SAME: elements: ![[PACK6a_ELEMS:[0-9]+]]
// CHECK: ![[PACK6a_ELEMS]] = !{![[PACK6a_THIS:[0-9]+]]
@@ -168,6 +166,7 @@ auto capture_binding_and_param_pack(Args... args) {
}();
}
+// CHECK: ![[PACK7:[0-9]+]] = distinct !DISubprogram(name: "capture_binding_and_param_pack<int, int>"
// CHECK: distinct !DICompositeType(tag: DW_TAG_structure_type, name: "C"
// CHECK: distinct !DICompositeType(tag: DW_TAG_class_type, scope: ![[PACK7]]
// CHECK-SAME: elements: ![[PACK7_ELEMS:[0-9]+]]
diff --git a/clang/test/DebugInfo/CXX/lambda-this.cpp b/clang/test/DebugInfo/CXX/lambda-this.cpp
index 019d09c..1df2109 100644
--- a/clang/test/DebugInfo/CXX/lambda-this.cpp
+++ b/clang/test/DebugInfo/CXX/lambda-this.cpp
@@ -13,10 +13,10 @@ void D::d(int x) {
}
// CHECK: ![[D:[0-9]+]] = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "D",
-// CHECK: ![[POINTER:.*]] = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: ![[D]], size: 64)
// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "this",
// CHECK-SAME: line: 11
-// CHECK-SAME: baseType: ![[POINTER]]
+// CHECK-SAME: baseType: ![[POINTER:[0-9]+]]
// CHECK-SAME: size: 64
// CHECK-NOT: offset: 0
// CHECK-SAME: ){{$}}
+// CHECK: ![[POINTER]] = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: ![[D]], size: 64)
diff --git a/clang/test/DebugInfo/Generic/codeview-unnamed.c b/clang/test/DebugInfo/Generic/codeview-unnamed.c
index 0df6e1a..7b88f53 100644
--- a/clang/test/DebugInfo/Generic/codeview-unnamed.c
+++ b/clang/test/DebugInfo/Generic/codeview-unnamed.c
@@ -8,23 +8,23 @@ int main(int argc, char* argv[], char* arge[]) {
//
struct { int bar; } one = {42};
//
- // LINUX: !{{[0-9]+}} = !DILocalVariable(name: "one"
- // LINUX-SAME: type: [[TYPE_OF_ONE:![0-9]+]]
- // LINUX-SAME: )
- // LINUX: [[TYPE_OF_ONE]] = distinct !DICompositeType(
+ // LINUX: [[TYPE_OF_ONE:![0-9]+]] = distinct !DICompositeType(
// LINUX-SAME: tag: DW_TAG_structure_type
// LINUX-NOT: name:
// LINUX-NOT: identifier:
// LINUX-SAME: )
+ // LINUX: !{{[0-9]+}} = !DILocalVariable(name: "one"
+ // LINUX-SAME: type: [[TYPE_OF_ONE]]
+ // LINUX-SAME: )
//
- // MSVC: !{{[0-9]+}} = !DILocalVariable(name: "one"
- // MSVC-SAME: type: [[TYPE_OF_ONE:![0-9]+]]
- // MSVC-SAME: )
- // MSVC: [[TYPE_OF_ONE]] = distinct !DICompositeType
+ // MSVC: [[TYPE_OF_ONE:![0-9]+]] = distinct !DICompositeType
// MSVC-SAME: tag: DW_TAG_structure_type
// MSVC-NOT: name:
// MSVC-NOT: identifier:
// MSVC-SAME: )
+ // MSVC: !{{[0-9]+}} = !DILocalVariable(name: "one"
+ // MSVC-SAME: type: [[TYPE_OF_ONE]]
+ // MSVC-SAME: )
return 0;
}
diff --git a/clang/test/DebugInfo/Generic/unused-types.c b/clang/test/DebugInfo/Generic/unused-types.c
index 3e9f7b0..31d608d 100644
--- a/clang/test/DebugInfo/Generic/unused-types.c
+++ b/clang/test/DebugInfo/Generic/unused-types.c
@@ -18,13 +18,15 @@ void quux(void) {
// CHECK: !DICompileUnit{{.+}}retainedTypes: [[RETTYPES:![0-9]+]]
// CHECK: [[TYPE0:![0-9]+]] = !DICompositeType(tag: DW_TAG_enumeration_type, name: "bar"
// CHECK: [[TYPE1:![0-9]+]] = !DIEnumerator(name: "BAR"
-// CHECK: [[TYPE2:![0-9]+]] = !DICompositeType(tag: DW_TAG_enumeration_type, name: "z"
-// CHECK: [[TYPE3:![0-9]+]] = !DIEnumerator(name: "Z"
-// CHECK: [[RETTYPES]] = !{[[TYPE4:![0-9]+]], [[TYPE5:![0-9]+]], [[TYPE0]], [[TYPE6:![0-9]+]], {{![0-9]+}}, [[TYPE7:![0-9]+]], [[TYPE2]], [[TYPE8:![0-9]+]]}
-// CHECK: [[TYPE4]] = !DIDerivedType(tag: DW_TAG_typedef, name: "my_int"
-// CHECK: [[TYPE5]] = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "foo"
-// CHECK: [[TYPE6]] = distinct !DICompositeType(tag: DW_TAG_union_type, name: "baz"
-// CHECK: [[TYPE7]] = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "y"
+// CHECK: [[RETTYPES]] = !{[[TYPE2:![0-9]+]], [[TYPE3:![0-9]+]], [[TYPE0]], [[TYPE4:![0-9]+]], {{![0-9]+}}}
+// CHECK: [[TYPE2]] = !DIDerivedType(tag: DW_TAG_typedef, name: "my_int"
+// CHECK: [[TYPE3]] = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "foo"
+// CHECK: [[TYPE4]] = distinct !DICompositeType(tag: DW_TAG_union_type, name: "baz"
+// CHECK: [[SP:![0-9]+]] = distinct !DISubprogram(name: "quux", {{.*}}, retainedNodes: [[SPRETNODES:![0-9]+]]
+// CHECK: [[SPRETNODES]] = !{[[TYPE5:![0-9]+]], [[TYPE6:![0-9]+]], [[TYPE8:![0-9]+]]}
+// CHECK: [[TYPE5]] = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "y"
+// CHECK: [[TYPE6]] = !DICompositeType(tag: DW_TAG_enumeration_type, name: "z"
+// CHECK: [[TYPE7:![0-9]+]] = !DIEnumerator(name: "Z"
// CHECK: [[TYPE8]] = distinct !DICompositeType(tag: DW_TAG_union_type, name: "w"
// Check that debug info is not emitted for the typedef, struct, enum, and
diff --git a/clang/test/DebugInfo/Generic/unused-types.cpp b/clang/test/DebugInfo/Generic/unused-types.cpp
index 023cac1..5b01c6d 100644
--- a/clang/test/DebugInfo/Generic/unused-types.cpp
+++ b/clang/test/DebugInfo/Generic/unused-types.cpp
@@ -13,12 +13,14 @@ void quux() {
// CHECK: !DICompileUnit{{.+}}retainedTypes: [[RETTYPES:![0-9]+]]
// CHECK: [[TYPE0:![0-9]+]] = !DICompositeType(tag: DW_TAG_enumeration_type, name: "baz"
// CHECK: [[TYPE1:![0-9]+]] = !DIEnumerator(name: "BAZ"
-// CHECK: [[TYPE2:![0-9]+]] = !DICompositeType(tag: DW_TAG_enumeration_type, name: "z"
-// CHECK: [[TYPE3:![0-9]+]] = !DIEnumerator(name: "Z"
-// CHECK: [[RETTYPES]] = !{[[TYPE4:![0-9]+]], [[TYPE5:![0-9]+]], [[TYPE0]], {{![0-9]+}}, [[TYPE6:![0-9]+]], [[TYPE2]]}
-// CHECK: [[TYPE4]] = !DIDerivedType(tag: DW_TAG_typedef, name: "foo"
-// CHECK: [[TYPE5]] = distinct !DICompositeType(tag: DW_TAG_class_type, name: "bar"
-// CHECK: [[TYPE6]] = distinct !DICompositeType(tag: DW_TAG_class_type, name: "y"
+// CHECK: [[RETTYPES]] = !{[[TYPE2:![0-9]+]], [[TYPE3:![0-9]+]], [[TYPE0]], {{![0-9]+}}}
+// CHECK: [[TYPE2]] = !DIDerivedType(tag: DW_TAG_typedef, name: "foo"
+// CHECK: [[TYPE3]] = distinct !DICompositeType(tag: DW_TAG_class_type, name: "bar"
+// CHECK: [[SP:![0-9]+]] = distinct !DISubprogram(name: "quux", {{.*}}, retainedNodes: [[SPRETNODES:![0-9]+]]
+// CHECK: [[SPRETNODES]] = !{[[TYPE4:![0-9]+]], [[TYPE5:![0-9]+]]}
+// CHECK: [[TYPE4]] = distinct !DICompositeType(tag: DW_TAG_class_type, name: "y", scope: [[SP]]
+// CHECK: [[TYPE5]] = !DICompositeType(tag: DW_TAG_enumeration_type, name: "z", scope: [[SP]]
+// CHECK: [[TYPE6:![0-9]+]] = !DIEnumerator(name: "Z"
// NODBG-NOT: !DI{{CompositeType|Enumerator|DerivedType}}
diff --git a/llvm/include/llvm/AsmParser/LLParser.h b/llvm/include/llvm/AsmParser/LLParser.h
index 9761cd3..040a215 100644
--- a/llvm/include/llvm/AsmParser/LLParser.h
+++ b/llvm/include/llvm/AsmParser/LLParser.h
@@ -181,6 +181,10 @@ namespace llvm {
/// Keeps track of source locations for Values, BasicBlocks, and Functions.
AsmParserContext *ParserContext;
+ /// retainedNodes of these subprograms should be cleaned up from incorrectly
+ /// scoped local types.
+ SmallVector<DISubprogram *> NewDistinctSPs;
+
/// Only the llvm-as tool may set this to false to bypass
/// UpgradeDebuginfo so it can generate broken bitcode.
bool UpgradeDebugInfo;
diff --git a/llvm/include/llvm/IR/DIBuilder.h b/llvm/include/llvm/IR/DIBuilder.h
index 9677c4b..9753c36 100644
--- a/llvm/include/llvm/IR/DIBuilder.h
+++ b/llvm/include/llvm/IR/DIBuilder.h
@@ -49,7 +49,7 @@ namespace llvm {
DICompileUnit *CUNode; ///< The one compile unit created by this DIBuiler.
- SmallVector<TrackingMDNodeRef, 4> AllEnumTypes;
+ SmallVector<TrackingMDNodeRef, 4> EnumTypes;
/// Track the RetainTypes, since they can be updated later on.
SmallVector<TrackingMDNodeRef, 4> AllRetainTypes;
SmallVector<DISubprogram *, 4> AllSubprograms;
@@ -64,8 +64,8 @@ namespace llvm {
SmallVector<TrackingMDNodeRef, 4> UnresolvedNodes;
bool AllowUnresolvedNodes;
- /// Each subprogram's preserved local variables, labels and imported
- /// entities.
+ /// Each subprogram's preserved local variables, labels, imported entities,
+ /// and types.
///
/// Do not use a std::vector. Some versions of libc++ apparently copy
/// instead of move on grow operations, and TrackingMDRef is expensive to
diff --git a/llvm/include/llvm/IR/DebugInfoMetadata.h b/llvm/include/llvm/IR/DebugInfoMetadata.h
index 220b6e2..3ce899d 100644
--- a/llvm/include/llvm/IR/DebugInfoMetadata.h
+++ b/llvm/include/llvm/IR/DebugInfoMetadata.h
@@ -2506,35 +2506,79 @@ public:
/// For the given retained node of DISubprogram, applies one of the
/// given functions depending on the type of the node.
- template <typename T, typename FuncLVT, typename FuncLabelT,
- typename FuncImportedEntityT, typename FuncUnknownT>
- static T
- visitRetainedNode(const Metadata *N, FuncLVT &&FuncLV, FuncLabelT &&FuncLabel,
- FuncImportedEntityT &&FuncIE, FuncUnknownT &&FuncUnknown) {
- if (const auto *LV = dyn_cast<DILocalVariable>(N))
+ template <typename T, typename MetadataT, typename FuncLVT,
+ typename FuncLabelT, typename FuncImportedEntityT,
+ typename FuncTypeT, typename FuncUnknownT>
+ static T visitRetainedNode(MetadataT *N, FuncLVT &&FuncLV,
+ FuncLabelT &&FuncLabel,
+ FuncImportedEntityT &&FuncIE, FuncTypeT &&FuncType,
+ FuncUnknownT &&FuncUnknown) {
+ static_assert(std::is_base_of_v<Metadata, MetadataT>,
+ "N must point to Metadata or const Metadata");
+
+ if (auto *LV = dyn_cast<DILocalVariable>(N))
return FuncLV(LV);
- if (const auto *L = dyn_cast<DILabel>(N))
+ if (auto *L = dyn_cast<DILabel>(N))
return FuncLabel(L);
- if (const auto *IE = dyn_cast<DIImportedEntity>(N))
+ if (auto *IE = dyn_cast<DIImportedEntity>(N))
return FuncIE(IE);
+ if (auto *Ty = dyn_cast<DIType>(N))
+ return FuncType(Ty);
return FuncUnknown(N);
}
/// Returns the scope of subprogram's retainedNodes.
static const DILocalScope *getRetainedNodeScope(const MDNode *N);
+ static DILocalScope *getRetainedNodeScope(MDNode *N);
// For use in Verifier.
static const DIScope *getRawRetainedNodeScope(const MDNode *N);
+ static DIScope *getRawRetainedNodeScope(MDNode *N);
/// For each retained node, applies one of the given functions depending
/// on the type of a node.
- template <typename FuncLVT, typename FuncLabelT, typename FuncImportedEntityT>
+ template <typename FuncLVT, typename FuncLabelT, typename FuncImportedEntityT,
+ typename FuncTypeT>
void forEachRetainedNode(FuncLVT &&FuncLV, FuncLabelT &&FuncLabel,
- FuncImportedEntityT &&FuncIE) const {
+ FuncImportedEntityT &&FuncIE, FuncTypeT &&FuncType) {
for (MDNode *N : getRetainedNodes())
- visitRetainedNode<void>(N, FuncLV, FuncLabel, FuncIE,
- [](const Metadata *N) {
- llvm_unreachable("Unexpected retained node!");
- });
+ visitRetainedNode<void>(
+ N, FuncLV, FuncLabel, FuncIE, FuncType,
+ [](auto *N) { llvm_unreachable("Unexpected retained node!"); });
+ }
+
+ /// When IR modules are merged, typically during LTO, the merged module
+ /// may contain several types having the same linkageName. They are
+ /// supposed to represent the same type included by multiple source code
+ /// files from a single header file.
+ ///
+ /// DebugTypeODRUniquing feature uniques (deduplicates) such types
+ /// based on their linkageName during metadata loading, to speed up
+ /// compilation and reduce debug info size.
+ ///
+ /// However, since function-local types are tracked in DISubprogram's
+ /// retainedNodes field, a single local type may be referenced by multiple
+ /// DISubprograms via retainedNodes as the result of DebugTypeODRUniquing.
+ /// But retainedNodes field of a DISubprogram is meant to hold only
+ /// subprogram's own local entities, therefore such references may
+ /// cause crashes.
+ ///
+ /// To address this problem, this method is called for each new subprogram
+ /// after module loading. It removes references to types belonging
+ /// to other DISubprograms from a subprogram's retainedNodes list.
+ /// If a corresponding IR function refers to local scopes from another
+ /// subprogram, emitted debug info (e.g. DWARF) should rely
+ /// on cross-subprogram references (and cross-CU references, as subprograms
+ /// may belong to different compile units). This is also a drawback:
+ /// when a subprogram refers to types that are local to another subprogram,
+ /// it is more complicated for debugger to properly discover local types
+ /// of a current scope for expression evaluation.
+ void cleanupRetainedNodes();
+
+ /// Calls SP->cleanupRetainedNodes() for a range of DISubprograms.
+ template <typename RangeT>
+ static void cleanupRetainedNodes(const RangeT &NewDistinctSPs) {
+ for (DISubprogram *SP : NewDistinctSPs)
+ SP->cleanupRetainedNodes();
}
/// Check if this subprogram describes the given function.
diff --git a/llvm/lib/AsmParser/LLParser.cpp b/llvm/lib/AsmParser/LLParser.cpp
index 5e61e05..a691caa 100644
--- a/llvm/lib/AsmParser/LLParser.cpp
+++ b/llvm/lib/AsmParser/LLParser.cpp
@@ -428,6 +428,9 @@ bool LLParser::validateEndOfModule(bool UpgradeDebugInfo) {
N.second->resolveCycles();
}
+ DISubprogram::cleanupRetainedNodes(NewDistinctSPs);
+ NewDistinctSPs.clear();
+
for (auto *Inst : InstsWithTBAATag) {
MDNode *MD = Inst->getMetadata(LLVMContext::MD_tbaa);
// With incomplete IR, the tbaa metadata may have been dropped.
@@ -6042,6 +6045,10 @@ bool LLParser::parseDISubprogram(MDNode *&Result, bool IsDistinct) {
thisAdjustment.Val, flags.Val, SPFlags, unit.Val, templateParams.Val,
declaration.Val, retainedNodes.Val, thrownTypes.Val, annotations.Val,
targetFuncName.Val, keyInstructions.Val));
+
+ if (IsDistinct)
+ NewDistinctSPs.push_back(cast<DISubprogram>(Result));
+
return false;
}
diff --git a/llvm/lib/Bitcode/Reader/MetadataLoader.cpp b/llvm/lib/Bitcode/Reader/MetadataLoader.cpp
index a12176d..4bc5523 100644
--- a/llvm/lib/Bitcode/Reader/MetadataLoader.cpp
+++ b/llvm/lib/Bitcode/Reader/MetadataLoader.cpp
@@ -116,6 +116,8 @@ public:
RefsUpperBound(std::min((size_t)std::numeric_limits<unsigned>::max(),
RefsUpperBound)) {}
+ using const_iterator = SmallVector<TrackingMDRef, 1>::const_iterator;
+
// vector compatibility methods
unsigned size() const { return MetadataPtrs.size(); }
void resize(unsigned N) { MetadataPtrs.resize(N); }
@@ -124,6 +126,8 @@ public:
Metadata *back() const { return MetadataPtrs.back(); }
void pop_back() { MetadataPtrs.pop_back(); }
bool empty() const { return MetadataPtrs.empty(); }
+ const_iterator begin() const { return MetadataPtrs.begin(); }
+ const_iterator end() const { return MetadataPtrs.end(); }
Metadata *operator[](unsigned i) const { return MetadataPtrs[i]; }
@@ -448,6 +452,11 @@ class MetadataLoader::MetadataLoaderImpl {
/// metadata.
SmallDenseMap<Function *, DISubprogram *, 16> FunctionsWithSPs;
+ /// retainedNodes of these subprograms should be cleaned up from incorrectly
+ /// scoped local types.
+ /// See \ref DISubprogram::cleanupRetainedNodes.
+ SmallVector<DISubprogram *> NewDistinctSPs;
+
// Map the bitcode's custom MDKind ID to the Module's MDKind ID.
DenseMap<unsigned, unsigned> MDKindMap;
@@ -538,56 +547,82 @@ class MetadataLoader::MetadataLoaderImpl {
/// Move local imports from DICompileUnit's 'imports' field to
/// DISubprogram's retainedNodes.
+ /// Move function-local enums from DICompileUnit's enums
+ /// to DISubprogram's retainedNodes.
void upgradeCULocals() {
- if (NamedMDNode *CUNodes = TheModule.getNamedMetadata("llvm.dbg.cu")) {
- for (MDNode *N : CUNodes->operands()) {
- auto *CU = dyn_cast<DICompileUnit>(N);
- if (!CU)
- continue;
-
- if (CU->getRawImportedEntities()) {
- // Collect a set of imported entities to be moved.
- SetVector<Metadata *> EntitiesToRemove;
- for (Metadata *Op : CU->getImportedEntities()->operands()) {
- auto *IE = cast<DIImportedEntity>(Op);
- if (isa_and_nonnull<DILocalScope>(IE->getScope())) {
- EntitiesToRemove.insert(IE);
- }
- }
-
- 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))) {
- NewImports.push_back(Op);
- }
- }
+ NamedMDNode *CUNodes = TheModule.getNamedMetadata("llvm.dbg.cu");
+ if (!CUNodes)
+ return;
- // 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);
- }
- }
+ // Filter out elements of ToRemove from tuple T.
+ auto FilterTuple = [this](MDNode *T,
+ const SetVector<Metadata *> &ToRemove) {
+ SmallVector<Metadata *> Result;
+ for (Metadata *Op : T->operands())
+ if (!ToRemove.contains(Op))
+ Result.push_back(Op);
+ return MDTuple::get(Context, Result);
+ };
- // 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));
- }
+ // For each CU:
+ // - Collect local metadata nodes from CU's imports: and enums: lists in
+ // MetadataToRemove set.
+ // - Remove metadata nodes of MetadataToRemove set from CU's imports: and
+ // enums: lists.
+ // - Group MetadataToRemove items by their parent subprograms (in
+ // SPToEntities map).
+ // - For each subprogram SP in SPToEntities:
+ // - Append collected local metadata nodes to SP's retainedNodes: list.
+ for (MDNode *N : CUNodes->operands()) {
+ auto *CU = dyn_cast<DICompileUnit>(N);
+ if (!CU)
+ continue;
- // Remove entities with local scope from CU.
- CU->replaceImportedEntities(MDTuple::get(Context, NewImports));
- }
+ SetVector<Metadata *> MetadataToRemove;
+ // Collect imported entities to be moved.
+ if (CU->getRawImportedEntities())
+ for (Metadata *Op : CU->getImportedEntities()->operands()) {
+ auto *IE = cast<DIImportedEntity>(Op);
+ if (isa_and_nonnull<DILocalScope>(IE->getScope()))
+ MetadataToRemove.insert(IE);
+ }
+ // Collect enums to be moved.
+ if (CU->getRawEnumTypes())
+ for (Metadata *Op : CU->getEnumTypes()->operands()) {
+ auto *Enum = cast<DICompositeType>(Op);
+ if (isa_and_nonnull<DILocalScope>(Enum->getScope()))
+ MetadataToRemove.insert(Enum);
}
+
+ if (MetadataToRemove.empty())
+ continue;
+
+ // Remove entities with local scope from CU.
+ if (CU->getRawImportedEntities())
+ CU->replaceImportedEntities(
+ FilterTuple(CU->getImportedEntities().get(), MetadataToRemove));
+
+ // Remove enums with local scope from CU.
+ if (CU->getRawEnumTypes())
+ CU->replaceEnumTypes(
+ FilterTuple(CU->getEnumTypes().get(), MetadataToRemove));
+
+ // Find DISubprogram corresponding to each entity.
+ SmallDenseMap<DISubprogram *, SmallVector<Metadata *>> SPToEntities;
+ for (auto *I : MetadataToRemove) {
+ DILocalScope *Scope =
+ DISubprogram::getRetainedNodeScope(cast<DINode>(I));
+ 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));
}
}
@@ -703,13 +738,40 @@ class MetadataLoader::MetadataLoaderImpl {
return Error::success();
}
- void upgradeDebugInfo(bool ModuleLevel) {
+ /// Specifies which kind of debug info upgrade should be performed.
+ ///
+ /// The upgrade of compile units' enums: and imports: fields is performed
+ /// only when module level metadata block is loaded (i.e. all elements of
+ /// "llvm.dbg.cu" named metadata node are loaded).
+ enum class DebugInfoUpgradeMode {
+ /// No debug info upgrade.
+ None,
+ /// Debug info upgrade after loading function-level metadata block.
+ Partial,
+ /// Debug info upgrade after loading module-level metadata block.
+ ModuleLevel,
+ };
+
+ void upgradeDebugInfo(DebugInfoUpgradeMode Mode) {
+ if (Mode == DebugInfoUpgradeMode::None)
+ return;
upgradeCUSubprograms();
upgradeCUVariables();
- if (ModuleLevel)
+ if (Mode == DebugInfoUpgradeMode::ModuleLevel)
upgradeCULocals();
}
+ /// Prepare loaded metadata nodes to be used by loader clients.
+ void resolveLoadedMetadata(PlaceholderQueue &Placeholders,
+ DebugInfoUpgradeMode DIUpgradeMode) {
+ resolveForwardRefsAndPlaceholders(Placeholders);
+ upgradeDebugInfo(DIUpgradeMode);
+ DISubprogram::cleanupRetainedNodes(NewDistinctSPs);
+ LLVM_DEBUG(llvm::dbgs() << "Resolved loaded metadata. Cleaned up "
+ << NewDistinctSPs.size() << " subprogram(s).\n");
+ NewDistinctSPs.clear();
+ }
+
void callMDTypeCallback(Metadata **Val, unsigned TypeID);
public:
@@ -735,7 +797,8 @@ public:
if (ID < (MDStringRef.size() + GlobalMetadataBitPosIndex.size())) {
PlaceholderQueue Placeholders;
lazyLoadOneMetadata(ID, Placeholders);
- resolveForwardRefsAndPlaceholders(Placeholders);
+ LLVM_DEBUG(llvm::dbgs() << "\nLazy metadata loading: ");
+ resolveLoadedMetadata(Placeholders, DebugInfoUpgradeMode::None);
return MetadataList.lookup(ID);
}
return MetadataList.getMetadataFwdRef(ID);
@@ -1060,6 +1123,8 @@ Error MetadataLoader::MetadataLoaderImpl::parseMetadata(bool ModuleLevel) {
SmallVector<uint64_t, 64> Record;
PlaceholderQueue Placeholders;
+ auto DIUpgradeMode = ModuleLevel ? DebugInfoUpgradeMode::ModuleLevel
+ : DebugInfoUpgradeMode::Partial;
// We lazy-load module-level metadata: we build an index for each record, and
// then load individual record as needed, starting with the named metadata.
@@ -1084,8 +1149,8 @@ Error MetadataLoader::MetadataLoaderImpl::parseMetadata(bool ModuleLevel) {
// Reading the named metadata created forward references and/or
// placeholders, that we flush here.
- resolveForwardRefsAndPlaceholders(Placeholders);
- upgradeDebugInfo(ModuleLevel);
+ LLVM_DEBUG(llvm::dbgs() << "\nNamed metadata loading: ");
+ resolveLoadedMetadata(Placeholders, DIUpgradeMode);
// Return at the beginning of the block, since it is easy to skip it
// entirely from there.
Stream.ReadBlockEnd(); // Pop the abbrev block context.
@@ -1115,8 +1180,8 @@ Error MetadataLoader::MetadataLoaderImpl::parseMetadata(bool ModuleLevel) {
case BitstreamEntry::Error:
return error("Malformed block");
case BitstreamEntry::EndBlock:
- resolveForwardRefsAndPlaceholders(Placeholders);
- upgradeDebugInfo(ModuleLevel);
+ LLVM_DEBUG(llvm::dbgs() << "\nEager metadata loading: ");
+ resolveLoadedMetadata(Placeholders, DIUpgradeMode);
return Error::success();
case BitstreamEntry::Record:
// The interesting case.
@@ -1999,6 +2064,9 @@ Error MetadataLoader::MetadataLoaderImpl::parseOneMetadata(
MetadataList.assignValue(SP, NextMetadataNo);
NextMetadataNo++;
+ if (IsDistinct)
+ NewDistinctSPs.push_back(SP);
+
// Upgrade sp->function mapping to function->sp mapping.
if (HasFn) {
if (auto *CMD = dyn_cast_or_null<ConstantAsMetadata>(CUorFn))
@@ -2482,7 +2550,8 @@ Error MetadataLoader::MetadataLoaderImpl::parseMetadataAttachment(
case BitstreamEntry::Error:
return error("Malformed block");
case BitstreamEntry::EndBlock:
- resolveForwardRefsAndPlaceholders(Placeholders);
+ LLVM_DEBUG(llvm::dbgs() << "\nAttachment metadata loading: ");
+ resolveLoadedMetadata(Placeholders, DebugInfoUpgradeMode::None);
return Error::success();
case BitstreamEntry::Record:
// The interesting case.
@@ -2525,7 +2594,8 @@ Error MetadataLoader::MetadataLoaderImpl::parseMetadataAttachment(
// Load the attachment if it is in the lazy-loadable range and hasn't
// been loaded yet.
lazyLoadOneMetadata(Idx, Placeholders);
- resolveForwardRefsAndPlaceholders(Placeholders);
+ LLVM_DEBUG(llvm::dbgs() << "\nLazy attachment metadata loading: ");
+ resolveLoadedMetadata(Placeholders, DebugInfoUpgradeMode::None);
}
Metadata *Node = MetadataList.getMetadataFwdRef(Idx);
diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp
index b815386..e87d3f3 100644
--- a/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp
@@ -644,10 +644,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);
}
@@ -768,14 +767,15 @@ 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);
+ ParentScopeDIE.addChild(ScopeDIE);
+
if (Scope->isAbstractScope()) {
assert(!getAbstractScopeDIEs().count(DS) &&
"Abstract DIE for this scope exists!");
@@ -1839,7 +1839,7 @@ void DwarfCompileUnit::createBaseTypeDIEs() {
}
}
-DIE *DwarfCompileUnit::getLexicalBlockDIE(const DILexicalBlock *LB) {
+DIE *DwarfCompileUnit::getLocalContextDIE(const DILexicalBlock *LB) {
// Assume if there is an abstract tree all the DIEs are already emitted.
bool isAbstract = getAbstractScopeDIEs().count(LB->getSubprogram());
if (isAbstract) {
@@ -1849,8 +1849,14 @@ DIE *DwarfCompileUnit::getLexicalBlockDIE(const DILexicalBlock *LB) {
}
assert(!isAbstract && "Missed lexical block DIE in abstract tree!");
- // Return a concrete DIE if it exists or nullptr otherwise.
- return LexicalBlockDIEs.lookup(LB);
+ // Check if we have a concrete DIE.
+ if (auto It = LexicalBlockDIEs.find(LB); It != LexicalBlockDIEs.end())
+ return It->second;
+
+ // If nothing available found, we cannot just create a new lexical block,
+ // because it isn't known where to put it into the DIE tree.
+ // So, we may only try to find the most close avaiable parent DIE.
+ return getOrCreateContextDIE(LB->getScope()->getNonLexicalBlockFileScope());
}
DIE *DwarfCompileUnit::getOrCreateContextDIE(const DIScope *Context) {
@@ -1858,7 +1864,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 a90fec0..04d4556 100644
--- a/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.h
+++ b/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.h
@@ -257,14 +257,9 @@ public:
/// 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);
@@ -283,6 +278,11 @@ public:
/// This instance of 'getOrCreateContextDIE()' can handle DILocalScope.
DIE *getOrCreateContextDIE(const DIScope *Ty) override;
+ /// Get DW_TAG_lexical_block for the given DILexicalBlock if available,
+ /// or the most close parent DIE, if no correspoding DW_TAG_lexical_block
+ /// exists.
+ DIE *getLocalContextDIE(const DILexicalBlock *LB);
+
DIE *getOrCreateSubprogramDIE(const DISubprogram *SP, const Function *F,
bool Minimal = false) override;
diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
index ead5253..f8c2c75 100644
--- a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
@@ -1289,12 +1289,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);
@@ -1495,9 +1496,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!");
}
diff --git a/llvm/lib/IR/DIBuilder.cpp b/llvm/lib/IR/DIBuilder.cpp
index 05c9d58b..ec6fd8e 100644
--- a/llvm/lib/IR/DIBuilder.cpp
+++ b/llvm/lib/IR/DIBuilder.cpp
@@ -29,7 +29,7 @@ DIBuilder::DIBuilder(Module &m, bool AllowUnresolvedNodes, DICompileUnit *CU)
AllowUnresolvedNodes(AllowUnresolvedNodes) {
if (CUNode) {
if (const auto &ETs = CUNode->getEnumTypes())
- AllEnumTypes.assign(ETs.begin(), ETs.end());
+ EnumTypes.assign(ETs.begin(), ETs.end());
if (const auto &RTs = CUNode->getRetainedTypes())
AllRetainTypes.assign(RTs.begin(), RTs.end());
if (const auto &GVs = CUNode->getGlobalVariables())
@@ -66,10 +66,10 @@ void DIBuilder::finalize() {
return;
}
- if (!AllEnumTypes.empty())
- CUNode->replaceEnumTypes(MDTuple::get(
- VMContext, SmallVector<Metadata *, 16>(AllEnumTypes.begin(),
- AllEnumTypes.end())));
+ if (!EnumTypes.empty())
+ CUNode->replaceEnumTypes(
+ MDTuple::get(VMContext, SmallVector<Metadata *, 16>(EnumTypes.begin(),
+ EnumTypes.end())));
SmallVector<Metadata *, 16> RetainValues;
// Declarations and definitions of the same type may be retained. Some
@@ -379,10 +379,13 @@ DIDerivedType *DIBuilder::createTypedef(DIType *Ty, StringRef Name,
DIScope *Context, uint32_t AlignInBits,
DINode::DIFlags Flags,
DINodeArray Annotations) {
- return DIDerivedType::get(VMContext, dwarf::DW_TAG_typedef, Name, File,
- LineNo, getNonCompileUnitScope(Context), Ty,
- (uint64_t)0, AlignInBits, (uint64_t)0, std::nullopt,
- std::nullopt, Flags, nullptr, Annotations);
+ auto *T = DIDerivedType::get(
+ VMContext, dwarf::DW_TAG_typedef, Name, File, LineNo,
+ getNonCompileUnitScope(Context), Ty, (uint64_t)0, AlignInBits,
+ (uint64_t)0, std::nullopt, std::nullopt, Flags, nullptr, Annotations);
+ if (isa_and_nonnull<DILocalScope>(Context))
+ getSubprogramNodesTrackingVector(Context).emplace_back(T);
+ return T;
}
DIDerivedType *
@@ -390,10 +393,14 @@ DIBuilder::createTemplateAlias(DIType *Ty, StringRef Name, DIFile *File,
unsigned LineNo, DIScope *Context,
DINodeArray TParams, uint32_t AlignInBits,
DINode::DIFlags Flags, DINodeArray Annotations) {
- return DIDerivedType::get(VMContext, dwarf::DW_TAG_template_alias, Name, File,
- LineNo, getNonCompileUnitScope(Context), Ty,
- (uint64_t)0, AlignInBits, (uint64_t)0, std::nullopt,
- std::nullopt, Flags, TParams.get(), Annotations);
+ auto *T =
+ DIDerivedType::get(VMContext, dwarf::DW_TAG_template_alias, Name, File,
+ LineNo, getNonCompileUnitScope(Context), Ty,
+ (uint64_t)0, AlignInBits, (uint64_t)0, std::nullopt,
+ std::nullopt, Flags, TParams.get(), Annotations);
+ if (isa_and_nonnull<DILocalScope>(Context))
+ getSubprogramNodesTrackingVector(Context).emplace_back(T);
+ return T;
}
DIDerivedType *DIBuilder::createFriend(DIType *Ty, DIType *FriendTy) {
@@ -584,6 +591,8 @@ DICompositeType *DIBuilder::createClassType(
OffsetInBits, Flags, Elements, RunTimeLang, /*EnumKind=*/std::nullopt,
VTableHolder, cast_or_null<MDTuple>(TemplateParams), UniqueIdentifier);
trackIfUnresolved(R);
+ if (isa_and_nonnull<DILocalScope>(Context))
+ getSubprogramNodesTrackingVector(Context).emplace_back(R);
return R;
}
@@ -600,6 +609,8 @@ DICompositeType *DIBuilder::createStructType(
nullptr, UniqueIdentifier, nullptr, nullptr, nullptr, nullptr, nullptr,
nullptr, Specification, NumExtraInhabitants);
trackIfUnresolved(R);
+ if (isa_and_nonnull<DILocalScope>(Context))
+ getSubprogramNodesTrackingVector(Context).emplace_back(R);
return R;
}
@@ -616,6 +627,8 @@ DICompositeType *DIBuilder::createStructType(
nullptr, UniqueIdentifier, nullptr, nullptr, nullptr, nullptr, nullptr,
nullptr, Specification, NumExtraInhabitants);
trackIfUnresolved(R);
+ if (isa_and_nonnull<DILocalScope>(Context))
+ getSubprogramNodesTrackingVector(Context).emplace_back(R);
return R;
}
@@ -629,6 +642,8 @@ DICompositeType *DIBuilder::createUnionType(
Elements, RunTimeLang, /*EnumKind=*/std::nullopt, nullptr, nullptr,
UniqueIdentifier);
trackIfUnresolved(R);
+ if (isa_and_nonnull<DILocalScope>(Scope))
+ getSubprogramNodesTrackingVector(Scope).emplace_back(R);
return R;
}
@@ -663,7 +678,10 @@ DICompositeType *DIBuilder::createEnumerationType(
getNonCompileUnitScope(Scope), UnderlyingType, SizeInBits, AlignInBits, 0,
IsScoped ? DINode::FlagEnumClass : DINode::FlagZero, Elements,
RunTimeLang, EnumKind, nullptr, nullptr, UniqueIdentifier);
- AllEnumTypes.emplace_back(CTy);
+ if (isa_and_nonnull<DILocalScope>(Scope))
+ getSubprogramNodesTrackingVector(Scope).emplace_back(CTy);
+ else
+ EnumTypes.emplace_back(CTy);
trackIfUnresolved(CTy);
return CTy;
}
@@ -677,6 +695,8 @@ DIDerivedType *DIBuilder::createSetType(DIScope *Scope, StringRef Name,
SizeInBits, AlignInBits, 0, std::nullopt,
std::nullopt, DINode::FlagZero);
trackIfUnresolved(R);
+ if (isa_and_nonnull<DILocalScope>(Scope))
+ getSubprogramNodesTrackingVector(Scope).emplace_back(R);
return R;
}
@@ -712,6 +732,8 @@ DICompositeType *DIBuilder::createArrayType(
: (Metadata *)cast<DIVariable *>(RK),
nullptr, nullptr, 0, BitStride);
trackIfUnresolved(R);
+ if (isa_and_nonnull<DILocalScope>(Scope))
+ getSubprogramNodesTrackingVector(Scope).emplace_back(R);
return R;
}
@@ -770,7 +792,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; }
@@ -786,6 +809,8 @@ DICompositeType *DIBuilder::createForwardDecl(
SizeInBits, AlignInBits, 0, DINode::FlagFwdDecl, nullptr, RuntimeLang,
/*EnumKind=*/EnumKind, nullptr, nullptr, UniqueIdentifier);
trackIfUnresolved(RetTy);
+ if (isa_and_nonnull<DILocalScope>(Scope))
+ getSubprogramNodesTrackingVector(Scope).emplace_back(RetTy);
return RetTy;
}
@@ -802,6 +827,8 @@ DICompositeType *DIBuilder::createReplaceableCompositeType(
nullptr, nullptr, Annotations)
.release();
trackIfUnresolved(RetTy);
+ if (isa_and_nonnull<DILocalScope>(Scope))
+ getSubprogramNodesTrackingVector(Scope).emplace_back(RetTy);
return RetTy;
}
@@ -861,9 +888,12 @@ DISubrangeType *DIBuilder::createSubrangeType(
uint64_t SizeInBits, uint32_t AlignInBits, DINode::DIFlags Flags,
DIType *Ty, Metadata *LowerBound, Metadata *UpperBound, Metadata *Stride,
Metadata *Bias) {
- return DISubrangeType::get(VMContext, Name, File, LineNo, Scope, SizeInBits,
- AlignInBits, Flags, Ty, LowerBound, UpperBound,
- Stride, Bias);
+ auto *T = DISubrangeType::get(VMContext, Name, File, LineNo, Scope,
+ SizeInBits, AlignInBits, Flags, Ty, LowerBound,
+ UpperBound, Stride, Bias);
+ if (isa_and_nonnull<DILocalScope>(Scope))
+ getSubprogramNodesTrackingVector(Scope).emplace_back(T);
+ return T;
}
static void checkGlobalVariableScope(DIScope *Context) {
diff --git a/llvm/lib/IR/DebugInfo.cpp b/llvm/lib/IR/DebugInfo.cpp
index 1859bc4..a357621 100644
--- a/llvm/lib/IR/DebugInfo.cpp
+++ b/llvm/lib/IR/DebugInfo.cpp
@@ -324,9 +324,9 @@ void DebugInfoFinder::processSubprogram(DISubprogram *SP) {
}
SP->forEachRetainedNode(
- [this](const DILocalVariable *LV) { processVariable(LV); },
- [](const DILabel *L) {},
- [this](const DIImportedEntity *IE) { processImportedEntity(IE); });
+ [this](DILocalVariable *LV) { processVariable(LV); }, [](DILabel *L) {},
+ [this](DIImportedEntity *IE) { processImportedEntity(IE); },
+ [this](DIType *T) { processType(T); });
}
void DebugInfoFinder::processVariable(const DILocalVariable *DV) {
diff --git a/llvm/lib/IR/DebugInfoMetadata.cpp b/llvm/lib/IR/DebugInfoMetadata.cpp
index 79b512d..5db2c86 100644
--- a/llvm/lib/IR/DebugInfoMetadata.cpp
+++ b/llvm/lib/IR/DebugInfoMetadata.cpp
@@ -1445,18 +1445,69 @@ bool DISubprogram::describes(const Function *F) const {
return F->getSubprogram() == this;
}
+template <typename ScopeT, typename NodeT>
+static ScopeT getRawRetainedNodeScopeInternal(NodeT *N) {
+ auto getScope = [](auto *N) { return N->getScope(); };
+
+ return DISubprogram::visitRetainedNode<ScopeT>(
+ N, getScope, getScope, getScope, getScope,
+ [](auto *N) { return nullptr; });
+}
+
const DIScope *DISubprogram::getRawRetainedNodeScope(const MDNode *N) {
- return visitRetainedNode<DIScope *>(
- N, [](const DILocalVariable *LV) { return LV->getScope(); },
- [](const DILabel *L) { return L->getScope(); },
- [](const DIImportedEntity *IE) { return IE->getScope(); },
- [](const Metadata *N) { return nullptr; });
+ return getRawRetainedNodeScopeInternal<const DIScope *>(N);
+}
+
+DIScope *DISubprogram::getRawRetainedNodeScope(MDNode *N) {
+ return getRawRetainedNodeScopeInternal<DIScope *>(N);
}
const DILocalScope *DISubprogram::getRetainedNodeScope(const MDNode *N) {
return cast<DILocalScope>(getRawRetainedNodeScope(N));
}
+DILocalScope *DISubprogram::getRetainedNodeScope(MDNode *N) {
+ return cast<DILocalScope>(getRawRetainedNodeScope(N));
+}
+
+void DISubprogram::cleanupRetainedNodes() {
+ // Checks if a metadata node from retainedTypes is a type not belonging to
+ // this subprogram.
+ auto IsAlienType = [this](DINode *N) {
+ auto *T = dyn_cast_or_null<DIType>(N);
+ if (!T)
+ return false;
+
+ DISubprogram *TypeSP = nullptr;
+ // The type might have been global in the previously loaded IR modules.
+ if (auto *LS = dyn_cast_or_null<DILocalScope>(T->getScope()))
+ TypeSP = LS->getSubprogram();
+
+ return this != TypeSP;
+ };
+
+ // As this is expected to be called during module loading, before
+ // stripping old or incorrect debug info, perform minimal sanity check.
+ if (!isa_and_present<MDTuple>(getRawRetainedNodes()))
+ return;
+
+ MDTuple *RetainedNodes = cast<MDTuple>(getRawRetainedNodes());
+ SmallVector<Metadata *> MDs;
+ MDs.reserve(RetainedNodes->getNumOperands());
+ for (const MDOperand &Node : RetainedNodes->operands()) {
+ // Ignore malformed retainedNodes.
+ if (Node && !isa<DINode>(Node))
+ return;
+
+ auto *N = cast_or_null<DINode>(Node);
+ if (!IsAlienType(N))
+ MDs.push_back(N);
+ }
+
+ if (MDs.size() != RetainedNodes->getNumOperands())
+ replaceRetainedNodes(MDNode::get(getContext(), MDs));
+}
+
DILexicalBlockBase::DILexicalBlockBase(LLVMContext &C, unsigned ID,
StorageType Storage,
ArrayRef<Metadata *> Ops)
diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp
index 5465ca4..01d32d4 100644
--- a/llvm/lib/IR/Verifier.cpp
+++ b/llvm/lib/IR/Verifier.cpp
@@ -1623,11 +1623,11 @@ void Verifier::visitDISubprogram(const DISubprogram &N) {
auto True = [](const Metadata *) { return true; };
auto False = [](const Metadata *) { return false; };
- bool IsTypeCorrect =
- DISubprogram::visitRetainedNode<bool>(Op, True, True, True, False);
+ bool IsTypeCorrect = DISubprogram::visitRetainedNode<bool>(
+ Op, True, True, True, True, False);
CheckDI(IsTypeCorrect,
- "invalid retained nodes, expected DILocalVariable, DILabel or "
- "DIImportedEntity",
+ "invalid retained nodes, expected DILocalVariable, DILabel, "
+ "DIImportedEntity or DIType",
&N, Node, Op);
auto *RetainedNode = cast<DINode>(Op);
@@ -1636,10 +1636,15 @@ void Verifier::visitDISubprogram(const DISubprogram &N) {
CheckDI(RetainedNodeScope,
"invalid retained nodes, retained node is not local", &N, Node,
RetainedNode);
+
+ DISubprogram *RetainedNodeSP = RetainedNodeScope->getSubprogram();
+ DICompileUnit *RetainedNodeUnit =
+ RetainedNodeSP ? RetainedNodeSP->getUnit() : nullptr;
CheckDI(
- RetainedNodeScope->getSubprogram() == &N,
+ RetainedNodeSP == &N,
"invalid retained nodes, retained node does not belong to subprogram",
- &N, Node, RetainedNode, RetainedNodeScope);
+ &N, Node, RetainedNode, RetainedNodeScope, RetainedNodeSP,
+ RetainedNodeUnit);
}
}
CheckDI(!hasConflictingReferenceFlags(N.getFlags()),
diff --git a/llvm/lib/Transforms/Utils/CloneFunction.cpp b/llvm/lib/Transforms/Utils/CloneFunction.cpp
index 260dc3a..192d313 100644
--- a/llvm/lib/Transforms/Utils/CloneFunction.cpp
+++ b/llvm/lib/Transforms/Utils/CloneFunction.cpp
@@ -88,8 +88,8 @@ createIdentityMDPredicate(const Function &F, CloneFunctionChangeType Changes) {
};
return [=](const Metadata *MD) {
- // Avoid cloning types, compile units, and (other) subprograms.
- if (isa<DICompileUnit>(MD) || isa<DIType>(MD))
+ // Avoid cloning compile units.
+ if (isa<DICompileUnit>(MD))
return true;
if (auto *SP = dyn_cast<DISubprogram>(MD))
@@ -104,6 +104,29 @@ createIdentityMDPredicate(const Function &F, CloneFunctionChangeType Changes) {
if (auto *S = dyn_cast_or_null<DILocalScope>(DV->getScope()))
return ShouldKeep(S->getSubprogram());
+ // Clone types that are local to subprograms being cloned.
+ // Avoid cloning other types.
+ auto *Type = dyn_cast<DIType>(MD);
+ if (!Type)
+ return false;
+
+ // No need to clone types if subprograms are not cloned.
+ if (SPClonedWithinModule == nullptr)
+ return true;
+
+ // Scopeless types may be derived from local types (e.g. pointers to local
+ // types). They may need cloning.
+ if (const DIDerivedType *DTy = dyn_cast_or_null<DIDerivedType>(Type);
+ DTy && !DTy->getScope())
+ return false;
+
+ auto *LScope = dyn_cast_or_null<DILocalScope>(Type->getScope());
+ if (!LScope)
+ return true;
+
+ if (ShouldKeep(LScope->getSubprogram()))
+ return true;
+
return false;
};
}
diff --git a/llvm/test/Bitcode/upgrade-cu-locals.ll b/llvm/test/Bitcode/upgrade-cu-locals.ll
index 9a590f0..da175f5 100644
--- a/llvm/test/Bitcode/upgrade-cu-locals.ll
+++ b/llvm/test/Bitcode/upgrade-cu-locals.ll
@@ -1,7 +1,7 @@
-; Test moving of local imports from DICompileUnit's 'imports' to DISubprogram's 'retainedNodes'
-;
; RUN: llvm-dis -o - %s.bc | FileCheck %s
+; Test moving of local imports/enums from DICompileUnit to DISubprogram's 'retainedNodes'
+
%"struct.ns::t1" = type { i8 }
declare void @llvm.dbg.declare(metadata, metadata, metadata) #0
@@ -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 9d0ea8f..f73d056 100644
--- a/llvm/test/Bitcode/upgrade-cu-locals.ll.bc
+++ b/llvm/test/Bitcode/upgrade-cu-locals.ll.bc
Binary files differ
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 0000000..67e2c46
--- /dev/null
+++ b/llvm/test/DebugInfo/Generic/inlined-local-type.ll
@@ -0,0 +1,127 @@
+; RUN: %llc_dwarf -O0 -filetype=obj < %s | llvm-dwarfdump -debug-info - | FileCheck --implicit-check-not "{{DW_TAG|NULL}}" %s
+
+; 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 abstract 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 0000000..091c9d9
--- /dev/null
+++ b/llvm/test/DebugInfo/Generic/lexical-block-retained-types.ll
@@ -0,0 +1,72 @@
+; 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 {};
+; }
+; {
+; struct Z {};
+; test_external();
+; }
+; }
+
+; 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")
+; The LexicalScope for parent scope of structure 'Z' is created (thanks to test_external
+; call), therefore, DW_TAG_structure_type for structure 'Z' is placed inside
+; the DW_TAG_lexical_block.
+; CHECK: DW_TAG_lexical_block
+; CHECK: DW_TAG_structure_type
+; CHECK: DW_AT_name ("Z")
+; CHECK: NULL
+; CHECK: NULL
+; CHECK: NULL
+
+declare void @_Z13test_externalv()
+
+define dso_local void @_Z11test_unusedv() !dbg !5 {
+entry:
+ call void @_Z13test_externalv(), !dbg !19
+ 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, !18}
+!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: 10, column: 1, scope: !5)
+!17 = distinct !DILexicalBlock(scope: !5, file: !4, line: 6, column: 3)
+!18 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "Z", scope: !17, file: !4, line: 7, size: 8, flags: DIFlagTypePassByValue, elements: !8)
+!19 = !DILocation(line: 8, column: 1, scope: !17)
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 0000000..bb8371a
--- /dev/null
+++ b/llvm/test/DebugInfo/Generic/lexical-block-types.ll
@@ -0,0 +1,424 @@
+; RUN: %llc_dwarf -O0 -filetype=obj < %s | llvm-dwarfdump -debug-info - | FileCheck --implicit-check-not "{{DW_TAG|NULL}}" %s
+
+; inline __attribute__((always_inline))
+; void removed() {
+; struct A1 { int i; };
+; typedef int Int1;
+; {
+; struct I1 { Int1 j; };
+; struct C1 { typedef char Char1; Char1 c; };
+; A1 a1; a1.i++;
+; {
+; I1 i1; i1.j++;
+; C1 c1; c1.c++;
+; }
+; }
+; }
+;
+; __attribute__((always_inline))
+; void not_removed() {
+; struct A2 { int i; };
+; typedef int Int2;
+; {
+; struct I2 { Int2 j; };
+; struct C2 { typedef char Char2; Char2 c; };
+; A2 a2; a2.i++;
+; {
+; I2 i2; i2.j++;
+; C2 c2; c2.c++;
+; }
+; }
+; }
+;
+; void foo() {
+; struct A3 { int i; };
+; typedef int Int3;
+; {
+; struct I3 { Int3 j; };
+; {
+; struct C3 { typedef char Char3; Char3 c; };
+; A3 a3; a3.i++;
+; {
+; I3 i3; i3.j++;
+; C3 c3; c3.c++;
+; }
+; }
+; }
+; removed();
+; not_removed();
+; }
+;
+; CHECK: DW_TAG_compile_unit
+
+; Out-of-line definition of `not_removed()` shouldn't contain any debug info for types.
+; CHECK: DW_TAG_subprogram
+; CHECK: DW_AT_abstract_origin {{.*}} "_Z11not_removedv"
+; CHECK: DW_TAG_lexical_block
+; CHECK: DW_TAG_variable
+; CHECK: DW_AT_abstract_origin {{.*}} "a2"
+; CHECK: DW_TAG_lexical_block
+; CHECK: DW_TAG_variable
+; CHECK: DW_AT_abstract_origin {{.*}} "i2"
+; CHECK: DW_TAG_variable
+; CHECK: DW_AT_abstract_origin {{.*}} "c2"
+; CHECK: NULL
+; CHECK: NULL
+; CHECK: NULL
+
+; Abstract definition of `removed()`.
+; CHECK: DW_TAG_subprogram
+; CHECK: DW_AT_name ("removed")
+; CHECK: DW_AT_inline (DW_INL_inlined)
+
+; I1 and C1 defined in the first lexical block, typedef Char1 is a child of C1.
+; CHECK: DW_TAG_lexical_block
+; CHECK: DW_TAG_variable
+; CHECK: DW_AT_name ("a1")
+; CHECK: DW_AT_type {{.*}} "A1"
+; CHECK: DW_TAG_lexical_block
+; CHECK: DW_TAG_variable
+; CHECK: DW_AT_type {{.*}} "I1"
+; CHECK: DW_TAG_variable
+; CHECK: DW_AT_type {{.*}} "C1"
+; CHECK: NULL
+; CHECK: DW_TAG_structure_type
+; CHECK: DW_AT_name ("I1")
+; CHECK: DW_TAG_member
+; CHECK: DW_AT_type {{.*}} "Int1"
+; CHECK: NULL
+; CHECK: DW_TAG_structure_type
+; CHECK: DW_AT_name ("C1")
+; CHECK: DW_TAG_member
+; CHECK: DW_AT_type {{.*}} "C1::Char1"
+; CHECK: DW_TAG_typedef
+; CHECK: DW_AT_name ("Char1")
+; CHECK: NULL
+; CHECK: NULL
+
+; A1 and typedef Int1 defined in the subprogram scope.
+; CHECK: DW_TAG_structure_type
+; CHECK: DW_AT_name ("A1")
+; CHECK: DW_TAG_member
+; CHECK: NULL
+; CHECK: DW_TAG_typedef
+; CHECK: DW_AT_name ("Int1")
+; CHECK: NULL
+
+; CHECK: DW_TAG_base_type
+; CHECK: DW_TAG_base_type
+
+; Abstract definition of `not_removed()`.
+; CHECK: DW_TAG_subprogram
+; CHECK: DW_AT_name ("not_removed")
+; CHECK: DW_AT_inline (DW_INL_inlined)
+
+; I2 and C2 defined in the first lexical block, typedef Char2 is a child of C2.
+; CHECK: DW_TAG_lexical_block
+; CHECK: DW_TAG_variable
+; CHECK: DW_AT_name ("a2")
+; CHECK: DW_AT_type {{.*}} "A2"
+; CHECK: DW_TAG_lexical_block
+; CHECK: DW_TAG_variable
+; CHECK: DW_AT_name ("i2")
+; CHECK: DW_AT_type {{.*}} "I2"
+; CHECK: DW_TAG_variable
+; CHECK: DW_AT_name ("c2")
+; CHECK: DW_AT_type {{.*}} "C2"
+; CHECK: NULL
+; CHECK: DW_TAG_structure_type
+; CHECK: DW_AT_name ("I2")
+; CHECK: DW_TAG_member
+; CHECK: DW_AT_type {{.*}} "Int2"
+; CHECK: NULL
+; CHECK: DW_TAG_structure_type
+; CHECK: DW_AT_name ("C2")
+; CHECK: DW_TAG_member
+; CHECK: DW_AT_type {{.*}} "C2::Char2"
+; CHECK: DW_TAG_typedef
+; CHECK: DW_AT_name ("Char2")
+; CHECK: NULL
+; CHECK: NULL
+
+; A2 and typedef Int2 defined in subprogram scope.
+; CHECK: DW_TAG_structure_type
+; CHECK: DW_AT_name ("A2")
+; CHECK: DW_TAG_member
+; CHECK: NULL
+; CHECK: DW_TAG_typedef
+; CHECK: DW_AT_name ("Int2")
+; CHECK: NULL
+
+; Definition of `foo()`.
+; CHECK: DW_TAG_subprogram
+; CHECK: DW_AT_name ("foo")
+
+; CHECK: DW_TAG_lexical_block
+; CHECK: DW_TAG_lexical_block
+; CHECK: DW_TAG_variable
+; CHECK: DW_AT_name ("a3")
+; CHECK: DW_AT_type {{.*}} "A3"
+; CHECK: DW_TAG_lexical_block
+; CHECK: DW_TAG_variable
+; CHECK: DW_AT_name ("i3")
+; CHECK: DW_AT_type {{.*}} "I3"
+; CHECK: DW_TAG_variable
+; CHECK: DW_AT_name ("c3")
+; CHECK: DW_AT_type {{.*}} "C3"
+; CHECK: NULL
+
+; C3 has the inner lexical block scope, typedef Char3 is a child of C3.
+; CHECK: DW_TAG_structure_type
+; CHECK: DW_AT_name ("C3")
+; CHECK: DW_TAG_member
+; CHECK: DW_AT_type {{.*}} "C3::Char3"
+; CHECK: DW_TAG_typedef
+; CHECK: DW_AT_name ("Char3")
+; CHECK: NULL
+; CHECK: NULL
+
+; I3 has the outer lexical block scope.
+; CHECK: DW_TAG_structure_type
+; CHECK: DW_AT_name ("I3")
+; CHECK: DW_TAG_member
+; CHECK: DW_AT_type {{.*}} "Int3"
+; CHECK: NULL
+; CHECK: NULL
+
+; CHECK: DW_TAG_inlined_subroutine
+; CHECK: DW_AT_abstract_origin {{.*}} "_Z7removedv"
+; CHECK: DW_TAG_lexical_block
+; CHECK: DW_TAG_variable
+; CHECK: DW_TAG_lexical_block
+; CHECK: DW_TAG_variable
+; CHECK: DW_TAG_variable
+; CHECK: NULL
+; CHECK: NULL
+; CHECK: NULL
+
+; CHECK: DW_TAG_inlined_subroutine
+; CHECK: DW_AT_abstract_origin {{.*}} "_Z11not_removedv"
+; CHECK: DW_TAG_lexical_block
+; CHECK: DW_TAG_variable
+; CHECK: DW_TAG_lexical_block
+; CHECK: DW_TAG_variable
+; CHECK: DW_TAG_variable
+; CHECK: NULL
+; CHECK: NULL
+; CHECK: NULL
+
+; A3 and Int3 defined within the subprogam scope.
+; CHECK: DW_TAG_structure_type
+; CHECK: DW_AT_name ("A3")
+; CHECK: DW_TAG_member
+; CHECK: NULL
+; CHECK: DW_TAG_typedef
+; CHECK: DW_AT_name ("Int3")
+; CHECK: NULL
+; CHECK: NULL
+
+%struct.A2 = type { i32 }
+%struct.I2 = type { i32 }
+%struct.C2 = type { i8 }
+%struct.A1 = type { i32 }
+%struct.I1 = type { i32 }
+%struct.C1 = type { i8 }
+%struct.A3 = type { i32 }
+%struct.I3 = type { i32 }
+%struct.C3 = type { i8 }
+
+define dso_local void @_Z11not_removedv() !dbg !8 {
+entry:
+ %a2 = alloca %struct.A2, align 4
+ %i2 = alloca %struct.I2, align 4
+ %c2 = alloca %struct.C2, align 1
+ call void @llvm.dbg.declare(metadata %struct.A2* %a2, metadata !12, metadata !DIExpression()), !dbg !18
+ %i = getelementptr inbounds %struct.A2, %struct.A2* %a2, i32 0, i32 0, !dbg !19
+ %0 = load i32, i32* %i, align 4, !dbg !20
+ %inc = add nsw i32 %0, 1, !dbg !20
+ store i32 %inc, i32* %i, align 4, !dbg !20
+ call void @llvm.dbg.declare(metadata %struct.I2* %i2, metadata !21, metadata !DIExpression()), !dbg !27
+ %j = getelementptr inbounds %struct.I2, %struct.I2* %i2, i32 0, i32 0, !dbg !28
+ %1 = load i32, i32* %j, align 4, !dbg !29
+ %inc1 = add nsw i32 %1, 1, !dbg !29
+ store i32 %inc1, i32* %j, align 4, !dbg !29
+ call void @llvm.dbg.declare(metadata %struct.C2* %c2, metadata !30, metadata !DIExpression()), !dbg !36
+ %c = getelementptr inbounds %struct.C2, %struct.C2* %c2, i32 0, i32 0, !dbg !37
+ %2 = load i8, i8* %c, align 1, !dbg !38
+ %inc2 = add i8 %2, 1, !dbg !38
+ store i8 %inc2, i8* %c, align 1, !dbg !38
+ ret void, !dbg !39
+}
+
+declare void @llvm.dbg.declare(metadata, metadata, metadata)
+
+define dso_local void @_Z3foov() !dbg !40 {
+entry:
+ %a1.i = alloca %struct.A1, align 4
+ %i1.i = alloca %struct.I1, align 4
+ %c1.i = alloca %struct.C1, align 1
+ %a2.i = alloca %struct.A2, align 4
+ %i2.i = alloca %struct.I2, align 4
+ %c2.i = alloca %struct.C2, align 1
+ %a3 = alloca %struct.A3, align 4
+ %i3 = alloca %struct.I3, align 4
+ %c3 = alloca %struct.C3, align 1
+ call void @llvm.dbg.declare(metadata %struct.A3* %a3, metadata !41, metadata !DIExpression()), !dbg !47
+ %i = getelementptr inbounds %struct.A3, %struct.A3* %a3, i32 0, i32 0, !dbg !48
+ %0 = load i32, i32* %i, align 4, !dbg !49
+ %inc = add nsw i32 %0, 1, !dbg !49
+ store i32 %inc, i32* %i, align 4, !dbg !49
+ call void @llvm.dbg.declare(metadata %struct.I3* %i3, metadata !50, metadata !DIExpression()), !dbg !56
+ %j = getelementptr inbounds %struct.I3, %struct.I3* %i3, i32 0, i32 0, !dbg !57
+ %1 = load i32, i32* %j, align 4, !dbg !58
+ %inc1 = add nsw i32 %1, 1, !dbg !58
+ store i32 %inc1, i32* %j, align 4, !dbg !58
+ call void @llvm.dbg.declare(metadata %struct.C3* %c3, metadata !59, metadata !DIExpression()), !dbg !64
+ %c = getelementptr inbounds %struct.C3, %struct.C3* %c3, i32 0, i32 0, !dbg !65
+ %2 = load i8, i8* %c, align 1, !dbg !66
+ %inc2 = add i8 %2, 1, !dbg !66
+ store i8 %inc2, i8* %c, align 1, !dbg !66
+ call void @llvm.dbg.declare(metadata %struct.A1* %a1.i, metadata !67, metadata !DIExpression()), !dbg !73
+ %i.i3 = getelementptr inbounds %struct.A1, %struct.A1* %a1.i, i32 0, i32 0, !dbg !75
+ %3 = load i32, i32* %i.i3, align 4, !dbg !76
+ %inc.i4 = add nsw i32 %3, 1, !dbg !76
+ store i32 %inc.i4, i32* %i.i3, align 4, !dbg !76
+ call void @llvm.dbg.declare(metadata %struct.I1* %i1.i, metadata !77, metadata !DIExpression()), !dbg !83
+ %j.i5 = getelementptr inbounds %struct.I1, %struct.I1* %i1.i, i32 0, i32 0, !dbg !84
+ %4 = load i32, i32* %j.i5, align 4, !dbg !85
+ %inc1.i6 = add nsw i32 %4, 1, !dbg !85
+ store i32 %inc1.i6, i32* %j.i5, align 4, !dbg !85
+ call void @llvm.dbg.declare(metadata %struct.C1* %c1.i, metadata !86, metadata !DIExpression()), !dbg !91
+ %c.i7 = getelementptr inbounds %struct.C1, %struct.C1* %c1.i, i32 0, i32 0, !dbg !92
+ %5 = load i8, i8* %c.i7, align 1, !dbg !93
+ %inc2.i8 = add i8 %5, 1, !dbg !93
+ store i8 %inc2.i8, i8* %c.i7, align 1, !dbg !93
+ call void @llvm.dbg.declare(metadata %struct.A2* %a2.i, metadata !12, metadata !DIExpression()), !dbg !94
+ %i.i = getelementptr inbounds %struct.A2, %struct.A2* %a2.i, i32 0, i32 0, !dbg !96
+ %6 = load i32, i32* %i.i, align 4, !dbg !97
+ %inc.i = add nsw i32 %6, 1, !dbg !97
+ store i32 %inc.i, i32* %i.i, align 4, !dbg !97
+ call void @llvm.dbg.declare(metadata %struct.I2* %i2.i, metadata !21, metadata !DIExpression()), !dbg !98
+ %j.i = getelementptr inbounds %struct.I2, %struct.I2* %i2.i, i32 0, i32 0, !dbg !99
+ %7 = load i32, i32* %j.i, align 4, !dbg !100
+ %inc1.i = add nsw i32 %7, 1, !dbg !100
+ store i32 %inc1.i, i32* %j.i, align 4, !dbg !100
+ call void @llvm.dbg.declare(metadata %struct.C2* %c2.i, metadata !30, metadata !DIExpression()), !dbg !101
+ %c.i = getelementptr inbounds %struct.C2, %struct.C2* %c2.i, i32 0, i32 0, !dbg !102
+ %8 = load i8, i8* %c.i, align 1, !dbg !103
+ %inc2.i = add i8 %8, 1, !dbg !103
+ store i8 %inc2.i, i8* %c.i, align 1, !dbg !103
+ ret void, !dbg !104
+}
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!2, !3, !4, !5, !6}
+!llvm.ident = !{!7}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang version 14.0.0", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None)
+!1 = !DIFile(filename: "test.cpp", directory: "/")
+!2 = !{i32 7, !"Dwarf Version", i32 4}
+!3 = !{i32 2, !"Debug Info Version", i32 3}
+!4 = !{i32 1, !"wchar_size", i32 4}
+!5 = !{i32 7, !"uwtable", i32 1}
+!6 = !{i32 7, !"frame-pointer", i32 2}
+!7 = !{!"clang version 14.0.0"}
+!8 = distinct !DISubprogram(name: "not_removed", linkageName: "_Z11not_removedv", scope: !1, file: !1, line: 17, type: !9, scopeLine: 17, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !105)
+!9 = !DISubroutineType(types: !10)
+!10 = !{null}
+!11 = !{}
+!12 = !DILocalVariable(name: "a2", scope: !13, file: !1, line: 23, type: !14)
+!13 = distinct !DILexicalBlock(scope: !8, file: !1, line: 20, column: 3)
+!14 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "A2", scope: !8, file: !1, line: 18, size: 32, flags: DIFlagTypePassByValue, elements: !15)
+!15 = !{!16}
+!16 = !DIDerivedType(tag: DW_TAG_member, name: "i", scope: !14, file: !1, line: 18, baseType: !17, size: 32)
+!17 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!18 = !DILocation(line: 23, column: 8, scope: !13)
+!19 = !DILocation(line: 23, column: 15, scope: !13)
+!20 = !DILocation(line: 23, column: 16, scope: !13)
+!21 = !DILocalVariable(name: "i2", scope: !22, file: !1, line: 25, type: !23)
+!22 = distinct !DILexicalBlock(scope: !13, file: !1, line: 24, column: 5)
+!23 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "I2", scope: !13, file: !1, line: 21, size: 32, flags: DIFlagTypePassByValue, elements: !24)
+!24 = !{!25}
+!25 = !DIDerivedType(tag: DW_TAG_member, name: "j", scope: !23, file: !1, line: 21, baseType: !26, size: 32)
+!26 = !DIDerivedType(tag: DW_TAG_typedef, name: "Int2", scope: !8, file: !1, line: 19, baseType: !17)
+!27 = !DILocation(line: 25, column: 10, scope: !22)
+!28 = !DILocation(line: 25, column: 17, scope: !22)
+!29 = !DILocation(line: 25, column: 18, scope: !22)
+!30 = !DILocalVariable(name: "c2", scope: !22, file: !1, line: 26, type: !31)
+!31 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "C2", scope: !13, file: !1, line: 22, size: 8, flags: DIFlagTypePassByValue, elements: !32)
+!32 = !{!33}
+!33 = !DIDerivedType(tag: DW_TAG_member, name: "c", scope: !31, file: !1, line: 22, baseType: !34, size: 8)
+!34 = !DIDerivedType(tag: DW_TAG_typedef, name: "Char2", scope: !31, file: !1, line: 22, baseType: !35)
+!35 = !DIBasicType(name: "char", size: 8, encoding: DW_ATE_signed_char)
+!36 = !DILocation(line: 26, column: 10, scope: !22)
+!37 = !DILocation(line: 26, column: 17, scope: !22)
+!38 = !DILocation(line: 26, column: 18, scope: !22)
+!39 = !DILocation(line: 29, column: 1, scope: !8)
+!40 = distinct !DISubprogram(name: "foo", linkageName: "_Z3foov", scope: !1, file: !1, line: 31, type: !9, scopeLine: 31, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !107)
+!41 = !DILocalVariable(name: "a3", scope: !42, file: !1, line: 38, type: !44)
+!42 = distinct !DILexicalBlock(scope: !43, file: !1, line: 36, column: 5)
+!43 = distinct !DILexicalBlock(scope: !40, file: !1, line: 34, column: 3)
+!44 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "A3", scope: !40, file: !1, line: 32, size: 32, flags: DIFlagTypePassByValue, elements: !45)
+!45 = !{!46}
+!46 = !DIDerivedType(tag: DW_TAG_member, name: "i", scope: !44, file: !1, line: 32, baseType: !17, size: 32)
+!47 = !DILocation(line: 38, column: 10, scope: !42)
+!48 = !DILocation(line: 38, column: 17, scope: !42)
+!49 = !DILocation(line: 38, column: 18, scope: !42)
+!50 = !DILocalVariable(name: "i3", scope: !51, file: !1, line: 40, type: !52)
+!51 = distinct !DILexicalBlock(scope: !42, file: !1, line: 39, column: 7)
+!52 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "I3", scope: !43, file: !1, line: 35, size: 32, flags: DIFlagTypePassByValue, elements: !53)
+!53 = !{!54}
+!54 = !DIDerivedType(tag: DW_TAG_member, name: "j", scope: !52, file: !1, line: 35, baseType: !55, size: 32)
+!55 = !DIDerivedType(tag: DW_TAG_typedef, name: "Int3", scope: !40, file: !1, line: 33, baseType: !17)
+!56 = !DILocation(line: 40, column: 12, scope: !51)
+!57 = !DILocation(line: 40, column: 19, scope: !51)
+!58 = !DILocation(line: 40, column: 20, scope: !51)
+!59 = !DILocalVariable(name: "c3", scope: !51, file: !1, line: 41, type: !60)
+!60 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "C3", scope: !42, file: !1, line: 37, size: 8, flags: DIFlagTypePassByValue, elements: !61)
+!61 = !{!62}
+!62 = !DIDerivedType(tag: DW_TAG_member, name: "c", scope: !60, file: !1, line: 37, baseType: !63, size: 8)
+!63 = !DIDerivedType(tag: DW_TAG_typedef, name: "Char3", scope: !60, file: !1, line: 37, baseType: !35)
+!64 = !DILocation(line: 41, column: 12, scope: !51)
+!65 = !DILocation(line: 41, column: 19, scope: !51)
+!66 = !DILocation(line: 41, column: 20, scope: !51)
+!67 = !DILocalVariable(name: "a1", scope: !68, file: !1, line: 8, type: !70)
+!68 = distinct !DILexicalBlock(scope: !69, file: !1, line: 5, column: 3)
+!69 = distinct !DISubprogram(name: "removed", linkageName: "_Z7removedv", scope: !1, file: !1, line: 2, type: !9, scopeLine: 2, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !110)
+!70 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "A1", scope: !69, file: !1, line: 3, size: 32, flags: DIFlagTypePassByValue, elements: !71, identifier: "_ZTSZ7removedvE2A1")
+!71 = !{!72}
+!72 = !DIDerivedType(tag: DW_TAG_member, name: "i", scope: !70, file: !1, line: 3, baseType: !17, size: 32)
+!73 = !DILocation(line: 8, column: 8, scope: !68, inlinedAt: !74)
+!74 = distinct !DILocation(line: 45, column: 3, scope: !40)
+!75 = !DILocation(line: 8, column: 15, scope: !68, inlinedAt: !74)
+!76 = !DILocation(line: 8, column: 16, scope: !68, inlinedAt: !74)
+!77 = !DILocalVariable(name: "i1", scope: !78, file: !1, line: 10, type: !79)
+!78 = distinct !DILexicalBlock(scope: !68, file: !1, line: 9, column: 5)
+!79 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "I1", scope: !68, file: !1, line: 6, size: 32, flags: DIFlagTypePassByValue, elements: !80, identifier: "_ZTSZ7removedvE2I1")
+!80 = !{!81}
+!81 = !DIDerivedType(tag: DW_TAG_member, name: "j", scope: !79, file: !1, line: 6, baseType: !82, size: 32)
+!82 = !DIDerivedType(tag: DW_TAG_typedef, name: "Int1", scope: !69, file: !1, line: 4, baseType: !17)
+!83 = !DILocation(line: 10, column: 10, scope: !78, inlinedAt: !74)
+!84 = !DILocation(line: 10, column: 17, scope: !78, inlinedAt: !74)
+!85 = !DILocation(line: 10, column: 18, scope: !78, inlinedAt: !74)
+!86 = !DILocalVariable(name: "c1", scope: !78, file: !1, line: 11, type: !87)
+!87 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "C1", scope: !68, file: !1, line: 7, size: 8, flags: DIFlagTypePassByValue, elements: !88, identifier: "_ZTSZ7removedvE2C1")
+!88 = !{!89}
+!89 = !DIDerivedType(tag: DW_TAG_member, name: "c", scope: !87, file: !1, line: 7, baseType: !90, size: 8)
+!90 = !DIDerivedType(tag: DW_TAG_typedef, name: "Char1", scope: !87, file: !1, line: 7, baseType: !35)
+!91 = !DILocation(line: 11, column: 10, scope: !78, inlinedAt: !74)
+!92 = !DILocation(line: 11, column: 17, scope: !78, inlinedAt: !74)
+!93 = !DILocation(line: 11, column: 18, scope: !78, inlinedAt: !74)
+!94 = !DILocation(line: 23, column: 8, scope: !13, inlinedAt: !95)
+!95 = distinct !DILocation(line: 46, column: 3, scope: !40)
+!96 = !DILocation(line: 23, column: 15, scope: !13, inlinedAt: !95)
+!97 = !DILocation(line: 23, column: 16, scope: !13, inlinedAt: !95)
+!98 = !DILocation(line: 25, column: 10, scope: !22, inlinedAt: !95)
+!99 = !DILocation(line: 25, column: 17, scope: !22, inlinedAt: !95)
+!100 = !DILocation(line: 25, column: 18, scope: !22, inlinedAt: !95)
+!101 = !DILocation(line: 26, column: 10, scope: !22, inlinedAt: !95)
+!102 = !DILocation(line: 26, column: 17, scope: !22, inlinedAt: !95)
+!103 = !DILocation(line: 26, column: 18, scope: !22, inlinedAt: !95)
+!104 = !DILocation(line: 47, column: 1, scope: !40)
+!105 = !{!14, !23, !26, !31}
+!107 = !{!44, !52, !55, !60}
+!110 = !{!70, !79, !82, !87}
diff --git a/llvm/test/DebugInfo/Generic/verifier-invalid-disubprogram.ll b/llvm/test/DebugInfo/Generic/verifier-invalid-disubprogram.ll
index 6d4d0e9..54ce1c5 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/Inputs/cleanup-retained-nodes.ll b/llvm/test/DebugInfo/Inputs/cleanup-retained-nodes.ll
new file mode 100644
index 0000000..d2cca1b
--- /dev/null
+++ b/llvm/test/DebugInfo/Inputs/cleanup-retained-nodes.ll
@@ -0,0 +1,17 @@
+target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-unknown-linux-gnu"
+
+@var = global i8 0, align 4, !dbg !7
+
+!llvm.module.flags = !{!0, !1, !2}
+!llvm.dbg.cu = !{!3}
+
+!0 = !{i32 7, !"Dwarf Version", i32 2}
+!1 = !{i32 2, !"Debug Info Version", i32 3}
+!2 = !{i32 1, !"wchar_size", i32 4}
+!3 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !5, producer: "clang", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None)
+!4 = !{}
+!5 = !DIFile(filename: "tmp2.cpp", directory: "/tmp/")
+!6 = !DICompositeType(tag: DW_TAG_class_type, scope: !3, file: !5, line: 212, size: 8, flags: DIFlagTypePassByValue, elements: !4, identifier: "type_global_in_another_module")
+!7 = !DIGlobalVariableExpression(var: !8, expr: !DIExpression())
+!8 = distinct !DIGlobalVariable(name: "var", scope: !3, file: !5, line: 1, type: !6, isLocal: false, isDefinition: true)
diff --git a/llvm/test/DebugInfo/X86/cleanup-retained-nodes.ll b/llvm/test/DebugInfo/X86/cleanup-retained-nodes.ll
new file mode 100644
index 0000000..86c5ca9
--- /dev/null
+++ b/llvm/test/DebugInfo/X86/cleanup-retained-nodes.ll
@@ -0,0 +1,45 @@
+; RUN: llvm-as %s -o %t.bc
+; RUN: llvm-as %p/../Inputs/cleanup-retained-nodes.ll -o %t.global.bc
+; RUN: llvm-link %t.global.bc %t.bc %t.bc -o - | llvm-dis - -o - \
+; RUN: | FileCheck %s --implicit-check-not=DICompositeType
+
+; During module loading, if a local type appears in retainedNodes
+; field of multiple DISubprograms due to ODR-uniquing,
+; retainedNodes should be cleaned up, so that only one DISubprogram
+; will have this type in its retainedNodes.
+
+; CHECK: distinct !DICompositeType(tag: DW_TAG_class_type, {{.*}}, identifier: "type_global_in_another_module")
+; CHECK: [[EMPTY:![0-9]+]] = !{}
+; CHECK: [[BAR1:![0-9]+]] = distinct !DISubprogram(name: "bar", {{.*}}, retainedNodes: [[RN_BAR1:![0-9]+]])
+; CHECK: [[RN_BAR1]] = !{[[T1:![0-9]+]], [[T1]], [[T1]], [[T2:![0-9]+]]}
+; CHECK: [[T1]] = distinct !DICompositeType(tag: DW_TAG_class_type, scope: [[BAR1]], {{.*}}, identifier: "local_type")
+; CHECK: [[T2]] = distinct !DICompositeType(tag: DW_TAG_class_type, scope: [[LB:![0-9]+]], {{.*}}, identifier: "local_type_in_block")
+; CHECK: [[LB]] = !DILexicalBlock(scope: [[BAR1]]
+; CHECK: {{![0-9]+}} = distinct !DISubprogram(name: "bar", {{.*}}, retainedNodes: [[EMPTY]])
+
+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"
+
+@llvm.used = appending global [1 x ptr] [ptr @bar]
+
+define internal void @bar(ptr %this) !dbg !10 {
+ ret void
+}
+
+!llvm.module.flags = !{!0, !1, !2}
+!llvm.dbg.cu = !{!8}
+
+!0 = !{i32 7, !"Dwarf Version", i32 2}
+!1 = !{i32 2, !"Debug Info Version", i32 3}
+!2 = !{i32 1, !"wchar_size", i32 4}
+!3 = !DICompositeType(tag: DW_TAG_class_type, scope: !10, file: !9, line: 212, size: 8, flags: DIFlagTypePassByValue, elements: !7, identifier: "type_global_in_another_module")
+!4 = !DICompositeType(tag: DW_TAG_class_type, scope: !5, file: !9, line: 211, size: 8, flags: DIFlagTypePassByValue, elements: !7, identifier: "local_type_in_block")
+!5 = !DILexicalBlock(scope: !10)
+; All repeating occurences of a uniqued type in retainedNodes must be checked.
+!6 = !{!12, !12, !12, !4, !3}
+!7 = !{}
+!8 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !9, producer: "clang", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None)
+!9 = !DIFile(filename: "tmp.cpp", directory: "/tmp/")
+!10 = distinct !DISubprogram(name: "bar", scope: !9, file: !9, line: 68, type: !11, scopeLine: 68, flags: DIFlagPrototyped, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition | DISPFlagOptimized, unit: !8, retainedNodes: !6)
+!11 = !DISubroutineType(types: !7)
+!12 = !DICompositeType(tag: DW_TAG_class_type, scope: !10, file: !9, line: 210, size: 8, flags: DIFlagTypePassByValue, elements: !7, identifier: "local_type")
diff --git a/llvm/test/DebugInfo/X86/llparser-cleanup-retained-nodes.ll b/llvm/test/DebugInfo/X86/llparser-cleanup-retained-nodes.ll
new file mode 100644
index 0000000..ee529ad
--- /dev/null
+++ b/llvm/test/DebugInfo/X86/llparser-cleanup-retained-nodes.ll
@@ -0,0 +1,35 @@
+; RUN: llvm-link %s %s -S -o - | FileCheck %s --implicit-check-not=DICompositeType
+
+; During module loading, if a local type appears in retainedNodes
+; field of multiple DISubprograms due to ODR-uniquing,
+; LLParser should clean up retainedNodes, so that only one DISubprogram
+; will have this type in its retainedNodes.
+
+; CHECK: [[BAR1:![0-9]+]] = distinct !DISubprogram(name: "bar", {{.*}}, retainedNodes: [[RN_BAR1:![0-9]+]])
+; CHECK: [[EMPTY:![0-9]+]] = !{}
+; CHECK: [[RN_BAR1]] = !{[[T1:![0-9]+]]}
+; CHECK: [[T1]] = distinct !DICompositeType(tag: DW_TAG_class_type, scope: [[BAR1]], {{.*}}, identifier: "local_type")
+; CHECK: {{![0-9]+}} = distinct !DISubprogram(name: "bar", {{.*}}, retainedNodes: [[EMPTY]])
+
+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"
+
+@llvm.used = appending global [1 x ptr] [ptr @bar]
+
+define internal void @bar(ptr %this) !dbg !5 {
+ ret void
+}
+
+!llvm.module.flags = !{!0, !1, !2}
+!llvm.dbg.cu = !{!3}
+
+!0 = !{i32 1, !"wchar_size", i32 4}
+!1 = !{i32 7, !"Dwarf Version", i32 2}
+!2 = !{i32 2, !"Debug Info Version", i32 3}
+!3 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !4, producer: "clang version 14.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None)
+!4 = !DIFile(filename: "tmp.cpp", directory: "/tmp/")
+!5 = distinct !DISubprogram(name: "bar", scope: !4, file: !4, line: 68, type: !6, scopeLine: 68, flags: DIFlagPrototyped, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition | DISPFlagOptimized, unit: !3, retainedNodes: !9)
+!6 = !DISubroutineType(types: !8)
+!7 = !DICompositeType(tag: DW_TAG_class_type, scope: !5, file: !4, line: 210, size: 8, flags: DIFlagTypePassByValue, elements: !8, identifier: "local_type")
+!8 = !{}
+!9 = !{!7}
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 0000000..7963efd
--- /dev/null
+++ b/llvm/test/DebugInfo/X86/local-type-as-template-parameter.ll
@@ -0,0 +1,161 @@
+; REQUIRES: 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 demonstrates misplacement for a local type (see https://github.com/llvm/llvm-project/issues/55680 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/Transforms/FunctionImport/Inputs/funcimport-debug-retained-nodes.ll b/llvm/test/Transforms/FunctionImport/Inputs/funcimport-debug-retained-nodes.ll
new file mode 100644
index 0000000..6e3a9c7
--- /dev/null
+++ b/llvm/test/Transforms/FunctionImport/Inputs/funcimport-debug-retained-nodes.ll
@@ -0,0 +1,32 @@
+target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-unknown-linux-gnu"
+
+define void @func() {
+entry:
+ ret void, !dbg !8
+}
+
+define i32 @foo() !dbg !13 {
+entry:
+ ret i32 0, !dbg !14
+}
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!3, !4}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !2)
+!1 = !DIFile(filename: "funcimport_debug2.c", directory: ".")
+!2 = !{}
+!3 = !{i32 2, !"Dwarf Version", i32 4}
+!4 = !{i32 2, !"Debug Info Version", i32 3}
+!5 = distinct !DISubprogram(name: "func", scope: !1, file: !1, line: 2, type: !6, scopeLine: 2, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !2)
+!6 = !DISubroutineType(types: !7)
+!7 = !{null}
+!8 = !DILocation(line: 2, column: 1, scope: !9, inlinedAt: !12)
+!9 = distinct !DISubprogram(name: "inlined_out_clone", scope: !1, file: !1, line: 20, type: !6, scopeLine: 2, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !10)
+!10 = !{!11}
+!11 = !DICompositeType(tag: DW_TAG_class_type, scope: !9, file: !1, line: 210, size: 8, flags: DIFlagTypePassByValue, elements: !2, identifier: "local_type")
+!12 = !DILocation(line: 11, scope: !5)
+!13 = distinct !DISubprogram(name: "foo", scope: !1, file: !1, line: 1, type: !6, scopeLine: 1, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !2)
+!14 = !DILocation(line: 3, column: 1, scope: !9, inlinedAt: !15)
+!15 = !DILocation(line: 3, column: 1, scope: !13)
diff --git a/llvm/test/Transforms/FunctionImport/funcimport-debug-retained-nodes.ll b/llvm/test/Transforms/FunctionImport/funcimport-debug-retained-nodes.ll
new file mode 100644
index 0000000..3908c22
--- /dev/null
+++ b/llvm/test/Transforms/FunctionImport/funcimport-debug-retained-nodes.ll
@@ -0,0 +1,118 @@
+; REQUIRES: asserts,x86-registered-target
+; RUN: opt --bitcode-mdindex-threshold=0 -module-summary %s -o %t.bc
+; RUN: opt --bitcode-mdindex-threshold=0 -module-summary %p/Inputs/funcimport-debug-retained-nodes.ll -o %t2.bc
+
+; RUN: llvm-lto2 run %t2.bc %t.bc --save-temps -o %t3 \
+; RUN: -r=%t.bc,main,px -r=%t.bc,func,px -r=%t2.bc,func,x -r=%t2.bc,foo,rx \
+; RUN: --debug-only=bitcode-reader --thinlto-threads=1 2>&1 \
+; RUN: | FileCheck --allow-empty --check-prefix=LTO %s \
+; RUN: --implicit-check-not='ignoring invalid debug info' \
+; RUN: --implicit-check-not='warning'
+
+; RUN: llvm-dis %t3.2.3.import.bc -o - | FileCheck %s \
+; RUN: --implicit-check-not='DISubprogram(name: "inlined_out_clone"' \
+; RUN: --implicit-check-not='DICompositeType({{.*}}, identifier: "local_type"'
+
+; Check that retained nodes of lazy-loaded DISubprograms are cleaned up
+; from incorrectly-scoped local types.
+
+; When DebugTypeODRUniquing feature is enabled (e.g. with ThinLTO),
+; local DITypes with the same `identifier` values are uniqued in scope
+; of LLVM context during metadata loading.
+; DISubprograms may reference their local types via `retainedNodes` attribute.
+; Thus, during ThinLTO, the final module may end up having multiple
+; DISubprograms referencing the same uniqued local type.
+; MetadataLoader should clean up retainedNodes lists of DISubprograms from
+; such references after loading subprograms and their local types.
+; This test checks that such cleanup is done when metadata nodes are loaded
+; in lazy fashion without relying on cleanup performed during
+; eager function-level or module-level METADATA_BLOCK loading.
+
+; In order to trigger lazy-loading of DISubprogram "inlined_out_clone"
+; from module-level METADATA_BLOCK in %p/Inputs/funcimport-debug-retained-nodes.ll:
+; 1. The emission of metadata index is forced by setting
+; --bitcode-md-index-threshold. If no MD index is emitted in BC file,
+; MetadataLoader loads all metadata from a module-level METADATA_BLOCK eagerly.
+; 2. The DISubprogram is referenced by locations inlined in two different
+; IR functions, thus, it is emitted in module-level METADATA_BLOCK.
+; 3. The DISubprogram is not referenced by any local variable of a function,
+; so that it is not loaded eagerly when reading function-level METADATA_BLOCK.
+; Otherwise, cleanup would be performed on it during function-level
+; METADATA_BLOCK loading.
+; 4. No other METADATA_BLOCK should be loaded after lazy-loading the target
+; DISubprogram, to avoid cleanup being performed later. We want to observe
+; the behavior of MetadataLoader when loading the target DISubprogram lazily
+; without interference from metadata blocks loaded later. Therefore, @foo from
+; %p/Inputs/funcimport-debug-retained-nodes.ll, that follows @func referencing
+; the target DISubprogram, is marked as dso_preemptable => unsafe for LTO
+; function import.
+
+; This test should pass if, after ThinLTO function import, the final module
+; contains two DISubprograms "inlined_out_clone", and none of them reference
+; the local type that doesn't belong to them via `retainedNodes`.
+; It should fail if `retainedNodes` field of DISubprogram "inlined_out_clone"
+; loaded from %p/Inputs/funcimport-debug-retained-nodes.ll references
+; DICompositeType from the scope of DISubprogram "inlined_out_clone" from
+; %p/funcimport-debug-retained-nodes.ll (the type that is uniqued
+; due to DebugTypeODRUniquing on).
+
+; Check that lazy loading codepath is triggered, the subprogram is cleaned up,
+; and MetadataLoaderImpl::resolveLoadedMetadata() is not called after that.
+; LTO: Lazy metadata loading: Resolved loaded metadata. Cleaned up 1 subprogram(s).
+; LTO-NOT: Resolved loaded metadata
+
+; The module %p/funcimport-debug-retained-nodes.ll contains:
+; - DICompositeType "local_type", and
+; - DISubprogram "inlined_out_clone" with empty retainedNodes list.
+; The module %p/Inputs/funcimport-debug-retained-nodes.ll contains:
+; - DICompositeType "local_type", and
+; - DISubprogram "inlined_out_clone" with "local_type" in its retainedNodes.
+; After function import into module %p/funcimport-debug-retained-nodes.ll,
+; the output module contains:
+; - a single DICompositeType "local_type" that comes from %p/funcimport-debug-retained-nodes.ll
+; (due to ODR-uniquing, "local_type" from %p/Inputs/funcimport-debug-retained-nodes.ll
+; is not imported during function import),
+; - DISubprogram "inlined_out_clone" from %p/funcimport-debug-retained-nodes.ll
+; with empty retainedNodes list, and
+; - DISubprogram "inlined_out_clone" from %p/Inputs/funcimport-debug-retained-nodes.ll.
+; This test expects its retaiendNodes to be empty, cleaned up from reference
+; to "local_type" from %p/funcimport-debug-retained-nodes.ll (that, without proper
+; cleanup, would occur because of ODR-uniquing). The following check lines ensure that.
+
+; CHECK: ![[ORIGINAL_FILE:[0-9]+]] = !DIFile(filename: "funcimport_debug.c",
+; CHECK: ![[EMPTY:[0-9]+]] = !{}
+; CHECK: ![[IMPORTED_FILE:[0-9]+]] = !DIFile(filename: "funcimport_debug2.c",
+; CHECK: ![[ORIGINAL_SP:[0-9]+]] = distinct !DISubprogram(name: "inlined_out_clone", {{.*}}, file: ![[ORIGINAL_FILE]], {{.*}}, retainedNodes: ![[EMPTY]]
+; CHECK: !DICompositeType(tag: DW_TAG_class_type, scope: ![[ORIGINAL_SP]], {{.*}}, identifier: "local_type"
+; CHECK: !DISubprogram(name: "inlined_out_clone", {{.*}}, file: ![[IMPORTED_FILE]], {{.*}}, retainedNodes: ![[EMPTY]]
+
+target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-unknown-linux-gnu"
+
+define i32 @main() !dbg !5 {
+entry:
+ %a = alloca i8, align 4
+ #dbg_declare(ptr %a, !9, !DIExpression(), !12)
+ call void (...) @func()
+ ret i32 0
+}
+
+declare void @func(...)
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!3, !4}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !2)
+!1 = !DIFile(filename: "funcimport_debug.c", directory: ".")
+!2 = !{}
+!3 = !{i32 2, !"Dwarf Version", i32 4}
+!4 = !{i32 2, !"Debug Info Version", i32 3}
+!5 = distinct !DISubprogram(name: "main", scope: !1, file: !1, line: 2, type: !6, scopeLine: 2, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !2)
+!6 = !DISubroutineType(types: !7)
+!7 = !{!8}
+!8 = !DIBasicType(name: "int", size: 32, align: 32, encoding: DW_ATE_signed)
+!9 = !DILocalVariable(name: "foo_ptr", scope: !10, file: !1, line: 4, type: !11)
+!10 = distinct !DISubprogram(name: "inlined_out_clone", scope: !1, file: !1, line: 20, type: !6, scopeLine: 2, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !2)
+!11 = !DICompositeType(tag: DW_TAG_class_type, scope: !10, file: !1, line: 210, size: 8, flags: DIFlagTypePassByValue, elements: !2, identifier: "local_type")
+!12 = !DILocation(line: 3, column: 1, scope: !10, inlinedAt: !13)
+!13 = !DILocation(line: 3, column: 3, scope: !5)
diff --git a/llvm/unittests/Transforms/Utils/CloningTest.cpp b/llvm/unittests/Transforms/Utils/CloningTest.cpp
index 545ca7c..8c138be8 100644
--- a/llvm/unittests/Transforms/Utils/CloningTest.cpp
+++ b/llvm/unittests/Transforms/Utils/CloningTest.cpp
@@ -808,6 +808,145 @@ 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 ptr poison, metadata !5, metadata !DIExpression()), !dbg !7
+ call void @llvm.dbg.declare(metadata ptr poison, metadata !25, metadata !DIExpression()), !dbg !7
+ call void @llvm.dbg.declare(metadata ptr poison, metadata !28, metadata !DIExpression()), !dbg !8
+ call void @llvm.dbg.declare(metadata ptr poison, metadata !30, metadata !DIExpression()), !dbg !8
+ 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, !26, !28, !30}
+ !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}
+ !26 = distinct !DICompositeType(tag: DW_TAG_enumeration_type, name: "mystruct", scope: !3, file: !1, line: 13, size: 208, elements: !{})
+ !27 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !26, size: 64)
+ !28 = !DILocalVariable(name: "ptr", scope: !3, type: !27)
+ !29 = !DIDerivedType(tag: DW_TAG_const_type, baseType: !27)
+ !30 = !DILocalVariable(name: "const_ptr", scope: !3, type: !29)
+ )";
+
+ 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(), 7u);
+ EXPECT_EQ(FuncSP->getRetainedNodes().size(),
+ ClonedSP->getRetainedNodes().size());
+
+ // Ensure that Orig node is a clone of Copy by checking that they are
+ // different objects with the same name.
+ auto CheckNodeIsCloned = [](auto *Orig, auto *Copy) {
+ EXPECT_FALSE(Orig->getName().empty());
+ EXPECT_EQ(Orig->getName(), Copy->getName());
+
+ // Check that node was copied.
+ EXPECT_NE(Orig, Copy);
+ };
+
+ // Check that retained nodes are cloned.
+ unsigned I = 0;
+ auto CheckRetainedNode = [&](auto *Node) {
+ // The order of retained nodes should be preserved.
+ auto *Copy =
+ cast<std::decay_t<decltype(*Node)>>(ClonedSP->getRetainedNodes()[I]);
+
+ CheckNodeIsCloned(Node, Copy);
+
+ ++I;
+ };
+ FuncSP->forEachRetainedNode(CheckRetainedNode, CheckRetainedNode,
+ CheckRetainedNode, CheckRetainedNode);
+
+ auto ToDerived = [](const DIType *Ty) { return cast<DIDerivedType>(Ty); };
+
+ // Check that derived type (pointer type) referencing local type is remapped
+ // in the cloned function even though it doesn't have an explicit scope.
+ auto CheckPointerTypeIsCloned = [&](const DIType *PtrTy,
+ const DIType *PtrTyCopy) {
+ auto *PointerType = ToDerived(PtrTy);
+ auto *PointerTypeCopy = ToDerived(PtrTyCopy);
+ ASSERT_EQ(PointerType->getBaseType()->getName(), "mystruct");
+ ASSERT_EQ(PointerType->getScope(), nullptr);
+ CheckNodeIsCloned(PointerType->getBaseType(),
+ PointerTypeCopy->getBaseType());
+ };
+ auto *PointerOrig = cast<DILocalVariable>(FuncSP->getRetainedNodes()[5]);
+ auto *PointerCopy = cast<DILocalVariable>(ClonedSP->getRetainedNodes()[5]);
+ ASSERT_EQ(PointerOrig->getName(), "ptr");
+ CheckPointerTypeIsCloned(PointerOrig->getType(), PointerCopy->getType());
+
+ // Check that scopeless derived type (const type) referencing scopeless
+ // derived type (pointer type) referencing local type gets cloned.
+ auto *ConstPointerOrig = cast<DILocalVariable>(FuncSP->getRetainedNodes()[6]);
+ auto *ConstPointerCopy =
+ cast<DILocalVariable>(ClonedSP->getRetainedNodes()[6]);
+ ASSERT_EQ(ConstPointerOrig->getName(), "const_ptr");
+ auto *ConstPointerTypeOrig = ToDerived(ConstPointerOrig->getType());
+ auto *ConstPointerTypeCopy = ToDerived(ConstPointerCopy->getType());
+ ASSERT_NE(ConstPointerOrig, ConstPointerCopy);
+ CheckPointerTypeIsCloned(ToDerived(ConstPointerTypeOrig)->getBaseType(),
+ ToDerived(ConstPointerTypeCopy)->getBaseType());
+
+ 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)