-
Notifications
You must be signed in to change notification settings - Fork 14.7k
[lldb] Zero extend APInt when piece size is bigger than the bitwidth #150149
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
0431e9a
to
3cec63f
Compare
@llvm/pr-subscribers-lldb Author: satyanarayana reddy janga (satyajanga) ChangesSummaryWe have internally seen cases like this We are extending the generated APInt to the piece size by filling zeros. Test planAdded a unit to cover this case. Full diff: https://github.com/llvm/llvm-project/pull/150149.diff 2 Files Affected:
diff --git a/lldb/source/Expression/DWARFExpression.cpp b/lldb/source/Expression/DWARFExpression.cpp
index 52891fcefd68b..c00795b97467b 100644
--- a/lldb/source/Expression/DWARFExpression.cpp
+++ b/lldb/source/Expression/DWARFExpression.cpp
@@ -1978,7 +1978,12 @@ llvm::Expected<Value> DWARFExpression::Evaluate(
// grows to the nearest host integer type.
llvm::APInt fail_value(1, 0, false);
llvm::APInt ap_int = scalar.UInt128(fail_value);
- assert(ap_int.getBitWidth() >= bit_size);
+ // We have seen a case where we have expression like:
+ // DW_OP_lit0, DW_OP_stack_value, DW_OP_piece 0x28
+ // here we are assuming the compiler was trying to zero
+ // extend the value that we should append to the buffer.
+ if (ap_int.getBitWidth() < bit_size)
+ ap_int = ap_int.zext(bit_size);
llvm::ArrayRef<uint64_t> buf{ap_int.getRawData(),
ap_int.getNumWords()};
curr_piece.GetScalar() = Scalar(llvm::APInt(bit_size, buf));
diff --git a/lldb/unittests/Expression/DWARFExpressionTest.cpp b/lldb/unittests/Expression/DWARFExpressionTest.cpp
index fdc9bfae1876c..86c3b56e320fd 100644
--- a/lldb/unittests/Expression/DWARFExpressionTest.cpp
+++ b/lldb/unittests/Expression/DWARFExpressionTest.cpp
@@ -358,6 +358,27 @@ TEST(DWARFExpression, DW_OP_piece) {
llvm::HasValue(GetScalar(16, 0xff00, true)));
}
+TEST(DWARFExpression, DW_OP_piece_host_address) {
+ static const uint8_t expr_data[] = {DW_OP_lit2, DW_OP_stack_value,
+ DW_OP_piece, 40};
+ llvm::ArrayRef<uint8_t> expr(expr_data, sizeof(expr_data));
+ DataExtractor extractor(expr.data(), expr.size(), lldb::eByteOrderLittle, 4);
+
+ // This tests if ap_int is extended to the right width.
+ // expect 40*8 = 320 bits size.
+ llvm::Expected<Value> result =
+ DWARFExpression::Evaluate(nullptr, nullptr, nullptr, extractor, nullptr,
+ lldb::eRegisterKindDWARF, nullptr, nullptr);
+ ASSERT_THAT_EXPECTED(result, llvm::Succeeded());
+ ASSERT_EQ(result->GetValueType(), Value::ValueType::HostAddress);
+ ASSERT_EQ(result->GetBuffer().GetByteSize(), 40ul);
+ const uint8_t *data = result->GetBuffer().GetBytes();
+ ASSERT_EQ(data[0], 2);
+ for (int i = 1; i < 40; i++) {
+ ASSERT_EQ(data[i], 0);
+ }
+}
+
TEST(DWARFExpression, DW_OP_implicit_value) {
unsigned char bytes = 4;
|
// DW_OP_lit0, DW_OP_stack_value, DW_OP_piece 0x28 | ||
// here we are assuming the compiler was trying to zero | ||
// extend the value that we should append to the buffer. | ||
if (ap_int.getBitWidth() < bit_size) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit, isn't this effectively just max(bit_Size, ap_int.getBitWidth())
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, APInt and APSInt classes can handle any sized integers.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This kind of expression seems to want to zero fill in the value.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am good with this. We should wait for anyone else to chime in for a day or two.
// DW_OP_lit0, DW_OP_stack_value, DW_OP_piece 0x28 | ||
// here we are assuming the compiler was trying to zero | ||
// extend the value that we should append to the buffer. | ||
if (ap_int.getBitWidth() < bit_size) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, APInt and APSInt classes can handle any sized integers.
// DW_OP_lit0, DW_OP_stack_value, DW_OP_piece 0x28 | ||
// here we are assuming the compiler was trying to zero | ||
// extend the value that we should append to the buffer. | ||
if (ap_int.getBitWidth() < bit_size) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This kind of expression seems to want to zero fill in the value.
I added Pavel Labath and David Blaikie in case they have any info on this. The story if we have some RiscV code that is creating ___location values for variables that use DW_OP_piece and they are using:
to try and zero fill in the value. Not sure if this is common or if there is a better way to do the zero fill. But clang is producing this. as a series of |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure that what clang is doing is completely compliant. According to the standard, DWARF expression values """can represent a value of any supported base type of the target machine. Instead of a base type, elements can have a generic type, which is an integral type that has the size of an address on the target machine and unspecified signedness""", so using a single value to represent 40 bytes is seems a bit dodgy. It's kind of obvious what you mean if the value is zero, but it gets a bit fuzzy for other values (where do you place that value, do you sign-extend it, etc.)
Nevertheless, looking at this from the consumer side, I think what you've done is a reasonable interpretation of this, and is better than crashing. I'm just wondering if there isn't a simpler way to express this. See the inline comment about a possible simplification.
llvm::APInt fail_value(1, 0, false); | ||
llvm::APInt ap_int = scalar.UInt128(fail_value); | ||
assert(ap_int.getBitWidth() >= bit_size); | ||
// We have seen a case where we have expression like: | ||
// DW_OP_lit0, DW_OP_stack_value, DW_OP_piece 0x28 | ||
// here we are assuming the compiler was trying to zero | ||
// extend the value that we should append to the buffer. | ||
if (ap_int.getBitWidth() < bit_size) | ||
ap_int = ap_int.zext(bit_size); | ||
llvm::ArrayRef<uint64_t> buf{ap_int.getRawData(), | ||
ap_int.getNumWords()}; | ||
curr_piece.GetScalar() = Scalar(llvm::APInt(bit_size, buf)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't all of this equivalent to curr_piece.GetScalar() = scalar.TruncOrExtendTo(bit_size, /*sign=*/false)
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't all of this equivalent to
curr_piece.GetScalar() = scalar.TruncOrExtendTo(bit_size, /*sign=*/false)
?
Yes. it seems so.
if the scalar has float value set then it wont work, but not sure if that case ever happens.
@labath do you know if the scalar contains a float value in the dwarf expression followed by DW_OP_Piece?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know, but I'm not particularly worried by that given that the only other call of this function is also inside DWARFExpression (in DW_OP_convert). If that becomes an issue, we can address both cases together.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know, but I'm not particularly worried by that given that the only other call of this function is also inside DWARFExpression (in DW_OP_convert). If that becomes an issue, we can address both cases together.
scalar.UInt128(fail_value)
seems to robust to handle all the edge cases. So I am leaving the way it is. let me know if you think otherwise.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think these two cases should be unified I don't see any reason why extension (or truncation) in DW_OP_piece should behave any differently from DW_OP_convert. And I don't like how this violates "campground rules" (leave the place in a better state than you found it in): instead of cleaning things up, it piles onto the existing dodgy implementation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree. updated based on the recommendation.
Summary: Test Plan: Reviewers: Subscribers: Tasks: Tags: Differential Revision: https://phabricator.intern.facebook.com/D78791142
3cec63f
to
f369157
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks.
Merging for @satyajanga |
Summary
We have internally seen cases like this
DW_OP_lit0, DW_OP_stack_value, DW_OP_piece 0x28
where we have longer op pieces than what Scalar supports (32, 64 or 128 bits). In these cases LLDB is currently hitting the assertion
assert(ap_int.getBitWidth() >= bit_size);
We are extending the generated APInt to the piece size by filling zeros.
Test plan
Added a unit to cover this case.
Reviewers
@clayborg , @jeffreytan81 , @Jlalond