Chromium Code Reviews| Index: src/compiler/register-allocator.cc |
| diff --git a/src/compiler/register-allocator.cc b/src/compiler/register-allocator.cc |
| index 0d22346027e0930edf566a5b7b090c1e36d14375..c6f88060b955e298e8349d40a98e7cd4af3f80ab 100644 |
| --- a/src/compiler/register-allocator.cc |
| +++ b/src/compiler/register-allocator.cc |
| @@ -225,6 +225,22 @@ UseInterval* UseInterval::SplitAt(LifetimePosition pos, Zone* zone) { |
| } |
| +std::ostream& operator<<(std::ostream& os, const LifetimePosition pos) { |
| + os << '@' << pos.ToInstructionIndex(); |
| + if (pos.IsGapPosition()) { |
| + os << 'g'; |
| + } else { |
| + os << 'i'; |
| + } |
| + if (pos.IsStart()) { |
| + os << 's'; |
| + } else { |
| + os << 'e'; |
| + } |
| + return os; |
| +} |
| + |
| + |
| struct LiveRange::SpillAtDefinitionList : ZoneObject { |
| SpillAtDefinitionList(int gap_index, InstructionOperand* operand, |
| SpillAtDefinitionList* next) |
| @@ -766,6 +782,35 @@ static bool AreUseIntervalsIntersecting(UseInterval* interval1, |
| } |
| +std::ostream& operator<<(std::ostream& os, |
| + const PrintableLiveRange printable_range) { |
| + const LiveRange* range = printable_range.range_; |
| + os << "Range: " << range->id() << " "; |
| + if (range->is_phi()) os << "phi "; |
| + if (range->is_non_loop_phi()) os << "nlphi "; |
| + |
| + os << "{" << std::endl; |
| + auto interval = range->first_interval(); |
| + auto use_pos = range->first_pos(); |
| + PrintableInstructionOperand pio; |
| + pio.register_configuration_ = printable_range.register_configuration_; |
| + while (use_pos != nullptr) { |
| + pio.op_ = *use_pos->operand(); |
| + os << pio << use_pos->pos() << " "; |
| + use_pos = use_pos->next(); |
| + } |
| + os << std::endl; |
| + |
| + while (interval != nullptr) { |
| + os << '[' << interval->start() << ", " << interval->end() << ')' |
| + << std::endl; |
| + interval = interval->next(); |
| + } |
| + os << "}"; |
| + return os; |
| +} |
| + |
| + |
| SpillRange::SpillRange(LiveRange* parent, Zone* zone) |
| : live_ranges_(zone), assigned_slot_(kUnassignedSlot) { |
| DCHECK(!parent->IsChild()); |
| @@ -2428,6 +2473,7 @@ const std::pair<int, int> CoallescedLiveRanges::Config::kNoKey = {0, 0}; |
| GreedyAllocator::GreedyAllocator(RegisterAllocationData* data, |
| RegisterKind kind, Zone* local_zone) |
| : RegisterAllocator(data, kind), |
| + local_zone_(local_zone), |
| allocations_(local_zone), |
| queue_(local_zone) {} |
| @@ -2461,11 +2507,19 @@ void GreedyAllocator::AssignRangeToRegister(int reg_id, LiveRange* range) { |
| float GreedyAllocator::CalculateSpillWeight(LiveRange* range) { |
| - if (range->IsFixed()) return std::numeric_limits<float>::max(); |
| + InstructionOperand* first_hint = nullptr; |
| + if (range->FirstHintPosition() != nullptr) { |
| + first_hint = range->FirstHintPosition()->operand(); |
| + } |
| - int hint_register; |
| - if (range->FirstHintPosition(&hint_register) != nullptr) { |
| + if (range->IsFixed()) return std::numeric_limits<float>::max(); |
| + bool spill; |
| + if (!FindProgressingSplitPosition(range, &spill).IsValid()) |
| return std::numeric_limits<float>::max(); |
| + |
| + float multiplier = 1.0; |
| + if (first_hint != nullptr && first_hint->IsRegister()) { |
| + multiplier = 3.0; |
| } |
| unsigned use_count = 0; |
| @@ -2475,11 +2529,11 @@ float GreedyAllocator::CalculateSpillWeight(LiveRange* range) { |
| pos = pos->next(); |
| } |
| - // GetLiveRangeSize is DCHECK-ed to not be 0 |
| unsigned range_size = GetLiveRangeSize(range); |
| DCHECK_NE(0U, range_size); |
| - return static_cast<float>(use_count) / static_cast<float>(range_size); |
| + return multiplier * static_cast<float>(use_count) / |
| + static_cast<float>(range_size); |
| } |
| @@ -2522,32 +2576,48 @@ bool GreedyAllocator::TryAllocatePhysicalRegister( |
| return true; |
| } |
| + |
| +int GreedyAllocator::GetHintedRegister(LiveRange* range) { |
| + int reg; |
| + if (range->FirstHintPosition(®) != nullptr) { |
| + return reg; |
| + } |
| + return -1; |
| +} |
| + |
| + |
| bool GreedyAllocator::TryAllocate(LiveRange* current, |
| ZoneSet<LiveRange*>* conflicting) { |
| - if (current->HasSpillOperand()) { |
| - Spill(current); |
| - return true; |
| - } |
| if (current->IsFixed()) { |
| return TryAllocatePhysicalRegister(current->assigned_register(), current, |
| conflicting); |
| } |
| - if (current->HasRegisterAssigned()) { |
| - int reg_id = current->assigned_register(); |
| - return TryAllocatePhysicalRegister(reg_id, current, conflicting); |
| + int hinted_reg_id = GetHintedRegister(current); |
| + if (hinted_reg_id >= 0) { |
| + if (TryAllocatePhysicalRegister(hinted_reg_id, current, conflicting)) { |
| + return true; |
| + } |
| } |
| + ZoneSet<LiveRange*> local_conflicts(local_zone()); |
| for (unsigned candidate_reg = 0; candidate_reg < allocations_.size(); |
| candidate_reg++) { |
| - if (TryAllocatePhysicalRegister(candidate_reg, current, conflicting)) { |
| + if (hinted_reg_id >= 0 && |
| + candidate_reg == static_cast<size_t>(hinted_reg_id)) |
| + continue; |
| + local_conflicts.clear(); |
| + if (TryAllocatePhysicalRegister(candidate_reg, current, &local_conflicts)) { |
| conflicting->clear(); |
| return true; |
| + } else { |
| + conflicting->insert(local_conflicts.begin(), local_conflicts.end()); |
| } |
| } |
| return false; |
| } |
| + |
| LiveRange* GreedyAllocator::SpillBetweenUntil(LiveRange* range, |
| LifetimePosition start, |
| LifetimePosition until, |
| @@ -2581,12 +2651,11 @@ LiveRange* GreedyAllocator::SpillBetweenUntil(LiveRange* range, |
| void GreedyAllocator::Enqueue(LiveRange* range) { |
| if (range == nullptr || range->IsEmpty()) return; |
| unsigned size = GetLiveRangeSize(range); |
| + TRACE("Enqueuing range %d\n", range->id()); |
| queue_.push(std::make_pair(size, range)); |
| } |
| -// TODO(mtrofin): consolidate with identical code segment in |
| -// LinearScanAllocator::AllocateRegisters |
| bool GreedyAllocator::HandleSpillOperands(LiveRange* range) { |
| auto position = range->Start(); |
| TRACE("Processing interval %d start=%d\n", range->id(), position.value()); |
| @@ -2611,9 +2680,7 @@ bool GreedyAllocator::HandleSpillOperands(LiveRange* range) { |
| return true; |
| } |
| } |
| - return false; |
| - // TODO(mtrofin): Do we need this? |
| - // return (TryReuseSpillForPhi(range)); |
| + return (TryReuseSpillForPhi(range)); |
|
dcarney
2015/05/01 07:56:03
extra ()
Mircea Trofin
2015/05/01 15:06:33
Done.
|
| } |
| @@ -2628,9 +2695,8 @@ void GreedyAllocator::AllocateRegisters() { |
| } |
| allocations_.resize(num_registers()); |
| - auto* zone = data()->allocation_zone(); |
| for (int i = 0; i < num_registers(); i++) { |
| - allocations_[i] = new (zone) CoallescedLiveRanges(zone); |
| + allocations_[i] = new (local_zone()) CoallescedLiveRanges(local_zone()); |
| } |
| for (auto* current : GetFixedRegisters(data(), mode())) { |
| @@ -2646,25 +2712,36 @@ void GreedyAllocator::AllocateRegisters() { |
| auto current_pair = queue_.top(); |
| queue_.pop(); |
| auto current = current_pair.second; |
| - if (HandleSpillOperands(current)) continue; |
| - ZoneSet<LiveRange*> conflicting(zone); |
| + if (HandleSpillOperands(current)) { |
| + continue; |
| + } |
| + bool spill = false; |
| + ZoneSet<LiveRange*> conflicting(local_zone()); |
| if (!TryAllocate(current, &conflicting)) { |
| DCHECK(!conflicting.empty()); |
| - float this_max = CalculateSpillWeight(current); |
| - float max_conflicting = CalculateMaxSpillWeight(conflicting); |
| - if (max_conflicting < this_max) { |
| - for (auto* conflict : conflicting) { |
| + float this_weight = std::numeric_limits<float>::max(); |
| + LifetimePosition split_pos = |
| + FindProgressingSplitPosition(current, &spill); |
| + if (split_pos.IsValid()) { |
| + this_weight = CalculateSpillWeight(current); |
| + } |
| + |
| + bool evicted = false; |
| + for (auto* conflict : conflicting) { |
| + if (CalculateSpillWeight(conflict) < this_weight) { |
| Evict(conflict); |
| Enqueue(conflict); |
| + evicted = true; |
| } |
| + } |
| + if (evicted) { |
| conflicting.clear(); |
| - bool allocated = TryAllocate(current, &conflicting); |
| - CHECK(allocated); |
| - DCHECK(conflicting.empty()); |
| - } else { |
| + TryAllocate(current, &conflicting); |
| + } |
| + if (!conflicting.empty()) { |
| DCHECK(!current->IsFixed() || current->CanBeSpilled(current->Start())); |
| - bool allocated = AllocateBlockedRange(current, conflicting); |
| - CHECK(allocated); |
| + DCHECK(split_pos.IsValid()); |
| + AllocateBlockedRange(current, split_pos, spill); |
| } |
| } |
| } |
| @@ -2677,20 +2754,87 @@ void GreedyAllocator::AllocateRegisters() { |
| } |
| -bool GreedyAllocator::AllocateBlockedRange( |
| - LiveRange* current, const ZoneSet<LiveRange*>& conflicts) { |
| - auto register_use = current->NextRegisterPosition(current->Start()); |
| - if (register_use == nullptr) { |
| - // There is no use in the current live range that requires a register. |
| - // We can just spill it. |
| - Spill(current); |
| - return true; |
| +LifetimePosition GreedyAllocator::GetSplittablePos(LifetimePosition pos) { |
| + auto ret = pos.PrevStart().End(); |
| + if (IsBlockBoundary(code(), pos.Start())) { |
| + ret = pos.Start(); |
| } |
| + DCHECK(ret <= pos); |
| + return ret; |
| +} |
| - auto second_part = SplitRangeAt(current, register_use->pos()); |
| - Spill(second_part); |
| +LifetimePosition GreedyAllocator::FindProgressingSplitPosition( |
| + LiveRange* range, bool* is_spill_pos) { |
| + auto start = range->Start(); |
| + auto end = range->End(); |
| - return true; |
| + UsePosition* next_reg_use = range->first_pos(); |
| + while (next_reg_use != nullptr && |
| + next_reg_use->type() != UsePositionType::kRequiresRegister) { |
| + next_reg_use = next_reg_use->next(); |
| + } |
| + |
| + if (next_reg_use == nullptr) { |
| + *is_spill_pos = true; |
| + auto ret = FindOptimalSpillingPos(range, start); |
| + DCHECK(ret.IsValid()); |
| + return ret; |
| + } |
| + |
| + *is_spill_pos = false; |
| + auto reg_pos = next_reg_use->pos(); |
| + auto correct_pos = GetSplittablePos(reg_pos); |
| + if (start < correct_pos && correct_pos < end) { |
| + return correct_pos; |
| + } |
| + |
| + if (correct_pos >= end) { |
| + return LifetimePosition::Invalid(); |
| + } |
| + |
| + // Correct_pos must be at or before start. Find the next use position. |
| + next_reg_use = next_reg_use->next(); |
| + auto reference = reg_pos; |
| + while (next_reg_use != nullptr) { |
| + auto pos = next_reg_use->pos(); |
| + // Skip over tight successive uses. |
| + if (reference.NextStart() < pos) { |
| + break; |
| + } |
| + reference = pos; |
| + next_reg_use = next_reg_use->next(); |
| + } |
| + |
| + if (next_reg_use == nullptr) { |
| + // While there may not be another use, we may still have space in the range |
| + // to clip off. |
| + correct_pos = reference.NextStart(); |
| + if (start < correct_pos && correct_pos < end) { |
| + return correct_pos; |
| + } |
| + return LifetimePosition::Invalid(); |
| + } |
| + |
| + correct_pos = GetSplittablePos(next_reg_use->pos()); |
| + if (start < correct_pos && correct_pos < end) { |
| + DCHECK(reference < correct_pos); |
| + return correct_pos; |
| + } |
| + return LifetimePosition::Invalid(); |
| +} |
| + |
| + |
| +void GreedyAllocator::AllocateBlockedRange(LiveRange* current, |
| + LifetimePosition pos, bool spill) { |
| + auto tail = SplitRangeAt(current, pos); |
| + if (spill) { |
| + Spill(tail); |
| + } else { |
| + Enqueue(tail); |
| + } |
| + if (tail != current) { |
| + Enqueue(current); |
| + } |
| } |
| @@ -2713,6 +2857,91 @@ void SpillSlotLocator::LocateSpillSlots() { |
| } |
| +bool GreedyAllocator::TryReuseSpillForPhi(LiveRange* range) { |
| + if (range->IsChild() || !range->is_phi()) return false; |
| + DCHECK(!range->HasSpillOperand()); |
| + |
| + auto phi_map_value = data()->GetPhiMapValueFor(range->id()); |
| + auto phi = phi_map_value->phi(); |
| + auto block = phi_map_value->block(); |
| + // Count the number of spilled operands. |
| + size_t spilled_count = 0; |
| + LiveRange* first_op = nullptr; |
| + for (size_t i = 0; i < phi->operands().size(); i++) { |
| + int op = phi->operands()[i]; |
| + LiveRange* op_range = LiveRangeFor(op); |
| + if (!op_range->HasSpillRange()) continue; |
| + auto pred = code()->InstructionBlockAt(block->predecessors()[i]); |
| + auto pred_end = LifetimePosition::InstructionFromInstructionIndex( |
| + pred->last_instruction_index()); |
| + while (op_range != nullptr && !op_range->CanCover(pred_end)) { |
| + op_range = op_range->next(); |
| + } |
| + if (op_range != nullptr && op_range->spilled()) { |
| + spilled_count++; |
| + if (first_op == nullptr) { |
| + first_op = op_range->TopLevel(); |
| + } |
| + } |
| + } |
| + |
| + // Only continue if more than half of the operands are spilled. |
| + if (spilled_count * 2 <= phi->operands().size()) { |
| + return false; |
| + } |
| + |
| + // Try to merge the spilled operands and count the number of merged spilled |
| + // operands. |
| + DCHECK(first_op != nullptr); |
| + auto first_op_spill = first_op->GetSpillRange(); |
| + size_t num_merged = 1; |
| + for (size_t i = 1; i < phi->operands().size(); i++) { |
| + int op = phi->operands()[i]; |
| + auto op_range = LiveRangeFor(op); |
| + if (!op_range->HasSpillRange()) continue; |
| + auto op_spill = op_range->GetSpillRange(); |
| + if (op_spill == first_op_spill || first_op_spill->TryMerge(op_spill)) { |
| + num_merged++; |
| + } |
| + } |
| + |
| + // Only continue if enough operands could be merged to the |
| + // same spill slot. |
| + if (num_merged * 2 <= phi->operands().size() || |
| + AreUseIntervalsIntersecting(first_op_spill->interval(), |
| + range->first_interval())) { |
| + return false; |
| + } |
| + |
| + // If the range does not need register soon, spill it to the merged |
| + // spill range. |
| + auto next_pos = range->Start(); |
| + if (next_pos.IsGapPosition()) next_pos = next_pos.NextStart(); |
| + auto pos = range->NextUsePositionRegisterIsBeneficial(next_pos); |
| + if (pos == nullptr) { |
| + auto spill_range = |
| + range->TopLevel()->HasSpillRange() |
| + ? range->TopLevel()->GetSpillRange() |
| + : data()->AssignSpillRangeToLiveRange(range->TopLevel()); |
| + bool merged = first_op_spill->TryMerge(spill_range); |
| + CHECK(merged); |
| + Spill(range); |
| + return true; |
| + } else if (pos->pos() > range->Start().NextStart()) { |
| + auto spill_range = |
| + range->TopLevel()->HasSpillRange() |
| + ? range->TopLevel()->GetSpillRange() |
| + : data()->AssignSpillRangeToLiveRange(range->TopLevel()); |
| + bool merged = first_op_spill->TryMerge(spill_range); |
| + CHECK(merged); |
| + Enqueue( |
| + SpillBetweenUntil(range, range->Start(), range->Start(), pos->pos())); |
| + return true; |
| + } |
| + return false; |
| +} |
| + |
| + |
| OperandAssigner::OperandAssigner(RegisterAllocationData* data) : data_(data) {} |
| @@ -2755,8 +2984,9 @@ void OperandAssigner::CommitAssignment() { |
| data()->GetPhiMapValueFor(range->id())->CommitAssignment(assigned); |
| } |
| if (!range->IsChild() && !spill_operand.IsInvalid()) { |
| - range->CommitSpillsAtDefinition(data()->code(), spill_operand, |
| - range->has_slot_use()); |
| + range->CommitSpillsAtDefinition( |
| + data()->code(), spill_operand, |
| + range->has_slot_use() || range->spilled()); |
| } |
| } |
| } |