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 @@ -2597,6 +2597,15 @@ static DIExpression *appendOpsToArg(const DIExpression *Expr, ArrayRef Ops, uint64_t ArgNo); + /// 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 /// will be appended to the elements of \c Expr. If \c Expr already contains 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 @@ -1188,6 +1188,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 @@ -2655,7 +2655,7 @@ EXPECT_VALID_FRAGMENT(0, 32, dwarf::DW_OP_deref); EXPECT_VALID_FRAGMENT(0, 32, dwarf::DW_OP_LLVM_fragment, 0, 32); EXPECT_VALID_FRAGMENT(16, 16, dwarf::DW_OP_LLVM_fragment, 0, 32); - + // Invalid fragment expressions (incompatible ops). EXPECT_INVALID_FRAGMENT(0, 32, dwarf::DW_OP_constu, 6, dwarf::DW_OP_plus); EXPECT_INVALID_FRAGMENT(0, 32, dwarf::DW_OP_constu, 14, dwarf::DW_OP_minus); @@ -2668,6 +2668,34 @@ #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) {