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