summaryrefslogtreecommitdiff
path: root/lldb/source/Target/RegisterFlags.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lldb/source/Target/RegisterFlags.cpp')
-rw-r--r--lldb/source/Target/RegisterFlags.cpp218
1 files changed, 209 insertions, 9 deletions
diff --git a/lldb/source/Target/RegisterFlags.cpp b/lldb/source/Target/RegisterFlags.cpp
index 5274960587bf..976e03870ad9 100644
--- a/lldb/source/Target/RegisterFlags.cpp
+++ b/lldb/source/Target/RegisterFlags.cpp
@@ -12,17 +12,42 @@
#include "llvm/ADT/StringExtras.h"
+#include <limits>
#include <numeric>
#include <optional>
using namespace lldb_private;
RegisterFlags::Field::Field(std::string name, unsigned start, unsigned end)
- : m_name(std::move(name)), m_start(start), m_end(end) {
+ : m_name(std::move(name)), m_start(start), m_end(end),
+ m_enum_type(nullptr) {
assert(m_start <= m_end && "Start bit must be <= end bit.");
}
-void RegisterFlags::Field::log(Log *log) const {
+RegisterFlags::Field::Field(std::string name, unsigned bit_position)
+ : m_name(std::move(name)), m_start(bit_position), m_end(bit_position),
+ m_enum_type(nullptr) {}
+
+RegisterFlags::Field::Field(std::string name, unsigned start, unsigned end,
+ const FieldEnum *enum_type)
+ : m_name(std::move(name)), m_start(start), m_end(end),
+ m_enum_type(enum_type) {
+ if (m_enum_type) {
+ // Check that all values fit into this field. The XML parser will also
+ // do this check so at runtime nothing should fail this check.
+ // We can also make enums in C++ at compile time, which might fail this
+ // check, so we catch them before it makes it into a release.
+ uint64_t max_value = GetMaxValue();
+ UNUSED_IF_ASSERT_DISABLED(max_value);
+ for (const auto &enumerator : m_enum_type->GetEnumerators()) {
+ UNUSED_IF_ASSERT_DISABLED(enumerator);
+ assert(enumerator.m_value <= max_value &&
+ "Enumerator value exceeds maximum value for this field");
+ }
+ }
+}
+
+void RegisterFlags::Field::DumpToLog(Log *log) const {
LLDB_LOG(log, " Name: \"{0}\" Start: {1} End: {2}", m_name.c_str(), m_start,
m_end);
}
@@ -53,11 +78,36 @@ unsigned RegisterFlags::Field::PaddingDistance(const Field &other) const {
return lhs_start - rhs_end - 1;
}
-void RegisterFlags::SetFields(const std::vector<Field> &fields) {
- // We expect that the XML processor will discard anything describing flags but
- // with no fields.
- assert(fields.size() && "Some fields must be provided.");
+unsigned RegisterFlags::Field::GetSizeInBits(unsigned start, unsigned end) {
+ return end - start + 1;
+}
+
+unsigned RegisterFlags::Field::GetSizeInBits() const {
+ return GetSizeInBits(m_start, m_end);
+}
+
+uint64_t RegisterFlags::Field::GetMaxValue(unsigned start, unsigned end) {
+ uint64_t max = std::numeric_limits<uint64_t>::max();
+ unsigned bits = GetSizeInBits(start, end);
+ // If the field is >= 64 bits the shift below would be undefined.
+ // We assume the GDB client has discarded any field that would fail this
+ // assert, it's only to check information we define directly in C++.
+ assert(bits <= 64 && "Cannot handle field with size > 64 bits");
+ if (bits < 64) {
+ max = ((uint64_t)1 << bits) - 1;
+ }
+ return max;
+}
+
+uint64_t RegisterFlags::Field::GetMaxValue() const {
+ return GetMaxValue(m_start, m_end);
+}
+
+uint64_t RegisterFlags::Field::GetMask() const {
+ return GetMaxValue() << m_start;
+}
+void RegisterFlags::SetFields(const std::vector<Field> &fields) {
// We expect that these are unsorted but do not overlap.
// They could fill the register but may have gaps.
std::vector<Field> provided_fields = fields;
@@ -102,10 +152,10 @@ RegisterFlags::RegisterFlags(std::string id, unsigned size,
SetFields(fields);
}
-void RegisterFlags::log(Log *log) const {
+void RegisterFlags::DumpToLog(Log *log) const {
LLDB_LOG(log, "ID: \"{0}\" Size: {1}", m_id.c_str(), m_size);
for (const Field &field : m_fields)
- field.log(log);
+ field.DumpToLog(log);
}
static StreamString FormatCell(const StreamString &content,
@@ -190,6 +240,142 @@ std::string RegisterFlags::AsTable(uint32_t max_width) const {
return table;
}
+// Print enums as:
+// value = name, value2 = name2
+// Subject to the limits of the terminal width.
+static void DumpEnumerators(StreamString &strm, size_t indent,
+ size_t current_width, uint32_t max_width,
+ const FieldEnum::Enumerators &enumerators) {
+ for (auto it = enumerators.cbegin(); it != enumerators.cend(); ++it) {
+ StreamString enumerator_strm;
+ // The first enumerator of a line doesn't need to be separated.
+ if (current_width != indent)
+ enumerator_strm << ' ';
+
+ enumerator_strm.Printf("%" PRIu64 " = %s", it->m_value, it->m_name.c_str());
+
+ // Don't put "," after the last enumerator.
+ if (std::next(it) != enumerators.cend())
+ enumerator_strm << ",";
+
+ llvm::StringRef enumerator_string = enumerator_strm.GetString();
+ // If printing the next enumerator would take us over the width, start
+ // a new line. However, if we're printing the first enumerator of this
+ // line, don't start a new one. Resulting in there being at least one per
+ // line.
+ //
+ // This means for very small widths we get:
+ // A: 0 = foo,
+ // 1 = bar
+ // Instead of:
+ // A:
+ // 0 = foo,
+ // 1 = bar
+ if ((current_width + enumerator_string.size() > max_width) &&
+ current_width != indent) {
+ current_width = indent;
+ strm << '\n' << std::string(indent, ' ');
+ // We're going to a new line so we don't need a space before the
+ // name of the enumerator.
+ enumerator_string = enumerator_string.drop_front();
+ }
+
+ current_width += enumerator_string.size();
+ strm << enumerator_string;
+ }
+}
+
+std::string RegisterFlags::DumpEnums(uint32_t max_width) const {
+ StreamString strm;
+ bool printed_enumerators_once = false;
+
+ for (const auto &field : m_fields) {
+ const FieldEnum *enum_type = field.GetEnum();
+ if (!enum_type)
+ continue;
+
+ const FieldEnum::Enumerators &enumerators = enum_type->GetEnumerators();
+ if (enumerators.empty())
+ continue;
+
+ // Break between enumerators of different fields.
+ if (printed_enumerators_once)
+ strm << "\n\n";
+ else
+ printed_enumerators_once = true;
+
+ std::string name_string = field.GetName() + ": ";
+ size_t indent = name_string.size();
+ size_t current_width = indent;
+
+ strm << name_string;
+
+ DumpEnumerators(strm, indent, current_width, max_width, enumerators);
+ }
+
+ return strm.GetString().str();
+}
+
+void RegisterFlags::EnumsToXML(Stream &strm, llvm::StringSet<> &seen) const {
+ for (const Field &field : m_fields)
+ if (const FieldEnum *enum_type = field.GetEnum()) {
+ const std::string &id = enum_type->GetID();
+ if (!seen.contains(id)) {
+ enum_type->ToXML(strm, GetSize());
+ seen.insert(id);
+ }
+ }
+}
+
+void FieldEnum::ToXML(Stream &strm, unsigned size) const {
+ // Example XML:
+ // <enum id="foo" size="4">
+ // <evalue name="bar" value="1"/>
+ // </enum>
+ // Note that "size" is only emitted for GDB compatibility, LLDB does not need
+ // it.
+
+ strm.Indent();
+ strm << "<enum id=\"" << GetID() << "\" ";
+ // This is the size of the underlying enum type if this were a C type.
+ // In other words, the size of the register in bytes.
+ strm.Printf("size=\"%d\"", size);
+
+ const Enumerators &enumerators = GetEnumerators();
+ if (enumerators.empty()) {
+ strm << "/>\n";
+ return;
+ }
+
+ strm << ">\n";
+ strm.IndentMore();
+ for (const auto &enumerator : enumerators) {
+ strm.Indent();
+ enumerator.ToXML(strm);
+ strm.PutChar('\n');
+ }
+ strm.IndentLess();
+ strm.Indent("</enum>\n");
+}
+
+void FieldEnum::Enumerator::ToXML(Stream &strm) const {
+ std::string escaped_name;
+ llvm::raw_string_ostream escape_strm(escaped_name);
+ llvm::printHTMLEscaped(m_name, escape_strm);
+ strm.Printf("<evalue name=\"%s\" value=\"%" PRIu64 "\"/>",
+ escaped_name.c_str(), m_value);
+}
+
+void FieldEnum::Enumerator::DumpToLog(Log *log) const {
+ LLDB_LOG(log, " Name: \"{0}\" Value: {1}", m_name.c_str(), m_value);
+}
+
+void FieldEnum::DumpToLog(Log *log) const {
+ LLDB_LOG(log, "ID: \"{0}\"", m_id.c_str());
+ for (const auto &enumerator : GetEnumerators())
+ enumerator.DumpToLog(log);
+}
+
void RegisterFlags::ToXML(Stream &strm) const {
// Example XML:
// <flags id="cpsr_flags" size="4">
@@ -214,7 +400,9 @@ void RegisterFlags::ToXML(Stream &strm) const {
}
void RegisterFlags::Field::ToXML(Stream &strm) const {
- // Example XML:
+ // Example XML with an enum:
+ // <field name="correct" start="0" end="0" type="some_enum">
+ // Without:
// <field name="correct" start="0" end="0"/>
strm.Indent();
strm << "<field name=\"";
@@ -225,5 +413,17 @@ void RegisterFlags::Field::ToXML(Stream &strm) const {
strm << escaped_name << "\" ";
strm.Printf("start=\"%d\" end=\"%d\"", GetStart(), GetEnd());
+
+ if (const FieldEnum *enum_type = GetEnum())
+ strm << " type=\"" << enum_type->GetID() << "\"";
+
strm << "/>";
}
+
+FieldEnum::FieldEnum(std::string id, const Enumerators &enumerators)
+ : m_id(id), m_enumerators(enumerators) {
+ for (const auto &enumerator : m_enumerators) {
+ UNUSED_IF_ASSERT_DISABLED(enumerator);
+ assert(enumerator.m_name.size() && "Enumerator name cannot be empty");
+ }
+} \ No newline at end of file