DavidSpickett created this revision.
Herald added a subscriber: kristof.beyls.
Herald added a project: All.
DavidSpickett requested review of this revision.
Herald added a project: LLDB.
Herald added a subscriber: lldb-commits.

This models the "flags" node from GDB's target XML:
https://sourceware.org/gdb/onlinedocs/gdb/Target-Description-Format.html

This node is used to describe the fields of registers like cpsr on AArch64.

RegisterFlags is a class that contains a list of register fields.
These fields will be extracted from the XML sent by the remote.

We assume that there is at least one field, that the fields are
sorted in descending order and do not overlap. That will be
enforced by the XML processor (the GDB client code in our case).

The fields may not cover the whole register. To account for this
RegisterFields will add anonymous padding fields so that
sizeof(all fields) == sizeof(register). This will save a lot
of hasssle later.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D145566

Files:
  lldb/include/lldb/Target/RegisterFlags.h
  lldb/source/Target/CMakeLists.txt
  lldb/source/Target/RegisterFlags.cpp
  lldb/unittests/Target/CMakeLists.txt
  lldb/unittests/Target/RegisterFlagsTest.cpp

Index: lldb/unittests/Target/RegisterFlagsTest.cpp
===================================================================
--- /dev/null
+++ lldb/unittests/Target/RegisterFlagsTest.cpp
@@ -0,0 +1,124 @@
+//===-- RegisterFlagsTest.cpp ---------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Target/RegisterFlags.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+using namespace lldb_private;
+using namespace lldb;
+
+TEST(RegisterFlagsTest, Field) {
+  // We assume that start <= end is always true, so that is not tested here.
+
+  RegisterFlags::Field f1("abc", 0, 0, "unknown");
+  ASSERT_EQ(f1.GetName(), "abc");
+  ASSERT_EQ(f1.GetType(), "unknown");
+  // start == end means a 1 bit field.
+  ASSERT_EQ(f1.GetSizeInBits(), (unsigned)1);
+  ASSERT_EQ(f1.GetMask(), (uint64_t)1);
+  ASSERT_EQ(f1.GetValue(0), (uint64_t)0);
+  ASSERT_EQ(f1.GetValue(3), (uint64_t)1);
+
+  // End is inclusive meaning that start 0 to end 1 includes bit 1
+  // to make a 2 bit field.
+  RegisterFlags::Field f2("", 0, 1, "");
+  ASSERT_EQ(f2.GetSizeInBits(), (unsigned)2);
+  ASSERT_EQ(f2.GetMask(), (uint64_t)3);
+  ASSERT_EQ(f2.GetValue(UINT64_MAX), (uint64_t)3);
+  ASSERT_EQ(f2.GetValue(UINT64_MAX & ~(uint64_t)3), (uint64_t)0);
+
+  // If the field doesn't start at 0 we need to shift up/down
+  // to account for it.
+  RegisterFlags::Field f3("", 2, 5, "");
+  ASSERT_EQ(f3.GetSizeInBits(), (unsigned)4);
+  ASSERT_EQ(f3.GetMask(), (uint64_t)0x3c);
+  ASSERT_EQ(f3.GetValue(UINT64_MAX), (uint64_t)0xf);
+  ASSERT_EQ(f3.GetValue(UINT64_MAX & ~(uint64_t)0x3c), (uint64_t)0);
+
+  // Fields are sorted lowest starting bit first.
+  ASSERT_TRUE(f2 < f3);
+  ASSERT_FALSE(f3 < f1);
+  ASSERT_FALSE(f1 < f2);
+  ASSERT_FALSE(f1 < f1);
+}
+
+static RegisterFlags::Field make_field(unsigned start, unsigned end) {
+  return RegisterFlags::Field("", start, end, "");
+}
+
+TEST(RegisterFlagsTest, FieldOverlaps) {
+  // Single bit fields
+  ASSERT_FALSE(make_field(0, 0).Overlaps(make_field(1, 1)));
+  ASSERT_TRUE(make_field(1, 1).Overlaps(make_field(1, 1)));
+  ASSERT_FALSE(make_field(1, 1).Overlaps(make_field(3, 3)));
+
+  ASSERT_TRUE(make_field(0, 1).Overlaps(make_field(1, 2)));
+  ASSERT_TRUE(make_field(1, 2).Overlaps(make_field(0, 1)));
+  ASSERT_FALSE(make_field(0, 1).Overlaps(make_field(2, 3)));
+  ASSERT_FALSE(make_field(2, 3).Overlaps(make_field(0, 1)));
+
+  ASSERT_FALSE(make_field(1, 5).Overlaps(make_field(10, 20)));
+  ASSERT_FALSE(make_field(15, 30).Overlaps(make_field(7, 12)));
+}
+
+TEST(RegisterFlagsTest, PaddingDistance) {
+  // We assume that this method is always called with a more significant
+  // (start bit is higher) field first and that they do not overlap.
+
+  // [field 1][field 2]
+  ASSERT_EQ(make_field(1, 1).PaddingDistance(make_field(0, 0)), 0ULL);
+  // [field 1][..][field 2]
+  ASSERT_EQ(make_field(2, 2).PaddingDistance(make_field(0, 0)), 1ULL);
+  // [field 1][field 1][field 2]
+  ASSERT_EQ(make_field(1, 2).PaddingDistance(make_field(0, 0)), 0ULL);
+  // [field 1][30 bits free][field 2]
+  ASSERT_EQ(make_field(31, 31).PaddingDistance(make_field(0, 0)), 30ULL);
+}
+
+static void test_padding(const std::vector<RegisterFlags::Field> &fields,
+                         const std::vector<RegisterFlags::Field> &expected) {
+  RegisterFlags rf("", 4, fields);
+  EXPECT_THAT(expected, ::testing::ContainerEq(rf.GetFields()));
+}
+
+TEST(RegisterFlagsTest, RegisterFlagsPadding) {
+  // When creating a set of flags we assume that:
+  // * There are >= 1 fields.
+  // * They are sorted in descending order.
+  // * There may be gaps between each field.
+
+  // Needs no padding
+  auto fields =
+      std::vector<RegisterFlags::Field>{make_field(16, 31), make_field(0, 15)};
+  test_padding(fields, fields);
+
+  // Needs padding in between the fields, single bit.
+  test_padding({make_field(17, 31), make_field(0, 15)},
+               {make_field(17, 31), make_field(16, 16), make_field(0, 15)});
+  // Multiple bits of padding.
+  test_padding({make_field(17, 31), make_field(0, 14)},
+               {make_field(17, 31), make_field(15, 16), make_field(0, 14)});
+
+  // Padding before first field, single bit.
+  test_padding({make_field(0, 30)}, {make_field(31, 31), make_field(0, 30)});
+  // Multiple bits.
+  test_padding({make_field(0, 15)}, {make_field(16, 31), make_field(0, 15)});
+
+  // Padding after last field, single bit.
+  test_padding({make_field(1, 31)}, {make_field(1, 31), make_field(0, 0)});
+  // Multiple bits.
+  test_padding({make_field(2, 31)}, {make_field(2, 31), make_field(0, 1)});
+
+  // Fields need padding before, in between and after.
+  // [31-28][field 27-24][23-22][field 21-20][19-12][field 11-8][7-0]
+  test_padding({make_field(24, 27), make_field(20, 21), make_field(8, 11)},
+               {make_field(28, 31), make_field(24, 27), make_field(22, 23),
+                make_field(20, 21), make_field(12, 19), make_field(8, 11),
+                make_field(0, 7)});
+}
\ No newline at end of file
Index: lldb/unittests/Target/CMakeLists.txt
===================================================================
--- lldb/unittests/Target/CMakeLists.txt
+++ lldb/unittests/Target/CMakeLists.txt
@@ -6,6 +6,7 @@
   MemoryTagMapTest.cpp
   ModuleCacheTest.cpp
   PathMappingListTest.cpp
