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 @@ -2406,6 +2406,16 @@ return Elements[I]; } + /// Return the number of unique location operands referred to (via + /// DW_OP_LLVM_arg) in this expression; this is not necessarily the number of + /// instances of DW_OP_LLVM_arg within the expression. + /// For example, for the expression: + /// (DW_OP_LLVM_arg 0, DW_OP_LLVM_arg 1, DW_OP_plus, + /// DW_OP_LLVM_arg 0, DW_OP_mul) + /// This function would return 2, as there are two unique location operands + /// (0 and 1). + uint64_t getLocationOperandCount() const; + /// Determine whether this represents a standalone constant value. bool isConstant() const; @@ -2551,6 +2561,10 @@ /// return true with an offset of zero. bool extractIfOffset(int64_t &Offset) const; + /// Returns true iff this DIExpression contains at least one instance of + /// `DW_OP_LLVM_arg, n` for all n in [0, N). + bool hasAllLocationOps(unsigned N) const; + /// Checks if the last 4 elements of the expression are DW_OP_constu DW_OP_swap DW_OP_xderef and extracts the . diff --git a/llvm/include/llvm/IR/Instructions.h b/llvm/include/llvm/IR/Instructions.h --- a/llvm/include/llvm/IR/Instructions.h +++ b/llvm/include/llvm/IR/Instructions.h @@ -1074,7 +1074,9 @@ /// must be at least as wide as the IntPtr type for the address space of /// the base GEP pointer. bool accumulateConstantOffset(const DataLayout &DL, APInt &Offset) const; - + bool collectOffset(const DataLayout &DL, unsigned BitWidth, + SmallDenseMap &VariableOffsets, + APInt &ConstantOffset) const; // Methods for support type inquiry through isa, cast, and dyn_cast: static bool classof(const Instruction *I) { return (I->getOpcode() == Instruction::GetElementPtr); 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 @@ -101,6 +101,11 @@ Value *getVariableLocationOp(unsigned OpIdx, bool AllowNullOp = true) const; void replaceVariableLocationOp(Value *OldValue, Value *NewValue); + /// Adding a new location operand will always result in this intrinsic using + /// an ArgList, and must always be accompanied by a new expression that uses + /// the new operand. + void addVariableLocationOps(ArrayRef NewValues, + DIExpression *NewExpr); void setVariable(DILocalVariable *NewVar) { setArgOperand(1, MetadataAsValue::get(NewVar->getContext(), NewVar)); diff --git a/llvm/include/llvm/IR/Operator.h b/llvm/include/llvm/IR/Operator.h --- a/llvm/include/llvm/IR/Operator.h +++ b/llvm/include/llvm/IR/Operator.h @@ -568,6 +568,15 @@ bool accumulateConstantOffset( const DataLayout &DL, APInt &Offset, function_ref ExternalAnalysis = nullptr) const; + + /// Collect the offset of this GEP as a map of Values to their associated + /// APInt multipliers. + /// + /// The nullptr key maps to the total constant offset, rather than a + /// multiplier. + bool collectOffset(const DataLayout &DL, unsigned BitWidth, + SmallDenseMap &VariableOffsets, + APInt &ConstantOffset) const; }; class PtrToIntOperator 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 @@ -388,7 +388,8 @@ /// appended to the expression. \p LocNo: the index of the location operand to /// which \p I applies, should be 0 for debug info without a DIArgList. DIExpression *salvageDebugInfoImpl(Instruction &I, DIExpression *DIExpr, - bool StackVal, unsigned LocNo); + bool StackVal, unsigned LocNo, + SmallVectorImpl &AdditionalValues); /// 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 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 @@ -1283,6 +1283,10 @@ } void SelectionDAGBuilder::salvageUnresolvedDbgValue(DanglingDebugInfo &DDI) { + // TODO: For the variadic implementation, instead of only checking the fail + // state of `handleDebugValue`, we need know specifically which values were + // invalid, so that we only attempt to salvage those values when processing + // a DIArgList. assert(!isa(DDI.getDI()->getRawLocation()) && "Not implemented for variadic dbg_values"); Value *V = DDI.getDI()->getValue(0); @@ -1306,16 +1310,21 @@ while (isa(V)) { Instruction &VAsInst = *cast(V); // Temporary "0", awaiting real implementation. - DIExpression *NewExpr = salvageDebugInfoImpl(VAsInst, Expr, StackValue, 0); + SmallVector AdditionalValues; + DIExpression *SalvagedExpr = + salvageDebugInfoImpl(VAsInst, Expr, StackValue, 0, AdditionalValues); // If we cannot salvage any further, and haven't yet found a suitable debug // expression, bail out. - if (!NewExpr) + // TODO: If AdditionalValues isn't empty, then the salvage can only be + // represented with a DBG_VALUE_LIST, so we give up. When we have support + // here for variadic dbg_values, remove that condition. + if (!SalvagedExpr || !AdditionalValues.empty()) break; // New value and expr now represent this debuginfo. V = VAsInst.getOperand(0); - Expr = NewExpr; + Expr = SalvagedExpr; // Some kind of simplification occurred: check whether the operand of the // salvaged debug expression can be encoded in this DAG. 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 @@ -1136,6 +1136,17 @@ return false; } +bool DIExpression::hasAllLocationOps(unsigned N) const { + SmallDenseSet SeenOps; + for (auto ExprOp : expr_ops()) + if (ExprOp.getOp() == dwarf::DW_OP_LLVM_arg) + SeenOps.insert(ExprOp.getArg(0)); + for (uint64_t Idx = 0; Idx < N; ++Idx) + if (!is_contained(SeenOps, Idx)) + return false; + return true; +} + const DIExpression *DIExpression::extractAddressClass(const DIExpression *Expr, unsigned &AddrClass) { // FIXME: This seems fragile. Nothing that verifies that these elements @@ -1351,6 +1362,16 @@ return DIExpression::get(Expr->getContext(), Ops); } +uint64_t DIExpression::getLocationOperandCount() const { + uint64_t Result = 0; + for (auto ExprOp : expr_ops()) + if (ExprOp.getOp() == dwarf::DW_OP_LLVM_arg) + Result = Result <= ExprOp.getArg(0) ? ExprOp.getArg(0) + 1 : Result; + assert(hasAllLocationOps(Result) && + "Expression is missing one or more location operands."); + return Result; +} + bool DIExpression::isConstant() const { // Recognize DW_OP_constu C DW_OP_stack_value (DW_OP_LLVM_fragment Len Ofs)?. if (getNumElements() != 3 && getNumElements() != 6) diff --git a/llvm/lib/IR/Instructions.cpp b/llvm/lib/IR/Instructions.cpp --- a/llvm/lib/IR/Instructions.cpp +++ b/llvm/lib/IR/Instructions.cpp @@ -1773,6 +1773,15 @@ return cast(this)->accumulateConstantOffset(DL, Offset); } +bool GetElementPtrInst::collectOffset( + const DataLayout &DL, unsigned BitWidth, + SmallDenseMap &VariableOffsets, + APInt &ConstantOffset) const { + // Delegate to the generic GEPOperator implementation. + return cast(this)->collectOffset(DL, BitWidth, VariableOffsets, + ConstantOffset); +} + //===----------------------------------------------------------------------===// // ExtractElementInst Implementation //===----------------------------------------------------------------------===// 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 @@ -113,6 +113,23 @@ 0, MetadataAsValue::get(getContext(), DIArgList::get(getContext(), MDs))); } +void DbgVariableIntrinsic::addVariableLocationOps(ArrayRef NewValues, + DIExpression *NewExpr) { + assert(NewExpr->hasAllLocationOps(getNumVariableLocationOps() + + NewValues.size()) && + "NewExpr for debug variable intrinsic does not reference every " + "location operand."); + setArgOperand(2, MetadataAsValue::get(getContext(), NewExpr)); + auto LocationOps = getVariableLocationOps(); + SmallVector MDs; + for (auto *VMD : LocationOps) + MDs.push_back(getAsMetadata(VMD)); + for (auto *VMD : NewValues) + MDs.push_back(getAsMetadata(VMD)); + setArgOperand( + 0, MetadataAsValue::get(getContext(), DIArgList::get(getContext(), MDs))); +} + Optional DbgVariableIntrinsic::getFragmentSizeInBits() const { if (auto Fragment = getExpression()->getFragmentInfo()) return Fragment->SizeInBits; diff --git a/llvm/lib/IR/Operator.cpp b/llvm/lib/IR/Operator.cpp --- a/llvm/lib/IR/Operator.cpp +++ b/llvm/lib/IR/Operator.cpp @@ -134,4 +134,63 @@ } return true; } + +bool GEPOperator::collectOffset( + const DataLayout &DL, unsigned BitWidth, + SmallDenseMap &VariableOffsets, + APInt &ConstantOffset) const { + assert(BitWidth == DL.getIndexSizeInBits(getPointerAddressSpace()) && + "The offset bit width does not match DL specification."); + + bool UsedExternalAnalysis = false; + + auto CollectConstantOffset = [&](APInt Index, uint64_t Size) { + Index = Index.sextOrTrunc(BitWidth); + APInt IndexedSize = APInt(BitWidth, Size); + ConstantOffset += Index * IndexedSize; + }; + + for (gep_type_iterator GTI = gep_type_begin(this), GTE = gep_type_end(this); + GTI != GTE; ++GTI) { + // Scalable vectors are multiplied by a runtime constant. + bool ScalableType = isa(GTI.getIndexedType()); + + Value *V = GTI.getOperand(); + StructType *STy = GTI.getStructTypeOrNull(); + // Handle ConstantInt if possible. + if (auto ConstOffset = dyn_cast(V)) { + if (ConstOffset->isZero()) + continue; + // If the type is scalable and the constant is not zero (vscale * n * 0 = + // 0) bailout. + // TODO: If the runtime value is accessible at any point before DWARF + // emission, then we could potentially keep a forward reference to it + // in the debug value to be filled in later. + if (ScalableType) + return false; + // Handle a struct index, which adds its field offset to the pointer. + if (STy) { + unsigned ElementIdx = ConstOffset->getZExtValue(); + const StructLayout *SL = DL.getStructLayout(STy); + // Element offset is in bytes. + CollectConstantOffset(APInt(BitWidth, SL->getElementOffset(ElementIdx)), + 1); + continue; + } + CollectConstantOffset(ConstOffset->getValue(), + DL.getTypeAllocSize(GTI.getIndexedType())); + continue; + } + + if (STy || ScalableType) + return false; + // Insert an initial offset of 0 for V iff none exists already, then + // increment the offset by IndexedSize. + VariableOffsets.try_emplace(V, BitWidth, 0); + APInt IndexedSize = + APInt(BitWidth, DL.getTypeAllocSize(GTI.getIndexedType())); + VariableOffsets[V] += IndexedSize; + } + return true; +} } // namespace llvm 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 @@ -1697,17 +1697,20 @@ is_contained(DIILocation, &I) && "DbgVariableIntrinsic must use salvaged instruction as its location"); unsigned LocNo = std::distance(DIILocation.begin(), find(DIILocation, &I)); - - DIExpression *DIExpr = - salvageDebugInfoImpl(I, DII->getExpression(), StackValue, LocNo); + SmallVector AdditionalValues; + DIExpression *SalvagedExpr = salvageDebugInfoImpl( + I, DII->getExpression(), StackValue, LocNo, AdditionalValues); // salvageDebugInfoImpl should fail on examining the first element of // DbgUsers, or none of them. - if (!DIExpr) + if (!SalvagedExpr) break; DII->replaceVariableLocationOp(&I, I.getOperand(0)); - DII->setExpression(DIExpr); + if (AdditionalValues.empty()) + DII->setExpression(SalvagedExpr); + else + DII->addVariableLocationOps(AdditionalValues, SalvagedExpr); LLVM_DEBUG(dbgs() << "SALVAGE: " << *DII << '\n'); Salvaged = true; } @@ -1721,9 +1724,113 @@ } } -DIExpression *llvm::salvageDebugInfoImpl(Instruction &I, - DIExpression *SrcDIExpr, - bool WithStackValue, unsigned LocNo) { +bool getSalvageOpsForGEP(GetElementPtrInst *GEP, const DataLayout &DL, + uint64_t CurrentLocOps, + SmallVectorImpl &Opcodes, + SmallVectorImpl &AdditionalValues) { + unsigned BitWidth = DL.getIndexSizeInBits(GEP->getPointerAddressSpace()); + // Rewrite a GEP into a DIExpression. + SmallDenseMap VariableOffsets; + APInt ConstantOffset(BitWidth, 0); + if (!GEP->collectOffset(DL, BitWidth, VariableOffsets, ConstantOffset)) + return false; + if (!VariableOffsets.empty() && !CurrentLocOps) { + Opcodes.insert(Opcodes.begin(), {dwarf::DW_OP_LLVM_arg, 0}); + CurrentLocOps = 1; + } + for (auto Offset : VariableOffsets) { + AdditionalValues.push_back(Offset.first); + assert(Offset.second.isStrictlyPositive() && + "Expected strictly positive multiplier for offset."); + Opcodes.append({dwarf::DW_OP_LLVM_arg, CurrentLocOps++, dwarf::DW_OP_constu, + Offset.second.getZExtValue(), dwarf::DW_OP_mul, + dwarf::DW_OP_plus}); + } + if (ConstantOffset.getBoolValue()) + DIExpression::appendOffset(Opcodes, ConstantOffset.getSExtValue()); + return true; +} + +bool getSalvageOpsForBinOp(BinaryOperator *BI, uint64_t CurrentLocOps, + SmallVectorImpl &Opcodes, + SmallVectorImpl &AdditionalValues) { + // Handle binary operations with constant integer operands as a special case. + auto *ConstInt = dyn_cast(BI->getOperand(1)); + // Values wider than 64 bits cannot be represented within a DWARF location + // expression. + if (ConstInt && ConstInt->getBitWidth() > 64) + return false; + + // Push any Constant Int operand onto the expression stack. + if (ConstInt) { + uint64_t Val = ConstInt->getSExtValue(); + // Add or Sub Instructions with a constant operand can potentially be + // simplified. + if (BI->getOpcode() == Instruction::Add) { + DIExpression::appendOffset(Opcodes, Val); + return true; + } + if (BI->getOpcode() == Instruction::Sub) { + DIExpression::appendOffset(Opcodes, -int64_t(Val)); + return true; + } + Opcodes.append({dwarf::DW_OP_constu, Val}); + } else { + if (!CurrentLocOps) { + Opcodes.append({dwarf::DW_OP_LLVM_arg, 0}); + CurrentLocOps = 1; + } + Opcodes.append({dwarf::DW_OP_LLVM_arg, CurrentLocOps}); + AdditionalValues.push_back(BI->getOperand(1)); + } + + // Add salvaged binary operator to expression stack. + switch (BI->getOpcode()) { + case Instruction::Add: + Opcodes.push_back(dwarf::DW_OP_plus); + break; + case Instruction::Sub: + Opcodes.push_back(dwarf::DW_OP_minus); + break; + case Instruction::Mul: + Opcodes.push_back(dwarf::DW_OP_mul); + break; + case Instruction::SDiv: + Opcodes.push_back(dwarf::DW_OP_div); + break; + case Instruction::SRem: + Opcodes.push_back(dwarf::DW_OP_mod); + break; + case Instruction::Or: + Opcodes.push_back(dwarf::DW_OP_or); + break; + case Instruction::And: + Opcodes.push_back(dwarf::DW_OP_and); + break; + case Instruction::Xor: + Opcodes.push_back(dwarf::DW_OP_xor); + break; + case Instruction::Shl: + Opcodes.push_back(dwarf::DW_OP_shl); + break; + case Instruction::LShr: + Opcodes.push_back(dwarf::DW_OP_shr); + break; + case Instruction::AShr: + Opcodes.push_back(dwarf::DW_OP_shra); + break; + default: + // TODO: Salvage from each kind of binop we know about. + return false; + } + return true; +} + +DIExpression * +llvm::salvageDebugInfoImpl(Instruction &I, DIExpression *SrcDIExpr, + bool WithStackValue, unsigned LocNo, + SmallVectorImpl &AdditionalValues) { + uint64_t CurrentLocOps = SrcDIExpr->getLocationOperandCount(); auto &M = *I.getModule(); auto &DL = M.getDataLayout(); @@ -1736,15 +1843,8 @@ 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 * { + auto applyOps = [&](ArrayRef Opcodes) { SmallVector Ops(Opcodes.begin(), Opcodes.end()); return doSalvage(Ops); }; @@ -1767,55 +1867,17 @@ return applyOps(DIExpression::getExtOps(FromTypeBitSize, ToTypeBitSize, isa(&I))); } - + SmallVector Ops; 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; - } + if (getSalvageOpsForGEP(GEP, DL, CurrentLocOps, Ops, AdditionalValues)) + return doSalvage(Ops); } 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. + if (getSalvageOpsForBinOp(BI, CurrentLocOps, Ops, AdditionalValues)) + return doSalvage(Ops); } + // *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; } diff --git a/llvm/test/DebugInfo/salvage-gep.ll b/llvm/test/DebugInfo/salvage-gep.ll new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/salvage-gep.ll @@ -0,0 +1,56 @@ +; RUN: opt %s -dce -S | FileCheck %s + +; Tests the salvaging of GEP instructions, specifically struct indexing and +; non-constant array indexing. + +%struct.S = type { i32, i32 } + +; CHECK: call void @llvm.dbg.value(metadata !DIArgList(%struct.S* %ptr, i64 %offset), +; CHECK-SAME: ![[VAR_OFFSET_PTR:[0-9]+]], +; CHECK-SAME: !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_arg, 1, DW_OP_constu, 8, DW_OP_mul, DW_OP_plus, DW_OP_plus_uconst, 4, DW_OP_stack_value)) + +; CHECK: ![[VAR_OFFSET_PTR]] = !DILocalVariable(name: "offset_ptr" + +define void @"?foo@@YAXPEAUS@@_J@Z"(%struct.S* %ptr, i64 %offset) !dbg !8 { +entry: + call void @llvm.dbg.value(metadata i64 %offset, metadata !20, metadata !DIExpression()), !dbg !24 + call void @llvm.dbg.value(metadata %struct.S* %ptr, metadata !21, metadata !DIExpression()), !dbg !24 + %arrayidx = getelementptr inbounds %struct.S, %struct.S* %ptr, i64 %offset, !dbg !25 + %b = getelementptr inbounds %struct.S, %struct.S* %arrayidx, i32 0, i32 1, !dbg !25 + call void @llvm.dbg.value(metadata i32* %b, metadata !22, metadata !DIExpression()), !dbg !24 + ret void, !dbg !26 +} + +declare void @llvm.dbg.value(metadata, metadata, metadata) + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!3, !4, !5, !6} +!llvm.ident = !{!7} + +!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang version 11.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, nameTableKind: None) +!1 = !DIFile(filename: "salvage-gep.cpp", directory: "/") +!2 = !{} +!3 = !{i32 2, !"CodeView", i32 1} +!4 = !{i32 2, !"Debug Info Version", i32 3} +!5 = !{i32 1, !"wchar_size", i32 2} +!6 = !{i32 7, !"PIC Level", i32 2} +!7 = !{!"clang version 11.0.0"} +!8 = distinct !DISubprogram(name: "foo", linkageName: "?foo@@YAXPEAUS@@_J@Z", scope: !9, file: !9, line: 7, type: !10, scopeLine: 7, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !19) +!9 = !DIFile(filename: ".\\salvage-gep.cpp", directory: "/") +!10 = !DISubroutineType(types: !11) +!11 = !{null, !12, !18} +!12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !13, size: 64) +!13 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "S", file: !9, line: 2, size: 64, flags: DIFlagTypePassByValue, elements: !14, identifier: ".?AUS@@") +!14 = !{!15, !17} +!15 = !DIDerivedType(tag: DW_TAG_member, name: "a", scope: !13, file: !9, line: 3, baseType: !16, size: 32) +!16 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!17 = !DIDerivedType(tag: DW_TAG_member, name: "b", scope: !13, file: !9, line: 4, baseType: !16, size: 32, offset: 32) +!18 = !DIBasicType(name: "long long int", size: 64, encoding: DW_ATE_signed) +!19 = !{!20, !21, !22} +!20 = !DILocalVariable(name: "offset", arg: 2, scope: !8, file: !9, line: 7, type: !18) +!21 = !DILocalVariable(name: "ptr", arg: 1, scope: !8, file: !9, line: 7, type: !12) +!22 = !DILocalVariable(name: "offset_ptr", scope: !8, file: !9, line: 8, type: !23) +!23 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !16, size: 64) +!24 = !DILocation(line: 0, scope: !8) +!25 = !DILocation(line: 8, scope: !8) +!26 = !DILocation(line: 9, scope: !8) diff --git a/llvm/test/DebugInfo/salvage-nonconst-binop.ll b/llvm/test/DebugInfo/salvage-nonconst-binop.ll new file mode 100644 --- /dev/null +++ b/llvm/test/DebugInfo/salvage-nonconst-binop.ll @@ -0,0 +1,45 @@ +; RUN: opt %s -dce -S | FileCheck %s + +; Tests the salvaging of binary operators that use more than one non-constant +; SSA value. + +; CHECK: call void @llvm.dbg.value(metadata !DIArgList(i32 %a, i32 %b), +; CHECK-SAME: ![[VAR_C:[0-9]+]], +; CHECK-SAME: !DIExpression(DW_OP_LLVM_arg, 0, DW_OP_LLVM_arg, 1, DW_OP_plus, DW_OP_stack_value)) + +; CHECK: ![[VAR_C]] = !DILocalVariable(name: "c" + +define i32 @"?multiply@@YAHHH@Z"(i32 %a, i32 %b) !dbg !8 { +entry: + call void @llvm.dbg.value(metadata i32 %b, metadata !12, metadata !DIExpression()), !dbg !13 + call void @llvm.dbg.value(metadata i32 %a, metadata !14, metadata !DIExpression()), !dbg !13 + %add = add nsw i32 %a, %b, !dbg !15 + call void @llvm.dbg.value(metadata i32 %add, metadata !16, metadata !DIExpression()), !dbg !13 + %mul = mul nsw i32 %a, %b, !dbg !17 + ret i32 %mul, !dbg !17 +} + +declare void @llvm.dbg.value(metadata, metadata, metadata) + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!3, !4, !5, !6} +!llvm.ident = !{!7} + +!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang version 11.0.0", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, nameTableKind: None) +!1 = !DIFile(filename: "test.cpp", directory: "/") +!2 = !{} +!3 = !{i32 2, !"CodeView", i32 1} +!4 = !{i32 2, !"Debug Info Version", i32 3} +!5 = !{i32 1, !"wchar_size", i32 2} +!6 = !{i32 7, !"PIC Level", i32 2} +!7 = !{!"clang version 11.0.0"} +!8 = distinct !DISubprogram(name: "multiply", linkageName: "?multiply@@YAHHH@Z", scope: !1, file: !1, line: 1, type: !9, scopeLine: 1, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !2) +!9 = !DISubroutineType(types: !10) +!10 = !{!11, !11, !11} +!11 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!12 = !DILocalVariable(name: "b", arg: 2, scope: !8, file: !1, line: 1, type: !11) +!13 = !DILocation(line: 0, scope: !8) +!14 = !DILocalVariable(name: "a", arg: 1, scope: !8, file: !1, line: 1, type: !11) +!15 = !DILocation(line: 2, scope: !8) +!16 = !DILocalVariable(name: "c", scope: !8, file: !1, line: 2, type: !11) +!17 = !DILocation(line: 3, scope: !8) diff --git a/llvm/test/Transforms/Reassociate/undef_intrinsics_when_deleting_instructions.ll b/llvm/test/Transforms/Reassociate/undef_intrinsics_when_deleting_instructions.ll --- a/llvm/test/Transforms/Reassociate/undef_intrinsics_when_deleting_instructions.ll +++ b/llvm/test/Transforms/Reassociate/undef_intrinsics_when_deleting_instructions.ll @@ -3,93 +3,70 @@ ; Check that reassociate pass now undefs debug intrinsics that reference a value ; that gets dropped and cannot be salvaged. -define hidden i32 @main() local_unnamed_addr { +; CHECK-NOT: %add = fadd fast float %a, %b +; CHECK: call void @llvm.dbg.value(metadata float undef, metadata [[VAR_X:![0-9]+]], metadata !DIExpression()) + +; CHECK-LABEL: if.then: +; CHECK-NOT: %add1 = fadd fast float %add, %c +; CHECK: call void @llvm.dbg.value(metadata float undef, metadata [[VAR_Y:![0-9]+]], metadata !DIExpression()) +; CHECK: [[VAR_Y]] = !DILocalVariable(name: "y" +; CHECK-DAG: [[VAR_X]] = !DILocalVariable(name: "x" + +define float @"?foo@@YAMMMMM@Z"(float %a, float %b, float %c, float %d) !dbg !8 { entry: - %foo = alloca i32, align 4, !dbg !20 - %foo.0.foo.0..sroa_cast = bitcast i32* %foo to i8*, !dbg !20 - call void @llvm.lifetime.start.p0i8(i64 4, i8* nonnull %foo.0.foo.0..sroa_cast), !dbg !20 - store volatile i32 4, i32* %foo, align 4, !dbg !20, !tbaa !21 - %foo.0.foo.0. = load volatile i32, i32* %foo, align 4, !dbg !25, !tbaa !21 - %foo.0.foo.0.15 = load volatile i32, i32* %foo, align 4, !dbg !27, !tbaa !21 - %foo.0.foo.0.16 = load volatile i32, i32* %foo, align 4, !dbg !28, !tbaa !21 - ; CHECK-NOT: %add = add nsw i32 %foo.0.foo.0., %foo.0.foo.0.15 - %add = add nsw i32 %foo.0.foo.0., %foo.0.foo.0.15, !dbg !29 - ; CHECK: call void @llvm.dbg.value(metadata i32 undef, metadata [[VAR_A:![0-9]+]], metadata !DIExpression()) - call void @llvm.dbg.value(metadata i32 %add, metadata !19, metadata !DIExpression()), !dbg !26 - %foo.0.foo.0.17 = load volatile i32, i32* %foo, align 4, !dbg !30, !tbaa !21 - %cmp = icmp eq i32 %foo.0.foo.0.17, 4, !dbg !30 - br i1 %cmp, label %if.then, label %if.end, !dbg !32 + call void @llvm.dbg.value(metadata float %d, metadata !12, metadata !DIExpression()), !dbg !13 + call void @llvm.dbg.value(metadata float %c, metadata !14, metadata !DIExpression()), !dbg !13 + call void @llvm.dbg.value(metadata float %b, metadata !15, metadata !DIExpression()), !dbg !13 + call void @llvm.dbg.value(metadata float %a, metadata !16, metadata !DIExpression()), !dbg !13 + %add = fadd fast float %a, %b, !dbg !17 + call void @llvm.dbg.value(metadata float %add, metadata !18, metadata !DIExpression()), !dbg !13 + %cmp = fcmp fast oeq float %d, 4.000000e+00, !dbg !19 + br i1 %cmp, label %if.then, label %return, !dbg !19 - ; CHECK-LABEL: if.then: -if.then: - ; CHECK-NOT: %add1 = add nsw i32 %add, %foo.0.foo.0.16 - %add1 = add nsw i32 %add, %foo.0.foo.0.16, !dbg !33 - ; CHECK: call void @llvm.dbg.value(metadata i32 undef, metadata [[VAR_A]], metadata !DIExpression()) - call void @llvm.dbg.value(metadata i32 %add1, metadata !19, metadata !DIExpression()), !dbg !26 - ; CHECK: call void @llvm.dbg.value(metadata i32 undef, metadata [[VAR_CHEESE:![0-9]+]], metadata !DIExpression()) - call void @llvm.dbg.value(metadata i32 %add, metadata !18, metadata !DIExpression()), !dbg !26 - %sub = add nsw i32 %add, -12, !dbg !34 - %sub3 = sub nsw i32 %add1, %sub, !dbg !34 - %mul = mul nsw i32 %sub3, 20, !dbg !36 - %div = sdiv i32 %mul, 3, !dbg !37 - br label %if.end, !dbg !38 +if.then: ; preds = %entry + %add1 = fadd fast float %add, %c, !dbg !20 + call void @llvm.dbg.value(metadata float %add1, metadata !23, metadata !DIExpression()), !dbg !24 + %sub = fsub fast float %add, 1.200000e+01, !dbg !25 + %sub2 = fsub fast float %add1, %sub, !dbg !25 + %mul = fmul fast float %sub2, 2.000000e+01, !dbg !25 + %div = fdiv fast float %mul, 3.000000e+00, !dbg !25 + br label %return, !dbg !25 -if.end: - %a.0 = phi i32 [ %div, %if.then ], [ 0, %entry ], !dbg !39 - call void @llvm.lifetime.end.p0i8(i64 4, i8* nonnull %foo.0.foo.0..sroa_cast), !dbg !40 - ret i32 %a.0, !dbg !41 +return: ; preds = %entry, %if.then + %retval.0 = phi float [ %div, %if.then ], [ 0.000000e+00, %entry ], !dbg !13 + ret float %retval.0, !dbg !26 } -declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #1 -declare void @llvm.dbg.declare(metadata, metadata, metadata) #2 -declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #1 -declare void @llvm.dbg.value(metadata, metadata, metadata) #2 +declare void @llvm.dbg.value(metadata, metadata, metadata) !llvm.dbg.cu = !{!0} !llvm.module.flags = !{!3, !4, !5, !6} !llvm.ident = !{!7} -!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang version 10.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, debugInfoForProfiling: true, nameTableKind: None) -!1 = !DIFile(filename: "test.cpp", directory: "F:\") +!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang version 11.0.0", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, nameTableKind: None) +!1 = !DIFile(filename: "undef_intrinsics_when_deleting_instructions.cpp", directory: "/") !2 = !{} -!3 = !{i32 2, !"Dwarf Version", i32 4} +!3 = !{i32 2, !"CodeView", i32 1} !4 = !{i32 2, !"Debug Info Version", i32 3} !5 = !{i32 1, !"wchar_size", i32 2} !6 = !{i32 7, !"PIC Level", i32 2} -!7 = !{!"clang version 10.0.0"} -!8 = distinct !DISubprogram(name: "main", scope: !9, file: !9, line: 1, type: !10, scopeLine: 1, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !13) -!9 = !DIFile(filename: "./test.cpp", directory: "F:\") -!10 = !DISubroutineType(types: !11) -!11 = !{!12} -!12 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) -!13 = !{!14, !16, !17, !18, !19} -!14 = !DILocalVariable(name: "foo", scope: !8, file: !9, line: 2, type: !15) -!15 = !DIDerivedType(tag: DW_TAG_volatile_type, baseType: !12) -!16 = !DILocalVariable(name: "read1", scope: !8, file: !9, line: 3, type: !12) -!17 = !DILocalVariable(name: "read2", scope: !8, file: !9, line: 4, type: !12) -; CHECK: [[VAR_CHEESE]] = !DILocalVariable(name: "cheese" -!18 = !DILocalVariable(name: "cheese", scope: !8, file: !9, line: 6, type: !12) -; CHECK: [[VAR_A]] = !DILocalVariable(name: "a" -!19 = !DILocalVariable(name: "a", scope: !8, file: !9, line: 7, type: !12) -!20 = !DILocation(line: 2, scope: !8) -!21 = !{!22, !22, i64 0} -!22 = !{!"int", !23, i64 0} -!23 = !{!"omnipotent char", !24, i64 0} -!24 = !{!"Simple C++ TBAA"} -!25 = !DILocation(line: 3, scope: !8) -!26 = !DILocation(line: 0, scope: !8) -!27 = !DILocation(line: 4, scope: !8) -!28 = !DILocation(line: 6, scope: !8) -!29 = !DILocation(line: 7, scope: !8) -!30 = !DILocation(line: 10, scope: !31) -!31 = distinct !DILexicalBlock(scope: !8, file: !9, line: 10) -!32 = !DILocation(line: 10, scope: !8) -!33 = !DILocation(line: 8, scope: !8) -!34 = !DILocation(line: 12, scope: !35) -!35 = distinct !DILexicalBlock(scope: !31, file: !9, line: 10) -!36 = !DILocation(line: 13, scope: !35) -!37 = !DILocation(line: 14, scope: !35) -!38 = !DILocation(line: 15, scope: !35) -!39 = !DILocation(line: 0, scope: !31) -!40 = !DILocation(line: 20, scope: !8) -!41 = !DILocation(line: 19, scope: !8) +!7 = !{!"clang version 11.0.0"} +!8 = distinct !DISubprogram(name: "foo", linkageName: "?foo@@YAMMMMM@Z", scope: !1, file: !1, line: 1, type: !9, scopeLine: 1, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !2) +!9 = !DISubroutineType(types: !10) +!10 = !{!11, !11, !11, !11, !11} +!11 = !DIBasicType(name: "float", size: 32, encoding: DW_ATE_float) +!12 = !DILocalVariable(name: "d", arg: 4, scope: !8, file: !1, line: 1, type: !11) +!13 = !DILocation(line: 0, scope: !8) +!14 = !DILocalVariable(name: "c", arg: 3, scope: !8, file: !1, line: 1, type: !11) +!15 = !DILocalVariable(name: "b", arg: 2, scope: !8, file: !1, line: 1, type: !11) +!16 = !DILocalVariable(name: "a", arg: 1, scope: !8, file: !1, line: 1, type: !11) +!17 = !DILocation(line: 2, scope: !8) +!18 = !DILocalVariable(name: "x", scope: !8, file: !1, line: 2, type: !11) +!19 = !DILocation(line: 3, scope: !8) +!20 = !DILocation(line: 4, scope: !21) +!21 = distinct !DILexicalBlock(scope: !22, file: !1, line: 3) +!22 = distinct !DILexicalBlock(scope: !8, file: !1, line: 3) +!23 = !DILocalVariable(name: "y", scope: !21, file: !1, line: 4, type: !11) +!24 = !DILocation(line: 0, scope: !21) +!25 = !DILocation(line: 5, scope: !21) +!26 = !DILocation(line: 8, scope: !8)