blob: be8251cffba3e434c25990eb216cfb3cbd1841b2 [file] [log] [blame]
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001// Copyright 2011 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#if V8_TARGET_ARCH_IA32
6
7#include "src/crankshaft/ia32/lithium-codegen-ia32.h"
8#include "src/crankshaft/ia32/lithium-gap-resolver-ia32.h"
9#include "src/register-configuration.h"
10
11namespace v8 {
12namespace internal {
13
14LGapResolver::LGapResolver(LCodeGen* owner)
15 : cgen_(owner),
16 moves_(32, owner->zone()),
17 source_uses_(),
18 destination_uses_(),
19 spilled_register_(-1) {}
20
21
22void LGapResolver::Resolve(LParallelMove* parallel_move) {
23 DCHECK(HasBeenReset());
24 // Build up a worklist of moves.
25 BuildInitialMoveList(parallel_move);
26
27 for (int i = 0; i < moves_.length(); ++i) {
28 LMoveOperands move = moves_[i];
29 // Skip constants to perform them last. They don't block other moves
30 // and skipping such moves with register destinations keeps those
31 // registers free for the whole algorithm.
32 if (!move.IsEliminated() && !move.source()->IsConstantOperand()) {
33 PerformMove(i);
34 }
35 }
36
37 // Perform the moves with constant sources.
38 for (int i = 0; i < moves_.length(); ++i) {
39 if (!moves_[i].IsEliminated()) {
40 DCHECK(moves_[i].source()->IsConstantOperand());
41 EmitMove(i);
42 }
43 }
44
45 Finish();
46 DCHECK(HasBeenReset());
47}
48
49
50void LGapResolver::BuildInitialMoveList(LParallelMove* parallel_move) {
51 // Perform a linear sweep of the moves to add them to the initial list of
52 // moves to perform, ignoring any move that is redundant (the source is
53 // the same as the destination, the destination is ignored and
54 // unallocated, or the move was already eliminated).
55 const ZoneList<LMoveOperands>* moves = parallel_move->move_operands();
56 for (int i = 0; i < moves->length(); ++i) {
57 LMoveOperands move = moves->at(i);
58 if (!move.IsRedundant()) AddMove(move);
59 }
60 Verify();
61}
62
63
64void LGapResolver::PerformMove(int index) {
65 // Each call to this function performs a move and deletes it from the move
66 // graph. We first recursively perform any move blocking this one. We
67 // mark a move as "pending" on entry to PerformMove in order to detect
68 // cycles in the move graph. We use operand swaps to resolve cycles,
69 // which means that a call to PerformMove could change any source operand
70 // in the move graph.
71
72 DCHECK(!moves_[index].IsPending());
73 DCHECK(!moves_[index].IsRedundant());
74
75 // Clear this move's destination to indicate a pending move. The actual
76 // destination is saved on the side.
77 DCHECK(moves_[index].source() != NULL); // Or else it will look eliminated.
78 LOperand* destination = moves_[index].destination();
79 moves_[index].set_destination(NULL);
80
81 // Perform a depth-first traversal of the move graph to resolve
82 // dependencies. Any unperformed, unpending move with a source the same
83 // as this one's destination blocks this one so recursively perform all
84 // such moves.
85 for (int i = 0; i < moves_.length(); ++i) {
86 LMoveOperands other_move = moves_[i];
87 if (other_move.Blocks(destination) && !other_move.IsPending()) {
88 // Though PerformMove can change any source operand in the move graph,
89 // this call cannot create a blocking move via a swap (this loop does
90 // not miss any). Assume there is a non-blocking move with source A
91 // and this move is blocked on source B and there is a swap of A and
92 // B. Then A and B must be involved in the same cycle (or they would
93 // not be swapped). Since this move's destination is B and there is
94 // only a single incoming edge to an operand, this move must also be
95 // involved in the same cycle. In that case, the blocking move will
96 // be created but will be "pending" when we return from PerformMove.
97 PerformMove(i);
98 }
99 }
100
101 // We are about to resolve this move and don't need it marked as
102 // pending, so restore its destination.
103 moves_[index].set_destination(destination);
104
105 // This move's source may have changed due to swaps to resolve cycles and
106 // so it may now be the last move in the cycle. If so remove it.
107 if (moves_[index].source()->Equals(destination)) {
108 RemoveMove(index);
109 return;
110 }
111
112 // The move may be blocked on a (at most one) pending move, in which case
113 // we have a cycle. Search for such a blocking move and perform a swap to
114 // resolve it.
115 for (int i = 0; i < moves_.length(); ++i) {
116 LMoveOperands other_move = moves_[i];
117 if (other_move.Blocks(destination)) {
118 DCHECK(other_move.IsPending());
119 EmitSwap(index);
120 return;
121 }
122 }
123
124 // This move is not blocked.
125 EmitMove(index);
126}
127
128
129void LGapResolver::AddMove(LMoveOperands move) {
130 LOperand* source = move.source();
131 if (source->IsRegister()) ++source_uses_[source->index()];
132
133 LOperand* destination = move.destination();
134 if (destination->IsRegister()) ++destination_uses_[destination->index()];
135
136 moves_.Add(move, cgen_->zone());
137}
138
139
140void LGapResolver::RemoveMove(int index) {
141 LOperand* source = moves_[index].source();
142 if (source->IsRegister()) {
143 --source_uses_[source->index()];
144 DCHECK(source_uses_[source->index()] >= 0);
145 }
146
147 LOperand* destination = moves_[index].destination();
148 if (destination->IsRegister()) {
149 --destination_uses_[destination->index()];
150 DCHECK(destination_uses_[destination->index()] >= 0);
151 }
152
153 moves_[index].Eliminate();
154}
155
156
157int LGapResolver::CountSourceUses(LOperand* operand) {
158 int count = 0;
159 for (int i = 0; i < moves_.length(); ++i) {
160 if (!moves_[i].IsEliminated() && moves_[i].source()->Equals(operand)) {
161 ++count;
162 }
163 }
164 return count;
165}
166
167
168Register LGapResolver::GetFreeRegisterNot(Register reg) {
169 int skip_index = reg.is(no_reg) ? -1 : reg.code();
Ben Murdoch61f157c2016-09-16 13:49:30 +0100170 const RegisterConfiguration* config = RegisterConfiguration::Crankshaft();
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000171 for (int i = 0; i < config->num_allocatable_general_registers(); ++i) {
172 int code = config->GetAllocatableGeneralCode(i);
173 if (source_uses_[code] == 0 && destination_uses_[code] > 0 &&
174 code != skip_index) {
175 return Register::from_code(code);
176 }
177 }
178 return no_reg;
179}
180
181
182bool LGapResolver::HasBeenReset() {
183 if (!moves_.is_empty()) return false;
184 if (spilled_register_ >= 0) return false;
Ben Murdoch61f157c2016-09-16 13:49:30 +0100185 const RegisterConfiguration* config = RegisterConfiguration::Crankshaft();
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000186 for (int i = 0; i < config->num_allocatable_general_registers(); ++i) {
187 int code = config->GetAllocatableGeneralCode(i);
188 if (source_uses_[code] != 0) return false;
189 if (destination_uses_[code] != 0) return false;
190 }
191 return true;
192}
193
194
195void LGapResolver::Verify() {
196#ifdef ENABLE_SLOW_DCHECKS
197 // No operand should be the destination for more than one move.
198 for (int i = 0; i < moves_.length(); ++i) {
199 LOperand* destination = moves_[i].destination();
200 for (int j = i + 1; j < moves_.length(); ++j) {
201 SLOW_DCHECK(!destination->Equals(moves_[j].destination()));
202 }
203 }
204#endif
205}
206
207
208#define __ ACCESS_MASM(cgen_->masm())
209
210void LGapResolver::Finish() {
211 if (spilled_register_ >= 0) {
212 __ pop(Register::from_code(spilled_register_));
213 spilled_register_ = -1;
214 }
215 moves_.Rewind(0);
216}
217
218
219void LGapResolver::EnsureRestored(LOperand* operand) {
220 if (operand->IsRegister() && operand->index() == spilled_register_) {
221 __ pop(Register::from_code(spilled_register_));
222 spilled_register_ = -1;
223 }
224}
225
226
227Register LGapResolver::EnsureTempRegister() {
228 // 1. We may have already spilled to create a temp register.
229 if (spilled_register_ >= 0) {
230 return Register::from_code(spilled_register_);
231 }
232
233 // 2. We may have a free register that we can use without spilling.
234 Register free = GetFreeRegisterNot(no_reg);
235 if (!free.is(no_reg)) return free;
236
237 // 3. Prefer to spill a register that is not used in any remaining move
238 // because it will not need to be restored until the end.
Ben Murdoch61f157c2016-09-16 13:49:30 +0100239 const RegisterConfiguration* config = RegisterConfiguration::Crankshaft();
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000240 for (int i = 0; i < config->num_allocatable_general_registers(); ++i) {
241 int code = config->GetAllocatableGeneralCode(i);
242 if (source_uses_[code] == 0 && destination_uses_[code] == 0) {
243 Register scratch = Register::from_code(code);
244 __ push(scratch);
245 spilled_register_ = code;
246 return scratch;
247 }
248 }
249
250 // 4. Use an arbitrary register. Register 0 is as arbitrary as any other.
251 spilled_register_ = config->GetAllocatableGeneralCode(0);
252 Register scratch = Register::from_code(spilled_register_);
253 __ push(scratch);
254 return scratch;
255}
256
257
258void LGapResolver::EmitMove(int index) {
259 LOperand* source = moves_[index].source();
260 LOperand* destination = moves_[index].destination();
261 EnsureRestored(source);
262 EnsureRestored(destination);
263
264 // Dispatch on the source and destination operand kinds. Not all
265 // combinations are possible.
266 if (source->IsRegister()) {
267 DCHECK(destination->IsRegister() || destination->IsStackSlot());
268 Register src = cgen_->ToRegister(source);
269 Operand dst = cgen_->ToOperand(destination);
270 __ mov(dst, src);
271
272 } else if (source->IsStackSlot()) {
273 DCHECK(destination->IsRegister() || destination->IsStackSlot());
274 Operand src = cgen_->ToOperand(source);
275 if (destination->IsRegister()) {
276 Register dst = cgen_->ToRegister(destination);
277 __ mov(dst, src);
278 } else {
279 // Spill on demand to use a temporary register for memory-to-memory
280 // moves.
281 Register tmp = EnsureTempRegister();
282 Operand dst = cgen_->ToOperand(destination);
283 __ mov(tmp, src);
284 __ mov(dst, tmp);
285 }
286
287 } else if (source->IsConstantOperand()) {
288 LConstantOperand* constant_source = LConstantOperand::cast(source);
289 if (destination->IsRegister()) {
290 Register dst = cgen_->ToRegister(destination);
291 Representation r = cgen_->IsSmi(constant_source)
292 ? Representation::Smi() : Representation::Integer32();
293 if (cgen_->IsInteger32(constant_source)) {
294 __ Move(dst, cgen_->ToImmediate(constant_source, r));
295 } else {
296 __ LoadObject(dst, cgen_->ToHandle(constant_source));
297 }
298 } else if (destination->IsDoubleRegister()) {
299 double v = cgen_->ToDouble(constant_source);
300 uint64_t int_val = bit_cast<uint64_t, double>(v);
301 int32_t lower = static_cast<int32_t>(int_val);
302 int32_t upper = static_cast<int32_t>(int_val >> kBitsPerInt);
303 XMMRegister dst = cgen_->ToDoubleRegister(destination);
304 if (int_val == 0) {
305 __ xorps(dst, dst);
306 } else {
307 __ push(Immediate(upper));
308 __ push(Immediate(lower));
309 __ movsd(dst, Operand(esp, 0));
310 __ add(esp, Immediate(kDoubleSize));
311 }
312 } else {
313 DCHECK(destination->IsStackSlot());
314 Operand dst = cgen_->ToOperand(destination);
315 Representation r = cgen_->IsSmi(constant_source)
316 ? Representation::Smi() : Representation::Integer32();
317 if (cgen_->IsInteger32(constant_source)) {
318 __ Move(dst, cgen_->ToImmediate(constant_source, r));
319 } else {
320 Register tmp = EnsureTempRegister();
321 __ LoadObject(tmp, cgen_->ToHandle(constant_source));
322 __ mov(dst, tmp);
323 }
324 }
325
326 } else if (source->IsDoubleRegister()) {
327 XMMRegister src = cgen_->ToDoubleRegister(source);
328 if (destination->IsDoubleRegister()) {
329 XMMRegister dst = cgen_->ToDoubleRegister(destination);
330 __ movaps(dst, src);
331 } else {
332 DCHECK(destination->IsDoubleStackSlot());
333 Operand dst = cgen_->ToOperand(destination);
334 __ movsd(dst, src);
335 }
336 } else if (source->IsDoubleStackSlot()) {
337 DCHECK(destination->IsDoubleRegister() ||
338 destination->IsDoubleStackSlot());
339 Operand src = cgen_->ToOperand(source);
340 if (destination->IsDoubleRegister()) {
341 XMMRegister dst = cgen_->ToDoubleRegister(destination);
342 __ movsd(dst, src);
343 } else {
344 // We rely on having xmm0 available as a fixed scratch register.
345 Operand dst = cgen_->ToOperand(destination);
346 __ movsd(xmm0, src);
347 __ movsd(dst, xmm0);
348 }
349 } else {
350 UNREACHABLE();
351 }
352
353 RemoveMove(index);
354}
355
356
357void LGapResolver::EmitSwap(int index) {
358 LOperand* source = moves_[index].source();
359 LOperand* destination = moves_[index].destination();
360 EnsureRestored(source);
361 EnsureRestored(destination);
362
363 // Dispatch on the source and destination operand kinds. Not all
364 // combinations are possible.
365 if (source->IsRegister() && destination->IsRegister()) {
366 // Register-register.
367 Register src = cgen_->ToRegister(source);
368 Register dst = cgen_->ToRegister(destination);
369 __ push(src);
370 __ mov(src, dst);
371 __ pop(dst);
372
373 } else if ((source->IsRegister() && destination->IsStackSlot()) ||
374 (source->IsStackSlot() && destination->IsRegister())) {
375 // Register-memory. Use a free register as a temp if possible. Do not
376 // spill on demand because the simple spill implementation cannot avoid
377 // spilling src at this point.
378 Register tmp = GetFreeRegisterNot(no_reg);
379 Register reg =
380 cgen_->ToRegister(source->IsRegister() ? source : destination);
381 Operand mem =
382 cgen_->ToOperand(source->IsRegister() ? destination : source);
383 if (tmp.is(no_reg)) {
384 __ xor_(reg, mem);
385 __ xor_(mem, reg);
386 __ xor_(reg, mem);
387 } else {
388 __ mov(tmp, mem);
389 __ mov(mem, reg);
390 __ mov(reg, tmp);
391 }
392
393 } else if (source->IsStackSlot() && destination->IsStackSlot()) {
394 // Memory-memory. Spill on demand to use a temporary. If there is a
395 // free register after that, use it as a second temporary.
396 Register tmp0 = EnsureTempRegister();
397 Register tmp1 = GetFreeRegisterNot(tmp0);
398 Operand src = cgen_->ToOperand(source);
399 Operand dst = cgen_->ToOperand(destination);
400 if (tmp1.is(no_reg)) {
401 // Only one temp register available to us.
402 __ mov(tmp0, dst);
403 __ xor_(tmp0, src);
404 __ xor_(src, tmp0);
405 __ xor_(tmp0, src);
406 __ mov(dst, tmp0);
407 } else {
408 __ mov(tmp0, dst);
409 __ mov(tmp1, src);
410 __ mov(dst, tmp1);
411 __ mov(src, tmp0);
412 }
413 } else if (source->IsDoubleRegister() && destination->IsDoubleRegister()) {
414 // XMM register-register swap. We rely on having xmm0
415 // available as a fixed scratch register.
416 XMMRegister src = cgen_->ToDoubleRegister(source);
417 XMMRegister dst = cgen_->ToDoubleRegister(destination);
418 __ movaps(xmm0, src);
419 __ movaps(src, dst);
420 __ movaps(dst, xmm0);
421 } else if (source->IsDoubleRegister() || destination->IsDoubleRegister()) {
422 // XMM register-memory swap. We rely on having xmm0
423 // available as a fixed scratch register.
424 DCHECK(source->IsDoubleStackSlot() || destination->IsDoubleStackSlot());
425 XMMRegister reg = cgen_->ToDoubleRegister(source->IsDoubleRegister()
426 ? source
427 : destination);
428 Operand other =
429 cgen_->ToOperand(source->IsDoubleRegister() ? destination : source);
430 __ movsd(xmm0, other);
431 __ movsd(other, reg);
432 __ movaps(reg, xmm0);
433 } else if (source->IsDoubleStackSlot() && destination->IsDoubleStackSlot()) {
434 // Double-width memory-to-memory. Spill on demand to use a general
435 // purpose temporary register and also rely on having xmm0 available as
436 // a fixed scratch register.
437 Register tmp = EnsureTempRegister();
438 Operand src0 = cgen_->ToOperand(source);
439 Operand src1 = cgen_->HighOperand(source);
440 Operand dst0 = cgen_->ToOperand(destination);
441 Operand dst1 = cgen_->HighOperand(destination);
442 __ movsd(xmm0, dst0); // Save destination in xmm0.
443 __ mov(tmp, src0); // Then use tmp to copy source to destination.
444 __ mov(dst0, tmp);
445 __ mov(tmp, src1);
446 __ mov(dst1, tmp);
447 __ movsd(src0, xmm0);
448
449 } else {
450 // No other combinations are possible.
451 UNREACHABLE();
452 }
453
454 // The swap of source and destination has executed a move from source to
455 // destination.
456 RemoveMove(index);
457
458 // Any unperformed (including pending) move with a source of either
459 // this move's source or destination needs to have their source
460 // changed to reflect the state of affairs after the swap.
461 for (int i = 0; i < moves_.length(); ++i) {
462 LMoveOperands other_move = moves_[i];
463 if (other_move.Blocks(source)) {
464 moves_[i].set_source(destination);
465 } else if (other_move.Blocks(destination)) {
466 moves_[i].set_source(source);
467 }
468 }
469
470 // In addition to swapping the actual uses as sources, we need to update
471 // the use counts.
472 if (source->IsRegister() && destination->IsRegister()) {
473 int temp = source_uses_[source->index()];
474 source_uses_[source->index()] = source_uses_[destination->index()];
475 source_uses_[destination->index()] = temp;
476 } else if (source->IsRegister()) {
477 // We don't have use counts for non-register operands like destination.
478 // Compute those counts now.
479 source_uses_[source->index()] = CountSourceUses(source);
480 } else if (destination->IsRegister()) {
481 source_uses_[destination->index()] = CountSourceUses(destination);
482 }
483}
484
485#undef __
486
487} // namespace internal
488} // namespace v8
489
490#endif // V8_TARGET_ARCH_IA32