diff options
author | David Spickett <david.spickett@linaro.org> | 2022-08-05 14:40:31 +0000 |
---|---|---|
committer | David Spickett <david.spickett@linaro.org> | 2023-04-13 10:54:52 +0000 |
commit | 6ea45e3007b8a489afa56af13a2b8bfcec201a93 (patch) | |
tree | 38638d18fd63abe44422cc99c0cde42ba3149072 /lldb/unittests | |
parent | 0bfbecf52e8fa04785cf0b5c38b25c13442fb53d (diff) | |
download | llvm-6ea45e3007b8a489afa56af13a2b8bfcec201a93.tar.gz |
[lldb] Add RegisterFlags class
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.
Reviewed By: jasonmolenda, JDevlieghere
Differential Revision: https://reviews.llvm.org/D145566
Diffstat (limited to 'lldb/unittests')
-rw-r--r-- | lldb/unittests/Target/CMakeLists.txt | 1 | ||||
-rw-r--r-- | lldb/unittests/Target/RegisterFlagsTest.cpp | 123 |
2 files changed, 124 insertions, 0 deletions
diff --git a/lldb/unittests/Target/CMakeLists.txt b/lldb/unittests/Target/CMakeLists.txt index 772d5847b11c..99431435d293 100644 --- a/lldb/unittests/Target/CMakeLists.txt +++ b/lldb/unittests/Target/CMakeLists.txt @@ -7,6 +7,7 @@ add_lldb_unittest(TargetTests MemoryTagMapTest.cpp ModuleCacheTest.cpp PathMappingListTest.cpp + RegisterFlagsTest.cpp RemoteAwarePlatformTest.cpp StackFrameRecognizerTest.cpp FindFileTest.cpp diff --git a/lldb/unittests/Target/RegisterFlagsTest.cpp b/lldb/unittests/Target/RegisterFlagsTest.cpp new file mode 100644 index 000000000000..04edb0caf117 --- /dev/null +++ b/lldb/unittests/Target/RegisterFlagsTest.cpp @@ -0,0 +1,123 @@ +//===-- 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); + ASSERT_EQ(f1.GetName(), "abc"); + // 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)}); +} |