summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorReid Kleckner <rnk@google.com>2017-09-21 19:52:03 +0000
committerReid Kleckner <rnk@google.com>2017-09-21 19:52:03 +0000
commit0e1ce271574ac0f9097000a08f13736c45352ced (patch)
tree9837a72ed491a1f347b31cf0724d3dd0ce97a749
parent1efe4ef3109d9ce19fd44e19ab47ca7fa61d4d20 (diff)
Re-land r313825: "[IR] Add llvm.dbg.addr, a control-dependent version of llvm.dbg.declare"
The fix is to avoid invalidating our insertion point in replaceDbgDeclare: Builder.insertDeclare(NewAddress, DIVar, DIExpr, Loc, InsertBefore); + if (DII == InsertBefore) + InsertBefore = &*std::next(InsertBefore->getIterator()); DII->eraseFromParent(); I had to write a unit tests for this instead of a lit test because the use list order matters in order to trigger the bug. The reduced C test case for this was: void useit(int*); static inline void inlineme() { int x[2]; useit(x); } void f() { inlineme(); inlineme(); } git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@313905 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r--docs/SourceLevelDebugging.rst62
-rw-r--r--include/llvm/IR/IntrinsicInst.h34
-rw-r--r--include/llvm/IR/Intrinsics.td8
-rw-r--r--include/llvm/Transforms/Utils/Local.h22
-rw-r--r--lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp48
-rw-r--r--lib/IR/DIBuilder.cpp14
-rw-r--r--lib/IR/Verifier.cpp2
-rw-r--r--lib/Transforms/InstCombine/InstructionCombining.cpp13
-rw-r--r--lib/Transforms/Scalar/SROA.cpp30
-rw-r--r--lib/Transforms/Utils/Local.cpp93
-rw-r--r--lib/Transforms/Utils/PromoteMemoryToRegister.cpp44
-rw-r--r--test/DebugInfo/X86/dbg-addr-dse.ll100
-rw-r--r--test/DebugInfo/X86/dbg-addr.ll67
-rw-r--r--test/DebugInfo/X86/sroasplit-5.ll4
-rw-r--r--test/DebugInfo/X86/sroasplit-dbg-declare.ll59
-rw-r--r--test/Transforms/Mem2Reg/dbg-addr-inline-dse.ll94
-rw-r--r--test/Transforms/Mem2Reg/dbg-addr.ll91
-rw-r--r--test/Transforms/SROA/dbg-addr-diamond.ll127
-rw-r--r--unittests/Transforms/Utils/CMakeLists.txt1
-rw-r--r--unittests/Transforms/Utils/Local.cpp71
20 files changed, 850 insertions, 134 deletions
diff --git a/docs/SourceLevelDebugging.rst b/docs/SourceLevelDebugging.rst
index ee4c5ce8bce..c46b51c4d81 100644
--- a/docs/SourceLevelDebugging.rst
+++ b/docs/SourceLevelDebugging.rst
@@ -171,35 +171,64 @@ Debugger intrinsic functions
----------------------------
LLVM uses several intrinsic functions (name prefixed with "``llvm.dbg``") to
-provide debug information at various points in generated code.
+track source local variables through optimization and code generation.
-``llvm.dbg.declare``
+``llvm.dbg.addr``
^^^^^^^^^^^^^^^^^^^^
.. code-block:: llvm
- void @llvm.dbg.declare(metadata, metadata, metadata)
+ void @llvm.dbg.addr(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
-argument is a `local variable <LangRef.html#dilocalvariable>`_ containing a
-description of the variable. The third argument is a `complex expression
-<LangRef.html#diexpression>`_. An `llvm.dbg.declare` instrinsic describes the
-*location* of a source variable.
+This intrinsic provides information about a local element (e.g., variable).
+The first argument is metadata holding the address of variable, typically a
+static alloca in the function entry block. 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>`_. An `llvm.dbg.addr` intrinsic describes the
+*address* 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
+ call void @llvm.dbg.addr(metadata i32* %i.addr, metadata !1,
+ metadata !DIExpression()), !dbg !2
!1 = !DILocalVariable(name: "i", ...) ; int i
- !2 = !DIExpression()
- !3 = !DILocation(...)
+ !2 = !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)
+ call void @llvm.dbg.addr(metadata [256 x i8]* %buffer, metadata !3,
+ metadata !DIExpression(DW_OP_plus, 64)), !dbg !4
+ !3 = !DILocalVariable(name: "i", ...) ; int i
+ !4 = !DILocation(...)
+
+A frontend should generate exactly one call to ``llvm.dbg.addr`` at the point
+of declaration of a source variable. Optimization passes that fully promote the
+variable from memory to SSA values will replace this call with possibly
+multiple calls to `llvm.dbg.value`. Passes that delete stores are effectively
+partial promotion, and they will insert a mix of calls to ``llvm.dbg.value``
+and ``llvm.dbg.addr`` to track the source variable value when it is available.
+After optimization, there may be multiple calls to ``llvm.dbg.addr`` describing
+the program points where the variables lives in memory. All calls for the same
+concrete source variable must agree on the memory location.
+
+
+``llvm.dbg.declare``
+^^^^^^^^^^^^^^^^^^^^
+
+.. code-block:: llvm
+
+ void @llvm.dbg.declare(metadata, metadata, metadata)
+
+This intrinsic is identical to `llvm.dbg.addr`, except that there can only be
+one call to `llvm.dbg.declare` for a given concrete `local variable
+<LangRef.html#dilocalvariable>`_. It is not control-dependent, meaning that if
+a call to `llvm.dbg.declare` exists and has a valid location argument, that
+address is considered to be the true home of the variable across its entire
+lifetime. This makes it hard for optimizations to preserve accurate debug info
+in the presence of ``llvm.dbg.declare``, so we are transitioning away from it,
+and we plan to deprecate it in future LLVM releases.
``llvm.dbg.value``
@@ -242,6 +271,9 @@ following C fragment, for example:
8. X = Y;
9. }
+.. FIXME: Update the following example to use llvm.dbg.addr once that is the
+ default in clang.
+
Compiled to LLVM, this function would be represented like this:
.. code-block:: text
diff --git a/include/llvm/IR/IntrinsicInst.h b/include/llvm/IR/IntrinsicInst.h
index eccb4080932..b8d6e23907c 100644
--- a/include/llvm/IR/IntrinsicInst.h
+++ b/include/llvm/IR/IntrinsicInst.h
@@ -71,6 +71,12 @@ namespace llvm {
/// variable's value or its address.
Value *getVariableLocation(bool AllowNullOp = true) const;
+ /// Does this describe the address of a local variable. True for dbg.addr
+ /// and dbg.declare, but not dbg.value, which describes its value.
+ bool isAddressOfVariable() const {
+ return getIntrinsicID() != Intrinsic::dbg_value;
+ }
+
DILocalVariable *getVariable() const {
return cast<DILocalVariable>(getRawVariable());
}
@@ -87,11 +93,13 @@ namespace llvm {
return cast<MetadataAsValue>(getArgOperand(2))->getMetadata();
}
- // Methods for support type inquiry through isa, cast, and dyn_cast:
+ /// \name Casting methods
+ /// @{
static bool classof(const IntrinsicInst *I) {
switch (I->getIntrinsicID()) {
case Intrinsic::dbg_declare:
case Intrinsic::dbg_value:
+ case Intrinsic::dbg_addr:
return true;
default: return false;
}
@@ -99,6 +107,7 @@ namespace llvm {
static bool classof(const Value *V) {
return isa<IntrinsicInst>(V) && classof(cast<IntrinsicInst>(V));
}
+ /// @}
};
/// This represents the llvm.dbg.declare instruction.
@@ -106,13 +115,30 @@ namespace llvm {
public:
Value *getAddress() const { return getVariableLocation(); }
- // Methods for support type inquiry through isa, cast, and dyn_cast:
+ /// \name Casting methods
+ /// @{
static bool classof(const IntrinsicInst *I) {
return I->getIntrinsicID() == Intrinsic::dbg_declare;
}
static bool classof(const Value *V) {
return isa<IntrinsicInst>(V) && classof(cast<IntrinsicInst>(V));
}
+ /// @}
+ };
+
+ /// This represents the llvm.dbg.addr instruction.
+ class DbgAddrIntrinsic : public DbgInfoIntrinsic {
+ public:
+ Value *getAddress() const { return getVariableLocation(); }
+
+ /// \name Casting methods
+ /// @{
+ static bool classof(const IntrinsicInst *I) {
+ return I->getIntrinsicID() == Intrinsic::dbg_addr;
+ }
+ static bool classof(const Value *V) {
+ return isa<IntrinsicInst>(V) && classof(cast<IntrinsicInst>(V));
+ }
};
/// This represents the llvm.dbg.value instruction.
@@ -122,13 +148,15 @@ namespace llvm {
return getVariableLocation(/* AllowNullOp = */ false);
}
- // Methods for support type inquiry through isa, cast, and dyn_cast:
+ /// \name Casting methods
+ /// @{
static bool classof(const IntrinsicInst *I) {
return I->getIntrinsicID() == Intrinsic::dbg_value;
}
static bool classof(const Value *V) {
return isa<IntrinsicInst>(V) && classof(cast<IntrinsicInst>(V));
}
+ /// @}
};
/// This is the common base class for constrained floating point intrinsics.
diff --git a/include/llvm/IR/Intrinsics.td b/include/llvm/IR/Intrinsics.td
index 6ced600ddc0..cb16c3d4849 100644
--- a/include/llvm/IR/Intrinsics.td
+++ b/include/llvm/IR/Intrinsics.td
@@ -583,12 +583,16 @@ let IntrProperties = [IntrNoMem, IntrSpeculatable] in {
let IntrProperties = [IntrNoMem, IntrSpeculatable] in {
def int_dbg_declare : Intrinsic<[],
[llvm_metadata_ty,
- llvm_metadata_ty,
- llvm_metadata_ty]>;
+ llvm_metadata_ty,
+ llvm_metadata_ty]>;
def int_dbg_value : Intrinsic<[],
[llvm_metadata_ty,
llvm_metadata_ty,
llvm_metadata_ty]>;
+ def int_dbg_addr : Intrinsic<[],
+ [llvm_metadata_ty,
+ llvm_metadata_ty,
+ llvm_metadata_ty]>;
}
//===------------------ Exception Handling Intrinsics----------------------===//
diff --git a/include/llvm/Transforms/Utils/Local.h b/include/llvm/Transforms/Utils/Local.h
index 42c2ec80beb..9816e7fecf2 100644
--- a/include/llvm/Transforms/Utils/Local.h
+++ b/include/llvm/Transforms/Utils/Local.h
@@ -16,6 +16,7 @@
#define LLVM_TRANSFORMS_UTILS_LOCAL_H
#include "llvm/ADT/SmallPtrSet.h"
+#include "llvm/ADT/TinyPtrVector.h"
#include "llvm/Analysis/AliasAnalysis.h"
#include "llvm/IR/DataLayout.h"
#include "llvm/IR/Dominators.h"
@@ -32,6 +33,7 @@ class BranchInst;
class Instruction;
class CallInst;
class DbgDeclareInst;
+class DbgInfoIntrinsic;
class DbgValueInst;
class StoreInst;
class LoadInst;
@@ -262,26 +264,28 @@ Value *EmitGEPOffset(IRBuilderTy *Builder, const DataLayout &DL, User *GEP,
///
/// Inserts a llvm.dbg.value intrinsic before a store to an alloca'd value
-/// that has an associated llvm.dbg.decl intrinsic.
-void ConvertDebugDeclareToDebugValue(DbgDeclareInst *DDI,
+/// that has an associated llvm.dbg.declare or llvm.dbg.addr intrinsic.
+void ConvertDebugDeclareToDebugValue(DbgInfoIntrinsic *DII,
StoreInst *SI, DIBuilder &Builder);
/// Inserts a llvm.dbg.value intrinsic before a load of an alloca'd value
-/// that has an associated llvm.dbg.decl intrinsic.
-void ConvertDebugDeclareToDebugValue(DbgDeclareInst *DDI,
+/// that has an associated llvm.dbg.declare or llvm.dbg.addr intrinsic.
+void ConvertDebugDeclareToDebugValue(DbgInfoIntrinsic *DII,
LoadInst *LI, DIBuilder &Builder);
-/// Inserts a llvm.dbg.value intrinsic after a phi of an alloca'd value
-/// that has an associated llvm.dbg.decl intrinsic.
-void ConvertDebugDeclareToDebugValue(DbgDeclareInst *DDI,
+/// Inserts a llvm.dbg.value intrinsic after a phi that has an associated
+/// llvm.dbg.declare or llvm.dbg.addr intrinsic.
+void ConvertDebugDeclareToDebugValue(DbgInfoIntrinsic *DII,
PHINode *LI, DIBuilder &Builder);
/// Lowers llvm.dbg.declare intrinsics into appropriate set of
/// llvm.dbg.value intrinsics.
bool LowerDbgDeclare(Function &F);
-/// Finds the llvm.dbg.declare intrinsic corresponding to an alloca, if any.
-DbgDeclareInst *FindAllocaDbgDeclare(Value *V);
+/// Finds all intrinsics declaring local variables as living in the memory that
+/// 'V' points to. This may include a mix of dbg.declare and
+/// dbg.addr intrinsics.
+TinyPtrVector<DbgInfoIntrinsic *> FindDbgAddrUses(Value *V);
/// Finds the llvm.dbg.value intrinsics describing a value.
void findDbgValues(SmallVectorImpl<DbgValueInst *> &DbgValues, Value *V);
diff --git a/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
index dda3fd92715..b103792d00c 100644
--- a/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
+++ b/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
@@ -5109,37 +5109,48 @@ SelectionDAGBuilder::visitIntrinsicCall(const CallInst &I, unsigned Intrinsic) {
DAG.setRoot(CallResult.second);
return nullptr;
}
+ case Intrinsic::dbg_addr:
case Intrinsic::dbg_declare: {
- const DbgDeclareInst &DI = cast<DbgDeclareInst>(I);
+ const DbgInfoIntrinsic &DI = cast<DbgInfoIntrinsic>(I);
DILocalVariable *Variable = DI.getVariable();
DIExpression *Expression = DI.getExpression();
- const Value *Address = DI.getAddress();
assert(Variable && "Missing variable");
- if (!Address) {
- DEBUG(dbgs() << "Dropping debug info for " << DI << "\n");
- return nullptr;
- }
// Check if address has undef value.
- if (isa<UndefValue>(Address) ||
+ const Value *Address = DI.getVariableLocation();
+ if (!Address || isa<UndefValue>(Address) ||
(Address->use_empty() && !isa<Argument>(Address))) {
DEBUG(dbgs() << "Dropping debug info for " << DI << "\n");
return nullptr;
}
- // Static allocas are handled more efficiently in the variable frame index
- // side table.
+ bool isParameter = Variable->isParameter() || isa<Argument>(Address);
+
+ // Check if this variable can be described by a frame index, typically
+ // either as a static alloca or a byval parameter.
+ int FI = INT_MAX;
if (const auto *AI =
- dyn_cast<AllocaInst>(Address->stripInBoundsConstantOffsets()))
- if (AI->isStaticAlloca() && FuncInfo.StaticAllocaMap.count(AI))
- return nullptr;
+ dyn_cast<AllocaInst>(Address->stripInBoundsConstantOffsets())) {
+ if (AI->isStaticAlloca()) {
+ auto I = FuncInfo.StaticAllocaMap.find(AI);
+ if (I != FuncInfo.StaticAllocaMap.end())
+ FI = I->second;
+ }
+ } else if (const auto *Arg = dyn_cast<Argument>(
+ Address->stripInBoundsConstantOffsets())) {
+ FI = FuncInfo.getArgumentFrameIndex(Arg);
+ }
- // Byval arguments with frame indices were already handled after argument
- // lowering and before isel.
- if (const auto *Arg =
- dyn_cast<Argument>(Address->stripInBoundsConstantOffsets()))
- if (FuncInfo.getArgumentFrameIndex(Arg) != INT_MAX)
- return nullptr;
+ // llvm.dbg.addr is control dependent and always generates indirect
+ // DBG_VALUE instructions. llvm.dbg.declare is handled as a frame index in
+ // the MachineFunction variable table.
+ if (FI != INT_MAX) {
+ if (Intrinsic == Intrinsic::dbg_addr)
+ DAG.AddDbgValue(DAG.getFrameIndexDbgValue(Variable, Expression, FI, dl,
+ SDNodeOrder),
+ getRoot().getNode(), isParameter);
+ return nullptr;
+ }
SDValue &N = NodeMap[Address];
if (!N.getNode() && isa<Argument>(Address))
@@ -5150,7 +5161,6 @@ SelectionDAGBuilder::visitIntrinsicCall(const CallInst &I, unsigned Intrinsic) {
if (const BitCastInst *BCI = dyn_cast<BitCastInst>(Address))
Address = BCI->getOperand(0);
// Parameters are handled specially.
- bool isParameter = Variable->isParameter() || isa<Argument>(Address);
auto FINode = dyn_cast<FrameIndexSDNode>(N.getNode());
if (isParameter && FINode) {
// Byval parameter. We have a frame index at this point.
diff --git a/lib/IR/DIBuilder.cpp b/lib/IR/DIBuilder.cpp
index 1a40d5ce13d..88f5b36dd58 100644
--- a/lib/IR/DIBuilder.cpp
+++ b/lib/IR/DIBuilder.cpp
@@ -24,6 +24,11 @@
using namespace llvm;
using namespace llvm::dwarf;
+cl::opt<bool>
+ UseDbgAddr("use-dbg-addr",
+ llvm::cl::desc("Use llvm.dbg.addr for all local variables"),
+ cl::init(false));
+
DIBuilder::DIBuilder(Module &m, bool AllowUnresolvedNodes)
: M(m), VMContext(M.getContext()), CUNode(nullptr),
DeclareFn(nullptr), ValueFn(nullptr),
@@ -776,6 +781,11 @@ static Instruction *withDebugLoc(Instruction *I, const DILocation *DL) {
return I;
}
+static Function *getDeclareIntrin(Module &M) {
+ return Intrinsic::getDeclaration(&M, UseDbgAddr ? Intrinsic::dbg_addr
+ : Intrinsic::dbg_declare);
+}
+
Instruction *DIBuilder::insertDeclare(Value *Storage, DILocalVariable *VarInfo,
DIExpression *Expr, const DILocation *DL,
Instruction *InsertBefore) {
@@ -785,7 +795,7 @@ Instruction *DIBuilder::insertDeclare(Value *Storage, DILocalVariable *VarInfo,
VarInfo->getScope()->getSubprogram() &&
"Expected matching subprograms");
if (!DeclareFn)
- DeclareFn = Intrinsic::getDeclaration(&M, Intrinsic::dbg_declare);
+ DeclareFn = getDeclareIntrin(M);
trackIfUnresolved(VarInfo);
trackIfUnresolved(Expr);
@@ -804,7 +814,7 @@ Instruction *DIBuilder::insertDeclare(Value *Storage, DILocalVariable *VarInfo,
VarInfo->getScope()->getSubprogram() &&
"Expected matching subprograms");
if (!DeclareFn)
- DeclareFn = Intrinsic::getDeclaration(&M, Intrinsic::dbg_declare);
+ DeclareFn = getDeclareIntrin(M);
trackIfUnresolved(VarInfo);
trackIfUnresolved(Expr);
diff --git a/lib/IR/Verifier.cpp b/lib/IR/Verifier.cpp
index c875e302188..9f671e41a18 100644
--- a/lib/IR/Verifier.cpp
+++ b/lib/IR/Verifier.cpp
@@ -4001,6 +4001,8 @@ void Verifier::visitIntrinsicCallSite(Intrinsic::ID ID, CallSite CS) {
"invalid llvm.dbg.declare intrinsic call 1", CS);
visitDbgIntrinsic("declare", cast<DbgInfoIntrinsic>(*CS.getInstruction()));
break;
+ case Intrinsic::dbg_addr: // llvm.dbg.addr
+ visitDbgIntrinsic("addr", cast<DbgInfoIntrinsic>(*CS.getInstruction()));
case Intrinsic::dbg_value: // llvm.dbg.value
visitDbgIntrinsic("value", cast<DbgInfoIntrinsic>(*CS.getInstruction()));
break;
diff --git a/lib/Transforms/InstCombine/InstructionCombining.cpp b/lib/Transforms/InstCombine/InstructionCombining.cpp
index f51b8381445..60db2ddb63c 100644
--- a/lib/Transforms/InstCombine/InstructionCombining.cpp
+++ b/lib/Transforms/InstCombine/InstructionCombining.cpp
@@ -2106,10 +2106,10 @@ Instruction *InstCombiner::visitAllocSite(Instruction &MI) {
// If we are removing an alloca with a dbg.declare, insert dbg.value calls
// before each store.
- DbgDeclareInst *DDI = nullptr;
+ TinyPtrVector<DbgInfoIntrinsic *> DIIs;
std::unique_ptr<DIBuilder> DIB;
if (isa<AllocaInst>(MI)) {
- DDI = FindAllocaDbgDeclare(&MI);
+ DIIs = FindDbgAddrUses(&MI);
DIB.reset(new DIBuilder(*MI.getModule(), /*AllowUnresolved=*/false));
}
@@ -2145,8 +2145,9 @@ Instruction *InstCombiner::visitAllocSite(Instruction &MI) {
} else if (isa<BitCastInst>(I) || isa<GetElementPtrInst>(I) ||
isa<AddrSpaceCastInst>(I)) {
replaceInstUsesWith(*I, UndefValue::get(I->getType()));
- } else if (DDI && isa<StoreInst>(I)) {
- ConvertDebugDeclareToDebugValue(DDI, cast<StoreInst>(I), *DIB);
+ } else if (auto *SI = dyn_cast<StoreInst>(I)) {
+ for (auto *DII : DIIs)
+ ConvertDebugDeclareToDebugValue(DII, SI, *DIB);
}
eraseInstFromFunction(*I);
}
@@ -2159,8 +2160,8 @@ Instruction *InstCombiner::visitAllocSite(Instruction &MI) {
None, "", II->getParent());
}
- if (DDI)
- eraseInstFromFunction(*DDI);
+ for (auto *DII : DIIs)
+ eraseInstFromFunction(*DII);
return eraseInstFromFunction(MI);
}
diff --git a/lib/Transforms/Scalar/SROA.cpp b/lib/Transforms/Scalar/SROA.cpp
index c96606af6bb..b968cb8c892 100644
--- a/lib/Transforms/Scalar/SROA.cpp
+++ b/lib/Transforms/Scalar/SROA.cpp
@@ -4102,9 +4102,10 @@ bool SROA::splitAlloca(AllocaInst &AI, AllocaSlices &AS) {
// Migrate debug information from the old alloca to the new alloca(s)
// and the individual partitions.
- if (DbgDeclareInst *DbgDecl = FindAllocaDbgDeclare(&AI)) {
- auto *Var = DbgDecl->getVariable();
- auto *Expr = DbgDecl->getExpression();
+ TinyPtrVector<DbgInfoIntrinsic *> DbgDeclares = FindDbgAddrUses(&AI);
+ if (!DbgDeclares.empty()) {
+ auto *Var = DbgDeclares.front()->getVariable();
+ auto *Expr = DbgDeclares.front()->getExpression();
DIBuilder DIB(*AI.getModule(), /*AllowUnresolved*/ false);
uint64_t AllocaSize = DL.getTypeSizeInBits(AI.getAllocatedType());
for (auto Fragment : Fragments) {
@@ -4136,12 +4137,12 @@ bool SROA::splitAlloca(AllocaInst &AI, AllocaSlices &AS) {
DIExpression::createFragmentExpression(Expr, Start, Size);
}
- // Remove any existing dbg.declare intrinsic describing the same alloca.
- if (DbgDeclareInst *OldDDI = FindAllocaDbgDeclare(Fragment.Alloca))
- OldDDI->eraseFromParent();
+ // Remove any existing intrinsics describing the same alloca.
+ for (DbgInfoIntrinsic *OldDII : FindDbgAddrUses(Fragment.Alloca))
+ OldDII->eraseFromParent();
DIB.insertDeclare(Fragment.Alloca, Var, FragmentExpr,
- DbgDecl->getDebugLoc(), &AI);
+ DbgDeclares.front()->getDebugLoc(), &AI);
}
}
return Changed;
@@ -4246,6 +4247,15 @@ void SROA::deleteDeadInstructions(
Instruction *I = DeadInsts.pop_back_val();
DEBUG(dbgs() << "Deleting dead instruction: " << *I << "\n");
+ // If the instruction is an alloca, find the possible dbg.declare connected
+ // to it, and remove it too. We must do this before calling RAUW or we will
+ // not be able to find it.
+ if (AllocaInst *AI = dyn_cast<AllocaInst>(I)) {
+ DeletedAllocas.insert(AI);
+ for (DbgInfoIntrinsic *OldDII : FindDbgAddrUses(AI))
+ OldDII->eraseFromParent();
+ }
+
I->replaceAllUsesWith(UndefValue::get(I->getType()));
for (Use &Operand : I->operands())
@@ -4256,12 +4266,6 @@ void SROA::deleteDeadInstructions(
DeadInsts.insert(U);
}
- if (AllocaInst *AI = dyn_cast<AllocaInst>(I)) {
- DeletedAllocas.insert(AI);
- if (DbgDeclareInst *DbgDecl = FindAllocaDbgDeclare(AI))
- DbgDecl->eraseFromParent();
- }
-
++NumDeleted;
I->eraseFromParent();
}
diff --git a/lib/Transforms/Utils/Local.cpp b/lib/Transforms/Utils/Local.cpp
index c3251e26340..21412dcf68e 100644
--- a/lib/Transforms/Utils/Local.cpp
+++ b/lib/Transforms/Utils/Local.cpp
@@ -1098,12 +1098,13 @@ static bool PhiHasDebugValue(DILocalVariable *DIVar,
}
/// Inserts a llvm.dbg.value intrinsic before a store to an alloca'd value
-/// that has an associated llvm.dbg.decl intrinsic.
-void llvm::ConvertDebugDeclareToDebugValue(DbgDeclareInst *DDI,
+/// that has an associated llvm.dbg.declare or llvm.dbg.addr intrinsic.
+void llvm::ConvertDebugDeclareToDebugValue(DbgInfoIntrinsic *DII,
StoreInst *SI, DIBuilder &Builder) {
- auto *DIVar = DDI->getVariable();
+ assert(DII->isAddressOfVariable());
+ auto *DIVar = DII->getVariable();
assert(DIVar && "Missing variable");
- auto *DIExpr = DDI->getExpression();
+ auto *DIExpr = DII->getExpression();
Value *DV = SI->getOperand(0);
// If an argument is zero extended then use argument directly. The ZExt
@@ -1114,7 +1115,7 @@ void llvm::ConvertDebugDeclareToDebugValue(DbgDeclareInst *DDI,
if (SExtInst *SExt = dyn_cast<SExtInst>(SI->getOperand(0)))
ExtendedArg = dyn_cast<Argument>(SExt->getOperand(0));
if (ExtendedArg) {
- // If this DDI was already describing only a fragment of a variable, ensure
+ // If this DII was already describing only a fragment of a variable, ensure
// that fragment is appropriately narrowed here.
// But if a fragment wasn't used, describe the value as the original
// argument (rather than the zext or sext) so that it remains described even
@@ -1127,23 +1128,23 @@ void llvm::ConvertDebugDeclareToDebugValue(DbgDeclareInst *DDI,
DIExpr->elements_end() - 3);
Ops.push_back(dwarf::DW_OP_LLVM_fragment);
Ops.push_back(FragmentOffset);
- const DataLayout &DL = DDI->getModule()->getDataLayout();
+ const DataLayout &DL = DII->getModule()->getDataLayout();
Ops.push_back(DL.getTypeSizeInBits(ExtendedArg->getType()));
DIExpr = Builder.createExpression(Ops);
}
DV = ExtendedArg;
}
if (!LdStHasDebugValue(DIVar, DIExpr, SI))
- Builder.insertDbgValueIntrinsic(DV, DIVar, DIExpr, DDI->getDebugLoc(),
+ Builder.insertDbgValueIntrinsic(DV, DIVar, DIExpr, DII->getDebugLoc(),
SI);
}
/// Inserts a llvm.dbg.value intrinsic before a load of an alloca'd value
-/// that has an associated llvm.dbg.decl intrinsic.
-void llvm::ConvertDebugDeclareToDebugValue(DbgDeclareInst *DDI,
+/// that has an associated llvm.dbg.declare or llvm.dbg.addr intrinsic.
+void llvm::ConvertDebugDeclareToDebugValue(DbgInfoIntrinsic *DII,
LoadInst *LI, DIBuilder &Builder) {
- auto *DIVar = DDI->getVariable();
- auto *DIExpr = DDI->getExpression();
+ auto *DIVar = DII->getVariable();
+ auto *DIExpr = DII->getExpression();
assert(DIVar && "Missing variable");
if (LdStHasDebugValue(DIVar, DIExpr, LI))
@@ -1154,16 +1155,16 @@ void llvm::ConvertDebugDeclareToDebugValue(DbgDeclareInst *DDI,
// preferable to keep tracking both the loaded value and the original
// address in case the alloca can not be elided.
Instruction *DbgValue = Builder.insertDbgValueIntrinsic(
- LI, DIVar, DIExpr, DDI->getDebugLoc(), (Instruction *)nullptr);
+ LI, DIVar, DIExpr, DII->getDebugLoc(), (Instruction *)nullptr);
DbgValue->insertAfter(LI);
}
-/// Inserts a llvm.dbg.value intrinsic after a phi
-/// that has an associated llvm.dbg.decl intrinsic.
-void llvm::ConvertDebugDeclareToDebugValue(DbgDeclareInst *DDI,
+/// Inserts a llvm.dbg.value intrinsic after a phi that has an associated
+/// llvm.dbg.declare or llvm.dbg.addr intrinsic.
+void llvm::ConvertDebugDeclareToDebugValue(DbgInfoIntrinsic *DII,
PHINode *APN, DIBuilder &Builder) {
- auto *DIVar = DDI->getVariable();
- auto *DIExpr = DDI->getExpression();
+ auto *DIVar = DII->getVariable();
+ auto *DIExpr = DII->getExpression();
assert(DIVar && "Missing variable");
if (PhiHasDebugValue(DIVar, DIExpr, APN))
@@ -1176,7 +1177,7 @@ void llvm::ConvertDebugDeclareToDebugValue(DbgDeclareInst *DDI,
// insertion point.
// FIXME: Insert dbg.value markers in the successors when appropriate.
if (InsertionPt != BB->end())
- Builder.insertDbgValueIntrinsic(APN, DIVar, DIExpr, DDI->getDebugLoc(),
+ Builder.insertDbgValueIntrinsic(APN, DIVar, DIExpr, DII->getDebugLoc(),
&*InsertionPt);
}
@@ -1231,16 +1232,25 @@ bool llvm::LowerDbgDeclare(Function &F) {
return true;
}
-/// FindAllocaDbgDeclare - Finds the llvm.dbg.declare intrinsic describing the
-/// alloca 'V', if any.
-DbgDeclareInst *llvm::FindAllocaDbgDeclare(Value *V) {
- if (auto *L = LocalAsMetadata::getIfExists(V))
- if (auto *MDV = MetadataAsValue::getIfExists(V->getContext(), L))
- for (User *U : MDV->users())
- if (DbgDeclareInst *DDI = dyn_cast<DbgDeclareInst>(U))
- return DDI;
+/// Finds all intrinsics declaring local variables as living in the memory that
+/// 'V' points to. This may include a mix of dbg.declare and
+/// dbg.addr intrinsics.
+TinyPtrVector<DbgInfoIntrinsic *> llvm::FindDbgAddrUses(Value *V) {
+ auto *L = LocalAsMetadata::getIfExists(V);
+ if (!L)
+ return {};
+ auto *MDV = MetadataAsValue::getIfExists(V->getContext(), L);
+ if (!MDV)
+ return {};
+
+ TinyPtrVector<DbgInfoIntrinsic *> Declares;
+ for (User *U : MDV->users()) {
+ if (auto *DII = dyn_cast<DbgInfoIntrinsic>(U))
+ if (DII->isAddressOfVariable())
+ Declares.push_back(DII);
+ }
- return nullptr;
+ return Declares;
}
void llvm::findDbgValues(SmallVectorImpl<DbgValueInst *> &DbgValues, Value *V) {
@@ -1251,23 +1261,24 @@ void llvm::findDbgValues(SmallVectorImpl<DbgValueInst *> &DbgValues, Value *V) {
DbgValues.push_back(DVI);
}
-
bool llvm::replaceDbgDeclare(Value *Address, Value *NewAddress,
Instruction *InsertBefore, DIBuilder &Builder,
bool Deref, int Offset) {
- DbgDeclareInst *DDI = FindAllocaDbgDeclare(Address);
- if (!DDI)
- return false;
- DebugLoc Loc = DDI->getDebugLoc();
- auto *DIVar = DDI->getVariable();
- auto *DIExpr = DDI->getExpression();
- assert(DIVar && "Missing variable");
- DIExpr = DIExpression::prepend(DIExpr, Deref, Offset);
- // Insert llvm.dbg.declare immediately after the original alloca, and remove
- // old llvm.dbg.declare.
- Builder.insertDeclare(NewAddress, DIVar, DIExpr, Loc, InsertBefore);
- DDI->eraseFromParent();
- return true;
+ auto DbgAddrs = FindDbgAddrUses(Address);
+ for (DbgInfoIntrinsic *DII : DbgAddrs) {
+ DebugLoc Loc = DII->getDebugLoc();
+ auto *DIVar = DII->getVariable();
+ auto *DIExpr = DII->getExpression();
+ assert(DIVar && "Missing variable");
+ DIExpr = DIExpression::prepend(DIExpr, Deref, Offset);
+ // Insert llvm.dbg.declare immediately after InsertBefore, and remove old
+ // llvm.dbg.declare.
+ Builder.insertDeclare(NewAddress, DIVar, DIExpr, Loc, InsertBefore);
+ if (DII == InsertBefore)
+ InsertBefore = &*std::next(InsertBefore->getIterator());
+ DII->eraseFromParent();
+ }
+ return !DbgAddrs.empty();
}
bool llvm::replaceDbgDeclareForAlloca(AllocaInst *AI, Value *NewAllocaAddress,
diff --git a/lib/Transforms/Utils/PromoteMemoryToRegister.cpp b/lib/Transforms/Utils/PromoteMemoryToRegister.cpp
index ac28f590b01..e2ba5c4cfbb 100644
--- a/lib/Transforms/Utils/PromoteMemoryToRegister.cpp
+++ b/lib/Transforms/Utils/PromoteMemoryToRegister.cpp
@@ -103,7 +103,7 @@ struct AllocaInfo {
bool OnlyUsedInOneBlock;
Value *AllocaPointerVal;
- DbgDeclareInst *DbgDeclare;
+ TinyPtrVector<DbgInfoIntrinsic*> DbgDeclares;
void clear() {
DefiningBlocks.clear();
@@ -112,7 +112,7 @@ struct AllocaInfo {
OnlyBlock = nullptr;
OnlyUsedInOneBlock = true;
AllocaPointerVal = nullptr;
- DbgDeclare = nullptr;
+ DbgDeclares.clear();
}
/// Scan the uses of the specified alloca, filling in the AllocaInfo used
@@ -147,7 +147,7 @@ struct AllocaInfo {
}
}
- DbgDeclare = FindAllocaDbgDeclare(AI);
+ DbgDeclares = FindDbgAddrUses(AI);
}
};
@@ -245,7 +245,7 @@ struct PromoteMem2Reg {
/// For each alloca, we keep track of the dbg.declare intrinsic that
/// describes it, if any, so that we can convert it to a dbg.value
/// intrinsic if the alloca gets promoted.
- SmallVector<DbgDeclareInst *, 8> AllocaDbgDeclares;
+ SmallVector<TinyPtrVector<DbgInfoIntrinsic *>, 8> AllocaDbgDeclares;
/// The set of basic blocks the renamer has already visited.
///
@@ -409,11 +409,11 @@ static bool rewriteSingleStoreAlloca(AllocaInst *AI, AllocaInfo &Info,
// Record debuginfo for the store and remove the declaration's
// debuginfo.
- if (DbgDeclareInst *DDI = Info.DbgDeclare) {
+ for (DbgInfoIntrinsic *DII : Info.DbgDeclares) {
DIBuilder DIB(*AI->getModule(), /*AllowUnresolved*/ false);
- ConvertDebugDeclareToDebugValue(DDI, Info.OnlyStore, DIB);
- DDI->eraseFromParent();
- LBI.deleteValue(DDI);
+ ConvertDebugDeclareToDebugValue(DII, Info.OnlyStore, DIB);
+ DII->eraseFromParent();
+ LBI.deleteValue(DII);
}
// Remove the (now dead) store and alloca.
Info.OnlyStore->eraseFromParent();
@@ -505,9 +505,9 @@ static bool promoteSingleBlockAlloca(AllocaInst *AI, const AllocaInfo &Info,
while (!AI->use_empty()) {
StoreInst *SI = cast<StoreInst>(AI->user_back());
// Record debuginfo for the store before removing it.
- if (DbgDeclareInst *DDI = Info.DbgDeclare) {
+ for (DbgInfoIntrinsic *DII : Info.DbgDeclares) {
DIBuilder DIB(*AI->getModule(), /*AllowUnresolved*/ false);
- ConvertDebugDeclareToDebugValue(DDI, SI, DIB);
+ ConvertDebugDeclareToDebugValue(DII, SI, DIB);
}
SI->eraseFromParent();
LBI.deleteValue(SI);
@@ -517,9 +517,9 @@ static bool promoteSingleBlockAlloca(AllocaInst *AI, const AllocaInfo &Info,
LBI.deleteValue(AI);
// The alloca's debuginfo can be removed as well.
- if (DbgDeclareInst *DDI = Info.DbgDeclare) {
- DDI->eraseFromParent();
- LBI.deleteValue(DDI);
+ for (DbgInfoIntrinsic *DII : Info.DbgDeclares) {
+ DII->eraseFromParent();
+ LBI.deleteValue(DII);
}
++NumLocalPromoted;
@@ -587,8 +587,8 @@ void PromoteMem2Reg::run() {
}
// Remember the dbg.declare intrinsic describing this alloca, if any.
- if (Info.DbgDeclare)
- AllocaDbgDeclares[AllocaNum] = Info.DbgDeclare;
+ if (!Info.DbgDeclares.empty())
+ AllocaDbgDeclares[AllocaNum] = Info.DbgDeclares;
// Keep the reverse mapping of the 'Allocas' array for the rename pass.
AllocaLookup[Allocas[AllocaNum]] = AllocaNum;
@@ -666,9 +666,9 @@ void PromoteMem2Reg::run() {
}
// Remove alloca's dbg.declare instrinsics from the function.
- for (DbgDeclareInst *DDI : AllocaDbgDeclares)
- if (DDI)
- DDI->eraseFromParent();
+ for (auto &Declares : AllocaDbgDeclares)
+ for (auto *DII : Declares)
+ DII->eraseFromParent();
// Loop over all of the PHI nodes and see if there are any that we can get
// rid of because they merge all of the same incoming values. This can
@@ -895,8 +895,8 @@ NextIteration:
// The currently active variable for this block is now the PHI.
IncomingVals[AllocaNo] = APN;
- if (DbgDeclareInst *DDI = AllocaDbgDeclares[AllocaNo])
- ConvertDebugDeclareToDebugValue(DDI, APN, DIB);
+ for (DbgInfoIntrinsic *DII : AllocaDbgDeclares[AllocaNo])
+ ConvertDebugDeclareToDebugValue(DII, APN, DIB);
// Get the next phi node.
++PNI;
@@ -952,8 +952,8 @@ NextIteration:
// what value were we writing?
IncomingVals[ai->second] = SI->getOperand(0);
// Record debuginfo for the store before removing it.
- if (DbgDeclareInst *DDI = AllocaDbgDeclares[ai->second])
- ConvertDebugDeclareToDebugValue(DDI, SI, DIB);
+ for (DbgInfoIntrinsic *DII : AllocaDbgDeclares[ai->second])
+ ConvertDebugDeclareToDebugValue(DII, SI, DIB);
BB->getInstList().erase(SI);
}
}
diff --git a/test/DebugInfo/X86/dbg-addr-dse.ll b/test/DebugInfo/X86/dbg-addr-dse.ll
new file mode 100644
index 00000000000..b438982b994
--- /dev/null
+++ b/test/DebugInfo/X86/dbg-addr-dse.ll
@@ -0,0 +1,100 @@
+; RUN: llc %s -o %t.s
+; RUN: llvm-mc %t.s -filetype=obj -triple=x86_64-windows-msvc -o %t.o
+; RUN: FileCheck %s < %t.s --check-prefix=ASM
+; RUN: llvm-dwarfdump %t.o | FileCheck %s --check-prefix=DWARF
+
+; In this example, the variable lives mostly in memory, but at the point of the
+; assignment to global, it lives nowhere, and is described as the constant
+; value 1.
+
+; C source:
+;
+; void escape(int *);
+; extern int global;
+; void f(int x) {
+; escape(&x);
+; x = 1; // DSE should delete and insert dbg.value(i32 1)
+; global = x;
+; x = 2; // DSE should insert dbg.addr
+; escape(&x);
+; }
+
+; ModuleID = 'dse.c'
+source_filename = "dse.c"
+target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-pc-windows-msvc19.0.24215"
+
+declare void @llvm.dbg.addr(metadata, metadata, metadata) #2
+declare void @llvm.dbg.value(metadata, metadata, metadata) #2
+declare void @escape(i32*)
+
+@global = external global i32, align 4
+
+; Function Attrs: nounwind uwtable
+define void @f(i32 %x) #0 !dbg !8 {
+entry:
+ %x.addr = alloca i32, align 4
+ store i32 %x, i32* %x.addr, align 4
+ call void @llvm.dbg.addr(metadata i32* %x.addr, metadata !13, metadata !DIExpression()), !dbg !18
+ call void @escape(i32* %x.addr), !dbg !19
+ call void @llvm.dbg.value(metadata i32 1, metadata !13, metadata !DIExpression()), !dbg !20
+ store i32 1, i32* @global, align 4, !dbg !22
+ call void @llvm.dbg.addr(metadata i32* %x.addr, metadata !13, metadata !DIExpression()), !dbg !23
+ store i32 2, i32* %x.addr, align 4, !dbg !23
+ call void @escape(i32* %x.addr), !dbg !24
+ ret void, !dbg !25
+}
+
+; ASM-LABEL: f: # @f
+; ASM: movl %ecx, [[OFF_X:[0-9]+]](%rsp)
+; ASM: #DEBUG_VALUE: f:x <- [DW_OP_plus_uconst [[OFF_X]]] [%RSP+0]
+; ASM: callq escape
+; ASM: #DEBUG_VALUE: f:x <- 1
+; ASM: movl $1, global(%rip)
+; FIXME: Needs a fix to LiveDebugVariables
+; ASMX: #DEBUG_VALUE: f:x <- [DW_OP_plus_uconst [[OFF_X]]] [%RSP+0]
+; ASM: movl $2, [[OFF_X]](%rsp)
+; ASM: callq escape
+; ASM: retq
+
+; DWARF: DW_TAG_formal_parameter
+; DWARF-NEXT: DW_AT_location (0x00000000
+; DWARF-NEXT: {{[^:]*}}: DW_OP_breg7 RSP+{{[0-9]+}}
+; DWARF-NEXT: {{[^:]*}}: DW_OP_consts +1, DW_OP_stack_value
+; FIXME: Needs a fix to LiveDebugVariables
+; DWARFX-NEXT: {{[^:]*}}: DW_OP_breg7 RSP+{{[0-9]+}})
+; DWARF-NEXT: DW_AT_name ("x")
+
+attributes #0 = { nounwind uwtable }
+attributes #2 = { nounwind readnone speculatable }
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!3, !4, !5, !6}
+!llvm.ident = !{!7}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 6.0.0 ", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2)
+!1 = !DIFile(filename: "dse.c", directory: "C:\5Csrc\5Cllvm-project\5Cbuild")
+!2 = !{}
+!3 = !{i32 2, !"Dwarf Version", i32 4}
+!4 = !{i32 2, !"Debug Info Version", i32 3}
+!5 = !{i32 1, !"wchar_size", i32 2}
+!6 = !{i32 7, !"PIC Level", i32 2}
+!7 = !{!"clang version 6.0.0 "}
+!8 = distinct !DISubprogram(name: "f", scope: !1, file: !1, line: 3, type: !9, isLocal: false, isDefinition: true, scopeLine: 3, flags: DIFlagPrototyped, isOptimized: true, unit: !0, variables: !12)
+!9 = !DISubroutineType(types: !10)
+!10 = !{null, !11}
+!11 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!12 = !{!13}
+!13 = !DILocalVariable(name: "x", arg: 1, scope: !8, file: !1, line: 3, type: !11)
+!14 = !{!15, !15, i64 0}
+!15 = !{!"int", !16, i64 0}
+!16 = !{!"omnipotent char", !17, i64 0}
+!17 = !{!"Simple C/C++ TBAA"}
+!18 = !DILocation(line: 3, column: 12, scope: !8)
+!19 = !DILocation(line: 4, column: 3, scope: !8)
+!20 = !DILocation(line: 5, column: 5, scope: !8)
+!21 = !DILocation(line: 6, column: 12, scope: !8)
+!22 = !DILocation(line: 6, column: 10, scope: !8)
+!23 = !DILocation(line: 7, column: 5, scope: !8)
+!24 = !DILocation(line: 8, column: 3, scope: !8)
+!25 = !DILocation(line: 9, column: 1, scope: !8)
diff --git a/test/DebugInfo/X86/dbg-addr.ll b/test/DebugInfo/X86/dbg-addr.ll
new file mode 100644
index 00000000000..ffd0f77ebb7
--- /dev/null
+++ b/test/DebugInfo/X86/dbg-addr.ll
@@ -0,0 +1,67 @@
+; RUN: llc %s -o %t.s
+; RUN: llvm-mc -triple x86_64--linux %t.s -filetype=obj -o %t.o
+; RUN: FileCheck < %t.s %s
+; RUN: llvm-dwarfdump %t.o | FileCheck %s --check-prefix=DWARF
+
+; Unlike dbg.declare, dbg.addr should be lowered to DBG_VALUE instructions. It
+; is control-dependent.
+
+; CHECK-LABEL: use_dbg_addr:
+; CHECK: #DEBUG_VALUE: use_dbg_addr:o <- [%RSP+0]
+
+; FIXME: Avoid the use of a single-location location list and use
+; DW_AT_start_offset instead.
+
+; DWARF: DW_TAG_variable
+; DWARF-NEXT: DW_AT_location (0x00000000
+; DWARF-NEXT: 0x{{.*}} - 0x{{.*}}: DW_OP_breg7 RSP+0)
+; DWARF-NEXT: DW_AT_name ("o")
+
+
+; ModuleID = 't.c'
+source_filename = "t.c"
+target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64--linux"
+
+%struct.Foo = type { i32 }
+
+; Function Attrs: noinline nounwind uwtable
+define void @use_dbg_addr() #0 !dbg !7 {
+entry:
+ %o = alloca %struct.Foo, align 4
+ call void @llvm.dbg.addr(metadata %struct.Foo* %o, metadata !10, metadata !15), !dbg !16
+ call void @escape_foo(%struct.Foo* %o), !dbg !17
+ ret void, !dbg !18
+}
+
+; Function Attrs: nounwind readnone speculatable
+declare void @llvm.dbg.addr(metadata, metadata, metadata) #1
+
+declare void @escape_foo(%struct.Foo*)
+
+attributes #0 = { noinline nounwind uwtable }
+attributes #1 = { nounwind readnone speculatable }
+
+!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 6.0.0 ", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !2)
+!1 = !DIFile(filename: "t.c", directory: "C:\5Csrc\5Cllvm-project\5Cbuild")
+!2 = !{}
+!3 = !{i32 2, !"Dwarf Version", i32 4}
+!4 = !{i32 2, !"Debug Info Version", i32 3}
+!5 = !{i32 1, !"wchar_size", i32 4}
+!6 = !{!"clang version 6.0.0 "}
+!7 = distinct !DISubprogram(name: "use_dbg_addr", scope: !1, file: !1, line: 3, type: !8, isLocal: false, isDefinition: true, scopeLine: 3, flags: DIFlagPrototyped, isOptimized: false, unit: !0, variables: !2)
+!8 = !DISubroutineType(types: !9)
+!9 = !{null}
+!10 = !DILocalVariable(name: "o", scope: !7, file: !1, line: 4, type: !11)
+!11 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "Foo", file: !1, line: 1, size: 32, elements: !12)
+!12 = !{!13}
+!13 = !DIDerivedType(tag: DW_TAG_member, name: "x", scope: !11, file: !1, line: 1, baseType: !14, size: 32)
+!14 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!15 = !DIExpression()
+!16 = !DILocation(line: 4, column: 14, scope: !7)
+!17 = !DILocation(line: 5, column: 3, scope: !7)
+!18 = !DILocation(line: 6, column: 1, scope: !7)
diff --git a/test/DebugInfo/X86/sroasplit-5.ll b/test/DebugInfo/X86/sroasplit-5.ll
index dbd3b49a6cb..78f5ca9a979 100644
--- a/test/DebugInfo/X86/sroasplit-5.ll
+++ b/test/DebugInfo/X86/sroasplit-5.ll
@@ -20,10 +20,10 @@ target triple = "x86_64-unknown-linux-gnu"
;
; There should be no debug info for the padding.
; CHECK-NOT: DW_OP_LLVM_fragment, 56
-; CHECK: DIExpression(DW_OP_LLVM_fragment, 32, 24)
-; CHECK-NOT: DW_OP_LLVM_fragment, 56
; CHECK: DIExpression(DW_OP_LLVM_fragment, 0, 32)
; CHECK-NOT: DW_OP_LLVM_fragment, 56
+; CHECK: DIExpression(DW_OP_LLVM_fragment, 32, 24)
+; CHECK-NOT: DW_OP_LLVM_fragment, 56
%struct.prog_src_register = type { i32, i24 }
; Function Attrs: nounwind
diff --git a/test/DebugInfo/X86/sroasplit-dbg-declare.ll b/test/DebugInfo/X86/sroasplit-dbg-declare.ll
new file mode 100644
index 00000000000..0a1a41bd591
--- /dev/null
+++ b/test/DebugInfo/X86/sroasplit-dbg-declare.ll
@@ -0,0 +1,59 @@
+; RUN: opt -S -sroa -o - %s | FileCheck %s
+
+; SROA should split the alloca in two new ones, each with its own dbg.declare.
+; The original alloca and dbg.declare should be removed.
+
+define void @f1() {
+entry:
+ %0 = alloca [9 x i32]
+ call void @llvm.dbg.declare(metadata [9 x i32]* %0, metadata !11, metadata !DIExpression()), !dbg !17
+ %1 = bitcast [9 x i32]* %0 to i8*
+ call void @llvm.memset.p0i8.i64(i8* %1, i8 0, i64 36, i32 16, i1 true)
+ %2 = getelementptr [9 x i32], [9 x i32]* %0, i32 0, i32 0
+ store volatile i32 1, i32* %2
+ ret void
+}
+
+; Function Attrs: nounwind readnone speculatable
+declare void @llvm.dbg.declare(metadata, metadata, metadata) #1
+
+; Function Attrs: argmemonly nounwind
+declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly, i8, i64, i32, i1) #0
+
+attributes #0 = { argmemonly nounwind }
+attributes #1 = { nounwind readnone speculatable }
+
+!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 6.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2)
+!1 = !DIFile(filename: "foo.c", directory: "/bar")
+!2 = !{}
+!3 = !{i32 2, !"Dwarf Version", i32 4}
+!4 = !{i32 2, !"Debug Info Version", i32 3}
+!5 = !{i32 1, !"wchar_size", i32 4}
+!6 = !{!"clang version 6.0.0"}
+!7 = distinct !DISubprogram(name: "f1", scope: !1, file: !1, line: 1, type: !8, isLocal: false, isDefinition: true, scopeLine: 2, flags: DIFlagPrototyped, isOptimized: true, unit: !0, variables: !10)
+!8 = !DISubroutineType(types: !9)
+!9 = !{null}
+!10 = !{!11}
+!11 = !DILocalVariable(name: "b", scope: !7, file: !1, line: 3, type: !12)
+!12 = !DICompositeType(tag: DW_TAG_array_type, baseType: !13, size: 288, elements: !15)
+!13 = !DIDerivedType(tag: DW_TAG_volatile_type, baseType: !14)
+!14 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!15 = !{!16}
+!16 = !DISubrange(count: 9)
+!17 = !DILocation(line: 3, column: 18, scope: !7)
+
+; CHECK-NOT: = alloca [9 x i32]
+; CHECK-NOT: call void @llvm.dbg.declare(metadata [9 x i32]*
+
+; CHECK: %[[VAR1:.*]] = alloca i32
+; CHECK-NEXT: %[[VAR2:.*]] = alloca [8 x i32]
+; CHECK-NEXT: call void @llvm.dbg.declare(metadata i32* %[[VAR1]]
+; CHECK-NEXT: call void @llvm.dbg.declare(metadata [8 x i32]* %[[VAR2]]
+
+; CHECK-NOT: = alloca [9 x i32]
+; CHECK-NOT: call void @llvm.dbg.declare(metadata [9 x i32]*
+
diff --git a/test/Transforms/Mem2Reg/dbg-addr-inline-dse.ll b/test/Transforms/Mem2Reg/dbg-addr-inline-dse.ll
new file mode 100644
index 00000000000..8fe2e0ab317
--- /dev/null
+++ b/test/Transforms/Mem2Reg/dbg-addr-inline-dse.ll
@@ -0,0 +1,94 @@
+; RUN: opt -mem2reg -S < %s | FileCheck %s -implicit-check-not="call void @llvm.dbg.addr"
+
+; This example is intended to simulate this pass pipeline, which may not exist
+; in practice:
+; 1. DSE f from the original C source
+; 2. Inline escape
+; 3. mem2reg
+; This exercises the corner case of multiple llvm.dbg.addr intrinsics.
+
+; C source:
+;
+; void escape(int *px) { ++*px; }
+; extern int global;
+; void f(int x) {
+; escape(&x);
+; x = 1; // DSE should delete and insert dbg.value(i32 1)
+; global = x;
+; x = 2; // DSE should insert dbg.addr
+; escape(&x);
+; }
+
+; ModuleID = 'dse.c'
+source_filename = "dse.c"
+target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-pc-windows-msvc19.0.24215"
+
+declare void @llvm.dbg.addr(metadata, metadata, metadata) #2
+declare void @llvm.dbg.value(metadata, metadata, metadata) #2
+
+@global = external global i32, align 4
+
+; Function Attrs: nounwind uwtable
+define void @f(i32 %x) #0 !dbg !8 {
+entry:
+ %x.addr = alloca i32, align 4
+ store i32 %x, i32* %x.addr, align 4
+ call void @llvm.dbg.addr(metadata i32* %x.addr, metadata !13, metadata !DIExpression()), !dbg !18
+ %ld.1 = load i32, i32* %x.addr, align 4, !dbg !19
+ %inc.1 = add nsw i32 %ld.1, 1, !dbg !19
+ store i32 %inc.1, i32* %x.addr, align 4, !dbg !19
+ call void @llvm.dbg.value(metadata i32 1, metadata !13, metadata !DIExpression()), !dbg !20
+ store i32 1, i32* @global, align 4, !dbg !22
+ call void @llvm.dbg.addr(metadata i32* %x.addr, metadata !13, metadata !DIExpression()), !dbg !23
+ store i32 2, i32* %x.addr, align 4, !dbg !23
+ %ld.2 = load i32, i32* %x.addr, align 4, !dbg !19
+ %inc.2 = add nsw i32 %ld.2, 1, !dbg !19
+ store i32 %inc.2, i32* %x.addr, align 4, !dbg !19
+ ret void, !dbg !25
+}
+
+; CHECK-LABEL: define void @f(i32 %x)
+; CHECK: call void @llvm.dbg.value(metadata i32 %x, metadata !13, metadata !DIExpression())
+; CHECK: %inc.1 = add nsw i32 %x, 1
+; CHECK: call void @llvm.dbg.value(metadata i32 %inc.1, metadata !13, metadata !DIExpression())
+; CHECK: call void @llvm.dbg.value(metadata i32 1, metadata !13, metadata !DIExpression())
+; CHECK: store i32 1, i32* @global, align 4
+; CHECK: call void @llvm.dbg.value(metadata i32 2, metadata !13, metadata !DIExpression())
+; CHECK: %inc.2 = add nsw i32 2, 1
+; CHECK: call void @llvm.dbg.value(metadata i32 %inc.2, metadata !13, metadata !DIExpression())
+; CHECK: ret void
+
+attributes #0 = { nounwind uwtable }
+attributes #2 = { nounwind readnone speculatable }
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!3, !4, !5, !6}
+!llvm.ident = !{!7}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 6.0.0 ", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2)
+!1 = !DIFile(filename: "dse.c", directory: "C:\5Csrc\5Cllvm-project\5Cbuild")
+!2 = !{}
+!3 = !{i32 2, !"Dwarf Version", i32 4}
+!4 = !{i32 2, !"Debug Info Version", i32 3}
+!5 = !{i32 1, !"wchar_size", i32 2}
+!6 = !{i32 7, !"PIC Level", i32 2}
+!7 = !{!"clang version 6.0.0 "}
+!8 = distinct !DISubprogram(name: "f", scope: !1, file: !1, line: 3, type: !9, isLocal: false, isDefinition: true, scopeLine: 3, flags: DIFlagPrototyped, isOptimized: true, unit: !0, variables: !12)
+!9 = !DISubroutineType(types: !10)
+!10 = !{null, !11}
+!11 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!12 = !{!13}
+!13 = !DILocalVariable(name: "x", arg: 1, scope: !8, file: !1, line: 3, type: !11)
+!14 = !{!15, !15, i64 0}
+!15 = !{!"int", !16, i64 0}
+!16 = !{!"omnipotent char", !17, i64 0}
+!17 = !{!"Simple C/C++ TBAA"}
+!18 = !DILocation(line: 3, column: 12, scope: !8)
+!19 = !DILocation(line: 4, column: 3, scope: !8)
+!20 = !DILocation(line: 5, column: 5, scope: !8)
+!21 = !DILocation(line: 6, column: 12, scope: !8)
+!22 = !DILocation(line: 6, column: 10, scope: !8)
+!23 = !DILocation(line: 7, column: 5, scope: !8)
+!24 = !DILocation(line: 8, column: 3, scope: !8)
+!25 = !DILocation(line: 9, column: 1, scope: !8)
diff --git a/test/Transforms/Mem2Reg/dbg-addr.ll b/test/Transforms/Mem2Reg/dbg-addr.ll
new file mode 100644
index 00000000000..8c97f791a08
--- /dev/null
+++ b/test/Transforms/Mem2Reg/dbg-addr.ll
@@ -0,0 +1,91 @@
+; RUN: opt -mem2reg -S < %s | FileCheck %s
+
+; ModuleID = 'newvars.c'
+source_filename = "newvars.c"
+target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-pc-windows-msvc19.0.24215"
+
+; Function Attrs: nounwind uwtable
+define i32 @if_else(i32 %cond, i32 %a, i32 %b) !dbg !8 {
+entry:
+ %x = alloca i32, align 4
+ call void @llvm.dbg.addr(metadata i32* %x, metadata !16, metadata !DIExpression()), !dbg !26
+ store i32 %a, i32* %x, align 4, !dbg !26, !tbaa !17
+ %tobool = icmp ne i32 %cond, 0, !dbg !28
+ br i1 %tobool, label %if.then, label %if.else, !dbg !30
+
+if.then: ; preds = %entry
+ store i32 0, i32* %x, align 4, !dbg !31, !tbaa !17
+ br label %if.end, !dbg !33
+
+if.else: ; preds = %entry
+ store i32 %b, i32* %x, align 4, !dbg !36, !tbaa !17
+ br label %if.end
+
+if.end: ; preds = %if.else, %if.then
+ %rv = load i32, i32* %x, align 4, !dbg !37, !tbaa !17
+ ret i32 %rv, !dbg !39
+}
+
+; CHECK-LABEL: define i32 @if_else({{.*}})
+; CHECK: entry:
+; CHECK-NOT: alloca i32
+; CHECK: call void @llvm.dbg.value(metadata i32 %a, metadata ![[X_LOCAL:[0-9]+]], metadata !DIExpression())
+; CHECK: if.then: ; preds = %entry
+; CHECK: call void @llvm.dbg.value(metadata i32 0, metadata ![[X_LOCAL]], metadata !DIExpression())
+; CHECK: if.else: ; preds = %entry
+; CHECK: call void @llvm.dbg.value(metadata i32 %b, metadata ![[X_LOCAL]], metadata !DIExpression())
+; CHECK: if.end: ; preds = %if.else, %if.then
+; CHECK: %[[PHI:[^ ]*]] = phi i32 [ 0, %if.then ], [ %b, %if.else ]
+; CHECK: call void @llvm.dbg.value(metadata i32 %[[PHI]], metadata ![[X_LOCAL]], metadata !DIExpression())
+; CHECK: ret i32
+
+; CHECK: ![[X_LOCAL]] = !DILocalVariable(name: "x", {{.*}})
+
+; Function Attrs: nounwind readnone speculatable
+declare void @llvm.dbg.declare(metadata, metadata, metadata)
+declare void @llvm.dbg.addr(metadata, metadata, metadata)
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!3, !4, !5, !6}
+!llvm.ident = !{!7}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 6.0.0 ", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2)
+!1 = !DIFile(filename: "newvars.c", directory: "C:\5Csrc\5Cllvm-project\5Cbuild")
+!2 = !{}
+!3 = !{i32 2, !"Dwarf Version", i32 4}
+!4 = !{i32 2, !"Debug Info Version", i32 3}
+!5 = !{i32 1, !"wchar_size", i32 2}
+!6 = !{i32 7, !"PIC Level", i32 2}
+!7 = !{!"clang version 6.0.0 "}
+!8 = distinct !DISubprogram(name: "if_else", scope: !1, file: !1, line: 1, type: !9, isLocal: false, isDefinition: true, scopeLine: 1, flags: DIFlagPrototyped, isOptimized: true, unit: !0, variables: !12)
+!9 = !DISubroutineType(types: !10)
+!10 = !{!11, !11, !11, !11}
+!11 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!12 = !{!13, !14, !15, !16}
+!13 = !DILocalVariable(name: "b", arg: 3, scope: !8, file: !1, line: 1, type: !11)
+!14 = !DILocalVariable(name: "a", arg: 2, scope: !8, file: !1, line: 1, type: !11)
+!15 = !DILocalVariable(name: "cond", arg: 1, scope: !8, file: !1, line: 1, type: !11)
+!16 = !DILocalVariable(name: "x", scope: !8, file: !1, line: 2, type: !11)
+!17 = !{!18, !18, i64 0}
+!18 = !{!"int", !19, i64 0}
+!19 = !{!"omnipotent char", !20, i64 0}
+!20 = !{!"Simple C/C++ TBAA"}
+!22 = !DILocation(line: 1, column: 34, scope: !8)
+!23 = !DILocation(line: 1, column: 27, scope: !8)
+!24 = !DILocation(line: 1, column: 17, scope: !8)
+!25 = !DILocation(line: 2, column: 3, scope: !8)
+!26 = !DILocation(line: 2, column: 7, scope: !8)
+!27 = !DILocation(line: 2, column: 11, scope: !8)
+!28 = !DILocation(line: 3, column: 7, scope: !29)
+!29 = distinct !DILexicalBlock(scope: !8, file: !1, line: 3, column: 7)
+!30 = !DILocation(line: 3, column: 7, scope: !8)
+!31 = !DILocation(line: 4, column: 7, scope: !32)
+!32 = distinct !DILexicalBlock(scope: !29, file: !1, line: 3, column: 13)
+!33 = !DILocation(line: 5, column: 3, scope: !32)
+!34 = !DILocation(line: 6, column: 9, scope: !35)
+!35 = distinct !DILexicalBlock(scope: !29, file: !1, line: 5, column: 10)
+!36 = !DILocation(line: 6, column: 7, scope: !35)
+!37 = !DILocation(line: 8, column: 10, scope: !8)
+!38 = !DILocation(line: 9, column: 1, scope: !8)
+!39 = !DILocation(line: 8, column: 3, scope: !8)
diff --git a/test/Transforms/SROA/dbg-addr-diamond.ll b/test/Transforms/SROA/dbg-addr-diamond.ll
new file mode 100644
index 00000000000..132da991464
--- /dev/null
+++ b/test/Transforms/SROA/dbg-addr-diamond.ll
@@ -0,0 +1,127 @@
+; RUN: opt -use-dbg-addr -sroa -S < %s | FileCheck %s
+
+; ModuleID = '<stdin>'
+source_filename = "newvars.c"
+target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-pc-windows-msvc19.0.24215"
+
+%struct.Pair = type { i32, i32 }
+
+@pair = internal global %struct.Pair zeroinitializer
+
+; Function Attrs: nounwind uwtable
+define void @if_else(i32 %cond, i32 %a, i32 %b) !dbg !8 {
+entry:
+ %p = alloca %struct.Pair, align 4
+ %0 = bitcast %struct.Pair* %p to i8*, !dbg !25
+ call void @llvm.dbg.addr(metadata %struct.Pair* %p, metadata !20, metadata !DIExpression()), !dbg !26
+ %x = getelementptr inbounds %struct.Pair, %struct.Pair* %p, i32 0, i32 0, !dbg !27
+ store i32 %a, i32* %x, align 4, !dbg !28
+ %y = getelementptr inbounds %struct.Pair, %struct.Pair* %p, i32 0, i32 1, !dbg !34
+ store i32 %b, i32* %y, align 4, !dbg !35
+ %tobool = icmp ne i32 %cond, 0, !dbg !37
+ br i1 %tobool, label %if.then, label %if.else, !dbg !39
+
+if.then: ; preds = %entry
+ %x1 = getelementptr inbounds %struct.Pair, %struct.Pair* %p, i32 0, i32 0, !dbg !40
+ store i32 0, i32* %x1, align 4, !dbg !42
+ %y2 = getelementptr inbounds %struct.Pair, %struct.Pair* %p, i32 0, i32 1, !dbg !43
+ store i32 %a, i32* %y2, align 4, !dbg !44
+ br label %if.end, !dbg !45
+
+if.else: ; preds = %entry
+ %x3 = getelementptr inbounds %struct.Pair, %struct.Pair* %p, i32 0, i32 0, !dbg !46
+ store i32 %b, i32* %x3, align 4, !dbg !48
+ %y4 = getelementptr inbounds %struct.Pair, %struct.Pair* %p, i32 0, i32 1, !dbg !49
+ store i32 0, i32* %y4, align 4, !dbg !50
+ br label %if.end
+
+if.end: ; preds = %if.else, %if.then
+ %1 = bitcast %struct.Pair* %p to i8*, !dbg !51
+ %2 = bitcast %struct.Pair* @pair to i8*, !dbg !51
+ call void @llvm.memcpy.p0i8.p0i8.i64(i8* %2, i8* %1, i64 8, i32 4, i1 false), !dbg !51
+ ret void
+}
+
+; CHECK-LABEL: define void @if_else(i32 %cond, i32 %a, i32 %b)
+; CHECK: entry:
+; CHECK: call void @llvm.dbg.value(metadata i32 %a, metadata ![[PVAR:[0-9]+]], metadata ![[XFRAG:DIExpression\(DW_OP_LLVM_fragment, 0, 32\)]])
+; CHECK: call void @llvm.dbg.value(metadata i32 %b, metadata ![[PVAR]], metadata ![[YFRAG:DIExpression\(DW_OP_LLVM_fragment, 32, 32\)]])
+; CHECK: if.then:
+; CHECK: call void @llvm.dbg.value(metadata i32 0, metadata ![[PVAR]], metadata ![[XFRAG]])
+; CHECK: call void @llvm.dbg.value(metadata i32 %a, metadata ![[PVAR]], metadata ![[YFRAG]])
+; CHECK: if.else:
+; CHECK: call void @llvm.dbg.value(metadata i32 %b, metadata ![[PVAR]], metadata ![[XFRAG]])
+; CHECK: call void @llvm.dbg.value(metadata i32 0, metadata ![[PVAR]], metadata ![[YFRAG]])
+; CHECK: if.end:
+; CHECK: %p.sroa.4.0 = phi i32 [ %a, %if.then ], [ 0, %if.else ]
+; CHECK: %p.sroa.0.0 = phi i32 [ 0, %if.then ], [ %b, %if.else ]
+; CHECK: call void @llvm.dbg.value(metadata i32 %p.sroa.0.0, metadata ![[PVAR]], metadata ![[XFRAG]])
+; CHECK: call void @llvm.dbg.value(metadata i32 %p.sroa.4.0, metadata ![[PVAR]], metadata ![[YFRAG]])
+
+; CHECK: ![[PVAR]] = !DILocalVariable(name: "p", {{.*}})
+
+; Function Attrs: argmemonly nounwind
+declare void @llvm.memcpy.p0i8.p0i8.i64(i8* nocapture writeonly, i8* nocapture readonly, i64, i32, i1) #2
+
+; Function Attrs: nounwind readnone speculatable
+declare void @llvm.dbg.addr(metadata, metadata, metadata)
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!3, !4, !5, !6}
+!llvm.ident = !{!7}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 6.0.0 ", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2)
+!1 = !DIFile(filename: "newvars.c", directory: "C:\5Csrc\5Cllvm-project\5Cbuild")
+!2 = !{}
+!3 = !{i32 2, !"Dwarf Version", i32 4}
+!4 = !{i32 2, !"Debug Info Version", i32 3}
+!5 = !{i32 1, !"wchar_size", i32 2}
+!6 = !{i32 7, !"PIC Level", i32 2}
+!7 = !{!"clang version 6.0.0 "}
+!8 = distinct !DISubprogram(name: "if_else", scope: !1, file: !1, line: 2, type: !9, isLocal: false, isDefinition: true, scopeLine: 2, flags: DIFlagPrototyped, isOptimized: true, unit: !0, variables: !16)
+!9 = !DISubroutineType(types: !10)
+!10 = !{!11, !14, !14, !14}
+!11 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "Pair", file: !1, line: 1, size: 64, elements: !12)
+!12 = !{!13, !15}
+!13 = !DIDerivedType(tag: DW_TAG_member, name: "x", scope: !11, file: !1, line: 1, baseType: !14, size: 32)
+!14 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!15 = !DIDerivedType(tag: DW_TAG_member, name: "y", scope: !11, file: !1, line: 1, baseType: !14, size: 32, offset: 32)
+!16 = !{!17, !18, !19, !20}
+!17 = !DILocalVariable(name: "b", arg: 3, scope: !8, file: !1, line: 2, type: !14)
+!18 = !DILocalVariable(name: "a", arg: 2, scope: !8, file: !1, line: 2, type: !14)
+!19 = !DILocalVariable(name: "cond", arg: 1, scope: !8, file: !1, line: 2, type: !14)
+!20 = !DILocalVariable(name: "p", scope: !8, file: !1, line: 3, type: !11)
+!22 = !DILocation(line: 2, column: 42, scope: !8)
+!23 = !DILocation(line: 2, column: 35, scope: !8)
+!24 = !DILocation(line: 2, column: 25, scope: !8)
+!25 = !DILocation(line: 3, column: 3, scope: !8)
+!26 = !DILocation(line: 3, column: 15, scope: !8)
+!27 = !DILocation(line: 4, column: 5, scope: !8)
+!28 = !DILocation(line: 4, column: 7, scope: !8)
+!29 = !{!30, !31, i64 0}
+!30 = !{!"Pair", !31, i64 0, !31, i64 4}
+!31 = !{!"int", !32, i64 0}
+!32 = !{!"omnipotent char", !33, i64 0}
+!33 = !{!"Simple C/C++ TBAA"}
+!34 = !DILocation(line: 5, column: 5, scope: !8)
+!35 = !DILocation(line: 5, column: 7, scope: !8)
+!36 = !{!30, !31, i64 4}
+!37 = !DILocation(line: 6, column: 7, scope: !38)
+!38 = distinct !DILexicalBlock(scope: !8, file: !1, line: 6, column: 7)
+!39 = !DILocation(line: 6, column: 7, scope: !8)
+!40 = !DILocation(line: 7, column: 7, scope: !41)
+!41 = distinct !DILexicalBlock(scope: !38, file: !1, line: 6, column: 13)
+!42 = !DILocation(line: 7, column: 9, scope: !41)
+!43 = !DILocation(line: 8, column: 7, scope: !41)
+!44 = !DILocation(line: 8, column: 9, scope: !41)
+!45 = !DILocation(line: 9, column: 3, scope: !41)
+!46 = !DILocation(line: 10, column: 7, scope: !47)
+!47 = distinct !DILexicalBlock(scope: !38, file: !1, line: 9, column: 10)
+!48 = !DILocation(line: 10, column: 9, scope: !47)
+!49 = !DILocation(line: 11, column: 7, scope: !47)
+!50 = !DILocation(line: 11, column: 9, scope: !47)
+!51 = !DILocation(line: 13, column: 10, scope: !8)
+!52 = !{i64 0, i64 4, !53, i64 4, i64 4, !53}
+!53 = !{!31, !31, i64 0}
+!54 = !DILocation(line: 14, column: 1, scope: !8)
diff --git a/unittests/Transforms/Utils/CMakeLists.txt b/unittests/Transforms/Utils/CMakeLists.txt
index 475c365dddc..8c09bae5aa3 100644
--- a/unittests/Transforms/Utils/CMakeLists.txt
+++ b/unittests/Transforms/Utils/CMakeLists.txt
@@ -1,5 +1,6 @@
set(LLVM_LINK_COMPONENTS
Analysis
+ AsmParser
Core
Support
TransformUtils
diff --git a/unittests/Transforms/Utils/Local.cpp b/unittests/Transforms/Utils/Local.cpp
index 5164bdbb2a4..1f58dc802fa 100644
--- a/unittests/Transforms/Utils/Local.cpp
+++ b/unittests/Transforms/Utils/Local.cpp
@@ -8,10 +8,14 @@
//===----------------------------------------------------------------------===//
#include "llvm/Transforms/Utils/Local.h"
+#include "llvm/AsmParser/Parser.h"
#include "llvm/IR/BasicBlock.h"
+#include "llvm/IR/DIBuilder.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/Instructions.h"
+#include "llvm/IR/IntrinsicInst.h"
#include "llvm/IR/LLVMContext.h"
+#include "llvm/Support/SourceMgr.h"
#include "gtest/gtest.h"
using namespace llvm;
@@ -95,3 +99,70 @@ TEST(Local, RemoveDuplicatePHINodes) {
EXPECT_TRUE(EliminateDuplicatePHINodes(BB));
EXPECT_EQ(3U, BB->size());
}
+
+std::unique_ptr<Module> parseIR(LLVMContext &C, const char *IR) {
+ SMDiagnostic Err;
+ std::unique_ptr<Module> Mod = parseAssemblyString(IR, Err, C);
+ if (!Mod)
+ Err.print("UtilsTests", errs());
+ return Mod;
+}
+
+TEST(Local, ReplaceDbgDeclare) {
+ LLVMContext C;
+
+ // Original C source to get debug info for a local variable:
+ // void f() { int x; }
+ std::unique_ptr<Module> M = parseIR(
+ C,
+ "define void @f() !dbg !8 {\n"
+ "entry:\n"
+ " %x = alloca i32, align 4\n"
+ " call void @llvm.dbg.declare(metadata i32* %x, metadata !11, metadata "
+ "!DIExpression()), !dbg !13\n"
+ " call void @llvm.dbg.declare(metadata i32* %x, metadata !11, metadata "
+ "!DIExpression()), !dbg !13\n"
+ " ret void, !dbg !14\n"
+ "}\n"
+ "declare void @llvm.dbg.declare(metadata, metadata, metadata)\n"
+ "!llvm.dbg.cu = !{!0}\n"
+ "!llvm.module.flags = !{!3, !4}\n"
+ "!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "
+ "\"clang version 6.0.0 \", isOptimized: false, runtimeVersion: 0, "
+ "emissionKind: FullDebug, enums: !2)\n"
+ "!1 = !DIFile(filename: \"t2.c\", directory: \"foo\")\n"
+ "!2 = !{}\n"
+ "!3 = !{i32 2, !\"Dwarf Version\", i32 4}\n"
+ "!4 = !{i32 2, !\"Debug Info Version\", i32 3}\n"
+ "!8 = distinct !DISubprogram(name: \"f\", scope: !1, file: !1, line: 1, "
+ "type: !9, isLocal: false, isDefinition: true, scopeLine: 1, "
+ "isOptimized: false, unit: !0, variables: !2)\n"
+ "!9 = !DISubroutineType(types: !10)\n"
+ "!10 = !{null}\n"
+ "!11 = !DILocalVariable(name: \"x\", scope: !8, file: !1, line: 2, type: "
+ "!12)\n"
+ "!12 = !DIBasicType(name: \"int\", size: 32, encoding: DW_ATE_signed)\n"
+ "!13 = !DILocation(line: 2, column: 7, scope: !8)\n"
+ "!14 = !DILocation(line: 3, column: 1, scope: !8)\n");
+ auto *GV = M->getNamedValue("f");
+ ASSERT_TRUE(GV);
+ auto *F = dyn_cast<Function>(GV);
+ ASSERT_TRUE(F);
+ Instruction *Inst = &F->front().front();
+ auto *AI = dyn_cast<AllocaInst>(Inst);
+ ASSERT_TRUE(AI);
+ Inst = Inst->getNextNode()->getNextNode();
+ ASSERT_TRUE(Inst);
+ auto *DII = dyn_cast<DbgDeclareInst>(Inst);
+ ASSERT_TRUE(DII);
+ Value *NewBase = Constant::getNullValue(Type::getInt32PtrTy(C));
+ DIBuilder DIB(*M);
+ replaceDbgDeclare(AI, NewBase, DII, DIB, /*Deref=*/false, /*Offset=*/0);
+
+ // There should be exactly two dbg.declares.
+ int Declares = 0;
+ for (const Instruction &I : F->front())
+ if (isa<DbgDeclareInst>(I))
+ Declares++;
+ EXPECT_EQ(2, Declares);
+}