OLD | NEW |
| (Empty) |
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 | |
3 // found in the LICENSE file. | |
4 | |
5 #include "src/compiler/greedy-allocator.h" | |
6 #include "src/compiler/register-allocator.h" | |
7 | |
8 namespace v8 { | |
9 namespace internal { | |
10 namespace compiler { | |
11 | |
12 | |
13 #define TRACE(...) \ | |
14 do { \ | |
15 if (FLAG_trace_alloc) PrintF(__VA_ARGS__); \ | |
16 } while (false) | |
17 | |
18 | |
19 const float GreedyAllocator::kAllocatedRangeMultiplier = 10.0; | |
20 | |
21 | |
22 namespace { | |
23 | |
24 void UpdateOperands(LiveRange* range, RegisterAllocationData* data) { | |
25 int reg_id = range->assigned_register(); | |
26 range->SetUseHints(reg_id); | |
27 if (range->IsTopLevel() && range->TopLevel()->is_phi()) { | |
28 data->GetPhiMapValueFor(range->TopLevel())->set_assigned_register(reg_id); | |
29 } | |
30 } | |
31 | |
32 | |
33 void UnsetOperands(LiveRange* range, RegisterAllocationData* data) { | |
34 range->UnsetUseHints(); | |
35 if (range->IsTopLevel() && range->TopLevel()->is_phi()) { | |
36 data->GetPhiMapValueFor(range->TopLevel())->UnsetAssignedRegister(); | |
37 } | |
38 } | |
39 | |
40 | |
41 LiveRange* Split(LiveRange* range, RegisterAllocationData* data, | |
42 LifetimePosition pos) { | |
43 DCHECK(range->Start() < pos && pos < range->End()); | |
44 DCHECK(pos.IsStart() || pos.IsGapPosition() || | |
45 (data->code() | |
46 ->GetInstructionBlock(pos.ToInstructionIndex()) | |
47 ->last_instruction_index() != pos.ToInstructionIndex())); | |
48 LiveRange* result = range->SplitAt(pos, data->allocation_zone()); | |
49 return result; | |
50 } | |
51 | |
52 | |
53 } // namespace | |
54 | |
55 | |
56 AllocationCandidate AllocationScheduler::GetNext() { | |
57 DCHECK(!queue_.empty()); | |
58 AllocationCandidate ret = queue_.top(); | |
59 queue_.pop(); | |
60 return ret; | |
61 } | |
62 | |
63 | |
64 void AllocationScheduler::Schedule(LiveRange* range) { | |
65 TRACE("Scheduling live range %d:%d.\n", range->TopLevel()->vreg(), | |
66 range->relative_id()); | |
67 queue_.push(AllocationCandidate(range)); | |
68 } | |
69 | |
70 | |
71 void AllocationScheduler::Schedule(LiveRangeGroup* group) { | |
72 queue_.push(AllocationCandidate(group)); | |
73 } | |
74 | |
75 GreedyAllocator::GreedyAllocator(RegisterAllocationData* data, | |
76 RegisterKind kind, Zone* local_zone) | |
77 : RegisterAllocator(data, kind), | |
78 local_zone_(local_zone), | |
79 allocations_(local_zone), | |
80 scheduler_(local_zone), | |
81 groups_(local_zone) {} | |
82 | |
83 | |
84 void GreedyAllocator::AssignRangeToRegister(int reg_id, LiveRange* range) { | |
85 TRACE("Assigning register %s to live range %d:%d\n", RegisterName(reg_id), | |
86 range->TopLevel()->vreg(), range->relative_id()); | |
87 | |
88 DCHECK(!range->HasRegisterAssigned()); | |
89 | |
90 AllocateRegisterToRange(reg_id, range); | |
91 | |
92 TRACE("Assigning %s to range %d%d.\n", RegisterName(reg_id), | |
93 range->TopLevel()->vreg(), range->relative_id()); | |
94 range->set_assigned_register(reg_id); | |
95 UpdateOperands(range, data()); | |
96 } | |
97 | |
98 | |
99 void GreedyAllocator::PreallocateFixedRanges() { | |
100 allocations_.resize(num_registers()); | |
101 for (int i = 0; i < num_registers(); i++) { | |
102 allocations_[i] = new (local_zone()) CoalescedLiveRanges(local_zone()); | |
103 } | |
104 | |
105 for (LiveRange* fixed_range : GetFixedRegisters()) { | |
106 if (fixed_range != nullptr) { | |
107 DCHECK_EQ(mode(), fixed_range->kind()); | |
108 DCHECK(fixed_range->TopLevel()->IsFixed()); | |
109 | |
110 int reg_nr = fixed_range->assigned_register(); | |
111 EnsureValidRangeWeight(fixed_range); | |
112 AllocateRegisterToRange(reg_nr, fixed_range); | |
113 } | |
114 } | |
115 } | |
116 | |
117 | |
118 void GreedyAllocator::GroupLiveRanges() { | |
119 CoalescedLiveRanges grouper(local_zone()); | |
120 for (TopLevelLiveRange* range : data()->live_ranges()) { | |
121 grouper.clear(); | |
122 // Skip splinters, because we do not want to optimize for them, and moves | |
123 // due to assigning them to different registers occur in deferred blocks. | |
124 if (!CanProcessRange(range) || range->IsSplinter() || !range->is_phi()) { | |
125 continue; | |
126 } | |
127 | |
128 // A phi can't be a memory operand, so it couldn't have been split. | |
129 DCHECK(!range->spilled()); | |
130 | |
131 // Maybe this phi range is itself an input to another phi which was already | |
132 // processed. | |
133 LiveRangeGroup* latest_grp = range->group() != nullptr | |
134 ? range->group() | |
135 : new (local_zone()) | |
136 LiveRangeGroup(local_zone()); | |
137 | |
138 // Populate the grouper. | |
139 if (range->group() == nullptr) { | |
140 grouper.AllocateRange(range); | |
141 } else { | |
142 for (LiveRange* member : range->group()->ranges()) { | |
143 grouper.AllocateRange(member); | |
144 } | |
145 } | |
146 for (int j : data()->GetPhiMapValueFor(range)->phi()->operands()) { | |
147 // skip output also in input, which may happen for loops. | |
148 if (j == range->vreg()) continue; | |
149 | |
150 TopLevelLiveRange* other_top = data()->live_ranges()[j]; | |
151 | |
152 if (other_top->IsSplinter()) continue; | |
153 // If the other was a memory operand, it might have been split. | |
154 // So get the unsplit part. | |
155 LiveRange* other = | |
156 other_top->next() == nullptr ? other_top : other_top->next(); | |
157 | |
158 if (other->spilled()) continue; | |
159 | |
160 LiveRangeGroup* other_group = other->group(); | |
161 if (other_group != nullptr) { | |
162 bool can_merge = true; | |
163 for (LiveRange* member : other_group->ranges()) { | |
164 if (grouper.GetConflicts(member).Current() != nullptr) { | |
165 can_merge = false; | |
166 break; | |
167 } | |
168 } | |
169 // If each member doesn't conflict with the current group, then since | |
170 // the members don't conflict with eachother either, we can merge them. | |
171 if (can_merge) { | |
172 latest_grp->ranges().insert(latest_grp->ranges().end(), | |
173 other_group->ranges().begin(), | |
174 other_group->ranges().end()); | |
175 for (LiveRange* member : other_group->ranges()) { | |
176 grouper.AllocateRange(member); | |
177 member->set_group(latest_grp); | |
178 } | |
179 // Clear the other range, so we avoid scheduling it. | |
180 other_group->ranges().clear(); | |
181 } | |
182 } else if (grouper.GetConflicts(other).Current() == nullptr) { | |
183 grouper.AllocateRange(other); | |
184 latest_grp->ranges().push_back(other); | |
185 other->set_group(latest_grp); | |
186 } | |
187 } | |
188 | |
189 if (latest_grp->ranges().size() > 0 && range->group() == nullptr) { | |
190 latest_grp->ranges().push_back(range); | |
191 DCHECK(latest_grp->ranges().size() > 1); | |
192 groups().push_back(latest_grp); | |
193 range->set_group(latest_grp); | |
194 } | |
195 } | |
196 } | |
197 | |
198 | |
199 void GreedyAllocator::ScheduleAllocationCandidates() { | |
200 for (LiveRangeGroup* group : groups()) { | |
201 if (group->ranges().size() > 0) { | |
202 // We shouldn't have added single-range groups. | |
203 DCHECK(group->ranges().size() != 1); | |
204 scheduler().Schedule(group); | |
205 } | |
206 } | |
207 for (LiveRange* range : data()->live_ranges()) { | |
208 if (CanProcessRange(range)) { | |
209 for (LiveRange* child = range; child != nullptr; child = child->next()) { | |
210 if (!child->spilled() && child->group() == nullptr) { | |
211 scheduler().Schedule(child); | |
212 } | |
213 } | |
214 } | |
215 } | |
216 } | |
217 | |
218 | |
219 void GreedyAllocator::TryAllocateCandidate( | |
220 const AllocationCandidate& candidate) { | |
221 if (candidate.is_group()) { | |
222 TryAllocateGroup(candidate.group()); | |
223 } else { | |
224 TryAllocateLiveRange(candidate.live_range()); | |
225 } | |
226 } | |
227 | |
228 | |
229 void GreedyAllocator::TryAllocateGroup(LiveRangeGroup* group) { | |
230 float group_weight = 0.0; | |
231 for (LiveRange* member : group->ranges()) { | |
232 EnsureValidRangeWeight(member); | |
233 group_weight = Max(group_weight, member->weight()); | |
234 } | |
235 | |
236 float eviction_weight = group_weight; | |
237 int eviction_reg = -1; | |
238 int free_reg = -1; | |
239 for (int i = 0; i < num_allocatable_registers(); ++i) { | |
240 int reg = allocatable_register_code(i); | |
241 float weight = GetMaximumConflictingWeight(reg, group, group_weight); | |
242 if (weight == LiveRange::kInvalidWeight) { | |
243 free_reg = reg; | |
244 break; | |
245 } | |
246 if (weight < eviction_weight) { | |
247 eviction_weight = weight; | |
248 eviction_reg = reg; | |
249 } | |
250 } | |
251 if (eviction_reg < 0 && free_reg < 0) { | |
252 for (LiveRange* member : group->ranges()) { | |
253 scheduler().Schedule(member); | |
254 } | |
255 return; | |
256 } | |
257 if (free_reg < 0) { | |
258 DCHECK(eviction_reg >= 0); | |
259 for (LiveRange* member : group->ranges()) { | |
260 EvictAndRescheduleConflicts(eviction_reg, member); | |
261 } | |
262 free_reg = eviction_reg; | |
263 } | |
264 | |
265 DCHECK(free_reg >= 0); | |
266 for (LiveRange* member : group->ranges()) { | |
267 AssignRangeToRegister(free_reg, member); | |
268 } | |
269 } | |
270 | |
271 | |
272 void GreedyAllocator::TryAllocateLiveRange(LiveRange* range) { | |
273 // TODO(mtrofin): once we introduce groups, we'll want to first try and | |
274 // allocate at the preferred register. | |
275 TRACE("Attempting to allocate live range %d:%d.\n", range->TopLevel()->vreg(), | |
276 range->relative_id()); | |
277 int free_reg = -1; | |
278 int evictable_reg = -1; | |
279 int hinted_reg = -1; | |
280 | |
281 EnsureValidRangeWeight(range); | |
282 float competing_weight = range->weight(); | |
283 DCHECK(competing_weight != LiveRange::kInvalidWeight); | |
284 | |
285 // Can we allocate at the hinted register? | |
286 if (range->FirstHintPosition(&hinted_reg) != nullptr) { | |
287 DCHECK(hinted_reg >= 0); | |
288 float max_conflict_weight = | |
289 GetMaximumConflictingWeight(hinted_reg, range, competing_weight); | |
290 if (max_conflict_weight == LiveRange::kInvalidWeight) { | |
291 free_reg = hinted_reg; | |
292 } else if (max_conflict_weight < range->weight()) { | |
293 evictable_reg = hinted_reg; | |
294 } | |
295 } | |
296 | |
297 if (free_reg < 0 && evictable_reg < 0) { | |
298 // There was no hinted reg, or we cannot allocate there. | |
299 float smallest_weight = LiveRange::kMaxWeight; | |
300 | |
301 // Seek either the first free register, or, from the set of registers | |
302 // where the maximum conflict is lower than the candidate's weight, the one | |
303 // with the smallest such weight. | |
304 for (int i = 0; i < num_allocatable_registers(); i++) { | |
305 int reg = allocatable_register_code(i); | |
306 // Skip unnecessarily re-visiting the hinted register, if any. | |
307 if (reg == hinted_reg) continue; | |
308 float max_conflict_weight = | |
309 GetMaximumConflictingWeight(reg, range, competing_weight); | |
310 if (max_conflict_weight == LiveRange::kInvalidWeight) { | |
311 free_reg = reg; | |
312 break; | |
313 } | |
314 if (max_conflict_weight < range->weight() && | |
315 max_conflict_weight < smallest_weight) { | |
316 smallest_weight = max_conflict_weight; | |
317 evictable_reg = reg; | |
318 } | |
319 } | |
320 } | |
321 | |
322 // We have a free register, so we use it. | |
323 if (free_reg >= 0) { | |
324 TRACE("Found free register %s for live range %d:%d.\n", | |
325 RegisterName(free_reg), range->TopLevel()->vreg(), | |
326 range->relative_id()); | |
327 AssignRangeToRegister(free_reg, range); | |
328 return; | |
329 } | |
330 | |
331 // We found a register to perform evictions, so we evict and allocate our | |
332 // candidate. | |
333 if (evictable_reg >= 0) { | |
334 TRACE("Found evictable register %s for live range %d:%d.\n", | |
335 RegisterName(free_reg), range->TopLevel()->vreg(), | |
336 range->relative_id()); | |
337 EvictAndRescheduleConflicts(evictable_reg, range); | |
338 AssignRangeToRegister(evictable_reg, range); | |
339 return; | |
340 } | |
341 | |
342 // The range needs to be split or spilled. | |
343 SplitOrSpillBlockedRange(range); | |
344 } | |
345 | |
346 | |
347 void GreedyAllocator::EvictAndRescheduleConflicts(unsigned reg_id, | |
348 const LiveRange* range) { | |
349 auto conflicts = current_allocations(reg_id)->GetConflicts(range); | |
350 for (LiveRange* conflict = conflicts.Current(); conflict != nullptr; | |
351 conflict = conflicts.RemoveCurrentAndGetNext()) { | |
352 DCHECK(conflict->HasRegisterAssigned()); | |
353 CHECK(!conflict->TopLevel()->IsFixed()); | |
354 conflict->UnsetAssignedRegister(); | |
355 UnsetOperands(conflict, data()); | |
356 UpdateWeightAtEviction(conflict); | |
357 scheduler().Schedule(conflict); | |
358 TRACE("Evicted range %d%d.\n", conflict->TopLevel()->vreg(), | |
359 conflict->relative_id()); | |
360 } | |
361 } | |
362 | |
363 | |
364 void GreedyAllocator::AllocateRegisters() { | |
365 CHECK(scheduler().empty()); | |
366 CHECK(allocations_.empty()); | |
367 | |
368 TRACE("Begin allocating function %s with the Greedy Allocator\n", | |
369 data()->debug_name()); | |
370 | |
371 SplitAndSpillRangesDefinedByMemoryOperand(true); | |
372 GroupLiveRanges(); | |
373 ScheduleAllocationCandidates(); | |
374 PreallocateFixedRanges(); | |
375 while (!scheduler().empty()) { | |
376 AllocationCandidate candidate = scheduler().GetNext(); | |
377 TryAllocateCandidate(candidate); | |
378 } | |
379 | |
380 for (size_t i = 0; i < allocations_.size(); ++i) { | |
381 if (!allocations_[i]->empty()) { | |
382 data()->MarkAllocated(mode(), static_cast<int>(i)); | |
383 } | |
384 } | |
385 allocations_.clear(); | |
386 | |
387 TryReuseSpillRangesForGroups(); | |
388 | |
389 TRACE("End allocating function %s with the Greedy Allocator\n", | |
390 data()->debug_name()); | |
391 } | |
392 | |
393 | |
394 void GreedyAllocator::TryReuseSpillRangesForGroups() { | |
395 for (TopLevelLiveRange* top : data()->live_ranges()) { | |
396 if (!CanProcessRange(top) || !top->is_phi() || top->group() == nullptr) { | |
397 continue; | |
398 } | |
399 | |
400 SpillRange* spill_range = nullptr; | |
401 for (LiveRange* member : top->group()->ranges()) { | |
402 if (!member->TopLevel()->HasSpillRange()) continue; | |
403 SpillRange* member_range = member->TopLevel()->GetSpillRange(); | |
404 if (spill_range == nullptr) { | |
405 spill_range = member_range; | |
406 } else { | |
407 // This may not always succeed, because we group non-conflicting ranges | |
408 // that may have been splintered, and the splinters may cause conflicts | |
409 // in the spill ranges. | |
410 // TODO(mtrofin): should the splinters own their own spill ranges? | |
411 spill_range->TryMerge(member_range); | |
412 } | |
413 } | |
414 } | |
415 } | |
416 | |
417 | |
418 float GreedyAllocator::GetMaximumConflictingWeight( | |
419 unsigned reg_id, const LiveRange* range, float competing_weight) const { | |
420 float ret = LiveRange::kInvalidWeight; | |
421 | |
422 auto conflicts = current_allocations(reg_id)->GetConflicts(range); | |
423 for (LiveRange* conflict = conflicts.Current(); conflict != nullptr; | |
424 conflict = conflicts.GetNext()) { | |
425 DCHECK_NE(conflict->weight(), LiveRange::kInvalidWeight); | |
426 if (competing_weight <= conflict->weight()) return LiveRange::kMaxWeight; | |
427 ret = Max(ret, conflict->weight()); | |
428 DCHECK(ret < LiveRange::kMaxWeight); | |
429 } | |
430 | |
431 return ret; | |
432 } | |
433 | |
434 | |
435 float GreedyAllocator::GetMaximumConflictingWeight(unsigned reg_id, | |
436 const LiveRangeGroup* group, | |
437 float group_weight) const { | |
438 float ret = LiveRange::kInvalidWeight; | |
439 | |
440 for (LiveRange* member : group->ranges()) { | |
441 float member_conflict_weight = | |
442 GetMaximumConflictingWeight(reg_id, member, group_weight); | |
443 if (member_conflict_weight == LiveRange::kMaxWeight) { | |
444 return LiveRange::kMaxWeight; | |
445 } | |
446 if (member_conflict_weight > group_weight) return LiveRange::kMaxWeight; | |
447 ret = Max(member_conflict_weight, ret); | |
448 } | |
449 | |
450 return ret; | |
451 } | |
452 | |
453 | |
454 void GreedyAllocator::EnsureValidRangeWeight(LiveRange* range) { | |
455 // The live range weight will be invalidated when ranges are created or split. | |
456 // Otherwise, it is consistently updated when the range is allocated or | |
457 // unallocated. | |
458 if (range->weight() != LiveRange::kInvalidWeight) return; | |
459 | |
460 if (range->TopLevel()->IsFixed()) { | |
461 range->set_weight(LiveRange::kMaxWeight); | |
462 return; | |
463 } | |
464 if (!IsProgressPossible(range)) { | |
465 range->set_weight(LiveRange::kMaxWeight); | |
466 return; | |
467 } | |
468 | |
469 float use_count = 0.0; | |
470 for (auto pos = range->first_pos(); pos != nullptr; pos = pos->next()) { | |
471 ++use_count; | |
472 } | |
473 range->set_weight(use_count / static_cast<float>(range->GetSize())); | |
474 } | |
475 | |
476 | |
477 void GreedyAllocator::SpillRangeAsLastResort(LiveRange* range) { | |
478 LifetimePosition start = range->Start(); | |
479 CHECK(range->CanBeSpilled(start)); | |
480 | |
481 DCHECK(range->NextRegisterPosition(start) == nullptr); | |
482 Spill(range); | |
483 } | |
484 | |
485 | |
486 LiveRange* GreedyAllocator::GetRemainderAfterSplittingAroundFirstCall( | |
487 LiveRange* range) { | |
488 LiveRange* ret = range; | |
489 for (UseInterval* interval = range->first_interval(); interval != nullptr; | |
490 interval = interval->next()) { | |
491 LifetimePosition start = interval->start(); | |
492 LifetimePosition end = interval->end(); | |
493 // If the interval starts at instruction end, then the first instruction | |
494 // in the interval is the next one. | |
495 int first_full_instruction = (start.IsGapPosition() || start.IsStart()) | |
496 ? start.ToInstructionIndex() | |
497 : start.ToInstructionIndex() + 1; | |
498 // If the interval ends in a gap or at instruction start, then the last | |
499 // instruction is the previous one. | |
500 int last_full_instruction = (end.IsGapPosition() || end.IsStart()) | |
501 ? end.ToInstructionIndex() - 1 | |
502 : end.ToInstructionIndex(); | |
503 | |
504 for (int instruction_index = first_full_instruction; | |
505 instruction_index <= last_full_instruction; ++instruction_index) { | |
506 if (!code()->InstructionAt(instruction_index)->IsCall()) continue; | |
507 | |
508 LifetimePosition before = | |
509 GetSplitPositionForInstruction(range, instruction_index); | |
510 LiveRange* second_part = | |
511 before.IsValid() ? Split(range, data(), before) : range; | |
512 | |
513 if (range != second_part) scheduler().Schedule(range); | |
514 | |
515 LifetimePosition after = | |
516 FindSplitPositionAfterCall(second_part, instruction_index); | |
517 | |
518 if (after.IsValid()) { | |
519 ret = Split(second_part, data(), after); | |
520 } else { | |
521 ret = nullptr; | |
522 } | |
523 Spill(second_part); | |
524 return ret; | |
525 } | |
526 } | |
527 return ret; | |
528 } | |
529 | |
530 | |
531 bool GreedyAllocator::TrySplitAroundCalls(LiveRange* range) { | |
532 bool modified = false; | |
533 | |
534 while (range != nullptr) { | |
535 LiveRange* remainder = GetRemainderAfterSplittingAroundFirstCall(range); | |
536 // If we performed no modification, we're done. | |
537 if (remainder == range) { | |
538 break; | |
539 } | |
540 // We performed a modification. | |
541 modified = true; | |
542 range = remainder; | |
543 } | |
544 // If we have a remainder and we made modifications, it means the remainder | |
545 // has no calls and we should schedule it for further processing. If we made | |
546 // no modifications, we will just return false, because we want the algorithm | |
547 // to make progress by trying some other heuristic. | |
548 if (modified && range != nullptr) { | |
549 DCHECK(!range->spilled()); | |
550 DCHECK(!range->HasRegisterAssigned()); | |
551 scheduler().Schedule(range); | |
552 } | |
553 return modified; | |
554 } | |
555 | |
556 | |
557 LifetimePosition GreedyAllocator::FindSplitPositionAfterCall( | |
558 const LiveRange* range, int call_index) { | |
559 LifetimePosition after_call = | |
560 Max(range->Start(), | |
561 LifetimePosition::GapFromInstructionIndex(call_index + 1)); | |
562 UsePosition* next_use = range->NextRegisterPosition(after_call); | |
563 if (!next_use) return LifetimePosition::Invalid(); | |
564 | |
565 LifetimePosition split_pos = FindOptimalSplitPos(after_call, next_use->pos()); | |
566 split_pos = | |
567 GetSplitPositionForInstruction(range, split_pos.ToInstructionIndex()); | |
568 return split_pos; | |
569 } | |
570 | |
571 | |
572 LifetimePosition GreedyAllocator::FindSplitPositionBeforeLoops( | |
573 LiveRange* range) { | |
574 LifetimePosition end = range->End(); | |
575 if (end.ToInstructionIndex() >= code()->LastInstructionIndex()) { | |
576 end = | |
577 LifetimePosition::GapFromInstructionIndex(end.ToInstructionIndex() - 1); | |
578 } | |
579 LifetimePosition pos = FindOptimalSplitPos(range->Start(), end); | |
580 pos = GetSplitPositionForInstruction(range, pos.ToInstructionIndex()); | |
581 return pos; | |
582 } | |
583 | |
584 | |
585 void GreedyAllocator::SplitOrSpillBlockedRange(LiveRange* range) { | |
586 if (TrySplitAroundCalls(range)) return; | |
587 | |
588 LifetimePosition pos = FindSplitPositionBeforeLoops(range); | |
589 | |
590 if (!pos.IsValid()) pos = GetLastResortSplitPosition(range); | |
591 if (pos.IsValid()) { | |
592 LiveRange* tail = Split(range, data(), pos); | |
593 DCHECK(tail != range); | |
594 scheduler().Schedule(tail); | |
595 scheduler().Schedule(range); | |
596 return; | |
597 } | |
598 SpillRangeAsLastResort(range); | |
599 } | |
600 | |
601 | |
602 // Basic heuristic for advancing the algorithm, if any other splitting heuristic | |
603 // failed. | |
604 LifetimePosition GreedyAllocator::GetLastResortSplitPosition( | |
605 const LiveRange* range) { | |
606 LifetimePosition previous = range->Start(); | |
607 for (UsePosition *pos = range->NextRegisterPosition(previous); pos != nullptr; | |
608 previous = previous.NextFullStart(), | |
609 pos = range->NextRegisterPosition(previous)) { | |
610 LifetimePosition optimal = FindOptimalSplitPos(previous, pos->pos()); | |
611 LifetimePosition before = | |
612 GetSplitPositionForInstruction(range, optimal.ToInstructionIndex()); | |
613 if (before.IsValid()) return before; | |
614 LifetimePosition after = GetSplitPositionForInstruction( | |
615 range, pos->pos().ToInstructionIndex() + 1); | |
616 if (after.IsValid()) return after; | |
617 } | |
618 return LifetimePosition::Invalid(); | |
619 } | |
620 | |
621 | |
622 bool GreedyAllocator::IsProgressPossible(const LiveRange* range) { | |
623 return range->CanBeSpilled(range->Start()) || | |
624 GetLastResortSplitPosition(range).IsValid(); | |
625 } | |
626 | |
627 } // namespace compiler | |
628 } // namespace internal | |
629 } // namespace v8 | |
OLD | NEW |