[llvm] 0da27ba - [DebugInfo] Add DWARF emission for DBG_VALUE_LIST
Stephen Tozer via llvm-commits
llvm-commits at lists.llvm.org
Wed Mar 10 05:46:52 PST 2021
Author: gbtozers
Date: 2021-03-10T13:46:20Z
New Revision: 0da27ba56c9f5e3f534a65401962301189eac342
URL: https://github.com/llvm/llvm-project/commit/0da27ba56c9f5e3f534a65401962301189eac342
DIFF: https://github.com/llvm/llvm-project/commit/0da27ba56c9f5e3f534a65401962301189eac342.diff
LOG: [DebugInfo] Add DWARF emission for DBG_VALUE_LIST
This patch allows DBG_VALUE_LIST instructions to be emitted to DWARF with valid
DW_AT_locations. This change mainly affects DbgEntityHistoryCalculator, which
now tracks multiple registers per value, and DwarfDebug+DwarfExpression, which
can now emit multiple machine locations as part of a DWARF expression.
Differential Revision: https://reviews.llvm.org/D83495
Added:
llvm/test/DebugInfo/X86/dbg_value_list_clobbers.mir
llvm/test/DebugInfo/X86/dbg_value_list_emission.mir
Modified:
llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
llvm/lib/CodeGen/AsmPrinter/DbgEntityHistoryCalculator.cpp
llvm/lib/CodeGen/AsmPrinter/DebugHandlerBase.cpp
llvm/lib/CodeGen/AsmPrinter/DebugLocEntry.h
llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp
llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
llvm/lib/CodeGen/AsmPrinter/DwarfExpression.cpp
llvm/lib/CodeGen/AsmPrinter/DwarfExpression.h
Removed:
################################################################################
diff --git a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
index dbe7028b1a48..d55747691ec5 100644
--- a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
@@ -918,9 +918,6 @@ static bool emitDebugValueComment(const MachineInstr *MI, AsmPrinter &AP) {
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 @@ static bool emitDebugValueComment(const MachineInstr *MI, AsmPrinter &AP) {
}
// 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<StackOffset> 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
index a30720cc20ff..bb24f1414ef1 100644
--- a/llvm/lib/CodeGen/AsmPrinter/DbgEntityHistoryCalculator.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/DbgEntityHistoryCalculator.cpp
@@ -37,22 +37,6 @@ namespace {
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 @@ static void addRegDescribedVar(RegDescribedVarsMap &RegVars, unsigned RegNo,
}
/// 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<Register> &FellowRegisters) {
EntryIndex ClobberIndex = HistMap.startClobber(Var, ClobberingInstr);
-
// Close all entries whose values are described by the register.
SmallVector<EntryIndex, 4> 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<Register, 4> MaybeRemovedRegisters;
+ SmallSet<Register, 4> 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 @@ static void handleNewDebugValue(InlinedEntity Var, const MachineInstr &DV,
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 @@ static void clobberRegisterUses(RegDescribedVarsMap &RegVars,
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<Register, 4> 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
index 68a4bfba42a7..b9a9e1cfaee0 100644
--- a/llvm/lib/CodeGen/AsmPrinter/DebugHandlerBase.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/DebugHandlerBase.cpp
@@ -35,7 +35,8 @@ Optional<DbgVariableLocation>
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 @@ DbgVariableLocation::extractFromMachineInstruction(
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 @@ void DebugHandlerBase::beginFunction(const MachineFunction *MF) {
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
index 36278f2e9e2d..525b839d67a0 100644
--- a/llvm/lib/CodeGen/AsmPrinter/DebugLocEntry.h
+++ b/llvm/lib/CodeGen/AsmPrinter/DebugLocEntry.h
@@ -34,10 +34,10 @@ struct TargetIndexLocation {
}
};
-/// 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 @@ class DbgValueLoc {
};
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<DIExpression>(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 @@ class DbgValueLoc {
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 @@ class DbgValueLoc {
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<DbgValueLocEntry, 2> ValueLocEntries;
+
+ bool IsVariadic;
+
+public:
+ DbgValueLoc(const DIExpression *Expr, ArrayRef<DbgValueLocEntry> 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<DbgValueLocEntry> Locs,
+ bool IsVariadic)
+ : Expression(Expr), ValueLocEntries(Locs.begin(), Locs.end()),
+ IsVariadic(IsVariadic) {
+#ifndef NDEBUG
+ assert(cast<DIExpression>(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<DbgValueLocEntry> 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 @@ class DebugLocEntry {
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
index 3d378df287ac..cba36ab584e9 100644
--- a/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp
@@ -728,36 +728,95 @@ DIE *DwarfCompileUnit::constructVariableDIEImpl(const DbgVariable &DV,
// 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<DIBasicType>(
+ static_cast<const Metadata *>(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<DIBasicType>(
- static_cast<const Metadata *>(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<uint64_t>(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
index beed2a5126c4..9e20527b2ac3 100644
--- a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
@@ -235,29 +235,27 @@ const DIType *DbgVariable::getType() const {
/// 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<DbgValueLocEntry, 4> 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 @@ static void finishCallSiteParams(ValT Val, const DIExpression *Expr,
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 @@ static bool validThroughout(LexicalScopes &LScopes,
// 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 @@ void DwarfDebug::emitDebugLocValue(const AsmPrinter &AP, const DIBasicType *BT,
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<uint64_t>(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<uint64_t>(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.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfExpression.cpp
index ef61e2e55e18..8dc260130f46 100644
--- a/llvm/lib/CodeGen/AsmPrinter/DwarfExpression.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/DwarfExpression.cpp
@@ -302,6 +302,12 @@ bool DwarfExpression::addMachineRegExpression(const TargetRegisterInfo &TRI,
}
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 @@ bool DwarfExpression::addMachineRegExpression(const TargetRegisterInfo &TRI,
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 @@ static bool isMemoryLocation(DIExpressionCursor ExprCursor) {
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<bool(unsigned, DIExpressionCursor &)> 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<DIExpression::ExprOperand> PrevConvertOp = None;
while (ExprCursor) {
@@ -476,6 +493,12 @@ void DwarfExpression::addExpression(DIExpressionCursor &&ExprCursor,
}
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/AsmPrinter/DwarfExpression.h b/llvm/lib/CodeGen/AsmPrinter/DwarfExpression.h
index 0d55144f9827..2e5edf18b38d 100644
--- a/llvm/lib/CodeGen/AsmPrinter/DwarfExpression.h
+++ b/llvm/lib/CodeGen/AsmPrinter/DwarfExpression.h
@@ -347,6 +347,9 @@ class DwarfExpression {
/// fragment inside the entire variable.
void addExpression(DIExpressionCursor &&Expr,
unsigned FragmentOffsetInBits = 0);
+ void
+ addExpression(DIExpressionCursor &&Expr,
+ std::function<bool(unsigned, DIExpressionCursor &)> 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/test/DebugInfo/X86/dbg_value_list_clobbers.mir b/llvm/test/DebugInfo/X86/dbg_value_list_clobbers.mir
new file mode 100644
index 000000000000..601067a80d01
--- /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
index 000000000000..e3380cb42a4b
--- /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
+...
More information about the llvm-commits
mailing list