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,14 @@ DenseMap AbstractSPDies; DenseMap> AbstractEntities; + /// A static variable that does not yet have a parent DIE. + struct UnfinishedStaticVariable { + const DIGlobalVariable *GV; + DIE *Die; + ArrayRef Exprs; + }; + SmallVector UnfinishedStaticVariables; + /// DWO ID for correlating skeleton and split units. uint64_t DWOId = 0; @@ -128,12 +144,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) {} @@ -144,6 +154,9 @@ std::vector ExprRefedBaseTypes; + /// Get or create the parent DIE of the global variable. + DIE *getOrCreateGlobalVariableParentDIE(const DIGlobalVariable *GV, + ArrayRef GlobalExprs); /// Get or create global variable DIE. DIE * getOrCreateGlobalVariableDIE(const DIGlobalVariable *GV, @@ -270,6 +283,8 @@ void finishSubprogramDefinition(const DISubprogram *SP); void finishEntityDefinition(const DbgEntity *Entity); + /// Move static variable DIEs to the appropriate parent DIE. + void finishStaticVariableDIEs(); /// 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,22 @@ File->getSource(), CUID); } +DIE *DwarfCompileUnit::getOrCreateGlobalVariableParentDIE( + const DIGlobalVariable *GV, ArrayRef GlobalExprs) { + auto *Scope = GV->getScope(); + if (auto *ParentDie = getAbstractSPDies()[Scope]) + return ParentDie; + else if (auto *CB = Scope ? dyn_cast(Scope) : nullptr) + return getOrCreateCommonBlock(CB, GlobalExprs); + else + return getOrCreateContextDIE(Scope); +} + +void DwarfCompileUnit::finishStaticVariableDIEs() { + for (auto &Var : UnfinishedStaticVariables) + getOrCreateGlobalVariableParentDIE(Var.GV, Var.Exprs)->addChild(Var.Die); +} + DIE *DwarfCompileUnit::getOrCreateGlobalVariableDIE( const DIGlobalVariable *GV, ArrayRef GlobalExprs) { // Check for pre-existence. @@ -139,18 +155,18 @@ return Die; assert(GV); - - auto *GVContext = GV->getScope(); const DIType *GTy = GV->getType(); - // 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 *VariableDIE = DIE::get(DIEValueAllocator, GV->getTag()); + if (GV->isLocalToUnit()) { + // The scope of a static variable can be a subprogram, but DIEs of inlined + // subprograms do not exist yet. + UnfinishedStaticVariables.push_back({GV, VariableDIE, GlobalExprs}); + } else { + getOrCreateGlobalVariableParentDIE(GV, GlobalExprs)->addChild(VariableDIE); + } + insertDIE(GV, VariableDIE); - // Add to map. - DIE *VariableDIE = &createAndAddDIE(GV->getTag(), *ContextDIE, GV); DIScope *DeclContext; if (auto *SDMDecl = GV->getStaticDataMemberDeclaration()) { DeclContext = SDMDecl->getScope(); @@ -475,7 +491,7 @@ // FIXME: when writing dwo, we need to avoid relocations. Probably // the "right" solution is to treat globals the way func and data // symbols are (with entries in .debug_addr). - // For now, since we only ever use index 0, this should work as-is. + // For now, since we only ever use index 0, this should work as-is. addUInt(*Loc, dwarf::DW_FORM_data4, FrameBase.Location.WasmLoc.Index); } addUInt(*Loc, dwarf::DW_FORM_data1, dwarf::DW_OP_stack_value); @@ -1010,6 +1026,8 @@ for (DbgVariable *DV : Locals) Children.push_back(constructVariableDIE(*DV, *Scope, ObjectPointer)); + // TODO: Emit static global variables. + // Skip imported directives in gmlt-like data. if (!includeMinimalInlineScopes()) { // There is no need to emit empty lexical block DIE. 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.finishStaticVariableDIEs(); // Emit DW_AT_containing_type attribute to connect types with their // vtable holding type. TheCU.constructContainingTypeDIEs(); diff --git a/llvm/test/CodeGen/ARM/2011-01-19-MergedGlobalDbg.ll b/llvm/test/CodeGen/ARM/2011-01-19-MergedGlobalDbg.ll --- a/llvm/test/CodeGen/ARM/2011-01-19-MergedGlobalDbg.ll +++ b/llvm/test/CodeGen/ARM/2011-01-19-MergedGlobalDbg.ll @@ -11,7 +11,6 @@ @x5 = global i8 1, align 1, !dbg !10 ; CHECK: DW_TAG_variable -; CHECK-NOT: DW_TAG ; CHECK: DW_AT_name {{.*}} "x1" ; CHECK-NOT: {{DW_TAG|NULL}} ; CHECK: DW_AT_location [DW_FORM_exprloc] (DW_OP_addr [[ADDR:0x[0-9a-fA-F]+]]) @@ -137,4 +136,3 @@ !51 = distinct !DILexicalBlock(scope: !47, file: !2, line: 16) !52 = !DILocation(line: 16, scope: !47) !53 = !DILocation(line: 16, scope: !51) - diff --git a/llvm/test/CodeGen/ARM/2011-08-02-MergedGlobalDbg.ll b/llvm/test/CodeGen/ARM/2011-08-02-MergedGlobalDbg.ll --- a/llvm/test/CodeGen/ARM/2011-08-02-MergedGlobalDbg.ll +++ b/llvm/test/CodeGen/ARM/2011-08-02-MergedGlobalDbg.ll @@ -1,7 +1,6 @@ ; RUN: llc -arm-global-merge -global-merge-group-by-use=false -filetype=obj < %s | llvm-dwarfdump -debug-info -v - | FileCheck %s ; CHECK: DW_TAG_variable -; CHECK-NOT: DW_TAG ; CHECK: DW_AT_name {{.*}} "x1" ; CHECK-NOT: {{DW_TAG|NULL}} ; CHECK: DW_AT_location [DW_FORM_exprloc] (DW_OP_addr [[ADDR:0x[0-9a-fA-F]+]]) @@ -128,4 +127,3 @@ !45 = distinct !DILexicalBlock(scope: !41, file: !3, line: 17, column: 19) !46 = !DILocation(line: 17, column: 16, scope: !41) !47 = !DILocation(line: 17, column: 32, scope: !45) - diff --git a/llvm/test/DebugInfo/Generic/static-var-inlined.ll b/llvm/test/DebugInfo/Generic/static-var-inlined.ll new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/Generic/static-var-inlined.ll @@ -0,0 +1,62 @@ +; RUN: %llc_dwarf -O0 -filetype=obj < %s | llvm-dwarfdump -v -debug-info - | FileCheck --implicit-check-not "{{DW_TAG|NULL}}" %s + +; CHECK: DW_TAG_compile_unit +; CHECK: DW_TAG_base_type +; CHECK: DW_TAG_imported_declaration +; CHECK: DW_TAG_subprogram +; CHECK: DW_AT_name {{.*}} "foo" +; CHECK: DW_AT_inline [DW_FORM_data1] (DW_INL_inlined) +; CHECK: DW_TAG_variable +; CHECK: DW_AT_name {{.*}} "local_var" +; CHECK: DW_TAG_variable +; CHECK: DW_AT_name {{.*}} "static_var" +; CHECK: DW_TAG_variable +; CHECK: DW_AT_name {{.*}} "imported_static_var" +; CHECK: NULL +; CHECK: DW_TAG_subprogram +; CHECK: DW_AT_name {{.*}} "main" +; CHECK: DW_TAG_inlined_subroutine +; CHECK: DW_AT_abstract_origin {{.*}} "foo" +; CHECK: DW_TAG_variable +; CHECK: DW_AT_abstract_origin {{.*}} "local_var" +; FIXME: The static variable should also be found in the subroutine. +; FIXME: DW_TAG_variable +; FIXME: DW_AT_abstract_origin {{.*}} "static_var" +; CHECK: NULL +; CHECK: NULL +; CHECK: NULL + +@static_var = internal global i32 4, align 4, !dbg !0 + +define i32 @main() !dbg !7 { + store i32 0, i32* @static_var, align 4, !dbg !15 + %local_var = alloca i32, align 4 + call void @llvm.dbg.declare(metadata i32* %local_var, metadata !2, metadata !DIExpression()), !dbg !15 + store i32 0, i32* %local_var, align 4, !dbg !15 + ret i32 0 +} + +declare void @llvm.dbg.declare(metadata, metadata, metadata) + +!llvm.dbg.cu = !{!4} +!llvm.module.flags = !{!11, !12, !13} + +!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) +!1 = !DIGlobalVariable(name: "static_var", scope: !6, file: !5, line: 3, type: !10, isLocal: true, isDefinition: true) +!2 = !DILocalVariable(name: "local_var", scope: !6, file: !5, line: 4, type: !10) +!3 = !DISubroutineType(types: !8) +!4 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !5, emissionKind: FullDebug, globals: !9, splitDebugInlining: false, imports: !17) +!5 = !DIFile(filename: "test.cpp", directory: "/") +!6 = distinct !DISubprogram(name: "foo", scope: !5, file: !5, line: 2, type: !3, scopeLine: 2, unit: !4) +!7 = distinct !DISubprogram(name: "main", scope: !5, file: !5, line: 7, type: !3, scopeLine: 7, unit: !4) +!8 = !{} +!9 = !{!0} +!10 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!11 = !{i32 7, !"Dwarf Version", i32 4} +!12 = !{i32 2, !"Debug Info Version", i32 3} +!13 = !{i32 1, !"wchar_size", i32 4} +!15 = !DILocation(line: 4, column: 7, scope: !6, inlinedAt: !16) +!16 = !DILocation(line: 9, column: 3, scope: !7) +!17 = !{!18} +!18 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !4, entity: !19, line: 122) +!19 = !DIGlobalVariable(name: "imported_static_var", scope: !6, file: !5, line: 3, type: !10, isLocal: true, isDefinition: true) diff --git a/llvm/test/DebugInfo/PowerPC/strict-dwarf.ll b/llvm/test/DebugInfo/PowerPC/strict-dwarf.ll --- a/llvm/test/DebugInfo/PowerPC/strict-dwarf.ll +++ b/llvm/test/DebugInfo/PowerPC/strict-dwarf.ll @@ -15,17 +15,20 @@ ; 3: DwarfUnit::addUInt() ; 4: addUInt(Block, (dwarf::Attribute)0, Form, Integer); +; CHECK: DW_AT_name ("f") +; CHECK-NOT: DW_TAG_ +; CHECK: DW_AT_noreturn ; CHECK: DW_AT_name ("var") ; CHECK-NOT: DW_TAG_ ; CHECK: DW_AT_alignment ; CHECK: DW_AT_location (DW_OP_addr 0x0) -; CHECK: DW_AT_noreturn ; +; STRICT: DW_AT_name ("f") +; STRICT-NOT: DW_AT_noreturn ; STRICT: DW_AT_name ("var") ; STRICT-NOT: DW_AT_alignment ; STRICT-NOT: DW_TAG_ ; STRICT: DW_AT_location (DW_OP_addr 0x0) -; STRICT-NOT: DW_AT_noreturn @_ZL3var = internal global i32 0, align 16, !dbg !0 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,54 +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 -; CHECK-NOT: {{DW_TAG|NULL}} -; CHECK: DW_AT_name {{.*}} "i" -; CHECK-NOT: {{DW_TAG|NULL}} -; CHECK: [[ANON_INNER:.*]]: DW_TAG_namespace -; CHECK-NOT: DW_TAG -; CHECK: DW_AT_name {{.*}} "inner" -; CHECK-NOT: {{DW_TAG|NULL}} -; CHECK: [[ANON_INNER_B:.*]]: DW_TAG_variable -; CHECK-NOT: DW_TAG -; CHECK: DW_AT_name {{.*}} "b" -; CHECK-NOT: {{DW_TAG|NULL}} -; CHECK: NULL -; CHECK-NOT: {{DW_TAG|NULL}} -; CHECK: NULL -; CHECK-NOT: {{DW_TAG|NULL}} - -; CHECK: [[OUTER:.*]]: DW_TAG_namespace -; CHECK-NOT: {{DW_TAG|NULL}} -; CHECK: DW_AT_name {{.*}} "outer" -; CHECK-NOT: {{DW_TAG|NULL}} -; CHECK: [[OUTER_ANON:.*]]: DW_TAG_namespace -; CHECK-NOT: {{DW_TAG|NULL}} -; CHECK-NOT: DW_AT_name -; CHECK: [[OUTER_ANON_C:.*]]: DW_TAG_variable -; CHECK-NOT: DW_TAG -; CHECK: DW_AT_name {{.*}} "c" -; CHECK-NOT: {{DW_TAG|NULL}} -; CHECK: NULL -; CHECK-NOT: {{DW_TAG|NULL}} -; CHECK: NULL -; CHECK-NOT: {{DW_TAG|NULL}} - ; CHECK: DW_TAG_enumeration ; CHECK-NOT: {{DW_AT_name|DW_TAG|NULL}} ; CHECK: [[UNNAMED_ENUM_ENUMERATOR:0x[0-9a-f]+]]: DW_TAG_enumerator @@ -238,6 +190,52 @@ ; 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: [[ANON:.*]]: DW_TAG_namespace +; CHECK-NOT: DW_AT_name +; CHECK: [[ANON_I:.*]]: DW_TAG_variable +; CHECK-NOT: {{DW_TAG|NULL}} +; CHECK: DW_AT_name {{.*}} "i" +; CHECK-NOT: {{DW_TAG|NULL}} +; CHECK: [[ANON_INNER:.*]]: DW_TAG_namespace +; CHECK-NOT: DW_TAG +; CHECK: DW_AT_name {{.*}} "inner" +; CHECK-NOT: {{DW_TAG|NULL}} +; CHECK: [[ANON_INNER_B:.*]]: DW_TAG_variable +; CHECK-NOT: DW_TAG +; CHECK: DW_AT_name {{.*}} "b" +; CHECK-NOT: {{DW_TAG|NULL}} +; CHECK: NULL +; CHECK-NOT: {{DW_TAG|NULL}} +; CHECK: NULL +; CHECK-NOT: {{DW_TAG|NULL}} + +; CHECK: [[OUTER:.*]]: DW_TAG_namespace +; CHECK-NOT: {{DW_TAG|NULL}} +; CHECK: DW_AT_name {{.*}} "outer" +; CHECK-NOT: {{DW_TAG|NULL}} +; CHECK: [[OUTER_ANON:.*]]: DW_TAG_namespace +; CHECK-NOT: {{DW_TAG|NULL}} +; CHECK-NOT: DW_AT_name +; CHECK: [[OUTER_ANON_C:.*]]: DW_TAG_variable +; CHECK-NOT: DW_TAG +; CHECK: DW_AT_name {{.*}} "c" +; CHECK-NOT: {{DW_TAG|NULL}} +; CHECK: NULL +; 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 @@ -245,7 +243,7 @@ ; CHECK-NEXT: [[NS]] EXTERNAL TYPE "ns" ; CHECK-NEXT: [[OUTER_ANON_C]] STATIC VARIABLE "outer::(anonymous namespace)::c" ; CHECK-NEXT: [[ANON_I]] STATIC VARIABLE "(anonymous namespace)::i" -; CHECK-NEXT: [[GLOB_NS_FUNC]] EXTERNAL FUNCTION "ns::global_namespace_function" +; CHECK-NEXT: [[ANON_INNER]] EXTERNAL TYPE "(anonymous namespace)::inner" ; GCC Doesn't put local statics in pubnames, but it seems not unreasonable and ; 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 @@ -254,13 +252,13 @@ ; CHECK-NEXT: [[ANON]] EXTERNAL TYPE "(anonymous namespace)" ; 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" ; FIXME: GCC produces enumerators as EXTERNAL, not STATIC ; CHECK-NEXT: [[NAMED_ENUM_CLASS_ENUMERATOR]] STATIC VARIABLE "named_enum_class_enumerator" +; CHECK-NEXT: [[OUTER]] EXTERNAL TYPE "outer" ; CHECK-NEXT: [[MEM_FUNC]] EXTERNAL FUNCTION "C::member_function" ; CHECK-NEXT: [[GLOB_VAR]] EXTERNAL VARIABLE "global_variable" ; CHECK-NEXT: [[GLOB_NS_VAR]] EXTERNAL VARIABLE "ns::global_namespace_variable" -; CHECK-NEXT: [[ANON_INNER]] EXTERNAL TYPE "(anonymous namespace)::inner" +; CHECK-NEXT: [[GLOB_NS_FUNC]] EXTERNAL FUNCTION "ns::global_namespace_function" ; CHECK-NEXT: [[D_VAR]] EXTERNAL VARIABLE "ns::d" ; CHECK-NEXT: [[NAMED_ENUM_ENUMERATOR]] STATIC VARIABLE "named_enum_enumerator" ; CHECK-NEXT: [[STATIC_MEM_VAR]] EXTERNAL VARIABLE "C::static_member_variable"