forked from OSchip/llvm-project
[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:
parent
e02dd790b1
commit
0da27ba56c
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
...
|
|
@ -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
|
||||
...
|
Loading…
Reference in New Issue