Reland "[DwarfDebug] Dump call site debug info"

The build failure found after the rL365467 has been
resolved.

Differential Revision: https://reviews.llvm.org/D60716

llvm-svn: 367446
This commit is contained in:
Djordje Todorovic 2019-07-31 16:51:28 +00:00
parent f7ef70501c
commit b9973f87c6
23 changed files with 1143 additions and 52 deletions

View File

@ -4769,6 +4769,10 @@ The current supported opcode vocabulary is limited:
``DW_OP_entry_value`` may also appear after the ``AsmPrinter`` pass when ``DW_OP_entry_value`` may also appear after the ``AsmPrinter`` pass when
a call site parameter value (``DW_AT_call_site_parameter_value``) a call site parameter value (``DW_AT_call_site_parameter_value``)
is represented as entry value of the parameter. is represented as entry value of the parameter.
- ``DW_OP_breg`` (or ``DW_OP_bregx``) represents a content on the provided
signed offset of the specified register. The opcode is only generated by the
``AsmPrinter`` pass to describe call site parameter value which requires an
expression over two registers.
DWARF specifies three kinds of simple location descriptions: Register, memory, DWARF specifies three kinds of simple location descriptions: Register, memory,
and implicit location descriptions. Note that a location description is and implicit location descriptions. Note that a location description is

View File

@ -60,6 +60,8 @@ class TargetSubtargetInfo;
template <class T> class SmallVectorImpl; template <class T> class SmallVectorImpl;
using ParamLoadedValue = std::pair<const MachineOperand*, DIExpression*>;
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
/// ///
/// TargetInstrInfo - Interface to description of machine instruction set /// TargetInstrInfo - Interface to description of machine instruction set
@ -1691,6 +1693,11 @@ public:
return false; return false;
} }
/// Produce the expression describing the \p MI loading a value into
/// the parameter's forwarding register.
virtual Optional<ParamLoadedValue>
describeLoadedValue(const MachineInstr &MI) const;
private: private:
unsigned CallFrameSetupOpcode, CallFrameDestroyOpcode; unsigned CallFrameSetupOpcode, CallFrameDestroyOpcode;
unsigned CatchRetOpcode; unsigned CatchRetOpcode;

View File

@ -535,6 +535,11 @@ public:
return false; return false;
} }
/// This is a wrapper around getCallPreservedMask().
/// Return true if the register is preserved after the call.
virtual bool isCalleeSavedPhysReg(unsigned PhysReg,
const MachineFunction &MF) const;
/// Prior to adding the live-out mask to a stackmap or patchpoint /// Prior to adding the live-out mask to a stackmap or patchpoint
/// instruction, provide the target the opportunity to adjust it (mainly to /// instruction, provide the target the opportunity to adjust it (mainly to
/// remove pseudo-registers that should be ignored). /// remove pseudo-registers that should be ignored).

View File

