Path: blob/main/contrib/llvm-project/lldb/source/Target/RegisterFlags.cpp
96333 views
//===-- RegisterFlags.cpp -------------------------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "lldb/Target/RegisterFlags.h"9#include "lldb/Utility/Log.h"10#include "lldb/Utility/StreamString.h"1112#include "llvm/ADT/StringExtras.h"1314#include <limits>15#include <numeric>16#include <optional>1718using namespace lldb_private;1920RegisterFlags::Field::Field(std::string name, unsigned start, unsigned end)21: m_name(std::move(name)), m_start(start), m_end(end),22m_enum_type(nullptr) {23assert(m_start <= m_end && "Start bit must be <= end bit.");24}2526RegisterFlags::Field::Field(std::string name, unsigned bit_position)27: m_name(std::move(name)), m_start(bit_position), m_end(bit_position),28m_enum_type(nullptr) {}2930RegisterFlags::Field::Field(std::string name, unsigned start, unsigned end,31const FieldEnum *enum_type)32: m_name(std::move(name)), m_start(start), m_end(end),33m_enum_type(enum_type) {34if (m_enum_type) {35// Check that all values fit into this field. The XML parser will also36// do this check so at runtime nothing should fail this check.37// We can also make enums in C++ at compile time, which might fail this38// check, so we catch them before it makes it into a release.39uint64_t max_value = GetMaxValue();40UNUSED_IF_ASSERT_DISABLED(max_value);41for (const auto &enumerator : m_enum_type->GetEnumerators()) {42UNUSED_IF_ASSERT_DISABLED(enumerator);43assert(enumerator.m_value <= max_value &&44"Enumerator value exceeds maximum value for this field");45}46}47}4849void RegisterFlags::Field::DumpToLog(Log *log) const {50LLDB_LOG(log, " Name: \"{0}\" Start: {1} End: {2}", m_name.c_str(), m_start,51m_end);52}5354bool RegisterFlags::Field::Overlaps(const Field &other) const {55unsigned overlap_start = std::max(GetStart(), other.GetStart());56unsigned overlap_end = std::min(GetEnd(), other.GetEnd());57return overlap_start <= overlap_end;58}5960unsigned RegisterFlags::Field::PaddingDistance(const Field &other) const {61assert(!Overlaps(other) &&62"Cannot get padding distance for overlapping fields.");63assert((other < (*this)) && "Expected fields in MSB to LSB order.");6465// If they don't overlap they are either next to each other or separated66// by some number of bits.6768// Where left will be the MSB and right will be the LSB.69unsigned lhs_start = GetStart();70unsigned rhs_end = other.GetStart() + other.GetSizeInBits() - 1;7172if (*this < other) {73lhs_start = other.GetStart();74rhs_end = GetStart() + GetSizeInBits() - 1;75}7677return lhs_start - rhs_end - 1;78}7980unsigned RegisterFlags::Field::GetSizeInBits(unsigned start, unsigned end) {81return end - start + 1;82}8384unsigned RegisterFlags::Field::GetSizeInBits() const {85return GetSizeInBits(m_start, m_end);86}8788uint64_t RegisterFlags::Field::GetMaxValue(unsigned start, unsigned end) {89uint64_t max = std::numeric_limits<uint64_t>::max();90unsigned bits = GetSizeInBits(start, end);91// If the field is >= 64 bits the shift below would be undefined.92// We assume the GDB client has discarded any field that would fail this93// assert, it's only to check information we define directly in C++.94assert(bits <= 64 && "Cannot handle field with size > 64 bits");95if (bits < 64) {96max = ((uint64_t)1 << bits) - 1;97}98return max;99}100101uint64_t RegisterFlags::Field::GetMaxValue() const {102return GetMaxValue(m_start, m_end);103}104105uint64_t RegisterFlags::Field::GetMask() const {106return GetMaxValue() << m_start;107}108109void RegisterFlags::SetFields(const std::vector<Field> &fields) {110// We expect that these are unsorted but do not overlap.111// They could fill the register but may have gaps.112std::vector<Field> provided_fields = fields;113114m_fields.clear();115m_fields.reserve(provided_fields.size());116117// ProcessGDBRemote should have sorted these in descending order already.118assert(std::is_sorted(provided_fields.rbegin(), provided_fields.rend()));119120// Build a new list of fields that includes anonymous (empty name) fields121// wherever there is a gap. This will simplify processing later.122std::optional<Field> previous_field;123unsigned register_msb = (m_size * 8) - 1;124for (auto field : provided_fields) {125if (previous_field) {126unsigned padding = previous_field->PaddingDistance(field);127if (padding) {128// -1 to end just before the previous field.129unsigned end = previous_field->GetStart() - 1;130// +1 because if you want to pad 1 bit you want to start and end131// on the same bit.132m_fields.push_back(Field("", field.GetEnd() + 1, end));133}134} else {135// This is the first field. Check that it starts at the register's MSB.136if (field.GetEnd() != register_msb)137m_fields.push_back(Field("", field.GetEnd() + 1, register_msb));138}139m_fields.push_back(field);140previous_field = field;141}142143// The last field may not extend all the way to bit 0.144if (previous_field && previous_field->GetStart() != 0)145m_fields.push_back(Field("", 0, previous_field->GetStart() - 1));146}147148RegisterFlags::RegisterFlags(std::string id, unsigned size,149const std::vector<Field> &fields)150: m_id(std::move(id)), m_size(size) {151SetFields(fields);152}153154void RegisterFlags::DumpToLog(Log *log) const {155LLDB_LOG(log, "ID: \"{0}\" Size: {1}", m_id.c_str(), m_size);156for (const Field &field : m_fields)157field.DumpToLog(log);158}159160static StreamString FormatCell(const StreamString &content,161unsigned column_width) {162unsigned pad = column_width - content.GetString().size();163std::string pad_l;164std::string pad_r;165if (pad) {166pad_l = std::string(pad / 2, ' ');167pad_r = std::string((pad / 2) + (pad % 2), ' ');168}169170StreamString aligned;171aligned.Printf("|%s%s%s", pad_l.c_str(), content.GetString().data(),172pad_r.c_str());173return aligned;174}175176static void EmitTable(std::string &out, std::array<std::string, 3> &table) {177// Close the table.178for (std::string &line : table)179line += '|';180181out += std::accumulate(table.begin() + 1, table.end(), table.front(),182[](std::string lhs, const auto &rhs) {183return std::move(lhs) + "\n" + rhs;184});185}186187std::string RegisterFlags::AsTable(uint32_t max_width) const {188std::string table;189// position / gridline / name190std::array<std::string, 3> lines;191uint32_t current_width = 0;192193for (const RegisterFlags::Field &field : m_fields) {194StreamString position;195if (field.GetEnd() == field.GetStart())196position.Printf(" %d ", field.GetEnd());197else198position.Printf(" %d-%d ", field.GetEnd(), field.GetStart());199200StreamString name;201name.Printf(" %s ", field.GetName().c_str());202203unsigned column_width = position.GetString().size();204unsigned name_width = name.GetString().size();205if (name_width > column_width)206column_width = name_width;207208// If the next column would overflow and we have already formatted at least209// one column, put out what we have and move to a new table on the next line210// (+1 here because we need to cap the ends with '|'). If this is the first211// column, just let it overflow and we'll wrap next time around. There's not212// much we can do with a very small terminal.213if (current_width && ((current_width + column_width + 1) >= max_width)) {214EmitTable(table, lines);215// Blank line between each.216table += "\n\n";217218for (std::string &line : lines)219line.clear();220current_width = 0;221}222223StreamString aligned_position = FormatCell(position, column_width);224lines[0] += aligned_position.GetString();225StreamString grid;226grid << '|' << std::string(column_width, '-');227lines[1] += grid.GetString();228StreamString aligned_name = FormatCell(name, column_width);229lines[2] += aligned_name.GetString();230231// +1 for the left side '|'.232current_width += column_width + 1;233}234235// If we didn't overflow and still have table to print out.236if (lines[0].size())237EmitTable(table, lines);238239return table;240}241242// Print enums as:243// value = name, value2 = name2244// Subject to the limits of the terminal width.245static void DumpEnumerators(StreamString &strm, size_t indent,246size_t current_width, uint32_t max_width,247const FieldEnum::Enumerators &enumerators) {248for (auto it = enumerators.cbegin(); it != enumerators.cend(); ++it) {249StreamString enumerator_strm;250// The first enumerator of a line doesn't need to be separated.251if (current_width != indent)252enumerator_strm << ' ';253254enumerator_strm.Printf("%" PRIu64 " = %s", it->m_value, it->m_name.c_str());255256// Don't put "," after the last enumerator.257if (std::next(it) != enumerators.cend())258enumerator_strm << ",";259260llvm::StringRef enumerator_string = enumerator_strm.GetString();261// If printing the next enumerator would take us over the width, start262// a new line. However, if we're printing the first enumerator of this263// line, don't start a new one. Resulting in there being at least one per264// line.265//266// This means for very small widths we get:267// A: 0 = foo,268// 1 = bar269// Instead of:270// A:271// 0 = foo,272// 1 = bar273if ((current_width + enumerator_string.size() > max_width) &&274current_width != indent) {275current_width = indent;276strm << '\n' << std::string(indent, ' ');277// We're going to a new line so we don't need a space before the278// name of the enumerator.279enumerator_string = enumerator_string.drop_front();280}281282current_width += enumerator_string.size();283strm << enumerator_string;284}285}286287std::string RegisterFlags::DumpEnums(uint32_t max_width) const {288StreamString strm;289bool printed_enumerators_once = false;290291for (const auto &field : m_fields) {292const FieldEnum *enum_type = field.GetEnum();293if (!enum_type)294continue;295296const FieldEnum::Enumerators &enumerators = enum_type->GetEnumerators();297if (enumerators.empty())298continue;299300// Break between enumerators of different fields.301if (printed_enumerators_once)302strm << "\n\n";303else304printed_enumerators_once = true;305306std::string name_string = field.GetName() + ": ";307size_t indent = name_string.size();308size_t current_width = indent;309310strm << name_string;311312DumpEnumerators(strm, indent, current_width, max_width, enumerators);313}314315return strm.GetString().str();316}317318void RegisterFlags::EnumsToXML(Stream &strm, llvm::StringSet<> &seen) const {319for (const Field &field : m_fields)320if (const FieldEnum *enum_type = field.GetEnum()) {321const std::string &id = enum_type->GetID();322if (!seen.contains(id)) {323enum_type->ToXML(strm, GetSize());324seen.insert(id);325}326}327}328329void FieldEnum::ToXML(Stream &strm, unsigned size) const {330// Example XML:331// <enum id="foo" size="4">332// <evalue name="bar" value="1"/>333// </enum>334// Note that "size" is only emitted for GDB compatibility, LLDB does not need335// it.336337strm.Indent();338strm << "<enum id=\"" << GetID() << "\" ";339// This is the size of the underlying enum type if this were a C type.340// In other words, the size of the register in bytes.341strm.Printf("size=\"%d\"", size);342343const Enumerators &enumerators = GetEnumerators();344if (enumerators.empty()) {345strm << "/>\n";346return;347}348349strm << ">\n";350strm.IndentMore();351for (const auto &enumerator : enumerators) {352strm.Indent();353enumerator.ToXML(strm);354strm.PutChar('\n');355}356strm.IndentLess();357strm.Indent("</enum>\n");358}359360void FieldEnum::Enumerator::ToXML(Stream &strm) const {361std::string escaped_name;362llvm::raw_string_ostream escape_strm(escaped_name);363llvm::printHTMLEscaped(m_name, escape_strm);364strm.Printf("<evalue name=\"%s\" value=\"%" PRIu64 "\"/>",365escaped_name.c_str(), m_value);366}367368void FieldEnum::Enumerator::DumpToLog(Log *log) const {369LLDB_LOG(log, " Name: \"{0}\" Value: {1}", m_name.c_str(), m_value);370}371372void FieldEnum::DumpToLog(Log *log) const {373LLDB_LOG(log, "ID: \"{0}\"", m_id.c_str());374for (const auto &enumerator : GetEnumerators())375enumerator.DumpToLog(log);376}377378void RegisterFlags::ToXML(Stream &strm) const {379// Example XML:380// <flags id="cpsr_flags" size="4">381// <field name="incorrect" start="0" end="0"/>382// </flags>383strm.Indent();384strm << "<flags id=\"" << GetID() << "\" ";385strm.Printf("size=\"%d\"", GetSize());386strm << ">";387for (const Field &field : m_fields) {388// Skip padding fields.389if (field.GetName().empty())390continue;391392strm << "\n";393strm.IndentMore();394field.ToXML(strm);395strm.IndentLess();396}397strm.PutChar('\n');398strm.Indent("</flags>\n");399}400401void RegisterFlags::Field::ToXML(Stream &strm) const {402// Example XML with an enum:403// <field name="correct" start="0" end="0" type="some_enum">404// Without:405// <field name="correct" start="0" end="0"/>406strm.Indent();407strm << "<field name=\"";408409std::string escaped_name;410llvm::raw_string_ostream escape_strm(escaped_name);411llvm::printHTMLEscaped(GetName(), escape_strm);412strm << escaped_name << "\" ";413414strm.Printf("start=\"%d\" end=\"%d\"", GetStart(), GetEnd());415416if (const FieldEnum *enum_type = GetEnum())417strm << " type=\"" << enum_type->GetID() << "\"";418419strm << "/>";420}421422FieldEnum::FieldEnum(std::string id, const Enumerators &enumerators)423: m_id(id), m_enumerators(enumerators) {424for (const auto &enumerator : m_enumerators) {425UNUSED_IF_ASSERT_DISABLED(enumerator);426assert(enumerator.m_name.size() && "Enumerator name cannot be empty");427}428}429430