[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
This commit is contained in:
gbtozers 2020-09-11 15:48:39 +01:00 committed by Stephen Tozer
parent e02dd790b1
commit 0da27ba56c
10 changed files with 622 additions and 206 deletions

View File

@ -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;
}
if (Reg == 0) {
// Suppress offset, it is not meaningful here.
OS << "undef";
case MachineOperand::MO_CImmediate: {
Op.getCImm()->getValue().print(OS, false /*isSigned*/);
break;
}
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;

View File

@ -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);
}

View File

@ -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,

View File

@ -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 @@ public:
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 @@ public:
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 @@ public:
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) {

View File

@ -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;
}

View File

@ -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);
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!");
}
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!");
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();
DwarfExpr.setLocation(Location, DIExpr);
DIExpressionCursor Cursor(DIExpr);
if (DIExpr->isEntryValue())
DwarfExpr.beginEntryValueExpression(Cursor);
// 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);
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,

View File

@ -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);

View File

@ -347,6 +347,9 @@ public:
/// 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.

View File

@ -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
...

View File

@ -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
...