[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
This commit is contained in:
Vedant Kumar 2018-10-05 20:37:17 +00:00
parent f84ece68ca
commit 5931b4e5b5
16 changed files with 814 additions and 5 deletions

View File

@ -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;
}

View File

@ -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;

View File

@ -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();
}

View File

@ -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:

View File

@ -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

View File

@ -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.

View File

@ -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());

View File

@ -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);

View File

@ -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

View File

@ -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);

View File

@ -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) {

View File

@ -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) {

View File

@ -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: "<stdin>", 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)

View File

@ -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)

View File

@ -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:

View File

@ -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);