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