Index: src/compiler/register-allocator.cc |
diff --git a/src/compiler/register-allocator.cc b/src/compiler/register-allocator.cc |
index 9d745d0097f4e627707a0349f006c6073db7592d..ec4120ea2e17ea4f11725cac22499739555655ca 100644 |
--- a/src/compiler/register-allocator.cc |
+++ b/src/compiler/register-allocator.cc |
@@ -15,22 +15,26 @@ namespace compiler { |
if (FLAG_trace_alloc) PrintF(__VA_ARGS__); \ |
} while (false) |
-static inline LifetimePosition Min(LifetimePosition a, LifetimePosition b) { |
+namespace { |
+ |
+inline LifetimePosition Min(LifetimePosition a, LifetimePosition b) { |
return a.Value() < b.Value() ? a : b; |
} |
-static inline LifetimePosition Max(LifetimePosition a, LifetimePosition b) { |
+inline LifetimePosition Max(LifetimePosition a, LifetimePosition b) { |
return a.Value() > b.Value() ? a : b; |
} |
-static void RemoveElement(ZoneVector<LiveRange*>* v, LiveRange* range) { |
+void RemoveElement(ZoneVector<LiveRange*>* v, LiveRange* range) { |
auto it = std::find(v->begin(), v->end(), range); |
DCHECK(it != v->end()); |
v->erase(it); |
} |
+} // namespace |
+ |
UsePosition::UsePosition(LifetimePosition pos, InstructionOperand* operand, |
InstructionOperand* hint) |
@@ -85,14 +89,33 @@ struct LiveRange::SpillAtDefinitionList : ZoneObject { |
}; |
-#ifdef DEBUG |
+LiveRange::LiveRange(int id, Zone* zone) |
+ : id_(id), |
+ spilled_(false), |
+ has_slot_use_(false), |
+ is_phi_(false), |
+ is_non_loop_phi_(false), |
+ kind_(UNALLOCATED_REGISTERS), |
+ assigned_register_(kInvalidAssignment), |
+ last_interval_(nullptr), |
+ first_interval_(nullptr), |
+ first_pos_(nullptr), |
+ parent_(nullptr), |
+ next_(nullptr), |
+ current_interval_(nullptr), |
+ last_processed_use_(nullptr), |
+ current_hint_operand_(nullptr), |
+ spill_start_index_(kMaxInt), |
+ spill_type_(SpillType::kNoSpillType), |
+ spill_operand_(nullptr), |
+ spills_at_definition_(nullptr) {} |
void LiveRange::Verify() const { |
UsePosition* cur = first_pos_; |
while (cur != nullptr) { |
- DCHECK(Start().Value() <= cur->pos().Value() && |
- cur->pos().Value() <= End().Value()); |
+ CHECK(Start().Value() <= cur->pos().Value() && |
+ cur->pos().Value() <= End().Value()); |
cur = cur->next(); |
} |
} |
@@ -112,31 +135,6 @@ bool LiveRange::HasOverlap(UseInterval* target) const { |
} |
-#endif |
- |
- |
-LiveRange::LiveRange(int id, Zone* zone) |
- : id_(id), |
- spilled_(false), |
- has_slot_use_(false), |
- is_phi_(false), |
- is_non_loop_phi_(false), |
- kind_(UNALLOCATED_REGISTERS), |
- assigned_register_(kInvalidAssignment), |
- last_interval_(nullptr), |
- first_interval_(nullptr), |
- first_pos_(nullptr), |
- parent_(nullptr), |
- next_(nullptr), |
- current_interval_(nullptr), |
- last_processed_use_(nullptr), |
- current_hint_operand_(nullptr), |
- spill_start_index_(kMaxInt), |
- spill_type_(SpillType::kNoSpillType), |
- spill_operand_(nullptr), |
- spills_at_definition_(nullptr) {} |
- |
- |
void LiveRange::set_assigned_register(int reg) { |
DCHECK(!HasRegisterAssigned() && !IsSpilled()); |
assigned_register_ = reg; |
@@ -571,244 +569,6 @@ LifetimePosition LiveRange::FirstIntersection(LiveRange* other) { |
} |
-RegisterAllocator::RegisterAllocator(const RegisterConfiguration* config, |
- Zone* zone, Frame* frame, |
- InstructionSequence* code, |
- const char* debug_name) |
- : local_zone_(zone), |
- frame_(frame), |
- code_(code), |
- debug_name_(debug_name), |
- config_(config), |
- phi_map_(local_zone()), |
- live_in_sets_(code->InstructionBlockCount(), nullptr, local_zone()), |
- live_ranges_(code->VirtualRegisterCount() * 2, nullptr, local_zone()), |
- fixed_live_ranges_(this->config()->num_general_registers(), nullptr, |
- local_zone()), |
- fixed_double_live_ranges_(this->config()->num_double_registers(), nullptr, |
- local_zone()), |
- unhandled_live_ranges_(local_zone()), |
- active_live_ranges_(local_zone()), |
- inactive_live_ranges_(local_zone()), |
- spill_ranges_(local_zone()), |
- mode_(UNALLOCATED_REGISTERS), |
- num_registers_(-1) { |
- DCHECK(this->config()->num_general_registers() <= |
- RegisterConfiguration::kMaxGeneralRegisters); |
- DCHECK(this->config()->num_double_registers() <= |
- RegisterConfiguration::kMaxDoubleRegisters); |
- // TryAllocateFreeReg and AllocateBlockedReg assume this |
- // when allocating local arrays. |
- DCHECK(RegisterConfiguration::kMaxDoubleRegisters >= |
- this->config()->num_general_registers()); |
- unhandled_live_ranges().reserve( |
- static_cast<size_t>(code->VirtualRegisterCount() * 2)); |
- active_live_ranges().reserve(8); |
- inactive_live_ranges().reserve(8); |
- spill_ranges().reserve(8); |
- assigned_registers_ = |
- new (code_zone()) BitVector(config->num_general_registers(), code_zone()); |
- assigned_double_registers_ = new (code_zone()) |
- BitVector(config->num_aliased_double_registers(), code_zone()); |
- frame->SetAllocatedRegisters(assigned_registers_); |
- frame->SetAllocatedDoubleRegisters(assigned_double_registers_); |
-} |
- |
- |
-BitVector* RegisterAllocator::ComputeLiveOut(const InstructionBlock* block) { |
- // Compute live out for the given block, except not including backward |
- // successor edges. |
- auto live_out = new (local_zone()) |
- BitVector(code()->VirtualRegisterCount(), local_zone()); |
- |
- // Process all successor blocks. |
- for (auto succ : block->successors()) { |
- // Add values live on entry to the successor. Note the successor's |
- // live_in will not be computed yet for backwards edges. |
- auto live_in = live_in_sets_[succ.ToSize()]; |
- if (live_in != nullptr) live_out->Union(*live_in); |
- |
- // All phi input operands corresponding to this successor edge are live |
- // out from this block. |
- auto successor = code()->InstructionBlockAt(succ); |
- size_t index = successor->PredecessorIndexOf(block->rpo_number()); |
- DCHECK(index < successor->PredecessorCount()); |
- for (auto phi : successor->phis()) { |
- live_out->Add(phi->operands()[index]); |
- } |
- } |
- return live_out; |
-} |
- |
- |
-void RegisterAllocator::AddInitialIntervals(const InstructionBlock* block, |
- BitVector* live_out) { |
- // Add an interval that includes the entire block to the live range for |
- // each live_out value. |
- auto start = LifetimePosition::GapFromInstructionIndex( |
- block->first_instruction_index()); |
- auto end = LifetimePosition::InstructionFromInstructionIndex( |
- block->last_instruction_index()).NextStart(); |
- BitVector::Iterator iterator(live_out); |
- while (!iterator.Done()) { |
- int operand_index = iterator.Current(); |
- auto range = LiveRangeFor(operand_index); |
- range->AddUseInterval(start, end, local_zone()); |
- iterator.Advance(); |
- } |
-} |
- |
- |
-int RegisterAllocator::FixedDoubleLiveRangeID(int index) { |
- return -index - 1 - config()->num_general_registers(); |
-} |
- |
- |
-InstructionOperand* RegisterAllocator::AllocateFixed( |
- UnallocatedOperand* operand, int pos, bool is_tagged) { |
- TRACE("Allocating fixed reg for op %d\n", operand->virtual_register()); |
- DCHECK(operand->HasFixedPolicy()); |
- InstructionOperand allocated; |
- if (operand->HasFixedSlotPolicy()) { |
- allocated = AllocatedOperand(AllocatedOperand::STACK_SLOT, |
- operand->fixed_slot_index()); |
- } else if (operand->HasFixedRegisterPolicy()) { |
- allocated = AllocatedOperand(AllocatedOperand::REGISTER, |
- operand->fixed_register_index()); |
- } else if (operand->HasFixedDoubleRegisterPolicy()) { |
- allocated = AllocatedOperand(AllocatedOperand::DOUBLE_REGISTER, |
- operand->fixed_register_index()); |
- } else { |
- UNREACHABLE(); |
- } |
- InstructionOperand::ReplaceWith(operand, &allocated); |
- if (is_tagged) { |
- TRACE("Fixed reg is tagged at %d\n", pos); |
- auto instr = InstructionAt(pos); |
- if (instr->HasReferenceMap()) { |
- instr->reference_map()->RecordReference(*operand); |
- } |
- } |
- return operand; |
-} |
- |
- |
-LiveRange* RegisterAllocator::NewLiveRange(int index) { |
- // The LiveRange object itself can go in the local zone, but the |
- // InstructionOperand needs to go in the code zone, since it may survive |
- // register allocation. |
- return new (local_zone()) LiveRange(index, code_zone()); |
-} |
- |
- |
-LiveRange* RegisterAllocator::FixedLiveRangeFor(int index) { |
- DCHECK(index < config()->num_general_registers()); |
- auto result = fixed_live_ranges()[index]; |
- if (result == nullptr) { |
- result = NewLiveRange(FixedLiveRangeID(index)); |
- DCHECK(result->IsFixed()); |
- result->kind_ = GENERAL_REGISTERS; |
- SetLiveRangeAssignedRegister(result, index); |
- fixed_live_ranges()[index] = result; |
- } |
- return result; |
-} |
- |
- |
-LiveRange* RegisterAllocator::FixedDoubleLiveRangeFor(int index) { |
- DCHECK(index < config()->num_aliased_double_registers()); |
- auto result = fixed_double_live_ranges()[index]; |
- if (result == nullptr) { |
- result = NewLiveRange(FixedDoubleLiveRangeID(index)); |
- DCHECK(result->IsFixed()); |
- result->kind_ = DOUBLE_REGISTERS; |
- SetLiveRangeAssignedRegister(result, index); |
- fixed_double_live_ranges()[index] = result; |
- } |
- return result; |
-} |
- |
- |
-LiveRange* RegisterAllocator::LiveRangeFor(int index) { |
- if (index >= static_cast<int>(live_ranges().size())) { |
- live_ranges().resize(index + 1, nullptr); |
- } |
- auto result = live_ranges()[index]; |
- if (result == nullptr) { |
- result = NewLiveRange(index); |
- live_ranges()[index] = result; |
- } |
- return result; |
-} |
- |
- |
-Instruction* RegisterAllocator::GetLastInstruction( |
- const InstructionBlock* block) { |
- return code()->InstructionAt(block->last_instruction_index()); |
-} |
- |
- |
-LiveRange* RegisterAllocator::LiveRangeFor(InstructionOperand* operand) { |
- if (operand->IsUnallocated()) { |
- return LiveRangeFor(UnallocatedOperand::cast(operand)->virtual_register()); |
- } else if (operand->IsConstant()) { |
- return LiveRangeFor(ConstantOperand::cast(operand)->virtual_register()); |
- } else if (operand->IsRegister()) { |
- return FixedLiveRangeFor(RegisterOperand::cast(operand)->index()); |
- } else if (operand->IsDoubleRegister()) { |
- return FixedDoubleLiveRangeFor( |
- DoubleRegisterOperand::cast(operand)->index()); |
- } else { |
- return nullptr; |
- } |
-} |
- |
- |
-void RegisterAllocator::Define(LifetimePosition position, |
- InstructionOperand* operand, |
- InstructionOperand* hint) { |
- auto range = LiveRangeFor(operand); |
- if (range == nullptr) return; |
- |
- if (range->IsEmpty() || range->Start().Value() > position.Value()) { |
- // Can happen if there is a definition without use. |
- range->AddUseInterval(position, position.NextStart(), local_zone()); |
- range->AddUsePosition(position.NextStart(), nullptr, nullptr, local_zone()); |
- } else { |
- range->ShortenTo(position); |
- } |
- |
- if (operand->IsUnallocated()) { |
- auto unalloc_operand = UnallocatedOperand::cast(operand); |
- range->AddUsePosition(position, unalloc_operand, hint, local_zone()); |
- } |
-} |
- |
- |
-void RegisterAllocator::Use(LifetimePosition block_start, |
- LifetimePosition position, |
- InstructionOperand* operand, |
- InstructionOperand* hint) { |
- auto range = LiveRangeFor(operand); |
- if (range == nullptr) return; |
- if (operand->IsUnallocated()) { |
- UnallocatedOperand* unalloc_operand = UnallocatedOperand::cast(operand); |
- range->AddUsePosition(position, unalloc_operand, hint, local_zone()); |
- } |
- range->AddUseInterval(block_start, position, local_zone()); |
-} |
- |
- |
-MoveOperands* RegisterAllocator::AddGapMove(int index, |
- Instruction::GapPosition position, |
- const InstructionOperand& from, |
- const InstructionOperand& to) { |
- auto instr = code()->InstructionAt(index); |
- auto moves = instr->GetOrCreateParallelMove(position, code_zone()); |
- return moves->AddMove(from, to); |
-} |
- |
- |
static bool AreUseIntervalsIntersecting(UseInterval* interval1, |
UseInterval* interval2) { |
while (interval1 != nullptr && interval2 != nullptr) { |
@@ -920,142 +680,295 @@ void SpillRange::MergeDisjointIntervals(UseInterval* other) { |
} |
-void RegisterAllocator::AssignSpillSlots() { |
- // Merge disjoint spill ranges |
- for (size_t i = 0; i < spill_ranges().size(); i++) { |
- auto range = spill_ranges()[i]; |
- if (range->IsEmpty()) continue; |
- for (size_t j = i + 1; j < spill_ranges().size(); j++) { |
- auto other = spill_ranges()[j]; |
- if (!other->IsEmpty()) { |
- range->TryMerge(other); |
- } |
- } |
- } |
- |
- // Allocate slots for the merged spill ranges. |
- for (auto range : spill_ranges()) { |
- if (range->IsEmpty()) continue; |
- // Allocate a new operand referring to the spill slot. |
- auto kind = range->Kind(); |
- int index = frame()->AllocateSpillSlot(kind == DOUBLE_REGISTERS); |
- auto op_kind = kind == DOUBLE_REGISTERS |
- ? AllocatedOperand::DOUBLE_STACK_SLOT |
- : AllocatedOperand::STACK_SLOT; |
- auto op = AllocatedOperand::New(code_zone(), op_kind, index); |
- range->SetOperand(op); |
- } |
+RegisterAllocationData::RegisterAllocationData( |
+ const RegisterConfiguration* config, Zone* zone, Frame* frame, |
+ InstructionSequence* code, const char* debug_name) |
+ : allocation_zone_(zone), |
+ frame_(frame), |
+ code_(code), |
+ debug_name_(debug_name), |
+ config_(config), |
+ phi_map_(allocation_zone()), |
+ live_in_sets_(code->InstructionBlockCount(), nullptr, allocation_zone()), |
+ live_ranges_(code->VirtualRegisterCount() * 2, nullptr, |
+ allocation_zone()), |
+ fixed_live_ranges_(this->config()->num_general_registers(), nullptr, |
+ allocation_zone()), |
+ fixed_double_live_ranges_(this->config()->num_double_registers(), nullptr, |
+ allocation_zone()), |
+ spill_ranges_(allocation_zone()), |
+ assigned_registers_(nullptr), |
+ assigned_double_registers_(nullptr) { |
+ DCHECK(this->config()->num_general_registers() <= |
+ RegisterConfiguration::kMaxGeneralRegisters); |
+ DCHECK(this->config()->num_double_registers() <= |
+ RegisterConfiguration::kMaxDoubleRegisters); |
+ spill_ranges().reserve(8); |
+ assigned_registers_ = new (code_zone()) |
+ BitVector(this->config()->num_general_registers(), code_zone()); |
+ assigned_double_registers_ = new (code_zone()) |
+ BitVector(this->config()->num_aliased_double_registers(), code_zone()); |
+ this->frame()->SetAllocatedRegisters(assigned_registers_); |
+ this->frame()->SetAllocatedDoubleRegisters(assigned_double_registers_); |
} |
-void RegisterAllocator::CommitAssignment() { |
- for (auto range : live_ranges()) { |
- if (range == nullptr || range->IsEmpty()) continue; |
- InstructionOperand* spill_operand = nullptr; |
- if (!range->TopLevel()->HasNoSpillType()) { |
- spill_operand = range->TopLevel()->GetSpillOperand(); |
- } |
- auto assigned = range->GetAssignedOperand(); |
- range->ConvertUsesToOperand(assigned, spill_operand); |
- if (range->is_phi()) AssignPhiInput(range, assigned); |
- if (!range->IsChild() && spill_operand != nullptr) { |
- range->CommitSpillsAtDefinition(code(), spill_operand, |
- range->has_slot_use()); |
+LiveRange* RegisterAllocationData::LiveRangeFor(int index) { |
+ if (index >= static_cast<int>(live_ranges().size())) { |
+ live_ranges().resize(index + 1, nullptr); |
+ } |
+ auto result = live_ranges()[index]; |
+ if (result == nullptr) { |
+ result = NewLiveRange(index); |
+ live_ranges()[index] = result; |
+ } |
+ return result; |
+} |
+ |
+ |
+MoveOperands* RegisterAllocationData::AddGapMove( |
+ int index, Instruction::GapPosition position, |
+ const InstructionOperand& from, const InstructionOperand& to) { |
+ auto instr = code()->InstructionAt(index); |
+ auto moves = instr->GetOrCreateParallelMove(position, code_zone()); |
+ return moves->AddMove(from, to); |
+} |
+ |
+ |
+void RegisterAllocationData::AssignPhiInput( |
+ LiveRange* range, const InstructionOperand& assignment) { |
+ DCHECK(range->is_phi()); |
+ auto it = phi_map().find(range->id()); |
+ DCHECK(it != phi_map().end()); |
+ for (auto move : it->second->incoming_moves) { |
+ move->set_destination(assignment); |
+ } |
+} |
+ |
+ |
+LiveRange* RegisterAllocationData::NewLiveRange(int index) { |
+ // The LiveRange object itself can go in the local zone, but the |
+ // InstructionOperand needs to go in the code zone, since it may survive |
+ // register allocation. |
+ return new (allocation_zone()) LiveRange(index, code_zone()); |
+} |
+ |
+ |
+bool RegisterAllocationData::ExistsUseWithoutDefinition() { |
+ bool found = false; |
+ BitVector::Iterator iterator(live_in_sets()[0]); |
+ while (!iterator.Done()) { |
+ found = true; |
+ int operand_index = iterator.Current(); |
+ PrintF("Register allocator error: live v%d reached first block.\n", |
+ operand_index); |
+ LiveRange* range = LiveRangeFor(operand_index); |
+ PrintF(" (first use is at %d)\n", range->first_pos()->pos().Value()); |
+ if (debug_name() == nullptr) { |
+ PrintF("\n"); |
+ } else { |
+ PrintF(" (function: %s)\n", debug_name()); |
} |
+ iterator.Advance(); |
} |
+ return found; |
} |
-SpillRange* RegisterAllocator::AssignSpillRangeToLiveRange(LiveRange* range) { |
- auto spill_range = new (local_zone()) SpillRange(range, local_zone()); |
+SpillRange* RegisterAllocationData::AssignSpillRangeToLiveRange( |
+ LiveRange* range) { |
+ auto spill_range = |
+ new (allocation_zone()) SpillRange(range, allocation_zone()); |
spill_ranges().push_back(spill_range); |
return spill_range; |
} |
-bool RegisterAllocator::TryReuseSpillForPhi(LiveRange* range) { |
- if (range->IsChild() || !range->is_phi()) return false; |
- DCHECK(!range->HasSpillOperand()); |
+void RegisterAllocationData::SetLiveRangeAssignedRegister(LiveRange* range, |
+ int reg) { |
+ if (range->Kind() == DOUBLE_REGISTERS) { |
+ assigned_double_registers_->Add(reg); |
+ } else { |
+ DCHECK(range->Kind() == GENERAL_REGISTERS); |
+ assigned_registers_->Add(reg); |
+ } |
+ range->set_assigned_register(reg); |
+ auto assignment = range->GetAssignedOperand(); |
+ // TODO(dcarney): stop aliasing hint operands. |
+ range->ConvertUsesToOperand(assignment, nullptr); |
+ if (range->is_phi()) AssignPhiInput(range, assignment); |
+} |
- auto lookup = phi_map_.find(range->id()); |
- DCHECK(lookup != phi_map_.end()); |
- auto phi = lookup->second->phi; |
- auto block = lookup->second->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->GetSpillRange() == nullptr) 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->IsSpilled()) { |
- spilled_count++; |
- if (first_op == nullptr) { |
- first_op = op_range->TopLevel(); |
- } |
+ |
+LiveRangeBuilder::LiveRangeBuilder(RegisterAllocationData* data) |
+ : data_(data) {} |
+ |
+ |
+BitVector* LiveRangeBuilder::ComputeLiveOut(const InstructionBlock* block) { |
+ // Compute live out for the given block, except not including backward |
+ // successor edges. |
+ auto live_out = new (allocation_zone()) |
+ BitVector(code()->VirtualRegisterCount(), allocation_zone()); |
+ |
+ // Process all successor blocks. |
+ for (auto succ : block->successors()) { |
+ // Add values live on entry to the successor. Note the successor's |
+ // live_in will not be computed yet for backwards edges. |
+ auto live_in = live_in_sets()[succ.ToSize()]; |
+ if (live_in != nullptr) live_out->Union(*live_in); |
+ |
+ // All phi input operands corresponding to this successor edge are live |
+ // out from this block. |
+ auto successor = code()->InstructionBlockAt(succ); |
+ size_t index = successor->PredecessorIndexOf(block->rpo_number()); |
+ DCHECK(index < successor->PredecessorCount()); |
+ for (auto phi : successor->phis()) { |
+ live_out->Add(phi->operands()[index]); |
} |
} |
+ return live_out; |
+} |
- // Only continue if more than half of the operands are spilled. |
- if (spilled_count * 2 <= phi->operands().size()) { |
- return false; |
+ |
+void LiveRangeBuilder::AddInitialIntervals(const InstructionBlock* block, |
+ BitVector* live_out) { |
+ // Add an interval that includes the entire block to the live range for |
+ // each live_out value. |
+ auto start = LifetimePosition::GapFromInstructionIndex( |
+ block->first_instruction_index()); |
+ auto end = LifetimePosition::InstructionFromInstructionIndex( |
+ block->last_instruction_index()).NextStart(); |
+ BitVector::Iterator iterator(live_out); |
+ while (!iterator.Done()) { |
+ int operand_index = iterator.Current(); |
+ auto range = LiveRangeFor(operand_index); |
+ range->AddUseInterval(start, end, allocation_zone()); |
+ iterator.Advance(); |
} |
+} |
- // 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); |
- auto op_spill = op_range->GetSpillRange(); |
- if (op_spill != nullptr && |
- (op_spill == first_op_spill || first_op_spill->TryMerge(op_spill))) { |
- num_merged++; |
+ |
+Instruction* LiveRangeBuilder::GetLastInstruction( |
+ const InstructionBlock* block) { |
+ return code()->InstructionAt(block->last_instruction_index()); |
+} |
+ |
+ |
+int LiveRangeBuilder::FixedDoubleLiveRangeID(int index) { |
+ return -index - 1 - config()->num_general_registers(); |
+} |
+ |
+ |
+InstructionOperand* LiveRangeBuilder::AllocateFixed(UnallocatedOperand* operand, |
+ int pos, bool is_tagged) { |
+ TRACE("Allocating fixed reg for op %d\n", operand->virtual_register()); |
+ DCHECK(operand->HasFixedPolicy()); |
+ InstructionOperand allocated; |
+ if (operand->HasFixedSlotPolicy()) { |
+ allocated = AllocatedOperand(AllocatedOperand::STACK_SLOT, |
+ operand->fixed_slot_index()); |
+ } else if (operand->HasFixedRegisterPolicy()) { |
+ allocated = AllocatedOperand(AllocatedOperand::REGISTER, |
+ operand->fixed_register_index()); |
+ } else if (operand->HasFixedDoubleRegisterPolicy()) { |
+ allocated = AllocatedOperand(AllocatedOperand::DOUBLE_REGISTER, |
+ operand->fixed_register_index()); |
+ } else { |
+ UNREACHABLE(); |
+ } |
+ InstructionOperand::ReplaceWith(operand, &allocated); |
+ if (is_tagged) { |
+ TRACE("Fixed reg is tagged at %d\n", pos); |
+ auto instr = InstructionAt(pos); |
+ if (instr->HasReferenceMap()) { |
+ instr->reference_map()->RecordReference(*operand); |
} |
} |
+ return operand; |
+} |
- // 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; |
+ |
+LiveRange* LiveRangeBuilder::FixedLiveRangeFor(int index) { |
+ DCHECK(index < config()->num_general_registers()); |
+ auto result = fixed_live_ranges()[index]; |
+ if (result == nullptr) { |
+ result = data()->NewLiveRange(FixedLiveRangeID(index)); |
+ DCHECK(result->IsFixed()); |
+ result->set_kind(GENERAL_REGISTERS); |
+ data()->SetLiveRangeAssignedRegister(result, index); |
+ fixed_live_ranges()[index] = result; |
} |
+ return result; |
+} |
- // 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() |
- : AssignSpillRangeToLiveRange(range->TopLevel()); |
- CHECK(first_op_spill->TryMerge(spill_range)); |
- Spill(range); |
- return true; |
- } else if (pos->pos().Value() > range->Start().NextStart().Value()) { |
- auto spill_range = range->TopLevel()->HasSpillRange() |
- ? range->TopLevel()->GetSpillRange() |
- : AssignSpillRangeToLiveRange(range->TopLevel()); |
- CHECK(first_op_spill->TryMerge(spill_range)); |
- SpillBetween(range, range->Start(), pos->pos()); |
- DCHECK(UnhandledIsSorted()); |
- return true; |
+ |
+LiveRange* LiveRangeBuilder::FixedDoubleLiveRangeFor(int index) { |
+ DCHECK(index < config()->num_aliased_double_registers()); |
+ auto result = fixed_double_live_ranges()[index]; |
+ if (result == nullptr) { |
+ result = data()->NewLiveRange(FixedDoubleLiveRangeID(index)); |
+ DCHECK(result->IsFixed()); |
+ result->set_kind(DOUBLE_REGISTERS); |
+ data()->SetLiveRangeAssignedRegister(result, index); |
+ fixed_double_live_ranges()[index] = result; |
} |
- return false; |
+ return result; |
+} |
+ |
+ |
+LiveRange* LiveRangeBuilder::LiveRangeFor(InstructionOperand* operand) { |
+ if (operand->IsUnallocated()) { |
+ return LiveRangeFor(UnallocatedOperand::cast(operand)->virtual_register()); |
+ } else if (operand->IsConstant()) { |
+ return LiveRangeFor(ConstantOperand::cast(operand)->virtual_register()); |
+ } else if (operand->IsRegister()) { |
+ return FixedLiveRangeFor(RegisterOperand::cast(operand)->index()); |
+ } else if (operand->IsDoubleRegister()) { |
+ return FixedDoubleLiveRangeFor( |
+ DoubleRegisterOperand::cast(operand)->index()); |
+ } else { |
+ return nullptr; |
+ } |
+} |
+ |
+ |
+void LiveRangeBuilder::Define(LifetimePosition position, |
+ InstructionOperand* operand, |
+ InstructionOperand* hint) { |
+ auto range = LiveRangeFor(operand); |
+ if (range == nullptr) return; |
+ |
+ if (range->IsEmpty() || range->Start().Value() > position.Value()) { |
+ // Can happen if there is a definition without use. |
+ range->AddUseInterval(position, position.NextStart(), allocation_zone()); |
+ range->AddUsePosition(position.NextStart(), nullptr, nullptr, |
+ allocation_zone()); |
+ } else { |
+ range->ShortenTo(position); |
+ } |
+ |
+ if (operand->IsUnallocated()) { |
+ auto unalloc_operand = UnallocatedOperand::cast(operand); |
+ range->AddUsePosition(position, unalloc_operand, hint, allocation_zone()); |
+ } |
+} |
+ |
+ |
+void LiveRangeBuilder::Use(LifetimePosition block_start, |
+ LifetimePosition position, |
+ InstructionOperand* operand, |
+ InstructionOperand* hint) { |
+ auto range = LiveRangeFor(operand); |
+ if (range == nullptr) return; |
+ if (operand->IsUnallocated()) { |
+ UnallocatedOperand* unalloc_operand = UnallocatedOperand::cast(operand); |
+ range->AddUsePosition(position, unalloc_operand, hint, allocation_zone()); |
+ } |
+ range->AddUseInterval(block_start, position, allocation_zone()); |
} |
-void RegisterAllocator::MeetRegisterConstraints(const InstructionBlock* block) { |
+void LiveRangeBuilder::MeetRegisterConstraints(const InstructionBlock* block) { |
int start = block->first_instruction_index(); |
int end = block->last_instruction_index(); |
DCHECK_NE(-1, start); |
@@ -1068,7 +981,7 @@ void RegisterAllocator::MeetRegisterConstraints(const InstructionBlock* block) { |
} |
-void RegisterAllocator::MeetRegisterConstraintsForLastInstructionInBlock( |
+void LiveRangeBuilder::MeetRegisterConstraintsForLastInstructionInBlock( |
const InstructionBlock* block) { |
int end = block->last_instruction_index(); |
auto last_instruction = InstructionAt(end); |
@@ -1084,7 +997,7 @@ void RegisterAllocator::MeetRegisterConstraintsForLastInstructionInBlock( |
// This value is produced on the stack, we never need to spill it. |
if (output->IsStackSlot()) { |
DCHECK(StackSlotOperand::cast(output)->index() < |
- frame_->GetSpillSlotCount()); |
+ data()->frame()->GetSpillSlotCount()); |
range->SetSpillOperand(StackSlotOperand::cast(output)); |
range->SetSpillStartIndex(end); |
assigned = true; |
@@ -1097,7 +1010,7 @@ void RegisterAllocator::MeetRegisterConstraintsForLastInstructionInBlock( |
// Create an unconstrained operand for the same virtual register |
// and insert a gap move from the fixed output to the operand. |
UnallocatedOperand output_copy(UnallocatedOperand::ANY, output_vreg); |
- AddGapMove(gap_index, Instruction::START, *output, output_copy); |
+ data()->AddGapMove(gap_index, Instruction::START, *output, output_copy); |
} |
} |
@@ -1106,7 +1019,7 @@ void RegisterAllocator::MeetRegisterConstraintsForLastInstructionInBlock( |
const InstructionBlock* successor = code()->InstructionBlockAt(succ); |
DCHECK(successor->PredecessorCount() == 1); |
int gap_index = successor->first_instruction_index(); |
- range->SpillAtDefinition(local_zone(), gap_index, output); |
+ range->SpillAtDefinition(allocation_zone(), gap_index, output); |
range->SetSpillStartIndex(gap_index); |
} |
} |
@@ -1114,7 +1027,7 @@ void RegisterAllocator::MeetRegisterConstraintsForLastInstructionInBlock( |
} |
-void RegisterAllocator::MeetConstraintsAfter(int instr_index) { |
+void LiveRangeBuilder::MeetConstraintsAfter(int instr_index) { |
auto first = InstructionAt(instr_index); |
// Handle fixed temporaries. |
for (size_t i = 0; i < first->TempCount(); i++) { |
@@ -1137,31 +1050,32 @@ void RegisterAllocator::MeetConstraintsAfter(int instr_index) { |
if (first_output->HasFixedPolicy()) { |
int output_vreg = first_output->virtual_register(); |
UnallocatedOperand output_copy(UnallocatedOperand::ANY, output_vreg); |
- bool is_tagged = HasTaggedValue(output_vreg); |
+ bool is_tagged = IsReference(output_vreg); |
AllocateFixed(first_output, instr_index, is_tagged); |
// This value is produced on the stack, we never need to spill it. |
if (first_output->IsStackSlot()) { |
DCHECK(StackSlotOperand::cast(first_output)->index() < |
- frame_->GetSpillSlotCount()); |
+ data()->frame()->GetSpillSlotCount()); |
range->SetSpillOperand(StackSlotOperand::cast(first_output)); |
range->SetSpillStartIndex(instr_index + 1); |
assigned = true; |
} |
- AddGapMove(instr_index + 1, Instruction::START, *first_output, |
- output_copy); |
+ data()->AddGapMove(instr_index + 1, Instruction::START, *first_output, |
+ output_copy); |
} |
// Make sure we add a gap move for spilling (if we have not done |
// so already). |
if (!assigned) { |
- range->SpillAtDefinition(local_zone(), instr_index + 1, first_output); |
+ range->SpillAtDefinition(allocation_zone(), instr_index + 1, |
+ first_output); |
range->SetSpillStartIndex(instr_index + 1); |
} |
} |
} |
-void RegisterAllocator::MeetConstraintsBefore(int instr_index) { |
+void LiveRangeBuilder::MeetConstraintsBefore(int instr_index) { |
auto second = InstructionAt(instr_index); |
// Handle fixed input operands of second instruction. |
for (size_t i = 0; i < second->InputCount(); i++) { |
@@ -1171,9 +1085,9 @@ void RegisterAllocator::MeetConstraintsBefore(int instr_index) { |
if (cur_input->HasFixedPolicy()) { |
int input_vreg = cur_input->virtual_register(); |
UnallocatedOperand input_copy(UnallocatedOperand::ANY, input_vreg); |
- bool is_tagged = HasTaggedValue(input_vreg); |
+ bool is_tagged = IsReference(input_vreg); |
AllocateFixed(cur_input, instr_index, is_tagged); |
- AddGapMove(instr_index, Instruction::END, input_copy, *cur_input); |
+ data()->AddGapMove(instr_index, Instruction::END, input_copy, *cur_input); |
} |
} |
// Handle "output same as input" for second instruction. |
@@ -1189,12 +1103,12 @@ void RegisterAllocator::MeetConstraintsBefore(int instr_index) { |
int input_vreg = cur_input->virtual_register(); |
UnallocatedOperand input_copy(UnallocatedOperand::ANY, input_vreg); |
cur_input->set_virtual_register(second_output->virtual_register()); |
- AddGapMove(instr_index, Instruction::END, input_copy, *cur_input); |
- if (HasTaggedValue(input_vreg) && !HasTaggedValue(output_vreg)) { |
+ data()->AddGapMove(instr_index, Instruction::END, input_copy, *cur_input); |
+ if (IsReference(input_vreg) && !IsReference(output_vreg)) { |
if (second->HasReferenceMap()) { |
second->reference_map()->RecordReference(input_copy); |
} |
- } else if (!HasTaggedValue(input_vreg) && HasTaggedValue(output_vreg)) { |
+ } else if (!IsReference(input_vreg) && IsReference(output_vreg)) { |
// The input is assumed to immediately have a tagged representation, |
// before the pointer map can be used. I.e. the pointer map at the |
// instruction will include the output operand (whose value at the |
@@ -1206,7 +1120,7 @@ void RegisterAllocator::MeetConstraintsBefore(int instr_index) { |
} |
-bool RegisterAllocator::IsOutputRegisterOf(Instruction* instr, int index) { |
+bool LiveRangeBuilder::IsOutputRegisterOf(Instruction* instr, int index) { |
for (size_t i = 0; i < instr->OutputCount(); i++) { |
auto output = instr->OutputAt(i); |
if (output->IsRegister() && RegisterOperand::cast(output)->index() == index) |
@@ -1216,8 +1130,7 @@ bool RegisterAllocator::IsOutputRegisterOf(Instruction* instr, int index) { |
} |
-bool RegisterAllocator::IsOutputDoubleRegisterOf(Instruction* instr, |
- int index) { |
+bool LiveRangeBuilder::IsOutputDoubleRegisterOf(Instruction* instr, int index) { |
for (size_t i = 0; i < instr->OutputCount(); i++) { |
auto output = instr->OutputAt(i); |
if (output->IsDoubleRegister() && |
@@ -1228,8 +1141,8 @@ bool RegisterAllocator::IsOutputDoubleRegisterOf(Instruction* instr, |
} |
-void RegisterAllocator::ProcessInstructions(const InstructionBlock* block, |
- BitVector* live) { |
+void LiveRangeBuilder::ProcessInstructions(const InstructionBlock* block, |
+ BitVector* live) { |
int block_start = block->first_instruction_index(); |
auto block_start_position = |
LifetimePosition::GapFromInstructionIndex(block_start); |
@@ -1261,7 +1174,7 @@ void RegisterAllocator::ProcessInstructions(const InstructionBlock* block, |
if (!IsOutputRegisterOf(instr, i)) { |
auto range = FixedLiveRangeFor(i); |
range->AddUseInterval(curr_position, curr_position.End(), |
- local_zone()); |
+ allocation_zone()); |
} |
} |
} |
@@ -1271,7 +1184,7 @@ void RegisterAllocator::ProcessInstructions(const InstructionBlock* block, |
if (!IsOutputDoubleRegisterOf(instr, i)) { |
auto range = FixedDoubleLiveRangeFor(i); |
range->AddUseInterval(curr_position, curr_position.End(), |
- local_zone()); |
+ allocation_zone()); |
} |
} |
} |
@@ -1362,11 +1275,12 @@ void RegisterAllocator::ProcessInstructions(const InstructionBlock* block, |
} |
-void RegisterAllocator::ResolvePhis(const InstructionBlock* block) { |
+void LiveRangeBuilder::ResolvePhis(const InstructionBlock* block) { |
for (auto phi : block->phis()) { |
int phi_vreg = phi->virtual_register(); |
- auto map_value = new (local_zone()) PhiMapValue(phi, block, local_zone()); |
- auto res = phi_map_.insert(std::make_pair(phi_vreg, map_value)); |
+ auto map_value = new (allocation_zone()) |
+ RegisterAllocationData::PhiMapValue(phi, block, allocation_zone()); |
+ auto res = phi_map().insert(std::make_pair(phi_vreg, map_value)); |
DCHECK(res.second); |
USE(res); |
auto& output = phi->output(); |
@@ -1374,15 +1288,15 @@ void RegisterAllocator::ResolvePhis(const InstructionBlock* block) { |
InstructionBlock* cur_block = |
code()->InstructionBlockAt(block->predecessors()[i]); |
UnallocatedOperand input(UnallocatedOperand::ANY, phi->operands()[i]); |
- auto move = AddGapMove(cur_block->last_instruction_index(), |
- Instruction::END, input, output); |
+ auto move = data()->AddGapMove(cur_block->last_instruction_index(), |
+ Instruction::END, input, output); |
map_value->incoming_moves.push_back(move); |
DCHECK(!InstructionAt(cur_block->last_instruction_index()) |
->HasReferenceMap()); |
} |
auto live_range = LiveRangeFor(phi_vreg); |
int gap_index = block->first_instruction_index(); |
- live_range->SpillAtDefinition(local_zone(), gap_index, &output); |
+ live_range->SpillAtDefinition(allocation_zone(), gap_index, &output); |
live_range->SetSpillStartIndex(gap_index); |
// We use the phi-ness of some nodes in some later heuristics. |
live_range->set_is_phi(true); |
@@ -1391,25 +1305,14 @@ void RegisterAllocator::ResolvePhis(const InstructionBlock* block) { |
} |
-void RegisterAllocator::AssignPhiInput(LiveRange* range, |
- const InstructionOperand& assignment) { |
- DCHECK(range->is_phi()); |
- auto it = phi_map_.find(range->id()); |
- DCHECK(it != phi_map_.end()); |
- for (auto move : it->second->incoming_moves) { |
- move->set_destination(assignment); |
- } |
-} |
- |
- |
-void RegisterAllocator::MeetRegisterConstraints() { |
+void LiveRangeBuilder::MeetRegisterConstraints() { |
for (auto block : code()->instruction_blocks()) { |
MeetRegisterConstraints(block); |
} |
} |
-void RegisterAllocator::ResolvePhis() { |
+void LiveRangeBuilder::ResolvePhis() { |
// Process the blocks in reverse order. |
for (auto i = code()->instruction_blocks().rbegin(); |
i != code()->instruction_blocks().rend(); ++i) { |
@@ -1418,275 +1321,7 @@ void RegisterAllocator::ResolvePhis() { |
} |
-const InstructionBlock* RegisterAllocator::GetInstructionBlock( |
- LifetimePosition pos) { |
- return code()->GetInstructionBlock(pos.ToInstructionIndex()); |
-} |
- |
- |
-void RegisterAllocator::ConnectRanges() { |
- ZoneMap<std::pair<ParallelMove*, InstructionOperand>, InstructionOperand> |
- delayed_insertion_map(local_zone()); |
- for (auto first_range : live_ranges()) { |
- if (first_range == nullptr || first_range->IsChild()) continue; |
- for (auto second_range = first_range->next(); second_range != nullptr; |
- first_range = second_range, second_range = second_range->next()) { |
- auto pos = second_range->Start(); |
- // Add gap move if the two live ranges touch and there is no block |
- // boundary. |
- if (second_range->IsSpilled()) continue; |
- if (first_range->End().Value() != pos.Value()) continue; |
- if (IsBlockBoundary(pos) && |
- !CanEagerlyResolveControlFlow(GetInstructionBlock(pos))) { |
- continue; |
- } |
- auto prev_operand = first_range->GetAssignedOperand(); |
- auto cur_operand = second_range->GetAssignedOperand(); |
- if (prev_operand == cur_operand) continue; |
- bool delay_insertion = false; |
- Instruction::GapPosition gap_pos; |
- int gap_index = pos.ToInstructionIndex(); |
- if (pos.IsGapPosition()) { |
- gap_pos = pos.IsStart() ? Instruction::START : Instruction::END; |
- } else { |
- if (pos.IsStart()) { |
- delay_insertion = true; |
- } else { |
- gap_index++; |
- } |
- gap_pos = delay_insertion ? Instruction::END : Instruction::START; |
- } |
- auto move = code()->InstructionAt(gap_index)->GetOrCreateParallelMove( |
- gap_pos, code_zone()); |
- if (!delay_insertion) { |
- move->AddMove(prev_operand, cur_operand); |
- } else { |
- delayed_insertion_map.insert( |
- std::make_pair(std::make_pair(move, prev_operand), cur_operand)); |
- } |
- } |
- } |
- if (delayed_insertion_map.empty()) return; |
- // Insert all the moves which should occur after the stored move. |
- ZoneVector<MoveOperands*> to_insert(local_zone()); |
- ZoneVector<MoveOperands*> to_eliminate(local_zone()); |
- to_insert.reserve(4); |
- to_eliminate.reserve(4); |
- auto moves = delayed_insertion_map.begin()->first.first; |
- for (auto it = delayed_insertion_map.begin();; ++it) { |
- bool done = it == delayed_insertion_map.end(); |
- if (done || it->first.first != moves) { |
- // Commit the MoveOperands for current ParallelMove. |
- for (auto move : to_eliminate) { |
- move->Eliminate(); |
- } |
- for (auto move : to_insert) { |
- moves->push_back(move); |
- } |
- if (done) break; |
- // Reset state. |
- to_eliminate.clear(); |
- to_insert.clear(); |
- moves = it->first.first; |
- } |
- // Gather all MoveOperands for a single ParallelMove. |
- auto move = new (code_zone()) MoveOperands(it->first.second, it->second); |
- auto eliminate = moves->PrepareInsertAfter(move); |
- to_insert.push_back(move); |
- if (eliminate != nullptr) to_eliminate.push_back(eliminate); |
- } |
-} |
- |
- |
-bool RegisterAllocator::CanEagerlyResolveControlFlow( |
- const InstructionBlock* block) const { |
- if (block->PredecessorCount() != 1) return false; |
- return block->predecessors()[0].IsNext(block->rpo_number()); |
-} |
- |
- |
-namespace { |
- |
-class LiveRangeBound { |
- public: |
- explicit LiveRangeBound(const LiveRange* range) |
- : range_(range), start_(range->Start()), end_(range->End()) { |
- DCHECK(!range->IsEmpty()); |
- } |
- |
- bool CanCover(LifetimePosition position) { |
- return start_.Value() <= position.Value() && |
- position.Value() < end_.Value(); |
- } |
- |
- const LiveRange* const range_; |
- const LifetimePosition start_; |
- const LifetimePosition end_; |
- |
- private: |
- DISALLOW_COPY_AND_ASSIGN(LiveRangeBound); |
-}; |
- |
- |
-struct FindResult { |
- const LiveRange* cur_cover_; |
- const LiveRange* pred_cover_; |
-}; |
- |
- |
-class LiveRangeBoundArray { |
- public: |
- LiveRangeBoundArray() : length_(0), start_(nullptr) {} |
- |
- bool ShouldInitialize() { return start_ == nullptr; } |
- |
- void Initialize(Zone* zone, const LiveRange* const range) { |
- size_t length = 0; |
- for (auto i = range; i != nullptr; i = i->next()) length++; |
- start_ = zone->NewArray<LiveRangeBound>(length); |
- length_ = length; |
- auto curr = start_; |
- for (auto i = range; i != nullptr; i = i->next(), ++curr) { |
- new (curr) LiveRangeBound(i); |
- } |
- } |
- |
- LiveRangeBound* Find(const LifetimePosition position) const { |
- size_t left_index = 0; |
- size_t right_index = length_; |
- while (true) { |
- size_t current_index = left_index + (right_index - left_index) / 2; |
- DCHECK(right_index > current_index); |
- auto bound = &start_[current_index]; |
- if (bound->start_.Value() <= position.Value()) { |
- if (position.Value() < bound->end_.Value()) return bound; |
- DCHECK(left_index < current_index); |
- left_index = current_index; |
- } else { |
- right_index = current_index; |
- } |
- } |
- } |
- |
- LiveRangeBound* FindPred(const InstructionBlock* pred) { |
- auto pred_end = LifetimePosition::InstructionFromInstructionIndex( |
- pred->last_instruction_index()); |
- return Find(pred_end); |
- } |
- |
- LiveRangeBound* FindSucc(const InstructionBlock* succ) { |
- auto succ_start = LifetimePosition::GapFromInstructionIndex( |
- succ->first_instruction_index()); |
- return Find(succ_start); |
- } |
- |
- void Find(const InstructionBlock* block, const InstructionBlock* pred, |
- FindResult* result) const { |
- auto pred_end = LifetimePosition::InstructionFromInstructionIndex( |
- pred->last_instruction_index()); |
- auto bound = Find(pred_end); |
- result->pred_cover_ = bound->range_; |
- auto cur_start = LifetimePosition::GapFromInstructionIndex( |
- block->first_instruction_index()); |
- // Common case. |
- if (bound->CanCover(cur_start)) { |
- result->cur_cover_ = bound->range_; |
- return; |
- } |
- result->cur_cover_ = Find(cur_start)->range_; |
- DCHECK(result->pred_cover_ != nullptr && result->cur_cover_ != nullptr); |
- } |
- |
- private: |
- size_t length_; |
- LiveRangeBound* start_; |
- |
- DISALLOW_COPY_AND_ASSIGN(LiveRangeBoundArray); |
-}; |
- |
- |
-class LiveRangeFinder { |
- public: |
- explicit LiveRangeFinder(const RegisterAllocator& allocator) |
- : allocator_(allocator), |
- bounds_length_(static_cast<int>(allocator.live_ranges().size())), |
- bounds_(allocator.local_zone()->NewArray<LiveRangeBoundArray>( |
- bounds_length_)) { |
- for (int i = 0; i < bounds_length_; ++i) { |
- new (&bounds_[i]) LiveRangeBoundArray(); |
- } |
- } |
- |
- LiveRangeBoundArray* ArrayFor(int operand_index) { |
- DCHECK(operand_index < bounds_length_); |
- auto range = allocator_.live_ranges()[operand_index]; |
- DCHECK(range != nullptr && !range->IsEmpty()); |
- auto array = &bounds_[operand_index]; |
- if (array->ShouldInitialize()) { |
- array->Initialize(allocator_.local_zone(), range); |
- } |
- return array; |
- } |
- |
- private: |
- const RegisterAllocator& allocator_; |
- const int bounds_length_; |
- LiveRangeBoundArray* const bounds_; |
- |
- DISALLOW_COPY_AND_ASSIGN(LiveRangeFinder); |
-}; |
- |
-} // namespace |
- |
- |
-void RegisterAllocator::ResolveControlFlow() { |
- // Lazily linearize live ranges in memory for fast lookup. |
- LiveRangeFinder finder(*this); |
- for (auto block : code()->instruction_blocks()) { |
- if (CanEagerlyResolveControlFlow(block)) continue; |
- auto live = live_in_sets_[block->rpo_number().ToInt()]; |
- BitVector::Iterator iterator(live); |
- while (!iterator.Done()) { |
- auto* array = finder.ArrayFor(iterator.Current()); |
- for (auto pred : block->predecessors()) { |
- FindResult result; |
- const auto* pred_block = code()->InstructionBlockAt(pred); |
- array->Find(block, pred_block, &result); |
- if (result.cur_cover_ == result.pred_cover_ || |
- result.cur_cover_->IsSpilled()) |
- continue; |
- auto pred_op = result.pred_cover_->GetAssignedOperand(); |
- auto cur_op = result.cur_cover_->GetAssignedOperand(); |
- if (pred_op == cur_op) continue; |
- ResolveControlFlow(block, cur_op, pred_block, pred_op); |
- } |
- iterator.Advance(); |
- } |
- } |
-} |
- |
- |
-void RegisterAllocator::ResolveControlFlow(const InstructionBlock* block, |
- const InstructionOperand& cur_op, |
- const InstructionBlock* pred, |
- const InstructionOperand& pred_op) { |
- DCHECK(pred_op != cur_op); |
- int gap_index; |
- Instruction::GapPosition position; |
- if (block->PredecessorCount() == 1) { |
- gap_index = block->first_instruction_index(); |
- position = Instruction::START; |
- } else { |
- DCHECK(pred->SuccessorCount() == 1); |
- DCHECK(!InstructionAt(pred->last_instruction_index())->HasReferenceMap()); |
- gap_index = pred->last_instruction_index(); |
- position = Instruction::END; |
- } |
- AddGapMove(gap_index, position, pred_op, cur_op); |
-} |
- |
- |
-void RegisterAllocator::BuildLiveRanges() { |
+void LiveRangeBuilder::BuildLiveRanges() { |
// Process the blocks in reverse order. |
for (int block_id = code()->InstructionBlockCount() - 1; block_id >= 0; |
--block_id) { |
@@ -1724,7 +1359,7 @@ void RegisterAllocator::BuildLiveRanges() { |
// Now live is live_in for this block except not including values live |
// out on backward successor edges. |
- live_in_sets_[block_id] = live; |
+ live_in_sets()[block_id] = live; |
if (block->IsLoopHeader()) { |
// Add a live range stretching from the first loop instruction to the last |
@@ -1737,23 +1372,23 @@ void RegisterAllocator::BuildLiveRanges() { |
while (!iterator.Done()) { |
int operand_index = iterator.Current(); |
auto range = LiveRangeFor(operand_index); |
- range->EnsureInterval(start, end, local_zone()); |
+ range->EnsureInterval(start, end, allocation_zone()); |
iterator.Advance(); |
} |
// Insert all values into the live in sets of all blocks in the loop. |
for (int i = block->rpo_number().ToInt() + 1; |
i < block->loop_end().ToInt(); ++i) { |
- live_in_sets_[i]->Union(*live); |
+ live_in_sets()[i]->Union(*live); |
} |
} |
} |
for (auto range : live_ranges()) { |
if (range == nullptr) continue; |
- range->kind_ = RequiredRegisterKind(range->id()); |
+ range->set_kind(RequiredRegisterKind(range->id())); |
// Give slots to all ranges with a non fixed slot use. |
if (range->has_slot_use() && range->HasNoSpillType()) { |
- AssignSpillRangeToLiveRange(range); |
+ data()->AssignSpillRangeToLiveRange(range); |
} |
// TODO(bmeurer): This is a horrible hack to make sure that for constant |
// live ranges, every use requires the constant to be in a register. |
@@ -1771,136 +1406,49 @@ void RegisterAllocator::BuildLiveRanges() { |
} |
} |
} |
+ |
+#ifdef DEBUG |
+ Verify(); |
+#endif |
} |
-bool RegisterAllocator::ExistsUseWithoutDefinition() { |
- bool found = false; |
- BitVector::Iterator iterator(live_in_sets_[0]); |
- while (!iterator.Done()) { |
- found = true; |
- int operand_index = iterator.Current(); |
- PrintF("Register allocator error: live v%d reached first block.\n", |
- operand_index); |
- LiveRange* range = LiveRangeFor(operand_index); |
- PrintF(" (first use is at %d)\n", range->first_pos()->pos().Value()); |
- if (debug_name() == nullptr) { |
- PrintF("\n"); |
- } else { |
- PrintF(" (function: %s)\n", debug_name()); |
- } |
- iterator.Advance(); |
- } |
- return found; |
-} |
- |
- |
-bool RegisterAllocator::SafePointsAreInOrder() const { |
- int safe_point = 0; |
- for (auto map : *code()->reference_maps()) { |
- if (safe_point > map->instruction_position()) return false; |
- safe_point = map->instruction_position(); |
- } |
- return true; |
+RegisterKind LiveRangeBuilder::RequiredRegisterKind( |
+ int virtual_register) const { |
+ return (code()->IsDouble(virtual_register)) ? DOUBLE_REGISTERS |
+ : GENERAL_REGISTERS; |
} |
-void RegisterAllocator::PopulateReferenceMaps() { |
- DCHECK(SafePointsAreInOrder()); |
- |
- // Iterate over all safe point positions and record a pointer |
- // for all spilled live ranges at this point. |
- int last_range_start = 0; |
- auto reference_maps = code()->reference_maps(); |
- ReferenceMapDeque::const_iterator first_it = reference_maps->begin(); |
- for (LiveRange* range : live_ranges()) { |
- if (range == nullptr) continue; |
- // Iterate over the first parts of multi-part live ranges. |
- if (range->IsChild()) continue; |
- // Skip non-reference values. |
- if (!HasTaggedValue(range->id())) continue; |
- // Skip empty live ranges. |
- if (range->IsEmpty()) continue; |
- |
- // Find the extent of the range and its children. |
- int start = range->Start().ToInstructionIndex(); |
- int end = 0; |
- for (auto cur = range; cur != nullptr; cur = cur->next()) { |
- auto this_end = cur->End(); |
- if (this_end.ToInstructionIndex() > end) |
- end = this_end.ToInstructionIndex(); |
- DCHECK(cur->Start().ToInstructionIndex() >= start); |
- } |
- |
- // Most of the ranges are in order, but not all. Keep an eye on when they |
- // step backwards and reset the first_it so we don't miss any safe points. |
- if (start < last_range_start) first_it = reference_maps->begin(); |
- last_range_start = start; |
- |
- // Step across all the safe points that are before the start of this range, |
- // recording how far we step in order to save doing this for the next range. |
- for (; first_it != reference_maps->end(); ++first_it) { |
- auto map = *first_it; |
- if (map->instruction_position() >= start) break; |
- } |
- |
- // Step through the safe points to see whether they are in the range. |
- for (auto it = first_it; it != reference_maps->end(); ++it) { |
- auto map = *it; |
- int safe_point = map->instruction_position(); |
- |
- // The safe points are sorted so we can stop searching here. |
- if (safe_point - 1 > end) break; |
- |
- // Advance to the next active range that covers the current |
- // safe point position. |
- auto safe_point_pos = |
- LifetimePosition::InstructionFromInstructionIndex(safe_point); |
- auto cur = range; |
- while (cur != nullptr && !cur->Covers(safe_point_pos)) { |
- cur = cur->next(); |
- } |
- if (cur == nullptr) continue; |
- |
- // Check if the live range is spilled and the safe point is after |
- // the spill position. |
- if (range->HasSpillOperand() && |
- safe_point >= range->spill_start_index() && |
- !range->GetSpillOperand()->IsConstant()) { |
- TRACE("Pointer for range %d (spilled at %d) at safe point %d\n", |
- range->id(), range->spill_start_index(), safe_point); |
- map->RecordReference(*range->GetSpillOperand()); |
- } |
- |
- if (!cur->IsSpilled()) { |
- TRACE( |
- "Pointer in register for range %d (start at %d) " |
- "at safe point %d\n", |
- cur->id(), cur->Start().Value(), safe_point); |
- auto operand = cur->GetAssignedOperand(); |
- DCHECK(!operand.IsStackSlot()); |
- map->RecordReference(operand); |
- } |
- } |
+void LiveRangeBuilder::Verify() const { |
+ for (auto current : data()->live_ranges()) { |
+ if (current != nullptr) current->Verify(); |
} |
} |
-void RegisterAllocator::AllocateGeneralRegisters() { |
- num_registers_ = config()->num_general_registers(); |
- mode_ = GENERAL_REGISTERS; |
- AllocateRegisters(); |
-} |
- |
- |
-void RegisterAllocator::AllocateDoubleRegisters() { |
- num_registers_ = config()->num_aliased_double_registers(); |
- mode_ = DOUBLE_REGISTERS; |
- AllocateRegisters(); |
+LinearScanAllocator::LinearScanAllocator(RegisterAllocationData* data, |
+ RegisterKind kind) |
+ : data_(data), |
+ mode_(kind), |
+ num_registers_(kind == DOUBLE_REGISTERS |
+ ? config()->num_aliased_double_registers() |
+ : config()->num_general_registers()), |
+ unhandled_live_ranges_(allocation_zone()), |
+ active_live_ranges_(allocation_zone()), |
+ inactive_live_ranges_(allocation_zone()) { |
+ unhandled_live_ranges().reserve( |
+ static_cast<size_t>(code()->VirtualRegisterCount() * 2)); |
+ active_live_ranges().reserve(8); |
+ inactive_live_ranges().reserve(8); |
+ // TryAllocateFreeReg and AllocateBlockedReg assume this |
+ // when allocating local arrays. |
+ DCHECK(RegisterConfiguration::kMaxDoubleRegisters >= |
+ config()->num_general_registers()); |
} |
-void RegisterAllocator::AllocateRegisters() { |
+void LinearScanAllocator::AllocateRegisters() { |
DCHECK(unhandled_live_ranges().empty()); |
for (auto range : live_ranges()) { |
@@ -1916,18 +1464,13 @@ void RegisterAllocator::AllocateRegisters() { |
DCHECK(inactive_live_ranges().empty()); |
if (mode_ == DOUBLE_REGISTERS) { |
- for (int i = 0; i < config()->num_aliased_double_registers(); ++i) { |
- auto current = fixed_double_live_ranges()[i]; |
- if (current != nullptr) { |
- AddToInactive(current); |
- } |
+ for (auto current : fixed_double_live_ranges()) { |
+ if (current != nullptr) AddToInactive(current); |
} |
} else { |
DCHECK(mode_ == GENERAL_REGISTERS); |
for (auto current : fixed_live_ranges()) { |
- if (current != nullptr) { |
- AddToInactive(current); |
- } |
+ if (current != nullptr) AddToInactive(current); |
} |
} |
@@ -2001,7 +1544,7 @@ void RegisterAllocator::AllocateRegisters() { |
} |
-const char* RegisterAllocator::RegisterName(int allocation_index) { |
+const char* LinearScanAllocator::RegisterName(int allocation_index) { |
if (mode_ == GENERAL_REGISTERS) { |
return config()->general_register_name(allocation_index); |
} else { |
@@ -2010,31 +1553,19 @@ const char* RegisterAllocator::RegisterName(int allocation_index) { |
} |
-bool RegisterAllocator::HasTaggedValue(int virtual_register) const { |
- return code()->IsReference(virtual_register); |
-} |
- |
- |
-RegisterKind RegisterAllocator::RequiredRegisterKind( |
- int virtual_register) const { |
- return (code()->IsDouble(virtual_register)) ? DOUBLE_REGISTERS |
- : GENERAL_REGISTERS; |
-} |
- |
- |
-void RegisterAllocator::AddToActive(LiveRange* range) { |
+void LinearScanAllocator::AddToActive(LiveRange* range) { |
TRACE("Add live range %d to active\n", range->id()); |
active_live_ranges().push_back(range); |
} |
-void RegisterAllocator::AddToInactive(LiveRange* range) { |
+void LinearScanAllocator::AddToInactive(LiveRange* range) { |
TRACE("Add live range %d to inactive\n", range->id()); |
inactive_live_ranges().push_back(range); |
} |
-void RegisterAllocator::AddToUnhandledSorted(LiveRange* range) { |
+void LinearScanAllocator::AddToUnhandledSorted(LiveRange* range) { |
if (range == nullptr || range->IsEmpty()) return; |
DCHECK(!range->HasRegisterAssigned() && !range->IsSpilled()); |
DCHECK(allocation_finger_.Value() <= range->Start().Value()); |
@@ -2054,7 +1585,7 @@ void RegisterAllocator::AddToUnhandledSorted(LiveRange* range) { |
} |
-void RegisterAllocator::AddToUnhandledUnsorted(LiveRange* range) { |
+void LinearScanAllocator::AddToUnhandledUnsorted(LiveRange* range) { |
if (range == nullptr || range->IsEmpty()) return; |
DCHECK(!range->HasRegisterAssigned() && !range->IsSpilled()); |
TRACE("Add live range %d to unhandled unsorted at end\n", range->id()); |
@@ -2073,14 +1604,14 @@ static bool UnhandledSortHelper(LiveRange* a, LiveRange* b) { |
// Sort the unhandled live ranges so that the ranges to be processed first are |
// at the end of the array list. This is convenient for the register allocation |
// algorithm because it is efficient to remove elements from the end. |
-void RegisterAllocator::SortUnhandled() { |
+void LinearScanAllocator::SortUnhandled() { |
TRACE("Sort unhandled\n"); |
std::sort(unhandled_live_ranges().begin(), unhandled_live_ranges().end(), |
&UnhandledSortHelper); |
} |
-bool RegisterAllocator::UnhandledIsSorted() { |
+bool LinearScanAllocator::UnhandledIsSorted() { |
size_t len = unhandled_live_ranges().size(); |
for (size_t i = 1; i < len; i++) { |
auto a = unhandled_live_ranges().at(i - 1); |
@@ -2091,33 +1622,33 @@ bool RegisterAllocator::UnhandledIsSorted() { |
} |
-void RegisterAllocator::ActiveToHandled(LiveRange* range) { |
+void LinearScanAllocator::ActiveToHandled(LiveRange* range) { |
RemoveElement(&active_live_ranges(), range); |
TRACE("Moving live range %d from active to handled\n", range->id()); |
} |
-void RegisterAllocator::ActiveToInactive(LiveRange* range) { |
+void LinearScanAllocator::ActiveToInactive(LiveRange* range) { |
RemoveElement(&active_live_ranges(), range); |
inactive_live_ranges().push_back(range); |
TRACE("Moving live range %d from active to inactive\n", range->id()); |
} |
-void RegisterAllocator::InactiveToHandled(LiveRange* range) { |
+void LinearScanAllocator::InactiveToHandled(LiveRange* range) { |
RemoveElement(&inactive_live_ranges(), range); |
TRACE("Moving live range %d from inactive to handled\n", range->id()); |
} |
-void RegisterAllocator::InactiveToActive(LiveRange* range) { |
+void LinearScanAllocator::InactiveToActive(LiveRange* range) { |
RemoveElement(&inactive_live_ranges(), range); |
active_live_ranges().push_back(range); |
TRACE("Moving live range %d from inactive to active\n", range->id()); |
} |
-bool RegisterAllocator::TryAllocateFreeReg(LiveRange* current) { |
+bool LinearScanAllocator::TryAllocateFreeReg(LiveRange* current) { |
LifetimePosition free_until_pos[RegisterConfiguration::kMaxDoubleRegisters]; |
for (int i = 0; i < num_registers_; i++) { |
@@ -2186,7 +1717,7 @@ bool RegisterAllocator::TryAllocateFreeReg(LiveRange* current) { |
} |
-void RegisterAllocator::AllocateBlockedReg(LiveRange* current) { |
+void LinearScanAllocator::AllocateBlockedReg(LiveRange* current) { |
auto register_use = current->NextRegisterPosition(current->Start()); |
if (register_use == nullptr) { |
// There is no use in the current live range that requires a register. |
@@ -2276,7 +1807,7 @@ static const InstructionBlock* GetContainingLoop( |
} |
-LifetimePosition RegisterAllocator::FindOptimalSpillingPos( |
+LifetimePosition LinearScanAllocator::FindOptimalSpillingPos( |
LiveRange* range, LifetimePosition pos) { |
auto block = GetInstructionBlock(pos.Start()); |
auto loop_header = |
@@ -2308,7 +1839,7 @@ LifetimePosition RegisterAllocator::FindOptimalSpillingPos( |
} |
-void RegisterAllocator::SplitAndSpillIntersecting(LiveRange* current) { |
+void LinearScanAllocator::SplitAndSpillIntersecting(LiveRange* current) { |
DCHECK(current->HasRegisterAssigned()); |
int reg = current->assigned_register(); |
auto split_pos = current->Start(); |
@@ -2356,15 +1887,94 @@ void RegisterAllocator::SplitAndSpillIntersecting(LiveRange* current) { |
} |
-bool RegisterAllocator::IsBlockBoundary(LifetimePosition pos) { |
- return pos.IsFullStart() && |
- code()->GetInstructionBlock(pos.ToInstructionIndex())->code_start() == |
- pos.ToInstructionIndex(); |
+bool LinearScanAllocator::TryReuseSpillForPhi(LiveRange* range) { |
+ if (range->IsChild() || !range->is_phi()) return false; |
+ DCHECK(!range->HasSpillOperand()); |
+ |
+ auto lookup = phi_map().find(range->id()); |
+ DCHECK(lookup != phi_map().end()); |
+ auto phi = lookup->second->phi; |
+ auto block = lookup->second->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->GetSpillRange() == nullptr) 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->IsSpilled()) { |
+ 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); |
+ auto op_spill = op_range->GetSpillRange(); |
+ if (op_spill != nullptr && |
+ (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().Value() > range->Start().NextStart().Value()) { |
+ auto spill_range = |
+ range->TopLevel()->HasSpillRange() |
+ ? range->TopLevel()->GetSpillRange() |
+ : data()->AssignSpillRangeToLiveRange(range->TopLevel()); |
+ bool merged = first_op_spill->TryMerge(spill_range); |
+ CHECK(merged); |
+ SpillBetween(range, range->Start(), pos->pos()); |
+ DCHECK(UnhandledIsSorted()); |
+ return true; |
+ } |
+ return false; |
} |
-LiveRange* RegisterAllocator::SplitRangeAt(LiveRange* range, |
- LifetimePosition pos) { |
+LiveRange* LinearScanAllocator::SplitRangeAt(LiveRange* range, |
+ LifetimePosition pos) { |
DCHECK(!range->IsFixed()); |
TRACE("Splitting live range %d at %d\n", range->id(), pos.Value()); |
@@ -2378,14 +1988,14 @@ LiveRange* RegisterAllocator::SplitRangeAt(LiveRange* range, |
int vreg = GetVirtualRegister(); |
auto result = LiveRangeFor(vreg); |
- range->SplitAt(pos, result, local_zone()); |
+ range->SplitAt(pos, result, allocation_zone()); |
return result; |
} |
-LiveRange* RegisterAllocator::SplitBetween(LiveRange* range, |
- LifetimePosition start, |
- LifetimePosition end) { |
+LiveRange* LinearScanAllocator::SplitBetween(LiveRange* range, |
+ LifetimePosition start, |
+ LifetimePosition end) { |
DCHECK(!range->IsFixed()); |
TRACE("Splitting live range %d in position between [%d, %d]\n", range->id(), |
start.Value(), end.Value()); |
@@ -2396,8 +2006,8 @@ LiveRange* RegisterAllocator::SplitBetween(LiveRange* range, |
} |
-LifetimePosition RegisterAllocator::FindOptimalSplitPos(LifetimePosition start, |
- LifetimePosition end) { |
+LifetimePosition LinearScanAllocator::FindOptimalSplitPos( |
+ LifetimePosition start, LifetimePosition end) { |
int start_instr = start.ToInstructionIndex(); |
int end_instr = end.ToInstructionIndex(); |
DCHECK(start_instr <= end_instr); |
@@ -2432,22 +2042,22 @@ LifetimePosition RegisterAllocator::FindOptimalSplitPos(LifetimePosition start, |
} |
-void RegisterAllocator::SpillAfter(LiveRange* range, LifetimePosition pos) { |
+void LinearScanAllocator::SpillAfter(LiveRange* range, LifetimePosition pos) { |
auto second_part = SplitRangeAt(range, pos); |
Spill(second_part); |
} |
-void RegisterAllocator::SpillBetween(LiveRange* range, LifetimePosition start, |
- LifetimePosition end) { |
+void LinearScanAllocator::SpillBetween(LiveRange* range, LifetimePosition start, |
+ LifetimePosition end) { |
SpillBetweenUntil(range, start, start, end); |
} |
-void RegisterAllocator::SpillBetweenUntil(LiveRange* range, |
- LifetimePosition start, |
- LifetimePosition until, |
- LifetimePosition end) { |
+void LinearScanAllocator::SpillBetweenUntil(LiveRange* range, |
+ LifetimePosition start, |
+ LifetimePosition until, |
+ LifetimePosition end) { |
CHECK(start.Value() < end.Value()); |
auto second_part = SplitRangeAt(range, start); |
@@ -2456,7 +2066,7 @@ void RegisterAllocator::SpillBetweenUntil(LiveRange* range, |
// Split it at position between ]start+1, end[, spill the middle part |
// and put the rest to unhandled. |
auto third_part_end = end.PrevStart().End(); |
- if (IsBlockBoundary(end.Start())) { |
+ if (data()->IsBlockBoundary(end.Start())) { |
third_part_end = end.Start(); |
} |
auto third_part = SplitBetween( |
@@ -2474,46 +2084,427 @@ void RegisterAllocator::SpillBetweenUntil(LiveRange* range, |
} |
-void RegisterAllocator::Spill(LiveRange* range) { |
+void LinearScanAllocator::Spill(LiveRange* range) { |
DCHECK(!range->IsSpilled()); |
TRACE("Spilling live range %d\n", range->id()); |
auto first = range->TopLevel(); |
if (first->HasNoSpillType()) { |
- AssignSpillRangeToLiveRange(first); |
+ data()->AssignSpillRangeToLiveRange(first); |
} |
range->MakeSpilled(); |
} |
-int RegisterAllocator::RegisterCount() const { return num_registers_; } |
+OperandAssigner::OperandAssigner(RegisterAllocationData* data) : data_(data) {} |
-#ifdef DEBUG |
+void OperandAssigner::AssignSpillSlots() { |
+ auto& spill_ranges = data()->spill_ranges(); |
+ // Merge disjoint spill ranges |
+ for (size_t i = 0; i < spill_ranges.size(); i++) { |
+ auto range = spill_ranges[i]; |
+ if (range->IsEmpty()) continue; |
+ for (size_t j = i + 1; j < spill_ranges.size(); j++) { |
+ auto other = spill_ranges[j]; |
+ if (!other->IsEmpty()) { |
+ range->TryMerge(other); |
+ } |
+ } |
+ } |
+ // Allocate slots for the merged spill ranges. |
+ for (auto range : spill_ranges) { |
+ if (range->IsEmpty()) continue; |
+ // Allocate a new operand referring to the spill slot. |
+ auto kind = range->Kind(); |
+ int index = data()->frame()->AllocateSpillSlot(kind == DOUBLE_REGISTERS); |
+ auto op_kind = kind == DOUBLE_REGISTERS |
+ ? AllocatedOperand::DOUBLE_STACK_SLOT |
+ : AllocatedOperand::STACK_SLOT; |
+ auto op = AllocatedOperand::New(data()->code_zone(), op_kind, index); |
+ range->SetOperand(op); |
+ } |
+} |
-void RegisterAllocator::Verify() const { |
- for (auto current : live_ranges()) { |
- if (current != nullptr) current->Verify(); |
+void OperandAssigner::CommitAssignment() { |
+ for (auto range : data()->live_ranges()) { |
+ if (range == nullptr || range->IsEmpty()) continue; |
+ InstructionOperand* spill_operand = nullptr; |
+ if (!range->TopLevel()->HasNoSpillType()) { |
+ spill_operand = range->TopLevel()->GetSpillOperand(); |
+ } |
+ auto assigned = range->GetAssignedOperand(); |
+ range->ConvertUsesToOperand(assigned, spill_operand); |
+ if (range->is_phi()) data()->AssignPhiInput(range, assigned); |
+ if (!range->IsChild() && spill_operand != nullptr) { |
+ range->CommitSpillsAtDefinition(data()->code(), spill_operand, |
+ range->has_slot_use()); |
+ } |
} |
} |
-#endif |
+ReferenceMapPopulator::ReferenceMapPopulator(RegisterAllocationData* data) |
+ : data_(data) {} |
-void RegisterAllocator::SetLiveRangeAssignedRegister(LiveRange* range, |
- int reg) { |
- if (range->Kind() == DOUBLE_REGISTERS) { |
- assigned_double_registers_->Add(reg); |
- } else { |
- DCHECK(range->Kind() == GENERAL_REGISTERS); |
- assigned_registers_->Add(reg); |
+bool ReferenceMapPopulator::SafePointsAreInOrder() const { |
+ int safe_point = 0; |
+ for (auto map : *data()->code()->reference_maps()) { |
+ if (safe_point > map->instruction_position()) return false; |
+ safe_point = map->instruction_position(); |
+ } |
+ return true; |
+} |
+ |
+ |
+void ReferenceMapPopulator::PopulateReferenceMaps() { |
+ DCHECK(SafePointsAreInOrder()); |
+ |
+ // Iterate over all safe point positions and record a pointer |
+ // for all spilled live ranges at this point. |
+ int last_range_start = 0; |
+ auto reference_maps = data()->code()->reference_maps(); |
+ ReferenceMapDeque::const_iterator first_it = reference_maps->begin(); |
+ for (LiveRange* range : data()->live_ranges()) { |
+ if (range == nullptr) continue; |
+ // Iterate over the first parts of multi-part live ranges. |
+ if (range->IsChild()) continue; |
+ // Skip non-reference values. |
+ if (!IsReference(range->id())) continue; |
+ // Skip empty live ranges. |
+ if (range->IsEmpty()) continue; |
+ |
+ // Find the extent of the range and its children. |
+ int start = range->Start().ToInstructionIndex(); |
+ int end = 0; |
+ for (auto cur = range; cur != nullptr; cur = cur->next()) { |
+ auto this_end = cur->End(); |
+ if (this_end.ToInstructionIndex() > end) |
+ end = this_end.ToInstructionIndex(); |
+ DCHECK(cur->Start().ToInstructionIndex() >= start); |
+ } |
+ |
+ // Most of the ranges are in order, but not all. Keep an eye on when they |
+ // step backwards and reset the first_it so we don't miss any safe points. |
+ if (start < last_range_start) first_it = reference_maps->begin(); |
+ last_range_start = start; |
+ |
+ // Step across all the safe points that are before the start of this range, |
+ // recording how far we step in order to save doing this for the next range. |
+ for (; first_it != reference_maps->end(); ++first_it) { |
+ auto map = *first_it; |
+ if (map->instruction_position() >= start) break; |
+ } |
+ |
+ // Step through the safe points to see whether they are in the range. |
+ for (auto it = first_it; it != reference_maps->end(); ++it) { |
+ auto map = *it; |
+ int safe_point = map->instruction_position(); |
+ |
+ // The safe points are sorted so we can stop searching here. |
+ if (safe_point - 1 > end) break; |
+ |
+ // Advance to the next active range that covers the current |
+ // safe point position. |
+ auto safe_point_pos = |
+ LifetimePosition::InstructionFromInstructionIndex(safe_point); |
+ auto cur = range; |
+ while (cur != nullptr && !cur->Covers(safe_point_pos)) { |
+ cur = cur->next(); |
+ } |
+ if (cur == nullptr) continue; |
+ |
+ // Check if the live range is spilled and the safe point is after |
+ // the spill position. |
+ if (range->HasSpillOperand() && |
+ safe_point >= range->spill_start_index() && |
+ !range->GetSpillOperand()->IsConstant()) { |
+ TRACE("Pointer for range %d (spilled at %d) at safe point %d\n", |
+ range->id(), range->spill_start_index(), safe_point); |
+ map->RecordReference(*range->GetSpillOperand()); |
+ } |
+ |
+ if (!cur->IsSpilled()) { |
+ TRACE( |
+ "Pointer in register for range %d (start at %d) " |
+ "at safe point %d\n", |
+ cur->id(), cur->Start().Value(), safe_point); |
+ auto operand = cur->GetAssignedOperand(); |
+ DCHECK(!operand.IsStackSlot()); |
+ map->RecordReference(operand); |
+ } |
+ } |
+ } |
+} |
+ |
+ |
+namespace { |
+ |
+class LiveRangeBound { |
+ public: |
+ explicit LiveRangeBound(const LiveRange* range) |
+ : range_(range), start_(range->Start()), end_(range->End()) { |
+ DCHECK(!range->IsEmpty()); |
+ } |
+ |
+ bool CanCover(LifetimePosition position) { |
+ return start_.Value() <= position.Value() && |
+ position.Value() < end_.Value(); |
+ } |
+ |
+ const LiveRange* const range_; |
+ const LifetimePosition start_; |
+ const LifetimePosition end_; |
+ |
+ private: |
+ DISALLOW_COPY_AND_ASSIGN(LiveRangeBound); |
+}; |
+ |
+ |
+struct FindResult { |
+ const LiveRange* cur_cover_; |
+ const LiveRange* pred_cover_; |
+}; |
+ |
+ |
+class LiveRangeBoundArray { |
+ public: |
+ LiveRangeBoundArray() : length_(0), start_(nullptr) {} |
+ |
+ bool ShouldInitialize() { return start_ == nullptr; } |
+ |
+ void Initialize(Zone* zone, const LiveRange* const range) { |
+ size_t length = 0; |
+ for (auto i = range; i != nullptr; i = i->next()) length++; |
+ start_ = zone->NewArray<LiveRangeBound>(length); |
+ length_ = length; |
+ auto curr = start_; |
+ for (auto i = range; i != nullptr; i = i->next(), ++curr) { |
+ new (curr) LiveRangeBound(i); |
+ } |
+ } |
+ |
+ LiveRangeBound* Find(const LifetimePosition position) const { |
+ size_t left_index = 0; |
+ size_t right_index = length_; |
+ while (true) { |
+ size_t current_index = left_index + (right_index - left_index) / 2; |
+ DCHECK(right_index > current_index); |
+ auto bound = &start_[current_index]; |
+ if (bound->start_.Value() <= position.Value()) { |
+ if (position.Value() < bound->end_.Value()) return bound; |
+ DCHECK(left_index < current_index); |
+ left_index = current_index; |
+ } else { |
+ right_index = current_index; |
+ } |
+ } |
+ } |
+ |
+ LiveRangeBound* FindPred(const InstructionBlock* pred) { |
+ auto pred_end = LifetimePosition::InstructionFromInstructionIndex( |
+ pred->last_instruction_index()); |
+ return Find(pred_end); |
+ } |
+ |
+ LiveRangeBound* FindSucc(const InstructionBlock* succ) { |
+ auto succ_start = LifetimePosition::GapFromInstructionIndex( |
+ succ->first_instruction_index()); |
+ return Find(succ_start); |
+ } |
+ |
+ void Find(const InstructionBlock* block, const InstructionBlock* pred, |
+ FindResult* result) const { |
+ auto pred_end = LifetimePosition::InstructionFromInstructionIndex( |
+ pred->last_instruction_index()); |
+ auto bound = Find(pred_end); |
+ result->pred_cover_ = bound->range_; |
+ auto cur_start = LifetimePosition::GapFromInstructionIndex( |
+ block->first_instruction_index()); |
+ // Common case. |
+ if (bound->CanCover(cur_start)) { |
+ result->cur_cover_ = bound->range_; |
+ return; |
+ } |
+ result->cur_cover_ = Find(cur_start)->range_; |
+ DCHECK(result->pred_cover_ != nullptr && result->cur_cover_ != nullptr); |
+ } |
+ |
+ private: |
+ size_t length_; |
+ LiveRangeBound* start_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(LiveRangeBoundArray); |
+}; |
+ |
+ |
+class LiveRangeFinder { |
+ public: |
+ explicit LiveRangeFinder(const RegisterAllocationData* data) |
+ : data_(data), |
+ bounds_length_(static_cast<int>(data_->live_ranges().size())), |
+ bounds_(data_->allocation_zone()->NewArray<LiveRangeBoundArray>( |
+ bounds_length_)) { |
+ for (int i = 0; i < bounds_length_; ++i) { |
+ new (&bounds_[i]) LiveRangeBoundArray(); |
+ } |
+ } |
+ |
+ LiveRangeBoundArray* ArrayFor(int operand_index) { |
+ DCHECK(operand_index < bounds_length_); |
+ auto range = data_->live_ranges()[operand_index]; |
+ DCHECK(range != nullptr && !range->IsEmpty()); |
+ auto array = &bounds_[operand_index]; |
+ if (array->ShouldInitialize()) { |
+ array->Initialize(data_->allocation_zone(), range); |
+ } |
+ return array; |
+ } |
+ |
+ private: |
+ const RegisterAllocationData* const data_; |
+ const int bounds_length_; |
+ LiveRangeBoundArray* const bounds_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(LiveRangeFinder); |
+}; |
+ |
+} // namespace |
+ |
+ |
+LiveRangeConnector::LiveRangeConnector(RegisterAllocationData* data) |
+ : data_(data) {} |
+ |
+ |
+bool LiveRangeConnector::CanEagerlyResolveControlFlow( |
+ const InstructionBlock* block) const { |
+ if (block->PredecessorCount() != 1) return false; |
+ return block->predecessors()[0].IsNext(block->rpo_number()); |
+} |
+ |
+ |
+void LiveRangeConnector::ResolveControlFlow() { |
+ // Lazily linearize live ranges in memory for fast lookup. |
+ LiveRangeFinder finder(data()); |
+ auto& live_in_sets = data()->live_in_sets(); |
+ for (auto block : code()->instruction_blocks()) { |
+ if (CanEagerlyResolveControlFlow(block)) continue; |
+ auto live = live_in_sets[block->rpo_number().ToInt()]; |
+ BitVector::Iterator iterator(live); |
+ while (!iterator.Done()) { |
+ auto* array = finder.ArrayFor(iterator.Current()); |
+ for (auto pred : block->predecessors()) { |
+ FindResult result; |
+ const auto* pred_block = code()->InstructionBlockAt(pred); |
+ array->Find(block, pred_block, &result); |
+ if (result.cur_cover_ == result.pred_cover_ || |
+ result.cur_cover_->IsSpilled()) |
+ continue; |
+ auto pred_op = result.pred_cover_->GetAssignedOperand(); |
+ auto cur_op = result.cur_cover_->GetAssignedOperand(); |
+ if (pred_op == cur_op) continue; |
+ ResolveControlFlow(block, cur_op, pred_block, pred_op); |
+ } |
+ iterator.Advance(); |
+ } |
+ } |
+} |
+ |
+ |
+void LiveRangeConnector::ResolveControlFlow(const InstructionBlock* block, |
+ const InstructionOperand& cur_op, |
+ const InstructionBlock* pred, |
+ const InstructionOperand& pred_op) { |
+ DCHECK(pred_op != cur_op); |
+ int gap_index; |
+ Instruction::GapPosition position; |
+ if (block->PredecessorCount() == 1) { |
+ gap_index = block->first_instruction_index(); |
+ position = Instruction::START; |
+ } else { |
+ DCHECK(pred->SuccessorCount() == 1); |
+ DCHECK(!data() |
+ ->InstructionAt(pred->last_instruction_index()) |
+ ->HasReferenceMap()); |
+ gap_index = pred->last_instruction_index(); |
+ position = Instruction::END; |
+ } |
+ data()->AddGapMove(gap_index, position, pred_op, cur_op); |
+} |
+ |
+ |
+void LiveRangeConnector::ConnectRanges(Zone* temp_zone) { |
+ ZoneMap<std::pair<ParallelMove*, InstructionOperand>, InstructionOperand> |
+ delayed_insertion_map(temp_zone); |
+ for (auto first_range : data()->live_ranges()) { |
+ if (first_range == nullptr || first_range->IsChild()) continue; |
+ for (auto second_range = first_range->next(); second_range != nullptr; |
+ first_range = second_range, second_range = second_range->next()) { |
+ auto pos = second_range->Start(); |
+ // Add gap move if the two live ranges touch and there is no block |
+ // boundary. |
+ if (second_range->IsSpilled()) continue; |
+ if (first_range->End().Value() != pos.Value()) continue; |
+ if (data()->IsBlockBoundary(pos) && |
+ !CanEagerlyResolveControlFlow(GetInstructionBlock(pos))) { |
+ continue; |
+ } |
+ auto prev_operand = first_range->GetAssignedOperand(); |
+ auto cur_operand = second_range->GetAssignedOperand(); |
+ if (prev_operand == cur_operand) continue; |
+ bool delay_insertion = false; |
+ Instruction::GapPosition gap_pos; |
+ int gap_index = pos.ToInstructionIndex(); |
+ if (pos.IsGapPosition()) { |
+ gap_pos = pos.IsStart() ? Instruction::START : Instruction::END; |
+ } else { |
+ if (pos.IsStart()) { |
+ delay_insertion = true; |
+ } else { |
+ gap_index++; |
+ } |
+ gap_pos = delay_insertion ? Instruction::END : Instruction::START; |
+ } |
+ auto move = code()->InstructionAt(gap_index)->GetOrCreateParallelMove( |
+ gap_pos, code_zone()); |
+ if (!delay_insertion) { |
+ move->AddMove(prev_operand, cur_operand); |
+ } else { |
+ delayed_insertion_map.insert( |
+ std::make_pair(std::make_pair(move, prev_operand), cur_operand)); |
+ } |
+ } |
+ } |
+ if (delayed_insertion_map.empty()) return; |
+ // Insert all the moves which should occur after the stored move. |
+ ZoneVector<MoveOperands*> to_insert(temp_zone); |
+ ZoneVector<MoveOperands*> to_eliminate(temp_zone); |
+ to_insert.reserve(4); |
+ to_eliminate.reserve(4); |
+ auto moves = delayed_insertion_map.begin()->first.first; |
+ for (auto it = delayed_insertion_map.begin();; ++it) { |
+ bool done = it == delayed_insertion_map.end(); |
+ if (done || it->first.first != moves) { |
+ // Commit the MoveOperands for current ParallelMove. |
+ for (auto move : to_eliminate) { |
+ move->Eliminate(); |
+ } |
+ for (auto move : to_insert) { |
+ moves->push_back(move); |
+ } |
+ if (done) break; |
+ // Reset state. |
+ to_eliminate.clear(); |
+ to_insert.clear(); |
+ moves = it->first.first; |
+ } |
+ // Gather all MoveOperands for a single ParallelMove. |
+ auto move = new (code_zone()) MoveOperands(it->first.second, it->second); |
+ auto eliminate = moves->PrepareInsertAfter(move); |
+ to_insert.push_back(move); |
+ if (eliminate != nullptr) to_eliminate.push_back(eliminate); |
} |
- range->set_assigned_register(reg); |
- auto assignment = range->GetAssignedOperand(); |
- // TODO(dcarney): stop aliasing hint operands. |
- range->ConvertUsesToOperand(assignment, nullptr); |
- if (range->is_phi()) AssignPhiInput(range, assignment); |
} |
} // namespace compiler |