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 @@ -1203,6 +1203,14 @@ bool isIdenticalTo(const MachineInstr &Other, MICheckType Check = CheckDefs) const; + /// Returns true if this instruction is a debug instruction that represents an + /// identical debug value to \p Other. + /// This is trivially true if the instructions themselves are identical; + /// otherwise this function compares the DIExpressions of the debug values, + /// folding in whether the debug values are direct and/or variadic, and + /// comparing the debug operands. + bool isEquivalentDbgInstr(const MachineInstr &Other) const; + /// Unlink 'this' from the containing basic block, and return it without /// deleting it. /// 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 @@ -2741,6 +2741,28 @@ /// it cannot be a simple register location. bool isComplex() const; + /// If \p Expr is a non-variadic location (i.e. one that does not contain + /// DW_OP_LLVM_arg), returns Expr converted to variadic form by adding a + /// leading [DW_OP_LLVM_arg, 0] to the expression; otherwise returns Expr. + static const DIExpression * + convertToVariadicExpression(const DIExpression *Expr); + + /// Determines whether two debug values should produce equivalent DWARF + /// expressions, using their DIExpressions and directness, ignoring the + /// difference between otherwise identical expressions in variadic and + /// non-variadic form. + /// \p FirstExpr is the DIExpression for the first debug value. + /// \p FirstIndirect should be true if the first debug value is indirect; in + /// IR this should be true for dbg.declare and dbg.addr intrinsics and false + /// for dbg.values, and in MIR this should be true only for DBG_VALUE + /// instructions whose second operand is an immediate value. + /// \p SecondExpr and \p SecondIndirect have the same meaning as the prior + /// arguments, but apply to the second debug value. + static bool isEqualDebugValue(const DIExpression *FirstExpr, + bool FirstIndirect, + const DIExpression *SecondExpr, + bool SecondIndirect); + /// Append \p Ops with operations to apply the \p Offset. static void appendOffset(SmallVectorImpl &Ops, int64_t Offset); diff --git a/llvm/lib/CodeGen/AsmPrinter/DbgEntityHistoryCalculator.cpp b/llvm/lib/CodeGen/AsmPrinter/DbgEntityHistoryCalculator.cpp --- a/llvm/lib/CodeGen/AsmPrinter/DbgEntityHistoryCalculator.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/DbgEntityHistoryCalculator.cpp @@ -76,7 +76,7 @@ auto &Entries = VarEntries[Var]; if (!Entries.empty() && Entries.back().isDbgValue() && !Entries.back().isClosed() && - Entries.back().getInstr()->isIdenticalTo(MI)) { + Entries.back().getInstr()->isEquivalentDbgInstr(MI)) { LLVM_DEBUG(dbgs() << "Coalescing identical DBG_VALUE entries:\n" << "\t" << Entries.back().getInstr() << "\t" << MI << "\n"); 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 @@ -76,6 +76,9 @@ : EntryKind(E_TargetIndexLocation), TIL(Loc) {} bool isLocation() const { return EntryKind == E_Location; } + bool isIndirectLocation() const { + return EntryKind == E_Location && Loc.isIndirect(); + } bool isTargetIndexLocation() const { return EntryKind == E_TargetIndexLocation; } @@ -150,6 +153,29 @@ bool isFragment() const { return getExpression()->isFragment(); } bool isEntryVal() const { return getExpression()->isEntryValue(); } bool isVariadic() const { return IsVariadic; } + bool isEquivalent(const DbgValueLoc &Other) const { + // Cannot be equivalent with different numbers of entries. + if (ValueLocEntries.size() != Other.ValueLocEntries.size()) + return false; + bool ThisIsIndirect = + !IsVariadic && ValueLocEntries[0].isIndirectLocation(); + bool OtherIsIndirect = + !Other.IsVariadic && Other.ValueLocEntries[0].isIndirectLocation(); + // Check equivalence of DIExpressions + Directness together. + if (!DIExpression::isEqualDebugValue(Expression, ThisIsIndirect, + Other.Expression, OtherIsIndirect)) + return false; + // Indirectness should have been accounted for in the above check, so just + // compare register values directly here. + if (ThisIsIndirect || OtherIsIndirect) { + DbgValueLocEntry ThisOp = ValueLocEntries[0]; + DbgValueLocEntry OtherOp = Other.ValueLocEntries[0]; + return ThisOp.isLocation() && OtherOp.isLocation() && + ThisOp.getLoc().getReg() == OtherOp.getLoc().getReg(); + } + // If neither are indirect, then just compare the loc entries directly. + return ValueLocEntries == Other.ValueLocEntries; + } const DIExpression *getExpression() const { return Expression; } ArrayRef getLocEntries() const { return ValueLocEntries; } friend bool operator==(const DbgValueLoc &, const DbgValueLoc &); @@ -191,11 +217,15 @@ /// Entry. bool MergeRanges(const DebugLocEntry &Next) { // If this and Next are describing the same variable, merge them. - if ((End == Next.Begin && Values == Next.Values)) { - End = Next.End; - return true; - } - return false; + if (End != Next.Begin) + return false; + if (Values.size() != Next.Values.size()) + return false; + for (unsigned EntryIdx = 0; EntryIdx < Values.size(); ++EntryIdx) + if (!Values[EntryIdx].isEquivalent(Next.Values[EntryIdx])) + return false; + End = Next.End; + return true; } const MCSymbol *getBeginSym() const { return Begin; } diff --git a/llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.h b/llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.h --- a/llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.h +++ b/llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.h @@ -247,6 +247,11 @@ Indirect = MI.isDebugOffsetImm(); } + bool isJoinable(const DbgValueProperties &Other) const { + return DIExpression::isEqualDebugValue(DIExpr, Indirect, Other.DIExpr, + Other.Indirect); + } + bool operator==(const DbgValueProperties &Other) const { return std::tie(DIExpr, Indirect, IsVariadic) == std::tie(Other.DIExpr, Other.Indirect, Other.IsVariadic); diff --git a/llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.cpp b/llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.cpp --- a/llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.cpp +++ b/llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.cpp @@ -2672,7 +2672,7 @@ if (OutVal.isUnjoinedPHI() && OutVal.BlockNo != MBB.getNumber()) return false; - if (FirstValue.Properties != OutVal.Properties) + if (!FirstValue.Properties.isJoinable(OutVal.Properties)) return false; for (unsigned Idx = 0; Idx < FirstValue.getLocationOpCount(); ++Idx) { @@ -2860,7 +2860,7 @@ // different DIExpressions, different indirectness, or are mixed constants / // non-constants. for (const auto &V : Values) { - if (V.second->Properties != FirstVal.Properties) + if (!V.second->Properties.isJoinable(FirstVal.Properties)) return false; if (V.second->Kind == DbgValue::NoVal) return false; 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 @@ -667,6 +667,24 @@ return true; } +bool MachineInstr::isEquivalentDbgInstr(const MachineInstr &Other) const { + if (!isDebugValue() || !Other.isDebugValue()) + return false; + // Identical debug instrs are trivially equivalent. + if (isIdenticalTo(Other)) + return true; + if (!DIExpression::isEqualDebugValue( + getDebugExpression(), isIndirectDebugValue(), + Other.getDebugExpression(), Other.isIndirectDebugValue())) + return false; + if (getNumDebugOperands() != Other.getNumDebugOperands()) + return false; + for (unsigned OpIdx = 0; OpIdx < getNumDebugOperands(); ++OpIdx) + if (!getDebugOperand(OpIdx).isIdenticalTo(Other.getDebugOperand(OpIdx))) + return false; + return true; +} + const MachineFunction *MachineInstr::getMF() const { return getParent()->getParent(); } 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 @@ -1286,12 +1286,15 @@ break; } case dwarf::DW_OP_LLVM_entry_value: { - // An entry value operator must appear at the beginning and the number of - // operations it cover can currently only be 1, because we support only - // entry values of a simple register location. One reason for this is that - // we currently can't calculate the size of the resulting DWARF block for - // other expressions. - return I->get() == expr_op_begin()->get() && I->getArg(0) == 1; + // An entry value operator must appear at the beginning or immediately + // following `DW_OP_LLVM_arg 0`, and the number of operations it cover can + // currently only be 1, because we support only entry values of a simple + // register location. One reason for this is that we currently can't + // calculate the size of the resulting DWARF block for other expressions. + auto FirstOp = expr_op_begin(); + if (FirstOp->getOp() == dwarf::DW_OP_LLVM_arg && FirstOp->getArg(0) == 0) + ++FirstOp; + return I->get() == FirstOp->get() && I->getArg(0) == 1; } case dwarf::DW_OP_LLVM_implicit_pointer: case dwarf::DW_OP_LLVM_convert: @@ -1369,6 +1372,34 @@ return false; } +const DIExpression * +DIExpression::convertToVariadicExpression(const DIExpression *Expr) { + if (any_of(Expr->expr_ops(), [](auto ExprOp) { + return ExprOp.getOp() == dwarf::DW_OP_LLVM_arg; + })) + return Expr; + SmallVector NewOps; + NewOps.reserve(Expr->getNumElements() + 2); + NewOps.append({dwarf::DW_OP_LLVM_arg, 0}); + NewOps.append(Expr->elements_begin(), Expr->elements_end()); + return DIExpression::get(Expr->getContext(), NewOps); +} + +bool DIExpression::isEqualDebugValue(const DIExpression *FirstExpr, + bool FirstIndirect, + const DIExpression *SecondExpr, + bool SecondIndirect) { + const DIExpression *FirstVarExpr = + DIExpression::convertToVariadicExpression(FirstExpr); + if (FirstIndirect) + FirstVarExpr = DIExpression::append(FirstVarExpr, dwarf::DW_OP_deref); + const DIExpression *SecondVarExpr = + DIExpression::convertToVariadicExpression(SecondExpr); + if (SecondIndirect) + SecondVarExpr = DIExpression::append(SecondVarExpr, dwarf::DW_OP_deref); + return FirstVarExpr == SecondVarExpr; +} + Optional DIExpression::getFragmentInfo(expr_op_iterator Start, expr_op_iterator End) { for (auto I = Start; I != End; ++I)