Author: friss Date: Tue Oct 8 08:35:59 2019 New Revision: 374067 URL: http://llvm.org/viewvc/llvm-project?rev=374067&view=rev Log: Add pretty printing of Clang "bitfield" enums
Summary: Using enumerators as flags is standard practice. This patch adds support to LLDB to display such enum values symbolically, eg: (E) e1 = A | B If enumerators don't cover the whole value, the remaining bits are displayed as hexadecimal: (E) e4 = A | 0x10 Detecting whether an enum is used as a bitfield or not is complicated. This patch implements a heuristic that assumes that such enumerators will either have only 1 bit set or will be a combination of previous values. This patch doesn't change the way we currently display enums which the above heuristic would not consider as bitfields. Reviewers: jingham, labath Subscribers: lldb-commits Differential Revision: https://reviews.llvm.org/D67520 Modified: lldb/trunk/lit/SymbolFile/DWARF/debug-types-missing-signature.test lldb/trunk/packages/Python/lldbsuite/test/lang/c/enum_types/TestEnumTypes.py lldb/trunk/packages/Python/lldbsuite/test/lang/c/enum_types/main.c lldb/trunk/source/Symbol/ClangASTContext.cpp Modified: lldb/trunk/lit/SymbolFile/DWARF/debug-types-missing-signature.test URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/lit/SymbolFile/DWARF/debug-types-missing-signature.test?rev=374067&r1=374066&r2=374067&view=diff ============================================================================== --- lldb/trunk/lit/SymbolFile/DWARF/debug-types-missing-signature.test (original) +++ lldb/trunk/lit/SymbolFile/DWARF/debug-types-missing-signature.test Tue Oct 8 08:35:59 2019 @@ -22,5 +22,5 @@ PRINTEC: use of undeclared identifier 'E RUN: %lldb %t -b -o "target variable a e ec" | FileCheck --check-prefix=VARS %s VARS: (const (anonymous struct)) a = {} -VARS: (const (anonymous enum)) e = 1 -VARS: (const (anonymous enum)) ec = 1 +VARS: (const (anonymous enum)) e = 0x1 +VARS: (const (anonymous enum)) ec = 0x1 Modified: lldb/trunk/packages/Python/lldbsuite/test/lang/c/enum_types/TestEnumTypes.py URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/packages/Python/lldbsuite/test/lang/c/enum_types/TestEnumTypes.py?rev=374067&r1=374066&r2=374067&view=diff ============================================================================== --- lldb/trunk/packages/Python/lldbsuite/test/lang/c/enum_types/TestEnumTypes.py (original) +++ lldb/trunk/packages/Python/lldbsuite/test/lang/c/enum_types/TestEnumTypes.py Tue Oct 8 08:35:59 2019 @@ -25,11 +25,35 @@ class EnumTypesTestCase(TestBase): exe = self.getBuildArtifact("a.out") self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + lldbutil.run_to_source_breakpoint( + self, '// Breakpoint for bitfield', lldb.SBFileSpec("main.c")) + + self.expect("fr var a", DATA_TYPES_DISPLAYED_CORRECTLY, + patterns=[' = A$']) + self.expect("fr var b", DATA_TYPES_DISPLAYED_CORRECTLY, + patterns=[' = B$']) + self.expect("fr var c", DATA_TYPES_DISPLAYED_CORRECTLY, + patterns=[' = C$']) + self.expect("fr var ab", DATA_TYPES_DISPLAYED_CORRECTLY, + patterns=[' = AB$']) + self.expect("fr var ac", DATA_TYPES_DISPLAYED_CORRECTLY, + patterns=[' = A | C$']) + self.expect("fr var all", DATA_TYPES_DISPLAYED_CORRECTLY, + patterns=[' = ALL$']) + # Test that an enum that doesn't match the heuristic we use in + # ClangASTContext::DumpEnumValue, gets printed as a raw integer. + self.expect("fr var omega", DATA_TYPES_DISPLAYED_CORRECTLY, + patterns=[' = 7$']) + # Test the behavior in case have a variable of a type considered + # 'bitfield' by the heuristic, but the value isn't actually fully + # covered by the enumerators. + self.expect("p (enum bitfield)nonsense", DATA_TYPES_DISPLAYED_CORRECTLY, + patterns=[' = B | C | 0x10$']) + # Break inside the main. bkpt_id = lldbutil.run_break_set_by_file_and_line( self, "main.c", self.line, num_expected_locations=1, loc_exact=True) - - self.runCmd("run", RUN_SUCCEEDED) + self.runCmd("c", RUN_SUCCEEDED) # The stop reason of the thread should be breakpoint. self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, Modified: lldb/trunk/packages/Python/lldbsuite/test/lang/c/enum_types/main.c URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/packages/Python/lldbsuite/test/lang/c/enum_types/main.c?rev=374067&r1=374066&r2=374067&view=diff ============================================================================== --- lldb/trunk/packages/Python/lldbsuite/test/lang/c/enum_types/main.c (original) +++ lldb/trunk/packages/Python/lldbsuite/test/lang/c/enum_types/main.c Tue Oct 8 08:35:59 2019 @@ -18,6 +18,20 @@ struct foo { int main (int argc, char const *argv[]) { + enum bitfield { + None = 0, + A = 1 << 0, + B = 1 << 1, + C = 1 << 2, + AB = A | B, + ALL = A | B | C, + }; + + enum non_bitfield { + Alpha = 3, + Beta = 4 + }; + enum days { Monday = -3, Tuesday, @@ -28,9 +42,14 @@ int main (int argc, char const *argv[]) Sunday, kNumDays }; + + enum bitfield a = A, b = B, c = C, ab = AB, ac = A | C, all = ALL; + int nonsense = a + b + c + ab + ac + all; + enum non_bitfield omega = Alpha | Beta; + enum days day; struct foo f; - f.op = NULL; + f.op = NULL; // Breakpoint for bitfield for (day = Monday - 1; day <= kNumDays + 1; day++) { printf("day as int is %i\n", (int)day); // Set break point at this line. Modified: lldb/trunk/source/Symbol/ClangASTContext.cpp URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Symbol/ClangASTContext.cpp?rev=374067&r1=374066&r2=374067&view=diff ============================================================================== --- lldb/trunk/source/Symbol/ClangASTContext.cpp (original) +++ lldb/trunk/source/Symbol/ClangASTContext.cpp Tue Oct 8 08:35:59 2019 @@ -9359,21 +9359,71 @@ static bool DumpEnumValue(const clang::Q llvm::cast<clang::EnumType>(qual_type.getTypePtr()); const clang::EnumDecl *enum_decl = enutype->getDecl(); assert(enum_decl); - clang::EnumDecl::enumerator_iterator enum_pos, enum_end_pos; - const bool is_signed = qual_type->isSignedIntegerOrEnumerationType(); lldb::offset_t offset = byte_offset; - const int64_t enum_value = data.GetMaxS64Bitfield( + const uint64_t enum_svalue = data.GetMaxS64Bitfield( &offset, byte_size, bitfield_bit_size, bitfield_bit_offset); + bool can_be_bitfield = true; + uint64_t covered_bits = 0; + int num_enumerators = 0; + // Try to find an exact match for the value. + // At the same time, we're applying a heuristic to determine whether we want + // to print this enum as a bitfield. We're likely dealing with a bitfield if + // every enumrator is either a one bit value or a superset of the previous + // enumerators. Also 0 doesn't make sense when the enumerators are used as + // flags. for (auto enumerator : enum_decl->enumerators()) { - if (enumerator->getInitVal().getSExtValue() == enum_value) { + uint64_t val = enumerator->getInitVal().getSExtValue(); + if (llvm::countPopulation(val) != 1 && (val & ~covered_bits) != 0) + can_be_bitfield = false; + covered_bits |= val; + ++num_enumerators; + if (val == enum_svalue) { + // Found an exact match, that's all we need to do. s->PutCString(enumerator->getNameAsString()); return true; } } - // If we have gotten here we didn't get find the enumerator in the - // enum decl, so just print the integer. - s->Printf("%" PRIi64, enum_value); + + // No exact match, but we don't think this is a bitfield. Print the value as + // decimal. + if (!can_be_bitfield) { + s->Printf("%" PRIi64, enum_svalue); + return true; + } + + // Unsigned values make more sense for flags. + offset = byte_offset; + const uint64_t enum_uvalue = data.GetMaxU64Bitfield( + &offset, byte_size, bitfield_bit_size, bitfield_bit_offset); + + uint64_t remaining_value = enum_uvalue; + std::vector<std::pair<uint64_t, llvm::StringRef>> values; + values.reserve(num_enumerators); + for (auto enumerator : enum_decl->enumerators()) + if (auto val = enumerator->getInitVal().getZExtValue()) + values.emplace_back(val, enumerator->getName()); + + // Sort in reverse order of the number of the population count, so that in + // `enum {A, B, ALL = A|B }` we visit ALL first. Use a stable sort so that + // A | C where A is declared before C is displayed in this order. + std::stable_sort(values.begin(), values.end(), [](const auto &a, const auto &b) { + return llvm::countPopulation(a.first) > llvm::countPopulation(b.first); + }); + + for (const auto &val : values) { + if ((remaining_value & val.first) != val.first) + continue; + remaining_value &= ~val.first; + s->PutCString(val.second); + if (remaining_value) + s->PutCString(" | "); + } + + // If there is a remainder that is not covered by the value, print it as hex. + if (remaining_value) + s->Printf("0x%" PRIx64, remaining_value); + return true; } @@ -9390,6 +9440,13 @@ bool ClangASTContext::DumpTypeValue( clang::QualType qual_type(GetQualType(type)); const clang::Type::TypeClass type_class = qual_type->getTypeClass(); + + if (type_class == clang::Type::Elaborated) { + qual_type = llvm::cast<clang::ElaboratedType>(qual_type)->getNamedType(); + return DumpTypeValue(qual_type.getAsOpaquePtr(), s, format, data, byte_offset, byte_size, + bitfield_bit_size, bitfield_bit_offset, exe_scope); + } + switch (type_class) { case clang::Type::Typedef: { clang::QualType typedef_qual_type = _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits