Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(3)

Side by Side Diff: src/compiler/register-allocator.cc

Issue 2347563004: [turbofan] Avoid large deopt blocks (Closed)
Patch Set: A few more adjustments Created 4 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « src/compiler/register-allocator.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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
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
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
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
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
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
OLDNEW
« no previous file with comments | « src/compiler/register-allocator.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698