CodeGenerator-x64.cpp (47830B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * vim: set ts=8 sts=2 et sw=2 tw=80: 3 * This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "jit/x64/CodeGenerator-x64.h" 8 9 #include "mozilla/CheckedInt.h" 10 #include "mozilla/FloatingPoint.h" 11 12 #include "jit/CodeGenerator.h" 13 #include "jit/MIR-wasm.h" 14 #include "jit/MIR.h" 15 #include "jit/ReciprocalMulConstants.h" 16 #include "js/ScalarType.h" // js::Scalar::Type 17 18 #include "jit/MacroAssembler-inl.h" 19 #include "jit/shared/CodeGenerator-shared-inl.h" 20 21 using namespace js; 22 using namespace js::jit; 23 24 CodeGeneratorX64::CodeGeneratorX64(MIRGenerator* gen, LIRGraph* graph, 25 MacroAssembler* masm, 26 const wasm::CodeMetadata* wasmCodeMeta) 27 : CodeGeneratorX86Shared(gen, graph, masm, wasmCodeMeta) {} 28 29 Operand CodeGeneratorX64::ToOperand64(const LInt64Allocation& a64) { 30 const LAllocation& a = a64.value(); 31 MOZ_ASSERT(!a.isFloatReg()); 32 if (a.isGeneralReg()) { 33 return Operand(a.toGeneralReg()->reg()); 34 } 35 return Operand(ToAddress(a)); 36 } 37 38 void CodeGenerator::visitBox(LBox* box) { 39 const LAllocation* in = box->payload(); 40 ValueOperand result = ToOutValue(box); 41 42 masm.moveValue(TypedOrValueRegister(box->type(), ToAnyRegister(in)), result); 43 44 if (JitOptions.spectreValueMasking && IsFloatingPointType(box->type())) { 45 ScratchRegisterScope scratch(masm); 46 masm.movePtr(ImmWord(JSVAL_SHIFTED_TAG_MAX_DOUBLE), scratch); 47 masm.cmpPtrMovePtr(Assembler::Below, scratch, result.valueReg(), scratch, 48 result.valueReg()); 49 } 50 } 51 52 void CodeGenerator::visitUnbox(LUnbox* unbox) { 53 MUnbox* mir = unbox->mir(); 54 55 Register result = ToRegister(unbox->output()); 56 57 if (mir->fallible()) { 58 Label bail; 59 auto fallibleUnboxImpl = [&](auto value) { 60 switch (mir->type()) { 61 case MIRType::Int32: 62 masm.fallibleUnboxInt32(value, result, &bail); 63 break; 64 case MIRType::Boolean: 65 masm.fallibleUnboxBoolean(value, result, &bail); 66 break; 67 case MIRType::Object: 68 masm.fallibleUnboxObject(value, result, &bail); 69 break; 70 case MIRType::String: 71 masm.fallibleUnboxString(value, result, &bail); 72 break; 73 case MIRType::Symbol: 74 masm.fallibleUnboxSymbol(value, result, &bail); 75 break; 76 case MIRType::BigInt: 77 masm.fallibleUnboxBigInt(value, result, &bail); 78 break; 79 default: 80 MOZ_CRASH("Given MIRType cannot be unboxed."); 81 } 82 }; 83 LAllocation* input = unbox->getOperand(LUnbox::Input); 84 if (input->isGeneralReg()) { 85 fallibleUnboxImpl(ValueOperand(ToRegister(input))); 86 } else { 87 fallibleUnboxImpl(ToAddress(input)); 88 } 89 bailoutFrom(&bail, unbox->snapshot()); 90 return; 91 } 92 93 // Infallible unbox. 94 95 Operand input = ToOperand(unbox->getOperand(LUnbox::Input)); 96 97 #ifdef DEBUG 98 // Assert the types match. 99 JSValueTag tag = MIRTypeToTag(mir->type()); 100 Label ok; 101 masm.splitTag(input, ScratchReg); 102 masm.branch32(Assembler::Equal, ScratchReg, Imm32(tag), &ok); 103 masm.assumeUnreachable("Infallible unbox type mismatch"); 104 masm.bind(&ok); 105 #endif 106 107 switch (mir->type()) { 108 case MIRType::Int32: 109 masm.unboxInt32(input, result); 110 break; 111 case MIRType::Boolean: 112 masm.unboxBoolean(input, result); 113 break; 114 case MIRType::Object: 115 masm.unboxObject(input, result); 116 break; 117 case MIRType::String: 118 masm.unboxString(input, result); 119 break; 120 case MIRType::Symbol: 121 masm.unboxSymbol(input, result); 122 break; 123 case MIRType::BigInt: 124 masm.unboxBigInt(input, result); 125 break; 126 default: 127 MOZ_CRASH("Given MIRType cannot be unboxed."); 128 } 129 } 130 131 void CodeGenerator::visitMulI64(LMulI64* lir) { 132 Register lhs = ToRegister64(lir->lhs()).reg; 133 LInt64Allocation rhs = lir->rhs(); 134 Register out = ToOutRegister64(lir).reg; 135 136 if (IsConstant(rhs)) { 137 int64_t constant = ToInt64(rhs); 138 switch (constant) { 139 case -1: 140 if (lhs != out) { 141 masm.movq(lhs, out); 142 } 143 masm.negq(out); 144 break; 145 case 0: 146 masm.xorq(out, out); 147 break; 148 case 1: 149 if (lhs != out) { 150 masm.movq(lhs, out); 151 } 152 break; 153 case 2: 154 if (lhs == out) { 155 masm.addq(lhs, lhs); 156 } else { 157 masm.lea(Operand(lhs, lhs, TimesOne), out); 158 } 159 break; 160 case 3: 161 masm.lea(Operand(lhs, lhs, TimesTwo), out); 162 break; 163 case 4: 164 if (lhs == out) { 165 masm.shlq(Imm32(2), lhs); 166 } else { 167 masm.lea(Operand(lhs, TimesFour, 0), out); 168 } 169 break; 170 case 5: 171 masm.lea(Operand(lhs, lhs, TimesFour), out); 172 break; 173 case 8: 174 if (lhs == out) { 175 masm.shlq(Imm32(3), lhs); 176 } else { 177 masm.lea(Operand(lhs, TimesEight, 0), out); 178 } 179 break; 180 case 9: 181 masm.lea(Operand(lhs, lhs, TimesEight), out); 182 break; 183 default: { 184 // Use shift if constant is power of 2. 185 int32_t shift = mozilla::FloorLog2(constant); 186 if (constant > 0 && (1 << shift) == constant) { 187 if (lhs != out) { 188 masm.movq(lhs, out); 189 } 190 masm.shlq(Imm32(shift), out); 191 } else if (int32_t(constant) == constant) { 192 masm.imulq(Imm32(constant), lhs, out); 193 } else { 194 MOZ_ASSERT(out == lhs); 195 masm.mul64(Imm64(constant), Register64(lhs)); 196 } 197 break; 198 } 199 } 200 } else { 201 MOZ_ASSERT(out == lhs); 202 masm.imulq(ToOperandOrRegister64(rhs), lhs); 203 } 204 } 205 206 template <class LIR> 207 static void TrapIfDivideByZero(MacroAssembler& masm, LIR* lir, Register rhs) { 208 auto* mir = lir->mir(); 209 MOZ_ASSERT(mir->trapOnError()); 210 211 if (mir->canBeDivideByZero()) { 212 Label nonZero; 213 masm.branchTestPtr(Assembler::NonZero, rhs, rhs, &nonZero); 214 masm.wasmTrap(wasm::Trap::IntegerDivideByZero, mir->trapSiteDesc()); 215 masm.bind(&nonZero); 216 } 217 } 218 219 void CodeGenerator::visitDivI64(LDivI64* lir) { 220 Register lhs = ToRegister(lir->lhs()); 221 Register rhs = ToRegister(lir->rhs()); 222 223 MOZ_ASSERT(lhs == rax); 224 MOZ_ASSERT(rhs != rax); 225 MOZ_ASSERT(rhs != rdx); 226 MOZ_ASSERT(ToRegister(lir->output()) == rax); 227 MOZ_ASSERT(ToRegister(lir->temp0()) == rdx); 228 229 MDiv* mir = lir->mir(); 230 231 // Handle divide by zero. 232 TrapIfDivideByZero(masm, lir, rhs); 233 234 // Handle an integer overflow exception from INT64_MIN / -1. 235 if (mir->canBeNegativeOverflow()) { 236 Label notOverflow; 237 masm.branchPtr(Assembler::NotEqual, lhs, ImmWord(INT64_MIN), ¬Overflow); 238 masm.branchPtr(Assembler::NotEqual, rhs, ImmWord(-1), ¬Overflow); 239 masm.wasmTrap(wasm::Trap::IntegerOverflow, mir->trapSiteDesc()); 240 masm.bind(¬Overflow); 241 } 242 243 // Sign extend the lhs into rdx to make rdx:rax. 244 masm.cqo(); 245 masm.idivq(rhs); 246 } 247 248 void CodeGenerator::visitModI64(LModI64* lir) { 249 Register lhs = ToRegister(lir->lhs()); 250 Register rhs = ToRegister(lir->rhs()); 251 Register output = ToRegister(lir->output()); 252 253 MOZ_ASSERT(lhs == rax); 254 MOZ_ASSERT(rhs != rax); 255 MOZ_ASSERT(rhs != rdx); 256 MOZ_ASSERT(ToRegister(lir->output()) == rdx); 257 MOZ_ASSERT(ToRegister(lir->temp0()) == rax); 258 259 MMod* mir = lir->mir(); 260 261 Label done; 262 263 // Handle divide by zero. 264 TrapIfDivideByZero(masm, lir, rhs); 265 266 // Handle an integer overflow exception from INT64_MIN / -1. 267 if (mir->canBeNegativeDividend()) { 268 Label notOverflow; 269 masm.branchPtr(Assembler::NotEqual, lhs, ImmWord(INT64_MIN), ¬Overflow); 270 masm.branchPtr(Assembler::NotEqual, rhs, ImmWord(-1), ¬Overflow); 271 { 272 masm.xorl(output, output); 273 masm.jump(&done); 274 } 275 masm.bind(¬Overflow); 276 } 277 278 // Sign extend the lhs into rdx to make rdx:rax. 279 masm.cqo(); 280 masm.idivq(rhs); 281 282 masm.bind(&done); 283 } 284 285 void CodeGenerator::visitUDivI64(LUDivI64* lir) { 286 Register rhs = ToRegister(lir->rhs()); 287 288 MOZ_ASSERT(ToRegister(lir->lhs()) == rax); 289 MOZ_ASSERT(rhs != rax); 290 MOZ_ASSERT(rhs != rdx); 291 MOZ_ASSERT(ToRegister(lir->output()) == rax); 292 MOZ_ASSERT(ToRegister(lir->temp0()) == rdx); 293 294 // Prevent divide by zero. 295 TrapIfDivideByZero(masm, lir, rhs); 296 297 // Zero extend the lhs into rdx to make (rdx:rax). 298 masm.xorl(rdx, rdx); 299 masm.udivq(rhs); 300 } 301 302 void CodeGenerator::visitUModI64(LUModI64* lir) { 303 Register rhs = ToRegister(lir->rhs()); 304 305 MOZ_ASSERT(ToRegister(lir->lhs()) == rax); 306 MOZ_ASSERT(rhs != rax); 307 MOZ_ASSERT(rhs != rdx); 308 MOZ_ASSERT(ToRegister(lir->output()) == rdx); 309 MOZ_ASSERT(ToRegister(lir->temp0()) == rax); 310 311 // Prevent divide by zero. 312 TrapIfDivideByZero(masm, lir, rhs); 313 314 // Zero extend the lhs into rdx to make (rdx:rax). 315 masm.xorl(rdx, rdx); 316 masm.udivq(rhs); 317 } 318 319 void CodeGenerator::visitDivPowTwoI64(LDivPowTwoI64* ins) { 320 Register lhs = ToRegister(ins->numerator()); 321 322 int32_t shift = ins->shift(); 323 bool negativeDivisor = ins->negativeDivisor(); 324 MDiv* mir = ins->mir(); 325 326 // We use defineReuseInput so these should always be the same, which is 327 // convenient since all of our instructions here are two-address. 328 MOZ_ASSERT(lhs == ToRegister(ins->output())); 329 330 // Unsigned division is just a right-shift. 331 if (mir->isUnsigned()) { 332 if (shift != 0) { 333 masm.shrq(Imm32(shift), lhs); 334 } 335 return; 336 } 337 338 if (shift != 0) { 339 // Adjust the value so that shifting produces a correctly rounded result 340 // when the numerator is negative. 341 // See 10-1 "Signed Division by a Known Power of 2" in Henry S. Warren, 342 // Jr.'s Hacker's Delight. 343 if (mir->canBeNegativeDividend()) { 344 Register lhsCopy = ToRegister(ins->numeratorCopy()); 345 MOZ_ASSERT(lhsCopy != lhs); 346 if (shift > 1) { 347 // Copy the sign bit of the numerator. (= (2^63 - 1) or 0) 348 masm.sarq(Imm32(63), lhs); 349 } 350 // Divide by 2^(64 - shift) 351 // i.e. (= (2^64 - 1) / 2^(64 - shift) or 0) 352 // i.e. (= (2^shift - 1) or 0) 353 masm.shrq(Imm32(64 - shift), lhs); 354 // If signed, make any 1 bit below the shifted bits to bubble up, such 355 // that once shifted the value would be rounded towards 0. 356 masm.addq(lhsCopy, lhs); 357 } 358 masm.sarq(Imm32(shift), lhs); 359 } 360 361 if (negativeDivisor) { 362 masm.negq(lhs); 363 } 364 365 if (shift == 0 && negativeDivisor) { 366 // INT64_MIN / -1 overflows. 367 Label ok; 368 masm.j(Assembler::NoOverflow, &ok); 369 masm.wasmTrap(wasm::Trap::IntegerOverflow, mir->trapSiteDesc()); 370 masm.bind(&ok); 371 } 372 } 373 374 void CodeGenerator::visitModPowTwoI64(LModPowTwoI64* ins) { 375 Register64 lhs = Register64(ToRegister(ins->input())); 376 int32_t shift = ins->shift(); 377 bool canBeNegative = 378 !ins->mir()->isUnsigned() && ins->mir()->canBeNegativeDividend(); 379 380 MOZ_ASSERT(lhs.reg == ToRegister(ins->output())); 381 382 if (shift == 0) { 383 masm.xorl(lhs.reg, lhs.reg); 384 return; 385 } 386 387 auto clearHighBits = [&]() { 388 switch (shift) { 389 case 8: 390 masm.movzbl(lhs.reg, lhs.reg); 391 break; 392 case 16: 393 masm.movzwl(lhs.reg, lhs.reg); 394 break; 395 case 32: 396 masm.movl(lhs.reg, lhs.reg); 397 break; 398 default: 399 masm.and64(Imm64((uint64_t(1) << shift) - 1), lhs); 400 break; 401 } 402 }; 403 404 Label negative; 405 406 if (canBeNegative) { 407 // Switch based on sign of the lhs. 408 // Positive numbers are just a bitmask 409 masm.branchTest64(Assembler::Signed, lhs, lhs, &negative); 410 } 411 412 clearHighBits(); 413 414 if (canBeNegative) { 415 Label done; 416 masm.jump(&done); 417 418 // Negative numbers need a negate, bitmask, negate 419 masm.bind(&negative); 420 421 // Unlike in the visitModI64 case, we are not computing the mod by means of 422 // a division. Therefore, the divisor = -1 case isn't problematic (the andq 423 // always returns 0, which is what we expect). 424 // 425 // The negq instruction overflows if lhs == INT64_MIN, but this is also not 426 // a problem: shift is at most 63, and so the andq also always returns 0. 427 masm.neg64(lhs); 428 clearHighBits(); 429 masm.neg64(lhs); 430 431 masm.bind(&done); 432 } 433 } 434 435 template <class LDivOrMod> 436 static void Divide64WithConstant(MacroAssembler& masm, LDivOrMod* ins) { 437 Register lhs = ToRegister(ins->numerator()); 438 [[maybe_unused]] Register output = ToRegister(ins->output()); 439 [[maybe_unused]] Register temp = ToRegister(ins->temp0()); 440 int64_t d = ins->denominator(); 441 442 MOZ_ASSERT(lhs != rax && lhs != rdx); 443 MOZ_ASSERT((output == rax && temp == rdx) || (output == rdx && temp == rax)); 444 445 // The absolute value of the denominator isn't a power of 2 (see LDivPowTwoI64 446 // and LModPowTwoI64). 447 MOZ_ASSERT(!mozilla::IsPowerOfTwo(mozilla::Abs(d))); 448 449 auto* mir = ins->mir(); 450 451 // We will first divide by Abs(d), and negate the answer if d is negative. 452 // If desired, this can be avoided by generalizing computeDivisionConstants. 453 auto rmc = ReciprocalMulConstants::computeSignedDivisionConstants(d); 454 455 // We first compute (M * n) >> 64, where M = rmc.multiplier. 456 masm.movq(ImmWord(uint64_t(rmc.multiplier)), rax); 457 masm.imulq(lhs); 458 if (rmc.multiplier > Int128(INT64_MAX)) { 459 MOZ_ASSERT(rmc.multiplier < (Int128(1) << 64)); 460 461 // We actually computed rdx = ((int64_t(M) * n) >> 64) instead. Since 462 // (M * n) >> 64 is the same as (rdx + n), we can correct for the overflow. 463 // (rdx + n) can't overflow, as n and rdx have opposite signs because 464 // int64_t(M) is negative. 465 masm.addq(lhs, rdx); 466 } 467 // (M * n) >> (64 + shift) is the truncated division answer if n is 468 // non-negative, as proved in the comments of computeDivisionConstants. We 469 // must add 1 later if n is negative to get the right answer in all cases. 470 if (rmc.shiftAmount > 0) { 471 masm.sarq(Imm32(rmc.shiftAmount), rdx); 472 } 473 474 // We'll subtract -1 instead of adding 1, because (n < 0 ? -1 : 0) can be 475 // computed with just a sign-extending shift of 63 bits. 476 if (mir->canBeNegativeDividend()) { 477 masm.movq(lhs, rax); 478 masm.sarq(Imm32(63), rax); 479 masm.subq(rax, rdx); 480 } 481 482 // After this, rdx contains the correct truncated division result. 483 if (d < 0) { 484 masm.negq(rdx); 485 } 486 } 487 488 void CodeGenerator::visitDivConstantI64(LDivConstantI64* ins) { 489 int32_t d = ins->denominator(); 490 491 // This emits the division answer into rdx. 492 MOZ_ASSERT(ToRegister(ins->output()) == rdx); 493 MOZ_ASSERT(ToRegister(ins->temp0()) == rax); 494 495 if (d == 0) { 496 masm.wasmTrap(wasm::Trap::IntegerDivideByZero, ins->mir()->trapSiteDesc()); 497 return; 498 } 499 500 // Compute the truncated division result in rdx. 501 Divide64WithConstant(masm, ins); 502 } 503 504 void CodeGenerator::visitModConstantI64(LModConstantI64* ins) { 505 Register lhs = ToRegister(ins->numerator()); 506 int64_t d = ins->denominator(); 507 508 // This emits the modulus answer into rax. 509 MOZ_ASSERT(ToRegister(ins->output()) == rax); 510 MOZ_ASSERT(ToRegister(ins->temp0()) == rdx); 511 512 if (d == 0) { 513 masm.wasmTrap(wasm::Trap::IntegerDivideByZero, ins->mir()->trapSiteDesc()); 514 return; 515 } 516 517 // Compute the truncated division result in rdx. 518 Divide64WithConstant(masm, ins); 519 520 // Compute the remainder in |rax|: rax = lhs - d * rdx 521 masm.mul64(Imm64(d), Register64(rdx)); 522 masm.movq(lhs, rax); 523 masm.subq(rdx, rax); 524 } 525 526 template <class LUDivOrUMod> 527 static void UnsignedDivide64WithConstant(MacroAssembler& masm, 528 LUDivOrUMod* ins) { 529 Register lhs = ToRegister(ins->numerator()); 530 [[maybe_unused]] Register output = ToRegister(ins->output()); 531 [[maybe_unused]] Register temp = ToRegister(ins->temp0()); 532 uint64_t d = ins->denominator(); 533 534 MOZ_ASSERT(lhs != rax && lhs != rdx); 535 MOZ_ASSERT((output == rax && temp == rdx) || (output == rdx && temp == rax)); 536 537 // The denominator isn't a power of 2 (see LDivPowTwoI and LModPowTwoI). 538 MOZ_ASSERT(!mozilla::IsPowerOfTwo(d)); 539 540 auto rmc = ReciprocalMulConstants::computeUnsignedDivisionConstants(d); 541 542 // We first compute (M * n) >> 64, where M = rmc.multiplier. 543 masm.movq(ImmWord(uint64_t(rmc.multiplier)), rax); 544 masm.umulq(lhs); 545 if (rmc.multiplier > Int128(UINT64_MAX)) { 546 // M >= 2^64 and shift == 0 is impossible, as d >= 2 implies that 547 // ((M * n) >> (64 + shift)) >= n > floor(n/d) whenever n >= d, 548 // contradicting the proof of correctness in computeDivisionConstants. 549 MOZ_ASSERT(rmc.shiftAmount > 0); 550 MOZ_ASSERT(rmc.multiplier < (Int128(1) << 65)); 551 552 // We actually computed rdx = ((uint64_t(M) * n) >> 64) instead. Since 553 // (M * n) >> (64 + shift) is the same as (rdx + n) >> shift, we can correct 554 // for the overflow. This case is a bit trickier than the signed case, 555 // though, as the (rdx + n) addition itself can overflow; however, note that 556 // (rdx + n) >> shift == (((n - rdx) >> 1) + rdx) >> (shift - 1), 557 // which is overflow-free. See Hacker's Delight, section 10-8 for details. 558 559 // Compute (n - rdx) >> 1 into temp. 560 masm.movq(lhs, rax); 561 masm.subq(rdx, rax); 562 masm.shrq(Imm32(1), rax); 563 564 // Finish the computation. 565 masm.addq(rax, rdx); 566 masm.shrq(Imm32(rmc.shiftAmount - 1), rdx); 567 } else { 568 if (rmc.shiftAmount > 0) { 569 masm.shrq(Imm32(rmc.shiftAmount), rdx); 570 } 571 } 572 } 573 574 void CodeGenerator::visitUDivConstantI64(LUDivConstantI64* ins) { 575 uint64_t d = ins->denominator(); 576 577 // This emits the division answer into rdx. 578 MOZ_ASSERT(ToRegister(ins->output()) == rdx); 579 MOZ_ASSERT(ToRegister(ins->temp0()) == rax); 580 581 if (d == 0) { 582 masm.wasmTrap(wasm::Trap::IntegerDivideByZero, ins->mir()->trapSiteDesc()); 583 return; 584 } 585 586 // Compute the truncated division result in rdx. 587 UnsignedDivide64WithConstant(masm, ins); 588 } 589 590 void CodeGenerator::visitUModConstantI64(LUModConstantI64* ins) { 591 Register lhs = ToRegister(ins->numerator()); 592 uint64_t d = ins->denominator(); 593 594 // This emits the modulus answer into rax. 595 MOZ_ASSERT(ToRegister(ins->output()) == rax); 596 MOZ_ASSERT(ToRegister(ins->temp0()) == rdx); 597 598 if (d == 0) { 599 masm.wasmTrap(wasm::Trap::IntegerDivideByZero, ins->mir()->trapSiteDesc()); 600 return; 601 } 602 603 // Compute the truncated division result in rdx. 604 UnsignedDivide64WithConstant(masm, ins); 605 606 // Compute the remainder in |rax|: rax = lhs - d * rdx 607 masm.mul64(Imm64(d), Register64(rdx)); 608 masm.movq(lhs, rax); 609 masm.subq(rdx, rax); 610 } 611 612 void CodeGeneratorX64::emitBigIntPtrDiv(LBigIntPtrDiv* ins, Register dividend, 613 Register divisor, Register output) { 614 // Callers handle division by zero and integer overflow. 615 616 MOZ_ASSERT(ToRegister(ins->temp0()) == rdx); 617 MOZ_ASSERT(output == rax); 618 619 if (dividend != rax) { 620 masm.movePtr(dividend, rax); 621 } 622 623 // Sign extend the lhs into rdx to make rdx:rax. 624 masm.cqo(); 625 626 masm.idivq(divisor); 627 } 628 629 void CodeGeneratorX64::emitBigIntPtrMod(LBigIntPtrMod* ins, Register dividend, 630 Register divisor, Register output) { 631 // Callers handle division by zero and integer overflow. 632 633 MOZ_ASSERT(dividend == rax); 634 MOZ_ASSERT(output == rdx); 635 636 // Sign extend the lhs into rdx to make rdx:rax. 637 masm.cqo(); 638 639 masm.idivq(divisor); 640 } 641 642 void CodeGenerator::visitShiftIntPtr(LShiftIntPtr* ins) { 643 Register lhs = ToRegister(ins->lhs()); 644 const LAllocation* rhs = ins->rhs(); 645 Register out = ToRegister(ins->output()); 646 647 if (rhs->isConstant()) { 648 MOZ_ASSERT(out == lhs); 649 650 int32_t shift = ToIntPtr(rhs) & 0x3f; 651 switch (ins->bitop()) { 652 case JSOp::Lsh: 653 if (shift) { 654 masm.lshiftPtr(Imm32(shift), lhs); 655 } 656 break; 657 case JSOp::Rsh: 658 if (shift) { 659 masm.rshiftPtrArithmetic(Imm32(shift), lhs); 660 } 661 break; 662 case JSOp::Ursh: 663 if (shift) { 664 masm.rshiftPtr(Imm32(shift), lhs); 665 } 666 break; 667 default: 668 MOZ_CRASH("Unexpected shift op"); 669 } 670 } else { 671 Register shift = ToRegister(rhs); 672 MOZ_ASSERT_IF(out != lhs, Assembler::HasBMI2()); 673 674 switch (ins->bitop()) { 675 case JSOp::Lsh: 676 if (out != lhs) { 677 masm.shlxq(lhs, shift, out); 678 } else { 679 masm.lshiftPtr(shift, lhs); 680 } 681 break; 682 case JSOp::Rsh: 683 if (out != lhs) { 684 masm.sarxq(lhs, shift, out); 685 } else { 686 masm.rshiftPtrArithmetic(shift, lhs); 687 } 688 break; 689 case JSOp::Ursh: 690 if (out != lhs) { 691 masm.shrxq(lhs, shift, out); 692 } else { 693 masm.rshiftPtr(shift, lhs); 694 } 695 break; 696 default: 697 MOZ_CRASH("Unexpected shift op"); 698 } 699 } 700 } 701 702 void CodeGenerator::visitShiftI64(LShiftI64* lir) { 703 Register lhs = ToRegister64(lir->lhs()).reg; 704 const LAllocation* rhs = lir->rhs(); 705 Register out = ToOutRegister64(lir).reg; 706 707 if (rhs->isConstant()) { 708 MOZ_ASSERT(out == lhs); 709 710 int32_t shift = int32_t(rhs->toConstant()->toInt64() & 0x3F); 711 switch (lir->bitop()) { 712 case JSOp::Lsh: 713 if (shift) { 714 masm.lshiftPtr(Imm32(shift), lhs); 715 } 716 break; 717 case JSOp::Rsh: 718 if (shift) { 719 masm.rshiftPtrArithmetic(Imm32(shift), lhs); 720 } 721 break; 722 case JSOp::Ursh: 723 if (shift) { 724 masm.rshiftPtr(Imm32(shift), lhs); 725 } 726 break; 727 default: 728 MOZ_CRASH("Unexpected shift op"); 729 } 730 return; 731 } 732 733 Register shift = ToRegister(rhs); 734 MOZ_ASSERT_IF(out != lhs, Assembler::HasBMI2()); 735 736 switch (lir->bitop()) { 737 case JSOp::Lsh: 738 if (out != lhs) { 739 masm.shlxq(lhs, shift, out); 740 } else { 741 masm.lshiftPtr(shift, lhs); 742 } 743 break; 744 case JSOp::Rsh: 745 if (out != lhs) { 746 masm.sarxq(lhs, shift, out); 747 } else { 748 masm.rshiftPtrArithmetic(shift, lhs); 749 } 750 break; 751 case JSOp::Ursh: 752 if (out != lhs) { 753 masm.shrxq(lhs, shift, out); 754 } else { 755 masm.rshiftPtr(shift, lhs); 756 } 757 break; 758 default: 759 MOZ_CRASH("Unexpected shift op"); 760 } 761 } 762 763 void CodeGenerator::visitAtomicLoad64(LAtomicLoad64* lir) { 764 Register elements = ToRegister(lir->elements()); 765 Register64 out = ToOutRegister64(lir); 766 767 Scalar::Type storageType = lir->mir()->storageType(); 768 769 auto source = ToAddressOrBaseIndex(elements, lir->index(), storageType); 770 771 // NOTE: the generated code must match the assembly code in gen_load in 772 // GenerateAtomicOperations.py 773 auto sync = Synchronization::Load(); 774 775 masm.memoryBarrierBefore(sync); 776 source.match([&](const auto& source) { masm.load64(source, out); }); 777 masm.memoryBarrierAfter(sync); 778 } 779 780 void CodeGenerator::visitAtomicStore64(LAtomicStore64* lir) { 781 Register elements = ToRegister(lir->elements()); 782 Register64 value = ToRegister64(lir->value()); 783 784 Scalar::Type writeType = lir->mir()->writeType(); 785 786 auto dest = ToAddressOrBaseIndex(elements, lir->index(), writeType); 787 788 // NOTE: the generated code must match the assembly code in gen_store in 789 // GenerateAtomicOperations.py 790 auto sync = Synchronization::Store(); 791 792 masm.memoryBarrierBefore(sync); 793 dest.match([&](const auto& dest) { masm.store64(value, dest); }); 794 masm.memoryBarrierAfter(sync); 795 } 796 797 void CodeGenerator::visitCompareExchangeTypedArrayElement64( 798 LCompareExchangeTypedArrayElement64* lir) { 799 Register elements = ToRegister(lir->elements()); 800 Register64 oldval = ToRegister64(lir->oldval()); 801 Register64 newval = ToRegister64(lir->newval()); 802 Register64 out = ToOutRegister64(lir); 803 804 MOZ_ASSERT(out.reg == rax); 805 806 Scalar::Type arrayType = lir->mir()->arrayType(); 807 808 auto dest = ToAddressOrBaseIndex(elements, lir->index(), arrayType); 809 810 dest.match([&](const auto& dest) { 811 masm.compareExchange64(Synchronization::Full(), dest, oldval, newval, out); 812 }); 813 } 814 815 void CodeGenerator::visitAtomicExchangeTypedArrayElement64( 816 LAtomicExchangeTypedArrayElement64* lir) { 817 Register elements = ToRegister(lir->elements()); 818 Register64 value = ToRegister64(lir->value()); 819 Register64 out = ToOutRegister64(lir); 820 821 Scalar::Type arrayType = lir->mir()->arrayType(); 822 823 auto dest = ToAddressOrBaseIndex(elements, lir->index(), arrayType); 824 825 dest.match([&](const auto& dest) { 826 masm.atomicExchange64(Synchronization::Full(), dest, value, out); 827 }); 828 } 829 830 void CodeGenerator::visitAtomicTypedArrayElementBinop64( 831 LAtomicTypedArrayElementBinop64* lir) { 832 MOZ_ASSERT(!lir->mir()->isForEffect()); 833 834 Register elements = ToRegister(lir->elements()); 835 Register64 value = ToRegister64(lir->value()); 836 Register64 temp = ToTempRegister64OrInvalid(lir->temp0()); 837 Register64 out = ToOutRegister64(lir); 838 839 Scalar::Type arrayType = lir->mir()->arrayType(); 840 AtomicOp atomicOp = lir->mir()->operation(); 841 842 // Add and Sub don't need |temp| and can save a `mov` when the value and 843 // output register are equal to each other. 844 if (atomicOp == AtomicOp::Add || atomicOp == AtomicOp::Sub) { 845 MOZ_ASSERT(temp == Register64::Invalid()); 846 MOZ_ASSERT(value == out); 847 } else { 848 MOZ_ASSERT(out.reg == rax); 849 } 850 851 auto dest = ToAddressOrBaseIndex(elements, lir->index(), arrayType); 852 853 dest.match([&](const auto& dest) { 854 masm.atomicFetchOp64(Synchronization::Full(), atomicOp, value, dest, temp, 855 out); 856 }); 857 } 858 859 void CodeGenerator::visitAtomicTypedArrayElementBinopForEffect64( 860 LAtomicTypedArrayElementBinopForEffect64* lir) { 861 MOZ_ASSERT(lir->mir()->isForEffect()); 862 863 Register elements = ToRegister(lir->elements()); 864 Register64 value = ToRegister64(lir->value()); 865 866 Scalar::Type arrayType = lir->mir()->arrayType(); 867 AtomicOp atomicOp = lir->mir()->operation(); 868 869 auto dest = ToAddressOrBaseIndex(elements, lir->index(), arrayType); 870 871 dest.match([&](const auto& dest) { 872 masm.atomicEffectOp64(Synchronization::Full(), atomicOp, value, dest); 873 }); 874 } 875 876 void CodeGenerator::visitWasmSelectI64(LWasmSelectI64* lir) { 877 MOZ_ASSERT(lir->mir()->type() == MIRType::Int64); 878 879 Register cond = ToRegister(lir->condExpr()); 880 881 Operand falseExpr = ToOperandOrRegister64(lir->falseExpr()); 882 883 Register64 out = ToOutRegister64(lir); 884 MOZ_ASSERT(ToRegister64(lir->trueExpr()) == out, 885 "true expr is reused for input"); 886 887 masm.test32(cond, cond); 888 masm.cmovzq(falseExpr, out.reg); 889 } 890 891 // We expect to handle only the cases: compare is {U,}Int{32,64}, and select 892 // is {U,}Int{32,64}, independently. Some values may be stack allocated, and 893 // the "true" input is reused for the output. 894 void CodeGenerator::visitWasmCompareAndSelect(LWasmCompareAndSelect* ins) { 895 bool cmpIs32bit = ins->compareType() == MCompare::Compare_Int32 || 896 ins->compareType() == MCompare::Compare_UInt32; 897 bool cmpIs64bit = ins->compareType() == MCompare::Compare_Int64 || 898 ins->compareType() == MCompare::Compare_UInt64; 899 bool selIs32bit = ins->mir()->type() == MIRType::Int32; 900 bool selIs64bit = ins->mir()->type() == MIRType::Int64; 901 902 // Throw out unhandled cases 903 MOZ_RELEASE_ASSERT( 904 cmpIs32bit != cmpIs64bit && selIs32bit != selIs64bit, 905 "CodeGenerator::visitWasmCompareAndSelect: unexpected types"); 906 907 using C = Assembler::Condition; 908 using R = Register; 909 using A = const Address&; 910 911 // Identify macroassembler methods to generate instructions, based on the 912 // type of the comparison and the select. This avoids having to duplicate 913 // the code-generation tree below 4 times. These assignments to 914 // `cmpMove_CRRRR` et al are unambiguous as a result of the combination of 915 // the template parameters and the 5 argument types ((C, R, R, R, R) etc). 916 void (MacroAssembler::*cmpMove_CRRRR)(C, R, R, R, R) = nullptr; 917 void (MacroAssembler::*cmpMove_CRARR)(C, R, A, R, R) = nullptr; 918 void (MacroAssembler::*cmpLoad_CRRAR)(C, R, R, A, R) = nullptr; 919 void (MacroAssembler::*cmpLoad_CRAAR)(C, R, A, A, R) = nullptr; 920 921 if (cmpIs32bit) { 922 if (selIs32bit) { 923 cmpMove_CRRRR = &MacroAssemblerX64::cmpMove<32, 32>; 924 cmpMove_CRARR = &MacroAssemblerX64::cmpMove<32, 32>; 925 cmpLoad_CRRAR = &MacroAssemblerX64::cmpLoad<32, 32>; 926 cmpLoad_CRAAR = &MacroAssemblerX64::cmpLoad<32, 32>; 927 } else { 928 cmpMove_CRRRR = &MacroAssemblerX64::cmpMove<32, 64>; 929 cmpMove_CRARR = &MacroAssemblerX64::cmpMove<32, 64>; 930 cmpLoad_CRRAR = &MacroAssemblerX64::cmpLoad<32, 64>; 931 cmpLoad_CRAAR = &MacroAssemblerX64::cmpLoad<32, 64>; 932 } 933 } else { 934 if (selIs32bit) { 935 cmpMove_CRRRR = &MacroAssemblerX64::cmpMove<64, 32>; 936 cmpMove_CRARR = &MacroAssemblerX64::cmpMove<64, 32>; 937 cmpLoad_CRRAR = &MacroAssemblerX64::cmpLoad<64, 32>; 938 cmpLoad_CRAAR = &MacroAssemblerX64::cmpLoad<64, 32>; 939 } else { 940 cmpMove_CRRRR = &MacroAssemblerX64::cmpMove<64, 64>; 941 cmpMove_CRARR = &MacroAssemblerX64::cmpMove<64, 64>; 942 cmpLoad_CRRAR = &MacroAssemblerX64::cmpLoad<64, 64>; 943 cmpLoad_CRAAR = &MacroAssemblerX64::cmpLoad<64, 64>; 944 } 945 } 946 947 Register trueExprAndDest = ToRegister(ins->output()); 948 MOZ_ASSERT(ToRegister(ins->ifTrueExpr()) == trueExprAndDest, 949 "true expr input is reused for output"); 950 951 Assembler::Condition cond = Assembler::InvertCondition( 952 JSOpToCondition(ins->compareType(), ins->jsop())); 953 const LAllocation* rhs = ins->rightExpr(); 954 const LAllocation* falseExpr = ins->ifFalseExpr(); 955 Register lhs = ToRegister(ins->leftExpr()); 956 957 // We generate one of four cmp+cmov pairings, depending on whether one of 958 // the cmp args and one of the cmov args is in memory or a register. 959 if (rhs->isGeneralReg()) { 960 if (falseExpr->isGeneralReg()) { 961 (masm.*cmpMove_CRRRR)(cond, lhs, ToRegister(rhs), ToRegister(falseExpr), 962 trueExprAndDest); 963 } else { 964 (masm.*cmpLoad_CRRAR)(cond, lhs, ToRegister(rhs), ToAddress(falseExpr), 965 trueExprAndDest); 966 } 967 } else { 968 if (falseExpr->isGeneralReg()) { 969 (masm.*cmpMove_CRARR)(cond, lhs, ToAddress(rhs), ToRegister(falseExpr), 970 trueExprAndDest); 971 } else { 972 (masm.*cmpLoad_CRAAR)(cond, lhs, ToAddress(rhs), ToAddress(falseExpr), 973 trueExprAndDest); 974 } 975 } 976 } 977 978 void CodeGenerator::visitWasmUint32ToDouble(LWasmUint32ToDouble* lir) { 979 masm.convertUInt32ToDouble(ToRegister(lir->input()), 980 ToFloatRegister(lir->output())); 981 } 982 983 void CodeGenerator::visitWasmUint32ToFloat32(LWasmUint32ToFloat32* lir) { 984 masm.convertUInt32ToFloat32(ToRegister(lir->input()), 985 ToFloatRegister(lir->output())); 986 } 987 988 void CodeGeneratorX64::wasmStore(const wasm::MemoryAccessDesc& access, 989 const LAllocation* value, Operand dstAddr) { 990 if (value->isConstant()) { 991 masm.memoryBarrierBefore(access.sync()); 992 993 const MConstant* mir = value->toConstant(); 994 Imm32 cst = 995 Imm32(mir->type() == MIRType::Int32 ? mir->toInt32() : mir->toInt64()); 996 997 switch (access.type()) { 998 case Scalar::Int8: 999 case Scalar::Uint8: 1000 masm.append(access, wasm::TrapMachineInsn::Store8, 1001 FaultingCodeOffset(masm.currentOffset())); 1002 masm.movb(cst, dstAddr); 1003 break; 1004 case Scalar::Int16: 1005 case Scalar::Uint16: 1006 masm.append(access, wasm::TrapMachineInsn::Store16, 1007 FaultingCodeOffset(masm.currentOffset())); 1008 masm.movw(cst, dstAddr); 1009 break; 1010 case Scalar::Int32: 1011 case Scalar::Uint32: 1012 masm.append(access, wasm::TrapMachineInsn::Store32, 1013 FaultingCodeOffset(masm.currentOffset())); 1014 masm.movl(cst, dstAddr); 1015 break; 1016 case Scalar::Int64: 1017 MOZ_ASSERT_IF(mir->type() == MIRType::Int64, 1018 mozilla::CheckedInt32(mir->toInt64()).isValid()); 1019 masm.append(access, wasm::TrapMachineInsn::Store64, 1020 FaultingCodeOffset(masm.currentOffset())); 1021 masm.movq(cst, dstAddr); 1022 break; 1023 case Scalar::Simd128: 1024 case Scalar::Float16: 1025 case Scalar::Float32: 1026 case Scalar::Float64: 1027 case Scalar::Uint8Clamped: 1028 case Scalar::BigInt64: 1029 case Scalar::BigUint64: 1030 case Scalar::MaxTypedArrayViewType: 1031 MOZ_CRASH("unexpected array type"); 1032 } 1033 1034 masm.memoryBarrierAfter(access.sync()); 1035 } else { 1036 masm.wasmStore(access, ToAnyRegister(value), dstAddr); 1037 } 1038 } 1039 1040 template <typename T> 1041 void CodeGeneratorX64::emitWasmLoad(T* ins) { 1042 const MWasmLoad* mir = ins->mir(); 1043 1044 mir->access().assertOffsetInGuardPages(); 1045 uint32_t offset = mir->access().offset32(); 1046 1047 // ptr is a GPR and is either a 32-bit value zero-extended to 64-bit, or a 1048 // true 64-bit value. 1049 const LAllocation* ptr = ins->ptr(); 1050 Register memoryBase = ToRegister(ins->memoryBase()); 1051 Operand srcAddr = 1052 ptr->isBogus() ? Operand(memoryBase, offset) 1053 : Operand(memoryBase, ToRegister(ptr), TimesOne, offset); 1054 1055 if (mir->type() == MIRType::Int64) { 1056 masm.wasmLoadI64(mir->access(), srcAddr, ToOutRegister64(ins)); 1057 } else { 1058 masm.wasmLoad(mir->access(), srcAddr, ToAnyRegister(ins->output())); 1059 } 1060 } 1061 1062 void CodeGenerator::visitWasmLoad(LWasmLoad* ins) { emitWasmLoad(ins); } 1063 1064 void CodeGenerator::visitWasmLoadI64(LWasmLoadI64* ins) { emitWasmLoad(ins); } 1065 1066 template <typename T> 1067 void CodeGeneratorX64::emitWasmStore(T* ins) { 1068 const MWasmStore* mir = ins->mir(); 1069 const wasm::MemoryAccessDesc& access = mir->access(); 1070 1071 mir->access().assertOffsetInGuardPages(); 1072 uint32_t offset = access.offset32(); 1073 1074 const LAllocation* value = ins->value(); 1075 const LAllocation* ptr = ins->ptr(); 1076 Register memoryBase = ToRegister(ins->memoryBase()); 1077 Operand dstAddr = 1078 ptr->isBogus() ? Operand(memoryBase, offset) 1079 : Operand(memoryBase, ToRegister(ptr), TimesOne, offset); 1080 1081 wasmStore(access, value, dstAddr); 1082 } 1083 1084 void CodeGenerator::visitWasmStore(LWasmStore* ins) { emitWasmStore(ins); } 1085 1086 void CodeGenerator::visitWasmStoreI64(LWasmStoreI64* ins) { 1087 MOZ_CRASH("Unused on this platform"); 1088 } 1089 1090 void CodeGenerator::visitWasmCompareExchangeHeap( 1091 LWasmCompareExchangeHeap* ins) { 1092 MWasmCompareExchangeHeap* mir = ins->mir(); 1093 1094 Register ptr = ToRegister(ins->ptr()); 1095 Register oldval = ToRegister(ins->oldValue()); 1096 Register newval = ToRegister(ins->newValue()); 1097 Register memoryBase = ToRegister(ins->memoryBase()); 1098 1099 Scalar::Type accessType = mir->access().type(); 1100 BaseIndex srcAddr(memoryBase, ptr, TimesOne, mir->access().offset32()); 1101 1102 if (accessType == Scalar::Int64) { 1103 masm.wasmCompareExchange64(mir->access(), srcAddr, Register64(oldval), 1104 Register64(newval), ToOutRegister64(ins)); 1105 } else { 1106 masm.wasmCompareExchange(mir->access(), srcAddr, oldval, newval, 1107 ToRegister(ins->output())); 1108 } 1109 } 1110 1111 void CodeGenerator::visitWasmAtomicExchangeHeap(LWasmAtomicExchangeHeap* ins) { 1112 MWasmAtomicExchangeHeap* mir = ins->mir(); 1113 1114 Register ptr = ToRegister(ins->ptr()); 1115 Register value = ToRegister(ins->value()); 1116 Register memoryBase = ToRegister(ins->memoryBase()); 1117 1118 Scalar::Type accessType = mir->access().type(); 1119 1120 BaseIndex srcAddr(memoryBase, ptr, TimesOne, mir->access().offset32()); 1121 1122 if (accessType == Scalar::Int64) { 1123 masm.wasmAtomicExchange64(mir->access(), srcAddr, Register64(value), 1124 ToOutRegister64(ins)); 1125 } else { 1126 masm.wasmAtomicExchange(mir->access(), srcAddr, value, 1127 ToRegister(ins->output())); 1128 } 1129 } 1130 1131 void CodeGenerator::visitWasmAtomicBinopHeap(LWasmAtomicBinopHeap* ins) { 1132 MWasmAtomicBinopHeap* mir = ins->mir(); 1133 MOZ_ASSERT(mir->hasUses()); 1134 1135 Register ptr = ToRegister(ins->ptr()); 1136 Register memoryBase = ToRegister(ins->memoryBase()); 1137 const LAllocation* value = ins->value(); 1138 Register temp = ToTempRegisterOrInvalid(ins->temp0()); 1139 Register output = ToRegister(ins->output()); 1140 1141 Scalar::Type accessType = mir->access().type(); 1142 if (accessType == Scalar::Uint32) { 1143 accessType = Scalar::Int32; 1144 } 1145 1146 AtomicOp op = mir->operation(); 1147 BaseIndex srcAddr(memoryBase, ptr, TimesOne, mir->access().offset32()); 1148 1149 if (accessType == Scalar::Int64) { 1150 Register64 val = Register64(ToRegister(value)); 1151 Register64 out = Register64(output); 1152 Register64 tmp = Register64(temp); 1153 masm.wasmAtomicFetchOp64(mir->access(), op, val, srcAddr, tmp, out); 1154 } else if (value->isConstant()) { 1155 masm.wasmAtomicFetchOp(mir->access(), op, Imm32(ToInt32(value)), srcAddr, 1156 temp, output); 1157 } else { 1158 masm.wasmAtomicFetchOp(mir->access(), op, ToRegister(value), srcAddr, temp, 1159 output); 1160 } 1161 } 1162 1163 void CodeGenerator::visitWasmAtomicBinopHeapForEffect( 1164 LWasmAtomicBinopHeapForEffect* ins) { 1165 MWasmAtomicBinopHeap* mir = ins->mir(); 1166 MOZ_ASSERT(!mir->hasUses()); 1167 1168 Register ptr = ToRegister(ins->ptr()); 1169 Register memoryBase = ToRegister(ins->memoryBase()); 1170 const LAllocation* value = ins->value(); 1171 1172 Scalar::Type accessType = mir->access().type(); 1173 AtomicOp op = mir->operation(); 1174 1175 BaseIndex srcAddr(memoryBase, ptr, TimesOne, mir->access().offset32()); 1176 1177 if (accessType == Scalar::Int64) { 1178 Register64 val = Register64(ToRegister(value)); 1179 masm.wasmAtomicEffectOp64(mir->access(), op, val, srcAddr); 1180 } else if (value->isConstant()) { 1181 Imm32 c(0); 1182 if (value->toConstant()->type() == MIRType::Int64) { 1183 c = Imm32(ToInt64(value)); 1184 } else { 1185 c = Imm32(ToInt32(value)); 1186 } 1187 masm.wasmAtomicEffectOp(mir->access(), op, c, srcAddr, InvalidReg); 1188 } else { 1189 masm.wasmAtomicEffectOp(mir->access(), op, ToRegister(value), srcAddr, 1190 InvalidReg); 1191 } 1192 } 1193 1194 class js::jit::OutOfLineTruncate : public OutOfLineCodeBase<CodeGeneratorX64> { 1195 FloatRegister input_; 1196 Register output_; 1197 Register temp_; 1198 1199 public: 1200 OutOfLineTruncate(FloatRegister input, Register output, Register temp) 1201 : input_(input), output_(output), temp_(temp) {} 1202 1203 void accept(CodeGeneratorX64* codegen) override { 1204 codegen->visitOutOfLineTruncate(this); 1205 } 1206 1207 FloatRegister input() const { return input_; } 1208 Register output() const { return output_; } 1209 Register temp() const { return temp_; } 1210 }; 1211 1212 void CodeGeneratorX64::visitOutOfLineTruncate(OutOfLineTruncate* ool) { 1213 FloatRegister input = ool->input(); 1214 Register output = ool->output(); 1215 Register temp = ool->temp(); 1216 1217 // Inline implementation of `JS::ToInt32(double)` for double values whose 1218 // exponent is ≥63. 1219 1220 #ifdef DEBUG 1221 Label ok; 1222 masm.branchTruncateDoubleMaybeModUint32(input, output, &ok); 1223 masm.assumeUnreachable("OOL path only used when vcvttsd2sq failed"); 1224 masm.bind(&ok); 1225 #endif 1226 1227 constexpr uint32_t ShiftedExponentBits = 1228 mozilla::FloatingPoint<double>::kExponentBits >> 1229 mozilla::FloatingPoint<double>::kExponentShift; 1230 static_assert(ShiftedExponentBits == 0x7ff); 1231 1232 constexpr uint32_t ExponentBiasAndShift = 1233 mozilla::FloatingPoint<double>::kExponentBias + 1234 mozilla::FloatingPoint<double>::kExponentShift; 1235 static_assert(ExponentBiasAndShift == (1023 + 52)); 1236 1237 constexpr size_t ResultWidth = CHAR_BIT * sizeof(int32_t); 1238 1239 // Extract the bit representation of |input|. 1240 masm.moveDoubleToGPR64(input, Register64(output)); 1241 1242 // Extract the exponent. 1243 masm.rshiftPtr(Imm32(mozilla::FloatingPoint<double>::kExponentShift), output, 1244 temp); 1245 masm.and32(Imm32(ShiftedExponentBits), temp); 1246 #ifdef DEBUG 1247 // The biased exponent must be at least `1023 + 63`, because otherwise 1248 // vcvttsd2sq wouldn't have failed. 1249 constexpr uint32_t MinBiasedExponent = 1250 mozilla::FloatingPoint<double>::kExponentBias + 63; 1251 1252 Label exponentOk; 1253 masm.branch32(Assembler::GreaterThanOrEqual, temp, Imm32(MinBiasedExponent), 1254 &exponentOk); 1255 masm.assumeUnreachable("exponent is greater-than-or-equals to 63"); 1256 masm.bind(&exponentOk); 1257 #endif 1258 masm.sub32(Imm32(ExponentBiasAndShift), temp); 1259 1260 // If the exponent is greater than or equal to |ResultWidth|, the number is 1261 // either infinite, NaN, or too large to have lower-order bits. We have to 1262 // return zero in this case. 1263 { 1264 ScratchRegisterScope scratch(masm); 1265 masm.movePtr(ImmWord(0), scratch); 1266 masm.cmp32MovePtr(Assembler::AboveOrEqual, temp, Imm32(ResultWidth), 1267 scratch, output); 1268 } 1269 1270 // Negate if the sign bit is set. 1271 { 1272 ScratchRegisterScope scratch(masm); 1273 masm.movePtr(output, scratch); 1274 masm.negPtr(scratch); 1275 masm.testPtr(output, output); 1276 masm.cmovCCq(Assembler::Signed, scratch, output); 1277 } 1278 1279 // The significand contains the bits that will determine the final result. 1280 // Shift those bits left by the exponent value in |temp|. 1281 masm.lshift32(temp, output); 1282 1283 // Return from OOL path. 1284 masm.jump(ool->rejoin()); 1285 } 1286 1287 void CodeGenerator::visitTruncateDToInt32(LTruncateDToInt32* ins) { 1288 FloatRegister input = ToFloatRegister(ins->input()); 1289 Register output = ToRegister(ins->output()); 1290 Register temp = ToRegister(ins->temp0()); 1291 1292 auto* ool = new (alloc()) OutOfLineTruncate(input, output, temp); 1293 addOutOfLineCode(ool, ins->mir()); 1294 1295 masm.branchTruncateDoubleMaybeModUint32(input, output, ool->entry()); 1296 masm.bind(ool->rejoin()); 1297 } 1298 1299 void CodeGenerator::visitWasmBuiltinTruncateDToInt32( 1300 LWasmBuiltinTruncateDToInt32* lir) { 1301 FloatRegister input = ToFloatRegister(lir->input()); 1302 Register output = ToRegister(lir->output()); 1303 Register temp = ToRegister(lir->temp0()); 1304 MOZ_ASSERT(lir->instance()->isBogus(), "instance not used for x64"); 1305 1306 auto* ool = new (alloc()) OutOfLineTruncate(input, output, temp); 1307 addOutOfLineCode(ool, lir->mir()); 1308 1309 masm.branchTruncateDoubleMaybeModUint32(input, output, ool->entry()); 1310 masm.bind(ool->rejoin()); 1311 } 1312 1313 void CodeGenerator::visitWasmBuiltinTruncateFToInt32( 1314 LWasmBuiltinTruncateFToInt32* lir) { 1315 FloatRegister input = ToFloatRegister(lir->input()); 1316 Register output = ToRegister(lir->output()); 1317 MOZ_ASSERT(lir->instance()->isBogus(), "instance not used for x64"); 1318 1319 masm.truncateFloat32ModUint32(input, output); 1320 } 1321 1322 void CodeGenerator::visitTruncateFToInt32(LTruncateFToInt32* ins) { 1323 FloatRegister input = ToFloatRegister(ins->input()); 1324 Register output = ToRegister(ins->output()); 1325 1326 masm.truncateFloat32ModUint32(input, output); 1327 } 1328 1329 void CodeGenerator::visitWrapInt64ToInt32(LWrapInt64ToInt32* lir) { 1330 LInt64Allocation input = lir->input(); 1331 Register output = ToRegister(lir->output()); 1332 1333 if (lir->mir()->bottomHalf()) { 1334 if (input.value().isMemory()) { 1335 masm.load32(ToAddress(input), output); 1336 } else { 1337 masm.move64To32(ToRegister64(input), output); 1338 } 1339 } else { 1340 MOZ_CRASH("Not implemented."); 1341 } 1342 } 1343 1344 void CodeGenerator::visitExtendInt32ToInt64(LExtendInt32ToInt64* lir) { 1345 const LAllocation* input = lir->input(); 1346 Register output = ToRegister(lir->output()); 1347 1348 if (lir->mir()->isUnsigned()) { 1349 masm.movl(ToOperand(input), output); 1350 } else { 1351 masm.movslq(ToOperand(input), output); 1352 } 1353 } 1354 1355 void CodeGenerator::visitWasmExtendU32Index(LWasmExtendU32Index* lir) { 1356 // Generates no code on this platform because the input is assumed to have 1357 // canonical form. 1358 Register output = ToRegister(lir->output()); 1359 MOZ_ASSERT(ToRegister(lir->input()) == output); 1360 masm.debugAssertCanonicalInt32(output); 1361 } 1362 1363 void CodeGenerator::visitWasmWrapU32Index(LWasmWrapU32Index* lir) { 1364 // Generates no code on this platform because the input is assumed to have 1365 // canonical form. 1366 Register output = ToRegister(lir->output()); 1367 MOZ_ASSERT(ToRegister(lir->input()) == output); 1368 masm.debugAssertCanonicalInt32(output); 1369 } 1370 1371 void CodeGenerator::visitSignExtendInt64(LSignExtendInt64* ins) { 1372 Register64 input = ToRegister64(ins->input()); 1373 Register64 output = ToOutRegister64(ins); 1374 switch (ins->mir()->mode()) { 1375 case MSignExtendInt64::Byte: 1376 masm.movsbq(Operand(input.reg), output.reg); 1377 break; 1378 case MSignExtendInt64::Half: 1379 masm.movswq(Operand(input.reg), output.reg); 1380 break; 1381 case MSignExtendInt64::Word: 1382 masm.movslq(Operand(input.reg), output.reg); 1383 break; 1384 } 1385 } 1386 1387 void CodeGenerator::visitWasmTruncateToInt64(LWasmTruncateToInt64* lir) { 1388 FloatRegister input = ToFloatRegister(lir->input()); 1389 Register64 output = ToOutRegister64(lir); 1390 1391 MWasmTruncateToInt64* mir = lir->mir(); 1392 MIRType inputType = mir->input()->type(); 1393 1394 MOZ_ASSERT(inputType == MIRType::Double || inputType == MIRType::Float32); 1395 1396 auto* ool = new (alloc()) OutOfLineWasmTruncateCheck(mir, input, output); 1397 addOutOfLineCode(ool, mir); 1398 1399 FloatRegister temp = 1400 mir->isUnsigned() ? ToFloatRegister(lir->temp0()) : InvalidFloatReg; 1401 1402 Label* oolEntry = ool->entry(); 1403 Label* oolRejoin = ool->rejoin(); 1404 bool isSaturating = mir->isSaturating(); 1405 if (inputType == MIRType::Double) { 1406 if (mir->isUnsigned()) { 1407 masm.wasmTruncateDoubleToUInt64(input, output, isSaturating, oolEntry, 1408 oolRejoin, temp); 1409 } else { 1410 masm.wasmTruncateDoubleToInt64(input, output, isSaturating, oolEntry, 1411 oolRejoin, temp); 1412 } 1413 } else { 1414 if (mir->isUnsigned()) { 1415 masm.wasmTruncateFloat32ToUInt64(input, output, isSaturating, oolEntry, 1416 oolRejoin, temp); 1417 } else { 1418 masm.wasmTruncateFloat32ToInt64(input, output, isSaturating, oolEntry, 1419 oolRejoin, temp); 1420 } 1421 } 1422 } 1423 1424 void CodeGenerator::visitInt64ToFloatingPoint(LInt64ToFloatingPoint* lir) { 1425 Register64 input = ToRegister64(lir->input()); 1426 FloatRegister output = ToFloatRegister(lir->output()); 1427 Register temp = ToTempRegisterOrInvalid(lir->temp0()); 1428 1429 MInt64ToFloatingPoint* mir = lir->mir(); 1430 bool isUnsigned = mir->isUnsigned(); 1431 1432 MIRType outputType = mir->type(); 1433 MOZ_ASSERT(outputType == MIRType::Double || outputType == MIRType::Float32); 1434 MOZ_ASSERT(isUnsigned == (temp != InvalidReg)); 1435 1436 if (outputType == MIRType::Double) { 1437 if (isUnsigned) { 1438 masm.convertUInt64ToDouble(input, output, temp); 1439 } else { 1440 masm.convertInt64ToDouble(input, output); 1441 } 1442 } else { 1443 if (isUnsigned) { 1444 masm.convertUInt64ToFloat32(input, output, temp); 1445 } else { 1446 masm.convertInt64ToFloat32(input, output); 1447 } 1448 } 1449 } 1450 1451 void CodeGenerator::visitBitNotI64(LBitNotI64* ins) { 1452 LInt64Allocation input = ins->input(); 1453 MOZ_ASSERT(!IsConstant(input)); 1454 Register64 inputR = ToRegister64(input); 1455 MOZ_ASSERT(inputR == ToOutRegister64(ins)); 1456 masm.notq(inputR.reg); 1457 } 1458 1459 void CodeGenerator::visitAddIntPtr(LAddIntPtr* ins) { 1460 Register lhs = ToRegister(ins->lhs()); 1461 MOZ_ASSERT(ToRegister(ins->output()) == lhs); 1462 1463 if (ins->rhs()->isConstant()) { 1464 masm.addPtr(ImmWord(ToIntPtr(ins->rhs())), lhs); 1465 } else { 1466 masm.addq(ToOperand(ins->rhs()), lhs); 1467 } 1468 } 1469 1470 void CodeGenerator::visitSubIntPtr(LSubIntPtr* ins) { 1471 Register lhs = ToRegister(ins->lhs()); 1472 MOZ_ASSERT(ToRegister(ins->output()) == lhs); 1473 1474 if (ins->rhs()->isConstant()) { 1475 masm.subPtr(ImmWord(ToIntPtr(ins->rhs())), lhs); 1476 } else { 1477 masm.subq(ToOperand(ins->rhs()), lhs); 1478 } 1479 } 1480 1481 void CodeGenerator::visitMulIntPtr(LMulIntPtr* ins) { 1482 Register lhs = ToRegister(ins->lhs()); 1483 MOZ_ASSERT(ToRegister(ins->output()) == lhs); 1484 const LAllocation* rhs = ins->rhs(); 1485 1486 if (rhs->isConstant()) { 1487 intptr_t constant = ToIntPtr(rhs); 1488 1489 switch (constant) { 1490 case -1: 1491 masm.negPtr(lhs); 1492 return; 1493 case 0: 1494 masm.xorPtr(lhs, lhs); 1495 return; 1496 case 1: 1497 return; 1498 case 2: 1499 masm.addPtr(lhs, lhs); 1500 return; 1501 } 1502 1503 // Use shift if constant is a power of 2. 1504 if (constant > 0 && mozilla::IsPowerOfTwo(uintptr_t(constant))) { 1505 uint32_t shift = mozilla::FloorLog2(constant); 1506 masm.lshiftPtr(Imm32(shift), lhs); 1507 return; 1508 } 1509 1510 masm.mulPtr(ImmWord(constant), lhs); 1511 } else { 1512 masm.imulq(ToOperand(rhs), lhs); 1513 } 1514 }