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 @@ -1637,6 +1637,9 @@ Optional DIExpression::createFragmentExpression( const DIExpression *Expr, unsigned OffsetInBits, unsigned SizeInBits) { SmallVector Ops; + // Track whether it's safe to split the value at the top of the DWARF stack, + // assuming that it'll be used as an implicit location value. + bool CanSplitValue = true; // Copy over the expression, but leave off any trailing DW_OP_LLVM_fragment. if (Expr) { for (auto Op : Expr->expr_ops()) { @@ -1654,7 +1657,23 @@ // // FIXME: We *could* preserve the lowest fragment of a constant offset // operation if the offset fits into SizeInBits. - return None; + CanSplitValue = false; + break; + case dwarf::DW_OP_deref: + case dwarf::DW_OP_deref_size: + case dwarf::DW_OP_deref_type: + case dwarf::DW_OP_xderef: + case dwarf::DW_OP_xderef_size: + case dwarf::DW_OP_xderef_type: + // Preceeding arithmetic operations have been applied to compute an + // address. It's okay to split the value loaded from that address. + CanSplitValue = true; + break; + case dwarf::DW_OP_stack_value: + // Bail if this expression computes a value that cannot be split. + if (!CanSplitValue) + return None; + break; case dwarf::DW_OP_LLVM_fragment: { // Make the new offset point into the existing fragment. uint64_t FragmentOffsetInBits = Op.getArg(0); @@ -1669,6 +1688,7 @@ Op.appendToVector(Ops); } } + assert((!Expr->isImplicit() || CanSplitValue) && "Expr can't be split"); assert(Expr && "Unknown DIExpression"); Ops.push_back(dwarf::DW_OP_LLVM_fragment); Ops.push_back(OffsetInBits); 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 @@ -3032,12 +3032,43 @@ 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); - EXPECT_INVALID_FRAGMENT(0, 32, dwarf::DW_OP_constu, 16, dwarf::DW_OP_shr); - EXPECT_INVALID_FRAGMENT(0, 32, dwarf::DW_OP_constu, 16, dwarf::DW_OP_shl); - EXPECT_INVALID_FRAGMENT(0, 32, dwarf::DW_OP_constu, 16, dwarf::DW_OP_shra); - EXPECT_INVALID_FRAGMENT(0, 32, dwarf::DW_OP_plus_uconst, 6); + EXPECT_INVALID_FRAGMENT(0, 32, dwarf::DW_OP_constu, 6, dwarf::DW_OP_plus, + dwarf::DW_OP_stack_value); + EXPECT_INVALID_FRAGMENT(0, 32, dwarf::DW_OP_constu, 14, dwarf::DW_OP_minus, + dwarf::DW_OP_stack_value); + EXPECT_INVALID_FRAGMENT(0, 32, dwarf::DW_OP_constu, 16, dwarf::DW_OP_shr, + dwarf::DW_OP_stack_value); + EXPECT_INVALID_FRAGMENT(0, 32, dwarf::DW_OP_constu, 16, dwarf::DW_OP_shl, + dwarf::DW_OP_stack_value); + EXPECT_INVALID_FRAGMENT(0, 32, dwarf::DW_OP_constu, 16, dwarf::DW_OP_shra, + dwarf::DW_OP_stack_value); + EXPECT_INVALID_FRAGMENT(0, 32, dwarf::DW_OP_plus_uconst, 6, + dwarf::DW_OP_stack_value); + + // Fragments can be created for expressions using DW_OP_plus to compute an + // address. + EXPECT_VALID_FRAGMENT(0, 32, dwarf::DW_OP_constu, 6, dwarf::DW_OP_plus); + EXPECT_VALID_FRAGMENT(0, 32, dwarf::DW_OP_plus_uconst, 6, dwarf::DW_OP_deref); + EXPECT_VALID_FRAGMENT(0, 32, dwarf::DW_OP_plus_uconst, 6, dwarf::DW_OP_deref, + dwarf::DW_OP_stack_value); + + // Check the other deref operations work in the same way. + EXPECT_VALID_FRAGMENT(0, 32, dwarf::DW_OP_plus_uconst, 6, + dwarf::DW_OP_deref_size, 1); + EXPECT_VALID_FRAGMENT(0, 32, dwarf::DW_OP_plus_uconst, 6, + dwarf::DW_OP_deref_type, 1, 1); + EXPECT_VALID_FRAGMENT(0, 32, dwarf::DW_OP_plus_uconst, 6, + dwarf::DW_OP_xderef); + EXPECT_VALID_FRAGMENT(0, 32, dwarf::DW_OP_plus_uconst, 6, + dwarf::DW_OP_xderef_size, 1); + EXPECT_VALID_FRAGMENT(0, 32, dwarf::DW_OP_plus_uconst, 6, + dwarf::DW_OP_xderef_type, 1, 1); + + // Fragments cannot be created for expressions using DW_OP_plus to compute an + // implicit value (check that this correctly fails even though there is a + // deref in the expression). + EXPECT_INVALID_FRAGMENT(0, 32, dwarf::DW_OP_deref, dwarf::DW_OP_plus_uconst, + 2, dwarf::DW_OP_stack_value); #undef EXPECT_VALID_FRAGMENT #undef EXPECT_INVALID_FRAGMENT