|
|
|
// Copyright 2015 the V8 project authors. All rights reserved.
|
|
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
|
|
// found in the LICENSE file.
|
|
|
|
|
|
|
|
#include "src/compiler/coalesced-live-ranges.h"
|
|
|
|
#include "src/compiler/greedy-allocator.h"
|
|
|
|
#include "src/compiler/register-allocator.h"
|
|
|
|
|
|
|
|
namespace v8 {
|
|
|
|
namespace internal {
|
|
|
|
namespace compiler {
|
|
|
|
|
|
|
|
|
|
|
|
LiveRangeConflictIterator::LiveRangeConflictIterator(const LiveRange* range,
|
|
|
|
IntervalStore* storage)
|
|
|
|
: query_(range->first_interval()),
|
|
|
|
pos_(storage->end()),
|
|
|
|
intervals_(storage) {
|
|
|
|
MovePosAndQueryToFirstConflict();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
LiveRange* LiveRangeConflictIterator::Current() const {
|
|
|
|
if (IsFinished()) return nullptr;
|
|
|
|
return pos_->range_;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void LiveRangeConflictIterator::MovePosToFirstConflictForQuery() {
|
|
|
|
DCHECK(query_ != nullptr);
|
|
|
|
auto end = intervals_->end();
|
|
|
|
LifetimePosition q_start = query_->start();
|
|
|
|
LifetimePosition q_end = query_->end();
|
|
|
|
|
|
|
|
if (intervals_->empty() || intervals_->rbegin()->end_ <= q_start ||
|
|
|
|
intervals_->begin()->start_ >= q_end) {
|
|
|
|
pos_ = end;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
pos_ = intervals_->upper_bound(AsAllocatedInterval(q_start));
|
|
|
|
// pos is either at the end (no start strictly greater than q_start) or
|
|
|
|
// at some position with the aforementioned property. In either case, the
|
|
|
|
// allocated interval before this one may intersect our query:
|
|
|
|
// either because, although it starts before this query's start, it ends
|
|
|
|
// after; or because it starts exactly at the query start. So unless we're
|
|
|
|
// right at the beginning of the storage - meaning the first allocated
|
|
|
|
// interval is also starting after this query's start - see what's behind.
|
|
|
|
if (pos_ != intervals_->begin()) {
|
|
|
|
--pos_;
|
|
|
|
if (!QueryIntersectsAllocatedInterval()) {
|
|
|
|
// The interval behind wasn't intersecting, so move back.
|
|
|
|
++pos_;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (pos_ == end || !QueryIntersectsAllocatedInterval()) {
|
|
|
|
pos_ = end;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void LiveRangeConflictIterator::MovePosAndQueryToFirstConflict() {
|
|
|
|
auto end = intervals_->end();
|
|
|
|
for (; query_ != nullptr; query_ = query_->next()) {
|
|
|
|
MovePosToFirstConflictForQuery();
|
|
|
|
if (pos_ != end) {
|
|
|
|
DCHECK(QueryIntersectsAllocatedInterval());
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Invalidate();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void LiveRangeConflictIterator::IncrementPosAndSkipOverRepetitions() {
|
|
|
|
auto end = intervals_->end();
|
|
|
|
DCHECK(pos_ != end);
|
|
|
|
LiveRange* current_conflict = Current();
|
|
|
|
while (pos_ != end && pos_->range_ == current_conflict) {
|
|
|
|
++pos_;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
LiveRange* LiveRangeConflictIterator::InternalGetNext(bool clean_behind) {
|
|
|
|
if (IsFinished()) return nullptr;
|
|
|
|
|
|
|
|
LiveRange* to_clear = Current();
|
|
|
|
IncrementPosAndSkipOverRepetitions();
|
|
|
|
// At this point, pos_ is either at the end, or on an interval that doesn't
|
|
|
|
// correspond to the same range as to_clear. This interval may not even be
|
|
|
|
// a conflict.
|
|
|
|
if (clean_behind) {
|
|
|
|
// Since we parked pos_ on an iterator that won't be affected by removal,
|
|
|
|
// we can safely delete to_clear's intervals.
|
|
|
|
for (auto interval = to_clear->first_interval(); interval != nullptr;
|
|
|
|
interval = interval->next()) {
|
|
|
|
AllocatedInterval erase_key(interval->start(), interval->end(), nullptr);
|
|
|
|
intervals_->erase(erase_key);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// We may have parked pos_ at the end, or on a non-conflict. In that case,
|
|
|
|
// move to the next query and reinitialize pos and query. This may invalidate
|
|
|
|
// the iterator, if no more conflicts are available.
|
|
|
|
if (!QueryIntersectsAllocatedInterval()) {
|
|
|
|
query_ = query_->next();
|
|
|
|
MovePosAndQueryToFirstConflict();
|
|
|
|
}
|
|
|
|
return Current();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
LiveRangeConflictIterator CoalescedLiveRanges::GetConflicts(
|
|
|
|
const LiveRange* range) {
|
|
|
|
return LiveRangeConflictIterator(range, &intervals());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void CoalescedLiveRanges::AllocateRange(LiveRange* range) {
|
|
|
|
for (auto interval = range->first_interval(); interval != nullptr;
|
|
|
|
interval = interval->next()) {
|
|
|
|
AllocatedInterval to_insert(interval->start(), interval->end(), range);
|
|
|
|
intervals().insert(to_insert);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool CoalescedLiveRanges::VerifyAllocationsAreValidForTesting() const {
|
|
|
|
LifetimePosition last_end = LifetimePosition::GapFromInstructionIndex(0);
|
|
|
|
for (auto i : intervals_) {
|
|
|
|
if (i.start_ < last_end) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
last_end = i.end_;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
} // namespace compiler
|
|
|
|
} // namespace internal
|
|
|
|
} // namespace v8
|