Index: lib/CodeGen/AsmPrinter/DwarfCompileUnit.h =================================================================== --- lib/CodeGen/AsmPrinter/DwarfCompileUnit.h +++ lib/CodeGen/AsmPrinter/DwarfCompileUnit.h @@ -157,6 +157,8 @@ void finishSubprogramDefinition(const DISubprogram *SP); + void finishLexicalBlockDefinitions(); + void collectDeadVariables(const DISubprogram *SP); /// Set the skeleton unit associated with this unit. Index: lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp =================================================================== --- lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp +++ lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp @@ -114,9 +114,14 @@ // Construct the context before querying for the existence of the DIE in // case such construction creates the DIE. DIE *ContextDIE = getOrCreateContextDIE(GVContext); + bool IsLBScope = GVContext && isa(GVContext); + assert(ContextDIE || IsLBScope); + + // Create new global variable and add to map. + DIE *VariableDIE = IsLBScope + ? createDIE(GV->getTag(), GV) + : &createAndAddDIE(GV->getTag(), *ContextDIE, GV); - // Add to map. - DIE *VariableDIE = &createAndAddDIE(GV->getTag(), *ContextDIE, GV); DIScope *DeclContext; if (auto *SDMDecl = GV->getStaticDataMemberDeclaration()) { DeclContext = resolve(SDMDecl->getScope()); @@ -335,17 +340,29 @@ // null and the children will be added to the scope DIE. createScopeChildrenDIE(Scope, Children, &ChildScopeCount); - // Skip imported directives in gmlt-like data. + bool HasLocalDclDie = false; + // Skip local decls in gmlt-like data. if (!includeMinimalInlineScopes()) { - // There is no need to emit empty lexical block DIE. - for (const auto &E : DD->findImportedEntitiesForScope(DS)) - Children.push_back( - constructImportedEntityDIE(cast(E.second))); + for (const auto &DI : DD->findLocalDeclNodesForScope(DS)) { + if (auto *IE = dyn_cast(DI.second)) + Children.push_back(constructImportedEntityDIE(IE)); + else { + DIE *D = nullptr; + if (auto *GV = dyn_cast(DI.second)) + D = getOrCreateGlobalVariableDIE(GV); + else if (auto *RT = dyn_cast(DI.second)) + D = getOrCreateTypeDIE(RT); + else + assert(false && "Unexpected DI node."); + DU->addLocalDieToLBScope(Scope, D); + HasLocalDclDie = true; + } + } } // If there are only other scopes as children, put them directly in the // parent instead, as this scope would serve no purpose. - if (Children.size() == ChildScopeCount) { + if (!HasLocalDclDie && Children.size() == ChildScopeCount) { FinalChildren.insert(FinalChildren.end(), std::make_move_iterator(Children.begin()), std::make_move_iterator(Children.end())); @@ -353,6 +370,7 @@ } ScopeDIE = constructLexicalScopeDIE(Scope); assert(ScopeDIE && "Scope DIE should not be null."); + DU->addLBDieToLBScope(Scope, ScopeDIE); } // Add children @@ -676,6 +694,18 @@ applySubprogramAttributesToDefinition(SP, *D); } } + +void DwarfCompileUnit::finishLexicalBlockDefinitions() { + for (const auto &I : DU->getLBDieInfoMap()) { + auto LBInfo = I.second; + DIE *LBDie = (LBInfo.AbstractLBDie) ? (LBInfo.AbstractLBDie) + : (LBInfo.ConcreteLBDie); + assert(LBDie || LBInfo.LocalDclDies.empty()); + for (auto &D : LBInfo.LocalDclDies) + LBDie->addChild(std::move(D)); + } +} + void DwarfCompileUnit::collectDeadVariables(const DISubprogram *SP) { assert(SP && "CU's subprogram list contains a non-subprogram"); assert(SP->isDefinition() && Index: lib/CodeGen/AsmPrinter/DwarfDebug.h =================================================================== --- lib/CodeGen/AsmPrinter/DwarfDebug.h +++ lib/CodeGen/AsmPrinter/DwarfDebug.h @@ -255,6 +255,10 @@ LexicalScopes LScopes; + /// Holder for scopes containing local declaration DI nodes per DI function. + DenseMap> + LocalScopesMap; + /// Collection of abstract variables. DenseMap> AbstractVariables; SmallVector, 64> ConcreteVariables; @@ -303,13 +307,10 @@ /// Holder for the file specific debug information. DwarfFile InfoHolder; - /// Holders for the various debug information flags that we might need to - /// have exposed. See accessor functions below for description. - - /// Holder for imported entities. + /// Holder for local declaration DI nodes per scope. typedef SmallVector, 32> - ImportedEntityMap; - ImportedEntityMap ScopesWithImportedEntities; + LocalDeclMap; + LocalDeclMap ScopesWithLocalDeclNodes; /// Map from MDNodes for user-defined types to the type units that /// describe them. @@ -411,6 +412,8 @@ void finishSubprogramDefinitions(); + void finishLexicalBlockDefinitions(); + /// Finish off debug information after all functions have been /// processed. void finalizeModuleInfo(); @@ -510,6 +513,10 @@ void constructAndAddImportedEntityDIE(DwarfCompileUnit &TheCU, const DIImportedEntity *N); + /// Collect MD Node with lexical scope node. + /// Return true if node was collected, false otherwise. + bool collectLexicalScopedNode(DIScope *S, const MDNode * N); + /// Register a source line with debug info. Returns the unique /// label that was emitted and which provides correspondence to the /// source line list. @@ -660,10 +667,11 @@ const MachineFunction *getCurrentFunction() const { return CurFn; } - iterator_range - findImportedEntitiesForScope(const MDNode *Scope) const { + iterator_range + findLocalDeclNodesForScope(const MDNode *Scope) const { + assert(DILexicalBlockBase::classof(Scope) && "Expected LexicalBlock scope"); return make_range(std::equal_range( - ScopesWithImportedEntities.begin(), ScopesWithImportedEntities.end(), + ScopesWithLocalDeclNodes.begin(), ScopesWithLocalDeclNodes.end(), std::pair(Scope, nullptr), less_first())); } @@ -683,6 +691,12 @@ SmallPtrSet &getProcessedSPNodes() { return ProcessedSPNodes; } + + /// Return collection of function scopes that contains local declararion DIEs. + SmallPtrSet & + getLocalScopes(const DISubprogram *SP) { + return LocalScopesMap[SP]; + } }; } // End of namespace llvm Index: lib/CodeGen/AsmPrinter/DwarfDebug.cpp =================================================================== --- lib/CodeGen/AsmPrinter/DwarfDebug.cpp +++ lib/CodeGen/AsmPrinter/DwarfDebug.cpp @@ -453,6 +453,24 @@ D->addChild(TheCU.constructImportedEntityDIE(N)); } +static const DISubprogram *getSubprogramScope(const DIScope *S) { + assert(S && "Expected a valid scope."); + if (const DISubprogram *SP = dyn_cast(S)) + return SP; + if (const DILexicalBlockBase *LB = dyn_cast(S)) + return getSubprogramScope(LB->getScope()); + assert(false && "Unexpected scope."); +} + +bool DwarfDebug::collectLexicalScopedNode(DIScope *S, const MDNode * N) { + auto *LB = dyn_cast_or_null(S); + if (!LB) + return false; + getLocalScopes(getSubprogramScope(S)).insert(LB); + ScopesWithLocalDeclNodes.push_back(std::make_pair(S, N)); + return true; +} + // Emit all Dwarf sections that should come prior to the content. Create // global DIEs and emit initial debug info sections. This is invoked by // the target AsmPrinter. @@ -475,14 +493,11 @@ auto *CUNode = cast(N); DwarfCompileUnit &CU = constructDwarfCompileUnit(CUNode); for (auto *IE : CUNode->getImportedEntities()) - ScopesWithImportedEntities.push_back(std::make_pair(IE->getScope(), IE)); - // Stable sort to preserve the order of appearance of imported entities. - // This is to avoid out-of-order processing of interdependent declarations - // within the same scope, e.g. { namespace A = base; namespace B = A; } - std::stable_sort(ScopesWithImportedEntities.begin(), - ScopesWithImportedEntities.end(), less_first()); - for (auto *GV : CUNode->getGlobalVariables()) - CU.getOrCreateGlobalVariableDIE(GV); + ScopesWithLocalDeclNodes.push_back(std::make_pair(IE->getScope(), IE)); + for (auto *GV : CUNode->getGlobalVariables()) { + if (!collectLexicalScopedNode(GV->getScope(), GV)) + CU.getOrCreateGlobalVariableDIE(GV); + } for (auto *SP : CUNode->getSubprograms()) SPMap.insert(std::make_pair(SP, &CU)); for (auto *Ty : CUNode->getEnumTypes()) { @@ -494,14 +509,22 @@ // The retained types array by design contains pointers to // MDNodes rather than DIRefs. Unique them here. DIType *RT = cast(resolve(Ty->getRef())); - if (!RT->isExternalTypeRef()) + if (RT->isExternalTypeRef()) // There is no point in force-emitting a forward declaration. + continue; + if (!collectLexicalScopedNode(resolve(Ty->getScope()), RT)) CU.getOrCreateTypeDIE(RT); } // Emit imported_modules last so that the relevant context is already // available. for (auto *IE : CUNode->getImportedEntities()) constructAndAddImportedEntityDIE(CU, IE); + + // Stable sort to preserve the order of appearance of imported entities. + // This is to avoid out-of-order processing of interdependent declarations + // within the same scope, e.g. { namespace A = base; namespace B = A; } + std::stable_sort(ScopesWithLocalDeclNodes.begin(), + ScopesWithLocalDeclNodes.end(), less_first()); } // Tell MMI that we have debug info. @@ -534,6 +557,12 @@ }); } +void DwarfDebug::finishLexicalBlockDefinitions() { + for (const auto &I : CUMap) + forBothCUs(*I.second, [&](DwarfCompileUnit &CU) { + CU.finishLexicalBlockDefinitions(); + }); +} // Collect info for variables that were optimized out. void DwarfDebug::collectDeadVariables() { @@ -560,6 +589,8 @@ finishSubprogramDefinitions(); + finishLexicalBlockDefinitions(); + finishVariableDefinitions(); // Collect info for variables that were optimized out. @@ -1253,6 +1284,10 @@ assert(LScopes.getAbstractScopesList().size() == NumAbstractScopes && "ensureAbstractVariableIsCreated inserted abstract scopes"); } + // Assure abstract lexical scope created for each one contains local DIEs. + for (const DILocalScope *LS : getLocalScopes(SP)) { + LScopes.getOrCreateAbstractScope(cast(LS)); + } constructAbstractSubprogramScopeDIE(AScope); } Index: lib/CodeGen/AsmPrinter/DwarfFile.h =================================================================== --- lib/CodeGen/AsmPrinter/DwarfFile.h +++ lib/CodeGen/AsmPrinter/DwarfFile.h @@ -14,6 +14,7 @@ #include "DwarfStringPool.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/FoldingSet.h" +#include "llvm/ADT/SetVector.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringMap.h" #include "llvm/Support/Allocator.h" @@ -56,6 +57,17 @@ // Collection of abstract subprogram DIEs. DenseMap AbstractSPDies; + struct LBDieInfo { + DIE *ConcreteLBDie; + DIE *AbstractLBDie; + SetVector InlineLBDies; + SetVector LocalDclDies; + + LBDieInfo() : ConcreteLBDie(nullptr), AbstractLBDie(nullptr) {} + }; + // Collection of lexical scope DIE info. + DenseMap LBDieInfoMap; + /// Maps MDNodes for type system with the corresponding DIEs. These DIEs can /// be shared across CUs, that is why we keep the map here instead /// of in DwarfCompileUnit. @@ -107,6 +119,16 @@ return AbstractSPDies; } + DenseMap &getLBDieInfoMap() { + return LBDieInfoMap; + } + + /// Add lexical block DIE entry to lexical scope info. + void addLBDieToLBScope(LexicalScope *LS, DIE *D); + /// Add DIE entry to lexical-block info. + /// Add local declaration DIE entry to lexical scope info. + void addLocalDieToLBScope(LexicalScope *LS, DIE *D); + void insertDIE(const MDNode *TypeMD, DIE *Die) { DITypeNodeToDieMap.insert(std::make_pair(TypeMD, Die)); } Index: lib/CodeGen/AsmPrinter/DwarfFile.cpp =================================================================== --- lib/CodeGen/AsmPrinter/DwarfFile.cpp +++ lib/CodeGen/AsmPrinter/DwarfFile.cpp @@ -170,4 +170,28 @@ Vars.push_back(Var); return true; } + +void DwarfFile::addLBDieToLBScope(LexicalScope *LS, DIE *D) { + auto &LBInfo = getLBDieInfoMap()[LS->getScopeNode()]; + if (LS->isAbstractScope()) { + assert(!LBInfo.AbstractLBDie && "Adding abstract LB DIE twice."); + LBInfo.AbstractLBDie = D; + return; + } + if (LS->getInlinedAt()) { + assert(!LBInfo.InlineLBDies.count(D) && "Adding inline LB DIE twice."); + LBInfo.InlineLBDies.insert(D); + return; + } + + assert(!LBInfo.ConcreteLBDie && "Adding cocncrete LB DIE twice."); + LBInfo.ConcreteLBDie = D; + return; +} + +void DwarfFile::addLocalDieToLBScope(LexicalScope *LS, DIE *D) { + auto &LBInfo = getLBDieInfoMap()[LS->getScopeNode()]; + LBInfo.LocalDclDies.insert(D); + return; +} } Index: lib/CodeGen/AsmPrinter/DwarfUnit.h =================================================================== --- lib/CodeGen/AsmPrinter/DwarfUnit.h +++ lib/CodeGen/AsmPrinter/DwarfUnit.h @@ -316,6 +316,9 @@ /// Construct function argument DIEs. void constructSubprogramArguments(DIE &Buffer, DITypeRefArray Args); + /// Create a DIE with the given Tag, and call insertDIE if MD is not null. + DIE *createDIE(unsigned Tag, const DINode *N = nullptr); + /// Create a DIE with the given Tag, add the DIE to its parent, and /// call insertDIE if MD is not null. DIE &createAndAddDIE(unsigned Tag, DIE &Parent, const DINode *N = nullptr); Index: lib/CodeGen/AsmPrinter/DwarfUnit.cpp =================================================================== --- lib/CodeGen/AsmPrinter/DwarfUnit.cpp +++ lib/CodeGen/AsmPrinter/DwarfUnit.cpp @@ -298,12 +298,17 @@ Entry); } -DIE &DwarfUnit::createAndAddDIE(unsigned Tag, DIE &Parent, const DINode *N) { +DIE *DwarfUnit::createDIE(unsigned Tag, const DINode *N) { assert(Tag != dwarf::DW_TAG_auto_variable && Tag != dwarf::DW_TAG_arg_variable); - DIE &Die = Parent.addChild(DIE::get(DIEValueAllocator, (dwarf::Tag)Tag)); + DIE *Die = DIE::get(DIEValueAllocator, (dwarf::Tag)Tag); if (N) - insertDIE(N, &Die); + insertDIE(N, Die); + return Die; +} + +DIE &DwarfUnit::createAndAddDIE(unsigned Tag, DIE &Parent, const DINode *N) { + DIE &Die = Parent.addChild(createDIE(Tag, N)); return Die; } @@ -728,13 +733,15 @@ // such construction creates the DIE. auto *Context = resolve(Ty->getScope()); DIE *ContextDIE = getOrCreateContextDIE(Context); - assert(ContextDIE); + bool IsLBScope = Context && isa(Context); + assert(ContextDIE || IsLBScope); if (DIE *TyDIE = getDIE(Ty)) return TyDIE; - // Create new type. - DIE &TyDIE = createAndAddDIE(Ty->getTag(), *ContextDIE, Ty); + // Create new type and add to map. + DIE &TyDIE = IsLBScope ? *createDIE(Ty->getTag(), Ty) + : createAndAddDIE(Ty->getTag(), *ContextDIE, Ty); updateAcceleratorTables(Context, Ty, TyDIE); Index: test/DebugInfo/X86/PR24008.ll =================================================================== --- test/DebugInfo/X86/PR24008.ll +++ test/DebugInfo/X86/PR24008.ll @@ -0,0 +1,67 @@ +; RUN: llc -mtriple=x86_64-unknown-linux-gnu -filetype=obj -O0 < %s | llvm-dwarfdump -debug-dump=info - | FileCheck %s + +;; This test checks the following: +;; 1. Compilation does not crash +;; 2. "f1" function is defined. +;; 3. "nested::f2" function is not defined. +;; +;; This test was generated by running following command: +;; clang -cc1 -O0 -g -emit-llvm foo.cpp -o - | opt -S -inline -sroa +;; Where test.cpp +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;void f1() { +;; while (true) { +;; struct nested { +;; static void f2() {} +;; }; +;; nested::f2(); +;; } +;;} +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +; CHECK: DW_TAG_compile_unit +; CHECK-NOT: {{DW_TAG|NULL}} +; CHECK: DW_TAG_subprogram +; CHECK-NOT: {{DW_TAG|NULL}} +; CHECK: DW_AT_name {{.*}} "f1" +; CHECK: NULL + +; CHECK-NOT: DW_TAG_subprogram + + +; Function Attrs: nounwind +define void @_Z2f1v() #0 { +entry: + br label %while.body, !dbg !16 + +while.body: ; preds = %while.body, %entry + br label %while.body, !dbg !16 + +return: ; No predecessors! + ret void, !dbg !17 +} + +attributes #0 = { nounwind "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-realign-stack" "stack-protector-buffer-size"="8" "target-features"="+sse,+sse2" "unsafe-fp-math"="false" "use-soft-float"="false" } + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!13, !14} +!llvm.ident = !{!15} + +!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !1, producer: "clang version 3.8.0 (trunk 242994) (llvm/trunk 242995)", isOptimized: false, runtimeVersion: 0, emissionKind: 1, enums: !2, retainedTypes: !3, subprograms: !11) +!1 = !DIFile(filename: "foo.cpp", directory: "/") +!2 = !{} +!3 = !{!4} +!4 = !DICompositeType(tag: DW_TAG_structure_type, name: "nested", scope: !5, file: !1, line: 3, size: 8, align: 8, elements: !9) +!5 = distinct !DILexicalBlock(scope: !6, file: !1, line: 2) +!6 = !DISubprogram(name: "f1", linkageName: "_Z2f1v", scope: !1, file: !1, line: 1, type: !7, isLocal: false, isDefinition: true, scopeLine: 1, flags: DIFlagPrototyped, isOptimized: false, function: void ()* @_Z2f1v, variables: !2) +!7 = !DISubroutineType(types: !8) +!8 = !{null} +!9 = !{!10} +!10 = !DISubprogram(name: "f2", scope: !4, file: !1, line: 4, type: !7, isLocal: false, isDefinition: false, scopeLine: 4, flags: DIFlagPrototyped, isOptimized: false) +!11 = !{!6, !12} +!12 = !DISubprogram(name: "f2", linkageName: "_ZZ2f1vEN6nested2f2Ev", scope: !4, file: !1, line: 4, type: !7, isLocal: true, isDefinition: true, scopeLine: 4, flags: DIFlagPrototyped, isOptimized: false, declaration: !10, variables: !2) +!13 = !{i32 2, !"Dwarf Version", i32 4} +!14 = !{i32 2, !"Debug Info Version", i32 3} +!15 = !{!"clang version 3.8.0 (trunk 242994) (llvm/trunk 242995)"} +!16 = !DILocation(line: 2, scope: !6) +!17 = !DILocation(line: 8, scope: !6) Index: test/DebugInfo/X86/lexical-block-inline.ll =================================================================== --- test/DebugInfo/X86/lexical-block-inline.ll +++ test/DebugInfo/X86/lexical-block-inline.ll @@ -0,0 +1,188 @@ +; RUN: llc -mtriple=x86_64-unknown-linux-gnu -filetype=obj -O0 < %s | llvm-dwarfdump -debug-dump=info - | FileCheck %s + +;; This test checks the following: +;; 1. Useless lexical block entry is not emitted. +;; 2. Function static variable, typedef, records (structure, class and union) +;; that are defined in lexical basic block are emitted as children to +;; these lexical blocks. +;; * For typedef and record check that both are emitted in lexical block +;; where they are declared and not in the one where they are used. +;; 3. "bar" function, which was inlined into "foo" function, are created as +;; abstract (with DW_AT_inline attribute). +;; All variables and types are defined in this abstract entry. +;; 4. "bar" function is created in "foo" function as inlined function +;; (with DW_TAG_inlined_subroutine attribute), and all its local variables +;; are created as concrete variables pointing to the abstract suitable entry +;; defined under abstract "bar" function. +;; +;; This test was generated by running following command: +;; clang -cc1 -O0 -g -emit-llvm foo.cpp -o - | opt -S -inline -sroa +;; Where foo.cpp +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;__inline int bar(int s) { +;; { +;; { +;; struct X { +;; int x; +;; }; +;; typedef int Y; +;; { +;; X a = { s }; +;; Y b(s); +;; static int c = 0; +;; return a.x + b + c++; +;; } +;; } +;; } +;;} +;; +;; +;;int foo(int i) { +;; return +;; bar(i); +;;} +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +; CHECK: [[Offset_bar:0x[0-9abcdef]+]]: DW_TAG_subprogram +; CHECK-NOT: {{DW_TAG|NULL}} +; CHECK: DW_AT_name {{.*}} "bar" +; CHECK: DW_AT_inline +; CHECK-NOT: NULL +; CHECK: DW_TAG_lexical_block + +; CHECK-NOT: {{DW_TAG|NULL}} +; CHECK: DW_TAG_lexical_block + +; CHECK-NOT: {{DW_TAG|NULL}} +; CHECK: [[Offset_a:0x[0-9abcdef]+]]: DW_TAG_variable +; CHECK-NOT: {{DW_TAG|NULL}} +; CHECK: DW_AT_name {{.*}} "a" + +; CHECK-NOT: {{DW_TAG|NULL}} +; CHECK: [[Offset_b:0x[0-9abcdef]+]]: DW_TAG_variable +; CHECK-NOT: {{DW_TAG|NULL}} +; CHECK: DW_AT_name {{.*}} "b" + +; CHECK-NOT: {{DW_TAG|NULL}} +; CHECK: [[Offset_c:0x[0-9abcdef]+]]: DW_TAG_variable +; CHECK-NOT: {{DW_TAG|NULL}} +; CHECK: DW_AT_name {{.*}} "c" +; CHECK-NOT: {{DW_TAG|NULL}} +; CHECK: DW_AT_location + +; CHECK-NOT: {{DW_TAG}} +; CHECK: NULL + +; CHECK-NOT: {{DW_TAG|NULL}} +; CHECK: DW_TAG_structure_type +; CHECK-NOT: {{DW_TAG|NULL}} +; CHECK: DW_AT_name {{.*}} "X" +; CHECK: NULL + +; CHECK-NOT: {{DW_TAG|NULL}} +; CHECK: DW_TAG_typedef +; CHECK-NOT: {{DW_TAG|NULL}} +; CHECK: DW_AT_name {{.*}} "Y" + +; CHECK-NOT: {{DW_TAG}} +; CHECK: NULL + + +; CHECK: DW_TAG_subprogram +; CHECK-NOT: {{DW_TAG|NULL}} +; CHECK: DW_AT_name {{.*}} "foo" +; CHECK-NOT: {{NULL}} +; CHECK: DW_TAG_inlined_subroutine +; CHECK-NEXT: DW_AT_abstract_origin {{.*}} {[[Offset_bar]]} + +; CHECK-NOT: {{NULL}} +; CHECK: DW_TAG_lexical_block + +; CHECK-NOT: {{DW_TAG|NULL}} +; CHECK: DW_TAG_lexical_block + +; CHECK-NOT: {{DW_TAG|NULL}} +; CHECK: DW_TAG_variable +; CHECK-NEXT: DW_AT_location +; CHECK-NEXT: DW_AT_abstract_origin {{.*}} {[[Offset_a]]} + +; CHECK-NOT: {{DW_TAG|NULL}} +; CHECK: DW_TAG_variable +; CHECK-NEXT: DW_AT_location +; CHECK-NEXT: DW_AT_abstract_origin {{.*}} {[[Offset_b]]} + +;; FIXME: Here should appear variable DW_TAG_variable "c" with only +;; DW_AT_abstract_origin to "Offset_c". +;; The following check will fail when LLVM support this. +; CHECK-NOT: {{DW_TAG}} +; CHECK: NULL + +%struct.X = type { i32 } + +$_ZZ3bariE1c = comdat any + +@_ZZ3bariE1c = linkonce_odr global i32 0, comdat, align 4 + +; Function Attrs: nounwind +define i32 @_Z3fooi(i32 %i) #0 { +entry: + call void @llvm.dbg.value(metadata i32 %i, i64 0, metadata !22, metadata !23), !dbg !24 + call void @llvm.dbg.value(metadata i32 %i, i64 0, metadata !25, metadata !23), !dbg !26 + call void @llvm.dbg.declare(metadata %struct.X* undef, metadata !28, metadata !23), !dbg !29 + call void @llvm.dbg.value(metadata i32 %i, i64 0, metadata !28, metadata !23), !dbg !29 + call void @llvm.dbg.value(metadata i32 %i, i64 0, metadata !30, metadata !23), !dbg !31 + %add.i = add nsw i32 %i, %i, !dbg !32 + %0 = load i32, i32* @_ZZ3bariE1c, align 4, !dbg !32 + %inc.i = add nsw i32 %0, 1, !dbg !32 + store i32 %inc.i, i32* @_ZZ3bariE1c, align 4, !dbg !32 + %add2.i = add nsw i32 %add.i, %0, !dbg !32 + ret i32 %add2.i, !dbg !33 +} + +; Function Attrs: nounwind readnone +declare void @llvm.dbg.declare(metadata, metadata, metadata) #1 + +; Function Attrs: nounwind readnone +declare void @llvm.dbg.value(metadata, i64, metadata, metadata) #1 + +attributes #0 = { nounwind "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-realign-stack" "stack-protector-buffer-size"="8" "target-features"="+sse,+sse2" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #1 = { nounwind readnone } + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!19, !20} +!llvm.ident = !{!21} + +!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !1, producer: "clang version 3.8.0 (trunk 242994) (llvm/trunk 242995)", isOptimized: false, runtimeVersion: 0, emissionKind: 1, enums: !2, retainedTypes: !3, subprograms: !14, globals: !16) +!1 = !DIFile(filename: "foo.cpp", directory: "/") +!2 = !{} +!3 = !{!4, !13} +!4 = !DICompositeType(tag: DW_TAG_structure_type, name: "X", scope: !5, file: !1, line: 4, size: 32, align: 32, elements: !11, identifier: "_ZTSZ3bariE1X") +!5 = distinct !DILexicalBlock(scope: !6, file: !1, line: 3) +!6 = distinct !DILexicalBlock(scope: !7, file: !1, line: 2) +!7 = !DISubprogram(name: "bar", linkageName: "_Z3bari", scope: !1, file: !1, line: 1, type: !8, isLocal: false, isDefinition: true, scopeLine: 1, flags: DIFlagPrototyped, isOptimized: false, variables: !2) +!8 = !DISubroutineType(types: !9) +!9 = !{!10, !10} +!10 = !DIBasicType(name: "int", size: 32, align: 32, encoding: DW_ATE_signed) +!11 = !{!12} +!12 = !DIDerivedType(tag: DW_TAG_member, name: "x", scope: !"_ZTSZ3bariE1X", file: !1, line: 5, baseType: !10, size: 32, align: 32) +!13 = !DIDerivedType(tag: DW_TAG_typedef, name: "Y", scope: !5, file: !1, line: 7, baseType: !10) +!14 = !{!15, !7} +!15 = !DISubprogram(name: "foo", linkageName: "_Z3fooi", scope: !1, file: !1, line: 19, type: !8, isLocal: false, isDefinition: true, scopeLine: 19, flags: DIFlagPrototyped, isOptimized: false, function: i32 (i32)* @_Z3fooi, variables: !2) +!16 = !{!17} +!17 = !DIGlobalVariable(name: "c", scope: !18, file: !1, line: 11, type: !10, isLocal: false, isDefinition: true, variable: i32* @_ZZ3bariE1c) +!18 = distinct !DILexicalBlock(scope: !5, file: !1, line: 8) +!19 = !{i32 2, !"Dwarf Version", i32 4} +!20 = !{i32 2, !"Debug Info Version", i32 3} +!21 = !{!"clang version 3.8.0 (trunk 242994) (llvm/trunk 242995)"} +!22 = !DILocalVariable(tag: DW_TAG_arg_variable, name: "i", arg: 1, scope: !15, file: !1, line: 19, type: !10) +!23 = !DIExpression() +!24 = !DILocation(line: 19, scope: !15) +!25 = !DILocalVariable(tag: DW_TAG_arg_variable, name: "s", arg: 1, scope: !7, file: !1, line: 1, type: !10) +!26 = !DILocation(line: 1, scope: !7, inlinedAt: !27) +!27 = distinct !DILocation(line: 21, scope: !15) +!28 = !DILocalVariable(tag: DW_TAG_auto_variable, name: "a", scope: !18, file: !1, line: 9, type: !"_ZTSZ3bariE1X") +!29 = !DILocation(line: 9, scope: !18, inlinedAt: !27) +!30 = !DILocalVariable(tag: DW_TAG_auto_variable, name: "b", scope: !18, file: !1, line: 10, type: !13) +!31 = !DILocation(line: 10, scope: !18, inlinedAt: !27) +!32 = !DILocation(line: 12, scope: !18, inlinedAt: !27) +!33 = !DILocation(line: 20, scope: !15) Index: test/DebugInfo/X86/lexical-block.ll =================================================================== --- test/DebugInfo/X86/lexical-block.ll +++ test/DebugInfo/X86/lexical-block.ll @@ -0,0 +1,131 @@ +; RUN: llc -mtriple=x86_64-unknown-linux-gnu -filetype=obj -O0 < %s | llvm-dwarfdump -debug-dump=info - | FileCheck %s + +;; This test checks the following: +;; 1. Useless lexical block entry is not emitted. +;; 2. Function static variable, typedef, records (structure, class and union) +;; that are defined in lexical basic block are emitted as children to +;; these lexical blocks. +;; * For typedef and record check that both are emitted in lexical block +;; where they are declared and not in the one where they are used. +;; +;; This test was generated by running following command: +;; clang -cc1 -O0 -g -emit-llvm foo.cpp +;; Where foo.cpp +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;int foo(void) { +;; { +;; { +;; struct X { +;; int x; +;; }; +;; typedef int Y; +;; { +;; X a; +;; Y b; +;; static int c; +;; return a.x + b + c; +;; } +;; } +;; } +;;} +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + +; CHECK: DW_TAG_subprogram +; CHECK-NOT: {{DW_TAG|NULL}} +; CHECK: DW_AT_name {{.*}} "foo" +; CHECK-NOT: NULL +; CHECK: DW_TAG_lexical_block + +; CHECK-NOT: {{DW_TAG|NULL}} +; CHECK: DW_TAG_lexical_block + +; CHECK-NOT: {{DW_TAG|NULL}} +; CHECK: DW_TAG_variable +; CHECK-NOT: {{DW_TAG|NULL}} +; CHECK: DW_AT_name {{.*}} "a" + +; CHECK-NOT: {{DW_TAG|NULL}} +; CHECK: DW_TAG_variable +; CHECK-NOT: {{DW_TAG|NULL}} +; CHECK: DW_AT_name {{.*}} "b" + +; CHECK-NOT: {{DW_TAG|NULL}} +; CHECK: DW_TAG_variable +; CHECK-NOT: {{DW_TAG|NULL}} +; CHECK: DW_AT_name {{.*}} "c" + +; CHECK-NOT: {{DW_TAG}} +; CHECK: NULL + +; CHECK-NOT: {{DW_TAG|NULL}} +; CHECK: DW_TAG_structure_type +; CHECK-NOT: {{DW_TAG|NULL}} +; CHECK: DW_AT_name {{.*}} "X" +; CHECK: NULL + +; CHECK-NOT: {{DW_TAG|NULL}} +; CHECK: DW_TAG_typedef +; CHECK-NOT: {{DW_TAG|NULL}} +; CHECK: DW_AT_name {{.*}} "Y" + +; CHECK-NOT: {{DW_TAG}} +; CHECK: NULL + +%struct.X = type { i32 } + +@_ZZ3foovE1c = internal global i32 0, align 4 + +; Function Attrs: nounwind +define i32 @_Z3foov() #0 { +entry: + %a = alloca %struct.X, align 4 + %b = alloca i32, align 4 + call void @llvm.dbg.declare(metadata %struct.X* %a, metadata !21, metadata !22), !dbg !23 + call void @llvm.dbg.declare(metadata i32* %b, metadata !24, metadata !22), !dbg !25 + %x = getelementptr inbounds %struct.X, %struct.X* %a, i32 0, i32 0, !dbg !26 + %0 = load i32, i32* %x, align 4, !dbg !26 + %1 = load i32, i32* %b, align 4, !dbg !26 + %add = add nsw i32 %0, %1, !dbg !26 + %2 = load i32, i32* @_ZZ3foovE1c, align 4, !dbg !26 + %add1 = add nsw i32 %add, %2, !dbg !26 + ret i32 %add1, !dbg !26 +} + +; Function Attrs: nounwind readnone +declare void @llvm.dbg.declare(metadata, metadata, metadata) #1 + +attributes #0 = { nounwind "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-realign-stack" "stack-protector-buffer-size"="8" "target-features"="+sse,+sse2" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #1 = { nounwind readnone } + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!18, !19} +!llvm.ident = !{!20} + +!0 = !DICompileUnit(language: DW_LANG_C_plus_plus, file: !1, producer: "clang version 3.7.0 (trunk 237245)", isOptimized: false, runtimeVersion: 0, emissionKind: 1, enums: !2, retainedTypes: !3, subprograms: !14, globals: !15, imports: !2) +!1 = !DIFile(filename: "foo.cpp", directory: "/") +!2 = !{} +!3 = !{!4, !13} +!4 = !DICompositeType(tag: DW_TAG_structure_type, name: "X", scope: !5, file: !1, line: 4, size: 32, align: 32, elements: !11) +!5 = distinct !DILexicalBlock(scope: !6, file: !1, line: 3) +!6 = distinct !DILexicalBlock(scope: !7, file: !1, line: 2) +!7 = !DISubprogram(name: "foo", linkageName: "_Z3foov", scope: !1, file: !1, line: 1, type: !8, isLocal: false, isDefinition: true, scopeLine: 1, flags: DIFlagPrototyped, isOptimized: false, function: i32 ()* @_Z3foov, variables: !2) +!8 = !DISubroutineType(types: !9) +!9 = !{!10} +!10 = !DIBasicType(name: "int", size: 32, align: 32, encoding: DW_ATE_signed) +!11 = !{!12} +!12 = !DIDerivedType(tag: DW_TAG_member, name: "x", scope: !4, file: !1, line: 5, baseType: !10, size: 32, align: 32) +!13 = !DIDerivedType(tag: DW_TAG_typedef, name: "Y", scope: !5, file: !1, line: 7, baseType: !10) +!14 = !{!7} +!15 = !{!16} +!16 = !DIGlobalVariable(name: "c", scope: !17, file: !1, line: 11, type: !10, isLocal: true, isDefinition: true, variable: i32* @_ZZ3foovE1c) +!17 = distinct !DILexicalBlock(scope: !5, file: !1, line: 8) +!18 = !{i32 2, !"Dwarf Version", i32 4} +!19 = !{i32 2, !"Debug Info Version", i32 3} +!20 = !{!"clang version 3.7.0 (trunk 237245)"} +!21 = !DILocalVariable(tag: DW_TAG_auto_variable, name: "a", scope: !17, file: !1, line: 9, type: !4) +!22 = !DIExpression() +!23 = !DILocation(line: 9, scope: !17) +!24 = !DILocalVariable(tag: DW_TAG_auto_variable, name: "b", scope: !17, file: !1, line: 10, type: !13) +!25 = !DILocation(line: 10, scope: !17) +!26 = !DILocation(line: 12, scope: !17)