Index: docs/LangRef.rst =================================================================== --- docs/LangRef.rst +++ docs/LangRef.rst @@ -4769,6 +4769,10 @@ ``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_breg`` (or ``DW_OP_bregx``) represents a content on the provided + signed offset of the specified register. The opcode is only generated by the + ``AsmPrinter`` pass to describe call site parameter value which requires an + expression over two registers. DWARF specifies three kinds of simple location descriptions: Register, memory, and implicit location descriptions. Note that a location description is Index: include/llvm/CodeGen/TargetInstrInfo.h =================================================================== --- include/llvm/CodeGen/TargetInstrInfo.h +++ include/llvm/CodeGen/TargetInstrInfo.h @@ -60,6 +60,8 @@ template class SmallVectorImpl; +using ParamLoadedValue = std::pair; + //--------------------------------------------------------------------------- /// /// TargetInstrInfo - Interface to description of machine instruction set @@ -1691,6 +1693,11 @@ return false; } + /// Produce the expression describing the \p MI loading a value into + /// the parameter's forwarding register. + virtual Optional + describeLoadedValue(const MachineInstr &MI) const; + private: unsigned CallFrameSetupOpcode, CallFrameDestroyOpcode; unsigned CatchRetOpcode; Index: include/llvm/CodeGen/TargetRegisterInfo.h =================================================================== --- include/llvm/CodeGen/TargetRegisterInfo.h +++ include/llvm/CodeGen/TargetRegisterInfo.h @@ -535,6 +535,11 @@ return false; } + /// This is a wrapper around getCallPreservedMask(). + /// Return true if the register is preserved after the call. + virtual bool isCalleeSavedPhysReg(unsigned PhysReg, + const MachineFunction &MF) const; + /// Prior to adding the live-out mask to a stackmap or patchpoint /// instruction, provide the target the opportunity to adjust it (mainly to /// remove pseudo-registers that should be ignored). Index: lib/CodeGen/AsmPrinter/DwarfCompileUnit.h =================================================================== --- lib/CodeGen/AsmPrinter/DwarfCompileUnit.h +++ lib/CodeGen/AsmPrinter/DwarfCompileUnit.h @@ -227,12 +227,33 @@ void constructAbstractSubprogramScopeDIE(LexicalScope *Scope); + /// This takes the official DWARF 5 tag and returns the appropriate + /// GNU tag if needed. + dwarf::Tag getDwarf5OrGNUCallSiteTag(dwarf::Tag Tag) const; + /// This takes the official DWARF 5 attribute and returns the appropriate + /// GNU attribute if needed. + dwarf::Attribute getDwarf5OrGNUCallSiteAttr(dwarf::Attribute Attr) const; + /// Construct a call site entry DIE describing a call within \p Scope to a - /// 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 + /// callee described by \p CalleeSP. + /// \p IsTail specifies whether the call is a tail call. + /// \p PCAddr (used for GDB + DWARF 4 tuning) points to the PC value after + /// the call instruction. + /// \p PCOffset (used for cases other than GDB + DWARF 4 tuning) must be + /// non-zero for non-tail calls (in the case of non-gdb tuning, since for + /// GDB + DWARF 5 tuning we still generate PC info for tail calls) or be the /// function-local offset to PC value after the call instruction. - DIE &constructCallSiteEntryDIE(DIE &ScopeDIE, const DISubprogram &CalleeSP, - bool IsTail, const MCExpr *PCOffset); + /// \p CallReg is a register location for an indirect call. For direct calls + /// the \p CallReg is set to 0. + DIE &constructCallSiteEntryDIE(DIE &ScopeDIE, const DISubprogram *CalleeSP, + bool IsTail, const MCSymbol *PCAddr, + const MCExpr *PCOffset, unsigned CallReg); + /// Construct call site parameter DIEs for the \p CallSiteDIE. The \p Params + /// were collected by the \ref collectCallSiteParameters. + /// Note: The order of parameters does not matter, since debuggers recognize + /// call site parameters by the DW_AT_location attribute. + void constructCallSiteParmEntryDIEs(DIE &CallSiteDIE, + SmallVector &Params); /// Construct import_module DIE. DIE *constructImportedEntityDIE(const DIImportedEntity *Module); Index: lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp =================================================================== --- lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp +++ lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp @@ -892,32 +892,106 @@ ContextCU->addDIEEntry(*AbsDef, dwarf::DW_AT_object_pointer, *ObjectPointer); } -DIE &DwarfCompileUnit::constructCallSiteEntryDIE(DIE &ScopeDIE, - const DISubprogram &CalleeSP, - bool IsTail, - const MCExpr *PCOffset) { - // Insert a call site entry DIE within ScopeDIE. - DIE &CallSiteDIE = - createAndAddDIE(dwarf::DW_TAG_call_site, ScopeDIE, nullptr); +dwarf::Tag DwarfCompileUnit::getDwarf5OrGNUCallSiteTag(dwarf::Tag Tag) const { + 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"); + } +} - // 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); +dwarf::Attribute +DwarfCompileUnit::getDwarf5OrGNUCallSiteAttr(dwarf::Attribute Attr) const { + bool ApplyGNUExtensions = DD->getDwarfVersion() == 4 && DD->tuneForGDB(); + if (!ApplyGNUExtensions) + return Attr; + switch (Attr) { + case dwarf::DW_AT_call_all_calls: + return dwarf::DW_AT_GNU_all_call_sites; + case dwarf::DW_AT_call_target: + return dwarf::DW_AT_GNU_call_site_target; + case dwarf::DW_AT_call_origin: + return dwarf::DW_AT_abstract_origin; + case dwarf::DW_AT_call_pc: + return dwarf::DW_AT_low_pc; + case dwarf::DW_AT_call_value: + return dwarf::DW_AT_GNU_call_site_value; + case dwarf::DW_AT_call_tail_call: + return dwarf::DW_AT_GNU_tail_call; + default: + llvm_unreachable("unhandled call site attribute"); + } +} - if (IsTail) { - // Attach DW_AT_call_tail_call to tail calls for standards compliance. - addFlag(CallSiteDIE, dwarf::DW_AT_call_tail_call); +DIE &DwarfCompileUnit::constructCallSiteEntryDIE( + DIE &ScopeDIE, const DISubprogram *CalleeSP, bool IsTail, + const MCSymbol *PCAddr, const MCExpr *PCOffset, unsigned CallReg) { + // Insert a call site entry DIE within ScopeDIE. + DIE &CallSiteDIE = createAndAddDIE( + getDwarf5OrGNUCallSiteTag(dwarf::DW_TAG_call_site), ScopeDIE, nullptr); + + 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 &Params) { + for (const auto &Param : Params) { + unsigned Register = Param.getRegister(); + auto CallSiteDieParam = + DIE::get(DIEValueAllocator, + getDwarf5OrGNUCallSiteTag(dwarf::DW_TAG_call_site_parameter)); + insertDIE(CallSiteDieParam); + addAddress(*CallSiteDieParam, dwarf::DW_AT_location, + MachineLocation(Register)); + + DIELoc *Loc = new (DIEValueAllocator) DIELoc; + DIEDwarfExpression DwarfExpr(*Asm, *this, *Loc); + DwarfExpr.setCallSiteParamValueFlag(); + + DwarfDebug::emitDebugLocValue(*Asm, nullptr, Param.getValue(), DwarfExpr); + + addBlock(*CallSiteDieParam, + getDwarf5OrGNUCallSiteAttr(dwarf::DW_AT_call_value), + DwarfExpr.finalize()); + + CallSiteDIE.addChild(CallSiteDieParam); + } +} + DIE *DwarfCompileUnit::constructImportedEntityDIE( const DIImportedEntity *Module) { DIE *IMDie = DIE::get(DIEValueAllocator, (dwarf::Tag)Module->getTag()); Index: lib/CodeGen/AsmPrinter/DwarfDebug.h =================================================================== --- lib/CodeGen/AsmPrinter/DwarfDebug.h +++ lib/CodeGen/AsmPrinter/DwarfDebug.h @@ -254,6 +254,25 @@ } }; +/// Used for tracking debug info about call site parameters. +class DbgCallSiteParam { +private: + unsigned Register; ///< Parameter register at the callee entry point. + DbgValueLoc Value; ///< Corresponding location for the parameter value at + ///< the call site. +public: + DbgCallSiteParam(unsigned Reg, DbgValueLoc Val) + : Register(Reg), Value(Val) { + assert(Reg && "Parameter register cannot be undef"); + } + + unsigned getRegister() const { return Register; } + DbgValueLoc getValue() const { return Value; } +}; + +/// Collection used for storing debug call site parameters. +using ParamSet = SmallVector; + /// 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: lib/CodeGen/AsmPrinter/DwarfDebug.cpp =================================================================== --- lib/CodeGen/AsmPrinter/DwarfDebug.cpp +++ lib/CodeGen/AsmPrinter/DwarfDebug.cpp @@ -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 @@ #define DEBUG_TYPE "dwarfdebug" +STATISTIC(NumCSParams, "Number of dbg call site params created"); + static cl::opt DisableDebugInfoPrinting("disable-debug-info-print", cl::Hidden, cl::desc("Disable debug info printing")); @@ -551,6 +555,146 @@ } } +/// 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 ForwardedRegWorklist; + // Add all the forwarding registers into the ForwardedRegWorklist. + for (auto ArgReg : CallFwdRegsInfo->second) { + bool InsertedReg = ForwardedRegWorklist.insert(ArgReg.Reg).second; + assert(InsertedReg && "Single register used to forward two arguments?"); + } + + // We erase, from the ForwardedRegWorklist, those forwarding registers for + // which we successfully describe a loaded value (by using + // the describeLoadedValue()). For those remaining arguments in the working + // list, for which we do not describe a loaded value by + // the describeLoadedValue(), we try to generate an entry value expression + // for their call site value desctipion, if the call is within the entry MBB. + // The RegsForEntryValues maps a forwarding register into the register holding + // the entry value. + // TODO: Handle situations when call site parameter value can be described + // as the entry value within basic blocks other then the first one. + bool ShouldTryEmitEntryVals = MBB->getIterator() == MF->begin(); + DenseMap RegsForEntryValues; + + // If the MI is an instruction defining a parameter's forwarding register, + // add it into the Defs. If the MI clobbers more then one register, we use + // the Defs in order to remove all the registers from + // the ForwardedRegWorklist, since we do not support such situations now. + auto getForwardingRegsDefinedByMI = [&](const MachineInstr &MI, + SmallVectorImpl &Defs) { + if (MI.isDebugInstr()) + return; + + for (const MachineOperand &MO : MI.operands()) { + if (MO.isReg() && MO.isDef() && TRI->isPhysicalRegister(MO.getReg())) { + for (auto FwdReg : ForwardedRegWorklist) { + if (TRI->regsOverlap(FwdReg, MO.getReg())) { + Defs.push_back(FwdReg); + break; + } + } + } + } + }; + + auto finishCallSiteParam = [&](DbgValueLoc DbgLocVal, unsigned Reg) { + unsigned FwdReg = Reg; + if (ShouldTryEmitEntryVals) { + auto EntryValReg = RegsForEntryValues.find(Reg); + if (EntryValReg != RegsForEntryValues.end()) + FwdReg = EntryValReg->second; + } + + DbgCallSiteParam CSParm(FwdReg, DbgLocVal); + Params.push_back(CSParm); + ++NumCSParams; + }; + + // Search for a loading value in forwaring registers. + for (; I != MBB->rend(); ++I) { + // If the next instruction is a call we can not interpret parameter's + // forwarding registers or we finished the interpretation of all parameters. + if (I->isCall()) + return; + + if (ForwardedRegWorklist.empty()) + return; + + SmallVector Defs; + getForwardingRegsDefinedByMI(*I, Defs); + if (Defs.empty()) + continue; + + // If the MI clobbers more then one forwarding register we must remove + // all of them from the working list. + for (auto Reg : Defs) + ForwardedRegWorklist.erase(Reg); + if (I->getNumDefs() != 1) + continue; + unsigned Reg = Defs[0]; + + if (auto ParamValue = TII->describeLoadedValue(*I)) { + if (ParamValue->first->isImm()) { + unsigned Val = ParamValue->first->getImm(); + DbgValueLoc DbgLocVal(ParamValue->second, Val); + finishCallSiteParam(DbgLocVal, Reg); + } else if (ParamValue->first->isReg()) { + unsigned RegLoc = ParamValue->first->getReg(); + unsigned SP = TLI->getStackPointerRegisterToSaveRestore(); + unsigned FP = TRI->getFrameRegister(*MF); + bool IsSPorFP = (RegLoc == SP) || (RegLoc == FP); + if (TRI->isCalleeSavedPhysReg(RegLoc, *MF) || IsSPorFP) { + DbgValueLoc DbgLocVal(ParamValue->second, + MachineLocation(RegLoc, + /*IsIndirect=*/IsSPorFP)); + finishCallSiteParam(DbgLocVal, Reg); + } else if (ShouldTryEmitEntryVals) { + ForwardedRegWorklist.insert(RegLoc); + RegsForEntryValues[RegLoc] = Reg; + } + } + } + } + + // Emit the call site parameter's value as an entry value. + if (ShouldTryEmitEntryVals) { + // Create an entry value expression where the expression following + // the 'DW_OP_entry_value' will be the size of 1 (a register operation). + DIExpression *EntryExpr = DIExpression::get(MF->getFunction().getContext(), + {dwarf::DW_OP_entry_value, 1}); + for (auto RegEntry : ForwardedRegWorklist) { + unsigned FwdReg = RegEntry; + auto EntryValReg = RegsForEntryValues.find(RegEntry); + if (EntryValReg != RegsForEntryValues.end()) + FwdReg = EntryValReg->second; + + DbgValueLoc DbgLocVal(EntryExpr, MachineLocation(RegEntry)); + DbgCallSiteParam CSParm(FwdReg, DbgLocVal); + Params.push_back(CSParm); + ++NumCSParams; + } + } +} + void DwarfDebug::constructCallSiteEntryDIEs(const DISubprogram &SP, DwarfCompileUnit &CU, DIE &ScopeDIE, const MachineFunction &MF) { @@ -563,10 +707,12 @@ // 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"); + 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 +727,66 @@ 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(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(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(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]" : "") - << "\n"); - CU.constructCallSiteEntryDIE(ScopeDIE, *CalleeDecl->getSubprogram(), - IsTail, PCOffset); + << (CalleeDecl ? CalleeDecl->getName() + : StringRef(MF.getSubtarget() + .getRegisterInfo() + ->getName(CallReg))) + << (IsTail ? " [IsTail]" : "") << "\n"); + + DIE &CallSiteDIE = + CU.constructCallSiteEntryDIE(ScopeDIE, CalleeSP, IsTail, PCAddr, + PCOffset, CallReg); + + // For now only GDB supports call site parameter debug info. + if (Asm->TM.Options.EnableDebugEntryValues && + tuneForGDB()) { + ParamSet Params; + // Try to interpret values of call site parameters. + collectCallSiteParameters(&MI, Params); + CU.constructCallSiteParmEntryDIEs(CallSiteDIE, Params); + } } } } Index: lib/CodeGen/AsmPrinter/DwarfExpression.h =================================================================== --- lib/CodeGen/AsmPrinter/DwarfExpression.h +++ lib/CodeGen/AsmPrinter/DwarfExpression.h @@ -120,7 +120,7 @@ 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 @@ return LocationFlags & EntryValue; } + bool isParameterValue() { + return LocationFlags & CallSiteParamValue; + } + Optional TagOffset; protected: @@ -264,6 +268,11 @@ 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: lib/CodeGen/AsmPrinter/DwarfExpression.cpp =================================================================== --- lib/CodeGen/AsmPrinter/DwarfExpression.cpp +++ lib/CodeGen/AsmPrinter/DwarfExpression.cpp @@ -241,15 +241,19 @@ 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 +344,17 @@ 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) { + emitOp(OpNum); + continue; + } else if (OpNum >= dwarf::DW_OP_breg0 && OpNum <= dwarf::DW_OP_breg31) { + addBReg(OpNum - dwarf::DW_OP_breg0, Op->getArg(0)); + continue; + } + + switch (OpNum) { case dwarf::DW_OP_LLVM_fragment: { unsigned SizeInBits = Op->getArg(1); unsigned FragmentOffset = Op->getArg(0); @@ -389,7 +403,7 @@ 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,21 @@ case dwarf::DW_OP_LLVM_tag_offset: TagOffset = Op->getArg(0); break; + case dwarf::DW_OP_regx: + emitOp(dwarf::DW_OP_regx); + emitUnsigned(Op->getArg(0)); + break; + case dwarf::DW_OP_bregx: + emitOp(dwarf::DW_OP_bregx); + emitUnsigned(Op->getArg(0)); + emitSigned(Op->getArg(1)); + break; default: llvm_unreachable("unhandled opcode found in expression"); } } - if (isImplicitLocation()) + if (isImplicitLocation() && !isParameterValue()) // Turn this into an implicit location description. addStackValue(); } Index: lib/CodeGen/AsmPrinter/DwarfUnit.h =================================================================== --- lib/CodeGen/AsmPrinter/DwarfUnit.h +++ lib/CodeGen/AsmPrinter/DwarfUnit.h @@ -127,6 +127,8 @@ /// 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: lib/CodeGen/AsmPrinter/DwarfUnit.cpp =================================================================== --- lib/CodeGen/AsmPrinter/DwarfUnit.cpp +++ lib/CodeGen/AsmPrinter/DwarfUnit.cpp @@ -205,6 +205,10 @@ 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: lib/CodeGen/TargetInstrInfo.cpp =================================================================== --- lib/CodeGen/TargetInstrInfo.cpp +++ lib/CodeGen/TargetInstrInfo.cpp @@ -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,36 @@ return (DefCycle != -1 && DefCycle <= 1); } +Optional +TargetInstrInfo::describeLoadedValue(const MachineInstr &MI) const { + const MachineFunction *MF = MI.getMF(); + const MachineOperand *Op = nullptr; + DIExpression *Expr = DIExpression::get(MF->getFunction().getContext(), {});; + const MachineOperand *SrcRegOp, *DestRegOp; + + if (isCopyInstr(MI, SrcRegOp, DestRegOp)) { + Op = SrcRegOp; + return ParamLoadedValue(Op, Expr); + } else if (MI.isMoveImmediate()) { + Op = &MI.getOperand(1); + return ParamLoadedValue(Op, Expr); + } else if (MI.hasOneMemOperand()) { + int64_t Offset; + const auto &TRI = MF->getSubtarget().getRegisterInfo(); + const auto &TII = MF->getSubtarget().getInstrInfo(); + const MachineOperand *BaseOp; + + if (!TII->getMemOperandWithOffset(MI, BaseOp, Offset, TRI)) + return None; + + Expr = DIExpression::prepend(Expr, DIExpression::DerefAfter, Offset); + Op = BaseOp; + return ParamLoadedValue(Op, Expr); + } + + return None; +} + /// Both DefMI and UseMI must be valid. By default, call directly to the /// itinerary. This may be overriden by the target. int TargetInstrInfo::getOperandLatency(const InstrItineraryData *ItinData, Index: lib/CodeGen/TargetRegisterInfo.cpp =================================================================== --- lib/CodeGen/TargetRegisterInfo.cpp +++ lib/CodeGen/TargetRegisterInfo.cpp @@ -433,6 +433,19 @@ return false; } +bool TargetRegisterInfo::isCalleeSavedPhysReg( + unsigned PhysReg, const MachineFunction &MF) const { + if (PhysReg == 0) + return false; + const uint32_t *callerPreservedRegs = + getCallPreservedMask(MF, MF.getFunction().getCallingConv()); + if (callerPreservedRegs) { + assert(isPhysicalRegister(PhysReg) && "Expected physical register"); + return (callerPreservedRegs[PhysReg / 32] >> PhysReg % 32) & 1; + } + return false; +} + bool TargetRegisterInfo::canRealignStack(const MachineFunction &MF) const { return !MF.getFunction().hasFnAttribute("no-realign-stack"); } Index: lib/DebugInfo/DWARF/DWARFDie.cpp =================================================================== --- lib/DebugInfo/DWARF/DWARFDie.cpp +++ lib/DebugInfo/DWARF/DWARFDie.cpp @@ -733,6 +733,7 @@ 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: lib/IR/DebugInfoMetadata.cpp =================================================================== --- lib/IR/DebugInfoMetadata.cpp +++ lib/IR/DebugInfoMetadata.cpp @@ -828,15 +828,23 @@ } unsigned DIExpression::ExprOperand::getSize() const { - switch (getOp()) { + uint64_t Op = getOp(); + + if (Op >= dwarf::DW_OP_breg0 && Op <= dwarf::DW_OP_breg31) + return 2; + + switch (Op) { case dwarf::DW_OP_LLVM_convert: case dwarf::DW_OP_LLVM_fragment: + case dwarf::DW_OP_bregx: 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 +857,13 @@ 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) || + (Op >= dwarf::DW_OP_breg0 && Op <= dwarf::DW_OP_breg31)) + return true; + // Check that the operand is valid. - switch (I->getOp()) { + switch (Op) { default: return false; case dwarf::DW_OP_LLVM_fragment: @@ -905,6 +918,8 @@ case dwarf::DW_OP_lit0: case dwarf::DW_OP_not: case dwarf::DW_OP_dup: + case dwarf::DW_OP_regx: + case dwarf::DW_OP_bregx: break; } } Index: lib/Target/X86/X86InstrInfo.h =================================================================== --- lib/Target/X86/X86InstrInfo.h +++ lib/Target/X86/X86InstrInfo.h @@ -527,6 +527,9 @@ #define GET_INSTRINFO_HELPER_DECLS #include "X86GenInstrInfo.inc" + Optional + 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 Index: lib/Target/X86/X86InstrInfo.cpp =================================================================== --- lib/Target/X86/X86InstrInfo.cpp +++ lib/Target/X86/X86InstrInfo.cpp @@ -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" @@ -7367,6 +7367,92 @@ } } +Optional +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 Ops; + + if ((Op1.isReg() && Op1.getReg() != X86::NoRegister)) { + Op = &Op1; + } else if (Op1.isFI()) + Op = &Op1; + + if (Op && Op->isReg() && Op->getReg() == Op2.getReg() && Coef > 0) { + Ops.push_back(dwarf::DW_OP_constu); + Ops.push_back(Coef + 1); + Ops.push_back(dwarf::DW_OP_mul); + } else { + if (Op && Op2.getReg() != X86::NoRegister) { + int dwarfReg = TRI->getDwarfRegNum(Op2.getReg(), false); + if (dwarfReg < 0) + return None; + else if (dwarfReg < 32) { + Ops.push_back(dwarf::DW_OP_breg0 + dwarfReg); + Ops.push_back(0); + } else { + Ops.push_back(dwarf::DW_OP_bregx); + Ops.push_back(dwarfReg); + Ops.push_back(0); + } + } else if (!Op) { + assert(Op2.getReg() != X86::NoRegister); + Op = &Op2; + } + + if (Coef > 1) { + assert(Op2.getReg() != X86::NoRegister); + Ops.push_back(dwarf::DW_OP_constu); + Ops.push_back(Coef); + Ops.push_back(dwarf::DW_OP_mul); + } + + if (((Op1.isReg() && Op1.getReg() != X86::NoRegister) || Op1.isFI()) && + Op2.getReg() != X86::NoRegister) { + Ops.push_back(dwarf::DW_OP_plus); + } + } + + DIExpression::appendOffset(Ops, Offset); + Expr = DIExpression::get(MI.getMF()->getFunction().getContext(), Ops); + + return ParamLoadedValue(Op, Expr);; + } + default: + return TargetInstrInfo::describeLoadedValue(MI); + } +} + /// This is an architecture-specific helper function of reassociateOps. /// Set special operand attributes for new instructions after reassociation. void X86InstrInfo::setSpecialOperandAttr(MachineInstr &OldMI1, Index: test/DebugInfo/MIR/X86/dbgcall-site-interpretation.mir =================================================================== --- /dev/null +++ test/DebugInfo/MIR/X86/dbgcall-site-interpretation.mir @@ -0,0 +1,202 @@ +# RUN: llc -debug-entry-values -start-after=machineverifier -filetype=obj %s -o -| llvm-dwarfdump -| FileCheck %s +# +# CHECK: DW_TAG_GNU_call_site +# CHECK-NEXT: DW_AT_abstract_origin {{.*}} "foo" +# CHECK-NEXT: DW_AT_low_pc +# CHECK-EMPTY: +# CHECK-NEXT: DW_TAG_GNU_call_site_parameter +# CHECK-NEXT: DW_AT_location (DW_OP_reg2 RCX) +# CHECK-NEXT: DW_AT_GNU_call_site_value (DW_OP_breg14 R14+0) +# CHECK-EMPTY: +# CHECK-NEXT: DW_TAG_GNU_call_site_parameter +# CHECK-NEXT: DW_AT_location (DW_OP_reg1 RDX) +# CHECK-NEXT: DW_AT_GNU_call_site_value (DW_OP_fbreg +8) +# CHECK-EMPTY: +# CHECK-NEXT: DW_TAG_GNU_call_site_parameter +# CHECK-NEXT: DW_AT_location (DW_OP_reg5 RDI) +# CHECK-NEXT: DW_AT_GNU_call_site_value (DW_OP_GNU_entry_value(DW_OP_reg4 RSI)) +# CHECK-EMPTY: +# CHECK: DW_TAG_GNU_call_site +# CHECK-NEXT: DW_AT_abstract_origin {{.*}}"foo" +# CHECK-NEXT: DW_AT_low_pc +# CHECK-EMPTY: +# CHECK-NEXT: DW_TAG_GNU_call_site_parameter +# CHECK-NEXT: DW_AT_location (DW_OP_reg2 RCX) +# CHECK-NEXT: DW_AT_GNU_call_site_value (DW_OP_fbreg +8, DW_OP_deref) +# CHECK-EMPTY: +# CHECK-NEXT: DW_TAG_GNU_call_site_parameter +# CHECK-NEXT: DW_AT_location (DW_OP_reg4 RSI) +# CHECK-NEXT: DW_AT_GNU_call_site_value (DW_OP_lit4) +# CHECK-EMPTY: +# CHECK-NOT: DW_TAG_GNU_call_site_parameter +# +# Check that call site interpretation analysis can interpret instructions such +# as move immediate, register to register moves, stack loading and LEA +# instructions. Last negative check should verify that we are not producing +# interpretation for RDX register since its loaded value is call clobberable. +# Also check that we are generating proper call site debug entities. +--- | + ; ModuleID = 'dbgcall-site-interpretation.c' + source_filename = "dbgcall-site-interpretation.c" + target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" + target triple = "x86_64-unknown-linux-gnu" + + ; Function Attrs: nounwind uwtable + define dso_local i32 @baa(i32 %arg1, i32 %arg2, i32 %arg3, i32 %arg4) local_unnamed_addr !dbg !9 { + entry: + %arg3.addr = alloca i32, align 4 + %local2 = alloca i32, align 4 + call void @llvm.dbg.value(metadata i32 %arg1, metadata !14, metadata !DIExpression()), !dbg !21 + call void @llvm.dbg.value(metadata i32 %arg2, metadata !15, metadata !DIExpression()), !dbg !21 + call void @llvm.dbg.value(metadata i32 %arg3, metadata !16, metadata !DIExpression()), !dbg !21 + store i32 %arg3, i32* %arg3.addr, align 4 + call void @llvm.dbg.value(metadata i32 %arg4, metadata !17, metadata !DIExpression()), !dbg !21 + %0 = bitcast i32* %local2 to i8*, !dbg !21 + call void @llvm.dbg.value(metadata i32* %arg3.addr, metadata !16, metadata !DIExpression(DW_OP_deref)), !dbg !21 + %call = call i32 @foo(i32 %arg1, i32 %arg2, i32* nonnull %arg3.addr, i32 %arg4), !dbg !21 + call void @llvm.dbg.value(metadata i32 %call, metadata !18, metadata !DIExpression()), !dbg !21 + %cmp = icmp sgt i32 %arg1, %arg2, !dbg !21 + %1 = load i32, i32* %arg3.addr, align 4, !dbg !21 + call void @llvm.dbg.value(metadata i32 %1, metadata !16, metadata !DIExpression()), !dbg !21 + %add = add nsw i32 %1, %arg1, !dbg !21 + %add1 = add nsw i32 %arg4, %arg2, !dbg !21 + %local1.0 = select i1 %cmp, i32 %add, i32 %add1, !dbg !21 + call void @llvm.dbg.value(metadata i32 %local1.0, metadata !18, metadata !DIExpression()), !dbg !21 + %rem = srem i32 %1, %arg1, !dbg !21 + %tobool = icmp eq i32 %rem, 0, !dbg !21 + %mul = mul nsw i32 %1, %arg1, !dbg !21 + %add3 = add nsw i32 %1, %arg4, !dbg !21 + %storemerge = select i1 %tobool, i32 %mul, i32 %add3, !dbg !21 + call void @llvm.dbg.value(metadata i32 %storemerge, metadata !19, metadata !DIExpression()), !dbg !21 + store i32 %storemerge, i32* %local2, align 4, !dbg !21 + %cmp6 = icmp slt i32 %storemerge, %arg4, !dbg !21 + %local3.0.v = select i1 %cmp6, i32 %local1.0, i32 %arg1, !dbg !21 + %local3.0 = mul nsw i32 %local3.0.v, %storemerge, !dbg !21 + call void @llvm.dbg.value(metadata i32 %local3.0, metadata !20, metadata !DIExpression()), !dbg !21 + call void @llvm.dbg.value(metadata i32* %local2, metadata !19, metadata !DIExpression(DW_OP_deref)), !dbg !21 + %call12 = call i32 @foo(i32 %local1.0, i32 4, i32* nonnull %local2, i32 %local3.0), !dbg !21 + call void @llvm.dbg.value(metadata i32 %call12, metadata !14, metadata !DIExpression()), !dbg !21 + %add13 = add nsw i32 %call12, 4, !dbg !21 + ret i32 %add13, !dbg !21 + } + + declare !dbg !4 dso_local i32 @foo(i32, i32, i32*, i32) local_unnamed_addr + + ; Function Attrs: nounwind readnone speculatable + declare void @llvm.dbg.value(metadata, metadata, metadata) + + !llvm.dbg.cu = !{!0} + !llvm.module.flags = !{!5, !6, !7} + !llvm.ident = !{!8} + + !0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 9.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, retainedTypes: !3, nameTableKind: None) + !1 = !DIFile(filename: "dbgcall-site-interpretation.c", directory: "/dir") + !2 = !{} + !3 = !{!4} + !4 = !DISubprogram(name: "foo", scope: !1, file: !1, line: 9, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !2) + !5 = !{i32 2, !"Dwarf Version", i32 4} + !6 = !{i32 2, !"Debug Info Version", i32 3} + !7 = !{i32 1, !"wchar_size", i32 4} + !8 = !{!"clang version 9.0.0"} + !9 = distinct !DISubprogram(name: "baa", scope: !1, file: !1, line: 10, type: !10, scopeLine: 10, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !13) + !10 = !DISubroutineType(types: !11) + !11 = !{!12, !12, !12, !12, !12} + !12 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) + !13 = !{!14, !15, !16, !17, !18, !19, !20} + !14 = !DILocalVariable(name: "arg1", arg: 1, scope: !9, file: !1, line: 10, type: !12) + !15 = !DILocalVariable(name: "arg2", arg: 2, scope: !9, file: !1, line: 10, type: !12, flags: DIFlagArgumentNotModified) + !16 = !DILocalVariable(name: "arg3", arg: 3, scope: !9, file: !1, line: 10, type: !12) + !17 = !DILocalVariable(name: "arg4", arg: 4, scope: !9, file: !1, line: 10, type: !12, flags: DIFlagArgumentNotModified) + !18 = !DILocalVariable(name: "local1", scope: !9, file: !1, line: 11, type: !12) + !19 = !DILocalVariable(name: "local2", scope: !9, file: !1, line: 11, type: !12) + !20 = !DILocalVariable(name: "local3", scope: !9, file: !1, line: 11, type: !12) + !21 = !DILocation(line: 10, column: 13, scope: !9) + +... +--- +name: baa +liveins: + - { reg: '$edi', virtual-reg: '' } + - { reg: '$esi', virtual-reg: '' } + - { reg: '$edx', virtual-reg: '' } + - { reg: '$ecx', virtual-reg: '' } +callSites: + - { bb: 0, offset: 23, fwdArgRegs: + - { arg: 0, reg: '$edi' } + - { arg: 1, reg: '$esi' } + - { arg: 2, reg: '$rdx' } + - { arg: 3, reg: '$ecx' } } + - { bb: 0, offset: 49, fwdArgRegs: + - { arg: 0, reg: '$edi' } + - { arg: 1, reg: '$esi' } + - { arg: 2, reg: '$rdx' } + - { arg: 3, reg: '$ecx' } } +body: | + bb.0.entry: + liveins: $ecx, $edi, $edx, $esi, $r15, $r14, $rbx + + DBG_VALUE $edi, $noreg, !14, !DIExpression(), debug-location !21 + DBG_VALUE $esi, $noreg, !15, !DIExpression(), debug-location !21 + DBG_VALUE $edx, $noreg, !16, !DIExpression(), debug-location !21 + DBG_VALUE $ecx, $noreg, !17, !DIExpression(), debug-location !21 + frame-setup PUSH64r killed $r15, implicit-def $rsp, implicit $rsp + CFI_INSTRUCTION def_cfa_offset 16 + frame-setup PUSH64r killed $r14, implicit-def $rsp, implicit $rsp + CFI_INSTRUCTION def_cfa_offset 24 + frame-setup PUSH64r killed $rbx, implicit-def $rsp, implicit $rsp + CFI_INSTRUCTION def_cfa_offset 32 + $rsp = frame-setup SUB64ri8 $rsp, 16, implicit-def dead $eflags + CFI_INSTRUCTION def_cfa_offset 48 + CFI_INSTRUCTION offset $rbx, -32 + CFI_INSTRUCTION offset $r14, -24 + CFI_INSTRUCTION offset $r15, -16 + $r14d = MOV32rr $ecx, implicit-def $r14 + DBG_VALUE $edx, $noreg, !16, !DIExpression(), debug-location !21 + $r15d = MOV32rr $esi, implicit-def $r15 + $ebx = MOV32rr $edi, implicit-def $rbx + $edi = MOV32rr $esi + MOV32mr $rsp, 1, $noreg, 8, $noreg, killed renamable $edx :: (store 4 into %ir.arg3.addr) + renamable $rdx = LEA64r $rsp, 1, $noreg, 8, $noreg + renamable $ecx = MOV32rr $r14d, + CALL64pcrel32 @foo, csr_64, implicit $rsp, implicit $ssp, implicit $edi, implicit $esi, implicit $rdx, implicit $ecx, implicit-def $rsp, implicit-def $ssp, implicit-def $eax, implicit-def $rax, debug-location !21 + DBG_VALUE $noreg, $noreg, !18, !DIExpression(), debug-location !21 + $rdx = MOV64rr renamable $rax + $ecx = KILL renamable $ecx, implicit-def $rcx + renamable $eax = LEA64_32r renamable $rcx, 1, renamable $rbx, 0, $noreg, debug-location !21 + renamable $edi = LEA64_32r renamable $r14, 1, renamable $r15, 0, $noreg, debug-location !21 + CMP32rr renamable $ebx, renamable $r15d, implicit-def $eflags, implicit killed $r15, debug-location !21 + renamable $edi = CMOV32rr killed renamable $edi, killed renamable $eax, 15, implicit killed $eflags, debug-location !21 + DBG_VALUE $edi, $noreg, !18, !DIExpression(), debug-location !21 + $eax = MOV32rr $ecx, debug-location !21 + CDQ implicit-def $eax, implicit-def $edx, implicit $eax, debug-location !21 + IDIV32r renamable $ebx, implicit-def dead $eax, implicit-def $edx, implicit-def dead $eflags, implicit $eax, implicit $edx, debug-location !21 + $eax = MOV32rr $ecx, debug-location !21 + renamable $eax = nsw IMUL32rr killed renamable $eax, renamable $ebx, implicit-def dead $eflags, debug-location !21 + renamable $ecx = nsw ADD32rr renamable $ecx, renamable $r14d, implicit-def dead $eflags, implicit killed $rcx, implicit-def $rcx, debug-location !21 + TEST32rr killed renamable $edx, renamable $edx, implicit-def $eflags, debug-location !21 + renamable $ecx = CMOV32rr renamable $ecx, killed renamable $eax, 4, implicit killed $eflags, implicit killed $rcx, implicit-def $rcx, debug-location !21 + DBG_VALUE $ecx, $noreg, !19, !DIExpression(), debug-location !21 + MOV32mr $rsp, 1, $noreg, 12, $noreg, renamable $ecx, debug-location !21 :: (store 4 into %ir.local2) + CMP32rr renamable $ecx, renamable $r14d, implicit-def $eflags, implicit killed $r14, debug-location !21 + renamable $ebx = CMOV32rr renamable $ebx, renamable $edi, 12, implicit killed $eflags, implicit killed $rbx, implicit-def $rbx, debug-location !21 + renamable $ecx = nsw IMUL32rr renamable $ecx, renamable $ebx, implicit-def dead $eflags, implicit killed $rbx, implicit killed $rcx, implicit-def $rcx, debug-location !21 + DBG_VALUE $rsp, $noreg, !19, !DIExpression(DW_OP_plus_uconst, 12, DW_OP_deref), debug-location !21 + DBG_VALUE $ecx, $noreg, !20, !DIExpression(), debug-location !21 + $esi = MOV32ri 4, debug-location !21 + renamable $ecx = MOV32rm $rsp, 1, $noreg, 8, $noreg, implicit-def $rcx, debug-location !21 :: (dereferenceable load 4 from %ir.arg3.addr) + CALL64pcrel32 @foo, csr_64, implicit $rsp, implicit $ssp, implicit $edi, implicit $esi, implicit $rdx, implicit $ecx, implicit-def $rsp, implicit-def $ssp, implicit-def $eax, implicit-def $rax, debug-location !21 + DBG_VALUE $eax, $noreg, !14, !DIExpression(), debug-location !21 + renamable $eax = nsw ADD32ri8 killed renamable $eax, 4, implicit-def dead $eflags, debug-location !21 + $rsp = frame-destroy ADD64ri8 $rsp, 16, implicit-def dead $eflags, debug-location !21 + CFI_INSTRUCTION def_cfa_offset 32, debug-location !21 + $rbx = frame-destroy POP64r implicit-def $rsp, implicit $rsp, debug-location !21 + CFI_INSTRUCTION def_cfa_offset 24, debug-location !21 + $r14 = frame-destroy POP64r implicit-def $rsp, implicit $rsp, debug-location !21 + DBG_VALUE $ecx, $noreg, !17, !DIExpression(DW_OP_entry_value, 1), debug-location !21 + CFI_INSTRUCTION def_cfa_offset 16, debug-location !21 + $r15 = frame-destroy POP64r implicit-def $rsp, implicit $rsp, debug-location !21 + DBG_VALUE $esi, $noreg, !15, !DIExpression(DW_OP_entry_value, 1), debug-location !21 + CFI_INSTRUCTION def_cfa_offset 8, debug-location !21 + RETQ $eax, debug-location !21 + +... Index: test/DebugInfo/MIR/X86/dbgcall-site-lea-interpretation.mir =================================================================== --- /dev/null +++ test/DebugInfo/MIR/X86/dbgcall-site-lea-interpretation.mir @@ -0,0 +1,140 @@ +# RUN: llc -debug-entry-values -start-after=machineverifier -filetype=obj %s -o -| llvm-dwarfdump -| FileCheck %s +# CHECK: DW_TAG_GNU_call_site +# CHECK-NEXT: DW_AT_abstract_origin {{.*}} "foo") +# CHECK-NEXT: DW_AT_low_pc {{.*}} +# CHECK-EMPTY: +# CHECK-NEXT: DW_TAG_GNU_call_site_parameter +# CHECK-NEXT: DW_AT_location (DW_OP_reg9 R9) +# CHECK-NEXT: DW_AT_GNU_call_site_value (DW_OP_breg15 R15+10) +# CHECK-EMPTY: +# CHECK-NEXT: DW_TAG_GNU_call_site_parameter +# CHECK-NEXT: DW_AT_location (DW_OP_reg8 R8) +# CHECK-NEXT: DW_AT_GNU_call_site_value (DW_OP_breg15 R15+0, DW_OP_lit2, DW_OP_mul, DW_OP_plus_uconst 0x8) +# CHECK-EMPTY: +# CHECK-NEXT: DW_TAG_GNU_call_site_parameter +# CHECK-NEXT: DW_AT_location (DW_OP_reg1 RDX) +# CHECK-NEXT: DW_AT_GNU_call_site_value (DW_OP_breg14 R14+0, DW_OP_lit5, DW_OP_mul, DW_OP_plus_uconst 0x8) +# CHECK-EMPTY: +# CHECK-NEXT: DW_TAG_GNU_call_site_parameter +# CHECK-NEXT: DW_AT_location (DW_OP_reg4 RSI) +# CHECK-NEXT: DW_AT_GNU_call_site_value (DW_OP_breg14 R14+0) +# CHECK-EMPTY: +# CHECK-NEXT: DW_TAG_GNU_call_site_parameter +# CHECK-NEXT: DW_AT_location (DW_OP_reg5 RDI) +# CHECK-NEXT: DW_AT_GNU_call_site_value (DW_OP_breg14 R14+0, DW_OP_breg15 R15+0, DW_OP_lit2, DW_OP_mul, DW_OP_plus, DW_OP_plus_uconst 0x4) +# CHECK-EMPTY: +# CHECK-NEXT: DW_TAG_GNU_call_site_parameter +# CHECK-NEXT: DW_AT_location (DW_OP_reg2 RCX) +# CHECK-NEXT: DW_AT_GNU_call_site_value (DW_OP_breg14 R14+0, DW_OP_breg15 R15+0, DW_OP_plus) +# CHECK: DW_TAG_GNU_call_site +# CHECK-NEXT: DW_AT_abstract_origin {{.*}} "foo2") +# CHECK-NEXT: DW_AT_low_pc {{.*}} +# CHECK-EMPTY: +# CHECK-NEXT: DW_TAG_GNU_call_site_parameter +# CHECK-NEXT: DW_AT_location (DW_OP_reg5 RDI) +# CHECK-NEXT: DW_AT_GNU_call_site_value (DW_OP_breg14 R14+0, DW_OP_lit2, DW_OP_mul) +--- | + ; ModuleID = 'dbgcall-site-lea-interpretation.ll' + source_filename = "dbgcall-site-lea-interpretation.c" + target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" + target triple = "x86_64-unknown-linux-gnu" + + define dso_local i32 @baa(i32 %arg1, i32 %arg2, i32 %arg3) local_unnamed_addr !dbg !10 { + entry: + %arg1.addr = alloca i32, align 4 + %arg3.addr = alloca i32, align 4 + %local1 = alloca i32, align 4 + store i32 %arg1, i32* %arg1.addr, align 4 + store i32 %arg3, i32* %arg3.addr, align 4 + %0 = bitcast i32* %local1 to i8*, !dbg !14 + %mul = mul nsw i32 %arg3, %arg1, !dbg !14 + store i32 %mul, i32* %local1, align 4, !dbg !14 + %add = add nsw i32 %arg2, %arg1, !dbg !14 + %sub = sub nsw i32 %add, %arg3, !dbg !14 + %call = call i32 @foo(i32 %mul, i32 %sub, i32* nonnull %local1, i32* nonnull %arg1.addr, i32* nonnull %arg3.addr, i32 %add), !dbg !14 + %1 = load i32, i32* %local1, align 4, !dbg !14 + %add2 = add nsw i32 %1, %call, !dbg !14 + store i32 %add2, i32* %local1, align 4, !dbg !14 + %call3 = call i32 @foo2(i32* nonnull %local1), !dbg !14 + %2 = load i32, i32* %local1, align 4, !dbg !14 + ret i32 %2, !dbg !14 + } + + declare !dbg !4 dso_local i32 @foo(i32, i32, i32*, i32*, i32*, i32) local_unnamed_addr + + declare !dbg !5 dso_local i32 @foo2(i32*) local_unnamed_addr + + !llvm.dbg.cu = !{!0} + !llvm.module.flags = !{!6, !7, !8} + !llvm.ident = !{!9} + + !0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 9.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, retainedTypes: !3, nameTableKind: None) + !1 = !DIFile(filename: "dbgcall-site-lea-interpretation.c", directory: "/dir") + !2 = !{} + !3 = !{!4, !5} + !4 = !DISubprogram(name: "foo", scope: !1, file: !1, line: 8, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !2) + !5 = !DISubprogram(name: "foo2", scope: !1, file: !1, line: 9, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !2) + !6 = !{i32 2, !"Dwarf Version", i32 4} + !7 = !{i32 2, !"Debug Info Version", i32 3} + !8 = !{i32 1, !"wchar_size", i32 4} + !9 = !{!"clang version 9.0.0"} + !10 = distinct !DISubprogram(name: "baa", scope: !1, file: !1, line: 11, type: !11, scopeLine: 11, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !2) + !11 = !DISubroutineType(types: !12) + !12 = !{!13, !13, !13, !13} + !13 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) + !14 = !DILocation(line: 11, column: 13, scope: !10) + +... +--- +name: baa +liveins: + - { reg: '$edi', virtual-reg: '' } + - { reg: '$esi', virtual-reg: '' } + - { reg: '$edx', virtual-reg: '' } +callSites: + - { bb: 0, offset: 21, fwdArgRegs: + - { arg: 0, reg: '$edi' } + - { arg: 1, reg: '$esi' } + - { arg: 2, reg: '$rdx' } + - { arg: 3, reg: '$rcx' } + - { arg: 4, reg: '$r8' } + - { arg: 5, reg: '$r9d' } } + - { bb: 0, offset: 24, fwdArgRegs: + - { arg: 0, reg: '$rdi' } } +body: | + bb.0.entry: + liveins: $edi, $edx, $esi, $rbx + + frame-setup PUSH64r killed $rbx, implicit-def $rsp, implicit $rsp + CFI_INSTRUCTION def_cfa_offset 16 + $rsp = frame-setup SUB64ri8 $rsp, 16, implicit-def dead $eflags + CFI_INSTRUCTION def_cfa_offset 32 + CFI_INSTRUCTION offset $rbx, -16 + $r9d = MOV32rr $esi + $r14 = MOV64rr $rsi + $r15 = MOV64rr $rdi + MOV32mr $rsp, 1, $noreg, 12, $noreg, renamable $edi :: (store 4 into %ir.arg1.addr) + MOV32mr $rsp, 1, $noreg, 8, $noreg, renamable $edx :: (store 4 into %ir.arg3.addr) + renamable $r9d = nsw ADD32rr killed renamable $r9d, renamable $edi, implicit-def dead $eflags, debug-location !14 + $esi = MOV32rr $r9d, debug-location !14 + renamable $esi = nsw SUB32rr killed renamable $esi, renamable $edx, implicit-def dead $eflags, debug-location !14 + renamable $edx = nsw IMUL32rr killed renamable $edx, killed renamable $edi, implicit-def dead $eflags, debug-location !14 + MOV32mr $rsp, 1, $noreg, 4, $noreg, renamable $edx, debug-location !14 :: (store 4 into %ir.local1) + renamable $rcx = LEA64r $r14, 1, $r15, 0, $noreg + renamable $rdi = LEA64r $r14, 2, $r15, 4, $noreg + renamable $rsi = LEA64r $r14, 1, $noreg, 0, $noreg + renamable $rdx = LEA64r $r14, 4, $r14, 8, $noreg + renamable $r8 = LEA64r $noreg, 2, $r15, 8, $noreg + renamable $r9 = LEA64r $noreg, 1, $r15, 10, $noreg, implicit-def $r9d + CALL64pcrel32 @foo, csr_64, implicit $rsp, implicit $ssp, implicit $edi, implicit $esi, implicit $rdx, implicit $rcx, implicit $r8, implicit $r9d, implicit-def $rsp, implicit-def $ssp, implicit-def $eax, debug-location !14 + ADD32mr $rsp, 1, $noreg, 4, $noreg, killed renamable $eax, implicit-def dead $eflags, debug-location !14 :: (store 4 into %ir.local1), (dereferenceable load 4 from %ir.local1) + $rdi = LEA64r $r14, 1, killed $r14, 0, $noreg + CALL64pcrel32 @foo2, csr_64, implicit $rsp, implicit $ssp, implicit $rdi, implicit-def $rsp, implicit-def $ssp, implicit-def dead $eax, debug-location !14 + renamable $eax = MOV32rm $rsp, 1, $noreg, 4, $noreg, debug-location !14 :: (dereferenceable load 4 from %ir.local1) + $rsp = frame-destroy ADD64ri8 $rsp, 16, implicit-def dead $eflags, debug-location !14 + CFI_INSTRUCTION def_cfa_offset 16, debug-location !14 + $rbx = frame-destroy POP64r implicit-def $rsp, implicit $rsp, debug-location !14 + CFI_INSTRUCTION def_cfa_offset 8, debug-location !14 + RETQ $eax, debug-location !14 + +... Index: test/DebugInfo/MIR/X86/debug-call-site-param.mir =================================================================== --- /dev/null +++ test/DebugInfo/MIR/X86/debug-call-site-param.mir @@ -0,0 +1,159 @@ +# RUN: llc -debug-entry-values -filetype=obj -mtriple=x86_64-unknown-unknown -start-after=machineverifier -o - %s| llvm-dwarfdump - | FileCheck %s +# +# extern void foo(int *a, int b, int c, int d, int e, int f); +# extern int getVal(); +# +# void baa(int arg1, int arg2, int arg3) { +# int local1 = getVal(); +# foo(&local1, arg2, 10, 15, arg3 + 3, arg1 + arg2); +# } +# +# CHECK: DW_TAG_GNU_call_site +# CHECK: DW_AT_abstract_origin {{.*}} "getVal" +# +# CHECK: DW_TAG_GNU_call_site +# CHECK: DW_AT_abstract_origin {{.*}} "foo" +# CHECK: DW_AT_low_pc {{.*}} +# CHECK-EMPTY: +# CHECK: DW_TAG_GNU_call_site_parameter +# CHECK-NEXT: DW_AT_location (DW_OP_reg2 RCX) +# CHECK-NEXT: DW_AT_GNU_call_site_value (DW_OP_lit15) +# CHECK: DW_TAG_GNU_call_site_parameter +# CHECK-NEXT: DW_AT_location (DW_OP_reg1 RDX) +# CHECK-NEXT: DW_AT_GNU_call_site_value (DW_OP_lit10) +# CHECK: DW_TAG_GNU_call_site_parameter +# CHECK-NEXT: DW_AT_location (DW_OP_reg4 RSI) +# CHECK-NEXT: DW_AT_GNU_call_site_value (DW_OP_breg3 RBX+0) +# CHECK: DW_TAG_GNU_call_site_parameter +# CHECK-NEXT: DW_AT_location (DW_OP_reg5 RDI) +# CHECK-NEXT: DW_AT_GNU_call_site_value (DW_OP_fbreg +12) +# CHECK: DW_TAG_GNU_call_site_parameter +# CHECK-NEXT: DW_AT_location (DW_OP_reg9 R9) +# CHECK-NEXT: DW_AT_GNU_call_site_value (DW_OP_breg15 R15+0, DW_OP_breg3 RBX+0, DW_OP_plus) +# CHECK: DW_TAG_GNU_call_site_parameter +# CHECK-NEXT: DW_AT_location (DW_OP_reg8 R8) +# CHECK-NEXT: DW_AT_GNU_call_site_value (DW_OP_breg14 R14+3) +--- | + ; ModuleID = 'test.c' + source_filename = "test.c" + target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" + target triple = "x86_64-unknown-linux-gnu" + + ; Function Attrs: nounwind uwtable + define dso_local void @baa(i32 %arg1, i32 %arg2, i32 %arg3) local_unnamed_addr !dbg !10 { + entry: + %local1 = alloca i32, align 4 + call void @llvm.dbg.value(metadata i32 %arg1, metadata !15, metadata !DIExpression()), !dbg !19 + call void @llvm.dbg.value(metadata i32 %arg2, metadata !16, metadata !DIExpression()), !dbg !20 + call void @llvm.dbg.value(metadata i32 %arg3, metadata !17, metadata !DIExpression()), !dbg !21 + %0 = bitcast i32* %local1 to i8*, !dbg !22 + %call = tail call i32 (...) @getVal(), !dbg !23 + call void @llvm.dbg.value(metadata i32 %call, metadata !18, metadata !DIExpression()), !dbg !24 + store i32 %call, i32* %local1, align 4, !dbg !24 + %add = add nsw i32 %arg3, 3, !dbg !24 + %add1 = add nsw i32 %arg2, %arg1, !dbg !24 + call void @llvm.dbg.value(metadata i32* %local1, metadata !18, metadata !DIExpression(DW_OP_deref)), !dbg !24 + call void @foo(i32* nonnull %local1, i32 %arg2, i32 10, i32 15, i32 %add, i32 %add1), !dbg !24 + ret void, !dbg !24 + } + + declare !dbg !4 dso_local i32 @getVal(...) local_unnamed_addr + + declare !dbg !5 dso_local void @foo(i32*, i32, i32, i32, i32, i32) local_unnamed_addr + + ; Function Attrs: nounwind readnone speculatable + declare void @llvm.dbg.value(metadata, metadata, metadata) + + !llvm.dbg.cu = !{!0} + !llvm.module.flags = !{!6, !7, !8} + !llvm.ident = !{!9} + + !0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 9.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, retainedTypes: !3, nameTableKind: None) + !1 = !DIFile(filename: "test.c", directory: "/dir") + !2 = !{} + !3 = !{!4, !5} + !4 = !DISubprogram(name: "getVal", scope: !1, file: !1, line: 2, spFlags: DISPFlagOptimized, retainedNodes: !2) + !5 = !DISubprogram(name: "foo", scope: !1, file: !1, line: 1, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !2) + !6 = !{i32 2, !"Dwarf Version", i32 4} + !7 = !{i32 2, !"Debug Info Version", i32 3} + !8 = !{i32 1, !"wchar_size", i32 4} + !9 = !{!"clang version 9.0.0"} + !10 = distinct !DISubprogram(name: "baa", scope: !1, file: !1, line: 4, type: !11, scopeLine: 4, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !14) + !11 = !DISubroutineType(types: !12) + !12 = !{null, !13, !13, !13} + !13 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) + !14 = !{!15, !16, !17, !18} + !15 = !DILocalVariable(name: "arg1", arg: 1, scope: !10, file: !1, line: 4, type: !13, flags: DIFlagArgumentNotModified) + !16 = !DILocalVariable(name: "arg2", arg: 2, scope: !10, file: !1, line: 4, type: !13, flags: DIFlagArgumentNotModified) + !17 = !DILocalVariable(name: "arg3", arg: 3, scope: !10, file: !1, line: 4, type: !13, flags: DIFlagArgumentNotModified) + !18 = !DILocalVariable(name: "local1", scope: !10, file: !1, line: 5, type: !13) + !19 = !DILocation(line: 4, column: 14, scope: !10) + !20 = !DILocation(line: 4, column: 24, scope: !10) + !21 = !DILocation(line: 4, column: 34, scope: !10) + !22 = !DILocation(line: 5, column: 3, scope: !10) + !23 = !DILocation(line: 5, column: 16, scope: !10) + !24 = !DILocation(line: 5, column: 7, scope: !10) + +... +--- +name: baa +liveins: + - { reg: '$edi', virtual-reg: '' } + - { reg: '$esi', virtual-reg: '' } + - { reg: '$edx', virtual-reg: '' } +callSites: + - { bb: 0, offset: 21, fwdArgRegs: [] } + - { bb: 0, offset: 31, fwdArgRegs: + - { arg: 0, reg: '$rdi' } + - { arg: 1, reg: '$esi' } + - { arg: 2, reg: '$edx' } + - { arg: 3, reg: '$ecx' } + - { arg: 4, reg: '$r8d' } + - { arg: 5, reg: '$r9d' } } +body: | + bb.0.entry: + liveins: $edi, $edx, $esi, $r15, $r14, $rbx + + DBG_VALUE $edi, $noreg, !15, !DIExpression(), debug-location !19 + DBG_VALUE $esi, $noreg, !16, !DIExpression(), debug-location !20 + DBG_VALUE $edx, $noreg, !17, !DIExpression(), debug-location !21 + frame-setup PUSH64r killed $r15, implicit-def $rsp, implicit $rsp + CFI_INSTRUCTION def_cfa_offset 16 + frame-setup PUSH64r killed $r14, implicit-def $rsp, implicit $rsp + CFI_INSTRUCTION def_cfa_offset 24 + frame-setup PUSH64r killed $rbx, implicit-def $rsp, implicit $rsp + CFI_INSTRUCTION def_cfa_offset 32 + $rsp = frame-setup SUB64ri8 $rsp, 16, implicit-def dead $eflags + CFI_INSTRUCTION def_cfa_offset 48 + CFI_INSTRUCTION offset $rbx, -32 + CFI_INSTRUCTION offset $r14, -24 + CFI_INSTRUCTION offset $r15, -16 + $r14d = MOV32rr $edx, implicit-def $r14 + $ebx = MOV32rr $esi, implicit-def $rbx + $r15d = MOV32rr $edi, implicit-def $r15 + DBG_VALUE $r14d, $noreg, !17, !DIExpression(), debug-location !21 + DBG_VALUE $ebx, $noreg, !16, !DIExpression(), debug-location !20 + DBG_VALUE $r15d, $noreg, !15, !DIExpression(), debug-location !19 + dead $eax = XOR32rr undef $eax, undef $eax, implicit-def dead $eflags, implicit-def $al, debug-location !23 + CALL64pcrel32 @getVal, csr_64, implicit $rsp, implicit $ssp, implicit $al, implicit-def $rsp, implicit-def $ssp, implicit-def $eax, debug-location !23 + DBG_VALUE $eax, $noreg, !18, !DIExpression(), debug-location !24 + MOV32mr $rsp, 1, $noreg, 12, $noreg, killed renamable $eax, debug-location !24 :: (store 4 into %ir.local1) + renamable $r8d = LEA64_32r killed renamable $r14, 1, $noreg, 3, $noreg, debug-location !24 + renamable $r9d = LEA64_32r killed renamable $r15, 1, renamable $rbx, 0, $noreg, debug-location !24 + DBG_VALUE $rsp, $noreg, !18, !DIExpression(DW_OP_plus_uconst, 12, DW_OP_deref), debug-location !24 + renamable $rdi = LEA64r $rsp, 1, $noreg, 12, $noreg + $esi = MOV32rr $ebx, implicit killed $rbx, debug-location !24 + $edx = MOV32ri 10, debug-location !24 + $ecx = MOV32ri 15, debug-location !24 + CALL64pcrel32 @foo, csr_64, implicit $rsp, implicit $ssp, implicit $rdi, implicit $esi, implicit $edx, implicit killed $ecx, implicit $r8d, implicit $r9d, implicit-def $rsp, implicit-def $ssp, debug-location !24 + $rsp = frame-destroy ADD64ri8 $rsp, 16, implicit-def dead $eflags, debug-location !24 + CFI_INSTRUCTION def_cfa_offset 32, debug-location !24 + $rbx = frame-destroy POP64r implicit-def $rsp, implicit $rsp, debug-location !24 + CFI_INSTRUCTION def_cfa_offset 24, debug-location !24 + $r14 = frame-destroy POP64r implicit-def $rsp, implicit $rsp, debug-location !24 + CFI_INSTRUCTION def_cfa_offset 16, debug-location !24 + $r15 = frame-destroy POP64r implicit-def $rsp, implicit $rsp, debug-location !24 + CFI_INSTRUCTION def_cfa_offset 8, debug-location !24 + RETQ debug-location !24 + +... Index: test/DebugInfo/X86/dwarf-callsite-related-attrs.ll =================================================================== --- test/DebugInfo/X86/dwarf-callsite-related-attrs.ll +++ test/DebugInfo/X86/dwarf-callsite-related-attrs.ll @@ -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 @@ ; 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 Index: test/tools/llvm-dwarfdump/X86/stats-dbg-callsite-info.ll =================================================================== --- /dev/null +++ test/tools/llvm-dwarfdump/X86/stats-dbg-callsite-info.ll @@ -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: tools/llvm-dwarfdump/Statistics.cpp =================================================================== --- tools/llvm-dwarfdump/Statistics.cpp +++ tools/llvm-dwarfdump/Statistics.cpp @@ -56,9 +56,12 @@ /// 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 @@ 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 @@ 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);