Skip to content

Commit f838f08

Browse files
[lldb] Add register field enum class (#90063)
This represents the enum type that can be assigned to a field using the `<enum>` element in the target XML. https://sourceware.org/gdb/current/onlinedocs/gdb.html/Enum-Target-Types.html Each enumerator has: * A non-empty name * A value that is within the range of the field it's applied to The XML includes a "size" but we don't need that for anything and it's a pain to verify so I've left it out of our internal structures. When emitting XML we'll set size to the size of the register using the enum. An Enumerator class is added to RegisterFlags and hooked up to the existing ToXML so lldb-server can use it to emit enums as well. As enums are elements on the same level as flags, when emitting XML we'll do so via the registers. Before emitting a flags element we look at all the fields and see what enums they reference. Then print all of those if we haven't already done so. Functions are added to dump enum information for `register info` to use to show the enum information.
1 parent e843f02 commit f838f08

File tree

3 files changed

+435
-10
lines changed

3 files changed

+435
-10
lines changed

lldb/include/lldb/Target/RegisterFlags.h

Lines changed: 65 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,42 @@
1313
#include <string>
1414
#include <vector>
1515

16+
#include "llvm/ADT/StringSet.h"
17+
1618
namespace lldb_private {
1719

1820
class Stream;
1921
class Log;
2022

23+
class FieldEnum {
24+
public:
25+
struct Enumerator {
26+
uint64_t m_value;
27+
// Short name for the value. Shown in tables and when printing the field's
28+
// value. For example "RZ".
29+
std::string m_name;
30+
31+
Enumerator(uint64_t value, std::string name)
32+
: m_value(value), m_name(std::move(name)) {}
33+
34+
void ToXML(Stream &strm) const;
35+
};
36+
37+
typedef std::vector<Enumerator> Enumerators;
38+
39+
FieldEnum(std::string id, const Enumerators &enumerators);
40+
41+
const Enumerators &GetEnumerators() const { return m_enumerators; }
42+
43+
const std::string &GetID() const { return m_id; }
44+
45+
void ToXML(Stream &strm, unsigned size) const;
46+
47+
private:
48+
std::string m_id;
49+
Enumerators m_enumerators;
50+
};
51+
2152
class RegisterFlags {
2253
public:
2354
class Field {
@@ -26,17 +57,27 @@ class RegisterFlags {
2657
/// significant bit. The start bit must be <= the end bit.
2758
Field(std::string name, unsigned start, unsigned end);
2859

60+
/// Construct a field that also has some known enum values.
61+
Field(std::string name, unsigned start, unsigned end,
62+
const FieldEnum *enum_type);
63+
2964
/// Construct a field that occupies a single bit.
30-
Field(std::string name, unsigned bit_position)
31-
: m_name(std::move(name)), m_start(bit_position), m_end(bit_position) {}
65+
Field(std::string name, unsigned bit_position);
3266

3367
/// Get size of the field in bits. Will always be at least 1.
34-
unsigned GetSizeInBits() const { return m_end - m_start + 1; }
68+
unsigned GetSizeInBits() const;
69+
70+
/// Identical to GetSizeInBits, but for the GDB client to use.
71+
static unsigned GetSizeInBits(unsigned start, unsigned end);
3572

3673
/// A mask that covers all bits of the field.
37-
uint64_t GetMask() const {
38-
return (((uint64_t)1 << (GetSizeInBits())) - 1) << m_start;
39-
}
74+
uint64_t GetMask() const;
75+
76+
/// The maximum unsigned value that could be contained in this field.
77+
uint64_t GetMaxValue() const;
78+
79+
/// Identical to GetMaxValue but for the GDB client to use.
80+
static uint64_t GetMaxValue(unsigned start, unsigned end);
4081

4182
/// Extract value of the field from a whole register value.
4283
uint64_t GetValue(uint64_t register_value) const {
@@ -46,6 +87,7 @@ class RegisterFlags {
4687
const std::string &GetName() const { return m_name; }
4788
unsigned GetStart() const { return m_start; }
4889
unsigned GetEnd() const { return m_end; }
90+
const FieldEnum *GetEnum() const { return m_enum_type; }
4991
bool Overlaps(const Field &other) const;
5092
void log(Log *log) const;
5193

@@ -69,12 +111,15 @@ class RegisterFlags {
69111

70112
private:
71113
std::string m_name;
114+
72115
/// Start/end bit positions. Where start N, end N means a single bit
73116
/// field at position N. We expect that start <= end. Bit positions begin
74117
/// at 0.
75118
/// Start is the LSB, end is the MSB.
76119
unsigned m_start;
77120
unsigned m_end;
121+
122+
const FieldEnum *m_enum_type;
78123
};
79124

80125
/// This assumes that:
@@ -89,6 +134,10 @@ class RegisterFlags {
89134
/// when runtime field detection is needed.
90135
void SetFields(const std::vector<Field> &fields);
91136

137+
/// Make a string where each line contains the name of a field that has
138+
/// enum values, and lists what those values are.
139+
std::string DumpEnums(uint32_t max_width) const;
140+
92141
// Reverse the order of the fields, keeping their values the same.
93142
// For example a field from bit 31 to 30 with value 0b10 will become bits
94143
// 1 to 0, with the same 0b10 value.
@@ -118,9 +167,18 @@ class RegisterFlags {
118167
/// be split into many tables as needed.
119168
std::string AsTable(uint32_t max_width) const;
120169

121-
// Output XML that describes this set of flags.
170+
/// Output XML that describes this set of flags.
171+
/// EnumsToXML should have been called before this.
122172
void ToXML(Stream &strm) const;
123173

174+
/// Enum types must be defined before use, and
175+
/// GDBRemoteCommunicationServerLLGS view of the register types is based only
176+
/// on the registers. So this method emits any enum types that the upcoming
177+
/// set of fields may need. "seen" is a set of Enum IDs that we have already
178+
/// printed, that is updated with any printed by this call. This prevents us
179+
/// printing the same enum multiple times.
180+
void EnumsToXML(Stream &strm, llvm::StringSet<> &seen) const;
181+
124182
private:
125183
const std::string m_id;
126184
/// Size in bytes

lldb/source/Target/RegisterFlags.cpp

Lines changed: 196 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,41 @@
1212

1313
#include "llvm/ADT/StringExtras.h"
1414

15+
#include <limits>
1516
#include <numeric>
1617
#include <optional>
1718

1819
using namespace lldb_private;
1920

2021
RegisterFlags::Field::Field(std::string name, unsigned start, unsigned end)
21-
: m_name(std::move(name)), m_start(start), m_end(end) {
22+
: m_name(std::move(name)), m_start(start), m_end(end),
23+
m_enum_type(nullptr) {
2224
assert(m_start <= m_end && "Start bit must be <= end bit.");
2325
}
2426

27+
RegisterFlags::Field::Field(std::string name, unsigned bit_position)
28+
: m_name(std::move(name)), m_start(bit_position), m_end(bit_position),
29+
m_enum_type(nullptr) {}
30+
31+
RegisterFlags::Field::Field(std::string name, unsigned start, unsigned end,
32+
const FieldEnum *enum_type)
33+
: m_name(std::move(name)), m_start(start), m_end(end),
34+
m_enum_type(enum_type) {
35+
if (m_enum_type) {
36+
// Check that all values fit into this field. The XML parser will also
37+
// do this check so at runtime nothing should fail this check.
38+
// We can also make enums in C++ at compile time, which might fail this
39+
// check, so we catch them before it makes it into a release.
40+
uint64_t max_value = GetMaxValue();
41+
UNUSED_IF_ASSERT_DISABLED(max_value);
42+
for (const auto &enumerator : m_enum_type->GetEnumerators()) {
43+
UNUSED_IF_ASSERT_DISABLED(enumerator);
44+
assert(enumerator.m_value <= max_value &&
45+
"Enumerator value exceeds maximum value for this field");
46+
}
47+
}
48+
}
49+
2550
void RegisterFlags::Field::log(Log *log) const {
2651
LLDB_LOG(log, " Name: \"{0}\" Start: {1} End: {2}", m_name.c_str(), m_start,
2752
m_end);
@@ -53,6 +78,35 @@ unsigned RegisterFlags::Field::PaddingDistance(const Field &other) const {
5378
return lhs_start - rhs_end - 1;
5479
}
5580

81+
unsigned RegisterFlags::Field::GetSizeInBits(unsigned start, unsigned end) {
82+
return end - start + 1;
83+
}
84+
85+
unsigned RegisterFlags::Field::GetSizeInBits() const {
86+
return GetSizeInBits(m_start, m_end);
87+
}
88+
89+
uint64_t RegisterFlags::Field::GetMaxValue(unsigned start, unsigned end) {
90+
uint64_t max = std::numeric_limits<uint64_t>::max();
91+
unsigned bits = GetSizeInBits(start, end);
92+
// If the field is >= 64 bits the shift below would be undefined.
93+
// We assume the GDB client has discarded any field that would fail this
94+
// assert, it's only to check information we define directly in C++.
95+
assert(bits <= 64 && "Cannot handle field with size > 64 bits");
96+
if (bits < 64) {
97+
max = ((uint64_t)1 << bits) - 1;
98+
}
99+
return max;
100+
}
101+
102+
uint64_t RegisterFlags::Field::GetMaxValue() const {
103+
return GetMaxValue(m_start, m_end);
104+
}
105+
106+
uint64_t RegisterFlags::Field::GetMask() const {
107+
return GetMaxValue() << m_start;
108+
}
109+
56110
void RegisterFlags::SetFields(const std::vector<Field> &fields) {
57111
// We expect that the XML processor will discard anything describing flags but
58112
// with no fields.
@@ -190,6 +244,132 @@ std::string RegisterFlags::AsTable(uint32_t max_width) const {
190244
return table;
191245
}
192246

247+
// Print enums as:
248+
// value = name, value2 = name2
249+
// Subject to the limits of the terminal width.
250+
static void DumpEnumerators(StreamString &strm, size_t indent,
251+
size_t current_width, uint32_t max_width,
252+
const FieldEnum::Enumerators &enumerators) {
253+
for (auto it = enumerators.cbegin(); it != enumerators.cend(); ++it) {
254+
StreamString enumerator_strm;
255+
// The first enumerator of a line doesn't need to be separated.
256+
if (current_width != indent)
257+
enumerator_strm << ' ';
258+
259+
enumerator_strm.Printf("%" PRIu64 " = %s", it->m_value, it->m_name.c_str());
260+
261+
// Don't put "," after the last enumerator.
262+
if (std::next(it) != enumerators.cend())
263+
enumerator_strm << ",";
264+
265+
llvm::StringRef enumerator_string = enumerator_strm.GetString();
266+
// If printing the next enumerator would take us over the width, start
267+
// a new line. However, if we're printing the first enumerator of this
268+
// line, don't start a new one. Resulting in there being at least one per
269+
// line.
270+
//
271+
// This means for very small widths we get:
272+
// A: 0 = foo,
273+
// 1 = bar
274+
// Instead of:
275+
// A:
276+
// 0 = foo,
277+
// 1 = bar
278+
if ((current_width + enumerator_string.size() > max_width) &&
279+
current_width != indent) {
280+
current_width = indent;
281+
strm << '\n' << std::string(indent, ' ');
282+
// We're going to a new line so we don't need a space before the
283+
// name of the enumerator.
284+
enumerator_string = enumerator_string.drop_front();
285+
}
286+
287+
current_width += enumerator_string.size();
288+
strm << enumerator_string;
289+
}
290+
}
291+
292+
std::string RegisterFlags::DumpEnums(uint32_t max_width) const {
293+
StreamString strm;
294+
bool printed_enumerators_once = false;
295+
296+
for (const auto &field : m_fields) {
297+
const FieldEnum *enum_type = field.GetEnum();
298+
if (!enum_type)
299+
continue;
300+
301+
const FieldEnum::Enumerators &enumerators = enum_type->GetEnumerators();
302+
if (enumerators.empty())
303+
continue;
304+
305+
// Break between enumerators of different fields.
306+
if (printed_enumerators_once)
307+
strm << "\n\n";
308+
else
309+
printed_enumerators_once = true;
310+
311+
std::string name_string = field.GetName() + ": ";
312+
size_t indent = name_string.size();
313+
size_t current_width = indent;
314+
315+
strm << name_string;
316+
317+
DumpEnumerators(strm, indent, current_width, max_width, enumerators);
318+
}
319+
320+
return strm.GetString().str();
321+
}
322+
323+
void RegisterFlags::EnumsToXML(Stream &strm, llvm::StringSet<> &seen) const {
324+
for (const Field &field : m_fields)
325+
if (const FieldEnum *enum_type = field.GetEnum()) {
326+
const std::string &id = enum_type->GetID();
327+
if (!seen.contains(id)) {
328+
enum_type->ToXML(strm, GetSize());
329+
seen.insert(id);
330+
}
331+
}
332+
}
333+
334+
void FieldEnum::ToXML(Stream &strm, unsigned size) const {
335+
// Example XML:
336+
// <enum id="foo" size="4">
337+
// <evalue name="bar" value="1"/>
338+
// </enum>
339+
// Note that "size" is only emitted for GDB compatibility, LLDB does not need
340+
// it.
341+
342+
strm.Indent();
343+
strm << "<enum id=\"" << GetID() << "\" ";
344+
// This is the size of the underlying enum type if this were a C type.
345+
// In other words, the size of the register in bytes.
346+
strm.Printf("size=\"%d\"", size);
347+
348+
const Enumerators &enumerators = GetEnumerators();
349+
if (enumerators.empty()) {
350+
strm << "/>\n";
351+
return;
352+
}
353+
354+
strm << ">\n";
355+
strm.IndentMore();
356+
for (const auto &enumerator : enumerators) {
357+
strm.Indent();
358+
enumerator.ToXML(strm);
359+
strm.PutChar('\n');
360+
}
361+
strm.IndentLess();
362+
strm.Indent("</enum>\n");
363+
}
364+
365+
void FieldEnum::Enumerator::ToXML(Stream &strm) const {
366+
std::string escaped_name;
367+
llvm::raw_string_ostream escape_strm(escaped_name);
368+
llvm::printHTMLEscaped(m_name, escape_strm);
369+
strm.Printf("<evalue name=\"%s\" value=\"%" PRIu64 "\"/>",
370+
escaped_name.c_str(), m_value);
371+
}
372+
193373
void RegisterFlags::ToXML(Stream &strm) const {
194374
// Example XML:
195375
// <flags id="cpsr_flags" size="4">
@@ -214,7 +394,9 @@ void RegisterFlags::ToXML(Stream &strm) const {
214394
}
215395

216396
void RegisterFlags::Field::ToXML(Stream &strm) const {
217-
// Example XML:
397+
// Example XML with an enum:
398+
// <field name="correct" start="0" end="0" type="some_enum">
399+
// Without:
218400
// <field name="correct" start="0" end="0"/>
219401
strm.Indent();
220402
strm << "<field name=\"";
@@ -225,5 +407,17 @@ void RegisterFlags::Field::ToXML(Stream &strm) const {
225407
strm << escaped_name << "\" ";
226408

227409
strm.Printf("start=\"%d\" end=\"%d\"", GetStart(), GetEnd());
410+
411+
if (const FieldEnum *enum_type = GetEnum())
412+
strm << " type=\"" << enum_type->GetID() << "\"";
413+
228414
strm << "/>";
229415
}
416+
417+
FieldEnum::FieldEnum(std::string id, const Enumerators &enumerators)
418+
: m_id(id), m_enumerators(enumerators) {
419+
for (const auto &enumerator : m_enumerators) {
420+
UNUSED_IF_ASSERT_DISABLED(enumerator);
421+
assert(enumerator.m_name.size() && "Enumerator name cannot be empty");
422+
}
423+
}

0 commit comments

Comments
 (0)