Commit b1769313 authored by Stephan Herhut's avatar Stephan Herhut Committed by Commit Bot

[regalloc] Introduce deferred fixed ranges

Fixed ranges are used to express register constraints in the
allocator. This change splits these fixed ranges into one for
normal code and deferred code. The former are handeled as before
whereas the latter are only made visible while allocating
registers for deferred code.

This prevents forward looking decisions in normal code to be
impacted by register constraints from deferred code.

Change-Id: I67d562bb41166194e62765d5ab051bc961054fc7
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1477742
Commit-Queue: Sigurd Schneider <sigurds@chromium.org>
Reviewed-by: 's avatarJaroslav Sevcik <jarin@chromium.org>
Reviewed-by: 's avatarSigurd Schneider <sigurds@chromium.org>
Cr-Commit-Position: refs/heads/master@{#60322}
parent 0188634e
......@@ -651,7 +651,11 @@ std::ostream& operator<<(std::ostream& os,
const InstructionSequence* code = printable_block.code_;
os << "B" << block->rpo_number();
os << ": AO#" << block->ao_number();
if (block->ao_number().IsValid()) {
os << ": AO#" << block->ao_number();
} else {
os << ": AO#?";
}
if (block->IsDeferred()) os << " (deferred)";
if (!block->needs_frame()) os << " (no frame)";
if (block->must_construct_frame()) os << " (construct frame)";
......
This diff is collapsed.
......@@ -467,11 +467,12 @@ class V8_EXPORT_PRIVATE LiveRange : public NON_EXPORTED_BASE(ZoneObject) {
void VerifyIntervals() const;
typedef BitField<bool, 0, 1> SpilledField;
// Bits (1,7] are used by TopLevelLiveRange.
// Bits (1,7[ are used by TopLevelLiveRange.
typedef BitField<int32_t, 7, 6> AssignedRegisterField;
typedef BitField<MachineRepresentation, 13, 8> RepresentationField;
typedef BitField<bool, 21, 1> RecombineField;
typedef BitField<uint8_t, 22, 6> ControlFlowRegisterHint;
// Bit 28 is used by TopLevelLiveRange.
// Unique among children and splinters of the same virtual register.
int relative_id_;
......@@ -569,6 +570,8 @@ class V8_EXPORT_PRIVATE TopLevelLiveRange final : public LiveRange {
bool IsFixed() const { return vreg_ < 0; }
bool IsDeferredFixed() const { return DeferredFixedField::decode(bits_); }
void set_deferred_fixed() { bits_ = DeferredFixedField::update(bits_, true); }
bool is_phi() const { return IsPhiField::decode(bits_); }
void set_is_phi(bool value) { bits_ = IsPhiField::update(bits_, value); }
......@@ -778,6 +781,7 @@ class V8_EXPORT_PRIVATE TopLevelLiveRange final : public LiveRange {
typedef BitField<bool, 3, 1> IsPhiField;
typedef BitField<bool, 4, 1> IsNonLoopPhiField;
typedef BitField<SpillType, 5, 2> SpillTypeField;
typedef BitField<bool, 28, 1> DeferredFixedField;
int vreg_;
int last_child_id_;
......@@ -860,6 +864,8 @@ class RegisterAllocationData final : public ZoneObject {
// Encodes whether a spill happens in deferred code (kSpillDeferred) or
// regular code (kSpillAtDefinition).
enum SpillMode { kSpillAtDefinition, kSpillDeferred };
static constexpr int kNumberOfFixedRangesPerRegister = 2;
class PhiMapValue : public ZoneObject {
public:
PhiMapValue(PhiInstruction* phi, const InstructionBlock* block, Zone* zone);
......@@ -1061,6 +1067,8 @@ class LiveRangeBuilder final : public ZoneObject {
private:
using SpillMode = RegisterAllocationData::SpillMode;
static constexpr int kNumberOfFixedRangesPerRegister =
RegisterAllocationData::kNumberOfFixedRangesPerRegister;
RegisterAllocationData* data() const { return data_; }
InstructionSequence* code() const { return data()->code(); }
......@@ -1086,8 +1094,9 @@ class LiveRangeBuilder final : public ZoneObject {
static int FixedLiveRangeID(int index) { return -index - 1; }
int FixedFPLiveRangeID(int index, MachineRepresentation rep);
TopLevelLiveRange* FixedLiveRangeFor(int index);
TopLevelLiveRange* FixedFPLiveRangeFor(int index, MachineRepresentation rep);
TopLevelLiveRange* FixedLiveRangeFor(int index, SpillMode spill_mode);
TopLevelLiveRange* FixedFPLiveRangeFor(int index, MachineRepresentation rep,
SpillMode spill_mode);
void MapPhiHint(InstructionOperand* operand, UsePosition* use_pos);
void ResolvePhiHint(InstructionOperand* operand, UsePosition* use_pos);
......@@ -1097,19 +1106,30 @@ class LiveRangeBuilder final : public ZoneObject {
UsePosition* NewUsePosition(LifetimePosition pos) {
return NewUsePosition(pos, nullptr, nullptr, UsePositionHintType::kNone);
}
TopLevelLiveRange* LiveRangeFor(InstructionOperand* operand);
TopLevelLiveRange* LiveRangeFor(InstructionOperand* operand,
SpillMode spill_mode);
// Helper methods for building intervals.
UsePosition* Define(LifetimePosition position, InstructionOperand* operand,
void* hint, UsePositionHintType hint_type);
void Define(LifetimePosition position, InstructionOperand* operand) {
Define(position, operand, nullptr, UsePositionHintType::kNone);
void* hint, UsePositionHintType hint_type,
SpillMode spill_mode);
void Define(LifetimePosition position, InstructionOperand* operand,
SpillMode spill_mode) {
Define(position, operand, nullptr, UsePositionHintType::kNone, spill_mode);
}
UsePosition* Use(LifetimePosition block_start, LifetimePosition position,
InstructionOperand* operand, void* hint,
UsePositionHintType hint_type);
UsePositionHintType hint_type, SpillMode spill_mode);
void Use(LifetimePosition block_start, LifetimePosition position,
InstructionOperand* operand) {
Use(block_start, position, operand, nullptr, UsePositionHintType::kNone);
InstructionOperand* operand, SpillMode spill_mode) {
Use(block_start, position, operand, nullptr, UsePositionHintType::kNone,
spill_mode);
}
SpillMode SpillModeForBlock(const InstructionBlock* block) const {
if (FLAG_turbo_control_flow_aware_allocation) {
return block->IsDeferred() ? SpillMode::kSpillDeferred
: SpillMode::kSpillAtDefinition;
}
return SpillMode::kSpillAtDefinition;
}
RegisterAllocationData* const data_;
ZoneMap<InstructionOperand*, UsePosition*> phi_hints_;
......@@ -1245,8 +1265,10 @@ class LinearScanAllocator final : public RegisterAllocator {
void ReloadLiveRanges(RangeWithRegisterSet& to_be_live,
LifetimePosition position);
void UpdateDeferredFixedRanges(SpillMode spill_mode, InstructionBlock* block);
bool BlockIsDeferredOrImmediatePredecessorIsNotDeferred(
const InstructionBlock* block);
bool HasNonDeferredPredecessor(InstructionBlock* block);
struct LiveRangeOrdering {
bool operator()(const LiveRange* a, const LiveRange* b) const {
......
......@@ -419,9 +419,9 @@ DEFINE_BOOL(print_deopt_stress, false, "print number of possible deopt points")
// Flags for TurboFan.
DEFINE_BOOL(turbo_sp_frame_access, false,
"use stack pointer-relative access to frame wherever possible")
DEFINE_BOOL(turbo_preprocess_ranges, true,
DEFINE_BOOL(turbo_preprocess_ranges, false,
"run pre-register allocation heuristics")
DEFINE_BOOL(turbo_control_flow_aware_allocation, false,
DEFINE_BOOL(turbo_control_flow_aware_allocation, true,
"consider control flow while allocating registers")
DEFINE_NEG_IMPLICATION(turbo_control_flow_aware_allocation,
turbo_preprocess_ranges)
......
......@@ -113,6 +113,11 @@ TEST_F(RegisterAllocatorTest, SimpleLoop) {
// while(true) { i++ }
StartBlock();
auto i_reg = DefineConstant();
// Add a branch around the loop to ensure the end-block
// is connected.
EndBlock(Branch(Reg(DefineConstant()), 3, 1));
StartBlock();
EndBlock();
{
......@@ -127,6 +132,9 @@ TEST_F(RegisterAllocatorTest, SimpleLoop) {
EndLoop();
}
StartBlock();
EndBlock();
Allocate();
}
......@@ -617,10 +625,10 @@ TEST_F(RegisterAllocatorTest, SingleDeferredBlockSpill) {
const int var_def_index = 1;
const int call_index = 3;
int expect_no_moves =
FLAG_turbo_preprocess_ranges ? var_def_index : call_index;
int expect_spill_move =
FLAG_turbo_preprocess_ranges ? call_index : var_def_index;
const bool spill_in_deferred =
FLAG_turbo_preprocess_ranges || FLAG_turbo_control_flow_aware_allocation;
int expect_no_moves = spill_in_deferred ? var_def_index : call_index;
int expect_spill_move = spill_in_deferred ? call_index : var_def_index;
// We should have no parallel moves at the "expect_no_moves" position.
EXPECT_EQ(
......@@ -685,6 +693,67 @@ TEST_F(RegisterAllocatorTest, MultipleDeferredBlockSpills) {
GetParallelMoveCount(start_of_b3, Instruction::START, sequence()));
}
TEST_F(RegisterAllocatorTest, ValidMultipleDeferredBlockSpills) {
if (!FLAG_turbo_control_flow_aware_allocation) return;
StartBlock(); // B0
auto var1 = EmitOI(Reg(0));
auto var2 = EmitOI(Reg(1));
auto var3 = EmitOI(Reg(2));
EndBlock(Branch(Reg(var1, 0), 1, 2));
StartBlock(true); // B1
EmitCall(Slot(-2), Slot(var1));
EndBlock(Jump(5));
StartBlock(); // B2
EmitNop();
EndBlock();
StartBlock(); // B3
EmitNop();
EndBlock(Branch(Reg(var2, 0), 1, 2));
StartBlock(true); // B4
EmitCall(Slot(-1), Slot(var2));
EndBlock(Jump(2));
StartBlock(); // B5
EmitNop();
EndBlock();
StartBlock(); // B6
Return(Reg(var3, 2));
EndBlock();
const int def_of_v2 = 2;
const int call_in_b1 = 4;
const int call_in_b4 = 10;
const int end_of_b1 = 5;
const int end_of_b4 = 11;
const int start_of_b6 = 14;
Allocate();
const int var3_reg = 2;
const int var3_slot = 2;
EXPECT_FALSE(IsParallelMovePresent(def_of_v2, Instruction::START, sequence(),
Reg(var3_reg), Slot()));
EXPECT_TRUE(IsParallelMovePresent(call_in_b1, Instruction::START, sequence(),
Reg(var3_reg), Slot(var3_slot)));
EXPECT_TRUE(IsParallelMovePresent(end_of_b1, Instruction::START, sequence(),
Slot(var3_slot), Reg()));
EXPECT_TRUE(IsParallelMovePresent(call_in_b4, Instruction::START, sequence(),
Reg(var3_reg), Slot(var3_slot)));
EXPECT_TRUE(IsParallelMovePresent(end_of_b4, Instruction::START, sequence(),
Slot(var3_slot), Reg()));
EXPECT_EQ(0,
GetParallelMoveCount(start_of_b6, Instruction::START, sequence()));
}
namespace {
enum class ParameterType { kFixedSlot, kSlot, kRegister, kFixedRegister };
......
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