[DwarfDebug] Dump call site debug info

Dump the DWARF information about call sites and call site parameters into
debug info sections.

The patch also provides an interface for the interpretation of instructions
that could load values of a call site parameters in order to generate DWARF
about the call site parameters.

([13/13] Introduce the debug entry values.)

Co-authored-by: Ananth Sowda <asowda@cisco.com>
Co-authored-by: Nikola Prica <nikola.prica@rt-rk.com>
Co-authored-by: Ivan Baev <ibaev@cisco.com>

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

llvm-svn: 365467
This commit is contained in:
Djordje Todorovic 2019-07-09 11:33:56 +00:00
parent 9b3f38f990
commit 01eaae6dd1
23 changed files with 1101 additions and 50 deletions

View File

@ -4755,6 +4755,9 @@ The current supported opcode vocabulary is limited:
``DW_OP_entry_value`` may also appear after the ``AsmPrinter`` pass when
a call site parameter value (``DW_AT_call_site_parameter_value``)
is represented as entry value of the parameter.
- ``DW_OP_reg`` (or ``DW_OP_regx``) represents a physical register location.
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,
and implicit location descriptions. Note that a location description is

View File

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

View File

@ -531,9 +531,7 @@ public:
/// prior to a call and is guaranteed to be restored (also by the caller)
/// after the call.
virtual bool isCallerPreservedPhysReg(unsigned PhysReg,
const MachineFunction &MF) const {
return false;
}
const MachineFunction &MF) const;
/// Prior to adding the live-out mask to a stackmap or patchpoint
/// instruction, provide the target the opportunity to adjust it (mainly to

View File

