Skip to content
This repository has been archived by the owner on Apr 23, 2020. It is now read-only.

Commit

Permalink
PR32382: Fix emitting complex DWARF expressions.
Browse files Browse the repository at this point in the history
The DWARF specification knows 3 kinds of non-empty simple location
descriptions:
1. Register location descriptions
  - describe a variable in a register
  - consist of only a DW_OP_reg
2. Memory location descriptions
  - describe the address of a variable
3. Implicit location descriptions
  - describe the value of a variable
  - end with DW_OP_stack_value & friends

The existing DwarfExpression code is pretty much ignorant of these
restrictions. This used to not matter because we only emitted very
short expressions that we happened to get right by accident.  This
patch makes DwarfExpression aware of the rules defined by the DWARF
standard and now chooses the right kind of location description for
each expression being emitted.

This would have been an NFC commit (for the existing testsuite) if not
for the way that clang describes captured block variables. Based on
how the previous code in LLVM emitted locations, DW_OP_deref
operations that should have come at the end of the expression are put
at its beginning. Fixing this means changing the semantics of
DIExpression, so this patch bumps the version number of DIExpression
and implements a bitcode upgrade.

There are two major changes in this patch:

I had to fix the semantics of dbg.declare for describing function
arguments. After this patch a dbg.declare always takes the *address*
of a variable as the first argument, even if the argument is not an
alloca.

When lowering a DBG_VALUE, the decision of whether to emit a register
location description or a memory location description depends on the
MachineLocation — register machine locations may get promoted to
memory locations based on their DIExpression. (Future) optimization
passes that want to salvage implicit debug location for variables may
do so by appending a DW_OP_stack_value. For example:
  DBG_VALUE, [RBP-8]                        --> DW_OP_fbreg -8
  DBG_VALUE, RAX                            --> DW_OP_reg0 +0
  DBG_VALUE, RAX, DIExpression(DW_OP_deref) --> DW_OP_reg0 +0

All testcases that were modified were regenerated from clang. I also
added source-based testcases for each of these to the debuginfo-tests
repository over the last week to make sure that no synchronized bugs
slip in. The debuginfo-tests compile from source and run the debugger.

https://bugs.llvm.org/show_bug.cgi?id=32382
<rdar://problem/31205000>

Differential Revision: https://reviews.llvm.org/D31439

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@300522 91177308-0d34-0410-b5e6-96231b3b80d8
  • Loading branch information
adrian-prantl committed Apr 18, 2017
1 parent cc56be2 commit b560ea7
Show file tree
Hide file tree
Showing 37 changed files with 381 additions and 178 deletions.
17 changes: 11 additions & 6 deletions docs/LangRef.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4380,7 +4380,7 @@ referenced LLVM variable relates to the source language variable.

The current supported vocabulary is limited:

- ``DW_OP_deref`` dereferences the working expression.
- ``DW_OP_deref`` dereferences the top of the expression stack.
- ``DW_OP_plus, 93`` adds ``93`` to the working expression.
- ``DW_OP_LLVM_fragment, 16, 8`` specifies the offset and size (``16`` and ``8``
here, respectively) of the variable fragment from the working expression. Note
Expand All @@ -4396,12 +4396,17 @@ DIExpression nodes that contain a ``DW_OP_stack_value`` operator are standalone
location descriptions that describe constant values. This form is used to
describe global constants that have been optimized away. All other expressions
are modifiers to another location: A debug intrinsic ties a location and a
DIExpression together. Contrary to DWARF expressions, a DIExpression always
describes the *value* of a source variable and never its *address*. In DWARF
terminology, a DIExpression can always be considered an implicit location
description regardless whether it contains a ``DW_OP_stack_value`` or not.
DIExpression together.

.. code-block:: text
DWARF specifies three kinds of simple location descriptions: Register, memory,
and implicit location descriptions. Register and memory location descriptions
describe the *location* of a source variable (in the sense that a debugger might
modify its value), whereas implicit locations describe merely the *value* of a
source variable. DIExpressions also follow this model: A DIExpression that
doesn't have a trailing ``DW_OP_stack_value`` will describe an *address* when
combined with a concrete location.

