From b113d5c452da833972f637da4c3db58116cc081a Mon Sep 17 00:00:00 2001 From: Jeremy Morse Date: Fri, 6 Dec 2024 16:42:28 +0000 Subject: [PATCH 1/2] [DebugMetadata][DwarfDebug] Support function-local types in lexical block scopes (4/7) RFC https://discourse.llvm.org/t/rfc-dwarfdebug-fix-and-improve-handling-imported-entities-types-and-static-local-in-subprogram-and-lexical-block-scopes/68544 Similar to imported declarations, the patch tracks function-local types in DISubprogram's 'retainedNodes' field. DwarfDebug is adjusted in accordance with the aforementioned metadata change and provided a support of function-local types scoped within a lexical block. The patch assumes that DICompileUnit's 'enums field' no longer tracks local types and DwarfDebug would assert if any locally-scoped types get placed there. Reviewed By: jmmartinez Authored-by: Kristina Bessonova Differential Revision: https://reviews.llvm.org/D144006 (cherry picked from commit 2953d4adcb36c28d12a3e3f8febf0995ef22c0d9) Signed-off-by: Jeremy Morse Conflicts: llvm/lib/IR/DIBuilder.cpp llvm/lib/IR/DebugInfo.cpp llvm/lib/Transforms/Utils/CloneFunction.cpp --- clang/test/DebugInfo/CXX/access.cpp | 2 +- clang/test/DebugInfo/CXX/anon-union-vars.cpp | 12 +- clang/test/DebugInfo/CXX/codeview-unnamed.cpp | 110 +++-- .../CXX/gline-tables-only-codeview.cpp | 4 +- clang/test/DebugInfo/CXX/lambda-this.cpp | 4 +- .../test/DebugInfo/Generic/codeview-unnamed.c | 16 +- clang/test/DebugInfo/Generic/unused-types.c | 16 +- clang/test/DebugInfo/Generic/unused-types.cpp | 14 +- llvm/include/llvm/IR/DIBuilder.h | 6 +- llvm/lib/Bitcode/Reader/MetadataLoader.cpp | 115 +++-- .../CodeGen/AsmPrinter/DwarfCompileUnit.cpp | 64 ++- .../lib/CodeGen/AsmPrinter/DwarfCompileUnit.h | 16 +- llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp | 13 +- llvm/lib/IR/DIBuilder.cpp | 40 +- llvm/lib/IR/DebugInfo.cpp | 2 + llvm/lib/IR/Verifier.cpp | 6 +- llvm/lib/Transforms/Utils/CloneFunction.cpp | 17 +- llvm/test/Bitcode/clone-local-types.ll | 46 ++ llvm/test/Bitcode/upgrade-cu-locals.ll | 68 +-- llvm/test/Bitcode/upgrade-cu-locals.ll.bc | Bin 2688 -> 2780 bytes .../DebugInfo/Generic/inlined-local-type.ll | 128 ++++++ .../Generic/lexical-block-retained-types.ll | 55 +++ .../DebugInfo/Generic/lexical-block-types.ll | 425 ++++++++++++++++++ .../Generic/verifier-invalid-disubprogram.ll | 2 +- .../X86/local-type-as-template-parameter.ll | 161 +++++++ .../split-dwarf-local-import.ll | 1 - .../split-dwarf-local-import2.ll | 1 - .../split-dwarf-local-import3.ll | 0 .../Transforms/Utils/CloningTest.cpp | 105 +++++ 29 files changed, 1254 insertions(+), 195 deletions(-) create mode 100644 llvm/test/Bitcode/clone-local-types.ll create mode 100644 llvm/test/DebugInfo/Generic/inlined-local-type.ll create mode 100644 llvm/test/DebugInfo/Generic/lexical-block-retained-types.ll create mode 100644 llvm/test/DebugInfo/Generic/lexical-block-types.ll create mode 100644 llvm/test/DebugInfo/X86/local-type-as-template-parameter.ll rename llvm/test/DebugInfo/{Generic => X86}/split-dwarf-local-import.ll (98%) rename llvm/test/DebugInfo/{Generic => X86}/split-dwarf-local-import2.ll (98%) rename llvm/test/DebugInfo/{Generic => X86}/split-dwarf-local-import3.ll (100%) diff --git a/clang/test/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-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/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/lib/Bitcode/Reader/MetadataLoader.cpp b/llvm/lib/Bitcode/Reader/MetadataLoader.cpp index ed0443f599a44..b9e1583f31462 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]; } @@ -538,6 +542,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 +551,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)); } } } @@ -710,6 +734,29 @@ class MetadataLoader::MetadataLoaderImpl { upgradeCULocals(); } + void cloneLocalTypes() { + for (Metadata *M : MetadataList) { + if (auto *SP = dyn_cast_or_null(M)) { + auto RetainedNodes = SP->getRetainedNodes(); + SmallVector MDs(RetainedNodes.begin(), RetainedNodes.end()); + bool HasChanged = false; + for (auto &N : MDs) + if (auto *T = dyn_cast(N)) + if (auto *LS = dyn_cast_or_null(T->getScope())) + if (auto *Parent = findEnclosingSubprogram(LS)) + if (Parent != SP) { + HasChanged = true; + auto NewT = T->clone(); + NewT->replaceOperandWith(1, SP); + N = MDNode::replaceWithUniqued(std::move(NewT)); + } + + if (HasChanged) + SP->replaceRetainedNodes(MDNode::get(Context, MDs)); + } + } + } + void callMDTypeCallback(Metadata **Val, unsigned TypeID); public: @@ -1086,6 +1133,7 @@ Error MetadataLoader::MetadataLoaderImpl::parseMetadata(bool ModuleLevel) { // placeholders, that we flush here. resolveForwardRefsAndPlaceholders(Placeholders); upgradeDebugInfo(ModuleLevel); + cloneLocalTypes(); // Return at the beginning of the block, since it is easy to skip it // entirely from there. Stream.ReadBlockEnd(); // Pop the abbrev block context. @@ -1117,6 +1165,7 @@ Error MetadataLoader::MetadataLoaderImpl::parseMetadata(bool ModuleLevel) { case BitstreamEntry::EndBlock: resolveForwardRefsAndPlaceholders(Placeholders); upgradeDebugInfo(ModuleLevel); + cloneLocalTypes(); return Error::success(); case BitstreamEntry::Record: // The interesting case. diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp index 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..49198ed7671dc 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 @@ -388,10 +388,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 +586,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; } @@ -614,6 +620,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 +635,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 +671,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; } @@ -768,7 +781,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 +798,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 +816,8 @@ DICompositeType *DIBuilder::createReplaceableCompositeType( nullptr, nullptr, Annotations) .release(); trackIfUnresolved(RetTy); + if (isa_and_nonnull(Scope)) + getSubprogramNodesTrackingVector(Scope).emplace_back(RetTy); return RetTy; } 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/Verifier.cpp b/llvm/lib/IR/Verifier.cpp index 7917712846990..3cea732c4e130 100644 --- a/llvm/lib/IR/Verifier.cpp +++ b/llvm/lib/IR/Verifier.cpp @@ -1564,9 +1564,9 @@ 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); } } 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/clone-local-types.ll b/llvm/test/Bitcode/clone-local-types.ll new file mode 100644 index 0000000000000..d2607303ec610 --- /dev/null +++ b/llvm/test/Bitcode/clone-local-types.ll @@ -0,0 +1,46 @@ +; RUN: llvm-as < %s -o %t0 +; RUN: llvm-dis %t0 -o - | FileCheck %s + +; Ensure that function-local types with the same ODR identifier belonging +; to different subprograms are not deduplicated when a module is being loaded. + +; CHECK: [[CU:![0-9]+]] = distinct !DICompileUnit +; CHECK: [[BAR:![0-9]+]] = distinct !DISubprogram(name: "bar", {{.*}}, retainedNodes: [[RN_BAR:![0-9]+]]) +; CHECK: [[RN_BAR]] = !{[[T2:![0-9]+]]} +; CHECK: [[T2]] = !DICompositeType(tag: DW_TAG_class_type, {{.*}}, identifier: "local_type") +; CHECK: [[FOO:![0-9]+]] = distinct !DISubprogram(name: "foo", {{.*}}, retainedNodes: [[RN_FOO:![0-9]+]]) +; CHECK: [[RN_FOO]] = !{[[T1:![0-9]+]]} +; CHECK: [[T1]] = !DICompositeType(tag: DW_TAG_class_type, {{.*}}, identifier: "local_type") + +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +declare void @foo() + +define void @bar(ptr %this) !dbg !10 { + call void @foo(), !dbg !17 + ret void, !dbg !13 +} + +!llvm.module.flags = !{!0, !1, !2, !3, !4, !5, !6, !7} +!llvm.dbg.cu = !{!8} + +!0 = !{i32 2, !"SDK Version", [2 x i32] [i32 11, i32 4]} +!1 = !{i32 7, !"Dwarf Version", i32 2} +!2 = !{i32 2, !"Debug Info Version", i32 3} +!3 = !{i32 1, !"wchar_size", i32 4} +!4 = !{i32 7, !"openmp", i32 50} +!5 = !{i32 7, !"openmp-device", i32 50} +!6 = !{i32 7, !"PIC Level", i32 2} +!7 = !{i32 7, !"frame-pointer", i32 2} +!8 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !9, producer: "clang version 14.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None) +!9 = !DIFile(filename: "tmp.cpp", directory: "/tmp/") +!10 = distinct !DISubprogram(name: "bar", scope: !9, file: !9, line: 68, type: !12, scopeLine: 68, flags: DIFlagPrototyped, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition | DISPFlagOptimized, unit: !8, retainedNodes: !16) +!11 = distinct !DISubprogram(name: "foo", scope: !9, file: !9, line: 68, type: !12, scopeLine: 68, flags: DIFlagPrototyped, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition | DISPFlagOptimized, unit: !8, retainedNodes: !16) +!12 = !DISubroutineType(types: !15) +!13 = !DILocation(line: 69, column: 18, scope: !10) +!14 = !DICompositeType(tag: DW_TAG_class_type, scope: !11, file: !9, line: 210, size: 8, flags: DIFlagTypePassByValue, elements: !15, identifier: "local_type") +!15 = !{} +!16 = !{!14} +!17 = !DILocation(line: 69, column: 18, scope: !11, inlinedAt: !18) +!18 = !DILocation(line: 4, column: 1, scope: !10) diff --git a/llvm/test/Bitcode/upgrade-cu-locals.ll b/llvm/test/Bitcode/upgrade-cu-locals.ll index 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 9d0ea8fd9a3704ea1fb3a92250500addc75f0587..f73d05668af5b16f9ee03e400e2877faab339a0d 100644 GIT binary patch delta 1080 zcmYk5e`p(J7{{NxT<^v2T79^Yj3eX zCa4ST^$4!hY-CFq`$I=`D*iELuKy_e=R!ISm`cGRL56hTA0ibxhWMA%_q{6H%X`oB z-1ohE-p}*!zJ}+nr}PA4Tzv8e=ETMO&yU}{_3PDJt*yNt=um-!|A&k}H9J|6zN*x) zz`pltPnw1RR8~Q?N^cBEwg8N+ilr9PDjEQ4=T<*p+z#fi@<&_{iTssC`dw0W77=EH z*c_9SV-gA2gRx8VvGQFm-JQ(6b&5U_YNDso)49lW?sWONHl>_yJF*18yXh_f*wo~p zm<*emHz?jF4h4_b4>b}uctVLpliLG87yzu1pRs>l{*5sRvWb=j0!DRwX=wDE!o5S^ zOf<4d(z>W#%y#_LCnWZe`V1s?y-g$s)sK#yRhD8G6zL2mOMNo{bO~2Hg=K}-PyV<3 zq#>kxqe*`rGx=)KWD%^aF+h`3j$?WG#v>f#G%w}o32^!&9A2suj-yWr%l!{I44%|a zL4<$~0r4859CHsjn1Ia@0;sw`ra%slionW?#uo{nWGL`pFMPtkj$s~}XJN(nVGSA# z4zOq@#XFE-No&eR!r!V%R>}e1C})ip;CBXV%(E=PadK-*f}AheRU;84oDiwJ=Y7|E^bU zPf#aaZ~liW_I^`?Z3Zt9R(*qaScJ9SUwv_3QfX~?T-W%k-X<>8UgL=j zd}ekQW|i;f7Vq_tsi(X3-JsT;L+v-o1XsyBp4;T7(%n-D;dAR-CSw{Q6hxpZ$6M`h_o> zw~J42>AA|JXZ&N&#J$xHv5k3on>Xg(ukv3b;h>Lna+U<_nRGCQVoQRnT!plNqI)a{8Ff@)_sAV5(!c!vJ$=_ozlX z<-P*D&c#bd?4=WAs{s;qIV!309p_*u{7g7Dd~zy26wOR#W081lI2)ci5Q#<(#PJXw bjz`1MY<74kUf$B@aE3>`OdxH z`DW%Ebf_IG{nX^@Bi~TU>Tgesr^SEYyYtha=j~yWY!HBg<7MPOE-e79rviCYP}RO~ z{gy^qhXoMu+C9!tM`Yd7K=*rCbF3X;nZdEU$I902B_4N`Kn>%1fgo0Y`Gqk@ou2o|DYupHkvUM?9G>Oq(ubP_5n(a4sm= zSie0NwAk3VXnE9Y+cR78YGZ0=kSKu|xb4S$DwriFb<1z>Yhhyjpv9A9-m_myaT zL(g*3@Li&q)A9VPEq8uL*&bj)mDsk61ggK}NN7uR?PWy%O-xFsCjs;lE_N)IMV>_D zKJvPZ8&dgmG{g!t_+n1JiKi&Q4*X8vq zpX}VuKUzy9i#hYNWpjYqoMKH>Ktt-x;^$Xi)Hp1+A%zQVaK+&J4", 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/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/test/DebugInfo/Generic/split-dwarf-local-import.ll b/llvm/test/DebugInfo/X86/split-dwarf-local-import.ll similarity index 98% rename from llvm/test/DebugInfo/Generic/split-dwarf-local-import.ll rename to llvm/test/DebugInfo/X86/split-dwarf-local-import.ll index 16a17edccc33e..8a565019eddfd 100644 --- a/llvm/test/DebugInfo/Generic/split-dwarf-local-import.ll +++ b/llvm/test/DebugInfo/X86/split-dwarf-local-import.ll @@ -1,4 +1,3 @@ -; REQUIRES: x86-registered-target ; RUN: %llc_dwarf -O1 -filetype=obj -split-dwarf-file=%t.dwo < %s | llvm-dwarfdump -debug-info - | FileCheck %s --implicit-check-not "{{DW_TAG|NULL}}" ; CHECK-LABEL: debug_info contents diff --git a/llvm/test/DebugInfo/Generic/split-dwarf-local-import2.ll b/llvm/test/DebugInfo/X86/split-dwarf-local-import2.ll similarity index 98% rename from llvm/test/DebugInfo/Generic/split-dwarf-local-import2.ll rename to llvm/test/DebugInfo/X86/split-dwarf-local-import2.ll index 0057f675f9b31..71f80992d1b04 100644 --- a/llvm/test/DebugInfo/Generic/split-dwarf-local-import2.ll +++ b/llvm/test/DebugInfo/X86/split-dwarf-local-import2.ll @@ -1,4 +1,3 @@ -; REQUIRES: x86-registered-target ; RUN: %llc_dwarf -split-dwarf-file=%t.dwo < %s | FileCheck %s ; Ensure function-local DW_TAG_imported_declaration get skipped if its parent subprogram was not emitted. diff --git a/llvm/test/DebugInfo/Generic/split-dwarf-local-import3.ll b/llvm/test/DebugInfo/X86/split-dwarf-local-import3.ll similarity index 100% rename from llvm/test/DebugInfo/Generic/split-dwarf-local-import3.ll rename to llvm/test/DebugInfo/X86/split-dwarf-local-import3.ll diff --git a/llvm/unittests/Transforms/Utils/CloningTest.cpp b/llvm/unittests/Transforms/Utils/CloningTest.cpp index 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) From e97af0a20734f79c1b7e9b3c35f28efe783bf569 Mon Sep 17 00:00:00 2001 From: Vladislav Dzhidzhoev Date: Wed, 15 Oct 2025 20:06:23 +0200 Subject: [PATCH 2/2] Reland "[DebugMetadata][DwarfDebug] Support function-local types in lexical block scopes (4/7)" This is an attempt to merge https://reviews.llvm.org/D144006 with LTO fix. The last merge attempt was https://github.com/llvm/llvm-project/pull/75385. The issue with it was investigated in https://github.com/llvm/llvm-project/pull/75385#issuecomment-2386684121. If the same (in the sense of ODR identifier/ODR uniquing rules) local type is present in two modules, and these modules are linked together, the type gets uniqued. A DIType, that happens to be loaded first, survives linking, and the references on other types with the same ODR identifier from the modules loaded later are replaced with the references on the DIType loaded first. Since defintion subprograms, in scope of which these types are located, are not deduplicated, the linker output may contain multiple DISubprogram's having the same (uniqued) type in their retainedNodes lists. Further compilation of such modules causes crashes. To tackle that, * previous solution to handle LTO linking with local types in retainedNodes is removed (cloneLocalTypes() function), * for each loaded distinct (definition) DISubprogram, its retainedNodes list is scanned after loading, and DITypes with a scope of another subprogram are removed. If something from a Function corresponding to the DISubprogram references uniqued type, we rely on cross-CU links. With this approach, clang builds without crashes in FullLTO (which is not the case for https://github.com/llvm/llvm-project/pull/75385). Additionally: * a check is added to Verifier to report about local types located in a wrong retainedNodes list, * DIBuilder's methods for type creation are updated, as https://reviews.llvm.org/D144006 has gotten slightly out-of-date. Commit https://github.com/llvm/llvm-project/pull/75385 and the new changes are placed in separate commits to simplify review process. --- .../DebugInfo/CXX/lambda-capture-packs.cpp | 15 +++--- llvm/include/llvm/AsmParser/LLParser.h | 4 ++ llvm/include/llvm/IR/DebugInfoMetadata.h | 15 ++++++ llvm/lib/AsmParser/LLParser.cpp | 7 +++ llvm/lib/Bitcode/Reader/MetadataLoader.cpp | 33 ++++--------- llvm/lib/IR/DIBuilder.cpp | 26 ++++++++--- llvm/lib/IR/DebugInfoMetadata.cpp | 39 ++++++++++++++++ llvm/lib/IR/Verifier.cpp | 10 ++++ ...one-local-types.ll => local-type-scope.ll} | 16 ++++--- .../split-dwarf-local-import.ll | 1 + .../split-dwarf-local-import2.ll | 1 + .../split-dwarf-local-import3.ll | 0 .../Inputs/cleanup-retained-nodes.ll | 17 +++++++ .../DebugInfo/X86/cleanup-retained-nodes.ll | 46 +++++++++++++++++++ .../X86/llparser-cleanup-retained-nodes.ll | 37 +++++++++++++++ 15 files changed, 220 insertions(+), 47 deletions(-) rename llvm/test/Bitcode/{clone-local-types.ll => local-type-scope.ll} (79%) rename llvm/test/DebugInfo/{X86 => Generic}/split-dwarf-local-import.ll (98%) rename llvm/test/DebugInfo/{X86 => Generic}/split-dwarf-local-import2.ll (98%) rename llvm/test/DebugInfo/{X86 => Generic}/split-dwarf-local-import3.ll (100%) create mode 100644 llvm/test/DebugInfo/Inputs/cleanup-retained-nodes.ll create mode 100644 llvm/test/DebugInfo/X86/cleanup-retained-nodes.ll create mode 100644 llvm/test/DebugInfo/X86/llparser-cleanup-retained-nodes.ll 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/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/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 b9e1583f31462..b4ca5ef4eaf26 100644 --- a/llvm/lib/Bitcode/Reader/MetadataLoader.cpp +++ b/llvm/lib/Bitcode/Reader/MetadataLoader.cpp @@ -452,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; @@ -732,29 +736,7 @@ class MetadataLoader::MetadataLoaderImpl { upgradeCUVariables(); if (ModuleLevel) upgradeCULocals(); - } - - void cloneLocalTypes() { - for (Metadata *M : MetadataList) { - if (auto *SP = dyn_cast_or_null(M)) { - auto RetainedNodes = SP->getRetainedNodes(); - SmallVector MDs(RetainedNodes.begin(), RetainedNodes.end()); - bool HasChanged = false; - for (auto &N : MDs) - if (auto *T = dyn_cast(N)) - if (auto *LS = dyn_cast_or_null(T->getScope())) - if (auto *Parent = findEnclosingSubprogram(LS)) - if (Parent != SP) { - HasChanged = true; - auto NewT = T->clone(); - NewT->replaceOperandWith(1, SP); - N = MDNode::replaceWithUniqued(std::move(NewT)); - } - - if (HasChanged) - SP->replaceRetainedNodes(MDNode::get(Context, MDs)); - } - } + DISubprogram::cleanupRetainedNodes(std::exchange(NewDistinctSPs, {})); } void callMDTypeCallback(Metadata **Val, unsigned TypeID); @@ -1133,7 +1115,6 @@ Error MetadataLoader::MetadataLoaderImpl::parseMetadata(bool ModuleLevel) { // placeholders, that we flush here. resolveForwardRefsAndPlaceholders(Placeholders); upgradeDebugInfo(ModuleLevel); - cloneLocalTypes(); // Return at the beginning of the block, since it is easy to skip it // entirely from there. Stream.ReadBlockEnd(); // Pop the abbrev block context. @@ -1165,7 +1146,6 @@ Error MetadataLoader::MetadataLoaderImpl::parseMetadata(bool ModuleLevel) { case BitstreamEntry::EndBlock: resolveForwardRefsAndPlaceholders(Placeholders); upgradeDebugInfo(ModuleLevel); - cloneLocalTypes(); return Error::success(); case BitstreamEntry::Record: // The interesting case. @@ -2048,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/IR/DIBuilder.cpp b/llvm/lib/IR/DIBuilder.cpp index 49198ed7671dc..2ee021e92415a 100644 --- a/llvm/lib/IR/DIBuilder.cpp +++ b/llvm/lib/IR/DIBuilder.cpp @@ -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 * @@ -604,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; } @@ -688,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; } @@ -723,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; } @@ -877,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/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 3cea732c4e130..dc29218516359 100644 --- a/llvm/lib/IR/Verifier.cpp +++ b/llvm/lib/IR/Verifier.cpp @@ -1568,6 +1568,16 @@ void Verifier::visitDISubprogram(const DISubprogram &N) { "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/test/Bitcode/clone-local-types.ll b/llvm/test/Bitcode/local-type-scope.ll similarity index 79% rename from llvm/test/Bitcode/clone-local-types.ll rename to llvm/test/Bitcode/local-type-scope.ll index d2607303ec610..7652093409ac3 100644 --- a/llvm/test/Bitcode/clone-local-types.ll +++ b/llvm/test/Bitcode/local-type-scope.ll @@ -1,16 +1,18 @@ -; RUN: llvm-as < %s -o %t0 -; RUN: llvm-dis %t0 -o - | FileCheck %s +; 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 -; Ensure that function-local types with the same ODR identifier belonging -; to different subprograms are not deduplicated when a module is being loaded. +; 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]] = !{[[T2:![0-9]+]]} -; CHECK: [[T2]] = !DICompositeType(tag: DW_TAG_class_type, {{.*}}, identifier: "local_type") +; 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, {{.*}}, identifier: "local_type") +; 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" diff --git a/llvm/test/DebugInfo/X86/split-dwarf-local-import.ll b/llvm/test/DebugInfo/Generic/split-dwarf-local-import.ll similarity index 98% rename from llvm/test/DebugInfo/X86/split-dwarf-local-import.ll rename to llvm/test/DebugInfo/Generic/split-dwarf-local-import.ll index 8a565019eddfd..16a17edccc33e 100644 --- a/llvm/test/DebugInfo/X86/split-dwarf-local-import.ll +++ b/llvm/test/DebugInfo/Generic/split-dwarf-local-import.ll @@ -1,3 +1,4 @@ +; REQUIRES: x86-registered-target ; RUN: %llc_dwarf -O1 -filetype=obj -split-dwarf-file=%t.dwo < %s | llvm-dwarfdump -debug-info - | FileCheck %s --implicit-check-not "{{DW_TAG|NULL}}" ; CHECK-LABEL: debug_info contents diff --git a/llvm/test/DebugInfo/X86/split-dwarf-local-import2.ll b/llvm/test/DebugInfo/Generic/split-dwarf-local-import2.ll similarity index 98% rename from llvm/test/DebugInfo/X86/split-dwarf-local-import2.ll rename to llvm/test/DebugInfo/Generic/split-dwarf-local-import2.ll index 71f80992d1b04..0057f675f9b31 100644 --- a/llvm/test/DebugInfo/X86/split-dwarf-local-import2.ll +++ b/llvm/test/DebugInfo/Generic/split-dwarf-local-import2.ll @@ -1,3 +1,4 @@ +; REQUIRES: x86-registered-target ; RUN: %llc_dwarf -split-dwarf-file=%t.dwo < %s | FileCheck %s ; Ensure function-local DW_TAG_imported_declaration get skipped if its parent subprogram was not emitted. diff --git a/llvm/test/DebugInfo/X86/split-dwarf-local-import3.ll b/llvm/test/DebugInfo/Generic/split-dwarf-local-import3.ll similarity index 100% rename from llvm/test/DebugInfo/X86/split-dwarf-local-import3.ll rename to llvm/test/DebugInfo/Generic/split-dwarf-local-import3.ll 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}