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 @@ -122,6 +122,7 @@ DW_OP_LLVM_convert = 0x1001, ///< Only used in LLVM metadata. 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_arg = 0x1004, ///< Only used in LLVM metadata. }; enum TypeKind : uint8_t { 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 @@ -412,11 +412,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); } @@ -481,26 +481,42 @@ 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; + /// Returns whether this debug value has at least one debug operand with the + /// register \p Reg. + const bool hasDebugOperandForReg(Register Reg) const { + return count_if(debug_operands(), [Reg](const MachineOperand &Op) { + return Op.isReg() && Op.getReg() == Reg; + }); + } + + /// Returns an array of pointers to all of the operands that correspond to a + /// debug use of \p Reg. + template + static ArrayRef getDebugOperandsForReg(Instruction *MI, + Register Reg) { + assert(MI->isDebugValue() && + "Tried to get debug operands for non-debug_value"); + SmallVector Ops; + for (Operand &Op : MI->debug_operands()) { + if (Op.isReg() && Op.getReg() == Reg) + Ops.push_back(&Op); + } + return Ops; + } + ArrayRef getDebugOperandsForReg(Register Reg) const { + return MachineInstr::getDebugOperandsForReg(this, Reg); } - 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; + ArrayRef 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); } @@ -580,12 +596,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! @@ -1140,16 +1160,26 @@ // 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 isDebugInstr() const { return isDebugValue() || isDebugLabel(); } - 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 @@ -1235,6 +1265,7 @@ case TargetOpcode::EH_LABEL: case TargetOpcode::GC_LABEL: case TargetOpcode::DBG_VALUE: + case TargetOpcode::DBG_VALUE_LIST: case TargetOpcode::DBG_LABEL: case TargetOpcode::LIFETIME_START: case TargetOpcode::LIFETIME_END: 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 @@ -438,9 +438,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. @@ -458,14 +465,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/IR/DebugInfoMetadata.h b/llvm/include/llvm/IR/DebugInfoMetadata.h --- a/llvm/include/llvm/IR/DebugInfoMetadata.h +++ b/llvm/include/llvm/IR/DebugInfoMetadata.h @@ -2591,6 +2591,12 @@ static DIExpression *appendToStack(const DIExpression *Expr, ArrayRef Ops); + /// Modify \p Expr by appending the given list of \p Ops to each instance of + /// the operand `DW_OP_LLVM_arg, \p LocNo`. This is used to modify a + /// specific location used by \p Expr, such as when salvaging that location. + static DIExpression *appendOpsToLoc(const DIExpression *Expr, + ArrayRef Ops, unsigned LocNo); + /// 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 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_LABEL - a mapping of the llvm.dbg.label intrinsic HANDLE_TARGET_OPCODE(DBG_LABEL) 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 = 0; } +def DBG_VALUE_LIST : StandardPseudoInstruction { + let OutOperandList = (outs); + let InOperandList = (ins variable_ops); + let AsmString = "DBG_VALUE_LIST"; + let hasSideEffects = 0; +} def DBG_LABEL : StandardPseudoInstruction { let OutOperandList = (outs); let InOperandList = (ins unknown:$label); 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 @@ -151,6 +151,8 @@ return "DW_OP_LLVM_tag_offset"; case DW_OP_LLVM_entry_value: return "DW_OP_LLVM_entry_value"; + case DW_OP_LLVM_arg: + return "DW_OP_LLVM_arg"; } } @@ -163,6 +165,7 @@ .Case("DW_OP_LLVM_fragment", DW_OP_LLVM_fragment) .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_arg", DW_OP_LLVM_arg) .Default(0); } 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 @@ -862,7 +862,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; @@ -1140,6 +1140,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/InlineSpiller.cpp b/llvm/lib/CodeGen/InlineSpiller.cpp --- a/llvm/lib/CodeGen/InlineSpiller.cpp +++ b/llvm/lib/CodeGen/InlineSpiller.cpp @@ -990,7 +990,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/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 @@ -989,7 +989,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 @@ -840,23 +840,27 @@ } MachineOperand &MachineInstr::getDebugVariableOp() { - assert(isDebugValue() && "not a DBG_VALUE"); - return getOperand(2); + assert(isDebugValue() && "not a DBG_VALUE*"); + unsigned VariableOp = isDebugValueList() ? 0 : 2; + return getOperand(VariableOp); } const DILocalVariable *MachineInstr::getDebugVariable() const { - assert(isDebugValue() && "not a DBG_VALUE"); - return cast(getOperand(2).getMetadata()); + assert(isDebugValue() && "not a DBG_VALUE*"); + unsigned VariableOp = isDebugValueList() ? 0 : 2; + return cast(getOperand(VariableOp).getMetadata()); } MachineOperand &MachineInstr::getDebugExpressionOp() { - assert(isDebugValue() && "not a DBG_VALUE"); - return getOperand(3); + assert(isDebugValue() && "not a DBG_VALUE*"); + unsigned ExpressionOp = isDebugValueList() ? 1 : 3; + return getOperand(ExpressionOp); } const DIExpression *MachineInstr::getDebugExpression() const { - assert(isDebugValue() && "not a DBG_VALUE"); - return cast(getOperand(3).getMetadata()); + assert(isDebugValue() && "not a DBG_VALUE*"); + unsigned ExpressionOp = isDebugValueList() ? 1 : 3; + return cast(getOperand(ExpressionOp).getMetadata()); } bool MachineInstr::isDebugEntryValue() const { @@ -1665,7 +1669,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() << '\"'; @@ -2064,8 +2068,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) && @@ -2079,7 +2083,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, @@ -2103,10 +2128,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"); @@ -2115,6 +2152,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::appendOpsToLoc(Expr, Ops, OpIdx); + } } return Expr; } @@ -2122,19 +2167,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); } @@ -2149,7 +2207,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); } } @@ -2167,14 +2225,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 @@ -549,8 +549,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/PrologEpilogInserter.cpp b/llvm/lib/CodeGen/PrologEpilogInserter.cpp --- a/llvm/lib/CodeGen/PrologEpilogInserter.cpp +++ b/llvm/lib/CodeGen/PrologEpilogInserter.cpp @@ -1221,22 +1221,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); + + 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 = DIExpression::prepend(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, deref`. + unsigned DebugOpIndex = MI.getDebugOperandIndex(&MI.getOperand(i)); + SmallVector Ops; + DIExpression::appendOffset(Ops, Offset); + DIExpr = DIExpression::appendOpsToLoc(DIExpr, Ops, DebugOpIndex); } - DIExpr = DIExpression::prepend(DIExpr, PrependFlags, Offset); 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 @@ -328,7 +328,8 @@ // value. 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); @@ -1156,38 +1157,40 @@ } void RegAllocFast::handleDebugValue(MachineInstr &MI) { - MachineOperand &MO = MI.getDebugOperand(0); + 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()) - return; - Register Reg = MO.getReg(); - if (!Register::isVirtualRegister(Reg)) - return; + // 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; - // 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 { - 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() << "Modifying debug info due to spill:" << "\t" << MI); - return; + // 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 { + 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() << "Modifying debug info due to spill:" + << "\t" << MI); + continue; + } + + // We can't allocate a physreg for a DebugValue, sorry! + LLVM_DEBUG(dbgs() << "Unable to allocate vreg used by DBG_VALUE"); + MO.setReg(Register()); } - // We can't allocate a physreg for a DebugValue, sorry! - LLVM_DEBUG(dbgs() << "Unable to allocate vreg used by DBG_VALUE"); - MO.setReg(Register()); + // 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); } - - // 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::allocateBasicBlock(MachineBasicBlock &MBB) { 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 @@ -961,6 +961,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: @@ -1017,6 +1018,7 @@ getNumElements() == 2; } 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: @@ -1170,6 +1172,22 @@ return prependOpcodes(Expr, Ops, StackValue, EntryValue); } +DIExpression *DIExpression::appendOpsToLoc(const DIExpression *Expr, + ArrayRef Ops, + unsigned LocNo) { + assert(Expr && "Can't add ops to this expression"); + + SmallVector NewOps; + + for (auto Op : Expr->expr_ops()) { + Op.appendToVector(NewOps); + if (Op.getOp() == dwarf::DW_OP_LLVM_arg && Op.getArg(0) == LocNo) + NewOps.insert(NewOps.end(), Ops.begin(), Ops.end()); + } + + return DIExpression::get(Expr->getContext(), NewOps); +} + DIExpression *DIExpression::prependOpcodes(const DIExpression *Expr, SmallVectorImpl &Ops, bool StackValue, 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 @@ -1070,7 +1070,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); @@ -1091,7 +1092,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/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,15 +65,26 @@ // 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); - MI.getOperand(0).ChangeToRegister(Reg, /*isDef=*/false); - MI.getOperand(0).setIsDebug(); - auto *DIExpr = DIExpression::prepend( - MI.getDebugExpression(), DIExpression::ApplyOffset, Offset); + TFI.getFrameIndexReference(MF, MI.getOperand(i).getIndex(), Reg); + MI.getOperand(i).ChangeToRegister(Reg, /*isDef=*/false); + MI.getOperand(i).setIsDebug(); + const DIExpression *DIExpr = MI.getDebugExpression(); + if (MI.isNonVariadicDebugValue()) { + DIExpr = DIExpression::prepend(MI.getDebugExpression(), + DIExpression::ApplyOffset, Offset); + } else { + SmallVector Ops; + DIExpression::appendOffset(Ops, Offset); + unsigned OpIdx = MI.getDebugOperandIndex(&MI.getOperand(i)); + DIExpr = DIExpression::appendOpsToLoc(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; @@ -271,7 +272,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)); + MI->getDebugExpressionOp().setMetadata( + DIExpression::appendOpsToLoc(MI->getDebugExpression(), Ops, OpIdx)); + } return; } 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,45 @@ } 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::appendOpsToLoc(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; + for (const MachineOperand &Op : MI.debug_operands()) + NewOps.push_back(Op.isReg() && Op.getReg() == OldReg + ? MachineOperand::CreateReg(NewReg, false, false, + false, false, false, false, + 0, + /*IsRenamable*/ true) + : 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 +659,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/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,121 @@ +# 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 its stack slot and all +# DW_OP_LLVM_arg operators that reference it have DW_OP_deref inserted after. + +# CHECK-LABEL: bb.0.entry: +# CHECK: DBG_VALUE_LIST !17, !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_arg, 1, DW_OP_deref, DW_OP_plus, DW_OP_stack_value), renamable $r8b, %stack.0 +# CHECK-LABEL: bb.1.if.then: +# CHECK: DBG_VALUE_LIST !18, !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_arg, 1, DW_OP_deref, DW_OP_plus, DW_OP_stack_value), $noreg, %stack.0 + +# Produced from the source: +# void bar(); +# +# int foo(int a, int b, bool c) { +# int d = a + b; +# if (c) { +# int e = d; +# bar(); +# return a; +# } +# return b; +# } + +--- | + ; ModuleID = 'test.ll' + source_filename = "test.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-unknown-linux-gnu" + + ; Function Attrs: uwtable + define dso_local i32 @"?foo@@YAHHH_N@Z"(i32 %a, i32 %b, i1 zeroext %c) local_unnamed_addr !dbg !8 { + entry: + call void @llvm.dbg.value(metadata i1 %c, metadata !14, metadata !DIExpression(DW_OP_LLVM_convert, 1, DW_ATE_unsigned, DW_OP_LLVM_convert, 8, DW_ATE_unsigned, DW_OP_stack_value)), !dbg !21 + call void @llvm.dbg.value(metadata i32 %b, metadata !15, metadata !DIExpression()), !dbg !21 + call void @llvm.dbg.value(metadata i32 %a, metadata !16, metadata !DIExpression()), !dbg !21 + call void @llvm.dbg.value(metadata i32 undef, metadata !17, metadata !DIExpression()), !dbg !21 + br i1 %c, label %if.then, label %cleanup, !dbg !22 + + if.then: ; preds = %entry + call void @llvm.dbg.value(metadata i32 undef, metadata !18, metadata !DIExpression()), !dbg !23 + tail call void @"?bar@@YAXXZ"(), !dbg !24 + br label %cleanup + + cleanup: ; preds = %if.then, %entry + %retval.0 = phi i32 [ %a, %if.then ], [ %b, %entry ], !dbg !21 + ret i32 %retval.0, !dbg !25 + } + + declare dso_local void @"?bar@@YAXXZ"() local_unnamed_addr + + ; Function Attrs: nounwind readnone speculatable willreturn + 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_N@Z", scope: !1, file: !1, line: 3, type: !9, scopeLine: 3, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !13) + !9 = !DISubroutineType(types: !10) + !10 = !{!11, !11, !11, !12} + !11 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) + !12 = !DIBasicType(name: "bool", size: 8, encoding: DW_ATE_boolean) + !13 = !{!14, !15, !16, !17, !18} + !14 = !DILocalVariable(name: "c", arg: 3, scope: !8, file: !1, line: 3, type: !12) + !15 = !DILocalVariable(name: "b", arg: 2, scope: !8, file: !1, line: 3, type: !11) + !16 = !DILocalVariable(name: "a", arg: 1, scope: !8, file: !1, line: 3, type: !11) + !17 = !DILocalVariable(name: "d", scope: !8, file: !1, line: 4, type: !11) + !18 = !DILocalVariable(name: "e", scope: !19, file: !1, line: 6, type: !11) + !19 = distinct !DILexicalBlock(scope: !20, file: !1, line: 5) + !20 = distinct !DILexicalBlock(scope: !8, file: !1, line: 5) + !21 = !DILocation(line: 0, scope: !8) + !22 = !DILocation(line: 5, scope: !8) + !23 = !DILocation(line: 0, scope: !19) + !24 = !DILocation(line: 7, scope: !19) + !25 = !DILocation(line: 11, scope: !8) + +... +--- +name: '?foo@@YAHHH_N@Z' +tracksRegLiveness: true +body: | + bb.0.entry: + successors: %bb.1(0x40000000), %bb.2(0x40000000) + liveins: $ecx, $edx, $r8b + + DBG_VALUE $r8b, $noreg, !14, !DIExpression(DW_OP_LLVM_convert, 1, DW_ATE_unsigned, DW_OP_LLVM_convert, 8, DW_ATE_unsigned, DW_OP_stack_value), debug-location !21 + DBG_VALUE $edx, $noreg, !15, !DIExpression(), debug-location !21 + DBG_VALUE $ecx, $noreg, !16, !DIExpression(), debug-location !21 + %3:gr8 = COPY $r8b + DBG_VALUE %3, $noreg, !14, !DIExpression(DW_OP_LLVM_convert, 1, DW_ATE_unsigned, DW_OP_LLVM_convert, 8, DW_ATE_unsigned, DW_OP_stack_value), debug-location !21 + %4:gr32 = COPY $edx + DBG_VALUE %4, $noreg, !15, !DIExpression(), debug-location !21 + %1:gr32 = COPY $ecx + DBG_VALUE %1, $noreg, !16, !DIExpression(), debug-location !21 + DBG_VALUE_LIST !17, !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_arg, 1, DW_OP_plus, DW_OP_stack_value), %3, %4, debug-location !21 + TEST8rr %3, %3, implicit-def $eflags, debug-location !22 + JCC_1 %bb.2, 4, implicit killed $eflags, debug-location !22 + JMP_1 %bb.1, debug-location !22 + + bb.1.if.then: + successors: %bb.2(0x80000000) + + DBG_VALUE_LIST !18, !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_arg, 1, DW_OP_plus, DW_OP_stack_value), %3, %4, debug-location !23 + ADJCALLSTACKDOWN64 32, 0, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp, debug-location !24 + CALL64pcrel32 @"?bar@@YAXXZ", csr_win64, implicit $rsp, implicit $ssp, implicit-def $rsp, implicit-def $ssp, debug-location !24 + ADJCALLSTACKUP64 32, 0, implicit-def dead $rsp, implicit-def dead $eflags, implicit-def dead $ssp, implicit $rsp, implicit $ssp, debug-location !24 + %4:gr32 = COPY %1, debug-location !21 + + bb.2.cleanup: + $eax = COPY %4, debug-location !25 + RET 0, killed $eax, debug-location !25 + +... 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 + +... +