.. code-block:: llvm
!0 = !DIExpression(DW_OP_deref)
!1 = !DIExpression(DW_OP_plus, 3)
Expand Down
22 changes: 19 additions & 3 deletions docs/SourceLevelDebugging.rst
Original file line number Diff line number Diff line change
Expand Up @@ -180,11 +180,27 @@ provide debug information at various points in generated code.
void @llvm.dbg.declare(metadata, metadata, metadata)
This intrinsic provides information about a local element (e.g., variable).
The first argument is metadata holding the alloca for the variable. The second
This intrinsic provides information about a local element (e.g., variable). The
first argument is metadata holding the alloca for the variable. The second
argument is a `local variable <LangRef.html#dilocalvariable>`_ containing a
description of the variable. The third argument is a `complex expression
<LangRef.html#diexpression>`_.
<LangRef.html#diexpression>`_. An `llvm.dbg.declare` instrinsic describes the
*location* of a source variable.

.. code-block:: llvm
%i.addr = alloca i32, align 4
call void @llvm.dbg.declare(metadata i32* %i.addr, metadata !1, metadata !2), !dbg !3
!1 = !DILocalVariable(name: "i", ...) ; int i
!2 = !DIExpression()
!3 = !DILocation(...)
...
%buffer = alloca [256 x i8], align 8
; The address of i is buffer+64.
call void @llvm.dbg.declare(metadata [256 x i8]* %buffer, metadata !1, metadata !2)
!1 = !DILocalVariable(name: "i", ...) ; int i
!2 = !DIExpression(DW_OP_plus, 64)
``llvm.dbg.value``
^^^^^^^^^^^^^^^^^^
Expand Down
5 changes: 5 additions & 0 deletions include/llvm/CodeGen/MachineInstrBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -413,6 +413,11 @@ MachineInstrBuilder BuildMI(MachineBasicBlock &BB,
unsigned Reg, unsigned Offset,
const MDNode *Variable, const MDNode *Expr);

/// Clone a DBG_VALUE whose value has been spilled to FrameIndex.
MachineInstr *buildDbgValueForSpill(MachineBasicBlock &BB,
MachineBasicBlock::iterator I,
const MachineInstr &Orig, int FrameIndex);

