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 @@ -2773,8 +2773,17 @@ /// modify a specific location used by \p Expr, such as when salvaging that /// location. static DIExpression *appendOpsToArg(const DIExpression *Expr, - ArrayRef Ops, unsigned ArgNo, - bool StackValue = false); + ArrayRef Ops, unsigned ArgNo, + bool StackValue = false); + + /// Create a copy of \p Expr with each instance of + /// `DW_OP_LLVM_arg, \p OldArg` replaced with `DW_OP_LLVM_arg, \p NewArg`, + /// and each instance of `DW_OP_LLVM_arg, Arg` with `DW_OP_LLVM_arg, Arg - 1` + /// for all Arg > \p OldArg. + /// This is used when replacing one of the operands of a debug value list + /// with another operand in the same list and deleting the old operand. + static DIExpression *replaceArg(const DIExpression *Expr, uint64_t OldArg, + uint64_t NewArg); /// Create a DIExpression to describe one part of an aggregate variable that /// is fragmented across multiple Values. The DW_OP_LLVM_fragment operation 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 @@ -1296,6 +1296,28 @@ return DIExpression::get(Expr->getContext(), NewOps); } +DIExpression *DIExpression::replaceArg(const DIExpression *Expr, + uint64_t OldArg, uint64_t NewArg) { + assert(Expr && "Can't replace args in this expression"); + + SmallVector NewOps; + + for (auto Op : Expr->expr_ops()) { + if (Op.getOp() != dwarf::DW_OP_LLVM_arg || Op.getArg(0) < OldArg) { + Op.appendToVector(NewOps); + continue; + } + NewOps.push_back(dwarf::DW_OP_LLVM_arg); + uint64_t Arg = Op.getArg(0) == OldArg ? NewArg : Op.getArg(0); + // OldArg has been deleted from the Op list, so decrement all indices + // greater than it. + if (Arg > OldArg) + --Arg; + NewOps.push_back(Arg); + } + return DIExpression::get(Expr->getContext(), NewOps); +} + DIExpression *DIExpression::prependOpcodes(const DIExpression *Expr, SmallVectorImpl &Ops, bool StackValue, diff --git a/llvm/unittests/IR/MetadataTest.cpp b/llvm/unittests/IR/MetadataTest.cpp --- a/llvm/unittests/IR/MetadataTest.cpp +++ b/llvm/unittests/IR/MetadataTest.cpp @@ -2862,6 +2862,33 @@ #undef EXPECT_INVALID_FRAGMENT } +TEST_F(DIExpressionTest, replaceArg) { +#define EXPECT_REPLACE_ARG_EQ(Expr, OldArg, NewArg, ...) \ + do { \ + ArrayRef Expected({__VA_ARGS__}); \ + DIExpression *Expression = DIExpression::replaceArg(Expr, OldArg, NewArg); \ + EXPECT_EQ(Expression->getElements(), Expected); \ + } while (false) + + auto N = DIExpression::get( + Context, {dwarf::DW_OP_LLVM_arg, 0, dwarf::DW_OP_LLVM_arg, 1, + dwarf::DW_OP_plus, dwarf::DW_OP_LLVM_arg, 2, dwarf::DW_OP_mul}); + EXPECT_REPLACE_ARG_EQ(N, 0, 1, dwarf::DW_OP_LLVM_arg, 0, + dwarf::DW_OP_LLVM_arg, 0, dwarf::DW_OP_plus, + dwarf::DW_OP_LLVM_arg, 1, dwarf::DW_OP_mul); + EXPECT_REPLACE_ARG_EQ(N, 0, 2, dwarf::DW_OP_LLVM_arg, 1, + dwarf::DW_OP_LLVM_arg, 0, dwarf::DW_OP_plus, + dwarf::DW_OP_LLVM_arg, 1, dwarf::DW_OP_mul); + EXPECT_REPLACE_ARG_EQ(N, 2, 0, dwarf::DW_OP_LLVM_arg, 0, + dwarf::DW_OP_LLVM_arg, 1, dwarf::DW_OP_plus, + dwarf::DW_OP_LLVM_arg, 0, dwarf::DW_OP_mul); + EXPECT_REPLACE_ARG_EQ(N, 2, 1, dwarf::DW_OP_LLVM_arg, 0, + dwarf::DW_OP_LLVM_arg, 1, dwarf::DW_OP_plus, + dwarf::DW_OP_LLVM_arg, 1, dwarf::DW_OP_mul); + +#undef EXPECT_REPLACE_ARG_EQ +} + typedef MetadataTest DIObjCPropertyTest; TEST_F(DIObjCPropertyTest, get) {