[turbofan] Split ConstraintBuilder off of LiveRangeBuilder.
Plus some driveby cleanup. Review URL: https://codereview.chromium.org/1099093002 Cr-Commit-Position: refs/heads/master@{#27961}
This commit is contained in:
parent
636cb4f365
commit
6a7cb78cbd
@ -716,7 +716,7 @@ struct MeetRegisterConstraintsPhase {
|
||||
static const char* phase_name() { return "meet register constraints"; }
|
||||
|
||||
void Run(PipelineData* data, Zone* temp_zone) {
|
||||
LiveRangeBuilder builder(data->register_allocation_data());
|
||||
ConstraintBuilder builder(data->register_allocation_data());
|
||||
builder.MeetRegisterConstraints();
|
||||
}
|
||||
};
|
||||
@ -726,7 +726,7 @@ struct ResolvePhisPhase {
|
||||
static const char* phase_name() { return "resolve phis"; }
|
||||
|
||||
void Run(PipelineData* data, Zone* temp_zone) {
|
||||
LiveRangeBuilder builder(data->register_allocation_data());
|
||||
ConstraintBuilder builder(data->register_allocation_data());
|
||||
builder.ResolvePhis();
|
||||
}
|
||||
};
|
||||
|
@ -69,6 +69,11 @@ bool IsBlockBoundary(const InstructionSequence* code, LifetimePosition pos) {
|
||||
}
|
||||
|
||||
|
||||
Instruction* GetLastInstruction(InstructionSequence* code,
|
||||
const InstructionBlock* block) {
|
||||
return code->InstructionAt(block->last_instruction_index());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
@ -833,6 +838,236 @@ void RegisterAllocationData::SetLiveRangeAssignedRegister(LiveRange* range,
|
||||
}
|
||||
|
||||
|
||||
ConstraintBuilder::ConstraintBuilder(RegisterAllocationData* data)
|
||||
: data_(data) {}
|
||||
|
||||
|
||||
InstructionOperand* ConstraintBuilder::AllocateFixed(
|
||||
UnallocatedOperand* operand, int pos, bool is_tagged) {
|
||||
TRACE("Allocating fixed reg for op %d\n", operand->virtual_register());
|
||||
DCHECK(operand->HasFixedPolicy());
|
||||
InstructionOperand allocated;
|
||||
if (operand->HasFixedSlotPolicy()) {
|
||||
allocated = AllocatedOperand(AllocatedOperand::STACK_SLOT,
|
||||
operand->fixed_slot_index());
|
||||
} else if (operand->HasFixedRegisterPolicy()) {
|
||||
allocated = AllocatedOperand(AllocatedOperand::REGISTER,
|
||||
operand->fixed_register_index());
|
||||
} else if (operand->HasFixedDoubleRegisterPolicy()) {
|
||||
allocated = AllocatedOperand(AllocatedOperand::DOUBLE_REGISTER,
|
||||
operand->fixed_register_index());
|
||||
} else {
|
||||
UNREACHABLE();
|
||||
}
|
||||
InstructionOperand::ReplaceWith(operand, &allocated);
|
||||
if (is_tagged) {
|
||||
TRACE("Fixed reg is tagged at %d\n", pos);
|
||||
auto instr = InstructionAt(pos);
|
||||
if (instr->HasReferenceMap()) {
|
||||
instr->reference_map()->RecordReference(*operand);
|
||||
}
|
||||
}
|
||||
return operand;
|
||||
}
|
||||
|
||||
|
||||
void ConstraintBuilder::MeetRegisterConstraints() {
|
||||
for (auto block : code()->instruction_blocks()) {
|
||||
MeetRegisterConstraints(block);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ConstraintBuilder::MeetRegisterConstraints(const InstructionBlock* block) {
|
||||
int start = block->first_instruction_index();
|
||||
int end = block->last_instruction_index();
|
||||
DCHECK_NE(-1, start);
|
||||
for (int i = start; i <= end; ++i) {
|
||||
MeetConstraintsBefore(i);
|
||||
if (i != end) MeetConstraintsAfter(i);
|
||||
}
|
||||
// Meet register constraints for the instruction in the end.
|
||||
MeetRegisterConstraintsForLastInstructionInBlock(block);
|
||||
}
|
||||
|
||||
|
||||
void ConstraintBuilder::MeetRegisterConstraintsForLastInstructionInBlock(
|
||||
const InstructionBlock* block) {
|
||||
int end = block->last_instruction_index();
|
||||
auto last_instruction = InstructionAt(end);
|
||||
for (size_t i = 0; i < last_instruction->OutputCount(); i++) {
|
||||
auto output_operand = last_instruction->OutputAt(i);
|
||||
DCHECK(!output_operand->IsConstant());
|
||||
auto output = UnallocatedOperand::cast(output_operand);
|
||||
int output_vreg = output->virtual_register();
|
||||
auto range = LiveRangeFor(output_vreg);
|
||||
bool assigned = false;
|
||||
if (output->HasFixedPolicy()) {
|
||||
AllocateFixed(output, -1, false);
|
||||
// This value is produced on the stack, we never need to spill it.
|
||||
if (output->IsStackSlot()) {
|
||||
DCHECK(StackSlotOperand::cast(output)->index() <
|
||||
data()->frame()->GetSpillSlotCount());
|
||||
range->SetSpillOperand(StackSlotOperand::cast(output));
|
||||
range->SetSpillStartIndex(end);
|
||||
assigned = true;
|
||||
}
|
||||
|
||||
for (auto succ : block->successors()) {
|
||||
const InstructionBlock* successor = code()->InstructionBlockAt(succ);
|
||||
DCHECK(successor->PredecessorCount() == 1);
|
||||
int gap_index = successor->first_instruction_index();
|
||||
// Create an unconstrained operand for the same virtual register
|
||||
// and insert a gap move from the fixed output to the operand.
|
||||
UnallocatedOperand output_copy(UnallocatedOperand::ANY, output_vreg);
|
||||
data()->AddGapMove(gap_index, Instruction::START, *output, output_copy);
|
||||
}
|
||||
}
|
||||
|
||||
if (!assigned) {
|
||||
for (auto succ : block->successors()) {
|
||||
const InstructionBlock* successor = code()->InstructionBlockAt(succ);
|
||||
DCHECK(successor->PredecessorCount() == 1);
|
||||
int gap_index = successor->first_instruction_index();
|
||||
range->SpillAtDefinition(allocation_zone(), gap_index, output);
|
||||
range->SetSpillStartIndex(gap_index);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ConstraintBuilder::MeetConstraintsAfter(int instr_index) {
|
||||
auto first = InstructionAt(instr_index);
|
||||
// Handle fixed temporaries.
|
||||
for (size_t i = 0; i < first->TempCount(); i++) {
|
||||
auto temp = UnallocatedOperand::cast(first->TempAt(i));
|
||||
if (temp->HasFixedPolicy()) AllocateFixed(temp, instr_index, false);
|
||||
}
|
||||
// Handle constant/fixed output operands.
|
||||
for (size_t i = 0; i < first->OutputCount(); i++) {
|
||||
InstructionOperand* output = first->OutputAt(i);
|
||||
if (output->IsConstant()) {
|
||||
int output_vreg = ConstantOperand::cast(output)->virtual_register();
|
||||
auto range = LiveRangeFor(output_vreg);
|
||||
range->SetSpillStartIndex(instr_index + 1);
|
||||
range->SetSpillOperand(output);
|
||||
continue;
|
||||
}
|
||||
auto first_output = UnallocatedOperand::cast(output);
|
||||
auto range = LiveRangeFor(first_output->virtual_register());
|
||||
bool assigned = false;
|
||||
if (first_output->HasFixedPolicy()) {
|
||||
int output_vreg = first_output->virtual_register();
|
||||
UnallocatedOperand output_copy(UnallocatedOperand::ANY, output_vreg);
|
||||
bool is_tagged = IsReference(output_vreg);
|
||||
AllocateFixed(first_output, instr_index, is_tagged);
|
||||
|
||||
// This value is produced on the stack, we never need to spill it.
|
||||
if (first_output->IsStackSlot()) {
|
||||
DCHECK(StackSlotOperand::cast(first_output)->index() <
|
||||
data()->frame()->GetSpillSlotCount());
|
||||
range->SetSpillOperand(StackSlotOperand::cast(first_output));
|
||||
range->SetSpillStartIndex(instr_index + 1);
|
||||
assigned = true;
|
||||
}
|
||||
data()->AddGapMove(instr_index + 1, Instruction::START, *first_output,
|
||||
output_copy);
|
||||
}
|
||||
// Make sure we add a gap move for spilling (if we have not done
|
||||
// so already).
|
||||
if (!assigned) {
|
||||
range->SpillAtDefinition(allocation_zone(), instr_index + 1,
|
||||
first_output);
|
||||
range->SetSpillStartIndex(instr_index + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ConstraintBuilder::MeetConstraintsBefore(int instr_index) {
|
||||
auto second = InstructionAt(instr_index);
|
||||
// Handle fixed input operands of second instruction.
|
||||
for (size_t i = 0; i < second->InputCount(); i++) {
|
||||
auto input = second->InputAt(i);
|
||||
if (input->IsImmediate()) continue; // Ignore immediates.
|
||||
auto cur_input = UnallocatedOperand::cast(input);
|
||||
if (cur_input->HasFixedPolicy()) {
|
||||
int input_vreg = cur_input->virtual_register();
|
||||
UnallocatedOperand input_copy(UnallocatedOperand::ANY, input_vreg);
|
||||
bool is_tagged = IsReference(input_vreg);
|
||||
AllocateFixed(cur_input, instr_index, is_tagged);
|
||||
data()->AddGapMove(instr_index, Instruction::END, input_copy, *cur_input);
|
||||
}
|
||||
}
|
||||
// Handle "output same as input" for second instruction.
|
||||
for (size_t i = 0; i < second->OutputCount(); i++) {
|
||||
auto output = second->OutputAt(i);
|
||||
if (!output->IsUnallocated()) continue;
|
||||
auto second_output = UnallocatedOperand::cast(output);
|
||||
if (!second_output->HasSameAsInputPolicy()) continue;
|
||||
DCHECK(i == 0); // Only valid for first output.
|
||||
UnallocatedOperand* cur_input =
|
||||
UnallocatedOperand::cast(second->InputAt(0));
|
||||
int output_vreg = second_output->virtual_register();
|
||||
int input_vreg = cur_input->virtual_register();
|
||||
UnallocatedOperand input_copy(UnallocatedOperand::ANY, input_vreg);
|
||||
cur_input->set_virtual_register(second_output->virtual_register());
|
||||
data()->AddGapMove(instr_index, Instruction::END, input_copy, *cur_input);
|
||||
if (IsReference(input_vreg) && !IsReference(output_vreg)) {
|
||||
if (second->HasReferenceMap()) {
|
||||
second->reference_map()->RecordReference(input_copy);
|
||||
}
|
||||
} else if (!IsReference(input_vreg) && IsReference(output_vreg)) {
|
||||
// The input is assumed to immediately have a tagged representation,
|
||||
// before the pointer map can be used. I.e. the pointer map at the
|
||||
// instruction will include the output operand (whose value at the
|
||||
// beginning of the instruction is equal to the input operand). If
|
||||
// this is not desired, then the pointer map at this instruction needs
|
||||
// to be adjusted manually.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ConstraintBuilder::ResolvePhis() {
|
||||
// Process the blocks in reverse order.
|
||||
for (InstructionBlock* block : base::Reversed(code()->instruction_blocks())) {
|
||||
ResolvePhis(block);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ConstraintBuilder::ResolvePhis(const InstructionBlock* block) {
|
||||
for (auto phi : block->phis()) {
|
||||
int phi_vreg = phi->virtual_register();
|
||||
auto map_value = new (allocation_zone())
|
||||
RegisterAllocationData::PhiMapValue(phi, block, allocation_zone());
|
||||
auto res = data()->phi_map().insert(std::make_pair(phi_vreg, map_value));
|
||||
DCHECK(res.second);
|
||||
USE(res);
|
||||
auto& output = phi->output();
|
||||
for (size_t i = 0; i < phi->operands().size(); ++i) {
|
||||
InstructionBlock* cur_block =
|
||||
code()->InstructionBlockAt(block->predecessors()[i]);
|
||||
UnallocatedOperand input(UnallocatedOperand::ANY, phi->operands()[i]);
|
||||
auto move = data()->AddGapMove(cur_block->last_instruction_index(),
|
||||
Instruction::END, input, output);
|
||||
map_value->incoming_moves.push_back(move);
|
||||
DCHECK(!InstructionAt(cur_block->last_instruction_index())
|
||||
->HasReferenceMap());
|
||||
}
|
||||
auto live_range = LiveRangeFor(phi_vreg);
|
||||
int gap_index = block->first_instruction_index();
|
||||
live_range->SpillAtDefinition(allocation_zone(), gap_index, &output);
|
||||
live_range->SetSpillStartIndex(gap_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());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
LiveRangeBuilder::LiveRangeBuilder(RegisterAllocationData* data)
|
||||
: data_(data) {}
|
||||
|
||||
@ -881,55 +1116,20 @@ void LiveRangeBuilder::AddInitialIntervals(const InstructionBlock* block,
|
||||
}
|
||||
|
||||
|
||||
Instruction* LiveRangeBuilder::GetLastInstruction(
|
||||
const InstructionBlock* block) {
|
||||
return code()->InstructionAt(block->last_instruction_index());
|
||||
}
|
||||
|
||||
|
||||
int LiveRangeBuilder::FixedDoubleLiveRangeID(int index) {
|
||||
return -index - 1 - config()->num_general_registers();
|
||||
}
|
||||
|
||||
|
||||
InstructionOperand* LiveRangeBuilder::AllocateFixed(UnallocatedOperand* operand,
|
||||
int pos, bool is_tagged) {
|
||||
TRACE("Allocating fixed reg for op %d\n", operand->virtual_register());
|
||||
DCHECK(operand->HasFixedPolicy());
|
||||
InstructionOperand allocated;
|
||||
if (operand->HasFixedSlotPolicy()) {
|
||||
allocated = AllocatedOperand(AllocatedOperand::STACK_SLOT,
|
||||
operand->fixed_slot_index());
|
||||
} else if (operand->HasFixedRegisterPolicy()) {
|
||||
allocated = AllocatedOperand(AllocatedOperand::REGISTER,
|
||||
operand->fixed_register_index());
|
||||
} else if (operand->HasFixedDoubleRegisterPolicy()) {
|
||||
allocated = AllocatedOperand(AllocatedOperand::DOUBLE_REGISTER,
|
||||
operand->fixed_register_index());
|
||||
} else {
|
||||
UNREACHABLE();
|
||||
}
|
||||
InstructionOperand::ReplaceWith(operand, &allocated);
|
||||
if (is_tagged) {
|
||||
TRACE("Fixed reg is tagged at %d\n", pos);
|
||||
auto instr = InstructionAt(pos);
|
||||
if (instr->HasReferenceMap()) {
|
||||
instr->reference_map()->RecordReference(*operand);
|
||||
}
|
||||
}
|
||||
return operand;
|
||||
}
|
||||
|
||||
|
||||
LiveRange* LiveRangeBuilder::FixedLiveRangeFor(int index) {
|
||||
DCHECK(index < config()->num_general_registers());
|
||||
auto result = fixed_live_ranges()[index];
|
||||
auto result = data()->fixed_live_ranges()[index];
|
||||
if (result == nullptr) {
|
||||
result = data()->NewLiveRange(FixedLiveRangeID(index));
|
||||
DCHECK(result->IsFixed());
|
||||
result->set_kind(GENERAL_REGISTERS);
|
||||
data()->SetLiveRangeAssignedRegister(result, index);
|
||||
fixed_live_ranges()[index] = result;
|
||||
data()->fixed_live_ranges()[index] = result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@ -937,13 +1137,13 @@ LiveRange* LiveRangeBuilder::FixedLiveRangeFor(int index) {
|
||||
|
||||
LiveRange* LiveRangeBuilder::FixedDoubleLiveRangeFor(int index) {
|
||||
DCHECK(index < config()->num_aliased_double_registers());
|
||||
auto result = fixed_double_live_ranges()[index];
|
||||
auto result = data()->fixed_double_live_ranges()[index];
|
||||
if (result == nullptr) {
|
||||
result = data()->NewLiveRange(FixedDoubleLiveRangeID(index));
|
||||
DCHECK(result->IsFixed());
|
||||
result->set_kind(DOUBLE_REGISTERS);
|
||||
data()->SetLiveRangeAssignedRegister(result, index);
|
||||
fixed_double_live_ranges()[index] = result;
|
||||
data()->fixed_double_live_ranges()[index] = result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@ -1001,158 +1201,6 @@ void LiveRangeBuilder::Use(LifetimePosition block_start,
|
||||
}
|
||||
|
||||
|
||||
void LiveRangeBuilder::MeetRegisterConstraints(const InstructionBlock* block) {
|
||||
int start = block->first_instruction_index();
|
||||
int end = block->last_instruction_index();
|
||||
DCHECK_NE(-1, start);
|
||||
for (int i = start; i <= end; ++i) {
|
||||
MeetConstraintsBefore(i);
|
||||
if (i != end) MeetConstraintsAfter(i);
|
||||
}
|
||||
// Meet register constraints for the instruction in the end.
|
||||
MeetRegisterConstraintsForLastInstructionInBlock(block);
|
||||
}
|
||||
|
||||
|
||||
void LiveRangeBuilder::MeetRegisterConstraintsForLastInstructionInBlock(
|
||||
const InstructionBlock* block) {
|
||||
int end = block->last_instruction_index();
|
||||
auto last_instruction = InstructionAt(end);
|
||||
for (size_t i = 0; i < last_instruction->OutputCount(); i++) {
|
||||
auto output_operand = last_instruction->OutputAt(i);
|
||||
DCHECK(!output_operand->IsConstant());
|
||||
auto output = UnallocatedOperand::cast(output_operand);
|
||||
int output_vreg = output->virtual_register();
|
||||
auto range = LiveRangeFor(output_vreg);
|
||||
bool assigned = false;
|
||||
if (output->HasFixedPolicy()) {
|
||||
AllocateFixed(output, -1, false);
|
||||
// This value is produced on the stack, we never need to spill it.
|
||||
if (output->IsStackSlot()) {
|
||||
DCHECK(StackSlotOperand::cast(output)->index() <
|
||||
data()->frame()->GetSpillSlotCount());
|
||||
range->SetSpillOperand(StackSlotOperand::cast(output));
|
||||
range->SetSpillStartIndex(end);
|
||||
assigned = true;
|
||||
}
|
||||
|
||||
for (auto succ : block->successors()) {
|
||||
const InstructionBlock* successor = code()->InstructionBlockAt(succ);
|
||||
DCHECK(successor->PredecessorCount() == 1);
|
||||
int gap_index = successor->first_instruction_index();
|
||||
// Create an unconstrained operand for the same virtual register
|
||||
// and insert a gap move from the fixed output to the operand.
|
||||
UnallocatedOperand output_copy(UnallocatedOperand::ANY, output_vreg);
|
||||
data()->AddGapMove(gap_index, Instruction::START, *output, output_copy);
|
||||
}
|
||||
}
|
||||
|
||||
if (!assigned) {
|
||||
for (auto succ : block->successors()) {
|
||||
const InstructionBlock* successor = code()->InstructionBlockAt(succ);
|
||||
DCHECK(successor->PredecessorCount() == 1);
|
||||
int gap_index = successor->first_instruction_index();
|
||||
range->SpillAtDefinition(allocation_zone(), gap_index, output);
|
||||
range->SetSpillStartIndex(gap_index);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void LiveRangeBuilder::MeetConstraintsAfter(int instr_index) {
|
||||
auto first = InstructionAt(instr_index);
|
||||
// Handle fixed temporaries.
|
||||
for (size_t i = 0; i < first->TempCount(); i++) {
|
||||
auto temp = UnallocatedOperand::cast(first->TempAt(i));
|
||||
if (temp->HasFixedPolicy()) AllocateFixed(temp, instr_index, false);
|
||||
}
|
||||
// Handle constant/fixed output operands.
|
||||
for (size_t i = 0; i < first->OutputCount(); i++) {
|
||||
InstructionOperand* output = first->OutputAt(i);
|
||||
if (output->IsConstant()) {
|
||||
int output_vreg = ConstantOperand::cast(output)->virtual_register();
|
||||
auto range = LiveRangeFor(output_vreg);
|
||||
range->SetSpillStartIndex(instr_index + 1);
|
||||
range->SetSpillOperand(output);
|
||||
continue;
|
||||
}
|
||||
auto first_output = UnallocatedOperand::cast(output);
|
||||
auto range = LiveRangeFor(first_output->virtual_register());
|
||||
bool assigned = false;
|
||||
if (first_output->HasFixedPolicy()) {
|
||||
int output_vreg = first_output->virtual_register();
|
||||
UnallocatedOperand output_copy(UnallocatedOperand::ANY, output_vreg);
|
||||
bool is_tagged = IsReference(output_vreg);
|
||||
AllocateFixed(first_output, instr_index, is_tagged);
|
||||
|
||||
// This value is produced on the stack, we never need to spill it.
|
||||
if (first_output->IsStackSlot()) {
|
||||
DCHECK(StackSlotOperand::cast(first_output)->index() <
|
||||
data()->frame()->GetSpillSlotCount());
|
||||
range->SetSpillOperand(StackSlotOperand::cast(first_output));
|
||||
range->SetSpillStartIndex(instr_index + 1);
|
||||
assigned = true;
|
||||
}
|
||||
data()->AddGapMove(instr_index + 1, Instruction::START, *first_output,
|
||||
output_copy);
|
||||
}
|
||||
// Make sure we add a gap move for spilling (if we have not done
|
||||
// so already).
|
||||
if (!assigned) {
|
||||
range->SpillAtDefinition(allocation_zone(), instr_index + 1,
|
||||
first_output);
|
||||
range->SetSpillStartIndex(instr_index + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void LiveRangeBuilder::MeetConstraintsBefore(int instr_index) {
|
||||
auto second = InstructionAt(instr_index);
|
||||
// Handle fixed input operands of second instruction.
|
||||
for (size_t i = 0; i < second->InputCount(); i++) {
|
||||
auto input = second->InputAt(i);
|
||||
if (input->IsImmediate()) continue; // Ignore immediates.
|
||||
auto cur_input = UnallocatedOperand::cast(input);
|
||||
if (cur_input->HasFixedPolicy()) {
|
||||
int input_vreg = cur_input->virtual_register();
|
||||
UnallocatedOperand input_copy(UnallocatedOperand::ANY, input_vreg);
|
||||
bool is_tagged = IsReference(input_vreg);
|
||||
AllocateFixed(cur_input, instr_index, is_tagged);
|
||||
data()->AddGapMove(instr_index, Instruction::END, input_copy, *cur_input);
|
||||
}
|
||||
}
|
||||
// Handle "output same as input" for second instruction.
|
||||
for (size_t i = 0; i < second->OutputCount(); i++) {
|
||||
auto output = second->OutputAt(i);
|
||||
if (!output->IsUnallocated()) continue;
|
||||
auto second_output = UnallocatedOperand::cast(output);
|
||||
if (!second_output->HasSameAsInputPolicy()) continue;
|
||||
DCHECK(i == 0); // Only valid for first output.
|
||||
UnallocatedOperand* cur_input =
|
||||
UnallocatedOperand::cast(second->InputAt(0));
|
||||
int output_vreg = second_output->virtual_register();
|
||||
int input_vreg = cur_input->virtual_register();
|
||||
UnallocatedOperand input_copy(UnallocatedOperand::ANY, input_vreg);
|
||||
cur_input->set_virtual_register(second_output->virtual_register());
|
||||
data()->AddGapMove(instr_index, Instruction::END, input_copy, *cur_input);
|
||||
if (IsReference(input_vreg) && !IsReference(output_vreg)) {
|
||||
if (second->HasReferenceMap()) {
|
||||
second->reference_map()->RecordReference(input_copy);
|
||||
}
|
||||
} else if (!IsReference(input_vreg) && IsReference(output_vreg)) {
|
||||
// The input is assumed to immediately have a tagged representation,
|
||||
// before the pointer map can be used. I.e. the pointer map at the
|
||||
// instruction will include the output operand (whose value at the
|
||||
// beginning of the instruction is equal to the input operand). If
|
||||
// this is not desired, then the pointer map at this instruction needs
|
||||
// to be adjusted manually.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool LiveRangeBuilder::IsOutputRegisterOf(Instruction* instr, int index) {
|
||||
for (size_t i = 0; i < instr->OutputCount(); i++) {
|
||||
auto output = instr->OutputAt(i);
|
||||
@ -1184,7 +1232,7 @@ void LiveRangeBuilder::ProcessInstructions(const InstructionBlock* block,
|
||||
index--) {
|
||||
auto curr_position =
|
||||
LifetimePosition::InstructionFromInstructionIndex(index);
|
||||
auto instr = InstructionAt(index);
|
||||
auto instr = code()->InstructionAt(index);
|
||||
DCHECK(instr != nullptr);
|
||||
DCHECK(curr_position.IsInstructionPosition());
|
||||
// Process output, inputs, and temps of this instruction.
|
||||
@ -1308,51 +1356,6 @@ void LiveRangeBuilder::ProcessInstructions(const InstructionBlock* block,
|
||||
}
|
||||
|
||||
|
||||
void LiveRangeBuilder::ResolvePhis(const InstructionBlock* block) {
|
||||
for (auto phi : block->phis()) {
|
||||
int phi_vreg = phi->virtual_register();
|
||||
auto map_value = new (allocation_zone())
|
||||
RegisterAllocationData::PhiMapValue(phi, block, allocation_zone());
|
||||
auto res = phi_map().insert(std::make_pair(phi_vreg, map_value));
|
||||
DCHECK(res.second);
|
||||
USE(res);
|
||||
auto& output = phi->output();
|
||||
for (size_t i = 0; i < phi->operands().size(); ++i) {
|
||||
InstructionBlock* cur_block =
|
||||
code()->InstructionBlockAt(block->predecessors()[i]);
|
||||
UnallocatedOperand input(UnallocatedOperand::ANY, phi->operands()[i]);
|
||||
auto move = data()->AddGapMove(cur_block->last_instruction_index(),
|
||||
Instruction::END, input, output);
|
||||
map_value->incoming_moves.push_back(move);
|
||||
DCHECK(!InstructionAt(cur_block->last_instruction_index())
|
||||
->HasReferenceMap());
|
||||
}
|
||||
auto live_range = LiveRangeFor(phi_vreg);
|
||||
int gap_index = block->first_instruction_index();
|
||||
live_range->SpillAtDefinition(allocation_zone(), gap_index, &output);
|
||||
live_range->SetSpillStartIndex(gap_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());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void LiveRangeBuilder::MeetRegisterConstraints() {
|
||||
for (auto block : code()->instruction_blocks()) {
|
||||
MeetRegisterConstraints(block);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void LiveRangeBuilder::ResolvePhis() {
|
||||
// Process the blocks in reverse order.
|
||||
for (InstructionBlock* block : base::Reversed(code()->instruction_blocks())) {
|
||||
ResolvePhis(block);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void LiveRangeBuilder::BuildLiveRanges() {
|
||||
// Process the blocks in reverse order.
|
||||
for (int block_id = code()->InstructionBlockCount() - 1; block_id >= 0;
|
||||
@ -1374,7 +1377,7 @@ void LiveRangeBuilder::BuildLiveRanges() {
|
||||
live->Remove(phi_vreg);
|
||||
InstructionOperand* hint = nullptr;
|
||||
auto instr = GetLastInstruction(
|
||||
code()->InstructionBlockAt(block->predecessors()[0]));
|
||||
code(), code()->InstructionBlockAt(block->predecessors()[0]));
|
||||
for (auto move : *instr->GetParallelMove(Instruction::END)) {
|
||||
auto& to = move->destination();
|
||||
if (to.IsUnallocated() &&
|
||||
@ -1415,7 +1418,7 @@ void LiveRangeBuilder::BuildLiveRanges() {
|
||||
}
|
||||
}
|
||||
|
||||
for (auto range : live_ranges()) {
|
||||
for (auto range : data()->live_ranges()) {
|
||||
if (range == nullptr) continue;
|
||||
range->set_kind(RequiredRegisterKind(range->id()));
|
||||
// Give slots to all ranges with a non fixed slot use.
|
||||
@ -1474,14 +1477,16 @@ LinearScanAllocator::LinearScanAllocator(RegisterAllocationData* data,
|
||||
// TryAllocateFreeReg and AllocateBlockedReg assume this
|
||||
// when allocating local arrays.
|
||||
DCHECK(RegisterConfiguration::kMaxDoubleRegisters >=
|
||||
config()->num_general_registers());
|
||||
this->data()->config()->num_general_registers());
|
||||
}
|
||||
|
||||
|
||||
void LinearScanAllocator::AllocateRegisters() {
|
||||
DCHECK(unhandled_live_ranges().empty());
|
||||
DCHECK(active_live_ranges().empty());
|
||||
DCHECK(inactive_live_ranges().empty());
|
||||
|
||||
for (auto range : live_ranges()) {
|
||||
for (auto range : data()->live_ranges()) {
|
||||
if (range == nullptr) continue;
|
||||
if (range->Kind() == mode_) {
|
||||
AddToUnhandledUnsorted(range);
|
||||
@ -1490,9 +1495,6 @@ void LinearScanAllocator::AllocateRegisters() {
|
||||
SortUnhandled();
|
||||
DCHECK(UnhandledIsSorted());
|
||||
|
||||
DCHECK(active_live_ranges().empty());
|
||||
DCHECK(inactive_live_ranges().empty());
|
||||
|
||||
auto& fixed_ranges = GetFixedRegisters(data(), mode_);
|
||||
for (auto current : fixed_ranges) {
|
||||
if (current != nullptr) {
|
||||
@ -1565,17 +1567,14 @@ void LinearScanAllocator::AllocateRegisters() {
|
||||
AddToActive(current);
|
||||
}
|
||||
}
|
||||
|
||||
active_live_ranges().clear();
|
||||
inactive_live_ranges().clear();
|
||||
}
|
||||
|
||||
|
||||
const char* LinearScanAllocator::RegisterName(int allocation_index) const {
|
||||
if (mode_ == GENERAL_REGISTERS) {
|
||||
return config()->general_register_name(allocation_index);
|
||||
return data()->config()->general_register_name(allocation_index);
|
||||
} else {
|
||||
return config()->double_register_name(allocation_index);
|
||||
return data()->config()->double_register_name(allocation_index);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1910,8 +1909,8 @@ bool LinearScanAllocator::TryReuseSpillForPhi(LiveRange* range) {
|
||||
if (range->IsChild() || !range->is_phi()) return false;
|
||||
DCHECK(!range->HasSpillOperand());
|
||||
|
||||
auto lookup = phi_map().find(range->id());
|
||||
DCHECK(lookup != phi_map().end());
|
||||
auto lookup = data()->phi_map().find(range->id());
|
||||
DCHECK(lookup != data()->phi_map().end());
|
||||
auto phi = lookup->second->phi;
|
||||
auto block = lookup->second->block;
|
||||
// Count the number of spilled operands.
|
||||
@ -2190,7 +2189,7 @@ void ReferenceMapPopulator::PopulateReferenceMaps() {
|
||||
// Iterate over the first parts of multi-part live ranges.
|
||||
if (range->IsChild()) continue;
|
||||
// Skip non-reference values.
|
||||
if (!IsReference(range->id())) continue;
|
||||
if (!data()->IsReference(range->id())) continue;
|
||||
// Skip empty live ranges.
|
||||
if (range->IsEmpty()) continue;
|
||||
|
||||
@ -2444,7 +2443,7 @@ void LiveRangeConnector::ResolveControlFlow(const InstructionBlock* block,
|
||||
position = Instruction::START;
|
||||
} else {
|
||||
DCHECK(pred->SuccessorCount() == 1);
|
||||
DCHECK(!data()
|
||||
DCHECK(!code()
|
||||
->InstructionAt(pred->last_instruction_index())
|
||||
->HasReferenceMap());
|
||||
gap_index = pred->last_instruction_index();
|
||||
|
@ -444,10 +444,8 @@ class RegisterAllocationData final : public ZoneObject {
|
||||
const ZoneVector<LiveRange*>& fixed_double_live_ranges() const {
|
||||
return fixed_double_live_ranges_;
|
||||
}
|
||||
const ZoneVector<BitVector*>& live_in_sets() const { return live_in_sets_; }
|
||||
ZoneVector<BitVector*>& live_in_sets() { return live_in_sets_; }
|
||||
ZoneVector<SpillRange*>& spill_ranges() { return spill_ranges_; }
|
||||
const ZoneVector<SpillRange*>& spill_ranges() const { return spill_ranges_; }
|
||||
InstructionSequence* code() const { return code_; }
|
||||
// This zone is for datastructures only needed during register allocation
|
||||
// phases.
|
||||
@ -463,9 +461,6 @@ class RegisterAllocationData final : public ZoneObject {
|
||||
|
||||
void SetLiveRangeAssignedRegister(LiveRange* range, int reg);
|
||||
LiveRange* LiveRangeFor(int index);
|
||||
Instruction* InstructionAt(int index) const {
|
||||
return code()->InstructionAt(index);
|
||||
}
|
||||
|
||||
void AssignPhiInput(LiveRange* range, const InstructionOperand& assignment);
|
||||
SpillRange* AssignSpillRangeToLiveRange(LiveRange* range);
|
||||
@ -502,9 +497,9 @@ class RegisterAllocationData final : public ZoneObject {
|
||||
};
|
||||
|
||||
|
||||
class LiveRangeBuilder final : public ZoneObject {
|
||||
class ConstraintBuilder final : public ZoneObject {
|
||||
public:
|
||||
explicit LiveRangeBuilder(RegisterAllocationData* data);
|
||||
explicit ConstraintBuilder(RegisterAllocationData* data);
|
||||
|
||||
// Phase 1 : insert moves to account for fixed register operands.
|
||||
void MeetRegisterConstraints();
|
||||
@ -513,6 +508,36 @@ class LiveRangeBuilder final : public ZoneObject {
|
||||
// of blocks containing phis.
|
||||
void ResolvePhis();
|
||||
|
||||
private:
|
||||
RegisterAllocationData* data() const { return data_; }
|
||||
InstructionSequence* code() const { return data()->code(); }
|
||||
Zone* allocation_zone() const { return data()->allocation_zone(); }
|
||||
|
||||
Instruction* InstructionAt(int index) { return code()->InstructionAt(index); }
|
||||
bool IsReference(int virtual_register) const {
|
||||
return data()->IsReference(virtual_register);
|
||||
}
|
||||
LiveRange* LiveRangeFor(int index) { return data()->LiveRangeFor(index); }
|
||||
|
||||
InstructionOperand* AllocateFixed(UnallocatedOperand* operand, int pos,
|
||||
bool is_tagged);
|
||||
void MeetRegisterConstraints(const InstructionBlock* block);
|
||||
void MeetConstraintsBefore(int index);
|
||||
void MeetConstraintsAfter(int index);
|
||||
void MeetRegisterConstraintsForLastInstructionInBlock(
|
||||
const InstructionBlock* block);
|
||||
void ResolvePhis(const InstructionBlock* block);
|
||||
|
||||
RegisterAllocationData* const data_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(ConstraintBuilder);
|
||||
};
|
||||
|
||||
|
||||
class LiveRangeBuilder final : public ZoneObject {
|
||||
public:
|
||||
explicit LiveRangeBuilder(RegisterAllocationData* data);
|
||||
|
||||
// Phase 3: compute liveness of all virtual register.
|
||||
void BuildLiveRanges();
|
||||
|
||||
@ -525,20 +550,8 @@ class LiveRangeBuilder final : public ZoneObject {
|
||||
ZoneVector<BitVector*>& live_in_sets() const {
|
||||
return data()->live_in_sets();
|
||||
}
|
||||
ZoneVector<LiveRange*>& live_ranges() { return data()->live_ranges(); }
|
||||
ZoneVector<LiveRange*>& fixed_live_ranges() {
|
||||
return data()->fixed_live_ranges();
|
||||
}
|
||||
ZoneVector<LiveRange*>& fixed_double_live_ranges() {
|
||||
return data()->fixed_double_live_ranges();
|
||||
}
|
||||
ZoneVector<SpillRange*>& spill_ranges() { return data()->spill_ranges(); }
|
||||
RegisterAllocationData::PhiMap& phi_map() { return data()->phi_map(); }
|
||||
|
||||
Instruction* InstructionAt(int index) { return code()->InstructionAt(index); }
|
||||
bool IsReference(int virtual_register) const {
|
||||
return data()->IsReference(virtual_register);
|
||||
}
|
||||
LiveRange* LiveRangeFor(int index) { return data()->LiveRangeFor(index); }
|
||||
|
||||
void Verify() const;
|
||||
|
||||
@ -548,26 +561,16 @@ class LiveRangeBuilder final : public ZoneObject {
|
||||
bool IsOutputRegisterOf(Instruction* instr, int index);
|
||||
bool IsOutputDoubleRegisterOf(Instruction* instr, int index);
|
||||
void ProcessInstructions(const InstructionBlock* block, BitVector* live);
|
||||
void MeetRegisterConstraints(const InstructionBlock* block);
|
||||
void MeetConstraintsBefore(int index);
|
||||
void MeetConstraintsAfter(int index);
|
||||
void MeetRegisterConstraintsForLastInstructionInBlock(
|
||||
const InstructionBlock* block);
|
||||
void ResolvePhis(const InstructionBlock* block);
|
||||
|
||||
LiveRange* LiveRangeFor(int index) { return data()->LiveRangeFor(index); }
|
||||
static int FixedLiveRangeID(int index) { return -index - 1; }
|
||||
int FixedDoubleLiveRangeID(int index);
|
||||
LiveRange* FixedLiveRangeFor(int index);
|
||||
LiveRange* FixedDoubleLiveRangeFor(int index);
|
||||
Instruction* GetLastInstruction(const InstructionBlock* block);
|
||||
|
||||
// Returns the register kind required by the given virtual register.
|
||||
RegisterKind RequiredRegisterKind(int virtual_register) const;
|
||||
|
||||
// Helper methods for building intervals.
|
||||
InstructionOperand* AllocateFixed(UnallocatedOperand* operand, int pos,
|
||||
bool is_tagged);
|
||||
LiveRange* LiveRangeFor(InstructionOperand* operand);
|
||||
void Define(LifetimePosition position, InstructionOperand* operand,
|
||||
InstructionOperand* hint);
|
||||
@ -592,12 +595,9 @@ class LinearScanAllocator final : public ZoneObject {
|
||||
RegisterAllocationData* data() const { return data_; }
|
||||
InstructionSequence* code() const { return data()->code(); }
|
||||
Zone* allocation_zone() const { return data()->allocation_zone(); }
|
||||
Zone* code_zone() const { return code()->zone(); }
|
||||
const RegisterConfiguration* config() const { return data()->config(); }
|
||||
int num_registers() const { return num_registers_; }
|
||||
const char* RegisterName(int allocation_index) const;
|
||||
|
||||
ZoneVector<LiveRange*>& live_ranges() { return data()->live_ranges(); }
|
||||
ZoneVector<LiveRange*>& unhandled_live_ranges() {
|
||||
return unhandled_live_ranges_;
|
||||
}
|
||||
@ -605,13 +605,7 @@ class LinearScanAllocator final : public ZoneObject {
|
||||
ZoneVector<LiveRange*>& inactive_live_ranges() {
|
||||
return inactive_live_ranges_;
|
||||
}
|
||||
ZoneVector<SpillRange*>& spill_ranges() { return data()->spill_ranges(); }
|
||||
RegisterAllocationData::PhiMap& phi_map() { return data()->phi_map(); }
|
||||
|
||||
Instruction* InstructionAt(int index) { return code()->InstructionAt(index); }
|
||||
bool IsReference(int virtual_register) const {
|
||||
return data()->IsReference(virtual_register);
|
||||
}
|
||||
LiveRange* LiveRangeFor(int index) { return data()->LiveRangeFor(index); }
|
||||
|
||||
// Helper methods for updating the life range lists.
|
||||
@ -715,14 +709,10 @@ class ReferenceMapPopulator final : public ZoneObject {
|
||||
void PopulateReferenceMaps();
|
||||
|
||||
private:
|
||||
bool SafePointsAreInOrder() const;
|
||||
|
||||
bool IsReference(int virtual_register) const {
|
||||
return data()->IsReference(virtual_register);
|
||||
}
|
||||
|
||||
RegisterAllocationData* data() const { return data_; }
|
||||
|
||||
bool SafePointsAreInOrder() const;
|
||||
|
||||
RegisterAllocationData* const data_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(ReferenceMapPopulator);
|
||||
@ -740,14 +730,15 @@ class LiveRangeConnector final : public ZoneObject {
|
||||
void ResolveControlFlow(Zone* local_zone);
|
||||
|
||||
private:
|
||||
RegisterAllocationData* data() const { return data_; }
|
||||
InstructionSequence* code() const { return data()->code(); }
|
||||
Zone* code_zone() const { return code()->zone(); }
|
||||
|
||||
bool CanEagerlyResolveControlFlow(const InstructionBlock* block) const;
|
||||
void ResolveControlFlow(const InstructionBlock* block,
|
||||
const InstructionOperand& cur_op,
|
||||
const InstructionBlock* pred,
|
||||
const InstructionOperand& pred_op);
|
||||
InstructionSequence* code() const { return data()->code(); }
|
||||
Zone* code_zone() const { return code()->zone(); }
|
||||
RegisterAllocationData* data() const { return data_; }
|
||||
|
||||
RegisterAllocationData* const data_;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user