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 #include "interference_graph.h"
17 #include <numeric>
18 #include "optimizer/analysis/liveness_analyzer.h"
19 
20 namespace panda::compiler {
AddEdge(unsigned a, unsigned b)21 bool GraphMatrix::AddEdge(unsigned a, unsigned b)
22 {
23     auto it = matrix_.begin() + FindEdge(a, b);
24     bool old_val = *it;
25     *it = true;
26     return old_val;
27 }
28 
AddAffinityEdge(unsigned a, unsigned b)29 bool GraphMatrix::AddAffinityEdge(unsigned a, unsigned b)
30 {
31     auto it = matrix_.begin() + FindAffinityEdge(a, b);
32     bool old_val = *it;
33     *it = true;
34     return old_val;
35 }
36 
37 // -------------------------------------------------------
38 
AllocNode()39 ColorNode *InterferenceGraph::AllocNode()
40 {
41     unsigned cur = nodes_.size();
42     nodes_.emplace_back(cur, nodes_.get_allocator());
43 
44     // Check matrix capacity
45     ASSERT(nodes_.size() <= matrix_.GetCapacity());
46 
47     return &nodes_.back();
48 }
49 
Reserve(size_t count)50 void InterferenceGraph::Reserve(size_t count)
51 {
52     nodes_.clear();
53     nodes_.reserve(count);
54     matrix_.SetCapacity(count);
55     biases_.clear();
56 }
57 
AddEdge(unsigned a, unsigned b)58 void InterferenceGraph::AddEdge(unsigned a, unsigned b)
59 {
60     matrix_.AddEdge(a, b);
61 }
62 
HasEdge(unsigned a, unsigned b) const63 bool InterferenceGraph::HasEdge(unsigned a, unsigned b) const
64 {
65     return matrix_.HasEdge(a, b);
66 }
67 
AddAffinityEdge(unsigned a, unsigned b)68 void InterferenceGraph::AddAffinityEdge(unsigned a, unsigned b)
69 {
70     matrix_.AddAffinityEdge(a, b);
71 }
72 
HasAffinityEdge(unsigned a, unsigned b) const73 bool InterferenceGraph::HasAffinityEdge(unsigned a, unsigned b) const
74 {
75     return matrix_.HasAffinityEdge(a, b);
76 }
77 
78 namespace {
79 constexpr size_t MIN_SIMPLITIAL_NODES = 3;
80 constexpr size_t DEFAULT_BOUNDARY_STACK = 16;
81 }  // namespace
82 
LexBFS() const83 ArenaVector<unsigned> InterferenceGraph::LexBFS() const
84 {
85     // Initialize out to sequentaly from 0
86     unsigned num = nodes_.size();
87     ArenaVector<unsigned> out(num, nodes_.get_allocator());
88     std::iota(out.begin(), out.end(), 0);
89 
90     // Less then 3 are all simplicial
91     if (out.size() < MIN_SIMPLITIAL_NODES) {
92         return out;
93     }
94 
95     // Control sub-sequences boundaries in stack maner
96     SmallVector<unsigned, DEFAULT_BOUNDARY_STACK> boundary_stack;
97     boundary_stack.reserve(num);
98     boundary_stack.push_back(num);  // Sentinel
99     unsigned pos = 0;               // Initialy we have set S of all elements
100 
101     while (true) {
102         ASSERT(pos < out.size());
103         auto id = out[pos];
104         pos++;
105 
106         // Check for boundaries colapse
107         ASSERT(!boundary_stack.empty());
108         auto prev_end = boundary_stack.back();
109         ASSERT(pos <= prev_end);
110         if (pos == prev_end) {
111             if (pos == num) {
112                 break;
113             }
114             boundary_stack.resize(boundary_stack.size() - 1);
115             ASSERT(!boundary_stack.empty());
116             prev_end = boundary_stack.back();
117         }
118 
119         // Partition on 2 groups: adjacent and not adjacent(last)
120         ASSERT(pos <= prev_end);
121         auto it = std::stable_partition(out.begin() + pos, out.begin() + prev_end,
122                                         [id, &out, this](unsigned val) { return HasEdge(id, out[val]); });
123         auto pivot = static_cast<unsigned>(std::distance(out.begin(), it));
124         // Split group if needed
125         if (pivot > pos && pivot != prev_end) {
126             boundary_stack.push_back(pivot);
127         }
128     }
129 
130     return out;
131 }
132 
133 namespace {
134 constexpr size_t DEFAULT_VECTOR_SIZE = 64;
135 }  // namespace
136 
137 bool InterferenceGraph::IsChordal() const
138 {
139     const auto &peo = LexBFS();
140     SmallVector<Register, DEFAULT_VECTOR_SIZE> processed_nbr;
141 
142     for (size_t i = 0; i < peo.size(); i++) {
143         processed_nbr.clear();
144 
145         // Collect processed neighbors
146         for (size_t j = 0; j < i; j++) {
147             if (HasEdge(peo[i], peo[j])) {
148                 processed_nbr.push_back(j);
149             }
150         }
151 
152         // Check that all processed neighbors in clique
153         for (auto nbr1 : processed_nbr) {
154             for (auto nbr2 : processed_nbr) {
155                 if (nbr1 != nbr2 && !HasEdge(peo[nbr1], peo[nbr2])) {
156                     return false;
157                 }
158             }
159         }
160     }
161 
162     return true;
163 }
164 
165 namespace {
166 const char *GetNodeShape(const InterferenceGraph &ig, unsigned i)
167 {
168     const char *shape = "ellipse";
169     if (ig.GetNode(i).IsPhysical()) {
170         shape = "box";
171     } else {
172         for (unsigned j = 0; j < ig.Size(); j++) {
173             if (i != j && ig.HasEdge(i, j) && ig.GetNode(j).IsPhysical()) {
174                 shape = "hexagon";
175                 break;
176             }
177         }
178     }
179     return shape;
180 }
181 }  // namespace
182 
183 void InterferenceGraph::Dump(const std::string &name, bool skip_physical, std::ostream &out) const
184 {
185     auto transformed_name = name;
186     std::replace(transformed_name.begin(), transformed_name.end(), ':', '_');
187     out << "Nodes: " << Size() << "\n\n"
188         << "\ngraph " << transformed_name << " {\nnode [colorscheme=spectral9]\n";
189     auto size = Size();
190     if (size == 0) {
191         out << "}\n";
192         return;
193     }
194 
195     // Map to colors
196     std::array<Register, std::numeric_limits<Register>::max()> colors {};
197     colors.fill(INVALID_REG);
198     Register cur_color = 0;
199 
200     for (auto &node : GetNodes()) {
201         if (!(skip_physical && node.IsPhysical()) && colors[node.GetColor()] == INVALID_REG) {
202             colors[node.GetColor()] = cur_color;
203             cur_color++;
204         }
205     }
206 
207     // Print header
208     for (unsigned i = 0; i < size; i++) {
209         if (skip_physical && GetNode(i).IsPhysical()) {
210             continue;
211         }
212         auto color = GetNode(i).GetColor();
213         out << i << " [color=" << unsigned(colors[color]) << ", xlabel=\"";
214         out << unsigned(color) << "\", tooltip=\"" << GetNode(i).GetLifeIntervals()->ToString<true>();
215         out << "\", shape=\"" << GetNodeShape(*this, i) << "\"]\n";
216     }
217 
218     auto edge_printer = [this, &out, skip_physical](auto node_num) {
219         for (unsigned j = 0; j < node_num; j++) {
220             if (!(skip_physical && GetNode(j).IsPhysical()) && HasEdge(node_num, j)) {
221                 if (GetNode(node_num).GetColor() == GetNode(j).GetColor() &&
222                     GetNode(node_num).GetColor() != INVALID_REG) {
223                     out << "Error: Same color\n";
224                 }
225                 out << node_num << "--" << j << "\n";
226             }
227         }
228     };
229 
230     // Print edges
231     for (unsigned i = 1; i < size; i++) {
232         if (skip_physical && GetNode(i).IsPhysical()) {
233             continue;
234         }
235         edge_printer(i);
236     }
237 
238     out << "}\n";
239 }
240 }  // namespace panda::compiler
241