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 @@ -23,6 +23,7 @@ #include "llvm/CodeGen/MachineMemOperand.h" #include "llvm/CodeGen/MachineOperand.h" #include "llvm/CodeGen/TargetOpcodes.h" +#include "llvm/IR/DebugInfoMetadata.h" #include "llvm/IR/DebugLoc.h" #include "llvm/IR/InlineAsm.h" #include "llvm/MC/MCInstrDesc.h" @@ -479,6 +480,11 @@ /// Returns the number of non-implicit definitions. unsigned getNumExplicitDefs() const; + /// Returns true if it has implicit pointer expression + bool isImplicitPointer() const { + return getDebugExpression()->isImplicitPointer(); + } + /// iterator/begin/end - Iterate over all operands of a machine instruction. using mop_iterator = MachineOperand *; using const_mop_iterator = const MachineOperand *; 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 @@ -224,10 +224,12 @@ return *this; } - const MachineInstrBuilder &addMetadata(const MDNode *MD) const { + const MachineInstrBuilder &addMetadata(const MDNode *MD, + bool IsImplicitPointer = false) const { MI->addOperand(*MF, MachineOperand::CreateMetadata(MD)); - assert((MI->isDebugValue() ? static_cast(MI->getDebugVariable()) - : true) && + assert(((MI->isDebugValue() && !IsImplicitPointer) + ? static_cast(MI->getDebugVariable()) + : true) && "first MDNode argument of a DBG_VALUE not a variable"); assert((MI->isDebugLabel() ? static_cast(MI->getDebugLabel()) : true) && 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 @@ -1309,6 +1309,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/DebugInfoMetadata.h b/llvm/include/llvm/IR/DebugInfoMetadata.h --- a/llvm/include/llvm/IR/DebugInfoMetadata.h +++ b/llvm/include/llvm/IR/DebugInfoMetadata.h @@ -2467,6 +2467,12 @@ /// 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_implicit_pointer); + } + /// 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 @@ -31,6 +31,7 @@ namespace llvm { class BasicBlock; +class DbgVariableIntrinsic; class FastMathFlags; class MDNode; class Module; @@ -311,6 +312,22 @@ /// Sets the metadata on this instruction from the AAMDNodes structure. void setAAMetadata(const AAMDNodes &N); + /// 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(DIExpression *SrcDIExpr, + bool WithStackValue); + + /// Implementation of salvageDebugInfo, applying only to instructions in + /// \p Insns, rather than all debug users of \p I. + bool salvageDebugInfoForDbgValues(ArrayRef DbgUsers); + + /// 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. + /// Returns true if any debug users were updated. + bool 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/Function.h" #include "llvm/IR/GlobalVariable.h" @@ -117,6 +118,14 @@ /// is described. Optional getFragmentSizeInBits() const; + /// Returns true if it has an implicit pointer expression + bool isImplicitPointer() const { + if (!isa(getRawExpression())) + return false; + + return getExpression()->isImplicitPointer(); + } + /// \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 @@ -189,6 +189,11 @@ ///< Format: (arch)(sub)-(vendor)-(sys0-(abi) void *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 std::map to use later + std::map VarAddrMap; friend class Constant; @@ -202,6 +207,10 @@ /// The module destructor. This will dropAllReferences. ~Module(); + void insertVarAddressMap(DbgVariableIntrinsic *DDI); + + Value *findVarAddressMap(Value *Var); + /// @} /// @name Module Level Accessors /// @{ 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 @@ -33,6 +33,7 @@ class ConstantData; class ConstantAggregate; class DataLayout; +class DbgVariableIntrinsic; class Function; class GlobalAlias; class GlobalIFunc; @@ -313,6 +314,8 @@ } } + 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/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 @@ -27,6 +27,7 @@ #include "llvm/IR/DataLayout.h" #include "llvm/IR/Dominators.h" #include "llvm/IR/GetElementPtrTypeIterator.h" +#include "llvm/IR/IntrinsicInst.h" #include "llvm/IR/Operator.h" #include "llvm/IR/Type.h" #include "llvm/IR/User.h" @@ -315,9 +316,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 @@ -349,23 +347,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. -/// Returns true if any debug users were updated. -bool salvageDebugInfo(Instruction &I); - -/// Implementation of salvageDebugInfo, applying only to instructions in -/// \p Insns, rather than all debug users of \p I. -bool 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/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 @@ -26,10 +26,16 @@ const DIExpression *Expression; /// Type of entry that this represents. - enum EntryType { E_Location, E_Integer, E_ConstantFP, E_ConstantInt }; + enum EntryType { + E_Location, + E_Integer, + E_ConstantFP, + E_ConstantInt, + E_ImplicitPtr + }; enum EntryType EntryKind; - /// Either a constant, + /// a constant, union { int64_t Int; const ConstantFP *CFP; @@ -39,6 +45,9 @@ /// Or a location in the machine frame. MachineLocation Loc; + /// Or an implicit pointer variable target + const DIVariable *ImpPtr; + public: DbgValueLoc(const DIExpression *Expr, int64_t i) : Expression(Expr), EntryKind(E_Integer) { @@ -56,15 +65,21 @@ : Expression(Expr), EntryKind(E_Location), Loc(Loc) { assert(cast(Expr)->isValid()); } + 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 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; } + const DIVariable *getImplicitPointer() const { return ImpPtr; } bool isFragment() const { return getExpression()->isFragment(); } bool isEntryVal() const { return getExpression()->isEntryValue(); } const DIExpression *getExpression() const { return Expression; } @@ -168,6 +183,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 @@ -72,6 +72,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 when available. + std::vector ImplicitVars; + // The base address of this unit, if any. Used for relative references in // ranges/locs. const MCSymbol *BaseAddress = nullptr; @@ -117,6 +126,19 @@ void initStmtList(); + unsigned findOrInsertImplicitVar(const DINode *var) { + std::vector::iterator it = + std::find(ImplicitVars.begin(), ImplicitVars.end(), var); + if (it == ImplicitVars.end()) { + ImplicitVars.push_back(var); + return ImplicitVars.size() - 1; + } else + 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 @@ -989,7 +989,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 @@ -758,7 +758,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 @@ -192,6 +192,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. @@ -247,6 +267,11 @@ return DbgValueLoc(Expr, MI->getOperand(0).getFPImm()); if (MI->getOperand(0).isCImm()) return DbgValueLoc(Expr, MI->getOperand(0).getCImm()); + if (MI->getOperand(0).isMetadata()) { + // first operand can be metadata in case of implicit pointer + const DIVariable *DVI = cast(MI->getOperand(0).getMetadata()); + return DbgValueLoc(Expr, DVI); + } llvm_unreachable("Unexpected 4-operand DBG_VALUE instruction!"); } @@ -1548,10 +1573,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->isImplicitPointer() && + (HistSize == 1 || SingleValueWithClobber)) { const auto *End = SingleValueWithClobber ? HistoryMapEntries[1].getInstr() : nullptr; if (validThroughout(LScopes, MInsn, End)) { @@ -1574,7 +1602,9 @@ // 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->isImplicitPointer() && isValidSingleLocation) { RegVar->initializeDbgValue(Entries[0].getValues()[0]); continue; } @@ -2177,6 +2207,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) + 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++) : ""); @@ -2189,7 +2240,8 @@ 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); @@ -2218,6 +2270,13 @@ } 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)); } @@ -2242,11 +2301,11 @@ "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(); } 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 @@ -188,6 +188,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(). @@ -337,6 +339,7 @@ void emitLegacySExt(unsigned FromBits); void emitLegacyZExt(unsigned FromBits); + void emitImplicitPointer(DIExpressionCursor &&Expr, unsigned ValOffset); }; /// DwarfExpression implementation for .debug_loc entries. @@ -362,6 +365,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; @@ -391,6 +395,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 @@ -576,3 +576,17 @@ emitUnsigned((1ULL << FromBits) - 1); emitOp(dwarf::DW_OP_and); } + +void DwarfExpression::emitImplicitPointer(DIExpressionCursor &&ExprCursor, + unsigned ValOffset) { + auto Op = ExprCursor.take(); + assert(Op->getOp() == dwarf::DW_OP_implicit_pointer && + "not dwarf::DW_OP_implicit_pointer"); + + emitOp(dwarf::DW_OP_implicit_pointer); + // this is a dummy offset which will later be replaced with actual offset + // of variable in .debug_info + 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 @@ -1124,7 +1124,6 @@ // If we removed all uses, nuke the cast. if (CI->use_empty()) { - salvageDebugInfo(*CI); CI->eraseFromParent(); MadeChange = true; } @@ -1684,7 +1683,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/LiveDebugValues.cpp b/llvm/lib/CodeGen/LiveDebugValues.cpp --- a/llvm/lib/CodeGen/LiveDebugValues.cpp +++ b/llvm/lib/CodeGen/LiveDebugValues.cpp @@ -692,7 +692,7 @@ OpenRanges.insert(ID, VL.Var); } else if (MI.hasOneMemOperand()) { llvm_unreachable("DBG_VALUE with mem operand encountered after regalloc?"); - } else { + } else if (!MI.isImplicitPointer()) { // This must be an undefined location. We should leave OpenRanges closed. assert(MI.getOperand(0).isReg() && MI.getOperand(0).getReg() == 0 && "Unexpected non-undef DBG_VALUE encountered"); 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 @@ -744,6 +744,10 @@ // dropped. MIB.addReg(0U); } + } else if (SD->getKind() == SDDbgValue::IMPPTR) { + const MDNode *DV = SD->getImplicitPointer(); + // insert implicit pointer + MIB.addMetadata(DV, /* IsImplicitPointer */ true); } 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 @@ -33,8 +33,10 @@ 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. + 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 @@ -8018,6 +8018,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.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp @@ -1232,7 +1232,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. @@ -1277,6 +1277,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)) { 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 @@ -769,6 +769,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/DWARFExpression.cpp b/llvm/lib/DebugInfo/DWARF/DWARFExpression.cpp --- a/llvm/lib/DebugInfo/DWARF/DWARFExpression.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFExpression.cpp @@ -101,6 +101,8 @@ Descriptions[DW_OP_convert] = Desc(Op::Dwarf5, Op::BaseTypeRef); Descriptions[DW_OP_entry_value] = Desc(Op::Dwarf5, Op::SizeLEB); + Descriptions[DW_OP_implicit_pointer] = + Desc(Op::Dwarf5, Op::SizeRefAddr, Op::SignedSizeLEB); return Descriptions; } 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 @@ -845,6 +845,7 @@ case dwarf::DW_OP_LLVM_tag_offset: case dwarf::DW_OP_LLVM_entry_value: case dwarf::DW_OP_regx: + case dwarf::DW_OP_implicit_pointer: return 2; default: return 1; @@ -922,6 +923,7 @@ case dwarf::DW_OP_dup: case dwarf::DW_OP_regx: case dwarf::DW_OP_bregx: + case dwarf::DW_OP_implicit_pointer: break; } } @@ -992,17 +994,23 @@ } bool DIExpression::extractIfOffset(int64_t &Offset) const { - if (getNumElements() == 0) { + unsigned numElements; + 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 @@ -16,6 +16,7 @@ #include "llvm/IR/Constants.h" #include "llvm/IR/Instructions.h" #include "llvm/IR/MDBuilder.h" +#include "llvm/IR/Module.h" #include "llvm/IR/Operator.h" #include "llvm/IR/Type.h" using namespace llvm; @@ -65,6 +66,9 @@ } iplist::iterator Instruction::eraseFromParent() { + // Try to preserve debug information attached to the instruction. + salvageDebugInfo(); + return getParent()->getInstList().erase(getIterator()); } @@ -752,3 +756,204 @@ MDBuilder MDB(getContext()); setMetadata(LLVMContext::MD_prof, MDB.createBranchWeights(Weights)); } + +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 = + [&](std::initializer_list Opcodes) -> DIExpression * { + SmallVector Ops(Opcodes); + return doSalvage(Ops); + }; + + if (auto *CI = dyn_cast(this)) { + // No-op casts and zexts are irrelevant for debug info. + if (CI->isNoopCast(DL) || isa(this)) + return SrcDIExpr; + return nullptr; + } else 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; +} + +bool Instruction::salvageDebugInfoForDbgValues( + ArrayRef DbgUsers) { + auto &Ctx = getContext(); + auto wrapMD = [&](Value *V) { + return MetadataAsValue::get(Ctx, ValueAsMetadata::get(V)); + }; + Value *debugDeclareAddress = nullptr; + + if ((getModule()->getDwarfVersion() >= 5) && 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); + } + + 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) + return false; + + if (isa(*this) && + (!debugDeclareAddress || DII->isAddressOfVariable())) + continue; + + if (StackValue && debugDeclareAddress) { + int64_t ExprOffset = 0; + if (DII->getExpression()->extractIfOffset(ExprOffset)) { + // It will cause below transformation + // + // Before transformation + // + // %a = alloca [2 x i32], align 4 + // call void @llvm.dbg.declare(metadata [2 x i32]* %arr, metadata !16, + // metadata !DIExpression()), !dbg !22 + // + // call void @llvm.dbg.value(metadata [2 x i32]* %arr, metadata !20, + // metadata !DIExpression()), !dbg !24 + // + // call void @llvm.dbg.value(metadata [2 x i32]* %arr, metadata !20, + // metadata !DIExpression(DW_OP_plus_uconst, 4, DW_OP_stack_value)), + // !dbg !24 + // + // !16 = !DILocalVariable(name: "arr", scope: !12, file: !3, line: 5, + // type: !17) !20 = !DILocalVariable(name: "ptr", scope: !12, file: !3, + // line: 6, type: !21) + // + // + // After transformation + // + // %a = alloca [2 x i32], align 4 + // call void @llvm.dbg.declare(metadata [2 x i32]* %arr, metadata !16, + // metadata !DIExpression()), !dbg !22 + // + // call void @llvm.dbg.value(metadata !16, metadata !20, metadata + // !DIExpression(DW_OP_implicit_pointer, 0)), !dbg !24 + // + // call void @llvm.dbg.value(metadata !16, metadata !20, metadata + // !DIExpression(DW_OP_implicit_pointer, 4)), !dbg !24 + // + DII->setOperand(0, debugDeclareAddress); + SmallVector Ops; + Ops.push_back(dwarf::DW_OP_implicit_pointer); + Ops.push_back(ExprOffset); + DIExpr = DIExpression::get(DIExpr->getContext(), Ops); + } else + return false; + } else + DII->setOperand(0, wrapMD(getOperand(0))); + + DII->setOperand(2, MetadataAsValue::get(Ctx, DIExpr)); + // LLVM_DEBUG(dbgs() << "SALVAGE: " << *DII << '\n'); + } + + return true; +} + +bool 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 false; + + // 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 (DDI->isAddressOfVariable()) + getModule()->insertVarAddressMap(DDI); + + findDbgUsers(DbgUsers); + if (DbgUsers.empty()) + return false; + + return salvageDebugInfoForDbgValues(DbgUsers); +} 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 @@ -41,6 +41,9 @@ 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/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/SymbolTableListTraits.h" @@ -609,3 +610,18 @@ } return GV; } + +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 @@ -451,6 +451,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/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp --- a/llvm/lib/IR/Verifier.cpp +++ b/llvm/lib/IR/Verifier.cpp @@ -4916,8 +4916,9 @@ void Verifier::visitDbgIntrinsic(StringRef Kind, DbgVariableIntrinsic &DII) { auto *MD = cast(DII.getArgOperand(0))->getMetadata(); AssertDI(isa(MD) || - (isa(MD) && !cast(MD)->getNumOperands()), - "invalid llvm.dbg." + Kind + " intrinsic address/value", &DII, MD); + (isa(MD) && !cast(MD)->getNumOperands()) || + DII.isImplicitPointer(), + "invalid llvm.dbg." + Kind + " intrinsic address/value", &DII, MD); AssertDI(isa(DII.getRawVariable()), "invalid llvm.dbg." + Kind + " intrinsic variable", &DII, DII.getRawVariable()); 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 @@ -705,7 +705,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/InstructionCombining.cpp b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp --- a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp +++ b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp @@ -3181,7 +3181,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); for (auto *DII : reverse(DbgUsers)) { if (DII->getParent() == SrcBlock) { if (isa(DII)) { @@ -3205,7 +3205,7 @@ SmallVector TmpUser{ cast(DII->clone())}; - if (!salvageDebugInfoForDbgValues(*I, TmpUser)) { + if (!I->salvageDebugInfoForDbgValues(TmpUser)) { // We are unable to salvage: sink the cloned dbg.value, and mark the // original as undef, terminating any earlier variable location. LLVM_DEBUG(dbgs() << "SINK: " << *DII << '\n'); @@ -3414,7 +3414,7 @@ if (isInstructionTriviallyDead(Inst, TLI)) { ++NumDeadInst; LLVM_DEBUG(dbgs() << "IC: DCE: " << *Inst << '\n'); - if (!salvageDebugInfo(*Inst)) + if (!Inst->salvageDebugInfo()) replaceDbgUsesWithUndef(Inst); Inst->eraseFromParent(); MadeIRChange = true; 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 @@ -101,7 +101,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 @@ -56,7 +56,6 @@ if (isInstructionTriviallyDead(Inst, TLI)) { if (!DebugCounter::shouldExecute(DCECounter)) continue; - salvageDebugInfo(*Inst); Inst->eraseFromParent(); Changed = true; ++DIEEliminated; @@ -87,7 +86,7 @@ if (!DebugCounter::shouldExecute(DCECounter)) return false; - salvageDebugInfo(*I); + I->salvageDebugInfo(); // Null out all of the instruction's operands to see if any operand becomes // dead as we go. 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 @@ -115,7 +115,7 @@ ++NumFastOther; // Try to preserve debug information attached to the dead instruction. - salvageDebugInfo(*DeadInst); + DeadInst->salvageDebugInfo(); // This instruction is dead, zap it, in stages. Start by removing it from // MemDep, which needs to know the operands and needs it to be in the 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 @@ -906,7 +906,7 @@ LLVM_DEBUG(dbgs() << "Skipping due to debug counter\n"); continue; } - if (!salvageDebugInfo(*Inst)) + if (!Inst->salvageDebugInfo()) replaceDbgUsesWithUndef(Inst); removeMSSA(Inst); Inst->eraseFromParent(); 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 @@ -2175,7 +2175,7 @@ for (auto *I : InstrsToErase) { assert(I->getParent() == BB && "Removing instruction from wrong block?"); LLVM_DEBUG(dbgs() << "GVN removed: " << *I << '\n'); - 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 @@ -528,7 +528,7 @@ // used in the loop, instead, just delete it. if (isInstructionTriviallyDead(&I, TLI)) { LLVM_DEBUG(dbgs() << "LICM deleting dead inst: " << I << '\n'); - salvageDebugInfo(I); + I.salvageDebugInfo(); ++II; 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 @@ -938,11 +938,12 @@ if (isReassociableOp(V1, Instruction::Add, Instruction::FAdd) || isReassociableOp(V1, Instruction::Sub, Instruction::FSub)) return true; - Value *VB = Sub->user_back(); - if (Sub->hasOneUse() && - (isReassociableOp(VB, Instruction::Add, Instruction::FAdd) || - isReassociableOp(VB, Instruction::Sub, Instruction::FSub))) - return true; + if (Sub->hasOneUse()) { + Value *VB = Sub->user_back(); + if (isReassociableOp(VB, Instruction::Add, Instruction::FAdd) || + isReassociableOp(VB, Instruction::Sub, Instruction::FSub)) + return true; + } return false; } 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 @@ -4462,6 +4462,11 @@ // 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 (DeadUser->getModule()->getDwarfVersion() >= 5) + DeadUser->salvageDebugInfo(); + // Free up everything used by this instruction. for (Use &DeadOp : DeadUser->operands()) clobberUse(DeadOp); @@ -4511,6 +4516,9 @@ Instruction *I = DeadInsts.pop_back_val(); LLVM_DEBUG(dbgs() << "Deleting dead instruction: " << *I << "\n"); + // Try to preserve debug information attached to the dead instruction. + if (I->getModule()->getDwarfVersion() >= 5) + I->salvageDebugInfo(); // If the instruction is an alloca, find the possible dbg.declare connected // to it, and remove it too. We must do this before calling RAUW or we will // not be able to find it. 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 @@ -1583,7 +1583,7 @@ // from the old function. SmallVector DbgUsers; for (Instruction &I : BB) - findDbgUsers(DbgUsers, &I); + I.findDbgUsers(DbgUsers); for (DbgVariableIntrinsic *DVI : DbgUsers) 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 @@ -461,7 +461,7 @@ "Live instruction found in dead worklist!"); // Don't lose the debug info while deleting the instructions. - salvageDebugInfo(I); + I.salvageDebugInfo(); // Null out all of the instruction's operands to see if any operand becomes // dead as we go. @@ -488,7 +488,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(), @@ -546,7 +546,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. @@ -1421,6 +1421,10 @@ })) continue; + // Try to preserve debug information attached to the instruction. + if (AI->getModule()->getDwarfVersion() >= 5) + AI->salvageDebugInfo(); + for (auto &AIUse : AI->uses()) { User *U = AIUse.getUser(); if (StoreInst *SI = dyn_cast(U)) { @@ -1439,6 +1443,7 @@ CI); } } + DDI->eraseFromParent(); } return true; @@ -1523,19 +1528,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, Instruction *InsertBefore, DIBuilder &Builder, uint8_t DIExprFlags, int Offset) { @@ -1602,127 +1594,6 @@ return MetadataAsValue::get(C, ValueAsMetadata::get(V)); } -bool llvm::salvageDebugInfo(Instruction &I) { - SmallVector DbgUsers; - findDbgUsers(DbgUsers, &I); - if (DbgUsers.empty()) - return false; - - return salvageDebugInfoForDbgValues(I, DbgUsers); -} - -bool llvm::salvageDebugInfoForDbgValues( - Instruction &I, ArrayRef DbgUsers) { - auto &Ctx = I.getContext(); - 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) - return false; - - DII->setOperand(0, wrapMD(I.getOperand(0))); - DII->setOperand(2, MetadataAsValue::get(Ctx, DIExpr)); - LLVM_DEBUG(dbgs() << "SALVAGE: " << *DII << '\n'); - } - - return true; -} - -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 = - [&](std::initializer_list Opcodes) -> DIExpression * { - SmallVector Ops(Opcodes); - return doSalvage(Ops); - }; - - if (auto *CI = dyn_cast(&I)) { - // No-op casts and zexts are irrelevant for debug info. - if (CI->isNoopCast(DL) || isa(&I)) - return SrcDIExpr; - return nullptr; - } else 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; @@ -1734,7 +1605,7 @@ function_ref RewriteExpr) { // Find debug users of From. SmallVector Users; - findDbgUsers(Users, &From); + From.findDbgUsers(Users); if (Users.empty()) return false; @@ -1778,7 +1649,7 @@ if (!DeleteOrSalvage.empty()) { // Try to salvage the remaining debug users. - Changed |= salvageDebugInfo(From); + Changed |= From.salvageDebugInfo(); // Delete the debug users which weren't salvaged. for (auto *DII : DeleteOrSalvage) { @@ -2583,7 +2454,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 @@ -410,6 +410,10 @@ if (!Info.UsingBlocks.empty()) return false; // If not, we'll have to fall back for the remainder. + // Try to preserve debug information attached to the dead instruction. + if (AI->getModule()->getDwarfVersion() >= 5) + AI->salvageDebugInfo(); + // Record debuginfo for the store and remove the declaration's // debuginfo. for (DbgVariableIntrinsic *DII : Info.DbgDeclares) { @@ -506,6 +510,10 @@ LBI.deleteValue(LI); } + // Try to preserve debug information attached to the dead instruction. + if (AI->getModule()->getDwarfVersion() >= 5) + AI->salvageDebugInfo(); + // Remove the (now dead) stores and alloca. while (!AI->use_empty()) { StoreInst *SI = cast(AI->user_back()); diff --git a/llvm/test/DebugInfo/dwarfdump-implicit_pointer_instcomb.c b/llvm/test/DebugInfo/dwarfdump-implicit_pointer_instcomb.c new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/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: DW_AT_name ("var") +// CHECK: DW_TAG_variable +// CHECK-NEXT: DW_AT_location +// CHECK-NEXT: DW_OP_implicit_pointer 0x75 +0 + +// CHECK: DW_TAG_formal_parameter +// CHECK-NEXT: DW_AT_location +// CHECK-NEXT: DW_OP_implicit_pointer 0x75 +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/dwarfdump-implicit_pointer_mem2reg.c b/llvm/test/DebugInfo/dwarfdump-implicit_pointer_mem2reg.c new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/dwarfdump-implicit_pointer_mem2reg.c @@ -0,0 +1,67 @@ +// RUN: clang %s -O2 -gdwarf-5 -o %t.o +// RUN: llvm-dwarfdump %t.o | FileCheck %s + +// CHECK: DW_AT_name ("arr1") + +// 1. Test if More than one member location list is printed +// (for pointer to scalar) +// CHECK: DW_AT_location ( +// CHECK-NEXT: : DW_OP_lit0, DW_OP_stack_value +// CHECK-NEXT: : DW_OP_implicit_pointer 0x53 +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: DW_AT_location ( +// CHECK-NEXT: : DW_OP_lit0, DW_OP_stack_value +// CHECK-NEXT: : DW_OP_implicit_pointer 0x68 +0 +// CHECK-NEXT: DW_AT_name ("ptrptr2") + +// 3. Test if one member location list is printed +// (for pointer to pointer to array) +// CHECK: DW_AT_location ( +// CHECK-NEXT: : DW_OP_implicit_pointer 0x98 +0 +// CHECK-NEXT: DW_AT_name ("ptrptr3") +// +// 4. Test if one member location list is printed +// (for pointer to pointer to scalar) +// CHECK: DW_AT_location ( +// CHECK-NEXT: : DW_OP_implicit_pointer 0xa4 +0 +// CHECK-NEXT: DW_AT_name ("ptrptr1") + +// 5. Test if one member location list is printed +// (for pointer to array) +// CHECK: DW_AT_location ( +// CHECK-NEXT: : DW_OP_implicit_pointer 0x5c +0 +// CHECK-NEXT: DW_AT_name ("ptr3") + +// 6. Test if one member location list is printed +// (for pointer to scalar) +// CHECK: DW_AT_location ( +// CHECK-NEXT: : DW_OP_implicit_pointer 0x4a +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/dwarfdump-implicit_pointer_sroa.c b/llvm/test/DebugInfo/dwarfdump-implicit_pointer_sroa.c new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/dwarfdump-implicit_pointer_sroa.c @@ -0,0 +1,47 @@ +// RUN: clang %s -O2 -gdwarf-5 -o %t.o +// RUN: llvm-dwarfdump %t.o | FileCheck %s + +// CHECK: DW_AT_name ("arr2") + +// 1. Test if More than 2 member location list is printed (for pointer pointing to aggregate) +// CHECK: DW_AT_location ( +// CHECK-NEXT: : DW_OP_lit0, DW_OP_stack_value +// CHECK-NEXT: : DW_OP_implicit_pointer 0x4a +0 +// CHECK-NEXT: : DW_OP_implicit_pointer 0x4a +4) +// CHECK-NEXT: DW_AT_name ("ptr1") + +// 2. Test if location lists are merged to two (for pointer pointing to aggregate) +// CHECK: DW_AT_location ( +// CHECK-NEXT: : DW_OP_lit0, DW_OP_stack_value +// CHECK-NEXT: : DW_OP_implicit_pointer 0x4a +0 +// CHECK-NEXT: DW_AT_name ("ptr3") + +// 3. Test if one member location list is not omited (for pointer pointing to aggregate) +// CHECK: DW_AT_location ( +// CHECK-NEXT: : DW_OP_implicit_pointer 0x56 +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/dwarfdump-implicit_pointer_sroa_inline.c b/llvm/test/DebugInfo/dwarfdump-implicit_pointer_sroa_inline.c new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/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 + +// 1. 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 0x5d +0 +// CHECK-NEXT: : DW_OP_implicit_pointer 0x5d +4) +// CHECK-NEXT: DW_AT_abstract_origin (0x00000040 "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/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 @@ -732,7 +732,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); @@ -744,7 +744,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);