| Index: src/compiler/register-allocator.cc
|
| diff --git a/src/compiler/register-allocator.cc b/src/compiler/register-allocator.cc
|
| index ca46ac2476f616313f080a478c26b1be49e4555a..98f40f6e7ce31995cf1917db7136811caff4c50e 100644
|
| --- a/src/compiler/register-allocator.cc
|
| +++ b/src/compiler/register-allocator.cc
|
| @@ -67,6 +67,16 @@ void UseInterval::SplitAt(LifetimePosition pos, Zone* zone) {
|
| }
|
|
|
|
|
| +struct LiveRange::SpillAtDefinitionList : ZoneObject {
|
| + SpillAtDefinitionList(int gap_index, InstructionOperand* operand,
|
| + SpillAtDefinitionList* next)
|
| + : gap_index(gap_index), operand(operand), next(next) {}
|
| + const int gap_index;
|
| + InstructionOperand* const operand;
|
| + SpillAtDefinitionList* const next;
|
| +};
|
| +
|
| +
|
| #ifdef DEBUG
|
|
|
|
|
| @@ -104,61 +114,78 @@ LiveRange::LiveRange(int id, Zone* zone)
|
| is_non_loop_phi_(false),
|
| kind_(UNALLOCATED_REGISTERS),
|
| assigned_register_(kInvalidAssignment),
|
| - last_interval_(NULL),
|
| - first_interval_(NULL),
|
| - first_pos_(NULL),
|
| - parent_(NULL),
|
| - next_(NULL),
|
| - current_interval_(NULL),
|
| - last_processed_use_(NULL),
|
| - current_hint_operand_(NULL),
|
| - spill_operand_(new (zone) InstructionOperand()),
|
| + 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_range_(NULL) {}
|
| + spill_type_(SpillType::kNoSpillType),
|
| + spill_operand_(nullptr),
|
| + spills_at_definition_(nullptr) {}
|
|
|
|
|
| -void LiveRange::set_assigned_register(int reg, Zone* zone) {
|
| +void LiveRange::set_assigned_register(int reg) {
|
| DCHECK(!HasRegisterAssigned() && !IsSpilled());
|
| assigned_register_ = reg;
|
| - if (spill_range_ == nullptr) {
|
| - ConvertOperands(zone);
|
| - }
|
| }
|
|
|
|
|
| -void LiveRange::MakeSpilled(Zone* zone) {
|
| +void LiveRange::MakeSpilled() {
|
| DCHECK(!IsSpilled());
|
| - DCHECK(TopLevel()->HasAllocatedSpillOperand());
|
| + DCHECK(!TopLevel()->HasNoSpillType());
|
| spilled_ = true;
|
| assigned_register_ = kInvalidAssignment;
|
| - ConvertOperands(zone);
|
| }
|
|
|
|
|
| -bool LiveRange::HasAllocatedSpillOperand() const {
|
| - DCHECK(spill_operand_ != NULL);
|
| - return !spill_operand_->IsIgnored() || spill_range_ != NULL;
|
| +void LiveRange::SpillAtDefinition(Zone* zone, int gap_index,
|
| + InstructionOperand* operand) {
|
| + DCHECK(HasNoSpillType());
|
| + spills_at_definition_ = new (zone)
|
| + SpillAtDefinitionList(gap_index, operand, spills_at_definition_);
|
| +}
|
| +
|
| +
|
| +void LiveRange::CommitSpillsAtDefinition(InstructionSequence* sequence,
|
| + InstructionOperand* op) {
|
| + auto to_spill = TopLevel()->spills_at_definition_;
|
| + if (to_spill == nullptr) return;
|
| + Zone* zone = sequence->zone();
|
| + for (; to_spill != nullptr; to_spill = to_spill->next) {
|
| + auto gap = sequence->GapAt(to_spill->gap_index);
|
| + auto move = gap->GetOrCreateParallelMove(GapInstruction::START, zone);
|
| + move->AddMove(to_spill->operand, op, zone);
|
| + }
|
| + TopLevel()->spills_at_definition_ = nullptr;
|
| }
|
|
|
|
|
| void LiveRange::SetSpillOperand(InstructionOperand* operand) {
|
| + DCHECK(HasNoSpillType());
|
| DCHECK(!operand->IsUnallocated());
|
| - DCHECK(spill_operand_ != NULL);
|
| - DCHECK(spill_operand_->IsIgnored());
|
| - spill_operand_->ConvertTo(operand->kind(), operand->index());
|
| + spill_type_ = SpillType::kSpillOperand;
|
| + spill_operand_ = operand;
|
| +}
|
| +
|
| +
|
| +void LiveRange::SetSpillRange(SpillRange* spill_range) {
|
| + DCHECK(HasNoSpillType() || HasSpillRange());
|
| + DCHECK_NE(spill_range, nullptr);
|
| + spill_type_ = SpillType::kSpillRange;
|
| + spill_range_ = spill_range;
|
| }
|
|
|
|
|
| void LiveRange::CommitSpillOperand(InstructionOperand* operand) {
|
| - DCHECK(spill_range_ != NULL);
|
| + DCHECK(HasSpillRange());
|
| + DCHECK(!operand->IsUnallocated());
|
| DCHECK(!IsChild());
|
| - spill_range_ = NULL;
|
| - SetSpillOperand(operand);
|
| - for (LiveRange* range = this; range != NULL; range = range->next()) {
|
| - if (range->IsSpilled()) {
|
| - range->ConvertUsesToOperand(operand);
|
| - }
|
| - }
|
| + spill_type_ = SpillType::kSpillOperand;
|
| + spill_operand_ = operand;
|
| }
|
|
|
|
|
| @@ -215,7 +242,7 @@ bool LiveRange::CanBeSpilled(LifetimePosition pos) {
|
|
|
|
|
| InstructionOperand* LiveRange::CreateAssignedOperand(Zone* zone) const {
|
| - InstructionOperand* op = NULL;
|
| + InstructionOperand* op = nullptr;
|
| if (HasRegisterAssigned()) {
|
| DCHECK(!IsSpilled());
|
| switch (Kind()) {
|
| @@ -228,15 +255,11 @@ InstructionOperand* LiveRange::CreateAssignedOperand(Zone* zone) const {
|
| default:
|
| UNREACHABLE();
|
| }
|
| - } else if (IsSpilled()) {
|
| + } else {
|
| + DCHECK(IsSpilled());
|
| DCHECK(!HasRegisterAssigned());
|
| op = TopLevel()->GetSpillOperand();
|
| DCHECK(!op->IsUnallocated());
|
| - } else {
|
| - UnallocatedOperand* unalloc =
|
| - new (zone) UnallocatedOperand(UnallocatedOperand::NONE);
|
| - unalloc->set_virtual_register(id_);
|
| - op = unalloc;
|
| }
|
| return op;
|
| }
|
| @@ -474,11 +497,6 @@ void LiveRange::ConvertUsesToOperand(InstructionOperand* op) {
|
| }
|
|
|
|
|
| -void LiveRange::ConvertOperands(Zone* zone) {
|
| - ConvertUsesToOperand(CreateAssignedOperand(zone));
|
| -}
|
| -
|
| -
|
| bool LiveRange::CanCover(LifetimePosition position) const {
|
| if (IsEmpty()) return false;
|
| return Start().Value() <= position.Value() &&
|
| @@ -555,7 +573,7 @@ RegisterAllocator::RegisterAllocator(const RegisterConfiguration* config,
|
| RegisterConfiguration::kMaxDoubleRegisters);
|
| // TryAllocateFreeReg and AllocateBlockedReg assume this
|
| // when allocating local arrays.
|
| - DCHECK(this->config()->num_double_registers() >=
|
| + DCHECK(RegisterConfiguration::kMaxDoubleRegisters >=
|
| this->config()->num_general_registers());
|
| assigned_registers_ =
|
| new (code_zone()) BitVector(config->num_general_registers(), code_zone());
|
| @@ -776,12 +794,12 @@ static bool AreUseIntervalsIntersecting(UseInterval* interval1,
|
| UseInterval* interval2) {
|
| while (interval1 != NULL && interval2 != NULL) {
|
| if (interval1->start().Value() < interval2->start().Value()) {
|
| - if (interval1->end().Value() >= interval2->start().Value()) {
|
| + if (interval1->end().Value() > interval2->start().Value()) {
|
| return true;
|
| }
|
| interval1 = interval1->next();
|
| } else {
|
| - if (interval2->end().Value() >= interval1->start().Value()) {
|
| + if (interval2->end().Value() > interval1->start().Value()) {
|
| return true;
|
| }
|
| interval2 = interval2->next();
|
| @@ -809,7 +827,7 @@ SpillRange::SpillRange(LiveRange* range, int id, Zone* zone)
|
| }
|
| use_interval_ = result;
|
| live_ranges_.Add(range, zone);
|
| - DCHECK(range->GetSpillRange() == NULL);
|
| + DCHECK(!range->HasSpillRange());
|
| range->SetSpillRange(this);
|
| }
|
|
|
| @@ -898,19 +916,27 @@ void RegisterAllocator::ReuseSpillSlots() {
|
|
|
| // Allocate slots for the merged spill ranges.
|
| for (int i = 0; i < spill_ranges_.length(); i++) {
|
| - SpillRange* range = spill_ranges_.at(i);
|
| - if (!range->IsEmpty()) {
|
| - // Allocate a new operand referring to the spill slot.
|
| - RegisterKind kind = range->Kind();
|
| - int index = frame()->AllocateSpillSlot(kind == DOUBLE_REGISTERS);
|
| - InstructionOperand* op = nullptr;
|
| - if (kind == DOUBLE_REGISTERS) {
|
| - op = DoubleStackSlotOperand::Create(index, local_zone());
|
| - } else {
|
| - DCHECK(kind == GENERAL_REGISTERS);
|
| - op = StackSlotOperand::Create(index, local_zone());
|
| - }
|
| - range->SetOperand(op);
|
| + auto range = spill_ranges_.at(i);
|
| + 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
|
| + ? InstructionOperand::DOUBLE_STACK_SLOT
|
| + : InstructionOperand::STACK_SLOT;
|
| + auto op = new (code_zone()) InstructionOperand(op_kind, index);
|
| + range->SetOperand(op);
|
| + }
|
| +}
|
| +
|
| +
|
| +void RegisterAllocator::CommitAssignment() {
|
| + for (auto range : live_ranges()) {
|
| + if (range == nullptr || range->IsEmpty()) continue;
|
| + auto assigned = range->CreateAssignedOperand(code_zone());
|
| + range->ConvertUsesToOperand(assigned);
|
| + if (range->IsSpilled()) {
|
| + range->CommitSpillsAtDefinition(code(), assigned);
|
| }
|
| }
|
| }
|
| @@ -928,7 +954,7 @@ SpillRange* RegisterAllocator::AssignSpillRangeToLiveRange(LiveRange* range) {
|
|
|
| bool RegisterAllocator::TryReuseSpillForPhi(LiveRange* range) {
|
| DCHECK(FLAG_turbo_reuse_spill_slots);
|
| - DCHECK(!range->HasAllocatedSpillOperand());
|
| + DCHECK(range->HasNoSpillType());
|
| if (range->IsChild() || !range->is_phi()) return false;
|
|
|
| auto lookup = phi_map_.find(range->id());
|
| @@ -1047,6 +1073,7 @@ void RegisterAllocator::MeetRegisterConstraintsForLastInstructionInBlock(
|
| AllocateFixed(output, -1, false);
|
| // This value is produced on the stack, we never need to spill it.
|
| if (output->IsStackSlot()) {
|
| + DCHECK(output->index() < 0);
|
| range->SetSpillOperand(output);
|
| range->SetSpillStartIndex(end);
|
| assigned = true;
|
| @@ -1073,16 +1100,8 @@ void RegisterAllocator::MeetRegisterConstraintsForLastInstructionInBlock(
|
| const InstructionBlock* successor = code()->InstructionBlockAt(succ);
|
| DCHECK(successor->PredecessorCount() == 1);
|
| int gap_index = successor->first_instruction_index() + 1;
|
| + range->SpillAtDefinition(local_zone(), gap_index, output);
|
| range->SetSpillStartIndex(gap_index);
|
| -
|
| - // This move to spill operand is not a real use. Liveness analysis
|
| - // and splitting of live ranges do not account for it.
|
| - // Thus it should be inserted to a lifetime position corresponding to
|
| - // the instruction end.
|
| - GapInstruction* gap = code()->GapAt(gap_index);
|
| - ParallelMove* move =
|
| - gap->GetOrCreateParallelMove(GapInstruction::BEFORE, code_zone());
|
| - move->AddMove(output, range->GetSpillOperand(), code_zone());
|
| }
|
| }
|
| }
|
| @@ -1121,6 +1140,7 @@ void RegisterAllocator::MeetConstraintsBetween(Instruction* first,
|
|
|
| // This value is produced on the stack, we never need to spill it.
|
| if (first_output->IsStackSlot()) {
|
| + DCHECK(first_output->index() < 0);
|
| range->SetSpillOperand(first_output);
|
| range->SetSpillStartIndex(gap_index - 1);
|
| assigned = true;
|
| @@ -1131,16 +1151,8 @@ void RegisterAllocator::MeetConstraintsBetween(Instruction* first,
|
| // Make sure we add a gap move for spilling (if we have not done
|
| // so already).
|
| if (!assigned) {
|
| + range->SpillAtDefinition(local_zone(), gap_index, first_output);
|
| range->SetSpillStartIndex(gap_index);
|
| -
|
| - // This move to spill operand is not a real use. Liveness analysis
|
| - // and splitting of live ranges do not account for it.
|
| - // Thus it should be inserted to a lifetime position corresponding to
|
| - // the instruction end.
|
| - GapInstruction* gap = code()->GapAt(gap_index);
|
| - ParallelMove* move =
|
| - gap->GetOrCreateParallelMove(GapInstruction::BEFORE, code_zone());
|
| - move->AddMove(first_output, range->GetSpillOperand(), code_zone());
|
| }
|
| }
|
| }
|
| @@ -1241,7 +1253,6 @@ void RegisterAllocator::ProcessInstructions(const InstructionBlock* block,
|
| const ZoneList<MoveOperands>* move_operands = move->move_operands();
|
| for (int i = 0; i < move_operands->length(); ++i) {
|
| MoveOperands* cur = &move_operands->at(i);
|
| - if (cur->IsIgnored()) continue;
|
| InstructionOperand* from = cur->source();
|
| InstructionOperand* to = cur->destination();
|
| InstructionOperand* hint = to;
|
| @@ -1363,11 +1374,9 @@ void RegisterAllocator::ResolvePhis(const InstructionBlock* block) {
|
| }
|
| }
|
| LiveRange* live_range = LiveRangeFor(phi_vreg);
|
| - BlockStartInstruction* block_start =
|
| - code()->GetBlockStart(block->rpo_number());
|
| - block_start->GetOrCreateParallelMove(GapInstruction::BEFORE, code_zone())
|
| - ->AddMove(output, live_range->GetSpillOperand(), code_zone());
|
| - live_range->SetSpillStartIndex(block->first_instruction_index());
|
| + int gap_index = block->first_instruction_index();
|
| + live_range->SpillAtDefinition(local_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);
|
| if (!block->IsLoopHeader()) {
|
| @@ -1742,8 +1751,7 @@ void RegisterAllocator::BuildLiveRanges() {
|
| // Without this hack, all uses with "any" policy would get the constant
|
| // operand assigned.
|
| LiveRange* range = live_ranges_[i];
|
| - if (range->HasAllocatedSpillOperand() &&
|
| - range->GetSpillOperand()->IsConstant()) {
|
| + if (range->HasSpillOperand() && range->GetSpillOperand()->IsConstant()) {
|
| for (UsePosition* pos = range->first_pos(); pos != NULL;
|
| pos = pos->next_) {
|
| pos->register_beneficial_ = true;
|
| @@ -1855,7 +1863,7 @@ void RegisterAllocator::PopulatePointerMaps() {
|
|
|
| // Check if the live range is spilled and the safe point is after
|
| // the spill position.
|
| - if (range->HasAllocatedSpillOperand() &&
|
| + if (range->HasSpillOperand() &&
|
| safe_point >= range->spill_start_index() &&
|
| !range->GetSpillOperand()->IsConstant()) {
|
| TraceAlloc("Pointer for range %d (spilled at %d) at safe point %d\n",
|
| @@ -1935,7 +1943,7 @@ void RegisterAllocator::AllocateRegisters() {
|
| TraceAlloc("Processing interval %d start=%d\n", current->id(),
|
| position.Value());
|
|
|
| - if (current->HasAllocatedSpillOperand()) {
|
| + if (!current->HasNoSpillType()) {
|
| TraceAlloc("Live range %d already has a spill operand\n", current->id());
|
| LifetimePosition next_pos = position;
|
| if (code()->IsGapAt(next_pos.InstructionIndex())) {
|
| @@ -2100,7 +2108,7 @@ void RegisterAllocator::FreeSpillSlot(LiveRange* range) {
|
| // Check that we are the last range.
|
| if (range->next() != NULL) return;
|
|
|
| - if (!range->TopLevel()->HasAllocatedSpillOperand()) return;
|
| + if (!range->TopLevel()->HasSpillOperand()) return;
|
|
|
| InstructionOperand* spill_operand = range->TopLevel()->GetSpillOperand();
|
| if (spill_operand->IsConstant()) return;
|
| @@ -2240,7 +2248,7 @@ void RegisterAllocator::AllocateBlockedReg(LiveRange* current) {
|
| return;
|
| }
|
|
|
| - LifetimePosition use_pos[RegisterConfiguration::kMaxGeneralRegisters];
|
| + LifetimePosition use_pos[RegisterConfiguration::kMaxDoubleRegisters];
|
| LifetimePosition block_pos[RegisterConfiguration::kMaxDoubleRegisters];
|
|
|
| for (int i = 0; i < num_registers_; i++) {
|
| @@ -2527,8 +2535,7 @@ void RegisterAllocator::Spill(LiveRange* range) {
|
| DCHECK(!range->IsSpilled());
|
| TraceAlloc("Spilling live range %d\n", range->id());
|
| LiveRange* first = range->TopLevel();
|
| -
|
| - if (!first->HasAllocatedSpillOperand()) {
|
| + if (first->HasNoSpillType()) {
|
| if (FLAG_turbo_reuse_spill_slots) {
|
| AssignSpillRangeToLiveRange(first);
|
| } else {
|
| @@ -2537,17 +2544,15 @@ void RegisterAllocator::Spill(LiveRange* range) {
|
| // Allocate a new operand referring to the spill slot.
|
| RegisterKind kind = range->Kind();
|
| int index = frame()->AllocateSpillSlot(kind == DOUBLE_REGISTERS);
|
| - if (kind == DOUBLE_REGISTERS) {
|
| - op = DoubleStackSlotOperand::Create(index, local_zone());
|
| - } else {
|
| - DCHECK(kind == GENERAL_REGISTERS);
|
| - op = StackSlotOperand::Create(index, local_zone());
|
| - }
|
| + auto op_kind = kind == DOUBLE_REGISTERS
|
| + ? InstructionOperand::DOUBLE_STACK_SLOT
|
| + : InstructionOperand::STACK_SLOT;
|
| + op = new (code_zone()) InstructionOperand(op_kind, index);
|
| }
|
| first->SetSpillOperand(op);
|
| }
|
| }
|
| - range->MakeSpilled(code_zone());
|
| + range->MakeSpilled();
|
| }
|
|
|
|
|
| @@ -2575,7 +2580,7 @@ void RegisterAllocator::SetLiveRangeAssignedRegister(LiveRange* range,
|
| DCHECK(range->Kind() == GENERAL_REGISTERS);
|
| assigned_registers_->Add(reg);
|
| }
|
| - range->set_assigned_register(reg, code_zone());
|
| + range->set_assigned_register(reg);
|
| }
|
|
|
| } // namespace compiler
|
|
|