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