| Index: src/compiler/coalesced-live-ranges.cc
|
| diff --git a/src/compiler/coalesced-live-ranges.cc b/src/compiler/coalesced-live-ranges.cc
|
| index e81f5518bd636908b93cd2a6bfc57f4f8f64e5f0..44dd336c83fc81b181199cad3cdff071e0e95018 100644
|
| --- a/src/compiler/coalesced-live-ranges.cc
|
| +++ b/src/compiler/coalesced-live-ranges.cc
|
| @@ -10,136 +10,131 @@ namespace v8 {
|
| namespace internal {
|
| namespace compiler {
|
|
|
| -#define TRACE(...) \
|
| - do { \
|
| - if (FLAG_trace_alloc) PrintF(__VA_ARGS__); \
|
| - } while (false)
|
|
|
| +LiveRangeConflictIterator::LiveRangeConflictIterator(const LiveRange* range,
|
| + IntervalStore* storage)
|
| + : query_(range->first_interval()),
|
| + pos_(storage->end()),
|
| + intervals_(storage) {
|
| + MovePosAndQueryToFirstConflict();
|
| +}
|
|
|
| -const float CoalescedLiveRanges::kAllocatedRangeMultiplier = 10.0;
|
|
|
| -void CoalescedLiveRanges::AllocateRange(LiveRange* range) {
|
| - UpdateWeightAtAllocation(range);
|
| - for (auto interval = range->first_interval(); interval != nullptr;
|
| - interval = interval->next()) {
|
| - storage().insert({interval->start(), interval->end(), range});
|
| - }
|
| +LiveRange* LiveRangeConflictIterator::Current() const {
|
| + if (IsFinished()) return nullptr;
|
| + return pos_->range_;
|
| }
|
|
|
|
|
| -void CoalescedLiveRanges::Remove(LiveRange* range) {
|
| - for (auto interval = range->first_interval(); interval != nullptr;
|
| - interval = interval->next()) {
|
| - storage().erase({interval->start(), interval->end(), nullptr});
|
| - }
|
| - range->UnsetAssignedRegister();
|
| -}
|
| +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;
|
| + }
|
|
|
| -float CoalescedLiveRanges::GetMaximumConflictingWeight(
|
| - const LiveRange* range) const {
|
| - float ret = LiveRange::kInvalidWeight;
|
| - auto end = storage().end();
|
| - for (auto query = range->first_interval(); query != nullptr;
|
| - query = query->next()) {
|
| - auto conflict = GetFirstConflict(query);
|
| -
|
| - if (conflict == end) continue;
|
| - for (; QueryIntersectsAllocatedInterval(query, conflict); ++conflict) {
|
| - // It is possible we'll visit the same range multiple times, because
|
| - // successive (not necessarily consecutive) intervals belong to the same
|
| - // range, or because different intervals of the query range have the same
|
| - // range as conflict.
|
| - DCHECK_NE(conflict->range->weight(), LiveRange::kInvalidWeight);
|
| - ret = Max(ret, conflict->range->weight());
|
| - if (ret == LiveRange::kMaxWeight) break;
|
| + 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_;
|
| }
|
| }
|
| - return ret;
|
| + if (pos_ == end || !QueryIntersectsAllocatedInterval()) {
|
| + pos_ = end;
|
| + }
|
| }
|
|
|
|
|
| -void CoalescedLiveRanges::EvictAndRescheduleConflicts(
|
| - LiveRange* range, AllocationScheduler* scheduler) {
|
| - auto end = storage().end();
|
| -
|
| - for (auto query = range->first_interval(); query != nullptr;
|
| - query = query->next()) {
|
| - auto conflict = GetFirstConflict(query);
|
| - if (conflict == end) continue;
|
| - while (QueryIntersectsAllocatedInterval(query, conflict)) {
|
| - LiveRange* range_to_evict = conflict->range;
|
| - // Bypass successive intervals belonging to the same range, because we're
|
| - // about to remove this range, and we don't want the storage iterator to
|
| - // become invalid.
|
| - while (conflict != end && conflict->range == range_to_evict) {
|
| - ++conflict;
|
| - }
|
| -
|
| - DCHECK(range_to_evict->HasRegisterAssigned());
|
| - CHECK(!range_to_evict->IsFixed());
|
| - Remove(range_to_evict);
|
| - UpdateWeightAtEviction(range_to_evict);
|
| - TRACE("Evicted range %d.\n", range_to_evict->id());
|
| - scheduler->Schedule(range_to_evict);
|
| +void LiveRangeConflictIterator::MovePosAndQueryToFirstConflict() {
|
| + auto end = intervals_->end();
|
| + for (; query_ != nullptr; query_ = query_->next()) {
|
| + MovePosToFirstConflictForQuery();
|
| + if (pos_ != end) {
|
| + DCHECK(QueryIntersectsAllocatedInterval());
|
| + return;
|
| }
|
| }
|
| +
|
| + Invalidate();
|
| }
|
|
|
|
|
| -bool CoalescedLiveRanges::VerifyAllocationsAreValid() const {
|
| - LifetimePosition last_end = LifetimePosition::GapFromInstructionIndex(0);
|
| - for (auto i : storage_) {
|
| - if (i.start < last_end) {
|
| - return false;
|
| - }
|
| - last_end = i.end;
|
| +void LiveRangeConflictIterator::IncrementPosAndSkipOverRepetitions() {
|
| + auto end = intervals_->end();
|
| + DCHECK(pos_ != end);
|
| + LiveRange* current_conflict = Current();
|
| + while (pos_ != end && pos_->range_ == current_conflict) {
|
| + ++pos_;
|
| }
|
| - return true;
|
| }
|
|
|
|
|
| -void CoalescedLiveRanges::UpdateWeightAtAllocation(LiveRange* range) {
|
| - DCHECK_NE(range->weight(), LiveRange::kInvalidWeight);
|
| - range->set_weight(range->weight() * kAllocatedRangeMultiplier);
|
| +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();
|
| }
|
|
|
|
|
| -void CoalescedLiveRanges::UpdateWeightAtEviction(LiveRange* range) {
|
| - DCHECK_NE(range->weight(), LiveRange::kInvalidWeight);
|
| - range->set_weight(range->weight() / kAllocatedRangeMultiplier);
|
| +LiveRangeConflictIterator CoalescedLiveRanges::GetConflicts(
|
| + const LiveRange* range) {
|
| + return LiveRangeConflictIterator(range, &intervals());
|
| }
|
|
|
|
|
| -CoalescedLiveRanges::interval_iterator CoalescedLiveRanges::GetFirstConflict(
|
| - const UseInterval* query) const {
|
| - DCHECK(query != nullptr);
|
| - auto end = storage().end();
|
| - LifetimePosition q_start = query->start();
|
| - LifetimePosition q_end = query->end();
|
| -
|
| - if (storage().empty() || storage().rbegin()->end <= q_start ||
|
| - storage().begin()->start >= q_end) {
|
| - return end;
|
| +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);
|
| }
|
| +}
|
|
|
| - auto ret = storage().upper_bound(AsAllocatedInterval(q_start));
|
| - // ret 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 (ret != storage().begin()) {
|
| - --ret;
|
| - if (!QueryIntersectsAllocatedInterval(query, ret)) {
|
| - // The interval behind wasn't intersecting, so move back.
|
| - ++ret;
|
| +
|
| +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_;
|
| }
|
| - if (ret != end && QueryIntersectsAllocatedInterval(query, ret)) return ret;
|
| - return end;
|
| + return true;
|
| }
|
|
|
|
|
|
|