+  RegisterFlagsTest.cpp
   RemoteAwarePlatformTest.cpp
   StackFrameRecognizerTest.cpp
   FindFileTest.cpp
Index: lldb/source/Target/RegisterFlags.cpp
===================================================================
--- /dev/null
+++ lldb/source/Target/RegisterFlags.cpp
@@ -0,0 +1,93 @@
+//===-- RegisterFlags.cpp -------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Target/RegisterFlags.h"
+
+#include <optional>
+
+using namespace lldb_private;
+
+void RegisterFlags::Field::log(Log *log) const {
+  LLDB_LOGF(log, "  Name: \"%s\" Start: %d End: %d Type: \"%s\"",
+            m_name.c_str(), m_start, m_end, m_type.c_str());
+}
+
+bool RegisterFlags::Field::Overlaps(const Field &other) const {
+  unsigned overlap_start = std::max(GetStart(), other.GetStart());
+  unsigned overlap_end = std::min(GetEnd(), other.GetEnd());
+  return overlap_start <= overlap_end;
+}
+
+unsigned RegisterFlags::Field::PaddingDistance(const Field &other) const {
+  assert(!Overlaps(other) &&
+         "Cannot get padding distance for overlapping fields.");
+  assert((other < (*this)) && "Expected fields in MSB to LSB order.");
+
+  // If they don't overlap they are either next to each other or separated
+  // by some number of bits.
+
+  // Where left will be the MSB and right will be the LSB.
+  unsigned lhs_start = GetStart();
+  unsigned rhs_end = other.GetStart() + other.GetSizeInBits() - 1;
+
+  if (*this < other) {
+    lhs_start = other.GetStart();
+    rhs_end = GetStart() + GetSizeInBits() - 1;
+  }
+
+  return lhs_start - rhs_end - 1;
+}
+
+RegisterFlags::RegisterFlags(const std::string &id, unsigned size,
+                             const std::vector<Field> &fields)
+    : m_id(id), m_size(size) {
+  // We expect that the XML processor will discard anything decsribing flags but
+  // with no fields.
+  assert(fields.size() && "Some fields must be provided.");
+
+  // 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;
+  m_fields.reserve(provided_fields.size());
+
+  // ProcessGDBRemote should have sorted these in descending order already.
+  assert(std::is_sorted(provided_fields.rbegin(), provided_fields.rend()));
+
+  // Build a new list of fields that includes anonymous (empty name) fields
+  // wherever there is a gap. This will simplify processing later.
+  std::optional<Field> previous_field;
+  unsigned register_msb = (size * 8) - 1;
+  for (auto field : provided_fields) {
+    if (previous_field) {
+      unsigned padding = previous_field->PaddingDistance(field);
+      if (padding) {
+        // -1 to end just before the previous field.
+        unsigned end = previous_field->GetStart() - 1;
+        // +1 because if you want to pad 1 bit you want to start and end
+        // on the same bit.
+        m_fields.push_back(Field("", field.GetEnd() + 1, end, ""));
+      }
+    } else {
+      // This is the first field. Check that it starts at the register's MSB.
+      if (field.GetEnd() != register_msb)
+        m_fields.push_back(Field("", field.GetEnd() + 1, register_msb, ""));
+    }
+    m_fields.push_back(field);
+    previous_field = field;
+  }
+
+  // The last field may not extend all the way to bit 0.
+  if (previous_field && previous_field->GetStart() != 0)
+    m_fields.push_back(Field("", 0, previous_field->GetStart() - 1, ""));
+}
+
+void RegisterFlags::log(Log *log) const {
+  LLDB_LOGF(log, "ID: \"%s\" Size: %d", m_id.c_str(), m_size);
+  for (const Field &field : m_fields)
+    field.log(log);
+}
Index: lldb/source/Target/CMakeLists.txt
===================================================================
--- lldb/source/Target/CMakeLists.txt
+++ lldb/source/Target/CMakeLists.txt
@@ -32,6 +32,7 @@
   QueueList.cpp
   RegisterContext.cpp
   RegisterContextUnwind.cpp
