brintos

brintos / llvm-project-archived public Read only

0
0
Text · 13.8 KiB · 976e038 Raw
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}