Index: lib/CodeGen/AsmPrinter/DwarfCompileUnit.h =================================================================== --- lib/CodeGen/AsmPrinter/DwarfCompileUnit.h +++ lib/CodeGen/AsmPrinter/DwarfCompileUnit.h @@ -77,8 +77,11 @@ /// Apply the DW_AT_stmt_list from this compile unit to the specified DIE. void applyStmtList(DIE &D); - /// getOrCreateGlobalVariableDIE - get or create global variable DIE. - DIE *getOrCreateGlobalVariableDIE(const DIGlobalVariable *GV); + /// Get or create global variable DIE. + /// \param GV Global Variable Node + /// \param SkipContext If true, GV node will not be attached to parent scope. + DIE *getOrCreateGlobalVariableDIE(const DIGlobalVariable *GV, + bool SkipContext = false); /// addLabelAddress - Add a dwarf label attribute data and value using /// either DW_FORM_addr or DW_FORM_GNU_addr_index. Index: lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp =================================================================== --- lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp +++ lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp @@ -101,7 +101,7 @@ /// getOrCreateGlobalVariableDIE - get or create global variable DIE. DIE *DwarfCompileUnit::getOrCreateGlobalVariableDIE( - const DIGlobalVariable *GV) { + const DIGlobalVariable *GV, bool SkipContext) { // Check for pre-existence. if (DIE *Die = getDIE(GV)) return Die; @@ -115,8 +115,11 @@ // case such construction creates the DIE. DIE *ContextDIE = getOrCreateContextDIE(GVContext); - // Add to map. - DIE *VariableDIE = &createAndAddDIE(GV->getTag(), *ContextDIE, GV); + // Create new global variable and add to map. + DIE *VariableDIE = + SkipContext ? createDIE(GV->getTag(), GV) + : &createAndAddDIE(GV->getTag(), *ContextDIE, GV); + DIScope *DeclContext; if (auto *SDMDecl = GV->getStaticDataMemberDeclaration()) { DeclContext = resolve(SDMDecl->getScope()); @@ -313,11 +316,12 @@ "subprograms"); SmallVector Children; + bool AddScopeDieToFinalChildren = true; // We try to create the scope DIE first, then the children DIEs. This will // avoid creating un-used children then removing them later when we find out // the scope DIE is null. - DIE *ScopeDIE; + DIE *ScopeDIE = nullptr; if (Scope->getParent() && isa(DS)) { ScopeDIE = constructInlinedScopeDIE(Scope); if (!ScopeDIE) @@ -329,19 +333,38 @@ if (DD->isLexicalScopeDIENull(Scope)) return; + bool RegularScope = !Scope->getInlinedAt() && !Scope->isAbstractScope(); + + // Skip local decls in gmlt-like data. + if (!includeMinimalInlineScopes()) { + auto LocalDeclNodeRangeForScope = DD->findLocalDeclNodesForScope(DS); + for (const auto &DI : LocalDeclNodeRangeForScope) { + if (auto *IE = dyn_cast(DI.second)) + Children.push_back(constructImportedEntityDIE(IE)); + else if (RegularScope) { + // Types & static variables are emitted only in regular lexical block + // located in the regular function, i.e. not abstract/inline function. + if (auto *GV = dyn_cast(DI.second)) { + assert(!getDIE(GV) && "Trying to generate static variable twice"); + Children.push_back( + getOrCreateGlobalVariableDIE(GV, /* SkipContext */ true)); + } + else if (auto *RT = dyn_cast(DI.second)) { + if (getDIE(RT)) + // Type was already created, nothing to do. + continue; + Children.push_back(getOrCreateTypeDIE(RT, /* SkipContext */ true)); + } + } + } + } + unsigned ChildScopeCount; // We create children here when we know the scope DIE is not going to be // null and the children will be added to the scope DIE. createScopeChildrenDIE(Scope, Children, &ChildScopeCount); - // Skip imported directives 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))); - } // If there are only other scopes as children, put them directly in the // parent instead, as this scope would serve no purpose. @@ -351,15 +374,27 @@ std::make_move_iterator(Children.end())); return; } - ScopeDIE = constructLexicalScopeDIE(Scope); - assert(ScopeDIE && "Scope DIE should not be null."); + // Lexical block of non-inline function is stored in the lexical scope map. + if (RegularScope) + ScopeDIE = getDIE(DS); + if (ScopeDIE) { + // Mapped lexical scope should be already attached to its parent scope. + assert(ScopeDIE->getParent() && "Scope DIE was not attached to parent."); + AddScopeDieToFinalChildren = false; + } else { + ScopeDIE = constructLexicalScopeDIE(Scope); + assert(ScopeDIE && "Scope DIE should not be null."); + if (RegularScope) + insertDIE(Scope->getScopeNode(), ScopeDIE); + } } // Add children for (auto &I : Children) ScopeDIE->addChild(std::move(I)); - FinalChildren.push_back(std::move(ScopeDIE)); + if (AddScopeDieToFinalChildren) + FinalChildren.push_back(std::move(ScopeDIE)); } DIE::value_iterator Index: lib/CodeGen/AsmPrinter/DwarfDebug.h =================================================================== --- lib/CodeGen/AsmPrinter/DwarfDebug.h +++ lib/CodeGen/AsmPrinter/DwarfDebug.h @@ -303,13 +303,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. @@ -660,10 +657,12 @@ const MachineFunction *getCurrentFunction() const { return CurFn; } - iterator_range - findImportedEntitiesForScope(const MDNode *Scope) const { + typedef iterator_range LocalDeclMapRange; + + LocalDeclMapRange 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())); } Index: lib/CodeGen/AsmPrinter/DwarfDebug.cpp =================================================================== --- lib/CodeGen/AsmPrinter/DwarfDebug.cpp +++ lib/CodeGen/AsmPrinter/DwarfDebug.cpp @@ -475,14 +475,14 @@ 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()) { + auto *Context = GV->getScope(); + if (Context && isa(Context)) + ScopesWithLocalDeclNodes.push_back(std::make_pair(Context, GV)); + else + CU.getOrCreateGlobalVariableDIE(GV); + } for (auto *SP : CUNode->getSubprograms()) SPMap.insert(std::make_pair(SP, &CU)); for (auto *Ty : CUNode->getEnumTypes()) { @@ -494,14 +494,25 @@ // 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; + auto *Context = resolve(Ty->getScope()); + if (Context && isa(Context)) + ScopesWithLocalDeclNodes.push_back(std::make_pair(Context, RT)); + else 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. @@ -692,7 +703,7 @@ DbgVariable * DwarfDebug::getExistingAbstractVariable(InlinedVariable IV, const DILocalVariable *&Cleansed) { - // More then one inlined variable corresponds to one abstract variable. + // More than one inlined variable corresponds to one abstract variable. Cleansed = IV.first; auto I = AbstractVariables.find(Cleansed); if (I != AbstractVariables.end()) Index: lib/CodeGen/AsmPrinter/DwarfUnit.h =================================================================== --- lib/CodeGen/AsmPrinter/DwarfUnit.h +++ lib/CodeGen/AsmPrinter/DwarfUnit.h @@ -297,12 +297,16 @@ DIE *getOrCreateNameSpace(const DINamespace *NS); DIE *getOrCreateModule(const DIModule *M); DIE *getOrCreateSubprogramDIE(const DISubprogram *SP, bool Minimal = false); + /// Get or create a lexical block DIE and attach it to its parent scope. + DIE *getOrCreateLexicalBlockDIE(const DILexicalBlockBase *LB); void applySubprogramAttributes(const DISubprogram *SP, DIE &SPDie, bool Minimal = false); /// Find existing DIE or create new DIE for the given type. - DIE *getOrCreateTypeDIE(const MDNode *N); + /// \param N Type Node + /// \param SkipContext If true, N node will not be attached to parent scope. + DIE *getOrCreateTypeDIE(const MDNode *N, bool SkipContext = false); /// Get context owner's DIE. DIE *createTypeDIE(const DICompositeType *Ty); @@ -316,6 +320,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)); + Tag != dwarf::DW_TAG_arg_variable); + 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; } @@ -712,7 +717,7 @@ return &TyDIE; } -DIE *DwarfUnit::getOrCreateTypeDIE(const MDNode *TyNode) { +DIE *DwarfUnit::getOrCreateTypeDIE(const MDNode *TyNode, bool SkipContext) { if (!TyNode) return nullptr; @@ -722,19 +727,29 @@ // DW_TAG_restrict_type is not supported in DWARF2 if (Ty->getTag() == dwarf::DW_TAG_restrict_type && DD->getDwarfVersion() <= 2) - return getOrCreateTypeDIE(resolve(cast(Ty)->getBaseType())); + return getOrCreateTypeDIE(resolve(cast(Ty)->getBaseType()), + SkipContext); // Construct the context before querying for the existence of the DIE in case // such construction creates the DIE. auto *Context = resolve(Ty->getScope()); DIE *ContextDIE = getOrCreateContextDIE(Context); - assert(ContextDIE); if (DIE *TyDIE = getDIE(Ty)) return TyDIE; - // Create new type. - DIE &TyDIE = createAndAddDIE(Ty->getTag(), *ContextDIE, Ty); + if (!ContextDIE && !SkipContext) { + // Can reach this point when the parent scope is a lexical block. + // Which was not created yet, or that does not contain code, create it. + assert(isa(Context) && "Expected lexical block scope"); + ContextDIE = getOrCreateLexicalBlockDIE(cast(Context)); + assert(ContextDIE); + } + + // Create new type and add to map. + DIE &TyDIE = + SkipContext ? *createDIE(Ty->getTag(), Ty) + : createAndAddDIE(Ty->getTag(), *ContextDIE, Ty); updateAcceleratorTables(Context, Ty, TyDIE); @@ -1139,6 +1154,25 @@ return &SPDie; } +DIE *DwarfUnit::getOrCreateLexicalBlockDIE(const DILexicalBlockBase *LB) { + // Construct the context before querying for the existence of the DIE in case + // such construction creates the DIE. + auto Context = LB->getScope(); + DIE *ContextDIE = getOrCreateContextDIE(Context); + if (!ContextDIE) { + // Can reach this point when the parent scope is a lexical block. + // Which was not created yet, or that does not contain code, create it. + assert(isa(Context) && "Expected lexical block scope"); + ContextDIE = getOrCreateLexicalBlockDIE(cast(Context)); + assert(ContextDIE); + } + if (DIE *LBDie = getDIE(LB)) + return LBDie; + + DIE &LBDie = createAndAddDIE(dwarf::DW_TAG_lexical_block, *ContextDIE, LB); + return &LBDie; +} + bool DwarfUnit::applySubprogramDefinitionAttributes(const DISubprogram *SP, DIE &SPDie) { DIE *DeclDie = nullptr; Index: test/DebugInfo/X86/PR24008.ll =================================================================== --- test/DebugInfo/X86/PR24008.ll +++ test/DebugInfo/X86/PR24008.ll @@ -0,0 +1,138 @@ +; 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. class Bar is declared nested as follow: +;; DW_TAG_subprogram (Foo::TestBody) +;; DW_TAG_lexical_block +;; DW_TAG_class_type (Bar) +;; 3. Bar::Run() function is defined nested to compile unit. +;; +;; This test was generated by running following command: +;; clang -cc1 -O2 -g -emit-llvm test.cpp +;; Where test.cpp +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;;class Foo{ +;;public: +;; Foo() {} +;;private: +;; virtual void TestBody(int& s); +;;}; +;; +;;void Foo::TestBody(int& s) { +;; do { +;; class Bar { +;; public: +;; static void Run() {} +;; }; +;; Bar::Run(); +;; } while (false); +;;} +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +; CHECK: DW_TAG_compile_unit + +; CHECK-NOT: {{DW_TAG|NULL}} +; CHECK: DW_TAG_class_type +; CHECK-NOT: {{DW_TAG|NULL}} +; CHECK: DW_AT_name {{.*}} "Foo" +; CHECK-NOT: NULL +; CHECK: DW_TAG_subprogram +; CHECK-NOT: {{DW_TAG|NULL}} +; CHECK: DW_AT_name {{.*}} "Foo" +; CHECK: NULL +; CHECK-NOT: {{DW_TAG|NULL}} +; CHECK: [[Offset1:0x[0-9abcdef]+]]: DW_TAG_subprogram +; CHECK-NOT: {{DW_TAG|NULL}} +; CHECK: DW_AT_name {{.*}} "TestBody" +; CHECK: NULL +; CHECK: NULL + +; CHECK-NOT: {{NULL}} +; CHECK: DW_TAG_subprogram +; CHECK-NOT: {{DW_TAG|NULL}} +; CHECK: DW_AT_specification {{.*}} {[[Offset1]]} +; CHECK-NOT: NULL +; CHECK: DW_TAG_lexical_block +; CHECK-NOT: {{DW_TAG|NULL}} +; CHECK: DW_TAG_class_type +; CHECK-NOT: {{DW_TAG|NULL}} +; CHECK: DW_AT_name {{.*}} "Bar" +; CHECK: [[Offset2:0x[0-9abcdef]+]]: DW_TAG_subprogram +; CHECK-NOT: {{DW_TAG|NULL}} +; CHECK: DW_AT_name {{.*}} "Run" +; CHECK: NULL +; CHECK: NULL +; CHECK: NULL + +; CHECK-NOT: {{DW_TAG|NULL}} +; CHECK: DW_TAG_subprogram +; CHECK-NOT: {{DW_TAG|NULL}} +; CHECK: DW_AT_specification {{.*}} {[[Offset2]]} + + +%class.Foo = type { i32 (...)** } + +@_ZTV3Foo = unnamed_addr constant [3 x i8*] [i8* null, i8* bitcast ({ i8*, i8* }* @_ZTI3Foo to i8*), i8* bitcast (void (%class.Foo*, i32*)* @_ZN3Foo8TestBodyERi to i8*)], align 8 +@_ZTVN10__cxxabiv117__class_type_infoE = external global i8* +@_ZTS3Foo = constant [5 x i8] c"3Foo\00" +@_ZTI3Foo = constant { i8*, i8* } { i8* bitcast (i8** getelementptr inbounds (i8*, i8** @_ZTVN10__cxxabiv117__class_type_infoE, i64 2) to i8*), i8* getelementptr inbounds ([5 x i8], [5 x i8]* @_ZTS3Foo, i32 0, i32 0) } + +; Function Attrs: nounwind readnone +define void @_ZN3Foo8TestBodyERi(%class.Foo* nocapture %this, i32* nocapture dereferenceable(4) %s) unnamed_addr #0 align 2 { +entry: + tail call void @llvm.dbg.value(metadata %class.Foo* %this, i64 0, metadata !24, metadata !36), !dbg !37 + tail call void @llvm.dbg.value(metadata i32* %s, i64 0, metadata !26, metadata !36), !dbg !38 + ret void, !dbg !39 +} + +; Function Attrs: nounwind readnone +declare void @llvm.dbg.value(metadata, i64, metadata, metadata) #0 + +attributes #0 = { nounwind readnone } + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!33, !34} +!llvm.ident = !{!35} + +!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !1, producer: "clang version 3.7.0 (trunk 241146) (llvm/trunk 241150)", isOptimized: true, runtimeVersion: 0, emissionKind: 1, enums: !2, retainedTypes: !3, subprograms: !31, globals: !2, imports: !2) +!1 = !DIFile(filename: "test.cpp", directory: "/") +!2 = !{} +!3 = !{!4, !20} +!4 = !DICompositeType(tag: DW_TAG_class_type, name: "Foo", file: !1, line: 2, size: 64, align: 64, elements: !5, vtableHolder: !"_ZTS3Foo", identifier: "_ZTS3Foo") +!5 = !{!6, !12, !16} +!6 = !DIDerivedType(tag: DW_TAG_member, name: "_vptr$Foo", scope: !1, file: !1, baseType: !7, size: 64, flags: DIFlagArtificial) +!7 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !8, size: 64) +!8 = !DIDerivedType(tag: DW_TAG_pointer_type, name: "__vtbl_ptr_type", baseType: !9, size: 64) +!9 = !DISubroutineType(types: !10) +!10 = !{!11} +!11 = !DIBasicType(name: "int", size: 32, align: 32, encoding: DW_ATE_signed) +!12 = !DISubprogram(name: "Foo", scope: !"_ZTS3Foo", file: !1, line: 4, type: !13, isLocal: false, isDefinition: false, scopeLine: 4, flags: DIFlagPublic | DIFlagPrototyped, isOptimized: true) +!13 = !DISubroutineType(types: !14) +!14 = !{null, !15} +!15 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !"_ZTS3Foo", size: 64, align: 64, flags: DIFlagArtificial | DIFlagObjectPointer) +!16 = !DISubprogram(name: "TestBody", linkageName: "_ZN3Foo8TestBodyERi", scope: !"_ZTS3Foo", file: !1, line: 6, type: !17, isLocal: false, isDefinition: false, scopeLine: 6, containingType: !"_ZTS3Foo", virtuality: DW_VIRTUALITY_virtual, flags: DIFlagPrototyped, isOptimized: true) +!17 = !DISubroutineType(types: !18) +!18 = !{null, !15, !19} +!19 = !DIDerivedType(tag: DW_TAG_reference_type, baseType: !11) +!20 = !DICompositeType(tag: DW_TAG_class_type, name: "Bar", scope: !21, file: !1, line: 11, size: 8, align: 8, elements: !27) +!21 = distinct !DILexicalBlock(scope: !22, file: !1, line: 10) +!22 = !DISubprogram(name: "TestBody", linkageName: "_ZN3Foo8TestBodyERi", scope: !"_ZTS3Foo", file: !1, line: 9, type: !17, isLocal: false, isDefinition: true, scopeLine: 9, flags: DIFlagPrototyped, isOptimized: true, function: void (%class.Foo*, i32*)* @_ZN3Foo8TestBodyERi, declaration: !16, variables: !23) +!23 = !{!24, !26} +!24 = !DILocalVariable(tag: DW_TAG_arg_variable, name: "this", arg: 1, scope: !22, type: !25, flags: DIFlagArtificial | DIFlagObjectPointer) +!25 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !"_ZTS3Foo", size: 64, align: 64) +!26 = !DILocalVariable(tag: DW_TAG_arg_variable, name: "s", arg: 2, scope: !22, file: !1, line: 9, type: !19) +!27 = !{!28} +!28 = !DISubprogram(name: "Run", scope: !20, file: !1, line: 13, type: !29, isLocal: false, isDefinition: false, scopeLine: 13, flags: DIFlagPublic | DIFlagPrototyped, isOptimized: true) +!29 = !DISubroutineType(types: !30) +!30 = !{null} +!31 = !{!22, !32} +!32 = !DISubprogram(name: "Run", linkageName: "_ZZN3Foo8TestBodyERiEN3Bar3RunEv", scope: !20, file: !1, line: 13, type: !29, isLocal: true, isDefinition: true, scopeLine: 13, flags: DIFlagPrototyped, isOptimized: true, declaration: !28, variables: !2) +!33 = !{i32 2, !"Dwarf Version", i32 4} +!34 = !{i32 2, !"Debug Info Version", i32 3} +!35 = !{!"clang version 3.7.0 (trunk 241146) (llvm/trunk 241150)"} +!36 = !DIExpression() +!37 = !DILocation(line: 0, scope: !22) +!38 = !DILocation(line: 9, scope: !22) +!39 = !DILocation(line: 17, scope: !22) 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,190 @@ +; 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). +;; 4. Concrete "bar" function is created in "foo" function as inlined function +;; (with DW_TAG_inlined_subroutin attribute), and all its local variables +;; are created as concrete variables pointing to the abstract suitable entry +;; defined under abstract "bar" function. +;; Note: static variable "c" is not checked (currently, static variables are +;; emitted only in non-inline function, if such has a code). +;; +;; This test was generated by running following command: +;; clang -cc1 -O2 -g -emit-llvm foo.cpp +;; 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: [[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}} +; CHECK: NULL +; CHECK-NOT: {{DW_TAG}} +; CHECK: NULL + +; CHECK: DW_TAG_subprogram +; CHECK-NEXT: DW_AT_abstract_origin {{.*}} {[[Offset_bar]]} + +; CHECK-NOT: {{DW_TAG|NULL}} +; CHECK: DW_TAG_lexical_block + +; CHECK-NOT: {{DW_TAG|NULL}} +; CHECK: DW_TAG_lexical_block + +; 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-NOT: {{DW_TAG}} +; CHECK: NULL +; 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_subroutin +; CHECK-NEXT: DW_AT_abstract_origin {{.*}} {[[Offset_bar]]} + +; CHECK-NOT: {{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]]} + +%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: + tail call void @llvm.dbg.value(metadata i32 %i, i64 0, metadata !22, metadata !28), !dbg !29 + tail call void @llvm.dbg.value(metadata i32 %i, i64 0, metadata !12, metadata !28), !dbg !30 + tail call void @llvm.dbg.declare(metadata %struct.X* undef, metadata !13, metadata !28), !dbg !32 + tail call void @llvm.dbg.value(metadata i32 %i, i64 0, metadata !13, metadata !28), !dbg !32 + tail call void @llvm.dbg.value(metadata i32 %i, i64 0, metadata !15, metadata !28), !dbg !33 + %add.i = shl i32 %i, 1, !dbg !34 + %0 = load i32, i32* @_ZZ3bariE1c, align 4, !dbg !34, !tbaa !35 + %inc.i = add nsw i32 %0, 1, !dbg !34 + store i32 %inc.i, i32* @_ZZ3bariE1c, align 4, !dbg !34, !tbaa !35 + %add2.i = add nsw i32 %0, %add.i, !dbg !34 + ret i32 %add2.i, !dbg !39 +} + +; 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 = !{!25, !26} +!llvm.ident = !{!27} + +!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !1, producer: "clang version 3.7.0 (trunk 241146) (llvm/trunk 241150)", isOptimized: true, runtimeVersion: 0, emissionKind: 1, enums: !2, retainedTypes: !3, subprograms: !19, globals: !23, imports: !2) +!1 = !DIFile(filename: "test.cpp", directory: "/") +!2 = !{} +!3 = !{!4, !16} +!4 = !DICompositeType(tag: DW_TAG_structure_type, name: "X", scope: !5, file: !1, line: 4, size: 32, align: 32, elements: !17, 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: true, variables: !11) +!8 = !DISubroutineType(types: !9) +!9 = !{!10, !10} +!10 = !DIBasicType(name: "int", size: 32, align: 32, encoding: DW_ATE_signed) +!11 = !{!12, !13, !15} +!12 = !DILocalVariable(tag: DW_TAG_arg_variable, name: "s", arg: 1, scope: !7, file: !1, line: 1, type: !10) +!13 = !DILocalVariable(tag: DW_TAG_auto_variable, name: "a", scope: !14, file: !1, line: 9, type: !"_ZTSZ3bariE1X") +!14 = distinct !DILexicalBlock(scope: !5, file: !1, line: 8) +!15 = !DILocalVariable(tag: DW_TAG_auto_variable, name: "b", scope: !14, file: !1, line: 10, type: !16) +!16 = !DIDerivedType(tag: DW_TAG_typedef, name: "Y", scope: !5, file: !1, line: 7, baseType: !10) +!17 = !{!18} +!18 = !DIDerivedType(tag: DW_TAG_member, name: "x", scope: !"_ZTSZ3bariE1X", file: !1, line: 5, baseType: !10, size: 32, align: 32) +!19 = !{!20, !7} +!20 = !DISubprogram(name: "foo", linkageName: "_Z3fooi", scope: !1, file: !1, line: 19, type: !8, isLocal: false, isDefinition: true, scopeLine: 19, flags: DIFlagPrototyped, isOptimized: true, function: i32 (i32)* @_Z3fooi, variables: !21) +!21 = !{!22} +!22 = !DILocalVariable(tag: DW_TAG_arg_variable, name: "i", arg: 1, scope: !20, file: !1, line: 19, type: !10) +!23 = !{!24} +!24 = !DIGlobalVariable(name: "c", scope: !14, file: !1, line: 11, type: !10, isLocal: false, isDefinition: true, variable: i32* @_ZZ3bariE1c) +!25 = !{i32 2, !"Dwarf Version", i32 4} +!26 = !{i32 2, !"Debug Info Version", i32 3} +!27 = !{!"clang version 3.7.0 (trunk 241146) (llvm/trunk 241150)"} +!28 = !DIExpression() +!29 = !DILocation(line: 19, scope: !20) +!30 = !DILocation(line: 1, scope: !7, inlinedAt: !31) +!31 = distinct !DILocation(line: 21, scope: !20) +!32 = !DILocation(line: 9, scope: !14, inlinedAt: !31) +!33 = !DILocation(line: 10, scope: !14, inlinedAt: !31) +!34 = !DILocation(line: 12, scope: !14, inlinedAt: !31) +!35 = !{!36, !36, i64 0} +!36 = !{!"int", !37, i64 0} +!37 = !{!"omnipotent char", !38, i64 0} +!38 = !{!"Simple C/C++ TBAA"} +!39 = !DILocation(line: 20, scope: !20) Index: test/DebugInfo/X86/lexical-block.ll =================================================================== --- test/DebugInfo/X86/lexical-block.ll +++ test/DebugInfo/X86/lexical-block.ll @@ -0,0 +1,129 @@ +; 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_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|NULL}} +; CHECK: DW_TAG_lexical_block + +; CHECK-NOT: {{DW_TAG|NULL}} +; CHECK: DW_TAG_variable +; CHECK-NOT: {{DW_TAG|NULL}} +; CHECK: DW_AT_name {{.*}} "c" + +; 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}} +; 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)