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 "optimizer/ir/inst.h"
17 #include "optimizer/ir/basicblock.h"
18 #include "optimizer/ir/graph.h"
19 #include "liveness_analyzer.h"
20 #include "optimizer/analysis/dominators_tree.h"
21 #include "optimizer/analysis/loop_analyzer.h"
22 #include "optimizer/optimizations/locations_builder.h"
23 #include "optimizer/optimizations/regalloc/reg_type.h"
24
25 namespace ark::compiler {
LivenessAnalyzer(Graph * graph)26 LivenessAnalyzer::LivenessAnalyzer(Graph *graph)
27 : Analysis(graph),
28 allocator_(graph->GetAllocator()),
29 linearBlocks_(graph->GetAllocator()->Adapter()),
30 instLifeNumbers_(graph->GetAllocator()->Adapter()),
31 instLifeIntervals_(graph->GetAllocator()->Adapter()),
32 instsByLifeNumber_(graph->GetAllocator()->Adapter()),
33 blockLiveRanges_(graph->GetAllocator()->Adapter()),
34 blockLiveSets_(graph->GetLocalAllocator()->Adapter()),
35 pendingCatchPhiInputs_(graph->GetAllocator()->Adapter()),
36 physicalGeneralIntervals_(graph->GetAllocator()->Adapter()),
37 physicalVectorIntervals_(graph->GetAllocator()->Adapter()),
38 intervalsForTemps_(graph->GetAllocator()->Adapter()),
39 useTable_(graph->GetAllocator()),
40 hasSafepointDuringCall_(graph->GetRuntime()->HasSafepointDuringCall())
41 {
42 }
43
RunImpl()44 bool LivenessAnalyzer::RunImpl()
45 {
46 if (!RunLocationsBuilder(GetGraph())) {
47 return false;
48 }
49 GetGraph()->RunPass<DominatorsTree>();
50 GetGraph()->RunPass<LoopAnalyzer>();
51 ResetLiveness();
52 BuildBlocksLinearOrder();
53 BuildInstLifeNumbers();
54 BuildInstLifeIntervals();
55 Finalize();
56 if (!pendingCatchPhiInputs_.empty()) {
57 COMPILER_LOG(ERROR, LIVENESS_ANALYZER)
58 << "Graph contains CatchPhi instructions whose inputs were not processed";
59 return false;
60 }
61 std::copy_if(physicalGeneralIntervals_.begin(), physicalGeneralIntervals_.end(),
62 std::back_inserter(instLifeIntervals_), [](auto li) { return li != nullptr; });
63 std::copy_if(physicalVectorIntervals_.begin(), physicalVectorIntervals_.end(),
64 std::back_inserter(instLifeIntervals_), [](auto li) { return li != nullptr; });
65 std::copy(intervalsForTemps_.begin(), intervalsForTemps_.end(), std::back_inserter(instLifeIntervals_));
66 COMPILER_LOG(DEBUG, LIVENESS_ANALYZER) << "Liveness analysis is constructed";
67 return true;
68 }
69
ResetLiveness()70 void LivenessAnalyzer::ResetLiveness()
71 {
72 instLifeNumbers_.clear();
73 instLifeIntervals_.clear();
74 blockLiveSets_.clear();
75 blockLiveRanges_.clear();
76 physicalGeneralIntervals_.clear();
77 physicalVectorIntervals_.clear();
78 intervalsForTemps_.clear();
79 if (GetGraph()->GetArch() != Arch::NONE) {
80 physicalGeneralIntervals_.resize(REGISTERS_NUM);
81 physicalVectorIntervals_.resize(VREGISTERS_NUM);
82 }
83 #ifndef NDEBUG
84 finalized_ = false;
85 #endif
86 }
87
88 /*
89 * Linear blocks order means:
90 * - all dominators of a block are visiting before this block;
91 * - all blocks belonging to the same loop are contiguous;
92 */
BuildBlocksLinearOrder()93 void LivenessAnalyzer::BuildBlocksLinearOrder()
94 {
95 ASSERT_PRINT(GetGraph()->IsAnalysisValid<DominatorsTree>(), "Liveness Analyzer needs valid Dom Tree");
96 auto size = GetGraph()->GetBlocksRPO().size();
97 linearBlocks_.reserve(size);
98 linearBlocks_.clear();
99 marker_ = GetGraph()->NewMarker();
100 ASSERT_PRINT(marker_ != UNDEF_MARKER, "There are no free markers");
101 if (GetGraph()->IsBytecodeOptimizer() && !GetGraph()->GetTryBeginBlocks().empty()) {
102 LinearizeBlocks<true>();
103 } else {
104 LinearizeBlocks<false>();
105 }
106 ASSERT(linearBlocks_.size() == size);
107 GetGraph()->EraseMarker(marker_);
108 ASSERT_PRINT(CheckLinearOrder(), "Linear block order isn't correct");
109
110 blockLiveSets_.resize(GetGraph()->GetVectorBlocks().size());
111 blockLiveRanges_.resize(GetGraph()->GetVectorBlocks().size());
112 }
113
114 /*
115 * Check if all forward edges of loop header were visited to get the resulting block order in RPO.
116 * Predecessors which are not in the same loop with a header - are forward edges.
117 */
AllForwardEdgesVisited(BasicBlock * block)118 bool LivenessAnalyzer::AllForwardEdgesVisited(BasicBlock *block)
119 {
120 if (!block->IsLoopHeader()) {
121 for (auto pred : block->GetPredsBlocks()) {
122 if (!pred->IsMarked(marker_)) {
123 return false;
124 }
125 }
126 } else {
127 // Head of irreducible loop can not dominate other blocks in the loop
128 if (block->GetLoop()->IsIrreducible()) {
129 return true;
130 }
131 // Predecessors which are not dominated - are forward edges,
132 for (auto pred : block->GetPredsBlocks()) {
133 if (!block->IsDominate(pred) && !pred->IsMarked(marker_)) {
134 return false;
135 }
136 }
137 }
138 return true;
139 }
140
141 template <bool USE_PC_ORDER>
LinearizeBlocks()142 void LivenessAnalyzer::LinearizeBlocks()
143 {
144 ArenaList<BasicBlock *> pending {GetGraph()->GetLocalAllocator()->Adapter()};
145 pending.push_back(GetGraph()->GetStartBlock());
146
147 while (!pending.empty()) {
148 auto current = pending.front();
149 pending.pop_front();
150
151 linearBlocks_.push_back(current);
152 current->SetMarker(marker_);
153
154 auto succs = current->GetSuccsBlocks();
155 // Each block is inserted into pending list before all already inserted blocks
156 // from the same loop. To process edges forwarding to a "true" branches successors
157 // should be processed in reversed order.
158 for (auto it = succs.rbegin(); it != succs.rend(); ++it) {
159 auto succ = *it;
160 if (succ->IsMarked(marker_) || !AllForwardEdgesVisited(succ)) {
161 continue;
162 }
163
164 if constexpr (USE_PC_ORDER) { // NOLINT(readability-braces-around-statements)
165 auto pcCompare = [succ](auto block) { return block->GetGuestPc() > succ->GetGuestPc(); };
166 auto insertBefore = std::find_if(pending.begin(), pending.end(), pcCompare);
167 pending.insert(insertBefore, succ);
168 } else {
169 // Insert successor right before the first block not from an inner loop.
170 // Such ordering guarantee that a loop and all it's inner loops will be processed
171 // before following edges leading to outer loop blocks.
172 auto isNotInnerLoop = [succ](auto block) { return !block->GetLoop()->IsInside(succ->GetLoop()); };
173 auto insertBefore = std::find_if(pending.begin(), pending.end(), isNotInnerLoop);
174 pending.insert(insertBefore, succ);
175 }
176 }
177 }
178 }
179
180 /*
181 * Check linear order correctness, using dominators tree
182 */
CheckLinearOrder()183 bool LivenessAnalyzer::CheckLinearOrder()
184 {
185 ArenaVector<size_t> blockPos(GetGraph()->GetVectorBlocks().size(), 0, GetAllocator()->Adapter());
186 size_t position = 0;
187 for (auto block : linearBlocks_) {
188 blockPos[block->GetId()] = position++;
189 }
190
191 for (auto block : linearBlocks_) {
192 if (block->GetDominator() != nullptr) {
193 ASSERT_PRINT(blockPos[block->GetDominator()->GetId()] < blockPos[block->GetId()],
194 "Each block should be visited after its dominator");
195 }
196 if (!block->IsTryEnd()) {
197 continue;
198 }
199 ASSERT(block->GetTryId() != INVALID_ID);
200 for (auto bb : linearBlocks_) {
201 if (bb->IsTry() && bb->GetTryId() == block->GetTryId()) {
202 ASSERT_PRINT(blockPos[bb->GetId()] < blockPos[block->GetId()],
203 "Each try-block should be visited before its try-end block");
204 }
205 }
206 }
207
208 return true;
209 }
210
211 /*
212 * Set lifetime number and visiting number for each instruction
213 */
BuildInstLifeNumbers()214 void LivenessAnalyzer::BuildInstLifeNumbers()
215 {
216 LifeNumber blockBegin;
217 LifeNumber lifeNumber = 0;
218 LinearNumber linearNumber = 0;
219
220 for (auto block : GetLinearizedBlocks()) {
221 blockBegin = lifeNumber;
222 // set the same number for each phi in the block
223 for (auto phi : block->PhiInsts()) {
224 phi->SetLinearNumber(linearNumber++);
225 SetInstLifeNumber(phi, lifeNumber);
226 CreateLifeIntervals(phi);
227 }
228 // ignore PHI instructions
229 instsByLifeNumber_.push_back(nullptr);
230 // set a unique number for each instruction in the block, differing by 2
231 // for the reason of adding spill/fill instructions
232 for (auto inst : block->Insts()) {
233 inst->SetLinearNumber(linearNumber++);
234 CreateLifeIntervals(inst);
235 if (IsPseudoUserOfMultiOutput(inst)) {
236 // Should be the same life number as pseudo-user, since actually they have the same definition
237 SetInstLifeNumber(inst, lifeNumber);
238 GetInstLifeIntervals(inst)->AddUsePosition(lifeNumber);
239 continue;
240 }
241 lifeNumber += LIFE_NUMBER_GAP;
242 SetInstLifeNumber(inst, lifeNumber);
243 SetUsePositions(inst, lifeNumber);
244 instsByLifeNumber_.push_back(inst);
245
246 if (inst->RequireTmpReg()) {
247 CreateIntervalForTemp(lifeNumber);
248 }
249 }
250 lifeNumber += LIFE_NUMBER_GAP;
251 SetBlockLiveRange(block, {blockBegin, lifeNumber});
252 }
253 }
254
SetUsePositions(Inst * userInst,LifeNumber lifeNumber)255 void LivenessAnalyzer::SetUsePositions(Inst *userInst, LifeNumber lifeNumber)
256 {
257 if (GetGraph()->IsBytecodeOptimizer()) {
258 return;
259 }
260 if (userInst->IsCatchPhi() || userInst->IsSaveState()) {
261 return;
262 }
263 for (size_t i = 0; i < userInst->GetInputsCount(); i++) {
264 auto location = userInst->GetLocation(i);
265 if (!location.IsAnyRegister()) {
266 continue;
267 }
268 auto inputInst = userInst->GetDataFlowInput(i);
269 auto li = GetInstLifeIntervals(inputInst);
270 if (location.IsRegisterValid()) {
271 useTable_.AddUseOnFixedLocation(inputInst, location, lifeNumber);
272 } else if (location.IsUnallocatedRegister()) {
273 li->AddUsePosition(lifeNumber);
274 }
275 }
276
277 // Constant can be defined without register
278 if (userInst->IsConst() && g_options.IsCompilerRematConst()) {
279 return;
280 }
281
282 // If instruction required dst register, set use position in the beginning of the interval
283 auto li = GetInstLifeIntervals(userInst);
284 if (!li->NoDest()) {
285 li->AddUsePosition(lifeNumber);
286 }
287 }
288
289 /*
290 * Get lifetime intervals for each instruction
291 */
BuildInstLifeIntervals()292 void LivenessAnalyzer::BuildInstLifeIntervals()
293 {
294 for (auto it = GetLinearizedBlocks().rbegin(); it != GetLinearizedBlocks().rend(); it++) {
295 auto block = *it;
296 auto liveSet = GetInitInstLiveSet(block);
297 ProcessBlockLiveInstructions(block, liveSet);
298 }
299 }
300
301 /*
302 * The initial set of live instructions in the `block` is the the union of all live instructions at the beginning of
303 * the block's successors.
304 * Also for each phi-instruction of the successors: input corresponding to the `block` is added to the live set.
305 */
GetInitInstLiveSet(BasicBlock * block)306 InstLiveSet *LivenessAnalyzer::GetInitInstLiveSet(BasicBlock *block)
307 {
308 unsigned instructionCount = instLifeIntervals_.size();
309 auto liveSet = GetAllocator()->New<InstLiveSet>(instructionCount, GetAllocator());
310 for (auto succ : block->GetSuccsBlocks()) {
311 // catch-begin is pseudo successor, its live set will be processed for blocks with throwable instructions
312 if (succ->IsCatchBegin()) {
313 continue;
314 }
315 liveSet->Union(GetBlockLiveSet(succ));
316 for (auto phi : succ->PhiInsts()) {
317 auto phiInput = phi->CastToPhi()->GetPhiDataflowInput(block);
318 liveSet->Add(phiInput->GetLinearNumber());
319 }
320 }
321
322 // if basic block contains throwable instruction, all instructions live in the catch-handlers should be live in this
323 // block
324 if (auto inst = block->GetFistThrowableInst(); inst != nullptr) {
325 auto handlers = GetGraph()->GetThrowableInstHandlers(inst);
326 for (auto catchHandler : handlers) {
327 liveSet->Union(GetBlockLiveSet(catchHandler));
328 }
329 }
330 return liveSet;
331 }
332
GetLoopEnd(Loop * loop)333 LifeNumber LivenessAnalyzer::GetLoopEnd(Loop *loop)
334 {
335 LifeNumber loopEnd = 0;
336 // find max LifeNumber of inner loops
337 for (auto inner : loop->GetInnerLoops()) {
338 loopEnd = std::max(loopEnd, GetLoopEnd(inner));
339 }
340 // find max LifeNumber of back_edges
341 for (auto backEdge : loop->GetBackEdges()) {
342 loopEnd = std::max(loopEnd, GetBlockLiveRange(backEdge).GetEnd());
343 }
344 return loopEnd;
345 }
346
347 /*
348 * Append and adjust the lifetime intervals for each instruction live in the block and for their inputs
349 */
ProcessBlockLiveInstructions(BasicBlock * block,InstLiveSet * liveSet)350 void LivenessAnalyzer::ProcessBlockLiveInstructions(BasicBlock *block, InstLiveSet *liveSet)
351 {
352 // For each live instruction set initial life range equals to the block life range
353 for (auto &interval : instLifeIntervals_) {
354 if (liveSet->IsSet(interval->GetInst()->GetLinearNumber())) {
355 interval->AppendRange(GetBlockLiveRange(block));
356 }
357 }
358
359 for (Inst *inst : block->InstsSafeReverse()) {
360 // Shorten instruction lifetime to the position where its defined
361 // and remove from the block's live set
362 auto instLifeNumber = GetInstLifeNumber(inst);
363 auto interval = GetInstLifeIntervals(inst);
364 interval->StartFrom(instLifeNumber);
365 liveSet->Remove(inst->GetLinearNumber());
366 if (inst->IsCatchPhi()) {
367 // catch-phi's liveness should overlap all linked try blocks' livenesses
368 for (auto pred : inst->GetBasicBlock()->GetPredsBlocks()) {
369 instLifeNumber = std::min(instLifeNumber, GetBlockLiveRange(pred).GetBegin());
370 }
371 interval->StartFrom(instLifeNumber);
372 AdjustCatchPhiInputsLifetime(inst);
373 } else {
374 if (inst->GetOpcode() == Opcode::LiveOut) {
375 ProcessOpcodeLiveOut(block, interval, instLifeNumber);
376 }
377 auto currentLiveRange = LiveRange {GetBlockLiveRange(block).GetBegin(), instLifeNumber};
378 AdjustInputsLifetime(inst, currentLiveRange, liveSet);
379 }
380
381 BlockFixedRegisters(inst);
382 }
383
384 // The lifetime interval of phis instructions starts at the beginning of the block
385 for (auto phi : block->PhiInsts()) {
386 liveSet->Remove(phi->GetLinearNumber());
387 }
388
389 // All instructions live at the beginning ot the loop header
390 // must be live for the entire loop
391 if (block->IsLoopHeader()) {
392 LifeNumber loopEndPosition = GetLoopEnd(block->GetLoop());
393
394 for (auto &interval : instLifeIntervals_) {
395 if (liveSet->IsSet(interval->GetInst()->GetLinearNumber())) {
396 interval->AppendGroupRange({GetBlockLiveRange(block).GetBegin(), loopEndPosition});
397 }
398 }
399 }
400 SetBlockLiveSet(block, liveSet);
401 }
402
ProcessOpcodeLiveOut(BasicBlock * block,LifeIntervals * interval,LifeNumber instLifeNumber)403 void LivenessAnalyzer::ProcessOpcodeLiveOut(BasicBlock *block, LifeIntervals *interval, LifeNumber instLifeNumber)
404 {
405 if (block->GetSuccsBlocks().size() == 1 && block->GetSuccessor(0)->IsEndBlock()) {
406 interval->AppendRange({instLifeNumber, GetBlockLiveRange(block).GetEnd()});
407 } else {
408 interval->AppendRange({instLifeNumber, GetBlockLiveRange(GetGraph()->GetEndBlock()).GetBegin()});
409 }
410 }
411
412 /* static */
GetPropagatedLiveRange(Inst * inst,LiveRange liveRange)413 LiveRange LivenessAnalyzer::GetPropagatedLiveRange(Inst *inst, LiveRange liveRange)
414 {
415 /*
416 * Implicit null check encoded as no-op and if the reference to check is null
417 * then SIGSEGV will be raised at the first (closest) user. Regmap generated for
418 * NullCheck's SaveState should be valid at that user so we need to extend
419 * life intervals of SaveState's inputs until NullCheck user.
420 */
421 if (inst->IsNullCheck() && !inst->GetUsers().Empty() && inst->CastToNullCheck()->IsImplicit()) {
422 auto extendUntil = std::numeric_limits<LifeNumber>::max();
423 for (auto &user : inst->GetUsers()) {
424 // Skip the users dominating the NullCheck as their live ranges
425 // start earlier than NullCheck's live range.
426 if (!user.GetInst()->IsDominate(inst)) {
427 auto li = GetInstLifeIntervals(user.GetInst());
428 ASSERT(li != nullptr);
429 extendUntil = std::min<LifeNumber>(extendUntil, li->GetBegin() + 1);
430 }
431 }
432 liveRange.SetEnd(extendUntil);
433 return liveRange;
434 }
435 /*
436 * We need to propagate liveness for instruction with CallRuntime to save registers before call;
437 * Otherwise, we will not be able to restore the value of the virtual registers
438 */
439 if (inst->IsPropagateLiveness()) {
440 liveRange.SetEnd(liveRange.GetEnd() + 1);
441 } else if (inst->GetOpcode() == Opcode::ReturnInlined && inst->CastToReturnInlined()->IsExtendedLiveness()) {
442 /*
443 * [ReturnInlined]
444 * [ReturnInlined]
445 * ...
446 * [Deoptimize/Throw]
447 *
448 * In this case we propagate ReturnInlined inputs liveness up to the end of basic block
449 */
450 liveRange.SetEnd(GetBlockLiveRange(inst->GetBasicBlock()).GetEnd());
451 }
452 return liveRange;
453 }
454
455 /*
456 * Adjust instruction inputs lifetime and add them to the block's live set
457 */
AdjustInputsLifetime(Inst * inst,LiveRange liveRange,InstLiveSet * liveSet)458 void LivenessAnalyzer::AdjustInputsLifetime(Inst *inst, LiveRange liveRange, InstLiveSet *liveSet)
459 {
460 for (auto input : inst->GetInputs()) {
461 auto inputInst = inst->GetDataFlowInput(input.GetInst());
462 liveSet->Add(inputInst->GetLinearNumber());
463 SetInputRange(inst, inputInst, liveRange);
464 }
465
466 // Extend SaveState inputs lifetime to the end of SaveState's lifetime
467 if (inst->RequireState()) {
468 auto saveState = inst->GetSaveState();
469 auto propagatedRange = GetPropagatedLiveRange(inst, liveRange);
470 while (true) {
471 ASSERT(saveState != nullptr);
472 for (auto ssInput : saveState->GetInputs()) {
473 auto inputInst = saveState->GetDataFlowInput(ssInput.GetInst());
474 liveSet->Add(inputInst->GetLinearNumber());
475 GetInstLifeIntervals(inputInst)->AppendRange(propagatedRange);
476 }
477 auto callerInst = saveState->GetCallerInst();
478 if (callerInst == nullptr) {
479 break;
480 }
481 saveState = callerInst->GetSaveState();
482 }
483 }
484
485 // Handle CatchPhi inputs associated with inst
486 auto range = pendingCatchPhiInputs_.equal_range(inst);
487 for (auto it = range.first; it != range.second; ++it) {
488 auto throwableInput = it->second;
489 auto throwableInputInterval = GetInstLifeIntervals(throwableInput);
490 liveSet->Add(throwableInput->GetLinearNumber());
491 throwableInputInterval->AppendRange(liveRange);
492 }
493 pendingCatchPhiInputs_.erase(inst);
494 }
495
496 /*
497 * Increase ref-input liveness in the 'no-async-jit' mode, since GC can be triggered and delete ref during callee-method
498 * compilation
499 */
SetInputRange(const Inst * inst,const Inst * input,LiveRange liveRange) const500 void LivenessAnalyzer::SetInputRange(const Inst *inst, const Inst *input, LiveRange liveRange) const
501 {
502 if (hasSafepointDuringCall_ && inst->IsCall() && DataType::IsReference(input->GetType())) {
503 GetInstLifeIntervals(input)->AppendRange(liveRange.GetBegin(), liveRange.GetEnd() + 1U);
504 } else {
505 GetInstLifeIntervals(input)->AppendRange(liveRange);
506 }
507 }
508
509 /*
510 * CatchPhi does not handle inputs as regular instructions - instead of performing
511 * some action at CatchPhi's definition copy instruction are added before throwable instructions.
512 * Instead of extending input life interval until CatchPhi it is extended until throwable instruction.
513 */
AdjustCatchPhiInputsLifetime(Inst * inst)514 void LivenessAnalyzer::AdjustCatchPhiInputsLifetime(Inst *inst)
515 {
516 auto catchPhi = inst->CastToCatchPhi();
517
518 for (ssize_t inputIdx = catchPhi->GetInputsCount() - 1; inputIdx >= 0; inputIdx--) {
519 auto inputInst = catchPhi->GetDataFlowInput(inputIdx);
520 auto throwableInst = const_cast<Inst *>(catchPhi->GetThrowableInst(inputIdx));
521
522 pendingCatchPhiInputs_.insert({throwableInst, inputInst});
523 }
524 }
525
SetInstLifeNumber(const Inst * inst,LifeNumber number)526 void LivenessAnalyzer::SetInstLifeNumber([[maybe_unused]] const Inst *inst, LifeNumber number)
527 {
528 ASSERT(instLifeNumbers_.size() == inst->GetLinearNumber());
529 instLifeNumbers_.push_back(number);
530 }
531
GetInstLifeNumber(const Inst * inst) const532 LifeNumber LivenessAnalyzer::GetInstLifeNumber(const Inst *inst) const
533 {
534 return instLifeNumbers_[inst->GetLinearNumber()];
535 }
536
537 /*
538 * Create new lifetime intervals for instruction, check that instruction linear number is equal to intervals
539 * position in vector
540 */
CreateLifeIntervals(Inst * inst)541 void LivenessAnalyzer::CreateLifeIntervals(Inst *inst)
542 {
543 ASSERT(!finalized_);
544 ASSERT(inst->GetLinearNumber() == instLifeIntervals_.size());
545 auto interval = GetAllocator()->New<LifeIntervals>(GetAllocator(), inst);
546 instLifeIntervals_.push_back(interval);
547 }
548
CreateIntervalForTemp(LifeNumber ln)549 void LivenessAnalyzer::CreateIntervalForTemp(LifeNumber ln)
550 {
551 ASSERT(!finalized_);
552 auto interval = GetAllocator()->New<LifeIntervals>(GetAllocator());
553 interval->AppendRange({ln - 1, ln});
554 interval->AddUsePosition(ln);
555 // DataType is INT64, since general register is reserved (for 32-bits arch will be converted to INT32)
556 interval->SetType(ConvertRegType(GetGraph(), DataType::INT64));
557 intervalsForTemps_.push_back(interval);
558 }
559
GetInstLifeIntervals(const Inst * inst) const560 LifeIntervals *LivenessAnalyzer::GetInstLifeIntervals(const Inst *inst) const
561 {
562 ASSERT(inst->GetLinearNumber() != INVALID_LINEAR_NUM);
563 return instLifeIntervals_[inst->GetLinearNumber()];
564 }
565
SetBlockLiveRange(BasicBlock * block,LiveRange lifeRange)566 void LivenessAnalyzer::SetBlockLiveRange(BasicBlock *block, LiveRange lifeRange)
567 {
568 blockLiveRanges_[block->GetId()] = lifeRange;
569 }
570
GetLinearizedBlocks() const571 const ArenaVector<BasicBlock *> &LivenessAnalyzer::GetLinearizedBlocks() const
572 {
573 return linearBlocks_;
574 }
575
GetInstByLifeNumber(LifeNumber ln) const576 Inst *LivenessAnalyzer::GetInstByLifeNumber(LifeNumber ln) const
577 {
578 return instsByLifeNumber_[ln / LIFE_NUMBER_GAP];
579 }
580
GetBlockCoversPoint(LifeNumber ln) const581 BasicBlock *LivenessAnalyzer::GetBlockCoversPoint(LifeNumber ln) const
582 {
583 for (auto bb : linearBlocks_) {
584 if (GetBlockLiveRange(bb).Contains(ln)) {
585 return bb;
586 }
587 }
588 return nullptr;
589 }
590
Cleanup()591 void LivenessAnalyzer::Cleanup()
592 {
593 for (auto *interv : instLifeIntervals_) {
594 if (!interv->IsPhysical() && !interv->IsPreassigned()) {
595 interv->ClearLocation();
596 }
597 if (interv->GetSibling() != nullptr) {
598 interv->MergeSibling();
599 }
600 }
601 }
602
Finalize()603 void LivenessAnalyzer::Finalize()
604 {
605 for (auto *interv : instLifeIntervals_) {
606 interv->Finalize();
607 }
608 for (auto *interv : intervalsForTemps_) {
609 interv->Finalize();
610 }
611 #ifndef NDEBUG
612 finalized_ = true;
613 #endif
614 }
615
GetLifeIntervals() const616 const ArenaVector<LifeIntervals *> &LivenessAnalyzer::GetLifeIntervals() const
617 {
618 return instLifeIntervals_;
619 }
620
GetBlockLiveRange(const BasicBlock * block) const621 LiveRange LivenessAnalyzer::GetBlockLiveRange(const BasicBlock *block) const
622 {
623 return blockLiveRanges_[block->GetId()];
624 }
625
SetBlockLiveSet(BasicBlock * block,InstLiveSet * liveSet)626 void LivenessAnalyzer::SetBlockLiveSet(BasicBlock *block, InstLiveSet *liveSet)
627 {
628 blockLiveSets_[block->GetId()] = liveSet;
629 }
630
GetBlockLiveSet(BasicBlock * block) const631 InstLiveSet *LivenessAnalyzer::GetBlockLiveSet(BasicBlock *block) const
632 {
633 return blockLiveSets_[block->GetId()];
634 }
635
GetUseTable() const636 const UseTable &LivenessAnalyzer::GetUseTable() const
637 {
638 return useTable_;
639 }
640
GetTmpRegInterval(const Inst * inst)641 LifeIntervals *LivenessAnalyzer::GetTmpRegInterval(const Inst *inst)
642 {
643 auto instLn = GetInstLifeNumber(inst);
644 auto it = std::find_if(intervalsForTemps_.begin(), intervalsForTemps_.end(),
645 [instLn](auto li) { return li->GetBegin() == instLn - 1; });
646 return it == intervalsForTemps_.end() ? nullptr : *it;
647 }
648
GetPassName() const649 const char *LivenessAnalyzer::GetPassName() const
650 {
651 return "LivenessAnalysis";
652 }
653
GetBlocksCount() const654 size_t LivenessAnalyzer::GetBlocksCount() const
655 {
656 return blockLiveRanges_.size();
657 }
658
GetAllocator()659 ArenaAllocator *LivenessAnalyzer::GetAllocator()
660 {
661 return allocator_;
662 }
663
DumpLifeIntervals(std::ostream & out) const664 void LivenessAnalyzer::DumpLifeIntervals(std::ostream &out) const
665 {
666 for (auto bb : GetGraph()->GetBlocksRPO()) {
667 if (bb->GetId() >= GetBlocksCount()) {
668 continue;
669 }
670 auto blockRange = GetBlockLiveRange(bb);
671 out << "BB " << bb->GetId() << "\t" << blockRange.ToString() << std::endl;
672
673 for (auto inst : bb->AllInsts()) {
674 if (inst->GetLinearNumber() == INVALID_LINEAR_NUM) {
675 continue;
676 }
677 auto interval = GetInstLifeIntervals(inst);
678
679 out << "v" << inst->GetId() << "\t";
680 for (auto sibling = interval; sibling != nullptr; sibling = sibling->GetSibling()) {
681 out << sibling->ToString<false>() << "@ " << sibling->GetLocation().ToString(GetGraph()->GetArch())
682 << "; ";
683 }
684 out << std::endl;
685 }
686 }
687
688 out << "Temps:" << std::endl;
689 for (auto interval : intervalsForTemps_) {
690 out << interval->ToString<false>() << "@ " << interval->GetLocation().ToString(GetGraph()->GetArch())
691 << std::endl;
692 }
693 DumpLocationsUsage(out);
694 }
695
DumpLocationsUsage(std::ostream & out) const696 void LivenessAnalyzer::DumpLocationsUsage(std::ostream &out) const
697 {
698 std::map<Register, std::vector<LifeIntervals *>> regsIntervals;
699 std::map<Register, std::vector<LifeIntervals *>> vregsIntervals;
700 std::map<Register, std::vector<LifeIntervals *>> slotsIntervals;
701 for (auto &interval : instLifeIntervals_) {
702 for (auto sibling = interval; sibling != nullptr; sibling = sibling->GetSibling()) {
703 auto location = sibling->GetLocation();
704 if (location.IsFpRegister()) {
705 ASSERT(DataType::IsFloatType(interval->GetType()));
706 vregsIntervals[location.GetValue()].push_back(sibling);
707 } else if (location.IsRegister()) {
708 regsIntervals[location.GetValue()].push_back(sibling);
709 } else if (location.IsStack()) {
710 slotsIntervals[location.GetValue()].push_back(sibling);
711 }
712 }
713 }
714
715 for (auto intervalsMap : {®sIntervals, &vregsIntervals, &slotsIntervals}) {
716 std::string locSymbol;
717 if (intervalsMap == ®sIntervals) {
718 out << std::endl << "Registers intervals" << std::endl;
719 locSymbol = "r";
720 } else if (intervalsMap == &vregsIntervals) {
721 out << std::endl << "Vector registers intervals" << std::endl;
722 locSymbol = "vr";
723 } else {
724 ASSERT(intervalsMap == &slotsIntervals);
725 out << std::endl << "Stack slots intervals" << std::endl;
726 locSymbol = "s";
727 }
728
729 if (intervalsMap->empty()) {
730 out << "-" << std::endl;
731 continue;
732 }
733 for (auto &[reg, intervals] : *intervalsMap) {
734 std::sort(intervals.begin(), intervals.end(),
735 [](const auto &lhs, const auto &rhs) { return lhs->GetBegin() < rhs->GetBegin(); });
736 out << locSymbol << std::to_string(reg) << ": ";
737 auto delim = "";
738 for (auto &interval : intervals) {
739 out << delim << interval->ToString<false>();
740 delim = "; ";
741 }
742 out << std::endl;
743 }
744 }
745 }
746
BlockFixedRegisters(Inst * inst)747 void LivenessAnalyzer::BlockFixedRegisters(Inst *inst)
748 {
749 if (GetGraph()->IsBytecodeOptimizer() || GetGraph()->GetMode().IsFastPath()) {
750 return;
751 }
752
753 auto blockFrom = GetInstLifeNumber(inst);
754 if (IsCallBlockingRegisters(inst)) {
755 BlockPhysicalRegisters<false>(blockFrom);
756 BlockPhysicalRegisters<true>(blockFrom);
757 }
758 for (auto i = 0U; i < inst->GetInputsCount(); ++i) {
759 BlockFixedLocationRegister(inst->GetLocation(i), blockFrom);
760 }
761 BlockFixedLocationRegister(inst->GetDstLocation(), blockFrom);
762
763 if (inst->IsParameter()) {
764 // Block a register starting from the position preceding entry block start to
765 // correctly handle register blocked by "current" instruction from registers blocked
766 // by other instructions during register allocation.
767 constexpr auto FIRST_AVAILABLE_LIFE_NUMBER = LIFE_NUMBER_GAP - 1;
768 // Registers holding parameters should be blocked starting from the beginning of entry block
769 // to avoid its assignment to parameter instructions in case of high register pressure.
770 // The required interval is blocked using two calls to correctly mark first use position of a
771 // blocked register.
772 BlockFixedLocationRegister(inst->CastToParameter()->GetLocationData().GetSrc(), blockFrom);
773 BlockFixedLocationRegister(inst->CastToParameter()->GetLocationData().GetSrc(), FIRST_AVAILABLE_LIFE_NUMBER,
774 blockFrom - 1, false);
775 // Block second parameter register that contains number of actual arguments in case of
776 // dynamic methods as it is used in parameter's code generation
777 if (GetGraph()->GetMode().IsDynamicMethod() &&
778 GetGraph()->FindParameter(ParameterInst::DYNAMIC_NUM_ARGS) == nullptr) {
779 BlockReg<false>(Target(GetGraph()->GetArch()).GetParamRegId(1), blockFrom, blockFrom + 1U, true);
780 BlockReg<false>(Target(GetGraph()->GetArch()).GetParamRegId(1), FIRST_AVAILABLE_LIFE_NUMBER, blockFrom - 1,
781 false);
782 }
783 }
784 }
785
786 template <bool IS_FP>
BlockPhysicalRegisters(LifeNumber blockFrom)787 void LivenessAnalyzer::BlockPhysicalRegisters(LifeNumber blockFrom)
788 {
789 auto arch = GetGraph()->GetArch();
790 RegMask callerRegs {GetCallerRegsMask(arch, IS_FP)};
791 for (auto reg = GetFirstCallerReg(arch, IS_FP); reg <= GetLastCallerReg(arch, IS_FP); ++reg) {
792 if (callerRegs.test(reg)) {
793 BlockReg<IS_FP>(reg, blockFrom, blockFrom + 1U, true);
794 }
795 }
796 }
797
BlockFixedLocationRegister(Location location,LifeNumber ln)798 void LivenessAnalyzer::BlockFixedLocationRegister(Location location, LifeNumber ln)
799 {
800 BlockFixedLocationRegister(location, ln, ln + 1U, true);
801 }
802
BlockFixedLocationRegister(Location location,LifeNumber blockFrom,LifeNumber blockTo,bool isUse)803 void LivenessAnalyzer::BlockFixedLocationRegister(Location location, LifeNumber blockFrom, LifeNumber blockTo,
804 bool isUse)
805 {
806 if (location.IsRegister() && location.IsRegisterValid()) {
807 BlockReg<false>(location.GetValue(), blockFrom, blockTo, isUse);
808 } else if (location.IsFpRegister() && location.IsRegisterValid()) {
809 BlockReg<true>(location.GetValue(), blockFrom, blockTo, isUse);
810 }
811 }
812
813 template <bool IS_FP>
BlockReg(Register reg,LifeNumber blockFrom,LifeNumber blockTo,bool isUse)814 void LivenessAnalyzer::BlockReg(Register reg, LifeNumber blockFrom, LifeNumber blockTo, bool isUse)
815 {
816 ASSERT(!finalized_);
817 auto &intervals = IS_FP ? physicalVectorIntervals_ : physicalGeneralIntervals_;
818 auto interval = intervals.at(reg);
819 if (interval == nullptr) {
820 interval = GetGraph()->GetAllocator()->New<LifeIntervals>(GetGraph()->GetAllocator());
821 interval->SetPhysicalReg(reg, IS_FP ? DataType::FLOAT64 : DataType::UINT64);
822 intervals.at(reg) = interval;
823 }
824 interval->AppendRange(blockFrom, blockTo);
825 if (isUse) {
826 interval->AddUsePosition(blockFrom);
827 }
828 }
829
IsCallBlockingRegisters(Inst * inst) const830 bool LivenessAnalyzer::IsCallBlockingRegisters(Inst *inst) const
831 {
832 if (inst->IsCall() && !inst->IsLaunchCall() && !static_cast<CallInst *>(inst)->IsInlined()) {
833 return true;
834 }
835 if (inst->IsIntrinsic() && inst->CastToIntrinsic()->IsNativeCall()) {
836 return true;
837 }
838 return false;
839 }
840
SplitAt(LifeNumber ln,ArenaAllocator * alloc)841 LifeIntervals *LifeIntervals::SplitAt(LifeNumber ln, ArenaAllocator *alloc)
842 {
843 ASSERT(!IsPhysical());
844 ASSERT(ln > GetBegin() && ln <= GetEnd());
845 auto splitChild = alloc->New<LifeIntervals>(alloc, GetInst());
846 #ifndef NDEBUG
847 ASSERT(finalized_);
848 splitChild->finalized_ = true;
849 #endif
850 if (sibling_ != nullptr) {
851 splitChild->sibling_ = sibling_;
852 }
853 splitChild->isSplitSibling_ = true;
854
855 sibling_ = splitChild;
856 splitChild->SetType(GetType());
857
858 auto i = liveRanges_.size();
859 while (i > 0 && liveRanges_[i - 1].GetEnd() <= ln) {
860 i--;
861 }
862 if (i > 0) {
863 auto &range = liveRanges_[i - 1];
864 if (range.GetBegin() < ln) {
865 splitChild->AppendRange(range.GetBegin(), ln);
866 range.SetBegin(ln);
867 }
868 }
869 splitChild->liveRanges_.insert(splitChild->liveRanges_.end(), liveRanges_.begin() + i, liveRanges_.end());
870 liveRanges_.erase(liveRanges_.begin() + i, liveRanges_.end());
871 std::swap(liveRanges_, splitChild->liveRanges_);
872
873 // Move use positions to the child
874 auto it = std::lower_bound(usePositions_.begin(), usePositions_.end(), ln);
875 splitChild->usePositions_.insert(splitChild->usePositions_.end(), it, usePositions_.end());
876 usePositions_.erase(it, usePositions_.end());
877
878 return splitChild;
879 }
880
SplitAroundUses(ArenaAllocator * alloc)881 void LifeIntervals::SplitAroundUses(ArenaAllocator *alloc)
882 {
883 if (GetUsePositions().empty()) {
884 return;
885 }
886 auto use = *GetUsePositions().begin();
887 if (use > GetBegin() + 1) {
888 auto split = SplitAt(use - 1, alloc);
889 split->SplitAroundUses(alloc);
890 } else if (use < GetEnd() - 1) {
891 auto split = SplitAt(use + 1, alloc);
892 split->SplitAroundUses(alloc);
893 }
894 }
895
MergeSibling()896 void LifeIntervals::MergeSibling()
897 {
898 ASSERT(sibling_ != nullptr);
899 #ifndef NDEBUG
900 ASSERT(finalized_);
901 ASSERT(sibling_->finalized_);
902 if (!usePositions_.empty() && !sibling_->usePositions_.empty()) {
903 ASSERT(usePositions_.back() <= sibling_->usePositions_.front());
904 }
905 #endif
906 for (auto range : liveRanges_) {
907 sibling_->AppendRange(range);
908 }
909 liveRanges_ = std::move(sibling_->liveRanges_);
910
911 for (auto &usePos : sibling_->usePositions_) {
912 AddUsePosition(usePos);
913 }
914 sibling_ = nullptr;
915 }
916
FindSiblingAt(LifeNumber ln)917 LifeIntervals *LifeIntervals::FindSiblingAt(LifeNumber ln)
918 {
919 ASSERT(!IsSplitSibling());
920 for (auto head = this; head != nullptr; head = head->GetSibling()) {
921 if (head->GetBegin() <= ln && ln <= head->GetEnd()) {
922 return head;
923 }
924 }
925 return nullptr;
926 }
927
Intersects(const LiveRange & range) const928 bool LifeIntervals::Intersects(const LiveRange &range) const
929 {
930 return // the interval starts within the range
931 (range.GetBegin() <= GetBegin() && GetBegin() <= range.GetEnd()) ||
932 // the interval ends within the range
933 (range.GetBegin() <= GetEnd() && GetEnd() <= range.GetEnd()) ||
934 // the range is fully covered by the interval
935 (GetBegin() <= range.GetBegin() && range.GetEnd() <= GetEnd());
936 }
937
GetFirstIntersectionWith(const LifeIntervals * other,LifeNumber searchFrom) const938 LifeNumber LifeIntervals::GetFirstIntersectionWith(const LifeIntervals *other, LifeNumber searchFrom) const
939 {
940 for (auto it = GetRanges().rbegin(); it != GetRanges().rend(); it++) {
941 auto range = *it;
942 if (range.GetEnd() <= searchFrom) {
943 continue;
944 }
945 for (auto otherIt = other->GetRanges().rbegin(); otherIt != other->GetRanges().rend(); otherIt++) {
946 auto otherRange = *otherIt;
947 if (otherRange.GetEnd() <= searchFrom) {
948 continue;
949 }
950 auto rangeBegin = std::max<LifeNumber>(searchFrom, range.GetBegin());
951 auto otherRangeBegin = std::max<LifeNumber>(searchFrom, otherRange.GetBegin());
952 if (rangeBegin <= otherRangeBegin && otherRangeBegin < range.GetEnd()) {
953 // [range]
954 // [other]
955 return otherRangeBegin;
956 // NOLINTNEXTLINE(readability-else-after-return)
957 } else if (rangeBegin > otherRangeBegin && rangeBegin < otherRange.GetEnd()) {
958 // [range]
959 // [other]
960 return rangeBegin;
961 }
962 }
963 }
964 return INVALID_LIFE_NUMBER;
965 }
966
FindRangeCoveringPosition(LifeNumber ln,LiveRange * dst) const967 bool LifeIntervals::FindRangeCoveringPosition(LifeNumber ln, LiveRange *dst) const
968 {
969 for (auto &range : GetRanges()) {
970 if (range.Contains(ln)) {
971 *dst = range;
972 return true;
973 }
974 }
975
976 return false;
977 }
978
GetSpillWeightAt(const LivenessAnalyzer & la,LifeNumber ln)979 static float GetSpillWeightAt(const LivenessAnalyzer &la, LifeNumber ln)
980 {
981 static constexpr float LOOP_MULT = 10.0;
982 auto block = la.GetBlockCoversPoint(ln);
983 return std::pow<float>(LOOP_MULT, block->GetLoop()->GetDepth());
984 }
985
CalcSpillWeight(const LivenessAnalyzer & la,LifeIntervals * interval)986 float CalcSpillWeight(const LivenessAnalyzer &la, LifeIntervals *interval)
987 {
988 if (interval->IsPhysical()) {
989 return std::numeric_limits<float>::max();
990 }
991
992 size_t length = interval->GetEnd() - interval->GetBegin();
993 // Interval can't be splitted
994 if (length <= LIFE_NUMBER_GAP) {
995 return std::numeric_limits<float>::max();
996 }
997
998 // Constant intervals are the first candidates to spill
999 if (interval->GetInst()->IsConst()) {
1000 return std::numeric_limits<float>::min();
1001 }
1002
1003 float useWeight = 0;
1004 if (interval->GetInst()->IsPhi()) {
1005 useWeight += GetSpillWeightAt(la, interval->GetBegin());
1006 }
1007
1008 for (auto use : interval->GetUsePositions()) {
1009 if (use == interval->GetBegin()) {
1010 useWeight += GetSpillWeightAt(la, use + 1);
1011 } else {
1012 useWeight += GetSpillWeightAt(la, use - 1);
1013 }
1014 }
1015 return useWeight;
1016 }
1017
1018 } // namespace ark::compiler
1019