Commit efdcd202 authored by mtrofin's avatar mtrofin Committed by Commit bot

[turbofan] Splinter into one range.

Before this CL, we created one live range per successive set of
deferred blocks. For scenarios with many such blocks, this creates
an upfront pressure for the register allocator to deal with many ranges.
Linear sorts ranges, which is a super-linear operation.

The change places all deferred intervals into one range, meaning that,
at most, there will be twice as many live ranges as the original set. In
pathological cases (benchmarks/Compile/slow_nbody1.js), this change
halves the compilation time. We see some improvements elsewhere,
notably SQLite at ~4-5%.

We may be able to avoid the subsequent merge. Its cost is the
additional ranges it may need to create. The sole reason for the merge
phase is to provide an unchanged view of the world to the subsequent
phases. With the at-most-one splinter model, we may be able to teach
the other phases about splintering - should we find perf hindrances
due to merging.

Review URL: https://codereview.chromium.org/1391023007

Cr-Commit-Position: refs/heads/master@{#31224}
parent 6327ed0c
...@@ -78,12 +78,14 @@ void CreateSplinter(TopLevelLiveRange *range, RegisterAllocationData *data, ...@@ -78,12 +78,14 @@ void CreateSplinter(TopLevelLiveRange *range, RegisterAllocationData *data,
if (range->MayRequireSpillRange()) { if (range->MayRequireSpillRange()) {
data->CreateSpillRangeForLiveRange(range); data->CreateSpillRangeForLiveRange(range);
} }
TopLevelLiveRange *result = data->NextLiveRange(range->machine_type()); if (range->splinter() == nullptr) {
DCHECK_NULL(data->live_ranges()[result->vreg()]); TopLevelLiveRange *splinter = data->NextLiveRange(range->machine_type());
data->live_ranges()[result->vreg()] = result; DCHECK_NULL(data->live_ranges()[splinter->vreg()]);
data->live_ranges()[splinter->vreg()] = splinter;
range->SetSplinter(splinter);
}
Zone *zone = data->allocation_zone(); Zone *zone = data->allocation_zone();
range->Splinter(start, end, result, zone); range->Splinter(start, end, zone);
} }
} }
......
...@@ -698,7 +698,8 @@ TopLevelLiveRange::TopLevelLiveRange(int vreg, MachineType machine_type) ...@@ -698,7 +698,8 @@ TopLevelLiveRange::TopLevelLiveRange(int vreg, MachineType machine_type)
spilled_in_deferred_blocks_(false), spilled_in_deferred_blocks_(false),
spill_start_index_(kMaxInt), spill_start_index_(kMaxInt),
last_child_(this), last_child_(this),
last_insertion_point_(this) { last_pos_(nullptr),
splinter_(nullptr) {
bits_ |= SpillTypeField::encode(SpillType::kNoSpillType); bits_ |= SpillTypeField::encode(SpillType::kNoSpillType);
} }
...@@ -845,12 +846,12 @@ AllocatedOperand TopLevelLiveRange::GetSpillRangeOperand() const { ...@@ -845,12 +846,12 @@ AllocatedOperand TopLevelLiveRange::GetSpillRangeOperand() const {
void TopLevelLiveRange::Splinter(LifetimePosition start, LifetimePosition end, void TopLevelLiveRange::Splinter(LifetimePosition start, LifetimePosition end,
TopLevelLiveRange* result, Zone* zone) { Zone* zone) {
DCHECK(start != Start() || end != End()); DCHECK(start != Start() || end != End());
DCHECK(start < end); DCHECK(start < end);
result->set_spill_type(spill_type()); TopLevelLiveRange splinter_temp(-1, machine_type());
UsePosition* last_in_splinter = nullptr;
if (start <= Start()) { if (start <= Start()) {
// TODO(mtrofin): here, the TopLevel part is in the deferred range, so we // TODO(mtrofin): here, the TopLevel part is in the deferred range, so we
// may want to continue processing the splinter. However, if the value is // may want to continue processing the splinter. However, if the value is
...@@ -859,21 +860,21 @@ void TopLevelLiveRange::Splinter(LifetimePosition start, LifetimePosition end, ...@@ -859,21 +860,21 @@ void TopLevelLiveRange::Splinter(LifetimePosition start, LifetimePosition end,
// should check this, however, this may not be the place, because we don't // should check this, however, this may not be the place, because we don't
// have access to the instruction sequence. // have access to the instruction sequence.
DCHECK(end < End()); DCHECK(end < End());
DetachAt(end, result, zone); DetachAt(end, &splinter_temp, zone);
next_ = nullptr; next_ = nullptr;
} else if (end >= End()) { } else if (end >= End()) {
DCHECK(start > Start()); DCHECK(start > Start());
DetachAt(start, result, zone); DetachAt(start, &splinter_temp, zone);
next_ = nullptr; next_ = nullptr;
} else { } else {
DCHECK(start < End() && Start() < end); DCHECK(start < End() && Start() < end);
const int kInvalidId = std::numeric_limits<int>::max(); const int kInvalidId = std::numeric_limits<int>::max();
UsePosition* last = DetachAt(start, result, zone); UsePosition* last = DetachAt(start, &splinter_temp, zone);
LiveRange end_part(kInvalidId, this->machine_type(), nullptr); LiveRange end_part(kInvalidId, this->machine_type(), nullptr);
result->DetachAt(end, &end_part, zone); last_in_splinter = splinter_temp.DetachAt(end, &end_part, zone);
next_ = end_part.next_; next_ = end_part.next_;
last_interval_->set_next(end_part.first_interval_); last_interval_->set_next(end_part.first_interval_);
...@@ -890,20 +891,39 @@ void TopLevelLiveRange::Splinter(LifetimePosition start, LifetimePosition end, ...@@ -890,20 +891,39 @@ void TopLevelLiveRange::Splinter(LifetimePosition start, LifetimePosition end,
if (last != nullptr) last->set_next(end_part.first_pos_); if (last != nullptr) last->set_next(end_part.first_pos_);
} }
} }
result->next_ = nullptr;
result->top_level_ = result;
result->SetSplinteredFrom(this); if (splinter()->IsEmpty()) {
// Ensure the result's relative ID is unique within the IDs used for this splinter()->first_interval_ = splinter_temp.first_interval_;
// virtual register's children and splinters. splinter()->last_interval_ = splinter_temp.last_interval_;
result->relative_id_ = GetNextChildId(); } else {
splinter()->last_interval_->set_next(splinter_temp.first_interval_);
splinter()->last_interval_ = splinter_temp.last_interval_;
}
if (splinter()->first_pos() == nullptr) {
splinter()->first_pos_ = splinter_temp.first_pos_;
} else {
splinter()->last_pos_->set_next(splinter_temp.first_pos_);
}
if (last_in_splinter != nullptr) {
splinter()->last_pos_ = last_in_splinter;
} else {
if (splinter()->first_pos() != nullptr &&
splinter()->last_pos_ == nullptr) {
splinter()->last_pos_ = splinter()->first_pos();
for (UsePosition* pos = splinter()->first_pos(); pos != nullptr;
pos = pos->next()) {
splinter()->last_pos_ = pos;
}
}
}
#if DEBUG
Verify();
splinter()->Verify();
#endif
} }
void TopLevelLiveRange::SetSplinteredFrom(TopLevelLiveRange* splinter_parent) { void TopLevelLiveRange::SetSplinteredFrom(TopLevelLiveRange* splinter_parent) {
// The splinter parent is always the original "Top".
DCHECK(splinter_parent->Start() < Start());
splintered_from_ = splinter_parent; splintered_from_ = splinter_parent;
if (!HasSpillOperand() && splinter_parent->spill_range_ != nullptr) { if (!HasSpillOperand() && splinter_parent->spill_range_ != nullptr) {
SetSpillRange(splinter_parent->spill_range_); SetSpillRange(splinter_parent->spill_range_);
...@@ -928,43 +948,55 @@ void TopLevelLiveRange::Merge(TopLevelLiveRange* other, Zone* zone) { ...@@ -928,43 +948,55 @@ void TopLevelLiveRange::Merge(TopLevelLiveRange* other, Zone* zone) {
DCHECK(Start() < other->Start()); DCHECK(Start() < other->Start());
DCHECK(other->splintered_from() == this); DCHECK(other->splintered_from() == this);
LiveRange* last_other = other->last_child(); LiveRange* first = this;
LiveRange* last_me = last_child(); LiveRange* second = other;
DCHECK(first->Start() < second->Start());
while (first != nullptr && second != nullptr) {
DCHECK(first != second);
// Make sure the ranges are in order each time we iterate.
if (second->Start() < first->Start()) {
LiveRange* tmp = second;
second = first;
first = tmp;
continue;
}
// Simple case: we just append at the end. if (first->End() <= second->Start()) {
if (last_me->End() <= other->Start()) return last_me->AppendAsChild(other); if (first->next() == nullptr ||
first->next()->Start() > second->Start()) {
// First is in order before second.
LiveRange* temp = first->next();
first->next_ = second;
first = temp;
} else {
// First is in order before its successor (or second), so advance first.
first = first->next();
}
continue;
}
DCHECK(last_me->End() > last_other->End()); DCHECK(first->Start() < second->Start());
// If first and second intersect, split first.
if (first->Start() < second->End() && second->Start() < first->End()) {
LiveRange* temp = first->SplitAt(second->Start(), zone);
// In the more general case, we need to find the ranges between which to temp->set_spilled(first->spilled());
// insert. if (!temp->spilled())
if (other->Start() < last_insertion_point_->Start()) { temp->set_assigned_register(first->assigned_register());
last_insertion_point_ = this;
}
for (; last_insertion_point_->next() != nullptr && first->next_ = second;
last_insertion_point_->next()->Start() <= other->Start(); first = temp;
last_insertion_point_ = last_insertion_point_->next()) { continue;
} }
DCHECK(first->End() <= second->Start());
// When we splintered the original range, we reconstituted the original range
// into one range without children, but with discontinuities. To merge the
// splinter back in, we need to split the range - or a child obtained after
// register allocation splitting.
LiveRange* after = last_insertion_point_->next();
if (last_insertion_point_->End() > other->Start()) {
LiveRange* new_after = last_insertion_point_->SplitAt(other->Start(), zone);
new_after->set_spilled(last_insertion_point_->spilled());
if (!new_after->spilled())
new_after->set_assigned_register(
last_insertion_point_->assigned_register());
after = new_after;
} }
last_other->next_ = after; TopLevel()->UpdateParentForAllChildren(TopLevel());
last_insertion_point_->next_ = other;
other->UpdateParentForAllChildren(TopLevel());
TopLevel()->UpdateSpillRangePostMerge(other); TopLevel()->UpdateSpillRangePostMerge(other);
#if DEBUG
Verify();
#endif
} }
......
...@@ -493,8 +493,7 @@ class TopLevelLiveRange final : public LiveRange { ...@@ -493,8 +493,7 @@ class TopLevelLiveRange final : public LiveRange {
// result. // result.
// The current range is pointed to as "splintered_from". No parent/child // The current range is pointed to as "splintered_from". No parent/child
// relationship is established between this and result. // relationship is established between this and result.
void Splinter(LifetimePosition start, LifetimePosition end, void Splinter(LifetimePosition start, LifetimePosition end, Zone* zone);
TopLevelLiveRange* result, Zone* zone);
// Assuming other was splintered from this range, embeds other and its // Assuming other was splintered from this range, embeds other and its
// children as part of the children sequence of this range. // children as part of the children sequence of this range.
...@@ -538,7 +537,6 @@ class TopLevelLiveRange final : public LiveRange { ...@@ -538,7 +537,6 @@ class TopLevelLiveRange final : public LiveRange {
spill_start_index_ = Min(start, spill_start_index_); spill_start_index_ = Min(start, spill_start_index_);
} }
void SetSplinteredFrom(TopLevelLiveRange* splinter_parent);
void CommitSpillsAtDefinition(InstructionSequence* sequence, void CommitSpillsAtDefinition(InstructionSequence* sequence,
const InstructionOperand& operand, const InstructionOperand& operand,
bool might_be_duplicated); bool might_be_duplicated);
...@@ -578,8 +576,20 @@ class TopLevelLiveRange final : public LiveRange { ...@@ -578,8 +576,20 @@ class TopLevelLiveRange final : public LiveRange {
} }
void set_last_child(LiveRange* range) { last_child_ = range; } void set_last_child(LiveRange* range) { last_child_ = range; }
LiveRange* last_child() const { return last_child_; } LiveRange* last_child() const { return last_child_; }
TopLevelLiveRange* splinter() const { return splinter_; }
void SetSplinter(TopLevelLiveRange* splinter) {
DCHECK_NULL(splinter_);
DCHECK_NOT_NULL(splinter);
splinter_ = splinter;
splinter->relative_id_ = GetNextChildId();
splinter->set_spill_type(spill_type());
splinter->SetSplinteredFrom(this);
}
private: private:
void SetSplinteredFrom(TopLevelLiveRange* splinter_parent);
typedef BitField<bool, 1, 1> HasSlotUseField; typedef BitField<bool, 1, 1> HasSlotUseField;
typedef BitField<bool, 2, 1> IsPhiField; typedef BitField<bool, 2, 1> IsPhiField;
typedef BitField<bool, 3, 1> IsNonLoopPhiField; typedef BitField<bool, 3, 1> IsNonLoopPhiField;
...@@ -599,7 +609,8 @@ class TopLevelLiveRange final : public LiveRange { ...@@ -599,7 +609,8 @@ class TopLevelLiveRange final : public LiveRange {
bool spilled_in_deferred_blocks_; bool spilled_in_deferred_blocks_;
int spill_start_index_; int spill_start_index_;
LiveRange* last_child_; LiveRange* last_child_;
LiveRange* last_insertion_point_; UsePosition* last_pos_;
TopLevelLiveRange* splinter_;
DISALLOW_COPY_AND_ASSIGN(TopLevelLiveRange); DISALLOW_COPY_AND_ASSIGN(TopLevelLiveRange);
}; };
......
...@@ -32,11 +32,14 @@ class LiveRangeUnitTest : public TestWithZone { ...@@ -32,11 +32,14 @@ class LiveRangeUnitTest : public TestWithZone {
TopLevelLiveRange* Splinter(TopLevelLiveRange* top, int start, int end, TopLevelLiveRange* Splinter(TopLevelLiveRange* top, int start, int end,
int new_id = 0) { int new_id = 0) {
if (top->splinter() == nullptr) {
TopLevelLiveRange* ret = TopLevelLiveRange* ret =
new (zone()) TopLevelLiveRange(new_id, MachineType::kRepTagged); new (zone()) TopLevelLiveRange(new_id, MachineType::kRepTagged);
top->SetSplinter(ret);
}
top->Splinter(LifetimePosition::FromInt(start), top->Splinter(LifetimePosition::FromInt(start),
LifetimePosition::FromInt(end), ret, zone()); LifetimePosition::FromInt(end), zone());
return ret; return top->splinter();
} }
// Ranges first and second match structurally. // Ranges first and second match structurally.
...@@ -377,6 +380,25 @@ TEST_F(LiveRangeUnitTest, SplinterMultipleIntervalsRight) { ...@@ -377,6 +380,25 @@ TEST_F(LiveRangeUnitTest, SplinterMultipleIntervalsRight) {
} }
TEST_F(LiveRangeUnitTest, SplinterMergeMultipleTimes) {
TopLevelLiveRange* range =
TestRangeBuilder(zone()).Add(0, 3).Add(5, 10).Add(12, 16).Build();
Splinter(range, 4, 6);
Splinter(range, 8, 14);
TopLevelLiveRange* splinter = range->splinter();
EXPECT_EQ(nullptr, range->next());
EXPECT_EQ(nullptr, splinter->next());
EXPECT_EQ(range, splinter->splintered_from());
TopLevelLiveRange* expected_source =
TestRangeBuilder(zone()).Add(0, 3).Add(6, 8).Add(14, 16).Build();
TopLevelLiveRange* expected_splinter =
TestRangeBuilder(zone()).Add(5, 6).Add(8, 10).Add(12, 14).Build();
EXPECT_TRUE(RangesMatch(expected_source, range));
EXPECT_TRUE(RangesMatch(expected_splinter, splinter));
}
TEST_F(LiveRangeUnitTest, MergeMultipleIntervalsRight) { TEST_F(LiveRangeUnitTest, MergeMultipleIntervalsRight) {
TopLevelLiveRange* original = TopLevelLiveRange* original =
TestRangeBuilder(zone()).Add(0, 3).Add(5, 8).Build(); TestRangeBuilder(zone()).Add(0, 3).Add(5, 8).Build();
...@@ -416,8 +438,9 @@ TEST_F(LiveRangeUnitTest, IDGeneration) { ...@@ -416,8 +438,9 @@ TEST_F(LiveRangeUnitTest, IDGeneration) {
TopLevelLiveRange* splinter = TopLevelLiveRange* splinter =
new (zone()) TopLevelLiveRange(101, MachineType::kRepTagged); new (zone()) TopLevelLiveRange(101, MachineType::kRepTagged);
vreg->SetSplinter(splinter);
vreg->Splinter(LifetimePosition::FromInt(4), LifetimePosition::FromInt(12), vreg->Splinter(LifetimePosition::FromInt(4), LifetimePosition::FromInt(12),
splinter, zone()); zone());
EXPECT_EQ(101, splinter->vreg()); EXPECT_EQ(101, splinter->vreg());
EXPECT_EQ(1, splinter->relative_id()); EXPECT_EQ(1, splinter->relative_id());
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment