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