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 }