1 // Copyright 2014 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "src/compiler/move-optimizer.h"
13 MoveOperands* PrepareInsertAfter(ParallelMove* left, MoveOperands* move,
15 auto move_ops = left->move_operands();
16 MoveOperands* replacement = nullptr;
17 MoveOperands* to_eliminate = nullptr;
18 for (auto curr = move_ops->begin(); curr != move_ops->end(); ++curr) {
19 if (curr->IsEliminated()) continue;
20 if (curr->destination()->Equals(move->source())) {
23 if (to_eliminate != nullptr) break;
24 } else if (curr->destination()->Equals(move->destination())) {
25 DCHECK(!to_eliminate);
27 if (replacement != nullptr) break;
30 DCHECK(!(replacement == to_eliminate && replacement != nullptr));
31 if (replacement != nullptr) {
32 auto new_source = InstructionOperand::New(
33 zone, replacement->source()->kind(), replacement->source()->index());
34 move->set_source(new_source);
40 bool GapsCanMoveOver(Instruction* instr) {
41 DCHECK(!instr->IsGapMoves());
42 return instr->IsSourcePosition() || instr->IsNop();
46 int FindFirstNonEmptySlot(GapInstruction* gap) {
47 int i = GapInstruction::FIRST_INNER_POSITION;
48 for (; i <= GapInstruction::LAST_INNER_POSITION; i++) {
49 auto move = gap->parallel_moves()[i];
50 if (move == nullptr) continue;
51 auto move_ops = move->move_operands();
52 auto op = move_ops->begin();
53 for (; op != move_ops->end(); ++op) {
54 if (!op->IsRedundant()) break;
57 if (op != move_ops->end()) break; // Found non-redundant move.
58 move_ops->Rewind(0); // Clear this redundant move.
66 MoveOptimizer::MoveOptimizer(Zone* local_zone, InstructionSequence* code)
67 : local_zone_(local_zone),
69 to_finalize_(local_zone),
70 temp_vector_0_(local_zone),
71 temp_vector_1_(local_zone) {}
74 void MoveOptimizer::Run() {
75 for (auto* block : code()->instruction_blocks()) {
78 for (auto gap : to_finalize_) {
84 void MoveOptimizer::CompressMoves(MoveOpVector* eliminated, ParallelMove* left,
85 ParallelMove* right) {
86 DCHECK(eliminated->empty());
87 auto move_ops = right->move_operands();
88 if (!left->move_operands()->is_empty()) {
89 // Modify the right moves in place and collect moves that will be killed by
90 // merging the two gaps.
91 for (auto op = move_ops->begin(); op != move_ops->end(); ++op) {
92 if (op->IsRedundant()) continue;
93 MoveOperands* to_eliminate = PrepareInsertAfter(left, op, code_zone());
94 if (to_eliminate != nullptr) eliminated->push_back(to_eliminate);
96 // Eliminate dead moves. Must happen before insertion of new moves as the
97 // contents of eliminated are pointers into a list.
98 for (auto to_eliminate : *eliminated) {
99 to_eliminate->Eliminate();
103 // Add all possibly modified moves from right side.
104 for (auto op = move_ops->begin(); op != move_ops->end(); ++op) {
105 if (op->IsRedundant()) continue;
106 left->move_operands()->Add(*op, code_zone());
113 // Smash all consecutive moves into the left most move slot and accumulate them
114 // as much as possible across instructions.
115 void MoveOptimizer::CompressBlock(InstructionBlock* block) {
116 auto temp_vector = temp_vector_0();
117 DCHECK(temp_vector.empty());
118 GapInstruction* prev_gap = nullptr;
119 for (int index = block->code_start(); index < block->code_end(); ++index) {
120 auto instr = code()->instructions()[index];
121 if (!instr->IsGapMoves()) {
122 if (GapsCanMoveOver(instr)) continue;
123 if (prev_gap != nullptr) to_finalize_.push_back(prev_gap);
127 auto gap = GapInstruction::cast(instr);
128 int i = FindFirstNonEmptySlot(gap);
129 // Nothing to do here.
130 if (i == GapInstruction::LAST_INNER_POSITION + 1) {
131 if (prev_gap != nullptr) {
132 // Slide prev_gap down so we always know where to look for it.
133 std::swap(prev_gap->parallel_moves()[0], gap->parallel_moves()[0]);
138 // Move the first non-empty gap to position 0.
139 std::swap(gap->parallel_moves()[0], gap->parallel_moves()[i]);
140 auto left = gap->parallel_moves()[0];
141 // Compress everything into position 0.
142 for (++i; i <= GapInstruction::LAST_INNER_POSITION; ++i) {
143 auto move = gap->parallel_moves()[i];
144 if (move == nullptr) continue;
145 CompressMoves(&temp_vector, left, move);
147 if (prev_gap != nullptr) {
148 // Smash left into prev_gap, killing left.
149 auto pred_moves = prev_gap->parallel_moves()[0];
150 CompressMoves(&temp_vector, pred_moves, left);
151 // Slide prev_gap down so we always know where to look for it.
152 std::swap(prev_gap->parallel_moves()[0], gap->parallel_moves()[0]);
156 if (prev_gap != nullptr) to_finalize_.push_back(prev_gap);
160 // Split multiple loads of the same constant or stack slot off into the second
161 // slot and keep remaining moves in the first slot.
162 void MoveOptimizer::FinalizeMoves(GapInstruction* gap) {
163 auto loads = temp_vector_0();
164 DCHECK(loads.empty());
165 auto new_moves = temp_vector_1();
166 DCHECK(new_moves.empty());
167 auto move_ops = gap->parallel_moves()[0]->move_operands();
168 for (auto move = move_ops->begin(); move != move_ops->end(); ++move) {
169 if (move->IsRedundant()) {
173 if (!(move->source()->IsConstant() || move->source()->IsStackSlot() ||
174 move->source()->IsDoubleStackSlot()))
176 // Search for existing move to this slot.
177 MoveOperands* found = nullptr;
178 for (auto load : loads) {
179 if (load->source()->Equals(move->source())) {
184 // Not found so insert.
185 if (found == nullptr) {
186 loads.push_back(move);
187 // Replace source with copy for later use.
188 auto dest = move->destination();
189 move->set_destination(
190 InstructionOperand::New(code_zone(), dest->kind(), dest->index()));
193 if ((found->destination()->IsStackSlot() ||
194 found->destination()->IsDoubleStackSlot()) &&
195 !(move->destination()->IsStackSlot() ||
196 move->destination()->IsDoubleStackSlot())) {
197 // Found a better source for this load. Smash it in place to affect other
198 // loads that have already been split.
199 InstructionOperand::Kind found_kind = found->destination()->kind();
200 int found_index = found->destination()->index();
202 InstructionOperand::New(code_zone(), found_kind, found_index);
203 auto dest = move->destination();
204 found->destination()->ConvertTo(dest->kind(), dest->index());
205 move->set_destination(next_dest);
207 // move from load destination.
208 move->set_source(found->destination());
209 new_moves.push_back(move);
212 if (new_moves.empty()) return;
213 // Insert all new moves into slot 1.
214 auto slot_1 = gap->GetOrCreateParallelMove(
215 static_cast<GapInstruction::InnerPosition>(1), code_zone());
216 DCHECK(slot_1->move_operands()->is_empty());
217 slot_1->move_operands()->AddBlock(MoveOperands(nullptr, nullptr),
218 static_cast<int>(new_moves.size()),
220 auto it = slot_1->move_operands()->begin();
221 for (auto new_move : new_moves) {
222 std::swap(*new_move, *it);
225 DCHECK_EQ(it, slot_1->move_operands()->end());
229 } // namespace compiler
230 } // namespace internal