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 @@ -45,6 +45,14 @@ enum class UnitKind { Skeleton, Full }; class DwarfCompileUnit final : public DwarfUnit { +public: + /// A pair of GlobalVariable and DIExpression. + struct GlobalExpr { + const GlobalVariable *Var; + const DIExpression *Expr; + }; + +private: /// A numeric ID unique among all CUs in the module unsigned UniqueID; bool HasRangeLists = false; @@ -83,6 +91,10 @@ DenseMap AbstractSPDies; DenseMap> AbstractEntities; + /// Static variables defined within the CU. + DenseMap> + DeferredStaticVariables; + /// DWO ID for correlating skeleton and split units. uint64_t DWOId = 0; @@ -128,12 +140,6 @@ /// Get line table start symbol for this unit. MCSymbol *getLineTableStartSym() const { return LineTableStartSym; } - /// A pair of GlobalVariable and DIExpression. - struct GlobalExpr { - const GlobalVariable *Var; - const DIExpression *Expr; - }; - struct BaseTypeRef { BaseTypeRef(unsigned BitSize, dwarf::TypeKind Encoding) : BitSize(BitSize), Encoding(Encoding) {} @@ -149,6 +155,14 @@ getOrCreateGlobalVariableDIE(const DIGlobalVariable *GV, ArrayRef GlobalExprs); + /// Defer the construction of a static variable DIE until its scope DIE has + /// been created. + void deferStaticVariable(const DIGlobalVariable *GV, + ArrayRef GlobalExprs) { + SmallVector GEs(GlobalExprs.begin(), GlobalExprs.end()); + DeferredStaticVariables.insert(std::make_pair(GV, GEs)); + } + DIE *getOrCreateCommonBlock(const DICommonBlock *CB, ArrayRef GlobalExprs); @@ -270,6 +284,7 @@ void finishSubprogramDefinition(const DISubprogram *SP); void finishEntityDefinition(const DbgEntity *Entity); + void finishDeferredStaticVariableDIEs(); /// 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 @@ -132,6 +132,12 @@ File->getSource(), CUID); } +void DwarfCompileUnit::finishDeferredStaticVariableDIEs() { + for (auto &Var : DeferredStaticVariables) + getOrCreateGlobalVariableDIE(Var.first, Var.second); + DeferredStaticVariables.clear(); +} + DIE *DwarfCompileUnit::getOrCreateGlobalVariableDIE( const DIGlobalVariable *GV, ArrayRef GlobalExprs) { // Check for pre-existence. @@ -145,9 +151,14 @@ // Construct the context before querying for the existence of the DIE in // case such construction creates the DIE. - auto *CB = GVContext ? dyn_cast(GVContext) : nullptr; - DIE *ContextDIE = CB ? getOrCreateCommonBlock(CB, GlobalExprs) - : getOrCreateContextDIE(GVContext); + DIE *ContextDIE = nullptr; + if (auto *CB = dyn_cast_or_null(GVContext)) + ContextDIE = getOrCreateCommonBlock(CB, GlobalExprs); + else if (auto *LS = dyn_cast_or_null(GVContext)) + ContextDIE = getAbstractSPDies().lookup(LS->getSubprogram()); + + if (!ContextDIE) + ContextDIE = getOrCreateContextDIE(GVContext); // Add to map. DIE *VariableDIE = &createAndAddDIE(GV->getTag(), *ContextDIE, GV); 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 @@ -1225,8 +1225,19 @@ DenseSet Processed; for (auto *GVE : CUNode->getGlobalVariables()) { DIGlobalVariable *GV = GVE->getVariable(); - if (Processed.insert(GV).second) - CU.getOrCreateGlobalVariableDIE(GV, sortGlobalExprs(GVMap[GV])); + if (Processed.insert(GV).second) { + auto &GEs = sortGlobalExprs(GVMap[GV]); + if (isa_and_nonnull(GV->getScope())) { + // If a DIGlobalVariable's scope is a DILocalScope, it is a + // function-local static global variable. At this point it is not + // possible to know whether the parent will be a separate abstract + // origin DIE or an out-of-line subprogram DIE. Defer creating these + // DIEs until all subprograms have been found and created. + CU.deferStaticVariable(GV, GEs); + continue; + } + CU.getOrCreateGlobalVariableDIE(GV, GEs); + } } for (auto *Ty : CUNode->getEnumTypes()) { @@ -1275,6 +1286,9 @@ finishSubprogramDefinitions(); + for (const auto &P : CUMap) + P.second->finishDeferredStaticVariableDIEs(); + finishEntityDefinitions(); // Include the DWO file name in the hash if there's more than one CU. 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,63 @@ +; RUN: %llc_dwarf -O0 -filetype=obj < %s | llvm-dwarfdump -debug-info - | FileCheck --implicit-check-not "{{DW_TAG|NULL}}" %s + +; inline __attribute__((always_inline)) +; int foo() { +; static int global; +; return global; +; } +; int bar() { return foo(); } + +; Ensure that global variables reference the correct subprograms even if +; those subprograms are inlined. + +; CHECK: DW_TAG_compile_unit +; CHECK: [[FOO:0x.*]]: DW_TAG_subprogram +; CHECK: DW_AT_name ("foo") +; CHECK: DW_AT_inline (DW_INL_inlined) +; CHECK: DW_TAG_variable +; CHECK: DW_AT_name ("global") +; CHECK: NULL +; CHECK: DW_TAG_base_type +; CHECK: DW_TAG_subprogram +; CHECK: DW_AT_name ("bar") +; CHECK: DW_TAG_inlined_subroutine +; CHECK: DW_AT_abstract_origin ([[FOO]] "_Z3foov") +; CHECK: NULL +; CHECK: NULL + +@_ZZ3foovE6global = linkonce_odr dso_local global i32 0, align 4, !dbg !0 + +define dso_local i32 @_Z3barv() !dbg !21 { + %1 = load i32, i32* @_ZZ3foovE6global, align 4, !dbg !22 + ret i32 %1, !dbg !24 +} + +!llvm.dbg.cu = !{!7} +!llvm.module.flags = !{!11, !12, !13, !14, !15, !16, !17, !18, !19} +!llvm.ident = !{!20} + +!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) +!1 = distinct !DIGlobalVariable(name: "global", scope: !2, file: !3, line: 3, type: !6, isLocal: false, isDefinition: true) +!2 = distinct !DISubprogram(name: "foo", linkageName: "_Z3foov", scope: !3, file: !3, line: 2, type: !4, scopeLine: 2, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !7, retainedNodes: !10) +!3 = !DIFile(filename: "static-var-inlined.cpp", directory: "") +!4 = !DISubroutineType(types: !5) +!5 = !{!6} +!6 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!7 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !8, producer: "clang version 14.0.0", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, globals: !9, splitDebugInlining: false, nameTableKind: None) +!8 = !DIFile(filename: "static-var-inlined.cpp", directory: "") +!9 = !{!0} +!10 = !{} +!11 = !{i32 7, !"Dwarf Version", i32 4} +!12 = !{i32 2, !"Debug Info Version", i32 3} +!13 = !{i32 1, !"wchar_size", i32 4} +!14 = !{i32 1, !"branch-target-enforcement", i32 0} +!15 = !{i32 1, !"sign-return-address", i32 0} +!16 = !{i32 1, !"sign-return-address-all", i32 0} +!17 = !{i32 1, !"sign-return-address-with-bkey", i32 0} +!18 = !{i32 7, !"uwtable", i32 1} +!19 = !{i32 7, !"frame-pointer", i32 1} +!20 = !{!"clang version 14.0.0"} +!21 = distinct !DISubprogram(name: "bar", linkageName: "_Z3barv", scope: !3, file: !3, line: 7, type: !4, scopeLine: 7, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !7, retainedNodes: !10) +!22 = !DILocation(line: 4, column: 10, scope: !2, inlinedAt: !23) +!23 = distinct !DILocation(line: 7, column: 20, scope: !21) +!24 = !DILocation(line: 7, column: 13, scope: !21) diff --git a/llvm/test/DebugInfo/X86/gnu-public-names.ll b/llvm/test/DebugInfo/X86/gnu-public-names.ll --- a/llvm/test/DebugInfo/X86/gnu-public-names.ll +++ b/llvm/test/DebugInfo/X86/gnu-public-names.ll @@ -137,19 +137,6 @@ ; CHECK-NOT: {{DW_TAG|NULL}} ; CHECK: DW_AT_name {{.*}} "global_namespace_function" -; CHECK: DW_TAG_subprogram -; CHECK-NOT: {{DW_TAG|NULL}} -; CHECK: DW_AT_name {{.*}} "f3" -; CHECK-NOT: {{DW_TAG|NULL}} -; CHECK: [[F3_Z:.*]]: DW_TAG_variable -; CHECK-NOT: DW_TAG -; CHECK: DW_AT_name {{.*}} "z" -; CHECK-NOT: {{DW_TAG|NULL}} -; CHECK: DW_AT_location -; CHECK-NOT: {{DW_TAG|NULL}} -; CHECK: NULL -; CHECK-NOT: {{DW_TAG|NULL}} - ; CHECK: [[ANON:.*]]: DW_TAG_namespace ; CHECK-NOT: DW_AT_name ; CHECK: [[ANON_I:.*]]: DW_TAG_variable @@ -238,6 +225,18 @@ ; CHECK-NOT: {{DW_TAG|NULL}} ; CHECK: DW_AT_name {{.*}} "global_function" +; CHECK: DW_TAG_subprogram +; CHECK-NOT: {{DW_TAG|NULL}} +; CHECK: DW_AT_name {{.*}} "f3" +; CHECK-NOT: {{DW_TAG|NULL}} +; CHECK: [[F3_Z:.*]]: DW_TAG_variable +; CHECK-NOT: DW_TAG +; CHECK: DW_AT_name {{.*}} "z" +; CHECK-NOT: {{DW_TAG|NULL}} +; CHECK: DW_AT_location +; CHECK-NOT: {{DW_TAG|NULL}} +; CHECK: NULL + ; CHECK-LABEL: .debug_gnu_pubnames contents: ; CHECK-NEXT: length = {{.*}}, version = 0x0002, unit_offset = 0x00000000, unit_size = {{.*}} ; CHECK-NEXT: Offset Linkage Kind Name @@ -250,8 +249,8 @@ ; comes out naturally from LLVM's implementation, so I'm OK with it for now. If ; it's demonstrated that this is a major size concern or degrades debug info ; consumer behavior, feel free to change it. -; CHECK-NEXT: [[F3_Z]] STATIC VARIABLE "f3::z" ; CHECK-NEXT: [[ANON]] EXTERNAL TYPE "(anonymous namespace)" +; CHECK-NEXT: [[F3_Z]] STATIC VARIABLE "f3::z" ; CHECK-NEXT: [[OUTER_ANON]] EXTERNAL TYPE "outer::(anonymous namespace)" ; CHECK-NEXT: [[ANON_INNER_B]] STATIC VARIABLE "(anonymous namespace)::inner::b" ; CHECK-NEXT: [[OUTER]] EXTERNAL TYPE "outer" diff --git a/llvm/test/DebugInfo/X86/namelist1.ll b/llvm/test/DebugInfo/X86/namelist1.ll --- a/llvm/test/DebugInfo/X86/namelist1.ll +++ b/llvm/test/DebugInfo/X86/namelist1.ll @@ -1,13 +1,14 @@ ; Namelist is a fortran feature, this test checks whether DW_TAG_namelist and ; DW_TAG_namelist_item attributes are emitted correctly. ; -; RUN: llc -O0 -mtriple=x86_64-unknown-linux-gnu %s -filetype=obj -o %t.o -; RUN: llvm-dwarfdump %t.o | FileCheck %s +; RUN: llc -O0 -mtriple=x86_64-unknown-linux-gnu %s -filetype=obj -o - | llvm-dwarfdump - | FileCheck %s ; -; CHECK: [[ITEM1:0x.+]]: DW_TAG_variable -; CHECK: DW_AT_name ("a") ; CHECK: [[ITEM2:0x.+]]: DW_TAG_variable ; CHECK: DW_AT_name ("b") +; CHECK: DW_TAG_variable +; CHECK: DW_AT_type ({{0x.+}} "nml") +; CHECK: [[ITEM1:0x.+]]: DW_TAG_variable +; CHECK: DW_AT_name ("a") ; CHECK: DW_TAG_namelist ; CHECK: DW_AT_name ("nml") ; CHECK: DW_TAG_namelist_item