Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/llvm-project/lldb/source/Target/RegisterFlags.cpp
96333 views
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
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
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
50
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
55
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
61
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
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
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
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
155
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
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
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
188
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.
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
288
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
319
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
330
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
361
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
369
void FieldEnum::Enumerator::DumpToLog(Log *log) const {
370
LLDB_LOG(log, " Name: \"{0}\" Value: {1}", m_name.c_str(), m_value);
371
}
372
373
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
379
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
402
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
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
}
430