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

llvm-svn: 313905
This commit is contained in:
Reid Kleckner 2017-09-21 19:52:03 +00:00
parent 977996d25b
commit 0fe506bc5e
20 changed files with 857 additions and 141 deletions

View File

@ -171,7 +171,48 @@ 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.addr``
^^^^^^^^^^^^^^^^^^^^
.. code-block:: llvm
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 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.addr(metadata i32* %i.addr, metadata !1,
metadata !DIExpression()), !dbg !2
!1 = !DILocalVariable(name: "i", ...) ; int i
!2 = !DILocation(...)
...
%buffer = alloca [256 x i8], align 8
; The address of i is buffer+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``
^^^^^^^^^^^^^^^^^^^^
@ -180,26 +221,14 @@ 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
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.
.. 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)
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

View File

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

View File

@ -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----------------------===//

View File

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

View File

@ -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.
if (const auto *AI =
dyn_cast<AllocaInst>(Address->stripInBoundsConstantOffsets()))
if (AI->isStaticAlloca() && FuncInfo.StaticAllocaMap.count(AI))
return nullptr;
bool isParameter = Variable->isParameter() || isa<Argument>(Address);
// 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;
// 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()) {
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);
}
// 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.

View File

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

View File

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

View File

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

View File

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

View File

@ -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 {};
return nullptr;
TinyPtrVector<DbgInfoIntrinsic *> Declares;
for (User *U : MDV->users()) {
if (auto *DII = dyn_cast<DbgInfoIntrinsic>(U))
if (DII->isAddressOfVariable())
Declares.push_back(DII);
}
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,

View File

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

View File

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

View File

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

View File

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

View File

@ -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]*

View File

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

View File

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

View File

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

View File

@ -1,5 +1,6 @@
set(LLVM_LINK_COMPONENTS
Analysis
AsmParser
Core
Support
TransformUtils

View File

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