1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #ifndef ART_COMPILER_OPTIMIZING_SSA_LIVENESS_ANALYSIS_H_ 18 #define ART_COMPILER_OPTIMIZING_SSA_LIVENESS_ANALYSIS_H_ 19 20 #include "nodes.h" 21 22 namespace art { 23 24 class CodeGenerator; 25 26 class BlockInfo : public ArenaObject { 27 public: BlockInfo(ArenaAllocator * allocator,const HBasicBlock & block,size_t number_of_ssa_values)28 BlockInfo(ArenaAllocator* allocator, const HBasicBlock& block, size_t number_of_ssa_values) 29 : block_(block), 30 live_in_(allocator, number_of_ssa_values, false), 31 live_out_(allocator, number_of_ssa_values, false), 32 kill_(allocator, number_of_ssa_values, false) { 33 live_in_.ClearAllBits(); 34 live_out_.ClearAllBits(); 35 kill_.ClearAllBits(); 36 } 37 38 private: 39 const HBasicBlock& block_; 40 ArenaBitVector live_in_; 41 ArenaBitVector live_out_; 42 ArenaBitVector kill_; 43 44 friend class SsaLivenessAnalysis; 45 46 DISALLOW_COPY_AND_ASSIGN(BlockInfo); 47 }; 48 49 /** 50 * A live range contains the start and end of a range where an instruction 51 * is live. 52 */ 53 class LiveRange : public ArenaObject { 54 public: LiveRange(size_t start,size_t end,LiveRange * next)55 LiveRange(size_t start, size_t end, LiveRange* next) : start_(start), end_(end), next_(next) { 56 DCHECK_LT(start, end); 57 DCHECK(next_ == nullptr || next_->GetStart() > GetEnd()); 58 } 59 GetStart()60 size_t GetStart() const { return start_; } GetEnd()61 size_t GetEnd() const { return end_; } GetNext()62 LiveRange* GetNext() const { return next_; } 63 IntersectsWith(const LiveRange & other)64 bool IntersectsWith(const LiveRange& other) { 65 return (start_ >= other.start_ && start_ < other.end_) 66 || (other.start_ >= start_ && other.start_ < end_); 67 } 68 IsBefore(const LiveRange & other)69 bool IsBefore(const LiveRange& other) { 70 return end_ <= other.start_; 71 } 72 Dump(std::ostream & stream)73 void Dump(std::ostream& stream) { 74 stream << "[" << start_ << ", " << end_ << ")"; 75 } 76 77 private: 78 size_t start_; 79 size_t end_; 80 LiveRange* next_; 81 82 friend class LiveInterval; 83 84 DISALLOW_COPY_AND_ASSIGN(LiveRange); 85 }; 86 87 /** 88 * A use position represents a live interval use at a given position. 89 */ 90 class UsePosition : public ArenaObject { 91 public: UsePosition(HInstruction * user,size_t input_index,bool is_environment,size_t position,UsePosition * next)92 UsePosition(HInstruction* user, 93 size_t input_index, 94 bool is_environment, 95 size_t position, 96 UsePosition* next) 97 : user_(user), 98 input_index_(input_index), 99 is_environment_(is_environment), 100 position_(position), 101 next_(next) { 102 DCHECK(user->AsPhi() != nullptr || GetPosition() == user->GetLifetimePosition() + 1); 103 DCHECK(next_ == nullptr || next->GetPosition() >= GetPosition()); 104 } 105 GetPosition()106 size_t GetPosition() const { return position_; } 107 GetNext()108 UsePosition* GetNext() const { return next_; } 109 GetUser()110 HInstruction* GetUser() const { return user_; } 111 GetIsEnvironment()112 bool GetIsEnvironment() const { return is_environment_; } 113 GetInputIndex()114 size_t GetInputIndex() const { return input_index_; } 115 Dump(std::ostream & stream)116 void Dump(std::ostream& stream) const { 117 stream << position_; 118 } 119 120 private: 121 HInstruction* const user_; 122 const size_t input_index_; 123 const bool is_environment_; 124 const size_t position_; 125 UsePosition* const next_; 126 127 DISALLOW_COPY_AND_ASSIGN(UsePosition); 128 }; 129 130 /** 131 * An interval is a list of disjoint live ranges where an instruction is live. 132 * Each instruction that has uses gets an interval. 133 */ 134 class LiveInterval : public ArenaObject { 135 public: 136 LiveInterval(ArenaAllocator* allocator, Primitive::Type type, HInstruction* defined_by = nullptr) allocator_(allocator)137 : allocator_(allocator), 138 first_range_(nullptr), 139 last_range_(nullptr), 140 first_use_(nullptr), 141 type_(type), 142 next_sibling_(nullptr), 143 parent_(this), 144 register_(kNoRegister), 145 spill_slot_(kNoSpillSlot), 146 is_fixed_(false), 147 defined_by_(defined_by) {} 148 MakeFixedInterval(ArenaAllocator * allocator,int reg,Primitive::Type type)149 static LiveInterval* MakeFixedInterval(ArenaAllocator* allocator, int reg, Primitive::Type type) { 150 LiveInterval* interval = new (allocator) LiveInterval(allocator, type); 151 interval->SetRegister(reg); 152 interval->is_fixed_ = true; 153 return interval; 154 } 155 IsFixed()156 bool IsFixed() const { return is_fixed_; } 157 AddUse(HInstruction * instruction,size_t input_index,bool is_environment)158 void AddUse(HInstruction* instruction, size_t input_index, bool is_environment) { 159 // Set the use within the instruction. 160 // TODO: Use the instruction's location to know whether the instruction can die 161 // at entry, or needs to say alive within the user. 162 size_t position = instruction->GetLifetimePosition() + 1; 163 size_t start_block_position = instruction->GetBlock()->GetLifetimeStart(); 164 size_t end_block_position = instruction->GetBlock()->GetLifetimeEnd(); 165 if (first_range_ == nullptr) { 166 // First time we see a use of that interval. 167 first_range_ = last_range_ = new (allocator_) LiveRange(start_block_position, position, nullptr); 168 } else if (first_range_->GetStart() == start_block_position) { 169 // There is a use later in the same block. 170 DCHECK_LE(position, first_range_->GetEnd()); 171 } else if (first_range_->GetStart() == end_block_position) { 172 // Last use is in the following block. 173 first_range_->start_ = start_block_position; 174 } else { 175 DCHECK(first_range_->GetStart() > position); 176 // There is a hole in the interval. Create a new range. 177 first_range_ = new (allocator_) LiveRange(start_block_position, position, first_range_); 178 } 179 first_use_ = new (allocator_) UsePosition( 180 instruction, input_index, is_environment, position, first_use_); 181 } 182 AddPhiUse(HInstruction * instruction,size_t input_index,HBasicBlock * block)183 void AddPhiUse(HInstruction* instruction, size_t input_index, HBasicBlock* block) { 184 DCHECK(instruction->AsPhi() != nullptr); 185 first_use_ = new (allocator_) UsePosition( 186 instruction, input_index, false, block->GetLifetimeEnd(), first_use_); 187 } 188 AddRange(size_t start,size_t end)189 void AddRange(size_t start, size_t end) { 190 if (first_range_ == nullptr) { 191 first_range_ = last_range_ = new (allocator_) LiveRange(start, end, first_range_); 192 } else if (first_range_->GetStart() == end) { 193 // There is a use in the following block. 194 first_range_->start_ = start; 195 } else { 196 DCHECK(first_range_->GetStart() > end); 197 // There is a hole in the interval. Create a new range. 198 first_range_ = new (allocator_) LiveRange(start, end, first_range_); 199 } 200 } 201 AddLoopRange(size_t start,size_t end)202 void AddLoopRange(size_t start, size_t end) { 203 DCHECK(first_range_ != nullptr); 204 while (first_range_ != nullptr && first_range_->GetEnd() < end) { 205 DCHECK_LE(start, first_range_->GetStart()); 206 first_range_ = first_range_->GetNext(); 207 } 208 if (first_range_ == nullptr) { 209 // Uses are only in the loop. 210 first_range_ = last_range_ = new (allocator_) LiveRange(start, end, nullptr); 211 } else { 212 // There are uses after the loop. 213 first_range_->start_ = start; 214 } 215 } 216 HasSpillSlot()217 bool HasSpillSlot() const { return spill_slot_ != kNoSpillSlot; } SetSpillSlot(int slot)218 void SetSpillSlot(int slot) { spill_slot_ = slot; } GetSpillSlot()219 int GetSpillSlot() const { return spill_slot_; } 220 SetFrom(size_t from)221 void SetFrom(size_t from) { 222 if (first_range_ != nullptr) { 223 first_range_->start_ = from; 224 } else { 225 // Instruction without uses. 226 DCHECK(!defined_by_->HasUses()); 227 DCHECK(from == defined_by_->GetLifetimePosition()); 228 first_range_ = last_range_ = new (allocator_) LiveRange(from, from + 2, nullptr); 229 } 230 } 231 GetParent()232 LiveInterval* GetParent() const { return parent_; } 233 GetFirstRange()234 LiveRange* GetFirstRange() const { return first_range_; } 235 GetRegister()236 int GetRegister() const { return register_; } SetRegister(int reg)237 void SetRegister(int reg) { register_ = reg; } ClearRegister()238 void ClearRegister() { register_ = kNoRegister; } HasRegister()239 bool HasRegister() const { return register_ != kNoRegister; } 240 IsDeadAt(size_t position)241 bool IsDeadAt(size_t position) const { 242 return last_range_->GetEnd() <= position; 243 } 244 Covers(size_t position)245 bool Covers(size_t position) const { 246 LiveRange* current = first_range_; 247 while (current != nullptr) { 248 if (position >= current->GetStart() && position < current->GetEnd()) { 249 return true; 250 } 251 current = current->GetNext(); 252 } 253 return false; 254 } 255 256 /** 257 * Returns the first intersection of this interval with `other`. 258 */ FirstIntersectionWith(LiveInterval * other)259 size_t FirstIntersectionWith(LiveInterval* other) const { 260 // Advance both intervals and find the first matching range start in 261 // this interval. 262 LiveRange* my_range = first_range_; 263 LiveRange* other_range = other->first_range_; 264 do { 265 if (my_range->IntersectsWith(*other_range)) { 266 return std::max(my_range->GetStart(), other_range->GetStart()); 267 } else if (my_range->IsBefore(*other_range)) { 268 my_range = my_range->GetNext(); 269 if (my_range == nullptr) { 270 return kNoLifetime; 271 } 272 } else { 273 DCHECK(other_range->IsBefore(*my_range)); 274 other_range = other_range->GetNext(); 275 if (other_range == nullptr) { 276 return kNoLifetime; 277 } 278 } 279 } while (true); 280 } 281 GetStart()282 size_t GetStart() const { 283 return first_range_->GetStart(); 284 } 285 GetEnd()286 size_t GetEnd() const { 287 return last_range_->GetEnd(); 288 } 289 FirstRegisterUseAfter(size_t position)290 size_t FirstRegisterUseAfter(size_t position) const { 291 if (position == GetStart() && defined_by_ != nullptr) { 292 LocationSummary* locations = defined_by_->GetLocations(); 293 Location location = locations->Out(); 294 // This interval is the first interval of the instruction. If the output 295 // of the instruction requires a register, we return the position of that instruction 296 // as the first register use. 297 if (location.IsUnallocated()) { 298 if ((location.GetPolicy() == Location::kRequiresRegister) 299 || (location.GetPolicy() == Location::kSameAsFirstInput 300 && locations->InAt(0).GetPolicy() == Location::kRequiresRegister)) { 301 return position; 302 } 303 } 304 } 305 306 UsePosition* use = first_use_; 307 size_t end = GetEnd(); 308 while (use != nullptr && use->GetPosition() <= end) { 309 size_t use_position = use->GetPosition(); 310 if (use_position >= position && !use->GetIsEnvironment()) { 311 Location location = use->GetUser()->GetLocations()->InAt(use->GetInputIndex()); 312 if (location.IsUnallocated() && location.GetPolicy() == Location::kRequiresRegister) { 313 return use_position; 314 } 315 } 316 use = use->GetNext(); 317 } 318 return kNoLifetime; 319 } 320 FirstRegisterUse()321 size_t FirstRegisterUse() const { 322 return FirstRegisterUseAfter(GetStart()); 323 } 324 GetFirstUse()325 UsePosition* GetFirstUse() const { 326 return first_use_; 327 } 328 GetType()329 Primitive::Type GetType() const { 330 return type_; 331 } 332 GetDefinedBy()333 HInstruction* GetDefinedBy() const { 334 return defined_by_; 335 } 336 337 /** 338 * Split this interval at `position`. This interval is changed to: 339 * [start ... position). 340 * 341 * The new interval covers: 342 * [position ... end) 343 */ SplitAt(size_t position)344 LiveInterval* SplitAt(size_t position) { 345 DCHECK(!is_fixed_); 346 DCHECK_GT(position, GetStart()); 347 348 if (last_range_->GetEnd() <= position) { 349 // This range dies before `position`, no need to split. 350 return nullptr; 351 } 352 353 LiveInterval* new_interval = new (allocator_) LiveInterval(allocator_, type_); 354 new_interval->next_sibling_ = next_sibling_; 355 next_sibling_ = new_interval; 356 new_interval->parent_ = parent_; 357 358 new_interval->first_use_ = first_use_; 359 LiveRange* current = first_range_; 360 LiveRange* previous = nullptr; 361 // Iterate over the ranges, and either find a range that covers this position, or 362 // a two ranges in between this position (that is, the position is in a lifetime hole). 363 do { 364 if (position >= current->GetEnd()) { 365 // Move to next range. 366 previous = current; 367 current = current->next_; 368 } else if (position <= current->GetStart()) { 369 // If the previous range did not cover this position, we know position is in 370 // a lifetime hole. We can just break the first_range_ and last_range_ links 371 // and return the new interval. 372 DCHECK(previous != nullptr); 373 DCHECK(current != first_range_); 374 new_interval->last_range_ = last_range_; 375 last_range_ = previous; 376 previous->next_ = nullptr; 377 new_interval->first_range_ = current; 378 return new_interval; 379 } else { 380 // This range covers position. We create a new last_range_ for this interval 381 // that covers last_range_->Start() and position. We also shorten the current 382 // range and make it the first range of the new interval. 383 DCHECK(position < current->GetEnd() && position > current->GetStart()); 384 new_interval->last_range_ = last_range_; 385 last_range_ = new (allocator_) LiveRange(current->start_, position, nullptr); 386 if (previous != nullptr) { 387 previous->next_ = last_range_; 388 } else { 389 first_range_ = last_range_; 390 } 391 new_interval->first_range_ = current; 392 current->start_ = position; 393 return new_interval; 394 } 395 } while (current != nullptr); 396 397 LOG(FATAL) << "Unreachable"; 398 return nullptr; 399 } 400 StartsBefore(LiveInterval * other)401 bool StartsBefore(LiveInterval* other) const { 402 return GetStart() <= other->GetStart(); 403 } 404 StartsAfter(LiveInterval * other)405 bool StartsAfter(LiveInterval* other) const { 406 return GetStart() >= other->GetStart(); 407 } 408 Dump(std::ostream & stream)409 void Dump(std::ostream& stream) const { 410 stream << "ranges: { "; 411 LiveRange* current = first_range_; 412 do { 413 current->Dump(stream); 414 stream << " "; 415 } while ((current = current->GetNext()) != nullptr); 416 stream << "}, uses: { "; 417 UsePosition* use = first_use_; 418 if (use != nullptr) { 419 do { 420 use->Dump(stream); 421 stream << " "; 422 } while ((use = use->GetNext()) != nullptr); 423 } 424 stream << "}"; 425 } 426 GetNextSibling()427 LiveInterval* GetNextSibling() const { return next_sibling_; } 428 429 private: 430 ArenaAllocator* const allocator_; 431 432 // Ranges of this interval. We need a quick access to the last range to test 433 // for liveness (see `IsDeadAt`). 434 LiveRange* first_range_; 435 LiveRange* last_range_; 436 437 // Uses of this interval. Note that this linked list is shared amongst siblings. 438 UsePosition* first_use_; 439 440 // The instruction type this interval corresponds to. 441 const Primitive::Type type_; 442 443 // Live interval that is the result of a split. 444 LiveInterval* next_sibling_; 445 446 // The first interval from which split intervals come from. 447 LiveInterval* parent_; 448 449 // The register allocated to this interval. 450 int register_; 451 452 // The spill slot allocated to this interval. 453 int spill_slot_; 454 455 // Whether the interval is for a fixed register. 456 bool is_fixed_; 457 458 // The instruction represented by this interval. 459 HInstruction* const defined_by_; 460 461 static constexpr int kNoRegister = -1; 462 static constexpr int kNoSpillSlot = -1; 463 464 DISALLOW_COPY_AND_ASSIGN(LiveInterval); 465 }; 466 467 class SsaLivenessAnalysis : public ValueObject { 468 public: SsaLivenessAnalysis(const HGraph & graph,CodeGenerator * codegen)469 SsaLivenessAnalysis(const HGraph& graph, CodeGenerator* codegen) 470 : graph_(graph), 471 codegen_(codegen), 472 linear_post_order_(graph.GetArena(), graph.GetBlocks().Size()), 473 block_infos_(graph.GetArena(), graph.GetBlocks().Size()), 474 instructions_from_ssa_index_(graph.GetArena(), 0), 475 instructions_from_lifetime_position_(graph.GetArena(), 0), 476 number_of_ssa_values_(0) { 477 block_infos_.SetSize(graph.GetBlocks().Size()); 478 } 479 480 void Analyze(); 481 GetLiveInSet(const HBasicBlock & block)482 BitVector* GetLiveInSet(const HBasicBlock& block) const { 483 return &block_infos_.Get(block.GetBlockId())->live_in_; 484 } 485 GetLiveOutSet(const HBasicBlock & block)486 BitVector* GetLiveOutSet(const HBasicBlock& block) const { 487 return &block_infos_.Get(block.GetBlockId())->live_out_; 488 } 489 GetKillSet(const HBasicBlock & block)490 BitVector* GetKillSet(const HBasicBlock& block) const { 491 return &block_infos_.Get(block.GetBlockId())->kill_; 492 } 493 GetLinearPostOrder()494 const GrowableArray<HBasicBlock*>& GetLinearPostOrder() const { 495 return linear_post_order_; 496 } 497 GetInstructionFromSsaIndex(size_t index)498 HInstruction* GetInstructionFromSsaIndex(size_t index) const { 499 return instructions_from_ssa_index_.Get(index); 500 } 501 GetInstructionFromPosition(size_t index)502 HInstruction* GetInstructionFromPosition(size_t index) const { 503 return instructions_from_lifetime_position_.Get(index); 504 } 505 GetMaxLifetimePosition()506 size_t GetMaxLifetimePosition() const { 507 return instructions_from_lifetime_position_.Size() * 2 - 1; 508 } 509 GetNumberOfSsaValues()510 size_t GetNumberOfSsaValues() const { 511 return number_of_ssa_values_; 512 } 513 514 private: 515 // Linearize the graph so that: 516 // (1): a block is always after its dominator, 517 // (2): blocks of loops are contiguous. 518 // This creates a natural and efficient ordering when visualizing live ranges. 519 void LinearizeGraph(); 520 521 // Give an SSA number to each instruction that defines a value used by another instruction, 522 // and setup the lifetime information of each instruction and block. 523 void NumberInstructions(); 524 525 // Compute live ranges of instructions, as well as live_in, live_out and kill sets. 526 void ComputeLiveness(); 527 528 // Compute the live ranges of instructions, as well as the initial live_in, live_out and 529 // kill sets, that do not take into account backward branches. 530 void ComputeLiveRanges(); 531 532 // After computing the initial sets, this method does a fixed point 533 // calculation over the live_in and live_out set to take into account 534 // backwards branches. 535 void ComputeLiveInAndLiveOutSets(); 536 537 // Update the live_in set of the block and returns whether it has changed. 538 bool UpdateLiveIn(const HBasicBlock& block); 539 540 // Update the live_out set of the block and returns whether it has changed. 541 bool UpdateLiveOut(const HBasicBlock& block); 542 543 const HGraph& graph_; 544 CodeGenerator* const codegen_; 545 GrowableArray<HBasicBlock*> linear_post_order_; 546 GrowableArray<BlockInfo*> block_infos_; 547 548 // Temporary array used when computing live_in, live_out, and kill sets. 549 GrowableArray<HInstruction*> instructions_from_ssa_index_; 550 551 // Temporary array used when inserting moves in the graph. 552 GrowableArray<HInstruction*> instructions_from_lifetime_position_; 553 size_t number_of_ssa_values_; 554 555 DISALLOW_COPY_AND_ASSIGN(SsaLivenessAnalysis); 556 }; 557 558 class HLinearOrderIterator : public ValueObject { 559 public: HLinearOrderIterator(const SsaLivenessAnalysis & liveness)560 explicit HLinearOrderIterator(const SsaLivenessAnalysis& liveness) 561 : post_order_(liveness.GetLinearPostOrder()), index_(liveness.GetLinearPostOrder().Size()) {} 562 Done()563 bool Done() const { return index_ == 0; } Current()564 HBasicBlock* Current() const { return post_order_.Get(index_ -1); } Advance()565 void Advance() { --index_; DCHECK_GE(index_, 0U); } 566 567 private: 568 const GrowableArray<HBasicBlock*>& post_order_; 569 size_t index_; 570 571 DISALLOW_COPY_AND_ASSIGN(HLinearOrderIterator); 572 }; 573 574 class HLinearPostOrderIterator : public ValueObject { 575 public: HLinearPostOrderIterator(const SsaLivenessAnalysis & liveness)576 explicit HLinearPostOrderIterator(const SsaLivenessAnalysis& liveness) 577 : post_order_(liveness.GetLinearPostOrder()), index_(0) {} 578 Done()579 bool Done() const { return index_ == post_order_.Size(); } Current()580 HBasicBlock* Current() const { return post_order_.Get(index_); } Advance()581 void Advance() { ++index_; } 582 583 private: 584 const GrowableArray<HBasicBlock*>& post_order_; 585 size_t index_; 586 587 DISALLOW_COPY_AND_ASSIGN(HLinearPostOrderIterator); 588 }; 589 590 } // namespace art 591 592 #endif // ART_COMPILER_OPTIMIZING_SSA_LIVENESS_ANALYSIS_H_ 593