OSDN Git Service

[DwarfDebug] Dump call site debug info
authorDjordje Todorovic <djordje.todorovic@rt-rk.com>
Tue, 9 Jul 2019 11:33:56 +0000 (11:33 +0000)
committerDjordje Todorovic <djordje.todorovic@rt-rk.com>
Tue, 9 Jul 2019 11:33:56 +0000 (11:33 +0000)
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

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@365467 91177308-0d34-0410-b5e6-96231b3b80d8

23 files changed:
docs/LangRef.rst
include/llvm/CodeGen/TargetInstrInfo.h
include/llvm/CodeGen/TargetRegisterInfo.h
lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp
lib/CodeGen/AsmPrinter/DwarfCompileUnit.h
lib/CodeGen/AsmPrinter/DwarfDebug.cpp
lib/CodeGen/AsmPrinter/DwarfDebug.h
lib/CodeGen/AsmPrinter/DwarfExpression.cpp
lib/CodeGen/AsmPrinter/DwarfExpression.h
lib/CodeGen/AsmPrinter/DwarfUnit.cpp
lib/CodeGen/AsmPrinter/DwarfUnit.h
lib/CodeGen/TargetInstrInfo.cpp
lib/CodeGen/TargetRegisterInfo.cpp
lib/DebugInfo/DWARF/DWARFDie.cpp
lib/IR/DebugInfoMetadata.cpp
lib/Target/X86/X86InstrInfo.cpp
lib/Target/X86/X86InstrInfo.h
test/DebugInfo/MIR/X86/dbgcall-site-interpretation.mir [new file with mode: 0644]
test/DebugInfo/MIR/X86/dbgcall-site-lea-interpretation.mir [new file with mode: 0644]
test/DebugInfo/MIR/X86/debug-call-site-param.mir [new file with mode: 0644]
test/DebugInfo/X86/dwarf-callsite-related-attrs.ll
test/tools/llvm-dwarfdump/X86/stats-dbg-callsite-info.ll [new file with mode: 0644]
tools/llvm-dwarfdump/Statistics.cpp

index e15c0a3..f98aa62 100644 (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
index 25b04f8..314bb72 100644 (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;
index ddbd677..9a3ab47 100644 (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
index 4163fdb..09780cc 100644 (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 (CallReg) {
+    // Indirect call.
+    addAddress(CallSiteDIE, getDwarf5OrGNUCallSiteAttr(dwarf::DW_AT_call_target),
+               MachineLocation(CallReg));
+  } 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) {
+  if (IsTail)
     // Attach DW_AT_call_tail_call to tail calls for standards compliance.
-    addFlag(CallSiteDIE, dwarf::DW_AT_call_tail_call);
-  } else {
-    // Attach the return PC to allow the debugger to disambiguate call paths
-    // from one function to another.
+    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());
index ea980df..0c2e66d 100644 (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);
index 71bb2b0..6c6349d 100644 (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);
+      }
     }
   }
 }
index 3ac474e..1c9de98 100644 (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) {}
index 2858afa..436852b 100644 (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();
 }
index ec2ef6e..9f9baf8 100644 (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.
index 991ab94..4990ea6 100644 (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,
index 56c934a..a376d5e 100644 (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);
 
index 868617f..f7f3e11 100644 (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,
index f1b2ecf..b5a4419 100644 (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");
 }
index 6212842..63afbec 100644 (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;
index 900df27..cdb9576 100644 (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;
     }
   }
index e5da23c..6ecee41 100644 (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,
index 13ca171..b18c1da 100644 (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
diff --git a/test/DebugInfo/MIR/X86/dbgcall-site-interpretation.mir b/test/DebugInfo/MIR/X86/dbgcall-site-interpretation.mir
new file mode 100644 (file)
index 0000000..84a3a00
--- /dev/null
@@ -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
+
+...
diff --git a/test/DebugInfo/MIR/X86/dbgcall-site-lea-interpretation.mir b/test/DebugInfo/MIR/X86/dbgcall-site-lea-interpretation.mir
new file mode 100644 (file)
index 0000000..5cef8fc
--- /dev/null
@@ -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
+
+...
diff --git a/test/DebugInfo/MIR/X86/debug-call-site-param.mir b/test/DebugInfo/MIR/X86/debug-call-site-param.mir
new file mode 100644 (file)
index 0000000..5bb318d
--- /dev/null
@@ -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
+
+...
index f17cd0b..c37ce1e 100644 (file)
 
 ; 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
diff --git a/test/tools/llvm-dwarfdump/X86/stats-dbg-callsite-info.ll b/test/tools/llvm-dwarfdump/X86/stats-dbg-callsite-info.ll
new file mode 100644 (file)
index 0000000..53cad41
--- /dev/null
@@ -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)
index f26369b..c76c77e 100644 (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);