xref: /freebsd/contrib/llvm-project/lldb/source/Target/RegisterFlags.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
1 //===-- RegisterFlags.cpp -------------------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "lldb/Target/RegisterFlags.h"
10 #include "lldb/Utility/Log.h"
11 #include "lldb/Utility/StreamString.h"
12 
13 #include "llvm/ADT/StringExtras.h"
14 
15 #include <limits>
16 #include <numeric>
17 #include <optional>
18 
19 using namespace lldb_private;
20 
Field(std::string name,unsigned start,unsigned end)21 RegisterFlags::Field::Field(std::string name, unsigned start, unsigned end)
22     : m_name(std::move(name)), m_start(start), m_end(end),
23       m_enum_type(nullptr) {
24   assert(m_start <= m_end && "Start bit must be <= end bit.");
25 }
26 
Field(std::string name,unsigned bit_position)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 
Field(std::string name,unsigned start,unsigned end,const FieldEnum * enum_type)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 
DumpToLog(Log * log) const50 void RegisterFlags::Field::DumpToLog(Log *log) const {
51   LLDB_LOG(log, "  Name: \"{0}\" Start: {1} End: {2}", m_name.c_str(), m_start,
52            m_end);
53 }
54 
Overlaps(const Field & other) const55 bool RegisterFlags::Field::Overlaps(const Field &other) const {
56   unsigned overlap_start = std::max(GetStart(), other.GetStart());
57   unsigned overlap_end = std::min(GetEnd(), other.GetEnd());
58   return overlap_start <= overlap_end;
59 }
60 
PaddingDistance(const Field & other) const61 unsigned RegisterFlags::Field::PaddingDistance(const Field &other) const {
62   assert(!Overlaps(other) &&
63          "Cannot get padding distance for overlapping fields.");
64   assert((other < (*this)) && "Expected fields in MSB to LSB order.");
65 
66   // If they don't overlap they are either next to each other or separated
67   // by some number of bits.
68 
69   // Where left will be the MSB and right will be the LSB.
70   unsigned lhs_start = GetStart();
71   unsigned rhs_end = other.GetStart() + other.GetSizeInBits() - 1;
72 
73   if (*this < other) {
74     lhs_start = other.GetStart();
75     rhs_end = GetStart() + GetSizeInBits() - 1;
76   }
77 
78   return lhs_start - rhs_end - 1;
79 }
80 
GetSizeInBits(unsigned start,unsigned end)81 unsigned RegisterFlags::Field::GetSizeInBits(unsigned start, unsigned end) {
82   return end - start + 1;
83 }
84 
GetSizeInBits() const85 unsigned RegisterFlags::Field::GetSizeInBits() const {
86   return GetSizeInBits(m_start, m_end);
87 }
88 
GetMaxValue(unsigned start,unsigned end)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 
GetMaxValue() const102 uint64_t RegisterFlags::Field::GetMaxValue() const {
103   return GetMaxValue(m_start, m_end);
104 }
105 
GetMask() const106 uint64_t RegisterFlags::Field::GetMask() const {
107   return GetMaxValue() << m_start;
108 }
109 
SetFields(const std::vector<Field> & fields)110 void RegisterFlags::SetFields(const std::vector<Field> &fields) {
111   // We expect that these are unsorted but do not overlap.
112   // They could fill the register but may have gaps.
113   std::vector<Field> provided_fields = fields;
114 
115   m_fields.clear();
116   m_fields.reserve(provided_fields.size());
117 
118   // ProcessGDBRemote should have sorted these in descending order already.
119   assert(std::is_sorted(provided_fields.rbegin(), provided_fields.rend()));
120 
121   // Build a new list of fields that includes anonymous (empty name) fields
122   // wherever there is a gap. This will simplify processing later.
123   std::optional<Field> previous_field;
124   unsigned register_msb = (m_size * 8) - 1;
125   for (auto field : provided_fields) {
126     if (previous_field) {
127       unsigned padding = previous_field->PaddingDistance(field);
128       if (padding) {
129         // -1 to end just before the previous field.
130         unsigned end = previous_field->GetStart() - 1;
131         // +1 because if you want to pad 1 bit you want to start and end
132         // on the same bit.
133         m_fields.push_back(Field("", field.GetEnd() + 1, end));
134       }
135     } else {
136       // This is the first field. Check that it starts at the register's MSB.
137       if (field.GetEnd() != register_msb)
138         m_fields.push_back(Field("", field.GetEnd() + 1, register_msb));
139     }
140     m_fields.push_back(field);
141     previous_field = field;
142   }
143 
144   // The last field may not extend all the way to bit 0.
145   if (previous_field && previous_field->GetStart() != 0)
146     m_fields.push_back(Field("", 0, previous_field->GetStart() - 1));
147 }
148 
RegisterFlags(std::string id,unsigned size,const std::vector<Field> & fields)149 RegisterFlags::RegisterFlags(std::string id, unsigned size,
150                              const std::vector<Field> &fields)
151     : m_id(std::move(id)), m_size(size) {
152   SetFields(fields);
153 }
154 
DumpToLog(Log * log) const155 void RegisterFlags::DumpToLog(Log *log) const {
156   LLDB_LOG(log, "ID: \"{0}\" Size: {1}", m_id.c_str(), m_size);
157   for (const Field &field : m_fields)
158     field.DumpToLog(log);
159 }
160 
FormatCell(const StreamString & content,unsigned column_width)161 static StreamString FormatCell(const StreamString &content,
162                                unsigned column_width) {
163   unsigned pad = column_width - content.GetString().size();
164   std::string pad_l;
165   std::string pad_r;
166   if (pad) {
167     pad_l = std::string(pad / 2, ' ');
168     pad_r = std::string((pad / 2) + (pad % 2), ' ');
169   }
170 
171   StreamString aligned;
172   aligned.Printf("|%s%s%s", pad_l.c_str(), content.GetString().data(),
173                  pad_r.c_str());
174   return aligned;
175 }
176 
EmitTable(std::string & out,std::array<std::string,3> & table)177 static void EmitTable(std::string &out, std::array<std::string, 3> &table) {
178   // Close the table.
179   for (std::string &line : table)
180     line += '|';
181 
182   out += std::accumulate(table.begin() + 1, table.end(), table.front(),
183                          [](std::string lhs, const auto &rhs) {
184                            return std::move(lhs) + "\n" + rhs;
185                          });
186 }
187 
AsTable(uint32_t max_width) const188 std::string RegisterFlags::AsTable(uint32_t max_width) const {
189   std::string table;
190   // position / gridline / name
191   std::array<std::string, 3> lines;
192   uint32_t current_width = 0;
193 
194   for (const RegisterFlags::Field &field : m_fields) {
195     StreamString position;
196     if (field.GetEnd() == field.GetStart())
197       position.Printf(" %d ", field.GetEnd());
198     else
199       position.Printf(" %d-%d ", field.GetEnd(), field.GetStart());
200 
201     StreamString name;
202     name.Printf(" %s ", field.GetName().c_str());
203 
204     unsigned column_width = position.GetString().size();
205     unsigned name_width = name.GetString().size();
206     if (name_width > column_width)
207       column_width = name_width;
208 
209     // If the next column would overflow and we have already formatted at least
210     // one column, put out what we have and move to a new table on the next line
211     // (+1 here because we need to cap the ends with '|'). If this is the first
212     // column, just let it overflow and we'll wrap next time around. There's not
213     // much we can do with a very small terminal.
214     if (current_width && ((current_width + column_width + 1) >= max_width)) {
215       EmitTable(table, lines);
216       // Blank line between each.
217       table += "\n\n";
218 
219       for (std::string &line : lines)
220         line.clear();
221       current_width = 0;
222     }
223 
224     StreamString aligned_position = FormatCell(position, column_width);
225     lines[0] += aligned_position.GetString();
226     StreamString grid;
227     grid << '|' << std::string(column_width, '-');
228     lines[1] += grid.GetString();
229     StreamString aligned_name = FormatCell(name, column_width);
230     lines[2] += aligned_name.GetString();
231 
232     // +1 for the left side '|'.
233     current_width += column_width + 1;
234   }
235 
236   // If we didn't overflow and still have table to print out.
237   if (lines[0].size())
238     EmitTable(table, lines);
239 
240   return table;
241 }
242 
243 // Print enums as:
244 // value = name, value2 = name2
245 // 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)246 static void DumpEnumerators(StreamString &strm, size_t indent,
247                             size_t current_width, uint32_t max_width,
248                             const FieldEnum::Enumerators &enumerators) {
249   for (auto it = enumerators.cbegin(); it != enumerators.cend(); ++it) {
250     StreamString enumerator_strm;
251     // The first enumerator of a line doesn't need to be separated.
252     if (current_width != indent)
253       enumerator_strm << ' ';
254 
255     enumerator_strm.Printf("%" PRIu64 " = %s", it->m_value, it->m_name.c_str());
256 
257     // Don't put "," after the last enumerator.
258     if (std::next(it) != enumerators.cend())
259       enumerator_strm << ",";
260 
261     llvm::StringRef enumerator_string = enumerator_strm.GetString();
262     // If printing the next enumerator would take us over the width, start
263     // a new line. However, if we're printing the first enumerator of this
264     // line, don't start a new one. Resulting in there being at least one per
265     // line.
266     //
267     // This means for very small widths we get:
268     // A: 0 = foo,
269     //    1 = bar
270     // Instead of:
271     // A:
272     //    0 = foo,
273     //    1 = bar
274     if ((current_width + enumerator_string.size() > max_width) &&
275         current_width != indent) {
276       current_width = indent;
277       strm << '\n' << std::string(indent, ' ');
278       // We're going to a new line so we don't need a space before the
279       // name of the enumerator.
280       enumerator_string = enumerator_string.drop_front();
281     }
282 
283     current_width += enumerator_string.size();
284     strm << enumerator_string;
285   }
286 }
287 
DumpEnums(uint32_t max_width) const288 std::string RegisterFlags::DumpEnums(uint32_t max_width) const {
289   StreamString strm;
290   bool printed_enumerators_once = false;
291 
292   for (const auto &field : m_fields) {
293     const FieldEnum *enum_type = field.GetEnum();
294     if (!enum_type)
295       continue;
296 
297     const FieldEnum::Enumerators &enumerators = enum_type->GetEnumerators();
298     if (enumerators.empty())
299       continue;
300 
301     // Break between enumerators of different fields.
302     if (printed_enumerators_once)
303       strm << "\n\n";
304     else
305       printed_enumerators_once = true;
306 
307     std::string name_string = field.GetName() + ": ";
308     size_t indent = name_string.size();
309     size_t current_width = indent;
310 
311     strm << name_string;
312 
313     DumpEnumerators(strm, indent, current_width, max_width, enumerators);
314   }
315 
316   return strm.GetString().str();
317 }
318 
EnumsToXML(Stream & strm,llvm::StringSet<> & seen) const319 void RegisterFlags::EnumsToXML(Stream &strm, llvm::StringSet<> &seen) const {
320   for (const Field &field : m_fields)
321     if (const FieldEnum *enum_type = field.GetEnum()) {
322       const std::string &id = enum_type->GetID();
323       if (!seen.contains(id)) {
324         enum_type->ToXML(strm, GetSize());
325         seen.insert(id);
326       }
327     }
328 }
329 
ToXML(Stream & strm,unsigned size) const330 void FieldEnum::ToXML(Stream &strm, unsigned size) const {
331   // Example XML:
332   // <enum id="foo" size="4">
333   //  <evalue name="bar" value="1"/>
334   // </enum>
335   // Note that "size" is only emitted for GDB compatibility, LLDB does not need
336   // it.
337 
338   strm.Indent();
339   strm << "<enum id=\"" << GetID() << "\" ";
340   // This is the size of the underlying enum type if this were a C type.
341   // In other words, the size of the register in bytes.
342   strm.Printf("size=\"%d\"", size);
343 
344   const Enumerators &enumerators = GetEnumerators();
345   if (enumerators.empty()) {
346     strm << "/>\n";
347     return;
348   }
349 
350   strm << ">\n";
351   strm.IndentMore();
352   for (const auto &enumerator : enumerators) {
353     strm.Indent();
354     enumerator.ToXML(strm);
355     strm.PutChar('\n');
356   }
357   strm.IndentLess();
358   strm.Indent("</enum>\n");
359 }
360 
ToXML(Stream & strm) const361 void FieldEnum::Enumerator::ToXML(Stream &strm) const {
362   std::string escaped_name;
363   llvm::raw_string_ostream escape_strm(escaped_name);
364   llvm::printHTMLEscaped(m_name, escape_strm);
365   strm.Printf("<evalue name=\"%s\" value=\"%" PRIu64 "\"/>",
366               escaped_name.c_str(), m_value);
367 }
368 
DumpToLog(Log * log) const369 void FieldEnum::Enumerator::DumpToLog(Log *log) const {
370   LLDB_LOG(log, "  Name: \"{0}\" Value: {1}", m_name.c_str(), m_value);
371 }
372 
DumpToLog(Log * log) const373 void FieldEnum::DumpToLog(Log *log) const {
374   LLDB_LOG(log, "ID: \"{0}\"", m_id.c_str());
375   for (const auto &enumerator : GetEnumerators())
376     enumerator.DumpToLog(log);
377 }
378 
ToXML(Stream & strm) const379 void RegisterFlags::ToXML(Stream &strm) const {
380   // Example XML:
381   // <flags id="cpsr_flags" size="4">
382   //   <field name="incorrect" start="0" end="0"/>
383   // </flags>
384   strm.Indent();
385   strm << "<flags id=\"" << GetID() << "\" ";
386   strm.Printf("size=\"%d\"", GetSize());
387   strm << ">";
388   for (const Field &field : m_fields) {
389     // Skip padding fields.
390     if (field.GetName().empty())
391       continue;
392 
393     strm << "\n";
394     strm.IndentMore();
395     field.ToXML(strm);
396     strm.IndentLess();
397   }
398   strm.PutChar('\n');
399   strm.Indent("</flags>\n");
400 }
401 
ToXML(Stream & strm) const402 void RegisterFlags::Field::ToXML(Stream &strm) const {
403   // Example XML with an enum:
404   // <field name="correct" start="0" end="0" type="some_enum">
405   // Without:
406   // <field name="correct" start="0" end="0"/>
407   strm.Indent();
408   strm << "<field name=\"";
409 
410   std::string escaped_name;
411   llvm::raw_string_ostream escape_strm(escaped_name);
412   llvm::printHTMLEscaped(GetName(), escape_strm);
413   strm << escaped_name << "\" ";
414 
415   strm.Printf("start=\"%d\" end=\"%d\"", GetStart(), GetEnd());
416 
417   if (const FieldEnum *enum_type = GetEnum())
418     strm << " type=\"" << enum_type->GetID() << "\"";
419 
420   strm << "/>";
421 }
422 
FieldEnum(std::string id,const Enumerators & enumerators)423 FieldEnum::FieldEnum(std::string id, const Enumerators &enumerators)
424     : m_id(id), m_enumerators(enumerators) {
425   for (const auto &enumerator : m_enumerators) {
426     UNUSED_IF_ASSERT_DISABLED(enumerator);
427     assert(enumerator.m_name.size() && "Enumerator name cannot be empty");
428   }
429 }