Index: runtime/vm/flow_graph_allocator.cc |
diff --git a/runtime/vm/flow_graph_allocator.cc b/runtime/vm/flow_graph_allocator.cc |
index 609d1d68b515ea7ca36d654776086f2ed589ac7b..099a0193d280b9428cf7ebabf8382ffb41d66c0f 100644 |
--- a/runtime/vm/flow_graph_allocator.cc |
+++ b/runtime/vm/flow_graph_allocator.cc |
@@ -62,6 +62,7 @@ static intptr_t ToInstructionEnd(intptr_t pos) { |
FlowGraphAllocator::FlowGraphAllocator(const FlowGraph& flow_graph) |
: flow_graph_(flow_graph), |
+ reaching_defs_(flow_graph), |
mint_values_(NULL), |
block_order_(flow_graph.reverse_postorder()), |
postorder_(flow_graph.postorder()), |
@@ -146,9 +147,15 @@ void FlowGraphAllocator::ComputeInitialSets() { |
} |
// Handle uses. |
+ LocationSummary* locs = current->locs(); |
+ ASSERT(locs->input_count() == current->InputCount()); |
for (intptr_t j = 0; j < current->InputCount(); j++) { |
Value* input = current->InputAt(j); |
const intptr_t use = input->definition()->ssa_temp_index(); |
+ |
+ ASSERT(!locs->in(j).IsConstant() || input->BindsToConstant()); |
+ if (locs->in(j).IsConstant()) continue; |
+ |
live_in->Add(use); |
} |
@@ -182,6 +189,8 @@ void FlowGraphAllocator::ComputeInitialSets() { |
// live-in for a predecessor. |
for (intptr_t k = 0; k < phi->InputCount(); k++) { |
Value* val = phi->InputAt(k); |
+ if (val->BindsToConstant()) continue; |
+ |
BlockEntryInstr* pred = block->PredecessorAt(k); |
const intptr_t use = val->definition()->ssa_temp_index(); |
if (!kill_[pred->postorder_number()]->Contains(use)) { |
@@ -517,9 +526,12 @@ static bool HasOnlyUnconstrainedUsesInLoop(LiveRange* range, |
void FlowGraphAllocator::BuildLiveRanges() { |
const intptr_t block_count = postorder_.length(); |
ASSERT(postorder_.Last()->IsGraphEntry()); |
+ BitVector* current_interference_set = NULL; |
for (intptr_t i = 0; i < (block_count - 1); i++) { |
BlockEntryInstr* block = postorder_[i]; |
+ BlockInfo* block_info = BlockInfoAt(block->start_pos()); |
+ |
// For every SSA value that is live out of this block, create an interval |
// that covers the whole block. It will be shortened if we encounter a |
// definition of this value in this block. |
@@ -528,23 +540,33 @@ void FlowGraphAllocator::BuildLiveRanges() { |
range->AddUseInterval(block->start_pos(), block->end_pos()); |
} |
+ BlockInfo* loop_header = block_info->loop_header(); |
+ if ((loop_header != NULL) && (loop_header->last_block() == block)) { |
+ current_interference_set = |
+ new BitVector(flow_graph_.max_virtual_register_number()); |
+ ASSERT(loop_header->backedge_interference() == NULL); |
+ loop_header->set_backedge_interference( |
+ current_interference_set); |
+ } |
+ |
// Connect outgoing phi-moves that were created in NumberInstructions |
// and find last instruction that contributes to liveness. |
- Instruction* current = ConnectOutgoingPhiMoves(block); |
+ Instruction* current = ConnectOutgoingPhiMoves(block, |
+ current_interference_set); |
// Now process all instructions in reverse order. |
while (current != block) { |
// Skip parallel moves that we insert while processing instructions. |
if (!current->IsParallelMove()) { |
- ProcessOneInstruction(block, current); |
+ ProcessOneInstruction(block, current, current_interference_set); |
} |
current = current->previous(); |
} |
// Check if any values live into the loop can be spilled for free. |
- BlockInfo* block_info = BlockInfoAt(block->start_pos()); |
if (block_info->is_loop_header()) { |
+ current_interference_set = NULL; |
for (BitVector::Iterator it(live_in_[i]); !it.Done(); it.Advance()) { |
LiveRange* range = GetLiveRange(it.Current()); |
if (HasOnlyUnconstrainedUsesInLoop(range, block_info)) { |
@@ -650,7 +672,7 @@ static Location::Kind RegisterKindForResult(Instruction* instr) { |
// |
Instruction* FlowGraphAllocator::ConnectOutgoingPhiMoves( |
- BlockEntryInstr* block) { |
+ BlockEntryInstr* block, BitVector* interfere_at_backedge) { |
Instruction* last = block->last_instruction(); |
GotoInstr* goto_instr = last->AsGoto(); |
@@ -696,7 +718,9 @@ Instruction* FlowGraphAllocator::ConnectOutgoingPhiMoves( |
// value --* |
// |
- LiveRange* range = GetLiveRange(val->definition()->ssa_temp_index()); |
+ const intptr_t vreg = val->definition()->ssa_temp_index(); |
+ LiveRange* range = GetLiveRange(vreg); |
+ if (interfere_at_backedge != NULL) interfere_at_backedge->Add(vreg); |
range->AddUseInterval(block->start_pos(), pos); |
range->AddHintedUse(pos, move->src_slot(), move->dest_slot()); |
@@ -821,7 +845,8 @@ void FlowGraphAllocator::ProcessEnvironmentUses(BlockEntryInstr* block, |
// Create and update live ranges corresponding to instruction's inputs, |
// temporaries and output. |
void FlowGraphAllocator::ProcessOneInstruction(BlockEntryInstr* block, |
- Instruction* current) { |
+ Instruction* current, |
+ BitVector* interference_set) { |
LocationSummary* locs = current->locs(); |
Definition* def = current->AsDefinition(); |
@@ -1065,6 +1090,12 @@ void FlowGraphAllocator::ProcessOneInstruction(BlockEntryInstr* block, |
range->AddHintedUse(pos, out, move->src_slot()); |
range->AddUse(pos, move->dest_slot()); |
range->AddUse(pos, locs->in_slot(0)); |
+ |
+ if ((interference_set != NULL) && |
+ (range->vreg() >= 0) && |
+ interference_set->Contains(range->vreg())) { |
+ interference_set->Add(input->ssa_temp_index()); |
+ } |
} else { |
// Normal unallocated location that requires a register. Expected shape of |
// live range: |
@@ -1451,7 +1482,7 @@ LiveRange* LiveRange::SplitAt(intptr_t split_pos) { |
LiveRange* FlowGraphAllocator::SplitBetween(LiveRange* range, |
intptr_t from, |
intptr_t to) { |
- TRACE_ALLOC(OS::Print("split %"Pd" [%"Pd", %"Pd") between [%"Pd", %"Pd")\n", |
+ TRACE_ALLOC(OS::Print("split v%"Pd" [%"Pd", %"Pd") between [%"Pd", %"Pd")\n", |
range->vreg(), range->Start(), range->End(), from, to)); |
intptr_t split_pos = kIllegalPosition; |
@@ -1489,7 +1520,7 @@ void FlowGraphAllocator::SpillBetween(LiveRange* range, |
intptr_t from, |
intptr_t to) { |
ASSERT(from < to); |
- TRACE_ALLOC(OS::Print("spill %"Pd" [%"Pd", %"Pd") " |
+ TRACE_ALLOC(OS::Print("spill v%"Pd" [%"Pd", %"Pd") " |
"between [%"Pd", %"Pd")\n", |
range->vreg(), range->Start(), range->End(), from, to)); |
LiveRange* tail = range->SplitAt(from); |
@@ -1507,7 +1538,7 @@ void FlowGraphAllocator::SpillBetween(LiveRange* range, |
void FlowGraphAllocator::SpillAfter(LiveRange* range, intptr_t from) { |
- TRACE_ALLOC(OS::Print("spill %"Pd" [%"Pd", %"Pd") after %"Pd"\n", |
+ TRACE_ALLOC(OS::Print("spill v%"Pd" [%"Pd", %"Pd") after %"Pd"\n", |
range->vreg(), range->Start(), range->End(), from)); |
// When spilling the value inside the loop check if this spill can |
@@ -1614,6 +1645,72 @@ intptr_t FlowGraphAllocator::FirstIntersectionWithAllocated( |
} |
+void ReachingDefs::AddPhi(PhiInstr* phi) { |
+ if (phi->reaching_defs() == NULL) { |
+ phi->set_reaching_defs( |
+ new BitVector(flow_graph_.max_virtual_register_number())); |
+ |
+ // Compute initial set reaching defs set. |
+ bool depends_on_phi = false; |
+ for (intptr_t i = 0; i < phi->InputCount(); i++) { |
+ Definition* input = phi->InputAt(i)->definition(); |
+ if (input->IsPhi()) { |
+ depends_on_phi = true; |
+ } |
+ phi->reaching_defs()->Add(input->ssa_temp_index()); |
+ } |
+ |
+ // If this phi depends on another phi then we need fix point iteration. |
+ if (depends_on_phi) phis_.Add(phi); |
+ } |
+} |
+ |
+ |
+void ReachingDefs::Compute() { |
+ // Transitively collect all phis that are used by the given phi. |
+ for (intptr_t i = 0; i < phis_.length(); i++) { |
+ PhiInstr* phi = phis_[i]; |
+ |
+ // Add all phis that affect this phi to the list. |
+ for (intptr_t i = 0; i < phi->InputCount(); i++) { |
+ Definition* input = phi->InputAt(i)->definition(); |
+ if (input->IsPhi()) { |
+ AddPhi(input->AsPhi()); |
+ } |
+ } |
+ } |
+ |
+ // Propagate values until fix point is reached. |
+ bool changed; |
+ do { |
+ changed = false; |
+ for (intptr_t i = 0; i < phis_.length(); i++) { |
+ PhiInstr* phi = phis_[i]; |
+ for (intptr_t i = 0; i < phi->InputCount(); i++) { |
+ Definition* input = phi->InputAt(i)->definition(); |
Florian Schneider
2012/11/23 13:58:06
Or shorter:
input_phi = phi->InputAt(i)->definiti
Vyacheslav Egorov (Google)
2012/11/23 15:38:53
Done.
|
+ if (input->IsPhi()) { |
+ if (phi->reaching_defs()->AddAll(input->AsPhi()->reaching_defs())) { |
+ changed = true; |
+ } |
+ } |
+ } |
+ } |
+ } while (changed); |
+ |
+ phis_.Clear(); |
+} |
+ |
+ |
+BitVector* ReachingDefs::Get(PhiInstr* phi) { |
+ if (phi->reaching_defs() == NULL) { |
+ ASSERT(phis_.is_empty()); |
+ AddPhi(phi); |
+ Compute(); |
+ } |
+ return phi->reaching_defs(); |
+} |
+ |
+ |
bool FlowGraphAllocator::AllocateFreeRegister(LiveRange* unallocated) { |
intptr_t candidate = kNoRegister; |
intptr_t free_until = 0; |
@@ -1628,10 +1725,10 @@ bool FlowGraphAllocator::AllocateFreeRegister(LiveRange* unallocated) { |
candidate = hint.register_code(); |
} |
- TRACE_ALLOC(OS::Print("found hint ")); |
- TRACE_ALLOC(hint.Print()); |
- TRACE_ALLOC(OS::Print(" for %"Pd": free until %"Pd"\n", |
- unallocated->vreg(), free_until)); |
+ TRACE_ALLOC(OS::Print("found hint %s for v%"Pd": free until %"Pd"\n", |
+ hint.Name(), |
+ unallocated->vreg(), |
+ free_until)); |
} else if (free_until != kMaxPosition) { |
for (intptr_t reg = 0; reg < NumberOfRegisters(); ++reg) { |
if (!blocked_registers_[reg] && (registers_[reg].length() == 0)) { |
@@ -1642,6 +1739,67 @@ bool FlowGraphAllocator::AllocateFreeRegister(LiveRange* unallocated) { |
} |
} |
+ // We have a very good candidate (either hinted to us or completely free). |
+ // If we are in the loop try to reduce number of moves on the back edge by |
Florian Schneider
2012/11/23 13:58:06
Which loop? s/the/a/?
Vyacheslav Egorov (Google)
2012/11/23 15:38:53
Done.
|
+ // searching for a candidate that does not interfere with phis on the back |
+ // edge. |
+ BlockInfo* loop_header = BlockInfoAt(unallocated->Start())->loop_header(); |
+ if ((unallocated->vreg() >= 0) && |
+ (loop_header != NULL) && |
+ (free_until >= loop_header->last_block()->end_pos()) && |
+ loop_header->backedge_interference()->Contains(unallocated->vreg())) { |
+ ASSERT(static_cast<intptr_t>(kNumberOfXmmRegisters) <= |
Florian Schneider
2012/11/23 13:58:06
COMPILE_ASSERT?
Vyacheslav Egorov (Google)
2012/11/23 15:38:53
COMPILE_ASSERT seems to be available only in debug
|
+ kNumberOfCpuRegisters); |
+ bool used_on_backedge[kNumberOfCpuRegisters] = { false }; |
+ |
+ for (PhiIterator it(loop_header->entry()->AsJoinEntry()); |
+ !it.Done(); |
+ it.Advance()) { |
+ PhiInstr* phi = it.Current(); |
+ if (phi->is_alive()) { |
+ const intptr_t phi_vreg = phi->ssa_temp_index(); |
+ LiveRange* range = GetLiveRange(phi_vreg); |
+ if (range->assigned_location().kind() == register_kind_) { |
+ const intptr_t reg = range->assigned_location().register_code(); |
+ |
+ if ((unallocated->vreg() >= 0) && |
Florian Schneider
2012/11/23 13:58:06
This condition is already tested in line 1747.
Vyacheslav Egorov (Google)
2012/11/23 15:38:53
Done.
|
+ !reaching_defs_.Get(phi)->Contains(unallocated->vreg())) { |
+ used_on_backedge[reg] = true; |
+ } |
+ } |
+ } |
+ } |
+ |
+ if (used_on_backedge[candidate]) { |
+ TRACE_ALLOC(OS::Print( |
+ "considering %s for v%"Pd": has interference on the back edge" |
+ " {loop [%"Pd", %"Pd")}\n", |
+ MakeRegisterLocation(candidate, Location::kDouble).Name(), |
+ unallocated->vreg(), |
+ loop_header->entry()->start_pos(), |
+ loop_header->last_block()->end_pos())); |
+ for (intptr_t reg = 0; reg < NumberOfRegisters(); ++reg) { |
+ if (blocked_registers_[reg] || |
+ (reg == candidate) || |
+ used_on_backedge[reg]) { |
+ continue; |
+ } |
+ |
+ const intptr_t intersection = |
+ FirstIntersectionWithAllocated(reg, unallocated); |
+ if (intersection >= free_until) { |
+ candidate = reg; |
+ free_until = intersection; |
+ TRACE_ALLOC(OS::Print( |
+ "found %s for v%"Pd" with no interference on the back edge\n", |
+ MakeRegisterLocation(candidate, Location::kDouble).Name(), |
+ candidate)); |
+ break; |
+ } |
+ } |
+ } |
+ } |
+ |
ASSERT(0 <= kMaxPosition); |
if (free_until != kMaxPosition) { |
for (intptr_t reg = 0; reg < NumberOfRegisters(); ++reg) { |
@@ -1661,7 +1819,7 @@ bool FlowGraphAllocator::AllocateFreeRegister(LiveRange* unallocated) { |
TRACE_ALLOC(OS::Print("assigning free register ")); |
TRACE_ALLOC(MakeRegisterLocation(candidate, Location::kDouble).Print()); |
- TRACE_ALLOC(OS::Print(" to %"Pd"\n", unallocated->vreg())); |
+ TRACE_ALLOC(OS::Print(" to v%"Pd"\n", unallocated->vreg())); |
if (free_until != kMaxPosition) { |
// There was an intersection. Split unallocated. |
@@ -1764,7 +1922,7 @@ void FlowGraphAllocator::AllocateAnyRegister(LiveRange* unallocated) { |
TRACE_ALLOC(OS::Print("assigning blocked register ")); |
TRACE_ALLOC(MakeRegisterLocation(candidate, Location::kDouble).Print()); |
- TRACE_ALLOC(OS::Print(" to live range %"Pd" until %"Pd"\n", |
+ TRACE_ALLOC(OS::Print(" to live range v%"Pd" until %"Pd"\n", |
unallocated->vreg(), blocked_at)); |
if (blocked_at < unallocated->End()) { |
@@ -2070,6 +2228,7 @@ void FlowGraphAllocator::PrepareForAllocation( |
const GrowableArray<LiveRange*>& unallocated, |
LiveRange** blocking_ranges, |
bool* blocked_registers) { |
+ ASSERT(number_of_registers <= kNumberOfCpuRegisters); |
register_kind_ = register_kind; |
number_of_registers_ = number_of_registers; |
@@ -2097,7 +2256,7 @@ void FlowGraphAllocator::AllocateUnallocatedRanges() { |
while (!unallocated_.is_empty()) { |
LiveRange* range = unallocated_.RemoveLast(); |
const intptr_t start = range->Start(); |
- TRACE_ALLOC(OS::Print("Processing live range for vreg %"Pd" " |
+ TRACE_ALLOC(OS::Print("Processing live range for v%"Pd" " |
"starting at %"Pd"\n", |
range->vreg(), |
start)); |
@@ -2134,12 +2293,13 @@ bool FlowGraphAllocator::TargetLocationIsSpillSlot(LiveRange* range, |
void FlowGraphAllocator::ConnectSplitSiblings(LiveRange* parent, |
BlockEntryInstr* source_block, |
BlockEntryInstr* target_block) { |
- TRACE_ALLOC(OS::Print("Connect source_block=%"Pd", target_block=%"Pd"\n", |
+ TRACE_ALLOC(OS::Print("Connect v%"Pd" on the edge B%"Pd" -> B%"Pd"\n", |
+ parent->vreg(), |
source_block->block_id(), |
target_block->block_id())); |
if (parent->next_sibling() == NULL) { |
// Nothing to connect. The whole range was allocated to the same location. |
- TRACE_ALLOC(OS::Print("range %"Pd" has no siblings\n", parent->vreg())); |
+ TRACE_ALLOC(OS::Print("range v%"Pd" has no siblings\n", parent->vreg())); |
return; |
} |
@@ -2176,13 +2336,15 @@ void FlowGraphAllocator::ConnectSplitSiblings(LiveRange* parent, |
range = range->next_sibling(); |
} |
- TRACE_ALLOC(OS::Print("connecting [%"Pd", %"Pd") [", |
- source_cover->Start(), source_cover->End())); |
- TRACE_ALLOC(source.Print()); |
- TRACE_ALLOC(OS::Print("] to [%"Pd", %"Pd") [", |
- target_cover->Start(), target_cover->End())); |
- TRACE_ALLOC(target.Print()); |
- TRACE_ALLOC(OS::Print("]\n")); |
+ TRACE_ALLOC(OS::Print("connecting v%"Pd" between [%"Pd", %"Pd") {%s} " |
+ "to [%"Pd", %"Pd") {%s}\n", |
+ parent->vreg(), |
+ source_cover->Start(), |
+ source_cover->End(), |
+ source.Name(), |
+ target_cover->Start(), |
+ target_cover->End(), |
+ target.Name())); |
// Siblings were allocated to the same register. |
if (source.Equals(target)) return; |