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_linear_scan.h"
17 #include "optimizer/analysis/loop_analyzer.h"
18 #include "optimizer/ir/basicblock.h"
19 #include "optimizer/ir/datatype.h"
20 #include "optimizer/ir/graph.h"
21
22 namespace panda::compiler {
23
24 static constexpr auto MAX_LIFE_NUMBER = std::numeric_limits<LifeNumber>::max();
25
26 /// Add interval in sorted order
AddInterval(LifeIntervals * interval,InstructionsIntervals * dest)27 static void AddInterval(LifeIntervals *interval, InstructionsIntervals *dest)
28 {
29 auto cmp = [](LifeIntervals *lhs, LifeIntervals *rhs) { return lhs->GetBegin() >= rhs->GetBegin(); };
30
31 if (dest->empty()) {
32 dest->push_back(interval);
33 return;
34 }
35
36 auto iter = dest->end();
37 --iter;
38 while (true) {
39 if (cmp(interval, *iter)) {
40 dest->insert(++iter, interval);
41 break;
42 }
43 if (iter == dest->begin()) {
44 dest->push_front(interval);
45 break;
46 }
47 --iter;
48 }
49 }
50
51 /// Use graph's registers masks and `MAX_NUM_STACK_SLOTS` stack slots
RegAllocLinearScan(Graph * graph)52 RegAllocLinearScan::RegAllocLinearScan(Graph *graph)
53 : RegAllocBase(graph),
54 workingIntervals_(graph->GetLocalAllocator()),
55 regsUsePositions_(graph->GetLocalAllocator()->Adapter()),
56 generalIntervals_(graph->GetLocalAllocator()),
57 vectorIntervals_(graph->GetLocalAllocator()),
58 regMap_(graph->GetLocalAllocator()),
59 rematConstants_(!graph->IsBytecodeOptimizer() && g_options.IsCompilerRematConst())
60 {
61 }
62
63 /// Use dynamic general registers mask (without vector regs) and zero stack slots
RegAllocLinearScan(Graph * graph,EmptyRegMask mask)64 RegAllocLinearScan::RegAllocLinearScan(Graph *graph, [[maybe_unused]] EmptyRegMask mask)
65 : RegAllocBase(graph, VIRTUAL_FRAME_SIZE),
66 workingIntervals_(graph->GetLocalAllocator()),
67 regsUsePositions_(graph->GetLocalAllocator()->Adapter()),
68 generalIntervals_(graph->GetLocalAllocator()),
69 vectorIntervals_(graph->GetLocalAllocator()),
70 regMap_(graph->GetLocalAllocator()),
71 rematConstants_(!graph->IsBytecodeOptimizer() && g_options.IsCompilerRematConst())
72 {
73 }
74
75 /// Allocate registers
Allocate()76 bool RegAllocLinearScan::Allocate()
77 {
78 AssignLocations<false>();
79 AssignLocations<true>();
80 return success_;
81 }
82
InitIntervals()83 void RegAllocLinearScan::InitIntervals()
84 {
85 GetIntervals<false>().fixed.resize(MAX_NUM_REGS);
86 GetIntervals<true>().fixed.resize(MAX_NUM_VREGS);
87 ReserveTempRegisters();
88 }
89
PrepareInterval(LifeIntervals * interval)90 void RegAllocLinearScan::PrepareInterval(LifeIntervals *interval)
91 {
92 bool isFp = DataType::IsFloatType(interval->GetType());
93 auto &intervals = isFp ? GetIntervals<true>() : GetIntervals<false>();
94
95 if (interval->IsPhysical()) {
96 ASSERT(intervals.fixed.size() > interval->GetReg());
97 ASSERT(intervals.fixed[interval->GetReg()] == nullptr);
98 intervals.fixed[interval->GetReg()] = interval;
99 return;
100 }
101
102 if (!interval->HasInst()) {
103 AddInterval(interval, &intervals.regular);
104 return;
105 }
106
107 if (interval->NoDest() || interval->GetInst()->GetDstCount() > 1U || interval->GetReg() == ACC_REG_ID) {
108 return;
109 }
110
111 if (interval->IsPreassigned() && interval->GetReg() == GetGraph()->GetZeroReg()) {
112 ASSERT(interval->GetReg() != INVALID_REG);
113 return;
114 }
115
116 AddInterval(interval, &intervals.regular);
117 }
118
119 template <bool IS_FP>
AssignLocations()120 void RegAllocLinearScan::AssignLocations()
121 {
122 auto ®ularIntervals = GetIntervals<IS_FP>().regular;
123 if (regularIntervals.empty()) {
124 return;
125 }
126
127 // NOLINTNEXTLINE(readability-magic-numbers,readability-braces-around-statements,bugprone-suspicious-semicolon)
128 if constexpr (IS_FP) {
129 GetGraph()->SetHasFloatRegs();
130 }
131
132 workingIntervals_.Clear();
133 auto arch = GetGraph()->GetArch();
134 size_t priority = arch != Arch::NONE ? GetFirstCalleeReg(arch, IS_FP) : 0;
135 regMap_.SetMask(GetLocationMask<IS_FP>(), priority);
136 regsUsePositions_.resize(regMap_.GetAvailableRegsCount());
137
138 AddFixedIntervalsToWorkingIntervals<IS_FP>();
139 PreprocessPreassignedIntervals<IS_FP>();
140 while (!regularIntervals.empty() && success_) {
141 ExpireIntervals<IS_FP>(regularIntervals.front()->GetBegin());
142 WalkIntervals<IS_FP>();
143 }
144 RemapRegistersIntervals();
145 }
146
147 template <bool IS_FP>
PreprocessPreassignedIntervals()148 void RegAllocLinearScan::PreprocessPreassignedIntervals()
149 {
150 for (auto &interval : GetIntervals<IS_FP>().regular) {
151 if (!interval->IsPreassigned() || interval->IsSplitSibling() || interval->GetReg() == ACC_REG_ID) {
152 continue;
153 }
154 interval->SetPreassignedReg(regMap_.CodegenToRegallocReg(interval->GetReg()));
155 COMPILER_LOG(DEBUG, REGALLOC) << "Preassigned interval " << interval->template ToString<true>();
156 }
157 }
158
159 /// Free registers from expired intervals
160 template <bool IS_FP>
ExpireIntervals(LifeNumber currentPosition)161 void RegAllocLinearScan::ExpireIntervals(LifeNumber currentPosition)
162 {
163 IterateIntervalsWithErasion(workingIntervals_.active, [this, currentPosition](const auto &interval) {
164 if (!interval->HasReg() || interval->GetEnd() <= currentPosition) {
165 workingIntervals_.handled.push_back(interval);
166 return true;
167 }
168 if (!interval->SplitCover(currentPosition)) {
169 AddInterval(interval, &workingIntervals_.inactive);
170 return true;
171 }
172 return false;
173 });
174 IterateIntervalsWithErasion(workingIntervals_.inactive, [this, currentPosition](const auto &interval) {
175 if (!interval->HasReg() || interval->GetEnd() <= currentPosition) {
176 workingIntervals_.handled.push_back(interval);
177 return true;
178 }
179 if (interval->SplitCover(currentPosition)) {
180 AddInterval(interval, &workingIntervals_.active);
181 return true;
182 }
183 return false;
184 });
185 IterateIntervalsWithErasion(workingIntervals_.stack, [this, currentPosition](const auto &interval) {
186 if (interval->GetEnd() <= currentPosition) {
187 GetStackMask().Reset(interval->GetLocation().GetValue());
188 return true;
189 }
190 return false;
191 });
192 }
193
194 /// Working intervals processing
195 template <bool IS_FP>
WalkIntervals()196 void RegAllocLinearScan::WalkIntervals()
197 {
198 auto currentInterval = GetIntervals<IS_FP>().regular.front();
199 GetIntervals<IS_FP>().regular.pop_front();
200 COMPILER_LOG(DEBUG, REGALLOC) << "----------------";
201 COMPILER_LOG(DEBUG, REGALLOC) << "Process interval " << currentInterval->template ToString<true>();
202
203 // Parameter that was passed in the stack slot: split its interval before first use
204 if (currentInterval->GetLocation().IsStackParameter()) {
205 ASSERT(currentInterval->GetInst()->IsParameter());
206 COMPILER_LOG(DEBUG, REGALLOC) << "Interval was defined in the stack parameter slot";
207 auto nextUse = currentInterval->GetNextUsage(currentInterval->GetBegin() + 1U);
208 SplitBeforeUse<IS_FP>(currentInterval, nextUse);
209 return;
210 }
211
212 if (!currentInterval->HasReg()) {
213 if (TryToAssignRegister<IS_FP>(currentInterval)) {
214 COMPILER_LOG(DEBUG, REGALLOC)
215 << currentInterval->GetLocation().ToString(GetGraph()->GetArch()) << " was assigned to the interval "
216 << currentInterval->template ToString<true>();
217 } else {
218 COMPILER_LOG(ERROR, REGALLOC) << "There are no available registers";
219 success_ = false;
220 return;
221 }
222 } else {
223 ASSERT(currentInterval->IsPreassigned());
224 COMPILER_LOG(DEBUG, REGALLOC) << "Interval has preassigned "
225 << currentInterval->GetLocation().ToString(GetGraph()->GetArch());
226 if (!IsIntervalRegFree(currentInterval, currentInterval->GetReg())) {
227 SplitAndSpill<IS_FP>(&workingIntervals_.active, currentInterval);
228 SplitAndSpill<IS_FP>(&workingIntervals_.inactive, currentInterval);
229 }
230 }
231 HandleFixedIntervalIntersection<IS_FP>(currentInterval);
232 AddInterval(currentInterval, &workingIntervals_.active);
233 }
234
235 template <bool IS_FP>
TryToAssignRegister(LifeIntervals * currentInterval)236 bool RegAllocLinearScan::TryToAssignRegister(LifeIntervals *currentInterval)
237 {
238 auto reg = GetSuitableRegister(currentInterval);
239 if (reg != INVALID_REG) {
240 currentInterval->SetReg(reg);
241 return true;
242 }
243
244 // Try to assign blocked register
245 auto [blockedReg, nextBlockedUse] = GetBlockedRegister(currentInterval);
246 auto nextUse = currentInterval->GetNextUsage(currentInterval->GetBegin());
247 // Spill current interval if its first use later than use of blocked register
248 if (blockedReg != INVALID_REG && nextBlockedUse < nextUse && !IsNonSpillableConstInterval(currentInterval)) {
249 SplitBeforeUse<IS_FP>(currentInterval, nextUse);
250 AssignStackSlot(currentInterval);
251 return true;
252 }
253
254 // Blocked register that will be used in the next position mustn't be reassigned
255 if (blockedReg == INVALID_REG || nextBlockedUse < currentInterval->GetBegin() + LIFE_NUMBER_GAP) {
256 return false;
257 }
258
259 currentInterval->SetReg(blockedReg);
260 SplitAndSpill<IS_FP>(&workingIntervals_.active, currentInterval);
261 SplitAndSpill<IS_FP>(&workingIntervals_.inactive, currentInterval);
262 return true;
263 }
264
265 template <bool IS_FP>
SplitAndSpill(const InstructionsIntervals * intervals,const LifeIntervals * currentInterval)266 void RegAllocLinearScan::SplitAndSpill(const InstructionsIntervals *intervals, const LifeIntervals *currentInterval)
267 {
268 for (auto interval : *intervals) {
269 if (interval->GetReg() != currentInterval->GetReg() ||
270 interval->GetFirstIntersectionWith(currentInterval) == INVALID_LIFE_NUMBER) {
271 continue;
272 }
273 COMPILER_LOG(DEBUG, REGALLOC) << "Found active interval: " << interval->template ToString<true>();
274 SplitActiveInterval<IS_FP>(interval, currentInterval->GetBegin());
275 }
276 }
277
278 /**
279 * Split interval with assigned 'reg' into 3 parts:
280 * [interval|split|split_next]
281 *
282 * 'interval' - holds assigned register;
283 * 'split' - stack slot is assigned to it;
284 * 'split_next' - added to the queue for future assignment;
285 */
286 template <bool IS_FP>
SplitActiveInterval(LifeIntervals * interval,LifeNumber splitPos)287 void RegAllocLinearScan::SplitActiveInterval(LifeIntervals *interval, LifeNumber splitPos)
288 {
289 BeforeConstantIntervalSpill(interval, splitPos);
290 auto prevUsePos = interval->GetPrevUsage(splitPos);
291 auto nextUsePos = interval->GetNextUsage(splitPos + 1U);
292 COMPILER_LOG(DEBUG, REGALLOC) << "Prev use position: " << std::to_string(prevUsePos)
293 << ", Next use position: " << std::to_string(nextUsePos);
294 auto split = interval;
295 if (prevUsePos == INVALID_LIFE_NUMBER) {
296 COMPILER_LOG(DEBUG, REGALLOC) << "Spill the whole interval " << interval->template ToString<true>();
297 interval->ClearLocation();
298 } else {
299 auto splitPosition = (splitPos % 2U == 1) ? splitPos : splitPos - 1;
300 COMPILER_LOG(DEBUG, REGALLOC) << "Split interval " << interval->template ToString<true>() << " at position "
301 << static_cast<int>(splitPosition);
302
303 split = interval->SplitAt(splitPosition, GetGraph()->GetAllocator());
304 }
305 SplitBeforeUse<IS_FP>(split, nextUsePos);
306 AssignStackSlot(split);
307 }
308
309 template <bool IS_FP>
AddToQueue(LifeIntervals * interval)310 void RegAllocLinearScan::AddToQueue(LifeIntervals *interval)
311 {
312 COMPILER_LOG(DEBUG, REGALLOC) << "Add to the queue: " << interval->template ToString<true>();
313 AddInterval(interval, &GetIntervals<IS_FP>().regular);
314 }
315
GetSuitableRegister(const LifeIntervals * currentInterval)316 Register RegAllocLinearScan::GetSuitableRegister(const LifeIntervals *currentInterval)
317 {
318 if (!currentInterval->HasInst()) {
319 return GetFreeRegister(currentInterval);
320 }
321 // First of all, try to assign register using hint
322 auto &useTable = GetGraph()->GetAnalysis<LivenessAnalyzer>().GetUseTable();
323 auto hintReg = useTable.GetNextUseOnFixedLocation(currentInterval->GetInst(), currentInterval->GetBegin());
324 if (hintReg != INVALID_REG) {
325 auto reg = regMap_.CodegenToRegallocReg(hintReg);
326 if (regMap_.IsRegAvailable(reg, GetGraph()->GetArch()) && IsIntervalRegFree(currentInterval, reg)) {
327 COMPILER_LOG(DEBUG, REGALLOC) << "Hint-register is available";
328 return reg;
329 }
330 }
331 // If hint doesn't exist or hint-register not available, try to assign any free register
332 return GetFreeRegister(currentInterval);
333 }
334
GetFreeRegister(const LifeIntervals * currentInterval)335 Register RegAllocLinearScan::GetFreeRegister(const LifeIntervals *currentInterval)
336 {
337 std::fill(regsUsePositions_.begin(), regsUsePositions_.end(), MAX_LIFE_NUMBER);
338
339 auto setFixedUsage = [this, ¤tInterval](const auto &interval, LifeNumber intersection) {
340 // If intersection is equal to the current_interval's begin
341 // than it means that current_interval is a call and fixed interval's range was
342 // created for it.
343 // Do not disable fixed register for a call.
344 if (intersection == currentInterval->GetBegin()) {
345 LiveRange range;
346 [[maybe_unused]] bool succ = interval->FindRangeCoveringPosition(intersection, &range);
347 ASSERT(succ);
348 if (range.GetBegin() == intersection) {
349 return;
350 }
351 }
352 ASSERT(regsUsePositions_.size() > interval->GetReg());
353 regsUsePositions_[interval->GetReg()] = intersection;
354 };
355 auto setInactiveUsage = [this](const auto &interval, LifeNumber intersection) {
356 ASSERT(regsUsePositions_.size() > interval->GetReg());
357 auto ®Use = regsUsePositions_[interval->GetReg()];
358 regUse = std::min<size_t>(intersection, regUse);
359 };
360
361 EnumerateIntersectedIntervals(workingIntervals_.fixed, currentInterval, setFixedUsage);
362 EnumerateIntersectedIntervals(workingIntervals_.inactive, currentInterval, setInactiveUsage);
363 EnumerateIntervals(workingIntervals_.active, [this](const auto &interval) {
364 ASSERT(regsUsePositions_.size() > interval->GetReg());
365 regsUsePositions_[interval->GetReg()] = 0;
366 });
367
368 BlockOverlappedRegisters(currentInterval);
369 BlockIndirectCallRegisters(currentInterval);
370
371 // Select register with max position
372 auto it = std::max_element(regsUsePositions_.cbegin(), regsUsePositions_.cend());
373 // Register is free if it's available for the whole interval
374 return (*it >= currentInterval->GetEnd()) ? std::distance(regsUsePositions_.cbegin(), it) : INVALID_REG;
375 }
376
377 // Return blocked register and its next use position
GetBlockedRegister(const LifeIntervals * currentInterval)378 std::pair<Register, LifeNumber> RegAllocLinearScan::GetBlockedRegister(const LifeIntervals *currentInterval)
379 {
380 // Using blocked registers is impossible in the `BytecodeOptimizer` mode
381 if (GetGraph()->IsBytecodeOptimizer()) {
382 return {INVALID_REG, INVALID_LIFE_NUMBER};
383 }
384
385 std::fill(regsUsePositions_.begin(), regsUsePositions_.end(), MAX_LIFE_NUMBER);
386
387 auto setFixedUsage = [this, currentInterval](const auto &interval, LifeNumber intersection) {
388 // If intersection is equal to the current_interval's begin
389 // than it means that current_interval is a call and fixed interval's range was
390 // created for it.
391 // Do not disable fixed register for a call.
392 if (intersection == currentInterval->GetBegin()) {
393 LiveRange range;
394 [[maybe_unused]] bool succ = interval->FindRangeCoveringPosition(intersection, &range);
395 ASSERT(succ);
396 if (range.GetBegin() == intersection) {
397 return;
398 }
399 }
400 ASSERT(regsUsePositions_.size() > interval->GetReg());
401 regsUsePositions_[interval->GetReg()] = intersection;
402 };
403 auto setInactiveUsage = [this](const auto &interval, LifeNumber intersection) {
404 ASSERT(regsUsePositions_.size() > interval->GetReg());
405 auto ®Use = regsUsePositions_[interval->GetReg()];
406 regUse = std::min<size_t>(interval->GetNextUsage(intersection), regUse);
407 };
408
409 EnumerateIntersectedIntervals(workingIntervals_.fixed, currentInterval, setFixedUsage);
410 EnumerateIntersectedIntervals(workingIntervals_.inactive, currentInterval, setInactiveUsage);
411 EnumerateIntervals(workingIntervals_.active, [this, ¤tInterval](const auto &interval) {
412 ASSERT(regsUsePositions_.size() > interval->GetReg());
413 auto ®Use = regsUsePositions_[interval->GetReg()];
414 regUse = std::min<size_t>(interval->GetNextUsage(currentInterval->GetBegin()), regUse);
415 });
416
417 BlockOverlappedRegisters(currentInterval);
418 BlockIndirectCallRegisters(currentInterval);
419
420 // Select register with max position
421 auto it = std::max_element(regsUsePositions_.cbegin(), regsUsePositions_.cend());
422 auto reg = std::distance(regsUsePositions_.cbegin(), it);
423 COMPILER_LOG(DEBUG, REGALLOC) << "Selected blocked r" << static_cast<int>(reg) << " with use next use position "
424 << *it;
425 return {reg, regsUsePositions_[reg]};
426 }
427
IsIntervalRegFree(const LifeIntervals * currentInterval,Register reg) const428 bool RegAllocLinearScan::IsIntervalRegFree(const LifeIntervals *currentInterval, Register reg) const
429 {
430 for (auto interval : workingIntervals_.fixed) {
431 if (interval == nullptr || interval->GetReg() != reg) {
432 continue;
433 }
434 if (interval->GetFirstIntersectionWith(currentInterval) < currentInterval->GetBegin() + LIFE_NUMBER_GAP) {
435 return false;
436 }
437 }
438
439 for (auto interval : workingIntervals_.inactive) {
440 if (interval->GetReg() == reg && interval->GetFirstIntersectionWith(currentInterval) != INVALID_LIFE_NUMBER) {
441 return false;
442 }
443 }
444 for (auto interval : workingIntervals_.active) {
445 if (interval->GetReg() == reg) {
446 return false;
447 }
448 }
449 return true;
450 }
451
AssignStackSlot(LifeIntervals * interval)452 void RegAllocLinearScan::AssignStackSlot(LifeIntervals *interval)
453 {
454 ASSERT(!interval->GetLocation().IsStack());
455 ASSERT(interval->HasInst());
456 if (rematConstants_ && interval->GetInst()->IsConst()) {
457 auto immSlot = GetGraph()->AddSpilledConstant(interval->GetInst()->CastToConstant());
458 if (immSlot != INVALID_IMM_TABLE_SLOT) {
459 interval->SetLocation(Location::MakeConstant(immSlot));
460 COMPILER_LOG(DEBUG, REGALLOC) << interval->GetLocation().ToString(GetGraph()->GetArch())
461 << " was assigned to the interval " << interval->template ToString<true>();
462 return;
463 }
464 }
465
466 auto slot = GetNextStackSlot(interval);
467 if (slot != INVALID_STACK_SLOT) {
468 interval->SetLocation(Location::MakeStackSlot(slot));
469 COMPILER_LOG(DEBUG, REGALLOC) << interval->GetLocation().ToString(GetGraph()->GetArch())
470 << " was assigned to the interval " << interval->template ToString<true>();
471 workingIntervals_.stack.push_back(interval);
472 } else {
473 COMPILER_LOG(ERROR, REGALLOC) << "There are no available stack slots";
474 success_ = false;
475 }
476 }
477
RemapRegallocReg(LifeIntervals * interval)478 void RegAllocLinearScan::RemapRegallocReg(LifeIntervals *interval)
479 {
480 if (interval->HasReg()) {
481 auto reg = interval->GetReg();
482 interval->SetReg(regMap_.RegallocToCodegenReg(reg));
483 }
484 }
485
RemapRegistersIntervals()486 void RegAllocLinearScan::RemapRegistersIntervals()
487 {
488 for (auto interval : workingIntervals_.handled) {
489 RemapRegallocReg(interval);
490 }
491 for (auto interval : workingIntervals_.active) {
492 RemapRegallocReg(interval);
493 }
494 for (auto interval : workingIntervals_.inactive) {
495 RemapRegallocReg(interval);
496 }
497 for (auto interval : workingIntervals_.fixed) {
498 if (interval != nullptr) {
499 RemapRegallocReg(interval);
500 }
501 }
502 }
503
504 template <bool IS_FP>
AddFixedIntervalsToWorkingIntervals()505 void RegAllocLinearScan::AddFixedIntervalsToWorkingIntervals()
506 {
507 workingIntervals_.fixed.resize(GetLocationMask<IS_FP>().GetSize());
508 // remap registers for fixed intervals and add it to working intervals
509 for (auto fixedInterval : GetIntervals<IS_FP>().fixed) {
510 if (fixedInterval == nullptr) {
511 continue;
512 }
513 auto reg = regMap_.CodegenToRegallocReg(fixedInterval->GetReg());
514 fixedInterval->SetReg(reg);
515 workingIntervals_.fixed[reg] = fixedInterval;
516 COMPILER_LOG(DEBUG, REGALLOC) << "Fixed interval for r" << static_cast<int>(fixedInterval->GetReg()) << ": "
517 << fixedInterval->template ToString<true>();
518 }
519 }
520
521 template <bool IS_FP>
HandleFixedIntervalIntersection(LifeIntervals * currentInterval)522 void RegAllocLinearScan::HandleFixedIntervalIntersection(LifeIntervals *currentInterval)
523 {
524 if (!currentInterval->HasReg()) {
525 return;
526 }
527 auto reg = currentInterval->GetReg();
528 if (reg >= workingIntervals_.fixed.size() || workingIntervals_.fixed[reg] == nullptr) {
529 return;
530 }
531 auto fixedInterval = workingIntervals_.fixed[reg];
532 auto intersection = currentInterval->GetFirstIntersectionWith(fixedInterval);
533 if (intersection == currentInterval->GetBegin()) {
534 // Current interval can intersect fixed interval at the beginning of its live range
535 // only if it's a call and fixed interval's range was created for it.
536 // Try to find first intersection excluding the range blocking registers during a call.
537 intersection = currentInterval->GetFirstIntersectionWith(fixedInterval, intersection + 1U);
538 }
539 if (intersection == INVALID_LIFE_NUMBER) {
540 return;
541 }
542 COMPILER_LOG(DEBUG, REGALLOC) << "Intersection with fixed interval at: " << std::to_string(intersection);
543
544 auto &useTable = GetGraph()->GetAnalysis<LivenessAnalyzer>().GetUseTable();
545 if (useTable.HasUseOnFixedLocation(currentInterval->GetInst(), intersection)) {
546 // Instruction is used at intersection position: split before that use
547 SplitBeforeUse<IS_FP>(currentInterval, intersection);
548 return;
549 }
550
551 BeforeConstantIntervalSpill(currentInterval, intersection);
552 auto lastUseBefore = currentInterval->GetLastUsageBefore(intersection);
553 if (lastUseBefore != INVALID_LIFE_NUMBER) {
554 // Split after the last use before intersection
555 SplitBeforeUse<IS_FP>(currentInterval, lastUseBefore + LIFE_NUMBER_GAP);
556 return;
557 }
558
559 // There is no use before intersection, split after intersection add splitted-interval to the queue
560 auto nextUse = currentInterval->GetNextUsage(intersection);
561 currentInterval->ClearLocation();
562 SplitBeforeUse<IS_FP>(currentInterval, nextUse);
563 AssignStackSlot(currentInterval);
564 }
565
566 template <bool IS_FP>
SplitBeforeUse(LifeIntervals * currentInterval,LifeNumber usePos)567 void RegAllocLinearScan::SplitBeforeUse(LifeIntervals *currentInterval, LifeNumber usePos)
568 {
569 if (usePos == INVALID_LIFE_NUMBER) {
570 return;
571 }
572 COMPILER_LOG(DEBUG, REGALLOC) << "Split at " << std::to_string(usePos - 1);
573 auto split = currentInterval->SplitAt(usePos - 1, GetGraph()->GetAllocator());
574 AddToQueue<IS_FP>(split);
575 }
576
BlockIndirectCallRegisters(const LifeIntervals * currentInterval)577 void RegAllocLinearScan::BlockIndirectCallRegisters(const LifeIntervals *currentInterval)
578 {
579 if (!currentInterval->HasInst()) {
580 return;
581 }
582 auto inst = currentInterval->GetInst();
583 for (auto &user : inst->GetUsers()) {
584 auto userInst = user.GetInst();
585 if (userInst->IsIndirectCall() && userInst->GetInput(0).GetInst() == inst) {
586 // CallIndirect is a special case:
587 // - input[0] is a call address, which may be allocated to any register
588 // - other inputs are call args, and LocationsBuilder binds them
589 // to the corresponding target CPU registers.
590 //
591 // Thus input[0] must not be allocated to the registers used to pass arguments.
592 for (size_t i = 1; i < userInst->GetInputsCount(); ++i) {
593 auto location = userInst->GetLocation(i);
594 ASSERT(location.IsFixedRegister());
595 auto reg = regMap_.CodegenToRegallocReg(location.GetValue());
596 regsUsePositions_[reg] = 0;
597 }
598 // LocationBuilder assigns locations the same way for all CallIndirect instructions.
599 break;
600 }
601 }
602 }
603
BlockOverlappedRegisters(const LifeIntervals * currentInterval)604 void RegAllocLinearScan::BlockOverlappedRegisters(const LifeIntervals *currentInterval)
605 {
606 if (!currentInterval->HasInst()) {
607 // current_interval - is additional life interval for an instruction required temp, block fixed registers of
608 // that instruction
609 auto &la = GetGraph()->GetAnalysis<LivenessAnalyzer>();
610 la.EnumerateFixedLocationsOverlappingTemp(currentInterval, [this](Location location) {
611 ASSERT(location.IsFixedRegister());
612 auto reg = regMap_.CodegenToRegallocReg(location.GetValue());
613 if (regMap_.IsRegAvailable(reg, GetGraph()->GetArch())) {
614 ASSERT(regsUsePositions_.size() > reg);
615 regsUsePositions_[reg] = 0;
616 }
617 });
618 }
619 }
620
621 /// Returns true if the interval corresponds to Constant instruction and it could not be spilled to a stack.
IsNonSpillableConstInterval(LifeIntervals * interval)622 bool RegAllocLinearScan::IsNonSpillableConstInterval(LifeIntervals *interval)
623 {
624 if (interval->IsSplitSibling() || interval->IsPhysical()) {
625 return false;
626 }
627 auto inst = interval->GetInst();
628 return inst != nullptr && inst->IsConst() && rematConstants_ &&
629 inst->CastToConstant()->GetImmTableSlot() == INVALID_IMM_TABLE_SLOT &&
630 !GetGraph()->HasAvailableConstantSpillSlots();
631 }
632
633 /**
634 * If constant rematerialization is enabled then constant intervals won't have use position
635 * at the beginning of an interval. If there are available immediate slots then it is not an issue
636 * as the interval will have a slot assigned to it during the spill. But if there are not available
637 * immediate slots then the whole interval will be spilled (which is incorrect) unless we add a use
638 * position at the beginning and thus force split creation right after it.
639 */
BeforeConstantIntervalSpill(LifeIntervals * interval,LifeNumber splitPos)640 void RegAllocLinearScan::BeforeConstantIntervalSpill(LifeIntervals *interval, LifeNumber splitPos)
641 {
642 if (!IsNonSpillableConstInterval(interval)) {
643 return;
644 }
645 if (interval->GetPrevUsage(splitPos) != INVALID_LIFE_NUMBER) {
646 return;
647 }
648 interval->PrependUsePosition(interval->GetBegin());
649 }
650
651 } // namespace panda::compiler
652