| Index: src/compiler/register-allocator.cc
|
| diff --git a/src/compiler/register-allocator.cc b/src/compiler/register-allocator.cc
|
| index 51522bd8c03534113ce48d6f178499e1e67cfdf4..9f804e744f1e99d39dede960731753cfa58464c6 100644
|
| --- a/src/compiler/register-allocator.cc
|
| +++ b/src/compiler/register-allocator.cc
|
| @@ -39,17 +39,22 @@ static void RemoveElement(ZoneVector<LiveRange*>* v, LiveRange* range) {
|
|
|
| UsePosition::UsePosition(LifetimePosition pos, InstructionOperand* operand,
|
| InstructionOperand* hint)
|
| - : operand_(operand),
|
| - hint_(hint),
|
| - pos_(pos),
|
| - next_(nullptr),
|
| - requires_reg_(false),
|
| - register_beneficial_(true) {
|
| + : operand_(operand), hint_(hint), pos_(pos), next_(nullptr), flags_(0) {
|
| + bool register_beneficial = true;
|
| + UsePositionType type = UsePositionType::kAny;
|
| if (operand_ != nullptr && operand_->IsUnallocated()) {
|
| const UnallocatedOperand* unalloc = UnallocatedOperand::cast(operand_);
|
| - requires_reg_ = unalloc->HasRegisterPolicy();
|
| - register_beneficial_ = !unalloc->HasAnyPolicy();
|
| + if (unalloc->HasRegisterPolicy()) {
|
| + type = UsePositionType::kRequiresRegister;
|
| + } else if (unalloc->HasSlotPolicy()) {
|
| + type = UsePositionType::kRequiresSlot;
|
| + register_beneficial = false;
|
| + } else {
|
| + register_beneficial = !unalloc->HasAnyPolicy();
|
| + }
|
| }
|
| + flags_ = TypeField::encode(type) |
|
| + RegisterBeneficialField::encode(register_beneficial);
|
| DCHECK(pos_.IsValid());
|
| }
|
|
|
| @@ -59,10 +64,11 @@ bool UsePosition::HasHint() const {
|
| }
|
|
|
|
|
| -bool UsePosition::RequiresRegister() const { return requires_reg_; }
|
| -
|
| -
|
| -bool UsePosition::RegisterIsBeneficial() const { return register_beneficial_; }
|
| +void UsePosition::set_type(UsePositionType type, bool register_beneficial) {
|
| + DCHECK_IMPLIES(type == UsePositionType::kRequiresSlot, !register_beneficial);
|
| + flags_ = TypeField::encode(type) |
|
| + RegisterBeneficialField::encode(register_beneficial);
|
| +}
|
|
|
|
|
| void UseInterval::SplitAt(LifetimePosition pos, Zone* zone) {
|
| @@ -117,6 +123,7 @@ bool LiveRange::HasOverlap(UseInterval* target) const {
|
| 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),
|
| @@ -140,7 +147,7 @@ void LiveRange::set_assigned_register(int reg,
|
| DCHECK(!HasRegisterAssigned() && !IsSpilled());
|
| assigned_register_ = reg;
|
| // TODO(dcarney): stop aliasing hint operands.
|
| - ConvertUsesToOperand(GetAssignedOperand(operand_cache));
|
| + ConvertUsesToOperand(GetAssignedOperand(operand_cache), nullptr);
|
| }
|
|
|
|
|
| @@ -161,16 +168,32 @@ void LiveRange::SpillAtDefinition(Zone* zone, int gap_index,
|
|
|
|
|
| void LiveRange::CommitSpillsAtDefinition(InstructionSequence* sequence,
|
| - InstructionOperand* op) {
|
| - auto to_spill = TopLevel()->spills_at_definition_;
|
| - if (to_spill == nullptr) return;
|
| + InstructionOperand* op,
|
| + bool might_be_duplicated) {
|
| + DCHECK(!IsChild());
|
| auto zone = sequence->zone();
|
| - for (; to_spill != nullptr; to_spill = to_spill->next) {
|
| + for (auto to_spill = spills_at_definition_; to_spill != nullptr;
|
| + to_spill = to_spill->next) {
|
| auto gap = sequence->GapAt(to_spill->gap_index);
|
| auto move = gap->GetOrCreateParallelMove(GapInstruction::START, zone);
|
| + // Skip insertion if it's possible that the move exists already as a
|
| + // constraint move from a fixed output register to a slot.
|
| + if (might_be_duplicated) {
|
| + bool found = false;
|
| + auto move_ops = move->move_operands();
|
| + for (auto move_op = move_ops->begin(); move_op != move_ops->end();
|
| + ++move_op) {
|
| + if (move_op->IsEliminated()) continue;
|
| + if (move_op->source()->Equals(to_spill->operand) &&
|
| + move_op->destination()->Equals(op)) {
|
| + found = true;
|
| + break;
|
| + }
|
| + }
|
| + if (found) continue;
|
| + }
|
| move->AddMove(to_spill->operand, op, zone);
|
| }
|
| - TopLevel()->spills_at_definition_ = nullptr;
|
| }
|
|
|
|
|
| @@ -234,7 +257,7 @@ UsePosition* LiveRange::PreviousUsePositionRegisterIsBeneficial(
|
|
|
| UsePosition* LiveRange::NextRegisterPosition(LifetimePosition start) {
|
| UsePosition* pos = NextUsePosition(start);
|
| - while (pos != nullptr && !pos->RequiresRegister()) {
|
| + while (pos != nullptr && pos->type() != UsePositionType::kRequiresRegister) {
|
| pos = pos->next();
|
| }
|
| return pos;
|
| @@ -509,18 +532,27 @@ void LiveRange::AddUsePosition(LifetimePosition pos,
|
| }
|
|
|
|
|
| -void LiveRange::ConvertUsesToOperand(InstructionOperand* op) {
|
| - auto use_pos = first_pos();
|
| - while (use_pos != nullptr) {
|
| - DCHECK(Start().Value() <= use_pos->pos().Value() &&
|
| - use_pos->pos().Value() <= End().Value());
|
| -
|
| - if (use_pos->HasOperand()) {
|
| - DCHECK(op->IsRegister() || op->IsDoubleRegister() ||
|
| - !use_pos->RequiresRegister());
|
| - use_pos->operand()->ConvertTo(op->kind(), op->index());
|
| +void LiveRange::ConvertUsesToOperand(InstructionOperand* op,
|
| + InstructionOperand* spill_op) {
|
| + for (auto pos = first_pos(); pos != nullptr; pos = pos->next()) {
|
| + DCHECK(Start().Value() <= pos->pos().Value() &&
|
| + pos->pos().Value() <= End().Value());
|
| + if (!pos->HasOperand()) {
|
| + continue;
|
| + }
|
| + switch (pos->type()) {
|
| + case UsePositionType::kRequiresSlot:
|
| + if (spill_op != nullptr) {
|
| + pos->operand()->ConvertTo(spill_op->kind(), spill_op->index());
|
| + }
|
| + break;
|
| + case UsePositionType::kRequiresRegister:
|
| + DCHECK(op->IsRegister() || op->IsDoubleRegister());
|
| + // Fall through.
|
| + case UsePositionType::kAny:
|
| + pos->operand()->ConvertTo(op->kind(), op->index());
|
| + break;
|
| }
|
| - use_pos = use_pos->next();
|
| }
|
| }
|
|
|
| @@ -957,12 +989,15 @@ void RegisterAllocator::AssignSpillSlots() {
|
| void RegisterAllocator::CommitAssignment() {
|
| for (auto range : live_ranges()) {
|
| if (range == nullptr || range->IsEmpty()) continue;
|
| - // Register assignments were committed in set_assigned_register.
|
| - if (range->HasRegisterAssigned()) continue;
|
| auto assigned = range->GetAssignedOperand(operand_cache());
|
| - range->ConvertUsesToOperand(assigned);
|
| - if (range->IsSpilled()) {
|
| - range->CommitSpillsAtDefinition(code(), assigned);
|
| + InstructionOperand* spill_operand = nullptr;
|
| + if (!range->TopLevel()->HasNoSpillType()) {
|
| + spill_operand = range->TopLevel()->GetSpillOperand();
|
| + }
|
| + range->ConvertUsesToOperand(assigned, spill_operand);
|
| + if (!range->IsChild() && spill_operand != nullptr) {
|
| + range->CommitSpillsAtDefinition(code(), spill_operand,
|
| + range->has_slot_use());
|
| }
|
| }
|
| }
|
| @@ -977,7 +1012,7 @@ SpillRange* RegisterAllocator::AssignSpillRangeToLiveRange(LiveRange* range) {
|
|
|
| bool RegisterAllocator::TryReuseSpillForPhi(LiveRange* range) {
|
| if (range->IsChild() || !range->is_phi()) return false;
|
| - DCHECK(range->HasNoSpillType());
|
| + DCHECK(!range->HasSpillOperand());
|
|
|
| auto lookup = phi_map_.find(range->id());
|
| DCHECK(lookup != phi_map_.end());
|
| @@ -1040,12 +1075,16 @@ bool RegisterAllocator::TryReuseSpillForPhi(LiveRange* range) {
|
| }
|
| auto pos = range->NextUsePositionRegisterIsBeneficial(next_pos);
|
| if (pos == nullptr) {
|
| - auto spill_range = AssignSpillRangeToLiveRange(range->TopLevel());
|
| + 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().NextInstruction().Value()) {
|
| - auto spill_range = AssignSpillRangeToLiveRange(range->TopLevel());
|
| + 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());
|
| @@ -1304,6 +1343,8 @@ void RegisterAllocator::ProcessInstructions(const InstructionBlock* block,
|
| for (size_t i = 0; i < instr->OutputCount(); i++) {
|
| auto output = instr->OutputAt(i);
|
| if (output->IsUnallocated()) {
|
| + // Unsupported.
|
| + DCHECK(!UnallocatedOperand::cast(output)->HasSlotPolicy());
|
| int out_vreg = UnallocatedOperand::cast(output)->virtual_register();
|
| live->Remove(out_vreg);
|
| } else if (output->IsConstant()) {
|
| @@ -1344,14 +1385,22 @@ void RegisterAllocator::ProcessInstructions(const InstructionBlock* block,
|
| use_pos = curr_position.InstructionEnd();
|
| }
|
|
|
| - Use(block_start_position, use_pos, input, nullptr);
|
| if (input->IsUnallocated()) {
|
| - live->Add(UnallocatedOperand::cast(input)->virtual_register());
|
| + UnallocatedOperand* unalloc = UnallocatedOperand::cast(input);
|
| + int vreg = unalloc->virtual_register();
|
| + live->Add(vreg);
|
| + if (unalloc->HasSlotPolicy()) {
|
| + LiveRangeFor(vreg)->set_has_slot_use(true);
|
| + }
|
| }
|
| + Use(block_start_position, use_pos, input, nullptr);
|
| }
|
|
|
| for (size_t i = 0; i < instr->TempCount(); i++) {
|
| auto temp = instr->TempAt(i);
|
| + // Unsupported.
|
| + DCHECK_IMPLIES(temp->IsUnallocated(),
|
| + !UnallocatedOperand::cast(temp)->HasSlotPolicy());
|
| if (instr->ClobbersTemps()) {
|
| if (temp->IsRegister()) continue;
|
| if (temp->IsUnallocated()) {
|
| @@ -1773,20 +1822,25 @@ void RegisterAllocator::BuildLiveRanges() {
|
| for (auto range : live_ranges()) {
|
| if (range == nullptr) continue;
|
| range->kind_ = RequiredRegisterKind(range->id());
|
| + // Give slots to all ranges with a non fixed slot use.
|
| + if (range->has_slot_use() && range->HasNoSpillType()) {
|
| + 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.
|
| // Without this hack, all uses with "any" policy would get the constant
|
| // operand assigned.
|
| if (range->HasSpillOperand() && range->GetSpillOperand()->IsConstant()) {
|
| for (auto pos = range->first_pos(); pos != nullptr; pos = pos->next_) {
|
| - pos->register_beneficial_ = true;
|
| - // TODO(dcarney): should the else case assert requires_reg_ == false?
|
| + if (pos->type() == UsePositionType::kRequiresSlot) continue;
|
| + UsePositionType new_type = UsePositionType::kAny;
|
| // Can't mark phis as needing a register.
|
| if (!code()
|
| ->InstructionAt(pos->pos().InstructionIndex())
|
| ->IsGapMoves()) {
|
| - pos->requires_reg_ = true;
|
| + new_type = UsePositionType::kRequiresRegister;
|
| }
|
| + pos->set_type(new_type, true);
|
| }
|
| }
|
| }
|
|
|