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