Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ unsigned DwarfCompileUnit::getOrCreateSourceID(const DIFile *File) {
DIE *DwarfCompileUnit::getOrCreateGlobalVariableDIE(
const DIGlobalVariable *GV, ArrayRef<GlobalExpr> GlobalExprs) {
// Check for pre-existence.
if (DIE *Die = getDIE(GV))
if (DIE *Die = getDIEs(GV).getVariableDIE(GV))
return Die;

assert(GV);
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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())
Expand Down
22 changes: 7 additions & 15 deletions llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,16 +79,10 @@ class DwarfCompileUnit final : public DwarfUnit {
// List of concrete lexical block scopes belong to subprograms within this CU.
DenseMap<const DILocalScope *, DIE *> LexicalBlockDIEs;

// List of abstract local scopes (either DISubprogram or DILexicalBlock).
DenseMap<const DILocalScope *, DIE *> AbstractLocalScopeDIEs;
SmallPtrSet<const DISubprogram *, 8> FinalizedAbstractSubprograms;

// List of inlined lexical block scopes that belong to subprograms within this
// CU.
DenseMap<const DILocalScope *, SmallVector<DIE *, 2>> InlinedLocalScopeDIEs;

DenseMap<const DINode *, std::unique_ptr<DbgEntity>> AbstractEntities;

/// DWO ID for correlating skeleton and split units.
uint64_t DWOId = 0;

Expand Down Expand Up @@ -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<const DILocalScope *, DIE *> &getAbstractScopeDIEs() {
if (isDwoUnit() && !DD->shareAcrossDWOCUs())
return AbstractLocalScopeDIEs;
return DU->getAbstractScopeDIEs();
return getDIEs().getAbstractScopeDIEs();
}

DenseMap<const DINode *, std::unique_ptr<DbgEntity>> &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;
Expand Down
3 changes: 2 additions & 1 deletion llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
169 changes: 136 additions & 33 deletions llvm/lib/CodeGen/AsmPrinter/DwarfFile.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 <functional>
#include <map>
#include <memory>
#include <optional>
#include <utility>

namespace llvm {
Expand All @@ -26,9 +29,6 @@ class AsmPrinter;
class DbgEntity;
class DbgVariable;
class DbgLabel;
class DINode;
class DILocalScope;
class DISubprogram;
class DwarfCompileUnit;
class DwarfUnit;
class LexicalScope;
Expand All @@ -53,6 +53,137 @@ struct RangeSpanList {
SmallVector<RangeSpan, 2> Ranges;
};

/// Tracks abstract and concrete DIEs for debug info entities of a certain type.
template <typename DINodeT, typename DbgEntityT> class DINodeInfoHolder {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe this could be DINodeMap?

public:
using AbstractMapT = DenseMap<const DINodeT *, DIE *>;
using ConcreteMapT =
DenseMap<const DINodeT *, SmallDenseMap<const DbgEntityT *, DIE *, 2>>;

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<const typename ConcreteMapT::mapped_type>>
getConcreteDIEs(const DINodeT *N) const {
if (auto I = ConcreteMap.find(N); I != ConcreteMap.end())
return std::make_optional(std::ref(I->second));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

optional references are a bit awkward - could we use a raw DIE*? (or does it need to be a DIE**, I guess? That'd be OK, if a bit awkward in its own right)

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;

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra blank line could be removed to make this consistent with similar code above?

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<DILocalVariable, DbgVariable> LVHolder;
/// DIEs of labels.
DINodeInfoHolder<DILabel, DbgLabel> LabelHolder;
DenseMap<const DINode *, std::unique_ptr<DbgEntity>> AbstractEntities;
// List of abstract local scopes (either DISubprogram or DILexicalBlock).
DenseMap<const DILocalScope *, DIE *> AbstractLocalScopeDIEs;
/// Keeps track of abstract subprograms to populate them only once.
// FIXME: merge creation and population of abstract scopes.
SmallPtrSet<const DISubprogram *, 8> FinalizedAbstractSubprograms;

/// Other DINodes with the corresponding DIEs.
DenseMap<const DINode *, DIE *> MDNodeToDieMap;
Comment on lines +122 to +135
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So this creates a union of the way we were tracking the node<>die map at the file level and the unit level - and then stores that union at both the file and the unit level?

That seems like a bit of added complexity (but I guess it's the title of the patch). I guess I didn't quite follow from the patch description why that's the goal.

Could you try to summarize/rephrase this goal/motivation?

Copy link
Member Author

@dzhidzhoev dzhidzhoev Oct 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently, we have fields, where the DINodes shared across CUs are stored:

  • DwarfFile::DITypeNodeToDieMap (which can also serve as a container for DISubprograms, according to DwarfUnit::isShareableAcrossCUs logic)
  • DwarfFile::AbstractLocalScopeDIEs
  • DwarfFile::AbstractEntities
  • DwarfFile::FinalizedAbstractSubprograms

And we have the corresponding fields in DwarfUnit/DwarfCompileUnit:

  • DwarfUnit::MDNodeToDieMap
  • DwarfCompileUnit::AbstractLocalScopeDIEs
  • DwarfCompileUnit::AbstractEntities
  • DwarfCompileUnit::FinalizedAbstractSubprograms

The first goal of DwarfInfoHolder is to deduplicate these field definitions by putting them inside one "container", and reuse that container when needed. DwarfUnit::InfoHolder will store CU-local (or type-unit-local) DIEs, DwarfFile::InfoHolder will store shareable DIEs.

So this creates a union of the way we were tracking the node<>die map at the file level and the unit level - and then stores that union at both the file and the unit level?

So, an instance of DwarfInfoHolder holds either shareable or CU-local DIEs. It's not a union of them.

Second goal is to split the general DIE map into separate maps for DILocalVariables, DILabels, DILocalScopes (in the following two PRs), and a map for the rest of DINodes (mostly types), since, as I assume, we can get DILocalVariables/DILabels/DILocalScopes used in multiple llvm::Functions during LTO with DISubprogram uniquing, so DIEs for these nodes created in different Functions should be somehow distinguished. It's done with additional DbgEntityT pointer.


public:
void insertDIE(const DINode *N, DIE *Die) {
assert((!isa<DILabel>(N) && !isa<DILocalVariable>(N)) &&
"Use getLabels().insertDIE() for labels or getLVs().insertDIE() for "
"local variables");
auto [_, Inserted] = MDNodeToDieMap.try_emplace(N, Die);
assert((Inserted || isa<DISubprogram>(N) || isa<DIType>(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<DILabel>(N) && !isa<DILocalVariable>(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; }
Comment on lines +157 to +161
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably worth spelling out the return types


/// 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<DILocalVariable>(V))
if (DIE *D = getLVs().getDIE(LV))
return D;
return getDIE(V);
}

DenseMap<const DILocalScope *, DIE *> &getAbstractScopeDIEs() {
return AbstractLocalScopeDIEs;
}

DenseMap<const DINode *, std::unique_ptr<DbgEntity>> &getAbstractEntities() {
return AbstractEntities;
}

auto &getFinalizedAbstractSubprograms() {
return FinalizedAbstractSubprograms;
}
};

class DwarfFile {
// Target of Dwarf emission, used for sizing of abbreviations.
AsmPrinter *Asm;
Expand Down Expand Up @@ -93,17 +224,7 @@ class DwarfFile {
using LabelList = SmallVector<DbgLabel *, 4>;
DenseMap<LexicalScope *, LabelList> ScopeLabels;

// Collection of abstract subprogram DIEs.
DenseMap<const DILocalScope *, DIE *> AbstractLocalScopeDIEs;
DenseMap<const DINode *, std::unique_ptr<DbgEntity>> AbstractEntities;
/// Keeps track of abstract subprograms to populate them only once.
// FIXME: merge creation and population of abstract scopes.
SmallPtrSet<const DISubprogram *, 8> 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<const MDNode *, DIE *> DITypeNodeToDieMap;
DwarfInfoHolder InfoHolder;

public:
DwarfFile(AsmPrinter *AP, StringRef Pref, BumpPtrAllocator &DA);
Expand Down Expand Up @@ -171,25 +292,7 @@ class DwarfFile {
return ScopeLabels;
}

DenseMap<const DILocalScope *, DIE *> &getAbstractScopeDIEs() {
return AbstractLocalScopeDIEs;
}

DenseMap<const DINode *, std::unique_ptr<DbgEntity>> &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
Expand Down
Loading