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 }