@ -892,32 +892,106 @@ void DwarfCompileUnit::constructAbstractSubprogramScopeDIE(
ContextCU->addDIEEntry(*AbsDef, dwarf::DW_AT_object_pointer, *ObjectPointer); ContextCU->addDIEEntry(*AbsDef, dwarf::DW_AT_object_pointer, *ObjectPointer);
} }
DIE &DwarfCompileUnit::constructCallSiteEntryDIE(DIE &ScopeDIE, dwarf::Tag DwarfCompileUnit::getDwarf5OrGNUCallSiteTag(dwarf::Tag Tag) const {
const DISubprogram &CalleeSP, bool ApplyGNUExtensions = DD->getDwarfVersion() == 4 && DD->tuneForGDB();
bool IsTail, if (!ApplyGNUExtensions)
const MCExpr *PCOffset) { return Tag;
switch (Tag) {
case dwarf::DW_TAG_call_site:
return dwarf::DW_TAG_GNU_call_site;
case dwarf::DW_TAG_call_site_parameter:
return dwarf::DW_TAG_GNU_call_site_parameter;
default:
llvm_unreachable("unhandled call site tag");
}
}
dwarf::Attribute
DwarfCompileUnit::getDwarf5OrGNUCallSiteAttr(dwarf::Attribute Attr) const {
bool ApplyGNUExtensions = DD->getDwarfVersion() == 4 && DD->tuneForGDB();
if (!ApplyGNUExtensions)
return Attr;
switch (Attr) {
case dwarf::DW_AT_call_all_calls:
return dwarf::DW_AT_GNU_all_call_sites;
case dwarf::DW_AT_call_target:
return dwarf::DW_AT_GNU_call_site_target;
case dwarf::DW_AT_call_origin:
return dwarf::DW_AT_abstract_origin;
case dwarf::DW_AT_call_pc:
return dwarf::DW_AT_low_pc;
case dwarf::DW_AT_call_value:
return dwarf::DW_AT_GNU_call_site_value;
case dwarf::DW_AT_call_tail_call:
return dwarf::DW_AT_GNU_tail_call;
default:
llvm_unreachable("unhandled call site attribute");
}
}
DIE &DwarfCompileUnit::constructCallSiteEntryDIE(
DIE &ScopeDIE, const DISubprogram *CalleeSP, bool IsTail,
const MCSymbol *PCAddr, const MCExpr *PCOffset, unsigned CallReg) {
// Insert a call site entry DIE within ScopeDIE. // Insert a call site entry DIE within ScopeDIE.
DIE &CallSiteDIE = DIE &CallSiteDIE = createAndAddDIE(
createAndAddDIE(dwarf::DW_TAG_call_site, ScopeDIE, nullptr); getDwarf5OrGNUCallSiteTag(dwarf::DW_TAG_call_site), ScopeDIE, nullptr);
// For the purposes of showing tail call frames in backtraces, a key piece of if (CallReg) {
// information is DW_AT_call_origin, a pointer to the callee DIE. // Indirect call.
DIE *CalleeDIE = getOrCreateSubprogramDIE(&CalleeSP); addAddress(CallSiteDIE,
assert(CalleeDIE && "Could not create DIE for call site entry origin"); getDwarf5OrGNUCallSiteAttr(dwarf::DW_AT_call_target),
addDIEEntry(CallSiteDIE, dwarf::DW_AT_call_origin, *CalleeDIE); MachineLocation(CallReg));
if (IsTail) {
// Attach DW_AT_call_tail_call to tail calls for standards compliance.
addFlag(CallSiteDIE, dwarf::DW_AT_call_tail_call);
} else { } else {
DIE *CalleeDIE = getOrCreateSubprogramDIE(CalleeSP);
assert(CalleeDIE && "Could not create DIE for call site entry origin");
addDIEEntry(CallSiteDIE,
getDwarf5OrGNUCallSiteAttr(dwarf::DW_AT_call_origin),
*CalleeDIE);
}
if (IsTail)
// Attach DW_AT_call_tail_call to tail calls for standards compliance.
addFlag(CallSiteDIE,
getDwarf5OrGNUCallSiteAttr(dwarf::DW_AT_call_tail_call));
// Attach the return PC to allow the debugger to disambiguate call paths // Attach the return PC to allow the debugger to disambiguate call paths
// from one function to another. // from one function to another.
if (DD->getDwarfVersion() == 4 && DD->tuneForGDB()) {
assert(PCAddr && "Missing PC information for a call");
addLabelAddress(CallSiteDIE, dwarf::DW_AT_low_pc, PCAddr);
} else if (!IsTail || DD->tuneForGDB()) {
assert(PCOffset && "Missing return PC information for a call"); assert(PCOffset && "Missing return PC information for a call");
addAddressExpr(CallSiteDIE, dwarf::DW_AT_call_return_pc, PCOffset); addAddressExpr(CallSiteDIE, dwarf::DW_AT_call_return_pc, PCOffset);
} }
return CallSiteDIE; return CallSiteDIE;
} }
void DwarfCompileUnit::constructCallSiteParmEntryDIEs(
DIE &CallSiteDIE, SmallVector<DbgCallSiteParam, 4> &Params) {
for (const auto &Param : Params) {
unsigned Register = Param.getRegister();
auto CallSiteDieParam =
DIE::get(DIEValueAllocator,
getDwarf5OrGNUCallSiteTag(dwarf::DW_TAG_call_site_parameter));
insertDIE(CallSiteDieParam);
addAddress(*CallSiteDieParam, dwarf::DW_AT_location,
MachineLocation(Register));
DIELoc *Loc = new (DIEValueAllocator) DIELoc;
DIEDwarfExpression DwarfExpr(*Asm, *this, *Loc);
DwarfExpr.setCallSiteParamValueFlag();
DwarfDebug::emitDebugLocValue(*Asm, nullptr, Param.getValue(), DwarfExpr);
addBlock(*CallSiteDieParam,
getDwarf5OrGNUCallSiteAttr(dwarf::DW_AT_call_value),
DwarfExpr.finalize());
CallSiteDIE.addChild(CallSiteDieParam);
}
}
DIE *DwarfCompileUnit::constructImportedEntityDIE( DIE *DwarfCompileUnit::constructImportedEntityDIE(
const DIImportedEntity *Module) { const DIImportedEntity *Module) {
DIE *IMDie = DIE::get(DIEValueAllocator, (dwarf::Tag)Module->getTag()); DIE *IMDie = DIE::get(DIEValueAllocator, (dwarf::Tag)Module->getTag());

View File

@ -227,12 +227,33 @@ public:
void constructAbstractSubprogramScopeDIE(LexicalScope *Scope); void constructAbstractSubprogramScopeDIE(LexicalScope *Scope);
/// This takes the official DWARF 5 tag and returns the appropriate
/// GNU tag if needed.
dwarf::Tag getDwarf5OrGNUCallSiteTag(dwarf::Tag Tag) const;
/// This takes the official DWARF 5 attribute and returns the appropriate
/// GNU attribute if needed.
dwarf::Attribute getDwarf5OrGNUCallSiteAttr(dwarf::Attribute Attr) const;
/// Construct a call site entry DIE describing a call within \p Scope to a /// 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 /// callee described by \p CalleeSP.
/// a tail call. \p PCOffset must be non-zero for non-tail calls or be the /// \p IsTail specifies whether the call is a tail call.
/// \p PCAddr (used for GDB + DWARF 4 tuning) points to the PC value after
/// the call instruction.
/// \p PCOffset (used for cases other than GDB + DWARF 4 tuning) must be
/// non-zero for non-tail calls (in the case of non-gdb tuning, since for
/// GDB + DWARF 5 tuning we still generate PC info for tail calls) or be the
/// function-local offset to PC value after the call instruction. /// function-local offset to PC value after the call instruction.
DIE &constructCallSiteEntryDIE(DIE &ScopeDIE, const DISubprogram &CalleeSP, /// \p CallReg is a register location for an indirect call. For direct calls
bool IsTail, const MCExpr *PCOffset); /// the \p CallReg is set to 0.
DIE &constructCallSiteEntryDIE(DIE &ScopeDIE, const DISubprogram *CalleeSP,
bool IsTail, const MCSymbol *PCAddr,
const MCExpr *PCOffset, 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
/// call site parameters by the DW_AT_location attribute.
void constructCallSiteParmEntryDIEs(DIE &CallSiteDIE,
SmallVector<DbgCallSiteParam, 4> &Params);
/// Construct import_module DIE. /// Construct import_module DIE.
DIE *constructImportedEntityDIE(const DIImportedEntity *Module); DIE *constructImportedEntityDIE(const DIImportedEntity *Module);

View File

@ -26,6 +26,7 @@
#include "llvm/ADT/STLExtras.h" #include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallVector.h" #include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h" #include "llvm/ADT/StringRef.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/ADT/Triple.h" #include "llvm/ADT/Triple.h"
#include "llvm/ADT/Twine.h" #include "llvm/ADT/Twine.h"
#include "llvm/BinaryFormat/Dwarf.h" #include "llvm/BinaryFormat/Dwarf.h"
@ -39,6 +40,7 @@
#include "llvm/CodeGen/MachineModuleInfo.h" #include "llvm/CodeGen/MachineModuleInfo.h"
#include "llvm/CodeGen/MachineOperand.h" #include "llvm/CodeGen/MachineOperand.h"
#include "llvm/CodeGen/TargetInstrInfo.h" #include "llvm/CodeGen/TargetInstrInfo.h"
#include "llvm/CodeGen/TargetLowering.h"
#include "llvm/CodeGen/TargetRegisterInfo.h" #include "llvm/CodeGen/TargetRegisterInfo.h"
#include "llvm/CodeGen/TargetSubtargetInfo.h" #include "llvm/CodeGen/TargetSubtargetInfo.h"
#include "llvm/DebugInfo/DWARF/DWARFExpression.h" #include "llvm/DebugInfo/DWARF/DWARFExpression.h"
@ -83,6 +85,8 @@ using namespace llvm;
#define DEBUG_TYPE "dwarfdebug" #define DEBUG_TYPE "dwarfdebug"
STATISTIC(NumCSParams, "Number of dbg call site params created");
static cl::opt<bool> static cl::opt<bool>
DisableDebugInfoPrinting("disable-debug-info-print", cl::Hidden, DisableDebugInfoPrinting("disable-debug-info-print", cl::Hidden,
cl::desc("Disable debug info printing")); cl::desc("Disable debug info printing"));
@ -551,6 +555,146 @@ void DwarfDebug::constructAbstractSubprogramScopeDIE(DwarfCompileUnit &SrcCU,
} }
} }
/// Try to interpret values loaded into registers that forward parameters
/// for \p CallMI. Store parameters with interpreted value into \p Params.
static void collectCallSiteParameters(const MachineInstr *CallMI,
ParamSet &Params) {
auto *MF = CallMI->getMF();
auto CalleesMap = MF->getCallSitesInfo();
auto CallFwdRegsInfo = CalleesMap.find(CallMI);
// There is no information for the call instruction.
if (CallFwdRegsInfo == CalleesMap.end())
return;
auto *MBB = CallMI->getParent();
const auto &TRI = MF->getSubtarget().getRegisterInfo();
const auto &TII = MF->getSubtarget().getInstrInfo();
const auto &TLI = MF->getSubtarget().getTargetLowering();
// Skip the call instruction.
auto I = std::next(CallMI->getReverseIterator());
DenseSet<unsigned> ForwardedRegWorklist;
// Add all the forwarding registers into the ForwardedRegWorklist.
for (auto ArgReg : CallFwdRegsInfo->second) {
bool InsertedReg = ForwardedRegWorklist.insert(ArgReg.Reg).second;
assert(InsertedReg && "Single register used to forward two arguments?");
}
// We erase, from the ForwardedRegWorklist, those forwarding registers for
// which we successfully describe a loaded value (by using
// the describeLoadedValue()). For those remaining arguments in the working
// list, for which we do not describe a loaded value by
// the describeLoadedValue(), we try to generate an entry value expression
// for their call site value desctipion, if the call is within the entry MBB.
// The RegsForEntryValues maps a forwarding register into the register holding
// the entry value.
// TODO: Handle situations when call site parameter value can be described
// as the entry value within basic blocks other then the first one.
bool ShouldTryEmitEntryVals = MBB->getIterator() == MF->begin();
DenseMap<unsigned, unsigned> RegsForEntryValues;
// If the MI is an instruction defining a parameter's forwarding register,
// add it into the Defs. If the MI clobbers more then one register, we use
// the Defs in order to remove all the registers from
// the ForwardedRegWorklist, since we do not support such situations now.
auto getForwardingRegsDefinedByMI = [&](const MachineInstr &MI,
SmallVectorImpl<unsigned> &Defs) {
if (MI.isDebugInstr())
return;
for (const MachineOperand &MO : MI.operands()) {
if (MO.isReg() && MO.isDef() && TRI->isPhysicalRegister(MO.getReg())) {
for (auto FwdReg : ForwardedRegWorklist) {
if (TRI->regsOverlap(FwdReg, MO.getReg())) {
Defs.push_back(FwdReg);
break;
}
}
}
}
};
auto finishCallSiteParam = [&](DbgValueLoc DbgLocVal, unsigned Reg) {
unsigned FwdReg = Reg;
if (ShouldTryEmitEntryVals) {
auto EntryValReg = RegsForEntryValues.find(Reg);
if (EntryValReg != RegsForEntryValues.end())
FwdReg = EntryValReg->second;
}
DbgCallSiteParam CSParm(FwdReg, DbgLocVal);
Params.push_back(CSParm);
++NumCSParams;
};
// Search for a loading value in forwaring registers.
for (; I != MBB->rend(); ++I) {
// If the next instruction is a call we can not interpret parameter's
// forwarding registers or we finished the interpretation of all parameters.
if (I->isCall())
return;
if (ForwardedRegWorklist.empty())
return;
SmallVector<unsigned, 4> Defs;
getForwardingRegsDefinedByMI(*I, Defs);
if (Defs.empty())
continue;
// If the MI clobbers more then one forwarding register we must remove
// all of them from the working list.
for (auto Reg : Defs)
ForwardedRegWorklist.erase(Reg);
if (I->getNumDefs() != 1)
continue;
unsigned Reg = Defs[0];
if (auto ParamValue = TII->describeLoadedValue(*I)) {
if (ParamValue->first->isImm()) {
unsigned Val = ParamValue->first->getImm();
DbgValueLoc DbgLocVal(ParamValue->second, Val);
finishCallSiteParam(DbgLocVal, Reg);
} else if (ParamValue->first->isReg()) {
unsigned RegLoc = ParamValue->first->getReg();
unsigned SP = TLI->getStackPointerRegisterToSaveRestore();
unsigned FP = TRI->getFrameRegister(*MF);
bool IsSPorFP = (RegLoc == SP) || (RegLoc == FP);
if (TRI->isCalleeSavedPhysReg(RegLoc, *MF) || IsSPorFP) {
DbgValueLoc DbgLocVal(ParamValue->second,
MachineLocation(RegLoc,
/*IsIndirect=*/IsSPorFP));
finishCallSiteParam(DbgLocVal, Reg);
} else if (ShouldTryEmitEntryVals) {
ForwardedRegWorklist.insert(RegLoc);
RegsForEntryValues[RegLoc] = Reg;
}
}
}
}
// Emit the call site parameter's value as an entry value.
if (ShouldTryEmitEntryVals) {
// Create an entry value expression where the expression following
// the 'DW_OP_entry_value' will be the size of 1 (a register operation).
DIExpression *EntryExpr = DIExpression::get(MF->getFunction().getContext(),
{dwarf::DW_OP_entry_value, 1});
for (auto RegEntry : ForwardedRegWorklist) {
unsigned FwdReg = RegEntry;
auto EntryValReg = RegsForEntryValues.find(RegEntry);
if (EntryValReg != RegsForEntryValues.end())
FwdReg = EntryValReg->second;
DbgValueLoc DbgLocVal(EntryExpr, MachineLocation(RegEntry));
DbgCallSiteParam CSParm(FwdReg, DbgLocVal);
Params.push_back(CSParm);
++NumCSParams;
}
}
}
void DwarfDebug::constructCallSiteEntryDIEs(const DISubprogram &SP, void DwarfDebug::constructCallSiteEntryDIEs(const DISubprogram &SP,
DwarfCompileUnit &CU, DIE &ScopeDIE, DwarfCompileUnit &CU, DIE &ScopeDIE,
const MachineFunction &MF) { const MachineFunction &MF) {
@ -563,10 +707,12 @@ void DwarfDebug::constructCallSiteEntryDIEs(const DISubprogram &SP,
// for both tail and non-tail calls. Don't use DW_AT_call_all_source_calls // 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 // because one of its requirements is not met: call site entries for
// optimized-out calls are elided. // optimized-out calls are elided.
CU.addFlag(ScopeDIE, dwarf::DW_AT_call_all_calls); CU.addFlag(ScopeDIE,
CU.getDwarf5OrGNUCallSiteAttr(dwarf::DW_AT_call_all_calls));
const TargetInstrInfo *TII = MF.getSubtarget().getInstrInfo(); const TargetInstrInfo *TII = MF.getSubtarget().getInstrInfo();
assert(TII && "TargetInstrInfo not found: cannot label tail calls"); assert(TII && "TargetInstrInfo not found: cannot label tail calls");
bool ApplyGNUExtensions = getDwarfVersion() == 4 && tuneForGDB();
// Emit call site entries for each call or tail call in the function. // Emit call site entries for each call or tail call in the function.
for (const MachineBasicBlock &MBB : MF) { for (const MachineBasicBlock &MBB : MF) {
@ -581,30 +727,66 @@ void DwarfDebug::constructCallSiteEntryDIEs(const DISubprogram &SP,
return; return;
// If this is a direct call, find the callee's subprogram. // If this is a direct call, find the callee's subprogram.
// In the case of an indirect call find the register that holds
// the callee.
const MachineOperand &CalleeOp = MI.getOperand(0); const MachineOperand &CalleeOp = MI.getOperand(0);
if (!CalleeOp.isGlobal()) if (!CalleeOp.isGlobal() && !CalleeOp.isReg())
continue;
const Function *CalleeDecl = dyn_cast<Function>(CalleeOp.getGlobal());
if (!CalleeDecl || !CalleeDecl->getSubprogram())
continue; continue;
unsigned CallReg = 0;
const DISubprogram *CalleeSP = nullptr;
const Function *CalleeDecl = nullptr;
if (CalleeOp.isReg()) {
CallReg = CalleeOp.getReg();
if (!CallReg)
continue;
} else {
CalleeDecl = dyn_cast<Function>(CalleeOp.getGlobal());
if (!CalleeDecl || !CalleeDecl->getSubprogram())
continue;
CalleeSP = CalleeDecl->getSubprogram();
}
// TODO: Omit call site entries for runtime calls (objc_msgSend, etc). // TODO: Omit call site entries for runtime calls (objc_msgSend, etc).
// TODO: Add support for indirect calls.
bool IsTail = TII->isTailCall(MI); bool IsTail = TII->isTailCall(MI);
// For tail calls, no return PC information is needed. For regular calls, // For tail calls, for non-gdb tuning, no return PC information is needed.
// the return PC is needed to disambiguate paths in the call graph which // For regular calls (and tail calls in GDB tuning), the return PC
// could lead to some target function. // is needed to disambiguate paths in the call graph which could lead to
// some target function.
const MCExpr *PCOffset = const MCExpr *PCOffset =
IsTail ? nullptr : getFunctionLocalOffsetAfterInsn(&MI); (IsTail && !tuneForGDB()) ? nullptr
: getFunctionLocalOffsetAfterInsn(&MI);
// Address of a call-like instruction for a normal call or a jump-like
// instruction for a tail call. This is needed for GDB + DWARF 4 tuning.
const MCSymbol *PCAddr =
ApplyGNUExtensions ? const_cast<MCSymbol*>(getLabelAfterInsn(&MI))
: nullptr;
assert((IsTail || PCOffset || PCAddr) &&
"Call without return PC information");
assert((IsTail || PCOffset) && "Call without return PC information");
LLVM_DEBUG(dbgs() << "CallSiteEntry: " << MF.getName() << " -> " LLVM_DEBUG(dbgs() << "CallSiteEntry: " << MF.getName() << " -> "
<< CalleeDecl->getName() << (IsTail ? " [tail]" : "") << (CalleeDecl ? CalleeDecl->getName()
<< "\n"); : StringRef(MF.getSubtarget()
CU.constructCallSiteEntryDIE(ScopeDIE, *CalleeDecl->getSubprogram(), .getRegisterInfo()
IsTail, PCOffset); ->getName(CallReg)))
<< (IsTail ? " [IsTail]" : "") << "\n");
DIE &CallSiteDIE =
CU.constructCallSiteEntryDIE(ScopeDIE, CalleeSP, IsTail, PCAddr,
PCOffset, CallReg);
// For now only GDB supports call site parameter debug info.
if (Asm->TM.Options.EnableDebugEntryValues &&
tuneForGDB()) {
ParamSet Params;
// Try to interpret values of call site parameters.
collectCallSiteParameters(&MI, Params);
CU.constructCallSiteParmEntryDIEs(CallSiteDIE, Params);
}
} }
} }
} }

