DavidSpickett updated this revision to Diff 503378.
DavidSpickett marked an inline comment as done.
DavidSpickett added a comment.

Add newline at end of file.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D145566/new/

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,125 @@
+//===-- 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)});
+}
+
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(llvm::StringRef id, unsigned size,
+                             const std::vector<Field> &fields)
+    : m_id(id.str()), 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,94 @@
+//===-- 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"
+#include "llvm/ADT/StringRef.h"
+
+namespace lldb_private {
+
+class RegisterFlags {
+public:
+  class Field {
+  public:
+    Field(llvm::StringRef name, unsigned start, unsigned end,
+          llvm::StringRef type)
+        : m_name(name.str()), m_start(start), m_end(end), m_type(type.str()) {
+      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(llvm::StringRef 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