Chromium Code Reviews| Index: src/compiler/register-allocator.cc |
| diff --git a/src/compiler/register-allocator.cc b/src/compiler/register-allocator.cc |
| index cdf4e9976b6d11ff974e10cf481f8b0466693585..82b762cc7f640acfe8d1ba5ae4faf9be102d5231 100644 |
| --- a/src/compiler/register-allocator.cc |
| +++ b/src/compiler/register-allocator.cc |
| @@ -2200,33 +2200,96 @@ void LiveRangeBuilder::ProcessPhis(const InstructionBlock* block, |
| // block. |
| int phi_vreg = phi->virtual_register(); |
| live->Remove(phi_vreg); |
| - // Select the hint from the first predecessor block that preceeds this block |
| - // in the rpo ordering. Prefer non-deferred blocks. The enforcement of |
| - // hinting in rpo order is required because hint resolution that happens |
| - // later in the compiler pipeline visits instructions in reverse rpo, |
| - // relying on the fact that phis are encountered before their hints. |
| - const Instruction* instr = nullptr; |
| - const InstructionBlock::Predecessors& predecessors = block->predecessors(); |
| - for (size_t i = 0; i < predecessors.size(); ++i) { |
| - const InstructionBlock* predecessor_block = |
| - code()->InstructionBlockAt(predecessors[i]); |
| - if (predecessor_block->rpo_number() < block->rpo_number()) { |
| - instr = GetLastInstruction(code(), predecessor_block); |
| - if (!predecessor_block->IsDeferred()) break; |
| - } |
| - } |
| - DCHECK_NOT_NULL(instr); |
| - |
| + // Select a hint from a predecessor block that preceeds this block in the |
| + // rpo order. In order of priority: |
| + // - Avoid hints from deferred blocks. |
| + // - Prefer hints from allocated (or explicit) operands. |
| + // - Prefer hints from empty blocks (containing just parallel moves and a |
| + // jump). This helps to avoid otherwise useless move + jump sequences. |
|
Mircea Trofin
2016/07/04 16:32:15
Nit: the moves wouldn't be useless, I think you're
jbramley
2016/07/04 16:58:54
Yes, that's what I meant. I'll re-word it.
|
| + // The enforcement of hinting in rpo order is required because hint |
| + // resolution that happens later in the compiler pipeline visits |
| + // instructions in reverse rpo order, relying on the fact that phis are |
| + // encountered before their hints. |
| InstructionOperand* hint = nullptr; |
| - for (MoveOperands* move : *instr->GetParallelMove(Instruction::END)) { |
| - InstructionOperand& to = move->destination(); |
| - if (to.IsUnallocated() && |
| - UnallocatedOperand::cast(to).virtual_register() == phi_vreg) { |
| - hint = &move->source(); |
| - break; |
| + int hint_preference = 0; |
| + |
| + for (RpoNumber predecessor : block->predecessors()) { |
| + const InstructionBlock* predecessor_block = |
| + code()->InstructionBlockAt(predecessor); |
| + DCHECK_EQ(predecessor_block->rpo_number(), predecessor); |
| + |
| + // Only take hints from earlier rpo numbers. |
| + if (predecessor >= block->rpo_number()) continue; |
| + |
| + // Look up the predecessor instruction. |
| + const Instruction* predecessor_instr = |
| + GetLastInstruction(code(), predecessor_block); |
| + InstructionOperand* predecessor_hint = nullptr; |
| + // Phis are assigned in an END move after the last instruction in each |
|
Mircea Trofin
2016/07/04 16:32:15
I think you meant to say "in the END position of t
jbramley
2016/07/04 16:58:54
Acknowledged.
|
| + // predecessor block. |
| + for (MoveOperands* move : |
| + *predecessor_instr->GetParallelMove(Instruction::END)) { |
| + InstructionOperand& to = move->destination(); |
| + if (to.IsUnallocated() && |
| + UnallocatedOperand::cast(to).virtual_register() == phi_vreg) { |
| + predecessor_hint = &move->source(); |
| + break; |
| + } |
| + } |
| + DCHECK_NOT_NULL(predecessor_hint); |
| + |
| + // For each predecessor, calculate a score according to the priorities |
| + // described above, and pick the best one. |
| + int predecessor_hint_preference = 0; |
| + const int kNotDeferredBlockPreference = 4; |
| + const int kMoveIsAllocatedPreference = 2; |
| + const int kBlockIsEmptyPreference = 1; |
| + |
| + // - Avoid hints from deferred blocks. |
| + if (!predecessor_block->IsDeferred()) { |
| + predecessor_hint_preference += kNotDeferredBlockPreference; |
|
Mircea Trofin
2016/07/04 16:32:15
Nit: replace with bitwise or, I think that'd make
jbramley
2016/07/04 16:58:54
I'd half planned to support an arithmetic score, t
Mircea Trofin
2016/07/04 21:20:35
Oh - the fact that they are powers of 2 threw me o
|
| + } |
| + |
| + // - Prefer hints from allocated (or explicit) operands. |
| + // |
| + // Already-allocated or explicit operands are typically assigned using |
| + // the parallel moves on the last instruction. For example: |
| + // |
| + // gap (v101 = [x0|R|w32]) (v100 = v101) |
| + // ArchJmp |
| + // ... |
| + // phi: v100 = v101 v102 |
| + // |
| + // We have already found the END move, so look for a matching START move |
| + // from an allocated (or explicit) operand. |
|
Mircea Trofin
2016/07/04 16:32:15
This happens if the previous instruction (the one
jbramley
2016/07/04 16:58:54
Reasonably common. Without this, the empty-block c
Mircea Trofin
2016/07/04 21:20:35
It may be worth investigating. The compile benchma
|
| + auto moves = predecessor_instr->GetParallelMove(Instruction::START); |
| + if (moves != nullptr) { |
| + for (MoveOperands* move : *moves) { |
| + InstructionOperand& to = move->destination(); |
| + if (predecessor_hint->Equals(to)) { |
| + if (move->source().IsAllocated() || move->source().IsExplicit()) { |
| + predecessor_hint_preference += kMoveIsAllocatedPreference; |
| + } |
| + break; |
| + } |
| + } |
| + } |
| + |
| + // - Prefer hints from empty blocks. |
| + if (predecessor_block->last_instruction_index() == |
| + predecessor_block->first_instruction_index()) { |
| + predecessor_hint_preference += kBlockIsEmptyPreference; |
| + } |
| + |
| + if ((hint == nullptr) || |
| + (predecessor_hint_preference > hint_preference)) { |
| + // Take the hint from this predecessor. |
| + hint = predecessor_hint; |
| + hint_preference = predecessor_hint_preference; |
| } |
| } |
| - DCHECK(hint != nullptr); |
| + DCHECK_NOT_NULL(hint); |
| + |
| LifetimePosition block_start = LifetimePosition::GapFromInstructionIndex( |
| block->first_instruction_index()); |
| UsePosition* use_pos = Define(block_start, &phi->output(), hint, |