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 #include "reg_alloc_graph_coloring.h"
17 #include <cmath>
18 #include "compiler_logger.h"
19 #include "interference_graph.h"
20 #include "optimizer/analysis/dominators_tree.h"
21 #include "optimizer/analysis/loop_analyzer.h"
22 #include "optimizer/code_generator/callconv.h"
23 #include "optimizer/ir/basicblock.h"
24 #include "optimizer/ir/datatype.h"
25 #include "optimizer/ir/graph.h"
26 #include "reg_type.h"
27
28 namespace ark::compiler {
RegAllocGraphColoring(Graph * graph)29 RegAllocGraphColoring::RegAllocGraphColoring(Graph *graph) : RegAllocBase(graph) {}
RegAllocGraphColoring(Graph * graph,size_t regsCount)30 RegAllocGraphColoring::RegAllocGraphColoring(Graph *graph, size_t regsCount) : RegAllocBase(graph, regsCount) {}
RegAllocGraphColoring(Graph * graph,LocationMask mask)31 RegAllocGraphColoring::RegAllocGraphColoring(Graph *graph, LocationMask mask) : RegAllocBase(graph, std::move(mask))
32 {
33 ASSERT(!IsFrameSizeLarge() || graph->IsAbcKit());
34 }
35
FillPhysicalNodes(InterferenceGraph * ig,WorkingRanges * ranges,ArenaVector<ColorNode * > & physicalNodes)36 void RegAllocGraphColoring::FillPhysicalNodes(InterferenceGraph *ig, WorkingRanges *ranges,
37 ArenaVector<ColorNode *> &physicalNodes)
38 {
39 for (auto physicalInterval : ranges->physical) {
40 ColorNode *node = ig->AllocNode();
41 node->Assign(physicalInterval);
42 node->SetPhysical();
43 physicalNodes.push_back(node);
44 }
45 }
46
BuildIG(InterferenceGraph * ig,WorkingRanges * ranges,bool rematConstants)47 void RegAllocGraphColoring::BuildIG(InterferenceGraph *ig, WorkingRanges *ranges, bool rematConstants)
48 {
49 ig->Reserve(ranges->regular.size() + ranges->physical.size());
50 ArenaDeque<ColorNode *> activeNodes(GetGraph()->GetLocalAllocator()->Adapter());
51 ArenaVector<ColorNode *> physicalNodes(GetGraph()->GetLocalAllocator()->Adapter());
52 const auto &la = GetGraph()->GetAnalysis<LivenessAnalyzer>();
53
54 FillPhysicalNodes(ig, ranges, physicalNodes);
55
56 for (auto currentInterval : ranges->regular) {
57 auto rangeStart = currentInterval->GetBegin();
58
59 if (rematConstants && TryToSpillConstant(currentInterval, GetGraph())) {
60 continue;
61 }
62
63 // Expire active_ranges
64 while (!activeNodes.empty() && activeNodes.front()->GetLifeIntervals()->GetEnd() <= rangeStart) {
65 activeNodes.pop_front();
66 }
67
68 ColorNode *node = ig->AllocNode();
69 node->Assign(currentInterval);
70
71 if (ig->IsUsedSpillWeight()) {
72 node->SetSpillWeight(CalcSpillWeight(la, currentInterval));
73 }
74
75 // Interfer node
76 for (auto activeNode : activeNodes) {
77 auto activeInterval = activeNode->GetLifeIntervals();
78 if (currentInterval->IntersectsWith(activeInterval)) {
79 ig->AddEdge(node->GetNumber(), activeNode->GetNumber());
80 }
81 }
82
83 for (auto physicalNode : physicalNodes) {
84 auto physicalInterval = physicalNode->GetLifeIntervals();
85 if (currentInterval->IntersectsWith<true>(physicalInterval)) {
86 ig->AddEdge(node->GetNumber(), physicalNode->GetNumber());
87 node->AddCallsite(rangeStart);
88 }
89 }
90 auto callback = [ig, node](Location location) {
91 ASSERT(location.IsFixedRegister());
92 auto physicalNode = ig->FindPhysicalNode(location);
93 if (physicalNode == nullptr) {
94 return;
95 }
96 ig->AddEdge(node->GetNumber(), physicalNode->GetNumber());
97 };
98 if (!currentInterval->HasInst()) {
99 // current_interval - is additional life interval for an instruction required temp, add edges to the fixed
100 // inputs' nodes of that instruction
101 la.EnumerateFixedLocationsOverlappingTemp(currentInterval, callback);
102 }
103
104 // Add node to active_nodes sorted by End time
105 auto rangesIter =
106 std::upper_bound(activeNodes.begin(), activeNodes.end(), node, [](const auto &lhs, const auto &rhs) {
107 return lhs->GetLifeIntervals()->GetEnd() <= rhs->GetLifeIntervals()->GetEnd();
108 });
109 activeNodes.insert(rangesIter, node);
110 }
111 }
112
PrecolorIG(InterferenceGraph * ig)113 RegAllocGraphColoring::IndexVector RegAllocGraphColoring::PrecolorIG(InterferenceGraph *ig)
114 {
115 // Walk nodes and propagate properties
116 IndexVector affinityNodes;
117 for (const auto &node : ig->GetNodes()) {
118 AddAffinityEdgesToSiblings(ig, node, &affinityNodes);
119 }
120 return affinityNodes;
121 }
122
123 // Find precolorings and set registers to intervals in advance
PrecolorIG(InterferenceGraph * ig,const RegisterMap & map)124 RegAllocGraphColoring::IndexVector RegAllocGraphColoring::PrecolorIG(InterferenceGraph *ig, const RegisterMap &map)
125 {
126 // Walk nodes and propagate properties
127 IndexVector affinityNodes;
128 for (auto &node : ig->GetNodes()) {
129 const auto *interv = node.GetLifeIntervals();
130 // Take in account preassigned registers in intervals
131 if (interv->IsPhysical() || interv->IsPreassigned()) {
132 // Translate preassigned register from interval to color graph
133 auto color = map.CodegenToRegallocReg(interv->GetReg());
134 node.SetFixedColor(color);
135 }
136
137 if (!interv->HasInst() || interv->IsSplitSibling()) {
138 continue;
139 }
140
141 AddAffinityEdgesToSiblings(ig, node, &affinityNodes);
142
143 const auto *inst = interv->GetInst();
144 ASSERT(inst != nullptr);
145 if (inst->IsPhi()) {
146 AddAffinityEdgesToPhi(ig, node, &affinityNodes);
147 }
148 }
149 AddAffinityEdgesToPhysicalNodes(ig, &affinityNodes);
150 return affinityNodes;
151 }
152
BuildBias(InterferenceGraph * ig,const IndexVector & affinityNodes)153 void RegAllocGraphColoring::BuildBias(InterferenceGraph *ig, const IndexVector &affinityNodes)
154 {
155 auto &nodes = ig->GetNodes();
156
157 // Build affinity connected-components UCC(Unilaterally Connected Components) for coalescing (assign bias number to
158 // nodes of same component)
159 SmallVector<unsigned, DEFAULT_VECTOR_SIZE> walked;
160 SmallVector<unsigned, DEFAULT_VECTOR_SIZE> biased;
161 for (auto index : affinityNodes) {
162 auto &node = nodes[index];
163
164 // Skip already biased
165 if (node.HasBias()) {
166 continue;
167 }
168
169 // Find connected component of graph UCC by (DFS), and collect Call-sites intersections
170 walked.clear();
171 walked.push_back(node.GetNumber());
172 biased.clear();
173 biased.push_back(node.GetNumber());
174 WalkNodes(std::make_pair(walked, biased), nodes, node, ig, affinityNodes);
175 }
176 }
177
WalkNodes(IndexVectorPair && vectors,NodeVector & nodes,ColorNode node,InterferenceGraph * ig,const IndexVector & affinityNodes)178 void RegAllocGraphColoring::WalkNodes(IndexVectorPair &&vectors, NodeVector &nodes, ColorNode node,
179 InterferenceGraph *ig, const IndexVector &affinityNodes)
180 {
181 auto &[walked, biased] = vectors;
182 unsigned biasNum = ig->GetBiasCount();
183 node.SetBias(biasNum);
184 auto &bias = ig->AddBias();
185 ig->UpdateBiasData(&bias, node);
186 do {
187 // Pop back
188 unsigned curIndex = walked.back();
189 walked.resize(walked.size() - 1);
190
191 // Walk N affine nodes
192 for (auto tryIndex : affinityNodes) {
193 auto &tryNode = nodes[tryIndex];
194 if (tryNode.HasBias() || !ig->HasAffinityEdge(curIndex, tryIndex)) {
195 continue;
196 }
197 // Check if the `try_node` intersects one of the already biased
198 auto it = std::find_if(biased.cbegin(), biased.cend(),
199 [ig, tryIndex](auto id) { return ig->HasEdge(id, tryIndex); });
200 if (it != biased.cend()) {
201 continue;
202 }
203
204 tryNode.SetBias(biasNum);
205 ig->UpdateBiasData(&bias, tryNode);
206 walked.push_back(tryIndex);
207 biased.push_back(tryIndex);
208 }
209 } while (!walked.empty());
210 }
211
AddAffinityEdgesToPhi(InterferenceGraph * ig,const ColorNode & node,IndexVector * affinityNodes)212 void RegAllocGraphColoring::AddAffinityEdgesToPhi(InterferenceGraph *ig, const ColorNode &node,
213 IndexVector *affinityNodes)
214 {
215 // Duplicates are possible but we tolerate it
216 affinityNodes->push_back(node.GetNumber());
217
218 auto phi = node.GetLifeIntervals()->GetInst();
219 ASSERT(phi->IsPhi());
220 auto la = &GetGraph()->GetAnalysis<LivenessAnalyzer>();
221 // Iterate over Phi inputs
222 for (size_t i = 0; i < phi->GetInputsCount(); i++) {
223 // Add affinity edge
224 auto inputLi = la->GetInstLifeIntervals(phi->GetDataFlowInput(i));
225 AddAffinityEdge(ig, affinityNodes, node, inputLi);
226 }
227 }
228
AddAffinityEdgesToSiblings(InterferenceGraph * ig,const ColorNode & node,IndexVector * affinityNodes)229 void RegAllocGraphColoring::AddAffinityEdgesToSiblings(InterferenceGraph *ig, const ColorNode &node,
230 IndexVector *affinityNodes)
231 {
232 auto sibling = node.GetLifeIntervals()->GetSibling();
233 if (sibling == nullptr) {
234 return;
235 }
236 affinityNodes->push_back(node.GetNumber());
237 while (sibling != nullptr) {
238 AddAffinityEdge(ig, affinityNodes, node, sibling);
239 sibling = sibling->GetSibling();
240 }
241 }
242
AddAffinityEdgesToPhysicalNodes(InterferenceGraph * ig,IndexVector * affinityNodes)243 void RegAllocGraphColoring::AddAffinityEdgesToPhysicalNodes(InterferenceGraph *ig, IndexVector *affinityNodes)
244 {
245 auto la = &GetGraph()->GetAnalysis<LivenessAnalyzer>();
246 for (auto *interval : la->GetLifeIntervals()) {
247 if (!interval->HasInst()) {
248 continue;
249 }
250 const auto *inst = interval->GetInst();
251 ASSERT(inst != nullptr);
252 // Add affinity edges to fixed locations
253 for (auto i = 0U; i < inst->GetInputsCount(); i++) {
254 auto location = inst->GetLocation(i);
255 if (!location.IsFixedRegister()) {
256 continue;
257 }
258 auto fixedNode = ig->FindPhysicalNode(location);
259 // Possible when general intervals are processing, while input is fp-interval or vice versa
260 if (fixedNode == nullptr) {
261 continue;
262 }
263 affinityNodes->push_back(fixedNode->GetNumber());
264
265 auto inputLi = la->GetInstLifeIntervals(inst->GetDataFlowInput(i));
266 auto sibling = inputLi->FindSiblingAt(interval->GetBegin());
267 ASSERT(sibling != nullptr);
268 AddAffinityEdge(ig, affinityNodes, *fixedNode, sibling);
269 }
270 }
271 }
272
273 /**
274 * Try to find node for the `li` interval in the IG;
275 * If node exists, create affinity edge between it and the `node`
276 */
AddAffinityEdge(InterferenceGraph * ig,IndexVector * affinityNodes,const ColorNode & node,LifeIntervals * li)277 void RegAllocGraphColoring::AddAffinityEdge(InterferenceGraph *ig, IndexVector *affinityNodes, const ColorNode &node,
278 LifeIntervals *li)
279 {
280 if (auto afNode = ig->FindNode(li)) {
281 COMPILER_LOG(DEBUG, REGALLOC) << "AfEdge: " << node.GetNumber() << " " << afNode->GetNumber();
282 ig->AddAffinityEdge(node.GetNumber(), afNode->GetNumber());
283 affinityNodes->push_back(afNode->GetNumber());
284 }
285 }
286
AllocateRegisters(InterferenceGraph * ig,WorkingRanges * ranges,WorkingRanges * stackRanges,const RegisterMap & map)287 bool RegAllocGraphColoring::AllocateRegisters(InterferenceGraph *ig, WorkingRanges *ranges, WorkingRanges *stackRanges,
288 const RegisterMap &map)
289 {
290 if (GetGraph()->IsBytecodeOptimizer()) {
291 BuildIG(ig, ranges);
292 BuildBias(ig, PrecolorIG(ig, map));
293 if (IsFrameSizeLarge()) {
294 return ig->AssignColors<VIRTUAL_FRAME_SIZE_LARGE>(map.GetAvailableRegsCount(), map.GetBorder());
295 }
296 return ig->AssignColors<VIRTUAL_FRAME_SIZE>(map.GetAvailableRegsCount(), map.GetBorder());
297 }
298
299 Presplit(ranges);
300 ig->SetUseSpillWeight(true);
301 auto rounds = 0;
302 static constexpr size_t ROUNDS_LIMIT = 30;
303 while (true) {
304 if (++rounds == ROUNDS_LIMIT) {
305 return false;
306 }
307 BuildIG(ig, ranges);
308 BuildBias(ig, PrecolorIG(ig, map));
309 if (ig->AssignColors<MAX_NUM_REGS>(map.GetAvailableRegsCount(), map.GetBorder())) {
310 break;
311 }
312 SparseIG(ig, map.GetAvailableRegsCount(), ranges, stackRanges);
313 }
314 return true;
315 }
316
AllocateSlots(InterferenceGraph * ig,WorkingRanges * stackRanges)317 bool RegAllocGraphColoring::AllocateSlots(InterferenceGraph *ig, WorkingRanges *stackRanges)
318 {
319 ig->SetUseSpillWeight(false);
320 BuildIG(ig, stackRanges, true);
321 BuildBias(ig, PrecolorIG(ig));
322 if (IsFrameSizeLarge()) {
323 return ig->AssignColors<MAX_NUM_STACK_SLOTS_LARGE>(GetMaxNumStackSlots(), 0);
324 }
325 return ig->AssignColors<MAX_NUM_STACK_SLOTS>(GetMaxNumStackSlots(), 0);
326 }
327
328 /*
329 * Coloring was unsuccessful, hence uncolored nodes should be split to sparse the interference graph
330 */
SparseIG(InterferenceGraph * ig,unsigned regsCount,WorkingRanges * ranges,WorkingRanges * stackRanges)331 void RegAllocGraphColoring::SparseIG(InterferenceGraph *ig, unsigned regsCount, WorkingRanges *ranges,
332 WorkingRanges *stackRanges)
333 {
334 for (const auto &node : ig->GetNodes()) {
335 if (node.GetColor() != regsCount) {
336 continue;
337 }
338 auto interval = node.GetLifeIntervals();
339 if (interval->GetUsePositions().empty()) {
340 SpillInterval(interval, ranges, stackRanges);
341 continue;
342 }
343
344 interval->SplitAroundUses(GetGraph()->GetAllocator());
345 bool isConst = interval->GetInst()->IsConst();
346 if (isConst && interval->GetUsePositions().empty()) {
347 SpillInterval(interval, ranges, stackRanges);
348 }
349 for (auto sibling = interval->GetSibling(); sibling != nullptr; sibling = sibling->GetSibling()) {
350 if (isConst && sibling->GetUsePositions().empty()) {
351 AddRange(sibling, &stackRanges->regular);
352 } else {
353 AddRange(sibling, &ranges->regular);
354 }
355 }
356 }
357 }
358
SpillInterval(LifeIntervals * interval,WorkingRanges * ranges,WorkingRanges * stackRanges)359 void RegAllocGraphColoring::SpillInterval(LifeIntervals *interval, WorkingRanges *ranges, WorkingRanges *stackRanges)
360 {
361 ASSERT(interval->GetUsePositions().empty());
362 ranges->regular.erase(std::remove(ranges->regular.begin(), ranges->regular.end(), interval), ranges->regular.end());
363 AddRange(interval, &stackRanges->regular);
364 }
365
Remap(const InterferenceGraph & ig,const RegisterMap & map)366 void RegAllocGraphColoring::Remap(const InterferenceGraph &ig, const RegisterMap &map)
367 {
368 // Map allocated colors to registers
369 for (const auto &node : ig.GetNodes()) {
370 auto *interval = node.GetLifeIntervals();
371 if (!node.IsFixedColor()) {
372 // Make interval's register
373 auto color = node.GetColor();
374 ASSERT(color != GetInvalidReg());
375 auto reg = map.RegallocToCodegenReg(color);
376 interval->SetReg(reg);
377 }
378 }
379 }
380
MapSlots(const InterferenceGraph & ig)381 bool RegAllocGraphColoring::MapSlots(const InterferenceGraph &ig)
382 {
383 // Map allocated colors to stack slots
384 for (const auto &node : ig.GetNodes()) {
385 auto *interval = node.GetLifeIntervals();
386 // Constant definition on the stack slot is not supported now
387 if (!interval->IsSplitSibling() && interval->GetInst()->IsConst()) {
388 COMPILER_LOG(DEBUG, REGALLOC) << "Stack slots RA failed";
389 return false;
390 }
391 auto slot = node.GetColor();
392 interval->SetLocation(Location::MakeStackSlot(slot));
393 GetStackMask().Set(slot);
394 }
395 return true;
396 }
397
Allocate()398 bool RegAllocGraphColoring::Allocate()
399 {
400 auto *gr = GetGraph();
401
402 ReserveTempRegisters();
403 // Create intervals sequences
404 WorkingRanges generalRanges(gr->GetLocalAllocator());
405 WorkingRanges fpRanges(gr->GetLocalAllocator());
406 WorkingRanges stackRanges(gr->GetLocalAllocator());
407 InitWorkingRanges(&generalRanges, &fpRanges);
408 COMPILER_LOG(INFO, REGALLOC) << "Ranges reg " << generalRanges.regular.size() << " fp " << fpRanges.regular.size();
409
410 // Register allocation
411 InterferenceGraph ig(gr->GetLocalAllocator());
412 RegisterMap map(gr->GetLocalAllocator());
413
414 if (!generalRanges.regular.empty()) {
415 InitMap(&map, false);
416 if (!AllocateRegisters(&ig, &generalRanges, &stackRanges, map)) {
417 COMPILER_LOG(DEBUG, REGALLOC) << "Integer RA failed";
418 return false;
419 }
420 Remap(ig, map);
421 }
422
423 if (!fpRanges.regular.empty()) {
424 GetGraph()->SetHasFloatRegs();
425 InitMap(&map, true);
426 if (!AllocateRegisters(&ig, &fpRanges, &stackRanges, map)) {
427 COMPILER_LOG(DEBUG, REGALLOC) << "Vector RA failed";
428 return false;
429 }
430 Remap(ig, map);
431 }
432
433 if (!stackRanges.regular.empty()) {
434 if (AllocateSlots(&ig, &stackRanges) && MapSlots(ig)) {
435 return true;
436 }
437 COMPILER_LOG(DEBUG, REGALLOC) << "Stack slots RA failed";
438 return false;
439 }
440 return true;
441 }
442
InitWorkingRanges(WorkingRanges * generalRanges,WorkingRanges * fpRanges)443 void RegAllocGraphColoring::InitWorkingRanges(WorkingRanges *generalRanges, WorkingRanges *fpRanges)
444 {
445 for (auto *interval : GetGraph()->GetAnalysis<LivenessAnalyzer>().GetLifeIntervals()) {
446 if (interval->GetReg() == GetAccReg()) {
447 continue;
448 }
449
450 if (interval->IsPreassigned() && interval->GetReg() == GetGraph()->GetZeroReg()) {
451 ASSERT(interval->GetReg() != GetInvalidReg());
452 continue;
453 }
454
455 // Skip instructions without destination register
456 if (interval->HasInst() && interval->NoDest()) {
457 ASSERT(interval->GetLocation().IsInvalid());
458 continue;
459 }
460
461 bool isFp = DataType::IsFloatType(interval->GetType());
462 auto *ranges = isFp ? fpRanges : generalRanges;
463 if (interval->IsPhysical()) {
464 auto mask = isFp ? GetVRegMask() : GetRegMask();
465 if (mask.IsSet(interval->GetReg())) {
466 // skip physical intervals for unavailable registers, they do not affect allocation
467 continue;
468 }
469 AddRange(interval, &ranges->physical);
470 } else {
471 AddRange(interval, &ranges->regular);
472 }
473 }
474 }
475
InitMap(RegisterMap * map,bool isVector)476 void RegAllocGraphColoring::InitMap(RegisterMap *map, bool isVector)
477 {
478 auto arch = GetGraph()->GetArch();
479 if (arch == Arch::NONE) {
480 ASSERT(GetGraph()->IsBytecodeOptimizer());
481 ASSERT(!isVector);
482 map->SetMask(GetRegMask(), 0);
483 } else {
484 size_t firstCallee = GetFirstCalleeReg(arch, isVector);
485 size_t lastCallee = GetLastCalleeReg(arch, isVector);
486 map->SetCallerFirstMask(isVector ? GetVRegMask() : GetRegMask(), firstCallee, lastCallee);
487 }
488 }
489
Presplit(WorkingRanges * ranges)490 void RegAllocGraphColoring::Presplit(WorkingRanges *ranges)
491 {
492 ArenaVector<LifeIntervals *> toSplit(GetGraph()->GetLocalAllocator()->Adapter());
493
494 for (auto interval : ranges->regular) {
495 if (!interval->GetLocation().IsFixedRegister()) {
496 continue;
497 }
498 for (auto next : ranges->regular) {
499 if (next->GetBegin() <= interval->GetBegin()) {
500 continue;
501 }
502 if (interval->GetLocation() == next->GetLocation() && interval->IntersectsWith(next)) {
503 toSplit.push_back(interval);
504 break;
505 }
506 }
507
508 if (!toSplit.empty() && toSplit.back() == interval) {
509 // Already added to split
510 continue;
511 }
512 for (auto physical : ranges->physical) {
513 if (interval->GetLocation() == physical->GetLocation() && interval->IntersectsWith<true>(physical)) {
514 toSplit.push_back(interval);
515 break;
516 }
517 }
518 }
519
520 for (auto interval : toSplit) {
521 COMPILER_LOG(DEBUG, REGALLOC) << "Split at the beginning: " << interval->ToString();
522 auto split = interval->SplitAt(interval->GetBegin() + 1, GetGraph()->GetAllocator());
523 AddRange(split, &ranges->regular);
524 }
525 }
526 } // namespace ark::compiler
527