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