429 lines · cpp
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-exception6//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 19using namespace lldb_private;20 21RegisterFlags::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 27RegisterFlags::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 31RegisterFlags::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 also37 // 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 this39 // 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 50void 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 55bool 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 61unsigned 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 separated67 // 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 81unsigned RegisterFlags::Field::GetSizeInBits(unsigned start, unsigned end) {82 return end - start + 1;83}84 85unsigned RegisterFlags::Field::GetSizeInBits() const {86 return GetSizeInBits(m_start, m_end);87}88 89uint64_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 this94 // 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 102uint64_t RegisterFlags::Field::GetMaxValue() const {103 return GetMaxValue(m_start, m_end);104}105 106uint64_t RegisterFlags::Field::GetMask() const {107 return GetMaxValue() << m_start;108}109 110void 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) fields122 // 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 end132 // 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 149RegisterFlags::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 155void 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 161static 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 177static 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 188std::string RegisterFlags::AsTable(uint32_t max_width) const {189 std::string table;190 // position / gridline / name191 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 else199 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 least210 // one column, put out what we have and move to a new table on the next line211 // (+1 here because we need to cap the ends with '|'). If this is the first212 // column, just let it overflow and we'll wrap next time around. There's not213 // 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 = name2245// Subject to the limits of the terminal width.246static 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, start263 // a new line. However, if we're printing the first enumerator of this264 // line, don't start a new one. Resulting in there being at least one per265 // line.266 //267 // This means for very small widths we get:268 // A: 0 = foo,269 // 1 = bar270 // Instead of:271 // A:272 // 0 = foo,273 // 1 = bar274 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 the279 // 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 288std::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 else305 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 319void 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 330void 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 need336 // 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 361void 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 369void FieldEnum::Enumerator::DumpToLog(Log *log) const {370 LLDB_LOG(log, " Name: \"{0}\" Value: {1}", m_name.c_str(), m_value);371}372 373void 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 379void 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 402void 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 423FieldEnum::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}