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