Skip to content

Commit dde3f17

Browse files
[lldb] Fix printing of unsigned enum bitfields when they contain the max value (#96202)
While testing register fields I found that if you put the max value into a bitfield with an underlying type that is an unsigned enum, lldb would not print the enum name. This is because the code to match values to names wasn't checking whether the enum's type was signed, it just assumed it was. So for example a 2 bit field with value 3 got signed extended to -1, which didn't match the enumerator value of 3. So lldb just printed the number instead of the name. For a value of 1, the top bit was 0 so the sign extend became a zero extend, and lldb did print the name of the enumerator. I added a new test because I needed to use C++ to get typed enums. It checks min, max and an in between value for signed and unsigned enums applied to a bitfield.
1 parent c1004ca commit dde3f17

File tree

4 files changed

+71
-5
lines changed

4 files changed

+71
-5
lines changed

lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8639,8 +8639,13 @@ static bool DumpEnumValue(const clang::QualType &qual_type, Stream &s,
86398639
const clang::EnumDecl *enum_decl = enutype->getDecl();
86408640
assert(enum_decl);
86418641
lldb::offset_t offset = byte_offset;
8642-
const uint64_t enum_svalue = data.GetMaxS64Bitfield(
8643-
&offset, byte_size, bitfield_bit_size, bitfield_bit_offset);
8642+
bool qual_type_is_signed = qual_type->isSignedIntegerOrEnumerationType();
8643+
const uint64_t enum_svalue =
8644+
qual_type_is_signed
8645+
? data.GetMaxS64Bitfield(&offset, byte_size, bitfield_bit_size,
8646+
bitfield_bit_offset)
8647+
: data.GetMaxU64Bitfield(&offset, byte_size, bitfield_bit_size,
8648+
bitfield_bit_offset);
86448649
bool can_be_bitfield = true;
86458650
uint64_t covered_bits = 0;
86468651
int num_enumerators = 0;
@@ -8652,8 +8657,11 @@ static bool DumpEnumValue(const clang::QualType &qual_type, Stream &s,
86528657
// enumerators. Also 0 doesn't make sense when the enumerators are used as
86538658
// flags.
86548659
for (auto *enumerator : enum_decl->enumerators()) {
8655-
uint64_t val = enumerator->getInitVal().getSExtValue();
8656-
val = llvm::SignExtend64(val, 8*byte_size);
8660+
llvm::APSInt init_val = enumerator->getInitVal();
8661+
uint64_t val =
8662+
qual_type_is_signed ? init_val.getSExtValue() : init_val.getZExtValue();
8663+
if (qual_type_is_signed)
8664+
val = llvm::SignExtend64(val, 8 * byte_size);
86578665
if (llvm::popcount(val) != 1 && (val & ~covered_bits) != 0)
86588666
can_be_bitfield = false;
86598667
covered_bits |= val;
@@ -8673,7 +8681,7 @@ static bool DumpEnumValue(const clang::QualType &qual_type, Stream &s,
86738681
// No exact match, but we don't think this is a bitfield. Print the value as
86748682
// decimal.
86758683
if (!can_be_bitfield) {
8676-
if (qual_type->isSignedIntegerOrEnumerationType())
8684+
if (qual_type_is_signed)
86778685
s.Printf("%" PRIi64, enum_svalue);
86788686
else
86798687
s.Printf("%" PRIu64, enum_uvalue);
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
CXX_SOURCES := main.cpp
2+
3+
include Makefile.rules
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
"""
2+
Test that the expression parser accounts for the underlying type of bitfield
3+
enums when looking for matching values.
4+
"""
5+
6+
import lldb
7+
from lldbsuite.test.decorators import *
8+
from lldbsuite.test.lldbtest import *
9+
from lldbsuite.test import lldbutil
10+
11+
12+
class TestBitfieldEnum(TestBase):
13+
def test_bitfield_enums(self):
14+
self.build()
15+
16+
lldbutil.run_to_source_breakpoint(
17+
self, "// break here", lldb.SBFileSpec("main.cpp", False)
18+
)
19+
20+
self.expect_expr(
21+
"bfs",
22+
result_type="BitfieldStruct",
23+
result_children=[
24+
ValueCheck(name="signed_min", value="min"),
25+
ValueCheck(name="signed_other", value="-1"),
26+
ValueCheck(name="signed_max", value="max"),
27+
ValueCheck(name="unsigned_min", value="min"),
28+
ValueCheck(name="unsigned_other", value="1"),
29+
ValueCheck(name="unsigned_max", value="max"),
30+
],
31+
)
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
enum class SignedEnum : int { min = -2, max = 1 };
2+
enum class UnsignedEnum : unsigned { min = 0, max = 3 };
3+
4+
struct BitfieldStruct {
5+
SignedEnum signed_min : 2;
6+
SignedEnum signed_other : 2;
7+
SignedEnum signed_max : 2;
8+
UnsignedEnum unsigned_min : 2;
9+
UnsignedEnum unsigned_other : 2;
10+
UnsignedEnum unsigned_max : 2;
11+
};
12+
13+
int main() {
14+
BitfieldStruct bfs;
15+
bfs.signed_min = SignedEnum::min;
16+
bfs.signed_other = static_cast<SignedEnum>(-1);
17+
bfs.signed_max = SignedEnum::max;
18+
19+
bfs.unsigned_min = UnsignedEnum::min;
20+
bfs.unsigned_other = static_cast<UnsignedEnum>(1);
21+
bfs.unsigned_max = UnsignedEnum::max;
22+
23+
return 0; // break here
24+
}

0 commit comments

Comments
 (0)