}
-struct LiveRange::SpillAtDefinitionList : ZoneObject {
- SpillAtDefinitionList(int gap_index, InstructionOperand* operand,
- SpillAtDefinitionList* next)
- : gap_index(gap_index), operand(operand), next(next) {}
- const int gap_index;
- InstructionOperand* const operand;
- SpillAtDefinitionList* const next;
-};
-
-
#ifdef DEBUG
current_interval_(nullptr),
last_processed_use_(nullptr),
current_hint_operand_(nullptr),
+ spill_operand_(new (zone) InstructionOperand()),
spill_start_index_(kMaxInt),
- spill_type_(SpillType::kNoSpillType),
- spill_operand_(nullptr),
- spills_at_definition_(nullptr) {}
+ spill_range_(nullptr) {}
void LiveRange::set_assigned_register(int reg, Zone* zone) {
DCHECK(!HasRegisterAssigned() && !IsSpilled());
assigned_register_ = reg;
- // TODO(dcarney): stop aliasing hint operands.
- ConvertUsesToOperand(CreateAssignedOperand(zone));
+ if (spill_range_ == nullptr) {
+ ConvertOperands(zone);
+ }
}
-void LiveRange::MakeSpilled() {
+void LiveRange::MakeSpilled(Zone* zone) {
DCHECK(!IsSpilled());
- DCHECK(!TopLevel()->HasNoSpillType());
+ DCHECK(TopLevel()->HasAllocatedSpillOperand());
spilled_ = true;
assigned_register_ = kInvalidAssignment;
+ ConvertOperands(zone);
}
-void LiveRange::SpillAtDefinition(Zone* zone, int gap_index,
- InstructionOperand* operand) {
- DCHECK(HasNoSpillType());
- spills_at_definition_ = new (zone)
- SpillAtDefinitionList(gap_index, operand, spills_at_definition_);
-}
-
-
-void LiveRange::CommitSpillsAtDefinition(InstructionSequence* sequence,
- InstructionOperand* op) {
- auto to_spill = TopLevel()->spills_at_definition_;
- if (to_spill == nullptr) return;
- auto zone = sequence->zone();
- for (; to_spill != nullptr; to_spill = to_spill->next) {
- auto gap = sequence->GapAt(to_spill->gap_index);
- auto move = gap->GetOrCreateParallelMove(GapInstruction::START, zone);
- move->AddMove(to_spill->operand, op, zone);
- }
- TopLevel()->spills_at_definition_ = nullptr;
+bool LiveRange::HasAllocatedSpillOperand() const {
+ DCHECK(spill_operand_ != nullptr);
+ return !spill_operand_->IsIgnored() || spill_range_ != nullptr;
}
void LiveRange::SetSpillOperand(InstructionOperand* operand) {
- DCHECK(HasNoSpillType());
DCHECK(!operand->IsUnallocated());
- spill_type_ = SpillType::kSpillOperand;
- spill_operand_ = operand;
-}
-
-
-void LiveRange::SetSpillRange(SpillRange* spill_range) {
- DCHECK(HasNoSpillType() || HasSpillRange());
- DCHECK_NE(spill_range, nullptr);
- spill_type_ = SpillType::kSpillRange;
- spill_range_ = spill_range;
+ DCHECK(spill_operand_ != nullptr);
+ DCHECK(spill_operand_->IsIgnored());
+ spill_operand_->ConvertTo(operand->kind(), operand->index());
}
void LiveRange::CommitSpillOperand(InstructionOperand* operand) {
- DCHECK(HasSpillRange());
- DCHECK(!operand->IsUnallocated());
+ DCHECK(spill_range_ != nullptr);
DCHECK(!IsChild());
- spill_type_ = SpillType::kSpillOperand;
- spill_operand_ = operand;
+ spill_range_ = nullptr;
+ SetSpillOperand(operand);
+ for (auto range = this; range != nullptr; range = range->next()) {
+ if (range->IsSpilled()) {
+ range->ConvertUsesToOperand(operand);
+ }
+ }
}
default:
UNREACHABLE();
}
- } else {
- DCHECK(IsSpilled());
+ } else if (IsSpilled()) {
DCHECK(!HasRegisterAssigned());
op = TopLevel()->GetSpillOperand();
DCHECK(!op->IsUnallocated());
+ } else {
+ UnallocatedOperand* unalloc =
+ new (zone) UnallocatedOperand(UnallocatedOperand::NONE);
+ unalloc->set_virtual_register(id_);
+ op = unalloc;
}
return op;
}
}
+void LiveRange::ConvertOperands(Zone* zone) {
+ ConvertUsesToOperand(CreateAssignedOperand(zone));
+}
+
+
bool LiveRange::CanCover(LifetimePosition position) const {
if (IsEmpty()) return false;
return Start().Value() <= position.Value() &&
use_interval_ = result;
live_ranges().push_back(range);
end_position_ = node->end();
- DCHECK(!range->HasSpillRange());
+ DCHECK(range->GetSpillRange() == nullptr);
range->SetSpillRange(this);
}
}
-void RegisterAllocator::CommitAssignment() {
- for (auto range : live_ranges()) {
- if (range == nullptr || range->IsEmpty()) continue;
- // Register assignments were committed in set_assigned_register.
- if (range->HasRegisterAssigned()) continue;
- auto assigned = range->CreateAssignedOperand(code_zone());
- range->ConvertUsesToOperand(assigned);
- if (range->IsSpilled()) {
- range->CommitSpillsAtDefinition(code(), assigned);
- }
- }
-}
-
-
SpillRange* RegisterAllocator::AssignSpillRangeToLiveRange(LiveRange* range) {
DCHECK(FLAG_turbo_reuse_spill_slots);
auto spill_range = new (local_zone()) SpillRange(range, local_zone());
bool RegisterAllocator::TryReuseSpillForPhi(LiveRange* range) {
DCHECK(FLAG_turbo_reuse_spill_slots);
- DCHECK(range->HasNoSpillType());
+ DCHECK(!range->HasAllocatedSpillOperand());
if (range->IsChild() || !range->is_phi()) return false;
auto lookup = phi_map_.find(range->id());
const InstructionBlock* successor = code()->InstructionBlockAt(succ);
DCHECK(successor->PredecessorCount() == 1);
int gap_index = successor->first_instruction_index() + 1;
- range->SpillAtDefinition(local_zone(), gap_index, output);
range->SetSpillStartIndex(gap_index);
+
+ // This move to spill operand is not a real use. Liveness analysis
+ // and splitting of live ranges do not account for it.
+ // Thus it should be inserted to a lifetime position corresponding to
+ // the instruction end.
+ auto gap = code()->GapAt(gap_index);
+ auto move =
+ gap->GetOrCreateParallelMove(GapInstruction::BEFORE, code_zone());
+ move->AddMove(output, range->GetSpillOperand(), code_zone());
}
}
}
// Make sure we add a gap move for spilling (if we have not done
// so already).
if (!assigned) {
- range->SpillAtDefinition(local_zone(), gap_index, first_output);
range->SetSpillStartIndex(gap_index);
+
+ // This move to spill operand is not a real use. Liveness analysis
+ // and splitting of live ranges do not account for it.
+ // Thus it should be inserted to a lifetime position corresponding to
+ // the instruction end.
+ auto gap = code()->GapAt(gap_index);
+ auto move =
+ gap->GetOrCreateParallelMove(GapInstruction::BEFORE, code_zone());
+ move->AddMove(first_output, range->GetSpillOperand(), code_zone());
}
}
}
const ZoneList<MoveOperands>* move_operands = move->move_operands();
for (int i = 0; i < move_operands->length(); ++i) {
auto cur = &move_operands->at(i);
+ if (cur->IsIgnored()) continue;
auto from = cur->source();
auto to = cur->destination();
auto hint = to;
}
}
auto live_range = LiveRangeFor(phi_vreg);
- int gap_index = block->first_instruction_index();
- live_range->SpillAtDefinition(local_zone(), gap_index, output);
- live_range->SetSpillStartIndex(gap_index);
+ auto block_start = code()->GetBlockStart(block->rpo_number());
+ block_start->GetOrCreateParallelMove(GapInstruction::BEFORE, code_zone())
+ ->AddMove(output, live_range->GetSpillOperand(), code_zone());
+ live_range->SetSpillStartIndex(block->first_instruction_index());
// We use the phi-ness of some nodes in some later heuristics.
live_range->set_is_phi(true);
live_range->set_is_non_loop_phi(!block->IsLoopHeader());
// live ranges, every use requires the constant to be in a register.
// Without this hack, all uses with "any" policy would get the constant
// operand assigned.
- if (range->HasSpillOperand() && range->GetSpillOperand()->IsConstant()) {
+ if (range->HasAllocatedSpillOperand() &&
+ range->GetSpillOperand()->IsConstant()) {
for (auto pos = range->first_pos(); pos != nullptr; pos = pos->next_) {
pos->register_beneficial_ = true;
// TODO(dcarney): should the else case assert requires_reg_ == false?
// Check if the live range is spilled and the safe point is after
// the spill position.
- if (range->HasSpillOperand() &&
+ if (range->HasAllocatedSpillOperand() &&
safe_point >= range->spill_start_index() &&
!range->GetSpillOperand()->IsConstant()) {
TraceAlloc("Pointer for range %d (spilled at %d) at safe point %d\n",
TraceAlloc("Processing interval %d start=%d\n", current->id(),
position.Value());
- if (!current->HasNoSpillType()) {
+ if (current->HasAllocatedSpillOperand()) {
TraceAlloc("Live range %d already has a spill operand\n", current->id());
auto next_pos = position;
if (code()->IsGapAt(next_pos.InstructionIndex())) {
DCHECK(!FLAG_turbo_reuse_spill_slots);
// Check that we are the last range.
if (range->next() != nullptr) return;
- if (!range->TopLevel()->HasSpillOperand()) return;
+ if (!range->TopLevel()->HasAllocatedSpillOperand()) return;
auto spill_operand = range->TopLevel()->GetSpillOperand();
if (spill_operand->IsConstant()) return;
if (spill_operand->index() >= 0) {
DCHECK(!range->IsSpilled());
TraceAlloc("Spilling live range %d\n", range->id());
auto first = range->TopLevel();
- if (first->HasNoSpillType()) {
+ if (!first->HasAllocatedSpillOperand()) {
if (FLAG_turbo_reuse_spill_slots) {
AssignSpillRangeToLiveRange(first);
} else {
// Allocate a new operand referring to the spill slot.
RegisterKind kind = range->Kind();
int index = frame()->AllocateSpillSlot(kind == DOUBLE_REGISTERS);
- auto op_kind = kind == DOUBLE_REGISTERS
- ? InstructionOperand::DOUBLE_STACK_SLOT
- : InstructionOperand::STACK_SLOT;
- op = new (code_zone()) InstructionOperand(op_kind, index);
+ if (kind == DOUBLE_REGISTERS) {
+ op = DoubleStackSlotOperand::Create(index, local_zone());
+ } else {
+ DCHECK(kind == GENERAL_REGISTERS);
+ op = StackSlotOperand::Create(index, local_zone());
+ }
}
first->SetSpillOperand(op);
}
}
- range->MakeSpilled();
+ range->MakeSpilled(code_zone());
}
int assigned_register() const { return assigned_register_; }
int spill_start_index() const { return spill_start_index_; }
void set_assigned_register(int reg, Zone* zone);
- void MakeSpilled();
+ void MakeSpilled(Zone* zone);
bool is_phi() const { return is_phi_; }
void set_is_phi(bool is_phi) { is_phi_ = is_phi; }
bool is_non_loop_phi() const { return is_non_loop_phi_; }
return last_interval_->end();
}
- enum class SpillType { kNoSpillType, kSpillOperand, kSpillRange };
- SpillType spill_type() const { return spill_type_; }
- InstructionOperand* GetSpillOperand() const {
- return spill_type_ == SpillType::kSpillOperand ? spill_operand_ : nullptr;
- }
- SpillRange* GetSpillRange() const {
- return spill_type_ == SpillType::kSpillRange ? spill_range_ : nullptr;
- }
- bool HasNoSpillType() const { return spill_type_ == SpillType::kNoSpillType; }
- bool HasSpillOperand() const {
- return spill_type_ == SpillType::kSpillOperand;
- }
- bool HasSpillRange() const { return spill_type_ == SpillType::kSpillRange; }
-
- void SpillAtDefinition(Zone* zone, int gap_index,
- InstructionOperand* operand);
+ bool HasAllocatedSpillOperand() const;
+ InstructionOperand* GetSpillOperand() const { return spill_operand_; }
void SetSpillOperand(InstructionOperand* operand);
- void SetSpillRange(SpillRange* spill_range);
+
+ void SetSpillRange(SpillRange* spill_range) { spill_range_ = spill_range; }
+ SpillRange* GetSpillRange() const { return spill_range_; }
void CommitSpillOperand(InstructionOperand* operand);
- void CommitSpillsAtDefinition(InstructionSequence* sequence,
- InstructionOperand* operand);
void SetSpillStartIndex(int start) {
spill_start_index_ = Min(start, spill_start_index_);
#endif
private:
- struct SpillAtDefinitionList;
-
+ void ConvertOperands(Zone* zone);
void ConvertUsesToOperand(InstructionOperand* op);
UseInterval* FirstSearchIntervalForPosition(LifetimePosition position) const;
void AdvanceLastProcessedMarker(UseInterval* to_start_of,
LifetimePosition but_not_past) const;
- // TODO(dcarney): pack this structure better.
int id_;
bool spilled_;
bool is_phi_;
UsePosition* last_processed_use_;
// This is used as a cache, it's invalid outside of BuildLiveRanges.
InstructionOperand* current_hint_operand_;
+ InstructionOperand* spill_operand_;
int spill_start_index_;
- SpillType spill_type_;
- union {
- InstructionOperand* spill_operand_;
- SpillRange* spill_range_;
- };
- SpillAtDefinitionList* spills_at_definition_;
+ SpillRange* spill_range_;
friend class RegisterAllocator; // Assigns to kind_.
// Phase 5: reassign spill splots for maximal reuse.
void ReuseSpillSlots();
- // Phase 6: commit assignment.
- void CommitAssignment();
-
- // Phase 7: compute values for pointer maps.
+ // Phase 6: compute values for pointer maps.
void PopulatePointerMaps(); // TODO(titzer): rename to PopulateReferenceMaps.
- // Phase 8: reconnect split ranges with moves.
+ // Phase 7: reconnect split ranges with moves.
void ConnectRanges();
- // Phase 9: insert moves to connect ranges across basic blocks.
+ // Phase 8: insert moves to connect ranges across basic blocks.
void ResolveControlFlow();
private: