[regalloc] Fix slow edge case in BuildBundles

The issue is with this pattern, assuming disjoint uses for all vregs:

phi: v1 = v0 ...
phi: v2 = v0 ...
phi: v3 = v0 ...
...
phi: vN = v0 ...

For every phi, BuildBundles proceeds as follows:
- Create a new bundle for the output
- Merge the input bundle into the output bundle

Since the bundle gets bigger at every iteration, the merges become more
and more expensive and consume Zone memory that is immediately thrown
away at the next iteration.

A simple fix is to check the size of the bundles before merging and
always copy the smallest one into the biggest. In the pattern above this
should always copy the single-range output bundle into the large input
bundle.

R=sigurds@chromium.org

Bug: v8:11237
Change-Id: I6ad9152035da698d94b02b5b41802545ba149307
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2584879
Reviewed-by: Sigurd Schneider <sigurds@chromium.org>
Commit-Queue: Thibaud Michaud <thibaudm@chromium.org>
Cr-Commit-Position: refs/heads/master@{#71714}
This commit is contained in:
Thibaud Michaud 2020-12-11 14:34:54 +01:00 committed by Commit Bot
parent 2b9a4d9a72
commit c46c195795
2 changed files with 31 additions and 15 deletions

View File

@ -2600,7 +2600,12 @@ void BundleBuilder::BuildBundles() {
LiveRangeBundle* input_bundle = input_range->get_bundle();
if (input_bundle != nullptr) {
TRACE("Merge\n");
if (out->TryMerge(input_bundle, data()->is_trace_alloc())) {
LiveRangeBundle* merged = LiveRangeBundle::TryMerge(
out, input_bundle, data()->is_trace_alloc());
if (merged != nullptr) {
DCHECK_EQ(out_range->get_bundle(), merged);
DCHECK_EQ(input_range->get_bundle(), merged);
out = merged;
TRACE("Merged %d and %d to %d\n", phi->virtual_register(), input,
out->id());
} else if (input_range->Start() > out_range->Start()) {
@ -2641,13 +2646,16 @@ bool LiveRangeBundle::TryAddRange(LiveRange* range) {
InsertUses(range->first_interval());
return true;
}
bool LiveRangeBundle::TryMerge(LiveRangeBundle* other, bool trace_alloc) {
if (other == this) return true;
auto iter1 = uses_.begin();
auto iter2 = other->uses_.begin();
LiveRangeBundle* LiveRangeBundle::TryMerge(LiveRangeBundle* lhs,
LiveRangeBundle* rhs,
bool trace_alloc) {
if (rhs == lhs) return nullptr;
while (iter1 != uses_.end() && iter2 != other->uses_.end()) {
auto iter1 = lhs->uses_.begin();
auto iter2 = rhs->uses_.begin();
while (iter1 != lhs->uses_.end() && iter2 != rhs->uses_.end()) {
if (iter1->start >= iter2->end) {
++iter2;
} else if (iter2->start >= iter1->end) {
@ -2655,21 +2663,25 @@ bool LiveRangeBundle::TryMerge(LiveRangeBundle* other, bool trace_alloc) {
} else {
TRACE_COND(trace_alloc, "No merge %d:%d %d:%d\n", iter1->start,
iter1->end, iter2->start, iter2->end);
return false;
return nullptr;
}
}
// Uses are disjoint, merging is possible.
for (auto it = other->ranges_.begin(); it != other->ranges_.end(); ++it) {
(*it)->set_bundle(this);
InsertUses((*it)->first_interval());
if (lhs->uses_.size() < rhs->uses_.size()) {
// Merge the smallest bundle into the biggest.
std::swap(lhs, rhs);
}
ranges_.insert(other->ranges_.begin(), other->ranges_.end());
other->ranges_.clear();
return true;
for (auto it = rhs->ranges_.begin(); it != rhs->ranges_.end(); ++it) {
(*it)->set_bundle(lhs);
lhs->InsertUses((*it)->first_interval());
}
lhs->ranges_.insert(rhs->ranges_.begin(), rhs->ranges_.end());
rhs->ranges_.clear();
return lhs;
}
void LiveRangeBundle::MergeSpillRanges() {
DCHECK_IMPLIES(ranges_.empty(), uses_.empty());
SpillRange* target = nullptr;
for (auto range : ranges_) {
if (range->TopLevel()->HasSpillRange()) {

View File

@ -782,7 +782,11 @@ class LiveRangeBundle : public ZoneObject {
: ranges_(zone), uses_(zone), id_(id) {}
bool TryAddRange(LiveRange* range);
bool TryMerge(LiveRangeBundle* other, bool trace_alloc);
// If merging is possible, merge either {lhs} into {rhs} or {rhs} into
// {lhs}, clear the source and return the result. Otherwise return nullptr.
static LiveRangeBundle* TryMerge(LiveRangeBundle* lhs, LiveRangeBundle* rhs,
bool trace_alloc);
ZoneSet<LiveRange*, LiveRangeOrdering> ranges_;
ZoneSet<Range, RangeOrdering> uses_;