Provide gmlt-like inline scope information in the skeleton CU to facilitate symbolication without needing the .dwo files

Clang -gsplit-dwarf self-host -O0, binary increases by 0.0005%, -O2,
binary increases by 25%.

A large binary inside Google, split-dwarf, -O0, and other internal flags
(GDB index, etc) increases by 1.8%, optimized build is 35%.

The size impact may be somewhat greater in .o files (I haven't measured
that much - since the linked executable -O0 numbers seemed low enough)
due to relocations. These relocations could be removed if we taught the
llvm-symbolizer to handle indexed addressing in the .o file (GDB can't
cope with this just yet, but GDB won't be reading this info anyway).
Also debug_ranges could be shared between .o and .dwo, though ideally
debug_ranges would get a schema that could used index(+offset)
addressing, and move to the .dwo file, then we'd be back to sharing
addresses in the address pool again.

But for now, these sizes seem small enough to go ahead with this.

Verified that no other DW_TAGs are produced into the .o file other than
subprograms and inlined_subroutines.

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@221306 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
David Blaikie 2014-11-04 22:12:25 +00:00
parent 41ebc263a9
commit f49ead7098
7 changed files with 195 additions and 32 deletions

View File

@ -283,7 +283,7 @@ void DwarfCompileUnit::attachLowHighPC(DIE &D, const MCSymbol *Begin,
// and DW_AT_high_pc attributes. If there are global variables in this
// scope then create and insert DIEs for these variables.
DIE &DwarfCompileUnit::updateSubprogramScopeDIE(DISubprogram SP) {
DIE *SPDie = getOrCreateSubprogramDIE(SP);
DIE *SPDie = getOrCreateSubprogramDIE(SP, includeMinimalInlineScopes());
attachLowHighPC(*SPDie, DD->getFunctionBeginSym(), DD->getFunctionEndSym());
if (!DD->getCurrentFunction()->getTarget().Options.DisableFramePointerElim(
@ -291,7 +291,7 @@ DIE &DwarfCompileUnit::updateSubprogramScopeDIE(DISubprogram SP) {
addFlag(*SPDie, dwarf::DW_AT_APPLE_omit_frame_ptr);
// Only include DW_AT_frame_base in full debug info
if (getCUNode().getEmissionKind() != DIBuilder::LineTablesOnly) {
if (!includeMinimalInlineScopes()) {
const TargetRegisterInfo *RI =
Asm->TM.getSubtargetImpl()->getRegisterInfo();
MachineLocation Location(RI->getFrameRegister(*Asm->MF));
@ -341,10 +341,14 @@ void DwarfCompileUnit::constructScopeDIE(
// null and the children will be added to the scope DIE.
createScopeChildrenDIE(Scope, Children, &ChildScopeCount);
// There is no need to emit empty lexical block DIE.
for (const auto &E : DD->findImportedEntitiesForScope(DS))
Children.push_back(
constructImportedEntityDIE(DIImportedEntity(E.second)));
// Skip imported directives in gmlt-like data.
if (!includeMinimalInlineScopes()) {
// There is no need to emit empty lexical block DIE.
for (const auto &E : DD->findImportedEntitiesForScope(DS))
Children.push_back(
constructImportedEntityDIE(DIImportedEntity(E.second)));
}
// If there are only other scopes as children, put them directly in the
// parent instead, as this scope would serve no purpose.
if (Children.size() == ChildScopeCount) {
@ -576,7 +580,8 @@ void DwarfCompileUnit::constructSubprogramScopeDIE(LexicalScope *Scope) {
// If we have more than one elements and the last one is null, it is a
// variadic function.
if (FnArgs.getNumElements() > 1 &&
!FnArgs.getElement(FnArgs.getNumElements() - 1))
!FnArgs.getElement(FnArgs.getNumElements() - 1) &&
!includeMinimalInlineScopes())
ScopeDIE.addChild(make_unique<DIE>(dwarf::DW_TAG_unspecified_parameters));
}
@ -603,11 +608,13 @@ DwarfCompileUnit::constructAbstractSubprogramScopeDIE(LexicalScope *Scope) {
DIE *ContextDIE;
if (includeMinimalInlineScopes())
ContextDIE = &getUnitDie();
// Some of this is duplicated from DwarfUnit::getOrCreateSubprogramDIE, with
// the important distinction that the DIDescriptor is not associated with the
// DIE (since the DIDescriptor will be associated with the concrete DIE, if
// any). It could be refactored to some common utility function.
if (DISubprogram SPDecl = SP.getFunctionDeclaration()) {
else if (DISubprogram SPDecl = SP.getFunctionDeclaration()) {
ContextDIE = &getUnitDie();
getOrCreateSubprogramDIE(SPDecl);
} else
@ -619,7 +626,7 @@ DwarfCompileUnit::constructAbstractSubprogramScopeDIE(LexicalScope *Scope) {
&createAndAddDIE(dwarf::DW_TAG_subprogram, *ContextDIE, DIDescriptor());
applySubprogramAttributesToDefinition(SP, *AbsDef);
if (getCUNode().getEmissionKind() != DIBuilder::LineTablesOnly)
if (!includeMinimalInlineScopes())
addUInt(*AbsDef, dwarf::DW_AT_inline, None, dwarf::DW_INL_inlined);
if (DIE *ObjectPointer = createAndAddScopeChildren(Scope, *AbsDef))
addDIEEntry(*AbsDef, dwarf::DW_AT_object_pointer, *ObjectPointer);
@ -660,7 +667,7 @@ void DwarfCompileUnit::finishSubprogramDefinition(DISubprogram SP) {
// If this subprogram has an abstract definition, reference that
addDIEEntry(*D, dwarf::DW_AT_abstract_origin, *AbsSPDIE);
} else {
if (!D && getCUNode().getEmissionKind() != DIBuilder::LineTablesOnly)
if (!D && !includeMinimalInlineScopes())
// Lazily construct the subprogram if we didn't see either concrete or
// inlined versions during codegen. (except in -gmlt ^ where we want
// to omit these entirely)
@ -703,7 +710,7 @@ void DwarfCompileUnit::emitHeader(const MCSymbol *ASectionSym) const {
/// addGlobalName - Add a new global name to the compile unit.
void DwarfCompileUnit::addGlobalName(StringRef Name, DIE &Die,
DIScope Context) {
if (getCUNode().getEmissionKind() == DIBuilder::LineTablesOnly)
if (includeMinimalInlineScopes())
return;
std::string FullName = getParentContextString(Context) + Name.str();
GlobalNames[FullName] = &Die;
@ -712,7 +719,7 @@ void DwarfCompileUnit::addGlobalName(StringRef Name, DIE &Die,
/// Add a new global type to the unit.
void DwarfCompileUnit::addGlobalType(DIType Ty, const DIE &Die,
DIScope Context) {
if (getCUNode().getEmissionKind() == DIBuilder::LineTablesOnly)
if (includeMinimalInlineScopes())
return;
std::string FullName = getParentContextString(Context) + Ty.getName().str();
GlobalTypes[FullName] = &Die;
@ -838,12 +845,16 @@ void DwarfCompileUnit::applySubprogramAttributesToDefinition(DISubprogram SP,
DIE &SPDie) {
DISubprogram SPDecl = SP.getFunctionDeclaration();
DIScope Context = resolve(SPDecl ? SPDecl.getContext() : SP.getContext());
applySubprogramAttributes(SP, SPDie, getCUNode().getEmissionKind() ==
DIBuilder::LineTablesOnly);
applySubprogramAttributes(SP, SPDie, includeMinimalInlineScopes());
addGlobalName(SP.getName(), SPDie, Context);
}
bool DwarfCompileUnit::isDwoUnit() const {
return DD->useSplitDwarf() && Skeleton;
}
bool DwarfCompileUnit::includeMinimalInlineScopes() const {
return getCUNode().getEmissionKind() == DIBuilder::LineTablesOnly ||
(DD->useSplitDwarf() && !Skeleton);
}
} // end llvm namespace

View File

@ -66,6 +66,8 @@ class DwarfCompileUnit : public DwarfUnit {
bool isDwoUnit() const override;
bool includeMinimalInlineScopes() const;
public:
DwarfCompileUnit(unsigned UID, DICompileUnit Node, AsmPrinter *A,
DwarfDebug *DW, DwarfFile *DWU);

View File

@ -330,6 +330,12 @@ bool DwarfDebug::isLexicalScopeDIENull(LexicalScope *Scope) {
return !getLabelAfterInsn(Ranges.front().second);
}
template <typename Func> void forBothCUs(DwarfCompileUnit &CU, Func F) {
F(CU);
if (auto *SkelCU = CU.getSkeleton())
F(*SkelCU);
}
void DwarfDebug::constructAbstractSubprogramScopeDIE(LexicalScope *Scope) {
assert(Scope && Scope->getScopeNode());
assert(Scope->isAbstractScope());
@ -341,7 +347,10 @@ void DwarfDebug::constructAbstractSubprogramScopeDIE(LexicalScope *Scope) {
// Find the subprogram's DwarfCompileUnit in the SPMap in case the subprogram
// was inlined from another compile unit.
SPMap[SP]->constructAbstractSubprogramScopeDIE(Scope);
auto &CU = SPMap[SP];
forBothCUs(*CU, [&](DwarfCompileUnit &CU) {
CU.constructAbstractSubprogramScopeDIE(Scope);
});
}
void DwarfDebug::addGnuPubAttributes(DwarfUnit &U, DIE &D) const {
@ -506,7 +515,9 @@ void DwarfDebug::finishVariableDefinitions() {
void DwarfDebug::finishSubprogramDefinitions() {
for (const auto &P : SPMap)
P.second->finishSubprogramDefinition(DISubprogram(P.first));
forBothCUs(*P.second, [&](DwarfCompileUnit &CU) {
CU.finishSubprogramDefinition(DISubprogram(P.first));
});
}
@ -1286,6 +1297,9 @@ void DwarfDebug::endFunction(const MachineFunction *MF) {
}
TheCU.constructSubprogramScopeDIE(FnScope);
if (auto *SkelCU = TheCU.getSkeleton())
if (!LScopes.getAbstractScopesList().empty())
SkelCU->constructSubprogramScopeDIE(FnScope);
// Clear debug info
// Ownership of DbgVariables is a bit subtle - ScopeVariables owns all the
@ -1984,6 +1998,9 @@ void DwarfDebug::emitDebugRanges() {
for (const auto &I : CUMap) {
DwarfCompileUnit *TheCU = I.second;
if (auto *Skel = TheCU->getSkeleton())
TheCU = Skel;
// Iterate over the misc ranges for the compile units in the module.
for (const RangeSpanList &List : TheCU->getRangeLists()) {
// Emit our symbol so we can find the beginning of the range.

View File

@ -1233,20 +1233,23 @@ DIE *DwarfUnit::getOrCreateNameSpace(DINameSpace NS) {
}
/// getOrCreateSubprogramDIE - Create new DIE using SP.
DIE *DwarfUnit::getOrCreateSubprogramDIE(DISubprogram SP) {
DIE *DwarfUnit::getOrCreateSubprogramDIE(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 = getOrCreateContextDIE(resolve(SP.getContext()));
DIE *ContextDIE =
Minimal ? &getUnitDie() : getOrCreateContextDIE(resolve(SP.getContext()));
if (DIE *SPDie = getDIE(SP))
return SPDie;
if (DISubprogram SPDecl = SP.getFunctionDeclaration()) {
// Add subprogram definitions to the CU die directly.
ContextDIE = &getUnitDie();
// Build the decl now to ensure it precedes the definition.
getOrCreateSubprogramDIE(SPDecl);
if (!Minimal) {
// Add subprogram definitions to the CU die directly.
ContextDIE = &getUnitDie();
// Build the decl now to ensure it precedes the definition.
getOrCreateSubprogramDIE(SPDecl);
}
}
// DW_TAG_inlined_subroutine may refer to this DIE.
@ -1261,8 +1264,8 @@ DIE *DwarfUnit::getOrCreateSubprogramDIE(DISubprogram SP) {
return &SPDie;
}
void DwarfUnit::applySubprogramAttributes(DISubprogram SP, DIE &SPDie,
bool Minimal) {
bool DwarfUnit::applySubprogramDefinitionAttributes(DISubprogram SP,
DIE &SPDie) {
DIE *DeclDie = nullptr;
StringRef DeclLinkageName;
if (DISubprogram SPDecl = SP.getFunctionDeclaration()) {
@ -1285,12 +1288,20 @@ void DwarfUnit::applySubprogramAttributes(DISubprogram SP, DIE &SPDie,
addString(SPDie, dwarf::DW_AT_MIPS_linkage_name,
GlobalValue::getRealLinkageName(LinkageName));
if (DeclDie) {
// Refer to the function declaration where all the other attributes will be
// found.
addDIEEntry(SPDie, dwarf::DW_AT_specification, *DeclDie);
return;
}
if (!DeclDie)
return false;
// Refer to the function declaration where all the other attributes will be
// found.
addDIEEntry(SPDie, dwarf::DW_AT_specification, *DeclDie);
return true;
}
void DwarfUnit::applySubprogramAttributes(DISubprogram SP, DIE &SPDie,
bool Minimal) {
if (!Minimal)
if (applySubprogramDefinitionAttributes(SP, SPDie))
return;
// Constructors and operators for anonymous aggregates do not have names.
if (!SP.getName().empty())

View File

@ -127,6 +127,8 @@ protected:
void addIndexedString(DIE &Die, dwarf::Attribute Attribute, StringRef Str);
bool applySubprogramDefinitionAttributes(DISubprogram SP, DIE &SPDie);
public:
virtual ~DwarfUnit();
@ -278,7 +280,7 @@ public:
DIE *getOrCreateNameSpace(DINameSpace NS);
/// getOrCreateSubprogramDIE - Create new DIE using SP.
DIE *getOrCreateSubprogramDIE(DISubprogram SP);
DIE *getOrCreateSubprogramDIE(DISubprogram SP, bool Minimal = false);
void applySubprogramAttributes(DISubprogram SP, DIE &SPDie,
bool Minimal = false);

View File

@ -0,0 +1,119 @@
; RUN: llc -split-dwarf=Enable -O0 < %s -mtriple=x86_64-unknown-linux-gnu -filetype=obj | llvm-dwarfdump -debug-dump=info - | FileCheck %s
; Test the emission of gmlt-like inlining information into the skeleton unit.
; This allows inline-aware symbolication/backtracing given only the linked
; executable, without needing access to the .dwos.
; A simple example of inlining generated with clang -gsplit-dwarf
; A member function is used to force emission of the declaration of the
; function into the .dwo file, which may be shared with other CUs in the dwo ;
; under fission, but should not be shared with the skeleton's CU. This also
; tests the general case of context emission, which is suppressed in gmlt-like
; data.
; Include a template just to test template parameters are not emitted in
; gmlt-like data.
; And some varargs to make sure DW_TAG_unspecified_parameters is not emitted.
; And a using declaration in a nested lexical_block... because that shouldn't
; be emitted either.
; Minor complication: after generating the LLVM IR, it was manually edited so
; that the 'f1()' call from f3 was reordered to appear between the two inlined
; f1 calls from f2. This causes f2's inlined_subroutine to use DW_AT_ranges,
; thus exercising range list generation/referencing which was buggy.
; struct foo {
; template<typename T>
; static void f2();
; static void f3(...);
; };
;
; void f1();
;
; template<typename T>
; inline __attribute__((always_inline)) void foo::f2() {
; f1();
; f1();
; }
;
; void foo::f3(...) {
; if (true) {
; f1();
; f2<int>();
; using ::foo;
; }
; }
; Check that we emit the usual gmlt-like data for this file, including brief
; descriptions of subprograms with inlined scopes.
; FIXME: Once tools support indexed addresses in the skeleton CU, we should use
; those (DW_FORM_addr would become DW_FORM_GNU_addr_index below) since those
; addresses will already be in the address pool anyway.
; CHECK: DW_TAG_subprogram
; CHECK-NEXT: DW_AT_name {{.*}} "f2<int>"
; CHECK-NOT: DW_
; CHECK: DW_TAG_subprogram
; CHECK-NEXT: DW_AT_low_pc [DW_FORM_addr]
; CHECK-NEXT: DW_AT_high_pc
; CHECK-NEXT: DW_AT_name {{.*}} "f3"
; CHECK-NOT: {{DW_|NULL}}
; CHECK: DW_TAG_inlined_subroutine
; CHECK-NEXT: DW_AT_abstract_origin {{.*}} "f2<int>"
; CHECK-NEXT: DW_AT_ranges
; CHECK-NEXT: DW_AT_call_file
; CHECK-NEXT: DW_AT_call_line {{.*}} (18)
; CHECK-NOT: DW_
; Function Attrs: uwtable
define void @_ZN3foo2f3Ez(...) #0 align 2 {
entry:
call void @_Z2f1v(), !dbg !26
call void @_Z2f1v(), !dbg !25
call void @_Z2f1v(), !dbg !28
ret void, !dbg !29
}
declare void @_Z2f1v() #1
attributes #0 = { uwtable "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" }
attributes #1 = { "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" }
!llvm.dbg.cu = !{!0}
!llvm.module.flags = !{!22, !23}
!llvm.ident = !{!24}
!0 = metadata !{metadata !"0x11\004\00clang version 3.6.0 \000\00\000\00fission-inline.dwo\001", metadata !1, metadata !2, metadata !3, metadata !9, metadata !2, metadata !18} ; [ DW_TAG_compile_unit ] [/tmp/dbginfo/fission-inline.cpp] [DW_LANG_C_plus_plus]
!1 = metadata !{metadata !"fission-inline.cpp", metadata !"/tmp/dbginfo"}
!2 = metadata !{}
!3 = metadata !{metadata !4}
!4 = metadata !{metadata !"0x13\00foo\001\008\008\000\000\000", metadata !1, null, null, metadata !5, null, null, metadata !"_ZTS3foo"} ; [ DW_TAG_structure_type ] [foo] [line 1, size 8, align 8, offset 0] [def] [from ]
!5 = metadata !{metadata !6}
!6 = metadata !{metadata !"0x2e\00f3\00f3\00_ZN3foo2f3Ez\004\000\000\000\000\00256\000\004", metadata !1, metadata !"_ZTS3foo", metadata !7, null, null, null, null, null} ; [ DW_TAG_subprogram ] [line 4] [f3]
!7 = metadata !{metadata !"0x15\00\000\000\000\000\000\000", null, null, null, metadata !8, null, null, null} ; [ DW_TAG_subroutine_type ] [line 0, size 0, align 0, offset 0] [from ]
!8 = metadata !{null, null}
!9 = metadata !{metadata !10, metadata !11}
!10 = metadata !{metadata !"0x2e\00f3\00f3\00_ZN3foo2f3Ez\0015\000\001\000\000\00256\000\0015", metadata !1, metadata !"_ZTS3foo", metadata !7, null, void (...)* @_ZN3foo2f3Ez, null, metadata !6, metadata !2} ; [ DW_TAG_subprogram ] [line 15] [def] [f3]
!11 = metadata !{metadata !"0x2e\00f2<int>\00f2<int>\00_ZN3foo2f2IiEEvv\0010\000\001\000\000\00256\000\0010", metadata !1, metadata !"_ZTS3foo", metadata !12, null, null, metadata !14, metadata !17, metadata !2} ; [ DW_TAG_subprogram ] [line 10] [def] [f2<int>]
!12 = metadata !{metadata !"0x15\00\000\000\000\000\000\000", null, null, null, metadata !13, null, null, null} ; [ DW_TAG_subroutine_type ] [line 0, size 0, align 0, offset 0] [from ]
!13 = metadata !{null}
!14 = metadata !{metadata !15}
!15 = metadata !{metadata !"0x2f\00T\000\000", null, metadata !16, null} ; [ DW_TAG_template_type_parameter ]
!16 = metadata !{metadata !"0x24\00int\000\0032\0032\000\000\005", null, null} ; [ DW_TAG_base_type ] [int] [line 0, size 32, align 32, offset 0, enc DW_ATE_signed]
!17 = metadata !{metadata !"0x2e\00f2<int>\00f2<int>\00_ZN3foo2f2IiEEvv\0010\000\000\000\000\00256\000\0010", metadata !1, metadata !"_ZTS3foo", metadata !12, null, null, metadata !14, null, null} ; [ DW_TAG_subprogram ] [line 10] [f2<int>]
!18 = metadata !{metadata !19}
!19 = metadata !{metadata !"0x8\0019\00", metadata !20, metadata !"_ZTS3foo"} ; [ DW_TAG_imported_declaration ]
!20 = metadata !{metadata !"0xb\0016\0013\001", metadata !1, metadata !21} ; [ DW_TAG_lexical_block ] [/tmp/dbginfo/fission-inline.cpp]
!21 = metadata !{metadata !"0xb\0016\007\000", metadata !1, metadata !10} ; [ DW_TAG_lexical_block ] [/tmp/dbginfo/fission-inline.cpp]
!22 = metadata !{i32 2, metadata !"Dwarf Version", i32 4}
!23 = metadata !{i32 2, metadata !"Debug Info Version", i32 2}
!24 = metadata !{metadata !"clang version 3.6.0 "}
!25 = metadata !{i32 17, i32 5, metadata !20, null}
!26 = metadata !{i32 11, i32 3, metadata !11, metadata !27}
!27 = metadata !{i32 18, i32 5, metadata !20, null}
!28 = metadata !{i32 12, i32 3, metadata !11, metadata !27}
!29 = metadata !{i32 21, i32 1, metadata !10, null}

View File

@ -82,7 +82,8 @@
; CHECK-NOT: type_signature
; CHECK-LABEL: type_signature = 0x1d02f3be30cc5688
; CHECK: DW_TAG_structure_type
; CHECK-NEXT: DW_AT_name{{.*}}"bar"
; FISSION-NEXT: DW_AT_name {{.*}} ( indexed {{.*}} "bar"
; SINGLE-NEXT: DW_AT_name {{.*}} "bar"
; Check that we generate a hash for fluffy and the value.