Reland "Reland "[regalloc] Introduce deferred fixed ranges""
This is a reland of1ca088652d
Original change's description: > Reland "[regalloc] Introduce deferred fixed ranges" > > This is a reland ofb176931311
> > 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:
parent
1d28a5d888
commit
85017f0428
@ -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)";
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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 {
|
||||
|
@ -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>();
|
||||
}
|
||||
|
||||
|
@ -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() {
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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 };
|
||||
|
Loading…
Reference in New Issue
Block a user