Commit 1ecf58f4 authored by mtrofin's avatar mtrofin Committed by Commit bot

[turbofan] fine grained in-block move optimization

So far, we've been moving down gaps wholesale. This change moves
individual move operations instead. This improves some benchmarks,
and should overall reduce code size, because it improves the chance of
reducing the number of moves.

For example, there are improvements on x64 in Emscripten (Bullet, in
particular) , JetStream geomean, Embenchen (zlib).

In the process of making this change, I noticed we can separate the
tasks performed by the move optimizer, as follows:

- group gaps into 1
- push gaps down, jumping instructions (these 2 were together before)
- merge blocks (and then push gaps down)
- finalize

We can do without a finalization list. This avoids duplicating storage -
we already have the list of instructions; it also simplifies the logic, since,
with this change, we may process an instruction's gap twice.

Compile time doesn't regress much (see pathological cases), but we
may want to avoid the allocations of the few sets used in the new code.
I'll do that in a subsequent change.

BUG=

Review URL: https://codereview.chromium.org/1634093002

Cr-Commit-Position: refs/heads/master@{#33715}
parent 152eb891
......@@ -633,8 +633,14 @@ class ParallelMove final : public ZoneVector<MoveOperands*>, public ZoneObject {
MoveOperands* AddMove(const InstructionOperand& from,
const InstructionOperand& to) {
auto zone = get_allocator().zone();
auto move = new (zone) MoveOperands(from, to);
Zone* zone = get_allocator().zone();
return AddMove(from, to, zone);
}
MoveOperands* AddMove(const InstructionOperand& from,
const InstructionOperand& to,
Zone* operand_allocation_zone) {
MoveOperands* move = new (operand_allocation_zone) MoveOperands(from, to);
push_back(move);
return move;
}
......
This diff is collapsed.
......@@ -26,15 +26,30 @@ class MoveOptimizer final {
Zone* code_zone() const { return code()->zone(); }
MoveOpVector& local_vector() { return local_vector_; }
void CompressBlock(InstructionBlock* blocke);
void CompressMoves(ParallelMove* left, ParallelMove* right);
// Consolidate moves into the first gap.
void CompressGaps(Instruction* instr);
// Attempt to push down to the last instruction those moves that can.
void CompressBlock(InstructionBlock* block);
// Consolidate moves into the first gap.
void CompressMoves(ParallelMove* left, MoveOpVector* right);
// Push down those moves in the gap of from that do not change the
// semantics of the from instruction, nor the semantics of the moves
// that remain behind.
void MigrateMoves(Instruction* to, Instruction* from);
void RemoveClobberedDestinations(Instruction* instruction);
const Instruction* LastInstruction(const InstructionBlock* block) const;
// Consolidate common moves appearing accross all predecessors of a block.
void OptimizeMerge(InstructionBlock* block);
void FinalizeMoves(Instruction* instr);
Zone* const local_zone_;
InstructionSequence* const code_;
Instructions to_finalize_;
MoveOpVector local_vector_;
DISALLOW_COPY_AND_ASSIGN(MoveOptimizer);
......
......@@ -3339,8 +3339,31 @@ void LiveRangeConnector::ResolveControlFlow(Zone* local_zone) {
if (pred_op.Equals(cur_op)) continue;
if (!pred_op.IsAnyRegister() && cur_op.IsAnyRegister()) {
// We're doing a reload.
// We don't need to, if:
// 1) there's no register use in this block, and
// 2) the range ends before the block does, and
// 3) we don't have a successor, or the successor is spilled.
LifetimePosition block_start =
LifetimePosition::GapFromInstructionIndex(block->code_start());
LifetimePosition block_end =
LifetimePosition::GapFromInstructionIndex(block->code_end());
const LiveRange* current = result.cur_cover_;
const LiveRange* successor = current->next();
if (current->End() < block_end &&
(successor == nullptr || successor->spilled())) {
// verify point 1: no register use. We can go to the end of the
// range, since it's all within the block.
bool uses_reg = false;
for (const UsePosition* use = current->NextUsePosition(block_start);
use != nullptr; use = use->next()) {
if (use->operand()->IsAnyRegister()) {
uses_reg = true;
break;
}
}
if (!uses_reg) continue;
}
if (current->TopLevel()->IsSpilledOnlyInDeferredBlocks() &&
pred_block->IsDeferred()) {
// The spill location should be defined in pred_block, so add
......
......@@ -3,6 +3,7 @@
// found in the LICENSE file.
#include "src/compiler/move-optimizer.h"
#include "src/compiler/pipeline.h"
#include "test/unittests/compiler/instruction-sequence-unittest.h"
namespace v8 {
......@@ -227,8 +228,8 @@ TEST_F(MoveOptimizerTest, GapsCanMoveOverInstruction) {
ctant_def->GetParallelMove(Instruction::GapPosition::END);
ParallelMove* last_start =
last->GetParallelMove(Instruction::GapPosition::START);
CHECK(inst1_start == nullptr || inst1_start->size() == 0);
CHECK(inst1_end == nullptr || inst1_end->size() == 0);
CHECK(inst1_start == nullptr || NonRedundantSize(inst1_start) == 0);
CHECK(inst1_end == nullptr || NonRedundantSize(inst1_end) == 0);
CHECK(last_start->size() == 2);
int redundants = 0;
int assignment = 0;
......@@ -321,6 +322,22 @@ TEST_F(MoveOptimizerTest, GapConflictSubsetMovesDoNotMerge) {
CHECK(Contains(b1_move, Reg(0), Reg(1)));
}
TEST_F(MoveOptimizerTest, ClobberedDestinationsAreEliminated) {
StartBlock();
EmitNop();
Instruction* first_instr = LastInstruction();
AddMove(first_instr, Reg(0), Reg(1));
EmitOI(Reg(1), 0, nullptr);
Instruction* last_instr = LastInstruction();
EndBlock();
Optimize();
ParallelMove* first_move = first_instr->parallel_moves()[0];
CHECK_EQ(0, NonRedundantSize(first_move));
ParallelMove* last_move = last_instr->parallel_moves()[0];
CHECK_EQ(0, NonRedundantSize(last_move));
}
} // namespace compiler
} // namespace internal
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment