xref: /src/contrib/llvm-project/lldb/source/Target/RegisterFlags.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
17fa27ce4SDimitry Andric //===-- RegisterFlags.cpp -------------------------------------------------===//
27fa27ce4SDimitry Andric //
37fa27ce4SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
47fa27ce4SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
57fa27ce4SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
67fa27ce4SDimitry Andric //
77fa27ce4SDimitry Andric //===----------------------------------------------------------------------===//
87fa27ce4SDimitry Andric 
97fa27ce4SDimitry Andric #include "lldb/Target/RegisterFlags.h"
10b1c73532SDimitry Andric #include "lldb/Utility/Log.h"
117fa27ce4SDimitry Andric #include "lldb/Utility/StreamString.h"
127fa27ce4SDimitry Andric 
13b1c73532SDimitry Andric #include "llvm/ADT/StringExtras.h"
14b1c73532SDimitry Andric 
15ac9a064cSDimitry Andric #include <limits>
167fa27ce4SDimitry Andric #include <numeric>
177fa27ce4SDimitry Andric #include <optional>
187fa27ce4SDimitry Andric 
197fa27ce4SDimitry Andric using namespace lldb_private;
207fa27ce4SDimitry Andric 
Field(std::string name,unsigned start,unsigned end)21b1c73532SDimitry Andric RegisterFlags::Field::Field(std::string name, unsigned start, unsigned end)
22ac9a064cSDimitry Andric     : m_name(std::move(name)), m_start(start), m_end(end),
23ac9a064cSDimitry Andric       m_enum_type(nullptr) {
24b1c73532SDimitry Andric   assert(m_start <= m_end && "Start bit must be <= end bit.");
25b1c73532SDimitry Andric }
26b1c73532SDimitry Andric 
Field(std::string name,unsigned bit_position)27ac9a064cSDimitry Andric RegisterFlags::Field::Field(std::string name, unsigned bit_position)
28ac9a064cSDimitry Andric     : m_name(std::move(name)), m_start(bit_position), m_end(bit_position),
29ac9a064cSDimitry Andric       m_enum_type(nullptr) {}
30ac9a064cSDimitry Andric 
Field(std::string name,unsigned start,unsigned end,const FieldEnum * enum_type)31ac9a064cSDimitry Andric RegisterFlags::Field::Field(std::string name, unsigned start, unsigned end,
32ac9a064cSDimitry Andric                             const FieldEnum *enum_type)
33ac9a064cSDimitry Andric     : m_name(std::move(name)), m_start(start), m_end(end),
34ac9a064cSDimitry Andric       m_enum_type(enum_type) {
35ac9a064cSDimitry Andric   if (m_enum_type) {
36ac9a064cSDimitry Andric     // Check that all values fit into this field. The XML parser will also
37ac9a064cSDimitry Andric     // do this check so at runtime nothing should fail this check.
38ac9a064cSDimitry Andric     // We can also make enums in C++ at compile time, which might fail this
39ac9a064cSDimitry Andric     // check, so we catch them before it makes it into a release.
40ac9a064cSDimitry Andric     uint64_t max_value = GetMaxValue();
41ac9a064cSDimitry Andric     UNUSED_IF_ASSERT_DISABLED(max_value);
42ac9a064cSDimitry Andric     for (const auto &enumerator : m_enum_type->GetEnumerators()) {
43ac9a064cSDimitry Andric       UNUSED_IF_ASSERT_DISABLED(enumerator);
44ac9a064cSDimitry Andric       assert(enumerator.m_value <= max_value &&
45ac9a064cSDimitry Andric              "Enumerator value exceeds maximum value for this field");
46ac9a064cSDimitry Andric     }
47ac9a064cSDimitry Andric   }
48ac9a064cSDimitry Andric }
49ac9a064cSDimitry Andric 
DumpToLog(Log * log) const50ac9a064cSDimitry Andric void RegisterFlags::Field::DumpToLog(Log *log) const {
517fa27ce4SDimitry Andric   LLDB_LOG(log, "  Name: \"{0}\" Start: {1} End: {2}", m_name.c_str(), m_start,
527fa27ce4SDimitry Andric            m_end);
537fa27ce4SDimitry Andric }
547fa27ce4SDimitry Andric 
Overlaps(const Field & other) const557fa27ce4SDimitry Andric bool RegisterFlags::Field::Overlaps(const Field &other) const {
567fa27ce4SDimitry Andric   unsigned overlap_start = std::max(GetStart(), other.GetStart());
577fa27ce4SDimitry Andric   unsigned overlap_end = std::min(GetEnd(), other.GetEnd());
587fa27ce4SDimitry Andric   return overlap_start <= overlap_end;
597fa27ce4SDimitry Andric }
607fa27ce4SDimitry Andric 
PaddingDistance(const Field & other) const617fa27ce4SDimitry Andric unsigned RegisterFlags::Field::PaddingDistance(const Field &other) const {
627fa27ce4SDimitry Andric   assert(!Overlaps(other) &&
637fa27ce4SDimitry Andric          "Cannot get padding distance for overlapping fields.");
647fa27ce4SDimitry Andric   assert((other < (*this)) && "Expected fields in MSB to LSB order.");
657fa27ce4SDimitry Andric 
667fa27ce4SDimitry Andric   // If they don't overlap they are either next to each other or separated
677fa27ce4SDimitry Andric   // by some number of bits.
687fa27ce4SDimitry Andric 
697fa27ce4SDimitry Andric   // Where left will be the MSB and right will be the LSB.
707fa27ce4SDimitry Andric   unsigned lhs_start = GetStart();
717fa27ce4SDimitry Andric   unsigned rhs_end = other.GetStart() + other.GetSizeInBits() - 1;
727fa27ce4SDimitry Andric 
737fa27ce4SDimitry Andric   if (*this < other) {
747fa27ce4SDimitry Andric     lhs_start = other.GetStart();
757fa27ce4SDimitry Andric     rhs_end = GetStart() + GetSizeInBits() - 1;
767fa27ce4SDimitry Andric   }
777fa27ce4SDimitry Andric 
787fa27ce4SDimitry Andric   return lhs_start - rhs_end - 1;
797fa27ce4SDimitry Andric }
807fa27ce4SDimitry Andric 
GetSizeInBits(unsigned start,unsigned end)81ac9a064cSDimitry Andric unsigned RegisterFlags::Field::GetSizeInBits(unsigned start, unsigned end) {
82ac9a064cSDimitry Andric   return end - start + 1;
83ac9a064cSDimitry Andric }
847fa27ce4SDimitry Andric 
GetSizeInBits() const85ac9a064cSDimitry Andric unsigned RegisterFlags::Field::GetSizeInBits() const {
86ac9a064cSDimitry Andric   return GetSizeInBits(m_start, m_end);
87ac9a064cSDimitry Andric }
88ac9a064cSDimitry Andric 
GetMaxValue(unsigned start,unsigned end)89ac9a064cSDimitry Andric uint64_t RegisterFlags::Field::GetMaxValue(unsigned start, unsigned end) {
90ac9a064cSDimitry Andric   uint64_t max = std::numeric_limits<uint64_t>::max();
91ac9a064cSDimitry Andric   unsigned bits = GetSizeInBits(start, end);
92ac9a064cSDimitry Andric   // If the field is >= 64 bits the shift below would be undefined.
93ac9a064cSDimitry Andric   // We assume the GDB client has discarded any field that would fail this
94ac9a064cSDimitry Andric   // assert, it's only to check information we define directly in C++.
95ac9a064cSDimitry Andric   assert(bits <= 64 && "Cannot handle field with size > 64 bits");
96ac9a064cSDimitry Andric   if (bits < 64) {
97ac9a064cSDimitry Andric     max = ((uint64_t)1 << bits) - 1;
98ac9a064cSDimitry Andric   }
99ac9a064cSDimitry Andric   return max;
100ac9a064cSDimitry Andric }
101ac9a064cSDimitry Andric 
GetMaxValue() const102ac9a064cSDimitry Andric uint64_t RegisterFlags::Field::GetMaxValue() const {
103ac9a064cSDimitry Andric   return GetMaxValue(m_start, m_end);
104ac9a064cSDimitry Andric }
105ac9a064cSDimitry Andric 
GetMask() const106ac9a064cSDimitry Andric uint64_t RegisterFlags::Field::GetMask() const {
107ac9a064cSDimitry Andric   return GetMaxValue() << m_start;
108ac9a064cSDimitry Andric }
109ac9a064cSDimitry Andric 
SetFields(const std::vector<Field> & fields)110ac9a064cSDimitry Andric void RegisterFlags::SetFields(const std::vector<Field> &fields) {
1117fa27ce4SDimitry Andric   // We expect that these are unsorted but do not overlap.
1127fa27ce4SDimitry Andric   // They could fill the register but may have gaps.
1137fa27ce4SDimitry Andric   std::vector<Field> provided_fields = fields;
114b1c73532SDimitry Andric 
115b1c73532SDimitry Andric   m_fields.clear();
1167fa27ce4SDimitry Andric   m_fields.reserve(provided_fields.size());
1177fa27ce4SDimitry Andric 
1187fa27ce4SDimitry Andric   // ProcessGDBRemote should have sorted these in descending order already.
1197fa27ce4SDimitry Andric   assert(std::is_sorted(provided_fields.rbegin(), provided_fields.rend()));
1207fa27ce4SDimitry Andric 
1217fa27ce4SDimitry Andric   // Build a new list of fields that includes anonymous (empty name) fields
1227fa27ce4SDimitry Andric   // wherever there is a gap. This will simplify processing later.
1237fa27ce4SDimitry Andric   std::optional<Field> previous_field;
124b1c73532SDimitry Andric   unsigned register_msb = (m_size * 8) - 1;
1257fa27ce4SDimitry Andric   for (auto field : provided_fields) {
1267fa27ce4SDimitry Andric     if (previous_field) {
1277fa27ce4SDimitry Andric       unsigned padding = previous_field->PaddingDistance(field);
1287fa27ce4SDimitry Andric       if (padding) {
1297fa27ce4SDimitry Andric         // -1 to end just before the previous field.
1307fa27ce4SDimitry Andric         unsigned end = previous_field->GetStart() - 1;
1317fa27ce4SDimitry Andric         // +1 because if you want to pad 1 bit you want to start and end
1327fa27ce4SDimitry Andric         // on the same bit.
1337fa27ce4SDimitry Andric         m_fields.push_back(Field("", field.GetEnd() + 1, end));
1347fa27ce4SDimitry Andric       }
1357fa27ce4SDimitry Andric     } else {
1367fa27ce4SDimitry Andric       // This is the first field. Check that it starts at the register's MSB.
1377fa27ce4SDimitry Andric       if (field.GetEnd() != register_msb)
1387fa27ce4SDimitry Andric         m_fields.push_back(Field("", field.GetEnd() + 1, register_msb));
1397fa27ce4SDimitry Andric     }
1407fa27ce4SDimitry Andric     m_fields.push_back(field);
1417fa27ce4SDimitry Andric     previous_field = field;
1427fa27ce4SDimitry Andric   }
1437fa27ce4SDimitry Andric 
1447fa27ce4SDimitry Andric   // The last field may not extend all the way to bit 0.
1457fa27ce4SDimitry Andric   if (previous_field && previous_field->GetStart() != 0)
1467fa27ce4SDimitry Andric     m_fields.push_back(Field("", 0, previous_field->GetStart() - 1));
1477fa27ce4SDimitry Andric }
1487fa27ce4SDimitry Andric 
RegisterFlags(std::string id,unsigned size,const std::vector<Field> & fields)149b1c73532SDimitry Andric RegisterFlags::RegisterFlags(std::string id, unsigned size,
150b1c73532SDimitry Andric                              const std::vector<Field> &fields)
151b1c73532SDimitry Andric     : m_id(std::move(id)), m_size(size) {
152b1c73532SDimitry Andric   SetFields(fields);
153b1c73532SDimitry Andric }
154b1c73532SDimitry Andric 
DumpToLog(Log * log) const155ac9a064cSDimitry Andric void RegisterFlags::DumpToLog(Log *log) const {
1567fa27ce4SDimitry Andric   LLDB_LOG(log, "ID: \"{0}\" Size: {1}", m_id.c_str(), m_size);
1577fa27ce4SDimitry Andric   for (const Field &field : m_fields)
158ac9a064cSDimitry Andric     field.DumpToLog(log);
1597fa27ce4SDimitry Andric }
1607fa27ce4SDimitry Andric 
FormatCell(const StreamString & content,unsigned column_width)1617fa27ce4SDimitry Andric static StreamString FormatCell(const StreamString &content,
1627fa27ce4SDimitry Andric                                unsigned column_width) {
1637fa27ce4SDimitry Andric   unsigned pad = column_width - content.GetString().size();
1647fa27ce4SDimitry Andric   std::string pad_l;
1657fa27ce4SDimitry Andric   std::string pad_r;
1667fa27ce4SDimitry Andric   if (pad) {
1677fa27ce4SDimitry Andric     pad_l = std::string(pad / 2, ' ');
1687fa27ce4SDimitry Andric     pad_r = std::string((pad / 2) + (pad % 2), ' ');
1697fa27ce4SDimitry Andric   }
1707fa27ce4SDimitry Andric 
1717fa27ce4SDimitry Andric   StreamString aligned;
1727fa27ce4SDimitry Andric   aligned.Printf("|%s%s%s", pad_l.c_str(), content.GetString().data(),
1737fa27ce4SDimitry Andric                  pad_r.c_str());
1747fa27ce4SDimitry Andric   return aligned;
1757fa27ce4SDimitry Andric }
1767fa27ce4SDimitry Andric 
EmitTable(std::string & out,std::array<std::string,3> & table)1777fa27ce4SDimitry Andric static void EmitTable(std::string &out, std::array<std::string, 3> &table) {
1787fa27ce4SDimitry Andric   // Close the table.
1797fa27ce4SDimitry Andric   for (std::string &line : table)
1807fa27ce4SDimitry Andric     line += '|';
1817fa27ce4SDimitry Andric 
1827fa27ce4SDimitry Andric   out += std::accumulate(table.begin() + 1, table.end(), table.front(),
1837fa27ce4SDimitry Andric                          [](std::string lhs, const auto &rhs) {
1847fa27ce4SDimitry Andric                            return std::move(lhs) + "\n" + rhs;
1857fa27ce4SDimitry Andric                          });
1867fa27ce4SDimitry Andric }
1877fa27ce4SDimitry Andric 
AsTable(uint32_t max_width) const1887fa27ce4SDimitry Andric std::string RegisterFlags::AsTable(uint32_t max_width) const {
1897fa27ce4SDimitry Andric   std::string table;
1907fa27ce4SDimitry Andric   // position / gridline / name
1917fa27ce4SDimitry Andric   std::array<std::string, 3> lines;
1927fa27ce4SDimitry Andric   uint32_t current_width = 0;
1937fa27ce4SDimitry Andric 
1947fa27ce4SDimitry Andric   for (const RegisterFlags::Field &field : m_fields) {
1957fa27ce4SDimitry Andric     StreamString position;
1967fa27ce4SDimitry Andric     if (field.GetEnd() == field.GetStart())
1977fa27ce4SDimitry Andric       position.Printf(" %d ", field.GetEnd());
1987fa27ce4SDimitry Andric     else
1997fa27ce4SDimitry Andric       position.Printf(" %d-%d ", field.GetEnd(), field.GetStart());
2007fa27ce4SDimitry Andric 
2017fa27ce4SDimitry Andric     StreamString name;
2027fa27ce4SDimitry Andric     name.Printf(" %s ", field.GetName().c_str());
2037fa27ce4SDimitry Andric 
2047fa27ce4SDimitry Andric     unsigned column_width = position.GetString().size();
2057fa27ce4SDimitry Andric     unsigned name_width = name.GetString().size();
2067fa27ce4SDimitry Andric     if (name_width > column_width)
2077fa27ce4SDimitry Andric       column_width = name_width;
2087fa27ce4SDimitry Andric 
2097fa27ce4SDimitry Andric     // If the next column would overflow and we have already formatted at least
2107fa27ce4SDimitry Andric     // one column, put out what we have and move to a new table on the next line
2117fa27ce4SDimitry Andric     // (+1 here because we need to cap the ends with '|'). If this is the first
2127fa27ce4SDimitry Andric     // column, just let it overflow and we'll wrap next time around. There's not
2137fa27ce4SDimitry Andric     // much we can do with a very small terminal.
2147fa27ce4SDimitry Andric     if (current_width && ((current_width + column_width + 1) >= max_width)) {
2157fa27ce4SDimitry Andric       EmitTable(table, lines);
2167fa27ce4SDimitry Andric       // Blank line between each.
2177fa27ce4SDimitry Andric       table += "\n\n";
2187fa27ce4SDimitry Andric 
2197fa27ce4SDimitry Andric       for (std::string &line : lines)
2207fa27ce4SDimitry Andric         line.clear();
2217fa27ce4SDimitry Andric       current_width = 0;
2227fa27ce4SDimitry Andric     }
2237fa27ce4SDimitry Andric 
2247fa27ce4SDimitry Andric     StreamString aligned_position = FormatCell(position, column_width);
2257fa27ce4SDimitry Andric     lines[0] += aligned_position.GetString();
2267fa27ce4SDimitry Andric     StreamString grid;
2277fa27ce4SDimitry Andric     grid << '|' << std::string(column_width, '-');
2287fa27ce4SDimitry Andric     lines[1] += grid.GetString();
2297fa27ce4SDimitry Andric     StreamString aligned_name = FormatCell(name, column_width);
2307fa27ce4SDimitry Andric     lines[2] += aligned_name.GetString();
2317fa27ce4SDimitry Andric 
2327fa27ce4SDimitry Andric     // +1 for the left side '|'.
2337fa27ce4SDimitry Andric     current_width += column_width + 1;
2347fa27ce4SDimitry Andric   }
2357fa27ce4SDimitry Andric 
2367fa27ce4SDimitry Andric   // If we didn't overflow and still have table to print out.
2377fa27ce4SDimitry Andric   if (lines[0].size())
2387fa27ce4SDimitry Andric     EmitTable(table, lines);
2397fa27ce4SDimitry Andric 
2407fa27ce4SDimitry Andric   return table;
2417fa27ce4SDimitry Andric }
242b1c73532SDimitry Andric 
243ac9a064cSDimitry Andric // Print enums as:
244ac9a064cSDimitry Andric // value = name, value2 = name2
245ac9a064cSDimitry Andric // Subject to the limits of the terminal width.
DumpEnumerators(StreamString & strm,size_t indent,size_t current_width,uint32_t max_width,const FieldEnum::Enumerators & enumerators)246ac9a064cSDimitry Andric static void DumpEnumerators(StreamString &strm, size_t indent,
247ac9a064cSDimitry Andric                             size_t current_width, uint32_t max_width,
248ac9a064cSDimitry Andric                             const FieldEnum::Enumerators &enumerators) {
249ac9a064cSDimitry Andric   for (auto it = enumerators.cbegin(); it != enumerators.cend(); ++it) {
250ac9a064cSDimitry Andric     StreamString enumerator_strm;
251ac9a064cSDimitry Andric     // The first enumerator of a line doesn't need to be separated.
252ac9a064cSDimitry Andric     if (current_width != indent)
253ac9a064cSDimitry Andric       enumerator_strm << ' ';
254ac9a064cSDimitry Andric 
255ac9a064cSDimitry Andric     enumerator_strm.Printf("%" PRIu64 " = %s", it->m_value, it->m_name.c_str());
256ac9a064cSDimitry Andric 
257ac9a064cSDimitry Andric     // Don't put "," after the last enumerator.
258ac9a064cSDimitry Andric     if (std::next(it) != enumerators.cend())
259ac9a064cSDimitry Andric       enumerator_strm << ",";
260ac9a064cSDimitry Andric 
261ac9a064cSDimitry Andric     llvm::StringRef enumerator_string = enumerator_strm.GetString();
262ac9a064cSDimitry Andric     // If printing the next enumerator would take us over the width, start
263ac9a064cSDimitry Andric     // a new line. However, if we're printing the first enumerator of this
264ac9a064cSDimitry Andric     // line, don't start a new one. Resulting in there being at least one per
265ac9a064cSDimitry Andric     // line.
266ac9a064cSDimitry Andric     //
267ac9a064cSDimitry Andric     // This means for very small widths we get:
268ac9a064cSDimitry Andric     // A: 0 = foo,
269ac9a064cSDimitry Andric     //    1 = bar
270ac9a064cSDimitry Andric     // Instead of:
271ac9a064cSDimitry Andric     // A:
272ac9a064cSDimitry Andric     //    0 = foo,
273ac9a064cSDimitry Andric     //    1 = bar
274ac9a064cSDimitry Andric     if ((current_width + enumerator_string.size() > max_width) &&
275ac9a064cSDimitry Andric         current_width != indent) {
276ac9a064cSDimitry Andric       current_width = indent;
277ac9a064cSDimitry Andric       strm << '\n' << std::string(indent, ' ');
278ac9a064cSDimitry Andric       // We're going to a new line so we don't need a space before the
279ac9a064cSDimitry Andric       // name of the enumerator.
280ac9a064cSDimitry Andric       enumerator_string = enumerator_string.drop_front();
281ac9a064cSDimitry Andric     }
282ac9a064cSDimitry Andric 
283ac9a064cSDimitry Andric     current_width += enumerator_string.size();
284ac9a064cSDimitry Andric     strm << enumerator_string;
285ac9a064cSDimitry Andric   }
286ac9a064cSDimitry Andric }
287ac9a064cSDimitry Andric 
DumpEnums(uint32_t max_width) const288ac9a064cSDimitry Andric std::string RegisterFlags::DumpEnums(uint32_t max_width) const {
289ac9a064cSDimitry Andric   StreamString strm;
290ac9a064cSDimitry Andric   bool printed_enumerators_once = false;
291ac9a064cSDimitry Andric 
292ac9a064cSDimitry Andric   for (const auto &field : m_fields) {
293ac9a064cSDimitry Andric     const FieldEnum *enum_type = field.GetEnum();
294ac9a064cSDimitry Andric     if (!enum_type)
295ac9a064cSDimitry Andric       continue;
296ac9a064cSDimitry Andric 
297ac9a064cSDimitry Andric     const FieldEnum::Enumerators &enumerators = enum_type->GetEnumerators();
298ac9a064cSDimitry Andric     if (enumerators.empty())
299ac9a064cSDimitry Andric       continue;
300ac9a064cSDimitry Andric 
301ac9a064cSDimitry Andric     // Break between enumerators of different fields.
302ac9a064cSDimitry Andric     if (printed_enumerators_once)
303ac9a064cSDimitry Andric       strm << "\n\n";
304ac9a064cSDimitry Andric     else
305ac9a064cSDimitry Andric       printed_enumerators_once = true;
306ac9a064cSDimitry Andric 
307ac9a064cSDimitry Andric     std::string name_string = field.GetName() + ": ";
308ac9a064cSDimitry Andric     size_t indent = name_string.size();
309ac9a064cSDimitry Andric     size_t current_width = indent;
310ac9a064cSDimitry Andric 
311ac9a064cSDimitry Andric     strm << name_string;
312ac9a064cSDimitry Andric 
313ac9a064cSDimitry Andric     DumpEnumerators(strm, indent, current_width, max_width, enumerators);
314ac9a064cSDimitry Andric   }
315ac9a064cSDimitry Andric 
316ac9a064cSDimitry Andric   return strm.GetString().str();
317ac9a064cSDimitry Andric }
318ac9a064cSDimitry Andric 
EnumsToXML(Stream & strm,llvm::StringSet<> & seen) const319ac9a064cSDimitry Andric void RegisterFlags::EnumsToXML(Stream &strm, llvm::StringSet<> &seen) const {
320ac9a064cSDimitry Andric   for (const Field &field : m_fields)
321ac9a064cSDimitry Andric     if (const FieldEnum *enum_type = field.GetEnum()) {
322ac9a064cSDimitry Andric       const std::string &id = enum_type->GetID();
323ac9a064cSDimitry Andric       if (!seen.contains(id)) {
324ac9a064cSDimitry Andric         enum_type->ToXML(strm, GetSize());
325ac9a064cSDimitry Andric         seen.insert(id);
326ac9a064cSDimitry Andric       }
327ac9a064cSDimitry Andric     }
328ac9a064cSDimitry Andric }
329ac9a064cSDimitry Andric 
ToXML(Stream & strm,unsigned size) const330ac9a064cSDimitry Andric void FieldEnum::ToXML(Stream &strm, unsigned size) const {
331ac9a064cSDimitry Andric   // Example XML:
332ac9a064cSDimitry Andric   // <enum id="foo" size="4">
333ac9a064cSDimitry Andric   //  <evalue name="bar" value="1"/>
334ac9a064cSDimitry Andric   // </enum>
335ac9a064cSDimitry Andric   // Note that "size" is only emitted for GDB compatibility, LLDB does not need
336ac9a064cSDimitry Andric   // it.
337ac9a064cSDimitry Andric 
338ac9a064cSDimitry Andric   strm.Indent();
339ac9a064cSDimitry Andric   strm << "<enum id=\"" << GetID() << "\" ";
340ac9a064cSDimitry Andric   // This is the size of the underlying enum type if this were a C type.
341ac9a064cSDimitry Andric   // In other words, the size of the register in bytes.
342ac9a064cSDimitry Andric   strm.Printf("size=\"%d\"", size);
343ac9a064cSDimitry Andric 
344ac9a064cSDimitry Andric   const Enumerators &enumerators = GetEnumerators();
345ac9a064cSDimitry Andric   if (enumerators.empty()) {
346ac9a064cSDimitry Andric     strm << "/>\n";
347ac9a064cSDimitry Andric     return;
348ac9a064cSDimitry Andric   }
349ac9a064cSDimitry Andric 
350ac9a064cSDimitry Andric   strm << ">\n";
351ac9a064cSDimitry Andric   strm.IndentMore();
352ac9a064cSDimitry Andric   for (const auto &enumerator : enumerators) {
353ac9a064cSDimitry Andric     strm.Indent();
354ac9a064cSDimitry Andric     enumerator.ToXML(strm);
355ac9a064cSDimitry Andric     strm.PutChar('\n');
356ac9a064cSDimitry Andric   }
357ac9a064cSDimitry Andric   strm.IndentLess();
358ac9a064cSDimitry Andric   strm.Indent("</enum>\n");
359ac9a064cSDimitry Andric }
360ac9a064cSDimitry Andric 
ToXML(Stream & strm) const361ac9a064cSDimitry Andric void FieldEnum::Enumerator::ToXML(Stream &strm) const {
362ac9a064cSDimitry Andric   std::string escaped_name;
363ac9a064cSDimitry Andric   llvm::raw_string_ostream escape_strm(escaped_name);
364ac9a064cSDimitry Andric   llvm::printHTMLEscaped(m_name, escape_strm);
365ac9a064cSDimitry Andric   strm.Printf("<evalue name=\"%s\" value=\"%" PRIu64 "\"/>",
366ac9a064cSDimitry Andric               escaped_name.c_str(), m_value);
367ac9a064cSDimitry Andric }
368ac9a064cSDimitry Andric 
DumpToLog(Log * log) const369ac9a064cSDimitry Andric void FieldEnum::Enumerator::DumpToLog(Log *log) const {
370ac9a064cSDimitry Andric   LLDB_LOG(log, "  Name: \"{0}\" Value: {1}", m_name.c_str(), m_value);
371ac9a064cSDimitry Andric }
372ac9a064cSDimitry Andric 
DumpToLog(Log * log) const373ac9a064cSDimitry Andric void FieldEnum::DumpToLog(Log *log) const {
374ac9a064cSDimitry Andric   LLDB_LOG(log, "ID: \"{0}\"", m_id.c_str());
375ac9a064cSDimitry Andric   for (const auto &enumerator : GetEnumerators())
376ac9a064cSDimitry Andric     enumerator.DumpToLog(log);
377ac9a064cSDimitry Andric }
378ac9a064cSDimitry Andric 
ToXML(Stream & strm) const379ac9a064cSDimitry Andric void RegisterFlags::ToXML(Stream &strm) const {
380b1c73532SDimitry Andric   // Example XML:
381b1c73532SDimitry Andric   // <flags id="cpsr_flags" size="4">
382b1c73532SDimitry Andric   //   <field name="incorrect" start="0" end="0"/>
383b1c73532SDimitry Andric   // </flags>
384b1c73532SDimitry Andric   strm.Indent();
385b1c73532SDimitry Andric   strm << "<flags id=\"" << GetID() << "\" ";
386b1c73532SDimitry Andric   strm.Printf("size=\"%d\"", GetSize());
387b1c73532SDimitry Andric   strm << ">";
388b1c73532SDimitry Andric   for (const Field &field : m_fields) {
389b1c73532SDimitry Andric     // Skip padding fields.
390b1c73532SDimitry Andric     if (field.GetName().empty())
391b1c73532SDimitry Andric       continue;
392b1c73532SDimitry Andric 
393b1c73532SDimitry Andric     strm << "\n";
394b1c73532SDimitry Andric     strm.IndentMore();
395b1c73532SDimitry Andric     field.ToXML(strm);
396b1c73532SDimitry Andric     strm.IndentLess();
397b1c73532SDimitry Andric   }
398b1c73532SDimitry Andric   strm.PutChar('\n');
399b1c73532SDimitry Andric   strm.Indent("</flags>\n");
400b1c73532SDimitry Andric }
401b1c73532SDimitry Andric 
ToXML(Stream & strm) const402ac9a064cSDimitry Andric void RegisterFlags::Field::ToXML(Stream &strm) const {
403ac9a064cSDimitry Andric   // Example XML with an enum:
404ac9a064cSDimitry Andric   // <field name="correct" start="0" end="0" type="some_enum">
405ac9a064cSDimitry Andric   // Without:
406b1c73532SDimitry Andric   // <field name="correct" start="0" end="0"/>
407b1c73532SDimitry Andric   strm.Indent();
408b1c73532SDimitry Andric   strm << "<field name=\"";
409b1c73532SDimitry Andric 
410b1c73532SDimitry Andric   std::string escaped_name;
411b1c73532SDimitry Andric   llvm::raw_string_ostream escape_strm(escaped_name);
412b1c73532SDimitry Andric   llvm::printHTMLEscaped(GetName(), escape_strm);
413b1c73532SDimitry Andric   strm << escaped_name << "\" ";
414b1c73532SDimitry Andric 
415b1c73532SDimitry Andric   strm.Printf("start=\"%d\" end=\"%d\"", GetStart(), GetEnd());
416ac9a064cSDimitry Andric 
417ac9a064cSDimitry Andric   if (const FieldEnum *enum_type = GetEnum())
418ac9a064cSDimitry Andric     strm << " type=\"" << enum_type->GetID() << "\"";
419ac9a064cSDimitry Andric 
420b1c73532SDimitry Andric   strm << "/>";
421b1c73532SDimitry Andric }
422ac9a064cSDimitry Andric 
FieldEnum(std::string id,const Enumerators & enumerators)423ac9a064cSDimitry Andric FieldEnum::FieldEnum(std::string id, const Enumerators &enumerators)
424ac9a064cSDimitry Andric     : m_id(id), m_enumerators(enumerators) {
425ac9a064cSDimitry Andric   for (const auto &enumerator : m_enumerators) {
426ac9a064cSDimitry Andric     UNUSED_IF_ASSERT_DISABLED(enumerator);
427ac9a064cSDimitry Andric     assert(enumerator.m_name.size() && "Enumerator name cannot be empty");
428ac9a064cSDimitry Andric   }
429ac9a064cSDimitry Andric }