Index: llvm/include/llvm/DWARFLinker/DWARFLinker.h =================================================================== --- llvm/include/llvm/DWARFLinker/DWARFLinker.h +++ llvm/include/llvm/DWARFLinker/DWARFLinker.h @@ -576,6 +576,9 @@ /// Value of DW_AT_call_return_pc in the input DIE uint64_t OrigCallReturnPc = 0; + /// Value of DW_AT_call_pc in the input DIE + uint64_t OrigCallPc = 0; + /// Offset to apply to PC addresses inside a function. int64_t PCOffset = 0; Index: llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.h =================================================================== --- llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.h +++ llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.h @@ -245,10 +245,12 @@ /// For indirect calls \p CalleeDIE is set to nullptr. /// \p IsTail specifies whether the call is a tail call. /// \p PCAddr points to the PC value after the call instruction. + /// \p CallAddr points to the PC value at the call instruction (or is null). /// \p CallReg is a register location for an indirect call. For direct calls /// the \p CallReg is set to 0. DIE &constructCallSiteEntryDIE(DIE &ScopeDIE, DIE *CalleeDIE, bool IsTail, - const MCSymbol *PCAddr, unsigned CallReg); + const MCSymbol *PCAddr, + const MCSymbol *CallAddr, unsigned CallReg); /// Construct call site parameter DIEs for the \p CallSiteDIE. The \p Params /// were collected by the \ref collectCallSiteParameters. /// Note: The order of parameters does not matter, since debuggers recognize Index: llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp =================================================================== --- llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp +++ llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp @@ -981,6 +981,7 @@ DIE *CalleeDIE, bool IsTail, const MCSymbol *PCAddr, + const MCSymbol *CallAddr, unsigned CallReg) { // Insert a call site entry DIE within ScopeDIE. DIE &CallSiteDIE = createAndAddDIE(getDwarf5OrGNUTag(dwarf::DW_TAG_call_site), @@ -996,10 +997,16 @@ *CalleeDIE); } - if (IsTail) + if (IsTail) { // Attach DW_AT_call_tail_call to tail calls for standards compliance. addFlag(CallSiteDIE, getDwarf5OrGNUAttr(dwarf::DW_AT_call_tail_call)); + // Attach the address of the branch instruction to allow the debugger to + // show where the tail call occurred. This attribute has no GNU analog. + if (!useGNUAnalogForDwarf5Feature(DD)) + addLabelAddress(CallSiteDIE, dwarf::DW_AT_call_pc, CallAddr); + } + // Attach the return PC to allow the debugger to disambiguate call paths // from one function to another. // Index: llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp =================================================================== --- llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp +++ llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp @@ -874,6 +874,11 @@ ? nullptr : const_cast(getLabelAfterInsn(TopLevelCallMI)); + // For tail calls, it's necessary to record the address of the branch + // instruction so that the debugger can show where the tail call occurred. + const MCSymbol *CallAddr = + IsTail ? getLabelBeforeInsn(TopLevelCallMI) : nullptr; + assert((IsTail || PCAddr) && "Call without return PC information"); LLVM_DEBUG(dbgs() << "CallSiteEntry: " << MF.getName() << " -> " @@ -883,8 +888,8 @@ ->getName(CallReg))) << (IsTail ? " [IsTail]" : "") << "\n"); - DIE &CallSiteDIE = CU.constructCallSiteEntryDIE(ScopeDIE, CalleeDIE, - IsTail, PCAddr, CallReg); + DIE &CallSiteDIE = CU.constructCallSiteEntryDIE( + ScopeDIE, CalleeDIE, IsTail, PCAddr, CallAddr, CallReg); // GDB and LLDB support call site parameter debug info. if (Asm->TM.Options.EnableDebugEntryValues && @@ -1774,11 +1779,32 @@ // Process beginning of an instruction. void DwarfDebug::beginInstruction(const MachineInstr *MI) { + const MachineFunction &MF = *MI->getMF(); + const auto *SP = MF.getFunction().getSubprogram(); + bool NoDebug = + !SP || SP->getUnit()->getEmissionKind() == DICompileUnit::NoDebug; + + // When describing calls, we need a label for the call instruction. + // TODO: Add support for targets with delay slots. + if (!NoDebug && SP->areAllCallsDescribed() && + MI->isCandidateForCallSiteEntry(MachineInstr::AnyInBundle) && + !MI->hasDelaySlot()) { + const TargetInstrInfo *TII = MF.getSubtarget().getInstrInfo(); + bool IsTail = TII->isTailCall(*MI); + // For tail calls, we need the address of the branch instruction for + // DW_AT_call_pc. + if (IsTail) + requestLabelBeforeInsn(MI); + // For non-tail calls, we need the return address for the call for + // DW_AT_call_return_pc. Under GDB tuning, this information is needed for + // tail calls as well. + requestLabelAfterInsn(MI); + } + DebugHandlerBase::beginInstruction(MI); assert(CurMI); - const auto *SP = MI->getMF()->getFunction().getSubprogram(); - if (!SP || SP->getUnit()->getEmissionKind() == DICompileUnit::NoDebug) + if (NoDebug) return; // Check if source location changes, but ignore DBG_VALUE and CFI locations. @@ -1792,11 +1818,6 @@ 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) Index: llvm/lib/DWARFLinker/DWARFLinker.cpp =================================================================== --- llvm/lib/DWARFLinker/DWARFLinker.cpp +++ llvm/lib/DWARFLinker/DWARFLinker.cpp @@ -1056,6 +1056,10 @@ if (Die.getTag() == dwarf::DW_TAG_call_site) Addr = (Info.OrigCallReturnPc ? Info.OrigCallReturnPc : Addr) + Info.PCOffset; + } else if (AttrSpec.Attr == dwarf::DW_AT_call_pc) { + // Relocate the address of a branch instruction within a call site entry. + if (Die.getTag() == dwarf::DW_TAG_call_site) + Addr = (Info.OrigCallPc ? Info.OrigCallPc : Addr) + Info.PCOffset; } Die.addValue(DIEAlloc, static_cast(AttrSpec.Attr), Index: llvm/test/DebugInfo/MIR/X86/call-site-gnu-vs-dwarf5-attrs.mir =================================================================== --- llvm/test/DebugInfo/MIR/X86/call-site-gnu-vs-dwarf5-attrs.mir +++ llvm/test/DebugInfo/MIR/X86/call-site-gnu-vs-dwarf5-attrs.mir @@ -2,15 +2,15 @@ # # RUN: llc -dwarf-version 4 -debugger-tune=gdb -emit-call-site-info -debug-entry-values -filetype=obj \ # RUN: -mtriple=x86_64-unknown-unknown -start-after=machineverifier -o - %s \ -# RUN: | llvm-dwarfdump - | FileCheck %s -check-prefixes=CHECK-GNU +# RUN: | llvm-dwarfdump - | FileCheck %s -check-prefixes=CHECK-GNU -implicit-check-not=DW_AT_call # # RUN: llc -dwarf-version 5 -debugger-tune=lldb -emit-call-site-info -debug-entry-values -filetype=obj \ # RUN: -mtriple=x86_64-unknown-unknown -start-after=machineverifier -o - %s \ -# RUN: | llvm-dwarfdump - | FileCheck %s -check-prefixes=CHECK-DWARF5 +# RUN: | llvm-dwarfdump - | FileCheck %s -check-prefixes=CHECK-DWARF5 -implicit-check-not=DW_AT_call # # RUN: llc -dwarf-version 5 -emit-call-site-info -debug-entry-values -filetype=obj \ # RUN: -mtriple=x86_64-unknown-unknown -start-after=machineverifier -o - %s \ -# RUN: | llvm-dwarfdump - | FileCheck %s -check-prefixes=CHECK-DWARF5 +# RUN: | llvm-dwarfdump - | FileCheck %s -check-prefixes=CHECK-DWARF5,CHECK-DWARF5-NOTUNE -implicit-check-not=DW_AT_call # # This is based on the following reproducer: # @@ -54,6 +54,9 @@ # CHECK-DWARF5: DW_TAG_call_site # CHECK-DWARF5-NEXT: DW_AT_call_origin # CHECK-DWARF5-NEXT: DW_AT_call_return_pc +# CHECK-DWARF5: DW_TAG_call_site +# CHECK-DWARF5-NEXT: DW_AT_call_origin +# CHECK-DWARF5-NEXT: DW_AT_call_return_pc # CHECK-DWARF5: DW_TAG_call_site_parameter # CHECK-DWARF5-NEXT: DW_AT_location # CHECK-DWARF5-NEXT: DW_AT_call_value @@ -63,6 +66,8 @@ # CHECK-DWARF5: DW_TAG_call_site # CHECK-DWARF5-NEXT: DW_AT_call_origin # CHECK-DWARF5-NEXT: DW_AT_call_tail_call +# CHECK-DWARF5-NEXT: DW_AT_call_pc +# CHECK-DWARF5-NOTUNE: DW_AT_call_return_pc # --- | ; ModuleID = 'call-site-attrs.c' Index: llvm/test/DebugInfo/X86/dwarf-callsite-related-attrs.ll =================================================================== --- llvm/test/DebugInfo/X86/dwarf-callsite-related-attrs.ll +++ llvm/test/DebugInfo/X86/dwarf-callsite-related-attrs.ll @@ -12,10 +12,9 @@ ; and fail with "failed to compute relocation: IMAGE_REL_AMD64_ADDR32". ; UNSUPPORTED: cygwin,windows-gnu,windows-msvc -; REQUIRES: x86 ; RUN: %llc_dwarf -mtriple=x86_64-- < %s -o - | FileCheck %s -check-prefix=ASM ; RUN: %llc_dwarf -debugger-tune=lldb -mtriple=x86_64-- < %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 %t.o -o - | FileCheck %s -check-prefix=OBJ -implicit-check-not=DW_TAG_call -implicit-check-not=DW_AT_call ; 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 @@ -76,6 +75,7 @@ ; OBJ: DW_TAG_call_site ; OBJ: DW_AT_call_origin ([[bat_sp]]) ; OBJ: DW_AT_call_tail_call +; OBJ: DW_AT_call_pc define void @_Z3foov() !dbg !25 { entry: tail call void @__has_no_subprogram() Index: llvm/test/tools/dsymutil/X86/Inputs/tail-call.cpp =================================================================== --- /dev/null +++ llvm/test/tools/dsymutil/X86/Inputs/tail-call.cpp @@ -0,0 +1,28 @@ +/* + * This file is used to test dsymutil support for call site entries with tail + * calls (DW_AT_call_pc). + * + * 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 tail-call.c /Inputs && cd /Inputs + * + * 2. Compile with call site info enabled. -O2 is used to get tail call + * promotion. + * + * clang -g -O2 tail-call.c -c -o tail-call.macho.x86_64.o + * clang tail-call.macho.x86_64.o -o tail-call.macho.x86_64 + * + * 3. Copy the binaries back into the repo's Inputs directory. You'll need + * -oso-prepend-path=%p to link. + */ + +volatile int x; + +__attribute__((disable_tail_calls, noinline)) void func2() { x++; } + +__attribute__((noinline)) void func1() { func2(); /* tail */ } + +__attribute__((disable_tail_calls)) int main() { func1(); /* regular */ } Index: llvm/test/tools/dsymutil/X86/tail-call-linking.test =================================================================== --- /dev/null +++ llvm/test/tools/dsymutil/X86/tail-call-linking.test @@ -0,0 +1,4 @@ +RUN: dsymutil -oso-prepend-path=%p %p/Inputs/tail-call.macho.x86_64 -o %t.dSYM +RUN: llvm-dwarfdump %t.dSYM | FileCheck %s -implicit-check-not=DW_AT_call_pc + +CHECK: DW_AT_call_pc (0x0000000100000f95)