Commit 5b0c6cde authored by Ross McIlroy's avatar Ross McIlroy Committed by Commit Bot

[TurboProp] Add support for spill slot allocation to fast reg alloc

Adds support for tracking the instruction range of spilled operands,
and then allocating spill slots to these ranges. It also adds some
unittests covering spill slot allocation.

Spill slots are allocated in a linear fashion, running through the
instruction stream in a linear order, ensuring that no spill operand
is allocated to a same spill slot that is already assigned to during
this whole start / end range. This isn’t optimal, since it doesn’t
take into account holes in these ranges (e.g, blocks between start
and end that aren’t dominated by the start), but in practice rarely
leads to more than one extra spill slot being allocated compared to
the current allocator.

BUG=v8:9684

Change-Id: Iedee7bcf552080e5b4b6a2f4e96b78b6c1396cab
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2297470Reviewed-by: 's avatarTobias Tebbi <tebbi@chromium.org>
Commit-Queue: Ross McIlroy <rmcilroy@chromium.org>
Cr-Commit-Position: refs/heads/master@{#69107}
parent 6ef0ec94
......@@ -48,6 +48,10 @@ class MidTierRegisterAllocationData final : public RegisterAllocationData {
const InstructionOperand& from,
const InstructionOperand& to);
// Adds a gap move where both sides are PendingOperand operands.
MoveOperands* AddPendingOperandGapMove(int instr_index,
Instruction::GapPosition position);
// Helpers to get a block from an |rpo_number| or |instr_index|.
const InstructionBlock* GetBlock(const RpoNumber rpo_number);
const InstructionBlock* GetBlock(int instr_index);
......@@ -57,6 +61,9 @@ class MidTierRegisterAllocationData final : public RegisterAllocationData {
return reference_map_instructions_;
}
// Returns a bitvector representing the virtual registers that were spilled.
BitVector& spilled_virtual_registers() { return spilled_virtual_registers_; }
// This zone is for data structures only needed during register allocation
// phases.
Zone* allocation_zone() const { return allocation_zone_; }
......@@ -80,6 +87,7 @@ class MidTierRegisterAllocationData final : public RegisterAllocationData {
ZoneVector<VirtualRegisterData> virtual_register_data_;
ZoneVector<int> reference_map_instructions_;
BitVector spilled_virtual_registers_;
TickCounter* const tick_counter_;
......@@ -105,6 +113,7 @@ class MidTierRegisterAllocator final {
// Allocate registers operations.
void AllocateRegisters(const InstructionBlock* block);
void UpdateSpillRangesForLoops();
bool IsFixedRegisterPolicy(const UnallocatedOperand* operand);
void ReserveFixedRegisters(int instr_index);
......@@ -137,6 +146,39 @@ class MidTierRegisterAllocator final {
DISALLOW_COPY_AND_ASSIGN(MidTierRegisterAllocator);
};
// Spill slot allocator for mid-tier register allocation.
class MidTierSpillSlotAllocator final {
public:
explicit MidTierSpillSlotAllocator(MidTierRegisterAllocationData* data);
// Phase 3: assign spilled operands to specific spill slots.
void AllocateSpillSlots();
private:
class SpillSlot;
void Allocate(VirtualRegisterData* virtual_register);
void AdvanceTo(int instr_index);
SpillSlot* GetFreeSpillSlot(int byte_width);
MidTierRegisterAllocationData* data() const { return data_; }
InstructionSequence* code() const { return data()->code(); }
Frame* frame() const { return data()->frame(); }
Zone* zone() const { return data()->allocation_zone(); }
struct OrderByLastUse {
bool operator()(const SpillSlot* a, const SpillSlot* b) const;
};
MidTierRegisterAllocationData* data_;
ZonePriorityQueue<SpillSlot*, OrderByLastUse> allocated_slots_;
ZoneLinkedList<SpillSlot*> free_slots_;
int position_;
DISALLOW_COPY_AND_ASSIGN(MidTierSpillSlotAllocator);
};
} // namespace compiler
} // namespace internal
} // namespace v8
......
......@@ -2291,6 +2291,16 @@ struct MidTierRegisterAllocatorPhase {
}
};
struct MidTierSpillSlotAllocatorPhase {
DECL_PIPELINE_PHASE_CONSTANTS(MidTierSpillSlotAllocator)
void Run(PipelineData* data, Zone* temp_zone) {
MidTierSpillSlotAllocator spill_allocator(
data->mid_tier_register_allocator_data());
spill_allocator.AllocateSpillSlots();
}
};
struct OptimizeMovesPhase {
DECL_PIPELINE_PHASE_CONSTANTS(OptimizeMoves)
......@@ -3659,8 +3669,9 @@ void PipelineImpl::AllocateRegistersForMidTier(
Run<MidTierRegisterAllocatorPhase>();
// TODO(rmcilroy): Run spill slot allocation and reference map population
// phases
Run<MidTierSpillSlotAllocatorPhase>();
// TODO(rmcilroy): Run reference map population phase.
TraceSequence(info(), data, "after register allocation");
......
......@@ -908,6 +908,7 @@ class RuntimeCallTimer final {
ADD_THREAD_SPECIFIC_COUNTER(V, Optimize, EffectLinearization) \
ADD_THREAD_SPECIFIC_COUNTER(V, Optimize, EscapeAnalysis) \
ADD_THREAD_SPECIFIC_COUNTER(V, Optimize, MidTierRegisterAllocator) \
ADD_THREAD_SPECIFIC_COUNTER(V, Optimize, MidTierSpillSlotAllocator) \
ADD_THREAD_SPECIFIC_COUNTER(V, Optimize, FinalizeCode) \
ADD_THREAD_SPECIFIC_COUNTER(V, Optimize, FrameElision) \
ADD_THREAD_SPECIFIC_COUNTER(V, Optimize, GenericLowering) \
......
......@@ -12,7 +12,7 @@ namespace compiler {
namespace {
class FastRegisterAllocatorTest : public InstructionSequenceTest {
class MidTierRegisterAllocatorTest : public InstructionSequenceTest {
public:
void Allocate() {
WireBlocks();
......@@ -20,7 +20,7 @@ class FastRegisterAllocatorTest : public InstructionSequenceTest {
}
};
TEST_F(FastRegisterAllocatorTest, CanAllocateThreeRegisters) {
TEST_F(MidTierRegisterAllocatorTest, CanAllocateThreeRegisters) {
// return p0 + p1;
StartBlock();
auto a_reg = Parameter();
......@@ -32,7 +32,7 @@ TEST_F(FastRegisterAllocatorTest, CanAllocateThreeRegisters) {
Allocate();
}
TEST_F(FastRegisterAllocatorTest, CanAllocateFPRegisters) {
TEST_F(MidTierRegisterAllocatorTest, CanAllocateFPRegisters) {
StartBlock();
TestOperand inputs[] = {
Reg(FPParameter(kFloat64)), Reg(FPParameter(kFloat64)),
......@@ -45,6 +45,187 @@ TEST_F(FastRegisterAllocatorTest, CanAllocateFPRegisters) {
Allocate();
}
TEST_F(MidTierRegisterAllocatorTest, MoveLotsOfConstants) {
FLAG_trace_turbo = true;
StartBlock();
VReg constants[Register::kNumRegisters];
for (size_t i = 0; i < arraysize(constants); ++i) {
constants[i] = DefineConstant();
}
TestOperand call_ops[Register::kNumRegisters * 2];
for (int i = 0; i < Register::kNumRegisters; ++i) {
call_ops[i] = Reg(constants[i], i);
}
for (int i = 0; i < Register::kNumRegisters; ++i) {
call_ops[i + Register::kNumRegisters] = Slot(constants[i], i);
}
EmitCall(Slot(-1), arraysize(call_ops), call_ops);
EndBlock(Last());
Allocate();
}
TEST_F(MidTierRegisterAllocatorTest, SplitBeforeInstruction) {
const int kNumRegs = 6;
SetNumRegs(kNumRegs, kNumRegs);
StartBlock();
// Stack parameters/spilled values.
auto p_0 = Define(Slot(-1));
auto p_1 = Define(Slot(-2));
// Fill registers.
VReg values[kNumRegs];
for (size_t i = 0; i < arraysize(values); ++i) {
values[i] = Define(Reg(static_cast<int>(i)));
}
// values[0] will be split in the second half of this instruction.
// Models Intel mod instructions.
EmitOI(Reg(0), Reg(p_0, 1), UniqueReg(p_1));
EmitI(Reg(values[0], 0));
EndBlock(Last());
Allocate();
}
TEST_F(MidTierRegisterAllocatorTest, SplitBeforeInstruction2) {
const int kNumRegs = 6;
SetNumRegs(kNumRegs, kNumRegs);
StartBlock();
// Stack parameters/spilled values.
auto p_0 = Define(Slot(-1));
auto p_1 = Define(Slot(-2));
// Fill registers.
VReg values[kNumRegs];
for (size_t i = 0; i < arraysize(values); ++i) {
values[i] = Define(Reg(static_cast<int>(i)));
}
// values[0] and [1] will be split in the second half of this instruction.
EmitOOI(Reg(0), Reg(1), Reg(p_0, 0), Reg(p_1, 1));
EmitI(Reg(values[0]), Reg(values[1]));
EndBlock(Last());
Allocate();
}
TEST_F(MidTierRegisterAllocatorTest, SplitBeforeAndMove) {
StartBlock();
// Fill registers.
VReg values[Register::kNumRegisters];
for (size_t i = 0; i < arraysize(values); ++i) {
if (i == 0 || i == 1) continue; // Leave a hole for c_1 to take.
values[i] = Define(Reg(static_cast<int>(i)));
}
auto c_0 = DefineConstant();
auto c_1 = DefineConstant();
EmitOI(Reg(1), Reg(c_0, 0), UniqueReg(c_1));
// Use previous values to force c_1 to split before the previous instruction.
for (size_t i = 0; i < arraysize(values); ++i) {
if (i == 0 || i == 1) continue;
EmitI(Reg(values[i], static_cast<int>(i)));
}
EndBlock(Last());
Allocate();
}
TEST_F(MidTierRegisterAllocatorTest, SpillTwice) {
StartBlock();
auto p_0 = Parameter(Reg(1));
EmitCall(Slot(-2), Unique(p_0), Reg(p_0, 1));
EndBlock(Last());
Allocate();
}
namespace {
enum class ParameterType { kFixedSlot, kSlot, kRegister, kFixedRegister };
const ParameterType kParameterTypes[] = {
ParameterType::kFixedSlot, ParameterType::kSlot, ParameterType::kRegister,
ParameterType::kFixedRegister};
class MidTierRegAllocSlotConstraintTest
: public MidTierRegisterAllocatorTest,
public ::testing::WithParamInterface<
::testing::tuple<ParameterType, int>> {
public:
static const int kMaxVariant = 5;
protected:
ParameterType parameter_type() const {
return ::testing::get<0>(B::GetParam());
}
int variant() const { return ::testing::get<1>(B::GetParam()); }
private:
using B = ::testing::WithParamInterface<::testing::tuple<ParameterType, int>>;
};
} // namespace
TEST_P(MidTierRegAllocSlotConstraintTest, SlotConstraint) {
FLAG_trace_turbo = true;
StartBlock();
VReg p_0;
switch (parameter_type()) {
case ParameterType::kFixedSlot:
p_0 = Parameter(Slot(-1));
break;
case ParameterType::kSlot:
p_0 = Parameter(Slot(-1));
break;
case ParameterType::kRegister:
p_0 = Parameter(Reg());
break;
case ParameterType::kFixedRegister:
p_0 = Parameter(Reg(1));
break;
}
switch (variant()) {
case 0:
EmitI(Slot(p_0), Reg(p_0));
break;
case 1:
EmitI(Slot(p_0));
break;
case 2:
EmitI(Reg(p_0));
EmitI(Slot(p_0));
break;
case 3:
EmitI(Slot(p_0));
EmitI(Reg(p_0));
break;
case 4:
EmitI(Slot(p_0, -1), Slot(p_0), Reg(p_0), Reg(p_0, 1));
break;
default:
UNREACHABLE();
}
EndBlock(Last());
Allocate();
}
INSTANTIATE_TEST_SUITE_P(
MidTierRegisterAllocatorTest, MidTierRegAllocSlotConstraintTest,
::testing::Combine(
::testing::ValuesIn(kParameterTypes),
::testing::Range(0, MidTierRegAllocSlotConstraintTest::kMaxVariant)));
} // namespace
} // namespace compiler
} // namespace internal
......
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