diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst --- a/llvm/docs/LangRef.rst +++ b/llvm/docs/LangRef.rst @@ -5159,6 +5159,11 @@ - ``DW_OP_push_object_address`` pushes the address of the object which can then serve as a descriptor in subsequent calculation. This opcode can be used to calculate bounds of fortran allocatable array which has array descriptors. +- ``DW_OP_LLVM_implicit_pointer, N`` can only appear at the + beginning of a ``DIExpression``, and it specifies the value of variable + represented by the ``DILocalVariable`` referred to by the instructions + value/address operand at offset N. +- ``DW_OP_LLVM_explicit_pointer`` it specifies the dereferenced value. DWARF specifies three kinds of simple location descriptions: Register, memory, and implicit location descriptions. Note that a location description is diff --git a/llvm/include/llvm/BinaryFormat/Dwarf.h b/llvm/include/llvm/BinaryFormat/Dwarf.h --- a/llvm/include/llvm/BinaryFormat/Dwarf.h +++ b/llvm/include/llvm/BinaryFormat/Dwarf.h @@ -118,10 +118,12 @@ #include "llvm/BinaryFormat/Dwarf.def" DW_OP_lo_user = 0xe0, DW_OP_hi_user = 0xff, - DW_OP_LLVM_fragment = 0x1000, ///< Only used in LLVM metadata. - DW_OP_LLVM_convert = 0x1001, ///< Only used in LLVM metadata. - DW_OP_LLVM_tag_offset = 0x1002, ///< Only used in LLVM metadata. - DW_OP_LLVM_entry_value = 0x1003, ///< Only used in LLVM metadata. + DW_OP_LLVM_fragment = 0x1000, ///< Only used in LLVM metadata. + DW_OP_LLVM_convert = 0x1001, ///< Only used in LLVM metadata. + DW_OP_LLVM_tag_offset = 0x1002, ///< Only used in LLVM metadata. + DW_OP_LLVM_entry_value = 0x1003, ///< Only used in LLVM metadata. + DW_OP_LLVM_implicit_pointer = 0x100c, ///< Only used in LLVM metadata. + DW_OP_LLVM_explicit_pointer = 0x100d, ///< Only used in LLVM metadata. }; enum TypeKind : uint8_t { diff --git a/llvm/include/llvm/CodeGen/GlobalISel/IRTranslator.h b/llvm/include/llvm/CodeGen/GlobalISel/IRTranslator.h --- a/llvm/include/llvm/CodeGen/GlobalISel/IRTranslator.h +++ b/llvm/include/llvm/CodeGen/GlobalISel/IRTranslator.h @@ -37,6 +37,7 @@ class CallLowering; class Constant; class DataLayout; +class DIBuilder; class FunctionLoweringInfo; class Instruction; class MachineBasicBlock; diff --git a/llvm/include/llvm/CodeGen/GlobalISel/MachineIRBuilder.h b/llvm/include/llvm/CodeGen/GlobalISel/MachineIRBuilder.h --- a/llvm/include/llvm/CodeGen/GlobalISel/MachineIRBuilder.h +++ b/llvm/include/llvm/CodeGen/GlobalISel/MachineIRBuilder.h @@ -403,6 +403,17 @@ const MDNode *Variable, const MDNode *Expr); + /// Build and insert a DBG_VALUE instructions specifying that the dereferenced + /// value of \p Variable is given by \p V (suitably modified by \p Expr). + MachineInstrBuilder buildDerefDbgValue(const Value *V, const MDNode *Variable, + const MDNode *Expr); + + /// Build and insert a DBG_VALUE instructions specifying that the dereferenced + /// value of \p Variable is given by \p V (suitably modified by \p Expr). + MachineInstrBuilder buildDerefDbgValue(const MDNode *DerefVar, + const MDNode *Variable, + const MDNode *Expr); + /// Build and insert a DBG_LABEL instructions specifying that \p Label is /// given. Convert "llvm.dbg.label Label" to "DBG_LABEL Label". MachineInstrBuilder buildDbgLabel(const MDNode *Label); diff --git a/llvm/include/llvm/CodeGen/MachineInstr.h b/llvm/include/llvm/CodeGen/MachineInstr.h --- a/llvm/include/llvm/CodeGen/MachineInstr.h +++ b/llvm/include/llvm/CodeGen/MachineInstr.h @@ -436,6 +436,10 @@ /// this DBG_VALUE instruction. MachineOperand &getDebugExpressionOp(); + /// Return the debug implicit variable referenced by + /// this DBG_VALUE instruction. + const DIVariable *getDebugImplicitVariable() const; + /// Return the complex address expression referenced by /// this DBG_VALUE instruction. const DIExpression *getDebugExpression() const; diff --git a/llvm/include/llvm/CodeGen/MachineInstrBuilder.h b/llvm/include/llvm/CodeGen/MachineInstrBuilder.h --- a/llvm/include/llvm/CodeGen/MachineInstrBuilder.h +++ b/llvm/include/llvm/CodeGen/MachineInstrBuilder.h @@ -235,6 +235,18 @@ return *this; } + const MachineInstrBuilder &addImplicitPtrMetadata(const MDNode *MD) const { + MI->addOperand(*MF, MachineOperand::CreateMetadata(MD)); + assert(((MI->isDebugValue()) + ? static_cast(MI->getDebugImplicitVariable()) + : true) && + "first MDNode argument of a DBG_VALUE not a DIVariable"); + assert( + (MI->isDebugLabel() ? static_cast(MI->getDebugLabel()) : true) && + "first MDNode argument of a DBG_LABEL not a label"); + return *this; + } + const MachineInstrBuilder &addCFIIndex(unsigned CFIIndex) const { MI->addOperand(*MF, MachineOperand::CreateCFIIndex(CFIIndex)); return *this; diff --git a/llvm/include/llvm/CodeGen/SelectionDAG.h b/llvm/include/llvm/CodeGen/SelectionDAG.h --- a/llvm/include/llvm/CodeGen/SelectionDAG.h +++ b/llvm/include/llvm/CodeGen/SelectionDAG.h @@ -1491,6 +1491,11 @@ unsigned VReg, bool IsIndirect, const DebugLoc &DL, unsigned O); + /// Creates an Implicit Pointer SDDbgValue node. + SDDbgValue *getImpPtrDbgValue(DIVariable *Var, DIExpression *Expr, + DIVariable *Val, const DebugLoc &DL, + unsigned O); + /// Creates a SDDbgLabel node. SDDbgLabel *getDbgLabel(DILabel *Label, const DebugLoc &DL, unsigned O); diff --git a/llvm/include/llvm/IR/DIBuilder.h b/llvm/include/llvm/IR/DIBuilder.h --- a/llvm/include/llvm/IR/DIBuilder.h +++ b/llvm/include/llvm/IR/DIBuilder.h @@ -617,6 +617,9 @@ DINode::DIFlags Flags = DINode::FlagZero, uint32_t AlignInBits = 0); + DILocalVariable *createImplicitTempVariable(DIScope *Scope, DIType *Ty, + uint32_t AlignInBits); + /// Create a new descriptor for an label. /// /// \c Scope must be a \a DILocalScope, and thus its scope chain eventually diff --git a/llvm/include/llvm/IR/DebugInfoMetadata.h b/llvm/include/llvm/IR/DebugInfoMetadata.h --- a/llvm/include/llvm/IR/DebugInfoMetadata.h +++ b/llvm/include/llvm/IR/DebugInfoMetadata.h @@ -2537,6 +2537,24 @@ /// Return whether this is a piece of an aggregate variable. bool isFragment() const { return getFragmentInfo().hasValue(); } + /// Return whether this is an Implicit Pointer + bool isImplicitPointer() const { + return getNumElements() > 0 && + (expr_op_begin()->getOp() == dwarf::DW_OP_LLVM_implicit_pointer); + } + + /// Return the number of indirction levels + unsigned explicitPointerCount() const { + unsigned Count = 0; + for (unsigned i = 0; i < getNumElements(); ++i) { + if (Elements[i] == dwarf::DW_OP_LLVM_explicit_pointer) + Count++; + else + break; + } + return Count; + } + /// Return whether this is an implicit location description. bool isImplicit() const; diff --git a/llvm/include/llvm/IR/Instruction.h b/llvm/include/llvm/IR/Instruction.h --- a/llvm/include/llvm/IR/Instruction.h +++ b/llvm/include/llvm/IR/Instruction.h @@ -33,6 +33,7 @@ namespace llvm { class BasicBlock; +class DbgVariableIntrinsic; class FastMathFlags; class MDNode; class Module; @@ -346,6 +347,22 @@ /// Sets the metadata on this instruction from the AAMDNodes structure. void setAAMetadata(const AAMDNodes &N); + /// Given DIExpression \p DIExpr operating on this instruction, write the + /// effects of this instruction into the returned DIExpression, or return + /// nullptr if it cannot be salvaged. \p StackVal: whether DW_OP_stack_value + /// should be appended to the expression. + DIExpression *salvageDebugInfoImpl(DIExpression *SrcDIExpr, + bool WithStackValue); + + /// Implementation of salvageDebugInfo, applying only to instructions in + /// \p DbgUsers, rather than all debug users of this instruction. + void salvageDebugInfoForDbgValues(ArrayRef DbgUsers); + + /// Assuming this instruction is going to be deleted, attempt to salvage + /// debug users by writing the effect of this instruction in a DIExpression. + /// Returns true if any debug users were updated. + void salvageDebugInfo(); + /// Retrieve the raw weight values of a conditional branch or select. /// Returns true on success with profile weights filled in. /// Returns false if no metadata or invalid metadata was found. diff --git a/llvm/include/llvm/IR/IntrinsicInst.h b/llvm/include/llvm/IR/IntrinsicInst.h --- a/llvm/include/llvm/IR/IntrinsicInst.h +++ b/llvm/include/llvm/IR/IntrinsicInst.h @@ -24,6 +24,7 @@ #define LLVM_IR_INTRINSICINST_H #include "llvm/IR/Constants.h" +#include "llvm/IR/DebugInfoMetadata.h" #include "llvm/IR/DerivedTypes.h" #include "llvm/IR/FPEnv.h" #include "llvm/IR/Function.h" @@ -98,6 +99,8 @@ /// variable's value or its address. Value *getVariableLocation(bool AllowNullOp = true) const; + Value *getDerefVariable(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 { @@ -124,6 +127,19 @@ /// is described. Optional getFragmentSizeInBits() const; + /// Returns true if it has an implicit pointer expression + bool isImplicitPointer() const { + return getExpression()->isImplicitPointer(); + } + + /// Returns true if it has an explicit pointer expression + unsigned explicitPointerCount() const { + if (!isa(getRawExpression())) + return false; + + return getExpression()->explicitPointerCount(); + } + /// \name Casting methods /// @{ static bool classof(const IntrinsicInst *I) { diff --git a/llvm/include/llvm/IR/Module.h b/llvm/include/llvm/IR/Module.h --- a/llvm/include/llvm/IR/Module.h +++ b/llvm/include/llvm/IR/Module.h @@ -41,6 +41,7 @@ namespace llvm { +class DbgVariableIntrinsic; class Error; class FunctionType; class GVMaterializer; @@ -197,6 +198,11 @@ ///< Format: (arch)(sub)-(vendor)-(sys0-(abi) NamedMDSymTabType NamedMDSymTab; ///< NamedMDNode names. DataLayout DL; ///< DataLayout associated with the module + /// Instruction combiner optimization deletes debugdeclare intrinsic, + /// cases when debugdeclare instrinsic is deleted from caller while callee + /// is later inlined, which makes opportunity to create DW_OP_implicit_pointer + /// we will preserve location of variable in DenseMap to use later + DenseMap VarAddrMap; friend class Constant; @@ -210,9 +216,13 @@ /// The module destructor. This will dropAllReferences. ~Module(); -/// @} -/// @name Module Level Accessors -/// @{ + void insertVarAddressMap(DbgVariableIntrinsic *DDI); + + Value *findVarAddressMap(Value *Var); + + /// @} + /// @name Module Level Accessors + /// @{ /// Get the module identifier which is, essentially, the name of the module. /// @returns the module identifier as a string diff --git a/llvm/include/llvm/IR/Value.h b/llvm/include/llvm/IR/Value.h --- a/llvm/include/llvm/IR/Value.h +++ b/llvm/include/llvm/IR/Value.h @@ -15,6 +15,7 @@ #include "llvm-c/Types.h" #include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallVector.h" #include "llvm/ADT/iterator_range.h" #include "llvm/IR/Use.h" #include "llvm/Support/Alignment.h" @@ -33,6 +34,7 @@ class ConstantData; class ConstantAggregate; class DataLayout; +class DbgVariableIntrinsic; class Function; class GlobalAlias; class GlobalIFunc; @@ -311,6 +313,9 @@ } } + /// Finds the debug info intrinsics describing this value. + void findDbgUsers(SmallVectorImpl &DbgUsers); + /// replaceUsesOutsideBlock - Go through the uses list for this definition and /// make each use point to "V" instead of "this" when the use is outside the /// block. 'This's use list is expected to have at least one element. diff --git a/llvm/include/llvm/Transforms/Scalar/SROA.h b/llvm/include/llvm/Transforms/Scalar/SROA.h --- a/llvm/include/llvm/Transforms/Scalar/SROA.h +++ b/llvm/include/llvm/Transforms/Scalar/SROA.h @@ -108,6 +108,18 @@ /// currently in the promotable queue. SetVector> SpeculatableSelects; + /// Describes the allocas introduced by rewritePartition in order to migrate + /// the debug info. + struct Fragment { + AllocaInst *Alloca; + uint64_t Offset; + uint64_t Size; + Fragment(AllocaInst *AI, uint64_t O, uint64_t S) + : Alloca(AI), Offset(O), Size(S) {} + }; + + SmallVector Fragments; + public: SROA() = default; @@ -125,6 +137,8 @@ bool presplitLoadsAndStores(AllocaInst &AI, sroa::AllocaSlices &AS); AllocaInst *rewritePartition(AllocaInst &AI, sroa::AllocaSlices &AS, sroa::Partition &P); + void salvageInstruction(Instruction *Ins); + void fixGEPWithSplitAllocas(); bool splitAlloca(AllocaInst &AI, sroa::AllocaSlices &AS); bool runOnAlloca(AllocaInst &AI); void clobberUse(Use &U); diff --git a/llvm/include/llvm/Transforms/Utils/Local.h b/llvm/include/llvm/Transforms/Utils/Local.h --- a/llvm/include/llvm/Transforms/Utils/Local.h +++ b/llvm/include/llvm/Transforms/Utils/Local.h @@ -286,9 +286,6 @@ /// Finds the llvm.dbg.value intrinsics describing a value. void findDbgValues(SmallVectorImpl &DbgValues, Value *V); -/// Finds the debug info intrinsics describing a value. -void findDbgUsers(SmallVectorImpl &DbgInsts, Value *V); - /// Replaces llvm.dbg.declare instruction when the address it /// describes is replaced with a new value. If Deref is true, an /// additional DW_OP_deref is prepended to the expression. If Offset @@ -309,26 +306,6 @@ AllocaInst *findAllocaForValue(Value *V, DenseMap &AllocaForValue); -/// Assuming the instruction \p I is going to be deleted, attempt to salvage -/// debug users of \p I by writing the effect of \p I in a DIExpression. If it -/// cannot be salvaged changes its debug uses to undef. -void salvageDebugInfo(Instruction &I); - - -/// Implementation of salvageDebugInfo, applying only to instructions in -/// \p Insns, rather than all debug users from findDbgUsers( \p I). -/// Returns true if any debug users were updated. -/// Mark undef if salvaging cannot be completed. -void salvageDebugInfoForDbgValues(Instruction &I, - ArrayRef Insns); - -/// Given an instruction \p I and DIExpression \p DIExpr operating on it, write -/// the effects of \p I into the returned DIExpression, or return nullptr if -/// it cannot be salvaged. \p StackVal: whether DW_OP_stack_value should be -/// appended to the expression. -DIExpression *salvageDebugInfoImpl(Instruction &I, DIExpression *DIExpr, - bool StackVal); - /// Point debug users of \p From to \p To or salvage them. Use this function /// only when replacing all uses of \p From with \p To, with a guarantee that /// \p From is going to be deleted. diff --git a/llvm/lib/BinaryFormat/Dwarf.cpp b/llvm/lib/BinaryFormat/Dwarf.cpp --- a/llvm/lib/BinaryFormat/Dwarf.cpp +++ b/llvm/lib/BinaryFormat/Dwarf.cpp @@ -151,6 +151,10 @@ return "DW_OP_LLVM_tag_offset"; case DW_OP_LLVM_entry_value: return "DW_OP_LLVM_entry_value"; + case DW_OP_LLVM_implicit_pointer: + return "DW_OP_LLVM_implicit_pointer"; + case DW_OP_LLVM_explicit_pointer: + return "DW_OP_LLVM_explicit_pointer"; } } @@ -163,6 +167,8 @@ .Case("DW_OP_LLVM_fragment", DW_OP_LLVM_fragment) .Case("DW_OP_LLVM_tag_offset", DW_OP_LLVM_tag_offset) .Case("DW_OP_LLVM_entry_value", DW_OP_LLVM_entry_value) + .Case("DW_OP_LLVM_implicit_pointer", DW_OP_LLVM_implicit_pointer) + .Case("DW_OP_LLVM_explicit_pointer", DW_OP_LLVM_explicit_pointer) .Default(0); } diff --git a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp --- a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp @@ -917,6 +917,8 @@ auto Op = MI->getDebugOperand(0); OS << "!target-index(" << Op.getIndex() << "," << Op.getOffset() << ")"; return true; + } else if (MI->getOperand(0).isMetadata()) { + OS << MI->getOperand(0).getMetadata(); } else { Register Reg; if (MI->getDebugOperand(0).isReg()) { diff --git a/llvm/lib/CodeGen/AsmPrinter/DebugLocEntry.h b/llvm/lib/CodeGen/AsmPrinter/DebugLocEntry.h --- a/llvm/lib/CodeGen/AsmPrinter/DebugLocEntry.h +++ b/llvm/lib/CodeGen/AsmPrinter/DebugLocEntry.h @@ -45,11 +45,12 @@ E_Integer, E_ConstantFP, E_ConstantInt, - E_TargetIndexLocation + E_TargetIndexLocation, + E_ImplicitPtr }; enum EntryType EntryKind; - /// Either a constant, + /// a constant, union { int64_t Int; const ConstantFP *CFP; @@ -63,6 +64,9 @@ TargetIndexLocation TIL; }; + /// Or an implicit pointer variable target. + const DIVariable *ImpPtr; + public: DbgValueLoc(const DIExpression *Expr, int64_t i) : Expression(Expr), EntryKind(E_Integer) { @@ -82,6 +86,10 @@ } DbgValueLoc(const DIExpression *Expr, TargetIndexLocation Loc) : Expression(Expr), EntryKind(E_TargetIndexLocation), TIL(Loc) {} + DbgValueLoc(const DIExpression *Expr, const DIVariable *ImpPtr) + : Expression(Expr), EntryKind(E_ImplicitPtr), ImpPtr(ImpPtr) { + assert(cast(Expr)->isValid()); + } bool isLocation() const { return EntryKind == E_Location; } bool isTargetIndexLocation() const { @@ -90,11 +98,13 @@ bool isInt() const { return EntryKind == E_Integer; } bool isConstantFP() const { return EntryKind == E_ConstantFP; } bool isConstantInt() const { return EntryKind == E_ConstantInt; } + bool isImplicitPtr() const { return EntryKind == E_ImplicitPtr; } int64_t getInt() const { return Constant.Int; } const ConstantFP *getConstantFP() const { return Constant.CFP; } const ConstantInt *getConstantInt() const { return Constant.CIP; } MachineLocation getLoc() const { return Loc; } TargetIndexLocation getTargetIndexLocation() const { return TIL; } + const DIVariable *getImplicitPointer() const { return ImpPtr; } bool isFragment() const { return getExpression()->isFragment(); } bool isEntryVal() const { return getExpression()->isEntryValue(); } const DIExpression *getExpression() const { return Expression; } @@ -200,6 +210,8 @@ return A.Constant.CFP == B.Constant.CFP; case DbgValueLoc::E_ConstantInt: return A.Constant.CIP == B.Constant.CIP; + case DbgValueLoc::E_ImplicitPtr: + return A.ImpPtr == B.ImpPtr; } llvm_unreachable("unhandled EntryKind"); } diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.h b/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.h --- a/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.h +++ b/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.h @@ -74,6 +74,15 @@ // List of ranges for a given compile unit. SmallVector CURanges; + // First operand of DW_OP_implicit_pointer operation is a reference to a + // debugging information entry that describes the dereferenced object’s value. + // When this operation is created, the debugging information entry is not yet + // formed. To solve this problem, we maintain a vector of dereferenced + // objects. We keep index of dereferenced object's at first operand of + // DW_OP_implicit_pointer operation temporarily. This temporary value is + // later replaced by actual value in function DwarfDebug::emitDebugLocEntry. + SmallVector ImplicitVars; + // The base address of this unit, if any. Used for relative references in // ranges/locs. const MCSymbol *BaseAddress = nullptr; @@ -120,6 +129,20 @@ void initStmtList(); + unsigned findOrInsertImplicitVar(const DINode *Var) { + SmallVector::iterator It = + std::find(ImplicitVars.begin(), ImplicitVars.end(), Var); + if (It == ImplicitVars.end()) { + ImplicitVars.push_back(Var); + return ImplicitVars.size() - 1; + } + + return std::distance(ImplicitVars.begin(), It); + } + + const DINode *findImplicitVarAtIndex(unsigned Idx) const { + return ImplicitVars[Idx]; + } /// Apply the DW_AT_stmt_list from this compile unit to the specified DIE. void applyStmtList(DIE &D); diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp --- a/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/DwarfCompileUnit.cpp @@ -1118,7 +1118,8 @@ DIEDwarfExpression DwarfExpr(*Asm, *this, *Loc); DwarfExpr.setCallSiteParamValueFlag(); - DwarfDebug::emitDebugLocValue(*Asm, nullptr, Param.getValue(), DwarfExpr); + DwarfDebug::emitDebugLocValue(*Asm, nullptr, Param.getValue(), DwarfExpr, + *this); addBlock(*CallSiteDieParam, getDwarf5OrGNUAttr(dwarf::DW_AT_call_value), DwarfExpr.finalize()); diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.h b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.h --- a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.h +++ b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.h @@ -783,7 +783,8 @@ static void emitDebugLocValue(const AsmPrinter &AP, const DIBasicType *BT, const DbgValueLoc &Value, - DwarfExpression &DwarfExpr); + DwarfExpression &DwarfExpr, + DwarfCompileUnit &TheCU); }; } // end namespace llvm diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp --- a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp @@ -201,6 +201,26 @@ getActiveStreamer().emitULEB128(Idx, Twine(Idx), ULEB128PadSize); } +void DebugLocDwarfExpression::emitAddressRef(uint64_t Idx) { + uint8_t Val; + uint8_t NumBytes; + + switch (CU.getAsmPrinter()->OutStreamer->getContext().getDwarfFormat()) { + case dwarf::DWARF32: + NumBytes = 4; + break; + case dwarf::DWARF64: + NumBytes = 8; + break; + } + + while (NumBytes--) { + Val = Idx & 0xff; + Idx = Idx >> 8; + getActiveStreamer().EmitInt8(Val, Twine(Idx)); + } +} + bool DebugLocDwarfExpression::isFrameRegister(const TargetRegisterInfo &TRI, unsigned MachineReg) { // This information is not available while emitting .debug_loc entries. @@ -261,6 +281,11 @@ return DbgValueLoc(Expr, MI->getDebugOperand(0).getFPImm()); if (MI->getDebugOperand(0).isCImm()) return DbgValueLoc(Expr, MI->getDebugOperand(0).getCImm()); + if (Expr->isImplicitPointer()) { + const DIVariable *DVI = + cast(MI->getDebugOperand(0).getMetadata()); + return DbgValueLoc(Expr, DVI); + } llvm_unreachable("Unexpected 4-operand DBG_VALUE instruction!"); } @@ -1817,10 +1842,13 @@ // Check if there is a single DBG_VALUE, valid throughout the var's scope. // If the history map contains a single debug value, there may be an // additional entry which clobbers the debug value. + // In case of implicit pointer, implicit target can not initialize + // variable so we need to create location list even in case of single value size_t HistSize = HistoryMapEntries.size(); bool SingleValueWithClobber = HistSize == 2 && HistoryMapEntries[1].isClobber(); - if (HistSize == 1 || SingleValueWithClobber) { + if (!MInsn->getDebugExpression()->isImplicitPointer() && + (HistSize == 1 || SingleValueWithClobber)) { const auto *End = SingleValueWithClobber ? HistoryMapEntries[1].getInstr() : nullptr; if (VeryLargeBlocks.count(MInsn->getParent()) == 0 && @@ -1845,7 +1873,10 @@ // Check whether buildLocationList managed to merge all locations to one // that is valid throughout the variable's scope. If so, produce single // value location. - if (isValidSingleLocation) { + // In case of implicit pointer, implicit target can not initialize + // variable so we need to create location list even in case of single value + if (!MInsn->getDebugExpression()->isImplicitPointer() && + isValidSingleLocation) { RegVar->initializeDbgValue(Entries[0].getValues()[0]); continue; } @@ -2472,6 +2503,27 @@ for (unsigned J = 0; J < ULEB128PadSize; ++J) if (Comment != End) Comment++; + } else if ((Op.getCode() == dwarf::DW_OP_implicit_pointer) && + (Op.getDescription().Op[I] == Encoding::SizeRefAddr)) { + // Till now we do not have actual offset from debug_info section + // all we have is index in a list of variable, we can now compute + // actual offset + unsigned ValOffset = 0; + // read the dummy offset (index) + for (uint64_t J = Op.getOperandEndOffset(I) - 1; J >= Offset; --J) + ValOffset = (ValOffset << 8) + (uint8_t)Data.getData()[J]; + // get the variable and compute the actual offset + const DINode *ImpVar = CU->findImplicitVarAtIndex(ValOffset); + DIE *Die = CU->getDIE(ImpVar); + if (Die) + ValOffset = Die->getDebugSectionOffset(); + + // emit the actual offset + for (uint64_t J = Offset; J < Op.getOperandEndOffset(I); ++J) { + Streamer.EmitInt8(ValOffset & 0xff, + Comment != End ? *(Comment++) : ""); + ValOffset = ValOffset >> 8; + } } else { for (uint64_t J = Offset; J < Op.getOperandEndOffset(I); ++J) Streamer.EmitInt8(Data.getData()[J], Comment != End ? *(Comment++) : ""); @@ -2484,10 +2536,12 @@ void DwarfDebug::emitDebugLocValue(const AsmPrinter &AP, const DIBasicType *BT, const DbgValueLoc &Value, - DwarfExpression &DwarfExpr) { + DwarfExpression &DwarfExpr, + DwarfCompileUnit &TheCU) { auto *DIExpr = Value.getExpression(); DIExpressionCursor ExprCursor(DIExpr); DwarfExpr.addFragmentOffset(DIExpr); + // Regular entry. if (Value.isInt()) { if (BT && (BT->getEncoding() == dwarf::DW_ATE_signed || @@ -2515,7 +2569,15 @@ } else if (Value.isConstantFP()) { APInt RawBytes = Value.getConstantFP()->getValueAPF().bitcastToAPInt(); DwarfExpr.addUnsignedConstant(RawBytes); + } else if (Value.isImplicitPtr()) { + const llvm::DIVariable *ImpVar = Value.getImplicitPointer(); + unsigned ValOffset = TheCU.findOrInsertImplicitVar(ImpVar); + // This works as dummy offset in first operand of DW_OP_implicit_pointer + // this will later be replace by actual offset of variable in .debug_info + DwarfExpr.emitImplicitPointer(std::move(ExprCursor), ValOffset); + return; } + DwarfExpr.addExpression(std::move(ExprCursor)); } @@ -2538,11 +2600,11 @@ assert(llvm::is_sorted(Values) && "fragments are expected to be sorted"); for (auto Fragment : Values) - DwarfDebug::emitDebugLocValue(AP, BT, Fragment, DwarfExpr); + DwarfDebug::emitDebugLocValue(AP, BT, Fragment, DwarfExpr, TheCU); } else { assert(Values.size() == 1 && "only fragments may have >1 value"); - DwarfDebug::emitDebugLocValue(AP, BT, Value, DwarfExpr); + DwarfDebug::emitDebugLocValue(AP, BT, Value, DwarfExpr, TheCU); } DwarfExpr.finalize(); if (DwarfExpr.TagOffset) diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfExpression.h b/llvm/lib/CodeGen/AsmPrinter/DwarfExpression.h --- a/llvm/lib/CodeGen/AsmPrinter/DwarfExpression.h +++ b/llvm/lib/CodeGen/AsmPrinter/DwarfExpression.h @@ -196,6 +196,8 @@ virtual void emitBaseTypeRef(uint64_t Idx) = 0; + virtual void emitAddressRef(uint64_t Idx) = 0; + /// Start emitting data to the temporary buffer. The data stored in the /// temporary buffer can be committed to the main output using /// commitTemporaryBuffer(). @@ -353,6 +355,8 @@ /// Emit location information expressed via WebAssembly location + offset /// The Index is an identifier for locals, globals or operand stack. void addWasmLocation(unsigned Index, uint64_t Offset); + + void emitImplicitPointer(DIExpressionCursor &&Expr, unsigned ValOffset); }; /// DwarfExpression implementation for .debug_loc entries. @@ -378,6 +382,7 @@ void emitUnsigned(uint64_t Value) override; void emitData1(uint8_t Value) override; void emitBaseTypeRef(uint64_t Idx) override; + void emitAddressRef(uint64_t Idx) override; void enableTemporaryBuffer() override; void disableTemporaryBuffer() override; @@ -408,6 +413,7 @@ void emitUnsigned(uint64_t Value) override; void emitData1(uint8_t Value) override; void emitBaseTypeRef(uint64_t Idx) override; + void emitAddressRef(uint64_t Idx) override{}; void enableTemporaryBuffer() override; void disableTemporaryBuffer() override; diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfExpression.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfExpression.cpp --- a/llvm/lib/CodeGen/AsmPrinter/DwarfExpression.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/DwarfExpression.cpp @@ -636,3 +636,17 @@ emitUnsigned(Index); emitUnsigned(Offset); } + +void DwarfExpression::emitImplicitPointer(DIExpressionCursor &&ExprCursor, + unsigned ValOffset) { + auto Op = ExprCursor.take(); + assert(Op->getOp() == dwarf::DW_OP_LLVM_implicit_pointer && + "not dwarf::DW_OP_LLVM_implicit_pointer"); + + emitOp(dwarf::DW_OP_implicit_pointer); + // This is a dummy offset which will be replaced with actual offset in + // function DwarfDebug::emitDebugLocEntry. + emitAddressRef(ValOffset); + emitSigned(Op->getArg(0)); + return; +} diff --git a/llvm/lib/CodeGen/CodeGenPrepare.cpp b/llvm/lib/CodeGen/CodeGenPrepare.cpp --- a/llvm/lib/CodeGen/CodeGenPrepare.cpp +++ b/llvm/lib/CodeGen/CodeGenPrepare.cpp @@ -1206,7 +1206,6 @@ // If we removed all uses, nuke the cast. if (CI->use_empty()) { - salvageDebugInfo(*CI); CI->eraseFromParent(); MadeChange = true; } @@ -1869,7 +1868,6 @@ // If we removed all uses, or there are none, nuke the shift. if (ShiftI->use_empty()) { - salvageDebugInfo(*ShiftI); ShiftI->eraseFromParent(); MadeChange = true; } diff --git a/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp b/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp --- a/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp +++ b/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp @@ -42,6 +42,7 @@ #include "llvm/IR/CFG.h" #include "llvm/IR/Constant.h" #include "llvm/IR/Constants.h" +#include "llvm/IR/DIBuilder.h" #include "llvm/IR/DataLayout.h" #include "llvm/IR/DebugInfo.h" #include "llvm/IR/DerivedTypes.h" @@ -53,6 +54,7 @@ #include "llvm/IR/Intrinsics.h" #include "llvm/IR/LLVMContext.h" #include "llvm/IR/Metadata.h" +#include "llvm/IR/Module.h" #include "llvm/IR/Type.h" #include "llvm/IR/User.h" #include "llvm/IR/Value.h" @@ -1444,24 +1446,66 @@ // This form of DBG_VALUE is target-independent. const DbgValueInst &DI = cast(CI); const Value *V = DI.getValue(); + const DIExpression *DE = DI.getExpression(); assert(DI.getVariable()->isValidLocationForIntrinsic( MIRBuilder.getDebugLoc()) && "Expected inlined-at fields to agree"); + DILocalVariable *Var = DI.getVariable(); + DILocalVariable *PtrVar = nullptr; + Module *Mod = const_cast(CI.getModule()); + + DIBuilder DIB(*Mod, /*AllowUnresolved*/ false); + + unsigned Level = DE->explicitPointerCount(); + SmallVector PtrVars; + if (Level > 0) { + DIScope *Scope = DI.getDebugLoc()->getScope(); + DIType *Type = DI.getVariable()->getType(); + PtrVar = Var; + unsigned Count = Level; + while (Count-- > 0) { + if (DIDerivedType *DT = dyn_cast(Type)) + Type = DT->getBaseType(); + uint32_t AlignInBits = DI.getVariable()->getAlignInBytes(); + Var = DIB.createImplicitTempVariable(Scope, Type, AlignInBits); + PtrVars.push_back(Var); + } + PtrVars.push_back(PtrVar); + Var = PtrVars[0]; + SmallVector Ops; + DE = DIExpression::get(DE->getContext(), Ops); + } else { + Var = DI.getVariable(); + DE = DI.getExpression(); + } + if (!V) { // Currently the optimizer can produce this; insert an undef to // help debugging. Probably the optimizer should not do this. - MIRBuilder.buildIndirectDbgValue(0, DI.getVariable(), DI.getExpression()); + MIRBuilder.buildIndirectDbgValue(0, Var, DE); } else if (const auto *CI = dyn_cast(V)) { - MIRBuilder.buildConstDbgValue(*CI, DI.getVariable(), DI.getExpression()); + MIRBuilder.buildConstDbgValue(*CI, DI.getVariable(), DE); + } else if (DE->isImplicitPointer()) { + MIRBuilder.buildDerefDbgValue(V, DI.getVariable(), DI.getExpression()); } else { for (Register Reg : getOrCreateVRegs(*V)) { // FIXME: This does not handle register-indirect values at offset 0. The // direct/indirect thing shouldn't really be handled by something as // implicit as reg+noreg vs reg+imm in the first place, but it seems // pretty baked in right now. - MIRBuilder.buildDirectDbgValue(Reg, DI.getVariable(), DI.getExpression()); + MIRBuilder.buildDirectDbgValue(Reg, Var, DE); } } + unsigned Count = 0; + if (Count++ < Level) { + SmallVector Ops; + Ops.push_back(dwarf::DW_OP_LLVM_implicit_pointer); + Ops.push_back(0); + DIExpression *DIExprNew = DIExpression::get(DE->getContext(), Ops); + + MIRBuilder.buildDerefDbgValue(PtrVars[Count - 1], PtrVars[Count], + DIExprNew); + } return true; } case Intrinsic::uadd_with_overflow: diff --git a/llvm/lib/CodeGen/GlobalISel/MachineIRBuilder.cpp b/llvm/lib/CodeGen/GlobalISel/MachineIRBuilder.cpp --- a/llvm/lib/CodeGen/GlobalISel/MachineIRBuilder.cpp +++ b/llvm/lib/CodeGen/GlobalISel/MachineIRBuilder.cpp @@ -114,6 +114,49 @@ return insertInstr(MIB); } +MachineInstrBuilder MachineIRBuilder::buildDerefDbgValue(const Value *Derefval, + const MDNode *Variable, + const MDNode *Expr) { + assert(isa(Variable) && "not a variable"); + assert(cast(Expr)->isValid() && "not an expression"); + assert( + cast(Variable)->isValidLocationForIntrinsic(getDL()) && + "Expected inlined-at fields to agree"); + auto MIB = buildInstr(TargetOpcode::DBG_VALUE); + if (const auto *MDV = dyn_cast(Derefval)) { + if (const auto *LDV = dyn_cast(MDV->getMetadata())) { + MIB.addImplicitPtrMetadata(LDV); + } + } else if (auto *CI = dyn_cast(Derefval)) { + if (CI->getBitWidth() > 64) + MIB.addCImm(CI); + else + MIB.addImm(CI->getZExtValue()); + } else if (auto *CFP = dyn_cast(Derefval)) { + MIB.addFPImm(CFP); + } else { + // Insert %noreg if we didn't find a usable constant and had to drop it. + MIB.addReg(0U); + } + + return MIB.addReg(0).addMetadata(Variable).addMetadata(Expr); +} + +MachineInstrBuilder MachineIRBuilder::buildDerefDbgValue(const MDNode *DerefVar, + const MDNode *Variable, + const MDNode *Expr) { + assert(isa(Variable) && "not a variable"); + assert(cast(Expr)->isValid() && "not an expression"); + assert( + cast(Variable)->isValidLocationForIntrinsic(getDL()) && + "Expected inlined-at fields to agree"); + auto MIB = buildInstr(TargetOpcode::DBG_VALUE); + + MIB.addImplicitPtrMetadata(DerefVar); + + return MIB.addReg(0).addMetadata(Variable).addMetadata(Expr); +} + MachineInstrBuilder MachineIRBuilder::buildDbgLabel(const MDNode *Label) { assert(isa(Label) && "not a label"); assert(cast(Label)->isValidLocationForIntrinsic(State.DL) && diff --git a/llvm/lib/CodeGen/LiveDebugValues.cpp b/llvm/lib/CodeGen/LiveDebugValues.cpp --- a/llvm/lib/CodeGen/LiveDebugValues.cpp +++ b/llvm/lib/CodeGen/LiveDebugValues.cpp @@ -310,7 +310,8 @@ ImmediateKind, EntryValueKind, EntryValueBackupKind, - EntryValueCopyBackupKind + EntryValueCopyBackupKind, + ImplicitPtrKind } Kind = InvalidKind; /// The value location. Stored separately to avoid repeatedly @@ -322,6 +323,7 @@ int64_t Immediate; const ConstantFP *FPImm; const ConstantInt *CImm; + const DIVariable *ImplicitPtr; } Loc; VarLoc(const MachineInstr &MI, LexicalScopes &LS) @@ -344,6 +346,9 @@ } else if (MI.getDebugOperand(0).isCImm()) { Kind = ImmediateKind; Loc.CImm = MI.getDebugOperand(0).getCImm(); + } else if (MI.getDebugOperand(0).isMetadata()) { + Kind = ImplicitPtrKind; + Loc.ImplicitPtr = MI.getDebugImplicitVariable(); } // We create the debug entry values from the factory functions rather than @@ -451,6 +456,10 @@ MachineOperand MO = MI.getDebugOperand(0); return BuildMI(MF, DbgLoc, IID, Indirect, MO, Var, DIExpr); } + case ImplicitPtrKind: { + MachineOperand MO = MI.getOperand(0); + return BuildMI(MF, DbgLoc, IID, Indirect, MO, Var, DIExpr); + } case EntryValueBackupKind: case EntryValueCopyBackupKind: case InvalidKind: @@ -516,6 +525,9 @@ case ImmediateKind: Out << Loc.Immediate; break; + case ImplicitPtrKind: + dbgs() << Loc.ImplicitPtr->getName(); + break; case InvalidKind: llvm_unreachable("Invalid VarLoc in dump method"); } @@ -1090,7 +1102,8 @@ } if (isDbgValueDescribedByReg(MI) || MI.getDebugOperand(0).isImm() || - MI.getDebugOperand(0).isFPImm() || MI.getDebugOperand(0).isCImm()) { + MI.getDebugOperand(0).isFPImm() || MI.getDebugOperand(0).isCImm() || + MI.getOperand(0).isMetadata()) { // Use normal VarLoc constructor for registers and immediates. VarLoc VL(MI, LS); // End all previous ranges of VL.Var. diff --git a/llvm/lib/CodeGen/MachineInstr.cpp b/llvm/lib/CodeGen/MachineInstr.cpp --- a/llvm/lib/CodeGen/MachineInstr.cpp +++ b/llvm/lib/CodeGen/MachineInstr.cpp @@ -858,6 +858,11 @@ return getOperand(3); } +const DIVariable *MachineInstr::getDebugImplicitVariable() const { + assert(isDebugValue() && "not a DBG_VALUE"); + return cast(getOperand(0).getMetadata()); +} + const DIExpression *MachineInstr::getDebugExpression() const { assert(isDebugValue() && "not a DBG_VALUE"); return cast(getOperand(3).getMetadata()); diff --git a/llvm/lib/CodeGen/SelectionDAG/FastISel.cpp b/llvm/lib/CodeGen/SelectionDAG/FastISel.cpp --- a/llvm/lib/CodeGen/SelectionDAG/FastISel.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/FastISel.cpp @@ -71,6 +71,7 @@ #include "llvm/IR/CallingConv.h" #include "llvm/IR/Constant.h" #include "llvm/IR/Constants.h" +#include "llvm/IR/DIBuilder.h" #include "llvm/IR/DataLayout.h" #include "llvm/IR/DebugInfo.h" #include "llvm/IR/DebugLoc.h" @@ -1434,42 +1435,89 @@ // This form of DBG_VALUE is target-independent. const DbgValueInst *DI = cast(II); const MCInstrDesc &II = TII.get(TargetOpcode::DBG_VALUE); - const Value *V = DI->getValue(); + const DIExpression *DE = DI->getExpression(); + DILocalVariable *Var = DI->getVariable(); + DILocalVariable *PtrVar = nullptr; + unsigned Level = DE->explicitPointerCount(); + const Value *V = (Level > 0) ? DI->getDerefVariable(false) + : DI->getVariableLocation(false); assert(DI->getVariable()->isValidLocationForIntrinsic(DbgLoc) && "Expected inlined-at fields to agree"); + + Module *Mod = const_cast(DI->getModule()); + DIBuilder DIB(*Mod, /*AllowUnresolved*/ false); + + SmallVector PtrVars; + if (Level > 0) { + DIScope *Scope = DI->getDebugLoc()->getScope(); + DIType *Type = DI->getVariable()->getType(); + unsigned Count = 0; + while (Count++ < Level) { + if (DIDerivedType *DT = dyn_cast(Type)) + Type = DT->getBaseType(); + uint32_t AlignInBits = DI->getVariable()->getAlignInBytes(); + + PtrVar = DIB.createImplicitTempVariable(Scope, Type, AlignInBits); + PtrVars.push_back(PtrVar); + } + PtrVars.push_back(Var); + + Var = PtrVars[0]; + SmallVector Ops; + DE = DIExpression::get(DE->getContext(), Ops); + } else { + Var = DI->getVariable(); + DE = DI->getExpression(); + } + if (!V || isa(V)) { // Currently the optimizer can produce this; insert an undef to // help debugging. - BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, II, false, 0U, - DI->getVariable(), DI->getExpression()); + BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, II, false, 0U, Var, DE); } else if (const auto *CI = dyn_cast(V)) { if (CI->getBitWidth() > 64) BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, II) .addCImm(CI) .addImm(0U) - .addMetadata(DI->getVariable()) - .addMetadata(DI->getExpression()); + .addMetadata(Var) + .addMetadata(DE); else BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, II) .addImm(CI->getZExtValue()) .addImm(0U) - .addMetadata(DI->getVariable()) - .addMetadata(DI->getExpression()); + .addMetadata(Var) + .addMetadata(DE); } else if (const auto *CF = dyn_cast(V)) { BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, II) .addFPImm(CF) .addImm(0U) - .addMetadata(DI->getVariable()) - .addMetadata(DI->getExpression()); + .addMetadata(Var) + .addMetadata(DE); } else if (Register Reg = lookUpRegForValue(V)) { // FIXME: This does not handle register-indirect values at offset 0. bool IsIndirect = false; BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, II, IsIndirect, Reg, - DI->getVariable(), DI->getExpression()); + Var, DE); } else { // We don't know how to handle other cases, so we drop. LLVM_DEBUG(dbgs() << "Dropping debug info for " << *DI << "\n"); } + + if (!PtrVars.empty()) { + unsigned Count = 0; + while (Count++ < Level) { + SmallVector Ops; + Ops.push_back(dwarf::DW_OP_LLVM_implicit_pointer); + Ops.push_back(0); + DIExpression *DIExprNew = DIExpression::get(DE->getContext(), Ops); + + BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, II) + .addImplicitPtrMetadata(PtrVars[Count - 1]) + .addReg(0U) + .addMetadata(PtrVars[Count]) + .addMetadata(DIExprNew); + } + } return true; } case Intrinsic::dbg_label: { diff --git a/llvm/lib/CodeGen/SelectionDAG/InstrEmitter.cpp b/llvm/lib/CodeGen/SelectionDAG/InstrEmitter.cpp --- a/llvm/lib/CodeGen/SelectionDAG/InstrEmitter.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/InstrEmitter.cpp @@ -742,6 +742,10 @@ // dropped. MIB.addReg(0U); } + } else if (SD->getKind() == SDDbgValue::IMPPTR) { + const MDNode *DV = SD->getImplicitPointer(); + // insert implicit pointer + MIB.addImplicitPtrMetadata(DV); } else { // Insert an Undef so we can see what we dropped. MIB.addReg(0U); diff --git a/llvm/lib/CodeGen/SelectionDAG/SDNodeDbgValue.h b/llvm/lib/CodeGen/SelectionDAG/SDNodeDbgValue.h --- a/llvm/lib/CodeGen/SelectionDAG/SDNodeDbgValue.h +++ b/llvm/lib/CodeGen/SelectionDAG/SDNodeDbgValue.h @@ -30,11 +30,13 @@ class SDDbgValue { public: enum DbgValueKind { - SDNODE = 0, ///< Value is the result of an expression. - CONST = 1, ///< Value is a constant. - FRAMEIX = 2, ///< Value is contents of a stack location. - VREG = 3 ///< Value is a virtual register. + SDNODE = 0, ///< Value is the result of an expression. + CONST = 1, ///< Value is a constant. + FRAMEIX = 2, ///< Value is contents of a stack location. + VREG = 3, ///< Value is a virtual register. + IMPPTR = 4 ///< Value is an implicit pointer. }; + private: union { struct { @@ -44,6 +46,7 @@ const Value *Const; ///< Valid for constants. unsigned FrameIx; ///< Valid for stack objects. unsigned VReg; ///< Valid for registers. + DIVariable *ImpPtr; } u; DIVariable *Var; DIExpression *Expr; @@ -86,6 +89,14 @@ u.FrameIx = VRegOrFrameIdx; } + /// Constructor for Implicit Pointer. + SDDbgValue(DIVariable *Var, DIExpression *Expr, DIVariable *Val, DebugLoc dl, + unsigned O) + : Var(Var), Expr(Expr), DL(std::move(dl)), Order(O), IsIndirect(false) { + kind = IMPPTR; + u.ImpPtr = Val; + } + /// Returns the kind. DbgValueKind getKind() const { return kind; } @@ -110,6 +121,12 @@ /// Returns the Virtual Register for a VReg unsigned getVReg() const { assert (kind==VREG); return u.VReg; } + /// Returns the implicit pointer target + DIVariable *getImplicitPointer() const { + assert(kind == IMPPTR); + return u.ImpPtr; + } + /// Returns whether this is an indirect value. bool isIndirect() const { return IsIndirect; } diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp @@ -8304,6 +8304,15 @@ SDDbgValue(Var, Expr, VReg, IsIndirect, DL, O, SDDbgValue::VREG); } +/// Implicit pointer +SDDbgValue *SelectionDAG::getImpPtrDbgValue(DIVariable *Var, DIExpression *Expr, + DIVariable *Val, const DebugLoc &DL, + unsigned O) { + assert(cast(Var)->isValidLocationForIntrinsic(DL) && + "Expected inlined-at fields to agree"); + return new (DbgInfo->getAlloc()) SDDbgValue(Var, Expr, Val, DL, O); +} + void SelectionDAG::transferDbgValues(SDValue From, SDValue To, unsigned OffsetInBits, unsigned SizeInBits, bool InvalidateDbg) { diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.h b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.h --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.h +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.h @@ -507,6 +507,9 @@ /// this cannot be done, produce an Undef debug value record. void salvageUnresolvedDbgValue(DanglingDebugInfo &DDI); + bool handleDebugValueWrapper(const DbgValueInst &DI, DebugLoc dl, + unsigned Order); + /// For a given Value, attempt to create and record a SDDbgValue in the /// SelectionDAG. bool handleDebugValue(const Value *V, DILocalVariable *Var, diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp @@ -73,6 +73,7 @@ #include "llvm/IR/Constant.h" #include "llvm/IR/ConstantRange.h" #include "llvm/IR/Constants.h" +#include "llvm/IR/DIBuilder.h" #include "llvm/IR/DataLayout.h" #include "llvm/IR/DebugInfoMetadata.h" #include "llvm/IR/DebugLoc.h" @@ -1185,7 +1186,7 @@ void SelectionDAGBuilder::dropDanglingDebugInfo(const DILocalVariable *Variable, const DIExpression *Expr) { auto isMatchingDbgValue = [&](DanglingDebugInfo &DDI) { - const DbgValueInst *DI = DDI.getDI(); + const DbgVariableIntrinsic *DI = DDI.getDI(); DIVariable *DanglingVariable = DI->getVariable(); DIExpression *DanglingExpr = DI->getExpression(); if (DanglingVariable == Variable && Expr->fragmentsOverlap(DanglingExpr)) { @@ -1286,7 +1287,7 @@ // variable. FIXME: Further work could recover those too. while (isa(V)) { Instruction &VAsInst = *cast(V); - DIExpression *NewExpr = salvageDebugInfoImpl(VAsInst, Expr, StackValue); + DIExpression *NewExpr = VAsInst.salvageDebugInfoImpl(Expr, StackValue); // If we cannot salvage any further, and haven't yet found a suitable debug // expression, bail out. @@ -1319,6 +1320,56 @@ << "\n"); } +bool SelectionDAGBuilder::handleDebugValueWrapper(const DbgValueInst &DI, + DebugLoc dl, unsigned Order) { + DILocalVariable *Variable = DI.getVariable(); + DIExpression *Expression = DI.getExpression(); + + const Value *V = DI.getValue(); + + unsigned Level = Expression->explicitPointerCount(); + if (Level > 0) { + DIScope *Scope = DI.getDebugLoc()->getScope(); + DIType *Type = DI.getVariable()->getType(); + Module *Mod = const_cast(DI.getModule()); + DIBuilder DIB(*Mod, /*AllowUnresolved*/ false); + SmallVector PtrVars; + unsigned Count = 0; + while (Count++ < Level) { + if (DIDerivedType *DT = dyn_cast(Type)) + Type = DT->getBaseType(); + uint32_t AlignInBits = DI.getVariable()->getAlignInBytes(); + + DILocalVariable *PteVar = + DIB.createImplicitTempVariable(Scope, Type, AlignInBits); + PtrVars.push_back(PteVar); + } + PtrVars.push_back(Variable); + + SmallVector Ops; + DIExpression *DE = DIExpression::get(Expression->getContext(), Ops); + + if (!handleDebugValue(V, PtrVars[0], DE, dl, DI.getDebugLoc(), SDNodeOrder)) + return false; + + SDDbgValue *SDV; + + Count = 0; + while (Count++ < Level) { + DE = DIExpression::get(DE->getContext(), + {dwarf::DW_OP_LLVM_implicit_pointer, 0}); + + SDV = DAG.getImpPtrDbgValue(PtrVars[Count], DE, PtrVars[Count - 1], dl, + SDNodeOrder); + DAG.AddDbgValue(SDV, nullptr, false); + } + return true; + } else { + return handleDebugValue(V, Variable, Expression, dl, DI.getDebugLoc(), + SDNodeOrder); + } +} + bool SelectionDAGBuilder::handleDebugValue(const Value *V, DILocalVariable *Var, DIExpression *Expr, DebugLoc dl, DebugLoc InstDL, unsigned Order) { @@ -1331,6 +1382,13 @@ return true; } + if (const llvm::MetadataAsValue *MDV = dyn_cast(V)) { + if (DILocalVariable *DLV = dyn_cast(MDV->getMetadata())) { + SDV = DAG.getImpPtrDbgValue(Var, Expr, DLV, dl, SDNodeOrder); + DAG.AddDbgValue(SDV, nullptr, false); + return true; + } + } // If the Value is a frame index, we can create a FrameIndex debug value // without relying on the DAG at all. if (const AllocaInst *AI = dyn_cast(V)) { @@ -5981,8 +6039,7 @@ if (!V) return; - if (handleDebugValue(V, Variable, Expression, dl, DI.getDebugLoc(), - SDNodeOrder)) + if (handleDebugValueWrapper(DI, dl, SDNodeOrder)) return; // TODO: Dangling debug info will eventually either be resolved or produce diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp @@ -799,6 +799,11 @@ case VREG: OS << "(VREG=" << getVReg() << ')'; break; + case IMPPTR: + // implicit pointer will be printed as + // DbgVal(Order=1)(IMPPTR=arr):"ptr"!DIExpression(DW_OP_implicit_pointer, 0) + OS << "(IMPPTR=" << u.ImpPtr->getName() << ')'; + break; } if (isIndirect()) OS << "(Indirect)"; OS << ":\"" << Var->getName() << '"'; diff --git a/llvm/lib/DebugInfo/DWARF/DWARFDebugLoc.cpp b/llvm/lib/DebugInfo/DWARF/DWARFDebugLoc.cpp --- a/llvm/lib/DebugInfo/DWARF/DWARFDebugLoc.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFDebugLoc.cpp @@ -114,7 +114,11 @@ // corresponding unit is known. For now, there is only one operation, // DW_OP_call_ref, which depends on the format; it is rarely used, and // is unexpected in location tables. - DWARFExpression(Extractor, AddressSize).print(OS, MRI, U); + if (U) + DWARFExpression(Extractor, AddressSize, U->getFormParams().Format) + .print(OS, MRI, U); + else + DWARFExpression(Extractor, AddressSize).print(OS, MRI, U); } bool DWARFLocationTable::dumpLocationList(uint64_t *Offset, raw_ostream &OS, diff --git a/llvm/lib/DebugInfo/DWARF/DWARFExpression.cpp b/llvm/lib/DebugInfo/DWARF/DWARFExpression.cpp --- a/llvm/lib/DebugInfo/DWARF/DWARFExpression.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFExpression.cpp @@ -104,6 +104,8 @@ Descriptions[DW_OP_entry_value] = Desc(Op::Dwarf5, Op::SizeLEB); Descriptions[DW_OP_regval_type] = Desc(Op::Dwarf5, Op::SizeLEB, Op::BaseTypeRef); + Descriptions[DW_OP_implicit_pointer] = + Desc(Op::Dwarf5, Op::SizeRefAddr, Op::SignedSizeLEB); return Descriptions; } diff --git a/llvm/lib/IR/AsmWriter.cpp b/llvm/lib/IR/AsmWriter.cpp --- a/llvm/lib/IR/AsmWriter.cpp +++ b/llvm/lib/IR/AsmWriter.cpp @@ -2230,6 +2230,8 @@ if (I->getOp() == dwarf::DW_OP_LLVM_convert) { Out << FS << I->getArg(0); Out << FS << dwarf::AttributeEncodingString(I->getArg(1)); + } else if (I->getOp() == dwarf::DW_OP_LLVM_implicit_pointer) { + Out << FS << I->getArg(0); } else { for (unsigned A = 0, AE = I->getNumArgs(); A != AE; ++A) Out << FS << I->getArg(A); diff --git a/llvm/lib/IR/DIBuilder.cpp b/llvm/lib/IR/DIBuilder.cpp --- a/llvm/lib/IR/DIBuilder.cpp +++ b/llvm/lib/IR/DIBuilder.cpp @@ -719,6 +719,19 @@ Flags, AlignInBits); } +DILocalVariable *DIBuilder::createImplicitTempVariable(DIScope *Scope, + DIType *Ty, + uint32_t AlignInBits) { + static unsigned TmpCount = 0; + std::string SName("__implicit_ptr_tmp_"); + SName.append(std::to_string(TmpCount++)); + llvm::StringRef Name(SName); + + return createLocalVariable(VMContext, PreservedVariables, Scope, Name, + /* ArgNo */ 0, nullptr, 0, Ty, false, + llvm::DINode::FlagArtificial, AlignInBits); +} + DILocalVariable *DIBuilder::createParameterVariable( DIScope *Scope, StringRef Name, unsigned ArgNo, DIFile *File, unsigned LineNo, DIType *Ty, bool AlwaysPreserve, DINode::DIFlags Flags) { diff --git a/llvm/lib/IR/DebugInfoMetadata.cpp b/llvm/lib/IR/DebugInfoMetadata.cpp --- a/llvm/lib/IR/DebugInfoMetadata.cpp +++ b/llvm/lib/IR/DebugInfoMetadata.cpp @@ -962,6 +962,7 @@ case dwarf::DW_OP_LLVM_tag_offset: case dwarf::DW_OP_LLVM_entry_value: case dwarf::DW_OP_regx: + case dwarf::DW_OP_LLVM_implicit_pointer: return 2; default: return 1; @@ -1016,6 +1017,12 @@ return I->get() == expr_op_begin()->get() && I->getArg(0) == 1 && getNumElements() == 2; } + case dwarf::DW_OP_LLVM_implicit_pointer: { + // A DW_OP_LLVM_implicit_pointer operator must appear at the beginning + // and it has one argument. + return (I == expr_op_begin()) && (getNumElements() == 2); + } + case dwarf::DW_OP_LLVM_explicit_pointer: case dwarf::DW_OP_LLVM_convert: case dwarf::DW_OP_LLVM_tag_offset: case dwarf::DW_OP_constu: @@ -1110,17 +1117,22 @@ } bool DIExpression::extractIfOffset(int64_t &Offset) const { - if (getNumElements() == 0) { + unsigned numElements = getNumElements(); + + if (numElements == 0) { Offset = 0; return true; } - if (getNumElements() == 2 && Elements[0] == dwarf::DW_OP_plus_uconst) { + if (Elements[numElements - 1] == dwarf::DW_OP_stack_value) + numElements--; + + if (numElements == 2 && Elements[0] == dwarf::DW_OP_plus_uconst) { Offset = Elements[1]; return true; } - if (getNumElements() == 3 && Elements[0] == dwarf::DW_OP_constu) { + if (numElements == 3 && Elements[0] == dwarf::DW_OP_constu) { if (Elements[2] == dwarf::DW_OP_plus) { Offset = Elements[1]; return true; diff --git a/llvm/lib/IR/Instruction.cpp b/llvm/lib/IR/Instruction.cpp --- a/llvm/lib/IR/Instruction.cpp +++ b/llvm/lib/IR/Instruction.cpp @@ -11,11 +11,14 @@ //===----------------------------------------------------------------------===// #include "llvm/IR/Instruction.h" -#include "llvm/IR/IntrinsicInst.h" #include "llvm/ADT/DenseSet.h" #include "llvm/IR/Constants.h" +#include "llvm/IR/DIBuilder.h" +#include "llvm/IR/DebugInfoMetadata.h" #include "llvm/IR/Instructions.h" +#include "llvm/IR/IntrinsicInst.h" #include "llvm/IR/MDBuilder.h" +#include "llvm/IR/Module.h" #include "llvm/IR/Operator.h" #include "llvm/IR/Type.h" using namespace llvm; @@ -78,6 +81,9 @@ } iplist::iterator Instruction::eraseFromParent() { + // Try to preserve debug information attached to the instruction. + salvageDebugInfo(); + return getParent()->getInstList().erase(getIterator()); } @@ -774,3 +780,227 @@ New->copyMetadata(*this); return New; } + +void Instruction::salvageDebugInfo() { + SmallVector DbgUsers; + + // Return false if any of the operand is already deleted + for (unsigned i = 0; i < getNumOperands(); ++i) + if (!getOperand(i)) + return; + + // preserve the information we may need later when called function from + // current function is inlined. We need this information for + // DW_OP_implicit_pointer. + if (auto DDI = dyn_cast(this)) + if (getModule()->getDwarfVersion() >= 5) + if (DDI->isAddressOfVariable()) + getModule()->insertVarAddressMap(DDI); + + findDbgUsers(DbgUsers); + if (DbgUsers.empty()) + return; + + salvageDebugInfoForDbgValues(DbgUsers); +} + +void Instruction::salvageDebugInfoForDbgValues( + ArrayRef DbgUsers) { + auto &Ctx = getContext(); + bool Salvaged = false; + auto wrapMD = [&](Value *V) { + return MetadataAsValue::get(Ctx, ValueAsMetadata::get(V)); + }; + Value *DebugDeclareAddress = nullptr; + SmallVector StoreInsts; + + if (getModule()->getDwarfVersion() >= 5) { + if (isa(*this)) { + for (auto *DII : DbgUsers) { + if (DII->isAddressOfVariable()) { + if (const llvm::MetadataAsValue *MDV = + dyn_cast(DII->getOperand(1))) + if (isa(MDV->getMetadata())) { + DebugDeclareAddress = DII->getOperand(1); + } + break; + } + } + } + + if (!DebugDeclareAddress) + DebugDeclareAddress = getModule()->findVarAddressMap(this); + + if (!DebugDeclareAddress) { + StoreInst *SI; + for (auto &AIUse : uses()) { + User *U = AIUse.getUser(); + if ((SI = dyn_cast(U)) && SI->getOperand(1) == this) + StoreInsts.push_back(SI); + } + } + } + + for (auto *DII : DbgUsers) { + // Do not add DW_OP_stack_value for DbgDeclare and DbgAddr, because they + // are implicitly pointing out the value as a DWARF memory location + // description. + bool StackValue = isa(DII); + + DIExpression *DIExpr = + salvageDebugInfoImpl(DII->getExpression(), StackValue); + + // salvageDebugInfoImpl should fail on examining the first element of + // DbgUsers, or none of them. + if (!DIExpr) + continue; + + int64_t ExprOffset = 0; + + // It is AllocaInst and either of below is true then continue + // - We failed to get the variable description, or + // - Current instruction is not llvm.dbg.value, or + // - Current instruction doesnt have constant offset + if (!StoreInsts.empty()) { + if (!DII->getExpression()->extractIfOffset(ExprOffset)) + continue; + } else if (isa(this) && + (!DebugDeclareAddress || DII->isAddressOfVariable() || + !DII->getExpression()->extractIfOffset(ExprOffset))) + continue; + + if (!StoreInsts.empty()) { + DIBuilder DIB(*getModule(), /*AllowUnresolved*/ false); + + DIExpression *DIExpr = DII->getExpression(); + + for (auto *SI : StoreInsts) { + Value *ReplVal = SI->getOperand(0); + SmallVector Ops; + Ops.push_back(dwarf::DW_OP_LLVM_explicit_pointer); + DIExpression *DIExprNew = DIExpression::get(DIExpr->getContext(), Ops); + + if (!StackValue) + DIB.insertDbgValueIntrinsic(ReplVal, DII->getVariable(), DIExprNew, + DII->getDebugLoc(), SI); + else { + DII->setOperand(0, wrapMD(ReplVal)); + DII->setOperand(2, MetadataAsValue::get(Ctx, DIExprNew)); + } + } + Salvaged = true; + } else if (!isa(this)) { + DII->setOperand(0, wrapMD(getOperand(0))); + DII->setOperand(2, MetadataAsValue::get(Ctx, DIExpr)); + Salvaged = true; + } + } + + if (Salvaged) + return; + + for (auto *DII : DbgUsers) { + Value *Undef = UndefValue::get(getType()); + DII->setOperand(0, MetadataAsValue::get(DII->getContext(), + ValueAsMetadata::get(Undef))); + } +} + +DIExpression *Instruction::salvageDebugInfoImpl(DIExpression *SrcDIExpr, + bool WithStackValue) { + auto &M = *getModule(); + auto &DL = M.getDataLayout(); + + // Apply a vector of opcodes to the source DIExpression. + auto doSalvage = [&](SmallVectorImpl &Ops) -> DIExpression * { + DIExpression *DIExpr = SrcDIExpr; + if (!Ops.empty()) { + DIExpr = DIExpression::prependOpcodes(DIExpr, Ops, WithStackValue); + } + return DIExpr; + }; + + // Apply the given offset to the source DIExpression. + auto applyOffset = [&](uint64_t Offset) -> DIExpression * { + SmallVector Ops; + DIExpression::appendOffset(Ops, Offset); + return doSalvage(Ops); + }; + + // initializer-list helper for applying operators to the source DIExpression. + auto applyOps = [&](ArrayRef Opcodes) -> DIExpression * { + SmallVector Ops(Opcodes.begin(), Opcodes.end()); + return doSalvage(Ops); + }; + + if (auto *CI = dyn_cast(this)) { + // No-op casts are irrelevant for debug info. + if (CI->isNoopCast(DL)) + return SrcDIExpr; + + Type *Type = CI->getType(); + // Casts other than Trunc, SExt, or ZExt to scalar types cannot be salvaged. + if (Type->isVectorTy() || + !(isa(this) || isa(this) || isa(this))) + return nullptr; + + Value *FromValue = CI->getOperand(0); + unsigned FromTypeBitSize = FromValue->getType()->getScalarSizeInBits(); + unsigned ToTypeBitSize = Type->getScalarSizeInBits(); + + return applyOps(DIExpression::getExtOps(FromTypeBitSize, ToTypeBitSize, + isa(this))); + } + + if (auto *GEP = dyn_cast(this)) { + unsigned BitWidth = + M.getDataLayout().getIndexSizeInBits(GEP->getPointerAddressSpace()); + // Rewrite a constant GEP into a DIExpression. + APInt Offset(BitWidth, 0); + if (GEP->accumulateConstantOffset(M.getDataLayout(), Offset)) { + return applyOffset(Offset.getSExtValue()); + } else { + return nullptr; + } + } else if (auto *BI = dyn_cast(this)) { + // Rewrite binary operations with constant integer operands. + auto *ConstInt = dyn_cast(getOperand(1)); + if (!ConstInt || ConstInt->getBitWidth() > 64) + return nullptr; + + uint64_t Val = ConstInt->getSExtValue(); + switch (BI->getOpcode()) { + case Instruction::Add: + return applyOffset(Val); + case Instruction::Sub: + return applyOffset(-int64_t(Val)); + case Instruction::Mul: + return applyOps({dwarf::DW_OP_constu, Val, dwarf::DW_OP_mul}); + case Instruction::SDiv: + return applyOps({dwarf::DW_OP_constu, Val, dwarf::DW_OP_div}); + case Instruction::SRem: + return applyOps({dwarf::DW_OP_constu, Val, dwarf::DW_OP_mod}); + case Instruction::Or: + return applyOps({dwarf::DW_OP_constu, Val, dwarf::DW_OP_or}); + case Instruction::And: + return applyOps({dwarf::DW_OP_constu, Val, dwarf::DW_OP_and}); + case Instruction::Xor: + return applyOps({dwarf::DW_OP_constu, Val, dwarf::DW_OP_xor}); + case Instruction::Shl: + return applyOps({dwarf::DW_OP_constu, Val, dwarf::DW_OP_shl}); + case Instruction::LShr: + return applyOps({dwarf::DW_OP_constu, Val, dwarf::DW_OP_shr}); + case Instruction::AShr: + return applyOps({dwarf::DW_OP_constu, Val, dwarf::DW_OP_shra}); + default: + // TODO: Salvage constants from each kind of binop we know about. + return nullptr; + } + // *Not* to do: we should not attempt to salvage load instructions, + // because the validity and lifetime of a dbg.value containing + // DW_OP_deref becomes difficult to analyze. See PR40628 for examples. + } else if ((M.getDwarfVersion() >= 5) && isa(this)) { + return SrcDIExpr; + } + return nullptr; +} diff --git a/llvm/lib/IR/IntrinsicInst.cpp b/llvm/lib/IR/IntrinsicInst.cpp --- a/llvm/lib/IR/IntrinsicInst.cpp +++ b/llvm/lib/IR/IntrinsicInst.cpp @@ -43,6 +43,26 @@ if (AllowNullOp && !Op) return nullptr; + if (isImplicitPointer()) + return Op; + + auto *MD = cast(Op)->getMetadata(); + if (auto *V = dyn_cast(MD)) + return V->getValue(); + + // When the value goes to null, it gets replaced by an empty MDNode. + assert(!cast(MD)->getNumOperands() && "Expected an empty MDNode"); + return nullptr; +} + +Value *DbgVariableIntrinsic::getDerefVariable(bool AllowNullOp) const { + Value *Op = getArgOperand(0); + if (AllowNullOp && !Op) + return nullptr; + + if (isImplicitPointer()) + return Op; + auto *MD = cast(Op)->getMetadata(); if (auto *V = dyn_cast(MD)) return V->getValue(); diff --git a/llvm/lib/IR/Metadata.cpp b/llvm/lib/IR/Metadata.cpp --- a/llvm/lib/IR/Metadata.cpp +++ b/llvm/lib/IR/Metadata.cpp @@ -392,7 +392,6 @@ assert(From && "Expected valid value"); assert(To && "Expected valid value"); assert(From != To && "Expected changed value"); - assert(From->getType() == To->getType() && "Unexpected type change"); LLVMContext &Context = From->getType()->getContext(); auto &Store = Context.pImpl->ValuesAsMetadata; diff --git a/llvm/lib/IR/Module.cpp b/llvm/lib/IR/Module.cpp --- a/llvm/lib/IR/Module.cpp +++ b/llvm/lib/IR/Module.cpp @@ -31,6 +31,7 @@ #include "llvm/IR/GlobalIFunc.h" #include "llvm/IR/GlobalValue.h" #include "llvm/IR/GlobalVariable.h" +#include "llvm/IR/IntrinsicInst.h" #include "llvm/IR/LLVMContext.h" #include "llvm/IR/Metadata.h" #include "llvm/IR/ModuleSummaryIndex.h" @@ -694,3 +695,18 @@ } } } + +void Module::insertVarAddressMap(DbgVariableIntrinsic *DDI) { + if (DDI->getOperand(0) != nullptr) + VarAddrMap.insert( + std::pair(DDI->getOperand(0), DDI->getOperand(1))); +} + +Value *Module::findVarAddressMap(Value *Var) { + if (Var->isUsedByMetadata()) + if (auto *L = LocalAsMetadata::getIfExists(Var)) + if (auto *MDV = MetadataAsValue::getIfExists(Var->getContext(), L)) + return VarAddrMap[MDV]; + + return nullptr; +} diff --git a/llvm/lib/IR/Value.cpp b/llvm/lib/IR/Value.cpp --- a/llvm/lib/IR/Value.cpp +++ b/llvm/lib/IR/Value.cpp @@ -456,8 +456,6 @@ assert(New && "Value::replaceAllUsesWith() is invalid!"); assert(!contains(New, this) && "this->replaceAllUsesWith(expr(this)) is NOT valid!"); - assert(New->getType() == getType() && - "replaceAllUses of value with new value of different type!"); // Notify all ValueHandles (if present) that this value is going away. if (HasValueHandle) @@ -508,6 +506,18 @@ }); } +void Value::findDbgUsers(SmallVectorImpl &DbgUsers) { + // This function is hot. Check whether the value has any metadata to avoid a + // DenseMap lookup. + if (!isUsedByMetadata()) + return; + if (auto *L = LocalAsMetadata::getIfExists(this)) + if (auto *MDV = MetadataAsValue::getIfExists(getContext(), L)) + for (User *U : MDV->users()) + if (DbgVariableIntrinsic *DII = dyn_cast(U)) + DbgUsers.push_back(DII); +} + namespace { // Various metrics for how much to strip off of pointers. enum PointerStripKind { diff --git a/llvm/lib/Transforms/InstCombine/InstCombineInternal.h b/llvm/lib/Transforms/InstCombine/InstCombineInternal.h --- a/llvm/lib/Transforms/InstCombine/InstCombineInternal.h +++ b/llvm/lib/Transforms/InstCombine/InstCombineInternal.h @@ -729,7 +729,7 @@ Instruction *eraseInstFromFunction(Instruction &I) { LLVM_DEBUG(dbgs() << "IC: ERASE " << I << '\n'); assert(I.use_empty() && "Cannot erase instruction that is used!"); - salvageDebugInfo(I); + I.salvageDebugInfo(); // Make sure that we reprocess all operands now that we reduced their // use counts. diff --git a/llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp b/llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp --- a/llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp @@ -88,8 +88,8 @@ Depth, I); if (!NewVal) return false; if (Instruction* OpInst = dyn_cast(U)) - salvageDebugInfo(*OpInst); - + OpInst->salvageDebugInfo(); + replaceUse(U, NewVal); return true; } diff --git a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp --- a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp +++ b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp @@ -2568,6 +2568,7 @@ } else { // Casts, GEP, or anything else: we're about to delete this instruction, // so it can not have any valid uses. + I->salvageDebugInfo(); replaceInstUsesWith(*I, UndefValue::get(I->getType())); } eraseInstFromFunction(*I); @@ -3390,7 +3391,7 @@ // mark the location undef: we know it was supposed to receive a new location // here, but that computation has been sunk. SmallVector DbgUsers; - findDbgUsers(DbgUsers, I); + I->findDbgUsers(DbgUsers); // Update the arguments of a dbg.declare instruction, so that it // does not point into a sunk instruction. @@ -3420,7 +3421,7 @@ // Perform salvaging without the clones, then sink the clones. if (!DIIClones.empty()) { - salvageDebugInfoForDbgValues(*I, DbgUsers); + I->salvageDebugInfoForDbgValues(DbgUsers); for (auto &DIIClone : DIIClones) { DIIClone->insertBefore(&*InsertPos); LLVM_DEBUG(dbgs() << "SINK: " << *DIIClone << '\n'); @@ -3699,7 +3700,7 @@ if (isInstructionTriviallyDead(Inst, TLI)) { ++NumDeadInst; LLVM_DEBUG(dbgs() << "IC: DCE: " << *Inst << '\n'); - salvageDebugInfo(*Inst); + Inst->salvageDebugInfo(); Inst->eraseFromParent(); MadeIRChange = true; continue; diff --git a/llvm/lib/Transforms/Scalar/BDCE.cpp b/llvm/lib/Transforms/Scalar/BDCE.cpp --- a/llvm/lib/Transforms/Scalar/BDCE.cpp +++ b/llvm/lib/Transforms/Scalar/BDCE.cpp @@ -106,7 +106,7 @@ (I.getType()->isIntOrIntVectorTy() && DB.getDemandedBits(&I).isNullValue() && wouldInstructionBeTriviallyDead(&I))) { - salvageDebugInfo(I); + I.salvageDebugInfo(); Worklist.push_back(&I); I.dropAllReferences(); Changed = true; diff --git a/llvm/lib/Transforms/Scalar/DCE.cpp b/llvm/lib/Transforms/Scalar/DCE.cpp --- a/llvm/lib/Transforms/Scalar/DCE.cpp +++ b/llvm/lib/Transforms/Scalar/DCE.cpp @@ -59,7 +59,6 @@ if (isInstructionTriviallyDead(Inst, TLI)) { if (!DebugCounter::shouldExecute(DCECounter)) continue; - salvageDebugInfo(*Inst); Inst->eraseFromParent(); Changed = true; ++DIEEliminated; @@ -127,7 +126,7 @@ if (!DebugCounter::shouldExecute(DCECounter)) return false; - salvageDebugInfo(*I); + I->salvageDebugInfo(); salvageKnowledge(I); // Null out all of the instruction's operands to see if any operand becomes diff --git a/llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp b/llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp --- a/llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp +++ b/llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp @@ -156,7 +156,7 @@ ++NumFastOther; // Try to preserve debug information attached to the dead instruction. - salvageDebugInfo(*DeadInst); + DeadInst->salvageDebugInfo(); salvageKnowledge(DeadInst); // This instruction is dead, zap it, in stages. Start by removing it from @@ -1962,7 +1962,7 @@ ++NumFastOther; // Try to preserve debug information attached to the dead instruction. - salvageDebugInfo(*DeadInst); + DeadInst->salvageDebugInfo(); salvageKnowledge(DeadInst); // Remove the Instruction from MSSA. diff --git a/llvm/lib/Transforms/Scalar/EarlyCSE.cpp b/llvm/lib/Transforms/Scalar/EarlyCSE.cpp --- a/llvm/lib/Transforms/Scalar/EarlyCSE.cpp +++ b/llvm/lib/Transforms/Scalar/EarlyCSE.cpp @@ -56,6 +56,7 @@ #include "llvm/Support/raw_ostream.h" #include "llvm/Transforms/Scalar.h" #include "llvm/Transforms/Utils/AssumeBundleBuilder.h" +#include "llvm/Transforms/Utils/BasicBlockUtils.h" #include "llvm/Transforms/Utils/GuardUtils.h" #include "llvm/Transforms/Utils/Local.h" #include @@ -966,7 +967,7 @@ } salvageKnowledge(&Inst, &AC); - salvageDebugInfo(Inst); + Inst.salvageDebugInfo(); removeMSSA(Inst); Inst.eraseFromParent(); Changed = true; @@ -1434,7 +1435,12 @@ EarlyCSE CSE(F.getParent()->getDataLayout(), TLI, TTI, DT, AC, MSSA); - return CSE.run(); + auto Changed = CSE.run(); + if (Changed) { + for (BasicBlock &BB : F) + RemoveRedundantDbgInstrs(&BB); + } + return Changed; } void getAnalysisUsage(AnalysisUsage &AU) const override { diff --git a/llvm/lib/Transforms/Scalar/GVN.cpp b/llvm/lib/Transforms/Scalar/GVN.cpp --- a/llvm/lib/Transforms/Scalar/GVN.cpp +++ b/llvm/lib/Transforms/Scalar/GVN.cpp @@ -2234,7 +2234,7 @@ assert(I->getParent() == BB && "Removing instruction from wrong block?"); LLVM_DEBUG(dbgs() << "GVN removed: " << *I << '\n'); salvageKnowledge(I, AC); - salvageDebugInfo(*I); + I->salvageDebugInfo(); if (MD) MD->removeInstruction(I); LLVM_DEBUG(verifyRemoved(I)); ICF->removeInstruction(I); diff --git a/llvm/lib/Transforms/Scalar/LICM.cpp b/llvm/lib/Transforms/Scalar/LICM.cpp --- a/llvm/lib/Transforms/Scalar/LICM.cpp +++ b/llvm/lib/Transforms/Scalar/LICM.cpp @@ -484,7 +484,7 @@ if (isInstructionTriviallyDead(&I, TLI)) { LLVM_DEBUG(dbgs() << "LICM deleting dead inst: " << I << '\n'); salvageKnowledge(&I); - salvageDebugInfo(I); + I.salvageDebugInfo(); ++II; eraseInstruction(I, *SafetyInfo, CurAST, MSSAU); Changed = true; @@ -504,7 +504,7 @@ if (sink(I, LI, DT, CurLoop, SafetyInfo, MSSAU, ORE)) { if (!FreeInLoop) { ++II; - salvageDebugInfo(I); + I.salvageDebugInfo(); eraseInstruction(I, *SafetyInfo, CurAST, MSSAU); } Changed = true; diff --git a/llvm/lib/Transforms/Scalar/Reassociate.cpp b/llvm/lib/Transforms/Scalar/Reassociate.cpp --- a/llvm/lib/Transforms/Scalar/Reassociate.cpp +++ b/llvm/lib/Transforms/Scalar/Reassociate.cpp @@ -1903,7 +1903,7 @@ ValueRankMap.erase(I); Insts.remove(I); RedoInsts.remove(I); - llvm::salvageDebugInfo(*I); + I->salvageDebugInfo(); I->eraseFromParent(); for (auto Op : Ops) if (Instruction *OpInst = dyn_cast(Op)) @@ -1920,7 +1920,7 @@ // Erase the dead instruction. ValueRankMap.erase(I); RedoInsts.remove(I); - llvm::salvageDebugInfo(*I); + I->salvageDebugInfo(); I->eraseFromParent(); // Optimize its operands. SmallPtrSet Visited; // Detect self-referential nodes. diff --git a/llvm/lib/Transforms/Scalar/SROA.cpp b/llvm/lib/Transforms/Scalar/SROA.cpp --- a/llvm/lib/Transforms/Scalar/SROA.cpp +++ b/llvm/lib/Transforms/Scalar/SROA.cpp @@ -80,6 +80,7 @@ #include "llvm/Support/MathExtras.h" #include "llvm/Support/raw_ostream.h" #include "llvm/Transforms/Scalar.h" +#include "llvm/Transforms/Utils/BasicBlockUtils.h" #include "llvm/Transforms/Utils/Local.h" #include "llvm/Transforms/Utils/PromoteMemToReg.h" #include @@ -4363,6 +4364,90 @@ return NewAI; } +void SROA::salvageInstruction(Instruction *Ins) { + SmallVector DbgUsers; + AllocaInst *AI = NULL; + int64_t ExprOffset = 0; + auto &Ctx = Ins->getContext(); + auto wrapMD = [&](Value *V) { + return MetadataAsValue::get(Ctx, ValueAsMetadata::get(V)); + }; + auto findFragmentAlloca = [&](int64_t ExprOffset) { + AllocaInst *AInst = NULL; + for (auto Fragment : SROA::Fragments) { + if ((uint64_t)(ExprOffset * 8) == Fragment.Offset) { + return Fragment.Alloca; + } + } + return AInst; + }; + if (isa(Ins)) { + for (Use &U2 : Ins->uses()) + if (isa(cast(U2.getUser()))) { + salvageInstruction(cast(U2.getUser())); + } + } + if (GetElementPtrInst *GEP = dyn_cast(Ins)) { + unsigned BitWidth = GEP->getModule()->getDataLayout().getIndexSizeInBits( + GEP->getPointerAddressSpace()); + APInt Offset(BitWidth, 0); + if (GEP->accumulateConstantOffset(GEP->getModule()->getDataLayout(), + Offset)) { + const uint64_t *Valptr = Offset.getRawData(); + if ((AI = findFragmentAlloca(*Valptr))) { + Ins->replaceAllUsesWith(AI); + DeadInsts.insert(Ins); + } + } + } + Ins->findDbgUsers(DbgUsers); + for (auto *DII : DbgUsers) { + if (!isa(DII)) + continue; + if ((DII->getExpression()->explicitPointerCount() > 0 || + DII->getExpression()->extractIfOffset(ExprOffset)) && + (AI = findFragmentAlloca(ExprOffset))) { + DII->setOperand(0, wrapMD(AI)); + } + } + for (Use &U : Ins->uses()) { + llvm::User *User = U.getUser(); + SmallVector DbgUsers; + if (isa(cast(User)) || + isa(cast(User))) + salvageInstruction(cast(User)); + } +} + +void SROA::fixGEPWithSplitAllocas() { + for (auto Fragment : SROA::Fragments) { + uint64_t NewOffset = Fragment.Offset; + for (Use &U : Fragment.Alloca->uses()) { + Instruction *I = cast(U.getUser()); + if (GetElementPtrInst *GEP = dyn_cast(I)) { + unsigned BitWidth = + GEP->getModule()->getDataLayout().getIndexSizeInBits( + GEP->getPointerAddressSpace()); + APInt Offset(BitWidth, 0); + if (GEP->accumulateConstantOffset(GEP->getModule()->getDataLayout(), + Offset)) { + + if (*Offset.getRawData()) { + NewOffset += 8 * (*(Offset.getRawData())); + for (auto Fragment2 : SROA::Fragments) { + if (Fragment2.Offset == NewOffset) { + I->replaceAllUsesWith(Fragment2.Alloca); + DeadInsts.insert(I); + break; + } + } + } + } + } + } + } +} + /// Walks the slices of an alloca and form partitions based on them, /// rewriting each of their uses. bool SROA::splitAlloca(AllocaInst &AI, AllocaSlices &AS) { @@ -4432,17 +4517,7 @@ if (!IsSorted) llvm::sort(AS); - /// Describes the allocas introduced by rewritePartition in order to migrate - /// the debug info. - struct Fragment { - AllocaInst *Alloca; - uint64_t Offset; - uint64_t Size; - Fragment(AllocaInst *AI, uint64_t O, uint64_t S) - : Alloca(AI), Offset(O), Size(S) {} - }; - SmallVector Fragments; - + SROA::Fragments.clear(); // Rewrite each partition. for (auto &P : AS.partitions()) { if (AllocaInst *NewAI = rewritePartition(AI, AS, P)) { @@ -4453,12 +4528,18 @@ DL.getTypeSizeInBits(NewAI->getAllocatedType()).getFixedSize(); // Don't include any padding. uint64_t Size = std::min(AllocaSize, P.size() * SizeOfByte); - Fragments.push_back(Fragment(NewAI, P.beginOffset() * SizeOfByte, Size)); + SROA::Fragments.push_back( + Fragment(NewAI, P.beginOffset() * SizeOfByte, Size)); } } ++NumPartitions; } + if (AI.getModule()->getDwarfVersion() >= 5) { + salvageInstruction(&AI); + fixGEPWithSplitAllocas(); + } + NumAllocaPartitions += NumPartitions; MaxPartitionsPerAlloca.updateMax(NumPartitions); @@ -4472,7 +4553,7 @@ DIBuilder DIB(*AI.getModule(), /*AllowUnresolved*/ false); uint64_t AllocaSize = DL.getTypeSizeInBits(AI.getAllocatedType()).getFixedSize(); - for (auto Fragment : Fragments) { + for (auto Fragment : SROA::Fragments) { // Create a fragment expression describing the new partition or reuse AI's // expression if there is only one partition. auto *FragmentExpr = Expr; @@ -4579,12 +4660,18 @@ // Delete all the dead users of this alloca before splitting and rewriting it. for (Instruction *DeadUser : AS.getDeadUsers()) { + + // Try to preserve debug information attached to the instruction. + if (AI.getModule()->getDwarfVersion() >= 5) + DeadUser->salvageDebugInfo(); + // Free up everything used by this instruction. for (Use &DeadOp : DeadUser->operands()) clobberUse(DeadOp); // Now replace the uses of this instruction. - DeadUser->replaceAllUsesWith(UndefValue::get(DeadUser->getType())); + if (AI.getModule()->getDwarfVersion() < 5) + DeadUser->replaceAllUsesWith(UndefValue::get(DeadUser->getType())); // And mark it for deletion. DeadInsts.insert(DeadUser); @@ -4667,6 +4754,8 @@ LLVM_DEBUG(dbgs() << "Promoting allocas with mem2reg...\n"); PromoteMemToReg(PromotableAllocas, *DT, AC); + for (BasicBlock &BB : F) + RemoveRedundantDbgInstrs(&BB); PromotableAllocas.clear(); return true; } diff --git a/llvm/lib/Transforms/Utils/CodeExtractor.cpp b/llvm/lib/Transforms/Utils/CodeExtractor.cpp --- a/llvm/lib/Transforms/Utils/CodeExtractor.cpp +++ b/llvm/lib/Transforms/Utils/CodeExtractor.cpp @@ -1407,7 +1407,7 @@ static void eraseDebugIntrinsicsWithNonLocalRefs(Function &F) { for (Instruction &I : instructions(F)) { SmallVector DbgUsers; - findDbgUsers(DbgUsers, &I); + I.findDbgUsers(DbgUsers); for (DbgVariableIntrinsic *DVI : DbgUsers) if (DVI->getFunction() != &F) DVI->eraseFromParent(); diff --git a/llvm/lib/Transforms/Utils/Local.cpp b/llvm/lib/Transforms/Utils/Local.cpp --- a/llvm/lib/Transforms/Utils/Local.cpp +++ b/llvm/lib/Transforms/Utils/Local.cpp @@ -501,7 +501,7 @@ assert(I->use_empty() && "Instructions with uses are not dead."); // Don't lose the debug info while deleting the instructions. - salvageDebugInfo(*I); + I->salvageDebugInfo(); if (AboutToDeleteCallback) AboutToDeleteCallback(I); @@ -531,7 +531,7 @@ bool llvm::replaceDbgUsesWithUndef(Instruction *I) { SmallVector DbgUsers; - findDbgUsers(DbgUsers, I); + I->findDbgUsers(DbgUsers); for (auto *DII : DbgUsers) { Value *Undef = UndefValue::get(I->getType()); DII->setOperand(0, MetadataAsValue::get(DII->getContext(), @@ -590,7 +590,7 @@ const DataLayout &DL, const TargetLibraryInfo *TLI) { if (isInstructionTriviallyDead(I, TLI)) { - salvageDebugInfo(*I); + I->salvageDebugInfo(); // Null out all of the instruction's operands to see if any operand becomes // dead as we go. @@ -1566,19 +1566,6 @@ DbgValues.push_back(DVI); } -void llvm::findDbgUsers(SmallVectorImpl &DbgUsers, - Value *V) { - // This function is hot. Check whether the value has any metadata to avoid a - // DenseMap lookup. - if (!V->isUsedByMetadata()) - return; - if (auto *L = LocalAsMetadata::getIfExists(V)) - if (auto *MDV = MetadataAsValue::getIfExists(V->getContext(), L)) - for (User *U : MDV->users()) - if (DbgVariableIntrinsic *DII = dyn_cast(U)) - DbgUsers.push_back(DII); -} - bool llvm::replaceDbgDeclare(Value *Address, Value *NewAddress, DIBuilder &Builder, uint8_t DIExprFlags, int Offset) { @@ -1636,148 +1623,6 @@ return MetadataAsValue::get(C, ValueAsMetadata::get(V)); } -/// Where possible to salvage debug information for \p I do so -/// and return True. If not possible mark undef and return False. -void llvm::salvageDebugInfo(Instruction &I) { - SmallVector DbgUsers; - findDbgUsers(DbgUsers, &I); - salvageDebugInfoForDbgValues(I, DbgUsers); -} - -void llvm::salvageDebugInfoForDbgValues( - Instruction &I, ArrayRef DbgUsers) { - auto &Ctx = I.getContext(); - bool Salvaged = false; - auto wrapMD = [&](Value *V) { return wrapValueInMetadata(Ctx, V); }; - - for (auto *DII : DbgUsers) { - // Do not add DW_OP_stack_value for DbgDeclare and DbgAddr, because they - // are implicitly pointing out the value as a DWARF memory location - // description. - bool StackValue = isa(DII); - - DIExpression *DIExpr = - salvageDebugInfoImpl(I, DII->getExpression(), StackValue); - - // salvageDebugInfoImpl should fail on examining the first element of - // DbgUsers, or none of them. - if (!DIExpr) - break; - - DII->setOperand(0, wrapMD(I.getOperand(0))); - DII->setOperand(2, MetadataAsValue::get(Ctx, DIExpr)); - LLVM_DEBUG(dbgs() << "SALVAGE: " << *DII << '\n'); - Salvaged = true; - } - - if (Salvaged) - return; - - for (auto *DII : DbgUsers) { - Value *Undef = UndefValue::get(I.getType()); - DII->setOperand(0, MetadataAsValue::get(DII->getContext(), - ValueAsMetadata::get(Undef))); - } -} - -DIExpression *llvm::salvageDebugInfoImpl(Instruction &I, - DIExpression *SrcDIExpr, - bool WithStackValue) { - auto &M = *I.getModule(); - auto &DL = M.getDataLayout(); - - // Apply a vector of opcodes to the source DIExpression. - auto doSalvage = [&](SmallVectorImpl &Ops) -> DIExpression * { - DIExpression *DIExpr = SrcDIExpr; - if (!Ops.empty()) { - DIExpr = DIExpression::prependOpcodes(DIExpr, Ops, WithStackValue); - } - return DIExpr; - }; - - // Apply the given offset to the source DIExpression. - auto applyOffset = [&](uint64_t Offset) -> DIExpression * { - SmallVector Ops; - DIExpression::appendOffset(Ops, Offset); - return doSalvage(Ops); - }; - - // initializer-list helper for applying operators to the source DIExpression. - auto applyOps = [&](ArrayRef Opcodes) -> DIExpression * { - SmallVector Ops(Opcodes.begin(), Opcodes.end()); - return doSalvage(Ops); - }; - - if (auto *CI = dyn_cast(&I)) { - // No-op casts are irrelevant for debug info. - if (CI->isNoopCast(DL)) - return SrcDIExpr; - - Type *Type = CI->getType(); - // Casts other than Trunc, SExt, or ZExt to scalar types cannot be salvaged. - if (Type->isVectorTy() || - !(isa(&I) || isa(&I) || isa(&I))) - return nullptr; - - Value *FromValue = CI->getOperand(0); - unsigned FromTypeBitSize = FromValue->getType()->getScalarSizeInBits(); - unsigned ToTypeBitSize = Type->getScalarSizeInBits(); - - return applyOps(DIExpression::getExtOps(FromTypeBitSize, ToTypeBitSize, - isa(&I))); - } - - if (auto *GEP = dyn_cast(&I)) { - unsigned BitWidth = - M.getDataLayout().getIndexSizeInBits(GEP->getPointerAddressSpace()); - // Rewrite a constant GEP into a DIExpression. - APInt Offset(BitWidth, 0); - if (GEP->accumulateConstantOffset(M.getDataLayout(), Offset)) { - return applyOffset(Offset.getSExtValue()); - } else { - return nullptr; - } - } else if (auto *BI = dyn_cast(&I)) { - // Rewrite binary operations with constant integer operands. - auto *ConstInt = dyn_cast(I.getOperand(1)); - if (!ConstInt || ConstInt->getBitWidth() > 64) - return nullptr; - - uint64_t Val = ConstInt->getSExtValue(); - switch (BI->getOpcode()) { - case Instruction::Add: - return applyOffset(Val); - case Instruction::Sub: - return applyOffset(-int64_t(Val)); - case Instruction::Mul: - return applyOps({dwarf::DW_OP_constu, Val, dwarf::DW_OP_mul}); - case Instruction::SDiv: - return applyOps({dwarf::DW_OP_constu, Val, dwarf::DW_OP_div}); - case Instruction::SRem: - return applyOps({dwarf::DW_OP_constu, Val, dwarf::DW_OP_mod}); - case Instruction::Or: - return applyOps({dwarf::DW_OP_constu, Val, dwarf::DW_OP_or}); - case Instruction::And: - return applyOps({dwarf::DW_OP_constu, Val, dwarf::DW_OP_and}); - case Instruction::Xor: - return applyOps({dwarf::DW_OP_constu, Val, dwarf::DW_OP_xor}); - case Instruction::Shl: - return applyOps({dwarf::DW_OP_constu, Val, dwarf::DW_OP_shl}); - case Instruction::LShr: - return applyOps({dwarf::DW_OP_constu, Val, dwarf::DW_OP_shr}); - case Instruction::AShr: - return applyOps({dwarf::DW_OP_constu, Val, dwarf::DW_OP_shra}); - default: - // TODO: Salvage constants from each kind of binop we know about. - return nullptr; - } - // *Not* to do: we should not attempt to salvage load instructions, - // because the validity and lifetime of a dbg.value containing - // DW_OP_deref becomes difficult to analyze. See PR40628 for examples. - } - return nullptr; -} - /// A replacement for a dbg.value expression. using DbgValReplacement = Optional; @@ -1789,7 +1634,7 @@ function_ref RewriteExpr) { // Find debug users of From. SmallVector Users; - findDbgUsers(Users, &From); + From.findDbgUsers(Users); if (Users.empty()) return false; @@ -1833,7 +1678,7 @@ if (!UndefOrSalvage.empty()) { // Try to salvage the remaining debug users. - salvageDebugInfo(From); + From.salvageDebugInfo(); Changed = true; } @@ -2640,7 +2485,7 @@ void llvm::dropDebugUsers(Instruction &I) { SmallVector DbgUsers; - findDbgUsers(DbgUsers, &I); + I.findDbgUsers(DbgUsers); for (auto *DII : DbgUsers) DII->eraseFromParent(); } diff --git a/llvm/lib/Transforms/Utils/PromoteMemoryToRegister.cpp b/llvm/lib/Transforms/Utils/PromoteMemoryToRegister.cpp --- a/llvm/lib/Transforms/Utils/PromoteMemoryToRegister.cpp +++ b/llvm/lib/Transforms/Utils/PromoteMemoryToRegister.cpp @@ -190,13 +190,14 @@ /// This code only looks at accesses to allocas. static bool isInterestingInstruction(const Instruction *I) { return (isa(I) && isa(I->getOperand(0))) || + (isa(I)) || (isa(I) && isa(I->getOperand(1))); } /// Get or calculate the index of the specified instruction. unsigned getInstructionIndex(const Instruction *I) { assert(isInterestingInstruction(I) && - "Not a load/store to/from an alloca?"); + "Not a dbg.value or load/store to/from an alloca?"); // If we already have this instruction number, return it. DenseMap::iterator It = InstNumbers.find(I); @@ -417,6 +418,39 @@ ConvertDebugDeclareToDebugValue(DII, Info.OnlyStore, DIB); DII->eraseFromParent(); } + + if (AI->getModule()->getDwarfVersion() >= 5) { + SmallVector DbgUsers; + AI->findDbgUsers(DbgUsers); + for (auto *DII : DbgUsers) { + if (!isa(DII)) + continue; + DIBuilder DIB(*AI->getModule(), /*AllowUnresolved*/ false); + auto *DIVar = DII->getVariable(); + auto *DIExpr = DII->getExpression(); + SmallVector Ops; + DIExpression::expr_op_iterator Itr = DIExpr->expr_op_begin(); + DIExpression::expr_op_iterator EndItr = DIExpr->expr_op_end(); + if (Itr != EndItr && Itr->getOp() == dwarf::DW_OP_deref) + Itr++; + else + Ops.push_back(dwarf::DW_OP_LLVM_explicit_pointer); + for (; Itr != EndItr; Itr++) { + Itr->appendToVector(Ops); + } + DIExpr = DIExpression::get(DIExpr->getContext(), Ops); + Value *DV = Info.OnlyStore->getValueOperand(); + DebugLoc DeclareLoc = DII->getDebugLoc(); + MDNode *Scope = DeclareLoc.getScope(); + DILocation *InlinedAt = DeclareLoc.getInlinedAt(); + DebugLoc NewLoc = DebugLoc::get(0, 0, Scope, InlinedAt); + + DIB.insertDbgValueIntrinsic(DV, DIVar, DIExpr, NewLoc, DII); + DII->eraseFromParent(); + LBI.deleteValue(DII); + } + } + // Remove the (now dead) store and alloca. Info.OnlyStore->eraseFromParent(); LBI.deleteValue(Info.OnlyStore); @@ -506,6 +540,113 @@ LBI.deleteValue(LI); } + if (AI->getModule()->getDwarfVersion() >= 5) { + StoresByIndex.clear(); + LBI.clear(); + + for (User *U : AI->users()) + if (StoreInst *SI = dyn_cast(U)) + StoresByIndex.push_back( + std::make_pair(LBI.getInstructionIndex(SI), SI)); + + llvm::sort(StoresByIndex, less_first()); + + using DbgValueByIndexTy = + SmallVector, 64>; + DbgValueByIndexTy DbgValueByIndex; + + for (Instruction &I : *(AI->getParent())) + if (DbgValueInst *DVI = dyn_cast(&I)) + DbgValueByIndex.push_back( + std::make_pair(LBI.getInstructionIndex(DVI), DVI)); + + llvm::sort(DbgValueByIndex, less_first()); + + for (auto User : DbgValueByIndex) { + DbgValueInst *DI = User.second; + + if (AI != DI->getValue()) + continue; + + unsigned Idx = LBI.getInstructionIndex(DI); + // Find the nearest store that has a lower index than DI. + StoresByIndexTy::iterator I = llvm::lower_bound( + StoresByIndex, std::make_pair(Idx, static_cast(nullptr)), + less_first()); + if (I == StoresByIndex.begin()) { +#if 0 // do nothing + if (StoresByIndex.empty()) + // If there are no stores, the load takes the undef value. + DI->replaceAllUsesWith(UndefValue::get(LI->getType())); + else + // There is no store before this load, bail out (load may be affected + // by the following stores - see main comment). + return false; +#endif + } else { + // Otherwise, there was a store before this load, the load takes its + // value. Note, if the load was marked as nonnull we don't want to lose + // that information when we erase it. So we preserve it with an assume. + Value *ReplVal = std::prev(I)->second->getOperand(0); +#if 0 + if (AC && LI->getMetadata(LLVMContext::MD_nonnull) && + !isKnownNonZero(ReplVal, DL, 0, AC, LI, &DT)) + addAssumeNonNull(AC, LI); + + // If the replacement value is the load, this must occur in unreachable + // code. + if (ReplVal == LI) + ReplVal = UndefValue::get(LI->getType()); +#endif + DIBuilder DIB(*AI->getModule(), /*AllowUnresolved*/ false); + auto *DIVar = DI->getVariable(); + auto *DIExpr = DI->getExpression(); + SmallVector Ops; + DIExpression::expr_op_iterator Itr = DIExpr->expr_op_begin(); + DIExpression::expr_op_iterator EndItr = DIExpr->expr_op_end(); + if (Itr != EndItr && Itr->getOp() == dwarf::DW_OP_deref) + Itr++; + else + Ops.push_back(dwarf::DW_OP_LLVM_explicit_pointer); + for (; Itr != EndItr; Itr++) { + Itr->appendToVector(Ops); + } + DIExpr = DIExpression::get(DIExpr->getContext(), Ops); + DebugLoc DeclareLoc = DI->getDebugLoc(); + MDNode *Scope = DeclareLoc.getScope(); + DILocation *InlinedAt = DeclareLoc.getInlinedAt(); + DebugLoc NewLoc = DebugLoc::get(0, 0, Scope, InlinedAt); + DIB.insertDbgValueIntrinsic(ReplVal, DIVar, DIExpr, NewLoc, DI); + + DbgValueByIndexTy::iterator Iter = llvm::lower_bound( + DbgValueByIndex, + std::make_pair(Idx, static_cast(nullptr)), + less_first()); + + StoresByIndexTy::iterator EI = StoresByIndex.end(); + while (++Iter != DbgValueByIndex.end()) { + if (DI->getOperand(1) == Iter->second->getOperand(1)) { + EI = llvm::lower_bound( + StoresByIndex, + std::make_pair(Iter->first, static_cast(nullptr)), + less_first()); + break; + } + } + + DI->eraseFromParent(); + LBI.deleteValue(DI); + + while (I != EI) { + ReplVal = I->second->getOperand(0); + DIB.insertDbgValueIntrinsic(ReplVal, DIVar, DIExpr, + I->second->getDebugLoc(), I->second); + I++; + } + } + } + } + // Remove the (now dead) stores and alloca. while (!AI->use_empty()) { StoreInst *SI = cast(AI->user_back()); diff --git a/llvm/test/DebugInfo/X86/LLVM_explicit_pointer.ll b/llvm/test/DebugInfo/X86/LLVM_explicit_pointer.ll new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/X86/LLVM_explicit_pointer.ll @@ -0,0 +1,74 @@ +; RUN: llvm-as < %s | llvm-dis | FileCheck %s + +; CHECK: !DIExpression(DW_OP_LLVM_explicit_pointer) + +;--------------------------- +;static const char *b = "opq"; +;volatile int v; +;int main() { +; int var1 = 4; +; int *ptr1; +; +; v++; +; ptr1 = &var1; +; v++; +; +; return *ptr1 - 5; +;} +;--------------------------- + +; ModuleID = 'LLVM_explicit_pointer.c' +source_filename = "LLVM_explicit_pointer.c" +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +@v = common dso_local global i32 0, align 4, !dbg !0 +; Function Attrs: nofree norecurse nounwind uwtable +define dso_local i32 @main() local_unnamed_addr !dbg !12 { +entry: + call void @llvm.dbg.value(metadata i32 4, metadata !16, metadata !DIExpression()), !dbg !19 + %0 = load volatile i32, i32* @v, align 4, !dbg !20, !tbaa !21 + %inc = add nsw i32 %0, 1, !dbg !20 + store volatile i32 %inc, i32* @v, align 4, !dbg !20, !tbaa !21 + + call void @llvm.dbg.value(metadata i32 4, metadata !17, metadata !DIExpression(DW_OP_LLVM_explicit_pointer)), !dbg !19 + + %1 = load volatile i32, i32* @v, align 4, !dbg !25, !tbaa !21 + %inc1 = add nsw i32 %1, 1, !dbg !25 + store volatile i32 %inc1, i32* @v, align 4, !dbg !25, !tbaa !21 + ret i32 -1, !dbg !26 +} +; Function Attrs: nounwind readnone speculatable willreturn +declare void @llvm.dbg.value(metadata, metadata, metadata) + +!llvm.dbg.cu = !{!2} +!llvm.module.flags = !{!8, !9, !10} +!llvm.ident = !{!11} + +!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) +!1 = distinct !DIGlobalVariable(name: "v", scope: !2, file: !3, line: 2, type: !6, isLocal: false, isDefinition: true) +!2 = distinct !DICompileUnit(language: DW_LANG_C99, file: !3, producer: "clang version 10.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !4, globals: !5, splitDebugInlining: false, nameTableKind: None) +!3 = !DIFile(filename: "LLVM_explicit_pointer.c", directory: "/dir", checksumkind: CSK_MD5, checksum: "218aaa8dc9f04b056b56d944d06383dd") +!4 = !{} +!5 = !{!0} +!6 = !DIDerivedType(tag: DW_TAG_volatile_type, baseType: !7) +!7 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!8 = !{i32 7, !"Dwarf Version", i32 5} +!9 = !{i32 2, !"Debug Info Version", i32 3} +!10 = !{i32 1, !"wchar_size", i32 4} +!11 = !{!"clang version 10.0.0"} +!12 = distinct !DISubprogram(name: "main", scope: !3, file: !3, line: 3, type: !13, scopeLine: 3, flags: DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !15) +!13 = !DISubroutineType(types: !14) +!14 = !{!7} +!15 = !{!16, !17} +!16 = !DILocalVariable(name: "var1", scope: !12, file: !3, line: 4, type: !7) +!17 = !DILocalVariable(name: "ptr1", scope: !12, file: !3, line: 5, type: !18) +!18 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) +!19 = !DILocation(line: 0, scope: !12) +!20 = !DILocation(line: 7, column: 4, scope: !12) +!21 = !{!22, !22, i64 0} +!22 = !{!"int", !23, i64 0} +!23 = !{!"omnipotent char", !24, i64 0} +!24 = !{!"Simple C/C++ TBAA"} +!25 = !DILocation(line: 9, column: 4, scope: !12) +!26 = !DILocation(line: 11, column: 3, scope: !12) diff --git a/llvm/test/DebugInfo/X86/LLVM_implicit_pointer.ll b/llvm/test/DebugInfo/X86/LLVM_implicit_pointer.ll new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/X86/LLVM_implicit_pointer.ll @@ -0,0 +1,74 @@ +; RUN: llvm-as < %s | llvm-dis | FileCheck %s + +; CHECK: !DIExpression(DW_OP_LLVM_implicit_pointer, 0) + +;--------------------------- +;static const char *b = "opq"; +;volatile int v; +;int main() { +; int var1 = 4; +; int *ptr1; +; +; v++; +; ptr1 = &var1; +; v++; +; +; return *ptr1 - 5; +;} +;--------------------------- + +; ModuleID = 'LLVM_implicit_pointer.c' +source_filename = "LLVM_implicit_pointer.c" +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +@v = common dso_local global i32 0, align 4, !dbg !0 +; Function Attrs: nofree norecurse nounwind uwtable +define dso_local i32 @main() local_unnamed_addr !dbg !12 { +entry: + call void @llvm.dbg.value(metadata i32 4, metadata !16, metadata !DIExpression()), !dbg !19 + %0 = load volatile i32, i32* @v, align 4, !dbg !20, !tbaa !21 + %inc = add nsw i32 %0, 1, !dbg !20 + store volatile i32 %inc, i32* @v, align 4, !dbg !20, !tbaa !21 + + call void @llvm.dbg.value(metadata i32 4, metadata !17, metadata !DIExpression(DW_OP_LLVM_implicit_pointer, 0)), !dbg !19 + + %1 = load volatile i32, i32* @v, align 4, !dbg !25, !tbaa !21 + %inc1 = add nsw i32 %1, 1, !dbg !25 + store volatile i32 %inc1, i32* @v, align 4, !dbg !25, !tbaa !21 + ret i32 -1, !dbg !26 +} +; Function Attrs: nounwind readnone speculatable willreturn +declare void @llvm.dbg.value(metadata, metadata, metadata) + +!llvm.dbg.cu = !{!2} +!llvm.module.flags = !{!8, !9, !10} +!llvm.ident = !{!11} + +!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) +!1 = distinct !DIGlobalVariable(name: "v", scope: !2, file: !3, line: 2, type: !6, isLocal: false, isDefinition: true) +!2 = distinct !DICompileUnit(language: DW_LANG_C99, file: !3, producer: "clang version 10.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !4, globals: !5, splitDebugInlining: false, nameTableKind: None) +!3 = !DIFile(filename: "LLVM_implicit_pointer.c", directory: "/dir", checksumkind: CSK_MD5, checksum: "218aaa8dc9f04b056b56d944d06383dd") +!4 = !{} +!5 = !{!0} +!6 = !DIDerivedType(tag: DW_TAG_volatile_type, baseType: !7) +!7 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!8 = !{i32 7, !"Dwarf Version", i32 5} +!9 = !{i32 2, !"Debug Info Version", i32 3} +!10 = !{i32 1, !"wchar_size", i32 4} +!11 = !{!"clang version 10.0.0"} +!12 = distinct !DISubprogram(name: "main", scope: !3, file: !3, line: 3, type: !13, scopeLine: 3, flags: DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !15) +!13 = !DISubroutineType(types: !14) +!14 = !{!7} +!15 = !{!16, !17} +!16 = !DILocalVariable(name: "var1", scope: !12, file: !3, line: 4, type: !7) +!17 = !DILocalVariable(name: "ptr1", scope: !12, file: !3, line: 5, type: !18) +!18 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) +!19 = !DILocation(line: 0, scope: !12) +!20 = !DILocation(line: 7, column: 4, scope: !12) +!21 = !{!22, !22, i64 0} +!22 = !{!"int", !23, i64 0} +!23 = !{!"omnipotent char", !24, i64 0} +!24 = !{!"Simple C/C++ TBAA"} +!25 = !DILocation(line: 9, column: 4, scope: !12) +!26 = !DILocation(line: 11, column: 3, scope: !12) diff --git a/llvm/test/DebugInfo/X86/dwarfdump-implicit_pointer_instcomb.c b/llvm/test/DebugInfo/X86/dwarfdump-implicit_pointer_instcomb.c new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/X86/dwarfdump-implicit_pointer_instcomb.c @@ -0,0 +1,28 @@ +// RUN: clang %s -O2 -gdwarf-5 -o %t.o +// RUN: llvm-dwarfdump %t.o | FileCheck %s + +// CHECK-LABEL: DW_AT_name ("__implicit_ptr_tmp_0") +// CHECK-LABEL: DW_TAG_variable +// CHECK-NEXT: DW_AT_location +// CHECK-NEXT: DW_OP_implicit_pointer {{0x.+}} +0 + +// CHECK-LABEL: DW_TAG_formal_parameter +// CHECK-NEXT: DW_AT_location +// CHECK-NEXT: DW_OP_implicit_pointer {{0x.+}} +0 +// CHECK-NEXT: "ptr" + +volatile int gvar = 7; + +int func(int *ptr) { + gvar = *ptr; + return *ptr + 5; +} + +int main() { + int var = 4; + int *ptrVar = &var; + + int res = func(ptrVar); + + return res; +} diff --git a/llvm/test/DebugInfo/X86/dwarfdump-implicit_pointer_instcomb.ll b/llvm/test/DebugInfo/X86/dwarfdump-implicit_pointer_instcomb.ll new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/X86/dwarfdump-implicit_pointer_instcomb.ll @@ -0,0 +1,108 @@ +; RUN: llc %s -O2 -filetype=obj -o %t.o +; RUN: llvm-dwarfdump %t.o | FileCheck %s + +; CHECK-LABEL: DW_AT_name ("__implicit_ptr_tmp_0") +; CHECK-LABEL: DW_TAG_variable +; CHECK-NEXT: DW_AT_location +; CHECK-NEXT: DW_OP_implicit_pointer {{0x.+}} +0 + +; CHECK-LABEL: DW_TAG_formal_parameter +; CHECK-NEXT: DW_AT_location +; CHECK-NEXT: DW_OP_implicit_pointer {{0x.+}} +0 +; CHECK-NEXT: "ptr" + +; Below is the original test case this IR is generated from +;--------------------------- +;volatile int gvar = 7; +; +;int func(int *ptr) { +; gvar = *ptr; +; return *ptr + 5; +;} +; +;int main() { +; int var = 4; +; int *ptrVar = &var; +; +; int res = func(ptrVar); +; +; return res; +;} +;--------------------------- + +; ModuleID = 'dwarfdump-implicit_pointer_instcomb.c' +source_filename = "dwarfdump-implicit_pointer_instcomb.c" +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +@gvar = dso_local global i32 7, align 4, !dbg !0 + +; Function Attrs: nofree norecurse nounwind uwtable +define dso_local i32 @func(i32* nocapture readonly %ptr) local_unnamed_addr #0 !dbg !12 { +entry: + call void @llvm.dbg.value(metadata i32* %ptr, metadata !17, metadata !DIExpression()), !dbg !18 + %0 = load i32, i32* %ptr, align 4, !dbg !19 + store volatile i32 %0, i32* @gvar, align 4, !dbg !24 + %1 = load i32, i32* %ptr, align 4, !dbg !25 + %add = add nsw i32 %1, 5, !dbg !26 + ret i32 %add, !dbg !27 +} + +; Function Attrs: nounwind uwtable +define dso_local i32 @main() local_unnamed_addr #1 !dbg !28 { +entry: + call void @llvm.dbg.value(metadata i32 4, metadata !33, metadata !DIExpression(DW_OP_LLVM_explicit_pointer)), !dbg !35 + call void @llvm.dbg.value(metadata i32 4, metadata !32, metadata !DIExpression()), !dbg !35 + call void @llvm.dbg.value(metadata i32 4, metadata !17, metadata !DIExpression(DW_OP_LLVM_explicit_pointer)), !dbg !36 + store volatile i32 4, i32* @gvar, align 4, !dbg !38 + call void @llvm.dbg.value(metadata i32 9, metadata !34, metadata !DIExpression()), !dbg !35 + ret i32 9, !dbg !39 +} + +; Function Attrs: nounwind readnone speculatable willreturn +declare void @llvm.dbg.value(metadata, metadata, metadata) + +!llvm.dbg.cu = !{!2} +!llvm.module.flags = !{!8, !9, !10} +!llvm.ident = !{!11} + +!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) +!1 = distinct !DIGlobalVariable(name: "gvar", scope: !2, file: !3, line: 14, type: !6, isLocal: false, isDefinition: true) +!2 = distinct !DICompileUnit(language: DW_LANG_C99, file: !3, producer: "clang version 10.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !4, globals: !5, nameTableKind: None) +!3 = !DIFile(filename: "dwarfdump-implicit_pointer_instcomb.c", directory: "/dir", checksumkind: CSK_MD5, checksum: "1c991e568deb5a6321b60bc8bf8be8d6") +!4 = !{} +!5 = !{!0} +!6 = !DIDerivedType(tag: DW_TAG_volatile_type, baseType: !7) +!7 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!8 = !{i32 2, !"Dwarf Version", i32 5} +!9 = !{i32 2, !"Debug Info Version", i32 3} +!10 = !{i32 1, !"wchar_size", i32 4} +!11 = !{!"clang version 11.0.0"} +!12 = distinct !DISubprogram(name: "func", scope: !3, file: !3, line: 16, type: !13, scopeLine: 16, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !16) +!13 = !DISubroutineType(types: !14) +!14 = !{!7, !15} +!15 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) +!16 = !{!17} +!17 = !DILocalVariable(name: "ptr", arg: 1, scope: !12, file: !3, line: 16, type: !15) +!18 = !DILocation(line: 0, scope: !12) +!19 = !DILocation(line: 17, column: 10, scope: !12) +!20 = !{!21, !21, i64 0} +!21 = !{!"int", !22, i64 0} +!22 = !{!"omnipotent char", !23, i64 0} +!23 = !{!"Simple C/C++ TBAA"} +!24 = !DILocation(line: 17, column: 8, scope: !12) +!25 = !DILocation(line: 18, column: 10, scope: !12) +!26 = !DILocation(line: 18, column: 15, scope: !12) +!27 = !DILocation(line: 18, column: 3, scope: !12) +!28 = distinct !DISubprogram(name: "main", scope: !3, file: !3, line: 21, type: !29, scopeLine: 21, flags: DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !31) +!29 = !DISubroutineType(types: !30) +!30 = !{!7} +!31 = !{!32, !33, !34} +!32 = !DILocalVariable(name: "var", scope: !28, file: !3, line: 22, type: !7) +!33 = !DILocalVariable(name: "ptrVar", scope: !28, file: !3, line: 23, type: !15) +!34 = !DILocalVariable(name: "res", scope: !28, file: !3, line: 25, type: !7) +!35 = !DILocation(line: 0, scope: !28) +!36 = !DILocation(line: 0, scope: !12, inlinedAt: !37) +!37 = distinct !DILocation(line: 25, column: 13, scope: !28) +!38 = !DILocation(line: 17, column: 8, scope: !12, inlinedAt: !37) +!39 = !DILocation(line: 27, column: 3, scope: !28) diff --git a/llvm/test/DebugInfo/X86/dwarfdump-implicit_pointer_mem2reg.c b/llvm/test/DebugInfo/X86/dwarfdump-implicit_pointer_mem2reg.c new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/X86/dwarfdump-implicit_pointer_mem2reg.c @@ -0,0 +1,70 @@ +// RUN: clang %s -O2 -gdwarf-5 -o %t.o +// RUN: llvm-dwarfdump %t.o | FileCheck %s + +// CHECK-LABEL: DW_AT_name ("arr1") + +// 1. Test if more than one member location list is printed +// (for pointer to scalar) +// CHECK-LABEL: DW_AT_location +// CHECK-NEXT: : DW_OP_lit0, DW_OP_stack_value +// CHECK-NEXT: : DW_OP_implicit_pointer {{0x.+}} +0 +// CHECK-NEXT: DW_AT_name ("ptr2") + +// 2. Test if More than one member location list is printed +// (for pointer to pointer to scalar) +// CHECK-LABEL: DW_AT_location +// CHECK-NEXT: : DW_OP_lit0, DW_OP_stack_value +// CHECK-NEXT: : DW_OP_implicit_pointer {{0x.+}} +0 +// CHECK-NEXT: DW_AT_name ("ptrptr2") + +// 3. Test if one member location list is printed +// (for pointer to pointer to array) +// CHECK-LABEL: DW_AT_location ( +// CHECK-NEXT: : DW_OP_implicit_pointer {{0x.+}} +0 +// CHECK-NEXT: DW_AT_name ("ptrptr3") +// +// CHECK-LABEL: DW_AT_name ("__implicit_ptr_tmp_5") +// 4. Test if one member location list is printed +// (for pointer to pointer to scalar) +// CHECK-LABEL: DW_AT_location ( +// CHECK-NEXT: : DW_OP_implicit_pointer {{0x.+}} +0 +// CHECK-NEXT: DW_AT_name ("ptrptr1") + +// CHECK-LABEL: DW_AT_name ("__implicit_ptr_tmp_3") +// 5. Test if one member location list is printed +// (for pointer to array) +// CHECK-LABEL: DW_AT_location ( +// CHECK-NEXT: : DW_OP_implicit_pointer {{0x.+}} +0 +// CHECK-NEXT: DW_AT_name ("ptr3") + +// CHECK-LABEL: DW_AT_name ("__implicit_ptr_tmp_1") +// 6. Test if one member location list is printed +// (for pointer to scalar) +// CHECK-LABEL: DW_AT_location ( +// CHECK-NEXT: : DW_OP_implicit_pointer {{0x.+}} +0 +// CHECK-NEXT: DW_AT_name ("ptr1") + +static const char *b = "opq"; +volatile int v; +int main() { + int var1 = 4; + int var2 = 5; + int arr1[2] = {2, 3}; + int *ptr1; + int *ptr2 = 0; + int *ptr3; + int **ptrptr1; + int **ptrptr2 = 0; + int **ptrptr3; + + v++; + ptr1 = &var1; + ptr2 = &var2; + ptr3 = arr1; + ptrptr1 = &ptr1; + ptrptr2 = &ptr2; + ptrptr3 = &ptr3; + v++; + + return arr1[0] + arr1[1] + *ptr1 + *ptr2 + **ptrptr1 + **ptrptr2 - 5; +} diff --git a/llvm/test/DebugInfo/X86/dwarfdump-implicit_pointer_mem2reg.ll b/llvm/test/DebugInfo/X86/dwarfdump-implicit_pointer_mem2reg.ll new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/X86/dwarfdump-implicit_pointer_mem2reg.ll @@ -0,0 +1,150 @@ +; RUN: llc %s -O2 -filetype=obj -o %t.o +; RUN: llvm-dwarfdump %t.o | FileCheck %s + +; CHECK-LABEL: DW_AT_name ("arr1") + +; 1. Test if more than one member location list is printed +; (for pointer to scalar) +; CHECK-LABEL: DW_AT_location +; CHECK-NEXT: : DW_OP_lit0, DW_OP_stack_value +; CHECK-NEXT: : DW_OP_implicit_pointer {{0x.+}} +0 +; CHECK-NEXT: DW_AT_name ("ptr2") + +; 2. Test if More than one member location list is printed +; (for pointer to pointer to scalar) +; CHECK-LABEL: DW_AT_location +; CHECK-NEXT: : DW_OP_lit0, DW_OP_stack_value +; CHECK-NEXT: : DW_OP_implicit_pointer {{0x.+}} +0 +; CHECK-NEXT: DW_AT_name ("ptrptr2") + +; 3. Test if one member location list is printed +; (for pointer to pointer to array) +; CHECK-LABEL: DW_AT_location ( +; CHECK-NEXT: : DW_OP_implicit_pointer {{0x.+}} +0 +; CHECK-NEXT: DW_AT_name ("ptrptr3") +; +; CHECK-LABEL: DW_AT_name ("__implicit_ptr_tmp_5") +; 4. Test if one member location list is printed +; (for pointer to pointer to scalar) +; CHECK-LABEL: DW_AT_location ( +; CHECK-NEXT: : DW_OP_implicit_pointer {{0x.+}} +0 +; CHECK-NEXT: DW_AT_name ("ptrptr1") + +; CHECK-LABEL: DW_AT_name ("__implicit_ptr_tmp_3") +; 5. Test if one member location list is printed +; (for pointer to array) +; CHECK-LABEL: DW_AT_location ( +; CHECK-NEXT: : DW_OP_implicit_pointer {{0x.+}} +0 +; CHECK-NEXT: DW_AT_name ("ptr3") + +; CHECK-LABEL: DW_AT_name ("__implicit_ptr_tmp_1") +; 6. Test if one member location list is printed +; (for pointer to scalar) +; CHECK-LABEL: DW_AT_location ( +; CHECK-NEXT: : DW_OP_implicit_pointer {{0x.+}} +0 +; CHECK-NEXT: DW_AT_name ("ptr1") + +; Below is the original test case this IR is generated from +;--------------------------- +;static const char *b = "opq"; +;volatile int v; +;int main() { +; int var1 = 4; +; int var2 = 5; +; int arr1[2] = {2, 3}; +; int *ptr1; +; int *ptr2 = 0; +; int *ptr3; +; int **ptrptr1; +; int **ptrptr2 = 0; +; int **ptrptr3; +; +; v++; +; ptr1 = &var1; +; ptr2 = &var2; +; ptr3 = arr1; +; ptrptr1 = &ptr1; +; ptrptr2 = &ptr2; +; ptrptr3 = &ptr3; +; v++; +; +; return arr1[0] + arr1[1] + *ptr1 + *ptr2 + **ptrptr1 + **ptrptr2 - 5; +;} +;--------------------------- + +; ModuleID = 'dwarfdump-implicit_pointer_mem2reg.c' +source_filename = "dwarfdump-implicit_pointer_mem2reg.c" +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +@v = common dso_local global i32 0, align 4, !dbg !0 + +; Function Attrs: nofree norecurse nounwind uwtable +define dso_local i32 @main() local_unnamed_addr #0 !dbg !12 { +entry: + call void @llvm.dbg.value(metadata i32 4, metadata !16, metadata !DIExpression()), !dbg !30 + call void @llvm.dbg.value(metadata i32 5, metadata !17, metadata !DIExpression()), !dbg !30 + call void @llvm.dbg.value(metadata i32 2, metadata !18, metadata !DIExpression(DW_OP_LLVM_fragment, 0, 32)), !dbg !30 + call void @llvm.dbg.value(metadata i32 3, metadata !18, metadata !DIExpression(DW_OP_LLVM_fragment, 32, 32)), !dbg !30 + call void @llvm.dbg.value(metadata i32* null, metadata !24, metadata !DIExpression()), !dbg !30 + call void @llvm.dbg.value(metadata i32** null, metadata !28, metadata !DIExpression()), !dbg !30 + %0 = load volatile i32, i32* @v, align 4, !dbg !31, !tbaa !32 + %inc = add nsw i32 %0, 1, !dbg !31 + store volatile i32 %inc, i32* @v, align 4, !dbg !31, !tbaa !32 + call void @llvm.dbg.value(metadata i32 4, metadata !22, metadata !DIExpression(DW_OP_LLVM_explicit_pointer)), !dbg !30 + call void @llvm.dbg.value(metadata i32 5, metadata !24, metadata !DIExpression(DW_OP_LLVM_explicit_pointer)), !dbg !30 + call void @llvm.dbg.value(metadata i32 2, metadata !25, metadata !DIExpression(DW_OP_LLVM_explicit_pointer)), !dbg !30 + call void @llvm.dbg.value(metadata i32 4, metadata !26, metadata !DIExpression(DW_OP_LLVM_explicit_pointer, DW_OP_LLVM_explicit_pointer)), !dbg !30 + call void @llvm.dbg.value(metadata i32 5, metadata !28, metadata !DIExpression(DW_OP_LLVM_explicit_pointer, DW_OP_LLVM_explicit_pointer)), !dbg !30 + call void @llvm.dbg.value(metadata i32 2, metadata !29, metadata !DIExpression(DW_OP_LLVM_explicit_pointer, DW_OP_LLVM_explicit_pointer)), !dbg !30 + %1 = load volatile i32, i32* @v, align 4, !dbg !36, !tbaa !32 + %inc1 = add nsw i32 %1, 1, !dbg !36 + store volatile i32 %inc1, i32* @v, align 4, !dbg !36, !tbaa !32 + ret i32 18, !dbg !37 +} + +; Function Attrs: nounwind readnone speculatable willreturn +declare void @llvm.dbg.value(metadata, metadata, metadata) + +!llvm.dbg.cu = !{!2} +!llvm.module.flags = !{!8, !9, !10} +!llvm.ident = !{!11} + +!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) +!1 = distinct !DIGlobalVariable(name: "v", scope: !2, file: !3, line: 45, type: !6, isLocal: false, isDefinition: true) +!2 = distinct !DICompileUnit(language: DW_LANG_C99, file: !3, producer: "clang version 10.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !4, globals: !5, nameTableKind: None) +!3 = !DIFile(filename: "dwarfdump-implicit_pointer_mem2reg.c", directory: "/dir", checksumkind: CSK_MD5, checksum: "6ad1488ed7a007c3a33e08ec42847d87") +!4 = !{} +!5 = !{!0} +!6 = !DIDerivedType(tag: DW_TAG_volatile_type, baseType: !7) +!7 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!8 = !{i32 2, !"Dwarf Version", i32 5} +!9 = !{i32 2, !"Debug Info Version", i32 3} +!10 = !{i32 1, !"wchar_size", i32 4} +!11 = !{!"clang version 10.0.0"} +!12 = distinct !DISubprogram(name: "main", scope: !3, file: !3, line: 46, type: !13, scopeLine: 46, flags: DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !15) +!13 = !DISubroutineType(types: !14) +!14 = !{!7} +!15 = !{!16, !17, !18, !22, !24, !25, !26, !28, !29} +!16 = !DILocalVariable(name: "var1", scope: !12, file: !3, line: 47, type: !7) +!17 = !DILocalVariable(name: "var2", scope: !12, file: !3, line: 48, type: !7) +!18 = !DILocalVariable(name: "arr1", scope: !12, file: !3, line: 49, type: !19) +!19 = !DICompositeType(tag: DW_TAG_array_type, baseType: !7, size: 64, elements: !20) +!20 = !{!21} +!21 = !DISubrange(count: 2) +!22 = !DILocalVariable(name: "ptr1", scope: !12, file: !3, line: 50, type: !23) +!23 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) +!24 = !DILocalVariable(name: "ptr2", scope: !12, file: !3, line: 51, type: !23) +!25 = !DILocalVariable(name: "ptr3", scope: !12, file: !3, line: 52, type: !23) +!26 = !DILocalVariable(name: "ptrptr1", scope: !12, file: !3, line: 53, type: !27) +!27 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !23, size: 64) +!28 = !DILocalVariable(name: "ptrptr2", scope: !12, file: !3, line: 54, type: !27) +!29 = !DILocalVariable(name: "ptrptr3", scope: !12, file: !3, line: 55, type: !27) +!30 = !DILocation(line: 0, scope: !12) +!31 = !DILocation(line: 57, column: 4, scope: !12) +!32 = !{!33, !33, i64 0} +!33 = !{!"int", !34, i64 0} +!34 = !{!"omnipotent char", !35, i64 0} +!35 = !{!"Simple C/C++ TBAA"} +!36 = !DILocation(line: 64, column: 4, scope: !12) +!37 = !DILocation(line: 66, column: 3, scope: !12) diff --git a/llvm/test/DebugInfo/X86/dwarfdump-implicit_pointer_sroa.c b/llvm/test/DebugInfo/X86/dwarfdump-implicit_pointer_sroa.c new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/X86/dwarfdump-implicit_pointer_sroa.c @@ -0,0 +1,50 @@ +// RUN: clang %s -O2 -gdwarf-5 -o %t.o +// RUN: llvm-dwarfdump %t.o | FileCheck %s + +// CHECK-LABEL: DW_AT_name ("arr2") + +// 1. Test if more than 2 member location list is printed (for pointer pointing to aggregate) +// CHECK-LABEL: DW_AT_location +// CHECK-NEXT: : DW_OP_lit0, DW_OP_stack_value +// CHECK-NEXT: : DW_OP_implicit_pointer {{0x.+}} +0 +// RCHECK-NEXT: : DW_OP_implicit_pointer {{0x.+}} +0) +// CHECK-NEXT: DW_AT_name ("ptr1") + +// 2. Test if location lists are merged to two (for pointer pointing to aggregate) +// CHECK-LABEL: DW_AT_location +// CHECK-NEXT: : DW_OP_lit0, DW_OP_stack_value +// CHECK-NEXT: : DW_OP_implicit_pointer {{0x.+}} +0 +// CHECK-NEXT: : DW_OP_implicit_pointer {{0x.+}} +0 +// CHECK-NEXT: DW_AT_name ("ptr3") + +// 3. Test if one member location list is not omited (for pointer pointing to aggregate) +// CHECK-LABEL: DW_AT_name ("__implicit_ptr_tmp_1") +// CHECK-LABEL: DW_AT_location +// CHECK-NEXT: : DW_OP_implicit_pointer {{0x.+}} +0 +// CHECK-NEXT: : DW_OP_implicit_pointer {{0x.+}} +0 +// CHECK-NEXT: DW_AT_name ("ptr2") + +static const char *b = "opq"; +volatile int v; +int main() { + int arr1[2] = {1, 2}; + int arr2[2] = {6, 7}; + int *ptr1 = 0; + int *ptr2; + int *ptr3 = 0; + + v++; + ptr1 = arr1; + ptr2 = arr2; + ptr3 = arr1; + (*ptr1)++; + (*ptr2)++; + (*ptr3)++; + v++; + ptr1++; + (*ptr1)++; + (*ptr2)++; + (*ptr3)++; + v++; + return arr1[0] + arr1[1] + arr2[0] + arr2[1] - 5; +} diff --git a/llvm/test/DebugInfo/X86/dwarfdump-implicit_pointer_sroa.ll b/llvm/test/DebugInfo/X86/dwarfdump-implicit_pointer_sroa.ll new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/X86/dwarfdump-implicit_pointer_sroa.ll @@ -0,0 +1,146 @@ +; RUN: llc %s -O2 -filetype=obj -o %t.o +; RUN: llvm-dwarfdump %t.o | FileCheck %s + +; CHECK-LABEL: DW_AT_name ("arr2") + +; 1. Test if more than 2 member location list is printed (for pointer pointing to aggregate) +; CHECK-LABEL: DW_AT_location +; CHECK-NEXT: : DW_OP_lit0, DW_OP_stack_value +; CHECK-NEXT: : DW_OP_implicit_pointer {{0x.+}} +0 +; CHECK-NEXT: : DW_OP_implicit_pointer {{0x.+}} +0) +; CHECK-NEXT: DW_AT_name ("ptr1") + +; 2. Test if location lists are merged to two (for pointer pointing to aggregate) +; CHECK-LABEL: DW_AT_location +; CHECK-NEXT: : DW_OP_lit0, DW_OP_stack_value +; CHECK-NEXT: : DW_OP_implicit_pointer {{0x.+}} +0 +; CHECK-NEXT: : DW_OP_implicit_pointer {{0x.+}} +0 +; CHECK-NEXT: DW_AT_name ("ptr3") + +; 3. Test if one member location list is not omited (for pointer pointing to aggregate) +; CHECK-LABEL: DW_AT_name ("__implicit_ptr_tmp_2") +; CHECK-LABEL: DW_AT_location +; CHECK-NEXT: : DW_OP_implicit_pointer {{0x.+}} +0 +; CHECK-NEXT: : DW_OP_implicit_pointer {{0x.+}} +0 +; CHECK-NEXT: DW_AT_name ("ptr2") + +; Below is the original test case this IR is generated from +;--------------------------- +;static const char *b = "opq"; +;volatile int v; +;int main() { +; int arr1[2] = {1, 2}; +; int arr2[2] = {6, 7}; +; int *ptr1 = 0; +; int *ptr2; +; int *ptr3 = 0; +; +; v++; +; ptr1 = arr1; +; ptr2 = arr2; +; ptr3 = arr1; +; (*ptr1)++; +; (*ptr2)++; +; (*ptr3)++; +; v++; +; ptr1++; +; (*ptr1)++; +; (*ptr2)++; +; (*ptr3)++; +; v++; +; return arr1[0] + arr1[1] + arr2[0] + arr2[1] - 5; +;} +;--------------------------- + +; ModuleID = 'dwarfdump-implicit_pointer_sroa.c' +source_filename = "dwarfdump-implicit_pointer_sroa.c" +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +@v = common dso_local global i32 0, align 4, !dbg !0 + +; Function Attrs: nofree norecurse nounwind uwtable +define dso_local i32 @main() local_unnamed_addr #0 !dbg !12 { +entry: + call void @llvm.dbg.value(metadata i32 1, metadata !16, metadata !DIExpression(DW_OP_LLVM_fragment, 0, 32)), !dbg !25 + call void @llvm.dbg.value(metadata i32 2, metadata !16, metadata !DIExpression(DW_OP_LLVM_fragment, 32, 32)), !dbg !25 + call void @llvm.dbg.value(metadata i32 6, metadata !20, metadata !DIExpression(DW_OP_LLVM_fragment, 0, 32)), !dbg !25 + call void @llvm.dbg.value(metadata i32 7, metadata !20, metadata !DIExpression(DW_OP_LLVM_fragment, 32, 32)), !dbg !25 + call void @llvm.dbg.value(metadata i32* null, metadata !21, metadata !DIExpression()), !dbg !25 + call void @llvm.dbg.value(metadata i32* null, metadata !24, metadata !DIExpression()), !dbg !25 + %0 = load volatile i32, i32* @v, align 4, !dbg !26, !tbaa !27 + %inc = add nsw i32 %0, 1, !dbg !26 + store volatile i32 %inc, i32* @v, align 4, !dbg !26, !tbaa !27 + call void @llvm.dbg.value(metadata i32 1, metadata !21, metadata !DIExpression(DW_OP_LLVM_explicit_pointer)), !dbg !25 + call void @llvm.dbg.value(metadata i32 6, metadata !23, metadata !DIExpression(DW_OP_LLVM_explicit_pointer)), !dbg !25 + call void @llvm.dbg.value(metadata i32 1, metadata !24, metadata !DIExpression(DW_OP_LLVM_explicit_pointer)), !dbg !25 + call void @llvm.dbg.value(metadata i32 2, metadata !21, metadata !DIExpression(DW_OP_LLVM_explicit_pointer)), !dbg !31 + call void @llvm.dbg.value(metadata i32 2, metadata !24, metadata !DIExpression(DW_OP_LLVM_explicit_pointer)), !dbg !31 + call void @llvm.dbg.value(metadata i32 2, metadata !16, metadata !DIExpression(DW_OP_LLVM_fragment, 0, 32)), !dbg !25 + call void @llvm.dbg.value(metadata i32 7, metadata !23, metadata !DIExpression(DW_OP_LLVM_explicit_pointer)), !dbg !32 + call void @llvm.dbg.value(metadata i32 7, metadata !20, metadata !DIExpression(DW_OP_LLVM_fragment, 0, 32)), !dbg !25 + call void @llvm.dbg.value(metadata i32 3, metadata !24, metadata !DIExpression(DW_OP_LLVM_explicit_pointer)), !dbg !33 + call void @llvm.dbg.value(metadata i32 3, metadata !16, metadata !DIExpression(DW_OP_LLVM_fragment, 0, 32)), !dbg !25 + %1 = load volatile i32, i32* @v, align 4, !dbg !34, !tbaa !27 + %inc6 = add nsw i32 %1, 1, !dbg !34 + store volatile i32 %inc6, i32* @v, align 4, !dbg !34, !tbaa !27 + call void @llvm.dbg.value(metadata i32 2, metadata !21, metadata !DIExpression(DW_OP_LLVM_explicit_pointer)), !dbg !25 + call void @llvm.dbg.value(metadata i32 3, metadata !21, metadata !DIExpression(DW_OP_LLVM_explicit_pointer)), !dbg !35 + call void @llvm.dbg.value(metadata i32 3, metadata !16, metadata !DIExpression(DW_OP_LLVM_fragment, 32, 32)), !dbg !25 + call void @llvm.dbg.value(metadata i32 8, metadata !23, metadata !DIExpression(DW_OP_LLVM_explicit_pointer)), !dbg !36 + call void @llvm.dbg.value(metadata i32 8, metadata !20, metadata !DIExpression(DW_OP_LLVM_fragment, 0, 32)), !dbg !25 + call void @llvm.dbg.value(metadata i32 4, metadata !24, metadata !DIExpression(DW_OP_LLVM_explicit_pointer)), !dbg !37 + call void @llvm.dbg.value(metadata i32 4, metadata !16, metadata !DIExpression(DW_OP_LLVM_fragment, 0, 32)), !dbg !25 + %2 = load volatile i32, i32* @v, align 4, !dbg !38, !tbaa !27 + %inc10 = add nsw i32 %2, 1, !dbg !38 + store volatile i32 %inc10, i32* @v, align 4, !dbg !38, !tbaa !27 + ret i32 17, !dbg !39 +} + +; Function Attrs: nounwind readnone speculatable willreturn +declare void @llvm.dbg.value(metadata, metadata, metadata) + +!llvm.dbg.cu = !{!2} +!llvm.module.flags = !{!8, !9, !10} +!llvm.ident = !{!11} + +!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) +!1 = distinct !DIGlobalVariable(name: "v", scope: !2, file: !3, line: 25, type: !6, isLocal: false, isDefinition: true) +!2 = distinct !DICompileUnit(language: DW_LANG_C99, file: !3, producer: "clang version 10.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !4, globals: !5, nameTableKind: None) +!3 = !DIFile(filename: "dwarfdump-implicit_pointer_sroa.c", directory: "/dir", checksumkind: CSK_MD5, checksum: "955d25ccee581cfd1dc4cfad0d994e8f") +!4 = !{} +!5 = !{!0} +!6 = !DIDerivedType(tag: DW_TAG_volatile_type, baseType: !7) +!7 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!8 = !{i32 2, !"Dwarf Version", i32 5} +!9 = !{i32 2, !"Debug Info Version", i32 3} +!10 = !{i32 1, !"wchar_size", i32 4} +!11 = !{!"clang version 10.0.0"} +!12 = distinct !DISubprogram(name: "main", scope: !3, file: !3, line: 26, type: !13, scopeLine: 26, flags: DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !15) +!13 = !DISubroutineType(types: !14) +!14 = !{!7} +!15 = !{!16, !20, !21, !23, !24} +!16 = !DILocalVariable(name: "arr1", scope: !12, file: !3, line: 27, type: !17) +!17 = !DICompositeType(tag: DW_TAG_array_type, baseType: !7, size: 64, elements: !18) +!18 = !{!19} +!19 = !DISubrange(count: 2) +!20 = !DILocalVariable(name: "arr2", scope: !12, file: !3, line: 28, type: !17) +!21 = !DILocalVariable(name: "ptr1", scope: !12, file: !3, line: 29, type: !22) +!22 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) +!23 = !DILocalVariable(name: "ptr2", scope: !12, file: !3, line: 30, type: !22) +!24 = !DILocalVariable(name: "ptr3", scope: !12, file: !3, line: 31, type: !22) +!25 = !DILocation(line: 0, scope: !12) +!26 = !DILocation(line: 33, column: 4, scope: !12) +!27 = !{!28, !28, i64 0} +!28 = !{!"int", !29, i64 0} +!29 = !{!"omnipotent char", !30, i64 0} +!30 = !{!"Simple C/C++ TBAA"} +!31 = !DILocation(line: 18, column: 10, scope: !12) +!32 = !DILocation(line: 19, column: 10, scope: !12) +!33 = !DILocation(line: 20, column: 10, scope: !12) +!34 = !DILocation(line: 24, column: 4, scope: !12) +!35 = !DILocation(line: 26, column: 10, scope: !12) +!36 = !DILocation(line: 27, column: 10, scope: !12) +!37 = !DILocation(line: 28, column: 10, scope: !12) +!38 = !DILocation(line: 32, column: 4, scope: !12) +!39 = !DILocation(line: 33, column: 3, scope: !12) diff --git a/llvm/test/DebugInfo/X86/dwarfdump-implicit_pointer_sroa_inline.c b/llvm/test/DebugInfo/X86/dwarfdump-implicit_pointer_sroa_inline.c new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/X86/dwarfdump-implicit_pointer_sroa_inline.c @@ -0,0 +1,28 @@ +// RUN: clang %s -O2 -gdwarf-5 -o %t.o +// RUN: llvm-dwarfdump %t.o | FileCheck %s + +// CHECK: DW_TAG_inlined_subroutine + +// Test if More than 2 member location list is printed (for pointer pointing to aggregate) +// CHECK: DW_TAG_formal_parameter +// CHECK-NEXT: DW_AT_location +// CHECK-NEXT: : DW_OP_implicit_pointer {{0x.+}} +0 +// CHECK-NEXT: : DW_OP_implicit_pointer {{0x.+}} +0) +// CHECK-NEXT: DW_AT_abstract_origin ({{0x.+}} "ptr") + +static const char *b = "opq"; +volatile int v; +static inline void foo(int *ptr) { + (*ptr)++; + v++; + ptr++; + (*ptr)++; + v++; +} + +int main() { + int arr[2] = {1, 2}; + v++; + foo(arr); + return arr[0] + arr[1] - 5; +} diff --git a/llvm/test/DebugInfo/X86/dwarfdump-implicit_pointer_sroa_inline.ll b/llvm/test/DebugInfo/X86/dwarfdump-implicit_pointer_sroa_inline.ll new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/X86/dwarfdump-implicit_pointer_sroa_inline.ll @@ -0,0 +1,101 @@ +; RUN: llc %s -O2 -filetype=obj -o %t.o +; RUN: llvm-dwarfdump %t.o | FileCheck %s + +; CHECK: DW_TAG_inlined_subroutine + +; Test if More than 2 member location list is printed (for pointer pointing to aggregate) +; CHECK: DW_TAG_formal_parameter +; CHECK-NEXT: DW_AT_location +; CHECK-NEXT: : DW_OP_implicit_pointer {{0x.+}} +0 +; CHECK-NEXT: : DW_OP_implicit_pointer {{0x.+}} +0) +; CHECK-NEXT: DW_AT_abstract_origin ({{0x.+}} "ptr") + +; Below is the original test case this IR is generated from +;--------------------------- +;static const char *b = "opq"; +;volatile int v; +;static inline void foo(int *ptr) { +; (*ptr)++; +; v++; +; ptr++; +; (*ptr)++; +; v++; +;} +; +;int main() { +; int arr[2] = {1, 2}; +; v++; +; foo(arr); +; return arr[0] + arr[1] - 5; +;} +;--------------------------- + +; ModuleID = 'dwarfdump-implicit_pointer_sroa_inline.c' +source_filename = "dwarfdump-implicit_pointer_sroa_inline.c" +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +@v = common dso_local global i32 0, align 4, !dbg !0 + +; Function Attrs: nounwind uwtable +define dso_local i32 @main() local_unnamed_addr #0 !dbg !12 { +entry: + %0 = load volatile i32, i32* @v, align 4, !dbg !21, !tbaa !22 + %inc = add nsw i32 %0, 1, !dbg !21 + store volatile i32 %inc, i32* @v, align 4, !dbg !21, !tbaa !22 + call void @llvm.dbg.value(metadata i32 2, metadata !26, metadata !DIExpression(DW_OP_LLVM_explicit_pointer)), !dbg !32 + %1 = load volatile i32, i32* @v, align 4, !dbg !34, !tbaa !22 + %inc1.i = add nsw i32 %1, 1, !dbg !34 + store volatile i32 %inc1.i, i32* @v, align 4, !dbg !34, !tbaa !22 + call void @llvm.dbg.value(metadata i32 3, metadata !26, metadata !DIExpression(DW_OP_LLVM_explicit_pointer)), !dbg !35 + %2 = load volatile i32, i32* @v, align 4, !dbg !36, !tbaa !22 + %inc3.i = add nsw i32 %2, 1, !dbg !36 + store volatile i32 %inc3.i, i32* @v, align 4, !dbg !36, !tbaa !22 + ret i32 0, !dbg !37 +} + +; Function Attrs: nounwind readnone speculatable willreturn +declare void @llvm.dbg.value(metadata, metadata, metadata) + +!llvm.dbg.cu = !{!2} +!llvm.module.flags = !{!8, !9, !10} +!llvm.ident = !{!11} + +!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) +!1 = distinct !DIGlobalVariable(name: "v", scope: !2, file: !3, line: 14, type: !6, isLocal: false, isDefinition: true) +!2 = distinct !DICompileUnit(language: DW_LANG_C99, file: !3, producer: "clang version 10.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !4, globals: !5, nameTableKind: None) +!3 = !DIFile(filename: "dwarfdump-implicit_pointer_sroa_inline.c", directory: "/dir", checksumkind: CSK_MD5, checksum: "f9bbaee6b152b3bc05125796b573408a") +!4 = !{} +!5 = !{!0} +!6 = !DIDerivedType(tag: DW_TAG_volatile_type, baseType: !7) +!7 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!8 = !{i32 2, !"Dwarf Version", i32 5} +!9 = !{i32 2, !"Debug Info Version", i32 3} +!10 = !{i32 1, !"wchar_size", i32 4} +!11 = !{!"clang version 11.0.0"} +!12 = distinct !DISubprogram(name: "main", scope: !3, file: !3, line: 23, type: !13, scopeLine: 23, flags: DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !15) +!13 = !DISubroutineType(types: !14) +!14 = !{!7} +!15 = !{!16} +!16 = !DILocalVariable(name: "arr", scope: !12, file: !3, line: 24, type: !17) +!17 = !DICompositeType(tag: DW_TAG_array_type, baseType: !7, size: 64, elements: !18) +!18 = !{!19} +!19 = !DISubrange(count: 2) +!20 = !DILocation(line: 24, column: 7, scope: !12) +!21 = !DILocation(line: 25, column: 4, scope: !12) +!22 = !{!23, !23, i64 0} +!23 = !{!"int", !24, i64 0} +!24 = !{!"omnipotent char", !25, i64 0} +!25 = !{!"Simple C/C++ TBAA"} +!26 = !DILocalVariable(name: "ptr", arg: 1, scope: !27, file: !3, line: 15, type: !30) +!27 = distinct !DISubprogram(name: "foo", scope: !3, file: !3, line: 15, type: !28, scopeLine: 15, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !31) +!28 = !DISubroutineType(types: !29) +!29 = !{null, !30} +!30 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) +!31 = !{!26} +!32 = !DILocation(line: 16, column: 9, scope: !27, inlinedAt: !33) +!33 = distinct !DILocation(line: 26, column: 3, scope: !12) +!34 = !DILocation(line: 17, column: 4, scope: !27, inlinedAt: !33) +!35 = !DILocation(line: 19, column: 9, scope: !27, inlinedAt: !33) +!36 = !DILocation(line: 20, column: 4, scope: !27, inlinedAt: !33) +!37 = !DILocation(line: 27, column: 3, scope: !12) diff --git a/llvm/test/DebugInfo/X86/implicit_pointer_fast.ll b/llvm/test/DebugInfo/X86/implicit_pointer_fast.ll new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/X86/implicit_pointer_fast.ll @@ -0,0 +1,99 @@ +; RUN: llc %s -fast-isel -O2 -filetype=obj -o %t.o +; RUN: llvm-dwarfdump %t.o | FileCheck %s + +; CHECK-LABEL: DW_TAG_inlined_subroutine + +; Test if More than 2 member location list is printed (for pointer pointing to aggregate) +; CHECK-LABEL: DW_TAG_formal_parameter +; CHECK-NEXT: DW_AT_location +; CHECK-NEXT: : DW_OP_implicit_pointer [[DIE1:0x.+]] +0 +; CHECK-NEXT: : DW_OP_implicit_pointer [[DIE2:0x.+]] +0) +; CHECK-NEXT: DW_AT_abstract_origin ({{0x.+}} "ptr") + +; Below is the original test case this IR is generated from +;--------------------------- +;static const char *b = "opq"; +;volatile int v; +;static inline void foo(int *ptr) { +; (*ptr)++; +; v++; +; ptr++; +; (*ptr)++; +; v++; +;} +; +;int main() { +; int arr[2] = {1, 2}; +; v++; +; foo(arr); +; return arr[0] + arr[1] - 5; +;} +;--------------------------- + +; ModuleID = 'dwarfdump-implicit_pointer_sroa_inline.c' +source_filename = "dwarfdump-implicit_pointer_sroa_inline.c" +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +@v = common dso_local global i32 0, align 4, !dbg !0 + +; Function Attrs: nounwind uwtable +define dso_local i32 @main() local_unnamed_addr #0 !dbg !12 { +entry: + %0 = load volatile i32, i32* @v, align 4, !dbg !20 + %inc = add nsw i32 %0, 1, !dbg !20 + store volatile i32 %inc, i32* @v, align 4, !dbg !20 + call void @llvm.dbg.value(metadata i32 2, metadata !25, metadata !DIExpression(DW_OP_LLVM_explicit_pointer)), !dbg !31 + %1 = load volatile i32, i32* @v, align 4, !dbg !33 + %inc1.i = add nsw i32 %1, 1, !dbg !33 + store volatile i32 %inc1.i, i32* @v, align 4, !dbg !33 + call void @llvm.dbg.value(metadata i32 3, metadata !25, metadata !DIExpression(DW_OP_LLVM_explicit_pointer)), !dbg !34 + %2 = load volatile i32, i32* @v, align 4, !dbg !34 + %inc3.i = add nsw i32 %2, 1, !dbg !34 + store volatile i32 %inc3.i, i32* @v, align 4, !dbg !34 + ret i32 0, !dbg !35 +} + +; Function Attrs: nounwind readnone speculatable willreturn +declare void @llvm.dbg.value(metadata, metadata, metadata) + +!llvm.dbg.cu = !{!2} +!llvm.module.flags = !{!8, !9, !10} +!llvm.ident = !{!11} + +!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) +!1 = distinct !DIGlobalVariable(name: "v", scope: !2, file: !3, line: 14, type: !6, isLocal: false, isDefinition: true) +!2 = distinct !DICompileUnit(language: DW_LANG_C99, file: !3, producer: "clang version 10.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !4, globals: !5, nameTableKind: None) +!3 = !DIFile(filename: "dwarfdump-implicit_pointer_sroa_inline.c", directory: "/dir", checksumkind: CSK_MD5, checksum: "f9bbaee6b152b3bc05125796b573408a") +!4 = !{} +!5 = !{!0} +!6 = !DIDerivedType(tag: DW_TAG_volatile_type, baseType: !7) +!7 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!8 = !{i32 2, !"Dwarf Version", i32 5} +!9 = !{i32 2, !"Debug Info Version", i32 3} +!10 = !{i32 1, !"wchar_size", i32 4} +!11 = !{!"clang version 10.0.0"} +!12 = distinct !DISubprogram(name: "main", scope: !3, file: !3, line: 23, type: !13, scopeLine: 23, flags: DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !15) +!13 = !DISubroutineType(types: !14) +!14 = !{!7} +!15 = !{!16} +!16 = !DILocalVariable(name: "arr", scope: !12, file: !3, line: 24, type: !17) +!17 = !DICompositeType(tag: DW_TAG_array_type, baseType: !7, size: 64, elements: !18) +!18 = !{!19} +!19 = !DISubrange(count: 2) +!20 = !DILocation(line: 25, column: 4, scope: !12) +!21 = !{!22, !22, i64 0} +!22 = !{!"int", !23, i64 0} +!23 = !{!"omnipotent char", !24, i64 0} +!24 = !{!"Simple C/C++ TBAA"} +!25 = !DILocalVariable(name: "ptr", arg: 1, scope: !26, file: !3, line: 15, type: !29) +!26 = distinct !DISubprogram(name: "foo", scope: !3, file: !3, line: 15, type: !27, scopeLine: 15, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !30) +!27 = !DISubroutineType(types: !28) +!28 = !{null, !29} +!29 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) +!30 = !{!25} +!31 = !DILocation(line: 0, scope: !26, inlinedAt: !32) +!32 = distinct !DILocation(line: 26, column: 3, scope: !12) +!33 = !DILocation(line: 17, column: 4, scope: !26, inlinedAt: !32) +!34 = !DILocation(line: 20, column: 4, scope: !26, inlinedAt: !32) +!35 = !DILocation(line: 27, column: 3, scope: !12) diff --git a/llvm/test/DebugInfo/X86/implicit_pointer_global.ll b/llvm/test/DebugInfo/X86/implicit_pointer_global.ll new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/X86/implicit_pointer_global.ll @@ -0,0 +1,99 @@ +; RUN: llc %s -global-isel -O2 -filetype=obj -o %t.o +; RUN: llvm-dwarfdump %t.o | FileCheck %s + +; CHECK-LABEL: DW_TAG_inlined_subroutine + +; Test if More than 2 member location list is printed (for pointer pointing to aggregate) +; CHECK-LABEL: DW_TAG_formal_parameter +; CHECK-NEXT: DW_AT_location +; CHECK-NEXT: : DW_OP_implicit_pointer [[DIE1:0x.+]] +0 +; CHECK-NEXT: : DW_OP_implicit_pointer [[DIE2:0x.+]] +0) +; CHECK-NEXT: DW_AT_abstract_origin ({{0x.+}} "ptr") + +; Below is the original test case this IR is generated from +;--------------------------- +;static const char *b = "opq"; +;volatile int v; +;static inline void foo(int *ptr) { +; (*ptr)++; +; v++; +; ptr++; +; (*ptr)++; +; v++; +;} +; +;int main() { +; int arr[2] = {1, 2}; +; v++; +; foo(arr); +; return arr[0] + arr[1] - 5; +;} +;--------------------------- + +; ModuleID = 'dwarfdump-implicit_pointer_sroa_inline.c' +source_filename = "dwarfdump-implicit_pointer_sroa_inline.c" +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +@v = common dso_local global i32 0, align 4, !dbg !0 + +; Function Attrs: nounwind uwtable +define dso_local i32 @main() local_unnamed_addr #0 !dbg !12 { +entry: + %0 = load volatile i32, i32* @v, align 4, !dbg !20 + %inc = add nsw i32 %0, 1, !dbg !20 + store volatile i32 %inc, i32* @v, align 4, !dbg !20 + call void @llvm.dbg.value(metadata i32 2, metadata !25, metadata !DIExpression(DW_OP_LLVM_explicit_pointer)), !dbg !31 + %1 = load volatile i32, i32* @v, align 4, !dbg !33 + %inc1.i = add nsw i32 %1, 1, !dbg !33 + store volatile i32 %inc1.i, i32* @v, align 4, !dbg !33 + call void @llvm.dbg.value(metadata i32 3, metadata !25, metadata !DIExpression(DW_OP_LLVM_explicit_pointer)), !dbg !34 + %2 = load volatile i32, i32* @v, align 4, !dbg !34 + %inc3.i = add nsw i32 %2, 1, !dbg !34 + store volatile i32 %inc3.i, i32* @v, align 4, !dbg !34 + ret i32 0, !dbg !35 +} + +; Function Attrs: nounwind readnone speculatable willreturn +declare void @llvm.dbg.value(metadata, metadata, metadata) + +!llvm.dbg.cu = !{!2} +!llvm.module.flags = !{!8, !9, !10} +!llvm.ident = !{!11} + +!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) +!1 = distinct !DIGlobalVariable(name: "v", scope: !2, file: !3, line: 14, type: !6, isLocal: false, isDefinition: true) +!2 = distinct !DICompileUnit(language: DW_LANG_C99, file: !3, producer: "clang version 10.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !4, globals: !5, nameTableKind: None) +!3 = !DIFile(filename: "dwarfdump-implicit_pointer_sroa_inline.c", directory: "/dir", checksumkind: CSK_MD5, checksum: "f9bbaee6b152b3bc05125796b573408a") +!4 = !{} +!5 = !{!0} +!6 = !DIDerivedType(tag: DW_TAG_volatile_type, baseType: !7) +!7 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!8 = !{i32 2, !"Dwarf Version", i32 5} +!9 = !{i32 2, !"Debug Info Version", i32 3} +!10 = !{i32 1, !"wchar_size", i32 4} +!11 = !{!"clang version 10.0.0"} +!12 = distinct !DISubprogram(name: "main", scope: !3, file: !3, line: 23, type: !13, scopeLine: 23, flags: DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !15) +!13 = !DISubroutineType(types: !14) +!14 = !{!7} +!15 = !{!16} +!16 = !DILocalVariable(name: "arr", scope: !12, file: !3, line: 24, type: !17) +!17 = !DICompositeType(tag: DW_TAG_array_type, baseType: !7, size: 64, elements: !18) +!18 = !{!19} +!19 = !DISubrange(count: 2) +!20 = !DILocation(line: 25, column: 4, scope: !12) +!21 = !{!22, !22, i64 0} +!22 = !{!"int", !23, i64 0} +!23 = !{!"omnipotent char", !24, i64 0} +!24 = !{!"Simple C/C++ TBAA"} +!25 = !DILocalVariable(name: "ptr", arg: 1, scope: !26, file: !3, line: 15, type: !29) +!26 = distinct !DISubprogram(name: "foo", scope: !3, file: !3, line: 15, type: !27, scopeLine: 15, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition | DISPFlagOptimized, unit: !2, retainedNodes: !30) +!27 = !DISubroutineType(types: !28) +!28 = !{null, !29} +!29 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) +!30 = !{!25} +!31 = !DILocation(line: 0, scope: !26, inlinedAt: !32) +!32 = distinct !DILocation(line: 26, column: 3, scope: !12) +!33 = !DILocation(line: 17, column: 4, scope: !26, inlinedAt: !32) +!34 = !DILocation(line: 20, column: 4, scope: !26, inlinedAt: !32) +!35 = !DILocation(line: 27, column: 3, scope: !12) diff --git a/llvm/test/DebugInfo/X86/implicit_pointer_instcomb.c b/llvm/test/DebugInfo/X86/implicit_pointer_instcomb.c new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/X86/implicit_pointer_instcomb.c @@ -0,0 +1,24 @@ +// RUN: clang %s -O2 -gdwarf-5 -S -emit-llvm -o %t.ll +// RUN: FileCheck %s --input-file=%t.ll + +volatile int gvar = 7; + +int func(int *ptr) { + gvar = *ptr; + return *ptr + 5; +} + +int main() { + int var = 4; + int *ptrVar = &var; + + // CHECK: call void @llvm.dbg.value(metadata i32 4, metadata [[MD1:!.*]], metadata !DIExpression(DW_OP_LLVM_explicit_pointer)) + // CHECK: call void @llvm.dbg.value(metadata i32 4, metadata [[MD2:!.*]], metadata !DIExpression()) + // CHECK: call void @llvm.dbg.value(metadata i32 4, metadata [[MD3:!.*]], metadata !DIExpression(DW_OP_LLVM_explicit_pointer)) + int res = func(ptrVar); + + return res; +} +// CHECK: [[MD3]] = !DILocalVariable(name: "ptr" +// CHECK: [[MD2]] = !DILocalVariable(name: "var" +// CHECK: [[MD1]] = !DILocalVariable(name: "ptrVar" diff --git a/llvm/test/DebugInfo/X86/implicit_pointer_mem2reg.c b/llvm/test/DebugInfo/X86/implicit_pointer_mem2reg.c new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/X86/implicit_pointer_mem2reg.c @@ -0,0 +1,40 @@ +// RUN: clang %s -O2 -gdwarf-5 -S -emit-llvm -o %t.ll +// RUN: FileCheck %s --input-file=%t.ll + +static const char *b = "opq"; +volatile int v; +int main() { + int var1 = 4; + int var2 = 5; + int arr1[2] = {2, 3}; + int *ptr1; + int *ptr2 = 0; + int *ptr3; + int **ptrptr1; + int **ptrptr2 = 0; + int **ptrptr3; + + v++; + ptr1 = &var1; + ptr2 = &var2; + ptr3 = arr1; + // CHECK: call void @llvm.dbg.value(metadata i32 4, metadata [[MD1:!.+]], metadata !DIExpression(DW_OP_LLVM_explicit_pointer)) + // CHECK: call void @llvm.dbg.value(metadata i32 5, metadata [[MD2:!.+]], metadata !DIExpression(DW_OP_LLVM_explicit_pointer)) + // CHECK: call void @llvm.dbg.value(metadata i32 2, metadata [[MD3:!.+]], metadata !DIExpression(DW_OP_LLVM_explicit_pointer)) + ptrptr1 = &ptr1; + ptrptr2 = &ptr2; + ptrptr3 = &ptr3; + // CHECK: call void @llvm.dbg.value(metadata i32 4, metadata [[MD4:!.+]], metadata !DIExpression(DW_OP_LLVM_explicit_pointer, DW_OP_LLVM_explicit_pointer)) + // CHECK: call void @llvm.dbg.value(metadata i32 5, metadata [[MD5:!.+]], metadata !DIExpression(DW_OP_LLVM_explicit_pointer, DW_OP_LLVM_explicit_pointer)) + // CHECK: call void @llvm.dbg.value(metadata i32 2, metadata [[MD6:!.+]], metadata !DIExpression(DW_OP_LLVM_explicit_pointer, DW_OP_LLVM_explicit_pointer)) + + v++; + + return arr1[0] + arr1[1] + *ptr1 + *ptr2 + **ptrptr1 + **ptrptr2 - 5; +} +// CHECK: [[MD1]] = !DILocalVariable(name: "ptr1" +// CHECK: [[MD2]] = !DILocalVariable(name: "ptr2" +// CHECK: [[MD3]] = !DILocalVariable(name: "ptr3" +// CHECK: [[MD4]] = !DILocalVariable(name: "ptrptr1" +// CHECK: [[MD5]] = !DILocalVariable(name: "ptrptr2" +// CHECK: [[MD6]] = !DILocalVariable(name: "ptrptr3" diff --git a/llvm/test/DebugInfo/X86/implicit_pointer_promote2reg.c b/llvm/test/DebugInfo/X86/implicit_pointer_promote2reg.c new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/X86/implicit_pointer_promote2reg.c @@ -0,0 +1,32 @@ +// RUN: clang %s -O2 -gdwarf-5 -S -emit-llvm -o %t.ll +// RUN: FileCheck %s --input-file=%t.ll + +static const char *b = "opq"; +volatile int v; +int main() { + int var1 = 4; + int var2 = 5; + int arr1[2] = {2, 3}; + int *ptr1; + int *ptr2 = 0; + int **ptrptr1; + int **ptrptr2 = 0; + + v++; + ptr1 = &var1; + ptr2 = &var2; + // CHECK: call void @llvm.dbg.value(metadata i32 4, metadata [[MD1:!.+]], metadata !DIExpression(DW_OP_LLVM_explicit_pointer)) + // CHECK: call void @llvm.dbg.value(metadata i32 5, metadata [[MD2:!.+]], metadata !DIExpression(DW_OP_LLVM_explicit_pointer)) + ptrptr1 = &ptr1; + ptrptr2 = &ptr2; + // CHECK: call void @llvm.dbg.value(metadata i32 4, metadata [[MD3:!.+]], metadata !DIExpression(DW_OP_LLVM_explicit_pointer, DW_OP_LLVM_explicit_pointer)) + // CHECK: call void @llvm.dbg.value(metadata i32 5, metadata [[MD4:!.+]], metadata !DIExpression(DW_OP_LLVM_explicit_pointer, DW_OP_LLVM_explicit_pointer)) + + v++; + + return arr1[0] + arr1[1] + *ptr1 + *ptr2 + **ptrptr1 + **ptrptr2 - 5; +} +// CHECK: [[MD1]] = !DILocalVariable(name: "ptr1" +// CHECK: [[MD2]] = !DILocalVariable(name: "ptr2" +// CHECK: [[MD3]] = !DILocalVariable(name: "ptrptr1" +// CHECK: [[MD4]] = !DILocalVariable(name: "ptrptr2" diff --git a/llvm/test/DebugInfo/X86/implicit_pointer_sroa.c b/llvm/test/DebugInfo/X86/implicit_pointer_sroa.c new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/X86/implicit_pointer_sroa.c @@ -0,0 +1,36 @@ +// RUN: clang %s -O2 -gdwarf-5 -S -emit-llvm -o %t.ll +// RUN: FileCheck %s --input-file=%t.ll + +static const char *b = "opq"; +volatile int v; +int main() { + int arr1[2] = {1, 2}; + int arr2[2] = {6, 7}; + int *ptr1 = 0; + int *ptr2; + int *ptr3 = 0; + // CHECK: call void @llvm.dbg.value(metadata i32* null, metadata [[MD1:!.+]], metadata !DIExpression()) + // CHECK: call void @llvm.dbg.value(metadata i32* null, metadata [[MD3:!.+]], metadata !DIExpression()) + v++; + ptr1 = arr1; + ptr2 = arr2; + ptr3 = arr1; + (*ptr1)++; + (*ptr2)++; + (*ptr3)++; + // CHECK: call void @llvm.dbg.value(metadata i32 7, metadata [[MD2:!.*]], metadata !DIExpression(DW_OP_LLVM_explicit_pointer)) + // CHECK: call void @llvm.dbg.value(metadata i32 3, metadata [[MD1]], metadata !DIExpression(DW_OP_LLVM_explicit_pointer)) + // CHECK: call void @llvm.dbg.value(metadata i32 3, metadata [[MD3]], metadata !DIExpression(DW_OP_LLVM_explicit_pointer)) + v++; + ptr1++; + (*ptr1)++; + (*ptr2)++; + (*ptr3)++; + // CHECK: call void @llvm.dbg.value(metadata i32 8, metadata [[MD2]], metadata !DIExpression(DW_OP_LLVM_explicit_pointer)) + // CHECK: call void @llvm.dbg.value(metadata i32 4, metadata [[MD3]], metadata !DIExpression(DW_OP_LLVM_explicit_pointer)) + v++; + return arr1[0] + arr1[1] + arr2[0] + arr2[1] - 5; +} +// CHECK: [[MD1]] = !DILocalVariable(name: "ptr1" +// CHECK: [[MD2]] = !DILocalVariable(name: "ptr2" +// CHECK: [[MD3]] = !DILocalVariable(name: "ptr3" diff --git a/llvm/test/DebugInfo/X86/implicit_pointer_sroa_inline.c b/llvm/test/DebugInfo/X86/implicit_pointer_sroa_inline.c new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/X86/implicit_pointer_sroa_inline.c @@ -0,0 +1,22 @@ +// RUN: clang %s -O2 -gdwarf-5 -S -emit-llvm -o %t.ll +// RUN: FileCheck %s --input-file=%t.ll + +static const char *b = "opq"; +volatile int v; +static inline void foo(int *ptr) { + (*ptr)++; + // CHECK: call void @llvm.dbg.value(metadata i32 2, metadata [[MD:!.*]], metadata !DIExpression(DW_OP_LLVM_explicit_pointer)) + v++; + ptr++; + (*ptr)++; + // CHECK: call void @llvm.dbg.value(metadata i32 3, metadata [[MD:!.*]], metadata !DIExpression(DW_OP_LLVM_explicit_pointer)) + v++; +} + +int main() { + int arr[2] = {1, 2}; + v++; + foo(arr); + return arr[0] + arr[1] - 5; +} +// CHECK: [[MD]] = !DILocalVariable(name: "ptr" diff --git a/llvm/test/DebugInfo/X86/implicit_pointer_temp_dyn_alloc.cc b/llvm/test/DebugInfo/X86/implicit_pointer_temp_dyn_alloc.cc new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/X86/implicit_pointer_temp_dyn_alloc.cc @@ -0,0 +1,15 @@ +// RUN: clang %s -O2 -gdwarf-5 -S -emit-llvm -o %t.ll +// RUN: FileCheck %s --input-file=%t.ll + +int main() { + int *i = new int(3); + int *j = new int(4); + // CHECK: call void @llvm.dbg.value(metadata i32 3, metadata [[MD1:!.+]], metadata !DIExpression(DW_OP_LLVM_explicit_pointer)) + // CHECK: call void @llvm.dbg.value(metadata i32 4, metadata [[MD2:!.+]], metadata !DIExpression(DW_OP_LLVM_explicit_pointer)) + delete i; + delete j; + + return 0; +} +// CHECK: [[MD1]] = !DILocalVariable(name: "i" +// CHECK: [[MD2]] = !DILocalVariable(name: "j" diff --git a/llvm/test/DebugInfo/X86/implicit_pointer_temp_reference.cc b/llvm/test/DebugInfo/X86/implicit_pointer_temp_reference.cc new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/X86/implicit_pointer_temp_reference.cc @@ -0,0 +1,16 @@ +// RUN: clang %s -O2 -gdwarf-5 -S -emit-llvm -o %t.ll +// RUN: FileCheck %s --input-file=%t.ll + +__attribute__((optnone)) int source() { + return 3; +} +__attribute__((optnone)) void f(int i) { +} +inline void sink(const int &p) { + f(p); +} +int main() { + // CHECK: call void @llvm.dbg.value(metadata i32 3, metadata [[MD:!.+]], metadata !DIExpression(DW_OP_LLVM_explicit_pointer)) + sink(source()); +} +// CHECK: [[MD]] = !DILocalVariable(name: "p" diff --git a/llvm/unittests/IR/DebugInfoTest.cpp b/llvm/unittests/IR/DebugInfoTest.cpp --- a/llvm/unittests/IR/DebugInfoTest.cpp +++ b/llvm/unittests/IR/DebugInfoTest.cpp @@ -180,9 +180,9 @@ SmallVector DVIs; findDbgValues(DVIs, &I); - // Delete %b. The dbg.value should now point to undef. + // Delete %b. The dbg.value should now point to %a. I.eraseFromParent(); - EXPECT_TRUE(isa(DVIs[0]->getValue())); + EXPECT_FALSE(isa(DVIs[0]->getValue())); } } // end namespace diff --git a/llvm/unittests/Transforms/Utils/LocalTest.cpp b/llvm/unittests/Transforms/Utils/LocalTest.cpp --- a/llvm/unittests/Transforms/Utils/LocalTest.cpp +++ b/llvm/unittests/Transforms/Utils/LocalTest.cpp @@ -733,7 +733,7 @@ EXPECT_TRUE(replaceAllDbgUsesWith(D, C, C, DT)); SmallVector CDbgVals; - findDbgUsers(CDbgVals, &C); + C.findDbgUsers(CDbgVals); EXPECT_EQ(2U, CDbgVals.size()); EXPECT_TRUE(any_of(CDbgVals, [](DbgVariableIntrinsic *DII) { return isa(DII); @@ -745,7 +745,7 @@ EXPECT_TRUE(replaceAllDbgUsesWith(C, D, D, DT)); SmallVector DDbgVals; - findDbgUsers(DDbgVals, &D); + D.findDbgUsers(DDbgVals); EXPECT_EQ(2U, DDbgVals.size()); EXPECT_TRUE(any_of(DDbgVals, [](DbgVariableIntrinsic *DII) { return isa(DII);