@ -890,32 +890,106 @@ void DwarfCompileUnit::constructAbstractSubprogramScopeDIE(
ContextCU->addDIEEntry(*AbsDef, dwarf::DW_AT_object_pointer, *ObjectPointer);
}
dwarf::Tag DwarfCompileUnit::getDwarf5OrGNUCallSiteTag(dwarf::Tag Tag) {
bool ApplyGNUExtensions = DD->getDwarfVersion() == 4 && DD->tuneForGDB();
if (!ApplyGNUExtensions)
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) {
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,
const DISubprogram *CalleeSP,
bool IsTail,
const MCExpr *PCOffset) {
const MCSymbol *PCAddr,
const MCExpr *PCOffset,
unsigned CallReg) {
// Insert a call site entry DIE within ScopeDIE.
DIE &CallSiteDIE =
createAndAddDIE(dwarf::DW_TAG_call_site, ScopeDIE, nullptr);
createAndAddDIE(getDwarf5OrGNUCallSiteTag(dwarf::DW_TAG_call_site),
ScopeDIE, nullptr);
// For the purposes of showing tail call frames in backtraces, a key piece of
// information is DW_AT_call_origin, a pointer to the callee DIE.
DIE *CalleeDIE = getOrCreateSubprogramDIE(&CalleeSP);
assert(CalleeDIE && "Could not create DIE for call site entry origin");
addDIEEntry(CallSiteDIE, dwarf::DW_AT_call_origin, *CalleeDIE);
if (IsTail) {
// Attach DW_AT_call_tail_call to tail calls for standards compliance.
addFlag(CallSiteDIE, dwarf::DW_AT_call_tail_call);
if (CallReg) {
// Indirect call.
addAddress(CallSiteDIE, getDwarf5OrGNUCallSiteAttr(dwarf::DW_AT_call_target),
MachineLocation(CallReg));
} else {
// Attach the return PC to allow the debugger to disambiguate call paths
// from one function to another.
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
// 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");
addAddressExpr(CallSiteDIE, dwarf::DW_AT_call_return_pc, PCOffset);
}
return CallSiteDIE;
}
void DwarfCompileUnit::constructCallSiteParmEntryDIEs(
DIE &CallSiteDIE, SmallVector<DbgCallSiteParam, 4> &Params) {
for (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(
const DIImportedEntity *Module) {
DIE *IMDie = DIE::get(DIEValueAllocator, (dwarf::Tag)Module->getTag());

View File

@ -227,12 +227,22 @@ public:
void constructAbstractSubprogramScopeDIE(LexicalScope *Scope);
dwarf::Tag getDwarf5OrGNUCallSiteTag(dwarf::Tag Tag);
dwarf::Attribute getDwarf5OrGNUCallSiteAttr(dwarf::Attribute Attr);
/// Construct a call site entry DIE describing a call within \p Scope to a
/// callee described by \p CalleeSP. \p IsTail specifies whether the call is
/// a tail call. \p PCOffset must be non-zero for non-tail calls or be the
/// function-local offset to PC value after the call instruction.
DIE &constructCallSiteEntryDIE(DIE &ScopeDIE, const DISubprogram &CalleeSP,
bool IsTail, const MCExpr *PCOffset);
/// 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) or be the function-local offset to PC value
/// after the call instruction. \p CallReg is a register location
/// for an indirect call.
DIE &constructCallSiteEntryDIE(DIE &ScopeDIE, const DISubprogram *CalleeSP,
bool IsTail, const MCSymbol *PCAddr,
const MCExpr *PCOffset, unsigned CallReg);
void constructCallSiteParmEntryDIEs(DIE &CallSiteDIE,
SmallVector<DbgCallSiteParam, 4> &Params);
/// Construct import_module DIE.
DIE *constructImportedEntityDIE(const DIImportedEntity *Module);

View File

@ -26,6 +26,7 @@
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/ADT/Triple.h"
#include "llvm/ADT/Twine.h"
#include "llvm/BinaryFormat/Dwarf.h"
@ -39,6 +40,7 @@
#include "llvm/CodeGen/MachineModuleInfo.h"
#include "llvm/CodeGen/MachineOperand.h"
#include "llvm/CodeGen/TargetInstrInfo.h"
#include "llvm/CodeGen/TargetLowering.h"
#include "llvm/CodeGen/TargetRegisterInfo.h"
#include "llvm/CodeGen/TargetSubtargetInfo.h"
#include "llvm/DebugInfo/DWARF/DWARFExpression.h"
@ -83,6 +85,8 @@ using namespace llvm;
#define DEBUG_TYPE "dwarfdebug"
STATISTIC(NumCSParams, "Number of dbg call site params created");
static cl::opt<bool>
DisableDebugInfoPrinting("disable-debug-info-print", cl::Hidden,
cl::desc("Disable debug info printing"));
@ -551,6 +555,123 @@ 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> ArgsRegsForProcess;
for (auto ArgReg : CallFwdRegsInfo->second)
ArgsRegsForProcess.insert(ArgReg.Reg);
// If we did not find loading a value into forwarding registers
// that means we can try generating 'DW_OP_entry_value' for the argument
// if a call is within entry MBB.
DenseMap<unsigned, unsigned> RegsForEntryValues;
bool ShouldTryEmitEntryVals = MBB->getIterator() == MF->begin();
// Return true if it is an instruction over a parameter's forwarding
// register that clobbers it.
auto shouldInterpret = [&](const MachineInstr &MI) -> unsigned {
if (MI.isDebugInstr())
return 0;
// If a MI clobbers a forwarding reg try to interpret
// a value loaded into the reg.
for (const MachineOperand &MO : MI.operands()) {
if (MO.isReg() && MO.isDef() && MO.getReg() &&
TRI->isPhysicalRegister(MO.getReg())) {
for (auto FwdReg : ArgsRegsForProcess)
if (TRI->regsOverlap(FwdReg, MO.getReg()))
return FwdReg;
}
}
return 0;
};
auto finishCallSiteParam = [&](DbgValueLoc &DbgLocVal,
unsigned &Reg) {
unsigned FwdReg = Reg;
if (ShouldTryEmitEntryVals && RegsForEntryValues.count(Reg))
FwdReg = RegsForEntryValues[Reg];
DbgCallSiteParam CSParm(FwdReg, DbgLocVal);
Params.push_back(CSParm);
NumCSParams++;
};
// Search for a loading value in forwaring registers.
while (I != MBB->rend()) {
// If the next instruction is a call we can not
// interpret parameter's forwarding registers or
// we finished interpretation of all parameters.
if (I->isCall())
return;
if (ArgsRegsForProcess.empty())
return;
if (unsigned Reg = shouldInterpret(*I)) {
ArgsRegsForProcess.erase(Reg);
const MachineOperand *Op;
DIExpression *Expr;
if (auto ParamValue = TII->describeLoadedValue(*I)) {
Op = ParamValue->first;
Expr = ParamValue->second;
if (Op->isImm()) {
unsigned Val = Op->getImm();
DbgValueLoc DbgLocVal(Expr, Val);
finishCallSiteParam(DbgLocVal, Reg);
} else if (Op->isReg()) {
unsigned RegLoc = Op->getReg();
unsigned SP = TLI->getStackPointerRegisterToSaveRestore();
unsigned FP = TRI->getFrameRegister(*MF);
bool IsSPorFP = (RegLoc == SP) || (RegLoc == FP);
if (TRI->isCallerPreservedPhysReg(RegLoc, *MF) || IsSPorFP) {
DbgValueLoc DbgLocVal(
Expr, MachineLocation(RegLoc, IsSPorFP));
finishCallSiteParam(DbgLocVal, Reg);
} else if (ShouldTryEmitEntryVals) {
ArgsRegsForProcess.insert(RegLoc);
RegsForEntryValues[RegLoc] = Reg;
}
}
}
}
++I;
}
// Emit call site parameter's value as entry value.
if (ShouldTryEmitEntryVals) {
DIExpression *EntryExpr = DIExpression::get(MF->getFunction().getContext(),
{dwarf::DW_OP_entry_value, 1});
for (auto RegEntry : ArgsRegsForProcess) {
unsigned FwdReg = RegsForEntryValues.count(RegEntry)
? RegsForEntryValues[RegEntry]
: RegEntry;
DbgValueLoc DbgLocVal(EntryExpr, MachineLocation(RegEntry));
DbgCallSiteParam CSParm(FwdReg, DbgLocVal);
Params.push_back(CSParm);
NumCSParams++;
}
}
}
void DwarfDebug::constructCallSiteEntryDIEs(const DISubprogram &SP,
DwarfCompileUnit &CU, DIE &ScopeDIE,
const MachineFunction &MF) {
@ -563,10 +684,13 @@ void DwarfDebug::constructCallSiteEntryDIEs(const DISubprogram &SP,
// for both tail and non-tail calls. Don't use DW_AT_call_all_source_calls
// because one of its requirements is not met: call site entries for
// optimized-out calls are elided.
CU.addFlag(ScopeDIE, dwarf::DW_AT_call_all_calls);
CU.addFlag(ScopeDIE,
CU.getDwarf5OrGNUCallSiteAttr(dwarf::DW_AT_call_all_calls));
const TargetInstrInfo *TII = MF.getSubtarget().getInstrInfo();
assert(TII && "TargetInstrInfo not found: cannot label tail calls");
const TargetRegisterInfo *TRI = MF.getSubtarget().getRegisterInfo();
bool ApplyGNUExtensions = getDwarfVersion() == 4 && tuneForGDB();
// Emit call site entries for each call or tail call in the function.
for (const MachineBasicBlock &MBB : MF) {
@ -581,30 +705,65 @@ void DwarfDebug::constructCallSiteEntryDIEs(const DISubprogram &SP,
return;
// 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);
if (!CalleeOp.isGlobal())
continue;
const Function *CalleeDecl = dyn_cast<Function>(CalleeOp.getGlobal());
if (!CalleeDecl || !CalleeDecl->getSubprogram())
if (!CalleeOp.isGlobal() && !CalleeOp.isReg())
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: Add support for indirect calls.
bool IsTail = TII->isTailCall(MI);
// For tail calls, no return PC information is needed. For regular calls,
// the return PC is needed to disambiguate paths in the call graph which
// could lead to some target function.
// For tail calls, for non-gdb tuning, 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.
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() << " -> "
<< CalleeDecl->getName() << (IsTail ? " [tail]" : "")
<< (CalleeDecl ? CalleeDecl->getName()
: StringRef(TRI->getName(CallReg)))
<< (IsTail ? " [IsTail]" : "")
<< "\n");
CU.constructCallSiteEntryDIE(ScopeDIE, *CalleeDecl->getSubprogram(),
IsTail, PCOffset);
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,22 @@ public:
}
};
/// Used for tracking debug info about call site parameters.
class DbgCallSiteParam {
private:
unsigned Register;
DbgValueLoc Value;
public:
DbgCallSiteParam(unsigned Reg, DbgValueLoc Val)
: Register(Reg), Value(Val) {}
unsigned getRegister() { return Register; }
DbgValueLoc getValue() { 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.
struct SymbolCU {
SymbolCU(DwarfCompileUnit *CU, const MCSymbol *Sym) : Sym(Sym), CU(CU) {}

View File

@ -241,15 +241,20 @@ bool DwarfExpression::addMachineRegExpression(const TargetRegisterInfo &TRI,
return false;
}
// Handle simple register locations.
if (!isMemoryLocation() && !HasComplexExpression) {
// Handle simple register locations. If we are supposed to
// emit 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) {
if (Reg.DwarfRegNo >= 0)
addReg(Reg.DwarfRegNo, Reg.Comment);
addOpPiece(Reg.Size);
}
if (isEntryValue() && DwarfVersion >= 4)
if (isEntryValue() && !isParameterValue() && DwarfVersion >= 4)
emitOp(dwarf::DW_OP_stack_value);
DwarfRegs.clear();
@ -340,7 +345,16 @@ void DwarfExpression::addExpression(DIExpressionCursor &&ExprCursor,
while (ExprCursor) {
auto Op = ExprCursor.take();
switch (Op->getOp()) {
uint64_t OpNum = Op->getOp();
if (OpNum >= dwarf::DW_OP_reg0 && OpNum <= dwarf::DW_OP_reg31) {
if (isParameterValue())
addBReg(OpNum - dwarf::DW_OP_reg0, 0);
else
emitOp(OpNum);
continue;
}
switch (OpNum) {
case dwarf::DW_OP_LLVM_fragment: {
unsigned SizeInBits = Op->getArg(1);
unsigned FragmentOffset = Op->getArg(0);
@ -389,7 +403,7 @@ void DwarfExpression::addExpression(DIExpressionCursor &&ExprCursor,
case dwarf::DW_OP_lit0:
case dwarf::DW_OP_not:
case dwarf::DW_OP_dup:
emitOp(Op->getOp());
emitOp(OpNum);
break;
case dwarf::DW_OP_deref:
assert(!isRegisterLocation());
@ -458,12 +472,22 @@ void DwarfExpression::addExpression(DIExpressionCursor &&ExprCursor,
case dwarf::DW_OP_LLVM_tag_offset:
TagOffset = Op->getArg(0);
break;
case dwarf::DW_OP_regx:
if (isParameterValue()) {
emitOp(dwarf::DW_OP_bregx);
emitUnsigned(Op->getArg(0));
emitSigned(Op->getArg(1));
} else {
emitOp(dwarf::DW_OP_regx);
emitUnsigned(Op->getArg(0));
}
break;
default:
llvm_unreachable("unhandled opcode found in expression");
}
}
if (isImplicitLocation())
if (isImplicitLocation() && !isParameterValue())
// Turn this into an implicit location description.
addStackValue();
}

View File

@ -120,7 +120,7 @@ protected:
enum { Unknown = 0, Register, Memory, Implicit };
/// The flags of location description being produced.
enum { EntryValue = 1 };
enum { EntryValue = 1, CallSiteParamValue };
unsigned LocationKind : 3;
unsigned LocationFlags : 2;
@ -147,6 +147,10 @@ public:
return LocationFlags & EntryValue;
}
bool isParameterValue() {
return LocationFlags & CallSiteParamValue;
}
Optional<uint8_t> TagOffset;
protected:
@ -264,6 +268,11 @@ public:
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
/// the prefix of a DwarfExpression if a more efficient representation for
/// 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));
}
void DwarfUnit::insertDIE(DIE *D) {
MDNodeToDieMap.insert(std::make_pair(nullptr, D));
}
void DwarfUnit::addFlag(DIE &Die, dwarf::Attribute Attribute) {
if (DD->getDwarfVersion() >= 4)
Die.addValue(DIEValueAllocator, Attribute, dwarf::DW_FORM_flag_present,

View File

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

View File

@ -23,6 +23,7 @@
#include "llvm/CodeGen/TargetRegisterInfo.h"
#include "llvm/CodeGen/TargetSchedule.h"
#include "llvm/IR/DataLayout.h"
#include "llvm/IR/DebugInfoMetadata.h"
#include "llvm/MC/MCAsmInfo.h"
#include "llvm/MC/MCInstrItineraries.h"
#include "llvm/Support/CommandLine.h"
@ -1120,6 +1121,45 @@ bool TargetInstrInfo::hasLowDefLatency(const TargetSchedModel &SchedModel,
return (DefCycle != -1 && DefCycle <= 1);
}
Optional<ParamLoadedValue>
TargetInstrInfo::describeLoadedValue(const MachineInstr &MI) const {
const MachineOperand *Op = nullptr;
DIExpression *Expr = nullptr;
const MachineOperand *SrcRegOp, *DestRegOp;
const MachineFunction *MF = MI.getMF();
if (isCopyInstr(MI, SrcRegOp, DestRegOp)) {
Expr = DIExpression::get(MF->getFunction().getContext(), {});
Op = SrcRegOp;
return ParamLoadedValue(Op, Expr);
} else if (MI.isMoveImmediate()) {
Expr = DIExpression::get(MF->getFunction().getContext(), {});
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;
unsigned CastedOffset = static_cast<unsigned>(Offset);
if (Offset > 0)
Expr = DIExpression::get(
MF->getFunction().getContext(),
{dwarf::DW_OP_plus_uconst, CastedOffset, dwarf::DW_OP_deref});
else
Expr = DIExpression::get(MF->getFunction().getContext(),
{dwarf::DW_OP_constu, -CastedOffset,
dwarf::DW_OP_minus, dwarf::DW_OP_deref});
Op = BaseOp;
return ParamLoadedValue(Op, Expr);
}
return None;
}
/// Both DefMI and UseMI must be valid. By default, call directly to the
/// itinerary. This may be overriden by the target.
int TargetInstrInfo::getOperandLatency(const InstrItineraryData *ItinData,

View File

@ -433,6 +433,20 @@ TargetRegisterInfo::getRegAllocationHints(unsigned VirtReg,
return false;
}
bool
TargetRegisterInfo::isCallerPreservedPhysReg(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 {
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:
// Extensions.
case DW_AT_GNU_call_site_value:
case DW_AT_GNU_call_site_target:
return true;
default:
return false;

View File

@ -833,10 +833,12 @@ unsigned DIExpression::ExprOperand::getSize() const {
case dwarf::DW_OP_LLVM_fragment:
return 3;
case dwarf::DW_OP_constu:
case dwarf::DW_OP_consts:
case dwarf::DW_OP_deref_size:
case dwarf::DW_OP_plus_uconst:
case dwarf::DW_OP_LLVM_tag_offset:
case dwarf::DW_OP_entry_value:
case dwarf::DW_OP_regx:
return 2;
default:
return 1;
@ -849,8 +851,12 @@ bool DIExpression::isValid() const {
if (I->get() + I->getSize() > E->get())
return false;
uint64_t Op = I->getOp();
if (Op >= dwarf::DW_OP_reg0 && Op <= dwarf::DW_OP_reg31)
continue;
// Check that the operand is valid.
switch (I->getOp()) {
switch (Op) {
default:
return false;
case dwarf::DW_OP_LLVM_fragment:
@ -905,6 +911,7 @@ bool DIExpression::isValid() const {
case dwarf::DW_OP_lit0:
case dwarf::DW_OP_not:
case dwarf::DW_OP_dup:
case dwarf::DW_OP_regx:
break;
}
}

View File

@ -30,7 +30,7 @@
#include "llvm/CodeGen/StackMaps.h"
#include "llvm/IR/DerivedTypes.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/DebugInfoMetadata.h"
#include "llvm/MC/MCAsmInfo.h"
#include "llvm/MC/MCExpr.h"
#include "llvm/MC/MCInst.h"
@ -7318,6 +7318,98 @@ 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> Elements;
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) {
Elements.push_back(dwarf::DW_OP_constu);
Elements.push_back(Coef + 1);
Elements.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)
Elements.push_back(dwarf::DW_OP_reg0 + dwarfReg);
else {
Elements.push_back(dwarf::DW_OP_regx);
Elements.push_back(dwarfReg);
}
} else if (!Op) {
assert(Op2.getReg() != X86::NoRegister);
Op = &Op2;
}
if (Coef > 1) {
assert(Op2.getReg() != X86::NoRegister);
Elements.push_back(dwarf::DW_OP_constu);
Elements.push_back(Coef);
Elements.push_back(dwarf::DW_OP_mul);
}
if (((Op1.isReg() && Op1.getReg() != X86::NoRegister) || Op1.isFI()) &&
Op2.getReg() != X86::NoRegister) {
Elements.push_back(dwarf::DW_OP_plus);
}
}
if (Offset < 0) {
Elements.push_back(dwarf::DW_OP_constu);
Elements.push_back(-Offset);
Elements.push_back(dwarf::DW_OP_minus);
} else if (Offset > 0) {
Elements.push_back(dwarf::DW_OP_constu);
Elements.push_back(Offset);
Elements.push_back(dwarf::DW_OP_plus);
}
Expr = DIExpression::get(MI.getMF()->getFunction().getContext(), Elements);
return ParamLoadedValue(Op, Expr);;
}
default:
return TargetInstrInfo::describeLoadedValue(MI);
}
}
/// This is an architecture-specific helper function of reassociateOps.
/// Set special operand attributes for new instructions after reassociation.
void X86InstrInfo::setSpecialOperandAttr(MachineInstr &OldMI1,

View File

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

View File

@ -0,0 +1,200 @@
# 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: 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-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_lit8, DW_OP_plus)
# 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_lit8, DW_OP_plus)
# 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_lit4, DW_OP_plus)
# 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,158 @@
# 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_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
; 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 -verify %t.o 2>&1 | FileCheck %s -check-prefix=VERIFY
; RUN: llvm-dwarfdump -statistics %t.o | FileCheck %s -check-prefix=STATS
; RUN: llvm-as < %s | llvm-dis | llvm-as | llvm-dis -o /dev/null
; VERIFY: No errors.
; STATS: "call site entries":5
; STATS: "call site DIEs":6
@sink = global i32 0, align 4, !dbg !0
@ -85,6 +85,9 @@ entry:
; OBJ: DW_TAG_call_site
; OBJ: DW_AT_call_origin ([[foo_sp]])
; 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 {
entry:
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,
/// starting from the first definition of the variable.
unsigned ScopeBytesFromFirstDefinition = 0;
/// Total number of call site entries (DW_TAG_call_site) or
/// (DW_AT_call_file & DW_AT_call_line).
/// Total number of call site entries (DW_AT_call_file & DW_AT_call_line).
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
/// inline functions contained in the concrete functions.
uint64_t FunctionSize = 0;
@ -94,8 +97,15 @@ static void collectStatsForDie(DWARFDie Die, std::string FnPrefix,
uint64_t BytesCovered = 0;
uint64_t OffsetToFirstDefinition = 0;
if (Die.getTag() == dwarf::DW_TAG_call_site) {
GlobalStats.CallSiteEntries++;
if (Die.getTag() == dwarf::DW_TAG_call_site ||
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;
}
@ -387,6 +397,8 @@ bool collectStatsForObjectFile(ObjectFile &Obj, DWARFContext &DICtx,
printDatum(OS, "source variables", VarParamTotal);
printDatum(OS, "variables with location", VarParamWithLoc);
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",
GlobalStats.ScopeBytesFromFirstDefinition);
printDatum(OS, "scope bytes covered", GlobalStats.ScopeBytesCovered);