| Index: src/compiler/move-optimizer.cc
|
| diff --git a/src/compiler/move-optimizer.cc b/src/compiler/move-optimizer.cc
|
| index 132ac02f423a2d03d069af28c6e3f3f79e9eefa3..477f139a14d331eacf9e608823bf9a71a856163d 100644
|
| --- a/src/compiler/move-optimizer.cc
|
| +++ b/src/compiler/move-optimizer.cc
|
| @@ -35,39 +35,6 @@ typedef ZoneMap<MoveKey, unsigned, MoveKeyCompare> MoveMap;
|
| typedef ZoneSet<InstructionOperand, CompareOperandModuloType> OperandSet;
|
|
|
|
|
| -bool GapsCanMoveOver(Instruction* instr, Zone* zone) {
|
| - if (instr->IsNop()) return true;
|
| - if (instr->ClobbersTemps() || instr->ClobbersRegisters() ||
|
| - instr->ClobbersDoubleRegisters()) {
|
| - return false;
|
| - }
|
| - if (instr->arch_opcode() != ArchOpcode::kArchNop) return false;
|
| -
|
| - ZoneSet<InstructionOperand, OperandCompare> operands(zone);
|
| - for (size_t i = 0; i < instr->InputCount(); ++i) {
|
| - operands.insert(*instr->InputAt(i));
|
| - }
|
| - for (size_t i = 0; i < instr->OutputCount(); ++i) {
|
| - operands.insert(*instr->OutputAt(i));
|
| - }
|
| - for (size_t i = 0; i < instr->TempCount(); ++i) {
|
| - operands.insert(*instr->TempAt(i));
|
| - }
|
| - for (int i = Instruction::GapPosition::FIRST_GAP_POSITION;
|
| - i <= Instruction::GapPosition::LAST_GAP_POSITION; ++i) {
|
| - ParallelMove* moves = instr->parallel_moves()[i];
|
| - if (moves == nullptr) continue;
|
| - for (MoveOperands* move : *moves) {
|
| - if (operands.count(move->source()) > 0 ||
|
| - operands.count(move->destination()) > 0) {
|
| - return false;
|
| - }
|
| - }
|
| - }
|
| - return true;
|
| -}
|
| -
|
| -
|
| int FindFirstNonEmptySlot(const Instruction* instr) {
|
| int i = Instruction::FIRST_GAP_POSITION;
|
| for (; i <= Instruction::LAST_GAP_POSITION; i++) {
|
| @@ -88,11 +55,13 @@ int FindFirstNonEmptySlot(const Instruction* instr) {
|
| MoveOptimizer::MoveOptimizer(Zone* local_zone, InstructionSequence* code)
|
| : local_zone_(local_zone),
|
| code_(code),
|
| - to_finalize_(local_zone),
|
| local_vector_(local_zone) {}
|
|
|
|
|
| void MoveOptimizer::Run() {
|
| + for (Instruction* instruction : code()->instructions()) {
|
| + CompressGaps(instruction);
|
| + }
|
| for (InstructionBlock* block : code()->instruction_blocks()) {
|
| CompressBlock(block);
|
| }
|
| @@ -114,13 +83,140 @@ void MoveOptimizer::Run() {
|
| }
|
| OptimizeMerge(block);
|
| }
|
| - for (Instruction* gap : to_finalize_) {
|
| + for (Instruction* gap : code()->instructions()) {
|
| FinalizeMoves(gap);
|
| }
|
| }
|
|
|
| +void MoveOptimizer::RemoveClobberedDestinations(Instruction* instruction) {
|
| + if (instruction->IsCall()) return;
|
| + ParallelMove* moves = instruction->parallel_moves()[0];
|
| + if (moves == nullptr) return;
|
|
|
| -void MoveOptimizer::CompressMoves(ParallelMove* left, ParallelMove* right) {
|
| + DCHECK(instruction->parallel_moves()[1] == nullptr ||
|
| + instruction->parallel_moves()[1]->empty());
|
| +
|
| + OperandSet outputs(local_zone());
|
| + OperandSet inputs(local_zone());
|
| +
|
| + // Outputs and temps are treated together as potentially clobbering a
|
| + // destination operand.
|
| + for (size_t i = 0; i < instruction->OutputCount(); ++i) {
|
| + outputs.insert(*instruction->OutputAt(i));
|
| + }
|
| + for (size_t i = 0; i < instruction->TempCount(); ++i) {
|
| + outputs.insert(*instruction->TempAt(i));
|
| + }
|
| +
|
| + // Input operands block elisions.
|
| + for (size_t i = 0; i < instruction->InputCount(); ++i) {
|
| + inputs.insert(*instruction->InputAt(i));
|
| + }
|
| +
|
| + // Elide moves made redundant by the instruction.
|
| + for (MoveOperands* move : *moves) {
|
| + if (outputs.find(move->destination()) != outputs.end() &&
|
| + inputs.find(move->destination()) == inputs.end()) {
|
| + move->Eliminate();
|
| + }
|
| + }
|
| +
|
| + // The ret instruction makes any assignment before it unnecessary, except for
|
| + // the one for its input.
|
| + if (instruction->opcode() == ArchOpcode::kArchRet) {
|
| + for (MoveOperands* move : *moves) {
|
| + if (inputs.find(move->destination()) == inputs.end()) {
|
| + move->Eliminate();
|
| + }
|
| + }
|
| + }
|
| +}
|
| +
|
| +void MoveOptimizer::MigrateMoves(Instruction* to, Instruction* from) {
|
| + if (from->IsCall()) return;
|
| +
|
| + ParallelMove* from_moves = from->parallel_moves()[0];
|
| + if (from_moves == nullptr || from_moves->empty()) return;
|
| +
|
| + ZoneSet<InstructionOperand, OperandCompare> dst_cant_be(local_zone());
|
| + ZoneSet<InstructionOperand, OperandCompare> src_cant_be(local_zone());
|
| +
|
| + // If an operand is an input to the instruction, we cannot move assignments
|
| + // where it appears on the LHS.
|
| + for (size_t i = 0; i < from->InputCount(); ++i) {
|
| + dst_cant_be.insert(*from->InputAt(i));
|
| + }
|
| + // If an operand is output to the instruction, we cannot move assignments
|
| + // where it appears on the RHS, because we would lose its value before the
|
| + // instruction.
|
| + // Same for temp operands.
|
| + // The output can't appear on the LHS because we performed
|
| + // RemoveClobberedDestinations for the "from" instruction.
|
| + for (size_t i = 0; i < from->OutputCount(); ++i) {
|
| + src_cant_be.insert(*from->OutputAt(i));
|
| + }
|
| + for (size_t i = 0; i < from->TempCount(); ++i) {
|
| + src_cant_be.insert(*from->TempAt(i));
|
| + }
|
| + for (MoveOperands* move : *from_moves) {
|
| + if (move->IsRedundant()) continue;
|
| + // Assume dest has a value "V". If we have a "dest = y" move, then we can't
|
| + // move "z = dest", because z would become y rather than "V".
|
| + // We assume CompressMoves has happened before this, which means we don't
|
| + // have more than one assignment to dest.
|
| + src_cant_be.insert(move->destination());
|
| + }
|
| +
|
| + ZoneSet<MoveKey, MoveKeyCompare> move_candidates(local_zone());
|
| + // We start with all the moves that don't have conflicting source or
|
| + // destination operands are eligible for being moved down.
|
| + for (MoveOperands* move : *from_moves) {
|
| + if (move->IsRedundant()) continue;
|
| + if (dst_cant_be.find(move->destination()) == dst_cant_be.end()) {
|
| + MoveKey key = {move->source(), move->destination()};
|
| + move_candidates.insert(key);
|
| + }
|
| + }
|
| + if (move_candidates.empty()) return;
|
| +
|
| + // Stabilize the candidate set.
|
| + bool changed = false;
|
| + do {
|
| + changed = false;
|
| + for (auto iter = move_candidates.begin(); iter != move_candidates.end();) {
|
| + auto current = iter;
|
| + ++iter;
|
| + InstructionOperand src = current->source;
|
| + if (src_cant_be.find(src) != src_cant_be.end()) {
|
| + src_cant_be.insert(current->destination);
|
| + move_candidates.erase(current);
|
| + changed = true;
|
| + }
|
| + }
|
| + } while (changed);
|
| +
|
| + ParallelMove to_move(local_zone());
|
| + for (MoveOperands* move : *from_moves) {
|
| + if (move->IsRedundant()) continue;
|
| + MoveKey key = {move->source(), move->destination()};
|
| + if (move_candidates.find(key) != move_candidates.end()) {
|
| + to_move.AddMove(move->source(), move->destination(), code_zone());
|
| + move->Eliminate();
|
| + }
|
| + }
|
| + if (to_move.empty()) return;
|
| +
|
| + ParallelMove* dest =
|
| + to->GetOrCreateParallelMove(Instruction::GapPosition::START, code_zone());
|
| +
|
| + CompressMoves(&to_move, dest);
|
| + DCHECK(dest->empty());
|
| + for (MoveOperands* m : to_move) {
|
| + dest->push_back(m);
|
| + }
|
| +}
|
| +
|
| +void MoveOptimizer::CompressMoves(ParallelMove* left, MoveOpVector* right) {
|
| if (right == nullptr) return;
|
|
|
| MoveOpVector& eliminated = local_vector();
|
| @@ -150,54 +246,49 @@ void MoveOptimizer::CompressMoves(ParallelMove* left, ParallelMove* right) {
|
| DCHECK(eliminated.empty());
|
| }
|
|
|
| +void MoveOptimizer::CompressGaps(Instruction* instruction) {
|
| + int i = FindFirstNonEmptySlot(instruction);
|
| + bool has_moves = i <= Instruction::LAST_GAP_POSITION;
|
| + USE(has_moves);
|
| +
|
| + if (i == Instruction::LAST_GAP_POSITION) {
|
| + std::swap(instruction->parallel_moves()[Instruction::FIRST_GAP_POSITION],
|
| + instruction->parallel_moves()[Instruction::LAST_GAP_POSITION]);
|
| + } else if (i == Instruction::FIRST_GAP_POSITION) {
|
| + CompressMoves(
|
| + instruction->parallel_moves()[Instruction::FIRST_GAP_POSITION],
|
| + instruction->parallel_moves()[Instruction::LAST_GAP_POSITION]);
|
| + }
|
| + // We either have no moves, or, after swapping or compressing, we have
|
| + // all the moves in the first gap position, and none in the second/end gap
|
| + // position.
|
| + ParallelMove* first =
|
| + instruction->parallel_moves()[Instruction::FIRST_GAP_POSITION];
|
| + ParallelMove* last =
|
| + instruction->parallel_moves()[Instruction::LAST_GAP_POSITION];
|
| + USE(first);
|
| + USE(last);
|
| +
|
| + DCHECK(!has_moves ||
|
| + (first != nullptr && (last == nullptr || last->empty())));
|
| +}
|
|
|
| -// Smash all consecutive moves into the left most move slot and accumulate them
|
| -// as much as possible across instructions.
|
| void MoveOptimizer::CompressBlock(InstructionBlock* block) {
|
| - Instruction* prev_instr = nullptr;
|
| - for (int index = block->code_start(); index < block->code_end(); ++index) {
|
| - Instruction* instr = code()->instructions()[index];
|
| - int i = FindFirstNonEmptySlot(instr);
|
| - bool has_moves = i <= Instruction::LAST_GAP_POSITION;
|
| -
|
| - if (i == Instruction::LAST_GAP_POSITION) {
|
| - std::swap(instr->parallel_moves()[Instruction::FIRST_GAP_POSITION],
|
| - instr->parallel_moves()[Instruction::LAST_GAP_POSITION]);
|
| - } else if (i == Instruction::FIRST_GAP_POSITION) {
|
| - CompressMoves(instr->parallel_moves()[Instruction::FIRST_GAP_POSITION],
|
| - instr->parallel_moves()[Instruction::LAST_GAP_POSITION]);
|
| - }
|
| - // We either have no moves, or, after swapping or compressing, we have
|
| - // all the moves in the first gap position, and none in the second/end gap
|
| - // position.
|
| - ParallelMove* first =
|
| - instr->parallel_moves()[Instruction::FIRST_GAP_POSITION];
|
| - ParallelMove* last =
|
| - instr->parallel_moves()[Instruction::LAST_GAP_POSITION];
|
| - USE(last);
|
| -
|
| - DCHECK(!has_moves ||
|
| - (first != nullptr && (last == nullptr || last->empty())));
|
| -
|
| - if (prev_instr != nullptr) {
|
| - if (has_moves) {
|
| - // Smash first into prev_instr, killing left.
|
| - ParallelMove* pred_moves = prev_instr->parallel_moves()[0];
|
| - CompressMoves(pred_moves, first);
|
| - }
|
| - // Slide prev_instr down so we always know where to look for it.
|
| - std::swap(prev_instr->parallel_moves()[0], instr->parallel_moves()[0]);
|
| - }
|
| + int first_instr_index = block->first_instruction_index();
|
| + int last_instr_index = block->last_instruction_index();
|
|
|
| - prev_instr = instr->parallel_moves()[0] == nullptr ? nullptr : instr;
|
| - if (GapsCanMoveOver(instr, local_zone())) continue;
|
| - if (prev_instr != nullptr) {
|
| - to_finalize_.push_back(prev_instr);
|
| - prev_instr = nullptr;
|
| - }
|
| - }
|
| - if (prev_instr != nullptr) {
|
| - to_finalize_.push_back(prev_instr);
|
| + // Start by removing gap assignments where the output of the subsequent
|
| + // instruction appears on LHS, as long as they are not needed by its input.
|
| + Instruction* prev_instr = code()->instructions()[first_instr_index];
|
| + RemoveClobberedDestinations(prev_instr);
|
| +
|
| + for (int index = first_instr_index + 1; index <= last_instr_index; ++index) {
|
| + Instruction* instr = code()->instructions()[index];
|
| + // Migrate to the gap of prev_instr eligible moves from instr.
|
| + MigrateMoves(instr, prev_instr);
|
| + // Remove gap assignments clobbered by instr's output.
|
| + RemoveClobberedDestinations(instr);
|
| + prev_instr = instr;
|
| }
|
| }
|
|
|
| @@ -258,15 +349,7 @@ void MoveOptimizer::OptimizeMerge(InstructionBlock* block) {
|
| if (move_map.empty() || correct_counts == 0) return;
|
|
|
| // Find insertion point.
|
| - Instruction* instr = nullptr;
|
| - for (int i = block->first_instruction_index();
|
| - i <= block->last_instruction_index(); ++i) {
|
| - instr = code()->instructions()[i];
|
| - if (correct_counts != move_map.size() ||
|
| - !GapsCanMoveOver(instr, local_zone()) || !instr->AreMovesRedundant())
|
| - break;
|
| - }
|
| - DCHECK_NOT_NULL(instr);
|
| + Instruction* instr = code()->instructions()[block->first_instruction_index()];
|
|
|
| if (correct_counts != move_map.size()) {
|
| // Moves that are unique to each predecessor won't be pushed to the common
|
| @@ -309,10 +392,8 @@ void MoveOptimizer::OptimizeMerge(InstructionBlock* block) {
|
|
|
| DCHECK_NOT_NULL(instr);
|
| bool gap_initialized = true;
|
| - if (instr->parallel_moves()[0] == nullptr ||
|
| - instr->parallel_moves()[0]->empty()) {
|
| - to_finalize_.push_back(instr);
|
| - } else {
|
| + if (instr->parallel_moves()[0] != nullptr &&
|
| + !instr->parallel_moves()[0]->empty()) {
|
| // Will compress after insertion.
|
| gap_initialized = false;
|
| std::swap(instr->parallel_moves()[0], instr->parallel_moves()[1]);
|
| @@ -340,6 +421,7 @@ void MoveOptimizer::OptimizeMerge(InstructionBlock* block) {
|
| if (!gap_initialized) {
|
| CompressMoves(instr->parallel_moves()[0], instr->parallel_moves()[1]);
|
| }
|
| + CompressBlock(block);
|
| }
|
|
|
|
|
| @@ -368,8 +450,10 @@ void MoveOptimizer::FinalizeMoves(Instruction* instr) {
|
| MoveOpVector& loads = local_vector();
|
| DCHECK(loads.empty());
|
|
|
| + ParallelMove* parallel_moves = instr->parallel_moves()[0];
|
| + if (parallel_moves == nullptr) return;
|
| // Find all the loads.
|
| - for (MoveOperands* move : *instr->parallel_moves()[0]) {
|
| + for (MoveOperands* move : *parallel_moves) {
|
| if (move->IsRedundant()) continue;
|
| if (move->source().IsConstant() || IsSlot(move->source())) {
|
| loads.push_back(move);
|
|
|