blob: 4753d15979a99fcb0f39e59db9ab3f6a1d979938 [file] [log] [blame]
Emily Bernierd0a1eb72015-03-24 16:35:39 -04001// 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.
4
5#include "src/compiler/move-optimizer.h"
6
7namespace v8 {
8namespace internal {
9namespace compiler {
10
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000011namespace {
12
Ben Murdoch097c5b22016-05-18 11:27:45 +010013struct MoveKey {
14 InstructionOperand source;
15 InstructionOperand destination;
16};
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000017
18struct MoveKeyCompare {
19 bool operator()(const MoveKey& a, const MoveKey& b) const {
Ben Murdoch097c5b22016-05-18 11:27:45 +010020 if (a.source.EqualsCanonicalized(b.source)) {
21 return a.destination.CompareCanonicalized(b.destination);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000022 }
Ben Murdoch097c5b22016-05-18 11:27:45 +010023 return a.source.CompareCanonicalized(b.source);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000024 }
25};
26
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000027typedef ZoneMap<MoveKey, unsigned, MoveKeyCompare> MoveMap;
28typedef ZoneSet<InstructionOperand, CompareOperandModuloType> OperandSet;
29
Ben Murdoch61f157c2016-09-16 13:49:30 +010030bool Blocks(const OperandSet& set, const InstructionOperand& operand) {
31 if (set.find(operand) != set.end()) return true;
32 // Only FP registers on archs with non-simple aliasing need extra checks.
33 if (!operand.IsFPRegister() || kSimpleFPAliasing) return false;
34
35 const LocationOperand& loc = LocationOperand::cast(operand);
36 MachineRepresentation rep = loc.representation();
37 MachineRepresentation other_fp_rep = rep == MachineRepresentation::kFloat64
38 ? MachineRepresentation::kFloat32
39 : MachineRepresentation::kFloat64;
40 const RegisterConfiguration* config = RegisterConfiguration::Turbofan();
41 if (config->fp_aliasing_kind() != RegisterConfiguration::COMBINE) {
42 // Overlap aliasing case.
43 return set.find(LocationOperand(loc.kind(), loc.location_kind(),
44 other_fp_rep, loc.register_code())) !=
45 set.end();
46 }
47 // Combine aliasing case.
48 int alias_base_index = -1;
49 int aliases = config->GetAliases(rep, loc.register_code(), other_fp_rep,
50 &alias_base_index);
51 while (aliases--) {
52 int aliased_reg = alias_base_index + aliases;
53 if (set.find(LocationOperand(loc.kind(), loc.location_kind(), other_fp_rep,
54 aliased_reg)) != set.end())
55 return true;
56 }
57 return false;
58}
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000059
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000060int FindFirstNonEmptySlot(const Instruction* instr) {
61 int i = Instruction::FIRST_GAP_POSITION;
62 for (; i <= Instruction::LAST_GAP_POSITION; i++) {
63 ParallelMove* moves = instr->parallel_moves()[i];
64 if (moves == nullptr) continue;
65 for (MoveOperands* move : *moves) {
66 if (!move->IsRedundant()) return i;
67 move->Eliminate();
68 }
69 moves->clear(); // Clear this redundant move.
70 }
71 return i;
72}
73
74} // namespace
75
76
Emily Bernierd0a1eb72015-03-24 16:35:39 -040077MoveOptimizer::MoveOptimizer(Zone* local_zone, InstructionSequence* code)
78 : local_zone_(local_zone),
79 code_(code),
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000080 local_vector_(local_zone) {}
Emily Bernierd0a1eb72015-03-24 16:35:39 -040081
82
83void MoveOptimizer::Run() {
Ben Murdoch097c5b22016-05-18 11:27:45 +010084 for (Instruction* instruction : code()->instructions()) {
85 CompressGaps(instruction);
86 }
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000087 for (InstructionBlock* block : code()->instruction_blocks()) {
88 CompressBlock(block);
89 }
90 for (InstructionBlock* block : code()->instruction_blocks()) {
91 if (block->PredecessorCount() <= 1) continue;
92 if (!block->IsDeferred()) {
93 bool has_only_deferred = true;
94 for (RpoNumber& pred_id : block->predecessors()) {
95 if (!code()->InstructionBlockAt(pred_id)->IsDeferred()) {
96 has_only_deferred = false;
97 break;
Emily Bernierd0a1eb72015-03-24 16:35:39 -040098 }
99 }
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000100 // This would pull down common moves. If the moves occur in deferred
101 // blocks, and the closest common successor is not deferred, we lose the
102 // optimization of just spilling/filling in deferred blocks, when the
103 // current block is not deferred.
104 if (has_only_deferred) continue;
Emily Bernierd0a1eb72015-03-24 16:35:39 -0400105 }
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000106 OptimizeMerge(block);
107 }
Ben Murdoch097c5b22016-05-18 11:27:45 +0100108 for (Instruction* gap : code()->instructions()) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000109 FinalizeMoves(gap);
Emily Bernierd0a1eb72015-03-24 16:35:39 -0400110 }
111}
112
Ben Murdoch097c5b22016-05-18 11:27:45 +0100113void MoveOptimizer::RemoveClobberedDestinations(Instruction* instruction) {
114 if (instruction->IsCall()) return;
115 ParallelMove* moves = instruction->parallel_moves()[0];
116 if (moves == nullptr) return;
Emily Bernierd0a1eb72015-03-24 16:35:39 -0400117
Ben Murdoch097c5b22016-05-18 11:27:45 +0100118 DCHECK(instruction->parallel_moves()[1] == nullptr ||
119 instruction->parallel_moves()[1]->empty());
120
121 OperandSet outputs(local_zone());
122 OperandSet inputs(local_zone());
123
124 // Outputs and temps are treated together as potentially clobbering a
125 // destination operand.
126 for (size_t i = 0; i < instruction->OutputCount(); ++i) {
127 outputs.insert(*instruction->OutputAt(i));
128 }
129 for (size_t i = 0; i < instruction->TempCount(); ++i) {
130 outputs.insert(*instruction->TempAt(i));
131 }
132
133 // Input operands block elisions.
134 for (size_t i = 0; i < instruction->InputCount(); ++i) {
135 inputs.insert(*instruction->InputAt(i));
136 }
137
138 // Elide moves made redundant by the instruction.
139 for (MoveOperands* move : *moves) {
140 if (outputs.find(move->destination()) != outputs.end() &&
141 inputs.find(move->destination()) == inputs.end()) {
142 move->Eliminate();
143 }
144 }
145
146 // The ret instruction makes any assignment before it unnecessary, except for
147 // the one for its input.
148 if (instruction->opcode() == ArchOpcode::kArchRet) {
149 for (MoveOperands* move : *moves) {
150 if (inputs.find(move->destination()) == inputs.end()) {
151 move->Eliminate();
152 }
153 }
154 }
155}
156
157void MoveOptimizer::MigrateMoves(Instruction* to, Instruction* from) {
158 if (from->IsCall()) return;
159
160 ParallelMove* from_moves = from->parallel_moves()[0];
161 if (from_moves == nullptr || from_moves->empty()) return;
162
Ben Murdoch61f157c2016-09-16 13:49:30 +0100163 OperandSet dst_cant_be(local_zone());
164 OperandSet src_cant_be(local_zone());
Ben Murdoch097c5b22016-05-18 11:27:45 +0100165
166 // If an operand is an input to the instruction, we cannot move assignments
167 // where it appears on the LHS.
168 for (size_t i = 0; i < from->InputCount(); ++i) {
169 dst_cant_be.insert(*from->InputAt(i));
170 }
171 // If an operand is output to the instruction, we cannot move assignments
172 // where it appears on the RHS, because we would lose its value before the
173 // instruction.
174 // Same for temp operands.
175 // The output can't appear on the LHS because we performed
176 // RemoveClobberedDestinations for the "from" instruction.
177 for (size_t i = 0; i < from->OutputCount(); ++i) {
178 src_cant_be.insert(*from->OutputAt(i));
179 }
180 for (size_t i = 0; i < from->TempCount(); ++i) {
181 src_cant_be.insert(*from->TempAt(i));
182 }
183 for (MoveOperands* move : *from_moves) {
184 if (move->IsRedundant()) continue;
185 // Assume dest has a value "V". If we have a "dest = y" move, then we can't
186 // move "z = dest", because z would become y rather than "V".
187 // We assume CompressMoves has happened before this, which means we don't
188 // have more than one assignment to dest.
189 src_cant_be.insert(move->destination());
190 }
191
192 ZoneSet<MoveKey, MoveKeyCompare> move_candidates(local_zone());
193 // We start with all the moves that don't have conflicting source or
194 // destination operands are eligible for being moved down.
195 for (MoveOperands* move : *from_moves) {
196 if (move->IsRedundant()) continue;
Ben Murdoch61f157c2016-09-16 13:49:30 +0100197 if (!Blocks(dst_cant_be, move->destination())) {
Ben Murdoch097c5b22016-05-18 11:27:45 +0100198 MoveKey key = {move->source(), move->destination()};
199 move_candidates.insert(key);
200 }
201 }
202 if (move_candidates.empty()) return;
203
204 // Stabilize the candidate set.
205 bool changed = false;
206 do {
207 changed = false;
208 for (auto iter = move_candidates.begin(); iter != move_candidates.end();) {
209 auto current = iter;
210 ++iter;
211 InstructionOperand src = current->source;
Ben Murdoch61f157c2016-09-16 13:49:30 +0100212 if (Blocks(src_cant_be, src)) {
Ben Murdoch097c5b22016-05-18 11:27:45 +0100213 src_cant_be.insert(current->destination);
214 move_candidates.erase(current);
215 changed = true;
216 }
217 }
218 } while (changed);
219
220 ParallelMove to_move(local_zone());
221 for (MoveOperands* move : *from_moves) {
222 if (move->IsRedundant()) continue;
223 MoveKey key = {move->source(), move->destination()};
224 if (move_candidates.find(key) != move_candidates.end()) {
225 to_move.AddMove(move->source(), move->destination(), code_zone());
226 move->Eliminate();
227 }
228 }
229 if (to_move.empty()) return;
230
231 ParallelMove* dest =
232 to->GetOrCreateParallelMove(Instruction::GapPosition::START, code_zone());
233
234 CompressMoves(&to_move, dest);
235 DCHECK(dest->empty());
236 for (MoveOperands* m : to_move) {
237 dest->push_back(m);
238 }
239}
240
241void MoveOptimizer::CompressMoves(ParallelMove* left, MoveOpVector* right) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000242 if (right == nullptr) return;
Emily Bernierd0a1eb72015-03-24 16:35:39 -0400243
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000244 MoveOpVector& eliminated = local_vector();
245 DCHECK(eliminated.empty());
Emily Bernierd0a1eb72015-03-24 16:35:39 -0400246
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000247 if (!left->empty()) {
248 // Modify the right moves in place and collect moves that will be killed by
249 // merging the two gaps.
250 for (MoveOperands* move : *right) {
251 if (move->IsRedundant()) continue;
252 MoveOperands* to_eliminate = left->PrepareInsertAfter(move);
253 if (to_eliminate != nullptr) eliminated.push_back(to_eliminate);
Emily Bernierd0a1eb72015-03-24 16:35:39 -0400254 }
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000255 // Eliminate dead moves.
256 for (MoveOperands* to_eliminate : eliminated) {
257 to_eliminate->Eliminate();
258 }
259 eliminated.clear();
Emily Bernierd0a1eb72015-03-24 16:35:39 -0400260 }
Emily Bernierd0a1eb72015-03-24 16:35:39 -0400261 // Add all possibly modified moves from right side.
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000262 for (MoveOperands* move : *right) {
263 if (move->IsRedundant()) continue;
264 left->push_back(move);
Emily Bernierd0a1eb72015-03-24 16:35:39 -0400265 }
266 // Nuke right.
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000267 right->clear();
268 DCHECK(eliminated.empty());
Emily Bernierd0a1eb72015-03-24 16:35:39 -0400269}
270
Ben Murdoch097c5b22016-05-18 11:27:45 +0100271void MoveOptimizer::CompressGaps(Instruction* instruction) {
272 int i = FindFirstNonEmptySlot(instruction);
273 bool has_moves = i <= Instruction::LAST_GAP_POSITION;
274 USE(has_moves);
Emily Bernierd0a1eb72015-03-24 16:35:39 -0400275
Ben Murdoch097c5b22016-05-18 11:27:45 +0100276 if (i == Instruction::LAST_GAP_POSITION) {
277 std::swap(instruction->parallel_moves()[Instruction::FIRST_GAP_POSITION],
278 instruction->parallel_moves()[Instruction::LAST_GAP_POSITION]);
279 } else if (i == Instruction::FIRST_GAP_POSITION) {
280 CompressMoves(
281 instruction->parallel_moves()[Instruction::FIRST_GAP_POSITION],
282 instruction->parallel_moves()[Instruction::LAST_GAP_POSITION]);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000283 }
Ben Murdoch097c5b22016-05-18 11:27:45 +0100284 // We either have no moves, or, after swapping or compressing, we have
285 // all the moves in the first gap position, and none in the second/end gap
286 // position.
287 ParallelMove* first =
288 instruction->parallel_moves()[Instruction::FIRST_GAP_POSITION];
289 ParallelMove* last =
290 instruction->parallel_moves()[Instruction::LAST_GAP_POSITION];
291 USE(first);
292 USE(last);
293
294 DCHECK(!has_moves ||
295 (first != nullptr && (last == nullptr || last->empty())));
296}
297
298void MoveOptimizer::CompressBlock(InstructionBlock* block) {
299 int first_instr_index = block->first_instruction_index();
300 int last_instr_index = block->last_instruction_index();
301
302 // Start by removing gap assignments where the output of the subsequent
303 // instruction appears on LHS, as long as they are not needed by its input.
304 Instruction* prev_instr = code()->instructions()[first_instr_index];
305 RemoveClobberedDestinations(prev_instr);
306
307 for (int index = first_instr_index + 1; index <= last_instr_index; ++index) {
308 Instruction* instr = code()->instructions()[index];
309 // Migrate to the gap of prev_instr eligible moves from instr.
310 MigrateMoves(instr, prev_instr);
311 // Remove gap assignments clobbered by instr's output.
312 RemoveClobberedDestinations(instr);
313 prev_instr = instr;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000314 }
315}
316
317
318const Instruction* MoveOptimizer::LastInstruction(
319 const InstructionBlock* block) const {
320 return code()->instructions()[block->last_instruction_index()];
321}
322
323
324void MoveOptimizer::OptimizeMerge(InstructionBlock* block) {
325 DCHECK(block->PredecessorCount() > 1);
326 // Ensure that the last instruction in all incoming blocks don't contain
327 // things that would prevent moving gap moves across them.
328 for (RpoNumber& pred_index : block->predecessors()) {
329 const InstructionBlock* pred = code()->InstructionBlockAt(pred_index);
Ben Murdoch097c5b22016-05-18 11:27:45 +0100330
331 // If the predecessor has more than one successor, we shouldn't attempt to
332 // move down to this block (one of the successors) any of the gap moves,
333 // because their effect may be necessary to the other successors.
334 if (pred->SuccessorCount() > 1) return;
335
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000336 const Instruction* last_instr =
337 code()->instructions()[pred->last_instruction_index()];
338 if (last_instr->IsCall()) return;
339 if (last_instr->TempCount() != 0) return;
340 if (last_instr->OutputCount() != 0) return;
341 for (size_t i = 0; i < last_instr->InputCount(); ++i) {
342 const InstructionOperand* op = last_instr->InputAt(i);
343 if (!op->IsConstant() && !op->IsImmediate()) return;
344 }
345 }
346 // TODO(dcarney): pass a ZonePool down for this?
347 MoveMap move_map(local_zone());
348 size_t correct_counts = 0;
349 // Accumulate set of shared moves.
350 for (RpoNumber& pred_index : block->predecessors()) {
351 const InstructionBlock* pred = code()->InstructionBlockAt(pred_index);
352 const Instruction* instr = LastInstruction(pred);
353 if (instr->parallel_moves()[0] == nullptr ||
354 instr->parallel_moves()[0]->empty()) {
355 return;
356 }
357 for (const MoveOperands* move : *instr->parallel_moves()[0]) {
358 if (move->IsRedundant()) continue;
359 InstructionOperand src = move->source();
360 InstructionOperand dst = move->destination();
361 MoveKey key = {src, dst};
362 auto res = move_map.insert(std::make_pair(key, 1));
363 if (!res.second) {
364 res.first->second++;
365 if (res.first->second == block->PredecessorCount()) {
366 correct_counts++;
367 }
Emily Bernierd0a1eb72015-03-24 16:35:39 -0400368 }
369 }
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000370 }
Ben Murdoch097c5b22016-05-18 11:27:45 +0100371 if (move_map.empty() || correct_counts == 0) return;
372
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000373 // Find insertion point.
Ben Murdoch097c5b22016-05-18 11:27:45 +0100374 Instruction* instr = code()->instructions()[block->first_instruction_index()];
375
376 if (correct_counts != move_map.size()) {
377 // Moves that are unique to each predecessor won't be pushed to the common
378 // successor.
379 OperandSet conflicting_srcs(local_zone());
380 for (auto iter = move_map.begin(), end = move_map.end(); iter != end;) {
381 auto current = iter;
382 ++iter;
383 if (current->second != block->PredecessorCount()) {
384 InstructionOperand dest = current->first.destination;
385 // Not all the moves in all the gaps are the same. Maybe some are. If
386 // there are such moves, we could move them, but the destination of the
387 // moves staying behind can't appear as a source of a common move,
388 // because the move staying behind will clobber this destination.
389 conflicting_srcs.insert(dest);
390 move_map.erase(current);
391 }
392 }
393
394 bool changed = false;
395 do {
396 // If a common move can't be pushed to the common successor, then its
397 // destination also can't appear as source to any move being pushed.
398 changed = false;
399 for (auto iter = move_map.begin(), end = move_map.end(); iter != end;) {
400 auto current = iter;
401 ++iter;
402 DCHECK_EQ(block->PredecessorCount(), current->second);
403 if (conflicting_srcs.find(current->first.source) !=
404 conflicting_srcs.end()) {
405 conflicting_srcs.insert(current->first.destination);
406 move_map.erase(current);
407 changed = true;
408 }
409 }
410 } while (changed);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000411 }
Ben Murdoch097c5b22016-05-18 11:27:45 +0100412
413 if (move_map.empty()) return;
414
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000415 DCHECK_NOT_NULL(instr);
416 bool gap_initialized = true;
Ben Murdoch097c5b22016-05-18 11:27:45 +0100417 if (instr->parallel_moves()[0] != nullptr &&
418 !instr->parallel_moves()[0]->empty()) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000419 // Will compress after insertion.
420 gap_initialized = false;
421 std::swap(instr->parallel_moves()[0], instr->parallel_moves()[1]);
422 }
423 ParallelMove* moves = instr->GetOrCreateParallelMove(
424 static_cast<Instruction::GapPosition>(0), code_zone());
425 // Delete relevant entries in predecessors and move everything to block.
426 bool first_iteration = true;
427 for (RpoNumber& pred_index : block->predecessors()) {
428 const InstructionBlock* pred = code()->InstructionBlockAt(pred_index);
429 for (MoveOperands* move : *LastInstruction(pred)->parallel_moves()[0]) {
430 if (move->IsRedundant()) continue;
431 MoveKey key = {move->source(), move->destination()};
432 auto it = move_map.find(key);
Ben Murdoch097c5b22016-05-18 11:27:45 +0100433 if (it != move_map.end()) {
434 if (first_iteration) {
435 moves->AddMove(move->source(), move->destination());
436 }
437 move->Eliminate();
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000438 }
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000439 }
440 first_iteration = false;
441 }
442 // Compress.
443 if (!gap_initialized) {
444 CompressMoves(instr->parallel_moves()[0], instr->parallel_moves()[1]);
445 }
Ben Murdoch097c5b22016-05-18 11:27:45 +0100446 CompressBlock(block);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000447}
448
449
450namespace {
451
452bool IsSlot(const InstructionOperand& op) {
453 return op.IsStackSlot() || op.IsDoubleStackSlot();
454}
455
456
457bool LoadCompare(const MoveOperands* a, const MoveOperands* b) {
458 if (!a->source().EqualsCanonicalized(b->source())) {
459 return a->source().CompareCanonicalized(b->source());
460 }
461 if (IsSlot(a->destination()) && !IsSlot(b->destination())) return false;
462 if (!IsSlot(a->destination()) && IsSlot(b->destination())) return true;
463 return a->destination().CompareCanonicalized(b->destination());
464}
465
466} // namespace
467
468
469// Split multiple loads of the same constant or stack slot off into the second
470// slot and keep remaining moves in the first slot.
471void MoveOptimizer::FinalizeMoves(Instruction* instr) {
472 MoveOpVector& loads = local_vector();
473 DCHECK(loads.empty());
474
Ben Murdoch097c5b22016-05-18 11:27:45 +0100475 ParallelMove* parallel_moves = instr->parallel_moves()[0];
476 if (parallel_moves == nullptr) return;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000477 // Find all the loads.
Ben Murdoch097c5b22016-05-18 11:27:45 +0100478 for (MoveOperands* move : *parallel_moves) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000479 if (move->IsRedundant()) continue;
480 if (move->source().IsConstant() || IsSlot(move->source())) {
481 loads.push_back(move);
482 }
483 }
484 if (loads.empty()) return;
485 // Group the loads by source, moving the preferred destination to the
486 // beginning of the group.
487 std::sort(loads.begin(), loads.end(), LoadCompare);
488 MoveOperands* group_begin = nullptr;
489 for (MoveOperands* load : loads) {
490 // New group.
491 if (group_begin == nullptr ||
492 !load->source().EqualsCanonicalized(group_begin->source())) {
493 group_begin = load;
Emily Bernierd0a1eb72015-03-24 16:35:39 -0400494 continue;
495 }
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000496 // Nothing to be gained from splitting here.
497 if (IsSlot(group_begin->destination())) continue;
498 // Insert new move into slot 1.
499 ParallelMove* slot_1 = instr->GetOrCreateParallelMove(
500 static_cast<Instruction::GapPosition>(1), code_zone());
501 slot_1->AddMove(group_begin->destination(), load->destination());
502 load->Eliminate();
Emily Bernierd0a1eb72015-03-24 16:35:39 -0400503 }
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000504 loads.clear();
Emily Bernierd0a1eb72015-03-24 16:35:39 -0400505}
506
507} // namespace compiler
508} // namespace internal
509} // namespace v8