Skip to content

[DebugInfo] Only create DIEs of concrete functions #90523

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
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
65 changes: 31 additions & 34 deletions llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1167,44 +1167,41 @@ DIE *DwarfCompileUnit::createAndAddScopeChildren(LexicalScope *Scope,
void DwarfCompileUnit::constructAbstractSubprogramScopeDIE(
LexicalScope *Scope) {
auto *SP = cast<DISubprogram>(Scope->getScopeNode());
if (getAbstractScopeDIEs().count(SP))
DIE *&AbsDef = getAbstractScopeDIEs()[SP];
if (AbsDef)
return;

DIE *ContextDIE;
DwarfCompileUnit *ContextCU = this;

if (includeMinimalInlineScopes())
ContextDIE = &getUnitDie();
// Some of this is duplicated from DwarfUnit::getOrCreateSubprogramDIE, with
// the important distinction that the debug node is not associated with the
// DIE (since the debug node will be associated with the concrete DIE, if
// any). It could be refactored to some common utility function.
else if (auto *SPDecl = SP->getDeclaration()) {
ContextDIE = &getUnitDie();
getOrCreateSubprogramDIE(SPDecl);
} else {
ContextDIE = getOrCreateContextDIE(SP->getScope());
// The scope may be shared with a subprogram that has already been
// constructed in another CU, in which case we need to construct this
// subprogram in the same CU.
ContextCU = DD->lookupCU(ContextDIE->getUnitDie());
}

// Passing null as the associated node because the abstract definition
// shouldn't be found by lookup.
DIE &AbsDef = ContextCU->createAndAddDIE(dwarf::DW_TAG_subprogram,
*ContextDIE, nullptr);

// Store the DIE before creating children.
ContextCU->getAbstractScopeDIEs()[SP] = &AbsDef;

ContextCU->applySubprogramAttributesToDefinition(SP, AbsDef);
ContextCU->addSInt(AbsDef, dwarf::DW_AT_inline,
DIE *ContextDIE =
getOrCreateSubprogramContextDIE(SP, includeMinimalInlineScopes());

if (auto *SPDecl = SP->getDeclaration())
if (!includeMinimalInlineScopes())
// Build the decl now to ensure it precedes the definition.
getOrCreateSubprogramDIE(SPDecl);

// The scope may be shared with a subprogram that has already been
// constructed in another CU, in which case we need to construct this
// subprogram in the same CU.
auto *ContextCU = (includeMinimalInlineScopes() || SP->getDeclaration())
? this
: DD->lookupCU(ContextDIE->getUnitDie());

// The abstract definition can only be looked up from the associated node if
// the subprogram does not have a concrete function.
if (!ContextCU->DD->isConcrete(SP))
AbsDef = ContextCU->getDIE(SP);
if (!AbsDef)
AbsDef = &ContextCU->createAndAddDIE(dwarf::DW_TAG_subprogram, *ContextDIE,
nullptr);

ContextCU->applySubprogramAttributesToDefinition(SP, *AbsDef);
ContextCU->addSInt(*AbsDef, dwarf::DW_AT_inline,
DD->getDwarfVersion() <= 4 ? std::optional<dwarf::Form>()
: dwarf::DW_FORM_implicit_const,
dwarf::DW_INL_inlined);
if (DIE *ObjectPointer = ContextCU->createAndAddScopeChildren(Scope, AbsDef))
ContextCU->addDIEEntry(AbsDef, dwarf::DW_AT_object_pointer, *ObjectPointer);
if (DIE *ObjectPointer = ContextCU->createAndAddScopeChildren(Scope, *AbsDef))
ContextCU->addDIEEntry(*AbsDef, dwarf::DW_AT_object_pointer,
*ObjectPointer);
}

bool DwarfCompileUnit::useGNUAnalogForDwarf5Feature() const {
Expand Down Expand Up @@ -1416,7 +1413,7 @@ DIE *DwarfCompileUnit::getOrCreateImportedEntityDIE(
void DwarfCompileUnit::finishSubprogramDefinition(const DISubprogram *SP) {
DIE *D = getDIE(SP);
if (DIE *AbsSPDIE = getAbstractScopeDIEs().lookup(SP)) {
if (D)
if (D && D != AbsSPDIE)
// If this subprogram has an abstract definition, reference that
addDIEEntry(*D, dwarf::DW_AT_abstract_origin, *AbsSPDIE);
} else {
Expand Down
12 changes: 5 additions & 7 deletions llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.h
Original file line number Diff line number Diff line change
Expand Up @@ -121,12 +121,6 @@ class DwarfCompileUnit final : public DwarfUnit {

bool isDwoUnit() const override;

DenseMap<const DILocalScope *, DIE *> &getAbstractScopeDIEs() {
if (isDwoUnit() && !DD->shareAcrossDWOCUs())
return AbstractLocalScopeDIEs;
return DU->getAbstractScopeDIEs();
}

DenseMap<const DINode *, std::unique_ptr<DbgEntity>> &getAbstractEntities() {
if (isDwoUnit() && !DD->shareAcrossDWOCUs())
return AbstractEntities;
Expand All @@ -143,7 +137,11 @@ class DwarfCompileUnit final : public DwarfUnit {
DwarfCompileUnit(unsigned UID, const DICompileUnit *Node, AsmPrinter *A,
DwarfDebug *DW, DwarfFile *DWU,
UnitKind Kind = UnitKind::Full);

DenseMap<const DILocalScope *, DIE *> &getAbstractScopeDIEs() {
if (isDwoUnit() && !DD->shareAcrossDWOCUs())
return AbstractLocalScopeDIEs;
return DU->getAbstractScopeDIEs();
}
bool hasRangeLists() const { return HasRangeLists; }

DwarfCompileUnit *getSkeleton() const {
Expand Down
4 changes: 4 additions & 0 deletions llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1154,6 +1154,10 @@ void DwarfDebug::beginModule(Module *M) {
assert(MMI->hasDebugInfo() &&
"DebugInfoAvailabilty unexpectedly not initialized");
SingleCU = NumDebugCUs == 1;
for (const auto &F : M->functions())
if (!F.isDeclaration())
if (const auto *SP = F.getSubprogram())
ConcreteSPs.insert(SP);
DenseMap<DIGlobalVariable *, SmallVector<DwarfCompileUnit::GlobalExpr, 1>>
GVMap;
for (const GlobalVariable &Global : M->globals()) {
Expand Down
13 changes: 13 additions & 0 deletions llvm/lib/CodeGen/AsmPrinter/DwarfDebug.h
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,11 @@ class DwarfDebug : public DebugHandlerBase {
/// create DIEs.
SmallSetVector<const DISubprogram *, 16> ProcessedSPNodes;

/// The set of subprograms that have concrete functions.
/// This does not include subprograms of machine outlined functions because
/// they are created after this set is computed.
SmallSetVector<const DISubprogram *, 16> ConcreteSPs;

/// Map function-local imported entities to their parent local scope
/// (either DILexicalBlock or DISubprogram) for a processed function
/// (including inlined subprograms).
Expand Down Expand Up @@ -919,6 +924,14 @@ class DwarfDebug : public DebugHandlerBase {
/// allocated in the MCContext.
std::optional<MD5::MD5Result> getMD5AsBytes(const DIFile *File) const;

/// Returns false for subprograms that do not have concrete functions and thus
/// could have only abstract DIEs. Also returns false for machine outlined
/// subprograms, but they will never be emitted as abstract DIEs in the first
/// place.
bool isConcrete(const DISubprogram *SP) const {
return ConcreteSPs.count(SP);
}

MDNodeSet &getLocalDeclsForScope(const DILocalScope *S) {
return LocalDeclsPerLS[S];
}
Expand Down
24 changes: 16 additions & 8 deletions llvm/lib/CodeGen/AsmPrinter/DwarfUnit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1168,24 +1168,32 @@ DIE *DwarfUnit::getOrCreateModule(const DIModule *M) {
return &MDie;
}

DIE *DwarfUnit::getOrCreateSubprogramContextDIE(const DISubprogram *SP,
bool Minimal) {
if (Minimal || SP->getDeclaration())
return &getUnitDie();
return getOrCreateContextDIE(SP->getScope());
}

DIE *DwarfUnit::getOrCreateSubprogramDIE(const DISubprogram *SP, bool Minimal) {
// Construct the context before querying for the existence of the DIE in case
// such construction creates the DIE (as is the case for member function
// declarations).
DIE *ContextDIE =
Minimal ? &getUnitDie() : getOrCreateContextDIE(SP->getScope());
DIE *ContextDIE = getOrCreateSubprogramContextDIE(SP, Minimal);

if (DIE *SPDie = getDIE(SP))
return SPDie;

if (auto *SPDecl = SP->getDeclaration()) {
if (!Minimal) {
// Add subprogram definitions to the CU die directly.
ContextDIE = &getUnitDie();
if (auto *SPDecl = SP->getDeclaration())
if (!Minimal)
// Build the decl now to ensure it precedes the definition.
getOrCreateSubprogramDIE(SPDecl);
}
}

// Try to find the abstract origin if the subprogram is not concrete.
if (auto *ContextCU = DD->lookupCU(ContextDIE->getUnitDie()))
if (!ContextCU->DD->isConcrete(SP))
if (auto *SPDie = ContextCU->getAbstractScopeDIEs().lookup(SP))
return SPDie;

// DW_TAG_inlined_subroutine may refer to this DIE.
DIE &SPDie = createAndAddDIE(dwarf::DW_TAG_subprogram, *ContextDIE, SP);
Expand Down
1 change: 1 addition & 0 deletions llvm/lib/CodeGen/AsmPrinter/DwarfUnit.h
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,7 @@ class DwarfUnit : public DIEUnit {

DIE *getOrCreateNameSpace(const DINamespace *NS);
DIE *getOrCreateModule(const DIModule *M);
DIE *getOrCreateSubprogramContextDIE(const DISubprogram *SP, bool Minimal);
DIE *getOrCreateSubprogramDIE(const DISubprogram *SP, bool Minimal = false);

void applySubprogramAttributes(const DISubprogram *SP, DIE &SPDie,
Expand Down
96 changes: 96 additions & 0 deletions llvm/test/DebugInfo/Generic/inlined-static-var.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
; RUN: %llc_dwarf -O0 -filetype=obj < %s | llvm-dwarfdump -debug-info - | FileCheck --implicit-check-not "{{DW_TAG|NULL}}" %s

; RUN: %llc_dwarf --try-experimental-debuginfo-iterators -O0 -filetype=obj < %s | llvm-dwarfdump -debug-info - | FileCheck --implicit-check-not "{{DW_TAG|NULL}}" %s

Copy link
Collaborator

Choose a reason for hiding this comment

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

some instructions on how to reproduce the IR would be good - just the clang command line or whatever (which optimizations, etc)

; inline __attribute__((always_inline))
; int removed() { static int A; return A++; }
;
; __attribute__((always_inline))
; int not_removed() { static int B; return B++; }
;
; int foo() { return removed() + not_removed(); }

; Ensure that global variables belong to the correct subprograms even if those
; subprograms are inlined.

; CHECK: DW_TAG_compile_unit
; CHECK: DW_TAG_subprogram
; TODO: Place the variable in the abstract DIE rather than this concrete DIE.
Comment on lines +17 to +18
Copy link
Collaborator

Choose a reason for hiding this comment

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

maybe CHECK that this has an abstract_origin, to show this is the concrete instance?

; CHECK: DW_TAG_variable
; CHECK: DW_AT_name ("B")
; CHECK: NULL
; CHECK: DW_TAG_base_type
; CHECK: DW_TAG_subprogram
; CHECK: DW_AT_name ("removed")
; CHECK: DW_TAG_variable
; CHECK: DW_AT_name ("A")
; CHECK: NULL
; CHECK: DW_TAG_subprogram
; CHECK: DW_AT_name ("not_removed")
; CHECK: DW_TAG_subprogram
; CHECK: DW_AT_name ("foo")
; CHECK: DW_TAG_inlined_subroutine
; CHECK: DW_TAG_inlined_subroutine
Comment on lines +32 to +33
Copy link
Collaborator

Choose a reason for hiding this comment

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

could check these have the right abstract_origins too (& I guess, maybe, check they have no TAGs inside them, to demonstrate that the static member variables aren't described in the inlined subroutines?)

; CHECK: NULL
; CHECK: NULL

$_ZZ7removedvE1A = comdat any

@_ZZ11not_removedvE1A = internal global i32 0, align 4, !dbg !0
@_ZZ7removedvE1A = linkonce_odr dso_local global i32 0, comdat, align 4, !dbg !10

define dso_local i32 @_Z11not_removedv() !dbg !2 {
%1 = load i32, ptr @_ZZ11not_removedvE1A, align 4, !dbg !24
%2 = add nsw i32 %1, 1, !dbg !24
store i32 %2, ptr @_ZZ11not_removedvE1A, align 4, !dbg !24
ret i32 %1, !dbg !25
}

define dso_local i32 @_Z3foov() !dbg !26 {
%1 = load i32, ptr @_ZZ7removedvE1A, align 4, !dbg !27
%2 = add nsw i32 %1, 1, !dbg !27
store i32 %2, ptr @_ZZ7removedvE1A, align 4, !dbg !27
%3 = load i32, ptr @_ZZ11not_removedvE1A, align 4, !dbg !29
%4 = add nsw i32 %3, 1, !dbg !29
store i32 %4, ptr @_ZZ11not_removedvE1A, align 4, !dbg !29
%5 = add nsw i32 %1, %3, !dbg !31
ret i32 %5, !dbg !32
}

!llvm.dbg.cu = !{!7}
!llvm.module.flags = !{!14, !15, !16, !17, !18, !19, !20, !21, !22}
!llvm.ident = !{!23}

!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression())
!1 = distinct !DIGlobalVariable(name: "B", scope: !2, file: !3, line: 5, type: !6, isLocal: true, isDefinition: true)
!2 = distinct !DISubprogram(name: "not_removed", linkageName: "_Z11not_removedv", scope: !3, file: !3, line: 5, type: !4, scopeLine: 5, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !7, retainedNodes: !13)
!3 = !DIFile(filename: "example.cpp", directory: "")
!4 = !DISubroutineType(types: !5)
!5 = !{!6}
!6 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
!7 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !8, producer: "clang version 14.0.0", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, globals: !9, splitDebugInlining: false, nameTableKind: None)
!8 = !DIFile(filename: "example.cpp", directory: "")
!9 = !{!0, !10}
!10 = !DIGlobalVariableExpression(var: !11, expr: !DIExpression())
!11 = distinct !DIGlobalVariable(name: "A", scope: !12, file: !3, line: 2, type: !6, isLocal: false, isDefinition: true)
!12 = distinct !DISubprogram(name: "removed", linkageName: "_Z7removedv", scope: !3, file: !3, line: 2, type: !4, scopeLine: 2, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !7, retainedNodes: !13)
!13 = !{}
!14 = !{i32 7, !"Dwarf Version", i32 4}
!15 = !{i32 2, !"Debug Info Version", i32 3}
!16 = !{i32 1, !"wchar_size", i32 4}
!17 = !{i32 1, !"branch-target-enforcement", i32 0}
!18 = !{i32 1, !"sign-return-address", i32 0}
!19 = !{i32 1, !"sign-return-address-all", i32 0}
!20 = !{i32 1, !"sign-return-address-with-bkey", i32 0}
!21 = !{i32 7, !"uwtable", i32 1}
!22 = !{i32 7, !"frame-pointer", i32 1}
!23 = !{!"clang version 14.0.0"}
!24 = !DILocation(line: 5, column: 43, scope: !2)
!25 = !DILocation(line: 5, column: 35, scope: !2)
!26 = distinct !DISubprogram(name: "foo", linkageName: "_Z3foov", scope: !3, file: !3, line: 7, type: !4, scopeLine: 7, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !7, retainedNodes: !13)
!27 = !DILocation(line: 2, column: 39, scope: !12, inlinedAt: !28)
!28 = distinct !DILocation(line: 7, column: 20, scope: !26)
!29 = !DILocation(line: 5, column: 43, scope: !2, inlinedAt: !30)
!30 = distinct !DILocation(line: 7, column: 32, scope: !26)
!31 = !DILocation(line: 7, column: 30, scope: !26)
!32 = !DILocation(line: 7, column: 13, scope: !26)
Loading