inline unsigned getDefRegState(bool B) {
return B ? RegState::Define : 0;
}
Expand Down
5 changes: 4 additions & 1 deletion include/llvm/IR/DebugInfoMetadata.h
Original file line number Diff line number Diff line change
Expand Up @@ -2232,6 +2232,9 @@ class DIExpression : public MDNode {
expr_op_iterator expr_op_end() const {
return expr_op_iterator(elements_end());
}
iterator_range<expr_op_iterator> expr_ops() const {
return {expr_op_begin(), expr_op_end()};
}
/// @}

bool isValid() const;
Expand All @@ -2240,7 +2243,7 @@ class DIExpression : public MDNode {
return MD->getMetadataID() == DIExpressionKind;
}

/// Is the first element a DW_OP_deref?.
/// Return whether the first element a DW_OP_deref.
bool startsWithDeref() const {
return getNumElements() > 0 && getElement(0) == dwarf::DW_OP_deref;
}
Expand Down
1 change: 1 addition & 0 deletions lib/Bitcode/Reader/BitcodeReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2631,6 +2631,7 @@ Error BitcodeReader::globalCleanup() {

// Look for intrinsic functions which need to be upgraded at some point
for (Function &F : *TheModule) {
MDLoader->upgradeDebugIntrinsics(F);
Function *NewFn;
if (UpgradeIntrinsicFunction(&F, NewFn))
UpgradedIntrinsics[&F] = NewFn;
Expand Down
57 changes: 52 additions & 5 deletions lib/Bitcode/Reader/MetadataLoader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
#include "llvm/IR/Instruction.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/Intrinsics.h"
#include "llvm/IR/IntrinsicInst.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/ModuleSummaryIndex.h"
Expand Down Expand Up @@ -452,6 +453,7 @@ class MetadataLoader::MetadataLoaderImpl {
bool StripTBAA = false;
bool HasSeenOldLoopTags = false;
bool NeedUpgradeToDIGlobalVariableExpression = false;
bool NeedDeclareExpressionUpgrade = false;

/// True if metadata is being parsed for a module being ThinLTO imported.
bool IsImporting = false;
Expand Down Expand Up @@ -511,6 +513,26 @@ class MetadataLoader::MetadataLoaderImpl {
}
}

/// Remove a leading DW_OP_deref from DIExpressions in a dbg.declare that
/// describes a function argument.
void upgradeDeclareExpressions(Function &F) {
if (!NeedDeclareExpressionUpgrade)
return;

for (auto &BB : F)
for (auto &I : BB)
if (auto *DDI = dyn_cast<DbgDeclareInst>(&I))
if (auto *DIExpr = DDI->getExpression())
if (DIExpr->startsWithDeref() &&
dyn_cast_or_null<Argument>(DDI->getAddress())) {
SmallVector<uint64_t, 8> Ops;
Ops.append(std::next(DIExpr->elements_begin()),
DIExpr->elements_end());
auto *E = DIExpression::get(Context, Ops);
DDI->setOperand(2, MetadataAsValue::get(Context, E));
}
}

void upgradeDebugInfo() {
upgradeCUSubprograms();
upgradeCUVariables();
Expand Down Expand Up @@ -565,6 +587,7 @@ class MetadataLoader::MetadataLoaderImpl {

unsigned size() const { return MetadataList.size(); }
void shrinkTo(unsigned N) { MetadataList.shrinkTo(N); }
void upgradeDebugIntrinsics(Function &F) { upgradeDeclareExpressions(F); }
};

static Error error(const Twine &Message) {
Expand Down Expand Up @@ -1520,12 +1543,32 @@ Error MetadataLoader::MetadataLoaderImpl::parseOneMetadata(
return error("Invalid record");

IsDistinct = Record[0] & 1;
bool HasOpFragment = Record[0] & 2;
uint64_t Version = Record[0] >> 1;
auto Elts = MutableArrayRef<uint64_t>(Record).slice(1);
if (!HasOpFragment)
if (unsigned N = Elts.size())
if (N >= 3 && Elts[N - 3] == dwarf::DW_OP_bit_piece)
Elts[N - 3] = dwarf::DW_OP_LLVM_fragment;
unsigned N = Elts.size();
// Perform various upgrades.
switch (Version) {
case 0:
if (N >= 3 && Elts[N - 3] == dwarf::DW_OP_bit_piece)
Elts[N - 3] = dwarf::DW_OP_LLVM_fragment;
LLVM_FALLTHROUGH;
case 1:
// Move DW_OP_deref to the end.
if (N && Elts[0] == dwarf::DW_OP_deref) {
auto End = Elts.end();
if (Elts.size() >= 3 && *std::prev(End, 3) == dwarf::DW_OP_LLVM_fragment)
End = std::prev(End, 3);
std::move(std::next(Elts.begin()), End, Elts.begin());
*std::prev(End) = dwarf::DW_OP_deref;
}
NeedDeclareExpressionUpgrade = true;
LLVM_FALLTHROUGH;
case 2:
// Up-to-date!
break;
default:
return error("Invalid record");
}

MetadataList.assignValue(
GET_OR_DISTINCT(DIExpression, (Context, makeArrayRef(Record).slice(1))),
Expand Down Expand Up @@ -1858,3 +1901,7 @@ bool MetadataLoader::isStrippingTBAA() { return Pimpl->isStrippingTBAA(); }

unsigned MetadataLoader::size() const { return Pimpl->size(); }
void MetadataLoader::shrinkTo(unsigned N) { return Pimpl->shrinkTo(N); }

void MetadataLoader::upgradeDebugIntrinsics(Function &F) {
return Pimpl->upgradeDebugIntrinsics(F);
}
3 changes: 3 additions & 0 deletions lib/Bitcode/Reader/MetadataLoader.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,9 @@ class MetadataLoader {

unsigned size() const;
void shrinkTo(unsigned N);

/// Perform bitcode upgrades on llvm.dbg.* calls.
void upgradeDebugIntrinsics(Function &F);
};
}

Expand Down
5 changes: 2 additions & 3 deletions lib/Bitcode/Writer/BitcodeWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1771,9 +1771,8 @@ void ModuleBitcodeWriter::writeDIExpression(const DIExpression *N,
SmallVectorImpl<uint64_t> &Record,
unsigned Abbrev) {
Record.reserve(N->getElements().size() + 1);

const uint64_t HasOpFragmentFlag = 1 << 1;
Record.push_back((uint64_t)N->isDistinct() | HasOpFragmentFlag);
const uint64_t Version = 2 << 1;
Record.push_back((uint64_t)N->isDistinct() | Version);
Record.append(N->elements_begin(), N->elements_end());

Stream.EmitRecord(bitc::METADATA_EXPRESSION, Record, Abbrev);
Expand Down
12 changes: 6 additions & 6 deletions lib/CodeGen/AsmPrinter/AsmPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -834,17 +834,17 @@ static bool emitDebugValueComment(const MachineInstr *MI, AsmPrinter &AP) {
OS << " <- ";

// The second operand is only an offset if it's an immediate.
bool Deref = MI->getOperand(0).isReg() && MI->getOperand(1).isImm();
int64_t Offset = Deref ? MI->getOperand(1).getImm() : 0;

bool Deref = false;
bool MemLoc = MI->getOperand(0).isReg() && MI->getOperand(1).isImm();
int64_t Offset = MemLoc ? MI->getOperand(1).getImm() : 0;
for (unsigned i = 0; i < Expr->getNumElements(); ++i) {
uint64_t Op = Expr->getElement(i);
if (Op == dwarf::DW_OP_LLVM_fragment) {
// There can't be any operands after this in a valid expression
break;
} else if (Deref) {
// We currently don't support extra Offsets or derefs after the first
// one. Bail out early instead of emitting an incorrect comment
// one. Bail out early instead of emitting an incorrect comment.
OS << " [complex expression]";
AP.OutStreamer->emitRawComment(OS.str());
return true;
Expand Down Expand Up @@ -899,12 +899,12 @@ static bool emitDebugValueComment(const MachineInstr *MI, AsmPrinter &AP) {
AP.OutStreamer->emitRawComment(OS.str());
return true;
}
if (Deref)
if (MemLoc || Deref)
OS << '[';
OS << PrintReg(Reg, AP.MF->getSubtarget().getRegisterInfo());
}

if (Deref)
if (MemLoc || Deref)
OS << '+' << Offset << ']';

// NOTE: Want this comment at start of line, don't emit with AddComment.
Expand Down
25 changes: 12 additions & 13 deletions lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -547,18 +547,19 @@ DIE *DwarfCompileUnit::constructVariableDIEImpl(const DbgVariable &DV,
DIEDwarfExpression DwarfExpr(*Asm, *this, *Loc);
for (auto &Fragment : DV.getFrameIndexExprs()) {
unsigned FrameReg = 0;
const DIExpression *Expr = Fragment.Expr;
const TargetFrameLowering *TFI = Asm->MF->getSubtarget().getFrameLowering();
int Offset = TFI->getFrameIndexReference(*Asm->MF, Fragment.FI, FrameReg);
DwarfExpr.addFragmentOffset(Fragment.Expr);
DwarfExpr.addFragmentOffset(Expr);
SmallVector<uint64_t, 8> Ops;
Ops.push_back(dwarf::DW_OP_plus);
Ops.push_back(Offset);
Ops.push_back(dwarf::DW_OP_deref);
Ops.append(Fragment.Expr->elements_begin(), Fragment.Expr->elements_end());
DIExpressionCursor Expr(Ops);
DwarfExpr.addMachineRegExpression(
*Asm->MF->getSubtarget().getRegisterInfo(), Expr, FrameReg);
DwarfExpr.addExpression(std::move(Expr));
Ops.append(Expr->elements_begin(), Expr->elements_end());
DIExpressionCursor Cursor(Ops);
DwarfExpr.addMachineLocExpression(
*Asm->MF->getSubtarget().getRegisterInfo(), Cursor,
MachineLocation(FrameReg));
DwarfExpr.addExpression(std::move(Cursor));
}
addBlock(*VariableDie, dwarf::DW_AT_location, DwarfExpr.finalize());

Expand Down Expand Up @@ -781,14 +782,13 @@ void DwarfCompileUnit::addAddress(DIE &Die, dwarf::Attribute Attribute,
DIEDwarfExpression DwarfExpr(*Asm, *this, *Loc);

SmallVector<uint64_t, 8> Ops;
if (Location.isIndirect()) {
if (Location.isIndirect() && Location.getOffset()) {
Ops.push_back(dwarf::DW_OP_plus);
Ops.push_back(Location.getOffset());
Ops.push_back(dwarf::DW_OP_deref);
}
DIExpressionCursor Cursor(Ops);
const TargetRegisterInfo &TRI = *Asm->MF->getSubtarget().getRegisterInfo();
if (!DwarfExpr.addMachineRegExpression(TRI, Cursor, Location.getReg()))
if (!DwarfExpr.addMachineLocExpression(TRI, Cursor, Location))
return;
DwarfExpr.addExpression(std::move(Cursor));

Expand All @@ -809,15 +809,14 @@ void DwarfCompileUnit::addComplexAddress(const DbgVariable &DV, DIE &Die,
DwarfExpr.addFragmentOffset(DIExpr);

SmallVector<uint64_t, 8> Ops;
if (Location.isIndirect()) {
if (Location.isIndirect() && Location.getOffset()) {
Ops.push_back(dwarf::DW_OP_plus);
Ops.push_back(Location.getOffset());
Ops.push_back(dwarf::DW_OP_deref);
}
Ops.append(DIExpr->elements_begin(), DIExpr->elements_end());
DIExpressionCursor Cursor(Ops);
const TargetRegisterInfo &TRI = *Asm->MF->getSubtarget().getRegisterInfo();
if (!DwarfExpr.addMachineRegExpression(TRI, Cursor, Location.getReg()))
if (!DwarfExpr.addMachineLocExpression(TRI, Cursor, Location))
return;
DwarfExpr.addExpression(std::move(Cursor));

Expand Down
7 changes: 2 additions & 5 deletions lib/CodeGen/AsmPrinter/DwarfDebug.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1517,18 +1517,15 @@ static void emitDebugLocValue(const AsmPrinter &AP, const DIBasicType *BT,
DwarfExpr.addUnsignedConstant(Value.getInt());
} else if (Value.isLocation()) {
MachineLocation Location = Value.getLoc();

SmallVector<uint64_t, 8> Ops;
// FIXME: Should this condition be Location.isIndirect() instead?
if (Location.getOffset()) {
if (Location.isIndirect() && Location.getOffset()) {
Ops.push_back(dwarf::DW_OP_plus);
Ops.push_back(Location.getOffset());
Ops.push_back(dwarf::DW_OP_deref);
}
Ops.append(DIExpr->elements_begin(), DIExpr->elements_end());
DIExpressionCursor Cursor(Ops);
const TargetRegisterInfo &TRI = *AP.MF->getSubtarget().getRegisterInfo();
if (!DwarfExpr.addMachineRegExpression(TRI, Cursor, Location.getReg()))
if (!DwarfExpr.addMachineLocExpression(TRI, Cursor, Location))
return;
return DwarfExpr.addExpression(std::move(Cursor));
} else if (Value.isConstantFP()) {
Expand Down
Loading

0 comments on commit b560ea7

Please sign in to comment.