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. 4 5 #include <algorithm> 6 7 #include "src/base/adapters.h" 8 #include "src/compiler/instruction-selector-impl.h" 9 #include "src/compiler/node-matchers.h" 10 #include "src/compiler/node-properties.h" 11 #include "src/turbo-assembler.h" 12 13 namespace v8 { 14 namespace internal { 15 namespace compiler { 16 17 // Adds X64-specific methods for generating operands. 18 class X64OperandGenerator final : public OperandGenerator { 19 public: 20 explicit X64OperandGenerator(InstructionSelector* selector) 21 : OperandGenerator(selector) {} 22 23 bool CanBeImmediate(Node* node) { 24 switch (node->opcode()) { 25 case IrOpcode::kInt32Constant: 26 case IrOpcode::kRelocatableInt32Constant: 27 return true; 28 case IrOpcode::kInt64Constant: { 29 const int64_t value = OpParameter<int64_t>(node->op()); 30 return std::numeric_limits<int32_t>::min() < value && 31 value <= std::numeric_limits<int32_t>::max(); 32 } 33 case IrOpcode::kNumberConstant: { 34 const double value = OpParameter<double>(node->op()); 35 return bit_cast<int64_t>(value) == 0; 36 } 37 default: 38 return false; 39 } 40 } 41 42 int32_t GetImmediateIntegerValue(Node* node) { 43 DCHECK(CanBeImmediate(node)); 44 if (node->opcode() == IrOpcode::kInt32Constant) { 45 return OpParameter<int32_t>(node->op()); 46 } 47 DCHECK_EQ(IrOpcode::kInt64Constant, node->opcode()); 48 return static_cast<int32_t>(OpParameter<int64_t>(node->op())); 49 } 50 51 bool CanBeMemoryOperand(InstructionCode opcode, Node* node, Node* input, 52 int effect_level) { 53 if (input->opcode() != IrOpcode::kLoad || 54 !selector()->CanCover(node, input)) { 55 return false; 56 } 57 if (effect_level != selector()->GetEffectLevel(input)) { 58 return false; 59 } 60 MachineRepresentation rep = 61 LoadRepresentationOf(input->op()).representation(); 62 switch (opcode) { 63 case kX64And: 64 case kX64Or: 65 case kX64Xor: 66 case kX64Add: 67 case kX64Sub: 68 case kX64Push: 69 case kX64Cmp: 70 case kX64Test: 71 return rep == MachineRepresentation::kWord64 || IsAnyTagged(rep); 72 case kX64And32: 73 case kX64Or32: 74 case kX64Xor32: 75 case kX64Add32: 76 case kX64Sub32: 77 case kX64Cmp32: 78 case kX64Test32: 79 return rep == MachineRepresentation::kWord32; 80 case kX64Cmp16: 81 case kX64Test16: 82 return rep == MachineRepresentation::kWord16; 83 case kX64Cmp8: 84 case kX64Test8: 85 return rep == MachineRepresentation::kWord8; 86 default: 87 break; 88 } 89 return false; 90 } 91 92 AddressingMode GenerateMemoryOperandInputs(Node* index, int scale_exponent, 93 Node* base, Node* displacement, 94 DisplacementMode displacement_mode, 95 InstructionOperand inputs[], 96 size_t* input_count) { 97 AddressingMode mode = kMode_MRI; 98 if (base != nullptr && (index != nullptr || displacement != nullptr)) { 99 if (base->opcode() == IrOpcode::kInt32Constant && 100 OpParameter<int32_t>(base->op()) == 0) { 101 base = nullptr; 102 } else if (base->opcode() == IrOpcode::kInt64Constant && 103 OpParameter<int64_t>(base->op()) == 0) { 104 base = nullptr; 105 } 106 } 107 if (base != nullptr) { 108 inputs[(*input_count)++] = UseRegister(base); 109 if (index != nullptr) { 110 DCHECK(scale_exponent >= 0 && scale_exponent <= 3); 111 inputs[(*input_count)++] = UseRegister(index); 112 if (displacement != nullptr) { 113 inputs[(*input_count)++] = displacement_mode == kNegativeDisplacement 114 ? UseNegatedImmediate(displacement) 115 : UseImmediate(displacement); 116 static const AddressingMode kMRnI_modes[] = {kMode_MR1I, kMode_MR2I, 117 kMode_MR4I, kMode_MR8I}; 118 mode = kMRnI_modes[scale_exponent]; 119 } else { 120 static const AddressingMode kMRn_modes[] = {kMode_MR1, kMode_MR2, 121 kMode_MR4, kMode_MR8}; 122 mode = kMRn_modes[scale_exponent]; 123 } 124 } else { 125 if (displacement == nullptr) { 126 mode = kMode_MR; 127 } else { 128 inputs[(*input_count)++] = displacement_mode == kNegativeDisplacement 129 ? UseNegatedImmediate(displacement) 130 : UseImmediate(displacement); 131 mode = kMode_MRI; 132 } 133 } 134 } else { 135 DCHECK(scale_exponent >= 0 && scale_exponent <= 3); 136 if (displacement != nullptr) { 137 if (index == nullptr) { 138 inputs[(*input_count)++] = UseRegister(displacement); 139 mode = kMode_MR; 140 } else { 141 inputs[(*input_count)++] = UseRegister(index); 142 inputs[(*input_count)++] = displacement_mode == kNegativeDisplacement 143 ? UseNegatedImmediate(displacement) 144 : UseImmediate(displacement); 145 static const AddressingMode kMnI_modes[] = {kMode_MRI, kMode_M2I, 146 kMode_M4I, kMode_M8I}; 147 mode = kMnI_modes[scale_exponent]; 148 } 149 } else { 150 inputs[(*input_count)++] = UseRegister(index); 151 static const AddressingMode kMn_modes[] = {kMode_MR, kMode_MR1, 152 kMode_M4, kMode_M8}; 153 mode = kMn_modes[scale_exponent]; 154 if (mode == kMode_MR1) { 155 // [%r1 + %r1*1] has a smaller encoding than [%r1*2+0] 156 inputs[(*input_count)++] = UseRegister(index); 157 } 158 } 159 } 160 return mode; 161 } 162 163 AddressingMode GetEffectiveAddressMemoryOperand(Node* operand, 164 InstructionOperand inputs[], 165 size_t* input_count) { 166 if (selector()->CanAddressRelativeToRootsRegister()) { 167 LoadMatcher<ExternalReferenceMatcher> m(operand); 168 if (m.index().HasValue() && m.object().HasValue()) { 169 ptrdiff_t const delta = 170 m.index().Value() + 171 TurboAssemblerBase::RootRegisterOffsetForExternalReference( 172 selector()->isolate(), m.object().Value()); 173 if (is_int32(delta)) { 174 inputs[(*input_count)++] = TempImmediate(static_cast<int32_t>(delta)); 175 return kMode_Root; 176 } 177 } 178 } 179 BaseWithIndexAndDisplacement64Matcher m(operand, AddressOption::kAllowAll); 180 DCHECK(m.matches()); 181 if (m.displacement() == nullptr || CanBeImmediate(m.displacement())) { 182 return GenerateMemoryOperandInputs( 183 m.index(), m.scale(), m.base(), m.displacement(), 184 m.displacement_mode(), inputs, input_count); 185 } else if (m.base() == nullptr && 186 m.displacement_mode() == kPositiveDisplacement) { 187 // The displacement cannot be an immediate, but we can use the 188 // displacement as base instead and still benefit from addressing 189 // modes for the scale. 190 return GenerateMemoryOperandInputs(m.index(), m.scale(), m.displacement(), 191 nullptr, m.displacement_mode(), inputs, 192 input_count); 193 } else { 194 inputs[(*input_count)++] = UseRegister(operand->InputAt(0)); 195 inputs[(*input_count)++] = UseRegister(operand->InputAt(1)); 196 return kMode_MR1; 197 } 198 } 199 200 InstructionOperand GetEffectiveIndexOperand(Node* index, 201 AddressingMode* mode) { 202 if (CanBeImmediate(index)) { 203 *mode = kMode_MRI; 204 return UseImmediate(index); 205 } else { 206 *mode = kMode_MR1; 207 return UseUniqueRegister(index); 208 } 209 } 210 211 bool CanBeBetterLeftOperand(Node* node) const { 212 return !selector()->IsLive(node); 213 } 214 }; 215 216 namespace { 217 ArchOpcode GetLoadOpcode(LoadRepresentation load_rep) { 218 ArchOpcode opcode = kArchNop; 219 switch (load_rep.representation()) { 220 case MachineRepresentation::kFloat32: 221 opcode = kX64Movss; 222 break; 223 case MachineRepresentation::kFloat64: 224 opcode = kX64Movsd; 225 break; 226 case MachineRepresentation::kBit: // Fall through. 227 case MachineRepresentation::kWord8: 228 opcode = load_rep.IsSigned() ? kX64Movsxbl : kX64Movzxbl; 229 break; 230 case MachineRepresentation::kWord16: 231 opcode = load_rep.IsSigned() ? kX64Movsxwl : kX64Movzxwl; 232 break; 233 case MachineRepresentation::kWord32: 234 opcode = kX64Movl; 235 break; 236 case MachineRepresentation::kTaggedSigned: // Fall through. 237 case MachineRepresentation::kTaggedPointer: // Fall through. 238 case MachineRepresentation::kTagged: // Fall through. 239 case MachineRepresentation::kWord64: 240 opcode = kX64Movq; 241 break; 242 case MachineRepresentation::kSimd128: // Fall through. 243 opcode = kX64Movdqu; 244 break; 245 case MachineRepresentation::kNone: 246 UNREACHABLE(); 247 break; 248 } 249 return opcode; 250 } 251 252 ArchOpcode GetStoreOpcode(StoreRepresentation store_rep) { 253 switch (store_rep.representation()) { 254 case MachineRepresentation::kFloat32: 255 return kX64Movss; 256 break; 257 case MachineRepresentation::kFloat64: 258 return kX64Movsd; 259 break; 260 case MachineRepresentation::kBit: // Fall through. 261 case MachineRepresentation::kWord8: 262 return kX64Movb; 263 break; 264 case MachineRepresentation::kWord16: 265 return kX64Movw; 266 break; 267 case MachineRepresentation::kWord32: 268 return kX64Movl; 269 break; 270 case MachineRepresentation::kTaggedSigned: // Fall through. 271 case MachineRepresentation::kTaggedPointer: // Fall through. 272 case MachineRepresentation::kTagged: // Fall through. 273 case MachineRepresentation::kWord64: 274 return kX64Movq; 275 break; 276 case MachineRepresentation::kSimd128: // Fall through. 277 return kX64Movdqu; 278 break; 279 case MachineRepresentation::kNone: 280 UNREACHABLE(); 281 } 282 UNREACHABLE(); 283 } 284 285 } // namespace 286 287 void InstructionSelector::VisitStackSlot(Node* node) { 288 StackSlotRepresentation rep = StackSlotRepresentationOf(node->op()); 289 int slot = frame_->AllocateSpillSlot(rep.size()); 290 OperandGenerator g(this); 291 292 Emit(kArchStackSlot, g.DefineAsRegister(node), 293 sequence()->AddImmediate(Constant(slot)), 0, nullptr); 294 } 295 296 void InstructionSelector::VisitDebugAbort(Node* node) { 297 X64OperandGenerator g(this); 298 Emit(kArchDebugAbort, g.NoOutput(), g.UseFixed(node->InputAt(0), rdx)); 299 } 300 301 void InstructionSelector::VisitSpeculationFence(Node* node) { 302 X64OperandGenerator g(this); 303 Emit(kLFence, g.NoOutput()); 304 } 305 306 void InstructionSelector::VisitLoad(Node* node) { 307 LoadRepresentation load_rep = LoadRepresentationOf(node->op()); 308 X64OperandGenerator g(this); 309 310 ArchOpcode opcode = GetLoadOpcode(load_rep); 311 InstructionOperand outputs[1]; 312 outputs[0] = g.DefineAsRegister(node); 313 InstructionOperand inputs[3]; 314 size_t input_count = 0; 315 AddressingMode mode = 316 g.GetEffectiveAddressMemoryOperand(node, inputs, &input_count); 317 InstructionCode code = opcode | AddressingModeField::encode(mode); 318 if (node->opcode() == IrOpcode::kProtectedLoad) { 319 code |= MiscField::encode(kMemoryAccessProtected); 320 } else if (node->opcode() == IrOpcode::kPoisonedLoad) { 321 CHECK_NE(poisoning_level_, PoisoningMitigationLevel::kDontPoison); 322 code |= MiscField::encode(kMemoryAccessPoisoned); 323 } 324 Emit(code, 1, outputs, input_count, inputs); 325 } 326 327 void InstructionSelector::VisitPoisonedLoad(Node* node) { VisitLoad(node); } 328 329 void InstructionSelector::VisitProtectedLoad(Node* node) { VisitLoad(node); } 330 331 void InstructionSelector::VisitStore(Node* node) { 332 X64OperandGenerator g(this); 333 Node* base = node->InputAt(0); 334 Node* index = node->InputAt(1); 335 Node* value = node->InputAt(2); 336 337 StoreRepresentation store_rep = StoreRepresentationOf(node->op()); 338 WriteBarrierKind write_barrier_kind = store_rep.write_barrier_kind(); 339 340 if (write_barrier_kind != kNoWriteBarrier) { 341 DCHECK(CanBeTaggedPointer(store_rep.representation())); 342 AddressingMode addressing_mode; 343 InstructionOperand inputs[] = { 344 g.UseUniqueRegister(base), 345 g.GetEffectiveIndexOperand(index, &addressing_mode), 346 g.UseUniqueRegister(value)}; 347 RecordWriteMode record_write_mode = RecordWriteMode::kValueIsAny; 348 switch (write_barrier_kind) { 349 case kNoWriteBarrier: 350 UNREACHABLE(); 351 break; 352 case kMapWriteBarrier: 353 record_write_mode = RecordWriteMode::kValueIsMap; 354 break; 355 case kPointerWriteBarrier: 356 record_write_mode = RecordWriteMode::kValueIsPointer; 357 break; 358 case kFullWriteBarrier: 359 record_write_mode = RecordWriteMode::kValueIsAny; 360 break; 361 } 362 InstructionOperand temps[] = {g.TempRegister(), g.TempRegister()}; 363 InstructionCode code = kArchStoreWithWriteBarrier; 364 code |= AddressingModeField::encode(addressing_mode); 365 code |= MiscField::encode(static_cast<int>(record_write_mode)); 366 Emit(code, 0, nullptr, arraysize(inputs), inputs, arraysize(temps), temps); 367 } else { 368 ArchOpcode opcode = GetStoreOpcode(store_rep); 369 InstructionOperand inputs[4]; 370 size_t input_count = 0; 371 AddressingMode addressing_mode = 372 g.GetEffectiveAddressMemoryOperand(node, inputs, &input_count); 373 InstructionCode code = 374 opcode | AddressingModeField::encode(addressing_mode); 375 if ((ElementSizeLog2Of(store_rep.representation()) < kPointerSizeLog2) && 376 (value->opcode() == IrOpcode::kTruncateInt64ToInt32) && 377 CanCover(node, value)) { 378 value = value->InputAt(0); 379 } 380 InstructionOperand value_operand = 381 g.CanBeImmediate(value) ? g.UseImmediate(value) : g.UseRegister(value); 382 inputs[input_count++] = value_operand; 383 Emit(code, 0, static_cast<InstructionOperand*>(nullptr), input_count, 384 inputs); 385 } 386 } 387 388 void InstructionSelector::VisitProtectedStore(Node* node) { 389 X64OperandGenerator g(this); 390 Node* value = node->InputAt(2); 391 392 StoreRepresentation store_rep = StoreRepresentationOf(node->op()); 393 394 ArchOpcode opcode = GetStoreOpcode(store_rep); 395 InstructionOperand inputs[4]; 396 size_t input_count = 0; 397 AddressingMode addressing_mode = 398 g.GetEffectiveAddressMemoryOperand(node, inputs, &input_count); 399 InstructionCode code = opcode | AddressingModeField::encode(addressing_mode) | 400 MiscField::encode(kMemoryAccessProtected); 401 InstructionOperand value_operand = 402 g.CanBeImmediate(value) ? g.UseImmediate(value) : g.UseRegister(value); 403 inputs[input_count++] = value_operand; 404 Emit(code, 0, static_cast<InstructionOperand*>(nullptr), input_count, inputs); 405 } 406 407 // Architecture supports unaligned access, therefore VisitLoad is used instead 408 void InstructionSelector::VisitUnalignedLoad(Node* node) { UNREACHABLE(); } 409 410 // Architecture supports unaligned access, therefore VisitStore is used instead 411 void InstructionSelector::VisitUnalignedStore(Node* node) { UNREACHABLE(); } 412 413 // Shared routine for multiple binary operations. 414 static void VisitBinop(InstructionSelector* selector, Node* node, 415 InstructionCode opcode, FlagsContinuation* cont) { 416 X64OperandGenerator g(selector); 417 Int32BinopMatcher m(node); 418 Node* left = m.left().node(); 419 Node* right = m.right().node(); 420 InstructionOperand inputs[8]; 421 size_t input_count = 0; 422 InstructionOperand outputs[1]; 423 size_t output_count = 0; 424 425 // TODO(turbofan): match complex addressing modes. 426 if (left == right) { 427 // If both inputs refer to the same operand, enforce allocating a register 428 // for both of them to ensure that we don't end up generating code like 429 // this: 430 // 431 // mov rax, [rbp-0x10] 432 // add rax, [rbp-0x10] 433 // jo label 434 InstructionOperand const input = g.UseRegister(left); 435 inputs[input_count++] = input; 436 inputs[input_count++] = input; 437 } else if (g.CanBeImmediate(right)) { 438 inputs[input_count++] = g.UseRegister(left); 439 inputs[input_count++] = g.UseImmediate(right); 440 } else { 441 int effect_level = selector->GetEffectLevel(node); 442 if (cont->IsBranch()) { 443 effect_level = selector->GetEffectLevel( 444 cont->true_block()->PredecessorAt(0)->control_input()); 445 } 446 if (node->op()->HasProperty(Operator::kCommutative) && 447 g.CanBeBetterLeftOperand(right) && 448 (!g.CanBeBetterLeftOperand(left) || 449 !g.CanBeMemoryOperand(opcode, node, right, effect_level))) { 450 std::swap(left, right); 451 } 452 if (g.CanBeMemoryOperand(opcode, node, right, effect_level)) { 453 inputs[input_count++] = g.UseRegister(left); 454 AddressingMode addressing_mode = 455 g.GetEffectiveAddressMemoryOperand(right, inputs, &input_count); 456 opcode |= AddressingModeField::encode(addressing_mode); 457 } else { 458 inputs[input_count++] = g.UseRegister(left); 459 inputs[input_count++] = g.Use(right); 460 } 461 } 462 463 if (cont->IsBranch()) { 464 inputs[input_count++] = g.Label(cont->true_block()); 465 inputs[input_count++] = g.Label(cont->false_block()); 466 } 467 468 outputs[output_count++] = g.DefineSameAsFirst(node); 469 470 DCHECK_NE(0u, input_count); 471 DCHECK_EQ(1u, output_count); 472 DCHECK_GE(arraysize(inputs), input_count); 473 DCHECK_GE(arraysize(outputs), output_count); 474 475 selector->EmitWithContinuation(opcode, output_count, outputs, input_count, 476 inputs, cont); 477 } 478 479 480 // Shared routine for multiple binary operations. 481 static void VisitBinop(InstructionSelector* selector, Node* node, 482 InstructionCode opcode) { 483 FlagsContinuation cont; 484 VisitBinop(selector, node, opcode, &cont); 485 } 486 487 488 void InstructionSelector::VisitWord32And(Node* node) { 489 X64OperandGenerator g(this); 490 Uint32BinopMatcher m(node); 491 if (m.right().Is(0xFF)) { 492 Emit(kX64Movzxbl, g.DefineAsRegister(node), g.Use(m.left().node())); 493 } else if (m.right().Is(0xFFFF)) { 494 Emit(kX64Movzxwl, g.DefineAsRegister(node), g.Use(m.left().node())); 495 } else { 496 VisitBinop(this, node, kX64And32); 497 } 498 } 499 500 501 void InstructionSelector::VisitWord64And(Node* node) { 502 VisitBinop(this, node, kX64And); 503 } 504 505 void InstructionSelector::VisitWord32Or(Node* node) { 506 VisitBinop(this, node, kX64Or32); 507 } 508 509 510 void InstructionSelector::VisitWord64Or(Node* node) { 511 VisitBinop(this, node, kX64Or); 512 } 513 514 515 void InstructionSelector::VisitWord32Xor(Node* node) { 516 X64OperandGenerator g(this); 517 Uint32BinopMatcher m(node); 518 if (m.right().Is(-1)) { 519 Emit(kX64Not32, g.DefineSameAsFirst(node), g.UseRegister(m.left().node())); 520 } else { 521 VisitBinop(this, node, kX64Xor32); 522 } 523 } 524 525 526 void InstructionSelector::VisitWord64Xor(Node* node) { 527 X64OperandGenerator g(this); 528 Uint64BinopMatcher m(node); 529 if (m.right().Is(-1)) { 530 Emit(kX64Not, g.DefineSameAsFirst(node), g.UseRegister(m.left().node())); 531 } else { 532 VisitBinop(this, node, kX64Xor); 533 } 534 } 535 536 537 namespace { 538 539 // Shared routine for multiple 32-bit shift operations. 540 // TODO(bmeurer): Merge this with VisitWord64Shift using template magic? 541 void VisitWord32Shift(InstructionSelector* selector, Node* node, 542 ArchOpcode opcode) { 543 X64OperandGenerator g(selector); 544 Int32BinopMatcher m(node); 545 Node* left = m.left().node(); 546 Node* right = m.right().node(); 547 548 if (g.CanBeImmediate(right)) { 549 selector->Emit(opcode, g.DefineSameAsFirst(node), g.UseRegister(left), 550 g.UseImmediate(right)); 551 } else { 552 selector->Emit(opcode, g.DefineSameAsFirst(node), g.UseRegister(left), 553 g.UseFixed(right, rcx)); 554 } 555 } 556 557 558 // Shared routine for multiple 64-bit shift operations. 559 // TODO(bmeurer): Merge this with VisitWord32Shift using template magic? 560 void VisitWord64Shift(InstructionSelector* selector, Node* node, 561 ArchOpcode opcode) { 562 X64OperandGenerator g(selector); 563 Int64BinopMatcher m(node); 564 Node* left = m.left().node(); 565 Node* right = m.right().node(); 566 567 if (g.CanBeImmediate(right)) { 568 selector->Emit(opcode, g.DefineSameAsFirst(node), g.UseRegister(left), 569 g.UseImmediate(right)); 570 } else { 571 if (m.right().IsWord64And()) { 572 Int64BinopMatcher mright(right); 573 if (mright.right().Is(0x3F)) { 574 right = mright.left().node(); 575 } 576 } 577 selector->Emit(opcode, g.DefineSameAsFirst(node), g.UseRegister(left), 578 g.UseFixed(right, rcx)); 579 } 580 } 581 582 // Shared routine for multiple shift operations with continuation. 583 template <typename BinopMatcher, int Bits> 584 bool TryVisitWordShift(InstructionSelector* selector, Node* node, 585 ArchOpcode opcode, FlagsContinuation* cont) { 586 X64OperandGenerator g(selector); 587 BinopMatcher m(node); 588 Node* left = m.left().node(); 589 Node* right = m.right().node(); 590 591 // If the shift count is 0, the flags are not affected. 592 if (!g.CanBeImmediate(right) || 593 (g.GetImmediateIntegerValue(right) & (Bits - 1)) == 0) { 594 return false; 595 } 596 InstructionOperand output = g.DefineSameAsFirst(node); 597 InstructionOperand inputs[2]; 598 inputs[0] = g.UseRegister(left); 599 inputs[1] = g.UseImmediate(right); 600 selector->EmitWithContinuation(opcode, 1, &output, 2, inputs, cont); 601 return true; 602 } 603 604 void EmitLea(InstructionSelector* selector, InstructionCode opcode, 605 Node* result, Node* index, int scale, Node* base, 606 Node* displacement, DisplacementMode displacement_mode) { 607 X64OperandGenerator g(selector); 608 609 InstructionOperand inputs[4]; 610 size_t input_count = 0; 611 AddressingMode mode = 612 g.GenerateMemoryOperandInputs(index, scale, base, displacement, 613 displacement_mode, inputs, &input_count); 614 615 DCHECK_NE(0u, input_count); 616 DCHECK_GE(arraysize(inputs), input_count); 617 618 InstructionOperand outputs[1]; 619 outputs[0] = g.DefineAsRegister(result); 620 621 opcode = AddressingModeField::encode(mode) | opcode; 622 623 selector->Emit(opcode, 1, outputs, input_count, inputs); 624 } 625 626 } // namespace 627 628 629 void InstructionSelector::VisitWord32Shl(Node* node) { 630 Int32ScaleMatcher m(node, true); 631 if (m.matches()) { 632 Node* index = node->InputAt(0); 633 Node* base = m.power_of_two_plus_one() ? index : nullptr; 634 EmitLea(this, kX64Lea32, node, index, m.scale(), base, nullptr, 635 kPositiveDisplacement); 636 return; 637 } 638 VisitWord32Shift(this, node, kX64Shl32); 639 } 640 641 642 void InstructionSelector::VisitWord64Shl(Node* node) { 643 X64OperandGenerator g(this); 644 Int64ScaleMatcher m(node, true); 645 if (m.matches()) { 646 Node* index = node->InputAt(0); 647 Node* base = m.power_of_two_plus_one() ? index : nullptr; 648 EmitLea(this, kX64Lea, node, index, m.scale(), base, nullptr, 649 kPositiveDisplacement); 650 return; 651 } else { 652 Int64BinopMatcher m(node); 653 if ((m.left().IsChangeInt32ToInt64() || 654 m.left().IsChangeUint32ToUint64()) && 655 m.right().IsInRange(32, 63)) { 656 // There's no need to sign/zero-extend to 64-bit if we shift out the upper 657 // 32 bits anyway. 658 Emit(kX64Shl, g.DefineSameAsFirst(node), 659 g.UseRegister(m.left().node()->InputAt(0)), 660 g.UseImmediate(m.right().node())); 661 return; 662 } 663 } 664 VisitWord64Shift(this, node, kX64Shl); 665 } 666 667 668 void InstructionSelector::VisitWord32Shr(Node* node) { 669 VisitWord32Shift(this, node, kX64Shr32); 670 } 671 672 namespace { 673 bool TryMatchLoadWord64AndShiftRight(InstructionSelector* selector, Node* node, 674 InstructionCode opcode) { 675 DCHECK(IrOpcode::kWord64Sar == node->opcode() || 676 IrOpcode::kWord64Shr == node->opcode()); 677 X64OperandGenerator g(selector); 678 Int64BinopMatcher m(node); 679 if (selector->CanCover(m.node(), m.left().node()) && m.left().IsLoad() && 680 m.right().Is(32)) { 681 // Just load and sign-extend the interesting 4 bytes instead. This happens, 682 // for example, when we're loading and untagging SMIs. 683 BaseWithIndexAndDisplacement64Matcher mleft(m.left().node(), 684 AddressOption::kAllowAll); 685 if (mleft.matches() && (mleft.displacement() == nullptr || 686 g.CanBeImmediate(mleft.displacement()))) { 687 size_t input_count = 0; 688 InstructionOperand inputs[3]; 689 AddressingMode mode = g.GetEffectiveAddressMemoryOperand( 690 m.left().node(), inputs, &input_count); 691 if (mleft.displacement() == nullptr) { 692 // Make sure that the addressing mode indicates the presence of an 693 // immediate displacement. It seems that we never use M1 and M2, but we 694 // handle them here anyways. 695 switch (mode) { 696 case kMode_MR: 697 mode = kMode_MRI; 698 break; 699 case kMode_MR1: 700 mode = kMode_MR1I; 701 break; 702 case kMode_MR2: 703 mode = kMode_MR2I; 704 break; 705 case kMode_MR4: 706 mode = kMode_MR4I; 707 break; 708 case kMode_MR8: 709 mode = kMode_MR8I; 710 break; 711 case kMode_M1: 712 mode = kMode_M1I; 713 break; 714 case kMode_M2: 715 mode = kMode_M2I; 716 break; 717 case kMode_M4: 718 mode = kMode_M4I; 719 break; 720 case kMode_M8: 721 mode = kMode_M8I; 722 break; 723 case kMode_None: 724 case kMode_MRI: 725 case kMode_MR1I: 726 case kMode_MR2I: 727 case kMode_MR4I: 728 case kMode_MR8I: 729 case kMode_M1I: 730 case kMode_M2I: 731 case kMode_M4I: 732 case kMode_M8I: 733 case kMode_Root: 734 UNREACHABLE(); 735 } 736 inputs[input_count++] = ImmediateOperand(ImmediateOperand::INLINE, 4); 737 } else { 738 // In the case that the base address was zero, the displacement will be 739 // in a register and replacing it with an immediate is not allowed. This 740 // usually only happens in dead code anyway. 741 if (!inputs[input_count - 1].IsImmediate()) return false; 742 int32_t displacement = g.GetImmediateIntegerValue(mleft.displacement()); 743 inputs[input_count - 1] = 744 ImmediateOperand(ImmediateOperand::INLINE, displacement + 4); 745 } 746 InstructionOperand outputs[] = {g.DefineAsRegister(node)}; 747 InstructionCode code = opcode | AddressingModeField::encode(mode); 748 selector->Emit(code, 1, outputs, input_count, inputs); 749 return true; 750 } 751 } 752 return false; 753 } 754 } // namespace 755 756 void InstructionSelector::VisitWord64Shr(Node* node) { 757 if (TryMatchLoadWord64AndShiftRight(this, node, kX64Movl)) return; 758 VisitWord64Shift(this, node, kX64Shr); 759 } 760 761 void InstructionSelector::VisitWord32Sar(Node* node) { 762 X64OperandGenerator g(this); 763 Int32BinopMatcher m(node); 764 if (CanCover(m.node(), m.left().node()) && m.left().IsWord32Shl()) { 765 Int32BinopMatcher mleft(m.left().node()); 766 if (mleft.right().Is(16) && m.right().Is(16)) { 767 Emit(kX64Movsxwl, g.DefineAsRegister(node), g.Use(mleft.left().node())); 768 return; 769 } else if (mleft.right().Is(24) && m.right().Is(24)) { 770 Emit(kX64Movsxbl, g.DefineAsRegister(node), g.Use(mleft.left().node())); 771 return; 772 } 773 } 774 VisitWord32Shift(this, node, kX64Sar32); 775 } 776 777 void InstructionSelector::VisitWord64Sar(Node* node) { 778 if (TryMatchLoadWord64AndShiftRight(this, node, kX64Movsxlq)) return; 779 VisitWord64Shift(this, node, kX64Sar); 780 } 781 782 783 void InstructionSelector::VisitWord32Ror(Node* node) { 784 VisitWord32Shift(this, node, kX64Ror32); 785 } 786 787 788 void InstructionSelector::VisitWord64Ror(Node* node) { 789 VisitWord64Shift(this, node, kX64Ror); 790 } 791 792 void InstructionSelector::VisitWord32ReverseBits(Node* node) { UNREACHABLE(); } 793 794 795 void InstructionSelector::VisitWord64ReverseBits(Node* node) { UNREACHABLE(); } 796 797 void InstructionSelector::VisitWord64ReverseBytes(Node* node) { 798 X64OperandGenerator g(this); 799 Emit(kX64Bswap, g.DefineSameAsFirst(node), g.UseRegister(node->InputAt(0))); 800 } 801 802 void InstructionSelector::VisitWord32ReverseBytes(Node* node) { 803 X64OperandGenerator g(this); 804 Emit(kX64Bswap32, g.DefineSameAsFirst(node), g.UseRegister(node->InputAt(0))); 805 } 806 807 void InstructionSelector::VisitInt32Add(Node* node) { 808 X64OperandGenerator g(this); 809 810 // Try to match the Add to a leal pattern 811 BaseWithIndexAndDisplacement32Matcher m(node); 812 if (m.matches() && 813 (m.displacement() == nullptr || g.CanBeImmediate(m.displacement()))) { 814 EmitLea(this, kX64Lea32, node, m.index(), m.scale(), m.base(), 815 m.displacement(), m.displacement_mode()); 816 return; 817 } 818 819 // No leal pattern match, use addl 820 VisitBinop(this, node, kX64Add32); 821 } 822 823 824 void InstructionSelector::VisitInt64Add(Node* node) { 825 X64OperandGenerator g(this); 826 827 // Try to match the Add to a leaq pattern 828 BaseWithIndexAndDisplacement64Matcher m(node); 829 if (m.matches() && 830 (m.displacement() == nullptr || g.CanBeImmediate(m.displacement()))) { 831 EmitLea(this, kX64Lea, node, m.index(), m.scale(), m.base(), 832 m.displacement(), m.displacement_mode()); 833 return; 834 } 835 836 // No leal pattern match, use addq 837 VisitBinop(this, node, kX64Add); 838 } 839 840 841 void InstructionSelector::VisitInt64AddWithOverflow(Node* node) { 842 if (Node* ovf = NodeProperties::FindProjection(node, 1)) { 843 FlagsContinuation cont = FlagsContinuation::ForSet(kOverflow, ovf); 844 return VisitBinop(this, node, kX64Add, &cont); 845 } 846 FlagsContinuation cont; 847 VisitBinop(this, node, kX64Add, &cont); 848 } 849 850 void InstructionSelector::VisitInt32Sub(Node* node) { 851 X64OperandGenerator g(this); 852 DCHECK_EQ(node->InputCount(), 2); 853 Node* input1 = node->InputAt(0); 854 Node* input2 = node->InputAt(1); 855 if (input1->opcode() == IrOpcode::kTruncateInt64ToInt32 && 856 g.CanBeImmediate(input2)) { 857 int32_t imm = g.GetImmediateIntegerValue(input2); 858 InstructionOperand int64_input = g.UseRegister(input1->InputAt(0)); 859 if (imm == 0) { 860 // Emit "movl" for subtraction of 0. 861 Emit(kX64Movl, g.DefineAsRegister(node), int64_input); 862 } else { 863 // Omit truncation and turn subtractions of constant values into immediate 864 // "leal" instructions by negating the value. 865 Emit(kX64Lea32 | AddressingModeField::encode(kMode_MRI), 866 g.DefineAsRegister(node), int64_input, g.TempImmediate(-imm)); 867 } 868 return; 869 } 870 871 Int32BinopMatcher m(node); 872 if (m.left().Is(0)) { 873 Emit(kX64Neg32, g.DefineSameAsFirst(node), g.UseRegister(m.right().node())); 874 } else if (m.right().Is(0)) { 875 // TODO(jarin): We should be able to use {EmitIdentity} here 876 // (https://crbug.com/v8/7947). 877 Emit(kArchNop, g.DefineSameAsFirst(node), g.Use(m.left().node())); 878 } else if (m.right().HasValue() && g.CanBeImmediate(m.right().node())) { 879 // Turn subtractions of constant values into immediate "leal" instructions 880 // by negating the value. 881 Emit(kX64Lea32 | AddressingModeField::encode(kMode_MRI), 882 g.DefineAsRegister(node), g.UseRegister(m.left().node()), 883 g.TempImmediate(-m.right().Value())); 884 } else { 885 VisitBinop(this, node, kX64Sub32); 886 } 887 } 888 889 void InstructionSelector::VisitInt64Sub(Node* node) { 890 X64OperandGenerator g(this); 891 Int64BinopMatcher m(node); 892 if (m.left().Is(0)) { 893 Emit(kX64Neg, g.DefineSameAsFirst(node), g.UseRegister(m.right().node())); 894 } else { 895 if (m.right().HasValue() && g.CanBeImmediate(m.right().node())) { 896 // Turn subtractions of constant values into immediate "leaq" instructions 897 // by negating the value. 898 Emit(kX64Lea | AddressingModeField::encode(kMode_MRI), 899 g.DefineAsRegister(node), g.UseRegister(m.left().node()), 900 g.TempImmediate(-static_cast<int32_t>(m.right().Value()))); 901 return; 902 } 903 VisitBinop(this, node, kX64Sub); 904 } 905 } 906 907 908 void InstructionSelector::VisitInt64SubWithOverflow(Node* node) { 909 if (Node* ovf = NodeProperties::FindProjection(node, 1)) { 910 FlagsContinuation cont = FlagsContinuation::ForSet(kOverflow, ovf); 911 return VisitBinop(this, node, kX64Sub, &cont); 912 } 913 FlagsContinuation cont; 914 VisitBinop(this, node, kX64Sub, &cont); 915 } 916 917 918 namespace { 919 920 void VisitMul(InstructionSelector* selector, Node* node, ArchOpcode opcode) { 921 X64OperandGenerator g(selector); 922 Int32BinopMatcher m(node); 923 Node* left = m.left().node(); 924 Node* right = m.right().node(); 925 if (g.CanBeImmediate(right)) { 926 selector->Emit(opcode, g.DefineAsRegister(node), g.Use(left), 927 g.UseImmediate(right)); 928 } else { 929 if (g.CanBeBetterLeftOperand(right)) { 930 std::swap(left, right); 931 } 932 selector->Emit(opcode, g.DefineSameAsFirst(node), g.UseRegister(left), 933 g.Use(right)); 934 } 935 } 936 937 void VisitMulHigh(InstructionSelector* selector, Node* node, 938 ArchOpcode opcode) { 939 X64OperandGenerator g(selector); 940 Node* left = node->InputAt(0); 941 Node* right = node->InputAt(1); 942 if (selector->IsLive(left) && !selector->IsLive(right)) { 943 std::swap(left, right); 944 } 945 InstructionOperand temps[] = {g.TempRegister(rax)}; 946 // TODO(turbofan): We use UseUniqueRegister here to improve register 947 // allocation. 948 selector->Emit(opcode, g.DefineAsFixed(node, rdx), g.UseFixed(left, rax), 949 g.UseUniqueRegister(right), arraysize(temps), temps); 950 } 951 952 953 void VisitDiv(InstructionSelector* selector, Node* node, ArchOpcode opcode) { 954 X64OperandGenerator g(selector); 955 InstructionOperand temps[] = {g.TempRegister(rdx)}; 956 selector->Emit( 957 opcode, g.DefineAsFixed(node, rax), g.UseFixed(node->InputAt(0), rax), 958 g.UseUniqueRegister(node->InputAt(1)), arraysize(temps), temps); 959 } 960 961 962 void VisitMod(InstructionSelector* selector, Node* node, ArchOpcode opcode) { 963 X64OperandGenerator g(selector); 964 InstructionOperand temps[] = {g.TempRegister(rax)}; 965 selector->Emit( 966 opcode, g.DefineAsFixed(node, rdx), g.UseFixed(node->InputAt(0), rax), 967 g.UseUniqueRegister(node->InputAt(1)), arraysize(temps), temps); 968 } 969 970 } // namespace 971 972 973 void InstructionSelector::VisitInt32Mul(Node* node) { 974 Int32ScaleMatcher m(node, true); 975 if (m.matches()) { 976 Node* index = node->InputAt(0); 977 Node* base = m.power_of_two_plus_one() ? index : nullptr; 978 EmitLea(this, kX64Lea32, node, index, m.scale(), base, nullptr, 979 kPositiveDisplacement); 980 return; 981 } 982 VisitMul(this, node, kX64Imul32); 983 } 984 985 void InstructionSelector::VisitInt32MulWithOverflow(Node* node) { 986 // TODO(mvstanton): Use Int32ScaleMatcher somehow. 987 if (Node* ovf = NodeProperties::FindProjection(node, 1)) { 988 FlagsContinuation cont = FlagsContinuation::ForSet(kOverflow, ovf); 989 return VisitBinop(this, node, kX64Imul32, &cont); 990 } 991 FlagsContinuation cont; 992 VisitBinop(this, node, kX64Imul32, &cont); 993 } 994 995 void InstructionSelector::VisitInt64Mul(Node* node) { 996 VisitMul(this, node, kX64Imul); 997 } 998 999 void InstructionSelector::VisitInt32MulHigh(Node* node) { 1000 VisitMulHigh(this, node, kX64ImulHigh32); 1001 } 1002 1003 1004 void InstructionSelector::VisitInt32Div(Node* node) { 1005 VisitDiv(this, node, kX64Idiv32); 1006 } 1007 1008 1009 void InstructionSelector::VisitInt64Div(Node* node) { 1010 VisitDiv(this, node, kX64Idiv); 1011 } 1012 1013 1014 void InstructionSelector::VisitUint32Div(Node* node) { 1015 VisitDiv(this, node, kX64Udiv32); 1016 } 1017 1018 1019 void InstructionSelector::VisitUint64Div(Node* node) { 1020 VisitDiv(this, node, kX64Udiv); 1021 } 1022 1023 1024 void InstructionSelector::VisitInt32Mod(Node* node) { 1025 VisitMod(this, node, kX64Idiv32); 1026 } 1027 1028 1029 void InstructionSelector::VisitInt64Mod(Node* node) { 1030 VisitMod(this, node, kX64Idiv); 1031 } 1032 1033 1034 void InstructionSelector::VisitUint32Mod(Node* node) { 1035 VisitMod(this, node, kX64Udiv32); 1036 } 1037 1038 1039 void InstructionSelector::VisitUint64Mod(Node* node) { 1040 VisitMod(this, node, kX64Udiv); 1041 } 1042 1043 1044 void InstructionSelector::VisitUint32MulHigh(Node* node) { 1045 VisitMulHigh(this, node, kX64UmulHigh32); 1046 } 1047 1048 void InstructionSelector::VisitTryTruncateFloat32ToInt64(Node* node) { 1049 X64OperandGenerator g(this); 1050 InstructionOperand inputs[] = {g.UseRegister(node->InputAt(0))}; 1051 InstructionOperand outputs[2]; 1052 size_t output_count = 0; 1053 outputs[output_count++] = g.DefineAsRegister(node); 1054 1055 Node* success_output = NodeProperties::FindProjection(node, 1); 1056 if (success_output) { 1057 outputs[output_count++] = g.DefineAsRegister(success_output); 1058 } 1059 1060 Emit(kSSEFloat32ToInt64, output_count, outputs, 1, inputs); 1061 } 1062 1063 1064 void InstructionSelector::VisitTryTruncateFloat64ToInt64(Node* node) { 1065 X64OperandGenerator g(this); 1066 InstructionOperand inputs[] = {g.UseRegister(node->InputAt(0))}; 1067 InstructionOperand outputs[2]; 1068 size_t output_count = 0; 1069 outputs[output_count++] = g.DefineAsRegister(node); 1070 1071 Node* success_output = NodeProperties::FindProjection(node, 1); 1072 if (success_output) { 1073 outputs[output_count++] = g.DefineAsRegister(success_output); 1074 } 1075 1076 Emit(kSSEFloat64ToInt64, output_count, outputs, 1, inputs); 1077 } 1078 1079 1080 void InstructionSelector::VisitTryTruncateFloat32ToUint64(Node* node) { 1081 X64OperandGenerator g(this); 1082 InstructionOperand inputs[] = {g.UseRegister(node->InputAt(0))}; 1083 InstructionOperand outputs[2]; 1084 size_t output_count = 0; 1085 outputs[output_count++] = g.DefineAsRegister(node); 1086 1087 Node* success_output = NodeProperties::FindProjection(node, 1); 1088 if (success_output) { 1089 outputs[output_count++] = g.DefineAsRegister(success_output); 1090 } 1091 1092 Emit(kSSEFloat32ToUint64, output_count, outputs, 1, inputs); 1093 } 1094 1095 1096 void InstructionSelector::VisitTryTruncateFloat64ToUint64(Node* node) { 1097 X64OperandGenerator g(this); 1098 InstructionOperand inputs[] = {g.UseRegister(node->InputAt(0))}; 1099 InstructionOperand outputs[2]; 1100 size_t output_count = 0; 1101 outputs[output_count++] = g.DefineAsRegister(node); 1102 1103 Node* success_output = NodeProperties::FindProjection(node, 1); 1104 if (success_output) { 1105 outputs[output_count++] = g.DefineAsRegister(success_output); 1106 } 1107 1108 Emit(kSSEFloat64ToUint64, output_count, outputs, 1, inputs); 1109 } 1110 1111 1112 void InstructionSelector::VisitChangeInt32ToInt64(Node* node) { 1113 X64OperandGenerator g(this); 1114 Node* const value = node->InputAt(0); 1115 if (value->opcode() == IrOpcode::kLoad && CanCover(node, value)) { 1116 LoadRepresentation load_rep = LoadRepresentationOf(value->op()); 1117 MachineRepresentation rep = load_rep.representation(); 1118 InstructionCode opcode = kArchNop; 1119 switch (rep) { 1120 case MachineRepresentation::kBit: // Fall through. 1121 case MachineRepresentation::kWord8: 1122 opcode = load_rep.IsSigned() ? kX64Movsxbq : kX64Movzxbq; 1123 break; 1124 case MachineRepresentation::kWord16: 1125 opcode = load_rep.IsSigned() ? kX64Movsxwq : kX64Movzxwq; 1126 break; 1127 case MachineRepresentation::kWord32: 1128 opcode = load_rep.IsSigned() ? kX64Movsxlq : kX64Movl; 1129 break; 1130 default: 1131 UNREACHABLE(); 1132 return; 1133 } 1134 InstructionOperand outputs[] = {g.DefineAsRegister(node)}; 1135 size_t input_count = 0; 1136 InstructionOperand inputs[3]; 1137 AddressingMode mode = g.GetEffectiveAddressMemoryOperand( 1138 node->InputAt(0), inputs, &input_count); 1139 opcode |= AddressingModeField::encode(mode); 1140 Emit(opcode, 1, outputs, input_count, inputs); 1141 } else { 1142 Emit(kX64Movsxlq, g.DefineAsRegister(node), g.Use(node->InputAt(0))); 1143 } 1144 } 1145 1146 namespace { 1147 1148 bool ZeroExtendsWord32ToWord64(Node* node) { 1149 switch (node->opcode()) { 1150 case IrOpcode::kWord32And: 1151 case IrOpcode::kWord32Or: 1152 case IrOpcode::kWord32Xor: 1153 case IrOpcode::kWord32Shl: 1154 case IrOpcode::kWord32Shr: 1155 case IrOpcode::kWord32Sar: 1156 case IrOpcode::kWord32Ror: 1157 case IrOpcode::kWord32Equal: 1158 case IrOpcode::kInt32Add: 1159 case IrOpcode::kInt32Sub: 1160 case IrOpcode::kInt32Mul: 1161 case IrOpcode::kInt32MulHigh: 1162 case IrOpcode::kInt32Div: 1163 case IrOpcode::kInt32LessThan: 1164 case IrOpcode::kInt32LessThanOrEqual: 1165 case IrOpcode::kInt32Mod: 1166 case IrOpcode::kUint32Div: 1167 case IrOpcode::kUint32LessThan: 1168 case IrOpcode::kUint32LessThanOrEqual: 1169 case IrOpcode::kUint32Mod: 1170 case IrOpcode::kUint32MulHigh: 1171 // These 32-bit operations implicitly zero-extend to 64-bit on x64, so the 1172 // zero-extension is a no-op. 1173 return true; 1174 case IrOpcode::kProjection: { 1175 Node* const value = node->InputAt(0); 1176 switch (value->opcode()) { 1177 case IrOpcode::kInt32AddWithOverflow: 1178 case IrOpcode::kInt32SubWithOverflow: 1179 case IrOpcode::kInt32MulWithOverflow: 1180 return true; 1181 default: 1182 return false; 1183 } 1184 } 1185 case IrOpcode::kLoad: 1186 case IrOpcode::kPoisonedLoad: { 1187 // The movzxbl/movsxbl/movzxwl/movsxwl/movl operations implicitly 1188 // zero-extend to 64-bit on x64, so the zero-extension is a no-op. 1189 LoadRepresentation load_rep = LoadRepresentationOf(node->op()); 1190 switch (load_rep.representation()) { 1191 case MachineRepresentation::kWord8: 1192 case MachineRepresentation::kWord16: 1193 case MachineRepresentation::kWord32: 1194 return true; 1195 default: 1196 return false; 1197 } 1198 } 1199 default: 1200 return false; 1201 } 1202 } 1203 1204 } // namespace 1205 1206 void InstructionSelector::VisitChangeUint32ToUint64(Node* node) { 1207 X64OperandGenerator g(this); 1208 Node* value = node->InputAt(0); 1209 if (ZeroExtendsWord32ToWord64(value)) { 1210 // These 32-bit operations implicitly zero-extend to 64-bit on x64, so the 1211 // zero-extension is a no-op. 1212 return EmitIdentity(node); 1213 } 1214 Emit(kX64Movl, g.DefineAsRegister(node), g.Use(value)); 1215 } 1216 1217 1218 namespace { 1219 1220 void VisitRO(InstructionSelector* selector, Node* node, 1221 InstructionCode opcode) { 1222 X64OperandGenerator g(selector); 1223 selector->Emit(opcode, g.DefineAsRegister(node), g.Use(node->InputAt(0))); 1224 } 1225 1226 1227 void VisitRR(InstructionSelector* selector, Node* node, 1228 InstructionCode opcode) { 1229 X64OperandGenerator g(selector); 1230 selector->Emit(opcode, g.DefineAsRegister(node), 1231 g.UseRegister(node->InputAt(0))); 1232 } 1233 1234 void VisitRRO(InstructionSelector* selector, Node* node, 1235 InstructionCode opcode) { 1236 X64OperandGenerator g(selector); 1237 selector->Emit(opcode, g.DefineSameAsFirst(node), 1238 g.UseRegister(node->InputAt(0)), g.Use(node->InputAt(1))); 1239 } 1240 1241 void VisitFloatBinop(InstructionSelector* selector, Node* node, 1242 ArchOpcode avx_opcode, ArchOpcode sse_opcode) { 1243 X64OperandGenerator g(selector); 1244 InstructionOperand operand0 = g.UseRegister(node->InputAt(0)); 1245 InstructionOperand operand1 = g.Use(node->InputAt(1)); 1246 if (selector->IsSupported(AVX)) { 1247 selector->Emit(avx_opcode, g.DefineAsRegister(node), operand0, operand1); 1248 } else { 1249 selector->Emit(sse_opcode, g.DefineSameAsFirst(node), operand0, operand1); 1250 } 1251 } 1252 1253 1254 void VisitFloatUnop(InstructionSelector* selector, Node* node, Node* input, 1255 ArchOpcode avx_opcode, ArchOpcode sse_opcode) { 1256 X64OperandGenerator g(selector); 1257 if (selector->IsSupported(AVX)) { 1258 selector->Emit(avx_opcode, g.DefineAsRegister(node), g.Use(input)); 1259 } else { 1260 selector->Emit(sse_opcode, g.DefineSameAsFirst(node), g.UseRegister(input)); 1261 } 1262 } 1263 1264 } // namespace 1265 1266 #define RO_OP_LIST(V) \ 1267 V(Word64Clz, kX64Lzcnt) \ 1268 V(Word32Clz, kX64Lzcnt32) \ 1269 V(Word64Ctz, kX64Tzcnt) \ 1270 V(Word32Ctz, kX64Tzcnt32) \ 1271 V(Word64Popcnt, kX64Popcnt) \ 1272 V(Word32Popcnt, kX64Popcnt32) \ 1273 V(Float64Sqrt, kSSEFloat64Sqrt) \ 1274 V(Float32Sqrt, kSSEFloat32Sqrt) \ 1275 V(ChangeFloat64ToInt32, kSSEFloat64ToInt32) \ 1276 V(ChangeFloat64ToUint32, kSSEFloat64ToUint32 | MiscField::encode(1)) \ 1277 V(TruncateFloat64ToUint32, kSSEFloat64ToUint32 | MiscField::encode(0)) \ 1278 V(ChangeFloat64ToUint64, kSSEFloat64ToUint64) \ 1279 V(TruncateFloat64ToFloat32, kSSEFloat64ToFloat32) \ 1280 V(ChangeFloat32ToFloat64, kSSEFloat32ToFloat64) \ 1281 V(TruncateFloat32ToInt32, kSSEFloat32ToInt32) \ 1282 V(TruncateFloat32ToUint32, kSSEFloat32ToUint32) \ 1283 V(ChangeInt32ToFloat64, kSSEInt32ToFloat64) \ 1284 V(ChangeUint32ToFloat64, kSSEUint32ToFloat64) \ 1285 V(RoundFloat64ToInt32, kSSEFloat64ToInt32) \ 1286 V(RoundInt32ToFloat32, kSSEInt32ToFloat32) \ 1287 V(RoundInt64ToFloat32, kSSEInt64ToFloat32) \ 1288 V(RoundUint64ToFloat32, kSSEUint64ToFloat32) \ 1289 V(RoundInt64ToFloat64, kSSEInt64ToFloat64) \ 1290 V(RoundUint64ToFloat64, kSSEUint64ToFloat64) \ 1291 V(RoundUint32ToFloat32, kSSEUint32ToFloat32) \ 1292 V(BitcastFloat32ToInt32, kX64BitcastFI) \ 1293 V(BitcastFloat64ToInt64, kX64BitcastDL) \ 1294 V(BitcastInt32ToFloat32, kX64BitcastIF) \ 1295 V(BitcastInt64ToFloat64, kX64BitcastLD) \ 1296 V(Float64ExtractLowWord32, kSSEFloat64ExtractLowWord32) \ 1297 V(Float64ExtractHighWord32, kSSEFloat64ExtractHighWord32) \ 1298 V(SignExtendWord8ToInt32, kX64Movsxbl) \ 1299 V(SignExtendWord16ToInt32, kX64Movsxwl) \ 1300 V(SignExtendWord8ToInt64, kX64Movsxbq) \ 1301 V(SignExtendWord16ToInt64, kX64Movsxwq) \ 1302 V(SignExtendWord32ToInt64, kX64Movsxlq) 1303 1304 #define RR_OP_LIST(V) \ 1305 V(Float32RoundDown, kSSEFloat32Round | MiscField::encode(kRoundDown)) \ 1306 V(Float64RoundDown, kSSEFloat64Round | MiscField::encode(kRoundDown)) \ 1307 V(Float32RoundUp, kSSEFloat32Round | MiscField::encode(kRoundUp)) \ 1308 V(Float64RoundUp, kSSEFloat64Round | MiscField::encode(kRoundUp)) \ 1309 V(Float32RoundTruncate, kSSEFloat32Round | MiscField::encode(kRoundToZero)) \ 1310 V(Float64RoundTruncate, kSSEFloat64Round | MiscField::encode(kRoundToZero)) \ 1311 V(Float32RoundTiesEven, \ 1312 kSSEFloat32Round | MiscField::encode(kRoundToNearest)) \ 1313 V(Float64RoundTiesEven, kSSEFloat64Round | MiscField::encode(kRoundToNearest)) 1314 1315 #define RO_VISITOR(Name, opcode) \ 1316 void InstructionSelector::Visit##Name(Node* node) { \ 1317 VisitRO(this, node, opcode); \ 1318 } 1319 RO_OP_LIST(RO_VISITOR) 1320 #undef RO_VISITOR 1321 #undef RO_OP_LIST 1322 1323 #define RR_VISITOR(Name, opcode) \ 1324 void InstructionSelector::Visit##Name(Node* node) { \ 1325 VisitRR(this, node, opcode); \ 1326 } 1327 RR_OP_LIST(RR_VISITOR) 1328 #undef RR_VISITOR 1329 #undef RR_OP_LIST 1330 1331 void InstructionSelector::VisitTruncateFloat64ToWord32(Node* node) { 1332 VisitRR(this, node, kArchTruncateDoubleToI); 1333 } 1334 1335 void InstructionSelector::VisitTruncateInt64ToInt32(Node* node) { 1336 X64OperandGenerator g(this); 1337 Node* value = node->InputAt(0); 1338 if (CanCover(node, value)) { 1339 switch (value->opcode()) { 1340 case IrOpcode::kWord64Sar: 1341 case IrOpcode::kWord64Shr: { 1342 Int64BinopMatcher m(value); 1343 if (m.right().Is(32)) { 1344 if (TryMatchLoadWord64AndShiftRight(this, value, kX64Movl)) { 1345 return EmitIdentity(node); 1346 } 1347 Emit(kX64Shr, g.DefineSameAsFirst(node), 1348 g.UseRegister(m.left().node()), g.TempImmediate(32)); 1349 return; 1350 } 1351 break; 1352 } 1353 default: 1354 break; 1355 } 1356 } 1357 Emit(kX64Movl, g.DefineAsRegister(node), g.Use(value)); 1358 } 1359 1360 void InstructionSelector::VisitFloat32Add(Node* node) { 1361 VisitFloatBinop(this, node, kAVXFloat32Add, kSSEFloat32Add); 1362 } 1363 1364 1365 void InstructionSelector::VisitFloat32Sub(Node* node) { 1366 VisitFloatBinop(this, node, kAVXFloat32Sub, kSSEFloat32Sub); 1367 } 1368 1369 void InstructionSelector::VisitFloat32Mul(Node* node) { 1370 VisitFloatBinop(this, node, kAVXFloat32Mul, kSSEFloat32Mul); 1371 } 1372 1373 1374 void InstructionSelector::VisitFloat32Div(Node* node) { 1375 VisitFloatBinop(this, node, kAVXFloat32Div, kSSEFloat32Div); 1376 } 1377 1378 1379 void InstructionSelector::VisitFloat32Abs(Node* node) { 1380 VisitFloatUnop(this, node, node->InputAt(0), kAVXFloat32Abs, kSSEFloat32Abs); 1381 } 1382 1383 1384 void InstructionSelector::VisitFloat32Max(Node* node) { 1385 VisitRRO(this, node, kSSEFloat32Max); 1386 } 1387 1388 void InstructionSelector::VisitFloat32Min(Node* node) { 1389 VisitRRO(this, node, kSSEFloat32Min); 1390 } 1391 1392 void InstructionSelector::VisitFloat64Add(Node* node) { 1393 VisitFloatBinop(this, node, kAVXFloat64Add, kSSEFloat64Add); 1394 } 1395 1396 1397 void InstructionSelector::VisitFloat64Sub(Node* node) { 1398 VisitFloatBinop(this, node, kAVXFloat64Sub, kSSEFloat64Sub); 1399 } 1400 1401 void InstructionSelector::VisitFloat64Mul(Node* node) { 1402 VisitFloatBinop(this, node, kAVXFloat64Mul, kSSEFloat64Mul); 1403 } 1404 1405 1406 void InstructionSelector::VisitFloat64Div(Node* node) { 1407 VisitFloatBinop(this, node, kAVXFloat64Div, kSSEFloat64Div); 1408 } 1409 1410 1411 void InstructionSelector::VisitFloat64Mod(Node* node) { 1412 X64OperandGenerator g(this); 1413 InstructionOperand temps[] = {g.TempRegister(rax)}; 1414 Emit(kSSEFloat64Mod, g.DefineSameAsFirst(node), 1415 g.UseRegister(node->InputAt(0)), g.UseRegister(node->InputAt(1)), 1, 1416 temps); 1417 } 1418 1419 1420 void InstructionSelector::VisitFloat64Max(Node* node) { 1421 VisitRRO(this, node, kSSEFloat64Max); 1422 } 1423 1424 1425 void InstructionSelector::VisitFloat64Min(Node* node) { 1426 VisitRRO(this, node, kSSEFloat64Min); 1427 } 1428 1429 1430 void InstructionSelector::VisitFloat64Abs(Node* node) { 1431 VisitFloatUnop(this, node, node->InputAt(0), kAVXFloat64Abs, kSSEFloat64Abs); 1432 } 1433 1434 1435 void InstructionSelector::VisitFloat64RoundTiesAway(Node* node) { 1436 UNREACHABLE(); 1437 } 1438 1439 1440 void InstructionSelector::VisitFloat32Neg(Node* node) { 1441 VisitFloatUnop(this, node, node->InputAt(0), kAVXFloat32Neg, kSSEFloat32Neg); 1442 } 1443 1444 void InstructionSelector::VisitFloat64Neg(Node* node) { 1445 VisitFloatUnop(this, node, node->InputAt(0), kAVXFloat64Neg, kSSEFloat64Neg); 1446 } 1447 1448 void InstructionSelector::VisitFloat64Ieee754Binop(Node* node, 1449 InstructionCode opcode) { 1450 X64OperandGenerator g(this); 1451 Emit(opcode, g.DefineAsFixed(node, xmm0), g.UseFixed(node->InputAt(0), xmm0), 1452 g.UseFixed(node->InputAt(1), xmm1)) 1453 ->MarkAsCall(); 1454 } 1455 1456 void InstructionSelector::VisitFloat64Ieee754Unop(Node* node, 1457 InstructionCode opcode) { 1458 X64OperandGenerator g(this); 1459 Emit(opcode, g.DefineAsFixed(node, xmm0), g.UseFixed(node->InputAt(0), xmm0)) 1460 ->MarkAsCall(); 1461 } 1462 1463 void InstructionSelector::EmitPrepareArguments( 1464 ZoneVector<PushParameter>* arguments, const CallDescriptor* call_descriptor, 1465 Node* node) { 1466 X64OperandGenerator g(this); 1467 1468 // Prepare for C function call. 1469 if (call_descriptor->IsCFunctionCall()) { 1470 Emit(kArchPrepareCallCFunction | MiscField::encode(static_cast<int>( 1471 call_descriptor->ParameterCount())), 1472 0, nullptr, 0, nullptr); 1473 1474 // Poke any stack arguments. 1475 for (size_t n = 0; n < arguments->size(); ++n) { 1476 PushParameter input = (*arguments)[n]; 1477 if (input.node) { 1478 int slot = static_cast<int>(n); 1479 InstructionOperand value = g.CanBeImmediate(input.node) 1480 ? g.UseImmediate(input.node) 1481 : g.UseRegister(input.node); 1482 Emit(kX64Poke | MiscField::encode(slot), g.NoOutput(), value); 1483 } 1484 } 1485 } else { 1486 // Push any stack arguments. 1487 int effect_level = GetEffectLevel(node); 1488 for (PushParameter input : base::Reversed(*arguments)) { 1489 // Skip any alignment holes in pushed nodes. We may have one in case of a 1490 // Simd128 stack argument. 1491 if (input.node == nullptr) continue; 1492 if (g.CanBeImmediate(input.node)) { 1493 Emit(kX64Push, g.NoOutput(), g.UseImmediate(input.node)); 1494 } else if (IsSupported(ATOM) || 1495 sequence()->IsFP(GetVirtualRegister(input.node))) { 1496 // TODO(titzer): X64Push cannot handle stack->stack double moves 1497 // because there is no way to encode fixed double slots. 1498 Emit(kX64Push, g.NoOutput(), g.UseRegister(input.node)); 1499 } else if (g.CanBeMemoryOperand(kX64Push, node, input.node, 1500 effect_level)) { 1501 InstructionOperand outputs[1]; 1502 InstructionOperand inputs[4]; 1503 size_t input_count = 0; 1504 InstructionCode opcode = kX64Push; 1505 AddressingMode mode = g.GetEffectiveAddressMemoryOperand( 1506 input.node, inputs, &input_count); 1507 opcode |= AddressingModeField::encode(mode); 1508 Emit(opcode, 0, outputs, input_count, inputs); 1509 } else { 1510 Emit(kX64Push, g.NoOutput(), g.Use(input.node)); 1511 } 1512 } 1513 } 1514 } 1515 1516 void InstructionSelector::EmitPrepareResults( 1517 ZoneVector<PushParameter>* results, const CallDescriptor* call_descriptor, 1518 Node* node) { 1519 X64OperandGenerator g(this); 1520 1521 int reverse_slot = 0; 1522 for (PushParameter output : *results) { 1523 if (!output.location.IsCallerFrameSlot()) continue; 1524 reverse_slot += output.location.GetSizeInPointers(); 1525 // Skip any alignment holes in nodes. 1526 if (output.node == nullptr) continue; 1527 DCHECK(!call_descriptor->IsCFunctionCall()); 1528 if (output.location.GetType() == MachineType::Float32()) { 1529 MarkAsFloat32(output.node); 1530 } else if (output.location.GetType() == MachineType::Float64()) { 1531 MarkAsFloat64(output.node); 1532 } 1533 InstructionOperand result = g.DefineAsRegister(output.node); 1534 InstructionOperand slot = g.UseImmediate(reverse_slot); 1535 Emit(kX64Peek, 1, &result, 1, &slot); 1536 } 1537 } 1538 1539 bool InstructionSelector::IsTailCallAddressImmediate() { return true; } 1540 1541 int InstructionSelector::GetTempsCountForTailCallFromJSFunction() { return 3; } 1542 1543 namespace { 1544 1545 void VisitCompareWithMemoryOperand(InstructionSelector* selector, 1546 InstructionCode opcode, Node* left, 1547 InstructionOperand right, 1548 FlagsContinuation* cont) { 1549 DCHECK_EQ(IrOpcode::kLoad, left->opcode()); 1550 X64OperandGenerator g(selector); 1551 size_t input_count = 0; 1552 InstructionOperand inputs[4]; 1553 AddressingMode addressing_mode = 1554 g.GetEffectiveAddressMemoryOperand(left, inputs, &input_count); 1555 opcode |= AddressingModeField::encode(addressing_mode); 1556 inputs[input_count++] = right; 1557 1558 selector->EmitWithContinuation(opcode, 0, nullptr, input_count, inputs, cont); 1559 } 1560 1561 // Shared routine for multiple compare operations. 1562 void VisitCompare(InstructionSelector* selector, InstructionCode opcode, 1563 InstructionOperand left, InstructionOperand right, 1564 FlagsContinuation* cont) { 1565 selector->EmitWithContinuation(opcode, left, right, cont); 1566 } 1567 1568 1569 // Shared routine for multiple compare operations. 1570 void VisitCompare(InstructionSelector* selector, InstructionCode opcode, 1571 Node* left, Node* right, FlagsContinuation* cont, 1572 bool commutative) { 1573 X64OperandGenerator g(selector); 1574 if (commutative && g.CanBeBetterLeftOperand(right)) { 1575 std::swap(left, right); 1576 } 1577 VisitCompare(selector, opcode, g.UseRegister(left), g.Use(right), cont); 1578 } 1579 1580 MachineType MachineTypeForNarrow(Node* node, Node* hint_node) { 1581 if (hint_node->opcode() == IrOpcode::kLoad) { 1582 MachineType hint = LoadRepresentationOf(hint_node->op()); 1583 if (node->opcode() == IrOpcode::kInt32Constant || 1584 node->opcode() == IrOpcode::kInt64Constant) { 1585 int64_t constant = node->opcode() == IrOpcode::kInt32Constant 1586 ? OpParameter<int32_t>(node->op()) 1587 : OpParameter<int64_t>(node->op()); 1588 if (hint == MachineType::Int8()) { 1589 if (constant >= std::numeric_limits<int8_t>::min() && 1590 constant <= std::numeric_limits<int8_t>::max()) { 1591 return hint; 1592 } 1593 } else if (hint == MachineType::Uint8()) { 1594 if (constant >= std::numeric_limits<uint8_t>::min() && 1595 constant <= std::numeric_limits<uint8_t>::max()) { 1596 return hint; 1597 } 1598 } else if (hint == MachineType::Int16()) { 1599 if (constant >= std::numeric_limits<int16_t>::min() && 1600 constant <= std::numeric_limits<int16_t>::max()) { 1601 return hint; 1602 } 1603 } else if (hint == MachineType::Uint16()) { 1604 if (constant >= std::numeric_limits<uint16_t>::min() && 1605 constant <= std::numeric_limits<uint16_t>::max()) { 1606 return hint; 1607 } 1608 } else if (hint == MachineType::Int32()) { 1609 return hint; 1610 } else if (hint == MachineType::Uint32()) { 1611 if (constant >= 0) return hint; 1612 } 1613 } 1614 } 1615 return node->opcode() == IrOpcode::kLoad ? LoadRepresentationOf(node->op()) 1616 : MachineType::None(); 1617 } 1618 1619 // Tries to match the size of the given opcode to that of the operands, if 1620 // possible. 1621 InstructionCode TryNarrowOpcodeSize(InstructionCode opcode, Node* left, 1622 Node* right, FlagsContinuation* cont) { 1623 // TODO(epertoso): we can probably get some size information out phi nodes. 1624 // If the load representations don't match, both operands will be 1625 // zero/sign-extended to 32bit. 1626 MachineType left_type = MachineTypeForNarrow(left, right); 1627 MachineType right_type = MachineTypeForNarrow(right, left); 1628 if (left_type == right_type) { 1629 switch (left_type.representation()) { 1630 case MachineRepresentation::kBit: 1631 case MachineRepresentation::kWord8: { 1632 if (opcode == kX64Test32) return kX64Test8; 1633 if (opcode == kX64Cmp32) { 1634 if (left_type.semantic() == MachineSemantic::kUint32) { 1635 cont->OverwriteUnsignedIfSigned(); 1636 } else { 1637 CHECK_EQ(MachineSemantic::kInt32, left_type.semantic()); 1638 } 1639 return kX64Cmp8; 1640 } 1641 break; 1642 } 1643 case MachineRepresentation::kWord16: 1644 if (opcode == kX64Test32) return kX64Test16; 1645 if (opcode == kX64Cmp32) { 1646 if (left_type.semantic() == MachineSemantic::kUint32) { 1647 cont->OverwriteUnsignedIfSigned(); 1648 } else { 1649 CHECK_EQ(MachineSemantic::kInt32, left_type.semantic()); 1650 } 1651 return kX64Cmp16; 1652 } 1653 break; 1654 default: 1655 break; 1656 } 1657 } 1658 return opcode; 1659 } 1660 1661 // Shared routine for multiple word compare operations. 1662 void VisitWordCompare(InstructionSelector* selector, Node* node, 1663 InstructionCode opcode, FlagsContinuation* cont) { 1664 X64OperandGenerator g(selector); 1665 Node* left = node->InputAt(0); 1666 Node* right = node->InputAt(1); 1667 1668 opcode = TryNarrowOpcodeSize(opcode, left, right, cont); 1669 1670 // If one of the two inputs is an immediate, make sure it's on the right, or 1671 // if one of the two inputs is a memory operand, make sure it's on the left. 1672 int effect_level = selector->GetEffectLevel(node); 1673 if (cont->IsBranch()) { 1674 effect_level = selector->GetEffectLevel( 1675 cont->true_block()->PredecessorAt(0)->control_input()); 1676 } 1677 1678 if ((!g.CanBeImmediate(right) && g.CanBeImmediate(left)) || 1679 (g.CanBeMemoryOperand(opcode, node, right, effect_level) && 1680 !g.CanBeMemoryOperand(opcode, node, left, effect_level))) { 1681 if (!node->op()->HasProperty(Operator::kCommutative)) cont->Commute(); 1682 std::swap(left, right); 1683 } 1684 1685 // Match immediates on right side of comparison. 1686 if (g.CanBeImmediate(right)) { 1687 if (g.CanBeMemoryOperand(opcode, node, left, effect_level)) { 1688 return VisitCompareWithMemoryOperand(selector, opcode, left, 1689 g.UseImmediate(right), cont); 1690 } 1691 return VisitCompare(selector, opcode, g.Use(left), g.UseImmediate(right), 1692 cont); 1693 } 1694 1695 // Match memory operands on left side of comparison. 1696 if (g.CanBeMemoryOperand(opcode, node, left, effect_level)) { 1697 return VisitCompareWithMemoryOperand(selector, opcode, left, 1698 g.UseRegister(right), cont); 1699 } 1700 1701 return VisitCompare(selector, opcode, left, right, cont, 1702 node->op()->HasProperty(Operator::kCommutative)); 1703 } 1704 1705 // Shared routine for 64-bit word comparison operations. 1706 void VisitWord64Compare(InstructionSelector* selector, Node* node, 1707 FlagsContinuation* cont) { 1708 X64OperandGenerator g(selector); 1709 if (selector->CanUseRootsRegister()) { 1710 Heap* const heap = selector->isolate()->heap(); 1711 Heap::RootListIndex root_index; 1712 HeapObjectBinopMatcher m(node); 1713 if (m.right().HasValue() && 1714 heap->IsRootHandle(m.right().Value(), &root_index)) { 1715 if (!node->op()->HasProperty(Operator::kCommutative)) cont->Commute(); 1716 InstructionCode opcode = 1717 kX64Cmp | AddressingModeField::encode(kMode_Root); 1718 return VisitCompare( 1719 selector, opcode, 1720 g.TempImmediate(TurboAssemblerBase::RootRegisterOffset(root_index)), 1721 g.UseRegister(m.left().node()), cont); 1722 } else if (m.left().HasValue() && 1723 heap->IsRootHandle(m.left().Value(), &root_index)) { 1724 InstructionCode opcode = 1725 kX64Cmp | AddressingModeField::encode(kMode_Root); 1726 return VisitCompare( 1727 selector, opcode, 1728 g.TempImmediate(TurboAssemblerBase::RootRegisterOffset(root_index)), 1729 g.UseRegister(m.right().node()), cont); 1730 } 1731 } 1732 StackCheckMatcher<Int64BinopMatcher, IrOpcode::kUint64LessThan> m( 1733 selector->isolate(), node); 1734 if (m.Matched()) { 1735 // Compare(Load(js_stack_limit), LoadStackPointer) 1736 if (!node->op()->HasProperty(Operator::kCommutative)) cont->Commute(); 1737 InstructionCode opcode = cont->Encode(kX64StackCheck); 1738 CHECK(cont->IsBranch()); 1739 selector->EmitWithContinuation(opcode, cont); 1740 return; 1741 } 1742 WasmStackCheckMatcher<Int64BinopMatcher, IrOpcode::kUint64LessThan> wasm_m( 1743 node); 1744 if (wasm_m.Matched()) { 1745 // This is a wasm stack check. By structure, we know that we can use the 1746 // stack pointer directly, as wasm code does not modify the stack at points 1747 // where stack checks are performed. 1748 Node* left = node->InputAt(0); 1749 LocationOperand rsp(InstructionOperand::EXPLICIT, LocationOperand::REGISTER, 1750 InstructionSequence::DefaultRepresentation(), 1751 RegisterCode::kRegCode_rsp); 1752 return VisitCompareWithMemoryOperand(selector, kX64Cmp, left, rsp, cont); 1753 } 1754 VisitWordCompare(selector, node, kX64Cmp, cont); 1755 } 1756 1757 // Shared routine for comparison with zero. 1758 void VisitCompareZero(InstructionSelector* selector, Node* user, Node* node, 1759 InstructionCode opcode, FlagsContinuation* cont) { 1760 X64OperandGenerator g(selector); 1761 if (cont->IsBranch() && 1762 (cont->condition() == kNotEqual || cont->condition() == kEqual)) { 1763 switch (node->opcode()) { 1764 #define FLAGS_SET_BINOP_LIST(V) \ 1765 V(kInt32Add, VisitBinop, kX64Add32) \ 1766 V(kInt32Sub, VisitBinop, kX64Sub32) \ 1767 V(kWord32And, VisitBinop, kX64And32) \ 1768 V(kWord32Or, VisitBinop, kX64Or32) \ 1769 V(kInt64Add, VisitBinop, kX64Add) \ 1770 V(kInt64Sub, VisitBinop, kX64Sub) \ 1771 V(kWord64And, VisitBinop, kX64And) \ 1772 V(kWord64Or, VisitBinop, kX64Or) 1773 #define FLAGS_SET_BINOP(opcode, Visit, archOpcode) \ 1774 case IrOpcode::opcode: \ 1775 if (selector->IsOnlyUserOfNodeInSameBlock(user, node)) { \ 1776 return Visit(selector, node, archOpcode, cont); \ 1777 } \ 1778 break; 1779 FLAGS_SET_BINOP_LIST(FLAGS_SET_BINOP) 1780 #undef FLAGS_SET_BINOP_LIST 1781 #undef FLAGS_SET_BINOP 1782 1783 #define TRY_VISIT_WORD32_SHIFT TryVisitWordShift<Int32BinopMatcher, 32> 1784 #define TRY_VISIT_WORD64_SHIFT TryVisitWordShift<Int64BinopMatcher, 64> 1785 // Skip Word64Sar/Word32Sar since no instruction reduction in most cases. 1786 #define FLAGS_SET_SHIFT_LIST(V) \ 1787 V(kWord32Shl, TRY_VISIT_WORD32_SHIFT, kX64Shl32) \ 1788 V(kWord32Shr, TRY_VISIT_WORD32_SHIFT, kX64Shr32) \ 1789 V(kWord64Shl, TRY_VISIT_WORD64_SHIFT, kX64Shl) \ 1790 V(kWord64Shr, TRY_VISIT_WORD64_SHIFT, kX64Shr) 1791 #define FLAGS_SET_SHIFT(opcode, TryVisit, archOpcode) \ 1792 case IrOpcode::opcode: \ 1793 if (selector->IsOnlyUserOfNodeInSameBlock(user, node)) { \ 1794 if (TryVisit(selector, node, archOpcode, cont)) return; \ 1795 } \ 1796 break; 1797 FLAGS_SET_SHIFT_LIST(FLAGS_SET_SHIFT) 1798 #undef TRY_VISIT_WORD32_SHIFT 1799 #undef TRY_VISIT_WORD64_SHIFT 1800 #undef FLAGS_SET_SHIFT_LIST 1801 #undef FLAGS_SET_SHIFT 1802 default: 1803 break; 1804 } 1805 } 1806 VisitCompare(selector, opcode, g.Use(node), g.TempImmediate(0), cont); 1807 } 1808 1809 1810 // Shared routine for multiple float32 compare operations (inputs commuted). 1811 void VisitFloat32Compare(InstructionSelector* selector, Node* node, 1812 FlagsContinuation* cont) { 1813 Node* const left = node->InputAt(0); 1814 Node* const right = node->InputAt(1); 1815 InstructionCode const opcode = 1816 selector->IsSupported(AVX) ? kAVXFloat32Cmp : kSSEFloat32Cmp; 1817 VisitCompare(selector, opcode, right, left, cont, false); 1818 } 1819 1820 1821 // Shared routine for multiple float64 compare operations (inputs commuted). 1822 void VisitFloat64Compare(InstructionSelector* selector, Node* node, 1823 FlagsContinuation* cont) { 1824 Node* const left = node->InputAt(0); 1825 Node* const right = node->InputAt(1); 1826 InstructionCode const opcode = 1827 selector->IsSupported(AVX) ? kAVXFloat64Cmp : kSSEFloat64Cmp; 1828 VisitCompare(selector, opcode, right, left, cont, false); 1829 } 1830 1831 // Shared routine for Word32/Word64 Atomic Binops 1832 void VisitAtomicBinop(InstructionSelector* selector, Node* node, 1833 ArchOpcode opcode) { 1834 X64OperandGenerator g(selector); 1835 Node* base = node->InputAt(0); 1836 Node* index = node->InputAt(1); 1837 Node* value = node->InputAt(2); 1838 AddressingMode addressing_mode; 1839 InstructionOperand inputs[] = { 1840 g.UseUniqueRegister(value), g.UseUniqueRegister(base), 1841 g.GetEffectiveIndexOperand(index, &addressing_mode)}; 1842 InstructionOperand outputs[] = {g.DefineAsFixed(node, rax)}; 1843 InstructionOperand temps[] = {g.TempRegister()}; 1844 InstructionCode code = opcode | AddressingModeField::encode(addressing_mode); 1845 selector->Emit(code, arraysize(outputs), outputs, arraysize(inputs), inputs, 1846 arraysize(temps), temps); 1847 } 1848 1849 // Shared routine for Word32/Word64 Atomic CmpExchg 1850 void VisitAtomicCompareExchange(InstructionSelector* selector, Node* node, 1851 ArchOpcode opcode) { 1852 X64OperandGenerator g(selector); 1853 Node* base = node->InputAt(0); 1854 Node* index = node->InputAt(1); 1855 Node* old_value = node->InputAt(2); 1856 Node* new_value = node->InputAt(3); 1857 AddressingMode addressing_mode; 1858 InstructionOperand inputs[] = { 1859 g.UseFixed(old_value, rax), g.UseUniqueRegister(new_value), 1860 g.UseUniqueRegister(base), 1861 g.GetEffectiveIndexOperand(index, &addressing_mode)}; 1862 InstructionOperand outputs[] = {g.DefineAsFixed(node, rax)}; 1863 InstructionCode code = opcode | AddressingModeField::encode(addressing_mode); 1864 selector->Emit(code, arraysize(outputs), outputs, arraysize(inputs), inputs); 1865 } 1866 1867 // Shared routine for Word32/Word64 Atomic Exchange 1868 void VisitAtomicExchange(InstructionSelector* selector, Node* node, 1869 ArchOpcode opcode) { 1870 X64OperandGenerator g(selector); 1871 Node* base = node->InputAt(0); 1872 Node* index = node->InputAt(1); 1873 Node* value = node->InputAt(2); 1874 AddressingMode addressing_mode; 1875 InstructionOperand inputs[] = { 1876 g.UseUniqueRegister(value), g.UseUniqueRegister(base), 1877 g.GetEffectiveIndexOperand(index, &addressing_mode)}; 1878 InstructionOperand outputs[] = {g.DefineSameAsFirst(node)}; 1879 InstructionCode code = opcode | AddressingModeField::encode(addressing_mode); 1880 selector->Emit(code, arraysize(outputs), outputs, arraysize(inputs), inputs); 1881 } 1882 1883 } // namespace 1884 1885 // Shared routine for word comparison against zero. 1886 void InstructionSelector::VisitWordCompareZero(Node* user, Node* value, 1887 FlagsContinuation* cont) { 1888 // Try to combine with comparisons against 0 by simply inverting the branch. 1889 while (value->opcode() == IrOpcode::kWord32Equal && CanCover(user, value)) { 1890 Int32BinopMatcher m(value); 1891 if (!m.right().Is(0)) break; 1892 1893 user = value; 1894 value = m.left().node(); 1895 cont->Negate(); 1896 } 1897 1898 if (CanCover(user, value)) { 1899 switch (value->opcode()) { 1900 case IrOpcode::kWord32Equal: 1901 cont->OverwriteAndNegateIfEqual(kEqual); 1902 return VisitWordCompare(this, value, kX64Cmp32, cont); 1903 case IrOpcode::kInt32LessThan: 1904 cont->OverwriteAndNegateIfEqual(kSignedLessThan); 1905 return VisitWordCompare(this, value, kX64Cmp32, cont); 1906 case IrOpcode::kInt32LessThanOrEqual: 1907 cont->OverwriteAndNegateIfEqual(kSignedLessThanOrEqual); 1908 return VisitWordCompare(this, value, kX64Cmp32, cont); 1909 case IrOpcode::kUint32LessThan: 1910 cont->OverwriteAndNegateIfEqual(kUnsignedLessThan); 1911 return VisitWordCompare(this, value, kX64Cmp32, cont); 1912 case IrOpcode::kUint32LessThanOrEqual: 1913 cont->OverwriteAndNegateIfEqual(kUnsignedLessThanOrEqual); 1914 return VisitWordCompare(this, value, kX64Cmp32, cont); 1915 case IrOpcode::kWord64Equal: { 1916 cont->OverwriteAndNegateIfEqual(kEqual); 1917 Int64BinopMatcher m(value); 1918 if (m.right().Is(0)) { 1919 // Try to combine the branch with a comparison. 1920 Node* const user = m.node(); 1921 Node* const value = m.left().node(); 1922 if (CanCover(user, value)) { 1923 switch (value->opcode()) { 1924 case IrOpcode::kInt64Sub: 1925 return VisitWord64Compare(this, value, cont); 1926 case IrOpcode::kWord64And: 1927 return VisitWordCompare(this, value, kX64Test, cont); 1928 default: 1929 break; 1930 } 1931 } 1932 return VisitCompareZero(this, user, value, kX64Cmp, cont); 1933 } 1934 return VisitWord64Compare(this, value, cont); 1935 } 1936 case IrOpcode::kInt64LessThan: 1937 cont->OverwriteAndNegateIfEqual(kSignedLessThan); 1938 return VisitWord64Compare(this, value, cont); 1939 case IrOpcode::kInt64LessThanOrEqual: 1940 cont->OverwriteAndNegateIfEqual(kSignedLessThanOrEqual); 1941 return VisitWord64Compare(this, value, cont); 1942 case IrOpcode::kUint64LessThan: 1943 cont->OverwriteAndNegateIfEqual(kUnsignedLessThan); 1944 return VisitWord64Compare(this, value, cont); 1945 case IrOpcode::kUint64LessThanOrEqual: 1946 cont->OverwriteAndNegateIfEqual(kUnsignedLessThanOrEqual); 1947 return VisitWord64Compare(this, value, cont); 1948 case IrOpcode::kFloat32Equal: 1949 cont->OverwriteAndNegateIfEqual(kUnorderedEqual); 1950 return VisitFloat32Compare(this, value, cont); 1951 case IrOpcode::kFloat32LessThan: 1952 cont->OverwriteAndNegateIfEqual(kUnsignedGreaterThan); 1953 return VisitFloat32Compare(this, value, cont); 1954 case IrOpcode::kFloat32LessThanOrEqual: 1955 cont->OverwriteAndNegateIfEqual(kUnsignedGreaterThanOrEqual); 1956 return VisitFloat32Compare(this, value, cont); 1957 case IrOpcode::kFloat64Equal: 1958 cont->OverwriteAndNegateIfEqual(kUnorderedEqual); 1959 return VisitFloat64Compare(this, value, cont); 1960 case IrOpcode::kFloat64LessThan: { 1961 Float64BinopMatcher m(value); 1962 if (m.left().Is(0.0) && m.right().IsFloat64Abs()) { 1963 // This matches the pattern 1964 // 1965 // Float64LessThan(#0.0, Float64Abs(x)) 1966 // 1967 // which TurboFan generates for NumberToBoolean in the general case, 1968 // and which evaluates to false if x is 0, -0 or NaN. We can compile 1969 // this to a simple (v)ucomisd using not_equal flags condition, which 1970 // avoids the costly Float64Abs. 1971 cont->OverwriteAndNegateIfEqual(kNotEqual); 1972 InstructionCode const opcode = 1973 IsSupported(AVX) ? kAVXFloat64Cmp : kSSEFloat64Cmp; 1974 return VisitCompare(this, opcode, m.left().node(), 1975 m.right().InputAt(0), cont, false); 1976 } 1977 cont->OverwriteAndNegateIfEqual(kUnsignedGreaterThan); 1978 return VisitFloat64Compare(this, value, cont); 1979 } 1980 case IrOpcode::kFloat64LessThanOrEqual: 1981 cont->OverwriteAndNegateIfEqual(kUnsignedGreaterThanOrEqual); 1982 return VisitFloat64Compare(this, value, cont); 1983 case IrOpcode::kProjection: 1984 // Check if this is the overflow output projection of an 1985 // <Operation>WithOverflow node. 1986 if (ProjectionIndexOf(value->op()) == 1u) { 1987 // We cannot combine the <Operation>WithOverflow with this branch 1988 // unless the 0th projection (the use of the actual value of the 1989 // <Operation> is either nullptr, which means there's no use of the 1990 // actual value, or was already defined, which means it is scheduled 1991 // *AFTER* this branch). 1992 Node* const node = value->InputAt(0); 1993 Node* const result = NodeProperties::FindProjection(node, 0); 1994 if (result == nullptr || IsDefined(result)) { 1995 switch (node->opcode()) { 1996 case IrOpcode::kInt32AddWithOverflow: 1997 cont->OverwriteAndNegateIfEqual(kOverflow); 1998 return VisitBinop(this, node, kX64Add32, cont); 1999 case IrOpcode::kInt32SubWithOverflow: 2000 cont->OverwriteAndNegateIfEqual(kOverflow); 2001 return VisitBinop(this, node, kX64Sub32, cont); 2002 case IrOpcode::kInt32MulWithOverflow: 2003 cont->OverwriteAndNegateIfEqual(kOverflow); 2004 return VisitBinop(this, node, kX64Imul32, cont); 2005 case IrOpcode::kInt64AddWithOverflow: 2006 cont->OverwriteAndNegateIfEqual(kOverflow); 2007 return VisitBinop(this, node, kX64Add, cont); 2008 case IrOpcode::kInt64SubWithOverflow: 2009 cont->OverwriteAndNegateIfEqual(kOverflow); 2010 return VisitBinop(this, node, kX64Sub, cont); 2011 default: 2012 break; 2013 } 2014 } 2015 } 2016 break; 2017 case IrOpcode::kInt32Sub: 2018 return VisitWordCompare(this, value, kX64Cmp32, cont); 2019 case IrOpcode::kWord32And: 2020 return VisitWordCompare(this, value, kX64Test32, cont); 2021 default: 2022 break; 2023 } 2024 } 2025 2026 // Branch could not be combined with a compare, emit compare against 0. 2027 VisitCompareZero(this, user, value, kX64Cmp32, cont); 2028 } 2029 2030 void InstructionSelector::VisitSwitch(Node* node, const SwitchInfo& sw) { 2031 X64OperandGenerator g(this); 2032 InstructionOperand value_operand = g.UseRegister(node->InputAt(0)); 2033 2034 // Emit either ArchTableSwitch or ArchLookupSwitch. 2035 if (enable_switch_jump_table_ == kEnableSwitchJumpTable) { 2036 static const size_t kMaxTableSwitchValueRange = 2 << 16; 2037 size_t table_space_cost = 4 + sw.value_range(); 2038 size_t table_time_cost = 3; 2039 size_t lookup_space_cost = 3 + 2 * sw.case_count(); 2040 size_t lookup_time_cost = sw.case_count(); 2041 if (sw.case_count() > 4 && 2042 table_space_cost + 3 * table_time_cost <= 2043 lookup_space_cost + 3 * lookup_time_cost && 2044 sw.min_value() > std::numeric_limits<int32_t>::min() && 2045 sw.value_range() <= kMaxTableSwitchValueRange) { 2046 InstructionOperand index_operand = g.TempRegister(); 2047 if (sw.min_value()) { 2048 // The leal automatically zero extends, so result is a valid 64-bit 2049 // index. 2050 Emit(kX64Lea32 | AddressingModeField::encode(kMode_MRI), index_operand, 2051 value_operand, g.TempImmediate(-sw.min_value())); 2052 } else { 2053 // Zero extend, because we use it as 64-bit index into the jump table. 2054 Emit(kX64Movl, index_operand, value_operand); 2055 } 2056 // Generate a table lookup. 2057 return EmitTableSwitch(sw, index_operand); 2058 } 2059 } 2060 2061 // Generate a tree of conditional jumps. 2062 return EmitBinarySearchSwitch(sw, value_operand); 2063 } 2064 2065 2066 void InstructionSelector::VisitWord32Equal(Node* const node) { 2067 Node* user = node; 2068 FlagsContinuation cont = FlagsContinuation::ForSet(kEqual, node); 2069 Int32BinopMatcher m(user); 2070 if (m.right().Is(0)) { 2071 return VisitWordCompareZero(m.node(), m.left().node(), &cont); 2072 } 2073 VisitWordCompare(this, node, kX64Cmp32, &cont); 2074 } 2075 2076 2077 void InstructionSelector::VisitInt32LessThan(Node* node) { 2078 FlagsContinuation cont = FlagsContinuation::ForSet(kSignedLessThan, node); 2079 VisitWordCompare(this, node, kX64Cmp32, &cont); 2080 } 2081 2082 2083 void InstructionSelector::VisitInt32LessThanOrEqual(Node* node) { 2084 FlagsContinuation cont = 2085 FlagsContinuation::ForSet(kSignedLessThanOrEqual, node); 2086 VisitWordCompare(this, node, kX64Cmp32, &cont); 2087 } 2088 2089 2090 void InstructionSelector::VisitUint32LessThan(Node* node) { 2091 FlagsContinuation cont = FlagsContinuation::ForSet(kUnsignedLessThan, node); 2092 VisitWordCompare(this, node, kX64Cmp32, &cont); 2093 } 2094 2095 2096 void InstructionSelector::VisitUint32LessThanOrEqual(Node* node) { 2097 FlagsContinuation cont = 2098 FlagsContinuation::ForSet(kUnsignedLessThanOrEqual, node); 2099 VisitWordCompare(this, node, kX64Cmp32, &cont); 2100 } 2101 2102 2103 void InstructionSelector::VisitWord64Equal(Node* const node) { 2104 FlagsContinuation cont = FlagsContinuation::ForSet(kEqual, node); 2105 Int64BinopMatcher m(node); 2106 if (m.right().Is(0)) { 2107 // Try to combine the equality check with a comparison. 2108 Node* const user = m.node(); 2109 Node* const value = m.left().node(); 2110 if (CanCover(user, value)) { 2111 switch (value->opcode()) { 2112 case IrOpcode::kInt64Sub: 2113 return VisitWord64Compare(this, value, &cont); 2114 case IrOpcode::kWord64And: 2115 return VisitWordCompare(this, value, kX64Test, &cont); 2116 default: 2117 break; 2118 } 2119 } 2120 } 2121 VisitWord64Compare(this, node, &cont); 2122 } 2123 2124 2125 void InstructionSelector::VisitInt32AddWithOverflow(Node* node) { 2126 if (Node* ovf = NodeProperties::FindProjection(node, 1)) { 2127 FlagsContinuation cont = FlagsContinuation::ForSet(kOverflow, ovf); 2128 return VisitBinop(this, node, kX64Add32, &cont); 2129 } 2130 FlagsContinuation cont; 2131 VisitBinop(this, node, kX64Add32, &cont); 2132 } 2133 2134 2135 void InstructionSelector::VisitInt32SubWithOverflow(Node* node) { 2136 if (Node* ovf = NodeProperties::FindProjection(node, 1)) { 2137 FlagsContinuation cont = FlagsContinuation::ForSet(kOverflow, ovf); 2138 return VisitBinop(this, node, kX64Sub32, &cont); 2139 } 2140 FlagsContinuation cont; 2141 VisitBinop(this, node, kX64Sub32, &cont); 2142 } 2143 2144 2145 void InstructionSelector::VisitInt64LessThan(Node* node) { 2146 FlagsContinuation cont = FlagsContinuation::ForSet(kSignedLessThan, node); 2147 VisitWord64Compare(this, node, &cont); 2148 } 2149 2150 2151 void InstructionSelector::VisitInt64LessThanOrEqual(Node* node) { 2152 FlagsContinuation cont = 2153 FlagsContinuation::ForSet(kSignedLessThanOrEqual, node); 2154 VisitWord64Compare(this, node, &cont); 2155 } 2156 2157 2158 void InstructionSelector::VisitUint64LessThan(Node* node) { 2159 FlagsContinuation cont = FlagsContinuation::ForSet(kUnsignedLessThan, node); 2160 VisitWord64Compare(this, node, &cont); 2161 } 2162 2163 2164 void InstructionSelector::VisitUint64LessThanOrEqual(Node* node) { 2165 FlagsContinuation cont = 2166 FlagsContinuation::ForSet(kUnsignedLessThanOrEqual, node); 2167 VisitWord64Compare(this, node, &cont); 2168 } 2169 2170 2171 void InstructionSelector::VisitFloat32Equal(Node* node) { 2172 FlagsContinuation cont = FlagsContinuation::ForSet(kUnorderedEqual, node); 2173 VisitFloat32Compare(this, node, &cont); 2174 } 2175 2176 2177 void InstructionSelector::VisitFloat32LessThan(Node* node) { 2178 FlagsContinuation cont = 2179 FlagsContinuation::ForSet(kUnsignedGreaterThan, node); 2180 VisitFloat32Compare(this, node, &cont); 2181 } 2182 2183 2184 void InstructionSelector::VisitFloat32LessThanOrEqual(Node* node) { 2185 FlagsContinuation cont = 2186 FlagsContinuation::ForSet(kUnsignedGreaterThanOrEqual, node); 2187 VisitFloat32Compare(this, node, &cont); 2188 } 2189 2190 2191 void InstructionSelector::VisitFloat64Equal(Node* node) { 2192 FlagsContinuation cont = FlagsContinuation::ForSet(kUnorderedEqual, node); 2193 VisitFloat64Compare(this, node, &cont); 2194 } 2195 2196 void InstructionSelector::VisitFloat64LessThan(Node* node) { 2197 Float64BinopMatcher m(node); 2198 if (m.left().Is(0.0) && m.right().IsFloat64Abs()) { 2199 // This matches the pattern 2200 // 2201 // Float64LessThan(#0.0, Float64Abs(x)) 2202 // 2203 // which TurboFan generates for NumberToBoolean in the general case, 2204 // and which evaluates to false if x is 0, -0 or NaN. We can compile 2205 // this to a simple (v)ucomisd using not_equal flags condition, which 2206 // avoids the costly Float64Abs. 2207 FlagsContinuation cont = FlagsContinuation::ForSet(kNotEqual, node); 2208 InstructionCode const opcode = 2209 IsSupported(AVX) ? kAVXFloat64Cmp : kSSEFloat64Cmp; 2210 return VisitCompare(this, opcode, m.left().node(), m.right().InputAt(0), 2211 &cont, false); 2212 } 2213 FlagsContinuation cont = 2214 FlagsContinuation::ForSet(kUnsignedGreaterThan, node); 2215 VisitFloat64Compare(this, node, &cont); 2216 } 2217 2218 void InstructionSelector::VisitFloat64LessThanOrEqual(Node* node) { 2219 FlagsContinuation cont = 2220 FlagsContinuation::ForSet(kUnsignedGreaterThanOrEqual, node); 2221 VisitFloat64Compare(this, node, &cont); 2222 } 2223 2224 void InstructionSelector::VisitFloat64InsertLowWord32(Node* node) { 2225 X64OperandGenerator g(this); 2226 Node* left = node->InputAt(0); 2227 Node* right = node->InputAt(1); 2228 Float64Matcher mleft(left); 2229 if (mleft.HasValue() && (bit_cast<uint64_t>(mleft.Value()) >> 32) == 0u) { 2230 Emit(kSSEFloat64LoadLowWord32, g.DefineAsRegister(node), g.Use(right)); 2231 return; 2232 } 2233 Emit(kSSEFloat64InsertLowWord32, g.DefineSameAsFirst(node), 2234 g.UseRegister(left), g.Use(right)); 2235 } 2236 2237 2238 void InstructionSelector::VisitFloat64InsertHighWord32(Node* node) { 2239 X64OperandGenerator g(this); 2240 Node* left = node->InputAt(0); 2241 Node* right = node->InputAt(1); 2242 Emit(kSSEFloat64InsertHighWord32, g.DefineSameAsFirst(node), 2243 g.UseRegister(left), g.Use(right)); 2244 } 2245 2246 void InstructionSelector::VisitFloat64SilenceNaN(Node* node) { 2247 X64OperandGenerator g(this); 2248 Emit(kSSEFloat64SilenceNaN, g.DefineSameAsFirst(node), 2249 g.UseRegister(node->InputAt(0))); 2250 } 2251 2252 void InstructionSelector::VisitWord32AtomicLoad(Node* node) { 2253 LoadRepresentation load_rep = LoadRepresentationOf(node->op()); 2254 DCHECK(load_rep.representation() == MachineRepresentation::kWord8 || 2255 load_rep.representation() == MachineRepresentation::kWord16 || 2256 load_rep.representation() == MachineRepresentation::kWord32); 2257 USE(load_rep); 2258 VisitLoad(node); 2259 } 2260 2261 void InstructionSelector::VisitWord64AtomicLoad(Node* node) { 2262 LoadRepresentation load_rep = LoadRepresentationOf(node->op()); 2263 USE(load_rep); 2264 VisitLoad(node); 2265 } 2266 2267 void InstructionSelector::VisitWord32AtomicStore(Node* node) { 2268 MachineRepresentation rep = AtomicStoreRepresentationOf(node->op()); 2269 ArchOpcode opcode = kArchNop; 2270 switch (rep) { 2271 case MachineRepresentation::kWord8: 2272 opcode = kWord32AtomicExchangeInt8; 2273 break; 2274 case MachineRepresentation::kWord16: 2275 opcode = kWord32AtomicExchangeInt16; 2276 break; 2277 case MachineRepresentation::kWord32: 2278 opcode = kWord32AtomicExchangeWord32; 2279 break; 2280 default: 2281 UNREACHABLE(); 2282 return; 2283 } 2284 VisitAtomicExchange(this, node, opcode); 2285 } 2286 2287 void InstructionSelector::VisitWord64AtomicStore(Node* node) { 2288 MachineRepresentation rep = AtomicStoreRepresentationOf(node->op()); 2289 ArchOpcode opcode = kArchNop; 2290 switch (rep) { 2291 case MachineRepresentation::kWord8: 2292 opcode = kX64Word64AtomicExchangeUint8; 2293 break; 2294 case MachineRepresentation::kWord16: 2295 opcode = kX64Word64AtomicExchangeUint16; 2296 break; 2297 case MachineRepresentation::kWord32: 2298 opcode = kX64Word64AtomicExchangeUint32; 2299 break; 2300 case MachineRepresentation::kWord64: 2301 opcode = kX64Word64AtomicExchangeUint64; 2302 break; 2303 default: 2304 UNREACHABLE(); 2305 return; 2306 } 2307 VisitAtomicExchange(this, node, opcode); 2308 } 2309 2310 void InstructionSelector::VisitWord32AtomicExchange(Node* node) { 2311 MachineType type = AtomicOpType(node->op()); 2312 ArchOpcode opcode = kArchNop; 2313 if (type == MachineType::Int8()) { 2314 opcode = kWord32AtomicExchangeInt8; 2315 } else if (type == MachineType::Uint8()) { 2316 opcode = kWord32AtomicExchangeUint8; 2317 } else if (type == MachineType::Int16()) { 2318 opcode = kWord32AtomicExchangeInt16; 2319 } else if (type == MachineType::Uint16()) { 2320 opcode = kWord32AtomicExchangeUint16; 2321 } else if (type == MachineType::Int32() || type == MachineType::Uint32()) { 2322 opcode = kWord32AtomicExchangeWord32; 2323 } else { 2324 UNREACHABLE(); 2325 return; 2326 } 2327 VisitAtomicExchange(this, node, opcode); 2328 } 2329 2330 void InstructionSelector::VisitWord64AtomicExchange(Node* node) { 2331 MachineType type = AtomicOpType(node->op()); 2332 ArchOpcode opcode = kArchNop; 2333 if (type == MachineType::Uint8()) { 2334 opcode = kX64Word64AtomicExchangeUint8; 2335 } else if (type == MachineType::Uint16()) { 2336 opcode = kX64Word64AtomicExchangeUint16; 2337 } else if (type == MachineType::Uint32()) { 2338 opcode = kX64Word64AtomicExchangeUint32; 2339 } else if (type == MachineType::Uint64()) { 2340 opcode = kX64Word64AtomicExchangeUint64; 2341 } else { 2342 UNREACHABLE(); 2343 return; 2344 } 2345 VisitAtomicExchange(this, node, opcode); 2346 } 2347 2348 void InstructionSelector::VisitWord32AtomicCompareExchange(Node* node) { 2349 MachineType type = AtomicOpType(node->op()); 2350 ArchOpcode opcode = kArchNop; 2351 if (type == MachineType::Int8()) { 2352 opcode = kWord32AtomicCompareExchangeInt8; 2353 } else if (type == MachineType::Uint8()) { 2354 opcode = kWord32AtomicCompareExchangeUint8; 2355 } else if (type == MachineType::Int16()) { 2356 opcode = kWord32AtomicCompareExchangeInt16; 2357 } else if (type == MachineType::Uint16()) { 2358 opcode = kWord32AtomicCompareExchangeUint16; 2359 } else if (type == MachineType::Int32() || type == MachineType::Uint32()) { 2360 opcode = kWord32AtomicCompareExchangeWord32; 2361 } else { 2362 UNREACHABLE(); 2363 return; 2364 } 2365 VisitAtomicCompareExchange(this, node, opcode); 2366 } 2367 2368 void InstructionSelector::VisitWord64AtomicCompareExchange(Node* node) { 2369 MachineType type = AtomicOpType(node->op()); 2370 ArchOpcode opcode = kArchNop; 2371 if (type == MachineType::Uint8()) { 2372 opcode = kX64Word64AtomicCompareExchangeUint8; 2373 } else if (type == MachineType::Uint16()) { 2374 opcode = kX64Word64AtomicCompareExchangeUint16; 2375 } else if (type == MachineType::Uint32()) { 2376 opcode = kX64Word64AtomicCompareExchangeUint32; 2377 } else if (type == MachineType::Uint64()) { 2378 opcode = kX64Word64AtomicCompareExchangeUint64; 2379 } else { 2380 UNREACHABLE(); 2381 return; 2382 } 2383 VisitAtomicCompareExchange(this, node, opcode); 2384 } 2385 2386 void InstructionSelector::VisitWord32AtomicBinaryOperation( 2387 Node* node, ArchOpcode int8_op, ArchOpcode uint8_op, ArchOpcode int16_op, 2388 ArchOpcode uint16_op, ArchOpcode word32_op) { 2389 MachineType type = AtomicOpType(node->op()); 2390 ArchOpcode opcode = kArchNop; 2391 if (type == MachineType::Int8()) { 2392 opcode = int8_op; 2393 } else if (type == MachineType::Uint8()) { 2394 opcode = uint8_op; 2395 } else if (type == MachineType::Int16()) { 2396 opcode = int16_op; 2397 } else if (type == MachineType::Uint16()) { 2398 opcode = uint16_op; 2399 } else if (type == MachineType::Int32() || type == MachineType::Uint32()) { 2400 opcode = word32_op; 2401 } else { 2402 UNREACHABLE(); 2403 return; 2404 } 2405 VisitAtomicBinop(this, node, opcode); 2406 } 2407 2408 #define VISIT_ATOMIC_BINOP(op) \ 2409 void InstructionSelector::VisitWord32Atomic##op(Node* node) { \ 2410 VisitWord32AtomicBinaryOperation( \ 2411 node, kWord32Atomic##op##Int8, kWord32Atomic##op##Uint8, \ 2412 kWord32Atomic##op##Int16, kWord32Atomic##op##Uint16, \ 2413 kWord32Atomic##op##Word32); \ 2414 } 2415 VISIT_ATOMIC_BINOP(Add) 2416 VISIT_ATOMIC_BINOP(Sub) 2417 VISIT_ATOMIC_BINOP(And) 2418 VISIT_ATOMIC_BINOP(Or) 2419 VISIT_ATOMIC_BINOP(Xor) 2420 #undef VISIT_ATOMIC_BINOP 2421 2422 void InstructionSelector::VisitWord64AtomicBinaryOperation( 2423 Node* node, ArchOpcode uint8_op, ArchOpcode uint16_op, ArchOpcode uint32_op, 2424 ArchOpcode word64_op) { 2425 MachineType type = AtomicOpType(node->op()); 2426 ArchOpcode opcode = kArchNop; 2427 if (type == MachineType::Uint8()) { 2428 opcode = uint8_op; 2429 } else if (type == MachineType::Uint16()) { 2430 opcode = uint16_op; 2431 } else if (type == MachineType::Uint32()) { 2432 opcode = uint32_op; 2433 } else if (type == MachineType::Uint64()) { 2434 opcode = word64_op; 2435 } else { 2436 UNREACHABLE(); 2437 return; 2438 } 2439 VisitAtomicBinop(this, node, opcode); 2440 } 2441 2442 #define VISIT_ATOMIC_BINOP(op) \ 2443 void InstructionSelector::VisitWord64Atomic##op(Node* node) { \ 2444 VisitWord64AtomicBinaryOperation( \ 2445 node, kX64Word64Atomic##op##Uint8, kX64Word64Atomic##op##Uint16, \ 2446 kX64Word64Atomic##op##Uint32, kX64Word64Atomic##op##Uint64); \ 2447 } 2448 VISIT_ATOMIC_BINOP(Add) 2449 VISIT_ATOMIC_BINOP(Sub) 2450 VISIT_ATOMIC_BINOP(And) 2451 VISIT_ATOMIC_BINOP(Or) 2452 VISIT_ATOMIC_BINOP(Xor) 2453 #undef VISIT_ATOMIC_BINOP 2454 2455 #define SIMD_TYPES(V) \ 2456 V(F32x4) \ 2457 V(I32x4) \ 2458 V(I16x8) \ 2459 V(I8x16) 2460 2461 #define SIMD_BINOP_LIST(V) \ 2462 V(F32x4Add) \ 2463 V(F32x4AddHoriz) \ 2464 V(F32x4Sub) \ 2465 V(F32x4Mul) \ 2466 V(F32x4Min) \ 2467 V(F32x4Max) \ 2468 V(F32x4Eq) \ 2469 V(F32x4Ne) \ 2470 V(F32x4Lt) \ 2471 V(F32x4Le) \ 2472 V(I32x4Add) \ 2473 V(I32x4AddHoriz) \ 2474 V(I32x4Sub) \ 2475 V(I32x4Mul) \ 2476 V(I32x4MinS) \ 2477 V(I32x4MaxS) \ 2478 V(I32x4Eq) \ 2479 V(I32x4Ne) \ 2480 V(I32x4GtS) \ 2481 V(I32x4GeS) \ 2482 V(I32x4MinU) \ 2483 V(I32x4MaxU) \ 2484 V(I32x4GtU) \ 2485 V(I32x4GeU) \ 2486 V(I16x8Add) \ 2487 V(I16x8AddSaturateS) \ 2488 V(I16x8AddHoriz) \ 2489 V(I16x8Sub) \ 2490 V(I16x8SubSaturateS) \ 2491 V(I16x8Mul) \ 2492 V(I16x8MinS) \ 2493 V(I16x8MaxS) \ 2494 V(I16x8Eq) \ 2495 V(I16x8Ne) \ 2496 V(I16x8GtS) \ 2497 V(I16x8GeS) \ 2498 V(I16x8AddSaturateU) \ 2499 V(I16x8SubSaturateU) \ 2500 V(I16x8MinU) \ 2501 V(I16x8MaxU) \ 2502 V(I16x8GtU) \ 2503 V(I16x8GeU) \ 2504 V(I8x16Add) \ 2505 V(I8x16AddSaturateS) \ 2506 V(I8x16Sub) \ 2507 V(I8x16SubSaturateS) \ 2508 V(I8x16MinS) \ 2509 V(I8x16MaxS) \ 2510 V(I8x16Eq) \ 2511 V(I8x16Ne) \ 2512 V(I8x16GtS) \ 2513 V(I8x16GeS) \ 2514 V(I8x16AddSaturateU) \ 2515 V(I8x16SubSaturateU) \ 2516 V(I8x16MinU) \ 2517 V(I8x16MaxU) \ 2518 V(I8x16GtU) \ 2519 V(I8x16GeU) \ 2520 V(S128And) \ 2521 V(S128Or) \ 2522 V(S128Xor) 2523 2524 #define SIMD_UNOP_LIST(V) \ 2525 V(F32x4Abs) \ 2526 V(F32x4Neg) \ 2527 V(F32x4RecipApprox) \ 2528 V(F32x4RecipSqrtApprox) \ 2529 V(I32x4Neg) \ 2530 V(I16x8Neg) \ 2531 V(I8x16Neg) \ 2532 V(S128Not) 2533 2534 #define SIMD_SHIFT_OPCODES(V) \ 2535 V(I32x4Shl) \ 2536 V(I32x4ShrS) \ 2537 V(I32x4ShrU) \ 2538 V(I16x8Shl) \ 2539 V(I16x8ShrS) \ 2540 V(I16x8ShrU) 2541 2542 void InstructionSelector::VisitS128Zero(Node* node) { 2543 X64OperandGenerator g(this); 2544 Emit(kX64S128Zero, g.DefineAsRegister(node), g.DefineAsRegister(node)); 2545 } 2546 2547 #define VISIT_SIMD_SPLAT(Type) \ 2548 void InstructionSelector::Visit##Type##Splat(Node* node) { \ 2549 X64OperandGenerator g(this); \ 2550 Emit(kX64##Type##Splat, g.DefineAsRegister(node), \ 2551 g.Use(node->InputAt(0))); \ 2552 } 2553 SIMD_TYPES(VISIT_SIMD_SPLAT) 2554 #undef VISIT_SIMD_SPLAT 2555 2556 #define VISIT_SIMD_EXTRACT_LANE(Type) \ 2557 void InstructionSelector::Visit##Type##ExtractLane(Node* node) { \ 2558 X64OperandGenerator g(this); \ 2559 int32_t lane = OpParameter<int32_t>(node->op()); \ 2560 Emit(kX64##Type##ExtractLane, g.DefineAsRegister(node), \ 2561 g.UseRegister(node->InputAt(0)), g.UseImmediate(lane)); \ 2562 } 2563 SIMD_TYPES(VISIT_SIMD_EXTRACT_LANE) 2564 #undef VISIT_SIMD_EXTRACT_LANE 2565 2566 #define VISIT_SIMD_REPLACE_LANE(Type) \ 2567 void InstructionSelector::Visit##Type##ReplaceLane(Node* node) { \ 2568 X64OperandGenerator g(this); \ 2569 int32_t lane = OpParameter<int32_t>(node->op()); \ 2570 Emit(kX64##Type##ReplaceLane, g.DefineSameAsFirst(node), \ 2571 g.UseRegister(node->InputAt(0)), g.UseImmediate(lane), \ 2572 g.Use(node->InputAt(1))); \ 2573 } 2574 SIMD_TYPES(VISIT_SIMD_REPLACE_LANE) 2575 #undef VISIT_SIMD_REPLACE_LANE 2576 2577 #define VISIT_SIMD_SHIFT(Opcode) \ 2578 void InstructionSelector::Visit##Opcode(Node* node) { \ 2579 X64OperandGenerator g(this); \ 2580 int32_t value = OpParameter<int32_t>(node->op()); \ 2581 Emit(kX64##Opcode, g.DefineSameAsFirst(node), \ 2582 g.UseRegister(node->InputAt(0)), g.UseImmediate(value)); \ 2583 } 2584 SIMD_SHIFT_OPCODES(VISIT_SIMD_SHIFT) 2585 #undef VISIT_SIMD_SHIFT 2586 2587 #define VISIT_SIMD_UNOP(Opcode) \ 2588 void InstructionSelector::Visit##Opcode(Node* node) { \ 2589 X64OperandGenerator g(this); \ 2590 Emit(kX64##Opcode, g.DefineAsRegister(node), \ 2591 g.UseRegister(node->InputAt(0))); \ 2592 } 2593 SIMD_UNOP_LIST(VISIT_SIMD_UNOP) 2594 #undef VISIT_SIMD_UNOP 2595 2596 #define VISIT_SIMD_BINOP(Opcode) \ 2597 void InstructionSelector::Visit##Opcode(Node* node) { \ 2598 X64OperandGenerator g(this); \ 2599 Emit(kX64##Opcode, g.DefineSameAsFirst(node), \ 2600 g.UseRegister(node->InputAt(0)), g.UseRegister(node->InputAt(1))); \ 2601 } 2602 SIMD_BINOP_LIST(VISIT_SIMD_BINOP) 2603 #undef VISIT_SIMD_BINOP 2604 #undef SIMD_TYPES 2605 #undef SIMD_BINOP_LIST 2606 #undef SIMD_UNOP_LIST 2607 #undef SIMD_SHIFT_OPCODES 2608 2609 void InstructionSelector::VisitS128Select(Node* node) { 2610 X64OperandGenerator g(this); 2611 Emit(kX64S128Select, g.DefineSameAsFirst(node), 2612 g.UseRegister(node->InputAt(0)), g.UseRegister(node->InputAt(1)), 2613 g.UseRegister(node->InputAt(2))); 2614 } 2615 2616 void InstructionSelector::VisitInt32AbsWithOverflow(Node* node) { 2617 UNREACHABLE(); 2618 } 2619 2620 void InstructionSelector::VisitInt64AbsWithOverflow(Node* node) { 2621 UNREACHABLE(); 2622 } 2623 2624 // static 2625 MachineOperatorBuilder::Flags 2626 InstructionSelector::SupportedMachineOperatorFlags() { 2627 MachineOperatorBuilder::Flags flags = 2628 MachineOperatorBuilder::kWord32ShiftIsSafe | 2629 MachineOperatorBuilder::kWord32Ctz | MachineOperatorBuilder::kWord64Ctz | 2630 MachineOperatorBuilder::kSpeculationFence; 2631 if (CpuFeatures::IsSupported(POPCNT)) { 2632 flags |= MachineOperatorBuilder::kWord32Popcnt | 2633 MachineOperatorBuilder::kWord64Popcnt; 2634 } 2635 if (CpuFeatures::IsSupported(SSE4_1)) { 2636 flags |= MachineOperatorBuilder::kFloat32RoundDown | 2637 MachineOperatorBuilder::kFloat64RoundDown | 2638 MachineOperatorBuilder::kFloat32RoundUp | 2639 MachineOperatorBuilder::kFloat64RoundUp | 2640 MachineOperatorBuilder::kFloat32RoundTruncate | 2641 MachineOperatorBuilder::kFloat64RoundTruncate | 2642 MachineOperatorBuilder::kFloat32RoundTiesEven | 2643 MachineOperatorBuilder::kFloat64RoundTiesEven; 2644 } 2645 return flags; 2646 } 2647 2648 // static 2649 MachineOperatorBuilder::AlignmentRequirements 2650 InstructionSelector::AlignmentRequirements() { 2651 return MachineOperatorBuilder::AlignmentRequirements:: 2652 FullUnalignedAccessSupport(); 2653 } 2654 2655 } // namespace compiler 2656 } // namespace internal 2657 } // namespace v8 2658