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