diff --git a/clang/test/DebugInfo/CXX/access.cpp b/clang/test/DebugInfo/CXX/access.cpp index 9f2c044843d0f..7c0bf8ccb0384 100644 --- a/clang/test/DebugInfo/CXX/access.cpp +++ b/clang/test/DebugInfo/CXX/access.cpp @@ -18,9 +18,9 @@ class B : public A { static int public_static; protected: + // CHECK-DAG: !DIDerivedType(tag: DW_TAG_typedef, name: "prot_using",{{.*}} line: [[@LINE+3]],{{.*}} flags: DIFlagProtected) // CHECK-DAG: !DIDerivedType(tag: DW_TAG_typedef, name: "prot_typedef",{{.*}} line: [[@LINE+1]],{{.*}} flags: DIFlagProtected) typedef int prot_typedef; - // CHECK-DAG: !DIDerivedType(tag: DW_TAG_typedef, name: "prot_using",{{.*}} line: [[@LINE+1]],{{.*}} flags: DIFlagProtected) using prot_using = prot_typedef; prot_using prot_member; diff --git a/clang/test/DebugInfo/CXX/anon-union-vars.cpp b/clang/test/DebugInfo/CXX/anon-union-vars.cpp index 3aca4e199ab8d..27e6c6850e013 100644 --- a/clang/test/DebugInfo/CXX/anon-union-vars.cpp +++ b/clang/test/DebugInfo/CXX/anon-union-vars.cpp @@ -51,13 +51,13 @@ void instantiate(int x) { // CHECK: !DIGlobalVariable(name: "b",{{.*}} file: [[FILE]], line: 6,{{.*}} isLocal: true, isDefinition: true // CHECK: !DIGlobalVariable(name: "result", {{.*}} isLocal: false, isDefinition: true // CHECK: !DIGlobalVariable(name: "value", {{.*}} isLocal: false, isDefinition: true -// CHECK: !DILocalVariable(name: "i", {{.*}}, flags: DIFlagArtificial -// CHECK: !DILocalVariable(name: "c", {{.*}}, flags: DIFlagArtificial -// CHECK: !DILocalVariable( -// CHECK-NOT: name: -// CHECK: type: ![[UNION:[0-9]+]] -// CHECK: ![[UNION]] = distinct !DICompositeType(tag: DW_TAG_union_type, +// CHECK: ![[UNION:[0-9]+]] = distinct !DICompositeType(tag: DW_TAG_union_type, // CHECK-NOT: name: // CHECK: elements // CHECK: !DIDerivedType(tag: DW_TAG_member, name: "i", scope: ![[UNION]], // CHECK: !DIDerivedType(tag: DW_TAG_member, name: "c", scope: ![[UNION]], +// CHECK: !DILocalVariable(name: "i", {{.*}}, flags: DIFlagArtificial +// CHECK: !DILocalVariable(name: "c", {{.*}}, flags: DIFlagArtificial +// CHECK: !DILocalVariable( +// CHECK-NOT: name: +// CHECK: type: ![[UNION]] diff --git a/clang/test/DebugInfo/CXX/codeview-unnamed.cpp b/clang/test/DebugInfo/CXX/codeview-unnamed.cpp index 30815bda020ea..b625a80313c38 100644 --- a/clang/test/DebugInfo/CXX/codeview-unnamed.cpp +++ b/clang/test/DebugInfo/CXX/codeview-unnamed.cpp @@ -4,6 +4,60 @@ int main(int argc, char* argv[], char* arge[]) { // + // LINUX: [[TYPE_OF_ONE:![0-9]+]] = distinct !DICompositeType( + // LINUX-SAME: tag: DW_TAG_structure_type + // LINUX-NOT: name: + // LINUX-NOT: identifier: + // LINUX-SAME: ) + // + // MSVC: [[TYPE_OF_ONE:![0-9]+]] = distinct !DICompositeType + // MSVC-SAME: tag: DW_TAG_structure_type + // MSVC-SAME: name: "" + // MSVC-SAME: identifier: ".?AU@?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: "" + // MSVC-SAME: identifier: ".?AU@?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: "" + // MSVC-SAME: identifier: ".?AV@?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: "" - // MSVC-SAME: identifier: ".?AU@?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: "" - // MSVC-SAME: identifier: ".?AU@?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: "" - // MSVC-SAME: identifier: ".?AV@?0??main@@9@" + // MSVC-SAME: type: [[TYPE_OF_FOUR]] // MSVC-SAME: ) return 0; diff --git a/clang/test/DebugInfo/CXX/gline-tables-only-codeview.cpp b/clang/test/DebugInfo/CXX/gline-tables-only-codeview.cpp index 6b9c9a243decd..122e4aa62ea7d 100644 --- a/clang/test/DebugInfo/CXX/gline-tables-only-codeview.cpp +++ b/clang/test/DebugInfo/CXX/gline-tables-only-codeview.cpp @@ -51,9 +51,9 @@ void test() { // CHECK-SAME: name: "", 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: "", // 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 021b85d4ce3a4..609a71c6ec015 100644 --- a/clang/test/DebugInfo/CXX/lambda-capture-packs.cpp +++ b/clang/test/DebugInfo/CXX/lambda-capture-packs.cpp @@ -2,14 +2,6 @@ // RUN: -debug-info-kind=standalone -std=c++26 %s -o - | FileCheck %s -// CHECK: ![[PACK1:[0-9]+]] = distinct !DISubprogram(name: "capture_pack" -// CHECK: ![[PACK2:[0-9]+]] = distinct !DISubprogram(name: "capture_pack" -// CHECK: ![[PACK3:[0-9]+]] = distinct !DISubprogram(name: "capture_pack_and_locals" -// CHECK: ![[PACK4:[0-9]+]] = distinct !DISubprogram(name: "capture_pack_and_locals" -// CHECK: ![[PACK5:[0-9]+]] = distinct !DISubprogram(name: "capture_pack_and_this" -// CHECK: ![[PACK6:[0-9]+]] = distinct !DISubprogram(name: "capture_pack_and_this" -// CHECK: ![[PACK7:[0-9]+]] = distinct !DISubprogram(name: "capture_binding_and_param_pack" - template 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" // 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" // 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" // 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" // 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" // 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" // 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" // 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 019d09c48f858..1df210953ac2e 100644 --- a/clang/test/DebugInfo/CXX/lambda-this.cpp +++ b/clang/test/DebugInfo/CXX/lambda-this.cpp @@ -13,10 +13,10 @@ void D::d(int x) { } // CHECK: ![[D:[0-9]+]] = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "D", -// CHECK: ![[POINTER:.*]] = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: ![[D]], size: 64) // CHECK: !DIDerivedType(tag: DW_TAG_member, name: "this", // CHECK-SAME: line: 11 -// CHECK-SAME: baseType: ![[POINTER]] +// CHECK-SAME: baseType: ![[POINTER:[0-9]+]] // CHECK-SAME: size: 64 // CHECK-NOT: offset: 0 // CHECK-SAME: ){{$}} +// CHECK: ![[POINTER]] = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: ![[D]], size: 64) diff --git a/clang/test/DebugInfo/Generic/codeview-unnamed.c b/clang/test/DebugInfo/Generic/codeview-unnamed.c index 0df6e1a0419bb..7b88f53a92e42 100644 --- a/clang/test/DebugInfo/Generic/codeview-unnamed.c +++ b/clang/test/DebugInfo/Generic/codeview-unnamed.c @@ -8,23 +8,23 @@ int main(int argc, char* argv[], char* arge[]) { // struct { int bar; } one = {42}; // - // LINUX: !{{[0-9]+}} = !DILocalVariable(name: "one" - // LINUX-SAME: type: [[TYPE_OF_ONE:![0-9]+]] - // LINUX-SAME: ) - // LINUX: [[TYPE_OF_ONE]] = distinct !DICompositeType( + // LINUX: [[TYPE_OF_ONE:![0-9]+]] = distinct !DICompositeType( // LINUX-SAME: tag: DW_TAG_structure_type // LINUX-NOT: name: // LINUX-NOT: identifier: // LINUX-SAME: ) + // LINUX: !{{[0-9]+}} = !DILocalVariable(name: "one" + // LINUX-SAME: type: [[TYPE_OF_ONE]] + // LINUX-SAME: ) // - // MSVC: !{{[0-9]+}} = !DILocalVariable(name: "one" - // MSVC-SAME: type: [[TYPE_OF_ONE:![0-9]+]] - // MSVC-SAME: ) - // MSVC: [[TYPE_OF_ONE]] = distinct !DICompositeType + // MSVC: [[TYPE_OF_ONE:![0-9]+]] = distinct !DICompositeType // MSVC-SAME: tag: DW_TAG_structure_type // MSVC-NOT: name: // MSVC-NOT: identifier: // MSVC-SAME: ) + // MSVC: !{{[0-9]+}} = !DILocalVariable(name: "one" + // MSVC-SAME: type: [[TYPE_OF_ONE]] + // MSVC-SAME: ) return 0; } diff --git a/clang/test/DebugInfo/Generic/unused-types.c b/clang/test/DebugInfo/Generic/unused-types.c index 3e9f7b07658e3..31d608d92a06b 100644 --- a/clang/test/DebugInfo/Generic/unused-types.c +++ b/clang/test/DebugInfo/Generic/unused-types.c @@ -18,13 +18,15 @@ void quux(void) { // CHECK: !DICompileUnit{{.+}}retainedTypes: [[RETTYPES:![0-9]+]] // CHECK: [[TYPE0:![0-9]+]] = !DICompositeType(tag: DW_TAG_enumeration_type, name: "bar" // CHECK: [[TYPE1:![0-9]+]] = !DIEnumerator(name: "BAR" -// CHECK: [[TYPE2:![0-9]+]] = !DICompositeType(tag: DW_TAG_enumeration_type, name: "z" -// CHECK: [[TYPE3:![0-9]+]] = !DIEnumerator(name: "Z" -// CHECK: [[RETTYPES]] = !{[[TYPE4:![0-9]+]], [[TYPE5:![0-9]+]], [[TYPE0]], [[TYPE6:![0-9]+]], {{![0-9]+}}, [[TYPE7:![0-9]+]], [[TYPE2]], [[TYPE8:![0-9]+]]} -// CHECK: [[TYPE4]] = !DIDerivedType(tag: DW_TAG_typedef, name: "my_int" -// CHECK: [[TYPE5]] = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "foo" -// CHECK: [[TYPE6]] = distinct !DICompositeType(tag: DW_TAG_union_type, name: "baz" -// CHECK: [[TYPE7]] = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "y" +// CHECK: [[RETTYPES]] = !{[[TYPE2:![0-9]+]], [[TYPE3:![0-9]+]], [[TYPE0]], [[TYPE4:![0-9]+]], {{![0-9]+}}} +// CHECK: [[TYPE2]] = !DIDerivedType(tag: DW_TAG_typedef, name: "my_int" +// CHECK: [[TYPE3]] = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "foo" +// CHECK: [[TYPE4]] = distinct !DICompositeType(tag: DW_TAG_union_type, name: "baz" +// CHECK: [[SP:![0-9]+]] = distinct !DISubprogram(name: "quux", {{.*}}, retainedNodes: [[SPRETNODES:![0-9]+]] +// CHECK: [[SPRETNODES]] = !{[[TYPE5:![0-9]+]], [[TYPE6:![0-9]+]], [[TYPE8:![0-9]+]]} +// CHECK: [[TYPE5]] = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "y" +// CHECK: [[TYPE6]] = !DICompositeType(tag: DW_TAG_enumeration_type, name: "z" +// CHECK: [[TYPE7:![0-9]+]] = !DIEnumerator(name: "Z" // CHECK: [[TYPE8]] = distinct !DICompositeType(tag: DW_TAG_union_type, name: "w" // Check that debug info is not emitted for the typedef, struct, enum, and diff --git a/clang/test/DebugInfo/Generic/unused-types.cpp b/clang/test/DebugInfo/Generic/unused-types.cpp index 023cac159faa4..5b01c6dbb3941 100644 --- a/clang/test/DebugInfo/Generic/unused-types.cpp +++ b/clang/test/DebugInfo/Generic/unused-types.cpp @@ -13,12 +13,14 @@ void quux() { // CHECK: !DICompileUnit{{.+}}retainedTypes: [[RETTYPES:![0-9]+]] // CHECK: [[TYPE0:![0-9]+]] = !DICompositeType(tag: DW_TAG_enumeration_type, name: "baz" // CHECK: [[TYPE1:![0-9]+]] = !DIEnumerator(name: "BAZ" -// CHECK: [[TYPE2:![0-9]+]] = !DICompositeType(tag: DW_TAG_enumeration_type, name: "z" -// CHECK: [[TYPE3:![0-9]+]] = !DIEnumerator(name: "Z" -// CHECK: [[RETTYPES]] = !{[[TYPE4:![0-9]+]], [[TYPE5:![0-9]+]], [[TYPE0]], {{![0-9]+}}, [[TYPE6:![0-9]+]], [[TYPE2]]} -// CHECK: [[TYPE4]] = !DIDerivedType(tag: DW_TAG_typedef, name: "foo" -// CHECK: [[TYPE5]] = distinct !DICompositeType(tag: DW_TAG_class_type, name: "bar" -// CHECK: [[TYPE6]] = distinct !DICompositeType(tag: DW_TAG_class_type, name: "y" +// CHECK: [[RETTYPES]] = !{[[TYPE2:![0-9]+]], [[TYPE3:![0-9]+]], [[TYPE0]], {{![0-9]+}}} +// CHECK: [[TYPE2]] = !DIDerivedType(tag: DW_TAG_typedef, name: "foo" +// CHECK: [[TYPE3]] = distinct !DICompositeType(tag: DW_TAG_class_type, name: "bar" +// CHECK: [[SP:![0-9]+]] = distinct !DISubprogram(name: "quux", {{.*}}, retainedNodes: [[SPRETNODES:![0-9]+]] +// CHECK: [[SPRETNODES]] = !{[[TYPE4:![0-9]+]], [[TYPE5:![0-9]+]]} +// CHECK: [[TYPE4]] = distinct !DICompositeType(tag: DW_TAG_class_type, name: "y", scope: [[SP]] +// CHECK: [[TYPE5]] = !DICompositeType(tag: DW_TAG_enumeration_type, name: "z", scope: [[SP]] +// CHECK: [[TYPE6:![0-9]+]] = !DIEnumerator(name: "Z" // NODBG-NOT: !DI{{CompositeType|Enumerator|DerivedType}} diff --git a/llvm/include/llvm/AsmParser/LLParser.h b/llvm/include/llvm/AsmParser/LLParser.h index 9eb31d7e0a451..ae568f8ead039 100644 --- a/llvm/include/llvm/AsmParser/LLParser.h +++ b/llvm/include/llvm/AsmParser/LLParser.h @@ -181,6 +181,10 @@ namespace llvm { /// Keeps track of source locations for Values, BasicBlocks, and Functions. AsmParserContext *ParserContext; + /// retainedNodes of these subprograms should be cleaned up from incorrectly + /// scoped local types. + SmallVector 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 f3839c9694f34..ce714d0388323 100644 --- a/llvm/include/llvm/IR/DIBuilder.h +++ b/llvm/include/llvm/IR/DIBuilder.h @@ -49,7 +49,7 @@ namespace llvm { DICompileUnit *CUNode; ///< The one compile unit created by this DIBuiler. - SmallVector AllEnumTypes; + SmallVector EnumTypes; /// Track the RetainTypes, since they can be updated later on. SmallVector AllRetainTypes; SmallVector AllSubprograms; @@ -64,8 +64,8 @@ namespace llvm { SmallVector 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 c626efc9daaa4..e16b08d122a65 100644 --- a/llvm/include/llvm/IR/DebugInfoMetadata.h +++ b/llvm/include/llvm/IR/DebugInfoMetadata.h @@ -2536,6 +2536,21 @@ class DISubprogram : public DILocalScope { replaceOperandWith(7, N.get()); } + /// Remove types that do not belong to the subprogram's scope from + /// retainedNodes list. + void cleanupRetainedNodes(); + + /// When DebugTypeODRUniquing is enabled, after multiple modules are loaded, + /// some subprograms (that are from different compilation units, usually) + /// may have references to the same local type in their retainedNodes lists. + /// + /// Clean up such references. + template + static void cleanupRetainedNodes(const RangeT &NewDistinctSPs) { + for (DISubprogram *SP : NewDistinctSPs) + SP->cleanupRetainedNodes(); + } + /// Check if this subprogram describes the given function. /// /// FIXME: Should this be looking through bitcasts? diff --git a/llvm/lib/AsmParser/LLParser.cpp b/llvm/lib/AsmParser/LLParser.cpp index 5164cec33e6f5..3d06a6c1d0da9 100644 --- a/llvm/lib/AsmParser/LLParser.cpp +++ b/llvm/lib/AsmParser/LLParser.cpp @@ -54,6 +54,7 @@ #include #include #include +#include #include using namespace llvm; @@ -428,6 +429,8 @@ bool LLParser::validateEndOfModule(bool UpgradeDebugInfo) { N.second->resolveCycles(); } + DISubprogram::cleanupRetainedNodes(std::exchange(NewDistinctSPs, {})); + for (auto *Inst : InstsWithTBAATag) { MDNode *MD = Inst->getMetadata(LLVMContext::MD_tbaa); // With incomplete IR, the tbaa metadata may have been dropped. @@ -5987,6 +5990,10 @@ bool LLParser::parseDISubprogram(MDNode *&Result, bool IsDistinct) { thisAdjustment.Val, flags.Val, SPFlags, unit.Val, templateParams.Val, declaration.Val, retainedNodes.Val, thrownTypes.Val, annotations.Val, targetFuncName.Val, keyInstructions.Val)); + + if (IsDistinct) + NewDistinctSPs.push_back(cast(Result)); + return false; } diff --git a/llvm/lib/Bitcode/Reader/MetadataLoader.cpp b/llvm/lib/Bitcode/Reader/MetadataLoader.cpp index ed0443f599a44..b4ca5ef4eaf26 100644 --- a/llvm/lib/Bitcode/Reader/MetadataLoader.cpp +++ b/llvm/lib/Bitcode/Reader/MetadataLoader.cpp @@ -116,6 +116,8 @@ class BitcodeReaderMetadataList { RefsUpperBound(std::min((size_t)std::numeric_limits::max(), RefsUpperBound)) {} + using const_iterator = SmallVector::const_iterator; + // vector compatibility methods unsigned size() const { return MetadataPtrs.size(); } void resize(unsigned N) { MetadataPtrs.resize(N); } @@ -124,6 +126,8 @@ class BitcodeReaderMetadataList { Metadata *back() const { return MetadataPtrs.back(); } void pop_back() { MetadataPtrs.pop_back(); } bool empty() const { return MetadataPtrs.empty(); } + const_iterator begin() { return MetadataPtrs.begin(); } + const_iterator end() { return MetadataPtrs.end(); } Metadata *operator[](unsigned i) const { return MetadataPtrs[i]; } @@ -448,6 +452,10 @@ class MetadataLoader::MetadataLoaderImpl { /// metadata. SmallDenseMap FunctionsWithSPs; + /// retainedNodes of these subprograms should be cleaned up from incorrectly + /// scoped local types. + SmallVector NewDistinctSPs; + // Map the bitcode's custom MDKind ID to the Module's MDKind ID. DenseMap MDKindMap; @@ -538,6 +546,8 @@ class MetadataLoader::MetadataLoaderImpl { /// Move local imports from DICompileUnit's 'imports' field to /// DISubprogram's retainedNodes. + /// Move function-local enums from DICompileUnit's enums + /// to DISubprogram's retainedNodes. void upgradeCULocals() { if (NamedMDNode *CUNodes = TheModule.getNamedMetadata("llvm.dbg.cu")) { for (MDNode *N : CUNodes->operands()) { @@ -545,48 +555,66 @@ class MetadataLoader::MetadataLoaderImpl { if (!CU) continue; - if (CU->getRawImportedEntities()) { - // Collect a set of imported entities to be moved. - SetVector EntitiesToRemove; + SetVector MetadataToRemove; + // Collect imported entities to be moved. + if (CU->getRawImportedEntities()) for (Metadata *Op : CU->getImportedEntities()->operands()) { auto *IE = cast(Op); - if (isa_and_nonnull(IE->getScope())) { - EntitiesToRemove.insert(IE); - } + if (isa_and_nonnull(IE->getScope())) + MetadataToRemove.insert(IE); + } + // Collect enums to be moved. + if (CU->getRawEnumTypes()) + for (Metadata *Op : CU->getEnumTypes()->operands()) { + auto *Enum = cast(Op); + if (isa_and_nonnull(Enum->getScope())) + MetadataToRemove.insert(Enum); } - if (!EntitiesToRemove.empty()) { - // Make a new list of CU's 'imports'. - SmallVector NewImports; - for (Metadata *Op : CU->getImportedEntities()->operands()) { - if (!EntitiesToRemove.contains(cast(Op))) { + if (!MetadataToRemove.empty()) { + // Make a new list of CU's 'imports'. + SmallVector NewImports; + if (CU->getRawImportedEntities()) + for (Metadata *Op : CU->getImportedEntities()->operands()) + if (!MetadataToRemove.contains(Op)) NewImports.push_back(Op); - } - } - // Find DISubprogram corresponding to each entity. - std::map> SPToEntities; - for (auto *I : EntitiesToRemove) { - auto *Entity = cast(I); - if (auto *SP = findEnclosingSubprogram( - cast(Entity->getScope()))) { - SPToEntities[SP].push_back(Entity); - } - } + // Make a new list of CU's 'enums'. + SmallVector NewEnums; + if (CU->getRawEnumTypes()) + for (Metadata *Op : CU->getEnumTypes()->operands()) + if (!MetadataToRemove.contains(Op)) + NewEnums.push_back(Op); + + // Find DISubprogram corresponding to each entity. + std::map> SPToEntities; + for (auto *I : MetadataToRemove) { + DILocalScope *Scope = nullptr; + if (auto *Entity = dyn_cast(I)) + Scope = cast(Entity->getScope()); + else if (auto *Enum = dyn_cast(I)) + Scope = cast(Enum->getScope()); + + if (auto *SP = findEnclosingSubprogram(Scope)) + SPToEntities[SP].push_back(I); + } - // Update DISubprograms' retainedNodes. - for (auto I = SPToEntities.begin(); I != SPToEntities.end(); ++I) { - auto *SP = I->first; - auto RetainedNodes = SP->getRetainedNodes(); - SmallVector MDs(RetainedNodes.begin(), - RetainedNodes.end()); - MDs.append(I->second); - SP->replaceRetainedNodes(MDNode::get(Context, MDs)); - } + // Update DISubprograms' retainedNodes. + for (auto I = SPToEntities.begin(); I != SPToEntities.end(); ++I) { + auto *SP = I->first; + auto RetainedNodes = SP->getRetainedNodes(); + SmallVector MDs(RetainedNodes.begin(), + RetainedNodes.end()); + MDs.append(I->second); + SP->replaceRetainedNodes(MDNode::get(Context, MDs)); + } - // Remove entities with local scope from CU. + // Remove entities with local scope from CU. + if (CU->getRawImportedEntities()) CU->replaceImportedEntities(MDTuple::get(Context, NewImports)); - } + // Remove enums with local scope from CU. + if (CU->getRawEnumTypes()) + CU->replaceEnumTypes(MDTuple::get(Context, NewEnums)); } } } @@ -708,6 +736,7 @@ class MetadataLoader::MetadataLoaderImpl { upgradeCUVariables(); if (ModuleLevel) upgradeCULocals(); + DISubprogram::cleanupRetainedNodes(std::exchange(NewDistinctSPs, {})); } void callMDTypeCallback(Metadata **Val, unsigned TypeID); @@ -1999,6 +2028,9 @@ Error MetadataLoader::MetadataLoaderImpl::parseOneMetadata( MetadataList.assignValue(SP, NextMetadataNo); NextMetadataNo++; + if (IsDistinct) + NewDistinctSPs.push_back(SP); + // Upgrade sp->function mapping to function->sp mapping. if (HasFn) { if (auto *CMD = dyn_cast_or_null(CUorFn)) diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp index 518121e200190..00f41c873cb53 100644 --- a/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp @@ -642,10 +642,9 @@ void DwarfCompileUnit::constructScopeDIE(LexicalScope *Scope, return; // Emit lexical blocks. - DIE *ScopeDIE = constructLexicalScopeDIE(Scope); + DIE *ScopeDIE = getOrCreateLexicalBlockDIE(Scope, ParentScopeDIE); assert(ScopeDIE && "Scope DIE should not be null."); - ParentScopeDIE.addChild(ScopeDIE); createAndAddScopeChildren(Scope, *ScopeDIE); } @@ -766,26 +765,41 @@ DIE *DwarfCompileUnit::constructInlinedScopeDIE(LexicalScope *Scope, return ScopeDIE; } -// Construct new DW_TAG_lexical_block for this scope and attach -// DW_AT_low_pc/DW_AT_high_pc labels. -DIE *DwarfCompileUnit::constructLexicalScopeDIE(LexicalScope *Scope) { +DIE *DwarfCompileUnit::getOrCreateLexicalBlockDIE(LexicalScope *Scope, + DIE &ParentScopeDIE) { if (DD->isLexicalScopeDIENull(Scope)) return nullptr; const auto *DS = Scope->getScopeNode(); - - auto ScopeDIE = DIE::get(DIEValueAllocator, dwarf::DW_TAG_lexical_block); - if (Scope->isAbstractScope()) { - assert(!getAbstractScopeDIEs().count(DS) && - "Abstract DIE for this scope exists!"); - getAbstractScopeDIEs()[DS] = ScopeDIE; - return ScopeDIE; + DIE *ScopeDIE = nullptr; + + // FIXME: We may have a concrete DIE for this scope already created. + // This may happen when we emit local variables for an abstract tree of + // an inlined function: if a local variable has a templated type with + // a function-local type as a template parameter. See PR55680 for details + // (see also llvm/test/DebugInfo/Generic/local-type-as-template-parameter.ll). + if (!Scope->isAbstractScope() && !Scope->getInlinedAt()) { + if (auto It = LexicalBlockDIEs.find(DS); It != LexicalBlockDIEs.end()) { + ScopeDIE = It->second; + assert(!ScopeDIE->findAttribute(dwarf::DW_AT_low_pc) && + !ScopeDIE->findAttribute(dwarf::DW_AT_ranges)); + assert(ScopeDIE->getParent() == &ParentScopeDIE); + } } - if (!Scope->getInlinedAt()) { - assert(!LexicalBlockDIEs.count(DS) && - "Concrete out-of-line DIE for this scope exists!"); - LexicalBlockDIEs[DS] = ScopeDIE; - } else { - InlinedLocalScopeDIEs[DS].push_back(ScopeDIE); + if (!ScopeDIE) { + ScopeDIE = DIE::get(DIEValueAllocator, dwarf::DW_TAG_lexical_block); + ParentScopeDIE.addChild(ScopeDIE); + + if (Scope->isAbstractScope()) { + assert(!getAbstractScopeDIEs().count(DS) && + "Abstract DIE for this scope exists!"); + getAbstractScopeDIEs()[DS] = ScopeDIE; + return ScopeDIE; + } + + if (!Scope->getInlinedAt()) + LexicalBlockDIEs[DS] = ScopeDIE; + else + InlinedLocalScopeDIEs[DS].push_back(ScopeDIE); } attachRangesOrLowHighPC(*ScopeDIE, Scope->getRanges()); @@ -1800,7 +1814,7 @@ void DwarfCompileUnit::createBaseTypeDIEs() { } } -DIE *DwarfCompileUnit::getLexicalBlockDIE(const DILexicalBlock *LB) { +DIE *DwarfCompileUnit::getLocalContextDIE(const DILexicalBlock *LB) { // Assume if there is an abstract tree all the DIEs are already emitted. bool isAbstract = getAbstractScopeDIEs().count(LB->getSubprogram()); if (isAbstract) { @@ -1810,8 +1824,14 @@ DIE *DwarfCompileUnit::getLexicalBlockDIE(const DILexicalBlock *LB) { } assert(!isAbstract && "Missed lexical block DIE in abstract tree!"); - // Return a concrete DIE if it exists or nullptr otherwise. - return LexicalBlockDIEs.lookup(LB); + // Check if we have a concrete DIE. + if (auto It = LexicalBlockDIEs.find(LB); It != LexicalBlockDIEs.end()) + return It->second; + + // If nothing available found, we cannot just create a new lexical block, + // because it isn't known where to put it into the DIE tree. + // So, we may only try to find the most close avaiable parent DIE. + return getOrCreateContextDIE(LB->getScope()->getNonLexicalBlockFileScope()); } DIE *DwarfCompileUnit::getOrCreateContextDIE(const DIScope *Context) { @@ -1819,7 +1839,7 @@ DIE *DwarfCompileUnit::getOrCreateContextDIE(const DIScope *Context) { if (auto *LFScope = dyn_cast(Context)) Context = LFScope->getNonLexicalBlockFileScope(); if (auto *LScope = dyn_cast(Context)) - return getLexicalBlockDIE(LScope); + return getLocalContextDIE(LScope); // Otherwise the context must be a DISubprogram. auto *SPScope = cast(Context); diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.h b/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.h index a3bbc8364599d..a705039eb486b 100644 --- a/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.h +++ b/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.h @@ -251,14 +251,9 @@ class DwarfCompileUnit final : public DwarfUnit { /// DIE to represent this concrete inlined copy of the function. DIE *constructInlinedScopeDIE(LexicalScope *Scope, DIE &ParentScopeDIE); - /// Construct new DW_TAG_lexical_block for this scope and - /// attach DW_AT_low_pc/DW_AT_high_pc labels. - DIE *constructLexicalScopeDIE(LexicalScope *Scope); - - /// Get a DIE for the given DILexicalBlock. - /// Note that this function assumes that the DIE has been already created - /// and it's an error, if it hasn't. - DIE *getLexicalBlockDIE(const DILexicalBlock *LB); + /// Get if available or create a new DW_TAG_lexical_block for the given + /// LexicalScope and attach DW_AT_low_pc/DW_AT_high_pc labels. + DIE *getOrCreateLexicalBlockDIE(LexicalScope *Scope, DIE &ParentDIE); /// Construct a DIE for the given DbgVariable. DIE *constructVariableDIE(DbgVariable &DV, bool Abstract = false); @@ -277,6 +272,11 @@ class DwarfCompileUnit final : public DwarfUnit { /// This instance of 'getOrCreateContextDIE()' can handle DILocalScope. DIE *getOrCreateContextDIE(const DIScope *Ty) override; + /// Get DW_TAG_lexical_block for the given DILexicalBlock if available, + /// or the most close parent DIE, if no correspoding DW_TAG_lexical_block + /// exists. + DIE *getLocalContextDIE(const DILexicalBlock *LB); + DIE *getOrCreateSubprogramDIE(const DISubprogram *SP, const Function *F, bool Minimal = false) override; diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp index 567acf75d1b8d..3096622da1d5a 100644 --- a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp @@ -1245,12 +1245,13 @@ void DwarfDebug::beginModule(Module *M) { CU.getOrCreateGlobalVariableDIE(GV, sortGlobalExprs(GVMap[GV])); } - for (auto *Ty : CUNode->getEnumTypes()) + for (auto *Ty : CUNode->getEnumTypes()) { + assert(!isa_and_nonnull(Ty->getScope()) && + "Unexpected function-local entity in 'enums' CU field."); CU.getOrCreateTypeDIE(cast(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(Ty)) // There is no point in force-emitting a forward declaration. CU.getOrCreateTypeDIE(RT); @@ -1451,9 +1452,13 @@ void DwarfDebug::endModule() { "Unexpected function-local entity in 'imports' CU field."); CU->getOrCreateImportedEntityDIE(IE); } + + // Emit function-local entities. for (const auto *D : CU->getDeferredLocalDecls()) { if (auto *IE = dyn_cast(D)) CU->getOrCreateImportedEntityDIE(IE); + else if (auto *Ty = dyn_cast(D)) + CU->getOrCreateTypeDIE(Ty); else llvm_unreachable("Unexpected local retained node!"); } @@ -1551,6 +1556,8 @@ static const DILocalScope *getRetainedNodeScope(const MDNode *N) { S = L->getScope(); else if (const auto *IE = dyn_cast(N)) S = IE->getScope(); + else if (const auto *T = dyn_cast(N)) + S = T->getScope(); else llvm_unreachable("Unexpected retained node!"); diff --git a/llvm/lib/IR/DIBuilder.cpp b/llvm/lib/IR/DIBuilder.cpp index 07a870f0630a5..2ee021e92415a 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(AllEnumTypes.begin(), - AllEnumTypes.end()))); + if (!EnumTypes.empty()) + CUNode->replaceEnumTypes( + MDTuple::get(VMContext, SmallVector(EnumTypes.begin(), + EnumTypes.end()))); SmallVector RetainValues; // Declarations and definitions of the same type may be retained. Some @@ -377,10 +377,13 @@ DIDerivedType *DIBuilder::createTypedef(DIType *Ty, StringRef Name, DIScope *Context, uint32_t AlignInBits, DINode::DIFlags Flags, DINodeArray Annotations) { - return DIDerivedType::get(VMContext, dwarf::DW_TAG_typedef, Name, File, - LineNo, getNonCompileUnitScope(Context), Ty, - (uint64_t)0, AlignInBits, (uint64_t)0, std::nullopt, - std::nullopt, Flags, nullptr, Annotations); + auto *T = DIDerivedType::get( + VMContext, dwarf::DW_TAG_typedef, Name, File, LineNo, + getNonCompileUnitScope(Context), Ty, (uint64_t)0, AlignInBits, + (uint64_t)0, std::nullopt, std::nullopt, Flags, nullptr, Annotations); + if (isa_and_nonnull(Context)) + getSubprogramNodesTrackingVector(Context).emplace_back(T); + return T; } DIDerivedType * @@ -388,10 +391,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(Context)) + getSubprogramNodesTrackingVector(Context).emplace_back(T); + return T; } DIDerivedType *DIBuilder::createFriend(DIType *Ty, DIType *FriendTy) { @@ -582,6 +589,8 @@ DICompositeType *DIBuilder::createClassType( OffsetInBits, Flags, Elements, RunTimeLang, /*EnumKind=*/std::nullopt, VTableHolder, cast_or_null(TemplateParams), UniqueIdentifier); trackIfUnresolved(R); + if (isa_and_nonnull(Context)) + getSubprogramNodesTrackingVector(Context).emplace_back(R); return R; } @@ -598,6 +607,8 @@ DICompositeType *DIBuilder::createStructType( nullptr, UniqueIdentifier, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, Specification, NumExtraInhabitants); trackIfUnresolved(R); + if (isa_and_nonnull(Context)) + getSubprogramNodesTrackingVector(Context).emplace_back(R); return R; } @@ -614,6 +625,8 @@ DICompositeType *DIBuilder::createStructType( nullptr, UniqueIdentifier, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, Specification, NumExtraInhabitants); trackIfUnresolved(R); + if (isa_and_nonnull(Context)) + getSubprogramNodesTrackingVector(Context).emplace_back(R); return R; } @@ -627,6 +640,8 @@ DICompositeType *DIBuilder::createUnionType( Elements, RunTimeLang, /*EnumKind=*/std::nullopt, nullptr, nullptr, UniqueIdentifier); trackIfUnresolved(R); + if (isa_and_nonnull(Scope)) + getSubprogramNodesTrackingVector(Scope).emplace_back(R); return R; } @@ -661,7 +676,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(Scope)) + getSubprogramNodesTrackingVector(Scope).emplace_back(CTy); + else + EnumTypes.emplace_back(CTy); trackIfUnresolved(CTy); return CTy; } @@ -675,6 +693,8 @@ DIDerivedType *DIBuilder::createSetType(DIScope *Scope, StringRef Name, SizeInBits, AlignInBits, 0, std::nullopt, std::nullopt, DINode::FlagZero); trackIfUnresolved(R); + if (isa_and_nonnull(Scope)) + getSubprogramNodesTrackingVector(Scope).emplace_back(R); return R; } @@ -710,6 +730,8 @@ DICompositeType *DIBuilder::createArrayType( : (Metadata *)cast(RK), nullptr, nullptr, 0, BitStride); trackIfUnresolved(R); + if (isa_and_nonnull(Scope)) + getSubprogramNodesTrackingVector(Scope).emplace_back(R); return R; } @@ -768,7 +790,8 @@ void DIBuilder::retainType(DIScope *T) { assert((isa(T) || (isa(T) && cast(T)->isDefinition() == false)) && "Expected type or subprogram declaration"); - AllRetainTypes.emplace_back(T); + if (!isa_and_nonnull(T->getScope())) + AllRetainTypes.emplace_back(T); } DIBasicType *DIBuilder::createUnspecifiedParameter() { return nullptr; } @@ -784,6 +807,8 @@ DICompositeType *DIBuilder::createForwardDecl( SizeInBits, AlignInBits, 0, DINode::FlagFwdDecl, nullptr, RuntimeLang, /*EnumKind=*/EnumKind, nullptr, nullptr, UniqueIdentifier); trackIfUnresolved(RetTy); + if (isa_and_nonnull(Scope)) + getSubprogramNodesTrackingVector(Scope).emplace_back(RetTy); return RetTy; } @@ -800,6 +825,8 @@ DICompositeType *DIBuilder::createReplaceableCompositeType( nullptr, nullptr, Annotations) .release(); trackIfUnresolved(RetTy); + if (isa_and_nonnull(Scope)) + getSubprogramNodesTrackingVector(Scope).emplace_back(RetTy); return RetTy; } @@ -859,9 +886,12 @@ DISubrangeType *DIBuilder::createSubrangeType( uint64_t SizeInBits, uint32_t AlignInBits, DINode::DIFlags Flags, DIType *Ty, Metadata *LowerBound, Metadata *UpperBound, Metadata *Stride, Metadata *Bias) { - return DISubrangeType::get(VMContext, Name, File, LineNo, Scope, SizeInBits, - AlignInBits, Flags, Ty, LowerBound, UpperBound, - Stride, Bias); + auto *T = DISubrangeType::get(VMContext, Name, File, LineNo, Scope, + SizeInBits, AlignInBits, Flags, Ty, LowerBound, + UpperBound, Stride, Bias); + if (isa_and_nonnull(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 58836068a4929..7d2a932d773b2 100644 --- a/llvm/lib/IR/DebugInfo.cpp +++ b/llvm/lib/IR/DebugInfo.cpp @@ -312,6 +312,8 @@ void DebugInfoFinder::processSubprogram(DISubprogram *SP) { processVariable(Var); else if (auto *Import = dyn_cast_or_null(N)) processImportedEntity(Import); + else if (auto *T = dyn_cast_or_null(N)) + processType(T); } } diff --git a/llvm/lib/IR/DebugInfoMetadata.cpp b/llvm/lib/IR/DebugInfoMetadata.cpp index e30df88e6b56b..5d268f16d38dd 100644 --- a/llvm/lib/IR/DebugInfoMetadata.cpp +++ b/llvm/lib/IR/DebugInfoMetadata.cpp @@ -1425,6 +1425,45 @@ bool DISubprogram::describes(const Function *F) const { assert(F && "Invalid function"); return F->getSubprogram() == this; } + +void DISubprogram::cleanupRetainedNodes() { + // Checks if a metadata node from retainedTypes is a type not belonging to + // this subprogram. + auto IsAlienType = [this](DINode *N) { + auto *T = dyn_cast_or_null(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(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(getRawRetainedNodes())) + return; + + MDTuple *RetainedNodes = cast(getRawRetainedNodes()); + SmallVector MDs; + MDs.reserve(RetainedNodes->getNumOperands()); + for (const MDOperand &Node : RetainedNodes->operands()) { + // Ignore malformed retainedNodes. + if (Node && !isa(Node)) + return; + + auto *N = cast_or_null(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 Ops) diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp index 7917712846990..dc29218516359 100644 --- a/llvm/lib/IR/Verifier.cpp +++ b/llvm/lib/IR/Verifier.cpp @@ -1564,10 +1564,20 @@ void Verifier::visitDISubprogram(const DISubprogram &N) { CheckDI(Node, "invalid retained nodes list", &N, RawNode); for (Metadata *Op : Node->operands()) { CheckDI(Op && (isa(Op) || isa(Op) || - isa(Op)), - "invalid retained nodes, expected DILocalVariable, DILabel or " - "DIImportedEntity", + isa(Op) || isa(Op)), + "invalid retained nodes, expected DILocalVariable, DILabel, " + "DIImportedEntity or DIType", &N, Node, Op); + if (auto *T = dyn_cast(Op)) { + auto *TypeScope = dyn_cast_or_null(T->getScope()); + DISubprogram *TypeSP = TypeScope ? TypeScope->getSubprogram() : nullptr; + DICompileUnit *TypeSPUnit = TypeSP ? TypeSP->getUnit() : nullptr; + if (isa_and_nonnull(T->getScope())) + CheckDI(!TypeScope || TypeSP == &N, + "invalid retained node, DIType should have the scope of " + "DISubprogram", + &N, N.getUnit(), Node, Op, TypeScope, TypeSP, TypeSPUnit); + } } } CheckDI(!hasConflictingReferenceFlags(N.getFlags()), diff --git a/llvm/lib/Transforms/Utils/CloneFunction.cpp b/llvm/lib/Transforms/Utils/CloneFunction.cpp index 32924e7b69fdc..4c733ae88251c 100644 --- a/llvm/lib/Transforms/Utils/CloneFunction.cpp +++ b/llvm/lib/Transforms/Utils/CloneFunction.cpp @@ -87,10 +87,23 @@ createIdentityMDPredicate(const Function &F, CloneFunctionChangeType Changes) { }; return [=](const Metadata *MD) { - // Avoid cloning types, compile units, and (other) subprograms. - if (isa(MD) || isa(MD)) + // Avoid cloning compile units. + if (isa(MD)) return true; + // Clone no types but local + if (auto *Type = dyn_cast(MD)) { + if (SPClonedWithinModule == nullptr) + return true; + + auto *LScope = dyn_cast_or_null(Type->getScope()); + if (!LScope) + return true; + + if (ShouldKeep(LScope->getSubprogram())) + return true; + } + if (auto *SP = dyn_cast(MD)) return ShouldKeep(SP); diff --git a/llvm/test/Bitcode/local-type-scope.ll b/llvm/test/Bitcode/local-type-scope.ll new file mode 100644 index 0000000000000..7652093409ac3 --- /dev/null +++ b/llvm/test/Bitcode/local-type-scope.ll @@ -0,0 +1,48 @@ +; RUN: llvm-as --disable-verify < %s -o %t0 +; RUN: opt --passes=verify %t0 -o /dev/null +; RUN: llvm-dis %t0 -o - | FileCheck %s --implicit-check-not=DICompositeType + +; During module loading, if a local type appears in retainedNodes +; field of multiple DISubprograms due to ODR-uniquing, +; retainedNodes should be cleaned up, so that only one DISubprogram +; will have this type in its retainedNodes. + +; CHECK: [[CU:![0-9]+]] = distinct !DICompileUnit +; CHECK: [[BAR:![0-9]+]] = distinct !DISubprogram(name: "bar", {{.*}}, retainedNodes: [[RN_BAR:![0-9]+]]) +; CHECK: [[RN_BAR]] = !{} +; CHECK: [[FOO:![0-9]+]] = distinct !DISubprogram(name: "foo", {{.*}}, retainedNodes: [[RN_FOO:![0-9]+]]) +; CHECK: [[RN_FOO]] = !{[[T1:![0-9]+]]} +; CHECK: [[T1]] = !DICompositeType(tag: DW_TAG_class_type, scope: [[FOO]], {{.*}}, identifier: "local_type") + +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +declare void @foo() + +define void @bar(ptr %this) !dbg !10 { + call void @foo(), !dbg !17 + ret void, !dbg !13 +} + +!llvm.module.flags = !{!0, !1, !2, !3, !4, !5, !6, !7} +!llvm.dbg.cu = !{!8} + +!0 = !{i32 2, !"SDK Version", [2 x i32] [i32 11, i32 4]} +!1 = !{i32 7, !"Dwarf Version", i32 2} +!2 = !{i32 2, !"Debug Info Version", i32 3} +!3 = !{i32 1, !"wchar_size", i32 4} +!4 = !{i32 7, !"openmp", i32 50} +!5 = !{i32 7, !"openmp-device", i32 50} +!6 = !{i32 7, !"PIC Level", i32 2} +!7 = !{i32 7, !"frame-pointer", i32 2} +!8 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !9, producer: "clang version 14.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None) +!9 = !DIFile(filename: "tmp.cpp", directory: "/tmp/") +!10 = distinct !DISubprogram(name: "bar", scope: !9, file: !9, line: 68, type: !12, scopeLine: 68, flags: DIFlagPrototyped, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition | DISPFlagOptimized, unit: !8, retainedNodes: !16) +!11 = distinct !DISubprogram(name: "foo", scope: !9, file: !9, line: 68, type: !12, scopeLine: 68, flags: DIFlagPrototyped, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition | DISPFlagOptimized, unit: !8, retainedNodes: !16) +!12 = !DISubroutineType(types: !15) +!13 = !DILocation(line: 69, column: 18, scope: !10) +!14 = !DICompositeType(tag: DW_TAG_class_type, scope: !11, file: !9, line: 210, size: 8, flags: DIFlagTypePassByValue, elements: !15, identifier: "local_type") +!15 = !{} +!16 = !{!14} +!17 = !DILocation(line: 69, column: 18, scope: !11, inlinedAt: !18) +!18 = !DILocation(line: 4, column: 1, scope: !10) diff --git a/llvm/test/Bitcode/upgrade-cu-locals.ll b/llvm/test/Bitcode/upgrade-cu-locals.ll index 9a590f0fc0774..7052ab379bea4 100644 --- a/llvm/test/Bitcode/upgrade-cu-locals.ll +++ b/llvm/test/Bitcode/upgrade-cu-locals.ll @@ -1,4 +1,4 @@ -; Test moving of local imports from DICompileUnit's 'imports' to DISubprogram's 'retainedNodes' +; Test moving of local imports/enums from DICompileUnit to DISubprogram's 'retainedNodes' ; ; RUN: llvm-dis -o - %s.bc | FileCheck %s @@ -31,31 +31,36 @@ attributes #0 = { nocallback nofree nosync nounwind speculatable willreturn memo ; CHECK: !4 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "t4" ; CHECK: !5 = !{} -; CHECK: !6 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !7, producer: "clang version 14.0.0", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, imports: !5, nameTableKind: GNU) - -; CHECK: !14 = distinct !DISubprogram(name: "main", scope: !7, file: !7, line: 2, type: !15, scopeLine: 2, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !6, retainedNodes: !18) -; CHECK: !18 = !{!19} -; CHECK: !19 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !20, entity: !23, -; CHECK: !20 = !DILexicalBlock(scope: !21, file: !7, line: 7, column: 35) -; CHECK: !21 = !DILexicalBlock(scope: !22, file: !7, line: 7, column: 35) -; CHECK: !22 = !DILexicalBlock(scope: !14, file: !7, line: 7, column: 35) -; CHECK: !23 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "t5", scope: !20, - -; CHECK: !25 = distinct !DISubprogram(name: "f1", linkageName: "_Z2f1v", scope: !1, file: !1, line: 3, type: !26, scopeLine: 3, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !28) -; CHECK: !28 = !{!29, !32, !34} -; CHECK: !29 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !25, entity: !30, -; CHECK: !30 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "t1", -; CHECK: !32 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !25, entity: !33, -; CHECK: !33 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "t2", -; CHECK: !34 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !25, entity: !35, -; CHECK: !35 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "t3", - -; CHECK: !40 = distinct !DISubprogram(name: "main2", scope: !7, file: !7, line: 10, type: !15, scopeLine: 10, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !6, retainedNodes: !41) -; CHECK: !41 = !{!42, !44} -; CHECK: !42 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !40, entity: !43, -; CHECK: !43 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "t6" -; CHECK: !44 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !40, entity: !45, -; CHECK: !45 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "t7", +; CHECK: !6 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !7, producer: "clang version 14.0.0", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !8, imports: !5, nameTableKind: GNU) +; CHECK: !7 = !DIFile(filename: "b.cpp" +; CHECK: !8 = !{!9} +; CHECK: !9 = !DICompositeType(tag: DW_TAG_enumeration_type, name: "Enum2", scope: !6, file: !7, line: 4, size: 8, align: 8, elements: !5) + +; CHECK: !16 = distinct !DISubprogram(name: "main", scope: !7, file: !7, line: 2, type: !17, scopeLine: 2, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !6, retainedNodes: !20) +; CHECK: !20 = !{!21} +; CHECK: !21 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !22, entity: !25, +; CHECK: !22 = !DILexicalBlock(scope: !23, file: !7, line: 7, column: 35) +; CHECK: !23 = !DILexicalBlock(scope: !24, file: !7, line: 7, column: 35) +; CHECK: !24 = !DILexicalBlock(scope: !16, file: !7, line: 7, column: 35) +; CHECK: !25 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "t5", scope: !22, + +; CHECK: !27 = distinct !DISubprogram(name: "f1", linkageName: "_Z2f1v", scope: !1, file: !1, line: 3, type: !28, scopeLine: 3, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !30) +; CHECK: !30 = !{!31, !34, !36} +; CHECK: !31 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !27, entity: !32, +; CHECK: !32 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "t1", +; CHECK: !34 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !27, entity: !35, +; CHECK: !35 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "t2", +; CHECK: !36 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !27, entity: !37, +; CHECK: !37 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "t3", + +; CHECK: !42 = distinct !DISubprogram(name: "main2", scope: !7, file: !7, line: 10, type: !17, scopeLine: 10, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !6, retainedNodes: !43) +; CHECK: !43 = !{!44, !46, !48, !49} +; CHECK: !44 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !42, entity: !45, +; CHECK: !45 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "t6" +; CHECK: !46 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !42, entity: !47, +; CHECK: !47 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "t7", +; CHECK: !48 = !DICompositeType(tag: DW_TAG_enumeration_type, name: "Enum", scope: !42, file: !7, line: 3, size: 8, align: 8, elements: !5) +; CHECK: !49 = !DICompositeType(tag: DW_TAG_enumeration_type, name: "Enum3", scope: !42, file: !7, line: 5, size: 8, align: 8, elements: !5) !0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang version 14.0.0", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, imports: !2, nameTableKind: GNU) @@ -82,7 +87,7 @@ attributes #0 = { nocallback nofree nosync nounwind speculatable willreturn memo ; Leave t4 in CU !14 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !0, entity: !15, file: !1, line: 3) !15 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "t4", scope: !0, file: !1, line: 1, size: 8, flags: DIFlagTypePassByValue, elements: !7, identifier: "_ZTSN2ns2t4E") -!16 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !17, producer: "clang version 14.0.0", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, imports: !18, nameTableKind: GNU) +!16 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !17, producer: "clang version 14.0.0", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, imports: !18, enums: !50, nameTableKind: GNU) !17 = !DIFile(filename: "b.cpp", directory: "/") !18 = !{!19, !28, !31} @@ -116,3 +121,12 @@ attributes #0 = { nocallback nofree nosync nounwind speculatable willreturn memo !41 = distinct !DILocation(line: 3, column: 3, scope: !23) !42 = !DILocation(line: 3, column: 41, scope: !4, inlinedAt: !41) !43 = !DILocation(line: 4, column: 1, scope: !23) + +!50 = !{!51, !52, !53} +; Move to main2 +!51 = !DICompositeType(tag: DW_TAG_enumeration_type, name: "Enum", scope: !29, file: !17, line: 3, size: 8, align: 8, elements: !7) +; Leave in b.cpp's CU +!52 = !DICompositeType(tag: DW_TAG_enumeration_type, name: "Enum2", scope: !16, file: !17, line: 4, size: 8, align: 8, elements: !7) +; Move to main2 +!53 = !DICompositeType(tag: DW_TAG_enumeration_type, name: "Enum3", scope: !29, file: !17, line: 5, size: 8, align: 8, elements: !7) + diff --git a/llvm/test/Bitcode/upgrade-cu-locals.ll.bc b/llvm/test/Bitcode/upgrade-cu-locals.ll.bc index 9d0ea8fd9a370..f73d05668af5b 100644 Binary files a/llvm/test/Bitcode/upgrade-cu-locals.ll.bc and b/llvm/test/Bitcode/upgrade-cu-locals.ll.bc 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 0000000000000..a6f7d294de928 --- /dev/null +++ b/llvm/test/DebugInfo/Generic/inlined-local-type.ll @@ -0,0 +1,128 @@ +; RUN: %llc_dwarf -O0 -filetype=obj < %s | llvm-dwarfdump -debug-info - | FileCheck --implicit-check-not "{{DW_TAG|NULL}}" %s +; REQUIRES: object-emission + +; inline __attribute__((always_inline)) +; int removed() { struct A {int i;}; struct A a; return a.i++; } +; +; __attribute__((always_inline)) +; int not_removed() { struct B {int i;}; struct B b; return b.i++; } +; +; int foo() { return removed() + not_removed(); }} + +; Ensure that function-local types have the correct subprogram parent even if +; those subprograms are inlined. + +; CHECK: DW_TAG_compile_unit +; CHECK: DW_TAG_subprogram +; CHECK: DW_AT_abstract_origin ({{0x.*}} "not_removed") +; CHECK: DW_TAG_variable +; CHECK: NULL +; CHECK: DW_TAG_subprogram +; CHECK: DW_AT_name ("removed") +; CHECK: [[A:0x.*]]: DW_TAG_structure_type +; CHECK: DW_AT_name ("A") +; CHECK: DW_TAG_member +; CHECK: NULL +; CHECK: DW_TAG_variable +; CHECK: DW_AT_type ([[A]] "A") +; CHECK: NULL +; CHECK: DW_TAG_base_type +; CHECK: DW_TAG_subprogram +; CHECK: DW_AT_name ("not_removed") +; CHECK: [[B:0x.*]]: DW_TAG_structure_type +; CHECK: DW_AT_name ("B") +; CHECK: DW_TAG_member +; CHECK: NULL +; CHECK: DW_TAG_variable +; CHECK: DW_AT_type ([[B]] "B") +; CHECK: NULL +; CHECK: DW_TAG_subprogram +; CHECK: DW_TAG_inlined_subroutine +; CHECK: DW_TAG_variable +; CHECK: NULL +; CHECK: DW_TAG_inlined_subroutine +; CHECK: DW_TAG_variable +; CHECK: NULL +; CHECK: NULL +; CHECK: NULL + +%struct.B = type { i32 } +%struct.A = type { i32 } + +define dso_local i32 @not_removed() !dbg !12 { + %1 = alloca %struct.B, align 4 + call void @llvm.dbg.declare(metadata %struct.B* %1, metadata !18, metadata !DIExpression()), !dbg !22 + %2 = getelementptr inbounds %struct.B, %struct.B* %1, i32 0, i32 0, !dbg !23 + %3 = load i32, i32* %2, align 4, !dbg !24 + %4 = add nsw i32 %3, 1, !dbg !24 + store i32 %4, i32* %2, align 4, !dbg !24 + ret i32 %3, !dbg !25 +} + +declare void @llvm.dbg.declare(metadata, metadata, metadata) + +define dso_local i32 @foo() !dbg !26 { + %1 = alloca %struct.A, align 4 + %2 = alloca %struct.B, align 4 + call void @llvm.dbg.declare(metadata %struct.A* %1, metadata !27, metadata !DIExpression()), !dbg !32 + %3 = getelementptr inbounds %struct.A, %struct.A* %1, i32 0, i32 0, !dbg !34 + %4 = load i32, i32* %3, align 4, !dbg !35 + %5 = add nsw i32 %4, 1, !dbg !35 + store i32 %5, i32* %3, align 4, !dbg !35 + call void @llvm.dbg.declare(metadata %struct.B* %2, metadata !18, metadata !DIExpression()), !dbg !36 + %6 = getelementptr inbounds %struct.B, %struct.B* %2, i32 0, i32 0, !dbg !38 + %7 = load i32, i32* %6, align 4, !dbg !39 + %8 = add nsw i32 %7, 1, !dbg !39 + store i32 %8, i32* %6, align 4, !dbg !39 + %9 = add nsw i32 %4, %7, !dbg !40 + ret i32 %9, !dbg !41 +} + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!2, !3, !4, !5, !6, !7, !8, !9, !10} +!llvm.ident = !{!11} + +!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 14.0.0", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None) +!1 = !DIFile(filename: "inlined-local-type.cpp", directory: "") +!2 = !{i32 7, !"Dwarf Version", i32 4} +!3 = !{i32 2, !"Debug Info Version", i32 3} +!4 = !{i32 1, !"wchar_size", i32 4} +!5 = !{i32 1, !"branch-target-enforcement", i32 0} +!6 = !{i32 1, !"sign-return-address", i32 0} +!7 = !{i32 1, !"sign-return-address-all", i32 0} +!8 = !{i32 1, !"sign-return-address-with-bkey", i32 0} +!9 = !{i32 7, !"uwtable", i32 1} +!10 = !{i32 7, !"frame-pointer", i32 1} +!11 = !{!"clang version 14.0.0"} +!12 = distinct !DISubprogram(name: "not_removed", scope: !13, file: !13, line: 5, type: !14, scopeLine: 5, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !43) +!13 = !DIFile(filename: "inlined-local-type.cpp", directory: "") +!14 = !DISubroutineType(types: !15) +!15 = !{!16} +!16 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!17 = !{} +!18 = !DILocalVariable(name: "b", scope: !12, file: !13, line: 5, type: !19) +!19 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "B", scope: !12, file: !13, line: 5, size: 32, elements: !20) +!20 = !{!21} +!21 = !DIDerivedType(tag: DW_TAG_member, name: "i", scope: !19, file: !13, line: 5, baseType: !16, size: 32) +!22 = !DILocation(line: 5, column: 49, scope: !12) +!23 = !DILocation(line: 5, column: 61, scope: !12) +!24 = !DILocation(line: 5, column: 62, scope: !12) +!25 = !DILocation(line: 5, column: 52, scope: !12) +!26 = distinct !DISubprogram(name: "foo", scope: !13, file: !13, line: 7, type: !14, scopeLine: 7, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !17) +!27 = !DILocalVariable(name: "a", scope: !28, file: !13, line: 2, type: !29) +!28 = distinct !DISubprogram(name: "removed", scope: !13, file: !13, line: 2, type: !14, scopeLine: 2, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !42) +!29 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "A", scope: !28, file: !13, line: 2, size: 32, elements: !30) +!30 = !{!31} +!31 = !DIDerivedType(tag: DW_TAG_member, name: "i", scope: !29, file: !13, line: 2, baseType: !16, size: 32) +!32 = !DILocation(line: 2, column: 45, scope: !28, inlinedAt: !33) +!33 = distinct !DILocation(line: 7, column: 20, scope: !26) +!34 = !DILocation(line: 2, column: 57, scope: !28, inlinedAt: !33) +!35 = !DILocation(line: 2, column: 58, scope: !28, inlinedAt: !33) +!36 = !DILocation(line: 5, column: 49, scope: !12, inlinedAt: !37) +!37 = distinct !DILocation(line: 7, column: 32, scope: !26) +!38 = !DILocation(line: 5, column: 61, scope: !12, inlinedAt: !37) +!39 = !DILocation(line: 5, column: 62, scope: !12, inlinedAt: !37) +!40 = !DILocation(line: 7, column: 30, scope: !26) +!41 = !DILocation(line: 7, column: 13, scope: !26) +!42 = !{!29} +!43 = !{!19} diff --git a/llvm/test/DebugInfo/Generic/lexical-block-retained-types.ll b/llvm/test/DebugInfo/Generic/lexical-block-retained-types.ll new file mode 100644 index 0000000000000..64de1323c98c6 --- /dev/null +++ b/llvm/test/DebugInfo/Generic/lexical-block-retained-types.ll @@ -0,0 +1,55 @@ +; RUN: %llc_dwarf -O0 -filetype=obj < %s | llvm-dwarfdump - | FileCheck --implicit-check-not "{{DW_TAG|NULL}}" %s + +; Test that retained unused (unreferenced) types emission. + +; Compiled from +; $ clang -cc1 -debug-info-kind=unused-types test.cpp -emit-llvm + +; void test_unused() { +; struct Y {}; +; { +; struct X {}; +; } +; } + +; CHECK: DW_TAG_compile_unit +; CHECK: DW_TAG_subprogram +; CHECK: DW_AT_name ("test_unused") +; CHECK: DW_TAG_structure_type +; CHECK: DW_AT_name ("Y") + +; FIXME: here should be DW_TAG_lexical_block as a parent of structure 'X'. +; But it's not possible to reliably emit a lexical block for which a LexicalScope +; wasn't created, so we just fallback to the most close parent DIE +; (see DwarfCompileUnit::getOrCreateLexicalBlockDIE() for details). + +; CHECK: DW_TAG_structure_type +; CHECK: DW_AT_name ("X") +; CHECK: NULL +; CHECK: NULL + +define dso_local void @_Z11test_unusedv() !dbg !5 { +entry: + ret void, !dbg !16 +} + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!13, !14} +!llvm.ident = !{!15} + +!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang version 15.0.0", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, retainedTypes: !2, splitDebugInlining: false, nameTableKind: None) +!1 = !DIFile(filename: "", directory: "/") +!2 = !{!3, !10} +!3 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "Y", scope: !5, file: !4, line: 2, size: 8, flags: DIFlagTypePassByValue, elements: !8) +!4 = !DIFile(filename: "test.cpp", directory: "/") +!5 = distinct !DISubprogram(name: "test_unused", linkageName: "_Z11test_unusedv", scope: !4, file: !4, line: 1, type: !6, scopeLine: 1, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !9) +!6 = !DISubroutineType(types: !7) +!7 = !{null} +!8 = !{} +!9 = !{!3, !10} +!10 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "X", scope: !11, file: !4, line: 4, size: 8, flags: DIFlagTypePassByValue, elements: !8) +!11 = distinct !DILexicalBlock(scope: !5, file: !4, line: 3, column: 3) +!13 = !{i32 2, !"Debug Info Version", i32 3} +!14 = !{i32 1, !"wchar_size", i32 4} +!15 = !{!"clang version 15.0.0"} +!16 = !DILocation(line: 6, column: 1, scope: !5) diff --git a/llvm/test/DebugInfo/Generic/lexical-block-types.ll b/llvm/test/DebugInfo/Generic/lexical-block-types.ll new file mode 100644 index 0000000000000..eea46b1b67d8c --- /dev/null +++ b/llvm/test/DebugInfo/Generic/lexical-block-types.ll @@ -0,0 +1,425 @@ +; RUN: %llc_dwarf -O0 -filetype=obj < %s | llvm-dwarfdump -debug-info - | FileCheck --implicit-check-not "{{DW_TAG|NULL}}" %s +; REQUIRES: object-emission + +; inline __attribute__((always_inline)) +; void removed() { +; struct A1 { int i; }; +; typedef int Int1; +; { +; struct I1 { Int1 j; }; +; struct C1 { typedef char Char1; Char1 c; }; +; A1 a1; a1.i++; +; { +; I1 i1; i1.j++; +; C1 c1; c1.c++; +; } +; } +; } +; +; __attribute__((always_inline)) +; void not_removed() { +; struct A2 { int i; }; +; typedef int Int2; +; { +; struct I2 { Int2 j; }; +; struct C2 { typedef char Char2; Char2 c; }; +; A2 a2; a2.i++; +; { +; I2 i2; i2.j++; +; C2 c2; c2.c++; +; } +; } +; } +; +; void foo() { +; struct A3 { int i; }; +; typedef int Int3; +; { +; struct I3 { Int3 j; }; +; { +; struct C3 { typedef char Char3; Char3 c; }; +; A3 a3; a3.i++; +; { +; I3 i3; i3.j++; +; C3 c3; c3.c++; +; } +; } +; } +; removed(); +; not_removed(); +; } +; +; CHECK: DW_TAG_compile_unit + +; Out-of-line definition of `not_removed()` shouldn't contain any debug info for types. +; CHECK: DW_TAG_subprogram +; CHECK: DW_AT_abstract_origin {{.*}} "_Z11not_removedv" +; CHECK: DW_TAG_lexical_block +; CHECK: DW_TAG_variable +; CHECK: DW_AT_abstract_origin {{.*}} "a2" +; CHECK: DW_TAG_lexical_block +; CHECK: DW_TAG_variable +; CHECK: DW_AT_abstract_origin {{.*}} "i2" +; CHECK: DW_TAG_variable +; CHECK: DW_AT_abstract_origin {{.*}} "c2" +; CHECK: NULL +; CHECK: NULL +; CHECK: NULL + +; Abstract definition of `removed()`. +; CHECK: DW_TAG_subprogram +; CHECK: DW_AT_name ("removed") +; CHECK: DW_AT_inline (DW_INL_inlined) + +; I1 and C1 defined in the first lexical block, typedef Char1 is a child of C1. +; CHECK: DW_TAG_lexical_block +; CHECK: DW_TAG_variable +; CHECK: DW_AT_name ("a1") +; CHECK: DW_AT_type {{.*}} "A1" +; CHECK: DW_TAG_lexical_block +; CHECK: DW_TAG_variable +; CHECK: DW_AT_type {{.*}} "I1" +; CHECK: DW_TAG_variable +; CHECK: DW_AT_type {{.*}} "C1" +; CHECK: NULL +; CHECK: DW_TAG_structure_type +; CHECK: DW_AT_name ("I1") +; CHECK: DW_TAG_member +; CHECK: DW_AT_type {{.*}} "Int1" +; CHECK: NULL +; CHECK: DW_TAG_structure_type +; CHECK: DW_AT_name ("C1") +; CHECK: DW_TAG_member +; CHECK: DW_AT_type {{.*}} "C1::Char1" +; CHECK: DW_TAG_typedef +; CHECK: DW_AT_name ("Char1") +; CHECK: NULL +; CHECK: NULL + +; A1 and typedef Int1 defined in the subprogram scope. +; CHECK: DW_TAG_structure_type +; CHECK: DW_AT_name ("A1") +; CHECK: DW_TAG_member +; CHECK: NULL +; CHECK: DW_TAG_typedef +; CHECK: DW_AT_name ("Int1") +; CHECK: NULL + +; CHECK: DW_TAG_base_type +; CHECK: DW_TAG_base_type + +; Abstract definition of `not_removed()`. +; CHECK: DW_TAG_subprogram +; CHECK: DW_AT_name ("not_removed") +; CHECK: DW_AT_inline (DW_INL_inlined) + +; I2 and C2 defined in the first lexical block, typedef Char2 is a child of C2. +; CHECK: DW_TAG_lexical_block +; CHECK: DW_TAG_variable +; CHECK: DW_AT_name ("a2") +; CHECK: DW_AT_type {{.*}} "A2" +; CHECK: DW_TAG_lexical_block +; CHECK: DW_TAG_variable +; CHECK: DW_AT_name ("i2") +; CHECK: DW_AT_type {{.*}} "I2" +; CHECK: DW_TAG_variable +; CHECK: DW_AT_name ("c2") +; CHECK: DW_AT_type {{.*}} "C2" +; CHECK: NULL +; CHECK: DW_TAG_structure_type +; CHECK: DW_AT_name ("I2") +; CHECK: DW_TAG_member +; CHECK: DW_AT_type {{.*}} "Int2" +; CHECK: NULL +; CHECK: DW_TAG_structure_type +; CHECK: DW_AT_name ("C2") +; CHECK: DW_TAG_member +; CHECK: DW_AT_type {{.*}} "C2::Char2" +; CHECK: DW_TAG_typedef +; CHECK: DW_AT_name ("Char2") +; CHECK: NULL +; CHECK: NULL + +; A2 and typedef Int2 defined in subprogram scope. +; CHECK: DW_TAG_structure_type +; CHECK: DW_AT_name ("A2") +; CHECK: DW_TAG_member +; CHECK: NULL +; CHECK: DW_TAG_typedef +; CHECK: DW_AT_name ("Int2") +; CHECK: NULL + +; Definition of `foo()`. +; CHECK: DW_TAG_subprogram +; CHECK: DW_AT_name ("foo") + +; CHECK: DW_TAG_lexical_block +; CHECK: DW_TAG_lexical_block +; CHECK: DW_TAG_variable +; CHECK: DW_AT_name ("a3") +; CHECK: DW_AT_type {{.*}} "A3" +; CHECK: DW_TAG_lexical_block +; CHECK: DW_TAG_variable +; CHECK: DW_AT_name ("i3") +; CHECK: DW_AT_type {{.*}} "I3" +; CHECK: DW_TAG_variable +; CHECK: DW_AT_name ("c3") +; CHECK: DW_AT_type {{.*}} "C3" +; CHECK: NULL + +; C3 has the inner lexical block scope, typedef Char3 is a child of C3. +; CHECK: DW_TAG_structure_type +; CHECK: DW_AT_name ("C3") +; CHECK: DW_TAG_member +; CHECK: DW_AT_type {{.*}} "C3::Char3" +; CHECK: DW_TAG_typedef +; CHECK: DW_AT_name ("Char3") +; CHECK: NULL +; CHECK: NULL + +; I3 has the outer lexical block scope. +; CHECK: DW_TAG_structure_type +; CHECK: DW_AT_name ("I3") +; CHECK: DW_TAG_member +; CHECK: DW_AT_type {{.*}} "Int3" +; CHECK: NULL +; CHECK: NULL + +; CHECK: DW_TAG_inlined_subroutine +; CHECK: DW_AT_abstract_origin {{.*}} "_Z7removedv" +; CHECK: DW_TAG_lexical_block +; CHECK: DW_TAG_variable +; CHECK: DW_TAG_lexical_block +; CHECK: DW_TAG_variable +; CHECK: DW_TAG_variable +; CHECK: NULL +; CHECK: NULL +; CHECK: NULL + +; CHECK: DW_TAG_inlined_subroutine +; CHECK: DW_AT_abstract_origin {{.*}} "_Z11not_removedv" +; CHECK: DW_TAG_lexical_block +; CHECK: DW_TAG_variable +; CHECK: DW_TAG_lexical_block +; CHECK: DW_TAG_variable +; CHECK: DW_TAG_variable +; CHECK: NULL +; CHECK: NULL +; CHECK: NULL + +; A3 and Int3 defined within the subprogam scope. +; CHECK: DW_TAG_structure_type +; CHECK: DW_AT_name ("A3") +; CHECK: DW_TAG_member +; CHECK: NULL +; CHECK: DW_TAG_typedef +; CHECK: DW_AT_name ("Int3") +; CHECK: NULL +; CHECK: NULL + +%struct.A2 = type { i32 } +%struct.I2 = type { i32 } +%struct.C2 = type { i8 } +%struct.A1 = type { i32 } +%struct.I1 = type { i32 } +%struct.C1 = type { i8 } +%struct.A3 = type { i32 } +%struct.I3 = type { i32 } +%struct.C3 = type { i8 } + +define dso_local void @_Z11not_removedv() !dbg !8 { +entry: + %a2 = alloca %struct.A2, align 4 + %i2 = alloca %struct.I2, align 4 + %c2 = alloca %struct.C2, align 1 + call void @llvm.dbg.declare(metadata %struct.A2* %a2, metadata !12, metadata !DIExpression()), !dbg !18 + %i = getelementptr inbounds %struct.A2, %struct.A2* %a2, i32 0, i32 0, !dbg !19 + %0 = load i32, i32* %i, align 4, !dbg !20 + %inc = add nsw i32 %0, 1, !dbg !20 + store i32 %inc, i32* %i, align 4, !dbg !20 + call void @llvm.dbg.declare(metadata %struct.I2* %i2, metadata !21, metadata !DIExpression()), !dbg !27 + %j = getelementptr inbounds %struct.I2, %struct.I2* %i2, i32 0, i32 0, !dbg !28 + %1 = load i32, i32* %j, align 4, !dbg !29 + %inc1 = add nsw i32 %1, 1, !dbg !29 + store i32 %inc1, i32* %j, align 4, !dbg !29 + call void @llvm.dbg.declare(metadata %struct.C2* %c2, metadata !30, metadata !DIExpression()), !dbg !36 + %c = getelementptr inbounds %struct.C2, %struct.C2* %c2, i32 0, i32 0, !dbg !37 + %2 = load i8, i8* %c, align 1, !dbg !38 + %inc2 = add i8 %2, 1, !dbg !38 + store i8 %inc2, i8* %c, align 1, !dbg !38 + ret void, !dbg !39 +} + +declare void @llvm.dbg.declare(metadata, metadata, metadata) + +define dso_local void @_Z3foov() !dbg !40 { +entry: + %a1.i = alloca %struct.A1, align 4 + %i1.i = alloca %struct.I1, align 4 + %c1.i = alloca %struct.C1, align 1 + %a2.i = alloca %struct.A2, align 4 + %i2.i = alloca %struct.I2, align 4 + %c2.i = alloca %struct.C2, align 1 + %a3 = alloca %struct.A3, align 4 + %i3 = alloca %struct.I3, align 4 + %c3 = alloca %struct.C3, align 1 + call void @llvm.dbg.declare(metadata %struct.A3* %a3, metadata !41, metadata !DIExpression()), !dbg !47 + %i = getelementptr inbounds %struct.A3, %struct.A3* %a3, i32 0, i32 0, !dbg !48 + %0 = load i32, i32* %i, align 4, !dbg !49 + %inc = add nsw i32 %0, 1, !dbg !49 + store i32 %inc, i32* %i, align 4, !dbg !49 + call void @llvm.dbg.declare(metadata %struct.I3* %i3, metadata !50, metadata !DIExpression()), !dbg !56 + %j = getelementptr inbounds %struct.I3, %struct.I3* %i3, i32 0, i32 0, !dbg !57 + %1 = load i32, i32* %j, align 4, !dbg !58 + %inc1 = add nsw i32 %1, 1, !dbg !58 + store i32 %inc1, i32* %j, align 4, !dbg !58 + call void @llvm.dbg.declare(metadata %struct.C3* %c3, metadata !59, metadata !DIExpression()), !dbg !64 + %c = getelementptr inbounds %struct.C3, %struct.C3* %c3, i32 0, i32 0, !dbg !65 + %2 = load i8, i8* %c, align 1, !dbg !66 + %inc2 = add i8 %2, 1, !dbg !66 + store i8 %inc2, i8* %c, align 1, !dbg !66 + call void @llvm.dbg.declare(metadata %struct.A1* %a1.i, metadata !67, metadata !DIExpression()), !dbg !73 + %i.i3 = getelementptr inbounds %struct.A1, %struct.A1* %a1.i, i32 0, i32 0, !dbg !75 + %3 = load i32, i32* %i.i3, align 4, !dbg !76 + %inc.i4 = add nsw i32 %3, 1, !dbg !76 + store i32 %inc.i4, i32* %i.i3, align 4, !dbg !76 + call void @llvm.dbg.declare(metadata %struct.I1* %i1.i, metadata !77, metadata !DIExpression()), !dbg !83 + %j.i5 = getelementptr inbounds %struct.I1, %struct.I1* %i1.i, i32 0, i32 0, !dbg !84 + %4 = load i32, i32* %j.i5, align 4, !dbg !85 + %inc1.i6 = add nsw i32 %4, 1, !dbg !85 + store i32 %inc1.i6, i32* %j.i5, align 4, !dbg !85 + call void @llvm.dbg.declare(metadata %struct.C1* %c1.i, metadata !86, metadata !DIExpression()), !dbg !91 + %c.i7 = getelementptr inbounds %struct.C1, %struct.C1* %c1.i, i32 0, i32 0, !dbg !92 + %5 = load i8, i8* %c.i7, align 1, !dbg !93 + %inc2.i8 = add i8 %5, 1, !dbg !93 + store i8 %inc2.i8, i8* %c.i7, align 1, !dbg !93 + call void @llvm.dbg.declare(metadata %struct.A2* %a2.i, metadata !12, metadata !DIExpression()), !dbg !94 + %i.i = getelementptr inbounds %struct.A2, %struct.A2* %a2.i, i32 0, i32 0, !dbg !96 + %6 = load i32, i32* %i.i, align 4, !dbg !97 + %inc.i = add nsw i32 %6, 1, !dbg !97 + store i32 %inc.i, i32* %i.i, align 4, !dbg !97 + call void @llvm.dbg.declare(metadata %struct.I2* %i2.i, metadata !21, metadata !DIExpression()), !dbg !98 + %j.i = getelementptr inbounds %struct.I2, %struct.I2* %i2.i, i32 0, i32 0, !dbg !99 + %7 = load i32, i32* %j.i, align 4, !dbg !100 + %inc1.i = add nsw i32 %7, 1, !dbg !100 + store i32 %inc1.i, i32* %j.i, align 4, !dbg !100 + call void @llvm.dbg.declare(metadata %struct.C2* %c2.i, metadata !30, metadata !DIExpression()), !dbg !101 + %c.i = getelementptr inbounds %struct.C2, %struct.C2* %c2.i, i32 0, i32 0, !dbg !102 + %8 = load i8, i8* %c.i, align 1, !dbg !103 + %inc2.i = add i8 %8, 1, !dbg !103 + store i8 %inc2.i, i8* %c.i, align 1, !dbg !103 + ret void, !dbg !104 +} + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!2, !3, !4, !5, !6} +!llvm.ident = !{!7} + +!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang version 14.0.0", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None) +!1 = !DIFile(filename: "test.cpp", directory: "/") +!2 = !{i32 7, !"Dwarf Version", i32 4} +!3 = !{i32 2, !"Debug Info Version", i32 3} +!4 = !{i32 1, !"wchar_size", i32 4} +!5 = !{i32 7, !"uwtable", i32 1} +!6 = !{i32 7, !"frame-pointer", i32 2} +!7 = !{!"clang version 14.0.0"} +!8 = distinct !DISubprogram(name: "not_removed", linkageName: "_Z11not_removedv", scope: !1, file: !1, line: 17, type: !9, scopeLine: 17, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !105) +!9 = !DISubroutineType(types: !10) +!10 = !{null} +!11 = !{} +!12 = !DILocalVariable(name: "a2", scope: !13, file: !1, line: 23, type: !14) +!13 = distinct !DILexicalBlock(scope: !8, file: !1, line: 20, column: 3) +!14 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "A2", scope: !8, file: !1, line: 18, size: 32, flags: DIFlagTypePassByValue, elements: !15) +!15 = !{!16} +!16 = !DIDerivedType(tag: DW_TAG_member, name: "i", scope: !14, file: !1, line: 18, baseType: !17, size: 32) +!17 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!18 = !DILocation(line: 23, column: 8, scope: !13) +!19 = !DILocation(line: 23, column: 15, scope: !13) +!20 = !DILocation(line: 23, column: 16, scope: !13) +!21 = !DILocalVariable(name: "i2", scope: !22, file: !1, line: 25, type: !23) +!22 = distinct !DILexicalBlock(scope: !13, file: !1, line: 24, column: 5) +!23 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "I2", scope: !13, file: !1, line: 21, size: 32, flags: DIFlagTypePassByValue, elements: !24) +!24 = !{!25} +!25 = !DIDerivedType(tag: DW_TAG_member, name: "j", scope: !23, file: !1, line: 21, baseType: !26, size: 32) +!26 = !DIDerivedType(tag: DW_TAG_typedef, name: "Int2", scope: !8, file: !1, line: 19, baseType: !17) +!27 = !DILocation(line: 25, column: 10, scope: !22) +!28 = !DILocation(line: 25, column: 17, scope: !22) +!29 = !DILocation(line: 25, column: 18, scope: !22) +!30 = !DILocalVariable(name: "c2", scope: !22, file: !1, line: 26, type: !31) +!31 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "C2", scope: !13, file: !1, line: 22, size: 8, flags: DIFlagTypePassByValue, elements: !32) +!32 = !{!33} +!33 = !DIDerivedType(tag: DW_TAG_member, name: "c", scope: !31, file: !1, line: 22, baseType: !34, size: 8) +!34 = !DIDerivedType(tag: DW_TAG_typedef, name: "Char2", scope: !31, file: !1, line: 22, baseType: !35) +!35 = !DIBasicType(name: "char", size: 8, encoding: DW_ATE_signed_char) +!36 = !DILocation(line: 26, column: 10, scope: !22) +!37 = !DILocation(line: 26, column: 17, scope: !22) +!38 = !DILocation(line: 26, column: 18, scope: !22) +!39 = !DILocation(line: 29, column: 1, scope: !8) +!40 = distinct !DISubprogram(name: "foo", linkageName: "_Z3foov", scope: !1, file: !1, line: 31, type: !9, scopeLine: 31, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !107) +!41 = !DILocalVariable(name: "a3", scope: !42, file: !1, line: 38, type: !44) +!42 = distinct !DILexicalBlock(scope: !43, file: !1, line: 36, column: 5) +!43 = distinct !DILexicalBlock(scope: !40, file: !1, line: 34, column: 3) +!44 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "A3", scope: !40, file: !1, line: 32, size: 32, flags: DIFlagTypePassByValue, elements: !45) +!45 = !{!46} +!46 = !DIDerivedType(tag: DW_TAG_member, name: "i", scope: !44, file: !1, line: 32, baseType: !17, size: 32) +!47 = !DILocation(line: 38, column: 10, scope: !42) +!48 = !DILocation(line: 38, column: 17, scope: !42) +!49 = !DILocation(line: 38, column: 18, scope: !42) +!50 = !DILocalVariable(name: "i3", scope: !51, file: !1, line: 40, type: !52) +!51 = distinct !DILexicalBlock(scope: !42, file: !1, line: 39, column: 7) +!52 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "I3", scope: !43, file: !1, line: 35, size: 32, flags: DIFlagTypePassByValue, elements: !53) +!53 = !{!54} +!54 = !DIDerivedType(tag: DW_TAG_member, name: "j", scope: !52, file: !1, line: 35, baseType: !55, size: 32) +!55 = !DIDerivedType(tag: DW_TAG_typedef, name: "Int3", scope: !40, file: !1, line: 33, baseType: !17) +!56 = !DILocation(line: 40, column: 12, scope: !51) +!57 = !DILocation(line: 40, column: 19, scope: !51) +!58 = !DILocation(line: 40, column: 20, scope: !51) +!59 = !DILocalVariable(name: "c3", scope: !51, file: !1, line: 41, type: !60) +!60 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "C3", scope: !42, file: !1, line: 37, size: 8, flags: DIFlagTypePassByValue, elements: !61) +!61 = !{!62} +!62 = !DIDerivedType(tag: DW_TAG_member, name: "c", scope: !60, file: !1, line: 37, baseType: !63, size: 8) +!63 = !DIDerivedType(tag: DW_TAG_typedef, name: "Char3", scope: !60, file: !1, line: 37, baseType: !35) +!64 = !DILocation(line: 41, column: 12, scope: !51) +!65 = !DILocation(line: 41, column: 19, scope: !51) +!66 = !DILocation(line: 41, column: 20, scope: !51) +!67 = !DILocalVariable(name: "a1", scope: !68, file: !1, line: 8, type: !70) +!68 = distinct !DILexicalBlock(scope: !69, file: !1, line: 5, column: 3) +!69 = distinct !DISubprogram(name: "removed", linkageName: "_Z7removedv", scope: !1, file: !1, line: 2, type: !9, scopeLine: 2, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !110) +!70 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "A1", scope: !69, file: !1, line: 3, size: 32, flags: DIFlagTypePassByValue, elements: !71, identifier: "_ZTSZ7removedvE2A1") +!71 = !{!72} +!72 = !DIDerivedType(tag: DW_TAG_member, name: "i", scope: !70, file: !1, line: 3, baseType: !17, size: 32) +!73 = !DILocation(line: 8, column: 8, scope: !68, inlinedAt: !74) +!74 = distinct !DILocation(line: 45, column: 3, scope: !40) +!75 = !DILocation(line: 8, column: 15, scope: !68, inlinedAt: !74) +!76 = !DILocation(line: 8, column: 16, scope: !68, inlinedAt: !74) +!77 = !DILocalVariable(name: "i1", scope: !78, file: !1, line: 10, type: !79) +!78 = distinct !DILexicalBlock(scope: !68, file: !1, line: 9, column: 5) +!79 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "I1", scope: !68, file: !1, line: 6, size: 32, flags: DIFlagTypePassByValue, elements: !80, identifier: "_ZTSZ7removedvE2I1") +!80 = !{!81} +!81 = !DIDerivedType(tag: DW_TAG_member, name: "j", scope: !79, file: !1, line: 6, baseType: !82, size: 32) +!82 = !DIDerivedType(tag: DW_TAG_typedef, name: "Int1", scope: !69, file: !1, line: 4, baseType: !17) +!83 = !DILocation(line: 10, column: 10, scope: !78, inlinedAt: !74) +!84 = !DILocation(line: 10, column: 17, scope: !78, inlinedAt: !74) +!85 = !DILocation(line: 10, column: 18, scope: !78, inlinedAt: !74) +!86 = !DILocalVariable(name: "c1", scope: !78, file: !1, line: 11, type: !87) +!87 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "C1", scope: !68, file: !1, line: 7, size: 8, flags: DIFlagTypePassByValue, elements: !88, identifier: "_ZTSZ7removedvE2C1") +!88 = !{!89} +!89 = !DIDerivedType(tag: DW_TAG_member, name: "c", scope: !87, file: !1, line: 7, baseType: !90, size: 8) +!90 = !DIDerivedType(tag: DW_TAG_typedef, name: "Char1", scope: !87, file: !1, line: 7, baseType: !35) +!91 = !DILocation(line: 11, column: 10, scope: !78, inlinedAt: !74) +!92 = !DILocation(line: 11, column: 17, scope: !78, inlinedAt: !74) +!93 = !DILocation(line: 11, column: 18, scope: !78, inlinedAt: !74) +!94 = !DILocation(line: 23, column: 8, scope: !13, inlinedAt: !95) +!95 = distinct !DILocation(line: 46, column: 3, scope: !40) +!96 = !DILocation(line: 23, column: 15, scope: !13, inlinedAt: !95) +!97 = !DILocation(line: 23, column: 16, scope: !13, inlinedAt: !95) +!98 = !DILocation(line: 25, column: 10, scope: !22, inlinedAt: !95) +!99 = !DILocation(line: 25, column: 17, scope: !22, inlinedAt: !95) +!100 = !DILocation(line: 25, column: 18, scope: !22, inlinedAt: !95) +!101 = !DILocation(line: 26, column: 10, scope: !22, inlinedAt: !95) +!102 = !DILocation(line: 26, column: 17, scope: !22, inlinedAt: !95) +!103 = !DILocation(line: 26, column: 18, scope: !22, inlinedAt: !95) +!104 = !DILocation(line: 47, column: 1, scope: !40) +!105 = !{!14, !23, !26, !31} +!107 = !{!44, !52, !55, !60} +!110 = !{!70, !79, !82, !87} diff --git a/llvm/test/DebugInfo/Generic/verifier-invalid-disubprogram.ll b/llvm/test/DebugInfo/Generic/verifier-invalid-disubprogram.ll index 6d4d0e93d38f9..54ce1c56c6b30 100644 --- a/llvm/test/DebugInfo/Generic/verifier-invalid-disubprogram.ll +++ b/llvm/test/DebugInfo/Generic/verifier-invalid-disubprogram.ll @@ -38,7 +38,7 @@ define void @invalid_subprogram_declaration() !dbg !9 { ret void } define void @invalid_retained_nodes_list() !dbg !10 { ret void } !10 = distinct !DISubprogram(retainedNodes: !0) -; CHECK: invalid retained nodes, expected DILocalVariable, DILabel or DIImportedEntity +; CHECK: invalid retained nodes, expected DILocalVariable, DILabel, DIImportedEntity or DIType define void @invalid_retained_nodes_expected() !dbg !11 { ret void } !11 = distinct !DISubprogram(retainedNodes: !{!0}) diff --git a/llvm/test/DebugInfo/Inputs/cleanup-retained-nodes.ll b/llvm/test/DebugInfo/Inputs/cleanup-retained-nodes.ll new file mode 100644 index 0000000000000..d2cca1bc4f8a2 --- /dev/null +++ b/llvm/test/DebugInfo/Inputs/cleanup-retained-nodes.ll @@ -0,0 +1,17 @@ +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +@var = global i8 0, align 4, !dbg !7 + +!llvm.module.flags = !{!0, !1, !2} +!llvm.dbg.cu = !{!3} + +!0 = !{i32 7, !"Dwarf Version", i32 2} +!1 = !{i32 2, !"Debug Info Version", i32 3} +!2 = !{i32 1, !"wchar_size", i32 4} +!3 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !5, producer: "clang", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None) +!4 = !{} +!5 = !DIFile(filename: "tmp2.cpp", directory: "/tmp/") +!6 = !DICompositeType(tag: DW_TAG_class_type, scope: !3, file: !5, line: 212, size: 8, flags: DIFlagTypePassByValue, elements: !4, identifier: "type_global_in_another_module") +!7 = !DIGlobalVariableExpression(var: !8, expr: !DIExpression()) +!8 = distinct !DIGlobalVariable(name: "var", scope: !3, file: !5, line: 1, type: !6, isLocal: false, isDefinition: true) diff --git a/llvm/test/DebugInfo/X86/cleanup-retained-nodes.ll b/llvm/test/DebugInfo/X86/cleanup-retained-nodes.ll new file mode 100644 index 0000000000000..f26faa14b9e9e --- /dev/null +++ b/llvm/test/DebugInfo/X86/cleanup-retained-nodes.ll @@ -0,0 +1,46 @@ +; RUN: llvm-as %s -o %t.bc +; RUN: llvm-as %p/../Inputs/cleanup-retained-nodes.ll -o %t.global.bc +; RUN: llvm-link %t.global.bc %t.bc %t.bc -o %t.linked.bc +; RUN: opt --passes=verify %t.linked.bc -o /dev/null +; RUN: llvm-dis %t.linked.bc -o - | FileCheck %s --implicit-check-not=DICompositeType + +; During module loading, if a local type appears in retainedNodes +; field of multiple DISubprograms due to ODR-uniquing, +; retainedNodes should be cleaned up, so that only one DISubprogram +; will have this type in its retainedNodes. + +; CHECK: distinct !DICompositeType(tag: DW_TAG_class_type, {{.*}}, identifier: "type_global_in_another_module") +; CHECK: [[EMPTY:![0-9]+]] = !{} +; CHECK: [[BAR1:![0-9]+]] = distinct !DISubprogram(name: "bar", {{.*}}, retainedNodes: [[RN_BAR1:![0-9]+]]) +; CHECK: [[RN_BAR1]] = !{[[T1:![0-9]+]], [[T1:![0-9]+]], [[T1:![0-9]+]], [[T2:![0-9]+]]} +; CHECK: [[T1]] = distinct !DICompositeType(tag: DW_TAG_class_type, scope: [[BAR1]], {{.*}}, identifier: "local_type") +; CHECK: [[T2]] = distinct !DICompositeType(tag: DW_TAG_class_type, scope: [[LB:![0-9]+]], {{.*}}, identifier: "local_type_in_block") +; CHECK: [[LB]] = !DILexicalBlock(scope: [[BAR1]] +; CHECK: {{![0-9]+}} = distinct !DISubprogram(name: "bar", {{.*}}, retainedNodes: [[EMPTY:![0-9]+]]) + +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +@llvm.used = appending global [1 x ptr] [ptr @bar] + +define internal void @bar(ptr %this) !dbg !10 { + ret void +} + +!llvm.module.flags = !{!0, !1, !2} +!llvm.dbg.cu = !{!8} + +!0 = !{i32 7, !"Dwarf Version", i32 2} +!1 = !{i32 2, !"Debug Info Version", i32 3} +!2 = !{i32 1, !"wchar_size", i32 4} +!3 = !DICompositeType(tag: DW_TAG_class_type, scope: !10, file: !9, line: 212, size: 8, flags: DIFlagTypePassByValue, elements: !7, identifier: "type_global_in_another_module") +!4 = !DICompositeType(tag: DW_TAG_class_type, scope: !5, file: !9, line: 211, size: 8, flags: DIFlagTypePassByValue, elements: !7, identifier: "local_type_in_block") +!5 = !DILexicalBlock(scope: !10) +; All repeating occurences of a uniqued type in retainedNodes must be checked. +!6 = !{!12, !12, !12, !4, !3} +!7 = !{} +!8 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !9, producer: "clang", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None) +!9 = !DIFile(filename: "tmp.cpp", directory: "/tmp/") +!10 = distinct !DISubprogram(name: "bar", scope: !9, file: !9, line: 68, type: !11, scopeLine: 68, flags: DIFlagPrototyped, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition | DISPFlagOptimized, unit: !8, retainedNodes: !6) +!11 = !DISubroutineType(types: !7) +!12 = !DICompositeType(tag: DW_TAG_class_type, scope: !10, file: !9, line: 210, size: 8, flags: DIFlagTypePassByValue, elements: !7, identifier: "local_type") diff --git a/llvm/test/DebugInfo/X86/llparser-cleanup-retained-nodes.ll b/llvm/test/DebugInfo/X86/llparser-cleanup-retained-nodes.ll new file mode 100644 index 0000000000000..4e61633ad7604 --- /dev/null +++ b/llvm/test/DebugInfo/X86/llparser-cleanup-retained-nodes.ll @@ -0,0 +1,37 @@ +; RUN: llvm-link %s %s -S -o %t +; RUN: opt --passes=verify %t -o /dev/null +; RUN: FileCheck --input-file=%t %s --implicit-check-not=DICompositeType + +; During module loading, if a local type appears in retainedNodes +; field of multiple DISubprograms due to ODR-uniquing, +; LLParser should clean up retainedNodes, so that only one DISubprogram +; will have this type in its retainedNodes. + +; CHECK: [[BAR1:![0-9]+]] = distinct !DISubprogram(name: "bar", {{.*}}, retainedNodes: [[RN_BAR1:![0-9]+]]) +; CHECK: [[EMPTY:![0-9]+]] = !{} +; CHECK: [[RN_BAR1]] = !{[[T1:![0-9]+]]} +; CHECK: [[T1]] = distinct !DICompositeType(tag: DW_TAG_class_type, scope: [[BAR1]], {{.*}}, identifier: "local_type") +; CHECK: {{![0-9]+}} = distinct !DISubprogram(name: "bar", {{.*}}, retainedNodes: [[EMPTY:![0-9]+]]) + +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +@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 0000000000000..586574d3e1aaf --- /dev/null +++ b/llvm/test/DebugInfo/X86/local-type-as-template-parameter.ll @@ -0,0 +1,161 @@ +; REQUIERES: system-linux +; RUN: %llc_dwarf -mtriple=x86_64-linux -O0 -filetype=obj < %s \ +; RUN: | llvm-dwarfdump --show-children --name=foo - \ +; RUN: | FileCheck --implicit-check-not "{{DW_TAG|NULL}}" %s + +; The test ensures that AsmPrinter doesn't crashed compiling this. +; It also demostrates misplacement for a local type (see PR55680 for details). + +; The test compiled from: + +; template +; struct A { +; A(T &in) : a(in) {} +; T a; +; }; +; +; __attribute__((always_inline)) +; void foo() { +; struct B { int i; }; +; B objB; +; A 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", 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/unittests/Transforms/Utils/CloningTest.cpp b/llvm/unittests/Transforms/Utils/CloningTest.cpp index d990808d31fe2..cf587a5002bf9 100644 --- a/llvm/unittests/Transforms/Utils/CloningTest.cpp +++ b/llvm/unittests/Transforms/Utils/CloningTest.cpp @@ -808,6 +808,111 @@ TEST(CloneFunction, CloneFunctionWithSubprograms) { EXPECT_FALSE(verifyModule(*ImplModule, &errs())); } +TEST(CloneFunction, CloneFunctionWithRetainedNodes) { + StringRef ImplAssembly = R"( + declare void @llvm.dbg.declare(metadata, metadata, metadata) + + define void @test() !dbg !3 { + call void @llvm.dbg.declare(metadata i8* undef, metadata !5, metadata !DIExpression()), !dbg !7 + call void @llvm.dbg.declare(metadata i8* undef, metadata !25, metadata !DIExpression()), !dbg !7 + ret void + } + + declare void @cloned() + + !llvm.dbg.cu = !{!0} + !llvm.module.flags = !{!2} + !0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, enums: !{!14}) + !1 = !DIFile(filename: "test.cpp", directory: "") + !2 = !{i32 1, !"Debug Info Version", i32 3} + !3 = distinct !DISubprogram(name: "test", scope: !1, unit: !0, retainedNodes: !9) + !4 = distinct !DISubprogram(name: "inlined", scope: !1, unit: !0, retainedNodes: !{!5}) + !5 = !DILocalVariable(name: "awaitables", scope: !4, type: !23) + !6 = distinct !DILexicalBlock(scope: !4, file: !1, line: 1) + !7 = !DILocation(line: 1, scope: !6, inlinedAt: !8) + !8 = !DILocation(line: 10, scope: !3) + !9 = !{!15, !17, !18, !23} + !14 = distinct !DICompositeType(tag: DW_TAG_enumeration_type, scope: !0, file: !1, line: 13, size: 200, elements: !{}) + !15 = !DILocalVariable(name: "a", scope: !3) + !16 = distinct !DICompositeType(tag: DW_TAG_enumeration_type, scope: !3, file: !1, line: 13, size: 208, elements: !{}) + !17 = !DIImportedEntity(tag: DW_TAG_imported_declaration, name: "imported_l", file: !1, line: 14, scope: !3, entity: !16) + !18 = !DILabel(scope: !3, name: "l", file: !1, line: 22) + !22 = !DIBasicType(name: "real", size: 32, align: 32, encoding: DW_ATE_float) + !23 = !DIDerivedType(name: "local_float", tag: DW_TAG_const_type, baseType: !22, scope: !3) + !float_type = !{!23} + !25 = !DILocalVariable(name: "inlined2", scope: !4, type: !23) + !inlined2 = !{!25} + )"; + + LLVMContext Context; + SMDiagnostic Error; + + auto ImplModule = parseAssemblyString(ImplAssembly, Error, Context); + EXPECT_TRUE(ImplModule != nullptr); + auto *Func = ImplModule->getFunction("test"); + EXPECT_TRUE(Func != nullptr); + auto *ClonedFunc = ImplModule->getFunction("cloned"); + EXPECT_TRUE(ClonedFunc != nullptr); + + EXPECT_FALSE(verifyModule(*ImplModule, &errs())); + + ValueToValueMapTy VMap; + SmallVector Returns; + ClonedCodeInfo CCI; + CloneFunctionInto(ClonedFunc, Func, VMap, + CloneFunctionChangeType::GlobalChanges, Returns, "", &CCI); + + EXPECT_FALSE(verifyModule(*ImplModule, &errs())); + + // Check that retained and local types are copied. + DISubprogram *FuncSP = Func->getSubprogram(); + DISubprogram *ClonedSP = ClonedFunc->getSubprogram(); + EXPECT_NE(FuncSP, nullptr); + EXPECT_NE(ClonedSP, nullptr); + EXPECT_EQ(FuncSP->getRetainedNodes().size(), 4u); + EXPECT_EQ(FuncSP->getRetainedNodes().size(), + ClonedSP->getRetainedNodes().size()); + for (unsigned I = 0; I < FuncSP->getRetainedNodes().size(); ++I) { + auto *Node = FuncSP->getRetainedNodes()[I]; + auto *Copy = ClonedSP->getRetainedNodes()[I]; + + // Check that the order of retainedNodes is preserved by + // checking that the corresponding node has the same name. + if (auto *Var = dyn_cast(Node)) { + auto *VarCopy = dyn_cast(Copy); + EXPECT_NE(VarCopy, nullptr); + EXPECT_EQ(Var->getName(), VarCopy->getName()); + } else if (auto *Label = dyn_cast(Node)) { + auto *LabelCopy = dyn_cast(Copy); + EXPECT_NE(LabelCopy, nullptr); + EXPECT_EQ(Label->getName(), LabelCopy->getName()); + } else if (auto *IE = dyn_cast(Node)) { + auto *IECopy = dyn_cast(Copy); + EXPECT_NE(IECopy, nullptr); + EXPECT_EQ(IE->getName(), IECopy->getName()); + } else if (auto *Ty = dyn_cast(Node)) { + auto *TyCopy = dyn_cast(Copy); + EXPECT_NE(TyCopy, nullptr); + EXPECT_EQ(Ty->getName(), TyCopy->getName()); + } + + // Check that node was copied + EXPECT_NE(Node, Copy); + } + + auto *FloatType = dyn_cast( + 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( + 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)