1 //===-- RegisterFlags.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 
12 #include <numeric>
13 #include <optional>
14 
15 using namespace lldb_private;
16 
17 void RegisterFlags::Field::log(Log *log) const {
18   LLDB_LOG(log, "  Name: \"{0}\" Start: {1} End: {2}", m_name.c_str(), m_start,
19            m_end);
20 }
21 
22 bool RegisterFlags::Field::Overlaps(const Field &other) const {
23   unsigned overlap_start = std::max(GetStart(), other.GetStart());
24   unsigned overlap_end = std::min(GetEnd(), other.GetEnd());
25   return overlap_start <= overlap_end;
26 }
27 
28 unsigned RegisterFlags::Field::PaddingDistance(const Field &other) const {
29   assert(!Overlaps(other) &&
30          "Cannot get padding distance for overlapping fields.");
31   assert((other < (*this)) && "Expected fields in MSB to LSB order.");
32 
33   // If they don't overlap they are either next to each other or separated
34   // by some number of bits.
35 
36   // Where left will be the MSB and right will be the LSB.
37   unsigned lhs_start = GetStart();
38   unsigned rhs_end = other.GetStart() + other.GetSizeInBits() - 1;
39 
40   if (*this < other) {
41     lhs_start = other.GetStart();
42     rhs_end = GetStart() + GetSizeInBits() - 1;
43   }
44 
45   return lhs_start - rhs_end - 1;
46 }
47 
48 RegisterFlags::RegisterFlags(std::string id, unsigned size,
49                              const std::vector<Field> &fields)
50     : m_id(std::move(id)), m_size(size) {
51   // We expect that the XML processor will discard anything describing flags but
52   // with no fields.
53   assert(fields.size() && "Some fields must be provided.");
54 
55   // We expect that these are unsorted but do not overlap.
56   // They could fill the register but may have gaps.
57   std::vector<Field> provided_fields = fields;
58   m_fields.reserve(provided_fields.size());
59 
60   // ProcessGDBRemote should have sorted these in descending order already.
61   assert(std::is_sorted(provided_fields.rbegin(), provided_fields.rend()));
62 
63   // Build a new list of fields that includes anonymous (empty name) fields
64   // wherever there is a gap. This will simplify processing later.
65   std::optional<Field> previous_field;
66   unsigned register_msb = (size * 8) - 1;
67   for (auto field : provided_fields) {
68     if (previous_field) {
69       unsigned padding = previous_field->PaddingDistance(field);
70       if (padding) {
71         // -1 to end just before the previous field.
72         unsigned end = previous_field->GetStart() - 1;
73         // +1 because if you want to pad 1 bit you want to start and end
74         // on the same bit.
75         m_fields.push_back(Field("", field.GetEnd() + 1, end));
76       }
77     } else {
78       // This is the first field. Check that it starts at the register's MSB.
79       if (field.GetEnd() != register_msb)
80         m_fields.push_back(Field("", field.GetEnd() + 1, register_msb));
81     }
82     m_fields.push_back(field);
83     previous_field = field;
84   }
85 
86   // The last field may not extend all the way to bit 0.
87   if (previous_field && previous_field->GetStart() != 0)
88     m_fields.push_back(Field("", 0, previous_field->GetStart() - 1));
89 }
90 
91 void RegisterFlags::log(Log *log) const {
92   LLDB_LOG(log, "ID: \"{0}\" Size: {1}", m_id.c_str(), m_size);
93   for (const Field &field : m_fields)
94     field.log(log);
95 }
96 
97 static StreamString FormatCell(const StreamString &content,
98                                unsigned column_width) {
99   unsigned pad = column_width - content.GetString().size();
100   std::string pad_l;
101   std::string pad_r;
102   if (pad) {
103     pad_l = std::string(pad / 2, ' ');
104     pad_r = std::string((pad / 2) + (pad % 2), ' ');
105   }
106 
107   StreamString aligned;
108   aligned.Printf("|%s%s%s", pad_l.c_str(), content.GetString().data(),
109                  pad_r.c_str());
110   return aligned;
111 }
112 
113 static void EmitTable(std::string &out, std::array<std::string, 3> &table) {
114   // Close the table.
115   for (std::string &line : table)
116     line += '|';
117 
118   out += std::accumulate(table.begin() + 1, table.end(), table.front(),
119                          [](std::string lhs, const auto &rhs) {
120                            return std::move(lhs) + "\n" + rhs;
121                          });
122 }
123 
124 std::string RegisterFlags::AsTable(uint32_t max_width) const {
125   std::string table;
126   // position / gridline / name
127   std::array<std::string, 3> lines;
128   uint32_t current_width = 0;
129 
130   for (const RegisterFlags::Field &field : m_fields) {
131     StreamString position;
132     if (field.GetEnd() == field.GetStart())
133       position.Printf(" %d ", field.GetEnd());
134     else
135       position.Printf(" %d-%d ", field.GetEnd(), field.GetStart());
136 
137     StreamString name;
138     name.Printf(" %s ", field.GetName().c_str());
139 
140     unsigned column_width = position.GetString().size();
141     unsigned name_width = name.GetString().size();
142     if (name_width > column_width)
143       column_width = name_width;
144 
145     // If the next column would overflow and we have already formatted at least
146     // one column, put out what we have and move to a new table on the next line
147     // (+1 here because we need to cap the ends with '|'). If this is the first
148     // column, just let it overflow and we'll wrap next time around. There's not
149     // much we can do with a very small terminal.
150     if (current_width && ((current_width + column_width + 1) >= max_width)) {
151       EmitTable(table, lines);
152       // Blank line between each.
153       table += "\n\n";
154 
155       for (std::string &line : lines)
156         line.clear();
157       current_width = 0;
158     }
159 
160     StreamString aligned_position = FormatCell(position, column_width);
161     lines[0] += aligned_position.GetString();
162     StreamString grid;
163     grid << '|' << std::string(column_width, '-');
164     lines[1] += grid.GetString();
165     StreamString aligned_name = FormatCell(name, column_width);
166     lines[2] += aligned_name.GetString();
167 
168     // +1 for the left side '|'.
169     current_width += column_width + 1;
170   }
171 
172   // If we didn't overflow and still have table to print out.
173   if (lines[0].size())
174     EmitTable(table, lines);
175 
176   return table;
177 }
178