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 <numeric> |
16 | #include <optional> |
17 | |
18 | using namespace lldb_private; |
19 | |
20 | RegisterFlags::Field::Field(std::string name, unsigned start, unsigned end) |
21 | : m_name(std::move(name)), m_start(start), m_end(end) { |
22 | assert(m_start <= m_end && "Start bit must be <= end bit." ); |
23 | } |
24 | |
25 | void RegisterFlags::Field::log(Log *log) const { |
26 | LLDB_LOG(log, " Name: \"{0}\" Start: {1} End: {2}" , m_name.c_str(), m_start, |
27 | m_end); |
28 | } |
29 | |
30 | bool RegisterFlags::Field::Overlaps(const Field &other) const { |
31 | unsigned overlap_start = std::max(a: GetStart(), b: other.GetStart()); |
32 | unsigned overlap_end = std::min(a: GetEnd(), b: other.GetEnd()); |
33 | return overlap_start <= overlap_end; |
34 | } |
35 | |
36 | unsigned RegisterFlags::Field::PaddingDistance(const Field &other) const { |
37 | assert(!Overlaps(other) && |
38 | "Cannot get padding distance for overlapping fields." ); |
39 | assert((other < (*this)) && "Expected fields in MSB to LSB order." ); |
40 | |
41 | // If they don't overlap they are either next to each other or separated |
42 | // by some number of bits. |
43 | |
44 | // Where left will be the MSB and right will be the LSB. |
45 | unsigned lhs_start = GetStart(); |
46 | unsigned rhs_end = other.GetStart() + other.GetSizeInBits() - 1; |
47 | |
48 | if (*this < other) { |
49 | lhs_start = other.GetStart(); |
50 | rhs_end = GetStart() + GetSizeInBits() - 1; |
51 | } |
52 | |
53 | return lhs_start - rhs_end - 1; |
54 | } |
55 | |
56 | void RegisterFlags::SetFields(const std::vector<Field> &fields) { |
57 | // We expect that the XML processor will discard anything describing flags but |
58 | // with no fields. |
59 | assert(fields.size() && "Some fields must be provided." ); |
60 | |
61 | // We expect that these are unsorted but do not overlap. |
62 | // They could fill the register but may have gaps. |
63 | std::vector<Field> provided_fields = fields; |
64 | |
65 | m_fields.clear(); |
66 | m_fields.reserve(n: provided_fields.size()); |
67 | |
68 | // ProcessGDBRemote should have sorted these in descending order already. |
69 | assert(std::is_sorted(provided_fields.rbegin(), provided_fields.rend())); |
70 | |
71 | // Build a new list of fields that includes anonymous (empty name) fields |
72 | // wherever there is a gap. This will simplify processing later. |
73 | std::optional<Field> previous_field; |
74 | unsigned register_msb = (m_size * 8) - 1; |
75 | for (auto field : provided_fields) { |
76 | if (previous_field) { |
77 | unsigned padding = previous_field->PaddingDistance(other: field); |
78 | if (padding) { |
79 | // -1 to end just before the previous field. |
80 | unsigned end = previous_field->GetStart() - 1; |
81 | // +1 because if you want to pad 1 bit you want to start and end |
82 | // on the same bit. |
83 | m_fields.push_back(x: Field("" , field.GetEnd() + 1, end)); |
84 | } |
85 | } else { |
86 | // This is the first field. Check that it starts at the register's MSB. |
87 | if (field.GetEnd() != register_msb) |
88 | m_fields.push_back(x: Field("" , field.GetEnd() + 1, register_msb)); |
89 | } |
90 | m_fields.push_back(x: field); |
91 | previous_field = field; |
92 | } |
93 | |
94 | // The last field may not extend all the way to bit 0. |
95 | if (previous_field && previous_field->GetStart() != 0) |
96 | m_fields.push_back(x: Field("" , 0, previous_field->GetStart() - 1)); |
97 | } |
98 | |
99 | RegisterFlags::RegisterFlags(std::string id, unsigned size, |
100 | const std::vector<Field> &fields) |
101 | : m_id(std::move(id)), m_size(size) { |
102 | SetFields(fields); |
103 | } |
104 | |
105 | void RegisterFlags::log(Log *log) const { |
106 | LLDB_LOG(log, "ID: \"{0}\" Size: {1}" , m_id.c_str(), m_size); |
107 | for (const Field &field : m_fields) |
108 | field.log(log); |
109 | } |
110 | |
111 | static StreamString FormatCell(const StreamString &content, |
112 | unsigned column_width) { |
113 | unsigned pad = column_width - content.GetString().size(); |
114 | std::string pad_l; |
115 | std::string pad_r; |
116 | if (pad) { |
117 | pad_l = std::string(pad / 2, ' '); |
118 | pad_r = std::string((pad / 2) + (pad % 2), ' '); |
119 | } |
120 | |
121 | StreamString aligned; |
122 | aligned.Printf(format: "|%s%s%s" , pad_l.c_str(), content.GetString().data(), |
123 | pad_r.c_str()); |
124 | return aligned; |
125 | } |
126 | |
127 | static void EmitTable(std::string &out, std::array<std::string, 3> &table) { |
128 | // Close the table. |
129 | for (std::string &line : table) |
130 | line += '|'; |
131 | |
132 | out += std::accumulate(first: table.begin() + 1, last: table.end(), init: table.front(), |
133 | binary_op: [](std::string lhs, const auto &rhs) { |
134 | return std::move(lhs) + "\n" + rhs; |
135 | }); |
136 | } |
137 | |
138 | std::string RegisterFlags::AsTable(uint32_t max_width) const { |
139 | std::string table; |
140 | // position / gridline / name |
141 | std::array<std::string, 3> lines; |
142 | uint32_t current_width = 0; |
143 | |
144 | for (const RegisterFlags::Field &field : m_fields) { |
145 | StreamString position; |
146 | if (field.GetEnd() == field.GetStart()) |
147 | position.Printf(format: " %d " , field.GetEnd()); |
148 | else |
149 | position.Printf(format: " %d-%d " , field.GetEnd(), field.GetStart()); |
150 | |
151 | StreamString name; |
152 | name.Printf(format: " %s " , field.GetName().c_str()); |
153 | |
154 | unsigned column_width = position.GetString().size(); |
155 | unsigned name_width = name.GetString().size(); |
156 | if (name_width > column_width) |
157 | column_width = name_width; |
158 | |
159 | // If the next column would overflow and we have already formatted at least |
160 | // one column, put out what we have and move to a new table on the next line |
161 | // (+1 here because we need to cap the ends with '|'). If this is the first |
162 | // column, just let it overflow and we'll wrap next time around. There's not |
163 | // much we can do with a very small terminal. |
164 | if (current_width && ((current_width + column_width + 1) >= max_width)) { |
165 | EmitTable(out&: table, table&: lines); |
166 | // Blank line between each. |
167 | table += "\n\n" ; |
168 | |
169 | for (std::string &line : lines) |
170 | line.clear(); |
171 | current_width = 0; |
172 | } |
173 | |
174 | StreamString aligned_position = FormatCell(content: position, column_width); |
175 | lines[0] += aligned_position.GetString(); |
176 | StreamString grid; |
177 | grid << '|' << std::string(column_width, '-'); |
178 | lines[1] += grid.GetString(); |
179 | StreamString aligned_name = FormatCell(content: name, column_width); |
180 | lines[2] += aligned_name.GetString(); |
181 | |
182 | // +1 for the left side '|'. |
183 | current_width += column_width + 1; |
184 | } |
185 | |
186 | // If we didn't overflow and still have table to print out. |
187 | if (lines[0].size()) |
188 | EmitTable(out&: table, table&: lines); |
189 | |
190 | return table; |
191 | } |
192 | |
193 | void RegisterFlags::ToXML(StreamString &strm) const { |
194 | // Example XML: |
195 | // <flags id="cpsr_flags" size="4"> |
196 | // <field name="incorrect" start="0" end="0"/> |
197 | // </flags> |
198 | strm.Indent(); |
199 | strm << "<flags id=\"" << GetID() << "\" " ; |
200 | strm.Printf(format: "size=\"%d\"" , GetSize()); |
201 | strm << ">" ; |
202 | for (const Field &field : m_fields) { |
203 | // Skip padding fields. |
204 | if (field.GetName().empty()) |
205 | continue; |
206 | |
207 | strm << "\n" ; |
208 | strm.IndentMore(); |
209 | field.ToXML(strm); |
210 | strm.IndentLess(); |
211 | } |
212 | strm.PutChar(ch: '\n'); |
213 | strm.Indent(s: "</flags>\n" ); |
214 | } |
215 | |
216 | void RegisterFlags::Field::ToXML(StreamString &strm) const { |
217 | // Example XML: |
218 | // <field name="correct" start="0" end="0"/> |
219 | strm.Indent(); |
220 | strm << "<field name=\"" ; |
221 | |
222 | std::string escaped_name; |
223 | llvm::raw_string_ostream escape_strm(escaped_name); |
224 | llvm::printHTMLEscaped(String: GetName(), Out&: escape_strm); |
225 | strm << escaped_name << "\" " ; |
226 | |
227 | strm.Printf(format: "start=\"%d\" end=\"%d\"" , GetStart(), GetEnd()); |
228 | strm << "/>" ; |
229 | } |
230 | |