Index: src/compiler/coalesced-live-ranges.h |
diff --git a/src/compiler/coalesced-live-ranges.h b/src/compiler/coalesced-live-ranges.h |
index f12517203f844e4a71a68e77eaff21eb16f67eff..e617c0a25194bd38b43f93dc8a2c99b5307f29b5 100644 |
--- a/src/compiler/coalesced-live-ranges.h |
+++ b/src/compiler/coalesced-live-ranges.h |
@@ -13,8 +13,96 @@ namespace internal { |
namespace compiler { |
-class AllocationScheduler; |
+// Implementation detail for CoalescedLiveRanges. |
+struct AllocatedInterval { |
+ AllocatedInterval(LifetimePosition start, LifetimePosition end, |
+ LiveRange* range) |
+ : start_(start), end_(end), range_(range) {} |
+ |
+ LifetimePosition start_; |
+ LifetimePosition end_; |
+ LiveRange* range_; |
+ bool operator<(const AllocatedInterval& other) const { |
+ return start_ < other.start_; |
+ } |
+ bool operator>(const AllocatedInterval& other) const { |
+ return start_ > other.start_; |
+ } |
+}; |
+typedef ZoneSet<AllocatedInterval> IntervalStore; |
+ |
+ |
+// An iterator over conflicts of a live range, obtained from CoalescedLiveRanges |
+// The design supports two main scenarios (see GreedyAllocator): |
+// (1) observing each conflicting range, without mutating the allocations, and |
+// (2) observing each conflicting range, and then moving to the next, after |
+// removing the current conflict. |
+class LiveRangeConflictIterator { |
+ public: |
+ // Current conflict. nullptr if no conflicts, or if we reached the end of |
+ // conflicts. |
+ LiveRange* Current() const; |
+ |
+ // Get the next conflict. Caller should handle non-consecutive repetitions of |
+ // the same range. |
+ LiveRange* GetNext() { return InternalGetNext(false); } |
+ |
+ // Get the next conflict, after evicting the current one. Caller may expect |
+ // to never observe the same live range more than once. |
+ LiveRange* RemoveCurrentAndGetNext() { return InternalGetNext(true); } |
+ |
+ private: |
+ friend class CoalescedLiveRanges; |
+ |
+ typedef IntervalStore::const_iterator interval_iterator; |
+ LiveRangeConflictIterator(const LiveRange* range, IntervalStore* store); |
+ |
+ // Move the store iterator to first interval intersecting query. Since the |
+ // intervals are sorted, subsequent intervals intersecting query follow. May |
+ // leave the store iterator at "end", meaning that the current query does not |
+ // have an intersection. |
+ void MovePosToFirstConflictForQuery(); |
+ |
+ // Move both query and store iterator to the first intersection, if any. If |
+ // none, then it invalidates the iterator (IsFinished() == true) |
+ void MovePosAndQueryToFirstConflict(); |
+ // Increment pos and skip over intervals belonging to the same range we |
+ // started with (i.e. Current() before the call). It is possible that range |
+ // will be seen again, but not consecutively. |
+ void IncrementPosAndSkipOverRepetitions(); |
+ |
+ // Common implementation used by both GetNext as well as |
+ // ClearCurrentAndGetNext. |
+ LiveRange* InternalGetNext(bool clean_behind); |
+ |
+ bool IsFinished() const { return query_ == nullptr; } |
+ |
+ static AllocatedInterval AsAllocatedInterval(LifetimePosition pos) { |
+ return AllocatedInterval(pos, LifetimePosition::Invalid(), nullptr); |
+ } |
+ |
+ // Intersection utilities. |
+ static bool Intersects(LifetimePosition a_start, LifetimePosition a_end, |
+ LifetimePosition b_start, LifetimePosition b_end) { |
+ return a_start < b_end && b_start < a_end; |
+ } |
+ |
+ bool QueryIntersectsAllocatedInterval() const { |
+ DCHECK(query_ != nullptr); |
+ return pos_ != intervals_->end() && |
+ Intersects(query_->start(), query_->end(), pos_->start_, pos_->end_); |
+ } |
+ |
+ void Invalidate() { |
+ query_ = nullptr; |
+ pos_ = intervals_->end(); |
+ } |
+ |
+ const UseInterval* query_; |
+ interval_iterator pos_; |
+ IntervalStore* intervals_; |
+}; |
// Collection of live ranges allocated to the same register. |
// It supports efficiently finding all conflicts for a given, non-allocated |
@@ -30,45 +118,27 @@ class AllocationScheduler; |
// traversal of conflicts. |
class CoalescedLiveRanges : public ZoneObject { |
public: |
- explicit CoalescedLiveRanges(Zone* zone) : storage_(zone) {} |
- void clear() { storage_.clear(); } |
+ explicit CoalescedLiveRanges(Zone* zone) : intervals_(zone) {} |
+ void clear() { intervals_.clear(); } |
- bool empty() const { return storage_.empty(); } |
+ bool empty() const { return intervals_.empty(); } |
- // Returns kInvalidWeight if there are no conflicts, or the largest weight of |
- // a range conflicting with the given range. |
- float GetMaximumConflictingWeight(const LiveRange* range) const; |
+ // Iterate over each live range conflicting with the provided one. |
+ // The same live range may be observed multiple, but non-consecutive times. |
+ LiveRangeConflictIterator GetConflicts(const LiveRange* range); |
- // Evicts all conflicts of the given range, and reschedules them with the |
- // provided scheduler. |
- void EvictAndRescheduleConflicts(LiveRange* range, |
- AllocationScheduler* scheduler); |
// Allocates a range with a pre-calculated candidate weight. |
void AllocateRange(LiveRange* range); |
- // TODO(mtrofin): remove this in favor of comprehensive unit tests. |
- bool VerifyAllocationsAreValid() const; |
+ // Unit testing API, verifying that allocated intervals do not overlap. |
+ bool VerifyAllocationsAreValidForTesting() const; |
private: |
static const float kAllocatedRangeMultiplier; |
- // Storage detail for CoalescedLiveRanges. |
- struct AllocatedInterval { |
- LifetimePosition start; |
- LifetimePosition end; |
- LiveRange* range; |
- bool operator<(const AllocatedInterval& other) const { |
- return start < other.start; |
- } |
- bool operator>(const AllocatedInterval& other) const { |
- return start > other.start; |
- } |
- }; |
- typedef ZoneSet<AllocatedInterval> IntervalStore; |
- typedef IntervalStore::const_iterator interval_iterator; |
- IntervalStore& storage() { return storage_; } |
- const IntervalStore& storage() const { return storage_; } |
+ IntervalStore& intervals() { return intervals_; } |
+ const IntervalStore& intervals() const { return intervals_; } |
// Augment the weight of a range that is about to be allocated. |
static void UpdateWeightAtAllocation(LiveRange* range); |
@@ -76,29 +146,8 @@ class CoalescedLiveRanges : public ZoneObject { |
// Reduce the weight of a range that has lost allocation. |
static void UpdateWeightAtEviction(LiveRange* range); |
- // Intersection utilities. |
- static bool Intersects(LifetimePosition a_start, LifetimePosition a_end, |
- LifetimePosition b_start, LifetimePosition b_end) { |
- return a_start < b_end && b_start < a_end; |
- } |
- static AllocatedInterval AsAllocatedInterval(LifetimePosition pos) { |
- return {pos, LifetimePosition::Invalid(), nullptr}; |
- } |
- |
- bool QueryIntersectsAllocatedInterval(const UseInterval* query, |
- interval_iterator& pos) const { |
- DCHECK(query != nullptr); |
- return pos != storage().end() && |
- Intersects(query->start(), query->end(), pos->start, pos->end); |
- } |
- |
- void Remove(LiveRange* range); |
- |
- // Get the first interval intersecting query. Since the intervals are sorted, |
- // subsequent intervals intersecting query follow. |
- interval_iterator GetFirstConflict(const UseInterval* query) const; |
- IntervalStore storage_; |
+ IntervalStore intervals_; |
DISALLOW_COPY_AND_ASSIGN(CoalescedLiveRanges); |
}; |