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 95 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
106 return 8; | 106 return 8; |
107 case MachineRepresentation::kNone: | 107 case MachineRepresentation::kNone: |
108 break; | 108 break; |
109 } | 109 } |
110 UNREACHABLE(); | 110 UNREACHABLE(); |
111 return 0; | 111 return 0; |
112 } | 112 } |
113 | 113 |
114 } // namespace | 114 } // namespace |
115 | 115 |
| 116 class LiveRangeBound { |
| 117 public: |
| 118 explicit LiveRangeBound(LiveRange* range, bool skip) |
| 119 : range_(range), start_(range->Start()), end_(range->End()), skip_(skip) { |
| 120 DCHECK(!range->IsEmpty()); |
| 121 } |
| 122 |
| 123 bool CanCover(LifetimePosition position) { |
| 124 return start_ <= position && position < end_; |
| 125 } |
| 126 |
| 127 LiveRange* const range_; |
| 128 const LifetimePosition start_; |
| 129 const LifetimePosition end_; |
| 130 const bool skip_; |
| 131 |
| 132 private: |
| 133 DISALLOW_COPY_AND_ASSIGN(LiveRangeBound); |
| 134 }; |
| 135 |
| 136 |
| 137 struct FindResult { |
| 138 LiveRange* cur_cover_; |
| 139 LiveRange* pred_cover_; |
| 140 }; |
| 141 |
| 142 |
| 143 class LiveRangeBoundArray { |
| 144 public: |
| 145 LiveRangeBoundArray() : length_(0), start_(nullptr) {} |
| 146 |
| 147 bool ShouldInitialize() { return start_ == nullptr; } |
| 148 |
| 149 void Initialize(Zone* zone, TopLevelLiveRange* range) { |
| 150 length_ = range->GetChildCount(); |
| 151 |
| 152 start_ = zone->NewArray<LiveRangeBound>(length_); |
| 153 LiveRangeBound* curr = start_; |
| 154 // Normally, spilled ranges do not need connecting moves, because the spill |
| 155 // location has been assigned at definition. For ranges spilled in deferred |
| 156 // blocks, that is not the case, so we need to connect the spilled children. |
| 157 for (LiveRange *i = range; i != nullptr; i = i->next(), ++curr) { |
| 158 new (curr) LiveRangeBound(i, i->spilled()); |
| 159 } |
| 160 } |
| 161 |
| 162 LiveRangeBound* Find(const LifetimePosition position) const { |
| 163 size_t left_index = 0; |
| 164 size_t right_index = length_; |
| 165 while (true) { |
| 166 size_t current_index = left_index + (right_index - left_index) / 2; |
| 167 DCHECK(right_index > current_index); |
| 168 LiveRangeBound* bound = &start_[current_index]; |
| 169 if (bound->start_ <= position) { |
| 170 if (position < bound->end_) return bound; |
| 171 DCHECK(left_index < current_index); |
| 172 left_index = current_index; |
| 173 } else { |
| 174 right_index = current_index; |
| 175 } |
| 176 } |
| 177 } |
| 178 |
| 179 LiveRangeBound* FindPred(const InstructionBlock* pred) { |
| 180 LifetimePosition pred_end = |
| 181 LifetimePosition::InstructionFromInstructionIndex( |
| 182 pred->last_instruction_index()); |
| 183 return Find(pred_end); |
| 184 } |
| 185 |
| 186 LiveRangeBound* FindSucc(const InstructionBlock* succ) { |
| 187 LifetimePosition succ_start = LifetimePosition::GapFromInstructionIndex( |
| 188 succ->first_instruction_index()); |
| 189 return Find(succ_start); |
| 190 } |
| 191 |
| 192 bool FindConnectableSubranges(const InstructionBlock* block, |
| 193 const InstructionBlock* pred, |
| 194 FindResult* result) const { |
| 195 LifetimePosition pred_end = |
| 196 LifetimePosition::InstructionFromInstructionIndex( |
| 197 pred->last_instruction_index()); |
| 198 LiveRangeBound* bound = Find(pred_end); |
| 199 result->pred_cover_ = bound->range_; |
| 200 LifetimePosition cur_start = LifetimePosition::GapFromInstructionIndex( |
| 201 block->first_instruction_index()); |
| 202 |
| 203 if (bound->CanCover(cur_start)) { |
| 204 // Both blocks are covered by the same range, so there is nothing to |
| 205 // connect. |
| 206 return false; |
| 207 } |
| 208 bound = Find(cur_start); |
| 209 if (bound->skip_) { |
| 210 return false; |
| 211 } |
| 212 result->cur_cover_ = bound->range_; |
| 213 DCHECK(result->pred_cover_ != nullptr && result->cur_cover_ != nullptr); |
| 214 return (result->cur_cover_ != result->pred_cover_); |
| 215 } |
| 216 |
| 217 private: |
| 218 size_t length_; |
| 219 LiveRangeBound* start_; |
| 220 |
| 221 DISALLOW_COPY_AND_ASSIGN(LiveRangeBoundArray); |
| 222 }; |
| 223 |
| 224 |
| 225 class LiveRangeFinder { |
| 226 public: |
| 227 explicit LiveRangeFinder(const RegisterAllocationData* data, Zone* zone) |
| 228 : data_(data), |
| 229 bounds_length_(static_cast<int>(data_->live_ranges().size())), |
| 230 bounds_(zone->NewArray<LiveRangeBoundArray>(bounds_length_)), |
| 231 zone_(zone) { |
| 232 for (int i = 0; i < bounds_length_; ++i) { |
| 233 new (&bounds_[i]) LiveRangeBoundArray(); |
| 234 } |
| 235 } |
| 236 |
| 237 LiveRangeBoundArray* ArrayFor(int operand_index) { |
| 238 DCHECK(operand_index < bounds_length_); |
| 239 TopLevelLiveRange* range = data_->live_ranges()[operand_index]; |
| 240 DCHECK(range != nullptr && !range->IsEmpty()); |
| 241 LiveRangeBoundArray* array = &bounds_[operand_index]; |
| 242 if (array->ShouldInitialize()) { |
| 243 array->Initialize(zone_, range); |
| 244 } |
| 245 return array; |
| 246 } |
| 247 |
| 248 private: |
| 249 const RegisterAllocationData* const data_; |
| 250 const int bounds_length_; |
| 251 LiveRangeBoundArray* const bounds_; |
| 252 Zone* const zone_; |
| 253 |
| 254 DISALLOW_COPY_AND_ASSIGN(LiveRangeFinder); |
| 255 }; |
| 256 |
| 257 |
| 258 typedef std::pair<ParallelMove*, InstructionOperand> DelayedInsertionMapKey; |
| 259 |
| 260 |
| 261 struct DelayedInsertionMapCompare { |
| 262 bool operator()(const DelayedInsertionMapKey& a, |
| 263 const DelayedInsertionMapKey& b) const { |
| 264 if (a.first == b.first) { |
| 265 return a.second.Compare(b.second); |
| 266 } |
| 267 return a.first < b.first; |
| 268 } |
| 269 }; |
| 270 |
| 271 |
| 272 typedef ZoneMap<DelayedInsertionMapKey, InstructionOperand, |
| 273 DelayedInsertionMapCompare> DelayedInsertionMap; |
| 274 |
116 | 275 |
117 UsePosition::UsePosition(LifetimePosition pos, InstructionOperand* operand, | 276 UsePosition::UsePosition(LifetimePosition pos, InstructionOperand* operand, |
118 void* hint, UsePositionHintType hint_type) | 277 void* hint, UsePositionHintType hint_type) |
119 : operand_(operand), hint_(hint), next_(nullptr), pos_(pos), flags_(0) { | 278 : operand_(operand), hint_(hint), next_(nullptr), pos_(pos), flags_(0) { |
120 DCHECK_IMPLIES(hint == nullptr, hint_type == UsePositionHintType::kNone); | 279 DCHECK_IMPLIES(hint == nullptr, hint_type == UsePositionHintType::kNone); |
121 bool register_beneficial = true; | 280 bool register_beneficial = true; |
122 UsePositionType type = UsePositionType::kAny; | 281 UsePositionType type = UsePositionType::kAny; |
123 if (operand_ != nullptr && operand_->IsUnallocated()) { | 282 if (operand_ != nullptr && operand_->IsUnallocated()) { |
124 const UnallocatedOperand* unalloc = UnallocatedOperand::cast(operand_); | 283 const UnallocatedOperand* unalloc = UnallocatedOperand::cast(operand_); |
125 if (unalloc->HasRegisterPolicy()) { | 284 if (unalloc->HasRegisterPolicy()) { |
(...skipping 601 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
727 #endif | 886 #endif |
728 | 887 |
729 | 888 |
730 void TopLevelLiveRange::RecordSpillLocation(Zone* zone, int gap_index, | 889 void TopLevelLiveRange::RecordSpillLocation(Zone* zone, int gap_index, |
731 InstructionOperand* operand) { | 890 InstructionOperand* operand) { |
732 DCHECK(HasNoSpillType()); | 891 DCHECK(HasNoSpillType()); |
733 spill_move_insertion_locations_ = new (zone) SpillMoveInsertionList( | 892 spill_move_insertion_locations_ = new (zone) SpillMoveInsertionList( |
734 gap_index, operand, spill_move_insertion_locations_); | 893 gap_index, operand, spill_move_insertion_locations_); |
735 } | 894 } |
736 | 895 |
737 | |
738 bool TopLevelLiveRange::TryCommitSpillInDeferredBlock( | |
739 InstructionSequence* code, const InstructionOperand& spill_operand) { | |
740 if (!IsSpilledOnlyInDeferredBlocks()) return false; | |
741 | |
742 TRACE("Live Range %d will be spilled only in deferred blocks.\n", vreg()); | |
743 // If we have ranges that aren't spilled but require the operand on the stack, | |
744 // make sure we insert the spill. | |
745 for (const LiveRange* child = this; child != nullptr; child = child->next()) { | |
746 if (!child->spilled() && | |
747 child->NextSlotPosition(child->Start()) != nullptr) { | |
748 Instruction* instr = | |
749 code->InstructionAt(child->Start().ToInstructionIndex()); | |
750 // Insert spill at the end to let live range connections happen at START. | |
751 ParallelMove* move = | |
752 instr->GetOrCreateParallelMove(Instruction::END, code->zone()); | |
753 InstructionOperand assigned = child->GetAssignedOperand(); | |
754 if (TopLevel()->has_slot_use()) { | |
755 bool found = false; | |
756 for (MoveOperands* move_op : *move) { | |
757 if (move_op->IsEliminated()) continue; | |
758 if (move_op->source().Equals(assigned) && | |
759 move_op->destination().Equals(spill_operand)) { | |
760 found = true; | |
761 break; | |
762 } | |
763 } | |
764 if (found) continue; | |
765 } | |
766 | |
767 move->AddMove(assigned, spill_operand); | |
768 } | |
769 } | |
770 | |
771 return true; | |
772 } | |
773 | |
774 | |
775 void TopLevelLiveRange::CommitSpillMoves(InstructionSequence* sequence, | 896 void TopLevelLiveRange::CommitSpillMoves(InstructionSequence* sequence, |
776 const InstructionOperand& op, | 897 const InstructionOperand& op, |
777 bool might_be_duplicated) { | 898 bool might_be_duplicated) { |
778 DCHECK_IMPLIES(op.IsConstant(), spill_move_insertion_locations() == nullptr); | 899 DCHECK_IMPLIES(op.IsConstant(), GetSpillMoveInsertionLocations() == nullptr); |
779 Zone* zone = sequence->zone(); | 900 Zone* zone = sequence->zone(); |
780 | 901 |
781 for (SpillMoveInsertionList* to_spill = spill_move_insertion_locations(); | 902 for (SpillMoveInsertionList* to_spill = GetSpillMoveInsertionLocations(); |
782 to_spill != nullptr; to_spill = to_spill->next) { | 903 to_spill != nullptr; to_spill = to_spill->next) { |
783 Instruction* instr = sequence->InstructionAt(to_spill->gap_index); | 904 Instruction* instr = sequence->InstructionAt(to_spill->gap_index); |
784 ParallelMove* move = | 905 ParallelMove* move = |
785 instr->GetOrCreateParallelMove(Instruction::START, zone); | 906 instr->GetOrCreateParallelMove(Instruction::START, zone); |
786 // Skip insertion if it's possible that the move exists already as a | 907 // Skip insertion if it's possible that the move exists already as a |
787 // constraint move from a fixed output register to a slot. | 908 // constraint move from a fixed output register to a slot. |
788 if (might_be_duplicated || has_preassigned_slot()) { | 909 if (might_be_duplicated || has_preassigned_slot()) { |
789 bool found = false; | 910 bool found = false; |
790 for (MoveOperands* move_op : *move) { | 911 for (MoveOperands* move_op : *move) { |
791 if (move_op->IsEliminated()) continue; | 912 if (move_op->IsEliminated()) continue; |
(...skipping 2166 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2958 if (!range->HasSpillRange()) continue; | 3079 if (!range->HasSpillRange()) continue; |
2959 if (range->IsSpilledOnlyInDeferredBlocks()) { | 3080 if (range->IsSpilledOnlyInDeferredBlocks()) { |
2960 for (LiveRange* child = range; child != nullptr; child = child->next()) { | 3081 for (LiveRange* child = range; child != nullptr; child = child->next()) { |
2961 if (child->spilled()) { | 3082 if (child->spilled()) { |
2962 code->GetInstructionBlock(child->Start().ToInstructionIndex()) | 3083 code->GetInstructionBlock(child->Start().ToInstructionIndex()) |
2963 ->mark_needs_frame(); | 3084 ->mark_needs_frame(); |
2964 } | 3085 } |
2965 } | 3086 } |
2966 } else { | 3087 } else { |
2967 TopLevelLiveRange::SpillMoveInsertionList* spills = | 3088 TopLevelLiveRange::SpillMoveInsertionList* spills = |
2968 range->spill_move_insertion_locations(); | 3089 range->GetSpillMoveInsertionLocations(); |
2969 DCHECK_NOT_NULL(spills); | 3090 DCHECK_NOT_NULL(spills); |
2970 for (; spills != nullptr; spills = spills->next) { | 3091 for (; spills != nullptr; spills = spills->next) { |
2971 code->GetInstructionBlock(spills->gap_index)->mark_needs_frame(); | 3092 code->GetInstructionBlock(spills->gap_index)->mark_needs_frame(); |
2972 } | 3093 } |
2973 } | 3094 } |
2974 } | 3095 } |
2975 } | 3096 } |
2976 | 3097 |
2977 | 3098 |
2978 OperandAssigner::OperandAssigner(RegisterAllocationData* data) : data_(data) {} | 3099 OperandAssigner::OperandAssigner(RegisterAllocationData* data) : data_(data) {} |
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3025 } | 3146 } |
3026 | 3147 |
3027 if (!spill_operand.IsInvalid()) { | 3148 if (!spill_operand.IsInvalid()) { |
3028 // If this top level range has a child spilled in a deferred block, we use | 3149 // If this top level range has a child spilled in a deferred block, we use |
3029 // the range and control flow connection mechanism instead of spilling at | 3150 // the range and control flow connection mechanism instead of spilling at |
3030 // definition. Refer to the ConnectLiveRanges and ResolveControlFlow | 3151 // definition. Refer to the ConnectLiveRanges and ResolveControlFlow |
3031 // phases. Normally, when we spill at definition, we do not insert a | 3152 // phases. Normally, when we spill at definition, we do not insert a |
3032 // connecting move when a successor child range is spilled - because the | 3153 // connecting move when a successor child range is spilled - because the |
3033 // spilled range picks up its value from the slot which was assigned at | 3154 // spilled range picks up its value from the slot which was assigned at |
3034 // definition. For ranges that are determined to spill only in deferred | 3155 // definition. For ranges that are determined to spill only in deferred |
3035 // blocks, we let ConnectLiveRanges and ResolveControlFlow insert such | 3156 // blocks, we let ConnectLiveRanges and ResolveControlFlow find the blocks |
3036 // moves between ranges. Because of how the ranges are split around | 3157 // where a spill operand is expected, and then finalize by inserting the |
3037 // deferred blocks, this amounts to spilling and filling inside such | 3158 // spills in the deferred blocks dominators. |
3038 // blocks. | 3159 if (!top_range->IsSpilledOnlyInDeferredBlocks()) { |
3039 if (!top_range->TryCommitSpillInDeferredBlock(data()->code(), | |
3040 spill_operand)) { | |
3041 // Spill at definition if the range isn't spilled only in deferred | 3160 // Spill at definition if the range isn't spilled only in deferred |
3042 // blocks. | 3161 // blocks. |
3043 top_range->CommitSpillMoves( | 3162 top_range->CommitSpillMoves( |
3044 data()->code(), spill_operand, | 3163 data()->code(), spill_operand, |
3045 top_range->has_slot_use() || top_range->spilled()); | 3164 top_range->has_slot_use() || top_range->spilled()); |
3046 } | 3165 } |
3047 } | 3166 } |
3048 } | 3167 } |
3049 } | 3168 } |
3050 | 3169 |
(...skipping 130 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3181 DCHECK(!operand.IsStackSlot()); | 3300 DCHECK(!operand.IsStackSlot()); |
3182 DCHECK_EQ(MachineRepresentation::kTagged, | 3301 DCHECK_EQ(MachineRepresentation::kTagged, |
3183 AllocatedOperand::cast(operand).representation()); | 3302 AllocatedOperand::cast(operand).representation()); |
3184 map->RecordReference(AllocatedOperand::cast(operand)); | 3303 map->RecordReference(AllocatedOperand::cast(operand)); |
3185 } | 3304 } |
3186 } | 3305 } |
3187 } | 3306 } |
3188 } | 3307 } |
3189 | 3308 |
3190 | 3309 |
3191 namespace { | |
3192 | |
3193 class LiveRangeBound { | |
3194 public: | |
3195 explicit LiveRangeBound(const LiveRange* range, bool skip) | |
3196 : range_(range), start_(range->Start()), end_(range->End()), skip_(skip) { | |
3197 DCHECK(!range->IsEmpty()); | |
3198 } | |
3199 | |
3200 bool CanCover(LifetimePosition position) { | |
3201 return start_ <= position && position < end_; | |
3202 } | |
3203 | |
3204 const LiveRange* const range_; | |
3205 const LifetimePosition start_; | |
3206 const LifetimePosition end_; | |
3207 const bool skip_; | |
3208 | |
3209 private: | |
3210 DISALLOW_COPY_AND_ASSIGN(LiveRangeBound); | |
3211 }; | |
3212 | |
3213 | |
3214 struct FindResult { | |
3215 const LiveRange* cur_cover_; | |
3216 const LiveRange* pred_cover_; | |
3217 }; | |
3218 | |
3219 | |
3220 class LiveRangeBoundArray { | |
3221 public: | |
3222 LiveRangeBoundArray() : length_(0), start_(nullptr) {} | |
3223 | |
3224 bool ShouldInitialize() { return start_ == nullptr; } | |
3225 | |
3226 void Initialize(Zone* zone, const TopLevelLiveRange* const range) { | |
3227 length_ = range->GetChildCount(); | |
3228 | |
3229 start_ = zone->NewArray<LiveRangeBound>(length_); | |
3230 LiveRangeBound* curr = start_; | |
3231 // Normally, spilled ranges do not need connecting moves, because the spill | |
3232 // location has been assigned at definition. For ranges spilled in deferred | |
3233 // blocks, that is not the case, so we need to connect the spilled children. | |
3234 bool spilled_in_blocks = range->IsSpilledOnlyInDeferredBlocks(); | |
3235 for (const LiveRange *i = range; i != nullptr; i = i->next(), ++curr) { | |
3236 new (curr) LiveRangeBound(i, !spilled_in_blocks && i->spilled()); | |
3237 } | |
3238 } | |
3239 | |
3240 LiveRangeBound* Find(const LifetimePosition position) const { | |
3241 size_t left_index = 0; | |
3242 size_t right_index = length_; | |
3243 while (true) { | |
3244 size_t current_index = left_index + (right_index - left_index) / 2; | |
3245 DCHECK(right_index > current_index); | |
3246 LiveRangeBound* bound = &start_[current_index]; | |
3247 if (bound->start_ <= position) { | |
3248 if (position < bound->end_) return bound; | |
3249 DCHECK(left_index < current_index); | |
3250 left_index = current_index; | |
3251 } else { | |
3252 right_index = current_index; | |
3253 } | |
3254 } | |
3255 } | |
3256 | |
3257 LiveRangeBound* FindPred(const InstructionBlock* pred) { | |
3258 LifetimePosition pred_end = | |
3259 LifetimePosition::InstructionFromInstructionIndex( | |
3260 pred->last_instruction_index()); | |
3261 return Find(pred_end); | |
3262 } | |
3263 | |
3264 LiveRangeBound* FindSucc(const InstructionBlock* succ) { | |
3265 LifetimePosition succ_start = LifetimePosition::GapFromInstructionIndex( | |
3266 succ->first_instruction_index()); | |
3267 return Find(succ_start); | |
3268 } | |
3269 | |
3270 bool FindConnectableSubranges(const InstructionBlock* block, | |
3271 const InstructionBlock* pred, | |
3272 FindResult* result) const { | |
3273 LifetimePosition pred_end = | |
3274 LifetimePosition::InstructionFromInstructionIndex( | |
3275 pred->last_instruction_index()); | |
3276 LiveRangeBound* bound = Find(pred_end); | |
3277 result->pred_cover_ = bound->range_; | |
3278 LifetimePosition cur_start = LifetimePosition::GapFromInstructionIndex( | |
3279 block->first_instruction_index()); | |
3280 | |
3281 if (bound->CanCover(cur_start)) { | |
3282 // Both blocks are covered by the same range, so there is nothing to | |
3283 // connect. | |
3284 return false; | |
3285 } | |
3286 bound = Find(cur_start); | |
3287 if (bound->skip_) { | |
3288 return false; | |
3289 } | |
3290 result->cur_cover_ = bound->range_; | |
3291 DCHECK(result->pred_cover_ != nullptr && result->cur_cover_ != nullptr); | |
3292 return (result->cur_cover_ != result->pred_cover_); | |
3293 } | |
3294 | |
3295 private: | |
3296 size_t length_; | |
3297 LiveRangeBound* start_; | |
3298 | |
3299 DISALLOW_COPY_AND_ASSIGN(LiveRangeBoundArray); | |
3300 }; | |
3301 | |
3302 | |
3303 class LiveRangeFinder { | |
3304 public: | |
3305 explicit LiveRangeFinder(const RegisterAllocationData* data, Zone* zone) | |
3306 : data_(data), | |
3307 bounds_length_(static_cast<int>(data_->live_ranges().size())), | |
3308 bounds_(zone->NewArray<LiveRangeBoundArray>(bounds_length_)), | |
3309 zone_(zone) { | |
3310 for (int i = 0; i < bounds_length_; ++i) { | |
3311 new (&bounds_[i]) LiveRangeBoundArray(); | |
3312 } | |
3313 } | |
3314 | |
3315 LiveRangeBoundArray* ArrayFor(int operand_index) { | |
3316 DCHECK(operand_index < bounds_length_); | |
3317 TopLevelLiveRange* range = data_->live_ranges()[operand_index]; | |
3318 DCHECK(range != nullptr && !range->IsEmpty()); | |
3319 LiveRangeBoundArray* array = &bounds_[operand_index]; | |
3320 if (array->ShouldInitialize()) { | |
3321 array->Initialize(zone_, range); | |
3322 } | |
3323 return array; | |
3324 } | |
3325 | |
3326 private: | |
3327 const RegisterAllocationData* const data_; | |
3328 const int bounds_length_; | |
3329 LiveRangeBoundArray* const bounds_; | |
3330 Zone* const zone_; | |
3331 | |
3332 DISALLOW_COPY_AND_ASSIGN(LiveRangeFinder); | |
3333 }; | |
3334 | |
3335 | |
3336 typedef std::pair<ParallelMove*, InstructionOperand> DelayedInsertionMapKey; | |
3337 | |
3338 | |
3339 struct DelayedInsertionMapCompare { | |
3340 bool operator()(const DelayedInsertionMapKey& a, | |
3341 const DelayedInsertionMapKey& b) const { | |
3342 if (a.first == b.first) { | |
3343 return a.second.Compare(b.second); | |
3344 } | |
3345 return a.first < b.first; | |
3346 } | |
3347 }; | |
3348 | |
3349 | |
3350 typedef ZoneMap<DelayedInsertionMapKey, InstructionOperand, | |
3351 DelayedInsertionMapCompare> DelayedInsertionMap; | |
3352 | |
3353 } // namespace | |
3354 | |
3355 | |
3356 LiveRangeConnector::LiveRangeConnector(RegisterAllocationData* data) | 3310 LiveRangeConnector::LiveRangeConnector(RegisterAllocationData* data) |
3357 : data_(data) {} | 3311 : data_(data) {} |
3358 | 3312 |
3359 | 3313 |
3360 bool LiveRangeConnector::CanEagerlyResolveControlFlow( | 3314 bool LiveRangeConnector::CanEagerlyResolveControlFlow( |
3361 const InstructionBlock* block) const { | 3315 const InstructionBlock* block) const { |
3362 if (block->PredecessorCount() != 1) return false; | 3316 if (block->PredecessorCount() != 1) return false; |
3363 return block->predecessors()[0].IsNext(block->rpo_number()); | 3317 return block->predecessors()[0].IsNext(block->rpo_number()); |
3364 } | 3318 } |
3365 | 3319 |
(...skipping 10 matching lines...) Expand all Loading... |
3376 LiveRangeBoundArray* array = finder.ArrayFor(iterator.Current()); | 3330 LiveRangeBoundArray* array = finder.ArrayFor(iterator.Current()); |
3377 for (const RpoNumber& pred : block->predecessors()) { | 3331 for (const RpoNumber& pred : block->predecessors()) { |
3378 FindResult result; | 3332 FindResult result; |
3379 const InstructionBlock* pred_block = code()->InstructionBlockAt(pred); | 3333 const InstructionBlock* pred_block = code()->InstructionBlockAt(pred); |
3380 if (!array->FindConnectableSubranges(block, pred_block, &result)) { | 3334 if (!array->FindConnectableSubranges(block, pred_block, &result)) { |
3381 continue; | 3335 continue; |
3382 } | 3336 } |
3383 InstructionOperand pred_op = result.pred_cover_->GetAssignedOperand(); | 3337 InstructionOperand pred_op = result.pred_cover_->GetAssignedOperand(); |
3384 InstructionOperand cur_op = result.cur_cover_->GetAssignedOperand(); | 3338 InstructionOperand cur_op = result.cur_cover_->GetAssignedOperand(); |
3385 if (pred_op.Equals(cur_op)) continue; | 3339 if (pred_op.Equals(cur_op)) continue; |
| 3340 if (!pred_op.IsAnyRegister() && cur_op.IsAnyRegister()) { |
| 3341 // We're doing a reload. |
| 3342 const LiveRange* current = result.cur_cover_; |
| 3343 |
| 3344 if (current->TopLevel()->IsSpilledOnlyInDeferredBlocks() && |
| 3345 pred_block->IsDeferred()) { |
| 3346 // The spill location should be defined in pred_block, so add |
| 3347 // pred_block to the list of blocks requiring a spill operand. |
| 3348 current->TopLevel()->GetListOfBlocksRequiringSpillOperands()->Add( |
| 3349 pred_block->rpo_number().ToInt()); |
| 3350 } |
| 3351 } |
3386 int move_loc = ResolveControlFlow(block, cur_op, pred_block, pred_op); | 3352 int move_loc = ResolveControlFlow(block, cur_op, pred_block, pred_op); |
3387 USE(move_loc); | 3353 USE(move_loc); |
3388 DCHECK_IMPLIES( | 3354 DCHECK_IMPLIES( |
3389 result.cur_cover_->TopLevel()->IsSpilledOnlyInDeferredBlocks() && | 3355 result.cur_cover_->TopLevel()->IsSpilledOnlyInDeferredBlocks() && |
3390 !(pred_op.IsAnyRegister() && cur_op.IsAnyRegister()), | 3356 !(pred_op.IsAnyRegister() && cur_op.IsAnyRegister()), |
3391 code()->GetInstructionBlock(move_loc)->IsDeferred()); | 3357 code()->GetInstructionBlock(move_loc)->IsDeferred()); |
3392 } | 3358 } |
3393 iterator.Advance(); | 3359 iterator.Advance(); |
3394 } | 3360 } |
3395 } | 3361 } |
| 3362 |
| 3363 // At this stage, we collected blocks needing a spill operand from |
| 3364 // ConnectRanges and from ResolveControlFlow. Time to commit the spills for |
| 3365 // deferred blocks. |
| 3366 for (TopLevelLiveRange* top : data()->live_ranges()) { |
| 3367 if (top == nullptr || top->IsEmpty() || |
| 3368 !top->IsSpilledOnlyInDeferredBlocks()) |
| 3369 continue; |
| 3370 CommitSpillsInDeferredBlocks(top, finder.ArrayFor(top->vreg()), local_zone); |
| 3371 } |
3396 } | 3372 } |
3397 | 3373 |
3398 | 3374 |
3399 int LiveRangeConnector::ResolveControlFlow(const InstructionBlock* block, | 3375 int LiveRangeConnector::ResolveControlFlow(const InstructionBlock* block, |
3400 const InstructionOperand& cur_op, | 3376 const InstructionOperand& cur_op, |
3401 const InstructionBlock* pred, | 3377 const InstructionBlock* pred, |
3402 const InstructionOperand& pred_op) { | 3378 const InstructionOperand& pred_op) { |
3403 DCHECK(!pred_op.Equals(cur_op)); | 3379 DCHECK(!pred_op.Equals(cur_op)); |
3404 int gap_index; | 3380 int gap_index; |
3405 Instruction::GapPosition position; | 3381 Instruction::GapPosition position; |
(...skipping 17 matching lines...) Expand all Loading... |
3423 DelayedInsertionMap delayed_insertion_map(local_zone); | 3399 DelayedInsertionMap delayed_insertion_map(local_zone); |
3424 for (TopLevelLiveRange* top_range : data()->live_ranges()) { | 3400 for (TopLevelLiveRange* top_range : data()->live_ranges()) { |
3425 if (top_range == nullptr) continue; | 3401 if (top_range == nullptr) continue; |
3426 bool connect_spilled = top_range->IsSpilledOnlyInDeferredBlocks(); | 3402 bool connect_spilled = top_range->IsSpilledOnlyInDeferredBlocks(); |
3427 LiveRange* first_range = top_range; | 3403 LiveRange* first_range = top_range; |
3428 for (LiveRange *second_range = first_range->next(); second_range != nullptr; | 3404 for (LiveRange *second_range = first_range->next(); second_range != nullptr; |
3429 first_range = second_range, second_range = second_range->next()) { | 3405 first_range = second_range, second_range = second_range->next()) { |
3430 LifetimePosition pos = second_range->Start(); | 3406 LifetimePosition pos = second_range->Start(); |
3431 // Add gap move if the two live ranges touch and there is no block | 3407 // Add gap move if the two live ranges touch and there is no block |
3432 // boundary. | 3408 // boundary. |
3433 if (!connect_spilled && second_range->spilled()) continue; | 3409 if (second_range->spilled()) continue; |
3434 if (first_range->End() != pos) continue; | 3410 if (first_range->End() != pos) continue; |
3435 if (data()->IsBlockBoundary(pos) && | 3411 if (data()->IsBlockBoundary(pos) && |
3436 !CanEagerlyResolveControlFlow(GetInstructionBlock(code(), pos))) { | 3412 !CanEagerlyResolveControlFlow(GetInstructionBlock(code(), pos))) { |
3437 continue; | 3413 continue; |
3438 } | 3414 } |
3439 InstructionOperand prev_operand = first_range->GetAssignedOperand(); | 3415 InstructionOperand prev_operand = first_range->GetAssignedOperand(); |
3440 InstructionOperand cur_operand = second_range->GetAssignedOperand(); | 3416 InstructionOperand cur_operand = second_range->GetAssignedOperand(); |
3441 if (prev_operand.Equals(cur_operand)) continue; | 3417 if (prev_operand.Equals(cur_operand)) continue; |
3442 bool delay_insertion = false; | 3418 bool delay_insertion = false; |
3443 Instruction::GapPosition gap_pos; | 3419 Instruction::GapPosition gap_pos; |
3444 int gap_index = pos.ToInstructionIndex(); | 3420 int gap_index = pos.ToInstructionIndex(); |
| 3421 if (connect_spilled && !prev_operand.IsAnyRegister() && |
| 3422 cur_operand.IsAnyRegister()) { |
| 3423 const InstructionBlock* block = code()->GetInstructionBlock(gap_index); |
| 3424 DCHECK(block->IsDeferred()); |
| 3425 // Performing a reload in this block, meaning the spill operand must |
| 3426 // be defined here. |
| 3427 top_range->GetListOfBlocksRequiringSpillOperands()->Add( |
| 3428 block->rpo_number().ToInt()); |
| 3429 } |
| 3430 |
3445 if (pos.IsGapPosition()) { | 3431 if (pos.IsGapPosition()) { |
3446 gap_pos = pos.IsStart() ? Instruction::START : Instruction::END; | 3432 gap_pos = pos.IsStart() ? Instruction::START : Instruction::END; |
3447 } else { | 3433 } else { |
3448 if (pos.IsStart()) { | 3434 if (pos.IsStart()) { |
3449 delay_insertion = true; | 3435 delay_insertion = true; |
3450 } else { | 3436 } else { |
3451 gap_index++; | 3437 gap_index++; |
3452 } | 3438 } |
3453 gap_pos = delay_insertion ? Instruction::END : Instruction::START; | 3439 gap_pos = delay_insertion ? Instruction::END : Instruction::START; |
3454 } | 3440 } |
3455 // Fills or spills for spilled in deferred blocks ranges must happen | 3441 // Reloads or spills for spilled in deferred blocks ranges must happen |
3456 // only in deferred blocks. | 3442 // only in deferred blocks. |
3457 DCHECK_IMPLIES( | 3443 DCHECK_IMPLIES( |
3458 connect_spilled && | 3444 connect_spilled && |
3459 !(prev_operand.IsAnyRegister() && cur_operand.IsAnyRegister()), | 3445 !(prev_operand.IsAnyRegister() && cur_operand.IsAnyRegister()), |
3460 code()->GetInstructionBlock(gap_index)->IsDeferred()); | 3446 code()->GetInstructionBlock(gap_index)->IsDeferred()); |
3461 | 3447 |
3462 ParallelMove* move = | 3448 ParallelMove* move = |
3463 code()->InstructionAt(gap_index)->GetOrCreateParallelMove( | 3449 code()->InstructionAt(gap_index)->GetOrCreateParallelMove( |
3464 gap_pos, code_zone()); | 3450 gap_pos, code_zone()); |
3465 if (!delay_insertion) { | 3451 if (!delay_insertion) { |
(...skipping 30 matching lines...) Expand all Loading... |
3496 // Gather all MoveOperands for a single ParallelMove. | 3482 // Gather all MoveOperands for a single ParallelMove. |
3497 MoveOperands* move = | 3483 MoveOperands* move = |
3498 new (code_zone()) MoveOperands(it->first.second, it->second); | 3484 new (code_zone()) MoveOperands(it->first.second, it->second); |
3499 MoveOperands* eliminate = moves->PrepareInsertAfter(move); | 3485 MoveOperands* eliminate = moves->PrepareInsertAfter(move); |
3500 to_insert.push_back(move); | 3486 to_insert.push_back(move); |
3501 if (eliminate != nullptr) to_eliminate.push_back(eliminate); | 3487 if (eliminate != nullptr) to_eliminate.push_back(eliminate); |
3502 } | 3488 } |
3503 } | 3489 } |
3504 | 3490 |
3505 | 3491 |
| 3492 void LiveRangeConnector::CommitSpillsInDeferredBlocks( |
| 3493 TopLevelLiveRange* range, LiveRangeBoundArray* array, Zone* temp_zone) { |
| 3494 DCHECK(range->IsSpilledOnlyInDeferredBlocks()); |
| 3495 DCHECK(!range->spilled()); |
| 3496 |
| 3497 InstructionSequence* code = data()->code(); |
| 3498 InstructionOperand spill_operand = range->GetSpillRangeOperand(); |
| 3499 |
| 3500 TRACE("Live Range %d will be spilled only in deferred blocks.\n", |
| 3501 range->vreg()); |
| 3502 // If we have ranges that aren't spilled but require the operand on the stack, |
| 3503 // make sure we insert the spill. |
| 3504 for (const LiveRange* child = range; child != nullptr; |
| 3505 child = child->next()) { |
| 3506 for (const UsePosition* pos = child->first_pos(); pos != nullptr; |
| 3507 pos = pos->next()) { |
| 3508 if (pos->type() != UsePositionType::kRequiresSlot && !child->spilled()) |
| 3509 continue; |
| 3510 range->AddBlockRequiringSpillOperand( |
| 3511 code->GetInstructionBlock(pos->pos().ToInstructionIndex()) |
| 3512 ->rpo_number()); |
| 3513 } |
| 3514 } |
| 3515 |
| 3516 ZoneQueue<int> worklist(temp_zone); |
| 3517 |
| 3518 for (BitVector::Iterator iterator( |
| 3519 range->GetListOfBlocksRequiringSpillOperands()); |
| 3520 !iterator.Done(); iterator.Advance()) { |
| 3521 worklist.push(iterator.Current()); |
| 3522 } |
| 3523 |
| 3524 // Seek the deferred blocks that dominate locations requiring spill operands, |
| 3525 // and spill there. We only need to spill at the start of such blocks. |
| 3526 BitVector done_blocks( |
| 3527 range->GetListOfBlocksRequiringSpillOperands()->length(), temp_zone); |
| 3528 while (!worklist.empty()) { |
| 3529 int block_id = worklist.front(); |
| 3530 worklist.pop(); |
| 3531 if (done_blocks.Contains(block_id)) continue; |
| 3532 done_blocks.Add(block_id); |
| 3533 const InstructionBlock* spill_block = |
| 3534 code->InstructionBlockAt(RpoNumber::FromInt(block_id)); |
| 3535 |
| 3536 for (const RpoNumber& pred : spill_block->predecessors()) { |
| 3537 const InstructionBlock* pred_block = code->InstructionBlockAt(pred); |
| 3538 |
| 3539 if (pred_block->IsDeferred()) { |
| 3540 worklist.push(pred_block->rpo_number().ToInt()); |
| 3541 } else { |
| 3542 LifetimePosition pred_end = |
| 3543 LifetimePosition::InstructionFromInstructionIndex( |
| 3544 pred_block->last_instruction_index()); |
| 3545 |
| 3546 LiveRangeBound* bound = array->Find(pred_end); |
| 3547 |
| 3548 InstructionOperand pred_op = bound->range_->GetAssignedOperand(); |
| 3549 |
| 3550 data()->AddGapMove(spill_block->first_instruction_index(), |
| 3551 Instruction::GapPosition::START, pred_op, |
| 3552 spill_operand); |
| 3553 } |
| 3554 } |
| 3555 } |
| 3556 } |
| 3557 |
| 3558 |
3506 } // namespace compiler | 3559 } // namespace compiler |
3507 } // namespace internal | 3560 } // namespace internal |
3508 } // namespace v8 | 3561 } // namespace v8 |
OLD | NEW |