Index: llvm/docs/SourceLevelDebugging.rst =================================================================== --- llvm/docs/SourceLevelDebugging.rst +++ llvm/docs/SourceLevelDebugging.rst @@ -171,35 +171,60 @@ ---------------------------- 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 `_ containing a -description of the variable. The third argument is a `complex expression -`_. 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 `_ containing a description of +the variable. The third argument is a `complex expression +`_. 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)) + !3 = !DILocalVariable(name: "i", ...) ; int i + +A frontend should generate 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. + + +``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 +`_. 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 +267,9 @@ 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 Index: llvm/include/llvm/IR/IntrinsicInst.h =================================================================== --- llvm/include/llvm/IR/IntrinsicInst.h +++ llvm/include/llvm/IR/IntrinsicInst.h @@ -71,6 +71,12 @@ /// 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(getRawVariable()); } @@ -92,6 +98,7 @@ switch (I->getIntrinsicID()) { case Intrinsic::dbg_declare: case Intrinsic::dbg_value: + case Intrinsic::dbg_addr: return true; default: return false; } @@ -115,6 +122,20 @@ } }; + /// This represents the llvm.dbg.addr instruction. + class DbgAddrIntrinsic : public DbgInfoIntrinsic { + public: + Value *getAddress() const { return getVariableLocation(); } + + // Methods for support type inquiry through isa, cast, and dyn_cast: + static bool classof(const IntrinsicInst *I) { + return I->getIntrinsicID() == Intrinsic::dbg_addr; + } + static bool classof(const Value *V) { + return isa(V) && classof(cast(V)); + } + }; + /// This represents the llvm.dbg.value instruction. class DbgValueInst : public DbgInfoIntrinsic { public: Index: llvm/include/llvm/IR/Intrinsics.td =================================================================== --- llvm/include/llvm/IR/Intrinsics.td +++ llvm/include/llvm/IR/Intrinsics.td @@ -583,12 +583,16 @@ 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----------------------===// Index: llvm/include/llvm/Transforms/Utils/Local.h =================================================================== --- llvm/include/llvm/Transforms/Utils/Local.h +++ llvm/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 Instruction; class CallInst; class DbgDeclareInst; +class DbgInfoIntrinsic; class DbgValueInst; class StoreInst; class LoadInst; @@ -263,25 +265,25 @@ /// 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, +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, +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, +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 debug info intrinsics describing local variables at this address. +TinyPtrVector FindDbgAddrUses(Value *V); /// Finds the llvm.dbg.value intrinsics describing a value. void findDbgValues(SmallVectorImpl &DbgValues, Value *V); Index: llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp =================================================================== --- llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp +++ llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp @@ -5109,37 +5109,48 @@ DAG.setRoot(CallResult.second); return nullptr; } + case Intrinsic::dbg_addr: case Intrinsic::dbg_declare: { - const DbgDeclareInst &DI = cast(I); + const DbgInfoIntrinsic &DI = cast(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(Address) || + const Value *Address = DI.getVariableLocation(); + if (!Address || isa(Address) || (Address->use_empty() && !isa(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(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(Address->stripInBoundsConstantOffsets())) - if (AI->isStaticAlloca() && FuncInfo.StaticAllocaMap.count(AI)) - return nullptr; + dyn_cast(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( + 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(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(Address)) @@ -5150,7 +5161,6 @@ if (const BitCastInst *BCI = dyn_cast(Address)) Address = BCI->getOperand(0); // Parameters are handled specially. - bool isParameter = Variable->isParameter() || isa(Address); auto FINode = dyn_cast(N.getNode()); if (isParameter && FINode) { // Byval parameter. We have a frame index at this point. Index: llvm/lib/IR/DIBuilder.cpp =================================================================== --- llvm/lib/IR/DIBuilder.cpp +++ llvm/lib/IR/DIBuilder.cpp @@ -24,6 +24,11 @@ using namespace llvm; using namespace llvm::dwarf; +cl::opt + 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 @@ 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 @@ 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 @@ VarInfo->getScope()->getSubprogram() && "Expected matching subprograms"); if (!DeclareFn) - DeclareFn = Intrinsic::getDeclaration(&M, Intrinsic::dbg_declare); + DeclareFn = getDeclareIntrin(M); trackIfUnresolved(VarInfo); trackIfUnresolved(Expr); Index: llvm/lib/IR/IntrinsicInst.cpp =================================================================== --- llvm/lib/IR/IntrinsicInst.cpp +++ llvm/lib/IR/IntrinsicInst.cpp @@ -24,6 +24,7 @@ #include "llvm/IR/IntrinsicInst.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/IR/Constants.h" +#include "llvm/IR/DebugInfoMetadata.h" #include "llvm/IR/GlobalVariable.h" #include "llvm/IR/Metadata.h" #include "llvm/IR/Module.h" Index: llvm/lib/IR/Verifier.cpp =================================================================== --- llvm/lib/IR/Verifier.cpp +++ llvm/lib/IR/Verifier.cpp @@ -4001,6 +4001,7 @@ "invalid llvm.dbg.declare intrinsic call 1", CS); visitDbgIntrinsic("declare", cast(*CS.getInstruction())); break; + case Intrinsic::dbg_addr: // llvm.dbg.addr case Intrinsic::dbg_value: // llvm.dbg.value visitDbgIntrinsic("value", cast(*CS.getInstruction())); break; Index: llvm/lib/Transforms/Scalar/SROA.cpp =================================================================== --- llvm/lib/Transforms/Scalar/SROA.cpp +++ llvm/lib/Transforms/Scalar/SROA.cpp @@ -4102,9 +4102,10 @@ // 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 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) { @@ -4137,11 +4138,11 @@ } // Remove any existing dbg.declare intrinsic describing the same alloca. - if (DbgDeclareInst *OldDDI = FindAllocaDbgDeclare(Fragment.Alloca)) - OldDDI->eraseFromParent(); + for (DbgInfoIntrinsic *OldDII : FindDbgAddrUses(Fragment.Alloca)) + OldDII->eraseFromParent(); DIB.insertDeclare(Fragment.Alloca, Var, FragmentExpr, - DbgDecl->getDebugLoc(), &AI); + DbgDeclares.front()->getDebugLoc(), &AI); } } return Changed; @@ -4258,8 +4259,8 @@ if (AllocaInst *AI = dyn_cast(I)) { DeletedAllocas.insert(AI); - if (DbgDeclareInst *DbgDecl = FindAllocaDbgDeclare(AI)) - DbgDecl->eraseFromParent(); + for (DbgInfoIntrinsic *OldDII : FindDbgAddrUses(AI)) + OldDII->eraseFromParent(); } ++NumDeleted; Index: llvm/lib/Transforms/Utils/Local.cpp =================================================================== --- llvm/lib/Transforms/Utils/Local.cpp +++ llvm/lib/Transforms/Utils/Local.cpp @@ -1099,11 +1099,12 @@ /// 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, +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 @@ if (SExtInst *SExt = dyn_cast(SI->getOperand(0))) ExtendedArg = dyn_cast(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 @@ 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, +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 @@ // 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, +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 @@ // 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,34 @@ 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(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.value+DW_OP_deref intrinsics, but they must all agree about which +/// variable is stored there. +TinyPtrVector llvm::FindDbgAddrUses(Value *V) { + auto *L = LocalAsMetadata::getIfExists(V); + if (!L) + return {}; + auto *MDV = MetadataAsValue::getIfExists(V->getContext(), L); + if (!MDV) + return {}; + + TinyPtrVector Declares; + for (User *U : MDV->users()) { + if (auto *DII = dyn_cast(U)) + if (DII->isAddressOfVariable()) + Declares.push_back(DII); + } + + // Return nothing if the locations or DILocalVariables disagree. + DILocalVariable *Local = + Declares.empty() ? nullptr : Declares.front()->getVariable(); + for (DbgInfoIntrinsic *DII : Declares) { + if (DII->getVariable() != Local) + return {}; + } - return nullptr; + return Declares; } void llvm::findDbgValues(SmallVectorImpl &DbgValues, Value *V) { @@ -1251,23 +1270,22 @@ 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 the original alloca, and remove + // old llvm.dbg.declare. + Builder.insertDeclare(NewAddress, DIVar, DIExpr, Loc, InsertBefore); + DII->eraseFromParent(); + } + return !DbgAddrs.empty(); } bool llvm::replaceDbgDeclareForAlloca(AllocaInst *AI, Value *NewAllocaAddress, Index: llvm/lib/Transforms/Utils/PromoteMemoryToRegister.cpp =================================================================== --- llvm/lib/Transforms/Utils/PromoteMemoryToRegister.cpp +++ llvm/lib/Transforms/Utils/PromoteMemoryToRegister.cpp @@ -103,7 +103,7 @@ bool OnlyUsedInOneBlock; Value *AllocaPointerVal; - DbgDeclareInst *DbgDeclare; + TinyPtrVector DbgDeclares; void clear() { DefiningBlocks.clear(); @@ -112,7 +112,7 @@ 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 @@ } } - DbgDeclare = FindAllocaDbgDeclare(AI); + DbgDeclares = FindDbgAddrUses(AI); } }; @@ -245,7 +245,7 @@ /// 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 AllocaDbgDeclares; + SmallVector, 8> AllocaDbgDeclares; /// The set of basic blocks the renamer has already visited. /// @@ -409,11 +409,11 @@ // 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 @@ while (!AI->use_empty()) { StoreInst *SI = cast(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 @@ 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 @@ } // 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 @@ } // 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 @@ // 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 @@ // 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); } } Index: llvm/test/DebugInfo/X86/dbg-addr-dse.ll =================================================================== --- /dev/null +++ llvm/test/DebugInfo/X86/dbg-addr-dse.ll @@ -0,0 +1,98 @@ +; 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) +; ASM: #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 +; DWARF-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) Index: llvm/test/DebugInfo/X86/dbg-addr.ll =================================================================== --- /dev/null +++ llvm/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) Index: llvm/test/Transforms/Mem2Reg/dbg-addr-inline-dse.ll =================================================================== --- /dev/null +++ llvm/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) Index: llvm/test/Transforms/Mem2Reg/dbg-addr.ll =================================================================== --- /dev/null +++ llvm/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) Index: llvm/test/Transforms/SROA/dbg-addr-diamond.ll =================================================================== --- /dev/null +++ llvm/test/Transforms/SROA/dbg-addr-diamond.ll @@ -0,0 +1,127 @@ +; RUN: opt -use-dbg-addr -sroa -S < %s | FileCheck %s + +; ModuleID = '' +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, !tbaa !29 + %y = getelementptr inbounds %struct.Pair, %struct.Pair* %p, i32 0, i32 1, !dbg !34 + store i32 %b, i32* %y, align 4, !dbg !35, !tbaa !36 + %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, !tbaa !29 + %y2 = getelementptr inbounds %struct.Pair, %struct.Pair* %p, i32 0, i32 1, !dbg !43 + store i32 %a, i32* %y2, align 4, !dbg !44, !tbaa !36 + 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, !tbaa !29 + %y4 = getelementptr inbounds %struct.Pair, %struct.Pair* %p, i32 0, i32 1, !dbg !49 + store i32 0, i32* %y4, align 4, !dbg !50, !tbaa !36 + 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, !tbaa.struct !52 + 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)