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

[turbofan] Greedy: split around calls heuristic.

Once  a range is found to have a conflict, split around all the calls it
crosses over, since it will anyway have conflicts there, too.

Incrementally, from the last change to greedy, this change brings
overall improvement in benchmarks. In fact, except for 2 regressions
in Jetstream (splay-latency and date-format-xparb, at 6 and 7%
respectivelly), everything else is in the green or noise. Quite a few
benchmarks are over 3%, with a few (zlib, for example) in the double
digits.

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

Cr-Commit-Position: refs/heads/master@{#30579}
parent 9be35ade
...@@ -390,9 +390,94 @@ void GreedyAllocator::SpillRangeAsLastResort(LiveRange* range) { ...@@ -390,9 +390,94 @@ void GreedyAllocator::SpillRangeAsLastResort(LiveRange* range) {
} }
LiveRange* GreedyAllocator::GetRemainderAfterSplittingAroundFirstCall(
LiveRange* range) {
LiveRange* ret = range;
for (UseInterval* interval = range->first_interval(); interval != nullptr;
interval = interval->next()) {
LifetimePosition start = interval->start();
LifetimePosition end = interval->end();
// If the interval starts at instruction end, then the first instruction
// in the interval is the next one.
int first_full_instruction = (start.IsGapPosition() || start.IsStart())
? start.ToInstructionIndex()
: start.ToInstructionIndex() + 1;
// If the interval ends in a gap or at instruction start, then the last
// instruction is the previous one.
int last_full_instruction = (end.IsGapPosition() || end.IsStart())
? end.ToInstructionIndex() - 1
: end.ToInstructionIndex();
for (int instruction_index = first_full_instruction;
instruction_index <= last_full_instruction; ++instruction_index) {
if (!code()->InstructionAt(instruction_index)->IsCall()) continue;
LifetimePosition before =
GetSplitPositionForInstruction(range, instruction_index);
LiveRange* second_part =
before.IsValid() ? Split(range, data(), before) : range;
if (range != second_part) scheduler().Schedule(range);
LifetimePosition after =
FindSplitPositionAfterCall(second_part, instruction_index);
if (after.IsValid()) {
ret = Split(second_part, data(), after);
} else {
ret = nullptr;
}
Spill(second_part);
return ret;
}
}
return ret;
}
bool GreedyAllocator::TrySplitAroundCalls(LiveRange* range) {
bool modified = false;
while (range != nullptr) {
LiveRange* remainder = GetRemainderAfterSplittingAroundFirstCall(range);
// If we performed no modification, we're done.
if (remainder == range) {
break;
}
// We performed a modification.
modified = true;
range = remainder;
}
// If we have a remainder and we made modifications, it means the remainder
// has no calls and we should schedule it for further processing. If we made
// no modifications, we will just return false, because we want the algorithm
// to make progress by trying some other heuristic.
if (modified && range != nullptr) {
DCHECK(!range->spilled());
DCHECK(!range->HasRegisterAssigned());
scheduler().Schedule(range);
}
return modified;
}
LifetimePosition GreedyAllocator::FindSplitPositionAfterCall(
const LiveRange* range, int call_index) {
LifetimePosition after_call =
Max(range->Start(),
LifetimePosition::GapFromInstructionIndex(call_index + 1));
UsePosition* next_use = range->NextRegisterPosition(after_call);
if (!next_use) return LifetimePosition::Invalid();
LifetimePosition split_pos = FindOptimalSplitPos(after_call, next_use->pos());
split_pos =
GetSplitPositionForInstruction(range, split_pos.ToInstructionIndex());
return split_pos;
}
void GreedyAllocator::SplitOrSpillBlockedRange(LiveRange* range) { void GreedyAllocator::SplitOrSpillBlockedRange(LiveRange* range) {
// TODO(mtrofin): replace the call below with the entry point selecting if (TrySplitAroundCalls(range)) return;
// heuristics, once they exist, out of which GLRSP is the last one.
auto pos = GetLastResortSplitPosition(range, code()); auto pos = GetLastResortSplitPosition(range, code());
if (pos.IsValid()) { if (pos.IsValid()) {
LiveRange* tail = Split(range, data(), pos); LiveRange* tail = Split(range, data(), pos);
......
...@@ -125,6 +125,22 @@ class GreedyAllocator final : public RegisterAllocator { ...@@ -125,6 +125,22 @@ class GreedyAllocator final : public RegisterAllocator {
// This is the extension point for splitting heuristics. // This is the extension point for splitting heuristics.
void SplitOrSpillBlockedRange(LiveRange* range); void SplitOrSpillBlockedRange(LiveRange* range);
// Find a good position where to fill, after a range was spilled after a call.
LifetimePosition FindSplitPositionAfterCall(const LiveRange* range,
int call_index);
// Split a range around all calls it passes over. Returns true if any changes
// were made, or false if no calls were found.
bool TrySplitAroundCalls(LiveRange* range);
// Finds the first call instruction in the path of this range. Splits before
// and requeues that segment (if any), spills the section over the call, and
// returns the section after the call. The return is:
// - same range, if no call was found
// - nullptr, if the range finished at the call and there's no "after the
// call" portion.
// - the portion after the call.
LiveRange* GetRemainderAfterSplittingAroundFirstCall(LiveRange* range);
// Necessary heuristic: spill when all else failed. // Necessary heuristic: spill when all else failed.
void SpillRangeAsLastResort(LiveRange* range); void SpillRangeAsLastResort(LiveRange* range);
......
...@@ -409,10 +409,6 @@ DEFINE_BOOL(turbo_preprocess_ranges, true, ...@@ -409,10 +409,6 @@ DEFINE_BOOL(turbo_preprocess_ranges, true,
"run pre-register allocation heuristics") "run pre-register allocation heuristics")
DEFINE_BOOL(turbo_loop_stackcheck, true, "enable stack checks in loops") DEFINE_BOOL(turbo_loop_stackcheck, true, "enable stack checks in loops")
// TODO(mtrofin): remove the 2 implications.
DEFINE_NEG_IMPLICATION(turbo_greedy_regalloc, turbo_preprocess_ranges)
DEFINE_NEG_IMPLICATION(turbo_greedy_regalloc, turbo_loop_stackcheck)
DEFINE_IMPLICATION(turbo, turbo_asm_deoptimization) DEFINE_IMPLICATION(turbo, turbo_asm_deoptimization)
DEFINE_STRING(turbo_filter, "~~", "optimization filter for TurboFan compiler") DEFINE_STRING(turbo_filter, "~~", "optimization filter for TurboFan compiler")
DEFINE_BOOL(trace_turbo, false, "trace generated TurboFan IR") DEFINE_BOOL(trace_turbo, false, "trace generated TurboFan IR")
......
...@@ -686,7 +686,7 @@ TEST_F(RegisterAllocatorTest, MultipleDeferredBlockSpills) { ...@@ -686,7 +686,7 @@ TEST_F(RegisterAllocatorTest, MultipleDeferredBlockSpills) {
EXPECT_TRUE(IsParallelMovePresent(call_in_b1, Instruction::START, sequence(), EXPECT_TRUE(IsParallelMovePresent(call_in_b1, Instruction::START, sequence(),
Reg(var3_reg), Slot(var3_slot))); Reg(var3_reg), Slot(var3_slot)));
EXPECT_TRUE(IsParallelMovePresent(end_of_b1, Instruction::START, sequence(), EXPECT_TRUE(IsParallelMovePresent(end_of_b1, Instruction::START, sequence(),
Slot(var3_slot), Reg(var3_reg))); Slot(var3_slot), Reg()));
EXPECT_TRUE(IsParallelMovePresent(call_in_b2, Instruction::START, sequence(), EXPECT_TRUE(IsParallelMovePresent(call_in_b2, Instruction::START, sequence(),
Reg(var3_reg), Slot(var3_slot))); Reg(var3_reg), Slot(var3_slot)));
......
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