[llvm] f7052da - [DWARF] Emit DW_AT_call_pc for tail calls
Vedant Kumar via llvm-commits
llvm-commits at lists.llvm.org
Tue Mar 24 12:02:28 PDT 2020
Author: Vedant Kumar
Date: 2020-03-24T12:01:55-07:00
New Revision: f7052da6db8f85814adb2e1a6742d607e774bb88
URL: https://github.com/llvm/llvm-project/commit/f7052da6db8f85814adb2e1a6742d607e774bb88
DIFF: https://github.com/llvm/llvm-project/commit/f7052da6db8f85814adb2e1a6742d607e774bb88.diff
LOG: [DWARF] Emit DW_AT_call_pc for tail calls
Record the address of a tail-calling branch instruction within its call
site entry using DW_AT_call_pc. This allows a debugger to determine the
address to use when creating aritificial frames.
This creates an extra attribute + relocation at tail call sites, which
constitute 3-5% of all call sites in xnu/clang respectively.
rdar://60307600
Differential Revision: https://reviews.llvm.org/D76336
Added:
llvm/test/tools/dsymutil/X86/Inputs/tail-call.cpp
llvm/test/tools/dsymutil/X86/Inputs/tail-call.macho.x86_64
llvm/test/tools/dsymutil/X86/Inputs/tail-call.macho.x86_64.o
llvm/test/tools/dsymutil/X86/tail-call-linking.test
Modified:
llvm/include/llvm/DWARFLinker/DWARFLinker.h
llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp
llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.h
llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
llvm/lib/DWARFLinker/DWARFLinker.cpp
llvm/test/DebugInfo/MIR/X86/call-site-gnu-vs-dwarf5-attrs.mir
llvm/test/DebugInfo/X86/dwarf-callsite-related-attrs.ll
Removed:
################################################################################
diff --git a/llvm/include/llvm/DWARFLinker/DWARFLinker.h b/llvm/include/llvm/DWARFLinker/DWARFLinker.h
index 5b0108606ac9..af49665391bb 100644
--- a/llvm/include/llvm/DWARFLinker/DWARFLinker.h
+++ b/llvm/include/llvm/DWARFLinker/DWARFLinker.h
@@ -576,6 +576,9 @@ class DWARFLinker {
/// 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;
diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp
index 4a570dbbe339..889303b4278e 100644
--- a/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp
@@ -925,13 +925,12 @@ void DwarfCompileUnit::constructAbstractSubprogramScopeDIE(
ContextCU->addDIEEntry(*AbsDef, dwarf::DW_AT_object_pointer, *ObjectPointer);
}
-/// Whether to use the GNU analog for a DWARF5 tag, attribute, or location atom.
-static bool useGNUAnalogForDwarf5Feature(DwarfDebug *DD) {
+bool DwarfCompileUnit::useGNUAnalogForDwarf5Feature() const {
return DD->getDwarfVersion() == 4 && DD->tuneForGDB();
}
dwarf::Tag DwarfCompileUnit::getDwarf5OrGNUTag(dwarf::Tag Tag) const {
- if (!useGNUAnalogForDwarf5Feature(DD))
+ if (!useGNUAnalogForDwarf5Feature())
return Tag;
switch (Tag) {
case dwarf::DW_TAG_call_site:
@@ -945,7 +944,7 @@ dwarf::Tag DwarfCompileUnit::getDwarf5OrGNUTag(dwarf::Tag Tag) const {
dwarf::Attribute
DwarfCompileUnit::getDwarf5OrGNUAttr(dwarf::Attribute Attr) const {
- if (!useGNUAnalogForDwarf5Feature(DD))
+ if (!useGNUAnalogForDwarf5Feature())
return Attr;
switch (Attr) {
case dwarf::DW_AT_call_all_calls:
@@ -967,7 +966,7 @@ DwarfCompileUnit::getDwarf5OrGNUAttr(dwarf::Attribute Attr) const {
dwarf::LocationAtom
DwarfCompileUnit::getDwarf5OrGNULocationAtom(dwarf::LocationAtom Loc) const {
- if (!useGNUAnalogForDwarf5Feature(DD))
+ if (!useGNUAnalogForDwarf5Feature())
return Loc;
switch (Loc) {
case dwarf::DW_OP_entry_value:
@@ -981,6 +980,7 @@ DIE &DwarfCompileUnit::constructCallSiteEntryDIE(DIE &ScopeDIE,
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,16 +996,33 @@ DIE &DwarfCompileUnit::constructCallSiteEntryDIE(DIE &ScopeDIE,
*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.
+ //
+ // GDB works backwards from non-standard usage of DW_AT_low_pc (in DWARF4
+ // mode -- equivalently, in DWARF5 mode, DW_AT_call_return_pc) at tail-call
+ // site entries to figure out the PC of tail-calling branch instructions.
+ // This means it doesn't need the compiler to emit DW_AT_call_pc, so we
+ // don't emit it here.
+ //
+ // There's no need to tie non-GDB debuggers to this non-standardness, as it
+ // adds unnecessary complexity to the debugger. For non-GDB debuggers, emit
+ // the standard DW_AT_call_pc info.
+ if (!useGNUAnalogForDwarf5Feature())
+ 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.
//
// The return PC is only really needed when the call /isn't/ a tail call, but
- // for some reason GDB always expects it.
- if (!IsTail || DD->tuneForGDB()) {
+ // GDB expects it in DWARF4 mode, even for tail calls (see the comment above
+ // the DW_AT_call_pc emission logic for an explanation).
+ if (!IsTail || useGNUAnalogForDwarf5Feature()) {
assert(PCAddr && "Missing return PC information for a call");
addLabelAddress(CallSiteDIE,
getDwarf5OrGNUAttr(dwarf::DW_AT_call_return_pc), PCAddr);
diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.h b/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.h
index 52e1c71a8e65..5d0afee4c3df 100644
--- a/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.h
+++ b/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.h
@@ -230,6 +230,10 @@ class DwarfCompileUnit final : public DwarfUnit {
void constructAbstractSubprogramScopeDIE(LexicalScope *Scope);
+ /// Whether to use the GNU analog for a DWARF5 tag, attribute, or location
+ /// atom. Only applicable when emitting otherwise DWARF4-compliant debug info.
+ bool useGNUAnalogForDwarf5Feature() const;
+
/// This takes a DWARF 5 tag and returns it or a GNU analog.
dwarf::Tag getDwarf5OrGNUTag(dwarf::Tag Tag) const;
@@ -245,10 +249,12 @@ class DwarfCompileUnit final : public DwarfUnit {
/// 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
diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
index 529c88f2ac91..94ceab2c3b82 100644
--- a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
@@ -878,16 +878,21 @@ void DwarfDebug::constructCallSiteEntryDIEs(const DISubprogram &SP,
const MachineInstr *TopLevelCallMI =
MI.isInsideBundle() ? &*getBundleStart(MI.getIterator()) : &MI;
- // For tail calls, no return PC information is needed.
- // For regular calls (and tail calls in GDB tuning), the return PC
- // is needed to disambiguate paths in the call graph which could lead to
- // some target function.
+ // For non-tail calls, the return PC is needed to disambiguate paths in
+ // the call graph which could lead to some target function. For tail
+ // calls, no return PC information is needed, unless tuning for GDB in
+ // DWARF4 mode in which case we fake a return PC for compatibility.
const MCSymbol *PCAddr =
- (IsTail && !tuneForGDB())
- ? nullptr
- : const_cast<MCSymbol *>(getLabelAfterInsn(TopLevelCallMI));
+ (!IsTail || CU.useGNUAnalogForDwarf5Feature())
+ ? const_cast<MCSymbol *>(getLabelAfterInsn(TopLevelCallMI))
+ : nullptr;
- assert((IsTail || PCAddr) && "Call without return PC information");
+ // 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) && "Non-tail call without return PC");
LLVM_DEBUG(dbgs() << "CallSiteEntry: " << MF.getName() << " -> "
<< (CalleeDecl ? CalleeDecl->getName()
@@ -896,8 +901,8 @@ void DwarfDebug::constructCallSiteEntryDIEs(const DISubprogram &SP,
->getName(CallReg)))
<< (IsTail ? " [IsTail]" : "") << "\n");
- DIE &CallSiteDIE = CU.constructCallSiteEntryDIE(ScopeDIE, CalleeDIE,
- IsTail, PCAddr, CallReg);
+ DIE &CallSiteDIE = CU.constructCallSiteEntryDIE(
+ ScopeDIE, CalleeDIE, IsTail, PCAddr, CallAddr, CallReg);
// Optionally emit call-site-param debug info.
if (emitDebugEntryValues()) {
@@ -1786,11 +1791,32 @@ void DwarfDebug::collectEntityInfo(DwarfCompileUnit &TheCU,
// 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.
@@ -1804,11 +1830,6 @@ 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)
diff --git a/llvm/lib/DWARFLinker/DWARFLinker.cpp b/llvm/lib/DWARFLinker/DWARFLinker.cpp
index 8464c04f801e..ab18cb005a3a 100644
--- a/llvm/lib/DWARFLinker/DWARFLinker.cpp
+++ b/llvm/lib/DWARFLinker/DWARFLinker.cpp
@@ -1056,6 +1056,10 @@ unsigned DWARFLinker::DIECloner::cloneAddressAttribute(
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<dwarf::Attribute>(AttrSpec.Attr),
diff --git a/llvm/test/DebugInfo/MIR/X86/call-site-gnu-vs-dwarf5-attrs.mir b/llvm/test/DebugInfo/MIR/X86/call-site-gnu-vs-dwarf5-attrs.mir
index 63d03050918f..b60c10a04e59 100644
--- a/llvm/test/DebugInfo/MIR/X86/call-site-gnu-vs-dwarf5-attrs.mir
+++ b/llvm/test/DebugInfo/MIR/X86/call-site-gnu-vs-dwarf5-attrs.mir
@@ -1,16 +1,24 @@
# Test the call site encoding in DWARF5 vs GNU extensions.
#
+# === DWARF4, tune for gdb ===
# RUN: llc -emit-call-site-info -dwarf-version 4 -debugger-tune=gdb -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 -emit-call-site-info -dwarf-version 5 -debugger-tune=lldb -filetype=obj \
+# === DWARF5, tune for gdb ===
+# RUN: llc -dwarf-version 5 -debugger-tune=gdb -emit-call-site-info -filetype=obj \
+# RUN: -mtriple=x86_64-unknown-unknown -start-after=machineverifier -o - %s \
+# RUN: | llvm-dwarfdump - | FileCheck %s -check-prefixes=CHECK-DWARF5 -implicit-check-not=DW_AT_call
+#
+# === DWARF4, tune for lldb ===
+# RUN: llc -dwarf-version 4 -debugger-tune=lldb -emit-call-site-info -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 -emit-call-site-info -dwarf-version 5 -filetype=obj \
+# === DWARF5, tune for lldb ===
+# RUN: llc -dwarf-version 5 -debugger-tune=lldb -emit-call-site-info -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 -emit-call-site-info -dwarf-version 5 -filetype=obj -debugger-tune=sce \
# RUN: -emit-debug-entry-values -debug-entry-values -mtriple=x86_64-unknown-unknown \
@@ -49,6 +57,7 @@
# CHECK-GNU: DW_TAG_GNU_call_site
# CHECK-GNU-NEXT: DW_AT_abstract_origin
# CHECK-GNU-NEXT: DW_AT_GNU_tail_call
+# CHECK-GNU-NEXT: DW_AT_low_pc
#
#
# Check DWARF 5:
@@ -58,6 +67,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
@@ -67,6 +79,7 @@
# 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
#
--- |
; ModuleID = 'call-site-attrs.c'
diff --git a/llvm/test/DebugInfo/X86/dwarf-callsite-related-attrs.ll b/llvm/test/DebugInfo/X86/dwarf-callsite-related-attrs.ll
index 931f77c72f78..3a34ff38809f 100644
--- a/llvm/test/DebugInfo/X86/dwarf-callsite-related-attrs.ll
+++ b/llvm/test/DebugInfo/X86/dwarf-callsite-related-attrs.ll
@@ -14,7 +14,7 @@
; 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
@@ -75,6 +75,7 @@ entry:
; 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()
diff --git a/llvm/test/tools/dsymutil/X86/Inputs/tail-call.cpp b/llvm/test/tools/dsymutil/X86/Inputs/tail-call.cpp
new file mode 100644
index 000000000000..57e512e7009d
--- /dev/null
+++ b/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 */ }
diff --git a/llvm/test/tools/dsymutil/X86/Inputs/tail-call.macho.x86_64 b/llvm/test/tools/dsymutil/X86/Inputs/tail-call.macho.x86_64
new file mode 100755
index 000000000000..d6098d0de5e4
Binary files /dev/null and b/llvm/test/tools/dsymutil/X86/Inputs/tail-call.macho.x86_64
diff er
diff --git a/llvm/test/tools/dsymutil/X86/Inputs/tail-call.macho.x86_64.o b/llvm/test/tools/dsymutil/X86/Inputs/tail-call.macho.x86_64.o
new file mode 100644
index 000000000000..1d5726d12e34
Binary files /dev/null and b/llvm/test/tools/dsymutil/X86/Inputs/tail-call.macho.x86_64.o
diff er
diff --git a/llvm/test/tools/dsymutil/X86/tail-call-linking.test b/llvm/test/tools/dsymutil/X86/tail-call-linking.test
new file mode 100644
index 000000000000..29ae2cc544cf
--- /dev/null
+++ b/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)
More information about the llvm-commits
mailing list