Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(575)

Unified Diff: src/compiler/register-allocator.cc

Issue 1157663007: Greedy allocator: perf work (Closed) Base URL: https://chromium.googlesource.com/v8/v8.git@master
Patch Set: Created 5 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: src/compiler/register-allocator.cc
diff --git a/src/compiler/register-allocator.cc b/src/compiler/register-allocator.cc
index 08b71f2e40488181657a064418bff797d9186b9c..d1ad6b248525a9e70b47b7b1655f6780520c7348 100644
--- a/src/compiler/register-allocator.cc
+++ b/src/compiler/register-allocator.cc
@@ -47,6 +47,17 @@ const InstructionBlock* GetContainingLoop(const InstructionSequence* sequence,
}
+unsigned GetContainingLoopCount(const InstructionSequence* sequence,
+ const InstructionBlock* block) {
+ unsigned ret = 0;
+ for (auto cursor = GetContainingLoop(sequence, block); cursor != nullptr;
+ cursor = GetContainingLoop(sequence, cursor)) {
+ ++ret;
+ }
+ return ret;
+}
+
+
const InstructionBlock* GetInstructionBlock(const InstructionSequence* code,
LifetimePosition pos) {
return code->GetInstructionBlock(pos.ToInstructionIndex());
@@ -110,6 +121,22 @@ int GetByteWidth(MachineType machine_type) {
}
}
+
+int GetHintedRegister(LiveRange* range) {
+ int reg;
+ const UsePosition* hint = range->FirstHintPosition(&reg);
+ if (hint != nullptr) {
+ if (hint->HasOperand()) {
+ if (hint->operand()->IsDoubleStackSlot() ||
+ hint->operand()->IsStackSlot()) {
+ return -1;
+ }
+ }
+ return reg;
+ }
+ return -1;
+}
+
} // namespace
@@ -143,6 +170,60 @@ bool UsePosition::HasHint() const {
}
+void UsePosition::dump_hint(std::ostream& os,
+ const RegisterConfiguration* config) {
+ if (hint_ == nullptr) {
+ os << "H:nil";
+ return;
+ }
+ switch (HintTypeField::decode(flags_)) {
+ case UsePositionHintType::kNone:
+ os << "H:N";
+ return;
+ case UsePositionHintType::kUnresolved:
+ os << "H:U";
+ return;
+ case UsePositionHintType::kUsePos: {
+ auto use_pos = reinterpret_cast<UsePosition*>(hint_);
+ int assigned_register = AssignedRegisterField::decode(use_pos->flags_);
+ if (assigned_register == kUnassignedRegister) {
+ os << "H:Use(R ?";
+ } else {
+ os << "H:Use(R " << assigned_register;
+ }
+ if (use_pos->HasOperand()) {
+ PrintableInstructionOperand pio{config, *use_pos->operand()};
+ os << " | " << pio;
+ }
+ os << ")";
+ return;
+ }
+ case UsePositionHintType::kOperand: {
+ auto operand = reinterpret_cast<InstructionOperand*>(hint_);
+ int assigned_register = AllocatedOperand::cast(operand)->index();
+ PrintableInstructionOperand pio{config, *operand};
+ os << "H:Op(R" << assigned_register << " | " << pio << ")";
+ return;
+ }
+ case UsePositionHintType::kPhi: {
+ auto phi = reinterpret_cast<RegisterAllocationData::PhiMapValue*>(hint_);
+ int assigned_register = phi->assigned_register();
+ PrintableInstructionOperand pio{config, phi->phi()->output()};
+ if (assigned_register == kUnassignedRegister) {
+ os << "H:Phi(R x";
+
+ } else {
+ os << "H:Phi(R" << assigned_register;
+ }
+ os << " | " << pio;
+ os << ")";
+ return;
+ }
+ }
+ UNREACHABLE();
+}
+
+
bool UsePosition::HintRegister(int* register_index) const {
if (hint_ == nullptr) return false;
switch (HintTypeField::decode(flags_)) {
@@ -264,11 +345,13 @@ LiveRange::LiveRange(int id, MachineType machine_type)
spills_at_definition_(nullptr),
current_interval_(nullptr),
last_processed_use_(nullptr),
- current_hint_position_(nullptr) {
+ current_hint_position_(nullptr),
+ group_(nullptr) {
Jarin 2015/06/12 04:09:11 Initialize size_ and weight_ here, please. It is s
DCHECK(AllocatedOperand::IsSupportedMachineType(machine_type));
bits_ = SpillTypeField::encode(SpillType::kNoSpillType) |
AssignedRegisterField::encode(kUnassignedRegister) |
MachineTypeField::encode(machine_type);
+ InvalidateWeightAndSize();
}
@@ -424,9 +507,10 @@ UsePosition* LiveRange::NextRegisterPosition(LifetimePosition start) const {
}
-bool LiveRange::CanBeSpilled(LifetimePosition pos) const {
- // We cannot spill a live range that has a use requiring a register
- // at the current or the immediate next position.
+bool LiveRange::CanBeSplit(LifetimePosition pos) const {
+ // We cannot split a live range that has a use requiring a register
+ // at the current or the immediate next position, because there would be
+ // no gap where to insert a parallel move.
auto use_pos = NextRegisterPosition(pos);
if (use_pos == nullptr) return true;
return use_pos->pos() > pos.NextStart().End();
@@ -572,6 +656,7 @@ void LiveRange::SplitAt(LifetimePosition position, LiveRange* result,
result->parent_ = (parent_ == nullptr) ? this : parent_;
result->next_ = next_;
next_ = result;
+ InvalidateWeightAndSize();
#ifdef DEBUG
Verify();
@@ -763,6 +848,81 @@ LifetimePosition LiveRange::FirstIntersection(LiveRange* other) const {
}
+LifetimePosition LiveRange::GetFirstSplittablePosition() {
+ for (auto c = first_pos(); c != nullptr; c = c->next()) {
Jarin 2015/06/12 04:09:11 Use the real type here.
+ LifetimePosition pos;
+ if (CanBeSpilled(c->pos())) {
Jarin 2015/06/12 04:09:11 I suppose you wanted to use your new CanBeSplit me
+ pos = c->pos();
+ } else {
+ auto after = c->pos().NextStart();
+ if (Covers(after) && CanBeSpilled(after)) {
+ pos = after;
+ }
+ }
+ if (pos.IsValid() && Start() < pos && pos < End()) {
+ return pos;
+ }
+ }
+ return LifetimePosition::Invalid();
+}
+
+
+void LiveRange::RecalculateSize() {
+ size_ = 0;
+ for (auto cursor = first_interval(); cursor != nullptr;
+ cursor = cursor->next()) {
+ size_ += cursor->end().value() - cursor->start().value();
+ }
+
+ DCHECK_NE(0U, static_cast<unsigned>(size_));
+}
+
+
+const float LiveRange::kMaxWeight = FLT_MAX;
+const float LiveRange::kUseInLoopWeightMultiplier = 10.0f;
+const float LiveRange::kPreferredRegisterWeightMultiplier = 10.0f;
+const float LiveRange::kInvalidWeight = -1.0f;
+
+
+void LiveRange::RecalculateWeight(InstructionSequence* code) {
+ LifetimePosition start = Start();
+
+ CHECK(!spilled());
+
+ if (IsFixed()) {
+ weight_ = kMaxWeight;
+ return;
+ }
+
+ if (first_interval()->next() == nullptr) {
+ bool can_be_split_or_spilled =
+ CanBeSpilled(start) || GetFirstSplittablePosition().IsValid();
+ if (!can_be_split_or_spilled) {
+ weight_ = kMaxWeight;
+ return;
+ }
+ }
+
+ float use_count = 0.0;
+ auto* pos = first_pos();
+
+ for (; pos != nullptr; pos = pos->next()) {
+ auto loop_count = GetContainingLoopCount(
Jarin 2015/06/12 04:09:10 Use the real type here.
+ code, code->GetInstructionBlock(pos->pos().ToInstructionIndex()));
+ use_count +=
+ std::pow(kUseInLoopWeightMultiplier, static_cast<float>(loop_count));
+ }
+
+
+ if (GetHintedRegister(this) >= 0 &&
+ GetHintedRegister(this) == assigned_register()) {
+ use_count *= kPreferredRegisterWeightMultiplier;
+ }
+
+ weight_ = use_count / static_cast<float>(GetSize());
+}
+
+
static bool AreUseIntervalsIntersecting(UseInterval* interval1,
UseInterval* interval2) {
while (interval1 != nullptr && interval2 != nullptr) {
@@ -782,35 +942,75 @@ static bool AreUseIntervalsIntersecting(UseInterval* interval1,
}
+void PrintIntervals(std::ostream& os, UseInterval* interval) {
+ while (interval != nullptr) {
+ os << '[' << interval->start() << ", " << interval->end() << ')'
+ << std::endl;
+ interval = interval->next();
+ }
+}
+
std::ostream& operator<<(std::ostream& os,
const PrintableLiveRange& printable_range) {
+ PrintableInstructionOperand pio;
+ pio.register_configuration_ = printable_range.register_configuration_;
+
const LiveRange* range = printable_range.range_;
os << "Range: " << range->id() << " ";
if (range->is_phi()) os << "phi ";
if (range->is_non_loop_phi()) os << "nlphi ";
+ if (range->HasRegisterAssigned())
+ os << "R: " << range->assigned_register() << " ";
+ if (range->HasSpillOperand()) {
+ pio.op_ = *(range->GetSpillOperand());
+ os << "SOp: " << pio << " ";
+ }
+ if (range->HasSpillRange()) {
+ os << "SR: ";
+ if (range->GetSpillRange()->IsSlotAssigned()) {
+ os << range->GetSpillRange()->assigned_slot() << " ";
+ } else {
+ os << "x ";
+ }
+ }
os << "{" << std::endl;
auto interval = range->first_interval();
auto use_pos = range->first_pos();
- PrintableInstructionOperand pio;
- pio.register_configuration_ = printable_range.register_configuration_;
while (use_pos != nullptr) {
- pio.op_ = *use_pos->operand();
- os << pio << use_pos->pos() << " ";
+ os << "[";
+ if (use_pos->HasOperand()) {
+ pio.op_ = *use_pos->operand();
+ os << pio << use_pos->pos() << " ";
+ } else {
+ os << "<no_op> ";
+ }
+ use_pos->dump_hint(os, printable_range.register_configuration_);
+ os << "] ";
use_pos = use_pos->next();
}
os << std::endl;
- while (interval != nullptr) {
- os << '[' << interval->start() << ", " << interval->end() << ')'
- << std::endl;
- interval = interval->next();
- }
+ PrintIntervals(os, interval);
os << "}";
return os;
}
+std::ostream& operator<<(std::ostream& os, SpillRange* range) {
+ if (range->IsSlotAssigned()) {
+ os << "Slot: " << range->assigned_slot();
+ } else {
+ os << "Unassigned Slot.";
+ }
+ os << std::endl;
+ os << "{";
+ PrintIntervals(os, range->interval());
+ os << "}" << std::endl;
+ return os;
+}
+
+
SpillRange::SpillRange(LiveRange* parent, Zone* zone)
: live_ranges_(zone), assigned_slot_(kUnassignedSlot) {
DCHECK(!parent->IsChild());
@@ -1013,8 +1213,8 @@ LiveRange* RegisterAllocationData::NewChildRangeFor(LiveRange* range) {
RegisterAllocationData::PhiMapValue* RegisterAllocationData::InitializePhiMap(
const InstructionBlock* block, PhiInstruction* phi) {
- auto map_value = new (allocation_zone())
- RegisterAllocationData::PhiMapValue(phi, block, allocation_zone());
+ auto map_value =
+ new (allocation_zone()) PhiMapValue(phi, block, allocation_zone());
auto res =
phi_map_.insert(std::make_pair(phi->virtual_register(), map_value));
DCHECK(res.second);
@@ -1844,6 +2044,8 @@ LifetimePosition RegisterAllocator::FindOptimalSpillingPos(
void RegisterAllocator::Spill(LiveRange* range) {
DCHECK(!range->spilled());
+ stats_.spills++;
+
TRACE("Spilling live range %d\n", range->id());
auto first = range->TopLevel();
if (first->HasNoSpillType()) {
@@ -1871,10 +2073,12 @@ LinearScanAllocator::LinearScanAllocator(RegisterAllocationData* data,
void LinearScanAllocator::AllocateRegisters() {
+ stats_.reset();
DCHECK(unhandled_live_ranges().empty());
DCHECK(active_live_ranges().empty());
DCHECK(inactive_live_ranges().empty());
-
+ TRACE("Begin allocating function %s with the Linear Allocator\n",
+ data()->debug_name());
for (auto range : data()->live_ranges()) {
if (range == nullptr) continue;
if (range->kind() == mode()) {
@@ -1951,15 +2155,22 @@ void LinearScanAllocator::AllocateRegisters() {
DCHECK(!current->HasRegisterAssigned() && !current->spilled());
bool result = TryAllocateFreeReg(current);
- if (!result) AllocateBlockedReg(current);
+ if (!result) {
+ TRACE("Failed to allocate a free reg for %d\n", current->id());
+ AllocateBlockedReg(current);
+ }
if (current->HasRegisterAssigned()) {
AddToActive(current);
+ } else {
+ TRACE("Failed to assign register to %d\n", current->id());
}
}
+ TRACE("End allocating function %s with the Linear Allocator\n",
+ data()->debug_name());
}
-const char* LinearScanAllocator::RegisterName(int allocation_index) const {
+const char* RegisterAllocator::RegisterName(int allocation_index) const {
if (mode() == GENERAL_REGISTERS) {
return data()->config()->general_register_name(allocation_index);
} else {
@@ -2127,6 +2338,10 @@ bool LinearScanAllocator::TryAllocateFreeReg(LiveRange* current) {
if (pos < current->End()) {
// Register reg is available at the range start but becomes blocked before
// the range end. Split current at position where it becomes blocked.
+ TRACE(
+ "Register %d is available at the range start but becomes blocked "
+ "before range %d end\n",
+ reg, current->id());
auto tail = SplitRangeAt(current, pos);
AddToUnhandledSorted(tail);
}
@@ -2199,6 +2414,8 @@ void LinearScanAllocator::AllocateBlockedReg(LiveRange* current) {
if (pos < register_use->pos()) {
// All registers are blocked before the first use that requires a register.
// Spill starting part of live range up to that use.
+ TRACE("All registers are blocked before the first use for %d\n",
+ current->id());
SpillBetween(current, current->Start(), register_use->pos());
return;
}
@@ -2206,6 +2423,8 @@ void LinearScanAllocator::AllocateBlockedReg(LiveRange* current) {
if (block_pos[reg] < current->End()) {
// Register becomes blocked before the current range end. Split before that
// position.
+ TRACE("Register %d becomes blocked before end of range %d\n", reg,
+ current->id());
LiveRange* tail =
SplitBetween(current, current->Start(), block_pos[reg].Start());
AddToUnhandledSorted(tail);
@@ -2234,6 +2453,8 @@ void LinearScanAllocator::SplitAndSpillIntersecting(LiveRange* current) {
auto next_pos = range->NextRegisterPosition(current->Start());
auto spill_pos = FindOptimalSpillingPos(range, split_pos);
if (next_pos == nullptr) {
+ TRACE("SplitAndSpillIntersecting (1). Range %d, for %d\n", range->id(),
+ current->id());
SpillAfter(range, spill_pos);
} else {
// When spilling between spill_pos and next_pos ensure that the range
@@ -2244,6 +2465,8 @@ void LinearScanAllocator::SplitAndSpillIntersecting(LiveRange* current) {
// live-ranges: ranges are allocated in order of their start positions,
// ranges are retired from active/inactive when the start of the
// current live-range is larger than their end.
+ TRACE("SplitAndSpillIntersecting (2). Range %d, for %d\n", range->id(),
+ current->id());
SpillBetweenUntil(range, spill_pos, current->Start(), next_pos->pos());
}
ActiveToHandled(range);
@@ -2259,9 +2482,13 @@ void LinearScanAllocator::SplitAndSpillIntersecting(LiveRange* current) {
if (next_intersection.IsValid()) {
UsePosition* next_pos = range->NextRegisterPosition(current->Start());
if (next_pos == nullptr) {
+ TRACE("SplitAndSpillIntersecting (3). Range %d, for %d\n",
+ range->id(), current->id());
SpillAfter(range, split_pos);
} else {
next_intersection = Min(next_intersection, next_pos->pos());
+ TRACE("SplitAndSpillIntersecting (4). Range %d, for %d\n",
+ range->id(), current->id());
SpillBetween(range, split_pos, next_intersection);
}
InactiveToHandled(range);
@@ -2398,77 +2625,196 @@ void LinearScanAllocator::SpillBetweenUntil(LiveRange* range,
}
+conflict_iterator& conflict_iterator::operator++() {
+ DCHECK(storage_ != nullptr);
+
+ if (pos_ == storage_->end()) {
Jarin 2015/06/12 04:09:11 The iterator should always be in canonical form, n
+ // We're at the end - ensure we are in a canonical "end of iterator" form.
+ Invalidate();
+ return *this;
+ }
+ DCHECK(QueryIntersectsAllocatedInterval());
+ ++pos_;
+ if (pos_ == storage_->end()) {
+ // Advancing got us at the end of storage, so the iterator becomes "end".
+ Invalidate();
+ return *this;
+ }
+ if (!QueryIntersectsAllocatedInterval()) {
+ query_ = query_->next();
+ if (query_ == nullptr) {
+ Invalidate();
+ return *this;
+ }
+ // We may discover it is better to linearly skip over non-conflicting
+ // use intervals rather than re-do the seeking in InitializeForNewQuery.
+ // No profiling data yet on this one.
+ InitializeForNewQuery();
+ }
+ return *this;
+}
+
+
+bool operator==(const conflict_iterator& first,
+ const conflict_iterator& second) {
+ DCHECK_EQ(first.storage_, second.storage_);
+
+ return first.query_ == second.query_ && first.pos_ == second.pos_;
+}
+
+
+bool operator!=(const conflict_iterator& first,
+ const conflict_iterator& second) {
+ return !(first == second);
+}
+
+
+void conflict_iterator::InitializeForNewQuery() {
+ DCHECK(storage_ != nullptr);
+ DCHECK(query_ != nullptr);
+ // If empty or the last element starts before the query, we have no conflicts.
+ if (storage_->empty() || storage_->rbegin()->end <= query_->start()) {
+ Invalidate();
+ return;
+ }
+
+ for (; query_ != nullptr; query_ = query_->next()) {
+ LifetimePosition q_start = query_->start();
+ LifetimePosition q_end = query_->end();
+ if (storage_->begin()->start >= q_end) {
+ // Skip over queried use intervals that end before the first stored
+ // element.
+ continue;
+ }
+
+ pos_ = storage_->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_ != storage_->begin()) {
+ --pos_;
+ if (QueryIntersectsAllocatedInterval()) break;
+ // The interval behind wasn't intersecting, so move back.
+ ++pos_;
+ }
+ if (pos_ != storage_->end() && QueryIntersectsAllocatedInterval()) break;
+ }
+
+ // If we got here because we couldn't find an intersection and got to the last
+ // use interval query, we have no conflicts.
+ if (query_ == nullptr) {
+ Invalidate();
+ return;
+ }
+
+ SkipMatchedQueries();
+}
+
+
+// Advance query_ at the last use interval that still intersects/overlaps with
+// the current conflict, so that operator++ can pick up from there.
+void conflict_iterator::SkipMatchedQueries() {
+ DCHECK(query_ != nullptr);
+ DCHECK(QueryIntersectsAllocatedInterval());
+
+ for (; query_->next() != nullptr; query_ = query_->next()) {
+ if (!NextQueryIntersectsAllocatedInterval()) {
+ break;
+ }
+ }
+ DCHECK(query_ != nullptr);
+ DCHECK(QueryIntersectsAllocatedInterval());
+ DCHECK(query_->next() == nullptr || !NextQueryIntersectsAllocatedInterval());
+}
+
+
+// Collection of live ranges allocated to the same register.
+// It supports efficiently finding all conflicts for a given, non-allocated
+// range. See AllocatedInterval.
+// Allocated live ranges do not intersect. At most, individual use intervals
+// touch. We store, for a live range, an AllocatedInterval corresponding to each
+// of that range's UseIntervals. We keep the list of AllocatedIntervals sorted
+// by starts. Then, given the non-intersecting property, we know that
+// consecutive AllocatedIntervals have the property that the "smaller"'s end is
+// less or equal to the "larger"'s start.
+// This allows for quick (logarithmic complexity) identification of the first
+// AllocatedInterval to conflict with a given LiveRange, and then for efficient
+// traversal of conflicts. See also conflict_iterator.
class CoalescedLiveRanges : public ZoneObject {
public:
explicit CoalescedLiveRanges(Zone* zone) : storage_(zone) {}
- LiveRange* Find(UseInterval* query) {
- ZoneSplayTree<Config>::Locator locator;
+ void clear() { storage_.clear(); }
- if (storage_.Find(GetKey(query), &locator)) {
- return locator.value();
- }
- return nullptr;
+
+ conflict_iterator conflicts_begin(LiveRange* query) {
+ return GetFirstConflict(query->first_interval());
}
- // TODO(mtrofin): Change to void returning if we do not care if the interval
- // was previously added.
- bool Insert(LiveRange* range) {
- auto* interval = range->first_interval();
- while (interval != nullptr) {
- if (!Insert(interval, range)) return false;
- interval = interval->next();
+ conflict_iterator conflicts_end() {
+ return conflict_iterator::EndIteratorForStorage(&storage_);
+ }
+
+ // We assume it was determined that this range does not conflict with
+ // contained ranges.
+ void insert(LiveRange* range) {
+ for (auto interval = range->first_interval(); interval != nullptr;
+ interval = interval->next()) {
+ storage_.insert({interval->start(), interval->end(), range});
}
- return true;
}
- bool Remove(LiveRange* range) {
- bool ret = false;
- auto* segment = range->first_interval();
- while (segment != nullptr) {
- ret |= Remove(segment);
- segment = segment->next();
+ void remove(LiveRange* range) {
+ for (auto interval = range->first_interval(); interval != nullptr;
+ interval = interval->next()) {
+ storage_.erase({interval->start(), interval->end(), range});
}
- return ret;
}
- bool IsEmpty() { return storage_.is_empty(); }
+ bool empty() const { return storage_.empty(); }
- private:
- struct Config {
- typedef std::pair<int, int> Key;
- typedef LiveRange* Value;
- static const Key kNoKey;
- static Value NoValue() { return nullptr; }
- static int Compare(const Key& a, const Key& b) {
- if (a.second <= b.first) return -1;
- if (a.first >= b.second) return 1;
- return 0;
+ bool IsValid() {
+ LifetimePosition last_end = LifetimePosition::GapFromInstructionIndex(0);
+ for (auto i : storage_) {
+ if (i.start < last_end) {
+ return false;
+ }
+ last_end = i.end;
}
- };
-
- Config::Key GetKey(UseInterval* interval) {
- if (interval == nullptr) return std::make_pair(0, 0);
- return std::make_pair(interval->start().value(), interval->end().value());
+ return true;
}
- // TODO(mtrofin): Change to void returning if we do not care if the interval
- // was previously added.
- bool Insert(UseInterval* interval, LiveRange* range) {
- ZoneSplayTree<Config>::Locator locator;
- bool ret = storage_.Insert(GetKey(interval), &locator);
- if (ret) locator.set_value(range);
- return ret;
+ private:
+ conflict_iterator GetFirstConflict(UseInterval* query) {
+ return conflict_iterator(query, &storage_);
}
- bool Remove(UseInterval* key) { return storage_.Remove(GetKey(key)); }
-
- ZoneSplayTree<Config> storage_;
+ ZoneSet<AllocatedInterval, AllocatedInterval::Comparer> storage_;
Jarin 2015/06/12 04:09:11 storage_ -> intervals_, perhaps?
DISALLOW_COPY_AND_ASSIGN(CoalescedLiveRanges);
};
-const std::pair<int, int> CoalescedLiveRanges::Config::kNoKey = {0, 0};
+std::ostream& operator<<(std::ostream& os, const AllocatorStats& stats) {
+ os << "losses after eviction: " << stats.losses_after_eviction << std::endl;
+ os << "losses, no eviction: " << stats.losses_no_eviction << std::endl;
+ os << "spills: " << stats.spills << std::endl;
+ os << "wins: " << stats.wins << std::endl;
+ os << "split attempts: " << stats.good_split_attempts << std::endl;
+ os << "split successes: " << stats.good_split_successes << std::endl;
+ os << "splits above: " << stats.good_split_above << std::endl;
+ os << "splits below: " << stats.good_split_below << std::endl;
+ os << "smart splits above: " << stats.good_split_smart_above << std::endl;
+ os << "smart splits below: " << stats.good_split_smart_below << std::endl;
+ os << "groups allocated: " << stats.groups_allocated << std::endl;
+ os << "groups attempted: " << stats.groups_attempted << std::endl;
+ os << "groups succeeded: " << stats.groups_succeeded << std::endl;
+ return os;
+}
+
GreedyAllocator::GreedyAllocator(RegisterAllocationData* data,
RegisterKind kind, Zone* local_zone)
@@ -2478,78 +2824,27 @@ GreedyAllocator::GreedyAllocator(RegisterAllocationData* data,
queue_(local_zone) {}
-unsigned GreedyAllocator::GetLiveRangeSize(LiveRange* range) {
- auto interval = range->first_interval();
- if (interval == nullptr) return 0;
-
- unsigned size = 0;
- while (interval != nullptr) {
- size += (interval->end().value() - interval->start().value());
- interval = interval->next();
- }
-
- return size;
-}
-
void GreedyAllocator::AssignRangeToRegister(int reg_id, LiveRange* range) {
- allocations_[reg_id]->Insert(range);
+ allocations_[reg_id]->insert(range);
if (range->HasRegisterAssigned()) {
DCHECK_EQ(reg_id, range->assigned_register());
return;
}
+ TRACE("Assigning %s to range %d\n", RegisterName(reg_id), range->id());
range->set_assigned_register(reg_id);
range->SetUseHints(reg_id);
if (range->is_phi()) {
data()->GetPhiMapValueFor(range->id())->set_assigned_register(reg_id);
}
-}
-
-
-float GreedyAllocator::CalculateSpillWeight(LiveRange* range) {
- InstructionOperand* first_hint = nullptr;
- if (range->FirstHintPosition() != nullptr) {
- first_hint = range->FirstHintPosition()->operand();
- }
-
- if (range->IsFixed()) return std::numeric_limits<float>::max();
- bool spill;
- if (!FindProgressingSplitPosition(range, &spill).IsValid())
- return std::numeric_limits<float>::max();
-
- float multiplier = 1.0;
- if (first_hint != nullptr && first_hint->IsRegister()) {
- multiplier = 3.0;
- }
-
- unsigned use_count = 0;
- auto* pos = range->first_pos();
- while (pos != nullptr) {
- use_count++;
- pos = pos->next();
- }
-
- unsigned range_size = GetLiveRangeSize(range);
- DCHECK_NE(0U, range_size);
-
- return multiplier * static_cast<float>(use_count) /
- static_cast<float>(range_size);
-}
-
-
-float GreedyAllocator::CalculateMaxSpillWeight(
- const ZoneSet<LiveRange*>& ranges) {
- float max = 0.0;
- for (auto* r : ranges) {
- max = std::max(max, CalculateSpillWeight(r));
- }
- return max;
+ range->RecalculateWeight(code());
+ DCHECK(allocations_[reg_id]->IsValid());
}
void GreedyAllocator::Evict(LiveRange* range) {
- bool removed = allocations_[range->assigned_register()]->Remove(range);
- CHECK(removed);
+ TRACE("Evicting range %d from register %s\n", range->id(),
+ RegisterName(range->assigned_register()));
range->UnsetUseHints();
range->UnsetAssignedRegister();
if (range->is_phi()) {
@@ -2558,66 +2853,6 @@ void GreedyAllocator::Evict(LiveRange* range) {
}
-bool GreedyAllocator::TryAllocatePhysicalRegister(
- unsigned reg_id, LiveRange* range, ZoneSet<LiveRange*>* conflicting) {
- auto* segment = range->first_interval();
-
- auto* alloc_info = allocations_[reg_id];
- while (segment != nullptr) {
- if (auto* existing = alloc_info->Find(segment)) {
- DCHECK(existing->HasRegisterAssigned());
- conflicting->insert(existing);
- }
- segment = segment->next();
- }
- if (!conflicting->empty()) return false;
- // No conflicts means we can safely allocate this register to this range.
- AssignRangeToRegister(reg_id, range);
- return true;
-}
-
-
-int GreedyAllocator::GetHintedRegister(LiveRange* range) {
- int reg;
- if (range->FirstHintPosition(&reg) != nullptr) {
- return reg;
- }
- return -1;
-}
-
-
-bool GreedyAllocator::TryAllocate(LiveRange* current,
- ZoneSet<LiveRange*>* conflicting) {
- if (current->IsFixed()) {
- return TryAllocatePhysicalRegister(current->assigned_register(), current,
- conflicting);
- }
-
- int hinted_reg_id = GetHintedRegister(current);
- if (hinted_reg_id >= 0) {
- if (TryAllocatePhysicalRegister(hinted_reg_id, current, conflicting)) {
- return true;
- }
- }
-
- ZoneSet<LiveRange*> local_conflicts(local_zone());
- for (unsigned candidate_reg = 0; candidate_reg < allocations_.size();
- candidate_reg++) {
- if (hinted_reg_id >= 0 &&
- candidate_reg == static_cast<size_t>(hinted_reg_id))
- continue;
- local_conflicts.clear();
- if (TryAllocatePhysicalRegister(candidate_reg, current, &local_conflicts)) {
- conflicting->clear();
- return true;
- } else {
- conflicting->insert(local_conflicts.begin(), local_conflicts.end());
- }
- }
- return false;
-}
-
-
LiveRange* GreedyAllocator::SpillBetweenUntil(LiveRange* range,
LifetimePosition start,
LifetimePosition until,
@@ -2649,14 +2884,32 @@ LiveRange* GreedyAllocator::SpillBetweenUntil(LiveRange* range,
void GreedyAllocator::Enqueue(LiveRange* range) {
- if (range == nullptr || range->IsEmpty()) return;
- unsigned size = GetLiveRangeSize(range);
- TRACE("Enqueuing range %d\n", range->id());
- queue_.push(std::make_pair(size, range));
+ unsigned size = range->GetSize();
+ range->RecalculateWeight(code());
+ TRACE("Enqueuing range %d size %d\n", range->id(), size);
+ DCHECK(size > 0);
+ queue_.push({size, PQueueElem(range)});
+}
+
+
+// We treat groups of ranges as one, so that we try first to allocate
+// them all to the same register. If that fails, they get processed as
+// individual ranges.
+void GreedyAllocator::Enqueue(LiveRangeGroup* group) {
+ unsigned size = 0;
+ for (auto r : group->ranges()) {
+ size += r->GetSize();
+ r->RecalculateWeight(code());
+ }
+
+ DCHECK(size > 0);
+ TRACE("Enqueuing group of size %d\n", size);
+ queue_.push({size, PQueueElem(group)});
}
-bool GreedyAllocator::HandleSpillOperands(LiveRange* range) {
+bool GreedyAllocator::HandleSpillOperands(LiveRange* range,
+ LiveRange** remaining) {
auto position = range->Start();
TRACE("Processing interval %d start=%d\n", range->id(), position.value());
@@ -2675,24 +2928,314 @@ bool GreedyAllocator::HandleSpillOperands(LiveRange* range) {
} else if (pos->pos() > range->Start().NextStart()) {
// Do not spill live range eagerly if use position that can benefit from
// the register is too close to the start of live range.
- auto* reminder = SpillBetweenUntil(range, position, position, pos->pos());
- Enqueue(reminder);
+ *remaining = SpillBetweenUntil(range, position, position, pos->pos());
return true;
}
}
- return TryReuseSpillForPhi(range);
+ return false;
}
-void GreedyAllocator::AllocateRegisters() {
+// TODO(mtrofin): consider using CoalescedLiveRanges for grouping.
+bool LiveRangeGroup::CanAdd(LiveRange* r) {
+ for (auto member : ranges()) {
+ if (member->FirstIntersection(r).IsValid()) {
+ return false;
+ }
+ }
+ return true;
+}
+
+
+void LiveRangeGroup::TryMerge(LiveRangeGroup* other) {
+ DCHECK(other != nullptr);
+
+ bool can_merge = true;
+ for (auto r : ranges()) {
+ if (!other->CanAdd(r)) {
+ can_merge = false;
+ break;
+ }
+ }
+ if (can_merge) {
+ for (auto r : ranges()) {
+ other->ranges().insert(r);
+ r->SetGroup(other);
+ }
+ }
+}
+
+
+void TryGroup(LiveRange* r1, LiveRange* r2, Zone* local_zone) {
+ if (r1->group() == nullptr) {
+ if (r2->group() == nullptr) {
+ if (!r1->FirstIntersection(r2).IsValid()) {
+ LiveRangeGroup* grp = new (local_zone) LiveRangeGroup(local_zone);
+ grp->ranges().insert(r1);
+ grp->ranges().insert(r2);
+ r1->SetGroup(grp);
+ r2->SetGroup(grp);
+ }
+ return;
+ }
+ return TryGroup(r2, r1, local_zone);
+ }
+ DCHECK(r1->group() != nullptr);
+ if (r2->group() == nullptr) {
+ if (r1->group()->CanAdd(r2)) {
+ r1->group()->ranges().insert(r2);
+ r2->SetGroup(r1->group());
+ }
+ return;
+ }
+ r1->group()->TryMerge(r2->group());
+}
+
+
+void GreedyAllocator::GroupAndEnqueue() {
+ // Group phi inputs to the output. Ideally, they get all allocated to the same
+ // register, avoiding moves.
+ for (auto r : data()->live_ranges()) {
+ if (r == nullptr || r->IsEmpty() || r->kind() != mode()) continue;
+ if (r->is_phi()) {
+ auto phi_map = data()->GetPhiMapValueFor(r->id());
+ auto phi = phi_map->phi();
+ auto inputs = phi->operands();
+ for (auto i : inputs) {
+ LiveRange* in_range = data()->live_ranges()[i];
+ TryGroup(r, in_range, local_zone());
+ }
+ }
+ }
+
+ ZoneSet<LiveRangeGroup*> seen_groups(local_zone());
for (auto range : data()->live_ranges()) {
- if (range == nullptr) continue;
- if (range->kind() == mode()) {
- DCHECK(!range->HasRegisterAssigned() && !range->spilled());
- TRACE("Enqueueing live range %d to priority queue \n", range->id());
+ if (range == nullptr || range->IsEmpty() || range->spilled() ||
+ range->kind() != mode())
+ continue;
+
+ if (range->group() != nullptr) {
Jarin 2015/06/12 04:09:11 Package the range->group() == nullptr check into t
+ auto grp = range->group();
Jarin 2015/06/12 04:09:11 grp -> group
+ if (seen_groups.count(grp) > 0) continue;
+ seen_groups.insert(grp);
+ Enqueue(grp);
+ if (FLAG_trace_alloc) {
+ OFStream os(stdout);
+ os << "group: " << std::endl;
+ PrintableLiveRange plr;
+ plr.register_configuration_ = data()->config();
+ for (auto r : grp->ranges()) {
+ plr.range_ = r;
+ os << plr;
+ }
+ os << std::endl;
+ }
+ } else {
Enqueue(range);
}
}
+}
+
+
+void GreedyAllocator::EvictAll(int reg,
+ const conflict_iterator& first_conflict) {
+ for (conflict_iterator c = first_conflict; !c.IsEnd();) {
+ auto range = *c;
+ while (!c.IsEnd() && *c == range) ++c;
Jarin 2015/06/12 04:09:11 When can it happen that the iterator gives the sam
+
+ DCHECK(range->HasRegisterAssigned());
+ CHECK(!range->IsFixed());
+ allocations_[reg]->remove(range);
+ Evict(range);
+ Enqueue(range);
+ }
+}
+
+
+void GreedyAllocator::AllocateRange(LiveRange* current) {
+ TRACE("Processing interval %d of size %d\n", current->id(),
+ current->GetSize());
+
+ LiveRange* remaining = nullptr;
+ if (HandleSpillOperands(current, &remaining)) {
+ if (remaining != nullptr) Enqueue(remaining);
+ return;
+ }
+
+ // TODO(mtrofin): Does the linear algo's hinting mechanism even matter,
+ // now that we have groups?
Jarin 2015/06/12 04:09:11 Does it make any performance difference if you kee
+ int hint_reg = GetHintedRegister(current);
+ float my_weight = current->GetWeight();
+ if (hint_reg >= 0) {
+ auto first_conflict = allocations_[hint_reg]->conflicts_begin(current);
+ if (first_conflict.IsEnd()) {
+ AssignRangeToRegister(hint_reg, current);
+ return;
+ }
+ float max_weight = CalculateMaxSpillWeight(
+ first_conflict, allocations_[hint_reg]->conflicts_end());
+ if (max_weight < my_weight) {
+ EvictAll(hint_reg, first_conflict);
+ AssignRangeToRegister(hint_reg, current);
+ return;
+ }
+ }
+
+ int free_reg = -1;
+ conflict_iterator all_conflicts[RegisterConfiguration::kMaxDoubleRegisters];
+ for (int i = 0; i < num_registers(); i++) {
+ auto conflict = allocations_[i]->conflicts_begin(current);
+ if (conflict.IsEnd()) {
+ free_reg = i;
+ break;
+ }
+ all_conflicts[i] = conflict;
+ }
+
+ if (free_reg >= 0) {
+ AssignRangeToRegister(free_reg, current);
+ return;
+ }
+ free_reg = FindRegisterToEvictForRange(all_conflicts, my_weight);
Jarin 2015/06/12 04:09:11 What happens if you do not evict here? (And spill
+ if (free_reg >= 0) {
+ EvictAll(free_reg, all_conflicts[free_reg]);
+ AssignRangeToRegister(free_reg, current);
+ return;
+ }
+ HandleBlockedRange(current);
+}
+
+template <typename TIter>
+float GreedyAllocator::CalculateMaxSpillWeight(const TIter& begin,
+ const TIter& end) {
+ float ret = 0.0;
+ for (auto s = begin; s != end; ++s) {
+ ret = Max(ret, (*s)->GetWeight());
+ if (ret == LiveRange::kMaxWeight) return ret;
+ }
+ return ret;
+}
+
+
+bool GreedyAllocator::TryAllocateGroup(LiveRangeGroup* grp) {
Jarin 2015/06/12 04:09:11 Do you have any performance numbers on group alloc
+ for (int i = 0; i < num_registers(); i++) {
+ if (TryAllocateGroupAtRegister(i, grp)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+bool GreedyAllocator::TryAllocateGroupAtRegister(unsigned reg,
+ LiveRangeGroup* grp) {
+ auto ranges = grp->ranges();
+ for (auto r : ranges) {
+ auto first_conflict = allocations_[reg]->conflicts_begin(r);
Jarin 2015/06/12 04:09:10 Could we have a method on CoalescedLiveRanges that
+ if (!first_conflict.IsEnd()) {
+ return false;
+ }
+ }
+ for (auto r : ranges) {
+ AssignRangeToRegister(reg, r);
+ }
+ return true;
+}
+
+
+int GreedyAllocator::FindRegisterToEvictForRange(
+ conflict_iterator all_conflicts[RegisterConfiguration::kMaxDoubleRegisters],
Jarin 2015/06/12 04:09:11 Sized array argument is a bit esoteric construct i
+ float competing) {
+ int ret = -1;
+ float smallest_weight = LiveRange::kMaxWeight;
+ for (int i = 0; i < num_registers(); ++i) {
+ float w = CalculateMaxSpillWeight(all_conflicts[i],
Jarin 2015/06/12 04:09:11 w -> weight
+ allocations_[i]->conflicts_end());
+ if (w >= competing) continue;
+ if (w < smallest_weight) {
+ smallest_weight = w;
+ ret = i;
+ }
+ }
+ return ret;
+}
+
+
+int GreedyAllocator::FindRegisterToEvictForGroup(LiveRangeGroup* grp,
+ float competing) {
+ int ret = -1;
+ auto ranges = grp->ranges();
+ float smallest_weight = LiveRange::kMaxWeight;
+ for (int i = 0; i < num_registers(); ++i) {
+ float grp_counter_weight = 0.0;
Jarin 2015/06/12 04:09:10 grp -> group.
+ for (auto r : ranges) {
+ auto first_conflict = allocations_[i]->conflicts_begin(r);
+ if (first_conflict.IsEnd()) continue;
+ auto w = CalculateMaxSpillWeight(first_conflict,
Jarin 2015/06/12 04:09:11 Do not use auto here.
+ allocations_[i]->conflicts_end());
+ grp_counter_weight = Max(grp_counter_weight, w);
+ if (grp_counter_weight >= competing) break;
+ }
+ if (grp_counter_weight >= competing) continue;
Jarin 2015/06/12 04:09:10 Could you fold the grp_counter_weight >= competing
+ if (grp_counter_weight < smallest_weight) {
+ smallest_weight = grp_counter_weight;
+ ret = i;
+ }
+ }
+ return ret;
+}
+
+
+// TODO(mtrofin): improved code reuse with AllocateRange?
+void GreedyAllocator::AllocateGroup(LiveRangeGroup* grp) {
Jarin 2015/06/12 04:09:11 grp -> group
+ // Modify the group ranges content after handling spill operands
+ for (auto iter = grp->ranges().begin(), end = grp->ranges().end();
+ iter != end;) {
+ auto iter_backup = iter;
+ auto range = *iter++;
+ LiveRange* remainder = nullptr;
+ if (HandleSpillOperands(range, &remainder)) {
+ grp->ranges().erase(iter_backup);
+ if (remainder != nullptr) {
+ grp->ranges().insert(remainder);
+ remainder->RecalculateWeight(code());
+ }
+ }
+ }
+
+ float grp_weight = -1.0;
Jarin 2015/06/12 04:09:10 grp_weight -> group_weight
+ if (TryAllocateGroup(grp)) {
+ stats_.groups_allocated++;
+ return;
+ }
+
+ if (grp_weight < 0.0) {
Jarin 2015/06/12 04:09:10 If you really insist on having the same types for
+ grp_weight =
+ CalculateMaxSpillWeight(grp->ranges().begin(), grp->ranges().end());
+ }
+
+ int reg_to_free = FindRegisterToEvictForGroup(grp, grp_weight);
+ if (reg_to_free >= 0) {
+ for (auto r : grp->ranges()) {
+ EvictAll(reg_to_free, allocations_[reg_to_free]->conflicts_begin(r));
+ AssignRangeToRegister(reg_to_free, r);
+ }
+ return;
+ }
+
+ for (auto r : grp->ranges()) {
+ Enqueue(r);
+ }
+}
+
+
+void GreedyAllocator::AllocateRegisters() {
+ stats_.reset();
+ CHECK(queue_.empty());
+ CHECK(allocations_.empty());
+
+ TRACE("Begin allocating function %s with the Greedy Allocator\n",
+ data()->debug_name());
allocations_.resize(num_registers());
for (int i = 0; i < num_registers(); i++) {
@@ -2703,138 +3246,225 @@ void GreedyAllocator::AllocateRegisters() {
if (current != nullptr) {
DCHECK_EQ(mode(), current->kind());
int reg_nr = current->assigned_register();
- bool inserted = allocations_[reg_nr]->Insert(current);
- CHECK(inserted);
+ allocations_[reg_nr]->insert(current);
+ current->RecalculateWeight(code());
}
}
+ GroupAndEnqueue();
+
while (!queue_.empty()) {
- auto current_pair = queue_.top();
+ std::pair<unsigned, PQueueElem> current_pair = queue_.top();
queue_.pop();
- auto current = current_pair.second;
- if (HandleSpillOperands(current)) {
- continue;
- }
- bool spill = false;
- ZoneSet<LiveRange*> conflicting(local_zone());
- if (!TryAllocate(current, &conflicting)) {
- DCHECK(!conflicting.empty());
- float this_weight = std::numeric_limits<float>::max();
- LifetimePosition split_pos =
- FindProgressingSplitPosition(current, &spill);
- if (split_pos.IsValid()) {
- this_weight = CalculateSpillWeight(current);
- }
-
- bool evicted = false;
- for (auto* conflict : conflicting) {
- if (CalculateSpillWeight(conflict) < this_weight) {
- Evict(conflict);
- Enqueue(conflict);
- evicted = true;
- }
- }
- if (evicted) {
- conflicting.clear();
- TryAllocate(current, &conflicting);
- }
- if (!conflicting.empty()) {
- DCHECK(!current->IsFixed() || current->CanBeSpilled(current->Start()));
- DCHECK(split_pos.IsValid());
- AllocateBlockedRange(current, split_pos, spill);
- }
+ auto element = current_pair.second;
+ if (element.IsGroup()) {
+ AllocateGroup(element.get_group());
+ } else {
+ auto current = element.get_range();
Jarin 2015/06/12 04:09:11 No need for the intermediate variable.
+ AllocateRange(current);
}
}
for (size_t i = 0; i < allocations_.size(); ++i) {
- if (!allocations_[i]->IsEmpty()) {
+ if (!allocations_[i]->empty()) {
data()->MarkAllocated(mode(), static_cast<int>(i));
}
}
+ allocations_.clear();
+
+ for (auto r : data()->live_ranges()) {
+ if (r == nullptr || r->IsEmpty() || r->kind() != mode()) continue;
+ if (!r->spilled()) continue;
Jarin 2015/06/12 04:09:11 Fold into the previous if?
+ auto top = r->TopLevel();
Jarin 2015/06/12 04:09:10 Use the real type here.
+ if (top->group() != nullptr) {
+ if (!top->HasSpillRange()) continue;
+ auto top_sp_range = top->GetSpillRange();
+ CHECK(top_sp_range != nullptr);
Jarin 2015/06/12 04:09:11 DCHECK?
+ for (auto m : top->group()->ranges()) {
+ if (!m->HasSpillRange()) continue;
+ auto m_sp_range = m->TopLevel()->GetSpillRange();
+ if (m_sp_range == top_sp_range) continue;
+ bool merged = top_sp_range->TryMerge(m_sp_range);
+ CHECK(merged);
+ }
+ }
+ }
+ TRACE("End allocating function %s with the Greedy Allocator\n",
+ data()->debug_name());
}
-LifetimePosition GreedyAllocator::GetSplittablePos(LifetimePosition pos) {
- auto ret = pos.PrevStart().End();
- if (IsBlockBoundary(code(), pos.Start())) {
- ret = pos.Start();
- }
- DCHECK(ret <= pos);
- return ret;
-}
+void GreedyAllocator::HandleBlockedRange(LiveRange* current) {
+ // Make the best possible decision for splitting this range. The resulting
+ // chunks may have a better chance at allocation, or, if not, will eventually
+ // be unsplittable and "fit".
-LifetimePosition GreedyAllocator::FindProgressingSplitPosition(
- LiveRange* range, bool* is_spill_pos) {
- auto start = range->Start();
- auto end = range->End();
+ // TODO(mtrofin): more tuning. Is the ordering the one we want?
+ auto start = current->Start();
Jarin 2015/06/12 04:09:10 Explicit types, please.
+ auto end = current->End();
+
+ UsePosition* next_reg_use =
+ current->NextUsePositionRegisterIsBeneficial(start);
- UsePosition* next_reg_use = range->first_pos();
- while (next_reg_use != nullptr &&
- next_reg_use->type() != UsePositionType::kRequiresRegister) {
- next_reg_use = next_reg_use->next();
+ if (current->is_phi()) {
+ CHECK(next_reg_use != nullptr && next_reg_use->pos() == start);
+ // If the range does not need register soon, spill it to the merged
+ // spill range.
+ auto next_pos = start;
+ if (next_pos.IsGapPosition()) next_pos = next_pos.NextStart();
+ auto pos = current->NextUsePositionRegisterIsBeneficial(next_pos);
+ if (pos == nullptr) {
+ Spill(current);
+ return;
+ } else if (pos->pos() > start.NextStart()) {
+ Enqueue(SpillBetweenUntil(current, start, start, pos->pos()));
+ return;
+ }
}
if (next_reg_use == nullptr) {
- *is_spill_pos = true;
- auto ret = FindOptimalSpillingPos(range, start);
- DCHECK(ret.IsValid());
- return ret;
+ auto pos = FindOptimalSpillingPos(current, start);
+ DCHECK(pos.IsValid());
+ auto tail = SplitRangeAt(current, pos);
+ Spill(tail);
+ if (tail != current) {
+ Enqueue(current);
+ }
+ return;
}
- *is_spill_pos = false;
- auto reg_pos = next_reg_use->pos();
- auto correct_pos = GetSplittablePos(reg_pos);
- if (start < correct_pos && correct_pos < end) {
- return correct_pos;
- }
+ if (TrySplitAroundCalls(current)) return;
+ if (TrySplitBeforeLoops(current)) return;
+ if (TrySplitAfterLoops(current)) return;
- if (correct_pos >= end) {
- return LifetimePosition::Invalid();
- }
- // Correct_pos must be at or before start. Find the next use position.
- next_reg_use = next_reg_use->next();
- auto reference = reg_pos;
- while (next_reg_use != nullptr) {
- auto pos = next_reg_use->pos();
- // Skip over tight successive uses.
- if (reference.NextStart() < pos) {
- break;
+ if (current->CanBeSpilled(start)) {
+ UsePosition* next_mandatory_use = nullptr;
+ for (next_mandatory_use = current->first_pos();
+ next_mandatory_use != nullptr;
+ next_mandatory_use = next_mandatory_use->next()) {
+ if (next_mandatory_use->type() == UsePositionType::kRequiresRegister)
+ break;
}
- reference = pos;
- next_reg_use = next_reg_use->next();
+ if (next_mandatory_use == nullptr) {
+ Spill(current);
+ } else {
+ Enqueue(
+ SpillBetweenUntil(current, start, start, next_mandatory_use->pos()));
+ }
+ return;
}
- if (next_reg_use == nullptr) {
- // While there may not be another use, we may still have space in the range
- // to clip off.
- correct_pos = reference.NextStart();
- if (start < correct_pos && correct_pos < end) {
- return correct_pos;
- }
- return LifetimePosition::Invalid();
+ if (current->first_interval()->next() != nullptr) {
+ auto tail = SplitRangeAt(current, current->first_interval()->end());
+ DCHECK(tail != current);
+ Enqueue(tail);
+ Enqueue(current);
+ return;
}
- correct_pos = GetSplittablePos(next_reg_use->pos());
- if (start < correct_pos && correct_pos < end) {
- DCHECK(reference < correct_pos);
- return correct_pos;
+ auto pos_to_split = current->GetFirstSplittablePosition();
+ CHECK(pos_to_split.IsValid() && start < pos_to_split && pos_to_split < end);
+ auto tail = SplitRangeAt(current, pos_to_split);
+ CHECK(tail != current);
+ Enqueue(tail);
+ Enqueue(current);
+}
+
+
+bool GreedyAllocator::TrySplitAroundCalls(LiveRange* range) {
+ // TODO(mtrofin): should we just split around all calls?
+ auto start = range->Start();
+ auto end = range->End();
+ for (auto i = range->first_interval(); i != nullptr; i = i->next()) {
+ for (int instr_pos = i->start().ToInstructionIndex();
+ instr_pos < i->end().ToInstructionIndex(); instr_pos++) {
+ auto instr = code()->InstructionAt(instr_pos);
+ if (instr->IsCall()) {
+ auto pos = LifetimePosition::GapFromInstructionIndex(instr_pos);
+ if (start >= pos || pos >= end) continue;
+ auto tail = SplitRangeAt(range, pos);
+ DCHECK(tail != range);
+ Enqueue(tail);
+ Enqueue(range);
Jarin 2015/06/12 04:09:11 I am wondering whether queuing a group for tail an
+ return true;
+ }
+ }
}
- return LifetimePosition::Invalid();
+ return false;
}
-void GreedyAllocator::AllocateBlockedRange(LiveRange* current,
- LifetimePosition pos, bool spill) {
- auto tail = SplitRangeAt(current, pos);
- if (spill) {
- Spill(tail);
- } else {
- Enqueue(tail);
+bool GreedyAllocator::TrySplitBeforeLoops(LiveRange* range) {
+ auto start = range->Start();
+ auto end = range->End();
+ for (auto pos = range->first_pos(); pos != nullptr; pos = pos->next()) {
+ if (!pos->RegisterIsBeneficial()) continue;
+ const InstructionBlock* block =
+ code()->GetInstructionBlock(pos->pos().ToInstructionIndex());
+ if (block->IsLoopHeader() || block->loop_header().IsValid()) {
+ block = block->IsLoopHeader() ? block : GetContainingLoop(code(), block);
+ auto split_pos = start;
+ while (block != nullptr) {
+ auto before_loop_start = LifetimePosition::GapFromInstructionIndex(
+ block->first_instruction_index() - 1);
+
+ if (range->Covers(before_loop_start)) {
+ split_pos = before_loop_start;
+ }
+
+ // Try hoisting out to an outer loop.
+ block = GetContainingLoop(code(), block);
+ }
+ if (start < split_pos && split_pos < end) {
+ auto tail = SplitRangeAt(range, split_pos);
+ Enqueue(tail);
+ Enqueue(range);
+ return true;
+ }
+ }
}
- if (tail != current) {
- Enqueue(current);
+ return false;
+}
+
+
+bool GreedyAllocator::TrySplitAfterLoops(LiveRange* range) {
+ auto start = range->Start();
+ auto end = range->End();
+ for (auto pos = range->first_pos(); pos != nullptr; pos = pos->next()) {
+ if (!pos->RegisterIsBeneficial()) continue;
+ const InstructionBlock* block =
+ code()->GetInstructionBlock(pos->pos().ToInstructionIndex());
+ if (block->IsLoopHeader() || block->loop_header().IsValid()) {
+ auto header = block->IsLoopHeader()
+ ? block
+ : code()->InstructionBlockAt(block->loop_header());
+
+ auto split_pos = start;
+ while (header != nullptr) {
+ if (header->loop_end().ToSize() >=
+ static_cast<size_t>(code()->InstructionBlockCount()))
+ break;
+ auto loop_end = code()->InstructionBlockAt(header->loop_end());
+ auto after_loop_start = LifetimePosition::GapFromInstructionIndex(
+ loop_end->last_instruction_index() + 1);
+
+ if (range->Covers(after_loop_start)) {
+ split_pos = after_loop_start;
+ }
+
+ // Try hoisting out to an outer loop.
+ header = GetContainingLoop(code(), header);
+ }
+ if (start < split_pos && split_pos < end) {
+ auto tail = SplitRangeAt(range, split_pos);
+ Enqueue(tail);
+ Enqueue(range);
+ return true;
+ }
+ }
}
+ return false;
}
@@ -2857,91 +3487,6 @@ void SpillSlotLocator::LocateSpillSlots() {
}
-bool GreedyAllocator::TryReuseSpillForPhi(LiveRange* range) {
- if (range->IsChild() || !range->is_phi()) return false;
- DCHECK(!range->HasSpillOperand());
-
- auto phi_map_value = data()->GetPhiMapValueFor(range->id());
- auto phi = phi_map_value->phi();
- auto block = phi_map_value->block();
- // Count the number of spilled operands.
- size_t spilled_count = 0;
- LiveRange* first_op = nullptr;
- for (size_t i = 0; i < phi->operands().size(); i++) {
- int op = phi->operands()[i];
- LiveRange* op_range = LiveRangeFor(op);
- if (!op_range->HasSpillRange()) continue;
- auto pred = code()->InstructionBlockAt(block->predecessors()[i]);
- auto pred_end = LifetimePosition::InstructionFromInstructionIndex(
- pred->last_instruction_index());
- while (op_range != nullptr && !op_range->CanCover(pred_end)) {
- op_range = op_range->next();
- }
- if (op_range != nullptr && op_range->spilled()) {
- spilled_count++;
- if (first_op == nullptr) {
- first_op = op_range->TopLevel();
- }
- }
- }
-
- // Only continue if more than half of the operands are spilled.
- if (spilled_count * 2 <= phi->operands().size()) {
- return false;
- }
-
- // Try to merge the spilled operands and count the number of merged spilled
- // operands.
- DCHECK(first_op != nullptr);
- auto first_op_spill = first_op->GetSpillRange();
- size_t num_merged = 1;
- for (size_t i = 1; i < phi->operands().size(); i++) {
- int op = phi->operands()[i];
- auto op_range = LiveRangeFor(op);
- if (!op_range->HasSpillRange()) continue;
- auto op_spill = op_range->GetSpillRange();
- if (op_spill == first_op_spill || first_op_spill->TryMerge(op_spill)) {
- num_merged++;
- }
- }
-
- // Only continue if enough operands could be merged to the
- // same spill slot.
- if (num_merged * 2 <= phi->operands().size() ||
- AreUseIntervalsIntersecting(first_op_spill->interval(),
- range->first_interval())) {
- return false;
- }
-
- // If the range does not need register soon, spill it to the merged
- // spill range.
- auto next_pos = range->Start();
- if (next_pos.IsGapPosition()) next_pos = next_pos.NextStart();
- auto pos = range->NextUsePositionRegisterIsBeneficial(next_pos);
- if (pos == nullptr) {
- auto spill_range =
- range->TopLevel()->HasSpillRange()
- ? range->TopLevel()->GetSpillRange()
- : data()->AssignSpillRangeToLiveRange(range->TopLevel());
- bool merged = first_op_spill->TryMerge(spill_range);
- CHECK(merged);
- Spill(range);
- return true;
- } else if (pos->pos() > range->Start().NextStart()) {
- auto spill_range =
- range->TopLevel()->HasSpillRange()
- ? range->TopLevel()->GetSpillRange()
- : data()->AssignSpillRangeToLiveRange(range->TopLevel());
- bool merged = first_op_spill->TryMerge(spill_range);
- CHECK(merged);
- Enqueue(
- SpillBetweenUntil(range, range->Start(), range->Start(), pos->pos()));
- return true;
- }
- return false;
-}
-
-
OperandAssigner::OperandAssigner(RegisterAllocationData* data) : data_(data) {}
@@ -3377,7 +3922,6 @@ void LiveRangeConnector::ConnectRanges(Zone* local_zone) {
moves->push_back(move);
}
if (done) break;
- // Reset state.
to_eliminate.clear();
to_insert.clear();
moves = it->first.first;

Powered by Google App Engine
This is Rietveld 408576698