Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2014 the V8 project authors. All rights reserved. | 1 // Copyright 2014 the V8 project authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "src/base/adapters.h" | 5 #include "src/base/adapters.h" |
| 6 #include "src/compiler/linkage.h" | 6 #include "src/compiler/linkage.h" |
| 7 #include "src/compiler/register-allocator.h" | 7 #include "src/compiler/register-allocator.h" |
| 8 #include "src/string-stream.h" | 8 #include "src/string-stream.h" |
| 9 | 9 |
| 10 namespace v8 { | 10 namespace v8 { |
| (...skipping 324 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 335 DCHECK(op.IsStackSlot() || op.IsFPStackSlot()); | 335 DCHECK(op.IsStackSlot() || op.IsFPStackSlot()); |
| 336 return UsePositionHintType::kNone; | 336 return UsePositionHintType::kNone; |
| 337 } | 337 } |
| 338 case InstructionOperand::INVALID: | 338 case InstructionOperand::INVALID: |
| 339 break; | 339 break; |
| 340 } | 340 } |
| 341 UNREACHABLE(); | 341 UNREACHABLE(); |
| 342 return UsePositionHintType::kNone; | 342 return UsePositionHintType::kNone; |
| 343 } | 343 } |
| 344 | 344 |
| 345 void UsePosition::SetHint(UsePosition* use_pos) { | |
| 346 DCHECK_NOT_NULL(use_pos); | |
| 347 hint_ = use_pos; | |
| 348 flags_ = HintTypeField::update(flags_, UsePositionHintType::kUsePos); | |
| 349 } | |
| 345 | 350 |
| 346 void UsePosition::ResolveHint(UsePosition* use_pos) { | 351 void UsePosition::ResolveHint(UsePosition* use_pos) { |
| 347 DCHECK_NOT_NULL(use_pos); | 352 DCHECK_NOT_NULL(use_pos); |
| 348 if (HintTypeField::decode(flags_) != UsePositionHintType::kUnresolved) return; | 353 if (HintTypeField::decode(flags_) != UsePositionHintType::kUnresolved) return; |
| 349 hint_ = use_pos; | 354 hint_ = use_pos; |
| 350 flags_ = HintTypeField::update(flags_, UsePositionHintType::kUsePos); | 355 flags_ = HintTypeField::update(flags_, UsePositionHintType::kUsePos); |
| 351 } | 356 } |
| 352 | 357 |
| 353 | 358 |
| 354 void UsePosition::set_type(UsePositionType type, bool register_beneficial) { | 359 void UsePosition::set_type(UsePositionType type, bool register_beneficial) { |
| (...skipping 219 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 574 : current_interval_->start(); | 579 : current_interval_->start(); |
| 575 if (to_start_of->start() > start) { | 580 if (to_start_of->start() > start) { |
| 576 current_interval_ = to_start_of; | 581 current_interval_ = to_start_of; |
| 577 } | 582 } |
| 578 } | 583 } |
| 579 | 584 |
| 580 | 585 |
| 581 LiveRange* LiveRange::SplitAt(LifetimePosition position, Zone* zone) { | 586 LiveRange* LiveRange::SplitAt(LifetimePosition position, Zone* zone) { |
| 582 int new_id = TopLevel()->GetNextChildId(); | 587 int new_id = TopLevel()->GetNextChildId(); |
| 583 LiveRange* child = new (zone) LiveRange(new_id, representation(), TopLevel()); | 588 LiveRange* child = new (zone) LiveRange(new_id, representation(), TopLevel()); |
| 584 DetachAt(position, child, zone); | 589 // If we split, we do so because we're about to switch registers or move |
| 590 // to/from | |
|
Jarin
2016/09/29 14:20:34
nit: merge the comment with the next line.
| |
| 591 // a slot, so there's no value in connecting hints. | |
| 592 DetachAt(position, child, zone, false); | |
| 585 | 593 |
| 586 child->top_level_ = TopLevel(); | 594 child->top_level_ = TopLevel(); |
| 587 child->next_ = next_; | 595 child->next_ = next_; |
| 588 next_ = child; | 596 next_ = child; |
| 589 return child; | 597 return child; |
| 590 } | 598 } |
| 591 | 599 |
| 592 | |
| 593 UsePosition* LiveRange::DetachAt(LifetimePosition position, LiveRange* result, | 600 UsePosition* LiveRange::DetachAt(LifetimePosition position, LiveRange* result, |
| 594 Zone* zone) { | 601 Zone* zone, bool connect_hints) { |
| 595 DCHECK(Start() < position); | 602 DCHECK(Start() < position); |
| 596 DCHECK(End() > position); | 603 DCHECK(End() > position); |
| 597 DCHECK(result->IsEmpty()); | 604 DCHECK(result->IsEmpty()); |
| 598 // Find the last interval that ends before the position. If the | 605 // Find the last interval that ends before the position. If the |
| 599 // position is contained in one of the intervals in the chain, we | 606 // position is contained in one of the intervals in the chain, we |
| 600 // split that interval and use the first part. | 607 // split that interval and use the first part. |
| 601 UseInterval* current = FirstSearchIntervalForPosition(position); | 608 UseInterval* current = FirstSearchIntervalForPosition(position); |
| 602 | 609 |
| 603 // If the split position coincides with the beginning of a use interval | 610 // If the split position coincides with the beginning of a use interval |
| 604 // we need to split use positons in a special way. | 611 // we need to split use positons in a special way. |
| (...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 663 } else { | 670 } else { |
| 664 first_pos_ = nullptr; | 671 first_pos_ = nullptr; |
| 665 } | 672 } |
| 666 result->first_pos_ = use_after; | 673 result->first_pos_ = use_after; |
| 667 | 674 |
| 668 // Discard cached iteration state. It might be pointing | 675 // Discard cached iteration state. It might be pointing |
| 669 // to the use that no longer belongs to this live range. | 676 // to the use that no longer belongs to this live range. |
| 670 last_processed_use_ = nullptr; | 677 last_processed_use_ = nullptr; |
| 671 current_interval_ = nullptr; | 678 current_interval_ = nullptr; |
| 672 | 679 |
| 680 if (connect_hints && use_before != nullptr && use_after != nullptr) { | |
| 681 use_after->SetHint(use_before); | |
| 682 } | |
| 673 #ifdef DEBUG | 683 #ifdef DEBUG |
| 674 VerifyChildStructure(); | 684 VerifyChildStructure(); |
| 675 result->VerifyChildStructure(); | 685 result->VerifyChildStructure(); |
| 676 #endif | 686 #endif |
| 677 return use_before; | 687 return use_before; |
| 678 } | 688 } |
| 679 | 689 |
| 680 | 690 |
| 681 void LiveRange::UpdateParentForAllChildren(TopLevelLiveRange* new_top_level) { | 691 void LiveRange::UpdateParentForAllChildren(TopLevelLiveRange* new_top_level) { |
| 682 LiveRange* child = this; | 692 LiveRange* child = this; |
| (...skipping 26 matching lines...) Expand all Loading... | |
| 709 | 719 |
| 710 // This implements an ordering on live ranges so that they are ordered by their | 720 // This implements an ordering on live ranges so that they are ordered by their |
| 711 // start positions. This is needed for the correctness of the register | 721 // start positions. This is needed for the correctness of the register |
| 712 // allocation algorithm. If two live ranges start at the same offset then there | 722 // allocation algorithm. If two live ranges start at the same offset then there |
| 713 // is a tie breaker based on where the value is first used. This part of the | 723 // is a tie breaker based on where the value is first used. This part of the |
| 714 // ordering is merely a heuristic. | 724 // ordering is merely a heuristic. |
| 715 bool LiveRange::ShouldBeAllocatedBefore(const LiveRange* other) const { | 725 bool LiveRange::ShouldBeAllocatedBefore(const LiveRange* other) const { |
| 716 LifetimePosition start = Start(); | 726 LifetimePosition start = Start(); |
| 717 LifetimePosition other_start = other->Start(); | 727 LifetimePosition other_start = other->Start(); |
| 718 if (start == other_start) { | 728 if (start == other_start) { |
| 729 bool this_is_splinter = TopLevel()->IsSplinter(); | |
| 730 bool other_is_splinter = other->TopLevel()->IsSplinter(); | |
| 731 if (this_is_splinter && other_is_splinter && | |
| 732 (this->FirstHintPosition() == nullptr) == | |
| 733 (other->FirstHintPosition() == nullptr)) { | |
| 734 if (this->FirstHintPosition() != nullptr && | |
| 735 other->FirstHintPosition() == nullptr) | |
|
Jarin
2016/09/29 14:20:35
I have no idea what these conditions say.
The con
Mircea Trofin
2016/09/30 05:16:51
Ya, they make no sense as written. Fixed.
| |
| 736 return true; | |
| 737 if (this->FirstHintPosition() == nullptr && | |
| 738 other->FirstHintPosition() != nullptr) | |
|
Jarin
2016/09/29 14:20:35
Also seems to be always false, which means that al
Mircea Trofin
2016/09/30 05:16:51
Acknowledged.
| |
| 739 return false; | |
| 740 } | |
| 719 UsePosition* pos = first_pos(); | 741 UsePosition* pos = first_pos(); |
| 720 if (pos == nullptr) return false; | 742 if (pos == nullptr) return false; |
| 721 UsePosition* other_pos = other->first_pos(); | 743 UsePosition* other_pos = other->first_pos(); |
| 722 if (other_pos == nullptr) return true; | 744 if (other_pos == nullptr) return true; |
| 723 return pos->pos() < other_pos->pos(); | 745 return pos->pos() < other_pos->pos(); |
| 724 } | 746 } |
| 725 return start < other_start; | 747 return start < other_start; |
| 726 } | 748 } |
| 727 | 749 |
| 728 | 750 |
| (...skipping 176 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 905 | 927 |
| 906 TopLevelLiveRange splinter_temp(-1, representation()); | 928 TopLevelLiveRange splinter_temp(-1, representation()); |
| 907 UsePosition* last_in_splinter = nullptr; | 929 UsePosition* last_in_splinter = nullptr; |
| 908 // Live ranges defined in deferred blocks stay in deferred blocks, so we | 930 // Live ranges defined in deferred blocks stay in deferred blocks, so we |
| 909 // don't need to splinter them. That means that start should always be | 931 // don't need to splinter them. That means that start should always be |
| 910 // after the beginning of the range. | 932 // after the beginning of the range. |
| 911 DCHECK(start > Start()); | 933 DCHECK(start > Start()); |
| 912 | 934 |
| 913 if (end >= End()) { | 935 if (end >= End()) { |
| 914 DCHECK(start > Start()); | 936 DCHECK(start > Start()); |
| 915 DetachAt(start, &splinter_temp, zone); | 937 DetachAt(start, &splinter_temp, zone, true); |
|
Jarin
2016/09/29 14:20:35
Here it is not really clear what "true" refers to.
Mircea Trofin
2016/09/30 05:16:51
Done.
| |
| 916 next_ = nullptr; | 938 next_ = nullptr; |
| 917 } else { | 939 } else { |
| 918 DCHECK(start < End() && Start() < end); | 940 DCHECK(start < End() && Start() < end); |
| 919 | 941 |
| 920 const int kInvalidId = std::numeric_limits<int>::max(); | 942 const int kInvalidId = std::numeric_limits<int>::max(); |
| 921 | 943 |
| 922 UsePosition* last = DetachAt(start, &splinter_temp, zone); | 944 UsePosition* last = DetachAt(start, &splinter_temp, zone, true); |
| 923 | 945 |
| 924 LiveRange end_part(kInvalidId, this->representation(), nullptr); | 946 LiveRange end_part(kInvalidId, this->representation(), nullptr); |
| 925 last_in_splinter = splinter_temp.DetachAt(end, &end_part, zone); | 947 // The last chunk exits the deferred region, and we don't want to connect |
| 948 // hints here, because the non-deferred region shouldn't be affected | |
| 949 // by allocation decisions on the deferred path. | |
| 950 last_in_splinter = splinter_temp.DetachAt(end, &end_part, zone, false); | |
| 926 | 951 |
| 927 next_ = end_part.next_; | 952 next_ = end_part.next_; |
| 928 last_interval_->set_next(end_part.first_interval_); | 953 last_interval_->set_next(end_part.first_interval_); |
| 929 // The next splinter will happen either at or after the current interval. | 954 // The next splinter will happen either at or after the current interval. |
| 930 // We can optimize DetachAt by setting current_interval_ accordingly, | 955 // We can optimize DetachAt by setting current_interval_ accordingly, |
| 931 // which will then be picked up by FirstSearchIntervalForPosition. | 956 // which will then be picked up by FirstSearchIntervalForPosition. |
| 932 current_interval_ = last_interval_; | 957 current_interval_ = last_interval_; |
| 933 last_interval_ = end_part.last_interval_; | 958 last_interval_ = end_part.last_interval_; |
| 934 | 959 |
| 935 if (first_pos_ == nullptr) { | 960 if (first_pos_ == nullptr) { |
| (...skipping 1458 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 2394 (range->HasSpillRange() && !range->has_slot_use())) { | 2419 (range->HasSpillRange() && !range->has_slot_use())) { |
| 2395 continue; | 2420 continue; |
| 2396 } | 2421 } |
| 2397 LifetimePosition start = range->Start(); | 2422 LifetimePosition start = range->Start(); |
| 2398 TRACE("Live range %d:%d is defined by a spill operand.\n", | 2423 TRACE("Live range %d:%d is defined by a spill operand.\n", |
| 2399 range->TopLevel()->vreg(), range->relative_id()); | 2424 range->TopLevel()->vreg(), range->relative_id()); |
| 2400 LifetimePosition next_pos = start; | 2425 LifetimePosition next_pos = start; |
| 2401 if (next_pos.IsGapPosition()) { | 2426 if (next_pos.IsGapPosition()) { |
| 2402 next_pos = next_pos.NextStart(); | 2427 next_pos = next_pos.NextStart(); |
| 2403 } | 2428 } |
| 2404 UsePosition* pos = range->NextUsePositionRegisterIsBeneficial(next_pos); | 2429 |
| 2430 // With splinters, we can be more strict and skip over positions | |
| 2431 // not strictly needing registers. | |
| 2432 UsePosition* pos = | |
| 2433 range->IsSplinter() | |
| 2434 ? range->NextRegisterPosition(next_pos) | |
| 2435 : range->NextUsePositionRegisterIsBeneficial(next_pos); | |
|
Jarin
2016/09/29 14:20:35
Does this make any measurable difference?
Mircea Trofin
2016/09/30 05:16:51
I remember seeing a case where this helped avoid a
| |
| 2405 // If the range already has a spill operand and it doesn't need a | 2436 // If the range already has a spill operand and it doesn't need a |
| 2406 // register immediately, split it and spill the first part of the range. | 2437 // register immediately, split it and spill the first part of the range. |
| 2407 if (pos == nullptr) { | 2438 if (pos == nullptr) { |
| 2408 Spill(range); | 2439 Spill(range); |
| 2409 } else if (pos->pos() > range->Start().NextStart()) { | 2440 } else if (pos->pos() > range->Start().NextStart()) { |
| 2410 // Do not spill live range eagerly if use position that can benefit from | 2441 // Do not spill live range eagerly if use position that can benefit from |
| 2411 // the register is too close to the start of live range. | 2442 // the register is too close to the start of live range. |
| 2412 LifetimePosition split_pos = GetSplitPositionForInstruction( | 2443 LifetimePosition split_pos = GetSplitPositionForInstruction( |
| 2413 range, pos->pos().ToInstructionIndex()); | 2444 range, pos->pos().ToInstructionIndex()); |
| 2414 // There is no place to split, so we can't split and spill. | 2445 // There is no place to split, so we can't split and spill. |
| (...skipping 382 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 2797 // The desired register is free until the end of the current live range. | 2828 // The desired register is free until the end of the current live range. |
| 2798 if (free_until_pos[hint_register] >= current->End()) { | 2829 if (free_until_pos[hint_register] >= current->End()) { |
| 2799 TRACE("Assigning preferred reg %s to live range %d:%d\n", | 2830 TRACE("Assigning preferred reg %s to live range %d:%d\n", |
| 2800 RegisterName(hint_register), current->TopLevel()->vreg(), | 2831 RegisterName(hint_register), current->TopLevel()->vreg(), |
| 2801 current->relative_id()); | 2832 current->relative_id()); |
| 2802 SetLiveRangeAssignedRegister(current, hint_register); | 2833 SetLiveRangeAssignedRegister(current, hint_register); |
| 2803 return true; | 2834 return true; |
| 2804 } | 2835 } |
| 2805 } | 2836 } |
| 2806 | 2837 |
| 2838 if (current->TopLevel()->IsSplinter()) { | |
| 2839 // We weren't able to give the splinter the hinted register. Whatever we do, | |
| 2840 // it will result in a move. For splinters, our goal is to minimize | |
|
Jarin
2016/09/29 14:20:35
I do not understand this reasoning - if there is n
Mircea Trofin
2016/09/30 05:16:51
The hint says, for splinters, what operand the pre
Jarin
2016/09/30 13:23:03
I still do not really understand when we get here.
Mircea Trofin
2016/09/30 14:45:48
The hints for splinters are added when we create t
| |
| 2841 // moves, for code size, not their quality - meaning, no need to try hard | |
| 2842 // and keep the value in a register. | |
| 2843 // If we can spill the whole range, great. Otherwise, split above the | |
| 2844 // first use needing a register and spill the top part. No need to involve | |
| 2845 // the hint (e.g. what if we could give the upper chunk the hint), because | |
| 2846 // we | |
|
Jarin
2016/09/29 14:20:35
What's up with the formatting of the comments in t
Mircea Trofin
2016/09/30 05:16:51
probably git cl format :) I'll re-brush them.
| |
| 2847 // would then have at least the same number of moves. | |
| 2848 const UsePosition* next_reg = | |
| 2849 current->NextRegisterPosition(current->Start()); | |
| 2850 if (next_reg == nullptr) { | |
| 2851 Spill(current); | |
| 2852 return true; | |
| 2853 } else if (next_reg->pos().PrevStart() > current->Start()) { | |
| 2854 LiveRange* tail = SplitRangeAt(current, next_reg->pos().PrevStart()); | |
| 2855 AddToUnhandledSorted(tail); | |
| 2856 Spill(current); | |
| 2857 return true; | |
| 2858 } | |
| 2859 } | |
| 2860 | |
| 2807 // Find the register which stays free for the longest time. | 2861 // Find the register which stays free for the longest time. |
| 2808 int reg = codes[0]; | 2862 int reg = codes[0]; |
| 2809 for (int i = 1; i < num_codes; ++i) { | 2863 for (int i = 1; i < num_codes; ++i) { |
| 2810 int code = codes[i]; | 2864 int code = codes[i]; |
| 2811 if (free_until_pos[code] > free_until_pos[reg]) { | 2865 if (free_until_pos[code] > free_until_pos[reg]) { |
| 2812 reg = code; | 2866 reg = code; |
| 2813 } | 2867 } |
| 2814 } | 2868 } |
| 2815 | 2869 |
| 2816 LifetimePosition pos = free_until_pos[reg]; | 2870 LifetimePosition pos = free_until_pos[reg]; |
| (...skipping 803 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 3620 } | 3674 } |
| 3621 } | 3675 } |
| 3622 } | 3676 } |
| 3623 } | 3677 } |
| 3624 } | 3678 } |
| 3625 | 3679 |
| 3626 | 3680 |
| 3627 } // namespace compiler | 3681 } // namespace compiler |
| 3628 } // namespace internal | 3682 } // namespace internal |
| 3629 } // namespace v8 | 3683 } // namespace v8 |
| OLD | NEW |