Index: clang/lib/CodeGen/CGDebugInfo.h =================================================================== --- clang/lib/CodeGen/CGDebugInfo.h +++ clang/lib/CodeGen/CGDebugInfo.h @@ -596,6 +596,11 @@ 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; Index: clang/lib/CodeGen/CGDebugInfo.cpp =================================================================== --- clang/lib/CodeGen/CGDebugInfo.cpp +++ clang/lib/CodeGen/CGDebugInfo.cpp @@ -3133,6 +3133,7 @@ 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), @@ -3370,6 +3371,8 @@ if (CurFuncIsThunk) Flags |= llvm::DINode::FlagThunk; + llvm::DINode::DIFlags FlagsForDef = Flags | getCallSiteRelatedAttrs(); + unsigned LineNo = getLineNumber(Loc); unsigned ScopeLine = getLineNumber(ScopeLoc); @@ -3381,7 +3384,7 @@ 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 @@ -4375,3 +4378,22 @@ 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; +} Index: clang/test/CodeGenCXX/dbg-info-all-calls-described.cpp =================================================================== --- /dev/null +++ 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(); +} Index: llvm/include/llvm/DebugInfo/DWARF/DWARFVerifier.h =================================================================== --- llvm/include/llvm/DebugInfo/DWARF/DWARFVerifier.h +++ llvm/include/llvm/DebugInfo/DWARF/DWARFVerifier.h @@ -147,6 +147,8 @@ /// - 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. /// \param UnitType An optional unit type which will be used to verify the @@ -165,6 +167,12 @@ 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: Index: llvm/include/llvm/IR/DebugInfoFlags.def =================================================================== --- llvm/include/llvm/IR/DebugInfoFlags.def +++ llvm/include/llvm/IR/DebugInfoFlags.def @@ -50,6 +50,7 @@ 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 @@ #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 Index: llvm/include/llvm/IR/DebugInfoMetadata.h =================================================================== --- llvm/include/llvm/IR/DebugInfoMetadata.h +++ llvm/include/llvm/IR/DebugInfoMetadata.h @@ -1725,6 +1725,9 @@ } 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. Index: llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.h =================================================================== --- llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.h +++ llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.h @@ -203,12 +203,20 @@ 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); Index: llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp =================================================================== --- llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp +++ llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp @@ -727,7 +727,8 @@ return ObjectPointer; } -void DwarfCompileUnit::constructSubprogramScopeDIE(const DISubprogram *Sub, LexicalScope *Scope) { +DIE &DwarfCompileUnit::constructSubprogramScopeDIE(const DISubprogram *Sub, + LexicalScope *Scope) { DIE &ScopeDIE = updateSubprogramScopeDIE(Sub); if (Scope) { @@ -750,6 +751,8 @@ !includeMinimalInlineScopes()) ScopeDIE.addChild( DIE::get(DIEValueAllocator, dwarf::DW_TAG_unspecified_parameters)); + + return ScopeDIE; } DIE *DwarfCompileUnit::createAndAddScopeChildren(LexicalScope *Scope, @@ -804,6 +807,32 @@ 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()); Index: llvm/lib/CodeGen/AsmPrinter/DwarfDebug.h =================================================================== --- llvm/lib/CodeGen/AsmPrinter/DwarfDebug.h +++ llvm/lib/CodeGen/AsmPrinter/DwarfDebug.h @@ -425,6 +425,10 @@ /// 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); Index: llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp =================================================================== --- llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp +++ 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::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; @@ -1328,6 +1386,11 @@ 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) @@ -1506,12 +1569,15 @@ } 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 Index: llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp =================================================================== --- llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp +++ llvm/lib/DebugInfo/DWARF/DWARFVerifier.cpp @@ -182,6 +182,7 @@ NumUnitErrors += verifyDebugInfoAttribute(Die, AttrValue); NumUnitErrors += verifyDebugInfoForm(Die, AttrValue); } + NumUnitErrors += verifyDebugInfoCallSite(Die); } DWARFDie Die = Unit.getUnitDIE(/* ExtractUnitDIEOnly = */ false); @@ -211,6 +212,38 @@ 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) { Index: llvm/lib/IR/Verifier.cpp =================================================================== --- llvm/lib/IR/Verifier.cpp +++ llvm/lib/IR/Verifier.cpp @@ -1119,6 +1119,10 @@ 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) { Index: llvm/test/DebugInfo/Generic/callsite-attr-invalid.ll =================================================================== --- /dev/null +++ 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) Index: llvm/test/DebugInfo/X86/dwarf-callsite-related-attrs.ll =================================================================== --- /dev/null +++ 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) Index: llvm/test/DebugInfo/X86/dwarfdump-callsite-invalid-1.s =================================================================== --- /dev/null +++ llvm/test/DebugInfo/X86/dwarfdump-callsite-invalid-1.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: Index: llvm/test/tools/dsymutil/Inputs/call-site-entry.c =================================================================== --- /dev/null +++ llvm/test/tools/dsymutil/Inputs/call-site-entry.c @@ -0,0 +1,27 @@ +/* + * This file is used to test dsymutil support for call site entries + * (DW_TAG_call_site). + * + * Instructions for regenerating binaries (on Darwin/x86_64): + * + * 1. Copy the source to a top-level directory to work around having absolute + * paths in the symtab's OSO entries. + * + * mkdir -p /Inputs/ && cp call-site-entry.c /Inputs && cd /Inputs + * + * 2. Compile with call site info enabled. + * + * clang -g -O1 -Xclang -disable-llvm-passes -mllvm -callsite-debuginfo-experimental=true call-site-entry.c -c -o call-site-entry.macho.x86_64.o + * clang call-site-entry.macho.x86_64.o -o call-site-entry.macho.x86_64 + * + * 3. Copy the binaries back into the repo's Inputs directory. You'll need + * -oso-prepend-path=%p to link. + */ + +int zero() { + return 0; +} + +int main() { + return zero(); +} Index: llvm/test/tools/dsymutil/call-site-entry-linking.test =================================================================== --- /dev/null +++ llvm/test/tools/dsymutil/call-site-entry-linking.test @@ -0,0 +1,4 @@ +RUN: dsymutil -oso-prepend-path=%p %p/Inputs/call-site-entry.macho.x86_64 -o %t.dSYM +RUN: llvm-dwarfdump %t.dSYM | FileCheck %s -implicit-check-not=DW_AT_call_return_pc + +CHECK: DW_AT_call_return_pc (0x0000000100000fa4) Index: llvm/tools/dsymutil/DwarfLinker.cpp =================================================================== --- llvm/tools/dsymutil/DwarfLinker.cpp +++ llvm/tools/dsymutil/DwarfLinker.cpp @@ -1095,6 +1095,10 @@ // it. Otherwise (when no relocations where applied) just use the // one we just decoded. Addr = (Info.OrigHighPc ? Info.OrigHighPc : Addr) + Info.PCOffset; + } else if (AttrSpec.Attr == dwarf::DW_AT_call_return_pc) { + // Relocate a return PC address within a call site entry. + if (Die.getTag() == dwarf::DW_TAG_call_site) + Addr += Info.PCOffset; } Die.addValue(DIEAlloc, static_cast(AttrSpec.Attr), Index: llvm/tools/llvm-dwarfdump/Statistics.cpp =================================================================== --- llvm/tools/llvm-dwarfdump/Statistics.cpp +++ llvm/tools/llvm-dwarfdump/Statistics.cpp @@ -30,6 +30,8 @@ /// 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. @@ -86,8 +88,11 @@ BytesCovered = BytesInScope; } } + } else if (Die.getTag() == dwarf::DW_TAG_call_site) { + GlobalStats.CallSiteEntries++; + return; } else { - // Not a variable or constant member. + // Not a variable, constant member, or call site. return; } @@ -237,6 +242,7 @@ 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);