Reland "Reland "[regalloc] Introduce deferred fixed ranges""

This is a reland of 1ca088652d

Original change's description:
> Reland "[regalloc] Introduce deferred fixed ranges"
> 
> This is a reland of b176931311
> 
> Original change's description:
> > [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: Jaroslav Sevcik <jarin@chromium.org>
> > Reviewed-by: Sigurd Schneider <sigurds@chromium.org>
> > Cr-Commit-Position: refs/heads/master@{#60322}
> 
> Change-Id: I1a31150256eb5608db985b144aab7ea457169d0d
> Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1530810
> Commit-Queue: Sigurd Schneider <sigurds@chromium.org>
> Reviewed-by: Jaroslav Sevcik <jarin@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#60364}

Change-Id: If4a956716e7e4de132f706be2c395cdfdc04ec94
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1532328
Reviewed-by: Jaroslav Sevcik <jarin@chromium.org>
Commit-Queue: Sigurd Schneider <sigurds@chromium.org>
Cr-Commit-Position: refs/heads/master@{#60408}
This commit is contained in:
Sigurd Schneider 2019-03-21 10:14:34 +01:00 committed by Commit Bot
parent 1d28a5d888
commit 85017f0428
8 changed files with 628 additions and 287 deletions

View File

@ -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)";

View File

@ -162,6 +162,7 @@ void LiveRangeMerger::MarkRangesSpilledInDeferredBlocks() {
}
}
if (child == nullptr) {
DCHECK(!data()->is_turbo_control_flow_aware_allocation());
top->TreatAsSpilledInDeferredBlock(data()->allocation_zone(),
code->InstructionBlockCount());
}

View File

@ -735,10 +735,10 @@ bool LiveRange::ShouldBeAllocatedBefore(const LiveRange* other) const {
return true;
}
// The other has a smaller hint.
if (other->controlflow_hint() != kUnassignedRegister) {
if (controlflow_hint() > other->controlflow_hint()) {
return false;
}
// No hint, use first use position.
// Both have the same hint or no hint at all. Use first use position.
UsePosition* pos = first_pos();
UsePosition* other_pos = other->first_pos();
// To make the order total, handle the case where both positions are null.
@ -891,13 +891,15 @@ void TopLevelLiveRange::RecordSpillLocation(Zone* zone, int gap_index,
gap_index, operand, spill_move_insertion_locations_);
}
void TopLevelLiveRange::CommitSpillMoves(InstructionSequence* sequence,
void TopLevelLiveRange::CommitSpillMoves(RegisterAllocationData* data,
const InstructionOperand& op,
bool might_be_duplicated) {
DCHECK_IMPLIES(op.IsConstant(), GetSpillMoveInsertionLocations() == nullptr);
DCHECK_IMPLIES(op.IsConstant(),
GetSpillMoveInsertionLocations(data) == nullptr);
InstructionSequence* sequence = data->code();
Zone* zone = sequence->zone();
for (SpillMoveInsertionList* to_spill = GetSpillMoveInsertionLocations();
for (SpillMoveInsertionList* to_spill = GetSpillMoveInsertionLocations(data);
to_spill != nullptr; to_spill = to_spill->next) {
Instruction* instr = sequence->InstructionAt(to_spill->gap_index);
ParallelMove* move =
@ -1467,7 +1469,8 @@ void RegisterAllocationData::PhiMapValue::CommitAssignment(
RegisterAllocationData::RegisterAllocationData(
const RegisterConfiguration* config, Zone* zone, Frame* frame,
InstructionSequence* code, const char* debug_name)
InstructionSequence* code, RegisterAllocationFlags flags,
const char* debug_name)
: allocation_zone_(zone),
frame_(frame),
code_(code),
@ -1478,11 +1481,13 @@ RegisterAllocationData::RegisterAllocationData(
live_out_sets_(code->InstructionBlockCount(), nullptr, allocation_zone()),
live_ranges_(code->VirtualRegisterCount() * 2, nullptr,
allocation_zone()),
fixed_live_ranges_(this->config()->num_general_registers(), nullptr,
allocation_zone()),
fixed_live_ranges_(kNumberOfFixedRangesPerRegister *
this->config()->num_general_registers(),
nullptr, allocation_zone()),
fixed_float_live_ranges_(allocation_zone()),
fixed_double_live_ranges_(this->config()->num_double_registers(), nullptr,
allocation_zone()),
fixed_double_live_ranges_(kNumberOfFixedRangesPerRegister *
this->config()->num_double_registers(),
nullptr, allocation_zone()),
fixed_simd128_live_ranges_(allocation_zone()),
spill_ranges_(code->VirtualRegisterCount(), nullptr, allocation_zone()),
delayed_references_(allocation_zone()),
@ -1491,12 +1496,16 @@ RegisterAllocationData::RegisterAllocationData(
virtual_register_count_(code->VirtualRegisterCount()),
preassigned_slot_ranges_(zone),
spill_state_(code->InstructionBlockCount(), ZoneVector<LiveRange*>(zone),
zone) {
zone),
flags_(flags) {
if (!kSimpleFPAliasing) {
fixed_float_live_ranges_.resize(this->config()->num_float_registers(),
nullptr);
fixed_simd128_live_ranges_.resize(this->config()->num_simd128_registers(),
nullptr);
fixed_float_live_ranges_.resize(
kNumberOfFixedRangesPerRegister * this->config()->num_float_registers(),
nullptr);
fixed_simd128_live_ranges_.resize(
kNumberOfFixedRangesPerRegister *
this->config()->num_simd128_registers(),
nullptr);
}
assigned_registers_ = new (code_zone())
@ -1646,7 +1655,7 @@ SpillRange* RegisterAllocationData::AssignSpillRangeToLiveRange(
}
if (spill_mode == SpillMode::kSpillDeferred &&
(range->spill_type() != SpillType::kSpillRange)) {
DCHECK(FLAG_turbo_control_flow_aware_allocation);
DCHECK(is_turbo_control_flow_aware_allocation());
range->set_spill_type(SpillType::kDeferredSpillRange);
} else {
range->set_spill_type(SpillType::kSpillRange);
@ -1662,7 +1671,7 @@ SpillRange* RegisterAllocationData::AssignSpillRangeToLiveRange(
SpillRange* RegisterAllocationData::CreateSpillRangeForLiveRange(
TopLevelLiveRange* range) {
DCHECK(FLAG_turbo_preprocess_ranges);
DCHECK(is_turbo_preprocess_ranges());
DCHECK(!range->HasSpillOperand());
DCHECK(!range->IsSplinter());
SpillRange* spill_range =
@ -2072,13 +2081,16 @@ int LiveRangeBuilder::FixedFPLiveRangeID(int index, MachineRepresentation rep) {
int result = -index - 1;
switch (rep) {
case MachineRepresentation::kSimd128:
result -= config()->num_float_registers();
result -=
kNumberOfFixedRangesPerRegister * config()->num_float_registers();
V8_FALLTHROUGH;
case MachineRepresentation::kFloat32:
result -= config()->num_double_registers();
result -=
kNumberOfFixedRangesPerRegister * config()->num_double_registers();
V8_FALLTHROUGH;
case MachineRepresentation::kFloat64:
result -= config()->num_general_registers();
result -=
kNumberOfFixedRangesPerRegister * config()->num_general_registers();
break;
default:
UNREACHABLE();
@ -2087,22 +2099,29 @@ int LiveRangeBuilder::FixedFPLiveRangeID(int index, MachineRepresentation rep) {
return result;
}
TopLevelLiveRange* LiveRangeBuilder::FixedLiveRangeFor(int index) {
TopLevelLiveRange* LiveRangeBuilder::FixedLiveRangeFor(int index,
SpillMode spill_mode) {
int offset = spill_mode == SpillMode::kSpillAtDefinition
? 0
: config()->num_general_registers();
DCHECK(index < config()->num_general_registers());
TopLevelLiveRange* result = data()->fixed_live_ranges()[index];
TopLevelLiveRange* result = data()->fixed_live_ranges()[offset + index];
if (result == nullptr) {
MachineRepresentation rep = InstructionSequence::DefaultRepresentation();
result = data()->NewLiveRange(FixedLiveRangeID(index), rep);
result = data()->NewLiveRange(FixedLiveRangeID(offset + index), rep);
DCHECK(result->IsFixed());
result->set_assigned_register(index);
data()->MarkAllocated(rep, index);
data()->fixed_live_ranges()[index] = result;
if (spill_mode == SpillMode::kSpillDeferred) {
result->set_deferred_fixed();
}
data()->fixed_live_ranges()[offset + index] = result;
}
return result;
}
TopLevelLiveRange* LiveRangeBuilder::FixedFPLiveRangeFor(
int index, MachineRepresentation rep) {
int index, MachineRepresentation rep, SpillMode spill_mode) {
int num_regs = config()->num_double_registers();
ZoneVector<TopLevelLiveRange*>* live_ranges =
&data()->fixed_double_live_ranges();
@ -2121,20 +2140,26 @@ TopLevelLiveRange* LiveRangeBuilder::FixedFPLiveRangeFor(
}
}
int offset = spill_mode == SpillMode::kSpillAtDefinition ? 0 : num_regs;
DCHECK(index < num_regs);
USE(num_regs);
TopLevelLiveRange* result = (*live_ranges)[index];
TopLevelLiveRange* result = (*live_ranges)[offset + index];
if (result == nullptr) {
result = data()->NewLiveRange(FixedFPLiveRangeID(index, rep), rep);
result = data()->NewLiveRange(FixedFPLiveRangeID(offset + index, rep), rep);
DCHECK(result->IsFixed());
result->set_assigned_register(index);
data()->MarkAllocated(rep, index);
(*live_ranges)[index] = result;
if (spill_mode == SpillMode::kSpillDeferred) {
result->set_deferred_fixed();
}
(*live_ranges)[offset + index] = result;
}
return result;
}
TopLevelLiveRange* LiveRangeBuilder::LiveRangeFor(InstructionOperand* operand) {
TopLevelLiveRange* LiveRangeBuilder::LiveRangeFor(InstructionOperand* operand,
SpillMode spill_mode) {
if (operand->IsUnallocated()) {
return data()->GetOrCreateLiveRangeFor(
UnallocatedOperand::cast(operand)->virtual_register());
@ -2143,10 +2168,11 @@ TopLevelLiveRange* LiveRangeBuilder::LiveRangeFor(InstructionOperand* operand) {
ConstantOperand::cast(operand)->virtual_register());
} else if (operand->IsRegister()) {
return FixedLiveRangeFor(
LocationOperand::cast(operand)->GetRegister().code());
LocationOperand::cast(operand)->GetRegister().code(), spill_mode);
} else if (operand->IsFPRegister()) {
LocationOperand* op = LocationOperand::cast(operand);
return FixedFPLiveRangeFor(op->register_code(), op->representation());
return FixedFPLiveRangeFor(op->register_code(), op->representation(),
spill_mode);
} else {
return nullptr;
}
@ -2161,8 +2187,9 @@ UsePosition* LiveRangeBuilder::NewUsePosition(LifetimePosition pos,
UsePosition* LiveRangeBuilder::Define(LifetimePosition position,
InstructionOperand* operand, void* hint,
UsePositionHintType hint_type) {
TopLevelLiveRange* range = LiveRangeFor(operand);
UsePositionHintType hint_type,
SpillMode spill_mode) {
TopLevelLiveRange* range = LiveRangeFor(operand, spill_mode);
if (range == nullptr) return nullptr;
if (range->IsEmpty() || range->Start() > position) {
@ -2183,8 +2210,9 @@ UsePosition* LiveRangeBuilder::Define(LifetimePosition position,
UsePosition* LiveRangeBuilder::Use(LifetimePosition block_start,
LifetimePosition position,
InstructionOperand* operand, void* hint,
UsePositionHintType hint_type) {
TopLevelLiveRange* range = LiveRangeFor(operand);
UsePositionHintType hint_type,
SpillMode spill_mode) {
TopLevelLiveRange* range = LiveRangeFor(operand, spill_mode);
if (range == nullptr) return nullptr;
UsePosition* use_pos = nullptr;
if (operand->IsUnallocated()) {
@ -2208,6 +2236,7 @@ void LiveRangeBuilder::ProcessInstructions(const InstructionBlock* block,
fixed_float_live_ranges = (mask & kFloat32Bit) != 0;
fixed_simd128_live_ranges = (mask & kSimd128Bit) != 0;
}
SpillMode spill_mode = SpillModeForBlock(block);
for (int index = block->last_instruction_index(); index >= block_start;
index--) {
@ -2236,9 +2265,10 @@ void LiveRangeBuilder::ProcessInstructions(const InstructionBlock* block,
// exception value.
// TODO(mtrofin): should we explore an explicit opcode for
// the first instruction in the handler?
Define(LifetimePosition::GapFromInstructionIndex(index), output);
Define(LifetimePosition::GapFromInstructionIndex(index), output,
spill_mode);
} else {
Define(curr_position, output);
Define(curr_position, output, spill_mode);
}
}
@ -2249,7 +2279,7 @@ void LiveRangeBuilder::ProcessInstructions(const InstructionBlock* block,
// is OK because AddUseInterval will just merge it with the existing
// one at the end of the range.
int code = config()->GetAllocatableGeneralCode(i);
TopLevelLiveRange* range = FixedLiveRangeFor(code);
TopLevelLiveRange* range = FixedLiveRangeFor(code, spill_mode);
range->AddUseInterval(curr_position, curr_position.End(),
allocation_zone());
}
@ -2260,8 +2290,8 @@ void LiveRangeBuilder::ProcessInstructions(const InstructionBlock* block,
// Add a UseInterval for all DoubleRegisters. See comment above for
// general registers.
int code = config()->GetAllocatableDoubleCode(i);
TopLevelLiveRange* range =
FixedFPLiveRangeFor(code, MachineRepresentation::kFloat64);
TopLevelLiveRange* range = FixedFPLiveRangeFor(
code, MachineRepresentation::kFloat64, spill_mode);
range->AddUseInterval(curr_position, curr_position.End(),
allocation_zone());
}
@ -2273,8 +2303,8 @@ void LiveRangeBuilder::ProcessInstructions(const InstructionBlock* block,
// Add a UseInterval for all FloatRegisters. See comment above for
// general registers.
int code = config()->GetAllocatableFloatCode(i);
TopLevelLiveRange* range =
FixedFPLiveRangeFor(code, MachineRepresentation::kFloat32);
TopLevelLiveRange* range = FixedFPLiveRangeFor(
code, MachineRepresentation::kFloat32, spill_mode);
range->AddUseInterval(curr_position, curr_position.End(),
allocation_zone());
}
@ -2283,8 +2313,8 @@ void LiveRangeBuilder::ProcessInstructions(const InstructionBlock* block,
for (int i = 0; i < config()->num_allocatable_simd128_registers();
++i) {
int code = config()->GetAllocatableSimd128Code(i);
TopLevelLiveRange* range =
FixedFPLiveRangeFor(code, MachineRepresentation::kSimd128);
TopLevelLiveRange* range = FixedFPLiveRangeFor(
code, MachineRepresentation::kSimd128, spill_mode);
range->AddUseInterval(curr_position, curr_position.End(),
allocation_zone());
}
@ -2310,7 +2340,7 @@ void LiveRangeBuilder::ProcessInstructions(const InstructionBlock* block,
int vreg = unalloc->virtual_register();
live->Add(vreg);
if (unalloc->HasSlotPolicy()) {
if (FLAG_turbo_control_flow_aware_allocation) {
if (data()->is_turbo_control_flow_aware_allocation()) {
data()->GetOrCreateLiveRangeFor(vreg)->register_slot_use(
block->IsDeferred()
? TopLevelLiveRange::SlotUseKind::kDeferredSlotUse
@ -2321,7 +2351,7 @@ void LiveRangeBuilder::ProcessInstructions(const InstructionBlock* block,
}
}
}
Use(block_start_position, use_pos, input);
Use(block_start_position, use_pos, input, spill_mode);
}
for (size_t i = 0; i < instr->TempCount(); i++) {
@ -2338,8 +2368,8 @@ void LiveRangeBuilder::ProcessInstructions(const InstructionBlock* block,
}
}
}
Use(block_start_position, curr_position.End(), temp);
Define(curr_position, temp);
Use(block_start_position, curr_position.End(), temp, spill_mode);
Define(curr_position, temp, spill_mode);
}
// Process the moves of the instruction's gaps, making their sources live.
@ -2378,8 +2408,9 @@ void LiveRangeBuilder::ProcessInstructions(const InstructionBlock* block,
}
} else {
if (live->Contains(to_vreg)) {
to_use = Define(curr_position, &to, &from,
UsePosition::HintTypeForOperand(from));
to_use =
Define(curr_position, &to, &from,
UsePosition::HintTypeForOperand(from), spill_mode);
live->Remove(to_vreg);
} else {
cur->Eliminate();
@ -2387,10 +2418,10 @@ void LiveRangeBuilder::ProcessInstructions(const InstructionBlock* block,
}
}
} else {
Define(curr_position, &to);
Define(curr_position, &to, spill_mode);
}
UsePosition* from_use =
Use(block_start_position, curr_position, &from, hint, hint_type);
UsePosition* from_use = Use(block_start_position, curr_position, &from,
hint, hint_type, spill_mode);
// Mark range live.
if (from.IsUnallocated()) {
live->Add(UnallocatedOperand::cast(from).virtual_register());
@ -2525,7 +2556,8 @@ void LiveRangeBuilder::ProcessPhis(const InstructionBlock* block,
LifetimePosition block_start = LifetimePosition::GapFromInstructionIndex(
block->first_instruction_index());
UsePosition* use_pos = Define(block_start, &phi->output(), hint,
UsePosition::HintTypeForOperand(*hint));
UsePosition::HintTypeForOperand(*hint),
SpillModeForBlock(block));
MapPhiHint(hint, use_pos);
}
}
@ -3403,6 +3435,117 @@ bool LinearScanAllocator::ConsiderBlockForControlFlow(
!code()->InstructionBlockAt(predecessor)->IsDeferred());
}
void LinearScanAllocator::UpdateDeferredFixedRanges(SpillMode spill_mode,
InstructionBlock* block) {
if (spill_mode == SpillMode::kSpillDeferred) {
LifetimePosition max = LifetimePosition::InstructionFromInstructionIndex(
LastDeferredInstructionIndex(block));
// Adds range back to inactive, resolving resulting conflicts.
auto add_to_inactive = [this, max](LiveRange* range) {
AddToInactive(range);
// Splits other if it conflicts with range. Other is placed in unhandled
// for later reallocation.
auto split_conflicting = [this, max](LiveRange* range, LiveRange* other,
std::function<void(LiveRange*)>
update_caches) {
if (other->TopLevel()->IsFixed()) return;
int reg = range->assigned_register();
if (kSimpleFPAliasing || !check_fp_aliasing()) {
if (other->assigned_register() != reg) {
return;
}
} else {
if (!data()->config()->AreAliases(range->representation(), reg,
other->representation(),
other->assigned_register())) {
return;
}
}
// The inactive range might conflict, so check whether we need to
// split and spill. We can look for the first intersection, as there
// cannot be any intersections in the past, as those would have been a
// conflict then.
LifetimePosition next_start = range->FirstIntersection(other);
if (!next_start.IsValid() || (next_start > max)) {
// There is no conflict or the conflict is outside of the current
// stretch of deferred code. In either case we can ignore the
// inactive range.
return;
}
// They overlap. So we need to split active and reschedule it
// for allocation.
TRACE("Resolving conflict of %d with deferred fixed for register %s\n",
other->TopLevel()->vreg(),
RegisterName(other->assigned_register()));
LiveRange* split_off =
other->SplitAt(next_start, data()->allocation_zone());
DCHECK_NE(split_off, other);
AddToUnhandled(split_off);
update_caches(other);
};
// Now check for conflicts in active and inactive ranges. We might have
// conflicts in inactive, as we do not do this check on every block
// boundary but only on deferred/non-deferred changes but inactive
// live ranges might become live on any block boundary.
for (auto active : active_live_ranges()) {
split_conflicting(range, active, [this](LiveRange* updated) {
next_active_ranges_change_ =
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_);
});
}
};
if (mode() == GENERAL_REGISTERS) {
for (TopLevelLiveRange* current : data()->fixed_live_ranges()) {
if (current != nullptr) {
if (current->IsDeferredFixed()) {
add_to_inactive(current);
}
}
}
} else {
for (TopLevelLiveRange* current : data()->fixed_double_live_ranges()) {
if (current != nullptr) {
if (current->IsDeferredFixed()) {
add_to_inactive(current);
}
}
}
if (!kSimpleFPAliasing && check_fp_aliasing()) {
for (TopLevelLiveRange* current : data()->fixed_float_live_ranges()) {
if (current != nullptr) {
if (current->IsDeferredFixed()) {
add_to_inactive(current);
}
}
}
for (TopLevelLiveRange* current : data()->fixed_simd128_live_ranges()) {
if (current != nullptr) {
if (current->IsDeferredFixed()) {
add_to_inactive(current);
}
}
}
}
}
} 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;
}
}
}
}
bool LinearScanAllocator::BlockIsDeferredOrImmediatePredecessorIsNotDeferred(
const InstructionBlock* block) {
if (block->IsDeferred()) return true;
@ -3417,6 +3560,14 @@ bool LinearScanAllocator::BlockIsDeferredOrImmediatePredecessorIsNotDeferred(
return !pred_is_deferred;
}
bool LinearScanAllocator::HasNonDeferredPredecessor(InstructionBlock* block) {
for (auto pred : block->predecessors()) {
InstructionBlock* pred_block = code()->InstructionBlockAt(pred);
if (!pred_block->IsDeferred()) return true;
}
return false;
}
void LinearScanAllocator::AllocateRegisters() {
DCHECK(unhandled_live_ranges().empty());
DCHECK(active_live_ranges().empty());
@ -3444,18 +3595,30 @@ void LinearScanAllocator::AllocateRegisters() {
if (mode() == GENERAL_REGISTERS) {
for (TopLevelLiveRange* current : data()->fixed_live_ranges()) {
if (current != nullptr) AddToInactive(current);
if (current != nullptr) {
if (current->IsDeferredFixed()) continue;
AddToInactive(current);
}
}
} else {
for (TopLevelLiveRange* current : data()->fixed_double_live_ranges()) {
if (current != nullptr) AddToInactive(current);
if (current != nullptr) {
if (current->IsDeferredFixed()) continue;
AddToInactive(current);
}
}
if (!kSimpleFPAliasing && check_fp_aliasing()) {
for (TopLevelLiveRange* current : data()->fixed_float_live_ranges()) {
if (current != nullptr) AddToInactive(current);
if (current != nullptr) {
if (current->IsDeferredFixed()) continue;
AddToInactive(current);
}
}
for (TopLevelLiveRange* current : data()->fixed_simd128_live_ranges()) {
if (current != nullptr) AddToInactive(current);
if (current != nullptr) {
if (current->IsDeferredFixed()) continue;
AddToInactive(current);
}
}
}
}
@ -3478,9 +3641,9 @@ void LinearScanAllocator::AllocateRegisters() {
// those. Not only does this produce a potentially bad assignment, it also
// breaks with the invariant that we undo spills that happen in deferred code
// when crossing a deferred/non-deferred boundary.
while (
!unhandled_live_ranges().empty() ||
(FLAG_turbo_control_flow_aware_allocation && last_block < max_blocks)) {
while (!unhandled_live_ranges().empty() ||
(data()->is_turbo_control_flow_aware_allocation() &&
last_block < max_blocks)) {
LiveRange* current = unhandled_live_ranges().empty()
? nullptr
: *unhandled_live_ranges().begin();
@ -3489,9 +3652,9 @@ void LinearScanAllocator::AllocateRegisters() {
#ifdef DEBUG
allocation_finger_ = position;
#endif
if (FLAG_turbo_control_flow_aware_allocation) {
if (data()->is_turbo_control_flow_aware_allocation()) {
// Splintering is not supported.
CHECK(!FLAG_turbo_preprocess_ranges);
CHECK(!data()->is_turbo_preprocess_ranges());
// Check whether we just moved across a block boundary. This will trigger
// for the first range that is past the current boundary.
if (position >= next_block_boundary) {
@ -3518,9 +3681,28 @@ void LinearScanAllocator::AllocateRegisters() {
current_block->predecessors()[0].IsNext(
current_block->rpo_number());
spill_mode = current_block->IsDeferred()
? SpillMode::kSpillDeferred
: SpillMode::kSpillAtDefinition;
// When crossing a deferred/non-deferred boundary, we have to load or
// remove the deferred fixed ranges from inactive.
if ((spill_mode == SpillMode::kSpillDeferred) !=
current_block->IsDeferred()) {
// Update spill mode.
spill_mode = current_block->IsDeferred()
? SpillMode::kSpillDeferred
: SpillMode::kSpillAtDefinition;
ForwardStateTo(next_block_boundary);
#ifdef DEBUG
// Allow allocation at current position.
allocation_finger_ = next_block_boundary;
#endif
UpdateDeferredFixedRanges(spill_mode, current_block);
}
// Allocation relies on the fact that each non-deferred block has at
// least one non-deferred predecessor. Check this invariant here.
DCHECK_IMPLIES(!current_block->IsDeferred(),
HasNonDeferredPredecessor(current_block));
if (!fallthrough) {
#ifdef DEBUG
@ -3650,7 +3832,7 @@ void LinearScanAllocator::AllocateRegisters() {
}
bool LinearScanAllocator::TrySplitAndSpillSplinter(LiveRange* range) {
DCHECK(!FLAG_turbo_control_flow_aware_allocation);
DCHECK(!data()->is_turbo_control_flow_aware_allocation());
DCHECK(range->TopLevel()->IsSplinter());
// If we can spill the whole range, great. Otherwise, split above the
// first use needing a register and spill the top part.
@ -3918,7 +4100,7 @@ void LinearScanAllocator::ProcessCurrentRange(LiveRange* current,
FindFreeRegistersForRange(current, free_until_pos);
if (!TryAllocatePreferredReg(current, free_until_pos)) {
if (current->TopLevel()->IsSplinter()) {
DCHECK(!FLAG_turbo_control_flow_aware_allocation);
DCHECK(!data()->is_turbo_control_flow_aware_allocation());
if (TrySplitAndSpillSplinter(current)) return;
}
if (!TryAllocateFreeReg(current, free_until_pos)) {
@ -4170,6 +4352,14 @@ void LinearScanAllocator::AllocateBlockedReg(LiveRange* current,
new_end = block_pos[reg].Start();
}
// If there is no register available at all, we can only spill this range.
// Happens for instance on entry to deferred code where registers might
// become blocked yet we aim to reload ranges.
if (new_end == current->Start()) {
SpillBetween(current, new_end, register_use->pos(), spill_mode);
return;
}
// Split at the new end if we found one.
if (new_end != current->End()) {
LiveRange* tail = SplitBetween(current, current->Start(), new_end);
@ -4395,11 +4585,12 @@ void SpillSlotLocator::LocateSpillSlots() {
data()->live_ranges().size()); // TODO(neis): crbug.com/831822
if (range == nullptr || range->IsEmpty()) continue;
// We care only about ranges which spill in the frame.
if (!range->HasSpillRange() || range->IsSpilledOnlyInDeferredBlocks()) {
if (!range->HasSpillRange() ||
range->IsSpilledOnlyInDeferredBlocks(data())) {
continue;
}
TopLevelLiveRange::SpillMoveInsertionList* spills =
range->GetSpillMoveInsertionLocations();
range->GetSpillMoveInsertionLocations(data());
DCHECK_NOT_NULL(spills);
for (; spills != nullptr; spills = spills->next) {
code->GetInstructionBlock(spills->gap_index)->mark_needs_frame();
@ -4410,10 +4601,10 @@ void SpillSlotLocator::LocateSpillSlots() {
OperandAssigner::OperandAssigner(RegisterAllocationData* data) : data_(data) {}
void OperandAssigner::DecideSpillingMode() {
if (FLAG_turbo_control_flow_aware_allocation) {
if (data()->is_turbo_control_flow_aware_allocation()) {
for (auto range : data()->live_ranges()) {
int max_blocks = data()->code()->InstructionBlockCount();
if (range != nullptr && range->IsSpilledOnlyInDeferredBlocks()) {
if (range != nullptr && range->IsSpilledOnlyInDeferredBlocks(data())) {
// If the range is spilled only in deferred blocks and starts in
// a non-deferred block, we transition its representation here so
// that the LiveRangeConnector processes them correctly. If,
@ -4429,6 +4620,7 @@ void OperandAssigner::DecideSpillingMode() {
TRACE(
"Live range %d is spilled deferred code only but alive outside\n",
range->vreg());
DCHECK(data()->is_turbo_control_flow_aware_allocation());
range->TransitionRangeToDeferredSpill(data()->allocation_zone(),
max_blocks);
}
@ -4501,11 +4693,11 @@ void OperandAssigner::CommitAssignment() {
// blocks, we let ConnectLiveRanges and ResolveControlFlow find the blocks
// where a spill operand is expected, and then finalize by inserting the
// spills in the deferred blocks dominators.
if (!top_range->IsSpilledOnlyInDeferredBlocks()) {
if (!top_range->IsSpilledOnlyInDeferredBlocks(data())) {
// Spill at definition if the range isn't spilled only in deferred
// blocks.
top_range->CommitSpillMoves(
data()->code(), spill_operand,
data(), spill_operand,
top_range->has_slot_use() || top_range->spilled());
}
}
@ -4543,7 +4735,7 @@ void ReferenceMapPopulator::PopulateReferenceMaps() {
data()->live_ranges().size()); // TODO(neis): crbug.com/831822
if (range == nullptr) continue;
// Skip non-reference values.
if (!data()->IsReference(range)) continue;
if (!data()->code()->IsReference(range->vreg())) continue;
// Skip empty live ranges.
if (range->IsEmpty()) continue;
if (range->has_preassigned_slot()) continue;
@ -4625,7 +4817,7 @@ void ReferenceMapPopulator::PopulateReferenceMaps() {
// Check if the live range is spilled and the safe point is after
// the spill position.
int spill_index = range->IsSpilledOnlyInDeferredBlocks()
int spill_index = range->IsSpilledOnlyInDeferredBlocks(data())
? cur->Start().ToInstructionIndex()
: range->spill_start_index();
@ -4708,21 +4900,23 @@ void LiveRangeConnector::ResolveControlFlow(Zone* local_zone) {
}
if (!uses_reg) continue;
}
if (current->TopLevel()->IsSpilledOnlyInDeferredBlocks() &&
if (current->TopLevel()->IsSpilledOnlyInDeferredBlocks(data()) &&
pred_block->IsDeferred()) {
// The spill location should be defined in pred_block, so add
// pred_block to the list of blocks requiring a spill operand.
TRACE("Adding B%d to list of spill blocks for %d\n",
pred_block->rpo_number().ToInt(),
current->TopLevel()->vreg());
current->TopLevel()->GetListOfBlocksRequiringSpillOperands()->Add(
pred_block->rpo_number().ToInt());
current->TopLevel()
->GetListOfBlocksRequiringSpillOperands(data())
->Add(pred_block->rpo_number().ToInt());
}
}
int move_loc = ResolveControlFlow(block, cur_op, pred_block, pred_op);
USE(move_loc);
DCHECK_IMPLIES(
result.cur_cover_->TopLevel()->IsSpilledOnlyInDeferredBlocks() &&
result.cur_cover_->TopLevel()->IsSpilledOnlyInDeferredBlocks(
data()) &&
!(pred_op.IsAnyRegister() && cur_op.IsAnyRegister()),
code()->GetInstructionBlock(move_loc)->IsDeferred());
}
@ -4738,7 +4932,7 @@ void LiveRangeConnector::ResolveControlFlow(Zone* local_zone) {
CHECK_EQ(live_ranges_size,
data()->live_ranges().size()); // TODO(neis): crbug.com/831822
if (top == nullptr || top->IsEmpty() ||
!top->IsSpilledOnlyInDeferredBlocks())
!top->IsSpilledOnlyInDeferredBlocks(data()))
continue;
CommitSpillsInDeferredBlocks(top, finder.ArrayFor(top->vreg()), local_zone);
}
@ -4773,7 +4967,7 @@ void LiveRangeConnector::ConnectRanges(Zone* local_zone) {
CHECK_EQ(live_ranges_size,
data()->live_ranges().size()); // TODO(neis): crbug.com/831822
if (top_range == nullptr) continue;
bool connect_spilled = top_range->IsSpilledOnlyInDeferredBlocks();
bool connect_spilled = top_range->IsSpilledOnlyInDeferredBlocks(data());
LiveRange* first_range = top_range;
for (LiveRange *second_range = first_range->next(); second_range != nullptr;
first_range = second_range, second_range = second_range->next()) {
@ -4798,7 +4992,7 @@ void LiveRangeConnector::ConnectRanges(Zone* local_zone) {
DCHECK(block->IsDeferred());
// Performing a reload in this block, meaning the spill operand must
// be defined here.
top_range->GetListOfBlocksRequiringSpillOperands()->Add(
top_range->GetListOfBlocksRequiringSpillOperands(data())->Add(
block->rpo_number().ToInt());
}
@ -4862,7 +5056,7 @@ void LiveRangeConnector::ConnectRanges(Zone* local_zone) {
void LiveRangeConnector::CommitSpillsInDeferredBlocks(
TopLevelLiveRange* range, LiveRangeBoundArray* array, Zone* temp_zone) {
DCHECK(range->IsSpilledOnlyInDeferredBlocks());
DCHECK(range->IsSpilledOnlyInDeferredBlocks(data()));
DCHECK(!range->spilled());
InstructionSequence* code = data()->code();
@ -4880,14 +5074,15 @@ void LiveRangeConnector::CommitSpillsInDeferredBlocks(
continue;
range->AddBlockRequiringSpillOperand(
code->GetInstructionBlock(pos->pos().ToInstructionIndex())
->rpo_number());
->rpo_number(),
data());
}
}
ZoneQueue<int> worklist(temp_zone);
for (BitVector::Iterator iterator(
range->GetListOfBlocksRequiringSpillOperands());
range->GetListOfBlocksRequiringSpillOperands(data()));
!iterator.Done(); iterator.Advance()) {
worklist.push(iterator.Current());
}
@ -4896,7 +5091,8 @@ void LiveRangeConnector::CommitSpillsInDeferredBlocks(
// Seek the deferred blocks that dominate locations requiring spill operands,
// and spill there. We only need to spill at the start of such blocks.
BitVector done_blocks(
range->GetListOfBlocksRequiringSpillOperands()->length(), temp_zone);
range->GetListOfBlocksRequiringSpillOperands(data())->length(),
temp_zone);
while (!worklist.empty()) {
int block_id = worklist.front();
worklist.pop();

View File

@ -8,6 +8,7 @@
#include "src/base/bits.h"
#include "src/base/compiler-specific.h"
#include "src/compiler/backend/instruction.h"
#include "src/flags.h"
#include "src/globals.h"
#include "src/ostreams.h"
#include "src/register-configuration.h"
@ -17,6 +18,8 @@ namespace v8 {
namespace internal {
namespace compiler {
static const int32_t kUnassignedRegister = RegisterConfiguration::kMaxRegisters;
enum RegisterKind { GENERAL_REGISTERS, FP_REGISTERS };
// This class represents a single point of a InstructionOperand's lifetime. For
@ -170,6 +173,191 @@ class LifetimePosition final {
std::ostream& operator<<(std::ostream& os, const LifetimePosition pos);
enum class RegisterAllocationFlag : unsigned {
kTurboControlFlowAwareAllocation = 1 << 0,
kTurboPreprocessRanges = 1 << 1
};
typedef base::Flags<RegisterAllocationFlag> RegisterAllocationFlags;
class SpillRange;
class LiveRange;
class TopLevelLiveRange;
class RegisterAllocationData final : public ZoneObject {
public:
// Encodes whether a spill happens in deferred code (kSpillDeferred) or
// regular code (kSpillAtDefinition).
enum SpillMode { kSpillAtDefinition, kSpillDeferred };
bool is_turbo_control_flow_aware_allocation() const {
return flags_ & RegisterAllocationFlag::kTurboControlFlowAwareAllocation;
}
bool is_turbo_preprocess_ranges() const {
return flags_ & RegisterAllocationFlag::kTurboPreprocessRanges;
}
static constexpr int kNumberOfFixedRangesPerRegister = 2;
class PhiMapValue : public ZoneObject {
public:
PhiMapValue(PhiInstruction* phi, const InstructionBlock* block, Zone* zone);
const PhiInstruction* phi() const { return phi_; }
const InstructionBlock* block() const { return block_; }
// For hinting.
int assigned_register() const { return assigned_register_; }
void set_assigned_register(int register_code) {
DCHECK_EQ(assigned_register_, kUnassignedRegister);
assigned_register_ = register_code;
}
void UnsetAssignedRegister() { assigned_register_ = kUnassignedRegister; }
void AddOperand(InstructionOperand* operand);
void CommitAssignment(const InstructionOperand& operand);
private:
PhiInstruction* const phi_;
const InstructionBlock* const block_;
ZoneVector<InstructionOperand*> incoming_operands_;
int assigned_register_;
};
typedef ZoneMap<int, PhiMapValue*> PhiMap;
struct DelayedReference {
ReferenceMap* map;
InstructionOperand* operand;
};
typedef ZoneVector<DelayedReference> DelayedReferences;
typedef ZoneVector<std::pair<TopLevelLiveRange*, int>>
RangesWithPreassignedSlots;
RegisterAllocationData(const RegisterConfiguration* config,
Zone* allocation_zone, Frame* frame,
InstructionSequence* code,
RegisterAllocationFlags flags,
const char* debug_name = nullptr);
const ZoneVector<TopLevelLiveRange*>& live_ranges() const {
return live_ranges_;
}
ZoneVector<TopLevelLiveRange*>& live_ranges() { return live_ranges_; }
const ZoneVector<TopLevelLiveRange*>& fixed_live_ranges() const {
return fixed_live_ranges_;
}
ZoneVector<TopLevelLiveRange*>& fixed_live_ranges() {
return fixed_live_ranges_;
}
ZoneVector<TopLevelLiveRange*>& fixed_float_live_ranges() {
return fixed_float_live_ranges_;
}
const ZoneVector<TopLevelLiveRange*>& fixed_float_live_ranges() const {
return fixed_float_live_ranges_;
}
ZoneVector<TopLevelLiveRange*>& fixed_double_live_ranges() {
return fixed_double_live_ranges_;
}
const ZoneVector<TopLevelLiveRange*>& fixed_double_live_ranges() const {
return fixed_double_live_ranges_;
}
ZoneVector<TopLevelLiveRange*>& fixed_simd128_live_ranges() {
return fixed_simd128_live_ranges_;
}
const ZoneVector<TopLevelLiveRange*>& fixed_simd128_live_ranges() const {
return fixed_simd128_live_ranges_;
}
ZoneVector<BitVector*>& live_in_sets() { return live_in_sets_; }
ZoneVector<BitVector*>& live_out_sets() { return live_out_sets_; }
ZoneVector<SpillRange*>& spill_ranges() { return spill_ranges_; }
DelayedReferences& delayed_references() { return delayed_references_; }
InstructionSequence* code() const { return code_; }
// This zone is for data structures only needed during register allocation
// phases.
Zone* allocation_zone() const { return allocation_zone_; }
// This zone is for InstructionOperands and moves that live beyond register
// allocation.
Zone* code_zone() const { return code()->zone(); }
Frame* frame() const { return frame_; }
const char* debug_name() const { return debug_name_; }
const RegisterConfiguration* config() const { return config_; }
MachineRepresentation RepresentationFor(int virtual_register);
TopLevelLiveRange* GetOrCreateLiveRangeFor(int index);
// Creates a new live range.
TopLevelLiveRange* NewLiveRange(int index, MachineRepresentation rep);
TopLevelLiveRange* NextLiveRange(MachineRepresentation rep);
SpillRange* AssignSpillRangeToLiveRange(TopLevelLiveRange* range,
SpillMode spill_mode);
SpillRange* CreateSpillRangeForLiveRange(TopLevelLiveRange* range);
MoveOperands* AddGapMove(int index, Instruction::GapPosition position,
const InstructionOperand& from,
const InstructionOperand& to);
bool ExistsUseWithoutDefinition();
bool RangesDefinedInDeferredStayInDeferred();
void MarkFixedUse(MachineRepresentation rep, int index);
bool HasFixedUse(MachineRepresentation rep, int index);
void MarkAllocated(MachineRepresentation rep, int index);
PhiMapValue* InitializePhiMap(const InstructionBlock* block,
PhiInstruction* phi);
PhiMapValue* GetPhiMapValueFor(TopLevelLiveRange* top_range);
PhiMapValue* GetPhiMapValueFor(int virtual_register);
bool IsBlockBoundary(LifetimePosition pos) const;
RangesWithPreassignedSlots& preassigned_slot_ranges() {
return preassigned_slot_ranges_;
}
void RememberSpillState(RpoNumber block,
const ZoneVector<LiveRange*>& state) {
spill_state_[block.ToSize()] = state;
}
ZoneVector<LiveRange*>& GetSpillState(RpoNumber block) {
auto& result = spill_state_[block.ToSize()];
return result;
}
void ResetSpillState() { spill_state_.clear(); }
private:
int GetNextLiveRangeId();
Zone* const allocation_zone_;
Frame* const frame_;
InstructionSequence* const code_;
const char* const debug_name_;
const RegisterConfiguration* const config_;
PhiMap phi_map_;
ZoneVector<BitVector*> live_in_sets_;
ZoneVector<BitVector*> live_out_sets_;
ZoneVector<TopLevelLiveRange*> live_ranges_;
ZoneVector<TopLevelLiveRange*> fixed_live_ranges_;
ZoneVector<TopLevelLiveRange*> fixed_float_live_ranges_;
ZoneVector<TopLevelLiveRange*> fixed_double_live_ranges_;
ZoneVector<TopLevelLiveRange*> fixed_simd128_live_ranges_;
ZoneVector<SpillRange*> spill_ranges_;
DelayedReferences delayed_references_;
BitVector* assigned_registers_;
BitVector* assigned_double_registers_;
BitVector* fixed_register_use_;
BitVector* fixed_fp_register_use_;
int virtual_register_count_;
RangesWithPreassignedSlots preassigned_slot_ranges_;
ZoneVector<ZoneVector<LiveRange*>> spill_state_;
RegisterAllocationFlags flags_;
DISALLOW_COPY_AND_ASSIGN(RegisterAllocationData);
};
// Representation of the non-empty interval [start,end[.
class UseInterval final : public ZoneObject {
public:
@ -242,8 +430,6 @@ enum class UsePositionHintType : uint8_t {
kUnresolved
};
static const int32_t kUnassignedRegister = RegisterConfiguration::kMaxRegisters;
// Representation of a use position.
class V8_EXPORT_PRIVATE UsePosition final
: public NON_EXPORTED_BASE(ZoneObject) {
@ -467,11 +653,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 +756,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); }
@ -670,7 +859,7 @@ class V8_EXPORT_PRIVATE TopLevelLiveRange final : public LiveRange {
spill_start_index_ = Min(start, spill_start_index_);
}
void CommitSpillMoves(InstructionSequence* sequence,
void CommitSpillMoves(RegisterAllocationData* data,
const InstructionOperand& operand,
bool might_be_duplicated);
@ -682,7 +871,6 @@ class V8_EXPORT_PRIVATE TopLevelLiveRange final : public LiveRange {
// deferred blocks. If so, we insert here spills for non-spilled ranges
// with slot use positions.
void TreatAsSpilledInDeferredBlock(Zone* zone, int total_block_count) {
DCHECK(!FLAG_turbo_control_flow_aware_allocation);
spill_start_index_ = -1;
spilled_in_deferred_blocks_ = true;
spill_move_insertion_locations_ = nullptr;
@ -693,7 +881,6 @@ class V8_EXPORT_PRIVATE TopLevelLiveRange final : public LiveRange {
// Updates internal data structures to reflect that this range is not
// spilled at definition but instead spilled in some blocks only.
void TransitionRangeToDeferredSpill(Zone* zone, int total_block_count) {
DCHECK(FLAG_turbo_control_flow_aware_allocation);
spill_start_index_ = -1;
spill_move_insertion_locations_ = nullptr;
list_of_blocks_requiring_spill_operands_ =
@ -732,8 +919,8 @@ class V8_EXPORT_PRIVATE TopLevelLiveRange final : public LiveRange {
int GetMaxChildCount() const { return last_child_id_ + 1; }
bool IsSpilledOnlyInDeferredBlocks() const {
if (FLAG_turbo_control_flow_aware_allocation) {
bool IsSpilledOnlyInDeferredBlocks(const RegisterAllocationData* data) const {
if (data->is_turbo_control_flow_aware_allocation()) {
return spill_type() == SpillType::kDeferredSpillRange;
}
return spilled_in_deferred_blocks_;
@ -741,8 +928,9 @@ class V8_EXPORT_PRIVATE TopLevelLiveRange final : public LiveRange {
struct SpillMoveInsertionList;
SpillMoveInsertionList* GetSpillMoveInsertionLocations() const {
DCHECK(!IsSpilledOnlyInDeferredBlocks());
SpillMoveInsertionList* GetSpillMoveInsertionLocations(
const RegisterAllocationData* data) const {
DCHECK(!IsSpilledOnlyInDeferredBlocks(data));
return spill_move_insertion_locations_;
}
TopLevelLiveRange* splinter() const { return splinter_; }
@ -760,13 +948,15 @@ class V8_EXPORT_PRIVATE TopLevelLiveRange final : public LiveRange {
void MarkHasPreassignedSlot() { has_preassigned_slot_ = true; }
bool has_preassigned_slot() const { return has_preassigned_slot_; }
void AddBlockRequiringSpillOperand(RpoNumber block_id) {
DCHECK(IsSpilledOnlyInDeferredBlocks());
GetListOfBlocksRequiringSpillOperands()->Add(block_id.ToInt());
void AddBlockRequiringSpillOperand(RpoNumber block_id,
const RegisterAllocationData* data) {
DCHECK(IsSpilledOnlyInDeferredBlocks(data));
GetListOfBlocksRequiringSpillOperands(data)->Add(block_id.ToInt());
}
BitVector* GetListOfBlocksRequiringSpillOperands() const {
DCHECK(IsSpilledOnlyInDeferredBlocks());
BitVector* GetListOfBlocksRequiringSpillOperands(
const RegisterAllocationData* data) const {
DCHECK(IsSpilledOnlyInDeferredBlocks(data));
return list_of_blocks_requiring_spill_operands_;
}
@ -778,6 +968,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_;
@ -855,171 +1046,6 @@ class SpillRange final : public ZoneObject {
DISALLOW_COPY_AND_ASSIGN(SpillRange);
};
class RegisterAllocationData final : public ZoneObject {
public:
// Encodes whether a spill happens in deferred code (kSpillDeferred) or
// regular code (kSpillAtDefinition).
enum SpillMode { kSpillAtDefinition, kSpillDeferred };
class PhiMapValue : public ZoneObject {
public:
PhiMapValue(PhiInstruction* phi, const InstructionBlock* block, Zone* zone);
const PhiInstruction* phi() const { return phi_; }
const InstructionBlock* block() const { return block_; }
// For hinting.
int assigned_register() const { return assigned_register_; }
void set_assigned_register(int register_code) {
DCHECK_EQ(assigned_register_, kUnassignedRegister);
assigned_register_ = register_code;
}
void UnsetAssignedRegister() { assigned_register_ = kUnassignedRegister; }
void AddOperand(InstructionOperand* operand);
void CommitAssignment(const InstructionOperand& operand);
private:
PhiInstruction* const phi_;
const InstructionBlock* const block_;
ZoneVector<InstructionOperand*> incoming_operands_;
int assigned_register_;
};
typedef ZoneMap<int, PhiMapValue*> PhiMap;
struct DelayedReference {
ReferenceMap* map;
InstructionOperand* operand;
};
typedef ZoneVector<DelayedReference> DelayedReferences;
typedef ZoneVector<std::pair<TopLevelLiveRange*, int>>
RangesWithPreassignedSlots;
RegisterAllocationData(const RegisterConfiguration* config,
Zone* allocation_zone, Frame* frame,
InstructionSequence* code,
const char* debug_name = nullptr);
const ZoneVector<TopLevelLiveRange*>& live_ranges() const {
return live_ranges_;
}
ZoneVector<TopLevelLiveRange*>& live_ranges() { return live_ranges_; }
const ZoneVector<TopLevelLiveRange*>& fixed_live_ranges() const {
return fixed_live_ranges_;
}
ZoneVector<TopLevelLiveRange*>& fixed_live_ranges() {
return fixed_live_ranges_;
}
ZoneVector<TopLevelLiveRange*>& fixed_float_live_ranges() {
return fixed_float_live_ranges_;
}
const ZoneVector<TopLevelLiveRange*>& fixed_float_live_ranges() const {
return fixed_float_live_ranges_;
}
ZoneVector<TopLevelLiveRange*>& fixed_double_live_ranges() {
return fixed_double_live_ranges_;
}
const ZoneVector<TopLevelLiveRange*>& fixed_double_live_ranges() const {
return fixed_double_live_ranges_;
}
ZoneVector<TopLevelLiveRange*>& fixed_simd128_live_ranges() {
return fixed_simd128_live_ranges_;
}
const ZoneVector<TopLevelLiveRange*>& fixed_simd128_live_ranges() const {
return fixed_simd128_live_ranges_;
}
ZoneVector<BitVector*>& live_in_sets() { return live_in_sets_; }
ZoneVector<BitVector*>& live_out_sets() { return live_out_sets_; }
ZoneVector<SpillRange*>& spill_ranges() { return spill_ranges_; }
DelayedReferences& delayed_references() { return delayed_references_; }
InstructionSequence* code() const { return code_; }
// This zone is for data structures only needed during register allocation
// phases.
Zone* allocation_zone() const { return allocation_zone_; }
// This zone is for InstructionOperands and moves that live beyond register
// allocation.
Zone* code_zone() const { return code()->zone(); }
Frame* frame() const { return frame_; }
const char* debug_name() const { return debug_name_; }
const RegisterConfiguration* config() const { return config_; }
MachineRepresentation RepresentationFor(int virtual_register);
TopLevelLiveRange* GetOrCreateLiveRangeFor(int index);
// Creates a new live range.
TopLevelLiveRange* NewLiveRange(int index, MachineRepresentation rep);
TopLevelLiveRange* NextLiveRange(MachineRepresentation rep);
SpillRange* AssignSpillRangeToLiveRange(TopLevelLiveRange* range,
SpillMode spill_mode);
SpillRange* CreateSpillRangeForLiveRange(TopLevelLiveRange* range);
MoveOperands* AddGapMove(int index, Instruction::GapPosition position,
const InstructionOperand& from,
const InstructionOperand& to);
bool IsReference(TopLevelLiveRange* top_range) const {
return code()->IsReference(top_range->vreg());
}
bool ExistsUseWithoutDefinition();
bool RangesDefinedInDeferredStayInDeferred();
void MarkFixedUse(MachineRepresentation rep, int index);
bool HasFixedUse(MachineRepresentation rep, int index);
void MarkAllocated(MachineRepresentation rep, int index);
PhiMapValue* InitializePhiMap(const InstructionBlock* block,
PhiInstruction* phi);
PhiMapValue* GetPhiMapValueFor(TopLevelLiveRange* top_range);
PhiMapValue* GetPhiMapValueFor(int virtual_register);
bool IsBlockBoundary(LifetimePosition pos) const;
RangesWithPreassignedSlots& preassigned_slot_ranges() {
return preassigned_slot_ranges_;
}
void RememberSpillState(RpoNumber block,
const ZoneVector<LiveRange*>& state) {
spill_state_[block.ToSize()] = state;
}
ZoneVector<LiveRange*>& GetSpillState(RpoNumber block) {
auto& result = spill_state_[block.ToSize()];
return result;
}
void ResetSpillState() { spill_state_.clear(); }
private:
int GetNextLiveRangeId();
Zone* const allocation_zone_;
Frame* const frame_;
InstructionSequence* const code_;
const char* const debug_name_;
const RegisterConfiguration* const config_;
PhiMap phi_map_;
ZoneVector<BitVector*> live_in_sets_;
ZoneVector<BitVector*> live_out_sets_;
ZoneVector<TopLevelLiveRange*> live_ranges_;
ZoneVector<TopLevelLiveRange*> fixed_live_ranges_;
ZoneVector<TopLevelLiveRange*> fixed_float_live_ranges_;
ZoneVector<TopLevelLiveRange*> fixed_double_live_ranges_;
ZoneVector<TopLevelLiveRange*> fixed_simd128_live_ranges_;
ZoneVector<SpillRange*> spill_ranges_;
DelayedReferences delayed_references_;
BitVector* assigned_registers_;
BitVector* assigned_double_registers_;
BitVector* fixed_register_use_;
BitVector* fixed_fp_register_use_;
int virtual_register_count_;
RangesWithPreassignedSlots preassigned_slot_ranges_;
ZoneVector<ZoneVector<LiveRange*>> spill_state_;
DISALLOW_COPY_AND_ASSIGN(RegisterAllocationData);
};
class ConstraintBuilder final : public ZoneObject {
public:
explicit ConstraintBuilder(RegisterAllocationData* data);
@ -1061,6 +1087,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 +1114,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 +1126,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 (data()->is_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 +1285,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 {

View File

@ -401,11 +401,12 @@ class PipelineData {
}
void InitializeRegisterAllocationData(const RegisterConfiguration* config,
CallDescriptor* call_descriptor) {
CallDescriptor* call_descriptor,
RegisterAllocationFlags flags) {
DCHECK_NULL(register_allocation_data_);
register_allocation_data_ = new (register_allocation_zone())
RegisterAllocationData(config, register_allocation_zone(), frame(),
sequence(), debug_name());
sequence(), flags, debug_name());
}
void InitializeOsrHelper() {
@ -2862,7 +2863,14 @@ void PipelineImpl::AllocateRegisters(const RegisterConfiguration* config,
data_->sequence()->ValidateDeferredBlockExitPaths();
#endif
data->InitializeRegisterAllocationData(config, call_descriptor);
RegisterAllocationFlags flags;
if (data->info()->is_turbo_control_flow_aware_allocation()) {
flags |= RegisterAllocationFlag::kTurboControlFlowAwareAllocation;
}
if (data->info()->is_turbo_preprocess_ranges()) {
flags |= RegisterAllocationFlag::kTurboPreprocessRanges;
}
data->InitializeRegisterAllocationData(config, call_descriptor, flags);
if (info()->is_osr()) data->osr_helper()->SetupFrame(data->frame());
Run<MeetRegisterConstraintsPhase>();
@ -2883,7 +2891,7 @@ void PipelineImpl::AllocateRegisters(const RegisterConfiguration* config,
data->register_allocation_data());
}
if (FLAG_turbo_preprocess_ranges) {
if (info()->is_turbo_preprocess_ranges()) {
Run<SplinterLiveRangesPhase>();
if (info()->trace_turbo_json_enabled() &&
!data->MayHaveUnverifiableGraph()) {
@ -2899,7 +2907,7 @@ void PipelineImpl::AllocateRegisters(const RegisterConfiguration* config,
Run<AllocateFPRegistersPhase<LinearScanAllocator>>();
}
if (FLAG_turbo_preprocess_ranges) {
if (info()->is_turbo_preprocess_ranges()) {
Run<MergeSplintersPhase>();
}

View File

@ -86,6 +86,13 @@ void OptimizedCompilationInfo::ConfigureFlags() {
default:
break;
}
if (FLAG_turbo_control_flow_aware_allocation) {
MarkAsTurboControlFlowAwareAllocation();
}
if (FLAG_turbo_preprocess_ranges) {
MarkAsTurboPreprocessRanges();
}
}
OptimizedCompilationInfo::~OptimizedCompilationInfo() {

View File

@ -55,7 +55,9 @@ class V8_EXPORT_PRIVATE OptimizedCompilationInfo final {
kTraceTurboJson = 1 << 14,
kTraceTurboGraph = 1 << 15,
kTraceTurboScheduled = 1 << 16,
kWasmRuntimeExceptionSupport = 1 << 17
kWasmRuntimeExceptionSupport = 1 << 17,
kTurboControlFlowAwareAllocation = 1 << 18,
kTurboPreprocessRanges = 1 << 19
};
// Construct a compilation info for optimized compilation.
@ -84,6 +86,18 @@ class V8_EXPORT_PRIVATE OptimizedCompilationInfo final {
// Flags used by optimized compilation.
void MarkAsTurboControlFlowAwareAllocation() {
SetFlag(kTurboControlFlowAwareAllocation);
}
bool is_turbo_control_flow_aware_allocation() const {
return GetFlag(kTurboControlFlowAwareAllocation);
}
void MarkAsTurboPreprocessRanges() { SetFlag(kTurboPreprocessRanges); }
bool is_turbo_preprocess_ranges() const {
return GetFlag(kTurboPreprocessRanges);
}
void MarkAsFunctionContextSpecializing() {
SetFlag(kFunctionContextSpecializing);
}

View File

@ -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 };