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 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 25 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 30 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 36 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 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 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 105 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 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 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 138 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 193 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 216 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