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

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

Issue 1312473018: [turbofan] Greedy: live range grouping. (Closed) Base URL: https://chromium.googlesource.com/v8/v8.git@master
Patch Set: Created 5 years, 3 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/greedy-allocator.h ('k') | src/compiler/register-allocator.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2015 the V8 project authors. All rights reserved. 1 // Copyright 2015 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/compiler/greedy-allocator.h" 5 #include "src/compiler/greedy-allocator.h"
6 #include "src/compiler/register-allocator.h" 6 #include "src/compiler/register-allocator.h"
7 7
8 namespace v8 { 8 namespace v8 {
9 namespace internal { 9 namespace internal {
10 namespace compiler { 10 namespace compiler {
(...skipping 109 matching lines...) Expand 10 before | Expand all | Expand 10 after
120 return ret; 120 return ret;
121 } 121 }
122 122
123 123
124 void AllocationScheduler::Schedule(LiveRange* range) { 124 void AllocationScheduler::Schedule(LiveRange* range) {
125 TRACE("Scheduling live range %d:%d.\n", range->TopLevel()->vreg(), 125 TRACE("Scheduling live range %d:%d.\n", range->TopLevel()->vreg(),
126 range->relative_id()); 126 range->relative_id());
127 queue_.push(AllocationCandidate(range)); 127 queue_.push(AllocationCandidate(range));
128 } 128 }
129 129
130
131 void AllocationScheduler::Schedule(LiveRangeGroup* group) {
132 queue_.push(AllocationCandidate(group));
133 }
134
130 GreedyAllocator::GreedyAllocator(RegisterAllocationData* data, 135 GreedyAllocator::GreedyAllocator(RegisterAllocationData* data,
131 RegisterKind kind, Zone* local_zone) 136 RegisterKind kind, Zone* local_zone)
132 : RegisterAllocator(data, kind), 137 : RegisterAllocator(data, kind),
133 local_zone_(local_zone), 138 local_zone_(local_zone),
134 allocations_(local_zone), 139 allocations_(local_zone),
135 scheduler_(local_zone) {} 140 scheduler_(local_zone),
141 groups_(local_zone) {}
136 142
137 143
138 void GreedyAllocator::AssignRangeToRegister(int reg_id, LiveRange* range) { 144 void GreedyAllocator::AssignRangeToRegister(int reg_id, LiveRange* range) {
139 TRACE("Assigning register %s to live range %d:%d\n", RegisterName(reg_id), 145 TRACE("Assigning register %s to live range %d:%d\n", RegisterName(reg_id),
140 range->TopLevel()->vreg(), range->relative_id()); 146 range->TopLevel()->vreg(), range->relative_id());
141 147
142 DCHECK(!range->HasRegisterAssigned()); 148 DCHECK(!range->HasRegisterAssigned());
143 149
144 AllocateRegisterToRange(reg_id, range); 150 AllocateRegisterToRange(reg_id, range);
145 151
(...skipping 16 matching lines...) Expand all
162 DCHECK(fixed_range->TopLevel()->IsFixed()); 168 DCHECK(fixed_range->TopLevel()->IsFixed());
163 169
164 int reg_nr = fixed_range->assigned_register(); 170 int reg_nr = fixed_range->assigned_register();
165 EnsureValidRangeWeight(fixed_range); 171 EnsureValidRangeWeight(fixed_range);
166 AllocateRegisterToRange(reg_nr, fixed_range); 172 AllocateRegisterToRange(reg_nr, fixed_range);
167 } 173 }
168 } 174 }
169 } 175 }
170 176
171 177
178 void GreedyAllocator::GroupLiveRanges() {
179 CoalescedLiveRanges groupper(local_zone());
180 for (TopLevelLiveRange* range : data()->live_ranges()) {
181 // Skip splinters, because we do not want to optimize for them, and moves
182 // due to assigning them to different registers occur in deferred blocks.
183 if (!CanProcessRange(range) || range->IsSplinter() || !range->is_phi()) {
184 continue;
185 }
186
187 // A phi can't be a memory operand, so it couldn't have been split.
188 DCHECK(!range->spilled());
189
190 // Maybe this phi range is itself an input to another phi which was already
191 // processed.
192 LiveRangeGroup* latest_grp = range->group() != nullptr
193 ? range->group()
194 : new (local_zone())
195 LiveRangeGroup(local_zone());
196
197 // Populate the groupper.
198 if (range->group() == nullptr) {
199 groupper.clear();
200 groupper.AllocateRange(range);
201 } else {
202 for (LiveRange* member : range->group()->ranges()) {
203 groupper.AllocateRange(member);
204 }
205 }
206 for (int j : data()->GetPhiMapValueFor(range)->phi()->operands()) {
207 // skip output also in input, which may happen for loops.
208 if (j == range->vreg()) continue;
209
210 TopLevelLiveRange* other_top = data()->live_ranges()[j];
211
212 if (other_top->IsSplinter()) continue;
213 // If the other was a memory operand, it might have been split.
214 // So get the unsplit part.
215 LiveRange* other =
216 other_top->next() == nullptr ? other_top : other_top->next();
217
218 if (other->spilled()) continue;
219
220 LiveRangeGroup* other_group = other->group();
221 if (other_group != nullptr) {
222 bool can_merge = true;
223 for (LiveRange* member : other_group->ranges()) {
224 if (groupper.GetConflicts(member).Current() != nullptr) {
225 can_merge = false;
226 break;
227 }
228 }
229 // If each member doesn't conflict with the current group, then since
230 // the members don't conflict with eachother either, we can merge them.
231 if (can_merge) {
232 latest_grp->ranges().insert(latest_grp->ranges().end(),
233 other_group->ranges().begin(),
234 other_group->ranges().end());
235 for (LiveRange* member : other_group->ranges()) {
236 groupper.AllocateRange(member);
237 member->set_group(latest_grp);
238 }
239 // Clear the other range, so we avoid scheduling it.
240 other_group->ranges().clear();
241 }
242 } else if (groupper.GetConflicts(other).Current() == nullptr) {
243 groupper.AllocateRange(other);
244 latest_grp->ranges().push_back(other);
245 other->set_group(latest_grp);
246 }
247 }
248
249 if (latest_grp->ranges().size() > 0 && range->group() == nullptr) {
250 latest_grp->ranges().push_back(range);
251 DCHECK(latest_grp->ranges().size() > 1);
252 groups().push_back(latest_grp);
253 range->set_group(latest_grp);
254 }
255 }
256 }
257
258
172 void GreedyAllocator::ScheduleAllocationCandidates() { 259 void GreedyAllocator::ScheduleAllocationCandidates() {
173 for (auto range : data()->live_ranges()) { 260 for (LiveRangeGroup* group : groups()) {
261 if (group->ranges().size() > 0) {
262 // We shouldn't have added single-range groups.
263 DCHECK(group->ranges().size() != 1);
264 scheduler().Schedule(group);
265 }
266 }
267 for (LiveRange* range : data()->live_ranges()) {
174 if (CanProcessRange(range)) { 268 if (CanProcessRange(range)) {
175 for (LiveRange* child = range; child != nullptr; child = child->next()) { 269 for (LiveRange* child = range; child != nullptr; child = child->next()) {
176 if (!child->spilled()) { 270 if (!child->spilled() && child->group() == nullptr) {
177 scheduler().Schedule(child); 271 scheduler().Schedule(child);
178 } 272 }
179 } 273 }
180 } 274 }
181 } 275 }
182 } 276 }
183 277
184 278
185 void GreedyAllocator::TryAllocateCandidate( 279 void GreedyAllocator::TryAllocateCandidate(
186 const AllocationCandidate& candidate) { 280 const AllocationCandidate& candidate) {
187 // At this point, this is just a live range. TODO: groups. 281 if (candidate.is_group()) {
188 TryAllocateLiveRange(candidate.live_range()); 282 TryAllocateGroup(candidate.group());
283 } else {
284 TryAllocateLiveRange(candidate.live_range());
285 }
189 } 286 }
190 287
191 288
289 void GreedyAllocator::TryAllocateGroup(LiveRangeGroup* group) {
290 float group_weight = 0.0;
291 for (LiveRange* member : group->ranges()) {
292 EnsureValidRangeWeight(member);
293 group_weight = Max(group_weight, member->weight());
294 }
295
296 float eviction_weight = group_weight;
297 int eviction_reg = -1;
298 int free_reg = -1;
299 for (int reg = 0; reg < num_registers(); ++reg) {
300 float weight = GetMaximumConflictingWeight(reg, group, group_weight);
301 if (weight == LiveRange::kInvalidWeight) {
302 free_reg = reg;
303 break;
304 }
305 if (weight < eviction_weight) {
306 eviction_weight = weight;
307 eviction_reg = reg;
308 }
309 }
310 if (eviction_reg < 0 && free_reg < 0) {
311 for (LiveRange* member : group->ranges()) {
312 scheduler().Schedule(member);
313 }
314 return;
315 }
316 if (free_reg < 0) {
317 DCHECK(eviction_reg >= 0);
318 for (LiveRange* member : group->ranges()) {
319 EvictAndRescheduleConflicts(eviction_reg, member);
320 }
321 free_reg = eviction_reg;
322 }
323
324 DCHECK(free_reg >= 0);
325 for (LiveRange* member : group->ranges()) {
326 AssignRangeToRegister(free_reg, member);
327 }
328 }
329
330
192 void GreedyAllocator::TryAllocateLiveRange(LiveRange* range) { 331 void GreedyAllocator::TryAllocateLiveRange(LiveRange* range) {
193 // TODO(mtrofin): once we introduce groups, we'll want to first try and 332 // TODO(mtrofin): once we introduce groups, we'll want to first try and
194 // allocate at the preferred register. 333 // allocate at the preferred register.
195 TRACE("Attempting to allocate live range %d:%d.\n", range->TopLevel()->vreg(), 334 TRACE("Attempting to allocate live range %d:%d.\n", range->TopLevel()->vreg(),
196 range->relative_id()); 335 range->relative_id());
197 int free_reg = -1; 336 int free_reg = -1;
198 int evictable_reg = -1; 337 int evictable_reg = -1;
199 int hinted_reg = -1; 338 int hinted_reg = -1;
200 339
201 EnsureValidRangeWeight(range); 340 EnsureValidRangeWeight(range);
(...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after
273 scheduler().Schedule(conflict); 412 scheduler().Schedule(conflict);
274 TRACE("Evicted range %d%d.\n", conflict->TopLevel()->vreg(), 413 TRACE("Evicted range %d%d.\n", conflict->TopLevel()->vreg(),
275 conflict->relative_id()); 414 conflict->relative_id());
276 } 415 }
277 } 416 }
278 417
279 418
280 void GreedyAllocator::SplitAndSpillRangesDefinedByMemoryOperand() { 419 void GreedyAllocator::SplitAndSpillRangesDefinedByMemoryOperand() {
281 size_t initial_range_count = data()->live_ranges().size(); 420 size_t initial_range_count = data()->live_ranges().size();
282 for (size_t i = 0; i < initial_range_count; ++i) { 421 for (size_t i = 0; i < initial_range_count; ++i) {
283 auto range = data()->live_ranges()[i]; 422 TopLevelLiveRange* range = data()->live_ranges()[i];
284 if (!CanProcessRange(range)) continue; 423 if (!CanProcessRange(range)) continue;
285 if (!range->HasSpillOperand()) continue; 424 if (!range->HasSpillOperand()) continue;
286 425
287 LifetimePosition start = range->Start(); 426 LifetimePosition start = range->Start();
288 TRACE("Live range %d:%d is defined by a spill operand.\n", 427 TRACE("Live range %d:%d is defined by a spill operand.\n",
289 range->TopLevel()->vreg(), range->relative_id()); 428 range->TopLevel()->vreg(), range->relative_id());
290 auto next_pos = start; 429 auto next_pos = start;
291 if (next_pos.IsGapPosition()) { 430 if (next_pos.IsGapPosition()) {
292 next_pos = next_pos.NextStart(); 431 next_pos = next_pos.NextStart();
293 } 432 }
(...skipping 21 matching lines...) Expand all
315 454
316 455
317 void GreedyAllocator::AllocateRegisters() { 456 void GreedyAllocator::AllocateRegisters() {
318 CHECK(scheduler().empty()); 457 CHECK(scheduler().empty());
319 CHECK(allocations_.empty()); 458 CHECK(allocations_.empty());
320 459
321 TRACE("Begin allocating function %s with the Greedy Allocator\n", 460 TRACE("Begin allocating function %s with the Greedy Allocator\n",
322 data()->debug_name()); 461 data()->debug_name());
323 462
324 SplitAndSpillRangesDefinedByMemoryOperand(); 463 SplitAndSpillRangesDefinedByMemoryOperand();
464 GroupLiveRanges();
465 ScheduleAllocationCandidates();
325 PreallocateFixedRanges(); 466 PreallocateFixedRanges();
326 ScheduleAllocationCandidates();
327
328 while (!scheduler().empty()) { 467 while (!scheduler().empty()) {
329 AllocationCandidate candidate = scheduler().GetNext(); 468 AllocationCandidate candidate = scheduler().GetNext();
330 TryAllocateCandidate(candidate); 469 TryAllocateCandidate(candidate);
331 } 470 }
332 471
333 for (size_t i = 0; i < allocations_.size(); ++i) { 472 for (size_t i = 0; i < allocations_.size(); ++i) {
334 if (!allocations_[i]->empty()) { 473 if (!allocations_[i]->empty()) {
335 data()->MarkAllocated(mode(), static_cast<int>(i)); 474 data()->MarkAllocated(mode(), static_cast<int>(i));
336 } 475 }
337 } 476 }
(...skipping 13 matching lines...) Expand all
351 conflict = conflicts.GetNext()) { 490 conflict = conflicts.GetNext()) {
352 DCHECK_NE(conflict->weight(), LiveRange::kInvalidWeight); 491 DCHECK_NE(conflict->weight(), LiveRange::kInvalidWeight);
353 ret = Max(ret, conflict->weight()); 492 ret = Max(ret, conflict->weight());
354 if (ret == LiveRange::kMaxWeight) return ret; 493 if (ret == LiveRange::kMaxWeight) return ret;
355 } 494 }
356 495
357 return ret; 496 return ret;
358 } 497 }
359 498
360 499
500 float GreedyAllocator::GetMaximumConflictingWeight(unsigned reg_id,
501 const LiveRangeGroup* group,
502 float group_weight) const {
503 float ret = LiveRange::kInvalidWeight;
504
505 for (LiveRange* member : group->ranges()) {
506 float member_conflict_weight = GetMaximumConflictingWeight(reg_id, member);
507 if (member_conflict_weight == LiveRange::kMaxWeight) {
508 return LiveRange::kMaxWeight;
509 }
510 if (member_conflict_weight > group_weight) return LiveRange::kMaxWeight;
511 ret = Max(member_conflict_weight, ret);
512 }
513
514 return ret;
515 }
516
517
361 void GreedyAllocator::EnsureValidRangeWeight(LiveRange* range) { 518 void GreedyAllocator::EnsureValidRangeWeight(LiveRange* range) {
362 // The live range weight will be invalidated when ranges are created or split. 519 // The live range weight will be invalidated when ranges are created or split.
363 // Otherwise, it is consistently updated when the range is allocated or 520 // Otherwise, it is consistently updated when the range is allocated or
364 // unallocated. 521 // unallocated.
365 if (range->weight() != LiveRange::kInvalidWeight) return; 522 if (range->weight() != LiveRange::kInvalidWeight) return;
366 523
367 if (range->TopLevel()->IsFixed()) { 524 if (range->TopLevel()->IsFixed()) {
368 range->set_weight(LiveRange::kMaxWeight); 525 range->set_weight(LiveRange::kMaxWeight);
369 return; 526 return;
370 } 527 }
(...skipping 131 matching lines...) Expand 10 before | Expand all | Expand 10 after
502 scheduler().Schedule(range); 659 scheduler().Schedule(range);
503 return; 660 return;
504 } 661 }
505 SpillRangeAsLastResort(range); 662 SpillRangeAsLastResort(range);
506 } 663 }
507 664
508 665
509 } // namespace compiler 666 } // namespace compiler
510 } // namespace internal 667 } // namespace internal
511 } // namespace v8 668 } // namespace v8
OLDNEW
« no previous file with comments | « src/compiler/greedy-allocator.h ('k') | src/compiler/register-allocator.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698