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 @@ -918,9 +918,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 << '['; @@ -934,56 +931,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; 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 @@ -333,24 +317,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); @@ -378,17 +382,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. @@ -411,9 +422,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 @@ -34,10 +34,10 @@ } }; -/// A single location or constant. -class DbgValueLoc { - /// Any complex address location expression for this DbgValueLoc. - const DIExpression *Expression; +/// A single location or constant within a variable location description, with +/// either a single entry (with an optional DIExpression) used for a DBG_VALUE, +/// or a list of entries used for a DBG_VALUE_LIST. +class DbgValueLocEntry { /// Type of entry that this represents. enum EntryType { @@ -64,24 +64,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 +87,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 +99,77 @@ 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()) && + "DBG_VALUE with a machine location must have a valid expression."); + } + + 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 +239,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 @@ -728,36 +728,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 @@ -235,29 +235,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) { @@ -645,7 +643,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; @@ -1613,7 +1611,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. @@ -2486,51 +2486,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())) + 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."); + } + return true; + }; + + if (!Value.isVariadic()) { + if (!EmitValueLocEntry(Value.getLocEntries()[0], ExprCursor)) 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); - return; - } - 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)); + + // 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 @@ -347,6 +347,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 @@ -302,6 +302,12 @@ } 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; } @@ -354,6 +360,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 +465,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 +493,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/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 - | FileCheck %s +# +# 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_constu 0xffffffff, DW_OP_and, 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_constu 0xffffffff, DW_OP_and, 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_constu 0xffffffff, DW_OP_and, DW_OP_breg2 RCX+0, DW_OP_constu 0xffffffff, DW_OP_and, 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_constu 0xffffffff, DW_OP_and, DW_OP_breg2 RCX+0, DW_OP_constu 0xffffffff, DW_OP_and, 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_constu 0xffffffff, DW_OP_and, DW_OP_stack_value, DW_OP_piece 0x2, DW_OP_breg2 RCX+0, DW_OP_constu 0xffffffff, DW_OP_and, 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_constu 0xffffffff, DW_OP_and, DW_OP_stack_value, DW_OP_piece 0x2, DW_OP_breg5 RDI+0, DW_OP_constu 0xffffffff, DW_OP_and, 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_constu 0xffffffff, DW_OP_and, DW_OP_stack_value, DW_OP_piece 0x2, DW_OP_breg0 RAX+0, DW_OP_constu 0xffffffff, DW_OP_and, DW_OP_breg2 RCX+0, DW_OP_constu 0xffffffff, DW_OP_and, 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,101 @@ +# RUN: llc %s --start-after=livedebugvalues -filetype=obj -o - \ +# RUN: | llvm-dwarfdump - -name local* -regex \ +# RUN: | FileCheck %s +# +# Test that we produce correct 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) + !30 = !DILocalVariable(name: "localh", 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") + + ; (8) Check that a single $noreg location invalidates the entire entry. + DBG_VALUE_LIST !30, !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_arg, 1, DW_OP_plus, DW_OP_stack_value), $eax, $noreg, debug-location !15 + ; CHECK: DW_TAG_variable + ; CHECK-NEXT: DW_AT_name ("localh") + ; CHECK-NOT: DW_AT_location + + RETQ debug-location !15 +...