483 lines · cpp
1//===-- RegisterFlagsTest.cpp ---------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "lldb/Target/RegisterFlags.h"10#include "lldb/Utility/StreamString.h"11#include "gmock/gmock.h"12#include "gtest/gtest.h"13 14using namespace lldb_private;15using namespace lldb;16 17TEST(RegisterFlagsTest, Field) {18 // We assume that start <= end is always true, so that is not tested here.19 20 RegisterFlags::Field f1("abc", 0);21 ASSERT_EQ(f1.GetName(), "abc");22 // start == end means a 1 bit field.23 ASSERT_EQ(f1.GetSizeInBits(), (unsigned)1);24 ASSERT_EQ(f1.GetMask(), (uint64_t)1);25 ASSERT_EQ(f1.GetValue(0), (uint64_t)0);26 ASSERT_EQ(f1.GetValue(3), (uint64_t)1);27 28 // End is inclusive meaning that start 0 to end 1 includes bit 129 // to make a 2 bit field.30 RegisterFlags::Field f2("", 0, 1);31 ASSERT_EQ(f2.GetSizeInBits(), (unsigned)2);32 ASSERT_EQ(f2.GetMask(), (uint64_t)3);33 ASSERT_EQ(f2.GetValue(UINT64_MAX), (uint64_t)3);34 ASSERT_EQ(f2.GetValue(UINT64_MAX & ~(uint64_t)3), (uint64_t)0);35 36 // If the field doesn't start at 0 we need to shift up/down37 // to account for it.38 RegisterFlags::Field f3("", 2, 5);39 ASSERT_EQ(f3.GetSizeInBits(), (unsigned)4);40 ASSERT_EQ(f3.GetMask(), (uint64_t)0x3c);41 ASSERT_EQ(f3.GetValue(UINT64_MAX), (uint64_t)0xf);42 ASSERT_EQ(f3.GetValue(UINT64_MAX & ~(uint64_t)0x3c), (uint64_t)0);43 44 // Fields are sorted lowest starting bit first.45 ASSERT_TRUE(f2 < f3);46 ASSERT_FALSE(f3 < f1);47 ASSERT_FALSE(f1 < f2);48 ASSERT_FALSE(f1 < f1);49}50 51static RegisterFlags::Field make_field(unsigned start, unsigned end) {52 return RegisterFlags::Field("", start, end);53}54 55static RegisterFlags::Field make_field(unsigned bit) {56 return RegisterFlags::Field("", bit);57}58 59TEST(RegisterFlagsTest, FieldOverlaps) {60 // Single bit fields61 ASSERT_FALSE(make_field(0, 0).Overlaps(make_field(1)));62 ASSERT_TRUE(make_field(1, 1).Overlaps(make_field(1)));63 ASSERT_FALSE(make_field(1, 1).Overlaps(make_field(3)));64 65 ASSERT_TRUE(make_field(0, 1).Overlaps(make_field(1, 2)));66 ASSERT_TRUE(make_field(1, 2).Overlaps(make_field(0, 1)));67 ASSERT_FALSE(make_field(0, 1).Overlaps(make_field(2, 3)));68 ASSERT_FALSE(make_field(2, 3).Overlaps(make_field(0, 1)));69 70 ASSERT_FALSE(make_field(1, 5).Overlaps(make_field(10, 20)));71 ASSERT_FALSE(make_field(15, 30).Overlaps(make_field(7, 12)));72}73 74TEST(RegisterFlagsTest, PaddingDistance) {75 // We assume that this method is always called with a more significant76 // (start bit is higher) field first and that they do not overlap.77 78 // [field 1][field 2]79 ASSERT_EQ(make_field(1, 1).PaddingDistance(make_field(0)), 0ULL);80 // [field 1][..][field 2]81 ASSERT_EQ(make_field(2, 2).PaddingDistance(make_field(0)), 1ULL);82 // [field 1][field 1][field 2]83 ASSERT_EQ(make_field(1, 2).PaddingDistance(make_field(0)), 0ULL);84 // [field 1][30 bits free][field 2]85 ASSERT_EQ(make_field(31, 31).PaddingDistance(make_field(0)), 30ULL);86}87 88static void test_padding(const std::vector<RegisterFlags::Field> &fields,89 const std::vector<RegisterFlags::Field> &expected) {90 RegisterFlags rf("", 4, fields);91 EXPECT_THAT(expected, ::testing::ContainerEq(rf.GetFields()));92}93 94TEST(RegisterFlagsTest, RegisterFlagsPadding) {95 // When creating a set of flags we assume that:96 // * There are >= 1 fields.97 // * They are sorted in descending order.98 // * There may be gaps between each field.99 100 // Needs no padding101 auto fields =102 std::vector<RegisterFlags::Field>{make_field(16, 31), make_field(0, 15)};103 test_padding(fields, fields);104 105 // Needs padding in between the fields, single bit.106 test_padding({make_field(17, 31), make_field(0, 15)},107 {make_field(17, 31), make_field(16), make_field(0, 15)});108 // Multiple bits of padding.109 test_padding({make_field(17, 31), make_field(0, 14)},110 {make_field(17, 31), make_field(15, 16), make_field(0, 14)});111 112 // Padding before first field, single bit.113 test_padding({make_field(0, 30)}, {make_field(31), make_field(0, 30)});114 // Multiple bits.115 test_padding({make_field(0, 15)}, {make_field(16, 31), make_field(0, 15)});116 117 // Padding after last field, single bit.118 test_padding({make_field(1, 31)}, {make_field(1, 31), make_field(0)});119 // Multiple bits.120 test_padding({make_field(2, 31)}, {make_field(2, 31), make_field(0, 1)});121 122 // Fields need padding before, in between and after.123 // [31-28][field 27-24][23-22][field 21-20][19-12][field 11-8][7-0]124 test_padding({make_field(24, 27), make_field(20, 21), make_field(8, 11)},125 {make_field(28, 31), make_field(24, 27), make_field(22, 23),126 make_field(20, 21), make_field(12, 19), make_field(8, 11),127 make_field(0, 7)});128}129 130TEST(RegisterFieldsTest, ReverseFieldOrder) {131 // Unchanged132 RegisterFlags rf("", 4, {make_field(0, 31)});133 ASSERT_EQ(0x12345678ULL, (unsigned long long)rf.ReverseFieldOrder(0x12345678));134 135 // Swap the two halves around.136 RegisterFlags rf2("", 4, {make_field(16, 31), make_field(0, 15)});137 ASSERT_EQ(0x56781234ULL, (unsigned long long)rf2.ReverseFieldOrder(0x12345678));138 139 // Many small fields.140 RegisterFlags rf3(141 "", 4, {make_field(31), make_field(30), make_field(29), make_field(28)});142 ASSERT_EQ(0x00000005ULL, rf3.ReverseFieldOrder(0xA0000000));143}144 145TEST(RegisterFlagsTest, AsTable) {146 // Anonymous fields are shown with an empty name cell,147 // whether they are known up front or added during construction.148 RegisterFlags anon_field("", 4, {make_field(0, 31)});149 ASSERT_EQ("| 31-0 |\n"150 "|------|\n"151 "| |",152 anon_field.AsTable(100));153 154 RegisterFlags anon_with_pad("", 4, {make_field(16, 31)});155 ASSERT_EQ("| 31-16 | 15-0 |\n"156 "|-------|------|\n"157 "| | |",158 anon_with_pad.AsTable(100));159 160 // Use the wider of position and name to set the column width.161 RegisterFlags name_wider("", 4, {RegisterFlags::Field("aardvark", 0, 31)});162 ASSERT_EQ("| 31-0 |\n"163 "|----------|\n"164 "| aardvark |",165 name_wider.AsTable(100));166 // When the padding is an odd number, put the remaining 1 on the right.167 RegisterFlags pos_wider("", 4, {RegisterFlags::Field("?", 0, 31)});168 ASSERT_EQ("| 31-0 |\n"169 "|------|\n"170 "| ? |",171 pos_wider.AsTable(100));172 173 // Single bit fields don't need to show start and end, just one of them.174 RegisterFlags single_bit("", 4, {make_field(31)});175 ASSERT_EQ("| 31 | 30-0 |\n"176 "|----|------|\n"177 "| | |",178 single_bit.AsTable(100));179 180 // Columns are printed horizontally if max width allows.181 RegisterFlags many_fields("", 4,182 {RegisterFlags::Field("cat", 28, 31),183 RegisterFlags::Field("pigeon", 20, 23),184 RegisterFlags::Field("wolf", 12),185 RegisterFlags::Field("x", 0, 4)});186 ASSERT_EQ("| 31-28 | 27-24 | 23-20 | 19-13 | 12 | 11-5 | 4-0 |\n"187 "|-------|-------|--------|-------|------|------|-----|\n"188 "| cat | | pigeon | | wolf | | x |",189 many_fields.AsTable(100));190 191 // max_width tells us when we need to split into further tables.192 // Here no split is needed.193 RegisterFlags exact_max_single_col("", 4, {RegisterFlags::Field("?", 0, 31)});194 ASSERT_EQ("| 31-0 |\n"195 "|------|\n"196 "| ? |",197 exact_max_single_col.AsTable(9));198 RegisterFlags exact_max_two_col(199 "", 4,200 {RegisterFlags::Field("?", 16, 31), RegisterFlags::Field("#", 0, 15)});201 ASSERT_EQ("| 31-16 | 15-0 |\n"202 "|-------|------|\n"203 "| ? | # |",204 exact_max_two_col.AsTable(16));205 206 // If max is less than a single column, just print the single column. The user207 // will have to put up with some wrapping in this niche case.208 RegisterFlags zero_max_single_col("", 4, {RegisterFlags::Field("?", 0, 31)});209 ASSERT_EQ("| 31-0 |\n"210 "|------|\n"211 "| ? |",212 zero_max_single_col.AsTable(0));213 // Same logic for any following columns. Effectively making a "vertical"214 // table, just with more grid lines.215 RegisterFlags zero_max_two_col(216 "", 4,217 {RegisterFlags::Field("?", 16, 31), RegisterFlags::Field("#", 0, 15)});218 ASSERT_EQ("| 31-16 |\n"219 "|-------|\n"220 "| ? |\n"221 "\n"222 "| 15-0 |\n"223 "|------|\n"224 "| # |",225 zero_max_two_col.AsTable(0));226 227 RegisterFlags max_less_than_single_col("", 4,228 {RegisterFlags::Field("?", 0, 31)});229 ASSERT_EQ("| 31-0 |\n"230 "|------|\n"231 "| ? |",232 max_less_than_single_col.AsTable(3));233 RegisterFlags max_less_than_two_col(234 "", 4,235 {RegisterFlags::Field("?", 16, 31), RegisterFlags::Field("#", 0, 15)});236 ASSERT_EQ("| 31-16 |\n"237 "|-------|\n"238 "| ? |\n"239 "\n"240 "| 15-0 |\n"241 "|------|\n"242 "| # |",243 max_less_than_two_col.AsTable(9));244 RegisterFlags max_many_columns(245 "", 4,246 {RegisterFlags::Field("A", 24, 31), RegisterFlags::Field("B", 16, 23),247 RegisterFlags::Field("C", 8, 15),248 RegisterFlags::Field("really long name", 0, 7)});249 ASSERT_EQ("| 31-24 | 23-16 |\n"250 "|-------|-------|\n"251 "| A | B |\n"252 "\n"253 "| 15-8 |\n"254 "|------|\n"255 "| C |\n"256 "\n"257 "| 7-0 |\n"258 "|------------------|\n"259 "| really long name |",260 max_many_columns.AsTable(23));261}262 263TEST(RegisterFlagsTest, DumpEnums) {264 ASSERT_EQ(RegisterFlags("", 8, {RegisterFlags::Field{"A", 0}}).DumpEnums(80),265 "");266 267 FieldEnum basic_enum("test", {{0, "an_enumerator"}});268 ASSERT_EQ(RegisterFlags("", 8, {RegisterFlags::Field{"A", 0, 0, &basic_enum}})269 .DumpEnums(80),270 "A: 0 = an_enumerator");271 272 // If width is smaller than the enumerator name, print it anyway.273 ASSERT_EQ(RegisterFlags("", 8, {RegisterFlags::Field{"A", 0, 0, &basic_enum}})274 .DumpEnums(5),275 "A: 0 = an_enumerator");276 277 // Multiple values can go on the same line, up to the width.278 FieldEnum more_enum("long_enum",279 {{0, "an_enumerator"},280 {1, "another_enumerator"},281 {2, "a_very_very_long_enumerator_has_its_own_line"},282 {3, "small"},283 {4, "small2"}});284 ASSERT_EQ(RegisterFlags("", 8, {RegisterFlags::Field{"A", 0, 2, &more_enum}})285 // Width is chosen to be exactly enough to allow 0 and 1286 // enumerators on the first line.287 .DumpEnums(45),288 "A: 0 = an_enumerator, 1 = another_enumerator,\n"289 " 2 = a_very_very_long_enumerator_has_its_own_line,\n"290 " 3 = small, 4 = small2");291 292 // If they all exceed width, one per line.293 FieldEnum another_enum("another_enum", {{0, "an_enumerator"},294 {1, "another_enumerator"},295 {2, "a_longer_enumerator"}});296 ASSERT_EQ(297 RegisterFlags("", 8, {RegisterFlags::Field{"A", 0, 1, &another_enum}})298 .DumpEnums(5),299 "A: 0 = an_enumerator,\n"300 " 1 = another_enumerator,\n"301 " 2 = a_longer_enumerator");302 303 // If the name is already > the width, put one value per line.304 FieldEnum short_enum("short_enum", {{0, "a"}, {1, "b"}, {2, "c"}});305 ASSERT_EQ(RegisterFlags("", 8,306 {RegisterFlags::Field{"AReallyLongFieldName", 0, 1,307 &short_enum}})308 .DumpEnums(10),309 "AReallyLongFieldName: 0 = a,\n"310 " 1 = b,\n"311 " 2 = c");312 313 // Fields are separated by a blank line. Indentation of lines split by width314 // is set by the size of the fields name (as opposed to some max of all field315 // names).316 FieldEnum enum_1("enum_1", {{0, "an_enumerator"}, {1, "another_enumerator"}});317 FieldEnum enum_2("enum_2",318 {{0, "Cdef_enumerator_1"}, {1, "Cdef_enumerator_2"}});319 ASSERT_EQ(RegisterFlags("", 8,320 {RegisterFlags::Field{"Ab", 1, 1, &enum_1},321 RegisterFlags::Field{"Cdef", 0, 0, &enum_2}})322 .DumpEnums(10),323 "Ab: 0 = an_enumerator,\n"324 " 1 = another_enumerator\n"325 "\n"326 "Cdef: 0 = Cdef_enumerator_1,\n"327 " 1 = Cdef_enumerator_2");328 329 // Having fields without enumerators shouldn't produce any extra newlines.330 ASSERT_EQ(RegisterFlags("", 8,331 {332 RegisterFlags::Field{"A", 4, 4},333 RegisterFlags::Field{"B", 3, 3, &enum_1},334 RegisterFlags::Field{"C", 2, 2},335 RegisterFlags::Field{"D", 1, 1, &enum_1},336 RegisterFlags::Field{"E", 0, 0},337 })338 .DumpEnums(80),339 "B: 0 = an_enumerator, 1 = another_enumerator\n"340 "\n"341 "D: 0 = an_enumerator, 1 = another_enumerator");342}343 344TEST(RegisterFieldsTest, FlagsToXML) {345 StreamString strm;346 347 // RegisterFlags requires that some fields be given, so no testing of empty348 // input.349 350 // Unnamed fields are padding that are ignored. This applies to fields passed351 // in, and those generated to fill the other bits (31-1 here).352 RegisterFlags("Foo", 4, {RegisterFlags::Field("", 0, 0)}).ToXML(strm);353 ASSERT_EQ(strm.GetString(), "<flags id=\"Foo\" size=\"4\">\n"354 "</flags>\n");355 356 strm.Clear();357 RegisterFlags("Foo", 4, {RegisterFlags::Field("abc", 0, 0)}).ToXML(strm);358 ASSERT_EQ(strm.GetString(), "<flags id=\"Foo\" size=\"4\">\n"359 " <field name=\"abc\" start=\"0\" end=\"0\"/>\n"360 "</flags>\n");361 362 strm.Clear();363 // Should use the current indentation level as a starting point.364 strm.IndentMore();365 RegisterFlags(366 "Bar", 5,367 {RegisterFlags::Field("f1", 25, 32), RegisterFlags::Field("f2", 10, 24)})368 .ToXML(strm);369 ASSERT_EQ(strm.GetString(),370 " <flags id=\"Bar\" size=\"5\">\n"371 " <field name=\"f1\" start=\"25\" end=\"32\"/>\n"372 " <field name=\"f2\" start=\"10\" end=\"24\"/>\n"373 " </flags>\n");374 375 strm.Clear();376 strm.IndentLess();377 // Should replace any XML unsafe characters in field names.378 RegisterFlags("Safe", 8,379 {RegisterFlags::Field("A<", 4), RegisterFlags::Field("B>", 3),380 RegisterFlags::Field("C'", 2), RegisterFlags::Field("D\"", 1),381 RegisterFlags::Field("E&", 0)})382 .ToXML(strm);383 ASSERT_EQ(strm.GetString(),384 "<flags id=\"Safe\" size=\"8\">\n"385 " <field name=\"A<\" start=\"4\" end=\"4\"/>\n"386 " <field name=\"B>\" start=\"3\" end=\"3\"/>\n"387 " <field name=\"C'\" start=\"2\" end=\"2\"/>\n"388 " <field name=\"D"\" start=\"1\" end=\"1\"/>\n"389 " <field name=\"E&\" start=\"0\" end=\"0\"/>\n"390 "</flags>\n");391 392 // Should include enumerators as the "type".393 strm.Clear();394 FieldEnum enum_single("enum_single", {{0, "a"}});395 RegisterFlags("Enumerators", 8,396 {RegisterFlags::Field("NoEnumerators", 4),397 RegisterFlags::Field("OneEnumerator", 3, 3, &enum_single)})398 .ToXML(strm);399 ASSERT_EQ(strm.GetString(),400 "<flags id=\"Enumerators\" size=\"8\">\n"401 " <field name=\"NoEnumerators\" start=\"4\" end=\"4\"/>\n"402 " <field name=\"OneEnumerator\" start=\"3\" end=\"3\" "403 "type=\"enum_single\"/>\n"404 "</flags>\n");405}406 407TEST(RegisterFlagsTest, EnumeratorToXML) {408 StreamString strm;409 410 FieldEnum::Enumerator(1234, "test").ToXML(strm);411 ASSERT_EQ(strm.GetString(), "<evalue name=\"test\" value=\"1234\"/>");412 413 // Special XML chars in names must be escaped.414 std::array special_names = {415 std::make_pair(FieldEnum::Enumerator(0, "A<"),416 "<evalue name=\"A<\" value=\"0\"/>"),417 std::make_pair(FieldEnum::Enumerator(1, "B>"),418 "<evalue name=\"B>\" value=\"1\"/>"),419 std::make_pair(FieldEnum::Enumerator(2, "C'"),420 "<evalue name=\"C'\" value=\"2\"/>"),421 std::make_pair(FieldEnum::Enumerator(3, "D\""),422 "<evalue name=\"D"\" value=\"3\"/>"),423 std::make_pair(FieldEnum::Enumerator(4, "E&"),424 "<evalue name=\"E&\" value=\"4\"/>"),425 };426 427 for (const auto &[enumerator, expected] : special_names) {428 strm.Clear();429 enumerator.ToXML(strm);430 ASSERT_EQ(strm.GetString(), expected);431 }432}433 434TEST(RegisterFlagsTest, EnumToXML) {435 StreamString strm;436 437 FieldEnum("empty_enum", {}).ToXML(strm, 4);438 ASSERT_EQ(strm.GetString(), "<enum id=\"empty_enum\" size=\"4\"/>\n");439 440 strm.Clear();441 FieldEnum("single_enumerator", {FieldEnum::Enumerator(0, "zero")})442 .ToXML(strm, 5);443 ASSERT_EQ(strm.GetString(), "<enum id=\"single_enumerator\" size=\"5\">\n"444 " <evalue name=\"zero\" value=\"0\"/>\n"445 "</enum>\n");446 447 strm.Clear();448 FieldEnum("multiple_enumerator",449 {FieldEnum::Enumerator(0, "zero"), FieldEnum::Enumerator(1, "one")})450 .ToXML(strm, 8);451 ASSERT_EQ(strm.GetString(), "<enum id=\"multiple_enumerator\" size=\"8\">\n"452 " <evalue name=\"zero\" value=\"0\"/>\n"453 " <evalue name=\"one\" value=\"1\"/>\n"454 "</enum>\n");455}456 457TEST(RegisterFlagsTest, EnumsToXML) {458 // This method should output all the enums used by the register flag set,459 // only once.460 461 StreamString strm;462 FieldEnum enum_a("enum_a", {FieldEnum::Enumerator(0, "zero")});463 FieldEnum enum_b("enum_b", {FieldEnum::Enumerator(1, "one")});464 FieldEnum enum_c("enum_c", {FieldEnum::Enumerator(2, "two")});465 llvm::StringSet<> seen;466 // Pretend that enum_c was already emitted for a different flag set.467 seen.insert("enum_c");468 469 RegisterFlags("Test", 4,470 {471 RegisterFlags::Field("f1", 31, 31, &enum_a),472 RegisterFlags::Field("f2", 30, 30, &enum_a),473 RegisterFlags::Field("f3", 29, 29, &enum_b),474 RegisterFlags::Field("f4", 27, 28, &enum_c),475 })476 .EnumsToXML(strm, seen);477 ASSERT_EQ(strm.GetString(), "<enum id=\"enum_a\" size=\"4\">\n"478 " <evalue name=\"zero\" value=\"0\"/>\n"479 "</enum>\n"480 "<enum id=\"enum_b\" size=\"4\">\n"481 " <evalue name=\"one\" value=\"1\"/>\n"482 "</enum>\n");483}