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