From f9a78ea808ab0e36bc7ada6dcb395cb31638f9aa Mon Sep 17 00:00:00 2001 From: Vladislav Dzhidzhoev Date: Thu, 31 Jul 2025 22:56:22 +0300 Subject: [PATCH 1/3] [DwarfDebug] Track abstract entities in DwarfUnit separately `DwarfCompileUnit::constructVariableDIE()` and `constructLabelDIE()` are meant for constructing both abstract and concrete DIEs of a DbgEntity. They use `DwarfUnit::insertDIE()` to store a freshly-created DIE. However, `insertDIE()`/`DwarfUnit::DITypeNodeToDieMap` store only single DIE per DINode. If `insertDIE()` is called several times for the same instance of DINode, only first DIE is saved in `DwarfUnit::DITypeNodeToDieMap`, as follows from `DenseMap::insert()` specification. It means, depending on what is called first, `DwarfCompileUnit::constructVariableDIE(LV, /* Abstract */ true)` or `DwarfCompileUnit::constructVariableDIE(LV, /* Abstract */ false)`, `DwarfUnit::DITypeNodeToDieMap` stores either abstract or concrete DIE of a node. This behavior suggests an obscure API of DwarfCompileUnit, as it depends on function call order and makes it unclear what `DwarfUnit::DITypeNodeToDieMap` is meant to store. To address that, DwarfInfoHolder class is introduced, which stores DIEs for DILocalVariables and DILabels separately from DIEs for other DINodes (as DILocalVariables and DILabels may have concrete and abstract DIEs), and allows explicit access to abstract/concrete DIEs of a debug info entity. Also, DwarfFile and DwarfUnit have a tiny duplicate code piece. AbstractEntities, AbstractLocalScopeDIEs and FinalizedAbstractSubprograms tracking were moved to DwarfInfoHolder, as the corresponding entities may be shared across CUs. DwarfInfoHolder may later be used for tracking DIEs of abstract/concrete lexical scopes. Currently, concrete lexical block/subprogram DIEs are distinguished by their DISubprogram/DILocalScope/DILocalScope+inlinedAt in DwarfCompileUnit. As a result, the same DISubprogram can't be attached to two llvm::Functions (https://lists.llvm.org/pipermail/llvm-dev/2020-September/145342.html). Matching DISubprogram/DILocalScope DIEs with their LexicalScopes and letting DwarfUnit members to access abstract scopes may enable linking DISubprogram to several llvm::Functions, and allow the transition from distinct to uniqued DISubprograms proposed here https://github.com/llvm/llvm-project/pull/142166#issuecomment-2981729002. --- .../CodeGen/AsmPrinter/DwarfCompileUnit.cpp | 10 +- .../lib/CodeGen/AsmPrinter/DwarfCompileUnit.h | 22 +-- llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp | 3 +- llvm/lib/CodeGen/AsmPrinter/DwarfFile.h | 169 ++++++++++++++---- llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp | 49 ++--- llvm/lib/CodeGen/AsmPrinter/DwarfUnit.h | 16 +- 6 files changed, 185 insertions(+), 84 deletions(-) diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp index 518121e200190..bfcc01829bce0 100644 --- a/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp @@ -178,7 +178,7 @@ unsigned DwarfCompileUnit::getOrCreateSourceID(const DIFile *File) { DIE *DwarfCompileUnit::getOrCreateGlobalVariableDIE( const DIGlobalVariable *GV, ArrayRef GlobalExprs) { // Check for pre-existence. - if (DIE *Die = getDIE(GV)) + if (DIE *Die = getDIEs(GV).getVariableDIE(GV)) return Die; assert(GV); @@ -795,7 +795,9 @@ DIE *DwarfCompileUnit::constructLexicalScopeDIE(LexicalScope *Scope) { DIE *DwarfCompileUnit::constructVariableDIE(DbgVariable &DV, bool Abstract) { auto *VariableDie = DIE::get(DIEValueAllocator, DV.getTag()); - insertDIE(DV.getVariable(), VariableDie); + getDIEs(DV.getVariable()) + .getLVs() + .insertDIE(DV.getVariable(), &DV, VariableDie, Abstract); DV.setDIE(*VariableDie); // Abstract variables don't get common attributes later, so apply them now. if (Abstract) { @@ -1010,7 +1012,9 @@ DIE *DwarfCompileUnit::constructVariableDIE(DbgVariable &DV, DIE *DwarfCompileUnit::constructLabelDIE(DbgLabel &DL, const LexicalScope &Scope) { auto LabelDie = DIE::get(DIEValueAllocator, DL.getTag()); - insertDIE(DL.getLabel(), LabelDie); + getDIEs(DL.getLabel()) + .getLabels() + .insertDIE(DL.getLabel(), &DL, LabelDie, Scope.isAbstractScope()); DL.setDIE(*LabelDie); if (Scope.isAbstractScope()) diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.h b/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.h index a3bbc8364599d..902cd8cac8d78 100644 --- a/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.h +++ b/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.h @@ -79,16 +79,10 @@ class DwarfCompileUnit final : public DwarfUnit { // List of concrete lexical block scopes belong to subprograms within this CU. DenseMap LexicalBlockDIEs; - // List of abstract local scopes (either DISubprogram or DILexicalBlock). - DenseMap AbstractLocalScopeDIEs; - SmallPtrSet FinalizedAbstractSubprograms; - // List of inlined lexical block scopes that belong to subprograms within this // CU. DenseMap> InlinedLocalScopeDIEs; - DenseMap> AbstractEntities; - /// DWO ID for correlating skeleton and split units. uint64_t DWOId = 0; @@ -126,22 +120,20 @@ class DwarfCompileUnit final : public DwarfUnit { bool isDwoUnit() const override; + DwarfInfoHolder &getDIEs(const DINode *N) { return DwarfUnit::getDIEs(N); } + + DwarfInfoHolder &getDIEs() { return getDIEs(nullptr); } + DenseMap &getAbstractScopeDIEs() { - if (isDwoUnit() && !DD->shareAcrossDWOCUs()) - return AbstractLocalScopeDIEs; - return DU->getAbstractScopeDIEs(); + return getDIEs().getAbstractScopeDIEs(); } DenseMap> &getAbstractEntities() { - if (isDwoUnit() && !DD->shareAcrossDWOCUs()) - return AbstractEntities; - return DU->getAbstractEntities(); + return getDIEs().getAbstractEntities(); } auto &getFinalizedAbstractSubprograms() { - if (isDwoUnit() && !DD->shareAcrossDWOCUs()) - return FinalizedAbstractSubprograms; - return DU->getFinalizedAbstractSubprograms(); + return getDIEs().getFinalizedAbstractSubprograms(); } void finishNonUnitTypeDIE(DIE& D, const DICompositeType *CTy) override; diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp index d751a7f9f01ef..2080b592d2fb8 100644 --- a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp @@ -502,7 +502,8 @@ void DwarfDebug::addSubprogramNames( // well into the name table. Only do that if we are going to actually emit // that name. if (LinkageName != "" && SP->getName() != LinkageName && - (useAllLinkageNames() || InfoHolder.getAbstractScopeDIEs().lookup(SP))) + (useAllLinkageNames() || + InfoHolder.getDIEs().getAbstractScopeDIEs().lookup(SP))) addAccelName(Unit, NameTableKind, LinkageName, Die); // If this is an Objective-C selector name add it to the ObjC accelerator diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfFile.h b/llvm/lib/CodeGen/AsmPrinter/DwarfFile.h index ef1524d875c84..18388c3b7c1e9 100644 --- a/llvm/lib/CodeGen/AsmPrinter/DwarfFile.h +++ b/llvm/lib/CodeGen/AsmPrinter/DwarfFile.h @@ -15,9 +15,12 @@ #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" #include "llvm/CodeGen/DIE.h" +#include "llvm/IR/DebugInfoMetadata.h" #include "llvm/Support/Allocator.h" +#include #include #include +#include #include namespace llvm { @@ -26,9 +29,6 @@ class AsmPrinter; class DbgEntity; class DbgVariable; class DbgLabel; -class DINode; -class DILocalScope; -class DISubprogram; class DwarfCompileUnit; class DwarfUnit; class LexicalScope; @@ -53,6 +53,137 @@ struct RangeSpanList { SmallVector Ranges; }; +/// Tracks abstract and concrete DIEs for debug info entities of a certain type. +template class DINodeInfoHolder { +public: + using AbstractMapT = DenseMap; + using ConcreteMapT = + DenseMap>; + +private: + AbstractMapT AbstractMap; + ConcreteMapT ConcreteMap; + +public: + void insertAbstractDIE(const DINodeT *N, DIE *D) { + auto [_, Inserted] = AbstractMap.try_emplace(N, D); + assert(Inserted && "Duplicate abstract DIE for debug info node"); + } + + void insertConcreteDIE(const DINodeT *N, const DbgEntityT *E, DIE *D) { + auto [_, Inserted] = ConcreteMap[N].try_emplace(E, D); + assert(Inserted && "Duplicate concrete DIE for debug info node"); + } + + void insertDIE(const DINodeT *N, const DbgEntityT *E, DIE *D, bool Abstract) { + if (Abstract) + insertAbstractDIE(N, D); + else + insertConcreteDIE(N, E, D); + } + + DIE *getAbstractDIE(const DINodeT *N) const { return AbstractMap.lookup(N); } + + std::optional< + std::reference_wrapper> + getConcreteDIEs(const DINodeT *N) const { + if (auto I = ConcreteMap.find(N); I != ConcreteMap.end()) + return std::make_optional(std::ref(I->second)); + return std::nullopt; + } + + DIE *getConcreteDIE(const DINodeT *N, const DbgEntityT *E) const { + if (auto I = getConcreteDIEs(N)) + return I->get().lookup(E); + return nullptr; + } + + DIE *getAnyConcreteDIE(const DINodeT *N) const { + if (auto I = getConcreteDIEs(N)) + return I->get().empty() ? nullptr : I->get().begin()->second; + return nullptr; + } + + /// Returns abstract DIE for the entity. + /// If no abstract DIE was created, returns any concrete DIE for the entity. + DIE *getDIE(const DINodeT *N) const { + if (DIE *D = getAbstractDIE(N)) + return D; + + return getAnyConcreteDIE(N); + } + + AbstractMapT &getAbstractDIEs() { return AbstractMap; } +}; + +/// Tracks DIEs for debug info entites. +/// These DIEs can be shared across CUs, that is why we keep the map here +/// instead of in DwarfCompileUnit. +class DwarfInfoHolder { + /// DIEs of local DbgVariables. + DINodeInfoHolder LVHolder; + /// DIEs of labels. + DINodeInfoHolder LabelHolder; + DenseMap> AbstractEntities; + // List of abstract local scopes (either DISubprogram or DILexicalBlock). + DenseMap AbstractLocalScopeDIEs; + /// Keeps track of abstract subprograms to populate them only once. + // FIXME: merge creation and population of abstract scopes. + SmallPtrSet FinalizedAbstractSubprograms; + + /// Other DINodes with the corresponding DIEs. + DenseMap MDNodeToDieMap; + +public: + void insertDIE(const DINode *N, DIE *Die) { + assert((!isa(N) && !isa(N)) && + "Use getLabels().insertDIE() for labels or getLVs().insertDIE() for " + "local variables"); + auto [_, Inserted] = MDNodeToDieMap.try_emplace(N, Die); + assert((Inserted || isa(N) || isa(N)) && + "DIE for this DINode has already been added"); + } + + void insertDIE(DIE *D) { MDNodeToDieMap.try_emplace(nullptr, D); } + + DIE *getDIE(const DINode *N) const { + DIE *D = MDNodeToDieMap.lookup(N); + assert((!D || (!isa(N) && !isa(N))) && + "Use getLabels().getDIE() for labels or getLVs().getDIE() for " + "local variables"); + return D; + } + + auto &getLVs() { return LVHolder; } + auto &getLVs() const { return LVHolder; } + + auto &getLabels() { return LabelHolder; } + auto &getLabels() const { return LabelHolder; } + + /// For a global variable, returns DIE of the variable. + /// + /// For a local variable, returns abstract DIE of the variable. + /// If no abstract DIE was created, returns any concrete DIE of the variable. + DIE *getVariableDIE(const DIVariable *V) const { + if (auto *LV = dyn_cast(V)) + if (DIE *D = getLVs().getDIE(LV)) + return D; + return getDIE(V); + } + + DenseMap &getAbstractScopeDIEs() { + return AbstractLocalScopeDIEs; + } + + DenseMap> &getAbstractEntities() { + return AbstractEntities; + } + + auto &getFinalizedAbstractSubprograms() { + return FinalizedAbstractSubprograms; + } +}; + class DwarfFile { // Target of Dwarf emission, used for sizing of abbreviations. AsmPrinter *Asm; @@ -93,17 +224,7 @@ class DwarfFile { using LabelList = SmallVector; DenseMap ScopeLabels; - // Collection of abstract subprogram DIEs. - DenseMap AbstractLocalScopeDIEs; - DenseMap> AbstractEntities; - /// Keeps track of abstract subprograms to populate them only once. - // FIXME: merge creation and population of abstract scopes. - SmallPtrSet FinalizedAbstractSubprograms; - - /// 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. - DenseMap DITypeNodeToDieMap; + DwarfInfoHolder InfoHolder; public: DwarfFile(AsmPrinter *AP, StringRef Pref, BumpPtrAllocator &DA); @@ -171,25 +292,7 @@ class DwarfFile { return ScopeLabels; } - DenseMap &getAbstractScopeDIEs() { - return AbstractLocalScopeDIEs; - } - - DenseMap> &getAbstractEntities() { - return AbstractEntities; - } - - auto &getFinalizedAbstractSubprograms() { - return FinalizedAbstractSubprograms; - } - - void insertDIE(const MDNode *TypeMD, DIE *Die) { - DITypeNodeToDieMap.insert(std::make_pair(TypeMD, Die)); - } - - DIE *getDIE(const MDNode *TypeMD) { - return DITypeNodeToDieMap.lookup(TypeMD); - } + DwarfInfoHolder &getDIEs() { return InfoHolder; } }; } // end namespace llvm diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp index aa078f3f81d49..73579e41605e4 100644 --- a/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp @@ -188,28 +188,16 @@ bool DwarfUnit::isShareableAcrossCUs(const DINode *D) const { // together. if (isDwoUnit() && !DD->shareAcrossDWOCUs()) return false; - return (isa(D) || - (isa(D) && !cast(D)->isDefinition())) && - !DD->generateTypeUnits(); -} - -DIE *DwarfUnit::getDIE(const DINode *D) const { - if (isShareableAcrossCUs(D)) - return DU->getDIE(D); - return MDNodeToDieMap.lookup(D); + return !D || ((isa(D) || (isa(D) && + !cast(D)->isDefinition())) && + !DD->generateTypeUnits()); } void DwarfUnit::insertDIE(const DINode *Desc, DIE *D) { - if (isShareableAcrossCUs(Desc)) { - DU->insertDIE(Desc, D); - return; - } - MDNodeToDieMap.insert(std::make_pair(Desc, D)); + getDIEs(Desc).insertDIE(Desc, D); } -void DwarfUnit::insertDIE(DIE *D) { - MDNodeToDieMap.insert(std::make_pair(nullptr, D)); -} +void DwarfUnit::insertDIE(DIE *D) { InfoHolder.insertDIE(D); } void DwarfUnit::addFlag(DIE &Die, dwarf::Attribute Attribute) { if (DD->getDwarfVersion() >= 4) @@ -803,7 +791,7 @@ void DwarfUnit::constructTypeDIE(DIE &Buffer, const DIStringType *STy) { addString(Buffer, dwarf::DW_AT_name, Name); if (DIVariable *Var = STy->getStringLength()) { - if (auto *VarDIE = getDIE(Var)) + if (auto *VarDIE = getDIEs(Var).getVariableDIE(Var)) addDIEEntry(Buffer, dwarf::DW_AT_string_length, *VarDIE); } else if (DIExpression *Expr = STy->getStringLengthExp()) { DIELoc *Loc = new (DIEValueAllocator) DIELoc; @@ -1122,8 +1110,8 @@ void DwarfUnit::constructTypeDIE(DIE &Buffer, const DICompositeType *CTy) { constructTypeDIE(VariantPart, Composite); } } else if (Tag == dwarf::DW_TAG_namelist) { - auto *Var = dyn_cast(Element); - auto *VarDIE = getDIE(Var); + auto *Var = dyn_cast(Element); + auto *VarDIE = getDIEs(Var).getVariableDIE(Var); if (VarDIE) { DIE &ItemDie = createAndAddDIE(dwarf::DW_TAG_namelist_item, Buffer); addDIEEntry(ItemDie, dwarf::DW_AT_namelist_item, *VarDIE); @@ -1185,7 +1173,7 @@ void DwarfUnit::constructTypeDIE(DIE &Buffer, const DICompositeType *CTy) { Tag == dwarf::DW_TAG_class_type || Tag == dwarf::DW_TAG_structure_type || Tag == dwarf::DW_TAG_union_type) { if (auto *Var = dyn_cast_or_null(CTy->getRawSizeInBits())) { - if (auto *VarDIE = getDIE(Var)) + if (auto *VarDIE = getDIEs(Var).getVariableDIE(Var)) addDIEEntry(Buffer, dwarf::DW_AT_bit_size, *VarDIE); } else if (auto *Exp = dyn_cast_or_null(CTy->getRawSizeInBits())) { @@ -1416,7 +1404,8 @@ bool DwarfUnit::applySubprogramDefinitionAttributes(const DISubprogram *SP, StringRef LinkageName = SP->getLinkageName(); // Always emit linkage name for abstract subprograms. if (DeclLinkageName != LinkageName && - (DD->useAllLinkageNames() || DU->getAbstractScopeDIEs().lookup(SP))) + (DD->useAllLinkageNames() || + DU->getDIEs().getAbstractScopeDIEs().lookup(SP))) addLinkageName(SPDie, LinkageName); if (!DeclDie) @@ -1586,7 +1575,7 @@ void DwarfUnit::constructSubrangeDIE(DIE &DW_Subrange, const DISubrangeType *SR, auto AddBoundTypeEntry = [&](dwarf::Attribute Attr, DISubrangeType::BoundType Bound) -> void { if (auto *BV = dyn_cast_if_present(Bound)) { - if (auto *VarDIE = getDIE(BV)) + if (auto *VarDIE = getDIEs(BV).getVariableDIE(BV)) addDIEEntry(DW_Subrange, Attr, *VarDIE); } else if (auto *BE = dyn_cast_if_present(Bound)) { DIELoc *Loc = new (DIEValueAllocator) DIELoc; @@ -1628,7 +1617,7 @@ void DwarfUnit::constructSubrangeDIE(DIE &Buffer, const DISubrange *SR) { auto AddBoundTypeEntry = [&](dwarf::Attribute Attr, DISubrange::BoundType Bound) -> void { if (auto *BV = dyn_cast_if_present(Bound)) { - if (auto *VarDIE = getDIE(BV)) + if (auto *VarDIE = getDIEs(BV).getVariableDIE(BV)) addDIEEntry(DW_Subrange, Attr, *VarDIE); } else if (auto *BE = dyn_cast_if_present(Bound)) { DIELoc *Loc = new (DIEValueAllocator) DIELoc; @@ -1670,7 +1659,7 @@ void DwarfUnit::constructGenericSubrangeDIE(DIE &Buffer, auto AddBoundTypeEntry = [&](dwarf::Attribute Attr, DIGenericSubrange::BoundType Bound) -> void { if (auto *BV = dyn_cast_if_present(Bound)) { - if (auto *VarDIE = getDIE(BV)) + if (auto *VarDIE = getDIEs(BV).getVariableDIE(BV)) addDIEEntry(DwGenericSubrange, Attr, *VarDIE); } else if (auto *BE = dyn_cast_if_present(Bound)) { if (BE->isConstant() && @@ -1749,7 +1738,7 @@ void DwarfUnit::constructArrayTypeDIE(DIE &Buffer, const DICompositeType *CTy) { } if (DIVariable *Var = CTy->getDataLocation()) { - if (auto *VarDIE = getDIE(Var)) + if (auto *VarDIE = getDIEs(Var).getVariableDIE(Var)) addDIEEntry(Buffer, dwarf::DW_AT_data_location, *VarDIE); } else if (DIExpression *Expr = CTy->getDataLocationExp()) { DIELoc *Loc = new (DIEValueAllocator) DIELoc; @@ -1760,7 +1749,7 @@ void DwarfUnit::constructArrayTypeDIE(DIE &Buffer, const DICompositeType *CTy) { } if (DIVariable *Var = CTy->getAssociated()) { - if (auto *VarDIE = getDIE(Var)) + if (auto *VarDIE = getDIEs(Var).getVariableDIE(Var)) addDIEEntry(Buffer, dwarf::DW_AT_associated, *VarDIE); } else if (DIExpression *Expr = CTy->getAssociatedExp()) { DIELoc *Loc = new (DIEValueAllocator) DIELoc; @@ -1771,7 +1760,7 @@ void DwarfUnit::constructArrayTypeDIE(DIE &Buffer, const DICompositeType *CTy) { } if (DIVariable *Var = CTy->getAllocated()) { - if (auto *VarDIE = getDIE(Var)) + if (auto *VarDIE = getDIEs(Var).getVariableDIE(Var)) addDIEEntry(Buffer, dwarf::DW_AT_allocated, *VarDIE); } else if (DIExpression *Expr = CTy->getAllocatedExp()) { DIELoc *Loc = new (DIEValueAllocator) DIELoc; @@ -1896,7 +1885,7 @@ DIE &DwarfUnit::constructMemberDIE(DIE &Buffer, const DIDerivedType *DT) { if (DT->getRawSizeInBits() == nullptr) { // No size, just ignore. } else if (auto *Var = dyn_cast(DT->getRawSizeInBits())) { - if (auto *VarDIE = getDIE(Var)) + if (auto *VarDIE = getDIEs(Var).getVariableDIE(Var)) addDIEEntry(MemberDie, dwarf::DW_AT_bit_size, *VarDIE); } else if (auto *Exp = dyn_cast(DT->getRawSizeInBits())) { DIELoc *Loc = new (DIEValueAllocator) DIELoc; @@ -1921,7 +1910,7 @@ DIE &DwarfUnit::constructMemberDIE(DIE &Buffer, const DIDerivedType *DT) { // See https://dwarfstd.org/issues/250501.1.html if (auto *Var = dyn_cast_or_null(DT->getRawOffsetInBits())) { if (!Asm->TM.Options.DebugStrictDwarf || DD->getDwarfVersion() >= 6) { - if (auto *VarDIE = getDIE(Var)) + if (auto *VarDIE = getDIEs(Var).getVariableDIE(Var)) addDIEEntry(MemberDie, dwarf::DW_AT_data_bit_offset, *VarDIE); } } else if (auto *Expr = diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.h b/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.h index 9288d7edbf156..6c297e1d0667f 100644 --- a/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.h +++ b/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.h @@ -61,7 +61,7 @@ class DwarfUnit : public DIEUnit { /// Tracks the mapping of unit level debug information variables to debug /// information entries. - DenseMap MDNodeToDieMap; + DwarfInfoHolder InfoHolder; /// A list of all the DIEBlocks in use. std::vector DIEBlocks; @@ -139,7 +139,7 @@ class DwarfUnit : public DIEUnit { /// We delegate the request to DwarfDebug when the MDNode can be part of the /// type system, since DIEs for the type system can be shared across CUs and /// the mappings are kept in DwarfDebug. - DIE *getDIE(const DINode *D) const; + DIE *getDIE(const DINode *D) const { return getDIEs(D).getDIE(D); } /// Returns a fresh newly allocated DIELoc. DIELoc *getDIELoc() { return new (DIEValueAllocator) DIELoc; } @@ -153,6 +153,18 @@ class DwarfUnit : public DIEUnit { void insertDIE(DIE *D); + const DwarfInfoHolder &getDIEs(const DINode *N) const { + if (isShareableAcrossCUs(N)) + return DU->getDIEs(); + + return InfoHolder; + } + + DwarfInfoHolder &getDIEs(const DINode *N) { + return const_cast( + const_cast(this)->getDIEs(N)); + } + /// Add a flag that is true to the DIE. void addFlag(DIE &Die, dwarf::Attribute Attribute); From 17c20a49cb9b9835ddbfa0b44e0ddd96e2530b8a Mon Sep 17 00:00:00 2001 From: Vladislav Dzhidzhoev Date: Fri, 10 Oct 2025 14:51:51 +0200 Subject: [PATCH 2/3] [DwarfDebug] Associate subprogram DIEs with their Functions Depends on: * https://github.com/llvm/llvm-project/pull/152680 With this change, DINodeInfoHolder is used to store abstract and concrete out-of-line subprogram DIEs in DwarfInfoHolder. Every definition subprogram DIE is associated with a corresponding llvm::Function (declaration subprograms are associated with nullptr). When a concrete subprogram DIE is queried via `getOrCreateSubprogramDIE`, the corresponding llvm::Function should be provided. If none is provided: * DwarfUnit/DwarfTypeUnit falls back and returns any concrete DIE for the given DISubprogram, * DwarfCompileUnit is expected to return abstract DIE. This is a step to support attachment of a DISubprogram to multiple llvm::Functions (and to establish one-to-one-to-many correspondence between DISubprograms, abstract DIEs and function clones, and, later, to make the backend use uniquied DISubprograms). --- .../CodeGen/AsmPrinter/DwarfCompileUnit.cpp | 5 +-- .../lib/CodeGen/AsmPrinter/DwarfCompileUnit.h | 6 ++-- llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp | 14 ++++---- llvm/lib/CodeGen/AsmPrinter/DwarfDebug.h | 3 +- llvm/lib/CodeGen/AsmPrinter/DwarfFile.h | 29 ++++++++++------- llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp | 32 ++++++++++++++++--- llvm/lib/CodeGen/AsmPrinter/DwarfUnit.h | 5 +++ 7 files changed, 66 insertions(+), 28 deletions(-) diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp index bfcc01829bce0..ba8daf7662319 100644 --- a/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp @@ -1476,8 +1476,9 @@ DIE *DwarfCompileUnit::getOrCreateImportedEntityDIE( return IMDie; } -void DwarfCompileUnit::finishSubprogramDefinition(const DISubprogram *SP) { - DIE *D = getDIE(SP); +void DwarfCompileUnit::finishSubprogramDefinition(const DISubprogram *SP, + const Function *F) { + DIE *D = getDIEs(SP).getLocalScopes().getConcreteDIE(SP, F); if (DIE *AbsSPDIE = getAbstractScopeDIEs().lookup(SP)) { if (D) // If this subprogram has an abstract definition, reference that diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.h b/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.h index 902cd8cac8d78..b0dcc3e432a03 100644 --- a/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.h +++ b/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.h @@ -124,8 +124,8 @@ class DwarfCompileUnit final : public DwarfUnit { DwarfInfoHolder &getDIEs() { return getDIEs(nullptr); } - DenseMap &getAbstractScopeDIEs() { - return getDIEs().getAbstractScopeDIEs(); + DwarfInfoHolder::AbstractScopeMapT &getAbstractScopeDIEs() { + return getDIEs().getLocalScopes().getAbstractDIEs(); } DenseMap> &getAbstractEntities() { @@ -319,7 +319,7 @@ class DwarfCompileUnit final : public DwarfUnit { DIE *getOrCreateImportedEntityDIE(const DIImportedEntity *IE); DIE *constructImportedEntityDIE(const DIImportedEntity *IE); - void finishSubprogramDefinition(const DISubprogram *SP); + void finishSubprogramDefinition(const DISubprogram *SP, const Function *F); void finishEntityDefinition(const DbgEntity *Entity); void attachLexicalScopesAbstractOrigins(); diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp index 2080b592d2fb8..5aa8b932facdc 100644 --- a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp @@ -503,7 +503,7 @@ void DwarfDebug::addSubprogramNames( // that name. if (LinkageName != "" && SP->getName() != LinkageName && (useAllLinkageNames() || - InfoHolder.getDIEs().getAbstractScopeDIEs().lookup(SP))) + InfoHolder.getDIEs().getLocalScopes().getAbstractDIEs().lookup(SP))) addAccelName(Unit, NameTableKind, LinkageName, Die); // If this is an Objective-C selector name add it to the ObjC accelerator @@ -1264,11 +1264,13 @@ void DwarfDebug::finishEntityDefinitions() { } void DwarfDebug::finishSubprogramDefinitions() { - for (const DISubprogram *SP : ProcessedSPNodes) { + for (auto SPF : ProcessedSPNodes) { + const DISubprogram *SP = SPF.first; assert(SP->getUnit()->getEmissionKind() != DICompileUnit::NoDebug); - forBothCUs( - getOrCreateDwarfCompileUnit(SP->getUnit()), - [&](DwarfCompileUnit &CU) { CU.finishSubprogramDefinition(SP); }); + forBothCUs(getOrCreateDwarfCompileUnit(SP->getUnit()), + [&](DwarfCompileUnit &CU) { + CU.finishSubprogramDefinition(SP, SPF.second); + }); } } @@ -2785,7 +2787,7 @@ void DwarfDebug::endFunctionImpl(const MachineFunction *MF) { constructAbstractSubprogramScopeDIE(TheCU, AScope); } - ProcessedSPNodes.insert(SP); + ProcessedSPNodes.insert(std::make_pair(SP, &F)); DIE &ScopeDIE = TheCU.constructSubprogramScopeDIE(SP, F, FnScope, FunctionLineTableLabel); if (auto *SkelCU = TheCU.getSkeleton()) diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.h b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.h index 1a1b28a6fc035..42ac225e2d17e 100644 --- a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.h +++ b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.h @@ -373,7 +373,8 @@ class DwarfDebug : public DebugHandlerBase { /// This is a collection of subprogram MDNodes that are processed to /// create DIEs. - SmallSetVector ProcessedSPNodes; + SmallSetVector, 16> + ProcessedSPNodes; /// Map function-local imported entities to their parent local scope /// (either DILexicalBlock or DISubprogram) for a processed function diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfFile.h b/llvm/lib/CodeGen/AsmPrinter/DwarfFile.h index 18388c3b7c1e9..94d4e5f0b7f05 100644 --- a/llvm/lib/CodeGen/AsmPrinter/DwarfFile.h +++ b/llvm/lib/CodeGen/AsmPrinter/DwarfFile.h @@ -120,13 +120,19 @@ template class DINodeInfoHolder { /// These DIEs can be shared across CUs, that is why we keep the map here /// instead of in DwarfCompileUnit. class DwarfInfoHolder { +public: + using LocalScopeHolderT = DINodeInfoHolder; + using AbstractScopeMapT = LocalScopeHolderT::AbstractMapT; + +private: /// DIEs of local DbgVariables. DINodeInfoHolder LVHolder; /// DIEs of labels. DINodeInfoHolder LabelHolder; DenseMap> AbstractEntities; - // List of abstract local scopes (either DISubprogram or DILexicalBlock). - DenseMap AbstractLocalScopeDIEs; + /// DIEs of abstract local scopes and concrete non-inlined subprograms. + /// Inlined subprograms and concrete lexical blocks are not stored here. + LocalScopeHolderT LSHolder; /// Keeps track of abstract subprograms to populate them only once. // FIXME: merge creation and population of abstract scopes. SmallPtrSet FinalizedAbstractSubprograms; @@ -136,11 +142,12 @@ class DwarfInfoHolder { public: void insertDIE(const DINode *N, DIE *Die) { - assert((!isa(N) && !isa(N)) && + assert((!isa(N) && !isa(N) && + !isa(N)) && "Use getLabels().insertDIE() for labels or getLVs().insertDIE() for " - "local variables"); + "local variables, or getSubprogram().insertDIE() for subprograms."); auto [_, Inserted] = MDNodeToDieMap.try_emplace(N, Die); - assert((Inserted || isa(N) || isa(N)) && + assert((Inserted || isa(N)) && "DIE for this DINode has already been added"); } @@ -148,9 +155,10 @@ class DwarfInfoHolder { DIE *getDIE(const DINode *N) const { DIE *D = MDNodeToDieMap.lookup(N); - assert((!D || (!isa(N) && !isa(N))) && + assert((!D || (!isa(N) && !isa(N) && + !isa(N))) && "Use getLabels().getDIE() for labels or getLVs().getDIE() for " - "local variables"); + "local variables, or getLocalScopes().getDIE() for local scopes."); return D; } @@ -160,6 +168,9 @@ class DwarfInfoHolder { auto &getLabels() { return LabelHolder; } auto &getLabels() const { return LabelHolder; } + auto &getLocalScopes() { return LSHolder; } + auto &getLocalScopes() const { return LSHolder; } + /// For a global variable, returns DIE of the variable. /// /// For a local variable, returns abstract DIE of the variable. @@ -171,10 +182,6 @@ class DwarfInfoHolder { return getDIE(V); } - DenseMap &getAbstractScopeDIEs() { - return AbstractLocalScopeDIEs; - } - DenseMap> &getAbstractEntities() { return AbstractEntities; } diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp index 73579e41605e4..b0d0fa147b3fc 100644 --- a/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp @@ -412,6 +412,14 @@ DIE &DwarfUnit::createAndAddDIE(dwarf::Tag Tag, DIE &Parent, const DINode *N) { return Die; } +DIE &DwarfUnit::createAndAddSubprogramDIE(DIE &Parent, const DISubprogram *SP, + const Function *F) { + DIE &Die = + Parent.addChild(DIE::get(DIEValueAllocator, dwarf::DW_TAG_subprogram)); + getDIEs(SP).getLocalScopes().insertConcreteDIE(SP, F, &Die); + return Die; +} + void DwarfUnit::addBlock(DIE &Die, dwarf::Attribute Attribute, DIELoc *Loc) { Loc->computeSize(Asm->getDwarfFormParams()); DIELocs.push_back(Loc); // Memoize so we can call the destructor later on. @@ -1328,6 +1336,19 @@ DIE *DwarfUnit::getOrCreateModule(const DIModule *M) { return &MDie; } +DIE *DwarfUnit::getExistingSubprogramDIE(const DISubprogram *SP, + const Function *F) const { + if (!F) { + if (DIE *SPDie = getDIEs(SP).getLocalScopes().getAnyConcreteDIE(SP)) + return SPDie; + } else { + if (DIE *SPDie = getDIEs(SP).getLocalScopes().getConcreteDIE(SP, F)) + return SPDie; + } + + return nullptr; +} + DIE *DwarfUnit::getOrCreateSubprogramDIE(const DISubprogram *SP, const Function *FnHint, bool Minimal) { // Construct the context before querying for the existence of the DIE in case @@ -1336,7 +1357,7 @@ DIE *DwarfUnit::getOrCreateSubprogramDIE(const DISubprogram *SP, DIE *ContextDIE = getOrCreateSubprogramContextDIE(SP, shouldPlaceInUnitDIE(SP, Minimal)); - if (DIE *SPDie = getDIE(SP)) + if (DIE *SPDie = getExistingSubprogramDIE(SP, FnHint)) return SPDie; if (auto *SPDecl = SP->getDeclaration()) { @@ -1348,13 +1369,13 @@ DIE *DwarfUnit::getOrCreateSubprogramDIE(const DISubprogram *SP, // FIXME: Should the creation of definition subprogram DIE during // the creation of declaration subprogram DIE be allowed? // See https://github.com/llvm/llvm-project/pull/154636. - if (DIE *SPDie = getDIE(SP)) + if (DIE *SPDie = getExistingSubprogramDIE(SP, FnHint)) return SPDie; } } // DW_TAG_inlined_subroutine may refer to this DIE. - DIE &SPDie = createAndAddDIE(dwarf::DW_TAG_subprogram, *ContextDIE, SP); + DIE &SPDie = createAndAddSubprogramDIE(*ContextDIE, SP, FnHint); // Stop here and fill this in later, depending on whether or not this // subprogram turns out to have inlined instances or not. @@ -1380,7 +1401,8 @@ bool DwarfUnit::applySubprogramDefinitionAttributes(const DISubprogram *SP, if (DefinitionArgs[0] != nullptr && DeclArgs[0] != DefinitionArgs[0]) addType(SPDie, DefinitionArgs[0]); - DeclDie = getDIE(SPDecl); + DeclDie = + getDIEs(SPDecl).getLocalScopes().getConcreteDIE(SPDecl, nullptr); assert(DeclDie && "This DIE should've already been constructed when the " "definition DIE was created in " "getOrCreateSubprogramDIE"); @@ -1405,7 +1427,7 @@ bool DwarfUnit::applySubprogramDefinitionAttributes(const DISubprogram *SP, // Always emit linkage name for abstract subprograms. if (DeclLinkageName != LinkageName && (DD->useAllLinkageNames() || - DU->getDIEs().getAbstractScopeDIEs().lookup(SP))) + DU->getDIEs().getLocalScopes().getAbstractDIEs().lookup(SP))) addLinkageName(SPDie, LinkageName); if (!DeclDie) diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.h b/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.h index 6c297e1d0667f..626e08964a875 100644 --- a/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.h +++ b/llvm/lib/CodeGen/AsmPrinter/DwarfUnit.h @@ -298,6 +298,8 @@ class DwarfUnit : public DIEUnit { /// Create a DIE with the given Tag, add the DIE to its parent, and /// call insertDIE if MD is not null. DIE &createAndAddDIE(dwarf::Tag Tag, DIE &Parent, const DINode *N = nullptr); + DIE &createAndAddSubprogramDIE(DIE &Parent, const DISubprogram *SP, + const Function *F); bool useSegmentedStringOffsetsTable() const { return DD->useSegmentedStringOffsetsTable(); @@ -399,6 +401,9 @@ class DwarfUnit : public DIEUnit { void constructTemplateValueParameterDIE(DIE &Buffer, const DITemplateValueParameter *TVP); + DIE *getExistingSubprogramDIE(const DISubprogram *SP, + const Function *FnHint) const; + /// Return the default lower bound for an array. /// /// If the DWARF version doesn't handle the language, return -1. From 24f2d676b58b2141e4f3a697f973629196502723 Mon Sep 17 00:00:00 2001 From: Vladislav Dzhidzhoev Date: Sun, 6 Jul 2025 18:15:18 +0200 Subject: [PATCH 3/3] [DebugInfo][DwarfDebug][CodeView] Allow DISubprogram to be attached to multiple Functions Depends on: * https://github.com/llvm/llvm-project/pull/152680 * https://github.com/llvm/llvm-project/pull/162852 In https://github.com/llvm/llvm-project/pull/75385 (and the following tries), an attempt was made, to support attaching local types to DILocalScopes, and to store function local types in DISubprogram's `retainedNodes:` field. That patch failed to land due to issues arising during LTO process. If two definition DISubprograms from different compile units represent, essentially, the same source code function, and have common local DICompositeType, and if this DICompositeType is uniqued (due to ODRUniquingDebugTypes feature), the subprograms end up having wrong retainedNodes list/scoping relationship. To tackle this issue, in https://github.com/llvm/llvm-project/pull/142166, it was proposed to force-unique all DISubporgrams even if they don't contain odr-uniqued types (https://github.com/llvm/llvm-project/pull/142166#issuecomment-2981729002). It should establish one-to-one-to-many relationship between DISubprograms, abstract DIEs and function clones (from different CUs, in case of LTO). To implement that, AsmPrinter should support correct emission of debug info for DISubprograms attached to multiple functions. This is the goal of this commit. Here, LexicalScope's function map is changed to multimap between DISubprogram and (possible multiple) functions attached to it. LexicalScope is modified to create an abstract scope for a DISubprogram having multiple lllvm::Function attachments. `DwarfCompileUnit::getOrCreateSubprogramDIE` can recognize the case of DISubprogram attached to multiple Functions, and return abstract DIE when needed. CodeViewDebug is adopted as well. UDTs are ensured to be emmited properly in the cases that are addressed here. Please let me know if more changes to CodeView needed, as I'm not very familiar with the format. --- llvm/include/llvm/CodeGen/LexicalScopes.h | 23 +++- llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp | 23 +++- llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.h | 7 +- .../CodeGen/AsmPrinter/DwarfCompileUnit.cpp | 6 +- llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp | 4 +- llvm/lib/CodeGen/LexicalScopes.cpp | 15 +- llvm/lib/IR/Verifier.cpp | 9 -- llvm/test/DebugInfo/COFF/shared-sp.ll | 129 ++++++++++++++++++ llvm/test/DebugInfo/COFF/udts-complete.ll | 4 +- llvm/test/DebugInfo/COFF/udts-fixpoint.ll | 4 +- llvm/test/DebugInfo/X86/namelist1.ll | 5 +- llvm/test/DebugInfo/X86/shared-empty-sp.ll | 48 +++++++ llvm/test/DebugInfo/X86/shared-sp.ll | 98 +++++++++++++ llvm/test/Verifier/metadata-function-dbg.ll | 10 -- llvm/unittests/CodeGen/LexicalScopesTest.cpp | 117 +++++++++++++++- 15 files changed, 454 insertions(+), 48 deletions(-) create mode 100644 llvm/test/DebugInfo/COFF/shared-sp.ll create mode 100644 llvm/test/DebugInfo/X86/shared-empty-sp.ll create mode 100644 llvm/test/DebugInfo/X86/shared-sp.ll diff --git a/llvm/include/llvm/CodeGen/LexicalScopes.h b/llvm/include/llvm/CodeGen/LexicalScopes.h index 993df54c05ad5..848a4a4ce2433 100644 --- a/llvm/include/llvm/CodeGen/LexicalScopes.h +++ b/llvm/include/llvm/CodeGen/LexicalScopes.h @@ -199,12 +199,18 @@ class LexicalScopes { return I != LexicalScopeMap.end() ? &I->second : nullptr; } + bool currentFunctionHasInlinedScopes() { + return !InlinedLexicalScopeMap.empty(); + } + /// Find or create an abstract lexical scope. LLVM_ABI LexicalScope *getOrCreateAbstractScope(const DILocalScope *Scope); - /// Get function to which the given subprogram is attached, if exists. - const Function *getFunction(const DISubprogram *SP) const { - return FunctionMap.lookup(SP); + /// Get functions to which the given subprogram is attached. + const SmallPtrSet * + getFunctions(const DISubprogram *SP) const { + auto I = FunctionMap.find(SP); + return I == FunctionMap.end() ? nullptr : &I->second; } private: @@ -218,6 +224,15 @@ class LexicalScopes { : nullptr; } + /// Create abstract lexical scope for local scopes used by multiple + /// functions, if needed. + void ensureAbstractLexicalScopeIsCreated(const DILocalScope *Scope) { + const DISubprogram *SP = Scope->getSubprogram(); + const auto *Fns = getFunctions(SP); + if (!Fns || Fns->size() != 1) + getOrCreateAbstractScope(Scope); + } + /// Find or create a regular lexical scope. LexicalScope *getOrCreateRegularScope(const DILocalScope *Scope); @@ -237,7 +252,7 @@ class LexicalScopes { const MachineFunction *MF = nullptr; /// Mapping between DISubprograms and IR functions. - DenseMap FunctionMap; + DenseMap> FunctionMap; /// Tracks the scopes in the current function. // Use an unordered_map to ensure value pointer validity over insertion. diff --git a/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp b/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp index 12d749ce56f06..9bc395fcf1e79 100644 --- a/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp @@ -1658,9 +1658,9 @@ void CodeViewDebug::addToUDTs(const DIType *Ty) { formatNestedName(ParentScopeNames, getPrettyScopeName(Ty)); if (ClosestSubprogram == nullptr) { - GlobalUDTs.emplace_back(std::move(FullyQualifiedName), Ty); + GlobalUDTs[Ty] = std::move(FullyQualifiedName); } else if (ClosestSubprogram == CurrentSubprogram) { - LocalUDTs.emplace_back(std::move(FullyQualifiedName), Ty); + LocalUDTs[Ty] = std::move(FullyQualifiedName); } // TODO: What if the ClosestSubprogram is neither null or the current @@ -2698,6 +2698,12 @@ TypeIndex CodeViewDebug::getTypeIndex(const DIType *Ty, const DIType *ClassTy) { if (!Ty) return TypeIndex::Void(); + if (Ty->getTag() == dwarf::DW_TAG_typedef) { + // Ensure that UDT is added even if the (local) type has been translated + // during processing of the previous function. + addToUDTs(Ty); + } + // Check if we've already translated this type. Don't try to do a // get-or-create style insertion that caches the hash lookup across the // lowerType call. It will update the TypeIndices map. @@ -2787,6 +2793,10 @@ TypeIndex CodeViewDebug::getCompleteTypeIndex(const DIType *Ty) { return FwdDeclTI; } + // Ensure that UDT is added even if the (local) type has been translated + // during processing of the previous function. + addToUDTs(CTy); + // Check if we've already translated the complete record type. // Insert the type with a null TypeIndex to signify that the type is currently // being lowered. @@ -3217,20 +3227,19 @@ void CodeViewDebug::emitEndSymbolRecord(SymbolKind EndKind) { OS.emitInt16(uint16_t(EndKind)); // Record Kind } -void CodeViewDebug::emitDebugInfoForUDTs( - const std::vector> &UDTs) { +template +void CodeViewDebug::emitDebugInfoForUDTs(Range &&UDTs) { #ifndef NDEBUG size_t OriginalSize = UDTs.size(); #endif - for (const auto &UDT : UDTs) { - const DIType *T = UDT.second; + for (const auto &[T, Name] : UDTs) { assert(shouldEmitUdt(T)); MCSymbol *UDTRecordEnd = beginSymbolRecord(SymbolKind::S_UDT); OS.AddComment("Type"); OS.emitInt32(getCompleteTypeIndex(T).getIndex()); assert(OriginalSize == UDTs.size() && "getCompleteTypeIndex found new UDTs!"); - emitNullTerminatedSymbolName(OS, UDT.first); + emitNullTerminatedSymbolName(OS, Name); endSymbolRecord(UDTRecordEnd); } } diff --git a/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.h b/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.h index c2b878e52e1c3..f4c5a5bb1625c 100644 --- a/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.h +++ b/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.h @@ -306,8 +306,8 @@ class LLVM_LIBRARY_VISIBILITY CodeViewDebug : public DebugHandlerBase { // The UDTs we have seen while processing types; each entry is a pair of type // index and type name. - std::vector> LocalUDTs; - std::vector> GlobalUDTs; + MapVector LocalUDTs; + MapVector GlobalUDTs; using FileToFilepathMapTy = std::map; FileToFilepathMapTy FileToFilepathMap; @@ -352,8 +352,7 @@ class LLVM_LIBRARY_VISIBILITY CodeViewDebug : public DebugHandlerBase { void emitDebugInfoForRetainedTypes(); - void emitDebugInfoForUDTs( - const std::vector> &UDTs); + template void emitDebugInfoForUDTs(Range &&UDTs); void collectDebugInfoForGlobals(); void emitDebugInfoForGlobals(); diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp index ba8daf7662319..a801b1e400ad9 100644 --- a/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp @@ -1839,14 +1839,16 @@ DIE *DwarfCompileUnit::getOrCreateSubprogramDIE(const DISubprogram *SP, const Function *F, bool Minimal) { if (!F && SP->isDefinition()) { - F = DD->getLexicalScopes().getFunction(SP); + const auto *Fs = DD->getLexicalScopes().getFunctions(SP); - if (!F) { + if (!Fs || Fs->size() != 1) { // SP may belong to another CU. Determine the CU similarly // to DwarfDebug::constructAbstractSubprogramScopeDIE. return &DD->getOrCreateAbstractSubprogramCU(SP, *this) .getOrCreateAbstractSubprogramDIE(SP); } + + F = *Fs->begin(); } return DwarfUnit::getOrCreateSubprogramDIE(SP, F, Minimal); diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp index 5aa8b932facdc..865e7b7d6d376 100644 --- a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp @@ -2750,7 +2750,7 @@ void DwarfDebug::endFunctionImpl(const MachineFunction *MF) { // is still needed as we need its source location. if (!TheCU.getCUNode()->getDebugInfoForProfiling() && TheCU.getCUNode()->getEmissionKind() == DICompileUnit::LineTablesOnly && - LScopes.getAbstractScopesList().empty() && !IsDarwin) { + !LScopes.currentFunctionHasInlinedScopes() && !IsDarwin) { for (const auto &R : Asm->MBBSectionRanges) addArangeLabel(SymbolCU(&TheCU, R.second.BeginLabel)); @@ -2791,7 +2791,7 @@ void DwarfDebug::endFunctionImpl(const MachineFunction *MF) { DIE &ScopeDIE = TheCU.constructSubprogramScopeDIE(SP, F, FnScope, FunctionLineTableLabel); if (auto *SkelCU = TheCU.getSkeleton()) - if (!LScopes.getAbstractScopesList().empty() && + if (LScopes.currentFunctionHasInlinedScopes() && TheCU.getCUNode()->getSplitDebugInlining()) SkelCU->constructSubprogramScopeDIE(SP, F, FnScope, FunctionLineTableLabel); diff --git a/llvm/lib/CodeGen/LexicalScopes.cpp b/llvm/lib/CodeGen/LexicalScopes.cpp index 9fc9ac9a66d41..49100fa153135 100644 --- a/llvm/lib/CodeGen/LexicalScopes.cpp +++ b/llvm/lib/CodeGen/LexicalScopes.cpp @@ -61,19 +61,28 @@ void LexicalScopes::initialize(const Module &M) { for (const Function &F : M) { DISubprogram *SP = F.getSubprogram(); if (SP && (!SP->getUnit() || !skipUnit(SP->getUnit()))) - FunctionMap[SP] = &F; + FunctionMap[SP].insert(&F); } } void LexicalScopes::scanFunction(const MachineFunction &Fn) { resetFunction(); // Don't attempt any lexical scope creation for a NoDebug compile unit. - if (skipUnit(Fn.getFunction().getSubprogram()->getUnit())) + const Function &IRFunc = Fn.getFunction(); + const DISubprogram *SP = IRFunc.getSubprogram(); + if (skipUnit(SP->getUnit())) return; + + // A new subprogram may be created during Codegen after module scan. + FunctionMap[SP].insert(&IRFunc); + MF = &Fn; SmallVector MIRanges; DenseMap MI2ScopeMap; extractLexicalScopes(MIRanges, MI2ScopeMap); + // If no scopes were extracted, abstract scope is still needed + // for a subprogram attached to multiple functions. + ensureAbstractLexicalScopeIsCreated(SP); if (CurrentFnLexicalScope) { constructScopeNest(CurrentFnLexicalScope); assignInstructionRanges(MIRanges, MI2ScopeMap); @@ -167,6 +176,8 @@ LexicalScope *LexicalScopes::getOrCreateLexicalScope(const DILocalScope *Scope, return getOrCreateInlinedScope(Scope, IA); } + // Parent DISubprogram may be attached by multiple functions. + ensureAbstractLexicalScopeIsCreated(Scope); return getOrCreateRegularScope(Scope); } diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp index c9ff86b7df16b..f674ba5b820cc 100644 --- a/llvm/lib/IR/Verifier.cpp +++ b/llvm/lib/IR/Verifier.cpp @@ -336,9 +336,6 @@ class Verifier : public InstVisitor, VerifierSupport { /// Keep track of the metadata nodes that have been checked already. SmallPtrSet MDNodes; - /// Keep track which DISubprogram is attached to which function. - DenseMap DISubprogramAttachments; - /// Track all DICompileUnits visited. SmallPtrSet CUVisited; @@ -485,7 +482,6 @@ class Verifier : public InstVisitor, VerifierSupport { verifyCompileUnits(); verifyDeoptimizeCallingConvs(); - DISubprogramAttachments.clear(); return !Broken; } @@ -3145,11 +3141,6 @@ void Verifier::visitFunction(const Function &F) { "function definition may only have a distinct !dbg attachment", &F); - auto *SP = cast(I.second); - const Function *&AttachedTo = DISubprogramAttachments[SP]; - CheckDI(!AttachedTo || AttachedTo == &F, - "DISubprogram attached to more than one function", SP, &F); - AttachedTo = &F; AllowLocs = AreDebugLocsAllowed::Yes; break; } diff --git a/llvm/test/DebugInfo/COFF/shared-sp.ll b/llvm/test/DebugInfo/COFF/shared-sp.ll new file mode 100644 index 0000000000000..5e985a5a9d07e --- /dev/null +++ b/llvm/test/DebugInfo/COFF/shared-sp.ll @@ -0,0 +1,129 @@ +; RUN: llc -filetype=obj < %s -o - 2>&1 | llvm-readobj - --codeview | FileCheck %s + +; Check that when DISubprogram is attached to two functions, CodeView is +; produced correctly. + +; CHECK: Subsection [ +; CHECK: GlobalProcIdSym { +; CHECK: Kind: S_GPROC32_ID (0x1147) +; CHECK: FunctionType: foo (0x1002) +; CHECK: CodeOffset: foo+0x0 +; CHECK: Flags [ (0x80) +; CHECK: HasOptimizedDebugInfo (0x80) +; CHECK: ] +; CHECK: DisplayName: foo +; CHECK: LinkageName: foo +; CHECK: } +; CHECK: LocalSym { +; CHECK: Kind: S_LOCAL (0x113E) +; CHECK: Type: int (0x74) +; CHECK: Flags [ (0x0) +; CHECK: ] +; CHECK: VarName: a +; CHECK: } +; CHECK: LocalSym { +; CHECK: Kind: S_LOCAL (0x113E) +; CHECK: Type: foo::bar (0x1005) +; CHECK: Flags [ (0x0) +; CHECK: ] +; CHECK: VarName: c +; CHECK: } +; CHECK: UDTSym { +; CHECK: Kind: S_UDT (0x1108) +; CHECK: Type: foo::bar (0x1005) +; CHECK: UDTName: foo::bar +; CHECK: } +; CHECK: ] +; CHECK: Subsection [ +; CHECK: GlobalProcIdSym { +; CHECK: Kind: S_GPROC32_ID (0x1147) +; CHECK: FunctionType: foo (0x1002) +; CHECK: CodeOffset: foo_clone+0x0 +; CHECK: Flags [ (0x80) +; CHECK: HasOptimizedDebugInfo (0x80) +; CHECK: ] +; CHECK: DisplayName: foo +; CHECK: LinkageName: foo_clone +; CHECK: } +; CHECK: LocalSym { +; CHECK: Kind: S_LOCAL (0x113E) +; CHECK: Type: int (0x74) +; CHECK: Flags [ (0x0) +; CHECK: ] +; CHECK: VarName: a +; CHECK: } +; CHECK: LocalSym { +; CHECK: Kind: S_LOCAL (0x113E) +; CHECK: Type: foo::bar (0x1005) +; CHECK: Flags [ (0x0) +; CHECK: ] +; CHECK: VarName: c +; CHECK: } +; CHECK: UDTSym { +; CHECK: Kind: S_UDT (0x1108) +; CHECK: Type: foo::bar (0x1005) +; CHECK: UDTName: foo::bar +; CHECK: } +; CHECK: ] + +; ModuleID = 'shared-sp.ll' +target datalayout = "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-windows-msvc19.29.30133" + +!0 = distinct !DISubprogram(name: "foo", scope: !1, file: !1, line: 1, type: !2, scopeLine: 1, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !4, retainedNodes: !{}) +!1 = !DIFile(filename: "example.c", directory: "/") +!2 = !DISubroutineType(types: !3) +!3 = !{!5} +!4 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, emissionKind: FullDebug) +!5 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) + +; Local variable. +!10 = !DILocalVariable(name: "a", scope: !0, file: !1, line: 2, type: !5) + +; DICompositeType local to foo. +!11 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "bar", scope: !0, file: !1, line: 2, size: 32, elements: !12) +!12 = !{!13} +!13 = !DIDerivedType(tag: DW_TAG_member, name: "m", scope: !11, file: !1, line: 2, baseType: !5, size: 32) + +; Local variable of type struct bar, local to foo. +!14 = !DILocalVariable(name: "c", scope: !0, file: !1, line: 2, type: !11) + +!101 = !DILocation(line: 2, column: 5, scope: !0) +!102 = !DILocation(line: 3, column: 1, scope: !0) +!103 = !DILocation(line: 2, column: 12, scope: !0) + +!llvm.dbg.cu = !{!4} +!llvm.module.flags = !{!29, !30} + +!29 = !{i32 2, !"CodeView", i32 1} +!30 = !{i32 2, !"Debug Info Version", i32 3} + +define i32 @foo() !dbg !0 { +entry: + ; Local variable 'a' debug info. + %a.addr = alloca i32, align 4, !dbg !101 + #dbg_declare(ptr %a.addr, !10, !DIExpression(), !101) + store i32 42, ptr %a.addr, align 4, !dbg !101 + + ; Local variable 'c' (struct bar) debug info. + %c.addr = alloca %struct.bar, align 4, !dbg !103 + #dbg_declare(ptr %c.addr, !14, !DIExpression(), !103) + + ret i32 42, !dbg !102 +} + +define i32 @foo_clone() !dbg !0 { +entry: + ; Local variable 'a' debug info. + %a.addr = alloca i32, align 4, !dbg !101 + #dbg_declare(ptr %a.addr, !10, !DIExpression(), !101) + store i32 42, ptr %a.addr, align 4, !dbg !101 + + ; Local variable 'c' (struct bar) debug info. + %c.addr = alloca %struct.bar, align 4, !dbg !103 + #dbg_declare(ptr %c.addr, !14, !DIExpression(), !103) + + ret i32 42, !dbg !102 +} + +%struct.bar = type { i32 } diff --git a/llvm/test/DebugInfo/COFF/udts-complete.ll b/llvm/test/DebugInfo/COFF/udts-complete.ll index e5ee8521ab489..50000c793ac29 100644 --- a/llvm/test/DebugInfo/COFF/udts-complete.ll +++ b/llvm/test/DebugInfo/COFF/udts-complete.ll @@ -33,10 +33,10 @@ ; CHECK: Mod 0000 | `.debug$S`: ; CHECK: 0 | S_GDATA32 [size = 20] `gv` ; CHECK: type = 0x1002 (Foo), addr = 0000:0000 -; CHECK: 0 | S_UDT [size = 12] `Bar` -; CHECK: original type = 0x1002 ; CHECK: 0 | S_UDT [size = 12] `Baz` ; CHECK: original type = 0x1002 +; CHECK: 0 | S_UDT [size = 12] `Bar` +; CHECK: original type = 0x1002 ; CHECK: 0 | S_UDT [size = 12] `Foo` ; CHECK: original type = 0x1002 diff --git a/llvm/test/DebugInfo/COFF/udts-fixpoint.ll b/llvm/test/DebugInfo/COFF/udts-fixpoint.ll index c070135edfda8..294d0afc0481d 100644 --- a/llvm/test/DebugInfo/COFF/udts-fixpoint.ll +++ b/llvm/test/DebugInfo/COFF/udts-fixpoint.ll @@ -16,11 +16,11 @@ ; Check that there are only two typedefs, a and c. ; CHECK: .short 4360 # Record kind: S_UDT ; CHECK: .long {{.*}} # Type -; CHECK: .asciz "a" +; CHECK: .asciz "c" ; CHECK: .p2align 2 ; CHECK: .short 4360 # Record kind: S_UDT ; CHECK: .long {{.*}} # Type -; CHECK: .asciz "c" +; CHECK: .asciz "a" ; CHECK: .p2align 2 ; No other S_UDTs. ; CHECK-NOT: S_UDT diff --git a/llvm/test/DebugInfo/X86/namelist1.ll b/llvm/test/DebugInfo/X86/namelist1.ll index 92c0777f3422e..22d0f3631e8aa 100644 --- a/llvm/test/DebugInfo/X86/namelist1.ll +++ b/llvm/test/DebugInfo/X86/namelist1.ll @@ -2,7 +2,10 @@ ; 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: llvm-dwarfdump %t.o | FileCheck %s --implicit-check-not=DW_TAG_subprogram +; +; Ensure that a single DW_TAG_subprogram is produced. +; CHECK: DW_TAG_subprogram ; ; CHECK: [[ITEM1:0x.+]]: DW_TAG_variable ; CHECK: DW_AT_name ("a") diff --git a/llvm/test/DebugInfo/X86/shared-empty-sp.ll b/llvm/test/DebugInfo/X86/shared-empty-sp.ll new file mode 100644 index 0000000000000..925efa2a22d0c --- /dev/null +++ b/llvm/test/DebugInfo/X86/shared-empty-sp.ll @@ -0,0 +1,48 @@ +; RUN: llc -filetype=obj < %s -o %t 2>&1 | FileCheck --allow-empty --implicit-check-not='warning:' %s +; RUN: llvm-dwarfdump -verify %t +; RUN: llvm-dwarfdump %t | FileCheck %s --check-prefix=DWARF --implicit-check-not=DW_TAG + +; DWARF: DW_TAG_compile_unit + +; Abstract subprogram. +; DWARF: [[FOO:.*]]: DW_TAG_subprogram +; DWARF: DW_AT_name ("foo" +; DWARF: DW_AT_inline (DW_INL_inlined) + +; Concrete subprogram. +; DWARF: DW_TAG_subprogram +; DWARF: DW_AT_abstract_origin ([[FOO]] + +; Concrete subprogram. +; DWARF: DW_TAG_subprogram +; DWARF: DW_AT_abstract_origin ([[FOO]] + +; Check that when DISubprogram is attached to two empty functions (with no lexical scopes), +; they get different DW_TAG_subprograms. + +; ModuleID = 'example' + +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64--linux" + +!0 = distinct !DISubprogram(name: "foo", scope: !1, file: !1, line: 1, type: !2, scopeLine: 1, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !3, retainedNodes: !{}) +!1 = !DIFile(filename: "example.c", directory: "/") +!2 = !DISubroutineType(types: !4) +!4 = !{} + +!3 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, emissionKind: FullDebug) + +!llvm.dbg.cu = !{!3} +!llvm.module.flags = !{!30} + +!30 = !{i32 1, !"Debug Info Version", i32 3} + +define void @foo() !dbg !0 { +entry: + ret void +} + +define void @foo_clone() !dbg !0 { +entry: + ret void +} diff --git a/llvm/test/DebugInfo/X86/shared-sp.ll b/llvm/test/DebugInfo/X86/shared-sp.ll new file mode 100644 index 0000000000000..34a6186d79e69 --- /dev/null +++ b/llvm/test/DebugInfo/X86/shared-sp.ll @@ -0,0 +1,98 @@ +; RUN: llc -filetype=obj < %s -o %t 2>&1 | FileCheck --allow-empty --implicit-check-not='warning:' %s +; RUN: llvm-dwarfdump -verify %t +; RUN: llvm-dwarfdump %t | FileCheck %s --check-prefix=DWARF --implicit-check-not=DW_TAG + +; Check that when DISubprogram is attached to two functions, DWARF is produced +; correctly. + +; DWARF: DW_TAG_compile_unit + +; Abstract subprogram. +; DWARF: [[FOO:.*]]: DW_TAG_subprogram +; DWARF: DW_AT_name ("foo" +; DWARF: DW_AT_inline (DW_INL_inlined) +; DWARF: [[A:.*]]: DW_TAG_variable +; DWARF: DW_AT_name ("a" +; DWARF: DW_TAG_structure_type +; DWARF: DW_TAG_member +; DWARF: [[C:.*]]: DW_TAG_variable +; DWARF: DW_AT_name ("c" + +; DWARF: DW_TAG_base_type + +; Concrete subprogram. +; DWARF: DW_TAG_subprogram +; DWARF: DW_AT_abstract_origin ([[FOO]] +; DWARF: DW_TAG_variable +; DWARF: DW_AT_abstract_origin ([[A]] +; DWARF: DW_TAG_variable +; DWARF: DW_AT_abstract_origin ([[C]] + +; Concrete subprogram. +; DWARF: DW_TAG_subprogram +; DWARF: DW_AT_abstract_origin ([[FOO]] +; DWARF: DW_TAG_variable +; DWARF: DW_AT_abstract_origin ([[A]] +; DWARF: DW_TAG_variable +; DWARF: DW_AT_abstract_origin ([[C]] + +; ModuleID = 'shared-sp.ll' +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64--linux" + +!0 = distinct !DISubprogram(name: "foo", scope: !1, file: !1, line: 1, type: !2, scopeLine: 1, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !4, retainedNodes: !{}) +!1 = !DIFile(filename: "example.c", directory: "/") +!2 = !DISubroutineType(types: !3) +!3 = !{!5} +!4 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, emissionKind: FullDebug) +!5 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) + +; Local variable. +!10 = !DILocalVariable(name: "a", scope: !0, file: !1, line: 2, type: !5) + +; DICompositeType local to foo. +!11 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "bar", scope: !0, file: !1, line: 2, size: 32, elements: !12) +!12 = !{!13} +!13 = !DIDerivedType(tag: DW_TAG_member, name: "m", scope: !11, file: !1, line: 2, baseType: !5, size: 32) + +; Local variable of type struct bar, local to foo. +!14 = !DILocalVariable(name: "c", scope: !0, file: !1, line: 2, type: !11) + +!101 = !DILocation(line: 2, column: 5, scope: !0) +!102 = !DILocation(line: 3, column: 1, scope: !0) +!103 = !DILocation(line: 2, column: 12, scope: !0) + +!llvm.dbg.cu = !{!4} +!llvm.module.flags = !{!30} + +!30 = !{i32 2, !"Debug Info Version", i32 3} + +define i32 @foo() !dbg !0 { +entry: + ; Local variable 'a' debug info. + %a.addr = alloca i32, align 4, !dbg !101 + #dbg_declare(ptr %a.addr, !10, !DIExpression(), !101) + store i32 42, ptr %a.addr, align 4, !dbg !101 + + ; Local variable 'c' (struct bar) debug info. + %c.addr = alloca %struct.bar, align 4, !dbg !103 + #dbg_declare(ptr %c.addr, !14, !DIExpression(), !103) + + ret i32 42, !dbg !102 +} + +define i32 @foo_clone() !dbg !0 { +entry: + ; Local variable 'a' debug info. + %a.addr = alloca i32, align 4, !dbg !101 + #dbg_declare(ptr %a.addr, !10, !DIExpression(), !101) + store i32 42, ptr %a.addr, align 4, !dbg !101 + + ; Local variable 'c' (struct bar) debug info. + %c.addr = alloca %struct.bar, align 4, !dbg !103 + #dbg_declare(ptr %c.addr, !14, !DIExpression(), !103) + + ret i32 42, !dbg !102 +} + +%struct.bar = type { i32 } diff --git a/llvm/test/Verifier/metadata-function-dbg.ll b/llvm/test/Verifier/metadata-function-dbg.ll index 23ac6f31c9e1a..79a7e99959cc0 100644 --- a/llvm/test/Verifier/metadata-function-dbg.ll +++ b/llvm/test/Verifier/metadata-function-dbg.ll @@ -11,16 +11,6 @@ define void @f2() !dbg !4 !dbg !4 { unreachable } -; CHECK: DISubprogram attached to more than one function -define void @f3() !dbg !4 { - unreachable -} - -; CHECK: DISubprogram attached to more than one function -define void @f4() !dbg !4 { - unreachable -} - ; CHECK-NOT: !dbg ; CHECK: function !dbg attachment must be a subprogram ; CHECK-NEXT: ptr @bar diff --git a/llvm/unittests/CodeGen/LexicalScopesTest.cpp b/llvm/unittests/CodeGen/LexicalScopesTest.cpp index 0c6b9326bcfd4..cd9a1d13a190f 100644 --- a/llvm/unittests/CodeGen/LexicalScopesTest.cpp +++ b/llvm/unittests/CodeGen/LexicalScopesTest.cpp @@ -140,6 +140,7 @@ TEST_F(LexicalScopesTest, FlatLayout) { LS.resetFunction(); EXPECT_EQ(LS.getCurrentFunctionScope(), nullptr); + LS.initialize(Mod); LS.scanFunction(*MF); EXPECT_FALSE(LS.empty()); LexicalScope *FuncScope = LS.getCurrentFunctionScope(); @@ -183,6 +184,7 @@ TEST_F(LexicalScopesTest, BlockScopes) { BuildMI(*MBB4, MBB4->end(), InBlockLoc, BeanInst); LexicalScopes LS; + LS.initialize(Mod); LS.scanFunction(*MF); LexicalScope *FuncScope = LS.getCurrentFunctionScope(); EXPECT_EQ(FuncScope->getDesc(), OurFunc); @@ -218,6 +220,7 @@ TEST_F(LexicalScopesTest, InlinedScopes) { BuildMI(*MBB4, MBB4->end(), InlinedLoc, BeanInst); LexicalScopes LS; + LS.initialize(Mod); LS.scanFunction(*MF); LexicalScope *FuncScope = LS.getCurrentFunctionScope(); auto &Children = FuncScope->getChildren(); @@ -253,6 +256,7 @@ TEST_F(LexicalScopesTest, FuncWithEmptyGap) { BuildMI(*MBB4, MBB4->end(), OutermostLoc, BeanInst); LexicalScopes LS; + LS.initialize(Mod); LS.scanFunction(*MF); LexicalScope *FuncScope = LS.getCurrentFunctionScope(); @@ -274,6 +278,7 @@ TEST_F(LexicalScopesTest, FuncWithRealGap) { MachineInstr *LastI = BuildMI(*MBB4, MBB4->end(), InBlockLoc, BeanInst); LexicalScopes LS; + LS.initialize(Mod); LS.scanFunction(*MF); LexicalScope *BlockScope = LS.findLexicalScope(InBlockLoc.get()); ASSERT_NE(BlockScope, nullptr); @@ -307,6 +312,7 @@ TEST_F(LexicalScopesTest, NotNested) { MachineInstr *FourthI = BuildMI(*MBB4, MBB4->end(), InBlockLoc, BeanInst); LexicalScopes LS; + LS.initialize(Mod); LS.scanFunction(*MF); LexicalScope *FuncScope = LS.getCurrentFunctionScope(); LexicalScope *BlockScope = LS.findLexicalScope(InBlockLoc.get()); @@ -345,6 +351,7 @@ TEST_F(LexicalScopesTest, TestDominates) { BuildMI(*MBB4, MBB4->end(), InBlockLoc, BeanInst); LexicalScopes LS; + LS.initialize(Mod); LS.scanFunction(*MF); LexicalScope *FuncScope = LS.getCurrentFunctionScope(); LexicalScope *BlockScope = LS.findLexicalScope(InBlockLoc.get()); @@ -387,6 +394,7 @@ TEST_F(LexicalScopesTest, TestGetBlocks) { BuildMI(*MBB4, MBB4->end(), InBlockLoc, BeanInst); LexicalScopes LS; + LS.initialize(Mod); LS.scanFunction(*MF); LexicalScope *FuncScope = LS.getCurrentFunctionScope(); LexicalScope *BlockScope = LS.findLexicalScope(InBlockLoc.get()); @@ -444,6 +452,7 @@ TEST_F(LexicalScopesTest, TestMetaInst) { BuildMI(*MBB4, MBB4->end(), InBlockLoc, BeanInst); LexicalScopes LS; + LS.initialize(Mod); LS.scanFunction(*MF); LexicalScope *FuncScope = LS.getCurrentFunctionScope(); LexicalScope *BlockScope = LS.findLexicalScope(InBlockLoc.get()); @@ -475,9 +484,111 @@ TEST_F(LexicalScopesTest, TestFunctionScan) { LexicalScopes LS; LS.initialize(Mod); - ASSERT_EQ(LS.getFunction(OurFunc), &MF->getFunction()); - ASSERT_EQ(LS.getFunction(Func2), &MF2->getFunction()); - ASSERT_EQ(LS.getFunction(UnattachedFunc), nullptr); + auto Fs = LS.getFunctions(OurFunc); + ASSERT_NE(Fs, nullptr); + ASSERT_EQ(Fs->size(), 1u); + ASSERT_EQ(*Fs->begin(), &MF->getFunction()); + + Fs = LS.getFunctions(Func2); + ASSERT_NE(Fs, nullptr); + ASSERT_EQ(Fs->size(), 1u); + ASSERT_EQ(*Fs->begin(), &MF2->getFunction()); + + Fs = LS.getFunctions(UnattachedFunc); + ASSERT_EQ(Fs, nullptr); +} + +// Test function map creation for subprogram attached to multiple functions. +// Ensure that abstract lexical scopes for subprograms attached to multiple +// functions are created. +TEST_F(LexicalScopesTest, TestRepeatingSubprogram) { + BuildMI(*MBB1, MBB1->end(), InBlockLoc, BeanInst); + + std::unique_ptr MF2 = + createMachineFunction(Ctx, Mod, "Test.1"); + auto &F2 = MF2->getFunction(); + F2.setSubprogram(OurFunc); + auto BB1_2 = BasicBlock::Create(Ctx, "a", &F2); + IRBuilder<> IRB1_2(BB1_2); + IRB1_2.CreateRetVoid(); + auto *MBB1_2 = MF->CreateMachineBasicBlock(BB1_2); + MF2->insert(MF2->end(), MBB1_2); + BuildMI(*MBB1_2, MBB1_2->end(), InBlockLoc, BeanInst); + + std::unique_ptr FooMF = + createMachineFunction(Ctx, Mod, "Foo"); + auto BB = BasicBlock::Create(Ctx, "a", &FooMF->getFunction()); + IRBuilder<> IRB(BB); + IRB.CreateRetVoid(); + auto FooMBB = FooMF->CreateMachineBasicBlock(BB); + FooMF->insert(FooMF->end(), FooMBB); + + DIBuilder DIB(Mod, true, OurCU); + auto OurSubT = DIB.createSubroutineType(DIB.getOrCreateTypeArray({})); + DISubprogram *FooFunc = + DIB.createFunction(OurCU, "Foo", "", OurFile, 1, OurSubT, 1, + DINode::FlagZero, DISubprogram::SPFlagDefinition); + FooMF->getFunction().setSubprogram(FooFunc); + DIB.finalize(); + + BuildMI(*FooMBB, FooMBB->end(), DILocation::get(Ctx, 10, 20, FooFunc), + BeanInst); + + LexicalScopes LS; + LS.initialize(Mod); + + auto Fs = LS.getFunctions(OurFunc); + ASSERT_NE(Fs, nullptr); + ASSERT_EQ(Fs->size(), 2u); + ASSERT_TRUE(Fs->contains(&MF->getFunction())); + ASSERT_TRUE(Fs->contains(&MF2->getFunction())); + + Fs = LS.getFunctions(FooFunc); + ASSERT_NE(Fs, nullptr); + ASSERT_EQ(Fs->size(), 1u); + ASSERT_TRUE(Fs->contains(&FooMF->getFunction())); + + LS.scanFunction(*MF); + EXPECT_FALSE(LS.currentFunctionHasInlinedScopes()); + EXPECT_EQ(LS.getAbstractScopesList().size(), 1u); + EXPECT_EQ(LS.getAbstractScopesList()[0]->getScopeNode(), OurFunc); + + LS.scanFunction(*MF2); + EXPECT_FALSE(LS.currentFunctionHasInlinedScopes()); + EXPECT_EQ(LS.getAbstractScopesList().size(), 1u); + EXPECT_EQ(LS.getAbstractScopesList()[0]->getScopeNode(), OurFunc); + + LS.scanFunction(*FooMF); + EXPECT_FALSE(LS.currentFunctionHasInlinedScopes()); + EXPECT_EQ(LS.getAbstractScopesList().size(), 0u); + EXPECT_NE(LS.findLexicalScope(FooFunc), nullptr); +} + +// Test that if a DISubprogram is attached to two functions, +// an abstract lexical block is created after scanning a first function. +TEST_F(LexicalScopesTest, TestRepeatingLexicalBlocks) { + BuildMI(*MBB1, MBB1->end(), InBlockLoc, BeanInst); + + std::unique_ptr MF2 = createMachineFunction(Ctx, Mod, "Foo"); + auto BB = BasicBlock::Create(Ctx, "a", &MF2->getFunction()); + IRBuilder<> IRB(BB); + IRB.CreateRetVoid(); + auto MBB = MF2->CreateMachineBasicBlock(BB); + MF2->insert(MF2->end(), MBB); + + MF2->getFunction().setSubprogram(OurFunc); + + BuildMI(*MBB, MBB->end(), InBlockLoc, BeanInst); + + LexicalScopes LS; + LS.initialize(Mod); + LS.scanFunction(*MF2); + EXPECT_NE(LS.findAbstractScope(OurFunc), nullptr); + EXPECT_NE(LS.findAbstractScope(OurBlock), nullptr); + + LS.scanFunction(*MF); + EXPECT_NE(LS.findAbstractScope(OurFunc), nullptr); + EXPECT_NE(LS.findAbstractScope(OurBlock), nullptr); } } // anonymous namespace