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