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 "reg_alloc_graph_coloring.h"
17 #include "reg_type.h"
18
19 namespace panda::compiler {
RegAllocGraphColoring(Graph * graph)20 RegAllocGraphColoring::RegAllocGraphColoring(Graph *graph) : RegAllocBase(graph) {}
RegAllocGraphColoring(Graph * graph,size_t regs_count)21 RegAllocGraphColoring::RegAllocGraphColoring(Graph *graph, size_t regs_count) : RegAllocBase(graph, regs_count) {}
22
BuildIG(InterferenceGraph * ig,WorkingRanges * ranges)23 void RegAllocGraphColoring::BuildIG(InterferenceGraph *ig, WorkingRanges *ranges)
24 {
25 ig->Reserve(ranges->regular.size() + ranges->physical.size());
26 ArenaDeque<ColorNode *> active_nodes(GetGraph()->GetLocalAllocator()->Adapter());
27 ArenaVector<ColorNode *> physical_nodes(GetGraph()->GetLocalAllocator()->Adapter());
28
29 for (auto physical_interval : ranges->physical) {
30 ColorNode *node = ig->AllocNode();
31 node->Assign(physical_interval);
32 physical_nodes.push_back(node);
33 }
34
35 for (auto current_interval : ranges->regular) {
36 auto range_start = current_interval->GetBegin();
37
38 // Expire active_ranges
39 while (!active_nodes.empty() && active_nodes.front()->GetLifeIntervals()->GetEnd() <= range_start) {
40 active_nodes.pop_front();
41 }
42
43 ColorNode *node = ig->AllocNode();
44 node->Assign(current_interval);
45
46 // Interfer node
47 for (auto active_node : active_nodes) {
48 auto active_interval = active_node->GetLifeIntervals();
49 if (current_interval->IntersectsWith(active_interval)) {
50 ig->AddEdge(node->GetNumber(), active_node->GetNumber());
51 }
52 }
53
54 for (auto physical_node : physical_nodes) {
55 auto physical_interval = physical_node->GetLifeIntervals();
56 auto intersection = current_interval->GetFirstIntersectionWith(physical_interval);
57 // Current interval can intersect the physical one at the beginning of its live range
58 // only if it's a call and physical interval's range was created for it.
59 // Try to find first intersection excluding the range blocking registers during a call.
60 if (intersection == current_interval->GetBegin()) {
61 intersection = current_interval->GetFirstIntersectionWith(physical_interval, intersection + 1U);
62 }
63
64 if (intersection != INVALID_LIFE_NUMBER) {
65 ig->AddEdge(node->GetNumber(), physical_node->GetNumber());
66 node->AddCallsite(range_start);
67 }
68 }
69
70 // Add node to active_nodes sorted by End time
71 auto ranges_iter =
72 std::upper_bound(active_nodes.begin(), active_nodes.end(), node, [](const auto &lhs, const auto &rhs) {
73 return lhs->GetLifeIntervals()->GetEnd() <= rhs->GetLifeIntervals()->GetEnd();
74 });
75 active_nodes.insert(ranges_iter, node);
76 }
77 }
78
79 namespace {
FindFixedNode(const NodeVector & nodes,Location location)80 const ColorNode *FindFixedNode(const NodeVector &nodes, Location location)
81 {
82 // Find node of input
83 auto it = std::find_if(nodes.begin(), nodes.end(), [location](const auto &el) {
84 auto liveness = el.GetLifeIntervals();
85 return liveness->IsPhysical() && liveness->GetLocation() == location;
86 });
87
88 return it != nodes.end() ? &*it : nullptr;
89 }
90
FindNode(const NodeVector & nodes,const Inst * inst)91 const ColorNode *FindNode(const NodeVector &nodes, const Inst *inst)
92 {
93 // Find node of input
94 auto it = std::find_if(nodes.begin(), nodes.end(), [inst](const auto &el) {
95 auto liveness = el.GetLifeIntervals();
96 return !liveness->IsPhysical() && liveness->GetInst() == inst && liveness->GetSibling() == nullptr;
97 });
98
99 return it != nodes.end() ? &*it : nullptr;
100 }
101 } // namespace
102
103 // Find precolorings and set registers to intervals in advance
PrecolorIG(InterferenceGraph * ig,const RegisterMap & map)104 RegAllocGraphColoring::IndexVector RegAllocGraphColoring::PrecolorIG(InterferenceGraph *ig, const RegisterMap &map)
105 {
106 auto &nodes = ig->GetNodes();
107
108 // Walk nodes and propagate properties
109 IndexVector affinity_nodes;
110 for (auto &node : nodes) {
111 const auto *interv = node.GetLifeIntervals();
112
113 // Take in account preassigned registers in intervals
114 if (interv->IsPhysical() || interv->IsPreassigned()) {
115 ASSERT(node.GetCallsiteIntersectCount() == 0);
116 // Translate preassigned register from interval to color graph
117 auto color = map.CodegenToRegallocReg(interv->GetReg());
118 node.SetFixedColor(color, interv->IsPhysical());
119 AddAffinityEdgeToSibling(ig, &node, &affinity_nodes);
120 continue;
121 }
122
123 const auto *inst = interv->GetInst();
124 ASSERT(inst != nullptr);
125 if (inst->IsPhi()) {
126 // TODO (Evgeny.Erokhin): Add precoloring for the node which is input for return if callsites == 0
127 // Add inputs to affinity subgraph
128 AddAffinityEdges(ig, &node, &affinity_nodes);
129 continue;
130 }
131
132 // Add affinity edges to fixed locations
133 for (auto i = 0U; i < inst->GetInputsCount(); i++) {
134 auto location = inst->GetLocation(i);
135 if (location.IsFixedRegister()) {
136 auto input_node = FindNode(nodes, inst->GetDataFlowInput(i));
137 auto fixed_node = FindFixedNode(nodes, location);
138 // Possible when general intervals are processing, while input is fp-interval or vice versa
139 if (input_node == nullptr || fixed_node == nullptr) {
140 continue;
141 }
142 affinity_nodes.push_back(input_node->GetNumber());
143 affinity_nodes.push_back(fixed_node->GetNumber());
144 ig->AddAffinityEdge(input_node->GetNumber(), fixed_node->GetNumber());
145 }
146 }
147 }
148
149 return affinity_nodes;
150 }
151
BuildBias(InterferenceGraph * ig,const IndexVector & affinity_nodes)152 void RegAllocGraphColoring::BuildBias(InterferenceGraph *ig, const IndexVector &affinity_nodes)
153 {
154 auto &nodes = ig->GetNodes();
155
156 // Build affinity connected-components UCC(Unilaterally Connected Components) for coalescing (assign bias number to
157 // nodes of same component)
158 SmallVector<unsigned, DEFAULT_VECTOR_SIZE> walked;
159 for (auto index : affinity_nodes) {
160 auto &node = nodes[index];
161
162 // Skip already biased
163 if (node.HasBias()) {
164 continue;
165 }
166
167 // Find connected component of graph UCC by (DFS), and collect Call-sites intersections
168 walked.clear();
169 walked.push_back(node.GetNumber());
170 unsigned bias_num = ig->GetBiasCount();
171 node.SetBias(bias_num);
172 auto &bias = ig->AddBias();
173 ig->UpdateBiasData(&bias, node);
174 do {
175 // Pop back
176 unsigned cur_index = walked.back();
177 walked.resize(walked.size() - 1);
178
179 // Walk N affine nodes
180 for (auto try_index : affinity_nodes) {
181 auto &try_node = nodes[try_index];
182 if (try_node.HasBias() || !ig->HasAffinityEdge(cur_index, try_index)) {
183 continue;
184 }
185 try_node.SetBias(bias_num);
186 ig->UpdateBiasData(&bias, try_node);
187 walked.push_back(try_index);
188 }
189 } while (!walked.empty());
190 }
191
192 // TODO (Evgeny.Erokhin): Add precoloring for nodes that have Use in call argument but but callsites == 0
193 }
194
AddAffinityEdges(InterferenceGraph * ig,ColorNode * node,IndexVector * affinity_nodes)195 void RegAllocGraphColoring::AddAffinityEdges(InterferenceGraph *ig, ColorNode *node, IndexVector *affinity_nodes)
196 {
197 auto &nodes = ig->GetNodes();
198
199 // Duplicates are possible but we tolerate it
200 affinity_nodes->push_back(node->GetNumber());
201
202 // Iterate over Phi inputs
203 SmallVector<unsigned, DEFAULT_VECTOR_SIZE> affine;
204 for (const auto &input : node->GetLifeIntervals()->GetInst()->GetInputs()) {
205 // Add affinity edge
206 if (const auto *anbr = FindNode(nodes, input.GetInst())) {
207 COMPILER_LOG(DEBUG, REGALLOC) << "AfEdge: " << node->GetNumber() << " " << anbr->GetNumber() << " "
208 << *anbr->GetLifeIntervals()->GetInst();
209 ig->AddAffinityEdge(node->GetNumber(), anbr->GetNumber());
210 affinity_nodes->push_back(anbr->GetNumber());
211 }
212 }
213 }
214
AddAffinityEdgeToSibling(InterferenceGraph * ig,ColorNode * node,IndexVector * affinity_nodes)215 void RegAllocGraphColoring::AddAffinityEdgeToSibling(InterferenceGraph *ig, ColorNode *node,
216 IndexVector *affinity_nodes)
217 {
218 const auto *interv = node->GetLifeIntervals();
219 if (interv->IsPhysical() || interv->GetSibling() == nullptr) {
220 return;
221 }
222 auto node_split = FindNode(ig->GetNodes(), interv->GetInst());
223 ASSERT(node_split != nullptr);
224 COMPILER_LOG(DEBUG, REGALLOC) << "AfEdge: " << node->GetNumber() << " " << node_split->GetNumber() << " "
225 << *node_split->GetLifeIntervals()->GetInst();
226 ig->AddAffinityEdge(node->GetNumber(), node_split->GetNumber());
227 affinity_nodes->push_back(node->GetNumber());
228 affinity_nodes->push_back(node_split->GetNumber());
229 }
230
AllocateRegisters(InterferenceGraph * ig,WorkingRanges * ranges,const RegisterMap & map)231 Register RegAllocGraphColoring::AllocateRegisters(InterferenceGraph *ig, WorkingRanges *ranges, const RegisterMap &map)
232 {
233 Presplit(ranges);
234 // Build and precolor IG
235 BuildIG(ig, ranges);
236 BuildBias(ig, PrecolorIG(ig, map));
237
238 #ifdef NDEBUG
239 if (!ig->IsChordal()) {
240 COMPILER_LOG(WARNING, REGALLOC) << "Nonchordal graph: nonoptimal coloring possible";
241 }
242 #endif // NDEBUG
243
244 // Color IG
245 unsigned colors = 0;
246 if (GetGraph()->IsBytecodeOptimizer()) {
247 colors = ig->AssignColors<VIRTUAL_FRAME_SIZE>(map.GetAvailableRegsCount(), map.GetBorder());
248 } else {
249 colors = ig->AssignColors<MAX_NUM_REGS>(map.GetAvailableRegsCount(), map.GetBorder());
250 }
251
252 if (colors == 0) {
253 COMPILER_LOG(DEBUG, REGALLOC) << "SPILL REQUIRED";
254 return 0;
255 }
256 COMPILER_LOG(INFO, REGALLOC) << "IG nodes " << ig->GetNodes().size() << " colored with " << unsigned(colors);
257
258 return colors;
259 }
260
Remap(const InterferenceGraph & ig,const RegisterMap & map)261 void RegAllocGraphColoring::Remap(const InterferenceGraph &ig, const RegisterMap &map)
262 {
263 // Map allocated colors to registers
264 for (const auto &node : ig.GetNodes()) {
265 auto *interval = node.GetLifeIntervals();
266 if (!node.IsFixedColor()) {
267 // Make interval's register
268 auto color = node.GetColor();
269 ASSERT(color != INVALID_REG);
270 auto reg = map.RegallocToCodegenReg(color);
271 interval->SetReg(reg);
272 }
273 }
274 }
275
Allocate()276 bool RegAllocGraphColoring::Allocate()
277 {
278 auto *gr = GetGraph();
279
280 ReserveTempRegisters();
281 // Create intervals sequences
282 WorkingRanges general_ranges(gr->GetLocalAllocator());
283 WorkingRanges fp_ranges(gr->GetLocalAllocator());
284 InitWorkingRanges(&general_ranges, &fp_ranges);
285 COMPILER_LOG(INFO, REGALLOC) << "Ranges reg " << general_ranges.regular.size() << " fp "
286 << fp_ranges.regular.size();
287
288 // Register allocation
289 InterferenceGraph ig(gr->GetLocalAllocator());
290 RegisterMap map(gr->GetLocalAllocator());
291
292 unsigned int_colors = 0;
293 if (!general_ranges.regular.empty()) {
294 InitMap(&map, false);
295 int_colors = AllocateRegisters(&ig, &general_ranges, map);
296 if (int_colors == 0) {
297 gr->GetAnalysis<LivenessAnalyzer>().Cleanup();
298 COMPILER_LOG(DEBUG, REGALLOC) << "Integer RA failed";
299 return false;
300 }
301 Remap(ig, map);
302 }
303
304 unsigned vec_colors = 0;
305 if (!fp_ranges.regular.empty()) {
306 GetGraph()->SetHasFloatRegs();
307
308 InitMap(&map, true);
309 vec_colors = AllocateRegisters(&ig, &fp_ranges, map);
310 if (vec_colors == 0) {
311 gr->GetAnalysis<LivenessAnalyzer>().Cleanup();
312 COMPILER_LOG(DEBUG, REGALLOC) << "Vector RA failed";
313 return false;
314 }
315 Remap(ig, map);
316 }
317
318 COMPILER_LOG(DEBUG, REGALLOC) << "GC RA passed " << gr->GetRuntime()->GetMethodName(gr->GetMethod()) << " int "
319 << int_colors << " vec " << vec_colors;
320
321 return true;
322 }
323
324 namespace {
325 /*
326 * Add range in sorted order
327 */
AddRange(LifeIntervals * interval,InstructionsRanges * dest)328 void AddRange(LifeIntervals *interval, InstructionsRanges *dest)
329 {
330 auto iter = std::upper_bound(dest->begin(), dest->end(), interval,
331 [](const auto &lhs, const auto &rhs) { return lhs->GetBegin() < rhs->GetBegin(); });
332 dest->insert(iter, interval);
333 }
334 } // namespace
335
InitWorkingRanges(WorkingRanges * general_ranges,WorkingRanges * fp_ranges)336 void RegAllocGraphColoring::InitWorkingRanges(WorkingRanges *general_ranges, WorkingRanges *fp_ranges)
337 {
338 for (auto *interval : GetGraph()->GetAnalysis<LivenessAnalyzer>().GetLifeIntervals()) {
339 if (interval->GetReg() == ACC_REG_ID) {
340 continue;
341 }
342
343 if (interval->IsPreassigned() && interval->GetReg() == INVALID_REG) {
344 ASSERT(interval->GetReg() != INVALID_REG);
345 continue;
346 }
347
348 // Skip instructions without destination register
349 if (!interval->IsPhysical() && interval->NoDest()) {
350 ASSERT(interval->GetLocation().IsInvalid());
351 continue;
352 }
353
354 bool is_fp = DataType::IsFloatType(interval->GetType());
355 auto *ranges = is_fp ? fp_ranges : general_ranges;
356 if (interval->IsPhysical()) {
357 auto mask = is_fp ? GetVRegMask() : GetRegMask();
358 if (mask.IsSet(interval->GetReg())) {
359 // skip physical intervals for unavailable registers, they do not affect allocation
360 continue;
361 }
362 AddRange(interval, &ranges->physical);
363 } else {
364 AddRange(interval, &ranges->regular);
365 }
366 }
367 }
368
InitMap(RegisterMap * map,bool is_vector)369 void RegAllocGraphColoring::InitMap(RegisterMap *map, bool is_vector)
370 {
371 auto arch = GetGraph()->GetArch();
372 if (arch == Arch::NONE) {
373 ASSERT(GetGraph()->IsBytecodeOptimizer());
374 ASSERT(!is_vector);
375 map->SetMask(GetRegMask(), 0);
376 } else {
377 size_t first_callee = GetFirstCalleeReg(arch, is_vector);
378 size_t last_callee = GetLastCalleeReg(arch, is_vector);
379 map->SetCallerFirstMask(is_vector ? GetVRegMask() : GetRegMask(), first_callee, last_callee);
380 }
381 }
382
Presplit(WorkingRanges * ranges)383 void RegAllocGraphColoring::Presplit(WorkingRanges *ranges)
384 {
385 ArenaVector<LifeIntervals *> to_split(GetGraph()->GetLocalAllocator()->Adapter());
386
387 for (auto interval : ranges->regular) {
388 if (!interval->GetLocation().IsFixedRegister()) {
389 continue;
390 }
391 for (auto next : ranges->regular) {
392 if (next->GetBegin() <= interval->GetBegin()) {
393 continue;
394 }
395 if (interval->GetLocation() == next->GetLocation() && interval->IntersectsWith(next)) {
396 to_split.push_back(interval);
397 break;
398 }
399 }
400
401 if (!to_split.empty() && to_split.back() == interval) {
402 // Already added to split
403 continue;
404 }
405 for (auto physical : ranges->physical) {
406 if (interval->GetLocation() == physical->GetLocation() && interval->IntersectsWith(physical)) {
407 to_split.push_back(interval);
408 break;
409 }
410 }
411 }
412
413 for (auto interval : to_split) {
414 COMPILER_LOG(DEBUG, REGALLOC) << "Split at the beginning: " << interval->ToString();
415 auto split = interval->SplitAt(interval->GetBegin() + 1, GetGraph()->GetAllocator());
416 AddRange(split, &ranges->regular);
417 }
418 }
419 } // namespace panda::compiler
420