• 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 #ifndef COMPILER_OPTIMIZER_ANALYSIS_LIVENESS_ANALIZER_H
17 #define COMPILER_OPTIMIZER_ANALYSIS_LIVENESS_ANALIZER_H
18 
19 #include "utils/arena_containers.h"
20 #include "optimizer/analysis/liveness_use_table.h"
21 #include "optimizer/ir/constants.h"
22 #include "optimizer/ir/inst.h"
23 #include "optimizer/ir/marker.h"
24 #include "optimizer/pass.h"
25 #include "optimizer/ir/locations.h"
26 #include "compiler_logger.h"
27 
28 namespace panda::compiler {
29 class BasicBlock;
30 class Graph;
31 class Inst;
32 class Loop;
33 
34 class LiveRange {
35 public:
LiveRange(LifeNumber begin,LifeNumber end)36     LiveRange(LifeNumber begin, LifeNumber end) : begin_(begin), end_(end) {}
37     LiveRange() = default;
38     DEFAULT_MOVE_SEMANTIC(LiveRange);
39     DEFAULT_COPY_SEMANTIC(LiveRange);
40     ~LiveRange() = default;
41 
42     // Check if range contains other range
Contains(const LiveRange & other)43     bool Contains(const LiveRange &other) const
44     {
45         return (begin_ <= other.begin_ && other.end_ <= end_);
46     }
47     // Check if range contains point
Contains(LifeNumber number)48     bool Contains(LifeNumber number) const
49     {
50         return (begin_ <= number && number <= end_);
51     }
52     // Check if ranges are equal
53     bool operator==(const LiveRange &other) const
54     {
55         return begin_ == other.begin_ && end_ == other.end_;
56     }
57 
SetBegin(LifeNumber begin)58     void SetBegin(LifeNumber begin)
59     {
60         begin_ = begin;
61     }
GetBegin()62     LifeNumber GetBegin() const
63     {
64         return begin_;
65     }
66 
SetEnd(LifeNumber end)67     void SetEnd(LifeNumber end)
68     {
69         end_ = end;
70     }
GetEnd()71     LifeNumber GetEnd() const
72     {
73         return end_;
74     }
75 
ToString()76     std::string ToString() const
77     {
78         std::stringstream ss;
79         ss << "[" << begin_ << ":" << end_ << ")";
80         return ss.str();
81     }
82 
83 private:
84     LifeNumber begin_ = 0;
85     LifeNumber end_ = 0;
86 };
87 
88 class LifeIntervals {
89 public:
LifeIntervals(ArenaAllocator * allocator)90     explicit LifeIntervals(ArenaAllocator *allocator) : LifeIntervals(allocator, nullptr) {}
91 
LifeIntervals(ArenaAllocator * allocator,Inst * inst)92     LifeIntervals(ArenaAllocator *allocator, Inst *inst) : LifeIntervals(allocator, inst, {}) {}
93 
LifeIntervals(ArenaAllocator * allocator,Inst * inst,LiveRange live_range)94     LifeIntervals(ArenaAllocator *allocator, Inst *inst, LiveRange live_range)
95         : inst_(inst),
96           live_ranges_(allocator->Adapter()),
97           use_positions_(allocator->Adapter()),
98           location_(Location::Invalid()),
99           type_(DataType::NO_TYPE),
100           is_preassigned_(),
101           is_physical_(),
102           is_split_sibling_()
103     {
104         if (live_range.GetEnd() != 0) {
105             live_ranges_.push_front(live_range);
106         }
107     }
108 
109     DEFAULT_MOVE_SEMANTIC(LifeIntervals);
110     DEFAULT_COPY_SEMANTIC(LifeIntervals);
111     ~LifeIntervals() = default;
112 
113     /*
114      * Basic blocks are visiting in descending lifetime order, so there are 3 ways to
115      * update lifetime:
116      * - append the first LiveRange
117      * - extend the first LiveRange
118      * - append a new one LiveRange due to lifetime hole
119      */
AppendRange(LiveRange live_range)120     void AppendRange(LiveRange live_range)
121     {
122         ASSERT(live_range.GetEnd() >= live_range.GetBegin());
123         // [live_range],[front]
124         if (live_ranges_.empty() || live_range.GetEnd() < live_ranges_.front().GetBegin()) {
125             live_ranges_.push_front(live_range);
126             /*
127              * [live_range]
128              *         [front]
129              * ->
130              * [    front    ]
131              */
132         } else if (live_range.GetEnd() <= live_ranges_.front().GetEnd()) {
133             live_ranges_.front().SetBegin(live_range.GetBegin());
134             /*
135              * [ live_range  ]
136              * [front]
137              * ->
138              * [    front    ]
139              */
140         } else if (!live_ranges_.front().Contains(live_range)) {
141             ASSERT(live_ranges_.front().GetBegin() == live_range.GetBegin());
142             live_ranges_.front().SetEnd(live_range.GetEnd());
143         }
144     }
145 
AppendRange(LifeNumber begin,LifeNumber end)146     void AppendRange(LifeNumber begin, LifeNumber end)
147     {
148         AppendRange({begin, end});
149     }
150 
151     /*
152      * Group range extends the first LiveRange, because it is covering the hole group,
153      * starting from its header
154      */
AppendGroupRange(LiveRange loop_range)155     void AppendGroupRange(LiveRange loop_range)
156     {
157         ASSERT(loop_range.GetBegin() == live_ranges_.front().GetBegin());
158         // extend the first LiveRange
159         live_ranges_.front().SetEnd(std::max(loop_range.GetEnd(), live_ranges_.front().GetEnd()));
160 
161         // resolve overlapping
162         for (size_t i = 1; i < live_ranges_.size(); i++) {
163             if (live_ranges_.front().Contains(live_ranges_[i])) {
164                 live_ranges_.erase(live_ranges_.begin() + i);
165                 i--;
166             } else if (live_ranges_.front().Contains(live_ranges_[i].GetBegin())) {
167                 ASSERT(live_ranges_[i].GetEnd() > live_ranges_.front().GetEnd());
168                 live_ranges_.front().SetEnd(live_ranges_[i].GetEnd());
169                 live_ranges_.erase(live_ranges_.begin() + i);
170                 break;
171             } else {
172                 break;
173             }
174         }
175     }
176 
Clear()177     void Clear()
178     {
179         live_ranges_.clear();
180     }
181 
182     /*
183      * Shorten the first range or create it if instruction has no users
184      */
StartFrom(LifeNumber from)185     void StartFrom(LifeNumber from)
186     {
187         if (live_ranges_.empty()) {
188             AppendRange(from, from + LIFE_NUMBER_GAP);
189         } else {
190             ASSERT(live_ranges_.front().GetEnd() >= from);
191             live_ranges_.front().SetBegin(from);
192         }
193     }
194 
GetRanges()195     const ArenaDeque<LiveRange> &GetRanges() const
196     {
197         return live_ranges_;
198     }
199 
GetBegin()200     LifeNumber GetBegin() const
201     {
202         ASSERT(!GetRanges().empty());
203         return GetRanges().front().GetBegin();
204     }
205 
GetEnd()206     LifeNumber GetEnd() const
207     {
208         ASSERT(!GetRanges().empty());
209         return GetRanges().back().GetEnd();
210     }
211 
212     template <bool include_border = false>
SplitCover(LifeNumber position)213     bool SplitCover(LifeNumber position) const
214     {
215         for (auto range : GetRanges()) {
216             if (range.GetBegin() <= position && position < range.GetEnd()) {
217                 return true;
218             }
219             if constexpr (include_border) {  // NOLINT(readability-braces-around-statements,
220                                              // bugprone-suspicious-semicolon)
221                 if (position == range.GetEnd()) {
222                     return true;
223                 }
224             }
225         }
226         return false;
227     }
228 
SetReg(Register reg)229     void SetReg(Register reg)
230     {
231         SetLocation(Location::MakeRegister(reg, type_));
232     }
233 
SetPreassignedReg(Register reg)234     void SetPreassignedReg(Register reg)
235     {
236         SetReg(reg);
237         is_preassigned_ = true;
238     }
239 
SetPhysicalReg(Register reg,DataType::Type type)240     void SetPhysicalReg(Register reg, DataType::Type type)
241     {
242         SetLocation(Location::MakeRegister(reg, type));
243         SetType(type);
244         is_physical_ = true;
245     }
246 
GetReg()247     Register GetReg() const
248     {
249         return location_.GetValue();
250     }
251 
HasReg()252     bool HasReg() const
253     {
254         return location_.IsFixedRegister();
255     }
256 
SetLocation(Location location)257     void SetLocation(Location location)
258     {
259         location_ = location;
260     }
261 
GetLocation()262     Location GetLocation() const
263     {
264         return location_;
265     }
266 
ClearLocation()267     void ClearLocation()
268     {
269         SetLocation(Location::Invalid());
270     }
271 
SetType(DataType::Type type)272     void SetType(DataType::Type type)
273     {
274         type_ = type;
275     }
276 
GetType()277     DataType::Type GetType() const
278     {
279         return type_;
280     }
281 
GetInst()282     Inst *GetInst() const
283     {
284         ASSERT(!is_physical_);
285         return inst_;
286     }
287 
GetUsePositions()288     const auto &GetUsePositions() const
289     {
290         return use_positions_;
291     }
292 
AddUsePosition(LifeNumber ln)293     void AddUsePosition(LifeNumber ln)
294     {
295         ASSERT(ln != 0 && ln != INVALID_LIFE_NUMBER);
296         use_positions_.insert(ln);
297     }
298 
GetNextUsage(LifeNumber pos)299     LifeNumber GetNextUsage(LifeNumber pos)
300     {
301         auto it = use_positions_.lower_bound(pos);
302         if (it != use_positions_.end()) {
303             return *it;
304         }
305         return INVALID_LIFE_NUMBER;
306     }
307 
GetLastUsageBefore(LifeNumber pos)308     LifeNumber GetLastUsageBefore(LifeNumber pos)
309     {
310         auto it = use_positions_.lower_bound(pos);
311         if (it == use_positions_.begin()) {
312             return INVALID_LIFE_NUMBER;
313         }
314         it = std::prev(it);
315         return it == use_positions_.end() ? INVALID_LIFE_NUMBER : *it;
316     }
317 
GetPrevUsage(LifeNumber pos)318     LifeNumber GetPrevUsage(LifeNumber pos) const
319     {
320         auto it = use_positions_.upper_bound(pos);
321         if (it != use_positions_.begin()) {
322             return *std::prev(it);
323         }
324         return INVALID_LIFE_NUMBER;
325     }
326 
NoUsageUntil(LifeNumber pos)327     bool NoUsageUntil(LifeNumber pos) const
328     {
329         return use_positions_.empty() || (*use_positions_.begin() > pos);
330     }
331 
NoDest()332     bool NoDest() const
333     {
334         if (IsPseudoUserOfMultiOutput(inst_)) {
335             return false;
336         }
337         return inst_->NoDest();
338     }
339 
IsPreassigned()340     bool IsPreassigned() const
341     {
342         return is_preassigned_;
343     }
344 
IsPhysical()345     bool IsPhysical() const
346     {
347         return is_physical_;
348     }
349 
350     template <bool with_inst_id = true>
ToString()351     std::string ToString() const
352     {
353         std::stringstream ss;
354         auto delim = "";
355         for (const auto &range : GetRanges()) {
356             ss << delim << range.ToString();
357             delim = " ";
358         }
359         // NOLINTNEXTLINE(readability-braces-around-statements, bugprone-suspicious-semicolon)
360         if constexpr (with_inst_id) {
361             if (!is_physical_) {
362                 ss << " {inst v" << std::to_string(GetInst()->GetId()) << "}";
363             } else {
364                 ss << " {physical}";
365             }
366         }
367         return ss.str();
368     }
369 
370     // Split current interval at specified life number and return new interval starting at `ln`.
371     // If interval has range [begin, end) then SplitAt call will truncate it to [begin, ln) and
372     // returned interval will have range [ln, end).
373     LifeIntervals *SplitAt(LifeNumber ln, ArenaAllocator *alloc);
374 
375     // Helper to merge interval, which was splitted at the beginning: [a, a+1) [a+1, b) -> [a,b)
376     void MergeSibling();
377 
378     // Return sibling interval created by SplitAt call or nullptr if there is no sibling for current interval.
GetSibling()379     LifeIntervals *GetSibling() const
380     {
381         return sibling_;
382     }
383 
384     // Return sibling interval covering specified life number or nullptr if there is no such sibling.
385     LifeIntervals *FindSiblingAt(LifeNumber ln);
386 
387     bool Intersects(const LiveRange &range) const;
388     // Return first point where `this` interval intersects with the `other
389     LifeNumber GetFirstIntersectionWith(const LifeIntervals *other, LifeNumber search_from = 0) const;
390 
391     bool IntersectsWith(const LifeIntervals *other) const;
392 
IsSplitSibling()393     bool IsSplitSibling() const
394     {
395         return is_split_sibling_;
396     }
397 
398 private:
399     Inst *inst_ {nullptr};
400     ArenaDeque<LiveRange> live_ranges_;
401     LifeIntervals *sibling_ {nullptr};
402     ArenaSet<LifeNumber> use_positions_;
403     Location location_;
404     DataType::Type type_;
405     uint8_t is_preassigned_ : 1;
406     uint8_t is_physical_ : 1;
407     uint8_t is_split_sibling_ : 1;
408 };
409 
410 /*
411  * Class to hold live instruction set
412  */
413 class InstLiveSet {
414 public:
InstLiveSet(size_t size,ArenaAllocator * allocator)415     explicit InstLiveSet(size_t size, ArenaAllocator *allocator) : bits_(size, allocator->Adapter()) {};
416     NO_MOVE_SEMANTIC(InstLiveSet);
417     NO_COPY_SEMANTIC(InstLiveSet);
418     ~InstLiveSet() = default;
419 
Union(const InstLiveSet * other)420     void Union(const InstLiveSet *other)
421     {
422         if (other == nullptr) {
423             return;
424         }
425         ASSERT(bits_.size() == other->bits_.size());
426         for (size_t i = 0; i < bits_.size(); i++) {
427             bits_[i] = bits_[i] || other->bits_[i];
428         }
429     }
430 
Add(size_t index)431     void Add(size_t index)
432     {
433         ASSERT(index < bits_.size());
434         bits_[index] = true;
435     }
436 
Remove(size_t index)437     void Remove(size_t index)
438     {
439         ASSERT(index < bits_.size());
440         bits_[index] = false;
441     }
442 
IsSet(size_t index)443     bool IsSet(size_t index)
444     {
445         ASSERT(index < bits_.size());
446         return bits_[index];
447     }
448 
449 private:
450     ArenaVector<bool> bits_;
451 };
452 
453 using LocationHints = ArenaMap<LifeNumber, Register>;
454 /*
455  * `LivenessAnalyzer` is based on algorithm, published by Christian Wimmer and Michael Franz in
456  * "Linear Scan Register Allocation on SSA Form" paper. ACM, 2010.
457  */
458 class LivenessAnalyzer : public Analysis {
459 public:
460     explicit LivenessAnalyzer(Graph *graph);
461 
462     NO_MOVE_SEMANTIC(LivenessAnalyzer);
463     NO_COPY_SEMANTIC(LivenessAnalyzer);
464     ~LivenessAnalyzer() override = default;
465 
466     bool RunImpl() override;
467 
GetLinearizedBlocks()468     const ArenaVector<BasicBlock *> &GetLinearizedBlocks() const
469     {
470         return linear_blocks_;
471     }
472     LifeIntervals *GetInstLifeIntervals(const Inst *inst) const;
473 
GetInstByLifeNumber(LifeNumber ln)474     Inst *GetInstByLifeNumber(LifeNumber ln) const
475     {
476         return insts_by_life_number_[ln / LIFE_NUMBER_GAP];
477     }
478 
GetBlockCoversPoint(LifeNumber ln)479     BasicBlock *GetBlockCoversPoint(LifeNumber ln) const
480     {
481         for (auto bb : linear_blocks_) {
482             if (GetBlockLiveRange(bb).Contains(ln)) {
483                 return bb;
484             }
485         }
486         return nullptr;
487     }
488 
Cleanup()489     void Cleanup()
490     {
491         for (auto *interv : inst_life_intervals_) {
492             if (!interv->IsPhysical() && !interv->IsPreassigned()) {
493                 interv->ClearLocation();
494             }
495             if (interv->GetSibling() != nullptr) {
496                 interv->MergeSibling();
497             }
498         }
499     }
500 
GetLifeIntervals()501     const ArenaVector<LifeIntervals *> &GetLifeIntervals() const
502     {
503         return inst_life_intervals_;
504     }
505     LiveRange GetBlockLiveRange(const BasicBlock *block) const;
506 
507     template <typename Func>
EnumerateLiveIntervalsForInst(Inst * inst,Func func)508     void EnumerateLiveIntervalsForInst(Inst *inst, Func func)
509     {
510         auto inst_number = GetInstLifeNumber(inst);
511         for (auto &li : GetLifeIntervals()) {
512             if (li->IsPhysical()) {
513                 continue;
514             }
515             auto li_inst = li->GetInst();
516             // phi-inst could be removed after regalloc
517             if (li_inst->GetBasicBlock() == nullptr) {
518                 ASSERT(li_inst->IsPhi());
519                 continue;
520             }
521             if (li_inst == inst || li->NoDest()) {
522                 continue;
523             }
524             auto sibling = li->FindSiblingAt(inst_number);
525             if (sibling != nullptr && sibling->SplitCover(inst_number)) {
526                 func(sibling);
527             }
528         }
529     }
530 
GetPassName()531     const char *GetPassName() const override
532     {
533         return "LivenessAnalysis";
534     }
535 
GetBlocksCount()536     size_t GetBlocksCount() const
537     {
538         return block_live_ranges_.size();
539     }
540 
541     bool IsCallBlockingRegisters(Inst *inst) const;
542 
543     void DumpLifeIntervals(std::ostream &out = std::cout) const;
544     void DumpLocationsUsage(std::ostream &out = std::cout) const;
545 
GetUseTable()546     const UseTable &GetUseTable() const
547     {
548         return use_table_;
549     }
550 
551 private:
GetAllocator()552     ArenaAllocator *GetAllocator()
553     {
554         return allocator_;
555     }
556 
557     void ResetLiveness();
558 
559     /*
560      * Blocks linearization methods
561      */
562     bool AllForwardEdgesVisited(BasicBlock *block);
563     void BuildBlocksLinearOrder();
564     template <bool use_pc_order>
565     void LinearizeBlocks();
566     bool CheckLinearOrder();
567 
568     /*
569      * Lifetime analysis methods
570      */
571     void BuildInstLifeNumbers();
572     void BuildInstLifeIntervals();
573     void ProcessBlockLiveInstructions(BasicBlock *block, InstLiveSet *live_set);
574     void AdjustInputsLifetime(Inst *inst, LiveRange live_range, InstLiveSet *live_set);
575     void SetInputRange(const Inst *inst, const Inst *input, LiveRange live_range) const;
576     void CreateLifeIntervals(Inst *inst);
577     InstLiveSet *GetInitInstLiveSet(BasicBlock *block);
578     LifeNumber GetInstLifeNumber(Inst *inst) const;
579     void SetInstLifeNumber(const Inst *inst, LifeNumber number);
580     void SetBlockLiveRange(BasicBlock *block, LiveRange life_range);
581     void SetBlockLiveSet(BasicBlock *block, InstLiveSet *live_set);
582     InstLiveSet *GetBlockLiveSet(BasicBlock *block) const;
583     LifeNumber GetLoopEnd(Loop *loop);
584     LiveRange GetPropagatedLiveRange(Inst *inst, LiveRange live_range);
585     void AdjustCatchPhiInputsLifetime(Inst *inst);
586 
587     template <bool is_fp>
588     void BlockReg(Register reg, LifeNumber block_from);
589     template <bool is_fp>
590     void BlockPhysicalRegisters(LifeNumber block_from);
591     void BlockFixedLocationRegister(Location location, LifeNumber ln);
592 
593 private:
594     ArenaAllocator *allocator_;
595     ArenaVector<BasicBlock *> linear_blocks_;
596     ArenaVector<LifeNumber> inst_life_numbers_;
597     ArenaVector<LifeIntervals *> inst_life_intervals_;
598     InstVector insts_by_life_number_;
599     ArenaVector<LiveRange> block_live_ranges_;
600     ArenaVector<InstLiveSet *> block_live_sets_;
601     ArenaMultiMap<Inst *, Inst *> pending_catch_phi_inputs_;
602     ArenaVector<LifeIntervals *> physical_general_intervals_;
603     ArenaVector<LifeIntervals *> physical_vector_intervals_;
604     UseTable use_table_;
605     bool has_safepoint_during_call_;
606 
607     Marker marker_ {UNDEF_MARKER};
608 };
609 }  // namespace panda::compiler
610 
611 #endif  // COMPILER_OPTIMIZER_ANALYSIS_LIVENESS_ANALIZER_H
612