summaryrefslogtreecommitdiffstats
path: root/llvm/lib
diff options
context:
space:
mode:
authorVedant Kumar <vsk@apple.com>2018-10-05 20:37:17 +0000
committerVedant Kumar <vsk@apple.com>2018-10-05 20:37:17 +0000
commit5931b4e5b5de31bd366cbeb773f53357a0985e68 (patch)
treec94a56afe545ae7bee551621c391f27a1abc94f5 /llvm/lib
parentf84ece68ca3e0630500ae2dd2a383cb3fa127568 (diff)
downloadbcm5719-llvm-5931b4e5b5de31bd366cbeb773f53357a0985e68.tar.gz
bcm5719-llvm-5931b4e5b5de31bd366cbeb773f53357a0985e68.zip
[DebugInfo] Add support for DWARF5 call site-related attributes
DWARF v5 introduces DW_AT_call_all_calls, a subprogram attribute which indicates that all calls (both regular and tail) within the subprogram have call site entries. The information within these call site entries can be used by a debugger to populate backtraces with synthetic tail call frames. Tail calling frames go missing in backtraces because the frame of the caller is reused by the callee. Call site entries allow a debugger to reconstruct a sequence of (tail) calls which led from one function to another. This improves backtrace quality. There are limitations: tail recursion isn't handled, variables within synthetic frames may not survive to be inspected, etc. This approach is not novel, see: https://gcc.gnu.org/wiki/summit2010?action=AttachFile&do=get&target=jelinek.pdf This patch adds an IR-level flag (DIFlagAllCallsDescribed) which lowers to DW_AT_call_all_calls. It adds the minimal amount of DWARF generation support needed to emit standards-compliant call site entries. For easier deployment, when the debugger tuning is LLDB, the DWARF requirement is adjusted to v4. Testing: Apart from check-{llvm, clang}, I built a stage2 RelWithDebInfo clang binary. Its dSYM passed verification and grew by 1.4% compared to the baseline. 151,879 call site entries were added. rdar://42001377 Differential Revision: https://reviews.llvm.org/D49887 llvm-svn: 343883
Diffstat (limited to 'llvm/lib')
-rw-r--r--llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp31
-rw-r--r--llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.h10
-rw-r--r--llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp68
-rw-r--r--llvm/lib/CodeGen/AsmPrinter/DwarfDebug.h4
-rw-r--r--llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp33
-rw-r--r--llvm/lib/IR/Verifier.cpp4
6 files changed, 147 insertions, 3 deletions
diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp
index d8d6f76d71e..443c8879f13 100644
--- a/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp
@@ -730,7 +730,8 @@ DIE *DwarfCompileUnit::createScopeChildrenDIE(LexicalScope *Scope,
return ObjectPointer;
}
-void DwarfCompileUnit::constructSubprogramScopeDIE(const DISubprogram *Sub, LexicalScope *Scope) {
+DIE &DwarfCompileUnit::constructSubprogramScopeDIE(const DISubprogram *Sub,
+ LexicalScope *Scope) {
DIE &ScopeDIE = updateSubprogramScopeDIE(Sub);
if (Scope) {
@@ -753,6 +754,8 @@ void DwarfCompileUnit::constructSubprogramScopeDIE(const DISubprogram *Sub, Lexi
!includeMinimalInlineScopes())
ScopeDIE.addChild(
DIE::get(DIEValueAllocator, dwarf::DW_TAG_unspecified_parameters));
+
+ return ScopeDIE;
}
DIE *DwarfCompileUnit::createAndAddScopeChildren(LexicalScope *Scope,
@@ -807,6 +810,32 @@ void DwarfCompileUnit::constructAbstractSubprogramScopeDIE(
ContextCU->addDIEEntry(*AbsDef, dwarf::DW_AT_object_pointer, *ObjectPointer);
}
+DIE &DwarfCompileUnit::constructCallSiteEntryDIE(DIE &ScopeDIE,
+ const DISubprogram &CalleeSP,
+ bool IsTail,
+ const MCSymbol *ReturnPC) {
+ // Insert a call site entry DIE within ScopeDIE.
+ DIE &CallSiteDIE =
+ createAndAddDIE(dwarf::DW_TAG_call_site, ScopeDIE, nullptr);
+
+ // For the purposes of showing tail call frames in backtraces, a key piece of
+ // information is DW_AT_call_origin, a pointer to the callee DIE.
+ DIE *CalleeDIE = getOrCreateSubprogramDIE(&CalleeSP);
+ assert(CalleeDIE && "Could not create DIE for call site entry origin");
+ addDIEEntry(CallSiteDIE, dwarf::DW_AT_call_origin, *CalleeDIE);
+
+ if (IsTail) {
+ // Attach DW_AT_call_tail_call to tail calls for standards compliance.
+ addFlag(CallSiteDIE, dwarf::DW_AT_call_tail_call);
+ } else {
+ // Attach the return PC to allow the debugger to disambiguate call paths
+ // from one function to another.
+ assert(ReturnPC && "Missing return PC information for a call");
+ addLabelAddress(CallSiteDIE, dwarf::DW_AT_call_return_pc, ReturnPC);
+ }
+ return CallSiteDIE;
+}
+
DIE *DwarfCompileUnit::constructImportedEntityDIE(
const DIImportedEntity *Module) {
DIE *IMDie = DIE::get(DIEValueAllocator, (dwarf::Tag)Module->getTag());
diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.h b/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.h
index 9bf1ce18b3a..6389ccd686d 100644
--- a/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.h
+++ b/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.h
@@ -203,12 +203,20 @@ public:
bool *HasNonScopeChildren = nullptr);
/// Construct a DIE for this subprogram scope.
- void constructSubprogramScopeDIE(const DISubprogram *Sub, LexicalScope *Scope);
+ DIE &constructSubprogramScopeDIE(const DISubprogram *Sub,
+ LexicalScope *Scope);
DIE *createAndAddScopeChildren(LexicalScope *Scope, DIE &ScopeDIE);
void constructAbstractSubprogramScopeDIE(LexicalScope *Scope);
+ /// Construct a call site entry DIE describing a call within \p Scope to a
+ /// callee described by \p CalleeSP. \p IsTail specifies whether the call is
+ /// a tail call. \p ReturnPC must be non-null for non-tail calls and point
+ /// to the PC value after the call returns.
+ DIE &constructCallSiteEntryDIE(DIE &ScopeDIE, const DISubprogram &CalleeSP,
+ bool IsTail, const MCSymbol *ReturnPC);
+
/// Construct import_module DIE.
DIE *constructImportedEntityDIE(const DIImportedEntity *Module);
diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
index ede606131ff..ab3559d63cc 100644
--- a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
@@ -39,6 +39,7 @@
#include "llvm/CodeGen/MachineInstr.h"
#include "llvm/CodeGen/MachineModuleInfo.h"
#include "llvm/CodeGen/MachineOperand.h"
+#include "llvm/CodeGen/TargetInstrInfo.h"
#include "llvm/CodeGen/TargetRegisterInfo.h"
#include "llvm/CodeGen/TargetSubtargetInfo.h"
#include "llvm/IR/Constants.h"
@@ -502,6 +503,63 @@ void DwarfDebug::constructAbstractSubprogramScopeDIE(DwarfCompileUnit &SrcCU,
}
}
+void DwarfDebug::constructCallSiteEntryDIEs(const DISubprogram &SP,
+ DwarfCompileUnit &CU, DIE &ScopeDIE,
+ const MachineFunction &MF) {
+ // Add a call site-related attribute (DWARF5, Sec. 3.3.1.3). Do this only if
+ // the subprogram is required to have one.
+ if (!SP.areAllCallsDescribed() || !SP.isDefinition())
+ return;
+
+ // Use DW_AT_call_all_calls to express that call site entries are present
+ // for both tail and non-tail calls. Don't use DW_AT_call_all_source_calls
+ // because one of its requirements is not met: call site entries for
+ // optimized-out calls are elided.
+ CU.addFlag(ScopeDIE, dwarf::DW_AT_call_all_calls);
+
+ const TargetInstrInfo *TII = MF.getSubtarget().getInstrInfo();
+ assert(TII && "TargetInstrInfo not found: cannot label tail calls");
+
+ // Emit call site entries for each call or tail call in the function.
+ for (const MachineBasicBlock &MBB : MF) {
+ for (const MachineInstr &MI : MBB.instrs()) {
+ // Skip instructions which aren't calls. Both calls and tail-calling jump
+ // instructions (e.g TAILJMPd64) are classified correctly here.
+ if (!MI.isCall())
+ continue;
+
+ // TODO: Add support for targets with delay slots (see: beginInstruction).
+ if (MI.hasDelaySlot())
+ return;
+
+ // If this is a direct call, find the callee's subprogram.
+ const MachineOperand &CalleeOp = MI.getOperand(0);
+ if (!CalleeOp.isGlobal())
+ continue;
+ const Function *CalleeDecl = dyn_cast<Function>(CalleeOp.getGlobal());
+ if (!CalleeDecl || !CalleeDecl->getSubprogram())
+ continue;
+
+ // TODO: Omit call site entries for runtime calls (objc_msgSend, etc).
+ // TODO: Add support for indirect calls.
+
+ bool IsTail = TII->isTailCall(MI);
+
+ // For tail calls, no return PC information is needed. For regular calls,
+ // the return PC is needed to disambiguate paths in the call graph which
+ // could lead to some target function.
+ const MCSymbol *ReturnPC = IsTail ? nullptr : getLabelAfterInsn(&MI);
+
+ assert((IsTail || ReturnPC) && "Call without return PC information");
+ LLVM_DEBUG(dbgs() << "CallSiteEntry: " << MF.getName() << " -> "
+ << CalleeDecl->getName() << (IsTail ? " [tail]" : "")
+ << "\n");
+ CU.constructCallSiteEntryDIE(ScopeDIE, *CalleeDecl->getSubprogram(),
+ IsTail, ReturnPC);
+ }
+ }
+}
+
void DwarfDebug::addGnuPubAttributes(DwarfCompileUnit &U, DIE &D) const {
if (!U.hasDwarfPubSections())
return;
@@ -1376,6 +1434,11 @@ void DwarfDebug::beginInstruction(const MachineInstr *MI) {
unsigned LastAsmLine =
Asm->OutStreamer->getContext().getCurrentDwarfLoc().getLine();
+ // Request a label after the call in order to emit AT_return_pc information
+ // in call site entries. TODO: Add support for targets with delay slots.
+ if (SP->areAllCallsDescribed() && MI->isCall() && !MI->hasDelaySlot())
+ requestLabelAfterInsn(MI);
+
if (DL == PrevInstLoc) {
// If we have an ongoing unspecified location, nothing to do here.
if (!DL)
@@ -1546,12 +1609,15 @@ void DwarfDebug::endFunctionImpl(const MachineFunction *MF) {
}
ProcessedSPNodes.insert(SP);
- TheCU.constructSubprogramScopeDIE(SP, FnScope);
+ DIE &ScopeDIE = TheCU.constructSubprogramScopeDIE(SP, FnScope);
if (auto *SkelCU = TheCU.getSkeleton())
if (!LScopes.getAbstractScopesList().empty() &&
TheCU.getCUNode()->getSplitDebugInlining())
SkelCU->constructSubprogramScopeDIE(SP, FnScope);
+ // Construct call site entries.
+ constructCallSiteEntryDIEs(*SP, TheCU, ScopeDIE, *MF);
+
// Clear debug info
// Ownership of DbgVariables is a bit subtle - ScopeVariables owns all the
// DbgVariables except those that are also in AbstractVariables (since they
diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.h b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.h
index ba40ad28eb7..e115eb771fb 100644
--- a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.h
+++ b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.h
@@ -424,6 +424,10 @@ class DwarfDebug : public DebugHandlerBase {
/// Construct a DIE for this abstract scope.
void constructAbstractSubprogramScopeDIE(DwarfCompileUnit &SrcCU, LexicalScope *Scope);
+ /// Construct DIEs for call site entries describing the calls in \p MF.
+ void constructCallSiteEntryDIEs(const DISubprogram &SP, DwarfCompileUnit &CU,
+ DIE &ScopeDIE, const MachineFunction &MF);
+
template <typename DataT>
void addAccelNameImpl(const DICompileUnit &CU, AccelTable<DataT> &AppleAccel,
StringRef Name, const DIE &Die);
diff --git a/llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp b/llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp
index 20259db7e47..c433fe470cb 100644
--- a/llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp
+++ b/llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp
@@ -194,6 +194,7 @@ unsigned DWARFVerifier::verifyUnitContents(DWARFUnit &Unit) {
dump(Die) << '\n';
NumUnitErrors++;
}
+ NumUnitErrors += verifyDebugInfoCallSite(Die);
}
DWARFDie Die = Unit.getUnitDIE(/* ExtractUnitDIEOnly = */ false);
@@ -223,6 +224,38 @@ unsigned DWARFVerifier::verifyUnitContents(DWARFUnit &Unit) {
return NumUnitErrors;
}
+unsigned DWARFVerifier::verifyDebugInfoCallSite(const DWARFDie &Die) {
+ if (Die.getTag() != DW_TAG_call_site)
+ return 0;
+
+ DWARFDie Curr = Die.getParent();
+ for (; Curr.isValid() && !Curr.isSubprogramDIE(); Curr = Die.getParent()) {
+ if (Curr.getTag() == DW_TAG_inlined_subroutine) {
+ error() << "Call site entry nested within inlined subroutine:";
+ Curr.dump(OS);
+ return 1;
+ }
+ }
+
+ if (!Curr.isValid()) {
+ error() << "Call site entry not nested within a valid subprogram:";
+ Die.dump(OS);
+ return 1;
+ }
+
+ Optional<DWARFFormValue> CallAttr =
+ Curr.find({DW_AT_call_all_calls, DW_AT_call_all_source_calls,
+ DW_AT_call_all_tail_calls});
+ if (!CallAttr) {
+ error() << "Subprogram with call site entry has no DW_AT_call attribute:";
+ Curr.dump(OS);
+ Die.dump(OS, /*indent*/ 1);
+ return 1;
+ }
+
+ return 0;
+}
+
unsigned DWARFVerifier::verifyAbbrevSection(const DWARFDebugAbbrev *Abbrev) {
unsigned NumErrors = 0;
if (Abbrev) {
diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp
index cd032a840b0..84e9c6f9100 100644
--- a/llvm/lib/IR/Verifier.cpp
+++ b/llvm/lib/IR/Verifier.cpp
@@ -1119,6 +1119,10 @@ void Verifier::visitDISubprogram(const DISubprogram &N) {
AssertDI(Op && isa<DIType>(Op), "invalid thrown type", &N, ThrownTypes,
Op);
}
+
+ if (N.areAllCallsDescribed())
+ AssertDI(N.isDefinition(),
+ "DIFlagAllCallsDescribed must be attached to a definition");
}
void Verifier::visitDILexicalBlockBase(const DILexicalBlockBase &N) {
OpenPOWER on IntegriCloud