From 5931b4e5b5de31bd366cbeb773f53357a0985e68 Mon Sep 17 00:00:00 2001 From: Vedant Kumar Date: Fri, 5 Oct 2018 20:37:17 +0000 Subject: [PATCH] [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 --- clang/lib/CodeGen/CGDebugInfo.cpp | 24 +- clang/lib/CodeGen/CGDebugInfo.h | 5 + .../CodeGenCXX/dbg-info-all-calls-described.cpp | 61 ++++ llvm/include/llvm/DebugInfo/DWARF/DWARFVerifier.h | 8 + llvm/include/llvm/IR/DebugInfoFlags.def | 3 +- llvm/include/llvm/IR/DebugInfoMetadata.h | 3 + llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp | 31 +- llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.h | 10 +- llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp | 68 +++- llvm/lib/CodeGen/AsmPrinter/DwarfDebug.h | 4 + llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp | 33 ++ llvm/lib/IR/Verifier.cpp | 4 + .../DebugInfo/Generic/callsite-attr-invalid.ll | 48 +++ .../DebugInfo/X86/dwarf-callsite-related-attrs.ll | 133 ++++++++ .../tools/llvm-dwarfdump/X86/callsite-invalid.s | 376 +++++++++++++++++++++ llvm/tools/llvm-dwarfdump/Statistics.cpp | 8 + 16 files changed, 814 insertions(+), 5 deletions(-) create mode 100644 clang/test/CodeGenCXX/dbg-info-all-calls-described.cpp create mode 100644 llvm/test/DebugInfo/Generic/callsite-attr-invalid.ll create mode 100644 llvm/test/DebugInfo/X86/dwarf-callsite-related-attrs.ll create mode 100644 llvm/test/tools/llvm-dwarfdump/X86/callsite-invalid.s diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp index c87d746..bcd5338 100644 --- a/clang/lib/CodeGen/CGDebugInfo.cpp +++ b/clang/lib/CodeGen/CGDebugInfo.cpp @@ -3168,6 +3168,7 @@ llvm::DISubprogram *CGDebugInfo::getFunctionFwdDeclOrStub(GlobalDecl GD, QualType FnType = CGM.getContext().getFunctionType( FD->getReturnType(), ArgTypes, FunctionProtoType::ExtProtoInfo(CC)); if (Stub) { + Flags |= getCallSiteRelatedAttrs(); return DBuilder.createFunction( DContext, Name, LinkageName, Unit, Line, getOrCreateFunctionType(GD.getDecl(), FnType, Unit), @@ -3407,6 +3408,8 @@ void CGDebugInfo::EmitFunctionStart(GlobalDecl GD, SourceLocation Loc, if (CurFuncIsThunk) Flags |= llvm::DINode::FlagThunk; + llvm::DINode::DIFlags FlagsForDef = Flags | getCallSiteRelatedAttrs(); + unsigned LineNo = getLineNumber(Loc); unsigned ScopeLine = getLineNumber(ScopeLoc); @@ -3418,7 +3421,7 @@ void CGDebugInfo::EmitFunctionStart(GlobalDecl GD, SourceLocation Loc, llvm::DISubprogram *SP = DBuilder.createFunction( FDContext, Name, LinkageName, Unit, LineNo, getOrCreateFunctionType(D, FnType, Unit), Fn->hasLocalLinkage(), - true /*definition*/, ScopeLine, Flags, CGM.getLangOpts().Optimize, + true /*definition*/, ScopeLine, FlagsForDef, CGM.getLangOpts().Optimize, TParamsArray.get(), getFunctionDeclaration(D)); Fn->setSubprogram(SP); // We might get here with a VarDecl in the case we're generating @@ -4422,3 +4425,22 @@ llvm::DebugLoc CGDebugInfo::SourceLocToDebugLoc(SourceLocation Loc) { llvm::MDNode *Scope = LexicalBlockStack.back(); return llvm::DebugLoc::get(getLineNumber(Loc), getColumnNumber(Loc), Scope); } + +llvm::DINode::DIFlags CGDebugInfo::getCallSiteRelatedAttrs() const { + // Call site-related attributes are only useful in optimized programs, and + // when there's a possibility of debugging backtraces. + if (!CGM.getLangOpts().Optimize || DebugKind == codegenoptions::NoDebugInfo || + DebugKind == codegenoptions::LocTrackingOnly) + return llvm::DINode::FlagZero; + + // Call site-related attributes are available in DWARF v5. Some debuggers, + // while not fully DWARF v5-compliant, may accept these attributes as if they + // were part of DWARF v4. + bool SupportsDWARFv4Ext = + CGM.getCodeGenOpts().DwarfVersion == 4 && + CGM.getCodeGenOpts().getDebuggerTuning() == llvm::DebuggerKind::LLDB; + if (!SupportsDWARFv4Ext && CGM.getCodeGenOpts().DwarfVersion < 5) + return llvm::DINode::FlagZero; + + return llvm::DINode::FlagAllCallsDescribed; +} diff --git a/clang/lib/CodeGen/CGDebugInfo.h b/clang/lib/CodeGen/CGDebugInfo.h index b007621..aceb91d 100644 --- a/clang/lib/CodeGen/CGDebugInfo.h +++ b/clang/lib/CodeGen/CGDebugInfo.h @@ -608,6 +608,11 @@ private: unsigned LineNo, StringRef LinkageName, llvm::GlobalVariable *Var, llvm::DIScope *DContext); + + /// Return flags which enable debug info emission for call sites, provided + /// that it is supported and enabled. + llvm::DINode::DIFlags getCallSiteRelatedAttrs() const; + /// Get the printing policy for producing names for debug info. PrintingPolicy getPrintingPolicy() const; diff --git a/clang/test/CodeGenCXX/dbg-info-all-calls-described.cpp b/clang/test/CodeGenCXX/dbg-info-all-calls-described.cpp new file mode 100644 index 0000000..2fc6533 --- /dev/null +++ b/clang/test/CodeGenCXX/dbg-info-all-calls-described.cpp @@ -0,0 +1,61 @@ +// Test that call site debug info is (un)supported in various configurations. + +// Supported: DWARF5, -O1, standalone DI +// RUN: %clang_cc1 -emit-llvm -triple %itanium_abi_triple %s -o - \ +// RUN: -O1 -disable-llvm-passes \ +// RUN: -debug-info-kind=standalone -dwarf-version=5 \ +// RUN: | FileCheck %s -check-prefix=HAS-ATTR \ +// RUN: -implicit-check-not=DISubprogram -implicit-check-not=DIFlagAllCallsDescribed + +// Supported: DWARF4 + LLDB tuning, -O1, limited DI +// RUN: %clang_cc1 -emit-llvm -triple %itanium_abi_triple %s -o - \ +// RUN: -O1 -disable-llvm-passes \ +// RUN: -debugger-tuning=lldb \ +// RUN: -debug-info-kind=standalone -dwarf-version=4 \ +// RUN: | FileCheck %s -check-prefix=HAS-ATTR \ +// RUN: -implicit-check-not=DISubprogram -implicit-check-not=DIFlagAllCallsDescribed + +// Supported: DWARF4 + LLDB tuning, -O1, line-tables only DI +// RUN: %clang_cc1 -emit-llvm -triple %itanium_abi_triple %s -o - \ +// RUN: -O1 -disable-llvm-passes \ +// RUN: -debugger-tuning=lldb \ +// RUN: -debug-info-kind=line-tables-only -dwarf-version=4 \ +// RUN: | FileCheck %s -check-prefix=LINE-TABLES-ONLY + +// Unsupported: -O0 +// RUN: %clang_cc1 -emit-llvm -triple %itanium_abi_triple %s -o - \ +// RUN: -O0 \ +// RUN: -debug-info-kind=standalone -dwarf-version=5 \ +// RUN: | FileCheck %s -check-prefix=NO-ATTR + +// Unsupported: DWARF4 +// RUN: %clang_cc1 -emit-llvm -triple %itanium_abi_triple %s -o - \ +// RUN: -O1 -disable-llvm-passes \ +// RUN: -debug-info-kind=standalone -dwarf-version=4 \ +// RUN: | FileCheck %s -check-prefix=NO-ATTR + +// NO-ATTR-NOT: FlagAllCallsDescribed + +// HAS-ATTR-DAG: DISubprogram(name: "declaration2", {{.*}}, isDefinition: true, {{.*}}, flags: DIFlagPrototyped | DIFlagAllCallsDescribed +// HAS-ATTR-DAG: DISubprogram(name: "struct1", {{.*}}, isDefinition: false, {{.*}}, flags: DIFlagPrototyped +// HAS-ATTR-DAG: DISubprogram(name: "struct1", {{.*}}, isDefinition: true, {{.*}}, flags: DIFlagPrototyped | DIFlagAllCallsDescribed +// HAS-ATTR-DAG: DISubprogram(name: "method1", {{.*}}, isDefinition: true, {{.*}}, flags: DIFlagPrototyped | DIFlagAllCallsDescribed +// HAS-ATTR-DAG: DISubprogram(name: "force_irgen", {{.*}}, isDefinition: true, {{.*}}, flags: DIFlagPrototyped | DIFlagAllCallsDescribed + +// LINE-TABLES-ONLY: DISubprogram(name: "force_irgen", {{.*}}, isDefinition: true, {{.*}}, flags: DIFlagPrototyped | DIFlagAllCallsDescribed + +void declaration1(); + +void declaration2(); + +void declaration2() {} + +struct struct1 { + struct1() {} + void method1() {} +}; + +void __attribute__((optnone)) force_irgen() { + declaration1(); + struct1().method1(); +} diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFVerifier.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFVerifier.h index 6de7fc7..3ad65cf 100644 --- a/llvm/include/llvm/DebugInfo/DWARF/DWARFVerifier.h +++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFVerifier.h @@ -148,6 +148,8 @@ private: /// - That the root DIE is a unit DIE. /// - If a unit type is provided, that the unit DIE matches the unit type. /// - The DIE ranges. + /// - That call site entries are only nested within subprograms with a + /// DW_AT_call attribute. /// /// \param Unit The DWARF Unit to verify. /// @@ -164,6 +166,12 @@ private: unsigned verifyUnitSection(const DWARFSection &S, DWARFSectionKind SectionKind); + /// Verifies that a call site entry is nested within a subprogram with a + /// DW_AT_call attribute. + /// + /// \returns Number of errors that occurred during verification. + unsigned verifyDebugInfoCallSite(const DWARFDie &Die); + /// Verify that all Die ranges are valid. /// /// This function currently checks for: diff --git a/llvm/include/llvm/IR/DebugInfoFlags.def b/llvm/include/llvm/IR/DebugInfoFlags.def index 53fa32d..a3c3430 100644 --- a/llvm/include/llvm/IR/DebugInfoFlags.def +++ b/llvm/include/llvm/IR/DebugInfoFlags.def @@ -50,6 +50,7 @@ HANDLE_DI_FLAG((1 << 25), Thunk) HANDLE_DI_FLAG((1 << 26), Trivial) HANDLE_DI_FLAG((1 << 27), BigEndian) HANDLE_DI_FLAG((1 << 28), LittleEndian) +HANDLE_DI_FLAG((1 << 29), AllCallsDescribed) // To avoid needing a dedicated value for IndirectVirtualBase, we use // the bitwise or of Virtual and FwdDecl, which does not otherwise @@ -59,7 +60,7 @@ HANDLE_DI_FLAG((1 << 2) | (1 << 5), IndirectVirtualBase) #ifdef DI_FLAG_LARGEST_NEEDED // intended to be used with ADT/BitmaskEnum.h // NOTE: always must be equal to largest flag, check this when adding new flag -HANDLE_DI_FLAG((1 << 28), Largest) +HANDLE_DI_FLAG((1 << 29), Largest) #undef DI_FLAG_LARGEST_NEEDED #endif diff --git a/llvm/include/llvm/IR/DebugInfoMetadata.h b/llvm/include/llvm/IR/DebugInfoMetadata.h index e512e43..2a9181e 100644 --- a/llvm/include/llvm/IR/DebugInfoMetadata.h +++ b/llvm/include/llvm/IR/DebugInfoMetadata.h @@ -1736,6 +1736,9 @@ public: } bool isExplicit() const { return getFlags() & FlagExplicit; } bool isPrototyped() const { return getFlags() & FlagPrototyped; } + bool areAllCallsDescribed() const { + return getFlags() & FlagAllCallsDescribed; + } bool isMainSubprogram() const { return getFlags() & FlagMainSubprogram; } /// Check if this is reference-qualified. diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp index d8d6f76..443c887 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 9bf1ce1..6389ccd 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 ede6061..ab3559d 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(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 ba40ad2..e115eb7 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 void addAccelNameImpl(const DICompileUnit &CU, AccelTable &AppleAccel, StringRef Name, const DIE &Die); diff --git a/llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp b/llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp index 20259db..c433fe4 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 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 cd032a8..84e9c6f 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(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) { diff --git a/llvm/test/DebugInfo/Generic/callsite-attr-invalid.ll b/llvm/test/DebugInfo/Generic/callsite-attr-invalid.ll new file mode 100644 index 0000000..b8ddd8d --- /dev/null +++ b/llvm/test/DebugInfo/Generic/callsite-attr-invalid.ll @@ -0,0 +1,48 @@ +; RUN: opt -verify < %s 2>&1 | FileCheck %s + +; CHECK: DIFlagAllCallsDescribed must be attached to a definition +; CHECK: warning: ignoring invalid debug info + +; Source: +; struct A { ~A(); }; +; void foo() { A x; } + +%struct.A = type { i8 } + +define void @_Z3foov() !dbg !8 { +entry: + %x = alloca %struct.A, align 1 + call void @llvm.dbg.declare(metadata %struct.A* %x, metadata !12, metadata !DIExpression()), !dbg !19 + call void @_ZN1AD1Ev(%struct.A* %x) #3, !dbg !20 + ret void, !dbg !20 +} + +declare void @llvm.dbg.declare(metadata, metadata, metadata) + +declare void @_ZN1AD1Ev(%struct.A*) + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!3, !4, !5, !6} +!llvm.ident = !{!7} + +!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !1, producer: "clang version 8.0.0 ", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !2) +!1 = !DIFile(filename: "-", directory: "/Users/vsk/src/builds/llvm-project-tailcall-RA") +!2 = !{} +!3 = !{i32 2, !"Dwarf Version", i32 4} +!4 = !{i32 2, !"Debug Info Version", i32 3} +!5 = !{i32 1, !"wchar_size", i32 4} +!6 = !{i32 7, !"PIC Level", i32 2} +!7 = !{!"clang version 8.0.0 "} +!8 = distinct !DISubprogram(name: "foo", linkageName: "_Z3foov", scope: !9, file: !9, line: 1, type: !10, isLocal: false, isDefinition: true, scopeLine: 1, flags: DIFlagPrototyped, isOptimized: false, unit: !0, retainedNodes: !2) +!9 = !DIFile(filename: "", directory: "/Users/vsk/src/builds/llvm-project-tailcall-RA") +!10 = !DISubroutineType(types: !11) +!11 = !{null} +!12 = !DILocalVariable(name: "x", scope: !8, file: !9, line: 1, type: !13) +!13 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "A", file: !9, line: 1, size: 8, flags: DIFlagTypePassByReference, elements: !14, identifier: "_ZTS1A") +!14 = !{!15} +!15 = !DISubprogram(name: "~A", scope: !13, file: !9, line: 1, type: !16, isLocal: false, isDefinition: false, scopeLine: 1, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, isOptimized: false) +!16 = !DISubroutineType(types: !17) +!17 = !{null, !18} +!18 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64, flags: DIFlagArtificial | DIFlagObjectPointer) +!19 = !DILocation(line: 1, column: 36, scope: !8) +!20 = !DILocation(line: 1, column: 39, scope: !8) diff --git a/llvm/test/DebugInfo/X86/dwarf-callsite-related-attrs.ll b/llvm/test/DebugInfo/X86/dwarf-callsite-related-attrs.ll new file mode 100644 index 0000000..5fb78e4 --- /dev/null +++ b/llvm/test/DebugInfo/X86/dwarf-callsite-related-attrs.ll @@ -0,0 +1,133 @@ +; $ clang++ -S -emit-llvm -o - -gdwarf-5 -o - -O1 tail2.cc +; volatile int sink; +; void __attribute__((noinline)) bat() { sink++; } +; void __attribute__((noinline)) bar() { sink++; } +; void __attribute__((noinline)) foo() { +; bar(); bat(); +; bar(); bat(); +; } +; int __attribute__((disable_tail_calls)) main() { foo(); } + +; REQUIRES: object-emission +; RUN: %llc_dwarf < %s -o - | FileCheck %s -check-prefix=ASM +; RUN: %llc_dwarf < %s -filetype=obj -o %t.o +; RUN: llvm-dwarfdump %t.o -o - | FileCheck %s -check-prefix=OBJ -implicit-check-not=DW_TAG_call_site +; RUN: llvm-dwarfdump -verify %t.o 2>&1 | FileCheck %s -check-prefix=VERIFY +; RUN: llvm-dwarfdump -statistics %t.o | FileCheck %s -check-prefix=STATS +; RUN: llvm-as < %s | llvm-dis | llvm-as | llvm-dis -o /dev/null + +; VERIFY: No errors. +; STATS: "call site entries":5 + +@sink = global i32 0, align 4, !dbg !0 + +; ASM: DW_TAG_subprogram +; ASM: DW_AT_call_all_calls +; OBJ: [[bat_sp:.*]]: DW_TAG_subprogram +; OBJ: DW_AT_call_all_calls (true) +; OBJ: DW_AT_name ("bat") +define void @_Z3batv() !dbg !13 { +entry: + %0 = load volatile i32, i32* @sink, align 4, !dbg !16, !tbaa !17 + %inc = add nsw i32 %0, 1, !dbg !16 + store volatile i32 %inc, i32* @sink, align 4, !dbg !16, !tbaa !17 + ret void, !dbg !21 +} + +; ASM: DW_TAG_subprogram +; ASM: DW_AT_call_all_calls +; OBJ: [[bar_sp:.*]]: DW_TAG_subprogram +; OBJ: DW_AT_call_all_calls (true) +; OBJ: DW_AT_name ("bar") +define void @_Z3barv() !dbg !22 { +entry: + %0 = load volatile i32, i32* @sink, align 4, !dbg !23, !tbaa !17 + %inc = add nsw i32 %0, 1, !dbg !23 + store volatile i32 %inc, i32* @sink, align 4, !dbg !23, !tbaa !17 + ret void, !dbg !24 +} + +; ASM: DW_TAG_subprogram +; ASM: DW_AT_call_all_calls +; OBJ: [[foo_sp:.*]]: DW_TAG_subprogram +; OBJ: DW_AT_call_all_calls (true) +; OBJ: DW_AT_name ("foo") +; OBJ: DW_TAG_call_site +; OBJ: DW_AT_call_origin ([[bar_sp]]) +; OBJ: DW_AT_call_return_pc ({{[0x]+}}26) +; OBJ: DW_TAG_call_site +; OBJ: DW_AT_call_origin ([[bat_sp]]) +; OBJ: DW_AT_call_return_pc ({{[0x]+}}2b) +; OBJ: DW_TAG_call_site +; OBJ: DW_AT_call_origin ([[bar_sp]]) +; OBJ: DW_AT_call_return_pc ({{[0x]+}}30) +; OBJ: DW_TAG_call_site +; OBJ: DW_AT_call_origin ([[bat_sp]]) +; OBJ: DW_AT_call_tail_call +define void @_Z3foov() !dbg !25 { +entry: + tail call void @_Z3barv(), !dbg !26 + tail call void @_Z3batv(), !dbg !27 + tail call void @_Z3barv(), !dbg !26 + tail call void @_Z3batv(), !dbg !27 + ret void, !dbg !28 +} + +; ASM: DW_TAG_subprogram +; ASM: DW_AT_call_all_calls +; OBJ: DW_TAG_subprogram +; OBJ: DW_AT_call_all_calls (true) +; OBJ: DW_AT_name ("main") +; OBJ: DW_TAG_call_site +; OBJ: DW_AT_call_origin ([[foo_sp]]) +; OBJ: DW_AT_call_return_pc ({{[0x]+}}46) +define i32 @main() !dbg !29 { +entry: + call void @_Z3foov(), !dbg !32 + + %indirect_target = load void ()*, void ()** undef + call void %indirect_target() + + call void asm sideeffect "", "~{dirflag},~{fpsr},~{flags}"() + + ret i32 0, !dbg !33 +} + +!llvm.dbg.cu = !{!2} +!llvm.module.flags = !{!8, !9, !10, !11} +!llvm.ident = !{!12} + +!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) +!1 = distinct !DIGlobalVariable(name: "sink", scope: !2, file: !3, line: 1, type: !6, isLocal: false, isDefinition: true) +!2 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !3, producer: "clang version 7.0.0 ", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !4, globals: !5) +!3 = !DIFile(filename: "/Users/vsk/src/llvm.org-tailcall/tail2.cc", directory: "/Users/vsk/src/builds/llvm-project-tailcall-RA", checksumkind: CSK_MD5, checksum: "3b61952c21b7f657ddb7c0ad44cf5529") +!4 = !{} +!5 = !{!0} +!6 = !DIDerivedType(tag: DW_TAG_volatile_type, baseType: !7) +!7 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!8 = !{i32 2, !"Dwarf Version", i32 5} +!9 = !{i32 2, !"Debug Info Version", i32 3} +!10 = !{i32 1, !"wchar_size", i32 4} +!11 = !{i32 7, !"PIC Level", i32 2} +!12 = !{!"clang version 7.0.0 "} +!13 = distinct !DISubprogram(name: "bat", linkageName: "_Z3batv", scope: !3, file: !3, line: 2, type: !14, isLocal: false, isDefinition: true, scopeLine: 2, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, isOptimized: true, unit: !2, retainedNodes: !4) +!14 = !DISubroutineType(types: !15) +!15 = !{null} +!16 = !DILocation(line: 2, column: 44, scope: !13) +!17 = !{!18, !18, i64 0} +!18 = !{!"int", !19, i64 0} +!19 = !{!"omnipotent char", !20, i64 0} +!20 = !{!"Simple C++ TBAA"} +!21 = !DILocation(line: 2, column: 48, scope: !13) +!22 = distinct !DISubprogram(name: "bar", linkageName: "_Z3barv", scope: !3, file: !3, line: 3, type: !14, isLocal: false, isDefinition: true, scopeLine: 3, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, isOptimized: true, unit: !2, retainedNodes: !4) +!23 = !DILocation(line: 3, column: 44, scope: !22) +!24 = !DILocation(line: 3, column: 48, scope: !22) +!25 = distinct !DISubprogram(name: "foo", linkageName: "_Z3foov", scope: !3, file: !3, line: 4, type: !14, isLocal: false, isDefinition: true, scopeLine: 4, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, isOptimized: true, unit: !2, retainedNodes: !4) +!26 = !DILocation(line: 5, column: 3, scope: !25) +!27 = !DILocation(line: 6, column: 3, scope: !25) +!28 = !DILocation(line: 7, column: 1, scope: !25) +!29 = distinct !DISubprogram(name: "main", scope: !3, file: !3, line: 8, type: !30, isLocal: false, isDefinition: true, scopeLine: 8, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, isOptimized: true, unit: !2, retainedNodes: !4) +!30 = !DISubroutineType(types: !31) +!31 = !{!7} +!32 = !DILocation(line: 8, column: 50, scope: !29) +!33 = !DILocation(line: 8, column: 57, scope: !29) diff --git a/llvm/test/tools/llvm-dwarfdump/X86/callsite-invalid.s b/llvm/test/tools/llvm-dwarfdump/X86/callsite-invalid.s new file mode 100644 index 0000000..dcb5ac5 --- /dev/null +++ b/llvm/test/tools/llvm-dwarfdump/X86/callsite-invalid.s @@ -0,0 +1,376 @@ +# RUN: llvm-mc -triple x86_64-apple-darwin %s -filetype=obj -o %t.o +# RUN: not llvm-dwarfdump -verify %t.o 2>&1 | FileCheck %s + +# CHECK: error: Subprogram with call site entry has no DW_AT_call attribute: +# CHECK: DW_TAG_subprogram +# CHECK: DW_AT_name ("main") +# CHECK: DW_TAG_call_site +# CHECK: DW_AT_call_origin +# CHECK: Errors detected. + +# Source: +## define void @foo() !dbg !25 { +## ret void, !dbg !28 +## } +## +## define i32 @main() !dbg !29 { +## call void @foo(), !dbg !32 +## ret i32 0, !dbg !33 +## } +## +## !llvm.dbg.cu = !{!2} +## !llvm.module.flags = !{!8, !9, !10, !11} +## !llvm.ident = !{!12} +## +## !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) +## !1 = distinct !DIGlobalVariable(name: "sink", scope: !2, file: !3, line: 1, type: !6, isLocal: false, isDefinition: true) +## !2 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !3, producer: "clang version 7.0.0 ", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !4, globals: !5) +## !3 = !DIFile(filename: "/Users/vsk/src/llvm.org-tailcall/tail2.cc", directory: "/Users/vsk/src/builds/llvm-project-tailcall-RA", checksumkind: CSK_MD5, checksum: "3b61952c21b7f657ddb7c0ad44cf5529") +## !4 = !{} +## !5 = !{!0} +## !6 = !DIDerivedType(tag: DW_TAG_volatile_type, baseType: !7) +## !7 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +## !8 = !{i32 2, !"Dwarf Version", i32 5} +## !9 = !{i32 2, !"Debug Info Version", i32 3} +## !10 = !{i32 1, !"wchar_size", i32 4} +## !11 = !{i32 7, !"PIC Level", i32 2} +## !12 = !{!"clang version 7.0.0 "} +## !13 = distinct !DISubprogram(name: "bat", linkageName: "_Z3batv", scope: !3, file: !3, line: 2, type: !14, isLocal: false, isDefinition: true, scopeLine: 2, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, isOptimized: true, unit: !2, retainedNodes: !4) +## !14 = !DISubroutineType(types: !15) +## !15 = !{null} +## !16 = !DILocation(line: 2, column: 44, scope: !13) +## !17 = !{!18, !18, i64 0} +## !18 = !{!"int", !19, i64 0} +## !19 = !{!"omnipotent char", !20, i64 0} +## !20 = !{!"Simple C++ TBAA"} +## !21 = !DILocation(line: 2, column: 48, scope: !13) +## !22 = distinct !DISubprogram(name: "bar", linkageName: "_Z3barv", scope: !3, file: !3, line: 3, type: !14, isLocal: false, isDefinition: true, scopeLine: 3, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, isOptimized: true, unit: !2, retainedNodes: !4) +## !23 = !DILocation(line: 3, column: 44, scope: !22) +## !24 = !DILocation(line: 3, column: 48, scope: !22) +## !25 = distinct !DISubprogram(name: "foo", linkageName: "_Z3foov", scope: !3, file: !3, line: 4, type: !14, isLocal: false, isDefinition: true, scopeLine: 4, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, isOptimized: true, unit: !2, retainedNodes: !4) +## !26 = !DILocation(line: 5, column: 3, scope: !25) +## !27 = !DILocation(line: 6, column: 3, scope: !25) +## !28 = !DILocation(line: 7, column: 1, scope: !25) +## !29 = distinct !DISubprogram(name: "main", scope: !3, file: !3, line: 8, type: !30, isLocal: false, isDefinition: true, scopeLine: 8, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, isOptimized: true, unit: !2, retainedNodes: !4) +## !30 = !DISubroutineType(types: !31) +## !31 = !{!7} +## !32 = !DILocation(line: 8, column: 50, scope: !29) +## !33 = !DILocation(line: 8, column: 57, scope: !29) + + .section __TEXT,__text,regular,pure_instructions + .globl _foo ## -- Begin function foo +_foo: ## @foo +Lfunc_begin0: + .cfi_startproc +## %bb.0: + retq +Ltmp0: +Lfunc_end0: + .cfi_endproc + ## -- End function + .globl _main ## -- Begin function main +_main: ## @main +Lfunc_begin1: + .cfi_startproc +## %bb.0: + pushq %rax + .cfi_def_cfa_offset 16 +Ltmp1: + callq _foo + xorl %eax, %eax + popq %rcx + retq +Ltmp2: +Lfunc_end1: + .cfi_endproc + ## -- End function + .section __DWARF,__debug_str_offs,regular,debug +Lsection_str_off: + .long 36 + .short 5 + .short 0 +Lstr_offsets_base0: + .section __DWARF,__debug_str,regular,debug +Linfo_string: + .asciz "clang version 7.0.0 " ## string offset=0 + .asciz "/Users/vsk/src/llvm.org-tailcall/tail2.cc" ## string offset=21 + .asciz "/Users/vsk/src/builds/llvm-project-tailcall-RA" ## string offset=63 + .asciz "sink" ## string offset=110 + .asciz "int" ## string offset=115 + .asciz "foo" ## string offset=119 + .asciz "_Z3foov" ## string offset=123 + .asciz "main" ## string offset=131 + .section __DWARF,__debug_str_offs,regular,debug + .long 0 + .long 21 + .long 63 + .long 110 + .long 115 + .long 119 + .long 123 + .long 131 + .section __DWARF,__debug_abbrev,regular,debug +Lsection_abbrev: + .byte 1 ## Abbreviation Code + .byte 17 ## DW_TAG_compile_unit + .byte 1 ## DW_CHILDREN_yes + .byte 37 ## DW_AT_producer + .byte 37 ## DW_FORM_strx1 + .byte 19 ## DW_AT_language + .byte 5 ## DW_FORM_data2 + .byte 3 ## DW_AT_name + .byte 37 ## DW_FORM_strx1 + .byte 114 ## DW_AT_str_offsets_base + .byte 23 ## DW_FORM_sec_offset + .byte 16 ## DW_AT_stmt_list + .byte 23 ## DW_FORM_sec_offset + .byte 27 ## DW_AT_comp_dir + .byte 37 ## DW_FORM_strx1 + .ascii "\341\177" ## DW_AT_APPLE_optimized + .byte 25 ## DW_FORM_flag_present + .byte 17 ## DW_AT_low_pc + .byte 1 ## DW_FORM_addr + .byte 18 ## DW_AT_high_pc + .byte 6 ## DW_FORM_data4 + .byte 0 ## EOM(1) + .byte 0 ## EOM(2) + .byte 2 ## Abbreviation Code + .byte 52 ## DW_TAG_variable + .byte 0 ## DW_CHILDREN_no + .byte 3 ## DW_AT_name + .byte 37 ## DW_FORM_strx1 + .byte 73 ## DW_AT_type + .byte 19 ## DW_FORM_ref4 + .byte 63 ## DW_AT_external + .byte 25 ## DW_FORM_flag_present + .byte 58 ## DW_AT_decl_file + .byte 11 ## DW_FORM_data1 + .byte 59 ## DW_AT_decl_line + .byte 11 ## DW_FORM_data1 + .byte 0 ## EOM(1) + .byte 0 ## EOM(2) + .byte 3 ## Abbreviation Code + .byte 53 ## DW_TAG_volatile_type + .byte 0 ## DW_CHILDREN_no + .byte 73 ## DW_AT_type + .byte 19 ## DW_FORM_ref4 + .byte 0 ## EOM(1) + .byte 0 ## EOM(2) + .byte 4 ## Abbreviation Code + .byte 36 ## DW_TAG_base_type + .byte 0 ## DW_CHILDREN_no + .byte 3 ## DW_AT_name + .byte 37 ## DW_FORM_strx1 + .byte 62 ## DW_AT_encoding + .byte 11 ## DW_FORM_data1 + .byte 11 ## DW_AT_byte_size + .byte 11 ## DW_FORM_data1 + .byte 0 ## EOM(1) + .byte 0 ## EOM(2) + .byte 5 ## Abbreviation Code + .byte 46 ## DW_TAG_subprogram + .byte 0 ## DW_CHILDREN_no + .byte 17 ## DW_AT_low_pc + .byte 1 ## DW_FORM_addr + .byte 18 ## DW_AT_high_pc + .byte 6 ## DW_FORM_data4 + .ascii "\347\177" ## DW_AT_APPLE_omit_frame_ptr + .byte 25 ## DW_FORM_flag_present + .byte 64 ## DW_AT_frame_base + .byte 24 ## DW_FORM_exprloc +## .byte 122 ## DW_AT_call_all_calls +## .byte 25 ## DW_FORM_flag_present + .byte 110 ## DW_AT_linkage_name + .byte 37 ## DW_FORM_strx1 + .byte 3 ## DW_AT_name + .byte 37 ## DW_FORM_strx1 + .byte 58 ## DW_AT_decl_file + .byte 11 ## DW_FORM_data1 + .byte 59 ## DW_AT_decl_line + .byte 11 ## DW_FORM_data1 + .byte 63 ## DW_AT_external + .byte 25 ## DW_FORM_flag_present + .ascii "\341\177" ## DW_AT_APPLE_optimized + .byte 25 ## DW_FORM_flag_present + .byte 0 ## EOM(1) + .byte 0 ## EOM(2) + .byte 6 ## Abbreviation Code + .byte 46 ## DW_TAG_subprogram + .byte 1 ## DW_CHILDREN_yes + .byte 17 ## DW_AT_low_pc + .byte 1 ## DW_FORM_addr + .byte 18 ## DW_AT_high_pc + .byte 6 ## DW_FORM_data4 + .ascii "\347\177" ## DW_AT_APPLE_omit_frame_ptr + .byte 25 ## DW_FORM_flag_present + .byte 64 ## DW_AT_frame_base + .byte 24 ## DW_FORM_exprloc +## .byte 122 ## DW_AT_call_all_calls +## .byte 25 ## DW_FORM_flag_present + .byte 3 ## DW_AT_name + .byte 37 ## DW_FORM_strx1 + .byte 58 ## DW_AT_decl_file + .byte 11 ## DW_FORM_data1 + .byte 59 ## DW_AT_decl_line + .byte 11 ## DW_FORM_data1 + .byte 73 ## DW_AT_type + .byte 19 ## DW_FORM_ref4 + .byte 63 ## DW_AT_external + .byte 25 ## DW_FORM_flag_present + .ascii "\341\177" ## DW_AT_APPLE_optimized + .byte 25 ## DW_FORM_flag_present + .byte 0 ## EOM(1) + .byte 0 ## EOM(2) + .byte 7 ## Abbreviation Code + .byte 72 ## DW_TAG_call_site + .byte 0 ## DW_CHILDREN_no + .byte 127 ## DW_AT_call_origin + .byte 19 ## DW_FORM_ref4 + .byte 0 ## EOM(1) + .byte 0 ## EOM(2) + .byte 0 ## EOM(3) + .section __DWARF,__debug_info,regular,debug +Lsection_info: +Lcu_begin0: + .long 99 ## Length of Unit + .short 5 ## DWARF version number + .byte 1 ## DWARF Unit Type + .byte 8 ## Address Size (in bytes) +.set Lset0, Lsection_abbrev-Lsection_abbrev ## Offset Into Abbrev. Section + .long Lset0 + .byte 1 ## Abbrev [1] 0xc:0x5b DW_TAG_compile_unit + .byte 0 ## DW_AT_producer + .short 4 ## DW_AT_language + .byte 1 ## DW_AT_name +.set Lset1, Lstr_offsets_base0-Lsection_str_off ## DW_AT_str_offsets_base + .long Lset1 +.set Lset2, Lline_table_start0-Lsection_line ## DW_AT_stmt_list + .long Lset2 + .byte 2 ## DW_AT_comp_dir + ## DW_AT_APPLE_optimized + .quad Lfunc_begin0 ## DW_AT_low_pc +.set Lset3, Lfunc_end1-Lfunc_begin0 ## DW_AT_high_pc + .long Lset3 + .byte 2 ## Abbrev [2] 0x26:0x8 DW_TAG_variable + .byte 3 ## DW_AT_name + .long 46 ## DW_AT_type + ## DW_AT_external + .byte 1 ## DW_AT_decl_file + .byte 1 ## DW_AT_decl_line + .byte 3 ## Abbrev [3] 0x2e:0x5 DW_TAG_volatile_type + .long 51 ## DW_AT_type + .byte 4 ## Abbrev [4] 0x33:0x4 DW_TAG_base_type + .byte 4 ## DW_AT_name + .byte 5 ## DW_AT_encoding + .byte 4 ## DW_AT_byte_size + .byte 5 ## Abbrev [5] 0x37:0x13 DW_TAG_subprogram + .quad Lfunc_begin0 ## DW_AT_low_pc +.set Lset4, Lfunc_end0-Lfunc_begin0 ## DW_AT_high_pc + .long Lset4 + ## DW_AT_APPLE_omit_frame_ptr + .byte 1 ## DW_AT_frame_base + .byte 87 + ## DW_AT_call_all_calls + .byte 6 ## DW_AT_linkage_name + .byte 5 ## DW_AT_name + .byte 1 ## DW_AT_decl_file + .byte 4 ## DW_AT_decl_line + ## DW_AT_external + ## DW_AT_APPLE_optimized + .byte 6 ## Abbrev [6] 0x4a:0x1c DW_TAG_subprogram + .quad Lfunc_begin1 ## DW_AT_low_pc +.set Lset5, Lfunc_end1-Lfunc_begin1 ## DW_AT_high_pc + .long Lset5 + ## DW_AT_APPLE_omit_frame_ptr + .byte 1 ## DW_AT_frame_base + .byte 87 + ## DW_AT_call_all_calls + .byte 7 ## DW_AT_name + .byte 1 ## DW_AT_decl_file + .byte 8 ## DW_AT_decl_line + .long 51 ## DW_AT_type + ## DW_AT_external + ## DW_AT_APPLE_optimized + .byte 7 ## Abbrev [7] 0x60:0x5 DW_TAG_call_site + .long 55 ## DW_AT_call_origin + .byte 0 ## End Of Children Mark + .byte 0 ## End Of Children Mark + .section __DWARF,__debug_macinfo,regular,debug +Ldebug_macinfo: + .byte 0 ## End Of Macro List Mark + .section __DWARF,__debug_names,regular,debug +Ldebug_names_begin: +.set Lset6, Lnames_end0-Lnames_start0 ## Header: unit length + .long Lset6 +Lnames_start0: + .short 5 ## Header: version + .short 0 ## Header: padding + .long 1 ## Header: compilation unit count + .long 0 ## Header: local type unit count + .long 0 ## Header: foreign type unit count + .long 4 ## Header: bucket count + .long 4 ## Header: name count +.set Lset7, Lnames_abbrev_end0-Lnames_abbrev_start0 ## Header: abbreviation table size + .long Lset7 + .long 8 ## Header: augmentation string size + .ascii "LLVM0700" ## Header: augmentation string +.set Lset8, Lcu_begin0-Lsection_info ## Compilation unit 0 + .long Lset8 + .long 1 ## Bucket 0 + .long 2 ## Bucket 1 + .long 3 ## Bucket 2 + .long 4 ## Bucket 3 + .long 193495088 ## Hash in Bucket 0 + .long 193491849 ## Hash in Bucket 1 + .long 2090499946 ## Hash in Bucket 2 + .long -1257882357 ## Hash in Bucket 3 + .long 115 ## String in Bucket 0: int + .long 119 ## String in Bucket 1: foo + .long 131 ## String in Bucket 2: main + .long 123 ## String in Bucket 3: _Z3foov +.set Lset9, Lnames3-Lnames_entries0 ## Offset in Bucket 0 + .long Lset9 +.set Lset10, Lnames0-Lnames_entries0 ## Offset in Bucket 1 + .long Lset10 +.set Lset11, Lnames1-Lnames_entries0 ## Offset in Bucket 2 + .long Lset11 +.set Lset12, Lnames2-Lnames_entries0 ## Offset in Bucket 3 + .long Lset12 +Lnames_abbrev_start0: + .byte 46 ## Abbrev code + .byte 46 ## DW_TAG_subprogram + .byte 3 ## DW_IDX_die_offset + .byte 19 ## DW_FORM_ref4 + .byte 0 ## End of abbrev + .byte 0 ## End of abbrev + .byte 36 ## Abbrev code + .byte 36 ## DW_TAG_base_type + .byte 3 ## DW_IDX_die_offset + .byte 19 ## DW_FORM_ref4 + .byte 0 ## End of abbrev + .byte 0 ## End of abbrev + .byte 0 ## End of abbrev list +Lnames_abbrev_end0: +Lnames_entries0: +Lnames3: + .byte 36 ## Abbreviation code + .long 51 ## DW_IDX_die_offset + .long 0 ## End of list: int +Lnames0: + .byte 46 ## Abbreviation code + .long 55 ## DW_IDX_die_offset + .long 0 ## End of list: foo +Lnames1: + .byte 46 ## Abbreviation code + .long 74 ## DW_IDX_die_offset + .long 0 ## End of list: main +Lnames2: + .byte 46 ## Abbreviation code + .long 55 ## DW_IDX_die_offset + .long 0 ## End of list: _Z3foov +Lnames_end0: + +.subsections_via_symbols + .section __DWARF,__debug_line,regular,debug +Lsection_line: +Lline_table_start0: diff --git a/llvm/tools/llvm-dwarfdump/Statistics.cpp b/llvm/tools/llvm-dwarfdump/Statistics.cpp index 45e96d5..4119dbf 100644 --- a/llvm/tools/llvm-dwarfdump/Statistics.cpp +++ b/llvm/tools/llvm-dwarfdump/Statistics.cpp @@ -32,6 +32,8 @@ struct GlobalStats { /// Total number of PC range bytes in each variable's enclosing scope, /// starting from the first definition of the variable. unsigned ScopeBytesFromFirstDefinition = 0; + /// Total number of call site entries (DW_TAG_call_site). + unsigned CallSiteEntries = 0; }; /// Extract the low pc from a Die. @@ -57,6 +59,11 @@ static void collectStatsForDie(DWARFDie Die, std::string FnPrefix, uint64_t BytesCovered = 0; uint64_t OffsetToFirstDefinition = 0; + if (Die.getTag() == dwarf::DW_TAG_call_site) { + GlobalStats.CallSiteEntries++; + return; + } + if (Die.getTag() != dwarf::DW_TAG_formal_parameter && Die.getTag() != dwarf::DW_TAG_variable && Die.getTag() != dwarf::DW_TAG_member) { @@ -260,6 +267,7 @@ bool collectStatsForObjectFile(ObjectFile &Obj, DWARFContext &DICtx, printDatum(OS, "unique source variables", VarUnique); printDatum(OS, "source variables", VarTotal); printDatum(OS, "variables with location", VarWithLoc); + printDatum(OS, "call site entries", GlobalStats.CallSiteEntries); printDatum(OS, "scope bytes total", GlobalStats.ScopeBytesFromFirstDefinition); printDatum(OS, "scope bytes covered", GlobalStats.ScopeBytesCovered); -- 2.7.4