1// 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(); 170 const RegisterConfiguration* config = RegisterConfiguration::Crankshaft(); 171 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; 185 const RegisterConfiguration* config = RegisterConfiguration::Crankshaft(); 186 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. 239 const RegisterConfiguration* config = RegisterConfiguration::Crankshaft(); 240 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 491