1 /** 2 * Copyright (c) 2021-2022 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16 #ifndef COMPILER_OPTIMIZER_OPTIMIZATIONS_COLOR_GRAPH_H 17 #define COMPILER_OPTIMIZER_OPTIMIZATIONS_COLOR_GRAPH_H 18 19 #include <algorithm> 20 #include <climits> 21 #include <iostream> 22 #include <unordered_set> 23 #include "compiler/optimizer/ir/constants.h" 24 #include "libpandabase/macros.h" 25 #include "libpandabase/utils/bit_utils.h" 26 #include "optimizer/code_generator/operands.h" 27 #include "utils/arena_containers.h" 28 #include "utils/small_vector.h" 29 #include "compiler_logger.h" 30 31 namespace panda::compiler { 32 class LifeIntervals; 33 34 class ColorNode { 35 public: 36 // Special color saying that color not in free registers but should be used on mapping 37 static constexpr uint16_t NO_BIAS = 0xffff; 38 39 template <typename T> ColorNode(unsigned number,T alloc)40 ColorNode(unsigned number, T alloc) : cs_point_set_(alloc), number_(number), physical_(), fixed_() 41 { 42 } 43 GetNumber()44 unsigned GetNumber() const noexcept 45 { 46 return number_; 47 } 48 SetColor(Register color)49 void SetColor(Register color) noexcept 50 { 51 color_ = color; 52 } 53 GetColor()54 Register GetColor() const noexcept 55 { 56 return color_; 57 } 58 SetBias(uint16_t bias)59 void SetBias(uint16_t bias) noexcept 60 { 61 bias_ = bias; 62 } 63 HasBias()64 bool HasBias() const noexcept 65 { 66 return bias_ != NO_BIAS; 67 } 68 GetBias()69 uint16_t GetBias() const noexcept 70 { 71 return bias_; 72 } 73 Assign(LifeIntervals * life_intervals)74 void Assign(LifeIntervals *life_intervals) noexcept 75 { 76 life_intervals_ = life_intervals; 77 } 78 IsFixedColor()79 bool IsFixedColor() const noexcept 80 { 81 return fixed_; 82 } 83 IsPhysical()84 bool IsPhysical() const noexcept 85 { 86 return physical_; 87 } 88 SetFixedColor(Register color,bool is_physical)89 void SetFixedColor(Register color, bool is_physical) noexcept 90 { 91 fixed_ = true; 92 physical_ = is_physical; 93 SetColor(color); 94 } 95 GetLifeIntervals()96 LifeIntervals *GetLifeIntervals() const noexcept 97 { 98 return life_intervals_; 99 } 100 AddCallsite(LifeNumber point)101 void AddCallsite(LifeNumber point) noexcept 102 { 103 cs_point_set_.insert(point); 104 } 105 GetCallsiteIntersectCount()106 unsigned GetCallsiteIntersectCount() const noexcept 107 { 108 return cs_point_set_.size(); 109 } 110 111 private: 112 LifeIntervals *life_intervals_ = nullptr; 113 ArenaUnorderedSet<LifeNumber> cs_point_set_; 114 unsigned number_; 115 uint16_t bias_ = NO_BIAS; // Bias is a group of nodes for coalescing 116 Register color_ = INVALID_REG; 117 uint8_t physical_ : 1; 118 uint8_t fixed_ : 1; 119 }; 120 121 class GraphMatrix { 122 public: GraphMatrix(ArenaAllocator * alloc)123 explicit GraphMatrix(ArenaAllocator *alloc) : matrix_(alloc->Adapter()) {} SetCapacity(unsigned capacity)124 void SetCapacity(unsigned capacity) 125 { 126 capacity_ = RoundUp(capacity, sizeof(uintptr_t)); 127 matrix_.clear(); 128 matrix_.resize(capacity_ * capacity_); 129 } 130 131 bool AddEdge(unsigned a, unsigned b); 132 HasEdge(unsigned a,unsigned b)133 bool HasEdge(unsigned a, unsigned b) const 134 { 135 return matrix_[FindEdge(a, b)]; 136 } 137 138 bool AddAffinityEdge(unsigned a, unsigned b); 139 HasAffinityEdge(unsigned a,unsigned b)140 bool HasAffinityEdge(unsigned a, unsigned b) const 141 { 142 return matrix_[FindAffinityEdge(a, b)]; 143 } 144 GetCapacity()145 unsigned GetCapacity() const noexcept 146 { 147 return capacity_; 148 } 149 150 private: 151 template <typename T> OrderNodes(T & a,T & b)152 void OrderNodes(T &a, T &b) const noexcept 153 { 154 ASSERT(a < capacity_); 155 ASSERT(b < capacity_); 156 if (a < b) { 157 std::swap(a, b); 158 } 159 } 160 FindEdge(unsigned a,unsigned b)161 unsigned FindEdge(unsigned a, unsigned b) const 162 { 163 // Placement in lower triangulated adjacency matrix 164 OrderNodes(a, b); 165 return a * capacity_ + b; 166 } 167 FindAffinityEdge(unsigned a,unsigned b)168 unsigned FindAffinityEdge(unsigned a, unsigned b) const 169 { 170 // Placement in lower triangulated adjacency matrix 171 OrderNodes(a, b); 172 return b * capacity_ + a; 173 } 174 175 ArenaVector<bool> matrix_; 176 unsigned capacity_ = 0; 177 }; 178 179 using NodeVector = ArenaVector<ColorNode>; 180 181 // TODO (Evgeny.Erokhin): In Appel's book described usage of 2 structures in parallel to hold interference: 182 // one is for random checks (here is a matrix) and lists on adjacency for sequental access. It's worth to add! 183 class InterferenceGraph { 184 public: InterferenceGraph(ArenaAllocator * alloc)185 explicit InterferenceGraph(ArenaAllocator *alloc) : nodes_(alloc->Adapter()), matrix_(alloc) {} 186 ColorNode *AllocNode(); 187 GetNodes()188 NodeVector &GetNodes() noexcept 189 { 190 return nodes_; 191 } 192 GetNodes()193 const NodeVector &GetNodes() const noexcept 194 { 195 return nodes_; 196 } 197 GetNode(unsigned num)198 ColorNode &GetNode(unsigned num) 199 { 200 return nodes_[num]; 201 } 202 GetNode(unsigned num)203 const ColorNode &GetNode(unsigned num) const 204 { 205 return nodes_[num]; 206 } 207 Size()208 unsigned Size() const noexcept 209 { 210 return nodes_.size(); 211 } 212 213 void Reserve(size_t count); 214 215 struct Bias { 216 unsigned callsites = 0; 217 Register color = INVALID_REG; 218 }; 219 AddBias()220 Bias &AddBias() noexcept 221 { 222 return biases_.emplace_back(); 223 } 224 UpdateBiasData(Bias * bias,const ColorNode & node)225 void UpdateBiasData(Bias *bias, const ColorNode &node) 226 { 227 ASSERT(bias != nullptr); 228 if (node.GetColor() != INVALID_REG) { 229 bias->color = node.GetColor(); 230 } 231 bias->callsites += node.GetCallsiteIntersectCount(); 232 } 233 GetBiasCount()234 unsigned GetBiasCount() const noexcept 235 { 236 return biases_.size(); 237 } 238 239 void AddEdge(unsigned a, unsigned b); 240 bool HasEdge(unsigned a, unsigned b) const; 241 void AddAffinityEdge(unsigned a, unsigned b); 242 bool HasAffinityEdge(unsigned a, unsigned b) const; 243 244 // Build LexBFS, so reverse order gives minimal coloring 245 ArenaVector<unsigned> LexBFS() const; 246 247 bool IsChordal() const; 248 void Dump(const std::string &name = "IG", bool skip_physical = true, std::ostream &out = std::cout) const; 249 250 template <unsigned MAX_COLORS> AssignColors(size_t colors,size_t callee_offset)251 Register AssignColors(size_t colors, size_t callee_offset) 252 { 253 ASSERT(colors <= MAX_COLORS); 254 std::bitset<MAX_COLORS> nbr_colors; 255 std::bitset<MAX_COLORS> nbr_bias_colors; 256 Register max_color = 0; 257 258 for (unsigned id = 0; id < Size(); id++) { 259 auto &node = GetNode(id); 260 261 // Skip colored 262 if (node.GetColor() != INVALID_REG) { 263 continue; 264 } 265 266 // Make busy colors maps 267 MakeBusyBitmap(id, &nbr_colors, &nbr_bias_colors); 268 269 // Try bias color first if free 270 size_t try_color; 271 if (node.HasBias() && biases_[node.GetBias()].color != INVALID_REG && 272 !nbr_colors[biases_[node.GetBias()].color]) { 273 try_color = biases_[node.GetBias()].color; 274 COMPILER_LOG(DEBUG, REGALLOC) << "Bias color chosen " << try_color; 275 } else { 276 // The best case is to find color that is not in neighbour colors and not in biased 277 nbr_bias_colors |= nbr_colors; // Make compound busy bitmap 278 279 // For nodes that take part in bias with callsites intersection, prefer callee saved registers. 280 // In cases first interval of bias isn't intersected it will be placed in callersaved register, 281 // that will affect entire coalesced bias group. 282 size_t off = 0; 283 if (node.HasBias() && biases_[node.GetBias()].callsites > 0) { 284 off = callee_offset; 285 } 286 287 // Find first not allocated disregard biasing 288 if ((try_color = FirstFree(nbr_colors, nbr_bias_colors, colors, off)) == colors) { 289 return 0; // No colors left 290 } 291 292 // Assign bias color if first observed in component 293 if (node.HasBias() && biases_[node.GetBias()].color == INVALID_REG) { 294 biases_[node.GetBias()].color = try_color; 295 COMPILER_LOG(DEBUG, REGALLOC) << "Set bias color " << try_color; 296 } 297 } 298 299 // Assign color 300 node.SetColor(try_color); 301 COMPILER_LOG(DEBUG, REGALLOC) << "Node " << node.GetNumber() << ": Set color: " 302 << " " << unsigned(node.GetColor()); 303 max_color = std::max<Register>(try_color, max_color); 304 } 305 306 return max_color + 1; 307 } 308 309 private: 310 template <typename T> MakeBusyBitmap(unsigned id,T * nbr_colors,T * nbr_bias_colors)311 void MakeBusyBitmap(unsigned id, T *nbr_colors, T *nbr_bias_colors) 312 { 313 nbr_colors->reset(); 314 nbr_bias_colors->reset(); 315 // Collect neighbors colors 316 for (unsigned nbr_id = 0; nbr_id < Size(); nbr_id++) { 317 auto &nbr_node = GetNode(nbr_id); 318 319 // Collect neighbour color 320 if (nbr_node.GetColor() != INVALID_REG && HasEdge(id, nbr_id)) { 321 ASSERT(nbr_node.GetColor() < nbr_colors->size()); 322 nbr_colors->set(nbr_node.GetColor()); 323 } else if (nbr_id != id && nbr_node.HasBias() && HasEdge(id, nbr_id)) { 324 // Collect biased neighbour color 325 ASSERT(nbr_node.GetBias() < GetBiasCount()); 326 if (biases_[nbr_node.GetBias()].color != INVALID_REG) { 327 nbr_bias_colors->set(biases_[nbr_node.GetBias()].color); 328 } 329 } 330 } 331 } 332 333 template <typename T> FirstFree(const T & nbr_bs,const T & nbr_bs_bias,size_t colors,size_t off)334 static size_t FirstFree(const T &nbr_bs, const T &nbr_bs_bias, size_t colors, size_t off) 335 { 336 // Find first free 337 size_t try_color; 338 for (try_color = off; try_color < colors + off; try_color++) { 339 if (!nbr_bs[try_color % colors]) { 340 break; 341 } 342 } 343 344 // Find free regarding biasing (higher part of bitmap) 345 for (auto i = try_color; i < colors + off; i++) { 346 if (!nbr_bs_bias[i % colors]) { 347 try_color = i; 348 break; 349 } 350 } 351 352 return try_color == colors + off ? colors : try_color % colors; 353 } 354 355 NodeVector nodes_; 356 GraphMatrix matrix_; 357 static const size_t DEFAULT_BIAS_SIZE = 16; 358 SmallVector<Bias, DEFAULT_BIAS_SIZE> biases_; 359 }; 360 } // namespace panda::compiler 361 362 #endif // COMPILER_OPTIMIZER_OPTIMIZATIONS_COLOR_GRAPH_H 363