| Index: src/compiler/register-allocator.cc
|
| diff --git a/src/compiler/register-allocator.cc b/src/compiler/register-allocator.cc
|
| index 065506f4499d5ced632bbd28c4a6f0842e74540e..232ad9fec1fa0627b9077ca9a557a7de3db2beae 100644
|
| --- a/src/compiler/register-allocator.cc
|
| +++ b/src/compiler/register-allocator.cc
|
| @@ -112,165 +112,6 @@
|
| }
|
|
|
| } // namespace
|
| -
|
| -class LiveRangeBound {
|
| - public:
|
| - explicit LiveRangeBound(LiveRange* range, bool skip)
|
| - : range_(range), start_(range->Start()), end_(range->End()), skip_(skip) {
|
| - DCHECK(!range->IsEmpty());
|
| - }
|
| -
|
| - bool CanCover(LifetimePosition position) {
|
| - return start_ <= position && position < end_;
|
| - }
|
| -
|
| - LiveRange* const range_;
|
| - const LifetimePosition start_;
|
| - const LifetimePosition end_;
|
| - const bool skip_;
|
| -
|
| - private:
|
| - DISALLOW_COPY_AND_ASSIGN(LiveRangeBound);
|
| -};
|
| -
|
| -
|
| -struct FindResult {
|
| - LiveRange* cur_cover_;
|
| - LiveRange* pred_cover_;
|
| -};
|
| -
|
| -
|
| -class LiveRangeBoundArray {
|
| - public:
|
| - LiveRangeBoundArray() : length_(0), start_(nullptr) {}
|
| -
|
| - bool ShouldInitialize() { return start_ == nullptr; }
|
| -
|
| - void Initialize(Zone* zone, TopLevelLiveRange* range) {
|
| - length_ = range->GetChildCount();
|
| -
|
| - start_ = zone->NewArray<LiveRangeBound>(length_);
|
| - LiveRangeBound* curr = start_;
|
| - // Normally, spilled ranges do not need connecting moves, because the spill
|
| - // location has been assigned at definition. For ranges spilled in deferred
|
| - // blocks, that is not the case, so we need to connect the spilled children.
|
| - for (LiveRange *i = range; i != nullptr; i = i->next(), ++curr) {
|
| - new (curr) LiveRangeBound(i, i->spilled());
|
| - }
|
| - }
|
| -
|
| - 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);
|
| - LiveRangeBound* bound = &start_[current_index];
|
| - if (bound->start_ <= position) {
|
| - if (position < bound->end_) return bound;
|
| - DCHECK(left_index < current_index);
|
| - left_index = current_index;
|
| - } else {
|
| - right_index = current_index;
|
| - }
|
| - }
|
| - }
|
| -
|
| - LiveRangeBound* FindPred(const InstructionBlock* pred) {
|
| - LifetimePosition pred_end =
|
| - LifetimePosition::InstructionFromInstructionIndex(
|
| - pred->last_instruction_index());
|
| - return Find(pred_end);
|
| - }
|
| -
|
| - LiveRangeBound* FindSucc(const InstructionBlock* succ) {
|
| - LifetimePosition succ_start = LifetimePosition::GapFromInstructionIndex(
|
| - succ->first_instruction_index());
|
| - return Find(succ_start);
|
| - }
|
| -
|
| - bool FindConnectableSubranges(const InstructionBlock* block,
|
| - const InstructionBlock* pred,
|
| - FindResult* result) const {
|
| - LifetimePosition pred_end =
|
| - LifetimePosition::InstructionFromInstructionIndex(
|
| - pred->last_instruction_index());
|
| - LiveRangeBound* bound = Find(pred_end);
|
| - result->pred_cover_ = bound->range_;
|
| - LifetimePosition cur_start = LifetimePosition::GapFromInstructionIndex(
|
| - block->first_instruction_index());
|
| -
|
| - if (bound->CanCover(cur_start)) {
|
| - // Both blocks are covered by the same range, so there is nothing to
|
| - // connect.
|
| - return false;
|
| - }
|
| - bound = Find(cur_start);
|
| - if (bound->skip_) {
|
| - return false;
|
| - }
|
| - result->cur_cover_ = bound->range_;
|
| - DCHECK(result->pred_cover_ != nullptr && result->cur_cover_ != nullptr);
|
| - return (result->cur_cover_ != result->pred_cover_);
|
| - }
|
| -
|
| - private:
|
| - size_t length_;
|
| - LiveRangeBound* start_;
|
| -
|
| - DISALLOW_COPY_AND_ASSIGN(LiveRangeBoundArray);
|
| -};
|
| -
|
| -
|
| -class LiveRangeFinder {
|
| - public:
|
| - explicit LiveRangeFinder(const RegisterAllocationData* data, Zone* zone)
|
| - : data_(data),
|
| - bounds_length_(static_cast<int>(data_->live_ranges().size())),
|
| - bounds_(zone->NewArray<LiveRangeBoundArray>(bounds_length_)),
|
| - zone_(zone) {
|
| - for (int i = 0; i < bounds_length_; ++i) {
|
| - new (&bounds_[i]) LiveRangeBoundArray();
|
| - }
|
| - }
|
| -
|
| - LiveRangeBoundArray* ArrayFor(int operand_index) {
|
| - DCHECK(operand_index < bounds_length_);
|
| - TopLevelLiveRange* range = data_->live_ranges()[operand_index];
|
| - DCHECK(range != nullptr && !range->IsEmpty());
|
| - LiveRangeBoundArray* array = &bounds_[operand_index];
|
| - if (array->ShouldInitialize()) {
|
| - array->Initialize(zone_, range);
|
| - }
|
| - return array;
|
| - }
|
| -
|
| - private:
|
| - const RegisterAllocationData* const data_;
|
| - const int bounds_length_;
|
| - LiveRangeBoundArray* const bounds_;
|
| - Zone* const zone_;
|
| -
|
| - DISALLOW_COPY_AND_ASSIGN(LiveRangeFinder);
|
| -};
|
| -
|
| -
|
| -typedef std::pair<ParallelMove*, InstructionOperand> DelayedInsertionMapKey;
|
| -
|
| -
|
| -struct DelayedInsertionMapCompare {
|
| - bool operator()(const DelayedInsertionMapKey& a,
|
| - const DelayedInsertionMapKey& b) const {
|
| - if (a.first == b.first) {
|
| - return a.second.Compare(b.second);
|
| - }
|
| - return a.first < b.first;
|
| - }
|
| -};
|
| -
|
| -
|
| -typedef ZoneMap<DelayedInsertionMapKey, InstructionOperand,
|
| - DelayedInsertionMapCompare> DelayedInsertionMap;
|
|
|
|
|
| UsePosition::UsePosition(LifetimePosition pos, InstructionOperand* operand,
|
| @@ -893,13 +734,51 @@
|
| gap_index, operand, spill_move_insertion_locations_);
|
| }
|
|
|
| +
|
| +bool TopLevelLiveRange::TryCommitSpillInDeferredBlock(
|
| + InstructionSequence* code, const InstructionOperand& spill_operand) {
|
| + if (!IsSpilledOnlyInDeferredBlocks()) return false;
|
| +
|
| + TRACE("Live Range %d will be spilled only in deferred blocks.\n", vreg());
|
| + // If we have ranges that aren't spilled but require the operand on the stack,
|
| + // make sure we insert the spill.
|
| + for (const LiveRange* child = this; child != nullptr; child = child->next()) {
|
| + if (!child->spilled() &&
|
| + child->NextSlotPosition(child->Start()) != nullptr) {
|
| + Instruction* instr =
|
| + code->InstructionAt(child->Start().ToInstructionIndex());
|
| + // Insert spill at the end to let live range connections happen at START.
|
| + ParallelMove* move =
|
| + instr->GetOrCreateParallelMove(Instruction::END, code->zone());
|
| + InstructionOperand assigned = child->GetAssignedOperand();
|
| + if (TopLevel()->has_slot_use()) {
|
| + bool found = false;
|
| + for (MoveOperands* move_op : *move) {
|
| + if (move_op->IsEliminated()) continue;
|
| + if (move_op->source().Equals(assigned) &&
|
| + move_op->destination().Equals(spill_operand)) {
|
| + found = true;
|
| + break;
|
| + }
|
| + }
|
| + if (found) continue;
|
| + }
|
| +
|
| + move->AddMove(assigned, spill_operand);
|
| + }
|
| + }
|
| +
|
| + return true;
|
| +}
|
| +
|
| +
|
| void TopLevelLiveRange::CommitSpillMoves(InstructionSequence* sequence,
|
| const InstructionOperand& op,
|
| bool might_be_duplicated) {
|
| - DCHECK_IMPLIES(op.IsConstant(), GetSpillMoveInsertionLocations() == nullptr);
|
| + DCHECK_IMPLIES(op.IsConstant(), spill_move_insertion_locations() == nullptr);
|
| Zone* zone = sequence->zone();
|
|
|
| - for (SpillMoveInsertionList* to_spill = GetSpillMoveInsertionLocations();
|
| + for (SpillMoveInsertionList* to_spill = spill_move_insertion_locations();
|
| to_spill != nullptr; to_spill = to_spill->next) {
|
| Instruction* instr = sequence->InstructionAt(to_spill->gap_index);
|
| ParallelMove* move =
|
| @@ -3086,7 +2965,7 @@
|
| }
|
| } else {
|
| TopLevelLiveRange::SpillMoveInsertionList* spills =
|
| - range->GetSpillMoveInsertionLocations();
|
| + range->spill_move_insertion_locations();
|
| DCHECK_NOT_NULL(spills);
|
| for (; spills != nullptr; spills = spills->next) {
|
| code->GetInstructionBlock(spills->gap_index)->mark_needs_frame();
|
| @@ -3153,10 +3032,12 @@
|
| // connecting move when a successor child range is spilled - because the
|
| // spilled range picks up its value from the slot which was assigned at
|
| // definition. For ranges that are determined to spill only in deferred
|
| - // blocks, we let ConnectLiveRanges and ResolveControlFlow find the blocks
|
| - // where a spill operand is expected, and then finalize by inserting the
|
| - // spills in the deferred blocks dominators.
|
| - if (!top_range->IsSpilledOnlyInDeferredBlocks()) {
|
| + // blocks, we let ConnectLiveRanges and ResolveControlFlow insert such
|
| + // moves between ranges. Because of how the ranges are split around
|
| + // deferred blocks, this amounts to spilling and filling inside such
|
| + // blocks.
|
| + if (!top_range->TryCommitSpillInDeferredBlock(data()->code(),
|
| + spill_operand)) {
|
| // Spill at definition if the range isn't spilled only in deferred
|
| // blocks.
|
| top_range->CommitSpillMoves(
|
| @@ -3307,6 +3188,171 @@
|
| }
|
|
|
|
|
| +namespace {
|
| +
|
| +class LiveRangeBound {
|
| + public:
|
| + explicit LiveRangeBound(const LiveRange* range, bool skip)
|
| + : range_(range), start_(range->Start()), end_(range->End()), skip_(skip) {
|
| + DCHECK(!range->IsEmpty());
|
| + }
|
| +
|
| + bool CanCover(LifetimePosition position) {
|
| + return start_ <= position && position < end_;
|
| + }
|
| +
|
| + const LiveRange* const range_;
|
| + const LifetimePosition start_;
|
| + const LifetimePosition end_;
|
| + const bool skip_;
|
| +
|
| + 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 TopLevelLiveRange* const range) {
|
| + length_ = range->GetChildCount();
|
| +
|
| + start_ = zone->NewArray<LiveRangeBound>(length_);
|
| + LiveRangeBound* curr = start_;
|
| + // Normally, spilled ranges do not need connecting moves, because the spill
|
| + // location has been assigned at definition. For ranges spilled in deferred
|
| + // blocks, that is not the case, so we need to connect the spilled children.
|
| + bool spilled_in_blocks = range->IsSpilledOnlyInDeferredBlocks();
|
| + for (const LiveRange *i = range; i != nullptr; i = i->next(), ++curr) {
|
| + new (curr) LiveRangeBound(i, !spilled_in_blocks && i->spilled());
|
| + }
|
| + }
|
| +
|
| + 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);
|
| + LiveRangeBound* bound = &start_[current_index];
|
| + if (bound->start_ <= position) {
|
| + if (position < bound->end_) return bound;
|
| + DCHECK(left_index < current_index);
|
| + left_index = current_index;
|
| + } else {
|
| + right_index = current_index;
|
| + }
|
| + }
|
| + }
|
| +
|
| + LiveRangeBound* FindPred(const InstructionBlock* pred) {
|
| + LifetimePosition pred_end =
|
| + LifetimePosition::InstructionFromInstructionIndex(
|
| + pred->last_instruction_index());
|
| + return Find(pred_end);
|
| + }
|
| +
|
| + LiveRangeBound* FindSucc(const InstructionBlock* succ) {
|
| + LifetimePosition succ_start = LifetimePosition::GapFromInstructionIndex(
|
| + succ->first_instruction_index());
|
| + return Find(succ_start);
|
| + }
|
| +
|
| + bool FindConnectableSubranges(const InstructionBlock* block,
|
| + const InstructionBlock* pred,
|
| + FindResult* result) const {
|
| + LifetimePosition pred_end =
|
| + LifetimePosition::InstructionFromInstructionIndex(
|
| + pred->last_instruction_index());
|
| + LiveRangeBound* bound = Find(pred_end);
|
| + result->pred_cover_ = bound->range_;
|
| + LifetimePosition cur_start = LifetimePosition::GapFromInstructionIndex(
|
| + block->first_instruction_index());
|
| +
|
| + if (bound->CanCover(cur_start)) {
|
| + // Both blocks are covered by the same range, so there is nothing to
|
| + // connect.
|
| + return false;
|
| + }
|
| + bound = Find(cur_start);
|
| + if (bound->skip_) {
|
| + return false;
|
| + }
|
| + result->cur_cover_ = bound->range_;
|
| + DCHECK(result->pred_cover_ != nullptr && result->cur_cover_ != nullptr);
|
| + return (result->cur_cover_ != result->pred_cover_);
|
| + }
|
| +
|
| + private:
|
| + size_t length_;
|
| + LiveRangeBound* start_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(LiveRangeBoundArray);
|
| +};
|
| +
|
| +
|
| +class LiveRangeFinder {
|
| + public:
|
| + explicit LiveRangeFinder(const RegisterAllocationData* data, Zone* zone)
|
| + : data_(data),
|
| + bounds_length_(static_cast<int>(data_->live_ranges().size())),
|
| + bounds_(zone->NewArray<LiveRangeBoundArray>(bounds_length_)),
|
| + zone_(zone) {
|
| + for (int i = 0; i < bounds_length_; ++i) {
|
| + new (&bounds_[i]) LiveRangeBoundArray();
|
| + }
|
| + }
|
| +
|
| + LiveRangeBoundArray* ArrayFor(int operand_index) {
|
| + DCHECK(operand_index < bounds_length_);
|
| + TopLevelLiveRange* range = data_->live_ranges()[operand_index];
|
| + DCHECK(range != nullptr && !range->IsEmpty());
|
| + LiveRangeBoundArray* array = &bounds_[operand_index];
|
| + if (array->ShouldInitialize()) {
|
| + array->Initialize(zone_, range);
|
| + }
|
| + return array;
|
| + }
|
| +
|
| + private:
|
| + const RegisterAllocationData* const data_;
|
| + const int bounds_length_;
|
| + LiveRangeBoundArray* const bounds_;
|
| + Zone* const zone_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(LiveRangeFinder);
|
| +};
|
| +
|
| +
|
| +typedef std::pair<ParallelMove*, InstructionOperand> DelayedInsertionMapKey;
|
| +
|
| +
|
| +struct DelayedInsertionMapCompare {
|
| + bool operator()(const DelayedInsertionMapKey& a,
|
| + const DelayedInsertionMapKey& b) const {
|
| + if (a.first == b.first) {
|
| + return a.second.Compare(b.second);
|
| + }
|
| + return a.first < b.first;
|
| + }
|
| +};
|
| +
|
| +
|
| +typedef ZoneMap<DelayedInsertionMapKey, InstructionOperand,
|
| + DelayedInsertionMapCompare> DelayedInsertionMap;
|
| +
|
| +} // namespace
|
| +
|
| +
|
| LiveRangeConnector::LiveRangeConnector(RegisterAllocationData* data)
|
| : data_(data) {}
|
|
|
| @@ -3337,18 +3383,6 @@
|
| InstructionOperand pred_op = result.pred_cover_->GetAssignedOperand();
|
| InstructionOperand cur_op = result.cur_cover_->GetAssignedOperand();
|
| if (pred_op.Equals(cur_op)) continue;
|
| - if (!pred_op.IsAnyRegister() && cur_op.IsAnyRegister()) {
|
| - // We're doing a reload.
|
| - const LiveRange* current = result.cur_cover_;
|
| -
|
| - if (current->TopLevel()->IsSpilledOnlyInDeferredBlocks() &&
|
| - pred_block->IsDeferred()) {
|
| - // The spill location should be defined in pred_block, so add
|
| - // pred_block to the list of blocks requiring a spill operand.
|
| - current->TopLevel()->GetListOfBlocksRequiringSpillOperands()->Add(
|
| - pred_block->rpo_number().ToInt());
|
| - }
|
| - }
|
| int move_loc = ResolveControlFlow(block, cur_op, pred_block, pred_op);
|
| USE(move_loc);
|
| DCHECK_IMPLIES(
|
| @@ -3358,16 +3392,6 @@
|
| }
|
| iterator.Advance();
|
| }
|
| - }
|
| -
|
| - // At this stage, we collected blocks needing a spill operand from
|
| - // ConnectRanges and from ResolveControlFlow. Time to commit the spills for
|
| - // deferred blocks.
|
| - for (TopLevelLiveRange* top : data()->live_ranges()) {
|
| - if (top == nullptr || top->IsEmpty() ||
|
| - !top->IsSpilledOnlyInDeferredBlocks())
|
| - continue;
|
| - CommitSpillsInDeferredBlocks(top, finder.ArrayFor(top->vreg()), local_zone);
|
| }
|
| }
|
|
|
| @@ -3406,7 +3430,7 @@
|
| LifetimePosition pos = second_range->Start();
|
| // Add gap move if the two live ranges touch and there is no block
|
| // boundary.
|
| - if (second_range->spilled()) continue;
|
| + if (!connect_spilled && second_range->spilled()) continue;
|
| if (first_range->End() != pos) continue;
|
| if (data()->IsBlockBoundary(pos) &&
|
| !CanEagerlyResolveControlFlow(GetInstructionBlock(code(), pos))) {
|
| @@ -3418,16 +3442,6 @@
|
| bool delay_insertion = false;
|
| Instruction::GapPosition gap_pos;
|
| int gap_index = pos.ToInstructionIndex();
|
| - if (connect_spilled && !prev_operand.IsAnyRegister() &&
|
| - cur_operand.IsAnyRegister()) {
|
| - const InstructionBlock* block = code()->GetInstructionBlock(gap_index);
|
| - DCHECK(block->IsDeferred());
|
| - // Performing a reload in this block, meaning the spill operand must
|
| - // be defined here.
|
| - top_range->GetListOfBlocksRequiringSpillOperands()->Add(
|
| - block->rpo_number().ToInt());
|
| - }
|
| -
|
| if (pos.IsGapPosition()) {
|
| gap_pos = pos.IsStart() ? Instruction::START : Instruction::END;
|
| } else {
|
| @@ -3438,7 +3452,7 @@
|
| }
|
| gap_pos = delay_insertion ? Instruction::END : Instruction::START;
|
| }
|
| - // Reloads or spills for spilled in deferred blocks ranges must happen
|
| + // Fills or spills for spilled in deferred blocks ranges must happen
|
| // only in deferred blocks.
|
| DCHECK_IMPLIES(
|
| connect_spilled &&
|
| @@ -3489,73 +3503,6 @@
|
| }
|
|
|
|
|
| -void LiveRangeConnector::CommitSpillsInDeferredBlocks(
|
| - TopLevelLiveRange* range, LiveRangeBoundArray* array, Zone* temp_zone) {
|
| - DCHECK(range->IsSpilledOnlyInDeferredBlocks());
|
| - DCHECK(!range->spilled());
|
| -
|
| - InstructionSequence* code = data()->code();
|
| - InstructionOperand spill_operand = range->GetSpillRangeOperand();
|
| -
|
| - TRACE("Live Range %d will be spilled only in deferred blocks.\n",
|
| - range->vreg());
|
| - // If we have ranges that aren't spilled but require the operand on the stack,
|
| - // make sure we insert the spill.
|
| - for (const LiveRange* child = range; child != nullptr;
|
| - child = child->next()) {
|
| - for (const UsePosition* pos = child->first_pos(); pos != nullptr;
|
| - pos = pos->next()) {
|
| - if (pos->type() != UsePositionType::kRequiresSlot && !child->spilled())
|
| - continue;
|
| - range->AddBlockRequiringSpillOperand(
|
| - code->GetInstructionBlock(pos->pos().ToInstructionIndex())
|
| - ->rpo_number());
|
| - }
|
| - }
|
| -
|
| - ZoneQueue<int> worklist(temp_zone);
|
| -
|
| - for (BitVector::Iterator iterator(
|
| - range->GetListOfBlocksRequiringSpillOperands());
|
| - !iterator.Done(); iterator.Advance()) {
|
| - worklist.push(iterator.Current());
|
| - }
|
| -
|
| - // Seek the deferred blocks that dominate locations requiring spill operands,
|
| - // and spill there. We only need to spill at the start of such blocks.
|
| - BitVector done_blocks(
|
| - range->GetListOfBlocksRequiringSpillOperands()->length(), temp_zone);
|
| - while (!worklist.empty()) {
|
| - int block_id = worklist.front();
|
| - worklist.pop();
|
| - if (done_blocks.Contains(block_id)) continue;
|
| - done_blocks.Add(block_id);
|
| - const InstructionBlock* spill_block =
|
| - code->InstructionBlockAt(RpoNumber::FromInt(block_id));
|
| -
|
| - for (const RpoNumber& pred : spill_block->predecessors()) {
|
| - const InstructionBlock* pred_block = code->InstructionBlockAt(pred);
|
| -
|
| - if (pred_block->IsDeferred()) {
|
| - worklist.push(pred_block->rpo_number().ToInt());
|
| - } else {
|
| - LifetimePosition pred_end =
|
| - LifetimePosition::InstructionFromInstructionIndex(
|
| - pred_block->last_instruction_index());
|
| -
|
| - LiveRangeBound* bound = array->Find(pred_end);
|
| -
|
| - InstructionOperand pred_op = bound->range_->GetAssignedOperand();
|
| -
|
| - data()->AddGapMove(spill_block->first_instruction_index(),
|
| - Instruction::GapPosition::START, pred_op,
|
| - spill_operand);
|
| - }
|
| - }
|
| - }
|
| -}
|
| -
|
| -
|
| } // namespace compiler
|
| } // namespace internal
|
| } // namespace v8
|
|
|