1 /* 2 * Copyright (c) 2021-2024 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 "compiler_logger.h" 22 #include "compiler/optimizer/ir/constants.h" 23 #include "compiler/optimizer/analysis/liveness_analyzer.h" 24 #include <iostream> 25 #include "libpandabase/macros.h" 26 #include "libpandabase/utils/bit_utils.h" 27 #include "optimizer/code_generator/operands.h" 28 #include <unordered_set> 29 #include "utils/arena_containers.h" 30 #include "utils/small_vector.h" 31 32 namespace ark::compiler { 33 class LifeIntervals; 34 35 constexpr size_t CALLEE_THRESHOLD = 2; 36 37 class ColorNode { 38 public: 39 // Special color saying that color not in free registers but should be used on mapping 40 static constexpr uint16_t NO_BIAS = 0xffff; 41 42 template <typename T> ColorNode(unsigned number,T alloc)43 ColorNode(unsigned number, T alloc) : csPointSet_(alloc), number_(number) 44 { 45 } 46 GetNumber()47 unsigned GetNumber() const noexcept 48 { 49 return number_; 50 } 51 SetColor(Register color)52 void SetColor(Register color) noexcept 53 { 54 color_ = color; 55 } 56 GetColor()57 Register GetColor() const noexcept 58 { 59 return color_; 60 } 61 SetBias(uint16_t bias)62 void SetBias(uint16_t bias) noexcept 63 { 64 bias_ = bias; 65 } 66 HasBias()67 bool HasBias() const noexcept 68 { 69 return bias_ != NO_BIAS; 70 } 71 GetBias()72 uint16_t GetBias() const noexcept 73 { 74 return bias_; 75 } 76 Assign(LifeIntervals * lifeIntervals)77 void Assign(LifeIntervals *lifeIntervals) noexcept 78 { 79 lifeIntervals_ = lifeIntervals; 80 } 81 IsFixedColor()82 bool IsFixedColor() const noexcept 83 { 84 return fixed_; 85 } 86 SetPhysical()87 void SetPhysical() noexcept 88 { 89 physical_ = true; 90 } 91 IsPhysical()92 bool IsPhysical() const noexcept 93 { 94 return physical_; 95 } 96 SetFixedColor(Register color)97 void SetFixedColor(Register color) noexcept 98 { 99 fixed_ = true; 100 SetColor(color); 101 } 102 GetLifeIntervals()103 LifeIntervals *GetLifeIntervals() const noexcept 104 { 105 return lifeIntervals_; 106 } 107 AddCallsite(LifeNumber point)108 void AddCallsite(LifeNumber point) noexcept 109 { 110 csPointSet_.insert(point); 111 } 112 GetCallsiteIntersectCount()113 unsigned GetCallsiteIntersectCount() const noexcept 114 { 115 return csPointSet_.size(); 116 } 117 SetSpillWeight(float weight)118 void SetSpillWeight(float weight) 119 { 120 weight_ = weight; 121 } 122 GetSpillWeight()123 float GetSpillWeight() const 124 { 125 return weight_; 126 } 127 128 private: 129 LifeIntervals *lifeIntervals_ = nullptr; 130 ArenaUnorderedSet<LifeNumber> csPointSet_; 131 unsigned number_; 132 float weight_ {}; 133 uint16_t bias_ = NO_BIAS; // Bias is a group of nodes for coalescing 134 Register color_ = GetInvalidReg(); 135 bool physical_ {}; 136 bool fixed_ {}; 137 }; 138 139 class GraphMatrix { 140 public: GraphMatrix(ArenaAllocator * alloc)141 explicit GraphMatrix(ArenaAllocator *alloc) : matrix_(alloc->Adapter()), amatrix_(alloc->Adapter()) {} SetCapacity(unsigned capacity)142 void SetCapacity(unsigned capacity) 143 { 144 capacity_ = RoundUp(capacity, sizeof(uintptr_t)); 145 matrix_.clear(); 146 matrix_.resize(capacity_ * capacity_); 147 amatrix_.clear(); 148 amatrix_.resize(capacity_ * capacity_); 149 } 150 AddEdge(unsigned a,unsigned b)151 bool AddEdge(unsigned a, unsigned b) 152 { 153 auto it = matrix_.begin() + a * capacity_ + b; 154 bool oldVal = (*it != 0U); 155 *it = 1U; 156 auto it1 = matrix_.begin() + b * capacity_ + a; 157 *it1 = 1U; 158 return oldVal; 159 } 160 AddAffinityEdge(unsigned a,unsigned b)161 bool AddAffinityEdge(unsigned a, unsigned b) 162 { 163 auto it = amatrix_.begin() + a * capacity_ + b; 164 bool oldVal = (*it != 0U); 165 *it = 1U; 166 auto it1 = amatrix_.begin() + b * capacity_ + a; 167 *it1 = 1U; 168 return oldVal; 169 } 170 HasEdge(unsigned a,unsigned b)171 bool HasEdge(unsigned a, unsigned b) const 172 { 173 return matrix_[a * capacity_ + b] != 0U; 174 } 175 HasAffinityEdge(unsigned a,unsigned b)176 bool HasAffinityEdge(unsigned a, unsigned b) const 177 { 178 return amatrix_[a * capacity_ + b] != 0U; 179 } 180 GetCapacity()181 unsigned GetCapacity() const noexcept 182 { 183 return capacity_; 184 } 185 186 private: 187 ArenaVector<uint8_t> matrix_; 188 ArenaVector<uint8_t> amatrix_; 189 unsigned capacity_ = 0; 190 }; 191 192 using NodeVector = ArenaVector<ColorNode>; 193 194 // NOTE (Evgeny.Erokhin): In Appel's book described usage of 2 structures in parallel to hold interference: 195 // one is for random checks (here is a matrix) and lists on adjacency for sequental access. It's worth to add! 196 class InterferenceGraph { 197 public: InterferenceGraph(ArenaAllocator * alloc)198 explicit InterferenceGraph(ArenaAllocator *alloc) : nodes_(alloc->Adapter()), matrix_(alloc), useSpillWeight_() {} 199 ColorNode *AllocNode(); 200 GetNodes()201 NodeVector &GetNodes() noexcept 202 { 203 return nodes_; 204 } 205 GetNodes()206 const NodeVector &GetNodes() const noexcept 207 { 208 return nodes_; 209 } 210 GetNode(unsigned num)211 ColorNode &GetNode(unsigned num) 212 { 213 return nodes_[num]; 214 } 215 GetNode(unsigned num)216 const ColorNode &GetNode(unsigned num) const 217 { 218 return nodes_[num]; 219 } 220 FindNode(const LifeIntervals * li)221 const ColorNode *FindNode(const LifeIntervals *li) const 222 { 223 auto it = std::find_if(nodes_.begin(), nodes_.end(), 224 [li](const auto &node) { return li == node.GetLifeIntervals(); }); 225 226 return it != nodes_.end() ? &*it : nullptr; 227 } 228 FindPhysicalNode(Location location)229 const ColorNode *FindPhysicalNode(Location location) const 230 { 231 auto it = std::find_if(nodes_.begin(), nodes_.end(), [location](const auto &node) { 232 return node.IsPhysical() && node.GetLifeIntervals()->GetLocation() == location; 233 }); 234 235 return it != nodes_.end() ? &*it : nullptr; 236 } 237 Size()238 unsigned Size() const noexcept 239 { 240 return nodes_.size(); 241 } 242 243 void Reserve(size_t count); 244 245 struct Bias { 246 unsigned callsites = 0; 247 Register color = GetInvalidReg(); 248 }; 249 AddBias()250 Bias &AddBias() noexcept 251 { 252 return biases_.emplace_back(); 253 } 254 UpdateBiasData(Bias * bias,const ColorNode & node)255 void UpdateBiasData(Bias *bias, const ColorNode &node) 256 { 257 ASSERT(bias != nullptr); 258 if (node.GetColor() != GetInvalidReg()) { 259 bias->color = node.GetColor(); 260 } 261 bias->callsites += node.GetCallsiteIntersectCount(); 262 } 263 GetBiasCount()264 unsigned GetBiasCount() const noexcept 265 { 266 return biases_.size(); 267 } 268 269 void AddEdge(unsigned a, unsigned b); 270 bool HasEdge(unsigned a, unsigned b) const; 271 void AddAffinityEdge(unsigned a, unsigned b); 272 bool HasAffinityEdge(unsigned a, unsigned b) const; 273 274 // Build LexBFS, so reverse order gives minimal coloring 275 ArenaVector<unsigned> LexBFS() const; 276 277 // Get nodes ids in order they will be colored 278 ArenaVector<unsigned> GetOrderedNodesIds() const; 279 SetUseSpillWeight(bool use)280 void SetUseSpillWeight(bool use) 281 { 282 useSpillWeight_ = use; 283 } 284 IsUsedSpillWeight()285 bool IsUsedSpillWeight() const 286 { 287 return useSpillWeight_; 288 } 289 290 bool IsChordal() const; 291 void Dump(const std::string &name = "IG", bool skipPhysical = true, std::ostream &out = std::cout) const; 292 293 template <unsigned MAX_COLORS> AssignColors(size_t colors,size_t calleeOffset)294 bool AssignColors(size_t colors, size_t calleeOffset) 295 { 296 ASSERT(colors <= MAX_COLORS); 297 std::bitset<MAX_COLORS> nbrColors; 298 std::bitset<MAX_COLORS> nbrBiasColors; 299 300 bool success = true; 301 size_ = Size(); 302 for (unsigned id : GetOrderedNodesIds()) { 303 auto &node = GetNode(id); 304 COMPILER_LOG(DEBUG, REGALLOC) 305 << "Visit Node " << node.GetNumber() << "; LI: " << node.GetLifeIntervals()->ToString() 306 << "; SW: " << node.GetSpillWeight(); 307 // Skip colored 308 if (node.GetColor() != GetInvalidReg()) { 309 continue; 310 } 311 312 // Make busy colors maps 313 MakeBusyBitmap(id, &nbrColors, &nbrBiasColors); 314 315 // Try bias color first if free 316 size_t tryColor; 317 if (node.HasBias() && biases_[node.GetBias()].color != GetInvalidReg() && 318 !nbrColors[biases_[node.GetBias()].color]) { 319 tryColor = biases_[node.GetBias()].color; 320 COMPILER_LOG(DEBUG, REGALLOC) << "Bias color chosen " << tryColor; 321 } else { 322 // The best case is to find color that is not in neighbour colors and not in biased 323 nbrBiasColors |= nbrColors; // Make compound busy bitmap 324 325 // For nodes that take part in bias with callsites intersection, prefer callee saved registers. 326 // In cases first interval of bias isn't intersected it will be placed in callersaved register, 327 // that will affect entire coalesced bias group. 328 size_t off = 0; 329 if (node.HasBias() && biases_[node.GetBias()].callsites > 0) { 330 off = calleeOffset; 331 } 332 333 // Find first not allocated disregard biasing 334 if ((tryColor = FirstFree(nbrColors, nbrBiasColors, colors, off)) == colors) { 335 node.SetColor(tryColor); 336 success = false; 337 continue; 338 } 339 340 // Assign bias color if first observed in component 341 if (node.HasBias() && biases_[node.GetBias()].color == GetInvalidReg()) { 342 biases_[node.GetBias()].color = tryColor; 343 COMPILER_LOG(DEBUG, REGALLOC) << "Set bias color " << tryColor; 344 } 345 } 346 347 // Assign color 348 node.SetColor(tryColor); 349 COMPILER_LOG(DEBUG, REGALLOC) << "Node " << node.GetNumber() << ": Set color: " 350 << " " << unsigned(node.GetColor()); 351 } 352 return success; 353 } 354 355 private: 356 template <size_t SIZE> 357 bool CheckNeighborsInClique(const ArenaVector<unsigned> &peo, SmallVector<Register, SIZE> &processedNbr) const; 358 359 template <typename T> MakeBusyBitmap(unsigned id,T * nbrColors,T * nbrBiasColors)360 void MakeBusyBitmap(unsigned id, T *nbrColors, T *nbrBiasColors) 361 { 362 nbrColors->reset(); 363 nbrBiasColors->reset(); 364 // Collect neighbors colors 365 for (unsigned nbrId = 0; nbrId < size_; nbrId++) { 366 auto &nbrNode = GetNode(nbrId); 367 368 // Collect neighbour color 369 if (nbrNode.GetColor() != GetInvalidReg() && HasEdge(id, nbrId)) { 370 ASSERT(nbrNode.GetColor() < nbrColors->size()); 371 nbrColors->set(nbrNode.GetColor()); 372 } else if (nbrId != id && nbrNode.HasBias() && HasEdge(id, nbrId)) { 373 // Collect biased neighbour color 374 ASSERT(nbrNode.GetBias() < GetBiasCount()); 375 if (biases_[nbrNode.GetBias()].color != GetInvalidReg()) { 376 nbrBiasColors->set(biases_[nbrNode.GetBias()].color); 377 } 378 } 379 } 380 } 381 382 template <typename T> FirstFree(const T & nbrBs,const T & nbrBsBias,size_t colors,size_t off)383 static size_t FirstFree(const T &nbrBs, const T &nbrBsBias, size_t colors, size_t off) 384 { 385 // Find first free 386 size_t tryColor; 387 for (tryColor = off; tryColor < colors + off; tryColor++) { 388 if (!nbrBs[tryColor % colors]) { 389 break; 390 } 391 } 392 393 // Find free regarding biasing (higher part of bitmap) 394 for (auto i = tryColor; i < colors + off; i++) { 395 if (!nbrBsBias[i % colors]) { 396 tryColor = i; 397 break; 398 } 399 } 400 401 return tryColor == colors + off ? colors : tryColor % colors; 402 } 403 404 NodeVector nodes_; 405 GraphMatrix matrix_; 406 static const size_t DEFAULT_BIAS_SIZE = 16; 407 SmallVector<Bias, DEFAULT_BIAS_SIZE> biases_; 408 uint8_t useSpillWeight_ : 1; 409 size_t size_ {0}; 410 }; 411 } // namespace ark::compiler 412 413 #endif // COMPILER_OPTIMIZER_OPTIMIZATIONS_COLOR_GRAPH_H 414