diff --git a/llvm/include/llvm/CodeGen/DIE.h b/llvm/include/llvm/CodeGen/DIE.h --- a/llvm/include/llvm/CodeGen/DIE.h +++ b/llvm/include/llvm/CodeGen/DIE.h @@ -554,6 +554,25 @@ }; template class IntrusiveBackList : IntrusiveBackListBase { + void releaseNodes(std::function f) { + if (empty()) + return; + + T *FirstNode = static_cast(Last->Next.getPointer()); + T *IterNode = FirstNode; + do { + // Keep a pointer to the node and increment the iterator. + T *TmpNode = IterNode; + IterNode = static_cast(IterNode->Next.getPointer()); + + // Unlink the node. + TmpNode->Next.setPointerAndInt(TmpNode, true); + f(TmpNode); + } while (IterNode != FirstNode); + + Last = nullptr; + } + public: using IntrusiveBackListBase::empty; @@ -569,22 +588,11 @@ } void takeNodes(IntrusiveBackList &Other) { - if (Other.empty()) - return; - - T *FirstNode = static_cast(Other.Last->Next.getPointer()); - T *IterNode = FirstNode; - do { - // Keep a pointer to the node and increment the iterator. - T *TmpNode = IterNode; - IterNode = static_cast(IterNode->Next.getPointer()); - - // Unlink the node and push it back to this list. - TmpNode->Next.setPointerAndInt(TmpNode, true); - push_back(*TmpNode); - } while (IterNode != FirstNode); + Other.releaseNodes([&](T *t) { push_back(*t); }); + } - Other.Last = nullptr; + void releaseNodes(SmallVectorImpl &Other) { + releaseNodes([&](T *t) { Other.push_back(t); }); } class const_iterator; @@ -857,6 +865,13 @@ return Children.front(); } + void releaseChildren(SmallVectorImpl &Result) { + Children.releaseNodes(Result); + assert(Children.empty()); + for (auto *Child : Result) + Child->Owner = nullptr; + } + /// Find a value in the DIE with the attribute given. /// /// Returns a default-constructed DIEValue (where \a DIEValue::getType() diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.h b/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.h --- a/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.h +++ b/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.h @@ -270,6 +270,7 @@ void finishSubprogramDefinition(const DISubprogram *SP); void finishEntityDefinition(const DbgEntity *Entity); + void finishAbstractSPDIEs(); /// Find abstract variable associated with Var. using InlinedEntity = DbgValueHistoryMap::InlinedEntity; diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp --- a/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp @@ -1320,6 +1320,43 @@ addLabelAddress(*Die, dwarf::DW_AT_low_pc, Sym); } +void DwarfCompileUnit::finishAbstractSPDIEs() { + auto BelongsToConcreteDIE = [](DIE *Child) -> bool { + // DIEs with location attributes might be global variables and those belong + // to the abstract origin DIE. + return Child->findAttribute(dwarf::DW_AT_abstract_origin) || + Child->findAttribute(dwarf::DW_AT_low_pc) || + Child->findAttribute(dwarf::DW_AT_high_pc) || + Child->findAttribute(dwarf::DW_AT_ranges) || + Child->findAttribute(dwarf::DW_AT_entry_pc) || + Child->findAttribute(dwarf::DW_AT_return_addr) || + Child->findAttribute(dwarf::DW_AT_start_scope) || + Child->findAttribute(dwarf::DW_AT_segment); + }; + for (auto &Pair : getAbstractSPDies()) { + auto *InlinedSP = cast(Pair.first); + auto *AbstractDIE = Pair.second; + if (DIE *ConcreteDIE = getDIE(InlinedSP)) { + // If an inlined subprogram DIE exists, then it may have been given + // children such as global variable DIEs. Move these children to the + // abstract origin DIE so that other inlined subroutines may find them. + SmallVector Children; + ConcreteDIE->releaseChildren(Children); + for (DIE *Child : Children) { + // TODO: For children that are moved to the abstract origin DIE, should + // we also reference them in the concrete DIE with + // DW_AT_abstract_origin? + if (BelongsToConcreteDIE(Child)) + ConcreteDIE->addChild(Child); + else + AbstractDIE->addChild(Child); + } + if (!ConcreteDIE->findAttribute(dwarf::DW_AT_abstract_origin)) + addDIEEntry(*ConcreteDIE, dwarf::DW_AT_abstract_origin, *AbstractDIE); + } + } +} + DbgEntity *DwarfCompileUnit::getExistingAbstractEntity(const DINode *Node) { auto &AbstractEntities = getAbstractEntities(); auto I = AbstractEntities.find(Node); diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp --- a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp @@ -1290,6 +1290,7 @@ auto &TheCU = *P.second; if (TheCU.getCUNode()->isDebugDirectivesOnly()) continue; + TheCU.finishAbstractSPDIEs(); // Emit DW_AT_containing_type attribute to connect types with their // vtable holding type. TheCU.constructContainingTypeDIEs(); diff --git a/llvm/test/DebugInfo/Generic/import-inlined-declaration.ll b/llvm/test/DebugInfo/Generic/import-inlined-declaration.ll new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/Generic/import-inlined-declaration.ll @@ -0,0 +1,73 @@ +; RUN: %llc_dwarf -O0 -filetype=obj < %s | llvm-dwarfdump - | FileCheck --implicit-check-not "{{DW_TAG|NULL}}" %s + +; namespace ns { +; inline __attribute__((always_inline)) +; void foo() { int a = 4; } +; } +; +; void goo() { +; using ns::foo; +; foo(); +; } + +; Ensure that imported declarations reference the correct subprograms even if +; those subprograms are inlined. + +; CHECK: DW_TAG_compile_unit +; CHECK: DW_TAG_namespace +; CHECK: DW_AT_name ("ns") +; CHECK: [[FOO_ABSTRACT:0x.*]]: DW_TAG_subprogram +; CHECK: DW_AT_name ("foo") +; CHECK: DW_TAG_variable +; CHECK: NULL +; CHECK: [[FOO:0x.*]]: DW_TAG_subprogram +; CHECK: DW_AT_abstract_origin ([[FOO_ABSTRACT]] +; CHECK: NULL +; CHECK: DW_TAG_base_type +; CHECK: DW_TAG_subprogram +; CHECK: DW_AT_name ("goo") +; CHECK: DW_TAG_imported_declaration +; CHECK: DW_AT_import ([[FOO]]) +; CHECK: DW_TAG_inlined_subroutine +; CHECK: DW_TAG_variable +; CHECK: NULL +; CHECK: NULL +; CHECK: NULL + +; Function Attrs: mustprogress noinline optnone uwtable +define dso_local void @_Z3goov() !dbg !4 { +entry: + %a.i = alloca i32, align 4 + call void @llvm.dbg.declare(metadata i32* %a.i, metadata !16, metadata !DIExpression()), !dbg !18 + store i32 4, i32* %a.i, align 4, !dbg !18 + ret void, !dbg !20 +} + +; Function Attrs: nofree nosync nounwind readnone speculatable willreturn +declare void @llvm.dbg.declare(metadata, metadata, metadata) + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!10, !11, !12, !13, !14} +!llvm.ident = !{!15} + +!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, splitDebugInlining: false, nameTableKind: None) +!1 = !DIFile(filename: "imported-inlined-declaration.cpp", directory: "") +!2 = !{!3} +!3 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !4, entity: !8, file: !1, line: 7) +!4 = distinct !DISubprogram(name: "goo", linkageName: "_Z3goov", scope: !1, file: !1, line: 6, type: !5, scopeLine: 6, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !7) +!5 = !DISubroutineType(types: !6) +!6 = !{null} +!7 = !{} +!8 = distinct !DISubprogram(name: "foo", linkageName: "_ZN2ns3fooEv", scope: !9, file: !1, line: 3, type: !5, scopeLine: 3, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !7) +!9 = !DINamespace(name: "ns", scope: null) +!10 = !{i32 7, !"Dwarf Version", i32 4} +!11 = !{i32 2, !"Debug Info Version", i32 3} +!12 = !{i32 1, !"wchar_size", i32 4} +!13 = !{i32 7, !"uwtable", i32 1} +!14 = !{i32 7, !"frame-pointer", i32 2} +!15 = !{!"clang version 14.0.0"} +!16 = !DILocalVariable(name: "a", scope: !8, file: !1, line: 3, type: !17) +!17 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!18 = !DILocation(line: 3, column: 18, scope: !8, inlinedAt: !19) +!19 = distinct !DILocation(line: 8, column: 2, scope: !4) +!20 = !DILocation(line: 9, column: 1, scope: !4) diff --git a/llvm/test/DebugInfo/Generic/inlined-local-type.ll b/llvm/test/DebugInfo/Generic/inlined-local-type.ll new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/Generic/inlined-local-type.ll @@ -0,0 +1,190 @@ +; RUN: %llc_dwarf -O0 -filetype=obj < %s | llvm-dwarfdump -debug-info - | FileCheck --implicit-check-not "{{DW_TAG|NULL}}" %s + +; inline __attribute__((always_inline)) +; int removed() { struct A {int a=0;}; A a; return a.a; } +; +; __attribute__((always_inline)) +; int not_removed() { struct B {int b=1;}; B b; return b.b; } +; +; int foo() { return removed() + not_removed(); } + +; Ensure that function-local types have the correct subprogram parent even if +; those subprograms are inlined. + +; CHECK: DW_TAG_compile_unit +; CHECK: DW_TAG_subprogram +; CHECK: DW_AT_low_pc +; CHECK: DW_AT_abstract_origin ([[NOT_REMOVED:0x.*]] "_Z11not_removedv") +; CHECK: DW_TAG_variable +; CHECK: NULL +; CHECK: DW_TAG_base_type +; CHECK: DW_TAG_subprogram +; CHECK: DW_AT_abstract_origin ([[REMOVED:0x.*]] "_Z7removedv") +; CHECK: DW_TAG_pointer_type +; CHECK: DW_TAG_subprogram +; CHECK: DW_TAG_formal_parameter +; CHECK: NULL +; CHECK: [[REMOVED]]: DW_TAG_subprogram +; CHECK: DW_AT_name ("removed") +; CHECK: DW_AT_inline (DW_INL_inlined) +; CHECK: DW_TAG_variable +; CHECK: DW_TAG_structure_type +; CHECK: DW_TAG_member +; CHECK: DW_TAG_subprogram +; CHECK: DW_TAG_formal_parameter +; CHECK: NULL +; CHECK: NULL +; CHECK: NULL +; CHECK: [[NOT_REMOVED]]: DW_TAG_subprogram +; CHECK: DW_AT_name ("not_removed") +; CHECK: DW_AT_inline (DW_INL_inlined) +; CHECK: DW_TAG_variable +; CHECK: DW_TAG_structure_type +; CHECK: DW_TAG_member +; CHECK: DW_TAG_subprogram +; CHECK: DW_TAG_formal_parameter +; CHECK: NULL +; CHECK: NULL +; CHECK: NULL +; CHECK: DW_TAG_subprogram +; CHECK: DW_AT_name ("foo") +; CHECK: DW_TAG_inlined_subroutine +; CHECK: DW_TAG_variable +; CHECK: NULL +; CHECK: DW_TAG_inlined_subroutine +; CHECK: DW_TAG_variable +; CHECK: NULL +; CHECK: NULL +; CHECK: DW_TAG_pointer_type +; CHECK: DW_TAG_subprogram +; CHECK: DW_TAG_formal_parameter +; CHECK: NULL +; CHECK: DW_TAG_pointer_type +; CHECK: DW_TAG_pointer_type +; CHECK: NULL + +source_filename = "/app/example.cpp" +target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128" +target triple = "aarch64-unknown-linux-gnu" + +%struct.B = type { i32 } +%struct.A = type { i32 } + +$_ZZ7removedvEN1AC2Ev = comdat any + +define dso_local i32 @_Z11not_removedv() #0 !dbg !5 { + %1 = alloca %struct.B, align 4 + call void @llvm.dbg.declare(metadata %struct.B* %1, metadata !26, metadata !DIExpression()), !dbg !27 + call void @_ZZ11not_removedvEN1BC2Ev(%struct.B* nonnull align 4 dereferenceable(4) %1) #4, !dbg !27 + %2 = getelementptr inbounds %struct.B, %struct.B* %1, i32 0, i32 0, !dbg !28 + %3 = load i32, i32* %2, align 4, !dbg !28 + ret i32 %3, !dbg !29 +} + +declare void @llvm.dbg.declare(metadata, metadata, metadata) #1 + +define internal void @_ZZ11not_removedvEN1BC2Ev(%struct.B* nonnull align 4 dereferenceable(4) %0) unnamed_addr #2 align 2 !dbg !30 { + %2 = alloca %struct.B*, align 8 + store %struct.B* %0, %struct.B** %2, align 8 + call void @llvm.dbg.declare(metadata %struct.B** %2, metadata !35, metadata !DIExpression()), !dbg !37 + %3 = load %struct.B*, %struct.B** %2, align 8 + %4 = getelementptr inbounds %struct.B, %struct.B* %3, i32 0, i32 0, !dbg !38 + store i32 1, i32* %4, align 4, !dbg !38 + ret void, !dbg !39 +} + +define dso_local i32 @_Z3foov() #3 !dbg !40 { + %1 = alloca %struct.A, align 4 + %2 = alloca %struct.B, align 4 + call void @llvm.dbg.declare(metadata %struct.A* %1, metadata !41, metadata !DIExpression()) #4, !dbg !42 + call void @_ZZ7removedvEN1AC2Ev(%struct.A* nonnull align 4 dereferenceable(4) %1) #4, !dbg !42 + %3 = getelementptr inbounds %struct.A, %struct.A* %1, i32 0, i32 0, !dbg !44 + %4 = load i32, i32* %3, align 4, !dbg !44 + call void @llvm.dbg.declare(metadata %struct.B* %2, metadata !26, metadata !DIExpression()) #4, !dbg !45 + call void @_ZZ11not_removedvEN1BC2Ev(%struct.B* nonnull align 4 dereferenceable(4) %2) #4, !dbg !45 + %5 = getelementptr inbounds %struct.B, %struct.B* %2, i32 0, i32 0, !dbg !47 + %6 = load i32, i32* %5, align 4, !dbg !47 + %7 = add nsw i32 %4, %6, !dbg !48 + ret i32 %7, !dbg !49 +} + +define linkonce_odr dso_local void @_ZZ7removedvEN1AC2Ev(%struct.A* nonnull align 4 dereferenceable(4) %0) unnamed_addr #2 comdat align 2 !dbg !50 { + %2 = alloca %struct.A*, align 8 + store %struct.A* %0, %struct.A** %2, align 8 + call void @llvm.dbg.declare(metadata %struct.A** %2, metadata !55, metadata !DIExpression()), !dbg !57 + %3 = load %struct.A*, %struct.A** %2, align 8 + %4 = getelementptr inbounds %struct.A, %struct.A* %3, i32 0, i32 0, !dbg !58 + store i32 0, i32* %4, align 4, !dbg !58 + ret void, !dbg !59 +} + +attributes #0 = { alwaysinline mustprogress nounwind uwtable "frame-pointer"="non-leaf" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="generic" "target-features"="+neon" } +attributes #1 = { nofree nosync nounwind readnone speculatable willreturn } +attributes #2 = { noinline nounwind optnone uwtable "frame-pointer"="non-leaf" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="generic" "target-features"="+neon" } +attributes #3 = { mustprogress noinline optnone uwtable "frame-pointer"="non-leaf" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="generic" "target-features"="+neon" } +attributes #4 = { nounwind } + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!16, !17, !18, !19, !20, !21, !22, !23, !24} +!llvm.ident = !{!25} + +!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang version 14.0.0 (https://github.com/llvm/llvm-project.git 3f96f7b30c91b912de1c6c7c03ab6a4c18e8aa26)", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, retainedTypes: !2, splitDebugInlining: false, nameTableKind: None) +!1 = !DIFile(filename: "/app/example.cpp", directory: "/app") +!2 = !{!3, !12} +!3 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "B", scope: !5, file: !4, line: 5, size: 32, flags: DIFlagTypePassByValue | DIFlagNonTrivial, elements: !10) +!4 = !DIFile(filename: "example.cpp", directory: "/app") +!5 = distinct !DISubprogram(name: "not_removed", linkageName: "_Z11not_removedv", scope: !4, file: !4, line: 5, type: !6, scopeLine: 5, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !9) +!6 = !DISubroutineType(types: !7) +!7 = !{!8} +!8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!9 = !{} +!10 = !{!11} +!11 = !DIDerivedType(tag: DW_TAG_member, name: "b", scope: !3, file: !4, line: 5, baseType: !8, size: 32) +!12 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "A", scope: !13, file: !4, line: 2, size: 32, flags: DIFlagTypePassByValue | DIFlagNonTrivial, elements: !14, identifier: "_ZTSZ7removedvE1A") +!13 = distinct !DISubprogram(name: "removed", linkageName: "_Z7removedv", scope: !4, file: !4, line: 2, type: !6, scopeLine: 2, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !9) +!14 = !{!15} +!15 = !DIDerivedType(tag: DW_TAG_member, name: "a", scope: !12, file: !4, line: 2, baseType: !8, size: 32) +!16 = !{i32 7, !"Dwarf Version", i32 4} +!17 = !{i32 2, !"Debug Info Version", i32 3} +!18 = !{i32 1, !"wchar_size", i32 4} +!19 = !{i32 1, !"branch-target-enforcement", i32 0} +!20 = !{i32 1, !"sign-return-address", i32 0} +!21 = !{i32 1, !"sign-return-address-all", i32 0} +!22 = !{i32 1, !"sign-return-address-with-bkey", i32 0} +!23 = !{i32 7, !"uwtable", i32 1} +!24 = !{i32 7, !"frame-pointer", i32 1} +!25 = !{!"clang version 14.0.0 (https://github.com/llvm/llvm-project.git 3f96f7b30c91b912de1c6c7c03ab6a4c18e8aa26)"} +!26 = !DILocalVariable(name: "b", scope: !5, file: !4, line: 5, type: !3) +!27 = !DILocation(line: 5, column: 44, scope: !5) +!28 = !DILocation(line: 5, column: 56, scope: !5) +!29 = !DILocation(line: 5, column: 47, scope: !5) +!30 = distinct !DISubprogram(name: "B", linkageName: "_ZZ11not_removedvEN1BC2Ev", scope: !3, file: !4, line: 5, type: !31, scopeLine: 5, flags: DIFlagArtificial | DIFlagPrototyped, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition, unit: !0, declaration: !34, retainedNodes: !9) +!31 = !DISubroutineType(types: !32) +!32 = !{null, !33} +!33 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !3, size: 64, flags: DIFlagArtificial | DIFlagObjectPointer) +!34 = !DISubprogram(name: "B", scope: !3, type: !31, flags: DIFlagArtificial | DIFlagPrototyped, spFlags: DISPFlagLocalToUnit) +!35 = !DILocalVariable(name: "this", arg: 1, scope: !30, type: !36, flags: DIFlagArtificial | DIFlagObjectPointer) +!36 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !3, size: 64) +!37 = !DILocation(line: 0, scope: !30) +!38 = !DILocation(line: 5, column: 35, scope: !30) +!39 = !DILocation(line: 5, column: 28, scope: !30) +!40 = distinct !DISubprogram(name: "foo", linkageName: "_Z3foov", scope: !4, file: !4, line: 7, type: !6, scopeLine: 7, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !9) +!41 = !DILocalVariable(name: "a", scope: !13, file: !4, line: 2, type: !12) +!42 = !DILocation(line: 2, column: 40, scope: !13, inlinedAt: !43) +!43 = distinct !DILocation(line: 7, column: 20, scope: !40) +!44 = !DILocation(line: 2, column: 52, scope: !13, inlinedAt: !43) +!45 = !DILocation(line: 5, column: 44, scope: !5, inlinedAt: !46) +!46 = distinct !DILocation(line: 7, column: 32, scope: !40) +!47 = !DILocation(line: 5, column: 56, scope: !5, inlinedAt: !46) +!48 = !DILocation(line: 7, column: 30, scope: !40) +!49 = !DILocation(line: 7, column: 13, scope: !40) +!50 = distinct !DISubprogram(name: "A", linkageName: "_ZZ7removedvEN1AC2Ev", scope: !12, file: !4, line: 2, type: !51, scopeLine: 2, flags: DIFlagArtificial | DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, declaration: !54, retainedNodes: !9) +!51 = !DISubroutineType(types: !52) +!52 = !{null, !53} +!53 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !12, size: 64, flags: DIFlagArtificial | DIFlagObjectPointer) +!54 = !DISubprogram(name: "A", scope: !12, type: !51, flags: DIFlagArtificial | DIFlagPrototyped, spFlags: 0) +!55 = !DILocalVariable(name: "this", arg: 1, scope: !50, type: !56, flags: DIFlagArtificial | DIFlagObjectPointer) +!56 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !12, size: 64) +!57 = !DILocation(line: 0, scope: !50) +!58 = !DILocation(line: 2, column: 31, scope: !50) +!59 = !DILocation(line: 2, column: 24, scope: !50) diff --git a/llvm/test/DebugInfo/Generic/inlined-static-var.ll b/llvm/test/DebugInfo/Generic/inlined-static-var.ll new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/Generic/inlined-static-var.ll @@ -0,0 +1,108 @@ +; RUN: %llc_dwarf -O0 -filetype=obj < %s | llvm-dwarfdump -debug-info - | FileCheck --implicit-check-not "{{DW_TAG|NULL}}" %s + +; inline __attribute__((always_inline)) +; int removed() { static int A; return A++; } +; +; __attribute__((always_inline)) +; int not_removed() { static int B; return B++; } +; +; int foo() { return removed() + not_removed(); }} + +; Ensure that global variables reference the correct subprograms even if +; those subprograms are inlined. + +; CHECK: DW_TAG_compile_unit +; CHECK: DW_TAG_subprogram +; CHECK: DW_AT_low_pc +; CHECK: DW_AT_abstract_origin ([[NOT_REMOVED:0x.*]] "_Z11not_removedv") +; CHECK: DW_TAG_base_type +; CHECK: DW_TAG_subprogram +; CHECK: DW_AT_abstract_origin ([[REMOVED:0x.*]] "_Z7removedv") +; CHECK: [[REMOVED]]: DW_TAG_subprogram +; CHECK: DW_AT_name ("removed") +; CHECK: DW_AT_inline (DW_INL_inlined) +; CHECK: DW_TAG_variable +; CHECK: DW_AT_name ("A") +; CHECK: NULL +; CHECK: [[NOT_REMOVED]]: DW_TAG_subprogram +; CHECK: DW_AT_name ("not_removed") +; CHECK: DW_AT_inline (DW_INL_inlined) +; CHECK: DW_TAG_variable +; CHECK: DW_AT_name ("B") +; CHECK: NULL +; CHECK: DW_TAG_subprogram +; CHECK: DW_AT_name ("foo") +; CHECK: DW_TAG_inlined_subroutine +; CHECK: DW_AT_abstract_origin ([[REMOVED]] "_Z7removedv") +; CHECK: DW_TAG_inlined_subroutine +; CHECK: DW_AT_abstract_origin ([[NOT_REMOVED]] "_Z11not_removedv") +; CHECK: NULL +; CHECK: NULL + +source_filename = "/app/example.cpp" +target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128" +target triple = "aarch64-unknown-linux-gnu" + +$_ZZ7removedvE1A = comdat any + +@_ZZ11not_removedvE1A = internal global i32 0, align 4, !dbg !0 +@_ZZ7removedvE1A = linkonce_odr dso_local global i32 0, comdat, align 4, !dbg !10 + +define dso_local i32 @_Z11not_removedv() #0 !dbg !2 { + %1 = load i32, i32* @_ZZ11not_removedvE1A, align 4, !dbg !24 + %2 = add nsw i32 %1, 1, !dbg !24 + store i32 %2, i32* @_ZZ11not_removedvE1A, align 4, !dbg !24 + ret i32 %1, !dbg !25 +} + +define dso_local i32 @_Z3foov() #1 !dbg !26 { + %1 = load i32, i32* @_ZZ7removedvE1A, align 4, !dbg !27 + %2 = add nsw i32 %1, 1, !dbg !27 + store i32 %2, i32* @_ZZ7removedvE1A, align 4, !dbg !27 + %3 = load i32, i32* @_ZZ11not_removedvE1A, align 4, !dbg !29 + %4 = add nsw i32 %3, 1, !dbg !29 + store i32 %4, i32* @_ZZ11not_removedvE1A, align 4, !dbg !29 + %5 = add nsw i32 %1, %3, !dbg !31 + ret i32 %5, !dbg !32 +} + +attributes #0 = { alwaysinline mustprogress nounwind uwtable "frame-pointer"="non-leaf" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="generic" "target-features"="+neon" } +attributes #1 = { mustprogress noinline optnone uwtable "frame-pointer"="non-leaf" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="generic" "target-features"="+neon" } + +!llvm.dbg.cu = !{!7} +!llvm.module.flags = !{!14, !15, !16, !17, !18, !19, !20, !21, !22} +!llvm.ident = !{!23} + +!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) +!1 = distinct !DIGlobalVariable(name: "B", scope: !2, file: !3, line: 5, type: !6, isLocal: true, isDefinition: true) +!2 = distinct !DISubprogram(name: "not_removed", linkageName: "_Z11not_removedv", scope: !3, file: !3, line: 5, type: !4, scopeLine: 5, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !7, retainedNodes: !13) +!3 = !DIFile(filename: "example.cpp", directory: "/app") +!4 = !DISubroutineType(types: !5) +!5 = !{!6} +!6 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!7 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !8, producer: "clang version 14.0.0 (https://github.com/llvm/llvm-project.git 3f96f7b30c91b912de1c6c7c03ab6a4c18e8aa26)", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, globals: !9, splitDebugInlining: false, nameTableKind: None) +!8 = !DIFile(filename: "/app/example.cpp", directory: "/app") +!9 = !{!0, !10} +!10 = !DIGlobalVariableExpression(var: !11, expr: !DIExpression()) +!11 = distinct !DIGlobalVariable(name: "A", scope: !12, file: !3, line: 2, type: !6, isLocal: false, isDefinition: true) +!12 = distinct !DISubprogram(name: "removed", linkageName: "_Z7removedv", scope: !3, file: !3, line: 2, type: !4, scopeLine: 2, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !7, retainedNodes: !13) +!13 = !{} +!14 = !{i32 7, !"Dwarf Version", i32 4} +!15 = !{i32 2, !"Debug Info Version", i32 3} +!16 = !{i32 1, !"wchar_size", i32 4} +!17 = !{i32 1, !"branch-target-enforcement", i32 0} +!18 = !{i32 1, !"sign-return-address", i32 0} +!19 = !{i32 1, !"sign-return-address-all", i32 0} +!20 = !{i32 1, !"sign-return-address-with-bkey", i32 0} +!21 = !{i32 7, !"uwtable", i32 1} +!22 = !{i32 7, !"frame-pointer", i32 1} +!23 = !{!"clang version 14.0.0 (https://github.com/llvm/llvm-project.git 3f96f7b30c91b912de1c6c7c03ab6a4c18e8aa26)"} +!24 = !DILocation(line: 5, column: 43, scope: !2) +!25 = !DILocation(line: 5, column: 35, scope: !2) +!26 = distinct !DISubprogram(name: "foo", linkageName: "_Z3foov", scope: !3, file: !3, line: 7, type: !4, scopeLine: 7, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !7, retainedNodes: !13) +!27 = !DILocation(line: 2, column: 39, scope: !12, inlinedAt: !28) +!28 = distinct !DILocation(line: 7, column: 20, scope: !26) +!29 = !DILocation(line: 5, column: 43, scope: !2, inlinedAt: !30) +!30 = distinct !DILocation(line: 7, column: 32, scope: !26) +!31 = !DILocation(line: 7, column: 30, scope: !26) +!32 = !DILocation(line: 7, column: 13, scope: !26) diff --git a/llvm/unittests/CodeGen/DIETest.cpp b/llvm/unittests/CodeGen/DIETest.cpp --- a/llvm/unittests/CodeGen/DIETest.cpp +++ b/llvm/unittests/CodeGen/DIETest.cpp @@ -18,6 +18,34 @@ namespace { +TEST(DIETest, TakeChildren) { + auto CountChildren = [](DIE *Node) { + return std::distance(Node->children().begin(), Node->children().end()); + }; + + BumpPtrAllocator Alloc; + DIE *ParentA = DIE::get(Alloc, dwarf::DW_TAG_subprogram); + DIE *ParentB = DIE::get(Alloc, dwarf::DW_TAG_subprogram); + DIE *ChildA = DIE::get(Alloc, dwarf::DW_TAG_variable); + DIE *ChildB = DIE::get(Alloc, dwarf::DW_TAG_variable); + DIE *ChildC = DIE::get(Alloc, dwarf::DW_TAG_variable); + ASSERT_FALSE(ParentA->hasChildren()); + ASSERT_FALSE(ParentB->hasChildren()); + + ParentA->addChild(ChildA); + ParentA->addChild(ChildB); + ParentB->addChild(ChildC); + ASSERT_EQ(2, CountChildren(ParentA)); + ASSERT_EQ(1, CountChildren(ParentB)); + + SmallVector Children; + ParentA->releaseChildren(Children); + for (DIE *Child : Children) + ParentB->addChild(Child); + ASSERT_FALSE(ParentA->hasChildren()); + ASSERT_EQ(3, CountChildren(ParentB)); +} + using DIETestParams = std::tuple;