• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "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 
23 namespace panda::compiler {
LivenessAnalyzer(Graph * graph)24 LivenessAnalyzer::LivenessAnalyzer(Graph *graph)
25     : Analysis(graph),
26       allocator_(graph->GetAllocator()),
27       linear_blocks_(graph->GetAllocator()->Adapter()),
28       inst_life_numbers_(graph->GetAllocator()->Adapter()),
29       inst_life_intervals_(graph->GetAllocator()->Adapter()),
30       insts_by_life_number_(graph->GetAllocator()->Adapter()),
31       block_live_ranges_(graph->GetAllocator()->Adapter()),
32       block_live_sets_(graph->GetLocalAllocator()->Adapter()),
33       pending_catch_phi_inputs_(graph->GetAllocator()->Adapter()),
34       physical_general_intervals_(graph->GetAllocator()->Adapter()),
35       physical_vector_intervals_(graph->GetAllocator()->Adapter()),
36       use_table_(graph->GetAllocator()),
37       has_safepoint_during_call_(graph->GetRuntime()->HasSafepointDuringCall())
38 {
39 }
40 
RunImpl()41 bool LivenessAnalyzer::RunImpl()
42 {
43     GetGraph()->RunPass<DominatorsTree>();
44     GetGraph()->RunPass<LoopAnalyzer>();
45     ResetLiveness();
46     BuildBlocksLinearOrder();
47     BuildInstLifeNumbers();
48     BuildInstLifeIntervals();
49     if (!pending_catch_phi_inputs_.empty()) {
50         COMPILER_LOG(ERROR, LIVENESS_ANALYZER)
51             << "Graph contains CatchPhi instructions whose inputs were not processed";
52         return false;
53     }
54     std::copy_if(physical_general_intervals_.begin(), physical_general_intervals_.end(),
55                  std::back_inserter(inst_life_intervals_), [](auto li) { return li != nullptr; });
56     std::copy_if(physical_vector_intervals_.begin(), physical_vector_intervals_.end(),
57                  std::back_inserter(inst_life_intervals_), [](auto li) { return li != nullptr; });
58     COMPILER_LOG(DEBUG, LIVENESS_ANALYZER) << "Liveness analysis is constructed";
59     return true;
60 }
61 
ResetLiveness()62 void LivenessAnalyzer::ResetLiveness()
63 {
64     inst_life_numbers_.clear();
65     inst_life_intervals_.clear();
66     block_live_sets_.clear();
67     block_live_ranges_.clear();
68     physical_general_intervals_.clear();
69     physical_vector_intervals_.clear();
70     if (GetGraph()->GetArch() != Arch::NONE) {
71         physical_general_intervals_.resize(REGISTERS_NUM);
72         physical_vector_intervals_.resize(VREGISTERS_NUM);
73     }
74 }
75 
76 /*
77  * Linear blocks order means:
78  * - all dominators of a block are visiting before this block;
79  * - all blocks belonging to the same loop are contiguous;
80  */
BuildBlocksLinearOrder()81 void LivenessAnalyzer::BuildBlocksLinearOrder()
82 {
83     ASSERT_PRINT(GetGraph()->IsAnalysisValid<DominatorsTree>(), "Liveness Analyzer needs valid Dom Tree");
84     auto size = GetGraph()->GetBlocksRPO().size();
85     linear_blocks_.reserve(size);
86     linear_blocks_.clear();
87     marker_ = GetGraph()->NewMarker();
88     ASSERT_PRINT(marker_ != UNDEF_MARKER, "There are no free markers");
89     if (GetGraph()->IsBytecodeOptimizer() && !GetGraph()->GetTryBeginBlocks().empty()) {
90         LinearizeBlocks<true>();
91     } else {
92         LinearizeBlocks<false>();
93     }
94     ASSERT(linear_blocks_.size() == size);
95     GetGraph()->EraseMarker(marker_);
96     ASSERT_PRINT(CheckLinearOrder(), "Linear block order isn't correct");
97 
98     block_live_sets_.resize(GetGraph()->GetVectorBlocks().size());
99     block_live_ranges_.resize(GetGraph()->GetVectorBlocks().size());
100 }
101 
102 /*
103  * Check if all forward edges of loop header were visited to get the resulting block order in RPO.
104  * Predecessors which are not in the same loop with a header - are forward edges.
105  */
AllForwardEdgesVisited(BasicBlock * block)106 bool LivenessAnalyzer::AllForwardEdgesVisited(BasicBlock *block)
107 {
108     if (!block->IsLoopHeader()) {
109         for (auto pred : block->GetPredsBlocks()) {
110             if (!pred->IsMarked(marker_)) {
111                 return false;
112             }
113         }
114     } else {
115         // Head of irreducible loop can not dominate other blocks in the loop
116         if (block->GetLoop()->IsIrreducible()) {
117             return true;
118         }
119         // Predecessors which are not dominated - are forward edges,
120         for (auto pred : block->GetPredsBlocks()) {
121             if (!block->IsDominate(pred) && !pred->IsMarked(marker_)) {
122                 return false;
123             }
124         }
125     }
126     return true;
127 }
128 
129 template <bool use_pc_order>
LinearizeBlocks()130 void LivenessAnalyzer::LinearizeBlocks()
131 {
132     ArenaList<BasicBlock *> pending {GetGraph()->GetLocalAllocator()->Adapter()};
133     pending.push_back(GetGraph()->GetStartBlock());
134 
135     while (!pending.empty()) {
136         auto current = pending.front();
137         pending.pop_front();
138 
139         linear_blocks_.push_back(current);
140         current->SetMarker(marker_);
141 
142         auto succs = current->GetSuccsBlocks();
143         // Each block is inserted into pending list before all already inserted blocks
144         // from the same loop. To process edges forwarding to a "true" branches successors
145         // should be processed in reversed order.
146         for (auto it = succs.rbegin(); it != succs.rend(); ++it) {
147             auto succ = *it;
148             if (succ->IsMarked(marker_) || !AllForwardEdgesVisited(succ)) {
149                 continue;
150             }
151 
152             if constexpr (use_pc_order) {  // NOLINT(readability-braces-around-statements)
153                 auto pc_compare = [succ](auto block) { return block->GetGuestPc() > succ->GetGuestPc(); };
154                 auto insert_before = std::find_if(pending.begin(), pending.end(), pc_compare);
155                 pending.insert(insert_before, succ);
156             } else {  // NOLINT(readability-misleading-indentation)
157                 // Insert successor right before a block from the same loop, outer loop's block
158                 // or before a block from the root loop.
159                 // Such ordering guarantee that a loop and all it's inner loops will be processed
160                 // before following edges leading to outer loop blocks.
161                 auto is_same_or_inner_loop = [succ](auto block) {
162                     return succ->GetLoop() == block->GetLoop() || block->GetLoop()->IsRoot() ||
163                            succ->GetLoop()->IsInside(block->GetLoop());
164                 };
165                 auto insert_before = std::find_if(pending.begin(), pending.end(), is_same_or_inner_loop);
166                 pending.insert(insert_before, succ);
167             }
168         }
169     }
170 }
171 
172 /*
173  * Check linear order correctness, using dominators tree
174  */
CheckLinearOrder()175 bool LivenessAnalyzer::CheckLinearOrder()
176 {
177     ArenaVector<size_t> block_pos(GetGraph()->GetVectorBlocks().size(), 0, GetAllocator()->Adapter());
178     size_t position = 0;
179     for (auto block : linear_blocks_) {
180         block_pos[block->GetId()] = position++;
181     }
182 
183     for (auto block : linear_blocks_) {
184         if (block->GetDominator() != nullptr) {
185             ASSERT_PRINT(block_pos[block->GetDominator()->GetId()] < block_pos[block->GetId()],
186                          "Each block should be visited after its dominator");
187         }
188         if (!block->IsTryEnd()) {
189             continue;
190         }
191         ASSERT(block->GetTryId() != INVALID_ID);
192         for (auto bb : linear_blocks_) {
193             if (bb->IsTry() && bb->GetTryId() == block->GetTryId()) {
194                 ASSERT_PRINT(block_pos[bb->GetId()] < block_pos[block->GetId()],
195                              "Each try-block should be visited before its try-end block");
196             }
197         }
198     }
199 
200     return true;
201 }
202 
203 /*
204  * Set lifetime number and visiting number for each instruction
205  */
BuildInstLifeNumbers()206 void LivenessAnalyzer::BuildInstLifeNumbers()
207 {
208     LifeNumber block_begin;
209     LifeNumber life_number = 0;
210     LinearNumber linear_number = 0;
211 
212     for (auto block : GetLinearizedBlocks()) {
213         block_begin = life_number;
214         // set the same number for each phi in the block
215         for (auto phi : block->PhiInsts()) {
216             phi->SetLinearNumber(linear_number++);
217             SetInstLifeNumber(phi, life_number);
218             CreateLifeIntervals(phi);
219         }
220         // ignore PHI instructions
221         insts_by_life_number_.push_back(nullptr);
222         // set a unique number for each instruction in the block, differing by 2
223         // for the reason of adding spill/fill instructions
224         for (auto inst : block->Insts()) {
225             inst->SetLinearNumber(linear_number++);
226             CreateLifeIntervals(inst);
227             if (IsPseudoUserOfMultiOutput(inst)) {
228                 // Should be the same life number as pseudo-user, since actually they have the same definition
229                 SetInstLifeNumber(inst, life_number);
230                 GetInstLifeIntervals(inst)->AddUsePosition(life_number);
231                 continue;
232             }
233             life_number += LIFE_NUMBER_GAP;
234             SetInstLifeNumber(inst, life_number);
235             insts_by_life_number_.push_back(inst);
236         }
237         life_number += LIFE_NUMBER_GAP;
238         SetBlockLiveRange(block, {block_begin, life_number});
239     }
240 }
241 
242 /*
243  * Get lifetime intervals for each instruction
244  */
BuildInstLifeIntervals()245 void LivenessAnalyzer::BuildInstLifeIntervals()
246 {
247     for (auto it = GetLinearizedBlocks().rbegin(); it != GetLinearizedBlocks().rend(); it++) {
248         auto block = *it;
249         auto live_set = GetInitInstLiveSet(block);
250         ProcessBlockLiveInstructions(block, live_set);
251     }
252 }
253 
254 /*
255  * The initial set of live instructions in the `block` is the the union of all live instructions at the beginning of
256  * the block's successors.
257  * Also for each phi-instruction of the successors: input corresponding to the `block` is added to the live set.
258  */
GetInitInstLiveSet(BasicBlock * block)259 InstLiveSet *LivenessAnalyzer::GetInitInstLiveSet(BasicBlock *block)
260 {
261     unsigned instruction_count = inst_life_intervals_.size();
262     auto live_set = GetAllocator()->New<InstLiveSet>(instruction_count, GetAllocator());
263     for (auto succ : block->GetSuccsBlocks()) {
264         // catch-begin is pseudo successor, its live set will be processed for blocks with throwable instructions
265         if (succ->IsCatchBegin()) {
266             continue;
267         }
268         live_set->Union(GetBlockLiveSet(succ));
269         for (auto phi : succ->PhiInsts()) {
270             auto phi_input = phi->CastToPhi()->GetPhiDataflowInput(block);
271             live_set->Add(phi_input->GetLinearNumber());
272         }
273     }
274 
275     // if basic block contains throwable instruction, all instucrions live in the catch-handlers should be live in this
276     // block
277     if (auto inst = block->GetFistThrowableInst(); inst != nullptr) {
278         auto handlers = GetGraph()->GetThrowableInstHandlers(inst);
279         for (auto catch_handler : handlers) {
280             live_set->Union(GetBlockLiveSet(catch_handler));
281         }
282     }
283     return live_set;
284 }
285 
GetLoopEnd(Loop * loop)286 LifeNumber LivenessAnalyzer::GetLoopEnd(Loop *loop)
287 {
288     LifeNumber loop_end = 0;
289     // find max LifeNumber of inner loops
290     for (auto inner : loop->GetInnerLoops()) {
291         loop_end = std::max(loop_end, GetLoopEnd(inner));
292     }
293     // find max LifeNumber of back_edges
294     for (auto back_edge : loop->GetBackEdges()) {
295         loop_end = std::max(loop_end, GetBlockLiveRange(back_edge).GetEnd());
296     }
297     return loop_end;
298 }
299 
300 /*
301  * Append and adjust the lifetime intervals for each instruction live in the block and for their inputs
302  */
ProcessBlockLiveInstructions(BasicBlock * block,InstLiveSet * live_set)303 void LivenessAnalyzer::ProcessBlockLiveInstructions(BasicBlock *block, InstLiveSet *live_set)
304 {
305     // For each live instruction set initial life range equals to the block life range
306     for (auto &interval : inst_life_intervals_) {
307         if (live_set->IsSet(interval->GetInst()->GetLinearNumber())) {
308             interval->AppendRange(GetBlockLiveRange(block));
309         }
310     }
311 
312     for (Inst *inst : block->InstsSafeReverse()) {
313         // Shorten instruction lifetime to the position where its defined
314         // and remove from the block's live set
315         auto inst_life_number = GetInstLifeNumber(inst);
316         auto interval = GetInstLifeIntervals(inst);
317         interval->StartFrom(inst_life_number);
318         live_set->Remove(inst->GetLinearNumber());
319         if (inst->IsCatchPhi()) {
320             // catch-phi's liveness should overlap all linked try blocks' livenesses
321             for (auto pred : inst->GetBasicBlock()->GetPredsBlocks()) {
322                 inst_life_number = std::min(inst_life_number, GetBlockLiveRange(pred).GetBegin());
323             }
324             interval->StartFrom(inst_life_number);
325             AdjustCatchPhiInputsLifetime(inst);
326         } else {
327             if (inst->GetOpcode() == Opcode::LiveOut) {
328                 interval->AppendRange({inst_life_number, GetBlockLiveRange(GetGraph()->GetEndBlock()).GetBegin()});
329             }
330             auto current_live_range = LiveRange {GetBlockLiveRange(block).GetBegin(), inst_life_number};
331             AdjustInputsLifetime(inst, current_live_range, live_set);
332         }
333     }
334 
335     // The lifetime interval of phis instructions starts at the beginning of the block
336     for (auto phi : block->PhiInsts()) {
337         live_set->Remove(phi->GetLinearNumber());
338     }
339 
340     // All instructions live at the beginning ot the loop header
341     // must be live for the entire loop
342     if (block->IsLoopHeader()) {
343         LifeNumber loop_end_position = GetLoopEnd(block->GetLoop());
344 
345         for (auto &interval : inst_life_intervals_) {
346             if (live_set->IsSet(interval->GetInst()->GetLinearNumber())) {
347                 interval->AppendGroupRange({GetBlockLiveRange(block).GetBegin(), loop_end_position});
348             }
349         }
350     }
351     SetBlockLiveSet(block, live_set);
352 }
353 
354 /* static */
GetPropagatedLiveRange(Inst * inst,LiveRange live_range)355 LiveRange LivenessAnalyzer::GetPropagatedLiveRange(Inst *inst, LiveRange live_range)
356 {
357     /*
358      * Implicit null check encoded as no-op and if the reference to check is null
359      * then SIGSEGV will be raised at the first (closest) user. Regmap generated for
360      * NullCheck's SaveState should be valid at that user so we need to extend
361      * life intervals of SaveState's inputs until NullCheck user.
362      */
363     if (inst->IsNullCheck() && !inst->GetUsers().Empty() && inst->CastToNullCheck()->IsImplicit()) {
364         auto extend_until = std::numeric_limits<LifeNumber>::max();
365         for (auto &user : inst->GetUsers()) {
366             auto li = GetInstLifeIntervals(user.GetInst());
367             ASSERT(li != nullptr);
368             extend_until = std::min<LifeNumber>(extend_until, li->GetBegin() + 1);
369         }
370         live_range.SetEnd(extend_until);
371         return live_range;
372     }
373     /*
374      * We need to propagate liveness for instruction with CallRuntime to save registers before call;
375      * Otherwise, we will not be able to restore the value of the virtual registers
376      */
377     if (inst->IsPropagateLiveness()) {
378         live_range.SetEnd(live_range.GetEnd() + 1);
379     } else if (inst->GetOpcode() == Opcode::ReturnInlined && inst->CastToReturnInlined()->IsExtendedLiveness()) {
380         /*
381          * [ReturnInlined]
382          * [ReturnInlined]
383          * ...
384          * [Deoptimize/Throw]
385          *
386          * In this case we propagate ReturnInlined inputs liveness up to the end of basic block
387          */
388         live_range.SetEnd(GetBlockLiveRange(inst->GetBasicBlock()).GetEnd());
389     }
390     return live_range;
391 }
392 
393 /*
394  * Adjust instruction inputs lifetime and add them to the block's live set
395  */
AdjustInputsLifetime(Inst * inst,LiveRange live_range,InstLiveSet * live_set)396 void LivenessAnalyzer::AdjustInputsLifetime(Inst *inst, LiveRange live_range, InstLiveSet *live_set)
397 {
398     for (auto input : inst->GetInputs()) {
399         auto input_inst = inst->GetDataFlowInput(input.GetInst());
400         live_set->Add(input_inst->GetLinearNumber());
401         SetInputRange(inst, input_inst, live_range);
402     }
403 
404     // Extend SaveState inputs lifetime to the end of SaveState's lifetime
405     if (inst->RequireState()) {
406         auto save_state = inst->GetSaveState();
407         ASSERT(save_state != nullptr);
408         auto propagated_range = GetPropagatedLiveRange(inst, live_range);
409         for (auto ss_input : save_state->GetInputs()) {
410             auto input_inst = save_state->GetDataFlowInput(ss_input.GetInst());
411             live_set->Add(input_inst->GetLinearNumber());
412             GetInstLifeIntervals(input_inst)->AppendRange(propagated_range);
413         }
414     }
415 
416     // Handle CatchPhi inputs associated with inst
417     auto range = pending_catch_phi_inputs_.equal_range(inst);
418     for (auto it = range.first; it != range.second; ++it) {
419         auto throwable_input = it->second;
420         auto throwable_input_interval = GetInstLifeIntervals(throwable_input);
421         live_set->Add(throwable_input->GetLinearNumber());
422         throwable_input_interval->AppendRange(live_range);
423     }
424     pending_catch_phi_inputs_.erase(inst);
425 }
426 
427 /*
428  * Increase ref-input liveness in the 'no-async-jit' mode, since GC can be triggered and delete ref during callee-method
429  * compilation
430  */
SetInputRange(const Inst * inst,const Inst * input,LiveRange live_range) const431 void LivenessAnalyzer::SetInputRange(const Inst *inst, const Inst *input, LiveRange live_range) const
432 {
433     if (has_safepoint_during_call_ && inst->IsCall() && DataType::IsReference(input->GetType())) {
434         GetInstLifeIntervals(input)->AppendRange(live_range.GetBegin(), live_range.GetEnd() + 1U);
435     } else {
436         GetInstLifeIntervals(input)->AppendRange(live_range);
437     }
438 }
439 
440 /*
441  * CatchPhi does not handle inputs as regular instructions - instead of performing
442  * some action at CatchPhi's definition copy instruction are added before throwable instructions.
443  * Instead of extending input life interval until CatchPhi it is extended until throwable instruction.
444  */
AdjustCatchPhiInputsLifetime(Inst * inst)445 void LivenessAnalyzer::AdjustCatchPhiInputsLifetime(Inst *inst)
446 {
447     auto catch_phi = inst->CastToCatchPhi();
448 
449     for (ssize_t input_idx = catch_phi->GetInputsCount() - 1; input_idx >= 0; input_idx--) {
450         auto input_inst = catch_phi->GetDataFlowInput(input_idx);
451         auto throwable_inst = const_cast<Inst *>(catch_phi->GetThrowableInst(input_idx));
452 
453         pending_catch_phi_inputs_.insert({throwable_inst, input_inst});
454     }
455 }
456 
SetInstLifeNumber(const Inst * inst,LifeNumber number)457 void LivenessAnalyzer::SetInstLifeNumber([[maybe_unused]] const Inst *inst, LifeNumber number)
458 {
459     ASSERT(inst_life_numbers_.size() == inst->GetLinearNumber());
460     inst_life_numbers_.push_back(number);
461 }
462 
GetInstLifeNumber(Inst * inst) const463 LifeNumber LivenessAnalyzer::GetInstLifeNumber(Inst *inst) const
464 {
465     return inst_life_numbers_[inst->GetLinearNumber()];
466 }
467 
468 /*
469  * Create new lifetime intervals for instruction, check that instruction linear number is equal to intervals
470  * position in vector
471  */
CreateLifeIntervals(Inst * inst)472 void LivenessAnalyzer::CreateLifeIntervals(Inst *inst)
473 {
474     ASSERT(inst->GetLinearNumber() == inst_life_intervals_.size());
475     inst_life_intervals_.push_back(GetAllocator()->New<LifeIntervals>(GetAllocator(), inst));
476 }
477 
GetInstLifeIntervals(const Inst * inst) const478 LifeIntervals *LivenessAnalyzer::GetInstLifeIntervals(const Inst *inst) const
479 {
480     ASSERT(inst->GetLinearNumber() != INVALID_LINEAR_NUM);
481     return inst_life_intervals_[inst->GetLinearNumber()];
482 }
483 
SetBlockLiveRange(BasicBlock * block,LiveRange life_range)484 void LivenessAnalyzer::SetBlockLiveRange(BasicBlock *block, LiveRange life_range)
485 {
486     block_live_ranges_[block->GetId()] = life_range;
487 }
488 
GetBlockLiveRange(const BasicBlock * block) const489 LiveRange LivenessAnalyzer::GetBlockLiveRange(const BasicBlock *block) const
490 {
491     return block_live_ranges_[block->GetId()];
492 }
493 
SetBlockLiveSet(BasicBlock * block,InstLiveSet * live_set)494 void LivenessAnalyzer::SetBlockLiveSet(BasicBlock *block, InstLiveSet *live_set)
495 {
496     block_live_sets_[block->GetId()] = live_set;
497 }
498 
GetBlockLiveSet(BasicBlock * block) const499 InstLiveSet *LivenessAnalyzer::GetBlockLiveSet(BasicBlock *block) const
500 {
501     return block_live_sets_[block->GetId()];
502 }
503 
DumpLifeIntervals(std::ostream & out) const504 void LivenessAnalyzer::DumpLifeIntervals(std::ostream &out) const
505 {
506     for (auto bb : GetGraph()->GetBlocksRPO()) {
507         if (bb->GetId() >= GetBlocksCount()) {
508             continue;
509         }
510         auto block_range = GetBlockLiveRange(bb);
511         out << "BB " << bb->GetId() << "\t" << block_range.ToString() << std::endl;
512 
513         for (auto inst : bb->AllInsts()) {
514             if (inst->GetLinearNumber() == INVALID_LINEAR_NUM) {
515                 continue;
516             }
517             auto interval = GetInstLifeIntervals(inst);
518 
519             out << "v" << inst->GetId() << "\t";
520             for (auto sibling = interval; sibling != nullptr; sibling = sibling->GetSibling()) {
521                 out << sibling->ToString<false>() << "@ " << sibling->GetLocation().ToString(GetGraph()->GetArch())
522                     << "; ";
523             }
524             out << std::endl;
525         }
526     }
527     DumpLocationsUsage(out);
528 }
529 
DumpLocationsUsage(std::ostream & out) const530 void LivenessAnalyzer::DumpLocationsUsage(std::ostream &out) const
531 {
532     std::map<Register, std::vector<LifeIntervals *>> regs_intervals;
533     std::map<Register, std::vector<LifeIntervals *>> vregs_intervals;
534     std::map<Register, std::vector<LifeIntervals *>> slots_intervals;
535     for (auto &interval : inst_life_intervals_) {
536         for (auto sibling = interval; sibling != nullptr; sibling = sibling->GetSibling()) {
537             auto location = sibling->GetLocation();
538             if (location.IsFpRegister()) {
539                 ASSERT(DataType::IsFloatType(interval->GetType()));
540                 vregs_intervals[location.GetValue()].push_back(sibling);
541             } else if (location.IsRegister()) {
542                 regs_intervals[location.GetValue()].push_back(sibling);
543             } else if (location.IsStack()) {
544                 slots_intervals[location.GetValue()].push_back(sibling);
545             }
546         }
547     }
548 
549     for (auto intervals_map : {&regs_intervals, &vregs_intervals, &slots_intervals}) {
550         std::string loc_symbol;
551         if (intervals_map == &regs_intervals) {
552             out << std::endl << "Registers intervals" << std::endl;
553             loc_symbol = "r";
554         } else if (intervals_map == &vregs_intervals) {
555             out << std::endl << "Vector registers intervals" << std::endl;
556             loc_symbol = "vr";
557         } else {
558             ASSERT(intervals_map == &slots_intervals);
559             out << std::endl << "Stack slots intervals" << std::endl;
560             loc_symbol = "s";
561         }
562 
563         if (intervals_map->empty()) {
564             out << "-" << std::endl;
565             continue;
566         }
567         for (auto &[reg, intervals] : *intervals_map) {
568             std::sort(intervals.begin(), intervals.end(),
569                       [](const auto &lhs, const auto &rhs) { return lhs->GetBegin() < rhs->GetBegin(); });
570             out << loc_symbol << std::to_string(reg) << ": ";
571             auto delim = "";
572             for (auto &interval : intervals) {
573                 out << delim << interval->ToString<false>();
574                 delim = "; ";
575             }
576             out << std::endl;
577         }
578     }
579 }
580 
581 template <bool is_fp>
BlockPhysicalRegisters(LifeNumber block_from)582 void LivenessAnalyzer::BlockPhysicalRegisters(LifeNumber block_from)
583 {
584     auto arch = GetGraph()->GetArch();
585     RegMask caller_regs {GetCallerRegsMask(arch, is_fp)};
586     for (auto reg = GetFirstCallerReg(arch, is_fp); reg <= GetLastCallerReg(arch, is_fp); ++reg) {
587         if (caller_regs.test(reg)) {
588             BlockReg<is_fp>(reg, block_from);
589         }
590     }
591 }
592 
BlockFixedLocationRegister(Location location,LifeNumber ln)593 void LivenessAnalyzer::BlockFixedLocationRegister(Location location, LifeNumber ln)
594 {
595     if (location.IsRegister() && location.IsRegisterValid()) {
596         BlockReg<false>(location.GetValue(), ln);
597     } else if (location.IsFpRegister() && location.IsRegisterValid()) {
598         BlockReg<true>(location.GetValue(), ln);
599     }
600 }
601 
602 template <bool is_fp>
BlockReg(Register reg,LifeNumber block_from)603 void LivenessAnalyzer::BlockReg(Register reg, LifeNumber block_from)
604 {
605     auto &intervals = is_fp ? physical_vector_intervals_ : physical_general_intervals_;
606     auto interval = intervals.at(reg);
607     if (interval == nullptr) {
608         interval = GetGraph()->GetAllocator()->New<LifeIntervals>(GetGraph()->GetAllocator());
609         interval->SetPhysicalReg(reg, is_fp ? DataType::FLOAT64 : DataType::UINT64);
610         intervals.at(reg) = interval;
611     }
612     interval->AppendRange(block_from, block_from + 1U);
613     interval->AddUsePosition(block_from);
614 }
615 
IsCallBlockingRegisters(Inst * inst) const616 bool LivenessAnalyzer::IsCallBlockingRegisters(Inst *inst) const
617 {
618     if (inst->IsCall() && !static_cast<CallInst *>(inst)->IsInlined()) {
619         return true;
620     }
621     if (inst->IsIntrinsic() && inst->CastToIntrinsic()->IsNativeCall()) {
622         return true;
623     }
624     return false;
625 }
626 
SplitAt(LifeNumber ln,ArenaAllocator * alloc)627 LifeIntervals *LifeIntervals::SplitAt(LifeNumber ln, ArenaAllocator *alloc)
628 {
629     ASSERT(!IsPhysical());
630     ASSERT(ln > GetBegin() && ln <= GetEnd());
631     auto split_child = alloc->New<LifeIntervals>(alloc, GetInst());
632     if (sibling_ != nullptr) {
633         split_child->sibling_ = sibling_;
634     }
635     split_child->is_split_sibling_ = true;
636 
637     sibling_ = split_child;
638     split_child->SetType(GetType());
639 
640     for (auto &range = live_ranges_.back(); range.GetEnd() > ln; range = live_ranges_.back()) {
641         live_ranges_.pop_back();
642         if (range.GetBegin() > ln) {
643             split_child->AppendRange(range);
644         } else {
645             split_child->AppendRange(ln, range.GetEnd());
646             range.SetEnd(ln);
647             if (range.GetBegin() != range.GetEnd()) {
648                 live_ranges_.push_back(range);
649             }
650 
651             break;
652         }
653     }
654 
655     // Move use positions to the child
656     auto it = use_positions_.lower_bound(ln);
657     split_child->use_positions_.insert(it, use_positions_.end());
658     use_positions_.erase(it, use_positions_.end());
659 
660     return split_child;
661 }
662 
MergeSibling()663 void LifeIntervals::MergeSibling()
664 {
665     ASSERT(sibling_ != nullptr);
666     while (!live_ranges_.empty()) {
667         sibling_->AppendRange(live_ranges_.back());
668         live_ranges_.pop_back();
669     }
670     live_ranges_ = std::move(sibling_->live_ranges_);
671 
672     for (auto &use_pos : sibling_->use_positions_) {
673         AddUsePosition(use_pos);
674     }
675     sibling_ = nullptr;
676 }
677 
FindSiblingAt(LifeNumber ln)678 LifeIntervals *LifeIntervals::FindSiblingAt(LifeNumber ln)
679 {
680     ASSERT(!IsSplitSibling());
681     for (auto head = this; head != nullptr; head = head->GetSibling()) {
682         if (head->GetBegin() <= ln && ln <= head->GetEnd()) {
683             return head;
684         }
685     }
686     return nullptr;
687 }
688 
Intersects(const LiveRange & range) const689 bool LifeIntervals::Intersects(const LiveRange &range) const
690 {
691     return  // the interval starts within the range
692         (range.GetBegin() <= GetBegin() && GetBegin() <= range.GetEnd()) ||
693         // the interval ends within the range
694         (range.GetBegin() <= GetEnd() && GetEnd() <= range.GetEnd()) ||
695         // the range is fully covered by the interval
696         (GetBegin() <= range.GetBegin() && range.GetEnd() <= GetEnd());
697 }
698 
GetFirstIntersectionWith(const LifeIntervals * other,LifeNumber search_from) const699 LifeNumber LifeIntervals::GetFirstIntersectionWith(const LifeIntervals *other, LifeNumber search_from) const
700 {
701     for (auto range : GetRanges()) {
702         if (range.GetEnd() <= search_from) {
703             continue;
704         }
705         for (auto other_range : other->GetRanges()) {
706             if (other_range.GetEnd() <= search_from) {
707                 continue;
708             }
709             auto range_begin = std::max<LifeNumber>(search_from, range.GetBegin());
710             auto other_range_begin = std::max<LifeNumber>(search_from, other_range.GetBegin());
711 
712             if (range_begin <= other_range_begin) {
713                 if (other_range_begin < range.GetEnd()) {
714                     // [range]
715                     //    [other]
716                     return other_range_begin;
717                 }
718                 ASSERT(other_range_begin >= range.GetEnd());
719             } else {
720                 //     [range]
721                 // [other]
722                 if (range_begin < other_range.GetEnd()) {
723                     return range_begin;
724                 }
725                 ASSERT(range_begin >= other_range.GetEnd());
726             }
727         }
728     }
729     return INVALID_LIFE_NUMBER;
730 }
731 
IntersectsWith(const LifeIntervals * other) const732 bool LifeIntervals::IntersectsWith(const LifeIntervals *other) const
733 {
734     return GetFirstIntersectionWith(other) != INVALID_LIFE_NUMBER;
735 }
736 
737 }  // namespace panda::compiler
738