Commit 969a0775 authored by Thibaud Michaud's avatar Thibaud Michaud Committed by Commit Bot

[regalloc] Optimize FindFreeRegistersForRange

Sort inactive live ranges by their assigned register and by their next
start. This allows {FindFreeRegistersForRange} to stop the search earlier
and significantly reduces compile time for some test cases.

R=sigurds@chromium.org
CC=neis@chromium.org

Bug: chromium:974804, v8:9529
Change-Id: I85e2ff8acf2c02ea0539c89daae5a427da775c2c
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1795350
Commit-Queue: Thibaud Michaud <thibaudm@chromium.org>
Reviewed-by: 's avatarSigurd Schneider <sigurds@chromium.org>
Cr-Commit-Position: refs/heads/master@{#63722}
parent afd83c07
......@@ -796,12 +796,13 @@ LifetimePosition LiveRange::NextEndAfter(LifetimePosition position) const {
return start_search->end();
}
LifetimePosition LiveRange::NextStartAfter(LifetimePosition position) const {
LifetimePosition LiveRange::NextStartAfter(LifetimePosition position) {
UseInterval* start_search = FirstSearchIntervalForPosition(position);
while (start_search->start() < position) {
start_search = start_search->next();
}
return start_search->start();
next_start_ = start_search->start();
return next_start_;
}
LifetimePosition LiveRange::FirstIntersection(LiveRange* other) const {
......@@ -3094,11 +3095,11 @@ LinearScanAllocator::LinearScanAllocator(RegisterAllocationData* data,
: RegisterAllocator(data, kind),
unhandled_live_ranges_(local_zone),
active_live_ranges_(local_zone),
inactive_live_ranges_(local_zone),
inactive_live_ranges_(num_registers(), InactiveLiveRangeQueue(local_zone),
local_zone),
next_active_ranges_change_(LifetimePosition::Invalid()),
next_inactive_ranges_change_(LifetimePosition::Invalid()) {
active_live_ranges().reserve(8);
inactive_live_ranges().reserve(8);
}
void LinearScanAllocator::MaybeSpillPreviousRanges(LiveRange* begin_range,
......@@ -3237,31 +3238,22 @@ LiveRange* LinearScanAllocator::AssignRegisterOnReload(LiveRange* range,
// give reloading registers pecedence. That way we would compute the
// intersection for the entire future.
LifetimePosition new_end = range->End();
for (const auto inactive : inactive_live_ranges()) {
if (kSimpleFPAliasing || !check_fp_aliasing()) {
if (inactive->assigned_register() != reg) continue;
} else {
bool conflict = inactive->assigned_register() == reg;
if (!conflict) {
int alias_base_index = -1;
int aliases = data()->config()->GetAliases(range->representation(), reg,
inactive->representation(),
&alias_base_index);
DCHECK(aliases > 0 || (aliases == 0 && alias_base_index == -1));
while (aliases-- && !conflict) {
int aliased_reg = alias_base_index + aliases;
if (aliased_reg == reg) {
conflict = true;
}
}
}
if (!conflict) continue;
for (int cur_reg = 0; cur_reg < num_registers(); ++cur_reg) {
if ((kSimpleFPAliasing || !check_fp_aliasing()) && cur_reg != reg) {
continue;
}
for (auto interval = inactive->first_interval(); interval != nullptr;
interval = interval->next()) {
if (interval->start() > new_end) break;
if (interval->end() <= range->Start()) continue;
if (new_end > interval->start()) new_end = interval->start();
for (const auto cur_inactive : inactive_live_ranges(cur_reg)) {
if (!kSimpleFPAliasing && check_fp_aliasing() &&
!data()->config()->AreAliases(cur_inactive->representation(), cur_reg,
range->representation(), reg)) {
continue;
}
for (auto interval = cur_inactive->first_interval(); interval != nullptr;
interval = interval->next()) {
if (interval->start() > new_end) break;
if (interval->end() <= range->Start()) continue;
if (new_end > interval->start()) new_end = interval->start();
}
}
}
if (new_end != range->End()) {
......@@ -3557,11 +3549,17 @@ void LinearScanAllocator::UpdateDeferredFixedRanges(SpillMode spill_mode,
Min(updated->End(), next_active_ranges_change_);
});
}
for (auto inactive : inactive_live_ranges()) {
split_conflicting(range, inactive, [this](LiveRange* updated) {
next_inactive_ranges_change_ =
Min(updated->End(), next_inactive_ranges_change_);
});
for (int reg = 0; reg < num_registers(); ++reg) {
if ((kSimpleFPAliasing || !check_fp_aliasing()) &&
reg != range->assigned_register()) {
continue;
}
for (auto inactive : inactive_live_ranges(reg)) {
split_conflicting(range, inactive, [this](LiveRange* updated) {
next_inactive_ranges_change_ =
Min(updated->End(), next_inactive_ranges_change_);
});
}
}
};
if (mode() == GENERAL_REGISTERS) {
......@@ -3599,12 +3597,14 @@ void LinearScanAllocator::UpdateDeferredFixedRanges(SpillMode spill_mode,
}
} else {
// Remove all ranges.
for (auto it = inactive_live_ranges().begin();
it != inactive_live_ranges().end();) {
if ((*it)->TopLevel()->IsDeferredFixed()) {
it = inactive_live_ranges().erase(it);
} else {
++it;
for (int reg = 0; reg < num_registers(); ++reg) {
for (auto it = inactive_live_ranges(reg).begin();
it != inactive_live_ranges(reg).end();) {
if ((*it)->TopLevel()->IsDeferredFixed()) {
it = inactive_live_ranges(reg).erase(it);
} else {
++it;
}
}
}
}
......@@ -3635,7 +3635,9 @@ bool LinearScanAllocator::HasNonDeferredPredecessor(InstructionBlock* block) {
void LinearScanAllocator::AllocateRegisters() {
DCHECK(unhandled_live_ranges().empty());
DCHECK(active_live_ranges().empty());
DCHECK(inactive_live_ranges().empty());
for (int reg = 0; reg < num_registers(); ++reg) {
DCHECK(inactive_live_ranges(reg).empty());
}
SplitAndSpillRangesDefinedByMemoryOperand();
data()->ResetSpillState();
......@@ -3940,9 +3942,10 @@ void LinearScanAllocator::AddToActive(LiveRange* range) {
void LinearScanAllocator::AddToInactive(LiveRange* range) {
TRACE("Add live range %d:%d to inactive\n", range->TopLevel()->vreg(),
range->relative_id());
inactive_live_ranges().push_back(range);
next_inactive_ranges_change_ = std::min(
next_inactive_ranges_change_, range->NextStartAfter(range->Start()));
DCHECK(range->HasRegisterAssigned());
inactive_live_ranges(range->assigned_register()).insert(range);
}
void LinearScanAllocator::AddToUnhandled(LiveRange* range) {
......@@ -3965,30 +3968,36 @@ ZoneVector<LiveRange*>::iterator LinearScanAllocator::ActiveToHandled(
ZoneVector<LiveRange*>::iterator LinearScanAllocator::ActiveToInactive(
const ZoneVector<LiveRange*>::iterator it, LifetimePosition position) {
LiveRange* range = *it;
inactive_live_ranges().push_back(range);
TRACE("Moving live range %d:%d from active to inactive\n",
(range)->TopLevel()->vreg(), range->relative_id());
LifetimePosition next_active = range->NextStartAfter(position);
next_inactive_ranges_change_ =
std::min(next_inactive_ranges_change_, range->NextStartAfter(position));
std::min(next_inactive_ranges_change_, next_active);
DCHECK(range->HasRegisterAssigned());
inactive_live_ranges(range->assigned_register()).insert(range);
return active_live_ranges().erase(it);
}
ZoneVector<LiveRange*>::iterator LinearScanAllocator::InactiveToHandled(
ZoneVector<LiveRange*>::iterator it) {
LinearScanAllocator::InactiveLiveRangeQueue::iterator
LinearScanAllocator::InactiveToHandled(InactiveLiveRangeQueue::iterator it) {
LiveRange* range = *it;
TRACE("Moving live range %d:%d from inactive to handled\n",
(*it)->TopLevel()->vreg(), (*it)->relative_id());
return inactive_live_ranges().erase(it);
range->TopLevel()->vreg(), range->relative_id());
int reg = range->assigned_register();
return inactive_live_ranges(reg).erase(it);
}
ZoneVector<LiveRange*>::iterator LinearScanAllocator::InactiveToActive(
ZoneVector<LiveRange*>::iterator it, LifetimePosition position) {
LinearScanAllocator::InactiveLiveRangeQueue::iterator
LinearScanAllocator::InactiveToActive(InactiveLiveRangeQueue::iterator it,
LifetimePosition position) {
LiveRange* range = *it;
active_live_ranges().push_back(range);
TRACE("Moving live range %d:%d from inactive to active\n",
range->TopLevel()->vreg(), range->relative_id());
next_active_ranges_change_ =
std::min(next_active_ranges_change_, range->NextEndAfter(position));
return inactive_live_ranges().erase(it);
int reg = range->assigned_register();
return inactive_live_ranges(reg).erase(it);
}
void LinearScanAllocator::ForwardStateTo(LifetimePosition position) {
......@@ -4011,18 +4020,25 @@ void LinearScanAllocator::ForwardStateTo(LifetimePosition position) {
if (position >= next_inactive_ranges_change_) {
next_inactive_ranges_change_ = LifetimePosition::MaxPosition();
for (auto it = inactive_live_ranges().begin();
it != inactive_live_ranges().end();) {
LiveRange* cur_inactive = *it;
if (cur_inactive->End() <= position) {
it = InactiveToHandled(it);
} else if (cur_inactive->Covers(position)) {
it = InactiveToActive(it, position);
} else {
next_inactive_ranges_change_ =
std::min(next_inactive_ranges_change_,
cur_inactive->NextStartAfter(position));
++it;
for (int reg = 0; reg < num_registers(); ++reg) {
ZoneVector<LiveRange*> reorder(data()->allocation_zone());
for (auto it = inactive_live_ranges(reg).begin();
it != inactive_live_ranges(reg).end();) {
LiveRange* cur_inactive = *it;
if (cur_inactive->End() <= position) {
it = InactiveToHandled(it);
} else if (cur_inactive->Covers(position)) {
it = InactiveToActive(it, position);
} else {
next_inactive_ranges_change_ =
std::min(next_inactive_ranges_change_,
cur_inactive->NextStartAfter(position));
it = inactive_live_ranges(reg).erase(it);
reorder.push_back(cur_inactive);
}
}
for (LiveRange* range : reorder) {
inactive_live_ranges(reg).insert(range);
}
}
}
......@@ -4093,31 +4109,34 @@ void LinearScanAllocator::FindFreeRegistersForRange(
}
}
for (LiveRange* cur_inactive : inactive_live_ranges()) {
DCHECK(cur_inactive->End() > range->Start());
int cur_reg = cur_inactive->assigned_register();
// No need to carry out intersections, when this register won't be
// interesting to this range anyway.
// TODO(mtrofin): extend to aliased ranges, too.
if ((kSimpleFPAliasing || !check_fp_aliasing()) &&
positions[cur_reg] < range->Start()) {
continue;
}
LifetimePosition next_intersection = cur_inactive->FirstIntersection(range);
if (!next_intersection.IsValid()) continue;
if (kSimpleFPAliasing || !check_fp_aliasing()) {
positions[cur_reg] = Min(positions[cur_reg], next_intersection);
TRACE("Register %s is free until pos %d (2)\n", RegisterName(cur_reg),
Min(positions[cur_reg], next_intersection).value());
} else {
int alias_base_index = -1;
int aliases = data()->config()->GetAliases(
cur_inactive->representation(), cur_reg, rep, &alias_base_index);
DCHECK(aliases > 0 || (aliases == 0 && alias_base_index == -1));
while (aliases--) {
int aliased_reg = alias_base_index + aliases;
positions[aliased_reg] = Min(positions[aliased_reg], next_intersection);
for (int cur_reg = 0; cur_reg < num_regs; ++cur_reg) {
for (LiveRange* cur_inactive : inactive_live_ranges(cur_reg)) {
DCHECK_GT(cur_inactive->End(), range->Start());
CHECK_EQ(cur_inactive->assigned_register(), cur_reg);
// No need to carry out intersections, when this register won't be
// interesting to this range anyway.
// TODO(mtrofin): extend to aliased ranges, too.
if ((kSimpleFPAliasing || !check_fp_aliasing()) &&
positions[cur_reg] <= cur_inactive->NextStart()) {
break;
}
LifetimePosition next_intersection =
cur_inactive->FirstIntersection(range);
if (!next_intersection.IsValid()) continue;
if (kSimpleFPAliasing || !check_fp_aliasing()) {
positions[cur_reg] = std::min(positions[cur_reg], next_intersection);
TRACE("Register %s is free until pos %d (2)\n", RegisterName(cur_reg),
positions[cur_reg].value());
} else {
int alias_base_index = -1;
int aliases = data()->config()->GetAliases(
cur_inactive->representation(), cur_reg, rep, &alias_base_index);
DCHECK(aliases > 0 || (aliases == 0 && alias_base_index == -1));
while (aliases--) {
int aliased_reg = alias_base_index + aliases;
positions[aliased_reg] =
std::min(positions[aliased_reg], next_intersection);
}
}
}
}
......@@ -4336,46 +4355,46 @@ void LinearScanAllocator::AllocateBlockedReg(LiveRange* current,
}
}
for (LiveRange* range : inactive_live_ranges()) {
DCHECK(range->End() > current->Start());
int cur_reg = range->assigned_register();
bool is_fixed = range->TopLevel()->IsFixed();
// Don't perform costly intersections if they are guaranteed to not update
// block_pos or use_pos.
// TODO(mtrofin): extend to aliased ranges, too.
if ((kSimpleFPAliasing || !check_fp_aliasing())) {
if (is_fixed) {
if (block_pos[cur_reg] < range->Start()) continue;
} else {
if (use_pos[cur_reg] < range->Start()) continue;
for (int cur_reg = 0; cur_reg < num_registers(); ++cur_reg) {
for (LiveRange* range : inactive_live_ranges(cur_reg)) {
DCHECK(range->End() > current->Start());
DCHECK_EQ(range->assigned_register(), cur_reg);
bool is_fixed = range->TopLevel()->IsFixed();
// Don't perform costly intersections if they are guaranteed to not update
// block_pos or use_pos.
// TODO(mtrofin): extend to aliased ranges, too.
if ((kSimpleFPAliasing || !check_fp_aliasing())) {
DCHECK_LE(use_pos[cur_reg], block_pos[cur_reg]);
if (block_pos[cur_reg] <= range->NextStart()) break;
if (!is_fixed && use_pos[cur_reg] <= range->NextStart()) continue;
}
}
LifetimePosition next_intersection = range->FirstIntersection(current);
if (!next_intersection.IsValid()) continue;
LifetimePosition next_intersection = range->FirstIntersection(current);
if (!next_intersection.IsValid()) continue;
if (kSimpleFPAliasing || !check_fp_aliasing()) {
if (is_fixed) {
block_pos[cur_reg] = Min(block_pos[cur_reg], next_intersection);
use_pos[cur_reg] = Min(block_pos[cur_reg], use_pos[cur_reg]);
} else {
use_pos[cur_reg] = Min(use_pos[cur_reg], next_intersection);
}
} else {
int alias_base_index = -1;
int aliases = data()->config()->GetAliases(
range->representation(), cur_reg, rep, &alias_base_index);
DCHECK(aliases > 0 || (aliases == 0 && alias_base_index == -1));
while (aliases--) {
int aliased_reg = alias_base_index + aliases;
if (kSimpleFPAliasing || !check_fp_aliasing()) {
if (is_fixed) {
block_pos[aliased_reg] =
Min(block_pos[aliased_reg], next_intersection);
use_pos[aliased_reg] =
Min(block_pos[aliased_reg], use_pos[aliased_reg]);
block_pos[cur_reg] = Min(block_pos[cur_reg], next_intersection);
use_pos[cur_reg] = Min(block_pos[cur_reg], use_pos[cur_reg]);
} else {
use_pos[aliased_reg] = Min(use_pos[aliased_reg], next_intersection);
use_pos[cur_reg] = Min(use_pos[cur_reg], next_intersection);
}
} else {
int alias_base_index = -1;
int aliases = data()->config()->GetAliases(
range->representation(), cur_reg, rep, &alias_base_index);
DCHECK(aliases > 0 || (aliases == 0 && alias_base_index == -1));
while (aliases--) {
int aliased_reg = alias_base_index + aliases;
if (is_fixed) {
block_pos[aliased_reg] =
Min(block_pos[aliased_reg], next_intersection);
use_pos[aliased_reg] =
Min(block_pos[aliased_reg], use_pos[aliased_reg]);
} else {
use_pos[aliased_reg] = Min(use_pos[aliased_reg], next_intersection);
}
}
}
}
......@@ -4489,40 +4508,38 @@ void LinearScanAllocator::SplitAndSpillIntersecting(LiveRange* current,
it = ActiveToHandled(it);
}
for (auto it = inactive_live_ranges().begin();
it != inactive_live_ranges().end();) {
LiveRange* range = *it;
DCHECK(range->End() > current->Start());
if (range->TopLevel()->IsFixed()) {
++it;
continue;
}
for (int cur_reg = 0; cur_reg < num_registers(); ++cur_reg) {
if (kSimpleFPAliasing || !check_fp_aliasing()) {
if (range->assigned_register() != reg) {
if (cur_reg != reg) continue;
}
for (auto it = inactive_live_ranges(cur_reg).begin();
it != inactive_live_ranges(cur_reg).end();) {
LiveRange* range = *it;
if (!kSimpleFPAliasing && check_fp_aliasing() &&
!data()->config()->AreAliases(current->representation(), reg,
range->representation(), cur_reg)) {
++it;
continue;
}
} else {
if (!data()->config()->AreAliases(current->representation(), reg,
range->representation(),
range->assigned_register())) {
DCHECK(range->End() > current->Start());
if (range->TopLevel()->IsFixed()) {
++it;
continue;
}
}
LifetimePosition next_intersection = range->FirstIntersection(current);
if (next_intersection.IsValid()) {
UsePosition* next_pos = range->NextRegisterPosition(current->Start());
if (next_pos == nullptr) {
SpillAfter(range, split_pos, spill_mode);
LifetimePosition next_intersection = range->FirstIntersection(current);
if (next_intersection.IsValid()) {
UsePosition* next_pos = range->NextRegisterPosition(current->Start());
if (next_pos == nullptr) {
SpillAfter(range, split_pos, spill_mode);
} else {
next_intersection = Min(next_intersection, next_pos->pos());
SpillBetween(range, split_pos, next_intersection, spill_mode);
}
it = InactiveToHandled(it);
} else {
next_intersection = Min(next_intersection, next_pos->pos());
SpillBetween(range, split_pos, next_intersection, spill_mode);
++it;
}
it = InactiveToHandled(it);
} else {
++it;
}
}
}
......
......@@ -626,9 +626,10 @@ class V8_EXPORT_PRIVATE LiveRange : public NON_EXPORTED_BASE(ZoneObject) {
bool ShouldBeAllocatedBefore(const LiveRange* other) const;
bool CanCover(LifetimePosition position) const;
bool Covers(LifetimePosition position) const;
LifetimePosition NextStartAfter(LifetimePosition position) const;
LifetimePosition NextStartAfter(LifetimePosition position);
LifetimePosition NextEndAfter(LifetimePosition position) const;
LifetimePosition FirstIntersection(LiveRange* other) const;
LifetimePosition NextStart() const { return next_start_; }
void VerifyChildStructure() const {
VerifyIntervals();
......@@ -689,6 +690,8 @@ class V8_EXPORT_PRIVATE LiveRange : public NON_EXPORTED_BASE(ZoneObject) {
// Cache the last position splintering stopped at.
mutable UsePosition* splitting_pointer_;
LiveRangeBundle* bundle_ = nullptr;
// Next interval start, relative to the current linear scan position.
LifetimePosition next_start_;
DISALLOW_COPY_AND_ASSIGN(LiveRange);
};
......@@ -1309,16 +1312,28 @@ class LinearScanAllocator final : public RegisterAllocator {
const InstructionBlock* block);
bool HasNonDeferredPredecessor(InstructionBlock* block);
struct LiveRangeOrdering {
struct UnhandledLiveRangeOrdering {
bool operator()(const LiveRange* a, const LiveRange* b) const {
return a->ShouldBeAllocatedBefore(b);
}
};
using LiveRangeQueue = ZoneMultiset<LiveRange*, LiveRangeOrdering>;
LiveRangeQueue& unhandled_live_ranges() { return unhandled_live_ranges_; }
struct InactiveLiveRangeOrdering {
bool operator()(const LiveRange* a, const LiveRange* b) const {
return a->NextStart() < b->NextStart();
}
};
using UnhandledLiveRangeQueue =
ZoneMultiset<LiveRange*, UnhandledLiveRangeOrdering>;
using InactiveLiveRangeQueue =
ZoneMultiset<LiveRange*, InactiveLiveRangeOrdering>;
UnhandledLiveRangeQueue& unhandled_live_ranges() {
return unhandled_live_ranges_;
}
ZoneVector<LiveRange*>& active_live_ranges() { return active_live_ranges_; }
ZoneVector<LiveRange*>& inactive_live_ranges() {
return inactive_live_ranges_;
InactiveLiveRangeQueue& inactive_live_ranges(int reg) {
return inactive_live_ranges_[reg];
}
void SetLiveRangeAssignedRegister(LiveRange* range, int reg);
......@@ -1331,10 +1346,10 @@ class LinearScanAllocator final : public RegisterAllocator {
ZoneVector<LiveRange*>::iterator it);
ZoneVector<LiveRange*>::iterator ActiveToInactive(
ZoneVector<LiveRange*>::iterator it, LifetimePosition position);
ZoneVector<LiveRange*>::iterator InactiveToHandled(
ZoneVector<LiveRange*>::iterator it);
ZoneVector<LiveRange*>::iterator InactiveToActive(
ZoneVector<LiveRange*>::iterator it, LifetimePosition position);
InactiveLiveRangeQueue::iterator InactiveToHandled(
InactiveLiveRangeQueue::iterator it);
InactiveLiveRangeQueue::iterator InactiveToActive(
InactiveLiveRangeQueue::iterator it, LifetimePosition position);
void ForwardStateTo(LifetimePosition position);
......@@ -1384,9 +1399,9 @@ class LinearScanAllocator final : public RegisterAllocator {
void PrintRangeOverview(std::ostream& os);
LiveRangeQueue unhandled_live_ranges_;
UnhandledLiveRangeQueue unhandled_live_ranges_;
ZoneVector<LiveRange*> active_live_ranges_;
ZoneVector<LiveRange*> inactive_live_ranges_;
ZoneVector<InactiveLiveRangeQueue> inactive_live_ranges_;
// Approximate at what position the set of ranges will change next.
// Used to avoid scanning for updates even if none are present.
......
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