diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst --- a/llvm/docs/LangRef.rst +++ b/llvm/docs/LangRef.rst @@ -5275,6 +5275,14 @@ introduced by 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_LLVM_arg, N`` is used in debug intrinsics that refer to more than one + value, such as one that calculates the sum of two registers. This is always + used in combination with an ordered list of values, such that + ``DW_OP_LLVM_arg, N`` refers to the ``N``th element in that list. For + example, ``!DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_arg, 1, DW_OP_minus, + DW_OP_stack_value)`` used with the list ``(%reg1, %reg2)`` would evaluate to + ``%reg1 - reg2``. This list of values should be provided by the containing + intrinsic/instruction. - ``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 @@ -5350,6 +5358,22 @@ !4 = !DIExpression(DW_OP_constu, 2, DW_OP_swap, DW_OP_xderef) !5 = !DIExpression(DW_OP_constu, 42, DW_OP_stack_value) +DIArgList +"""""""""""" + +``DIArgList`` nodes hold a list of constant or SSA value references. These are +used in :ref:`debug intrinsics` (currently only in +``llvm.dbg.value``) in combination with a ``DIExpression`` that uses the +``DW_OP_LLVM_arg`` operator. Because a DIArgList may refer to local values +within a function, it must only be used as a function argument, must always be +inlined, and cannot appear in named metadata. + +.. code-block:: text + + llvm.dbg.value(metadata !DIArgList(i32 %a, i32 %b), + metadata !16, + metadata !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_arg, 1, DW_OP_plus)) + DIFlags """"""""""""""" diff --git a/llvm/docs/SourceLevelDebugging.rst b/llvm/docs/SourceLevelDebugging.rst --- a/llvm/docs/SourceLevelDebugging.rst +++ b/llvm/docs/SourceLevelDebugging.rst @@ -576,13 +576,15 @@ After MIR locations are assigned to each variable, machine pseudo-instructions corresponding to each ``llvm.dbg.value`` and ``llvm.dbg.addr`` intrinsic are -inserted. These ``DBG_VALUE`` instructions appear thus: +inserted. There are two forms of this type of instruction. + +The first form, ``DBG_VALUE``, appears thus: .. code-block:: text DBG_VALUE %1, $noreg, !123, !DIExpression() -And have the following operands: +And has the following operands: * The first operand can record the variable location as a register, a frame index, an immediate, or the base address register if the original debug intrinsic referred to memory. ``$noreg`` indicates the variable @@ -594,6 +596,22 @@ * Operand 3 is the Variable field of the original debug intrinsic. * Operand 4 is the Expression field of the original debug intrinsic. +The second form, ``DBG_VALUE_LIST``, appears thus: + +.. code-block:: text + + DBG_VALUE_LIST !123, !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_arg, 1, DW_OP_plus), %1, %2 + +And has the following operands: + * The first operand is the Variable field of the original debug intrinsic. + * The second operand is the Expression field of the original debug intrinsic. + * Any number of operands, from the 3rd onwards, record a sequence of variable + location operands, which may take any of the same values as the first + operand of the ``DBG_VALUE`` instruction above. These variable location + operands are inserted into the final DWARF Expression in positions indicated + by the DW_OP_LLVM_arg operator in the DIExpression, as described in + :ref:`DIExpression<_DIExpression>`. + The position at which the DBG_VALUEs are inserted should correspond to the positions of their matching ``llvm.dbg.value`` intrinsics in the IR block. As with optimization, LLVM aims to preserve the order in which variable diff --git a/llvm/include/llvm-c/DebugInfo.h b/llvm/include/llvm-c/DebugInfo.h --- a/llvm/include/llvm-c/DebugInfo.h +++ b/llvm/include/llvm-c/DebugInfo.h @@ -161,7 +161,8 @@ LLVMDIMacroFileMetadataKind, LLVMDICommonBlockMetadataKind, LLVMDIStringTypeMetadataKind, - LLVMDIGenericSubrangeMetadataKind + LLVMDIGenericSubrangeMetadataKind, + LLVMDIArgListMetadataKind }; typedef unsigned LLVMMetadataKind; diff --git a/llvm/include/llvm/BinaryFormat/Dwarf.h b/llvm/include/llvm/BinaryFormat/Dwarf.h --- a/llvm/include/llvm/BinaryFormat/Dwarf.h +++ b/llvm/include/llvm/BinaryFormat/Dwarf.h @@ -125,6 +125,7 @@ DW_OP_LLVM_tag_offset = 0x1002, ///< Only used in LLVM metadata. DW_OP_LLVM_entry_value = 0x1003, ///< Only used in LLVM metadata. DW_OP_LLVM_implicit_pointer = 0x1004, ///< Only used in LLVM metadata. + DW_OP_LLVM_arg = 0x1005, ///< Only used in LLVM metadata. }; enum TypeKind : uint8_t { diff --git a/llvm/include/llvm/Bitcode/LLVMBitCodes.h b/llvm/include/llvm/Bitcode/LLVMBitCodes.h --- a/llvm/include/llvm/Bitcode/LLVMBitCodes.h +++ b/llvm/include/llvm/Bitcode/LLVMBitCodes.h @@ -342,8 +342,9 @@ METADATA_STRING_TYPE = 41, // [distinct, name, size, align,...] // Codes 42 and 43 are reserved for support for Fortran array specific debug // info. - METADATA_COMMON_BLOCK = 44, // [distinct, scope, name, variable,...] - METADATA_GENERIC_SUBRANGE = 45 // [distinct, count, lo, up, stride] + METADATA_COMMON_BLOCK = 44, // [distinct, scope, name, variable,...] + METADATA_GENERIC_SUBRANGE = 45, // [distinct, count, lo, up, stride] + METADATA_ARG_LIST = 46 // [n x [type num, value num]] }; // The constants block (CONSTANTS_BLOCK_ID) describes emission for each diff --git a/llvm/include/llvm/CodeGen/MachineInstr.h b/llvm/include/llvm/CodeGen/MachineInstr.h --- a/llvm/include/llvm/CodeGen/MachineInstr.h +++ b/llvm/include/llvm/CodeGen/MachineInstr.h @@ -419,11 +419,11 @@ /// instruction is indirect; will be an invalid register if this value is /// not indirect, and an immediate with value 0 otherwise. const MachineOperand &getDebugOffset() const { - assert(isDebugValue() && "not a DBG_VALUE"); + assert(isNonListDebugValue() && "not a DBG_VALUE"); return getOperand(1); } MachineOperand &getDebugOffset() { - assert(isDebugValue() && "not a DBG_VALUE"); + assert(isNonListDebugValue() && "not a DBG_VALUE"); return getOperand(1); } @@ -438,6 +438,7 @@ /// Return the operand for the complex address expression referenced by /// this DBG_VALUE instruction. + const MachineOperand &getDebugExpressionOp() const; MachineOperand &getDebugExpressionOp(); /// Return the complex address expression referenced by @@ -500,26 +501,43 @@ return *(debug_operands().begin() + Index); } - /// Returns a pointer to the operand corresponding to a debug use of Reg, or - /// nullptr if Reg is not used in any debug operand. - const MachineOperand *getDebugOperandForReg(Register Reg) const { - const MachineOperand *RegOp = - find_if(debug_operands(), [Reg](const MachineOperand &Op) { - return Op.isReg() && Op.getReg() == Reg; - }); - return RegOp == adl_end(debug_operands()) ? nullptr : RegOp; - } - MachineOperand *getDebugOperandForReg(Register Reg) { - MachineOperand *RegOp = - find_if(debug_operands(), [Reg](const MachineOperand &Op) { - return Op.isReg() && Op.getReg() == Reg; - }); - return RegOp == adl_end(debug_operands()) ? nullptr : RegOp; + /// Returns whether this debug value has at least one debug operand with the + /// register \p Reg. + bool hasDebugOperandForReg(Register Reg) const { + return any_of(debug_operands(), [Reg](const MachineOperand &Op) { + return Op.isReg() && Op.getReg() == Reg; + }); + } + + /// Returns a range of all of the operands that correspond to a debug use of + /// \p Reg. + template + static iterator_range< + filter_iterator>> + getDebugOperandsForReg(Instruction *MI, Register Reg) { + std::function OpUsesReg( + [Reg](Operand &Op) { return Op.isReg() && Op.getReg() == Reg; }); + return make_filter_range(MI->debug_operands(), OpUsesReg); + } + iterator_range>> + getDebugOperandsForReg(Register Reg) const { + return MachineInstr::getDebugOperandsForReg(this, Reg); + } + iterator_range>> + getDebugOperandsForReg(Register Reg) { + return MachineInstr::getDebugOperandsForReg( + this, Reg); + } + + bool isDebugOperand(const MachineOperand *Op) const { + return Op >= adl_begin(debug_operands()) && Op <= adl_end(debug_operands()); } unsigned getDebugOperandIndex(const MachineOperand *Op) const { - assert(Op >= adl_begin(debug_operands()) && - Op <= adl_end(debug_operands()) && "Expected a debug operand."); + assert(isDebugOperand(Op) && "Expected a debug operand."); return std::distance(adl_begin(debug_operands()), Op); } @@ -599,12 +617,16 @@ /// location for this DBG_VALUE instruction. iterator_range debug_operands() { assert(isDebugValue() && "Must be a debug value instruction."); - return make_range(operands_begin(), operands_begin() + 1); + return isDebugValueList() + ? make_range(operands_begin() + 2, operands_end()) + : make_range(operands_begin(), operands_begin() + 1); } /// \copydoc debug_operands() iterator_range debug_operands() const { assert(isDebugValue() && "Must be a debug value instruction."); - return make_range(operands_begin(), operands_begin() + 1); + return isDebugValueList() + ? make_range(operands_begin() + 2, operands_end()) + : make_range(operands_begin(), operands_begin() + 1); } /// Returns a range over all explicit operands that are register definitions. /// Implicit definition are not included! @@ -1159,19 +1181,29 @@ // True if the instruction represents a position in the function. bool isPosition() const { return isLabel() || isCFIInstruction(); } - bool isDebugValue() const { return getOpcode() == TargetOpcode::DBG_VALUE; } + bool isNonListDebugValue() const { + return getOpcode() == TargetOpcode::DBG_VALUE; + } + bool isDebugValueList() const { + return getOpcode() == TargetOpcode::DBG_VALUE_LIST; + } + bool isDebugValue() const { + return isNonListDebugValue() || isDebugValueList(); + } bool isDebugLabel() const { return getOpcode() == TargetOpcode::DBG_LABEL; } bool isDebugRef() const { return getOpcode() == TargetOpcode::DBG_INSTR_REF; } bool isDebugInstr() const { return isDebugValue() || isDebugLabel() || isDebugRef(); } - bool isDebugOffsetImm() const { return getDebugOffset().isImm(); } + bool isDebugOffsetImm() const { + return isNonListDebugValue() && getDebugOffset().isImm(); + } /// A DBG_VALUE is indirect iff the location operand is a register and /// the offset operand is an immediate. bool isIndirectDebugValue() const { - return isDebugValue() && getDebugOperand(0).isReg() && isDebugOffsetImm(); + return isDebugOffsetImm() && getDebugOperand(0).isReg(); } /// A DBG_VALUE is an entry value iff its debug expression contains the @@ -1181,8 +1213,13 @@ /// Return true if the instruction is a debug value which describes a part of /// a variable as unavailable. bool isUndefDebugValue() const { - return isDebugValue() && getDebugOperand(0).isReg() && - !getDebugOperand(0).getReg().isValid(); + if (!isDebugValue()) + return false; + // If any $noreg locations are given, this DV is undef. + for (const MachineOperand &Op : debug_operands()) + if (Op.isReg() && !Op.getReg().isValid()) + return true; + return false; } bool isPHI() const { @@ -1257,6 +1294,7 @@ case TargetOpcode::EH_LABEL: case TargetOpcode::GC_LABEL: case TargetOpcode::DBG_VALUE: + case TargetOpcode::DBG_VALUE_LIST: case TargetOpcode::DBG_INSTR_REF: case TargetOpcode::DBG_LABEL: case TargetOpcode::LIFETIME_START: diff --git a/llvm/include/llvm/CodeGen/MachineInstrBuilder.h b/llvm/include/llvm/CodeGen/MachineInstrBuilder.h --- a/llvm/include/llvm/CodeGen/MachineInstrBuilder.h +++ b/llvm/include/llvm/CodeGen/MachineInstrBuilder.h @@ -451,9 +451,16 @@ /// for a MachineOperand. MachineInstrBuilder BuildMI(MachineFunction &MF, const DebugLoc &DL, const MCInstrDesc &MCID, bool IsIndirect, - MachineOperand &MO, const MDNode *Variable, + const MachineOperand &MO, const MDNode *Variable, const MDNode *Expr); +/// This version of the builder builds a DBG_VALUE or DBG_VALUE_LIST intrinsic +/// for a MachineOperand. +MachineInstrBuilder BuildMI(MachineFunction &MF, const DebugLoc &DL, + const MCInstrDesc &MCID, bool IsIndirect, + ArrayRef MOs, + const MDNode *Variable, const MDNode *Expr); + /// This version of the builder builds a DBG_VALUE intrinsic /// for either a value in a register or a register-indirect /// address and inserts it at position I. @@ -471,14 +478,23 @@ MachineOperand &MO, const MDNode *Variable, const MDNode *Expr); +/// This version of the builder builds a DBG_VALUE or DBG_VALUE_LIST intrinsic +/// for a machine operand and inserts it at position I. +MachineInstrBuilder BuildMI(MachineBasicBlock &BB, + MachineBasicBlock::iterator I, const DebugLoc &DL, + const MCInstrDesc &MCID, bool IsIndirect, + ArrayRef MOs, + const MDNode *Variable, const MDNode *Expr); + /// Clone a DBG_VALUE whose value has been spilled to FrameIndex. MachineInstr *buildDbgValueForSpill(MachineBasicBlock &BB, MachineBasicBlock::iterator I, - const MachineInstr &Orig, int FrameIndex); + const MachineInstr &Orig, int FrameIndex, + Register SpillReg); /// Update a DBG_VALUE whose value has been spilled to FrameIndex. Useful when /// modifying an instruction in place while iterating over a basic block. -void updateDbgValueForSpill(MachineInstr &Orig, int FrameIndex); +void updateDbgValueForSpill(MachineInstr &Orig, int FrameIndex, Register Reg); inline unsigned getDefRegState(bool B) { return B ? RegState::Define : 0; diff --git a/llvm/include/llvm/CodeGen/SelectionDAG.h b/llvm/include/llvm/CodeGen/SelectionDAG.h --- a/llvm/include/llvm/CodeGen/SelectionDAG.h +++ b/llvm/include/llvm/CodeGen/SelectionDAG.h @@ -74,6 +74,7 @@ class OptimizationRemarkEmitter; class ProfileSummaryInfo; class SDDbgValue; +class SDDbgOperand; class SDDbgLabel; class SelectionDAG; class SelectionDAGTargetInfo; @@ -159,17 +160,9 @@ SDDbgInfo(const SDDbgInfo &) = delete; SDDbgInfo &operator=(const SDDbgInfo &) = delete; - void add(SDDbgValue *V, const SDNode *Node, bool isParameter) { - if (isParameter) { - ByvalParmDbgValues.push_back(V); - } else DbgValues.push_back(V); - if (Node) - DbgValMap[Node].push_back(V); - } + void add(SDDbgValue *V, bool isParameter); - void add(SDDbgLabel *L) { - DbgLabels.push_back(L); - } + void add(SDDbgLabel *L) { DbgLabels.push_back(L); } /// Invalidate all DbgValues attached to the node and remove /// it from the Node-to-DbgValues map. @@ -1534,11 +1527,24 @@ unsigned FI, bool IsIndirect, const DebugLoc &DL, unsigned O); + /// Creates a FrameIndex SDDbgValue node. + SDDbgValue *getFrameIndexDbgValue(DIVariable *Var, DIExpression *Expr, + unsigned FI, + ArrayRef Dependencies, + bool IsIndirect, const DebugLoc &DL, + unsigned O); + /// Creates a VReg SDDbgValue node. SDDbgValue *getVRegDbgValue(DIVariable *Var, DIExpression *Expr, unsigned VReg, bool IsIndirect, const DebugLoc &DL, unsigned O); + /// Creates a SDDbgValue node from a list of locations. + SDDbgValue *getDbgValueList(DIVariable *Var, DIExpression *Expr, + ArrayRef Locs, + ArrayRef Dependencies, bool IsIndirect, + const DebugLoc &DL, unsigned O, bool IsVariadic); + /// Creates a SDDbgLabel node. SDDbgLabel *getDbgLabel(DILabel *Label, const DebugLoc &DL, unsigned O); @@ -1629,7 +1635,7 @@ /// Add a dbg_value SDNode. If SD is non-null that means the /// value is produced by SD. - void AddDbgValue(SDDbgValue *DB, SDNode *SD, bool isParameter); + void AddDbgValue(SDDbgValue *DB, bool isParameter); /// Add a dbg_label SDNode. void AddDbgLabel(SDDbgLabel *DB); diff --git a/llvm/include/llvm/IR/DebugInfoMetadata.h b/llvm/include/llvm/IR/DebugInfoMetadata.h --- a/llvm/include/llvm/IR/DebugInfoMetadata.h +++ b/llvm/include/llvm/IR/DebugInfoMetadata.h @@ -2587,6 +2587,16 @@ return Elements[I]; } + /// Return the number of unique location operands referred to (via + /// DW_OP_LLVM_arg) in this expression; this is not necessarily the number of + /// instances of DW_OP_LLVM_arg within the expression. + /// For example, for the expression: + /// (DW_OP_LLVM_arg 0, DW_OP_LLVM_arg 1, DW_OP_plus, + /// DW_OP_LLVM_arg 0, DW_OP_mul) + /// This function would return 2, as there are two unique location operands + /// (0 and 1). + uint64_t getNumLocationOperands() const; + /// Determine whether this represents a standalone constant value. bool isConstant() const; @@ -2735,6 +2745,10 @@ /// return true with an offset of zero. bool extractIfOffset(int64_t &Offset) const; + /// Returns true iff this DIExpression contains at least one instance of + /// `DW_OP_LLVM_arg, n` for all n in [0, N). + bool hasAllLocationOps(unsigned N) const; + /// Checks if the last 4 elements of the expression are DW_OP_constu DW_OP_swap DW_OP_xderef and extracts the . @@ -2775,6 +2789,23 @@ static DIExpression *appendToStack(const DIExpression *Expr, ArrayRef Ops); + /// Create a copy of \p Expr by appending the given list of \p Ops to each + /// instance of the operand `DW_OP_LLVM_arg, \p ArgNo`. This is used to + /// modify a specific location used by \p Expr, such as when salvaging that + /// location. + static DIExpression *appendOpsToArg(const DIExpression *Expr, + ArrayRef Ops, unsigned ArgNo, + bool StackValue = false); + + /// Create a copy of \p Expr with each instance of + /// `DW_OP_LLVM_arg, \p OldArg` replaced with `DW_OP_LLVM_arg, \p NewArg`, + /// and each instance of `DW_OP_LLVM_arg, Arg` with `DW_OP_LLVM_arg, Arg - 1` + /// for all Arg > \p OldArg. + /// This is used when replacing one of the operands of a debug value list + /// with another operand in the same list and deleting the old operand. + static DIExpression *replaceArg(const DIExpression *Expr, uint64_t OldArg, + uint64_t NewArg); + /// Create a DIExpression to describe one part of an aggregate variable that /// is fragmented across multiple Values. The DW_OP_LLVM_fragment operation /// will be appended to the elements of \c Expr. If \c Expr already contains @@ -3512,6 +3543,52 @@ } }; +/// List of ValueAsMetadata, to be used as an argument to a dbg.value +/// intrinsic. +class DIArgList : public MDNode { + friend class LLVMContextImpl; + friend class MDNode; + using iterator = SmallVectorImpl::iterator; + + SmallVector Args; + + DIArgList(LLVMContext &C, StorageType Storage, + ArrayRef Args) + : MDNode(C, DIArgListKind, Storage, None), + Args(Args.begin(), Args.end()) { + track(); + } + ~DIArgList() { untrack(); } + + static DIArgList *getImpl(LLVMContext &Context, + ArrayRef Args, + StorageType Storage, bool ShouldCreate = true); + + TempDIArgList cloneImpl() const { + return getTemporary(getContext(), getArgs()); + } + + void track(); + void untrack(); + void dropAllReferences(); + +public: + DEFINE_MDNODE_GET(DIArgList, (ArrayRef Args), (Args)) + + TempDIArgList clone() const { return cloneImpl(); } + + ArrayRef getArgs() const { return Args; } + + iterator args_begin() { return Args.begin(); } + iterator args_end() { return Args.end(); } + + static bool classof(const Metadata *MD) { + return MD->getMetadataID() == DIArgListKind; + } + + void handleChangedOperand(void *Ref, Metadata *New); +}; + /// Identifies a unique instance of a variable. /// /// Storage for identifying a potentially inlined instance of a variable, diff --git a/llvm/include/llvm/IR/Instructions.h b/llvm/include/llvm/IR/Instructions.h --- a/llvm/include/llvm/IR/Instructions.h +++ b/llvm/include/llvm/IR/Instructions.h @@ -1122,7 +1122,9 @@ /// must be at least as wide as the IntPtr type for the address space of /// the base GEP pointer. bool accumulateConstantOffset(const DataLayout &DL, APInt &Offset) const; - + bool collectOffset(const DataLayout &DL, unsigned BitWidth, + SmallDenseMap &VariableOffsets, + APInt &ConstantOffset) const; // Methods for support type inquiry through isa, cast, and dyn_cast: static bool classof(const Instruction *I) { return (I->getOpcode() == Instruction::GetElementPtr); diff --git a/llvm/include/llvm/IR/IntrinsicInst.h b/llvm/include/llvm/IR/IntrinsicInst.h --- a/llvm/include/llvm/IR/IntrinsicInst.h +++ b/llvm/include/llvm/IR/IntrinsicInst.h @@ -24,6 +24,7 @@ #define LLVM_IR_INTRINSICINST_H #include "llvm/IR/Constants.h" +#include "llvm/IR/DebugInfoMetadata.h" #include "llvm/IR/DerivedTypes.h" #include "llvm/IR/FPEnv.h" #include "llvm/IR/Function.h" @@ -123,10 +124,83 @@ /// This is the common base class for debug info intrinsics for variables. class DbgVariableIntrinsic : public DbgInfoIntrinsic { public: - /// Get the location corresponding to the variable referenced by the debug + // Iterator for ValueAsMetadata that internally uses direct pointer iteration + // over either a ValueAsMetadata* or a ValueAsMetadata**, dereferencing to the + // ValueAsMetadata . + class location_op_iterator + : public iterator_facade_base { + PointerUnion I; + + public: + location_op_iterator(ValueAsMetadata *SingleIter) : I(SingleIter) {} + location_op_iterator(ValueAsMetadata **MultiIter) : I(MultiIter) {} + + location_op_iterator &operator=(const location_op_iterator &R) { + I = R.I; + return *this; + } + bool operator==(const location_op_iterator &RHS) const { + return I == RHS.I; + } + const Value *operator*() const { + ValueAsMetadata *VAM = I.is() + ? I.get() + : *I.get(); + return VAM->getValue(); + }; + Value *operator*() { + ValueAsMetadata *VAM = I.is() + ? I.get() + : *I.get(); + return VAM->getValue(); + } + location_op_iterator &operator++() { + if (I.is()) + I = I.get() + 1; + else + I = I.get() + 1; + return *this; + } + location_op_iterator &operator--() { + if (I.is()) + I = I.get() - 1; + else + I = I.get() - 1; + return *this; + } + }; + + /// Get the locations corresponding to the variable referenced by the debug /// info intrinsic. Depending on the intrinsic, this could be the /// variable's value or its address. - Value *getVariableLocation(bool AllowNullOp = true) const; + iterator_range location_ops() const; + + Value *getVariableLocationOp(unsigned OpIdx) const; + + void replaceVariableLocationOp(Value *OldValue, Value *NewValue); + void replaceVariableLocationOp(unsigned OpIdx, Value *NewValue); + /// Adding a new location operand will always result in this intrinsic using + /// an ArgList, and must always be accompanied by a new expression that uses + /// the new operand. + void addVariableLocationOps(ArrayRef NewValues, + DIExpression *NewExpr); + + void setVariable(DILocalVariable *NewVar) { + setArgOperand(1, MetadataAsValue::get(NewVar->getContext(), NewVar)); + } + + void setExpression(DIExpression *NewExpr) { + setArgOperand(2, MetadataAsValue::get(NewExpr->getContext(), NewExpr)); + } + + unsigned getNumVariableLocationOps() const { + if (hasArgList()) + return cast(getRawLocation())->getArgs().size(); + return 1; + } + + bool hasArgList() const { return isa(getRawLocation()); } /// Does this describe the address of a local variable. True for dbg.addr /// and dbg.declare, but not dbg.value, which describes its value. @@ -134,6 +208,24 @@ return getIntrinsicID() != Intrinsic::dbg_value; } + void setUndef() { + // TODO: When/if we remove duplicate values from DIArgLists, we don't need + // this set anymore. + SmallPtrSet RemovedValues; + for (Value *OldValue : location_ops()) { + if (!RemovedValues.insert(OldValue).second) + continue; + Value *Undef = UndefValue::get(OldValue->getType()); + replaceVariableLocationOp(OldValue, Undef); + } + } + + bool isUndef() const { + return (getNumVariableLocationOps() == 0 && + !getExpression()->isComplex()) || + any_of(location_ops(), [](Value *V) { return isa(V); }); + } + DILocalVariable *getVariable() const { return cast(getRawVariable()); } @@ -142,6 +234,10 @@ return cast(getRawExpression()); } + Metadata *getRawLocation() const { + return cast(getArgOperand(0))->getMetadata(); + } + Metadata *getRawVariable() const { return cast(getArgOperand(1))->getMetadata(); } @@ -170,12 +266,21 @@ return isa(V) && classof(cast(V)); } /// @} +private: + void setArgOperand(unsigned i, Value *v) { + DbgInfoIntrinsic::setArgOperand(i, v); + } + void setOperand(unsigned i, Value *v) { DbgInfoIntrinsic::setOperand(i, v); } }; /// This represents the llvm.dbg.declare instruction. class DbgDeclareInst : public DbgVariableIntrinsic { public: - Value *getAddress() const { return getVariableLocation(); } + Value *getAddress() const { + assert(getNumVariableLocationOps() == 1 && + "dbg.declare must have exactly 1 location operand."); + return getVariableLocationOp(0); + } /// \name Casting methods /// @{ @@ -191,7 +296,11 @@ /// This represents the llvm.dbg.addr instruction. class DbgAddrIntrinsic : public DbgVariableIntrinsic { public: - Value *getAddress() const { return getVariableLocation(); } + Value *getAddress() const { + assert(getNumVariableLocationOps() == 1 && + "dbg.addr must have exactly 1 location operand."); + return getVariableLocationOp(0); + } /// \name Casting methods /// @{ @@ -206,8 +315,13 @@ /// This represents the llvm.dbg.value instruction. class DbgValueInst : public DbgVariableIntrinsic { public: - Value *getValue() const { - return getVariableLocation(/* AllowNullOp = */ false); + // The default argument should only be used in ISel, and the default option + // should be removed once ISel support for multiple location ops is complete. + Value *getValue(unsigned OpIdx = 0) const { + return getVariableLocationOp(OpIdx); + } + iterator_range getValues() const { + return location_ops(); } /// \name Casting methods diff --git a/llvm/include/llvm/IR/Metadata.h b/llvm/include/llvm/IR/Metadata.h --- a/llvm/include/llvm/IR/Metadata.h +++ b/llvm/include/llvm/IR/Metadata.h @@ -301,6 +301,9 @@ /// Replace all uses of this with \c MD, which is allowed to be null. void replaceAllUsesWith(Metadata *MD); + /// Returns the list of all DIArgList users of this. + SmallVector getAllArgListUsers(); + /// Resolve all uses of this. /// /// Resolve all uses of this, turning off RAUW permanently. If \c @@ -380,6 +383,10 @@ Type *getType() const { return V->getType(); } LLVMContext &getContext() const { return V->getContext(); } + SmallVector getAllArgListUsers() { + return ReplaceableMetadataImpl::getAllArgListUsers(); + } + static void handleDeletion(Value *V); static void handleRAUW(Value *From, Value *To); diff --git a/llvm/include/llvm/IR/Metadata.def b/llvm/include/llvm/IR/Metadata.def --- a/llvm/include/llvm/IR/Metadata.def +++ b/llvm/include/llvm/IR/Metadata.def @@ -114,6 +114,7 @@ HANDLE_SPECIALIZED_MDNODE_LEAF_UNIQUABLE(DIMacro) HANDLE_SPECIALIZED_MDNODE_LEAF_UNIQUABLE(DIMacroFile) HANDLE_SPECIALIZED_MDNODE_LEAF_UNIQUABLE(DICommonBlock) +HANDLE_SPECIALIZED_MDNODE_LEAF_UNIQUABLE(DIArgList) HANDLE_SPECIALIZED_MDNODE_LEAF_UNIQUABLE(DIStringType) HANDLE_SPECIALIZED_MDNODE_LEAF_UNIQUABLE(DIGenericSubrange) diff --git a/llvm/include/llvm/IR/Operator.h b/llvm/include/llvm/IR/Operator.h --- a/llvm/include/llvm/IR/Operator.h +++ b/llvm/include/llvm/IR/Operator.h @@ -568,6 +568,15 @@ bool accumulateConstantOffset( const DataLayout &DL, APInt &Offset, function_ref ExternalAnalysis = nullptr) const; + + /// Collect the offset of this GEP as a map of Values to their associated + /// APInt multipliers. + /// + /// The nullptr key maps to the total constant offset, rather than a + /// multiplier. + bool collectOffset(const DataLayout &DL, unsigned BitWidth, + SmallDenseMap &VariableOffsets, + APInt &ConstantOffset) const; }; class PtrToIntOperator diff --git a/llvm/include/llvm/Support/TargetOpcodes.def b/llvm/include/llvm/Support/TargetOpcodes.def --- a/llvm/include/llvm/Support/TargetOpcodes.def +++ b/llvm/include/llvm/Support/TargetOpcodes.def @@ -77,6 +77,10 @@ /// DBG_VALUE - a mapping of the llvm.dbg.value intrinsic HANDLE_TARGET_OPCODE(DBG_VALUE) +/// DBG_VALUE - a mapping of the llvm.dbg.value intrinsic with a variadic +/// list of locations +HANDLE_TARGET_OPCODE(DBG_VALUE_LIST) + /// DBG_INSTR_REF - A mapping of llvm.dbg.value referring to the instruction /// that defines the value, rather than a virtual register. HANDLE_TARGET_OPCODE(DBG_INSTR_REF) diff --git a/llvm/include/llvm/Target/Target.td b/llvm/include/llvm/Target/Target.td --- a/llvm/include/llvm/Target/Target.td +++ b/llvm/include/llvm/Target/Target.td @@ -1100,6 +1100,12 @@ let AsmString = "DBG_VALUE"; let hasSideEffects = false; } +def DBG_VALUE_LIST : StandardPseudoInstruction { + let OutOperandList = (outs); + let InOperandList = (ins variable_ops); + let AsmString = "DBG_VALUE_LIST"; + let hasSideEffects = 0; +} def DBG_INSTR_REF : StandardPseudoInstruction { let OutOperandList = (outs); let InOperandList = (ins variable_ops); diff --git a/llvm/include/llvm/Transforms/Utils/Local.h b/llvm/include/llvm/Transforms/Utils/Local.h --- a/llvm/include/llvm/Transforms/Utils/Local.h +++ b/llvm/include/llvm/Transforms/Utils/Local.h @@ -312,9 +312,11 @@ /// Given an instruction \p I and DIExpression \p DIExpr operating on it, write /// the effects of \p I into the returned DIExpression, or return nullptr if /// it cannot be salvaged. \p StackVal: whether DW_OP_stack_value should be -/// appended to the expression. +/// appended to the expression. \p LocNo: the index of the location operand to +/// which \p I applies, should be 0 for debug info without a DIArgList. DIExpression *salvageDebugInfoImpl(Instruction &I, DIExpression *DIExpr, - bool StackVal); + bool StackVal, unsigned LocNo, + SmallVectorImpl &AdditionalValues); /// Point debug users of \p From to \p To or salvage them. Use this function /// only when replacing all uses of \p From with \p To, with a guarantee that diff --git a/llvm/lib/AsmParser/LLParser.h b/llvm/lib/AsmParser/LLParser.h --- a/llvm/lib/AsmParser/LLParser.h +++ b/llvm/lib/AsmParser/LLParser.h @@ -536,6 +536,8 @@ #define HANDLE_SPECIALIZED_MDNODE_LEAF(CLASS) \ bool parse##CLASS(MDNode *&Result, bool IsDistinct); #include "llvm/IR/Metadata.def" + bool parseDIArgList(MDNode *&Result, bool IsDistinct, + PerFunctionState *PFS); // Function Parsing. struct ArgInfo { diff --git a/llvm/lib/AsmParser/LLParser.cpp b/llvm/lib/AsmParser/LLParser.cpp --- a/llvm/lib/AsmParser/LLParser.cpp +++ b/llvm/lib/AsmParser/LLParser.cpp @@ -776,6 +776,11 @@ Lex.getStrVal() == "DIExpression") { if (parseDIExpression(N, /*IsDistinct=*/false)) return true; + // DIArgLists should only appear inline in a function, as they may + // contain LocalAsMetadata arguments which require a function context. + } else if (Lex.getKind() == lltok::MetadataVar && + Lex.getStrVal() == "DIArgList") { + return tokError("found DIArgList outside of function"); } else if (parseToken(lltok::exclaim, "Expected '!' here") || parseMDNodeID(N)) { return true; @@ -5294,6 +5299,36 @@ return false; } +bool LLParser::parseDIArgList(MDNode *&Result, bool IsDistinct) { + return parseDIArgList(Result, IsDistinct, nullptr); +} +/// ParseDIArgList: +/// ::= !DIArgList(i32 7, i64 %0) +bool LLParser::parseDIArgList(MDNode *&Result, bool IsDistinct, + PerFunctionState *PFS) { + assert(PFS && "Expected valid function state"); + assert(Lex.getKind() == lltok::MetadataVar && "Expected metadata type name"); + Lex.Lex(); + + if (parseToken(lltok::lparen, "expected '(' here")) + return true; + + SmallVector Args; + if (Lex.getKind() != lltok::rparen) + do { + Metadata *MD; + if (parseValueAsMetadata(MD, "expected value-as-metadata operand", PFS)) + return true; + Args.push_back(dyn_cast(MD)); + } while (EatIfPresent(lltok::comma)); + + if (parseToken(lltok::rparen, "expected ')' here")) + return true; + + Result = GET_OR_DISTINCT(DIArgList, (Context, Args)); + return false; +} + /// parseDIGlobalVariableExpression: /// ::= !DIGlobalVariableExpression(var: !0, expr: !1) bool LLParser::parseDIGlobalVariableExpression(MDNode *&Result, @@ -5404,8 +5439,14 @@ bool LLParser::parseMetadata(Metadata *&MD, PerFunctionState *PFS) { if (Lex.getKind() == lltok::MetadataVar) { MDNode *N; - if (parseSpecializedMDNode(N)) + // DIArgLists are a special case, as they are a list of ValueAsMetadata and + // so parsing this requires a Function State. + if (Lex.getStrVal() == "DIArgList") { + if (parseDIArgList(N, false, PFS)) + return true; + } else if (parseSpecializedMDNode(N)) { return true; + } MD = N; return false; } diff --git a/llvm/lib/BinaryFormat/Dwarf.cpp b/llvm/lib/BinaryFormat/Dwarf.cpp --- a/llvm/lib/BinaryFormat/Dwarf.cpp +++ b/llvm/lib/BinaryFormat/Dwarf.cpp @@ -153,6 +153,8 @@ return "DW_OP_LLVM_entry_value"; case DW_OP_LLVM_implicit_pointer: return "DW_OP_LLVM_implicit_pointer"; + case DW_OP_LLVM_arg: + return "DW_OP_LLVM_arg"; } } @@ -166,6 +168,7 @@ .Case("DW_OP_LLVM_tag_offset", DW_OP_LLVM_tag_offset) .Case("DW_OP_LLVM_entry_value", DW_OP_LLVM_entry_value) .Case("DW_OP_LLVM_implicit_pointer", DW_OP_LLVM_implicit_pointer) + .Case("DW_OP_LLVM_arg", DW_OP_LLVM_arg) .Default(0); } diff --git a/llvm/lib/Bitcode/Reader/BitcodeAnalyzer.cpp b/llvm/lib/Bitcode/Reader/BitcodeAnalyzer.cpp --- a/llvm/lib/Bitcode/Reader/BitcodeAnalyzer.cpp +++ b/llvm/lib/Bitcode/Reader/BitcodeAnalyzer.cpp @@ -361,6 +361,7 @@ STRINGIFY_CODE(METADATA, GLOBAL_VAR_EXPR) STRINGIFY_CODE(METADATA, INDEX_OFFSET) STRINGIFY_CODE(METADATA, INDEX) + STRINGIFY_CODE(METADATA, ARG_LIST) } case bitc::METADATA_KIND_BLOCK_ID: switch (CodeID) { diff --git a/llvm/lib/Bitcode/Reader/MetadataLoader.cpp b/llvm/lib/Bitcode/Reader/MetadataLoader.cpp --- a/llvm/lib/Bitcode/Reader/MetadataLoader.cpp +++ b/llvm/lib/Bitcode/Reader/MetadataLoader.cpp @@ -550,8 +550,7 @@ SmallVector Ops; Ops.append(std::next(DIExpr->elements_begin()), DIExpr->elements_end()); - auto *E = DIExpression::get(Context, Ops); - DDI->setOperand(2, MetadataAsValue::get(Context, E)); + DDI->setExpression(DIExpression::get(Context, Ops)); } } @@ -2076,6 +2075,16 @@ return Err; break; } + case bitc::METADATA_ARG_LIST: { + SmallVector Elts; + Elts.reserve(Record.size()); + for (uint64_t Elt : Record) + Elts.push_back(dyn_cast_or_null(getMDOrNull(Elt))); + + MetadataList.assignValue(DIArgList::get(Context, Elts), NextMetadataNo); + NextMetadataNo++; + break; + } } return Error::success(); #undef GET_OR_DISTINCT diff --git a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp --- a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp +++ b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp @@ -335,6 +335,8 @@ unsigned Abbrev); void writeDIMacroFile(const DIMacroFile *N, SmallVectorImpl &Record, unsigned Abbrev); + void writeDIArgList(const DIArgList *N, SmallVectorImpl &Record, + unsigned Abbrev); void writeDIModule(const DIModule *N, SmallVectorImpl &Record, unsigned Abbrev); void writeDITemplateTypeParameter(const DITemplateTypeParameter *N, @@ -1862,6 +1864,17 @@ Record.clear(); } +void ModuleBitcodeWriter::writeDIArgList(const DIArgList *N, + SmallVectorImpl &Record, + unsigned Abbrev) { + Record.reserve(N->getArgs().size()); + for (ValueAsMetadata *MD : N->getArgs()) + Record.push_back(VE.getMetadataOrNullID(MD)); + + Stream.EmitRecord(bitc::METADATA_ARG_LIST, Record, Abbrev); + Record.clear(); +} + void ModuleBitcodeWriter::writeDIModule(const DIModule *N, SmallVectorImpl &Record, unsigned Abbrev) { diff --git a/llvm/lib/Bitcode/Writer/ValueEnumerator.cpp b/llvm/lib/Bitcode/Writer/ValueEnumerator.cpp --- a/llvm/lib/Bitcode/Writer/ValueEnumerator.cpp +++ b/llvm/lib/Bitcode/Writer/ValueEnumerator.cpp @@ -449,7 +449,8 @@ } // Local metadata is enumerated during function-incorporation. - if (isa(MD->getMetadata())) + if (isa(MD->getMetadata()) || + isa(MD->getMetadata())) continue; EnumerateMetadata(&F, MD->getMetadata()); @@ -1031,14 +1032,25 @@ FirstInstID = Values.size(); SmallVector FnLocalMDVector; + SmallVector ArgListMDVector; // Add all of the instructions. for (const BasicBlock &BB : F) { for (const Instruction &I : BB) { for (const Use &OI : I.operands()) { if (auto *MD = dyn_cast(&OI)) - if (auto *Local = dyn_cast(MD->getMetadata())) + if (auto *Local = dyn_cast(MD->getMetadata())) { // Enumerate metadata after the instructions they might refer to. FnLocalMDVector.push_back(Local); + } else if (auto *ArgList = dyn_cast(MD->getMetadata())) { + ArgListMDVector.push_back(ArgList); + for (ValueAsMetadata *VMD : ArgList->getArgs()) { + if (auto *Local = dyn_cast(VMD)) { + // Enumerate metadata after the instructions they might refer + // to. + FnLocalMDVector.push_back(Local); + } + } + } } if (!I.getType()->isVoidTy()) @@ -1054,6 +1066,10 @@ "Missing value for metadata operand"); EnumerateFunctionLocalMetadata(F, FnLocalMDVector[i]); } + // DIArgList entries must come after function-local metadata, as it is not + // possible to forward-reference them. + for (const DIArgList *ArgList : ArgListMDVector) + EnumerateMetadata(&F, ArgList); } void ValueEnumerator::purgeFunction() { diff --git a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp --- a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp @@ -900,7 +900,7 @@ /// means the target will need to handle MI in EmitInstruction. static bool emitDebugValueComment(const MachineInstr *MI, AsmPrinter &AP) { // This code handles only the 4-operand target-independent form. - if (MI->getNumOperands() != 4) + if (MI->isNonListDebugValue() && MI->getNumOperands() != 4) return false; SmallString<128> Str; @@ -916,9 +916,6 @@ OS << V->getName(); OS << " <- "; - // The second operand is only an offset if it's an immediate. - bool MemLoc = MI->isIndirectDebugValue(); - auto Offset = StackOffset::getFixed(MemLoc ? MI->getOperand(1).getImm() : 0); const DIExpression *Expr = MI->getDebugExpression(); if (Expr->getNumElements()) { OS << '['; @@ -936,56 +933,71 @@ } // Register or immediate value. Register 0 means undef. - if (MI->getDebugOperand(0).isFPImm()) { - APFloat APF = APFloat(MI->getDebugOperand(0).getFPImm()->getValueAPF()); - if (MI->getDebugOperand(0).getFPImm()->getType()->isFloatTy()) { - OS << (double)APF.convertToFloat(); - } else if (MI->getDebugOperand(0).getFPImm()->getType()->isDoubleTy()) { - OS << APF.convertToDouble(); - } else { - // There is no good way to print long double. Convert a copy to - // double. Ah well, it's only a comment. - bool ignored; - APF.convert(APFloat::IEEEdouble(), APFloat::rmNearestTiesToEven, - &ignored); - OS << "(long double) " << APF.convertToDouble(); + for (const MachineOperand &Op : MI->debug_operands()) { + if (&Op != MI->debug_operands().begin()) + OS << ", "; + switch (Op.getType()) { + case MachineOperand::MO_FPImmediate: { + APFloat APF = APFloat(Op.getFPImm()->getValueAPF()); + if (Op.getFPImm()->getType()->isFloatTy()) { + OS << (double)APF.convertToFloat(); + } else if (Op.getFPImm()->getType()->isDoubleTy()) { + OS << APF.convertToDouble(); + } else { + // There is no good way to print long double. Convert a copy to + // double. Ah well, it's only a comment. + bool ignored; + APF.convert(APFloat::IEEEdouble(), APFloat::rmNearestTiesToEven, + &ignored); + OS << "(long double) " << APF.convertToDouble(); + } + break; } - } else if (MI->getDebugOperand(0).isImm()) { - OS << MI->getDebugOperand(0).getImm(); - } else if (MI->getDebugOperand(0).isCImm()) { - MI->getDebugOperand(0).getCImm()->getValue().print(OS, false /*isSigned*/); - } else if (MI->getDebugOperand(0).isTargetIndex()) { - auto Op = MI->getDebugOperand(0); - OS << "!target-index(" << Op.getIndex() << "," << Op.getOffset() << ")"; - // NOTE: Want this comment at start of line, don't emit with AddComment. - AP.OutStreamer->emitRawComment(OS.str()); - return true; - } else { - Register Reg; - if (MI->getDebugOperand(0).isReg()) { - Reg = MI->getDebugOperand(0).getReg(); - } else { - assert(MI->getDebugOperand(0).isFI() && "Unknown operand type"); - const TargetFrameLowering *TFI = AP.MF->getSubtarget().getFrameLowering(); - Offset += TFI->getFrameIndexReference( - *AP.MF, MI->getDebugOperand(0).getIndex(), Reg); - MemLoc = true; + case MachineOperand::MO_Immediate: { + OS << Op.getImm(); + break; + } + case MachineOperand::MO_CImmediate: { + Op.getCImm()->getValue().print(OS, false /*isSigned*/); + break; } - if (Reg == 0) { - // Suppress offset, it is not meaningful here. - OS << "undef"; + case MachineOperand::MO_TargetIndex: { + OS << "!target-index(" << Op.getIndex() << "," << Op.getOffset() << ")"; // NOTE: Want this comment at start of line, don't emit with AddComment. AP.OutStreamer->emitRawComment(OS.str()); - return true; + break; + } + case MachineOperand::MO_Register: + case MachineOperand::MO_FrameIndex: { + Register Reg; + Optional Offset; + if (Op.isReg()) { + Reg = Op.getReg(); + } else { + const TargetFrameLowering *TFI = + AP.MF->getSubtarget().getFrameLowering(); + Offset = TFI->getFrameIndexReference(*AP.MF, Op.getIndex(), Reg); + } + if (!Reg) { + // Suppress offset, it is not meaningful here. + OS << "undef"; + break; + } + // The second operand is only an offset if it's an immediate. + if (MI->isIndirectDebugValue()) + Offset = StackOffset::getFixed(MI->getDebugOffset().getImm()); + if (Offset) + OS << '['; + OS << printReg(Reg, AP.MF->getSubtarget().getRegisterInfo()); + if (Offset) + OS << '+' << Offset->getFixed() << ']'; + break; + } + default: + llvm_unreachable("Unknown operand type"); } - if (MemLoc) - OS << '['; - OS << printReg(Reg, AP.MF->getSubtarget().getRegisterInfo()); } - if (MemLoc) - OS << '+' << Offset.getFixed() << ']'; - // NOTE: Want this comment at start of line, don't emit with AddComment. AP.OutStreamer->emitRawComment(OS.str()); return true; @@ -1227,6 +1239,7 @@ emitInlineAsm(&MI); break; case TargetOpcode::DBG_VALUE: + case TargetOpcode::DBG_VALUE_LIST: if (isVerbose()) { if (!emitDebugValueComment(&MI, *this)) emitInstruction(&MI); diff --git a/llvm/lib/CodeGen/AsmPrinter/DbgEntityHistoryCalculator.cpp b/llvm/lib/CodeGen/AsmPrinter/DbgEntityHistoryCalculator.cpp --- a/llvm/lib/CodeGen/AsmPrinter/DbgEntityHistoryCalculator.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/DbgEntityHistoryCalculator.cpp @@ -37,22 +37,6 @@ using EntryIndex = DbgValueHistoryMap::EntryIndex; } -// If @MI is a DBG_VALUE with debug value described by a -// defined register, returns the number of this register. -// In the other case, returns 0. -static Register isDescribedByReg(const MachineInstr &MI) { - assert(MI.isDebugValue()); - assert(MI.getNumOperands() == 4); - // If the location of variable is an entry value (DW_OP_LLVM_entry_value) - // do not consider it as a register location. - if (MI.getDebugExpression()->isEntryValue()) - return 0; - // If location of variable is described using a register (directly or - // indirectly), this register is always a first operand. - return MI.getDebugOperand(0).isReg() ? MI.getDebugOperand(0).getReg() - : Register(); -} - void InstructionOrdering::initialize(const MachineFunction &MF) { // We give meta instructions the same ordinal as the preceding instruction // because this class is written for the task of comparing positions of @@ -316,24 +300,44 @@ } /// Create a clobbering entry and end all open debug value entries -/// for \p Var that are described by \p RegNo using that entry. +/// for \p Var that are described by \p RegNo using that entry. Inserts into \p +/// FellowRegisters the set of Registers that were also used to describe \p Var +/// alongside \p RegNo. static void clobberRegEntries(InlinedEntity Var, unsigned RegNo, const MachineInstr &ClobberingInstr, DbgValueEntriesMap &LiveEntries, - DbgValueHistoryMap &HistMap) { + DbgValueHistoryMap &HistMap, + SmallVectorImpl &FellowRegisters) { EntryIndex ClobberIndex = HistMap.startClobber(Var, ClobberingInstr); - // Close all entries whose values are described by the register. SmallVector IndicesToErase; + // If a given register appears in a live DBG_VALUE_LIST for Var alongside the + // clobbered register, and never appears in a live DBG_VALUE* for Var without + // the clobbered register, then it is no longer linked to the variable. + SmallSet MaybeRemovedRegisters; + SmallSet KeepRegisters; for (auto Index : LiveEntries[Var]) { auto &Entry = HistMap.getEntry(Var, Index); assert(Entry.isDbgValue() && "Not a DBG_VALUE in LiveEntries"); - if (isDescribedByReg(*Entry.getInstr()) == RegNo) { + if (Entry.getInstr()->isDebugEntryValue()) + continue; + if (Entry.getInstr()->hasDebugOperandForReg(RegNo)) { IndicesToErase.push_back(Index); Entry.endEntry(ClobberIndex); + for (auto &MO : Entry.getInstr()->debug_operands()) + if (MO.isReg() && MO.getReg() && MO.getReg() != RegNo) + MaybeRemovedRegisters.insert(MO.getReg()); + } else { + for (auto &MO : Entry.getInstr()->debug_operands()) + if (MO.isReg() && MO.getReg()) + KeepRegisters.insert(MO.getReg()); } } + for (Register Reg : MaybeRemovedRegisters) + if (!KeepRegisters.contains(Reg)) + FellowRegisters.push_back(Reg); + // Drop all entries that have ended. for (auto Index : IndicesToErase) LiveEntries[Var].erase(Index); @@ -361,17 +365,24 @@ IndicesToErase.push_back(Index); Entry.endEntry(NewIndex); } - if (Register Reg = isDescribedByReg(DV)) - TrackedRegs[Reg] |= !Overlaps; + if (!DV.isDebugEntryValue()) + for (const MachineOperand &Op : DV.debug_operands()) + if (Op.isReg() && Op.getReg()) + TrackedRegs[Op.getReg()] |= !Overlaps; } // If the new debug value is described by a register, add tracking of // that register if it is not already tracked. - if (Register NewReg = isDescribedByReg(DV)) { - if (!TrackedRegs.count(NewReg)) - addRegDescribedVar(RegVars, NewReg, Var); - LiveEntries[Var].insert(NewIndex); - TrackedRegs[NewReg] = true; + if (!DV.isDebugEntryValue()) { + for (const MachineOperand &Op : DV.debug_operands()) { + if (Op.isReg() && Op.getReg()) { + Register NewReg = Op.getReg(); + if (!TrackedRegs.count(NewReg)) + addRegDescribedVar(RegVars, NewReg, Var); + LiveEntries[Var].insert(NewIndex); + TrackedRegs[NewReg] = true; + } + } } // Drop tracking of registers that are no longer used. @@ -394,9 +405,16 @@ DbgValueEntriesMap &LiveEntries, const MachineInstr &ClobberingInstr) { // Iterate over all variables described by this register and add this - // instruction to their history, clobbering it. - for (const auto &Var : I->second) - clobberRegEntries(Var, I->first, ClobberingInstr, LiveEntries, HistMap); + // instruction to their history, clobbering it. All registers that also + // describe the clobbered variables (i.e. in variadic debug values) will have + // those Variables removed from their DescribedVars. + for (const auto &Var : I->second) { + SmallVector FellowRegisters; + clobberRegEntries(Var, I->first, ClobberingInstr, LiveEntries, HistMap, + FellowRegisters); + for (Register RegNo : FellowRegisters) + dropRegDescribedVar(RegVars, RegNo, Var); + } RegVars.erase(I); } diff --git a/llvm/lib/CodeGen/AsmPrinter/DebugHandlerBase.cpp b/llvm/lib/CodeGen/AsmPrinter/DebugHandlerBase.cpp --- a/llvm/lib/CodeGen/AsmPrinter/DebugHandlerBase.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/DebugHandlerBase.cpp @@ -35,7 +35,8 @@ DbgVariableLocation::extractFromMachineInstruction( const MachineInstr &Instruction) { DbgVariableLocation Location; - if (!Instruction.isDebugValue()) + // Variables calculated from multiple locations can't be represented here. + if (Instruction.getNumDebugOperands() != 1) return None; if (!Instruction.getDebugOperand(0).isReg()) return None; @@ -46,6 +47,15 @@ int64_t Offset = 0; const DIExpression *DIExpr = Instruction.getDebugExpression(); auto Op = DIExpr->expr_op_begin(); + // We can handle a DBG_VALUE_LIST iff it has exactly one location operand that + // appears exactly once at the start of the expression. + if (Instruction.isDebugValueList()) { + if (Instruction.getNumDebugOperands() == 1 && + Op->getOp() == dwarf::DW_OP_LLVM_arg) + ++Op; + else + return None; + } while (Op != DIExpr->expr_op_end()) { switch (Op->getOp()) { case dwarf::DW_OP_constu: { @@ -261,7 +271,8 @@ continue; auto IsDescribedByReg = [](const MachineInstr *MI) { - return MI->getDebugOperand(0).isReg() && MI->getDebugOperand(0).getReg(); + return any_of(MI->debug_operands(), + [](auto &MO) { return MO.isReg() && MO.getReg(); }); }; // The first mention of a function argument gets the CurrentFnBegin label, diff --git a/llvm/lib/CodeGen/AsmPrinter/DebugLocEntry.h b/llvm/lib/CodeGen/AsmPrinter/DebugLocEntry.h --- a/llvm/lib/CodeGen/AsmPrinter/DebugLocEntry.h +++ b/llvm/lib/CodeGen/AsmPrinter/DebugLocEntry.h @@ -35,9 +35,7 @@ }; /// A single location or constant. -class DbgValueLoc { - /// Any complex address location expression for this DbgValueLoc. - const DIExpression *Expression; +class DbgValueLocEntry { /// Type of entry that this represents. enum EntryType { @@ -64,24 +62,16 @@ }; public: - DbgValueLoc(const DIExpression *Expr, int64_t i) - : Expression(Expr), EntryKind(E_Integer) { - Constant.Int = i; - } - DbgValueLoc(const DIExpression *Expr, const ConstantFP *CFP) - : Expression(Expr), EntryKind(E_ConstantFP) { + DbgValueLocEntry(int64_t i) : EntryKind(E_Integer) { Constant.Int = i; } + DbgValueLocEntry(const ConstantFP *CFP) : EntryKind(E_ConstantFP) { Constant.CFP = CFP; } - DbgValueLoc(const DIExpression *Expr, const ConstantInt *CIP) - : Expression(Expr), EntryKind(E_ConstantInt) { + DbgValueLocEntry(const ConstantInt *CIP) : EntryKind(E_ConstantInt) { Constant.CIP = CIP; } - DbgValueLoc(const DIExpression *Expr, MachineLocation Loc) - : Expression(Expr), EntryKind(E_Location), Loc(Loc) { - assert(cast(Expr)->isValid()); - } - DbgValueLoc(const DIExpression *Expr, TargetIndexLocation Loc) - : Expression(Expr), EntryKind(E_TargetIndexLocation), TIL(Loc) {} + DbgValueLocEntry(MachineLocation Loc) : EntryKind(E_Location), Loc(Loc) {} + DbgValueLocEntry(TargetIndexLocation Loc) + : EntryKind(E_TargetIndexLocation), TIL(Loc) {} bool isLocation() const { return EntryKind == E_Location; } bool isTargetIndexLocation() const { @@ -95,11 +85,7 @@ const ConstantInt *getConstantInt() const { return Constant.CIP; } MachineLocation getLoc() const { return Loc; } TargetIndexLocation getTargetIndexLocation() const { return TIL; } - bool isFragment() const { return getExpression()->isFragment(); } - bool isEntryVal() const { return getExpression()->isEntryValue(); } - const DIExpression *getExpression() const { return Expression; } - friend bool operator==(const DbgValueLoc &, const DbgValueLoc &); - friend bool operator<(const DbgValueLoc &, const DbgValueLoc &); + friend bool operator==(const DbgValueLocEntry &, const DbgValueLocEntry &); #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) LLVM_DUMP_METHOD void dump() const { if (isLocation()) { @@ -111,6 +97,76 @@ Constant.CIP->dump(); else if (isConstantFP()) Constant.CFP->dump(); + } +#endif +}; + +/// The location of a single variable, composed of an expression and 0 or more +/// DbgValueLocEntries. +class DbgValueLoc { + /// Any complex address location expression for this DbgValueLoc. + const DIExpression *Expression; + + SmallVector ValueLocEntries; + + bool IsVariadic; + +public: + DbgValueLoc(const DIExpression *Expr, ArrayRef Locs) + : Expression(Expr), ValueLocEntries(Locs.begin(), Locs.end()), + IsVariadic(true) { +#ifndef NDEBUG + // Currently, DBG_VALUE_VAR expressions must use stack_value. + assert(Expr && Expr->isValid() && + is_contained(Locs, dwarf::DW_OP_stack_value)); + for (DbgValueLocEntry &Entry : ValueLocEntries) { + assert(!Entry.isConstantFP() && !Entry.isConstantInt() && + "Constant values should only be present in non-variadic " + "DBG_VALUEs."); + } +#endif + } + + DbgValueLoc(const DIExpression *Expr, ArrayRef Locs, + bool IsVariadic) + : Expression(Expr), ValueLocEntries(Locs.begin(), Locs.end()), + IsVariadic(IsVariadic) { +#ifndef NDEBUG + assert(cast(Expr)->isValid() || + !any_of(Locs, [](auto LE) { return LE.isLocation(); })); + if (!IsVariadic) { + assert(ValueLocEntries.size() == 1); + } else { + // Currently, DBG_VALUE_VAR expressions must use stack_value. + assert(Expr && Expr->isValid() && + is_contained(Expr->getElements(), dwarf::DW_OP_stack_value)); + for (DbgValueLocEntry &Entry : ValueLocEntries) { + assert(!Entry.isConstantFP() && !Entry.isConstantInt() && + "Constant values should only be present in non-variadic " + "DBG_VALUEs."); + } + } +#endif + } + + DbgValueLoc(const DIExpression *Expr, DbgValueLocEntry Loc) + : Expression(Expr), ValueLocEntries(1, Loc), IsVariadic(false) { + assert(Expr && Expr->isValid() || !Loc.isLocation()); + } + + bool isFragment() const { return getExpression()->isFragment(); } + bool isEntryVal() const { return getExpression()->isEntryValue(); } + bool isVariadic() const { return IsVariadic; } + const DIExpression *getExpression() const { return Expression; } + const ArrayRef getLocEntries() const { + return ValueLocEntries; + } + friend bool operator==(const DbgValueLoc &, const DbgValueLoc &); + friend bool operator<(const DbgValueLoc &, const DbgValueLoc &); +#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) + LLVM_DUMP_METHOD void dump() const { + for (DbgValueLocEntry DV : ValueLocEntries) + DV.dump(); if (Expression) Expression->dump(); } @@ -180,30 +236,32 @@ DwarfCompileUnit &TheCU); }; -/// Compare two DbgValueLocs for equality. -inline bool operator==(const DbgValueLoc &A, - const DbgValueLoc &B) { +/// Compare two DbgValueLocEntries for equality. +inline bool operator==(const DbgValueLocEntry &A, const DbgValueLocEntry &B) { if (A.EntryKind != B.EntryKind) return false; - if (A.Expression != B.Expression) - return false; - switch (A.EntryKind) { - case DbgValueLoc::E_Location: + case DbgValueLocEntry::E_Location: return A.Loc == B.Loc; - case DbgValueLoc::E_TargetIndexLocation: + case DbgValueLocEntry::E_TargetIndexLocation: return A.TIL == B.TIL; - case DbgValueLoc::E_Integer: + case DbgValueLocEntry::E_Integer: return A.Constant.Int == B.Constant.Int; - case DbgValueLoc::E_ConstantFP: + case DbgValueLocEntry::E_ConstantFP: return A.Constant.CFP == B.Constant.CFP; - case DbgValueLoc::E_ConstantInt: + case DbgValueLocEntry::E_ConstantInt: return A.Constant.CIP == B.Constant.CIP; } llvm_unreachable("unhandled EntryKind"); } +/// Compare two DbgValueLocs for equality. +inline bool operator==(const DbgValueLoc &A, const DbgValueLoc &B) { + return A.ValueLocEntries == B.ValueLocEntries && + A.Expression == B.Expression && A.IsVariadic == B.IsVariadic; +} + /// Compare two fragments based on their offset. inline bool operator<(const DbgValueLoc &A, const DbgValueLoc &B) { diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp --- a/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp @@ -698,36 +698,95 @@ // Check if variable has a single location description. if (auto *DVal = DV.getValueLoc()) { - if (DVal->isLocation()) - addVariableAddress(DV, *VariableDie, DVal->getLoc()); - else if (DVal->isInt()) { - auto *Expr = DV.getSingleExpression(); - if (Expr && Expr->getNumElements()) { + if (!DVal->isVariadic()) { + const DbgValueLocEntry *Entry = DVal->getLocEntries().begin(); + if (Entry->isLocation()) { + addVariableAddress(DV, *VariableDie, Entry->getLoc()); + } else if (Entry->isInt()) { + auto *Expr = DV.getSingleExpression(); + if (Expr && Expr->getNumElements()) { + DIELoc *Loc = new (DIEValueAllocator) DIELoc; + DIEDwarfExpression DwarfExpr(*Asm, *this, *Loc); + // If there is an expression, emit raw unsigned bytes. + DwarfExpr.addFragmentOffset(Expr); + DwarfExpr.addUnsignedConstant(Entry->getInt()); + DwarfExpr.addExpression(Expr); + addBlock(*VariableDie, dwarf::DW_AT_location, DwarfExpr.finalize()); + if (DwarfExpr.TagOffset) + addUInt(*VariableDie, dwarf::DW_AT_LLVM_tag_offset, + dwarf::DW_FORM_data1, *DwarfExpr.TagOffset); + } else + addConstantValue(*VariableDie, Entry->getInt(), DV.getType()); + } else if (Entry->isConstantFP()) { + addConstantFPValue(*VariableDie, Entry->getConstantFP()); + } else if (Entry->isConstantInt()) { + addConstantValue(*VariableDie, Entry->getConstantInt(), DV.getType()); + } else if (Entry->isTargetIndexLocation()) { DIELoc *Loc = new (DIEValueAllocator) DIELoc; DIEDwarfExpression DwarfExpr(*Asm, *this, *Loc); - // If there is an expression, emit raw unsigned bytes. - DwarfExpr.addFragmentOffset(Expr); - DwarfExpr.addUnsignedConstant(DVal->getInt()); - DwarfExpr.addExpression(Expr); + const DIBasicType *BT = dyn_cast( + static_cast(DV.getVariable()->getType())); + DwarfDebug::emitDebugLocValue(*Asm, BT, *DVal, DwarfExpr); addBlock(*VariableDie, dwarf::DW_AT_location, DwarfExpr.finalize()); - if (DwarfExpr.TagOffset) - addUInt(*VariableDie, dwarf::DW_AT_LLVM_tag_offset, - dwarf::DW_FORM_data1, *DwarfExpr.TagOffset); - - } else - addConstantValue(*VariableDie, DVal->getInt(), DV.getType()); - } else if (DVal->isConstantFP()) { - addConstantFPValue(*VariableDie, DVal->getConstantFP()); - } else if (DVal->isConstantInt()) { - addConstantValue(*VariableDie, DVal->getConstantInt(), DV.getType()); - } else if (DVal->isTargetIndexLocation()) { - DIELoc *Loc = new (DIEValueAllocator) DIELoc; - DIEDwarfExpression DwarfExpr(*Asm, *this, *Loc); - const DIBasicType *BT = dyn_cast( - static_cast(DV.getVariable()->getType())); - DwarfDebug::emitDebugLocValue(*Asm, BT, *DVal, DwarfExpr); - addBlock(*VariableDie, dwarf::DW_AT_location, DwarfExpr.finalize()); + } + return VariableDie; } + // If any of the location entries are registers with the value 0, then the + // location is undefined. + if (any_of(DVal->getLocEntries(), [](const DbgValueLocEntry &Entry) { + return Entry.isLocation() && !Entry.getLoc().getReg(); + })) + return VariableDie; + const DIExpression *Expr = DV.getSingleExpression(); + assert(Expr && "Variadic Debug Value must have an Expression."); + DIELoc *Loc = new (DIEValueAllocator) DIELoc; + DIEDwarfExpression DwarfExpr(*Asm, *this, *Loc); + DwarfExpr.addFragmentOffset(Expr); + DIExpressionCursor Cursor(Expr); + const TargetRegisterInfo &TRI = *Asm->MF->getSubtarget().getRegisterInfo(); + + // Declare the TargetMachine locally so we don't need to capture `this` in + // the lambda. + TargetMachine &TM = Asm->TM; + auto AddEntry = [&DwarfExpr, &TRI, &TM](const DbgValueLocEntry &Entry, + DIExpressionCursor &Cursor) { + if (Entry.isLocation()) { + if (!DwarfExpr.addMachineRegExpression(TRI, Cursor, + Entry.getLoc().getReg())) + return false; + } else if (Entry.isInt()) { + // If there is an expression, emit raw unsigned bytes. + DwarfExpr.addUnsignedConstant(Entry.getInt()); + } else if (Entry.isConstantFP()) { + APInt RawBytes = Entry.getConstantFP()->getValueAPF().bitcastToAPInt(); + DwarfExpr.addUnsignedConstant(RawBytes); + } else if (Entry.isConstantInt()) { + APInt RawBytes = Entry.getConstantInt()->getValue(); + DwarfExpr.addUnsignedConstant(RawBytes); + } else if (Entry.isTargetIndexLocation()) { + TargetIndexLocation Loc = Entry.getTargetIndexLocation(); + // TODO TargetIndexLocation is a target-independent. Currently only the + // WebAssembly-specific encoding is supported. + assert(TM.getTargetTriple().isWasm()); + DwarfExpr.addWasmLocation(Loc.Index, static_cast(Loc.Offset)); + } else { + llvm_unreachable("Unsupported Entry type."); + } + return true; + }; + + DwarfExpr.addExpression( + std::move(Cursor), + [&AddEntry, &DVal](unsigned Idx, DIExpressionCursor &Cursor) -> bool { + return AddEntry(DVal->getLocEntries()[Idx], Cursor); + }); + + // Now attach the location information to the DIE. + addBlock(*VariableDie, dwarf::DW_AT_location, DwarfExpr.finalize()); + if (DwarfExpr.TagOffset) + addUInt(*VariableDie, dwarf::DW_AT_LLVM_tag_offset, dwarf::DW_FORM_data1, + *DwarfExpr.TagOffset); + return VariableDie; } diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp --- a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp @@ -223,29 +223,27 @@ /// Get .debug_loc entry for the instruction range starting at MI. static DbgValueLoc getDebugLocValue(const MachineInstr *MI) { const DIExpression *Expr = MI->getDebugExpression(); - assert(MI->getNumOperands() == 4); - if (MI->getDebugOperand(0).isReg()) { - const auto &RegOp = MI->getDebugOperand(0); - const auto &Op1 = MI->getDebugOffset(); - // If the second operand is an immediate, this is a - // register-indirect address. - assert((!Op1.isImm() || (Op1.getImm() == 0)) && "unexpected offset"); - MachineLocation MLoc(RegOp.getReg(), Op1.isImm()); - return DbgValueLoc(Expr, MLoc); - } - if (MI->getDebugOperand(0).isTargetIndex()) { - const auto &Op = MI->getDebugOperand(0); - return DbgValueLoc(Expr, - TargetIndexLocation(Op.getIndex(), Op.getOffset())); - } - if (MI->getDebugOperand(0).isImm()) - return DbgValueLoc(Expr, MI->getDebugOperand(0).getImm()); - if (MI->getDebugOperand(0).isFPImm()) - return DbgValueLoc(Expr, MI->getDebugOperand(0).getFPImm()); - if (MI->getDebugOperand(0).isCImm()) - return DbgValueLoc(Expr, MI->getDebugOperand(0).getCImm()); - - llvm_unreachable("Unexpected 4-operand DBG_VALUE instruction!"); + const bool IsVariadic = MI->isDebugValueList(); + assert(MI->getNumOperands() >= 3); + SmallVector DbgValueLocEntries; + for (const MachineOperand &Op : MI->debug_operands()) { + if (Op.isReg()) { + MachineLocation MLoc(Op.getReg(), + MI->isNonListDebugValue() && MI->isDebugOffsetImm()); + DbgValueLocEntries.push_back(DbgValueLocEntry(MLoc)); + } else if (Op.isTargetIndex()) { + DbgValueLocEntries.push_back( + DbgValueLocEntry(TargetIndexLocation(Op.getIndex(), Op.getOffset()))); + } else if (Op.isImm()) + DbgValueLocEntries.push_back(DbgValueLocEntry(Op.getImm())); + else if (Op.isFPImm()) + DbgValueLocEntries.push_back(DbgValueLocEntry(Op.getFPImm())); + else if (Op.isCImm()) + DbgValueLocEntries.push_back(DbgValueLocEntry(Op.getCImm())); + else + llvm_unreachable("Unexpected debug operand in DBG_VALUE* instruction!"); + } + return DbgValueLoc(Expr, DbgValueLocEntries, IsVariadic); } void DbgVariable::initializeDbgValue(const MachineInstr *DbgValue) { @@ -624,7 +622,7 @@ assert((!CombinedExpr || CombinedExpr->isValid()) && "Combined debug expression is invalid"); - DbgValueLoc DbgLocVal(CombinedExpr, Val); + DbgValueLoc DbgLocVal(CombinedExpr, DbgValueLocEntry(Val)); DbgCallSiteParam CSParm(Param.ParamReg, DbgLocVal); Params.push_back(CSParm); ++NumCSParams; @@ -1592,7 +1590,9 @@ // throughout the function. This is a hack, presumably for DWARF v2 and not // necessarily correct. It would be much better to use a dbg.declare instead // if we know the constant is live throughout the scope. - if (DbgValue->getDebugOperand(0).isImm() && MBB->pred_empty()) + if (MBB->pred_empty() && + all_of(DbgValue->debug_operands(), + [](const MachineOperand &Op) { return Op.isImm(); })) return true; // Test if the location terminates before the end of the scope. @@ -2466,51 +2466,95 @@ auto *DIExpr = Value.getExpression(); DIExpressionCursor ExprCursor(DIExpr); DwarfExpr.addFragmentOffset(DIExpr); - // Regular entry. - if (Value.isInt()) { - if (BT && (BT->getEncoding() == dwarf::DW_ATE_signed || - BT->getEncoding() == dwarf::DW_ATE_signed_char)) - DwarfExpr.addSignedConstant(Value.getInt()); - else - DwarfExpr.addUnsignedConstant(Value.getInt()); - } else if (Value.isLocation()) { - MachineLocation Location = Value.getLoc(); + + // If the DIExpr is is an Entry Value, we want to follow the same code path + // regardless of whether the DBG_VALUE is variadic or not. + if (DIExpr && DIExpr->isEntryValue()) { + // Entry values can only be a single register with no additional DIExpr, + // so just add it directly. + assert(Value.getLocEntries().size() == 1); + assert(Value.getLocEntries()[0].isLocation()); + MachineLocation Location = Value.getLocEntries()[0].getLoc(); DwarfExpr.setLocation(Location, DIExpr); - DIExpressionCursor Cursor(DIExpr); - if (DIExpr->isEntryValue()) - DwarfExpr.beginEntryValueExpression(Cursor); + DwarfExpr.beginEntryValueExpression(ExprCursor); const TargetRegisterInfo &TRI = *AP.MF->getSubtarget().getRegisterInfo(); - if (!DwarfExpr.addMachineRegExpression(TRI, Cursor, Location.getReg())) - return; - return DwarfExpr.addExpression(std::move(Cursor)); - } else if (Value.isTargetIndexLocation()) { - TargetIndexLocation Loc = Value.getTargetIndexLocation(); - // TODO TargetIndexLocation is a target-independent. Currently only the WebAssembly-specific - // encoding is supported. - assert(AP.TM.getTargetTriple().isWasm()); - DwarfExpr.addWasmLocation(Loc.Index, static_cast(Loc.Offset)); - DwarfExpr.addExpression(std::move(ExprCursor)); - return; - } else if (Value.isConstantFP()) { - if (AP.getDwarfVersion() >= 4 && !AP.getDwarfDebug()->tuneForSCE() && - !ExprCursor) { - DwarfExpr.addConstantFP(Value.getConstantFP()->getValueAPF(), AP); + if (!DwarfExpr.addMachineRegExpression(TRI, ExprCursor, Location.getReg())) return; + return DwarfExpr.addExpression(std::move(ExprCursor)); + } + + // Regular entry. + auto EmitValueLocEntry = [&DwarfExpr, &BT, + &AP](const DbgValueLocEntry &Entry, + DIExpressionCursor &Cursor) -> bool { + if (Entry.isInt()) { + if (BT && (BT->getEncoding() == dwarf::DW_ATE_signed || + BT->getEncoding() == dwarf::DW_ATE_signed_char)) + DwarfExpr.addSignedConstant(Entry.getInt()); + else + DwarfExpr.addUnsignedConstant(Entry.getInt()); + } else if (Entry.isLocation()) { + MachineLocation Location = Entry.getLoc(); + if (Location.isIndirect()) + DwarfExpr.setMemoryLocationKind(); + + const TargetRegisterInfo &TRI = *AP.MF->getSubtarget().getRegisterInfo(); + if (!DwarfExpr.addMachineRegExpression(TRI, Cursor, Location.getReg())) + return false; + } else if (Entry.isTargetIndexLocation()) { + TargetIndexLocation Loc = Entry.getTargetIndexLocation(); + // TODO TargetIndexLocation is a target-independent. Currently only the + // WebAssembly-specific encoding is supported. + assert(AP.TM.getTargetTriple().isWasm()); + DwarfExpr.addWasmLocation(Loc.Index, static_cast(Loc.Offset)); + } else if (Entry.isConstantFP()) { + if (AP.getDwarfVersion() >= 4 && !AP.getDwarfDebug()->tuneForSCE() && + !Cursor) { + DwarfExpr.addConstantFP(Entry.getConstantFP()->getValueAPF(), AP); + } else if (Entry.getConstantFP() + ->getValueAPF() + .bitcastToAPInt() + .getBitWidth() <= 64 /*bits*/) { + DwarfExpr.addUnsignedConstant( + Entry.getConstantFP()->getValueAPF().bitcastToAPInt()); + } else { + LLVM_DEBUG( + dbgs() << "Skipped DwarfExpression creation for ConstantFP of size" + << Entry.getConstantFP() + ->getValueAPF() + .bitcastToAPInt() + .getBitWidth() + << " bits\n"); + return false; + } + } else { + llvm_unreachable("Invalid Entry for a DW_AT_location expression."); } - if (Value.getConstantFP()->getValueAPF().bitcastToAPInt().getBitWidth() <= - 64 /*bits*/) - DwarfExpr.addUnsignedConstant( - Value.getConstantFP()->getValueAPF().bitcastToAPInt()); - else - LLVM_DEBUG( - dbgs() - << "Skipped DwarfExpression creation for ConstantFP of size" - << Value.getConstantFP()->getValueAPF().bitcastToAPInt().getBitWidth() - << " bits\n"); - } - DwarfExpr.addExpression(std::move(ExprCursor)); + return true; + }; + + if (!Value.isVariadic()) { + if (!EmitValueLocEntry(Value.getLocEntries()[0], ExprCursor)) + return; + DwarfExpr.addExpression(std::move(ExprCursor)); + return; + } + + // If any of the location entries are registers with the value 0, then the + // location is undefined. + if (any_of(Value.getLocEntries(), [](const DbgValueLocEntry &Entry) { + return Entry.isLocation() && !Entry.getLoc().getReg(); + })) + return; + + DwarfExpr.addExpression( + std::move(ExprCursor), + [EmitValueLocEntry, &Value](unsigned Idx, + DIExpressionCursor &Cursor) -> bool { + return EmitValueLocEntry(Value.getLocEntries()[Idx], Cursor); + }); } void DebugLocEntry::finalize(const AsmPrinter &AP, diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfExpression.h b/llvm/lib/CodeGen/AsmPrinter/DwarfExpression.h --- a/llvm/lib/CodeGen/AsmPrinter/DwarfExpression.h +++ b/llvm/lib/CodeGen/AsmPrinter/DwarfExpression.h @@ -346,6 +346,9 @@ /// fragment inside the entire variable. void addExpression(DIExpressionCursor &&Expr, unsigned FragmentOffsetInBits = 0); + void + addExpression(DIExpressionCursor &&Expr, + std::function InsertArg); /// If applicable, emit an empty DW_OP_piece / DW_OP_bit_piece to advance to /// the fragment described by \c Expr. diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfExpression.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfExpression.cpp --- a/llvm/lib/CodeGen/AsmPrinter/DwarfExpression.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/DwarfExpression.cpp @@ -353,6 +353,14 @@ else addBReg(Reg.DwarfRegNo, SignedOffset); DwarfRegs.clear(); + + // If we need to mask out a subregister, do it now, unless the next + // operation would emit an OpPiece anyway. + auto NextOp = ExprCursor.peek(); + if (SubRegisterSizeInBits && NextOp && + (NextOp->getOp() != dwarf::DW_OP_LLVM_fragment)) + maskSubRegister(); + return true; } @@ -451,16 +459,19 @@ void DwarfExpression::addExpression(DIExpressionCursor &&ExprCursor, unsigned FragmentOffsetInBits) { + addExpression(std::move(ExprCursor), + [](unsigned Idx, DIExpressionCursor &Cursor) -> bool { + llvm_unreachable("unhandled opcode found in expression"); + }); +} + +void DwarfExpression::addExpression( + DIExpressionCursor &&ExprCursor, + std::function InsertArg) { // Entry values can currently only cover the initial register location, // and not any other parts of the following DWARF expression. assert(!IsEmittingEntryValue && "Can't emit entry value around expression"); - // If we need to mask out a subregister, do it now, unless the next - // operation would emit an OpPiece anyway. - auto N = ExprCursor.peek(); - if (SubRegisterSizeInBits && N && (N->getOp() != dwarf::DW_OP_LLVM_fragment)) - maskSubRegister(); - Optional PrevConvertOp = None; while (ExprCursor) { @@ -476,6 +487,12 @@ } switch (OpNum) { + case dwarf::DW_OP_LLVM_arg: + if (!InsertArg(Op->getArg(0), ExprCursor)) { + LocationKind = Unknown; + return; + } + break; case dwarf::DW_OP_LLVM_fragment: { unsigned SizeInBits = Op->getArg(1); unsigned FragmentOffset = Op->getArg(0); diff --git a/llvm/lib/CodeGen/CodeGenPrepare.cpp b/llvm/lib/CodeGen/CodeGenPrepare.cpp --- a/llvm/lib/CodeGen/CodeGenPrepare.cpp +++ b/llvm/lib/CodeGen/CodeGenPrepare.cpp @@ -2775,11 +2775,16 @@ /// Keep track of the debug users. SmallVector DbgValues; + /// Keep track of the new value so that we can undo it by replacing + /// instances of the new value with the original value. + Value *New; + using use_iterator = SmallVectorImpl::iterator; public: /// Replace all the use of \p Inst by \p New. - UsesReplacer(Instruction *Inst, Value *New) : TypePromotionAction(Inst) { + UsesReplacer(Instruction *Inst, Value *New) + : TypePromotionAction(Inst), New(New) { LLVM_DEBUG(dbgs() << "Do: UsersReplacer: " << *Inst << " with " << *New << "\n"); // Record the original uses. @@ -2807,11 +2812,8 @@ // including the debug uses. Since we are undoing the replacements, // the original debug uses must also be reinstated to maintain the // correctness and utility of debug value instructions. - for (auto *DVI: DbgValues) { - LLVMContext &Ctx = Inst->getType()->getContext(); - auto *MV = MetadataAsValue::get(Ctx, ValueAsMetadata::get(Inst)); - DVI->setOperand(0, MV); - } + for (auto *DVI : DbgValues) + DVI->replaceVariableLocationOp(New, Inst); } }; @@ -7733,19 +7735,21 @@ DbgValueInst &DVI = *cast(I); // Does this dbg.value refer to a sunk address calculation? - Value *Location = DVI.getVariableLocation(); - WeakTrackingVH SunkAddrVH = SunkAddrs[Location]; - Value *SunkAddr = SunkAddrVH.pointsToAliveValue() ? SunkAddrVH : nullptr; - if (SunkAddr) { - // Point dbg.value at locally computed address, which should give the best - // opportunity to be accurately lowered. This update may change the type of - // pointer being referred to; however this makes no difference to debugging - // information, and we can't generate bitcasts that may affect codegen. - DVI.setOperand(0, MetadataAsValue::get(DVI.getContext(), - ValueAsMetadata::get(SunkAddr))); - return true; - } - return false; + bool AnyChange = false; + for (Value *Location : DVI.getValues()) { + WeakTrackingVH SunkAddrVH = SunkAddrs[Location]; + Value *SunkAddr = SunkAddrVH.pointsToAliveValue() ? SunkAddrVH : nullptr; + if (SunkAddr) { + // Point dbg.value at locally computed address, which should give the best + // opportunity to be accurately lowered. This update may change the type + // of pointer being referred to; however this makes no difference to + // debugging information, and we can't generate bitcasts that may affect + // codegen. + DVI.replaceVariableLocationOp(Location, SunkAddr); + AnyChange = true; + } + } + return AnyChange; } // A llvm.dbg.value may be using a value before its definition, due to @@ -7764,30 +7768,51 @@ if (!DVI) continue; - Instruction *VI = dyn_cast_or_null(DVI->getValue()); - - if (!VI || VI->isTerminator()) - continue; - - // If VI is a phi in a block with an EHPad terminator, we can't insert - // after it. - if (isa(VI) && VI->getParent()->getTerminator()->isEHPad()) - continue; - - // If the defining instruction dominates the dbg.value, we do not need - // to move the dbg.value. - if (DT.dominates(VI, DVI)) - continue; - - LLVM_DEBUG(dbgs() << "Moving Debug Value before :\n" - << *DVI << ' ' << *VI); - DVI->removeFromParent(); - if (isa(VI)) - DVI->insertBefore(&*VI->getParent()->getFirstInsertionPt()); - else - DVI->insertAfter(VI); - MadeChange = true; - ++NumDbgValueMoved; + SmallVector VIs; + for (Value *V : DVI->getValues()) + if (Instruction *VI = dyn_cast_or_null(V)) + VIs.push_back(VI); + + // This DVI may depend on multiple instructions, complicating any + // potential sink. This block takes the defensive approach, opting to + // "undef" the DVI if it has more than one instruction and any of them do + // not dominate DVI. + for (Instruction *VI : VIs) { + if (VI->isTerminator()) + continue; + + // If VI is a phi in a block with an EHPad terminator, we can't insert + // after it. + if (isa(VI) && VI->getParent()->getTerminator()->isEHPad()) + continue; + + // If the defining instruction dominates the dbg.value, we do not need + // to move the dbg.value. + if (DT.dominates(VI, DVI)) + continue; + + // If we depend on multiple instructions and any of them doesn't + // dominate this DVI, we probably can't salvage it: moving it to + // after any of the instructions could cause us to lose the others. + if (VIs.size() > 1) { + LLVM_DEBUG( + dbgs() + << "Unable to find valid location for Debug Value, undefing:\n" + << *DVI); + DVI->setUndef(); + break; + } + + LLVM_DEBUG(dbgs() << "Moving Debug Value before :\n" + << *DVI << ' ' << *VI); + DVI->removeFromParent(); + if (isa(VI)) + DVI->insertBefore(&*VI->getParent()->getFirstInsertionPt()); + else + DVI->insertAfter(VI); + MadeChange = true; + ++NumDbgValueMoved; + } } } return MadeChange; diff --git a/llvm/lib/CodeGen/InlineSpiller.cpp b/llvm/lib/CodeGen/InlineSpiller.cpp --- a/llvm/lib/CodeGen/InlineSpiller.cpp +++ b/llvm/lib/CodeGen/InlineSpiller.cpp @@ -1044,7 +1044,7 @@ // Modify DBG_VALUE now that the value is in a spill slot. MachineBasicBlock *MBB = MI->getParent(); LLVM_DEBUG(dbgs() << "Modifying debug info due to spill:\t" << *MI); - buildDbgValueForSpill(*MBB, MI, *MI, StackSlot); + buildDbgValueForSpill(*MBB, MI, *MI, StackSlot, Reg); MBB->erase(MI); continue; } diff --git a/llvm/lib/CodeGen/LiveDebugValues/VarLocBasedImpl.cpp b/llvm/lib/CodeGen/LiveDebugValues/VarLocBasedImpl.cpp --- a/llvm/lib/CodeGen/LiveDebugValues/VarLocBasedImpl.cpp +++ b/llvm/lib/CodeGen/LiveDebugValues/VarLocBasedImpl.cpp @@ -76,20 +76,23 @@ /// that are not through dataflow. /// /// Within LiveDebugValues: each variable location is represented by a -/// VarLoc object that identifies the source variable, its current -/// machine-location, and the DBG_VALUE inst that specifies the location. Each -/// VarLoc is indexed in the (function-scope) \p VarLocMap, giving each VarLoc a -/// unique index. Rather than operate directly on machine locations, the -/// dataflow analysis in this pass identifies locations by their index in the -/// VarLocMap, meaning all the variable locations in a block can be described -/// by a sparse vector of VarLocMap indicies. +/// VarLoc object that identifies the source variable, the set of +/// machine-locations that currently describe it (a single location for +/// DBG_VALUE or multiple for DBG_VALUE_LIST), and the DBG_VALUE inst that +/// specifies the location. Each VarLoc is indexed in the (function-scope) \p +/// VarLocMap, giving each VarLoc a set of unique indexes, each of which +/// corresponds to one of the VarLoc's machine-locations and can be used to +/// lookup the VarLoc in the VarLocMap. Rather than operate directly on machine +/// locations, the dataflow analysis in this pass identifies locations by their +/// indices in the VarLocMap, meaning all the variable locations in a block can +/// be described by a sparse vector of VarLocMap indicies. /// /// All the storage for the dataflow analysis is local to the ExtendRanges /// method and passed down to helper methods. "OutLocs" and "InLocs" record the /// in and out lattice values for each block. "OpenRanges" maintains a list of /// variable locations and, with the "process" method, evaluates the transfer -/// function of each block. "flushPendingLocs" installs DBG_VALUEs for each -/// live-in location at the start of blocks, while "Transfers" records +/// function of each block. "flushPendingLocs" installs debug value instructions +/// for each live-in location at the start of blocks, while "Transfers" records /// transfers of values between machine-locations. /// /// We avoid explicitly representing the "Unknown" (\top) lattice value in the @@ -175,17 +178,6 @@ "Maximum input DBG_VALUE insts supported by debug range extension"), cl::init(50000), cl::Hidden); -// If @MI is a DBG_VALUE with debug value described by a defined -// register, returns the number of this register. In the other case, returns 0. -static Register isDbgValueDescribedByReg(const MachineInstr &MI) { - assert(MI.isDebugValue() && "expected a DBG_VALUE"); - assert(MI.getNumOperands() == 4 && "malformed DBG_VALUE"); - // If location of variable is described using a register (directly - // or indirectly), this register is always a first operand. - return MI.getDebugOperand(0).isReg() ? MI.getDebugOperand(0).getReg() - : Register(); -} - /// If \p Op is a stack or frame register return true, otherwise return false. /// This is used to avoid basing the debug entry values on the registers, since /// we do not support it at the moment. @@ -210,6 +202,12 @@ // this prevents fallback to std::set::count() operations. using DefinedRegsSet = SmallSet; +// The IDs in this set correspond to MachineLocs in VarLocs, as well as VarLocs +// that represent Entry Values; every VarLoc in the set will also appear +// exactly once at Location=0. +// As a result, each VarLoc may appear more than once in this set, but each +// range corresponding to a Reg, SpillLoc, or EntryValue type will still be a +// true set, and the range Location=0 is the set of all VarLocs. using VarLocSet = CoalescingBitVector; /// A type-checked pair of {Register Location (or 0), Index}, used to index @@ -272,6 +270,13 @@ } }; +// Simple Set for storing all the VarLoc Indices at a Location bucket. +using VarLocsInRange = SmallSet; +// Vector of all `LocIndex`s for a given VarLoc; the same Location should not +// appear in any two of these, as each VarLoc appears at most once in any +// Location bucket. +using LocIndices = SmallVector; + class VarLocBasedLDV : public LDVImpl { private: const TargetRegisterInfo *TRI; @@ -312,51 +317,126 @@ /// is moved. const MachineInstr &MI; - enum VarLocKind { + enum class MachineLocKind { InvalidKind = 0, RegisterKind, SpillLocKind, - ImmediateKind, + ImmediateKind + }; + + enum class EntryValueLocKind { + NonEntryValueKind = 0, EntryValueKind, EntryValueBackupKind, EntryValueCopyBackupKind - } Kind = InvalidKind; + } EVKind; /// The value location. Stored separately to avoid repeatedly /// extracting it from MI. - union LocUnion { + union MachineLocValue { uint64_t RegNo; SpillLoc SpillLocation; uint64_t Hash; int64_t Immediate; const ConstantFP *FPImm; const ConstantInt *CImm; - LocUnion() : Hash(0) {} - } Loc; + MachineLocValue() : Hash(0) {} + }; + + /// A single machine location; its Kind is either a register, spill + /// location, or immediate value. + struct MachineLoc { + MachineLocKind Kind; + MachineLocValue Value; + bool operator==(const MachineLoc &Other) const { + if (Kind != Other.Kind) + return false; + switch (Kind) { + case MachineLocKind::SpillLocKind: + return Value.SpillLocation == Other.Value.SpillLocation; + case MachineLocKind::RegisterKind: + case MachineLocKind::ImmediateKind: + return Value.Hash == Other.Value.Hash; + default: + llvm_unreachable("Invalid kind"); + } + } + bool operator<(const MachineLoc &Other) const { + switch (Kind) { + case MachineLocKind::SpillLocKind: + return std::make_tuple( + Kind, Value.SpillLocation.SpillBase, + Value.SpillLocation.SpillOffset.getFixed(), + Value.SpillLocation.SpillOffset.getScalable()) < + std::make_tuple( + Other.Kind, Other.Value.SpillLocation.SpillBase, + Other.Value.SpillLocation.SpillOffset.getFixed(), + Other.Value.SpillLocation.SpillOffset.getScalable()); + case MachineLocKind::RegisterKind: + case MachineLocKind::ImmediateKind: + return std::tie(Kind, Value.Hash) < + std::tie(Other.Kind, Other.Value.Hash); + default: + llvm_unreachable("Invalid kind"); + } + } + }; + + /// The set of machine locations used to determine the variable's value, in + /// conjunction with Expr. Initially populated with MI's debug operands, + /// but maybe modified independently afterwards. + SmallVector Locs; + /// Used to map the index of each location in Locs back to the index of its + /// original debug operand in MI. + SmallVector OrigLocMap; VarLoc(const MachineInstr &MI, LexicalScopes &LS) : Var(MI.getDebugVariable(), MI.getDebugExpression(), MI.getDebugLoc()->getInlinedAt()), - Expr(MI.getDebugExpression()), MI(MI) { + Expr(MI.getDebugExpression()), MI(MI), + EVKind(EntryValueLocKind::NonEntryValueKind) { assert(MI.isDebugValue() && "not a DBG_VALUE"); - assert(MI.getNumOperands() == 4 && "malformed DBG_VALUE"); - if (int RegNo = isDbgValueDescribedByReg(MI)) { - Kind = RegisterKind; - Loc.RegNo = RegNo; - } else if (MI.getDebugOperand(0).isImm()) { - Kind = ImmediateKind; - Loc.Immediate = MI.getDebugOperand(0).getImm(); - } else if (MI.getDebugOperand(0).isFPImm()) { - Kind = ImmediateKind; - Loc.FPImm = MI.getDebugOperand(0).getFPImm(); - } else if (MI.getDebugOperand(0).isCImm()) { - Kind = ImmediateKind; - Loc.CImm = MI.getDebugOperand(0).getCImm(); + assert(MI.isDebugValueList() || + MI.getNumOperands() == 4 && "malformed DBG_VALUE"); + for (const MachineOperand &Op : MI.debug_operands()) { + MachineLoc ML = GetLocForOp(Op); + auto It = find(Locs, ML); + if (It == Locs.end()) { + Locs.push_back(ML); + OrigLocMap.push_back(MI.getDebugOperandIndex(&Op)); + } else { + // ML duplicates an element in Locs; replace references to Op + // with references to the duplicating element. + unsigned OpIdx = Locs.size(); + unsigned DuplicatingIdx = std::distance(Locs.begin(), It); + Expr = DIExpression::replaceArg(Expr, OpIdx, DuplicatingIdx); + } } - // We create the debug entry values from the factory functions rather than - // from this ctor. - assert(Kind != EntryValueKind && !isEntryBackupLoc()); + // We create the debug entry values from the factory functions rather + // than from this ctor. + assert(EVKind != EntryValueLocKind::EntryValueKind && + !isEntryBackupLoc()); + } + + static MachineLoc GetLocForOp(const MachineOperand &Op) { + MachineLocKind Kind; + MachineLocValue Loc; + if (Op.isReg()) { + Kind = MachineLocKind::RegisterKind; + Loc.RegNo = Op.getReg(); + } else if (Op.isImm()) { + Kind = MachineLocKind::ImmediateKind; + Loc.Immediate = Op.getImm(); + } else if (Op.isFPImm()) { + Kind = MachineLocKind::ImmediateKind; + Loc.FPImm = Op.getFPImm(); + } else if (Op.isCImm()) { + Kind = MachineLocKind::ImmediateKind; + Loc.CImm = Op.getCImm(); + } else + llvm_unreachable("Invalid Op kind for MachineLoc."); + return {Kind, Loc}; } /// Take the variable and machine-location in DBG_VALUE MI, and build an @@ -364,10 +444,11 @@ static VarLoc CreateEntryLoc(const MachineInstr &MI, LexicalScopes &LS, const DIExpression *EntryExpr, Register Reg) { VarLoc VL(MI, LS); - assert(VL.Kind == RegisterKind); - VL.Kind = EntryValueKind; + assert(VL.Locs.size() == 1 && + VL.Locs[0].Kind == MachineLocKind::RegisterKind); + VL.EVKind = EntryValueLocKind::EntryValueKind; VL.Expr = EntryExpr; - VL.Loc.RegNo = Reg; + VL.Locs[0].Value.RegNo = Reg; return VL; } @@ -379,8 +460,9 @@ LexicalScopes &LS, const DIExpression *EntryExpr) { VarLoc VL(MI, LS); - assert(VL.Kind == RegisterKind); - VL.Kind = EntryValueBackupKind; + assert(VL.Locs.size() == 1 && + VL.Locs[0].Kind == MachineLocKind::RegisterKind); + VL.EVKind = EntryValueLocKind::EntryValueBackupKind; VL.Expr = EntryExpr; return VL; } @@ -393,32 +475,40 @@ const DIExpression *EntryExpr, Register NewReg) { VarLoc VL(MI, LS); - assert(VL.Kind == RegisterKind); - VL.Kind = EntryValueCopyBackupKind; + assert(VL.Locs.size() == 1 && + VL.Locs[0].Kind == MachineLocKind::RegisterKind); + VL.EVKind = EntryValueLocKind::EntryValueCopyBackupKind; VL.Expr = EntryExpr; - VL.Loc.RegNo = NewReg; + VL.Locs[0].Value.RegNo = NewReg; return VL; } /// Copy the register location in DBG_VALUE MI, updating the register to /// be NewReg. - static VarLoc CreateCopyLoc(const MachineInstr &MI, LexicalScopes &LS, + static VarLoc CreateCopyLoc(const VarLoc &OldVL, const MachineLoc &OldML, Register NewReg) { - VarLoc VL(MI, LS); - assert(VL.Kind == RegisterKind); - VL.Loc.RegNo = NewReg; - return VL; + VarLoc VL = OldVL; + for (size_t I = 0, E = VL.Locs.size(); I < E; ++I) + if (VL.Locs[I] == OldML) { + VL.Locs[I].Kind = MachineLocKind::RegisterKind; + VL.Locs[I].Value.RegNo = NewReg; + return VL; + } + llvm_unreachable("Should have found OldML in new VarLoc."); } - /// Take the variable described by DBG_VALUE MI, and create a VarLoc + /// Take the variable described by DBG_VALUE* MI, and create a VarLoc /// locating it in the specified spill location. - static VarLoc CreateSpillLoc(const MachineInstr &MI, unsigned SpillBase, - StackOffset SpillOffset, LexicalScopes &LS) { - VarLoc VL(MI, LS); - assert(VL.Kind == RegisterKind); - VL.Kind = SpillLocKind; - VL.Loc.SpillLocation = {SpillBase, SpillOffset}; - return VL; + static VarLoc CreateSpillLoc(const VarLoc &OldVL, const MachineLoc &OldML, + unsigned SpillBase, StackOffset SpillOffset) { + VarLoc VL = OldVL; + for (int I = 0, E = VL.Locs.size(); I < E; ++I) + if (VL.Locs[I] == OldML) { + VL.Locs[I].Kind = MachineLocKind::SpillLocKind; + VL.Locs[I].Value.SpillLocation = {SpillBase, SpillOffset}; + return VL; + } + llvm_unreachable("Should have found OldML in new VarLoc."); } /// Create a DBG_VALUE representing this VarLoc in the given function. @@ -426,79 +516,143 @@ /// inlining information from the original DBG_VALUE instruction, which may /// have been several transfers ago. MachineInstr *BuildDbgValue(MachineFunction &MF) const { + assert(!isEntryBackupLoc() && + "Tried to produce DBG_VALUE for backup VarLoc"); const DebugLoc &DbgLoc = MI.getDebugLoc(); bool Indirect = MI.isIndirectDebugValue(); const auto &IID = MI.getDesc(); const DILocalVariable *Var = MI.getDebugVariable(); - const DIExpression *DIExpr = MI.getDebugExpression(); NumInserted++; - switch (Kind) { - case EntryValueKind: - // An entry value is a register location -- but with an updated - // expression. The register location of such DBG_VALUE is always the one - // from the entry DBG_VALUE, it does not matter if the entry value was - // copied in to another register due to some optimizations. - return BuildMI(MF, DbgLoc, IID, Indirect, - MI.getDebugOperand(0).getReg(), Var, Expr); - case RegisterKind: - // Register locations are like the source DBG_VALUE, but with the - // register number from this VarLoc. - return BuildMI(MF, DbgLoc, IID, Indirect, Loc.RegNo, Var, DIExpr); - case SpillLocKind: { - // Spills are indirect DBG_VALUEs, with a base register and offset. - // Use the original DBG_VALUEs expression to build the spilt location - // on top of. FIXME: spill locations created before this pass runs - // are not recognized, and not handled here. - auto *TRI = MF.getSubtarget().getRegisterInfo(); - auto *SpillExpr = TRI->prependOffsetExpression( - DIExpr, DIExpression::ApplyOffset, Loc.SpillLocation.SpillOffset); - unsigned Base = Loc.SpillLocation.SpillBase; - return BuildMI(MF, DbgLoc, IID, true, Base, Var, SpillExpr); - } - case ImmediateKind: { - MachineOperand MO = MI.getDebugOperand(0); - return BuildMI(MF, DbgLoc, IID, Indirect, MO, Var, DIExpr); - } - case EntryValueBackupKind: - case EntryValueCopyBackupKind: - case InvalidKind: - llvm_unreachable( - "Tried to produce DBG_VALUE for invalid or backup VarLoc"); + const DIExpression *DIExpr = Expr; + SmallVector MOs; + for (unsigned I = 0, E = Locs.size(); I < E; ++I) { + MachineLocKind LocKind = Locs[I].Kind; + MachineLocValue Loc = Locs[I].Value; + const MachineOperand &Orig = MI.getDebugOperand(OrigLocMap[I]); + switch (LocKind) { + case MachineLocKind::RegisterKind: + // An entry value is a register location -- but with an updated + // expression. The register location of such DBG_VALUE is always the + // one from the entry DBG_VALUE, it does not matter if the entry value + // was copied in to another register due to some optimizations. + // Non-entry value register locations are like the source + // DBG_VALUE, but with the register number from this VarLoc. + MOs.push_back(MachineOperand::CreateReg( + EVKind == EntryValueLocKind::EntryValueKind ? Orig.getReg() + : Register(Loc.RegNo), + false)); + MOs.back().setIsDebug(); + break; + case MachineLocKind::SpillLocKind: { + // Spills are indirect DBG_VALUEs, with a base register and offset. + // Use the original DBG_VALUEs expression to build the spilt location + // on top of. FIXME: spill locations created before this pass runs + // are not recognized, and not handled here. + unsigned Base = Loc.SpillLocation.SpillBase; + auto *TRI = MF.getSubtarget().getRegisterInfo(); + if (MI.isNonListDebugValue()) { + DIExpr = + TRI->prependOffsetExpression(DIExpr, DIExpression::ApplyOffset, + Loc.SpillLocation.SpillOffset); + Indirect = true; + } else { + SmallVector Ops; + TRI->getOffsetOpcodes(Loc.SpillLocation.SpillOffset, Ops); + Ops.push_back(dwarf::DW_OP_deref); + DIExpr = DIExpression::appendOpsToArg(DIExpr, Ops, I); + } + MOs.push_back(MachineOperand::CreateReg(Base, false)); + MOs.back().setIsDebug(); + break; + } + case MachineLocKind::ImmediateKind: { + MOs.push_back(Orig); + break; + } + case MachineLocKind::InvalidKind: + llvm_unreachable("Tried to produce DBG_VALUE for invalid VarLoc"); + } } - llvm_unreachable("Unrecognized VarLocBasedLDV.VarLoc.Kind enum"); + return BuildMI(MF, DbgLoc, IID, Indirect, MOs, Var, DIExpr); } /// Is the Loc field a constant or constant object? - bool isConstant() const { return Kind == ImmediateKind; } + bool isConstant(MachineLocKind Kind) const { + return Kind == MachineLocKind::ImmediateKind; + } /// Check if the Loc field is an entry backup location. bool isEntryBackupLoc() const { - return Kind == EntryValueBackupKind || Kind == EntryValueCopyBackupKind; + return EVKind == EntryValueLocKind::EntryValueBackupKind || + EVKind == EntryValueLocKind::EntryValueCopyBackupKind; + } + + /// If this variable is described by register \p Reg holding the entry + /// value, return true. + bool isEntryValueBackupReg(Register Reg) const { + return EVKind == EntryValueLocKind::EntryValueBackupKind && usesReg(Reg); + } + + /// If this variable is described by register \p Reg holding a copy of the + /// entry value, return true. + bool isEntryValueCopyBackupReg(Register Reg) const { + return EVKind == EntryValueLocKind::EntryValueCopyBackupKind && + usesReg(Reg); + } + + /// If this variable is described in whole or part by \p Reg, return true. + bool usesReg(Register Reg) const { + MachineLoc RegML; + RegML.Kind = MachineLocKind::RegisterKind; + RegML.Value.RegNo = Reg; + return is_contained(Locs, RegML); + } + + /// If this variable is described in whole or part by \p Reg, return true. + unsigned getRegIdx(Register Reg) const { + for (unsigned Idx = 0; Idx < Locs.size(); ++Idx) + if (Locs[Idx].Kind == MachineLocKind::RegisterKind && + Locs[Idx].Value.RegNo == Reg) + return Idx; + llvm_unreachable("Could not find given Reg in Locs"); + } + + /// If this variable is described in whole or part by 1 or more registers, + /// add each of them to \p Regs and return true. + bool getDescribingRegs(SmallVectorImpl &Regs) const { + bool AnyRegs = false; + for (auto Loc : Locs) + if (Loc.Kind == MachineLocKind::RegisterKind) { + Regs.push_back(Loc.Value.RegNo); + AnyRegs = true; + } + return AnyRegs; } - /// If this variable is described by a register holding the entry value, - /// return it, otherwise return 0. - unsigned getEntryValueBackupReg() const { - if (Kind == EntryValueBackupKind) - return Loc.RegNo; - return 0; + bool containsSpillLocs() const { + return any_of(Locs, [](VarLoc::MachineLoc ML) { + return ML.Kind == VarLoc::MachineLocKind::SpillLocKind; + }); } - /// If this variable is described by a register holding the copy of the - /// entry value, return it, otherwise return 0. - unsigned getEntryValueCopyBackupReg() const { - if (Kind == EntryValueCopyBackupKind) - return Loc.RegNo; - return 0; + /// If this variable is described in whole or part by \p SpillLocation, + /// return true. + bool usesSpillLoc(SpillLoc SpillLocation) const { + MachineLoc SpillML; + SpillML.Kind = MachineLocKind::SpillLocKind; + SpillML.Value.SpillLocation = SpillLocation; + return is_contained(Locs, SpillML); } - /// If this variable is described by a register, return it, - /// otherwise return 0. - unsigned isDescribedByReg() const { - if (Kind == RegisterKind) - return Loc.RegNo; - return 0; + /// If this variable is described in whole or part by \p SpillLocation, + /// return the index . + unsigned getSpillLocIdx(SpillLoc SpillLocation) const { + for (unsigned Idx = 0; Idx < Locs.size(); ++Idx) + if (Locs[Idx].Kind == MachineLocKind::SpillLocKind && + Locs[Idx].Value.SpillLocation == SpillLocation) + return Idx; + llvm_unreachable("Could not find given SpillLoc in Locs"); } /// Determine whether the lexical scope of this value's debug location @@ -511,24 +665,26 @@ // TRI can be null. void dump(const TargetRegisterInfo *TRI, raw_ostream &Out = dbgs()) const { Out << "VarLoc("; - switch (Kind) { - case RegisterKind: - case EntryValueKind: - case EntryValueBackupKind: - case EntryValueCopyBackupKind: - Out << printReg(Loc.RegNo, TRI); - break; - case SpillLocKind: - Out << printReg(Loc.SpillLocation.SpillBase, TRI); - Out << "[" << Loc.SpillLocation.SpillOffset.getFixed() << " + " - << Loc.SpillLocation.SpillOffset.getScalable() << "x vscale" - << "]"; - break; - case ImmediateKind: - Out << Loc.Immediate; - break; - case InvalidKind: - llvm_unreachable("Invalid VarLoc in dump method"); + for (const MachineLoc &MLoc : Locs) { + if (Locs.begin() != &MLoc) + Out << ", "; + switch (MLoc.Kind) { + case MachineLocKind::RegisterKind: + Out << printReg(MLoc.Value.RegNo, TRI); + break; + case MachineLocKind::SpillLocKind: + Out << printReg(MLoc.Value.SpillLocation.SpillBase, TRI); + Out << "[" << MLoc.Value.SpillLocation.SpillOffset.getFixed() << " + " + << MLoc.Value.SpillLocation.SpillOffset.getScalable() + << "x vscale" + << "]"; + break; + case MachineLocKind::ImmediateKind: + Out << MLoc.Value.Immediate; + break; + case MachineLocKind::InvalidKind: + llvm_unreachable("Invalid VarLoc in dump method"); + } } Out << ", \"" << Var.getVariable()->getName() << "\", " << *Expr << ", "; @@ -545,90 +701,71 @@ #endif bool operator==(const VarLoc &Other) const { - if (Kind != Other.Kind || !(Var == Other.Var) || Expr != Other.Expr) - return false; - - switch (Kind) { - case SpillLocKind: - return Loc.SpillLocation == Other.Loc.SpillLocation; - case RegisterKind: - case ImmediateKind: - case EntryValueKind: - case EntryValueBackupKind: - case EntryValueCopyBackupKind: - return Loc.Hash == Other.Loc.Hash; - default: - llvm_unreachable("Invalid kind"); - } + return EVKind == Other.EVKind && Var == Other.Var && Expr == Other.Expr && + Locs == Other.Locs; } /// This operator guarantees that VarLocs are sorted by Variable first. bool operator<(const VarLoc &Other) const { - switch (Kind) { - case SpillLocKind: - return std::make_tuple(Var, Kind, Loc.SpillLocation.SpillBase, - Loc.SpillLocation.SpillOffset.getFixed(), - Loc.SpillLocation.SpillOffset.getScalable(), - Expr) < - std::make_tuple( - Other.Var, Other.Kind, Other.Loc.SpillLocation.SpillBase, - Other.Loc.SpillLocation.SpillOffset.getFixed(), - Other.Loc.SpillLocation.SpillOffset.getScalable(), - Other.Expr); - case RegisterKind: - case ImmediateKind: - case EntryValueKind: - case EntryValueBackupKind: - case EntryValueCopyBackupKind: - return std::tie(Var, Kind, Loc.Hash, Expr) < - std::tie(Other.Var, Other.Kind, Other.Loc.Hash, Other.Expr); - default: - llvm_unreachable("Invalid kind"); - } + return std::tie(Var, EVKind, Locs, Expr) < + std::tie(Other.Var, Other.EVKind, Other.Locs, Other.Expr); } }; + using VarVec = SmallVector; + /// VarLocMap is used for two things: - /// 1) Assigning a unique LocIndex to a VarLoc. This LocIndex can be used to + /// 1) Assigning LocIndices to a VarLoc. The LocIndices can be used to /// virtually insert a VarLoc into a VarLocSet. /// 2) Given a LocIndex, look up the unique associated VarLoc. class VarLocMap { /// Map a VarLoc to an index within the vector reserved for its location /// within Loc2Vars. - std::map Var2Index; + std::map Var2Indices; /// Map a location to a vector which holds VarLocs which live in that /// location. SmallDenseMap> Loc2Vars; - /// Determine the 32-bit location reserved for \p VL, based on its kind. - static LocIndex::u32_location_t getLocationForVar(const VarLoc &VL) { - switch (VL.Kind) { - case VarLoc::RegisterKind: - assert((VL.Loc.RegNo < LocIndex::kFirstInvalidRegLocation) && - "Physreg out of range?"); - return VL.Loc.RegNo; - case VarLoc::SpillLocKind: - return LocIndex::kSpillLocation; - case VarLoc::EntryValueBackupKind: - case VarLoc::EntryValueCopyBackupKind: - return LocIndex::kEntryValueBackupLocation; - default: - return 0; - } - } - public: - /// Retrieve a unique LocIndex for \p VL. - LocIndex insert(const VarLoc &VL) { - LocIndex::u32_location_t Location = getLocationForVar(VL); - LocIndex::u32_index_t &Index = Var2Index[VL]; - if (!Index) { + /// Retrieve LocIndices for \p VL. + LocIndices insert(const VarLoc &VL) { + LocIndices &Indices = Var2Indices[VL]; + // If Indices is not empty, VL is already in the map. + if (!Indices.empty()) + return Indices; + SmallVector Locations; + // LocIndices are determined by EVKind and MLs; each Register has a + // unique location, while all SpillLocs use a single bucket, and any EV + // VarLocs use only the Backup bucket or none at all (except Location 0). + // LocIndices will always have an index at Location=0 as the last index. + if (VL.EVKind == VarLoc::EntryValueLocKind::NonEntryValueKind) { + VL.getDescribingRegs(Locations); + for (auto RegNo : Locations) + assert((RegNo < LocIndex::kFirstInvalidRegLocation) && + "Physreg out of range?"); + if (VL.containsSpillLocs()) { + LocIndex::u32_location_t Loc = LocIndex::kSpillLocation; + Locations.push_back(Loc); + } + } else if (VL.EVKind != VarLoc::EntryValueLocKind::EntryValueKind) { + LocIndex::u32_location_t Loc = LocIndex::kEntryValueBackupLocation; + Locations.push_back(Loc); + } + Locations.push_back(0); + for (LocIndex::u32_location_t Location : Locations) { auto &Vars = Loc2Vars[Location]; + Indices.push_back( + {Location, static_cast(Vars.size())}); Vars.push_back(VL); - Index = Vars.size(); } - return {Location, Index - 1}; + return Indices; + } + + LocIndices getAllIndices(const VarLoc &VL) const { + auto IndIt = Var2Indices.find(VL); + assert(IndIt != Var2Indices.end() && "VarLoc not tracked"); + return IndIt->second; } /// Retrieve the unique VarLoc associated with \p ID. @@ -660,6 +797,15 @@ using VarToFragments = DenseMap>; + static void collectVarLocsForReg(SmallVectorImpl &Collected, + uint32_t Reg, const VarLocSet &CollectFrom, + const VarLocMap &VarLocIDs); + + /// Get the registers which are used by VarLocs of kind RegisterKind tracked + /// by \p CollectFrom. + void getUsedRegs(const VarLocSet &CollectFrom, + SmallVectorImpl &UsedRegs) const; + /// This holds the working set of currently open ranges. For fast /// access, this is done both as a set of VarLocIDs, and a map of /// DebugVariable to recent VarLocID. Note that a DBG_VALUE ends all @@ -670,39 +816,44 @@ /// we will erase/insert from the EntryValuesBackupVars map, otherwise /// we perform the operation on the Vars. class OpenRangesSet { + VarLocSet::Allocator &Alloc; VarLocSet VarLocs; // Map the DebugVariable to recent primary location ID. - SmallDenseMap Vars; + SmallDenseMap Vars; // Map the DebugVariable to recent backup location ID. - SmallDenseMap EntryValuesBackupVars; + SmallDenseMap EntryValuesBackupVars; OverlapMap &OverlappingFragments; public: OpenRangesSet(VarLocSet::Allocator &Alloc, OverlapMap &_OLapMap) - : VarLocs(Alloc), OverlappingFragments(_OLapMap) {} + : Alloc(Alloc), VarLocs(Alloc), OverlappingFragments(_OLapMap) {} const VarLocSet &getVarLocs() const { return VarLocs; } + // Fetches all VarLocs in \p VarLocIDs and inserts them into \p Collected. + // This method is needed to get every VarLoc once, as each VarLoc may have + // multiple indices in a VarLocMap (corresponding to each applicable + // location), but all VarLocs appear exactly once at Location 0. + void getUniqueVarLocs(SmallVectorImpl &Collected, + const VarLocMap &VarLocIDs) const { + collectVarLocsForReg(Collected, 0, VarLocs, VarLocIDs); + } + /// Terminate all open ranges for VL.Var by removing it from the set. void erase(const VarLoc &VL); - /// Terminate all open ranges listed in \c KillSet by removing - /// them from the set. - void erase(const VarLocSet &KillSet, const VarLocMap &VarLocIDs); + /// Terminate all open ranges listed as indices in \c KillSet with + /// \c Location (default 0) by removing them from the set. + void erase(const VarLocsInRange &KillSet, const VarLocMap &VarLocIDs, + LocIndex::u32_location_t Location = 0); /// Insert a new range into the set. - void insert(LocIndex VarLocID, const VarLoc &VL); + void insert(LocIndices VarLocIDs, const VarLoc &VL); /// Insert a set of ranges. - void insertFromLocSet(const VarLocSet &ToLoad, const VarLocMap &Map) { - for (uint64_t ID : ToLoad) { - LocIndex Idx = LocIndex::fromRawInteger(ID); - const VarLoc &VarL = Map[Idx]; - insert(Idx, VarL); - } - } + void insertFromLocSet(const VarLocSet &ToLoad, const VarLocMap &Map); - llvm::Optional getEntryValueBackup(DebugVariable Var); + llvm::Optional getEntryValueBackup(DebugVariable Var); /// Empty the set. void clear() { @@ -725,18 +876,18 @@ getVarLocs().end()); } - /// Get all set IDs for VarLocs of kind RegisterKind in \p Reg. + /// Get all set IDs for VarLocs with MLs of kind RegisterKind in \p Reg. auto getRegisterVarLocs(Register Reg) const { return LocIndex::indexRangeForLocation(getVarLocs(), Reg); } - /// Get all set IDs for VarLocs of kind SpillLocKind. + /// Get all set IDs for VarLocs with MLs of kind SpillLocKind. auto getSpillVarLocs() const { return LocIndex::indexRangeForLocation(getVarLocs(), LocIndex::kSpillLocation); } - /// Get all set IDs for VarLocs of kind EntryValueBackupKind or + /// Get all set IDs for VarLocs of EVKind EntryValueBackupKind or /// EntryValueCopyBackupKind. auto getEntryValueBackupVarLocs() const { return LocIndex::indexRangeForLocation( @@ -744,16 +895,13 @@ } }; - /// Collect all VarLoc IDs from \p CollectFrom for VarLocs of kind - /// RegisterKind which are located in any reg in \p Regs. Insert collected IDs - /// into \p Collected. - void collectIDsForRegs(VarLocSet &Collected, const DefinedRegsSet &Regs, - const VarLocSet &CollectFrom) const; - - /// Get the registers which are used by VarLocs of kind RegisterKind tracked - /// by \p CollectFrom. - void getUsedRegs(const VarLocSet &CollectFrom, - SmallVectorImpl &UsedRegs) const; + /// Collect all VarLoc IDs from \p CollectFrom for VarLocs with MLs of kind + /// RegisterKind which are located in any reg in \p Regs. Insert collected + /// IDs into \p Collected. + static void collectIDsForRegs(VarLocsInRange &Collected, + const DefinedRegsSet &Regs, + const VarLocSet &CollectFrom, + const VarLocMap &VarLocIDs); VarLocSet &getVarLocsInMBB(const MachineBasicBlock *MBB, VarLocInMBB &Locs) { std::unique_ptr &VLS = Locs[MBB]; @@ -800,6 +948,7 @@ void insertTransferDebugPair(MachineInstr &MI, OpenRangesSet &OpenRanges, TransferMap &Transfers, VarLocMap &VarLocIDs, LocIndex OldVarID, TransferKind Kind, + const VarLoc::MachineLoc &OldLoc, Register NewReg = Register()); void transferDebugValue(const MachineInstr &MI, OpenRangesSet &OpenRanges, @@ -810,7 +959,7 @@ VarLocMap &VarLocIDs, const VarLoc &EntryVL); void emitEntryValues(MachineInstr &MI, OpenRangesSet &OpenRanges, VarLocMap &VarLocIDs, TransferMap &Transfers, - VarLocSet &KillSet); + VarLocsInRange &KillSet); void recordEntryValue(const MachineInstr &MI, const DefinedRegsSet &DefinedRegs, OpenRangesSet &OpenRanges, VarLocMap &VarLocIDs); @@ -871,8 +1020,9 @@ auto *EraseFrom = VL.isEntryBackupLoc() ? &EntryValuesBackupVars : &Vars; auto It = EraseFrom->find(VarToErase); if (It != EraseFrom->end()) { - LocIndex ID = It->second; - VarLocs.reset(ID.getAsRawInteger()); + LocIndices IDs = It->second; + for (LocIndex ID : IDs) + VarLocs.reset(ID.getAsRawInteger()); EraseFrom->erase(It); } }; @@ -899,26 +1049,46 @@ } } -void VarLocBasedLDV::OpenRangesSet::erase(const VarLocSet &KillSet, - const VarLocMap &VarLocIDs) { - VarLocs.intersectWithComplement(KillSet); - for (uint64_t ID : KillSet) { - const VarLoc *VL = &VarLocIDs[LocIndex::fromRawInteger(ID)]; - auto *EraseFrom = VL->isEntryBackupLoc() ? &EntryValuesBackupVars : &Vars; - EraseFrom->erase(VL->Var); +void VarLocBasedLDV::OpenRangesSet::erase(const VarLocsInRange &KillSet, + const VarLocMap &VarLocIDs, + LocIndex::u32_location_t Location) { + VarLocSet RemoveSet(Alloc); + for (uint32_t ID : KillSet) { + const VarLoc &VL = VarLocIDs[LocIndex(Location, ID)]; + auto *EraseFrom = VL.isEntryBackupLoc() ? &EntryValuesBackupVars : &Vars; + EraseFrom->erase(VL.Var); + LocIndices VLI = VarLocIDs.getAllIndices(VL); + for (LocIndex ID : VLI) + RemoveSet.set(ID.getAsRawInteger()); + } + VarLocs.intersectWithComplement(RemoveSet); +} + +void VarLocBasedLDV::OpenRangesSet::insertFromLocSet(const VarLocSet &ToLoad, + const VarLocMap &Map) { + VarLocsInRange UniqueVarLocIDs; + DefinedRegsSet Regs; + Regs.insert(0); + collectIDsForRegs(UniqueVarLocIDs, Regs, ToLoad, Map); + for (uint64_t ID : UniqueVarLocIDs) { + LocIndex Idx = LocIndex::fromRawInteger(ID); + const VarLoc &VarL = Map[Idx]; + const LocIndices Indices = Map.getAllIndices(VarL); + insert(Indices, VarL); } } -void VarLocBasedLDV::OpenRangesSet::insert(LocIndex VarLocID, - const VarLoc &VL) { +void VarLocBasedLDV::OpenRangesSet::insert(LocIndices VarLocIDs, + const VarLoc &VL) { auto *InsertInto = VL.isEntryBackupLoc() ? &EntryValuesBackupVars : &Vars; - VarLocs.set(VarLocID.getAsRawInteger()); - InsertInto->insert({VL.Var, VarLocID}); + for (LocIndex ID : VarLocIDs) + VarLocs.set(ID.getAsRawInteger()); + InsertInto->insert({VL.Var, VarLocIDs}); } /// Return the Loc ID of an entry value backup location, if it exists for the /// variable. -llvm::Optional +llvm::Optional VarLocBasedLDV::OpenRangesSet::getEntryValueBackup(DebugVariable Var) { auto It = EntryValuesBackupVars.find(Var); if (It != EntryValuesBackupVars.end()) @@ -927,9 +1097,10 @@ return llvm::None; } -void VarLocBasedLDV::collectIDsForRegs(VarLocSet &Collected, - const DefinedRegsSet &Regs, - const VarLocSet &CollectFrom) const { +void VarLocBasedLDV::collectIDsForRegs(VarLocsInRange &Collected, + const DefinedRegsSet &Regs, + const VarLocSet &CollectFrom, + const VarLocMap &VarLocIDs) { assert(!Regs.empty() && "Nothing to collect"); SmallVector SortedRegs; for (Register Reg : Regs) @@ -938,15 +1109,21 @@ auto It = CollectFrom.find(LocIndex::rawIndexForReg(SortedRegs.front())); auto End = CollectFrom.end(); for (uint32_t Reg : SortedRegs) { - // The half-open interval [FirstIndexForReg, FirstInvalidIndex) contains all - // possible VarLoc IDs for VarLocs of kind RegisterKind which live in Reg. + // The half-open interval [FirstIndexForReg, FirstInvalidIndex) contains + // all possible VarLoc IDs for VarLocs with MLs of kind RegisterKind which + // live in Reg. uint64_t FirstIndexForReg = LocIndex::rawIndexForReg(Reg); uint64_t FirstInvalidIndex = LocIndex::rawIndexForReg(Reg + 1); It.advanceToLowerBound(FirstIndexForReg); // Iterate through that half-open interval and collect all the set IDs. - for (; It != End && *It < FirstInvalidIndex; ++It) - Collected.set(*It); + for (; It != End && *It < FirstInvalidIndex; ++It) { + LocIndex ItIdx = LocIndex::fromRawInteger(*It); + const VarLoc &VL = VarLocIDs[ItIdx]; + LocIndices LI = VarLocIDs.getAllIndices(VL); + // For now, the back index is always Loc=0 + Collected.insert(LI.back().Index); + } if (It == End) return; @@ -995,9 +1172,10 @@ const VarLocSet &L = getVarLocsInMBB(&BB, V); if (L.empty()) continue; + SmallVector VarLocs; + collectVarLocsForReg(VarLocs, 0, L, VarLocIDs); Out << "MBB: " << BB.getNumber() << ":\n"; - for (uint64_t VLL : L) { - const VarLoc &VL = VarLocIDs[LocIndex::fromRawInteger(VLL)]; + for (const VarLoc &VL : VarLocs) { Out << " Var: " << VL.Var.getVariable()->getName(); Out << " MI: "; VL.dump(TRI, Out); @@ -1044,32 +1222,30 @@ // If the DBG_VALUE comes from a copy instruction that copies the entry value, // it means the parameter's value has not changed and we should be able to use // its entry value. - bool TrySalvageEntryValue = false; Register Reg = MI.getDebugOperand(0).getReg(); auto I = std::next(MI.getReverseIterator()); const MachineOperand *SrcRegOp, *DestRegOp; - if (I != MI.getParent()->rend()) { - // TODO: Try to keep tracking of an entry value if we encounter a propagated - // DBG_VALUE describing the copy of the entry value. (Propagated entry value - // does not indicate the parameter modification.) - auto DestSrc = TII->isCopyInstr(*I); - if (!DestSrc) - return true; - - SrcRegOp = DestSrc->Source; - DestRegOp = DestSrc->Destination; - if (Reg != DestRegOp->getReg()) - return true; - TrySalvageEntryValue = true; - } - - if (TrySalvageEntryValue) { - for (uint64_t ID : OpenRanges.getEntryValueBackupVarLocs()) { - const VarLoc &VL = VarLocIDs[LocIndex::fromRawInteger(ID)]; - if (VL.getEntryValueCopyBackupReg() == Reg && - VL.MI.getDebugOperand(0).getReg() == SrcRegOp->getReg()) - return false; - } + if (I == MI.getParent()->rend()) + return true; + + // TODO: Try to keep tracking of an entry value if we encounter a propagated + // DBG_VALUE describing the copy of the entry value. (Propagated entry value + // does not indicate the parameter modification.) + auto DestSrc = TII->isCopyInstr(*I); + if (!DestSrc) + return true; + + SrcRegOp = DestSrc->Source; + DestRegOp = DestSrc->Destination; + if (Reg != DestRegOp->getReg()) + return true; + + for (uint64_t ID : OpenRanges.getEntryValueBackupVarLocs()) { + const VarLoc &VL = VarLocIDs[LocIndex::fromRawInteger(ID)]; + if (VL.isEntryValueCopyBackupReg(Reg) && + // Entry Values should not be variadic. + VL.MI.getDebugOperand(0).getReg() == SrcRegOp->getReg()) + return false; } return true; @@ -1095,7 +1271,7 @@ // If that is the case, we should stop tracking its entry value. auto EntryValBackupID = OpenRanges.getEntryValueBackup(V); if (Var->isParameter() && EntryValBackupID) { - const VarLoc &EntryVL = VarLocIDs[*EntryValBackupID]; + const VarLoc &EntryVL = VarLocIDs[EntryValBackupID->back()]; if (removeEntryValue(MI, OpenRanges, VarLocIDs, EntryVL)) { LLVM_DEBUG(dbgs() << "Deleting a DBG entry value because of: "; MI.print(dbgs(), /*IsStandalone*/ false, @@ -1105,59 +1281,78 @@ } } - if (isDbgValueDescribedByReg(MI) || MI.getDebugOperand(0).isImm() || - MI.getDebugOperand(0).isFPImm() || MI.getDebugOperand(0).isCImm()) { + if (all_of(MI.debug_operands(), [](const MachineOperand &MO) { + return (MO.isReg() && MO.getReg()) || MO.isImm() || MO.isFPImm() || + MO.isCImm(); + })) { // Use normal VarLoc constructor for registers and immediates. VarLoc VL(MI, LS); // End all previous ranges of VL.Var. OpenRanges.erase(VL); - LocIndex ID = VarLocIDs.insert(VL); + LocIndices IDs = VarLocIDs.insert(VL); // Add the VarLoc to OpenRanges from this DBG_VALUE. - OpenRanges.insert(ID, VL); - } else if (MI.hasOneMemOperand()) { + OpenRanges.insert(IDs, VL); + } else if (MI.memoperands().size() > 0) { llvm_unreachable("DBG_VALUE with mem operand encountered after regalloc?"); } else { // This must be an undefined location. If it has an open range, erase it. - assert(MI.getDebugOperand(0).isReg() && - MI.getDebugOperand(0).getReg() == 0 && + assert(MI.isUndefDebugValue() && "Unexpected non-undef DBG_VALUE encountered"); VarLoc VL(MI, LS); OpenRanges.erase(VL); } } +// This should be removed later, doesn't fit the new design. +void VarLocBasedLDV::collectVarLocsForReg(SmallVectorImpl &Collected, + uint32_t Reg, + const VarLocSet &CollectFrom, + const VarLocMap &VarLocIDs) { + // The half-open interval [FirstIndexForReg, FirstInvalidIndex) contains all + // possible VarLoc IDs for VarLocs with MLs of kind RegisterKind which live + // in Reg. + uint64_t FirstIndexForReg = LocIndex::rawIndexForReg(Reg); + uint64_t FirstInvalidIndex = LocIndex::rawIndexForReg(Reg + 1); + // Iterate through that half-open interval and collect all the set IDs. + for (auto It = CollectFrom.find(FirstIndexForReg), End = CollectFrom.end(); + It != End && *It < FirstInvalidIndex; ++It) { + LocIndex RegIdx = LocIndex::fromRawInteger(*It); + Collected.push_back(VarLocIDs[RegIdx]); + } +} + /// Turn the entry value backup locations into primary locations. void VarLocBasedLDV::emitEntryValues(MachineInstr &MI, - OpenRangesSet &OpenRanges, - VarLocMap &VarLocIDs, - TransferMap &Transfers, - VarLocSet &KillSet) { + OpenRangesSet &OpenRanges, + VarLocMap &VarLocIDs, + TransferMap &Transfers, + VarLocsInRange &KillSet) { // Do not insert entry value locations after a terminator. if (MI.isTerminator()) return; - for (uint64_t ID : KillSet) { - LocIndex Idx = LocIndex::fromRawInteger(ID); + for (LocIndex::u32_index_t ID : KillSet) { + LocIndex Idx = LocIndex(0, ID); const VarLoc &VL = VarLocIDs[Idx]; if (!VL.Var.getVariable()->isParameter()) continue; auto DebugVar = VL.Var; - Optional EntryValBackupID = + Optional EntryValBackupIDs = OpenRanges.getEntryValueBackup(DebugVar); // If the parameter has the entry value backup, it means we should // be able to use its entry value. - if (!EntryValBackupID) + if (!EntryValBackupIDs) continue; - const VarLoc &EntryVL = VarLocIDs[*EntryValBackupID]; - VarLoc EntryLoc = - VarLoc::CreateEntryLoc(EntryVL.MI, LS, EntryVL.Expr, EntryVL.Loc.RegNo); - LocIndex EntryValueID = VarLocIDs.insert(EntryLoc); - Transfers.push_back({&MI, EntryValueID}); - OpenRanges.insert(EntryValueID, EntryLoc); + const VarLoc &EntryVL = VarLocIDs[EntryValBackupIDs->back()]; + VarLoc EntryLoc = VarLoc::CreateEntryLoc(EntryVL.MI, LS, EntryVL.Expr, + EntryVL.Locs[0].Value.RegNo); + LocIndices EntryValueIDs = VarLocIDs.insert(EntryLoc); + Transfers.push_back({&MI, EntryValueIDs.back()}); + OpenRanges.insert(EntryValueIDs, EntryLoc); } } @@ -1169,20 +1364,20 @@ void VarLocBasedLDV::insertTransferDebugPair( MachineInstr &MI, OpenRangesSet &OpenRanges, TransferMap &Transfers, VarLocMap &VarLocIDs, LocIndex OldVarID, TransferKind Kind, - Register NewReg) { - const MachineInstr *DebugInstr = &VarLocIDs[OldVarID].MI; + const VarLoc::MachineLoc &OldLoc, Register NewReg) { + const VarLoc &OldVarLoc = VarLocIDs[OldVarID]; auto ProcessVarLoc = [&MI, &OpenRanges, &Transfers, &VarLocIDs](VarLoc &VL) { - LocIndex LocId = VarLocIDs.insert(VL); + LocIndices LocIds = VarLocIDs.insert(VL); // Close this variable's previous location range. OpenRanges.erase(VL); // Record the new location as an open range, and a postponed transfer // inserting a DBG_VALUE for this location. - OpenRanges.insert(LocId, VL); + OpenRanges.insert(LocIds, VL); assert(!MI.isTerminator() && "Cannot insert DBG_VALUE after terminator"); - TransferDebugPair MIP = {&MI, LocId}; + TransferDebugPair MIP = {&MI, LocIds.back()}; Transfers.push_back(MIP); }; @@ -1194,7 +1389,7 @@ "No register supplied when handling a copy of a debug value"); // Create a DBG_VALUE instruction to describe the Var in its new // register location. - VarLoc VL = VarLoc::CreateCopyLoc(*DebugInstr, LS, NewReg); + VarLoc VL = VarLoc::CreateCopyLoc(OldVarLoc, OldLoc, NewReg); ProcessVarLoc(VL); LLVM_DEBUG({ dbgs() << "Creating VarLoc for register copy:"; @@ -1206,8 +1401,8 @@ // Create a DBG_VALUE instruction to describe the Var in its spilled // location. VarLoc::SpillLoc SpillLocation = extractSpillBaseRegAndOffset(MI); - VarLoc VL = VarLoc::CreateSpillLoc(*DebugInstr, SpillLocation.SpillBase, - SpillLocation.SpillOffset, LS); + VarLoc VL = VarLoc::CreateSpillLoc( + OldVarLoc, OldLoc, SpillLocation.SpillBase, SpillLocation.SpillOffset); ProcessVarLoc(VL); LLVM_DEBUG({ dbgs() << "Creating VarLoc for spill:"; @@ -1220,7 +1415,7 @@ "No register supplied when handling a restore of a debug value"); // DebugInstr refers to the pre-spill location, therefore we can reuse // its expression. - VarLoc VL = VarLoc::CreateCopyLoc(*DebugInstr, LS, NewReg); + VarLoc VL = VarLoc::CreateCopyLoc(OldVarLoc, OldLoc, NewReg); ProcessVarLoc(VL); LLVM_DEBUG({ dbgs() << "Creating VarLoc for restore:"; @@ -1290,8 +1485,8 @@ if (DeadRegs.empty()) return; - VarLocSet KillSet(Alloc); - collectIDsForRegs(KillSet, DeadRegs, OpenRanges.getVarLocs()); + VarLocsInRange KillSet; + collectIDsForRegs(KillSet, DeadRegs, OpenRanges.getVarLocs(), VarLocIDs); OpenRanges.erase(KillSet, VarLocIDs); if (TPC) { @@ -1390,14 +1585,14 @@ // First, if there are any DBG_VALUEs pointing at a spill slot that is // written to, then close the variable location. The value in memory // will have changed. - VarLocSet KillSet(Alloc); + VarLocsInRange KillSet; if (isSpillInstruction(MI, MF)) { Loc = extractSpillBaseRegAndOffset(MI); for (uint64_t ID : OpenRanges.getSpillVarLocs()) { LocIndex Idx = LocIndex::fromRawInteger(ID); const VarLoc &VL = VarLocIDs[Idx]; - assert(VL.Kind == VarLoc::SpillLocKind && "Broken VarLocSet?"); - if (VL.Loc.SpillLocation == *Loc) { + assert(VL.containsSpillLocs() && "Broken VarLocSet?"); + if (VL.usesSpillLoc(*Loc)) { // This location is overwritten by the current instruction -- terminate // the open range, and insert an explicit DBG_VALUE $noreg. // @@ -1408,13 +1603,15 @@ // // At this stage, we already know which DBG_VALUEs are for spills and // where they are located; it's best to fix handle overwrites now. - KillSet.set(ID); - VarLoc UndefVL = VarLoc::CreateCopyLoc(VL.MI, LS, 0); - LocIndex UndefLocID = VarLocIDs.insert(UndefVL); - Transfers.push_back({&MI, UndefLocID}); + KillSet.insert(ID); + unsigned SpillLocIdx = VL.getSpillLocIdx(*Loc); + VarLoc::MachineLoc OldLoc = VL.Locs[SpillLocIdx]; + VarLoc UndefVL = VarLoc::CreateCopyLoc(VL, OldLoc, 0); + LocIndices UndefLocIDs = VarLocIDs.insert(UndefVL); + Transfers.push_back({&MI, UndefLocIDs.back()}); } } - OpenRanges.erase(KillSet, VarLocIDs); + OpenRanges.erase(KillSet, VarLocIDs, LocIndex::kSpillLocation); } // Try to recognise spill and restore instructions that may create a new @@ -1441,21 +1638,25 @@ for (uint64_t ID : TransferCandidates) { LocIndex Idx = LocIndex::fromRawInteger(ID); const VarLoc &VL = VarLocIDs[Idx]; + unsigned LocIdx; if (TKind == TransferKind::TransferSpill) { - assert(VL.isDescribedByReg() == Reg && "Broken VarLocSet?"); + assert(VL.usesReg(Reg) && "Broken VarLocSet?"); LLVM_DEBUG(dbgs() << "Spilling Register " << printReg(Reg, TRI) << '(' << VL.Var.getVariable()->getName() << ")\n"); + LocIdx = VL.getRegIdx(Reg); } else { - assert(TKind == TransferKind::TransferRestore && - VL.Kind == VarLoc::SpillLocKind && "Broken VarLocSet?"); - if (VL.Loc.SpillLocation != *Loc) + assert(TKind == TransferKind::TransferRestore && VL.containsSpillLocs() && + "Broken VarLocSet?"); + if (!VL.usesSpillLoc(*Loc)) // The spill location is not the location of a debug value. continue; LLVM_DEBUG(dbgs() << "Restoring Register " << printReg(Reg, TRI) << '(' << VL.Var.getVariable()->getName() << ")\n"); + LocIdx = VL.getSpillLocIdx(*Loc); } + VarLoc::MachineLoc MLoc = VL.Locs[LocIdx]; insertTransferDebugPair(MI, OpenRanges, Transfers, VarLocIDs, Idx, TKind, - Reg); + MLoc, Reg); // FIXME: A comment should explain why it's correct to return early here, // if that is in fact correct. return; @@ -1504,17 +1705,16 @@ for (uint64_t ID : OpenRanges.getEntryValueBackupVarLocs()) { LocIndex Idx = LocIndex::fromRawInteger(ID); const VarLoc &VL = VarLocIDs[Idx]; - if (VL.getEntryValueBackupReg() == SrcReg) { + if (VL.isEntryValueBackupReg(SrcReg)) { LLVM_DEBUG(dbgs() << "Copy of the entry value: "; MI.dump();); VarLoc EntryValLocCopyBackup = VarLoc::CreateEntryCopyBackupLoc(VL.MI, LS, VL.Expr, DestReg); - // Stop tracking the original entry value. OpenRanges.erase(VL); // Start tracking the entry value copy. - LocIndex EntryValCopyLocID = VarLocIDs.insert(EntryValLocCopyBackup); - OpenRanges.insert(EntryValCopyLocID, EntryValLocCopyBackup); + LocIndices EntryValCopyLocIDs = VarLocIDs.insert(EntryValLocCopyBackup); + OpenRanges.insert(EntryValCopyLocIDs, EntryValLocCopyBackup); break; } } @@ -1525,9 +1725,12 @@ for (uint64_t ID : OpenRanges.getRegisterVarLocs(SrcReg)) { LocIndex Idx = LocIndex::fromRawInteger(ID); - assert(VarLocIDs[Idx].isDescribedByReg() == SrcReg && "Broken VarLocSet?"); + assert(VarLocIDs[Idx].usesReg(SrcReg) && "Broken VarLocSet?"); + VarLoc::MachineLocValue Loc; + Loc.RegNo = SrcReg; + VarLoc::MachineLoc MLoc{VarLoc::MachineLocKind::RegisterKind, Loc}; insertTransferDebugPair(MI, OpenRanges, Transfers, VarLocIDs, Idx, - TransferKind::TransferCopy, DestReg); + TransferKind::TransferCopy, MLoc, DestReg); // FIXME: A comment should explain why it's correct to return early here, // if that is in fact correct. return; @@ -1540,12 +1743,14 @@ VarLocInMBB &OutLocs, const VarLocMap &VarLocIDs) { bool Changed = false; - - LLVM_DEBUG(for (uint64_t ID - : OpenRanges.getVarLocs()) { - // Copy OpenRanges to OutLocs, if not already present. - dbgs() << "Add to OutLocs in MBB #" << CurMBB->getNumber() << ": "; - VarLocIDs[LocIndex::fromRawInteger(ID)].dump(TRI); + LLVM_DEBUG({ + VarVec VarLocs; + OpenRanges.getUniqueVarLocs(VarLocs, VarLocIDs); + for (VarLoc &VL : VarLocs) { + // Copy OpenRanges to OutLocs, if not already present. + dbgs() << "Add to OutLocs in MBB #" << CurMBB->getNumber() << ": "; + VL.dump(TRI); + } }); VarLocSet &VLS = getVarLocsInMBB(CurMBB, OutLocs); Changed = VLS != OpenRanges.getVarLocs(); @@ -1668,12 +1873,11 @@ LLVM_DEBUG({ if (!InLocsT.empty()) { - for (uint64_t ID : InLocsT) + VarVec VarLocs; + collectVarLocsForReg(VarLocs, 0, InLocsT, VarLocIDs); + for (const VarLoc &VL : VarLocs) dbgs() << " gathered candidate incoming var: " - << VarLocIDs[LocIndex::fromRawInteger(ID)] - .Var.getVariable() - ->getName() - << "\n"; + << VL.Var.getVariable()->getName() << "\n"; } }); @@ -1722,10 +1926,12 @@ auto &MBB = const_cast(*Iter.first); VarLocSet &Pending = *Iter.second.get(); - for (uint64_t ID : Pending) { + SmallVector VarLocs; + collectVarLocsForReg(VarLocs, 0, Pending, VarLocIDs); + + for (VarLoc DiffIt : VarLocs) { // The ID location is live-in to MBB -- work out what kind of machine // location it is and create a DBG_VALUE. - const VarLoc &DiffIt = VarLocIDs[LocIndex::fromRawInteger(ID)]; if (DiffIt.isEntryBackupLoc()) continue; MachineInstr *MI = DiffIt.BuildDbgValue(*MBB.getParent()); @@ -1810,8 +2016,8 @@ DIExpression *NewExpr = DIExpression::prepend(MI.getDebugExpression(), DIExpression::EntryValue); VarLoc EntryValLocAsBackup = VarLoc::CreateEntryBackupLoc(MI, LS, NewExpr); - LocIndex EntryValLocID = VarLocIDs.insert(EntryValLocAsBackup); - OpenRanges.insert(EntryValLocID, EntryValLocAsBackup); + LocIndices EntryValLocIDs = VarLocIDs.insert(EntryValLocAsBackup); + OpenRanges.insert(EntryValLocIDs, EntryValLocAsBackup); } /// Calculate the liveness information for the given machine function and diff --git a/llvm/lib/CodeGen/LiveDebugVariables.cpp b/llvm/lib/CodeGen/LiveDebugVariables.cpp --- a/llvm/lib/CodeGen/LiveDebugVariables.cpp +++ b/llvm/lib/CodeGen/LiveDebugVariables.cpp @@ -100,29 +100,103 @@ /// with some flags about the original usage of the location. class DbgVariableValue { public: - DbgVariableValue(unsigned LocNo, bool WasIndirect, - const DIExpression &Expression) - : LocNo(LocNo), WasIndirect(WasIndirect), Expression(&Expression) { - assert(getLocNo() == LocNo && "location truncation"); + DbgVariableValue(ArrayRef NewLocs, bool WasIndirect, bool WasList, + bool IsReal, const DIExpression &Expr) + : WasIndirect(WasIndirect), WasList(WasList), IsReal(IsReal), + Expression(&Expr), LocNos() { + assert(!(WasIndirect && WasList) && + "DBG_VALUE_LISTs should not be indirect."); + std::vector LocNoVec; + for (unsigned LocNo : NewLocs) { + auto It = find(LocNoVec, LocNo); + if (It == LocNoVec.end()) + LocNoVec.push_back(LocNo); + else { + // Loc duplicates an element in LocNos; replace references to Op + // with references to the duplicating element. + unsigned OpIdx = LocNoVec.size(); + unsigned DuplicatingIdx = std::distance(LocNoVec.begin(), It); + Expression = + DIExpression::replaceArg(Expression, OpIdx, DuplicatingIdx); + } + } + LocNos.reset(new std::vector(std::move(LocNoVec))); + assert(LocNos->size() > 0 || !IsReal); } - DbgVariableValue() : LocNo(0), WasIndirect(0) {} + DbgVariableValue() + : LocNos(new std::vector()), WasIndirect(0), WasList(0), + IsReal(0) {} + DbgVariableValue(const DbgVariableValue &Other) + : WasIndirect(Other.getWasIndirect()), WasList(Other.getWasList()), + IsReal(Other.IsReal), Expression(Other.getExpression()), + LocNos(new std::vector(Other.getLocNos().begin(), + Other.getLocNos().end())) { + assert(LocNos->size() > 0 || !IsReal); + } + + DbgVariableValue &operator=(const DbgVariableValue &Other) { + ArrayRef OtherLocNos = Other.getLocNos(); + LocNos.reset( + new std::vector(OtherLocNos.begin(), OtherLocNos.end())); + WasIndirect = Other.getWasIndirect(); + WasList = Other.getWasList(); + IsReal = Other.IsReal; + Expression = Other.getExpression(); + assert(LocNos->size() > 0 || !IsReal); + return *this; + } const DIExpression *getExpression() const { return Expression; } - unsigned getLocNo() const { + const ArrayRef getLocNos() const { // Fix up the undef location number, which gets truncated. - return LocNo == INT_MAX ? UndefLocNo : LocNo; + return *LocNos; + } + bool containsLocNo(unsigned LocNo) const { + return is_contained(*LocNos, LocNo); } bool getWasIndirect() const { return WasIndirect; } - bool isUndef() const { return getLocNo() == UndefLocNo; } - - DbgVariableValue changeLocNo(unsigned NewLocNo) const { - return DbgVariableValue(NewLocNo, WasIndirect, *Expression); + bool getWasList() const { return WasList; } + bool isUndef() const { return LocNos->empty() || containsLocNo(UndefLocNo); } + + DbgVariableValue decrementLocNosAfterPivot(unsigned Pivot) const { + SmallVector NewLocNos; + for (unsigned LocNo : *LocNos) + NewLocNos.push_back(LocNo != UndefLocNo && LocNo > Pivot ? LocNo - 1 + : LocNo); + return DbgVariableValue(NewLocNos, WasIndirect, WasList, IsReal, + *Expression); + } + + DbgVariableValue remapLocNos(ArrayRef LocNoMap) const { + SmallVector NewLocNos; + for (unsigned LocNo : *LocNos) + // Undef values don't exist in locations (and thus not in LocNoMap + // either) so skip over them. See getLocationNo(). + NewLocNos.push_back(LocNo == UndefLocNo ? UndefLocNo : LocNoMap[LocNo]); + return DbgVariableValue(NewLocNos, WasIndirect, WasList, IsReal, + *Expression); + } + + DbgVariableValue changeLocNo(unsigned OldLocNo, unsigned NewLocNo) const { + std::vector NewLocNos; + NewLocNos.assign(LocNos->begin(), LocNos->end()); + auto OldLocIt = find(NewLocNos, OldLocNo); + assert(OldLocIt != NewLocNos.end() && "Old location must be present."); + *OldLocIt = NewLocNo; + return DbgVariableValue(NewLocNos, WasIndirect, WasList, IsReal, + *Expression); + } + + void printLocNos(llvm::raw_ostream &OS) const { + for (unsigned Loc : getLocNos()) + OS << (Loc == *getLocNos().begin() ? " " : ", ") << Loc; } friend inline bool operator==(const DbgVariableValue &LHS, const DbgVariableValue &RHS) { - return LHS.LocNo == RHS.LocNo && LHS.WasIndirect == RHS.WasIndirect && + return *LHS.LocNos == *RHS.LocNos && LHS.WasIndirect == RHS.WasIndirect && + LHS.WasList == RHS.WasList && LHS.IsReal == RHS.IsReal && LHS.Expression == RHS.Expression; } @@ -132,8 +206,10 @@ } private: - unsigned LocNo : 31; - unsigned WasIndirect : 1; + std::unique_ptr> LocNos; + bool WasIndirect : 1; + bool WasList : 1; + bool IsReal : 1; const DIExpression *Expression = nullptr; }; } // namespace @@ -179,7 +255,8 @@ /// Insert a DBG_VALUE into MBB at Idx for DbgValue. void insertDebugValue(MachineBasicBlock *MBB, SlotIndex StartIdx, SlotIndex StopIdx, DbgVariableValue DbgValue, - bool Spilled, unsigned SpillOffset, LiveIntervals &LIS, + ArrayRef LocSpills, + ArrayRef SpillOffsets, LiveIntervals &LIS, const TargetInstrInfo &TII, const TargetRegisterInfo &TRI); @@ -264,17 +341,16 @@ void removeLocationIfUnused(unsigned LocNo) { // Bail out if LocNo still is used. for (LocMap::const_iterator I = locInts.begin(); I.valid(); ++I) { - DbgVariableValue DbgValue = I.value(); - if (DbgValue.getLocNo() == LocNo) + const DbgVariableValue &DbgValue = I.value(); + if (DbgValue.containsLocNo(LocNo)) return; } // Remove the entry in the locations vector, and adjust all references to // location numbers above the removed entry. locations.erase(locations.begin() + LocNo); for (LocMap::iterator I = locInts.begin(); I.valid(); ++I) { - DbgVariableValue DbgValue = I.value(); - if (!DbgValue.isUndef() && DbgValue.getLocNo() > LocNo) - I.setValueUnchecked(DbgValue.changeLocNo(DbgValue.getLocNo() - 1)); + const DbgVariableValue &DbgValue = I.value(); + I.setValueUnchecked(DbgValue.decrementLocNosAfterPivot(LocNo)); } } @@ -282,16 +358,19 @@ void mapVirtRegs(LDVImpl *LDV); /// Add a definition point to this user value. - void addDef(SlotIndex Idx, const MachineOperand &LocMO, bool IsIndirect, - const DIExpression &Expr) { - DbgVariableValue DbgValue(getLocationNo(LocMO), IsIndirect, Expr); + void addDef(SlotIndex Idx, ArrayRef LocMOs, bool IsIndirect, + bool IsList, const DIExpression &Expr) { + SmallVector Locs; + for (MachineOperand Op : LocMOs) + Locs.push_back(getLocationNo(Op)); + DbgVariableValue DbgValue(Locs, IsIndirect, IsList, true, Expr); // Add a singular (Idx,Idx) -> value mapping. LocMap::iterator I = locInts.find(Idx); if (!I.valid() || I.start() != Idx) - I.insert(Idx, Idx.getNextSlot(), DbgValue); + I.insert(Idx, Idx.getNextSlot(), std::move(DbgValue)); else // A later DBG_VALUE at the same SlotIndex overrides the old location. - I.setValue(DbgValue); + I.setValue(std::move(DbgValue)); } /// Extend the current definition as far as possible down. @@ -304,25 +383,30 @@ /// /// \param Idx Starting point for the definition. /// \param DbgValue value to propagate. - /// \param LR Restrict liveness to where LR has the value VNI. May be null. - /// \param VNI When LR is not null, this is the value to restrict to. + /// \param LiveIntervalInfo For each location number key in this map, + /// restricts liveness to where the LiveRange has the value equal to the\ + /// VNInfo. /// \param [out] Kills Append end points of VNI's live range to Kills. /// \param LIS Live intervals analysis. - void extendDef(SlotIndex Idx, DbgVariableValue DbgValue, LiveRange *LR, - const VNInfo *VNI, SmallVectorImpl *Kills, + void extendDef(SlotIndex Idx, DbgVariableValue DbgValue, + SmallDenseMap> + &LiveIntervalInfo, + Optional>> &Kills, LiveIntervals &LIS); /// The value in LI may be copies to other registers. Determine if /// any of the copies are available at the kill points, and add defs if /// possible. /// - /// \param LI Scan for copies of the value in LI->reg. /// \param DbgValue Location number of LI->reg, and DIExpression. - /// \param Kills Points where the range of DbgValue could be extended. + /// \param LocIntervals Scan for copies of the value for each location in the + /// corresponding LiveInterval->reg. + /// \param KilledAt The point where the range of DbgValue could be extended. /// \param [in,out] NewDefs Append (Idx, DbgValue) of inserted defs here. void addDefsFromCopies( - LiveInterval *LI, DbgVariableValue DbgValue, - const SmallVectorImpl &Kills, + DbgVariableValue DbgValue, + SmallVectorImpl> &LocIntervals, + SlotIndex KilledAt, SmallVectorImpl> &NewDefs, MachineRegisterInfo &MRI, LiveIntervals &LIS); @@ -555,11 +639,13 @@ for (LocMap::const_iterator I = locInts.begin(); I.valid(); ++I) { OS << " [" << I.start() << ';' << I.stop() << "):"; if (I.value().isUndef()) - OS << "undef"; + OS << " undef"; else { - OS << I.value().getLocNo(); + I.value().printLocNos(OS); if (I.value().getWasIndirect()) OS << " ind"; + else if (I.value().getWasList()) + OS << " list"; } } for (unsigned i = 0, e = locations.size(); i != e; ++i) { @@ -623,10 +709,12 @@ } bool LDVImpl::handleDebugValue(MachineInstr &MI, SlotIndex Idx) { - // DBG_VALUE loc, offset, variable - if (MI.getNumOperands() != 4 || - !(MI.getDebugOffset().isReg() || MI.getDebugOffset().isImm()) || - !MI.getDebugVariableOp().isMetadata()) { + // DBG_VALUE loc, offset, variable, expr + // DBG_VALUE_LIST variable, expr, locs... + if (!MI.isDebugValue() || !MI.getDebugVariableOp().isMetadata() || + MI.isNonListDebugValue() && + (!(MI.getDebugOffset().isImm() || MI.getDebugOffset().isReg()) || + MI.getNumOperands() != 4)) { LLVM_DEBUG(dbgs() << "Can't handle " << MI); return false; } @@ -639,27 +727,28 @@ // (and if the machine verifier is improved to catch this), then these checks // could be removed or replaced by asserts. bool Discard = false; - if (MI.getDebugOperand(0).isReg() && - Register::isVirtualRegister(MI.getDebugOperand(0).getReg())) { - const Register Reg = MI.getDebugOperand(0).getReg(); - if (!LIS->hasInterval(Reg)) { - // The DBG_VALUE is described by a virtual register that does not have a - // live interval. Discard the DBG_VALUE. - Discard = true; - LLVM_DEBUG(dbgs() << "Discarding debug info (no LIS interval): " << Idx - << " " << MI); - } else { - // The DBG_VALUE is only valid if either Reg is live out from Idx, or Reg - // is defined dead at Idx (where Idx is the slot index for the instruction - // preceding the DBG_VALUE). - const LiveInterval &LI = LIS->getInterval(Reg); - LiveQueryResult LRQ = LI.Query(Idx); - if (!LRQ.valueOutOrDead()) { - // We have found a DBG_VALUE with the value in a virtual register that - // is not live. Discard the DBG_VALUE. + for (const MachineOperand &Op : MI.debug_operands()) { + if (Op.isReg() && Register::isVirtualRegister(Op.getReg())) { + const Register Reg = Op.getReg(); + if (!LIS->hasInterval(Reg)) { + // The DBG_VALUE is described by a virtual register that does not have a + // live interval. Discard the DBG_VALUE. Discard = true; - LLVM_DEBUG(dbgs() << "Discarding debug info (reg not live): " << Idx + LLVM_DEBUG(dbgs() << "Discarding debug info (no LIS interval): " << Idx << " " << MI); + } else { + // The DBG_VALUE is only valid if either Reg is live out from Idx, or + // Reg is defined dead at Idx (where Idx is the slot index for the + // instruction preceding the DBG_VALUE). + const LiveInterval &LI = LIS->getInterval(Reg); + LiveQueryResult LRQ = LI.Query(Idx); + if (!LRQ.valueOutOrDead()) { + // We have found a DBG_VALUE with the value in a virtual register that + // is not live. Discard the DBG_VALUE. + Discard = true; + LLVM_DEBUG(dbgs() << "Discarding debug info (reg not live): " << Idx + << " " << MI); + } } } } @@ -669,15 +758,21 @@ if (IsIndirect) assert(MI.getDebugOffset().getImm() == 0 && "DBG_VALUE with nonzero offset"); + bool IsList = MI.isDebugValueList(); const DILocalVariable *Var = MI.getDebugVariable(); const DIExpression *Expr = MI.getDebugExpression(); UserValue *UV = getUserValue(Var, Expr->getFragmentInfo(), MI.getDebugLoc()); if (!Discard) - UV->addDef(Idx, MI.getDebugOperand(0), IsIndirect, *Expr); + UV->addDef(Idx, SmallVector(MI.debug_operands()), + IsIndirect, IsList, *Expr); else { MachineOperand MO = MachineOperand::CreateReg(0U, false); MO.setIsDebug(); - UV->addDef(Idx, MO, false, *Expr); + // We should still pass a list the same size as MI.debug_operands() even if + // all MOs are undef, so that DbgVariableValue can correctly adjust the + // expression while removing the duplicated undefs. + SmallVector UndefMOs(MI.getNumDebugOperands(), MO); + UV->addDef(Idx, UndefMOs, false, IsList, *Expr); } return true; } @@ -754,26 +849,33 @@ return Changed; } -void UserValue::extendDef(SlotIndex Idx, DbgVariableValue DbgValue, LiveRange *LR, - const VNInfo *VNI, SmallVectorImpl *Kills, - LiveIntervals &LIS) { +void UserValue::extendDef( + SlotIndex Idx, DbgVariableValue DbgValue, + SmallDenseMap> + &LiveIntervalInfo, + Optional>> &Kills, + LiveIntervals &LIS) { SlotIndex Start = Idx; MachineBasicBlock *MBB = LIS.getMBBFromIndex(Start); SlotIndex Stop = LIS.getMBBEndIdx(MBB); LocMap::iterator I = locInts.find(Start); - // Limit to VNI's live range. + // Limit to the intersection of the VNIs' live ranges. bool ToEnd = true; - if (LR && VNI) { + for (auto &LII : LiveIntervalInfo) { + LiveRange *LR = LII.second.first; + const VNInfo *VNI = LII.second.second; + assert(LR && VNI && "Missing range info for Idx."); LiveInterval::Segment *Segment = LR->getSegmentContaining(Start); - if (!Segment || Segment->valno != VNI) { - if (Kills) - Kills->push_back(Start); - return; - } + assert(Segment && Segment->valno == VNI && "Invalid VNInfo for Idx given?"); if (Segment->end < Stop) { Stop = Segment->end; ToEnd = false; + Kills = {Stop, {LII.first}}; + } else if (Segment->end == Stop && Kills.hasValue()) { + // If multiple locations end at the same place, track all of them in + // Kills. + Kills->second.push_back(LII.first); } } @@ -781,94 +883,114 @@ if (I.valid() && I.start() <= Start) { // Stop when meeting a different location or an already extended interval. Start = Start.getNextSlot(); - if (I.value() != DbgValue || I.stop() != Start) + if (I.value() != DbgValue || I.stop() != Start) { + // Clear `Kills`, as we have a new def available. + Kills = None; return; + } // This is a one-slot placeholder. Just skip it. ++I; } // Limited by the next def. - if (I.valid() && I.start() < Stop) + if (I.valid() && I.start() < Stop) { Stop = I.start(); - // Limited by VNI's live range. - else if (!ToEnd && Kills) - Kills->push_back(Stop); - - if (Start < Stop) - I.insert(Start, Stop, DbgValue); + // Clear `Kills`, as we have a new def available. + Kills = None; + } + + if (Start < Stop) { + DbgVariableValue ExtDbgValue(DbgValue); + I.insert(Start, Stop, std::move(ExtDbgValue)); + } } void UserValue::addDefsFromCopies( - LiveInterval *LI, DbgVariableValue DbgValue, - const SmallVectorImpl &Kills, + DbgVariableValue DbgValue, + SmallVectorImpl> &LocIntervals, + SlotIndex KilledAt, SmallVectorImpl> &NewDefs, MachineRegisterInfo &MRI, LiveIntervals &LIS) { - if (Kills.empty()) - return; // Don't track copies from physregs, there are too many uses. - if (!Register::isVirtualRegister(LI->reg())) + if (any_of(LocIntervals, [](auto LocI) { + return !Register::isVirtualRegister(LocI.second->reg()); + })) return; // Collect all the (vreg, valno) pairs that are copies of LI. - SmallVector, 8> CopyValues; - for (MachineOperand &MO : MRI.use_nodbg_operands(LI->reg())) { - MachineInstr *MI = MO.getParent(); - // Copies of the full value. - if (MO.getSubReg() || !MI->isCopy()) - continue; - Register DstReg = MI->getOperand(0).getReg(); - - // Don't follow copies to physregs. These are usually setting up call - // arguments, and the argument registers are always call clobbered. We are - // better off in the source register which could be a callee-saved register, - // or it could be spilled. - if (!Register::isVirtualRegister(DstReg)) - continue; - - // Is the value extended to reach this copy? If not, another def may be - // blocking it, or we are looking at a wrong value of LI. - SlotIndex Idx = LIS.getInstructionIndex(*MI); - LocMap::iterator I = locInts.find(Idx.getRegSlot(true)); - if (!I.valid() || I.value() != DbgValue) - continue; - - if (!LIS.hasInterval(DstReg)) - continue; - LiveInterval *DstLI = &LIS.getInterval(DstReg); - const VNInfo *DstVNI = DstLI->getVNInfoAt(Idx.getRegSlot()); - assert(DstVNI && DstVNI->def == Idx.getRegSlot() && "Bad copy value"); - CopyValues.push_back(std::make_pair(DstLI, DstVNI)); + SmallDenseMap, 4>> + CopyValues; + for (auto &LocInterval : LocIntervals) { + unsigned LocNo = LocInterval.first; + LiveInterval *LI = LocInterval.second; + for (MachineOperand &MO : MRI.use_nodbg_operands(LI->reg())) { + MachineInstr *MI = MO.getParent(); + // Copies of the full value. + if (MO.getSubReg() || !MI->isCopy()) + continue; + Register DstReg = MI->getOperand(0).getReg(); + + // Don't follow copies to physregs. These are usually setting up call + // arguments, and the argument registers are always call clobbered. We are + // better off in the source register which could be a callee-saved + // register, or it could be spilled. + if (!Register::isVirtualRegister(DstReg)) + continue; + + // Is the value extended to reach this copy? If not, another def may be + // blocking it, or we are looking at a wrong value of LI. + SlotIndex Idx = LIS.getInstructionIndex(*MI); + LocMap::iterator I = locInts.find(Idx.getRegSlot(true)); + if (!I.valid() || I.value() != DbgValue) + continue; + + if (!LIS.hasInterval(DstReg)) + continue; + LiveInterval *DstLI = &LIS.getInterval(DstReg); + const VNInfo *DstVNI = DstLI->getVNInfoAt(Idx.getRegSlot()); + assert(DstVNI && DstVNI->def == Idx.getRegSlot() && "Bad copy value"); + CopyValues[LocNo].push_back(std::make_pair(DstLI, DstVNI)); + } } if (CopyValues.empty()) return; - LLVM_DEBUG(dbgs() << "Got " << CopyValues.size() << " copies of " << *LI - << '\n'); - - // Try to add defs of the copied values for each kill point. - for (unsigned i = 0, e = Kills.size(); i != e; ++i) { - SlotIndex Idx = Kills[i]; - for (unsigned j = 0, e = CopyValues.size(); j != e; ++j) { - LiveInterval *DstLI = CopyValues[j].first; - const VNInfo *DstVNI = CopyValues[j].second; - if (DstLI->getVNInfoAt(Idx) != DstVNI) + for (auto &LocInterval : LocIntervals) + LLVM_DEBUG(dbgs() << "Got " << CopyValues[LocInterval.first].size() + << " copies of " << *LocInterval.second << '\n'); + + // Try to add defs of the copied values for the kill point. Check that there + // isn't already a def at Idx. + LocMap::iterator I = locInts.find(KilledAt); + if (I.valid() && I.start() <= KilledAt) + return; + DbgVariableValue NewValue(DbgValue); + for (auto &LocInterval : LocIntervals) { + unsigned LocNo = LocInterval.first; + bool FoundCopy = false; + for (auto &LIAndVNI : CopyValues[LocNo]) { + LiveInterval *DstLI = LIAndVNI.first; + const VNInfo *DstVNI = LIAndVNI.second; + if (DstLI->getVNInfoAt(KilledAt) != DstVNI) continue; - // Check that there isn't already a def at Idx - LocMap::iterator I = locInts.find(Idx); - if (I.valid() && I.start() <= Idx) - continue; - LLVM_DEBUG(dbgs() << "Kill at " << Idx << " covered by valno #" + LLVM_DEBUG(dbgs() << "Kill at " << KilledAt << " covered by valno #" << DstVNI->id << " in " << *DstLI << '\n'); MachineInstr *CopyMI = LIS.getInstructionFromIndex(DstVNI->def); assert(CopyMI && CopyMI->isCopy() && "Bad copy value"); - unsigned LocNo = getLocationNo(CopyMI->getOperand(0)); - DbgVariableValue NewValue = DbgValue.changeLocNo(LocNo); - I.insert(Idx, Idx.getNextSlot(), NewValue); - NewDefs.push_back(std::make_pair(Idx, NewValue)); + unsigned NewLocNo = getLocationNo(CopyMI->getOperand(0)); + NewValue = NewValue.changeLocNo(LocNo, NewLocNo); + FoundCopy = true; break; } + // If there are any killed locations we can't find a copy for, we can't + // extend the variable value. + if (!FoundCopy) + return; } + I.insert(KilledAt, KilledAt.getNextSlot(), NewValue); + NewDefs.push_back(std::make_pair(KilledAt, NewValue)); } void UserValue::computeIntervals(MachineRegisterInfo &MRI, @@ -885,34 +1007,54 @@ for (unsigned i = 0; i != Defs.size(); ++i) { SlotIndex Idx = Defs[i].first; DbgVariableValue DbgValue = Defs[i].second; - const MachineOperand &LocMO = locations[DbgValue.getLocNo()]; - - if (!LocMO.isReg()) { - extendDef(Idx, DbgValue, nullptr, nullptr, nullptr, LIS); - continue; - } - - // Register locations are constrained to where the register value is live. - if (Register::isVirtualRegister(LocMO.getReg())) { + SmallDenseMap> LIs; + SmallVector VNIs; + bool ShouldExtendDef = false; + for (unsigned LocNo : DbgValue.getLocNos()) { + const MachineOperand &LocMO = locations[LocNo]; + if (!LocMO.isReg() || !Register::isVirtualRegister(LocMO.getReg())) { + ShouldExtendDef |= !LocMO.isReg(); + continue; + } + ShouldExtendDef = true; LiveInterval *LI = nullptr; const VNInfo *VNI = nullptr; if (LIS.hasInterval(LocMO.getReg())) { LI = &LIS.getInterval(LocMO.getReg()); VNI = LI->getVNInfoAt(Idx); } - SmallVector Kills; - extendDef(Idx, DbgValue, LI, VNI, &Kills, LIS); - // FIXME: Handle sub-registers in addDefsFromCopies. The problem is that - // if the original location for example is %vreg0:sub_hi, and we find a - // full register copy in addDefsFromCopies (at the moment it only handles - // full register copies), then we must add the sub1 sub-register index to - // the new location. However, that is only possible if the new virtual - // register is of the same regclass (or if there is an equivalent - // sub-register in that regclass). For now, simply skip handling copies if - // a sub-register is involved. - if (LI && !LocMO.getSubReg()) - addDefsFromCopies(LI, DbgValue, Kills, Defs, MRI, LIS); - continue; + if (LI && VNI) + LIs[LocNo] = {LI, VNI}; + } + if (ShouldExtendDef) { + Optional>> Kills; + extendDef(Idx, DbgValue, LIs, Kills, LIS); + + if (Kills) { + SmallVector, 2> LocIntervals; + bool AnySubreg = false; + for (unsigned LocNo : Kills->second) { + const MachineOperand &LocMO = this->locations[LocNo]; + if (LocMO.getSubReg()) { + AnySubreg = true; + break; + } + LiveInterval *LI = &LIS.getInterval(LocMO.getReg()); + LocIntervals.push_back({LocNo, LI}); + } + + // FIXME: Handle sub-registers in addDefsFromCopies. The problem is that + // if the original location for example is %vreg0:sub_hi, and we find a + // full register copy in addDefsFromCopies (at the moment it only + // handles full register copies), then we must add the sub1 sub-register + // index to the new location. However, that is only possible if the new + // virtual register is of the same regclass (or if there is an + // equivalent sub-register in that regclass). For now, simply skip + // handling copies if a sub-register is involved. + if (!AnySubreg) + addDefsFromCopies(DbgValue, LocIntervals, Kills->first, Defs, MRI, + LIS); + } } // For physregs, we only mark the start slot idx. DwarfDebug will see it @@ -1091,7 +1233,7 @@ break; // Now LII->end > LocMapI.start(). Do we have an overlap? - if (LocMapI.value().getLocNo() == OldLocNo && + if (LocMapI.value().containsLocNo(OldLocNo) && LII->start < LocMapI.stop()) { // Overlapping correct location. Allocate NewLocNo now. if (NewLocNo == UndefLocNo) { @@ -1112,7 +1254,7 @@ LocMapI.setStopUnchecked(LII->end); // Change the value in the overlap. This may trigger coalescing. - LocMapI.setValue(OldDbgValue.changeLocNo(NewLocNo)); + LocMapI.setValue(OldDbgValue.changeLocNo(OldLocNo, NewLocNo)); // Re-insert any removed OldDbgValue ranges. if (LStart < LocMapI.start()) { @@ -1269,13 +1411,7 @@ // DBG_VALUE intervals with different vregs that were allocated to the same // physical register. for (LocMap::iterator I = locInts.begin(); I.valid(); ++I) { - DbgVariableValue DbgValue = I.value(); - // Undef values don't exist in locations (and thus not in LocNoMap either) - // so skip over them. See getLocationNo(). - if (DbgValue.isUndef()) - continue; - unsigned NewLocNo = LocNoMap[DbgValue.getLocNo()]; - I.setValueUnchecked(DbgValue.changeLocNo(NewLocNo)); + I.setValueUnchecked(I.value().remapLocNos(LocNoMap)); I.setStart(I.start()); } } @@ -1306,21 +1442,24 @@ /// Find an iterator for inserting the next DBG_VALUE instruction /// (or end if no more insert locations found). static MachineBasicBlock::iterator -findNextInsertLocation(MachineBasicBlock *MBB, - MachineBasicBlock::iterator I, - SlotIndex StopIdx, MachineOperand &LocMO, - LiveIntervals &LIS, - const TargetRegisterInfo &TRI) { - if (!LocMO.isReg()) +findNextInsertLocation(MachineBasicBlock *MBB, MachineBasicBlock::iterator I, + SlotIndex StopIdx, ArrayRef LocMOs, + LiveIntervals &LIS, const TargetRegisterInfo &TRI) { + SmallVector Regs; + for (const MachineOperand &LocMO : LocMOs) + if (LocMO.isReg()) + Regs.push_back(LocMO.getReg()); + if (Regs.empty()) return MBB->instr_end(); - Register Reg = LocMO.getReg(); // Find the next instruction in the MBB that define the register Reg. while (I != MBB->end() && !I->isTerminator()) { if (!LIS.isNotInMIMap(*I) && SlotIndex::isEarlierEqualInstr(StopIdx, LIS.getInstructionIndex(*I))) break; - if (I->definesRegister(Reg, &TRI)) + if (any_of(Regs, [&I, &TRI](Register &Reg) { + return I->definesRegister(Reg, &TRI); + })) // The insert location is directly after the instruction/bundle. return std::next(I); ++I; @@ -1330,7 +1469,8 @@ void UserValue::insertDebugValue(MachineBasicBlock *MBB, SlotIndex StartIdx, SlotIndex StopIdx, DbgVariableValue DbgValue, - bool Spilled, unsigned SpillOffset, + ArrayRef LocSpills, + ArrayRef SpillOffsets, LiveIntervals &LIS, const TargetInstrInfo &TII, const TargetRegisterInfo &TRI) { SlotIndex MBBEndIdx = LIS.getMBBEndIdx(&*MBB); @@ -1339,14 +1479,18 @@ MachineBasicBlock::iterator I = findInsertLocation(MBB, StartIdx, LIS); // Undef values don't exist in locations so create new "noreg" register MOs // for them. See getLocationNo(). - MachineOperand MO = - !DbgValue.isUndef() - ? locations[DbgValue.getLocNo()] - : MachineOperand::CreateReg( - /* Reg */ 0, /* isDef */ false, /* isImp */ false, - /* isKill */ false, /* isDead */ false, - /* isUndef */ false, /* isEarlyClobber */ false, - /* SubReg */ 0, /* isDebug */ true); + SmallVector MOs; + if (DbgValue.isUndef()) { + MOs.assign(DbgValue.getLocNos().size(), + MachineOperand::CreateReg( + /* Reg */ 0, /* isDef */ false, /* isImp */ false, + /* isKill */ false, /* isDead */ false, + /* isUndef */ false, /* isEarlyClobber */ false, + /* SubReg */ 0, /* isDebug */ true)); + } else { + for (unsigned LocNo : DbgValue.getLocNos()) + MOs.push_back(locations[LocNo]); + } ++NumInsertedDebugValues; @@ -1359,25 +1503,37 @@ // that the original virtual register was a pointer. Also, add the stack slot // offset for the spilled register to the expression. const DIExpression *Expr = DbgValue.getExpression(); - uint8_t DIExprFlags = DIExpression::ApplyOffset; bool IsIndirect = DbgValue.getWasIndirect(); - if (Spilled) { - if (IsIndirect) - DIExprFlags |= DIExpression::DerefAfter; - Expr = - DIExpression::prepend(Expr, DIExprFlags, SpillOffset); - IsIndirect = true; - } - - assert((!Spilled || MO.isFI()) && "a spilled location must be a frame index"); - + bool IsList = DbgValue.getWasList(); + for (unsigned I = 0, E = LocSpills.size(); I != E; ++I) { + if (LocSpills[I]) { + if (!IsList) { + uint8_t DIExprFlags = DIExpression::ApplyOffset; + if (IsIndirect) + DIExprFlags |= DIExpression::DerefAfter; + Expr = DIExpression::prepend(Expr, DIExprFlags, SpillOffsets[I]); + IsIndirect = true; + } else { + SmallVector Ops; + DIExpression::appendOffset(Ops, SpillOffsets[I]); + Ops.push_back(dwarf::DW_OP_deref); + Expr = DIExpression::appendOpsToArg(Expr, Ops, I); + } + } + + assert((!LocSpills[I] || MOs[I].isFI()) && + "a spilled location must be a frame index"); + } + + unsigned DbgValueOpcode = + IsList ? TargetOpcode::DBG_VALUE_LIST : TargetOpcode::DBG_VALUE; do { - BuildMI(*MBB, I, getDebugLoc(), TII.get(TargetOpcode::DBG_VALUE), - IsIndirect, MO, Variable, Expr); + BuildMI(*MBB, I, getDebugLoc(), TII.get(DbgValueOpcode), IsIndirect, MOs, + Variable, Expr); - // Continue and insert DBG_VALUES after every redefinition of register + // Continue and insert DBG_VALUES after every redefinition of a register // associated with the debug value within the range - I = findNextInsertLocation(MBB, I, StopIdx, MO, LIS, TRI); + I = findNextInsertLocation(MBB, I, StopIdx, MOs, LIS, TRI); } while (I != MBB->end()); } @@ -1400,10 +1556,16 @@ SlotIndex Start = I.start(); SlotIndex Stop = I.stop(); DbgVariableValue DbgValue = I.value(); - auto SpillIt = !DbgValue.isUndef() ? SpillOffsets.find(DbgValue.getLocNo()) - : SpillOffsets.end(); - bool Spilled = SpillIt != SpillOffsets.end(); - unsigned SpillOffset = Spilled ? SpillIt->second : 0; + + SmallVector SpilledLocs; + SmallVector LocSpillOffsets; + for (unsigned LocNo : DbgValue.getLocNos()) { + auto SpillIt = + !DbgValue.isUndef() ? SpillOffsets.find(LocNo) : SpillOffsets.end(); + bool Spilled = SpillIt != SpillOffsets.end(); + SpilledLocs.push_back(Spilled); + LocSpillOffsets.push_back(Spilled ? SpillIt->second : 0); + } // If the interval start was trimmed to the lexical scope insert the // DBG_VALUE at the previous index (otherwise it appears after the @@ -1411,14 +1573,14 @@ if (trimmedDefs.count(Start)) Start = Start.getPrevIndex(); - LLVM_DEBUG(dbgs() << "\t[" << Start << ';' << Stop - << "):" << DbgValue.getLocNo()); + LLVM_DEBUG(auto &dbg = dbgs(); dbg << "\t[" << Start << ';' << Stop << "):"; + DbgValue.printLocNos(dbg)); MachineFunction::iterator MBB = LIS.getMBBFromIndex(Start)->getIterator(); SlotIndex MBBEnd = LIS.getMBBEndIdx(&*MBB); LLVM_DEBUG(dbgs() << ' ' << printMBBReference(*MBB) << '-' << MBBEnd); - insertDebugValue(&*MBB, Start, Stop, DbgValue, Spilled, SpillOffset, LIS, - TII, TRI); + insertDebugValue(&*MBB, Start, Stop, DbgValue, SpilledLocs, LocSpillOffsets, + LIS, TII, TRI); // This interval may span multiple basic blocks. // Insert a DBG_VALUE into each one. while (Stop > MBBEnd) { @@ -1428,8 +1590,8 @@ break; MBBEnd = LIS.getMBBEndIdx(&*MBB); LLVM_DEBUG(dbgs() << ' ' << printMBBReference(*MBB) << '-' << MBBEnd); - insertDebugValue(&*MBB, Start, Stop, DbgValue, Spilled, SpillOffset, LIS, - TII, TRI); + insertDebugValue(&*MBB, Start, Stop, DbgValue, SpilledLocs, + LocSpillOffsets, LIS, TII, TRI); } LLVM_DEBUG(dbgs() << '\n'); if (MBB == MFEnd) diff --git a/llvm/lib/CodeGen/LiveRangeShrink.cpp b/llvm/lib/CodeGen/LiveRangeShrink.cpp --- a/llvm/lib/CodeGen/LiveRangeShrink.cpp +++ b/llvm/lib/CodeGen/LiveRangeShrink.cpp @@ -234,7 +234,7 @@ MachineBasicBlock::iterator EndIter = std::next(MI.getIterator()); if (MI.getOperand(0).isReg()) for (; EndIter != MBB.end() && EndIter->isDebugValue() && - EndIter->getDebugOperandForReg(MI.getOperand(0).getReg()); + EndIter->hasDebugOperandForReg(MI.getOperand(0).getReg()); ++EndIter, ++Next) IOM[&*EndIter] = NewOrder; MBB.splice(I, &MBB, MI.getIterator(), EndIter); diff --git a/llvm/lib/CodeGen/MIRParser/MIParser.cpp b/llvm/lib/CodeGen/MIRParser/MIParser.cpp --- a/llvm/lib/CodeGen/MIRParser/MIParser.cpp +++ b/llvm/lib/CodeGen/MIRParser/MIParser.cpp @@ -987,7 +987,9 @@ Optional TiedDefIdx; if (parseMachineOperandAndTargetFlags(OpCode, Operands.size(), MO, TiedDefIdx)) return true; - if (OpCode == TargetOpcode::DBG_VALUE && MO.isReg()) + if ((OpCode == TargetOpcode::DBG_VALUE || + OpCode == TargetOpcode::DBG_VALUE_LIST) && + MO.isReg()) MO.setIsDebug(); Operands.push_back( ParsedMachineOperand(MO, Loc, Token.location(), TiedDefIdx)); diff --git a/llvm/lib/CodeGen/MachineInstr.cpp b/llvm/lib/CodeGen/MachineInstr.cpp --- a/llvm/lib/CodeGen/MachineInstr.cpp +++ b/llvm/lib/CodeGen/MachineInstr.cpp @@ -843,28 +843,35 @@ } const MachineOperand &MachineInstr::getDebugVariableOp() const { - assert((isDebugValue() || isDebugRef()) && "not a DBG_VALUE"); - return getOperand(2); + assert((isDebugValue() || isDebugRef()) && "not a DBG_VALUE*"); + unsigned VariableOp = isDebugValueList() ? 0 : 2; + return getOperand(VariableOp); } MachineOperand &MachineInstr::getDebugVariableOp() { - assert((isDebugValue() || isDebugRef()) && "not a DBG_VALUE"); - return getOperand(2); + assert((isDebugValue() || isDebugRef()) && "not a DBG_VALUE*"); + unsigned VariableOp = isDebugValueList() ? 0 : 2; + return getOperand(VariableOp); } const DILocalVariable *MachineInstr::getDebugVariable() const { - assert((isDebugValue() || isDebugRef()) && "not a DBG_VALUE"); - return cast(getOperand(2).getMetadata()); + return cast(getDebugVariableOp().getMetadata()); +} + +const MachineOperand &MachineInstr::getDebugExpressionOp() const { + assert((isDebugValue() || isDebugRef()) && "not a DBG_VALUE*"); + unsigned ExpressionOp = isDebugValueList() ? 1 : 3; + return getOperand(ExpressionOp); } MachineOperand &MachineInstr::getDebugExpressionOp() { - assert((isDebugValue() || isDebugRef()) && "not a DBG_VALUE"); - return getOperand(3); + assert((isDebugValue() || isDebugRef()) && "not a DBG_VALUE*"); + unsigned ExpressionOp = isDebugValueList() ? 1 : 3; + return getOperand(ExpressionOp); } const DIExpression *MachineInstr::getDebugExpression() const { - assert((isDebugValue() || isDebugRef()) && "not a DBG_VALUE"); - return cast(getOperand(3).getMetadata()); + return cast(getDebugExpressionOp().getMetadata()); } bool MachineInstr::isDebugEntryValue() const { @@ -1713,7 +1720,7 @@ OS << " "; if (isDebugValue() && MO.isMetadata()) { - // Pretty print DBG_VALUE instructions. + // Pretty print DBG_VALUE* instructions. auto *DIV = dyn_cast(MO.getMetadata()); if (DIV && !DIV->getName().empty()) OS << "!\"" << DIV->getName() << '\"'; @@ -2118,8 +2125,8 @@ MachineInstrBuilder llvm::BuildMI(MachineFunction &MF, const DebugLoc &DL, const MCInstrDesc &MCID, bool IsIndirect, - MachineOperand &MO, const MDNode *Variable, - const MDNode *Expr) { + const MachineOperand &MO, + const MDNode *Variable, const MDNode *Expr) { assert(isa(Variable) && "not a variable"); assert(cast(Expr)->isValid() && "not an expression"); assert(cast(Variable)->isValidLocationForIntrinsic(DL) && @@ -2133,7 +2140,28 @@ else MIB.addReg(0U, RegState::Debug); return MIB.addMetadata(Variable).addMetadata(Expr); - } +} + +MachineInstrBuilder llvm::BuildMI(MachineFunction &MF, const DebugLoc &DL, + const MCInstrDesc &MCID, bool IsIndirect, + ArrayRef MOs, + const MDNode *Variable, const MDNode *Expr) { + assert(isa(Variable) && "not a variable"); + assert(cast(Expr)->isValid() && "not an expression"); + assert(cast(Variable)->isValidLocationForIntrinsic(DL) && + "Expected inlined-at fields to agree"); + if (MCID.Opcode == TargetOpcode::DBG_VALUE) + return BuildMI(MF, DL, MCID, IsIndirect, MOs[0], Variable, Expr); + + auto MIB = BuildMI(MF, DL, MCID); + MIB.addMetadata(Variable).addMetadata(Expr); + for (const MachineOperand &MO : MOs) + if (MO.isReg()) + MIB.addReg(MO.getReg(), RegState::Debug); + else + MIB.add(MO); + return MIB; +} MachineInstrBuilder llvm::BuildMI(MachineBasicBlock &BB, MachineBasicBlock::iterator I, @@ -2157,10 +2185,22 @@ return MachineInstrBuilder(MF, *MI); } +MachineInstrBuilder llvm::BuildMI(MachineBasicBlock &BB, + MachineBasicBlock::iterator I, + const DebugLoc &DL, const MCInstrDesc &MCID, + bool IsIndirect, ArrayRef MOs, + const MDNode *Variable, const MDNode *Expr) { + MachineFunction &MF = *BB.getParent(); + MachineInstr *MI = BuildMI(MF, DL, MCID, IsIndirect, MOs, Variable, Expr); + BB.insert(I, MI); + return MachineInstrBuilder(MF, *MI); +} + /// Compute the new DIExpression to use with a DBG_VALUE for a spill slot. /// This prepends DW_OP_deref when spilling an indirect DBG_VALUE. -static const DIExpression *computeExprForSpill(const MachineInstr &MI) { - assert(MI.getOperand(0).isReg() && "can't spill non-register"); +static const DIExpression *computeExprForSpill(const MachineInstr &MI, + Register SpillReg) { + assert(MI.hasDebugOperandForReg(SpillReg) && "Spill Reg is not used in MI."); assert(MI.getDebugVariable()->isValidLocationForIntrinsic(MI.getDebugLoc()) && "Expected inlined-at fields to agree"); @@ -2169,6 +2209,14 @@ assert(MI.getDebugOffset().getImm() == 0 && "DBG_VALUE with nonzero offset"); Expr = DIExpression::prepend(Expr, DIExpression::DerefBefore); + } else if (MI.isDebugValueList()) { + // We will replace the spilled register with a frame index, so + // immediately deref all references to the spilled register. + std::array Ops{{dwarf::DW_OP_deref}}; + for (const MachineOperand &Op : MI.getDebugOperandsForReg(SpillReg)) { + unsigned OpIdx = MI.getDebugOperandIndex(&Op); + Expr = DIExpression::appendOpsToArg(Expr, Ops, OpIdx); + } } return Expr; } @@ -2176,19 +2224,32 @@ MachineInstr *llvm::buildDbgValueForSpill(MachineBasicBlock &BB, MachineBasicBlock::iterator I, const MachineInstr &Orig, - int FrameIndex) { - const DIExpression *Expr = computeExprForSpill(Orig); - return BuildMI(BB, I, Orig.getDebugLoc(), Orig.getDesc()) - .addFrameIndex(FrameIndex) - .addImm(0U) - .addMetadata(Orig.getDebugVariable()) - .addMetadata(Expr); -} - -void llvm::updateDbgValueForSpill(MachineInstr &Orig, int FrameIndex) { - const DIExpression *Expr = computeExprForSpill(Orig); - Orig.getDebugOperand(0).ChangeToFrameIndex(FrameIndex); - Orig.getDebugOffset().ChangeToImmediate(0U); + int FrameIndex, Register SpillReg) { + const DIExpression *Expr = computeExprForSpill(Orig, SpillReg); + MachineInstrBuilder NewMI = + BuildMI(BB, I, Orig.getDebugLoc(), Orig.getDesc()); + // Non-Variadic Operands: Location, Offset, Variable, Expression + // Variadic Operands: Variable, Expression, Locations... + if (Orig.isNonListDebugValue()) + NewMI.addFrameIndex(FrameIndex).addImm(0U); + NewMI.addMetadata(Orig.getDebugVariable()).addMetadata(Expr); + if (Orig.isDebugValueList()) { + for (const MachineOperand &Op : Orig.debug_operands()) + if (Op.isReg() && Op.getReg() == SpillReg) + NewMI.addFrameIndex(FrameIndex); + else + NewMI.add(MachineOperand(Op)); + } + return NewMI; +} + +void llvm::updateDbgValueForSpill(MachineInstr &Orig, int FrameIndex, + Register Reg) { + const DIExpression *Expr = computeExprForSpill(Orig, Reg); + if (Orig.isNonListDebugValue()) + Orig.getDebugOffset().ChangeToImmediate(0U); + for (MachineOperand &Op : Orig.getDebugOperandsForReg(Reg)) + Op.ChangeToFrameIndex(FrameIndex); Orig.getDebugExpressionOp().setMetadata(Expr); } @@ -2203,7 +2264,7 @@ DI != DE; ++DI) { if (!DI->isDebugValue()) return; - if (DI->getDebugOperandForReg(MI.getOperand(0).getReg())) + if (DI->hasDebugOperandForReg(MI.getOperand(0).getReg())) DbgValues.push_back(&*DI); } } @@ -2221,14 +2282,15 @@ auto *DI = MO.getParent(); if (!DI->isDebugValue()) continue; - if (DI->getDebugOperandForReg(DefReg)) { + if (DI->hasDebugOperandForReg(DefReg)) { DbgValues.push_back(DI); } } // Propagate Reg to debug value instructions. for (auto *DBI : DbgValues) - DBI->getDebugOperandForReg(DefReg)->setReg(Reg); + for (MachineOperand &Op : DBI->getDebugOperandsForReg(DefReg)) + Op.setReg(Reg); } using MMOList = SmallVector; diff --git a/llvm/lib/CodeGen/MachineRegisterInfo.cpp b/llvm/lib/CodeGen/MachineRegisterInfo.cpp --- a/llvm/lib/CodeGen/MachineRegisterInfo.cpp +++ b/llvm/lib/CodeGen/MachineRegisterInfo.cpp @@ -542,8 +542,8 @@ I != E; I = nextI) { nextI = std::next(I); // I is invalidated by the setReg MachineInstr *UseMI = &*I; - if (UseMI->isDebugValue()) - UseMI->getDebugOperandForReg(Reg)->setReg(0U); + if (UseMI->isDebugValue() && UseMI->hasDebugOperandForReg(Reg)) + UseMI->setDebugValueUndef(); } } diff --git a/llvm/lib/CodeGen/MachineSink.cpp b/llvm/lib/CodeGen/MachineSink.cpp --- a/llvm/lib/CodeGen/MachineSink.cpp +++ b/llvm/lib/CodeGen/MachineSink.cpp @@ -16,6 +16,7 @@ //===----------------------------------------------------------------------===// #include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/MapVector.h" #include "llvm/ADT/PointerIntPair.h" #include "llvm/ADT/SetVector.h" #include "llvm/ADT/SmallSet.h" @@ -464,9 +465,10 @@ MI.getDebugLoc()->getInlinedAt()); bool SeenBefore = SeenDbgVars.contains(Var); - MachineOperand &MO = MI.getDebugOperand(0); - if (MO.isReg() && MO.getReg().isVirtual()) - SeenDbgUsers[MO.getReg()].push_back(SeenDbgUser(&MI, SeenBefore)); + for (MachineOperand &MO : MI.debug_operands()) { + if (MO.isReg() && MO.getReg().isVirtual()) + SeenDbgUsers[MO.getReg()].push_back(SeenDbgUser(&MI, SeenBefore)); + } // Record the variable for any DBG_VALUE, to avoid re-ordering any of them. SeenDbgVars.insert(Var); @@ -926,14 +928,14 @@ /// leaving an 'undef' DBG_VALUE in the original location. Don't do this if /// there's any subregister weirdness involved. Returns true if copy /// propagation occurred. -static bool attemptDebugCopyProp(MachineInstr &SinkInst, MachineInstr &DbgMI) { +static bool attemptDebugCopyProp(MachineInstr &SinkInst, MachineInstr &DbgMI, + Register Reg) { const MachineRegisterInfo &MRI = SinkInst.getMF()->getRegInfo(); const TargetInstrInfo &TII = *SinkInst.getMF()->getSubtarget().getInstrInfo(); // Copy DBG_VALUE operand and set the original to undef. We then check to // see whether this is something that can be copy-forwarded. If it isn't, // continue around the loop. - MachineOperand &DbgMO = DbgMI.getDebugOperand(0); const MachineOperand *SrcMO = nullptr, *DstMO = nullptr; auto CopyOperands = TII.isCopyInstr(SinkInst); @@ -946,36 +948,41 @@ bool PostRA = MRI.getNumVirtRegs() == 0; // Trying to forward between physical and virtual registers is too hard. - if (DbgMO.getReg().isVirtual() != SrcMO->getReg().isVirtual()) + if (Reg.isVirtual() != SrcMO->getReg().isVirtual()) return false; // Only try virtual register copy-forwarding before regalloc, and physical // register copy-forwarding after regalloc. - bool arePhysRegs = !DbgMO.getReg().isVirtual(); + bool arePhysRegs = !Reg.isVirtual(); if (arePhysRegs != PostRA) return false; // Pre-regalloc, only forward if all subregisters agree (or there are no // subregs at all). More analysis might recover some forwardable copies. - if (!PostRA && (DbgMO.getSubReg() != SrcMO->getSubReg() || - DbgMO.getSubReg() != DstMO->getSubReg())) - return false; + if (!PostRA) + for (auto &DbgMO : DbgMI.getDebugOperandsForReg(Reg)) + if (DbgMO.getSubReg() != SrcMO->getSubReg() || + DbgMO.getSubReg() != DstMO->getSubReg()) + return false; // Post-regalloc, we may be sinking a DBG_VALUE of a sub or super-register // of this copy. Only forward the copy if the DBG_VALUE operand exactly // matches the copy destination. - if (PostRA && DbgMO.getReg() != DstMO->getReg()) + if (PostRA && Reg != DstMO->getReg()) return false; - DbgMO.setReg(SrcMO->getReg()); - DbgMO.setSubReg(SrcMO->getSubReg()); + for (auto &DbgMO : DbgMI.getDebugOperandsForReg(Reg)) { + DbgMO.setReg(SrcMO->getReg()); + DbgMO.setSubReg(SrcMO->getSubReg()); + } return true; } +using MIRegs = std::pair>; /// Sink an instruction and its associated debug instructions. static void performSink(MachineInstr &MI, MachineBasicBlock &SuccToSinkTo, MachineBasicBlock::iterator InsertPos, - SmallVectorImpl &DbgValuesToSink) { + SmallVectorImpl &DbgValuesToSink) { // If we cannot find a location to use (merge with), then we erase the debug // location to prevent debug-info driven tools from potentially reporting @@ -995,14 +1002,21 @@ // DBG_VALUE location as 'undef', indicating that any earlier variable // location should be terminated as we've optimised away the value at this // point. - for (SmallVectorImpl::iterator DBI = DbgValuesToSink.begin(), - DBE = DbgValuesToSink.end(); - DBI != DBE; ++DBI) { - MachineInstr *DbgMI = *DBI; - MachineInstr *NewDbgMI = DbgMI->getMF()->CloneMachineInstr(*DBI); + for (auto DbgValueToSink : DbgValuesToSink) { + MachineInstr *DbgMI = DbgValueToSink.first; + MachineInstr *NewDbgMI = DbgMI->getMF()->CloneMachineInstr(DbgMI); SuccToSinkTo.insert(InsertPos, NewDbgMI); - if (!attemptDebugCopyProp(MI, *DbgMI)) + bool PropagatedAllSunkOps = true; + for (unsigned Reg : DbgValueToSink.second) { + if (DbgMI->hasDebugOperandForReg(Reg)) { + if (!attemptDebugCopyProp(MI, *DbgMI, Reg)) { + PropagatedAllSunkOps = false; + break; + } + } + } + if (!PropagatedAllSunkOps) DbgMI->setDebugValueUndef(); } } @@ -1214,7 +1228,7 @@ ++InsertPos; // Collect debug users of any vreg that this inst defines. - SmallVector DbgUsersToSink; + SmallVector DbgUsersToSink; for (auto &MO : MI.operands()) { if (!MO.isReg() || !MO.isDef() || !MO.getReg().isVirtual()) continue; @@ -1228,10 +1242,11 @@ if (User.getInt()) { // This DBG_VALUE would re-order assignments. If we can't copy-propagate // it, it can't be recovered. Set it undef. - if (!attemptDebugCopyProp(MI, *DbgMI)) + if (!attemptDebugCopyProp(MI, *DbgMI, MO.getReg())) DbgMI->setDebugValueUndef(); } else { - DbgUsersToSink.push_back(DbgMI); + DbgUsersToSink.push_back( + {DbgMI, SmallVector(1, MO.getReg())}); } } } @@ -1266,10 +1281,12 @@ // be sunk. For the rest, if they are not dominated by the block we will sink // MI into, propagate the copy source to them. SmallVector DbgDefUsers; + SmallVector DbgUseRegs; const MachineRegisterInfo &MRI = MI.getMF()->getRegInfo(); for (auto &MO : MI.operands()) { if (!MO.isReg() || !MO.isDef() || !MO.getReg().isVirtual()) continue; + DbgUseRegs.push_back(MO.getReg()); for (auto &User : MRI.use_instructions(MO.getReg())) { if (!User.isDebugValue() || DT->dominates(TargetBlock, User.getParent())) continue; @@ -1278,8 +1295,8 @@ if (User.getParent() == MI.getParent()) continue; - assert(User.getDebugOperand(0).isReg() && - "DBG_VALUE user of vreg, but non reg operand?"); + assert(User.hasDebugOperandForReg(MO.getReg()) && + "DBG_VALUE user of vreg, but has no operand for it?"); DbgDefUsers.push_back(&User); } } @@ -1287,8 +1304,12 @@ // Point the users of this copy that are no longer dominated, at the source // of the copy. for (auto *User : DbgDefUsers) { - User->getDebugOperand(0).setReg(MI.getOperand(1).getReg()); - User->getDebugOperand(0).setSubReg(MI.getOperand(1).getSubReg()); + for (auto &Reg : DbgUseRegs) { + for (auto &DbgOp : User->getDebugOperandsForReg(Reg)) { + DbgOp.setReg(MI.getOperand(1).getReg()); + DbgOp.setSubReg(MI.getOperand(1).getSubReg()); + } + } } } @@ -1351,8 +1372,10 @@ LiveRegUnits ModifiedRegUnits, UsedRegUnits; /// Track DBG_VALUEs of (unmodified) register units. Each DBG_VALUE has an - /// entry in this map for each unit it touches. - DenseMap> SeenDbgInstrs; + /// entry in this map for each unit it touches. The DBG_VALUE's entry + /// consists of a pointer to the instruction itself, and a vector of registers + /// referred to by the instruction that overlap the key register unit. + DenseMap> SeenDbgInstrs; /// Sink Copy instructions unused in the same block close to their uses in /// successors. @@ -1534,18 +1557,27 @@ // We must sink this DBG_VALUE if its operand is sunk. To avoid searching // for DBG_VALUEs later, record them when they're encountered. if (MI->isDebugValue()) { - auto &MO = MI->getDebugOperand(0); - if (MO.isReg() && Register::isPhysicalRegister(MO.getReg())) { - // Bail if we can already tell the sink would be rejected, rather - // than needlessly accumulating lots of DBG_VALUEs. - if (hasRegisterDependency(MI, UsedOpsInCopy, DefedRegsInCopy, - ModifiedRegUnits, UsedRegUnits)) - continue; - - // Record debug use of each reg unit. - SmallSet Units = getRegUnits(MO.getReg(), TRI); - for (MCRegister Reg : Units) - SeenDbgInstrs[Reg].push_back(MI); + SmallDenseMap, 4> MIUnits; + bool IsValid = true; + for (MachineOperand &MO : MI->debug_operands()) { + if (MO.isReg() && Register::isPhysicalRegister(MO.getReg())) { + // Bail if we can already tell the sink would be rejected, rather + // than needlessly accumulating lots of DBG_VALUEs. + if (hasRegisterDependency(MI, UsedOpsInCopy, DefedRegsInCopy, + ModifiedRegUnits, UsedRegUnits)) { + IsValid = false; + break; + } + + // Record debug use of each reg unit. + SmallSet RegUnits = getRegUnits(MO.getReg(), TRI); + for (MCRegister Reg : RegUnits) + MIUnits[Reg].push_back(MO.getReg()); + } + } + if (IsValid) { + for (auto RegOps : MIUnits) + SeenDbgInstrs[RegOps.first].push_back({MI, RegOps.second}); } continue; } @@ -1587,18 +1619,22 @@ // Collect DBG_VALUEs that must sink with this copy. We've previously // recorded which reg units that DBG_VALUEs read, if this instruction // writes any of those units then the corresponding DBG_VALUEs must sink. - SetVector DbgValsToSinkSet; + MapVector DbgValsToSinkMap; for (auto &MO : MI->operands()) { if (!MO.isReg() || !MO.isDef()) continue; SmallSet Units = getRegUnits(MO.getReg(), TRI); - for (MCRegister Reg : Units) - for (auto *MI : SeenDbgInstrs.lookup(Reg)) - DbgValsToSinkSet.insert(MI); + for (MCRegister Reg : Units) { + for (auto MIRegs : SeenDbgInstrs.lookup(Reg)) { + auto &Regs = DbgValsToSinkMap[MIRegs.first]; + for (unsigned Reg : MIRegs.second) + Regs.push_back(Reg); + } + } } - SmallVector DbgValsToSink(DbgValsToSinkSet.begin(), - DbgValsToSinkSet.end()); + SmallVector DbgValsToSink(DbgValsToSinkMap.begin(), + DbgValsToSinkMap.end()); // Clear the kill flag if SrcReg is killed between MI and the end of the // block. diff --git a/llvm/lib/CodeGen/PrologEpilogInserter.cpp b/llvm/lib/CodeGen/PrologEpilogInserter.cpp --- a/llvm/lib/CodeGen/PrologEpilogInserter.cpp +++ b/llvm/lib/CodeGen/PrologEpilogInserter.cpp @@ -185,7 +185,8 @@ break; if (!MI.isDebugValue() || !MI.getDebugVariable()->isParameter()) continue; - if (MI.getDebugOperand(0).isFI()) { + if (any_of(MI.debug_operands(), + [](const MachineOperand &MO) { return MO.isFI(); })) { // We can only emit valid locations for frame indices after the frame // setup, so do not stash away them. FrameIndexValues.push_back(&MI); @@ -1203,16 +1204,19 @@ // way with simply the frame index and offset rather than any // target-specific addressing mode. if (MI.isDebugValue()) { - assert(i == 0 && "Frame indices can only appear as the first " - "operand of a DBG_VALUE machine instruction"); + MachineOperand &Op = MI.getOperand(i); + assert( + MI.isDebugOperand(&Op) && + "Frame indices can only appear as a debug operand in a DBG_VALUE*" + " machine instruction"); Register Reg; - unsigned FrameIdx = MI.getOperand(0).getIndex(); + unsigned FrameIdx = Op.getIndex(); unsigned Size = MF.getFrameInfo().getObjectSize(FrameIdx); StackOffset Offset = TFI->getFrameIndexReference(MF, FrameIdx, Reg); - MI.getOperand(0).ChangeToRegister(Reg, false /*isDef*/); - MI.getOperand(0).setIsDebug(); + Op.ChangeToRegister(Reg, false /*isDef*/); + Op.setIsDebug(); const DIExpression *DIExpr = MI.getDebugExpression(); @@ -1221,23 +1225,33 @@ // complex location that is interpreted as being a memory address. // This changes a pointer-valued variable to dereference that pointer, // which is incorrect. Fix by adding DW_OP_stack_value. - unsigned PrependFlags = DIExpression::ApplyOffset; - if (!MI.isIndirectDebugValue() && !DIExpr->isComplex()) - PrependFlags |= DIExpression::StackValue; - - // If we have DBG_VALUE that is indirect and has a Implicit location - // expression need to insert a deref before prepending a Memory - // location expression. Also after doing this we change the DBG_VALUE - // to be direct. - if (MI.isIndirectDebugValue() && DIExpr->isImplicit()) { - SmallVector Ops = {dwarf::DW_OP_deref_size, Size}; - bool WithStackValue = true; - DIExpr = DIExpression::prependOpcodes(DIExpr, Ops, WithStackValue); - // Make the DBG_VALUE direct. - MI.getDebugOffset().ChangeToRegister(0, false); - } - - DIExpr = TRI.prependOffsetExpression(DIExpr, PrependFlags, Offset); + + if (MI.isNonListDebugValue()) { + unsigned PrependFlags = DIExpression::ApplyOffset; + if (!MI.isIndirectDebugValue() && !DIExpr->isComplex()) + PrependFlags |= DIExpression::StackValue; + + // If we have DBG_VALUE that is indirect and has a Implicit location + // expression need to insert a deref before prepending a Memory + // location expression. Also after doing this we change the DBG_VALUE + // to be direct. + if (MI.isIndirectDebugValue() && DIExpr->isImplicit()) { + SmallVector Ops = {dwarf::DW_OP_deref_size, Size}; + bool WithStackValue = true; + DIExpr = DIExpression::prependOpcodes(DIExpr, Ops, WithStackValue); + // Make the DBG_VALUE direct. + MI.getDebugOffset().ChangeToRegister(0, false); + } + DIExpr = TRI.prependOffsetExpression(DIExpr, PrependFlags, Offset); + } else { + // The debug operand at DebugOpIndex was a frame index at offset + // `Offset`; now the operand has been replaced with the frame + // register, we must add Offset with `register x, plus Offset`. + unsigned DebugOpIndex = MI.getDebugOperandIndex(&Op); + SmallVector Ops; + TRI.getOffsetOpcodes(Offset, Ops); + DIExpr = DIExpression::appendOpsToArg(DIExpr, Ops, DebugOpIndex); + } MI.getDebugExpressionOp().setMetadata(DIExpr); continue; } diff --git a/llvm/lib/CodeGen/RegAllocFast.cpp b/llvm/lib/CodeGen/RegAllocFast.cpp --- a/llvm/lib/CodeGen/RegAllocFast.cpp +++ b/llvm/lib/CodeGen/RegAllocFast.cpp @@ -408,7 +408,8 @@ // to just reference the stack slot. SmallVectorImpl &LRIDbgValues = LiveDbgValueMap[VirtReg]; for (MachineInstr *DBG : LRIDbgValues) { - MachineInstr *NewDV = buildDbgValueForSpill(*MBB, Before, *DBG, FI); + MachineInstr *NewDV = + buildDbgValueForSpill(*MBB, Before, *DBG, FI, AssignedReg); assert(NewDV->getParent() == MBB && "dangling parent pointer"); (void)NewDV; LLVM_DEBUG(dbgs() << "Inserting debug info due to spill:\n" << *NewDV); @@ -424,9 +425,14 @@ } // Rewrite unassigned dbg_values to use the stack slot. - MachineOperand &MO = DBG->getOperand(0); - if (MO.isReg() && MO.getReg() == 0) - updateDbgValueForSpill(*DBG, FI); + // TODO We can potentially do this for list debug values as well if we know + // how the dbg_values are getting unassigned. + if (DBG->isNonListDebugValue()) { + MachineOperand &MO = DBG->getDebugOperand(0); + if (MO.isReg() && MO.getReg() == 0) { + updateDbgValueForSpill(*DBG, FI, 0); + } + } } // Now this register is spilled there is should not be any DBG_VALUE // pointing to this register because they are all pointing to spilled value @@ -623,8 +629,7 @@ SmallVectorImpl &Dangling = UDBGValIter->second; for (MachineInstr *DbgValue : Dangling) { assert(DbgValue->isDebugValue()); - MachineOperand &MO = DbgValue->getOperand(0); - if (!MO.isReg()) + if (!DbgValue->hasDebugOperandForReg(VirtReg)) continue; // Test whether the physreg survives from the definition to the DBG_VALUE. @@ -639,9 +644,11 @@ break; } } - MO.setReg(SetToReg); - if (SetToReg != 0) - MO.setIsRenamable(); + for (MachineOperand &MO : DbgValue->getDebugOperandsForReg(VirtReg)) { + MO.setReg(SetToReg); + if (SetToReg != 0) + MO.setIsRenamable(); + } } Dangling.clear(); } @@ -1361,37 +1368,44 @@ } void RegAllocFast::handleDebugValue(MachineInstr &MI) { - MachineOperand &MO = MI.getDebugOperand(0); - - // Ignore DBG_VALUEs that aren't based on virtual registers. These are - // mostly constants and frame indices. - if (!MO.isReg()) - return; - Register Reg = MO.getReg(); - if (!Register::isVirtualRegister(Reg)) - return; - - // Already spilled to a stackslot? - int SS = StackSlotForVirtReg[Reg]; - if (SS != -1) { - // Modify DBG_VALUE now that the value is in a spill slot. - updateDbgValueForSpill(MI, SS); - LLVM_DEBUG(dbgs() << "Rewrite DBG_VALUE for spilled memory: " << MI); - return; + SmallSet SeenRegisters; + for (MachineOperand &MO : MI.debug_operands()) { + // Ignore DBG_VALUEs that aren't based on virtual registers. These are + // mostly constants and frame indices. + if (!MO.isReg()) + continue; + Register Reg = MO.getReg(); + if (!Register::isVirtualRegister(Reg)) + continue; + // Only process each register once per MI, each use of that register will + // be updated if necessary. + if (!SeenRegisters.insert(Reg).second) + continue; + + // Already spilled to a stackslot? + int SS = StackSlotForVirtReg[Reg]; + if (SS != -1) { + // Modify DBG_VALUE now that the value is in a spill slot. + updateDbgValueForSpill(MI, SS, Reg); + LLVM_DEBUG(dbgs() << "Rewrite DBG_VALUE for spilled memory: " << MI); + continue; + } + + // See if this virtual register has already been allocated to a physical + // register or spilled to a stack slot. + LiveRegMap::iterator LRI = findLiveVirtReg(Reg); + if (LRI != LiveVirtRegs.end() && LRI->PhysReg) { + // Update every use of Reg within MI. + for (auto &RegMO : MI.getDebugOperandsForReg(Reg)) + setPhysReg(MI, RegMO, LRI->PhysReg); + } else { + DanglingDbgValues[Reg].push_back(&MI); + } + + // If Reg hasn't been spilled, put this DBG_VALUE in LiveDbgValueMap so + // that future spills of Reg will have DBG_VALUEs. + LiveDbgValueMap[Reg].push_back(&MI); } - - // See if this virtual register has already been allocated to a physical - // register or spilled to a stack slot. - LiveRegMap::iterator LRI = findLiveVirtReg(Reg); - if (LRI != LiveVirtRegs.end() && LRI->PhysReg) { - setPhysReg(MI, MO, LRI->PhysReg); - } else { - DanglingDbgValues[Reg].push_back(&MI); - } - - // If Reg hasn't been spilled, put this DBG_VALUE in LiveDbgValueMap so - // that future spills of Reg will have DBG_VALUEs. - LiveDbgValueMap[Reg].push_back(&MI); } void RegAllocFast::handleBundle(MachineInstr &MI) { @@ -1473,13 +1487,12 @@ for (auto &UDBGPair : DanglingDbgValues) { for (MachineInstr *DbgValue : UDBGPair.second) { assert(DbgValue->isDebugValue() && "expected DBG_VALUE"); - MachineOperand &MO = DbgValue->getOperand(0); // Nothing to do if the vreg was spilled in the meantime. - if (!MO.isReg()) + if (!DbgValue->hasDebugOperandForReg(UDBGPair.first)) continue; LLVM_DEBUG(dbgs() << "Register did not survive for " << *DbgValue << '\n'); - MO.setReg(0); + DbgValue->setDebugValueUndef(); } } DanglingDbgValues.clear(); diff --git a/llvm/lib/CodeGen/RegisterCoalescer.cpp b/llvm/lib/CodeGen/RegisterCoalescer.cpp --- a/llvm/lib/CodeGen/RegisterCoalescer.cpp +++ b/llvm/lib/CodeGen/RegisterCoalescer.cpp @@ -3565,8 +3565,12 @@ // After collecting a block of DBG_VALUEs into ToInsert, enter them into the // vreg => DbgValueLoc map. auto CloseNewDVRange = [this, &ToInsert](SlotIndex Slot) { - for (auto *X : ToInsert) - DbgVRegToValues[X->getDebugOperand(0).getReg()].push_back({Slot, X}); + for (auto *X : ToInsert) { + for (auto Op : X->debug_operands()) { + if (Op.isReg() && Op.getReg().isVirtual()) + DbgVRegToValues[Op.getReg()].push_back({Slot, X}); + } + } ToInsert.clear(); }; @@ -3578,9 +3582,11 @@ SlotIndex CurrentSlot = Slots.getMBBStartIdx(&MBB); for (auto &MI : MBB) { - if (MI.isDebugValue() && MI.getDebugOperand(0).isReg() && - MI.getDebugOperand(0).getReg().isVirtual()) { - ToInsert.push_back(&MI); + if (MI.isDebugValue()) { + if (any_of(MI.debug_operands(), [](const MachineOperand &MO) { + return MO.isReg() && MO.getReg().isVirtual(); + })) + ToInsert.push_back(&MI); } else if (!MI.isDebugInstr()) { CurrentSlot = Slots.getInstructionIndex(MI); CloseNewDVRange(CurrentSlot); @@ -3677,12 +3683,14 @@ if (DbgValueSetIt->first < SegmentIt->end) { // "Other" is live and there is a DBG_VALUE of Reg: test if we should // set it undef. - if (DbgValueSetIt->first >= SegmentIt->start && - DbgValueSetIt->second->getDebugOperand(0).getReg() != 0 && - ShouldUndef(DbgValueSetIt->first)) { - // Mark undef, erase record of this DBG_VALUE to avoid revisiting. - DbgValueSetIt->second->setDebugValueUndef(); - continue; + if (DbgValueSetIt->first >= SegmentIt->start) { + bool HasReg = DbgValueSetIt->second->hasDebugOperandForReg(Reg); + bool ShouldUndefReg = ShouldUndef(DbgValueSetIt->first); + if (HasReg && ShouldUndefReg) { + // Mark undef, erase record of this DBG_VALUE to avoid revisiting. + DbgValueSetIt->second->setDebugValueUndef(); + continue; + } } ++DbgValueSetIt; } else { diff --git a/llvm/lib/CodeGen/SelectionDAG/InstrEmitter.h b/llvm/lib/CodeGen/SelectionDAG/InstrEmitter.h --- a/llvm/lib/CodeGen/SelectionDAG/InstrEmitter.h +++ b/llvm/lib/CodeGen/SelectionDAG/InstrEmitter.h @@ -25,6 +25,7 @@ class MCInstrDesc; class SDDbgLabel; class SDDbgValue; +class SDDbgOperand; class TargetLowering; class LLVM_LIBRARY_VISIBILITY InstrEmitter { @@ -107,6 +108,11 @@ /// (which do not go into the machine instrs.) static unsigned CountResults(SDNode *Node); + void AddDbgValueLocationOps(MachineInstrBuilder &MIB, + const MCInstrDesc &DbgValDesc, + ArrayRef Locations, + DenseMap &VRBaseMap); + /// EmitDbgValue - Generate machine instruction for a dbg_value node. /// MachineInstr *EmitDbgValue(SDDbgValue *SD, @@ -147,7 +153,6 @@ void EmitSpecialNode(SDNode *Node, bool IsClone, bool IsCloned, DenseMap &VRBaseMap); }; - -} +} // namespace llvm #endif diff --git a/llvm/lib/CodeGen/SelectionDAG/InstrEmitter.cpp b/llvm/lib/CodeGen/SelectionDAG/InstrEmitter.cpp --- a/llvm/lib/CodeGen/SelectionDAG/InstrEmitter.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/InstrEmitter.cpp @@ -684,6 +684,28 @@ SD->setIsEmitted(); + ArrayRef LocationOps = SD->getLocationOps(); + assert(!LocationOps.empty() && "dbg_value with no location operands?"); + + // Emit variadic dbg_value nodes as DBG_VALUE_LIST. + if (SD->isVariadic()) { + // DBG_VALUE_LIST := "DBG_VALUE_LIST" var, expression, loc (, loc)* + const MCInstrDesc &DbgValDesc = TII->get(TargetOpcode::DBG_VALUE_LIST); + // Build the DBG_VALUE_LIST instruction base. + auto MIB = BuildMI(*MF, DL, DbgValDesc); + MIB.addMetadata(Var); + MIB.addMetadata(Expr); + if (SD->isInvalidated()) { + // At least one node is no longer computed so we are unable to emit the + // location. Simply mark all of the operands as undef. + for (size_t i = 0; i < LocationOps.size(); ++i) + MIB.addReg(0U, RegState::Debug); + } else { + AddDbgValueLocationOps(MIB, DbgValDesc, LocationOps, VRBaseMap); + } + return &*MIB; + } + if (SD->isInvalidated()) { // An invalidated SDNode must generate an undef DBG_VALUE: although the // original value is no longer computed, earlier DBG_VALUEs live ranges @@ -697,87 +719,94 @@ } // Attempt to produce a DBG_INSTR_REF if we've been asked to. + // We currently exclude the possibility of instruction references for + // variadic nodes; if at some point we enable them, this should be moved + // above the variadic block. if (EmitDebugInstrRefs) if (auto *InstrRef = EmitDbgInstrRef(SD, VRBaseMap)) return InstrRef; - if (SD->getKind() == SDDbgValue::FRAMEIX) { - // Stack address; this needs to be lowered in target-dependent fashion. - // EmitTargetCodeForFrameDebugValue is responsible for allocation. - auto FrameMI = BuildMI(*MF, DL, TII->get(TargetOpcode::DBG_VALUE)) - .addFrameIndex(SD->getFrameIx()); - if (SD->isIndirect()) - // Push [fi + 0] onto the DIExpression stack. - FrameMI.addImm(0); - else - // Push fi onto the DIExpression stack. - FrameMI.addReg(0); - return FrameMI.addMetadata(Var).addMetadata(Expr); - } - // Otherwise, we're going to create an instruction here. - const MCInstrDesc &II = TII->get(TargetOpcode::DBG_VALUE); - MachineInstrBuilder MIB = BuildMI(*MF, DL, II); - if (SD->getKind() == SDDbgValue::SDNODE) { - SDNode *Node = SD->getSDNode(); - SDValue Op = SDValue(Node, SD->getResNo()); - // It's possible we replaced this SDNode with other(s) and therefore - // didn't generate code for it. It's better to catch these cases where - // they happen and transfer the debug info, but trying to guarantee that - // in all cases would be very fragile; this is a safeguard for any - // that were missed. - DenseMap::iterator I = VRBaseMap.find(Op); - if (I==VRBaseMap.end()) - MIB.addReg(0U); // undef - else - AddOperand(MIB, Op, (*MIB).getNumOperands(), &II, VRBaseMap, - /*IsDebug=*/true, /*IsClone=*/false, /*IsCloned=*/false); - } else if (SD->getKind() == SDDbgValue::VREG) { - MIB.addReg(SD->getVReg(), RegState::Debug); - } else if (SD->getKind() == SDDbgValue::CONST) { - const Value *V = SD->getConst(); - if (const ConstantInt *CI = dyn_cast(V)) { - if (CI->getBitWidth() > 64) - MIB.addCImm(CI); - else - MIB.addImm(CI->getSExtValue()); - } else if (const ConstantFP *CF = dyn_cast(V)) { - MIB.addFPImm(CF); - } else if (isa(V)) { - // Note: This assumes that all nullptr constants are zero-valued. - MIB.addImm(0); - } else { - // Could be an Undef. In any case insert an Undef so we can see what we - // dropped. - MIB.addReg(0U); - } - } else { - // Insert an Undef so we can see what we dropped. - MIB.addReg(0U); - } - - // Indirect addressing is indicated by an Imm as the second parameter. + // Emit non-variadic dbg_value nodes as DBG_VALUE. + // DBG_VALUE := "DBG_VALUE" loc, isIndirect, var, expr + const MCInstrDesc &DbgValDesc = TII->get(TargetOpcode::DBG_VALUE); + // Build the DBG_VALUE instruction base. + auto MIB = BuildMI(*MF, DL, DbgValDesc); + // Add the single location componenet 'loc'. + assert(LocationOps.size() == 1 && + "Non variadic dbg_value should have only one location op"); + AddDbgValueLocationOps(MIB, DbgValDesc, LocationOps, VRBaseMap); + // Add 'isIndirect'. if (SD->isIndirect()) MIB.addImm(0U); else MIB.addReg(0U, RegState::Debug); - MIB.addMetadata(Var); MIB.addMetadata(Expr); - return &*MIB; } +void InstrEmitter::AddDbgValueLocationOps( + MachineInstrBuilder &MIB, const MCInstrDesc &DbgValDesc, + ArrayRef LocationOps, + DenseMap &VRBaseMap) { + for (const SDDbgOperand &Op : LocationOps) { + switch (Op.getKind()) { + case SDDbgOperand::FRAMEIX: + MIB.addFrameIndex(Op.getFrameIx()); + break; + case SDDbgOperand::VREG: + MIB.addReg(Op.getVReg(), RegState::Debug); + break; + case SDDbgOperand::SDNODE: { + SDValue V = SDValue(Op.getSDNode(), Op.getResNo()); + // It's possible we replaced this SDNode with other(s) and therefore + // didn't generate code for it. It's better to catch these cases where + // they happen and transfer the debug info, but trying to guarantee that + // in all cases would be very fragile; this is a safeguard for any + // that were missed. + if (VRBaseMap.count(V) == 0) + MIB.addReg(0U); // undef + else + AddOperand(MIB, V, (*MIB).getNumOperands(), &DbgValDesc, VRBaseMap, + /*IsDebug=*/true, /*IsClone=*/false, /*IsCloned=*/false); + } break; + case SDDbgOperand::CONST: { + const Value *V = Op.getConst(); + if (const ConstantInt *CI = dyn_cast(V)) { + if (CI->getBitWidth() > 64) + MIB.addCImm(CI); + else + MIB.addImm(CI->getSExtValue()); + } else if (const ConstantFP *CF = dyn_cast(V)) { + MIB.addFPImm(CF); + } else if (isa(V)) { + // Note: This assumes that all nullptr constants are zero-valued. + MIB.addImm(0); + } else { + // Could be an Undef. In any case insert an Undef so we can see what we + // dropped. + MIB.addReg(0U); + } + } break; + } + } +} + MachineInstr * InstrEmitter::EmitDbgInstrRef(SDDbgValue *SD, DenseMap &VRBaseMap) { + // No support for instruction references for variadic values yet. + if (SD->isVariadic()) + return nullptr; + SDDbgOperand DbgOperand = SD->getLocationOps()[0]; // Instruction referencing is still in a prototype state: for now we're only // going to support SDNodes within a block. Copies are not supported, they // don't actually define a value. - if (SD->getKind() != SDDbgValue::SDNODE) + if (DbgOperand.getKind() != SDDbgOperand::SDNODE) return nullptr; - SDNode *Node = SD->getSDNode(); - SDValue Op = SDValue(Node, SD->getResNo()); + SDNode *Node = DbgOperand.getSDNode(); + SDValue Op = SDValue(Node, DbgOperand.getResNo()); DenseMap::iterator I = VRBaseMap.find(Op); if (I==VRBaseMap.end()) return nullptr; // undef value: let EmitDbgValue produce a DBG_VALUE $noreg. diff --git a/llvm/lib/CodeGen/SelectionDAG/SDNodeDbgValue.h b/llvm/lib/CodeGen/SelectionDAG/SDNodeDbgValue.h --- a/llvm/lib/CodeGen/SelectionDAG/SDNodeDbgValue.h +++ b/llvm/lib/CodeGen/SelectionDAG/SDNodeDbgValue.h @@ -23,96 +23,163 @@ class DIExpression; class SDNode; class Value; -class raw_ostream; - -/// Holds the information from a dbg_value node through SDISel. -/// We do not use SDValue here to avoid including its header. -class SDDbgValue { -public: - enum DbgValueKind { - SDNODE = 0, ///< Value is the result of an expression. - CONST = 1, ///< Value is a constant. - FRAMEIX = 2, ///< Value is contents of a stack location. - VREG = 3 ///< Value is a virtual register. - }; -private: +class raw_ostream; + +/// Holds the information for a single machine location through SDISel; either +/// an SDNode, a constant, a stack location, or a virtual register. +class SDDbgOperand { +public: + enum Kind { + SDNODE = 0, ///< Value is the result of an expression. + CONST = 1, ///< Value is a constant. + FRAMEIX = 2, ///< Value is contents of a stack location. + VREG = 3 ///< Value is a virtual register. + }; + Kind getKind() const { return kind; } + + /// Returns the SDNode* for a register ref + SDNode *getSDNode() const { + assert(kind == SDNODE); + return u.s.Node; + } + + /// Returns the ResNo for a register ref + unsigned getResNo() const { + assert(kind == SDNODE); + return u.s.ResNo; + } + + /// Returns the Value* for a constant + const Value *getConst() const { + assert(kind == CONST); + return u.Const; + } + + /// Returns the FrameIx for a stack object + unsigned getFrameIx() const { + assert(kind == FRAMEIX); + return u.FrameIx; + } + + /// Returns the Virtual Register for a VReg + unsigned getVReg() const { + assert(kind == VREG); + return u.VReg; + } + + static SDDbgOperand fromNode(SDNode *Node, unsigned ResNo) { + return SDDbgOperand(Node, ResNo); + } + static SDDbgOperand fromFrameIdx(unsigned FrameIdx) { + return SDDbgOperand(FrameIdx, FRAMEIX); + } + static SDDbgOperand fromVReg(unsigned VReg) { + return SDDbgOperand(VReg, VREG); + } + static SDDbgOperand fromConst(const Value *Const) { + return SDDbgOperand(Const); + } + + bool operator!=(const SDDbgOperand &Other) const { return !(*this == Other); } + bool operator==(const SDDbgOperand &Other) const { + if (kind != Other.kind) + return false; + switch (kind) { + case SDNODE: + return getSDNode() == Other.getSDNode() && getResNo() == Other.getResNo(); + case CONST: + return getConst() == Other.getConst(); + case VREG: + return getVReg() == Other.getVReg(); + case FRAMEIX: + return getFrameIx() == Other.getFrameIx(); + default: + llvm_unreachable("unknown kind"); + } + } + +private: + Kind kind; union { - struct { - SDNode *Node; ///< Valid for expressions. - unsigned ResNo; ///< Valid for expressions. - } s; - const Value *Const; ///< Valid for constants. - unsigned FrameIx; ///< Valid for stack objects. - unsigned VReg; ///< Valid for registers. + struct { + SDNode *Node; ///< Valid for expressions. + unsigned ResNo; ///< Valid for expressions. + } s; + const Value *Const; ///< Valid for constants. + unsigned FrameIx; ///< Valid for stack objects. + unsigned VReg; ///< Valid for registers. } u; - DIVariable *Var; - DIExpression *Expr; - DebugLoc DL; - unsigned Order; - enum DbgValueKind kind; - bool IsIndirect; - bool Invalid = false; - bool Emitted = false; -public: - /// Constructor for non-constants. - SDDbgValue(DIVariable *Var, DIExpression *Expr, SDNode *N, unsigned R, - bool indir, DebugLoc dl, unsigned O) - : Var(Var), Expr(Expr), DL(std::move(dl)), Order(O), IsIndirect(indir) { - kind = SDNODE; + /// Constructor for non-constants. + SDDbgOperand(SDNode *N, unsigned R) : kind(SDNODE) { u.s.Node = N; - u.s.ResNo = R; - } - - /// Constructor for constants. - SDDbgValue(DIVariable *Var, DIExpression *Expr, const Value *C, DebugLoc dl, - unsigned O) - : Var(Var), Expr(Expr), DL(std::move(dl)), Order(O), IsIndirect(false) { - kind = CONST; - u.Const = C; - } - - /// Constructor for virtual registers and frame indices. - SDDbgValue(DIVariable *Var, DIExpression *Expr, unsigned VRegOrFrameIdx, - bool IsIndirect, DebugLoc DL, unsigned Order, - enum DbgValueKind Kind) - : Var(Var), Expr(Expr), DL(DL), Order(Order), IsIndirect(IsIndirect) { + u.s.ResNo = R; + } + /// Constructor for constants. + SDDbgOperand(const Value *C) : kind(CONST) { u.Const = C; } + /// Constructor for virtual registers and frame indices. + SDDbgOperand(unsigned VRegOrFrameIdx, Kind Kind) : kind(Kind) { assert((Kind == VREG || Kind == FRAMEIX) && "Invalid SDDbgValue constructor"); - kind = Kind; if (kind == VREG) u.VReg = VRegOrFrameIdx; else - u.FrameIx = VRegOrFrameIdx; - } - - /// Returns the kind. - DbgValueKind getKind() const { return kind; } - + u.FrameIx = VRegOrFrameIdx; + } +}; + +/// Holds the information from a dbg_value node through SDISel. +/// We do not use SDValue here to avoid including its header. +class SDDbgValue { +public: + // FIXME: These SmallVector sizes were chosen without any kind of performance + // testing. + using LocOpVector = SmallVector; + using SDNodeVector = SmallVector; + +private: + LocOpVector LocationOps; + SDNodeVector SDNodes; + DIVariable *Var; + DIExpression *Expr; + DebugLoc DL; + unsigned Order; + bool IsIndirect; + bool IsVariadic; + bool Invalid = false; + bool Emitted = false; + +public: + SDDbgValue(DIVariable *Var, DIExpression *Expr, ArrayRef L, + ArrayRef Dependencies, bool IsIndirect, DebugLoc DL, + unsigned O, bool IsVariadic) + : LocationOps(L.begin(), L.end()), + SDNodes(Dependencies.begin(), Dependencies.end()), Var(Var), Expr(Expr), + DL(DL), Order(O), IsIndirect(IsIndirect), IsVariadic(IsVariadic) { + assert(IsVariadic || L.size() == 1); + assert(!(IsVariadic && IsIndirect)); + } + /// Returns the DIVariable pointer for the variable. DIVariable *getVariable() const { return Var; } /// Returns the DIExpression pointer for the expression. - DIExpression *getExpression() const { return Expr; } - - /// Returns the SDNode* for a register ref - SDNode *getSDNode() const { assert (kind==SDNODE); return u.s.Node; } - - /// Returns the ResNo for a register ref - unsigned getResNo() const { assert (kind==SDNODE); return u.s.ResNo; } - - /// Returns the Value* for a constant - const Value *getConst() const { assert (kind==CONST); return u.Const; } - - /// Returns the FrameIx for a stack object - unsigned getFrameIx() const { assert (kind==FRAMEIX); return u.FrameIx; } - - /// Returns the Virtual Register for a VReg - unsigned getVReg() const { assert (kind==VREG); return u.VReg; } - + DIExpression *getExpression() const { return Expr; } + + ArrayRef getLocationOps() const { return LocationOps; } + + LocOpVector copyLocationOps() const { return LocationOps; } + + // Returns the SDNodes which this SDDbgValue depends on. + ArrayRef getSDNodes() const { return SDNodes; } + + SDNodeVector copySDNodes() const { return SDNodes; } + /// Returns whether this is an indirect value. - bool isIndirect() const { return IsIndirect; } - + bool isIndirect() const { return IsIndirect; } + + bool isVariadic() const { return IsVariadic; } + /// Returns the DebugLoc. DebugLoc getDebugLoc() const { return DL; } @@ -136,9 +203,9 @@ void clearIsEmitted() { Emitted = false; } LLVM_DUMP_METHOD void dump() const; - LLVM_DUMP_METHOD void print(raw_ostream &OS) const; -}; - + LLVM_DUMP_METHOD void print(raw_ostream &OS) const; +}; + /// Holds the information from a dbg_label node through SDISel. /// We do not use SDValue here to avoid including its header. class SDDbgLabel { diff --git a/llvm/lib/CodeGen/SelectionDAG/ScheduleDAGSDNodes.cpp b/llvm/lib/CodeGen/SelectionDAG/ScheduleDAGSDNodes.cpp --- a/llvm/lib/CodeGen/SelectionDAG/ScheduleDAGSDNodes.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/ScheduleDAGSDNodes.cpp @@ -739,6 +739,17 @@ if (!N->getHasDebugValue()) return; + /// Returns true if \p DV has any VReg operand locations which don't exist in + /// VRBaseMap. + auto HasUnknownVReg = [&VRBaseMap](SDDbgValue *DV) { + for (SDDbgOperand L : DV->getLocationOps()) { + if (L.getKind() == SDDbgOperand::SDNODE && + VRBaseMap.count({L.getSDNode(), L.getResNo()}) == 0) + return true; + } + return false; + }; + // Opportunistically insert immediate dbg_value uses, i.e. those with the same // source order number as N. MachineBasicBlock *BB = Emitter.getBlock(); @@ -747,13 +758,20 @@ if (DV->isEmitted()) continue; unsigned DVOrder = DV->getOrder(); - if (!Order || DVOrder == Order) { - MachineInstr *DbgMI = Emitter.EmitDbgValue(DV, VRBaseMap); - if (DbgMI) { - Orders.push_back({DVOrder, DbgMI}); - BB->insert(InsertPos, DbgMI); - } - } + if (Order != 0 && DVOrder != Order) + continue; + // If DV has any VReg location operands which haven't been mapped then + // either that node is no longer available or we just haven't visited the + // node yet. In the former case we should emit an undef dbg_value, but we + // can do it later. And for the latter we'll want to wait until all + // dependent nodes have been visited. + if (HasUnknownVReg(DV)) + continue; + MachineInstr *DbgMI = Emitter.EmitDbgValue(DV, VRBaseMap); + if (!DbgMI) + continue; + Orders.push_back({DVOrder, DbgMI}); + BB->insert(InsertPos, DbgMI); } } diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp @@ -878,6 +878,17 @@ DeallocateNode(N); } +void SDDbgInfo::add(SDDbgValue *V, bool isParameter) { + assert(!(V->isVariadic() && isParameter)); + if (isParameter) + ByvalParmDbgValues.push_back(V); + else + DbgValues.push_back(V); + for (const SDNode *Node : V->getSDNodes()) + if (Node) + DbgValMap[Node].push_back(V); +} + void SDDbgInfo::erase(const SDNode *Node) { DbgValMapType::iterator I = DbgValMap.find(Node); if (I == DbgValMap.end()) @@ -8396,7 +8407,8 @@ assert(cast(Var)->isValidLocationForIntrinsic(DL) && "Expected inlined-at fields to agree"); return new (DbgInfo->getAlloc()) - SDDbgValue(Var, Expr, N, R, IsIndirect, DL, O); + SDDbgValue(Var, Expr, SDDbgOperand::fromNode(N, R), N, IsIndirect, DL, O, + /*IsVariadic=*/false); } /// Constant @@ -8406,7 +8418,9 @@ const DebugLoc &DL, unsigned O) { assert(cast(Var)->isValidLocationForIntrinsic(DL) && "Expected inlined-at fields to agree"); - return new (DbgInfo->getAlloc()) SDDbgValue(Var, Expr, C, DL, O); + return new (DbgInfo->getAlloc()) SDDbgValue( + Var, Expr, SDDbgOperand::fromConst(C), {}, /*IsIndirect=*/false, DL, O, + /*IsVariadic=*/false); } /// FrameIndex @@ -8417,19 +8431,44 @@ unsigned O) { assert(cast(Var)->isValidLocationForIntrinsic(DL) && "Expected inlined-at fields to agree"); + return getFrameIndexDbgValue(Var, Expr, FI, {}, IsIndirect, DL, O); +} + +/// FrameIndex with dependencies +SDDbgValue *SelectionDAG::getFrameIndexDbgValue(DIVariable *Var, + DIExpression *Expr, unsigned FI, + ArrayRef Dependencies, + bool IsIndirect, + const DebugLoc &DL, + unsigned O) { + assert(cast(Var)->isValidLocationForIntrinsic(DL) && + "Expected inlined-at fields to agree"); return new (DbgInfo->getAlloc()) - SDDbgValue(Var, Expr, FI, IsIndirect, DL, O, SDDbgValue::FRAMEIX); + SDDbgValue(Var, Expr, SDDbgOperand::fromFrameIdx(FI), Dependencies, + IsIndirect, DL, O, + /*IsVariadic=*/false); } /// VReg -SDDbgValue *SelectionDAG::getVRegDbgValue(DIVariable *Var, - DIExpression *Expr, +SDDbgValue *SelectionDAG::getVRegDbgValue(DIVariable *Var, DIExpression *Expr, unsigned VReg, bool IsIndirect, const DebugLoc &DL, unsigned O) { assert(cast(Var)->isValidLocationForIntrinsic(DL) && "Expected inlined-at fields to agree"); return new (DbgInfo->getAlloc()) - SDDbgValue(Var, Expr, VReg, IsIndirect, DL, O, SDDbgValue::VREG); + SDDbgValue(Var, Expr, SDDbgOperand::fromVReg(VReg), {}, IsIndirect, DL, O, + /*IsVariadic=*/false); +} + +SDDbgValue *SelectionDAG::getDbgValueList(DIVariable *Var, DIExpression *Expr, + ArrayRef Locs, + ArrayRef Dependencies, + bool IsIndirect, const DebugLoc &DL, + unsigned O, bool IsVariadic) { + assert(cast(Var)->isValidLocationForIntrinsic(DL) && + "Expected inlined-at fields to agree"); + return new (DbgInfo->getAlloc()) + SDDbgValue(Var, Expr, Locs, Dependencies, IsIndirect, DL, O, IsVariadic); } void SelectionDAG::transferDbgValues(SDValue From, SDValue To, @@ -8448,15 +8487,31 @@ if (!FromNode->getHasDebugValue()) return; + SDDbgOperand FromLocOp = + SDDbgOperand::fromNode(From.getNode(), From.getResNo()); + SDDbgOperand ToLocOp = SDDbgOperand::fromNode(To.getNode(), To.getResNo()); + SmallVector ClonedDVs; for (SDDbgValue *Dbg : GetDbgValues(FromNode)) { - if (Dbg->getKind() != SDDbgValue::SDNODE || Dbg->isInvalidated()) + if (Dbg->isInvalidated()) continue; // TODO: assert(!Dbg->isInvalidated() && "Transfer of invalid dbg value"); - // Just transfer the dbg value attached to From. - if (Dbg->getResNo() != From.getResNo()) + // Create a new location ops vector that is equal to the old vector, but + // with each instance of FromLocOp replaced with ToLocOp. + bool Changed = false; + auto NewLocOps = Dbg->copyLocationOps(); + std::replace_if( + NewLocOps.begin(), NewLocOps.end(), + [&Changed, FromLocOp](const SDDbgOperand &Op) { + bool Match = Op == FromLocOp; + Changed |= Match; + return Match; + }, + ToLocOp); + // Ignore this SDDbgValue if we didn't find a matching location. + if (!Changed) continue; DIVariable *Var = Dbg->getVariable(); @@ -8475,10 +8530,15 @@ continue; Expr = *Fragment; } + + auto NewDependencies = Dbg->copySDNodes(); + std::replace(NewDependencies.begin(), NewDependencies.end(), FromNode, + ToNode); // Clone the SDDbgValue and move it to To. - SDDbgValue *Clone = getDbgValue( - Var, Expr, ToNode, To.getResNo(), Dbg->isIndirect(), Dbg->getDebugLoc(), - std::max(ToNode->getIROrder(), Dbg->getOrder())); + SDDbgValue *Clone = getDbgValueList( + Var, Expr, NewLocOps, NewDependencies, Dbg->isIndirect(), + Dbg->getDebugLoc(), std::max(ToNode->getIROrder(), Dbg->getOrder()), + Dbg->isVariadic()); ClonedDVs.push_back(Clone); if (InvalidateDbg) { @@ -8488,8 +8548,11 @@ } } - for (SDDbgValue *Dbg : ClonedDVs) - AddDbgValue(Dbg, ToNode, false); + for (SDDbgValue *Dbg : ClonedDVs) { + assert(is_contained(Dbg->getSDNodes(), ToNode) && + "Transferred DbgValues should depend on the new SDNode"); + AddDbgValue(Dbg, false); + } } void SelectionDAG::salvageDebugInfo(SDNode &N) { @@ -8509,16 +8572,37 @@ if (!isConstantIntBuildVectorOrConstantInt(N0) && isConstantIntBuildVectorOrConstantInt(N1)) { uint64_t Offset = N.getConstantOperandVal(1); + // Rewrite an ADD constant node into a DIExpression. Since we are // performing arithmetic to compute the variable's *value* in the // DIExpression, we need to mark the expression with a // DW_OP_stack_value. auto *DIExpr = DV->getExpression(); - DIExpr = - DIExpression::prepend(DIExpr, DIExpression::StackValue, Offset); - SDDbgValue *Clone = - getDbgValue(DV->getVariable(), DIExpr, N0.getNode(), N0.getResNo(), - DV->isIndirect(), DV->getDebugLoc(), DV->getOrder()); + auto NewLocOps = DV->copyLocationOps(); + bool Changed = false; + for (size_t i = 0; i < NewLocOps.size(); ++i) { + // We're not given a ResNo to compare against because the whole + // node is going away. We know that any ISD::ADD only has one + // result, so we can assume any node match is using the result. + if (NewLocOps[i].getKind() != SDDbgOperand::SDNODE || + NewLocOps[i].getSDNode() != &N) + continue; + NewLocOps[i] = SDDbgOperand::fromNode(N0.getNode(), N0.getResNo()); + SmallVector ExprOps; + DIExpression::appendOffset(ExprOps, Offset); + DIExpr = DIExpression::appendOpsToArg(DIExpr, ExprOps, i, true); + Changed = true; + } + (void)Changed; + assert(Changed && "Salvage target doesn't use N"); + + auto NewDependencies = DV->copySDNodes(); + std::replace(NewDependencies.begin(), NewDependencies.end(), &N, + N0.getNode()); + SDDbgValue *Clone = getDbgValueList(DV->getVariable(), DIExpr, + NewLocOps, NewDependencies, + DV->isIndirect(), DV->getDebugLoc(), + DV->getOrder(), DV->isVariadic()); ClonedDVs.push_back(Clone); DV->setIsInvalidated(); DV->setIsEmitted(); @@ -8529,8 +8613,11 @@ } } - for (SDDbgValue *Dbg : ClonedDVs) - AddDbgValue(Dbg, Dbg->getSDNode(), false); + for (SDDbgValue *Dbg : ClonedDVs) { + assert(!Dbg->getSDNodes().empty() && + "Salvaged DbgValue should depend on a new SDNode"); + AddDbgValue(Dbg, false); + } } /// Creates a SDDbgLabel node. @@ -9013,17 +9100,17 @@ /// AddDbgValue - Add a dbg_value SDNode. If SD is non-null that means the /// value is produced by SD. -void SelectionDAG::AddDbgValue(SDDbgValue *DB, SDNode *SD, bool isParameter) { - if (SD) { +void SelectionDAG::AddDbgValue(SDDbgValue *DB, bool isParameter) { + for (SDNode *SD : DB->getSDNodes()) { + if (!SD) + continue; assert(DbgInfo->getSDDbgValues(SD).empty() || SD->getHasDebugValue()); SD->setHasDebugValue(true); } - DbgInfo->add(DB, SD, isParameter); + DbgInfo->add(DB, isParameter); } -void SelectionDAG::AddDbgLabel(SDDbgLabel *DB) { - DbgInfo->add(DB); -} +void SelectionDAG::AddDbgLabel(SDDbgLabel *DB) { DbgInfo->add(DB); } SDValue SelectionDAG::makeEquivalentMemoryOrdering(SDValue OldChain, SDValue NewMemOpChain) { diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.h b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.h --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.h +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.h @@ -492,6 +492,10 @@ /// of the specified type Ty. Return empty SDValue() otherwise. SDValue getCopyFromRegs(const Value *V, Type *Ty); + /// Register a dbg_value which relies on a Value which we have not yet seen. + void addDanglingDebugInfo(const DbgValueInst *DI, DebugLoc DL, + unsigned Order); + /// If we have dangling debug info that describes \p Variable, or an /// overlapping part of variable considering the \p Expr, then this method /// will drop that debug info as it isn't valid any longer. @@ -507,11 +511,11 @@ /// this cannot be done, produce an Undef debug value record. void salvageUnresolvedDbgValue(DanglingDebugInfo &DDI); - /// For a given Value, attempt to create and record a SDDbgValue in the - /// SelectionDAG. - bool handleDebugValue(const Value *V, DILocalVariable *Var, - DIExpression *Expr, DebugLoc CurDL, - DebugLoc InstDL, unsigned Order); + /// For a given list of Values, attempt to create and record a SDDbgValue in + /// the SelectionDAG. + bool handleDebugValue(ArrayRef Values, DILocalVariable *Var, + DIExpression *Expr, DebugLoc CurDL, DebugLoc InstDL, + unsigned Order, bool IsVariadic); /// Evict any dangling debug information, attempting to salvage it first. void resolveOrClearDbgInfo(); diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp @@ -1119,6 +1119,33 @@ } } +void SelectionDAGBuilder::addDanglingDebugInfo(const DbgValueInst *DI, + DebugLoc DL, unsigned Order) { + // We treat variadic dbg_values differently at this stage. + if (DI->hasArgList()) { + // For variadic dbg_values we will now insert an undef. + // FIXME: We can potentially recover these! + SmallVector Locs; + for (const Value *V : DI->getValues()) { + auto Undef = UndefValue::get(V->getType()); + Locs.push_back(SDDbgOperand::fromConst(Undef)); + } + SDDbgValue *SDV = DAG.getDbgValueList( + DI->getVariable(), DI->getExpression(), Locs, {}, + /*IsIndirect=*/false, DL, Order, /*IsVariadic=*/true); + DAG.AddDbgValue(SDV, /*isParameter=*/false); + } else { + // TODO: Dangling debug info will eventually either be resolved or produce + // an Undef DBG_VALUE. However in the resolution case, a gap may appear + // between the original dbg.value location and its resolved DBG_VALUE, + // which we should ideally fill with an extra Undef DBG_VALUE. + assert(DI->getNumVariableLocationOps() == 1 && + "DbgValueInst without an ArgList should have a single location " + "operand."); + DanglingDebugInfoMap[DI->getValue(0)].emplace_back(DI, DL, Order); + } +} + void SelectionDAGBuilder::dropDanglingDebugInfo(const DILocalVariable *Variable, const DIExpression *Expr) { auto isMatchingDbgValue = [&](DanglingDebugInfo &DDI) { @@ -1156,6 +1183,7 @@ DanglingDebugInfoVector &DDIV = DanglingDbgInfoIt->second; for (auto &DDI : DDIV) { const DbgValueInst *DI = DDI.getDI(); + assert(!DI->hasArgList() && "Not implemented for variadic dbg_values"); assert(DI && "Ill-formed DanglingDebugInfo"); DebugLoc dl = DDI.getdl(); unsigned ValSDNodeOrder = Val.getNode()->getIROrder(); @@ -1185,37 +1213,41 @@ << ValSDNodeOrder << "\n"); SDV = getDbgValue(Val, Variable, Expr, dl, std::max(DbgSDNodeOrder, ValSDNodeOrder)); - DAG.AddDbgValue(SDV, Val.getNode(), false); + DAG.AddDbgValue(SDV, false); } else LLVM_DEBUG(dbgs() << "Resolved dangling debug info for " << *DI << "in EmitFuncArgumentDbgValue\n"); } else { LLVM_DEBUG(dbgs() << "Dropping debug info for " << *DI << "\n"); - auto Undef = - UndefValue::get(DDI.getDI()->getVariableLocation()->getType()); + auto Undef = UndefValue::get(DDI.getDI()->getValue(0)->getType()); auto SDV = DAG.getConstantDbgValue(Variable, Expr, Undef, dl, DbgSDNodeOrder); - DAG.AddDbgValue(SDV, nullptr, false); + DAG.AddDbgValue(SDV, false); } } DDIV.clear(); } void SelectionDAGBuilder::salvageUnresolvedDbgValue(DanglingDebugInfo &DDI) { - Value *V = DDI.getDI()->getValue(); + // TODO: For the variadic implementation, instead of only checking the fail + // state of `handleDebugValue`, we need know specifically which values were + // invalid, so that we attempt to salvage only those values when processing + // a DIArgList. + assert(!DDI.getDI()->hasArgList() && + "Not implemented for variadic dbg_values"); + Value *V = DDI.getDI()->getValue(0); DILocalVariable *Var = DDI.getDI()->getVariable(); DIExpression *Expr = DDI.getDI()->getExpression(); DebugLoc DL = DDI.getdl(); DebugLoc InstDL = DDI.getDI()->getDebugLoc(); unsigned SDOrder = DDI.getSDNodeOrder(); - // Currently we consider only dbg.value intrinsics -- we tell the salvager // that DW_OP_stack_value is desired. assert(isa(DDI.getDI())); bool StackValue = true; // Can this Value can be encoded without any further work? - if (handleDebugValue(V, Var, Expr, DL, InstDL, SDOrder)) + if (handleDebugValue(V, Var, Expr, DL, InstDL, SDOrder, /*IsVariadic=*/false)) return; // Attempt to salvage back through as many instructions as possible. Bail if @@ -1223,20 +1255,27 @@ // variable. FIXME: Further work could recover those too. while (isa(V)) { Instruction &VAsInst = *cast(V); - DIExpression *NewExpr = salvageDebugInfoImpl(VAsInst, Expr, StackValue); + // Temporary "0", awaiting real implementation. + SmallVector AdditionalValues; + DIExpression *SalvagedExpr = + salvageDebugInfoImpl(VAsInst, Expr, StackValue, 0, AdditionalValues); // If we cannot salvage any further, and haven't yet found a suitable debug // expression, bail out. - if (!NewExpr) + // TODO: If AdditionalValues isn't empty, then the salvage can only be + // represented with a DBG_VALUE_LIST, so we give up. When we have support + // here for variadic dbg_values, remove that condition. + if (!SalvagedExpr || !AdditionalValues.empty()) break; // New value and expr now represent this debuginfo. V = VAsInst.getOperand(0); - Expr = NewExpr; + Expr = SalvagedExpr; // Some kind of simplification occurred: check whether the operand of the // salvaged debug expression can be encoded in this DAG. - if (handleDebugValue(V, Var, Expr, DL, InstDL, SDOrder)) { + if (handleDebugValue(V, Var, Expr, DL, InstDL, SDOrder, + /*IsVariadic=*/false)) { LLVM_DEBUG(dbgs() << "Salvaged debug location info for:\n " << DDI.getDI() << "\nBy stripping back to:\n " << V); return; @@ -1246,9 +1285,9 @@ // This was the final opportunity to salvage this debug information, and it // couldn't be done. Place an undef DBG_VALUE at this location to terminate // any earlier variable location. - auto Undef = UndefValue::get(DDI.getDI()->getVariableLocation()->getType()); + auto Undef = UndefValue::get(DDI.getDI()->getValue(0)->getType()); auto SDV = DAG.getConstantDbgValue(Var, Expr, Undef, DL, SDNodeOrder); - DAG.AddDbgValue(SDV, nullptr, false); + DAG.AddDbgValue(SDV, false); LLVM_DEBUG(dbgs() << "Dropping debug value info for:\n " << DDI.getDI() << "\n"); @@ -1256,53 +1295,72 @@ << "\n"); } -bool SelectionDAGBuilder::handleDebugValue(const Value *V, DILocalVariable *Var, +bool SelectionDAGBuilder::handleDebugValue(ArrayRef Values, + DILocalVariable *Var, DIExpression *Expr, DebugLoc dl, - DebugLoc InstDL, unsigned Order) { - const TargetLowering &TLI = DAG.getTargetLoweringInfo(); - SDDbgValue *SDV; - if (isa(V) || isa(V) || isa(V) || - isa(V)) { - SDV = DAG.getConstantDbgValue(Var, Expr, V, dl, SDNodeOrder); - DAG.AddDbgValue(SDV, nullptr, false); + DebugLoc InstDL, unsigned Order, + bool IsVariadic) { + if (Values.empty()) return true; - } - - // If the Value is a frame index, we can create a FrameIndex debug value - // without relying on the DAG at all. - if (const AllocaInst *AI = dyn_cast(V)) { - auto SI = FuncInfo.StaticAllocaMap.find(AI); - if (SI != FuncInfo.StaticAllocaMap.end()) { - auto SDV = - DAG.getFrameIndexDbgValue(Var, Expr, SI->second, - /*IsIndirect*/ false, dl, SDNodeOrder); - // Do not attach the SDNodeDbgValue to an SDNode: this variable location - // is still available even if the SDNode gets optimized out. - DAG.AddDbgValue(SDV, nullptr, false); - return true; - } - } + SDDbgValue::LocOpVector LocationOps; + SDDbgValue::SDNodeVector Dependencies; + for (const Value *V : Values) { + // Constant value. + if (isa(V) || isa(V) || isa(V) || + isa(V)) { + LocationOps.emplace_back(SDDbgOperand::fromConst(V)); + continue; + } + + // If the Value is a frame index, we can create a FrameIndex debug value + // without relying on the DAG at all. + if (const AllocaInst *AI = dyn_cast(V)) { + auto SI = FuncInfo.StaticAllocaMap.find(AI); + if (SI != FuncInfo.StaticAllocaMap.end()) { + LocationOps.emplace_back(SDDbgOperand::fromFrameIdx(SI->second)); + continue; + } + } + + // Do not use getValue() in here; we don't want to generate code at + // this point if it hasn't been done yet. + SDValue N = NodeMap[V]; + if (!N.getNode() && isa(V)) // Check unused arguments map. + N = UnusedArgNodeMap[V]; + if (N.getNode()) { + // Only emit func arg dbg value for non-variadic dbg.values for now. + if (!IsVariadic && EmitFuncArgumentDbgValue(V, Var, Expr, dl, false, N)) + return true; + Dependencies.push_back(N.getNode()); + if (auto *FISDN = dyn_cast(N.getNode())) { + // Construct a FrameIndexDbgValue for FrameIndexSDNodes so we can + // describe stack slot locations. + // + // Consider "int x = 0; int *px = &x;". There are two kinds of + // interesting debug values here after optimization: + // + // dbg.value(i32* %px, !"int *px", !DIExpression()), and + // dbg.value(i32* %px, !"int x", !DIExpression(DW_OP_deref)) + // + // Both describe the direct values of their associated variables. + LocationOps.emplace_back(SDDbgOperand::fromFrameIdx(FISDN->getIndex())); + continue; + } + LocationOps.emplace_back( + SDDbgOperand::fromNode(N.getNode(), N.getResNo())); + continue; + } + + const TargetLowering &TLI = DAG.getTargetLoweringInfo(); + // Special rules apply for the first dbg.values of parameter variables in a + // function. Identify them by the fact they reference Argument Values, that + // they're parameters, and they are parameters of the current function. We + // need to let them dangle until they get an SDNode. + bool IsParamOfFunc = + isa(V) && Var->isParameter() && !InstDL.getInlinedAt(); + if (IsParamOfFunc) + return false; - // Do not use getValue() in here; we don't want to generate code at - // this point if it hasn't been done yet. - SDValue N = NodeMap[V]; - if (!N.getNode() && isa(V)) // Check unused arguments map. - N = UnusedArgNodeMap[V]; - if (N.getNode()) { - if (EmitFuncArgumentDbgValue(V, Var, Expr, dl, false, N)) - return true; - SDV = getDbgValue(N, Var, Expr, dl, SDNodeOrder); - DAG.AddDbgValue(SDV, N.getNode(), false); - return true; - } - - // Special rules apply for the first dbg.values of parameter variables in a - // function. Identify them by the fact they reference Argument Values, that - // they're parameters, and they are parameters of the current function. We - // need to let them dangle until they get an SDNode. - bool IsParamOfFunc = isa(V) && Var->isParameter() && - !InstDL.getInlinedAt(); - if (!IsParamOfFunc) { // The value is not used in this block yet (or it would have an SDNode). // We still want the value to appear for the user if possible -- if it has // an associated VReg, we can refer to that instead. @@ -1314,6 +1372,9 @@ RegsForValue RFV(V->getContext(), TLI, DAG.getDataLayout(), Reg, V->getType(), None); if (RFV.occupiesMultipleRegs()) { + // FIXME: We could potentially support variadic dbg_values here. + if (IsVariadic) + return false; unsigned Offset = 0; unsigned BitsToDescribe = 0; if (auto VarSize = Var->getSizeInBits()) @@ -1326,26 +1387,35 @@ if (Offset >= BitsToDescribe) break; unsigned FragmentSize = (Offset + RegisterSize > BitsToDescribe) - ? BitsToDescribe - Offset - : RegisterSize; + ? BitsToDescribe - Offset + : RegisterSize; auto FragmentExpr = DIExpression::createFragmentExpression( Expr, Offset, FragmentSize); if (!FragmentExpr) - continue; - SDV = DAG.getVRegDbgValue(Var, *FragmentExpr, RegAndSize.first, - false, dl, SDNodeOrder); - DAG.AddDbgValue(SDV, nullptr, false); + continue; + SDDbgValue *SDV = DAG.getVRegDbgValue( + Var, *FragmentExpr, RegAndSize.first, false, dl, SDNodeOrder); + DAG.AddDbgValue(SDV, false); Offset += RegisterSize; } - } else { - SDV = DAG.getVRegDbgValue(Var, Expr, Reg, false, dl, SDNodeOrder); - DAG.AddDbgValue(SDV, nullptr, false); + return true; } - return true; + // We can use simple vreg locations for variadic dbg_values as well. + LocationOps.emplace_back(SDDbgOperand::fromVReg(Reg)); + continue; } + // We failed to create a SDDbgOperand for V. + return false; } - return false; + // We have created a SDDbgOperand for each Value in Values. + // Should use Order instead of SDNodeOrder? + assert(!LocationOps.empty()); + SDDbgValue *SDV = + DAG.getDbgValueList(Var, Expr, LocationOps, Dependencies, + /*IsIndirect=*/false, dl, SDNodeOrder, IsVariadic); + DAG.AddDbgValue(SDV, /*isParameter=*/false); + return true; } void SelectionDAGBuilder::resolveOrClearDbgInfo() { @@ -5377,6 +5447,8 @@ /// If the DbgValueInst is a dbg_value of a function argument, create the /// corresponding DBG_VALUE machine instruction for it now. At the end of /// instruction selection, they will be inserted to the entry BB. +/// We don't currently support this for variadic dbg_values, as they shouldn't +/// appear for function arguments or in the prologue. bool SelectionDAGBuilder::EmitFuncArgumentDbgValue( const Value *V, DILocalVariable *Variable, DIExpression *Expr, DILocation *DL, bool IsDbgDeclare, const SDValue &N) { @@ -5518,7 +5590,7 @@ if (!FragmentExpr) { SDDbgValue *SDV = DAG.getConstantDbgValue( Variable, Expr, UndefValue::get(V->getType()), DL, SDNodeOrder); - DAG.AddDbgValue(SDV, nullptr, false); + DAG.AddDbgValue(SDV, false); continue; } assert(!IsDbgDeclare && "DbgDeclare operand is not in memory?"); @@ -5859,7 +5931,10 @@ } case Intrinsic::dbg_addr: case Intrinsic::dbg_declare: { + // Assume dbg.addr and dbg.declare can not currently use DIArgList, i.e. + // they are non-variadic. const auto &DI = cast(I); + assert(!DI.hasArgList() && "Only dbg.value should currently use DIArgList"); DILocalVariable *Variable = DI.getVariable(); DIExpression *Expression = DI.getExpression(); dropDanglingDebugInfo(Variable, Expression); @@ -5867,7 +5942,7 @@ LLVM_DEBUG(dbgs() << "SelectionDAG visiting debug intrinsic: " << DI << "\n"); // Check if address has undef value. - const Value *Address = DI.getVariableLocation(); + const Value *Address = DI.getVariableLocationOp(0); if (!Address || isa(Address) || (Address->use_empty() && !isa(Address))) { LLVM_DEBUG(dbgs() << "Dropping debug info for " << DI @@ -5898,8 +5973,9 @@ if (FI != std::numeric_limits::max()) { if (Intrinsic == Intrinsic::dbg_addr) { SDDbgValue *SDV = DAG.getFrameIndexDbgValue( - Variable, Expression, FI, /*IsIndirect*/ true, dl, SDNodeOrder); - DAG.AddDbgValue(SDV, getRoot().getNode(), isParameter); + Variable, Expression, FI, getRoot().getNode(), /*IsIndirect*/ true, + dl, SDNodeOrder); + DAG.AddDbgValue(SDV, isParameter); } else { LLVM_DEBUG(dbgs() << "Skipping " << DI << " (variable info stashed in MF side table)\n"); @@ -5931,7 +6007,7 @@ SDV = DAG.getDbgValue(Variable, Expression, N.getNode(), N.getResNo(), true, dl, SDNodeOrder); } - DAG.AddDbgValue(SDV, N.getNode(), isParameter); + DAG.AddDbgValue(SDV, isParameter); } else { // If Address is an argument then try to emit its dbg value using // virtual register info from the FuncInfo.ValueMap. @@ -5960,20 +6036,17 @@ DILocalVariable *Variable = DI.getVariable(); DIExpression *Expression = DI.getExpression(); dropDanglingDebugInfo(Variable, Expression); - const Value *V = DI.getValue(); - if (!V) + SmallVector Values(DI.getValues()); + if (Values.empty()) return; - if (handleDebugValue(V, Variable, Expression, dl, DI.getDebugLoc(), - SDNodeOrder)) + if (std::count(Values.begin(), Values.end(), nullptr)) return; - // TODO: Dangling debug info will eventually either be resolved or produce - // an Undef DBG_VALUE. However in the resolution case, a gap may appear - // between the original dbg.value location and its resolved DBG_VALUE, which - // we should ideally fill with an extra Undef DBG_VALUE. - - DanglingDebugInfoMap[V].emplace_back(&DI, dl, SDNodeOrder); + bool IsVariadic = DI.hasArgList(); + if (!handleDebugValue(Values, Variable, Expression, dl, DI.getDebugLoc(), + SDNodeOrder, IsVariadic)) + addDanglingDebugInfo(&DI, dl, SDNodeOrder); return; } diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp @@ -828,26 +828,38 @@ LLVM_DUMP_METHOD void SDDbgValue::print(raw_ostream &OS) const { OS << " DbgVal(Order=" << getOrder() << ')'; - if (isInvalidated()) OS << "(Invalidated)"; - if (isEmitted()) OS << "(Emitted)"; - switch (getKind()) { - case SDNODE: - if (getSDNode()) - OS << "(SDNODE=" << PrintNodeId(*getSDNode()) << ':' << getResNo() << ')'; - else - OS << "(SDNODE)"; - break; - case CONST: - OS << "(CONST)"; - break; - case FRAMEIX: - OS << "(FRAMEIX=" << getFrameIx() << ')'; - break; - case VREG: - OS << "(VREG=" << getVReg() << ')'; - break; + if (isInvalidated()) + OS << "(Invalidated)"; + if (isEmitted()) + OS << "(Emitted)"; + OS << "("; + bool Comma = false; + for (const SDDbgOperand &Op : getLocationOps()) { + if (Comma) + OS << ", "; + switch (Op.getKind()) { + case SDDbgOperand::SDNODE: + if (Op.getSDNode()) + OS << "SDNODE=" << PrintNodeId(*Op.getSDNode()) << ':' << Op.getResNo(); + else + OS << "SDNODE"; + break; + case SDDbgOperand::CONST: + OS << "CONST"; + break; + case SDDbgOperand::FRAMEIX: + OS << "FRAMEIX=" << Op.getFrameIx(); + break; + case SDDbgOperand::VREG: + OS << "VREG=" << Op.getVReg(); + break; + } + Comma = true; } + OS << ")"; if (isIndirect()) OS << "(Indirect)"; + if (isVariadic()) + OS << "(Variadic)"; OS << ":\"" << Var->getName() << '"'; #ifndef NDEBUG if (Expr->getNumElements()) diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp @@ -572,7 +572,9 @@ // Insert DBG_VALUE instructions for function arguments to the entry block. for (unsigned i = 0, e = FuncInfo->ArgDbgValues.size(); i != e; ++i) { - MachineInstr *MI = FuncInfo->ArgDbgValues[e-i-1]; + MachineInstr *MI = FuncInfo->ArgDbgValues[e - i - 1]; + assert(MI->getOpcode() != TargetOpcode::DBG_VALUE_LIST && + "Function parameters should not be described by DBG_VALUE_LIST."); bool hasFI = MI->getOperand(0).isFI(); Register Reg = hasFI ? TRI.getFrameRegister(*MF) : MI->getOperand(0).getReg(); @@ -605,6 +607,8 @@ "DBG_VALUE with nonzero offset"); assert(cast(Variable)->isValidLocationForIntrinsic(DL) && "Expected inlined-at fields to agree"); + assert(MI->getOpcode() != TargetOpcode::DBG_VALUE_LIST && + "Didn't expect to see a DBG_VALUE_LIST here"); // Def is never a terminator here, so it is ok to increment InsertPos. BuildMI(*EntryMBB, ++InsertPos, DL, TII->get(TargetOpcode::DBG_VALUE), IsIndirect, LDI->second, Variable, Expr); diff --git a/llvm/lib/IR/AsmWriter.cpp b/llvm/lib/IR/AsmWriter.cpp --- a/llvm/lib/IR/AsmWriter.cpp +++ b/llvm/lib/IR/AsmWriter.cpp @@ -1240,8 +1240,9 @@ void SlotTracker::CreateMetadataSlot(const MDNode *N) { assert(N && "Can't insert a null Value into SlotTracker!"); - // Don't make slots for DIExpressions. We just print them inline everywhere. - if (isa(N)) + // Don't make slots for DIExpressions or DIArgLists. We just print them inline + // everywhere. + if (isa(N) || isa(N)) return; unsigned DestSlot = mdnNext; @@ -2353,6 +2354,21 @@ Out << ")"; } +static void writeDIArgList(raw_ostream &Out, const DIArgList *N, + TypePrinting *TypePrinter, SlotTracker *Machine, + const Module *Context, bool FromValue = false) { + assert(FromValue && + "Unexpected DIArgList metadata outside of value argument"); + Out << "!DIArgList("; + FieldSeparator FS; + MDFieldPrinter Printer(Out, TypePrinter, Machine, Context); + for (Metadata *Arg : N->getArgs()) { + Out << FS; + WriteAsOperandInternal(Out, Arg, TypePrinter, Machine, Context, true); + } + Out << ")"; +} + static void writeDIGlobalVariableExpression(raw_ostream &Out, const DIGlobalVariableExpression *N, TypePrinting *TypePrinter, @@ -2498,12 +2514,16 @@ TypePrinting *TypePrinter, SlotTracker *Machine, const Module *Context, bool FromValue) { - // Write DIExpressions inline when used as a value. Improves readability of - // debug info intrinsics. + // Write DIExpressions and DIArgLists inline when used as a value. Improves + // readability of debug info intrinsics. if (const DIExpression *Expr = dyn_cast(MD)) { writeDIExpression(Out, Expr, TypePrinter, Machine, Context); return; } + if (const DIArgList *ArgList = dyn_cast(MD)) { + writeDIArgList(Out, ArgList, TypePrinter, Machine, Context, FromValue); + return; + } if (const MDNode *N = dyn_cast(MD)) { std::unique_ptr MachineStorage; @@ -3416,6 +3436,8 @@ // Write DIExpressions inline. // FIXME: Ban DIExpressions in NamedMDNodes, they will serve no purpose. MDNode *Op = NMD->getOperand(i); + assert(!isa(Op) && + "DIArgLists should not appear in NamedMDNodes"); if (auto *Expr = dyn_cast(Op)) { writeDIExpression(Out, Expr, nullptr, nullptr, nullptr); continue; @@ -4686,7 +4708,7 @@ /* FromValue */ true); auto *N = dyn_cast(&MD); - if (OnlyAsOperand || !N || isa(MD)) + if (OnlyAsOperand || !N || isa(MD) || isa(MD)) return; OS << " = "; diff --git a/llvm/lib/IR/Constants.cpp b/llvm/lib/IR/Constants.cpp --- a/llvm/lib/IR/Constants.cpp +++ b/llvm/lib/IR/Constants.cpp @@ -712,6 +712,12 @@ return false; // Constant wasn't dead } + // If C is only used by metadata, it should not be preserved but should have + // its uses replaced. + if (C->isUsedByMetadata()) { + const_cast(C)->replaceAllUsesWith( + UndefValue::get(C->getType())); + } const_cast(C)->destroyConstant(); return true; } diff --git a/llvm/lib/IR/DebugInfoMetadata.cpp b/llvm/lib/IR/DebugInfoMetadata.cpp --- a/llvm/lib/IR/DebugInfoMetadata.cpp +++ b/llvm/lib/IR/DebugInfoMetadata.cpp @@ -1059,6 +1059,7 @@ case dwarf::DW_OP_plus_uconst: case dwarf::DW_OP_LLVM_tag_offset: case dwarf::DW_OP_LLVM_entry_value: + case dwarf::DW_OP_LLVM_arg: case dwarf::DW_OP_regx: return 2; default: @@ -1116,6 +1117,7 @@ } case dwarf::DW_OP_LLVM_implicit_pointer: case dwarf::DW_OP_LLVM_convert: + case dwarf::DW_OP_LLVM_arg: case dwarf::DW_OP_LLVM_tag_offset: case dwarf::DW_OP_constu: case dwarf::DW_OP_plus_uconst: @@ -1235,6 +1237,17 @@ return false; } +bool DIExpression::hasAllLocationOps(unsigned N) const { + SmallDenseSet SeenOps; + for (auto ExprOp : expr_ops()) + if (ExprOp.getOp() == dwarf::DW_OP_LLVM_arg) + SeenOps.insert(ExprOp.getArg(0)); + for (uint64_t Idx = 0; Idx < N; ++Idx) + if (!is_contained(SeenOps, Idx)) + return false; + return true; +} + const DIExpression *DIExpression::extractAddressClass(const DIExpression *Expr, unsigned &AddrClass) { // FIXME: This seems fragile. Nothing that verifies that these elements @@ -1271,6 +1284,52 @@ return prependOpcodes(Expr, Ops, StackValue, EntryValue); } +DIExpression *DIExpression::appendOpsToArg(const DIExpression *Expr, + ArrayRef Ops, + unsigned ArgNo, bool StackValue) { + assert(Expr && "Can't add ops to this expression"); + + // Handle non-variadic intrinsics by prepending the opcodes. + if (!any_of(Expr->expr_ops(), + [](auto Op) { return Op.getOp() == dwarf::DW_OP_LLVM_arg; })) { + assert(ArgNo == 0 && + "Location Index must be 0 for a non-variadic expression."); + SmallVector NewOps(Ops.begin(), Ops.end()); + return DIExpression::prependOpcodes(Expr, NewOps, StackValue); + } + + SmallVector NewOps; + for (auto Op : Expr->expr_ops()) { + Op.appendToVector(NewOps); + if (Op.getOp() == dwarf::DW_OP_LLVM_arg && Op.getArg(0) == ArgNo) + NewOps.insert(NewOps.end(), Ops.begin(), Ops.end()); + } + + return DIExpression::get(Expr->getContext(), NewOps); +} + +DIExpression *DIExpression::replaceArg(const DIExpression *Expr, + uint64_t OldArg, uint64_t NewArg) { + assert(Expr && "Can't replace args in this expression"); + + SmallVector NewOps; + + for (auto Op : Expr->expr_ops()) { + if (Op.getOp() != dwarf::DW_OP_LLVM_arg || Op.getArg(0) < OldArg) { + Op.appendToVector(NewOps); + continue; + } + NewOps.push_back(dwarf::DW_OP_LLVM_arg); + uint64_t Arg = Op.getArg(0) == OldArg ? NewArg : Op.getArg(0); + // OldArg has been deleted from the Op list, so decrement all indices + // greater than it. + if (Arg > OldArg) + --Arg; + NewOps.push_back(Arg); + } + return DIExpression::get(Expr->getContext(), NewOps); +} + DIExpression *DIExpression::prependOpcodes(const DIExpression *Expr, SmallVectorImpl &Ops, bool StackValue, @@ -1402,6 +1461,16 @@ return DIExpression::get(Expr->getContext(), Ops); } +uint64_t DIExpression::getNumLocationOperands() const { + uint64_t Result = 0; + for (auto ExprOp : expr_ops()) + if (ExprOp.getOp() == dwarf::DW_OP_LLVM_arg) + Result = std::max(Result, ExprOp.getArg(0) + 1); + assert(hasAllLocationOps(Result) && + "Expression is missing one or more location operands."); + return Result; +} + bool DIExpression::isConstant() const { // Recognize DW_OP_constu C DW_OP_stack_value (DW_OP_LLVM_fragment Len Ofs)?. if (getNumElements() != 3 && getNumElements() != 6) @@ -1489,3 +1558,42 @@ Metadata *Ops[] = { File, Elements }; DEFINE_GETIMPL_STORE(DIMacroFile, (MIType, Line), Ops); } + +DIArgList *DIArgList::getImpl(LLVMContext &Context, + ArrayRef Args, + StorageType Storage, bool ShouldCreate) { + DEFINE_GETIMPL_LOOKUP(DIArgList, (Args)); + DEFINE_GETIMPL_STORE_NO_OPS(DIArgList, (Args)); +} + +void DIArgList::handleChangedOperand(void *Ref, Metadata *New) { + ValueAsMetadata **OldVMPtr = static_cast(Ref); + assert((!New || isa(New)) && + "DIArgList must be passed a ValueAsMetadata"); + untrack(); + ValueAsMetadata *NewVM = cast_or_null(New); + for (ValueAsMetadata *&VM : Args) { + if (&VM == OldVMPtr) { + if (NewVM) + VM = NewVM; + else + VM = ValueAsMetadata::get(UndefValue::get(VM->getValue()->getType())); + } + } + track(); +} +void DIArgList::track() { + for (ValueAsMetadata *&VAM : Args) + if (VAM) + MetadataTracking::track(&VAM, *VAM, *this); +} +void DIArgList::untrack() { + for (ValueAsMetadata *&VAM : Args) + if (VAM) + MetadataTracking::untrack(&VAM, *VAM); +} +void DIArgList::dropAllReferences() { + untrack(); + Args.clear(); + MDNode::dropAllReferences(); +} diff --git a/llvm/lib/IR/Instructions.cpp b/llvm/lib/IR/Instructions.cpp --- a/llvm/lib/IR/Instructions.cpp +++ b/llvm/lib/IR/Instructions.cpp @@ -1776,6 +1776,15 @@ return cast(this)->accumulateConstantOffset(DL, Offset); } +bool GetElementPtrInst::collectOffset( + const DataLayout &DL, unsigned BitWidth, + SmallDenseMap &VariableOffsets, + APInt &ConstantOffset) const { + // Delegate to the generic GEPOperator implementation. + return cast(this)->collectOffset(DL, BitWidth, VariableOffsets, + ConstantOffset); +} + //===----------------------------------------------------------------------===// // ExtractElementInst Implementation //===----------------------------------------------------------------------===// diff --git a/llvm/lib/IR/IntrinsicInst.cpp b/llvm/lib/IR/IntrinsicInst.cpp --- a/llvm/lib/IR/IntrinsicInst.cpp +++ b/llvm/lib/IR/IntrinsicInst.cpp @@ -38,18 +38,101 @@ /// intrinsics for variables. /// -Value *DbgVariableIntrinsic::getVariableLocation(bool AllowNullOp) const { - Value *Op = getArgOperand(0); - if (AllowNullOp && !Op) +iterator_range +DbgVariableIntrinsic::location_ops() const { + auto *MD = getRawLocation(); + assert(MD && "First operand of DbgVariableIntrinsic should be non-null."); + + // If operand is ValueAsMetadata, return a range over just that operand. + if (auto *VAM = dyn_cast(MD)) { + return {location_op_iterator(VAM), location_op_iterator(VAM + 1)}; + } + // If operand is DIArgList, return a range over its args. + if (auto *AL = dyn_cast(MD)) + return {location_op_iterator(AL->args_begin()), + location_op_iterator(AL->args_end())}; + // Operand must be an empty metadata tuple, so return empty iterator. + return {location_op_iterator(static_cast(nullptr)), + location_op_iterator(static_cast(nullptr))}; +} + +Value *DbgVariableIntrinsic::getVariableLocationOp(unsigned OpIdx) const { + auto *MD = getRawLocation(); + assert(MD && "First operand of DbgVariableIntrinsic should be non-null."); + if (auto *AL = dyn_cast(MD)) + return AL->getArgs()[OpIdx]->getValue(); + if (isa(MD)) return nullptr; - - auto *MD = cast(Op)->getMetadata(); - if (auto *V = dyn_cast(MD)) - return V->getValue(); - - // When the value goes to null, it gets replaced by an empty MDNode. - assert(!cast(MD)->getNumOperands() && "Expected an empty MDNode"); - return nullptr; + assert( + isa(MD) && + "Attempted to get location operand from DbgVariableIntrinsic with none."); + auto *V = cast(MD); + assert(OpIdx == 0 && "Operand Index must be 0 for a debug intrinsic with a " + "single location operand."); + return V->getValue(); +} + +static ValueAsMetadata *getAsMetadata(Value *V) { + return isa(V) ? dyn_cast( + cast(V)->getMetadata()) + : ValueAsMetadata::get(V); +} + +void DbgVariableIntrinsic::replaceVariableLocationOp(Value *OldValue, + Value *NewValue) { + assert(NewValue && "Values must be non-null"); + auto Locations = location_ops(); + auto OldIt = find(Locations, OldValue); + assert(OldIt != Locations.end() && "OldValue must be a current location"); + if (!hasArgList()) { + Value *NewOperand = isa(NewValue) + ? NewValue + : MetadataAsValue::get( + getContext(), ValueAsMetadata::get(NewValue)); + return setArgOperand(0, NewOperand); + } + SmallVector MDs; + ValueAsMetadata *NewOperand = getAsMetadata(NewValue); + for (auto *VMD : Locations) + MDs.push_back(VMD == *OldIt ? NewOperand : getAsMetadata(VMD)); + setArgOperand( + 0, MetadataAsValue::get(getContext(), DIArgList::get(getContext(), MDs))); +} +void DbgVariableIntrinsic::replaceVariableLocationOp(unsigned OpIdx, + Value *NewValue) { + assert(OpIdx < getNumVariableLocationOps() && "Invalid Operand Index"); + if (!hasArgList()) { + Value *NewOperand = isa(NewValue) + ? NewValue + : MetadataAsValue::get( + getContext(), ValueAsMetadata::get(NewValue)); + return setArgOperand(0, NewOperand); + } + SmallVector MDs; + ValueAsMetadata *NewOperand = getAsMetadata(NewValue); + for (unsigned Idx = 0; Idx < getNumVariableLocationOps(); ++Idx) + MDs.push_back(Idx == OpIdx ? NewOperand + : getAsMetadata(getVariableLocationOp(Idx))); + setArgOperand( + 0, MetadataAsValue::get(getContext(), DIArgList::get(getContext(), MDs))); +} + +void DbgVariableIntrinsic::addVariableLocationOps(ArrayRef NewValues, + DIExpression *NewExpr) { + assert(NewExpr->hasAllLocationOps(getNumVariableLocationOps() + + NewValues.size()) && + "NewExpr for debug variable intrinsic does not reference every " + "location operand."); + for (auto V : NewValues) + assert(V && "New values must be non-null"); + setArgOperand(2, MetadataAsValue::get(getContext(), NewExpr)); + SmallVector MDs; + for (auto *VMD : location_ops()) + MDs.push_back(getAsMetadata(VMD)); + for (auto *VMD : NewValues) + MDs.push_back(getAsMetadata(VMD)); + setArgOperand( + 0, MetadataAsValue::get(getContext(), DIArgList::get(getContext(), MDs))); } Optional DbgVariableIntrinsic::getFragmentSizeInBits() const { diff --git a/llvm/lib/IR/LLVMContextImpl.h b/llvm/lib/IR/LLVMContextImpl.h --- a/llvm/lib/IR/LLVMContextImpl.h +++ b/llvm/lib/IR/LLVMContextImpl.h @@ -1220,6 +1220,19 @@ } }; +template <> struct MDNodeKeyImpl { + ArrayRef Args; + + MDNodeKeyImpl(ArrayRef Args) : Args(Args) {} + MDNodeKeyImpl(const DIArgList *N) : Args(N->getArgs()) {} + + bool isKeyOf(const DIArgList *RHS) const { return Args == RHS->getArgs(); } + + unsigned getHashValue() const { + return hash_combine_range(Args.begin(), Args.end()); + } +}; + /// DenseMapInfo for MDNode subclasses. template struct MDNodeInfo { using KeyTy = MDNodeKeyImpl; diff --git a/llvm/lib/IR/Metadata.cpp b/llvm/lib/IR/Metadata.cpp --- a/llvm/lib/IR/Metadata.cpp +++ b/llvm/lib/IR/Metadata.cpp @@ -195,6 +195,19 @@ return ReplaceableMetadataImpl::isReplaceable(MD); } +SmallVector ReplaceableMetadataImpl::getAllArgListUsers() { + SmallVector MDUsers; + for (auto Pair : UseMap) { + OwnerTy Owner = Pair.second.first; + if (!Owner.is()) + continue; + Metadata *OwnerMD = Owner.get(); + if (OwnerMD->getMetadataID() == Metadata::DIArgListKind) + MDUsers.push_back(OwnerMD); + } + return MDUsers; +} + void ReplaceableMetadataImpl::addRef(void *Ref, OwnerTy Owner) { bool WasInserted = UseMap.insert(std::make_pair(Ref, std::make_pair(Owner, NextIndex))) diff --git a/llvm/lib/IR/Operator.cpp b/llvm/lib/IR/Operator.cpp --- a/llvm/lib/IR/Operator.cpp +++ b/llvm/lib/IR/Operator.cpp @@ -134,4 +134,63 @@ } return true; } + +bool GEPOperator::collectOffset( + const DataLayout &DL, unsigned BitWidth, + SmallDenseMap &VariableOffsets, + APInt &ConstantOffset) const { + assert(BitWidth == DL.getIndexSizeInBits(getPointerAddressSpace()) && + "The offset bit width does not match DL specification."); + + bool UsedExternalAnalysis = false; + + auto CollectConstantOffset = [&](APInt Index, uint64_t Size) { + Index = Index.sextOrTrunc(BitWidth); + APInt IndexedSize = APInt(BitWidth, Size); + ConstantOffset += Index * IndexedSize; + }; + + for (gep_type_iterator GTI = gep_type_begin(this), GTE = gep_type_end(this); + GTI != GTE; ++GTI) { + // Scalable vectors are multiplied by a runtime constant. + bool ScalableType = isa(GTI.getIndexedType()); + + Value *V = GTI.getOperand(); + StructType *STy = GTI.getStructTypeOrNull(); + // Handle ConstantInt if possible. + if (auto ConstOffset = dyn_cast(V)) { + if (ConstOffset->isZero()) + continue; + // If the type is scalable and the constant is not zero (vscale * n * 0 = + // 0) bailout. + // TODO: If the runtime value is accessible at any point before DWARF + // emission, then we could potentially keep a forward reference to it + // in the debug value to be filled in later. + if (ScalableType) + return false; + // Handle a struct index, which adds its field offset to the pointer. + if (STy) { + unsigned ElementIdx = ConstOffset->getZExtValue(); + const StructLayout *SL = DL.getStructLayout(STy); + // Element offset is in bytes. + CollectConstantOffset(APInt(BitWidth, SL->getElementOffset(ElementIdx)), + 1); + continue; + } + CollectConstantOffset(ConstOffset->getValue(), + DL.getTypeAllocSize(GTI.getIndexedType())); + continue; + } + + if (STy || ScalableType) + return false; + // Insert an initial offset of 0 for V iff none exists already, then + // increment the offset by IndexedSize. + VariableOffsets.try_emplace(V, BitWidth, 0); + APInt IndexedSize = + APInt(BitWidth, DL.getTypeAllocSize(GTI.getIndexedType())); + VariableOffsets[V] += IndexedSize; + } + return true; +} } // namespace llvm diff --git a/llvm/lib/IR/User.cpp b/llvm/lib/IR/User.cpp --- a/llvm/lib/IR/User.cpp +++ b/llvm/lib/IR/User.cpp @@ -31,6 +31,10 @@ // most importantly, removing "this" from the use list of "From". setOperand(i, To); } + if (auto DVI = dyn_cast_or_null(this)) { + if (is_contained(DVI->location_ops(), From)) + DVI->replaceVariableLocationOp(From, To); + } } //===----------------------------------------------------------------------===// diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp --- a/llvm/lib/IR/Verifier.cpp +++ b/llvm/lib/IR/Verifier.cpp @@ -1307,6 +1307,13 @@ } } +void Verifier::visitDIArgList(const DIArgList &N) { + AssertDI(!N.getNumOperands(), + "DIArgList should have no operands other than a list of " + "ValueAsMetadata", + &N); +} + void Verifier::visitDIModule(const DIModule &N) { AssertDI(N.getTag() == dwarf::DW_TAG_module, "invalid tag", &N); AssertDI(!N.getName().empty(), "anonymous module", &N); @@ -5345,10 +5352,10 @@ } void Verifier::visitDbgIntrinsic(StringRef Kind, DbgVariableIntrinsic &DII) { - auto *MD = cast(DII.getArgOperand(0))->getMetadata(); - AssertDI(isa(MD) || - (isa(MD) && !cast(MD)->getNumOperands()), - "invalid llvm.dbg." + Kind + " intrinsic address/value", &DII, MD); + auto *MD = DII.getRawLocation(); + AssertDI(isa(MD) || isa(MD) || + (isa(MD) && !cast(MD)->getNumOperands()), + "invalid llvm.dbg." + Kind + " intrinsic address/value", &DII, MD); AssertDI(isa(DII.getRawVariable()), "invalid llvm.dbg." + Kind + " intrinsic variable", &DII, DII.getRawVariable()); diff --git a/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp b/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp --- a/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp +++ b/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp @@ -768,11 +768,15 @@ OS << MI->getDebugVariable()->getName(); OS << " <- "; // Frame address. Currently handles register +- offset only. - assert(MI->getDebugOperand(0).isReg() && MI->isDebugOffsetImm()); + assert(MI->isIndirectDebugValue()); OS << '['; - printOperand(MI, 0, OS); - OS << '+'; - printOperand(MI, 1, OS); + for (unsigned I = 0, E = std::distance(MI->debug_operands().begin(), + MI->debug_operands().end()); + I < E; ++I) { + if (I != 0) + OS << ", "; + printOperand(MI, I, OS); + } OS << ']'; OS << "+"; printOperand(MI, NOps - 2, OS); @@ -1185,7 +1189,8 @@ } break; - case AArch64::DBG_VALUE: { + case AArch64::DBG_VALUE: + case AArch64::DBG_VALUE_LIST: { if (isVerbose() && OutStreamer->hasRawTextSupport()) { SmallString<128> TmpStr; raw_svector_ostream OS(TmpStr); @@ -1206,7 +1211,7 @@ OutStreamer->emitCFIBKeyFrame(); return; } - } + } // Tail calls use pseudo instructions so they have the proper code-gen // attributes (isCall, isReturn, etc.). We lower them to the real diff --git a/llvm/lib/Target/AArch64/AArch64StackTagging.cpp b/llvm/lib/Target/AArch64/AArch64StackTagging.cpp --- a/llvm/lib/Target/AArch64/AArch64StackTagging.cpp +++ b/llvm/lib/Target/AArch64/AArch64StackTagging.cpp @@ -284,6 +284,7 @@ class AArch64StackTagging : public FunctionPass { struct AllocaInfo { AllocaInst *AI; + TrackingVH OldAI; // Track through RAUW to replace debug uses. SmallVector LifetimeStart; SmallVector LifetimeEnd; SmallVector DbgVariableIntrinsics; @@ -557,14 +558,14 @@ Instruction *I = &*IT; if (auto *AI = dyn_cast(I)) { Allocas[AI].AI = AI; + Allocas[AI].OldAI = AI; continue; } if (auto *DVI = dyn_cast(I)) { - if (auto *AI = - dyn_cast_or_null(DVI->getVariableLocation())) { - Allocas[AI].DbgVariableIntrinsics.push_back(DVI); - } + for (Value *V : DVI->location_ops()) + if (auto *AI = dyn_cast_or_null(V)) + Allocas[AI].DbgVariableIntrinsics.push_back(DVI); continue; } @@ -705,9 +706,7 @@ // Fixup debug intrinsics to point to the new alloca. for (auto DVI : Info.DbgVariableIntrinsics) - DVI->setArgOperand( - 0, - MetadataAsValue::get(F->getContext(), LocalAsMetadata::get(Info.AI))); + DVI->replaceVariableLocationOp(Info.OldAI, Info.AI); } // If we have instrumented at least one alloca, all unrecognized lifetime diff --git a/llvm/lib/Target/NVPTX/NVPTXPrologEpilogPass.cpp b/llvm/lib/Target/NVPTX/NVPTXPrologEpilogPass.cpp --- a/llvm/lib/Target/NVPTX/NVPTXPrologEpilogPass.cpp +++ b/llvm/lib/Target/NVPTX/NVPTXPrologEpilogPass.cpp @@ -65,16 +65,27 @@ // way with simply the frame index and offset rather than any // target-specific addressing mode. if (MI.isDebugValue()) { - assert(i == 0 && "Frame indices can only appear as the first " - "operand of a DBG_VALUE machine instruction"); + MachineOperand &Op = MI.getOperand(i); + assert( + MI.isDebugOperand(&Op) && + "Frame indices can only appear as a debug operand in a DBG_VALUE*" + " machine instruction"); Register Reg; int64_t Offset = - TFI.getFrameIndexReference(MF, MI.getOperand(0).getIndex(), Reg) + TFI.getFrameIndexReference(MF, MI.getOperand(i).getIndex(), Reg) .getFixed(); - MI.getOperand(0).ChangeToRegister(Reg, /*isDef=*/false); - MI.getOperand(0).setIsDebug(); - auto *DIExpr = DIExpression::prepend( - MI.getDebugExpression(), DIExpression::ApplyOffset, Offset); + MI.getOperand(i).ChangeToRegister(Reg, /*isDef=*/false); + MI.getOperand(i).setIsDebug(); + const DIExpression *DIExpr = MI.getDebugExpression(); + if (MI.isNonListDebugValue()) { + DIExpr = DIExpression::prepend(MI.getDebugExpression(), + DIExpression::ApplyOffset, Offset); + } else { + SmallVector Ops; + DIExpression::appendOffset(Ops, Offset); + unsigned OpIdx = MI.getDebugOperandIndex(&MI.getOperand(i)); + DIExpr = DIExpression::appendOpsToArg(DIExpr, Ops, OpIdx); + } MI.getDebugExpressionOp().setMetadata(DIExpr); continue; } diff --git a/llvm/lib/Target/SystemZ/SystemZRegisterInfo.cpp b/llvm/lib/Target/SystemZ/SystemZRegisterInfo.cpp --- a/llvm/lib/Target/SystemZ/SystemZRegisterInfo.cpp +++ b/llvm/lib/Target/SystemZ/SystemZRegisterInfo.cpp @@ -9,12 +9,13 @@ #include "SystemZRegisterInfo.h" #include "SystemZInstrInfo.h" #include "SystemZSubtarget.h" -#include "llvm/CodeGen/LiveIntervals.h" #include "llvm/ADT/SmallSet.h" +#include "llvm/CodeGen/LiveIntervals.h" #include "llvm/CodeGen/MachineInstrBuilder.h" #include "llvm/CodeGen/MachineRegisterInfo.h" #include "llvm/CodeGen/TargetFrameLowering.h" #include "llvm/CodeGen/VirtRegMap.h" +#include "llvm/IR/DebugInfoMetadata.h" using namespace llvm; @@ -273,7 +274,16 @@ // Special handling of dbg_value instructions. if (MI->isDebugValue()) { MI->getOperand(FIOperandNum).ChangeToRegister(BasePtr, /*isDef*/ false); - MI->getDebugOffset().ChangeToImmediate(Offset); + if (MI->isNonListDebugValue()) { + MI->getDebugOffset().ChangeToImmediate(Offset); + } else { + unsigned OpIdx = MI->getDebugOperandIndex(&MI->getOperand(FIOperandNum)); + SmallVector Ops; + DIExpression::appendOffset( + Ops, TFI->getFrameIndexReference(MF, FrameIndex, BasePtr).getFixed()); + MI->getDebugExpressionOp().setMetadata( + DIExpression::appendOpsToArg(MI->getDebugExpression(), Ops, OpIdx)); + } return; } diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyDebugValueManager.h b/llvm/lib/Target/WebAssembly/WebAssemblyDebugValueManager.h --- a/llvm/lib/Target/WebAssembly/WebAssemblyDebugValueManager.h +++ b/llvm/lib/Target/WebAssembly/WebAssemblyDebugValueManager.h @@ -23,6 +23,7 @@ class WebAssemblyDebugValueManager { SmallVector DbgValues; + unsigned CurrentReg; public: WebAssemblyDebugValueManager(MachineInstr *Instr); diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyDebugValueManager.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyDebugValueManager.cpp --- a/llvm/lib/Target/WebAssembly/WebAssemblyDebugValueManager.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyDebugValueManager.cpp @@ -24,13 +24,14 @@ // the whole BB, not just contiguous DBG_VALUEs. if (!Instr->getOperand(0).isReg()) return; + CurrentReg = Instr->getOperand(0).getReg(); MachineBasicBlock::iterator DI = *Instr; ++DI; for (MachineBasicBlock::iterator DE = Instr->getParent()->end(); DI != DE; ++DI) { if (DI->isDebugValue() && - DI->getDebugOperandForReg(Instr->getOperand(0).getReg())) + DI->hasDebugOperandForReg(Instr->getOperand(0).getReg())) DbgValues.push_back(&*DI); } } @@ -43,7 +44,9 @@ void WebAssemblyDebugValueManager::updateReg(unsigned Reg) { for (auto *DBI : DbgValues) - DBI->getDebugOperand(0).setReg(Reg); + for (auto &MO : DBI->getDebugOperandsForReg(CurrentReg)) + MO.setReg(Reg); + CurrentReg = Reg; } void WebAssemblyDebugValueManager::clone(MachineInstr *Insert, @@ -52,14 +55,15 @@ MachineFunction *MF = MBB->getParent(); for (MachineInstr *DBI : reverse(DbgValues)) { MachineInstr *Clone = MF->CloneMachineInstr(DBI); - Clone->getDebugOperand(0).setReg(NewReg); + for (auto &MO : Clone->getDebugOperandsForReg(CurrentReg)) + MO.setReg(NewReg); MBB->insert(Insert, Clone); } } void WebAssemblyDebugValueManager::replaceWithLocal(unsigned LocalId) { for (auto *DBI : DbgValues) { - MachineOperand &Op = DBI->getDebugOperand(0); - Op.ChangeToTargetIndex(llvm::WebAssembly::TI_LOCAL, LocalId); + for (auto &MO : DBI->getDebugOperandsForReg(CurrentReg)) + MO.ChangeToTargetIndex(llvm::WebAssembly::TI_LOCAL, LocalId); } } diff --git a/llvm/lib/Target/X86/X86OptimizeLEAs.cpp b/llvm/lib/Target/X86/X86OptimizeLEAs.cpp --- a/llvm/lib/Target/X86/X86OptimizeLEAs.cpp +++ b/llvm/lib/Target/X86/X86OptimizeLEAs.cpp @@ -295,8 +295,8 @@ /// Replace debug value MI with a new debug value instruction using register /// VReg with an appropriate offset and DIExpression to incorporate the /// address displacement AddrDispShift. Return new debug value instruction. - MachineInstr *replaceDebugValue(MachineInstr &MI, unsigned VReg, - int64_t AddrDispShift); + MachineInstr *replaceDebugValue(MachineInstr &MI, unsigned OldReg, + unsigned NewReg, int64_t AddrDispShift); /// Removes LEAs which calculate similar addresses. bool removeRedundantLEAs(MemOpMap &LEAs); @@ -576,21 +576,49 @@ } MachineInstr *X86OptimizeLEAPass::replaceDebugValue(MachineInstr &MI, - unsigned VReg, + unsigned OldReg, + unsigned NewReg, int64_t AddrDispShift) { const DIExpression *Expr = MI.getDebugExpression(); if (AddrDispShift != 0) - Expr = DIExpression::prepend(Expr, DIExpression::StackValue, AddrDispShift); + if (MI.isNonListDebugValue()) + Expr = + DIExpression::prepend(Expr, DIExpression::StackValue, AddrDispShift); + else { + // Update the Expression, appending an offset of `AddrDispShift` to the + // Op corresponding to `OldReg`. + SmallVector Ops; + DIExpression::appendOffset(Ops, AddrDispShift); + for (MachineOperand &Op : MI.getDebugOperandsForReg(OldReg)) { + unsigned OpIdx = MI.getDebugOperandIndex(&Op); + Expr = DIExpression::appendOpsToArg(Expr, Ops, OpIdx); + } + } // Replace DBG_VALUE instruction with modified version. MachineBasicBlock *MBB = MI.getParent(); DebugLoc DL = MI.getDebugLoc(); bool IsIndirect = MI.isIndirectDebugValue(); const MDNode *Var = MI.getDebugVariable(); + unsigned Opcode = MI.isNonListDebugValue() ? TargetOpcode::DBG_VALUE + : TargetOpcode::DBG_VALUE_LIST; if (IsIndirect) - assert(MI.getOperand(1).getImm() == 0 && "DBG_VALUE with nonzero offset"); - return BuildMI(*MBB, MBB->erase(&MI), DL, TII->get(TargetOpcode::DBG_VALUE), - IsIndirect, VReg, Var, Expr); + assert(MI.getDebugOffset().getImm() == 0 && + "DBG_VALUE with nonzero offset"); + SmallVector NewOps; + // If we encounter an operand using the old register, replace it with an + // operand that uses the new register; otherwise keep the old operand. + auto replaceOldReg = [OldReg, NewReg](const MachineOperand &Op) { + if (Op.isReg() && Op.getReg() == OldReg) + return MachineOperand::CreateReg(NewReg, false, false, false, false, + false, false, false, false, 0, + /*IsRenamable*/ true); + return Op; + }; + for (const MachineOperand &Op : MI.debug_operands()) + NewOps.push_back(replaceOldReg(Op)); + return BuildMI(*MBB, MBB->erase(&MI), DL, TII->get(Opcode), IsIndirect, + NewOps, Var, Expr); } // Try to find similar LEAs in the list and replace one with another. @@ -635,7 +663,7 @@ // Replace DBG_VALUE instruction with modified version using the // register from the replacing LEA and the address displacement // between the LEA instructions. - replaceDebugValue(MI, FirstVReg, AddrDispShift); + replaceDebugValue(MI, LastVReg, FirstVReg, AddrDispShift); continue; } diff --git a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp --- a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp +++ b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp @@ -3540,37 +3540,45 @@ // here, but that computation has been sunk. SmallVector DbgUsers; findDbgUsers(DbgUsers, I); - - // Update the arguments of a dbg.declare instruction, so that it - // does not point into a sunk instruction. - auto updateDbgDeclare = [&I](DbgVariableIntrinsic *DII) { - if (!isa(DII)) - return false; - - if (isa(I)) - DII->setOperand( - 0, MetadataAsValue::get(I->getContext(), - ValueAsMetadata::get(I->getOperand(0)))); - return true; - }; + // Process the DbgUsers in reverse order, as we only want to clone the last + // appearing debug intrinsic for each given variable. + llvm::sort(DbgUsers, [](auto *A, auto *B) { + if (A->getParent() == B->getParent()) + return B->comesBefore(A); + // We only care about sorting DbgUsers in the same BB, cross-block ordering + // does not matter here. + return A->getParent() > B->getParent(); + }); SmallVector DIIClones; + SmallSet SunkVariables; for (auto User : DbgUsers) { // A dbg.declare instruction should not be cloned, since there can only be // one per variable fragment. It should be left in the original place // because the sunk instruction is not an alloca (otherwise we could not be // here). - if (User->getParent() != SrcBlock || updateDbgDeclare(User)) + if (User->getParent() != SrcBlock || isa(User)) continue; + DebugVariable DbgUserVariable = + DebugVariable(User->getVariable(), User->getExpression(), + User->getDebugLoc()->getInlinedAt()); + + if (!SunkVariables.insert(DbgUserVariable).second) + continue; + DIIClones.emplace_back(cast(User->clone())); + if (isa(User) && isa(I)) + DIIClones.back()->replaceVariableLocationOp(I, I->getOperand(0)); LLVM_DEBUG(dbgs() << "CLONE: " << *DIIClones.back() << '\n'); } // Perform salvaging without the clones, then sink the clones. if (!DIIClones.empty()) { salvageDebugInfoForDbgValues(*I, DbgUsers); - for (auto &DIIClone : DIIClones) { + // The clones are in reverse order of original appearance, reverse again to + // maintain the original order. + for (auto &DIIClone : llvm::reverse(DIIClones)) { DIIClone->insertBefore(&*InsertPos); LLVM_DEBUG(dbgs() << "SINK: " << *DIIClone << '\n'); } diff --git a/llvm/lib/Transforms/Instrumentation/HWAddressSanitizer.cpp b/llvm/lib/Transforms/Instrumentation/HWAddressSanitizer.cpp --- a/llvm/lib/Transforms/Instrumentation/HWAddressSanitizer.cpp +++ b/llvm/lib/Transforms/Instrumentation/HWAddressSanitizer.cpp @@ -1153,9 +1153,10 @@ // to put it at the beginning of the expression. SmallVector NewOps = {dwarf::DW_OP_LLVM_tag_offset, RetagMask(N)}; - DDI->setArgOperand( - 2, MetadataAsValue::get(*C, DIExpression::prependOpcodes( - DDI->getExpression(), NewOps))); + auto Locations = DDI->location_ops(); + unsigned LocNo = std::distance(Locations.begin(), find(Locations, AI)); + DDI->setExpression( + DIExpression::appendOpsToArg(DDI->getExpression(), NewOps, LocNo)); } size_t Size = getAllocaSizeInBytes(*AI); @@ -1217,10 +1218,10 @@ isa(Inst)) RetVec.push_back(&Inst); - if (auto *DDI = dyn_cast(&Inst)) - if (auto *Alloca = - dyn_cast_or_null(DDI->getVariableLocation())) - AllocaDbgMap[Alloca].push_back(DDI); + if (auto *DVI = dyn_cast(&Inst)) + for (Value *V : DVI->location_ops()) + if (auto *Alloca = dyn_cast_or_null(V)) + AllocaDbgMap[Alloca].push_back(DVI); if (InstrumentLandingPads && isa(Inst)) LandingPadVec.push_back(&Inst); @@ -1296,16 +1297,20 @@ } if (!AllocaToPaddedAllocaMap.empty()) { - for (auto &BB : F) - for (auto &Inst : BB) - if (auto *DVI = dyn_cast(&Inst)) - if (auto *AI = - dyn_cast_or_null(DVI->getVariableLocation())) - if (auto *NewAI = AllocaToPaddedAllocaMap.lookup(AI)) - DVI->setArgOperand( - 0, MetadataAsValue::get(*C, LocalAsMetadata::get(NewAI))); - for (auto &P : AllocaToPaddedAllocaMap) - P.first->eraseFromParent(); + for (auto &BB : F) { + for (auto &Inst : BB) { + if (auto *DVI = dyn_cast(&Inst)) { + for (Value *V : DVI->location_ops()) { + if (auto *AI = dyn_cast_or_null(V)) { + if (auto *NewAI = AllocaToPaddedAllocaMap.lookup(AI)) + DVI->replaceVariableLocationOp(V, NewAI); + } + } + } + } + for (auto &P : AllocaToPaddedAllocaMap) + P.first->eraseFromParent(); + } } // If we split the entry block, move any allocas that were originally in the diff --git a/llvm/lib/Transforms/Scalar/ADCE.cpp b/llvm/lib/Transforms/Scalar/ADCE.cpp --- a/llvm/lib/Transforms/Scalar/ADCE.cpp +++ b/llvm/lib/Transforms/Scalar/ADCE.cpp @@ -521,10 +521,14 @@ // If intrinsic is pointing at a live SSA value, there may be an // earlier optimization bug: if we know the location of the variable, // why isn't the scope of the location alive? - if (Value *V = DII->getVariableLocation()) - if (Instruction *II = dyn_cast(V)) - if (isLive(II)) + for (Value *V : DII->location_ops()) { + if (Instruction *II = dyn_cast(V)) { + if (isLive(II)) { dbgs() << "Dropping debug info for " << *DII << "\n"; + break; + } + } + } } } }); diff --git a/llvm/lib/Transforms/Scalar/LoopStrengthReduce.cpp b/llvm/lib/Transforms/Scalar/LoopStrengthReduce.cpp --- a/llvm/lib/Transforms/Scalar/LoopStrengthReduce.cpp +++ b/llvm/lib/Transforms/Scalar/LoopStrengthReduce.cpp @@ -5773,58 +5773,72 @@ AU.addPreserved(); } -using EqualValues = SmallVector, 4>; -using EqualValuesMap = DenseMap; +using EqualValues = SmallVector, 4>; +using EqualValuesMap = + DenseMap, EqualValues>; +using ExpressionMap = DenseMap; static void DbgGatherEqualValues(Loop *L, ScalarEvolution &SE, - EqualValuesMap &DbgValueToEqualSet) { + EqualValuesMap &DbgValueToEqualSet, + ExpressionMap &DbgValueToExpression) { for (auto &B : L->getBlocks()) { for (auto &I : *B) { auto DVI = dyn_cast(&I); if (!DVI) continue; - auto V = DVI->getVariableLocation(); - if (!V || !SE.isSCEVable(V->getType())) - continue; - auto DbgValueSCEV = SE.getSCEV(V); - EqualValues EqSet; - for (PHINode &Phi : L->getHeader()->phis()) { - if (V->getType() != Phi.getType()) - continue; - if (!SE.isSCEVable(Phi.getType())) + for (unsigned Idx = 0; Idx < DVI->getNumVariableLocationOps(); ++Idx) { + // TODO: We can duplicate results if the same arg appears more than + // once. + Value *V = DVI->getVariableLocationOp(Idx); + if (!V || !SE.isSCEVable(V->getType())) continue; - auto PhiSCEV = SE.getSCEV(&Phi); - Optional Offset = - SE.computeConstantDifference(DbgValueSCEV, PhiSCEV); - if (Offset && Offset->getMinSignedBits() <= 64) - EqSet.emplace_back(std::make_tuple( - &Phi, Offset.getValue().getSExtValue(), DVI->getExpression())); + auto DbgValueSCEV = SE.getSCEV(V); + EqualValues EqSet; + for (PHINode &Phi : L->getHeader()->phis()) { + if (V->getType() != Phi.getType()) + continue; + if (!SE.isSCEVable(Phi.getType())) + continue; + auto PhiSCEV = SE.getSCEV(&Phi); + Optional Offset = + SE.computeConstantDifference(DbgValueSCEV, PhiSCEV); + if (Offset && Offset->getMinSignedBits() <= 64) + EqSet.emplace_back( + std::make_tuple(&Phi, Offset.getValue().getSExtValue())); + } + DbgValueToEqualSet[{DVI, Idx}] = std::move(EqSet); + DbgValueToExpression[DVI] = DVI->getExpression(); } - DbgValueToEqualSet[DVI] = std::move(EqSet); } } } -static void DbgApplyEqualValues(EqualValuesMap &DbgValueToEqualSet) { +static void DbgApplyEqualValues(EqualValuesMap &DbgValueToEqualSet, + ExpressionMap &DbgValueToExpression) { for (auto A : DbgValueToEqualSet) { - auto DVI = A.first; + auto DVI = A.first.first; + auto Idx = A.first.second; // Only update those that are now undef. - if (!isa_and_nonnull(DVI->getVariableLocation())) + if (!isa_and_nonnull(DVI->getVariableLocationOp(Idx))) continue; for (auto EV : A.second) { - auto V = std::get(EV); - if (!V) + auto EVHandle = std::get(EV); + if (!EVHandle) continue; - auto DbgDIExpr = std::get(EV); + // The dbg.value may have had its value changed by LSR; refresh it from + // the map, but continue to update the mapped expression as it may be + // updated multiple times in this function. + auto DbgDIExpr = DbgValueToExpression[DVI]; auto Offset = std::get(EV); auto &Ctx = DVI->getContext(); - DVI->setOperand(0, MetadataAsValue::get(Ctx, ValueAsMetadata::get(V))); + DVI->replaceVariableLocationOp(Idx, EVHandle); if (Offset) { SmallVector Ops; DIExpression::appendOffset(Ops, Offset); - DbgDIExpr = DIExpression::prependOpcodes(DbgDIExpr, Ops, true); + DbgDIExpr = DIExpression::appendOpsToArg(DbgDIExpr, Ops, Idx, true); } - DVI->setOperand(2, MetadataAsValue::get(Ctx, DbgDIExpr)); + DVI->setExpression(DbgDIExpr); + DbgValueToExpression[DVI] = DbgDIExpr; break; } } @@ -5848,7 +5862,8 @@ // Debug preservation - before we start removing anything create equivalence // sets for the llvm.dbg.value intrinsics. EqualValuesMap DbgValueToEqualSet; - DbgGatherEqualValues(L, SE, DbgValueToEqualSet); + ExpressionMap DbgValueToExpression; + DbgGatherEqualValues(L, SE, DbgValueToEqualSet, DbgValueToExpression); // Remove any extra phis created by processing inner loops. Changed |= DeleteDeadPHIs(L->getHeader(), &TLI, MSSAU.get()); @@ -5868,7 +5883,7 @@ } } - DbgApplyEqualValues(DbgValueToEqualSet); + DbgApplyEqualValues(DbgValueToEqualSet, DbgValueToExpression); return Changed; } diff --git a/llvm/lib/Transforms/Scalar/SpeculativeExecution.cpp b/llvm/lib/Transforms/Scalar/SpeculativeExecution.cpp --- a/llvm/lib/Transforms/Scalar/SpeculativeExecution.cpp +++ b/llvm/lib/Transforms/Scalar/SpeculativeExecution.cpp @@ -265,11 +265,13 @@ const auto AllPrecedingUsesFromBlockHoisted = [&NotHoisted](const User *U) { // Debug variable has special operand to check it's not hoisted. if (const auto *DVI = dyn_cast(U)) { - if (const auto *I = - dyn_cast_or_null(DVI->getVariableLocation())) - if (NotHoisted.count(I) == 0) - return true; - return false; + return all_of(DVI->location_ops(), [&NotHoisted](Value *V) { + if (const auto *I = dyn_cast_or_null(V)) { + if (NotHoisted.count(I) == 0) + return true; + } + return false; + }); } // Usially debug label instrinsic corresponds to label in LLVM IR. In these diff --git a/llvm/lib/Transforms/Utils/BasicBlockUtils.cpp b/llvm/lib/Transforms/Utils/BasicBlockUtils.cpp --- a/llvm/lib/Transforms/Utils/BasicBlockUtils.cpp +++ b/llvm/lib/Transforms/Utils/BasicBlockUtils.cpp @@ -409,7 +409,8 @@ /// - Keep track of non-overlapping fragments. static bool removeRedundantDbgInstrsUsingForwardScan(BasicBlock *BB) { SmallVector ToBeRemoved; - DenseMap > VariableMap; + DenseMap, DIExpression *>> + VariableMap; for (auto &I : *BB) { if (DbgValueInst *DVI = dyn_cast(&I)) { DebugVariable Key(DVI->getVariable(), @@ -418,10 +419,10 @@ auto VMI = VariableMap.find(Key); // Update the map if we found a new value/expression describing the // variable, or if the variable wasn't mapped already. - if (VMI == VariableMap.end() || - VMI->second.first != DVI->getValue() || + SmallVector Values(DVI->getValues()); + if (VMI == VariableMap.end() || VMI->second.first != Values || VMI->second.second != DVI->getExpression()) { - VariableMap[Key] = { DVI->getValue(), DVI->getExpression() }; + VariableMap[Key] = {Values, DVI->getExpression()}; continue; } // Found an identical mapping. Remember the instruction for later removal. diff --git a/llvm/lib/Transforms/Utils/CodeExtractor.cpp b/llvm/lib/Transforms/Utils/CodeExtractor.cpp --- a/llvm/lib/Transforms/Utils/CodeExtractor.cpp +++ b/llvm/lib/Transforms/Utils/CodeExtractor.cpp @@ -1513,20 +1513,19 @@ continue; } - // If the location isn't a constant or an instruction, delete the - // intrinsic. - auto *DVI = cast(DII); - Value *Location = DVI->getVariableLocation(); - if (!Location || - (!isa(Location) && !isa(Location))) { - DebugIntrinsicsToDelete.push_back(DVI); - continue; - } - - // If the variable location is an instruction but isn't in the new - // function, delete the intrinsic. - Instruction *LocationInst = dyn_cast(Location); - if (LocationInst && LocationInst->getFunction() != &NewFunc) { + auto IsInvalidLocation = [&NewFunc](Value *Location) { + // Location is invalid if it isn't a constant or an instruction, or is an + // instruction but isn't in the new function. + if (!Location || + (!isa(Location) && !isa(Location))) + return true; + Instruction *LocationInst = dyn_cast(Location); + return LocationInst && LocationInst->getFunction() != &NewFunc; + }; + + auto *DVI = cast(DII); + // If any of the used locations are invalid, delete the intrinsic. + if (any_of(DVI->location_ops(), IsInvalidLocation)) { DebugIntrinsicsToDelete.push_back(DVI); continue; } @@ -1539,7 +1538,7 @@ NewSP, OldVar->getName(), OldVar->getFile(), OldVar->getLine(), OldVar->getType(), /*AlwaysPreserve=*/false, DINode::FlagZero, OldVar->getAlignInBits()); - DVI->setArgOperand(1, MetadataAsValue::get(Ctx, NewVar)); + DVI->setVariable(cast(NewVar)); } for (auto *DII : DebugIntrinsicsToDelete) DII->eraseFromParent(); diff --git a/llvm/lib/Transforms/Utils/Debugify.cpp b/llvm/lib/Transforms/Utils/Debugify.cpp --- a/llvm/lib/Transforms/Utils/Debugify.cpp +++ b/llvm/lib/Transforms/Utils/Debugify.cpp @@ -264,15 +264,16 @@ // // TODO: This, along with a check for non-null value operands, should be // promoted to verifier failures. - Value *V = DVI->getValue(); - if (!V) - return false; // For now, don't try to interpret anything more complicated than an empty // DIExpression. Eventually we should try to handle OP_deref and fragments. if (DVI->getExpression()->getNumElements()) return false; + Value *V = DVI->getVariableLocationOp(0); + if (!V) + return false; + Type *Ty = V->getType(); uint64_t ValueOperandSize = getAllocSizeInBits(M, Ty); Optional DbgVarSize = DVI->getFragmentSizeInBits(); diff --git a/llvm/lib/Transforms/Utils/LCSSA.cpp b/llvm/lib/Transforms/Utils/LCSSA.cpp --- a/llvm/lib/Transforms/Utils/LCSSA.cpp +++ b/llvm/lib/Transforms/Utils/LCSSA.cpp @@ -236,7 +236,6 @@ llvm::findDbgValues(DbgValues, I); // Update pre-existing debug value uses that reside outside the loop. - auto &Ctx = I->getContext(); for (auto DVI : DbgValues) { BasicBlock *UserBB = DVI->getParent(); if (InstBB == UserBB || L->contains(UserBB)) @@ -247,7 +246,7 @@ Value *V = AddedPHIs.size() == 1 ? AddedPHIs[0] : SSAUpdate.FindValueForBlock(UserBB); if (V) - DVI->setOperand(0, MetadataAsValue::get(Ctx, ValueAsMetadata::get(V))); + DVI->replaceVariableLocationOp(I, V); } // SSAUpdater might have inserted phi-nodes inside other loops. We'll need diff --git a/llvm/lib/Transforms/Utils/Local.cpp b/llvm/lib/Transforms/Utils/Local.cpp --- a/llvm/lib/Transforms/Utils/Local.cpp +++ b/llvm/lib/Transforms/Utils/Local.cpp @@ -410,7 +410,7 @@ return true; } if (DbgValueInst *DVI = dyn_cast(I)) { - if (DVI->getValue()) + if (DVI->hasArgList() || DVI->getValue(0)) return false; return true; } @@ -562,8 +562,7 @@ findDbgUsers(DbgUsers, I); for (auto *DII : DbgUsers) { Value *Undef = UndefValue::get(I->getType()); - DII->setOperand(0, MetadataAsValue::get(DII->getContext(), - ValueAsMetadata::get(Undef))); + DII->replaceVariableLocationOp(I, Undef); } return !DbgUsers.empty(); } @@ -1331,7 +1330,7 @@ SmallVector DbgValues; findDbgValues(DbgValues, APN); for (auto *DVI : DbgValues) { - assert(DVI->getValue() == APN); + assert(is_contained(DVI->getValues(), APN)); if ((DVI->getVariable() == DIVar) && (DVI->getExpression() == DIExpr)) return true; } @@ -1358,13 +1357,19 @@ // We can't always calculate the size of the DI variable (e.g. if it is a // VLA). Try to use the size of the alloca that the dbg intrinsic describes // intead. - if (DII->isAddressOfVariable()) - if (auto *AI = dyn_cast_or_null(DII->getVariableLocation())) + if (DII->isAddressOfVariable()) { + // DII should have exactly 1 location when it is an address. + assert(DII->getNumVariableLocationOps() == 1 && + "address of variable must have exactly 1 location operand."); + if (auto *AI = + dyn_cast_or_null(DII->getVariableLocationOp(0))) { if (Optional FragmentSize = AI->getAllocationSizeInBits(DL)) { assert(ValueSize.isScalable() == FragmentSize->isScalable() && "Both sizes should agree on the scalable flag."); return TypeSize::isKnownGE(ValueSize, *FragmentSize); } + } + } // Could not determine size of variable. Conservatively return false. return false; } @@ -1567,35 +1572,56 @@ ValueToValueMapTy DbgValueMap; for (auto &I : *BB) { if (auto DbgII = dyn_cast(&I)) { - if (auto *Loc = dyn_cast_or_null(DbgII->getVariableLocation())) - DbgValueMap.insert({Loc, DbgII}); + for (Value *V : DbgII->location_ops()) + if (auto *Loc = dyn_cast_or_null(V)) + DbgValueMap.insert({Loc, DbgII}); } } if (DbgValueMap.size() == 0) return; + // Map a pair of the destination BB and old dbg.value to the new dbg.value, + // so that if a dbg.value is being rewritten to use more than one of the + // inserted PHIs in the same destination BB, we can update the same dbg.value + // with all the new PHIs instead of creating one copy for each. + SmallDenseMap, + DbgVariableIntrinsic *> + NewDbgValueMap; // Then iterate through the new PHIs and look to see if they use one of the - // previously mapped PHIs. If so, insert a new dbg.value intrinsic that will - // propagate the info through the new PHI. - LLVMContext &C = BB->getContext(); + // previously mapped PHIs. If so, create a new dbg.value intrinsic that will + // propagate the info through the new PHI. If we use more than one new PHI in + // a single destination BB with the same old dbg.value, merge the updates so + // that we get a single new dbg.value with all the new PHIs. for (auto PHI : InsertedPHIs) { BasicBlock *Parent = PHI->getParent(); // Avoid inserting an intrinsic into an EH block. if (Parent->getFirstNonPHI()->isEHPad()) continue; - auto PhiMAV = MetadataAsValue::get(C, ValueAsMetadata::get(PHI)); for (auto VI : PHI->operand_values()) { auto V = DbgValueMap.find(VI); if (V != DbgValueMap.end()) { auto *DbgII = cast(V->second); - Instruction *NewDbgII = DbgII->clone(); - NewDbgII->setOperand(0, PhiMAV); - auto InsertionPt = Parent->getFirstInsertionPt(); - assert(InsertionPt != Parent->end() && "Ill-formed basic block"); - NewDbgII->insertBefore(&*InsertionPt); + auto NewDI = NewDbgValueMap.find({Parent, DbgII}); + if (NewDI == NewDbgValueMap.end()) { + auto *NewDbgII = cast(DbgII->clone()); + NewDI = NewDbgValueMap.insert({{Parent, DbgII}, NewDbgII}).first; + } + DbgVariableIntrinsic *NewDbgII = NewDI->second; + // If PHI contains VI as an operand more than once, we may + // replaced it in NewDbgII; confirm that it is present. + if (is_contained(NewDbgII->location_ops(), VI)) + NewDbgII->replaceVariableLocationOp(VI, PHI); } } } + // Insert thew new dbg.values into their destination blocks. + for (auto DI : NewDbgValueMap) { + BasicBlock *Parent = DI.first.first; + auto *NewDbgII = DI.second; + auto InsertionPt = Parent->getFirstInsertionPt(); + assert(InsertionPt != Parent->end() && "Ill-formed basic block"); + NewDbgII->insertBefore(&*InsertionPt); + } } /// Finds all intrinsics declaring local variables as living in the memory that @@ -1636,11 +1662,25 @@ // DenseMap lookup. if (!V->isUsedByMetadata()) return; - if (auto *L = LocalAsMetadata::getIfExists(V)) - if (auto *MDV = MetadataAsValue::getIfExists(V->getContext(), L)) + // TODO: If this value appears multiple times in a DIArgList, we should still + // only add the owning DbgValueInst once; use this set to track ArgListUsers. + // This behaviour can be removed when we can automatically remove duplicates. + SmallPtrSet EncounteredDbgValues; + if (auto *L = LocalAsMetadata::getIfExists(V)) { + if (auto *MDV = MetadataAsValue::getIfExists(V->getContext(), L)) { for (User *U : MDV->users()) if (DbgValueInst *DVI = dyn_cast(U)) DbgValues.push_back(DVI); + } + for (Metadata *AL : L->getAllArgListUsers()) { + if (auto *MDV = MetadataAsValue::getIfExists(V->getContext(), AL)) { + for (User *U : MDV->users()) + if (DbgValueInst *DVI = dyn_cast(U)) + if (EncounteredDbgValues.insert(DVI).second) + DbgValues.push_back(DVI); + } + } + } } void llvm::findDbgUsers(SmallVectorImpl &DbgUsers, @@ -1649,11 +1689,25 @@ // DenseMap lookup. if (!V->isUsedByMetadata()) return; - if (auto *L = LocalAsMetadata::getIfExists(V)) - if (auto *MDV = MetadataAsValue::getIfExists(V->getContext(), L)) + // TODO: If this value appears multiple times in a DIArgList, we should still + // only add the owning DbgValueInst once; use this set to track ArgListUsers. + // This behaviour can be removed when we can automatically remove duplicates. + SmallPtrSet EncounteredDbgValues; + if (auto *L = LocalAsMetadata::getIfExists(V)) { + if (auto *MDV = MetadataAsValue::getIfExists(V->getContext(), L)) { for (User *U : MDV->users()) if (DbgVariableIntrinsic *DII = dyn_cast(U)) DbgUsers.push_back(DII); + } + for (Metadata *AL : L->getAllArgListUsers()) { + if (auto *MDV = MetadataAsValue::getIfExists(V->getContext(), AL)) { + for (User *U : MDV->users()) + if (DbgVariableIntrinsic *DII = dyn_cast(U)) + if (EncounteredDbgValues.insert(DII).second) + DbgUsers.push_back(DII); + } + } + } } bool llvm::replaceDbgDeclare(Value *Address, Value *NewAddress, @@ -1723,26 +1777,32 @@ void llvm::salvageDebugInfoForDbgValues( Instruction &I, ArrayRef DbgUsers) { - auto &Ctx = I.getContext(); bool Salvaged = false; - auto wrapMD = [&](Value *V) { return wrapValueInMetadata(Ctx, V); }; for (auto *DII : DbgUsers) { // Do not add DW_OP_stack_value for DbgDeclare and DbgAddr, because they // are implicitly pointing out the value as a DWARF memory location // description. bool StackValue = isa(DII); - - DIExpression *DIExpr = - salvageDebugInfoImpl(I, DII->getExpression(), StackValue); + auto DIILocation = DII->location_ops(); + assert( + is_contained(DIILocation, &I) && + "DbgVariableIntrinsic must use salvaged instruction as its location"); + unsigned LocNo = std::distance(DIILocation.begin(), find(DIILocation, &I)); + SmallVector AdditionalValues; + DIExpression *SalvagedExpr = salvageDebugInfoImpl( + I, DII->getExpression(), StackValue, LocNo, AdditionalValues); // salvageDebugInfoImpl should fail on examining the first element of // DbgUsers, or none of them. - if (!DIExpr) + if (!SalvagedExpr) break; - DII->setOperand(0, wrapMD(I.getOperand(0))); - DII->setOperand(2, MetadataAsValue::get(Ctx, DIExpr)); + DII->replaceVariableLocationOp(&I, I.getOperand(0)); + if (AdditionalValues.empty()) + DII->setExpression(SalvagedExpr); + else + DII->addVariableLocationOps(AdditionalValues, SalvagedExpr); LLVM_DEBUG(dbgs() << "SALVAGE: " << *DII << '\n'); Salvaged = true; } @@ -1752,14 +1812,109 @@ for (auto *DII : DbgUsers) { Value *Undef = UndefValue::get(I.getType()); - DII->setOperand(0, MetadataAsValue::get(DII->getContext(), - ValueAsMetadata::get(Undef))); - } -} - -DIExpression *llvm::salvageDebugInfoImpl(Instruction &I, - DIExpression *SrcDIExpr, - bool WithStackValue) { + DII->replaceVariableLocationOp(&I, Undef); + } +} + +bool getSalvageOpsForGEP(GetElementPtrInst *GEP, const DataLayout &DL, + uint64_t CurrentLocOps, + SmallVectorImpl &Opcodes, + SmallVectorImpl &AdditionalValues) { + unsigned BitWidth = DL.getIndexSizeInBits(GEP->getPointerAddressSpace()); + // Rewrite a GEP into a DIExpression. + SmallDenseMap VariableOffsets; + APInt ConstantOffset(BitWidth, 0); + if (!GEP->collectOffset(DL, BitWidth, VariableOffsets, ConstantOffset)) + return false; + if (!VariableOffsets.empty() && !CurrentLocOps) { + Opcodes.insert(Opcodes.begin(), {dwarf::DW_OP_LLVM_arg, 0}); + CurrentLocOps = 1; + } + for (auto Offset : VariableOffsets) { + AdditionalValues.push_back(Offset.first); + assert(Offset.second.isStrictlyPositive() && + "Expected strictly positive multiplier for offset."); + Opcodes.append({dwarf::DW_OP_LLVM_arg, CurrentLocOps++, dwarf::DW_OP_constu, + Offset.second.getZExtValue(), dwarf::DW_OP_mul, + dwarf::DW_OP_plus}); + } + DIExpression::appendOffset(Opcodes, ConstantOffset.getSExtValue()); + return true; +} + +uint64_t getDwarfOpForBinOp(Instruction::BinaryOps Opcode) { + switch (Opcode) { + case Instruction::Add: + return dwarf::DW_OP_plus; + case Instruction::Sub: + return dwarf::DW_OP_minus; + case Instruction::Mul: + return dwarf::DW_OP_mul; + case Instruction::SDiv: + return dwarf::DW_OP_div; + case Instruction::SRem: + return dwarf::DW_OP_mod; + case Instruction::Or: + return dwarf::DW_OP_or; + case Instruction::And: + return dwarf::DW_OP_and; + case Instruction::Xor: + return dwarf::DW_OP_xor; + case Instruction::Shl: + return dwarf::DW_OP_shl; + case Instruction::LShr: + return dwarf::DW_OP_shr; + case Instruction::AShr: + return dwarf::DW_OP_shra; + default: + // TODO: Salvage from each kind of binop we know about. + return 0; + } +} + +bool getSalvageOpsForBinOp(BinaryOperator *BI, uint64_t CurrentLocOps, + SmallVectorImpl &Opcodes, + SmallVectorImpl &AdditionalValues) { + // Handle binary operations with constant integer operands as a special case. + auto *ConstInt = dyn_cast(BI->getOperand(1)); + // Values wider than 64 bits cannot be represented within a DIExpression. + if (ConstInt && ConstInt->getBitWidth() > 64) + return false; + + Instruction::BinaryOps BinOpcode = BI->getOpcode(); + // Push any Constant Int operand onto the expression stack. + if (ConstInt) { + uint64_t Val = ConstInt->getSExtValue(); + // Add or Sub Instructions with a constant operand can potentially be + // simplified. + if (BinOpcode == Instruction::Add || BinOpcode == Instruction::Sub) { + uint64_t Offset = BinOpcode == Instruction::Add ? Val : -int64_t(Val); + DIExpression::appendOffset(Opcodes, Offset); + return true; + } + Opcodes.append({dwarf::DW_OP_constu, Val}); + } else { + if (!CurrentLocOps) { + Opcodes.append({dwarf::DW_OP_LLVM_arg, 0}); + CurrentLocOps = 1; + } + Opcodes.append({dwarf::DW_OP_LLVM_arg, CurrentLocOps}); + AdditionalValues.push_back(BI->getOperand(1)); + } + + // Add salvaged binary operator to expression stack. + uint64_t DwarfBinOp = getDwarfOpForBinOp(BinOpcode); + if (!DwarfBinOp) + return false; + Opcodes.push_back(DwarfBinOp); + return true; +} + +DIExpression * +llvm::salvageDebugInfoImpl(Instruction &I, DIExpression *SrcDIExpr, + bool WithStackValue, unsigned LocNo, + SmallVectorImpl &AdditionalValues) { + uint64_t CurrentLocOps = SrcDIExpr->getNumLocationOperands(); auto &M = *I.getModule(); auto &DL = M.getDataLayout(); @@ -1767,20 +1922,13 @@ auto doSalvage = [&](SmallVectorImpl &Ops) -> DIExpression * { DIExpression *DIExpr = SrcDIExpr; if (!Ops.empty()) { - DIExpr = DIExpression::prependOpcodes(DIExpr, Ops, WithStackValue); + DIExpr = DIExpression::appendOpsToArg(DIExpr, Ops, LocNo, WithStackValue); } return DIExpr; }; - // Apply the given offset to the source DIExpression. - auto applyOffset = [&](uint64_t Offset) -> DIExpression * { - SmallVector Ops; - DIExpression::appendOffset(Ops, Offset); - return doSalvage(Ops); - }; - // initializer-list helper for applying operators to the source DIExpression. - auto applyOps = [&](ArrayRef Opcodes) -> DIExpression * { + auto applyOps = [&](ArrayRef Opcodes) { SmallVector Ops(Opcodes.begin(), Opcodes.end()); return doSalvage(Ops); }; @@ -1804,54 +1952,17 @@ isa(&I))); } + SmallVector Ops; if (auto *GEP = dyn_cast(&I)) { - unsigned BitWidth = - M.getDataLayout().getIndexSizeInBits(GEP->getPointerAddressSpace()); - // Rewrite a constant GEP into a DIExpression. - APInt Offset(BitWidth, 0); - if (GEP->accumulateConstantOffset(M.getDataLayout(), Offset)) { - return applyOffset(Offset.getSExtValue()); - } else { - return nullptr; - } + if (getSalvageOpsForGEP(GEP, DL, CurrentLocOps, Ops, AdditionalValues)) + return doSalvage(Ops); } else if (auto *BI = dyn_cast(&I)) { - // Rewrite binary operations with constant integer operands. - auto *ConstInt = dyn_cast(I.getOperand(1)); - if (!ConstInt || ConstInt->getBitWidth() > 64) - return nullptr; - - uint64_t Val = ConstInt->getSExtValue(); - switch (BI->getOpcode()) { - case Instruction::Add: - return applyOffset(Val); - case Instruction::Sub: - return applyOffset(-int64_t(Val)); - case Instruction::Mul: - return applyOps({dwarf::DW_OP_constu, Val, dwarf::DW_OP_mul}); - case Instruction::SDiv: - return applyOps({dwarf::DW_OP_constu, Val, dwarf::DW_OP_div}); - case Instruction::SRem: - return applyOps({dwarf::DW_OP_constu, Val, dwarf::DW_OP_mod}); - case Instruction::Or: - return applyOps({dwarf::DW_OP_constu, Val, dwarf::DW_OP_or}); - case Instruction::And: - return applyOps({dwarf::DW_OP_constu, Val, dwarf::DW_OP_and}); - case Instruction::Xor: - return applyOps({dwarf::DW_OP_constu, Val, dwarf::DW_OP_xor}); - case Instruction::Shl: - return applyOps({dwarf::DW_OP_constu, Val, dwarf::DW_OP_shl}); - case Instruction::LShr: - return applyOps({dwarf::DW_OP_constu, Val, dwarf::DW_OP_shr}); - case Instruction::AShr: - return applyOps({dwarf::DW_OP_constu, Val, dwarf::DW_OP_shra}); - default: - // TODO: Salvage constants from each kind of binop we know about. - return nullptr; - } - // *Not* to do: we should not attempt to salvage load instructions, - // because the validity and lifetime of a dbg.value containing - // DW_OP_deref becomes difficult to analyze. See PR40628 for examples. + if (getSalvageOpsForBinOp(BI, CurrentLocOps, Ops, AdditionalValues)) + return doSalvage(Ops); } + // *Not* to do: we should not attempt to salvage load instructions, + // because the validity and lifetime of a dbg.value containing + // DW_OP_deref becomes difficult to analyze. See PR40628 for examples. return nullptr; } @@ -1897,13 +2008,12 @@ if (UndefOrSalvage.count(DII)) continue; - LLVMContext &Ctx = DII->getContext(); DbgValReplacement DVR = RewriteExpr(*DII); if (!DVR) continue; - DII->setOperand(0, wrapValueInMetadata(Ctx, &To)); - DII->setOperand(2, MetadataAsValue::get(Ctx, *DVR)); + DII->replaceVariableLocationOp(&From, &To); + DII->setExpression(*DVR); LLVM_DEBUG(dbgs() << "REWRITE: " << *DII << '\n'); Changed = true; } diff --git a/llvm/lib/Transforms/Utils/LoopRotationUtils.cpp b/llvm/lib/Transforms/Utils/LoopRotationUtils.cpp --- a/llvm/lib/Transforms/Utils/LoopRotationUtils.cpp +++ b/llvm/lib/Transforms/Utils/LoopRotationUtils.cpp @@ -176,9 +176,7 @@ NewVal = SSA.GetValueInMiddleOfBlock(UserBB); else NewVal = UndefValue::get(OrigHeaderVal->getType()); - DbgValue->setOperand(0, - MetadataAsValue::get(OrigHeaderVal->getContext(), - ValueAsMetadata::get(NewVal))); + DbgValue->replaceVariableLocationOp(OrigHeaderVal, NewVal); } } } @@ -378,11 +376,15 @@ // possible or create a clone in the OldPreHeader if not. Instruction *LoopEntryBranch = OrigPreheader->getTerminator(); - // Record all debug intrinsics preceding LoopEntryBranch to avoid duplication. + // Record all debug intrinsics preceding LoopEntryBranch to avoid + // duplication. using DbgIntrinsicHash = - std::pair, DIExpression *>; + std::pair, DIExpression *>; auto makeHash = [](DbgVariableIntrinsic *D) -> DbgIntrinsicHash { - return {{D->getVariableLocation(), D->getVariable()}, D->getExpression()}; + auto VarLocOps = D->location_ops(); + return {{hash_combine_range(VarLocOps.begin(), VarLocOps.end()), + D->getVariable()}, + D->getExpression()}; }; SmallDenseSet DbgIntrinsics; for (auto I = std::next(OrigPreheader->rbegin()), E = OrigPreheader->rend(); diff --git a/llvm/lib/Transforms/Utils/ValueMapper.cpp b/llvm/lib/Transforms/Utils/ValueMapper.cpp --- a/llvm/lib/Transforms/Utils/ValueMapper.cpp +++ b/llvm/lib/Transforms/Utils/ValueMapper.cpp @@ -390,6 +390,26 @@ : MetadataAsValue::get(V->getContext(), MDTuple::get(V->getContext(), None)); } + if (auto *AL = dyn_cast(MD)) { + SmallVector MappedArgs; + for (auto *VAM : AL->getArgs()) { + // Map both Local and Constant VAMs here; they will both ultimately + // be mapped via mapValue (apart from constants when we have no + // module level changes, which have an identity mapping). + if ((Flags & RF_NoModuleLevelChanges) && isa(VAM)) { + MappedArgs.push_back(VAM); + } else if (Value *LV = mapValue(VAM->getValue())) { + MappedArgs.push_back( + LV == VAM->getValue() ? VAM : ValueAsMetadata::get(LV)); + } else { + // If we cannot map the value, set the argument as undef. + MappedArgs.push_back(ValueAsMetadata::get( + UndefValue::get(VAM->getValue()->getType()))); + } + } + return MetadataAsValue::get(V->getContext(), + DIArgList::get(V->getContext(), MappedArgs)); + } // If this is a module-level metadata and we know that nothing at the module // level is changing, then use an identity mapping. diff --git a/llvm/test/CodeGen/MIR/Generic/dbg-value-list-spill.mir b/llvm/test/CodeGen/MIR/Generic/dbg-value-list-spill.mir new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/MIR/Generic/dbg-value-list-spill.mir @@ -0,0 +1,194 @@ +# RUN: llc -run-pass regallocfast -o - %s | FileCheck %s +# Test that when virtual registers referenced by a DBG_VALUE_LIST are spilled, +# the reference to the register is replaced by the stack pointer (with ) and all +# DW_OP_LLVM_arg operators that reference it have an offset (if the offset is +# non-zero) and DW_OP_deref inserted after. + +# CHECK-DAG: ![[VAR_A:[0-9]+]] = !DILocalVariable(name: "a" +# CHECK-DAG: ![[VAR_B:[0-9]+]] = !DILocalVariable(name: "b" +# CHECK-DAG: ![[VAR_C:[0-9]+]] = !DILocalVariable(name: "c" + +# CHECK-LABEL: bb.0.entry: +# CHECK: DBG_VALUE_LIST ![[VAR_C]], !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_deref, DW_OP_LLVM_arg, 1, DW_OP_plus_uconst, 8, DW_OP_deref, DW_OP_mul, DW_OP_stack_value), $rsp, $rsp, +# CHECK-LABEL: INLINEASM +# CHECK-DAG: DBG_VALUE $[[RESTORE_A:[a-z0-9]+]], $noreg, ![[VAR_A]] +# CHECK-DAG: DBG_VALUE $[[RESTORE_B:[a-z0-9]+]], $noreg, ![[VAR_B]] +# CHECK-DAG: DBG_VALUE_LIST ![[VAR_C]], !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_arg, 1, DW_OP_mul, DW_OP_stack_value), $[[RESTORE_A]], $[[RESTORE_B]] + +# Produced from the source: +# #define FORCE_SPILL() \ +# __asm volatile("" : : : \ +# "rax", "rbx", "rcx", "rdx", "rsi", "rdi", "rbp", "r8", \ +# "r9", "r10", "r11", "r12", "r13", "r14", "r15") +# +# long long add(long long a, long long b) { +# long long c = a * b; +# FORCE_SPILL(); +# return a + b; +# } + + +--- | + ; ModuleID = 'dbg-value-list-spill.cpp' + source_filename = "dbg-value-list-spill.cpp" + target datalayout = "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" + target triple = "x86_64-pc-windows-msvc19.28.29336" + + define dso_local i64 @"?add@@YA_J_J0@Z"(i64 %a, i64 %b) local_unnamed_addr !dbg !8 { + entry: + call void @llvm.dbg.value(metadata i64 %b, metadata !13, metadata !DIExpression()), !dbg !16 + call void @llvm.dbg.value(metadata i64 %a, metadata !14, metadata !DIExpression()), !dbg !16 + call void @llvm.dbg.value(metadata i64 undef, metadata !15, metadata !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_arg, 1, DW_OP_mul, DW_OP_stack_value)), !dbg !16 + tail call void asm sideeffect "", "~{rax},~{rbx},~{rcx},~{rdx},~{rsi},~{rdi},~{rbp},~{r8},~{r9},~{r10},~{r11},~{r12},~{r13},~{r14},~{r15},~{dirflag},~{fpsr},~{flags}"() #2, !dbg !17, !srcloc !18 + %add = add nsw i64 %b, %a, !dbg !19 + ret i64 %add, !dbg !19 + } + + declare void @llvm.dbg.value(metadata, metadata, metadata) + + !llvm.dbg.cu = !{!0} + !llvm.module.flags = !{!3, !4, !5, !6} + !llvm.ident = !{!7} + + !0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang version 12.0.0 (https://github.com/llvm/llvm-project.git 2c2601561c49e62448bbbf374c26ae78020f5fe0)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, nameTableKind: None) + !1 = !DIFile(filename: "dbg-value-list-spill.cpp", directory: "C:\\dev\\llvm-project", checksumkind: CSK_MD5, checksum: "c9b3aae84e6f3b8c5b4169c81eb40004") + !2 = !{} + !3 = !{i32 2, !"CodeView", i32 1} + !4 = !{i32 2, !"Debug Info Version", i32 3} + !5 = !{i32 1, !"wchar_size", i32 2} + !6 = !{i32 7, !"PIC Level", i32 2} + !7 = !{!"clang version 12.0.0"} + !8 = distinct !DISubprogram(name: "add", linkageName: "?add@@YA_J_J0@Z", scope: !1, file: !1, line: 6, type: !9, scopeLine: 6, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !12) + !9 = !DISubroutineType(types: !10) + !10 = !{!11, !11, !11} + !11 = !DIBasicType(name: "long long int", size: 64, encoding: DW_ATE_signed) + !12 = !{!13, !14, !15} + !13 = !DILocalVariable(name: "b", arg: 2, scope: !8, file: !1, line: 6, type: !11) + !14 = !DILocalVariable(name: "a", arg: 1, scope: !8, file: !1, line: 6, type: !11) + !15 = !DILocalVariable(name: "c", scope: !8, file: !1, line: 7, type: !11) + !16 = !DILocation(line: 0, scope: !8) + !17 = !DILocation(line: 8, scope: !8) + !18 = !{i32 -2147470861} + !19 = !DILocation(line: 9, scope: !8) + +... +--- +name: '?add@@YA_J_J0@Z' +alignment: 16 +exposesReturnsTwice: false +legalized: false +regBankSelected: false +selected: false +failedISel: false +tracksRegLiveness: true +hasWinCFI: true +registers: [] +liveins: + - { reg: '$rcx', virtual-reg: '' } + - { reg: '$rdx', virtual-reg: '' } +frameInfo: + isFrameAddressTaken: false + isReturnAddressTaken: false + hasStackMap: false + hasPatchPoint: false + stackSize: 80 + offsetAdjustment: -80 + maxAlignment: 8 + adjustsStack: false + hasCalls: false + stackProtector: '' + maxCallFrameSize: 0 + cvBytesOfCalleeSavedRegisters: 64 + hasOpaqueSPAdjustment: false + hasVAStart: false + hasMustTailInVarArgFunc: false + localFrameSize: 0 + savePoint: '' + restorePoint: '' +fixedStack: + - { id: 0, type: spill-slot, offset: -72, size: 8, alignment: 8, stack-id: default, + callee-saved-register: '$rbx', callee-saved-restored: true, debug-info-variable: '', + debug-info-expression: '', debug-info-location: '' } + - { id: 1, type: spill-slot, offset: -64, size: 8, alignment: 16, stack-id: default, + callee-saved-register: '$rbp', callee-saved-restored: true, debug-info-variable: '', + debug-info-expression: '', debug-info-location: '' } + - { id: 2, type: spill-slot, offset: -56, size: 8, alignment: 8, stack-id: default, + callee-saved-register: '$rdi', callee-saved-restored: true, debug-info-variable: '', + debug-info-expression: '', debug-info-location: '' } + - { id: 3, type: spill-slot, offset: -48, size: 8, alignment: 16, stack-id: default, + callee-saved-register: '$rsi', callee-saved-restored: true, debug-info-variable: '', + debug-info-expression: '', debug-info-location: '' } + - { id: 4, type: spill-slot, offset: -40, size: 8, alignment: 8, stack-id: default, + callee-saved-register: '$r12', callee-saved-restored: true, debug-info-variable: '', + debug-info-expression: '', debug-info-location: '' } + - { id: 5, type: spill-slot, offset: -32, size: 8, alignment: 16, stack-id: default, + callee-saved-register: '$r13', callee-saved-restored: true, debug-info-variable: '', + debug-info-expression: '', debug-info-location: '' } + - { id: 6, type: spill-slot, offset: -24, size: 8, alignment: 8, stack-id: default, + callee-saved-register: '$r14', callee-saved-restored: true, debug-info-variable: '', + debug-info-expression: '', debug-info-location: '' } + - { id: 7, type: spill-slot, offset: -16, size: 8, alignment: 16, stack-id: default, + callee-saved-register: '$r15', callee-saved-restored: true, debug-info-variable: '', + debug-info-expression: '', debug-info-location: '' } +stack: + - { id: 0, name: '', type: spill-slot, offset: -80, size: 8, alignment: 8, + stack-id: default, callee-saved-register: '', callee-saved-restored: true, + debug-info-variable: '', debug-info-expression: '', debug-info-location: '' } + - { id: 1, name: '', type: spill-slot, offset: -88, size: 8, alignment: 8, + stack-id: default, callee-saved-register: '', callee-saved-restored: true, + debug-info-variable: '', debug-info-expression: '', debug-info-location: '' } +callSites: [] +debugValueSubstitutions: [] +constants: [] +machineFunctionInfo: {} +body: | + bb.0.entry: + liveins: $rcx, $rdx, $r15, $r14, $r13, $r12, $rsi, $rdi, $rbp, $rbx + + DBG_VALUE $rdx, $noreg, !13, !DIExpression(), debug-location !16 + DBG_VALUE $rcx, $noreg, !14, !DIExpression(), debug-location !16 + frame-setup PUSH64r killed $r15, implicit-def $rsp, implicit $rsp + frame-setup SEH_PushReg 135 + frame-setup PUSH64r killed $r14, implicit-def $rsp, implicit $rsp + frame-setup SEH_PushReg 134 + frame-setup PUSH64r killed $r13, implicit-def $rsp, implicit $rsp + frame-setup SEH_PushReg 133 + frame-setup PUSH64r killed $r12, implicit-def $rsp, implicit $rsp + frame-setup SEH_PushReg 132 + frame-setup PUSH64r killed $rsi, implicit-def $rsp, implicit $rsp + frame-setup SEH_PushReg 57 + frame-setup PUSH64r killed $rdi, implicit-def $rsp, implicit $rsp + frame-setup SEH_PushReg 53 + frame-setup PUSH64r killed $rbp, implicit-def $rsp, implicit $rsp + frame-setup SEH_PushReg 50 + frame-setup PUSH64r killed $rbx, implicit-def $rsp, implicit $rsp + frame-setup SEH_PushReg 51 + $rsp = frame-setup SUB64ri8 $rsp, 16, implicit-def dead $eflags + frame-setup SEH_StackAlloc 16 + frame-setup SEH_EndPrologue + MOV64mr $rsp, 1, $noreg, 8, $noreg, $rdx :: (store 8 into %stack.0) + MOV64mr $rsp, 1, $noreg, 0, $noreg, $rcx :: (store 8 into %stack.1) + DBG_VALUE_LIST !15, !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_deref, DW_OP_LLVM_arg, 1, DW_OP_plus_uconst, 8, DW_OP_deref, DW_OP_mul, DW_OP_stack_value), $rsp, $rsp, debug-location !16 + DBG_VALUE $rsp, 0, !14, !DIExpression(), debug-location !16 + DBG_VALUE $rsp, 0, !13, !DIExpression(DW_OP_plus_uconst, 8), debug-location !16 + INLINEASM &"", 1 /* sideeffect attdialect */, 12 /* clobber */, implicit-def dead early-clobber $rax, 12 /* clobber */, implicit-def dead early-clobber $rbx, 12 /* clobber */, implicit-def dead early-clobber $rcx, 12 /* clobber */, implicit-def dead early-clobber $rdx, 12 /* clobber */, implicit-def dead early-clobber $rsi, 12 /* clobber */, implicit-def dead early-clobber $rdi, 12 /* clobber */, implicit-def dead early-clobber $rbp, 12 /* clobber */, implicit-def dead early-clobber $r8, 12 /* clobber */, implicit-def dead early-clobber $r9, 12 /* clobber */, implicit-def dead early-clobber $r10, 12 /* clobber */, implicit-def dead early-clobber $r11, 12 /* clobber */, implicit-def dead early-clobber $r12, 12 /* clobber */, implicit-def dead early-clobber $r13, 12 /* clobber */, implicit-def dead early-clobber $r14, 12 /* clobber */, implicit-def dead early-clobber $r15, 12 /* clobber */, implicit-def dead early-clobber $df, 12 /* clobber */, implicit-def early-clobber $fpsw, 12 /* clobber */, implicit-def dead early-clobber $eflags, !18, debug-location !17 + renamable $rax = MOV64rm $rsp, 1, $noreg, 8, $noreg :: (load 8 from %stack.0) + DBG_VALUE_LIST !15, !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_deref, DW_OP_LLVM_arg, 1, DW_OP_mul, DW_OP_stack_value), $rsp, $rax, debug-location !16 + DBG_VALUE $rax, $noreg, !13, !DIExpression(), debug-location !16 + renamable $rcx = MOV64rm $rsp, 1, $noreg, 0, $noreg :: (load 8 from %stack.1) + DBG_VALUE_LIST !15, !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_arg, 1, DW_OP_mul, DW_OP_stack_value), $rcx, $rax, debug-location !16 + DBG_VALUE $rcx, $noreg, !14, !DIExpression(), debug-location !16 + $rax = ADD64rr $rax, $rcx, implicit-def $eflags, debug-location !19 + SEH_Epilogue debug-location !19 + $rsp = frame-destroy ADD64ri8 $rsp, 16, implicit-def dead $eflags, debug-location !19 + $rbx = frame-destroy POP64r implicit-def $rsp, implicit $rsp, debug-location !19 + $rbp = frame-destroy POP64r implicit-def $rsp, implicit $rsp, debug-location !19 + $rdi = frame-destroy POP64r implicit-def $rsp, implicit $rsp, debug-location !19 + $rsi = frame-destroy POP64r implicit-def $rsp, implicit $rsp, debug-location !19 + $r12 = frame-destroy POP64r implicit-def $rsp, implicit $rsp, debug-location !19 + $r13 = frame-destroy POP64r implicit-def $rsp, implicit $rsp, debug-location !19 + $r14 = frame-destroy POP64r implicit-def $rsp, implicit $rsp, debug-location !19 + $r15 = frame-destroy POP64r implicit-def $rsp, implicit $rsp, debug-location !19 + RETQ $rax, debug-location !19 + +... diff --git a/llvm/test/CodeGen/MIR/Generic/dbg-value-list.mir b/llvm/test/CodeGen/MIR/Generic/dbg-value-list.mir new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/MIR/Generic/dbg-value-list.mir @@ -0,0 +1,63 @@ +# RUN: llc -run-pass machineverifier -o - %s | FileCheck %s +# Simple round-trip test for DBG_VALUE_LIST. +# CHECK: DBG_VALUE_LIST !14, !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_arg, 1, DW_OP_plus, DW_OP_stack_value), $edi, $esi, debug-location !15 +--- | + ; ModuleID = 'test.cpp' + source_filename = "test.cpp" + target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" + target triple = "x86_64-unknown-linux-gnu" + + ; Function Attrs: norecurse nounwind readnone uwtable + define dso_local i32 @_Z3fooii(i32 %a, i32 %b) local_unnamed_addr !dbg !7 { + entry: + call void @llvm.dbg.value(metadata i32 %a, metadata !12, metadata !DIExpression()), !dbg !15 + call void @llvm.dbg.value(metadata i32 %b, metadata !13, metadata !DIExpression()), !dbg !15 + call void @llvm.dbg.value(metadata i32 undef, metadata !14, metadata !DIExpression()), !dbg !15 + %mul = mul nsw i32 %b, %a, !dbg !16 + ret i32 %mul, !dbg !17 + } + + ; Function Attrs: nounwind readnone speculatable willreturn + declare void @llvm.dbg.value(metadata, metadata, metadata) + + !llvm.dbg.cu = !{!0} + !llvm.module.flags = !{!3, !4, !5} + !llvm.ident = !{!6} + + !0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang version 11.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, splitDebugInlining: false, nameTableKind: None) + !1 = !DIFile(filename: "test.cpp", directory: "/") + !2 = !{} + !3 = !{i32 7, !"Dwarf Version", i32 4} + !4 = !{i32 2, !"Debug Info Version", i32 3} + !5 = !{i32 1, !"wchar_size", i32 4} + !6 = !{!"clang version 11.0.0"} + !7 = distinct !DISubprogram(name: "foo", linkageName: "_Z3fooii", scope: !1, file: !1, line: 2, type: !8, scopeLine: 2, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !11) + !8 = !DISubroutineType(types: !9) + !9 = !{!10, !10, !10} + !10 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) + !11 = !{!12, !13, !14} + !12 = !DILocalVariable(name: "a", arg: 1, scope: !7, file: !1, line: 2, type: !10) + !13 = !DILocalVariable(name: "b", arg: 2, scope: !7, file: !1, line: 2, type: !10) + !14 = !DILocalVariable(name: "c", scope: !7, file: !1, line: 3, type: !10) + !15 = !DILocation(line: 0, scope: !7) + !16 = !DILocation(line: 4, column: 12, scope: !7) + !17 = !DILocation(line: 4, column: 3, scope: !7) + +... +--- +name: _Z3fooii +body: | + bb.0.entry: + liveins: $edi, $esi + + DBG_VALUE $edi, $noreg, !12, !DIExpression(), debug-location !15 + DBG_VALUE $esi, $noreg, !13, !DIExpression(), debug-location !15 + $eax = MOV32rr $edi + DBG_VALUE_LIST !14, !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_arg, 1, DW_OP_plus, DW_OP_stack_value), $edi, $esi, debug-location !15 + DBG_VALUE $esi, $noreg, !13, !DIExpression(), debug-location !15 + DBG_VALUE $eax, $noreg, !12, !DIExpression(), debug-location !15 + renamable $eax = nsw IMUL32rr killed renamable $eax, killed renamable $esi, implicit-def dead $eflags, debug-location !16 + RETQ $eax, debug-location !17 + +... + diff --git a/llvm/test/DebugInfo/Generic/debug_value_list.ll b/llvm/test/DebugInfo/Generic/debug_value_list.ll new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/Generic/debug_value_list.ll @@ -0,0 +1,50 @@ +; RUN: opt -verify < %s | opt -verify -S | FileCheck %s + +; Simple IR-BC-IR round-trip test for a @llvm.dbg.value that uses !DIArgList +; and DW_OP_LLVM_arg. + +source_filename = ".\\debug_value_list.cpp" +target datalayout = "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-windows-msvc19.16.27034" + +; CHECK-COUNT-3: llvm.dbg.value( +; CHECK-SAME: metadata !DIArgList(i32 %a, i32 %b) +; CHECK-SAME: metadata !16, +; CHECK-SAME: metadata !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_arg, 1, DW_OP_plus) +define dso_local i32 @"?foo@@YAHHH@Z"(i32 %a, i32 %b) local_unnamed_addr !dbg !8 { +entry: + call void @llvm.dbg.value(metadata !DIArgList(i32 %b), metadata !14, metadata !DIExpression(DW_OP_LLVM_arg, 0)), !dbg !17 + call void @llvm.dbg.value(metadata !DIArgList(i32 %a), metadata !15, metadata !DIExpression(DW_OP_LLVM_arg, 0)), !dbg !17 + call void @llvm.dbg.value( + metadata !DIArgList(i32 %a, i32 %b), + metadata !16, + metadata !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_arg, 1, DW_OP_plus)), !dbg !17 + %mul = mul nsw i32 %b, %a, !dbg !18 + ret i32 %mul, !dbg !18 +} + +declare void @llvm.dbg.value(metadata, metadata, metadata) + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!3, !4, !5, !6} +!llvm.ident = !{!7} + +!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang version 11.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, nameTableKind: None) +!1 = !DIFile(filename: "debug_value_list.cpp", directory: "/tmp") +!2 = !{} +!3 = !{i32 2, !"CodeView", i32 1} +!4 = !{i32 2, !"Debug Info Version", i32 3} +!5 = !{i32 1, !"wchar_size", i32 2} +!6 = !{i32 7, !"PIC Level", i32 2} +!7 = !{!"clang version 11.0.0"} +!8 = distinct !DISubprogram(name: "foo", linkageName: "?foo@@YAHHH@Z", scope: !9, file: !9, line: 1, type: !10, scopeLine: 1, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !13) +!9 = !DIFile(filename: ".\\debug_value_list.cpp", directory: "/tmp") +!10 = !DISubroutineType(types: !11) +!11 = !{!12, !12, !12} +!12 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!13 = !{!14, !15, !16} +!14 = !DILocalVariable(name: "b", arg: 2, scope: !8, file: !9, line: 1, type: !12) +!15 = !DILocalVariable(name: "a", arg: 1, scope: !8, file: !9, line: 1, type: !12) +!16 = !DILocalVariable(name: "c", scope: !8, file: !9, line: 2, type: !12) +!17 = !DILocation(line: 0, scope: !8) +!18 = !DILocation(line: 3, scope: !8) diff --git a/llvm/test/DebugInfo/MIR/X86/dvl-livedebugvalues-clobber.mir b/llvm/test/DebugInfo/MIR/X86/dvl-livedebugvalues-clobber.mir new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/MIR/X86/dvl-livedebugvalues-clobber.mir @@ -0,0 +1,106 @@ +# RUN: llc -mtriple=x86_64-- -run-pass livedebugvalues -o - %s | FileCheck %s --implicit-check-not=DBG_VALUE_LIST +# +# Test that even after a move, clobbering a register terminates a DBG_VALUE_LIST. +# Check the same for DBG_VALUE $noreg. +# +# CHECK: ![[VAR:[0-9]+]] = !DILocalVariable(name: "c" +# +# CHECK-LABEL: bb.0.entry: +# CHECK: DBG_VALUE_LIST ![[VAR]], !DIExpression(DW_OP_LLVM_arg, 0, +# CHECK-SAME: DW_OP_LLVM_arg, 1, DW_OP_plus), $rdi, $rsi +# CHECK: $rbx = COPY killed $rdi +# CHECK-NEXT: DBG_VALUE_LIST ![[VAR]], !DIExpression(DW_OP_LLVM_arg, 0, +# CHECK-SAME: DW_OP_LLVM_arg, 1, DW_OP_plus), $rbx, $rsi +# CHECK-LABEL: bb.1: +# CHECK: DBG_VALUE_LIST ![[VAR]], !DIExpression(DW_OP_LLVM_arg, 0, +# CHECK-SAME: DW_OP_LLVM_arg, 1, DW_OP_plus), $rbx, $rsi +# CHECK: $rsi = MOV64ri 0 +# CHECK-LABEL: bb.2: +# no live-in! +# CHECK: $rsi = MOV64ri 0 +# CHECK-NEXT: DBG_VALUE_LIST ![[VAR]], !DIExpression(DW_OP_LLVM_arg, 0, +# CHECK-SAME: DW_OP_LLVM_arg, 1, DW_OP_plus), $rbx, $rsi +# CHECK-LABEL: bb.3: +# CHECK: DBG_VALUE_LIST ![[VAR]], !DIExpression(DW_OP_LLVM_arg, 0, +# CHECK-SAME: DW_OP_LLVM_arg, 1, DW_OP_plus), $rbx, $rsi +# live-in to bb.3, then explicitly undef'd, should be no further locations +# propagated. +# CHECK-LABEL: bb.4: +--- | + ; ModuleID = 'test.cpp' + source_filename = "test.cpp" + target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" + target triple = "x86_64-unknown-linux-gnu" + + ; Function Attrs: norecurse nounwind readnone uwtable + define dso_local i32 @_Z3fooii(i32 %a, i32 %b) local_unnamed_addr !dbg !7 { + entry: + ret i32 0, !dbg !17 + } + + ; Function Attrs: nounwind readnone speculatable willreturn + declare void @llvm.dbg.value(metadata, metadata, metadata) + + !llvm.dbg.cu = !{!0} + !llvm.module.flags = !{!3, !4, !5} + !llvm.ident = !{!6} + + !0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang version 11.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, splitDebugInlining: false, nameTableKind: None) + !1 = !DIFile(filename: "test.cpp", directory: "/") + !2 = !{} + !3 = !{i32 7, !"Dwarf Version", i32 4} + !4 = !{i32 2, !"Debug Info Version", i32 3} + !5 = !{i32 1, !"wchar_size", i32 4} + !6 = !{!"clang version 11.0.0"} + !7 = distinct !DISubprogram(name: "foo", linkageName: "_Z3fooii", scope: !1, file: !1, line: 2, type: !8, scopeLine: 2, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !11) + !8 = !DISubroutineType(types: !9) + !9 = !{!10, !10, !10} + !10 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) + !11 = !{!12, !13, !14} + !12 = !DILocalVariable(name: "a", arg: 1, scope: !7, file: !1, line: 2, type: !10) + !13 = !DILocalVariable(name: "b", arg: 2, scope: !7, file: !1, line: 2, type: !10) + !14 = !DILocalVariable(name: "c", scope: !7, file: !1, line: 3, type: !10) + !15 = !DILocation(line: 0, scope: !7) + !16 = !DILocation(line: 4, column: 12, scope: !7) + !17 = !DILocation(line: 4, column: 3, scope: !7) + +... +--- +name: _Z3fooii +fixedStack: + - { id: 0, type: spill-slot, offset: -32, size: 8, alignment: 16, stack-id: default, + callee-saved-register: '$rbx', callee-saved-restored: true } + - { id: 1, type: spill-slot, offset: -16, size: 8, alignment: 16, stack-id: default, + callee-saved-register: '$rbp', callee-saved-restored: true } +body: | + bb.0.entry: + liveins: $rdi, $rsi + + DBG_VALUE_LIST !14, !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_arg, 1, DW_OP_plus), $rdi, $rsi, debug-location !15 + $rbx = COPY killed $rdi + $rdi = MOV64ri 0 + JMP_1 %bb.1 + + bb.1: + liveins: $rbx, $rsi + $rsi = MOV64ri 0 + JMP_1 %bb.2 + + bb.2: + liveins: $rbx, $rsi + + $rsi = MOV64ri 0 + DBG_VALUE_LIST !14, !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_arg, 1, DW_OP_plus), $rbx, $rsi, debug-location !15 + JMP_1 %bb.3 + + bb.3: + liveins: $rbx, $rsi + DBG_VALUE $noreg, $noreg, !14, !DIExpression(), debug-location !15 + JMP_1 %bb.4 + + bb.4: + liveins: $rbx, $rsi + RETQ $rbx, debug-location !17 + +... + diff --git a/llvm/test/DebugInfo/MIR/X86/dvl-livedebugvalues-join.mir b/llvm/test/DebugInfo/MIR/X86/dvl-livedebugvalues-join.mir new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/MIR/X86/dvl-livedebugvalues-join.mir @@ -0,0 +1,160 @@ +# RUN: llc -mtriple=x86_64-- -run-pass livedebugvalues -o - %s | FileCheck %s --implicit-check-not=DBG_VALUE_LIST +# +# Test a series of joins, where: +# * The locations agree, although registers have changed, +# * A register down one of the predecessors has been def'd, +# * The register operands to DBG_VALUE_LIST have been swapped, +# * A spurious additional operand has been added to one path, +# * The expressions are not the same (plus -> minus). +# +# Each join block below checks for one DBG_VALUE_LIST: either we re-state the var +# location in a block for the next test, or it's created as a live-in and we +# use that for the next test. Two DBG_VALUE_LISTs in a block would represent +# a live-in that we didn't expect, and a test failure. +# +# Each conditional block should have at least one, possibly two. +# +# CHECK: ![[VAR:[0-9]+]] = !DILocalVariable(name: "c" +# +# CHECK-LABEL: bb.0.entry: +# CHECK: DBG_VALUE_LIST ![[VAR]], +# CHECK-LABEL: bb.1: +# CHECK: DBG_VALUE_LIST ![[VAR]], +# CHECK: DBG_VALUE_LIST ![[VAR]], +# CHECK-LABEL: bb.2: +# CHECK: DBG_VALUE_LIST ![[VAR]], !DIExpression(DW_OP_LLVM_arg, 0, +# CHECK-SAME: DW_OP_LLVM_arg, 1, DW_OP_plus), $rdi, $rsi +# CHECK-LABEL: bb.3: +# CHECK: DBG_VALUE_LIST ![[VAR]], +# CHECK-LABEL: bb.4: +# CHECK: DBG_VALUE_LIST ![[VAR]], +# CHECK-SAME: $rdi, $rsi +# CHECK-LABEL: bb.5: +# CHECK: DBG_VALUE_LIST ![[VAR]], +# CHECK: DBG_VALUE_LIST ![[VAR]], +# CHECK-SAME: $rsi, $rdi +# CHECK-LABEL: bb.6: +# CHECK: DBG_VALUE_LIST ![[VAR]], +# CHECK-LABEL: bb.7: +# CHECK: DBG_VALUE_LIST ![[VAR]], +# CHECK: DBG_VALUE_LIST ![[VAR]], +# CHECK-SAME: $rdi, $rsi, $rax +# CHECK-LABEL: bb.8: +# CHECK: DBG_VALUE_LIST ![[VAR]], +# CHECK-LABEL: bb.9: +# CHECK: DBG_VALUE_LIST ![[VAR]], +# CHECK: DBG_VALUE_LIST ![[VAR]], +# CHECK-SAME: DW_OP_minus +# CHECK-LABEL: bb.10: +--- | + ; ModuleID = 'test.cpp' + source_filename = "test.cpp" + target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" + target triple = "x86_64-unknown-linux-gnu" + + ; Function Attrs: norecurse nounwind readnone uwtable + define dso_local i32 @_Z3fooii(i32 %a, i32 %b) local_unnamed_addr !dbg !7 { + entry: + ret i32 0, !dbg !17 + } + + ; Function Attrs: nounwind readnone speculatable willreturn + declare void @llvm.dbg.value(metadata, metadata, metadata) + + !llvm.dbg.cu = !{!0} + !llvm.module.flags = !{!3, !4, !5} + !llvm.ident = !{!6} + + !0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang version 11.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, splitDebugInlining: false, nameTableKind: None) + !1 = !DIFile(filename: "test.cpp", directory: "/") + !2 = !{} + !3 = !{i32 7, !"Dwarf Version", i32 4} + !4 = !{i32 2, !"Debug Info Version", i32 3} + !5 = !{i32 1, !"wchar_size", i32 4} + !6 = !{!"clang version 11.0.0"} + !7 = distinct !DISubprogram(name: "foo", linkageName: "_Z3fooii", scope: !1, file: !1, line: 2, type: !8, scopeLine: 2, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !11) + !8 = !DISubroutineType(types: !9) + !9 = !{!10, !10, !10} + !10 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) + !11 = !{!12, !13, !14} + !12 = !DILocalVariable(name: "a", arg: 1, scope: !7, file: !1, line: 2, type: !10) + !13 = !DILocalVariable(name: "b", arg: 2, scope: !7, file: !1, line: 2, type: !10) + !14 = !DILocalVariable(name: "c", scope: !7, file: !1, line: 3, type: !10) + !15 = !DILocation(line: 0, scope: !7) + !16 = !DILocation(line: 4, column: 12, scope: !7) + !17 = !DILocation(line: 4, column: 3, scope: !7) + +... +--- +name: _Z3fooii +body: | + bb.0.entry: + liveins: $rdi, $rsi + + DBG_VALUE_LIST !14, !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_arg, 1, DW_OP_plus), $rdi, $rsi, debug-location !15 + CMP64ri8 $rdi, 0, implicit-def $eflags + JCC_1 %bb.2, 4, implicit $eflags + + bb.1: + liveins: $rdi, $rsi + $rdi = MOV64ri 0 + $rsi = MOV64ri 0 + DBG_VALUE_LIST !14, !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_arg, 1, DW_OP_plus), $rdi, $rsi, debug-location !15 + + bb.2: + liveins: $rdi, $rsi + ; Should be a live-in loc here, + CMP64ri8 $rdi, 0, implicit-def $eflags + JCC_1 %bb.4, 4, implicit $eflags + + bb.3: + liveins: $rdi, $rsi + $rsi = MOV64ri 0 + + bb.4: + liveins: $rdi, $rsi + ; Should _not_ be a live-in loc here. + DBG_VALUE_LIST !14, !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_arg, 1, DW_OP_plus), $rdi, $rsi, debug-location !15 + CMP64ri8 $rdi, 0, implicit-def $eflags + JCC_1 %bb.6, 4, implicit $eflags + + bb.5: + liveins: $rdi, $rsi + ; Flip some args, + DBG_VALUE_LIST !14, !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_arg, 1, DW_OP_plus), $rsi, $rdi, debug-location !15 + + bb.6: + liveins: $rdi, $rsi + ; Should _not_ be a live-in loc here. + $rax = MOV64ri 0 + DBG_VALUE_LIST !14, !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_arg, 1, DW_OP_plus), $rdi, $rsi, debug-location !15 + CMP64ri8 $rdi, 0, implicit-def $eflags + JCC_1 %bb.8, 4, implicit $eflags + + bb.7: + liveins: $rdi, $rsi + ; Add an extra, spurious, unused argument + $rax = MOV64ri 1 + DBG_VALUE_LIST !14, !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_arg, 1, DW_OP_plus), $rdi, $rsi, $rax, debug-location !15 + + bb.8: + liveins: $rdi, $rsi + ; Should _not_ be a live-in loc here. + $rax = MOV64ri 0 + DBG_VALUE_LIST !14, !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_arg, 1, DW_OP_plus), $rdi, $rsi, debug-location !15 + CMP64ri8 $rdi, 0, implicit-def $eflags + JCC_1 %bb.10, 4, implicit $eflags + + bb.9: + liveins: $rdi, $rsi + ; Replace add with sub in the expr + $rax = MOV64ri 1 + DBG_VALUE_LIST !14, !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_arg, 1, DW_OP_minus), $rdi, $rsi, debug-location !15 + + bb.10: + liveins: $rdi, $rsi + ; Should _not_ be a live-in loc here. + RETQ + +... + diff --git a/llvm/test/DebugInfo/MIR/X86/dvl-livedebugvalues-movements.mir b/llvm/test/DebugInfo/MIR/X86/dvl-livedebugvalues-movements.mir new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/MIR/X86/dvl-livedebugvalues-movements.mir @@ -0,0 +1,90 @@ +# RUN: llc -mtriple=x86_64-- -run-pass livedebugvalues -o - %s | FileCheck %s --implicit-check-not=DBG_VALUE_LIST +# +# The MIR below moves values from argument registers to callee saved registers, +# moves that are followed by DBG_VALUEs and which should also result in +# DBG_VALUE_LISTs moving their operands. +# +# CHECK: ![[VAR:[0-9]+]] = !DILocalVariable(name: "c" +# +# CHECK-LABEL: bb.0.entry: +# CHECK: DBG_VALUE_LIST ![[VAR]], !DIExpression(DW_OP_LLVM_arg, 0, +# CHECK-SAME: DW_OP_LLVM_arg, 1, DW_OP_plus), $rdi, $rsi +# CHECK: $rbx = COPY killed $rdi +# CHECK-NEXT: DBG_VALUE_LIST ![[VAR]], !DIExpression(DW_OP_LLVM_arg, 0, +# CHECK-SAME: DW_OP_LLVM_arg, 1, DW_OP_plus), $rbx, $rsi +# CHECK-LABEL: bb.1: +# CHECK: DBG_VALUE_LIST ![[VAR]], !DIExpression(DW_OP_LLVM_arg, 0, +# CHECK-SAME: DW_OP_LLVM_arg, 1, DW_OP_plus), $rbx, $rsi +# CHECK: $rbp = COPY killed $rsi +# CHECK-NEXT: DBG_VALUE_LIST ![[VAR]], !DIExpression(DW_OP_LLVM_arg, 0, +# CHECK-SAME: DW_OP_LLVM_arg, 1, DW_OP_plus), $rbx, $rbp +# CHECK-LABEL: bb.2: +# CHECK: DBG_VALUE_LIST ![[VAR]], !DIExpression(DW_OP_LLVM_arg, 0, +# CHECK-SAME: DW_OP_LLVM_arg, 1, DW_OP_plus), $rbx, $rbp +--- | + ; ModuleID = 'test.cpp' + source_filename = "test.cpp" + target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" + target triple = "x86_64-unknown-linux-gnu" + + ; Function Attrs: norecurse nounwind readnone uwtable + define dso_local i32 @_Z3fooii(i32 %a, i32 %b) local_unnamed_addr !dbg !7 { + entry: + ret i32 0, !dbg !17 + } + + ; Function Attrs: nounwind readnone speculatable willreturn + declare void @llvm.dbg.value(metadata, metadata, metadata) + + !llvm.dbg.cu = !{!0} + !llvm.module.flags = !{!3, !4, !5} + !llvm.ident = !{!6} + + !0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang version 11.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, splitDebugInlining: false, nameTableKind: None) + !1 = !DIFile(filename: "test.cpp", directory: "/") + !2 = !{} + !3 = !{i32 7, !"Dwarf Version", i32 4} + !4 = !{i32 2, !"Debug Info Version", i32 3} + !5 = !{i32 1, !"wchar_size", i32 4} + !6 = !{!"clang version 11.0.0"} + !7 = distinct !DISubprogram(name: "foo", linkageName: "_Z3fooii", scope: !1, file: !1, line: 2, type: !8, scopeLine: 2, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !11) + !8 = !DISubroutineType(types: !9) + !9 = !{!10, !10, !10} + !10 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) + !11 = !{!12, !13, !14} + !12 = !DILocalVariable(name: "a", arg: 1, scope: !7, file: !1, line: 2, type: !10) + !13 = !DILocalVariable(name: "b", arg: 2, scope: !7, file: !1, line: 2, type: !10) + !14 = !DILocalVariable(name: "c", scope: !7, file: !1, line: 3, type: !10) + !15 = !DILocation(line: 0, scope: !7) + !16 = !DILocation(line: 4, column: 12, scope: !7) + !17 = !DILocation(line: 4, column: 3, scope: !7) + +... +--- +name: _Z3fooii +fixedStack: + - { id: 0, type: spill-slot, offset: -32, size: 8, alignment: 16, stack-id: default, + callee-saved-register: '$rbx', callee-saved-restored: true } + - { id: 1, type: spill-slot, offset: -16, size: 8, alignment: 16, stack-id: default, + callee-saved-register: '$rbp', callee-saved-restored: true } +body: | + bb.0.entry: + liveins: $rdi, $rsi + + DBG_VALUE_LIST !14, !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_arg, 1, DW_OP_plus), $rdi, $rsi, debug-location !15 + $rbx = COPY killed $rdi + $rdi = MOV64ri 0 + JMP_1 %bb.1 + + bb.1: + liveins: $rbx, $rsi + $rbp = COPY killed $rsi + $rsi = MOV64ri 0 + JMP_1 %bb.2 + + bb.2: + liveins: $rbx, $rbp + RETQ $rbp, debug-location !17 + +... + diff --git a/llvm/test/DebugInfo/MIR/X86/dvl-livedebugvalues-spillrestore.mir b/llvm/test/DebugInfo/MIR/X86/dvl-livedebugvalues-spillrestore.mir new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/MIR/X86/dvl-livedebugvalues-spillrestore.mir @@ -0,0 +1,77 @@ +# RUN: llc -mtriple=x86_64-- -run-pass livedebugvalues -o - %s | FileCheck %s --implicit-check-not=DBG_VALUE_LIST +# +# A DBG_VALUE_LIST that has a component spilt and restored should had its +# expression and operands updated to refer to the stack for that period, and +# then return to normal once the value is restored. +# +# CHECK: ![[VAR:[0-9]+]] = !DILocalVariable(name: "c" +# +# CHECK-LABEL: bb.0.entry: +# CHECK: DBG_VALUE_LIST ![[VAR]], +# CHECK-SAME: !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_arg, 1, DW_OP_plus), +# CHECK-SAME: $rdi, $rsi, +# CHECK: MOV64mr $rsp, 1, $noreg, -16, $noreg, $rdi +# CHECK-NEXT: DBG_VALUE_LIST ![[VAR]], +# CHECK-SAME: !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_constu, 8, DW_OP_minus, DW_OP_deref, DW_OP_LLVM_arg, 1, DW_OP_plus), +# CHECK-SAME: $rsp, $rsi, +# CHECK: $rdi = MOV64rm $rsp, 1, $noreg, -16, +# CHECK-NEXT: DBG_VALUE_LIST ![[VAR]], +# CHECK-SAME: !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_arg, 1, DW_OP_plus), +# CHECK-SAME: $rdi, $rsi, + +--- | + ; ModuleID = 'test.cpp' + source_filename = "test.cpp" + target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" + target triple = "x86_64-unknown-linux-gnu" + + ; Function Attrs: norecurse nounwind readnone uwtable + define dso_local i32 @_Z3fooii(i32 %a, i32 %b) local_unnamed_addr !dbg !7 { + entry: + ret i32 0, !dbg !17 + } + + ; Function Attrs: nounwind readnone speculatable willreturn + declare void @llvm.dbg.value(metadata, metadata, metadata) + + !llvm.dbg.cu = !{!0} + !llvm.module.flags = !{!3, !4, !5} + !llvm.ident = !{!6} + + !0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang version 11.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, splitDebugInlining: false, nameTableKind: None) + !1 = !DIFile(filename: "test.cpp", directory: "/") + !2 = !{} + !3 = !{i32 7, !"Dwarf Version", i32 4} + !4 = !{i32 2, !"Debug Info Version", i32 3} + !5 = !{i32 1, !"wchar_size", i32 4} + !6 = !{!"clang version 11.0.0"} + !7 = distinct !DISubprogram(name: "foo", linkageName: "_Z3fooii", scope: !1, file: !1, line: 2, type: !8, scopeLine: 2, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !11) + !8 = !DISubroutineType(types: !9) + !9 = !{!10, !10, !10} + !10 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) + !11 = !{!12, !13, !14} + !12 = !DILocalVariable(name: "a", arg: 1, scope: !7, file: !1, line: 2, type: !10) + !13 = !DILocalVariable(name: "b", arg: 2, scope: !7, file: !1, line: 2, type: !10) + !14 = !DILocalVariable(name: "c", scope: !7, file: !1, line: 3, type: !10) + !15 = !DILocation(line: 0, scope: !7) + !16 = !DILocation(line: 4, column: 12, scope: !7) + !17 = !DILocation(line: 4, column: 3, scope: !7) + +... +--- +name: _Z3fooii +stack: + - { id: 0, offset: -16, size: 8, alignment: 8, type: spill-slot } +body: | + bb.0.entry: + liveins: $rdi, $rsi + + $rsp = frame-setup SUB64ri8 $rsp, 24, implicit-def dead $eflags + DBG_VALUE_LIST !14, !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_arg, 1, DW_OP_plus), $rdi, $rsi, debug-location !15 + MOV64mr $rsp, 1, _, -16, _, $rdi, debug-location !15 :: (store 8 into %stack.0) + $rax = COPY killed $rdi + $rdi = MOV64ri 0 + $rdi = MOV64rm $rsp, 1, $noreg, -16, $noreg, debug-location !15 :: (load 8 from %stack.0) + RETQ +... + diff --git a/llvm/test/DebugInfo/MIR/X86/dvl-livedebugvars-movements.mir b/llvm/test/DebugInfo/MIR/X86/dvl-livedebugvars-movements.mir new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/MIR/X86/dvl-livedebugvars-movements.mir @@ -0,0 +1,109 @@ +# RUN: llc -start-after=phi-node-elimination -stop-after=virtregrewriter %s -mtriple=x86_64-unknown-unknown -o - | FileCheck %s +# +# Test that when a livedebugvars interval is split, DBG_VALUE_LISTs are created +# with the correct operands and exprs. Force values to be moved around between +# registers and stack through inline asm blocks that clobber things. +# +# CHECK-LABEL: bb.0.entry: +# CHECK: $rbx = COPY $rsi +# CHECK-NEXT: MOV64mr %stack.0, 1, $noreg, 0, $noreg, $rdi +# CHECK-NEXT: DBG_VALUE_LIST !14, !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_deref, DW_OP_LLVM_arg, 1, DW_OP_plus), %stack.0, $rbx, +# CHECK-NEXT: INLINEASM +# CHECK-NEXT: $rax = MOV64rm %stack.0, +# CHECK-NEXT: DBG_VALUE_LIST !14, !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_arg, 1, DW_OP_plus), $rax, $rbx, +# CHECK-NEXT: CALL64pcrel32 @foo +# CHECK-NEXT: DBG_VALUE_LIST !14, !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_deref, DW_OP_LLVM_arg, 1, DW_OP_plus), %stack.0, $rbx, +# CHECK-NEXT: $rcx = COPY killed renamable $rbx +# CHECK-NEXT: DBG_VALUE_LIST !14, !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_deref, DW_OP_LLVM_arg, 1, DW_OP_plus), %stack.0, $rcx, +# CHECK-NEXT: INLINEASM +# CHECK-NEXT: $rax = MOV64rm %stack.0 +# CHECK-NEXT: DBG_VALUE_LIST !14, !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_arg, 1, DW_OP_plus), $rax, $rcx, + +--- | + ; ModuleID = 'tmp.ll' + source_filename = "tmp.ll" + target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" + + %struct.a = type { i32 } + + ; Function Attrs: nounwind ssp + define i32 @bar(%struct.a* nocapture %b, i32 %shoes) !dbg !4 { + entry: + tail call void @llvm.dbg.value(metadata i32 %shoes, metadata !9, metadata !DIExpression()), !dbg !16 + %tmp1 = getelementptr inbounds %struct.a, %struct.a* %b, i64 0, i32 0, !dbg !17 + br label %bb3 + + bb1: ; preds = %bb2 + tail call void @llvm.dbg.value(metadata i32 %shoes, metadata !9, metadata !DIExpression()), !dbg !16 + %add = add nsw i32 %tmp2, 1, !dbg !18 + br label %exit + + bb2: ; preds = %bb3 + tail call void @llvm.dbg.value(metadata i32 %tmp2, metadata !14, metadata !DIExpression()), !dbg !17 + %call = tail call i32 (...) @foo(i32 %tmp2), !dbg !19 + br label %bb1 + + bb3: ; preds = %entry + %tmp2 = load i32, i32* %tmp1, align 4, !dbg !17 + br label %bb2 + + exit: ; preds = %bb1 + ret i32 %shoes, !dbg !18 + } + + declare i32 @foo(...) + + ; Function Attrs: nounwind readnone speculatable willreturn + declare void @llvm.dbg.value(metadata, metadata, metadata) + + !llvm.dbg.cu = !{!0} + !llvm.module.flags = !{!3} + + !0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 2.9 (trunk 122997)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, retainedTypes: !2) + !1 = !DIFile(filename: "bar.c", directory: "/private/tmp") + !2 = !{} + !3 = !{i32 1, !"Debug Info Version", i32 3} + !4 = distinct !DISubprogram(name: "bar", scope: !1, file: !1, line: 5, type: !5, virtualIndex: 6, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !8) + !5 = !DISubroutineType(types: !6) + !6 = !{!7} + !7 = !DIBasicType(name: "int", size: 32, align: 32, encoding: DW_ATE_signed) + !8 = !{!9, !14} + !9 = !DILocalVariable(name: "b", arg: 1, scope: !4, file: !1, line: 5, type: !10) + !10 = !DIDerivedType(tag: DW_TAG_pointer_type, scope: !0, baseType: !11, size: 64, align: 64) + !11 = !DICompositeType(tag: DW_TAG_structure_type, name: "a", scope: !0, file: !1, line: 1, size: 32, align: 32, elements: !12) + !12 = !{!13} + !13 = !DIDerivedType(tag: DW_TAG_member, name: "c", scope: !1, file: !1, line: 2, baseType: !7, size: 32, align: 32) + !14 = !DILocalVariable(name: "x", scope: !15, file: !1, line: 6, type: !7) + !15 = distinct !DILexicalBlock(scope: !4, file: !1, line: 5, column: 22) + !16 = !DILocation(line: 5, column: 19, scope: !4) + !17 = !DILocation(line: 6, column: 14, scope: !15) + !18 = !DILocation(line: 8, column: 2, scope: !15) + !19 = !DILocation(line: 7, column: 2, scope: !15) + +... +--- +name: bar +tracksRegLiveness: true +body: | + bb.0.entry: + liveins: $rdi, $rsi + + %4:gr64= COPY $rsi + %2:gr64 = COPY $rdi + DBG_VALUE_LIST !14, !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_arg, 1, DW_OP_plus), %2, %4, debug-location !17 + %3:gr64 = COPY killed %2 + %5:gr64 = COPY killed %4 + + ; Force allocation into $rax and $rbx + INLINEASM &"", 1, 12, implicit-def dead $rcx, 12, implicit-def dead $rdx, 12, implicit-def dead $rsi, 12, implicit-def dead $rdi, 12, implicit-def $rbp, 12, implicit-def dead $r8, 12, implicit-def dead $r9, 12, implicit-def dead $r10, 12, implicit-def dead $r11, 12, implicit-def dead $r12, 12, implicit-def dead $r13, 12, implicit-def dead $r14, 12, implicit-def dead $r15, 12, !18, debug-location !17 + + ; Force a use of these two registers. + CALL64pcrel32 @foo, csr_64, implicit $rsp, implicit $ssp, implicit %3, implicit %5 + + ; Now make the register allocator move them to rcx and rdx! + INLINEASM &"", 1, 12, implicit-def dead $rax, 12, implicit-def dead $rbx, 12, implicit-def dead $rsi, 12, implicit-def dead $rdi, 12, implicit-def $rbp, 12, implicit-def dead $r8, 12, implicit-def dead $r9, 12, implicit-def dead $r10, 12, implicit-def dead $r11, 12, implicit-def dead $r12, 12, implicit-def dead $r13, 12, implicit-def dead $r14, 12, implicit-def dead $r15, 12, !18, debug-location !17 + + CALL64pcrel32 @foo, csr_64, implicit $rsp, implicit $ssp, implicit %3, implicit %5 + + RETQ +... diff --git a/llvm/test/DebugInfo/MIR/X86/dvl-livedebugvars-stackptr.mir b/llvm/test/DebugInfo/MIR/X86/dvl-livedebugvars-stackptr.mir new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/MIR/X86/dvl-livedebugvars-stackptr.mir @@ -0,0 +1,113 @@ +# RUN: llc -start-after=phi-node-elimination -stop-after=virtregrewriter %s -mtriple=x86_64-unknown-unknown -o - | FileCheck %s +# +# This is a copy of the adjacent "-movements.mir" file, but where one of the +# operands to DBG_VALUE_LIST is a stack _pointer_ rather than a spilt value. +# The expression should grow no additional derefs for the stack pointer. +# +# CHECK-LABEL: bb.0.entry: +# CHECK: $rbx = COPY $rsi +# CHECK-NEXT: MOV64mr %stack.1, 1, $noreg, 0, $noreg, $rdi +# CHECK-NEXT: DBG_VALUE_LIST !14, !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_deref, DW_OP_LLVM_arg, 1, DW_OP_plus), %stack.1, %stack.0.local1 +# CHECK-NEXT: INLINEASM +# CHECK-NEXT: $rax = MOV64rm %stack.1, +# CHECK-NEXT: DBG_VALUE_LIST !14, !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_arg, 1, DW_OP_plus), $rax, %stack.0.local1, +# CHECK-NEXT: CALL64pcrel32 @foo +# CHECK-NEXT: DBG_VALUE_LIST !14, !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_deref, DW_OP_LLVM_arg, 1, DW_OP_plus), %stack.1, %stack.0.local1, +# CHECK-NEXT: $rcx = COPY killed renamable $rbx +# CHECK-NEXT: INLINEASM +# CHECK-NEXT: $rax = MOV64rm %stack.1 +# CHECK-NEXT: DBG_VALUE_LIST !14, !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_arg, 1, DW_OP_plus), $rax, %stack.0.local1, + +--- | + ; ModuleID = 'tmp.ll' + source_filename = "tmp.ll" + target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" + + %struct.a = type { i32 } + + ; Function Attrs: nounwind ssp + define i32 @bar(%struct.a* nocapture %b, i32 %shoes) !dbg !4 { + entry: + %local1 = alloca i64 + tail call void @llvm.dbg.value(metadata i32 %shoes, metadata !9, metadata !DIExpression()), !dbg !16 + %tmp1 = getelementptr inbounds %struct.a, %struct.a* %b, i64 0, i32 0, !dbg !17 + br label %bb3 + + bb1: ; preds = %bb2 + tail call void @llvm.dbg.value(metadata i32 %shoes, metadata !9, metadata !DIExpression()), !dbg !16 + %add = add nsw i32 %tmp2, 1, !dbg !18 + br label %exit + + bb2: ; preds = %bb3 + tail call void @llvm.dbg.value(metadata i32 %tmp2, metadata !14, metadata !DIExpression()), !dbg !17 + %call = tail call i32 (...) @foo(i32 %tmp2), !dbg !19 + br label %bb1 + + bb3: ; preds = %entry + %tmp2 = load i32, i32* %tmp1, align 4, !dbg !17 + br label %bb2 + + exit: ; preds = %bb1 + ret i32 %shoes, !dbg !18 + } + + declare i32 @foo(...) + + ; Function Attrs: nounwind readnone speculatable willreturn + declare void @llvm.dbg.value(metadata, metadata, metadata) + + !llvm.dbg.cu = !{!0} + !llvm.module.flags = !{!3} + + !0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 2.9 (trunk 122997)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, retainedTypes: !2) + !1 = !DIFile(filename: "bar.c", directory: "/private/tmp") + !2 = !{} + !3 = !{i32 1, !"Debug Info Version", i32 3} + !4 = distinct !DISubprogram(name: "bar", scope: !1, file: !1, line: 5, type: !5, virtualIndex: 6, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !8) + !5 = !DISubroutineType(types: !6) + !6 = !{!7} + !7 = !DIBasicType(name: "int", size: 32, align: 32, encoding: DW_ATE_signed) + !8 = !{!9, !14} + !9 = !DILocalVariable(name: "b", arg: 1, scope: !4, file: !1, line: 5, type: !10) + !10 = !DIDerivedType(tag: DW_TAG_pointer_type, scope: !0, baseType: !11, size: 64, align: 64) + !11 = !DICompositeType(tag: DW_TAG_structure_type, name: "a", scope: !0, file: !1, line: 1, size: 32, align: 32, elements: !12) + !12 = !{!13} + !13 = !DIDerivedType(tag: DW_TAG_member, name: "c", scope: !1, file: !1, line: 2, baseType: !7, size: 32, align: 32) + !14 = !DILocalVariable(name: "x", scope: !15, file: !1, line: 6, type: !7) + !15 = distinct !DILexicalBlock(scope: !4, file: !1, line: 5, column: 22) + !16 = !DILocation(line: 5, column: 19, scope: !4) + !17 = !DILocation(line: 6, column: 14, scope: !15) + !18 = !DILocation(line: 8, column: 2, scope: !15) + !19 = !DILocation(line: 7, column: 2, scope: !15) + +... +--- +name: bar +tracksRegLiveness: true +stack: + - { id: 0, name: local1, type: default, offset: -24, size: 8, alignment: 8, + stack-id: default, callee-saved-register: '', callee-saved-restored: true, + debug-info-variable: '', debug-info-expression: '', debug-info-location: '' } +body: | + bb.0.entry: + liveins: $rdi, $rsi + + %4:gr64= COPY $rsi + %2:gr64 = COPY $rdi + DBG_VALUE_LIST !14, !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_arg, 1, DW_OP_plus), %2, %stack.0, debug-location !17 + %3:gr64 = COPY killed %2 + %5:gr64 = COPY killed %4 + + ; Force allocation into $rax and $rbx + INLINEASM &"", 1, 12, implicit-def dead $rcx, 12, implicit-def dead $rdx, 12, implicit-def dead $rsi, 12, implicit-def dead $rdi, 12, implicit-def $rbp, 12, implicit-def dead $r8, 12, implicit-def dead $r9, 12, implicit-def dead $r10, 12, implicit-def dead $r11, 12, implicit-def dead $r12, 12, implicit-def dead $r13, 12, implicit-def dead $r14, 12, implicit-def dead $r15, 12, !18, debug-location !17 + + ; Force a use of these two registers. + CALL64pcrel32 @foo, csr_64, implicit $rsp, implicit $ssp, implicit %3, implicit %5 + + ; Now make the register allocator move them to rcx and rdx! + INLINEASM &"", 1, 12, implicit-def dead $rax, 12, implicit-def dead $rbx, 12, implicit-def dead $rsi, 12, implicit-def dead $rdi, 12, implicit-def $rbp, 12, implicit-def dead $r8, 12, implicit-def dead $r9, 12, implicit-def dead $r10, 12, implicit-def dead $r11, 12, implicit-def dead $r12, 12, implicit-def dead $r13, 12, implicit-def dead $r14, 12, implicit-def dead $r15, 12, !18, debug-location !17 + + CALL64pcrel32 @foo, csr_64, implicit $rsp, implicit $ssp, implicit %3, implicit %5 + + RETQ +... diff --git a/llvm/test/DebugInfo/MIR/X86/live-debug-vars-unused-arg-debugonly.mir b/llvm/test/DebugInfo/MIR/X86/live-debug-vars-unused-arg-debugonly.mir --- a/llvm/test/DebugInfo/MIR/X86/live-debug-vars-unused-arg-debugonly.mir +++ b/llvm/test/DebugInfo/MIR/X86/live-debug-vars-unused-arg-debugonly.mir @@ -153,11 +153,11 @@ # not cover the whole BB. # # CHECKDBG-LABEL: ********** EMITTING LIVE DEBUG VARIABLES ********** -# CHECKDBG-NEXT: !"argc,5" [0B;0e):0 Loc0=$edi -# CHECKDBG-NEXT: [0B;0e):0 %bb.0-160B -# CHECKDBG-NEXT: !"argv,5" [0B;0e):0 Loc0=$rsi -# CHECKDBG-NEXT: [0B;0e):0 %bb.0-160B -# CHECKDBG-NEXT: !"a0,7" [16r;64r):0 Loc0=%2 -# CHECKDBG-NEXT: [16r;64r):0 %bb.0-160B -# CHECKDBG-NEXT: !"a1,8" [32r;80r):0 Loc0=%3 -# CHECKDBG-NEXT: [32r;80r):0 %bb.0-160B +# CHECKDBG-NEXT: !"argc,5" [0B;0e): 0 Loc0=$edi +# CHECKDBG-NEXT: [0B;0e): 0 %bb.0-160B +# CHECKDBG-NEXT: !"argv,5" [0B;0e): 0 Loc0=$rsi +# CHECKDBG-NEXT: [0B;0e): 0 %bb.0-160B +# CHECKDBG-NEXT: !"a0,7" [16r;64r): 0 Loc0=%2 +# CHECKDBG-NEXT: [16r;64r): 0 %bb.0-160B +# CHECKDBG-NEXT: !"a1,8" [32r;80r): 0 Loc0=%3 +# CHECKDBG-NEXT: [32r;80r): 0 %bb.0-160B diff --git a/llvm/test/DebugInfo/X86/arg-dbg-value-list.ll b/llvm/test/DebugInfo/X86/arg-dbg-value-list.ll new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/X86/arg-dbg-value-list.ll @@ -0,0 +1,50 @@ +;; FIXME: We currently don't do any special bookkeeping for unused args used by +;; variadic dbg_values. When/if we support them, the DBG_VALUE_LIST should be +;; updated accordingly. + +; RUN: llc %s -start-after=codegenprepare -stop-before=finalize-isel -o - | FileCheck %s + +;; Check that unused argument values are handled the same way for variadic +;; dbg_values as non-variadics. + +; CHECK: ![[A:[0-9]+]] = !DILocalVariable(name: "a", +; CHECK: ![[B:[0-9]+]] = !DILocalVariable(name: "b", +; CHECK: ![[C:[0-9]+]] = !DILocalVariable(name: "c", + +; CHECK: DBG_VALUE $ecx, $noreg, ![[A]], !DIExpression(), debug-location +; CHECK: DBG_VALUE $edx, $noreg, ![[B]], !DIExpression(), debug-location +; CHECK: DBG_VALUE_LIST ![[C]], !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_arg, 1, DW_OP_plus, DW_OP_stack_value), $noreg, $noreg, debug-location + +target triple = "x86_64-pc-windows-msvc19.16.27034" +define dso_local i32 @"?foo@@YAHHH@Z"(i32 %a, i32 %b) local_unnamed_addr !dbg !8 { +entry: + call void @llvm.dbg.value(metadata i32 %a, metadata !14, metadata !DIExpression()), !dbg !17 + call void @llvm.dbg.value(metadata i32 %b, metadata !15, metadata !DIExpression()), !dbg !17 + call void @llvm.dbg.value(metadata !DIArgList(i32 %a, i32 %b), metadata !16, metadata !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_arg, 1, DW_OP_plus, DW_OP_stack_value)), !dbg !17 + ret i32 0 +} + +declare void @llvm.dbg.value(metadata, metadata, metadata) + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!3, !4, !5, !6} +!llvm.ident = !{!7} + +!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang version 11.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, nameTableKind: None) +!1 = !DIFile(filename: "test.cpp", directory: "/") +!2 = !{} +!3 = !{i32 2, !"CodeView", i32 1} +!4 = !{i32 2, !"Debug Info Version", i32 3} +!5 = !{i32 1, !"wchar_size", i32 2} +!6 = !{i32 7, !"PIC Level", i32 2} +!7 = !{!"clang version 11.0.0"} +!8 = distinct !DISubprogram(name: "foo", linkageName: "?foo@@YAHHH@Z", scope: !9, file: !9, line: 1, type: !10, scopeLine: 1, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !13) +!9 = !DIFile(filename: "test.cpp", directory: "/") +!10 = !DISubroutineType(types: !11) +!11 = !{!12, !12, !12} +!12 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!13 = !{!14, !15, !16} +!14 = !DILocalVariable(name: "a", arg: 1, scope: !8, file: !9, line: 1, type: !12) +!15 = !DILocalVariable(name: "b", arg: 2, scope: !8, file: !9, line: 1, type: !12) +!16 = !DILocalVariable(name: "c", scope: !8, file: !9, line: 2, type: !12) +!17 = !DILocation(line: 0, scope: !8) diff --git a/llvm/test/DebugInfo/X86/dbg-val-list-dangling.ll b/llvm/test/DebugInfo/X86/dbg-val-list-dangling.ll new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/X86/dbg-val-list-dangling.ll @@ -0,0 +1,68 @@ +;; At the moment we emit an undef as soon as we encounter "dangling" variadic +;; dbg_value nodes. This does not reduce correctness but does reduce coverage. +;; We should make variadic dbg_values work in the same way as their +;; non-variadic counterparts here. +;; FIXME: When dangling nodes for a variadic dbg_value are found, we should be +;; able to recover the value in some cases. + +; RUN: llc %s -start-after=codegenprepare -stop-before=finalize-isel -o - | FileCheck %s + +;; Check that dangling debug info in the SelectionDAG build phase is handled +;; in the same way for variadic dbg_value ndoes as non-variadics. + +;; Generated from the following source with -g -O2. Second dbg.value modified +;; to be variadic by hand: +;; void a(char*); +;; void b() { +;; char *c = "abc"; +;; char *d = "abc"; +;; a("abc"); +;; } + +; CHECK: ![[C:[0-9]+]] = !DILocalVariable(name: "c", +; CHECK: ![[D:[0-9]+]] = !DILocalVariable(name: "d", + +; CHECK-DAG: DBG_VALUE %[[VREG:[0-9]]], $noreg, ![[C]], !DIExpression(), debug-location +; CHECK-DAG: DBG_VALUE_LIST ![[D]], !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_stack_value), $noreg, debug-location + +target triple = "x86_64-unknown-linux-gnu" + +@.str = private unnamed_addr constant [4 x i8] c"abc\00", align 1 + +define dso_local void @b() local_unnamed_addr !dbg !7 { +entry: + call void @llvm.dbg.value(metadata i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str, i64 0, i64 0), metadata !11, metadata !DIExpression()), !dbg !15 + call void @llvm.dbg.value(metadata !DIArgList(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str, i64 0, i64 0)), metadata !14, metadata !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_stack_value)), !dbg !15 + tail call void @a(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str, i64 0, i64 0)) #3, !dbg !16 + ret void, !dbg !17 +} + +declare !dbg !18 dso_local void @a(i8*) local_unnamed_addr +declare void @llvm.dbg.value(metadata, metadata, metadata) + + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!3, !4, !5} +!llvm.ident = !{!6} + +!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 11.0.0)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, splitDebugInlining: false, nameTableKind: None) +!1 = !DIFile(filename: "test.c", directory: "/") +!2 = !{} +!3 = !{i32 7, !"Dwarf Version", i32 4} +!4 = !{i32 2, !"Debug Info Version", i32 3} +!5 = !{i32 1, !"wchar_size", i32 4} +!6 = !{!"clang version 11.0.0"} +!7 = distinct !DISubprogram(name: "b", scope: !1, file: !1, line: 2, type: !8, scopeLine: 2, flags: DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !10) +!8 = !DISubroutineType(types: !9) +!9 = !{null} +!10 = !{!11, !14} +!11 = !DILocalVariable(name: "c", scope: !7, file: !1, line: 3, type: !12) +!12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) +!13 = !DIBasicType(name: "char", size: 8, encoding: DW_ATE_signed_char) +!14 = !DILocalVariable(name: "d", scope: !7, file: !1, line: 4, type: !12) +!15 = !DILocation(line: 0, scope: !7) +!16 = !DILocation(line: 5, column: 3, scope: !7) +!17 = !DILocation(line: 6, column: 1, scope: !7) +!18 = !DISubprogram(name: "a", scope: !1, file: !1, line: 1, type: !19, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !2) +!19 = !DISubroutineType(types: !20) +!20 = !{null, !12} diff --git a/llvm/test/DebugInfo/X86/dbg-val-list-undef.ll b/llvm/test/DebugInfo/X86/dbg-val-list-undef.ll new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/X86/dbg-val-list-undef.ll @@ -0,0 +1,41 @@ +; RUN: llc %s -start-after=codegenprepare -stop-before=finalize-isel -o - | FileCheck %s + +;; %y is unused and cannot (FIXME: currently) be salvaged. Ensure that the +;; variadic dbg_value using %y becomes undef. + +; CHECK: ![[C:[0-9]+]] = !DILocalVariable(name: "c", +; CHECK: DBG_VALUE_LIST ![[C]], !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_arg, 1, DW_OP_plus, DW_OP_stack_value), $noreg, $noreg, debug-location + +target triple = "x86_64-pc-windows-msvc19.16.27034" +define dso_local i32 @"?foo@@YAHHH@Z"(i32 %a, i32 %b) local_unnamed_addr !dbg !8 { +entry: + %x = add i32 %a, %b + %y = mul i32 %x, %b + call void @llvm.dbg.value(metadata !DIArgList(i32 %x, i32 %y), metadata !16, metadata !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_arg, 1, DW_OP_plus, DW_OP_stack_value)), !dbg !17 + ret i32 %x +} + +declare void @llvm.dbg.value(metadata, metadata, metadata) + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!3, !4, !5, !6} +!llvm.ident = !{!7} + +!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang version 11.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, nameTableKind: None) +!1 = !DIFile(filename: "test.cpp", directory: "/") +!2 = !{} +!3 = !{i32 2, !"CodeView", i32 1} +!4 = !{i32 2, !"Debug Info Version", i32 3} +!5 = !{i32 1, !"wchar_size", i32 2} +!6 = !{i32 7, !"PIC Level", i32 2} +!7 = !{!"clang version 11.0.0"} +!8 = distinct !DISubprogram(name: "foo", linkageName: "?foo@@YAHHH@Z", scope: !9, file: !9, line: 1, type: !10, scopeLine: 1, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !13) +!9 = !DIFile(filename: "test.cpp", directory: "/") +!10 = !DISubroutineType(types: !11) +!11 = !{!12, !12, !12} +!12 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!13 = !{!14, !15, !16} +!14 = !DILocalVariable(name: "a", arg: 1, scope: !8, file: !9, line: 1, type: !12) +!15 = !DILocalVariable(name: "b", arg: 2, scope: !8, file: !9, line: 1, type: !12) +!16 = !DILocalVariable(name: "c", scope: !8, file: !9, line: 2, type: !12) +!17 = !DILocation(line: 0, scope: !8) diff --git a/llvm/test/DebugInfo/X86/dbg-value-list-dag-combine.ll b/llvm/test/DebugInfo/X86/dbg-value-list-dag-combine.ll new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/X86/dbg-value-list-dag-combine.ll @@ -0,0 +1,61 @@ +; RUN: llc < %s -start-after=codegenprepare -stop-before=finalize-isel | FileCheck %s --implicit-check-not=DBG_VALUE + +;; Test for PR 9817 adapted for variadic dbg.values (those using !DIArgList) by +;; hand. The debug nodes for idx and gid are transferred to new nodes via +;; TransferDbgValue. There should be a DEBUG_VALUE_LIST for each call to +;; llvm.dbg.value. + +; CHECK-DAG: ![[ip:[0-9]+]] = !DILocalVariable(name: "ip", +; CHECK-DAG: ![[gid:[0-9]+]] = !DILocalVariable(name: "gid", +; CHECK-DAG: ![[xxx:[0-9]+]] = !DILocalVariable(name: "xxx", +; CHECK-DAG: ![[idx:[0-9]+]] = !DILocalVariable(name: "idx", + +; CHECK: DBG_VALUE_LIST ![[ip]], !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_stack_value), +; CHECK: DBG_VALUE_LIST ![[gid]], !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_stack_value), +;; Constant value dbg.value should keep its value. +; CHECK: DBG_VALUE_LIST ![[xxx]], !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_stack_value), 0 +; CHECK: DBG_VALUE_LIST ![[idx]], !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_stack_value), + +target datalayout = "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:32" +target triple = "i686-apple-darwin" + +declare <4 x i32> @__amdil_get_global_id_int() +declare void @llvm.dbg.value(metadata, metadata, metadata) +define void @__OpenCL_test_kernel(i32 addrspace(1)* %ip) nounwind !dbg !0 { +entry: + call void @llvm.dbg.value(metadata !DIArgList(i32 addrspace(1)* %ip), metadata !7, metadata !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_stack_value)), !dbg !8 + %0 = call <4 x i32> @__amdil_get_global_id_int() nounwind + %1 = extractelement <4 x i32> %0, i32 0 + call void @llvm.dbg.value(metadata !DIArgList(i32 %1), metadata !9, metadata !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_stack_value)), !dbg !11 + call void @llvm.dbg.value(metadata !DIArgList(i32 0), metadata !21, metadata !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_stack_value)), !dbg !14 + %tmp2 = load i32, i32 addrspace(1)* %ip, align 4, !dbg !15 + %tmp3 = add i32 0, %tmp2, !dbg !15 + call void @llvm.dbg.value(metadata !DIArgList(i32 %tmp3), metadata !13, metadata !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_stack_value)), !dbg !15 + %arrayidx = getelementptr i32, i32 addrspace(1)* %ip, i32 %1, !dbg !16 + store i32 %tmp3, i32 addrspace(1)* %arrayidx, align 4, !dbg !16 + ret void, !dbg !17 +} +!llvm.dbg.cu = !{!2} +!llvm.module.flags = !{!20} + +!0 = distinct !DISubprogram(name: "__OpenCL_test_kernel", linkageName: "__OpenCL_test_kernel", line: 2, isLocal: false, isDefinition: true, virtualIndex: 6, isOptimized: false, unit: !2, file: !19, scope: !1, type: !3) +!1 = !DIFile(filename: "OCL6368.tmp.cl", directory: "E:CUsersCmvillmow.AMDCAppDataCLocalCTemp") +!2 = distinct !DICompileUnit(language: DW_LANG_C89, producer: "clc", isOptimized: false, emissionKind: FullDebug, file: !19, enums: !12, retainedTypes: !12, imports: null) +!3 = !DISubroutineType(types: !4) +!4 = !{null, !5} +!5 = !DIDerivedType(tag: DW_TAG_pointer_type, size: 32, align: 32, scope: !2, baseType: !6) +!6 = !DIBasicType(tag: DW_TAG_base_type, name: "unsigned int", size: 32, align: 32, encoding: DW_ATE_unsigned) +!7 = !DILocalVariable(name: "ip", line: 1, arg: 1, scope: !0, file: !1, type: !5) +!8 = !DILocation(line: 1, column: 42, scope: !0) +!9 = !DILocalVariable(name: "gid", line: 3, scope: !10, file: !1, type: !6) +!10 = distinct !DILexicalBlock(line: 2, column: 1, file: !19, scope: !0) +!11 = !DILocation(line: 3, column: 41, scope: !10) +!12 = !{} +!13 = !DILocalVariable(name: "idx", line: 4, scope: !10, file: !1, type: !6) +!14 = !DILocation(line: 4, column: 20, scope: !10) +!15 = !DILocation(line: 5, column: 15, scope: !10) +!16 = !DILocation(line: 6, column: 18, scope: !10) +!17 = !DILocation(line: 7, column: 1, scope: !0) +!19 = !DIFile(filename: "OCL6368.tmp.cl", directory: "E:\5CUsers\5Cmvillmow.AMD\5CAppData\5CLocal\5CTemp") +!20 = !{i32 1, !"Debug Info Version", i32 3} +!21 = !DILocalVariable(name: "xxx", line: 4, scope: !10, file: !1, type: !6) diff --git a/llvm/test/DebugInfo/X86/dbg-value-list-selectiondag-salvage.ll b/llvm/test/DebugInfo/X86/dbg-value-list-selectiondag-salvage.ll new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/X86/dbg-value-list-selectiondag-salvage.ll @@ -0,0 +1,72 @@ +; RUN: llc %s -start-after=codegenprepare -stop-before=finalize-isel -o - | FileCheck %s + +;; Generated from clang -O2 -emit-llvm -S -g reduce.c -o - +;; $ cat reduce.c +;; struct { +;; int a; +;; } * b; +;; int c; +;; void d() { +;; int *e = &b->a - 1; // XXX +;; c = *e; +;; } +;; +;; The line marked XXX becomes a load and gep in IR. We have a variadic +;; dbg.value using the gep, but we lose that gep in SelectionDAG. Ensure that +;; we salvage the value. + +; CHECK: [[E_REG:%[0-9]+]]{{.+}} = MOV{{.+}} @b +; CHECK: DBG_VALUE_LIST {{.*}}, !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_constu, 4, DW_OP_minus, DW_OP_stack_value), [[E_REG]], debug-location + +target triple = "x86_64-unknown-linux-gnu" + +%struct.anon = type { i32 } + +@b = dso_local local_unnamed_addr global %struct.anon* null, align 8, !dbg !0 +@c = dso_local local_unnamed_addr global i32 0, align 4, !dbg !6 + +define dso_local void @d() local_unnamed_addr !dbg !17 { +entry: + %0 = load %struct.anon*, %struct.anon** @b, align 8, !dbg !23 + %add.ptr = getelementptr inbounds %struct.anon, %struct.anon* %0, i64 -1, i32 0, !dbg !28 + call void @llvm.dbg.value(metadata !DIArgList(i32* %add.ptr), metadata !21, metadata !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_stack_value)), !dbg !29 + %1 = load i32, i32* %add.ptr, align 4, !dbg !30 + store i32 %1, i32* @c, align 4, !dbg !33 + ret void, !dbg !34 +} + +declare void @llvm.dbg.value(metadata, metadata, metadata) + +!llvm.dbg.cu = !{!2} +!llvm.module.flags = !{!13, !14, !15} +!llvm.ident = !{!16} + +!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) +!1 = distinct !DIGlobalVariable(name: "b", scope: !2, file: !3, line: 3, type: !9, isLocal: false, isDefinition: true) +!2 = distinct !DICompileUnit(language: DW_LANG_C99, file: !3, producer: "clang version 11.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !4, globals: !5, splitDebugInlining: false, nameTableKind: None) +!3 = !DIFile(filename: "reduce.c", directory: "/") +!4 = !{} +!5 = !{!0, !6} +!6 = !DIGlobalVariableExpression(var: !7, expr: !DIExpression()) +!7 = distinct !DIGlobalVariable(name: "c", scope: !2, file: !3, line: 4, type: !8, isLocal: false, isDefinition: true) +!8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!9 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !10, size: 64) +!10 = distinct !DICompositeType(tag: DW_TAG_structure_type, file: !3, line: 1, size: 32, elements: !11) +!11 = !{!12} +!12 = !DIDerivedType(tag: DW_TAG_member, name: "a", scope: !10, file: !3, line: 2, baseType: !8, size: 32) +!13 = !{i32 7, !"Dwarf Version", i32 4} +!14 = !{i32 2, !"Debug Info Version", i32 3} +!15 = !{i32 1, !"wchar_size", i32 4} +!16 = !{!"clang version 11.0.0"} +!17 = distinct !DISubprogram(name: "d", scope: !3, file: !3, line: 5, type: !18, scopeLine: 5, flags: DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !20) +!18 = !DISubroutineType(types: !19) +!19 = !{null} +!20 = !{!21} +!21 = !DILocalVariable(name: "e", scope: !17, file: !3, line: 6, type: !22) +!22 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !8, size: 64) +!23 = !DILocation(line: 6, column: 13, scope: !17) +!28 = !DILocation(line: 6, column: 18, scope: !17) +!29 = !DILocation(line: 0, scope: !17) +!30 = !DILocation(line: 7, column: 7, scope: !17) +!33 = !DILocation(line: 7, column: 5, scope: !17) +!34 = !DILocation(line: 8, column: 1, scope: !17) diff --git a/llvm/test/DebugInfo/X86/dbg_value_list_clobbers.mir b/llvm/test/DebugInfo/X86/dbg_value_list_clobbers.mir new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/X86/dbg_value_list_clobbers.mir @@ -0,0 +1,84 @@ +# RUN: llc %s --start-after=livedebugvalues -filetype=obj -o - \ +# RUN: | llvm-dwarfdump - -name locala -o - +# +# Test that clobbers between DBG_VALUE_LIST and DBG_VALUE instructions work as +# expected. Comments and test directives inline. + +--- | + target triple = "x86_64-unknown-linux-gnu" + define dso_local i32 @fun() local_unnamed_addr !dbg !7 { + entry: + ret i32 0 + } + + !llvm.dbg.cu = !{!0} + !llvm.module.flags = !{!3, !4, !5} + !llvm.ident = !{!6} + + !0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 11.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, splitDebugInlining: false, nameTableKind: None) + !1 = !DIFile(filename: "example.c", directory: "/") + !2 = !{} + !3 = !{i32 7, !"Dwarf Version", i32 4} + !4 = !{i32 2, !"Debug Info Version", i32 3} + !5 = !{i32 1, !"wchar_size", i32 4} + !6 = !{!"clang version 11.0.0"} + !8 = !DISubroutineType(types: !9) + !9 = !{!10} + !10 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) + !11 = !{!12} + !22 = !DISubroutineType(types: !23) + !23 = !{!10, !10} + ; --- Important metadata --- + !7 = distinct !DISubprogram(name: "fun", scope: !1, file: !1, line: 2, type: !8, scopeLine: 2, flags: DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !11) + !15 = !DILocation(line: 1, column: 1, scope: !7) + !12 = !DILocalVariable(name: "locala", scope: !7, file: !1, line: 1, type: !10) + +... +--- +name: fun +body: | + bb.0.entry: + ; This test checks that we see expected location ranges for a single variable. + ; CHECK: {{.*}} DW_TAG_variable + ; CHECK-NEXT: DW_AT_location {{.*}} + + DBG_VALUE_LIST !12, !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_stack_value), $eax, debug-location !15 + ; CHECK-NEXT: [{{.*}}): DW_OP_breg0 RAX+0, DW_OP_stack_value + + $edi = MOV32ri 1 + DBG_VALUE_LIST !12, !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_stack_value), $esi, debug-location !15 + ; CHECK-NEXT: [{{.*}}): DW_OP_breg4 RSI+0, DW_OP_stack_value + + $eax = MOV32ri 2 + DBG_VALUE $eax, $noreg, !12, !DIExpression(), debug-location !15 + ; CHECK-NEXT: [{{.*}}): DW_OP_reg0 RAX + + $ecx = MOV32ri 3 + DBG_VALUE_LIST !12, !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_arg, 1, DW_OP_plus, DW_OP_stack_value), $eax, $ecx, debug-location !15 + ; CHECK-NEXT: [{{.*}}): DW_OP_breg0 RAX+0, DW_OP_breg2 RCX+0, DW_OP_plus, DW_OP_stack_value + + ; Check that a reg clobber prevents identical locations merging. + $ecx = MOV32ri 4 + $ecx = MOV32ri 5 + DBG_VALUE_LIST !12, !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_arg, 1, DW_OP_plus, DW_OP_stack_value), $eax, $ecx, debug-location !15 + ; CHECK-NEXT: [{{.*}}): DW_OP_breg0 RAX+0, DW_OP_breg2 RCX+0, DW_OP_plus, DW_OP_stack_value + + ; Check that fragments are composed correctly. + $ecx = MOV32ri 6 + DBG_VALUE_LIST !12, !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_stack_value, DW_OP_LLVM_fragment, 0, 16), $eax, debug-location !15 + DBG_VALUE_LIST !12, !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_stack_value, DW_OP_LLVM_fragment, 16, 16), $ecx, debug-location !15 + ; CHECK-NEXT: [{{.*}}): DW_OP_breg0 RAX+0, DW_OP_stack_value, DW_OP_piece 0x2, DW_OP_breg2 RCX+0, DW_OP_stack_value, DW_OP_piece 0x2 + + ; Check that fragments clobber preceeding overlap. + $edi = MOV32ri 7 + DBG_VALUE_LIST !12, !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_stack_value, DW_OP_LLVM_fragment, 16, 16), $edi, debug-location !15 + ; CHECK-NEXT: [{{.*}}): DW_OP_breg0 RAX+0, DW_OP_stack_value, DW_OP_piece 0x2, DW_OP_breg5 RDI+0, DW_OP_stack_value, DW_OP_piece 0x2 + + ; Check that a (non-zero-offset) fragment works. + $ecx = MOV32ri 8 + $ecx = MOV32ri 9 + DBG_VALUE_LIST !12, !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_arg, 1, DW_OP_plus, DW_OP_stack_value, DW_OP_LLVM_fragment, 16, 16), $eax, $ecx, debug-location !15 + ; CHECK-NEXT: [{{.*}}): DW_OP_breg0 RAX+0, DW_OP_stack_value, DW_OP_piece 0x2, DW_OP_breg0 RAX+0, DW_OP_breg2 RCX+0, DW_OP_plus, DW_OP_stack_value, DW_OP_piece 0x2 + + RETQ debug-location !15 +... diff --git a/llvm/test/DebugInfo/X86/dbg_value_list_emission.mir b/llvm/test/DebugInfo/X86/dbg_value_list_emission.mir new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/X86/dbg_value_list_emission.mir @@ -0,0 +1,94 @@ +# RUN: llc %s --start-after=livedebugvalues -filetype=obj -o - \ +# RUN: | llvm-dwarfdump - -name local* -regex \ +# RUN: | FileCheck %s +# +# Test that we produce good DWARF from DBG_VALUE_LIST instructions. +# Comments and test directives inline. + +--- | + target triple = "x86_64-unknown-linux-gnu" + define dso_local i32 @fun() local_unnamed_addr !dbg !7 { + entry: + ret i32 0 + } + + !llvm.dbg.cu = !{!0} + !llvm.module.flags = !{!3, !4, !5} + !llvm.ident = !{!6} + + !0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 11.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, splitDebugInlining: false, nameTableKind: None) + !1 = !DIFile(filename: "example.c", directory: "/") + !2 = !{} + !3 = !{i32 7, !"Dwarf Version", i32 4} + !4 = !{i32 2, !"Debug Info Version", i32 3} + !5 = !{i32 1, !"wchar_size", i32 4} + !6 = !{!"clang version 11.0.0"} + !8 = !DISubroutineType(types: !9) + !9 = !{!10} + !10 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) + !11 = !{!12, !13, !25} + !22 = !DISubroutineType(types: !23) + !23 = !{!10, !10} + ; --- Important metadata --- + !7 = distinct !DISubprogram(name: "fun", scope: !1, file: !1, line: 2, type: !8, scopeLine: 2, flags: DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !11) + !15 = !DILocation(line: 1, column: 1, scope: !7) + !12 = !DILocalVariable(name: "locala", scope: !7, file: !1, line: 1, type: !10) + !13 = !DILocalVariable(name: "localb", scope: !7, file: !1, line: 2, type: !10) + !25 = !DILocalVariable(name: "localc", scope: !7, file: !1, line: 3, type: !10) + !26 = !DILocalVariable(name: "locald", scope: !7, file: !1, line: 4, type: !10) + !27 = !DILocalVariable(name: "locale", scope: !7, file: !1, line: 5, type: !10) + !28 = !DILocalVariable(name: "localf", scope: !7, file: !1, line: 6, type: !10) + !29 = !DILocalVariable(name: "localg", scope: !7, file: !1, line: 6, type: !10) + +... +--- +name: fun +body: | + bb.0.entry: + ; NOTE: By design, all DBG_VALUE_LIST instructions describe stack_value + ; locations, so they are always created with a DW_OP_stack_value op. + ; + ; (1) Check a single reg arg works. + DBG_VALUE_LIST !12, !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_stack_value), $eax, debug-location !15 + ; CHECK: DW_TAG_variable + ; CHECK-NEXT: (DW_OP_breg0 RAX+0, DW_OP_constu 0xffffffff, DW_OP_and, DW_OP_stack_value) + ; CHECK-NEXT: DW_AT_name ("locala") + + ; (2) Check multiple reg args work. + DBG_VALUE_LIST !13, !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_arg, 1, DW_OP_plus, DW_OP_stack_value), $eax, $edi, debug-location !15 + ; CHECK: DW_TAG_variable + ; CHECK-NEXT: (DW_OP_breg0 RAX+0, DW_OP_constu 0xffffffff, DW_OP_and, DW_OP_breg5 RDI+0, DW_OP_constu 0xffffffff, DW_OP_and, DW_OP_plus, DW_OP_stack_value) + ; CHECK-NEXT: DW_AT_name ("localb") + + ; (3) Check that multiple references to one reg arg works. + DBG_VALUE_LIST !25, !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_arg, 0, DW_OP_minus, DW_OP_stack_value), $eax, debug-location !15 + ; CHECK: DW_TAG_variable + ; CHECK-NEXT: (DW_OP_breg0 RAX+0, DW_OP_constu 0xffffffff, DW_OP_and, DW_OP_breg0 RAX+0, DW_OP_constu 0xffffffff, DW_OP_and, DW_OP_minus, DW_OP_stack_value) + ; CHECK-NEXT: DW_AT_name ("localc") + + ; (4) Check constant and reg args work together. + DBG_VALUE_LIST !26, !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_arg, 1, DW_OP_mul, DW_OP_stack_value), $eax, 5, debug-location !15 + ; CHECK: DW_TAG_variable + ; CHECK-NEXT: (DW_OP_breg0 RAX+0, DW_OP_constu 0xffffffff, DW_OP_and, DW_OP_lit5, DW_OP_mul, DW_OP_stack_value) + ; CHECK-NEXT: DW_AT_name ("locald") + + ; (5) Check that arg deref works. + DBG_VALUE_LIST !27, !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_deref, DW_OP_stack_value), $eax, debug-location !15 + ; CHECK: DW_TAG_variable + ; CHECK-NEXT: (DW_OP_breg0 RAX+0, DW_OP_constu 0xffffffff, DW_OP_and, DW_OP_deref, DW_OP_stack_value) + ; CHECK-NEXT: DW_AT_name ("locale") + + ; (6) Check that fragments work. + DBG_VALUE_LIST !28, !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_stack_value, DW_OP_LLVM_fragment, 0, 16), $eax, debug-location !15 + ; CHECK: DW_TAG_variable + ; CHECK-NEXT: (DW_OP_breg0 RAX+0, DW_OP_constu 0xffffffff, DW_OP_and, DW_OP_stack_value, DW_OP_piece 0x2) + ; CHECK-NEXT: DW_AT_name ("localf") + + ; (7) Check that constant register offsets are correctly folded. + DBG_VALUE_LIST !29, !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_plus_uconst, 5, DW_OP_LLVM_arg, 1, DW_OP_plus_uconst, 17, DW_OP_plus, DW_OP_stack_value), $eax, $edi, debug-location !15 + ; CHECK: DW_TAG_variable + ; CHECK-NEXT: (DW_OP_breg0 RAX+5, DW_OP_constu 0xffffffff, DW_OP_and, DW_OP_breg5 RDI+17, DW_OP_constu 0xffffffff, DW_OP_and, DW_OP_plus, DW_OP_stack_value) + ; CHECK-NEXT: DW_AT_name ("localg") + + RETQ debug-location !15 +... diff --git a/llvm/test/DebugInfo/X86/debug_value_list_selectiondag.ll b/llvm/test/DebugInfo/X86/debug_value_list_selectiondag.ll new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/X86/debug_value_list_selectiondag.ll @@ -0,0 +1,50 @@ +; RUN: llc %s -start-after=codegenprepare -stop-before=finalize-isel -o - | FileCheck %s + +;; Test that a dbg.value that uses a DIArgList is correctly converted to a +;; DBG_VALUE_LIST that uses the registers corresponding to its operands. + +; CHECK-DAG: [[A_VAR:![0-9]+]] = !DILocalVariable(name: "a" +; CHECK-DAG: [[B_VAR:![0-9]+]] = !DILocalVariable(name: "b" +; CHECK-DAG: [[C_VAR:![0-9]+]] = !DILocalVariable(name: "c" +; CHECK-LABEL: bb.0.entry +; CHECK-DAG: DBG_VALUE_LIST [[A_VAR]], !DIExpression(DW_OP_LLVM_arg, 0), %0, debug-location +; CHECK-DAG: DBG_VALUE_LIST [[B_VAR]], !DIExpression(DW_OP_LLVM_arg, 0), %1, debug-location +; CHECK: DBG_VALUE_LIST [[C_VAR]], !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_arg, 1, DW_OP_plus), %0, %1, debug-location + +target datalayout = "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-windows-msvc19.16.27034" + +define dso_local i32 @"?foo@@YAHHH@Z"(i32 %a, i32 %b) local_unnamed_addr !dbg !8 { +entry: + call void @llvm.dbg.value(metadata !DIArgList(i32 %b), metadata !14, metadata !DIExpression(DW_OP_LLVM_arg, 0)), !dbg !17 + call void @llvm.dbg.value(metadata !DIArgList(i32 %a), metadata !15, metadata !DIExpression(DW_OP_LLVM_arg, 0)), !dbg !17 + call void @llvm.dbg.value(metadata !DIArgList(i32 %a, i32 %b), metadata !16, metadata !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_arg, 1, DW_OP_plus)), !dbg !17 + %mul = mul nsw i32 %b, %a, !dbg !18 + ret i32 %mul, !dbg !18 +} + +declare void @llvm.dbg.value(metadata, metadata, metadata) + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!3, !4, !5, !6} +!llvm.ident = !{!7} + +!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang version 11.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, nameTableKind: None) +!1 = !DIFile(filename: "debug_value_list_selectiondag.cpp", directory: "/") +!2 = !{} +!3 = !{i32 2, !"CodeView", i32 1} +!4 = !{i32 2, !"Debug Info Version", i32 3} +!5 = !{i32 1, !"wchar_size", i32 2} +!6 = !{i32 7, !"PIC Level", i32 2} +!7 = !{!"clang version 11.0.0"} +!8 = distinct !DISubprogram(name: "foo", linkageName: "?foo@@YAHHH@Z", scope: !9, file: !9, line: 1, type: !10, scopeLine: 1, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !13) +!9 = !DIFile(filename: ".\\debug_value_list.cpp", directory: "/tmp") +!10 = !DISubroutineType(types: !11) +!11 = !{!12, !12, !12} +!12 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!13 = !{!14, !15, !16} +!14 = !DILocalVariable(name: "b", arg: 2, scope: !8, file: !9, line: 1, type: !12) +!15 = !DILocalVariable(name: "a", arg: 1, scope: !8, file: !9, line: 1, type: !12) +!16 = !DILocalVariable(name: "c", scope: !8, file: !9, line: 2, type: !12) +!17 = !DILocation(line: 0, scope: !8) +!18 = !DILocation(line: 3, scope: !8) diff --git a/llvm/test/DebugInfo/salvage-gep.ll b/llvm/test/DebugInfo/salvage-gep.ll new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/salvage-gep.ll @@ -0,0 +1,56 @@ +; RUN: opt %s -dce -S | FileCheck %s + +; Tests the salvaging of GEP instructions, specifically struct indexing and +; non-constant array indexing. + +%struct.S = type { i32, i32 } + +; CHECK: call void @llvm.dbg.value(metadata !DIArgList(%struct.S* %ptr, i64 %offset), +; CHECK-SAME: ![[VAR_OFFSET_PTR:[0-9]+]], +; CHECK-SAME: !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_arg, 1, DW_OP_constu, 8, DW_OP_mul, DW_OP_plus, DW_OP_plus_uconst, 4, DW_OP_stack_value)) + +; CHECK: ![[VAR_OFFSET_PTR]] = !DILocalVariable(name: "offset_ptr" + +define void @"?foo@@YAXPEAUS@@_J@Z"(%struct.S* %ptr, i64 %offset) !dbg !8 { +entry: + call void @llvm.dbg.value(metadata i64 %offset, metadata !20, metadata !DIExpression()), !dbg !24 + call void @llvm.dbg.value(metadata %struct.S* %ptr, metadata !21, metadata !DIExpression()), !dbg !24 + %arrayidx = getelementptr inbounds %struct.S, %struct.S* %ptr, i64 %offset, !dbg !25 + %b = getelementptr inbounds %struct.S, %struct.S* %arrayidx, i32 0, i32 1, !dbg !25 + call void @llvm.dbg.value(metadata i32* %b, metadata !22, metadata !DIExpression()), !dbg !24 + ret void, !dbg !26 +} + +declare void @llvm.dbg.value(metadata, metadata, metadata) + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!3, !4, !5, !6} +!llvm.ident = !{!7} + +!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang version 11.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, nameTableKind: None) +!1 = !DIFile(filename: "salvage-gep.cpp", directory: "/") +!2 = !{} +!3 = !{i32 2, !"CodeView", i32 1} +!4 = !{i32 2, !"Debug Info Version", i32 3} +!5 = !{i32 1, !"wchar_size", i32 2} +!6 = !{i32 7, !"PIC Level", i32 2} +!7 = !{!"clang version 11.0.0"} +!8 = distinct !DISubprogram(name: "foo", linkageName: "?foo@@YAXPEAUS@@_J@Z", scope: !9, file: !9, line: 7, type: !10, scopeLine: 7, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !19) +!9 = !DIFile(filename: ".\\salvage-gep.cpp", directory: "/") +!10 = !DISubroutineType(types: !11) +!11 = !{null, !12, !18} +!12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) +!13 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "S", file: !9, line: 2, size: 64, flags: DIFlagTypePassByValue, elements: !14, identifier: ".?AUS@@") +!14 = !{!15, !17} +!15 = !DIDerivedType(tag: DW_TAG_member, name: "a", scope: !13, file: !9, line: 3, baseType: !16, size: 32) +!16 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!17 = !DIDerivedType(tag: DW_TAG_member, name: "b", scope: !13, file: !9, line: 4, baseType: !16, size: 32, offset: 32) +!18 = !DIBasicType(name: "long long int", size: 64, encoding: DW_ATE_signed) +!19 = !{!20, !21, !22} +!20 = !DILocalVariable(name: "offset", arg: 2, scope: !8, file: !9, line: 7, type: !18) +!21 = !DILocalVariable(name: "ptr", arg: 1, scope: !8, file: !9, line: 7, type: !12) +!22 = !DILocalVariable(name: "offset_ptr", scope: !8, file: !9, line: 8, type: !23) +!23 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !16, size: 64) +!24 = !DILocation(line: 0, scope: !8) +!25 = !DILocation(line: 8, scope: !8) +!26 = !DILocation(line: 9, scope: !8) diff --git a/llvm/test/DebugInfo/salvage-nonconst-binop.ll b/llvm/test/DebugInfo/salvage-nonconst-binop.ll new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/salvage-nonconst-binop.ll @@ -0,0 +1,45 @@ +; RUN: opt %s -dce -S | FileCheck %s + +; Tests the salvaging of binary operators that use more than one non-constant +; SSA value. + +; CHECK: call void @llvm.dbg.value(metadata !DIArgList(i32 %a, i32 %b), +; CHECK-SAME: ![[VAR_C:[0-9]+]], +; CHECK-SAME: !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_arg, 1, DW_OP_plus, DW_OP_stack_value)) + +; CHECK: ![[VAR_C]] = !DILocalVariable(name: "c" + +define i32 @"?multiply@@YAHHH@Z"(i32 %a, i32 %b) !dbg !8 { +entry: + call void @llvm.dbg.value(metadata i32 %b, metadata !12, metadata !DIExpression()), !dbg !13 + call void @llvm.dbg.value(metadata i32 %a, metadata !14, metadata !DIExpression()), !dbg !13 + %add = add nsw i32 %a, %b, !dbg !15 + call void @llvm.dbg.value(metadata i32 %add, metadata !16, metadata !DIExpression()), !dbg !13 + %mul = mul nsw i32 %a, %b, !dbg !17 + ret i32 %mul, !dbg !17 +} + +declare void @llvm.dbg.value(metadata, metadata, metadata) + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!3, !4, !5, !6} +!llvm.ident = !{!7} + +!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang version 11.0.0", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, nameTableKind: None) +!1 = !DIFile(filename: "test.cpp", directory: "/") +!2 = !{} +!3 = !{i32 2, !"CodeView", i32 1} +!4 = !{i32 2, !"Debug Info Version", i32 3} +!5 = !{i32 1, !"wchar_size", i32 2} +!6 = !{i32 7, !"PIC Level", i32 2} +!7 = !{!"clang version 11.0.0"} +!8 = distinct !DISubprogram(name: "multiply", linkageName: "?multiply@@YAHHH@Z", scope: !1, file: !1, line: 1, type: !9, scopeLine: 1, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !2) +!9 = !DISubroutineType(types: !10) +!10 = !{!11, !11, !11} +!11 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!12 = !DILocalVariable(name: "b", arg: 2, scope: !8, file: !1, line: 1, type: !11) +!13 = !DILocation(line: 0, scope: !8) +!14 = !DILocalVariable(name: "a", arg: 1, scope: !8, file: !1, line: 1, type: !11) +!15 = !DILocation(line: 2, scope: !8) +!16 = !DILocalVariable(name: "c", scope: !8, file: !1, line: 2, type: !11) +!17 = !DILocation(line: 3, scope: !8) diff --git a/llvm/test/Transforms/InstCombine/debuginfo-sink.ll b/llvm/test/Transforms/InstCombine/debuginfo-sink.ll --- a/llvm/test/Transforms/InstCombine/debuginfo-sink.ll +++ b/llvm/test/Transforms/InstCombine/debuginfo-sink.ll @@ -33,23 +33,25 @@ ; value range. ; CHECK-LABEL: define i32 @bar( -; CHECK: call void @llvm.dbg.value(metadata i32* undef, +; CHECK: call void @llvm.dbg.value(metadata * undef, ; CHECK-NEXT: br label %sink2 -define i32 @bar(i32 *%a, i32 %b) !dbg !70 { +define i32 @bar(* %a, i32 %b) !dbg !70 { entry: - %gep = getelementptr i32, i32 *%a, i32 %b - call void @llvm.dbg.value(metadata i32* %gep, metadata !73, metadata !12), !dbg !74 + %gep = getelementptr , * %a, i32 %b + call void @llvm.dbg.value(metadata * %gep, metadata !73, metadata !12), !dbg !74 br label %sink2 sink2: ; CHECK-LABEL: sink2: -; CHECK: call void @llvm.dbg.value(metadata i32* %gep, +; CHECK: call void @llvm.dbg.value(metadata * %gep, ; CHECK-SAME: metadata !{{[0-9]+}}, metadata !DIExpression()) ; CHECK-NEXT: load +; CHECK-NEXT: extractelement ; CHECK-NEXT: ret - %0 = load i32, i32* %gep - ret i32 %0 + %0 = load , * %gep + %extract = extractelement %0, i32 1 + ret i32 %extract } !llvm.dbg.cu = !{!0} @@ -76,3 +78,7 @@ !72 = !{!10, !10, !10} !73 = !DILocalVariable(name: "k", scope: !70, file: !1, line: 2, type: !10) !74 = !DILocation(line: 5, column: 3, scope: !70) +!80 = distinct !DISubprogram(name: "baz", scope: !1, file: !1, line: 2, type: !8, isLocal: false, isDefinition: true, scopeLine: 3, flags: DIFlagPrototyped, isOptimized: false, unit: !0, retainedNodes: !2) +!83 = !DILocalVariable(name: "l", scope: !80, file: !1, line: 2, type: !10) +!84 = !DILocation(line: 5, column: 3, scope: !80) +!85 = !DILocation(line: 6, column: 3, scope: !80) diff --git a/llvm/test/Transforms/InstCombine/debuginfo_add.ll b/llvm/test/Transforms/InstCombine/debuginfo_add.ll --- a/llvm/test/Transforms/InstCombine/debuginfo_add.ll +++ b/llvm/test/Transforms/InstCombine/debuginfo_add.ll @@ -36,8 +36,8 @@ ; The add is later eliminated, so we verify that the dbg.value is salvaged by using DW_OP_minus. ; CHECK-LABEL: for.body.lr.ph: ; CHECK-NEXT: %0 = load - ; CHECK-NEXT: call void @llvm.dbg.value(metadata i64 %0, metadata !26, metadata !DIExpression(DW_OP_constu, 4096, DW_OP_minus, DW_OP_stack_value)), !dbg ! ; CHECK-NEXT: call void @llvm.dbg.value(metadata i64 %0, metadata !25, metadata !DIExpression()), !dbg ! + ; CHECK-NEXT: call void @llvm.dbg.value(metadata i64 %0, metadata !26, metadata !DIExpression(DW_OP_constu, 4096, DW_OP_minus, DW_OP_stack_value)), !dbg ! br label %for.body, !dbg !32 for.body: ; preds = %for.body.lr.ph, %for.body diff --git a/llvm/test/Transforms/Reassociate/undef_intrinsics_when_deleting_instructions.ll b/llvm/test/Transforms/Reassociate/undef_intrinsics_when_deleting_instructions.ll --- a/llvm/test/Transforms/Reassociate/undef_intrinsics_when_deleting_instructions.ll +++ b/llvm/test/Transforms/Reassociate/undef_intrinsics_when_deleting_instructions.ll @@ -1,95 +1,73 @@ -; RUN: opt < %s -reassociate -S | FileCheck %s +; RUN: opt < %s -reassociate -S | FileCheck %s + +; Check that reassociate pass now undefs debug intrinsics that reference a value +; that gets dropped and cannot be salvaged. + +; CHECK-NOT: %add = fadd fast float %a, %b +; CHECK: call void @llvm.dbg.value(metadata float undef, metadata [[VAR_X:![0-9]+]], metadata !DIExpression()) + +; CHECK-LABEL: if.then: +; CHECK-NOT: %add1 = fadd fast float %add, %c +; CHECK: call void @llvm.dbg.value(metadata float undef, metadata [[VAR_Y:![0-9]+]], metadata !DIExpression()) +; CHECK-LABEL: !0 = +; CHECK-DAG: [[VAR_Y]] = !DILocalVariable(name: "y" +; CHECK-DAG: [[VAR_X]] = !DILocalVariable(name: "x" + +define float @"?foo@@YAMMMMM@Z"(float %a, float %b, float %c, float %d) !dbg !8 { +entry: + call void @llvm.dbg.value(metadata float %d, metadata !12, metadata !DIExpression()), !dbg !13 + call void @llvm.dbg.value(metadata float %c, metadata !14, metadata !DIExpression()), !dbg !13 + call void @llvm.dbg.value(metadata float %b, metadata !15, metadata !DIExpression()), !dbg !13 + call void @llvm.dbg.value(metadata float %a, metadata !16, metadata !DIExpression()), !dbg !13 + %add = fadd fast float %a, %b, !dbg !17 + call void @llvm.dbg.value(metadata float %add, metadata !18, metadata !DIExpression()), !dbg !13 + %cmp = fcmp fast oeq float %d, 4.000000e+00, !dbg !19 + br i1 %cmp, label %if.then, label %return, !dbg !19 -; Check that reassociate pass now undefs debug intrinsics that reference a value -; that gets dropped and cannot be salvaged. +if.then: ; preds = %entry + %add1 = fadd fast float %add, %c, !dbg !20 + call void @llvm.dbg.value(metadata float %add1, metadata !23, metadata !DIExpression()), !dbg !24 + %sub = fsub fast float %add, 1.200000e+01, !dbg !25 + %sub2 = fsub fast float %add1, %sub, !dbg !25 + %mul = fmul fast float %sub2, 2.000000e+01, !dbg !25 + %div = fdiv fast float %mul, 3.000000e+00, !dbg !25 + br label %return, !dbg !25 -define hidden i32 @main() local_unnamed_addr { -entry: - %foo = alloca i32, align 4, !dbg !20 - %foo.0.foo.0..sroa_cast = bitcast i32* %foo to i8*, !dbg !20 - call void @llvm.lifetime.start.p0i8(i64 4, i8* nonnull %foo.0.foo.0..sroa_cast), !dbg !20 - store volatile i32 4, i32* %foo, align 4, !dbg !20, !tbaa !21 - %foo.0.foo.0. = load volatile i32, i32* %foo, align 4, !dbg !25, !tbaa !21 - %foo.0.foo.0.15 = load volatile i32, i32* %foo, align 4, !dbg !27, !tbaa !21 - %foo.0.foo.0.16 = load volatile i32, i32* %foo, align 4, !dbg !28, !tbaa !21 - ; CHECK-NOT: %add = add nsw i32 %foo.0.foo.0., %foo.0.foo.0.15 - %add = add nsw i32 %foo.0.foo.0., %foo.0.foo.0.15, !dbg !29 - ; CHECK: call void @llvm.dbg.value(metadata i32 undef, metadata [[VAR_A:![0-9]+]], metadata !DIExpression()) - call void @llvm.dbg.value(metadata i32 %add, metadata !19, metadata !DIExpression()), !dbg !26 - %foo.0.foo.0.17 = load volatile i32, i32* %foo, align 4, !dbg !30, !tbaa !21 - %cmp = icmp eq i32 %foo.0.foo.0.17, 4, !dbg !30 - br i1 %cmp, label %if.then, label %if.end, !dbg !32 - - ; CHECK-LABEL: if.then: -if.then: - ; CHECK-NOT: %add1 = add nsw i32 %add, %foo.0.foo.0.16 - %add1 = add nsw i32 %add, %foo.0.foo.0.16, !dbg !33 - ; CHECK: call void @llvm.dbg.value(metadata i32 undef, metadata [[VAR_A]], metadata !DIExpression()) - call void @llvm.dbg.value(metadata i32 %add1, metadata !19, metadata !DIExpression()), !dbg !26 - ; CHECK: call void @llvm.dbg.value(metadata i32 undef, metadata [[VAR_CHEESE:![0-9]+]], metadata !DIExpression()) - call void @llvm.dbg.value(metadata i32 %add, metadata !18, metadata !DIExpression()), !dbg !26 - %sub = add nsw i32 %add, -12, !dbg !34 - %sub3 = sub nsw i32 %add1, %sub, !dbg !34 - %mul = mul nsw i32 %sub3, 20, !dbg !36 - %div = sdiv i32 %mul, 3, !dbg !37 - br label %if.end, !dbg !38 - -if.end: - %a.0 = phi i32 [ %div, %if.then ], [ 0, %entry ], !dbg !39 - call void @llvm.lifetime.end.p0i8(i64 4, i8* nonnull %foo.0.foo.0..sroa_cast), !dbg !40 - ret i32 %a.0, !dbg !41 +return: ; preds = %entry, %if.then + %retval.0 = phi float [ %div, %if.then ], [ 0.000000e+00, %entry ], !dbg !13 + ret float %retval.0, !dbg !26 } -declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #1 -declare void @llvm.dbg.declare(metadata, metadata, metadata) #2 -declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #1 -declare void @llvm.dbg.value(metadata, metadata, metadata) #2 +declare void @llvm.dbg.value(metadata, metadata, metadata) !llvm.dbg.cu = !{!0} !llvm.module.flags = !{!3, !4, !5, !6} !llvm.ident = !{!7} -!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang version 10.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, debugInfoForProfiling: true, nameTableKind: None) -!1 = !DIFile(filename: "test.cpp", directory: "F:\") +!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang version 11.0.0", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, nameTableKind: None) +!1 = !DIFile(filename: "undef_intrinsics_when_deleting_instructions.cpp", directory: "/") !2 = !{} -!3 = !{i32 2, !"Dwarf Version", i32 4} +!3 = !{i32 2, !"CodeView", i32 1} !4 = !{i32 2, !"Debug Info Version", i32 3} !5 = !{i32 1, !"wchar_size", i32 2} !6 = !{i32 7, !"PIC Level", i32 2} -!7 = !{!"clang version 10.0.0"} -!8 = distinct !DISubprogram(name: "main", scope: !9, file: !9, line: 1, type: !10, scopeLine: 1, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !13) -!9 = !DIFile(filename: "./test.cpp", directory: "F:\") -!10 = !DISubroutineType(types: !11) -!11 = !{!12} -!12 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) -!13 = !{!14, !16, !17, !18, !19} -!14 = !DILocalVariable(name: "foo", scope: !8, file: !9, line: 2, type: !15) -!15 = !DIDerivedType(tag: DW_TAG_volatile_type, baseType: !12) -!16 = !DILocalVariable(name: "read1", scope: !8, file: !9, line: 3, type: !12) -!17 = !DILocalVariable(name: "read2", scope: !8, file: !9, line: 4, type: !12) -; CHECK: [[VAR_CHEESE]] = !DILocalVariable(name: "cheese" -!18 = !DILocalVariable(name: "cheese", scope: !8, file: !9, line: 6, type: !12) -; CHECK: [[VAR_A]] = !DILocalVariable(name: "a" -!19 = !DILocalVariable(name: "a", scope: !8, file: !9, line: 7, type: !12) -!20 = !DILocation(line: 2, scope: !8) -!21 = !{!22, !22, i64 0} -!22 = !{!"int", !23, i64 0} -!23 = !{!"omnipotent char", !24, i64 0} -!24 = !{!"Simple C++ TBAA"} -!25 = !DILocation(line: 3, scope: !8) -!26 = !DILocation(line: 0, scope: !8) -!27 = !DILocation(line: 4, scope: !8) -!28 = !DILocation(line: 6, scope: !8) -!29 = !DILocation(line: 7, scope: !8) -!30 = !DILocation(line: 10, scope: !31) -!31 = distinct !DILexicalBlock(scope: !8, file: !9, line: 10) -!32 = !DILocation(line: 10, scope: !8) -!33 = !DILocation(line: 8, scope: !8) -!34 = !DILocation(line: 12, scope: !35) -!35 = distinct !DILexicalBlock(scope: !31, file: !9, line: 10) -!36 = !DILocation(line: 13, scope: !35) -!37 = !DILocation(line: 14, scope: !35) -!38 = !DILocation(line: 15, scope: !35) -!39 = !DILocation(line: 0, scope: !31) -!40 = !DILocation(line: 20, scope: !8) -!41 = !DILocation(line: 19, scope: !8) +!7 = !{!"clang version 11.0.0"} +!8 = distinct !DISubprogram(name: "foo", linkageName: "?foo@@YAMMMMM@Z", scope: !1, file: !1, line: 1, type: !9, scopeLine: 1, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !2) +!9 = !DISubroutineType(types: !10) +!10 = !{!11, !11, !11, !11, !11} +!11 = !DIBasicType(name: "float", size: 32, encoding: DW_ATE_float) +!12 = !DILocalVariable(name: "d", arg: 4, scope: !8, file: !1, line: 1, type: !11) +!13 = !DILocation(line: 0, scope: !8) +!14 = !DILocalVariable(name: "c", arg: 3, scope: !8, file: !1, line: 1, type: !11) +!15 = !DILocalVariable(name: "b", arg: 2, scope: !8, file: !1, line: 1, type: !11) +!16 = !DILocalVariable(name: "a", arg: 1, scope: !8, file: !1, line: 1, type: !11) +!17 = !DILocation(line: 2, scope: !8) +!18 = !DILocalVariable(name: "x", scope: !8, file: !1, line: 2, type: !11) +!19 = !DILocation(line: 3, scope: !8) +!20 = !DILocation(line: 4, scope: !21) +!21 = distinct !DILexicalBlock(scope: !22, file: !1, line: 3) +!22 = distinct !DILexicalBlock(scope: !8, file: !1, line: 3) +!23 = !DILocalVariable(name: "y", scope: !21, file: !1, line: 4, type: !11) +!24 = !DILocation(line: 0, scope: !21) +!25 = !DILocation(line: 5, scope: !21) +!26 = !DILocation(line: 8, scope: !8) diff --git a/llvm/unittests/IR/DebugInfoTest.cpp b/llvm/unittests/IR/DebugInfoTest.cpp --- a/llvm/unittests/IR/DebugInfoTest.cpp +++ b/llvm/unittests/IR/DebugInfoTest.cpp @@ -183,7 +183,8 @@ // Delete %b. The dbg.value should now point to undef. I.eraseFromParent(); - EXPECT_TRUE(isa(DVIs[0]->getValue())); + EXPECT_EQ(DVIs[0]->getNumVariableLocationOps(), 1); + EXPECT_TRUE(isa(DVIs[0]->getValue(0))); } TEST(DIBuilder, CreateFortranArrayTypeWithAttributes) { diff --git a/llvm/unittests/IR/MetadataTest.cpp b/llvm/unittests/IR/MetadataTest.cpp --- a/llvm/unittests/IR/MetadataTest.cpp +++ b/llvm/unittests/IR/MetadataTest.cpp @@ -2866,6 +2866,33 @@ #undef EXPECT_INVALID_FRAGMENT } +TEST_F(DIExpressionTest, replaceArg) { +#define EXPECT_REPLACE_ARG_EQ(Expr, OldArg, NewArg, ...) \ + do { \ + ArrayRef Expected({__VA_ARGS__}); \ + DIExpression *Expression = DIExpression::replaceArg(Expr, OldArg, NewArg); \ + EXPECT_EQ(Expression->getElements(), Expected); \ + } while (false) + + auto N = DIExpression::get( + Context, {dwarf::DW_OP_LLVM_arg, 0, dwarf::DW_OP_LLVM_arg, 1, + dwarf::DW_OP_plus, dwarf::DW_OP_LLVM_arg, 2, dwarf::DW_OP_mul}); + EXPECT_REPLACE_ARG_EQ(N, 0, 1, dwarf::DW_OP_LLVM_arg, 0, + dwarf::DW_OP_LLVM_arg, 0, dwarf::DW_OP_plus, + dwarf::DW_OP_LLVM_arg, 1, dwarf::DW_OP_mul); + EXPECT_REPLACE_ARG_EQ(N, 0, 2, dwarf::DW_OP_LLVM_arg, 1, + dwarf::DW_OP_LLVM_arg, 0, dwarf::DW_OP_plus, + dwarf::DW_OP_LLVM_arg, 1, dwarf::DW_OP_mul); + EXPECT_REPLACE_ARG_EQ(N, 2, 0, dwarf::DW_OP_LLVM_arg, 0, + dwarf::DW_OP_LLVM_arg, 1, dwarf::DW_OP_plus, + dwarf::DW_OP_LLVM_arg, 0, dwarf::DW_OP_mul); + EXPECT_REPLACE_ARG_EQ(N, 2, 1, dwarf::DW_OP_LLVM_arg, 0, + dwarf::DW_OP_LLVM_arg, 1, dwarf::DW_OP_plus, + dwarf::DW_OP_LLVM_arg, 1, dwarf::DW_OP_mul); + +#undef EXPECT_REPLACE_ARG_EQ +} + typedef MetadataTest DIObjCPropertyTest; TEST_F(DIObjCPropertyTest, get) { @@ -3056,6 +3083,42 @@ Temp->replaceAllUsesWith(nullptr); } +typedef MetadataTest DIArgListTest; + +TEST_F(DIArgListTest, get) { + SmallVector VMs; + VMs.push_back( + ConstantAsMetadata::get(ConstantInt::get(Context, APInt(8, 0)))); + VMs.push_back( + ConstantAsMetadata::get(ConstantInt::get(Context, APInt(2, 0)))); + DIArgList *DV0 = DIArgList::get(Context, VMs); + DIArgList *DV1 = DIArgList::get(Context, VMs); + EXPECT_EQ(DV0, DV1); +} + +TEST_F(DIArgListTest, UpdatesOnRAUW) { + Type *Ty = Type::getInt1PtrTy(Context); + ConstantAsMetadata *CI = + ConstantAsMetadata::get(ConstantInt::get(Context, APInt(8, 0))); + std::unique_ptr GV0( + new GlobalVariable(Ty, false, GlobalValue::ExternalLinkage)); + auto *MD0 = ValueAsMetadata::get(GV0.get()); + + SmallVector VMs; + VMs.push_back(CI); + VMs.push_back(MD0); + auto *AL = DIArgList::get(Context, VMs); + EXPECT_EQ(AL->getArgs()[0], CI); + EXPECT_EQ(AL->getArgs()[1], MD0); + + std::unique_ptr GV1( + new GlobalVariable(Ty, false, GlobalValue::ExternalLinkage)); + auto *MD1 = ValueAsMetadata::get(GV1.get()); + GV0->replaceAllUsesWith(GV1.get()); + EXPECT_EQ(AL->getArgs()[0], CI); + EXPECT_EQ(AL->getArgs()[1], MD1); +} + typedef MetadataTest TrackingMDRefTest; TEST_F(TrackingMDRefTest, UpdatesOnRAUW) { diff --git a/llvm/unittests/Transforms/Utils/LocalTest.cpp b/llvm/unittests/Transforms/Utils/LocalTest.cpp --- a/llvm/unittests/Transforms/Utils/LocalTest.cpp +++ b/llvm/unittests/Transforms/Utils/LocalTest.cpp @@ -524,7 +524,9 @@ } bool doesDebugValueDescribeX(const DbgValueInst &DI) { - const auto &CI = *cast(DI.getValue()); + if (DI.getNumVariableLocationOps() != 1) + return false; + const auto &CI = *cast(DI.getValue(0)); if (CI.isZero()) return DI.getExpression()->getElements().equals( {dwarf::DW_OP_plus_uconst, 1, dwarf::DW_OP_stack_value}); @@ -534,7 +536,9 @@ } bool doesDebugValueDescribeY(const DbgValueInst &DI) { - const auto &CI = *cast(DI.getValue()); + if (DI.getNumVariableLocationOps() != 1) + return false; + const auto &CI = *cast(DI.getValues(0)); if (CI.isZero()) return DI.getExpression()->getElements().equals( {dwarf::DW_OP_plus_uconst, 1, dwarf::DW_OP_plus_uconst, 2, @@ -758,13 +762,15 @@ EXPECT_TRUE(replaceAllDbgUsesWith(A, F_, F_, DT)); auto *ADbgVal = cast(A.getNextNode()); - EXPECT_EQ(ConstantInt::get(A.getType(), 0), ADbgVal->getVariableLocation()); + EXPECT_EQ(ADbgVal->getNumVariableLocationOps(), 1); + EXPECT_EQ(ConstantInt::get(A.getType(), 0), ADbgVal->getValues(0)); // Introduce a use-before-def. Check that the dbg.values for %f become undef. EXPECT_TRUE(replaceAllDbgUsesWith(F_, G, G, DT)); auto *FDbgVal = cast(F_.getNextNode()); - EXPECT_TRUE(isa(FDbgVal->getVariableLocation())); + EXPECT_EQ(FDbgVal->getNumVariableLocationOps(), 1); + EXPECT_TRUE(FDbgVal->isUndef()); SmallVector FDbgVals; findDbgValues(FDbgVals, &F_);