1// Copyright 2011 the V8 project authors. All rights reserved. 2// Redistribution and use in source and binary forms, with or without 3// modification, are permitted provided that the following conditions are 4// met: 5// 6// * Redistributions of source code must retain the above copyright 7// notice, this list of conditions and the following disclaimer. 8// * Redistributions in binary form must reproduce the above 9// copyright notice, this list of conditions and the following 10// disclaimer in the documentation and/or other materials provided 11// with the distribution. 12// * Neither the name of Google Inc. nor the names of its 13// contributors may be used to endorse or promote products derived 14// from this software without specific prior written permission. 15// 16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 28#include "v8.h" 29 30#if defined(V8_TARGET_ARCH_IA32) 31 32#include "ia32/lithium-gap-resolver-ia32.h" 33#include "ia32/lithium-codegen-ia32.h" 34 35namespace v8 { 36namespace internal { 37 38LGapResolver::LGapResolver(LCodeGen* owner) 39 : cgen_(owner), 40 moves_(32), 41 source_uses_(), 42 destination_uses_(), 43 spilled_register_(-1) {} 44 45 46void LGapResolver::Resolve(LParallelMove* parallel_move) { 47 ASSERT(HasBeenReset()); 48 // Build up a worklist of moves. 49 BuildInitialMoveList(parallel_move); 50 51 for (int i = 0; i < moves_.length(); ++i) { 52 LMoveOperands move = moves_[i]; 53 // Skip constants to perform them last. They don't block other moves 54 // and skipping such moves with register destinations keeps those 55 // registers free for the whole algorithm. 56 if (!move.IsEliminated() && !move.source()->IsConstantOperand()) { 57 PerformMove(i); 58 } 59 } 60 61 // Perform the moves with constant sources. 62 for (int i = 0; i < moves_.length(); ++i) { 63 if (!moves_[i].IsEliminated()) { 64 ASSERT(moves_[i].source()->IsConstantOperand()); 65 EmitMove(i); 66 } 67 } 68 69 Finish(); 70 ASSERT(HasBeenReset()); 71} 72 73 74void LGapResolver::BuildInitialMoveList(LParallelMove* parallel_move) { 75 // Perform a linear sweep of the moves to add them to the initial list of 76 // moves to perform, ignoring any move that is redundant (the source is 77 // the same as the destination, the destination is ignored and 78 // unallocated, or the move was already eliminated). 79 const ZoneList<LMoveOperands>* moves = parallel_move->move_operands(); 80 for (int i = 0; i < moves->length(); ++i) { 81 LMoveOperands move = moves->at(i); 82 if (!move.IsRedundant()) AddMove(move); 83 } 84 Verify(); 85} 86 87 88void LGapResolver::PerformMove(int index) { 89 // Each call to this function performs a move and deletes it from the move 90 // graph. We first recursively perform any move blocking this one. We 91 // mark a move as "pending" on entry to PerformMove in order to detect 92 // cycles in the move graph. We use operand swaps to resolve cycles, 93 // which means that a call to PerformMove could change any source operand 94 // in the move graph. 95 96 ASSERT(!moves_[index].IsPending()); 97 ASSERT(!moves_[index].IsRedundant()); 98 99 // Clear this move's destination to indicate a pending move. The actual 100 // destination is saved on the side. 101 ASSERT(moves_[index].source() != NULL); // Or else it will look eliminated. 102 LOperand* destination = moves_[index].destination(); 103 moves_[index].set_destination(NULL); 104 105 // Perform a depth-first traversal of the move graph to resolve 106 // dependencies. Any unperformed, unpending move with a source the same 107 // as this one's destination blocks this one so recursively perform all 108 // such moves. 109 for (int i = 0; i < moves_.length(); ++i) { 110 LMoveOperands other_move = moves_[i]; 111 if (other_move.Blocks(destination) && !other_move.IsPending()) { 112 // Though PerformMove can change any source operand in the move graph, 113 // this call cannot create a blocking move via a swap (this loop does 114 // not miss any). Assume there is a non-blocking move with source A 115 // and this move is blocked on source B and there is a swap of A and 116 // B. Then A and B must be involved in the same cycle (or they would 117 // not be swapped). Since this move's destination is B and there is 118 // only a single incoming edge to an operand, this move must also be 119 // involved in the same cycle. In that case, the blocking move will 120 // be created but will be "pending" when we return from PerformMove. 121 PerformMove(i); 122 } 123 } 124 125 // We are about to resolve this move and don't need it marked as 126 // pending, so restore its destination. 127 moves_[index].set_destination(destination); 128 129 // This move's source may have changed due to swaps to resolve cycles and 130 // so it may now be the last move in the cycle. If so remove it. 131 if (moves_[index].source()->Equals(destination)) { 132 RemoveMove(index); 133 return; 134 } 135 136 // The move may be blocked on a (at most one) pending move, in which case 137 // we have a cycle. Search for such a blocking move and perform a swap to 138 // resolve it. 139 for (int i = 0; i < moves_.length(); ++i) { 140 LMoveOperands other_move = moves_[i]; 141 if (other_move.Blocks(destination)) { 142 ASSERT(other_move.IsPending()); 143 EmitSwap(index); 144 return; 145 } 146 } 147 148 // This move is not blocked. 149 EmitMove(index); 150} 151 152 153void LGapResolver::AddMove(LMoveOperands move) { 154 LOperand* source = move.source(); 155 if (source->IsRegister()) ++source_uses_[source->index()]; 156 157 LOperand* destination = move.destination(); 158 if (destination->IsRegister()) ++destination_uses_[destination->index()]; 159 160 moves_.Add(move); 161} 162 163 164void LGapResolver::RemoveMove(int index) { 165 LOperand* source = moves_[index].source(); 166 if (source->IsRegister()) { 167 --source_uses_[source->index()]; 168 ASSERT(source_uses_[source->index()] >= 0); 169 } 170 171 LOperand* destination = moves_[index].destination(); 172 if (destination->IsRegister()) { 173 --destination_uses_[destination->index()]; 174 ASSERT(destination_uses_[destination->index()] >= 0); 175 } 176 177 moves_[index].Eliminate(); 178} 179 180 181int LGapResolver::CountSourceUses(LOperand* operand) { 182 int count = 0; 183 for (int i = 0; i < moves_.length(); ++i) { 184 if (!moves_[i].IsEliminated() && moves_[i].source()->Equals(operand)) { 185 ++count; 186 } 187 } 188 return count; 189} 190 191 192Register LGapResolver::GetFreeRegisterNot(Register reg) { 193 int skip_index = reg.is(no_reg) ? -1 : Register::ToAllocationIndex(reg); 194 for (int i = 0; i < Register::kNumAllocatableRegisters; ++i) { 195 if (source_uses_[i] == 0 && destination_uses_[i] > 0 && i != skip_index) { 196 return Register::FromAllocationIndex(i); 197 } 198 } 199 return no_reg; 200} 201 202 203bool LGapResolver::HasBeenReset() { 204 if (!moves_.is_empty()) return false; 205 if (spilled_register_ >= 0) return false; 206 207 for (int i = 0; i < Register::kNumAllocatableRegisters; ++i) { 208 if (source_uses_[i] != 0) return false; 209 if (destination_uses_[i] != 0) return false; 210 } 211 return true; 212} 213 214 215void LGapResolver::Verify() { 216#ifdef ENABLE_SLOW_ASSERTS 217 // No operand should be the destination for more than one move. 218 for (int i = 0; i < moves_.length(); ++i) { 219 LOperand* destination = moves_[i].destination(); 220 for (int j = i + 1; j < moves_.length(); ++j) { 221 SLOW_ASSERT(!destination->Equals(moves_[j].destination())); 222 } 223 } 224#endif 225} 226 227 228#define __ ACCESS_MASM(cgen_->masm()) 229 230void LGapResolver::Finish() { 231 if (spilled_register_ >= 0) { 232 __ pop(Register::FromAllocationIndex(spilled_register_)); 233 spilled_register_ = -1; 234 } 235 moves_.Rewind(0); 236} 237 238 239void LGapResolver::EnsureRestored(LOperand* operand) { 240 if (operand->IsRegister() && operand->index() == spilled_register_) { 241 __ pop(Register::FromAllocationIndex(spilled_register_)); 242 spilled_register_ = -1; 243 } 244} 245 246 247Register LGapResolver::EnsureTempRegister() { 248 // 1. We may have already spilled to create a temp register. 249 if (spilled_register_ >= 0) { 250 return Register::FromAllocationIndex(spilled_register_); 251 } 252 253 // 2. We may have a free register that we can use without spilling. 254 Register free = GetFreeRegisterNot(no_reg); 255 if (!free.is(no_reg)) return free; 256 257 // 3. Prefer to spill a register that is not used in any remaining move 258 // because it will not need to be restored until the end. 259 for (int i = 0; i < Register::kNumAllocatableRegisters; ++i) { 260 if (source_uses_[i] == 0 && destination_uses_[i] == 0) { 261 Register scratch = Register::FromAllocationIndex(i); 262 __ push(scratch); 263 spilled_register_ = i; 264 return scratch; 265 } 266 } 267 268 // 4. Use an arbitrary register. Register 0 is as arbitrary as any other. 269 Register scratch = Register::FromAllocationIndex(0); 270 __ push(scratch); 271 spilled_register_ = 0; 272 return scratch; 273} 274 275 276void LGapResolver::EmitMove(int index) { 277 LOperand* source = moves_[index].source(); 278 LOperand* destination = moves_[index].destination(); 279 EnsureRestored(source); 280 EnsureRestored(destination); 281 282 // Dispatch on the source and destination operand kinds. Not all 283 // combinations are possible. 284 if (source->IsRegister()) { 285 ASSERT(destination->IsRegister() || destination->IsStackSlot()); 286 Register src = cgen_->ToRegister(source); 287 Operand dst = cgen_->ToOperand(destination); 288 __ mov(dst, src); 289 290 } else if (source->IsStackSlot()) { 291 ASSERT(destination->IsRegister() || destination->IsStackSlot()); 292 Operand src = cgen_->ToOperand(source); 293 if (destination->IsRegister()) { 294 Register dst = cgen_->ToRegister(destination); 295 __ mov(dst, src); 296 } else { 297 // Spill on demand to use a temporary register for memory-to-memory 298 // moves. 299 Register tmp = EnsureTempRegister(); 300 Operand dst = cgen_->ToOperand(destination); 301 __ mov(tmp, src); 302 __ mov(dst, tmp); 303 } 304 305 } else if (source->IsConstantOperand()) { 306 LConstantOperand* constant_source = LConstantOperand::cast(source); 307 if (destination->IsRegister()) { 308 Register dst = cgen_->ToRegister(destination); 309 if (cgen_->IsInteger32(constant_source)) { 310 __ Set(dst, cgen_->ToInteger32Immediate(constant_source)); 311 } else { 312 __ LoadObject(dst, cgen_->ToHandle(constant_source)); 313 } 314 } else { 315 ASSERT(destination->IsStackSlot()); 316 Operand dst = cgen_->ToOperand(destination); 317 if (cgen_->IsInteger32(constant_source)) { 318 __ Set(dst, cgen_->ToInteger32Immediate(constant_source)); 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 ASSERT(destination->IsDoubleStackSlot()); 333 Operand dst = cgen_->ToOperand(destination); 334 __ movdbl(dst, src); 335 } 336 } else if (source->IsDoubleStackSlot()) { 337 ASSERT(destination->IsDoubleRegister() || 338 destination->IsDoubleStackSlot()); 339 Operand src = cgen_->ToOperand(source); 340 if (destination->IsDoubleRegister()) { 341 XMMRegister dst = cgen_->ToDoubleRegister(destination); 342 __ movdbl(dst, src); 343 } else { 344 // We rely on having xmm0 available as a fixed scratch register. 345 Operand dst = cgen_->ToOperand(destination); 346 __ movdbl(xmm0, src); 347 __ movdbl(dst, xmm0); 348 } 349 350 } else { 351 UNREACHABLE(); 352 } 353 354 RemoveMove(index); 355} 356 357 358void LGapResolver::EmitSwap(int index) { 359 LOperand* source = moves_[index].source(); 360 LOperand* destination = moves_[index].destination(); 361 EnsureRestored(source); 362 EnsureRestored(destination); 363 364 // Dispatch on the source and destination operand kinds. Not all 365 // combinations are possible. 366 if (source->IsRegister() && destination->IsRegister()) { 367 // Register-register. 368 Register src = cgen_->ToRegister(source); 369 Register dst = cgen_->ToRegister(destination); 370 __ xchg(dst, src); 371 372 } else if ((source->IsRegister() && destination->IsStackSlot()) || 373 (source->IsStackSlot() && destination->IsRegister())) { 374 // Register-memory. Use a free register as a temp if possible. Do not 375 // spill on demand because the simple spill implementation cannot avoid 376 // spilling src at this point. 377 Register tmp = GetFreeRegisterNot(no_reg); 378 Register reg = 379 cgen_->ToRegister(source->IsRegister() ? source : destination); 380 Operand mem = 381 cgen_->ToOperand(source->IsRegister() ? destination : source); 382 if (tmp.is(no_reg)) { 383 __ xor_(reg, mem); 384 __ xor_(mem, reg); 385 __ xor_(reg, mem); 386 } else { 387 __ mov(tmp, mem); 388 __ mov(mem, reg); 389 __ mov(reg, tmp); 390 } 391 392 } else if (source->IsStackSlot() && destination->IsStackSlot()) { 393 // Memory-memory. Spill on demand to use a temporary. If there is a 394 // free register after that, use it as a second temporary. 395 Register tmp0 = EnsureTempRegister(); 396 Register tmp1 = GetFreeRegisterNot(tmp0); 397 Operand src = cgen_->ToOperand(source); 398 Operand dst = cgen_->ToOperand(destination); 399 if (tmp1.is(no_reg)) { 400 // Only one temp register available to us. 401 __ mov(tmp0, dst); 402 __ xor_(tmp0, src); 403 __ xor_(src, tmp0); 404 __ xor_(tmp0, src); 405 __ mov(dst, tmp0); 406 } else { 407 __ mov(tmp0, dst); 408 __ mov(tmp1, src); 409 __ mov(dst, tmp1); 410 __ mov(src, tmp0); 411 } 412 } else if (source->IsDoubleRegister() && destination->IsDoubleRegister()) { 413 // XMM register-register swap. We rely on having xmm0 414 // available as a fixed scratch register. 415 XMMRegister src = cgen_->ToDoubleRegister(source); 416 XMMRegister dst = cgen_->ToDoubleRegister(destination); 417 __ movaps(xmm0, src); 418 __ movaps(src, dst); 419 __ movaps(dst, xmm0); 420 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 ASSERT(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 __ movdbl(xmm0, other); 431 __ movdbl(other, reg); 432 __ movdbl(reg, Operand(xmm0)); 433 434 } else if (source->IsDoubleStackSlot() && destination->IsDoubleStackSlot()) { 435 // Double-width memory-to-memory. Spill on demand to use a general 436 // purpose temporary register and also rely on having xmm0 available as 437 // a fixed scratch register. 438 Register tmp = EnsureTempRegister(); 439 Operand src0 = cgen_->ToOperand(source); 440 Operand src1 = cgen_->HighOperand(source); 441 Operand dst0 = cgen_->ToOperand(destination); 442 Operand dst1 = cgen_->HighOperand(destination); 443 __ movdbl(xmm0, dst0); // Save destination in xmm0. 444 __ mov(tmp, src0); // Then use tmp to copy source to destination. 445 __ mov(dst0, tmp); 446 __ mov(tmp, src1); 447 __ mov(dst1, tmp); 448 __ movdbl(src0, xmm0); 449 450 } else { 451 // No other combinations are possible. 452 UNREACHABLE(); 453 } 454 455 // The swap of source and destination has executed a move from source to 456 // destination. 457 RemoveMove(index); 458 459 // Any unperformed (including pending) move with a source of either 460 // this move's source or destination needs to have their source 461 // changed to reflect the state of affairs after the swap. 462 for (int i = 0; i < moves_.length(); ++i) { 463 LMoveOperands other_move = moves_[i]; 464 if (other_move.Blocks(source)) { 465 moves_[i].set_source(destination); 466 } else if (other_move.Blocks(destination)) { 467 moves_[i].set_source(source); 468 } 469 } 470 471 // In addition to swapping the actual uses as sources, we need to update 472 // the use counts. 473 if (source->IsRegister() && destination->IsRegister()) { 474 int temp = source_uses_[source->index()]; 475 source_uses_[source->index()] = source_uses_[destination->index()]; 476 source_uses_[destination->index()] = temp; 477 } else if (source->IsRegister()) { 478 // We don't have use counts for non-register operands like destination. 479 // Compute those counts now. 480 source_uses_[source->index()] = CountSourceUses(source); 481 } else if (destination->IsRegister()) { 482 source_uses_[destination->index()] = CountSourceUses(destination); 483 } 484} 485 486#undef __ 487 488} } // namespace v8::internal 489 490#endif // V8_TARGET_ARCH_IA32 491