xref: /llvm-project/lldb/unittests/Target/RegisterFlagsTest.cpp (revision e87f94a6a806a941242506680f88573d6a87a828)
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-exception
6 //
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 
14 using namespace lldb_private;
15 using namespace lldb;
16 
17 TEST(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 1
29   // 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/down
37   // 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 
51 static RegisterFlags::Field make_field(unsigned start, unsigned end) {
52   return RegisterFlags::Field("", start, end);
53 }
54 
55 static RegisterFlags::Field make_field(unsigned bit) {
56   return RegisterFlags::Field("", bit);
57 }
58 
59 TEST(RegisterFlagsTest, FieldOverlaps) {
60   // Single bit fields
61   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 
74 TEST(RegisterFlagsTest, PaddingDistance) {
75   // We assume that this method is always called with a more significant
76   // (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 
88 static 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 
94 TEST(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 padding
101   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 
130 TEST(RegisterFieldsTest, ReverseFieldOrder) {
131   // Unchanged
132   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 
145 TEST(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 user
207   // 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 
263 TEST(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 1
286                 // 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 width
314   // is set by the size of the fields name (as opposed to some max of all field
315   // 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 
344 TEST(RegisterFieldsTest, FlagsToXML) {
345   StreamString strm;
346 
347   // RegisterFlags requires that some fields be given, so no testing of empty
348   // input.
349 
350   // Unnamed fields are padding that are ignored. This applies to fields passed
351   // 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&lt;\" start=\"4\" end=\"4\"/>\n"
386             "  <field name=\"B&gt;\" start=\"3\" end=\"3\"/>\n"
387             "  <field name=\"C&apos;\" start=\"2\" end=\"2\"/>\n"
388             "  <field name=\"D&quot;\" start=\"1\" end=\"1\"/>\n"
389             "  <field name=\"E&amp;\" 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 
407 TEST(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&lt;\" value=\"0\"/>"),
417       std::make_pair(FieldEnum::Enumerator(1, "B>"),
418                      "<evalue name=\"B&gt;\" value=\"1\"/>"),
419       std::make_pair(FieldEnum::Enumerator(2, "C'"),
420                      "<evalue name=\"C&apos;\" value=\"2\"/>"),
421       std::make_pair(FieldEnum::Enumerator(3, "D\""),
422                      "<evalue name=\"D&quot;\" value=\"3\"/>"),
423       std::make_pair(FieldEnum::Enumerator(4, "E&"),
424                      "<evalue name=\"E&amp;\" 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 
434 TEST(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 
457 TEST(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 }