+  RegisterFlags.cpp
   RegisterNumber.cpp
   RemoteAwarePlatform.cpp
   SectionLoadHistory.cpp
Index: lldb/include/lldb/Target/RegisterFlags.h
===================================================================
--- /dev/null
+++ lldb/include/lldb/Target/RegisterFlags.h
@@ -0,0 +1,93 @@
+//===-- RegisterFlags.h -----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_TARGET_REGISTERFLAGS_H
+#define LLDB_TARGET_REGISTERFLAGS_H
+
+#include "lldb/Utility/Log.h"
+
+namespace lldb_private {
+
+class RegisterFlags {
+public:
+  class Field {
+  public:
+    Field(const std::string &name, unsigned start, unsigned end,
+          const std::string &type)
+        : m_name(name), m_start(start), m_end(end), m_type(type) {
+      assert(m_start <= m_end && "Start bit must be <= end bit.");
+    }
+
+    // Get size of the field in bits. Will always be at least 1.
+    unsigned GetSizeInBits() const { return m_end - m_start + 1; }
+
+    // A mask that covers all bits of the field.
+    uint64_t GetMask() const {
+      return (((uint64_t)1 << (GetSizeInBits())) - 1) << m_start;
+    }
+
+    // Extract value of the field from a whole register value.
+    uint64_t GetValue(uint64_t register_value) const {
+      return (register_value & GetMask()) >> m_start;
+    }
+
+    const std::string &GetName() const { return m_name; }
+    const std::string &GetType() const { return m_type; }
+    unsigned GetStart() const { return m_start; }
+    unsigned GetEnd() const { return m_end; }
+    bool Overlaps(const Field &other) const;
+    void log(Log *log) const;
+
+    // Return the number of bits between this field and the other, that are not
+    // covered by either field.
+    unsigned PaddingDistance(const Field &other) const;
+
+    bool operator<(const Field &rhs) const {
+      return GetStart() < rhs.GetStart();
+    }
+
+    bool operator==(const Field &rhs) const {
+      return (m_name == rhs.m_name) && (m_start == rhs.m_start) &&
+             (m_end == rhs.m_end) && (m_type == rhs.m_type);
+    }
+
+  private:
+    std::string m_name;
+    // Start/end bit positions. Where start N, end N means a single bit
+    // field at position N. We expect that start <= end. Bit positions begin
+    // at 0.
+    // Start is the LSB, end is the MSB.
+    unsigned m_start;
+    unsigned m_end;
+    // Likely "bool" (for single bits) or "uint32" for larger fields.
+    // We do not expect to see another flags type here.
+    std::string m_type;
+  };
+
+  // This assumes that:
+  // * There is at least one field.
+  // * The fields are sorted in descending order.
+  // Gaps are allowed, they will be filled with anonymous padding fields.
+  RegisterFlags(const std::string &id, unsigned size,
+                const std::vector<Field> &fields);
+
+  const std::vector<Field> &GetFields() const { return m_fields; }
+  const std::string &GetID() const { return m_id; }
+  unsigned GetSize() const { return m_size; }
+  void log(Log *log) const;
+
+private:
+  std::string m_id;
+  // Size in bytes
+  unsigned m_size;
+  std::vector<Field> m_fields;
+};
+
+} // namespace lldb_private
+
+#endif // LLDB_TARGET_REGISTERFLAGS_H
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to