View File

@ -254,6 +254,25 @@ public:
} }
}; };
/// Used for tracking debug info about call site parameters.
class DbgCallSiteParam {
private:
unsigned Register; ///< Parameter register at the callee entry point.
DbgValueLoc Value; ///< Corresponding location for the parameter value at
///< the call site.
public:
DbgCallSiteParam(unsigned Reg, DbgValueLoc Val)
: Register(Reg), Value(Val) {
assert(Reg && "Parameter register cannot be undef");
}
unsigned getRegister() const { return Register; }
DbgValueLoc getValue() const { return Value; }
};
/// Collection used for storing debug call site parameters.
using ParamSet = SmallVector<DbgCallSiteParam, 4>;
/// Helper used to pair up a symbol and its DWARF compile unit. /// Helper used to pair up a symbol and its DWARF compile unit.
struct SymbolCU { struct SymbolCU {
SymbolCU(DwarfCompileUnit *CU, const MCSymbol *Sym) : Sym(Sym), CU(CU) {} SymbolCU(DwarfCompileUnit *CU, const MCSymbol *Sym) : Sym(Sym), CU(CU) {}

View File

@ -241,15 +241,19 @@ bool DwarfExpression::addMachineRegExpression(const TargetRegisterInfo &TRI,
return false; return false;
} }
// Handle simple register locations. // Handle simple register locations. If we are supposed to emit
if (!isMemoryLocation() && !HasComplexExpression) { // a call site parameter expression and if that expression is just a register
// location, emit it with addBReg and offset 0, because we should emit a DWARF
// expression representing a value, rather than a location.
if (!isMemoryLocation() && !HasComplexExpression && (!isParameterValue() ||
isEntryValue())) {
for (auto &Reg : DwarfRegs) { for (auto &Reg : DwarfRegs) {
if (Reg.DwarfRegNo >= 0) if (Reg.DwarfRegNo >= 0)
addReg(Reg.DwarfRegNo, Reg.Comment); addReg(Reg.DwarfRegNo, Reg.Comment);
addOpPiece(Reg.Size); addOpPiece(Reg.Size);
} }
if (isEntryValue() && DwarfVersion >= 4) if (isEntryValue() && !isParameterValue() && DwarfVersion >= 4)
emitOp(dwarf::DW_OP_stack_value); emitOp(dwarf::DW_OP_stack_value);
DwarfRegs.clear(); DwarfRegs.clear();
@ -340,7 +344,17 @@ void DwarfExpression::addExpression(DIExpressionCursor &&ExprCursor,
while (ExprCursor) { while (ExprCursor) {
auto Op = ExprCursor.take(); auto Op = ExprCursor.take();
switch (Op->getOp()) { uint64_t OpNum = Op->getOp();
if (OpNum >= dwarf::DW_OP_reg0 && OpNum <= dwarf::DW_OP_reg31) {
emitOp(OpNum);
continue;
} else if (OpNum >= dwarf::DW_OP_breg0 && OpNum <= dwarf::DW_OP_breg31) {
addBReg(OpNum - dwarf::DW_OP_breg0, Op->getArg(0));
continue;
}
switch (OpNum) {
case dwarf::DW_OP_LLVM_fragment: { case dwarf::DW_OP_LLVM_fragment: {
unsigned SizeInBits = Op->getArg(1); unsigned SizeInBits = Op->getArg(1);
unsigned FragmentOffset = Op->getArg(0); unsigned FragmentOffset = Op->getArg(0);
@ -389,7 +403,7 @@ void DwarfExpression::addExpression(DIExpressionCursor &&ExprCursor,
case dwarf::DW_OP_lit0: case dwarf::DW_OP_lit0:
case dwarf::DW_OP_not: case dwarf::DW_OP_not:
case dwarf::DW_OP_dup: case dwarf::DW_OP_dup:
emitOp(Op->getOp()); emitOp(OpNum);
break; break;
case dwarf::DW_OP_deref: case dwarf::DW_OP_deref:
assert(!isRegisterLocation()); assert(!isRegisterLocation());
@ -458,12 +472,21 @@ void DwarfExpression::addExpression(DIExpressionCursor &&ExprCursor,
case dwarf::DW_OP_LLVM_tag_offset: case dwarf::DW_OP_LLVM_tag_offset:
TagOffset = Op->getArg(0); TagOffset = Op->getArg(0);
break; break;
case dwarf::DW_OP_regx:
emitOp(dwarf::DW_OP_regx);
emitUnsigned(Op->getArg(0));
break;
case dwarf::DW_OP_bregx:
emitOp(dwarf::DW_OP_bregx);
emitUnsigned(Op->getArg(0));
emitSigned(Op->getArg(1));
break;
default: default:
llvm_unreachable("unhandled opcode found in expression"); llvm_unreachable("unhandled opcode found in expression");
} }
} }
if (isImplicitLocation()) if (isImplicitLocation() && !isParameterValue())
// Turn this into an implicit location description. // Turn this into an implicit location description.
addStackValue(); addStackValue();
} }

View File

@ -120,7 +120,7 @@ protected:
enum { Unknown = 0, Register, Memory, Implicit }; enum { Unknown = 0, Register, Memory, Implicit };
/// The flags of location description being produced. /// The flags of location description being produced.
enum { EntryValue = 1 }; enum { EntryValue = 1, CallSiteParamValue };
unsigned LocationKind : 3; unsigned LocationKind : 3;
unsigned LocationFlags : 2; unsigned LocationFlags : 2;
@ -147,6 +147,10 @@ public:
return LocationFlags & EntryValue; return LocationFlags & EntryValue;
} }
bool isParameterValue() {
return LocationFlags & CallSiteParamValue;
}
Optional<uint8_t> TagOffset; Optional<uint8_t> TagOffset;
protected: protected:
@ -264,6 +268,11 @@ public:
LocationFlags |= EntryValue; LocationFlags |= EntryValue;
} }
/// Lock this down to become a call site parameter location.
void setCallSiteParamValueFlag() {
LocationFlags |= CallSiteParamValue;
}
/// Emit a machine register location. As an optimization this may also consume /// Emit a machine register location. As an optimization this may also consume
/// the prefix of a DwarfExpression if a more efficient representation for /// the prefix of a DwarfExpression if a more efficient representation for
/// combining the register location and the first operation exists. /// combining the register location and the first operation exists.

View File

@ -205,6 +205,10 @@ void DwarfUnit::insertDIE(const DINode *Desc, DIE *D) {
MDNodeToDieMap.insert(std::make_pair(Desc, D)); MDNodeToDieMap.insert(std::make_pair(Desc, D));
} }
void DwarfUnit::insertDIE(DIE *D) {
MDNodeToDieMap.insert(std::make_pair(nullptr, D));
}
void DwarfUnit::addFlag(DIE &Die, dwarf::Attribute Attribute) { void DwarfUnit::addFlag(DIE &Die, dwarf::Attribute Attribute) {
if (DD->getDwarfVersion() >= 4) if (DD->getDwarfVersion() >= 4)
Die.addValue(DIEValueAllocator, Attribute, dwarf::DW_FORM_flag_present, Die.addValue(DIEValueAllocator, Attribute, dwarf::DW_FORM_flag_present,

View File

@ -127,6 +127,8 @@ public:
/// the mappings are kept in DwarfDebug. /// the mappings are kept in DwarfDebug.
void insertDIE(const DINode *Desc, DIE *D); void insertDIE(const DINode *Desc, DIE *D);
void insertDIE(DIE *D);
/// Add a flag that is true to the DIE. /// Add a flag that is true to the DIE.
void addFlag(DIE &Die, dwarf::Attribute Attribute); void addFlag(DIE &Die, dwarf::Attribute Attribute);

View File

@ -23,6 +23,7 @@
#include "llvm/CodeGen/TargetRegisterInfo.h" #include "llvm/CodeGen/TargetRegisterInfo.h"
#include "llvm/CodeGen/TargetSchedule.h" #include "llvm/CodeGen/TargetSchedule.h"
#include "llvm/IR/DataLayout.h" #include "llvm/IR/DataLayout.h"
#include "llvm/IR/DebugInfoMetadata.h"
#include "llvm/MC/MCAsmInfo.h" #include "llvm/MC/MCAsmInfo.h"
#include "llvm/MC/MCInstrItineraries.h" #include "llvm/MC/MCInstrItineraries.h"
#include "llvm/Support/CommandLine.h" #include "llvm/Support/CommandLine.h"
@ -1120,6 +1121,36 @@ bool TargetInstrInfo::hasLowDefLatency(const TargetSchedModel &SchedModel,
return (DefCycle != -1 && DefCycle <= 1); return (DefCycle != -1 && DefCycle <= 1);
} }
Optional<ParamLoadedValue>
TargetInstrInfo::describeLoadedValue(const MachineInstr &MI) const {
const MachineFunction *MF = MI.getMF();
const MachineOperand *Op = nullptr;
DIExpression *Expr = DIExpression::get(MF->getFunction().getContext(), {});;
const MachineOperand *SrcRegOp, *DestRegOp;
if (isCopyInstr(MI, SrcRegOp, DestRegOp)) {
Op = SrcRegOp;
return ParamLoadedValue(Op, Expr);
} else if (MI.isMoveImmediate()) {
Op = &MI.getOperand(1);
return ParamLoadedValue(Op, Expr);
} else if (MI.hasOneMemOperand()) {
int64_t Offset;
const auto &TRI = MF->getSubtarget().getRegisterInfo();
const auto &TII = MF->getSubtarget().getInstrInfo();
const MachineOperand *BaseOp;
if (!TII->getMemOperandWithOffset(MI, BaseOp, Offset, TRI))
return None;
Expr = DIExpression::prepend(Expr, DIExpression::DerefAfter, Offset);
Op = BaseOp;
return ParamLoadedValue(Op, Expr);
}
return None;
}
/// Both DefMI and UseMI must be valid. By default, call directly to the /// Both DefMI and UseMI must be valid. By default, call directly to the
/// itinerary. This may be overriden by the target. /// itinerary. This may be overriden by the target.
int TargetInstrInfo::getOperandLatency(const InstrItineraryData *ItinData, int TargetInstrInfo::getOperandLatency(const InstrItineraryData *ItinData,

View File

@ -433,6 +433,19 @@ TargetRegisterInfo::getRegAllocationHints(unsigned VirtReg,
return false; return false;
} }
bool TargetRegisterInfo::isCalleeSavedPhysReg(
unsigned PhysReg, const MachineFunction &MF) const {
if (PhysReg == 0)
return false;
const uint32_t *callerPreservedRegs =
getCallPreservedMask(MF, MF.getFunction().getCallingConv());
if (callerPreservedRegs) {
assert(isPhysicalRegister(PhysReg) && "Expected physical register");
return (callerPreservedRegs[PhysReg / 32] >> PhysReg % 32) & 1;
}
return false;
}
bool TargetRegisterInfo::canRealignStack(const MachineFunction &MF) const { bool TargetRegisterInfo::canRealignStack(const MachineFunction &MF) const {
return !MF.getFunction().hasFnAttribute("no-realign-stack"); return !MF.getFunction().hasFnAttribute("no-realign-stack");
} }

View File

@ -733,6 +733,7 @@ bool DWARFAttribute::mayHaveLocationDescription(dwarf::Attribute Attr) {
case DW_AT_call_data_value: case DW_AT_call_data_value:
// Extensions. // Extensions.
case DW_AT_GNU_call_site_value: case DW_AT_GNU_call_site_value:
case DW_AT_GNU_call_site_target:
return true; return true;
default: default:
return false; return false;

View File

@ -828,15 +828,23 @@ DIExpression *DIExpression::getImpl(LLVMContext &Context,
} }
unsigned DIExpression::ExprOperand::getSize() const { unsigned DIExpression::ExprOperand::getSize() const {
switch (getOp()) { uint64_t Op = getOp();
if (Op >= dwarf::DW_OP_breg0 && Op <= dwarf::DW_OP_breg31)
return 2;
switch (Op) {
case dwarf::DW_OP_LLVM_convert: case dwarf::DW_OP_LLVM_convert:
case dwarf::DW_OP_LLVM_fragment: case dwarf::DW_OP_LLVM_fragment:
case dwarf::DW_OP_bregx:
return 3; return 3;
case dwarf::DW_OP_constu: case dwarf::DW_OP_constu:
case dwarf::DW_OP_consts:
case dwarf::DW_OP_deref_size: case dwarf::DW_OP_deref_size:
case dwarf::DW_OP_plus_uconst: case dwarf::DW_OP_plus_uconst:
case dwarf::DW_OP_LLVM_tag_offset: case dwarf::DW_OP_LLVM_tag_offset:
case dwarf::DW_OP_entry_value: case dwarf::DW_OP_entry_value:
case dwarf::DW_OP_regx:
return 2; return 2;
default: default:
return 1; return 1;
@ -849,8 +857,13 @@ bool DIExpression::isValid() const {
if (I->get() + I->getSize() > E->get()) if (I->get() + I->getSize() > E->get())
return false; return false;
uint64_t Op = I->getOp();
if ((Op >= dwarf::DW_OP_reg0 && Op <= dwarf::DW_OP_reg31) ||
(Op >= dwarf::DW_OP_breg0 && Op <= dwarf::DW_OP_breg31))
return true;
// Check that the operand is valid. // Check that the operand is valid.
switch (I->getOp()) { switch (Op) {
default: default:
return false; return false;
case dwarf::DW_OP_LLVM_fragment: case dwarf::DW_OP_LLVM_fragment:
@ -905,6 +918,8 @@ bool DIExpression::isValid() const {
case dwarf::DW_OP_lit0: case dwarf::DW_OP_lit0:
case dwarf::DW_OP_not: case dwarf::DW_OP_not:
case dwarf::DW_OP_dup: case dwarf::DW_OP_dup:
case dwarf::DW_OP_regx:
case dwarf::DW_OP_bregx:
break; break;
} }
} }

View File

@ -30,7 +30,7 @@
#include "llvm/CodeGen/StackMaps.h" #include "llvm/CodeGen/StackMaps.h"
#include "llvm/IR/DerivedTypes.h" #include "llvm/IR/DerivedTypes.h"
#include "llvm/IR/Function.h" #include "llvm/IR/Function.h"
#include "llvm/IR/LLVMContext.h" #include "llvm/IR/DebugInfoMetadata.h"
#include "llvm/MC/MCAsmInfo.h" #include "llvm/MC/MCAsmInfo.h"
#include "llvm/MC/MCExpr.h" #include "llvm/MC/MCExpr.h"
#include "llvm/MC/MCInst.h" #include "llvm/MC/MCInst.h"
@ -7367,6 +7367,92 @@ bool X86InstrInfo::isAssociativeAndCommutative(const MachineInstr &Inst) const {
} }
} }
Optional<ParamLoadedValue>
X86InstrInfo::describeLoadedValue(const MachineInstr &MI) const {
const MachineOperand *Op = nullptr;
DIExpression *Expr = nullptr;
switch (MI.getOpcode()) {
case X86::LEA32r:
case X86::LEA64r:
case X86::LEA64_32r: {
// Operand 4 could be global address. For now we do not support
// such situation.
if (!MI.getOperand(4).isImm() || !MI.getOperand(2).isImm())
return None;
const MachineOperand &Op1 = MI.getOperand(1);
const MachineOperand &Op2 = MI.getOperand(3);
const TargetRegisterInfo *TRI = &getRegisterInfo();
assert(Op2.isReg() &&
(Op2.getReg() == X86::NoRegister ||
TargetRegisterInfo::isPhysicalRegister(Op2.getReg())));
// Omit situations like:
// %rsi = lea %rsi, 4, ...
if ((Op1.isReg() && Op1.getReg() == MI.getOperand(0).getReg()) ||
Op2.getReg() == MI.getOperand(0).getReg())
return None;
else if ((Op1.isReg() && Op1.getReg() != X86::NoRegister &&
TRI->regsOverlap(Op1.getReg(), MI.getOperand(0).getReg())) ||
(Op2.getReg() != X86::NoRegister &&
TRI->regsOverlap(Op2.getReg(), MI.getOperand(0).getReg())))
return None;
int64_t Coef = MI.getOperand(2).getImm();
int64_t Offset = MI.getOperand(4).getImm();
SmallVector<uint64_t, 8> Ops;
if ((Op1.isReg() && Op1.getReg() != X86::NoRegister)) {
Op = &Op1;
} else if (Op1.isFI())
Op = &Op1;
if (Op && Op->isReg() && Op->getReg() == Op2.getReg() && Coef > 0) {
Ops.push_back(dwarf::DW_OP_constu);
Ops.push_back(Coef + 1);
Ops.push_back(dwarf::DW_OP_mul);
} else {
if (Op && Op2.getReg() != X86::NoRegister) {
int dwarfReg = TRI->getDwarfRegNum(Op2.getReg(), false);
if (dwarfReg < 0)
return None;
else if (dwarfReg < 32) {
Ops.push_back(dwarf::DW_OP_breg0 + dwarfReg);
Ops.push_back(0);
} else {
Ops.push_back(dwarf::DW_OP_bregx);
Ops.push_back(dwarfReg);
Ops.push_back(0);
}
} else if (!Op) {
assert(Op2.getReg() != X86::NoRegister);
Op = &Op2;
}
if (Coef > 1) {
assert(Op2.getReg() != X86::NoRegister);
Ops.push_back(dwarf::DW_OP_constu);
Ops.push_back(Coef);
Ops.push_back(dwarf::DW_OP_mul);
}
if (((Op1.isReg() && Op1.getReg() != X86::NoRegister) || Op1.isFI()) &&
Op2.getReg() != X86::NoRegister) {
Ops.push_back(dwarf::DW_OP_plus);
}
}
DIExpression::appendOffset(Ops, Offset);
Expr = DIExpression::get(MI.getMF()->getFunction().getContext(), Ops);
return ParamLoadedValue(Op, Expr);;
}
default:
return TargetInstrInfo::describeLoadedValue(MI);
}
}
/// This is an architecture-specific helper function of reassociateOps. /// This is an architecture-specific helper function of reassociateOps.
/// Set special operand attributes for new instructions after reassociation. /// Set special operand attributes for new instructions after reassociation.
void X86InstrInfo::setSpecialOperandAttr(MachineInstr &OldMI1, void X86InstrInfo::setSpecialOperandAttr(MachineInstr &OldMI1,

View File

@ -527,6 +527,9 @@ public:
#define GET_INSTRINFO_HELPER_DECLS #define GET_INSTRINFO_HELPER_DECLS
#include "X86GenInstrInfo.inc" #include "X86GenInstrInfo.inc"
Optional<ParamLoadedValue>
describeLoadedValue(const MachineInstr &MI) const override;
protected: protected:
/// Commutes the operands in the given instruction by changing the operands /// Commutes the operands in the given instruction by changing the operands
/// order and/or changing the instruction's opcode and/or the immediate value /// order and/or changing the instruction's opcode and/or the immediate value

View File

@ -0,0 +1,202 @@
# RUN: llc -debug-entry-values -start-after=machineverifier -filetype=obj %s -o -| llvm-dwarfdump -| FileCheck %s
#
# CHECK: DW_TAG_GNU_call_site
# CHECK-NEXT: DW_AT_abstract_origin {{.*}} "foo"
# CHECK-NEXT: DW_AT_low_pc
# CHECK-EMPTY:
# CHECK-NEXT: DW_TAG_GNU_call_site_parameter
# CHECK-NEXT: DW_AT_location (DW_OP_reg2 RCX)
# CHECK-NEXT: DW_AT_GNU_call_site_value (DW_OP_breg14 R14+0)
# CHECK-EMPTY:
# CHECK-NEXT: DW_TAG_GNU_call_site_parameter
# CHECK-NEXT: DW_AT_location (DW_OP_reg1 RDX)
# CHECK-NEXT: DW_AT_GNU_call_site_value (DW_OP_fbreg +8)
# CHECK-EMPTY:
# CHECK-NEXT: DW_TAG_GNU_call_site_parameter
# CHECK-NEXT: DW_AT_location (DW_OP_reg5 RDI)
# CHECK-NEXT: DW_AT_GNU_call_site_value (DW_OP_GNU_entry_value(DW_OP_reg4 RSI))
# CHECK-EMPTY:
# CHECK: DW_TAG_GNU_call_site
# CHECK-NEXT: DW_AT_abstract_origin {{.*}}"foo"
# CHECK-NEXT: DW_AT_low_pc
# CHECK-EMPTY:
# CHECK-NEXT: DW_TAG_GNU_call_site_parameter
# CHECK-NEXT: DW_AT_location (DW_OP_reg2 RCX)
# CHECK-NEXT: DW_AT_GNU_call_site_value (DW_OP_fbreg +8, DW_OP_deref)
# CHECK-EMPTY:
# CHECK-NEXT: DW_TAG_GNU_call_site_parameter
# CHECK-NEXT: DW_AT_location (DW_OP_reg4 RSI)
# CHECK-NEXT: DW_AT_GNU_call_site_value (DW_OP_lit4)
# CHECK-EMPTY:
# CHECK-NOT: DW_TAG_GNU_call_site_parameter
#
# Check that call site interpretation analysis can interpret instructions such
# as move immediate, register to register moves, stack loading and LEA
# instructions. Last negative check should verify that we are not producing
# interpretation for RDX register since its loaded value is call clobberable.
# Also check that we are generating proper call site debug entities.
--- |
; ModuleID = 'dbgcall-site-interpretation.c'
source_filename = "dbgcall-site-interpretation.c"
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"
; Function Attrs: nounwind uwtable
define dso_local i32 @baa(i32 %arg1, i32 %arg2, i32 %arg3, i32 %arg4) local_unnamed_addr !dbg !9 {
entry:
%arg3.addr = alloca i32, align 4
%local2 = alloca i32, align 4
call void @llvm.dbg.value(metadata i32 %arg1, metadata !14, metadata !DIExpression()), !dbg !21
call void @llvm.dbg.value(metadata i32 %arg2, metadata !15, metadata !DIExpression()), !dbg !21
call void @llvm.dbg.value(metadata i32 %arg3, metadata !16, metadata !DIExpression()), !dbg !21
store i32 %arg3, i32* %arg3.addr, align 4
call void @llvm.dbg.value(metadata i32 %arg4, metadata !17, metadata !DIExpression()), !dbg !21
%0 = bitcast i32* %local2 to i8*, !dbg !21
call void @llvm.dbg.value(metadata i32* %arg3.addr, metadata !16, metadata !DIExpression(DW_OP_deref)), !dbg !21
%call = call i32 @foo(i32 %arg1, i32 %arg2, i32* nonnull %arg3.addr, i32 %arg4), !dbg !21
call void @llvm.dbg.value(metadata i32 %call, metadata !18, metadata !DIExpression()), !dbg !21
%cmp = icmp sgt i32 %arg1, %arg2, !dbg !21
%1 = load i32, i32* %arg3.addr, align 4, !dbg !21
call void @llvm.dbg.value(metadata i32 %1, metadata !16, metadata !DIExpression()), !dbg !21
%add = add nsw i32 %1, %arg1, !dbg !21
%add1 = add nsw i32 %arg4, %arg2, !dbg !21
%local1.0 = select i1 %cmp, i32 %add, i32 %add1, !dbg !21
call void @llvm.dbg.value(metadata i32 %local1.0, metadata !18, metadata !DIExpression()), !dbg !21
%rem = srem i32 %1, %arg1, !dbg !21
%tobool = icmp eq i32 %rem, 0, !dbg !21
%mul = mul nsw i32 %1, %arg1, !dbg !21
%add3 = add nsw i32 %1, %arg4, !dbg !21
%storemerge = select i1 %tobool, i32 %mul, i32 %add3, !dbg !21
call void @llvm.dbg.value(metadata i32 %storemerge, metadata !19, metadata !DIExpression()), !dbg !21
store i32 %storemerge, i32* %local2, align 4, !dbg !21
%cmp6 = icmp slt i32 %storemerge, %arg4, !dbg !21
%local3.0.v = select i1 %cmp6, i32 %local1.0, i32 %arg1, !dbg !21
%local3.0 = mul nsw i32 %local3.0.v, %storemerge, !dbg !21
call void @llvm.dbg.value(metadata i32 %local3.0, metadata !20, metadata !DIExpression()), !dbg !21
call void @llvm.dbg.value(metadata i32* %local2, metadata !19, metadata !DIExpression(DW_OP_deref)), !dbg !21
%call12 = call i32 @foo(i32 %local1.0, i32 4, i32* nonnull %local2, i32 %local3.0), !dbg !21
call void @llvm.dbg.value(metadata i32 %call12, metadata !14, metadata !DIExpression()), !dbg !21
%add13 = add nsw i32 %call12, 4, !dbg !21
ret i32 %add13, !dbg !21
}
declare !dbg !4 dso_local i32 @foo(i32, i32, i32*, i32) local_unnamed_addr
; Function Attrs: nounwind readnone speculatable
declare void @llvm.dbg.value(metadata, metadata, metadata)
!llvm.dbg.cu = !{!0}
!llvm.module.flags = !{!5, !6, !7}
!llvm.ident = !{!8}
!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 9.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, retainedTypes: !3, nameTableKind: None)
!1 = !DIFile(filename: "dbgcall-site-interpretation.c", directory: "/dir")
!2 = !{}
!3 = !{!4}
!4 = !DISubprogram(name: "foo", scope: !1, file: !1, line: 9, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !2)
!5 = !{i32 2, !"Dwarf Version", i32 4}
!6 = !{i32 2, !"Debug Info Version", i32 3}
!7 = !{i32 1, !"wchar_size", i32 4}
!8 = !{!"clang version 9.0.0"}
!9 = distinct !DISubprogram(name: "baa", scope: !1, file: !1, line: 10, type: !10, scopeLine: 10, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !13)
!10 = !DISubroutineType(types: !11)
!11 = !{!12, !12, !12, !12, !12}
!12 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
!13 = !{!14, !15, !16, !17, !18, !19, !20}
!14 = !DILocalVariable(name: "arg1", arg: 1, scope: !9, file: !1, line: 10, type: !12)
!15 = !DILocalVariable(name: "arg2", arg: 2, scope: !9, file: !1, line: 10, type: !12, flags: DIFlagArgumentNotModified)
!16 = !DILocalVariable(name: "arg3", arg: 3, scope: !9, file: !1, line: 10, type: !12)
!17 = !DILocalVariable(name: "arg4", arg: 4, scope: !9, file: !1, line: 10, type: !12, flags: DIFlagArgumentNotModified)
!18 = !DILocalVariable(name: "local1", scope: !9, file: !1, line: 11, type: !12)
!19 = !DILocalVariable(name: "local2", scope: !9, file: !1, line: 11, type: !12)
!20 = !DILocalVariable(name: "local3", scope: !9, file: !1, line: 11, type: !12)
!21 = !DILocation(line: 10, column: 13, scope: !9)
...
---
name: baa
liveins:
- { reg: '$edi', virtual-reg: '' }
- { reg: '$esi', virtual-reg: '' }
- { reg: '$edx', virtual-reg: '' }
- { reg: '$ecx', virtual-reg: '' }
callSites:
- { bb: 0, offset: 23, fwdArgRegs:
- { arg: 0, reg: '$edi' }
- { arg: 1, reg: '$esi' }
- { arg: 2, reg: '$rdx' }
- { arg: 3, reg: '$ecx' } }
- { bb: 0, offset: 49, fwdArgRegs:
- { arg: 0, reg: '$edi' }
- { arg: 1, reg: '$esi' }
- { arg: 2, reg: '$rdx' }
- { arg: 3, reg: '$ecx' } }
body: |
bb.0.entry:
liveins: $ecx, $edi, $edx, $esi, $r15, $r14, $rbx
DBG_VALUE $edi, $noreg, !14, !DIExpression(), debug-location !21
DBG_VALUE $esi, $noreg, !15, !DIExpression(), debug-location !21
DBG_VALUE $edx, $noreg, !16, !DIExpression(), debug-location !21
DBG_VALUE $ecx, $noreg, !17, !DIExpression(), debug-location !21
frame-setup PUSH64r killed $r15, implicit-def $rsp, implicit $rsp
CFI_INSTRUCTION def_cfa_offset 16
frame-setup PUSH64r killed $r14, implicit-def $rsp, implicit $rsp
CFI_INSTRUCTION def_cfa_offset 24
frame-setup PUSH64r killed $rbx, implicit-def $rsp, implicit $rsp
CFI_INSTRUCTION def_cfa_offset 32
$rsp = frame-setup SUB64ri8 $rsp, 16, implicit-def dead $eflags
CFI_INSTRUCTION def_cfa_offset 48
CFI_INSTRUCTION offset $rbx, -32
CFI_INSTRUCTION offset $r14, -24
CFI_INSTRUCTION offset $r15, -16
$r14d = MOV32rr $ecx, implicit-def $r14
DBG_VALUE $edx, $noreg, !16, !DIExpression(), debug-location !21
$r15d = MOV32rr $esi, implicit-def $r15
$ebx = MOV32rr $edi, implicit-def $rbx
$edi = MOV32rr $esi
MOV32mr $rsp, 1, $noreg, 8, $noreg, killed renamable $edx :: (store 4 into %ir.arg3.addr)
renamable $rdx = LEA64r $rsp, 1, $noreg, 8, $noreg
renamable $ecx = MOV32rr $r14d,
CALL64pcrel32 @foo, csr_64, implicit $rsp, implicit $ssp, implicit $edi, implicit $esi, implicit $rdx, implicit $ecx, implicit-def $rsp, implicit-def $ssp, implicit-def $eax, implicit-def $rax, debug-location !21
DBG_VALUE $noreg, $noreg, !18, !DIExpression(), debug-location !21
$rdx = MOV64rr renamable $rax
$ecx = KILL renamable $ecx, implicit-def $rcx
renamable $eax = LEA64_32r renamable $rcx, 1, renamable $rbx, 0, $noreg, debug-location !21
renamable $edi = LEA64_32r renamable $r14, 1, renamable $r15, 0, $noreg, debug-location !21
CMP32rr renamable $ebx, renamable $r15d, implicit-def $eflags, implicit killed $r15, debug-location !21
renamable $edi = CMOV32rr killed renamable $edi, killed renamable $eax, 15, implicit killed $eflags, debug-location !21
DBG_VALUE $edi, $noreg, !18, !DIExpression(), debug-location !21
$eax = MOV32rr $ecx, debug-location !21
CDQ implicit-def $eax, implicit-def $edx, implicit $eax, debug-location !21
IDIV32r renamable $ebx, implicit-def dead $eax, implicit-def $edx, implicit-def dead $eflags, implicit $eax, implicit $edx, debug-location !21
$eax = MOV32rr $ecx, debug-location !21
renamable $eax = nsw IMUL32rr killed renamable $eax, renamable $ebx, implicit-def dead $eflags, debug-location !21
renamable $ecx = nsw ADD32rr renamable $ecx, renamable $r14d, implicit-def dead $eflags, implicit killed $rcx, implicit-def $rcx, debug-location !21
TEST32rr killed renamable $edx, renamable $edx, implicit-def $eflags, debug-location !21
renamable $ecx = CMOV32rr renamable $ecx, killed renamable $eax, 4, implicit killed $eflags, implicit killed $rcx, implicit-def $rcx, debug-location !21
DBG_VALUE $ecx, $noreg, !19, !DIExpression(), debug-location !21
MOV32mr $rsp, 1, $noreg, 12, $noreg, renamable $ecx, debug-location !21 :: (store 4 into %ir.local2)
CMP32rr renamable $ecx, renamable $r14d, implicit-def $eflags, implicit killed $r14, debug-location !21
renamable $ebx = CMOV32rr renamable $ebx, renamable $edi, 12, implicit killed $eflags, implicit killed $rbx, implicit-def $rbx, debug-location !21
renamable $ecx = nsw IMUL32rr renamable $ecx, renamable $ebx, implicit-def dead $eflags, implicit killed $rbx, implicit killed $rcx, implicit-def $rcx, debug-location !21
DBG_VALUE $rsp, $noreg, !19, !DIExpression(DW_OP_plus_uconst, 12, DW_OP_deref), debug-location !21
DBG_VALUE $ecx, $noreg, !20, !DIExpression(), debug-location !21
$esi = MOV32ri 4, debug-location !21
renamable $ecx = MOV32rm $rsp, 1, $noreg, 8, $noreg, implicit-def $rcx, debug-location !21 :: (dereferenceable load 4 from %ir.arg3.addr)
CALL64pcrel32 @foo, csr_64, implicit $rsp, implicit $ssp, implicit $edi, implicit $esi, implicit $rdx, implicit $ecx, implicit-def $rsp, implicit-def $ssp, implicit-def $eax, implicit-def $rax, debug-location !21
DBG_VALUE $eax, $noreg, !14, !DIExpression(), debug-location !21
renamable $eax = nsw ADD32ri8 killed renamable $eax, 4, implicit-def dead $eflags, debug-location !21
$rsp = frame-destroy ADD64ri8 $rsp, 16, implicit-def dead $eflags, debug-location !21
CFI_INSTRUCTION def_cfa_offset 32, debug-location !21
$rbx = frame-destroy POP64r implicit-def $rsp, implicit $rsp, debug-location !21
CFI_INSTRUCTION def_cfa_offset 24, debug-location !21
$r14 = frame-destroy POP64r implicit-def $rsp, implicit $rsp, debug-location !21
DBG_VALUE $ecx, $noreg, !17, !DIExpression(DW_OP_entry_value, 1), debug-location !21
CFI_INSTRUCTION def_cfa_offset 16, debug-location !21
$r15 = frame-destroy POP64r implicit-def $rsp, implicit $rsp, debug-location !21
DBG_VALUE $esi, $noreg, !15, !DIExpression(DW_OP_entry_value, 1), debug-location !21
CFI_INSTRUCTION def_cfa_offset 8, debug-location !21
RETQ $eax, debug-location !21
...

View File

@ -0,0 +1,140 @@
# RUN: llc -debug-entry-values -start-after=machineverifier -filetype=obj %s -o -| llvm-dwarfdump -| FileCheck %s
# CHECK: DW_TAG_GNU_call_site
# CHECK-NEXT: DW_AT_abstract_origin {{.*}} "foo")
# CHECK-NEXT: DW_AT_low_pc {{.*}}
# CHECK-EMPTY:
# CHECK-NEXT: DW_TAG_GNU_call_site_parameter
# CHECK-NEXT: DW_AT_location (DW_OP_reg9 R9)
# CHECK-NEXT: DW_AT_GNU_call_site_value (DW_OP_breg15 R15+10)
# CHECK-EMPTY:
# CHECK-NEXT: DW_TAG_GNU_call_site_parameter
# CHECK-NEXT: DW_AT_location (DW_OP_reg8 R8)
# CHECK-NEXT: DW_AT_GNU_call_site_value (DW_OP_breg15 R15+0, DW_OP_lit2, DW_OP_mul, DW_OP_plus_uconst 0x8)
# CHECK-EMPTY:
# CHECK-NEXT: DW_TAG_GNU_call_site_parameter
# CHECK-NEXT: DW_AT_location (DW_OP_reg1 RDX)
# CHECK-NEXT: DW_AT_GNU_call_site_value (DW_OP_breg14 R14+0, DW_OP_lit5, DW_OP_mul, DW_OP_plus_uconst 0x8)
# CHECK-EMPTY:
# CHECK-NEXT: DW_TAG_GNU_call_site_parameter
# CHECK-NEXT: DW_AT_location (DW_OP_reg4 RSI)
# CHECK-NEXT: DW_AT_GNU_call_site_value (DW_OP_breg14 R14+0)
# CHECK-EMPTY:
# CHECK-NEXT: DW_TAG_GNU_call_site_parameter
# CHECK-NEXT: DW_AT_location (DW_OP_reg5 RDI)
# CHECK-NEXT: DW_AT_GNU_call_site_value (DW_OP_breg14 R14+0, DW_OP_breg15 R15+0, DW_OP_lit2, DW_OP_mul, DW_OP_plus, DW_OP_plus_uconst 0x4)
# CHECK-EMPTY:
# CHECK-NEXT: DW_TAG_GNU_call_site_parameter
# CHECK-NEXT: DW_AT_location (DW_OP_reg2 RCX)
# CHECK-NEXT: DW_AT_GNU_call_site_value (DW_OP_breg14 R14+0, DW_OP_breg15 R15+0, DW_OP_plus)
# CHECK: DW_TAG_GNU_call_site
# CHECK-NEXT: DW_AT_abstract_origin {{.*}} "foo2")
# CHECK-NEXT: DW_AT_low_pc {{.*}}
# CHECK-EMPTY:
# CHECK-NEXT: DW_TAG_GNU_call_site_parameter
# CHECK-NEXT: DW_AT_location (DW_OP_reg5 RDI)
# CHECK-NEXT: DW_AT_GNU_call_site_value (DW_OP_breg14 R14+0, DW_OP_lit2, DW_OP_mul)
--- |
; ModuleID = 'dbgcall-site-lea-interpretation.ll'
source_filename = "dbgcall-site-lea-interpretation.c"
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"
define dso_local i32 @baa(i32 %arg1, i32 %arg2, i32 %arg3) local_unnamed_addr !dbg !10 {
entry:
%arg1.addr = alloca i32, align 4
%arg3.addr = alloca i32, align 4
%local1 = alloca i32, align 4
store i32 %arg1, i32* %arg1.addr, align 4
store i32 %arg3, i32* %arg3.addr, align 4
%0 = bitcast i32* %local1 to i8*, !dbg !14
%mul = mul nsw i32 %arg3, %arg1, !dbg !14
store i32 %mul, i32* %local1, align 4, !dbg !14
%add = add nsw i32 %arg2, %arg1, !dbg !14
%sub = sub nsw i32 %add, %arg3, !dbg !14
%call = call i32 @foo(i32 %mul, i32 %sub, i32* nonnull %local1, i32* nonnull %arg1.addr, i32* nonnull %arg3.addr, i32 %add), !dbg !14
%1 = load i32, i32* %local1, align 4, !dbg !14
%add2 = add nsw i32 %1, %call, !dbg !14
store i32 %add2, i32* %local1, align 4, !dbg !14
%call3 = call i32 @foo2(i32* nonnull %local1), !dbg !14
%2 = load i32, i32* %local1, align 4, !dbg !14
ret i32 %2, !dbg !14
}
declare !dbg !4 dso_local i32 @foo(i32, i32, i32*, i32*, i32*, i32) local_unnamed_addr
declare !dbg !5 dso_local i32 @foo2(i32*) local_unnamed_addr
!llvm.dbg.cu = !{!0}
!llvm.module.flags = !{!6, !7, !8}
!llvm.ident = !{!9}
!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 9.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, retainedTypes: !3, nameTableKind: None)
!1 = !DIFile(filename: "dbgcall-site-lea-interpretation.c", directory: "/dir")
!2 = !{}
!3 = !{!4, !5}
!4 = !DISubprogram(name: "foo", scope: !1, file: !1, line: 8, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !2)
!5 = !DISubprogram(name: "foo2", scope: !1, file: !1, line: 9, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !2)
!6 = !{i32 2, !"Dwarf Version", i32 4}
!7 = !{i32 2, !"Debug Info Version", i32 3}
!8 = !{i32 1, !"wchar_size", i32 4}
!9 = !{!"clang version 9.0.0"}
!10 = distinct !DISubprogram(name: "baa", scope: !1, file: !1, line: 11, type: !11, scopeLine: 11, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !2)
!11 = !DISubroutineType(types: !12)
!12 = !{!13, !13, !13, !13}
!13 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
!14 = !DILocation(line: 11, column: 13, scope: !10)
...
---
name: baa
liveins:
- { reg: '$edi', virtual-reg: '' }
- { reg: '$esi', virtual-reg: '' }
- { reg: '$edx', virtual-reg: '' }
callSites:
- { bb: 0, offset: 21, fwdArgRegs:
- { arg: 0, reg: '$edi' }
- { arg: 1, reg: '$esi' }
- { arg: 2, reg: '$rdx' }
- { arg: 3, reg: '$rcx' }
- { arg: 4, reg: '$r8' }
- { arg: 5, reg: '$r9d' } }
- { bb: 0, offset: 24, fwdArgRegs:
- { arg: 0, reg: '$rdi' } }
body: |
bb.0.entry:
liveins: $edi, $edx, $esi, $rbx
frame-setup PUSH64r killed $rbx, implicit-def $rsp, implicit $rsp
CFI_INSTRUCTION def_cfa_offset 16
$rsp = frame-setup SUB64ri8 $rsp, 16, implicit-def dead $eflags
CFI_INSTRUCTION def_cfa_offset 32
CFI_INSTRUCTION offset $rbx, -16
$r9d = MOV32rr $esi
$r14 = MOV64rr $rsi
$r15 = MOV64rr $rdi
MOV32mr $rsp, 1, $noreg, 12, $noreg, renamable $edi :: (store 4 into %ir.arg1.addr)
MOV32mr $rsp, 1, $noreg, 8, $noreg, renamable $edx :: (store 4 into %ir.arg3.addr)
renamable $r9d = nsw ADD32rr killed renamable $r9d, renamable $edi, implicit-def dead $eflags, debug-location !14
$esi = MOV32rr $r9d, debug-location !14
renamable $esi = nsw SUB32rr killed renamable $esi, renamable $edx, implicit-def dead $eflags, debug-location !14
renamable $edx = nsw IMUL32rr killed renamable $edx, killed renamable $edi, implicit-def dead $eflags, debug-location !14
MOV32mr $rsp, 1, $noreg, 4, $noreg, renamable $edx, debug-location !14 :: (store 4 into %ir.local1)
renamable $rcx = LEA64r $r14, 1, $r15, 0, $noreg
renamable $rdi = LEA64r $r14, 2, $r15, 4, $noreg
renamable $rsi = LEA64r $r14, 1, $noreg, 0, $noreg
renamable $rdx = LEA64r $r14, 4, $r14, 8, $noreg
renamable $r8 = LEA64r $noreg, 2, $r15, 8, $noreg
renamable $r9 = LEA64r $noreg, 1, $r15, 10, $noreg, implicit-def $r9d
CALL64pcrel32 @foo, csr_64, implicit $rsp, implicit $ssp, implicit $edi, implicit $esi, implicit $rdx, implicit $rcx, implicit $r8, implicit $r9d, implicit-def $rsp, implicit-def $ssp, implicit-def $eax, debug-location !14
ADD32mr $rsp, 1, $noreg, 4, $noreg, killed renamable $eax, implicit-def dead $eflags, debug-location !14 :: (store 4 into %ir.local1), (dereferenceable load 4 from %ir.local1)
$rdi = LEA64r $r14, 1, killed $r14, 0, $noreg
CALL64pcrel32 @foo2, csr_64, implicit $rsp, implicit $ssp, implicit $rdi, implicit-def $rsp, implicit-def $ssp, implicit-def dead $eax, debug-location !14
renamable $eax = MOV32rm $rsp, 1, $noreg, 4, $noreg, debug-location !14 :: (dereferenceable load 4 from %ir.local1)
$rsp = frame-destroy ADD64ri8 $rsp, 16, implicit-def dead $eflags, debug-location !14
CFI_INSTRUCTION def_cfa_offset 16, debug-location !14
$rbx = frame-destroy POP64r implicit-def $rsp, implicit $rsp, debug-location !14
CFI_INSTRUCTION def_cfa_offset 8, debug-location !14
RETQ $eax, debug-location !14
...

View File

@ -0,0 +1,159 @@
# RUN: llc -debug-entry-values -filetype=obj -mtriple=x86_64-unknown-unknown -start-after=machineverifier -o - %s| llvm-dwarfdump - | FileCheck %s
#
# extern void foo(int *a, int b, int c, int d, int e, int f);
# extern int getVal();
#
# void baa(int arg1, int arg2, int arg3) {
# int local1 = getVal();
# foo(&local1, arg2, 10, 15, arg3 + 3, arg1 + arg2);
# }
#
# CHECK: DW_TAG_GNU_call_site
# CHECK: DW_AT_abstract_origin {{.*}} "getVal"
#
# CHECK: DW_TAG_GNU_call_site
# CHECK: DW_AT_abstract_origin {{.*}} "foo"
# CHECK: DW_AT_low_pc {{.*}}
# CHECK-EMPTY:
# CHECK: DW_TAG_GNU_call_site_parameter
# CHECK-NEXT: DW_AT_location (DW_OP_reg2 RCX)
# CHECK-NEXT: DW_AT_GNU_call_site_value (DW_OP_lit15)
# CHECK: DW_TAG_GNU_call_site_parameter
# CHECK-NEXT: DW_AT_location (DW_OP_reg1 RDX)
# CHECK-NEXT: DW_AT_GNU_call_site_value (DW_OP_lit10)
# CHECK: DW_TAG_GNU_call_site_parameter
# CHECK-NEXT: DW_AT_location (DW_OP_reg4 RSI)
# CHECK-NEXT: DW_AT_GNU_call_site_value (DW_OP_breg3 RBX+0)
# CHECK: DW_TAG_GNU_call_site_parameter
# CHECK-NEXT: DW_AT_location (DW_OP_reg5 RDI)
# CHECK-NEXT: DW_AT_GNU_call_site_value (DW_OP_fbreg +12)
# CHECK: DW_TAG_GNU_call_site_parameter
# CHECK-NEXT: DW_AT_location (DW_OP_reg9 R9)
# CHECK-NEXT: DW_AT_GNU_call_site_value (DW_OP_breg15 R15+0, DW_OP_breg3 RBX+0, DW_OP_plus)
# CHECK: DW_TAG_GNU_call_site_parameter
# CHECK-NEXT: DW_AT_location (DW_OP_reg8 R8)
# CHECK-NEXT: DW_AT_GNU_call_site_value (DW_OP_breg14 R14+3)
--- |
; ModuleID = 'test.c'
source_filename = "test.c"
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"
; Function Attrs: nounwind uwtable
define dso_local void @baa(i32 %arg1, i32 %arg2, i32 %arg3) local_unnamed_addr !dbg !10 {
entry:
%local1 = alloca i32, align 4
call void @llvm.dbg.value(metadata i32 %arg1, metadata !15, metadata !DIExpression()), !dbg !19
call void @llvm.dbg.value(metadata i32 %arg2, metadata !16, metadata !DIExpression()), !dbg !20
call void @llvm.dbg.value(metadata i32 %arg3, metadata !17, metadata !DIExpression()), !dbg !21
%0 = bitcast i32* %local1 to i8*, !dbg !22
%call = tail call i32 (...) @getVal(), !dbg !23
call void @llvm.dbg.value(metadata i32 %call, metadata !18, metadata !DIExpression()), !dbg !24
store i32 %call, i32* %local1, align 4, !dbg !24
%add = add nsw i32 %arg3, 3, !dbg !24
%add1 = add nsw i32 %arg2, %arg1, !dbg !24
call void @llvm.dbg.value(metadata i32* %local1, metadata !18, metadata !DIExpression(DW_OP_deref)), !dbg !24
call void @foo(i32* nonnull %local1, i32 %arg2, i32 10, i32 15, i32 %add, i32 %add1), !dbg !24
ret void, !dbg !24
}
declare !dbg !4 dso_local i32 @getVal(...) local_unnamed_addr
declare !dbg !5 dso_local void @foo(i32*, i32, i32, i32, i32, i32) local_unnamed_addr
; Function Attrs: nounwind readnone speculatable
declare void @llvm.dbg.value(metadata, metadata, metadata)
!llvm.dbg.cu = !{!0}
!llvm.module.flags = !{!6, !7, !8}
!llvm.ident = !{!9}
!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 9.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, retainedTypes: !3, nameTableKind: None)
!1 = !DIFile(filename: "test.c", directory: "/dir")
!2 = !{}
!3 = !{!4, !5}
!4 = !DISubprogram(name: "getVal", scope: !1, file: !1, line: 2, spFlags: DISPFlagOptimized, retainedNodes: !2)
!5 = !DISubprogram(name: "foo", scope: !1, file: !1, line: 1, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !2)
!6 = !{i32 2, !"Dwarf Version", i32 4}
!7 = !{i32 2, !"Debug Info Version", i32 3}
!8 = !{i32 1, !"wchar_size", i32 4}
!9 = !{!"clang version 9.0.0"}
!10 = distinct !DISubprogram(name: "baa", scope: !1, file: !1, line: 4, type: !11, scopeLine: 4, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !14)
!11 = !DISubroutineType(types: !12)
!12 = !{null, !13, !13, !13}
!13 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
!14 = !{!15, !16, !17, !18}
!15 = !DILocalVariable(name: "arg1", arg: 1, scope: !10, file: !1, line: 4, type: !13, flags: DIFlagArgumentNotModified)
!16 = !DILocalVariable(name: "arg2", arg: 2, scope: !10, file: !1, line: 4, type: !13, flags: DIFlagArgumentNotModified)
!17 = !DILocalVariable(name: "arg3", arg: 3, scope: !10, file: !1, line: 4, type: !13, flags: DIFlagArgumentNotModified)
!18 = !DILocalVariable(name: "local1", scope: !10, file: !1, line: 5, type: !13)
!19 = !DILocation(line: 4, column: 14, scope: !10)
!20 = !DILocation(line: 4, column: 24, scope: !10)
!21 = !DILocation(line: 4, column: 34, scope: !10)
!22 = !DILocation(line: 5, column: 3, scope: !10)
!23 = !DILocation(line: 5, column: 16, scope: !10)
!24 = !DILocation(line: 5, column: 7, scope: !10)
...
---
name: baa
liveins:
- { reg: '$edi', virtual-reg: '' }
- { reg: '$esi', virtual-reg: '' }
- { reg: '$edx', virtual-reg: '' }
callSites:
- { bb: 0, offset: 21, fwdArgRegs: [] }
- { bb: 0, offset: 31, fwdArgRegs:
- { arg: 0, reg: '$rdi' }
- { arg: 1, reg: '$esi' }
- { arg: 2, reg: '$edx' }
- { arg: 3, reg: '$ecx' }
- { arg: 4, reg: '$r8d' }
- { arg: 5, reg: '$r9d' } }
body: |
bb.0.entry:
liveins: $edi, $edx, $esi, $r15, $r14, $rbx
DBG_VALUE $edi, $noreg, !15, !DIExpression(), debug-location !19
DBG_VALUE $esi, $noreg, !16, !DIExpression(), debug-location !20
DBG_VALUE $edx, $noreg, !17, !DIExpression(), debug-location !21
frame-setup PUSH64r killed $r15, implicit-def $rsp, implicit $rsp
CFI_INSTRUCTION def_cfa_offset 16
frame-setup PUSH64r killed $r14, implicit-def $rsp, implicit $rsp
CFI_INSTRUCTION def_cfa_offset 24
frame-setup PUSH64r killed $rbx, implicit-def $rsp, implicit $rsp
CFI_INSTRUCTION def_cfa_offset 32
$rsp = frame-setup SUB64ri8 $rsp, 16, implicit-def dead $eflags
CFI_INSTRUCTION def_cfa_offset 48
CFI_INSTRUCTION offset $rbx, -32
CFI_INSTRUCTION offset $r14, -24
CFI_INSTRUCTION offset $r15, -16
$r14d = MOV32rr $edx, implicit-def $r14
$ebx = MOV32rr $esi, implicit-def $rbx
$r15d = MOV32rr $edi, implicit-def $r15
DBG_VALUE $r14d, $noreg, !17, !DIExpression(), debug-location !21
DBG_VALUE $ebx, $noreg, !16, !DIExpression(), debug-location !20
DBG_VALUE $r15d, $noreg, !15, !DIExpression(), debug-location !19
dead $eax = XOR32rr undef $eax, undef $eax, implicit-def dead $eflags, implicit-def $al, debug-location !23
CALL64pcrel32 @getVal, csr_64, implicit $rsp, implicit $ssp, implicit $al, implicit-def $rsp, implicit-def $ssp, implicit-def $eax, debug-location !23
DBG_VALUE $eax, $noreg, !18, !DIExpression(), debug-location !24
MOV32mr $rsp, 1, $noreg, 12, $noreg, killed renamable $eax, debug-location !24 :: (store 4 into %ir.local1)
renamable $r8d = LEA64_32r killed renamable $r14, 1, $noreg, 3, $noreg, debug-location !24
renamable $r9d = LEA64_32r killed renamable $r15, 1, renamable $rbx, 0, $noreg, debug-location !24
DBG_VALUE $rsp, $noreg, !18, !DIExpression(DW_OP_plus_uconst, 12, DW_OP_deref), debug-location !24
renamable $rdi = LEA64r $rsp, 1, $noreg, 12, $noreg
$esi = MOV32rr $ebx, implicit killed $rbx, debug-location !24
$edx = MOV32ri 10, debug-location !24
$ecx = MOV32ri 15, debug-location !24
CALL64pcrel32 @foo, csr_64, implicit $rsp, implicit $ssp, implicit $rdi, implicit $esi, implicit $edx, implicit killed $ecx, implicit $r8d, implicit $r9d, implicit-def $rsp, implicit-def $ssp, debug-location !24
$rsp = frame-destroy ADD64ri8 $rsp, 16, implicit-def dead $eflags, debug-location !24
CFI_INSTRUCTION def_cfa_offset 32, debug-location !24
$rbx = frame-destroy POP64r implicit-def $rsp, implicit $rsp, debug-location !24
CFI_INSTRUCTION def_cfa_offset 24, debug-location !24
$r14 = frame-destroy POP64r implicit-def $rsp, implicit $rsp, debug-location !24
CFI_INSTRUCTION def_cfa_offset 16, debug-location !24
$r15 = frame-destroy POP64r implicit-def $rsp, implicit $rsp, debug-location !24
CFI_INSTRUCTION def_cfa_offset 8, debug-location !24
RETQ debug-location !24
...

View File

@ -14,14 +14,14 @@
; REQUIRES: object-emission ; REQUIRES: object-emission
; RUN: %llc_dwarf -mtriple=x86_64-- < %s -o - | FileCheck %s -check-prefix=ASM ; RUN: %llc_dwarf -mtriple=x86_64-- < %s -o - | FileCheck %s -check-prefix=ASM
; RUN: %llc_dwarf -mtriple=x86_64-- < %s -filetype=obj -o %t.o ; 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_site
; RUN: llvm-dwarfdump -verify %t.o 2>&1 | FileCheck %s -check-prefix=VERIFY ; 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-dwarfdump -statistics %t.o | FileCheck %s -check-prefix=STATS
; RUN: llvm-as < %s | llvm-dis | llvm-as | llvm-dis -o /dev/null ; RUN: llvm-as < %s | llvm-dis | llvm-as | llvm-dis -o /dev/null
; VERIFY: No errors. ; VERIFY: No errors.
; STATS: "call site entries":5 ; STATS: "call site DIEs":6
@sink = global i32 0, align 4, !dbg !0 @sink = global i32 0, align 4, !dbg !0
@ -85,6 +85,9 @@ entry:
; OBJ: DW_TAG_call_site ; OBJ: DW_TAG_call_site
; OBJ: DW_AT_call_origin ([[foo_sp]]) ; OBJ: DW_AT_call_origin ([[foo_sp]])
; OBJ: DW_AT_call_return_pc ; OBJ: DW_AT_call_return_pc
; OBJ: DW_TAG_call_site
; OBJ: DW_AT_call_target
; OBJ: DW_AT_call_return_pc
define i32 @main() !dbg !29 { define i32 @main() !dbg !29 {
entry: entry:
call void @_Z3foov(), !dbg !32 call void @_Z3foov(), !dbg !32

View File

@ -0,0 +1,76 @@
; RUN: llc -debug-entry-values %s -o - -filetype=obj \
; RUN: | llvm-dwarfdump -statistics - | FileCheck %s
;
; The LLVM IR file was generated on this source code by using
; option '-femit-param-entry-values'.
;
; extern void foo(int *a, int b, int c, int d, int e, int f);
; extern int getVal();
;
; void baa(int arg1, int arg2, int arg3) {
; int local1 = getVal();
; foo(&local1, arg2, 10, 15, arg3 + 3, arg1 + arg2);
; }
;
; CHECK: "call site DIEs":2
; CHECK-SAME: "call site parameter DIEs":6
;
; ModuleID = 'test.c'
source_filename = "test.c"
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"
; Function Attrs: nounwind uwtable
define dso_local void @baa(i32 %arg1, i32 %arg2, i32 %arg3) local_unnamed_addr #0 !dbg !10 {
entry:
%local1 = alloca i32, align 4
call void @llvm.dbg.value(metadata i32 %arg1, metadata !15, metadata !DIExpression()), !dbg !19
call void @llvm.dbg.value(metadata i32 %arg2, metadata !16, metadata !DIExpression()), !dbg !20
call void @llvm.dbg.value(metadata i32 %arg3, metadata !17, metadata !DIExpression()), !dbg !21
%0 = bitcast i32* %local1 to i8*, !dbg !22
%call = tail call i32 (...) @getVal(), !dbg !23
call void @llvm.dbg.value(metadata i32 %call, metadata !18, metadata !DIExpression()), !dbg !24
store i32 %call, i32* %local1, align 4, !dbg !24
%add = add nsw i32 %arg3, 3, !dbg !24
%add1 = add nsw i32 %arg2, %arg1, !dbg !24
call void @llvm.dbg.value(metadata i32* %local1, metadata !18, metadata !DIExpression(DW_OP_deref)), !dbg !24
call void @foo(i32* nonnull %local1, i32 %arg2, i32 10, i32 15, i32 %add, i32 %add1), !dbg !24
ret void, !dbg !24
}
declare !dbg !4 dso_local i32 @getVal(...) local_unnamed_addr
declare !dbg !5 dso_local void @foo(i32*, i32, i32, i32, i32, i32) local_unnamed_addr
; Function Attrs: nounwind readnone speculatable
declare void @llvm.dbg.value(metadata, metadata, metadata)
!llvm.dbg.cu = !{!0}
!llvm.module.flags = !{!6, !7, !8}
!llvm.ident = !{!9}
!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 9.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, retainedTypes: !3, nameTableKind: None)
!1 = !DIFile(filename: "test.c", directory: "/dir")
!2 = !{}
!3 = !{!4, !5}
!4 = !DISubprogram(name: "getVal", scope: !1, file: !1, line: 2, spFlags: DISPFlagOptimized, retainedNodes: !2)
!5 = !DISubprogram(name: "foo", scope: !1, file: !1, line: 1, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !2)
!6 = !{i32 2, !"Dwarf Version", i32 4}
!7 = !{i32 2, !"Debug Info Version", i32 3}
!8 = !{i32 1, !"wchar_size", i32 4}
!9 = !{!"clang version 9.0.0"}
!10 = distinct !DISubprogram(name: "baa", scope: !1, file: !1, line: 4, type: !11, scopeLine: 4, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !14)
!11 = !DISubroutineType(types: !12)
!12 = !{null, !13, !13, !13}
!13 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
!14 = !{!15, !16, !17, !18}
!15 = !DILocalVariable(name: "arg1", arg: 1, scope: !10, file: !1, line: 4, type: !13, flags: DIFlagArgumentNotModified)
!16 = !DILocalVariable(name: "arg2", arg: 2, scope: !10, file: !1, line: 4, type: !13, flags: DIFlagArgumentNotModified)
!17 = !DILocalVariable(name: "arg3", arg: 3, scope: !10, file: !1, line: 4, type: !13, flags: DIFlagArgumentNotModified)
!18 = !DILocalVariable(name: "local1", scope: !10, file: !1, line: 5, type: !13)
!19 = !DILocation(line: 4, column: 14, scope: !10)
!20 = !DILocation(line: 4, column: 24, scope: !10)
!21 = !DILocation(line: 4, column: 34, scope: !10)
!22 = !DILocation(line: 5, column: 3, scope: !10)
!23 = !DILocation(line: 5, column: 16, scope: !10)
!24 = !DILocation(line: 5, column: 7, scope: !10)

View File

@ -56,9 +56,12 @@ struct GlobalStats {
/// Total number of PC range bytes in each variable's enclosing scope, /// Total number of PC range bytes in each variable's enclosing scope,
/// starting from the first definition of the variable. /// starting from the first definition of the variable.
unsigned ScopeBytesFromFirstDefinition = 0; unsigned ScopeBytesFromFirstDefinition = 0;
/// Total number of call site entries (DW_TAG_call_site) or /// Total number of call site entries (DW_AT_call_file & DW_AT_call_line).
/// (DW_AT_call_file & DW_AT_call_line).
unsigned CallSiteEntries = 0; unsigned CallSiteEntries = 0;
/// Total number of call site DIEs (DW_TAG_call_site).
unsigned CallSiteDIEs = 0;
/// Total number of call site parameter DIEs (DW_TAG_call_site_parameter).
unsigned CallSiteParamDIEs = 0;
/// Total byte size of concrete functions. This byte size includes /// Total byte size of concrete functions. This byte size includes
/// inline functions contained in the concrete functions. /// inline functions contained in the concrete functions.
uint64_t FunctionSize = 0; uint64_t FunctionSize = 0;
@ -94,8 +97,15 @@ static void collectStatsForDie(DWARFDie Die, std::string FnPrefix,
uint64_t BytesCovered = 0; uint64_t BytesCovered = 0;
uint64_t OffsetToFirstDefinition = 0; uint64_t OffsetToFirstDefinition = 0;
if (Die.getTag() == dwarf::DW_TAG_call_site) { if (Die.getTag() == dwarf::DW_TAG_call_site ||
GlobalStats.CallSiteEntries++; Die.getTag() == dwarf::DW_TAG_GNU_call_site) {
GlobalStats.CallSiteDIEs++;
return;
}
if (Die.getTag() == dwarf::DW_TAG_call_site_parameter ||
Die.getTag() == dwarf::DW_TAG_GNU_call_site_parameter) {
GlobalStats.CallSiteParamDIEs++;
return; return;
} }
@ -387,6 +397,8 @@ bool collectStatsForObjectFile(ObjectFile &Obj, DWARFContext &DICtx,
printDatum(OS, "source variables", VarParamTotal); printDatum(OS, "source variables", VarParamTotal);
printDatum(OS, "variables with location", VarParamWithLoc); printDatum(OS, "variables with location", VarParamWithLoc);
printDatum(OS, "call site entries", GlobalStats.CallSiteEntries); printDatum(OS, "call site entries", GlobalStats.CallSiteEntries);
printDatum(OS, "call site DIEs", GlobalStats.CallSiteDIEs);
printDatum(OS, "call site parameter DIEs", GlobalStats.CallSiteParamDIEs);
printDatum(OS, "scope bytes total", printDatum(OS, "scope bytes total",
GlobalStats.ScopeBytesFromFirstDefinition); GlobalStats.ScopeBytesFromFirstDefinition);
printDatum(OS, "scope bytes covered", GlobalStats.ScopeBytesCovered); printDatum(OS, "scope bytes covered", GlobalStats.ScopeBytesCovered);