• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #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 ark::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 liveRange)94     LifeIntervals(ArenaAllocator *allocator, Inst *inst, LiveRange liveRange)
95         : inst_(inst),
96           liveRanges_(allocator->Adapter()),
97           usePositions_(allocator->Adapter()),
98           location_(Location::Invalid()),
99           type_(DataType::NO_TYPE),
100           isPreassigned_(0),
101           isPhysical_(0),
102           isSplitSibling_(0)
103     {
104 #ifndef NDEBUG
105         finalized_ = 0;
106 #endif
107         if (liveRange.GetEnd() != 0) {
108             liveRanges_.push_back(liveRange);
109         }
110     }
111 
112     DEFAULT_MOVE_SEMANTIC(LifeIntervals);
113     DEFAULT_COPY_SEMANTIC(LifeIntervals);
114     ~LifeIntervals() = default;
115 
116     /*
117      * Basic blocks are visiting in descending lifetime order, so there are 3 ways to
118      * update lifetime:
119      * - append the first LiveRange
120      * - extend the first LiveRange
121      * - append a new one LiveRange due to lifetime hole
122      */
AppendRange(LiveRange liveRange)123     void AppendRange(LiveRange liveRange)
124     {
125         ASSERT(liveRange.GetEnd() >= liveRange.GetBegin());
126         // [live_range],[back]
127         if (liveRanges_.empty() || liveRange.GetEnd() < liveRanges_.back().GetBegin()) {
128             liveRanges_.push_back(liveRange);
129             /*
130              * [live_range]
131              *         [front]
132              * ->
133              * [    front    ]
134              */
135         } else if (liveRange.GetEnd() <= liveRanges_.back().GetEnd()) {
136             liveRanges_.back().SetBegin(liveRange.GetBegin());
137             /*
138              * [ live_range  ]
139              * [front]
140              * ->
141              * [    front    ]
142              */
143         } else if (!liveRanges_.back().Contains(liveRange)) {
144             ASSERT(liveRanges_.back().GetBegin() == liveRange.GetBegin());
145             liveRanges_.back().SetEnd(liveRange.GetEnd());
146         }
147     }
148 
AppendRange(LifeNumber begin,LifeNumber end)149     void AppendRange(LifeNumber begin, LifeNumber end)
150     {
151         AppendRange({begin, end});
152     }
153 
154     /*
155      * Group range extends the first LiveRange, because it is covering the hole group,
156      * starting from its header
157      */
AppendGroupRange(LiveRange loopRange)158     void AppendGroupRange(LiveRange loopRange)
159     {
160         auto firstRange = liveRanges_.back();
161         liveRanges_.pop_back();
162         ASSERT(loopRange.GetBegin() == firstRange.GetBegin());
163         // extend the first LiveRange
164         firstRange.SetEnd(std::max(loopRange.GetEnd(), firstRange.GetEnd()));
165 
166         // resolve overlapping
167         while (!liveRanges_.empty()) {
168             if (firstRange.Contains(liveRanges_.back())) {
169                 liveRanges_.pop_back();
170             } else if (firstRange.Contains(liveRanges_.back().GetBegin())) {
171                 ASSERT(liveRanges_.back().GetEnd() > firstRange.GetEnd());
172                 firstRange.SetEnd(liveRanges_.back().GetEnd());
173                 liveRanges_.pop_back();
174                 break;
175             } else {
176                 break;
177             }
178         }
179         liveRanges_.push_back(firstRange);
180     }
181 
Clear()182     void Clear()
183     {
184         liveRanges_.clear();
185     }
186 
187     /*
188      * Shorten the first range or create it if instruction has no users
189      */
StartFrom(LifeNumber from)190     void StartFrom(LifeNumber from)
191     {
192         if (liveRanges_.empty()) {
193             AppendRange(from, from + LIFE_NUMBER_GAP);
194         } else {
195             ASSERT(liveRanges_.back().GetEnd() >= from);
196             liveRanges_.back().SetBegin(from);
197         }
198     }
199 
GetRanges()200     const ArenaVector<LiveRange> &GetRanges() const
201     {
202         return liveRanges_;
203     }
204 
GetBegin()205     LifeNumber GetBegin() const
206     {
207         ASSERT(!GetRanges().empty());
208         return GetRanges().back().GetBegin();
209     }
210 
GetEnd()211     LifeNumber GetEnd() const
212     {
213         ASSERT(!GetRanges().empty());
214         return GetRanges().front().GetEnd();
215     }
216 
217     template <bool INCLUDE_BORDER = false>
SplitCover(LifeNumber position)218     bool SplitCover(LifeNumber position) const
219     {
220         for (auto range : GetRanges()) {
221             if (range.GetBegin() <= position && position < range.GetEnd()) {
222                 return true;
223             }
224             if constexpr (INCLUDE_BORDER) {  // NOLINT(readability-braces-around-statements,
225                                              // bugprone-suspicious-semicolon)
226                 if (position == range.GetEnd()) {
227                     return true;
228                 }
229             }
230         }
231         return false;
232     }
233 
SetReg(Register reg)234     void SetReg(Register reg)
235     {
236         SetLocation(Location::MakeRegister(reg, type_));
237     }
238 
SetPreassignedReg(Register reg)239     void SetPreassignedReg(Register reg)
240     {
241         SetReg(reg);
242         isPreassigned_ = true;
243     }
244 
SetPhysicalReg(Register reg,DataType::Type type)245     void SetPhysicalReg(Register reg, DataType::Type type)
246     {
247         SetLocation(Location::MakeRegister(reg, type));
248         SetType(type);
249         isPhysical_ = true;
250     }
251 
GetReg()252     Register GetReg() const
253     {
254         return location_.GetValue();
255     }
256 
HasReg()257     bool HasReg() const
258     {
259         return location_.IsFixedRegister();
260     }
261 
SetLocation(Location location)262     void SetLocation(Location location)
263     {
264         location_ = location;
265     }
266 
GetLocation()267     Location GetLocation() const
268     {
269         return location_;
270     }
271 
ClearLocation()272     void ClearLocation()
273     {
274         SetLocation(Location::Invalid());
275     }
276 
SetType(DataType::Type type)277     void SetType(DataType::Type type)
278     {
279         type_ = type;
280     }
281 
GetType()282     DataType::Type GetType() const
283     {
284         return type_;
285     }
286 
GetInst()287     Inst *GetInst() const
288     {
289         ASSERT(!isPhysical_);
290         return inst_;
291     }
292 
HasInst()293     bool HasInst() const
294     {
295         return inst_ != nullptr;
296     }
297 
GetUsePositions()298     const auto &GetUsePositions() const
299     {
300         return usePositions_;
301     }
302 
AddUsePosition(LifeNumber ln)303     void AddUsePosition(LifeNumber ln)
304     {
305         ASSERT(ln != 0 && ln != INVALID_LIFE_NUMBER);
306         ASSERT(!finalized_);
307         usePositions_.push_back(ln);
308     }
309 
PrependUsePosition(LifeNumber ln)310     void PrependUsePosition(LifeNumber ln)
311     {
312         ASSERT(ln != 0 && ln != INVALID_LIFE_NUMBER);
313         ASSERT(finalized_);
314         ASSERT(usePositions_.empty() || ln <= usePositions_.front());
315         usePositions_.insert(usePositions_.begin(), ln);
316     }
317 
GetNextUsage(LifeNumber pos)318     LifeNumber GetNextUsage(LifeNumber pos) const
319     {
320         ASSERT(finalized_);
321         auto it = std::lower_bound(usePositions_.begin(), usePositions_.end(), pos);
322         if (it != usePositions_.end()) {
323             return *it;
324         }
325         return INVALID_LIFE_NUMBER;
326     }
327 
GetLastUsageBefore(LifeNumber pos)328     LifeNumber GetLastUsageBefore(LifeNumber pos) const
329     {
330         ASSERT(finalized_);
331         auto it = std::lower_bound(usePositions_.begin(), usePositions_.end(), pos);
332         if (it == usePositions_.begin()) {
333             return INVALID_LIFE_NUMBER;
334         }
335         it = std::prev(it);
336         return it == usePositions_.end() ? INVALID_LIFE_NUMBER : *it;
337     }
338 
GetPrevUsage(LifeNumber pos)339     LifeNumber GetPrevUsage(LifeNumber pos) const
340     {
341         ASSERT(finalized_);
342         auto it = std::upper_bound(usePositions_.begin(), usePositions_.end(), pos);
343         if (it != usePositions_.begin()) {
344             return *std::prev(it);
345         }
346         return INVALID_LIFE_NUMBER;
347     }
348 
NoUsageUntil(LifeNumber pos)349     bool NoUsageUntil(LifeNumber pos) const
350     {
351         ASSERT(finalized_);
352         return usePositions_.empty() || (*usePositions_.begin() > pos);
353     }
354 
NoDest()355     bool NoDest() const
356     {
357         if (IsPseudoUserOfMultiOutput(inst_)) {
358             return false;
359         }
360         return inst_->NoDest();
361     }
362 
IsPreassigned()363     bool IsPreassigned() const
364     {
365         return isPreassigned_;
366     }
367 
IsPhysical()368     bool IsPhysical() const
369     {
370         return isPhysical_;
371     }
372 
373     template <bool WITH_INST_ID = true>
ToString()374     std::string ToString() const
375     {
376         std::stringstream ss;
377         auto delim = "";
378         for (auto it = GetRanges().rbegin(); it != GetRanges().rend(); it++) {
379             ss << delim << it->ToString();
380             delim = " ";
381         }
382         // NOLINTNEXTLINE(readability-braces-around-statements, bugprone-suspicious-semicolon)
383         if constexpr (WITH_INST_ID) {
384             if (HasInst()) {
385                 ss << " {inst v" << std::to_string(GetInst()->GetId()) << "}";
386             } else {
387                 ss << " {physical}";
388             }
389         }
390         return ss.str();
391     }
392 
393     // Split current interval at specified life number and return new interval starting at `ln`.
394     // If interval has range [begin, end) then SplitAt call will truncate it to [begin, ln) and
395     // returned interval will have range [ln, end).
396     LifeIntervals *SplitAt(LifeNumber ln, ArenaAllocator *alloc);
397 
398     // Split current interval before or after the first use, then recursively do the same with splits.
399     // The result will be
400     // [use1---use2---useN) -> [use1),[---),[use2),[---)[useN)
401     void SplitAroundUses(ArenaAllocator *alloc);
402 
403     // Helper to merge interval, which was splitted at the beginning: [a, a+1) [a+1, b) -> [a,b)
404     void MergeSibling();
405 
406     // Return sibling interval created by SplitAt call or nullptr if there is no sibling for current interval.
GetSibling()407     LifeIntervals *GetSibling() const
408     {
409         return sibling_;
410     }
411 
412     // Return sibling interval covering specified life number or nullptr if there is no such sibling.
413     LifeIntervals *FindSiblingAt(LifeNumber ln);
414 
415     bool Intersects(const LiveRange &range) const;
416     // Return first point where `this` interval intersects with the `other
417     LifeNumber GetFirstIntersectionWith(const LifeIntervals *other, LifeNumber searchFrom = 0) const;
418 
419     template <bool OTHER_IS_PHYSICAL = false>
IntersectsWith(const LifeIntervals * other)420     bool IntersectsWith(const LifeIntervals *other) const
421     {
422         auto intersection = GetFirstIntersectionWith(other);
423         if constexpr (OTHER_IS_PHYSICAL) {
424             ASSERT(other->IsPhysical());
425             // Interval can intersect the physical one at the beginning of its live range only if that physical
426             // interval's range was created for it. Try to find next intersection
427             //
428             // interval [--------------------------]
429             // physical [-]       [-]           [-]
430             //           ^         ^
431             //          skip      intersection
432             if (intersection == GetBegin()) {
433                 intersection = GetFirstIntersectionWith(other, intersection + 1U);
434             }
435         }
436         return intersection != INVALID_LIFE_NUMBER;
437     }
438 
IsSplitSibling()439     bool IsSplitSibling() const
440     {
441         return isSplitSibling_;
442     }
443 
444     bool FindRangeCoveringPosition(LifeNumber ln, LiveRange *dst) const;
445 
Finalize()446     void Finalize()
447     {
448 #ifndef NDEBUG
449         ASSERT(!finalized_);
450         finalized_ = true;
451 #endif
452         std::sort(usePositions_.begin(), usePositions_.end());
453     }
454 
455 private:
456     Inst *inst_ {nullptr};
457     ArenaVector<LiveRange> liveRanges_;
458     LifeIntervals *sibling_ {nullptr};
459     ArenaVector<LifeNumber> usePositions_;
460     Location location_;
461     DataType::Type type_;
462     uint8_t isPreassigned_ : 1;
463     uint8_t isPhysical_ : 1;
464     uint8_t isSplitSibling_ : 1;
465 #ifndef NDEBUG
466     uint8_t finalized_ : 1;
467 #endif
468 };
469 
470 /*
471  * Class to hold live instruction set
472  */
473 class InstLiveSet {
474 public:
InstLiveSet(size_t size,ArenaAllocator * allocator)475     explicit InstLiveSet(size_t size, ArenaAllocator *allocator) : bits_(size, allocator) {};
476     NO_MOVE_SEMANTIC(InstLiveSet);
477     NO_COPY_SEMANTIC(InstLiveSet);
478     ~InstLiveSet() = default;
479 
Union(const InstLiveSet * other)480     void Union(const InstLiveSet *other)
481     {
482         if (other == nullptr) {
483             return;
484         }
485         ASSERT(bits_.size() == other->bits_.size());
486         bits_ |= other->bits_;
487     }
488 
Add(size_t index)489     void Add(size_t index)
490     {
491         ASSERT(index < bits_.size());
492         bits_.SetBit(index);
493     }
494 
Remove(size_t index)495     void Remove(size_t index)
496     {
497         ASSERT(index < bits_.size());
498         bits_.ClearBit(index);
499     }
500 
IsSet(size_t index)501     bool IsSet(size_t index)
502     {
503         ASSERT(index < bits_.size());
504         return bits_.GetBit(index);
505     }
506 
507 private:
508     ArenaBitVector bits_;
509 };
510 
511 using LocationHints = ArenaMap<LifeNumber, Register>;
512 /*
513  * `LivenessAnalyzer` is based on algorithm, published by Christian Wimmer and Michael Franz in
514  * "Linear Scan Register Allocation on SSA Form" paper. ACM, 2010.
515  */
516 class LivenessAnalyzer : public Analysis {
517 public:
518     explicit LivenessAnalyzer(Graph *graph);
519 
520     NO_MOVE_SEMANTIC(LivenessAnalyzer);
521     NO_COPY_SEMANTIC(LivenessAnalyzer);
522     ~LivenessAnalyzer() override = default;
523 
524     bool RunImpl() override;
525     void Cleanup();
526     void Finalize();
527     const char *GetPassName() const override;
528 
529     const ArenaVector<BasicBlock *> &GetLinearizedBlocks() const;
530     const ArenaVector<LifeIntervals *> &GetLifeIntervals() const;
531 
532     LifeIntervals *GetInstLifeIntervals(const Inst *inst) const;
533     Inst *GetInstByLifeNumber(LifeNumber ln) const;
534     BasicBlock *GetBlockCoversPoint(LifeNumber ln) const;
535     LiveRange GetBlockLiveRange(const BasicBlock *block) const;
536 
537     template <typename Func>
EnumerateLiveIntervalsForInst(Inst * inst,Func func)538     void EnumerateLiveIntervalsForInst(Inst *inst, Func func)
539     {
540         auto instNumber = GetInstLifeNumber(inst);
541         for (auto &li : GetLifeIntervals()) {
542             if (!li->HasInst()) {
543                 continue;
544             }
545             auto liInst = li->GetInst();
546             // phi-inst could be removed after regalloc
547             if (liInst->GetBasicBlock() == nullptr) {
548                 ASSERT(liInst->IsPhi());
549                 continue;
550             }
551             if (liInst == inst || li->NoDest()) {
552                 continue;
553             }
554             auto sibling = li->FindSiblingAt(instNumber);
555             if (sibling != nullptr && sibling->SplitCover(instNumber)) {
556                 func(sibling);
557             }
558         }
559     }
560 
561     /*
562      * 'interval_for_temp' - is additional life interval for an instruction required temp;
563      * - Find instruction for which was created 'interval_for_temp'
564      * - Enumerate instruction's inputs with fixed locations
565      */
566     template <typename Func>
EnumerateFixedLocationsOverlappingTemp(const LifeIntervals * intervalForTemp,Func func)567     void EnumerateFixedLocationsOverlappingTemp(const LifeIntervals *intervalForTemp, Func func) const
568     {
569         ASSERT(!intervalForTemp->HasInst());
570         ASSERT(intervalForTemp->GetBegin() + 1 == intervalForTemp->GetEnd());
571 
572         auto ln = intervalForTemp->GetEnd();
573         auto inst = GetInstByLifeNumber(ln);
574         ASSERT(inst != nullptr);
575 
576         for (size_t i = 0; i < inst->GetInputsCount(); i++) {
577             auto location = inst->GetLocation(i);
578             if (location.IsFixedRegister()) {
579                 func(location);
580             }
581         }
582     }
583 
584     const UseTable &GetUseTable() const;
585     size_t GetBlocksCount() const;
586     LifeIntervals *GetTmpRegInterval(const Inst *inst);
587     bool IsCallBlockingRegisters(Inst *inst) const;
588 
589     void DumpLifeIntervals(std::ostream &out = std::cout) const;
590     void DumpLocationsUsage(std::ostream &out = std::cout) const;
591 
592 private:
593     ArenaAllocator *GetAllocator();
594     void ResetLiveness();
595 
596     /*
597      * Blocks linearization methods
598      */
599     bool AllForwardEdgesVisited(BasicBlock *block);
600     void BuildBlocksLinearOrder();
601     template <bool USE_PC_ORDER>
602     void LinearizeBlocks();
603     template <bool USE_PC_ORDER>
604     void InsertSuccToPendingList(ArenaList<BasicBlock *> &pending, BasicBlock *succ);
605     bool CheckLinearOrder();
606 
607     /*
608      * Lifetime analysis methods
609      */
610     void BuildInstLifeNumbers();
611     void BuildInstLifeIntervals();
612     void ProcessBlockLiveInstructions(BasicBlock *block, InstLiveSet *liveSet);
613     void AdjustInputsLifetime(Inst *inst, LiveRange liveRange, InstLiveSet *liveSet);
614     void SetInputRange(const Inst *inst, const Inst *input, LiveRange liveRange) const;
615     void CreateLifeIntervals(Inst *inst);
616     void CreateIntervalForTemp(LifeNumber ln);
617     InstLiveSet *GetInitInstLiveSet(BasicBlock *block);
618     LifeNumber GetInstLifeNumber(const Inst *inst) const;
619     void SetInstLifeNumber(const Inst *inst, LifeNumber number);
620     void SetBlockLiveRange(BasicBlock *block, LiveRange lifeRange);
621     void SetBlockLiveSet(BasicBlock *block, InstLiveSet *liveSet);
622     InstLiveSet *GetBlockLiveSet(BasicBlock *block) const;
623     LifeNumber GetLoopEnd(Loop *loop);
624     LiveRange GetPropagatedLiveRange(Inst *inst, LiveRange liveRange);
625     void AdjustCatchPhiInputsLifetime(Inst *inst);
626     void SetUsePositions(Inst *userInst, LifeNumber lifeNumber);
627 
628     void BlockFixedRegisters(Inst *inst);
629     template <bool IS_FP>
630     void BlockReg(Register reg, LifeNumber blockFrom, LifeNumber blockTo, bool isUse);
631     template <bool IS_FP>
632     void BlockPhysicalRegisters(LifeNumber blockFrom);
633     void BlockFixedLocationRegister(Location location, LifeNumber ln);
634     void BlockFixedLocationRegister(Location location, LifeNumber blockFrom, LifeNumber blockTo, bool isUse);
635     void ProcessOpcodeLiveOut(BasicBlock *block, LifeIntervals *interval, LifeNumber instLifeNumber);
636 
637 private:
638     ArenaAllocator *allocator_;
639     ArenaVector<BasicBlock *> linearBlocks_;
640     ArenaVector<LifeNumber> instLifeNumbers_;
641     ArenaVector<LifeIntervals *> instLifeIntervals_;
642     InstVector instsByLifeNumber_;
643     ArenaVector<LiveRange> blockLiveRanges_;
644     ArenaVector<InstLiveSet *> blockLiveSets_;
645     ArenaMultiMap<Inst *, Inst *> pendingCatchPhiInputs_;
646     ArenaVector<LifeIntervals *> physicalGeneralIntervals_;
647     ArenaVector<LifeIntervals *> physicalVectorIntervals_;
648     ArenaVector<LifeIntervals *> intervalsForTemps_;
649     UseTable useTable_;
650     bool hasSafepointDuringCall_;
651 
652     Marker marker_ {UNDEF_MARKER};
653 #ifndef NDEBUG
654     bool finalized_ {};
655 #endif
656 };
657 
658 float CalcSpillWeight(const LivenessAnalyzer &la, LifeIntervals *interval);
659 }  // namespace ark::compiler
660 
661 #endif  // COMPILER_OPTIMIZER_ANALYSIS_LIVENESS_ANALIZER_H
662