• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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