• 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 #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