• 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_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 &regularIntervals = 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, &currentInterval](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 &regUse = 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 &regUse = 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, &currentInterval](const auto &interval) {
412         ASSERT(regsUsePositions_.size() > interval->GetReg());
413         auto &regUse = 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